diff --git a/contracts/README.md b/contracts/README.md index ac06b25111..72faa54ffb 100644 --- a/contracts/README.md +++ b/contracts/README.md @@ -341,9 +341,7 @@ export DEFENDER_TEAM_SECRET= # set -o allexport && source ../../.env && set +o allexport # Set the DEBUG environment variable to oeth* for the Defender Action -npx hardhat setActionVars --id 38e44420-f38b-4d4a-86b0-6012a8897ad9 npx hardhat setActionVars --id f4b5b8d4-82ff-483f-bfae-9fef015790ca -npx hardhat setActionVars --id 191d9631-70b9-43c5-9db4-1dd985fde05c npx hardhat setActionVars --id e2929f53-db56-49b2-b054-35f7df7fc4fb npx hardhat setActionVars --id 12c153c8-c5ca-420b-9696-e80c827996d1 npx hardhat setActionVars --id 6e4f764d-4126-45a5-b7d9-1ab90cd3ffd6 @@ -354,9 +352,6 @@ npx hardhat setActionVars --id b1d831f1-29d4-4943-bb2e-8e625b76e82c # The Defender autotask client uses generic env var names so we'll set them first from the values in the .env file export API_KEY=${DEFENDER_TEAM_KEY} export API_SECRET=${DEFENDER_TEAM_SECRET} -# Holesky -npx defender-autotask update-code 38e44420-f38b-4d4a-86b0-6012a8897ad9 ./dist/registerValidators -npx defender-autotask update-code 191d9631-70b9-43c5-9db4-1dd985fde05c ./dist/doAccounting # Mainnet npx defender-autotask update-code f4b5b8d4-82ff-483f-bfae-9fef015790ca ./dist/registerValidators npx defender-autotask update-code 12c153c8-c5ca-420b-9696-e80c827996d1 ./dist/stakeValidators diff --git a/contracts/contracts/beacon/BeaconConsolidation.sol b/contracts/contracts/beacon/BeaconConsolidation.sol new file mode 100644 index 0000000000..14bafe12b9 --- /dev/null +++ b/contracts/contracts/beacon/BeaconConsolidation.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +/** + * @title Library to request validator consolidation on the beacon chain. + * @author Origin Protocol Inc + */ +library BeaconConsolidation { + /// @notice The address the validator consolidation requests are sent + /// See https://eips.ethereum.org/EIPS/eip-7251 + address internal constant CONSOLIDATION_REQUEST_ADDRESS = + 0x0000BBdDc7CE488642fb579F8B00f3a590007251; + + function request(bytes calldata source, bytes calldata target) + internal + returns (uint256 fee_) + { + require(source.length == 48, "Invalid source byte length"); + require(target.length == 48, "Invalid target byte length"); + + fee_ = fee(); + + // Call the Consolidation Request contract with the public keys of the source and target + // validators packed together. + // This does not have a function signature, so we use a call + (bool success, ) = CONSOLIDATION_REQUEST_ADDRESS.call{ value: fee_ }( + abi.encodePacked(source, target) + ); + + require(success, "Consolidation request failed"); + } + + function fee() internal view returns (uint256) { + // Get fee from the consolidation request contract + (bool success, bytes memory result) = CONSOLIDATION_REQUEST_ADDRESS + .staticcall(""); + + require(success && result.length > 0, "Failed to get fee"); + return abi.decode(result, (uint256)); + } +} diff --git a/contracts/contracts/beacon/BeaconProofs.sol b/contracts/contracts/beacon/BeaconProofs.sol new file mode 100644 index 0000000000..7fda6926b4 --- /dev/null +++ b/contracts/contracts/beacon/BeaconProofs.sol @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { BeaconProofsLib } from "./BeaconProofsLib.sol"; +import { IBeaconProofs } from "../interfaces/IBeaconProofs.sol"; + +/** + * @title Verifies merkle proofs of beacon chain data. + * @author Origin Protocol Inc + */ +contract BeaconProofs is IBeaconProofs { + /// @notice Verifies the validator index is for the given validator public key. + /// Also verify the validator's withdrawal credential points to the withdrawal address. + /// BeaconBlock.state.validators[validatorIndex].pubkey + /// @param beaconBlockRoot The root of the beacon block + /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format + /// @param proof The merkle proof for the validator public key to the beacon block root. + /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node. + /// @param validatorIndex The validator index + /// @param withdrawalCredentials a value containing the validator type and withdrawal address. + function verifyValidator( + bytes32 beaconBlockRoot, + bytes32 pubKeyHash, + bytes calldata proof, + uint40 validatorIndex, + bytes32 withdrawalCredentials + ) external view { + BeaconProofsLib.verifyValidator( + beaconBlockRoot, + pubKeyHash, + proof, + validatorIndex, + withdrawalCredentials + ); + } + + function verifyValidatorWithdrawable( + bytes32 beaconBlockRoot, + uint40 validatorIndex, + uint64 withdrawableEpoch, + bytes calldata withdrawableEpochProof + ) external view { + BeaconProofsLib.verifyValidatorWithdrawableEpoch( + beaconBlockRoot, + validatorIndex, + withdrawableEpoch, + withdrawableEpochProof + ); + } + + /// @notice Verifies the balances container to the beacon block root + /// BeaconBlock.state.balances + /// @param beaconBlockRoot The root of the beacon block + /// @param balancesContainerRoot The merkle root of the the balances container + /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root. + /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node. + function verifyBalancesContainer( + bytes32 beaconBlockRoot, + bytes32 balancesContainerRoot, + bytes calldata balancesContainerProof + ) external view { + BeaconProofsLib.verifyBalancesContainer( + beaconBlockRoot, + balancesContainerRoot, + balancesContainerProof + ); + } + + /// @notice Verifies the validator balance to the root of the Balances container. + /// @param balancesContainerRoot The merkle root of the Balances container. + /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances. + /// @param balanceProof The merkle proof for the validator balance to the Balances container root. + /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node. + /// @param validatorIndex The validator index to verify the balance for + /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index + function verifyValidatorBalance( + bytes32 balancesContainerRoot, + bytes32 validatorBalanceLeaf, + bytes calldata balanceProof, + uint40 validatorIndex + ) external view returns (uint256 validatorBalanceGwei) { + validatorBalanceGwei = BeaconProofsLib.verifyValidatorBalance( + balancesContainerRoot, + validatorBalanceLeaf, + balanceProof, + validatorIndex + ); + } + + /// @notice Verifies the pending deposits container to the beacon block root. + /// BeaconBlock.state.pendingDeposits + /// @param beaconBlockRoot The root of the beacon block. + /// @param pendingDepositsContainerRoot The merkle root of the the pending deposits container. + /// @param proof The merkle proof for the pending deposits container to the beacon block root. + /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node. + function verifyPendingDepositsContainer( + bytes32 beaconBlockRoot, + bytes32 pendingDepositsContainerRoot, + bytes calldata proof + ) external view { + BeaconProofsLib.verifyPendingDepositsContainer( + beaconBlockRoot, + pendingDepositsContainerRoot, + proof + ); + } + + /// @notice Verified a pending deposit to the root of the Pending Deposits container. + /// @param pendingDepositsContainerRoot The merkle root of the Pending Deposits container. + /// @param pendingDepositRoot The leaf node containing the validator balance with three other balances. + /// @param proof The merkle proof for the pending deposit root to the Pending Deposits container root. + /// This is 28 witness hashes of 32 bytes each concatenated together starting from the leaf node. + /// @param pendingDepositIndex The pending deposit index in the Pending Deposits container + function verifyPendingDeposit( + bytes32 pendingDepositsContainerRoot, + bytes32 pendingDepositRoot, + bytes calldata proof, + uint32 pendingDepositIndex + ) external view { + BeaconProofsLib.verifyPendingDeposit( + pendingDepositsContainerRoot, + pendingDepositRoot, + proof, + pendingDepositIndex + ); + } + + /// @notice If the deposit queue is not empty, + /// verify the slot of the first pending deposit to the beacon block root. + /// BeaconBlock.state.pendingDeposits[0].slot + /// If the deposit queue is empty, verify the root of the first pending deposit is empty + /// BeaconBlock.state.PendingDeposits[0] + /// @param beaconBlockRoot The root of the beacon block. + /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue. + /// Can be anything if the deposit queue is empty. + /// @param firstPendingDepositSlotProof The merkle proof to the beacon block root. Can be either: + /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty. + /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty. + /// The 32 byte witness hashes are concatenated together starting from the leaf node. + /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise. + function verifyFirstPendingDeposit( + bytes32 beaconBlockRoot, + uint64 slot, + bytes calldata firstPendingDepositSlotProof + ) external view returns (bool isEmptyDepositQueue) { + isEmptyDepositQueue = BeaconProofsLib.verifyFirstPendingDeposit( + beaconBlockRoot, + slot, + firstPendingDepositSlotProof + ); + } + + /// @notice Merkleizes a beacon chain pending deposit. + /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format + /// @param withdrawalCredentials The 32 byte withdrawal credentials. + /// @param amountGwei The amount of the deposit in Gwei. + /// @param signature The 96 byte BLS signature. + /// @param slot The beacon chain slot the deposit was made in. + /// @return root The merkle root of the pending deposit. + function merkleizePendingDeposit( + bytes32 pubKeyHash, + bytes calldata withdrawalCredentials, + uint64 amountGwei, + bytes calldata signature, + uint64 slot + ) external pure returns (bytes32) { + return + BeaconProofsLib.merkleizePendingDeposit( + pubKeyHash, + withdrawalCredentials, + amountGwei, + signature, + slot + ); + } + + /// @notice Merkleizes a BLS signature used for validator deposits. + /// @param signature The 96 byte BLS signature. + /// @return root The merkle root of the signature. + function merkleizeSignature(bytes calldata signature) + external + pure + returns (bytes32 root) + { + return BeaconProofsLib.merkleizeSignature(signature); + } +} diff --git a/contracts/contracts/beacon/BeaconProofsLib.sol b/contracts/contracts/beacon/BeaconProofsLib.sol new file mode 100644 index 0000000000..9a7539827e --- /dev/null +++ b/contracts/contracts/beacon/BeaconProofsLib.sol @@ -0,0 +1,430 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Merkle } from "./Merkle.sol"; +import { Endian } from "./Endian.sol"; + +/** + * @title Library to verify merkle proofs of beacon chain data. + * @author Origin Protocol Inc + */ +library BeaconProofsLib { + // Known generalized indices in the beacon block + /// @dev BeaconBlock.state.PendingDeposits[0] + /// Beacon block container: height 3, state at at index 3 + /// Beacon state container: height 6, pending deposits at index 34 + /// Pending deposits container: height 28, first deposit at index 0 + /// ((2 ^ 3 + 3) * 2 ^ 6 + 34) * 2 ^ 28 + 0 = 198105366528 + uint256 internal constant FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX = + 198105366528; + /// @dev BeaconBlock.state.PendingDeposits[0].pubkey + /// Pending Deposit container: height 3, pubkey at index 4 + /// (((2 ^ 3 + 3) * 2 ^ 6 + 34) * 2 ^ 28 + 0) * 2 ^ 3 + 4 = 1584842932228 + uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX = + 1584842932228; + /// @dev BeaconBlock.state.validators + /// Beacon block container: height 3, state at at index 3 + /// Beacon state container: height 6, validators at index 11 + /// (2 ^ 3 + 3) * 2 ^ 6 + 11 = 715 + uint256 internal constant VALIDATORS_CONTAINER_GENERALIZED_INDEX = 715; + /// @dev BeaconBlock.state.balances + /// Beacon block container: height 3, state at at index 3 + /// Beacon state container: height 6, balances at index 12 + /// (2 ^ 3 + 3) * 2 ^ 6 + 12 = 716 + uint256 internal constant BALANCES_CONTAINER_GENERALIZED_INDEX = 716; + + /// @dev BeaconBlock.state.pendingDeposits + /// Beacon block container: height 3, state at at index 3 + /// Beacon state container: height 6, balances at index 34 + /// (2 ^ 3 + 3) * 2 ^ 6 + 34 = 738 + uint256 internal constant PENDING_DEPOSITS_CONTAINER_GENERALIZED_INDEX = + 738; + + /// @dev Number of bytes in the proof to the first pending deposit. + /// 37 witness hashes of 32 bytes each concatenated together. + /// BeaconBlock.state.PendingDeposits[0] + /// 37 * 32 bytes = 1184 bytes + uint256 internal constant FIRST_PENDING_DEPOSIT_PROOF_LENGTH = 1184; + /// @dev Number of bytes in the proof from the slot of the first pending deposit to the beacon block root. + /// 40 witness hashes of 32 bytes each concatenated together. + /// BeaconBlock.state.PendingDeposits[0].slot + /// 40 * 32 bytes = 1280 bytes + uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_PROOF_LENGTH = 1280; + + /// @dev Merkle height of the Balances container + /// BeaconBlock.state.balances + uint256 internal constant BALANCES_HEIGHT = 39; + /// @dev Merkle height of the Validators container list + /// BeaconBlock.state.validators + uint256 internal constant VALIDATORS_LIST_HEIGHT = 41; + /// @dev Merkle height of the Pending Deposits container list + /// BeaconBlock.state.pendingDeposits + uint256 internal constant PENDING_DEPOSITS_LIST_HEIGHT = 28; + /// @dev Merkle height of the Validator container + /// BeaconBlock.state.validators[validatorIndex] + uint256 internal constant VALIDATOR_CONTAINER_HEIGHT = 3; + + /// @dev Position of the pubkey field in the Validator container. + /// BeaconBlock.state.validators[validatorIndex].pubkey + uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0; + /// @dev Position of the withdrawable epoch field in the Validator container. + /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch + uint256 internal constant VALIDATOR_WITHDRAWABLE_EPOCH_INDEX = 7; + + /// @notice Verifies the validator index is for the given validator public key. + /// Also verify the validator's withdrawal credential points to the withdrawal address. + /// BeaconBlock.state.validators[validatorIndex].pubkey + /// @param beaconBlockRoot The root of the beacon block + /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format + /// @param proof The merkle proof for the validator public key to the beacon block root. + /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node. + /// @param validatorIndex The validator index + /// @param withdrawalCredentials a value containing the validator type and withdrawal address. + function verifyValidator( + bytes32 beaconBlockRoot, + bytes32 pubKeyHash, + bytes calldata proof, + uint40 validatorIndex, + bytes32 withdrawalCredentials + ) internal view { + require(beaconBlockRoot != bytes32(0), "Invalid block root"); + + // BeaconBlock.state.validators[validatorIndex] + uint256 generalizedIndex = concatGenIndices( + VALIDATORS_CONTAINER_GENERALIZED_INDEX, + VALIDATORS_LIST_HEIGHT, + validatorIndex + ); + // BeaconBlock.state.validators[validatorIndex].pubkey + generalizedIndex = concatGenIndices( + generalizedIndex, + VALIDATOR_CONTAINER_HEIGHT, + VALIDATOR_PUBKEY_INDEX + ); + + // Get the withdrawal credentials from the first witness in the pubkey merkle proof. + bytes32 withdrawalCredentialsFromProof = bytes32(proof[:32]); + + require( + withdrawalCredentialsFromProof == withdrawalCredentials, + "Invalid withdrawal cred" + ); + + require( + // 53 * 32 bytes = 1696 bytes + proof.length == 1696 && + Merkle.verifyInclusionSha256({ + proof: proof, + root: beaconBlockRoot, + leaf: pubKeyHash, + index: generalizedIndex + }), + "Invalid validator proof" + ); + } + + /// @notice Verifies a validator's withdrawable epoch to the beacon block root + /// for a given validator index. + /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch + /// @param beaconBlockRoot The root of the beacon block + /// @param validatorIndex The validator index to verify the withdrawable epoch for. + /// @param withdrawableEpoch The withdrawable epoch to verify in big endian uint64 format + /// @param proof The merkle proof for the validator's withdrawable epoch to the beacon block root. + /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node. + function verifyValidatorWithdrawableEpoch( + bytes32 beaconBlockRoot, + uint40 validatorIndex, + uint64 withdrawableEpoch, + bytes calldata proof + ) internal view { + require(beaconBlockRoot != bytes32(0), "Invalid block root"); + + // BeaconBlock.state.validators[validatorIndex] + uint256 exitEpochGenIndex = concatGenIndices( + VALIDATORS_CONTAINER_GENERALIZED_INDEX, + VALIDATORS_LIST_HEIGHT, + validatorIndex + ); + // BeaconBlock.state.validators[validatorIndex].withdrawableEpoch + exitEpochGenIndex = concatGenIndices( + exitEpochGenIndex, + VALIDATOR_CONTAINER_HEIGHT, + VALIDATOR_WITHDRAWABLE_EPOCH_INDEX + ); + + require( + // 53 * 32 bytes = 1696 bytes + proof.length == 1696 && + Merkle.verifyInclusionSha256({ + proof: proof, + root: beaconBlockRoot, + leaf: Endian.toLittleEndianUint64(withdrawableEpoch), + index: exitEpochGenIndex + }), + "Invalid withdrawable proof" + ); + } + + /// @notice Verifies the balances container to the beacon block root. + /// BeaconBlock.state.balances + /// @param beaconBlockRoot The root of the beacon block. + /// @param balancesContainerRoot The merkle root of the the balances container. + /// @param proof The merkle proof for the balances container to the beacon block root. + /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node. + function verifyBalancesContainer( + bytes32 beaconBlockRoot, + bytes32 balancesContainerRoot, + bytes calldata proof + ) internal view { + require(beaconBlockRoot != bytes32(0), "Invalid block root"); + + // BeaconBlock.state.balances + require( + // 9 * 32 bytes = 288 bytes + proof.length == 288 && + Merkle.verifyInclusionSha256({ + proof: proof, + root: beaconBlockRoot, + leaf: balancesContainerRoot, + index: BALANCES_CONTAINER_GENERALIZED_INDEX + }), + "Invalid balance container proof" + ); + } + + /// @notice Verifies the validator balance to the root of the Balances container. + /// @param balancesContainerRoot The merkle root of the Balances container. + /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances. + /// @param proof The merkle proof for the validator balance to the Balances container root. + /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node. + /// @param validatorIndex The validator index to verify the balance for. + /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index. + function verifyValidatorBalance( + bytes32 balancesContainerRoot, + bytes32 validatorBalanceLeaf, + bytes calldata proof, + uint40 validatorIndex + ) internal view returns (uint256 validatorBalanceGwei) { + require(balancesContainerRoot != bytes32(0), "Invalid container root"); + + // Four balances are stored in each leaf so the validator index is divided by 4 + uint64 balanceIndex = validatorIndex / 4; + + // Get the index within the balances container, not the Beacon Block + // BeaconBlock.state.balances[balanceIndex] + uint256 generalizedIndex = concatGenIndices( + 1, + BALANCES_HEIGHT, + balanceIndex + ); + + validatorBalanceGwei = balanceAtIndex( + validatorBalanceLeaf, + validatorIndex + ); + + require( + // 39 * 32 bytes = 1248 bytes + proof.length == 1248 && + Merkle.verifyInclusionSha256({ + proof: proof, + root: balancesContainerRoot, + leaf: validatorBalanceLeaf, + index: generalizedIndex + }), + "Invalid balance proof" + ); + } + + /// @notice Verifies the pending deposits container to the beacon block root. + /// BeaconBlock.state.pendingDeposits + /// @param beaconBlockRoot The root of the beacon block. + /// @param pendingDepositsContainerRoot The merkle root of the the pending deposits container. + /// @param proof The merkle proof for the pending deposits container to the beacon block root. + /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node. + function verifyPendingDepositsContainer( + bytes32 beaconBlockRoot, + bytes32 pendingDepositsContainerRoot, + bytes calldata proof + ) internal view { + require(beaconBlockRoot != bytes32(0), "Invalid block root"); + + // BeaconBlock.state.pendingDeposits + require( + // 9 * 32 bytes = 288 bytes + proof.length == 288 && + Merkle.verifyInclusionSha256({ + proof: proof, + root: beaconBlockRoot, + leaf: pendingDepositsContainerRoot, + index: PENDING_DEPOSITS_CONTAINER_GENERALIZED_INDEX + }), + "Invalid deposit container proof" + ); + } + + /// @notice Verifies a pending deposit in the pending deposits container. + /// BeaconBlock.state.pendingDeposits[depositIndex] + /// @param pendingDepositsContainerRoot The merkle root of the pending deposits list container + /// @param pendingDepositRoot The merkle root of the pending deposit to verify + /// @param proof The merkle proof for the pending deposit root to the pending deposits list container root. + /// This is 28 witness hashes of 32 bytes each concatenated together starting from the leaf node. + /// @param pendingDepositIndex The index in the pending deposits list container for the deposit to verify. + function verifyPendingDeposit( + bytes32 pendingDepositsContainerRoot, + bytes32 pendingDepositRoot, + bytes calldata proof, + uint32 pendingDepositIndex + ) internal view { + require(pendingDepositsContainerRoot != bytes32(0), "Invalid root"); + // ssz-merkleizing a list which has a variable length, an additional + // sha256(pending_deposits_root, pending_deposits_length) operation is done to get the + // actual pending deposits root so the max pending deposit index is 2^(28 - 1) + require( + pendingDepositIndex < 2**(PENDING_DEPOSITS_LIST_HEIGHT - 1), + "Invalid deposit index" + ); + + // BeaconBlock.state.pendingDeposits[depositIndex] + uint256 generalizedIndex = concatGenIndices( + 1, + PENDING_DEPOSITS_LIST_HEIGHT, + pendingDepositIndex + ); + + require( + // 28 * 32 bytes = 896 bytes + proof.length == 896 && + Merkle.verifyInclusionSha256({ + proof: proof, + root: pendingDepositsContainerRoot, + leaf: pendingDepositRoot, + index: generalizedIndex + }), + "Invalid deposit proof" + ); + } + + /// @notice If the deposit queue is not empty, + /// verify the slot of the first pending deposit to the beacon block root. + /// BeaconBlock.state.pendingDeposits[0].slot + /// If the deposit queue is empty, verify the root of the first pending deposit is empty + /// BeaconBlock.state.PendingDeposits[0] + /// @param beaconBlockRoot The root of the beacon block. + /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue. + /// Can be anything if the deposit queue is empty. + /// @param proof The merkle proof to the beacon block root. Can be either: + /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty. + /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty. + /// The 32 byte witness hashes are concatenated together starting from the leaf node. + /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise. + function verifyFirstPendingDeposit( + bytes32 beaconBlockRoot, + uint64 slot, + bytes calldata proof + ) internal view returns (bool isEmptyDepositQueue) { + require(beaconBlockRoot != bytes32(0), "Invalid block root"); + + // If the deposit queue is empty + if (proof.length == FIRST_PENDING_DEPOSIT_PROOF_LENGTH) { + require( + Merkle.verifyInclusionSha256({ + proof: proof, + root: beaconBlockRoot, + leaf: bytes32(0), + index: FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX + }), + "Invalid empty deposits proof" + ); + return true; + } + + // Verify the public key of the first pending deposit + // BeaconBlock.state.PendingDeposits[0].slot + require( + proof.length == FIRST_PENDING_DEPOSIT_SLOT_PROOF_LENGTH && + Merkle.verifyInclusionSha256({ + proof: proof, + root: beaconBlockRoot, + leaf: Endian.toLittleEndianUint64(slot), + index: FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX + }), + "Invalid deposit slot proof" + ); + } + + /// @notice Merkleizes a beacon chain pending deposit. + /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format + /// @param withdrawalCredentials The 32 byte withdrawal credentials. + /// @param amountGwei The amount of the deposit in Gwei. + /// @param signature The 96 byte BLS signature. + /// @param slot The beacon chain slot the deposit was made in. + /// @return root The merkle root of the pending deposit. + function merkleizePendingDeposit( + bytes32 pubKeyHash, + bytes calldata withdrawalCredentials, + uint64 amountGwei, + bytes calldata signature, + uint64 slot + ) internal pure returns (bytes32 root) { + bytes32[] memory leaves = new bytes32[](8); + leaves[0] = pubKeyHash; + leaves[1] = bytes32(withdrawalCredentials[:32]); + leaves[2] = Endian.toLittleEndianUint64(amountGwei); + leaves[3] = merkleizeSignature(signature); + leaves[4] = Endian.toLittleEndianUint64(slot); + leaves[5] = bytes32(0); + leaves[6] = bytes32(0); + leaves[7] = bytes32(0); + + root = Merkle.merkleizeSha256(leaves); + } + + /// @notice Merkleizes a BLS signature used for validator deposits. + /// @param signature The 96 byte BLS signature. + /// @return root The merkle root of the signature. + function merkleizeSignature(bytes calldata signature) + internal + pure + returns (bytes32) + { + require(signature.length == 96, "Invalid signature"); + + bytes32[] memory leaves = new bytes32[](4); + leaves[0] = bytes32(signature[:32]); + leaves[1] = bytes32(signature[32:64]); + leaves[2] = bytes32(signature[64:96]); + leaves[3] = bytes32(0); + + return Merkle.merkleizeSha256(leaves); + } + + //////////////////////////////////////////////////// + /// Internal Helper Functions + //////////////////////////////////////////////////// + + function balanceAtIndex(bytes32 validatorBalanceLeaf, uint40 validatorIndex) + internal + pure + returns (uint256) + { + uint256 bitShiftAmount = (validatorIndex % 4) * 64; + return + Endian.fromLittleEndianUint64( + bytes32((uint256(validatorBalanceLeaf) << bitShiftAmount)) + ); + } + + /// @notice Concatenates two beacon chain generalized indices into one. + /// @param genIndex The first generalized index or 1 if calculating for a single container. + /// @param height The merkle tree height of the second container. eg 39 for balances, 41 for validators. + /// @param index The index within the second container. eg the validator index. + /// @return genIndex The concatenated generalized index. + function concatGenIndices( + uint256 genIndex, + uint256 height, + uint256 index + ) internal pure returns (uint256) { + return (genIndex << height) | index; + } +} diff --git a/contracts/contracts/beacon/BeaconRoots.sol b/contracts/contracts/beacon/BeaconRoots.sol new file mode 100644 index 0000000000..bb5814d107 --- /dev/null +++ b/contracts/contracts/beacon/BeaconRoots.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +/** + * @title Library to retrieve beacon block roots. + * @author Origin Protocol Inc + */ +library BeaconRoots { + /// @notice The address of beacon block roots oracle + /// See https://eips.ethereum.org/EIPS/eip-4788 + address internal constant BEACON_ROOTS_ADDRESS = + 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02; + + /// @notice Returns the beacon block root for the previous block. + /// This comes from the Beacon Roots contract defined in EIP-4788. + /// This will revert if the block is more than 8,191 blocks old as + /// that is the size of the beacon root's ring buffer. + /// @param timestamp The timestamp of the block for which to get the parent root. + /// @return parentRoot The parent block root for the given timestamp. + function parentBlockRoot(uint64 timestamp) + internal + view + returns (bytes32 parentRoot) + { + // Call the Beacon Roots contract to get the parent block root. + // This does not have a function signature, so we use a staticcall. + (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall( + abi.encode(timestamp) + ); + + require(success && result.length > 0, "Invalid beacon timestamp"); + parentRoot = abi.decode(result, (bytes32)); + } +} diff --git a/contracts/contracts/beacon/Endian.sol b/contracts/contracts/beacon/Endian.sol new file mode 100644 index 0000000000..0abdd90e91 --- /dev/null +++ b/contracts/contracts/beacon/Endian.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** + * @title Library to handle conversion between little-endian and big-endian formats. + * @author Origin Protocol Inc + */ +library Endian { + /** + * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64 + * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type + * @return n The big endian-formatted uint64 + * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits), + * but it is immediately truncated to a uint64 (i.e. 64 bits) + * through a right-shift/shr operation. + */ + function fromLittleEndianUint64(bytes32 lenum) + internal + pure + returns (uint64 n) + { + // the number needs to be stored in little-endian encoding (ie in bytes 0-8) + n = uint64(uint256(lenum >> 192)); + // forgefmt: disable-next-item + return + (n >> 56) | + ((0x00FF000000000000 & n) >> 40) | + ((0x0000FF0000000000 & n) >> 24) | + ((0x000000FF00000000 & n) >> 8) | + ((0x00000000FF000000 & n) << 8) | + ((0x0000000000FF0000 & n) << 24) | + ((0x000000000000FF00 & n) << 40) | + ((0x00000000000000FF & n) << 56); + } + + function toLittleEndianUint64(uint64 benum) + internal + pure + returns (bytes32 n) + { + // Convert to little-endian by reversing byte order + uint64 reversed = (benum >> 56) | + ((0x00FF000000000000 & benum) >> 40) | + ((0x0000FF0000000000 & benum) >> 24) | + ((0x000000FF00000000 & benum) >> 8) | + ((0x00000000FF000000 & benum) << 8) | + ((0x0000000000FF0000 & benum) << 24) | + ((0x000000000000FF00 & benum) << 40) | + ((0x00000000000000FF & benum) << 56); + + // Store the little-endian uint64 in the least significant 64 bits of bytes32 + n = bytes32(uint256(reversed)); + // Shift to most significant bits + n = n << 192; + } +} diff --git a/contracts/contracts/beacon/Merkle.sol b/contracts/contracts/beacon/Merkle.sol new file mode 100644 index 0000000000..8a998635c5 --- /dev/null +++ b/contracts/contracts/beacon/Merkle.sol @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: MIT +// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol) + +pragma solidity ^0.8.0; + +/** + * @dev These functions deal with verification of Merkle Tree proofs. + * + * The tree and the proofs can be generated using our + * https://github.com/OpenZeppelin/merkle-tree[JavaScript library]. + * You will find a quickstart guide in the readme. + * + * WARNING: You should avoid using leaf values that are 64 bytes long prior to + * hashing, or use a hash function other than keccak256 for hashing leaves. + * This is because the concatenation of a sorted pair of internal nodes in + * the merkle tree could be reinterpreted as a leaf value. + * OpenZeppelin's JavaScript library generates merkle trees that are safe + * against this attack out of the box. + */ +library Merkle { + error InvalidProofLength(); + + /** + * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up + * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt + * hash matches the root of the tree. The tree is built assuming `leaf` is + * the 0 indexed `index`'th leaf from the bottom left of the tree. + * + * Note this is for a Merkle tree using the sha256 hash function + */ + function verifyInclusionSha256( + bytes memory proof, + bytes32 root, + bytes32 leaf, + uint256 index + ) internal view returns (bool) { + return processInclusionProofSha256(proof, leaf, index) == root; + } + + /** + * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up + * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt + * hash matches the root of the tree. The tree is built assuming `leaf` is + * the 0 indexed `index`'th leaf from the bottom left of the tree. + * + * _Available since v4.4._ + * + * Note this is for a Merkle tree using the sha256 hash function + */ + function processInclusionProofSha256( + bytes memory proof, + bytes32 leaf, + uint256 index + ) internal view returns (bytes32) { + require( + proof.length != 0 && proof.length % 32 == 0, + InvalidProofLength() + ); + bytes32[1] memory computedHash = [leaf]; + for (uint256 i = 32; i <= proof.length; i += 32) { + if (index % 2 == 0) { + // if ith bit of index is 0, then computedHash is a left sibling + // solhint-disable-next-line no-inline-assembly + assembly { + mstore(0x00, mload(computedHash)) + mstore(0x20, mload(add(proof, i))) + if iszero( + staticcall( + sub(gas(), 2000), + 2, + 0x00, + 0x40, + computedHash, + 0x20 + ) + ) { + revert(0, 0) + } + } + } else { + // if ith bit of index is 1, then computedHash is a right sibling + // solhint-disable-next-line no-inline-assembly + assembly { + mstore(0x00, mload(add(proof, i))) + mstore(0x20, mload(computedHash)) + if iszero( + staticcall( + sub(gas(), 2000), + 2, + 0x00, + 0x40, + computedHash, + 0x20 + ) + ) { + revert(0, 0) + } + } + } + index = index / 2; + } + return computedHash[0]; + } + + /** + * @notice Returns the merkle root of a tree created from a set of leaves using sha256 as its hash function. + * @param leaves the leaves of the merkle tree + * @return The computed Merkle root of the tree. + * @dev A pre-condition to this function is that leaves.length is a power of two. + * If not, the function will merkleize the inputs incorrectly. + */ + function merkleizeSha256(bytes32[] memory leaves) + internal + pure + returns (bytes32) + { + //there are half as many nodes in the layer above the leaves + uint256 numNodesInLayer = leaves.length / 2; + //create a layer to store the internal nodes + bytes32[] memory layer = new bytes32[](numNodesInLayer); + //fill the layer with the pairwise hashes of the leaves + for (uint256 i = 0; i < numNodesInLayer; i++) { + layer[i] = sha256( + abi.encodePacked(leaves[2 * i], leaves[2 * i + 1]) + ); + } + //the next layer above has half as many nodes + numNodesInLayer /= 2; + //while we haven't computed the root + while (numNodesInLayer != 0) { + //overwrite the first numNodesInLayer nodes in layer with the pairwise hashes of their children + for (uint256 i = 0; i < numNodesInLayer; i++) { + layer[i] = sha256( + abi.encodePacked(layer[2 * i], layer[2 * i + 1]) + ); + } + //the next layer above has half as many nodes + numNodesInLayer /= 2; + } + //the first node in the layer is the root + return layer[0]; + } +} diff --git a/contracts/contracts/beacon/PartialWithdrawal.sol b/contracts/contracts/beacon/PartialWithdrawal.sol new file mode 100644 index 0000000000..aaf2160e46 --- /dev/null +++ b/contracts/contracts/beacon/PartialWithdrawal.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +/** + * @title Library to request full or partial withdrawals from validators on the beacon chain. + * @author Origin Protocol Inc + */ +library PartialWithdrawal { + /// @notice The address where the withdrawal request is sent to + /// See https://eips.ethereum.org/EIPS/eip-7002 + address internal constant WITHDRAWAL_REQUEST_ADDRESS = + 0x00000961Ef480Eb55e80D19ad83579A64c007002; + + /// @notice Requests a partial withdrawal for a given validator public key and amount. + /// @param validatorPubKey The public key of the validator to withdraw from + /// @param amount The amount of ETH to withdraw + function request(bytes calldata validatorPubKey, uint64 amount) + internal + returns (uint256 fee_) + { + require(validatorPubKey.length == 48, "Invalid validator byte length"); + fee_ = fee(); + + // Call the Withdrawal Request contract with the validator public key + // and amount to be withdrawn packed together + + // This is a general purpose EL to CL request: + // https://eips.ethereum.org/EIPS/eip-7685 + (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }( + abi.encodePacked(validatorPubKey, amount) + ); + + require(success, "Withdrawal request failed"); + } + + /// @notice Gets fee for withdrawal requests contract on Beacon chain + function fee() internal view returns (uint256) { + // Get fee from the withdrawal request contract + (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS + .staticcall(""); + + require(success && result.length > 0, "Failed to get fee"); + return abi.decode(result, (uint256)); + } +} diff --git a/contracts/contracts/interfaces/IBeaconProofs.sol b/contracts/contracts/interfaces/IBeaconProofs.sol new file mode 100644 index 0000000000..315db83c9c --- /dev/null +++ b/contracts/contracts/interfaces/IBeaconProofs.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +interface IBeaconProofs { + function verifyValidator( + bytes32 beaconBlockRoot, + bytes32 pubKeyHash, + bytes calldata validatorPubKeyProof, + uint40 validatorIndex, + bytes32 withdrawalCredentials + ) external view; + + function verifyValidatorWithdrawable( + bytes32 beaconBlockRoot, + uint40 validatorIndex, + uint64 withdrawableEpoch, + bytes calldata withdrawableEpochProof + ) external view; + + function verifyBalancesContainer( + bytes32 beaconBlockRoot, + bytes32 balancesContainerLeaf, + bytes calldata balancesContainerProof + ) external view; + + function verifyValidatorBalance( + bytes32 balancesContainerRoot, + bytes32 validatorBalanceLeaf, + bytes calldata balanceProof, + uint40 validatorIndex + ) external view returns (uint256 validatorBalance); + + function verifyPendingDepositsContainer( + bytes32 beaconBlockRoot, + bytes32 pendingDepositsContainerRoot, + bytes calldata proof + ) external view; + + function verifyPendingDeposit( + bytes32 pendingDepositsContainerRoot, + bytes32 pendingDepositRoot, + bytes calldata proof, + uint32 pendingDepositIndex + ) external view; + + function verifyFirstPendingDeposit( + bytes32 beaconBlockRoot, + uint64 slot, + bytes calldata firstPendingDepositSlotProof + ) external view returns (bool isEmptyDepositQueue); + + function merkleizePendingDeposit( + bytes32 pubKeyHash, + bytes calldata withdrawalCredentials, + uint64 amountGwei, + bytes calldata signature, + uint64 slot + ) external pure returns (bytes32 root); + + function merkleizeSignature(bytes calldata signature) + external + pure + returns (bytes32 root); +} diff --git a/contracts/contracts/interfaces/IConsolidation.sol b/contracts/contracts/interfaces/IConsolidation.sol new file mode 100644 index 0000000000..4743824b3c --- /dev/null +++ b/contracts/contracts/interfaces/IConsolidation.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.4; + +interface IConsolidationSource { + function confirmConsolidation() + external + returns (uint256 consolidationCount); +} + +interface IConsolidationTarget { + function requestConsolidation( + bytes32 lastSourcePubKeyHash, + bytes32 targetPubKeyHash + ) external; +} diff --git a/contracts/contracts/mocks/MockBeaconConsolidation.sol b/contracts/contracts/mocks/MockBeaconConsolidation.sol new file mode 100644 index 0000000000..731b45150a --- /dev/null +++ b/contracts/contracts/mocks/MockBeaconConsolidation.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { BeaconConsolidation } from "../beacon/BeaconConsolidation.sol"; + +contract MockBeaconConsolidation { + function fee() external view returns (uint256) { + return BeaconConsolidation.fee(); + } + + function request(bytes calldata source, bytes calldata target) + external + returns (uint256 fee_) + { + return BeaconConsolidation.request(source, target); + } +} diff --git a/contracts/contracts/mocks/MockPartialWithdrawal.sol b/contracts/contracts/mocks/MockPartialWithdrawal.sol new file mode 100644 index 0000000000..fcf0d056c6 --- /dev/null +++ b/contracts/contracts/mocks/MockPartialWithdrawal.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { PartialWithdrawal } from "../beacon/PartialWithdrawal.sol"; + +contract MockPartialWithdrawal { + function fee() external view returns (uint256) { + return PartialWithdrawal.fee(); + } + + function request(bytes calldata validatorPubKey, uint64 amount) + external + returns (uint256 fee_) + { + return PartialWithdrawal.request(validatorPubKey, amount); + } +} diff --git a/contracts/contracts/mocks/beacon/EnhancedBeaconProofs.sol b/contracts/contracts/mocks/beacon/EnhancedBeaconProofs.sol new file mode 100644 index 0000000000..87cc613564 --- /dev/null +++ b/contracts/contracts/mocks/beacon/EnhancedBeaconProofs.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { BeaconProofsLib } from "../../beacon/BeaconProofsLib.sol"; +import { BeaconProofs } from "../../beacon/BeaconProofs.sol"; + +contract EnhancedBeaconProofs is BeaconProofs { + function concatGenIndices( + uint256 index1, + uint256 height2, + uint256 index2 + ) external pure returns (uint256 genIndex) { + return BeaconProofsLib.concatGenIndices(index1, height2, index2); + } +} diff --git a/contracts/contracts/mocks/beacon/ExecutionLayerConsolidation.sol b/contracts/contracts/mocks/beacon/ExecutionLayerConsolidation.sol new file mode 100644 index 0000000000..5c747b9f2b --- /dev/null +++ b/contracts/contracts/mocks/beacon/ExecutionLayerConsolidation.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { GeneralPurposeToConsensusLayerRequest } from "./GeneralPurposeToConsensusLayerRequest.sol"; + +contract ExecutionLayerConsolidation is GeneralPurposeToConsensusLayerRequest { + event ConsolidationRequestIssued(bytes sourceKey, bytes targetKey); + bytes public lastSource; + bytes public lastTarget; + + function handleRequest(bytes calldata data) internal override { + // parameters should consist of twice the 48 bytes for 2 public keys + require(data.length == 96, "Invalid Consolidation data"); + lastSource = data[:48]; + lastTarget = data[48:]; + + emit ConsolidationRequestIssued(lastSource, lastTarget); + } +} diff --git a/contracts/contracts/mocks/beacon/ExecutionLayerWithdrawal.sol b/contracts/contracts/mocks/beacon/ExecutionLayerWithdrawal.sol new file mode 100644 index 0000000000..2280c41f89 --- /dev/null +++ b/contracts/contracts/mocks/beacon/ExecutionLayerWithdrawal.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { GeneralPurposeToConsensusLayerRequest } from "./GeneralPurposeToConsensusLayerRequest.sol"; + +contract ExecutionLayerWithdrawal is GeneralPurposeToConsensusLayerRequest { + event WithdrawalRequestIssued(bytes publicKey, uint64 amount); + + bytes public lastPublicKey; + uint64 public lastAmount; + + function handleRequest(bytes calldata data) internal override { + // parameters should consist of 48 bytes for public key and 8 bytes for uint64 amount + require(data.length == 56, "Invalid Withdrawal data"); + lastPublicKey = data[:48]; + lastAmount = uint64(bytes8(data[48:])); + emit WithdrawalRequestIssued(lastPublicKey, lastAmount); + } +} diff --git a/contracts/contracts/mocks/beacon/GeneralPurposeToConsensusLayerRequest.sol b/contracts/contracts/mocks/beacon/GeneralPurposeToConsensusLayerRequest.sol new file mode 100644 index 0000000000..be117cb18d --- /dev/null +++ b/contracts/contracts/mocks/beacon/GeneralPurposeToConsensusLayerRequest.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +abstract contract GeneralPurposeToConsensusLayerRequest { + // solhint-disable no-complex-fallback + fallback() external payable { + // fee requested + if (msg.data.length == 0) { + uint256 fee = _fee(); + // solhint-disable-next-line no-inline-assembly + assembly { + // Return a uint256 value + mstore(0, fee) + return(0, 32) // Return 32 bytes from memory + } + } + + // else handle request + handleRequest(msg.data); + } + + /*************************************** + Abstract + ****************************************/ + + function _fee() internal virtual returns (uint256) { + return 1; + } + + function handleRequest(bytes calldata data) internal virtual; +} diff --git a/contracts/contracts/mocks/beacon/MockBeaconProofs.sol b/contracts/contracts/mocks/beacon/MockBeaconProofs.sol new file mode 100644 index 0000000000..bed394cb57 --- /dev/null +++ b/contracts/contracts/mocks/beacon/MockBeaconProofs.sol @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { IBeaconProofs } from "../../interfaces/IBeaconProofs.sol"; + +// solhint-disable no-unused-vars + +/** + * @title Mock contract for test purposes verifying Merkle proofs + * @author Origin Protocol Inc + */ +contract MockBeaconProofs is IBeaconProofs { + /// @dev Number of bytes in the proof to the first pending deposit. + /// 37 witness hashes of 32 bytes each concatenated together. + /// BeaconBlock.state.PendingDeposits[0] + /// 37 * 32 bytes = 1184 bytes + uint256 internal constant FIRST_PENDING_DEPOSIT_PROOF_LENGTH = 1184; + + uint256 internal constant DEFAULT_VALIDATOR_BALANCE_GWEI = 33 ether / 1e9; + // mapping of validator indexes to validator balances + mapping(uint40 => uint256) public validatorBalances; + + function setValidatorBalance(uint40 index, uint256 validatorBalanceGwei) + external + { + // set special max value instead of 0 + if (validatorBalanceGwei == 0) { + validatorBalances[index] = type(uint256).max; + } else { + validatorBalances[index] = validatorBalanceGwei; + } + } + + /// @notice Verifies the validator index is for the given validator public key. + /// Also verify the validator's withdrawal credential points to the withdrawal address. + /// BeaconBlock.state.validators[validatorIndex].pubkey + /// @param beaconBlockRoot The root of the beacon block + /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format + /// @param proof The merkle proof for the validator public key to the beacon block root. + /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node. + /// @param validatorIndex The validator index + /// @param withdrawalCredentials a value containing the validator type and withdrawal address. + function verifyValidator( + bytes32 beaconBlockRoot, + bytes32 pubKeyHash, + bytes calldata proof, + uint40 validatorIndex, + bytes32 withdrawalCredentials + ) external view { + // always pass + } + + function verifyValidatorWithdrawable( + bytes32 beaconBlockRoot, + uint40 validatorIndex, + uint64 withdrawableEpoch, + bytes calldata withdrawableEpochProof + ) external view { + // always pass + } + + /// @notice Verifies the balances container to the beacon block root + /// BeaconBlock.state.balances + /// @param beaconBlockRoot The root of the beacon block + /// @param balancesContainerRoot The merkle root of the the balances container + /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root. + /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node. + function verifyBalancesContainer( + bytes32 beaconBlockRoot, + bytes32 balancesContainerRoot, + bytes calldata balancesContainerProof + ) external view { + // always pass + } + + /// @notice Verifies the validator balance to the root of the Balances container. + /// @param balancesContainerRoot The merkle root of the Balances container. + /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances. + /// @param balanceProof The merkle proof for the validator balance to the Balances container root. + /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node. + /// @param validatorIndex The validator index to verify the balance for + /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index + function verifyValidatorBalance( + bytes32 balancesContainerRoot, + bytes32 validatorBalanceLeaf, + bytes calldata balanceProof, + uint40 validatorIndex + ) external view returns (uint256 validatorBalanceGwei) { + uint256 validatorBalance = validatorBalances[validatorIndex]; + + // special setting representing 0 balance + if (validatorBalance == type(uint256).max) { + return 0; + } + // validator balance not set by the test cases + else if (validatorBalance == 0) { + return DEFAULT_VALIDATOR_BALANCE_GWEI; + } + + return validatorBalance; + } + + function verifyPendingDepositsContainer( + bytes32 beaconBlockRoot, + bytes32 pendingDepositsContainerRoot, + bytes calldata proof + ) external view { + // always pass + } + + function verifyPendingDeposit( + bytes32 pendingDepositsContainerRoot, + bytes32 pendingDepositRoot, + bytes calldata proof, + uint32 pendingDepositIndex + ) external view { + // always pass + } + + /// @notice If the deposit queue is not empty, + /// verify the slot of the first pending deposit to the beacon block root. + /// BeaconBlock.state.pendingDeposits[0].slot + /// If the deposit queue is empty, verify the root of the first pending deposit is empty + /// BeaconBlock.state.PendingDeposits[0] + /// @param beaconBlockRoot The root of the beacon block. + /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue. + /// Can be anything if the deposit queue is empty. + /// @param firstPendingDepositSlotProof The merkle proof to the beacon block root. Can be either: + /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty. + /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty. + /// The 32 byte witness hashes are concatenated together starting from the leaf node. + /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise. + function verifyFirstPendingDeposit( + bytes32 beaconBlockRoot, + uint64 slot, + bytes calldata firstPendingDepositSlotProof + ) external view returns (bool isEmptyDepositQueue) { + if ( + firstPendingDepositSlotProof.length == + FIRST_PENDING_DEPOSIT_PROOF_LENGTH + ) { + isEmptyDepositQueue = true; + } + } + + function merkleizePendingDeposit( + bytes32 pubKeyHash, + bytes calldata withdrawalCredentials, + uint64 amountGwei, + bytes calldata signature, + uint64 slot + ) external pure returns (bytes32) { + return + keccak256( + abi.encodePacked( + pubKeyHash, + withdrawalCredentials, + amountGwei, + signature, + slot + ) + ); + } + + function merkleizeSignature(bytes calldata signature) + external + pure + returns (bytes32 root) + { + return keccak256(abi.encodePacked(signature)); + } +} diff --git a/contracts/contracts/mocks/beacon/MockBeaconRoots.sol b/contracts/contracts/mocks/beacon/MockBeaconRoots.sol new file mode 100644 index 0000000000..92e29b7aed --- /dev/null +++ b/contracts/contracts/mocks/beacon/MockBeaconRoots.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { BeaconRoots } from "../../beacon/BeaconRoots.sol"; + +contract MockBeaconRoots { + // Mapping to simulate the ring buffer: timestamp => beacon block root + mapping(uint256 => bytes32) internal _beaconRoots; + + // Event to log when a new root is set (for testing) + event RootSet(uint256 indexed timestamp, bytes32 root); + + // Fallback function to handle raw 32-byte timestamp input + // solhint-disable no-complex-fallback + fallback() external { + // Ensure input is exactly 32 bytes (big-endian encoded timestamp) + require(msg.data.length == 32, "Input must be 32 bytes"); + + // Decode the 32-byte input as a uint256 timestamp (big-endian) + uint256 timestamp = abi.decode(msg.data, (uint256)); + + // Don't do any validation of timestamp so we can test any block + + // Retrieve the root. Will return bytes32(0) if not set. + bytes32 root = _beaconRoots[timestamp]; + + // Return the 32-byte root directly + // solhint-disable-next-line no-inline-assembly + assembly { + mstore(0, root) + return(0, 32) + } + } + + // Mock function to set a beacon block root (for testing) + function setBeaconRoot(uint256 timestamp, bytes32 root) external { + require(timestamp > 0, "Invalid timestamp"); + require(root != bytes32(0), "Invalid root"); + + // Store the root at the given timestamp + _beaconRoots[timestamp] = root; + + emit RootSet(timestamp, root); + } + + function setBeaconRoot(bytes32 root) external { + require(root != bytes32(0), "Invalid root"); + + // Store the root at the given timestamp + _beaconRoots[block.timestamp] = root; + + emit RootSet(block.timestamp, root); + } + + function parentBlockRoot(uint64 timestamp) + external + view + returns (bytes32 parentRoot) + { + return BeaconRoots.parentBlockRoot(timestamp); + } + + function latestBlockRoot() + external + view + returns (bytes32 parentRoot, uint64 timestamp) + { + timestamp = uint64(block.timestamp); + parentRoot = BeaconRoots.parentBlockRoot(timestamp); + } +} diff --git a/contracts/contracts/proxies/Proxies.sol b/contracts/contracts/proxies/Proxies.sol index 214fb05987..76eb607eb0 100644 --- a/contracts/contracts/proxies/Proxies.sol +++ b/contracts/contracts/proxies/Proxies.sol @@ -311,3 +311,12 @@ contract OUSDCurveAMOProxy is InitializeGovernedUpgradeabilityProxy { contract OETHCurveAMOProxy is InitializeGovernedUpgradeabilityProxy { } + +/** + * @notice CompoundingStakingSSVStrategyProxy delegates calls to a CompoundingStakingSSVStrategy implementation + */ +contract CompoundingStakingSSVStrategyProxy is + InitializeGovernedUpgradeabilityProxy +{ + +} diff --git a/contracts/contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol b/contracts/contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol new file mode 100644 index 0000000000..dee216cb34 --- /dev/null +++ b/contracts/contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +import { InitializableAbstractStrategy } from "../../utils/InitializableAbstractStrategy.sol"; +import { IWETH9 } from "../../interfaces/IWETH9.sol"; +import { CompoundingValidatorManager } from "./CompoundingValidatorManager.sol"; + +/// @title Compounding Staking SSV Strategy +/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network +/// @author Origin Protocol Inc +contract CompoundingStakingSSVStrategy is + CompoundingValidatorManager, + InitializableAbstractStrategy +{ + /// @notice SSV ERC20 token that serves as a payment for operating SSV validators + address public immutable SSV_TOKEN; + + // For future use + uint256[50] private __gap; + + /// @param _baseConfig Base strategy config with + /// `platformAddress` not used so empty address + /// `vaultAddress` the address of the OETH Vault contract + /// @param _wethAddress Address of the WETH Token contract + /// @param _ssvToken Address of the SSV Token contract + /// @param _ssvNetwork Address of the SSV Network contract + /// @param _beaconChainDepositContract Address of the beacon chain deposit contract + /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data + /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis. + constructor( + BaseStrategyConfig memory _baseConfig, + address _wethAddress, + address _ssvToken, + address _ssvNetwork, + address _beaconChainDepositContract, + address _beaconProofs, + uint64 _beaconGenesisTimestamp + ) + InitializableAbstractStrategy(_baseConfig) + CompoundingValidatorManager( + _wethAddress, + _baseConfig.vaultAddress, + _beaconChainDepositContract, + _ssvNetwork, + _beaconProofs, + _beaconGenesisTimestamp + ) + { + SSV_TOKEN = _ssvToken; + + // Make sure nobody owns the implementation contract + _setGovernor(address(0)); + } + + /// @notice Set up initial internal state including + /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract + /// @param _rewardTokenAddresses Not used so empty array + /// @param _assets Not used so empty array + /// @param _pTokens Not used so empty array + function initialize( + address[] memory _rewardTokenAddresses, + address[] memory _assets, + address[] memory _pTokens + ) external onlyGovernor initializer { + InitializableAbstractStrategy._initialize( + _rewardTokenAddresses, + _assets, + _pTokens + ); + + safeApproveAllTokens(); + } + + /// @notice Unlike other strategies, this does not deposit assets into the underlying platform. + /// It just checks the asset is WETH and emits the Deposit event. + /// To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used. + /// @param _asset Address of the WETH token. + /// @param _amount Amount of WETH that was transferred to the strategy by the vault. + function deposit(address _asset, uint256 _amount) + external + override + onlyVault + nonReentrant + { + require(_asset == WETH, "Unsupported asset"); + require(_amount > 0, "Must deposit something"); + + // Account for the new WETH + depositedWethAccountedFor += _amount; + + emit Deposit(_asset, address(0), _amount); + } + + /// @notice Unlike other strategies, this does not deposit assets into the underlying platform. + /// It just emits the Deposit event. + /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used. + function depositAll() external override onlyVault nonReentrant { + uint256 wethBalance = IERC20(WETH).balanceOf(address(this)); + uint256 newWeth = wethBalance - depositedWethAccountedFor; + + if (newWeth > 0) { + // Account for the new WETH + depositedWethAccountedFor = wethBalance; + + emit Deposit(WETH, address(0), newWeth); + } + } + + /// @notice Withdraw ETH and WETH from this strategy contract. + /// @param _recipient Address to receive withdrawn assets. + /// @param _asset Address of the WETH token. + /// @param _amount Amount of WETH to withdraw. + function withdraw( + address _recipient, + address _asset, + uint256 _amount + ) external override onlyVault nonReentrant { + require(_asset == WETH, "Unsupported asset"); + + _withdraw(_recipient, _amount, address(this).balance); + } + + function _withdraw( + address _recipient, + uint256 _withdrawAmount, + uint256 _ethBalance + ) internal { + require(_withdrawAmount > 0, "Must withdraw something"); + require(_recipient != address(0), "Must specify recipient"); + + // Convert any ETH from validator partial withdrawals, exits + // or execution rewards to WETH and do the necessary accounting. + if (_ethBalance > 0) _convertEthToWeth(_ethBalance); + + // Transfer WETH to the recipient and do the necessary accounting. + _transferWeth(_withdrawAmount, _recipient); + + emit Withdrawal(WETH, address(0), _withdrawAmount); + } + + /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from + /// execution rewards in this strategy to the vault. + /// This does not withdraw from the validators. That has to be done separately with the + /// `validatorWithdrawal` operation. + function withdrawAll() external override onlyVaultOrGovernor nonReentrant { + uint256 ethBalance = address(this).balance; + uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) + + ethBalance; + + if (withdrawAmount > 0) { + _withdraw(vaultAddress, withdrawAmount, ethBalance); + } + } + + /// @notice Accounts for all the assets managed by this strategy which includes: + /// 1. The current WETH in this strategy contract + /// 2. The last verified ETH balance, total deposits and total validator balances + /// @param _asset Address of WETH asset. + /// @return balance Total value in ETH + function checkBalance(address _asset) + external + view + override + returns (uint256 balance) + { + require(_asset == WETH, "Unsupported asset"); + + // Load the last verified balance from the storage + // and add to the latest WETH balance of this strategy. + balance = + lastVerifiedEthBalance + + IWETH9(WETH).balanceOf(address(this)); + } + + /// @notice Returns bool indicating whether asset is supported by the strategy. + /// @param _asset The address of the WETH token. + function supportsAsset(address _asset) public view override returns (bool) { + return _asset == WETH; + } + + /// @notice Approves the SSV Network contract to transfer SSV tokens for validator registration. + function safeApproveAllTokens() public override { + // Approves the SSV Network contract to transfer SSV tokens when validators are registered + IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max); + } + + /** + * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting + * like it did in the legacy NativeStakingStrategy. + * The new ETH will be accounted for in `checkBalance` after the next snapBalances and verifyBalances txs. + */ + receive() external payable {} + + /*************************************** + Internal functions + ****************************************/ + + /// @notice is not supported for this strategy as there is no platform token. + function setPTokenAddress(address, address) external pure override { + revert("Unsupported function"); + } + + /// @notice is not supported for this strategy as there is no platform token. + function removePToken(uint256) external pure override { + revert("Unsupported function"); + } + + /// @dev This strategy does not use a platform token like the old Aave and Compound strategies. + function _abstractSetPToken(address _asset, address) internal override {} + + /// @dev Consensus rewards are compounded to the validator's balance instead of being + /// swept to this strategy contract. + /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract. + /// Withdrawals from validators also accumulate as ETH in this strategy contract. + /// It's too complex to separate the rewards from withdrawals so this function is not implemented. + /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can now regulate + /// the increase in assets. + function _collectRewardTokens() internal pure override { + revert("Unsupported function"); + } +} diff --git a/contracts/contracts/strategies/NativeStaking/CompoundingStakingView.sol b/contracts/contracts/strategies/NativeStaking/CompoundingStakingView.sol new file mode 100644 index 0000000000..b72dc9b903 --- /dev/null +++ b/contracts/contracts/strategies/NativeStaking/CompoundingStakingView.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { CompoundingValidatorManager } from "./CompoundingValidatorManager.sol"; + +/** + * @title Viewing contract for the Compounding Staking Strategy. + * @author Origin Protocol Inc + */ +contract CompoundingStakingStrategyView { + /// @notice The address of the Compounding Staking Strategy contract + CompoundingValidatorManager public immutable stakingStrategy; + + constructor(address _stakingStrategy) { + stakingStrategy = CompoundingValidatorManager(_stakingStrategy); + } + + struct ValidatorView { + bytes32 pubKeyHash; + uint64 index; + CompoundingValidatorManager.ValidatorState state; + } + + struct DepositView { + bytes32 pendingDepositRoot; + bytes32 pubKeyHash; + uint64 amountGwei; + uint64 slot; + } + + /// @notice Returns the strategy's active validators. + /// These are the ones that have been verified and have a non-zero balance. + /// @return validators An array of `ValidatorView` containing the public key hash, validator index and state. + function getVerifiedValidators() + external + view + returns (ValidatorView[] memory validators) + { + uint256 validatorCount = stakingStrategy.verifiedValidatorsLength(); + validators = new ValidatorView[](validatorCount); + for (uint256 i = 0; i < validatorCount; ++i) { + bytes32 pubKeyHash = stakingStrategy.verifiedValidators(i); + ( + CompoundingValidatorManager.ValidatorState state, + uint64 index + ) = stakingStrategy.validator(pubKeyHash); + validators[i] = ValidatorView({ + pubKeyHash: pubKeyHash, + index: index, + state: state + }); + } + } + + /// @notice Returns the deposits that are still to be verified. + /// These may or may not have been processed by the beacon chain. + /// @return pendingDeposits An array of `DepositView` containing the deposit ID, public key hash, + /// amount in Gwei and the slot of the deposit. + function getPendingDeposits() + external + view + returns (DepositView[] memory pendingDeposits) + { + uint256 depositsCount = stakingStrategy.depositListLength(); + pendingDeposits = new DepositView[](depositsCount); + for (uint256 i = 0; i < depositsCount; ++i) { + ( + bytes32 pubKeyHash, + uint64 amountGwei, + uint64 slot, + , + + ) = stakingStrategy.deposits(stakingStrategy.depositList(i)); + pendingDeposits[i] = DepositView({ + pendingDepositRoot: stakingStrategy.depositList(i), + pubKeyHash: pubKeyHash, + amountGwei: amountGwei, + slot: slot + }); + } + } +} diff --git a/contracts/contracts/strategies/NativeStaking/CompoundingValidatorManager.sol b/contracts/contracts/strategies/NativeStaking/CompoundingValidatorManager.sol new file mode 100644 index 0000000000..f0d030c4a6 --- /dev/null +++ b/contracts/contracts/strategies/NativeStaking/CompoundingValidatorManager.sol @@ -0,0 +1,1279 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; +import { Math } from "@openzeppelin/contracts/utils/math/Math.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { Pausable } from "@openzeppelin/contracts/security/Pausable.sol"; +import { Governable } from "../../governance/Governable.sol"; +import { IDepositContract } from "../../interfaces/IDepositContract.sol"; +import { IWETH9 } from "../../interfaces/IWETH9.sol"; +import { ISSVNetwork, Cluster } from "../../interfaces/ISSVNetwork.sol"; +import { BeaconRoots } from "../../beacon/BeaconRoots.sol"; +import { PartialWithdrawal } from "../../beacon/PartialWithdrawal.sol"; +import { IBeaconProofs } from "../../interfaces/IBeaconProofs.sol"; + +/** + * @title Validator lifecycle management contract + * @notice This contract implements all the required functionality to + * register, deposit, withdraw, exit and remove validators. + * @author Origin Protocol Inc + */ +abstract contract CompoundingValidatorManager is Governable, Pausable { + using SafeERC20 for IERC20; + + /// @dev The amount of ETH in wei that is required for a deposit to a new validator. + uint256 internal constant DEPOSIT_AMOUNT_WEI = 1 ether; + /// @dev Validator balances over this amount will eventually become active on the beacon chain. + /// Due to hysteresis, if the effective balance is 31 ETH, the actual balance + /// must rise to 32.25 ETH to trigger an effective balance update to 32 ETH. + /// https://eth2book.info/capella/part2/incentives/balances/#hysteresis + uint256 internal constant MIN_ACTIVATION_BALANCE_GWEI = 32.25 ether / 1e9; + /// @dev The maximum number of deposits that are waiting to be verified as processed on the beacon chain. + uint256 internal constant MAX_DEPOSITS = 12; + /// @dev The maximum number of validators that can be verified. + uint256 internal constant MAX_VERIFIED_VALIDATORS = 48; + /// @dev The default withdrawable epoch value on the Beacon chain. + /// A value in the far future means the validator is not exiting. + uint64 internal constant FAR_FUTURE_EPOCH = type(uint64).max; + /// @dev The number of seconds between each beacon chain slot. + uint64 internal constant SLOT_DURATION = 12; + /// @dev The number of slots in each beacon chain epoch. + uint64 internal constant SLOTS_PER_EPOCH = 32; + /// @dev Minimum time in seconds to allow snapped balances to be verified. + /// Set to 35 slots which is 3 slots more than 1 epoch (32 slots). Deposits get processed + /// once per epoch. This larger than 1 epoch delay should achieve that `snapBalances` sometimes + /// get called in the middle (or towards the end) of the epoch. Giving the off-chain script + /// sufficient time after the end of the epoch to prepare the proofs and call `verifyBalances`. + /// This is considering a malicious actor would keep calling `snapBalances` as frequent as possible + /// to disturb our operations. + uint64 internal constant SNAP_BALANCES_DELAY = 35 * SLOT_DURATION; + + /// @notice The address of the Wrapped ETH (WETH) token contract + address internal immutable WETH; + /// @notice The address of the beacon chain deposit contract + address internal immutable BEACON_CHAIN_DEPOSIT_CONTRACT; + /// @notice The address of the SSV Network contract used to interface with + address public immutable SSV_NETWORK; + /// @notice Address of the OETH Vault proxy contract + address internal immutable VAULT_ADDRESS; + /// @notice Address of the Beacon Proofs contract that verifies beacon chain data + address public immutable BEACON_PROOFS; + /// @notice The timestamp of the Beacon chain genesis. + /// @dev this is different on Testnets like Hoodi so is set at deployment time. + uint64 internal immutable BEACON_GENESIS_TIMESTAMP; + + /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators + address public validatorRegistrator; + + /// @notice Deposit data for new compounding validators. + /// @dev A `VERIFIED` deposit can mean 3 separate things: + /// - a deposit has been processed by the beacon chain and shall be included in the + /// balance of the next verifyBalances call + /// - a deposit has been done to a slashed validator and has probably been recovered + /// back to this strategy. Probably because we can not know for certain. This contract + /// only detects when the validator has passed its withdrawal epoch. It is close to impossible + /// to prove with Merkle Proofs that the postponed deposit this contract is responsible for + /// creating is not present anymore in BeaconChain.state.pending_deposits. This in effect + /// means that there might be a period where this contract thinks the deposit has been already + /// returned as ETH balance before it happens. This will result in some days (or weeks) + /// -> depending on the size of deposit queue of showing a deficit when calling `checkBalance`. + /// As this only offsets the yield and doesn't cause a critical double-counting we are not addressing + /// this issue. + /// - A deposit has been done to the validator, but our deposit has been front run by a malicious + /// actor. Funds in the deposit this contract makes are not recoverable. + enum DepositStatus { + UNKNOWN, // default value + PENDING, // deposit is pending and waiting to be verified + VERIFIED // deposit has been verified + } + + /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format + /// @param amountGwei Amount of ETH in gwei that has been deposited to the beacon chain deposit contract + /// @param slot The beacon chain slot number when the deposit has been made + /// @param depositIndex The index of the deposit in the list of active deposits + /// @param status The status of the deposit, either UNKNOWN, PENDING or VERIFIED + struct DepositData { + bytes32 pubKeyHash; + uint64 amountGwei; + uint64 slot; + uint32 depositIndex; + DepositStatus status; + } + /// @notice Restricts to only one deposit to an unverified validator at a time. + /// This is to limit front-running attacks of deposits to the beacon chain contract. + /// + /// @dev The value is set to true when a deposit to a new validator has been done that has + /// not yet be verified. + bool public firstDeposit; + /// @notice Mapping of the pending deposit roots to the deposit data + mapping(bytes32 => DepositData) public deposits; + /// @notice List of strategy deposit IDs to a validator. + /// The ID is the merkle root of the pending deposit data which is unique for each validator, amount and block. + /// Duplicate pending deposit roots are prevented so can be used as an identifier to each strategy deposit. + /// The list can be for deposits waiting to be verified as processed on the beacon chain, + /// or deposits that have been verified to an exiting validator and is now waiting for the + /// validator's balance to be swept. + /// The list may not be ordered by time of deposit. + /// Removed deposits will move the last deposit to the removed index. + bytes32[] public depositList; + + enum ValidatorState { + NON_REGISTERED, // validator is not registered on the SSV network + REGISTERED, // validator is registered on the SSV network + STAKED, // validator has funds staked + VERIFIED, // validator has been verified to exist on the beacon chain + ACTIVE, // The validator balance is at least 32 ETH. The validator may not yet be active on the beacon chain. + EXITING, // The validator has been requested to exit + EXITED, // The validator has been verified to have a zero balance + REMOVED, // validator has funds withdrawn to this strategy contract and is removed from the SSV + INVALID // The validator has been front-run and the withdrawal address is not this strategy + } + + // Validator data + struct ValidatorData { + ValidatorState state; // The state of the validator known to this contract + uint40 index; // The index of the validator on the beacon chain + } + /// @notice List of validator public key hashes that have been verified to exist on the beacon chain. + /// These have had a deposit processed and the validator's balance increased. + /// Validators will be removed from this list when its verified they have a zero balance. + bytes32[] public verifiedValidators; + /// @notice Mapping of the hash of the validator's public key to the validator state and index. + /// Uses the Beacon chain hashing for BLSPubkey which is sha256(abi.encodePacked(validator.pubkey, bytes16(0))) + mapping(bytes32 => ValidatorData) public validator; + + /// @param blockRoot Beacon chain block root of the snapshot + /// @param timestamp Timestamp of the snapshot + /// @param ethBalance The balance of ETH in the strategy contract at the snapshot + struct Balances { + bytes32 blockRoot; + uint64 timestamp; + uint128 ethBalance; + } + /// @notice Mapping of the block root to the balances at that slot + Balances public snappedBalance; + /// @notice The last verified ETH balance of the strategy + uint256 public lastVerifiedEthBalance; + + /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately + /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators. + /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been + /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track + /// of WETH that has already been accounted for. + /// This value represents the amount of WETH balance of this contract that has already been accounted for by the + /// deposit events. + /// It is important to note that this variable is not concerned with WETH that is a result of full/partial + /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to + /// be staked. + uint256 public depositedWethAccountedFor; + + // For future use + uint256[41] private __gap; + + event RegistratorChanged(address indexed newAddress); + event FirstDepositReset(); + event SSVValidatorRegistered( + bytes32 indexed pubKeyHash, + uint64[] operatorIds + ); + event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds); + event ETHStaked( + bytes32 indexed pubKeyHash, + bytes32 indexed pendingDepositRoot, + bytes pubKey, + uint256 amountWei + ); + event ValidatorVerified( + bytes32 indexed pubKeyHash, + uint40 indexed validatorIndex + ); + event ValidatorInvalid(bytes32 indexed pubKeyHash); + event DepositVerified( + bytes32 indexed pendingDepositRoot, + uint256 amountWei + ); + event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei); + event BalancesSnapped(bytes32 indexed blockRoot, uint256 ethBalance); + event BalancesVerified( + uint64 indexed timestamp, + uint256 totalDepositsWei, + uint256 totalValidatorBalance, + uint256 ethBalance + ); + + /// @dev Throws if called by any account other than the Registrator + modifier onlyRegistrator() { + require(msg.sender == validatorRegistrator, "Not Registrator"); + _; + } + + /// @dev Throws if called by any account other than the Registrator or Governor + modifier onlyRegistratorOrGovernor() { + require( + msg.sender == validatorRegistrator || isGovernor(), + "Not Registrator or Governor" + ); + _; + } + + /// @param _wethAddress Address of the Erc20 WETH Token contract + /// @param _vaultAddress Address of the Vault + /// @param _beaconChainDepositContract Address of the beacon chain deposit contract + /// @param _ssvNetwork Address of the SSV Network contract + /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data + /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis. + constructor( + address _wethAddress, + address _vaultAddress, + address _beaconChainDepositContract, + address _ssvNetwork, + address _beaconProofs, + uint64 _beaconGenesisTimestamp + ) { + WETH = _wethAddress; + BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract; + SSV_NETWORK = _ssvNetwork; + VAULT_ADDRESS = _vaultAddress; + BEACON_PROOFS = _beaconProofs; + BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp; + + require( + block.timestamp > _beaconGenesisTimestamp, + "Invalid genesis timestamp" + ); + } + + /** + * + * Admin Functions + * + */ + + /// @notice Set the address of the registrator which can register, exit and remove validators + function setRegistrator(address _address) external onlyGovernor { + validatorRegistrator = _address; + emit RegistratorChanged(_address); + } + + /// @notice Reset the `firstDeposit` flag to false so deposits to unverified validators can be made again. + function resetFirstDeposit() external onlyGovernor { + require(firstDeposit, "No first deposit"); + + firstDeposit = false; + + emit FirstDepositReset(); + } + + function pause() external onlyRegistratorOrGovernor { + _pause(); + } + + function unPause() external onlyGovernor { + _unpause(); + } + + /** + * + * Validator Management + * + */ + + /// @notice Registers a single validator in a SSV Cluster. + /// Only the Registrator can call this function. + /// @param publicKey The public key of the validator + /// @param operatorIds The operator IDs of the SSV Cluster + /// @param sharesData The shares data for the validator + /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster + /// @param cluster The SSV cluster details including the validator count and SSV balance + // slither-disable-start reentrancy-no-eth + function registerSsvValidator( + bytes calldata publicKey, + uint64[] calldata operatorIds, + bytes calldata sharesData, + uint256 ssvAmount, + Cluster calldata cluster + ) external onlyRegistrator whenNotPaused { + // Hash the public key using the Beacon Chain's format + bytes32 pubKeyHash = _hashPubKey(publicKey); + // Check each public key has not already been used + require( + validator[pubKeyHash].state == ValidatorState.NON_REGISTERED, + "Validator already registered" + ); + + // Store the validator state as registered + validator[pubKeyHash].state = ValidatorState.REGISTERED; + + ISSVNetwork(SSV_NETWORK).registerValidator( + publicKey, + operatorIds, + sharesData, + ssvAmount, + cluster + ); + + emit SSVValidatorRegistered(pubKeyHash, operatorIds); + } + + // slither-disable-end reentrancy-no-eth + + struct ValidatorStakeData { + bytes pubkey; + bytes signature; + bytes32 depositDataRoot; + } + + /// @notice Stakes WETH in this strategy to a compounding validator. + /// The first deposit to a new validator, the amount must be 1 ETH. + /// Another deposit of at least 31 ETH is required for the validator to be activated. + /// This second deposit has to be done after the validator has been verified. + /// Does not convert any ETH sitting in this strategy to WETH. + /// There can not be two deposits to the same validator in the same block for the same amount. + /// Function is pausable so in case a run-away Registrator can be prevented from continuing + /// to deposit funds to slashed or undesired validators. + /// @param validatorStakeData validator data needed to stake. + /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot. + /// Only the registrator can call this function. + /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei. + // slither-disable-start reentrancy-eth,reentrancy-no-eth + function stakeEth( + ValidatorStakeData calldata validatorStakeData, + uint64 depositAmountGwei + ) external onlyRegistrator whenNotPaused { + uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei; + // Check there is enough WETH from the deposits sitting in this strategy contract + // There could be ETH from withdrawals but we'll ignore that. If it's really needed + // the ETH can be withdrawn and then deposited back to the strategy. + require( + depositAmountWei <= IWETH9(WETH).balanceOf(address(this)), + "Insufficient WETH" + ); + require(depositList.length < MAX_DEPOSITS, "Max deposits"); + + // Convert required ETH from WETH and do the necessary accounting + _convertWethToEth(depositAmountWei); + + // Hash the public key using the Beacon Chain's hashing for BLSPubkey + bytes32 pubKeyHash = _hashPubKey(validatorStakeData.pubkey); + ValidatorState currentState = validator[pubKeyHash].state; + // Can only stake to a validator that has been registered, verified or active. + // Can not stake to a validator that has been staked but not yet verified. + require( + (currentState == ValidatorState.REGISTERED || + currentState == ValidatorState.VERIFIED || + currentState == ValidatorState.ACTIVE), + "Not registered or verified" + ); + require(depositAmountWei >= 1 ether, "Deposit too small"); + if (currentState == ValidatorState.REGISTERED) { + // Can only have one pending deposit to an unverified validator at a time. + // This is to limit front-running deposit attacks to a single deposit. + // The exiting deposit needs to be verified before another deposit can be made. + // If there was a front-running attack, the validator needs to be verified as invalid + // and the Governor calls `resetFirstDeposit` to set `firstDeposit` to false. + require(!firstDeposit, "Existing first deposit"); + // Limits the amount of ETH that can be at risk from a front-running deposit attack. + require( + depositAmountWei == DEPOSIT_AMOUNT_WEI, + "Invalid first deposit amount" + ); + // Limits the number of validator balance proofs to verifyBalances + require( + verifiedValidators.length + 1 <= MAX_VERIFIED_VALIDATORS, + "Max validators" + ); + + // Flag a deposit to an unverified validator so no other deposits can be made + // to an unverified validator. + firstDeposit = true; + validator[pubKeyHash].state = ValidatorState.STAKED; + } + + /* 0x02 to indicate that withdrawal credentials are for a compounding validator + * that was introduced with the Pectra upgrade. + * bytes11(0) to fill up the required zeros + * remaining bytes20 are for the address + */ + bytes memory withdrawalCredentials = abi.encodePacked( + bytes1(0x02), + bytes11(0), + address(this) + ); + + /// After the Pectra upgrade the validators have a new restriction when proposing + /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block + /// forward. Each slot is created at strict 12 second intervals and those slots can + /// either have blocks attached to them or not. This way using the block.timestamp + /// the slot number can easily be calculated. + uint64 depositSlot = (SafeCast.toUint64(block.timestamp) - + BEACON_GENESIS_TIMESTAMP) / SLOT_DURATION; + + // Calculate the merkle root of the beacon chain pending deposit data. + // This is used as the unique ID of the deposit. + bytes32 pendingDepositRoot = IBeaconProofs(BEACON_PROOFS) + .merkleizePendingDeposit( + pubKeyHash, + withdrawalCredentials, + depositAmountGwei, + validatorStakeData.signature, + depositSlot + ); + require( + deposits[pendingDepositRoot].status == DepositStatus.UNKNOWN, + "Duplicate deposit" + ); + + // Store the deposit data for verifyDeposit and verifyBalances + deposits[pendingDepositRoot] = DepositData({ + pubKeyHash: pubKeyHash, + amountGwei: depositAmountGwei, + slot: depositSlot, + depositIndex: SafeCast.toUint32(depositList.length), + status: DepositStatus.PENDING + }); + depositList.push(pendingDepositRoot); + + // Deposit to the Beacon Chain deposit contract. + // This will create a deposit in the beacon chain's pending deposit queue. + IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{ + value: depositAmountWei + }( + validatorStakeData.pubkey, + withdrawalCredentials, + validatorStakeData.signature, + validatorStakeData.depositDataRoot + ); + + emit ETHStaked( + pubKeyHash, + pendingDepositRoot, + validatorStakeData.pubkey, + depositAmountWei + ); + } + + // slither-disable-end reentrancy-eth,reentrancy-no-eth + + /// @notice Request a full or partial withdrawal from a validator. + /// A zero amount will trigger a full withdrawal. + /// If the remaining balance is < 32 ETH then only the amount in excess of 32 ETH will be withdrawn. + /// Only the Registrator can call this function. + /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee. + /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any. + /// If no ETH balance, the tx will revert. + /// @param publicKey The public key of the validator + /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei. + /// A zero amount will trigger a full withdrawal. + // slither-disable-start reentrancy-no-eth + function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei) + external + payable + onlyRegistrator + { + // Hash the public key using the Beacon Chain's format + bytes32 pubKeyHash = _hashPubKey(publicKey); + ValidatorData memory validatorDataMem = validator[pubKeyHash]; + // Validator full withdrawal could be denied due to multiple reasons: + // - the validator has not been activated or active long enough + // (current_epoch < activation_epoch + SHARD_COMMITTEE_PERIOD) + // - the validator has pending balance to withdraw from a previous partial withdrawal request + // + // Meaning that the on-chain to beacon chain full withdrawal request could fail. Instead + // of adding complexity of verifying if a validator is eligible for a full exit, we allow + // multiple full withdrawal requests per validator. + require( + validatorDataMem.state == ValidatorState.ACTIVE || + validatorDataMem.state == ValidatorState.EXITING, + "Validator not active/exiting" + ); + + // If a full withdrawal (validator exit) + if (amountGwei == 0) { + // For each staking strategy's deposits + uint256 depositsCount = depositList.length; + for (uint256 i = 0; i < depositsCount; ++i) { + bytes32 pendingDepositRoot = depositList[i]; + // Check there is no pending deposits to the exiting validator + require( + pubKeyHash != deposits[pendingDepositRoot].pubKeyHash, + "Pending deposit" + ); + } + + // Store the validator state as exiting so no more deposits can be made to it. + // This may already be EXITING if the previous exit request failed. eg the validator + // was not active long enough. + validator[pubKeyHash].state = ValidatorState.EXITING; + } + + // Do not remove from the list of verified validators. + // This is done in the verifyBalances function once the validator's balance has been verified to be zero. + // The validator state will be set to EXITED in the verifyBalances function. + + PartialWithdrawal.request(publicKey, amountGwei); + + emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei); + } + + // slither-disable-end reentrancy-no-eth + + /// @notice Remove the validator from the SSV Cluster after: + /// - the validator has been exited from `validatorWithdrawal` or slashed + /// - the validator has incorrectly registered and can not be staked to + /// - the initial deposit was front-run and the withdrawal address is not this strategy's address. + /// Make sure `validatorWithdrawal` is called with a zero amount and the validator has exited the Beacon chain. + /// If removed before the validator has exited the beacon chain will result in the validator being slashed. + /// Only the registrator can call this function. + /// @param publicKey The public key of the validator + /// @param operatorIds The operator IDs of the SSV Cluster + /// @param cluster The SSV cluster details including the validator count and SSV balance + // slither-disable-start reentrancy-no-eth + function removeSsvValidator( + bytes calldata publicKey, + uint64[] calldata operatorIds, + Cluster calldata cluster + ) external onlyRegistrator { + // Hash the public key using the Beacon Chain's format + bytes32 pubKeyHash = _hashPubKey(publicKey); + ValidatorState currentState = validator[pubKeyHash].state; + // Can remove SSV validators that were incorrectly registered and can not be deposited to. + require( + currentState == ValidatorState.REGISTERED || + currentState == ValidatorState.EXITED || + currentState == ValidatorState.INVALID, + "Validator not regd or exited" + ); + + validator[pubKeyHash].state = ValidatorState.REMOVED; + + ISSVNetwork(SSV_NETWORK).removeValidator( + publicKey, + operatorIds, + cluster + ); + + emit SSVValidatorRemoved(pubKeyHash, operatorIds); + } + + /** + * + * SSV Management + * + */ + + // slither-disable-end reentrancy-no-eth + + /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly + /// by the Strategist which is already holding SSV tokens. + + /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators. + /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds. + /// @param operatorIds The operator IDs of the SSV Cluster + /// @param ssvAmount The amount of SSV tokens to be withdrawn from the SSV cluster + /// @param cluster The SSV cluster details including the validator count and SSV balance + function withdrawSSV( + uint64[] memory operatorIds, + uint256 ssvAmount, + Cluster memory cluster + ) external onlyGovernor { + ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster); + } + + /** + * + * Beacon Chain Proofs + * + */ + + /// @notice Verifies a validator's index to its public key. + /// Adds to the list of verified validators if the validator's withdrawal address is this strategy's address. + /// Marks the validator as invalid and removes the deposit if the withdrawal address is not this strategy's address. + /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot + /// we are verifying. + /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp, + /// which is the beacon block root of the previous block. + /// @param validatorIndex The index of the validator on the beacon chain. + /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format + /// @param withdrawalCredentials contain the validator type and withdrawal address. These can be incorrect and/or + /// malformed. In case of incorrect withdrawalCredentials the validator deposit has been front run + /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root. + /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node. + /// BeaconBlock.state.validators[validatorIndex].pubkey + function verifyValidator( + uint64 nextBlockTimestamp, + uint40 validatorIndex, + bytes32 pubKeyHash, + bytes32 withdrawalCredentials, + bytes calldata validatorPubKeyProof + ) external { + require( + validator[pubKeyHash].state == ValidatorState.STAKED, + "Validator not staked" + ); + + // Get the beacon block root of the slot we are verifying the validator in. + // The parent beacon block root of the next block is the beacon block root of the slot we are verifying. + bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp); + + // Verify the validator index is for the validator with the given public key. + // Also verify the validator's withdrawal credentials + IBeaconProofs(BEACON_PROOFS).verifyValidator( + blockRoot, + pubKeyHash, + validatorPubKeyProof, + validatorIndex, + withdrawalCredentials + ); + + // Store the validator state as verified + validator[pubKeyHash] = ValidatorData({ + state: ValidatorState.VERIFIED, + index: validatorIndex + }); + + bytes32 expectedWithdrawalCredentials = bytes32( + abi.encodePacked(bytes1(0x02), bytes11(0), address(this)) + ); + + // If the initial deposit was front-run and the withdrawal address is not this strategy + // or the validator type is not a compounding validator (0x02) + if (expectedWithdrawalCredentials != withdrawalCredentials) { + // override the validator state + validator[pubKeyHash].state = ValidatorState.INVALID; + + // Find and remove the deposit as the funds can not be recovered + uint256 depositCount = depositList.length; + for (uint256 i = 0; i < depositCount; i++) { + DepositData memory deposit = deposits[depositList[i]]; + if (deposit.pubKeyHash == pubKeyHash) { + // next verifyBalances will correctly account for the loss of a front-run + // deposit. Doing it here accounts for the loss as soon as possible + lastVerifiedEthBalance -= Math.min( + lastVerifiedEthBalance, + uint256(deposit.amountGwei) * 1 gwei + ); + _removeDeposit(depositList[i], deposit); + break; + } + } + + // Leave the `firstDeposit` flag as true so no more deposits to unverified validators can be made. + // The Governor has to reset the `firstDeposit` to false before another deposit to + // an unverified validator can be made. + // The Governor can set a new `validatorRegistrator` if they suspect it has been compromised. + + emit ValidatorInvalid(pubKeyHash); + return; + } + + // Add the new validator to the list of verified validators + verifiedValidators.push(pubKeyHash); + + // Reset the firstDeposit flag as the first deposit to an unverified validator has been verified. + firstDeposit = false; + + emit ValidatorVerified(pubKeyHash, validatorIndex); + } + + struct FirstPendingDepositSlotProofData { + uint64 slot; + bytes proof; + } + + struct StrategyValidatorProofData { + uint64 withdrawableEpoch; + bytes withdrawableEpochProof; + } + + /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain. + /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance. + /// + /// Important: this function has a limitation where `depositProcessedSlot` that is passed by the off-chain + /// verifier requires a slot immediately after it to propose a block otherwise the `BeaconRoots.parentBlockRoot` + /// will fail. This shouldn't be a problem, since by the current behaviour of beacon chain only 1%-3% slots + /// don't propose a block. + /// @param pendingDepositRoot The unique identifier of the deposit emitted in `ETHStaked` from + /// the `stakeEth` function. + /// @param depositProcessedSlot Any slot on or after the strategy's deposit was processed on the beacon chain. + /// Can not be a slot with pending deposits with the same slot as the deposit being verified. + /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root + /// set for the next block timestamp in 12 seconds time. + /// @param firstPendingDeposit a `FirstPendingDepositSlotProofData` struct containing: + /// - slot: The beacon chain slot of the first deposit in the beacon chain's deposit queue. + /// Can be any non-zero value if the deposit queue is empty. + /// - proof: The merkle proof of the first pending deposit's slot to the beacon block root. + /// Can be either: + /// * 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty. + /// * 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty. + /// The 32 byte witness hashes are concatenated together starting from the leaf node. + /// @param strategyValidatorData a `StrategyValidatorProofData` struct containing: + /// - withdrawableEpoch: The withdrawable epoch of the validator the strategy is depositing to. + /// - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the validator the strategy + /// is depositing to, to the beacon block root. + /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node. + // slither-disable-start reentrancy-no-eth + function verifyDeposit( + bytes32 pendingDepositRoot, + uint64 depositProcessedSlot, + FirstPendingDepositSlotProofData calldata firstPendingDeposit, + StrategyValidatorProofData calldata strategyValidatorData + ) external { + // Load into memory the previously saved deposit data + DepositData memory deposit = deposits[pendingDepositRoot]; + ValidatorData memory strategyValidator = validator[deposit.pubKeyHash]; + require(deposit.status == DepositStatus.PENDING, "Deposit not pending"); + require(firstPendingDeposit.slot != 0, "Zero 1st pending deposit slot"); + + // We should allow the verification of deposits for validators that have been marked as exiting + // to cover this situation: + // - there are 2 pending deposits + // - beacon chain has slashed the validator + // - when verifyDeposit is called for the first deposit it sets the Validator state to EXITING + // - verifyDeposit should allow a secondary call for the other deposit to a slashed validator + require( + strategyValidator.state == ValidatorState.VERIFIED || + strategyValidator.state == ValidatorState.ACTIVE || + strategyValidator.state == ValidatorState.EXITING, + "Not verified/active/exiting" + ); + // The verification slot must be after the deposit's slot. + // This is needed for when the deposit queue is empty. + require(deposit.slot < depositProcessedSlot, "Slot not after deposit"); + + uint64 snapTimestamp = snappedBalance.timestamp; + + // This check prevents an accounting error that can happen if: + // - snapBalances are snapped at the time of T + // - deposit is processed on the beacon chain after time T and before verifyBalances() + // - verifyDeposit is called before verifyBalances which removes a deposit from depositList + // and deposit balance from totalDepositsWei + // - verifyBalances is called under-reporting the strategy's balance + require( + (_calcNextBlockTimestamp(depositProcessedSlot) <= snapTimestamp) || + snapTimestamp == 0, + "Deposit after balance snapshot" + ); + + // Get the parent beacon block root of the next block which is the block root of the deposit verification slot. + // This will revert if the slot after the verification slot was missed. + bytes32 depositBlockRoot = BeaconRoots.parentBlockRoot( + _calcNextBlockTimestamp(depositProcessedSlot) + ); + + // Verify the slot of the first pending deposit matches the beacon chain + bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS) + .verifyFirstPendingDeposit( + depositBlockRoot, + firstPendingDeposit.slot, + firstPendingDeposit.proof + ); + + // Verify the withdrawableEpoch on the validator of the strategy's deposit + IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable( + depositBlockRoot, + strategyValidator.index, + strategyValidatorData.withdrawableEpoch, + strategyValidatorData.withdrawableEpochProof + ); + + uint64 firstPendingDepositEpoch = firstPendingDeposit.slot / + SLOTS_PER_EPOCH; + + // If deposit queue is empty all deposits have certainly been processed. If not + // a validator can either be not exiting and no further checks are required. + // Or a validator is exiting then this function needs to make sure that the + // pending deposit to an exited validator has certainly been processed. The + // slot/epoch of first pending deposit is the one that contains the transaction + // where the deposit to the ETH Deposit Contract has been made. + // + // Once the firstPendingDepositEpoch becomes greater than the withdrawableEpoch of + // the slashed validator then the deposit has certainly been processed. When the beacon + // chain reaches the withdrawableEpoch of the validator the deposit will no longer be + // postponed. And any new deposits created (and present in the deposit queue) + // will have an equal or larger withdrawableEpoch. + require( + strategyValidatorData.withdrawableEpoch == FAR_FUTURE_EPOCH || + strategyValidatorData.withdrawableEpoch <= + firstPendingDepositEpoch || + isDepositQueueEmpty, + "Exit Deposit likely not proc." + ); + + // solhint-disable max-line-length + // Check the deposit slot is before the first pending deposit's slot on the beacon chain. + // If this is not true then we can't guarantee the deposit has been processed by the beacon chain. + // The deposit's slot can not be the same slot as the first pending deposit as there could be + // many deposits in the same block, hence have the same pending deposit slot. + // If the deposit queue is empty then our deposit must have been processed on the beacon chain. + // The deposit slot can be zero for validators consolidating to a compounding validator or 0x01 validator + // being promoted to a compounding one. Reference: + // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator + // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance) + // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request) + // We can not guarantee that the deposit has been processed in that case. + // solhint-enable max-line-length + require( + deposit.slot < firstPendingDeposit.slot || isDepositQueueEmpty, + "Deposit likely not processed" + ); + + // Remove the deposit now it has been verified as processed on the beacon chain. + _removeDeposit(pendingDepositRoot, deposit); + + emit DepositVerified( + pendingDepositRoot, + uint256(deposit.amountGwei) * 1 gwei + ); + } + + function _removeDeposit( + bytes32 pendingDepositRoot, + DepositData memory deposit + ) internal { + // After verifying the proof, update the contract storage + deposits[pendingDepositRoot].status = DepositStatus.VERIFIED; + // Move the last deposit to the index of the verified deposit + bytes32 lastDeposit = depositList[depositList.length - 1]; + depositList[deposit.depositIndex] = lastDeposit; + deposits[lastDeposit].depositIndex = deposit.depositIndex; + // Delete the last deposit from the list + depositList.pop(); + } + + /// @dev Calculates the timestamp of the next execution block from the given slot. + /// @param slot The beacon chain slot number used for merkle proof verification. + function _calcNextBlockTimestamp(uint64 slot) + internal + view + returns (uint64) + { + // Calculate the next block timestamp from the slot. + return SLOT_DURATION * slot + BEACON_GENESIS_TIMESTAMP + SLOT_DURATION; + } + + // slither-disable-end reentrancy-no-eth + + /// @notice Stores the current ETH balance at the current block and beacon block root + /// of the slot that is associated with the previous block. + /// + /// When snapping / verifying balance it is of a high importance that there is no + /// miss-match in respect to ETH that is held by the contract and balances that are + /// verified on the validators. + /// + /// First some context on the beacon-chain block building behaviour. Relevant parts of + /// constructing a block on the beacon chain consist of: + /// - process_withdrawals: ETH is deducted from the validator's balance + /// - process_execution_payload: immediately after the previous step executing all the + /// transactions + /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address + /// contained in the withdrawal credentials of the exited validators + /// + /// That means that balance increases which are part of the post-block execution state are + /// done within the block, but the transaction that are contained within that block can not + /// see / interact with the balance from the exited validators. Only transactions in the + /// next block can do that. + /// + /// When snap balances is performed the state of the chain is snapped across 2 separate + /// chain states: + /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y + /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1 + /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how + /// many slots have not managed to propose a block. For the sake of simplicity this slot + /// will be referred to as Y - 1 as it makes no difference in the argument + /// + /// Given these 2 separate chain states it is paramount that verify balances can not experience + /// miss-counting ETH or much more dangerous double counting of the ETH. + /// + /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify + /// balances adds up all the ETH (omitting WETH) controlled by this contract: + /// - ETH balance in the contract on block X + /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1 + /// - ETH balance in validators that are active in slot Y - 1 + /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner) + /// and have their balance visible to transactions in slot Y and corresponding block X + /// (or sooner) + /// + /// Lets verify the correctness of ETH accounting given the above described behaviour. + /// + /// *ETH balance in the contract on block X* + /// + /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the + /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X + /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in + /// case `verifyBalances` has not been called yet. Not performing this would result in not + /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z]. + /// + /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH + /// accounted for since the last `verifyBalances` has been called. And it invalidates the + /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this + /// would result in double counting the `stakedEth` since it would be present once in the + /// snapped contract balance and the second time in deposit storage variables. + /// + /// This behaviour is correct. + /// + /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1* + /// + /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit + /// contract at block Z. The execution layer doesn't have direct access to the state of + /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be + /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars) + /// and could also be part of the validator balances. It does that by verifying that at + /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since + /// the last snap till now all are still in queue. Which ensures they can not be part of + /// the validator balances in later steps. + /// + /// This behaviour is correct. + /// + /// *ETH balance in validators that are active in slot Y - 1* + /// + /// The contract is verifying none of the deposits on Y - 1 slot have been processed and + /// for that reason it checks the validator balances in the same slot. Ensuring accounting + /// correctness. + /// + /// This behaviour is correct. + /// + /// *The withdrawn validators* + /// + /// The withdrawn validators could have their balances deducted in any slot before slot + /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets + /// look at the "worst case scenario" where the validator withdrawal is processed in the + /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot + /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that + /// even if the validator exits at the latest possible time it is paramount that the ETH + /// balance on the execution layer is recorded in the next block. Correctly accounting + /// for the withdrawn ETH. + /// + /// Worth mentioning if the validator exit is processed by the slot Y and balance increase + /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the + /// validator balance verification as well as execution layer contract balance snap. + /// + /// This behaviour is correct. + /// + /// The validator balances on the beacon chain can then be proved with `verifyBalances`. + function snapBalances() external { + uint64 currentTimestamp = SafeCast.toUint64(block.timestamp); + require( + snappedBalance.timestamp + SNAP_BALANCES_DELAY < currentTimestamp, + "Snap too soon" + ); + + bytes32 blockRoot = BeaconRoots.parentBlockRoot(currentTimestamp); + // Get the current ETH balance + uint256 ethBalance = address(this).balance; + + // Store the snapped balance + snappedBalance = Balances({ + blockRoot: blockRoot, + timestamp: currentTimestamp, + ethBalance: SafeCast.toUint128(ethBalance) + }); + + emit BalancesSnapped(blockRoot, ethBalance); + } + + // A struct is used to avoid stack too deep errors + struct BalanceProofs { + // BeaconBlock.state.balances + bytes32 balancesContainerRoot; + bytes balancesContainerProof; + // BeaconBlock.state.balances[validatorIndex] + bytes32[] validatorBalanceLeaves; + bytes[] validatorBalanceProofs; + } + + struct PendingDepositProofs { + bytes32 pendingDepositContainerRoot; + bytes pendingDepositContainerProof; + uint32[] pendingDepositIndexes; + bytes[] pendingDepositProofs; + } + + /// @notice Verifies the balances of all active validators on the beacon chain + /// and checks each of the strategy's deposits are still to be processed by the beacon chain. + /// @param balanceProofs a `BalanceProofs` struct containing the following: + /// - balancesContainerRoot: The merkle root of the balances container + /// - balancesContainerProof: The merkle proof for the balances container to the beacon block root. + /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node. + /// - validatorBalanceLeaves: Array of leaf nodes containing the validator balance with three other balances. + /// - validatorBalanceProofs: Array of merkle proofs for the validator balance to the Balances container root. + /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node. + /// @param pendingDepositProofs a `PendingDepositProofs` struct containing the following: + /// - pendingDepositContainerRoot: The merkle root of the pending deposits list container + /// - pendingDepositContainerProof: The merkle proof from the pending deposits list container + /// to the beacon block root. + /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node. + /// - pendingDepositIndexes: Array of indexes in the pending deposits list container for each + /// of the strategy's deposits. + /// - pendingDepositProofs: Array of merkle proofs for each strategy deposit in the + /// beacon chain's pending deposit list container to the pending deposits list container root. + /// These are 28 witness hashes of 32 bytes each concatenated together starting from the leaf node. + // slither-disable-start reentrancy-no-eth + function verifyBalances( + BalanceProofs calldata balanceProofs, + PendingDepositProofs calldata pendingDepositProofs + ) external { + // Load previously snapped balances for the given block root + Balances memory balancesMem = snappedBalance; + // Check the balances are the latest + require(balancesMem.timestamp > 0, "No snapped balances"); + + uint256 verifiedValidatorsCount = verifiedValidators.length; + uint256 totalValidatorBalance = 0; + uint256 depositsCount = depositList.length; + + // If there are no verified validators then we can skip the balance verification + if (verifiedValidatorsCount > 0) { + require( + balanceProofs.validatorBalanceProofs.length == + verifiedValidatorsCount, + "Invalid balance proofs" + ); + require( + balanceProofs.validatorBalanceLeaves.length == + verifiedValidatorsCount, + "Invalid balance leaves" + ); + // verify beaconBlock.state.balances root to beacon block root + IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer( + balancesMem.blockRoot, + balanceProofs.balancesContainerRoot, + balanceProofs.balancesContainerProof + ); + + bytes32[] + memory validatorHashesMem = _getPendingDepositValidatorHashes( + depositsCount + ); + + // for each validator in reverse order so we can pop off exited validators at the end + for (uint256 i = verifiedValidatorsCount; i > 0; ) { + --i; + ValidatorData memory validatorDataMem = validator[ + verifiedValidators[i] + ]; + // verify validator's balance in beaconBlock.state.balances to the + // beaconBlock.state.balances container root + uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS) + .verifyValidatorBalance( + balanceProofs.balancesContainerRoot, + balanceProofs.validatorBalanceLeaves[i], + balanceProofs.validatorBalanceProofs[i], + validatorDataMem.index + ); + + // If the validator has exited and the balance is now zero + if (validatorBalanceGwei == 0) { + // Check if there are any pending deposits to this validator + bool depositPending = false; + for (uint256 j = 0; j < validatorHashesMem.length; j++) { + if (validatorHashesMem[j] == verifiedValidators[i]) { + depositPending = true; + break; + } + } + + // If validator has a pending deposit we can not remove due to + // the following situation: + // - validator has a pending deposit + // - validator has been slashed + // - sweep cycle has withdrawn all ETH from the validator. Balance is 0 + // - beacon chain has processed the deposit and set the validator balance + // to deposit amount + // - if validator is no longer in the list of verifiedValidators its + // balance will not be considered and be under-counted. + if (!depositPending) { + // Store the validator state as exited + // This could have been in VERIFIED, ACTIVE or EXITING state + validator[verifiedValidators[i]].state = ValidatorState + .EXITED; + + // Remove the validator with a zero balance from the list of verified validators + + // Reduce the count of verified validators which is the last index before the pop removes it. + verifiedValidatorsCount -= 1; + + // Move the last validator that has already been verified to the current index. + // There's an extra SSTORE if i is the last active validator but that's fine, + // It's not a common case and the code is simpler this way. + verifiedValidators[i] = verifiedValidators[ + verifiedValidatorsCount + ]; + // Delete the last validator from the list + verifiedValidators.pop(); + } + + // The validator balance is zero so not need to add to totalValidatorBalance + continue; + } else if ( + validatorDataMem.state == ValidatorState.VERIFIED && + validatorBalanceGwei > MIN_ACTIVATION_BALANCE_GWEI + ) { + // Store the validator state as active. This does not necessarily mean the + // validator is active on the beacon chain yet. It just means the validator has + // enough balance that it can become active. + validator[verifiedValidators[i]].state = ValidatorState + .ACTIVE; + } + + // convert Gwei balance to Wei and add to the total validator balance + totalValidatorBalance += validatorBalanceGwei * 1 gwei; + } + } + + uint256 totalDepositsWei = 0; + + // If there are no deposits then we can skip the deposit verification. + // This section is after the validator balance verifications so an exited validator will be marked + // as EXITED before the deposits are verified. If there was a deposit to an exited validator + // then the deposit can only be removed once the validator is fully exited. + // It is possible that validator fully exits and a postponed deposit to an exited validator increases + // its balance again. In such case the contract will erroneously consider a deposit applied before it + // has been applied on the beacon chain showing a smaller than real `totalValidatorBalance`. + if (depositsCount > 0) { + require( + pendingDepositProofs.pendingDepositProofs.length == + depositsCount, + "Invalid deposit proofs" + ); + require( + pendingDepositProofs.pendingDepositIndexes.length == + depositsCount, + "Invalid deposit indexes" + ); + + // Verify from the root of the pending deposit list container to the beacon block root + IBeaconProofs(BEACON_PROOFS).verifyPendingDepositsContainer( + balancesMem.blockRoot, + pendingDepositProofs.pendingDepositContainerRoot, + pendingDepositProofs.pendingDepositContainerProof + ); + + // For each staking strategy's deposit. + for (uint256 i = 0; i < depositsCount; ++i) { + bytes32 pendingDepositRoot = depositList[i]; + + // Verify the strategy's deposit is still pending on the beacon chain. + IBeaconProofs(BEACON_PROOFS).verifyPendingDeposit( + pendingDepositProofs.pendingDepositContainerRoot, + pendingDepositRoot, + pendingDepositProofs.pendingDepositProofs[i], + pendingDepositProofs.pendingDepositIndexes[i] + ); + + // Convert the deposit amount from Gwei to Wei and add to the total + totalDepositsWei += + uint256(deposits[pendingDepositRoot].amountGwei) * + 1 gwei; + } + } + + // Store the verified balance in storage + lastVerifiedEthBalance = + totalDepositsWei + + totalValidatorBalance + + balancesMem.ethBalance; + // Reset the last snap timestamp so a new snapBalances has to be made + snappedBalance.timestamp = 0; + + emit BalancesVerified( + balancesMem.timestamp, + totalDepositsWei, + totalValidatorBalance, + balancesMem.ethBalance + ); + } + + // slither-disable-end reentrancy-no-eth + + /// @notice get a list of all validator hashes present in the pending deposits + /// list can have duplicate entries + function _getPendingDepositValidatorHashes(uint256 depositsCount) + internal + view + returns (bytes32[] memory validatorHashes) + { + validatorHashes = new bytes32[](depositsCount); + for (uint256 i = 0; i < depositsCount; i++) { + validatorHashes[i] = deposits[depositList[i]].pubKeyHash; + } + } + + /// @notice Hash a validator public key using the Beacon Chain's format + function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) { + require(pubKey.length == 48, "Invalid public key"); + return sha256(abi.encodePacked(pubKey, bytes16(0))); + } + + /** + * + * WETH and ETH Accounting + * + */ + + /// @dev Called when WETH is transferred out of the strategy so + /// the strategy knows how much WETH it has on deposit. + /// This is so it can emit the correct amount in the Deposit event in depositAll(). + function _transferWeth(uint256 _amount, address _recipient) internal { + IERC20(WETH).safeTransfer(_recipient, _amount); + + // The min is required as more WETH can be withdrawn than deposited + // as the strategy earns consensus and execution rewards. + uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor); + depositedWethAccountedFor -= deductAmount; + + // No change in ETH balance so no need to snapshot the balances + } + + /// @dev Converts ETH to WETH and updates the accounting. + /// @param _ethAmount The amount of ETH in wei. + function _convertEthToWeth(uint256 _ethAmount) internal { + // slither-disable-next-line arbitrary-send-eth + IWETH9(WETH).deposit{ value: _ethAmount }(); + + depositedWethAccountedFor += _ethAmount; + + // Store the reduced ETH balance. + // The ETH balance in this strategy contract can be more than the last verified ETH balance + // due to partial withdrawals or full exits being processed by the beacon chain since the last snapBalances. + // It can also happen from execution rewards (MEV) or ETH donations. + lastVerifiedEthBalance -= Math.min(lastVerifiedEthBalance, _ethAmount); + + // The ETH balance was decreased to WETH so we need to invalidate the last balances snap. + snappedBalance.timestamp = 0; + } + + /// @dev Converts WETH to ETH and updates the accounting. + /// @param _wethAmount The amount of WETH in wei. + function _convertWethToEth(uint256 _wethAmount) internal { + IWETH9(WETH).withdraw(_wethAmount); + + uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor); + depositedWethAccountedFor -= deductAmount; + + // Store the increased ETH balance + lastVerifiedEthBalance += _wethAmount; + + // The ETH balance was increased from WETH so we need to invalidate the last balances snap. + snappedBalance.timestamp = 0; + } + + /** + * + * View Functions + * + */ + + /// @notice Returns the number of deposits waiting to be verified as processed on the beacon chain, + /// or deposits that have been verified to an exiting validator and is now waiting for the + /// validator's balance to be swept. + function depositListLength() external view returns (uint256) { + return depositList.length; + } + + /// @notice Returns the number of verified validators. + function verifiedValidatorsLength() external view returns (uint256) { + return verifiedValidators.length; + } +} diff --git a/contracts/contracts/strategies/NativeStaking/ValidatorRegistrator.sol b/contracts/contracts/strategies/NativeStaking/ValidatorRegistrator.sol index 5b8eaec184..1289adfc3a 100644 --- a/contracts/contracts/strategies/NativeStaking/ValidatorRegistrator.sol +++ b/contracts/contracts/strategies/NativeStaking/ValidatorRegistrator.sol @@ -51,6 +51,7 @@ abstract contract ValidatorRegistrator is Governable, Pausable { /// @notice Amount of ETH that has been staked since the `stakingMonitor` last called `resetStakeETHTally`. /// This can not go above `stakeETHThreshold`. uint256 public stakeETHTally; + // For future use uint256[47] private __gap; @@ -230,7 +231,6 @@ abstract contract ValidatorRegistrator is Governable, Pausable { /// @param sharesData The shares data for each validator /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster /// @param cluster The SSV cluster details including the validator count and SSV balance - // slither-disable-start reentrancy-no-eth function registerSsvValidators( bytes[] calldata publicKeys, uint64[] calldata operatorIds, @@ -267,66 +267,86 @@ abstract contract ValidatorRegistrator is Governable, Pausable { ); } - // slither-disable-end reentrancy-no-eth - - /// @notice Exit a validator from the Beacon chain. + /// @notice Exit validators from the Beacon chain. /// The staked ETH will eventually swept to this native staking strategy. /// Only the registrator can call this function. - /// @param publicKey The public key of the validator + /// @param publicKeys List of SSV validator public keys /// @param operatorIds The operator IDs of the SSV Cluster - // slither-disable-start reentrancy-no-eth - function exitSsvValidator( - bytes calldata publicKey, + function exitSsvValidators( + bytes[] calldata publicKeys, uint64[] calldata operatorIds - ) external onlyRegistrator whenNotPaused { - bytes32 pubKeyHash = keccak256(publicKey); - VALIDATOR_STATE currentState = validatorsStates[pubKeyHash]; - require(currentState == VALIDATOR_STATE.STAKED, "Validator not staked"); + ) external onlyRegistrator whenNotPaused nonReentrant { + ISSVNetwork(SSV_NETWORK).bulkExitValidator(publicKeys, operatorIds); - ISSVNetwork(SSV_NETWORK).exitValidator(publicKey, operatorIds); + bytes32 pubKeyHash; + VALIDATOR_STATE currentState; + for (uint256 i = 0; i < publicKeys.length; ++i) { + pubKeyHash = keccak256(publicKeys[i]); + currentState = validatorsStates[pubKeyHash]; - validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXITING; + // Check each validator has not already been staked. + // This would normally be done before the external call but is after + // so only one for loop of the validators is needed. + require( + currentState == VALIDATOR_STATE.STAKED, + "Validator not staked" + ); - emit SSVValidatorExitInitiated(pubKeyHash, publicKey, operatorIds); - } + // Store the new validator state + validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXITING; - // slither-disable-end reentrancy-no-eth + emit SSVValidatorExitInitiated( + pubKeyHash, + publicKeys[i], + operatorIds + ); + } + } - /// @notice Remove a validator from the SSV Cluster. + /// @notice Remove validators from the SSV Cluster. /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain. /// If removed before the validator has exited the beacon chain will result in the validator being slashed. /// Only the registrator can call this function. - /// @param publicKey The public key of the validator + /// @param publicKeys List of SSV validator public keys /// @param operatorIds The operator IDs of the SSV Cluster /// @param cluster The SSV cluster details including the validator count and SSV balance - // slither-disable-start reentrancy-no-eth - function removeSsvValidator( - bytes calldata publicKey, + function removeSsvValidators( + bytes[] calldata publicKeys, uint64[] calldata operatorIds, Cluster calldata cluster - ) external onlyRegistrator whenNotPaused { - bytes32 pubKeyHash = keccak256(publicKey); - VALIDATOR_STATE currentState = validatorsStates[pubKeyHash]; - // Can remove SSV validators that were incorrectly registered and can not be deposited to. - require( - currentState == VALIDATOR_STATE.EXITING || - currentState == VALIDATOR_STATE.REGISTERED, - "Validator not regd or exiting" - ); - - ISSVNetwork(SSV_NETWORK).removeValidator( - publicKey, + ) external onlyRegistrator whenNotPaused nonReentrant { + ISSVNetwork(SSV_NETWORK).bulkRemoveValidator( + publicKeys, operatorIds, cluster ); - validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXIT_COMPLETE; + bytes32 pubKeyHash; + VALIDATOR_STATE currentState; + for (uint256 i = 0; i < publicKeys.length; ++i) { + pubKeyHash = keccak256(publicKeys[i]); + currentState = validatorsStates[pubKeyHash]; + + // Check each validator is either registered or exited. + // This would normally be done before the external call but is after + // so only one for loop of the validators is needed. + require( + currentState == VALIDATOR_STATE.EXITING || + currentState == VALIDATOR_STATE.REGISTERED, + "Validator not regd or exiting" + ); + + // Store the new validator state + validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXIT_COMPLETE; - emit SSVValidatorExitCompleted(pubKeyHash, publicKey, operatorIds); + emit SSVValidatorExitCompleted( + pubKeyHash, + publicKeys[i], + operatorIds + ); + } } - // slither-disable-end reentrancy-no-eth - /// @notice Deposits more SSV Tokens to the SSV Network contract which is used to pay the SSV Operators. /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds. /// uses "onlyStrategist" modifier so continuous front-running can't DOS our maintenance service diff --git a/contracts/deploy/deployActions.js b/contracts/deploy/deployActions.js index f1292bf63b..6acb8f0688 100644 --- a/contracts/deploy/deployActions.js +++ b/contracts/deploy/deployActions.js @@ -1,4 +1,7 @@ const hre = require("hardhat"); +const { setStorageAt } = require("@nomicfoundation/hardhat-network-helpers"); +const { getNetworkName } = require("../utils/hardhat-helpers"); +const { parseUnits } = require("ethers/lib/utils.js"); const addresses = require("../utils/addresses"); const { @@ -11,10 +14,20 @@ const { isTest, isFork, isPlume, + isHoodi, + isHoodiOrFork, } = require("../test/helpers.js"); const { deployWithConfirmation, withConfirmation } = require("../utils/deploy"); const { metapoolLPCRVPid } = require("../utils/constants"); -const { parseUnits } = require("ethers/lib/utils.js"); +const { replaceContractAt } = require("../utils/hardhat"); +const { resolveContract } = require("../utils/resolvers"); +const { impersonateAccount, getSigner } = require("../utils/signers"); +const { getDefenderSigner } = require("../utils/signersNoHardhat"); +const { getTxOpts } = require("../utils/tx"); +const { + beaconChainGenesisTimeHoodi, + beaconChainGenesisTimeMainnet, +} = require("../utils/constants"); const log = require("../utils/logger")("deploy:core"); @@ -308,9 +321,10 @@ const configureVault = async () => { */ const configureOETHVault = async (isSimpleOETH) => { const assetAddresses = await getAssetAddresses(deployments); - const { governorAddr, strategistAddr } = await getNamedAccounts(); + let { governorAddr, deployerAddr, strategistAddr } = await getNamedAccounts(); // Signers - const sGovernor = await ethers.provider.getSigner(governorAddr); + let sGovernor = await ethers.provider.getSigner(governorAddr); + const sDeployer = await ethers.provider.getSigner(deployerAddr); const cVault = await ethers.getContractAt( "IVault", @@ -318,6 +332,12 @@ const configureOETHVault = async (isSimpleOETH) => { await ethers.getContract("OETHVaultProxy") ).address ); + + if (isHoodiOrFork) { + governorAddr = deployerAddr; + sGovernor = sDeployer; + } + // Set up supported assets for Vault const { WETH, RETH, stETH, frxETH } = assetAddresses; const assets = isSimpleOETH ? [WETH] : [WETH, RETH, stETH, frxETH]; @@ -398,7 +418,9 @@ const deployOUSDHarvester = async (ousdDripper) => { cHarvester .connect(sGovernor) .setRewardProceedsAddress( - isMainnet || isHolesky ? ousdDripper.address : cVaultProxy.address + isMainnet || isHolesky || isHoodi + ? ousdDripper.address + : cVaultProxy.address ) ); @@ -447,10 +469,10 @@ const deployOETHHarvester = async (oethDripper) => { await withConfirmation( // prettier-ignore cOETHHarvesterProxy["initialize(address,address,bytes)"]( - dOETHHarvester.address, - governorAddr, - [] - ) + dOETHHarvester.address, + governorAddr, + [] + ) ); log("Initialized OETHHarvesterProxy"); @@ -459,7 +481,9 @@ const deployOETHHarvester = async (oethDripper) => { cOETHHarvester .connect(sGovernor) .setRewardProceedsAddress( - isMainnet || isHolesky ? oethDripper.address : cOETHVaultProxy.address + isMainnet || isHolesky || isHoodi + ? oethDripper.address + : cOETHVaultProxy.address ) ); @@ -634,12 +658,10 @@ const upgradeNativeStakingFeeAccumulator = async () => { */ const upgradeNativeStakingSSVStrategy = async () => { const assetAddresses = await getAssetAddresses(deployments); - const { deployerAddr } = await getNamedAccounts(); const cOETHVaultProxy = await ethers.getContract("OETHVaultProxy"); const strategyProxy = await ethers.getContract( "NativeStakingSSVStrategyProxy" ); - const sDeployer = await ethers.provider.getSigner(deployerAddr); const cFeeAccumulatorProxy = await ethers.getContract( "NativeStakingFeeAccumulatorProxy" @@ -660,9 +682,50 @@ const upgradeNativeStakingSSVStrategy = async () => { ); log(`New NativeStakingSSVStrategy implementation: ${dStrategyImpl.address}`); + const networkName = await getNetworkName(); + if (networkName == "hoodi") { + const sGovernor = isFork ? await getSigner() : await getDefenderSigner(); + await withConfirmation( + strategyProxy.connect(sGovernor).upgradeTo(dStrategyImpl.address) + ); + } +}; + +const upgradeCompoundingStakingSSVStrategy = async () => { + const assetAddresses = await getAssetAddresses(deployments); + + const cOETHVaultProxy = await resolveContract("OETHVaultProxy"); + const cBeaconProofs = await resolveContract("BeaconProofs"); + const strategyProxy = await resolveContract( + "CompoundingStakingSSVStrategyProxy" + ); + + log("Deploy CompoundingStakingSSVStrategy implementation"); + + const genesisTimestamp = isHoodiOrFork + ? beaconChainGenesisTimeHoodi + : beaconChainGenesisTimeMainnet; + const dStrategyImpl = await deployWithConfirmation( + "CompoundingStakingSSVStrategy", + [ + [addresses.zero, cOETHVaultProxy.address], //_baseConfig + assetAddresses.WETH, // wethAddress + assetAddresses.SSV, // ssvToken + assetAddresses.SSVNetwork, // ssvNetwork + assetAddresses.beaconChainDepositContract, // depositContractMock + cBeaconProofs.address, // BeaconProofs + genesisTimestamp, + ] + ); + + const sDeployer = isFork ? await getSigner() : await getDefenderSigner(); await withConfirmation( strategyProxy.connect(sDeployer).upgradeTo(dStrategyImpl.address) ); + + console.log( + `Upgraded CompoundingStakingSSVStrategyProxy to implementation at ${dStrategyImpl.address}` + ); }; /** @@ -672,10 +735,14 @@ const upgradeNativeStakingSSVStrategy = async () => { */ const deployNativeStakingSSVStrategy = async () => { const assetAddresses = await getAssetAddresses(deployments); - const { governorAddr, deployerAddr } = await getNamedAccounts(); + let { governorAddr, deployerAddr } = await getNamedAccounts(); const sDeployer = await ethers.provider.getSigner(deployerAddr); const cOETHVaultProxy = await ethers.getContract("OETHVaultProxy"); + if (isHoodiOrFork) { + governorAddr = deployerAddr; + } + log("Deploy NativeStakingSSVStrategyProxy"); const dNativeStakingSSVStrategyProxy = await deployWithConfirmation( "NativeStakingSSVStrategyProxy" @@ -716,7 +783,7 @@ const deployNativeStakingSSVStrategy = async () => { "initialize(address[],address[],address[])", [ [assetAddresses.WETH], // reward token addresses - /* no need to specify WETH as an asset, since we have that overriden in the "supportsAsset" + /* no need to specify WETH as an asset, since we have that overridden in the "supportsAsset" * function on the strategy */ [], // asset token addresses @@ -768,7 +835,157 @@ const deployNativeStakingSSVStrategy = async () => { }; /** - * Deploy the OracleRouter and initialise it with Chainlink sources. + * Deploy CompoundingStakingSSVStrategy + * Deploys a proxy, the actual strategy, initializes the proxy and initializes + * the strategy. + */ +const deployCompoundingStakingSSVStrategy = async () => { + const assetAddresses = await getAssetAddresses(deployments); + const { governorAddr, deployerAddr } = await getNamedAccounts(); + const sDeployer = await ethers.provider.getSigner(deployerAddr); + const networkName = await getNetworkName(); + + const cOETHVaultProxy = await ethers.getContract("OETHVaultProxy"); + + log("Deploy Beacon Proofs"); + await deployWithConfirmation("BeaconProofs", []); + const cBeaconProofs = await ethers.getContract("BeaconProofs"); + + let governorAddress; + // Deploy the proxy on Hoodi fork not as defender relayer since we will not + // test SSV token claiming on that testnet + if ((isTest && !isFork) || networkName == "hoodi") { + // For unit tests and Hoodi, use the Governor contract + governorAddress = governorAddr; + + log("Deploy CompoundingStakingSSVStrategyProxy"); + await deployWithConfirmation("CompoundingStakingSSVStrategyProxy"); + } else { + // For fork tests and mainnet deployments, use the Timelock contract + governorAddress = addresses.mainnet.Timelock; + log(`Mainnet governor is the Timelock contract ${governorAddress}`); + } + + let cCompoundingStakingSSVStrategyProxy; + if (isTest) { + log(`Fix CompoundingStakingSSVStrategyProxy address for unit tests`); + // For unit tests, fix the address of compoundingStakingSSVStrategy so the withdrawal credentials + // are fixed for the validator public key proofs + await replaceContractAt( + addresses.mainnet.CompoundingStakingStrategyProxy, + await ethers.getContract("CompoundingStakingSSVStrategyProxy") + ); + // Set the governor in storage of the proxy to the deployer + await setStorageAt( + addresses.mainnet.CompoundingStakingStrategyProxy, + "0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a", // governor storage slot + deployerAddr + ); + cCompoundingStakingSSVStrategyProxy = await ethers.getContractAt( + "CompoundingStakingSSVStrategyProxy", + addresses.mainnet.CompoundingStakingStrategyProxy + ); + } else { + // For fork tests, mainnet and Hoodi deployments. + // Should have already been deployed by the Defender Relayer as SSV rewards are sent to the deployer. + // Use the deployStakingProxy Hardhat task to deploy + cCompoundingStakingSSVStrategyProxy = await ethers.getContract( + "CompoundingStakingSSVStrategyProxy" + ); + } + + const proxyGovernor = await cCompoundingStakingSSVStrategyProxy.governor(); + log(`CompoundingStakingSSVStrategyProxy's governor: ${proxyGovernor}`); + if (isFork && proxyGovernor != deployerAddr) { + // For fork tests, transfer the governance to the deployer account + const currentSigner = await impersonateAccount(proxyGovernor); + await withConfirmation( + cCompoundingStakingSSVStrategyProxy + .connect(currentSigner) + .transferGovernance(deployerAddr) + ); + + await withConfirmation( + cCompoundingStakingSSVStrategyProxy.connect(sDeployer).claimGovernance() + ); + } else { + /* Before kicking off the deploy script make sure the Defender relayer transfers the governance + * of the proxy to the deployer account that shall be deploying this script so it will be able + * to initialize the proxy contract + * + * Run the following to make it happen, and comment this error block out: + * yarn run hardhat transferGovernance --proxy CompoundingStakingSSVStrategyProxy --governor 0xdeployerAddress --network mainnet + */ + if (proxyGovernor != deployerAddr) { + throw new Error( + `Compounding Staking Strategy proxy's governor: ${proxyGovernor} does not match current deployer ${deployerAddr}` + ); + } + } + + log("Deploy CompoundingStakingSSVStrategy"); + const genesisTimestamp = isHoodiOrFork + ? beaconChainGenesisTimeHoodi + : beaconChainGenesisTimeMainnet; + const dStrategyImpl = await deployWithConfirmation( + "CompoundingStakingSSVStrategy", + [ + [addresses.zero, cOETHVaultProxy.address], //_baseConfig + assetAddresses.WETH, // wethAddress + assetAddresses.SSV, // ssvToken + assetAddresses.SSVNetwork, // ssvNetwork + assetAddresses.beaconChainDepositContract, // depositContractMock + cBeaconProofs.address, // BeaconProofs + genesisTimestamp, + ] + ); + const cStrategyImpl = await ethers.getContractAt( + "CompoundingStakingSSVStrategy", + dStrategyImpl.address + ); + + log("Deploy encode initialize function of the strategy contract"); + const initData = cStrategyImpl.interface.encodeFunctionData( + "initialize(address[],address[],address[])", + [ + [], // reward token addresses + /* no need to specify WETH as an asset, since we have that overridden in the "supportsAsset" + * function on the strategy + */ + [], // asset token addresses + [], // platform tokens addresses + ] + ); + + log( + `Initialize the CompoundingStakingSSVStrategy proxy ${cCompoundingStakingSSVStrategyProxy.address} to implementation ${cStrategyImpl.address} and execute the initialize strategy function using deployer ${deployerAddr}` + ); + await withConfirmation( + cCompoundingStakingSSVStrategyProxy.connect(sDeployer)[ + // eslint-disable-next-line no-unexpected-multiline + "initialize(address,address,bytes)" + ]( + cStrategyImpl.address, // implementation address + governorAddress, + initData // data for call to the initialize function on the strategy + ) + ); + + const cStrategy = await ethers.getContractAt( + "CompoundingStakingSSVStrategy", + cCompoundingStakingSSVStrategyProxy.address + ); + + log("Deploy CompoundingStakingStrategyView"); + await deployWithConfirmation("CompoundingStakingStrategyView", [ + cCompoundingStakingSSVStrategyProxy.address, + ]); + + return cStrategy; +}; + +/** + * Deploy the OracleRouter and initialize it with Chainlink sources. */ const deployOracles = async () => { const { deployerAddr } = await getNamedAccounts(); @@ -780,10 +997,10 @@ const deployOracles = async () => { let args = []; if (isMainnet) { oracleContract = "OracleRouter"; - } else if (isHoleskyOrFork) { + } else if (isHoleskyOrFork || isHoodiOrFork) { oracleContract = "OETHFixedOracle"; contractName = "OETHOracleRouter"; - args = [addresses.zero]; + args = []; } else if (isSonicOrFork) { oracleContract = "OSonicOracleRouter"; contractName = "OSonicOracleRouter"; @@ -791,7 +1008,7 @@ const deployOracles = async () => { } await deployWithConfirmation(contractName, args, oracleContract); - if (isHoleskyOrFork || isSonicOrFork) { + if (isHoleskyOrFork || isHoodiOrFork || isSonicOrFork) { // no need to configure any feeds since they are hardcoded to a fixed feed // TODO: further deployments will require more intelligent separation of different // chains / environment oracle deployments @@ -842,12 +1059,20 @@ const deployOracles = async () => { }; const deployOETHCore = async () => { - const { governorAddr } = await hre.getNamedAccounts(); + let { governorAddr, deployerAddr } = await hre.getNamedAccounts(); const assetAddresses = await getAssetAddresses(deployments); log(`Using asset addresses: ${JSON.stringify(assetAddresses, null, 2)}`); // Signers - const sGovernor = await ethers.provider.getSigner(governorAddr); + let sGovernor = await ethers.provider.getSigner(governorAddr); + const sDeployer = await ethers.provider.getSigner(deployerAddr); + + // In case of Hoodie let the deployer be governor. + if (isHoodiOrFork) { + console.log("isHoodiOrFork", "YES"); + governorAddr = deployerAddr; + sGovernor = sDeployer; + } // Proxies await deployWithConfirmation("OETHProxy"); @@ -867,30 +1092,32 @@ const deployOETHCore = async () => { const cOETHProxy = await ethers.getContract("OETHProxy"); const cOETHVaultProxy = await ethers.getContract("OETHVaultProxy"); const cOETH = await ethers.getContractAt("OETH", cOETHProxy.address); - const cOracleRouter = await ethers.getContract("OracleRouter"); - const cOETHOracleRouter = isMainnet - ? await ethers.getContract("OETHOracleRouter") - : cOracleRouter; + const oracleRouterContractName = + isMainnet || isHoodiOrFork ? "OETHOracleRouter" : "OracleRouter"; + const cOETHOracleRouter = await ethers.getContract(oracleRouterContractName); const cOETHVault = await ethers.getContractAt( "IVault", cOETHVaultProxy.address ); + // prettier-ignore await withConfirmation( - cOETHProxy["initialize(address,address,bytes)"]( + cOETHProxy.connect(sDeployer)["initialize(address,address,bytes)"]( dOETH.address, governorAddr, - [] + [], + await getTxOpts() ) ); log("Initialized OETHProxy"); - + // prettier-ignore await withConfirmation( - cOETHVaultProxy["initialize(address,address,bytes)"]( + cOETHVaultProxy.connect(sDeployer)["initialize(address,address,bytes)"]( dOETHVault.address, governorAddr, - [] + [], + await getTxOpts() ) ); log("Initialized OETHVaultProxy"); @@ -898,7 +1125,11 @@ const deployOETHCore = async () => { await withConfirmation( cOETHVault .connect(sGovernor) - .initialize(cOETHOracleRouter.address, cOETHProxy.address) + .initialize( + cOETHOracleRouter.address, + cOETHProxy.address, + await getTxOpts() + ) ); log("Initialized OETHVault"); @@ -1441,11 +1672,11 @@ const deploySonicSwapXAMOStrategyImplementation = async () => { await withConfirmation( // prettier-ignore cSonicSwapXAMOStrategyProxy - .connect(sDeployer)["initialize(address,address,bytes)"]( - dSonicSwapXAMOStrategy.address, - addresses.sonic.timelock, - initData - ) + .connect(sDeployer)["initialize(address,address,bytes)"]( + dSonicSwapXAMOStrategy.address, + addresses.sonic.timelock, + initData + ) ); return cSonicSwapXAMOStrategy; @@ -1462,6 +1693,7 @@ module.exports = { deployConvexStrategy, deployConvexOUSDMetaStrategy, deployNativeStakingSSVStrategy, + deployCompoundingStakingSSVStrategy, deployDrippers, deployOETHDripper, deployOUSDDripper, @@ -1481,6 +1713,7 @@ module.exports = { deployOUSDSwapper, upgradeNativeStakingSSVStrategy, upgradeNativeStakingFeeAccumulator, + upgradeCompoundingStakingSSVStrategy, deployBaseAerodromeAMOStrategyImplementation, deployPlumeRoosterAMOStrategyImplementation, deployPlumeMockRoosterAMOStrategyImplementation, diff --git a/contracts/deploy/holesky/019_upgrade_strategy.js b/contracts/deploy/holesky/019_upgrade_strategy.js new file mode 100644 index 0000000000..9f514276ac --- /dev/null +++ b/contracts/deploy/holesky/019_upgrade_strategy.js @@ -0,0 +1,17 @@ +const { upgradeNativeStakingSSVStrategy } = require("../deployActions"); + +const mainExport = async () => { + console.log("Running 019 deployment on Holesky..."); + + await upgradeNativeStakingSSVStrategy(); + + console.log("Running 019 deployment done"); + return true; +}; + +mainExport.id = "019_upgrade_strategy"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/001_core.js b/contracts/deploy/hoodi/001_core.js new file mode 100644 index 0000000000..c019fa6ab3 --- /dev/null +++ b/contracts/deploy/hoodi/001_core.js @@ -0,0 +1,101 @@ +const { + deployOracles, + deployOETHCore, + deployNativeStakingSSVStrategy, + deployCompoundingStakingSSVStrategy, + configureOETHVault, +} = require("../deployActions"); +const addresses = require("../../utils/addresses.js"); + +const { withConfirmation } = require("../../utils/deploy"); +const { impersonateAndFund } = require("../../utils/signers"); +const { isFork } = require("../../test/helpers"); + +const mainExport = async () => { + console.log("Running 001_core deployment on Hoodi..."); + const { deployerAddr } = await getNamedAccounts(); + // deployer is governor + const sDeployer = await ethers.provider.getSigner(deployerAddr); + + if (isFork) { + // Fund the deployer on fork + impersonateAndFund(deployerAddr, "10000"); + } + + console.log("Deploying Oracles"); + await deployOracles(); + console.log("Deploying Core"); + await deployOETHCore(); + + console.log("Deploying Native Staking"); + await deployNativeStakingSSVStrategy(); + + console.log("Deploy compounding ssv strategy"); + const compoundingSsvStrategy = await deployCompoundingStakingSSVStrategy(); + + await configureOETHVault(true); + + const cVault = await ethers.getContractAt( + "IVault", + ( + await ethers.getContract("OETHVaultProxy") + ).address + ); + + const nativeStakingSSVStrategyProxy = await ethers.getContract( + "NativeStakingSSVStrategyProxy" + ); + + const nativeStakingSSVStrategy = await ethers.getContractAt( + "NativeStakingSSVStrategy", + nativeStakingSSVStrategyProxy.address + ); + + // await withConfirmation( + // nativeStakingSSVStrategy + // .connect(sDeployer) + // .setHarvesterAddress(cOETHHarvester.address) + // ); + + await withConfirmation( + cVault + .connect(sDeployer) + .approveStrategy(nativeStakingSSVStrategyProxy.address) + ); + + await withConfirmation( + cVault.connect(sDeployer).approveStrategy(compoundingSsvStrategy.address) + ); + + await withConfirmation( + nativeStakingSSVStrategy + .connect(sDeployer) + .setRegistrator(addresses.hoodi.defenderRelayer) + ); + await withConfirmation( + nativeStakingSSVStrategy + .connect(sDeployer) + .addTargetStrategy(compoundingSsvStrategy.address) + ); + + await withConfirmation( + compoundingSsvStrategy + .connect(sDeployer) + .addSourceStrategy(nativeStakingSSVStrategy.address) + ); + await withConfirmation( + compoundingSsvStrategy + .connect(sDeployer) + .setRegistrator(addresses.hoodi.defenderRelayer) + ); + + console.log("001_core deploy done."); + return true; +}; + +mainExport.id = "001_core"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/002_beacon_root_testing.js b/contracts/deploy/hoodi/002_beacon_root_testing.js new file mode 100644 index 0000000000..20caf03ab4 --- /dev/null +++ b/contracts/deploy/hoodi/002_beacon_root_testing.js @@ -0,0 +1,14 @@ +const { deployWithConfirmation } = require("../../utils/deploy"); + +const mainExport = async () => { + const dMockBeaconRoots = await deployWithConfirmation("MockBeaconRoots"); + console.log(`Deployed MockBeaconRoots ${dMockBeaconRoots.address}`); + return true; +}; + +mainExport.id = "002_beacon_root_testing"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/003_upgrade_compounding_staking_strategy.js b/contracts/deploy/hoodi/003_upgrade_compounding_staking_strategy.js new file mode 100644 index 0000000000..df00af96c3 --- /dev/null +++ b/contracts/deploy/hoodi/003_upgrade_compounding_staking_strategy.js @@ -0,0 +1,15 @@ +const { upgradeCompoundingStakingSSVStrategy } = require("../deployActions"); + +const mainExport = async () => { + await upgradeCompoundingStakingSSVStrategy(); + + console.log("Running 003 deployment done"); + return true; +}; + +mainExport.id = "003_upgrade_compounding_staking_strategy"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/004_set_strategist.js b/contracts/deploy/hoodi/004_set_strategist.js new file mode 100644 index 0000000000..1bfc60e09e --- /dev/null +++ b/contracts/deploy/hoodi/004_set_strategist.js @@ -0,0 +1,31 @@ +const addresses = require("../../utils/addresses.js"); + +const { withConfirmation } = require("../../utils/deploy"); + +const mainExport = async () => { + console.log("Running 004_set_strategist deployment on Hoodi..."); + const { deployerAddr } = await getNamedAccounts(); + // deployer is governor + const sDeployer = await ethers.provider.getSigner(deployerAddr); + + const cVault = await ethers.getContractAt( + "IVault", + ( + await ethers.getContract("OETHVaultProxy") + ).address + ); + + await withConfirmation( + cVault.connect(sDeployer).setStrategistAddr(addresses.hoodi.defenderRelayer) + ); + + console.log("004_set_strategist deploy done."); + return true; +}; + +mainExport.id = "004_set_strategist"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/005_upgrade_vault.js b/contracts/deploy/hoodi/005_upgrade_vault.js new file mode 100644 index 0000000000..f0f5d84b85 --- /dev/null +++ b/contracts/deploy/hoodi/005_upgrade_vault.js @@ -0,0 +1,47 @@ +const { + deployWithConfirmation, + withConfirmation, +} = require("../../utils/deploy"); +const { getAssetAddresses } = require("../../test/helpers.js"); +const { resolveContract } = require("../../utils/resolvers"); +const { getDefenderSigner } = require("../../utils/signersNoHardhat.js"); + +const mainExport = async () => { + console.log("Running 005_upgrade_vault deployment on Hoodi..."); + // Governor is the Defender Relayer + const sDeployer = await getDefenderSigner(); + const assetAddresses = await getAssetAddresses(); + + const cVaultProxy = await resolveContract("OETHVaultProxy"); + const cVault = await resolveContract("OETHVaultProxy", "IVault"); + + const dOETHVaultCore = await deployWithConfirmation("OETHVaultCore", [ + assetAddresses.WETH, + ]); + console.log(`Deployed OETHVaultCore to ${dOETHVaultCore.address}`); + + const dOETHVaultAdmin = await deployWithConfirmation("OETHVaultAdmin", [ + assetAddresses.WETH, + ]); + console.log(`Deployed OETHVaultAdmin to ${dOETHVaultAdmin.address}`); + + await withConfirmation( + cVaultProxy.connect(sDeployer).upgradeTo(dOETHVaultCore.address) + ); + console.log(`Upgraded OETHVaultCore to ${dOETHVaultCore.address}`); + + await withConfirmation( + cVault.connect(sDeployer).setAdminImpl(dOETHVaultAdmin.address) + ); + console.log(`Set OETHVaultAdmin to ${dOETHVaultAdmin.address}`); + + console.log("005_upgrade_vault deploy done."); + return true; +}; + +mainExport.id = "005_upgrade_vault"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/006_upgrade_compounding_staking_strategy.js b/contracts/deploy/hoodi/006_upgrade_compounding_staking_strategy.js new file mode 100644 index 0000000000..ef51df9db2 --- /dev/null +++ b/contracts/deploy/hoodi/006_upgrade_compounding_staking_strategy.js @@ -0,0 +1,15 @@ +const { upgradeCompoundingStakingSSVStrategy } = require("../deployActions"); + +const mainExport = async () => { + await upgradeCompoundingStakingSSVStrategy(); + + console.log("Running 006 deployment done"); + return true; +}; + +mainExport.id = "006_upgrade_compounding_staking_strategy"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/007_upgrade_compounding_staking_strategy.js b/contracts/deploy/hoodi/007_upgrade_compounding_staking_strategy.js new file mode 100644 index 0000000000..a226e36766 --- /dev/null +++ b/contracts/deploy/hoodi/007_upgrade_compounding_staking_strategy.js @@ -0,0 +1,24 @@ +const { + deployBeaconContracts, + upgradeCompoundingStakingSSVStrategy, +} = require("../deployActions"); + +const mainExport = async () => { + console.log( + "Running 007_upgrade_compounding_staking_strategy deployment on Hoodi..." + ); + + await deployBeaconContracts(); + + await upgradeCompoundingStakingSSVStrategy(); + + console.log("Running 007 deployment done"); + return true; +}; + +mainExport.id = "007_upgrade_compounding_staking_strategy"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/008_upgrade_native_staking_strategy.js b/contracts/deploy/hoodi/008_upgrade_native_staking_strategy.js new file mode 100644 index 0000000000..4f8cdcbae0 --- /dev/null +++ b/contracts/deploy/hoodi/008_upgrade_native_staking_strategy.js @@ -0,0 +1,19 @@ +const { upgradeNativeStakingSSVStrategy } = require("../deployActions"); + +const mainExport = async () => { + console.log( + "Running 008_upgrade_native_staking_strategy deployment on Hoodi..." + ); + + await upgradeNativeStakingSSVStrategy(); + + console.log("Running 008 deployment done"); + return true; +}; + +mainExport.id = "008_upgrade_native_staking_strategy"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/009_upgrade_staking_contracts.js b/contracts/deploy/hoodi/009_upgrade_staking_contracts.js new file mode 100644 index 0000000000..ba401cb4d7 --- /dev/null +++ b/contracts/deploy/hoodi/009_upgrade_staking_contracts.js @@ -0,0 +1,22 @@ +const { + upgradeNativeStakingSSVStrategy, + upgradeCompoundingStakingSSVStrategy, +} = require("../deployActions"); + +const mainExport = async () => { + console.log("Running 009_upgrade_staking_contract deployment on Hoodi..."); + + await upgradeNativeStakingSSVStrategy(); + + await upgradeCompoundingStakingSSVStrategy(); + + console.log("Running 009 deployment done"); + return true; +}; + +mainExport.id = "009_upgrade_staking_contract"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/010_upgrade_compounding_staking_strategy.js b/contracts/deploy/hoodi/010_upgrade_compounding_staking_strategy.js new file mode 100644 index 0000000000..ad210e28b8 --- /dev/null +++ b/contracts/deploy/hoodi/010_upgrade_compounding_staking_strategy.js @@ -0,0 +1,15 @@ +const { upgradeCompoundingStakingSSVStrategy } = require("../deployActions"); + +const mainExport = async () => { + await upgradeCompoundingStakingSSVStrategy(); + + console.log("Running 010 deployment done"); + return true; +}; + +mainExport.id = "010_upgrade_compounding_staking_strategy"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/011_upgrade_compounding_staking_strategy.js b/contracts/deploy/hoodi/011_upgrade_compounding_staking_strategy.js new file mode 100644 index 0000000000..043143f562 --- /dev/null +++ b/contracts/deploy/hoodi/011_upgrade_compounding_staking_strategy.js @@ -0,0 +1,15 @@ +const { upgradeCompoundingStakingSSVStrategy } = require("../deployActions"); + +const mainExport = async () => { + await upgradeCompoundingStakingSSVStrategy(); + + console.log("Running 011 deployment done"); + return true; +}; + +mainExport.id = "011_upgrade_compounding_staking_strategy"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/012_upgrade_compounding_staking_strategy.js b/contracts/deploy/hoodi/012_upgrade_compounding_staking_strategy.js new file mode 100644 index 0000000000..5718ca167e --- /dev/null +++ b/contracts/deploy/hoodi/012_upgrade_compounding_staking_strategy.js @@ -0,0 +1,18 @@ +const { deployWithConfirmation } = require("../../utils/deploy"); +const { upgradeCompoundingStakingSSVStrategy } = require("../deployActions"); + +const mainExport = async () => { + await deployWithConfirmation("BeaconProofs", []); + + await upgradeCompoundingStakingSSVStrategy(); + + console.log("Running 012 deployment done"); + return true; +}; + +mainExport.id = "012_upgrade_compounding_staking_strategy"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/013_upgrade_staking_contracts.js b/contracts/deploy/hoodi/013_upgrade_staking_contracts.js new file mode 100644 index 0000000000..0722d2987f --- /dev/null +++ b/contracts/deploy/hoodi/013_upgrade_staking_contracts.js @@ -0,0 +1,15 @@ +const { upgradeCompoundingStakingSSVStrategy } = require("../deployActions"); + +const mainExport = async () => { + await upgradeCompoundingStakingSSVStrategy(); + + console.log("Running 013 deployment done"); + return true; +}; + +mainExport.id = "013_upgrade_staking_contract"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/014_upgrade_staking_contracts.js b/contracts/deploy/hoodi/014_upgrade_staking_contracts.js new file mode 100644 index 0000000000..1f31a7691f --- /dev/null +++ b/contracts/deploy/hoodi/014_upgrade_staking_contracts.js @@ -0,0 +1,15 @@ +const { upgradeCompoundingStakingSSVStrategy } = require("../deployActions"); + +const mainExport = async () => { + await upgradeCompoundingStakingSSVStrategy(); + + console.log("Running 014 deployment done"); + return true; +}; + +mainExport.id = "014_upgrade_staking_contract"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/015_upgrade_staking_contracts.js b/contracts/deploy/hoodi/015_upgrade_staking_contracts.js new file mode 100644 index 0000000000..34a2622ca6 --- /dev/null +++ b/contracts/deploy/hoodi/015_upgrade_staking_contracts.js @@ -0,0 +1,18 @@ +const { deployWithConfirmation } = require("../../utils/deploy"); +const { upgradeCompoundingStakingSSVStrategy } = require("../deployActions"); + +const mainExport = async () => { + await deployWithConfirmation("BeaconProofs", []); + + await upgradeCompoundingStakingSSVStrategy(); + + console.log("Running 015 deployment done"); + return true; +}; + +mainExport.id = "015_upgrade_staking_contract"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/016_upgrade_staking_contracts.js b/contracts/deploy/hoodi/016_upgrade_staking_contracts.js new file mode 100644 index 0000000000..d5414f142e --- /dev/null +++ b/contracts/deploy/hoodi/016_upgrade_staking_contracts.js @@ -0,0 +1,18 @@ +const { deployWithConfirmation } = require("../../utils/deploy"); +const { upgradeCompoundingStakingSSVStrategy } = require("../deployActions"); + +const mainExport = async () => { + await deployWithConfirmation("BeaconProofs", []); + + await upgradeCompoundingStakingSSVStrategy(); + + console.log("Running 016 deployment done"); + return true; +}; + +mainExport.id = "016_upgrade_staking_contract"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/017_upgrade_staking_contracts.js b/contracts/deploy/hoodi/017_upgrade_staking_contracts.js new file mode 100644 index 0000000000..182680d4c0 --- /dev/null +++ b/contracts/deploy/hoodi/017_upgrade_staking_contracts.js @@ -0,0 +1,18 @@ +const { deployWithConfirmation } = require("../../utils/deploy"); +const { upgradeCompoundingStakingSSVStrategy } = require("../deployActions"); + +const mainExport = async () => { + await deployWithConfirmation("BeaconProofs", []); + + await upgradeCompoundingStakingSSVStrategy(); + + console.log("Running 017 deployment done"); + return true; +}; + +mainExport.id = "017_upgrade_staking_contract"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/018_upgrade_staking_contracts.js b/contracts/deploy/hoodi/018_upgrade_staking_contracts.js new file mode 100644 index 0000000000..16e9b2a79a --- /dev/null +++ b/contracts/deploy/hoodi/018_upgrade_staking_contracts.js @@ -0,0 +1,18 @@ +const { deployWithConfirmation } = require("../../utils/deploy"); +const { upgradeCompoundingStakingSSVStrategy } = require("../deployActions"); + +const mainExport = async () => { + await deployWithConfirmation("BeaconProofs", []); + + await upgradeCompoundingStakingSSVStrategy(); + + console.log("Running 018 deployment done"); + return true; +}; + +mainExport.id = "018_upgrade_staking_contract"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/019_upgrade_staking_contracts.js b/contracts/deploy/hoodi/019_upgrade_staking_contracts.js new file mode 100644 index 0000000000..0aee280bcb --- /dev/null +++ b/contracts/deploy/hoodi/019_upgrade_staking_contracts.js @@ -0,0 +1,27 @@ +const { deployWithConfirmation } = require("../../utils/deploy"); +const { resolveContract } = require("../../utils/resolvers"); +const { upgradeCompoundingStakingSSVStrategy } = require("../deployActions"); + +const mainExport = async () => { + await deployWithConfirmation("BeaconProofs", []); + + await upgradeCompoundingStakingSSVStrategy(); + + const cStrategyProxy = await resolveContract( + "CompoundingStakingSSVStrategyProxy" + ); + + await deployWithConfirmation("CompoundingStakingStrategyView", [ + cStrategyProxy.address, + ]); + + console.log("Running 019 deployment done"); + return true; +}; + +mainExport.id = "019_upgrade_staking_contract"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/020_upgrade_staking_contracts.js b/contracts/deploy/hoodi/020_upgrade_staking_contracts.js new file mode 100644 index 0000000000..89f67424eb --- /dev/null +++ b/contracts/deploy/hoodi/020_upgrade_staking_contracts.js @@ -0,0 +1,15 @@ +const { upgradeCompoundingStakingSSVStrategy } = require("../deployActions"); + +const mainExport = async () => { + await upgradeCompoundingStakingSSVStrategy(); + + console.log("Running 020 deployment done"); + return true; +}; + +mainExport.id = "020_upgrade_staking_contract"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/021_deploy_new_staking_contracts.js b/contracts/deploy/hoodi/021_deploy_new_staking_contracts.js new file mode 100644 index 0000000000..2f3b0dc5ac --- /dev/null +++ b/contracts/deploy/hoodi/021_deploy_new_staking_contracts.js @@ -0,0 +1,40 @@ +const { resolveContract } = require("../../utils/resolvers"); +const { deployCompoundingStakingSSVStrategy } = require("../deployActions"); +const { withConfirmation } = require("../../utils/deploy"); +const { getDefenderSigner } = require("../../utils/signersNoHardhat"); +const addresses = require("../../utils/addresses.js"); + +const log = require("../../utils/logger")("deploy:hoodi"); + +const mainExport = async () => { + console.log("Deploy compounding staking strategy"); + const sGovernor = await getDefenderSigner(); + + const compoundingSsvStrategy = await deployCompoundingStakingSSVStrategy(); + + const cVault = await resolveContract("OETHVaultProxy", "IVault"); + + log("Approving compounding strategy on OETH Vault"); + await withConfirmation( + cVault.connect(sGovernor).approveStrategy(compoundingSsvStrategy.address) + ); + + log( + `Setting Registrator on Compounding Strategy to ${addresses.hoodi.defenderRelayer}` + ); + await withConfirmation( + compoundingSsvStrategy + .connect(sGovernor) + .setRegistrator(addresses.hoodi.defenderRelayer) + ); + + console.log("Running 021 deployment done"); + return true; +}; + +mainExport.id = "021_deploy_new_staking_contracts"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/022_upgrade_staking_contracts.js b/contracts/deploy/hoodi/022_upgrade_staking_contracts.js new file mode 100644 index 0000000000..aad25f61b3 --- /dev/null +++ b/contracts/deploy/hoodi/022_upgrade_staking_contracts.js @@ -0,0 +1,15 @@ +const { upgradeCompoundingStakingSSVStrategy } = require("../deployActions"); + +const mainExport = async () => { + await upgradeCompoundingStakingSSVStrategy(); + + console.log("Running 022 deployment done"); + return true; +}; + +mainExport.id = "022_upgrade_staking_contract"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/023_upgrade_staking_contracts.js b/contracts/deploy/hoodi/023_upgrade_staking_contracts.js new file mode 100644 index 0000000000..679932c3aa --- /dev/null +++ b/contracts/deploy/hoodi/023_upgrade_staking_contracts.js @@ -0,0 +1,15 @@ +const { upgradeCompoundingStakingSSVStrategy } = require("../deployActions"); + +const mainExport = async () => { + await upgradeCompoundingStakingSSVStrategy(); + + console.log("Running 023 deployment done"); + return true; +}; + +mainExport.id = "023_upgrade_staking_contract"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/024_upgrade_staking_contracts.js b/contracts/deploy/hoodi/024_upgrade_staking_contracts.js new file mode 100644 index 0000000000..bd55daeeb4 --- /dev/null +++ b/contracts/deploy/hoodi/024_upgrade_staking_contracts.js @@ -0,0 +1,18 @@ +const { deployWithConfirmation } = require("../../utils/deploy"); +const { upgradeCompoundingStakingSSVStrategy } = require("../deployActions"); + +const mainExport = async () => { + await deployWithConfirmation("BeaconProofs", []); + + await upgradeCompoundingStakingSSVStrategy(); + + console.log("Running 024 deployment done"); + return true; +}; + +mainExport.id = "024_upgrade_staking_contract"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/025_deploy_new_staking_contracts.js b/contracts/deploy/hoodi/025_deploy_new_staking_contracts.js new file mode 100644 index 0000000000..4f92843321 --- /dev/null +++ b/contracts/deploy/hoodi/025_deploy_new_staking_contracts.js @@ -0,0 +1,40 @@ +const { resolveContract } = require("../../utils/resolvers"); +const { deployCompoundingStakingSSVStrategy } = require("../deployActions"); +const { withConfirmation } = require("../../utils/deploy"); +const { getDefenderSigner } = require("../../utils/signersNoHardhat"); +const addresses = require("../../utils/addresses.js"); + +const log = require("../../utils/logger")("deploy:hoodi"); + +const mainExport = async () => { + console.log("Deploy new compounding staking strategy"); + const sGovernor = await getDefenderSigner(); + + const compoundingSsvStrategy = await deployCompoundingStakingSSVStrategy(); + + const cVault = await resolveContract("OETHVaultProxy", "IVault"); + + log("Approving compounding strategy on OETH Vault"); + await withConfirmation( + cVault.connect(sGovernor).approveStrategy(compoundingSsvStrategy.address) + ); + + log( + `Setting Registrator on Compounding Strategy to ${addresses.hoodi.defenderRelayer}` + ); + await withConfirmation( + compoundingSsvStrategy + .connect(sGovernor) + .setRegistrator(addresses.hoodi.defenderRelayer) + ); + + console.log("Running 025 deployment done"); + return true; +}; + +mainExport.id = "025_deploy_new_staking_contracts"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/026_upgrade_staking_contracts.js b/contracts/deploy/hoodi/026_upgrade_staking_contracts.js new file mode 100644 index 0000000000..647bc8269d --- /dev/null +++ b/contracts/deploy/hoodi/026_upgrade_staking_contracts.js @@ -0,0 +1,15 @@ +const { upgradeCompoundingStakingSSVStrategy } = require("../deployActions"); + +const mainExport = async () => { + await upgradeCompoundingStakingSSVStrategy(); + + console.log("Running 026 deployment done"); + return true; +}; + +mainExport.id = "026_upgrade_staking_contract"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/027_upgrade_staking_contracts.js b/contracts/deploy/hoodi/027_upgrade_staking_contracts.js new file mode 100644 index 0000000000..240fe17d4c --- /dev/null +++ b/contracts/deploy/hoodi/027_upgrade_staking_contracts.js @@ -0,0 +1,18 @@ +const { deployWithConfirmation } = require("../../utils/deploy"); +const { upgradeCompoundingStakingSSVStrategy } = require("../deployActions"); + +const mainExport = async () => { + await deployWithConfirmation("BeaconProofs", []); + + await upgradeCompoundingStakingSSVStrategy(); + + console.log("Running 027 deployment done"); + return true; +}; + +mainExport.id = "027_upgrade_staking_contract"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/028_deploy_new_staking_contracts.js b/contracts/deploy/hoodi/028_deploy_new_staking_contracts.js new file mode 100644 index 0000000000..f1eba19323 --- /dev/null +++ b/contracts/deploy/hoodi/028_deploy_new_staking_contracts.js @@ -0,0 +1,40 @@ +const { resolveContract } = require("../../utils/resolvers"); +const { deployCompoundingStakingSSVStrategy } = require("../deployActions"); +const { withConfirmation } = require("../../utils/deploy"); +const { getDefenderSigner } = require("../../utils/signersNoHardhat"); +const addresses = require("../../utils/addresses.js"); + +const log = require("../../utils/logger")("deploy:hoodi"); + +const mainExport = async () => { + console.log("Deploy new compounding staking strategy"); + const sGovernor = await getDefenderSigner(); + + const compoundingSsvStrategy = await deployCompoundingStakingSSVStrategy(); + + const cVault = await resolveContract("OETHVaultProxy", "IVault"); + + log("Approving compounding strategy on OETH Vault"); + await withConfirmation( + cVault.connect(sGovernor).approveStrategy(compoundingSsvStrategy.address) + ); + + log( + `Setting Registrator on Compounding Strategy to ${addresses.hoodi.defenderRelayer}` + ); + await withConfirmation( + compoundingSsvStrategy + .connect(sGovernor) + .setRegistrator(addresses.hoodi.defenderRelayer) + ); + + console.log("Running 028 deployment done"); + return true; +}; + +mainExport.id = "028_deploy_new_staking_contracts"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/029_upgrade_staking_contracts.js b/contracts/deploy/hoodi/029_upgrade_staking_contracts.js new file mode 100644 index 0000000000..cb8cd51d6a --- /dev/null +++ b/contracts/deploy/hoodi/029_upgrade_staking_contracts.js @@ -0,0 +1,18 @@ +const { deployWithConfirmation } = require("../../utils/deploy"); +const { upgradeCompoundingStakingSSVStrategy } = require("../deployActions"); + +const mainExport = async () => { + await deployWithConfirmation("BeaconProofs", []); + + await upgradeCompoundingStakingSSVStrategy(); + + console.log("Running 029 deployment done"); + return true; +}; + +mainExport.id = "029_upgrade_staking_contract"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/hoodi/030_upgrade_staking_contracts.js b/contracts/deploy/hoodi/030_upgrade_staking_contracts.js new file mode 100644 index 0000000000..ff0f798b98 --- /dev/null +++ b/contracts/deploy/hoodi/030_upgrade_staking_contracts.js @@ -0,0 +1,18 @@ +const { deployWithConfirmation } = require("../../utils/deploy"); +const { upgradeCompoundingStakingSSVStrategy } = require("../deployActions"); + +const mainExport = async () => { + await deployWithConfirmation("BeaconProofs", []); + + await upgradeCompoundingStakingSSVStrategy(); + + console.log("Running 030 deployment done"); + return true; +}; + +mainExport.id = "030_upgrade_staking_contract"; +mainExport.tags = []; +mainExport.dependencies = []; +mainExport.skip = () => false; + +module.exports = mainExport; diff --git a/contracts/deploy/mainnet/000_mock.js b/contracts/deploy/mainnet/000_mock.js index 252e2f15ad..f2a598e705 100644 --- a/contracts/deploy/mainnet/000_mock.js +++ b/contracts/deploy/mainnet/000_mock.js @@ -441,6 +441,12 @@ const deployMocks = async ({ getNamedAccounts, deployments }) => { from: deployerAddr, }); + await deploy("MockBeaconProofs", { from: deployerAddr }); + await deploy("EnhancedBeaconProofs", { from: deployerAddr }); + await deploy("MockBeaconRoots", { from: deployerAddr }); + const mockBeaconRoots = await ethers.getContract("MockBeaconRoots"); + await replaceContractAt(addresses.mainnet.beaconRoots, mockBeaconRoots); + console.log("000_mock deploy done."); return true; diff --git a/contracts/deploy/mainnet/001_core.js b/contracts/deploy/mainnet/001_core.js index b454ca6612..024146b8b9 100644 --- a/contracts/deploy/mainnet/001_core.js +++ b/contracts/deploy/mainnet/001_core.js @@ -8,6 +8,7 @@ const { deployAaveStrategy, deployConvexStrategy, deployNativeStakingSSVStrategy, + deployCompoundingStakingSSVStrategy, deployDrippers, deployHarvesters, configureVault, @@ -31,6 +32,7 @@ const main = async () => { await deployAaveStrategy(); await deployConvexStrategy(); await deployNativeStakingSSVStrategy(); + await deployCompoundingStakingSSVStrategy(); const [ousdDripper, oethDripper] = await deployDrippers(); const [harvesterProxy, oethHarvesterProxy] = await deployHarvesters( ousdDripper, diff --git a/contracts/deploy/mainnet/152_beacon_root_testing.js b/contracts/deploy/mainnet/152_beacon_root_testing.js new file mode 100644 index 0000000000..d388cfffd7 --- /dev/null +++ b/contracts/deploy/mainnet/152_beacon_root_testing.js @@ -0,0 +1,24 @@ +const { deploymentWithGovernanceProposal } = require("../../utils/deploy"); + +module.exports = deploymentWithGovernanceProposal( + { + deployName: "152_beacon_root_testing", + forceDeploy: false, + //forceSkip: true, + reduceQueueTime: true, + deployerIsProposer: false, + // proposalId: "", + }, + async ({ deployWithConfirmation }) => { + // 1. Deploy the MockBeaconRoots + const dMockBeaconRoots = await deployWithConfirmation("MockBeaconRoots"); + console.log(`Deployed MockBeaconRoots ${dMockBeaconRoots.address}`); + + // Governance Actions + // ---------------- + return { + name: "", + actions: [], + }; + } +); diff --git a/contracts/deploy/mainnet/153_upgrade_native_staking.js b/contracts/deploy/mainnet/153_upgrade_native_staking.js new file mode 100644 index 0000000000..6d27b9950b --- /dev/null +++ b/contracts/deploy/mainnet/153_upgrade_native_staking.js @@ -0,0 +1,118 @@ +const { deploymentWithGovernanceProposal } = require("../../utils/deploy"); +const { deployCompoundingStakingSSVStrategy } = require("../deployActions"); +const addresses = require("../../utils/addresses"); + +module.exports = deploymentWithGovernanceProposal( + { + deployName: "153_upgrade_native_staking", + forceDeploy: false, + //forceSkip: true, + reduceQueueTime: true, + deployerIsProposer: false, + // proposalId: "", + }, + async ({ deployWithConfirmation, ethers }) => { + // Current contracts + const cVaultProxy = await ethers.getContract("OETHVaultProxy"); + const cVaultAdmin = await ethers.getContractAt( + "OETHVaultAdmin", + cVaultProxy.address + ); + // Deployer Actions + // ---------------- + + // 1. Fetch the Native Strategy proxies + const cNativeStakingStrategyProxy_2 = await ethers.getContract( + "NativeStakingSSVStrategy2Proxy" + ); + const cNativeStakingStrategyProxy_3 = await ethers.getContract( + "NativeStakingSSVStrategy3Proxy" + ); + + // 2. Fetch the Fee Accumulator proxies + const cFeeAccumulatorProxy_2 = await ethers.getContract( + "NativeStakingFeeAccumulator2Proxy" + ); + const cFeeAccumulatorProxy_3 = await ethers.getContract( + "NativeStakingFeeAccumulator3Proxy" + ); + + // 3. Deploy the new Native Staking Strategy implementation + const dNativeStakingStrategyImpl_2 = await deployWithConfirmation( + "NativeStakingSSVStrategy", + [ + [addresses.zero, cVaultProxy.address], //_baseConfig + addresses.mainnet.WETH, // wethAddress + addresses.mainnet.SSV, // ssvToken + addresses.mainnet.SSVNetwork, // ssvNetwork + 500, // maxValidators + cFeeAccumulatorProxy_2.address, // feeAccumulator + addresses.mainnet.beaconChainDepositContract, // beacon chain deposit contract + ] + ); + console.log( + `Deployed 2nd NativeStakingSSVStrategy ${dNativeStakingStrategyImpl_2.address}` + ); + + const dNativeStakingStrategyImpl_3 = await deployWithConfirmation( + "NativeStakingSSVStrategy", + [ + [addresses.zero, cVaultProxy.address], //_baseConfig + addresses.mainnet.WETH, // wethAddress + addresses.mainnet.SSV, // ssvToken + addresses.mainnet.SSVNetwork, // ssvNetwork + 500, // maxValidators + cFeeAccumulatorProxy_3.address, // feeAccumulator + addresses.mainnet.beaconChainDepositContract, // beacon chain deposit contract + ], + undefined, + true // skipUpgradeSafety + ); + console.log( + `Deployed 3rd NativeStakingSSVStrategy ${dNativeStakingStrategyImpl_3.address}` + ); + + // 4. Deploy the new Compounding Staking Strategy contracts + const cCompoundingStakingStrategy = + await deployCompoundingStakingSSVStrategy(); + + // Governance Actions + // ---------------- + return { + name: `Upgrade the existing Native Staking Strategies and deploy new Compounding Staking Strategy`, + actions: [ + // 1. Upgrade the second Native Staking Strategy + { + contract: cNativeStakingStrategyProxy_2, + signature: "upgradeTo(address)", + args: [dNativeStakingStrategyImpl_2.address], + }, + // 2. Upgrade the third Native Staking Strategy + { + contract: cNativeStakingStrategyProxy_3, + signature: "upgradeTo(address)", + args: [dNativeStakingStrategyImpl_3.address], + }, + // 3. Add new strategy to vault + { + contract: cVaultAdmin, + signature: "approveStrategy(address)", + args: [cCompoundingStakingStrategy.address], + }, + // 4. set harvester to the Defender Relayer + { + contract: cCompoundingStakingStrategy, + signature: "setHarvesterAddress(address)", + args: [addresses.mainnet.validatorRegistrator], + }, + // 5. set validator registrator to the Defender Relayer + { + contract: cCompoundingStakingStrategy, + signature: "setRegistrator(address)", + // The Defender Relayer + args: [addresses.mainnet.validatorRegistrator], + }, + ], + }; + } +); diff --git a/contracts/deployments/hoodi/.migrations.json b/contracts/deployments/hoodi/.migrations.json index 0967ef424b..f9d0cad3cd 100644 --- a/contracts/deployments/hoodi/.migrations.json +++ b/contracts/deployments/hoodi/.migrations.json @@ -1 +1,32 @@ -{} +{ + "001_core": 1752833738, + "002_beacon_root_testing": 1753281051, + "003_upgrade_compounding_staking_strategy": 1753792336, + "004_set_strategist": 1753796746, + "005_upgrade_vault": 1753836739, + "006_upgrade_compounding_staking_strategy": 1753837350, + "007_upgrade_compounding_staking_strategy": 1753845225, + "008_upgrade_native_staking_strategy": 1753874668, + "009_upgrade_staking_contract": 1753961442, + "010_upgrade_compounding_staking_strategy": 1754030898, + "011_upgrade_compounding_staking_strategy": 1754034570, + "012_upgrade_compounding_staking_strategy": 1754346065, + "013_upgrade_staking_contract": 1754370520, + "014_upgrade_staking_contract": 1754392444, + "015_upgrade_staking_contract": 1754401746, + "016_upgrade_staking_contract": 1754446134, + "017_upgrade_staking_contract": 1754467673, + "018_upgrade_staking_contract": 1754627314, + "019_upgrade_staking_contract": 1755919597, + "020_upgrade_staking_contract": 1755920788, + "021_deploy_new_staking_contracts": 1756002665, + "022_upgrade_staking_contract": 1756016922, + "023_upgrade_staking_contract": 1756243916, + "024_upgrade_staking_contract": 1756527898, + "025_deploy_new_staking_contracts": 1756691955, + "026_upgrade_staking_contract": 1756696253, + "027_upgrade_staking_contract": 1757382067, + "028_deploy_new_staking_contracts": 1757935264, + "029_upgrade_staking_contract": 1758151852, + "030_upgrade_staking_contract": 1758515961 +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/BeaconOracle.json b/contracts/deployments/hoodi/BeaconOracle.json new file mode 100644 index 0000000000..cf2295b140 --- /dev/null +++ b/contracts/deployments/hoodi/BeaconOracle.json @@ -0,0 +1,288 @@ +{ + "address": "0x6dD9Fde25eDdD7a7adc9F6A3DBB04AF059c40d2b", + "abi": [ + { + "inputs": [], + "name": "InvalidProofLength", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "blockRoot", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "slot", + "type": "uint64" + } + ], + "name": "BlockToSlot", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + } + ], + "name": "blockToSlot", + "outputs": [ + { + "internalType": "uint64", + "name": "slot", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + } + ], + "name": "isBlockMapped", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "slot", + "type": "uint64" + } + ], + "name": "isSlotMapped", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "slot", + "type": "uint64" + } + ], + "name": "slotToBlock", + "outputs": [ + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "slot", + "type": "uint64" + } + ], + "name": "slotToRoot", + "outputs": [ + { + "internalType": "bytes32", + "name": "blockRoot", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "nextBlockTimestamp", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "slot", + "type": "uint64" + }, + { + "internalType": "bytes", + "name": "slotProof", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "blockProof", + "type": "bytes" + } + ], + "name": "verifySlot", + "outputs": [ + { + "internalType": "bytes32", + "name": "blockRoot", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "transactionHash": "0x289ac15437ed68fd1f897db9ad978e5ebbd4a8e4758016d788b6de584b0a75fc", + "receipt": { + "to": null, + "from": "0xf7749B41db006860cEc0650D18b8013d69C44Eeb", + "contractAddress": "0x6dD9Fde25eDdD7a7adc9F6A3DBB04AF059c40d2b", + "transactionIndex": 9, + "gasUsed": "559627", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0xf2152ee6d5b231dbde4c2b708b965b2a356b4a263dea0c4d453bbce8ca4bf1bb", + "transactionHash": "0x289ac15437ed68fd1f897db9ad978e5ebbd4a8e4758016d788b6de584b0a75fc", + "logs": [], + "blockNumber": 905471, + "cumulativeGasUsed": "3595131", + "status": 1, + "byzantium": true + }, + "args": [], + "numDeployments": 2, + "solcInputHash": "3de98f56666780054699342674f08b14", + "metadata": "{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"InvalidProofLength\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"blockRoot\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"slot\",\"type\":\"uint64\"}],\"name\":\"BlockToSlot\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"}],\"name\":\"blockToSlot\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"slot\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"}],\"name\":\"isBlockMapped\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"slot\",\"type\":\"uint64\"}],\"name\":\"isSlotMapped\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"slot\",\"type\":\"uint64\"}],\"name\":\"slotToBlock\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"slot\",\"type\":\"uint64\"}],\"name\":\"slotToRoot\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"blockRoot\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"nextBlockTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"slot\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"slotProof\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"blockProof\",\"type\":\"bytes\"}],\"name\":\"verifySlot\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"blockRoot\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"author\":\"Origin Protocol Inc\",\"kind\":\"dev\",\"methods\":{\"verifySlot(uint64,uint64,uint64,bytes,bytes)\":{\"params\":{\"blockNumber\":\"The execution layer block number.\",\"blockProof\":\"The merkle proof witnesses for the block number against the beacon block root.\",\"nextBlockTimestamp\":\"The timestamp of the block after the one being proven.\",\"slot\":\"The beacon chain slot.\",\"slotProof\":\"The merkle proof witnesses for the slot against the beacon block root.\"}}},\"title\":\"Beacon Chain Oracle\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"blockToSlot(uint64)\":{\"notice\":\"Returns the beacon chain slot for a given execution layer block number.\"},\"isBlockMapped(uint64)\":{\"notice\":\"Returns true if an execution layer block number has been mapped to a beacon chain slot.\"},\"isSlotMapped(uint64)\":{\"notice\":\"Returns true if a beacon chain slot has been mapped to an execution layer block number.\"},\"slotToBlock(uint64)\":{\"notice\":\"Returns the execution layer block number for a given beacon chain slot.\"},\"slotToRoot(uint64)\":{\"notice\":\"Returns the beacon block root for a given beacon chain slot.\"},\"verifySlot(uint64,uint64,uint64,bytes,bytes)\":{\"notice\":\"Uses merkle a proof against the beacon block root to link an execution layer block number to a beacon chain slot.\"}},\"notice\":\"An Oracle for mapping execution layer block numbers to beacon chain slots.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/beacon/BeaconOracle.sol\":\"BeaconOracle\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"contracts/beacon/BeaconOracle.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\nimport { BeaconProofsLib } from \\\"./BeaconProofsLib.sol\\\";\\nimport { BeaconRoots } from \\\"./BeaconRoots.sol\\\";\\n\\n/// @title Beacon Chain Oracle\\n/// @notice An Oracle for mapping execution layer block numbers to beacon chain slots.\\n/// @author Origin Protocol Inc\\ncontract BeaconOracle {\\n /// @notice Maps a block number to slot\\n mapping(uint64 => uint64) internal _blockToSlot;\\n /// @notice Maps a slot to a number\\n mapping(uint64 => uint64) internal _slotToBlock;\\n /// @notice Maps a slot to a beacon block root\\n mapping(uint64 => bytes32) internal _slotToRoot;\\n\\n event BlockToSlot(\\n bytes32 indexed blockRoot,\\n uint64 indexed blockNumber,\\n uint64 indexed slot\\n );\\n\\n /// @notice Uses merkle a proof against the beacon block root to link\\n /// an execution layer block number to a beacon chain slot.\\n /// @param nextBlockTimestamp The timestamp of the block after the one being proven.\\n /// @param blockNumber The execution layer block number.\\n /// @param slot The beacon chain slot.\\n /// @param slotProof The merkle proof witnesses for the slot against the beacon block root.\\n /// @param blockProof The merkle proof witnesses for the block number against the beacon block root.\\n function verifySlot(\\n uint64 nextBlockTimestamp,\\n uint64 blockNumber,\\n uint64 slot,\\n bytes calldata slotProof,\\n bytes calldata blockProof\\n ) external returns (bytes32 blockRoot) {\\n require(_blockToSlot[blockNumber] == 0, \\\"Block already mapped\\\");\\n\\n // Get the parent beacon block root for the given timestamp.\\n // This is the beacon block root of the previous slot.\\n blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\\n\\n // Verify the slot to the beacon block root\\n BeaconProofsLib.verifySlot(blockRoot, slot, slotProof);\\n\\n // Verify the block number to the beacon block root\\n BeaconProofsLib.verifyBlockNumber(blockRoot, blockNumber, blockProof);\\n\\n // Store mappings\\n _blockToSlot[blockNumber] = slot;\\n _slotToBlock[slot] = blockNumber;\\n _slotToRoot[slot] = blockRoot;\\n\\n emit BlockToSlot(blockRoot, blockNumber, slot);\\n }\\n\\n /// @notice Returns the beacon chain slot for a given execution layer block number.\\n function blockToSlot(uint64 blockNumber)\\n external\\n view\\n returns (uint64 slot)\\n {\\n slot = _blockToSlot[blockNumber];\\n\\n require(slot != 0, \\\"Block not mapped\\\");\\n }\\n\\n /// @notice Returns the execution layer block number for a given beacon chain slot.\\n function slotToBlock(uint64 slot)\\n external\\n view\\n returns (uint64 blockNumber)\\n {\\n blockNumber = _slotToBlock[slot];\\n\\n require(blockNumber != 0, \\\"Slot not mapped\\\");\\n }\\n\\n /// @notice Returns the beacon block root for a given beacon chain slot.\\n function slotToRoot(uint64 slot) external view returns (bytes32 blockRoot) {\\n blockRoot = _slotToRoot[slot];\\n\\n require(blockRoot != 0, \\\"Slot not mapped\\\");\\n }\\n\\n /// @notice Returns true if an execution layer block number has been mapped to a beacon chain slot.\\n function isBlockMapped(uint64 blockNumber) external view returns (bool) {\\n return _blockToSlot[blockNumber] != 0;\\n }\\n\\n /// @notice Returns true if a beacon chain slot has been mapped to an execution layer block number.\\n function isSlotMapped(uint64 slot) external view returns (bool) {\\n return _slotToBlock[slot] != 0;\\n }\\n}\\n\",\"keccak256\":\"0x66093d2922dec7e8381b4694f46afc1396ff7ac2687d2c49b494bf56b865d20f\",\"license\":\"MIT\"},\"contracts/beacon/BeaconProofsLib.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { Merkle } from \\\"./Merkle.sol\\\";\\nimport { Endian } from \\\"./Endian.sol\\\";\\n\\nlibrary BeaconProofsLib {\\n // Known generalized indices in the beacon block\\n // BeaconBlock.slot\\n uint256 internal constant SLOT_GENERALIZED_INDEX = 8;\\n // BeaconBlock.state.PendingDeposits[0].slot\\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX =\\n 1584842932228;\\n // BeaconBlock.body.executionPayload.blockNumber\\n uint256 internal constant BLOCK_NUMBER_GENERALIZED_INDEX = 6438;\\n // BeaconBlock.state.validators\\n uint256 internal constant VALIDATORS_CONTAINER_GENERALIZED_INDEX = 715;\\n // BeaconBlock.state.balances\\n uint256 internal constant BALANCES_CONTAINER_GENERALIZED_INDEX = 716;\\n\\n // Beacon Container Tree Heights\\n uint256 internal constant BALANCES_HEIGHT = 39;\\n uint256 internal constant VALIDATORS_HEIGHT = 41;\\n uint256 internal constant VALIDATOR_HEIGHT = 3;\\n\\n /// @notice Fields in the Validator container for phase 0\\n /// See https://ethereum.github.io/consensus-specs/specs/phase0/beacon-chain/#validator\\n uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;\\n\\n enum BalanceProofLevel {\\n Container,\\n BeaconBlock\\n }\\n\\n /// @notice Verifies the validator public key against the beacon block root\\n /// BeaconBlock.state.validators[validatorIndex].pubkey\\n /// @param beaconBlockRoot The root of the beacon block\\n /// @param pubKeyHash The beacon chain hash of the validator public key\\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\\n /// This is the witness hashes concatenated together starting from the leaf node.\\n /// @param validatorIndex The validator index\\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\\n function verifyValidatorPubkey(\\n bytes32 beaconBlockRoot,\\n bytes32 pubKeyHash,\\n bytes calldata validatorPubKeyProof,\\n uint64 validatorIndex,\\n address withdrawalAddress\\n ) internal view {\\n // BeaconBlock.state.validators[validatorIndex].pubkey\\n uint256 generalizedIndex = concatGenIndices(\\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\\n VALIDATORS_HEIGHT,\\n validatorIndex\\n );\\n generalizedIndex = concatGenIndices(\\n generalizedIndex,\\n VALIDATOR_HEIGHT,\\n VALIDATOR_PUBKEY_INDEX\\n );\\n\\n // Get the withdrawal address from the first witness in the pubkey merkle proof.\\n address withdrawalAddressFromProof;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n // The first 32 bytes of the proof is the withdrawal credential so load it into memory.\\n calldatacopy(0, validatorPubKeyProof.offset, 32)\\n // Cast the 32 bytes in memory to an address which is the last 20 bytes.\\n withdrawalAddressFromProof := mload(0)\\n }\\n require(\\n withdrawalAddressFromProof == withdrawalAddress,\\n \\\"Invalid withdrawal address\\\"\\n );\\n\\n require(\\n Merkle.verifyInclusionSha256({\\n proof: validatorPubKeyProof,\\n root: beaconBlockRoot,\\n leaf: pubKeyHash,\\n index: generalizedIndex\\n }),\\n \\\"Invalid validator pubkey proof\\\"\\n );\\n }\\n\\n /// @notice Verifies the balances container against the beacon block root\\n /// BeaconBlock.state.balances\\n /// @param beaconBlockRoot The root of the beacon block\\n /// @param balancesContainerLeaf The leaf node containing the balances container\\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\\n /// This is the witness hashes concatenated together starting from the leaf node.\\n function verifyBalancesContainer(\\n bytes32 beaconBlockRoot,\\n bytes32 balancesContainerLeaf,\\n bytes calldata balancesContainerProof\\n ) internal view {\\n // BeaconBlock.state.balances\\n require(\\n Merkle.verifyInclusionSha256({\\n proof: balancesContainerProof,\\n root: beaconBlockRoot,\\n leaf: balancesContainerLeaf,\\n index: BALANCES_CONTAINER_GENERALIZED_INDEX\\n }),\\n \\\"Invalid balance container proof\\\"\\n );\\n }\\n\\n /// @notice Verifies the validator balance against the root of the Balances container\\n /// or the beacon block root\\n /// @param root The root of the Balances container or the beacon block root\\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances\\n /// @param balanceProof The merkle proof for the validator balance against the root.\\n /// This is the witness hashes concatenated together starting from the leaf node.\\n /// @param validatorIndex The validator index to verify the balance for\\n /// @param level The level of the balance proof, either Container or BeaconBlock\\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\\n function verifyValidatorBalance(\\n bytes32 root,\\n bytes32 validatorBalanceLeaf,\\n bytes calldata balanceProof,\\n uint64 validatorIndex,\\n BalanceProofLevel level\\n ) internal view returns (uint256 validatorBalanceGwei) {\\n // Four balances are stored in each leaf so the validator index is divided by 4\\n uint64 balanceIndex = validatorIndex / 4;\\n\\n // slither-disable-next-line uninitialized-local\\n uint256 generalizedIndex;\\n if (level == BalanceProofLevel.Container) {\\n // Get the index within the balances container, not the Beacon Block\\n // BeaconBlock.state.balances[balanceIndex]\\n generalizedIndex = concatGenIndices(\\n 1,\\n BALANCES_HEIGHT,\\n balanceIndex\\n );\\n }\\n\\n if (level == BalanceProofLevel.BeaconBlock) {\\n generalizedIndex = concatGenIndices(\\n BALANCES_CONTAINER_GENERALIZED_INDEX,\\n BALANCES_HEIGHT,\\n balanceIndex\\n );\\n }\\n\\n validatorBalanceGwei = balanceAtIndex(\\n validatorBalanceLeaf,\\n validatorIndex\\n );\\n\\n require(\\n Merkle.verifyInclusionSha256({\\n proof: balanceProof,\\n root: root,\\n leaf: validatorBalanceLeaf,\\n index: generalizedIndex\\n }),\\n \\\"Invalid balance proof\\\"\\n );\\n }\\n\\n /// @notice Verifies the slot of the first pending deposit against the beacon block root\\n /// BeaconBlock.state.PendingDeposits[0].slot\\n /// @param beaconBlockRoot The root of the beacon block\\n /// @param slot The beacon chain slot to verify\\n /// @param firstPendingDepositSlotProof The merkle proof for the first pending deposit's slot\\n /// against the beacon block root.\\n /// This is the witness hashes concatenated together starting from the leaf node.\\n function verifyFirstPendingDepositSlot(\\n bytes32 beaconBlockRoot,\\n uint64 slot,\\n bytes calldata firstPendingDepositSlotProof\\n ) internal view {\\n // Convert uint64 slot number to a little endian bytes32\\n bytes32 slotLeaf = Endian.toLittleEndianUint64(slot);\\n\\n require(\\n Merkle.verifyInclusionSha256({\\n proof: firstPendingDepositSlotProof,\\n root: beaconBlockRoot,\\n leaf: slotLeaf,\\n index: FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX\\n }),\\n \\\"Invalid pending deposit proof\\\"\\n );\\n }\\n\\n /// @notice Verifies the block number to the the beacon block root\\n /// BeaconBlock.body.executionPayload.blockNumber\\n /// @param beaconBlockRoot The root of the beacon block\\n /// @param blockNumber The execution layer block number to verify\\n /// @param blockNumberProof The merkle proof for the block number against the beacon block\\n /// This is the witness hashes concatenated together starting from the leaf node.\\n function verifyBlockNumber(\\n bytes32 beaconBlockRoot,\\n uint256 blockNumber,\\n bytes calldata blockNumberProof\\n ) internal view {\\n // Convert uint64 block number to a little endian bytes32\\n bytes32 blockNumberLeaf = Endian.toLittleEndianUint64(\\n uint64(blockNumber)\\n );\\n require(\\n Merkle.verifyInclusionSha256({\\n proof: blockNumberProof,\\n root: beaconBlockRoot,\\n leaf: blockNumberLeaf,\\n index: BLOCK_NUMBER_GENERALIZED_INDEX\\n }),\\n \\\"Invalid block number proof\\\"\\n );\\n }\\n\\n /// @notice Verifies the slot number against the beacon block root.\\n /// BeaconBlock.slot\\n /// @param beaconBlockRoot The root of the beacon block\\n /// @param slot The beacon chain slot to verify\\n /// @param slotProof The merkle proof for the slot against the beacon block root.\\n /// This is the witness hashes concatenated together starting from the leaf node.\\n function verifySlot(\\n bytes32 beaconBlockRoot,\\n uint256 slot,\\n bytes calldata slotProof\\n ) internal view {\\n require(\\n Merkle.verifyInclusionSha256({\\n proof: slotProof,\\n root: beaconBlockRoot,\\n leaf: Endian.toLittleEndianUint64(uint64(slot)),\\n index: SLOT_GENERALIZED_INDEX\\n }),\\n \\\"Invalid slot number proof\\\"\\n );\\n }\\n\\n ////////////////////////////////////////////////////\\n /// Internal Helper Functions\\n ////////////////////////////////////////////////////\\n\\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\\n internal\\n pure\\n returns (uint256)\\n {\\n uint256 bitShiftAmount = (validatorIndex % 4) * 64;\\n return\\n Endian.fromLittleEndianUint64(\\n bytes32((uint256(validatorBalanceLeaf) << bitShiftAmount))\\n );\\n }\\n\\n /// @notice Concatenates two beacon chain generalized indices into one.\\n /// @param genIndex The first generalized index or 1 if calculating for a single container.\\n /// @param height The merkle tree height of the second container. eg 39 for balances, 41 for validators.\\n /// @param index The index within the second container. eg the validator index.\\n /// @return genIndex The concatenated generalized index.\\n function concatGenIndices(\\n uint256 genIndex,\\n uint256 height,\\n uint256 index\\n ) internal pure returns (uint256) {\\n return (genIndex << height) | index;\\n }\\n}\\n\",\"keccak256\":\"0x2cdd350db8a5964fd876c9fc5c5ed1c9a43432d77a5f2454fd80862f92bacf9c\",\"license\":\"BUSL-1.1\"},\"contracts/beacon/BeaconRoots.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nlibrary BeaconRoots {\\n /// @notice The address of beacon block roots oracle\\n /// See https://eips.ethereum.org/EIPS/eip-4788\\n address internal constant BEACON_ROOTS_ADDRESS =\\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\\n\\n /// @notice Returns the Beacon Block Root for the previous block.\\n /// This comes from the Beacon Roots contract defined in EIP-4788.\\n /// This will revert if the block is more than 8,191 blocks old as\\n /// that is the size of the beacon root's ring buffer.\\n /// @param timestamp The timestamp of the block for which to get the parent root.\\n /// @return parentRoot The parent block root for the given timestamp.\\n function parentBlockRoot(uint64 timestamp)\\n internal\\n view\\n returns (bytes32 parentRoot)\\n {\\n // Call the Beacon Block Root Oracle to get the parent block root\\n // This does not have a function signature, so we use a staticcall.\\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\\n abi.encode(timestamp)\\n );\\n\\n require(success && result.length > 0, \\\"Invalid beacon timestamp\\\");\\n parentRoot = abi.decode(result, (bytes32));\\n }\\n}\\n\",\"keccak256\":\"0x8b678da4c214faae35a2a5dd99b32e2a12f68676ddc2620f0f684bc7c4bb517c\",\"license\":\"BUSL-1.1\"},\"contracts/beacon/Endian.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\nlibrary Endian {\\n /**\\n * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64\\n * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type\\n * @return n The big endian-formatted uint64\\n * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits),\\n * but it is immediately truncated to a uint64 (i.e. 64 bits)\\n * through a right-shift/shr operation.\\n */\\n function fromLittleEndianUint64(bytes32 lenum)\\n internal\\n pure\\n returns (uint64 n)\\n {\\n // the number needs to be stored in little-endian encoding (ie in bytes 0-8)\\n n = uint64(uint256(lenum >> 192));\\n // forgefmt: disable-next-item\\n return\\n (n >> 56) |\\n ((0x00FF000000000000 & n) >> 40) |\\n ((0x0000FF0000000000 & n) >> 24) |\\n ((0x000000FF00000000 & n) >> 8) |\\n ((0x00000000FF000000 & n) << 8) |\\n ((0x0000000000FF0000 & n) << 24) |\\n ((0x000000000000FF00 & n) << 40) |\\n ((0x00000000000000FF & n) << 56);\\n }\\n\\n function toLittleEndianUint64(uint64 benum)\\n internal\\n pure\\n returns (bytes32 n)\\n {\\n // Convert to little-endian by reversing byte order\\n uint64 reversed = (benum >> 56) |\\n ((0x00FF000000000000 & benum) >> 40) |\\n ((0x0000FF0000000000 & benum) >> 24) |\\n ((0x000000FF00000000 & benum) >> 8) |\\n ((0x00000000FF000000 & benum) << 8) |\\n ((0x0000000000FF0000 & benum) << 24) |\\n ((0x000000000000FF00 & benum) << 40) |\\n ((0x00000000000000FF & benum) << 56);\\n\\n // Store the little-endian uint64 in the least significant 64 bits of bytes32\\n n = bytes32(uint256(reversed));\\n // Shift to most significant bits\\n n = n << 192;\\n }\\n}\\n\",\"keccak256\":\"0xd1184c7f56025b694bc4dfe20b610ed208b973eb9040d4084708c39511a42c68\",\"license\":\"MIT\"},\"contracts/beacon/Merkle.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev These functions deal with verification of Merkle Tree proofs.\\n *\\n * The tree and the proofs can be generated using our\\n * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].\\n * You will find a quickstart guide in the readme.\\n *\\n * WARNING: You should avoid using leaf values that are 64 bytes long prior to\\n * hashing, or use a hash function other than keccak256 for hashing leaves.\\n * This is because the concatenation of a sorted pair of internal nodes in\\n * the merkle tree could be reinterpreted as a leaf value.\\n * OpenZeppelin's JavaScript library generates merkle trees that are safe\\n * against this attack out of the box.\\n */\\nlibrary Merkle {\\n error InvalidProofLength();\\n\\n /**\\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\\n * hash matches the root of the tree. The tree is built assuming `leaf` is\\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\\n *\\n * Note this is for a Merkle tree using the sha256 hash function\\n */\\n function verifyInclusionSha256(\\n bytes memory proof,\\n bytes32 root,\\n bytes32 leaf,\\n uint256 index\\n ) internal view returns (bool) {\\n return processInclusionProofSha256(proof, leaf, index) == root;\\n }\\n\\n /**\\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\\n * hash matches the root of the tree. The tree is built assuming `leaf` is\\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\\n *\\n * _Available since v4.4._\\n *\\n * Note this is for a Merkle tree using the sha256 hash function\\n */\\n function processInclusionProofSha256(\\n bytes memory proof,\\n bytes32 leaf,\\n uint256 index\\n ) internal view returns (bytes32) {\\n require(\\n proof.length != 0 && proof.length % 32 == 0,\\n InvalidProofLength()\\n );\\n bytes32[1] memory computedHash = [leaf];\\n for (uint256 i = 32; i <= proof.length; i += 32) {\\n if (index % 2 == 0) {\\n // if ith bit of index is 0, then computedHash is a left sibling\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n mstore(0x00, mload(computedHash))\\n mstore(0x20, mload(add(proof, i)))\\n if iszero(\\n staticcall(\\n sub(gas(), 2000),\\n 2,\\n 0x00,\\n 0x40,\\n computedHash,\\n 0x20\\n )\\n ) {\\n revert(0, 0)\\n }\\n index := div(index, 2)\\n }\\n } else {\\n // if ith bit of index is 1, then computedHash is a right sibling\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n mstore(0x00, mload(add(proof, i)))\\n mstore(0x20, mload(computedHash))\\n if iszero(\\n staticcall(\\n sub(gas(), 2000),\\n 2,\\n 0x00,\\n 0x40,\\n computedHash,\\n 0x20\\n )\\n ) {\\n revert(0, 0)\\n }\\n index := div(index, 2)\\n }\\n }\\n }\\n return computedHash[0];\\n }\\n}\\n\",\"keccak256\":\"0x01e60fb19fe4439b27c4b7fa6e7fc68892b2affa3c4ef8769fa24ad3d20e2273\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x6080604052348015600f57600080fd5b5061092b8061001f6000396000f3fe608060405234801561001057600080fd5b50600436106100625760003560e01c806347b59153146100675780635f09d08c146100975780638e514b5b146100d5578063984f9c32146100e8578063c5242c0214610116578063fbbd18bc14610137575b600080fd5b61007a610075366004610755565b61014a565b6040516001600160401b0390911681526020015b60405180910390f35b6100c56100a5366004610755565b6001600160401b0390811660009081526001602052604090205416151590565b604051901515815260200161008e565b61007a6100e3366004610755565b6101b3565b6100c56100f6366004610755565b6001600160401b0390811660009081526020819052604090205416151590565b610129610124366004610755565b610213565b60405190815260200161008e565b6101296101453660046107bf565b61026e565b6001600160401b03808216600090815260016020526040812054909116908190036101ae5760405162461bcd60e51b815260206004820152600f60248201526e14db1bdd081b9bdd081b585c1c1959608a1b60448201526064015b60405180910390fd5b919050565b6001600160401b03808216600090815260208190526040812054909116908190036101ae5760405162461bcd60e51b815260206004820152601060248201526f109b1bd8dac81b9bdd081b585c1c195960821b60448201526064016101a5565b6001600160401b038116600090815260026020526040812054908190036101ae5760405162461bcd60e51b815260206004820152600f60248201526e14db1bdd081b9bdd081b585c1c1959608a1b60448201526064016101a5565b6001600160401b03808716600090815260208190526040812054909116156102cf5760405162461bcd60e51b8152602060048201526014602482015273109b1bd8dac8185b1c9958591e481b585c1c195960621b60448201526064016101a5565b6102d888610385565b90506102ef81876001600160401b0316878761048a565b61030481886001600160401b03168585610529565b6001600160401b038088166000818152602081815260408083208054958c1667ffffffffffffffff199687168117909155808452600183528184208054909616851790955560029091528082208590555184917f1269041f7d9261ffae4d51c46319f36ddae183581e04ce1532817b3e36ddef7f91a4979650505050505050565b604080516001600160401b038316602082015260009182918291720f3df6d732807ef1319fb7b8bb8522d0beac02910160408051601f19818403018152908290526103cf91610864565b600060405180830381855afa9150503d806000811461040a576040519150601f19603f3d011682016040523d82523d6000602084013e61040f565b606091505b5091509150818015610422575060008151115b61046e5760405162461bcd60e51b815260206004820152601860248201527f496e76616c696420626561636f6e2074696d657374616d70000000000000000060448201526064016101a5565b808060200190518101906104829190610893565b949350505050565b6104d782828080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508892506104d091508790506105cf565b6008610649565b6105235760405162461bcd60e51b815260206004820152601960248201527f496e76616c696420736c6f74206e756d6265722070726f6f660000000000000060448201526064016101a5565b50505050565b6000610534846105cf565b905061057c83838080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508992508591506119269050610649565b6105c85760405162461bcd60e51b815260206004820152601a60248201527f496e76616c696420626c6f636b206e756d6265722070726f6f6600000000000060448201526064016101a5565b5050505050565b603881811c60ff16602883811c61ff001691909117601884811c62ff00001691909117600885811c63ff000000169190911764ff000000009186901b919091161765ff00000000009185901b919091161766ff0000000000009184901b919091161767ff000000000000009290911b919091161760c01b90565b600083610657868585610661565b1495945050505050565b6000835160001415801561068057506020845161067e91906108ac565b155b61069d576040516313717da960e21b815260040160405180910390fd5b604080516020808201909252848152905b85518111610734576106c16002856108ac565b6000036106f7578151600052808601516020526020826040600060026107d05a03fa6106ec57600080fd5b600284049350610722565b8086015160005281516020526020826040600060026107d05a03fa61071b57600080fd5b6002840493505b61072d6020826108ce565b90506106ae565b5051949350505050565b80356001600160401b03811681146101ae57600080fd5b60006020828403121561076757600080fd5b6107708261073e565b9392505050565b60008083601f84011261078957600080fd5b5081356001600160401b038111156107a057600080fd5b6020830191508360208285010111156107b857600080fd5b9250929050565b600080600080600080600060a0888a0312156107da57600080fd5b6107e38861073e565b96506107f16020890161073e565b95506107ff6040890161073e565b945060608801356001600160401b0381111561081a57600080fd5b6108268a828b01610777565b90955093505060808801356001600160401b0381111561084557600080fd5b6108518a828b01610777565b989b979a50959850939692959293505050565b6000825160005b81811015610885576020818601810151858301520161086b565b506000920191825250919050565b6000602082840312156108a557600080fd5b5051919050565b6000826108c957634e487b7160e01b600052601260045260246000fd5b500690565b808201808211156108ef57634e487b7160e01b600052601160045260246000fd5b9291505056fea26469706673582212203dc11bf4ccb1f31418f93e212f1a538823ebc50b2054532764b5ade758b87aaf64736f6c634300081c0033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100625760003560e01c806347b59153146100675780635f09d08c146100975780638e514b5b146100d5578063984f9c32146100e8578063c5242c0214610116578063fbbd18bc14610137575b600080fd5b61007a610075366004610755565b61014a565b6040516001600160401b0390911681526020015b60405180910390f35b6100c56100a5366004610755565b6001600160401b0390811660009081526001602052604090205416151590565b604051901515815260200161008e565b61007a6100e3366004610755565b6101b3565b6100c56100f6366004610755565b6001600160401b0390811660009081526020819052604090205416151590565b610129610124366004610755565b610213565b60405190815260200161008e565b6101296101453660046107bf565b61026e565b6001600160401b03808216600090815260016020526040812054909116908190036101ae5760405162461bcd60e51b815260206004820152600f60248201526e14db1bdd081b9bdd081b585c1c1959608a1b60448201526064015b60405180910390fd5b919050565b6001600160401b03808216600090815260208190526040812054909116908190036101ae5760405162461bcd60e51b815260206004820152601060248201526f109b1bd8dac81b9bdd081b585c1c195960821b60448201526064016101a5565b6001600160401b038116600090815260026020526040812054908190036101ae5760405162461bcd60e51b815260206004820152600f60248201526e14db1bdd081b9bdd081b585c1c1959608a1b60448201526064016101a5565b6001600160401b03808716600090815260208190526040812054909116156102cf5760405162461bcd60e51b8152602060048201526014602482015273109b1bd8dac8185b1c9958591e481b585c1c195960621b60448201526064016101a5565b6102d888610385565b90506102ef81876001600160401b0316878761048a565b61030481886001600160401b03168585610529565b6001600160401b038088166000818152602081815260408083208054958c1667ffffffffffffffff199687168117909155808452600183528184208054909616851790955560029091528082208590555184917f1269041f7d9261ffae4d51c46319f36ddae183581e04ce1532817b3e36ddef7f91a4979650505050505050565b604080516001600160401b038316602082015260009182918291720f3df6d732807ef1319fb7b8bb8522d0beac02910160408051601f19818403018152908290526103cf91610864565b600060405180830381855afa9150503d806000811461040a576040519150601f19603f3d011682016040523d82523d6000602084013e61040f565b606091505b5091509150818015610422575060008151115b61046e5760405162461bcd60e51b815260206004820152601860248201527f496e76616c696420626561636f6e2074696d657374616d70000000000000000060448201526064016101a5565b808060200190518101906104829190610893565b949350505050565b6104d782828080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508892506104d091508790506105cf565b6008610649565b6105235760405162461bcd60e51b815260206004820152601960248201527f496e76616c696420736c6f74206e756d6265722070726f6f660000000000000060448201526064016101a5565b50505050565b6000610534846105cf565b905061057c83838080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508992508591506119269050610649565b6105c85760405162461bcd60e51b815260206004820152601a60248201527f496e76616c696420626c6f636b206e756d6265722070726f6f6600000000000060448201526064016101a5565b5050505050565b603881811c60ff16602883811c61ff001691909117601884811c62ff00001691909117600885811c63ff000000169190911764ff000000009186901b919091161765ff00000000009185901b919091161766ff0000000000009184901b919091161767ff000000000000009290911b919091161760c01b90565b600083610657868585610661565b1495945050505050565b6000835160001415801561068057506020845161067e91906108ac565b155b61069d576040516313717da960e21b815260040160405180910390fd5b604080516020808201909252848152905b85518111610734576106c16002856108ac565b6000036106f7578151600052808601516020526020826040600060026107d05a03fa6106ec57600080fd5b600284049350610722565b8086015160005281516020526020826040600060026107d05a03fa61071b57600080fd5b6002840493505b61072d6020826108ce565b90506106ae565b5051949350505050565b80356001600160401b03811681146101ae57600080fd5b60006020828403121561076757600080fd5b6107708261073e565b9392505050565b60008083601f84011261078957600080fd5b5081356001600160401b038111156107a057600080fd5b6020830191508360208285010111156107b857600080fd5b9250929050565b600080600080600080600060a0888a0312156107da57600080fd5b6107e38861073e565b96506107f16020890161073e565b95506107ff6040890161073e565b945060608801356001600160401b0381111561081a57600080fd5b6108268a828b01610777565b90955093505060808801356001600160401b0381111561084557600080fd5b6108518a828b01610777565b989b979a50959850939692959293505050565b6000825160005b81811015610885576020818601810151858301520161086b565b506000920191825250919050565b6000602082840312156108a557600080fd5b5051919050565b6000826108c957634e487b7160e01b600052601260045260246000fd5b500690565b808201808211156108ef57634e487b7160e01b600052601160045260246000fd5b9291505056fea26469706673582212203dc11bf4ccb1f31418f93e212f1a538823ebc50b2054532764b5ade758b87aaf64736f6c634300081c0033", + "libraries": {}, + "devdoc": { + "author": "Origin Protocol Inc", + "kind": "dev", + "methods": { + "verifySlot(uint64,uint64,uint64,bytes,bytes)": { + "params": { + "blockNumber": "The execution layer block number.", + "blockProof": "The merkle proof witnesses for the block number against the beacon block root.", + "nextBlockTimestamp": "The timestamp of the block after the one being proven.", + "slot": "The beacon chain slot.", + "slotProof": "The merkle proof witnesses for the slot against the beacon block root." + } + } + }, + "title": "Beacon Chain Oracle", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "blockToSlot(uint64)": { + "notice": "Returns the beacon chain slot for a given execution layer block number." + }, + "isBlockMapped(uint64)": { + "notice": "Returns true if an execution layer block number has been mapped to a beacon chain slot." + }, + "isSlotMapped(uint64)": { + "notice": "Returns true if a beacon chain slot has been mapped to an execution layer block number." + }, + "slotToBlock(uint64)": { + "notice": "Returns the execution layer block number for a given beacon chain slot." + }, + "slotToRoot(uint64)": { + "notice": "Returns the beacon block root for a given beacon chain slot." + }, + "verifySlot(uint64,uint64,uint64,bytes,bytes)": { + "notice": "Uses merkle a proof against the beacon block root to link an execution layer block number to a beacon chain slot." + } + }, + "notice": "An Oracle for mapping execution layer block numbers to beacon chain slots.", + "version": 1 + }, + "storageLayout": { + "storage": [ + { + "astId": 12580, + "contract": "contracts/beacon/BeaconOracle.sol:BeaconOracle", + "label": "_blockToSlot", + "offset": 0, + "slot": "0", + "type": "t_mapping(t_uint64,t_uint64)" + }, + { + "astId": 12585, + "contract": "contracts/beacon/BeaconOracle.sol:BeaconOracle", + "label": "_slotToBlock", + "offset": 0, + "slot": "1", + "type": "t_mapping(t_uint64,t_uint64)" + }, + { + "astId": 12590, + "contract": "contracts/beacon/BeaconOracle.sol:BeaconOracle", + "label": "_slotToRoot", + "offset": 0, + "slot": "2", + "type": "t_mapping(t_uint64,t_bytes32)" + } + ], + "types": { + "t_bytes32": { + "encoding": "inplace", + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_mapping(t_uint64,t_bytes32)": { + "encoding": "mapping", + "key": "t_uint64", + "label": "mapping(uint64 => bytes32)", + "numberOfBytes": "32", + "value": "t_bytes32" + }, + "t_mapping(t_uint64,t_uint64)": { + "encoding": "mapping", + "key": "t_uint64", + "label": "mapping(uint64 => uint64)", + "numberOfBytes": "32", + "value": "t_uint64" + }, + "t_uint64": { + "encoding": "inplace", + "label": "uint64", + "numberOfBytes": "8" + } + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/BeaconProofs.json b/contracts/deployments/hoodi/BeaconProofs.json new file mode 100644 index 0000000000..8fcbdcd7c8 --- /dev/null +++ b/contracts/deployments/hoodi/BeaconProofs.json @@ -0,0 +1,403 @@ +{ + "address": "0x04b2D730106B9cA91aA011984eD2F7d62D00cF9f", + "abi": [ + { + "inputs": [], + "name": "InvalidProofLength", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "withdrawalCredentials", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "amountGwei", + "type": "uint64" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "slot", + "type": "uint64" + } + ], + "name": "merkleizePendingDeposit", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "merkleizeSignature", + "outputs": [ + { + "internalType": "bytes32", + "name": "root", + "type": "bytes32" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "beaconBlockRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "balancesContainerRoot", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "balancesContainerProof", + "type": "bytes" + } + ], + "name": "verifyBalancesContainer", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "beaconBlockRoot", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "slot", + "type": "uint64" + }, + { + "internalType": "bytes", + "name": "firstPendingDepositSlotProof", + "type": "bytes" + } + ], + "name": "verifyFirstPendingDeposit", + "outputs": [ + { + "internalType": "bool", + "name": "isEmptyDepositQueue", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "pendingDepositsContainerRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "pendingDepositRoot", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "proof", + "type": "bytes" + }, + { + "internalType": "uint32", + "name": "pendingDepositIndex", + "type": "uint32" + } + ], + "name": "verifyPendingDeposit", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "beaconBlockRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "pendingDepositsContainerRoot", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "proof", + "type": "bytes" + } + ], + "name": "verifyPendingDepositsContainer", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "beaconBlockRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "proof", + "type": "bytes" + }, + { + "internalType": "uint40", + "name": "validatorIndex", + "type": "uint40" + }, + { + "internalType": "bytes32", + "name": "withdrawalCredentials", + "type": "bytes32" + } + ], + "name": "verifyValidator", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "balancesContainerRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "validatorBalanceLeaf", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "balanceProof", + "type": "bytes" + }, + { + "internalType": "uint40", + "name": "validatorIndex", + "type": "uint40" + } + ], + "name": "verifyValidatorBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "validatorBalanceGwei", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "beaconBlockRoot", + "type": "bytes32" + }, + { + "internalType": "uint40", + "name": "validatorIndex", + "type": "uint40" + }, + { + "internalType": "uint64", + "name": "withdrawableEpoch", + "type": "uint64" + }, + { + "internalType": "bytes", + "name": "withdrawableEpochProof", + "type": "bytes" + } + ], + "name": "verifyValidatorWithdrawable", + "outputs": [], + "stateMutability": "view", + "type": "function" + } + ], + "transactionHash": "0xa14765ee4e19cc215fc0717a0541c928dbb25a6024a3a2bd91bd480bd0421c1b", + "receipt": { + "to": null, + "from": "0xf7749B41db006860cEc0650D18b8013d69C44Eeb", + "contractAddress": "0x04b2D730106B9cA91aA011984eD2F7d62D00cF9f", + "transactionIndex": 56, + "gasUsed": "1354130", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x94f6389e96b007953e5e1e44a9935ab6c0f8ff1160863689a84c356bed9251d3", + "transactionHash": "0xa14765ee4e19cc215fc0717a0541c928dbb25a6024a3a2bd91bd480bd0421c1b", + "logs": [], + "blockNumber": 1263388, + "cumulativeGasUsed": "43673019", + "status": 1, + "byzantium": true + }, + "args": [], + "numDeployments": 13, + "solcInputHash": "7de90067c6ddbeeea4788e7c02ed9ce8", + "metadata": "{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"InvalidProofLength\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"withdrawalCredentials\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"amountGwei\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"slot\",\"type\":\"uint64\"}],\"name\":\"merkleizePendingDeposit\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"merkleizeSignature\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"beaconBlockRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"balancesContainerRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"balancesContainerProof\",\"type\":\"bytes\"}],\"name\":\"verifyBalancesContainer\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"beaconBlockRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"slot\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"firstPendingDepositSlotProof\",\"type\":\"bytes\"}],\"name\":\"verifyFirstPendingDeposit\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"isEmptyDepositQueue\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"pendingDepositsContainerRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"pendingDepositRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"proof\",\"type\":\"bytes\"},{\"internalType\":\"uint32\",\"name\":\"pendingDepositIndex\",\"type\":\"uint32\"}],\"name\":\"verifyPendingDeposit\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"beaconBlockRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"pendingDepositsContainerRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"proof\",\"type\":\"bytes\"}],\"name\":\"verifyPendingDepositsContainer\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"beaconBlockRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"proof\",\"type\":\"bytes\"},{\"internalType\":\"uint40\",\"name\":\"validatorIndex\",\"type\":\"uint40\"},{\"internalType\":\"bytes32\",\"name\":\"withdrawalCredentials\",\"type\":\"bytes32\"}],\"name\":\"verifyValidator\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"balancesContainerRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"validatorBalanceLeaf\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"balanceProof\",\"type\":\"bytes\"},{\"internalType\":\"uint40\",\"name\":\"validatorIndex\",\"type\":\"uint40\"}],\"name\":\"verifyValidatorBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"validatorBalanceGwei\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"beaconBlockRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint40\",\"name\":\"validatorIndex\",\"type\":\"uint40\"},{\"internalType\":\"uint64\",\"name\":\"withdrawableEpoch\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"withdrawableEpochProof\",\"type\":\"bytes\"}],\"name\":\"verifyValidatorWithdrawable\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"author\":\"Origin Protocol Inc\",\"kind\":\"dev\",\"methods\":{\"merkleizePendingDeposit(bytes32,bytes,uint64,bytes,uint64)\":{\"params\":{\"amountGwei\":\"The amount of the deposit in Gwei.\",\"pubKeyHash\":\"Hash of validator's public key using the Beacon Chain's format\",\"signature\":\"The 96 byte BLS signature.\",\"slot\":\"The beacon chain slot the deposit was made in.\",\"withdrawalCredentials\":\"The 32 byte withdrawal credentials.\"},\"returns\":{\"_0\":\"root The merkle root of the pending deposit.\"}},\"merkleizeSignature(bytes)\":{\"params\":{\"signature\":\"The 96 byte BLS signature.\"},\"returns\":{\"root\":\"The merkle root of the signature.\"}},\"verifyBalancesContainer(bytes32,bytes32,bytes)\":{\"params\":{\"balancesContainerProof\":\"The merkle proof for the balances container to the beacon block root. This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\",\"balancesContainerRoot\":\"The merkle root of the the balances container\",\"beaconBlockRoot\":\"The root of the beacon block\"}},\"verifyFirstPendingDeposit(bytes32,uint64,bytes)\":{\"params\":{\"beaconBlockRoot\":\"The root of the beacon block.\",\"firstPendingDepositSlotProof\":\"The merkle proof to the beacon block root. Can be either: - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty. - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty. The 32 byte witness hashes are concatenated together starting from the leaf node.\",\"slot\":\"The beacon chain slot of the first deposit in the beacon chain's deposit queue. Can be anything if the deposit queue is empty.\"},\"returns\":{\"isEmptyDepositQueue\":\"True if the deposit queue is empty, false otherwise.\"}},\"verifyPendingDeposit(bytes32,bytes32,bytes,uint32)\":{\"params\":{\"pendingDepositIndex\":\"The pending deposit index in the Pending Deposits container\",\"pendingDepositRoot\":\"The leaf node containing the validator balance with three other balances.\",\"pendingDepositsContainerRoot\":\"The merkle root of the Pending Deposits container.\",\"proof\":\"The merkle proof for the pending deposit root to the Pending Deposits container root. This is 28 witness hashes of 32 bytes each concatenated together starting from the leaf node.\"}},\"verifyPendingDepositsContainer(bytes32,bytes32,bytes)\":{\"params\":{\"beaconBlockRoot\":\"The root of the beacon block.\",\"pendingDepositsContainerRoot\":\"The merkle root of the the pending deposits container.\",\"proof\":\"The merkle proof for the pending deposits container to the beacon block root. This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\"}},\"verifyValidator(bytes32,bytes32,bytes,uint40,bytes32)\":{\"params\":{\"beaconBlockRoot\":\"The root of the beacon block\",\"proof\":\"The merkle proof for the validator public key to the beacon block root. This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\",\"pubKeyHash\":\"Hash of validator's public key using the Beacon Chain's format\",\"validatorIndex\":\"The validator index\",\"withdrawalCredentials\":\"a value containing the validator type and withdrawal address.\"}},\"verifyValidatorBalance(bytes32,bytes32,bytes,uint40)\":{\"params\":{\"balanceProof\":\"The merkle proof for the validator balance to the Balances container root. This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\",\"balancesContainerRoot\":\"The merkle root of the Balances container.\",\"validatorBalanceLeaf\":\"The leaf node containing the validator balance with three other balances.\",\"validatorIndex\":\"The validator index to verify the balance for\"},\"returns\":{\"validatorBalanceGwei\":\"The balance in Gwei of the validator at the given index\"}}},\"title\":\"Verifies merkle proofs of beacon chain data.\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"merkleizePendingDeposit(bytes32,bytes,uint64,bytes,uint64)\":{\"notice\":\"Merkleizes a beacon chain pending deposit.\"},\"merkleizeSignature(bytes)\":{\"notice\":\"Merkleizes a BLS signature used for validator deposits.\"},\"verifyBalancesContainer(bytes32,bytes32,bytes)\":{\"notice\":\"Verifies the balances container to the beacon block root BeaconBlock.state.balances\"},\"verifyFirstPendingDeposit(bytes32,uint64,bytes)\":{\"notice\":\"If the deposit queue is not empty, verify the slot of the first pending deposit to the beacon block root. BeaconBlock.state.pendingDeposits[0].slot If the deposit queue is empty, verify the root of the first pending deposit is empty BeaconBlock.state.PendingDeposits[0]\"},\"verifyPendingDeposit(bytes32,bytes32,bytes,uint32)\":{\"notice\":\"Verified a pending deposit to the root of the Pending Deposits container.\"},\"verifyPendingDepositsContainer(bytes32,bytes32,bytes)\":{\"notice\":\"Verifies the pending deposits container to the beacon block root. BeaconBlock.state.pendingDeposits\"},\"verifyValidator(bytes32,bytes32,bytes,uint40,bytes32)\":{\"notice\":\"Verifies the validator index is for the given validator public key. Also verify the validator's withdrawal credential points to the withdrawal address. BeaconBlock.state.validators[validatorIndex].pubkey\"},\"verifyValidatorBalance(bytes32,bytes32,bytes,uint40)\":{\"notice\":\"Verifies the validator balance to the root of the Balances container.\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/beacon/BeaconProofs.sol\":\"BeaconProofs\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"contracts/beacon/BeaconProofs.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { BeaconProofsLib } from \\\"./BeaconProofsLib.sol\\\";\\nimport { IBeaconProofs } from \\\"../interfaces/IBeaconProofs.sol\\\";\\n\\n/**\\n * @title Verifies merkle proofs of beacon chain data.\\n * @author Origin Protocol Inc\\n */\\ncontract BeaconProofs is IBeaconProofs {\\n /// @notice Verifies the validator index is for the given validator public key.\\n /// Also verify the validator's withdrawal credential points to the withdrawal address.\\n /// BeaconBlock.state.validators[validatorIndex].pubkey\\n /// @param beaconBlockRoot The root of the beacon block\\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\\n /// @param proof The merkle proof for the validator public key to the beacon block root.\\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n /// @param validatorIndex The validator index\\n /// @param withdrawalCredentials a value containing the validator type and withdrawal address.\\n function verifyValidator(\\n bytes32 beaconBlockRoot,\\n bytes32 pubKeyHash,\\n bytes calldata proof,\\n uint40 validatorIndex,\\n bytes32 withdrawalCredentials\\n ) external view {\\n BeaconProofsLib.verifyValidator(\\n beaconBlockRoot,\\n pubKeyHash,\\n proof,\\n validatorIndex,\\n withdrawalCredentials\\n );\\n }\\n\\n function verifyValidatorWithdrawable(\\n bytes32 beaconBlockRoot,\\n uint40 validatorIndex,\\n uint64 withdrawableEpoch,\\n bytes calldata withdrawableEpochProof\\n ) external view {\\n BeaconProofsLib.verifyValidatorWithdrawableEpoch(\\n beaconBlockRoot,\\n validatorIndex,\\n withdrawableEpoch,\\n withdrawableEpochProof\\n );\\n }\\n\\n /// @notice Verifies the balances container to the beacon block root\\n /// BeaconBlock.state.balances\\n /// @param beaconBlockRoot The root of the beacon block\\n /// @param balancesContainerRoot The merkle root of the the balances container\\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n function verifyBalancesContainer(\\n bytes32 beaconBlockRoot,\\n bytes32 balancesContainerRoot,\\n bytes calldata balancesContainerProof\\n ) external view {\\n BeaconProofsLib.verifyBalancesContainer(\\n beaconBlockRoot,\\n balancesContainerRoot,\\n balancesContainerProof\\n );\\n }\\n\\n /// @notice Verifies the validator balance to the root of the Balances container.\\n /// @param balancesContainerRoot The merkle root of the Balances container.\\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\\n /// @param balanceProof The merkle proof for the validator balance to the Balances container root.\\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n /// @param validatorIndex The validator index to verify the balance for\\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\\n function verifyValidatorBalance(\\n bytes32 balancesContainerRoot,\\n bytes32 validatorBalanceLeaf,\\n bytes calldata balanceProof,\\n uint40 validatorIndex\\n ) external view returns (uint256 validatorBalanceGwei) {\\n validatorBalanceGwei = BeaconProofsLib.verifyValidatorBalance(\\n balancesContainerRoot,\\n validatorBalanceLeaf,\\n balanceProof,\\n validatorIndex\\n );\\n }\\n\\n /// @notice Verifies the pending deposits container to the beacon block root.\\n /// BeaconBlock.state.pendingDeposits\\n /// @param beaconBlockRoot The root of the beacon block.\\n /// @param pendingDepositsContainerRoot The merkle root of the the pending deposits container.\\n /// @param proof The merkle proof for the pending deposits container to the beacon block root.\\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n function verifyPendingDepositsContainer(\\n bytes32 beaconBlockRoot,\\n bytes32 pendingDepositsContainerRoot,\\n bytes calldata proof\\n ) external view {\\n BeaconProofsLib.verifyPendingDepositsContainer(\\n beaconBlockRoot,\\n pendingDepositsContainerRoot,\\n proof\\n );\\n }\\n\\n /// @notice Verified a pending deposit to the root of the Pending Deposits container.\\n /// @param pendingDepositsContainerRoot The merkle root of the Pending Deposits container.\\n /// @param pendingDepositRoot The leaf node containing the validator balance with three other balances.\\n /// @param proof The merkle proof for the pending deposit root to the Pending Deposits container root.\\n /// This is 28 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n /// @param pendingDepositIndex The pending deposit index in the Pending Deposits container\\n function verifyPendingDeposit(\\n bytes32 pendingDepositsContainerRoot,\\n bytes32 pendingDepositRoot,\\n bytes calldata proof,\\n uint32 pendingDepositIndex\\n ) external view {\\n BeaconProofsLib.verifyPendingDeposit(\\n pendingDepositsContainerRoot,\\n pendingDepositRoot,\\n proof,\\n pendingDepositIndex\\n );\\n }\\n\\n /// @notice If the deposit queue is not empty,\\n /// verify the slot of the first pending deposit to the beacon block root.\\n /// BeaconBlock.state.pendingDeposits[0].slot\\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\\n /// BeaconBlock.state.PendingDeposits[0]\\n /// @param beaconBlockRoot The root of the beacon block.\\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\\n /// Can be anything if the deposit queue is empty.\\n /// @param firstPendingDepositSlotProof The merkle proof to the beacon block root. Can be either:\\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\\n function verifyFirstPendingDeposit(\\n bytes32 beaconBlockRoot,\\n uint64 slot,\\n bytes calldata firstPendingDepositSlotProof\\n ) external view returns (bool isEmptyDepositQueue) {\\n isEmptyDepositQueue = BeaconProofsLib.verifyFirstPendingDeposit(\\n beaconBlockRoot,\\n slot,\\n firstPendingDepositSlotProof\\n );\\n }\\n\\n /// @notice Merkleizes a beacon chain pending deposit.\\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\\n /// @param withdrawalCredentials The 32 byte withdrawal credentials.\\n /// @param amountGwei The amount of the deposit in Gwei.\\n /// @param signature The 96 byte BLS signature.\\n /// @param slot The beacon chain slot the deposit was made in.\\n /// @return root The merkle root of the pending deposit.\\n function merkleizePendingDeposit(\\n bytes32 pubKeyHash,\\n bytes calldata withdrawalCredentials,\\n uint64 amountGwei,\\n bytes calldata signature,\\n uint64 slot\\n ) external pure returns (bytes32) {\\n return\\n BeaconProofsLib.merkleizePendingDeposit(\\n pubKeyHash,\\n withdrawalCredentials,\\n amountGwei,\\n signature,\\n slot\\n );\\n }\\n\\n /// @notice Merkleizes a BLS signature used for validator deposits.\\n /// @param signature The 96 byte BLS signature.\\n /// @return root The merkle root of the signature.\\n function merkleizeSignature(bytes calldata signature)\\n external\\n pure\\n returns (bytes32 root)\\n {\\n return BeaconProofsLib.merkleizeSignature(signature);\\n }\\n}\\n\",\"keccak256\":\"0xe535fd71eb933e1f12e8951d16810865c7d48878f6053bd322d128aaed44f276\",\"license\":\"BUSL-1.1\"},\"contracts/beacon/BeaconProofsLib.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { Merkle } from \\\"./Merkle.sol\\\";\\nimport { Endian } from \\\"./Endian.sol\\\";\\n\\n/**\\n * @title Library to verify merkle proofs of beacon chain data.\\n * @author Origin Protocol Inc\\n */\\nlibrary BeaconProofsLib {\\n // Known generalized indices in the beacon block\\n /// @dev BeaconBlock.state.PendingDeposits[0]\\n /// Beacon block container: height 3, state at at index 3\\n /// Beacon state container: height 6, pending deposits at index 34\\n /// Pending deposits container: height 28, first deposit at index 0\\n /// ((2 ^ 3 + 3) * 2 ^ 6 + 34) * 2 ^ 28 + 0 = 198105366528\\n uint256 internal constant FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX =\\n 198105366528;\\n /// @dev BeaconBlock.state.PendingDeposits[0].pubkey\\n /// Pending Deposit container: height 3, pubkey at index 4\\n /// (((2 ^ 3 + 3) * 2 ^ 6 + 34) * 2 ^ 28 + 0) * 2 ^ 3 + 4 = 1584842932228\\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX =\\n 1584842932228;\\n /// @dev BeaconBlock.state.validators\\n /// Beacon block container: height 3, state at at index 3\\n /// Beacon state container: height 6, validators at index 11\\n /// (2 ^ 3 + 3) * 2 ^ 6 + 11 = 715\\n uint256 internal constant VALIDATORS_CONTAINER_GENERALIZED_INDEX = 715;\\n /// @dev BeaconBlock.state.balances\\n /// Beacon block container: height 3, state at at index 3\\n /// Beacon state container: height 6, balances at index 12\\n /// (2 ^ 3 + 3) * 2 ^ 6 + 12 = 716\\n uint256 internal constant BALANCES_CONTAINER_GENERALIZED_INDEX = 716;\\n\\n /// @dev BeaconBlock.state.pendingDeposits\\n /// Beacon block container: height 3, state at at index 3\\n /// Beacon state container: height 6, balances at index 34\\n /// (2 ^ 3 + 3) * 2 ^ 6 + 34 = 738\\n uint256 internal constant PENDING_DEPOSITS_CONTAINER_GENERALIZED_INDEX =\\n 738;\\n\\n /// @dev Number of bytes in the proof to the first pending deposit.\\n /// 37 witness hashes of 32 bytes each concatenated together.\\n /// BeaconBlock.state.PendingDeposits[0]\\n /// 37 * 32 bytes = 1184 bytes\\n uint256 internal constant FIRST_PENDING_DEPOSIT_PROOF_LENGTH = 1184;\\n /// @dev Number of bytes in the proof from the slot of the first pending deposit to the beacon block root.\\n /// 40 witness hashes of 32 bytes each concatenated together.\\n /// BeaconBlock.state.PendingDeposits[0].slot\\n /// 40 * 32 bytes = 1280 bytes\\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_PROOF_LENGTH = 1280;\\n\\n /// @dev Merkle height of the Balances container\\n /// BeaconBlock.state.balances\\n uint256 internal constant BALANCES_HEIGHT = 39;\\n /// @dev Merkle height of the Validators container list\\n /// BeaconBlock.state.validators\\n uint256 internal constant VALIDATORS_LIST_HEIGHT = 41;\\n /// @dev Merkle height of the Pending Deposits container list\\n /// BeaconBlock.state.pendingDeposits\\n uint256 internal constant PENDING_DEPOSITS_LIST_HEIGHT = 28;\\n /// @dev Merkle height of the Validator container\\n /// BeaconBlock.state.validators[validatorIndex]\\n uint256 internal constant VALIDATOR_CONTAINER_HEIGHT = 3;\\n\\n /// @dev Position of the pubkey field in the Validator container.\\n /// BeaconBlock.state.validators[validatorIndex].pubkey\\n uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;\\n /// @dev Position of the withdrawable epoch field in the Validator container.\\n /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\\n uint256 internal constant VALIDATOR_WITHDRAWABLE_EPOCH_INDEX = 7;\\n\\n /// @notice Verifies the validator index is for the given validator public key.\\n /// Also verify the validator's withdrawal credential points to the withdrawal address.\\n /// BeaconBlock.state.validators[validatorIndex].pubkey\\n /// @param beaconBlockRoot The root of the beacon block\\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\\n /// @param proof The merkle proof for the validator public key to the beacon block root.\\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n /// @param validatorIndex The validator index\\n /// @param withdrawalCredentials a value containing the validator type and withdrawal address.\\n function verifyValidator(\\n bytes32 beaconBlockRoot,\\n bytes32 pubKeyHash,\\n bytes calldata proof,\\n uint40 validatorIndex,\\n bytes32 withdrawalCredentials\\n ) internal view {\\n require(beaconBlockRoot != bytes32(0), \\\"Invalid block root\\\");\\n\\n // BeaconBlock.state.validators[validatorIndex]\\n uint256 generalizedIndex = concatGenIndices(\\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\\n VALIDATORS_LIST_HEIGHT,\\n validatorIndex\\n );\\n // BeaconBlock.state.validators[validatorIndex].pubkey\\n generalizedIndex = concatGenIndices(\\n generalizedIndex,\\n VALIDATOR_CONTAINER_HEIGHT,\\n VALIDATOR_PUBKEY_INDEX\\n );\\n\\n // Get the withdrawal credentials from the first witness in the pubkey merkle proof.\\n bytes32 withdrawalCredentialsFromProof = bytes32(proof[:32]);\\n\\n require(\\n withdrawalCredentialsFromProof == withdrawalCredentials,\\n \\\"Invalid withdrawal cred\\\"\\n );\\n\\n require(\\n // 53 * 32 bytes = 1696 bytes\\n proof.length == 1696 &&\\n Merkle.verifyInclusionSha256({\\n proof: proof,\\n root: beaconBlockRoot,\\n leaf: pubKeyHash,\\n index: generalizedIndex\\n }),\\n \\\"Invalid validator proof\\\"\\n );\\n }\\n\\n /// @notice Verifies a validator's withdrawable epoch to the beacon block root\\n /// for a given validator index.\\n /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\\n /// @param beaconBlockRoot The root of the beacon block\\n /// @param validatorIndex The validator index to verify the withdrawable epoch for.\\n /// @param withdrawableEpoch The withdrawable epoch to verify in big endian uint64 format\\n /// @param proof The merkle proof for the validator's withdrawable epoch to the beacon block root.\\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n function verifyValidatorWithdrawableEpoch(\\n bytes32 beaconBlockRoot,\\n uint40 validatorIndex,\\n uint64 withdrawableEpoch,\\n bytes calldata proof\\n ) internal view {\\n require(beaconBlockRoot != bytes32(0), \\\"Invalid block root\\\");\\n\\n // BeaconBlock.state.validators[validatorIndex]\\n uint256 exitEpochGenIndex = concatGenIndices(\\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\\n VALIDATORS_LIST_HEIGHT,\\n validatorIndex\\n );\\n // BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\\n exitEpochGenIndex = concatGenIndices(\\n exitEpochGenIndex,\\n VALIDATOR_CONTAINER_HEIGHT,\\n VALIDATOR_WITHDRAWABLE_EPOCH_INDEX\\n );\\n\\n require(\\n // 53 * 32 bytes = 1696 bytes\\n proof.length == 1696 &&\\n Merkle.verifyInclusionSha256({\\n proof: proof,\\n root: beaconBlockRoot,\\n leaf: Endian.toLittleEndianUint64(withdrawableEpoch),\\n index: exitEpochGenIndex\\n }),\\n \\\"Invalid withdrawable proof\\\"\\n );\\n }\\n\\n /// @notice Verifies the balances container to the beacon block root.\\n /// BeaconBlock.state.balances\\n /// @param beaconBlockRoot The root of the beacon block.\\n /// @param balancesContainerRoot The merkle root of the the balances container.\\n /// @param proof The merkle proof for the balances container to the beacon block root.\\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n function verifyBalancesContainer(\\n bytes32 beaconBlockRoot,\\n bytes32 balancesContainerRoot,\\n bytes calldata proof\\n ) internal view {\\n require(beaconBlockRoot != bytes32(0), \\\"Invalid block root\\\");\\n\\n // BeaconBlock.state.balances\\n require(\\n // 9 * 32 bytes = 288 bytes\\n proof.length == 288 &&\\n Merkle.verifyInclusionSha256({\\n proof: proof,\\n root: beaconBlockRoot,\\n leaf: balancesContainerRoot,\\n index: BALANCES_CONTAINER_GENERALIZED_INDEX\\n }),\\n \\\"Invalid balance container proof\\\"\\n );\\n }\\n\\n /// @notice Verifies the validator balance to the root of the Balances container.\\n /// @param balancesContainerRoot The merkle root of the Balances container.\\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\\n /// @param proof The merkle proof for the validator balance to the Balances container root.\\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n /// @param validatorIndex The validator index to verify the balance for.\\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index.\\n function verifyValidatorBalance(\\n bytes32 balancesContainerRoot,\\n bytes32 validatorBalanceLeaf,\\n bytes calldata proof,\\n uint40 validatorIndex\\n ) internal view returns (uint256 validatorBalanceGwei) {\\n require(balancesContainerRoot != bytes32(0), \\\"Invalid container root\\\");\\n\\n // Four balances are stored in each leaf so the validator index is divided by 4\\n uint64 balanceIndex = validatorIndex / 4;\\n\\n // Get the index within the balances container, not the Beacon Block\\n // BeaconBlock.state.balances[balanceIndex]\\n uint256 generalizedIndex = concatGenIndices(\\n 1,\\n BALANCES_HEIGHT,\\n balanceIndex\\n );\\n\\n validatorBalanceGwei = balanceAtIndex(\\n validatorBalanceLeaf,\\n validatorIndex\\n );\\n\\n require(\\n // 39 * 32 bytes = 1248 bytes\\n proof.length == 1248 &&\\n Merkle.verifyInclusionSha256({\\n proof: proof,\\n root: balancesContainerRoot,\\n leaf: validatorBalanceLeaf,\\n index: generalizedIndex\\n }),\\n \\\"Invalid balance proof\\\"\\n );\\n }\\n\\n /// @notice Verifies the pending deposits container to the beacon block root.\\n /// BeaconBlock.state.pendingDeposits\\n /// @param beaconBlockRoot The root of the beacon block.\\n /// @param pendingDepositsContainerRoot The merkle root of the the pending deposits container.\\n /// @param proof The merkle proof for the pending deposits container to the beacon block root.\\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n function verifyPendingDepositsContainer(\\n bytes32 beaconBlockRoot,\\n bytes32 pendingDepositsContainerRoot,\\n bytes calldata proof\\n ) internal view {\\n require(beaconBlockRoot != bytes32(0), \\\"Invalid block root\\\");\\n\\n // BeaconBlock.state.pendingDeposits\\n require(\\n // 9 * 32 bytes = 288 bytes\\n proof.length == 288 &&\\n Merkle.verifyInclusionSha256({\\n proof: proof,\\n root: beaconBlockRoot,\\n leaf: pendingDepositsContainerRoot,\\n index: PENDING_DEPOSITS_CONTAINER_GENERALIZED_INDEX\\n }),\\n \\\"Invalid deposit container proof\\\"\\n );\\n }\\n\\n /// @notice Verifies a pending deposit in the pending deposits container.\\n /// BeaconBlock.state.pendingDeposits[depositIndex]\\n /// @param pendingDepositsContainerRoot The merkle root of the pending deposits list container\\n /// @param pendingDepositRoot The merkle root of the pending deposit to verify\\n /// @param proof The merkle proof for the pending deposit root to the pending deposits list container root.\\n /// This is 28 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n /// @param pendingDepositIndex The index in the pending deposits list container for the deposit to verify.\\n function verifyPendingDeposit(\\n bytes32 pendingDepositsContainerRoot,\\n bytes32 pendingDepositRoot,\\n bytes calldata proof,\\n uint32 pendingDepositIndex\\n ) internal view {\\n require(pendingDepositsContainerRoot != bytes32(0), \\\"Invalid root\\\");\\n // ssz-merkleizing a list which has a variable length, an additional\\n // sha256(pending_deposits_root, pending_deposits_length) operation is done to get the\\n // actual pending deposits root so the max pending deposit index is 2^(28 - 1)\\n require(\\n pendingDepositIndex < 2**(PENDING_DEPOSITS_LIST_HEIGHT - 1),\\n \\\"Invalid deposit index\\\"\\n );\\n\\n // BeaconBlock.state.pendingDeposits[depositIndex]\\n uint256 generalizedIndex = concatGenIndices(\\n 1,\\n PENDING_DEPOSITS_LIST_HEIGHT,\\n pendingDepositIndex\\n );\\n\\n require(\\n // 28 * 32 bytes = 896 bytes\\n proof.length == 896 &&\\n Merkle.verifyInclusionSha256({\\n proof: proof,\\n root: pendingDepositsContainerRoot,\\n leaf: pendingDepositRoot,\\n index: generalizedIndex\\n }),\\n \\\"Invalid deposit proof\\\"\\n );\\n }\\n\\n /// @notice If the deposit queue is not empty,\\n /// verify the slot of the first pending deposit to the beacon block root.\\n /// BeaconBlock.state.pendingDeposits[0].slot\\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\\n /// BeaconBlock.state.PendingDeposits[0]\\n /// @param beaconBlockRoot The root of the beacon block.\\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\\n /// Can be anything if the deposit queue is empty.\\n /// @param proof The merkle proof to the beacon block root. Can be either:\\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\\n function verifyFirstPendingDeposit(\\n bytes32 beaconBlockRoot,\\n uint64 slot,\\n bytes calldata proof\\n ) internal view returns (bool isEmptyDepositQueue) {\\n require(beaconBlockRoot != bytes32(0), \\\"Invalid block root\\\");\\n\\n // If the deposit queue is empty\\n if (proof.length == FIRST_PENDING_DEPOSIT_PROOF_LENGTH) {\\n require(\\n Merkle.verifyInclusionSha256({\\n proof: proof,\\n root: beaconBlockRoot,\\n leaf: bytes32(0),\\n index: FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX\\n }),\\n \\\"Invalid empty deposits proof\\\"\\n );\\n return true;\\n }\\n\\n // Verify the public key of the first pending deposit\\n // BeaconBlock.state.PendingDeposits[0].slot\\n require(\\n proof.length == FIRST_PENDING_DEPOSIT_SLOT_PROOF_LENGTH &&\\n Merkle.verifyInclusionSha256({\\n proof: proof,\\n root: beaconBlockRoot,\\n leaf: Endian.toLittleEndianUint64(slot),\\n index: FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX\\n }),\\n \\\"Invalid deposit slot proof\\\"\\n );\\n }\\n\\n /// @notice Merkleizes a beacon chain pending deposit.\\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\\n /// @param withdrawalCredentials The 32 byte withdrawal credentials.\\n /// @param amountGwei The amount of the deposit in Gwei.\\n /// @param signature The 96 byte BLS signature.\\n /// @param slot The beacon chain slot the deposit was made in.\\n /// @return root The merkle root of the pending deposit.\\n function merkleizePendingDeposit(\\n bytes32 pubKeyHash,\\n bytes calldata withdrawalCredentials,\\n uint64 amountGwei,\\n bytes calldata signature,\\n uint64 slot\\n ) internal pure returns (bytes32 root) {\\n bytes32[] memory leaves = new bytes32[](8);\\n leaves[0] = pubKeyHash;\\n leaves[1] = bytes32(withdrawalCredentials[:32]);\\n leaves[2] = Endian.toLittleEndianUint64(amountGwei);\\n leaves[3] = merkleizeSignature(signature);\\n leaves[4] = Endian.toLittleEndianUint64(slot);\\n leaves[5] = bytes32(0);\\n leaves[6] = bytes32(0);\\n leaves[7] = bytes32(0);\\n\\n root = Merkle.merkleizeSha256(leaves);\\n }\\n\\n /// @notice Merkleizes a BLS signature used for validator deposits.\\n /// @param signature The 96 byte BLS signature.\\n /// @return root The merkle root of the signature.\\n function merkleizeSignature(bytes calldata signature)\\n internal\\n pure\\n returns (bytes32)\\n {\\n require(signature.length == 96, \\\"Invalid signature\\\");\\n\\n bytes32[] memory leaves = new bytes32[](4);\\n leaves[0] = bytes32(signature[:32]);\\n leaves[1] = bytes32(signature[32:64]);\\n leaves[2] = bytes32(signature[64:96]);\\n leaves[3] = bytes32(0);\\n\\n return Merkle.merkleizeSha256(leaves);\\n }\\n\\n ////////////////////////////////////////////////////\\n /// Internal Helper Functions\\n ////////////////////////////////////////////////////\\n\\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint40 validatorIndex)\\n internal\\n pure\\n returns (uint256)\\n {\\n uint256 bitShiftAmount = (validatorIndex % 4) * 64;\\n return\\n Endian.fromLittleEndianUint64(\\n bytes32((uint256(validatorBalanceLeaf) << bitShiftAmount))\\n );\\n }\\n\\n /// @notice Concatenates two beacon chain generalized indices into one.\\n /// @param genIndex The first generalized index or 1 if calculating for a single container.\\n /// @param height The merkle tree height of the second container. eg 39 for balances, 41 for validators.\\n /// @param index The index within the second container. eg the validator index.\\n /// @return genIndex The concatenated generalized index.\\n function concatGenIndices(\\n uint256 genIndex,\\n uint256 height,\\n uint256 index\\n ) internal pure returns (uint256) {\\n return (genIndex << height) | index;\\n }\\n}\\n\",\"keccak256\":\"0xd7e569caa928bcbb4553d3895f30f9902502b4622bcab7d04c17d120ef349448\",\"license\":\"BUSL-1.1\"},\"contracts/beacon/Endian.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Library to handle conversion between little-endian and big-endian formats.\\n * @author Origin Protocol Inc\\n */\\nlibrary Endian {\\n /**\\n * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64\\n * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type\\n * @return n The big endian-formatted uint64\\n * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits),\\n * but it is immediately truncated to a uint64 (i.e. 64 bits)\\n * through a right-shift/shr operation.\\n */\\n function fromLittleEndianUint64(bytes32 lenum)\\n internal\\n pure\\n returns (uint64 n)\\n {\\n // the number needs to be stored in little-endian encoding (ie in bytes 0-8)\\n n = uint64(uint256(lenum >> 192));\\n // forgefmt: disable-next-item\\n return\\n (n >> 56) |\\n ((0x00FF000000000000 & n) >> 40) |\\n ((0x0000FF0000000000 & n) >> 24) |\\n ((0x000000FF00000000 & n) >> 8) |\\n ((0x00000000FF000000 & n) << 8) |\\n ((0x0000000000FF0000 & n) << 24) |\\n ((0x000000000000FF00 & n) << 40) |\\n ((0x00000000000000FF & n) << 56);\\n }\\n\\n function toLittleEndianUint64(uint64 benum)\\n internal\\n pure\\n returns (bytes32 n)\\n {\\n // Convert to little-endian by reversing byte order\\n uint64 reversed = (benum >> 56) |\\n ((0x00FF000000000000 & benum) >> 40) |\\n ((0x0000FF0000000000 & benum) >> 24) |\\n ((0x000000FF00000000 & benum) >> 8) |\\n ((0x00000000FF000000 & benum) << 8) |\\n ((0x0000000000FF0000 & benum) << 24) |\\n ((0x000000000000FF00 & benum) << 40) |\\n ((0x00000000000000FF & benum) << 56);\\n\\n // Store the little-endian uint64 in the least significant 64 bits of bytes32\\n n = bytes32(uint256(reversed));\\n // Shift to most significant bits\\n n = n << 192;\\n }\\n}\\n\",\"keccak256\":\"0x823cc85083bc08271f32602afc45eb62548c51b334da0e19080764e5aa503506\",\"license\":\"MIT\"},\"contracts/beacon/Merkle.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev These functions deal with verification of Merkle Tree proofs.\\n *\\n * The tree and the proofs can be generated using our\\n * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].\\n * You will find a quickstart guide in the readme.\\n *\\n * WARNING: You should avoid using leaf values that are 64 bytes long prior to\\n * hashing, or use a hash function other than keccak256 for hashing leaves.\\n * This is because the concatenation of a sorted pair of internal nodes in\\n * the merkle tree could be reinterpreted as a leaf value.\\n * OpenZeppelin's JavaScript library generates merkle trees that are safe\\n * against this attack out of the box.\\n */\\nlibrary Merkle {\\n error InvalidProofLength();\\n\\n /**\\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\\n * hash matches the root of the tree. The tree is built assuming `leaf` is\\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\\n *\\n * Note this is for a Merkle tree using the sha256 hash function\\n */\\n function verifyInclusionSha256(\\n bytes memory proof,\\n bytes32 root,\\n bytes32 leaf,\\n uint256 index\\n ) internal view returns (bool) {\\n return processInclusionProofSha256(proof, leaf, index) == root;\\n }\\n\\n /**\\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\\n * hash matches the root of the tree. The tree is built assuming `leaf` is\\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\\n *\\n * _Available since v4.4._\\n *\\n * Note this is for a Merkle tree using the sha256 hash function\\n */\\n function processInclusionProofSha256(\\n bytes memory proof,\\n bytes32 leaf,\\n uint256 index\\n ) internal view returns (bytes32) {\\n require(\\n proof.length != 0 && proof.length % 32 == 0,\\n InvalidProofLength()\\n );\\n bytes32[1] memory computedHash = [leaf];\\n for (uint256 i = 32; i <= proof.length; i += 32) {\\n if (index % 2 == 0) {\\n // if ith bit of index is 0, then computedHash is a left sibling\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n mstore(0x00, mload(computedHash))\\n mstore(0x20, mload(add(proof, i)))\\n if iszero(\\n staticcall(\\n sub(gas(), 2000),\\n 2,\\n 0x00,\\n 0x40,\\n computedHash,\\n 0x20\\n )\\n ) {\\n revert(0, 0)\\n }\\n }\\n } else {\\n // if ith bit of index is 1, then computedHash is a right sibling\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n mstore(0x00, mload(add(proof, i)))\\n mstore(0x20, mload(computedHash))\\n if iszero(\\n staticcall(\\n sub(gas(), 2000),\\n 2,\\n 0x00,\\n 0x40,\\n computedHash,\\n 0x20\\n )\\n ) {\\n revert(0, 0)\\n }\\n }\\n }\\n index = index / 2;\\n }\\n return computedHash[0];\\n }\\n\\n /**\\n * @notice Returns the merkle root of a tree created from a set of leaves using sha256 as its hash function.\\n * @param leaves the leaves of the merkle tree\\n * @return The computed Merkle root of the tree.\\n * @dev A pre-condition to this function is that leaves.length is a power of two.\\n * If not, the function will merkleize the inputs incorrectly.\\n */\\n function merkleizeSha256(bytes32[] memory leaves)\\n internal\\n pure\\n returns (bytes32)\\n {\\n //there are half as many nodes in the layer above the leaves\\n uint256 numNodesInLayer = leaves.length / 2;\\n //create a layer to store the internal nodes\\n bytes32[] memory layer = new bytes32[](numNodesInLayer);\\n //fill the layer with the pairwise hashes of the leaves\\n for (uint256 i = 0; i < numNodesInLayer; i++) {\\n layer[i] = sha256(\\n abi.encodePacked(leaves[2 * i], leaves[2 * i + 1])\\n );\\n }\\n //the next layer above has half as many nodes\\n numNodesInLayer /= 2;\\n //while we haven't computed the root\\n while (numNodesInLayer != 0) {\\n //overwrite the first numNodesInLayer nodes in layer with the pairwise hashes of their children\\n for (uint256 i = 0; i < numNodesInLayer; i++) {\\n layer[i] = sha256(\\n abi.encodePacked(layer[2 * i], layer[2 * i + 1])\\n );\\n }\\n //the next layer above has half as many nodes\\n numNodesInLayer /= 2;\\n }\\n //the first node in the layer is the root\\n return layer[0];\\n }\\n}\\n\",\"keccak256\":\"0xbc4f4d5e06379b03ee535140a6f06addba42fef3578d234c8940a72889c2d898\",\"license\":\"MIT\"},\"contracts/interfaces/IBeaconProofs.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\ninterface IBeaconProofs {\\n function verifyValidator(\\n bytes32 beaconBlockRoot,\\n bytes32 pubKeyHash,\\n bytes calldata validatorPubKeyProof,\\n uint40 validatorIndex,\\n bytes32 withdrawalCredentials\\n ) external view;\\n\\n function verifyValidatorWithdrawable(\\n bytes32 beaconBlockRoot,\\n uint40 validatorIndex,\\n uint64 withdrawableEpoch,\\n bytes calldata withdrawableEpochProof\\n ) external view;\\n\\n function verifyBalancesContainer(\\n bytes32 beaconBlockRoot,\\n bytes32 balancesContainerLeaf,\\n bytes calldata balancesContainerProof\\n ) external view;\\n\\n function verifyValidatorBalance(\\n bytes32 balancesContainerRoot,\\n bytes32 validatorBalanceLeaf,\\n bytes calldata balanceProof,\\n uint40 validatorIndex\\n ) external view returns (uint256 validatorBalance);\\n\\n function verifyPendingDepositsContainer(\\n bytes32 beaconBlockRoot,\\n bytes32 pendingDepositsContainerRoot,\\n bytes calldata proof\\n ) external view;\\n\\n function verifyPendingDeposit(\\n bytes32 pendingDepositsContainerRoot,\\n bytes32 pendingDepositRoot,\\n bytes calldata proof,\\n uint32 pendingDepositIndex\\n ) external view;\\n\\n function verifyFirstPendingDeposit(\\n bytes32 beaconBlockRoot,\\n uint64 slot,\\n bytes calldata firstPendingDepositSlotProof\\n ) external view returns (bool isEmptyDepositQueue);\\n\\n function merkleizePendingDeposit(\\n bytes32 pubKeyHash,\\n bytes calldata withdrawalCredentials,\\n uint64 amountGwei,\\n bytes calldata signature,\\n uint64 slot\\n ) external pure returns (bytes32 root);\\n\\n function merkleizeSignature(bytes calldata signature)\\n external\\n pure\\n returns (bytes32 root);\\n}\\n\",\"keccak256\":\"0xbc611f259b296451c358638a6e164227179bcee079656f5078800cc2f06c22fd\",\"license\":\"BUSL-1.1\"}},\"version\":1}", + "bytecode": "0x6080604052348015600f57600080fd5b5061178c8061001f6000396000f3fe608060405234801561001057600080fd5b50600436106100935760003560e01c80638a050dd4116100665780638a050dd4146100f957806391ad640d1461010c578063afe768871461011f578063d98a556414610132578063f34ad34c1461015557600080fd5b8063258b83d8146100985780632b00e796146100be578063334a88fb146100d357806354ba95b2146100e6575b600080fd5b6100ab6100a6366004611112565b610168565b6040519081526020015b60405180910390f35b6100d16100cc366004611153565b61017d565b005b6100d16100e13660046111d6565b61018f565b6100d16100f4366004611244565b6101a3565b6100d16101073660046112b5565b6101b9565b6100d161011a366004611153565b6101c6565b6100ab61012d366004611328565b6101d2565b6101456101403660046113c7565b6101ef565b60405190151581526020016100b5565b6100ab610163366004611408565b610206565b6000610174838361021f565b90505b92915050565b61018984848484610365565b50505050565b61019c8585858585610420565b5050505050565b6101b18686868686866104f8565b505050505050565b61019c8585858585610638565b61018984848484610778565b60006101e388888888888888610833565b98975050505050505050565b60006101fd858585856109ab565b95945050505050565b60006102158686868686610b19565b9695505050505050565b60006060821461026a5760405162461bcd60e51b8152602060048201526011602482015270496e76616c6964207369676e617475726560781b60448201526064015b60405180910390fd5b60408051600480825260a082019092526000916020820160808036833701905050905061029b602060008587611485565b6102a4916114af565b816000815181106102b7576102b76114cd565b6020908102919091018101919091526102d4906040908587611485565b6102dd916114af565b816001815181106102f0576102f06114cd565b6020908102919091010152610309606060408587611485565b610312916114af565b81600281518110610325576103256114cd565b6020026020010181815250506000801b81600381518110610348576103486114cd565b60200260200101818152505061035d81610c2a565b949350505050565b836103825760405162461bcd60e51b8152600401610261906114e3565b610120811480156103d457506103d482828080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508892508791506102e29050610ec3565b6101895760405162461bcd60e51b815260206004820152601f60248201527f496e76616c6964206465706f73697420636f6e7461696e65722070726f6f66006044820152606401610261565b8461043d5760405162461bcd60e51b8152600401610261906114e3565b600764ffffffffff851666059600000000001760031b176106a0821480156104ac57506104ac83838080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508a92506104a69150889050610edb565b84610ec3565b6101b15760405162461bcd60e51b815260206004820152601a60248201527f496e76616c696420776974686472617761626c652070726f6f660000000000006044820152606401610261565b856105155760405162461bcd60e51b8152600401610261906114e3565b64ffffffffff821666059600000000001760031b60006105386020828789611485565b610541916114af565b90508281146105925760405162461bcd60e51b815260206004820152601760248201527f496e76616c6964207769746864726177616c20637265640000000000000000006044820152606401610261565b6106a0851480156105e257506105e286868080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508c92508b9150869050610ec3565b61062e5760405162461bcd60e51b815260206004820152601760248201527f496e76616c69642076616c696461746f722070726f6f660000000000000000006044820152606401610261565b5050505050505050565b846106745760405162461bcd60e51b815260206004820152600c60248201526b125b9d985b1a59081c9bdbdd60a21b6044820152606401610261565b6106806001601c611525565b61068b90600261161f565b8163ffffffff16106106d75760405162461bcd60e51b8152602060048201526015602482015274092dcecc2d8d2c840c8cae0dee6d2e840d2dcc8caf605b1b6044820152606401610261565b63ffffffff811663100000001761038083148015610734575061073484848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508a9250899150859050610ec3565b6101b15760405162461bcd60e51b815260206004820152601560248201527424b73b30b634b2103232b837b9b4ba10383937b7b360591b6044820152606401610261565b836107955760405162461bcd60e51b8152600401610261906114e3565b610120811480156107e757506107e782828080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508892508791506102cc9050610ec3565b6101895760405162461bcd60e51b815260206004820152601f60248201527f496e76616c69642062616c616e636520636f6e7461696e65722070726f6f66006044820152606401610261565b60408051600880825261012082019092526000918291906020820161010080368337019050509050888160008151811061086f5761086f6114cd565b60209081029190910181019190915261088b906000898b611485565b610894916114af565b816001815181106108a7576108a76114cd565b6020026020010181815250506108bc86610edb565b816002815181106108cf576108cf6114cd565b6020026020010181815250506108e5858561021f565b816003815181106108f8576108f86114cd565b60200260200101818152505061090d83610edb565b81600481518110610920576109206114cd565b6020026020010181815250506000801b81600581518110610943576109436114cd565b6020026020010181815250506000801b81600681518110610966576109666114cd565b6020026020010181815250506000801b81600781518110610989576109896114cd565b60200260200101818152505061099e81610c2a565b9998505050505050505050565b6000846109ca5760405162461bcd60e51b8152600401610261906114e3565b61049f198201610a6f57610a1b83838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201829052508a93509150642e200000009050610ec3565b610a675760405162461bcd60e51b815260206004820152601c60248201527f496e76616c696420656d707479206465706f736974732070726f6f66000000006044820152606401610261565b50600161035d565b61050082148015610acd5750610acd83838080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250899250610ac19150889050610edb565b65017100000004610ec3565b61035d5760405162461bcd60e51b815260206004820152601a60248201527f496e76616c6964206465706f73697420736c6f742070726f6f660000000000006044820152606401610261565b600085610b615760405162461bcd60e51b8152602060048201526016602482015275125b9d985b1a590818dbdb9d185a5b995c881c9bdbdd60521b6044820152606401610261565b6000610b6e600484611641565b64ffffffffff1690506480000000008117610b898785610f51565b92506104e085148015610bdb5750610bdb86868080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508c92508b9150859050610ec3565b610c1f5760405162461bcd60e51b815260206004820152601560248201527424b73b30b634b2103130b630b731b290383937b7b360591b6044820152606401610261565b505095945050505050565b60008060028351610c3b919061166b565b90506000816001600160401b03811115610c5757610c5761146f565b604051908082528060200260200182016040528015610c80578160200160208202803683370190505b50905060005b82811015610d7d57600285610c9b838361167f565b81518110610cab57610cab6114cd565b602002602001015186836002610cc1919061167f565b610ccc906001611696565b81518110610cdc57610cdc6114cd565b6020026020010151604051602001610cfe929190918252602082015260400190565b60408051601f1981840301815290829052610d18916116a9565b602060405180830381855afa158015610d35573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190610d5891906116d8565b828281518110610d6a57610d6a6114cd565b6020908102919091010152600101610c86565b50610d8960028361166b565b91505b8115610e9f5760005b82811015610e8c57600282610daa838361167f565b81518110610dba57610dba6114cd565b602002602001015183836002610dd0919061167f565b610ddb906001611696565b81518110610deb57610deb6114cd565b6020026020010151604051602001610e0d929190918252602082015260400190565b60408051601f1981840301815290829052610e27916116a9565b602060405180830381855afa158015610e44573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190610e6791906116d8565b828281518110610e7957610e796114cd565b6020908102919091010152600101610d95565b50610e9860028361166b565b9150610d8c565b80600081518110610eb257610eb26114cd565b602002602001015192505050919050565b600083610ed1868585610fed565b1495945050505050565b603881811c60ff16602883811c61ff001691909117601884811c62ff00001691909117600885811c63ff000000169190911764ff000000009186901b919091161765ff00000000009185901b919091161766ff0000000000009184901b919091161760ff60381b9290911b919091161760c01b90565b600080610f5f6004846116f1565b610f6a90604061171b565b64ffffffffff169050610fdc84821b60f881901c60e882901c61ff00161760d882901c62ff0000161760c882901c63ff000000161764ff0000000060b883901c161765ff000000000060a883901c161766ff000000000000609883901c161760ff60381b60889290921c919091161790565b6001600160401b0316949350505050565b6000835160001415801561100c57506020845161100a9190611742565b155b611029576040516313717da960e21b815260040160405180910390fd5b604080516020808201909252848152905b855181116110c05761104d600285611742565b60000361107d578151600052808601516020526020826040600060026107d05a03fa61107857600080fd5b6110a1565b8086015160005281516020526020826040600060026107d05a03fa6110a157600080fd5b6110ac60028561166b565b93506110b9602082611696565b905061103a565b5051949350505050565b60008083601f8401126110dc57600080fd5b5081356001600160401b038111156110f357600080fd5b60208301915083602082850101111561110b57600080fd5b9250929050565b6000806020838503121561112557600080fd5b82356001600160401b0381111561113b57600080fd5b611147858286016110ca565b90969095509350505050565b6000806000806060858703121561116957600080fd5b843593506020850135925060408501356001600160401b0381111561118d57600080fd5b611199878288016110ca565b95989497509550505050565b803564ffffffffff811681146111ba57600080fd5b919050565b80356001600160401b03811681146111ba57600080fd5b6000806000806000608086880312156111ee57600080fd5b853594506111fe602087016111a5565b935061120c604087016111bf565b925060608601356001600160401b0381111561122757600080fd5b611233888289016110ca565b969995985093965092949392505050565b60008060008060008060a0878903121561125d57600080fd5b863595506020870135945060408701356001600160401b0381111561128157600080fd5b61128d89828a016110ca565b90955093506112a09050606088016111a5565b95989497509295919493608090920135925050565b6000806000806000608086880312156112cd57600080fd5b853594506020860135935060408601356001600160401b038111156112f157600080fd5b6112fd888289016110ca565b909450925050606086013563ffffffff8116811461131a57600080fd5b809150509295509295909350565b600080600080600080600060a0888a03121561134357600080fd5b8735965060208801356001600160401b0381111561136057600080fd5b61136c8a828b016110ca565b909750955061137f9050604089016111bf565b935060608801356001600160401b0381111561139a57600080fd5b6113a68a828b016110ca565b90945092506113b99050608089016111bf565b905092959891949750929550565b600080600080606085870312156113dd57600080fd5b843593506113ed602086016111bf565b925060408501356001600160401b0381111561118d57600080fd5b60008060008060006080868803121561142057600080fd5b853594506020860135935060408601356001600160401b0381111561144457600080fd5b611450888289016110ca565b90945092506114639050606087016111a5565b90509295509295909350565b634e487b7160e01b600052604160045260246000fd5b6000808585111561149557600080fd5b838611156114a257600080fd5b5050820193919092039150565b8035602083101561017757600019602084900360031b1b1692915050565b634e487b7160e01b600052603260045260246000fd5b602080825260129082015271125b9d985b1a5908189b1bd8dac81c9bdbdd60721b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b818103818111156101775761017761150f565b6001815b6001841115611573578085048111156115575761155761150f565b600184161561156557908102905b60019390931c92800261153c565b935093915050565b60008261158a57506001610177565b8161159757506000610177565b81600181146115ad57600281146115b7576115d3565b6001915050610177565b60ff8411156115c8576115c861150f565b50506001821b610177565b5060208310610133831016604e8410600b84101617156115f6575081810a610177565b6116036000198484611538565b80600019048211156116175761161761150f565b029392505050565b6000610174838361157b565b634e487b7160e01b600052601260045260246000fd5b600064ffffffffff8316806116585761165861162b565b8064ffffffffff84160491505092915050565b60008261167a5761167a61162b565b500490565b80820281158282048414176101775761017761150f565b808201808211156101775761017761150f565b6000825160005b818110156116ca57602081860181015185830152016116b0565b506000920191825250919050565b6000602082840312156116ea57600080fd5b5051919050565b600064ffffffffff8316806117085761170861162b565b8064ffffffffff84160691505092915050565b64ffffffffff818116838216029081169081811461173b5761173b61150f565b5092915050565b6000826117515761175161162b565b50069056fea26469706673582212205e6c180b3544abbcda0a898d1a795e4ddbd861b7b533fbede356a76fa464c51064736f6c634300081c0033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100935760003560e01c80638a050dd4116100665780638a050dd4146100f957806391ad640d1461010c578063afe768871461011f578063d98a556414610132578063f34ad34c1461015557600080fd5b8063258b83d8146100985780632b00e796146100be578063334a88fb146100d357806354ba95b2146100e6575b600080fd5b6100ab6100a6366004611112565b610168565b6040519081526020015b60405180910390f35b6100d16100cc366004611153565b61017d565b005b6100d16100e13660046111d6565b61018f565b6100d16100f4366004611244565b6101a3565b6100d16101073660046112b5565b6101b9565b6100d161011a366004611153565b6101c6565b6100ab61012d366004611328565b6101d2565b6101456101403660046113c7565b6101ef565b60405190151581526020016100b5565b6100ab610163366004611408565b610206565b6000610174838361021f565b90505b92915050565b61018984848484610365565b50505050565b61019c8585858585610420565b5050505050565b6101b18686868686866104f8565b505050505050565b61019c8585858585610638565b61018984848484610778565b60006101e388888888888888610833565b98975050505050505050565b60006101fd858585856109ab565b95945050505050565b60006102158686868686610b19565b9695505050505050565b60006060821461026a5760405162461bcd60e51b8152602060048201526011602482015270496e76616c6964207369676e617475726560781b60448201526064015b60405180910390fd5b60408051600480825260a082019092526000916020820160808036833701905050905061029b602060008587611485565b6102a4916114af565b816000815181106102b7576102b76114cd565b6020908102919091018101919091526102d4906040908587611485565b6102dd916114af565b816001815181106102f0576102f06114cd565b6020908102919091010152610309606060408587611485565b610312916114af565b81600281518110610325576103256114cd565b6020026020010181815250506000801b81600381518110610348576103486114cd565b60200260200101818152505061035d81610c2a565b949350505050565b836103825760405162461bcd60e51b8152600401610261906114e3565b610120811480156103d457506103d482828080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508892508791506102e29050610ec3565b6101895760405162461bcd60e51b815260206004820152601f60248201527f496e76616c6964206465706f73697420636f6e7461696e65722070726f6f66006044820152606401610261565b8461043d5760405162461bcd60e51b8152600401610261906114e3565b600764ffffffffff851666059600000000001760031b176106a0821480156104ac57506104ac83838080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508a92506104a69150889050610edb565b84610ec3565b6101b15760405162461bcd60e51b815260206004820152601a60248201527f496e76616c696420776974686472617761626c652070726f6f660000000000006044820152606401610261565b856105155760405162461bcd60e51b8152600401610261906114e3565b64ffffffffff821666059600000000001760031b60006105386020828789611485565b610541916114af565b90508281146105925760405162461bcd60e51b815260206004820152601760248201527f496e76616c6964207769746864726177616c20637265640000000000000000006044820152606401610261565b6106a0851480156105e257506105e286868080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508c92508b9150869050610ec3565b61062e5760405162461bcd60e51b815260206004820152601760248201527f496e76616c69642076616c696461746f722070726f6f660000000000000000006044820152606401610261565b5050505050505050565b846106745760405162461bcd60e51b815260206004820152600c60248201526b125b9d985b1a59081c9bdbdd60a21b6044820152606401610261565b6106806001601c611525565b61068b90600261161f565b8163ffffffff16106106d75760405162461bcd60e51b8152602060048201526015602482015274092dcecc2d8d2c840c8cae0dee6d2e840d2dcc8caf605b1b6044820152606401610261565b63ffffffff811663100000001761038083148015610734575061073484848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508a9250899150859050610ec3565b6101b15760405162461bcd60e51b815260206004820152601560248201527424b73b30b634b2103232b837b9b4ba10383937b7b360591b6044820152606401610261565b836107955760405162461bcd60e51b8152600401610261906114e3565b610120811480156107e757506107e782828080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508892508791506102cc9050610ec3565b6101895760405162461bcd60e51b815260206004820152601f60248201527f496e76616c69642062616c616e636520636f6e7461696e65722070726f6f66006044820152606401610261565b60408051600880825261012082019092526000918291906020820161010080368337019050509050888160008151811061086f5761086f6114cd565b60209081029190910181019190915261088b906000898b611485565b610894916114af565b816001815181106108a7576108a76114cd565b6020026020010181815250506108bc86610edb565b816002815181106108cf576108cf6114cd565b6020026020010181815250506108e5858561021f565b816003815181106108f8576108f86114cd565b60200260200101818152505061090d83610edb565b81600481518110610920576109206114cd565b6020026020010181815250506000801b81600581518110610943576109436114cd565b6020026020010181815250506000801b81600681518110610966576109666114cd565b6020026020010181815250506000801b81600781518110610989576109896114cd565b60200260200101818152505061099e81610c2a565b9998505050505050505050565b6000846109ca5760405162461bcd60e51b8152600401610261906114e3565b61049f198201610a6f57610a1b83838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201829052508a93509150642e200000009050610ec3565b610a675760405162461bcd60e51b815260206004820152601c60248201527f496e76616c696420656d707479206465706f736974732070726f6f66000000006044820152606401610261565b50600161035d565b61050082148015610acd5750610acd83838080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250899250610ac19150889050610edb565b65017100000004610ec3565b61035d5760405162461bcd60e51b815260206004820152601a60248201527f496e76616c6964206465706f73697420736c6f742070726f6f660000000000006044820152606401610261565b600085610b615760405162461bcd60e51b8152602060048201526016602482015275125b9d985b1a590818dbdb9d185a5b995c881c9bdbdd60521b6044820152606401610261565b6000610b6e600484611641565b64ffffffffff1690506480000000008117610b898785610f51565b92506104e085148015610bdb5750610bdb86868080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508c92508b9150859050610ec3565b610c1f5760405162461bcd60e51b815260206004820152601560248201527424b73b30b634b2103130b630b731b290383937b7b360591b6044820152606401610261565b505095945050505050565b60008060028351610c3b919061166b565b90506000816001600160401b03811115610c5757610c5761146f565b604051908082528060200260200182016040528015610c80578160200160208202803683370190505b50905060005b82811015610d7d57600285610c9b838361167f565b81518110610cab57610cab6114cd565b602002602001015186836002610cc1919061167f565b610ccc906001611696565b81518110610cdc57610cdc6114cd565b6020026020010151604051602001610cfe929190918252602082015260400190565b60408051601f1981840301815290829052610d18916116a9565b602060405180830381855afa158015610d35573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190610d5891906116d8565b828281518110610d6a57610d6a6114cd565b6020908102919091010152600101610c86565b50610d8960028361166b565b91505b8115610e9f5760005b82811015610e8c57600282610daa838361167f565b81518110610dba57610dba6114cd565b602002602001015183836002610dd0919061167f565b610ddb906001611696565b81518110610deb57610deb6114cd565b6020026020010151604051602001610e0d929190918252602082015260400190565b60408051601f1981840301815290829052610e27916116a9565b602060405180830381855afa158015610e44573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190610e6791906116d8565b828281518110610e7957610e796114cd565b6020908102919091010152600101610d95565b50610e9860028361166b565b9150610d8c565b80600081518110610eb257610eb26114cd565b602002602001015192505050919050565b600083610ed1868585610fed565b1495945050505050565b603881811c60ff16602883811c61ff001691909117601884811c62ff00001691909117600885811c63ff000000169190911764ff000000009186901b919091161765ff00000000009185901b919091161766ff0000000000009184901b919091161760ff60381b9290911b919091161760c01b90565b600080610f5f6004846116f1565b610f6a90604061171b565b64ffffffffff169050610fdc84821b60f881901c60e882901c61ff00161760d882901c62ff0000161760c882901c63ff000000161764ff0000000060b883901c161765ff000000000060a883901c161766ff000000000000609883901c161760ff60381b60889290921c919091161790565b6001600160401b0316949350505050565b6000835160001415801561100c57506020845161100a9190611742565b155b611029576040516313717da960e21b815260040160405180910390fd5b604080516020808201909252848152905b855181116110c05761104d600285611742565b60000361107d578151600052808601516020526020826040600060026107d05a03fa61107857600080fd5b6110a1565b8086015160005281516020526020826040600060026107d05a03fa6110a157600080fd5b6110ac60028561166b565b93506110b9602082611696565b905061103a565b5051949350505050565b60008083601f8401126110dc57600080fd5b5081356001600160401b038111156110f357600080fd5b60208301915083602082850101111561110b57600080fd5b9250929050565b6000806020838503121561112557600080fd5b82356001600160401b0381111561113b57600080fd5b611147858286016110ca565b90969095509350505050565b6000806000806060858703121561116957600080fd5b843593506020850135925060408501356001600160401b0381111561118d57600080fd5b611199878288016110ca565b95989497509550505050565b803564ffffffffff811681146111ba57600080fd5b919050565b80356001600160401b03811681146111ba57600080fd5b6000806000806000608086880312156111ee57600080fd5b853594506111fe602087016111a5565b935061120c604087016111bf565b925060608601356001600160401b0381111561122757600080fd5b611233888289016110ca565b969995985093965092949392505050565b60008060008060008060a0878903121561125d57600080fd5b863595506020870135945060408701356001600160401b0381111561128157600080fd5b61128d89828a016110ca565b90955093506112a09050606088016111a5565b95989497509295919493608090920135925050565b6000806000806000608086880312156112cd57600080fd5b853594506020860135935060408601356001600160401b038111156112f157600080fd5b6112fd888289016110ca565b909450925050606086013563ffffffff8116811461131a57600080fd5b809150509295509295909350565b600080600080600080600060a0888a03121561134357600080fd5b8735965060208801356001600160401b0381111561136057600080fd5b61136c8a828b016110ca565b909750955061137f9050604089016111bf565b935060608801356001600160401b0381111561139a57600080fd5b6113a68a828b016110ca565b90945092506113b99050608089016111bf565b905092959891949750929550565b600080600080606085870312156113dd57600080fd5b843593506113ed602086016111bf565b925060408501356001600160401b0381111561118d57600080fd5b60008060008060006080868803121561142057600080fd5b853594506020860135935060408601356001600160401b0381111561144457600080fd5b611450888289016110ca565b90945092506114639050606087016111a5565b90509295509295909350565b634e487b7160e01b600052604160045260246000fd5b6000808585111561149557600080fd5b838611156114a257600080fd5b5050820193919092039150565b8035602083101561017757600019602084900360031b1b1692915050565b634e487b7160e01b600052603260045260246000fd5b602080825260129082015271125b9d985b1a5908189b1bd8dac81c9bdbdd60721b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b818103818111156101775761017761150f565b6001815b6001841115611573578085048111156115575761155761150f565b600184161561156557908102905b60019390931c92800261153c565b935093915050565b60008261158a57506001610177565b8161159757506000610177565b81600181146115ad57600281146115b7576115d3565b6001915050610177565b60ff8411156115c8576115c861150f565b50506001821b610177565b5060208310610133831016604e8410600b84101617156115f6575081810a610177565b6116036000198484611538565b80600019048211156116175761161761150f565b029392505050565b6000610174838361157b565b634e487b7160e01b600052601260045260246000fd5b600064ffffffffff8316806116585761165861162b565b8064ffffffffff84160491505092915050565b60008261167a5761167a61162b565b500490565b80820281158282048414176101775761017761150f565b808201808211156101775761017761150f565b6000825160005b818110156116ca57602081860181015185830152016116b0565b506000920191825250919050565b6000602082840312156116ea57600080fd5b5051919050565b600064ffffffffff8316806117085761170861162b565b8064ffffffffff84160691505092915050565b64ffffffffff818116838216029081169081811461173b5761173b61150f565b5092915050565b6000826117515761175161162b565b50069056fea26469706673582212205e6c180b3544abbcda0a898d1a795e4ddbd861b7b533fbede356a76fa464c51064736f6c634300081c0033", + "libraries": {}, + "devdoc": { + "author": "Origin Protocol Inc", + "kind": "dev", + "methods": { + "merkleizePendingDeposit(bytes32,bytes,uint64,bytes,uint64)": { + "params": { + "amountGwei": "The amount of the deposit in Gwei.", + "pubKeyHash": "Hash of validator's public key using the Beacon Chain's format", + "signature": "The 96 byte BLS signature.", + "slot": "The beacon chain slot the deposit was made in.", + "withdrawalCredentials": "The 32 byte withdrawal credentials." + }, + "returns": { + "_0": "root The merkle root of the pending deposit." + } + }, + "merkleizeSignature(bytes)": { + "params": { + "signature": "The 96 byte BLS signature." + }, + "returns": { + "root": "The merkle root of the signature." + } + }, + "verifyBalancesContainer(bytes32,bytes32,bytes)": { + "params": { + "balancesContainerProof": "The merkle proof for the balances container to the beacon block root. This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.", + "balancesContainerRoot": "The merkle root of the the balances container", + "beaconBlockRoot": "The root of the beacon block" + } + }, + "verifyFirstPendingDeposit(bytes32,uint64,bytes)": { + "params": { + "beaconBlockRoot": "The root of the beacon block.", + "firstPendingDepositSlotProof": "The merkle proof to the beacon block root. Can be either: - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty. - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty. The 32 byte witness hashes are concatenated together starting from the leaf node.", + "slot": "The beacon chain slot of the first deposit in the beacon chain's deposit queue. Can be anything if the deposit queue is empty." + }, + "returns": { + "isEmptyDepositQueue": "True if the deposit queue is empty, false otherwise." + } + }, + "verifyPendingDeposit(bytes32,bytes32,bytes,uint32)": { + "params": { + "pendingDepositIndex": "The pending deposit index in the Pending Deposits container", + "pendingDepositRoot": "The leaf node containing the validator balance with three other balances.", + "pendingDepositsContainerRoot": "The merkle root of the Pending Deposits container.", + "proof": "The merkle proof for the pending deposit root to the Pending Deposits container root. This is 28 witness hashes of 32 bytes each concatenated together starting from the leaf node." + } + }, + "verifyPendingDepositsContainer(bytes32,bytes32,bytes)": { + "params": { + "beaconBlockRoot": "The root of the beacon block.", + "pendingDepositsContainerRoot": "The merkle root of the the pending deposits container.", + "proof": "The merkle proof for the pending deposits container to the beacon block root. This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node." + } + }, + "verifyValidator(bytes32,bytes32,bytes,uint40,bytes32)": { + "params": { + "beaconBlockRoot": "The root of the beacon block", + "proof": "The merkle proof for the validator public key to the beacon block root. This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.", + "pubKeyHash": "Hash of validator's public key using the Beacon Chain's format", + "validatorIndex": "The validator index", + "withdrawalCredentials": "a value containing the validator type and withdrawal address." + } + }, + "verifyValidatorBalance(bytes32,bytes32,bytes,uint40)": { + "params": { + "balanceProof": "The merkle proof for the validator balance to the Balances container root. This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.", + "balancesContainerRoot": "The merkle root of the Balances container.", + "validatorBalanceLeaf": "The leaf node containing the validator balance with three other balances.", + "validatorIndex": "The validator index to verify the balance for" + }, + "returns": { + "validatorBalanceGwei": "The balance in Gwei of the validator at the given index" + } + } + }, + "title": "Verifies merkle proofs of beacon chain data.", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "merkleizePendingDeposit(bytes32,bytes,uint64,bytes,uint64)": { + "notice": "Merkleizes a beacon chain pending deposit." + }, + "merkleizeSignature(bytes)": { + "notice": "Merkleizes a BLS signature used for validator deposits." + }, + "verifyBalancesContainer(bytes32,bytes32,bytes)": { + "notice": "Verifies the balances container to the beacon block root BeaconBlock.state.balances" + }, + "verifyFirstPendingDeposit(bytes32,uint64,bytes)": { + "notice": "If the deposit queue is not empty, verify the slot of the first pending deposit to the beacon block root. BeaconBlock.state.pendingDeposits[0].slot If the deposit queue is empty, verify the root of the first pending deposit is empty BeaconBlock.state.PendingDeposits[0]" + }, + "verifyPendingDeposit(bytes32,bytes32,bytes,uint32)": { + "notice": "Verified a pending deposit to the root of the Pending Deposits container." + }, + "verifyPendingDepositsContainer(bytes32,bytes32,bytes)": { + "notice": "Verifies the pending deposits container to the beacon block root. BeaconBlock.state.pendingDeposits" + }, + "verifyValidator(bytes32,bytes32,bytes,uint40,bytes32)": { + "notice": "Verifies the validator index is for the given validator public key. Also verify the validator's withdrawal credential points to the withdrawal address. BeaconBlock.state.validators[validatorIndex].pubkey" + }, + "verifyValidatorBalance(bytes32,bytes32,bytes,uint40)": { + "notice": "Verifies the validator balance to the root of the Balances container." + } + }, + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/CompoundingStakingSSVStrategy.json b/contracts/deployments/hoodi/CompoundingStakingSSVStrategy.json new file mode 100644 index 0000000000..240bf9b7ff --- /dev/null +++ b/contracts/deployments/hoodi/CompoundingStakingSSVStrategy.json @@ -0,0 +1,2247 @@ +{ + "address": "0x3302C7ba6076D39E70336c799a1bcf7619faE5bf", + "abi": [ + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "platformAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "vaultAddress", + "type": "address" + } + ], + "internalType": "struct InitializableAbstractStrategy.BaseStrategyConfig", + "name": "_baseConfig", + "type": "tuple" + }, + { + "internalType": "address", + "name": "_wethAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "_ssvToken", + "type": "address" + }, + { + "internalType": "address", + "name": "_ssvNetwork", + "type": "address" + }, + { + "internalType": "address", + "name": "_beaconChainDepositContract", + "type": "address" + }, + { + "internalType": "address", + "name": "_beaconProofs", + "type": "address" + }, + { + "internalType": "uint64", + "name": "_beaconGenesisTimestamp", + "type": "uint64" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "blockRoot", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "ethBalance", + "type": "uint256" + } + ], + "name": "BalancesSnapped", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "timestamp", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalDepositsWei", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalValidatorBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "ethBalance", + "type": "uint256" + } + ], + "name": "BalancesVerified", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_pToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "pendingDepositRoot", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountWei", + "type": "uint256" + } + ], + "name": "DepositVerified", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "pendingDepositRoot", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "pubKey", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountWei", + "type": "uint256" + } + ], + "name": "ETHStaked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "FirstDepositReset", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "GovernorshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_oldHarvesterAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_newHarvesterAddress", + "type": "address" + } + ], + "name": "HarvesterAddressesUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_pToken", + "type": "address" + } + ], + "name": "PTokenAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_pToken", + "type": "address" + } + ], + "name": "PTokenRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "PendingGovernorshipTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "newAddress", + "type": "address" + } + ], + "name": "RegistratorChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "_oldAddresses", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "_newAddresses", + "type": "address[]" + } + ], + "name": "RewardTokenAddressesUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "rewardToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "RewardTokenCollected", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + } + ], + "name": "SSVValidatorRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + } + ], + "name": "SSVValidatorRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "newAddress", + "type": "address" + } + ], + "name": "StakingMonitorChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + } + ], + "name": "ValidatorInvalid", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "uint40", + "name": "validatorIndex", + "type": "uint40" + } + ], + "name": "ValidatorVerified", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountWei", + "type": "uint256" + } + ], + "name": "ValidatorWithdraw", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_pToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "Withdrawal", + "type": "event" + }, + { + "inputs": [], + "name": "BEACON_PROOFS", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "SSV_NETWORK", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "SSV_TOKEN", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "assetToPToken", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + } + ], + "name": "checkBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "claimGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "collectRewardTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "deposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "depositAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "depositList", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "depositListLength", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "depositedWethAccountedFor", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "deposits", + "outputs": [ + { + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "amountGwei", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "slot", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "depositIndex", + "type": "uint32" + }, + { + "internalType": "enum CompoundingValidatorManager.DepositStatus", + "name": "status", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "firstDeposit", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRewardTokenAddresses", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "governor", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "harvesterAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_rewardTokenAddresses", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "_assets", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "_pTokens", + "type": "address[]" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isGovernor", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastVerifiedEthBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "platformAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "internalType": "bytes", + "name": "sharesData", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "ssvAmount", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "internalType": "struct Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "registerSsvValidator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "removePToken", + "outputs": [], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "internalType": "struct Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "removeSsvValidator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "resetFirstDeposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "rewardTokenAddresses", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "safeApproveAllTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_harvesterAddress", + "type": "address" + } + ], + "name": "setHarvesterAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "setPTokenAddress", + "outputs": [], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "setRegistrator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_rewardTokenAddresses", + "type": "address[]" + } + ], + "name": "setRewardTokenAddresses", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "snapBalances", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "snappedBalance", + "outputs": [ + { + "internalType": "bytes32", + "name": "blockRoot", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "timestamp", + "type": "uint64" + }, + { + "internalType": "uint128", + "name": "ethBalance", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes", + "name": "pubkey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "depositDataRoot", + "type": "bytes32" + } + ], + "internalType": "struct CompoundingValidatorManager.ValidatorStakeData", + "name": "validatorStakeData", + "type": "tuple" + }, + { + "internalType": "uint64", + "name": "depositAmountGwei", + "type": "uint64" + } + ], + "name": "stakeEth", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + } + ], + "name": "supportsAsset", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newGovernor", + "type": "address" + } + ], + "name": "transferGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "transferToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unPause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "validator", + "outputs": [ + { + "internalType": "enum CompoundingValidatorManager.ValidatorState", + "name": "state", + "type": "uint8" + }, + { + "internalType": "uint40", + "name": "index", + "type": "uint40" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "validatorRegistrator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "amountGwei", + "type": "uint64" + } + ], + "name": "validatorWithdrawal", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "vaultAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "verifiedValidators", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "verifiedValidatorsLength", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "balancesContainerRoot", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "balancesContainerProof", + "type": "bytes" + }, + { + "internalType": "bytes32[]", + "name": "validatorBalanceLeaves", + "type": "bytes32[]" + }, + { + "internalType": "bytes[]", + "name": "validatorBalanceProofs", + "type": "bytes[]" + } + ], + "internalType": "struct CompoundingValidatorManager.BalanceProofs", + "name": "balanceProofs", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "pendingDepositContainerRoot", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "pendingDepositContainerProof", + "type": "bytes" + }, + { + "internalType": "uint32[]", + "name": "pendingDepositIndexes", + "type": "uint32[]" + }, + { + "internalType": "bytes[]", + "name": "pendingDepositProofs", + "type": "bytes[]" + } + ], + "internalType": "struct CompoundingValidatorManager.PendingDepositProofs", + "name": "pendingDepositProofs", + "type": "tuple" + } + ], + "name": "verifyBalances", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "pendingDepositRoot", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "depositProcessedSlot", + "type": "uint64" + }, + { + "components": [ + { + "internalType": "uint64", + "name": "slot", + "type": "uint64" + }, + { + "internalType": "bytes", + "name": "proof", + "type": "bytes" + } + ], + "internalType": "struct CompoundingValidatorManager.FirstPendingDepositSlotProofData", + "name": "firstPendingDeposit", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint64", + "name": "withdrawableEpoch", + "type": "uint64" + }, + { + "internalType": "bytes", + "name": "withdrawableEpochProof", + "type": "bytes" + } + ], + "internalType": "struct CompoundingValidatorManager.StrategyValidatorProofData", + "name": "strategyValidatorData", + "type": "tuple" + } + ], + "name": "verifyDeposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "nextBlockTimestamp", + "type": "uint64" + }, + { + "internalType": "uint40", + "name": "validatorIndex", + "type": "uint40" + }, + { + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "withdrawalCredentials", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "validatorPubKeyProof", + "type": "bytes" + } + ], + "name": "verifyValidator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_recipient", + "type": "address" + }, + { + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "internalType": "uint256", + "name": "ssvAmount", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "internalType": "struct Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "withdrawSSV", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "transactionHash": "0x6459f4fbf847ea6faead199e52aaed7dbd0c406c1648c9840688a597674fff82", + "receipt": { + "to": null, + "from": "0xf7749B41db006860cEc0650D18b8013d69C44Eeb", + "contractAddress": "0x3302C7ba6076D39E70336c799a1bcf7619faE5bf", + "transactionIndex": 21, + "gasUsed": "5357056", + "logsBloom": "0x000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000008000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000100000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0xa3063a1ebd9849c07f737010d37b6888c0ce6cf56150563d635cd979eb3f3d67", + "transactionHash": "0x6459f4fbf847ea6faead199e52aaed7dbd0c406c1648c9840688a597674fff82", + "logs": [ + { + "transactionIndex": 21, + "blockNumber": 1263390, + "transactionHash": "0x6459f4fbf847ea6faead199e52aaed7dbd0c406c1648c9840688a597674fff82", + "address": "0x3302C7ba6076D39E70336c799a1bcf7619faE5bf", + "topics": [ + "0xc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "data": "0x", + "logIndex": 248, + "blockHash": "0xa3063a1ebd9849c07f737010d37b6888c0ce6cf56150563d635cd979eb3f3d67" + } + ], + "blockNumber": 1263390, + "cumulativeGasUsed": "19822017", + "status": 1, + "byzantium": true + }, + "args": [ + [ + "0x0000000000000000000000000000000000000000", + "0xD0cC28bc8F4666286F3211e465ecF1fe5c72AC8B" + ], + "0x2387fD72C1DA19f6486B843F5da562679FbB4057", + "0x9F5d4Ec84fC4785788aB44F9de973cF34F7A038e", + "0x58410Bef803ECd7E63B23664C586A6DB72DAf59c", + "0x00000000219ab540356cBB839Cbe05303d7705Fa", + "0x04b2D730106B9cA91aA011984eD2F7d62D00cF9f", + 1742213400 + ], + "numDeployments": 6, + "solcInputHash": "7de90067c6ddbeeea4788e7c02ed9ce8", + "metadata": "{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"platformAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"vaultAddress\",\"type\":\"address\"}],\"internalType\":\"struct InitializableAbstractStrategy.BaseStrategyConfig\",\"name\":\"_baseConfig\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"_wethAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_ssvToken\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_ssvNetwork\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_beaconChainDepositContract\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_beaconProofs\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"_beaconGenesisTimestamp\",\"type\":\"uint64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"blockRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"ethBalance\",\"type\":\"uint256\"}],\"name\":\"BalancesSnapped\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"totalDepositsWei\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"totalValidatorBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"ethBalance\",\"type\":\"uint256\"}],\"name\":\"BalancesVerified\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_pToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"Deposit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pendingDepositRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amountWei\",\"type\":\"uint256\"}],\"name\":\"DepositVerified\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pendingDepositRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"pubKey\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amountWei\",\"type\":\"uint256\"}],\"name\":\"ETHStaked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"FirstDepositReset\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"GovernorshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_oldHarvesterAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_newHarvesterAddress\",\"type\":\"address\"}],\"name\":\"HarvesterAddressesUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_pToken\",\"type\":\"address\"}],\"name\":\"PTokenAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_pToken\",\"type\":\"address\"}],\"name\":\"PTokenRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"PendingGovernorshipTransfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newAddress\",\"type\":\"address\"}],\"name\":\"RegistratorChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"_oldAddresses\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"_newAddresses\",\"type\":\"address[]\"}],\"name\":\"RewardTokenAddressesUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"rewardToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"RewardTokenCollected\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64[]\",\"name\":\"operatorIds\",\"type\":\"uint64[]\"}],\"name\":\"SSVValidatorRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64[]\",\"name\":\"operatorIds\",\"type\":\"uint64[]\"}],\"name\":\"SSVValidatorRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newAddress\",\"type\":\"address\"}],\"name\":\"StakingMonitorChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"}],\"name\":\"ValidatorInvalid\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint40\",\"name\":\"validatorIndex\",\"type\":\"uint40\"}],\"name\":\"ValidatorVerified\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amountWei\",\"type\":\"uint256\"}],\"name\":\"ValidatorWithdraw\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_pToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"Withdrawal\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BEACON_PROOFS\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SSV_NETWORK\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SSV_TOKEN\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"assetToPToken\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"}],\"name\":\"checkBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"collectRewardTokens\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"deposit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"depositAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"depositList\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"depositListLength\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"depositedWethAccountedFor\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"deposits\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"amountGwei\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"slot\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"depositIndex\",\"type\":\"uint32\"},{\"internalType\":\"enum CompoundingValidatorManager.DepositStatus\",\"name\":\"status\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"firstDeposit\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRewardTokenAddresses\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"governor\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"harvesterAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_rewardTokenAddresses\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"_assets\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"_pTokens\",\"type\":\"address[]\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isGovernor\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastVerifiedEthBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"platformAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"publicKey\",\"type\":\"bytes\"},{\"internalType\":\"uint64[]\",\"name\":\"operatorIds\",\"type\":\"uint64[]\"},{\"internalType\":\"bytes\",\"name\":\"sharesData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"ssvAmount\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"validatorCount\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"networkFeeIndex\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"index\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"internalType\":\"struct Cluster\",\"name\":\"cluster\",\"type\":\"tuple\"}],\"name\":\"registerSsvValidator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"removePToken\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"publicKey\",\"type\":\"bytes\"},{\"internalType\":\"uint64[]\",\"name\":\"operatorIds\",\"type\":\"uint64[]\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"validatorCount\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"networkFeeIndex\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"index\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"internalType\":\"struct Cluster\",\"name\":\"cluster\",\"type\":\"tuple\"}],\"name\":\"removeSsvValidator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"resetFirstDeposit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"rewardTokenAddresses\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"safeApproveAllTokens\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_harvesterAddress\",\"type\":\"address\"}],\"name\":\"setHarvesterAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"setPTokenAddress\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_address\",\"type\":\"address\"}],\"name\":\"setRegistrator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_rewardTokenAddresses\",\"type\":\"address[]\"}],\"name\":\"setRewardTokenAddresses\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"snapBalances\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"snappedBalance\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"blockRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint128\",\"name\":\"ethBalance\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"pubkey\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"},{\"internalType\":\"bytes32\",\"name\":\"depositDataRoot\",\"type\":\"bytes32\"}],\"internalType\":\"struct CompoundingValidatorManager.ValidatorStakeData\",\"name\":\"validatorStakeData\",\"type\":\"tuple\"},{\"internalType\":\"uint64\",\"name\":\"depositAmountGwei\",\"type\":\"uint64\"}],\"name\":\"stakeEth\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"}],\"name\":\"supportsAsset\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newGovernor\",\"type\":\"address\"}],\"name\":\"transferGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"transferToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unPause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"validator\",\"outputs\":[{\"internalType\":\"enum CompoundingValidatorManager.ValidatorState\",\"name\":\"state\",\"type\":\"uint8\"},{\"internalType\":\"uint40\",\"name\":\"index\",\"type\":\"uint40\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"validatorRegistrator\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"publicKey\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"amountGwei\",\"type\":\"uint64\"}],\"name\":\"validatorWithdrawal\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"vaultAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"verifiedValidators\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"verifiedValidatorsLength\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"balancesContainerRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"balancesContainerProof\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"validatorBalanceLeaves\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes[]\",\"name\":\"validatorBalanceProofs\",\"type\":\"bytes[]\"}],\"internalType\":\"struct CompoundingValidatorManager.BalanceProofs\",\"name\":\"balanceProofs\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"pendingDepositContainerRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"pendingDepositContainerProof\",\"type\":\"bytes\"},{\"internalType\":\"uint32[]\",\"name\":\"pendingDepositIndexes\",\"type\":\"uint32[]\"},{\"internalType\":\"bytes[]\",\"name\":\"pendingDepositProofs\",\"type\":\"bytes[]\"}],\"internalType\":\"struct CompoundingValidatorManager.PendingDepositProofs\",\"name\":\"pendingDepositProofs\",\"type\":\"tuple\"}],\"name\":\"verifyBalances\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"pendingDepositRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"depositProcessedSlot\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"slot\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"proof\",\"type\":\"bytes\"}],\"internalType\":\"struct CompoundingValidatorManager.FirstPendingDepositSlotProofData\",\"name\":\"firstPendingDeposit\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"withdrawableEpoch\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"withdrawableEpochProof\",\"type\":\"bytes\"}],\"internalType\":\"struct CompoundingValidatorManager.StrategyValidatorProofData\",\"name\":\"strategyValidatorData\",\"type\":\"tuple\"}],\"name\":\"verifyDeposit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"nextBlockTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint40\",\"name\":\"validatorIndex\",\"type\":\"uint40\"},{\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"withdrawalCredentials\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"validatorPubKeyProof\",\"type\":\"bytes\"}],\"name\":\"verifyValidator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_recipient\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"withdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64[]\",\"name\":\"operatorIds\",\"type\":\"uint64[]\"},{\"internalType\":\"uint256\",\"name\":\"ssvAmount\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"validatorCount\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"networkFeeIndex\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"index\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"internalType\":\"struct Cluster\",\"name\":\"cluster\",\"type\":\"tuple\"}],\"name\":\"withdrawSSV\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}],\"devdoc\":{\"author\":\"Origin Protocol Inc\",\"events\":{\"Paused(address)\":{\"details\":\"Emitted when the pause is triggered by `account`.\"},\"Unpaused(address)\":{\"details\":\"Emitted when the pause is lifted by `account`.\"}},\"kind\":\"dev\",\"methods\":{\"checkBalance(address)\":{\"params\":{\"_asset\":\"Address of WETH asset.\"},\"returns\":{\"balance\":\" Total value in ETH\"}},\"constructor\":{\"params\":{\"_baseConfig\":\"Base strategy config with `platformAddress` not used so empty address `vaultAddress` the address of the OETH Vault contract\",\"_beaconChainDepositContract\":\"Address of the beacon chain deposit contract\",\"_beaconGenesisTimestamp\":\"The timestamp of the Beacon chain's genesis.\",\"_beaconProofs\":\"Address of the Beacon Proofs contract that verifies beacon chain data\",\"_ssvNetwork\":\"Address of the SSV Network contract\",\"_ssvToken\":\"Address of the SSV Token contract\",\"_wethAddress\":\"Address of the WETH Token contract\"}},\"deposit(address,uint256)\":{\"params\":{\"_amount\":\"Amount of WETH that was transferred to the strategy by the vault.\",\"_asset\":\"Address of the WETH token.\"}},\"getRewardTokenAddresses()\":{\"returns\":{\"_0\":\"address[] the reward token addresses.\"}},\"initialize(address[],address[],address[])\":{\"params\":{\"_assets\":\"Not used so empty array\",\"_pTokens\":\"Not used so empty array\",\"_rewardTokenAddresses\":\"Not used so empty array\"}},\"paused()\":{\"details\":\"Returns true if the contract is paused, and false otherwise.\"},\"registerSsvValidator(bytes,uint64[],bytes,uint256,(uint32,uint64,uint64,bool,uint256))\":{\"params\":{\"cluster\":\"The SSV cluster details including the validator count and SSV balance\",\"operatorIds\":\"The operator IDs of the SSV Cluster\",\"publicKey\":\"The public key of the validator\",\"sharesData\":\"The shares data for the validator\",\"ssvAmount\":\"The amount of SSV tokens to be deposited to the SSV cluster\"}},\"removeSsvValidator(bytes,uint64[],(uint32,uint64,uint64,bool,uint256))\":{\"params\":{\"cluster\":\"The SSV cluster details including the validator count and SSV balance\",\"operatorIds\":\"The operator IDs of the SSV Cluster\",\"publicKey\":\"The public key of the validator\"}},\"setHarvesterAddress(address)\":{\"params\":{\"_harvesterAddress\":\"Address of the harvester contract.\"}},\"setRewardTokenAddresses(address[])\":{\"params\":{\"_rewardTokenAddresses\":\"Array of reward token addresses\"}},\"stakeEth((bytes,bytes,bytes32),uint64)\":{\"params\":{\"depositAmountGwei\":\"The amount of WETH to stake to the validator in Gwei.\",\"validatorStakeData\":\"validator data needed to stake. The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot. Only the registrator can call this function.\"}},\"supportsAsset(address)\":{\"params\":{\"_asset\":\"The address of the WETH token.\"}},\"transferGovernance(address)\":{\"params\":{\"_newGovernor\":\"Address of the new Governor\"}},\"transferToken(address,uint256)\":{\"params\":{\"_amount\":\"Amount of the asset to transfer\",\"_asset\":\"Address for the asset\"}},\"validatorWithdrawal(bytes,uint64)\":{\"params\":{\"amountGwei\":\"The amount of ETH to be withdrawn from the validator in Gwei. A zero amount will trigger a full withdrawal.\",\"publicKey\":\"The public key of the validator\"}},\"verifyBalances((bytes32,bytes,bytes32[],bytes[]),(bytes32,bytes,uint32[],bytes[]))\":{\"params\":{\"balanceProofs\":\"a `BalanceProofs` struct containing the following: - balancesContainerRoot: The merkle root of the balances container - balancesContainerProof: The merkle proof for the balances container to the beacon block root. This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node. - validatorBalanceLeaves: Array of leaf nodes containing the validator balance with three other balances. - validatorBalanceProofs: Array of merkle proofs for the validator balance to the Balances container root. This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\",\"pendingDepositProofs\":\"a `PendingDepositProofs` struct containing the following: - pendingDepositContainerRoot: The merkle root of the pending deposits list container - pendingDepositContainerProof: The merkle proof from the pending deposits list container to the beacon block root. This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node. - pendingDepositIndexes: Array of indexes in the pending deposits list container for each of the strategy's deposits. - pendingDepositProofs: Array of merkle proofs for each strategy deposit in the beacon chain's pending deposit list container to the pending deposits list container root. These are 28 witness hashes of 32 bytes each concatenated together starting from the leaf node.\"}},\"verifyDeposit(bytes32,uint64,(uint64,bytes),(uint64,bytes))\":{\"params\":{\"depositProcessedSlot\":\"Any slot on or after the strategy's deposit was processed on the beacon chain. Can not be a slot with pending deposits with the same slot as the deposit being verified. Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root set for the next block timestamp in 12 seconds time.\",\"firstPendingDeposit\":\"a `FirstPendingDepositSlotProofData` struct containing: - slot: The beacon chain slot of the first deposit in the beacon chain's deposit queue. Can be any non-zero value if the deposit queue is empty. - proof: The merkle proof of the first pending deposit's slot to the beacon block root. Can be either: * 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty. * 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty. The 32 byte witness hashes are concatenated together starting from the leaf node.\",\"pendingDepositRoot\":\"The unique identifier of the deposit emitted in `ETHStaked` from the `stakeEth` function.\",\"strategyValidatorData\":\"a `StrategyValidatorProofData` struct containing: - withdrawableEpoch: The withdrawable epoch of the validator the strategy is depositing to. - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the validator the strategy is depositing to, to the beacon block root. This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\"}},\"verifyValidator(uint64,uint40,bytes32,bytes32,bytes)\":{\"params\":{\"nextBlockTimestamp\":\"The timestamp of the execution layer block after the beacon chain slot we are verifying. The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp, which is the beacon block root of the previous block.\",\"pubKeyHash\":\"The hash of the validator's public key using the Beacon Chain's format\",\"validatorIndex\":\"The index of the validator on the beacon chain.\",\"validatorPubKeyProof\":\"The merkle proof for the validator public key to the beacon block root. This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node. BeaconBlock.state.validators[validatorIndex].pubkey\",\"withdrawalCredentials\":\"contain the validator type and withdrawal address. These can be incorrect and/or malformed. In case of incorrect withdrawalCredentials the validator deposit has been front run\"}},\"withdraw(address,address,uint256)\":{\"params\":{\"_amount\":\"Amount of WETH to withdraw.\",\"_asset\":\"Address of the WETH token.\",\"_recipient\":\"Address to receive withdrawn assets.\"}},\"withdrawSSV(uint64[],uint256,(uint32,uint64,uint64,bool,uint256))\":{\"details\":\"A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\",\"params\":{\"cluster\":\"The SSV cluster details including the validator count and SSV balance\",\"operatorIds\":\"The operator IDs of the SSV Cluster\",\"ssvAmount\":\"The amount of SSV tokens to be withdrawn from the SSV cluster\"}}},\"title\":\"Compounding Staking SSV Strategy\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"BEACON_PROOFS()\":{\"notice\":\"Address of the Beacon Proofs contract that verifies beacon chain data\"},\"SSV_NETWORK()\":{\"notice\":\"The address of the SSV Network contract used to interface with\"},\"SSV_TOKEN()\":{\"notice\":\"SSV ERC20 token that serves as a payment for operating SSV validators\"},\"assetToPToken(address)\":{\"notice\":\"asset => pToken (Platform Specific Token Address)\"},\"checkBalance(address)\":{\"notice\":\"Accounts for all the assets managed by this strategy which includes: 1. The current WETH in this strategy contract 2. The last verified ETH balance, total deposits and total validator balances\"},\"claimGovernance()\":{\"notice\":\"Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor.\"},\"collectRewardTokens()\":{\"notice\":\"Collect accumulated reward token and send to Vault.\"},\"deposit(address,uint256)\":{\"notice\":\"Unlike other strategies, this does not deposit assets into the underlying platform. It just checks the asset is WETH and emits the Deposit event. To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\"},\"depositAll()\":{\"notice\":\"Unlike other strategies, this does not deposit assets into the underlying platform. It just emits the Deposit event. To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\"},\"depositList(uint256)\":{\"notice\":\"List of strategy deposit IDs to a validator. The ID is the merkle root of the pending deposit data which is unique for each validator, amount and block. Duplicate pending deposit roots are prevented so can be used as an identifier to each strategy deposit. The list can be for deposits waiting to be verified as processed on the beacon chain, or deposits that have been verified to an exiting validator and is now waiting for the validator's balance to be swept. The list may not be ordered by time of deposit. Removed deposits will move the last deposit to the removed index.\"},\"depositListLength()\":{\"notice\":\"Returns the number of deposits waiting to be verified as processed on the beacon chain, or deposits that have been verified to an exiting validator and is now waiting for the validator's balance to be swept.\"},\"deposits(bytes32)\":{\"notice\":\"Mapping of the pending deposit roots to the deposit data\"},\"firstDeposit()\":{\"notice\":\"Restricts to only one deposit to an unverified validator at a time. This is to limit front-running attacks of deposits to the beacon chain contract.\"},\"getRewardTokenAddresses()\":{\"notice\":\"Get the reward token addresses.\"},\"governor()\":{\"notice\":\"Returns the address of the current Governor.\"},\"harvesterAddress()\":{\"notice\":\"Address of the Harvester contract allowed to collect reward tokens\"},\"initialize(address[],address[],address[])\":{\"notice\":\"Set up initial internal state including 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\"},\"isGovernor()\":{\"notice\":\"Returns true if the caller is the current Governor.\"},\"lastVerifiedEthBalance()\":{\"notice\":\"The last verified ETH balance of the strategy\"},\"platformAddress()\":{\"notice\":\"Address of the underlying platform\"},\"registerSsvValidator(bytes,uint64[],bytes,uint256,(uint32,uint64,uint64,bool,uint256))\":{\"notice\":\"Registers a single validator in a SSV Cluster. Only the Registrator can call this function.\"},\"removePToken(uint256)\":{\"notice\":\"is not supported for this strategy as there is no platform token.\"},\"removeSsvValidator(bytes,uint64[],(uint32,uint64,uint64,bool,uint256))\":{\"notice\":\"Remove the validator from the SSV Cluster after: - the validator has been exited from `validatorWithdrawal` or slashed - the validator has incorrectly registered and can not be staked to - the initial deposit was front-run and the withdrawal address is not this strategy's address. Make sure `validatorWithdrawal` is called with a zero amount and the validator has exited the Beacon chain. If removed before the validator has exited the beacon chain will result in the validator being slashed. Only the registrator can call this function.\"},\"resetFirstDeposit()\":{\"notice\":\"Reset the `firstDeposit` flag to false so deposits to unverified validators can be made again.\"},\"rewardTokenAddresses(uint256)\":{\"notice\":\"Address of the reward tokens. eg CRV, BAL, CVX, AURA\"},\"safeApproveAllTokens()\":{\"notice\":\"Approves the SSV Network contract to transfer SSV tokens for validator registration.\"},\"setHarvesterAddress(address)\":{\"notice\":\"Set the Harvester contract that can collect rewards.\"},\"setPTokenAddress(address,address)\":{\"notice\":\"is not supported for this strategy as there is no platform token.\"},\"setRegistrator(address)\":{\"notice\":\"Set the address of the registrator which can register, exit and remove validators\"},\"setRewardTokenAddresses(address[])\":{\"notice\":\"Set the reward token addresses. Any old addresses will be overwritten.\"},\"snapBalances()\":{\"notice\":\"Stores the current ETH balance at the current block and beacon block root of the slot that is associated with the previous block. When snapping / verifying balance it is of a high importance that there is no miss-match in respect to ETH that is held by the contract and balances that are verified on the validators. First some context on the beacon-chain block building behaviour. Relevant parts of constructing a block on the beacon chain consist of: - process_withdrawals: ETH is deducted from the validator's balance - process_execution_payload: immediately after the previous step executing all the transactions - apply the withdrawals: adding ETH to the recipient which is the withdrawal address contained in the withdrawal credentials of the exited validators That means that balance increases which are part of the post-block execution state are done within the block, but the transaction that are contained within that block can not see / interact with the balance from the exited validators. Only transactions in the next block can do that. When snap balances is performed the state of the chain is snapped across 2 separate chain states: - ETH balance of the contract is recorded on block X -> and corresponding slot Y - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1 given there were no missed slots. It could also be Y - 2, Y - 3 depending on how many slots have not managed to propose a block. For the sake of simplicity this slot will be referred to as Y - 1 as it makes no difference in the argument Given these 2 separate chain states it is paramount that verify balances can not experience miss-counting ETH or much more dangerous double counting of the ETH. When verifyBalances is called it is performed on the current block Z where Z > X. Verify balances adds up all the ETH (omitting WETH) controlled by this contract: - ETH balance in the contract on block X - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1 - ETH balance in validators that are active in slot Y - 1 - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner) and have their balance visible to transactions in slot Y and corresponding block X (or sooner) Lets verify the correctness of ETH accounting given the above described behaviour. *ETH balance in the contract on block X* This is an ETH balance of the contract on a non current X block. Any ETH leaving the contract as a result of a withdrawal subtracts from the ETH accounted for on block X if `verifyBalances` has already been called. It also invalidates a `snapBalances` in case `verifyBalances` has not been called yet. Not performing this would result in not accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z]. Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH accounted for since the last `verifyBalances` has been called. And it invalidates the `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this would result in double counting the `stakedEth` since it would be present once in the snapped contract balance and the second time in deposit storage variables. This behaviour is correct. *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1* The contract sums up all the ETH that has been deposited to the Beacon chain deposit contract at block Z. The execution layer doesn't have direct access to the state of deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be deposited it needs to be sure to not double count ETH that is in deposits (storage vars) and could also be part of the validator balances. It does that by verifying that at slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since the last snap till now all are still in queue. Which ensures they can not be part of the validator balances in later steps. This behaviour is correct. *ETH balance in validators that are active in slot Y - 1* The contract is verifying none of the deposits on Y - 1 slot have been processed and for that reason it checks the validator balances in the same slot. Ensuring accounting correctness. This behaviour is correct. *The withdrawn validators* The withdrawn validators could have their balances deducted in any slot before slot Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets look at the \\\"worst case scenario\\\" where the validator withdrawal is processed in the slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot Y -> block X. The ETH balance on the contract is snapped at block X meaning that even if the validator exits at the latest possible time it is paramount that the ETH balance on the execution layer is recorded in the next block. Correctly accounting for the withdrawn ETH. Worth mentioning if the validator exit is processed by the slot Y and balance increase seen on the execution layer on block X + 1 the withdrawal is ignored by both the validator balance verification as well as execution layer contract balance snap. This behaviour is correct. The validator balances on the beacon chain can then be proved with `verifyBalances`.\"},\"snappedBalance()\":{\"notice\":\"Mapping of the block root to the balances at that slot\"},\"stakeEth((bytes,bytes,bytes32),uint64)\":{\"notice\":\"Stakes WETH in this strategy to a compounding validator. The the first deposit to a new validator, the amount must be 1 ETH. Another deposit of at least 31 ETH is required for the validator to be activated. This second deposit has to be done after the validator has been verified. Does not convert any ETH sitting in this strategy to WETH. There can not be two deposits to the same validator in the same block for the same amount. Function is pausable so in case a run-away Registrator can be prevented from continuing to deposit funds to slashed or undesired validators.\"},\"supportsAsset(address)\":{\"notice\":\"Returns bool indicating whether asset is supported by the strategy.\"},\"transferGovernance(address)\":{\"notice\":\"Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete\"},\"transferToken(address,uint256)\":{\"notice\":\"Transfer token to governor. Intended for recovering tokens stuck in strategy contracts, i.e. mistaken sends.\"},\"validator(bytes32)\":{\"notice\":\"Mapping of the hash of the validator's public key to the validator state and index. Uses the Beacon chain hashing for BLSPubkey which is sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\"},\"validatorRegistrator()\":{\"notice\":\"Address of the registrator - allowed to register, withdraw, exit and remove validators\"},\"validatorWithdrawal(bytes,uint64)\":{\"notice\":\"Request a full or partial withdrawal from a validator. A zero amount will trigger a full withdrawal. If the remaining balance is < 32 ETH then only the amount in excess of 32 ETH will be withdrawn. Only the Registrator can call this function. 1 wei of value should be sent with the tx to pay for the withdrawal request fee. If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any. If no ETH balance, the tx will revert.\"},\"vaultAddress()\":{\"notice\":\"Address of the OToken vault\"},\"verifiedValidators(uint256)\":{\"notice\":\"List of validator public key hashes that have been verified to exist on the beacon chain. These have had a deposit processed and the validator's balance increased. Validators will be removed from this list when its verified they have a zero balance.\"},\"verifiedValidatorsLength()\":{\"notice\":\"Returns the number of verified validators.\"},\"verifyBalances((bytes32,bytes,bytes32[],bytes[]),(bytes32,bytes,uint32[],bytes[]))\":{\"notice\":\"Verifies the balances of all active validators on the beacon chain and checks each of the strategy's deposits are still to be processed by the beacon chain.\"},\"verifyDeposit(bytes32,uint64,(uint64,bytes),(uint64,bytes))\":{\"notice\":\"Verifies a deposit on the execution layer has been processed by the beacon chain. This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance. Important: this function has a limitation where `depositProcessedSlot` that is passed by the off-chain verifier requires a slot immediately after it to propose a block otherwise the `BeaconRoots.parentBlockRoot` will fail. This shouldn't be a problem, since by the current behaviour of beacon chain only 1%-3% slots don't propose a block.\"},\"verifyValidator(uint64,uint40,bytes32,bytes32,bytes)\":{\"notice\":\"Verifies a validator's index to its public key. Adds to the list of verified validators if the validator's withdrawal address is this strategy's address. Marks the validator as invalid and removes the deposit if the withdrawal address is not this strategy's address.\"},\"withdraw(address,address,uint256)\":{\"notice\":\"Withdraw ETH and WETH from this strategy contract.\"},\"withdrawAll()\":{\"notice\":\"Transfer all WETH deposits, ETH from validator withdrawals and ETH from execution rewards in this strategy to the vault. This does not withdraw from the validators. That has to be done separately with the `validatorWithdrawal` operation.\"},\"withdrawSSV(uint64[],uint256,(uint32,uint64,uint64,bool,uint256))\":{\"notice\":\"Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\"}},\"notice\":\"Strategy to deploy funds into DVT validators powered by the SSV Network\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol\":\"CompoundingStakingSSVStrategy\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/security/Pausable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../utils/Context.sol\\\";\\n\\n/**\\n * @dev Contract module which allows children to implement an emergency stop\\n * mechanism that can be triggered by an authorized account.\\n *\\n * This module is used through inheritance. It will make available the\\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\\n * the functions of your contract. Note that they will not be pausable by\\n * simply including this module, only once the modifiers are put in place.\\n */\\nabstract contract Pausable is Context {\\n /**\\n * @dev Emitted when the pause is triggered by `account`.\\n */\\n event Paused(address account);\\n\\n /**\\n * @dev Emitted when the pause is lifted by `account`.\\n */\\n event Unpaused(address account);\\n\\n bool private _paused;\\n\\n /**\\n * @dev Initializes the contract in unpaused state.\\n */\\n constructor() {\\n _paused = false;\\n }\\n\\n /**\\n * @dev Returns true if the contract is paused, and false otherwise.\\n */\\n function paused() public view virtual returns (bool) {\\n return _paused;\\n }\\n\\n /**\\n * @dev Modifier to make a function callable only when the contract is not paused.\\n *\\n * Requirements:\\n *\\n * - The contract must not be paused.\\n */\\n modifier whenNotPaused() {\\n require(!paused(), \\\"Pausable: paused\\\");\\n _;\\n }\\n\\n /**\\n * @dev Modifier to make a function callable only when the contract is paused.\\n *\\n * Requirements:\\n *\\n * - The contract must be paused.\\n */\\n modifier whenPaused() {\\n require(paused(), \\\"Pausable: not paused\\\");\\n _;\\n }\\n\\n /**\\n * @dev Triggers stopped state.\\n *\\n * Requirements:\\n *\\n * - The contract must not be paused.\\n */\\n function _pause() internal virtual whenNotPaused {\\n _paused = true;\\n emit Paused(_msgSender());\\n }\\n\\n /**\\n * @dev Returns to normal state.\\n *\\n * Requirements:\\n *\\n * - The contract must be paused.\\n */\\n function _unpause() internal virtual whenPaused {\\n _paused = false;\\n emit Unpaused(_msgSender());\\n }\\n}\\n\",\"keccak256\":\"0xe68ed7fb8766ed1e888291f881e36b616037f852b37d96877045319ad298ba87\",\"license\":\"MIT\"},\"@openzeppelin/contracts/token/ERC20/IERC20.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Interface of the ERC20 standard as defined in the EIP.\\n */\\ninterface IERC20 {\\n /**\\n * @dev Returns the amount of tokens in existence.\\n */\\n function totalSupply() external view returns (uint256);\\n\\n /**\\n * @dev Returns the amount of tokens owned by `account`.\\n */\\n function balanceOf(address account) external view returns (uint256);\\n\\n /**\\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transfer(address recipient, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Returns the remaining number of tokens that `spender` will be\\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\\n * zero by default.\\n *\\n * This value changes when {approve} or {transferFrom} are called.\\n */\\n function allowance(address owner, address spender) external view returns (uint256);\\n\\n /**\\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\\n * that someone may use both the old and the new allowance by unfortunate\\n * transaction ordering. One possible solution to mitigate this race\\n * condition is to first reduce the spender's allowance to 0 and set the\\n * desired value afterwards:\\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\\n *\\n * Emits an {Approval} event.\\n */\\n function approve(address spender, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\\n * allowance mechanism. `amount` is then deducted from the caller's\\n * allowance.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transferFrom(\\n address sender,\\n address recipient,\\n uint256 amount\\n ) external returns (bool);\\n\\n /**\\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\\n * another (`to`).\\n *\\n * Note that `value` may be zero.\\n */\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n\\n /**\\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n * a call to {approve}. `value` is the new allowance.\\n */\\n event Approval(address indexed owner, address indexed spender, uint256 value);\\n}\\n\",\"keccak256\":\"0x61437cb513a887a1bbad006e7b1c8b414478427d33de47c5600af3c748f108da\",\"license\":\"MIT\"},\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../IERC20.sol\\\";\\nimport \\\"../../../utils/Address.sol\\\";\\n\\n/**\\n * @title SafeERC20\\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\\n * contract returns false). Tokens that return no value (and instead revert or\\n * throw on failure) are also supported, non-reverting calls are assumed to be\\n * successful.\\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\\n */\\nlibrary SafeERC20 {\\n using Address for address;\\n\\n function safeTransfer(\\n IERC20 token,\\n address to,\\n uint256 value\\n ) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\\n }\\n\\n function safeTransferFrom(\\n IERC20 token,\\n address from,\\n address to,\\n uint256 value\\n ) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\\n }\\n\\n /**\\n * @dev Deprecated. This function has issues similar to the ones found in\\n * {IERC20-approve}, and its usage is discouraged.\\n *\\n * Whenever possible, use {safeIncreaseAllowance} and\\n * {safeDecreaseAllowance} instead.\\n */\\n function safeApprove(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n // safeApprove should only be called when setting an initial allowance,\\n // or when resetting it to zero. To increase and decrease it, use\\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\\n require(\\n (value == 0) || (token.allowance(address(this), spender) == 0),\\n \\\"SafeERC20: approve from non-zero to non-zero allowance\\\"\\n );\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\\n }\\n\\n function safeIncreaseAllowance(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n uint256 newAllowance = token.allowance(address(this), spender) + value;\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\\n }\\n\\n function safeDecreaseAllowance(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n unchecked {\\n uint256 oldAllowance = token.allowance(address(this), spender);\\n require(oldAllowance >= value, \\\"SafeERC20: decreased allowance below zero\\\");\\n uint256 newAllowance = oldAllowance - value;\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\\n }\\n }\\n\\n /**\\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\\n * on the return value: the return value is optional (but if data is returned, it must not be false).\\n * @param token The token targeted by the call.\\n * @param data The call data (encoded using abi.encode or one of its variants).\\n */\\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\\n // the target address contains contract code and also asserts for success in the low-level call.\\n\\n bytes memory returndata = address(token).functionCall(data, \\\"SafeERC20: low-level call failed\\\");\\n if (returndata.length > 0) {\\n // Return data is optional\\n require(abi.decode(returndata, (bool)), \\\"SafeERC20: ERC20 operation did not succeed\\\");\\n }\\n }\\n}\\n\",\"keccak256\":\"0xc3d946432c0ddbb1f846a0d3985be71299df331b91d06732152117f62f0be2b5\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize, which returns 0 for contracts in\\n // construction, since the code is only stored at the end of the\\n // constructor execution.\\n\\n uint256 size;\\n assembly {\\n size := extcodesize(account)\\n }\\n return size > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCall(target, data, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n require(isContract(target), \\\"Address: static call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(isContract(target), \\\"Address: delegate call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x51b758a8815ecc9596c66c37d56b1d33883a444631a3f916b9fe65cb863ef7c4\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Context.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Provides information about the current execution context, including the\\n * sender of the transaction and its data. While these are generally available\\n * via msg.sender and msg.data, they should not be accessed in such a direct\\n * manner, since when dealing with meta-transactions the account sending and\\n * paying for execution may not be the actual sender (as far as an application\\n * is concerned).\\n *\\n * This contract is only required for intermediate, library-like contracts.\\n */\\nabstract contract Context {\\n function _msgSender() internal view virtual returns (address) {\\n return msg.sender;\\n }\\n\\n function _msgData() internal view virtual returns (bytes calldata) {\\n return msg.data;\\n }\\n}\\n\",\"keccak256\":\"0xe2e337e6dde9ef6b680e07338c493ebea1b5fd09b43424112868e9cc1706bca7\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/math/Math.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Standard math utilities missing in the Solidity language.\\n */\\nlibrary Math {\\n /**\\n * @dev Returns the largest of two numbers.\\n */\\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a >= b ? a : b;\\n }\\n\\n /**\\n * @dev Returns the smallest of two numbers.\\n */\\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a < b ? a : b;\\n }\\n\\n /**\\n * @dev Returns the average of two numbers. The result is rounded towards\\n * zero.\\n */\\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\\n // (a + b) / 2 can overflow.\\n return (a & b) + (a ^ b) / 2;\\n }\\n\\n /**\\n * @dev Returns the ceiling of the division of two numbers.\\n *\\n * This differs from standard division with `/` in that it rounds up instead\\n * of rounding down.\\n */\\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\\n // (a + b - 1) / b can overflow on addition, so we distribute.\\n return a / b + (a % b == 0 ? 0 : 1);\\n }\\n}\\n\",\"keccak256\":\"0xfaad496c1c944b6259b7dc70b4865eb1775d6402bc0c81b38a0b24d9f525ae37\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/math/SafeCast.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\\n * checks.\\n *\\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\\n * easily result in undesired exploitation or bugs, since developers usually\\n * assume that overflows raise errors. `SafeCast` restores this intuition by\\n * reverting the transaction when such an operation overflows.\\n *\\n * Using this library instead of the unchecked operations eliminates an entire\\n * class of bugs, so it's recommended to use it always.\\n *\\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\\n * all math on `uint256` and `int256` and then downcasting.\\n */\\nlibrary SafeCast {\\n /**\\n * @dev Returns the downcasted uint224 from uint256, reverting on\\n * overflow (when the input is greater than largest uint224).\\n *\\n * Counterpart to Solidity's `uint224` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 224 bits\\n */\\n function toUint224(uint256 value) internal pure returns (uint224) {\\n require(value <= type(uint224).max, \\\"SafeCast: value doesn't fit in 224 bits\\\");\\n return uint224(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint128 from uint256, reverting on\\n * overflow (when the input is greater than largest uint128).\\n *\\n * Counterpart to Solidity's `uint128` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 128 bits\\n */\\n function toUint128(uint256 value) internal pure returns (uint128) {\\n require(value <= type(uint128).max, \\\"SafeCast: value doesn't fit in 128 bits\\\");\\n return uint128(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint96 from uint256, reverting on\\n * overflow (when the input is greater than largest uint96).\\n *\\n * Counterpart to Solidity's `uint96` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 96 bits\\n */\\n function toUint96(uint256 value) internal pure returns (uint96) {\\n require(value <= type(uint96).max, \\\"SafeCast: value doesn't fit in 96 bits\\\");\\n return uint96(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint64 from uint256, reverting on\\n * overflow (when the input is greater than largest uint64).\\n *\\n * Counterpart to Solidity's `uint64` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 64 bits\\n */\\n function toUint64(uint256 value) internal pure returns (uint64) {\\n require(value <= type(uint64).max, \\\"SafeCast: value doesn't fit in 64 bits\\\");\\n return uint64(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint32 from uint256, reverting on\\n * overflow (when the input is greater than largest uint32).\\n *\\n * Counterpart to Solidity's `uint32` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 32 bits\\n */\\n function toUint32(uint256 value) internal pure returns (uint32) {\\n require(value <= type(uint32).max, \\\"SafeCast: value doesn't fit in 32 bits\\\");\\n return uint32(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint16 from uint256, reverting on\\n * overflow (when the input is greater than largest uint16).\\n *\\n * Counterpart to Solidity's `uint16` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 16 bits\\n */\\n function toUint16(uint256 value) internal pure returns (uint16) {\\n require(value <= type(uint16).max, \\\"SafeCast: value doesn't fit in 16 bits\\\");\\n return uint16(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint8 from uint256, reverting on\\n * overflow (when the input is greater than largest uint8).\\n *\\n * Counterpart to Solidity's `uint8` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 8 bits.\\n */\\n function toUint8(uint256 value) internal pure returns (uint8) {\\n require(value <= type(uint8).max, \\\"SafeCast: value doesn't fit in 8 bits\\\");\\n return uint8(value);\\n }\\n\\n /**\\n * @dev Converts a signed int256 into an unsigned uint256.\\n *\\n * Requirements:\\n *\\n * - input must be greater than or equal to 0.\\n */\\n function toUint256(int256 value) internal pure returns (uint256) {\\n require(value >= 0, \\\"SafeCast: value must be positive\\\");\\n return uint256(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int128 from int256, reverting on\\n * overflow (when the input is less than smallest int128 or\\n * greater than largest int128).\\n *\\n * Counterpart to Solidity's `int128` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 128 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt128(int256 value) internal pure returns (int128) {\\n require(value >= type(int128).min && value <= type(int128).max, \\\"SafeCast: value doesn't fit in 128 bits\\\");\\n return int128(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int64 from int256, reverting on\\n * overflow (when the input is less than smallest int64 or\\n * greater than largest int64).\\n *\\n * Counterpart to Solidity's `int64` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 64 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt64(int256 value) internal pure returns (int64) {\\n require(value >= type(int64).min && value <= type(int64).max, \\\"SafeCast: value doesn't fit in 64 bits\\\");\\n return int64(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int32 from int256, reverting on\\n * overflow (when the input is less than smallest int32 or\\n * greater than largest int32).\\n *\\n * Counterpart to Solidity's `int32` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 32 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt32(int256 value) internal pure returns (int32) {\\n require(value >= type(int32).min && value <= type(int32).max, \\\"SafeCast: value doesn't fit in 32 bits\\\");\\n return int32(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int16 from int256, reverting on\\n * overflow (when the input is less than smallest int16 or\\n * greater than largest int16).\\n *\\n * Counterpart to Solidity's `int16` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 16 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt16(int256 value) internal pure returns (int16) {\\n require(value >= type(int16).min && value <= type(int16).max, \\\"SafeCast: value doesn't fit in 16 bits\\\");\\n return int16(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int8 from int256, reverting on\\n * overflow (when the input is less than smallest int8 or\\n * greater than largest int8).\\n *\\n * Counterpart to Solidity's `int8` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 8 bits.\\n *\\n * _Available since v3.1._\\n */\\n function toInt8(int256 value) internal pure returns (int8) {\\n require(value >= type(int8).min && value <= type(int8).max, \\\"SafeCast: value doesn't fit in 8 bits\\\");\\n return int8(value);\\n }\\n\\n /**\\n * @dev Converts an unsigned uint256 into a signed int256.\\n *\\n * Requirements:\\n *\\n * - input must be less than or equal to maxInt256.\\n */\\n function toInt256(uint256 value) internal pure returns (int256) {\\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\\n require(value <= uint256(type(int256).max), \\\"SafeCast: value doesn't fit in an int256\\\");\\n return int256(value);\\n }\\n}\\n\",\"keccak256\":\"0x5c6caab697d302ad7eb59c234a4d2dbc965c1bae87709bd2850060b7695b28c7\",\"license\":\"MIT\"},\"contracts/beacon/BeaconRoots.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Library to retrieve beacon block roots.\\n * @author Origin Protocol Inc\\n */\\nlibrary BeaconRoots {\\n /// @notice The address of beacon block roots oracle\\n /// See https://eips.ethereum.org/EIPS/eip-4788\\n address internal constant BEACON_ROOTS_ADDRESS =\\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\\n\\n /// @notice Returns the beacon block root for the previous block.\\n /// This comes from the Beacon Roots contract defined in EIP-4788.\\n /// This will revert if the block is more than 8,191 blocks old as\\n /// that is the size of the beacon root's ring buffer.\\n /// @param timestamp The timestamp of the block for which to get the parent root.\\n /// @return parentRoot The parent block root for the given timestamp.\\n function parentBlockRoot(uint64 timestamp)\\n internal\\n view\\n returns (bytes32 parentRoot)\\n {\\n // Call the Beacon Roots contract to get the parent block root.\\n // This does not have a function signature, so we use a staticcall.\\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\\n abi.encode(timestamp)\\n );\\n\\n require(success && result.length > 0, \\\"Invalid beacon timestamp\\\");\\n parentRoot = abi.decode(result, (bytes32));\\n }\\n}\\n\",\"keccak256\":\"0x4005989f852a68bbcdc1cdc3472ebd3911395e75b4e6366ffcaae4d1c128691e\",\"license\":\"BUSL-1.1\"},\"contracts/beacon/PartialWithdrawal.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Library to request full or partial withdrawals from validators on the beacon chain.\\n * @author Origin Protocol Inc\\n */\\nlibrary PartialWithdrawal {\\n /// @notice The address where the withdrawal request is sent to\\n /// See https://eips.ethereum.org/EIPS/eip-7002\\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\\n\\n /// @notice Requests a partial withdrawal for a given validator public key and amount.\\n /// @param validatorPubKey The public key of the validator to withdraw from\\n /// @param amount The amount of ETH to withdraw\\n function request(bytes calldata validatorPubKey, uint64 amount)\\n internal\\n returns (uint256 fee_)\\n {\\n require(validatorPubKey.length == 48, \\\"Invalid validator byte length\\\");\\n fee_ = fee();\\n\\n // Call the Withdrawal Request contract with the validator public key\\n // and amount to be withdrawn packed together\\n\\n // This is a general purpose EL to CL request:\\n // https://eips.ethereum.org/EIPS/eip-7685\\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\\n abi.encodePacked(validatorPubKey, amount)\\n );\\n\\n require(success, \\\"Withdrawal request failed\\\");\\n }\\n\\n /// @notice Gets fee for withdrawal requests contract on Beacon chain\\n function fee() internal view returns (uint256) {\\n // Get fee from the withdrawal request contract\\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\\n .staticcall(\\\"\\\");\\n\\n require(success && result.length > 0, \\\"Failed to get fee\\\");\\n return abi.decode(result, (uint256));\\n }\\n}\\n\",\"keccak256\":\"0x80d29153ff7eb5c6841692aca98eb0cc14ac43ad2d8e402890b6c6b6e4a9719d\",\"license\":\"BUSL-1.1\"},\"contracts/governance/Governable.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\\n * from owner to governor and renounce methods removed. Does not use\\n * Context.sol like Ownable.sol does for simplification.\\n * @author Origin Protocol Inc\\n */\\nabstract contract Governable {\\n // Storage position of the owner and pendingOwner of the contract\\n // keccak256(\\\"OUSD.governor\\\");\\n bytes32 private constant governorPosition =\\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\\n\\n // keccak256(\\\"OUSD.pending.governor\\\");\\n bytes32 private constant pendingGovernorPosition =\\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\\n\\n // keccak256(\\\"OUSD.reentry.status\\\");\\n bytes32 private constant reentryStatusPosition =\\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\\n\\n // See OpenZeppelin ReentrancyGuard implementation\\n uint256 constant _NOT_ENTERED = 1;\\n uint256 constant _ENTERED = 2;\\n\\n event PendingGovernorshipTransfer(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n event GovernorshipTransferred(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n /**\\n * @notice Returns the address of the current Governor.\\n */\\n function governor() public view returns (address) {\\n return _governor();\\n }\\n\\n /**\\n * @dev Returns the address of the current Governor.\\n */\\n function _governor() internal view returns (address governorOut) {\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n governorOut := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Returns the address of the pending Governor.\\n */\\n function _pendingGovernor()\\n internal\\n view\\n returns (address pendingGovernor)\\n {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n pendingGovernor := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the Governor.\\n */\\n modifier onlyGovernor() {\\n require(isGovernor(), \\\"Caller is not the Governor\\\");\\n _;\\n }\\n\\n /**\\n * @notice Returns true if the caller is the current Governor.\\n */\\n function isGovernor() public view returns (bool) {\\n return msg.sender == _governor();\\n }\\n\\n function _setGovernor(address newGovernor) internal {\\n emit GovernorshipTransferred(_governor(), newGovernor);\\n\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @dev Prevents a contract from calling itself, directly or indirectly.\\n * Calling a `nonReentrant` function from another `nonReentrant`\\n * function is not supported. It is possible to prevent this from happening\\n * by making the `nonReentrant` function external, and make it call a\\n * `private` function that does the actual work.\\n */\\n modifier nonReentrant() {\\n bytes32 position = reentryStatusPosition;\\n uint256 _reentry_status;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n _reentry_status := sload(position)\\n }\\n\\n // On the first call to nonReentrant, _notEntered will be true\\n require(_reentry_status != _ENTERED, \\\"Reentrant call\\\");\\n\\n // Any calls to nonReentrant after this point will fail\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _ENTERED)\\n }\\n\\n _;\\n\\n // By storing the original value once again, a refund is triggered (see\\n // https://eips.ethereum.org/EIPS/eip-2200)\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _NOT_ENTERED)\\n }\\n }\\n\\n function _setPendingGovernor(address newGovernor) internal {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the current Governor. Must be claimed for this to complete\\n * @param _newGovernor Address of the new Governor\\n */\\n function transferGovernance(address _newGovernor) external onlyGovernor {\\n _setPendingGovernor(_newGovernor);\\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\\n }\\n\\n /**\\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the new Governor.\\n */\\n function claimGovernance() external {\\n require(\\n msg.sender == _pendingGovernor(),\\n \\\"Only the pending Governor can complete the claim\\\"\\n );\\n _changeGovernor(msg.sender);\\n }\\n\\n /**\\n * @dev Change Governance of the contract to a new account (`newGovernor`).\\n * @param _newGovernor Address of the new Governor\\n */\\n function _changeGovernor(address _newGovernor) internal {\\n require(_newGovernor != address(0), \\\"New Governor is address(0)\\\");\\n _setGovernor(_newGovernor);\\n }\\n}\\n\",\"keccak256\":\"0xf32f873c8bfbacf2e5f01d0cf37bc7f54fbd5aa656e95c8a599114229946f107\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IBasicToken.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\ninterface IBasicToken {\\n function symbol() external view returns (string memory);\\n\\n function decimals() external view returns (uint8);\\n}\\n\",\"keccak256\":\"0xa562062698aa12572123b36dfd2072f1a39e44fed2031cc19c2c9fd522f96ec2\",\"license\":\"MIT\"},\"contracts/interfaces/IBeaconProofs.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\ninterface IBeaconProofs {\\n function verifyValidator(\\n bytes32 beaconBlockRoot,\\n bytes32 pubKeyHash,\\n bytes calldata validatorPubKeyProof,\\n uint40 validatorIndex,\\n bytes32 withdrawalCredentials\\n ) external view;\\n\\n function verifyValidatorWithdrawable(\\n bytes32 beaconBlockRoot,\\n uint40 validatorIndex,\\n uint64 withdrawableEpoch,\\n bytes calldata withdrawableEpochProof\\n ) external view;\\n\\n function verifyBalancesContainer(\\n bytes32 beaconBlockRoot,\\n bytes32 balancesContainerLeaf,\\n bytes calldata balancesContainerProof\\n ) external view;\\n\\n function verifyValidatorBalance(\\n bytes32 balancesContainerRoot,\\n bytes32 validatorBalanceLeaf,\\n bytes calldata balanceProof,\\n uint40 validatorIndex\\n ) external view returns (uint256 validatorBalance);\\n\\n function verifyPendingDepositsContainer(\\n bytes32 beaconBlockRoot,\\n bytes32 pendingDepositsContainerRoot,\\n bytes calldata proof\\n ) external view;\\n\\n function verifyPendingDeposit(\\n bytes32 pendingDepositsContainerRoot,\\n bytes32 pendingDepositRoot,\\n bytes calldata proof,\\n uint32 pendingDepositIndex\\n ) external view;\\n\\n function verifyFirstPendingDeposit(\\n bytes32 beaconBlockRoot,\\n uint64 slot,\\n bytes calldata firstPendingDepositSlotProof\\n ) external view returns (bool isEmptyDepositQueue);\\n\\n function merkleizePendingDeposit(\\n bytes32 pubKeyHash,\\n bytes calldata withdrawalCredentials,\\n uint64 amountGwei,\\n bytes calldata signature,\\n uint64 slot\\n ) external pure returns (bytes32 root);\\n\\n function merkleizeSignature(bytes calldata signature)\\n external\\n pure\\n returns (bytes32 root);\\n}\\n\",\"keccak256\":\"0xbc611f259b296451c358638a6e164227179bcee079656f5078800cc2f06c22fd\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IDepositContract.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\ninterface IDepositContract {\\n /// @notice A processed deposit event.\\n event DepositEvent(\\n bytes pubkey,\\n bytes withdrawal_credentials,\\n bytes amount,\\n bytes signature,\\n bytes index\\n );\\n\\n /// @notice Submit a Phase 0 DepositData object.\\n /// @param pubkey A BLS12-381 public key.\\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\\n /// @param signature A BLS12-381 signature.\\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\\n /// Used as a protection against malformed input.\\n function deposit(\\n bytes calldata pubkey,\\n bytes calldata withdrawal_credentials,\\n bytes calldata signature,\\n bytes32 deposit_data_root\\n ) external payable;\\n\\n /// @notice Query the current deposit root hash.\\n /// @return The deposit root hash.\\n function get_deposit_root() external view returns (bytes32);\\n\\n /// @notice Query the current deposit count.\\n /// @return The deposit count encoded as a little endian 64-bit number.\\n function get_deposit_count() external view returns (bytes memory);\\n}\\n\",\"keccak256\":\"0x598f90bdbc854250bbd5991426bfb43367207e64e33109c41aa8b54323fd8d8e\",\"license\":\"MIT\"},\"contracts/interfaces/ISSVNetwork.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\nstruct Cluster {\\n uint32 validatorCount;\\n uint64 networkFeeIndex;\\n uint64 index;\\n bool active;\\n uint256 balance;\\n}\\n\\ninterface ISSVNetwork {\\n /**********/\\n /* Errors */\\n /**********/\\n\\n error CallerNotOwner(); // 0x5cd83192\\n error CallerNotWhitelisted(); // 0x8c6e5d71\\n error FeeTooLow(); // 0x732f9413\\n error FeeExceedsIncreaseLimit(); // 0x958065d9\\n error NoFeeDeclared(); // 0x1d226c30\\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\\n error OperatorDoesNotExist(); // 0x961e3e8c\\n error InsufficientBalance(); // 0xf4d678b8\\n error ValidatorDoesNotExist(); // 0xe51315d2\\n error ClusterNotLiquidatable(); // 0x60300a8d\\n error InvalidPublicKeyLength(); // 0x637297a4\\n error InvalidOperatorIdsLength(); // 0x38186224\\n error ClusterAlreadyEnabled(); // 0x3babafd2\\n error ClusterIsLiquidated(); // 0x95a0cf33\\n error ClusterDoesNotExists(); // 0x185e2b16\\n error IncorrectClusterState(); // 0x12e04c87\\n error UnsortedOperatorsList(); // 0xdd020e25\\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\\n error ExceedValidatorLimit(); // 0x6df5ab76\\n error TokenTransferFailed(); // 0x045c4b02\\n error SameFeeChangeNotAllowed(); // 0xc81272f8\\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\\n error NotAuthorized(); // 0xea8e4eb5\\n error OperatorsListNotUnique(); // 0xa5a1ff5d\\n error OperatorAlreadyExists(); // 0x289c9494\\n error TargetModuleDoesNotExist(); // 0x8f9195fb\\n error MaxValueExceeded(); // 0x91aa3017\\n error FeeTooHigh(); // 0xcd4e6167\\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\\n error EmptyPublicKeysList(); // df83e679\\n\\n // legacy errors\\n error ValidatorAlreadyExists(); // 0x8d09a73e\\n error IncorrectValidatorState(); // 0x2feda3c1\\n\\n event AdminChanged(address previousAdmin, address newAdmin);\\n event BeaconUpgraded(address indexed beacon);\\n event ClusterDeposited(\\n address indexed owner,\\n uint64[] operatorIds,\\n uint256 value,\\n Cluster cluster\\n );\\n event ClusterLiquidated(\\n address indexed owner,\\n uint64[] operatorIds,\\n Cluster cluster\\n );\\n event ClusterReactivated(\\n address indexed owner,\\n uint64[] operatorIds,\\n Cluster cluster\\n );\\n event ClusterWithdrawn(\\n address indexed owner,\\n uint64[] operatorIds,\\n uint256 value,\\n Cluster cluster\\n );\\n event DeclareOperatorFeePeriodUpdated(uint64 value);\\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\\n event FeeRecipientAddressUpdated(\\n address indexed owner,\\n address recipientAddress\\n );\\n event Initialized(uint8 version);\\n event LiquidationThresholdPeriodUpdated(uint64 value);\\n event MinimumLiquidationCollateralUpdated(uint256 value);\\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\\n event OperatorAdded(\\n uint64 indexed operatorId,\\n address indexed owner,\\n bytes publicKey,\\n uint256 fee\\n );\\n event OperatorFeeDeclarationCancelled(\\n address indexed owner,\\n uint64 indexed operatorId\\n );\\n event OperatorFeeDeclared(\\n address indexed owner,\\n uint64 indexed operatorId,\\n uint256 blockNumber,\\n uint256 fee\\n );\\n event OperatorFeeExecuted(\\n address indexed owner,\\n uint64 indexed operatorId,\\n uint256 blockNumber,\\n uint256 fee\\n );\\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\\n event OperatorMaximumFeeUpdated(uint64 maxFee);\\n event OperatorRemoved(uint64 indexed operatorId);\\n event OperatorWhitelistUpdated(\\n uint64 indexed operatorId,\\n address whitelisted\\n );\\n event OperatorWithdrawn(\\n address indexed owner,\\n uint64 indexed operatorId,\\n uint256 value\\n );\\n event OwnershipTransferStarted(\\n address indexed previousOwner,\\n address indexed newOwner\\n );\\n event OwnershipTransferred(\\n address indexed previousOwner,\\n address indexed newOwner\\n );\\n event Upgraded(address indexed implementation);\\n event ValidatorAdded(\\n address indexed owner,\\n uint64[] operatorIds,\\n bytes publicKey,\\n bytes shares,\\n Cluster cluster\\n );\\n event ValidatorExited(\\n address indexed owner,\\n uint64[] operatorIds,\\n bytes publicKey\\n );\\n event ValidatorRemoved(\\n address indexed owner,\\n uint64[] operatorIds,\\n bytes publicKey,\\n Cluster cluster\\n );\\n\\n fallback() external;\\n\\n function acceptOwnership() external;\\n\\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\\n\\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\\n\\n function deposit(\\n address clusterOwner,\\n uint64[] memory operatorIds,\\n uint256 amount,\\n Cluster memory cluster\\n ) external;\\n\\n function executeOperatorFee(uint64 operatorId) external;\\n\\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\\n external;\\n\\n function bulkExitValidator(\\n bytes[] calldata publicKeys,\\n uint64[] calldata operatorIds\\n ) external;\\n\\n function getVersion() external pure returns (string memory version);\\n\\n function initialize(\\n address token_,\\n address ssvOperators_,\\n address ssvClusters_,\\n address ssvDAO_,\\n address ssvViews_,\\n uint64 minimumBlocksBeforeLiquidation_,\\n uint256 minimumLiquidationCollateral_,\\n uint32 validatorsPerOperatorLimit_,\\n uint64 declareOperatorFeePeriod_,\\n uint64 executeOperatorFeePeriod_,\\n uint64 operatorMaxFeeIncrease_\\n ) external;\\n\\n function liquidate(\\n address clusterOwner,\\n uint64[] memory operatorIds,\\n Cluster memory cluster\\n ) external;\\n\\n function owner() external view returns (address);\\n\\n function pendingOwner() external view returns (address);\\n\\n function proxiableUUID() external view returns (bytes32);\\n\\n function reactivate(\\n uint64[] memory operatorIds,\\n uint256 amount,\\n Cluster memory cluster\\n ) external;\\n\\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\\n\\n function registerOperator(bytes memory publicKey, uint256 fee)\\n external\\n returns (uint64 id);\\n\\n function registerValidator(\\n bytes memory publicKey,\\n uint64[] memory operatorIds,\\n bytes memory sharesData,\\n uint256 amount,\\n Cluster memory cluster\\n ) external;\\n\\n function bulkRegisterValidator(\\n bytes[] calldata publicKeys,\\n uint64[] calldata operatorIds,\\n bytes[] calldata sharesData,\\n uint256 amount,\\n Cluster memory cluster\\n ) external;\\n\\n function removeOperator(uint64 operatorId) external;\\n\\n function removeValidator(\\n bytes memory publicKey,\\n uint64[] memory operatorIds,\\n Cluster memory cluster\\n ) external;\\n\\n function bulkRemoveValidator(\\n bytes[] calldata publicKeys,\\n uint64[] calldata operatorIds,\\n Cluster memory cluster\\n ) external;\\n\\n function renounceOwnership() external;\\n\\n function setFeeRecipientAddress(address recipientAddress) external;\\n\\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\\n external;\\n\\n function transferOwnership(address newOwner) external;\\n\\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\\n\\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\\n\\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\\n\\n function updateMaximumOperatorFee(uint64 maxFee) external;\\n\\n function updateMinimumLiquidationCollateral(uint256 amount) external;\\n\\n function updateModule(uint8 moduleId, address moduleAddress) external;\\n\\n function updateNetworkFee(uint256 fee) external;\\n\\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\\n\\n function upgradeTo(address newImplementation) external;\\n\\n function upgradeToAndCall(address newImplementation, bytes memory data)\\n external\\n payable;\\n\\n function withdraw(\\n uint64[] memory operatorIds,\\n uint256 amount,\\n Cluster memory cluster\\n ) external;\\n\\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\\n\\n function withdrawNetworkEarnings(uint256 amount) external;\\n\\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\\n external;\\n}\\n\",\"keccak256\":\"0xbd86cb74702aebc5b53c8fc738a2e3ad1b410583460617be84b22ce922af12a7\",\"license\":\"MIT\"},\"contracts/interfaces/IStrategy.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\\n */\\ninterface IStrategy {\\n /**\\n * @dev Deposit the given asset to platform\\n * @param _asset asset address\\n * @param _amount Amount to deposit\\n */\\n function deposit(address _asset, uint256 _amount) external;\\n\\n /**\\n * @dev Deposit the entire balance of all supported assets in the Strategy\\n * to the platform\\n */\\n function depositAll() external;\\n\\n /**\\n * @dev Withdraw given asset from Lending platform\\n */\\n function withdraw(\\n address _recipient,\\n address _asset,\\n uint256 _amount\\n ) external;\\n\\n /**\\n * @dev Liquidate all assets in strategy and return them to Vault.\\n */\\n function withdrawAll() external;\\n\\n /**\\n * @dev Returns the current balance of the given asset.\\n */\\n function checkBalance(address _asset)\\n external\\n view\\n returns (uint256 balance);\\n\\n /**\\n * @dev Returns bool indicating whether strategy supports asset.\\n */\\n function supportsAsset(address _asset) external view returns (bool);\\n\\n /**\\n * @dev Collect reward tokens from the Strategy.\\n */\\n function collectRewardTokens() external;\\n\\n /**\\n * @dev The address array of the reward tokens for the Strategy.\\n */\\n function getRewardTokenAddresses() external view returns (address[] memory);\\n\\n function harvesterAddress() external view returns (address);\\n\\n function transferToken(address token, uint256 amount) external;\\n\\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\\n external;\\n}\\n\",\"keccak256\":\"0x79ca47defb3b5a56bba13f14c440838152fd1c1aa640476154516a16da4da8ba\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IVault.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { VaultStorage } from \\\"../vault/VaultStorage.sol\\\";\\n\\ninterface IVault {\\n // slither-disable-start constable-states\\n\\n event AssetSupported(address _asset);\\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\\n event StrategyApproved(address _addr);\\n event StrategyRemoved(address _addr);\\n event Mint(address _addr, uint256 _value);\\n event Redeem(address _addr, uint256 _value);\\n event CapitalPaused();\\n event CapitalUnpaused();\\n event RebasePaused();\\n event RebaseUnpaused();\\n event VaultBufferUpdated(uint256 _vaultBuffer);\\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\\n event PriceProviderUpdated(address _priceProvider);\\n event AllocateThresholdUpdated(uint256 _threshold);\\n event RebaseThresholdUpdated(uint256 _threshold);\\n event StrategistUpdated(address _address);\\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\\n event TrusteeFeeBpsChanged(uint256 _basis);\\n event TrusteeAddressChanged(address _address);\\n event SwapperChanged(address _address);\\n event SwapAllowedUndervalueChanged(uint256 _basis);\\n event SwapSlippageChanged(address _asset, uint256 _basis);\\n event Swapped(\\n address indexed _fromAsset,\\n address indexed _toAsset,\\n uint256 _fromAssetAmount,\\n uint256 _toAssetAmount\\n );\\n event StrategyAddedToMintWhitelist(address indexed strategy);\\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\\n event DripperChanged(address indexed _dripper);\\n event WithdrawalRequested(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount,\\n uint256 _queued\\n );\\n event WithdrawalClaimed(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount\\n );\\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\\n\\n // Governable.sol\\n function transferGovernance(address _newGovernor) external;\\n\\n function claimGovernance() external;\\n\\n function governor() external view returns (address);\\n\\n function ADMIN_IMPLEMENTATION() external view returns (address);\\n\\n // VaultAdmin.sol\\n function setPriceProvider(address _priceProvider) external;\\n\\n function priceProvider() external view returns (address);\\n\\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\\n\\n function redeemFeeBps() external view returns (uint256);\\n\\n function setVaultBuffer(uint256 _vaultBuffer) external;\\n\\n function vaultBuffer() external view returns (uint256);\\n\\n function setAutoAllocateThreshold(uint256 _threshold) external;\\n\\n function autoAllocateThreshold() external view returns (uint256);\\n\\n function setRebaseThreshold(uint256 _threshold) external;\\n\\n function rebaseThreshold() external view returns (uint256);\\n\\n function setStrategistAddr(address _address) external;\\n\\n function strategistAddr() external view returns (address);\\n\\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\\n\\n function maxSupplyDiff() external view returns (uint256);\\n\\n function setTrusteeAddress(address _address) external;\\n\\n function trusteeAddress() external view returns (address);\\n\\n function setTrusteeFeeBps(uint256 _basis) external;\\n\\n function trusteeFeeBps() external view returns (uint256);\\n\\n function ousdMetaStrategy() external view returns (address);\\n\\n function setSwapper(address _swapperAddr) external;\\n\\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\\n\\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\\n external;\\n\\n function supportAsset(address _asset, uint8 _unitConversion) external;\\n\\n function approveStrategy(address _addr) external;\\n\\n function removeStrategy(address _addr) external;\\n\\n function setAssetDefaultStrategy(address _asset, address _strategy)\\n external;\\n\\n function assetDefaultStrategies(address _asset)\\n external\\n view\\n returns (address);\\n\\n function pauseRebase() external;\\n\\n function unpauseRebase() external;\\n\\n function rebasePaused() external view returns (bool);\\n\\n function pauseCapital() external;\\n\\n function unpauseCapital() external;\\n\\n function capitalPaused() external view returns (bool);\\n\\n function transferToken(address _asset, uint256 _amount) external;\\n\\n function priceUnitMint(address asset) external view returns (uint256);\\n\\n function priceUnitRedeem(address asset) external view returns (uint256);\\n\\n function withdrawAllFromStrategy(address _strategyAddr) external;\\n\\n function withdrawAllFromStrategies() external;\\n\\n function withdrawFromStrategy(\\n address _strategyFromAddress,\\n address[] calldata _assets,\\n uint256[] calldata _amounts\\n ) external;\\n\\n function depositToStrategy(\\n address _strategyToAddress,\\n address[] calldata _assets,\\n uint256[] calldata _amounts\\n ) external;\\n\\n // VaultCore.sol\\n function mint(\\n address _asset,\\n uint256 _amount,\\n uint256 _minimumOusdAmount\\n ) external;\\n\\n function mintForStrategy(uint256 _amount) external;\\n\\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\\n\\n function burnForStrategy(uint256 _amount) external;\\n\\n function allocate() external;\\n\\n function rebase() external;\\n\\n function swapCollateral(\\n address fromAsset,\\n address toAsset,\\n uint256 fromAssetAmount,\\n uint256 minToAssetAmount,\\n bytes calldata data\\n ) external returns (uint256 toAssetAmount);\\n\\n function totalValue() external view returns (uint256 value);\\n\\n function checkBalance(address _asset) external view returns (uint256);\\n\\n function calculateRedeemOutputs(uint256 _amount)\\n external\\n view\\n returns (uint256[] memory);\\n\\n function getAssetCount() external view returns (uint256);\\n\\n function getAssetConfig(address _asset)\\n external\\n view\\n returns (VaultStorage.Asset memory config);\\n\\n function getAllAssets() external view returns (address[] memory);\\n\\n function getStrategyCount() external view returns (uint256);\\n\\n function swapper() external view returns (address);\\n\\n function allowedSwapUndervalue() external view returns (uint256);\\n\\n function getAllStrategies() external view returns (address[] memory);\\n\\n function isSupportedAsset(address _asset) external view returns (bool);\\n\\n function netOusdMintForStrategyThreshold() external view returns (uint256);\\n\\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\\n\\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\\n\\n function netOusdMintedForStrategy() external view returns (int256);\\n\\n function setDripper(address _dripper) external;\\n\\n function dripper() external view returns (address);\\n\\n function weth() external view returns (address);\\n\\n function cacheWETHAssetIndex() external;\\n\\n function wethAssetIndex() external view returns (uint256);\\n\\n function initialize(address, address) external;\\n\\n function setAdminImpl(address) external;\\n\\n function removeAsset(address _asset) external;\\n\\n // These are OETH specific functions\\n function addWithdrawalQueueLiquidity() external;\\n\\n function requestWithdrawal(uint256 _amount)\\n external\\n returns (uint256 requestId, uint256 queued);\\n\\n function claimWithdrawal(uint256 requestId)\\n external\\n returns (uint256 amount);\\n\\n function claimWithdrawals(uint256[] memory requestIds)\\n external\\n returns (uint256[] memory amounts, uint256 totalAmount);\\n\\n function withdrawalQueueMetadata()\\n external\\n view\\n returns (VaultStorage.WithdrawalQueueMetadata memory);\\n\\n function withdrawalRequests(uint256 requestId)\\n external\\n view\\n returns (VaultStorage.WithdrawalRequest memory);\\n\\n // OETHb specific functions\\n function addStrategyToMintWhitelist(address strategyAddr) external;\\n\\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\\n\\n function isMintWhitelistedStrategy(address strategyAddr)\\n external\\n view\\n returns (bool);\\n\\n function withdrawalClaimDelay() external view returns (uint256);\\n\\n function setWithdrawalClaimDelay(uint256 newDelay) external;\\n\\n function lastRebase() external view returns (uint64);\\n\\n function dripDuration() external view returns (uint64);\\n\\n function setDripDuration(uint256 _dripDuration) external;\\n\\n function rebasePerSecondMax() external view returns (uint64);\\n\\n function setRebaseRateMax(uint256 yearlyApr) external;\\n\\n function rebasePerSecondTarget() external view returns (uint64);\\n\\n function previewYield() external view returns (uint256 yield);\\n\\n // slither-disable-end constable-states\\n}\\n\",\"keccak256\":\"0x8d0a60f594d97578b0513b4da3d8fcafaa601950c6c4c016bf60b1344733269c\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IWETH9.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\ninterface IWETH9 {\\n event Approval(address indexed src, address indexed guy, uint256 wad);\\n event Deposit(address indexed dst, uint256 wad);\\n event Transfer(address indexed src, address indexed dst, uint256 wad);\\n event Withdrawal(address indexed src, uint256 wad);\\n\\n function allowance(address, address) external view returns (uint256);\\n\\n function approve(address guy, uint256 wad) external returns (bool);\\n\\n function balanceOf(address) external view returns (uint256);\\n\\n function decimals() external view returns (uint8);\\n\\n function deposit() external payable;\\n\\n function name() external view returns (string memory);\\n\\n function symbol() external view returns (string memory);\\n\\n function totalSupply() external view returns (uint256);\\n\\n function transfer(address dst, uint256 wad) external returns (bool);\\n\\n function transferFrom(\\n address src,\\n address dst,\\n uint256 wad\\n ) external returns (bool);\\n\\n function withdraw(uint256 wad) external;\\n}\\n\",\"keccak256\":\"0x05b7dce6c24d3cd4e48b5c6346d86e5e40ecc3291bcdf3f3ef091c98fc826519\",\"license\":\"MIT\"},\"contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { IERC20 } from \\\"@openzeppelin/contracts/token/ERC20/IERC20.sol\\\";\\n\\nimport { InitializableAbstractStrategy } from \\\"../../utils/InitializableAbstractStrategy.sol\\\";\\nimport { IWETH9 } from \\\"../../interfaces/IWETH9.sol\\\";\\nimport { CompoundingValidatorManager } from \\\"./CompoundingValidatorManager.sol\\\";\\n\\n/// @title Compounding Staking SSV Strategy\\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\\n/// @author Origin Protocol Inc\\ncontract CompoundingStakingSSVStrategy is\\n CompoundingValidatorManager,\\n InitializableAbstractStrategy\\n{\\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\\n address public immutable SSV_TOKEN;\\n\\n // For future use\\n uint256[50] private __gap;\\n\\n /// @param _baseConfig Base strategy config with\\n /// `platformAddress` not used so empty address\\n /// `vaultAddress` the address of the OETH Vault contract\\n /// @param _wethAddress Address of the WETH Token contract\\n /// @param _ssvToken Address of the SSV Token contract\\n /// @param _ssvNetwork Address of the SSV Network contract\\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\\n constructor(\\n BaseStrategyConfig memory _baseConfig,\\n address _wethAddress,\\n address _ssvToken,\\n address _ssvNetwork,\\n address _beaconChainDepositContract,\\n address _beaconProofs,\\n uint64 _beaconGenesisTimestamp\\n )\\n InitializableAbstractStrategy(_baseConfig)\\n CompoundingValidatorManager(\\n _wethAddress,\\n _baseConfig.vaultAddress,\\n _beaconChainDepositContract,\\n _ssvNetwork,\\n _beaconProofs,\\n _beaconGenesisTimestamp\\n )\\n {\\n SSV_TOKEN = _ssvToken;\\n\\n // Make sure nobody owns the implementation contract\\n _setGovernor(address(0));\\n }\\n\\n /// @notice Set up initial internal state including\\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\\n /// @param _rewardTokenAddresses Not used so empty array\\n /// @param _assets Not used so empty array\\n /// @param _pTokens Not used so empty array\\n function initialize(\\n address[] memory _rewardTokenAddresses,\\n address[] memory _assets,\\n address[] memory _pTokens\\n ) external onlyGovernor initializer {\\n InitializableAbstractStrategy._initialize(\\n _rewardTokenAddresses,\\n _assets,\\n _pTokens\\n );\\n\\n safeApproveAllTokens();\\n }\\n\\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\\n /// It just checks the asset is WETH and emits the Deposit event.\\n /// To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\\n /// @param _asset Address of the WETH token.\\n /// @param _amount Amount of WETH that was transferred to the strategy by the vault.\\n function deposit(address _asset, uint256 _amount)\\n external\\n override\\n onlyVault\\n nonReentrant\\n {\\n require(_asset == WETH, \\\"Unsupported asset\\\");\\n require(_amount > 0, \\\"Must deposit something\\\");\\n\\n // Account for the new WETH\\n depositedWethAccountedFor += _amount;\\n\\n emit Deposit(_asset, address(0), _amount);\\n }\\n\\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\\n /// It just emits the Deposit event.\\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\\n function depositAll() external override onlyVault nonReentrant {\\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\\n\\n if (newWeth > 0) {\\n // Account for the new WETH\\n depositedWethAccountedFor = wethBalance;\\n\\n emit Deposit(WETH, address(0), newWeth);\\n }\\n }\\n\\n /// @notice Withdraw ETH and WETH from this strategy contract.\\n /// @param _recipient Address to receive withdrawn assets.\\n /// @param _asset Address of the WETH token.\\n /// @param _amount Amount of WETH to withdraw.\\n function withdraw(\\n address _recipient,\\n address _asset,\\n uint256 _amount\\n ) external override onlyVault nonReentrant {\\n require(_asset == WETH, \\\"Unsupported asset\\\");\\n\\n _withdraw(_recipient, _amount, address(this).balance);\\n }\\n\\n function _withdraw(\\n address _recipient,\\n uint256 _withdrawAmount,\\n uint256 _ethBalance\\n ) internal {\\n require(_withdrawAmount > 0, \\\"Must withdraw something\\\");\\n require(_recipient != address(0), \\\"Must specify recipient\\\");\\n\\n // Convert any ETH from validator partial withdrawals, exits\\n // or execution rewards to WETH and do the necessary accounting.\\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\\n\\n // Transfer WETH to the recipient and do the necessary accounting.\\n _transferWeth(_withdrawAmount, _recipient);\\n\\n emit Withdrawal(WETH, address(0), _withdrawAmount);\\n }\\n\\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\\n /// execution rewards in this strategy to the vault.\\n /// This does not withdraw from the validators. That has to be done separately with the\\n /// `validatorWithdrawal` operation.\\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\\n uint256 ethBalance = address(this).balance;\\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\\n ethBalance;\\n\\n if (withdrawAmount > 0) {\\n _withdraw(vaultAddress, withdrawAmount, ethBalance);\\n }\\n }\\n\\n /// @notice Accounts for all the assets managed by this strategy which includes:\\n /// 1. The current WETH in this strategy contract\\n /// 2. The last verified ETH balance, total deposits and total validator balances\\n /// @param _asset Address of WETH asset.\\n /// @return balance Total value in ETH\\n function checkBalance(address _asset)\\n external\\n view\\n override\\n returns (uint256 balance)\\n {\\n require(_asset == WETH, \\\"Unsupported asset\\\");\\n\\n // Load the last verified balance from the storage\\n // and add to the latest WETH balance of this strategy.\\n balance =\\n lastVerifiedEthBalance +\\n IWETH9(WETH).balanceOf(address(this));\\n }\\n\\n /// @notice Returns bool indicating whether asset is supported by the strategy.\\n /// @param _asset The address of the WETH token.\\n function supportsAsset(address _asset) public view override returns (bool) {\\n return _asset == WETH;\\n }\\n\\n /// @notice Approves the SSV Network contract to transfer SSV tokens for validator registration.\\n function safeApproveAllTokens() public override {\\n // Approves the SSV Network contract to transfer SSV tokens when validators are registered\\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\\n }\\n\\n /**\\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\\n * like it did in the legacy NativeStakingStrategy.\\n * The new ETH will be accounted for in `checkBalance` after the next snapBalances and verifyBalances txs.\\n */\\n receive() external payable {}\\n\\n /***************************************\\n Internal functions\\n ****************************************/\\n\\n /// @notice is not supported for this strategy as there is no platform token.\\n function setPTokenAddress(address, address) external pure override {\\n revert(\\\"Unsupported function\\\");\\n }\\n\\n /// @notice is not supported for this strategy as there is no platform token.\\n function removePToken(uint256) external pure override {\\n revert(\\\"Unsupported function\\\");\\n }\\n\\n /// @dev This strategy does not use a platform token like the old Aave and Compound strategies.\\n function _abstractSetPToken(address _asset, address) internal override {}\\n\\n /// @dev Consensus rewards are compounded to the validator's balance instead of being\\n /// swept to this strategy contract.\\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract.\\n /// Withdrawals from validators also accumulate as ETH in this strategy contract.\\n /// It's too complex to separate the rewards from withdrawals so this function is not implemented.\\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can now regulate\\n /// the increase in assets.\\n function _collectRewardTokens() internal pure override {\\n revert(\\\"Unsupported function\\\");\\n }\\n}\\n\",\"keccak256\":\"0x1c54018f3c60ed6c0a3a9c5c2c6e6ca4c29ffd4434e96fab0cb7f3d06e3bd649\",\"license\":\"BUSL-1.1\"},\"contracts/strategies/NativeStaking/CompoundingValidatorManager.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { SafeERC20 } from \\\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\\\";\\nimport { SafeCast } from \\\"@openzeppelin/contracts/utils/math/SafeCast.sol\\\";\\nimport { Math } from \\\"@openzeppelin/contracts/utils/math/Math.sol\\\";\\nimport { IERC20 } from \\\"@openzeppelin/contracts/token/ERC20/IERC20.sol\\\";\\nimport { Pausable } from \\\"@openzeppelin/contracts/security/Pausable.sol\\\";\\nimport { Governable } from \\\"../../governance/Governable.sol\\\";\\nimport { IDepositContract } from \\\"../../interfaces/IDepositContract.sol\\\";\\nimport { IWETH9 } from \\\"../../interfaces/IWETH9.sol\\\";\\nimport { ISSVNetwork, Cluster } from \\\"../../interfaces/ISSVNetwork.sol\\\";\\nimport { BeaconRoots } from \\\"../../beacon/BeaconRoots.sol\\\";\\nimport { PartialWithdrawal } from \\\"../../beacon/PartialWithdrawal.sol\\\";\\nimport { IBeaconProofs } from \\\"../../interfaces/IBeaconProofs.sol\\\";\\n\\n/**\\n * @title Validator lifecycle management contract\\n * @notice This contract implements all the required functionality to\\n * register, deposit, withdraw, exit and remove validators.\\n * @author Origin Protocol Inc\\n */\\nabstract contract CompoundingValidatorManager is Governable, Pausable {\\n using SafeERC20 for IERC20;\\n\\n /// @dev The amount of ETH in wei that is required for a deposit to a new validator.\\n uint256 internal constant DEPOSIT_AMOUNT_WEI = 1 ether;\\n /// @dev Validator balances over this amount will eventually become active on the beacon chain.\\n /// Due to hysteresis, if the effective balance is 31 ETH, the actual balance\\n /// must rise to 32.25 ETH to trigger an effective balance update to 32 ETH.\\n /// https://eth2book.info/capella/part2/incentives/balances/#hysteresis\\n uint256 internal constant MIN_ACTIVATION_BALANCE_GWEI = 32.25 ether / 1e9;\\n /// @dev The maximum number of deposits that are waiting to be verified as processed on the beacon chain.\\n uint256 internal constant MAX_DEPOSITS = 12;\\n /// @dev The maximum number of validators that can be verified.\\n uint256 internal constant MAX_VERIFIED_VALIDATORS = 48;\\n /// @dev The default withdrawable epoch value on the Beacon chain.\\n /// A value in the far future means the validator is not exiting.\\n uint64 internal constant FAR_FUTURE_EPOCH = type(uint64).max;\\n /// @dev The number of seconds between each beacon chain slot.\\n uint64 internal constant SLOT_DURATION = 12;\\n /// @dev The number of slots in each beacon chain epoch.\\n uint64 internal constant SLOTS_PER_EPOCH = 32;\\n /// @dev Minimum time in seconds to allow snapped balances to be verified.\\n /// Set to 35 slots which is 3 slots more than 1 epoch (32 slots). Deposits get processed\\n /// once per epoch. This larger than 1 epoch delay should achieve that `snapBalances` sometimes\\n /// get called in the middle (or towards the end) of the epoch. Giving the off-chain script\\n /// sufficient time after the end of the epoch to prepare the proofs and call `verifyBalances`.\\n /// This is considering a malicious actor would keep calling `snapBalances` as frequent as possible\\n /// to disturb our operations.\\n uint64 internal constant SNAP_BALANCES_DELAY = 35 * SLOT_DURATION;\\n\\n /// @notice The address of the Wrapped ETH (WETH) token contract\\n address internal immutable WETH;\\n /// @notice The address of the beacon chain deposit contract\\n address internal immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\\n /// @notice The address of the SSV Network contract used to interface with\\n address public immutable SSV_NETWORK;\\n /// @notice Address of the OETH Vault proxy contract\\n address internal immutable VAULT_ADDRESS;\\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\\n address public immutable BEACON_PROOFS;\\n /// @notice The timestamp of the Beacon chain genesis.\\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\\n uint64 internal immutable BEACON_GENESIS_TIMESTAMP;\\n\\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\\n address public validatorRegistrator;\\n\\n /// @notice Deposit data for new compounding validators.\\n /// @dev A `VERIFIED` deposit can mean 3 separate things:\\n /// - a deposit has been processed by the beacon chain and shall be included in the\\n /// balance of the next verifyBalances call\\n /// - a deposit has been done to a slashed validator and has probably been recovered\\n /// back to this strategy. Probably because we can not know for certain. This contract\\n /// only detects when the validator has passed its withdrawal epoch. It is close to impossible\\n /// to prove with Merkle Proofs that the postponed deposit this contract is responsible for\\n /// creating is not present anymore in BeaconChain.state.pending_deposits. This in effect\\n /// means that there might be a period where this contract thinks the deposit has been already\\n /// returned as ETH balance before it happens. This will result in some days (or weeks)\\n /// -> depending on the size of deposit queue of showing a deficit when calling `checkBalance`.\\n /// As this only offsets the yield and doesn't cause a critical double-counting we are not addressing\\n /// this issue.\\n /// - A deposit has been done to the validator, but our deposit has been front run by a malicious\\n /// actor. Funds in the deposit this contract makes are not recoverable.\\n enum DepositStatus {\\n UNKNOWN, // default value\\n PENDING, // deposit is pending and waiting to be verified\\n VERIFIED // deposit has been verified\\n }\\n\\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\\n /// @param amountGwei Amount of ETH in gwei that has been deposited to the beacon chain deposit contract\\n /// @param slot The beacon chain slot number when the deposit has been made\\n /// @param depositIndex The index of the deposit in the list of active deposits\\n /// @param status The status of the deposit, either UNKNOWN, PENDING or VERIFIED\\n struct DepositData {\\n bytes32 pubKeyHash;\\n uint64 amountGwei;\\n uint64 slot;\\n uint32 depositIndex;\\n DepositStatus status;\\n }\\n /// @notice Restricts to only one deposit to an unverified validator at a time.\\n /// This is to limit front-running attacks of deposits to the beacon chain contract.\\n ///\\n /// @dev The value is set to true when a deposit to a new validator has been done that has\\n /// not yet be verified.\\n bool public firstDeposit;\\n /// @notice Mapping of the pending deposit roots to the deposit data\\n mapping(bytes32 => DepositData) public deposits;\\n /// @notice List of strategy deposit IDs to a validator.\\n /// The ID is the merkle root of the pending deposit data which is unique for each validator, amount and block.\\n /// Duplicate pending deposit roots are prevented so can be used as an identifier to each strategy deposit.\\n /// The list can be for deposits waiting to be verified as processed on the beacon chain,\\n /// or deposits that have been verified to an exiting validator and is now waiting for the\\n /// validator's balance to be swept.\\n /// The list may not be ordered by time of deposit.\\n /// Removed deposits will move the last deposit to the removed index.\\n bytes32[] public depositList;\\n\\n enum ValidatorState {\\n NON_REGISTERED, // validator is not registered on the SSV network\\n REGISTERED, // validator is registered on the SSV network\\n STAKED, // validator has funds staked\\n VERIFIED, // validator has been verified to exist on the beacon chain\\n ACTIVE, // The validator balance is at least 32 ETH. The validator may not yet be active on the beacon chain.\\n EXITING, // The validator has been requested to exit or has been verified as forced exit\\n EXITED, // The validator has been verified to have a zero balance\\n REMOVED, // validator has funds withdrawn to this strategy contract and is removed from the SSV\\n INVALID // The validator has been front-run and the withdrawal address is not this strategy\\n }\\n\\n // Validator data\\n struct ValidatorData {\\n ValidatorState state; // The state of the validator known to this contract\\n uint40 index; // The index of the validator on the beacon chain\\n }\\n /// @notice List of validator public key hashes that have been verified to exist on the beacon chain.\\n /// These have had a deposit processed and the validator's balance increased.\\n /// Validators will be removed from this list when its verified they have a zero balance.\\n bytes32[] public verifiedValidators;\\n /// @notice Mapping of the hash of the validator's public key to the validator state and index.\\n /// Uses the Beacon chain hashing for BLSPubkey which is sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\\n mapping(bytes32 => ValidatorData) public validator;\\n\\n /// @param blockRoot Beacon chain block root of the snapshot\\n /// @param timestamp Timestamp of the snapshot\\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\\n struct Balances {\\n bytes32 blockRoot;\\n uint64 timestamp;\\n uint128 ethBalance;\\n }\\n /// @notice Mapping of the block root to the balances at that slot\\n Balances public snappedBalance;\\n /// @notice The last verified ETH balance of the strategy\\n uint256 public lastVerifiedEthBalance;\\n\\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\\n /// of WETH that has already been accounted for.\\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\\n /// deposit events.\\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\\n /// be staked.\\n uint256 public depositedWethAccountedFor;\\n\\n // For future use\\n uint256[41] private __gap;\\n\\n event RegistratorChanged(address indexed newAddress);\\n event StakingMonitorChanged(address indexed newAddress);\\n event FirstDepositReset();\\n event SSVValidatorRegistered(\\n bytes32 indexed pubKeyHash,\\n uint64[] operatorIds\\n );\\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\\n event ETHStaked(\\n bytes32 indexed pubKeyHash,\\n bytes32 indexed pendingDepositRoot,\\n bytes pubKey,\\n uint256 amountWei\\n );\\n event ValidatorVerified(\\n bytes32 indexed pubKeyHash,\\n uint40 indexed validatorIndex\\n );\\n event ValidatorInvalid(bytes32 indexed pubKeyHash);\\n event DepositVerified(\\n bytes32 indexed pendingDepositRoot,\\n uint256 amountWei\\n );\\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\\n event BalancesSnapped(bytes32 indexed blockRoot, uint256 ethBalance);\\n event BalancesVerified(\\n uint64 indexed timestamp,\\n uint256 totalDepositsWei,\\n uint256 totalValidatorBalance,\\n uint256 ethBalance\\n );\\n\\n /// @dev Throws if called by any account other than the Registrator\\n modifier onlyRegistrator() {\\n require(msg.sender == validatorRegistrator, \\\"Not Registrator\\\");\\n _;\\n }\\n\\n /// @dev Throws if called by any account other than the Registrator or Governor\\n modifier onlyRegistratorOrGovernor() {\\n require(\\n msg.sender == validatorRegistrator || isGovernor(),\\n \\\"Not Registrator or Governor\\\"\\n );\\n _;\\n }\\n\\n /// @param _wethAddress Address of the Erc20 WETH Token contract\\n /// @param _vaultAddress Address of the Vault\\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\\n /// @param _ssvNetwork Address of the SSV Network contract\\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\\n constructor(\\n address _wethAddress,\\n address _vaultAddress,\\n address _beaconChainDepositContract,\\n address _ssvNetwork,\\n address _beaconProofs,\\n uint64 _beaconGenesisTimestamp\\n ) {\\n WETH = _wethAddress;\\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\\n SSV_NETWORK = _ssvNetwork;\\n VAULT_ADDRESS = _vaultAddress;\\n BEACON_PROOFS = _beaconProofs;\\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\\n\\n require(\\n block.timestamp > _beaconGenesisTimestamp,\\n \\\"Invalid genesis timestamp\\\"\\n );\\n }\\n\\n /**\\n *\\n * Admin Functions\\n *\\n */\\n\\n /// @notice Set the address of the registrator which can register, exit and remove validators\\n function setRegistrator(address _address) external onlyGovernor {\\n validatorRegistrator = _address;\\n emit RegistratorChanged(_address);\\n }\\n\\n /// @notice Reset the `firstDeposit` flag to false so deposits to unverified validators can be made again.\\n function resetFirstDeposit() external onlyGovernor {\\n require(firstDeposit, \\\"No first deposit\\\");\\n\\n firstDeposit = false;\\n\\n emit FirstDepositReset();\\n }\\n\\n function pause() external onlyRegistratorOrGovernor {\\n _pause();\\n }\\n\\n function unPause() external onlyGovernor {\\n _unpause();\\n }\\n\\n /**\\n *\\n * Validator Management\\n *\\n */\\n\\n /// @notice Registers a single validator in a SSV Cluster.\\n /// Only the Registrator can call this function.\\n /// @param publicKey The public key of the validator\\n /// @param operatorIds The operator IDs of the SSV Cluster\\n /// @param sharesData The shares data for the validator\\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\\n /// @param cluster The SSV cluster details including the validator count and SSV balance\\n // slither-disable-start reentrancy-no-eth\\n function registerSsvValidator(\\n bytes calldata publicKey,\\n uint64[] calldata operatorIds,\\n bytes calldata sharesData,\\n uint256 ssvAmount,\\n Cluster calldata cluster\\n ) external onlyRegistrator whenNotPaused {\\n // Hash the public key using the Beacon Chain's format\\n bytes32 pubKeyHash = _hashPubKey(publicKey);\\n // Check each public key has not already been used\\n require(\\n validator[pubKeyHash].state == ValidatorState.NON_REGISTERED,\\n \\\"Validator already registered\\\"\\n );\\n\\n // Store the validator state as registered\\n validator[pubKeyHash].state = ValidatorState.REGISTERED;\\n\\n ISSVNetwork(SSV_NETWORK).registerValidator(\\n publicKey,\\n operatorIds,\\n sharesData,\\n ssvAmount,\\n cluster\\n );\\n\\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\\n }\\n\\n // slither-disable-end reentrancy-no-eth\\n\\n struct ValidatorStakeData {\\n bytes pubkey;\\n bytes signature;\\n bytes32 depositDataRoot;\\n }\\n\\n /// @notice Stakes WETH in this strategy to a compounding validator.\\n /// The the first deposit to a new validator, the amount must be 1 ETH.\\n /// Another deposit of at least 31 ETH is required for the validator to be activated.\\n /// This second deposit has to be done after the validator has been verified.\\n /// Does not convert any ETH sitting in this strategy to WETH.\\n /// There can not be two deposits to the same validator in the same block for the same amount.\\n /// Function is pausable so in case a run-away Registrator can be prevented from continuing\\n /// to deposit funds to slashed or undesired validators.\\n /// @param validatorStakeData validator data needed to stake.\\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\\n /// Only the registrator can call this function.\\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\\n // slither-disable-start reentrancy-eth,reentrancy-no-eth\\n function stakeEth(\\n ValidatorStakeData calldata validatorStakeData,\\n uint64 depositAmountGwei\\n ) external onlyRegistrator whenNotPaused {\\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\\n // Check there is enough WETH from the deposits sitting in this strategy contract\\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\\n // the ETH can be withdrawn and then deposited back to the strategy.\\n require(\\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\\n \\\"Insufficient WETH\\\"\\n );\\n require(depositList.length < MAX_DEPOSITS, \\\"Max deposits\\\");\\n\\n // Convert required ETH from WETH and do the necessary accounting\\n _convertWethToEth(depositAmountWei);\\n\\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\\n bytes32 pubKeyHash = _hashPubKey(validatorStakeData.pubkey);\\n ValidatorState currentState = validator[pubKeyHash].state;\\n // Can only stake to a validator has have been registered, verified or active.\\n // Can not stake to a validator that has been staked but not yet verified.\\n require(\\n (currentState == ValidatorState.REGISTERED ||\\n currentState == ValidatorState.VERIFIED ||\\n currentState == ValidatorState.ACTIVE),\\n \\\"Not registered or verified\\\"\\n );\\n require(depositAmountWei >= 1 ether, \\\"Deposit too small\\\");\\n if (currentState == ValidatorState.REGISTERED) {\\n // Can only have one pending deposit to an unverified validator at a time.\\n // This is to limit front-running deposit attacks to a single deposit.\\n // The exiting deposit needs to be verified before another deposit can be made.\\n // If there was a front-running attack, the validator needs to be verified as invalid\\n // and the Governor calls `resetFirstDeposit` to set `firstDeposit` to false.\\n require(!firstDeposit, \\\"Existing first deposit\\\");\\n // Limits the amount of ETH that can be at risk from a front-running deposit attack.\\n require(\\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\\n \\\"Invalid first deposit amount\\\"\\n );\\n // Limits the number of validator balance proofs to verifyBalances\\n require(\\n verifiedValidators.length + 1 < MAX_VERIFIED_VALIDATORS,\\n \\\"Max validators\\\"\\n );\\n\\n // Flag a deposit to an unverified validator so only no other deposits can be made\\n // to an unverified validator.\\n firstDeposit = true;\\n validator[pubKeyHash].state = ValidatorState.STAKED;\\n }\\n\\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\\n * that was introduced with the Pectra upgrade.\\n * bytes11(0) to fill up the required zeros\\n * remaining bytes20 are for the address\\n */\\n bytes memory withdrawalCredentials = abi.encodePacked(\\n bytes1(0x02),\\n bytes11(0),\\n address(this)\\n );\\n\\n /// After the Pectra upgrade the validators have a new restriction when proposing\\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\\n /// forward. Each slot is created at strict 12 second intervals and those slots can\\n /// either have blocks attached to them or not. This way using the block.timestamp\\n /// the slot number can easily be calculated.\\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\\n BEACON_GENESIS_TIMESTAMP) / SLOT_DURATION;\\n\\n // Calculate the merkle root of the beacon chain pending deposit data.\\n // This is used as the unique ID of the deposit.\\n bytes32 pendingDepositRoot = IBeaconProofs(BEACON_PROOFS)\\n .merkleizePendingDeposit(\\n pubKeyHash,\\n withdrawalCredentials,\\n depositAmountGwei,\\n validatorStakeData.signature,\\n depositSlot\\n );\\n require(\\n deposits[pendingDepositRoot].status == DepositStatus.UNKNOWN,\\n \\\"Duplicate deposit\\\"\\n );\\n\\n // Store the deposit data for verifyDeposit and verifyBalances\\n deposits[pendingDepositRoot] = DepositData({\\n pubKeyHash: pubKeyHash,\\n amountGwei: depositAmountGwei,\\n slot: depositSlot,\\n depositIndex: SafeCast.toUint32(depositList.length),\\n status: DepositStatus.PENDING\\n });\\n depositList.push(pendingDepositRoot);\\n\\n // Deposit to the Beacon Chain deposit contract.\\n // This will create a deposit in the beacon chain's pending deposit queue.\\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\\n value: depositAmountWei\\n }(\\n validatorStakeData.pubkey,\\n withdrawalCredentials,\\n validatorStakeData.signature,\\n validatorStakeData.depositDataRoot\\n );\\n\\n emit ETHStaked(\\n pubKeyHash,\\n pendingDepositRoot,\\n validatorStakeData.pubkey,\\n depositAmountWei\\n );\\n }\\n\\n // slither-disable-end reentrancy-eth,reentrancy-no-eth\\n\\n /// @notice Request a full or partial withdrawal from a validator.\\n /// A zero amount will trigger a full withdrawal.\\n /// If the remaining balance is < 32 ETH then only the amount in excess of 32 ETH will be withdrawn.\\n /// Only the Registrator can call this function.\\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\\n /// If no ETH balance, the tx will revert.\\n /// @param publicKey The public key of the validator\\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\\n /// A zero amount will trigger a full withdrawal.\\n // slither-disable-start reentrancy-no-eth\\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\\n external\\n payable\\n onlyRegistrator\\n {\\n // Hash the public key using the Beacon Chain's format\\n bytes32 pubKeyHash = _hashPubKey(publicKey);\\n ValidatorData memory validatorDataMem = validator[pubKeyHash];\\n // Validator full withdrawal could be denied due to multiple reasons:\\n // - the validator has not been activated or active long enough\\n // (current_epoch < activation_epoch + SHARD_COMMITTEE_PERIOD)\\n // - the validator has pending balance to withdraw from a previous partial withdrawal request\\n //\\n // Meaning that the on-chain to beacon chain full withdrawal request could fail. Instead\\n // of adding complexity of verifying if a validator is eligible for a full exit, we allow\\n // multiple full withdrawal requests per validator.\\n require(\\n validatorDataMem.state == ValidatorState.ACTIVE ||\\n validatorDataMem.state == ValidatorState.EXITING,\\n \\\"Validator not active/exiting\\\"\\n );\\n\\n // If a full withdrawal (validator exit)\\n if (amountGwei == 0) {\\n // For each staking strategy's deposits\\n uint256 depositsCount = depositList.length;\\n for (uint256 i = 0; i < depositsCount; ++i) {\\n bytes32 pendingDepositRoot = depositList[i];\\n // Check there is no pending deposits to the exiting validator\\n require(\\n pubKeyHash != deposits[pendingDepositRoot].pubKeyHash,\\n \\\"Pending deposit\\\"\\n );\\n }\\n\\n // Store the validator state as exiting so no more deposits can be made to it.\\n // This may already be EXITING if the previous exit request failed. eg the validator\\n // was not active long enough.\\n validator[pubKeyHash].state = ValidatorState.EXITING;\\n }\\n\\n // Do not remove from the list of verified validators.\\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\\n // The validator state will be set to EXITED in the verifyBalances function.\\n\\n PartialWithdrawal.request(publicKey, amountGwei);\\n\\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\\n }\\n\\n // slither-disable-end reentrancy-no-eth\\n\\n /// @notice Remove the validator from the SSV Cluster after:\\n /// - the validator has been exited from `validatorWithdrawal` or slashed\\n /// - the validator has incorrectly registered and can not be staked to\\n /// - the initial deposit was front-run and the withdrawal address is not this strategy's address.\\n /// Make sure `validatorWithdrawal` is called with a zero amount and the validator has exited the Beacon chain.\\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\\n /// Only the registrator can call this function.\\n /// @param publicKey The public key of the validator\\n /// @param operatorIds The operator IDs of the SSV Cluster\\n /// @param cluster The SSV cluster details including the validator count and SSV balance\\n // slither-disable-start reentrancy-no-eth\\n function removeSsvValidator(\\n bytes calldata publicKey,\\n uint64[] calldata operatorIds,\\n Cluster calldata cluster\\n ) external onlyRegistrator {\\n // Hash the public key using the Beacon Chain's format\\n bytes32 pubKeyHash = _hashPubKey(publicKey);\\n ValidatorState currentState = validator[pubKeyHash].state;\\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\\n require(\\n currentState == ValidatorState.REGISTERED ||\\n currentState == ValidatorState.EXITED ||\\n currentState == ValidatorState.INVALID,\\n \\\"Validator not regd or exited\\\"\\n );\\n\\n validator[pubKeyHash].state = ValidatorState.REMOVED;\\n\\n ISSVNetwork(SSV_NETWORK).removeValidator(\\n publicKey,\\n operatorIds,\\n cluster\\n );\\n\\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\\n }\\n\\n /**\\n *\\n * SSV Management\\n *\\n */\\n\\n // slither-disable-end reentrancy-no-eth\\n\\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\\n /// by the Strategist which is already holding SSV tokens.\\n\\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\\n /// @param operatorIds The operator IDs of the SSV Cluster\\n /// @param ssvAmount The amount of SSV tokens to be withdrawn from the SSV cluster\\n /// @param cluster The SSV cluster details including the validator count and SSV balance\\n function withdrawSSV(\\n uint64[] memory operatorIds,\\n uint256 ssvAmount,\\n Cluster memory cluster\\n ) external onlyGovernor {\\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\\n }\\n\\n /**\\n *\\n * Beacon Chain Proofs\\n *\\n */\\n\\n /// @notice Verifies a validator's index to its public key.\\n /// Adds to the list of verified validators if the validator's withdrawal address is this strategy's address.\\n /// Marks the validator as invalid and removes the deposit if the withdrawal address is not this strategy's address.\\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\\n /// we are verifying.\\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\\n /// which is the beacon block root of the previous block.\\n /// @param validatorIndex The index of the validator on the beacon chain.\\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\\n /// @param withdrawalCredentials contain the validator type and withdrawal address. These can be incorrect and/or\\n /// malformed. In case of incorrect withdrawalCredentials the validator deposit has been front run\\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n /// BeaconBlock.state.validators[validatorIndex].pubkey\\n function verifyValidator(\\n uint64 nextBlockTimestamp,\\n uint40 validatorIndex,\\n bytes32 pubKeyHash,\\n bytes32 withdrawalCredentials,\\n bytes calldata validatorPubKeyProof\\n ) external {\\n require(\\n validator[pubKeyHash].state == ValidatorState.STAKED,\\n \\\"Validator not staked\\\"\\n );\\n\\n // Get the beacon block root of the slot we are verifying the validator in.\\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\\n\\n // Verify the validator index is for the validator with the given public key.\\n // Also verify the validator's withdrawal credentials\\n IBeaconProofs(BEACON_PROOFS).verifyValidator(\\n blockRoot,\\n pubKeyHash,\\n validatorPubKeyProof,\\n validatorIndex,\\n withdrawalCredentials\\n );\\n\\n // Store the validator state as verified\\n validator[pubKeyHash] = ValidatorData({\\n state: ValidatorState.VERIFIED,\\n index: validatorIndex\\n });\\n\\n bytes32 expectedWithdrawalCredentials = bytes32(\\n abi.encodePacked(bytes1(0x02), bytes11(0), address(this))\\n );\\n\\n // If the initial deposit was front-run and the withdrawal address is not this strategy\\n // or the validator type is not a compounding validator (0x02)\\n if (expectedWithdrawalCredentials != withdrawalCredentials) {\\n // override the validator state\\n validator[pubKeyHash].state = ValidatorState.INVALID;\\n\\n // Find and remove the deposit as the funds can not be recovered\\n uint256 depositCount = depositList.length;\\n for (uint256 i = 0; i < depositCount; i++) {\\n DepositData memory deposit = deposits[depositList[i]];\\n if (deposit.pubKeyHash == pubKeyHash) {\\n // next verifyBalances will correctly account for the loss of a front-run\\n // deposit. Doing it here accounts for the loss as soon as possible\\n lastVerifiedEthBalance -= Math.min(\\n lastVerifiedEthBalance,\\n uint256(deposit.amountGwei) * 1 gwei\\n );\\n _removeDeposit(depositList[i], deposit);\\n break;\\n }\\n }\\n\\n // Leave the `firstDeposit` flag as true so no more deposits to unverified validators can be made.\\n // The Governor has to reset the `firstDeposit` to false before another deposit to\\n // an unverified validator can be made.\\n // The Governor can set a new `validatorRegistrator` if they suspect it has been compromised.\\n\\n emit ValidatorInvalid(pubKeyHash);\\n return;\\n }\\n\\n // Add the new validator to the list of verified validators\\n verifiedValidators.push(pubKeyHash);\\n\\n // Reset the firstDeposit flag as the first deposit to an unverified validator has been verified.\\n firstDeposit = false;\\n\\n emit ValidatorVerified(pubKeyHash, validatorIndex);\\n }\\n\\n struct FirstPendingDepositSlotProofData {\\n uint64 slot;\\n bytes proof;\\n }\\n\\n struct StrategyValidatorProofData {\\n uint64 withdrawableEpoch;\\n bytes withdrawableEpochProof;\\n }\\n\\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\\n ///\\n /// Important: this function has a limitation where `depositProcessedSlot` that is passed by the off-chain\\n /// verifier requires a slot immediately after it to propose a block otherwise the `BeaconRoots.parentBlockRoot`\\n /// will fail. This shouldn't be a problem, since by the current behaviour of beacon chain only 1%-3% slots\\n /// don't propose a block.\\n /// @param pendingDepositRoot The unique identifier of the deposit emitted in `ETHStaked` from\\n /// the `stakeEth` function.\\n /// @param depositProcessedSlot Any slot on or after the strategy's deposit was processed on the beacon chain.\\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\\n /// set for the next block timestamp in 12 seconds time.\\n /// @param firstPendingDeposit a `FirstPendingDepositSlotProofData` struct containing:\\n /// - slot: The beacon chain slot of the first deposit in the beacon chain's deposit queue.\\n /// Can be any non-zero value if the deposit queue is empty.\\n /// - proof: The merkle proof of the first pending deposit's slot to the beacon block root.\\n /// Can be either:\\n /// * 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\\n /// * 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\\n /// @param strategyValidatorData a `StrategyValidatorProofData` struct containing:\\n /// - withdrawableEpoch: The withdrawable epoch of the validator the strategy is depositing to.\\n /// - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the validator the strategy\\n /// is depositing to, to the beacon block root.\\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n // slither-disable-start reentrancy-no-eth\\n function verifyDeposit(\\n bytes32 pendingDepositRoot,\\n uint64 depositProcessedSlot,\\n FirstPendingDepositSlotProofData calldata firstPendingDeposit,\\n StrategyValidatorProofData calldata strategyValidatorData\\n ) external {\\n // Load into memory the previously saved deposit data\\n DepositData memory deposit = deposits[pendingDepositRoot];\\n ValidatorData memory strategyValidator = validator[deposit.pubKeyHash];\\n require(deposit.status == DepositStatus.PENDING, \\\"Deposit not pending\\\");\\n require(firstPendingDeposit.slot != 0, \\\"Zero 1st pending deposit slot\\\");\\n\\n // We should allow the verification of deposits for validators that have been marked as exiting\\n // to cover this situation:\\n // - there are 2 pending deposits\\n // - beacon chain has slashed the validator\\n // - when verifyDeposit is called for the first deposit it sets the Validator state to EXITING\\n // - verifyDeposit should allow a secondary call for the other deposit to a slashed validator\\n require(\\n strategyValidator.state == ValidatorState.VERIFIED ||\\n strategyValidator.state == ValidatorState.ACTIVE ||\\n strategyValidator.state == ValidatorState.EXITING,\\n \\\"Not verified/active/exiting\\\"\\n );\\n // The verification slot must be after the deposit's slot.\\n // This is needed for when the deposit queue is empty.\\n require(deposit.slot < depositProcessedSlot, \\\"Slot not after deposit\\\");\\n\\n uint64 snapTimestamp = snappedBalance.timestamp;\\n\\n // This check prevents an accounting error that can happen if:\\n // - snapBalances are snapped at the time of T\\n // - deposit is processed on the beacon chain after time T and before verifyBalances()\\n // - verifyDeposit is called before verifyBalances which removes a deposit from depositList\\n // and deposit balance from totalDepositsWei\\n // - verifyBalances is called under-reporting the strategy's balance\\n require(\\n (_calcNextBlockTimestamp(depositProcessedSlot) <= snapTimestamp) ||\\n snapTimestamp == 0,\\n \\\"Deposit after balance snapshot\\\"\\n );\\n\\n // Get the parent beacon block root of the next block which is the block root of the deposit verification slot.\\n // This will revert if the slot after the verification slot was missed.\\n bytes32 depositBlockRoot = BeaconRoots.parentBlockRoot(\\n _calcNextBlockTimestamp(depositProcessedSlot)\\n );\\n\\n // Verify the slot of the first pending deposit matches the beacon chain\\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\\n .verifyFirstPendingDeposit(\\n depositBlockRoot,\\n firstPendingDeposit.slot,\\n firstPendingDeposit.proof\\n );\\n\\n // Verify the withdrawableEpoch on the validator of the strategy's deposit\\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\\n depositBlockRoot,\\n strategyValidator.index,\\n strategyValidatorData.withdrawableEpoch,\\n strategyValidatorData.withdrawableEpochProof\\n );\\n\\n uint64 firstPendingDepositEpoch = firstPendingDeposit.slot /\\n SLOTS_PER_EPOCH;\\n\\n // If deposit queue is empty all deposits have certainly been processed. If not\\n // a validator can either be not exiting and no further checks are required.\\n // Or a validator is exiting then this function needs to make sure that the\\n // pending deposit to an exited validator has certainly been processed. The\\n // slot/epoch of first pending deposit is the one that contains the transaction\\n // where the deposit to the ETH Deposit Contract has been made.\\n //\\n // Once the firstPendingDepositEpoch becomes greater than the withdrawableEpoch of\\n // the slashed validator then the deposit has certainly been processed. When the beacon\\n // chain reaches the withdrawableEpoch of the validator the deposit will no longer be\\n // postponed. And any new deposits created (and present in the deposit queue)\\n // will have an equal or larger withdrawableEpoch.\\n require(\\n strategyValidatorData.withdrawableEpoch == FAR_FUTURE_EPOCH ||\\n strategyValidatorData.withdrawableEpoch <=\\n firstPendingDepositEpoch ||\\n isDepositQueueEmpty,\\n \\\"Exit Deposit likely not proc.\\\"\\n );\\n\\n // solhint-disable max-line-length\\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\\n // many deposits in the same block, hence have the same pending deposit slot.\\n // If the deposit queue is empty then our deposit must have been processed on the beacon chain.\\n // The deposit slot can be zero for validators consolidating to a compounding validator or 0x01 validator\\n // being promoted to a compounding one. Reference:\\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\\n // We can not guarantee that the deposit has been processed in that case.\\n // solhint-enable max-line-length\\n require(\\n deposit.slot < firstPendingDeposit.slot || isDepositQueueEmpty,\\n \\\"Deposit likely not processed\\\"\\n );\\n\\n // Remove the deposit now it has been verified as processed on the beacon chain.\\n _removeDeposit(pendingDepositRoot, deposit);\\n\\n emit DepositVerified(\\n pendingDepositRoot,\\n uint256(deposit.amountGwei) * 1 gwei\\n );\\n }\\n\\n function _removeDeposit(\\n bytes32 pendingDepositRoot,\\n DepositData memory deposit\\n ) internal {\\n // After verifying the proof, update the contract storage\\n deposits[pendingDepositRoot].status = DepositStatus.VERIFIED;\\n // Move the last deposit to the index of the verified deposit\\n bytes32 lastDeposit = depositList[depositList.length - 1];\\n depositList[deposit.depositIndex] = lastDeposit;\\n deposits[lastDeposit].depositIndex = deposit.depositIndex;\\n // Delete the last deposit from the list\\n depositList.pop();\\n }\\n\\n /// @dev Calculates the timestamp of the next execution block from the given slot.\\n /// @param slot The beacon chain slot number used for merkle proof verification.\\n function _calcNextBlockTimestamp(uint64 slot)\\n internal\\n view\\n returns (uint64)\\n {\\n // Calculate the next block timestamp from the slot.\\n return SLOT_DURATION * slot + BEACON_GENESIS_TIMESTAMP + SLOT_DURATION;\\n }\\n\\n // slither-disable-end reentrancy-no-eth\\n\\n /// @notice Stores the current ETH balance at the current block and beacon block root\\n /// of the slot that is associated with the previous block.\\n ///\\n /// When snapping / verifying balance it is of a high importance that there is no\\n /// miss-match in respect to ETH that is held by the contract and balances that are\\n /// verified on the validators.\\n ///\\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\\n /// constructing a block on the beacon chain consist of:\\n /// - process_withdrawals: ETH is deducted from the validator's balance\\n /// - process_execution_payload: immediately after the previous step executing all the\\n /// transactions\\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\\n /// contained in the withdrawal credentials of the exited validators\\n ///\\n /// That means that balance increases which are part of the post-block execution state are\\n /// done within the block, but the transaction that are contained within that block can not\\n /// see / interact with the balance from the exited validators. Only transactions in the\\n /// next block can do that.\\n ///\\n /// When snap balances is performed the state of the chain is snapped across 2 separate\\n /// chain states:\\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\\n /// will be referred to as Y - 1 as it makes no difference in the argument\\n ///\\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\\n /// miss-counting ETH or much more dangerous double counting of the ETH.\\n ///\\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\\n /// - ETH balance in the contract on block X\\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\\n /// - ETH balance in validators that are active in slot Y - 1\\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\\n /// and have their balance visible to transactions in slot Y and corresponding block X\\n /// (or sooner)\\n ///\\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\\n ///\\n /// *ETH balance in the contract on block X*\\n ///\\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\\n ///\\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\\n /// would result in double counting the `stakedEth` since it would be present once in the\\n /// snapped contract balance and the second time in deposit storage variables.\\n ///\\n /// This behaviour is correct.\\n ///\\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\\n ///\\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\\n /// contract at block Z. The execution layer doesn't have direct access to the state of\\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\\n /// and could also be part of the validator balances. It does that by verifying that at\\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\\n /// the last snap till now all are still in queue. Which ensures they can not be part of\\n /// the validator balances in later steps.\\n ///\\n /// This behaviour is correct.\\n ///\\n /// *ETH balance in validators that are active in slot Y - 1*\\n ///\\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\\n /// correctness.\\n ///\\n /// This behaviour is correct.\\n ///\\n /// *The withdrawn validators*\\n ///\\n /// The withdrawn validators could have their balances deducted in any slot before slot\\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\\n /// look at the \\\"worst case scenario\\\" where the validator withdrawal is processed in the\\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\\n /// even if the validator exits at the latest possible time it is paramount that the ETH\\n /// balance on the execution layer is recorded in the next block. Correctly accounting\\n /// for the withdrawn ETH.\\n ///\\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\\n /// validator balance verification as well as execution layer contract balance snap.\\n ///\\n /// This behaviour is correct.\\n ///\\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\\n function snapBalances() external {\\n uint64 currentTimestamp = SafeCast.toUint64(block.timestamp);\\n require(\\n snappedBalance.timestamp + SNAP_BALANCES_DELAY < currentTimestamp,\\n \\\"Snap too soon\\\"\\n );\\n\\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(currentTimestamp);\\n // Get the current ETH balance\\n uint256 ethBalance = address(this).balance;\\n\\n // Store the snapped balance\\n snappedBalance = Balances({\\n blockRoot: blockRoot,\\n timestamp: currentTimestamp,\\n ethBalance: SafeCast.toUint128(ethBalance)\\n });\\n\\n emit BalancesSnapped(blockRoot, ethBalance);\\n }\\n\\n // A struct is used to avoid stack too deep errors\\n struct BalanceProofs {\\n // BeaconBlock.state.balances\\n bytes32 balancesContainerRoot;\\n bytes balancesContainerProof;\\n // BeaconBlock.state.balances[validatorIndex]\\n bytes32[] validatorBalanceLeaves;\\n bytes[] validatorBalanceProofs;\\n }\\n\\n struct PendingDepositProofs {\\n bytes32 pendingDepositContainerRoot;\\n bytes pendingDepositContainerProof;\\n uint32[] pendingDepositIndexes;\\n bytes[] pendingDepositProofs;\\n }\\n\\n /// @notice Verifies the balances of all active validators on the beacon chain\\n /// and checks each of the strategy's deposits are still to be processed by the beacon chain.\\n /// @param balanceProofs a `BalanceProofs` struct containing the following:\\n /// - balancesContainerRoot: The merkle root of the balances container\\n /// - balancesContainerProof: The merkle proof for the balances container to the beacon block root.\\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n /// - validatorBalanceLeaves: Array of leaf nodes containing the validator balance with three other balances.\\n /// - validatorBalanceProofs: Array of merkle proofs for the validator balance to the Balances container root.\\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n /// @param pendingDepositProofs a `PendingDepositProofs` struct containing the following:\\n /// - pendingDepositContainerRoot: The merkle root of the pending deposits list container\\n /// - pendingDepositContainerProof: The merkle proof from the pending deposits list container\\n /// to the beacon block root.\\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n /// - pendingDepositIndexes: Array of indexes in the pending deposits list container for each\\n /// of the strategy's deposits.\\n /// - pendingDepositProofs: Array of merkle proofs for each strategy deposit in the\\n /// beacon chain's pending deposit list container to the pending deposits list container root.\\n /// These are 28 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n // slither-disable-start reentrancy-no-eth\\n function verifyBalances(\\n BalanceProofs calldata balanceProofs,\\n PendingDepositProofs calldata pendingDepositProofs\\n ) external {\\n // Load previously snapped balances for the given block root\\n Balances memory balancesMem = snappedBalance;\\n // Check the balances are the latest\\n require(balancesMem.timestamp > 0, \\\"No snapped balances\\\");\\n\\n uint256 verifiedValidatorsCount = verifiedValidators.length;\\n uint256 totalValidatorBalance = 0;\\n uint256 depositsCount = depositList.length;\\n\\n // If there are no verified validators then we can skip the balance verification\\n if (verifiedValidatorsCount > 0) {\\n require(\\n balanceProofs.validatorBalanceProofs.length ==\\n verifiedValidatorsCount,\\n \\\"Invalid balance proofs\\\"\\n );\\n require(\\n balanceProofs.validatorBalanceLeaves.length ==\\n verifiedValidatorsCount,\\n \\\"Invalid balance leaves\\\"\\n );\\n // verify beaconBlock.state.balances root to beacon block root\\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\\n balancesMem.blockRoot,\\n balanceProofs.balancesContainerRoot,\\n balanceProofs.balancesContainerProof\\n );\\n\\n bytes32[]\\n memory validatorHashesMem = _getPendingDepositValidatorHashes(\\n depositsCount\\n );\\n\\n // for each validator in reverse order so we can pop off exited validators at the end\\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\\n --i;\\n ValidatorData memory validatorDataMem = validator[\\n verifiedValidators[i]\\n ];\\n // verify validator's balance in beaconBlock.state.balances to the\\n // beaconBlock.state.balances container root\\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\\n .verifyValidatorBalance(\\n balanceProofs.balancesContainerRoot,\\n balanceProofs.validatorBalanceLeaves[i],\\n balanceProofs.validatorBalanceProofs[i],\\n validatorDataMem.index\\n );\\n\\n // If the validator has exited and the balance is now zero\\n if (validatorBalanceGwei == 0) {\\n // Check if there are any pending deposits to this validator\\n bool depositPending = false;\\n for (uint256 j = 0; j < validatorHashesMem.length; j++) {\\n if (validatorHashesMem[j] == verifiedValidators[i]) {\\n depositPending = true;\\n break;\\n }\\n }\\n\\n // If validator has a pending deposit we can not remove due to\\n // the following situation:\\n // - validator has a pending deposit\\n // - validator has been slashed\\n // - sweep cycle has withdrawn all ETH from the validator. Balance is 0\\n // - beacon chain has processed the deposit and set the validator balance\\n // to deposit amount\\n // - if validator is no longer in the list of verifiedValidators its\\n // balance will not be considered and be under-counted.\\n if (!depositPending) {\\n // Store the validator state as exited\\n // This could have been in VERIFIED, ACTIVE or EXITING state\\n validator[verifiedValidators[i]].state = ValidatorState\\n .EXITED;\\n\\n // Remove the validator with a zero balance from the list of verified validators\\n\\n // Reduce the count of verified validators which is the last index before the pop removes it.\\n verifiedValidatorsCount -= 1;\\n\\n // Move the last validator that has already been verified to the current index.\\n // There's an extra SSTORE if i is the last active validator but that's fine,\\n // It's not a common case and the code is simpler this way.\\n verifiedValidators[i] = verifiedValidators[\\n verifiedValidatorsCount\\n ];\\n // Delete the last validator from the list\\n verifiedValidators.pop();\\n }\\n\\n // The validator balance is zero so not need to add to totalValidatorBalance\\n continue;\\n } else if (\\n validatorDataMem.state == ValidatorState.VERIFIED &&\\n validatorBalanceGwei > MIN_ACTIVATION_BALANCE_GWEI\\n ) {\\n // Store the validator state as active. This does not necessarily mean the\\n // validator is active on the beacon chain yet. It just means the validator has\\n // enough balance that it can become active.\\n validator[verifiedValidators[i]].state = ValidatorState\\n .ACTIVE;\\n }\\n\\n // convert Gwei balance to Wei and add to the total validator balance\\n totalValidatorBalance += validatorBalanceGwei * 1 gwei;\\n }\\n }\\n\\n uint256 totalDepositsWei = 0;\\n\\n // If there are no deposits then we can skip the deposit verification.\\n // This section is after the validator balance verifications so an exited validator will be marked\\n // as EXITED before the deposits are verified. If there was a deposit to an exited validator\\n // then the deposit can only be removed once the validator is fully exited.\\n // It is possible that validator fully exits and a postponed deposit to an exited validator increases\\n // its balance again. In such case the contract will erroneously consider a deposit applied before it\\n // has been applied on the beacon chain showing a smaller than real `totalValidatorBalance`.\\n if (depositsCount > 0) {\\n require(\\n pendingDepositProofs.pendingDepositProofs.length ==\\n depositsCount,\\n \\\"Invalid deposit proofs\\\"\\n );\\n require(\\n pendingDepositProofs.pendingDepositIndexes.length ==\\n depositsCount,\\n \\\"Invalid deposit indexes\\\"\\n );\\n\\n // Verify from the root of the pending deposit list container to the beacon block root\\n IBeaconProofs(BEACON_PROOFS).verifyPendingDepositsContainer(\\n balancesMem.blockRoot,\\n pendingDepositProofs.pendingDepositContainerRoot,\\n pendingDepositProofs.pendingDepositContainerProof\\n );\\n\\n // For each staking strategy's deposit.\\n for (uint256 i = 0; i < depositsCount; ++i) {\\n bytes32 pendingDepositRoot = depositList[i];\\n\\n // Verify the strategy's deposit is still pending on the beacon chain.\\n IBeaconProofs(BEACON_PROOFS).verifyPendingDeposit(\\n pendingDepositProofs.pendingDepositContainerRoot,\\n pendingDepositRoot,\\n pendingDepositProofs.pendingDepositProofs[i],\\n pendingDepositProofs.pendingDepositIndexes[i]\\n );\\n\\n // Convert the deposit amount from Gwei to Wei and add to the total\\n totalDepositsWei +=\\n uint256(deposits[pendingDepositRoot].amountGwei) *\\n 1 gwei;\\n }\\n }\\n\\n // Store the verified balance in storage\\n lastVerifiedEthBalance =\\n totalDepositsWei +\\n totalValidatorBalance +\\n balancesMem.ethBalance;\\n // Reset the last snap timestamp so a new snapBalances has to be made\\n snappedBalance.timestamp = 0;\\n\\n emit BalancesVerified(\\n balancesMem.timestamp,\\n totalDepositsWei,\\n totalValidatorBalance,\\n balancesMem.ethBalance\\n );\\n }\\n\\n // slither-disable-end reentrancy-no-eth\\n\\n /// @notice get a list of all validator hashes present in the pending deposits\\n /// list can have duplicate entries\\n function _getPendingDepositValidatorHashes(uint256 depositsCount)\\n internal\\n view\\n returns (bytes32[] memory validatorHashes)\\n {\\n validatorHashes = new bytes32[](depositsCount);\\n for (uint256 i = 0; i < depositsCount; i++) {\\n validatorHashes[i] = deposits[depositList[i]].pubKeyHash;\\n }\\n }\\n\\n /// @notice Hash a validator public key using the Beacon Chain's format\\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\\n require(pubKey.length == 48, \\\"Invalid public key\\\");\\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\\n }\\n\\n /**\\n *\\n * WETH and ETH Accounting\\n *\\n */\\n\\n /// @dev Called when WETH is transferred out of the strategy so\\n /// the strategy knows how much WETH it has on deposit.\\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\\n function _transferWeth(uint256 _amount, address _recipient) internal {\\n IERC20(WETH).safeTransfer(_recipient, _amount);\\n\\n // The min is required as more WETH can be withdrawn than deposited\\n // as the strategy earns consensus and execution rewards.\\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\\n depositedWethAccountedFor -= deductAmount;\\n\\n // No change in ETH balance so no need to snapshot the balances\\n }\\n\\n /// @dev Converts ETH to WETH and updates the accounting.\\n /// @param _ethAmount The amount of ETH in wei.\\n function _convertEthToWeth(uint256 _ethAmount) internal {\\n // slither-disable-next-line arbitrary-send-eth\\n IWETH9(WETH).deposit{ value: _ethAmount }();\\n\\n depositedWethAccountedFor += _ethAmount;\\n\\n // Store the reduced ETH balance.\\n // The ETH balance in this strategy contract can be more than the last verified ETH balance\\n // due to partial withdrawals or full exits being processed by the beacon chain since the last snapBalances.\\n // It can also happen from execution rewards (MEV) or ETH donations.\\n lastVerifiedEthBalance -= Math.min(lastVerifiedEthBalance, _ethAmount);\\n\\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\\n snappedBalance.timestamp = 0;\\n }\\n\\n /// @dev Converts WETH to ETH and updates the accounting.\\n /// @param _wethAmount The amount of WETH in wei.\\n function _convertWethToEth(uint256 _wethAmount) internal {\\n IWETH9(WETH).withdraw(_wethAmount);\\n\\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\\n depositedWethAccountedFor -= deductAmount;\\n\\n // Store the increased ETH balance\\n lastVerifiedEthBalance += _wethAmount;\\n\\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\\n snappedBalance.timestamp = 0;\\n }\\n\\n /**\\n *\\n * View Functions\\n *\\n */\\n\\n /// @notice Returns the number of deposits waiting to be verified as processed on the beacon chain,\\n /// or deposits that have been verified to an exiting validator and is now waiting for the\\n /// validator's balance to be swept.\\n function depositListLength() external view returns (uint256) {\\n return depositList.length;\\n }\\n\\n /// @notice Returns the number of verified validators.\\n function verifiedValidatorsLength() external view returns (uint256) {\\n return verifiedValidators.length;\\n }\\n}\\n\",\"keccak256\":\"0x43e67349bb86dc110c4c923d5cced012aff0d80a6fe61fe27dc7228f66bcba28\",\"license\":\"BUSL-1.1\"},\"contracts/token/OUSD.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title OUSD Token Contract\\n * @dev ERC20 compatible contract for OUSD\\n * @dev Implements an elastic supply\\n * @author Origin Protocol Inc\\n */\\nimport { IVault } from \\\"../interfaces/IVault.sol\\\";\\nimport { Governable } from \\\"../governance/Governable.sol\\\";\\nimport { SafeCast } from \\\"@openzeppelin/contracts/utils/math/SafeCast.sol\\\";\\n\\ncontract OUSD is Governable {\\n using SafeCast for int256;\\n using SafeCast for uint256;\\n\\n /// @dev Event triggered when the supply changes\\n /// @param totalSupply Updated token total supply\\n /// @param rebasingCredits Updated token rebasing credits\\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\\n event TotalSupplyUpdatedHighres(\\n uint256 totalSupply,\\n uint256 rebasingCredits,\\n uint256 rebasingCreditsPerToken\\n );\\n /// @dev Event triggered when an account opts in for rebasing\\n /// @param account Address of the account\\n event AccountRebasingEnabled(address account);\\n /// @dev Event triggered when an account opts out of rebasing\\n /// @param account Address of the account\\n event AccountRebasingDisabled(address account);\\n /// @dev Emitted when `value` tokens are moved from one account `from` to\\n /// another `to`.\\n /// @param from Address of the account tokens are moved from\\n /// @param to Address of the account tokens are moved to\\n /// @param value Amount of tokens transferred\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n /// a call to {approve}. `value` is the new allowance.\\n /// @param owner Address of the owner approving allowance\\n /// @param spender Address of the spender allowance is granted to\\n /// @param value Amount of tokens spender can transfer\\n event Approval(\\n address indexed owner,\\n address indexed spender,\\n uint256 value\\n );\\n /// @dev Yield resulting from {changeSupply} that a `source` account would\\n /// receive is directed to `target` account.\\n /// @param source Address of the source forwarding the yield\\n /// @param target Address of the target receiving the yield\\n event YieldDelegated(address source, address target);\\n /// @dev Yield delegation from `source` account to the `target` account is\\n /// suspended.\\n /// @param source Address of the source suspending yield forwarding\\n /// @param target Address of the target no longer receiving yield from `source`\\n /// account\\n event YieldUndelegated(address source, address target);\\n\\n enum RebaseOptions {\\n NotSet,\\n StdNonRebasing,\\n StdRebasing,\\n YieldDelegationSource,\\n YieldDelegationTarget\\n }\\n\\n uint256[154] private _gap; // Slots to align with deployed contract\\n uint256 private constant MAX_SUPPLY = type(uint128).max;\\n /// @dev The amount of tokens in existence\\n uint256 public totalSupply;\\n mapping(address => mapping(address => uint256)) private allowances;\\n /// @dev The vault with privileges to execute {mint}, {burn}\\n /// and {changeSupply}\\n address public vaultAddress;\\n mapping(address => uint256) internal creditBalances;\\n // the 2 storage variables below need trailing underscores to not name collide with public functions\\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\\n uint256 private rebasingCreditsPerToken_;\\n /// @dev The amount of tokens that are not rebasing - receiving yield\\n uint256 public nonRebasingSupply;\\n mapping(address => uint256) internal alternativeCreditsPerToken;\\n /// @dev A map of all addresses and their respective RebaseOptions\\n mapping(address => RebaseOptions) public rebaseState;\\n mapping(address => uint256) private __deprecated_isUpgraded;\\n /// @dev A map of addresses that have yields forwarded to. This is an\\n /// inverse mapping of {yieldFrom}\\n /// Key Account forwarding yield\\n /// Value Account receiving yield\\n mapping(address => address) public yieldTo;\\n /// @dev A map of addresses that are receiving the yield. This is an\\n /// inverse mapping of {yieldTo}\\n /// Key Account receiving yield\\n /// Value Account forwarding yield\\n mapping(address => address) public yieldFrom;\\n\\n uint256 private constant RESOLUTION_INCREASE = 1e9;\\n uint256[34] private __gap; // including below gap totals up to 200\\n\\n /// @dev Verifies that the caller is the Governor or Strategist.\\n modifier onlyGovernorOrStrategist() {\\n require(\\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\\n \\\"Caller is not the Strategist or Governor\\\"\\n );\\n _;\\n }\\n\\n /// @dev Initializes the contract and sets necessary variables.\\n /// @param _vaultAddress Address of the vault contract\\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\\n external\\n onlyGovernor\\n {\\n require(_vaultAddress != address(0), \\\"Zero vault address\\\");\\n require(vaultAddress == address(0), \\\"Already initialized\\\");\\n\\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\\n vaultAddress = _vaultAddress;\\n }\\n\\n /// @dev Returns the symbol of the token, a shorter version\\n /// of the name.\\n function symbol() external pure virtual returns (string memory) {\\n return \\\"OUSD\\\";\\n }\\n\\n /// @dev Returns the name of the token.\\n function name() external pure virtual returns (string memory) {\\n return \\\"Origin Dollar\\\";\\n }\\n\\n /// @dev Returns the number of decimals used to get its user representation.\\n function decimals() external pure virtual returns (uint8) {\\n return 18;\\n }\\n\\n /**\\n * @dev Verifies that the caller is the Vault contract\\n */\\n modifier onlyVault() {\\n require(vaultAddress == msg.sender, \\\"Caller is not the Vault\\\");\\n _;\\n }\\n\\n /**\\n * @return High resolution rebasingCreditsPerToken\\n */\\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\\n return rebasingCreditsPerToken_;\\n }\\n\\n /**\\n * @return Low resolution rebasingCreditsPerToken\\n */\\n function rebasingCreditsPerToken() external view returns (uint256) {\\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\\n }\\n\\n /**\\n * @return High resolution total number of rebasing credits\\n */\\n function rebasingCreditsHighres() external view returns (uint256) {\\n return rebasingCredits_;\\n }\\n\\n /**\\n * @return Low resolution total number of rebasing credits\\n */\\n function rebasingCredits() external view returns (uint256) {\\n return rebasingCredits_ / RESOLUTION_INCREASE;\\n }\\n\\n /**\\n * @notice Gets the balance of the specified address.\\n * @param _account Address to query the balance of.\\n * @return A uint256 representing the amount of base units owned by the\\n * specified address.\\n */\\n function balanceOf(address _account) public view returns (uint256) {\\n RebaseOptions state = rebaseState[_account];\\n if (state == RebaseOptions.YieldDelegationSource) {\\n // Saves a slot read when transferring to or from a yield delegating source\\n // since we know creditBalances equals the balance.\\n return creditBalances[_account];\\n }\\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\\n _creditsPerToken(_account);\\n if (state == RebaseOptions.YieldDelegationTarget) {\\n // creditBalances of yieldFrom accounts equals token balances\\n return baseBalance - creditBalances[yieldFrom[_account]];\\n }\\n return baseBalance;\\n }\\n\\n /**\\n * @notice Gets the credits balance of the specified address.\\n * @dev Backwards compatible with old low res credits per token.\\n * @param _account The address to query the balance of.\\n * @return (uint256, uint256) Credit balance and credits per token of the\\n * address\\n */\\n function creditsBalanceOf(address _account)\\n external\\n view\\n returns (uint256, uint256)\\n {\\n uint256 cpt = _creditsPerToken(_account);\\n if (cpt == 1e27) {\\n // For a period before the resolution upgrade, we created all new\\n // contract accounts at high resolution. Since they are not changing\\n // as a result of this upgrade, we will return their true values\\n return (creditBalances[_account], cpt);\\n } else {\\n return (\\n creditBalances[_account] / RESOLUTION_INCREASE,\\n cpt / RESOLUTION_INCREASE\\n );\\n }\\n }\\n\\n /**\\n * @notice Gets the credits balance of the specified address.\\n * @param _account The address to query the balance of.\\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\\n * address, and isUpgraded\\n */\\n function creditsBalanceOfHighres(address _account)\\n external\\n view\\n returns (\\n uint256,\\n uint256,\\n bool\\n )\\n {\\n return (\\n creditBalances[_account],\\n _creditsPerToken(_account),\\n true // all accounts have their resolution \\\"upgraded\\\"\\n );\\n }\\n\\n // Backwards compatible view\\n function nonRebasingCreditsPerToken(address _account)\\n external\\n view\\n returns (uint256)\\n {\\n return alternativeCreditsPerToken[_account];\\n }\\n\\n /**\\n * @notice Transfer tokens to a specified address.\\n * @param _to the address to transfer to.\\n * @param _value the amount to be transferred.\\n * @return true on success.\\n */\\n function transfer(address _to, uint256 _value) external returns (bool) {\\n require(_to != address(0), \\\"Transfer to zero address\\\");\\n\\n _executeTransfer(msg.sender, _to, _value);\\n\\n emit Transfer(msg.sender, _to, _value);\\n return true;\\n }\\n\\n /**\\n * @notice Transfer tokens from one address to another.\\n * @param _from The address you want to send tokens from.\\n * @param _to The address you want to transfer to.\\n * @param _value The amount of tokens to be transferred.\\n * @return true on success.\\n */\\n function transferFrom(\\n address _from,\\n address _to,\\n uint256 _value\\n ) external returns (bool) {\\n require(_to != address(0), \\\"Transfer to zero address\\\");\\n uint256 userAllowance = allowances[_from][msg.sender];\\n require(_value <= userAllowance, \\\"Allowance exceeded\\\");\\n\\n unchecked {\\n allowances[_from][msg.sender] = userAllowance - _value;\\n }\\n\\n _executeTransfer(_from, _to, _value);\\n\\n emit Transfer(_from, _to, _value);\\n return true;\\n }\\n\\n function _executeTransfer(\\n address _from,\\n address _to,\\n uint256 _value\\n ) internal {\\n (\\n int256 fromRebasingCreditsDiff,\\n int256 fromNonRebasingSupplyDiff\\n ) = _adjustAccount(_from, -_value.toInt256());\\n (\\n int256 toRebasingCreditsDiff,\\n int256 toNonRebasingSupplyDiff\\n ) = _adjustAccount(_to, _value.toInt256());\\n\\n _adjustGlobals(\\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\\n );\\n }\\n\\n function _adjustAccount(address _account, int256 _balanceChange)\\n internal\\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\\n {\\n RebaseOptions state = rebaseState[_account];\\n int256 currentBalance = balanceOf(_account).toInt256();\\n if (currentBalance + _balanceChange < 0) {\\n revert(\\\"Transfer amount exceeds balance\\\");\\n }\\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\\n\\n if (state == RebaseOptions.YieldDelegationSource) {\\n address target = yieldTo[_account];\\n uint256 targetOldBalance = balanceOf(target);\\n uint256 targetNewCredits = _balanceToRebasingCredits(\\n targetOldBalance + newBalance\\n );\\n rebasingCreditsDiff =\\n targetNewCredits.toInt256() -\\n creditBalances[target].toInt256();\\n\\n creditBalances[_account] = newBalance;\\n creditBalances[target] = targetNewCredits;\\n } else if (state == RebaseOptions.YieldDelegationTarget) {\\n uint256 newCredits = _balanceToRebasingCredits(\\n newBalance + creditBalances[yieldFrom[_account]]\\n );\\n rebasingCreditsDiff =\\n newCredits.toInt256() -\\n creditBalances[_account].toInt256();\\n creditBalances[_account] = newCredits;\\n } else {\\n _autoMigrate(_account);\\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\\n _account\\n ];\\n if (alternativeCreditsPerTokenMem > 0) {\\n nonRebasingSupplyDiff = _balanceChange;\\n if (alternativeCreditsPerTokenMem != 1e18) {\\n alternativeCreditsPerToken[_account] = 1e18;\\n }\\n creditBalances[_account] = newBalance;\\n } else {\\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\\n rebasingCreditsDiff =\\n newCredits.toInt256() -\\n creditBalances[_account].toInt256();\\n creditBalances[_account] = newCredits;\\n }\\n }\\n }\\n\\n function _adjustGlobals(\\n int256 _rebasingCreditsDiff,\\n int256 _nonRebasingSupplyDiff\\n ) internal {\\n if (_rebasingCreditsDiff != 0) {\\n rebasingCredits_ = (rebasingCredits_.toInt256() +\\n _rebasingCreditsDiff).toUint256();\\n }\\n if (_nonRebasingSupplyDiff != 0) {\\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\\n _nonRebasingSupplyDiff).toUint256();\\n }\\n }\\n\\n /**\\n * @notice Function to check the amount of tokens that _owner has allowed\\n * to `_spender`.\\n * @param _owner The address which owns the funds.\\n * @param _spender The address which will spend the funds.\\n * @return The number of tokens still available for the _spender.\\n */\\n function allowance(address _owner, address _spender)\\n external\\n view\\n returns (uint256)\\n {\\n return allowances[_owner][_spender];\\n }\\n\\n /**\\n * @notice Approve the passed address to spend the specified amount of\\n * tokens on behalf of msg.sender.\\n * @param _spender The address which will spend the funds.\\n * @param _value The amount of tokens to be spent.\\n * @return true on success.\\n */\\n function approve(address _spender, uint256 _value) external returns (bool) {\\n allowances[msg.sender][_spender] = _value;\\n emit Approval(msg.sender, _spender, _value);\\n return true;\\n }\\n\\n /**\\n * @notice Creates `_amount` tokens and assigns them to `_account`,\\n * increasing the total supply.\\n */\\n function mint(address _account, uint256 _amount) external onlyVault {\\n require(_account != address(0), \\\"Mint to the zero address\\\");\\n\\n // Account\\n (\\n int256 toRebasingCreditsDiff,\\n int256 toNonRebasingSupplyDiff\\n ) = _adjustAccount(_account, _amount.toInt256());\\n // Globals\\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\\n totalSupply = totalSupply + _amount;\\n\\n require(totalSupply < MAX_SUPPLY, \\\"Max supply\\\");\\n emit Transfer(address(0), _account, _amount);\\n }\\n\\n /**\\n * @notice Destroys `_amount` tokens from `_account`,\\n * reducing the total supply.\\n */\\n function burn(address _account, uint256 _amount) external onlyVault {\\n require(_account != address(0), \\\"Burn from the zero address\\\");\\n if (_amount == 0) {\\n return;\\n }\\n\\n // Account\\n (\\n int256 toRebasingCreditsDiff,\\n int256 toNonRebasingSupplyDiff\\n ) = _adjustAccount(_account, -_amount.toInt256());\\n // Globals\\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\\n totalSupply = totalSupply - _amount;\\n\\n emit Transfer(_account, address(0), _amount);\\n }\\n\\n /**\\n * @dev Get the credits per token for an account. Returns a fixed amount\\n * if the account is non-rebasing.\\n * @param _account Address of the account.\\n */\\n function _creditsPerToken(address _account)\\n internal\\n view\\n returns (uint256)\\n {\\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\\n _account\\n ];\\n if (alternativeCreditsPerTokenMem != 0) {\\n return alternativeCreditsPerTokenMem;\\n } else {\\n return rebasingCreditsPerToken_;\\n }\\n }\\n\\n /**\\n * @dev Auto migrate contracts to be non rebasing,\\n * unless they have opted into yield.\\n * @param _account Address of the account.\\n */\\n function _autoMigrate(address _account) internal {\\n uint256 codeLen = _account.code.length;\\n bool isEOA = (codeLen == 0) ||\\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\\n // In previous code versions, contracts would not have had their\\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\\n // therefore we check the actual accounting used on the account as well.\\n if (\\n (!isEOA) &&\\n rebaseState[_account] == RebaseOptions.NotSet &&\\n alternativeCreditsPerToken[_account] == 0\\n ) {\\n _rebaseOptOut(_account);\\n }\\n }\\n\\n /**\\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\\n * also balance that corresponds to those credits. The latter is important\\n * when adjusting the contract's global nonRebasingSupply to circumvent any\\n * possible rounding errors.\\n *\\n * @param _balance Balance of the account.\\n */\\n function _balanceToRebasingCredits(uint256 _balance)\\n internal\\n view\\n returns (uint256 rebasingCredits)\\n {\\n // Rounds up, because we need to ensure that accounts always have\\n // at least the balance that they should have.\\n // Note this should always be used on an absolute account value,\\n // not on a possibly negative diff, because then the rounding would be wrong.\\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\\n }\\n\\n /**\\n * @notice The calling account will start receiving yield after a successful call.\\n * @param _account Address of the account.\\n */\\n function governanceRebaseOptIn(address _account) external onlyGovernor {\\n require(_account != address(0), \\\"Zero address not allowed\\\");\\n _rebaseOptIn(_account);\\n }\\n\\n /**\\n * @notice The calling account will start receiving yield after a successful call.\\n */\\n function rebaseOptIn() external {\\n _rebaseOptIn(msg.sender);\\n }\\n\\n function _rebaseOptIn(address _account) internal {\\n uint256 balance = balanceOf(_account);\\n\\n // prettier-ignore\\n require(\\n alternativeCreditsPerToken[_account] > 0 ||\\n // Accounts may explicitly `rebaseOptIn` regardless of\\n // accounting if they have a 0 balance.\\n creditBalances[_account] == 0\\n ,\\n \\\"Account must be non-rebasing\\\"\\n );\\n RebaseOptions state = rebaseState[_account];\\n // prettier-ignore\\n require(\\n state == RebaseOptions.StdNonRebasing ||\\n state == RebaseOptions.NotSet,\\n \\\"Only standard non-rebasing accounts can opt in\\\"\\n );\\n\\n uint256 newCredits = _balanceToRebasingCredits(balance);\\n\\n // Account\\n rebaseState[_account] = RebaseOptions.StdRebasing;\\n alternativeCreditsPerToken[_account] = 0;\\n creditBalances[_account] = newCredits;\\n // Globals\\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\\n\\n emit AccountRebasingEnabled(_account);\\n }\\n\\n /**\\n * @notice The calling account will no longer receive yield\\n */\\n function rebaseOptOut() external {\\n _rebaseOptOut(msg.sender);\\n }\\n\\n function _rebaseOptOut(address _account) internal {\\n require(\\n alternativeCreditsPerToken[_account] == 0,\\n \\\"Account must be rebasing\\\"\\n );\\n RebaseOptions state = rebaseState[_account];\\n require(\\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\\n \\\"Only standard rebasing accounts can opt out\\\"\\n );\\n\\n uint256 oldCredits = creditBalances[_account];\\n uint256 balance = balanceOf(_account);\\n\\n // Account\\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\\n alternativeCreditsPerToken[_account] = 1e18;\\n creditBalances[_account] = balance;\\n // Globals\\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\\n\\n emit AccountRebasingDisabled(_account);\\n }\\n\\n /**\\n * @notice Distribute yield to users. This changes the exchange rate\\n * between \\\"credits\\\" and OUSD tokens to change rebasing user's balances.\\n * @param _newTotalSupply New total supply of OUSD.\\n */\\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\\n require(totalSupply > 0, \\\"Cannot increase 0 supply\\\");\\n\\n if (totalSupply == _newTotalSupply) {\\n emit TotalSupplyUpdatedHighres(\\n totalSupply,\\n rebasingCredits_,\\n rebasingCreditsPerToken_\\n );\\n return;\\n }\\n\\n totalSupply = _newTotalSupply > MAX_SUPPLY\\n ? MAX_SUPPLY\\n : _newTotalSupply;\\n\\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\\n // round up in the favour of the protocol\\n rebasingCreditsPerToken_ =\\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\\n rebasingSupply;\\n\\n require(rebasingCreditsPerToken_ > 0, \\\"Invalid change in supply\\\");\\n\\n emit TotalSupplyUpdatedHighres(\\n totalSupply,\\n rebasingCredits_,\\n rebasingCreditsPerToken_\\n );\\n }\\n\\n /*\\n * @notice Send the yield from one account to another account.\\n * Each account keeps its own balances.\\n */\\n function delegateYield(address _from, address _to)\\n external\\n onlyGovernorOrStrategist\\n {\\n require(_from != address(0), \\\"Zero from address not allowed\\\");\\n require(_to != address(0), \\\"Zero to address not allowed\\\");\\n\\n require(_from != _to, \\\"Cannot delegate to self\\\");\\n require(\\n yieldFrom[_to] == address(0) &&\\n yieldTo[_to] == address(0) &&\\n yieldFrom[_from] == address(0) &&\\n yieldTo[_from] == address(0),\\n \\\"Blocked by existing yield delegation\\\"\\n );\\n RebaseOptions stateFrom = rebaseState[_from];\\n RebaseOptions stateTo = rebaseState[_to];\\n\\n require(\\n stateFrom == RebaseOptions.NotSet ||\\n stateFrom == RebaseOptions.StdNonRebasing ||\\n stateFrom == RebaseOptions.StdRebasing,\\n \\\"Invalid rebaseState from\\\"\\n );\\n\\n require(\\n stateTo == RebaseOptions.NotSet ||\\n stateTo == RebaseOptions.StdNonRebasing ||\\n stateTo == RebaseOptions.StdRebasing,\\n \\\"Invalid rebaseState to\\\"\\n );\\n\\n if (alternativeCreditsPerToken[_from] == 0) {\\n _rebaseOptOut(_from);\\n }\\n if (alternativeCreditsPerToken[_to] > 0) {\\n _rebaseOptIn(_to);\\n }\\n\\n uint256 fromBalance = balanceOf(_from);\\n uint256 toBalance = balanceOf(_to);\\n uint256 oldToCredits = creditBalances[_to];\\n uint256 newToCredits = _balanceToRebasingCredits(\\n fromBalance + toBalance\\n );\\n\\n // Set up the bidirectional links\\n yieldTo[_from] = _to;\\n yieldFrom[_to] = _from;\\n\\n // Local\\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\\n alternativeCreditsPerToken[_from] = 1e18;\\n creditBalances[_from] = fromBalance;\\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\\n creditBalances[_to] = newToCredits;\\n\\n // Global\\n int256 creditsChange = newToCredits.toInt256() -\\n oldToCredits.toInt256();\\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\\n emit YieldDelegated(_from, _to);\\n }\\n\\n /*\\n * @notice Stop sending the yield from one account to another account.\\n */\\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\\n // Require a delegation, which will also ensure a valid delegation\\n require(yieldTo[_from] != address(0), \\\"Zero address not allowed\\\");\\n\\n address to = yieldTo[_from];\\n uint256 fromBalance = balanceOf(_from);\\n uint256 toBalance = balanceOf(to);\\n uint256 oldToCredits = creditBalances[to];\\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\\n\\n // Remove the bidirectional links\\n yieldFrom[to] = address(0);\\n yieldTo[_from] = address(0);\\n\\n // Local\\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\\n creditBalances[_from] = fromBalance;\\n rebaseState[to] = RebaseOptions.StdRebasing;\\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\\n creditBalances[to] = newToCredits;\\n\\n // Global\\n int256 creditsChange = newToCredits.toInt256() -\\n oldToCredits.toInt256();\\n _adjustGlobals(creditsChange, fromBalance.toInt256());\\n emit YieldUndelegated(_from, to);\\n }\\n}\\n\",\"keccak256\":\"0x73439bef6569f5adf6f5ce2cb54a5f0d3109d4819457532236e172a7091980a9\",\"license\":\"BUSL-1.1\"},\"contracts/utils/Helpers.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { IBasicToken } from \\\"../interfaces/IBasicToken.sol\\\";\\n\\nlibrary Helpers {\\n /**\\n * @notice Fetch the `symbol()` from an ERC20 token\\n * @dev Grabs the `symbol()` from a contract\\n * @param _token Address of the ERC20 token\\n * @return string Symbol of the ERC20 token\\n */\\n function getSymbol(address _token) internal view returns (string memory) {\\n string memory symbol = IBasicToken(_token).symbol();\\n return symbol;\\n }\\n\\n /**\\n * @notice Fetch the `decimals()` from an ERC20 token\\n * @dev Grabs the `decimals()` from a contract and fails if\\n * the decimal value does not live within a certain range\\n * @param _token Address of the ERC20 token\\n * @return uint256 Decimals of the ERC20 token\\n */\\n function getDecimals(address _token) internal view returns (uint256) {\\n uint256 decimals = IBasicToken(_token).decimals();\\n require(\\n decimals >= 4 && decimals <= 18,\\n \\\"Token must have sufficient decimal places\\\"\\n );\\n\\n return decimals;\\n }\\n}\\n\",\"keccak256\":\"0x4366f8d90b34c1eef8bbaaf369b1e5cd59f04027bb3c111f208eaee65bbc0346\",\"license\":\"BUSL-1.1\"},\"contracts/utils/Initializable.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base contract any contracts that need to initialize state after deployment.\\n * @author Origin Protocol Inc\\n */\\nabstract contract Initializable {\\n /**\\n * @dev Indicates that the contract has been initialized.\\n */\\n bool private initialized;\\n\\n /**\\n * @dev Indicates that the contract is in the process of being initialized.\\n */\\n bool private initializing;\\n\\n /**\\n * @dev Modifier to protect an initializer function from being invoked twice.\\n */\\n modifier initializer() {\\n require(\\n initializing || !initialized,\\n \\\"Initializable: contract is already initialized\\\"\\n );\\n\\n bool isTopLevelCall = !initializing;\\n if (isTopLevelCall) {\\n initializing = true;\\n initialized = true;\\n }\\n\\n _;\\n\\n if (isTopLevelCall) {\\n initializing = false;\\n }\\n }\\n\\n uint256[50] private ______gap;\\n}\\n\",\"keccak256\":\"0x50d39ebf38a3d3111f2b77a6c75ece1d4ae731552fec4697ab16fcf6c0d4d5e8\",\"license\":\"BUSL-1.1\"},\"contracts/utils/InitializableAbstractStrategy.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base contract for vault strategies.\\n * @author Origin Protocol Inc\\n */\\nimport { IERC20 } from \\\"@openzeppelin/contracts/token/ERC20/IERC20.sol\\\";\\nimport { SafeERC20 } from \\\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\\\";\\n\\nimport { Initializable } from \\\"../utils/Initializable.sol\\\";\\nimport { Governable } from \\\"../governance/Governable.sol\\\";\\nimport { IVault } from \\\"../interfaces/IVault.sol\\\";\\n\\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\\n using SafeERC20 for IERC20;\\n\\n event PTokenAdded(address indexed _asset, address _pToken);\\n event PTokenRemoved(address indexed _asset, address _pToken);\\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\\n event RewardTokenCollected(\\n address recipient,\\n address rewardToken,\\n uint256 amount\\n );\\n event RewardTokenAddressesUpdated(\\n address[] _oldAddresses,\\n address[] _newAddresses\\n );\\n event HarvesterAddressesUpdated(\\n address _oldHarvesterAddress,\\n address _newHarvesterAddress\\n );\\n\\n /// @notice Address of the underlying platform\\n address public immutable platformAddress;\\n /// @notice Address of the OToken vault\\n address public immutable vaultAddress;\\n\\n /// @dev Replaced with an immutable variable\\n // slither-disable-next-line constable-states\\n address private _deprecated_platformAddress;\\n\\n /// @dev Replaced with an immutable\\n // slither-disable-next-line constable-states\\n address private _deprecated_vaultAddress;\\n\\n /// @notice asset => pToken (Platform Specific Token Address)\\n mapping(address => address) public assetToPToken;\\n\\n /// @notice Full list of all assets supported by the strategy\\n address[] internal assetsMapped;\\n\\n // Deprecated: Reward token address\\n // slither-disable-next-line constable-states\\n address private _deprecated_rewardTokenAddress;\\n\\n // Deprecated: now resides in Harvester's rewardTokenConfigs\\n // slither-disable-next-line constable-states\\n uint256 private _deprecated_rewardLiquidationThreshold;\\n\\n /// @notice Address of the Harvester contract allowed to collect reward tokens\\n address public harvesterAddress;\\n\\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\\n address[] public rewardTokenAddresses;\\n\\n /* Reserved for future expansion. Used to be 100 storage slots\\n * and has decreased to accommodate:\\n * - harvesterAddress\\n * - rewardTokenAddresses\\n */\\n int256[98] private _reserved;\\n\\n struct BaseStrategyConfig {\\n address platformAddress; // Address of the underlying platform\\n address vaultAddress; // Address of the OToken's Vault\\n }\\n\\n /**\\n * @dev Verifies that the caller is the Governor or Strategist.\\n */\\n modifier onlyGovernorOrStrategist() {\\n require(\\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\\n \\\"Caller is not the Strategist or Governor\\\"\\n );\\n _;\\n }\\n\\n /**\\n * @param _config The platform and OToken vault addresses\\n */\\n constructor(BaseStrategyConfig memory _config) {\\n platformAddress = _config.platformAddress;\\n vaultAddress = _config.vaultAddress;\\n }\\n\\n /**\\n * @dev Internal initialize function, to set up initial internal state\\n * @param _rewardTokenAddresses Address of reward token for platform\\n * @param _assets Addresses of initial supported assets\\n * @param _pTokens Platform Token corresponding addresses\\n */\\n function _initialize(\\n address[] memory _rewardTokenAddresses,\\n address[] memory _assets,\\n address[] memory _pTokens\\n ) internal {\\n rewardTokenAddresses = _rewardTokenAddresses;\\n\\n uint256 assetCount = _assets.length;\\n require(assetCount == _pTokens.length, \\\"Invalid input arrays\\\");\\n for (uint256 i = 0; i < assetCount; ++i) {\\n _setPTokenAddress(_assets[i], _pTokens[i]);\\n }\\n }\\n\\n /**\\n * @notice Collect accumulated reward token and send to Vault.\\n */\\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\\n _collectRewardTokens();\\n }\\n\\n /**\\n * @dev Default implementation that transfers reward tokens to the Harvester\\n * Implementing strategies need to add custom logic to collect the rewards.\\n */\\n function _collectRewardTokens() internal virtual {\\n uint256 rewardTokenCount = rewardTokenAddresses.length;\\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\\n uint256 balance = rewardToken.balanceOf(address(this));\\n if (balance > 0) {\\n emit RewardTokenCollected(\\n harvesterAddress,\\n address(rewardToken),\\n balance\\n );\\n rewardToken.safeTransfer(harvesterAddress, balance);\\n }\\n }\\n }\\n\\n /**\\n * @dev Verifies that the caller is the Vault.\\n */\\n modifier onlyVault() {\\n require(msg.sender == vaultAddress, \\\"Caller is not the Vault\\\");\\n _;\\n }\\n\\n /**\\n * @dev Verifies that the caller is the Harvester.\\n */\\n modifier onlyHarvester() {\\n require(msg.sender == harvesterAddress, \\\"Caller is not the Harvester\\\");\\n _;\\n }\\n\\n /**\\n * @dev Verifies that the caller is the Vault or Governor.\\n */\\n modifier onlyVaultOrGovernor() {\\n require(\\n msg.sender == vaultAddress || msg.sender == governor(),\\n \\\"Caller is not the Vault or Governor\\\"\\n );\\n _;\\n }\\n\\n /**\\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\\n */\\n modifier onlyVaultOrGovernorOrStrategist() {\\n require(\\n msg.sender == vaultAddress ||\\n msg.sender == governor() ||\\n msg.sender == IVault(vaultAddress).strategistAddr(),\\n \\\"Caller is not the Vault, Governor, or Strategist\\\"\\n );\\n _;\\n }\\n\\n /**\\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\\n * @param _rewardTokenAddresses Array of reward token addresses\\n */\\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\\n external\\n onlyGovernor\\n {\\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\\n require(\\n _rewardTokenAddresses[i] != address(0),\\n \\\"Can not set an empty address as a reward token\\\"\\n );\\n }\\n\\n emit RewardTokenAddressesUpdated(\\n rewardTokenAddresses,\\n _rewardTokenAddresses\\n );\\n rewardTokenAddresses = _rewardTokenAddresses;\\n }\\n\\n /**\\n * @notice Get the reward token addresses.\\n * @return address[] the reward token addresses.\\n */\\n function getRewardTokenAddresses()\\n external\\n view\\n returns (address[] memory)\\n {\\n return rewardTokenAddresses;\\n }\\n\\n /**\\n * @notice Provide support for asset by passing its pToken address.\\n * This method can only be called by the system Governor\\n * @param _asset Address for the asset\\n * @param _pToken Address for the corresponding platform token\\n */\\n function setPTokenAddress(address _asset, address _pToken)\\n external\\n virtual\\n onlyGovernor\\n {\\n _setPTokenAddress(_asset, _pToken);\\n }\\n\\n /**\\n * @notice Remove a supported asset by passing its index.\\n * This method can only be called by the system Governor\\n * @param _assetIndex Index of the asset to be removed\\n */\\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\\n require(_assetIndex < assetsMapped.length, \\\"Invalid index\\\");\\n address asset = assetsMapped[_assetIndex];\\n address pToken = assetToPToken[asset];\\n\\n if (_assetIndex < assetsMapped.length - 1) {\\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\\n }\\n assetsMapped.pop();\\n assetToPToken[asset] = address(0);\\n\\n emit PTokenRemoved(asset, pToken);\\n }\\n\\n /**\\n * @notice Provide support for asset by passing its pToken address.\\n * Add to internal mappings and execute the platform specific,\\n * abstract method `_abstractSetPToken`\\n * @param _asset Address for the asset\\n * @param _pToken Address for the corresponding platform token\\n */\\n function _setPTokenAddress(address _asset, address _pToken) internal {\\n require(assetToPToken[_asset] == address(0), \\\"pToken already set\\\");\\n require(\\n _asset != address(0) && _pToken != address(0),\\n \\\"Invalid addresses\\\"\\n );\\n\\n assetToPToken[_asset] = _pToken;\\n assetsMapped.push(_asset);\\n\\n emit PTokenAdded(_asset, _pToken);\\n\\n _abstractSetPToken(_asset, _pToken);\\n }\\n\\n /**\\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\\n * strategy contracts, i.e. mistaken sends.\\n * @param _asset Address for the asset\\n * @param _amount Amount of the asset to transfer\\n */\\n function transferToken(address _asset, uint256 _amount)\\n public\\n virtual\\n onlyGovernor\\n {\\n require(!supportsAsset(_asset), \\\"Cannot transfer supported asset\\\");\\n IERC20(_asset).safeTransfer(governor(), _amount);\\n }\\n\\n /**\\n * @notice Set the Harvester contract that can collect rewards.\\n * @param _harvesterAddress Address of the harvester contract.\\n */\\n function setHarvesterAddress(address _harvesterAddress)\\n external\\n onlyGovernor\\n {\\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\\n harvesterAddress = _harvesterAddress;\\n }\\n\\n /***************************************\\n Abstract\\n ****************************************/\\n\\n function _abstractSetPToken(address _asset, address _pToken)\\n internal\\n virtual;\\n\\n function safeApproveAllTokens() external virtual;\\n\\n /**\\n * @notice Deposit an amount of assets into the platform\\n * @param _asset Address for the asset\\n * @param _amount Units of asset to deposit\\n */\\n function deposit(address _asset, uint256 _amount) external virtual;\\n\\n /**\\n * @notice Deposit all supported assets in this strategy contract to the platform\\n */\\n function depositAll() external virtual;\\n\\n /**\\n * @notice Withdraw an `amount` of assets from the platform and\\n * send to the `_recipient`.\\n * @param _recipient Address to which the asset should be sent\\n * @param _asset Address of the asset\\n * @param _amount Units of asset to withdraw\\n */\\n function withdraw(\\n address _recipient,\\n address _asset,\\n uint256 _amount\\n ) external virtual;\\n\\n /**\\n * @notice Withdraw all supported assets from platform and\\n * sends to the OToken's Vault.\\n */\\n function withdrawAll() external virtual;\\n\\n /**\\n * @notice Get the total asset value held in the platform.\\n * This includes any interest that was generated since depositing.\\n * @param _asset Address of the asset\\n * @return balance Total value of the asset in the platform\\n */\\n function checkBalance(address _asset)\\n external\\n view\\n virtual\\n returns (uint256 balance);\\n\\n /**\\n * @notice Check if an asset is supported.\\n * @param _asset Address of the asset\\n * @return bool Whether asset is supported\\n */\\n function supportsAsset(address _asset) public view virtual returns (bool);\\n}\\n\",\"keccak256\":\"0x0160d435384d75e8764f4a916764ba47c87fda46872ca5900d46e5e80e956ff9\",\"license\":\"BUSL-1.1\"},\"contracts/vault/VaultStorage.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title OToken VaultStorage contract\\n * @notice The VaultStorage contract defines the storage for the Vault contracts\\n * @author Origin Protocol Inc\\n */\\n\\nimport { IERC20 } from \\\"@openzeppelin/contracts/token/ERC20/IERC20.sol\\\";\\nimport { SafeERC20 } from \\\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\\\";\\nimport { Address } from \\\"@openzeppelin/contracts/utils/Address.sol\\\";\\n\\nimport { IStrategy } from \\\"../interfaces/IStrategy.sol\\\";\\nimport { Governable } from \\\"../governance/Governable.sol\\\";\\nimport { OUSD } from \\\"../token/OUSD.sol\\\";\\nimport { Initializable } from \\\"../utils/Initializable.sol\\\";\\nimport \\\"../utils/Helpers.sol\\\";\\n\\ncontract VaultStorage is Initializable, Governable {\\n using SafeERC20 for IERC20;\\n\\n event AssetSupported(address _asset);\\n event AssetRemoved(address _asset);\\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\\n event StrategyApproved(address _addr);\\n event StrategyRemoved(address _addr);\\n event Mint(address _addr, uint256 _value);\\n event Redeem(address _addr, uint256 _value);\\n event CapitalPaused();\\n event CapitalUnpaused();\\n event RebasePaused();\\n event RebaseUnpaused();\\n event VaultBufferUpdated(uint256 _vaultBuffer);\\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\\n event PriceProviderUpdated(address _priceProvider);\\n event AllocateThresholdUpdated(uint256 _threshold);\\n event RebaseThresholdUpdated(uint256 _threshold);\\n event StrategistUpdated(address _address);\\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\\n event TrusteeFeeBpsChanged(uint256 _basis);\\n event TrusteeAddressChanged(address _address);\\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\\n event SwapperChanged(address _address);\\n event SwapAllowedUndervalueChanged(uint256 _basis);\\n event SwapSlippageChanged(address _asset, uint256 _basis);\\n event Swapped(\\n address indexed _fromAsset,\\n address indexed _toAsset,\\n uint256 _fromAssetAmount,\\n uint256 _toAssetAmount\\n );\\n event StrategyAddedToMintWhitelist(address indexed strategy);\\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\\n event DripDurationChanged(uint256 dripDuration);\\n event WithdrawalRequested(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount,\\n uint256 _queued\\n );\\n event WithdrawalClaimed(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount\\n );\\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\\n\\n // Since we are proxy, all state should be uninitalized.\\n // Since this storage contract does not have logic directly on it\\n // we should not be checking for to see if these variables can be constant.\\n // slither-disable-start uninitialized-state\\n // slither-disable-start constable-states\\n\\n // Assets supported by the Vault, i.e. Stablecoins\\n enum UnitConversion {\\n DECIMALS,\\n GETEXCHANGERATE\\n }\\n // Changed to fit into a single storage slot so the decimals needs to be recached\\n struct Asset {\\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\\n // redeeming or checking balance of assets.\\n bool isSupported;\\n UnitConversion unitConversion;\\n uint8 decimals;\\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\\n // For example 40 == 0.4% slippage\\n uint16 allowedOracleSlippageBps;\\n }\\n\\n /// @dev mapping of supported vault assets to their configuration\\n mapping(address => Asset) internal assets;\\n /// @dev list of all assets supported by the vault.\\n address[] internal allAssets;\\n\\n // Strategies approved for use by the Vault\\n struct Strategy {\\n bool isSupported;\\n uint256 _deprecated; // Deprecated storage slot\\n }\\n /// @dev mapping of strategy contracts to their configuration\\n mapping(address => Strategy) public strategies;\\n /// @dev list of all vault strategies\\n address[] internal allStrategies;\\n\\n /// @notice Address of the Oracle price provider contract\\n address public priceProvider;\\n /// @notice pause rebasing if true\\n bool public rebasePaused;\\n /// @notice pause operations that change the OToken supply.\\n /// eg mint, redeem, allocate, mint/burn for strategy\\n bool public capitalPaused;\\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\\n uint256 public redeemFeeBps;\\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\\n uint256 public vaultBuffer;\\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\\n uint256 public autoAllocateThreshold;\\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\\n uint256 public rebaseThreshold;\\n\\n /// @dev Address of the OToken token. eg OUSD or OETH.\\n OUSD public oUSD;\\n\\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\\n // keccak256(\\\"OUSD.vault.governor.admin.impl\\\");\\n bytes32 public constant adminImplPosition =\\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\\n\\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\\n address private _deprecated_rebaseHooksAddr = address(0);\\n\\n /// @dev Deprecated: Address of Uniswap\\n address private _deprecated_uniswapAddr = address(0);\\n\\n /// @notice Address of the Strategist\\n address public strategistAddr = address(0);\\n\\n /// @notice Mapping of asset address to the Strategy that they should automatically\\n // be allocated to\\n mapping(address => address) public assetDefaultStrategies;\\n\\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\\n uint256 public maxSupplyDiff;\\n\\n /// @notice Trustee contract that can collect a percentage of yield\\n address public trusteeAddress;\\n\\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\\n uint256 public trusteeFeeBps;\\n\\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\\n address[] private _deprecated_swapTokens;\\n\\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\\n\\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\\n\\n address public ousdMetaStrategy;\\n\\n /// @notice How much OTokens are currently minted by the strategy\\n int256 public netOusdMintedForStrategy;\\n\\n /// @notice How much net total OTokens are allowed to be minted by all strategies\\n uint256 public netOusdMintForStrategyThreshold;\\n\\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\\n\\n /// @notice Collateral swap configuration.\\n /// @dev is packed into a single storage slot to save gas.\\n struct SwapConfig {\\n // Contract that swaps the vault's collateral assets\\n address swapper;\\n // Max allowed percentage the total value can drop below the total supply in basis points.\\n // For example 100 == 1%\\n uint16 allowedUndervalueBps;\\n }\\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\\n\\n // List of strategies that can mint oTokens directly\\n // Used in OETHBaseVaultCore\\n mapping(address => bool) public isMintWhitelistedStrategy;\\n\\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\\n address private _deprecated_dripper;\\n\\n /// Withdrawal Queue Storage /////\\n\\n struct WithdrawalQueueMetadata {\\n // cumulative total of all withdrawal requests included the ones that have already been claimed\\n uint128 queued;\\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\\n uint128 claimable;\\n // total of all the requests that have been claimed\\n uint128 claimed;\\n // index of the next withdrawal request starting at 0\\n uint128 nextWithdrawalIndex;\\n }\\n\\n /// @notice Global metadata for the withdrawal queue including:\\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\\n /// claimed - total of all the requests that have been claimed\\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\\n\\n struct WithdrawalRequest {\\n address withdrawer;\\n bool claimed;\\n uint40 timestamp; // timestamp of the withdrawal request\\n // Amount of oTokens to redeem. eg OETH\\n uint128 amount;\\n // cumulative total of all withdrawal requests including this one.\\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\\n uint128 queued;\\n }\\n\\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\\n\\n /// @notice Sets a minimum delay that is required to elapse between\\n /// requesting async withdrawals and claiming the request.\\n /// When set to 0 async withdrawals are disabled.\\n uint256 public withdrawalClaimDelay;\\n\\n /// @notice Time in seconds that the vault last rebased yield.\\n uint64 public lastRebase;\\n\\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\\n uint64 public dripDuration;\\n\\n /// @notice max rebase percentage per second\\n /// Can be used to set maximum yield of the protocol,\\n /// spreading out yield over time\\n uint64 public rebasePerSecondMax;\\n\\n /// @notice target rebase rate limit, based on past rates and funds available.\\n uint64 public rebasePerSecondTarget;\\n\\n uint256 internal constant MAX_REBASE = 0.02 ether;\\n uint256 internal constant MAX_REBASE_PER_SECOND =\\n uint256(0.05 ether) / 1 days;\\n\\n // For future use\\n uint256[43] private __gap;\\n\\n // slither-disable-end constable-states\\n // slither-disable-end uninitialized-state\\n\\n /**\\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\\n * @param newImpl address of the implementation\\n */\\n function setAdminImpl(address newImpl) external onlyGovernor {\\n require(\\n Address.isContract(newImpl),\\n \\\"new implementation is not a contract\\\"\\n );\\n bytes32 position = adminImplPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newImpl)\\n }\\n }\\n}\\n\",\"keccak256\":\"0xe8c1056879e4d67e0085a30a525a4cb23b954ade0f22fce502278f35b9c69d3b\",\"license\":\"BUSL-1.1\"}},\"version\":1}", + "bytecode": "0x6101a060405234801561001157600080fd5b506040516163af3803806163af833981016040819052610030916101ac565b60208701516033805460ff191690556001600160a01b0380881660805280851660a05280861660c05280821660e0528316610100526001600160401b03821661012081905288918891869088908790879042116100d35760405162461bcd60e51b815260206004820152601960248201527f496e76616c69642067656e657369732074696d657374616d7000000000000000604482015260640160405180910390fd5b505084516001600160a01b0390811661014052602090950151851661016052505050508516610180526101066000610112565b50505050505050610289565b6001600160a01b03811661013260008051602061638f8339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a360008051602061638f83398151915255565b80516001600160a01b038116811461019057600080fd5b919050565b80516001600160401b038116811461019057600080fd5b60008060008060008060008789036101008112156101c957600080fd5b60408112156101d757600080fd5b50604080519081016001600160401b038111828210171561020857634e487b7160e01b600052604160045260246000fd5b60405261021489610179565b815261022260208a01610179565b6020820152965061023560408901610179565b955061024360608901610179565b945061025160808901610179565b935061025f60a08901610179565b925061026d60c08901610179565b915061027b60e08901610195565b905092959891949750929550565b60805160a05160c05160e0516101005161012051610140516101605161018051615fb96103d66000396000818161037501526134da01526000818161054301528181612302015281816131d40152818161334c015281816139620152613a4601526000610a0e015260008181611f840152613c6601526000818161074d01528181610de701528181610e8b0152818161132c0152818161145f015281816117ec015281816118a901528181611fce0152612b8b015260005050600081816107eb01528181613081015281816134aa0152818161358501526137330152600061220e0152600081816108b70152818161115601528181611bcd01528181612377015281816128ce01528181612932015281816132c2015281816139d701528181613ad101528181613b7c015281816140590152818161460701528181614a2b0152614af80152615fb96000f3fe6080604052600436106103035760003560e01c806371a735f311610190578063b6e2b520116100dc578063d38bfff411610095578063dbe55e561161006f578063dbe55e56146109fc578063de5f626814610a30578063f6ca71b014610a45578063f7b188a514610a6757600080fd5b8063d38bfff4146109a7578063d79e4032146109c7578063d9caed12146109dc57600080fd5b8063b6e2b520146108fc578063b8ec66781461091c578063bb1b918d1461093c578063c2e1e3f41461095c578063c7af33521461097c578063d059f6ef1461099157600080fd5b80639136616a1161014957806398245f1b1161012357806398245f1b1461082d578063a5f5be5414610879578063aa388af61461089a578063ad1728cb146108e757600080fd5b80639136616a146107be57806391649751146107d957806396d538bb1461080d57600080fd5b806371a735f3146106fb5780637b2d9b2c1461071b5780637da9982a1461073b5780638456cb591461076f578063853828b61461078457806387bae8671461079957600080fd5b806347e7ef241161024f5780635c975abb1161020857806367c7066c116101e257806367c7066c146106865780636874469d146106a65780636c341d1a146106bb5780636e811d38146106db57600080fd5b80635c975abb1461062d5780635d36b190146106515780635f5152261461066657600080fd5b806347e7ef24146105a55780634896b31a146105c55780634c84e6f8146105da578063522e4245146105ef57806359ff4158146106025780635a063f631461061857600080fd5b80631072cbea116102bc5780633d4dff7b116102965780633d4dff7b146104bd578063430bf08a14610531578063435356d1146105655780634583ef101461058557600080fd5b80631072cbea1461041b5780631a1a15711461043b57806325e2e9f31461045b57600080fd5b80630c340a241461030f5780630d304174146103415780630df1ecfd146103635780630ed57b3a146103975780630ef99855146103b75780630fc3b4c4146103e557600080fd5b3661030a57005b600080fd5b34801561031b57600080fd5b50610324610a7c565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561034d57600080fd5b5061036161035c366004614d4d565b610a99565b005b34801561036f57600080fd5b506103247f000000000000000000000000000000000000000000000000000000000000000081565b3480156103a357600080fd5b506103616103b2366004614de5565b6110c8565b3480156103c357600080fd5b506103d76103d2366004614e18565b611107565b604051908152602001610338565b3480156103f157600080fd5b50610324610400366004614e31565b6067602052600090815260409020546001600160a01b031681565b34801561042757600080fd5b50610361610436366004614e4c565b611128565b34801561044757600080fd5b50610361610456366004614e88565b6111e7565b34801561046757600080fd5b5060385460395461049191906001600160401b03811690600160401b90046001600160801b031683565b604080519384526001600160401b0390921660208401526001600160801b031690820152606001610338565b3480156104c957600080fd5b506105206104d8366004614e18565b603460205260009081526040902080546001909101546001600160401b0380821691600160401b810490911690600160801b810463ffffffff1690600160a01b900460ff1685565b604051610338959493929190614f05565b34801561053d57600080fd5b506103247f000000000000000000000000000000000000000000000000000000000000000081565b34801561057157600080fd5b5061036161058036600461502c565b611a5d565b34801561059157600080fd5b506103616105a03660046150bd565b611b4a565b3480156105b157600080fd5b506103616105c0366004614e4c565b6122f7565b3480156105d157600080fd5b506035546103d7565b3480156105e657600080fd5b50610361612474565b6103616105fd36600461514e565b61251c565b34801561060e57600080fd5b506103d7603a5481565b34801561062457600080fd5b50610361612785565b34801561063957600080fd5b5060335460ff165b6040519015158152602001610338565b34801561065d57600080fd5b50610361612824565b34801561067257600080fd5b506103d7610681366004614e31565b6128ca565b34801561069257600080fd5b50606b54610324906001600160a01b031681565b3480156106b257600080fd5b506103616129b8565b3480156106c757600080fd5b506103616106d63660046151a1565b612afd565b3480156106e757600080fd5b506103616106f6366004614e31565b612ec5565b34801561070757600080fd5b5061036161071636600461527a565b612f3b565b34801561072757600080fd5b50610324610736366004614e18565b61312a565b34801561074757600080fd5b506103247f000000000000000000000000000000000000000000000000000000000000000081565b34801561077b57600080fd5b50610361613154565b34801561079057600080fd5b506103616131c9565b3480156107a557600080fd5b506033546103249061010090046001600160a01b031681565b3480156107ca57600080fd5b506103616103b2366004614e18565b3480156107e557600080fd5b506103247f000000000000000000000000000000000000000000000000000000000000000081565b34801561081957600080fd5b506103616108283660046152ff565b61337c565b34801561083957600080fd5b5061086b610848366004614e18565b60376020526000908152604090205460ff811690610100900464ffffffffff1682565b604051610338929190615340565b34801561088557600080fd5b5060335461064190600160a81b900460ff1681565b3480156108a657600080fd5b506106416108b5366004614e31565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811691161490565b3480156108f357600080fd5b50610361613493565b34801561090857600080fd5b50610361610917366004615417565b61354a565b34801561092857600080fd5b506103d7610937366004614e18565b6135f5565b34801561094857600080fd5b506103616109573660046154c5565b613605565b34801561096857600080fd5b50610361610977366004614e31565b6137f5565b34801561098857600080fd5b50610641613882565b34801561099d57600080fd5b506103d7603b5481565b3480156109b357600080fd5b506103616109c2366004614e31565b6138b3565b3480156109d357600080fd5b506036546103d7565b3480156109e857600080fd5b506103616109f7366004615583565b613957565b348015610a0857600080fd5b506103247f000000000000000000000000000000000000000000000000000000000000000081565b348015610a3c57600080fd5b50610361613a3b565b348015610a5157600080fd5b50610a5a613bd2565b60405161033891906155c0565b348015610a7357600080fd5b50610361613c34565b6000610a94600080516020615f648339815191525490565b905090565b6000848152603460209081526040808320815160a0810183528154815260018201546001600160401b0380821695830195909552600160401b810490941692810192909252600160801b830463ffffffff16606083015290916080830190600160a01b900460ff166002811115610b1257610b12614eef565b6002811115610b2357610b23614eef565b9052508051600090815260376020526040808220815180830190925280549394509192909190829060ff166008811115610b5f57610b5f614eef565b6008811115610b7057610b70614eef565b81529054610100900464ffffffffff166020909101529050600182608001516002811115610ba057610ba0614eef565b14610be85760405162461bcd60e51b81526020600482015260136024820152724465706f736974206e6f742070656e64696e6760681b60448201526064015b60405180910390fd5b610bf5602085018561560c565b6001600160401b0316600003610c4d5760405162461bcd60e51b815260206004820152601d60248201527f5a65726f203173742070656e64696e67206465706f73697420736c6f740000006044820152606401610bdf565b600381516008811115610c6257610c62614eef565b1480610c805750600481516008811115610c7e57610c7e614eef565b145b80610c9d5750600581516008811115610c9b57610c9b614eef565b145b610ce95760405162461bcd60e51b815260206004820152601b60248201527f4e6f742076657269666965642f6163746976652f65786974696e6700000000006044820152606401610bdf565b846001600160401b031682604001516001600160401b031610610d475760405162461bcd60e51b815260206004820152601660248201527514db1bdd081b9bdd0818599d195c8819195c1bdcda5d60521b6044820152606401610bdf565b6039546001600160401b031680610d5d87613c60565b6001600160401b0316111580610d7a57506001600160401b038116155b610dc65760405162461bcd60e51b815260206004820152601e60248201527f4465706f7369742061667465722062616c616e636520736e617073686f7400006044820152606401610bdf565b6000610dd9610dd488613c60565b613ca3565b905060006001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663d98a556483610e1a60208b018b61560c565b610e2760208c018c615627565b6040518563ffffffff1660e01b8152600401610e469493929190615696565b602060405180830381865afa158015610e63573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e8791906156c9565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663334a88fb838660200151896000016020810190610ed1919061560c565b610ede60208c018c615627565b6040518663ffffffff1660e01b8152600401610efe9594939291906156e6565b60006040518083038186803b158015610f1657600080fd5b505afa158015610f2a573d6000803e3d6000fd5b506000925060209150610f4190508982018a61560c565b610f4b9190615732565b90506001600160401b03610f62602089018961560c565b6001600160401b03161480610f9557506001600160401b038116610f89602089018961560c565b6001600160401b031611155b80610f9d5750815b610fe95760405162461bcd60e51b815260206004820152601d60248201527f45786974204465706f736974206c696b656c79206e6f742070726f632e0000006044820152606401610bdf565b610ff6602089018961560c565b6001600160401b031686604001516001600160401b031610806110165750815b6110625760405162461bcd60e51b815260206004820152601c60248201527f4465706f736974206c696b656c79206e6f742070726f636573736564000000006044820152606401610bdf565b61106c8a87613da8565b897fae0e4f727389efd70d748d667436e0264f370ae498b339b713797dbab57b12ff87602001516001600160401b0316633b9aca006110ab919061576e565b60405190815260200160405180910390a250505050505050505050565b60405162461bcd60e51b81526020600482015260146024820152732ab739bab83837b93a32b210333ab731ba34b7b760611b6044820152606401610bdf565b6036818154811061111757600080fd5b600091825260209091200154905081565b611130613882565b61114c5760405162461bcd60e51b8152600401610bdf90615785565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116908316036111c75760405162461bcd60e51b815260206004820152601f60248201527f43616e6e6f74207472616e7366657220737570706f72746564206173736574006044820152606401610bdf565b6111e36111d2610a7c565b6001600160a01b0384169083613e89565b5050565b6040805160608101825260385481526039546001600160401b03811660208301819052600160401b9091046001600160801b031692820192909252906112655760405162461bcd60e51b81526020600482015260136024820152724e6f20736e61707065642062616c616e63657360681b6044820152606401610bdf565b6036546035546000908215611725578261128260608801886157bc565b9050146112ca5760405162461bcd60e51b8152602060048201526016602482015275496e76616c69642062616c616e63652070726f6f667360501b6044820152606401610bdf565b826112d860408801886157bc565b9050146113205760405162461bcd60e51b8152602060048201526016602482015275496e76616c69642062616c616e6365206c656176657360501b6044820152606401610bdf565b83516001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906391ad640d90883561136260208b018b615627565b6040518563ffffffff1660e01b81526004016113819493929190615805565b60006040518083038186803b15801561139957600080fd5b505afa1580156113ad573d6000803e3d6000fd5b5050505060006113bc82613ee0565b9050835b8015611722576113cf81615825565b9050600060376000603684815481106113ea576113ea61583c565b906000526020600020015481526020019081526020016000206040518060400160405290816000820160009054906101000a900460ff16600881111561143257611432614eef565b600881111561144357611443614eef565b81529054610100900464ffffffffff16602090910152905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f34ad34c8b3561149b60408e018e6157bc565b878181106114ab576114ab61583c565b905060200201358d80606001906114c291906157bc565b888181106114d2576114d261583c565b90506020028101906114e49190615627565b87602001516040518663ffffffff1660e01b8152600401611509959493929190615852565b602060405180830381865afa158015611526573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061154a919061588b565b905080600003611682576000805b85518110156115af57603685815481106115745761157461583c565b90600052602060002001548682815181106115915761159161583c565b6020026020010151036115a757600191506115af565b600101611558565b508061167a57600660376000603687815481106115ce576115ce61583c565b600091825260208083209091015483528201929092526040019020805460ff1916600183600881111561160357611603614eef565b02179055506116136001896158a4565b9750603688815481106116285761162861583c565b9060005260206000200154603685815481106116465761164661583c565b6000918252602090912001556036805480611663576116636158b7565b600190038181906000526020600020016000905590555b5050506113c0565b60038251600881111561169757611697614eef565b1480156116a857506407823ff28081115b1561170157600460376000603686815481106116c6576116c661583c565b600091825260208083209091015483528201929092526040019020805460ff191660018360088111156116fb576116fb614eef565b02179055505b61170f81633b9aca0061576e565b61171990876158cd565b955050506113c0565b50505b600081156119c2578161173b60608801886157bc565b9050146117835760405162461bcd60e51b8152602060048201526016602482015275496e76616c6964206465706f7369742070726f6f667360501b6044820152606401610bdf565b8161179160408801886157bc565b9050146117e05760405162461bcd60e51b815260206004820152601760248201527f496e76616c6964206465706f73697420696e64657865730000000000000000006044820152606401610bdf565b84516001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690632b00e79690883561182260208b018b615627565b6040518563ffffffff1660e01b81526004016118419493929190615805565b60006040518083038186803b15801561185957600080fd5b505afa15801561186d573d6000803e3d6000fd5b5050505060005b828110156119c0576000603582815481106118915761189161583c565b60009182526020909120015490506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016638a050dd48935836118de60608d018d6157bc565b878181106118ee576118ee61583c565b90506020028101906119009190615627565b61190d60408f018f6157bc565b8981811061191d5761191d61583c565b905060200201602081019061193291906158e0565b6040518663ffffffff1660e01b81526004016119529594939291906158fb565b60006040518083038186803b15801561196a57600080fd5b505afa15801561197e573d6000803e3d6000fd5b5050506000828152603460205260409020600101546119ab91506001600160401b0316633b9aca0061576e565b6119b590846158cd565b925050600101611874565b505b60408501516001600160801b03166119da84836158cd565b6119e491906158cd565b603a556039805467ffffffffffffffff1916905560208581015160408088015181518581529384018790526001600160801b0316908301526001600160401b0316907fed2528338eefb63fd1860078b91e35106bc25e3fd528634d180f662582fe5ec1906060015b60405180910390a250505050505050565b611a65613882565b611a815760405162461bcd60e51b8152600401610bdf90615785565b600054610100900460ff1680611a9a575060005460ff16155b611afd5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610bdf565b600054610100900460ff16158015611b1f576000805461ffff19166101011790555b611b2a848484613f90565b611b32613493565b8015611b44576000805461ff00191690555b50505050565b60335461010090046001600160a01b03163314611b795760405162461bcd60e51b8152600401610bdf90615933565b60335460ff1615611b9c5760405162461bcd60e51b8152600401610bdf9061595c565b6000611bb56001600160401b038316633b9aca0061576e565b6040516370a0823160e01b81523060048201529091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611c1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c40919061588b565b811115611c835760405162461bcd60e51b8152602060048201526011602482015270092dce6eaccccd2c6d2cadce840ae8aa89607b1b6044820152606401610bdf565b603554600c11611cc45760405162461bcd60e51b815260206004820152600c60248201526b4d6178206465706f7369747360a01b6044820152606401610bdf565b611ccd81614043565b6000611d16611cdc8580615627565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061411292505050565b60008181526037602052604090205490915060ff166001816008811115611d3f57611d3f614eef565b1480611d5c57506003816008811115611d5a57611d5a614eef565b145b80611d7857506004816008811115611d7657611d76614eef565b145b611dc45760405162461bcd60e51b815260206004820152601a60248201527f4e6f742072656769737465726564206f722076657269666965640000000000006044820152606401610bdf565b670de0b6b3a7640000831015611e105760405162461bcd60e51b815260206004820152601160248201527011195c1bdcda5d081d1bdbc81cdb585b1b607a1b6044820152606401610bdf565b6001816008811115611e2457611e24614eef565b03611f5157603354600160a81b900460ff1615611e7c5760405162461bcd60e51b8152602060048201526016602482015275115e1a5cdd1a5b99c8199a5c9cdd0819195c1bdcda5d60521b6044820152606401610bdf565b670de0b6b3a76400008314611ed35760405162461bcd60e51b815260206004820152601c60248201527f496e76616c6964206669727374206465706f73697420616d6f756e74000000006044820152606401610bdf565b603654603090611ee49060016158cd565b10611f225760405162461bcd60e51b815260206004820152600e60248201526d4d61782076616c696461746f727360901b6044820152606401610bdf565b60338054600160a81b60ff60a81b199091161790556000828152603760205260409020805460ff191660021790555b604051600090611f6d90600160f91b9083903090602001615986565b60405160208183030381529060405290506000600c7f0000000000000000000000000000000000000000000000000000000000000000611fac426141cb565b611fb691906159c4565b611fc09190615732565b905060006001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663afe7688786858a61200360208e018e615627565b886040518763ffffffff1660e01b815260040161202596959493929190615a33565b602060405180830381865afa158015612042573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612066919061588b565b905060008082815260346020526040902060010154600160a01b900460ff16600281111561209657612096614eef565b146120d75760405162461bcd60e51b8152602060048201526011602482015270111d5c1b1a58d85d194819195c1bdcda5d607a1b6044820152606401610bdf565b6040518060a00160405280868152602001886001600160401b03168152602001836001600160401b03168152602001612114603580549050614237565b63ffffffff16815260200160019052600082815260346020908152604091829020835181559083015160018201805493850151606086015163ffffffff16600160801b0263ffffffff60801b196001600160401b03928316600160401b026001600160801b03199097169290941691909117949094179182168417815560808501519293909160ff60a01b1990911664ffffffffff60801b1990911617600160a01b8360028111156121c8576121c8614eef565b021790555050603580546001810182556000919091527fcfa4bec1d3298408bb5afcfcd9c430549c5b31f8aa5c5848151c0a55f473c34d01829055506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663228951188761223e8b80615627565b8761224c60208f018f615627565b8f604001356040518863ffffffff1660e01b815260040161227296959493929190615a8b565b6000604051808303818588803b15801561228b57600080fd5b505af115801561229f573d6000803e3d6000fd5b508493508892507faca97428a1d7f2b7c4cee2fbe4feda457e132b404b0c9c3ff73bf7a988d889a891506122d590508b80615627565b8a6040516122e593929190615ada565b60405180910390a35050505050505050565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461233f5760405162461bcd60e51b8152600401610bdf90615afe565b600080516020615f44833981519152805460011981016123715760405162461bcd60e51b8152600401610bdf90615b35565b600282557f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b0316146123c65760405162461bcd60e51b8152600401610bdf90615b5d565b6000831161240f5760405162461bcd60e51b81526020600482015260166024820152754d757374206465706f73697420736f6d657468696e6760501b6044820152606401610bdf565b82603b600082825461242191906158cd565b90915550506040805160008152602081018590526001600160a01b038616917f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f62910160405180910390a250600190555050565b61247c613882565b6124985760405162461bcd60e51b8152600401610bdf90615785565b603354600160a81b900460ff166124e45760405162461bcd60e51b815260206004820152601060248201526f139bc8199a5c9cdd0819195c1bdcda5d60821b6044820152606401610bdf565b6033805460ff60a81b191690556040517fce77f85e30b0e6df0d12527ddf038f900fdeda0eeda4284c52be47b05de31a9790600090a1565b60335461010090046001600160a01b0316331461254b5760405162461bcd60e51b8152600401610bdf90615933565b600061258c84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061411292505050565b600081815260376020526040808220815180830190925280549394509192909190829060ff1660088111156125c3576125c3614eef565b60088111156125d4576125d4614eef565b81529054610100900464ffffffffff16602090910152905060048151600881111561260157612601614eef565b148061261f575060058151600881111561261d5761261d614eef565b145b61266b5760405162461bcd60e51b815260206004820152601c60248201527f56616c696461746f72206e6f74206163746976652f65786974696e67000000006044820152606401610bdf565b826001600160401b03166000036127285760355460005b8181101561270c5760006035828154811061269f5761269f61583c565b90600052602060002001549050603460008281526020019081526020016000206000015485036127035760405162461bcd60e51b815260206004820152600f60248201526e14195b991a5b99c819195c1bdcda5d608a1b6044820152606401610bdf565b50600101612682565b50506000828152603760205260409020805460ff191660051790555b61273385858561429c565b50817f8dd83105dbd4263d41c76e5d414905babdd3f035bd2031f6ce8895715595979c61276d6001600160401b038616633b9aca0061576e565b60405190815260200160405180910390a25050505050565b606b546001600160a01b031633146127df5760405162461bcd60e51b815260206004820152601b60248201527f43616c6c6572206973206e6f74207468652048617276657374657200000000006044820152606401610bdf565b600080516020615f44833981519152805460011981016128115760405162461bcd60e51b8152600401610bdf90615b35565b6002825561281d6110c8565b5060019055565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b0316146128bf5760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b6064820152608401610bdf565b6128c8336143e1565b565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b03161461291d5760405162461bcd60e51b8152600401610bdf90615b5d565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015612981573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129a5919061588b565b603a546129b291906158cd565b92915050565b60006129c3426141cb565b90506001600160401b0381166129db600c6023615b88565b6039546129f191906001600160401b0316615bb1565b6001600160401b031610612a375760405162461bcd60e51b815260206004820152600d60248201526c29b730b8103a37b79039b7b7b760991b6044820152606401610bdf565b6000612a4282613ca3565b905060004790506040518060600160405280838152602001846001600160401b03168152602001612a7283614440565b6001600160801b039081169091528151603855602082015160398054604094850151909316600160401b026001600160c01b03199093166001600160401b03909216919091179190911790555182907fb7523e03ed4a74718427c422a01fee1138835adb5bd592240f30bd8b5e1b929a90612af09084815260200190565b60405180910390a2505050565b600260008581526037602052604090205460ff166008811115612b2257612b22614eef565b14612b665760405162461bcd60e51b815260206004820152601460248201527315985b1a59185d1bdc881b9bdd081cdd185ad95960621b6044820152606401610bdf565b6000612b7187613ca3565b604051632a5d4ad960e11b81529091506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906354ba95b290612bca9084908990889088908d908c90600401615bd0565b60006040518083038186803b158015612be257600080fd5b505afa158015612bf6573d6000803e3d6000fd5b50505050604051806040016040528060036008811115612c1857612c18614eef565b815264ffffffffff8816602091820152600087815260379091526040902081518154829060ff19166001836008811115612c5457612c54614eef565b0217905550602091820151815464ffffffffff9091166101000265ffffffffff0019909116179055604051600091612c9691600160f91b918491309101615986565b604051602081830303815290604052612cae90615c0c565b9050848114612e47576000868152603760205260408120805460ff19166008179055603554905b81811015612e135760006034600060358481548110612cf657612cf661583c565b600091825260208083209091015483528281019390935260409182019020815160a0810183528154815260018201546001600160401b0380821695830195909552600160401b81049094169281019290925263ffffffff600160801b84041660608301529091608083019060ff600160a01b909104166002811115612d7d57612d7d614eef565b6002811115612d8e57612d8e614eef565b9052508051909150899003612e0a57612dc5603a5482602001516001600160401b0316633b9aca00612dc0919061576e565b6144a9565b603a6000828254612dd691906158a4565b92505081905550612e0460358381548110612df357612df361583c565b906000526020600020015482613da8565b50612e13565b50600101612cd5565b5060405187907fb8318df57b70f6381fb18aaf762e33efa2cc92627aae83d417f6710e1415d8d890600090a2505050612ebd565b6036805460018101825560009182527f4a11f94e20a93c79f6ec743a1954ec4fc2c08429ae2122118bf234b2185c81b8018790556033805460ff60a81b1916905560405164ffffffffff89169188917f8142f1367675d1a37dc1aa31258c38b05f5348de55b799764472d94ccb4a71f49190a350505b505050505050565b612ecd613882565b612ee95760405162461bcd60e51b8152600401610bdf90615785565b60338054610100600160a81b0319166101006001600160a01b038416908102919091179091556040517f83f29c79feb71f8fba9d0fbc4ba5f0982a28b6b1e868b3fc50e6400d100bca0f90600090a250565b60335461010090046001600160a01b03163314612f6a5760405162461bcd60e51b8152600401610bdf90615933565b6000612fab86868080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061411292505050565b60008181526037602052604090205490915060ff166001816008811115612fd457612fd4614eef565b1480612ff157506006816008811115612fef57612fef614eef565b145b8061300d5750600881600881111561300b5761300b614eef565b145b6130595760405162461bcd60e51b815260206004820152601c60248201527f56616c696461746f72206e6f742072656764206f7220657869746564000000006044820152606401610bdf565b60008281526037602052604090819020805460ff19166007179055516312b3fc1960e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906312b3fc19906130c6908a908a908a908a908a90600401615cdd565b600060405180830381600087803b1580156130e057600080fd5b505af11580156130f4573d6000803e3d6000fd5b50505050817f63d54ea43f163d6e28fc23abec67eb7c3294e7e6f0620955a73cd8d17c7367f48686604051611a4c929190615d14565b606c818154811061313a57600080fd5b6000918252602090912001546001600160a01b0316905081565b60335461010090046001600160a01b03163314806131755750613175613882565b6131c15760405162461bcd60e51b815260206004820152601b60248201527f4e6f74205265676973747261746f72206f7220476f7665726e6f7200000000006044820152606401610bdf565b6128c86144c1565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614806132185750613203610a7c565b6001600160a01b0316336001600160a01b0316145b6132705760405162461bcd60e51b815260206004820152602360248201527f43616c6c6572206973206e6f7420746865205661756c74206f7220476f7665726044820152623737b960e91b6064820152608401610bdf565b600080516020615f44833981519152805460011981016132a25760405162461bcd60e51b8152600401610bdf90615b35565b600282556040516370a0823160e01b8152306004820152479060009082907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015613311573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613335919061588b565b61333f91906158cd565b90508015613372576133727f00000000000000000000000000000000000000000000000000000000000000008284614536565b5050600182555050565b613384613882565b6133a05760405162461bcd60e51b8152600401610bdf90615785565b8060005b8181101561344a5760008484838181106133c0576133c061583c565b90506020020160208101906133d59190614e31565b6001600160a01b0316036134425760405162461bcd60e51b815260206004820152602e60248201527f43616e206e6f742073657420616e20656d70747920616464726573732061732060448201526d30903932bbb0b932103a37b5b2b760911b6064820152608401610bdf565b6001016133a4565b507f04c0b9649497d316554306e53678d5f5f5dbc3a06f97dec13ff4cfe98b986bbc606c848460405161347f93929190615d28565b60405180910390a1611b44606c8484614c56565b60405163095ea7b360e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015260001960248301527f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b3906044016020604051808303816000875af1158015613523573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061354791906156c9565b50565b613552613882565b61356e5760405162461bcd60e51b8152600401610bdf90615785565b604051631a1b9a0b60e21b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063686e682c906135be90869086908690600401615dc1565b600060405180830381600087803b1580156135d857600080fd5b505af11580156135ec573d6000803e3d6000fd5b50505050505050565b6035818154811061111757600080fd5b60335461010090046001600160a01b031633146136345760405162461bcd60e51b8152600401610bdf90615933565b60335460ff16156136575760405162461bcd60e51b8152600401610bdf9061595c565b600061369889898080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061411292505050565b90506000808281526037602052604090205460ff1660088111156136be576136be614eef565b1461370b5760405162461bcd60e51b815260206004820152601c60248201527f56616c696461746f7220616c72656164792072656769737465726564000000006044820152606401610bdf565b60008181526037602052604090819020805460ff19166001179055516301ba3ee760e21b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906306e8fb9c9061377e908c908c908c908c908c908c908c908c90600401615e63565b600060405180830381600087803b15801561379857600080fd5b505af11580156137ac573d6000803e3d6000fd5b50505050807f50837f89f5e75ae0a7bcc858f53ea15fa398dc007fd52cbfe4683ae9a6c2d72288886040516137e2929190615d14565b60405180910390a2505050505050505050565b6137fd613882565b6138195760405162461bcd60e51b8152600401610bdf90615785565b606b54604080516001600160a01b03928316815291831660208301527fe48386b84419f4d36e0f96c10cc3510b6fb1a33795620c5098b22472bbe90796910160405180910390a1606b80546001600160a01b0319166001600160a01b0392909216919091179055565b600061389a600080516020615f648339815191525490565b6001600160a01b0316336001600160a01b031614905090565b6138bb613882565b6138d75760405162461bcd60e51b8152600401610bdf90615785565b6138ff817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b031661391f600080516020615f648339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461399f5760405162461bcd60e51b8152600401610bdf90615afe565b600080516020615f44833981519152805460011981016139d15760405162461bcd60e51b8152600401610bdf90615b35565b600282557f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b031614613a265760405162461bcd60e51b8152600401610bdf90615b5d565b613a31858447614536565b5060019055505050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614613a835760405162461bcd60e51b8152600401610bdf90615afe565b600080516020615f4483398151915280546001198101613ab55760405162461bcd60e51b8152600401610bdf90615b35565b600282556040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015613b20573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b44919061588b565b90506000603b5482613b5691906158a4565b9050801561337257603b8290556040805160008152602081018390526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016917f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f62910160405180910390a25050600182555050565b6060606c805480602002602001604051908101604052809291908181526020018280548015613c2a57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613c0c575b5050505050905090565b613c3c613882565b613c585760405162461bcd60e51b8152600401610bdf90615785565b6128c8614650565b6000600c7f0000000000000000000000000000000000000000000000000000000000000000613c8f8483615b88565b613c999190615bb1565b6129b29190615bb1565b604080516001600160401b038316602082015260009182918291720f3df6d732807ef1319fb7b8bb8522d0beac02910160408051601f1981840301815290829052613ced91615ec4565b600060405180830381855afa9150503d8060008114613d28576040519150601f19603f3d011682016040523d82523d6000602084013e613d2d565b606091505b5091509150818015613d40575060008151115b613d8c5760405162461bcd60e51b815260206004820152601860248201527f496e76616c696420626561636f6e2074696d657374616d7000000000000000006044820152606401610bdf565b80806020019051810190613da0919061588b565b949350505050565b60008281526034602052604081206001908101805460ff60a01b1916600160a11b179055603580549091613ddb916158a4565b81548110613deb57613deb61583c565b90600052602060002001549050806035836060015163ffffffff1681548110613e1657613e1661583c565b60009182526020808320909101929092556060840151838252603490925260409020600101805463ffffffff909216600160801b0263ffffffff60801b199092169190911790556035805480613e6e57613e6e6158b7565b60019003818190600052602060002001600090559055505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052613edb9084906146ca565b505050565b6060816001600160401b03811115613efa57613efa614f4f565b604051908082528060200260200182016040528015613f23578160200160208202803683370190505b50905060005b82811015613f8a576034600060358381548110613f4857613f4861583c565b9060005260206000200154815260200190815260200160002060000154828281518110613f7757613f7761583c565b6020908102919091010152600101613f29565b50919050565b8251613fa390606c906020860190614cb5565b50815181518114613fed5760405162461bcd60e51b8152602060048201526014602482015273496e76616c696420696e7075742061727261797360601b6044820152606401610bdf565b60005b8181101561403c5761403484828151811061400d5761400d61583c565b60200260200101518483815181106140275761402761583c565b602002602001015161479c565b600101613ff0565b5050505050565b604051632e1a7d4d60e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b1580156140a557600080fd5b505af11580156140b9573d6000803e3d6000fd5b5050505060006140cb82603b546144a9565b905080603b60008282546140df91906158a4565b9250508190555081603a60008282546140f891906158cd565b90915550506039805467ffffffffffffffff191690555050565b6000815160301461415a5760405162461bcd60e51b8152602060048201526012602482015271496e76616c6964207075626c6963206b657960701b6044820152606401610bdf565b604051600290614171908490600090602001615ee0565b60408051601f198184030181529082905261418b91615ec4565b602060405180830381855afa1580156141a8573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906129b2919061588b565b60006001600160401b038211156142335760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203660448201526534206269747360d01b6064820152608401610bdf565b5090565b600063ffffffff8211156142335760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201526532206269747360d01b6064820152608401610bdf565b6000603083146142ee5760405162461bcd60e51b815260206004820152601d60248201527f496e76616c69642076616c696461746f722062797465206c656e6774680000006044820152606401610bdf565b6142f66148fb565b90506000710961ef480eb55e80d19ad83579a64c0070026001600160a01b03168286868660405160200161432c93929190615f0f565b60408051601f198184030181529082905261434691615ec4565b60006040518083038185875af1925050503d8060008114614383576040519150601f19603f3d011682016040523d82523d6000602084013e614388565b606091505b50509050806143d95760405162461bcd60e51b815260206004820152601960248201527f5769746864726177616c2072657175657374206661696c6564000000000000006044820152606401610bdf565b509392505050565b6001600160a01b0381166144375760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f7220697320616464726573732830290000000000006044820152606401610bdf565b613547816149c2565b60006001600160801b038211156142335760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316044820152663238206269747360c81b6064820152608401610bdf565b60008183106144b857816144ba565b825b9392505050565b60335460ff16156144e45760405162461bcd60e51b8152600401610bdf9061595c565b6033805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586145193390565b6040516001600160a01b03909116815260200160405180910390a1565b600082116145865760405162461bcd60e51b815260206004820152601760248201527f4d75737420776974686472617720736f6d657468696e670000000000000000006044820152606401610bdf565b6001600160a01b0383166145d55760405162461bcd60e51b8152602060048201526016602482015275135d5cdd081cdc1958da599e481c9958da5c1a595b9d60521b6044820152606401610bdf565b80156145e4576145e481614a29565b6145ee8284614aeb565b6040805160008152602081018490526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016917f2717ead6b9200dd235aad468c9809ea400fe33ac69b5bfaa6d3e90fc922b63989101612af0565b60335460ff166146995760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610bdf565b6033805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33614519565b600061471f826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614b4b9092919063ffffffff16565b805190915015613edb578080602001905181019061473d91906156c9565b613edb5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610bdf565b6001600160a01b0382811660009081526067602052604090205416156147f95760405162461bcd60e51b81526020600482015260126024820152711c151bdad95b88185b1c9958591e481cd95d60721b6044820152606401610bdf565b6001600160a01b0382161580159061481957506001600160a01b03811615155b6148595760405162461bcd60e51b8152602060048201526011602482015270496e76616c69642061646472657373657360781b6044820152606401610bdf565b6001600160a01b03828116600081815260676020908152604080832080549587166001600160a01b031996871681179091556068805460018101825594527fa2153420d844928b4421650203c77babc8b33d7f2e7b450e2966db0c2209775390930180549095168417909455925190815290917fef6485b84315f9b1483beffa32aae9a0596890395e3d7521f1c5fbb51790e765910160405180910390a25050565b60405160009081908190710961ef480eb55e80d19ad83579a64c0070029082818181855afa9150503d806000811461494f576040519150601f19603f3d011682016040523d82523d6000602084013e614954565b606091505b5091509150818015614967575060008151115b6149a75760405162461bcd60e51b81526020600482015260116024820152704661696c656420746f206765742066656560781b6044820152606401610bdf565b808060200190518101906149bb919061588b565b9250505090565b806001600160a01b03166149e2600080516020615f648339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a3600080516020615f6483398151915255565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015614a8457600080fd5b505af1158015614a98573d6000803e3d6000fd5b505050505080603b6000828254614aaf91906158cd565b9091555050603a54614ac190826144a9565b603a6000828254614ad291906158a4565b90915550506039805467ffffffffffffffff1916905550565b614b1f6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168284613e89565b6000614b2d83603b546144a9565b905080603b6000828254614b4191906158a4565b9091555050505050565b6060613da0848460008585843b614ba45760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610bdf565b600080866001600160a01b03168587604051614bc09190615ec4565b60006040518083038185875af1925050503d8060008114614bfd576040519150601f19603f3d011682016040523d82523d6000602084013e614c02565b606091505b5091509150614c12828286614c1d565b979650505050505050565b60608315614c2c5750816144ba565b825115614c3c5782518084602001fd5b8160405162461bcd60e51b8152600401610bdf9190615f30565b828054828255906000526020600020908101928215614ca9579160200282015b82811115614ca95781546001600160a01b0319166001600160a01b03843516178255602090920191600190910190614c76565b50614233929150614d0a565b828054828255906000526020600020908101928215614ca9579160200282015b82811115614ca957825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190614cd5565b5b808211156142335760008155600101614d0b565b80356001600160401b0381168114614d3657600080fd5b919050565b600060408284031215613f8a57600080fd5b60008060008060808587031215614d6357600080fd5b84359350614d7360208601614d1f565b925060408501356001600160401b03811115614d8e57600080fd5b614d9a87828801614d3b565b92505060608501356001600160401b03811115614db657600080fd5b614dc287828801614d3b565b91505092959194509250565b80356001600160a01b0381168114614d3657600080fd5b60008060408385031215614df857600080fd5b614e0183614dce565b9150614e0f60208401614dce565b90509250929050565b600060208284031215614e2a57600080fd5b5035919050565b600060208284031215614e4357600080fd5b6144ba82614dce565b60008060408385031215614e5f57600080fd5b614e6883614dce565b946020939093013593505050565b600060808284031215613f8a57600080fd5b60008060408385031215614e9b57600080fd5b82356001600160401b03811115614eb157600080fd5b614ebd85828601614e76565b92505060208301356001600160401b03811115614ed957600080fd5b614ee585828601614e76565b9150509250929050565b634e487b7160e01b600052602160045260246000fd5b8581526001600160401b0385811660208301528416604082015263ffffffff8316606082015260a0810160038310614f3f57614f3f614eef565b8260808301529695505050505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715614f8d57614f8d614f4f565b604052919050565b60006001600160401b03821115614fae57614fae614f4f565b5060051b60200190565b600082601f830112614fc957600080fd5b8135614fdc614fd782614f95565b614f65565b8082825260208201915060208360051b860101925085831115614ffe57600080fd5b602085015b838110156150225761501481614dce565b835260209283019201615003565b5095945050505050565b60008060006060848603121561504157600080fd5b83356001600160401b0381111561505757600080fd5b61506386828701614fb8565b93505060208401356001600160401b0381111561507f57600080fd5b61508b86828701614fb8565b92505060408401356001600160401b038111156150a757600080fd5b6150b386828701614fb8565b9150509250925092565b600080604083850312156150d057600080fd5b82356001600160401b038111156150e657600080fd5b8301606081860312156150f857600080fd5b9150614e0f60208401614d1f565b60008083601f84011261511857600080fd5b5081356001600160401b0381111561512f57600080fd5b60208301915083602082850101111561514757600080fd5b9250929050565b60008060006040848603121561516357600080fd5b83356001600160401b0381111561517957600080fd5b61518586828701615106565b9094509250615198905060208501614d1f565b90509250925092565b60008060008060008060a087890312156151ba57600080fd5b6151c387614d1f565b9550602087013564ffffffffff811681146151dd57600080fd5b9450604087013593506060870135925060808701356001600160401b0381111561520657600080fd5b61521289828a01615106565b979a9699509497509295939492505050565b60008083601f84011261523657600080fd5b5081356001600160401b0381111561524d57600080fd5b6020830191508360208260051b850101111561514757600080fd5b600060a08284031215613f8a57600080fd5b600080600080600060e0868803121561529257600080fd5b85356001600160401b038111156152a857600080fd5b6152b488828901615106565b90965094505060208601356001600160401b038111156152d357600080fd5b6152df88828901615224565b90945092506152f390508760408801615268565b90509295509295909350565b6000806020838503121561531257600080fd5b82356001600160401b0381111561532857600080fd5b61533485828601615224565b90969095509350505050565b604081016009841061535457615354614eef565b92815264ffffffffff9190911660209091015290565b803563ffffffff81168114614d3657600080fd5b801515811461354757600080fd5b600060a0828403121561539e57600080fd5b60405160a081016001600160401b03811182821017156153c0576153c0614f4f565b6040529050806153cf8361536a565b81526153dd60208401614d1f565b60208201526153ee60408401614d1f565b604082015260608301356154018161537e565b6060820152608092830135920191909152919050565b600080600060e0848603121561542c57600080fd5b83356001600160401b0381111561544257600080fd5b8401601f8101861361545357600080fd5b8035615461614fd782614f95565b8082825260208201915060208360051b85010192508883111561548357600080fd5b6020840193505b828410156154ac5761549b84614d1f565b82526020938401939091019061548a565b955050505060208401359150615198856040860161538c565b600080600080600080600080610120898b0312156154e257600080fd5b88356001600160401b038111156154f857600080fd5b6155048b828c01615106565b90995097505060208901356001600160401b0381111561552357600080fd5b61552f8b828c01615224565b90975095505060408901356001600160401b0381111561554e57600080fd5b61555a8b828c01615106565b909550935050606089013591506155748a60808b01615268565b90509295985092959890939650565b60008060006060848603121561559857600080fd5b6155a184614dce565b92506155af60208501614dce565b929592945050506040919091013590565b602080825282518282018190526000918401906040840190835b818110156156015783516001600160a01b03168352602093840193909201916001016155da565b509095945050505050565b60006020828403121561561e57600080fd5b6144ba82614d1f565b6000808335601e1984360301811261563e57600080fd5b8301803591506001600160401b0382111561565857600080fd5b60200191503681900382131561514757600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b8481526001600160401b03841660208201526060604082015260006156bf60608301848661566d565b9695505050505050565b6000602082840312156156db57600080fd5b81516144ba8161537e565b85815264ffffffffff851660208201526001600160401b0384166040820152608060608201526000614c1260808301848661566d565b634e487b7160e01b600052601160045260246000fd5b60006001600160401b0383168061575957634e487b7160e01b600052601260045260246000fd5b806001600160401b0384160491505092915050565b80820281158282048414176129b2576129b261571c565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b6000808335601e198436030181126157d357600080fd5b8301803591506001600160401b038211156157ed57600080fd5b6020019150600581901b360382131561514757600080fd5b8481528360208201526060604082015260006156bf60608301848661566d565b6000816158345761583461571c565b506000190190565b634e487b7160e01b600052603260045260246000fd5b85815284602082015260806040820152600061587260808301858761566d565b905064ffffffffff831660608301529695505050505050565b60006020828403121561589d57600080fd5b5051919050565b818103818111156129b2576129b261571c565b634e487b7160e01b600052603160045260246000fd5b808201808211156129b2576129b261571c565b6000602082840312156158f257600080fd5b6144ba8261536a565b85815284602082015260806040820152600061591b60808301858761566d565b905063ffffffff831660608301529695505050505050565b6020808252600f908201526e2737ba102932b3b4b9ba3930ba37b960891b604082015260600190565b60208082526010908201526f14185d5cd8589b194e881c185d5cd95960821b604082015260600190565b6001600160f81b03199390931683526001600160a81b031991909116600183015260601b6bffffffffffffffffffffffff1916600c82015260200190565b6001600160401b0382811682821603908111156129b2576129b261571c565b60005b838110156159fe5781810151838201526020016159e6565b50506000910152565b60008151808452615a1f8160208601602086016159e3565b601f01601f19169290920160200192915050565b86815260a060208201526000615a4c60a0830188615a07565b6001600160401b03871660408401528281036060840152615a6e81868861566d565b9150506001600160401b0383166080830152979650505050505050565b608081526000615a9f60808301888a61566d565b8281036020840152615ab18188615a07565b90508281036040840152615ac681868861566d565b915050826060830152979650505050505050565b604081526000615aee60408301858761566d565b9050826020830152949350505050565b60208082526017908201527f43616c6c6572206973206e6f7420746865205661756c74000000000000000000604082015260600190565b6020808252600e908201526d1499595b9d1c985b9d0818d85b1b60921b604082015260600190565b602080825260119082015270155b9cdd5c1c1bdc9d195908185cdcd95d607a1b604082015260600190565b6001600160401b038181168382160290811690818114615baa57615baa61571c565b5092915050565b6001600160401b0381811683821601908111156129b2576129b261571c565b86815285602082015260a060408201526000615bf060a08301868861566d565b64ffffffffff9490941660608301525060800152949350505050565b80516020808301519190811015613f8a5760001960209190910360031b1b16919050565b81835260208301925060008160005b84811015615c6e576001600160401b03615c5883614d1f565b1686526020958601959190910190600101615c3f565b5093949350505050565b63ffffffff615c868261536a565b1682526001600160401b03615c9d60208301614d1f565b1660208301526001600160401b03615cb760408301614d1f565b1660408301526060810135615ccb8161537e565b15156060830152608090810135910152565b60e081526000615cf160e08301878961566d565b8281036020840152615d04818688615c30565b9150506156bf6040830184615c78565b602081526000613da0602083018486615c30565b6040808252845490820181905260008581526020812090916060840190835b81811015615d6e5783546001600160a01b0316835260019384019360209093019201615d47565b50508381036020808601919091528582520190508460005b85811015615db5576001600160a01b03615d9f83614dce565b1683526020928301929190910190600101615d86565b50909695505050505050565b60e080825284519082018190526000906020860190610100840190835b81811015615e055783516001600160401b0316835260209384019390920191600101615dde565b5050809250505083602083015263ffffffff83511660408301526001600160401b0360208401511660608301526001600160401b0360408401511660808301526060830151151560a0830152608083015160c0830152949350505050565b61012081526000615e7961012083018a8c61566d565b8281036020840152615e8c81898b615c30565b90508281036040840152615ea181878961566d565b915050836060830152615eb76080830184615c78565b9998505050505050505050565b60008251615ed68184602087016159e3565b9190910192915050565b60008351615ef28184602088016159e3565b6001600160801b0319939093169190920190815260100192915050565b8284823760c09190911b6001600160c01b0319169101908152600801919050565b6020815260006144ba6020830184615a0756fe53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac45357bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa2646970667358221220cee775df8d65f3ce633ccb3adad18cf76e2c539a2711bd2695a6e1e771d4f3bb64736f6c634300081c00337bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a", + "deployedBytecode": "0x6080604052600436106103035760003560e01c806371a735f311610190578063b6e2b520116100dc578063d38bfff411610095578063dbe55e561161006f578063dbe55e56146109fc578063de5f626814610a30578063f6ca71b014610a45578063f7b188a514610a6757600080fd5b8063d38bfff4146109a7578063d79e4032146109c7578063d9caed12146109dc57600080fd5b8063b6e2b520146108fc578063b8ec66781461091c578063bb1b918d1461093c578063c2e1e3f41461095c578063c7af33521461097c578063d059f6ef1461099157600080fd5b80639136616a1161014957806398245f1b1161012357806398245f1b1461082d578063a5f5be5414610879578063aa388af61461089a578063ad1728cb146108e757600080fd5b80639136616a146107be57806391649751146107d957806396d538bb1461080d57600080fd5b806371a735f3146106fb5780637b2d9b2c1461071b5780637da9982a1461073b5780638456cb591461076f578063853828b61461078457806387bae8671461079957600080fd5b806347e7ef241161024f5780635c975abb1161020857806367c7066c116101e257806367c7066c146106865780636874469d146106a65780636c341d1a146106bb5780636e811d38146106db57600080fd5b80635c975abb1461062d5780635d36b190146106515780635f5152261461066657600080fd5b806347e7ef24146105a55780634896b31a146105c55780634c84e6f8146105da578063522e4245146105ef57806359ff4158146106025780635a063f631461061857600080fd5b80631072cbea116102bc5780633d4dff7b116102965780633d4dff7b146104bd578063430bf08a14610531578063435356d1146105655780634583ef101461058557600080fd5b80631072cbea1461041b5780631a1a15711461043b57806325e2e9f31461045b57600080fd5b80630c340a241461030f5780630d304174146103415780630df1ecfd146103635780630ed57b3a146103975780630ef99855146103b75780630fc3b4c4146103e557600080fd5b3661030a57005b600080fd5b34801561031b57600080fd5b50610324610a7c565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561034d57600080fd5b5061036161035c366004614d4d565b610a99565b005b34801561036f57600080fd5b506103247f000000000000000000000000000000000000000000000000000000000000000081565b3480156103a357600080fd5b506103616103b2366004614de5565b6110c8565b3480156103c357600080fd5b506103d76103d2366004614e18565b611107565b604051908152602001610338565b3480156103f157600080fd5b50610324610400366004614e31565b6067602052600090815260409020546001600160a01b031681565b34801561042757600080fd5b50610361610436366004614e4c565b611128565b34801561044757600080fd5b50610361610456366004614e88565b6111e7565b34801561046757600080fd5b5060385460395461049191906001600160401b03811690600160401b90046001600160801b031683565b604080519384526001600160401b0390921660208401526001600160801b031690820152606001610338565b3480156104c957600080fd5b506105206104d8366004614e18565b603460205260009081526040902080546001909101546001600160401b0380821691600160401b810490911690600160801b810463ffffffff1690600160a01b900460ff1685565b604051610338959493929190614f05565b34801561053d57600080fd5b506103247f000000000000000000000000000000000000000000000000000000000000000081565b34801561057157600080fd5b5061036161058036600461502c565b611a5d565b34801561059157600080fd5b506103616105a03660046150bd565b611b4a565b3480156105b157600080fd5b506103616105c0366004614e4c565b6122f7565b3480156105d157600080fd5b506035546103d7565b3480156105e657600080fd5b50610361612474565b6103616105fd36600461514e565b61251c565b34801561060e57600080fd5b506103d7603a5481565b34801561062457600080fd5b50610361612785565b34801561063957600080fd5b5060335460ff165b6040519015158152602001610338565b34801561065d57600080fd5b50610361612824565b34801561067257600080fd5b506103d7610681366004614e31565b6128ca565b34801561069257600080fd5b50606b54610324906001600160a01b031681565b3480156106b257600080fd5b506103616129b8565b3480156106c757600080fd5b506103616106d63660046151a1565b612afd565b3480156106e757600080fd5b506103616106f6366004614e31565b612ec5565b34801561070757600080fd5b5061036161071636600461527a565b612f3b565b34801561072757600080fd5b50610324610736366004614e18565b61312a565b34801561074757600080fd5b506103247f000000000000000000000000000000000000000000000000000000000000000081565b34801561077b57600080fd5b50610361613154565b34801561079057600080fd5b506103616131c9565b3480156107a557600080fd5b506033546103249061010090046001600160a01b031681565b3480156107ca57600080fd5b506103616103b2366004614e18565b3480156107e557600080fd5b506103247f000000000000000000000000000000000000000000000000000000000000000081565b34801561081957600080fd5b506103616108283660046152ff565b61337c565b34801561083957600080fd5b5061086b610848366004614e18565b60376020526000908152604090205460ff811690610100900464ffffffffff1682565b604051610338929190615340565b34801561088557600080fd5b5060335461064190600160a81b900460ff1681565b3480156108a657600080fd5b506106416108b5366004614e31565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811691161490565b3480156108f357600080fd5b50610361613493565b34801561090857600080fd5b50610361610917366004615417565b61354a565b34801561092857600080fd5b506103d7610937366004614e18565b6135f5565b34801561094857600080fd5b506103616109573660046154c5565b613605565b34801561096857600080fd5b50610361610977366004614e31565b6137f5565b34801561098857600080fd5b50610641613882565b34801561099d57600080fd5b506103d7603b5481565b3480156109b357600080fd5b506103616109c2366004614e31565b6138b3565b3480156109d357600080fd5b506036546103d7565b3480156109e857600080fd5b506103616109f7366004615583565b613957565b348015610a0857600080fd5b506103247f000000000000000000000000000000000000000000000000000000000000000081565b348015610a3c57600080fd5b50610361613a3b565b348015610a5157600080fd5b50610a5a613bd2565b60405161033891906155c0565b348015610a7357600080fd5b50610361613c34565b6000610a94600080516020615f648339815191525490565b905090565b6000848152603460209081526040808320815160a0810183528154815260018201546001600160401b0380821695830195909552600160401b810490941692810192909252600160801b830463ffffffff16606083015290916080830190600160a01b900460ff166002811115610b1257610b12614eef565b6002811115610b2357610b23614eef565b9052508051600090815260376020526040808220815180830190925280549394509192909190829060ff166008811115610b5f57610b5f614eef565b6008811115610b7057610b70614eef565b81529054610100900464ffffffffff166020909101529050600182608001516002811115610ba057610ba0614eef565b14610be85760405162461bcd60e51b81526020600482015260136024820152724465706f736974206e6f742070656e64696e6760681b60448201526064015b60405180910390fd5b610bf5602085018561560c565b6001600160401b0316600003610c4d5760405162461bcd60e51b815260206004820152601d60248201527f5a65726f203173742070656e64696e67206465706f73697420736c6f740000006044820152606401610bdf565b600381516008811115610c6257610c62614eef565b1480610c805750600481516008811115610c7e57610c7e614eef565b145b80610c9d5750600581516008811115610c9b57610c9b614eef565b145b610ce95760405162461bcd60e51b815260206004820152601b60248201527f4e6f742076657269666965642f6163746976652f65786974696e6700000000006044820152606401610bdf565b846001600160401b031682604001516001600160401b031610610d475760405162461bcd60e51b815260206004820152601660248201527514db1bdd081b9bdd0818599d195c8819195c1bdcda5d60521b6044820152606401610bdf565b6039546001600160401b031680610d5d87613c60565b6001600160401b0316111580610d7a57506001600160401b038116155b610dc65760405162461bcd60e51b815260206004820152601e60248201527f4465706f7369742061667465722062616c616e636520736e617073686f7400006044820152606401610bdf565b6000610dd9610dd488613c60565b613ca3565b905060006001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663d98a556483610e1a60208b018b61560c565b610e2760208c018c615627565b6040518563ffffffff1660e01b8152600401610e469493929190615696565b602060405180830381865afa158015610e63573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e8791906156c9565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663334a88fb838660200151896000016020810190610ed1919061560c565b610ede60208c018c615627565b6040518663ffffffff1660e01b8152600401610efe9594939291906156e6565b60006040518083038186803b158015610f1657600080fd5b505afa158015610f2a573d6000803e3d6000fd5b506000925060209150610f4190508982018a61560c565b610f4b9190615732565b90506001600160401b03610f62602089018961560c565b6001600160401b03161480610f9557506001600160401b038116610f89602089018961560c565b6001600160401b031611155b80610f9d5750815b610fe95760405162461bcd60e51b815260206004820152601d60248201527f45786974204465706f736974206c696b656c79206e6f742070726f632e0000006044820152606401610bdf565b610ff6602089018961560c565b6001600160401b031686604001516001600160401b031610806110165750815b6110625760405162461bcd60e51b815260206004820152601c60248201527f4465706f736974206c696b656c79206e6f742070726f636573736564000000006044820152606401610bdf565b61106c8a87613da8565b897fae0e4f727389efd70d748d667436e0264f370ae498b339b713797dbab57b12ff87602001516001600160401b0316633b9aca006110ab919061576e565b60405190815260200160405180910390a250505050505050505050565b60405162461bcd60e51b81526020600482015260146024820152732ab739bab83837b93a32b210333ab731ba34b7b760611b6044820152606401610bdf565b6036818154811061111757600080fd5b600091825260209091200154905081565b611130613882565b61114c5760405162461bcd60e51b8152600401610bdf90615785565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116908316036111c75760405162461bcd60e51b815260206004820152601f60248201527f43616e6e6f74207472616e7366657220737570706f72746564206173736574006044820152606401610bdf565b6111e36111d2610a7c565b6001600160a01b0384169083613e89565b5050565b6040805160608101825260385481526039546001600160401b03811660208301819052600160401b9091046001600160801b031692820192909252906112655760405162461bcd60e51b81526020600482015260136024820152724e6f20736e61707065642062616c616e63657360681b6044820152606401610bdf565b6036546035546000908215611725578261128260608801886157bc565b9050146112ca5760405162461bcd60e51b8152602060048201526016602482015275496e76616c69642062616c616e63652070726f6f667360501b6044820152606401610bdf565b826112d860408801886157bc565b9050146113205760405162461bcd60e51b8152602060048201526016602482015275496e76616c69642062616c616e6365206c656176657360501b6044820152606401610bdf565b83516001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906391ad640d90883561136260208b018b615627565b6040518563ffffffff1660e01b81526004016113819493929190615805565b60006040518083038186803b15801561139957600080fd5b505afa1580156113ad573d6000803e3d6000fd5b5050505060006113bc82613ee0565b9050835b8015611722576113cf81615825565b9050600060376000603684815481106113ea576113ea61583c565b906000526020600020015481526020019081526020016000206040518060400160405290816000820160009054906101000a900460ff16600881111561143257611432614eef565b600881111561144357611443614eef565b81529054610100900464ffffffffff16602090910152905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f34ad34c8b3561149b60408e018e6157bc565b878181106114ab576114ab61583c565b905060200201358d80606001906114c291906157bc565b888181106114d2576114d261583c565b90506020028101906114e49190615627565b87602001516040518663ffffffff1660e01b8152600401611509959493929190615852565b602060405180830381865afa158015611526573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061154a919061588b565b905080600003611682576000805b85518110156115af57603685815481106115745761157461583c565b90600052602060002001548682815181106115915761159161583c565b6020026020010151036115a757600191506115af565b600101611558565b508061167a57600660376000603687815481106115ce576115ce61583c565b600091825260208083209091015483528201929092526040019020805460ff1916600183600881111561160357611603614eef565b02179055506116136001896158a4565b9750603688815481106116285761162861583c565b9060005260206000200154603685815481106116465761164661583c565b6000918252602090912001556036805480611663576116636158b7565b600190038181906000526020600020016000905590555b5050506113c0565b60038251600881111561169757611697614eef565b1480156116a857506407823ff28081115b1561170157600460376000603686815481106116c6576116c661583c565b600091825260208083209091015483528201929092526040019020805460ff191660018360088111156116fb576116fb614eef565b02179055505b61170f81633b9aca0061576e565b61171990876158cd565b955050506113c0565b50505b600081156119c2578161173b60608801886157bc565b9050146117835760405162461bcd60e51b8152602060048201526016602482015275496e76616c6964206465706f7369742070726f6f667360501b6044820152606401610bdf565b8161179160408801886157bc565b9050146117e05760405162461bcd60e51b815260206004820152601760248201527f496e76616c6964206465706f73697420696e64657865730000000000000000006044820152606401610bdf565b84516001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690632b00e79690883561182260208b018b615627565b6040518563ffffffff1660e01b81526004016118419493929190615805565b60006040518083038186803b15801561185957600080fd5b505afa15801561186d573d6000803e3d6000fd5b5050505060005b828110156119c0576000603582815481106118915761189161583c565b60009182526020909120015490506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016638a050dd48935836118de60608d018d6157bc565b878181106118ee576118ee61583c565b90506020028101906119009190615627565b61190d60408f018f6157bc565b8981811061191d5761191d61583c565b905060200201602081019061193291906158e0565b6040518663ffffffff1660e01b81526004016119529594939291906158fb565b60006040518083038186803b15801561196a57600080fd5b505afa15801561197e573d6000803e3d6000fd5b5050506000828152603460205260409020600101546119ab91506001600160401b0316633b9aca0061576e565b6119b590846158cd565b925050600101611874565b505b60408501516001600160801b03166119da84836158cd565b6119e491906158cd565b603a556039805467ffffffffffffffff1916905560208581015160408088015181518581529384018790526001600160801b0316908301526001600160401b0316907fed2528338eefb63fd1860078b91e35106bc25e3fd528634d180f662582fe5ec1906060015b60405180910390a250505050505050565b611a65613882565b611a815760405162461bcd60e51b8152600401610bdf90615785565b600054610100900460ff1680611a9a575060005460ff16155b611afd5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610bdf565b600054610100900460ff16158015611b1f576000805461ffff19166101011790555b611b2a848484613f90565b611b32613493565b8015611b44576000805461ff00191690555b50505050565b60335461010090046001600160a01b03163314611b795760405162461bcd60e51b8152600401610bdf90615933565b60335460ff1615611b9c5760405162461bcd60e51b8152600401610bdf9061595c565b6000611bb56001600160401b038316633b9aca0061576e565b6040516370a0823160e01b81523060048201529091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611c1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c40919061588b565b811115611c835760405162461bcd60e51b8152602060048201526011602482015270092dce6eaccccd2c6d2cadce840ae8aa89607b1b6044820152606401610bdf565b603554600c11611cc45760405162461bcd60e51b815260206004820152600c60248201526b4d6178206465706f7369747360a01b6044820152606401610bdf565b611ccd81614043565b6000611d16611cdc8580615627565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061411292505050565b60008181526037602052604090205490915060ff166001816008811115611d3f57611d3f614eef565b1480611d5c57506003816008811115611d5a57611d5a614eef565b145b80611d7857506004816008811115611d7657611d76614eef565b145b611dc45760405162461bcd60e51b815260206004820152601a60248201527f4e6f742072656769737465726564206f722076657269666965640000000000006044820152606401610bdf565b670de0b6b3a7640000831015611e105760405162461bcd60e51b815260206004820152601160248201527011195c1bdcda5d081d1bdbc81cdb585b1b607a1b6044820152606401610bdf565b6001816008811115611e2457611e24614eef565b03611f5157603354600160a81b900460ff1615611e7c5760405162461bcd60e51b8152602060048201526016602482015275115e1a5cdd1a5b99c8199a5c9cdd0819195c1bdcda5d60521b6044820152606401610bdf565b670de0b6b3a76400008314611ed35760405162461bcd60e51b815260206004820152601c60248201527f496e76616c6964206669727374206465706f73697420616d6f756e74000000006044820152606401610bdf565b603654603090611ee49060016158cd565b10611f225760405162461bcd60e51b815260206004820152600e60248201526d4d61782076616c696461746f727360901b6044820152606401610bdf565b60338054600160a81b60ff60a81b199091161790556000828152603760205260409020805460ff191660021790555b604051600090611f6d90600160f91b9083903090602001615986565b60405160208183030381529060405290506000600c7f0000000000000000000000000000000000000000000000000000000000000000611fac426141cb565b611fb691906159c4565b611fc09190615732565b905060006001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663afe7688786858a61200360208e018e615627565b886040518763ffffffff1660e01b815260040161202596959493929190615a33565b602060405180830381865afa158015612042573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612066919061588b565b905060008082815260346020526040902060010154600160a01b900460ff16600281111561209657612096614eef565b146120d75760405162461bcd60e51b8152602060048201526011602482015270111d5c1b1a58d85d194819195c1bdcda5d607a1b6044820152606401610bdf565b6040518060a00160405280868152602001886001600160401b03168152602001836001600160401b03168152602001612114603580549050614237565b63ffffffff16815260200160019052600082815260346020908152604091829020835181559083015160018201805493850151606086015163ffffffff16600160801b0263ffffffff60801b196001600160401b03928316600160401b026001600160801b03199097169290941691909117949094179182168417815560808501519293909160ff60a01b1990911664ffffffffff60801b1990911617600160a01b8360028111156121c8576121c8614eef565b021790555050603580546001810182556000919091527fcfa4bec1d3298408bb5afcfcd9c430549c5b31f8aa5c5848151c0a55f473c34d01829055506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663228951188761223e8b80615627565b8761224c60208f018f615627565b8f604001356040518863ffffffff1660e01b815260040161227296959493929190615a8b565b6000604051808303818588803b15801561228b57600080fd5b505af115801561229f573d6000803e3d6000fd5b508493508892507faca97428a1d7f2b7c4cee2fbe4feda457e132b404b0c9c3ff73bf7a988d889a891506122d590508b80615627565b8a6040516122e593929190615ada565b60405180910390a35050505050505050565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461233f5760405162461bcd60e51b8152600401610bdf90615afe565b600080516020615f44833981519152805460011981016123715760405162461bcd60e51b8152600401610bdf90615b35565b600282557f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b0316146123c65760405162461bcd60e51b8152600401610bdf90615b5d565b6000831161240f5760405162461bcd60e51b81526020600482015260166024820152754d757374206465706f73697420736f6d657468696e6760501b6044820152606401610bdf565b82603b600082825461242191906158cd565b90915550506040805160008152602081018590526001600160a01b038616917f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f62910160405180910390a250600190555050565b61247c613882565b6124985760405162461bcd60e51b8152600401610bdf90615785565b603354600160a81b900460ff166124e45760405162461bcd60e51b815260206004820152601060248201526f139bc8199a5c9cdd0819195c1bdcda5d60821b6044820152606401610bdf565b6033805460ff60a81b191690556040517fce77f85e30b0e6df0d12527ddf038f900fdeda0eeda4284c52be47b05de31a9790600090a1565b60335461010090046001600160a01b0316331461254b5760405162461bcd60e51b8152600401610bdf90615933565b600061258c84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061411292505050565b600081815260376020526040808220815180830190925280549394509192909190829060ff1660088111156125c3576125c3614eef565b60088111156125d4576125d4614eef565b81529054610100900464ffffffffff16602090910152905060048151600881111561260157612601614eef565b148061261f575060058151600881111561261d5761261d614eef565b145b61266b5760405162461bcd60e51b815260206004820152601c60248201527f56616c696461746f72206e6f74206163746976652f65786974696e67000000006044820152606401610bdf565b826001600160401b03166000036127285760355460005b8181101561270c5760006035828154811061269f5761269f61583c565b90600052602060002001549050603460008281526020019081526020016000206000015485036127035760405162461bcd60e51b815260206004820152600f60248201526e14195b991a5b99c819195c1bdcda5d608a1b6044820152606401610bdf565b50600101612682565b50506000828152603760205260409020805460ff191660051790555b61273385858561429c565b50817f8dd83105dbd4263d41c76e5d414905babdd3f035bd2031f6ce8895715595979c61276d6001600160401b038616633b9aca0061576e565b60405190815260200160405180910390a25050505050565b606b546001600160a01b031633146127df5760405162461bcd60e51b815260206004820152601b60248201527f43616c6c6572206973206e6f74207468652048617276657374657200000000006044820152606401610bdf565b600080516020615f44833981519152805460011981016128115760405162461bcd60e51b8152600401610bdf90615b35565b6002825561281d6110c8565b5060019055565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b0316146128bf5760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b6064820152608401610bdf565b6128c8336143e1565b565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b03161461291d5760405162461bcd60e51b8152600401610bdf90615b5d565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015612981573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129a5919061588b565b603a546129b291906158cd565b92915050565b60006129c3426141cb565b90506001600160401b0381166129db600c6023615b88565b6039546129f191906001600160401b0316615bb1565b6001600160401b031610612a375760405162461bcd60e51b815260206004820152600d60248201526c29b730b8103a37b79039b7b7b760991b6044820152606401610bdf565b6000612a4282613ca3565b905060004790506040518060600160405280838152602001846001600160401b03168152602001612a7283614440565b6001600160801b039081169091528151603855602082015160398054604094850151909316600160401b026001600160c01b03199093166001600160401b03909216919091179190911790555182907fb7523e03ed4a74718427c422a01fee1138835adb5bd592240f30bd8b5e1b929a90612af09084815260200190565b60405180910390a2505050565b600260008581526037602052604090205460ff166008811115612b2257612b22614eef565b14612b665760405162461bcd60e51b815260206004820152601460248201527315985b1a59185d1bdc881b9bdd081cdd185ad95960621b6044820152606401610bdf565b6000612b7187613ca3565b604051632a5d4ad960e11b81529091506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906354ba95b290612bca9084908990889088908d908c90600401615bd0565b60006040518083038186803b158015612be257600080fd5b505afa158015612bf6573d6000803e3d6000fd5b50505050604051806040016040528060036008811115612c1857612c18614eef565b815264ffffffffff8816602091820152600087815260379091526040902081518154829060ff19166001836008811115612c5457612c54614eef565b0217905550602091820151815464ffffffffff9091166101000265ffffffffff0019909116179055604051600091612c9691600160f91b918491309101615986565b604051602081830303815290604052612cae90615c0c565b9050848114612e47576000868152603760205260408120805460ff19166008179055603554905b81811015612e135760006034600060358481548110612cf657612cf661583c565b600091825260208083209091015483528281019390935260409182019020815160a0810183528154815260018201546001600160401b0380821695830195909552600160401b81049094169281019290925263ffffffff600160801b84041660608301529091608083019060ff600160a01b909104166002811115612d7d57612d7d614eef565b6002811115612d8e57612d8e614eef565b9052508051909150899003612e0a57612dc5603a5482602001516001600160401b0316633b9aca00612dc0919061576e565b6144a9565b603a6000828254612dd691906158a4565b92505081905550612e0460358381548110612df357612df361583c565b906000526020600020015482613da8565b50612e13565b50600101612cd5565b5060405187907fb8318df57b70f6381fb18aaf762e33efa2cc92627aae83d417f6710e1415d8d890600090a2505050612ebd565b6036805460018101825560009182527f4a11f94e20a93c79f6ec743a1954ec4fc2c08429ae2122118bf234b2185c81b8018790556033805460ff60a81b1916905560405164ffffffffff89169188917f8142f1367675d1a37dc1aa31258c38b05f5348de55b799764472d94ccb4a71f49190a350505b505050505050565b612ecd613882565b612ee95760405162461bcd60e51b8152600401610bdf90615785565b60338054610100600160a81b0319166101006001600160a01b038416908102919091179091556040517f83f29c79feb71f8fba9d0fbc4ba5f0982a28b6b1e868b3fc50e6400d100bca0f90600090a250565b60335461010090046001600160a01b03163314612f6a5760405162461bcd60e51b8152600401610bdf90615933565b6000612fab86868080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061411292505050565b60008181526037602052604090205490915060ff166001816008811115612fd457612fd4614eef565b1480612ff157506006816008811115612fef57612fef614eef565b145b8061300d5750600881600881111561300b5761300b614eef565b145b6130595760405162461bcd60e51b815260206004820152601c60248201527f56616c696461746f72206e6f742072656764206f7220657869746564000000006044820152606401610bdf565b60008281526037602052604090819020805460ff19166007179055516312b3fc1960e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906312b3fc19906130c6908a908a908a908a908a90600401615cdd565b600060405180830381600087803b1580156130e057600080fd5b505af11580156130f4573d6000803e3d6000fd5b50505050817f63d54ea43f163d6e28fc23abec67eb7c3294e7e6f0620955a73cd8d17c7367f48686604051611a4c929190615d14565b606c818154811061313a57600080fd5b6000918252602090912001546001600160a01b0316905081565b60335461010090046001600160a01b03163314806131755750613175613882565b6131c15760405162461bcd60e51b815260206004820152601b60248201527f4e6f74205265676973747261746f72206f7220476f7665726e6f7200000000006044820152606401610bdf565b6128c86144c1565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614806132185750613203610a7c565b6001600160a01b0316336001600160a01b0316145b6132705760405162461bcd60e51b815260206004820152602360248201527f43616c6c6572206973206e6f7420746865205661756c74206f7220476f7665726044820152623737b960e91b6064820152608401610bdf565b600080516020615f44833981519152805460011981016132a25760405162461bcd60e51b8152600401610bdf90615b35565b600282556040516370a0823160e01b8152306004820152479060009082907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015613311573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613335919061588b565b61333f91906158cd565b90508015613372576133727f00000000000000000000000000000000000000000000000000000000000000008284614536565b5050600182555050565b613384613882565b6133a05760405162461bcd60e51b8152600401610bdf90615785565b8060005b8181101561344a5760008484838181106133c0576133c061583c565b90506020020160208101906133d59190614e31565b6001600160a01b0316036134425760405162461bcd60e51b815260206004820152602e60248201527f43616e206e6f742073657420616e20656d70747920616464726573732061732060448201526d30903932bbb0b932103a37b5b2b760911b6064820152608401610bdf565b6001016133a4565b507f04c0b9649497d316554306e53678d5f5f5dbc3a06f97dec13ff4cfe98b986bbc606c848460405161347f93929190615d28565b60405180910390a1611b44606c8484614c56565b60405163095ea7b360e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015260001960248301527f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b3906044016020604051808303816000875af1158015613523573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061354791906156c9565b50565b613552613882565b61356e5760405162461bcd60e51b8152600401610bdf90615785565b604051631a1b9a0b60e21b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063686e682c906135be90869086908690600401615dc1565b600060405180830381600087803b1580156135d857600080fd5b505af11580156135ec573d6000803e3d6000fd5b50505050505050565b6035818154811061111757600080fd5b60335461010090046001600160a01b031633146136345760405162461bcd60e51b8152600401610bdf90615933565b60335460ff16156136575760405162461bcd60e51b8152600401610bdf9061595c565b600061369889898080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061411292505050565b90506000808281526037602052604090205460ff1660088111156136be576136be614eef565b1461370b5760405162461bcd60e51b815260206004820152601c60248201527f56616c696461746f7220616c72656164792072656769737465726564000000006044820152606401610bdf565b60008181526037602052604090819020805460ff19166001179055516301ba3ee760e21b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906306e8fb9c9061377e908c908c908c908c908c908c908c908c90600401615e63565b600060405180830381600087803b15801561379857600080fd5b505af11580156137ac573d6000803e3d6000fd5b50505050807f50837f89f5e75ae0a7bcc858f53ea15fa398dc007fd52cbfe4683ae9a6c2d72288886040516137e2929190615d14565b60405180910390a2505050505050505050565b6137fd613882565b6138195760405162461bcd60e51b8152600401610bdf90615785565b606b54604080516001600160a01b03928316815291831660208301527fe48386b84419f4d36e0f96c10cc3510b6fb1a33795620c5098b22472bbe90796910160405180910390a1606b80546001600160a01b0319166001600160a01b0392909216919091179055565b600061389a600080516020615f648339815191525490565b6001600160a01b0316336001600160a01b031614905090565b6138bb613882565b6138d75760405162461bcd60e51b8152600401610bdf90615785565b6138ff817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b031661391f600080516020615f648339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461399f5760405162461bcd60e51b8152600401610bdf90615afe565b600080516020615f44833981519152805460011981016139d15760405162461bcd60e51b8152600401610bdf90615b35565b600282557f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b031614613a265760405162461bcd60e51b8152600401610bdf90615b5d565b613a31858447614536565b5060019055505050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614613a835760405162461bcd60e51b8152600401610bdf90615afe565b600080516020615f4483398151915280546001198101613ab55760405162461bcd60e51b8152600401610bdf90615b35565b600282556040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015613b20573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b44919061588b565b90506000603b5482613b5691906158a4565b9050801561337257603b8290556040805160008152602081018390526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016917f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f62910160405180910390a25050600182555050565b6060606c805480602002602001604051908101604052809291908181526020018280548015613c2a57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613c0c575b5050505050905090565b613c3c613882565b613c585760405162461bcd60e51b8152600401610bdf90615785565b6128c8614650565b6000600c7f0000000000000000000000000000000000000000000000000000000000000000613c8f8483615b88565b613c999190615bb1565b6129b29190615bb1565b604080516001600160401b038316602082015260009182918291720f3df6d732807ef1319fb7b8bb8522d0beac02910160408051601f1981840301815290829052613ced91615ec4565b600060405180830381855afa9150503d8060008114613d28576040519150601f19603f3d011682016040523d82523d6000602084013e613d2d565b606091505b5091509150818015613d40575060008151115b613d8c5760405162461bcd60e51b815260206004820152601860248201527f496e76616c696420626561636f6e2074696d657374616d7000000000000000006044820152606401610bdf565b80806020019051810190613da0919061588b565b949350505050565b60008281526034602052604081206001908101805460ff60a01b1916600160a11b179055603580549091613ddb916158a4565b81548110613deb57613deb61583c565b90600052602060002001549050806035836060015163ffffffff1681548110613e1657613e1661583c565b60009182526020808320909101929092556060840151838252603490925260409020600101805463ffffffff909216600160801b0263ffffffff60801b199092169190911790556035805480613e6e57613e6e6158b7565b60019003818190600052602060002001600090559055505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052613edb9084906146ca565b505050565b6060816001600160401b03811115613efa57613efa614f4f565b604051908082528060200260200182016040528015613f23578160200160208202803683370190505b50905060005b82811015613f8a576034600060358381548110613f4857613f4861583c565b9060005260206000200154815260200190815260200160002060000154828281518110613f7757613f7761583c565b6020908102919091010152600101613f29565b50919050565b8251613fa390606c906020860190614cb5565b50815181518114613fed5760405162461bcd60e51b8152602060048201526014602482015273496e76616c696420696e7075742061727261797360601b6044820152606401610bdf565b60005b8181101561403c5761403484828151811061400d5761400d61583c565b60200260200101518483815181106140275761402761583c565b602002602001015161479c565b600101613ff0565b5050505050565b604051632e1a7d4d60e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b1580156140a557600080fd5b505af11580156140b9573d6000803e3d6000fd5b5050505060006140cb82603b546144a9565b905080603b60008282546140df91906158a4565b9250508190555081603a60008282546140f891906158cd565b90915550506039805467ffffffffffffffff191690555050565b6000815160301461415a5760405162461bcd60e51b8152602060048201526012602482015271496e76616c6964207075626c6963206b657960701b6044820152606401610bdf565b604051600290614171908490600090602001615ee0565b60408051601f198184030181529082905261418b91615ec4565b602060405180830381855afa1580156141a8573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906129b2919061588b565b60006001600160401b038211156142335760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203660448201526534206269747360d01b6064820152608401610bdf565b5090565b600063ffffffff8211156142335760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201526532206269747360d01b6064820152608401610bdf565b6000603083146142ee5760405162461bcd60e51b815260206004820152601d60248201527f496e76616c69642076616c696461746f722062797465206c656e6774680000006044820152606401610bdf565b6142f66148fb565b90506000710961ef480eb55e80d19ad83579a64c0070026001600160a01b03168286868660405160200161432c93929190615f0f565b60408051601f198184030181529082905261434691615ec4565b60006040518083038185875af1925050503d8060008114614383576040519150601f19603f3d011682016040523d82523d6000602084013e614388565b606091505b50509050806143d95760405162461bcd60e51b815260206004820152601960248201527f5769746864726177616c2072657175657374206661696c6564000000000000006044820152606401610bdf565b509392505050565b6001600160a01b0381166144375760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f7220697320616464726573732830290000000000006044820152606401610bdf565b613547816149c2565b60006001600160801b038211156142335760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316044820152663238206269747360c81b6064820152608401610bdf565b60008183106144b857816144ba565b825b9392505050565b60335460ff16156144e45760405162461bcd60e51b8152600401610bdf9061595c565b6033805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586145193390565b6040516001600160a01b03909116815260200160405180910390a1565b600082116145865760405162461bcd60e51b815260206004820152601760248201527f4d75737420776974686472617720736f6d657468696e670000000000000000006044820152606401610bdf565b6001600160a01b0383166145d55760405162461bcd60e51b8152602060048201526016602482015275135d5cdd081cdc1958da599e481c9958da5c1a595b9d60521b6044820152606401610bdf565b80156145e4576145e481614a29565b6145ee8284614aeb565b6040805160008152602081018490526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016917f2717ead6b9200dd235aad468c9809ea400fe33ac69b5bfaa6d3e90fc922b63989101612af0565b60335460ff166146995760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610bdf565b6033805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33614519565b600061471f826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614b4b9092919063ffffffff16565b805190915015613edb578080602001905181019061473d91906156c9565b613edb5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610bdf565b6001600160a01b0382811660009081526067602052604090205416156147f95760405162461bcd60e51b81526020600482015260126024820152711c151bdad95b88185b1c9958591e481cd95d60721b6044820152606401610bdf565b6001600160a01b0382161580159061481957506001600160a01b03811615155b6148595760405162461bcd60e51b8152602060048201526011602482015270496e76616c69642061646472657373657360781b6044820152606401610bdf565b6001600160a01b03828116600081815260676020908152604080832080549587166001600160a01b031996871681179091556068805460018101825594527fa2153420d844928b4421650203c77babc8b33d7f2e7b450e2966db0c2209775390930180549095168417909455925190815290917fef6485b84315f9b1483beffa32aae9a0596890395e3d7521f1c5fbb51790e765910160405180910390a25050565b60405160009081908190710961ef480eb55e80d19ad83579a64c0070029082818181855afa9150503d806000811461494f576040519150601f19603f3d011682016040523d82523d6000602084013e614954565b606091505b5091509150818015614967575060008151115b6149a75760405162461bcd60e51b81526020600482015260116024820152704661696c656420746f206765742066656560781b6044820152606401610bdf565b808060200190518101906149bb919061588b565b9250505090565b806001600160a01b03166149e2600080516020615f648339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a3600080516020615f6483398151915255565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015614a8457600080fd5b505af1158015614a98573d6000803e3d6000fd5b505050505080603b6000828254614aaf91906158cd565b9091555050603a54614ac190826144a9565b603a6000828254614ad291906158a4565b90915550506039805467ffffffffffffffff1916905550565b614b1f6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168284613e89565b6000614b2d83603b546144a9565b905080603b6000828254614b4191906158a4565b9091555050505050565b6060613da0848460008585843b614ba45760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610bdf565b600080866001600160a01b03168587604051614bc09190615ec4565b60006040518083038185875af1925050503d8060008114614bfd576040519150601f19603f3d011682016040523d82523d6000602084013e614c02565b606091505b5091509150614c12828286614c1d565b979650505050505050565b60608315614c2c5750816144ba565b825115614c3c5782518084602001fd5b8160405162461bcd60e51b8152600401610bdf9190615f30565b828054828255906000526020600020908101928215614ca9579160200282015b82811115614ca95781546001600160a01b0319166001600160a01b03843516178255602090920191600190910190614c76565b50614233929150614d0a565b828054828255906000526020600020908101928215614ca9579160200282015b82811115614ca957825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190614cd5565b5b808211156142335760008155600101614d0b565b80356001600160401b0381168114614d3657600080fd5b919050565b600060408284031215613f8a57600080fd5b60008060008060808587031215614d6357600080fd5b84359350614d7360208601614d1f565b925060408501356001600160401b03811115614d8e57600080fd5b614d9a87828801614d3b565b92505060608501356001600160401b03811115614db657600080fd5b614dc287828801614d3b565b91505092959194509250565b80356001600160a01b0381168114614d3657600080fd5b60008060408385031215614df857600080fd5b614e0183614dce565b9150614e0f60208401614dce565b90509250929050565b600060208284031215614e2a57600080fd5b5035919050565b600060208284031215614e4357600080fd5b6144ba82614dce565b60008060408385031215614e5f57600080fd5b614e6883614dce565b946020939093013593505050565b600060808284031215613f8a57600080fd5b60008060408385031215614e9b57600080fd5b82356001600160401b03811115614eb157600080fd5b614ebd85828601614e76565b92505060208301356001600160401b03811115614ed957600080fd5b614ee585828601614e76565b9150509250929050565b634e487b7160e01b600052602160045260246000fd5b8581526001600160401b0385811660208301528416604082015263ffffffff8316606082015260a0810160038310614f3f57614f3f614eef565b8260808301529695505050505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715614f8d57614f8d614f4f565b604052919050565b60006001600160401b03821115614fae57614fae614f4f565b5060051b60200190565b600082601f830112614fc957600080fd5b8135614fdc614fd782614f95565b614f65565b8082825260208201915060208360051b860101925085831115614ffe57600080fd5b602085015b838110156150225761501481614dce565b835260209283019201615003565b5095945050505050565b60008060006060848603121561504157600080fd5b83356001600160401b0381111561505757600080fd5b61506386828701614fb8565b93505060208401356001600160401b0381111561507f57600080fd5b61508b86828701614fb8565b92505060408401356001600160401b038111156150a757600080fd5b6150b386828701614fb8565b9150509250925092565b600080604083850312156150d057600080fd5b82356001600160401b038111156150e657600080fd5b8301606081860312156150f857600080fd5b9150614e0f60208401614d1f565b60008083601f84011261511857600080fd5b5081356001600160401b0381111561512f57600080fd5b60208301915083602082850101111561514757600080fd5b9250929050565b60008060006040848603121561516357600080fd5b83356001600160401b0381111561517957600080fd5b61518586828701615106565b9094509250615198905060208501614d1f565b90509250925092565b60008060008060008060a087890312156151ba57600080fd5b6151c387614d1f565b9550602087013564ffffffffff811681146151dd57600080fd5b9450604087013593506060870135925060808701356001600160401b0381111561520657600080fd5b61521289828a01615106565b979a9699509497509295939492505050565b60008083601f84011261523657600080fd5b5081356001600160401b0381111561524d57600080fd5b6020830191508360208260051b850101111561514757600080fd5b600060a08284031215613f8a57600080fd5b600080600080600060e0868803121561529257600080fd5b85356001600160401b038111156152a857600080fd5b6152b488828901615106565b90965094505060208601356001600160401b038111156152d357600080fd5b6152df88828901615224565b90945092506152f390508760408801615268565b90509295509295909350565b6000806020838503121561531257600080fd5b82356001600160401b0381111561532857600080fd5b61533485828601615224565b90969095509350505050565b604081016009841061535457615354614eef565b92815264ffffffffff9190911660209091015290565b803563ffffffff81168114614d3657600080fd5b801515811461354757600080fd5b600060a0828403121561539e57600080fd5b60405160a081016001600160401b03811182821017156153c0576153c0614f4f565b6040529050806153cf8361536a565b81526153dd60208401614d1f565b60208201526153ee60408401614d1f565b604082015260608301356154018161537e565b6060820152608092830135920191909152919050565b600080600060e0848603121561542c57600080fd5b83356001600160401b0381111561544257600080fd5b8401601f8101861361545357600080fd5b8035615461614fd782614f95565b8082825260208201915060208360051b85010192508883111561548357600080fd5b6020840193505b828410156154ac5761549b84614d1f565b82526020938401939091019061548a565b955050505060208401359150615198856040860161538c565b600080600080600080600080610120898b0312156154e257600080fd5b88356001600160401b038111156154f857600080fd5b6155048b828c01615106565b90995097505060208901356001600160401b0381111561552357600080fd5b61552f8b828c01615224565b90975095505060408901356001600160401b0381111561554e57600080fd5b61555a8b828c01615106565b909550935050606089013591506155748a60808b01615268565b90509295985092959890939650565b60008060006060848603121561559857600080fd5b6155a184614dce565b92506155af60208501614dce565b929592945050506040919091013590565b602080825282518282018190526000918401906040840190835b818110156156015783516001600160a01b03168352602093840193909201916001016155da565b509095945050505050565b60006020828403121561561e57600080fd5b6144ba82614d1f565b6000808335601e1984360301811261563e57600080fd5b8301803591506001600160401b0382111561565857600080fd5b60200191503681900382131561514757600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b8481526001600160401b03841660208201526060604082015260006156bf60608301848661566d565b9695505050505050565b6000602082840312156156db57600080fd5b81516144ba8161537e565b85815264ffffffffff851660208201526001600160401b0384166040820152608060608201526000614c1260808301848661566d565b634e487b7160e01b600052601160045260246000fd5b60006001600160401b0383168061575957634e487b7160e01b600052601260045260246000fd5b806001600160401b0384160491505092915050565b80820281158282048414176129b2576129b261571c565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b6000808335601e198436030181126157d357600080fd5b8301803591506001600160401b038211156157ed57600080fd5b6020019150600581901b360382131561514757600080fd5b8481528360208201526060604082015260006156bf60608301848661566d565b6000816158345761583461571c565b506000190190565b634e487b7160e01b600052603260045260246000fd5b85815284602082015260806040820152600061587260808301858761566d565b905064ffffffffff831660608301529695505050505050565b60006020828403121561589d57600080fd5b5051919050565b818103818111156129b2576129b261571c565b634e487b7160e01b600052603160045260246000fd5b808201808211156129b2576129b261571c565b6000602082840312156158f257600080fd5b6144ba8261536a565b85815284602082015260806040820152600061591b60808301858761566d565b905063ffffffff831660608301529695505050505050565b6020808252600f908201526e2737ba102932b3b4b9ba3930ba37b960891b604082015260600190565b60208082526010908201526f14185d5cd8589b194e881c185d5cd95960821b604082015260600190565b6001600160f81b03199390931683526001600160a81b031991909116600183015260601b6bffffffffffffffffffffffff1916600c82015260200190565b6001600160401b0382811682821603908111156129b2576129b261571c565b60005b838110156159fe5781810151838201526020016159e6565b50506000910152565b60008151808452615a1f8160208601602086016159e3565b601f01601f19169290920160200192915050565b86815260a060208201526000615a4c60a0830188615a07565b6001600160401b03871660408401528281036060840152615a6e81868861566d565b9150506001600160401b0383166080830152979650505050505050565b608081526000615a9f60808301888a61566d565b8281036020840152615ab18188615a07565b90508281036040840152615ac681868861566d565b915050826060830152979650505050505050565b604081526000615aee60408301858761566d565b9050826020830152949350505050565b60208082526017908201527f43616c6c6572206973206e6f7420746865205661756c74000000000000000000604082015260600190565b6020808252600e908201526d1499595b9d1c985b9d0818d85b1b60921b604082015260600190565b602080825260119082015270155b9cdd5c1c1bdc9d195908185cdcd95d607a1b604082015260600190565b6001600160401b038181168382160290811690818114615baa57615baa61571c565b5092915050565b6001600160401b0381811683821601908111156129b2576129b261571c565b86815285602082015260a060408201526000615bf060a08301868861566d565b64ffffffffff9490941660608301525060800152949350505050565b80516020808301519190811015613f8a5760001960209190910360031b1b16919050565b81835260208301925060008160005b84811015615c6e576001600160401b03615c5883614d1f565b1686526020958601959190910190600101615c3f565b5093949350505050565b63ffffffff615c868261536a565b1682526001600160401b03615c9d60208301614d1f565b1660208301526001600160401b03615cb760408301614d1f565b1660408301526060810135615ccb8161537e565b15156060830152608090810135910152565b60e081526000615cf160e08301878961566d565b8281036020840152615d04818688615c30565b9150506156bf6040830184615c78565b602081526000613da0602083018486615c30565b6040808252845490820181905260008581526020812090916060840190835b81811015615d6e5783546001600160a01b0316835260019384019360209093019201615d47565b50508381036020808601919091528582520190508460005b85811015615db5576001600160a01b03615d9f83614dce565b1683526020928301929190910190600101615d86565b50909695505050505050565b60e080825284519082018190526000906020860190610100840190835b81811015615e055783516001600160401b0316835260209384019390920191600101615dde565b5050809250505083602083015263ffffffff83511660408301526001600160401b0360208401511660608301526001600160401b0360408401511660808301526060830151151560a0830152608083015160c0830152949350505050565b61012081526000615e7961012083018a8c61566d565b8281036020840152615e8c81898b615c30565b90508281036040840152615ea181878961566d565b915050836060830152615eb76080830184615c78565b9998505050505050505050565b60008251615ed68184602087016159e3565b9190910192915050565b60008351615ef28184602088016159e3565b6001600160801b0319939093169190920190815260100192915050565b8284823760c09190911b6001600160c01b0319169101908152600801919050565b6020815260006144ba6020830184615a0756fe53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac45357bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa2646970667358221220cee775df8d65f3ce633ccb3adad18cf76e2c539a2711bd2695a6e1e771d4f3bb64736f6c634300081c0033", + "libraries": {}, + "devdoc": { + "author": "Origin Protocol Inc", + "events": { + "Paused(address)": { + "details": "Emitted when the pause is triggered by `account`." + }, + "Unpaused(address)": { + "details": "Emitted when the pause is lifted by `account`." + } + }, + "kind": "dev", + "methods": { + "checkBalance(address)": { + "params": { + "_asset": "Address of WETH asset." + }, + "returns": { + "balance": " Total value in ETH" + } + }, + "constructor": { + "params": { + "_baseConfig": "Base strategy config with `platformAddress` not used so empty address `vaultAddress` the address of the OETH Vault contract", + "_beaconChainDepositContract": "Address of the beacon chain deposit contract", + "_beaconGenesisTimestamp": "The timestamp of the Beacon chain's genesis.", + "_beaconProofs": "Address of the Beacon Proofs contract that verifies beacon chain data", + "_ssvNetwork": "Address of the SSV Network contract", + "_ssvToken": "Address of the SSV Token contract", + "_wethAddress": "Address of the WETH Token contract" + } + }, + "deposit(address,uint256)": { + "params": { + "_amount": "Amount of WETH that was transferred to the strategy by the vault.", + "_asset": "Address of the WETH token." + } + }, + "getRewardTokenAddresses()": { + "returns": { + "_0": "address[] the reward token addresses." + } + }, + "initialize(address[],address[],address[])": { + "params": { + "_assets": "Not used so empty array", + "_pTokens": "Not used so empty array", + "_rewardTokenAddresses": "Not used so empty array" + } + }, + "paused()": { + "details": "Returns true if the contract is paused, and false otherwise." + }, + "registerSsvValidator(bytes,uint64[],bytes,uint256,(uint32,uint64,uint64,bool,uint256))": { + "params": { + "cluster": "The SSV cluster details including the validator count and SSV balance", + "operatorIds": "The operator IDs of the SSV Cluster", + "publicKey": "The public key of the validator", + "sharesData": "The shares data for the validator", + "ssvAmount": "The amount of SSV tokens to be deposited to the SSV cluster" + } + }, + "removeSsvValidator(bytes,uint64[],(uint32,uint64,uint64,bool,uint256))": { + "params": { + "cluster": "The SSV cluster details including the validator count and SSV balance", + "operatorIds": "The operator IDs of the SSV Cluster", + "publicKey": "The public key of the validator" + } + }, + "setHarvesterAddress(address)": { + "params": { + "_harvesterAddress": "Address of the harvester contract." + } + }, + "setRewardTokenAddresses(address[])": { + "params": { + "_rewardTokenAddresses": "Array of reward token addresses" + } + }, + "stakeEth((bytes,bytes,bytes32),uint64)": { + "params": { + "depositAmountGwei": "The amount of WETH to stake to the validator in Gwei.", + "validatorStakeData": "validator data needed to stake. The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot. Only the registrator can call this function." + } + }, + "supportsAsset(address)": { + "params": { + "_asset": "The address of the WETH token." + } + }, + "transferGovernance(address)": { + "params": { + "_newGovernor": "Address of the new Governor" + } + }, + "transferToken(address,uint256)": { + "params": { + "_amount": "Amount of the asset to transfer", + "_asset": "Address for the asset" + } + }, + "validatorWithdrawal(bytes,uint64)": { + "params": { + "amountGwei": "The amount of ETH to be withdrawn from the validator in Gwei. A zero amount will trigger a full withdrawal.", + "publicKey": "The public key of the validator" + } + }, + "verifyBalances((bytes32,bytes,bytes32[],bytes[]),(bytes32,bytes,uint32[],bytes[]))": { + "params": { + "balanceProofs": "a `BalanceProofs` struct containing the following: - balancesContainerRoot: The merkle root of the balances container - balancesContainerProof: The merkle proof for the balances container to the beacon block root. This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node. - validatorBalanceLeaves: Array of leaf nodes containing the validator balance with three other balances. - validatorBalanceProofs: Array of merkle proofs for the validator balance to the Balances container root. This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.", + "pendingDepositProofs": "a `PendingDepositProofs` struct containing the following: - pendingDepositContainerRoot: The merkle root of the pending deposits list container - pendingDepositContainerProof: The merkle proof from the pending deposits list container to the beacon block root. This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node. - pendingDepositIndexes: Array of indexes in the pending deposits list container for each of the strategy's deposits. - pendingDepositProofs: Array of merkle proofs for each strategy deposit in the beacon chain's pending deposit list container to the pending deposits list container root. These are 28 witness hashes of 32 bytes each concatenated together starting from the leaf node." + } + }, + "verifyDeposit(bytes32,uint64,(uint64,bytes),(uint64,bytes))": { + "params": { + "depositProcessedSlot": "Any slot on or after the strategy's deposit was processed on the beacon chain. Can not be a slot with pending deposits with the same slot as the deposit being verified. Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root set for the next block timestamp in 12 seconds time.", + "firstPendingDeposit": "a `FirstPendingDepositSlotProofData` struct containing: - slot: The beacon chain slot of the first deposit in the beacon chain's deposit queue. Can be any non-zero value if the deposit queue is empty. - proof: The merkle proof of the first pending deposit's slot to the beacon block root. Can be either: * 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty. * 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty. The 32 byte witness hashes are concatenated together starting from the leaf node.", + "pendingDepositRoot": "The unique identifier of the deposit emitted in `ETHStaked` from the `stakeEth` function.", + "strategyValidatorData": "a `StrategyValidatorProofData` struct containing: - withdrawableEpoch: The withdrawable epoch of the validator the strategy is depositing to. - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the validator the strategy is depositing to, to the beacon block root. This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node." + } + }, + "verifyValidator(uint64,uint40,bytes32,bytes32,bytes)": { + "params": { + "nextBlockTimestamp": "The timestamp of the execution layer block after the beacon chain slot we are verifying. The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp, which is the beacon block root of the previous block.", + "pubKeyHash": "The hash of the validator's public key using the Beacon Chain's format", + "validatorIndex": "The index of the validator on the beacon chain.", + "validatorPubKeyProof": "The merkle proof for the validator public key to the beacon block root. This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node. BeaconBlock.state.validators[validatorIndex].pubkey", + "withdrawalCredentials": "contain the validator type and withdrawal address. These can be incorrect and/or malformed. In case of incorrect withdrawalCredentials the validator deposit has been front run" + } + }, + "withdraw(address,address,uint256)": { + "params": { + "_amount": "Amount of WETH to withdraw.", + "_asset": "Address of the WETH token.", + "_recipient": "Address to receive withdrawn assets." + } + }, + "withdrawSSV(uint64[],uint256,(uint32,uint64,uint64,bool,uint256))": { + "details": "A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.", + "params": { + "cluster": "The SSV cluster details including the validator count and SSV balance", + "operatorIds": "The operator IDs of the SSV Cluster", + "ssvAmount": "The amount of SSV tokens to be withdrawn from the SSV cluster" + } + } + }, + "title": "Compounding Staking SSV Strategy", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "BEACON_PROOFS()": { + "notice": "Address of the Beacon Proofs contract that verifies beacon chain data" + }, + "SSV_NETWORK()": { + "notice": "The address of the SSV Network contract used to interface with" + }, + "SSV_TOKEN()": { + "notice": "SSV ERC20 token that serves as a payment for operating SSV validators" + }, + "assetToPToken(address)": { + "notice": "asset => pToken (Platform Specific Token Address)" + }, + "checkBalance(address)": { + "notice": "Accounts for all the assets managed by this strategy which includes: 1. The current WETH in this strategy contract 2. The last verified ETH balance, total deposits and total validator balances" + }, + "claimGovernance()": { + "notice": "Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor." + }, + "collectRewardTokens()": { + "notice": "Collect accumulated reward token and send to Vault." + }, + "deposit(address,uint256)": { + "notice": "Unlike other strategies, this does not deposit assets into the underlying platform. It just checks the asset is WETH and emits the Deposit event. To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used." + }, + "depositAll()": { + "notice": "Unlike other strategies, this does not deposit assets into the underlying platform. It just emits the Deposit event. To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used." + }, + "depositList(uint256)": { + "notice": "List of strategy deposit IDs to a validator. The ID is the merkle root of the pending deposit data which is unique for each validator, amount and block. Duplicate pending deposit roots are prevented so can be used as an identifier to each strategy deposit. The list can be for deposits waiting to be verified as processed on the beacon chain, or deposits that have been verified to an exiting validator and is now waiting for the validator's balance to be swept. The list may not be ordered by time of deposit. Removed deposits will move the last deposit to the removed index." + }, + "depositListLength()": { + "notice": "Returns the number of deposits waiting to be verified as processed on the beacon chain, or deposits that have been verified to an exiting validator and is now waiting for the validator's balance to be swept." + }, + "deposits(bytes32)": { + "notice": "Mapping of the pending deposit roots to the deposit data" + }, + "firstDeposit()": { + "notice": "Restricts to only one deposit to an unverified validator at a time. This is to limit front-running attacks of deposits to the beacon chain contract." + }, + "getRewardTokenAddresses()": { + "notice": "Get the reward token addresses." + }, + "governor()": { + "notice": "Returns the address of the current Governor." + }, + "harvesterAddress()": { + "notice": "Address of the Harvester contract allowed to collect reward tokens" + }, + "initialize(address[],address[],address[])": { + "notice": "Set up initial internal state including 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract" + }, + "isGovernor()": { + "notice": "Returns true if the caller is the current Governor." + }, + "lastVerifiedEthBalance()": { + "notice": "The last verified ETH balance of the strategy" + }, + "platformAddress()": { + "notice": "Address of the underlying platform" + }, + "registerSsvValidator(bytes,uint64[],bytes,uint256,(uint32,uint64,uint64,bool,uint256))": { + "notice": "Registers a single validator in a SSV Cluster. Only the Registrator can call this function." + }, + "removePToken(uint256)": { + "notice": "is not supported for this strategy as there is no platform token." + }, + "removeSsvValidator(bytes,uint64[],(uint32,uint64,uint64,bool,uint256))": { + "notice": "Remove the validator from the SSV Cluster after: - the validator has been exited from `validatorWithdrawal` or slashed - the validator has incorrectly registered and can not be staked to - the initial deposit was front-run and the withdrawal address is not this strategy's address. Make sure `validatorWithdrawal` is called with a zero amount and the validator has exited the Beacon chain. If removed before the validator has exited the beacon chain will result in the validator being slashed. Only the registrator can call this function." + }, + "resetFirstDeposit()": { + "notice": "Reset the `firstDeposit` flag to false so deposits to unverified validators can be made again." + }, + "rewardTokenAddresses(uint256)": { + "notice": "Address of the reward tokens. eg CRV, BAL, CVX, AURA" + }, + "safeApproveAllTokens()": { + "notice": "Approves the SSV Network contract to transfer SSV tokens for validator registration." + }, + "setHarvesterAddress(address)": { + "notice": "Set the Harvester contract that can collect rewards." + }, + "setPTokenAddress(address,address)": { + "notice": "is not supported for this strategy as there is no platform token." + }, + "setRegistrator(address)": { + "notice": "Set the address of the registrator which can register, exit and remove validators" + }, + "setRewardTokenAddresses(address[])": { + "notice": "Set the reward token addresses. Any old addresses will be overwritten." + }, + "snapBalances()": { + "notice": "Stores the current ETH balance at the current block and beacon block root of the slot that is associated with the previous block. When snapping / verifying balance it is of a high importance that there is no miss-match in respect to ETH that is held by the contract and balances that are verified on the validators. First some context on the beacon-chain block building behaviour. Relevant parts of constructing a block on the beacon chain consist of: - process_withdrawals: ETH is deducted from the validator's balance - process_execution_payload: immediately after the previous step executing all the transactions - apply the withdrawals: adding ETH to the recipient which is the withdrawal address contained in the withdrawal credentials of the exited validators That means that balance increases which are part of the post-block execution state are done within the block, but the transaction that are contained within that block can not see / interact with the balance from the exited validators. Only transactions in the next block can do that. When snap balances is performed the state of the chain is snapped across 2 separate chain states: - ETH balance of the contract is recorded on block X -> and corresponding slot Y - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1 given there were no missed slots. It could also be Y - 2, Y - 3 depending on how many slots have not managed to propose a block. For the sake of simplicity this slot will be referred to as Y - 1 as it makes no difference in the argument Given these 2 separate chain states it is paramount that verify balances can not experience miss-counting ETH or much more dangerous double counting of the ETH. When verifyBalances is called it is performed on the current block Z where Z > X. Verify balances adds up all the ETH (omitting WETH) controlled by this contract: - ETH balance in the contract on block X - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1 - ETH balance in validators that are active in slot Y - 1 - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner) and have their balance visible to transactions in slot Y and corresponding block X (or sooner) Lets verify the correctness of ETH accounting given the above described behaviour. *ETH balance in the contract on block X* This is an ETH balance of the contract on a non current X block. Any ETH leaving the contract as a result of a withdrawal subtracts from the ETH accounted for on block X if `verifyBalances` has already been called. It also invalidates a `snapBalances` in case `verifyBalances` has not been called yet. Not performing this would result in not accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z]. Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH accounted for since the last `verifyBalances` has been called. And it invalidates the `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this would result in double counting the `stakedEth` since it would be present once in the snapped contract balance and the second time in deposit storage variables. This behaviour is correct. *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1* The contract sums up all the ETH that has been deposited to the Beacon chain deposit contract at block Z. The execution layer doesn't have direct access to the state of deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be deposited it needs to be sure to not double count ETH that is in deposits (storage vars) and could also be part of the validator balances. It does that by verifying that at slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since the last snap till now all are still in queue. Which ensures they can not be part of the validator balances in later steps. This behaviour is correct. *ETH balance in validators that are active in slot Y - 1* The contract is verifying none of the deposits on Y - 1 slot have been processed and for that reason it checks the validator balances in the same slot. Ensuring accounting correctness. This behaviour is correct. *The withdrawn validators* The withdrawn validators could have their balances deducted in any slot before slot Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets look at the \"worst case scenario\" where the validator withdrawal is processed in the slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot Y -> block X. The ETH balance on the contract is snapped at block X meaning that even if the validator exits at the latest possible time it is paramount that the ETH balance on the execution layer is recorded in the next block. Correctly accounting for the withdrawn ETH. Worth mentioning if the validator exit is processed by the slot Y and balance increase seen on the execution layer on block X + 1 the withdrawal is ignored by both the validator balance verification as well as execution layer contract balance snap. This behaviour is correct. The validator balances on the beacon chain can then be proved with `verifyBalances`." + }, + "snappedBalance()": { + "notice": "Mapping of the block root to the balances at that slot" + }, + "stakeEth((bytes,bytes,bytes32),uint64)": { + "notice": "Stakes WETH in this strategy to a compounding validator. The the first deposit to a new validator, the amount must be 1 ETH. Another deposit of at least 31 ETH is required for the validator to be activated. This second deposit has to be done after the validator has been verified. Does not convert any ETH sitting in this strategy to WETH. There can not be two deposits to the same validator in the same block for the same amount. Function is pausable so in case a run-away Registrator can be prevented from continuing to deposit funds to slashed or undesired validators." + }, + "supportsAsset(address)": { + "notice": "Returns bool indicating whether asset is supported by the strategy." + }, + "transferGovernance(address)": { + "notice": "Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete" + }, + "transferToken(address,uint256)": { + "notice": "Transfer token to governor. Intended for recovering tokens stuck in strategy contracts, i.e. mistaken sends." + }, + "validator(bytes32)": { + "notice": "Mapping of the hash of the validator's public key to the validator state and index. Uses the Beacon chain hashing for BLSPubkey which is sha256(abi.encodePacked(validator.pubkey, bytes16(0)))" + }, + "validatorRegistrator()": { + "notice": "Address of the registrator - allowed to register, withdraw, exit and remove validators" + }, + "validatorWithdrawal(bytes,uint64)": { + "notice": "Request a full or partial withdrawal from a validator. A zero amount will trigger a full withdrawal. If the remaining balance is < 32 ETH then only the amount in excess of 32 ETH will be withdrawn. Only the Registrator can call this function. 1 wei of value should be sent with the tx to pay for the withdrawal request fee. If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any. If no ETH balance, the tx will revert." + }, + "vaultAddress()": { + "notice": "Address of the OToken vault" + }, + "verifiedValidators(uint256)": { + "notice": "List of validator public key hashes that have been verified to exist on the beacon chain. These have had a deposit processed and the validator's balance increased. Validators will be removed from this list when its verified they have a zero balance." + }, + "verifiedValidatorsLength()": { + "notice": "Returns the number of verified validators." + }, + "verifyBalances((bytes32,bytes,bytes32[],bytes[]),(bytes32,bytes,uint32[],bytes[]))": { + "notice": "Verifies the balances of all active validators on the beacon chain and checks each of the strategy's deposits are still to be processed by the beacon chain." + }, + "verifyDeposit(bytes32,uint64,(uint64,bytes),(uint64,bytes))": { + "notice": "Verifies a deposit on the execution layer has been processed by the beacon chain. This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance. Important: this function has a limitation where `depositProcessedSlot` that is passed by the off-chain verifier requires a slot immediately after it to propose a block otherwise the `BeaconRoots.parentBlockRoot` will fail. This shouldn't be a problem, since by the current behaviour of beacon chain only 1%-3% slots don't propose a block." + }, + "verifyValidator(uint64,uint40,bytes32,bytes32,bytes)": { + "notice": "Verifies a validator's index to its public key. Adds to the list of verified validators if the validator's withdrawal address is this strategy's address. Marks the validator as invalid and removes the deposit if the withdrawal address is not this strategy's address." + }, + "withdraw(address,address,uint256)": { + "notice": "Withdraw ETH and WETH from this strategy contract." + }, + "withdrawAll()": { + "notice": "Transfer all WETH deposits, ETH from validator withdrawals and ETH from execution rewards in this strategy to the vault. This does not withdraw from the validators. That has to be done separately with the `validatorWithdrawal` operation." + }, + "withdrawSSV(uint64[],uint256,(uint32,uint64,uint64,bool,uint256))": { + "notice": "Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators." + } + }, + "notice": "Strategy to deploy funds into DVT validators powered by the SSV Network", + "version": 1 + }, + "storageLayout": { + "storage": [ + { + "astId": 8857, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "initialized", + "offset": 0, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 8860, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "initializing", + "offset": 1, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 8900, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "______gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage" + }, + { + "astId": 17, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "_paused", + "offset": 0, + "slot": "51", + "type": "t_bool" + }, + { + "astId": 5323, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "validatorRegistrator", + "offset": 1, + "slot": "51", + "type": "t_address" + }, + { + "astId": 5344, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "firstDeposit", + "offset": 21, + "slot": "51", + "type": "t_bool" + }, + { + "astId": 5350, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "deposits", + "offset": 0, + "slot": "52", + "type": "t_mapping(t_bytes32,t_struct(DepositData)5341_storage)" + }, + { + "astId": 5354, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "depositList", + "offset": 0, + "slot": "53", + "type": "t_array(t_bytes32)dyn_storage" + }, + { + "astId": 5374, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "verifiedValidators", + "offset": 0, + "slot": "54", + "type": "t_array(t_bytes32)dyn_storage" + }, + { + "astId": 5380, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "validator", + "offset": 0, + "slot": "55", + "type": "t_mapping(t_bytes32,t_struct(ValidatorData)5370_storage)" + }, + { + "astId": 5392, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "snappedBalance", + "offset": 0, + "slot": "56", + "type": "t_struct(Balances)5388_storage" + }, + { + "astId": 5395, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "lastVerifiedEthBalance", + "offset": 0, + "slot": "58", + "type": "t_uint256" + }, + { + "astId": 5398, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "depositedWethAccountedFor", + "offset": 0, + "slot": "59", + "type": "t_uint256" + }, + { + "astId": 5402, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "__gap", + "offset": 0, + "slot": "60", + "type": "t_array(t_uint256)41_storage" + }, + { + "astId": 8980, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "_deprecated_platformAddress", + "offset": 0, + "slot": "101", + "type": "t_address" + }, + { + "astId": 8983, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "_deprecated_vaultAddress", + "offset": 0, + "slot": "102", + "type": "t_address" + }, + { + "astId": 8988, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "assetToPToken", + "offset": 0, + "slot": "103", + "type": "t_mapping(t_address,t_address)" + }, + { + "astId": 8992, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "assetsMapped", + "offset": 0, + "slot": "104", + "type": "t_array(t_address)dyn_storage" + }, + { + "astId": 8994, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "_deprecated_rewardTokenAddress", + "offset": 0, + "slot": "105", + "type": "t_address" + }, + { + "astId": 8996, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "_deprecated_rewardLiquidationThreshold", + "offset": 0, + "slot": "106", + "type": "t_uint256" + }, + { + "astId": 8999, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "harvesterAddress", + "offset": 0, + "slot": "107", + "type": "t_address" + }, + { + "astId": 9003, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "rewardTokenAddresses", + "offset": 0, + "slot": "108", + "type": "t_array(t_address)dyn_storage" + }, + { + "astId": 9007, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "_reserved", + "offset": 0, + "slot": "109", + "type": "t_array(t_int256)98_storage" + }, + { + "astId": 4659, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "__gap", + "offset": 0, + "slot": "207", + "type": "t_array(t_uint256)50_storage" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_address)dyn_storage": { + "base": "t_address", + "encoding": "dynamic_array", + "label": "address[]", + "numberOfBytes": "32" + }, + "t_array(t_bytes32)dyn_storage": { + "base": "t_bytes32", + "encoding": "dynamic_array", + "label": "bytes32[]", + "numberOfBytes": "32" + }, + "t_array(t_int256)98_storage": { + "base": "t_int256", + "encoding": "inplace", + "label": "int256[98]", + "numberOfBytes": "3136" + }, + "t_array(t_uint256)41_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[41]", + "numberOfBytes": "1312" + }, + "t_array(t_uint256)50_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "encoding": "inplace", + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes32": { + "encoding": "inplace", + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_enum(DepositStatus)5328": { + "encoding": "inplace", + "label": "enum CompoundingValidatorManager.DepositStatus", + "numberOfBytes": "1" + }, + "t_enum(ValidatorState)5364": { + "encoding": "inplace", + "label": "enum CompoundingValidatorManager.ValidatorState", + "numberOfBytes": "1" + }, + "t_int256": { + "encoding": "inplace", + "label": "int256", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_address)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => address)", + "numberOfBytes": "32", + "value": "t_address" + }, + "t_mapping(t_bytes32,t_struct(DepositData)5341_storage)": { + "encoding": "mapping", + "key": "t_bytes32", + "label": "mapping(bytes32 => struct CompoundingValidatorManager.DepositData)", + "numberOfBytes": "32", + "value": "t_struct(DepositData)5341_storage" + }, + "t_mapping(t_bytes32,t_struct(ValidatorData)5370_storage)": { + "encoding": "mapping", + "key": "t_bytes32", + "label": "mapping(bytes32 => struct CompoundingValidatorManager.ValidatorData)", + "numberOfBytes": "32", + "value": "t_struct(ValidatorData)5370_storage" + }, + "t_struct(Balances)5388_storage": { + "encoding": "inplace", + "label": "struct CompoundingValidatorManager.Balances", + "members": [ + { + "astId": 5383, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "blockRoot", + "offset": 0, + "slot": "0", + "type": "t_bytes32" + }, + { + "astId": 5385, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "timestamp", + "offset": 0, + "slot": "1", + "type": "t_uint64" + }, + { + "astId": 5387, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "ethBalance", + "offset": 8, + "slot": "1", + "type": "t_uint128" + } + ], + "numberOfBytes": "64" + }, + "t_struct(DepositData)5341_storage": { + "encoding": "inplace", + "label": "struct CompoundingValidatorManager.DepositData", + "members": [ + { + "astId": 5331, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "pubKeyHash", + "offset": 0, + "slot": "0", + "type": "t_bytes32" + }, + { + "astId": 5333, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "amountGwei", + "offset": 0, + "slot": "1", + "type": "t_uint64" + }, + { + "astId": 5335, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "slot", + "offset": 8, + "slot": "1", + "type": "t_uint64" + }, + { + "astId": 5337, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "depositIndex", + "offset": 16, + "slot": "1", + "type": "t_uint32" + }, + { + "astId": 5340, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "status", + "offset": 20, + "slot": "1", + "type": "t_enum(DepositStatus)5328" + } + ], + "numberOfBytes": "64" + }, + "t_struct(ValidatorData)5370_storage": { + "encoding": "inplace", + "label": "struct CompoundingValidatorManager.ValidatorData", + "members": [ + { + "astId": 5367, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "state", + "offset": 0, + "slot": "0", + "type": "t_enum(ValidatorState)5364" + }, + { + "astId": 5369, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "index", + "offset": 1, + "slot": "0", + "type": "t_uint40" + } + ], + "numberOfBytes": "32" + }, + "t_uint128": { + "encoding": "inplace", + "label": "uint128", + "numberOfBytes": "16" + }, + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint32": { + "encoding": "inplace", + "label": "uint32", + "numberOfBytes": "4" + }, + "t_uint40": { + "encoding": "inplace", + "label": "uint40", + "numberOfBytes": "5" + }, + "t_uint64": { + "encoding": "inplace", + "label": "uint64", + "numberOfBytes": "8" + } + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/CompoundingStakingSSVStrategyP2P.json b/contracts/deployments/hoodi/CompoundingStakingSSVStrategyP2P.json new file mode 100644 index 0000000000..e208dc433e --- /dev/null +++ b/contracts/deployments/hoodi/CompoundingStakingSSVStrategyP2P.json @@ -0,0 +1,2336 @@ +{ + "address": "0x6459069275849C6Aa019594cC884946f8A3Cc78E", + "abi": [ + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "platformAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "vaultAddress", + "type": "address" + } + ], + "internalType": "struct InitializableAbstractStrategy.BaseStrategyConfig", + "name": "_baseConfig", + "type": "tuple" + }, + { + "internalType": "address", + "name": "_wethAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "_ssvToken", + "type": "address" + }, + { + "internalType": "address", + "name": "_ssvNetwork", + "type": "address" + }, + { + "internalType": "address", + "name": "_beaconChainDepositContract", + "type": "address" + }, + { + "internalType": "address", + "name": "_beaconProofs", + "type": "address" + }, + { + "internalType": "uint64", + "name": "_beaconGenesisTimestamp", + "type": "uint64" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "blockRoot", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "ethBalance", + "type": "uint256" + } + ], + "name": "BalancesSnapped", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "timestamp", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalDepositsWei", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalValidatorBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "ethBalance", + "type": "uint256" + } + ], + "name": "BalancesVerified", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_pToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "depositID", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountWei", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "withdrawableEpoch", + "type": "uint64" + } + ], + "name": "DepositToValidatorExiting", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "depositID", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountWei", + "type": "uint256" + } + ], + "name": "DepositValidatorExited", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "depositID", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountWei", + "type": "uint256" + } + ], + "name": "DepositVerified", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "depositID", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "pubKey", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountWei", + "type": "uint256" + } + ], + "name": "ETHStaked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "FirstDepositReset", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "GovernorshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_oldHarvesterAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_newHarvesterAddress", + "type": "address" + } + ], + "name": "HarvesterAddressesUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_pToken", + "type": "address" + } + ], + "name": "PTokenAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_pToken", + "type": "address" + } + ], + "name": "PTokenRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "PendingGovernorshipTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "newAddress", + "type": "address" + } + ], + "name": "RegistratorChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "_oldAddresses", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "_newAddresses", + "type": "address[]" + } + ], + "name": "RewardTokenAddressesUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "rewardToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "RewardTokenCollected", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + } + ], + "name": "SSVValidatorRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + } + ], + "name": "SSVValidatorRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "newAddress", + "type": "address" + } + ], + "name": "StakingMonitorChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + } + ], + "name": "ValidatorInvalid", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "uint40", + "name": "validatorIndex", + "type": "uint40" + } + ], + "name": "ValidatorVerified", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountWei", + "type": "uint256" + } + ], + "name": "ValidatorWithdraw", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_pToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "Withdrawal", + "type": "event" + }, + { + "inputs": [], + "name": "BEACON_CHAIN_DEPOSIT_CONTRACT", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "BEACON_GENESIS_TIMESTAMP", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "BEACON_PROOFS", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "SSV_NETWORK", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "SSV_TOKEN", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "VAULT_ADDRESS", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "WETH", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "assetToPToken", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + } + ], + "name": "checkBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "claimGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "collectRewardTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "deposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "depositAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "depositList", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "depositListLength", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "depositedWethAccountedFor", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "deposits", + "outputs": [ + { + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "amountGwei", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "slot", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "depositIndex", + "type": "uint32" + }, + { + "internalType": "enum CompoundingValidatorManager.DepositStatus", + "name": "status", + "type": "uint8" + }, + { + "internalType": "uint64", + "name": "withdrawableEpoch", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "firstDeposit", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRewardTokenAddresses", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "governor", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "harvesterAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_rewardTokenAddresses", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "_assets", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "_pTokens", + "type": "address[]" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isGovernor", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastVerifiedEthBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "nextDepositID", + "outputs": [ + { + "internalType": "uint128", + "name": "", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "platformAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "internalType": "bytes", + "name": "sharesData", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "ssvAmount", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "internalType": "struct Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "registerSsvValidator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "removePToken", + "outputs": [], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "internalType": "struct Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "removeSsvValidator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "resetFirstDeposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "rewardTokenAddresses", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "safeApproveAllTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_harvesterAddress", + "type": "address" + } + ], + "name": "setHarvesterAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "setPTokenAddress", + "outputs": [], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "setRegistrator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_rewardTokenAddresses", + "type": "address[]" + } + ], + "name": "setRewardTokenAddresses", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "snapBalances", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "snappedBalance", + "outputs": [ + { + "internalType": "bytes32", + "name": "blockRoot", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "timestamp", + "type": "uint64" + }, + { + "internalType": "uint128", + "name": "ethBalance", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes", + "name": "pubkey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "depositDataRoot", + "type": "bytes32" + } + ], + "internalType": "struct CompoundingValidatorManager.ValidatorStakeData", + "name": "validatorStakeData", + "type": "tuple" + }, + { + "internalType": "uint64", + "name": "depositAmountGwei", + "type": "uint64" + } + ], + "name": "stakeEth", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + } + ], + "name": "supportsAsset", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newGovernor", + "type": "address" + } + ], + "name": "transferGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "transferToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "validator", + "outputs": [ + { + "internalType": "enum CompoundingValidatorManager.ValidatorState", + "name": "state", + "type": "uint8" + }, + { + "internalType": "uint40", + "name": "index", + "type": "uint40" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "validatorRegistrator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "amountGwei", + "type": "uint64" + } + ], + "name": "validatorWithdrawal", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "vaultAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "verifiedValidators", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "verifiedValidatorsLength", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "validatorVerificationBlockTimestamp", + "type": "uint64" + }, + { + "components": [ + { + "internalType": "uint64", + "name": "slot", + "type": "uint64" + }, + { + "internalType": "uint40", + "name": "validatorIndex", + "type": "uint40" + }, + { + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "pendingDepositPubKeyProof", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "withdrawableEpochProof", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "validatorPubKeyProof", + "type": "bytes" + } + ], + "internalType": "struct CompoundingValidatorManager.FirstPendingDepositWithdrawableProofData", + "name": "firstPendingDeposit", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "balancesContainerRoot", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "balancesContainerProof", + "type": "bytes" + }, + { + "internalType": "bytes32[]", + "name": "validatorBalanceLeaves", + "type": "bytes32[]" + }, + { + "internalType": "bytes[]", + "name": "validatorBalanceProofs", + "type": "bytes[]" + } + ], + "internalType": "struct CompoundingValidatorManager.BalanceProofs", + "name": "balanceProofs", + "type": "tuple" + } + ], + "name": "verifyBalances", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "depositID", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "depositProcessedSlot", + "type": "uint64" + }, + { + "components": [ + { + "internalType": "uint64", + "name": "slot", + "type": "uint64" + }, + { + "internalType": "bytes", + "name": "proof", + "type": "bytes" + } + ], + "internalType": "struct CompoundingValidatorManager.FirstPendingDepositSlotProofData", + "name": "firstPendingDeposit", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint64", + "name": "withdrawableEpoch", + "type": "uint64" + }, + { + "internalType": "bytes", + "name": "withdrawableEpochProof", + "type": "bytes" + } + ], + "internalType": "struct CompoundingValidatorManager.StrategyValidatorProofData", + "name": "strategyValidatorData", + "type": "tuple" + } + ], + "name": "verifyDeposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "nextBlockTimestamp", + "type": "uint64" + }, + { + "internalType": "uint40", + "name": "validatorIndex", + "type": "uint40" + }, + { + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "withdrawalAddress", + "type": "address" + }, + { + "internalType": "bytes", + "name": "validatorPubKeyProof", + "type": "bytes" + } + ], + "name": "verifyValidator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_recipient", + "type": "address" + }, + { + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "internalType": "uint256", + "name": "ssvAmount", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "internalType": "struct Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "withdrawSSV", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "transactionHash": "0x1aab10bef4e8d779e4a12a80ae25555fe616871f1b70da44965e964552c8c0ea", + "receipt": { + "to": null, + "from": "0xf7749B41db006860cEc0650D18b8013d69C44Eeb", + "contractAddress": "0x6459069275849C6Aa019594cC884946f8A3Cc78E", + "transactionIndex": 17, + "gasUsed": "5216738", + "logsBloom": "0x00000000000000000000000000000000100000040000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000020000000000000000000000000200000000000000000000000000000000000000000", + "blockHash": "0x486cda5ab5f7e392bfab87ae6402a3d455152dbe3f214d064d413f0f5402c6c2", + "transactionHash": "0x1aab10bef4e8d779e4a12a80ae25555fe616871f1b70da44965e964552c8c0ea", + "logs": [ + { + "transactionIndex": 17, + "blockNumber": 1108797, + "transactionHash": "0x1aab10bef4e8d779e4a12a80ae25555fe616871f1b70da44965e964552c8c0ea", + "address": "0x6459069275849C6Aa019594cC884946f8A3Cc78E", + "topics": [ + "0xc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "data": "0x", + "logIndex": 34, + "blockHash": "0x486cda5ab5f7e392bfab87ae6402a3d455152dbe3f214d064d413f0f5402c6c2" + } + ], + "blockNumber": 1108797, + "cumulativeGasUsed": "13309880", + "status": 1, + "byzantium": true + }, + "args": [ + [ + "0x0000000000000000000000000000000000000000", + "0xD0cC28bc8F4666286F3211e465ecF1fe5c72AC8B" + ], + "0x2387fD72C1DA19f6486B843F5da562679FbB4057", + "0x9F5d4Ec84fC4785788aB44F9de973cF34F7A038e", + "0x58410Bef803ECd7E63B23664C586A6DB72DAf59c", + "0x00000000219ab540356cBB839Cbe05303d7705Fa", + "0x0b24D24d583e83F3405De16c27e5aE36E2214ab3", + 1742213400 + ], + "numDeployments": 21, + "solcInputHash": "a4cf2397e59aecfd08078ec66ccb9b2b", + "metadata": "{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"platformAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"vaultAddress\",\"type\":\"address\"}],\"internalType\":\"struct InitializableAbstractStrategy.BaseStrategyConfig\",\"name\":\"_baseConfig\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"_wethAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_ssvToken\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_ssvNetwork\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_beaconChainDepositContract\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_beaconProofs\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"_beaconGenesisTimestamp\",\"type\":\"uint64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"blockRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"ethBalance\",\"type\":\"uint256\"}],\"name\":\"BalancesSnapped\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"totalDepositsWei\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"totalValidatorBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"ethBalance\",\"type\":\"uint256\"}],\"name\":\"BalancesVerified\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_pToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"Deposit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"depositID\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amountWei\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"withdrawableEpoch\",\"type\":\"uint64\"}],\"name\":\"DepositToValidatorExiting\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"depositID\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amountWei\",\"type\":\"uint256\"}],\"name\":\"DepositValidatorExited\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"depositID\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amountWei\",\"type\":\"uint256\"}],\"name\":\"DepositVerified\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"depositID\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"pubKey\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amountWei\",\"type\":\"uint256\"}],\"name\":\"ETHStaked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"FirstDepositReset\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"GovernorshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_oldHarvesterAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_newHarvesterAddress\",\"type\":\"address\"}],\"name\":\"HarvesterAddressesUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_pToken\",\"type\":\"address\"}],\"name\":\"PTokenAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_pToken\",\"type\":\"address\"}],\"name\":\"PTokenRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"PendingGovernorshipTransfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newAddress\",\"type\":\"address\"}],\"name\":\"RegistratorChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"_oldAddresses\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"_newAddresses\",\"type\":\"address[]\"}],\"name\":\"RewardTokenAddressesUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"rewardToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"RewardTokenCollected\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64[]\",\"name\":\"operatorIds\",\"type\":\"uint64[]\"}],\"name\":\"SSVValidatorRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64[]\",\"name\":\"operatorIds\",\"type\":\"uint64[]\"}],\"name\":\"SSVValidatorRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newAddress\",\"type\":\"address\"}],\"name\":\"StakingMonitorChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"}],\"name\":\"ValidatorInvalid\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint40\",\"name\":\"validatorIndex\",\"type\":\"uint40\"}],\"name\":\"ValidatorVerified\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amountWei\",\"type\":\"uint256\"}],\"name\":\"ValidatorWithdraw\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_pToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"Withdrawal\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BEACON_CHAIN_DEPOSIT_CONTRACT\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"BEACON_GENESIS_TIMESTAMP\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"BEACON_PROOFS\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SSV_NETWORK\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SSV_TOKEN\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"VAULT_ADDRESS\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"WETH\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"assetToPToken\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"}],\"name\":\"checkBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"collectRewardTokens\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"deposit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"depositAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"depositList\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"depositListLength\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"depositedWethAccountedFor\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"deposits\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"amountGwei\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"slot\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"depositIndex\",\"type\":\"uint32\"},{\"internalType\":\"enum CompoundingValidatorManager.DepositStatus\",\"name\":\"status\",\"type\":\"uint8\"},{\"internalType\":\"uint64\",\"name\":\"withdrawableEpoch\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"firstDeposit\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRewardTokenAddresses\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"governor\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"harvesterAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_rewardTokenAddresses\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"_assets\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"_pTokens\",\"type\":\"address[]\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isGovernor\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastVerifiedEthBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"nextDepositID\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"platformAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"publicKey\",\"type\":\"bytes\"},{\"internalType\":\"uint64[]\",\"name\":\"operatorIds\",\"type\":\"uint64[]\"},{\"internalType\":\"bytes\",\"name\":\"sharesData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"ssvAmount\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"validatorCount\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"networkFeeIndex\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"index\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"internalType\":\"struct Cluster\",\"name\":\"cluster\",\"type\":\"tuple\"}],\"name\":\"registerSsvValidator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"removePToken\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"publicKey\",\"type\":\"bytes\"},{\"internalType\":\"uint64[]\",\"name\":\"operatorIds\",\"type\":\"uint64[]\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"validatorCount\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"networkFeeIndex\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"index\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"internalType\":\"struct Cluster\",\"name\":\"cluster\",\"type\":\"tuple\"}],\"name\":\"removeSsvValidator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"resetFirstDeposit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"rewardTokenAddresses\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"safeApproveAllTokens\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_harvesterAddress\",\"type\":\"address\"}],\"name\":\"setHarvesterAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"setPTokenAddress\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_address\",\"type\":\"address\"}],\"name\":\"setRegistrator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_rewardTokenAddresses\",\"type\":\"address[]\"}],\"name\":\"setRewardTokenAddresses\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"snapBalances\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"snappedBalance\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"blockRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint128\",\"name\":\"ethBalance\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"pubkey\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"},{\"internalType\":\"bytes32\",\"name\":\"depositDataRoot\",\"type\":\"bytes32\"}],\"internalType\":\"struct CompoundingValidatorManager.ValidatorStakeData\",\"name\":\"validatorStakeData\",\"type\":\"tuple\"},{\"internalType\":\"uint64\",\"name\":\"depositAmountGwei\",\"type\":\"uint64\"}],\"name\":\"stakeEth\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"}],\"name\":\"supportsAsset\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newGovernor\",\"type\":\"address\"}],\"name\":\"transferGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"transferToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"validator\",\"outputs\":[{\"internalType\":\"enum CompoundingValidatorManager.ValidatorState\",\"name\":\"state\",\"type\":\"uint8\"},{\"internalType\":\"uint40\",\"name\":\"index\",\"type\":\"uint40\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"validatorRegistrator\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"publicKey\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"amountGwei\",\"type\":\"uint64\"}],\"name\":\"validatorWithdrawal\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"vaultAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"verifiedValidators\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"verifiedValidatorsLength\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"validatorVerificationBlockTimestamp\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"slot\",\"type\":\"uint64\"},{\"internalType\":\"uint40\",\"name\":\"validatorIndex\",\"type\":\"uint40\"},{\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"pendingDepositPubKeyProof\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"withdrawableEpochProof\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"validatorPubKeyProof\",\"type\":\"bytes\"}],\"internalType\":\"struct CompoundingValidatorManager.FirstPendingDepositWithdrawableProofData\",\"name\":\"firstPendingDeposit\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"balancesContainerRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"balancesContainerProof\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"validatorBalanceLeaves\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes[]\",\"name\":\"validatorBalanceProofs\",\"type\":\"bytes[]\"}],\"internalType\":\"struct CompoundingValidatorManager.BalanceProofs\",\"name\":\"balanceProofs\",\"type\":\"tuple\"}],\"name\":\"verifyBalances\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"depositID\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"depositProcessedSlot\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"slot\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"proof\",\"type\":\"bytes\"}],\"internalType\":\"struct CompoundingValidatorManager.FirstPendingDepositSlotProofData\",\"name\":\"firstPendingDeposit\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"withdrawableEpoch\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"withdrawableEpochProof\",\"type\":\"bytes\"}],\"internalType\":\"struct CompoundingValidatorManager.StrategyValidatorProofData\",\"name\":\"strategyValidatorData\",\"type\":\"tuple\"}],\"name\":\"verifyDeposit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"nextBlockTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint40\",\"name\":\"validatorIndex\",\"type\":\"uint40\"},{\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"withdrawalAddress\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"validatorPubKeyProof\",\"type\":\"bytes\"}],\"name\":\"verifyValidator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_recipient\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"withdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64[]\",\"name\":\"operatorIds\",\"type\":\"uint64[]\"},{\"internalType\":\"uint256\",\"name\":\"ssvAmount\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"validatorCount\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"networkFeeIndex\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"index\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"internalType\":\"struct Cluster\",\"name\":\"cluster\",\"type\":\"tuple\"}],\"name\":\"withdrawSSV\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}],\"devdoc\":{\"author\":\"Origin Protocol Inc\",\"kind\":\"dev\",\"methods\":{\"checkBalance(address)\":{\"params\":{\"_asset\":\"Address of WETH asset.\"},\"returns\":{\"balance\":\" Total value in ETH\"}},\"constructor\":{\"params\":{\"_baseConfig\":\"Base strategy config with `platformAddress` not used so empty address `vaultAddress` the address of the OETH Vault contract\",\"_beaconChainDepositContract\":\"Address of the beacon chain deposit contract\",\"_beaconGenesisTimestamp\":\"The timestamp of the Beacon chain's genesis.\",\"_beaconProofs\":\"Address of the Beacon Proofs contract that verifies beacon chain data\",\"_ssvNetwork\":\"Address of the SSV Network contract\",\"_ssvToken\":\"Address of the SSV Token contract\",\"_wethAddress\":\"Address of the WETH Token contract\"}},\"deposit(address,uint256)\":{\"params\":{\"_amount\":\"Amount of WETH that was transferred to the strategy by the vault.\",\"_asset\":\"Address of the WETH token.\"}},\"getRewardTokenAddresses()\":{\"returns\":{\"_0\":\"address[] the reward token addresses.\"}},\"initialize(address[],address[],address[])\":{\"params\":{\"_assets\":\"Not used so empty array\",\"_pTokens\":\"Not used so empty array\",\"_rewardTokenAddresses\":\"Not used so empty array\"}},\"registerSsvValidator(bytes,uint64[],bytes,uint256,(uint32,uint64,uint64,bool,uint256))\":{\"params\":{\"cluster\":\"The SSV cluster details including the validator count and SSV balance\",\"operatorIds\":\"The operator IDs of the SSV Cluster\",\"publicKey\":\"The public key of the validator\",\"sharesData\":\"The shares data for the validator\",\"ssvAmount\":\"The amount of SSV tokens to be deposited to the SSV cluster\"}},\"removeSsvValidator(bytes,uint64[],(uint32,uint64,uint64,bool,uint256))\":{\"params\":{\"cluster\":\"The SSV cluster details including the validator count and SSV balance\",\"operatorIds\":\"The operator IDs of the SSV Cluster\",\"publicKey\":\"The public key of the validator\"}},\"setHarvesterAddress(address)\":{\"params\":{\"_harvesterAddress\":\"Address of the harvester contract.\"}},\"setRewardTokenAddresses(address[])\":{\"params\":{\"_rewardTokenAddresses\":\"Array of reward token addresses\"}},\"stakeEth((bytes,bytes,bytes32),uint64)\":{\"params\":{\"depositAmountGwei\":\"The amount of WETH to stake to the validator in Gwei.\",\"validatorStakeData\":\"validator data needed to stake. The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot. Only the registrator can call this function.\"}},\"supportsAsset(address)\":{\"params\":{\"_asset\":\"The address of the WETH token.\"}},\"transferGovernance(address)\":{\"params\":{\"_newGovernor\":\"Address of the new Governor\"}},\"transferToken(address,uint256)\":{\"params\":{\"_amount\":\"Amount of the asset to transfer\",\"_asset\":\"Address for the asset\"}},\"validatorWithdrawal(bytes,uint64)\":{\"params\":{\"amountGwei\":\"The amount of ETH to be withdrawn from the validator in Gwei. A zero amount will trigger a full withdrawal.\",\"publicKey\":\"The public key of the validator\"}},\"verifyBalances(uint64,(uint64,uint40,bytes32,bytes,bytes,bytes),(bytes32,bytes,bytes32[],bytes[]))\":{\"params\":{\"balanceProofs\":\"a `BalanceProofs` struct containing the following: - balancesContainerRoot: The merkle root of the balances container - balancesContainerProof: The merkle proof for the balances container to the beacon block root. This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node. - validatorBalanceLeaves: Array of leaf nodes containing the validator balance with three other balances. - validatorBalanceProofs: Array of merkle proofs for the validator balance to the Balances container root. This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\",\"firstPendingDeposit\":\"a `FirstPendingDepositWithdrawableProofData` struct containing: - slot: The beacon chain slot of the first deposit in the beacon chain's deposit queue. Can be anything if the deposit queue is empty, but zero is a good choice. - validatorIndex: The index of the validator of the first pending deposit. Can be anything if the deposit queue is empty, but zero is a good choice. - pubKeyHash: The hash of the public key of the validator of the first pending deposit. Can be anything if the deposit queue is empty, but 32 zero bytes is a good choice - pendingDepositPubKeyProof: The merkle proof of the first pending deposit to the beacon block root. Can be either: * 40 witness hashes for BeaconBlock.state.PendingDeposits[0].pubKey when the deposit queue is not empty. * 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty. The 32 byte witness hashes are concatenated together starting from the leaf node. - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the first pending deposit's validator to the beacon block root. This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node. - validatorPubKeyProof The merkle proof for the public key of the first pending deposit's validator to the this witness hash of withdrawableEpochProof. This is 2 witness hashes of 32 bytes each concatenated together starting from the leaf node.\",\"validatorVerificationBlockTimestamp\":\"next block's timestamp of a slot that has the first pending deposit already applied to the validator.\"}},\"verifyDeposit(uint256,uint64,(uint64,bytes),(uint64,bytes))\":{\"params\":{\"depositID\":\"The deposit ID emitted in `ETHStaked` from the `stakeEth` function.\",\"depositProcessedSlot\":\"Any slot on or after the strategy's deposit was processed on the beacon chain. Can not be a slot with pending deposits with the same slot as the deposit being verified. Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root set for the next block timestamp in 12 seconds time.\",\"firstPendingDeposit\":\"a `FirstPendingDepositSlotProofData` struct containing: - slot: The beacon chain slot of the first deposit in the beacon chain's deposit queue. Can be anything if the deposit queue is empty, but zero is a good choice. - proof: The merkle proof of the first pending deposit's slot to the beacon block root. Can be either: * 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty. * 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty. The 32 byte witness hashes are concatenated together starting from the leaf node.\",\"strategyValidatorData\":\"a `StrategyValidatorProofData` struct containing: - withdrawableEpoch: The withdrawable epoch of the validator the strategy is depositing to. - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the validator the strategy is depositing to, to the beacon block root. This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\"}},\"verifyValidator(uint64,uint40,bytes32,address,bytes)\":{\"params\":{\"nextBlockTimestamp\":\"The timestamp of the execution layer block after the beacon chain slot we are verifying. The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp, which is the beacon block root of the previous block.\",\"pubKeyHash\":\"The hash of the validator's public key using the Beacon Chain's format\",\"validatorIndex\":\"The index of the validator on the beacon chain.\",\"validatorPubKeyProof\":\"The merkle proof for the validator public key to the beacon block root. This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node. BeaconBlock.state.validators[validatorIndex].pubkey\",\"withdrawalAddress\":\"The withdrawal address of the validator which should be this strategy's address. If the withdrawal address is not this strategy's address, the initial deposit was front-run and the validator is marked as invalid.\"}},\"withdraw(address,address,uint256)\":{\"params\":{\"_amount\":\"Amount of WETH to withdraw.\",\"_asset\":\"Address of the WETH token.\",\"_recipient\":\"Address to receive withdrawn assets.\"}},\"withdrawSSV(uint64[],uint256,(uint32,uint64,uint64,bool,uint256))\":{\"details\":\"A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\",\"params\":{\"cluster\":\"The SSV cluster details including the validator count and SSV balance\",\"operatorIds\":\"The operator IDs of the SSV Cluster\",\"ssvAmount\":\"The amount of SSV tokens to be withdrawn from the SSV cluster\"}}},\"title\":\"Compounding Staking SSV Strategy\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"BEACON_CHAIN_DEPOSIT_CONTRACT()\":{\"notice\":\"The address of the beacon chain deposit contract\"},\"BEACON_GENESIS_TIMESTAMP()\":{\"notice\":\"The timestamp of the Beacon chain genesis.\"},\"BEACON_PROOFS()\":{\"notice\":\"Address of the Beacon Proofs contract that verifies beacon chain data\"},\"SSV_NETWORK()\":{\"notice\":\"The address of the SSV Network contract used to interface with\"},\"SSV_TOKEN()\":{\"notice\":\"SSV ERC20 token that serves as a payment for operating SSV validators\"},\"VAULT_ADDRESS()\":{\"notice\":\"Address of the OETH Vault proxy contract\"},\"WETH()\":{\"notice\":\"The address of the Wrapped ETH (WETH) token contract\"},\"assetToPToken(address)\":{\"notice\":\"asset => pToken (Platform Specific Token Address)\"},\"checkBalance(address)\":{\"notice\":\"Accounts for all the assets managed by this strategy which includes: 1. The current WETH in this strategy contract 2. The last verified ETH balance, total deposits and total validator balances\"},\"claimGovernance()\":{\"notice\":\"Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor.\"},\"collectRewardTokens()\":{\"notice\":\"Collect accumulated reward token and send to Vault.\"},\"deposit(address,uint256)\":{\"notice\":\"Unlike other strategies, this does not deposit assets into the underlying platform. It just checks the asset is WETH and emits the Deposit event. To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\"},\"depositAll()\":{\"notice\":\"Unlike other strategies, this does not deposit assets into the underlying platform. It just emits the Deposit event. To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\"},\"depositList(uint256)\":{\"notice\":\"List of strategy deposit IDs to a validator. The list can be for deposits waiting to be verified as processed on the beacon chain, or deposits that have been verified to an exiting validator and is now waiting for the validator's balance to be swept. The list may not be ordered by time of deposit. Removed deposits will move the last deposit to the removed index.\"},\"depositListLength()\":{\"notice\":\"Returns the number of deposits waiting to be verified as processed on the beacon chain, or deposits that have been verified to an exiting validator and is now waiting for the validator's balance to be swept.\"},\"deposits(uint256)\":{\"notice\":\"Mapping of the deposit ID to the deposit data\"},\"firstDeposit()\":{\"notice\":\"Restricts to only one deposit to an unverified validator at a time. This is to limit front-running attacks of deposits to the beacon chain contract.\"},\"getRewardTokenAddresses()\":{\"notice\":\"Get the reward token addresses.\"},\"governor()\":{\"notice\":\"Returns the address of the current Governor.\"},\"harvesterAddress()\":{\"notice\":\"Address of the Harvester contract allowed to collect reward tokens\"},\"initialize(address[],address[],address[])\":{\"notice\":\"Set up initial internal state including 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\"},\"isGovernor()\":{\"notice\":\"Returns true if the caller is the current Governor.\"},\"lastVerifiedEthBalance()\":{\"notice\":\"The last verified ETH balance of the strategy\"},\"nextDepositID()\":{\"notice\":\"Unique identifier of the next validator deposit.\"},\"platformAddress()\":{\"notice\":\"Address of the underlying platform\"},\"registerSsvValidator(bytes,uint64[],bytes,uint256,(uint32,uint64,uint64,bool,uint256))\":{\"notice\":\"Registers a single validator in a SSV Cluster. Only the Registrator can call this function.\"},\"removePToken(uint256)\":{\"notice\":\"is not supported for this strategy as there is no platform token.\"},\"removeSsvValidator(bytes,uint64[],(uint32,uint64,uint64,bool,uint256))\":{\"notice\":\"Remove the validator from the SSV Cluster after: - the validator has been exited from `validatorWithdrawal` or slashed - the validator has incorrectly registered and can not be staked to - the initial deposit was front-run and the withdrawal address is not this strategy's address. Make sure `validatorWithdrawal` is called with a zero amount and the validator has exited the Beacon chain. If removed before the validator has exited the beacon chain will result in the validator being slashed. Only the registrator can call this function.\"},\"resetFirstDeposit()\":{\"notice\":\"Reset the `firstDeposit` flag to false so deposits to unverified validators can be made again.\"},\"rewardTokenAddresses(uint256)\":{\"notice\":\"Address of the reward tokens. eg CRV, BAL, CVX, AURA\"},\"safeApproveAllTokens()\":{\"notice\":\"Approves the SSV Network contract to transfer SSV tokens for validator registration.\"},\"setHarvesterAddress(address)\":{\"notice\":\"Set the Harvester contract that can collect rewards.\"},\"setPTokenAddress(address,address)\":{\"notice\":\"is not supported for this strategy as there is no platform token.\"},\"setRegistrator(address)\":{\"notice\":\"Set the address of the registrator which can register, exit and remove validators\"},\"setRewardTokenAddresses(address[])\":{\"notice\":\"Set the reward token addresses. Any old addresses will be overwritten.\"},\"snapBalances()\":{\"notice\":\"Stores the current ETH balance at the current block and beacon block root of the slot that is associated with the previous block. When snapping / verifying balance it is of a high importance that there is no miss-match in respect to ETH that is held by the contract and balances that are verified on the validators. First some context on the beacon-chain block building behaviour. Relevant parts of constructing a block on the beacon chain consist of: - process_withdrawals: ETH is deducted from the validator's balance - process_execution_payload: immediately after the previous step executing all the transactions - apply the withdrawals: adding ETH to the recipient which is the withdrawal address contained in the withdrawal credentials of the exited validators That means that balance increases which are part of the post-block execution state are done within the block, but the transaction that are contained within that block can not see / interact with the balance from the exited validators. Only transactions in the next block can do that. When snap balances is performed the state of the chain is snapped across 2 separate chain states: - ETH balance of the contract is recorded on block X -> and corresponding slot Y - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1 given there were no missed slots. It could also be Y - 2, Y - 3 depending on how many slots have not managed to propose a block. For the sake of simplicity this slot will be referred to as Y - 1 as it makes no difference in the argument Given these 2 separate chain states it is paramount that verify balances can not experience miss-counting ETH or much more dangerous double counting of the ETH. When verifyBalances is called it is performed on the current block Z where Z > X. Verify balances adds up all the ETH (omitting WETH) controlled by this contract: - ETH balance in the contract on block X - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1 - ETH balance in validators that are active in slot Y - 1 - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner) and have their balance visible to transactions in slot Y and corresponding block X (or sooner) Lets verify the correctness of ETH accounting given the above described behaviour. *ETH balance in the contract on block X* This is an ETH balance of the contract on a non current X block. Any ETH leaving the contract as a result of a withdrawal subtracts from the ETH accounted for on block X if `verifyBalances` has already been called. It also invalidates a `snapBalances` in case `verifyBalances` has not been called yet. Not performing this would result in not accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z]. Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH accounted for since the last `verifyBalances` has been called. And it invalidates the `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this would result in double counting the `stakedEth` since it would be present once in the snapped contract balance and the second time in deposit storage variables. This behaviour is correct. *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1* The contract sums up all the ETH that has been deposited to the Beacon chain deposit contract at block Z. The execution layer doesn't have direct access to the state of deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be deposited it needs to be sure to not double count ETH that is in deposits (storage vars) and could also be part of the validator balances. It does that by verifying that at slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since the last snap till now all are still in queue. Which ensures they can not be part of the validator balances in later steps. This behaviour is correct. *ETH balance in validators that are active in slot Y - 1* The contract is verifying none of the deposits on Y - 1 slot have been processed and for that reason it checks the validator balances in the same slot. Ensuring accounting correctness. This behaviour is correct. *The withdrawn validators* The withdrawn validators could have their balances deducted in any slot before slot Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets look at the \\\"worst case scenario\\\" where the validator withdrawal is processed in the slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot Y -> block X. The ETH balance on the contract is snapped at block X meaning that even if the validator exits at the latest possible time it is paramount that the ETH balance on the execution layer is recorded in the next block. Correctly accounting for the withdrawn ETH. Worth mentioning if the validator exit is processed by the slot Y and balance increase seen on the execution layer on block X + 1 the withdrawal is ignored by both the validator balance verification as well as execution layer contract balance snap. This behaviour is correct. The validator balances on the beacon chain can then be proved with `verifyBalances`.\"},\"snappedBalance()\":{\"notice\":\"Mapping of the block root to the balances at that slot\"},\"stakeEth((bytes,bytes,bytes32),uint64)\":{\"notice\":\"Stakes WETH in this strategy to a compounding validator. Does not convert any ETH sitting in this strategy to WETH.\"},\"supportsAsset(address)\":{\"notice\":\"Returns bool indicating whether asset is supported by the strategy.\"},\"transferGovernance(address)\":{\"notice\":\"Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete\"},\"transferToken(address,uint256)\":{\"notice\":\"Transfer token to governor. Intended for recovering tokens stuck in strategy contracts, i.e. mistaken sends.\"},\"validator(bytes32)\":{\"notice\":\"Mapping of the hash of the validator's public key to the validator state and index. Uses the Beacon chain hashing for BLSPubkey which is sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\"},\"validatorRegistrator()\":{\"notice\":\"Address of the registrator - allowed to register, withdraw, exit and remove validators\"},\"validatorWithdrawal(bytes,uint64)\":{\"notice\":\"Request a full or partial withdrawal from a validator. A zero amount will trigger a full withdrawal. If the remaining balance is < 32 ETH then only the amount in excess of 32 ETH will be withdrawn. Only the Registrator can call this function. 1 wei of value should be sent with the tx to pay for the withdrawal request fee. If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any. If no ETH balance, the tx will revert.\"},\"vaultAddress()\":{\"notice\":\"Address of the OToken vault\"},\"verifiedValidators(uint256)\":{\"notice\":\"List of validator public key hashes that have been verified to exist on the beacon chain. These have had a deposit processed and the validator's balance increased. Validators will be removed from this list when its verified they have a zero balance.\"},\"verifiedValidatorsLength()\":{\"notice\":\"Returns the number of verified validators.\"},\"verifyBalances(uint64,(uint64,uint40,bytes32,bytes,bytes,bytes),(bytes32,bytes,bytes32[],bytes[]))\":{\"notice\":\"Verifies the balances of all active validators on the beacon chain and checks no pending deposits have been processed by the beacon chain.\"},\"verifyDeposit(uint256,uint64,(uint64,bytes),(uint64,bytes))\":{\"notice\":\"Verifies a deposit on the execution layer has been processed by the beacon chain. This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance. Important: this function has a limitation where `depositProcessedSlot` and `firstDepositValidatorCreatedSlot` that is passed by the off-chain verifier requires a slot immediately after them to propose a block otherwise the `BeaconRoots.parentBlockRoot` will fail. This shouldn't be a problem, since by the current behaviour of beacon chain only 1%-3% slots don't propose a block.\"},\"verifyValidator(uint64,uint40,bytes32,address,bytes)\":{\"notice\":\"Verifies a validator's index to its public key. Adds to the list of verified validators if the validator's withdrawal address is this strategy's address. Marks the validator as invalid and removes the deposit if the withdrawal address is not this strategy's address.\"},\"withdraw(address,address,uint256)\":{\"notice\":\"Withdraw ETH and WETH from this strategy contract.\"},\"withdrawAll()\":{\"notice\":\"Transfer all WETH deposits, ETH from validator withdrawals and ETH from execution rewards in this strategy to the vault. This does not withdraw from the validators. That has to be done separately with the `validatorWithdrawal` operation.\"},\"withdrawSSV(uint64[],uint256,(uint32,uint64,uint64,bool,uint256))\":{\"notice\":\"Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\"}},\"notice\":\"Strategy to deploy funds into DVT validators powered by the SSV Network\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol\":\"CompoundingStakingSSVStrategy\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/token/ERC20/IERC20.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Interface of the ERC20 standard as defined in the EIP.\\n */\\ninterface IERC20 {\\n /**\\n * @dev Returns the amount of tokens in existence.\\n */\\n function totalSupply() external view returns (uint256);\\n\\n /**\\n * @dev Returns the amount of tokens owned by `account`.\\n */\\n function balanceOf(address account) external view returns (uint256);\\n\\n /**\\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transfer(address recipient, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Returns the remaining number of tokens that `spender` will be\\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\\n * zero by default.\\n *\\n * This value changes when {approve} or {transferFrom} are called.\\n */\\n function allowance(address owner, address spender) external view returns (uint256);\\n\\n /**\\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\\n * that someone may use both the old and the new allowance by unfortunate\\n * transaction ordering. One possible solution to mitigate this race\\n * condition is to first reduce the spender's allowance to 0 and set the\\n * desired value afterwards:\\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\\n *\\n * Emits an {Approval} event.\\n */\\n function approve(address spender, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\\n * allowance mechanism. `amount` is then deducted from the caller's\\n * allowance.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transferFrom(\\n address sender,\\n address recipient,\\n uint256 amount\\n ) external returns (bool);\\n\\n /**\\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\\n * another (`to`).\\n *\\n * Note that `value` may be zero.\\n */\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n\\n /**\\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n * a call to {approve}. `value` is the new allowance.\\n */\\n event Approval(address indexed owner, address indexed spender, uint256 value);\\n}\\n\",\"keccak256\":\"0x61437cb513a887a1bbad006e7b1c8b414478427d33de47c5600af3c748f108da\",\"license\":\"MIT\"},\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../IERC20.sol\\\";\\nimport \\\"../../../utils/Address.sol\\\";\\n\\n/**\\n * @title SafeERC20\\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\\n * contract returns false). Tokens that return no value (and instead revert or\\n * throw on failure) are also supported, non-reverting calls are assumed to be\\n * successful.\\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\\n */\\nlibrary SafeERC20 {\\n using Address for address;\\n\\n function safeTransfer(\\n IERC20 token,\\n address to,\\n uint256 value\\n ) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\\n }\\n\\n function safeTransferFrom(\\n IERC20 token,\\n address from,\\n address to,\\n uint256 value\\n ) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\\n }\\n\\n /**\\n * @dev Deprecated. This function has issues similar to the ones found in\\n * {IERC20-approve}, and its usage is discouraged.\\n *\\n * Whenever possible, use {safeIncreaseAllowance} and\\n * {safeDecreaseAllowance} instead.\\n */\\n function safeApprove(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n // safeApprove should only be called when setting an initial allowance,\\n // or when resetting it to zero. To increase and decrease it, use\\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\\n require(\\n (value == 0) || (token.allowance(address(this), spender) == 0),\\n \\\"SafeERC20: approve from non-zero to non-zero allowance\\\"\\n );\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\\n }\\n\\n function safeIncreaseAllowance(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n uint256 newAllowance = token.allowance(address(this), spender) + value;\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\\n }\\n\\n function safeDecreaseAllowance(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n unchecked {\\n uint256 oldAllowance = token.allowance(address(this), spender);\\n require(oldAllowance >= value, \\\"SafeERC20: decreased allowance below zero\\\");\\n uint256 newAllowance = oldAllowance - value;\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\\n }\\n }\\n\\n /**\\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\\n * on the return value: the return value is optional (but if data is returned, it must not be false).\\n * @param token The token targeted by the call.\\n * @param data The call data (encoded using abi.encode or one of its variants).\\n */\\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\\n // the target address contains contract code and also asserts for success in the low-level call.\\n\\n bytes memory returndata = address(token).functionCall(data, \\\"SafeERC20: low-level call failed\\\");\\n if (returndata.length > 0) {\\n // Return data is optional\\n require(abi.decode(returndata, (bool)), \\\"SafeERC20: ERC20 operation did not succeed\\\");\\n }\\n }\\n}\\n\",\"keccak256\":\"0xc3d946432c0ddbb1f846a0d3985be71299df331b91d06732152117f62f0be2b5\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize, which returns 0 for contracts in\\n // construction, since the code is only stored at the end of the\\n // constructor execution.\\n\\n uint256 size;\\n assembly {\\n size := extcodesize(account)\\n }\\n return size > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCall(target, data, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n require(isContract(target), \\\"Address: static call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(isContract(target), \\\"Address: delegate call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x51b758a8815ecc9596c66c37d56b1d33883a444631a3f916b9fe65cb863ef7c4\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/math/Math.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Standard math utilities missing in the Solidity language.\\n */\\nlibrary Math {\\n /**\\n * @dev Returns the largest of two numbers.\\n */\\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a >= b ? a : b;\\n }\\n\\n /**\\n * @dev Returns the smallest of two numbers.\\n */\\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a < b ? a : b;\\n }\\n\\n /**\\n * @dev Returns the average of two numbers. The result is rounded towards\\n * zero.\\n */\\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\\n // (a + b) / 2 can overflow.\\n return (a & b) + (a ^ b) / 2;\\n }\\n\\n /**\\n * @dev Returns the ceiling of the division of two numbers.\\n *\\n * This differs from standard division with `/` in that it rounds up instead\\n * of rounding down.\\n */\\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\\n // (a + b - 1) / b can overflow on addition, so we distribute.\\n return a / b + (a % b == 0 ? 0 : 1);\\n }\\n}\\n\",\"keccak256\":\"0xfaad496c1c944b6259b7dc70b4865eb1775d6402bc0c81b38a0b24d9f525ae37\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/math/SafeCast.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\\n * checks.\\n *\\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\\n * easily result in undesired exploitation or bugs, since developers usually\\n * assume that overflows raise errors. `SafeCast` restores this intuition by\\n * reverting the transaction when such an operation overflows.\\n *\\n * Using this library instead of the unchecked operations eliminates an entire\\n * class of bugs, so it's recommended to use it always.\\n *\\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\\n * all math on `uint256` and `int256` and then downcasting.\\n */\\nlibrary SafeCast {\\n /**\\n * @dev Returns the downcasted uint224 from uint256, reverting on\\n * overflow (when the input is greater than largest uint224).\\n *\\n * Counterpart to Solidity's `uint224` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 224 bits\\n */\\n function toUint224(uint256 value) internal pure returns (uint224) {\\n require(value <= type(uint224).max, \\\"SafeCast: value doesn't fit in 224 bits\\\");\\n return uint224(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint128 from uint256, reverting on\\n * overflow (when the input is greater than largest uint128).\\n *\\n * Counterpart to Solidity's `uint128` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 128 bits\\n */\\n function toUint128(uint256 value) internal pure returns (uint128) {\\n require(value <= type(uint128).max, \\\"SafeCast: value doesn't fit in 128 bits\\\");\\n return uint128(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint96 from uint256, reverting on\\n * overflow (when the input is greater than largest uint96).\\n *\\n * Counterpart to Solidity's `uint96` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 96 bits\\n */\\n function toUint96(uint256 value) internal pure returns (uint96) {\\n require(value <= type(uint96).max, \\\"SafeCast: value doesn't fit in 96 bits\\\");\\n return uint96(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint64 from uint256, reverting on\\n * overflow (when the input is greater than largest uint64).\\n *\\n * Counterpart to Solidity's `uint64` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 64 bits\\n */\\n function toUint64(uint256 value) internal pure returns (uint64) {\\n require(value <= type(uint64).max, \\\"SafeCast: value doesn't fit in 64 bits\\\");\\n return uint64(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint32 from uint256, reverting on\\n * overflow (when the input is greater than largest uint32).\\n *\\n * Counterpart to Solidity's `uint32` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 32 bits\\n */\\n function toUint32(uint256 value) internal pure returns (uint32) {\\n require(value <= type(uint32).max, \\\"SafeCast: value doesn't fit in 32 bits\\\");\\n return uint32(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint16 from uint256, reverting on\\n * overflow (when the input is greater than largest uint16).\\n *\\n * Counterpart to Solidity's `uint16` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 16 bits\\n */\\n function toUint16(uint256 value) internal pure returns (uint16) {\\n require(value <= type(uint16).max, \\\"SafeCast: value doesn't fit in 16 bits\\\");\\n return uint16(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint8 from uint256, reverting on\\n * overflow (when the input is greater than largest uint8).\\n *\\n * Counterpart to Solidity's `uint8` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 8 bits.\\n */\\n function toUint8(uint256 value) internal pure returns (uint8) {\\n require(value <= type(uint8).max, \\\"SafeCast: value doesn't fit in 8 bits\\\");\\n return uint8(value);\\n }\\n\\n /**\\n * @dev Converts a signed int256 into an unsigned uint256.\\n *\\n * Requirements:\\n *\\n * - input must be greater than or equal to 0.\\n */\\n function toUint256(int256 value) internal pure returns (uint256) {\\n require(value >= 0, \\\"SafeCast: value must be positive\\\");\\n return uint256(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int128 from int256, reverting on\\n * overflow (when the input is less than smallest int128 or\\n * greater than largest int128).\\n *\\n * Counterpart to Solidity's `int128` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 128 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt128(int256 value) internal pure returns (int128) {\\n require(value >= type(int128).min && value <= type(int128).max, \\\"SafeCast: value doesn't fit in 128 bits\\\");\\n return int128(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int64 from int256, reverting on\\n * overflow (when the input is less than smallest int64 or\\n * greater than largest int64).\\n *\\n * Counterpart to Solidity's `int64` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 64 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt64(int256 value) internal pure returns (int64) {\\n require(value >= type(int64).min && value <= type(int64).max, \\\"SafeCast: value doesn't fit in 64 bits\\\");\\n return int64(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int32 from int256, reverting on\\n * overflow (when the input is less than smallest int32 or\\n * greater than largest int32).\\n *\\n * Counterpart to Solidity's `int32` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 32 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt32(int256 value) internal pure returns (int32) {\\n require(value >= type(int32).min && value <= type(int32).max, \\\"SafeCast: value doesn't fit in 32 bits\\\");\\n return int32(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int16 from int256, reverting on\\n * overflow (when the input is less than smallest int16 or\\n * greater than largest int16).\\n *\\n * Counterpart to Solidity's `int16` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 16 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt16(int256 value) internal pure returns (int16) {\\n require(value >= type(int16).min && value <= type(int16).max, \\\"SafeCast: value doesn't fit in 16 bits\\\");\\n return int16(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int8 from int256, reverting on\\n * overflow (when the input is less than smallest int8 or\\n * greater than largest int8).\\n *\\n * Counterpart to Solidity's `int8` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 8 bits.\\n *\\n * _Available since v3.1._\\n */\\n function toInt8(int256 value) internal pure returns (int8) {\\n require(value >= type(int8).min && value <= type(int8).max, \\\"SafeCast: value doesn't fit in 8 bits\\\");\\n return int8(value);\\n }\\n\\n /**\\n * @dev Converts an unsigned uint256 into a signed int256.\\n *\\n * Requirements:\\n *\\n * - input must be less than or equal to maxInt256.\\n */\\n function toInt256(uint256 value) internal pure returns (int256) {\\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\\n require(value <= uint256(type(int256).max), \\\"SafeCast: value doesn't fit in an int256\\\");\\n return int256(value);\\n }\\n}\\n\",\"keccak256\":\"0x5c6caab697d302ad7eb59c234a4d2dbc965c1bae87709bd2850060b7695b28c7\",\"license\":\"MIT\"},\"contracts/beacon/BeaconRoots.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Library to retrieve beacon block roots.\\n * @author Origin Protocol Inc\\n */\\nlibrary BeaconRoots {\\n /// @notice The address of beacon block roots oracle\\n /// See https://eips.ethereum.org/EIPS/eip-4788\\n address internal constant BEACON_ROOTS_ADDRESS =\\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\\n\\n /// @notice Returns the beacon block root for the previous block.\\n /// This comes from the Beacon Roots contract defined in EIP-4788.\\n /// This will revert if the block is more than 8,191 blocks old as\\n /// that is the size of the beacon root's ring buffer.\\n /// @param timestamp The timestamp of the block for which to get the parent root.\\n /// @return parentRoot The parent block root for the given timestamp.\\n function parentBlockRoot(uint64 timestamp)\\n internal\\n view\\n returns (bytes32 parentRoot)\\n {\\n // Call the Beacon Roots contract to get the parent block root.\\n // This does not have a function signature, so we use a staticcall.\\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\\n abi.encode(timestamp)\\n );\\n\\n require(success && result.length > 0, \\\"Invalid beacon timestamp\\\");\\n parentRoot = abi.decode(result, (bytes32));\\n }\\n}\\n\",\"keccak256\":\"0x4005989f852a68bbcdc1cdc3472ebd3911395e75b4e6366ffcaae4d1c128691e\",\"license\":\"BUSL-1.1\"},\"contracts/beacon/PartialWithdrawal.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Library to request full or partial withdrawals from validators on the beacon chain.\\n * @author Origin Protocol Inc\\n */\\nlibrary PartialWithdrawal {\\n /// @notice The address where the withdrawal request is sent to\\n /// See https://eips.ethereum.org/EIPS/eip-7002\\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\\n\\n /// @notice Requests a partial withdrawal for a given validator public key and amount.\\n /// @param validatorPubKey The public key of the validator to withdraw from\\n /// @param amount The amount of ETH to withdraw\\n function request(bytes calldata validatorPubKey, uint64 amount)\\n internal\\n returns (uint256 fee_)\\n {\\n require(validatorPubKey.length == 48, \\\"Invalid validator byte length\\\");\\n fee_ = fee();\\n\\n // Call the Withdrawal Request contract with the validator public key\\n // and amount to be withdrawn packed together\\n\\n // This is a general purpose EL to CL request:\\n // https://eips.ethereum.org/EIPS/eip-7685\\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\\n abi.encodePacked(validatorPubKey, amount)\\n );\\n\\n require(success, \\\"Withdrawal request failed\\\");\\n }\\n\\n /// @notice Gets fee for withdrawal requests contract on Beacon chain\\n function fee() internal view returns (uint256) {\\n // Get fee from the withdrawal request contract\\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\\n .staticcall(\\\"\\\");\\n\\n require(success && result.length > 0, \\\"Failed to get fee\\\");\\n return abi.decode(result, (uint256));\\n }\\n}\\n\",\"keccak256\":\"0x80d29153ff7eb5c6841692aca98eb0cc14ac43ad2d8e402890b6c6b6e4a9719d\",\"license\":\"BUSL-1.1\"},\"contracts/governance/Governable.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\\n * from owner to governor and renounce methods removed. Does not use\\n * Context.sol like Ownable.sol does for simplification.\\n * @author Origin Protocol Inc\\n */\\nabstract contract Governable {\\n // Storage position of the owner and pendingOwner of the contract\\n // keccak256(\\\"OUSD.governor\\\");\\n bytes32 private constant governorPosition =\\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\\n\\n // keccak256(\\\"OUSD.pending.governor\\\");\\n bytes32 private constant pendingGovernorPosition =\\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\\n\\n // keccak256(\\\"OUSD.reentry.status\\\");\\n bytes32 private constant reentryStatusPosition =\\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\\n\\n // See OpenZeppelin ReentrancyGuard implementation\\n uint256 constant _NOT_ENTERED = 1;\\n uint256 constant _ENTERED = 2;\\n\\n event PendingGovernorshipTransfer(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n event GovernorshipTransferred(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n /**\\n * @notice Returns the address of the current Governor.\\n */\\n function governor() public view returns (address) {\\n return _governor();\\n }\\n\\n /**\\n * @dev Returns the address of the current Governor.\\n */\\n function _governor() internal view returns (address governorOut) {\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n governorOut := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Returns the address of the pending Governor.\\n */\\n function _pendingGovernor()\\n internal\\n view\\n returns (address pendingGovernor)\\n {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n pendingGovernor := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the Governor.\\n */\\n modifier onlyGovernor() {\\n require(isGovernor(), \\\"Caller is not the Governor\\\");\\n _;\\n }\\n\\n /**\\n * @notice Returns true if the caller is the current Governor.\\n */\\n function isGovernor() public view returns (bool) {\\n return msg.sender == _governor();\\n }\\n\\n function _setGovernor(address newGovernor) internal {\\n emit GovernorshipTransferred(_governor(), newGovernor);\\n\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @dev Prevents a contract from calling itself, directly or indirectly.\\n * Calling a `nonReentrant` function from another `nonReentrant`\\n * function is not supported. It is possible to prevent this from happening\\n * by making the `nonReentrant` function external, and make it call a\\n * `private` function that does the actual work.\\n */\\n modifier nonReentrant() {\\n bytes32 position = reentryStatusPosition;\\n uint256 _reentry_status;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n _reentry_status := sload(position)\\n }\\n\\n // On the first call to nonReentrant, _notEntered will be true\\n require(_reentry_status != _ENTERED, \\\"Reentrant call\\\");\\n\\n // Any calls to nonReentrant after this point will fail\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _ENTERED)\\n }\\n\\n _;\\n\\n // By storing the original value once again, a refund is triggered (see\\n // https://eips.ethereum.org/EIPS/eip-2200)\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _NOT_ENTERED)\\n }\\n }\\n\\n function _setPendingGovernor(address newGovernor) internal {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the current Governor. Must be claimed for this to complete\\n * @param _newGovernor Address of the new Governor\\n */\\n function transferGovernance(address _newGovernor) external onlyGovernor {\\n _setPendingGovernor(_newGovernor);\\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\\n }\\n\\n /**\\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the new Governor.\\n */\\n function claimGovernance() external {\\n require(\\n msg.sender == _pendingGovernor(),\\n \\\"Only the pending Governor can complete the claim\\\"\\n );\\n _changeGovernor(msg.sender);\\n }\\n\\n /**\\n * @dev Change Governance of the contract to a new account (`newGovernor`).\\n * @param _newGovernor Address of the new Governor\\n */\\n function _changeGovernor(address _newGovernor) internal {\\n require(_newGovernor != address(0), \\\"New Governor is address(0)\\\");\\n _setGovernor(_newGovernor);\\n }\\n}\\n\",\"keccak256\":\"0xf32f873c8bfbacf2e5f01d0cf37bc7f54fbd5aa656e95c8a599114229946f107\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IBasicToken.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\ninterface IBasicToken {\\n function symbol() external view returns (string memory);\\n\\n function decimals() external view returns (uint8);\\n}\\n\",\"keccak256\":\"0xa562062698aa12572123b36dfd2072f1a39e44fed2031cc19c2c9fd522f96ec2\",\"license\":\"MIT\"},\"contracts/interfaces/IBeaconProofs.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\ninterface IBeaconProofs {\\n function verifyValidator(\\n bytes32 beaconBlockRoot,\\n bytes32 pubKeyHash,\\n bytes calldata validatorPubKeyProof,\\n uint40 validatorIndex,\\n address withdrawalAddress\\n ) external view;\\n\\n function verifyValidatorWithdrawable(\\n bytes32 beaconBlockRoot,\\n uint40 validatorIndex,\\n bytes32 pubKeyHash,\\n uint64 withdrawableEpoch,\\n bytes calldata withdrawableEpochProof,\\n bytes calldata validatorPubKeyProof\\n ) external view;\\n\\n function verifyValidatorWithdrawable(\\n bytes32 beaconBlockRoot,\\n uint40 validatorIndex,\\n uint64 withdrawableEpoch,\\n bytes calldata withdrawableEpochProof\\n ) external view;\\n\\n function verifyBalancesContainer(\\n bytes32 beaconBlockRoot,\\n bytes32 balancesContainerLeaf,\\n bytes calldata balancesContainerProof\\n ) external view;\\n\\n function verifyValidatorBalance(\\n bytes32 balancesContainerRoot,\\n bytes32 validatorBalanceLeaf,\\n bytes calldata balanceProof,\\n uint40 validatorIndex\\n ) external view returns (uint256 validatorBalance);\\n\\n function verifyFirstPendingDeposit(\\n bytes32 beaconBlockRoot,\\n uint64 slot,\\n bytes32 pubKeyHash,\\n bytes calldata firstPendingDepositPubKeyProof\\n ) external view returns (bool isEmptyDepositQueue);\\n\\n function verifyFirstPendingDeposit(\\n bytes32 beaconBlockRoot,\\n uint64 slot,\\n bytes calldata firstPendingDepositSlotProof\\n ) external view returns (bool isEmptyDepositQueue);\\n}\\n\",\"keccak256\":\"0x78359f9f351d1b685d41a63862f8f2f99e7ae3f1665fc0f49492d8193fc4b6fa\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IDepositContract.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\ninterface IDepositContract {\\n /// @notice A processed deposit event.\\n event DepositEvent(\\n bytes pubkey,\\n bytes withdrawal_credentials,\\n bytes amount,\\n bytes signature,\\n bytes index\\n );\\n\\n /// @notice Submit a Phase 0 DepositData object.\\n /// @param pubkey A BLS12-381 public key.\\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\\n /// @param signature A BLS12-381 signature.\\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\\n /// Used as a protection against malformed input.\\n function deposit(\\n bytes calldata pubkey,\\n bytes calldata withdrawal_credentials,\\n bytes calldata signature,\\n bytes32 deposit_data_root\\n ) external payable;\\n\\n /// @notice Query the current deposit root hash.\\n /// @return The deposit root hash.\\n function get_deposit_root() external view returns (bytes32);\\n\\n /// @notice Query the current deposit count.\\n /// @return The deposit count encoded as a little endian 64-bit number.\\n function get_deposit_count() external view returns (bytes memory);\\n}\\n\",\"keccak256\":\"0x598f90bdbc854250bbd5991426bfb43367207e64e33109c41aa8b54323fd8d8e\",\"license\":\"MIT\"},\"contracts/interfaces/ISSVNetwork.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\nstruct Cluster {\\n uint32 validatorCount;\\n uint64 networkFeeIndex;\\n uint64 index;\\n bool active;\\n uint256 balance;\\n}\\n\\ninterface ISSVNetwork {\\n /**********/\\n /* Errors */\\n /**********/\\n\\n error CallerNotOwner(); // 0x5cd83192\\n error CallerNotWhitelisted(); // 0x8c6e5d71\\n error FeeTooLow(); // 0x732f9413\\n error FeeExceedsIncreaseLimit(); // 0x958065d9\\n error NoFeeDeclared(); // 0x1d226c30\\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\\n error OperatorDoesNotExist(); // 0x961e3e8c\\n error InsufficientBalance(); // 0xf4d678b8\\n error ValidatorDoesNotExist(); // 0xe51315d2\\n error ClusterNotLiquidatable(); // 0x60300a8d\\n error InvalidPublicKeyLength(); // 0x637297a4\\n error InvalidOperatorIdsLength(); // 0x38186224\\n error ClusterAlreadyEnabled(); // 0x3babafd2\\n error ClusterIsLiquidated(); // 0x95a0cf33\\n error ClusterDoesNotExists(); // 0x185e2b16\\n error IncorrectClusterState(); // 0x12e04c87\\n error UnsortedOperatorsList(); // 0xdd020e25\\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\\n error ExceedValidatorLimit(); // 0x6df5ab76\\n error TokenTransferFailed(); // 0x045c4b02\\n error SameFeeChangeNotAllowed(); // 0xc81272f8\\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\\n error NotAuthorized(); // 0xea8e4eb5\\n error OperatorsListNotUnique(); // 0xa5a1ff5d\\n error OperatorAlreadyExists(); // 0x289c9494\\n error TargetModuleDoesNotExist(); // 0x8f9195fb\\n error MaxValueExceeded(); // 0x91aa3017\\n error FeeTooHigh(); // 0xcd4e6167\\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\\n error EmptyPublicKeysList(); // df83e679\\n\\n // legacy errors\\n error ValidatorAlreadyExists(); // 0x8d09a73e\\n error IncorrectValidatorState(); // 0x2feda3c1\\n\\n event AdminChanged(address previousAdmin, address newAdmin);\\n event BeaconUpgraded(address indexed beacon);\\n event ClusterDeposited(\\n address indexed owner,\\n uint64[] operatorIds,\\n uint256 value,\\n Cluster cluster\\n );\\n event ClusterLiquidated(\\n address indexed owner,\\n uint64[] operatorIds,\\n Cluster cluster\\n );\\n event ClusterReactivated(\\n address indexed owner,\\n uint64[] operatorIds,\\n Cluster cluster\\n );\\n event ClusterWithdrawn(\\n address indexed owner,\\n uint64[] operatorIds,\\n uint256 value,\\n Cluster cluster\\n );\\n event DeclareOperatorFeePeriodUpdated(uint64 value);\\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\\n event FeeRecipientAddressUpdated(\\n address indexed owner,\\n address recipientAddress\\n );\\n event Initialized(uint8 version);\\n event LiquidationThresholdPeriodUpdated(uint64 value);\\n event MinimumLiquidationCollateralUpdated(uint256 value);\\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\\n event OperatorAdded(\\n uint64 indexed operatorId,\\n address indexed owner,\\n bytes publicKey,\\n uint256 fee\\n );\\n event OperatorFeeDeclarationCancelled(\\n address indexed owner,\\n uint64 indexed operatorId\\n );\\n event OperatorFeeDeclared(\\n address indexed owner,\\n uint64 indexed operatorId,\\n uint256 blockNumber,\\n uint256 fee\\n );\\n event OperatorFeeExecuted(\\n address indexed owner,\\n uint64 indexed operatorId,\\n uint256 blockNumber,\\n uint256 fee\\n );\\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\\n event OperatorMaximumFeeUpdated(uint64 maxFee);\\n event OperatorRemoved(uint64 indexed operatorId);\\n event OperatorWhitelistUpdated(\\n uint64 indexed operatorId,\\n address whitelisted\\n );\\n event OperatorWithdrawn(\\n address indexed owner,\\n uint64 indexed operatorId,\\n uint256 value\\n );\\n event OwnershipTransferStarted(\\n address indexed previousOwner,\\n address indexed newOwner\\n );\\n event OwnershipTransferred(\\n address indexed previousOwner,\\n address indexed newOwner\\n );\\n event Upgraded(address indexed implementation);\\n event ValidatorAdded(\\n address indexed owner,\\n uint64[] operatorIds,\\n bytes publicKey,\\n bytes shares,\\n Cluster cluster\\n );\\n event ValidatorExited(\\n address indexed owner,\\n uint64[] operatorIds,\\n bytes publicKey\\n );\\n event ValidatorRemoved(\\n address indexed owner,\\n uint64[] operatorIds,\\n bytes publicKey,\\n Cluster cluster\\n );\\n\\n fallback() external;\\n\\n function acceptOwnership() external;\\n\\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\\n\\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\\n\\n function deposit(\\n address clusterOwner,\\n uint64[] memory operatorIds,\\n uint256 amount,\\n Cluster memory cluster\\n ) external;\\n\\n function executeOperatorFee(uint64 operatorId) external;\\n\\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\\n external;\\n\\n function bulkExitValidator(\\n bytes[] calldata publicKeys,\\n uint64[] calldata operatorIds\\n ) external;\\n\\n function getVersion() external pure returns (string memory version);\\n\\n function initialize(\\n address token_,\\n address ssvOperators_,\\n address ssvClusters_,\\n address ssvDAO_,\\n address ssvViews_,\\n uint64 minimumBlocksBeforeLiquidation_,\\n uint256 minimumLiquidationCollateral_,\\n uint32 validatorsPerOperatorLimit_,\\n uint64 declareOperatorFeePeriod_,\\n uint64 executeOperatorFeePeriod_,\\n uint64 operatorMaxFeeIncrease_\\n ) external;\\n\\n function liquidate(\\n address clusterOwner,\\n uint64[] memory operatorIds,\\n Cluster memory cluster\\n ) external;\\n\\n function owner() external view returns (address);\\n\\n function pendingOwner() external view returns (address);\\n\\n function proxiableUUID() external view returns (bytes32);\\n\\n function reactivate(\\n uint64[] memory operatorIds,\\n uint256 amount,\\n Cluster memory cluster\\n ) external;\\n\\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\\n\\n function registerOperator(bytes memory publicKey, uint256 fee)\\n external\\n returns (uint64 id);\\n\\n function registerValidator(\\n bytes memory publicKey,\\n uint64[] memory operatorIds,\\n bytes memory sharesData,\\n uint256 amount,\\n Cluster memory cluster\\n ) external;\\n\\n function bulkRegisterValidator(\\n bytes[] calldata publicKeys,\\n uint64[] calldata operatorIds,\\n bytes[] calldata sharesData,\\n uint256 amount,\\n Cluster memory cluster\\n ) external;\\n\\n function removeOperator(uint64 operatorId) external;\\n\\n function removeValidator(\\n bytes memory publicKey,\\n uint64[] memory operatorIds,\\n Cluster memory cluster\\n ) external;\\n\\n function bulkRemoveValidator(\\n bytes[] calldata publicKeys,\\n uint64[] calldata operatorIds,\\n Cluster memory cluster\\n ) external;\\n\\n function renounceOwnership() external;\\n\\n function setFeeRecipientAddress(address recipientAddress) external;\\n\\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\\n external;\\n\\n function transferOwnership(address newOwner) external;\\n\\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\\n\\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\\n\\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\\n\\n function updateMaximumOperatorFee(uint64 maxFee) external;\\n\\n function updateMinimumLiquidationCollateral(uint256 amount) external;\\n\\n function updateModule(uint8 moduleId, address moduleAddress) external;\\n\\n function updateNetworkFee(uint256 fee) external;\\n\\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\\n\\n function upgradeTo(address newImplementation) external;\\n\\n function upgradeToAndCall(address newImplementation, bytes memory data)\\n external\\n payable;\\n\\n function withdraw(\\n uint64[] memory operatorIds,\\n uint256 amount,\\n Cluster memory cluster\\n ) external;\\n\\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\\n\\n function withdrawNetworkEarnings(uint256 amount) external;\\n\\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\\n external;\\n}\\n\",\"keccak256\":\"0xbd86cb74702aebc5b53c8fc738a2e3ad1b410583460617be84b22ce922af12a7\",\"license\":\"MIT\"},\"contracts/interfaces/IStrategy.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\\n */\\ninterface IStrategy {\\n /**\\n * @dev Deposit the given asset to platform\\n * @param _asset asset address\\n * @param _amount Amount to deposit\\n */\\n function deposit(address _asset, uint256 _amount) external;\\n\\n /**\\n * @dev Deposit the entire balance of all supported assets in the Strategy\\n * to the platform\\n */\\n function depositAll() external;\\n\\n /**\\n * @dev Withdraw given asset from Lending platform\\n */\\n function withdraw(\\n address _recipient,\\n address _asset,\\n uint256 _amount\\n ) external;\\n\\n /**\\n * @dev Liquidate all assets in strategy and return them to Vault.\\n */\\n function withdrawAll() external;\\n\\n /**\\n * @dev Returns the current balance of the given asset.\\n */\\n function checkBalance(address _asset)\\n external\\n view\\n returns (uint256 balance);\\n\\n /**\\n * @dev Returns bool indicating whether strategy supports asset.\\n */\\n function supportsAsset(address _asset) external view returns (bool);\\n\\n /**\\n * @dev Collect reward tokens from the Strategy.\\n */\\n function collectRewardTokens() external;\\n\\n /**\\n * @dev The address array of the reward tokens for the Strategy.\\n */\\n function getRewardTokenAddresses() external view returns (address[] memory);\\n\\n function harvesterAddress() external view returns (address);\\n\\n function transferToken(address token, uint256 amount) external;\\n\\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\\n external;\\n}\\n\",\"keccak256\":\"0x79ca47defb3b5a56bba13f14c440838152fd1c1aa640476154516a16da4da8ba\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IVault.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { VaultStorage } from \\\"../vault/VaultStorage.sol\\\";\\n\\ninterface IVault {\\n // slither-disable-start constable-states\\n\\n event AssetSupported(address _asset);\\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\\n event StrategyApproved(address _addr);\\n event StrategyRemoved(address _addr);\\n event Mint(address _addr, uint256 _value);\\n event Redeem(address _addr, uint256 _value);\\n event CapitalPaused();\\n event CapitalUnpaused();\\n event RebasePaused();\\n event RebaseUnpaused();\\n event VaultBufferUpdated(uint256 _vaultBuffer);\\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\\n event PriceProviderUpdated(address _priceProvider);\\n event AllocateThresholdUpdated(uint256 _threshold);\\n event RebaseThresholdUpdated(uint256 _threshold);\\n event StrategistUpdated(address _address);\\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\\n event TrusteeFeeBpsChanged(uint256 _basis);\\n event TrusteeAddressChanged(address _address);\\n event SwapperChanged(address _address);\\n event SwapAllowedUndervalueChanged(uint256 _basis);\\n event SwapSlippageChanged(address _asset, uint256 _basis);\\n event Swapped(\\n address indexed _fromAsset,\\n address indexed _toAsset,\\n uint256 _fromAssetAmount,\\n uint256 _toAssetAmount\\n );\\n event StrategyAddedToMintWhitelist(address indexed strategy);\\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\\n event DripperChanged(address indexed _dripper);\\n event WithdrawalRequested(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount,\\n uint256 _queued\\n );\\n event WithdrawalClaimed(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount\\n );\\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\\n\\n // Governable.sol\\n function transferGovernance(address _newGovernor) external;\\n\\n function claimGovernance() external;\\n\\n function governor() external view returns (address);\\n\\n function ADMIN_IMPLEMENTATION() external view returns (address);\\n\\n // VaultAdmin.sol\\n function setPriceProvider(address _priceProvider) external;\\n\\n function priceProvider() external view returns (address);\\n\\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\\n\\n function redeemFeeBps() external view returns (uint256);\\n\\n function setVaultBuffer(uint256 _vaultBuffer) external;\\n\\n function vaultBuffer() external view returns (uint256);\\n\\n function setAutoAllocateThreshold(uint256 _threshold) external;\\n\\n function autoAllocateThreshold() external view returns (uint256);\\n\\n function setRebaseThreshold(uint256 _threshold) external;\\n\\n function rebaseThreshold() external view returns (uint256);\\n\\n function setStrategistAddr(address _address) external;\\n\\n function strategistAddr() external view returns (address);\\n\\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\\n\\n function maxSupplyDiff() external view returns (uint256);\\n\\n function setTrusteeAddress(address _address) external;\\n\\n function trusteeAddress() external view returns (address);\\n\\n function setTrusteeFeeBps(uint256 _basis) external;\\n\\n function trusteeFeeBps() external view returns (uint256);\\n\\n function ousdMetaStrategy() external view returns (address);\\n\\n function setSwapper(address _swapperAddr) external;\\n\\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\\n\\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\\n external;\\n\\n function supportAsset(address _asset, uint8 _unitConversion) external;\\n\\n function approveStrategy(address _addr) external;\\n\\n function removeStrategy(address _addr) external;\\n\\n function setAssetDefaultStrategy(address _asset, address _strategy)\\n external;\\n\\n function assetDefaultStrategies(address _asset)\\n external\\n view\\n returns (address);\\n\\n function pauseRebase() external;\\n\\n function unpauseRebase() external;\\n\\n function rebasePaused() external view returns (bool);\\n\\n function pauseCapital() external;\\n\\n function unpauseCapital() external;\\n\\n function capitalPaused() external view returns (bool);\\n\\n function transferToken(address _asset, uint256 _amount) external;\\n\\n function priceUnitMint(address asset) external view returns (uint256);\\n\\n function priceUnitRedeem(address asset) external view returns (uint256);\\n\\n function withdrawAllFromStrategy(address _strategyAddr) external;\\n\\n function withdrawAllFromStrategies() external;\\n\\n function withdrawFromStrategy(\\n address _strategyFromAddress,\\n address[] calldata _assets,\\n uint256[] calldata _amounts\\n ) external;\\n\\n function depositToStrategy(\\n address _strategyToAddress,\\n address[] calldata _assets,\\n uint256[] calldata _amounts\\n ) external;\\n\\n // VaultCore.sol\\n function mint(\\n address _asset,\\n uint256 _amount,\\n uint256 _minimumOusdAmount\\n ) external;\\n\\n function mintForStrategy(uint256 _amount) external;\\n\\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\\n\\n function burnForStrategy(uint256 _amount) external;\\n\\n function allocate() external;\\n\\n function rebase() external;\\n\\n function swapCollateral(\\n address fromAsset,\\n address toAsset,\\n uint256 fromAssetAmount,\\n uint256 minToAssetAmount,\\n bytes calldata data\\n ) external returns (uint256 toAssetAmount);\\n\\n function totalValue() external view returns (uint256 value);\\n\\n function checkBalance(address _asset) external view returns (uint256);\\n\\n function calculateRedeemOutputs(uint256 _amount)\\n external\\n view\\n returns (uint256[] memory);\\n\\n function getAssetCount() external view returns (uint256);\\n\\n function getAssetConfig(address _asset)\\n external\\n view\\n returns (VaultStorage.Asset memory config);\\n\\n function getAllAssets() external view returns (address[] memory);\\n\\n function getStrategyCount() external view returns (uint256);\\n\\n function swapper() external view returns (address);\\n\\n function allowedSwapUndervalue() external view returns (uint256);\\n\\n function getAllStrategies() external view returns (address[] memory);\\n\\n function isSupportedAsset(address _asset) external view returns (bool);\\n\\n function netOusdMintForStrategyThreshold() external view returns (uint256);\\n\\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\\n\\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\\n\\n function netOusdMintedForStrategy() external view returns (int256);\\n\\n function setDripper(address _dripper) external;\\n\\n function dripper() external view returns (address);\\n\\n function weth() external view returns (address);\\n\\n function cacheWETHAssetIndex() external;\\n\\n function wethAssetIndex() external view returns (uint256);\\n\\n function initialize(address, address) external;\\n\\n function setAdminImpl(address) external;\\n\\n function removeAsset(address _asset) external;\\n\\n // These are OETH specific functions\\n function addWithdrawalQueueLiquidity() external;\\n\\n function requestWithdrawal(uint256 _amount)\\n external\\n returns (uint256 requestId, uint256 queued);\\n\\n function claimWithdrawal(uint256 requestId)\\n external\\n returns (uint256 amount);\\n\\n function claimWithdrawals(uint256[] memory requestIds)\\n external\\n returns (uint256[] memory amounts, uint256 totalAmount);\\n\\n function withdrawalQueueMetadata()\\n external\\n view\\n returns (VaultStorage.WithdrawalQueueMetadata memory);\\n\\n function withdrawalRequests(uint256 requestId)\\n external\\n view\\n returns (VaultStorage.WithdrawalRequest memory);\\n\\n // OETHb specific functions\\n function addStrategyToMintWhitelist(address strategyAddr) external;\\n\\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\\n\\n function isMintWhitelistedStrategy(address strategyAddr)\\n external\\n view\\n returns (bool);\\n\\n function withdrawalClaimDelay() external view returns (uint256);\\n\\n function setWithdrawalClaimDelay(uint256 newDelay) external;\\n\\n function lastRebase() external view returns (uint64);\\n\\n function dripDuration() external view returns (uint64);\\n\\n function setDripDuration(uint256 _dripDuration) external;\\n\\n function rebasePerSecondMax() external view returns (uint64);\\n\\n function setRebaseRateMax(uint256 yearlyApr) external;\\n\\n function rebasePerSecondTarget() external view returns (uint64);\\n\\n function previewYield() external view returns (uint256 yield);\\n\\n // slither-disable-end constable-states\\n}\\n\",\"keccak256\":\"0x8d0a60f594d97578b0513b4da3d8fcafaa601950c6c4c016bf60b1344733269c\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IWETH9.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\ninterface IWETH9 {\\n event Approval(address indexed src, address indexed guy, uint256 wad);\\n event Deposit(address indexed dst, uint256 wad);\\n event Transfer(address indexed src, address indexed dst, uint256 wad);\\n event Withdrawal(address indexed src, uint256 wad);\\n\\n function allowance(address, address) external view returns (uint256);\\n\\n function approve(address guy, uint256 wad) external returns (bool);\\n\\n function balanceOf(address) external view returns (uint256);\\n\\n function decimals() external view returns (uint8);\\n\\n function deposit() external payable;\\n\\n function name() external view returns (string memory);\\n\\n function symbol() external view returns (string memory);\\n\\n function totalSupply() external view returns (uint256);\\n\\n function transfer(address dst, uint256 wad) external returns (bool);\\n\\n function transferFrom(\\n address src,\\n address dst,\\n uint256 wad\\n ) external returns (bool);\\n\\n function withdraw(uint256 wad) external;\\n}\\n\",\"keccak256\":\"0x05b7dce6c24d3cd4e48b5c6346d86e5e40ecc3291bcdf3f3ef091c98fc826519\",\"license\":\"MIT\"},\"contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { IERC20 } from \\\"@openzeppelin/contracts/token/ERC20/IERC20.sol\\\";\\n\\nimport { InitializableAbstractStrategy } from \\\"../../utils/InitializableAbstractStrategy.sol\\\";\\nimport { IWETH9 } from \\\"../../interfaces/IWETH9.sol\\\";\\nimport { CompoundingValidatorManager } from \\\"./CompoundingValidatorManager.sol\\\";\\n\\n/// @title Compounding Staking SSV Strategy\\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\\n/// @author Origin Protocol Inc\\ncontract CompoundingStakingSSVStrategy is\\n CompoundingValidatorManager,\\n InitializableAbstractStrategy\\n{\\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\\n address public immutable SSV_TOKEN;\\n\\n // For future use\\n uint256[50] private __gap;\\n\\n /// @param _baseConfig Base strategy config with\\n /// `platformAddress` not used so empty address\\n /// `vaultAddress` the address of the OETH Vault contract\\n /// @param _wethAddress Address of the WETH Token contract\\n /// @param _ssvToken Address of the SSV Token contract\\n /// @param _ssvNetwork Address of the SSV Network contract\\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\\n constructor(\\n BaseStrategyConfig memory _baseConfig,\\n address _wethAddress,\\n address _ssvToken,\\n address _ssvNetwork,\\n address _beaconChainDepositContract,\\n address _beaconProofs,\\n uint64 _beaconGenesisTimestamp\\n )\\n InitializableAbstractStrategy(_baseConfig)\\n CompoundingValidatorManager(\\n _wethAddress,\\n _baseConfig.vaultAddress,\\n _beaconChainDepositContract,\\n _ssvNetwork,\\n _beaconProofs,\\n _beaconGenesisTimestamp\\n )\\n {\\n SSV_TOKEN = _ssvToken;\\n\\n // Make sure nobody owns the implementation contract\\n _setGovernor(address(0));\\n }\\n\\n /// @notice Set up initial internal state including\\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\\n /// @param _rewardTokenAddresses Not used so empty array\\n /// @param _assets Not used so empty array\\n /// @param _pTokens Not used so empty array\\n function initialize(\\n address[] memory _rewardTokenAddresses,\\n address[] memory _assets,\\n address[] memory _pTokens\\n ) external onlyGovernor initializer {\\n InitializableAbstractStrategy._initialize(\\n _rewardTokenAddresses,\\n _assets,\\n _pTokens\\n );\\n\\n safeApproveAllTokens();\\n }\\n\\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\\n /// It just checks the asset is WETH and emits the Deposit event.\\n /// To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\\n /// @param _asset Address of the WETH token.\\n /// @param _amount Amount of WETH that was transferred to the strategy by the vault.\\n function deposit(address _asset, uint256 _amount)\\n external\\n override\\n onlyVault\\n nonReentrant\\n {\\n require(_asset == WETH, \\\"Unsupported asset\\\");\\n require(_amount > 0, \\\"Must deposit something\\\");\\n\\n // Account for the new WETH\\n depositedWethAccountedFor += _amount;\\n\\n emit Deposit(_asset, address(0), _amount);\\n }\\n\\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\\n /// It just emits the Deposit event.\\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\\n function depositAll() external override onlyVault nonReentrant {\\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\\n\\n if (newWeth > 0) {\\n // Account for the new WETH\\n depositedWethAccountedFor = wethBalance;\\n\\n emit Deposit(WETH, address(0), newWeth);\\n }\\n }\\n\\n /// @notice Withdraw ETH and WETH from this strategy contract.\\n /// @param _recipient Address to receive withdrawn assets.\\n /// @param _asset Address of the WETH token.\\n /// @param _amount Amount of WETH to withdraw.\\n function withdraw(\\n address _recipient,\\n address _asset,\\n uint256 _amount\\n ) external override onlyVault nonReentrant {\\n require(_asset == WETH, \\\"Unsupported asset\\\");\\n\\n _withdraw(_recipient, _amount, address(this).balance);\\n }\\n\\n function _withdraw(\\n address _recipient,\\n uint256 _withdrawAmount,\\n uint256 _ethBalance\\n ) internal {\\n require(_withdrawAmount > 0, \\\"Must withdraw something\\\");\\n require(_recipient != address(0), \\\"Must specify recipient\\\");\\n\\n // Convert any ETH from validator partial withdrawals, exits\\n // or execution rewards to WETH and do the necessary accounting.\\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\\n\\n // Transfer WETH to the recipient and do the necessary accounting.\\n _transferWeth(_withdrawAmount, _recipient);\\n\\n emit Withdrawal(WETH, address(0), _withdrawAmount);\\n }\\n\\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\\n /// execution rewards in this strategy to the vault.\\n /// This does not withdraw from the validators. That has to be done separately with the\\n /// `validatorWithdrawal` operation.\\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\\n uint256 ethBalance = address(this).balance;\\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\\n ethBalance;\\n\\n if (withdrawAmount > 0) {\\n _withdraw(vaultAddress, withdrawAmount, ethBalance);\\n }\\n }\\n\\n /// @notice Accounts for all the assets managed by this strategy which includes:\\n /// 1. The current WETH in this strategy contract\\n /// 2. The last verified ETH balance, total deposits and total validator balances\\n /// @param _asset Address of WETH asset.\\n /// @return balance Total value in ETH\\n function checkBalance(address _asset)\\n external\\n view\\n override\\n returns (uint256 balance)\\n {\\n require(_asset == WETH, \\\"Unsupported asset\\\");\\n\\n // Load the last verified balance from the storage\\n // and add to the latest WETH balance of this strategy.\\n balance =\\n lastVerifiedEthBalance +\\n IWETH9(WETH).balanceOf(address(this));\\n }\\n\\n /// @notice Returns bool indicating whether asset is supported by the strategy.\\n /// @param _asset The address of the WETH token.\\n function supportsAsset(address _asset) public view override returns (bool) {\\n return _asset == WETH;\\n }\\n\\n /// @notice Approves the SSV Network contract to transfer SSV tokens for validator registration.\\n function safeApproveAllTokens() public override {\\n // Approves the SSV Network contract to transfer SSV tokens when validators are registered\\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\\n }\\n\\n /**\\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\\n * like it did in the legacy NativeStakingStrategy.\\n * The new ETH will be accounted for in `checkBalance` after the next snapBalances and verifyBalances txs.\\n */\\n receive() external payable {}\\n\\n /***************************************\\n Internal functions\\n ****************************************/\\n\\n /// @notice is not supported for this strategy as there is no platform token.\\n function setPTokenAddress(address, address) external pure override {\\n revert(\\\"Unsupported function\\\");\\n }\\n\\n /// @notice is not supported for this strategy as there is no platform token.\\n function removePToken(uint256) external pure override {\\n revert(\\\"Unsupported function\\\");\\n }\\n\\n /// @dev This strategy does not use a platform token like the old Aave and Compound strategies.\\n function _abstractSetPToken(address _asset, address) internal override {}\\n\\n /// @dev Consensus rewards are compounded to the validator's balance instead of being\\n /// swept to this strategy contract.\\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract.\\n /// Withdrawals from validators also accumulate as ETH in this strategy contract.\\n /// It's too complex to separate the rewards from withdrawals so this function is not implemented.\\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can now regulate\\n /// the increase in assets.\\n function _collectRewardTokens() internal pure override {\\n revert(\\\"Unsupported function\\\");\\n }\\n}\\n\",\"keccak256\":\"0x1c54018f3c60ed6c0a3a9c5c2c6e6ca4c29ffd4434e96fab0cb7f3d06e3bd649\",\"license\":\"BUSL-1.1\"},\"contracts/strategies/NativeStaking/CompoundingValidatorManager.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { SafeERC20 } from \\\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\\\";\\nimport { SafeCast } from \\\"@openzeppelin/contracts/utils/math/SafeCast.sol\\\";\\nimport { Math } from \\\"@openzeppelin/contracts/utils/math/Math.sol\\\";\\nimport { IERC20 } from \\\"@openzeppelin/contracts/token/ERC20/IERC20.sol\\\";\\nimport { Governable } from \\\"../../governance/Governable.sol\\\";\\nimport { IDepositContract } from \\\"../../interfaces/IDepositContract.sol\\\";\\nimport { IWETH9 } from \\\"../../interfaces/IWETH9.sol\\\";\\nimport { ISSVNetwork, Cluster } from \\\"../../interfaces/ISSVNetwork.sol\\\";\\nimport { BeaconRoots } from \\\"../../beacon/BeaconRoots.sol\\\";\\nimport { PartialWithdrawal } from \\\"../../beacon/PartialWithdrawal.sol\\\";\\nimport { IBeaconProofs } from \\\"../../interfaces/IBeaconProofs.sol\\\";\\n\\n/**\\n * @title Validator lifecycle management contract\\n * @notice This contract implements all the required functionality to\\n * register, deposit, withdraw, exit and remove validators.\\n * @author Origin Protocol Inc\\n */\\nabstract contract CompoundingValidatorManager is Governable {\\n using SafeERC20 for IERC20;\\n\\n /// @dev The amount of ETH in wei that is required for a deposit to a new validator.\\n /// Initially this is 32 ETH, but will be reduced to 1 ETH after P2P's APIs have been updated\\n /// to support deposits of 1 ETH.\\n uint256 internal constant DEPOSIT_AMOUNT_WEI = 32 ether;\\n /// @dev The maximum number of deposits that are waiting to be verified as processed on the beacon chain.\\n uint256 internal constant MAX_DEPOSITS = 12;\\n /// @dev The maximum number of validators that can be verified.\\n uint256 internal constant MAX_VERIFIED_VALIDATORS = 48;\\n /// @dev The default withdrawable epoch value on the Beacon chain.\\n /// A value in the far future means the validator is not exiting.\\n uint64 internal constant FAR_FUTURE_EPOCH = type(uint64).max;\\n /// @dev The number of seconds between each beacon chain slot.\\n uint64 internal constant SLOT_DURATION = 12;\\n /// @dev The number of slots in each beacon chain epoch.\\n uint64 internal constant SLOTS_PER_EPOCH = 32;\\n /// @dev Minimum time in seconds to allow snapped balances to be verified.\\n /// Set to 35 slots which is 3 slots more than 1 epoch (32 slots). Deposits get processed\\n /// once per epoch. This larger than 1 epoch delay should achieve that `snapBalances` sometimes\\n /// get called in the middle (or towards the end) of the epoch. Giving the off-chain script\\n /// sufficient time after the end of the epoch to prepare the proofs and call `verifyBalances`.\\n /// This is considering a malicious actor would keep calling `snapBalances` as frequent as possible\\n /// to disturb our operations.\\n uint64 internal constant SNAP_BALANCES_DELAY = 35 * SLOT_DURATION;\\n\\n /// @notice The address of the Wrapped ETH (WETH) token contract\\n address public immutable WETH;\\n /// @notice The address of the beacon chain deposit contract\\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\\n /// @notice The address of the SSV Network contract used to interface with\\n address public immutable SSV_NETWORK;\\n /// @notice Address of the OETH Vault proxy contract\\n address public immutable VAULT_ADDRESS;\\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\\n address public immutable BEACON_PROOFS;\\n /// @notice The timestamp of the Beacon chain genesis.\\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\\n uint64 public immutable BEACON_GENESIS_TIMESTAMP;\\n\\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\\n address public validatorRegistrator;\\n\\n /// @notice Deposit data for new compounding validators.\\n /// @dev A `VERIFIED` deposit can mean 3 separate things:\\n /// - a deposit has been processed by the beacon chain and shall be included in the\\n /// balance of the next verifyBalances call\\n /// - a deposit has been done to a slashed validator and has probably been recovered\\n /// back to this strategy. Probably because we can not know for certain. This contract\\n /// only detects when the validator has passed its withdrawal epoch. It is close to impossible\\n /// to prove with Merkle Proofs that the postponed deposit this contract is responsible for\\n /// creating is not present anymore in BeaconChain.state.pending_deposits. This in effect\\n /// means that there might be a period where this contract thinks the deposit has been already\\n /// returned as ETH balance before it happens. This will result in some days (or weeks)\\n /// -> depending on the size of deposit queue of showing a deficit when calling `checkBalance`.\\n /// As this only offsets the yield and doesn't cause a critical double-counting we are not addressing\\n /// this issue.\\n /// - A deposit has been done to the validator, but our deposit has been front run by a malicious\\n /// actor. Funds in the deposit this contract makes are not recoverable.\\n enum DepositStatus {\\n UNKNOWN, // default value\\n PENDING, // deposit is pending and waiting to be verified\\n VERIFIED // deposit has been verified\\n }\\n\\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\\n /// @param amountGwei Amount of ETH in gwei that has been deposited to the beacon chain deposit contract\\n /// @param slot The beacon chain slot number when the deposit has been made\\n /// @param depositIndex The index of the deposit in the list of active deposits\\n /// @param status The status of the deposit, either UNKNOWN, PENDING or VERIFIED\\n /// @param withdrawableEpoch The withdrawableEpoch of the validator which is being deposited to.\\n /// At deposit time this is set to max default value (FAR_FUTURE_EPOCH). If a deposit has\\n /// made to a slashed validator the `withdrawableEpoch` will be set to the epoch of that\\n /// validator.\\n struct DepositData {\\n bytes32 pubKeyHash;\\n uint64 amountGwei;\\n uint64 slot;\\n uint32 depositIndex;\\n DepositStatus status;\\n uint64 withdrawableEpoch;\\n }\\n /// @notice Restricts to only one deposit to an unverified validator at a time.\\n /// This is to limit front-running attacks of deposits to the beacon chain contract.\\n ///\\n /// @dev The value is set to true when a deposit to a new validator has been done that has\\n /// not yet be verified.\\n bool public firstDeposit;\\n /// @notice Unique identifier of the next validator deposit.\\n uint128 public nextDepositID;\\n /// @notice Mapping of the deposit ID to the deposit data\\n mapping(uint256 => DepositData) public deposits;\\n /// @notice List of strategy deposit IDs to a validator.\\n /// The list can be for deposits waiting to be verified as processed on the beacon chain,\\n /// or deposits that have been verified to an exiting validator and is now waiting for the\\n /// validator's balance to be swept.\\n /// The list may not be ordered by time of deposit.\\n /// Removed deposits will move the last deposit to the removed index.\\n uint256[] public depositList;\\n\\n enum ValidatorState {\\n NON_REGISTERED, // validator is not registered on the SSV network\\n REGISTERED, // validator is registered on the SSV network\\n STAKED, // validator has funds staked\\n VERIFIED, // validator has been verified to exist on the beacon chain\\n EXITING, // The validator has been requested to exit or has been verified as forced exit\\n EXITED, // The validator has been verified to have a zero balance\\n REMOVED, // validator has funds withdrawn to this strategy contract and is removed from the SSV\\n INVALID // The validator has been front-run and the withdrawal address is not this strategy\\n }\\n\\n // Validator data\\n struct ValidatorData {\\n ValidatorState state; // The state of the validator known to this contract\\n uint40 index; // The index of the validator on the beacon chain\\n }\\n /// @notice List of validator public key hashes that have been verified to exist on the beacon chain.\\n /// These have had a deposit processed and the validator's balance increased.\\n /// Validators will be removed from this list when its verified they have a zero balance.\\n bytes32[] public verifiedValidators;\\n /// @notice Mapping of the hash of the validator's public key to the validator state and index.\\n /// Uses the Beacon chain hashing for BLSPubkey which is sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\\n mapping(bytes32 => ValidatorData) public validator;\\n\\n /// @param blockRoot Beacon chain block root of the snapshot\\n /// @param timestamp Timestamp of the snapshot\\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\\n struct Balances {\\n bytes32 blockRoot;\\n uint64 timestamp;\\n uint128 ethBalance;\\n }\\n /// @notice Mapping of the block root to the balances at that slot\\n Balances public snappedBalance;\\n /// @notice The last verified ETH balance of the strategy\\n uint256 public lastVerifiedEthBalance;\\n\\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\\n /// of WETH that has already been accounted for.\\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\\n /// deposit events.\\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\\n /// be staked.\\n uint256 public depositedWethAccountedFor;\\n\\n // For future use\\n uint256[50] private __gap;\\n\\n event RegistratorChanged(address indexed newAddress);\\n event StakingMonitorChanged(address indexed newAddress);\\n event FirstDepositReset();\\n event SSVValidatorRegistered(\\n bytes32 indexed pubKeyHash,\\n uint64[] operatorIds\\n );\\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\\n event ETHStaked(\\n bytes32 indexed pubKeyHash,\\n uint256 indexed depositID,\\n bytes pubKey,\\n uint256 amountWei\\n );\\n event ValidatorVerified(\\n bytes32 indexed pubKeyHash,\\n uint40 indexed validatorIndex\\n );\\n event ValidatorInvalid(bytes32 indexed pubKeyHash);\\n event DepositVerified(uint256 indexed depositID, uint256 amountWei);\\n event DepositToValidatorExiting(\\n uint256 indexed depositID,\\n uint256 amountWei,\\n uint64 withdrawableEpoch\\n );\\n event DepositValidatorExited(uint256 indexed depositID, uint256 amountWei);\\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\\n event BalancesSnapped(bytes32 indexed blockRoot, uint256 ethBalance);\\n event BalancesVerified(\\n uint64 indexed timestamp,\\n uint256 totalDepositsWei,\\n uint256 totalValidatorBalance,\\n uint256 ethBalance\\n );\\n\\n /// @dev Throws if called by any account other than the Registrator\\n modifier onlyRegistrator() {\\n require(msg.sender == validatorRegistrator, \\\"Not Registrator\\\");\\n _;\\n }\\n\\n /// @param _wethAddress Address of the Erc20 WETH Token contract\\n /// @param _vaultAddress Address of the Vault\\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\\n /// @param _ssvNetwork Address of the SSV Network contract\\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\\n constructor(\\n address _wethAddress,\\n address _vaultAddress,\\n address _beaconChainDepositContract,\\n address _ssvNetwork,\\n address _beaconProofs,\\n uint64 _beaconGenesisTimestamp\\n ) {\\n WETH = _wethAddress;\\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\\n SSV_NETWORK = _ssvNetwork;\\n VAULT_ADDRESS = _vaultAddress;\\n BEACON_PROOFS = _beaconProofs;\\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\\n\\n require(\\n block.timestamp > _beaconGenesisTimestamp,\\n \\\"Invalid genesis timestamp\\\"\\n );\\n }\\n\\n /**\\n *\\n * Admin Functions\\n *\\n */\\n\\n /// @notice Set the address of the registrator which can register, exit and remove validators\\n function setRegistrator(address _address) external onlyGovernor {\\n validatorRegistrator = _address;\\n emit RegistratorChanged(_address);\\n }\\n\\n /// @notice Reset the `firstDeposit` flag to false so deposits to unverified validators can be made again.\\n function resetFirstDeposit() external onlyGovernor {\\n require(firstDeposit, \\\"No first deposit\\\");\\n\\n firstDeposit = false;\\n\\n emit FirstDepositReset();\\n }\\n\\n /**\\n *\\n * Validator Management\\n *\\n */\\n\\n /// @notice Registers a single validator in a SSV Cluster.\\n /// Only the Registrator can call this function.\\n /// @param publicKey The public key of the validator\\n /// @param operatorIds The operator IDs of the SSV Cluster\\n /// @param sharesData The shares data for the validator\\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\\n /// @param cluster The SSV cluster details including the validator count and SSV balance\\n // slither-disable-start reentrancy-no-eth\\n function registerSsvValidator(\\n bytes calldata publicKey,\\n uint64[] calldata operatorIds,\\n bytes calldata sharesData,\\n uint256 ssvAmount,\\n Cluster calldata cluster\\n ) external onlyRegistrator {\\n // Hash the public key using the Beacon Chain's format\\n bytes32 pubKeyHash = _hashPubKey(publicKey);\\n // Check each public key has not already been used\\n require(\\n validator[pubKeyHash].state == ValidatorState.NON_REGISTERED,\\n \\\"Validator already registered\\\"\\n );\\n\\n // Store the validator state as registered\\n validator[pubKeyHash].state = ValidatorState.REGISTERED;\\n\\n ISSVNetwork(SSV_NETWORK).registerValidator(\\n publicKey,\\n operatorIds,\\n sharesData,\\n ssvAmount,\\n cluster\\n );\\n\\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\\n }\\n\\n // slither-disable-end reentrancy-no-eth\\n\\n struct ValidatorStakeData {\\n bytes pubkey;\\n bytes signature;\\n bytes32 depositDataRoot;\\n }\\n\\n /// @notice Stakes WETH in this strategy to a compounding validator.\\n /// Does not convert any ETH sitting in this strategy to WETH.\\n /// @param validatorStakeData validator data needed to stake.\\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\\n /// Only the registrator can call this function.\\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\\n // slither-disable-start reentrancy-eth\\n function stakeEth(\\n ValidatorStakeData calldata validatorStakeData,\\n uint64 depositAmountGwei\\n ) external onlyRegistrator {\\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\\n // Check there is enough WETH from the deposits sitting in this strategy contract\\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\\n // the ETH can be withdrawn and then deposited back to the strategy.\\n require(\\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\\n \\\"Insufficient WETH\\\"\\n );\\n require(depositList.length < MAX_DEPOSITS, \\\"Max deposits\\\");\\n\\n // Convert required ETH from WETH and do the necessary accounting\\n _convertWethToEth(depositAmountWei);\\n\\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\\n bytes32 pubKeyHash = _hashPubKey(validatorStakeData.pubkey);\\n ValidatorState currentState = validator[pubKeyHash].state;\\n // Can only stake to a validator has have been registered or verified.\\n // Can not stake to a validator that has been staked but not yet verified.\\n require(\\n (currentState == ValidatorState.REGISTERED ||\\n currentState == ValidatorState.VERIFIED),\\n \\\"Not registered or verified\\\"\\n );\\n require(depositAmountWei >= 1 ether, \\\"Deposit too small\\\");\\n if (currentState == ValidatorState.REGISTERED) {\\n // Can only have one pending deposit to an unverified validator at a time.\\n // This is to limit front-running deposit attacks to a single deposit.\\n // The exiting deposit needs to be verified before another deposit can be made.\\n // If there was a front-running attack, the validator needs to be verified as invalid\\n // and the Governor calls `resetFirstDeposit` to set `firstDeposit` to false.\\n require(!firstDeposit, \\\"Existing first deposit\\\");\\n // Limits the amount of ETH that can be at risk from a front-running deposit attack.\\n require(\\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\\n \\\"Invalid first deposit amount\\\"\\n );\\n // Limits the number of validator balance proofs to verifyBalances\\n require(\\n verifiedValidators.length + 1 < MAX_VERIFIED_VALIDATORS,\\n \\\"Max validators\\\"\\n );\\n\\n // Flag a deposit to an unverified validator so only no other deposits can be made\\n // to an unverified validator.\\n firstDeposit = true;\\n }\\n\\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\\n * that was introduced with the Pectra upgrade.\\n * bytes11(0) to fill up the required zeros\\n * remaining bytes20 are for the address\\n */\\n bytes memory withdrawalCredentials = abi.encodePacked(\\n bytes1(0x02),\\n bytes11(0),\\n address(this)\\n );\\n\\n //// Update contract storage\\n // Store the validator state if needed\\n if (currentState == ValidatorState.REGISTERED) {\\n validator[pubKeyHash].state = ValidatorState.STAKED;\\n }\\n\\n /// After the Pectra upgrade the validators have a new restriction when proposing\\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\\n /// forward. Each slot is created at strict 12 second intervals and those slots can\\n /// either have blocks attached to them or not. This way using the block.timestamp\\n /// the slot number can easily be calculated.\\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\\n BEACON_GENESIS_TIMESTAMP) / SLOT_DURATION;\\n\\n // Store the deposit data for verifyDeposit and verifyBalances\\n uint256 depositID = nextDepositID++;\\n deposits[depositID] = DepositData({\\n pubKeyHash: pubKeyHash,\\n amountGwei: depositAmountGwei,\\n slot: depositSlot,\\n depositIndex: SafeCast.toUint32(depositList.length),\\n status: DepositStatus.PENDING,\\n withdrawableEpoch: FAR_FUTURE_EPOCH\\n });\\n depositList.push(depositID);\\n\\n // Deposit to the Beacon Chain deposit contract.\\n // This will create a deposit in the beacon chain's pending deposit queue.\\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\\n value: depositAmountWei\\n }(\\n validatorStakeData.pubkey,\\n withdrawalCredentials,\\n validatorStakeData.signature,\\n validatorStakeData.depositDataRoot\\n );\\n\\n emit ETHStaked(\\n pubKeyHash,\\n depositID,\\n validatorStakeData.pubkey,\\n depositAmountWei\\n );\\n }\\n\\n // slither-disable-end reentrancy-eth\\n\\n /// @notice Request a full or partial withdrawal from a validator.\\n /// A zero amount will trigger a full withdrawal.\\n /// If the remaining balance is < 32 ETH then only the amount in excess of 32 ETH will be withdrawn.\\n /// Only the Registrator can call this function.\\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\\n /// If no ETH balance, the tx will revert.\\n /// @param publicKey The public key of the validator\\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\\n /// A zero amount will trigger a full withdrawal.\\n // slither-disable-start reentrancy-no-eth\\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\\n external\\n payable\\n onlyRegistrator\\n {\\n // Hash the public key using the Beacon Chain's format\\n bytes32 pubKeyHash = _hashPubKey(publicKey);\\n ValidatorState currentState = validator[pubKeyHash].state;\\n // Validator full withdrawal could be denied due to multiple reasons:\\n // - the validator has not been activated or active long enough\\n // (current_epoch < activation_epoch + SHARD_COMMITTEE_PERIOD)\\n // - the validator has pending balance to withdraw from a previous partial withdrawal request\\n //\\n // Meaning that the on-chain to beacon chain full withdrawal request could fail. Instead\\n // of adding complexity of verifying if a validator is eligible for a full exit, we allow\\n // multiple full withdrawal requests per validator.\\n require(\\n currentState == ValidatorState.VERIFIED ||\\n currentState == ValidatorState.EXITING,\\n \\\"Validator not verified/exiting\\\"\\n );\\n\\n // If a full withdrawal (validator exit)\\n if (amountGwei == 0) {\\n // Store the validator state as exiting so no more deposits can be made to it.\\n validator[pubKeyHash].state = ValidatorState.EXITING;\\n }\\n\\n // Do not remove from the list of verified validators.\\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\\n // The validator state will be set to EXITED in the verifyBalances function.\\n\\n PartialWithdrawal.request(publicKey, amountGwei);\\n\\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\\n }\\n\\n // slither-disable-end reentrancy-no-eth\\n\\n /// @notice Remove the validator from the SSV Cluster after:\\n /// - the validator has been exited from `validatorWithdrawal` or slashed\\n /// - the validator has incorrectly registered and can not be staked to\\n /// - the initial deposit was front-run and the withdrawal address is not this strategy's address.\\n /// Make sure `validatorWithdrawal` is called with a zero amount and the validator has exited the Beacon chain.\\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\\n /// Only the registrator can call this function.\\n /// @param publicKey The public key of the validator\\n /// @param operatorIds The operator IDs of the SSV Cluster\\n /// @param cluster The SSV cluster details including the validator count and SSV balance\\n // slither-disable-start reentrancy-no-eth\\n function removeSsvValidator(\\n bytes calldata publicKey,\\n uint64[] calldata operatorIds,\\n Cluster calldata cluster\\n ) external onlyRegistrator {\\n // Hash the public key using the Beacon Chain's format\\n bytes32 pubKeyHash = _hashPubKey(publicKey);\\n ValidatorState currentState = validator[pubKeyHash].state;\\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\\n require(\\n currentState == ValidatorState.REGISTERED ||\\n currentState == ValidatorState.EXITED ||\\n currentState == ValidatorState.INVALID,\\n \\\"Validator not regd or exited\\\"\\n );\\n\\n validator[pubKeyHash].state = ValidatorState.REMOVED;\\n\\n ISSVNetwork(SSV_NETWORK).removeValidator(\\n publicKey,\\n operatorIds,\\n cluster\\n );\\n\\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\\n }\\n\\n /**\\n *\\n * SSV Management\\n *\\n */\\n\\n // slither-disable-end reentrancy-no-eth\\n\\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\\n /// by the Strategist which is already holding SSV tokens.\\n\\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\\n /// @param operatorIds The operator IDs of the SSV Cluster\\n /// @param ssvAmount The amount of SSV tokens to be withdrawn from the SSV cluster\\n /// @param cluster The SSV cluster details including the validator count and SSV balance\\n function withdrawSSV(\\n uint64[] memory operatorIds,\\n uint256 ssvAmount,\\n Cluster memory cluster\\n ) external onlyGovernor {\\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\\n }\\n\\n /**\\n *\\n * Beacon Chain Proofs\\n *\\n */\\n\\n /// @notice Verifies a validator's index to its public key.\\n /// Adds to the list of verified validators if the validator's withdrawal address is this strategy's address.\\n /// Marks the validator as invalid and removes the deposit if the withdrawal address is not this strategy's address.\\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\\n /// we are verifying.\\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\\n /// which is the beacon block root of the previous block.\\n /// @param validatorIndex The index of the validator on the beacon chain.\\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\\n /// @param withdrawalAddress The withdrawal address of the validator which should be this strategy's address.\\n /// If the withdrawal address is not this strategy's address, the initial deposit was front-run\\n /// and the validator is marked as invalid.\\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n /// BeaconBlock.state.validators[validatorIndex].pubkey\\n function verifyValidator(\\n uint64 nextBlockTimestamp,\\n uint40 validatorIndex,\\n bytes32 pubKeyHash,\\n address withdrawalAddress,\\n bytes calldata validatorPubKeyProof\\n ) external {\\n require(\\n validator[pubKeyHash].state == ValidatorState.STAKED,\\n \\\"Validator not staked\\\"\\n );\\n\\n // Get the beacon block root of the slot we are verifying the validator in.\\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\\n\\n // Verify the validator index is for the validator with the given public key.\\n // Also verify the validator's withdrawal credential points to the `withdrawalAddress`.\\n IBeaconProofs(BEACON_PROOFS).verifyValidator(\\n blockRoot,\\n pubKeyHash,\\n validatorPubKeyProof,\\n validatorIndex,\\n withdrawalAddress\\n );\\n\\n // Store the validator state as verified\\n validator[pubKeyHash] = ValidatorData({\\n state: ValidatorState.VERIFIED,\\n index: validatorIndex\\n });\\n\\n // If the initial deposit was front-run and the withdrawal address is not this strategy\\n if (withdrawalAddress != address(this)) {\\n // override the validator state\\n validator[pubKeyHash].state = ValidatorState.INVALID;\\n\\n // Find and remove the deposit as the funds can not be recovered\\n uint256 depositCount = depositList.length;\\n for (uint256 i = 0; i < depositCount; i++) {\\n DepositData memory deposit = deposits[depositList[i]];\\n if (deposit.pubKeyHash == pubKeyHash) {\\n // next verifyBalances will correctly account for the loss of a front-run\\n // deposit. Doing it here accounts for the loss as soon as possible\\n lastVerifiedEthBalance -= Math.min(\\n lastVerifiedEthBalance,\\n uint256(deposit.amountGwei) * 1 gwei\\n );\\n _removeDeposit(depositList[i], deposit);\\n break;\\n }\\n }\\n\\n // Leave the `firstDeposit` flag as true so no more deposits to unverified validators can be made.\\n // The Governor has to reset the `firstDeposit` to false before another deposit to\\n // an unverified validator can be made.\\n // The Governor can set a new `validatorRegistrator` if they suspect it has been compromised.\\n\\n emit ValidatorInvalid(pubKeyHash);\\n return;\\n }\\n\\n // Add the new validator to the list of verified validators\\n verifiedValidators.push(pubKeyHash);\\n\\n // Reset the firstDeposit flag as the first deposit to an unverified validator has been verified.\\n firstDeposit = false;\\n\\n emit ValidatorVerified(pubKeyHash, validatorIndex);\\n }\\n\\n struct FirstPendingDepositSlotProofData {\\n uint64 slot;\\n bytes proof;\\n }\\n struct FirstPendingDepositWithdrawableProofData {\\n uint64 slot;\\n uint40 validatorIndex;\\n bytes32 pubKeyHash;\\n bytes pendingDepositPubKeyProof;\\n bytes withdrawableEpochProof;\\n bytes validatorPubKeyProof;\\n }\\n\\n struct StrategyValidatorProofData {\\n uint64 withdrawableEpoch;\\n bytes withdrawableEpochProof;\\n }\\n\\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\\n ///\\n /// Important: this function has a limitation where `depositProcessedSlot` and `firstDepositValidatorCreatedSlot`\\n /// that is passed by the off-chain verifier requires a slot immediately after them to propose a block otherwise\\n /// the `BeaconRoots.parentBlockRoot` will fail. This shouldn't be a problem, since by the current behaviour of\\n /// beacon chain only 1%-3% slots don't propose a block.\\n /// @param depositID The deposit ID emitted in `ETHStaked` from the `stakeEth` function.\\n /// @param depositProcessedSlot Any slot on or after the strategy's deposit was processed on the beacon chain.\\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\\n /// set for the next block timestamp in 12 seconds time.\\n /// @param firstPendingDeposit a `FirstPendingDepositSlotProofData` struct containing:\\n /// - slot: The beacon chain slot of the first deposit in the beacon chain's deposit queue.\\n /// Can be anything if the deposit queue is empty, but zero is a good choice.\\n /// - proof: The merkle proof of the first pending deposit's slot to the beacon block root.\\n /// Can be either:\\n /// * 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\\n /// * 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\\n /// @param strategyValidatorData a `StrategyValidatorProofData` struct containing:\\n /// - withdrawableEpoch: The withdrawable epoch of the validator the strategy is depositing to.\\n /// - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the validator the strategy\\n /// is depositing to, to the beacon block root.\\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n // slither-disable-start reentrancy-no-eth\\n function verifyDeposit(\\n uint256 depositID,\\n uint64 depositProcessedSlot,\\n FirstPendingDepositSlotProofData calldata firstPendingDeposit,\\n StrategyValidatorProofData calldata strategyValidatorData\\n ) external {\\n // Load into memory the previously saved deposit data\\n DepositData memory deposit = deposits[depositID];\\n ValidatorData memory strategyValidator = validator[deposit.pubKeyHash];\\n require(deposit.status == DepositStatus.PENDING, \\\"Deposit not pending\\\");\\n require(\\n strategyValidator.state == ValidatorState.VERIFIED,\\n \\\"Validator not verified\\\"\\n );\\n // The verification slot must be after the deposit's slot.\\n // This is needed for when the deposit queue is empty.\\n require(deposit.slot < depositProcessedSlot, \\\"Slot not after deposit\\\");\\n\\n // Get the parent beacon block root of the next block which is the block root of the deposit verification slot.\\n // This will revert if the slot after the verification slot was missed.\\n bytes32 depositBlockRoot = BeaconRoots.parentBlockRoot(\\n _calcNextBlockTimestamp(depositProcessedSlot)\\n );\\n\\n // Verify the slot of the first pending deposit matches the beacon chain\\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\\n .verifyFirstPendingDeposit(\\n depositBlockRoot,\\n firstPendingDeposit.slot,\\n firstPendingDeposit.proof\\n );\\n\\n // Verify the withdrawableEpoch on the validator of the strategy's deposit\\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\\n depositBlockRoot,\\n strategyValidator.index,\\n strategyValidatorData.withdrawableEpoch,\\n strategyValidatorData.withdrawableEpochProof\\n );\\n\\n // If the validator is exiting because it has been slashed\\n if (strategyValidatorData.withdrawableEpoch != FAR_FUTURE_EPOCH) {\\n // Store the exit epoch in the deposit data\\n deposit.withdrawableEpoch = strategyValidatorData.withdrawableEpoch;\\n\\n emit DepositToValidatorExiting(\\n depositID,\\n uint256(deposit.amountGwei) * 1 gwei,\\n strategyValidatorData.withdrawableEpoch\\n );\\n\\n validator[deposit.pubKeyHash].state = ValidatorState.EXITING;\\n\\n // Leave the deposit status as PENDING\\n return;\\n }\\n\\n // solhint-disable max-line-length\\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\\n // many deposits in the same block, hence have the same pending deposit slot.\\n // If the deposit queue is empty then our deposit must have been processed on the beacon chain.\\n // The deposit slot can be zero for validators consolidating to a compounding validator or 0x01 validator\\n // being promoted to a compounding one. Reference:\\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\\n // We can not guarantee that the deposit has been processed in that case.\\n // solhint-enable max-line-length\\n require(\\n deposit.slot < firstPendingDeposit.slot || isDepositQueueEmpty,\\n \\\"Deposit likely not processed\\\"\\n );\\n\\n // Remove the deposit now it has been verified as processed on the beacon chain.\\n _removeDeposit(depositID, deposit);\\n\\n emit DepositVerified(depositID, uint256(deposit.amountGwei) * 1 gwei);\\n }\\n\\n function _removeDeposit(uint256 depositID, DepositData memory deposit)\\n internal\\n {\\n // After verifying the proof, update the contract storage\\n deposits[depositID].status = DepositStatus.VERIFIED;\\n // Move the last deposit to the index of the verified deposit\\n uint256 lastDeposit = depositList[depositList.length - 1];\\n depositList[deposit.depositIndex] = lastDeposit;\\n deposits[lastDeposit].depositIndex = deposit.depositIndex;\\n // Delete the last deposit from the list\\n depositList.pop();\\n }\\n\\n /// @dev Calculates the timestamp of the next execution block from the given slot.\\n /// @param slot The beacon chain slot number used for merkle proof verification.\\n function _calcNextBlockTimestamp(uint64 slot)\\n internal\\n view\\n returns (uint64)\\n {\\n // Calculate the next block timestamp from the slot.\\n return SLOT_DURATION * slot + BEACON_GENESIS_TIMESTAMP + SLOT_DURATION;\\n }\\n\\n // slither-disable-end reentrancy-no-eth\\n\\n /// @notice Stores the current ETH balance at the current block and beacon block root\\n /// of the slot that is associated with the previous block.\\n ///\\n /// When snapping / verifying balance it is of a high importance that there is no\\n /// miss-match in respect to ETH that is held by the contract and balances that are\\n /// verified on the validators.\\n ///\\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\\n /// constructing a block on the beacon chain consist of:\\n /// - process_withdrawals: ETH is deducted from the validator's balance\\n /// - process_execution_payload: immediately after the previous step executing all the\\n /// transactions\\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\\n /// contained in the withdrawal credentials of the exited validators\\n ///\\n /// That means that balance increases which are part of the post-block execution state are\\n /// done within the block, but the transaction that are contained within that block can not\\n /// see / interact with the balance from the exited validators. Only transactions in the\\n /// next block can do that.\\n ///\\n /// When snap balances is performed the state of the chain is snapped across 2 separate\\n /// chain states:\\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\\n /// will be referred to as Y - 1 as it makes no difference in the argument\\n ///\\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\\n /// miss-counting ETH or much more dangerous double counting of the ETH.\\n ///\\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\\n /// - ETH balance in the contract on block X\\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\\n /// - ETH balance in validators that are active in slot Y - 1\\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\\n /// and have their balance visible to transactions in slot Y and corresponding block X\\n /// (or sooner)\\n ///\\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\\n ///\\n /// *ETH balance in the contract on block X*\\n ///\\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\\n ///\\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\\n /// would result in double counting the `stakedEth` since it would be present once in the\\n /// snapped contract balance and the second time in deposit storage variables.\\n ///\\n /// This behaviour is correct.\\n ///\\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\\n ///\\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\\n /// contract at block Z. The execution layer doesn't have direct access to the state of\\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\\n /// and could also be part of the validator balances. It does that by verifying that at\\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\\n /// the last snap till now all are still in queue. Which ensures they can not be part of\\n /// the validator balances in later steps.\\n ///\\n /// This behaviour is correct.\\n ///\\n /// *ETH balance in validators that are active in slot Y - 1*\\n ///\\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\\n /// correctness.\\n ///\\n /// This behaviour is correct.\\n ///\\n /// *The withdrawn validators*\\n ///\\n /// The withdrawn validators could have their balances deducted in any slot before slot\\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\\n /// look at the \\\"worst case scenario\\\" where the validator withdrawal is processed in the\\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\\n /// even if the validator exits at the latest possible time it is paramount that the ETH\\n /// balance on the execution layer is recorded in the next block. Correctly accounting\\n /// for the withdrawn ETH.\\n ///\\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\\n /// validator balance verification as well as execution layer contract balance snap.\\n ///\\n /// This behaviour is correct.\\n ///\\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\\n function snapBalances() external {\\n uint64 currentTimestamp = SafeCast.toUint64(block.timestamp);\\n require(\\n snappedBalance.timestamp + SNAP_BALANCES_DELAY < currentTimestamp,\\n \\\"Snap too soon\\\"\\n );\\n\\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(currentTimestamp);\\n // Get the current ETH balance\\n uint256 ethBalance = address(this).balance;\\n\\n // Store the snapped balance\\n snappedBalance = Balances({\\n blockRoot: blockRoot,\\n timestamp: currentTimestamp,\\n ethBalance: SafeCast.toUint128(ethBalance)\\n });\\n\\n emit BalancesSnapped(blockRoot, ethBalance);\\n }\\n\\n // A struct is used to avoid stack too deep errors\\n struct BalanceProofs {\\n // BeaconBlock.state.balances\\n bytes32 balancesContainerRoot;\\n bytes balancesContainerProof;\\n // BeaconBlock.state.balances[validatorIndex]\\n bytes32[] validatorBalanceLeaves;\\n bytes[] validatorBalanceProofs;\\n }\\n\\n /// @notice Verifies the balances of all active validators on the beacon chain\\n /// and checks no pending deposits have been processed by the beacon chain.\\n /// @param validatorVerificationBlockTimestamp next block's timestamp of a slot that has the first pending\\n /// deposit already applied to the validator.\\n /// @param firstPendingDeposit a `FirstPendingDepositWithdrawableProofData` struct containing:\\n /// - slot: The beacon chain slot of the first deposit in the beacon chain's deposit queue.\\n /// Can be anything if the deposit queue is empty, but zero is a good choice.\\n /// - validatorIndex: The index of the validator of the first pending deposit.\\n /// Can be anything if the deposit queue is empty, but zero is a good choice.\\n /// - pubKeyHash: The hash of the public key of the validator of the first pending deposit.\\n /// Can be anything if the deposit queue is empty, but 32 zero bytes is a good choice\\n /// - pendingDepositPubKeyProof: The merkle proof of the first pending deposit to the beacon block root.\\n /// Can be either:\\n /// * 40 witness hashes for BeaconBlock.state.PendingDeposits[0].pubKey when the deposit queue is not empty.\\n /// * 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\\n /// - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the first pending deposit's validator\\n /// to the beacon block root.\\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n /// - validatorPubKeyProof The merkle proof for the public key of the first pending deposit's validator\\n /// to the this witness hash of withdrawableEpochProof.\\n /// This is 2 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n /// @param balanceProofs a `BalanceProofs` struct containing the following:\\n /// - balancesContainerRoot: The merkle root of the balances container\\n /// - balancesContainerProof: The merkle proof for the balances container to the beacon block root.\\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n /// - validatorBalanceLeaves: Array of leaf nodes containing the validator balance with three other balances.\\n /// - validatorBalanceProofs: Array of merkle proofs for the validator balance to the Balances container root.\\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n // slither-disable-start reentrancy-no-eth\\n function verifyBalances(\\n uint64 validatorVerificationBlockTimestamp,\\n FirstPendingDepositWithdrawableProofData calldata firstPendingDeposit,\\n BalanceProofs calldata balanceProofs\\n ) external {\\n // Load previously snapped balances for the given block root\\n Balances memory balancesMem = snappedBalance;\\n // Check the balances are the latest\\n require(balancesMem.timestamp > 0, \\\"No snapped balances\\\");\\n\\n uint256 verifiedValidatorsCount = verifiedValidators.length;\\n uint256 totalValidatorBalance = 0;\\n\\n // If there are no verified validators then we can skip the balance verification\\n if (verifiedValidatorsCount > 0) {\\n require(\\n balanceProofs.validatorBalanceProofs.length ==\\n verifiedValidatorsCount,\\n \\\"Invalid balance proofs\\\"\\n );\\n require(\\n balanceProofs.validatorBalanceLeaves.length ==\\n verifiedValidatorsCount,\\n \\\"Invalid balance leaves\\\"\\n );\\n // verify beaconBlock.state.balances root to beacon block root\\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\\n balancesMem.blockRoot,\\n balanceProofs.balancesContainerRoot,\\n balanceProofs.balancesContainerProof\\n );\\n\\n // for each validator in reverse order so we can pop off exited validators at the end\\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\\n --i;\\n // verify validator's balance in beaconBlock.state.balances to the\\n // beaconBlock.state.balances container root\\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\\n .verifyValidatorBalance(\\n balanceProofs.balancesContainerRoot,\\n balanceProofs.validatorBalanceLeaves[i],\\n balanceProofs.validatorBalanceProofs[i],\\n validator[verifiedValidators[i]].index\\n );\\n\\n // If the validator balance is zero\\n if (validatorBalanceGwei == 0) {\\n // Store the validator state as exited\\n // This could have been in VERIFIED or EXITING state\\n validator[verifiedValidators[i]].state = ValidatorState\\n .EXITED;\\n\\n // Remove the validator with a zero balance from the list of verified validators\\n\\n // Reduce the count of verified validators which is the last index before the pop removes it.\\n verifiedValidatorsCount -= 1;\\n\\n // Move the last validator that has already been verified to the current index.\\n // There's an extra SSTORE if i is the last active validator but that's fine,\\n // It's not a common case and the code is simpler this way.\\n verifiedValidators[i] = verifiedValidators[\\n verifiedValidatorsCount\\n ];\\n // Delete the last validator from the list\\n verifiedValidators.pop();\\n\\n // The validator balance is zero so not need to add to totalValidatorBalance\\n continue;\\n }\\n\\n // convert Gwei balance to Wei and add to the total validator balance\\n totalValidatorBalance += validatorBalanceGwei * 1 gwei;\\n }\\n }\\n\\n uint256 depositsCount = depositList.length;\\n uint256 totalDepositsWei = 0;\\n\\n // If there are no deposits then we can skip the deposit verification.\\n // This section is after the validator balance verifications so an exited validator will be marked\\n // as EXITED before the deposits are verified. If there was a deposit to an exited validator\\n // then the deposit can only be removed once the validator is fully exited.\\n // It is possible that validator fully exits and a postponed deposit to an exited validator increases\\n // its balance again. In such case the contract will erroneously consider a deposit applied before it\\n // has been applied on the beacon chain showing a smaller than real `totalValidatorBalance`.\\n if (depositsCount > 0) {\\n // Verify the slot of the first pending deposit matches the beacon chain\\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\\n .verifyFirstPendingDeposit(\\n balancesMem.blockRoot,\\n firstPendingDeposit.slot,\\n firstPendingDeposit.pubKeyHash,\\n firstPendingDeposit.pendingDepositPubKeyProof\\n );\\n\\n // If there are no deposits in the beacon chain queue then our deposits must have been processed.\\n // If the deposits have been processed, each deposit will need to be verified with `verifyDeposit`\\n require(!isDepositQueueEmpty, \\\"Deposits have been processed\\\");\\n\\n // The verification of the validator the first pending deposit is for must be on or after when\\n // `snapBalances` was called.\\n require(\\n balancesMem.timestamp <= validatorVerificationBlockTimestamp,\\n \\\"Invalid validator timestamp\\\"\\n );\\n\\n // Verify the validator of the first pending deposit is not exiting by checking\\n // the withdrawable epoch is far into the future.\\n // If it is exiting we can't be sure this deposit has not been postponed in the deposit queue.\\n // Hence we can not verify if the strategy's deposit has been processed or not.\\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\\n // Get the parent beacon block root of the next block which is\\n // the block root of the validator verification slot.\\n // This will revert if the slot after the verification slot was missed.\\n BeaconRoots.parentBlockRoot(\\n validatorVerificationBlockTimestamp\\n ),\\n firstPendingDeposit.validatorIndex,\\n firstPendingDeposit.pubKeyHash,\\n // Validator is not exiting\\n FAR_FUTURE_EPOCH,\\n firstPendingDeposit.withdrawableEpochProof,\\n firstPendingDeposit.validatorPubKeyProof\\n );\\n\\n // solhint-disable max-line-length\\n // If a validator is converted from a sweeping validator to a compounding validator, any balance in excess\\n // of the min 32 ETH is put in the pending deposit queue. Reference:\\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\\n // This will have a slot value of zero unfortunately.\\n // We can not prove the strategy's deposits are still pending with a zero slot value so revert the tx.\\n // Another snapBalances will need to be taken that does not have consolidation deposits at the front of the\\n // beacon chain deposit queue.\\n // solhint-enable max-line-length\\n require(\\n firstPendingDeposit.slot > 0,\\n \\\"Invalid first pending deposit\\\"\\n );\\n\\n // Calculate the epoch at the time of the snapBalances\\n uint64 verificationEpoch = (SafeCast.toUint64(\\n balancesMem.timestamp\\n ) - BEACON_GENESIS_TIMESTAMP) / (SLOT_DURATION * SLOTS_PER_EPOCH);\\n\\n // For each staking strategy's deposits\\n for (uint256 i = 0; i < depositsCount; ++i) {\\n uint256 depositID = depositList[i];\\n DepositData memory depositData = deposits[depositID];\\n\\n // Check the stored deposit is still waiting to be processed on the beacon chain.\\n // That is, the first pending deposit slot is before the slot of the staking strategy's deposit.\\n // If the deposit has been processed, it will need to be verified with `verifyDeposit`.\\n // OR the deposit is to an exiting validator so check it is still not withdrawable.\\n // If the validator is not withdrawable, then the deposit can not have been processed yet.\\n // If the validator is now withdrawable, then the deposit may have been processed. The strategy\\n // now has to wait until the validator's balance is verified to be zero.\\n // OR the validator has exited and the deposit is now verified as processed.\\n require(\\n firstPendingDeposit.slot < depositData.slot ||\\n (verificationEpoch < depositData.withdrawableEpoch &&\\n depositData.withdrawableEpoch !=\\n FAR_FUTURE_EPOCH) ||\\n validator[depositData.pubKeyHash].state ==\\n ValidatorState.EXITED,\\n \\\"Deposit likely processed\\\"\\n );\\n\\n // Remove the deposit if the validator has exited.\\n if (\\n validator[depositData.pubKeyHash].state ==\\n ValidatorState.EXITED\\n ) {\\n _removeDeposit(depositID, depositData);\\n\\n emit DepositValidatorExited(\\n depositID,\\n uint256(depositData.amountGwei) * 1 gwei\\n );\\n\\n // Skip to the next deposit as the deposit amount is now in the strategy's ETH balance\\n continue;\\n }\\n\\n // Convert the deposit amount from Gwei to Wei and add to the total\\n totalDepositsWei += uint256(depositData.amountGwei) * 1 gwei;\\n }\\n }\\n\\n // Store the verified balance in storage\\n lastVerifiedEthBalance =\\n totalDepositsWei +\\n totalValidatorBalance +\\n balancesMem.ethBalance;\\n // Reset the last snap timestamp so a new snapBalances has to be made\\n snappedBalance.timestamp = 0;\\n\\n emit BalancesVerified(\\n balancesMem.timestamp,\\n totalDepositsWei,\\n totalValidatorBalance,\\n balancesMem.ethBalance\\n );\\n }\\n\\n // slither-disable-end reentrancy-no-eth\\n\\n /// @notice Hash a validator public key using the Beacon Chain's format\\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\\n require(pubKey.length == 48, \\\"Invalid public key length\\\");\\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\\n }\\n\\n /**\\n *\\n * WETH and ETH Accounting\\n *\\n */\\n\\n /// @dev Called when WETH is transferred out of the strategy so\\n /// the strategy knows how much WETH it has on deposit.\\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\\n function _transferWeth(uint256 _amount, address _recipient) internal {\\n IERC20(WETH).safeTransfer(_recipient, _amount);\\n\\n // The min is required as more WETH can be withdrawn than deposited\\n // as the strategy earns consensus and execution rewards.\\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\\n depositedWethAccountedFor -= deductAmount;\\n\\n // No change in ETH balance so no need to snapshot the balances\\n }\\n\\n /// @dev Converts ETH to WETH and updates the accounting.\\n /// @param _ethAmount The amount of ETH in wei.\\n function _convertEthToWeth(uint256 _ethAmount) internal {\\n // slither-disable-next-line arbitrary-send-eth\\n IWETH9(WETH).deposit{ value: _ethAmount }();\\n\\n depositedWethAccountedFor += _ethAmount;\\n\\n // Store the reduced ETH balance.\\n // The ETH balance in this strategy contract can be more than the last verified ETH balance\\n // due to partial withdrawals or full exits being processed by the beacon chain since the last snapBalances.\\n // It can also happen from execution rewards (MEV) or ETH donations.\\n lastVerifiedEthBalance -= Math.min(lastVerifiedEthBalance, _ethAmount);\\n\\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\\n snappedBalance.timestamp = 0;\\n }\\n\\n /// @dev Converts WETH to ETH and updates the accounting.\\n /// @param _wethAmount The amount of WETH in wei.\\n function _convertWethToEth(uint256 _wethAmount) internal {\\n IWETH9(WETH).withdraw(_wethAmount);\\n\\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\\n depositedWethAccountedFor -= deductAmount;\\n\\n // Store the increased ETH balance\\n lastVerifiedEthBalance += _wethAmount;\\n\\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\\n snappedBalance.timestamp = 0;\\n }\\n\\n /**\\n *\\n * View Functions\\n *\\n */\\n\\n /// @notice Returns the number of deposits waiting to be verified as processed on the beacon chain,\\n /// or deposits that have been verified to an exiting validator and is now waiting for the\\n /// validator's balance to be swept.\\n function depositListLength() external view returns (uint256) {\\n return depositList.length;\\n }\\n\\n /// @notice Returns the number of verified validators.\\n function verifiedValidatorsLength() external view returns (uint256) {\\n return verifiedValidators.length;\\n }\\n}\\n\",\"keccak256\":\"0x89ed5d6b8b7c383746ab93472a65419513be08cb5d8a6e7ccc5b13be8a0f8e24\",\"license\":\"BUSL-1.1\"},\"contracts/token/OUSD.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title OUSD Token Contract\\n * @dev ERC20 compatible contract for OUSD\\n * @dev Implements an elastic supply\\n * @author Origin Protocol Inc\\n */\\nimport { IVault } from \\\"../interfaces/IVault.sol\\\";\\nimport { Governable } from \\\"../governance/Governable.sol\\\";\\nimport { SafeCast } from \\\"@openzeppelin/contracts/utils/math/SafeCast.sol\\\";\\n\\ncontract OUSD is Governable {\\n using SafeCast for int256;\\n using SafeCast for uint256;\\n\\n /// @dev Event triggered when the supply changes\\n /// @param totalSupply Updated token total supply\\n /// @param rebasingCredits Updated token rebasing credits\\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\\n event TotalSupplyUpdatedHighres(\\n uint256 totalSupply,\\n uint256 rebasingCredits,\\n uint256 rebasingCreditsPerToken\\n );\\n /// @dev Event triggered when an account opts in for rebasing\\n /// @param account Address of the account\\n event AccountRebasingEnabled(address account);\\n /// @dev Event triggered when an account opts out of rebasing\\n /// @param account Address of the account\\n event AccountRebasingDisabled(address account);\\n /// @dev Emitted when `value` tokens are moved from one account `from` to\\n /// another `to`.\\n /// @param from Address of the account tokens are moved from\\n /// @param to Address of the account tokens are moved to\\n /// @param value Amount of tokens transferred\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n /// a call to {approve}. `value` is the new allowance.\\n /// @param owner Address of the owner approving allowance\\n /// @param spender Address of the spender allowance is granted to\\n /// @param value Amount of tokens spender can transfer\\n event Approval(\\n address indexed owner,\\n address indexed spender,\\n uint256 value\\n );\\n /// @dev Yield resulting from {changeSupply} that a `source` account would\\n /// receive is directed to `target` account.\\n /// @param source Address of the source forwarding the yield\\n /// @param target Address of the target receiving the yield\\n event YieldDelegated(address source, address target);\\n /// @dev Yield delegation from `source` account to the `target` account is\\n /// suspended.\\n /// @param source Address of the source suspending yield forwarding\\n /// @param target Address of the target no longer receiving yield from `source`\\n /// account\\n event YieldUndelegated(address source, address target);\\n\\n enum RebaseOptions {\\n NotSet,\\n StdNonRebasing,\\n StdRebasing,\\n YieldDelegationSource,\\n YieldDelegationTarget\\n }\\n\\n uint256[154] private _gap; // Slots to align with deployed contract\\n uint256 private constant MAX_SUPPLY = type(uint128).max;\\n /// @dev The amount of tokens in existence\\n uint256 public totalSupply;\\n mapping(address => mapping(address => uint256)) private allowances;\\n /// @dev The vault with privileges to execute {mint}, {burn}\\n /// and {changeSupply}\\n address public vaultAddress;\\n mapping(address => uint256) internal creditBalances;\\n // the 2 storage variables below need trailing underscores to not name collide with public functions\\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\\n uint256 private rebasingCreditsPerToken_;\\n /// @dev The amount of tokens that are not rebasing - receiving yield\\n uint256 public nonRebasingSupply;\\n mapping(address => uint256) internal alternativeCreditsPerToken;\\n /// @dev A map of all addresses and their respective RebaseOptions\\n mapping(address => RebaseOptions) public rebaseState;\\n mapping(address => uint256) private __deprecated_isUpgraded;\\n /// @dev A map of addresses that have yields forwarded to. This is an\\n /// inverse mapping of {yieldFrom}\\n /// Key Account forwarding yield\\n /// Value Account receiving yield\\n mapping(address => address) public yieldTo;\\n /// @dev A map of addresses that are receiving the yield. This is an\\n /// inverse mapping of {yieldTo}\\n /// Key Account receiving yield\\n /// Value Account forwarding yield\\n mapping(address => address) public yieldFrom;\\n\\n uint256 private constant RESOLUTION_INCREASE = 1e9;\\n uint256[34] private __gap; // including below gap totals up to 200\\n\\n /// @dev Verifies that the caller is the Governor or Strategist.\\n modifier onlyGovernorOrStrategist() {\\n require(\\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\\n \\\"Caller is not the Strategist or Governor\\\"\\n );\\n _;\\n }\\n\\n /// @dev Initializes the contract and sets necessary variables.\\n /// @param _vaultAddress Address of the vault contract\\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\\n external\\n onlyGovernor\\n {\\n require(_vaultAddress != address(0), \\\"Zero vault address\\\");\\n require(vaultAddress == address(0), \\\"Already initialized\\\");\\n\\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\\n vaultAddress = _vaultAddress;\\n }\\n\\n /// @dev Returns the symbol of the token, a shorter version\\n /// of the name.\\n function symbol() external pure virtual returns (string memory) {\\n return \\\"OUSD\\\";\\n }\\n\\n /// @dev Returns the name of the token.\\n function name() external pure virtual returns (string memory) {\\n return \\\"Origin Dollar\\\";\\n }\\n\\n /// @dev Returns the number of decimals used to get its user representation.\\n function decimals() external pure virtual returns (uint8) {\\n return 18;\\n }\\n\\n /**\\n * @dev Verifies that the caller is the Vault contract\\n */\\n modifier onlyVault() {\\n require(vaultAddress == msg.sender, \\\"Caller is not the Vault\\\");\\n _;\\n }\\n\\n /**\\n * @return High resolution rebasingCreditsPerToken\\n */\\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\\n return rebasingCreditsPerToken_;\\n }\\n\\n /**\\n * @return Low resolution rebasingCreditsPerToken\\n */\\n function rebasingCreditsPerToken() external view returns (uint256) {\\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\\n }\\n\\n /**\\n * @return High resolution total number of rebasing credits\\n */\\n function rebasingCreditsHighres() external view returns (uint256) {\\n return rebasingCredits_;\\n }\\n\\n /**\\n * @return Low resolution total number of rebasing credits\\n */\\n function rebasingCredits() external view returns (uint256) {\\n return rebasingCredits_ / RESOLUTION_INCREASE;\\n }\\n\\n /**\\n * @notice Gets the balance of the specified address.\\n * @param _account Address to query the balance of.\\n * @return A uint256 representing the amount of base units owned by the\\n * specified address.\\n */\\n function balanceOf(address _account) public view returns (uint256) {\\n RebaseOptions state = rebaseState[_account];\\n if (state == RebaseOptions.YieldDelegationSource) {\\n // Saves a slot read when transferring to or from a yield delegating source\\n // since we know creditBalances equals the balance.\\n return creditBalances[_account];\\n }\\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\\n _creditsPerToken(_account);\\n if (state == RebaseOptions.YieldDelegationTarget) {\\n // creditBalances of yieldFrom accounts equals token balances\\n return baseBalance - creditBalances[yieldFrom[_account]];\\n }\\n return baseBalance;\\n }\\n\\n /**\\n * @notice Gets the credits balance of the specified address.\\n * @dev Backwards compatible with old low res credits per token.\\n * @param _account The address to query the balance of.\\n * @return (uint256, uint256) Credit balance and credits per token of the\\n * address\\n */\\n function creditsBalanceOf(address _account)\\n external\\n view\\n returns (uint256, uint256)\\n {\\n uint256 cpt = _creditsPerToken(_account);\\n if (cpt == 1e27) {\\n // For a period before the resolution upgrade, we created all new\\n // contract accounts at high resolution. Since they are not changing\\n // as a result of this upgrade, we will return their true values\\n return (creditBalances[_account], cpt);\\n } else {\\n return (\\n creditBalances[_account] / RESOLUTION_INCREASE,\\n cpt / RESOLUTION_INCREASE\\n );\\n }\\n }\\n\\n /**\\n * @notice Gets the credits balance of the specified address.\\n * @param _account The address to query the balance of.\\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\\n * address, and isUpgraded\\n */\\n function creditsBalanceOfHighres(address _account)\\n external\\n view\\n returns (\\n uint256,\\n uint256,\\n bool\\n )\\n {\\n return (\\n creditBalances[_account],\\n _creditsPerToken(_account),\\n true // all accounts have their resolution \\\"upgraded\\\"\\n );\\n }\\n\\n // Backwards compatible view\\n function nonRebasingCreditsPerToken(address _account)\\n external\\n view\\n returns (uint256)\\n {\\n return alternativeCreditsPerToken[_account];\\n }\\n\\n /**\\n * @notice Transfer tokens to a specified address.\\n * @param _to the address to transfer to.\\n * @param _value the amount to be transferred.\\n * @return true on success.\\n */\\n function transfer(address _to, uint256 _value) external returns (bool) {\\n require(_to != address(0), \\\"Transfer to zero address\\\");\\n\\n _executeTransfer(msg.sender, _to, _value);\\n\\n emit Transfer(msg.sender, _to, _value);\\n return true;\\n }\\n\\n /**\\n * @notice Transfer tokens from one address to another.\\n * @param _from The address you want to send tokens from.\\n * @param _to The address you want to transfer to.\\n * @param _value The amount of tokens to be transferred.\\n * @return true on success.\\n */\\n function transferFrom(\\n address _from,\\n address _to,\\n uint256 _value\\n ) external returns (bool) {\\n require(_to != address(0), \\\"Transfer to zero address\\\");\\n uint256 userAllowance = allowances[_from][msg.sender];\\n require(_value <= userAllowance, \\\"Allowance exceeded\\\");\\n\\n unchecked {\\n allowances[_from][msg.sender] = userAllowance - _value;\\n }\\n\\n _executeTransfer(_from, _to, _value);\\n\\n emit Transfer(_from, _to, _value);\\n return true;\\n }\\n\\n function _executeTransfer(\\n address _from,\\n address _to,\\n uint256 _value\\n ) internal {\\n (\\n int256 fromRebasingCreditsDiff,\\n int256 fromNonRebasingSupplyDiff\\n ) = _adjustAccount(_from, -_value.toInt256());\\n (\\n int256 toRebasingCreditsDiff,\\n int256 toNonRebasingSupplyDiff\\n ) = _adjustAccount(_to, _value.toInt256());\\n\\n _adjustGlobals(\\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\\n );\\n }\\n\\n function _adjustAccount(address _account, int256 _balanceChange)\\n internal\\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\\n {\\n RebaseOptions state = rebaseState[_account];\\n int256 currentBalance = balanceOf(_account).toInt256();\\n if (currentBalance + _balanceChange < 0) {\\n revert(\\\"Transfer amount exceeds balance\\\");\\n }\\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\\n\\n if (state == RebaseOptions.YieldDelegationSource) {\\n address target = yieldTo[_account];\\n uint256 targetOldBalance = balanceOf(target);\\n uint256 targetNewCredits = _balanceToRebasingCredits(\\n targetOldBalance + newBalance\\n );\\n rebasingCreditsDiff =\\n targetNewCredits.toInt256() -\\n creditBalances[target].toInt256();\\n\\n creditBalances[_account] = newBalance;\\n creditBalances[target] = targetNewCredits;\\n } else if (state == RebaseOptions.YieldDelegationTarget) {\\n uint256 newCredits = _balanceToRebasingCredits(\\n newBalance + creditBalances[yieldFrom[_account]]\\n );\\n rebasingCreditsDiff =\\n newCredits.toInt256() -\\n creditBalances[_account].toInt256();\\n creditBalances[_account] = newCredits;\\n } else {\\n _autoMigrate(_account);\\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\\n _account\\n ];\\n if (alternativeCreditsPerTokenMem > 0) {\\n nonRebasingSupplyDiff = _balanceChange;\\n if (alternativeCreditsPerTokenMem != 1e18) {\\n alternativeCreditsPerToken[_account] = 1e18;\\n }\\n creditBalances[_account] = newBalance;\\n } else {\\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\\n rebasingCreditsDiff =\\n newCredits.toInt256() -\\n creditBalances[_account].toInt256();\\n creditBalances[_account] = newCredits;\\n }\\n }\\n }\\n\\n function _adjustGlobals(\\n int256 _rebasingCreditsDiff,\\n int256 _nonRebasingSupplyDiff\\n ) internal {\\n if (_rebasingCreditsDiff != 0) {\\n rebasingCredits_ = (rebasingCredits_.toInt256() +\\n _rebasingCreditsDiff).toUint256();\\n }\\n if (_nonRebasingSupplyDiff != 0) {\\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\\n _nonRebasingSupplyDiff).toUint256();\\n }\\n }\\n\\n /**\\n * @notice Function to check the amount of tokens that _owner has allowed\\n * to `_spender`.\\n * @param _owner The address which owns the funds.\\n * @param _spender The address which will spend the funds.\\n * @return The number of tokens still available for the _spender.\\n */\\n function allowance(address _owner, address _spender)\\n external\\n view\\n returns (uint256)\\n {\\n return allowances[_owner][_spender];\\n }\\n\\n /**\\n * @notice Approve the passed address to spend the specified amount of\\n * tokens on behalf of msg.sender.\\n * @param _spender The address which will spend the funds.\\n * @param _value The amount of tokens to be spent.\\n * @return true on success.\\n */\\n function approve(address _spender, uint256 _value) external returns (bool) {\\n allowances[msg.sender][_spender] = _value;\\n emit Approval(msg.sender, _spender, _value);\\n return true;\\n }\\n\\n /**\\n * @notice Creates `_amount` tokens and assigns them to `_account`,\\n * increasing the total supply.\\n */\\n function mint(address _account, uint256 _amount) external onlyVault {\\n require(_account != address(0), \\\"Mint to the zero address\\\");\\n\\n // Account\\n (\\n int256 toRebasingCreditsDiff,\\n int256 toNonRebasingSupplyDiff\\n ) = _adjustAccount(_account, _amount.toInt256());\\n // Globals\\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\\n totalSupply = totalSupply + _amount;\\n\\n require(totalSupply < MAX_SUPPLY, \\\"Max supply\\\");\\n emit Transfer(address(0), _account, _amount);\\n }\\n\\n /**\\n * @notice Destroys `_amount` tokens from `_account`,\\n * reducing the total supply.\\n */\\n function burn(address _account, uint256 _amount) external onlyVault {\\n require(_account != address(0), \\\"Burn from the zero address\\\");\\n if (_amount == 0) {\\n return;\\n }\\n\\n // Account\\n (\\n int256 toRebasingCreditsDiff,\\n int256 toNonRebasingSupplyDiff\\n ) = _adjustAccount(_account, -_amount.toInt256());\\n // Globals\\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\\n totalSupply = totalSupply - _amount;\\n\\n emit Transfer(_account, address(0), _amount);\\n }\\n\\n /**\\n * @dev Get the credits per token for an account. Returns a fixed amount\\n * if the account is non-rebasing.\\n * @param _account Address of the account.\\n */\\n function _creditsPerToken(address _account)\\n internal\\n view\\n returns (uint256)\\n {\\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\\n _account\\n ];\\n if (alternativeCreditsPerTokenMem != 0) {\\n return alternativeCreditsPerTokenMem;\\n } else {\\n return rebasingCreditsPerToken_;\\n }\\n }\\n\\n /**\\n * @dev Auto migrate contracts to be non rebasing,\\n * unless they have opted into yield.\\n * @param _account Address of the account.\\n */\\n function _autoMigrate(address _account) internal {\\n uint256 codeLen = _account.code.length;\\n bool isEOA = (codeLen == 0) ||\\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\\n // In previous code versions, contracts would not have had their\\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\\n // therefore we check the actual accounting used on the account as well.\\n if (\\n (!isEOA) &&\\n rebaseState[_account] == RebaseOptions.NotSet &&\\n alternativeCreditsPerToken[_account] == 0\\n ) {\\n _rebaseOptOut(_account);\\n }\\n }\\n\\n /**\\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\\n * also balance that corresponds to those credits. The latter is important\\n * when adjusting the contract's global nonRebasingSupply to circumvent any\\n * possible rounding errors.\\n *\\n * @param _balance Balance of the account.\\n */\\n function _balanceToRebasingCredits(uint256 _balance)\\n internal\\n view\\n returns (uint256 rebasingCredits)\\n {\\n // Rounds up, because we need to ensure that accounts always have\\n // at least the balance that they should have.\\n // Note this should always be used on an absolute account value,\\n // not on a possibly negative diff, because then the rounding would be wrong.\\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\\n }\\n\\n /**\\n * @notice The calling account will start receiving yield after a successful call.\\n * @param _account Address of the account.\\n */\\n function governanceRebaseOptIn(address _account) external onlyGovernor {\\n require(_account != address(0), \\\"Zero address not allowed\\\");\\n _rebaseOptIn(_account);\\n }\\n\\n /**\\n * @notice The calling account will start receiving yield after a successful call.\\n */\\n function rebaseOptIn() external {\\n _rebaseOptIn(msg.sender);\\n }\\n\\n function _rebaseOptIn(address _account) internal {\\n uint256 balance = balanceOf(_account);\\n\\n // prettier-ignore\\n require(\\n alternativeCreditsPerToken[_account] > 0 ||\\n // Accounts may explicitly `rebaseOptIn` regardless of\\n // accounting if they have a 0 balance.\\n creditBalances[_account] == 0\\n ,\\n \\\"Account must be non-rebasing\\\"\\n );\\n RebaseOptions state = rebaseState[_account];\\n // prettier-ignore\\n require(\\n state == RebaseOptions.StdNonRebasing ||\\n state == RebaseOptions.NotSet,\\n \\\"Only standard non-rebasing accounts can opt in\\\"\\n );\\n\\n uint256 newCredits = _balanceToRebasingCredits(balance);\\n\\n // Account\\n rebaseState[_account] = RebaseOptions.StdRebasing;\\n alternativeCreditsPerToken[_account] = 0;\\n creditBalances[_account] = newCredits;\\n // Globals\\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\\n\\n emit AccountRebasingEnabled(_account);\\n }\\n\\n /**\\n * @notice The calling account will no longer receive yield\\n */\\n function rebaseOptOut() external {\\n _rebaseOptOut(msg.sender);\\n }\\n\\n function _rebaseOptOut(address _account) internal {\\n require(\\n alternativeCreditsPerToken[_account] == 0,\\n \\\"Account must be rebasing\\\"\\n );\\n RebaseOptions state = rebaseState[_account];\\n require(\\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\\n \\\"Only standard rebasing accounts can opt out\\\"\\n );\\n\\n uint256 oldCredits = creditBalances[_account];\\n uint256 balance = balanceOf(_account);\\n\\n // Account\\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\\n alternativeCreditsPerToken[_account] = 1e18;\\n creditBalances[_account] = balance;\\n // Globals\\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\\n\\n emit AccountRebasingDisabled(_account);\\n }\\n\\n /**\\n * @notice Distribute yield to users. This changes the exchange rate\\n * between \\\"credits\\\" and OUSD tokens to change rebasing user's balances.\\n * @param _newTotalSupply New total supply of OUSD.\\n */\\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\\n require(totalSupply > 0, \\\"Cannot increase 0 supply\\\");\\n\\n if (totalSupply == _newTotalSupply) {\\n emit TotalSupplyUpdatedHighres(\\n totalSupply,\\n rebasingCredits_,\\n rebasingCreditsPerToken_\\n );\\n return;\\n }\\n\\n totalSupply = _newTotalSupply > MAX_SUPPLY\\n ? MAX_SUPPLY\\n : _newTotalSupply;\\n\\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\\n // round up in the favour of the protocol\\n rebasingCreditsPerToken_ =\\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\\n rebasingSupply;\\n\\n require(rebasingCreditsPerToken_ > 0, \\\"Invalid change in supply\\\");\\n\\n emit TotalSupplyUpdatedHighres(\\n totalSupply,\\n rebasingCredits_,\\n rebasingCreditsPerToken_\\n );\\n }\\n\\n /*\\n * @notice Send the yield from one account to another account.\\n * Each account keeps its own balances.\\n */\\n function delegateYield(address _from, address _to)\\n external\\n onlyGovernorOrStrategist\\n {\\n require(_from != address(0), \\\"Zero from address not allowed\\\");\\n require(_to != address(0), \\\"Zero to address not allowed\\\");\\n\\n require(_from != _to, \\\"Cannot delegate to self\\\");\\n require(\\n yieldFrom[_to] == address(0) &&\\n yieldTo[_to] == address(0) &&\\n yieldFrom[_from] == address(0) &&\\n yieldTo[_from] == address(0),\\n \\\"Blocked by existing yield delegation\\\"\\n );\\n RebaseOptions stateFrom = rebaseState[_from];\\n RebaseOptions stateTo = rebaseState[_to];\\n\\n require(\\n stateFrom == RebaseOptions.NotSet ||\\n stateFrom == RebaseOptions.StdNonRebasing ||\\n stateFrom == RebaseOptions.StdRebasing,\\n \\\"Invalid rebaseState from\\\"\\n );\\n\\n require(\\n stateTo == RebaseOptions.NotSet ||\\n stateTo == RebaseOptions.StdNonRebasing ||\\n stateTo == RebaseOptions.StdRebasing,\\n \\\"Invalid rebaseState to\\\"\\n );\\n\\n if (alternativeCreditsPerToken[_from] == 0) {\\n _rebaseOptOut(_from);\\n }\\n if (alternativeCreditsPerToken[_to] > 0) {\\n _rebaseOptIn(_to);\\n }\\n\\n uint256 fromBalance = balanceOf(_from);\\n uint256 toBalance = balanceOf(_to);\\n uint256 oldToCredits = creditBalances[_to];\\n uint256 newToCredits = _balanceToRebasingCredits(\\n fromBalance + toBalance\\n );\\n\\n // Set up the bidirectional links\\n yieldTo[_from] = _to;\\n yieldFrom[_to] = _from;\\n\\n // Local\\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\\n alternativeCreditsPerToken[_from] = 1e18;\\n creditBalances[_from] = fromBalance;\\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\\n creditBalances[_to] = newToCredits;\\n\\n // Global\\n int256 creditsChange = newToCredits.toInt256() -\\n oldToCredits.toInt256();\\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\\n emit YieldDelegated(_from, _to);\\n }\\n\\n /*\\n * @notice Stop sending the yield from one account to another account.\\n */\\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\\n // Require a delegation, which will also ensure a valid delegation\\n require(yieldTo[_from] != address(0), \\\"Zero address not allowed\\\");\\n\\n address to = yieldTo[_from];\\n uint256 fromBalance = balanceOf(_from);\\n uint256 toBalance = balanceOf(to);\\n uint256 oldToCredits = creditBalances[to];\\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\\n\\n // Remove the bidirectional links\\n yieldFrom[to] = address(0);\\n yieldTo[_from] = address(0);\\n\\n // Local\\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\\n creditBalances[_from] = fromBalance;\\n rebaseState[to] = RebaseOptions.StdRebasing;\\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\\n creditBalances[to] = newToCredits;\\n\\n // Global\\n int256 creditsChange = newToCredits.toInt256() -\\n oldToCredits.toInt256();\\n _adjustGlobals(creditsChange, fromBalance.toInt256());\\n emit YieldUndelegated(_from, to);\\n }\\n}\\n\",\"keccak256\":\"0x73439bef6569f5adf6f5ce2cb54a5f0d3109d4819457532236e172a7091980a9\",\"license\":\"BUSL-1.1\"},\"contracts/utils/Helpers.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { IBasicToken } from \\\"../interfaces/IBasicToken.sol\\\";\\n\\nlibrary Helpers {\\n /**\\n * @notice Fetch the `symbol()` from an ERC20 token\\n * @dev Grabs the `symbol()` from a contract\\n * @param _token Address of the ERC20 token\\n * @return string Symbol of the ERC20 token\\n */\\n function getSymbol(address _token) internal view returns (string memory) {\\n string memory symbol = IBasicToken(_token).symbol();\\n return symbol;\\n }\\n\\n /**\\n * @notice Fetch the `decimals()` from an ERC20 token\\n * @dev Grabs the `decimals()` from a contract and fails if\\n * the decimal value does not live within a certain range\\n * @param _token Address of the ERC20 token\\n * @return uint256 Decimals of the ERC20 token\\n */\\n function getDecimals(address _token) internal view returns (uint256) {\\n uint256 decimals = IBasicToken(_token).decimals();\\n require(\\n decimals >= 4 && decimals <= 18,\\n \\\"Token must have sufficient decimal places\\\"\\n );\\n\\n return decimals;\\n }\\n}\\n\",\"keccak256\":\"0x4366f8d90b34c1eef8bbaaf369b1e5cd59f04027bb3c111f208eaee65bbc0346\",\"license\":\"BUSL-1.1\"},\"contracts/utils/Initializable.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base contract any contracts that need to initialize state after deployment.\\n * @author Origin Protocol Inc\\n */\\nabstract contract Initializable {\\n /**\\n * @dev Indicates that the contract has been initialized.\\n */\\n bool private initialized;\\n\\n /**\\n * @dev Indicates that the contract is in the process of being initialized.\\n */\\n bool private initializing;\\n\\n /**\\n * @dev Modifier to protect an initializer function from being invoked twice.\\n */\\n modifier initializer() {\\n require(\\n initializing || !initialized,\\n \\\"Initializable: contract is already initialized\\\"\\n );\\n\\n bool isTopLevelCall = !initializing;\\n if (isTopLevelCall) {\\n initializing = true;\\n initialized = true;\\n }\\n\\n _;\\n\\n if (isTopLevelCall) {\\n initializing = false;\\n }\\n }\\n\\n uint256[50] private ______gap;\\n}\\n\",\"keccak256\":\"0x50d39ebf38a3d3111f2b77a6c75ece1d4ae731552fec4697ab16fcf6c0d4d5e8\",\"license\":\"BUSL-1.1\"},\"contracts/utils/InitializableAbstractStrategy.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base contract for vault strategies.\\n * @author Origin Protocol Inc\\n */\\nimport { IERC20 } from \\\"@openzeppelin/contracts/token/ERC20/IERC20.sol\\\";\\nimport { SafeERC20 } from \\\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\\\";\\n\\nimport { Initializable } from \\\"../utils/Initializable.sol\\\";\\nimport { Governable } from \\\"../governance/Governable.sol\\\";\\nimport { IVault } from \\\"../interfaces/IVault.sol\\\";\\n\\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\\n using SafeERC20 for IERC20;\\n\\n event PTokenAdded(address indexed _asset, address _pToken);\\n event PTokenRemoved(address indexed _asset, address _pToken);\\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\\n event RewardTokenCollected(\\n address recipient,\\n address rewardToken,\\n uint256 amount\\n );\\n event RewardTokenAddressesUpdated(\\n address[] _oldAddresses,\\n address[] _newAddresses\\n );\\n event HarvesterAddressesUpdated(\\n address _oldHarvesterAddress,\\n address _newHarvesterAddress\\n );\\n\\n /// @notice Address of the underlying platform\\n address public immutable platformAddress;\\n /// @notice Address of the OToken vault\\n address public immutable vaultAddress;\\n\\n /// @dev Replaced with an immutable variable\\n // slither-disable-next-line constable-states\\n address private _deprecated_platformAddress;\\n\\n /// @dev Replaced with an immutable\\n // slither-disable-next-line constable-states\\n address private _deprecated_vaultAddress;\\n\\n /// @notice asset => pToken (Platform Specific Token Address)\\n mapping(address => address) public assetToPToken;\\n\\n /// @notice Full list of all assets supported by the strategy\\n address[] internal assetsMapped;\\n\\n // Deprecated: Reward token address\\n // slither-disable-next-line constable-states\\n address private _deprecated_rewardTokenAddress;\\n\\n // Deprecated: now resides in Harvester's rewardTokenConfigs\\n // slither-disable-next-line constable-states\\n uint256 private _deprecated_rewardLiquidationThreshold;\\n\\n /// @notice Address of the Harvester contract allowed to collect reward tokens\\n address public harvesterAddress;\\n\\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\\n address[] public rewardTokenAddresses;\\n\\n /* Reserved for future expansion. Used to be 100 storage slots\\n * and has decreased to accommodate:\\n * - harvesterAddress\\n * - rewardTokenAddresses\\n */\\n int256[98] private _reserved;\\n\\n struct BaseStrategyConfig {\\n address platformAddress; // Address of the underlying platform\\n address vaultAddress; // Address of the OToken's Vault\\n }\\n\\n /**\\n * @dev Verifies that the caller is the Governor or Strategist.\\n */\\n modifier onlyGovernorOrStrategist() {\\n require(\\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\\n \\\"Caller is not the Strategist or Governor\\\"\\n );\\n _;\\n }\\n\\n /**\\n * @param _config The platform and OToken vault addresses\\n */\\n constructor(BaseStrategyConfig memory _config) {\\n platformAddress = _config.platformAddress;\\n vaultAddress = _config.vaultAddress;\\n }\\n\\n /**\\n * @dev Internal initialize function, to set up initial internal state\\n * @param _rewardTokenAddresses Address of reward token for platform\\n * @param _assets Addresses of initial supported assets\\n * @param _pTokens Platform Token corresponding addresses\\n */\\n function _initialize(\\n address[] memory _rewardTokenAddresses,\\n address[] memory _assets,\\n address[] memory _pTokens\\n ) internal {\\n rewardTokenAddresses = _rewardTokenAddresses;\\n\\n uint256 assetCount = _assets.length;\\n require(assetCount == _pTokens.length, \\\"Invalid input arrays\\\");\\n for (uint256 i = 0; i < assetCount; ++i) {\\n _setPTokenAddress(_assets[i], _pTokens[i]);\\n }\\n }\\n\\n /**\\n * @notice Collect accumulated reward token and send to Vault.\\n */\\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\\n _collectRewardTokens();\\n }\\n\\n /**\\n * @dev Default implementation that transfers reward tokens to the Harvester\\n * Implementing strategies need to add custom logic to collect the rewards.\\n */\\n function _collectRewardTokens() internal virtual {\\n uint256 rewardTokenCount = rewardTokenAddresses.length;\\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\\n uint256 balance = rewardToken.balanceOf(address(this));\\n if (balance > 0) {\\n emit RewardTokenCollected(\\n harvesterAddress,\\n address(rewardToken),\\n balance\\n );\\n rewardToken.safeTransfer(harvesterAddress, balance);\\n }\\n }\\n }\\n\\n /**\\n * @dev Verifies that the caller is the Vault.\\n */\\n modifier onlyVault() {\\n require(msg.sender == vaultAddress, \\\"Caller is not the Vault\\\");\\n _;\\n }\\n\\n /**\\n * @dev Verifies that the caller is the Harvester.\\n */\\n modifier onlyHarvester() {\\n require(msg.sender == harvesterAddress, \\\"Caller is not the Harvester\\\");\\n _;\\n }\\n\\n /**\\n * @dev Verifies that the caller is the Vault or Governor.\\n */\\n modifier onlyVaultOrGovernor() {\\n require(\\n msg.sender == vaultAddress || msg.sender == governor(),\\n \\\"Caller is not the Vault or Governor\\\"\\n );\\n _;\\n }\\n\\n /**\\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\\n */\\n modifier onlyVaultOrGovernorOrStrategist() {\\n require(\\n msg.sender == vaultAddress ||\\n msg.sender == governor() ||\\n msg.sender == IVault(vaultAddress).strategistAddr(),\\n \\\"Caller is not the Vault, Governor, or Strategist\\\"\\n );\\n _;\\n }\\n\\n /**\\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\\n * @param _rewardTokenAddresses Array of reward token addresses\\n */\\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\\n external\\n onlyGovernor\\n {\\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\\n require(\\n _rewardTokenAddresses[i] != address(0),\\n \\\"Can not set an empty address as a reward token\\\"\\n );\\n }\\n\\n emit RewardTokenAddressesUpdated(\\n rewardTokenAddresses,\\n _rewardTokenAddresses\\n );\\n rewardTokenAddresses = _rewardTokenAddresses;\\n }\\n\\n /**\\n * @notice Get the reward token addresses.\\n * @return address[] the reward token addresses.\\n */\\n function getRewardTokenAddresses()\\n external\\n view\\n returns (address[] memory)\\n {\\n return rewardTokenAddresses;\\n }\\n\\n /**\\n * @notice Provide support for asset by passing its pToken address.\\n * This method can only be called by the system Governor\\n * @param _asset Address for the asset\\n * @param _pToken Address for the corresponding platform token\\n */\\n function setPTokenAddress(address _asset, address _pToken)\\n external\\n virtual\\n onlyGovernor\\n {\\n _setPTokenAddress(_asset, _pToken);\\n }\\n\\n /**\\n * @notice Remove a supported asset by passing its index.\\n * This method can only be called by the system Governor\\n * @param _assetIndex Index of the asset to be removed\\n */\\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\\n require(_assetIndex < assetsMapped.length, \\\"Invalid index\\\");\\n address asset = assetsMapped[_assetIndex];\\n address pToken = assetToPToken[asset];\\n\\n if (_assetIndex < assetsMapped.length - 1) {\\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\\n }\\n assetsMapped.pop();\\n assetToPToken[asset] = address(0);\\n\\n emit PTokenRemoved(asset, pToken);\\n }\\n\\n /**\\n * @notice Provide support for asset by passing its pToken address.\\n * Add to internal mappings and execute the platform specific,\\n * abstract method `_abstractSetPToken`\\n * @param _asset Address for the asset\\n * @param _pToken Address for the corresponding platform token\\n */\\n function _setPTokenAddress(address _asset, address _pToken) internal {\\n require(assetToPToken[_asset] == address(0), \\\"pToken already set\\\");\\n require(\\n _asset != address(0) && _pToken != address(0),\\n \\\"Invalid addresses\\\"\\n );\\n\\n assetToPToken[_asset] = _pToken;\\n assetsMapped.push(_asset);\\n\\n emit PTokenAdded(_asset, _pToken);\\n\\n _abstractSetPToken(_asset, _pToken);\\n }\\n\\n /**\\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\\n * strategy contracts, i.e. mistaken sends.\\n * @param _asset Address for the asset\\n * @param _amount Amount of the asset to transfer\\n */\\n function transferToken(address _asset, uint256 _amount)\\n public\\n virtual\\n onlyGovernor\\n {\\n require(!supportsAsset(_asset), \\\"Cannot transfer supported asset\\\");\\n IERC20(_asset).safeTransfer(governor(), _amount);\\n }\\n\\n /**\\n * @notice Set the Harvester contract that can collect rewards.\\n * @param _harvesterAddress Address of the harvester contract.\\n */\\n function setHarvesterAddress(address _harvesterAddress)\\n external\\n onlyGovernor\\n {\\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\\n harvesterAddress = _harvesterAddress;\\n }\\n\\n /***************************************\\n Abstract\\n ****************************************/\\n\\n function _abstractSetPToken(address _asset, address _pToken)\\n internal\\n virtual;\\n\\n function safeApproveAllTokens() external virtual;\\n\\n /**\\n * @notice Deposit an amount of assets into the platform\\n * @param _asset Address for the asset\\n * @param _amount Units of asset to deposit\\n */\\n function deposit(address _asset, uint256 _amount) external virtual;\\n\\n /**\\n * @notice Deposit all supported assets in this strategy contract to the platform\\n */\\n function depositAll() external virtual;\\n\\n /**\\n * @notice Withdraw an `amount` of assets from the platform and\\n * send to the `_recipient`.\\n * @param _recipient Address to which the asset should be sent\\n * @param _asset Address of the asset\\n * @param _amount Units of asset to withdraw\\n */\\n function withdraw(\\n address _recipient,\\n address _asset,\\n uint256 _amount\\n ) external virtual;\\n\\n /**\\n * @notice Withdraw all supported assets from platform and\\n * sends to the OToken's Vault.\\n */\\n function withdrawAll() external virtual;\\n\\n /**\\n * @notice Get the total asset value held in the platform.\\n * This includes any interest that was generated since depositing.\\n * @param _asset Address of the asset\\n * @return balance Total value of the asset in the platform\\n */\\n function checkBalance(address _asset)\\n external\\n view\\n virtual\\n returns (uint256 balance);\\n\\n /**\\n * @notice Check if an asset is supported.\\n * @param _asset Address of the asset\\n * @return bool Whether asset is supported\\n */\\n function supportsAsset(address _asset) public view virtual returns (bool);\\n}\\n\",\"keccak256\":\"0x0160d435384d75e8764f4a916764ba47c87fda46872ca5900d46e5e80e956ff9\",\"license\":\"BUSL-1.1\"},\"contracts/vault/VaultStorage.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title OToken VaultStorage contract\\n * @notice The VaultStorage contract defines the storage for the Vault contracts\\n * @author Origin Protocol Inc\\n */\\n\\nimport { IERC20 } from \\\"@openzeppelin/contracts/token/ERC20/IERC20.sol\\\";\\nimport { SafeERC20 } from \\\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\\\";\\nimport { Address } from \\\"@openzeppelin/contracts/utils/Address.sol\\\";\\n\\nimport { IStrategy } from \\\"../interfaces/IStrategy.sol\\\";\\nimport { Governable } from \\\"../governance/Governable.sol\\\";\\nimport { OUSD } from \\\"../token/OUSD.sol\\\";\\nimport { Initializable } from \\\"../utils/Initializable.sol\\\";\\nimport \\\"../utils/Helpers.sol\\\";\\n\\ncontract VaultStorage is Initializable, Governable {\\n using SafeERC20 for IERC20;\\n\\n event AssetSupported(address _asset);\\n event AssetRemoved(address _asset);\\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\\n event StrategyApproved(address _addr);\\n event StrategyRemoved(address _addr);\\n event Mint(address _addr, uint256 _value);\\n event Redeem(address _addr, uint256 _value);\\n event CapitalPaused();\\n event CapitalUnpaused();\\n event RebasePaused();\\n event RebaseUnpaused();\\n event VaultBufferUpdated(uint256 _vaultBuffer);\\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\\n event PriceProviderUpdated(address _priceProvider);\\n event AllocateThresholdUpdated(uint256 _threshold);\\n event RebaseThresholdUpdated(uint256 _threshold);\\n event StrategistUpdated(address _address);\\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\\n event TrusteeFeeBpsChanged(uint256 _basis);\\n event TrusteeAddressChanged(address _address);\\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\\n event SwapperChanged(address _address);\\n event SwapAllowedUndervalueChanged(uint256 _basis);\\n event SwapSlippageChanged(address _asset, uint256 _basis);\\n event Swapped(\\n address indexed _fromAsset,\\n address indexed _toAsset,\\n uint256 _fromAssetAmount,\\n uint256 _toAssetAmount\\n );\\n event StrategyAddedToMintWhitelist(address indexed strategy);\\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\\n event DripDurationChanged(uint256 dripDuration);\\n event WithdrawalRequested(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount,\\n uint256 _queued\\n );\\n event WithdrawalClaimed(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount\\n );\\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\\n\\n // Since we are proxy, all state should be uninitalized.\\n // Since this storage contract does not have logic directly on it\\n // we should not be checking for to see if these variables can be constant.\\n // slither-disable-start uninitialized-state\\n // slither-disable-start constable-states\\n\\n // Assets supported by the Vault, i.e. Stablecoins\\n enum UnitConversion {\\n DECIMALS,\\n GETEXCHANGERATE\\n }\\n // Changed to fit into a single storage slot so the decimals needs to be recached\\n struct Asset {\\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\\n // redeeming or checking balance of assets.\\n bool isSupported;\\n UnitConversion unitConversion;\\n uint8 decimals;\\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\\n // For example 40 == 0.4% slippage\\n uint16 allowedOracleSlippageBps;\\n }\\n\\n /// @dev mapping of supported vault assets to their configuration\\n mapping(address => Asset) internal assets;\\n /// @dev list of all assets supported by the vault.\\n address[] internal allAssets;\\n\\n // Strategies approved for use by the Vault\\n struct Strategy {\\n bool isSupported;\\n uint256 _deprecated; // Deprecated storage slot\\n }\\n /// @dev mapping of strategy contracts to their configuration\\n mapping(address => Strategy) public strategies;\\n /// @dev list of all vault strategies\\n address[] internal allStrategies;\\n\\n /// @notice Address of the Oracle price provider contract\\n address public priceProvider;\\n /// @notice pause rebasing if true\\n bool public rebasePaused;\\n /// @notice pause operations that change the OToken supply.\\n /// eg mint, redeem, allocate, mint/burn for strategy\\n bool public capitalPaused;\\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\\n uint256 public redeemFeeBps;\\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\\n uint256 public vaultBuffer;\\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\\n uint256 public autoAllocateThreshold;\\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\\n uint256 public rebaseThreshold;\\n\\n /// @dev Address of the OToken token. eg OUSD or OETH.\\n OUSD public oUSD;\\n\\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\\n // keccak256(\\\"OUSD.vault.governor.admin.impl\\\");\\n bytes32 public constant adminImplPosition =\\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\\n\\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\\n address private _deprecated_rebaseHooksAddr = address(0);\\n\\n /// @dev Deprecated: Address of Uniswap\\n address private _deprecated_uniswapAddr = address(0);\\n\\n /// @notice Address of the Strategist\\n address public strategistAddr = address(0);\\n\\n /// @notice Mapping of asset address to the Strategy that they should automatically\\n // be allocated to\\n mapping(address => address) public assetDefaultStrategies;\\n\\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\\n uint256 public maxSupplyDiff;\\n\\n /// @notice Trustee contract that can collect a percentage of yield\\n address public trusteeAddress;\\n\\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\\n uint256 public trusteeFeeBps;\\n\\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\\n address[] private _deprecated_swapTokens;\\n\\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\\n\\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\\n\\n address public ousdMetaStrategy;\\n\\n /// @notice How much OTokens are currently minted by the strategy\\n int256 public netOusdMintedForStrategy;\\n\\n /// @notice How much net total OTokens are allowed to be minted by all strategies\\n uint256 public netOusdMintForStrategyThreshold;\\n\\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\\n\\n /// @notice Collateral swap configuration.\\n /// @dev is packed into a single storage slot to save gas.\\n struct SwapConfig {\\n // Contract that swaps the vault's collateral assets\\n address swapper;\\n // Max allowed percentage the total value can drop below the total supply in basis points.\\n // For example 100 == 1%\\n uint16 allowedUndervalueBps;\\n }\\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\\n\\n // List of strategies that can mint oTokens directly\\n // Used in OETHBaseVaultCore\\n mapping(address => bool) public isMintWhitelistedStrategy;\\n\\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\\n address private _deprecated_dripper;\\n\\n /// Withdrawal Queue Storage /////\\n\\n struct WithdrawalQueueMetadata {\\n // cumulative total of all withdrawal requests included the ones that have already been claimed\\n uint128 queued;\\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\\n uint128 claimable;\\n // total of all the requests that have been claimed\\n uint128 claimed;\\n // index of the next withdrawal request starting at 0\\n uint128 nextWithdrawalIndex;\\n }\\n\\n /// @notice Global metadata for the withdrawal queue including:\\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\\n /// claimed - total of all the requests that have been claimed\\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\\n\\n struct WithdrawalRequest {\\n address withdrawer;\\n bool claimed;\\n uint40 timestamp; // timestamp of the withdrawal request\\n // Amount of oTokens to redeem. eg OETH\\n uint128 amount;\\n // cumulative total of all withdrawal requests including this one.\\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\\n uint128 queued;\\n }\\n\\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\\n\\n /// @notice Sets a minimum delay that is required to elapse between\\n /// requesting async withdrawals and claiming the request.\\n /// When set to 0 async withdrawals are disabled.\\n uint256 public withdrawalClaimDelay;\\n\\n /// @notice Time in seconds that the vault last rebased yield.\\n uint64 public lastRebase;\\n\\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\\n uint64 public dripDuration;\\n\\n /// @notice max rebase percentage per second\\n /// Can be used to set maximum yield of the protocol,\\n /// spreading out yield over time\\n uint64 public rebasePerSecondMax;\\n\\n /// @notice target rebase rate limit, based on past rates and funds available.\\n uint64 public rebasePerSecondTarget;\\n\\n uint256 internal constant MAX_REBASE = 0.02 ether;\\n uint256 internal constant MAX_REBASE_PER_SECOND =\\n uint256(0.05 ether) / 1 days;\\n\\n // For future use\\n uint256[43] private __gap;\\n\\n // slither-disable-end constable-states\\n // slither-disable-end uninitialized-state\\n\\n /**\\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\\n * @param newImpl address of the implementation\\n */\\n function setAdminImpl(address newImpl) external onlyGovernor {\\n require(\\n Address.isContract(newImpl),\\n \\\"new implementation is not a contract\\\"\\n );\\n bytes32 position = adminImplPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newImpl)\\n }\\n }\\n}\\n\",\"keccak256\":\"0xe8c1056879e4d67e0085a30a525a4cb23b954ade0f22fce502278f35b9c69d3b\",\"license\":\"BUSL-1.1\"}},\"version\":1}", + "bytecode": "0x6101a060405234801561001157600080fd5b50604051616143380380616143833981016040819052610030916101a2565b60208701516001600160a01b0380881660805280851660a05280861660c05280821660e0528316610100526001600160401b03821661012081905288918891869088908790879042116100c95760405162461bcd60e51b815260206004820152601960248201527f496e76616c69642067656e657369732074696d657374616d7000000000000000604482015260640160405180910390fd5b505084516001600160a01b0390811661014052602090950151851661016052505050508516610180526100fc6000610108565b5050505050505061027f565b6001600160a01b0381166101286000805160206161238339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a360008051602061612383398151915255565b80516001600160a01b038116811461018657600080fd5b919050565b80516001600160401b038116811461018657600080fd5b60008060008060008060008789036101008112156101bf57600080fd5b60408112156101cd57600080fd5b50604080519081016001600160401b03811182821017156101fe57634e487b7160e01b600052604160045260246000fd5b60405261020a8961016f565b815261021860208a0161016f565b6020820152965061022b6040890161016f565b95506102396060890161016f565b94506102476080890161016f565b935061025560a0890161016f565b925061026360c0890161016f565b915061027160e0890161018b565b905092959891949750929550565b60805160a05160c05160e0516101005161012051610140516101605161018051615d3f6103e460003960008181610389015261346e0152600081816104e5015281816118870152818161223c015281816123b4015281816138ce01526139b201526000610b400152600081816107ef015281816115a401528181612a9601526144390152600081816106ab01528181610d6801528181612525015281816125ca015281816127fd0152818161295c0152818161314601526131ea0152600061074c01526000818161079b0152818161214d0152818161343e01528181613519015261369f015260008181610aa101526117930152600081816109030152818161095a01528181610c49015281816111da015281816118fc01528181611d6f01528181611dd30152818161232a0152818161394301528181613a3d01528181613ae801528181613ebe015281816143ea015281816147d701526148a40152615d3f6000f3fe6080604052600436106103395760003560e01c80638bd87652116101ab578063b02c43d0116100f7578063d059f6ef11610095578063d9caed121161006f578063d9caed1214610b0e578063dbe55e5614610b2e578063de5f626814610b62578063f6ca71b014610b7757600080fd5b8063d059f6ef14610ac3578063d38bfff414610ad9578063d79e403214610af957600080fd5b8063bb1b918d116100d1578063bb1b918d14610a3a578063c2e1e3f414610a5a578063c7af335214610a7a578063cceab75014610a8f57600080fd5b8063b02c43d01461097c578063b6e2b520146109fa578063b8ec667814610a1a57600080fd5b806396d538bb11610164578063a5f5be541161013e578063a5f5be54146108b5578063aa388af6146108e6578063ad1728cb14610933578063ad5c46481461094857600080fd5b806396d538bb1461082957806398245f1b146108495780639c6155a31461089557600080fd5b80638bd87652146107025780639092c31c1461073a5780639136616a1461076e578063916497511461078957806394e5cbd6146107bd57806396998732146107dd57600080fd5b80634c84e6f8116102855780636874469d116102235780637b2d9b2c116101fd5780637b2d9b2c146106795780637da9982a14610699578063853828b6146106cd57806387bae867146106e257600080fd5b80636874469d146106245780636e811d381461063957806371a735f31461065957600080fd5b80635a063f631161025f5780635a063f63146105ba5780635d36b190146105cf5780635f515226146105e457806367c7066c1461060457600080fd5b80634c84e6f81461057c578063522e42451461059157806359ff4158146105a457600080fd5b80631e2adaad116102f2578063435356d1116102cc578063435356d1146105075780634583ef101461052757806347e7ef24146105475780634896b31a1461056757600080fd5b80631e2adaad1461045157806325e2e9f314610471578063430bf08a146104d357600080fd5b80630c340a24146103455780630df1ecfd146103775780630ed57b3a146103ab5780630ef99855146103cd5780630fc3b4c4146103fb5780631072cbea1461043157600080fd5b3661034057005b600080fd5b34801561035157600080fd5b5061035a610b99565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561038357600080fd5b5061035a7f000000000000000000000000000000000000000000000000000000000000000081565b3480156103b757600080fd5b506103cb6103c6366004614ae7565b610bb6565b005b3480156103d957600080fd5b506103ed6103e8366004614b1a565b610bfa565b60405190815260200161036e565b34801561040757600080fd5b5061035a610416366004614b33565b6071602052600090815260409020546001600160a01b031681565b34801561043d57600080fd5b506103cb61044c366004614b4e565b610c1b565b34801561045d57600080fd5b506103cb61046c366004614bec565b610cda565b34801561047d57600080fd5b50603954603a546104a791906001600160401b03811690600160401b90046001600160801b031683565b604080519384526001600160401b0390921660208401526001600160801b03169082015260600161036e565b3480156104df57600080fd5b5061035a7f000000000000000000000000000000000000000000000000000000000000000081565b34801561051357600080fd5b506103cb610522366004614d47565b611092565b34801561053357600080fd5b506103cb610542366004614dd8565b61117f565b34801561055357600080fd5b506103cb610562366004614b4e565b61187c565b34801561057357600080fd5b506036546103ed565b34801561058857600080fd5b506103cb6119f9565b6103cb61059f366004614e21565b611aa1565b3480156105b057600080fd5b506103ed603b5481565b3480156105c657600080fd5b506103cb611c26565b3480156105db57600080fd5b506103cb611cc5565b3480156105f057600080fd5b506103ed6105ff366004614b33565b611d6b565b34801561061057600080fd5b5060755461035a906001600160a01b031681565b34801561063057600080fd5b506103cb611e59565b34801561064557600080fd5b506103cb610654366004614b33565b611f9e565b34801561066557600080fd5b506103cb610674366004614ed0565b61200c565b34801561068557600080fd5b5061035a610694366004614b1a565b612207565b3480156106a557600080fd5b5061035a7f000000000000000000000000000000000000000000000000000000000000000081565b3480156106d957600080fd5b506103cb612231565b3480156106ee57600080fd5b5060335461035a906001600160a01b031681565b34801561070e57600080fd5b50603454610722906001600160801b031681565b6040516001600160801b03909116815260200161036e565b34801561074657600080fd5b5061035a7f000000000000000000000000000000000000000000000000000000000000000081565b34801561077a57600080fd5b506103cb6103c6366004614b1a565b34801561079557600080fd5b5061035a7f000000000000000000000000000000000000000000000000000000000000000081565b3480156107c957600080fd5b506103cb6107d8366004614f55565b6123e4565b3480156107e957600080fd5b506108117f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160401b03909116815260200161036e565b34801561083557600080fd5b506103cb610844366004614fd8565b612def565b34801561085557600080fd5b50610887610864366004614b1a565b60386020526000908152604090205460ff811690610100900464ffffffffff1682565b60405161036e92919061502f565b3480156108a157600080fd5b506103cb6108b036600461506b565b612f06565b3480156108c157600080fd5b506033546108d690600160a01b900460ff1681565b604051901515815260200161036e565b3480156108f257600080fd5b506108d6610901366004614b33565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811691161490565b34801561093f57600080fd5b506103cb613427565b34801561095457600080fd5b5061035a7f000000000000000000000000000000000000000000000000000000000000000081565b34801561098857600080fd5b506109e8610997366004614b1a565b603560205260009081526040902080546001909101546001600160401b0380821691600160401b810482169163ffffffff600160801b8304169160ff600160a01b82041691600160a81b9091041686565b60405161036e969594939291906150ec565b348015610a0657600080fd5b506103cb610a153660046151f3565b6134de565b348015610a2657600080fd5b506103ed610a35366004614b1a565b613589565b348015610a4657600080fd5b506103cb610a553660046152a1565b613599565b348015610a6657600080fd5b506103cb610a75366004614b33565b613761565b348015610a8657600080fd5b506108d66137ee565b348015610a9b57600080fd5b5061035a7f000000000000000000000000000000000000000000000000000000000000000081565b348015610acf57600080fd5b506103ed603c5481565b348015610ae557600080fd5b506103cb610af4366004614b33565b61381f565b348015610b0557600080fd5b506037546103ed565b348015610b1a57600080fd5b506103cb610b2936600461535f565b6138c3565b348015610b3a57600080fd5b5061035a7f000000000000000000000000000000000000000000000000000000000000000081565b348015610b6e57600080fd5b506103cb6139a7565b348015610b8357600080fd5b50610b8c613b3e565b60405161036e919061539c565b6000610bb1600080516020615cea8339815191525490565b905090565b60405162461bcd60e51b81526020600482015260146024820152732ab739bab83837b93a32b210333ab731ba34b7b760611b60448201526064015b60405180910390fd5b60378181548110610c0a57600080fd5b600091825260209091200154905081565b610c236137ee565b610c3f5760405162461bcd60e51b8152600401610bf1906153e8565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811690831603610cba5760405162461bcd60e51b815260206004820152601f60248201527f43616e6e6f74207472616e7366657220737570706f72746564206173736574006044820152606401610bf1565b610cd6610cc5610b99565b6001600160a01b0384169083613ba0565b5050565b600260008581526038602052604090205460ff166007811115610cff57610cff615019565b14610d435760405162461bcd60e51b815260206004820152601460248201527315985b1a59185d1bdc881b9bdd081cdd185ad95960621b6044820152606401610bf1565b6000610d4e87613bf7565b604051630afd62ef60e41b81529091506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063afd62ef090610da79084908990889088908d908c90600401615448565b60006040518083038186803b158015610dbf57600080fd5b505afa158015610dd3573d6000803e3d6000fd5b50505050604051806040016040528060036007811115610df557610df5615019565b815264ffffffffff8816602091820152600087815260389091526040902081518154829060ff19166001836007811115610e3157610e31615019565b021790555060209190910151815464ffffffffff9091166101000265ffffffffff00199091161790556001600160a01b0384163014611015576000858152603860205260408120805460ff19166007179055603654905b81811015610fe25760006035600060368481548110610ea957610ea9615492565b600091825260208083209091015483528281019390935260409182019020815160c0810183528154815260018201546001600160401b0380821695830195909552600160401b81049094169281019290925263ffffffff600160801b84041660608301529091608083019060ff600160a01b909104166002811115610f3057610f30615019565b6002811115610f4157610f41615019565b815260019190910154600160a81b90046001600160401b03166020909101528051909150889003610fd957610f94603b5482602001516001600160401b0316633b9aca00610f8f91906154be565b613cfc565b603b6000828254610fa591906154d5565b92505081905550610fd360368381548110610fc257610fc2615492565b906000526020600020015482613d14565b50610fe2565b50600101610e88565b5060405186907fb8318df57b70f6381fb18aaf762e33efa2cc92627aae83d417f6710e1415d8d890600090a2505061108a565b6037805460018101825560009182527f42a7b7dd785cd69714a189dffb3fd7d7174edc9ece837694ce50f7078f7c31ae018690556033805460ff60a01b1916905560405164ffffffffff88169187917f8142f1367675d1a37dc1aa31258c38b05f5348de55b799764472d94ccb4a71f49190a3505b505050505050565b61109a6137ee565b6110b65760405162461bcd60e51b8152600401610bf1906153e8565b600054610100900460ff16806110cf575060005460ff16155b6111325760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610bf1565b600054610100900460ff16158015611154576000805461ffff19166101011790555b61115f848484613df5565b611167613427565b8015611179576000805461ff00191690555b50505050565b6033546001600160a01b031633146111a95760405162461bcd60e51b8152600401610bf1906154e8565b60006111c26001600160401b038316633b9aca006154be565b6040516370a0823160e01b81523060048201529091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611229573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061124d9190615511565b8111156112905760405162461bcd60e51b8152602060048201526011602482015270092dce6eaccccd2c6d2cadce840ae8aa89607b1b6044820152606401610bf1565b603654600c116112d15760405162461bcd60e51b815260206004820152600c60248201526b4d6178206465706f7369747360a01b6044820152606401610bf1565b6112da81613ea8565b60006113236112e9858061552a565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613f7792505050565b60008181526038602052604090205490915060ff16600181600781111561134c5761134c615019565b14806113695750600381600781111561136757611367615019565b145b6113b55760405162461bcd60e51b815260206004820152601a60248201527f4e6f742072656769737465726564206f722076657269666965640000000000006044820152606401610bf1565b670de0b6b3a76400008310156114015760405162461bcd60e51b815260206004820152601160248201527011195c1bdcda5d081d1bdbc81cdb585b1b607a1b6044820152606401610bf1565b600181600781111561141557611415615019565b0361152857603354600160a01b900460ff161561146d5760405162461bcd60e51b8152602060048201526016602482015275115e1a5cdd1a5b99c8199a5c9cdd0819195c1bdcda5d60521b6044820152606401610bf1565b6801bc16d674ec80000083146114c55760405162461bcd60e51b815260206004820152601c60248201527f496e76616c6964206669727374206465706f73697420616d6f756e74000000006044820152606401610bf1565b6037546030906114d6906001615570565b106115145760405162461bcd60e51b815260206004820152600e60248201526d4d61782076616c696461746f727360901b6044820152606401610bf1565b6033805460ff60a01b1916600160a01b1790555b60408051600160f91b60208201526000602182018190526bffffffffffffffffffffffff193060601b16602c830152910160408051601f198184030181529190529050600182600781111561157f5761157f615019565b0361159e576000838152603860205260409020805460ff191660021790555b6000600c7f00000000000000000000000000000000000000000000000000000000000000006115cc4261403b565b6115d69190615583565b6115e091906155a2565b603480549192506000916001600160801b031690826115fe836155de565b91906101000a8154816001600160801b0302191690836001600160801b031602179055506001600160801b031690506040518060c00160405280868152602001886001600160401b03168152602001836001600160401b0316815260200161166a6036805490506140a7565b63ffffffff168152602001600181526001600160401b036020918201819052600084815260358352604090819020845181559284015160018401805492860151606087015163ffffffff16600160801b0263ffffffff60801b19918616600160401b026001600160801b03199095169390951692909217929092179081168317825560808501519260ff60a01b191664ffffffffff60801b1990911617600160a01b83600281111561171e5761171e615019565b021790555060a09190910151600191820180546001600160401b03909216600160a81b0267ffffffffffffffff60a81b199092169190911790556036805491820181556000527f4a11f94e20a93c79f6ec743a1954ec4fc2c08429ae2122118bf234b2185c81b8018190556001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166322895118876117c38b8061552a565b876117d160208f018f61552a565b8f604001356040518863ffffffff1660e01b81526004016117f796959493929190615659565b6000604051808303818588803b15801561181057600080fd5b505af1158015611824573d6000803e3d6000fd5b508493508892507fe6b13456d345a251702a9dab19a68b9e3550781d95fa9faf104a06ff8bc30495915061185a90508b8061552a565b8a60405161186a939291906156a8565b60405180910390a35050505050505050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146118c45760405162461bcd60e51b8152600401610bf1906156cc565b600080516020615cca833981519152805460011981016118f65760405162461bcd60e51b8152600401610bf190615703565b600282557f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b03161461194b5760405162461bcd60e51b8152600401610bf19061572b565b600083116119945760405162461bcd60e51b81526020600482015260166024820152754d757374206465706f73697420736f6d657468696e6760501b6044820152606401610bf1565b82603c60008282546119a69190615570565b90915550506040805160008152602081018590526001600160a01b038616917f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f62910160405180910390a250600190555050565b611a016137ee565b611a1d5760405162461bcd60e51b8152600401610bf1906153e8565b603354600160a01b900460ff16611a695760405162461bcd60e51b815260206004820152601060248201526f139bc8199a5c9cdd0819195c1bdcda5d60821b6044820152606401610bf1565b6033805460ff60a01b191690556040517fce77f85e30b0e6df0d12527ddf038f900fdeda0eeda4284c52be47b05de31a9790600090a1565b6033546001600160a01b03163314611acb5760405162461bcd60e51b8152600401610bf1906154e8565b6000611b0c84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613f7792505050565b60008181526038602052604090205490915060ff166003816007811115611b3557611b35615019565b1480611b5257506004816007811115611b5057611b50615019565b145b611b9e5760405162461bcd60e51b815260206004820152601e60248201527f56616c696461746f72206e6f742076657269666965642f65786974696e6700006044820152606401610bf1565b826001600160401b0316600003611bc9576000828152603860205260409020805460ff191660041790555b611bd485858561410c565b50817f8dd83105dbd4263d41c76e5d414905babdd3f035bd2031f6ce8895715595979c611c0e6001600160401b038616633b9aca006154be565b60405190815260200160405180910390a25050505050565b6075546001600160a01b03163314611c805760405162461bcd60e51b815260206004820152601b60248201527f43616c6c6572206973206e6f74207468652048617276657374657200000000006044820152606401610bf1565b600080516020615cca83398151915280546001198101611cb25760405162461bcd60e51b8152600401610bf190615703565b60028255611cbe610bb6565b5060019055565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b031614611d605760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b6064820152608401610bf1565b611d6933614251565b565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b031614611dbe5760405162461bcd60e51b8152600401610bf19061572b565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611e22573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e469190615511565b603b54611e539190615570565b92915050565b6000611e644261403b565b90506001600160401b038116611e7c600c6023615756565b603a54611e9291906001600160401b031661577f565b6001600160401b031610611ed85760405162461bcd60e51b815260206004820152600d60248201526c29b730b8103a37b79039b7b7b760991b6044820152606401610bf1565b6000611ee382613bf7565b905060004790506040518060600160405280838152602001846001600160401b03168152602001611f13836142b0565b6001600160801b0390811690915281516039556020820151603a8054604094850151909316600160401b026001600160c01b03199093166001600160401b03909216919091179190911790555182907fb7523e03ed4a74718427c422a01fee1138835adb5bd592240f30bd8b5e1b929a90611f919084815260200190565b60405180910390a2505050565b611fa66137ee565b611fc25760405162461bcd60e51b8152600401610bf1906153e8565b603380546001600160a01b0319166001600160a01b0383169081179091556040517f83f29c79feb71f8fba9d0fbc4ba5f0982a28b6b1e868b3fc50e6400d100bca0f90600090a250565b6033546001600160a01b031633146120365760405162461bcd60e51b8152600401610bf1906154e8565b600061207786868080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613f7792505050565b60008181526038602052604090205490915060ff1660018160078111156120a0576120a0615019565b14806120bd575060058160078111156120bb576120bb615019565b145b806120d9575060078160078111156120d7576120d7615019565b145b6121255760405162461bcd60e51b815260206004820152601c60248201527f56616c696461746f72206e6f742072656764206f7220657869746564000000006044820152606401610bf1565b60008281526038602052604090819020805460ff19166006179055516312b3fc1960e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906312b3fc1990612192908a908a908a908a908a9060040161584b565b600060405180830381600087803b1580156121ac57600080fd5b505af11580156121c0573d6000803e3d6000fd5b50505050817f63d54ea43f163d6e28fc23abec67eb7c3294e7e6f0620955a73cd8d17c7367f486866040516121f692919061588c565b60405180910390a250505050505050565b6076818154811061221757600080fd5b6000918252602090912001546001600160a01b0316905081565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161480612280575061226b610b99565b6001600160a01b0316336001600160a01b0316145b6122d85760405162461bcd60e51b815260206004820152602360248201527f43616c6c6572206973206e6f7420746865205661756c74206f7220476f7665726044820152623737b960e91b6064820152608401610bf1565b600080516020615cca8339815191528054600119810161230a5760405162461bcd60e51b8152600401610bf190615703565b600282556040516370a0823160e01b8152306004820152479060009082907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015612379573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061239d9190615511565b6123a79190615570565b905080156123da576123da7f00000000000000000000000000000000000000000000000000000000000000008284614319565b5050600182555050565b604080516060810182526039548152603a546001600160401b03811660208301819052600160401b9091046001600160801b031692820192909252906124625760405162461bcd60e51b81526020600482015260136024820152724e6f20736e61707065642062616c616e63657360681b6044820152606401610bf1565b603754600081156127e3578161247b60608601866158a0565b9050146124c35760405162461bcd60e51b8152602060048201526016602482015275496e76616c69642062616c616e63652070726f6f667360501b6044820152606401610bf1565b816124d160408601866158a0565b9050146125195760405162461bcd60e51b8152602060048201526016602482015275496e76616c69642062616c616e6365206c656176657360501b6044820152606401610bf1565b82516001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906391ad640d90863561255b602089018961552a565b6040518563ffffffff1660e01b815260040161257a94939291906158e9565b60006040518083038186803b15801561259257600080fd5b505afa1580156125a6573d6000803e3d6000fd5b5084925050505b80156127e1576125bc81615909565b905060006001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663f34ad34c87356125fe60408a018a6158a0565b8681811061260e5761260e615492565b9050602002013589806060019061262591906158a0565b8781811061263557612635615492565b9050602002810190612647919061552a565b6038600060378a8154811061265e5761265e615492565b9060005260206000200154815260200190815260200160002060000160019054906101000a900464ffffffffff166040518663ffffffff1660e01b81526004016126ac959493929190615920565b602060405180830381865afa1580156126c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126ed9190615511565b9050806000036127c1576005603860006037858154811061271057612710615492565b600091825260208083209091015483528201929092526040019020805460ff1916600183600781111561274557612745615019565b02179055506127556001856154d5565b93506037848154811061276a5761276a615492565b90600052602060002001546037838154811061278857612788615492565b60009182526020909120015560378054806127a5576127a5615959565b60019003818190600052602060002001600090559055506125ad565b6127cf81633b9aca006154be565b6127d99084615570565b9250506125ad565b505b60365460008115612d535784516000906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690637383a9fc9061283160208c018c61596f565b60408c013561284360608e018e61552a565b6040518663ffffffff1660e01b815260040161286395949392919061598a565b602060405180830381865afa158015612880573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128a491906159b9565b905080156128f45760405162461bcd60e51b815260206004820152601c60248201527f4465706f736974732068617665206265656e2070726f636573736564000000006044820152606401610bf1565b886001600160401b031686602001516001600160401b0316111561295a5760405162461bcd60e51b815260206004820152601b60248201527f496e76616c69642076616c696461746f722074696d657374616d7000000000006044820152606401610bf1565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b3421d0f6129928b613bf7565b6129a260408c0160208d016159d6565b60408c01356001600160401b036129bc60808f018f61552a565b8f8060a001906129cc919061552a565b6040518963ffffffff1660e01b81526004016129ef9897969594939291906159f1565b60006040518083038186803b158015612a0757600080fd5b505afa158015612a1b573d6000803e3d6000fd5b5060009250612a3091505060208a018a61596f565b6001600160401b031611612a865760405162461bcd60e51b815260206004820152601d60248201527f496e76616c69642066697273742070656e64696e67206465706f7369740000006044820152606401610bf1565b6000612a946020600c615756565b7f0000000000000000000000000000000000000000000000000000000000000000612acb89602001516001600160401b031661403b565b612ad59190615583565b612adf91906155a2565b905060005b84811015612d4f57600060368281548110612b0157612b01615492565b6000918252602080832090910154808352603582526040808420815160c0810183528154815260018201546001600160401b0380821696830196909652600160401b81049095169281019290925263ffffffff600160801b850416606083015291945091608083019060ff600160a01b909104166002811115612b8657612b86615019565b6002811115612b9757612b97615019565b8152600191909101546001600160401b03600160a81b909104811660209283015260408301519293509190911690612bd1908e018e61596f565b6001600160401b03161080612c1457508060a001516001600160401b0316846001600160401b0316108015612c14575060a08101516001600160401b0390811614155b80612c4357506005815160009081526038602052604090205460ff166007811115612c4157612c41615019565b145b612c8f5760405162461bcd60e51b815260206004820152601860248201527f4465706f736974206c696b656c792070726f63657373656400000000000000006044820152606401610bf1565b6005815160009081526038602052604090205460ff166007811115612cb657612cb6615019565b03612d1c57612cc58282613d14565b817fd37dfd28ce3c6abccf25dc583376c1fe84be5abe9cd4b8dc94904b25d62a784582602001516001600160401b0316633b9aca00612d0491906154be565b60405190815260200160405180910390a25050612d47565b6020810151612d38906001600160401b0316633b9aca006154be565b612d429087615570565b955050505b600101612ae4565b5050505b60408501516001600160801b0316612d6b8483615570565b612d759190615570565b603b55603a805467ffffffffffffffff1916905560208581015160408088015181518581529384018790526001600160801b0316908301526001600160401b0316907fed2528338eefb63fd1860078b91e35106bc25e3fd528634d180f662582fe5ec1906060015b60405180910390a25050505050505050565b612df76137ee565b612e135760405162461bcd60e51b8152600401610bf1906153e8565b8060005b81811015612ebd576000848483818110612e3357612e33615492565b9050602002016020810190612e489190614b33565b6001600160a01b031603612eb55760405162461bcd60e51b815260206004820152602e60248201527f43616e206e6f742073657420616e20656d70747920616464726573732061732060448201526d30903932bbb0b932103a37b5b2b760911b6064820152608401610bf1565b600101612e17565b507f04c0b9649497d316554306e53678d5f5f5dbc3a06f97dec13ff4cfe98b986bbc60768484604051612ef293929190615a4f565b60405180910390a161117960768484614a02565b6000848152603560209081526040808320815160c0810183528154815260018201546001600160401b0380821695830195909552600160401b810490941692810192909252600160801b830463ffffffff16606083015290916080830190600160a01b900460ff166002811115612f7f57612f7f615019565b6002811115612f9057612f90615019565b815260019190910154600160a81b90046001600160401b03166020918201528151600090815260389091526040808220815180830190925280549394509192909190829060ff166007811115612fe857612fe8615019565b6007811115612ff957612ff9615019565b81529054610100900464ffffffffff16602090910152905060018260800151600281111561302957613029615019565b1461306c5760405162461bcd60e51b81526020600482015260136024820152724465706f736974206e6f742070656e64696e6760681b6044820152606401610bf1565b60038151600781111561308157613081615019565b146130c75760405162461bcd60e51b815260206004820152601660248201527515985b1a59185d1bdc881b9bdd081d995c9a599a595960521b6044820152606401610bf1565b846001600160401b031682604001516001600160401b0316106131255760405162461bcd60e51b815260206004820152601660248201527514db1bdd081b9bdd0818599d195c8819195c1bdcda5d60521b6044820152606401610bf1565b600061313861313387614433565b613bf7565b905060006001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663d98a55648361317960208a018a61596f565b61318660208b018b61552a565b6040518563ffffffff1660e01b81526004016131a59493929190615ae8565b602060405180830381865afa1580156131c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131e691906159b9565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663334a88fb838560200151886000016020810190613230919061596f565b61323d60208b018b61552a565b6040518663ffffffff1660e01b815260040161325d959493929190615b11565b60006040518083038186803b15801561327557600080fd5b505afa158015613289573d6000803e3d6000fd5b506001600160401b0392506132a4915050602087018761596f565b6001600160401b031614613357576132bf602086018661596f565b6001600160401b0390811660a0860152602085015189917fbc3fdc080055a5d0342452fd2f970db3142457507168345b18a9621e5510dafa916133079116633b9aca006154be565b613314602089018961596f565b604080519283526001600160401b0390911660208301520160405180910390a2505090516000908152603860205260409020805460ff1916600417905550611179565b613364602087018761596f565b6001600160401b031684604001516001600160401b031610806133845750805b6133d05760405162461bcd60e51b815260206004820152601c60248201527f4465706f736974206c696b656c79206e6f742070726f636573736564000000006044820152606401610bf1565b6133da8885613d14565b877fcc6c22fbaf9f36d51193b29b3114d27c22dee0b6616975e3a1e0cf67022633ae85602001516001600160401b0316633b9aca0061341991906154be565b604051908152602001612ddd565b60405163095ea7b360e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015260001960248301527f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b3906044016020604051808303816000875af11580156134b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134db91906159b9565b50565b6134e66137ee565b6135025760405162461bcd60e51b8152600401610bf1906153e8565b604051631a1b9a0b60e21b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063686e682c9061355290869086908690600401615b47565b600060405180830381600087803b15801561356c57600080fd5b505af1158015613580573d6000803e3d6000fd5b50505050505050565b60368181548110610c0a57600080fd5b6033546001600160a01b031633146135c35760405162461bcd60e51b8152600401610bf1906154e8565b600061360489898080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613f7792505050565b90506000808281526038602052604090205460ff16600781111561362a5761362a615019565b146136775760405162461bcd60e51b815260206004820152601c60248201527f56616c696461746f7220616c72656164792072656769737465726564000000006044820152606401610bf1565b60008181526038602052604090819020805460ff19166001179055516301ba3ee760e21b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906306e8fb9c906136ea908c908c908c908c908c908c908c908c90600401615be9565b600060405180830381600087803b15801561370457600080fd5b505af1158015613718573d6000803e3d6000fd5b50505050807f50837f89f5e75ae0a7bcc858f53ea15fa398dc007fd52cbfe4683ae9a6c2d722888860405161374e92919061588c565b60405180910390a2505050505050505050565b6137696137ee565b6137855760405162461bcd60e51b8152600401610bf1906153e8565b607554604080516001600160a01b03928316815291831660208301527fe48386b84419f4d36e0f96c10cc3510b6fb1a33795620c5098b22472bbe90796910160405180910390a1607580546001600160a01b0319166001600160a01b0392909216919091179055565b6000613806600080516020615cea8339815191525490565b6001600160a01b0316336001600160a01b031614905090565b6138276137ee565b6138435760405162461bcd60e51b8152600401610bf1906153e8565b61386b817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b031661388b600080516020615cea8339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461390b5760405162461bcd60e51b8152600401610bf1906156cc565b600080516020615cca8339815191528054600119810161393d5760405162461bcd60e51b8152600401610bf190615703565b600282557f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b0316146139925760405162461bcd60e51b8152600401610bf19061572b565b61399d858447614319565b5060019055505050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146139ef5760405162461bcd60e51b8152600401610bf1906156cc565b600080516020615cca83398151915280546001198101613a215760405162461bcd60e51b8152600401610bf190615703565b600282556040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015613a8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ab09190615511565b90506000603c5482613ac291906154d5565b905080156123da57603c8290556040805160008152602081018390526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016917f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f62910160405180910390a25050600182555050565b60606076805480602002602001604051908101604052809291908181526020018280548015613b9657602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613b78575b5050505050905090565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052613bf2908490614476565b505050565b604080516001600160401b038316602082015260009182918291720f3df6d732807ef1319fb7b8bb8522d0beac02910160408051601f1981840301815290829052613c4191615c4a565b600060405180830381855afa9150503d8060008114613c7c576040519150601f19603f3d011682016040523d82523d6000602084013e613c81565b606091505b5091509150818015613c94575060008151115b613ce05760405162461bcd60e51b815260206004820152601860248201527f496e76616c696420626561636f6e2074696d657374616d7000000000000000006044820152606401610bf1565b80806020019051810190613cf49190615511565b949350505050565b6000818310613d0b5781613d0d565b825b9392505050565b60008281526035602052604081206001908101805460ff60a01b1916600160a11b179055603680549091613d47916154d5565b81548110613d5757613d57615492565b90600052602060002001549050806036836060015163ffffffff1681548110613d8257613d82615492565b60009182526020808320909101929092556060840151838252603590925260409020600101805463ffffffff909216600160801b0263ffffffff60801b199092169190911790556036805480613dda57613dda615959565b60019003818190600052602060002001600090559055505050565b8251613e08906076906020860190614a61565b50815181518114613e525760405162461bcd60e51b8152602060048201526014602482015273496e76616c696420696e7075742061727261797360601b6044820152606401610bf1565b60005b81811015613ea157613e99848281518110613e7257613e72615492565b6020026020010151848381518110613e8c57613e8c615492565b6020026020010151614548565b600101613e55565b5050505050565b604051632e1a7d4d60e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b158015613f0a57600080fd5b505af1158015613f1e573d6000803e3d6000fd5b505050506000613f3082603c54613cfc565b905080603c6000828254613f4491906154d5565b9250508190555081603b6000828254613f5d9190615570565b9091555050603a805467ffffffffffffffff191690555050565b60008151603014613fca5760405162461bcd60e51b815260206004820152601960248201527f496e76616c6964207075626c6963206b6579206c656e677468000000000000006044820152606401610bf1565b604051600290613fe1908490600090602001615c66565b60408051601f1981840301815290829052613ffb91615c4a565b602060405180830381855afa158015614018573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190611e539190615511565b60006001600160401b038211156140a35760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203660448201526534206269747360d01b6064820152608401610bf1565b5090565b600063ffffffff8211156140a35760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201526532206269747360d01b6064820152608401610bf1565b60006030831461415e5760405162461bcd60e51b815260206004820152601d60248201527f496e76616c69642076616c696461746f722062797465206c656e6774680000006044820152606401610bf1565b6141666146a7565b90506000710961ef480eb55e80d19ad83579a64c0070026001600160a01b03168286868660405160200161419c93929190615c95565b60408051601f19818403018152908290526141b691615c4a565b60006040518083038185875af1925050503d80600081146141f3576040519150601f19603f3d011682016040523d82523d6000602084013e6141f8565b606091505b50509050806142495760405162461bcd60e51b815260206004820152601960248201527f5769746864726177616c2072657175657374206661696c6564000000000000006044820152606401610bf1565b509392505050565b6001600160a01b0381166142a75760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f7220697320616464726573732830290000000000006044820152606401610bf1565b6134db8161476e565b60006001600160801b038211156140a35760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316044820152663238206269747360c81b6064820152608401610bf1565b600082116143695760405162461bcd60e51b815260206004820152601760248201527f4d75737420776974686472617720736f6d657468696e670000000000000000006044820152606401610bf1565b6001600160a01b0383166143b85760405162461bcd60e51b8152602060048201526016602482015275135d5cdd081cdc1958da599e481c9958da5c1a595b9d60521b6044820152606401610bf1565b80156143c7576143c7816147d5565b6143d18284614897565b6040805160008152602081018490526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016917f2717ead6b9200dd235aad468c9809ea400fe33ac69b5bfaa6d3e90fc922b63989101611f91565b6000600c7f00000000000000000000000000000000000000000000000000000000000000006144628483615756565b61446c919061577f565b611e53919061577f565b60006144cb826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166148f79092919063ffffffff16565b805190915015613bf257808060200190518101906144e991906159b9565b613bf25760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610bf1565b6001600160a01b0382811660009081526071602052604090205416156145a55760405162461bcd60e51b81526020600482015260126024820152711c151bdad95b88185b1c9958591e481cd95d60721b6044820152606401610bf1565b6001600160a01b038216158015906145c557506001600160a01b03811615155b6146055760405162461bcd60e51b8152602060048201526011602482015270496e76616c69642061646472657373657360781b6044820152606401610bf1565b6001600160a01b03828116600081815260716020908152604080832080549587166001600160a01b031996871681179091556072805460018101825594527fdffbd64cc7c1a7eb27984335d9416d51137a03d3fabec7141025c62663253fe190930180549095168417909455925190815290917fef6485b84315f9b1483beffa32aae9a0596890395e3d7521f1c5fbb51790e765910160405180910390a25050565b60405160009081908190710961ef480eb55e80d19ad83579a64c0070029082818181855afa9150503d80600081146146fb576040519150601f19603f3d011682016040523d82523d6000602084013e614700565b606091505b5091509150818015614713575060008151115b6147535760405162461bcd60e51b81526020600482015260116024820152704661696c656420746f206765742066656560781b6044820152606401610bf1565b808060200190518101906147679190615511565b9250505090565b806001600160a01b031661478e600080516020615cea8339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a3600080516020615cea83398151915255565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561483057600080fd5b505af1158015614844573d6000803e3d6000fd5b505050505080603c600082825461485b9190615570565b9091555050603b5461486d9082613cfc565b603b600082825461487e91906154d5565b9091555050603a805467ffffffffffffffff1916905550565b6148cb6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168284613ba0565b60006148d983603c54613cfc565b905080603c60008282546148ed91906154d5565b9091555050505050565b6060613cf4848460008585843b6149505760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610bf1565b600080866001600160a01b0316858760405161496c9190615c4a565b60006040518083038185875af1925050503d80600081146149a9576040519150601f19603f3d011682016040523d82523d6000602084013e6149ae565b606091505b50915091506149be8282866149c9565b979650505050505050565b606083156149d8575081613d0d565b8251156149e85782518084602001fd5b8160405162461bcd60e51b8152600401610bf19190615cb6565b828054828255906000526020600020908101928215614a55579160200282015b82811115614a555781546001600160a01b0319166001600160a01b03843516178255602090920191600190910190614a22565b506140a3929150614ab6565b828054828255906000526020600020908101928215614a55579160200282015b82811115614a5557825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190614a81565b5b808211156140a35760008155600101614ab7565b80356001600160a01b0381168114614ae257600080fd5b919050565b60008060408385031215614afa57600080fd5b614b0383614acb565b9150614b1160208401614acb565b90509250929050565b600060208284031215614b2c57600080fd5b5035919050565b600060208284031215614b4557600080fd5b613d0d82614acb565b60008060408385031215614b6157600080fd5b614b6a83614acb565b946020939093013593505050565b80356001600160401b0381168114614ae257600080fd5b803564ffffffffff81168114614ae257600080fd5b60008083601f840112614bb657600080fd5b5081356001600160401b03811115614bcd57600080fd5b602083019150836020828501011115614be557600080fd5b9250929050565b60008060008060008060a08789031215614c0557600080fd5b614c0e87614b78565b9550614c1c60208801614b8f565b945060408701359350614c3160608801614acb565b925060808701356001600160401b03811115614c4c57600080fd5b614c5889828a01614ba4565b979a9699509497509295939492505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715614ca857614ca8614c6a565b604052919050565b60006001600160401b03821115614cc957614cc9614c6a565b5060051b60200190565b600082601f830112614ce457600080fd5b8135614cf7614cf282614cb0565b614c80565b8082825260208201915060208360051b860101925085831115614d1957600080fd5b602085015b83811015614d3d57614d2f81614acb565b835260209283019201614d1e565b5095945050505050565b600080600060608486031215614d5c57600080fd5b83356001600160401b03811115614d7257600080fd5b614d7e86828701614cd3565b93505060208401356001600160401b03811115614d9a57600080fd5b614da686828701614cd3565b92505060408401356001600160401b03811115614dc257600080fd5b614dce86828701614cd3565b9150509250925092565b60008060408385031215614deb57600080fd5b82356001600160401b03811115614e0157600080fd5b830160608186031215614e1357600080fd5b9150614b1160208401614b78565b600080600060408486031215614e3657600080fd5b83356001600160401b03811115614e4c57600080fd5b614e5886828701614ba4565b9094509250614e6b905060208501614b78565b90509250925092565b60008083601f840112614e8657600080fd5b5081356001600160401b03811115614e9d57600080fd5b6020830191508360208260051b8501011115614be557600080fd5b600060a08284031215614eca57600080fd5b50919050565b600080600080600060e08688031215614ee857600080fd5b85356001600160401b03811115614efe57600080fd5b614f0a88828901614ba4565b90965094505060208601356001600160401b03811115614f2957600080fd5b614f3588828901614e74565b9094509250614f4990508760408801614eb8565b90509295509295909350565b600080600060608486031215614f6a57600080fd5b614f7384614b78565b925060208401356001600160401b03811115614f8e57600080fd5b840160c08187031215614fa057600080fd5b915060408401356001600160401b03811115614fbb57600080fd5b840160808187031215614fcd57600080fd5b809150509250925092565b60008060208385031215614feb57600080fd5b82356001600160401b0381111561500157600080fd5b61500d85828601614e74565b90969095509350505050565b634e487b7160e01b600052602160045260246000fd5b604081016008841061504357615043615019565b92815264ffffffffff9190911660209091015290565b600060408284031215614eca57600080fd5b6000806000806080858703121561508157600080fd5b8435935061509160208601614b78565b925060408501356001600160401b038111156150ac57600080fd5b6150b887828801615059565b92505060608501356001600160401b038111156150d457600080fd5b6150e087828801615059565b91505092959194509250565b8681526001600160401b0386811660208301528516604082015263ffffffff8416606082015260c081016003841061512657615126615019565b8360808301526001600160401b03831660a0830152979650505050505050565b803563ffffffff81168114614ae257600080fd5b80151581146134db57600080fd5b600060a0828403121561517a57600080fd5b60405160a081016001600160401b038111828210171561519c5761519c614c6a565b6040529050806151ab83615146565b81526151b960208401614b78565b60208201526151ca60408401614b78565b604082015260608301356151dd8161515a565b6060820152608092830135920191909152919050565b600080600060e0848603121561520857600080fd5b83356001600160401b0381111561521e57600080fd5b8401601f8101861361522f57600080fd5b803561523d614cf282614cb0565b8082825260208201915060208360051b85010192508883111561525f57600080fd5b6020840193505b828410156152885761527784614b78565b825260209384019390910190615266565b955050505060208401359150614e6b8560408601615168565b600080600080600080600080610120898b0312156152be57600080fd5b88356001600160401b038111156152d457600080fd5b6152e08b828c01614ba4565b90995097505060208901356001600160401b038111156152ff57600080fd5b61530b8b828c01614e74565b90975095505060408901356001600160401b0381111561532a57600080fd5b6153368b828c01614ba4565b909550935050606089013591506153508a60808b01614eb8565b90509295985092959890939650565b60008060006060848603121561537457600080fd5b61537d84614acb565b925061538b60208501614acb565b929592945050506040919091013590565b602080825282518282018190526000918401906040840190835b818110156153dd5783516001600160a01b03168352602093840193909201916001016153b6565b509095945050505050565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b86815285602082015260a06040820152600061546860a08301868861541f565b64ffffffffff949094166060830152506001600160a01b0391909116608090910152949350505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417611e5357611e536154a8565b81810381811115611e5357611e536154a8565b6020808252600f908201526e2737ba102932b3b4b9ba3930ba37b960891b604082015260600190565b60006020828403121561552357600080fd5b5051919050565b6000808335601e1984360301811261554157600080fd5b8301803591506001600160401b0382111561555b57600080fd5b602001915036819003821315614be557600080fd5b80820180821115611e5357611e536154a8565b6001600160401b038281168282160390811115611e5357611e536154a8565b60006001600160401b038316806155c957634e487b7160e01b600052601260045260246000fd5b806001600160401b0384160491505092915050565b60006001600160801b0382166001600160801b038103615600576156006154a8565b60010192915050565b60005b8381101561562457818101518382015260200161560c565b50506000910152565b60008151808452615645816020860160208601615609565b601f01601f19169290920160200192915050565b60808152600061566d60808301888a61541f565b828103602084015261567f818861562d565b9050828103604084015261569481868861541f565b915050826060830152979650505050505050565b6040815260006156bc60408301858761541f565b9050826020830152949350505050565b60208082526017908201527f43616c6c6572206973206e6f7420746865205661756c74000000000000000000604082015260600190565b6020808252600e908201526d1499595b9d1c985b9d0818d85b1b60921b604082015260600190565b602080825260119082015270155b9cdd5c1c1bdc9d195908185cdcd95d607a1b604082015260600190565b6001600160401b038181168382160290811690818114615778576157786154a8565b5092915050565b6001600160401b038181168382160190811115611e5357611e536154a8565b81835260208301925060008160005b848110156157dc576001600160401b036157c683614b78565b16865260209586019591909101906001016157ad565b5093949350505050565b63ffffffff6157f482615146565b1682526001600160401b0361580b60208301614b78565b1660208301526001600160401b0361582560408301614b78565b16604083015260608101356158398161515a565b15156060830152608090810135910152565b60e08152600061585f60e08301878961541f565b828103602084015261587281868861579e565b91505061588260408301846157e6565b9695505050505050565b602081526000613cf460208301848661579e565b6000808335601e198436030181126158b757600080fd5b8301803591506001600160401b038211156158d157600080fd5b6020019150600581901b3603821315614be557600080fd5b84815283602082015260606040820152600061588260608301848661541f565b600081615918576159186154a8565b506000190190565b85815284602082015260806040820152600061594060808301858761541f565b905064ffffffffff831660608301529695505050505050565b634e487b7160e01b600052603160045260246000fd5b60006020828403121561598157600080fd5b613d0d82614b78565b8581526001600160401b03851660208201528360408201526080606082015260006149be60808301848661541f565b6000602082840312156159cb57600080fd5b8151613d0d8161515a565b6000602082840312156159e857600080fd5b613d0d82614b8f565b88815264ffffffffff881660208201528660408201526001600160401b038616606082015260c060808201526000615a2d60c08301868861541f565b82810360a0840152615a4081858761541f565b9b9a5050505050505050505050565b6040808252845490820181905260008581526020812090916060840190835b81811015615a955783546001600160a01b0316835260019384019360209093019201615a6e565b50508381036020808601919091528582520190508460005b85811015615adc576001600160a01b03615ac683614acb565b1683526020928301929190910190600101615aad565b50909695505050505050565b8481526001600160401b038416602082015260606040820152600061588260608301848661541f565b85815264ffffffffff851660208201526001600160401b03841660408201526080606082015260006149be60808301848661541f565b60e080825284519082018190526000906020860190610100840190835b81811015615b8b5783516001600160401b0316835260209384019390920191600101615b64565b5050809250505083602083015263ffffffff83511660408301526001600160401b0360208401511660608301526001600160401b0360408401511660808301526060830151151560a0830152608083015160c0830152949350505050565b61012081526000615bff61012083018a8c61541f565b8281036020840152615c1281898b61579e565b90508281036040840152615c2781878961541f565b915050836060830152615c3d60808301846157e6565b9998505050505050505050565b60008251615c5c818460208701615609565b9190910192915050565b60008351615c78818460208801615609565b6001600160801b0319939093169190920190815260100192915050565b8284823760c09190911b6001600160c01b0319169101908152600801919050565b602081526000613d0d602083018461562d56fe53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac45357bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa264697066735822122076a0f28f409c5b21fe38d516dbb2b9162dc16ebd26f719328b5a99a559a7c5a964736f6c634300081c00337bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a", + "deployedBytecode": "0x6080604052600436106103395760003560e01c80638bd87652116101ab578063b02c43d0116100f7578063d059f6ef11610095578063d9caed121161006f578063d9caed1214610b0e578063dbe55e5614610b2e578063de5f626814610b62578063f6ca71b014610b7757600080fd5b8063d059f6ef14610ac3578063d38bfff414610ad9578063d79e403214610af957600080fd5b8063bb1b918d116100d1578063bb1b918d14610a3a578063c2e1e3f414610a5a578063c7af335214610a7a578063cceab75014610a8f57600080fd5b8063b02c43d01461097c578063b6e2b520146109fa578063b8ec667814610a1a57600080fd5b806396d538bb11610164578063a5f5be541161013e578063a5f5be54146108b5578063aa388af6146108e6578063ad1728cb14610933578063ad5c46481461094857600080fd5b806396d538bb1461082957806398245f1b146108495780639c6155a31461089557600080fd5b80638bd87652146107025780639092c31c1461073a5780639136616a1461076e578063916497511461078957806394e5cbd6146107bd57806396998732146107dd57600080fd5b80634c84e6f8116102855780636874469d116102235780637b2d9b2c116101fd5780637b2d9b2c146106795780637da9982a14610699578063853828b6146106cd57806387bae867146106e257600080fd5b80636874469d146106245780636e811d381461063957806371a735f31461065957600080fd5b80635a063f631161025f5780635a063f63146105ba5780635d36b190146105cf5780635f515226146105e457806367c7066c1461060457600080fd5b80634c84e6f81461057c578063522e42451461059157806359ff4158146105a457600080fd5b80631e2adaad116102f2578063435356d1116102cc578063435356d1146105075780634583ef101461052757806347e7ef24146105475780634896b31a1461056757600080fd5b80631e2adaad1461045157806325e2e9f314610471578063430bf08a146104d357600080fd5b80630c340a24146103455780630df1ecfd146103775780630ed57b3a146103ab5780630ef99855146103cd5780630fc3b4c4146103fb5780631072cbea1461043157600080fd5b3661034057005b600080fd5b34801561035157600080fd5b5061035a610b99565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561038357600080fd5b5061035a7f000000000000000000000000000000000000000000000000000000000000000081565b3480156103b757600080fd5b506103cb6103c6366004614ae7565b610bb6565b005b3480156103d957600080fd5b506103ed6103e8366004614b1a565b610bfa565b60405190815260200161036e565b34801561040757600080fd5b5061035a610416366004614b33565b6071602052600090815260409020546001600160a01b031681565b34801561043d57600080fd5b506103cb61044c366004614b4e565b610c1b565b34801561045d57600080fd5b506103cb61046c366004614bec565b610cda565b34801561047d57600080fd5b50603954603a546104a791906001600160401b03811690600160401b90046001600160801b031683565b604080519384526001600160401b0390921660208401526001600160801b03169082015260600161036e565b3480156104df57600080fd5b5061035a7f000000000000000000000000000000000000000000000000000000000000000081565b34801561051357600080fd5b506103cb610522366004614d47565b611092565b34801561053357600080fd5b506103cb610542366004614dd8565b61117f565b34801561055357600080fd5b506103cb610562366004614b4e565b61187c565b34801561057357600080fd5b506036546103ed565b34801561058857600080fd5b506103cb6119f9565b6103cb61059f366004614e21565b611aa1565b3480156105b057600080fd5b506103ed603b5481565b3480156105c657600080fd5b506103cb611c26565b3480156105db57600080fd5b506103cb611cc5565b3480156105f057600080fd5b506103ed6105ff366004614b33565b611d6b565b34801561061057600080fd5b5060755461035a906001600160a01b031681565b34801561063057600080fd5b506103cb611e59565b34801561064557600080fd5b506103cb610654366004614b33565b611f9e565b34801561066557600080fd5b506103cb610674366004614ed0565b61200c565b34801561068557600080fd5b5061035a610694366004614b1a565b612207565b3480156106a557600080fd5b5061035a7f000000000000000000000000000000000000000000000000000000000000000081565b3480156106d957600080fd5b506103cb612231565b3480156106ee57600080fd5b5060335461035a906001600160a01b031681565b34801561070e57600080fd5b50603454610722906001600160801b031681565b6040516001600160801b03909116815260200161036e565b34801561074657600080fd5b5061035a7f000000000000000000000000000000000000000000000000000000000000000081565b34801561077a57600080fd5b506103cb6103c6366004614b1a565b34801561079557600080fd5b5061035a7f000000000000000000000000000000000000000000000000000000000000000081565b3480156107c957600080fd5b506103cb6107d8366004614f55565b6123e4565b3480156107e957600080fd5b506108117f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160401b03909116815260200161036e565b34801561083557600080fd5b506103cb610844366004614fd8565b612def565b34801561085557600080fd5b50610887610864366004614b1a565b60386020526000908152604090205460ff811690610100900464ffffffffff1682565b60405161036e92919061502f565b3480156108a157600080fd5b506103cb6108b036600461506b565b612f06565b3480156108c157600080fd5b506033546108d690600160a01b900460ff1681565b604051901515815260200161036e565b3480156108f257600080fd5b506108d6610901366004614b33565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811691161490565b34801561093f57600080fd5b506103cb613427565b34801561095457600080fd5b5061035a7f000000000000000000000000000000000000000000000000000000000000000081565b34801561098857600080fd5b506109e8610997366004614b1a565b603560205260009081526040902080546001909101546001600160401b0380821691600160401b810482169163ffffffff600160801b8304169160ff600160a01b82041691600160a81b9091041686565b60405161036e969594939291906150ec565b348015610a0657600080fd5b506103cb610a153660046151f3565b6134de565b348015610a2657600080fd5b506103ed610a35366004614b1a565b613589565b348015610a4657600080fd5b506103cb610a553660046152a1565b613599565b348015610a6657600080fd5b506103cb610a75366004614b33565b613761565b348015610a8657600080fd5b506108d66137ee565b348015610a9b57600080fd5b5061035a7f000000000000000000000000000000000000000000000000000000000000000081565b348015610acf57600080fd5b506103ed603c5481565b348015610ae557600080fd5b506103cb610af4366004614b33565b61381f565b348015610b0557600080fd5b506037546103ed565b348015610b1a57600080fd5b506103cb610b2936600461535f565b6138c3565b348015610b3a57600080fd5b5061035a7f000000000000000000000000000000000000000000000000000000000000000081565b348015610b6e57600080fd5b506103cb6139a7565b348015610b8357600080fd5b50610b8c613b3e565b60405161036e919061539c565b6000610bb1600080516020615cea8339815191525490565b905090565b60405162461bcd60e51b81526020600482015260146024820152732ab739bab83837b93a32b210333ab731ba34b7b760611b60448201526064015b60405180910390fd5b60378181548110610c0a57600080fd5b600091825260209091200154905081565b610c236137ee565b610c3f5760405162461bcd60e51b8152600401610bf1906153e8565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811690831603610cba5760405162461bcd60e51b815260206004820152601f60248201527f43616e6e6f74207472616e7366657220737570706f72746564206173736574006044820152606401610bf1565b610cd6610cc5610b99565b6001600160a01b0384169083613ba0565b5050565b600260008581526038602052604090205460ff166007811115610cff57610cff615019565b14610d435760405162461bcd60e51b815260206004820152601460248201527315985b1a59185d1bdc881b9bdd081cdd185ad95960621b6044820152606401610bf1565b6000610d4e87613bf7565b604051630afd62ef60e41b81529091506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063afd62ef090610da79084908990889088908d908c90600401615448565b60006040518083038186803b158015610dbf57600080fd5b505afa158015610dd3573d6000803e3d6000fd5b50505050604051806040016040528060036007811115610df557610df5615019565b815264ffffffffff8816602091820152600087815260389091526040902081518154829060ff19166001836007811115610e3157610e31615019565b021790555060209190910151815464ffffffffff9091166101000265ffffffffff00199091161790556001600160a01b0384163014611015576000858152603860205260408120805460ff19166007179055603654905b81811015610fe25760006035600060368481548110610ea957610ea9615492565b600091825260208083209091015483528281019390935260409182019020815160c0810183528154815260018201546001600160401b0380821695830195909552600160401b81049094169281019290925263ffffffff600160801b84041660608301529091608083019060ff600160a01b909104166002811115610f3057610f30615019565b6002811115610f4157610f41615019565b815260019190910154600160a81b90046001600160401b03166020909101528051909150889003610fd957610f94603b5482602001516001600160401b0316633b9aca00610f8f91906154be565b613cfc565b603b6000828254610fa591906154d5565b92505081905550610fd360368381548110610fc257610fc2615492565b906000526020600020015482613d14565b50610fe2565b50600101610e88565b5060405186907fb8318df57b70f6381fb18aaf762e33efa2cc92627aae83d417f6710e1415d8d890600090a2505061108a565b6037805460018101825560009182527f42a7b7dd785cd69714a189dffb3fd7d7174edc9ece837694ce50f7078f7c31ae018690556033805460ff60a01b1916905560405164ffffffffff88169187917f8142f1367675d1a37dc1aa31258c38b05f5348de55b799764472d94ccb4a71f49190a3505b505050505050565b61109a6137ee565b6110b65760405162461bcd60e51b8152600401610bf1906153e8565b600054610100900460ff16806110cf575060005460ff16155b6111325760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610bf1565b600054610100900460ff16158015611154576000805461ffff19166101011790555b61115f848484613df5565b611167613427565b8015611179576000805461ff00191690555b50505050565b6033546001600160a01b031633146111a95760405162461bcd60e51b8152600401610bf1906154e8565b60006111c26001600160401b038316633b9aca006154be565b6040516370a0823160e01b81523060048201529091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611229573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061124d9190615511565b8111156112905760405162461bcd60e51b8152602060048201526011602482015270092dce6eaccccd2c6d2cadce840ae8aa89607b1b6044820152606401610bf1565b603654600c116112d15760405162461bcd60e51b815260206004820152600c60248201526b4d6178206465706f7369747360a01b6044820152606401610bf1565b6112da81613ea8565b60006113236112e9858061552a565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613f7792505050565b60008181526038602052604090205490915060ff16600181600781111561134c5761134c615019565b14806113695750600381600781111561136757611367615019565b145b6113b55760405162461bcd60e51b815260206004820152601a60248201527f4e6f742072656769737465726564206f722076657269666965640000000000006044820152606401610bf1565b670de0b6b3a76400008310156114015760405162461bcd60e51b815260206004820152601160248201527011195c1bdcda5d081d1bdbc81cdb585b1b607a1b6044820152606401610bf1565b600181600781111561141557611415615019565b0361152857603354600160a01b900460ff161561146d5760405162461bcd60e51b8152602060048201526016602482015275115e1a5cdd1a5b99c8199a5c9cdd0819195c1bdcda5d60521b6044820152606401610bf1565b6801bc16d674ec80000083146114c55760405162461bcd60e51b815260206004820152601c60248201527f496e76616c6964206669727374206465706f73697420616d6f756e74000000006044820152606401610bf1565b6037546030906114d6906001615570565b106115145760405162461bcd60e51b815260206004820152600e60248201526d4d61782076616c696461746f727360901b6044820152606401610bf1565b6033805460ff60a01b1916600160a01b1790555b60408051600160f91b60208201526000602182018190526bffffffffffffffffffffffff193060601b16602c830152910160408051601f198184030181529190529050600182600781111561157f5761157f615019565b0361159e576000838152603860205260409020805460ff191660021790555b6000600c7f00000000000000000000000000000000000000000000000000000000000000006115cc4261403b565b6115d69190615583565b6115e091906155a2565b603480549192506000916001600160801b031690826115fe836155de565b91906101000a8154816001600160801b0302191690836001600160801b031602179055506001600160801b031690506040518060c00160405280868152602001886001600160401b03168152602001836001600160401b0316815260200161166a6036805490506140a7565b63ffffffff168152602001600181526001600160401b036020918201819052600084815260358352604090819020845181559284015160018401805492860151606087015163ffffffff16600160801b0263ffffffff60801b19918616600160401b026001600160801b03199095169390951692909217929092179081168317825560808501519260ff60a01b191664ffffffffff60801b1990911617600160a01b83600281111561171e5761171e615019565b021790555060a09190910151600191820180546001600160401b03909216600160a81b0267ffffffffffffffff60a81b199092169190911790556036805491820181556000527f4a11f94e20a93c79f6ec743a1954ec4fc2c08429ae2122118bf234b2185c81b8018190556001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166322895118876117c38b8061552a565b876117d160208f018f61552a565b8f604001356040518863ffffffff1660e01b81526004016117f796959493929190615659565b6000604051808303818588803b15801561181057600080fd5b505af1158015611824573d6000803e3d6000fd5b508493508892507fe6b13456d345a251702a9dab19a68b9e3550781d95fa9faf104a06ff8bc30495915061185a90508b8061552a565b8a60405161186a939291906156a8565b60405180910390a35050505050505050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146118c45760405162461bcd60e51b8152600401610bf1906156cc565b600080516020615cca833981519152805460011981016118f65760405162461bcd60e51b8152600401610bf190615703565b600282557f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b03161461194b5760405162461bcd60e51b8152600401610bf19061572b565b600083116119945760405162461bcd60e51b81526020600482015260166024820152754d757374206465706f73697420736f6d657468696e6760501b6044820152606401610bf1565b82603c60008282546119a69190615570565b90915550506040805160008152602081018590526001600160a01b038616917f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f62910160405180910390a250600190555050565b611a016137ee565b611a1d5760405162461bcd60e51b8152600401610bf1906153e8565b603354600160a01b900460ff16611a695760405162461bcd60e51b815260206004820152601060248201526f139bc8199a5c9cdd0819195c1bdcda5d60821b6044820152606401610bf1565b6033805460ff60a01b191690556040517fce77f85e30b0e6df0d12527ddf038f900fdeda0eeda4284c52be47b05de31a9790600090a1565b6033546001600160a01b03163314611acb5760405162461bcd60e51b8152600401610bf1906154e8565b6000611b0c84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613f7792505050565b60008181526038602052604090205490915060ff166003816007811115611b3557611b35615019565b1480611b5257506004816007811115611b5057611b50615019565b145b611b9e5760405162461bcd60e51b815260206004820152601e60248201527f56616c696461746f72206e6f742076657269666965642f65786974696e6700006044820152606401610bf1565b826001600160401b0316600003611bc9576000828152603860205260409020805460ff191660041790555b611bd485858561410c565b50817f8dd83105dbd4263d41c76e5d414905babdd3f035bd2031f6ce8895715595979c611c0e6001600160401b038616633b9aca006154be565b60405190815260200160405180910390a25050505050565b6075546001600160a01b03163314611c805760405162461bcd60e51b815260206004820152601b60248201527f43616c6c6572206973206e6f74207468652048617276657374657200000000006044820152606401610bf1565b600080516020615cca83398151915280546001198101611cb25760405162461bcd60e51b8152600401610bf190615703565b60028255611cbe610bb6565b5060019055565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b031614611d605760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b6064820152608401610bf1565b611d6933614251565b565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b031614611dbe5760405162461bcd60e51b8152600401610bf19061572b565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611e22573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e469190615511565b603b54611e539190615570565b92915050565b6000611e644261403b565b90506001600160401b038116611e7c600c6023615756565b603a54611e9291906001600160401b031661577f565b6001600160401b031610611ed85760405162461bcd60e51b815260206004820152600d60248201526c29b730b8103a37b79039b7b7b760991b6044820152606401610bf1565b6000611ee382613bf7565b905060004790506040518060600160405280838152602001846001600160401b03168152602001611f13836142b0565b6001600160801b0390811690915281516039556020820151603a8054604094850151909316600160401b026001600160c01b03199093166001600160401b03909216919091179190911790555182907fb7523e03ed4a74718427c422a01fee1138835adb5bd592240f30bd8b5e1b929a90611f919084815260200190565b60405180910390a2505050565b611fa66137ee565b611fc25760405162461bcd60e51b8152600401610bf1906153e8565b603380546001600160a01b0319166001600160a01b0383169081179091556040517f83f29c79feb71f8fba9d0fbc4ba5f0982a28b6b1e868b3fc50e6400d100bca0f90600090a250565b6033546001600160a01b031633146120365760405162461bcd60e51b8152600401610bf1906154e8565b600061207786868080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613f7792505050565b60008181526038602052604090205490915060ff1660018160078111156120a0576120a0615019565b14806120bd575060058160078111156120bb576120bb615019565b145b806120d9575060078160078111156120d7576120d7615019565b145b6121255760405162461bcd60e51b815260206004820152601c60248201527f56616c696461746f72206e6f742072656764206f7220657869746564000000006044820152606401610bf1565b60008281526038602052604090819020805460ff19166006179055516312b3fc1960e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906312b3fc1990612192908a908a908a908a908a9060040161584b565b600060405180830381600087803b1580156121ac57600080fd5b505af11580156121c0573d6000803e3d6000fd5b50505050817f63d54ea43f163d6e28fc23abec67eb7c3294e7e6f0620955a73cd8d17c7367f486866040516121f692919061588c565b60405180910390a250505050505050565b6076818154811061221757600080fd5b6000918252602090912001546001600160a01b0316905081565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161480612280575061226b610b99565b6001600160a01b0316336001600160a01b0316145b6122d85760405162461bcd60e51b815260206004820152602360248201527f43616c6c6572206973206e6f7420746865205661756c74206f7220476f7665726044820152623737b960e91b6064820152608401610bf1565b600080516020615cca8339815191528054600119810161230a5760405162461bcd60e51b8152600401610bf190615703565b600282556040516370a0823160e01b8152306004820152479060009082907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015612379573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061239d9190615511565b6123a79190615570565b905080156123da576123da7f00000000000000000000000000000000000000000000000000000000000000008284614319565b5050600182555050565b604080516060810182526039548152603a546001600160401b03811660208301819052600160401b9091046001600160801b031692820192909252906124625760405162461bcd60e51b81526020600482015260136024820152724e6f20736e61707065642062616c616e63657360681b6044820152606401610bf1565b603754600081156127e3578161247b60608601866158a0565b9050146124c35760405162461bcd60e51b8152602060048201526016602482015275496e76616c69642062616c616e63652070726f6f667360501b6044820152606401610bf1565b816124d160408601866158a0565b9050146125195760405162461bcd60e51b8152602060048201526016602482015275496e76616c69642062616c616e6365206c656176657360501b6044820152606401610bf1565b82516001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906391ad640d90863561255b602089018961552a565b6040518563ffffffff1660e01b815260040161257a94939291906158e9565b60006040518083038186803b15801561259257600080fd5b505afa1580156125a6573d6000803e3d6000fd5b5084925050505b80156127e1576125bc81615909565b905060006001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663f34ad34c87356125fe60408a018a6158a0565b8681811061260e5761260e615492565b9050602002013589806060019061262591906158a0565b8781811061263557612635615492565b9050602002810190612647919061552a565b6038600060378a8154811061265e5761265e615492565b9060005260206000200154815260200190815260200160002060000160019054906101000a900464ffffffffff166040518663ffffffff1660e01b81526004016126ac959493929190615920565b602060405180830381865afa1580156126c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126ed9190615511565b9050806000036127c1576005603860006037858154811061271057612710615492565b600091825260208083209091015483528201929092526040019020805460ff1916600183600781111561274557612745615019565b02179055506127556001856154d5565b93506037848154811061276a5761276a615492565b90600052602060002001546037838154811061278857612788615492565b60009182526020909120015560378054806127a5576127a5615959565b60019003818190600052602060002001600090559055506125ad565b6127cf81633b9aca006154be565b6127d99084615570565b9250506125ad565b505b60365460008115612d535784516000906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690637383a9fc9061283160208c018c61596f565b60408c013561284360608e018e61552a565b6040518663ffffffff1660e01b815260040161286395949392919061598a565b602060405180830381865afa158015612880573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128a491906159b9565b905080156128f45760405162461bcd60e51b815260206004820152601c60248201527f4465706f736974732068617665206265656e2070726f636573736564000000006044820152606401610bf1565b886001600160401b031686602001516001600160401b0316111561295a5760405162461bcd60e51b815260206004820152601b60248201527f496e76616c69642076616c696461746f722074696d657374616d7000000000006044820152606401610bf1565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b3421d0f6129928b613bf7565b6129a260408c0160208d016159d6565b60408c01356001600160401b036129bc60808f018f61552a565b8f8060a001906129cc919061552a565b6040518963ffffffff1660e01b81526004016129ef9897969594939291906159f1565b60006040518083038186803b158015612a0757600080fd5b505afa158015612a1b573d6000803e3d6000fd5b5060009250612a3091505060208a018a61596f565b6001600160401b031611612a865760405162461bcd60e51b815260206004820152601d60248201527f496e76616c69642066697273742070656e64696e67206465706f7369740000006044820152606401610bf1565b6000612a946020600c615756565b7f0000000000000000000000000000000000000000000000000000000000000000612acb89602001516001600160401b031661403b565b612ad59190615583565b612adf91906155a2565b905060005b84811015612d4f57600060368281548110612b0157612b01615492565b6000918252602080832090910154808352603582526040808420815160c0810183528154815260018201546001600160401b0380821696830196909652600160401b81049095169281019290925263ffffffff600160801b850416606083015291945091608083019060ff600160a01b909104166002811115612b8657612b86615019565b6002811115612b9757612b97615019565b8152600191909101546001600160401b03600160a81b909104811660209283015260408301519293509190911690612bd1908e018e61596f565b6001600160401b03161080612c1457508060a001516001600160401b0316846001600160401b0316108015612c14575060a08101516001600160401b0390811614155b80612c4357506005815160009081526038602052604090205460ff166007811115612c4157612c41615019565b145b612c8f5760405162461bcd60e51b815260206004820152601860248201527f4465706f736974206c696b656c792070726f63657373656400000000000000006044820152606401610bf1565b6005815160009081526038602052604090205460ff166007811115612cb657612cb6615019565b03612d1c57612cc58282613d14565b817fd37dfd28ce3c6abccf25dc583376c1fe84be5abe9cd4b8dc94904b25d62a784582602001516001600160401b0316633b9aca00612d0491906154be565b60405190815260200160405180910390a25050612d47565b6020810151612d38906001600160401b0316633b9aca006154be565b612d429087615570565b955050505b600101612ae4565b5050505b60408501516001600160801b0316612d6b8483615570565b612d759190615570565b603b55603a805467ffffffffffffffff1916905560208581015160408088015181518581529384018790526001600160801b0316908301526001600160401b0316907fed2528338eefb63fd1860078b91e35106bc25e3fd528634d180f662582fe5ec1906060015b60405180910390a25050505050505050565b612df76137ee565b612e135760405162461bcd60e51b8152600401610bf1906153e8565b8060005b81811015612ebd576000848483818110612e3357612e33615492565b9050602002016020810190612e489190614b33565b6001600160a01b031603612eb55760405162461bcd60e51b815260206004820152602e60248201527f43616e206e6f742073657420616e20656d70747920616464726573732061732060448201526d30903932bbb0b932103a37b5b2b760911b6064820152608401610bf1565b600101612e17565b507f04c0b9649497d316554306e53678d5f5f5dbc3a06f97dec13ff4cfe98b986bbc60768484604051612ef293929190615a4f565b60405180910390a161117960768484614a02565b6000848152603560209081526040808320815160c0810183528154815260018201546001600160401b0380821695830195909552600160401b810490941692810192909252600160801b830463ffffffff16606083015290916080830190600160a01b900460ff166002811115612f7f57612f7f615019565b6002811115612f9057612f90615019565b815260019190910154600160a81b90046001600160401b03166020918201528151600090815260389091526040808220815180830190925280549394509192909190829060ff166007811115612fe857612fe8615019565b6007811115612ff957612ff9615019565b81529054610100900464ffffffffff16602090910152905060018260800151600281111561302957613029615019565b1461306c5760405162461bcd60e51b81526020600482015260136024820152724465706f736974206e6f742070656e64696e6760681b6044820152606401610bf1565b60038151600781111561308157613081615019565b146130c75760405162461bcd60e51b815260206004820152601660248201527515985b1a59185d1bdc881b9bdd081d995c9a599a595960521b6044820152606401610bf1565b846001600160401b031682604001516001600160401b0316106131255760405162461bcd60e51b815260206004820152601660248201527514db1bdd081b9bdd0818599d195c8819195c1bdcda5d60521b6044820152606401610bf1565b600061313861313387614433565b613bf7565b905060006001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663d98a55648361317960208a018a61596f565b61318660208b018b61552a565b6040518563ffffffff1660e01b81526004016131a59493929190615ae8565b602060405180830381865afa1580156131c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131e691906159b9565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663334a88fb838560200151886000016020810190613230919061596f565b61323d60208b018b61552a565b6040518663ffffffff1660e01b815260040161325d959493929190615b11565b60006040518083038186803b15801561327557600080fd5b505afa158015613289573d6000803e3d6000fd5b506001600160401b0392506132a4915050602087018761596f565b6001600160401b031614613357576132bf602086018661596f565b6001600160401b0390811660a0860152602085015189917fbc3fdc080055a5d0342452fd2f970db3142457507168345b18a9621e5510dafa916133079116633b9aca006154be565b613314602089018961596f565b604080519283526001600160401b0390911660208301520160405180910390a2505090516000908152603860205260409020805460ff1916600417905550611179565b613364602087018761596f565b6001600160401b031684604001516001600160401b031610806133845750805b6133d05760405162461bcd60e51b815260206004820152601c60248201527f4465706f736974206c696b656c79206e6f742070726f636573736564000000006044820152606401610bf1565b6133da8885613d14565b877fcc6c22fbaf9f36d51193b29b3114d27c22dee0b6616975e3a1e0cf67022633ae85602001516001600160401b0316633b9aca0061341991906154be565b604051908152602001612ddd565b60405163095ea7b360e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015260001960248301527f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b3906044016020604051808303816000875af11580156134b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134db91906159b9565b50565b6134e66137ee565b6135025760405162461bcd60e51b8152600401610bf1906153e8565b604051631a1b9a0b60e21b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063686e682c9061355290869086908690600401615b47565b600060405180830381600087803b15801561356c57600080fd5b505af1158015613580573d6000803e3d6000fd5b50505050505050565b60368181548110610c0a57600080fd5b6033546001600160a01b031633146135c35760405162461bcd60e51b8152600401610bf1906154e8565b600061360489898080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613f7792505050565b90506000808281526038602052604090205460ff16600781111561362a5761362a615019565b146136775760405162461bcd60e51b815260206004820152601c60248201527f56616c696461746f7220616c72656164792072656769737465726564000000006044820152606401610bf1565b60008181526038602052604090819020805460ff19166001179055516301ba3ee760e21b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906306e8fb9c906136ea908c908c908c908c908c908c908c908c90600401615be9565b600060405180830381600087803b15801561370457600080fd5b505af1158015613718573d6000803e3d6000fd5b50505050807f50837f89f5e75ae0a7bcc858f53ea15fa398dc007fd52cbfe4683ae9a6c2d722888860405161374e92919061588c565b60405180910390a2505050505050505050565b6137696137ee565b6137855760405162461bcd60e51b8152600401610bf1906153e8565b607554604080516001600160a01b03928316815291831660208301527fe48386b84419f4d36e0f96c10cc3510b6fb1a33795620c5098b22472bbe90796910160405180910390a1607580546001600160a01b0319166001600160a01b0392909216919091179055565b6000613806600080516020615cea8339815191525490565b6001600160a01b0316336001600160a01b031614905090565b6138276137ee565b6138435760405162461bcd60e51b8152600401610bf1906153e8565b61386b817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b031661388b600080516020615cea8339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461390b5760405162461bcd60e51b8152600401610bf1906156cc565b600080516020615cca8339815191528054600119810161393d5760405162461bcd60e51b8152600401610bf190615703565b600282557f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b0316146139925760405162461bcd60e51b8152600401610bf19061572b565b61399d858447614319565b5060019055505050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146139ef5760405162461bcd60e51b8152600401610bf1906156cc565b600080516020615cca83398151915280546001198101613a215760405162461bcd60e51b8152600401610bf190615703565b600282556040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015613a8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ab09190615511565b90506000603c5482613ac291906154d5565b905080156123da57603c8290556040805160008152602081018390526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016917f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f62910160405180910390a25050600182555050565b60606076805480602002602001604051908101604052809291908181526020018280548015613b9657602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613b78575b5050505050905090565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052613bf2908490614476565b505050565b604080516001600160401b038316602082015260009182918291720f3df6d732807ef1319fb7b8bb8522d0beac02910160408051601f1981840301815290829052613c4191615c4a565b600060405180830381855afa9150503d8060008114613c7c576040519150601f19603f3d011682016040523d82523d6000602084013e613c81565b606091505b5091509150818015613c94575060008151115b613ce05760405162461bcd60e51b815260206004820152601860248201527f496e76616c696420626561636f6e2074696d657374616d7000000000000000006044820152606401610bf1565b80806020019051810190613cf49190615511565b949350505050565b6000818310613d0b5781613d0d565b825b9392505050565b60008281526035602052604081206001908101805460ff60a01b1916600160a11b179055603680549091613d47916154d5565b81548110613d5757613d57615492565b90600052602060002001549050806036836060015163ffffffff1681548110613d8257613d82615492565b60009182526020808320909101929092556060840151838252603590925260409020600101805463ffffffff909216600160801b0263ffffffff60801b199092169190911790556036805480613dda57613dda615959565b60019003818190600052602060002001600090559055505050565b8251613e08906076906020860190614a61565b50815181518114613e525760405162461bcd60e51b8152602060048201526014602482015273496e76616c696420696e7075742061727261797360601b6044820152606401610bf1565b60005b81811015613ea157613e99848281518110613e7257613e72615492565b6020026020010151848381518110613e8c57613e8c615492565b6020026020010151614548565b600101613e55565b5050505050565b604051632e1a7d4d60e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b158015613f0a57600080fd5b505af1158015613f1e573d6000803e3d6000fd5b505050506000613f3082603c54613cfc565b905080603c6000828254613f4491906154d5565b9250508190555081603b6000828254613f5d9190615570565b9091555050603a805467ffffffffffffffff191690555050565b60008151603014613fca5760405162461bcd60e51b815260206004820152601960248201527f496e76616c6964207075626c6963206b6579206c656e677468000000000000006044820152606401610bf1565b604051600290613fe1908490600090602001615c66565b60408051601f1981840301815290829052613ffb91615c4a565b602060405180830381855afa158015614018573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190611e539190615511565b60006001600160401b038211156140a35760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203660448201526534206269747360d01b6064820152608401610bf1565b5090565b600063ffffffff8211156140a35760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201526532206269747360d01b6064820152608401610bf1565b60006030831461415e5760405162461bcd60e51b815260206004820152601d60248201527f496e76616c69642076616c696461746f722062797465206c656e6774680000006044820152606401610bf1565b6141666146a7565b90506000710961ef480eb55e80d19ad83579a64c0070026001600160a01b03168286868660405160200161419c93929190615c95565b60408051601f19818403018152908290526141b691615c4a565b60006040518083038185875af1925050503d80600081146141f3576040519150601f19603f3d011682016040523d82523d6000602084013e6141f8565b606091505b50509050806142495760405162461bcd60e51b815260206004820152601960248201527f5769746864726177616c2072657175657374206661696c6564000000000000006044820152606401610bf1565b509392505050565b6001600160a01b0381166142a75760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f7220697320616464726573732830290000000000006044820152606401610bf1565b6134db8161476e565b60006001600160801b038211156140a35760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316044820152663238206269747360c81b6064820152608401610bf1565b600082116143695760405162461bcd60e51b815260206004820152601760248201527f4d75737420776974686472617720736f6d657468696e670000000000000000006044820152606401610bf1565b6001600160a01b0383166143b85760405162461bcd60e51b8152602060048201526016602482015275135d5cdd081cdc1958da599e481c9958da5c1a595b9d60521b6044820152606401610bf1565b80156143c7576143c7816147d5565b6143d18284614897565b6040805160008152602081018490526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016917f2717ead6b9200dd235aad468c9809ea400fe33ac69b5bfaa6d3e90fc922b63989101611f91565b6000600c7f00000000000000000000000000000000000000000000000000000000000000006144628483615756565b61446c919061577f565b611e53919061577f565b60006144cb826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166148f79092919063ffffffff16565b805190915015613bf257808060200190518101906144e991906159b9565b613bf25760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610bf1565b6001600160a01b0382811660009081526071602052604090205416156145a55760405162461bcd60e51b81526020600482015260126024820152711c151bdad95b88185b1c9958591e481cd95d60721b6044820152606401610bf1565b6001600160a01b038216158015906145c557506001600160a01b03811615155b6146055760405162461bcd60e51b8152602060048201526011602482015270496e76616c69642061646472657373657360781b6044820152606401610bf1565b6001600160a01b03828116600081815260716020908152604080832080549587166001600160a01b031996871681179091556072805460018101825594527fdffbd64cc7c1a7eb27984335d9416d51137a03d3fabec7141025c62663253fe190930180549095168417909455925190815290917fef6485b84315f9b1483beffa32aae9a0596890395e3d7521f1c5fbb51790e765910160405180910390a25050565b60405160009081908190710961ef480eb55e80d19ad83579a64c0070029082818181855afa9150503d80600081146146fb576040519150601f19603f3d011682016040523d82523d6000602084013e614700565b606091505b5091509150818015614713575060008151115b6147535760405162461bcd60e51b81526020600482015260116024820152704661696c656420746f206765742066656560781b6044820152606401610bf1565b808060200190518101906147679190615511565b9250505090565b806001600160a01b031661478e600080516020615cea8339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a3600080516020615cea83398151915255565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561483057600080fd5b505af1158015614844573d6000803e3d6000fd5b505050505080603c600082825461485b9190615570565b9091555050603b5461486d9082613cfc565b603b600082825461487e91906154d5565b9091555050603a805467ffffffffffffffff1916905550565b6148cb6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168284613ba0565b60006148d983603c54613cfc565b905080603c60008282546148ed91906154d5565b9091555050505050565b6060613cf4848460008585843b6149505760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610bf1565b600080866001600160a01b0316858760405161496c9190615c4a565b60006040518083038185875af1925050503d80600081146149a9576040519150601f19603f3d011682016040523d82523d6000602084013e6149ae565b606091505b50915091506149be8282866149c9565b979650505050505050565b606083156149d8575081613d0d565b8251156149e85782518084602001fd5b8160405162461bcd60e51b8152600401610bf19190615cb6565b828054828255906000526020600020908101928215614a55579160200282015b82811115614a555781546001600160a01b0319166001600160a01b03843516178255602090920191600190910190614a22565b506140a3929150614ab6565b828054828255906000526020600020908101928215614a55579160200282015b82811115614a5557825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190614a81565b5b808211156140a35760008155600101614ab7565b80356001600160a01b0381168114614ae257600080fd5b919050565b60008060408385031215614afa57600080fd5b614b0383614acb565b9150614b1160208401614acb565b90509250929050565b600060208284031215614b2c57600080fd5b5035919050565b600060208284031215614b4557600080fd5b613d0d82614acb565b60008060408385031215614b6157600080fd5b614b6a83614acb565b946020939093013593505050565b80356001600160401b0381168114614ae257600080fd5b803564ffffffffff81168114614ae257600080fd5b60008083601f840112614bb657600080fd5b5081356001600160401b03811115614bcd57600080fd5b602083019150836020828501011115614be557600080fd5b9250929050565b60008060008060008060a08789031215614c0557600080fd5b614c0e87614b78565b9550614c1c60208801614b8f565b945060408701359350614c3160608801614acb565b925060808701356001600160401b03811115614c4c57600080fd5b614c5889828a01614ba4565b979a9699509497509295939492505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715614ca857614ca8614c6a565b604052919050565b60006001600160401b03821115614cc957614cc9614c6a565b5060051b60200190565b600082601f830112614ce457600080fd5b8135614cf7614cf282614cb0565b614c80565b8082825260208201915060208360051b860101925085831115614d1957600080fd5b602085015b83811015614d3d57614d2f81614acb565b835260209283019201614d1e565b5095945050505050565b600080600060608486031215614d5c57600080fd5b83356001600160401b03811115614d7257600080fd5b614d7e86828701614cd3565b93505060208401356001600160401b03811115614d9a57600080fd5b614da686828701614cd3565b92505060408401356001600160401b03811115614dc257600080fd5b614dce86828701614cd3565b9150509250925092565b60008060408385031215614deb57600080fd5b82356001600160401b03811115614e0157600080fd5b830160608186031215614e1357600080fd5b9150614b1160208401614b78565b600080600060408486031215614e3657600080fd5b83356001600160401b03811115614e4c57600080fd5b614e5886828701614ba4565b9094509250614e6b905060208501614b78565b90509250925092565b60008083601f840112614e8657600080fd5b5081356001600160401b03811115614e9d57600080fd5b6020830191508360208260051b8501011115614be557600080fd5b600060a08284031215614eca57600080fd5b50919050565b600080600080600060e08688031215614ee857600080fd5b85356001600160401b03811115614efe57600080fd5b614f0a88828901614ba4565b90965094505060208601356001600160401b03811115614f2957600080fd5b614f3588828901614e74565b9094509250614f4990508760408801614eb8565b90509295509295909350565b600080600060608486031215614f6a57600080fd5b614f7384614b78565b925060208401356001600160401b03811115614f8e57600080fd5b840160c08187031215614fa057600080fd5b915060408401356001600160401b03811115614fbb57600080fd5b840160808187031215614fcd57600080fd5b809150509250925092565b60008060208385031215614feb57600080fd5b82356001600160401b0381111561500157600080fd5b61500d85828601614e74565b90969095509350505050565b634e487b7160e01b600052602160045260246000fd5b604081016008841061504357615043615019565b92815264ffffffffff9190911660209091015290565b600060408284031215614eca57600080fd5b6000806000806080858703121561508157600080fd5b8435935061509160208601614b78565b925060408501356001600160401b038111156150ac57600080fd5b6150b887828801615059565b92505060608501356001600160401b038111156150d457600080fd5b6150e087828801615059565b91505092959194509250565b8681526001600160401b0386811660208301528516604082015263ffffffff8416606082015260c081016003841061512657615126615019565b8360808301526001600160401b03831660a0830152979650505050505050565b803563ffffffff81168114614ae257600080fd5b80151581146134db57600080fd5b600060a0828403121561517a57600080fd5b60405160a081016001600160401b038111828210171561519c5761519c614c6a565b6040529050806151ab83615146565b81526151b960208401614b78565b60208201526151ca60408401614b78565b604082015260608301356151dd8161515a565b6060820152608092830135920191909152919050565b600080600060e0848603121561520857600080fd5b83356001600160401b0381111561521e57600080fd5b8401601f8101861361522f57600080fd5b803561523d614cf282614cb0565b8082825260208201915060208360051b85010192508883111561525f57600080fd5b6020840193505b828410156152885761527784614b78565b825260209384019390910190615266565b955050505060208401359150614e6b8560408601615168565b600080600080600080600080610120898b0312156152be57600080fd5b88356001600160401b038111156152d457600080fd5b6152e08b828c01614ba4565b90995097505060208901356001600160401b038111156152ff57600080fd5b61530b8b828c01614e74565b90975095505060408901356001600160401b0381111561532a57600080fd5b6153368b828c01614ba4565b909550935050606089013591506153508a60808b01614eb8565b90509295985092959890939650565b60008060006060848603121561537457600080fd5b61537d84614acb565b925061538b60208501614acb565b929592945050506040919091013590565b602080825282518282018190526000918401906040840190835b818110156153dd5783516001600160a01b03168352602093840193909201916001016153b6565b509095945050505050565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b86815285602082015260a06040820152600061546860a08301868861541f565b64ffffffffff949094166060830152506001600160a01b0391909116608090910152949350505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417611e5357611e536154a8565b81810381811115611e5357611e536154a8565b6020808252600f908201526e2737ba102932b3b4b9ba3930ba37b960891b604082015260600190565b60006020828403121561552357600080fd5b5051919050565b6000808335601e1984360301811261554157600080fd5b8301803591506001600160401b0382111561555b57600080fd5b602001915036819003821315614be557600080fd5b80820180821115611e5357611e536154a8565b6001600160401b038281168282160390811115611e5357611e536154a8565b60006001600160401b038316806155c957634e487b7160e01b600052601260045260246000fd5b806001600160401b0384160491505092915050565b60006001600160801b0382166001600160801b038103615600576156006154a8565b60010192915050565b60005b8381101561562457818101518382015260200161560c565b50506000910152565b60008151808452615645816020860160208601615609565b601f01601f19169290920160200192915050565b60808152600061566d60808301888a61541f565b828103602084015261567f818861562d565b9050828103604084015261569481868861541f565b915050826060830152979650505050505050565b6040815260006156bc60408301858761541f565b9050826020830152949350505050565b60208082526017908201527f43616c6c6572206973206e6f7420746865205661756c74000000000000000000604082015260600190565b6020808252600e908201526d1499595b9d1c985b9d0818d85b1b60921b604082015260600190565b602080825260119082015270155b9cdd5c1c1bdc9d195908185cdcd95d607a1b604082015260600190565b6001600160401b038181168382160290811690818114615778576157786154a8565b5092915050565b6001600160401b038181168382160190811115611e5357611e536154a8565b81835260208301925060008160005b848110156157dc576001600160401b036157c683614b78565b16865260209586019591909101906001016157ad565b5093949350505050565b63ffffffff6157f482615146565b1682526001600160401b0361580b60208301614b78565b1660208301526001600160401b0361582560408301614b78565b16604083015260608101356158398161515a565b15156060830152608090810135910152565b60e08152600061585f60e08301878961541f565b828103602084015261587281868861579e565b91505061588260408301846157e6565b9695505050505050565b602081526000613cf460208301848661579e565b6000808335601e198436030181126158b757600080fd5b8301803591506001600160401b038211156158d157600080fd5b6020019150600581901b3603821315614be557600080fd5b84815283602082015260606040820152600061588260608301848661541f565b600081615918576159186154a8565b506000190190565b85815284602082015260806040820152600061594060808301858761541f565b905064ffffffffff831660608301529695505050505050565b634e487b7160e01b600052603160045260246000fd5b60006020828403121561598157600080fd5b613d0d82614b78565b8581526001600160401b03851660208201528360408201526080606082015260006149be60808301848661541f565b6000602082840312156159cb57600080fd5b8151613d0d8161515a565b6000602082840312156159e857600080fd5b613d0d82614b8f565b88815264ffffffffff881660208201528660408201526001600160401b038616606082015260c060808201526000615a2d60c08301868861541f565b82810360a0840152615a4081858761541f565b9b9a5050505050505050505050565b6040808252845490820181905260008581526020812090916060840190835b81811015615a955783546001600160a01b0316835260019384019360209093019201615a6e565b50508381036020808601919091528582520190508460005b85811015615adc576001600160a01b03615ac683614acb565b1683526020928301929190910190600101615aad565b50909695505050505050565b8481526001600160401b038416602082015260606040820152600061588260608301848661541f565b85815264ffffffffff851660208201526001600160401b03841660408201526080606082015260006149be60808301848661541f565b60e080825284519082018190526000906020860190610100840190835b81811015615b8b5783516001600160401b0316835260209384019390920191600101615b64565b5050809250505083602083015263ffffffff83511660408301526001600160401b0360208401511660608301526001600160401b0360408401511660808301526060830151151560a0830152608083015160c0830152949350505050565b61012081526000615bff61012083018a8c61541f565b8281036020840152615c1281898b61579e565b90508281036040840152615c2781878961541f565b915050836060830152615c3d60808301846157e6565b9998505050505050505050565b60008251615c5c818460208701615609565b9190910192915050565b60008351615c78818460208801615609565b6001600160801b0319939093169190920190815260100192915050565b8284823760c09190911b6001600160c01b0319169101908152600801919050565b602081526000613d0d602083018461562d56fe53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac45357bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa264697066735822122076a0f28f409c5b21fe38d516dbb2b9162dc16ebd26f719328b5a99a559a7c5a964736f6c634300081c0033", + "libraries": {}, + "devdoc": { + "author": "Origin Protocol Inc", + "kind": "dev", + "methods": { + "checkBalance(address)": { + "params": { + "_asset": "Address of WETH asset." + }, + "returns": { + "balance": " Total value in ETH" + } + }, + "constructor": { + "params": { + "_baseConfig": "Base strategy config with `platformAddress` not used so empty address `vaultAddress` the address of the OETH Vault contract", + "_beaconChainDepositContract": "Address of the beacon chain deposit contract", + "_beaconGenesisTimestamp": "The timestamp of the Beacon chain's genesis.", + "_beaconProofs": "Address of the Beacon Proofs contract that verifies beacon chain data", + "_ssvNetwork": "Address of the SSV Network contract", + "_ssvToken": "Address of the SSV Token contract", + "_wethAddress": "Address of the WETH Token contract" + } + }, + "deposit(address,uint256)": { + "params": { + "_amount": "Amount of WETH that was transferred to the strategy by the vault.", + "_asset": "Address of the WETH token." + } + }, + "getRewardTokenAddresses()": { + "returns": { + "_0": "address[] the reward token addresses." + } + }, + "initialize(address[],address[],address[])": { + "params": { + "_assets": "Not used so empty array", + "_pTokens": "Not used so empty array", + "_rewardTokenAddresses": "Not used so empty array" + } + }, + "registerSsvValidator(bytes,uint64[],bytes,uint256,(uint32,uint64,uint64,bool,uint256))": { + "params": { + "cluster": "The SSV cluster details including the validator count and SSV balance", + "operatorIds": "The operator IDs of the SSV Cluster", + "publicKey": "The public key of the validator", + "sharesData": "The shares data for the validator", + "ssvAmount": "The amount of SSV tokens to be deposited to the SSV cluster" + } + }, + "removeSsvValidator(bytes,uint64[],(uint32,uint64,uint64,bool,uint256))": { + "params": { + "cluster": "The SSV cluster details including the validator count and SSV balance", + "operatorIds": "The operator IDs of the SSV Cluster", + "publicKey": "The public key of the validator" + } + }, + "setHarvesterAddress(address)": { + "params": { + "_harvesterAddress": "Address of the harvester contract." + } + }, + "setRewardTokenAddresses(address[])": { + "params": { + "_rewardTokenAddresses": "Array of reward token addresses" + } + }, + "stakeEth((bytes,bytes,bytes32),uint64)": { + "params": { + "depositAmountGwei": "The amount of WETH to stake to the validator in Gwei.", + "validatorStakeData": "validator data needed to stake. The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot. Only the registrator can call this function." + } + }, + "supportsAsset(address)": { + "params": { + "_asset": "The address of the WETH token." + } + }, + "transferGovernance(address)": { + "params": { + "_newGovernor": "Address of the new Governor" + } + }, + "transferToken(address,uint256)": { + "params": { + "_amount": "Amount of the asset to transfer", + "_asset": "Address for the asset" + } + }, + "validatorWithdrawal(bytes,uint64)": { + "params": { + "amountGwei": "The amount of ETH to be withdrawn from the validator in Gwei. A zero amount will trigger a full withdrawal.", + "publicKey": "The public key of the validator" + } + }, + "verifyBalances(uint64,(uint64,uint40,bytes32,bytes,bytes,bytes),(bytes32,bytes,bytes32[],bytes[]))": { + "params": { + "balanceProofs": "a `BalanceProofs` struct containing the following: - balancesContainerRoot: The merkle root of the balances container - balancesContainerProof: The merkle proof for the balances container to the beacon block root. This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node. - validatorBalanceLeaves: Array of leaf nodes containing the validator balance with three other balances. - validatorBalanceProofs: Array of merkle proofs for the validator balance to the Balances container root. This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.", + "firstPendingDeposit": "a `FirstPendingDepositWithdrawableProofData` struct containing: - slot: The beacon chain slot of the first deposit in the beacon chain's deposit queue. Can be anything if the deposit queue is empty, but zero is a good choice. - validatorIndex: The index of the validator of the first pending deposit. Can be anything if the deposit queue is empty, but zero is a good choice. - pubKeyHash: The hash of the public key of the validator of the first pending deposit. Can be anything if the deposit queue is empty, but 32 zero bytes is a good choice - pendingDepositPubKeyProof: The merkle proof of the first pending deposit to the beacon block root. Can be either: * 40 witness hashes for BeaconBlock.state.PendingDeposits[0].pubKey when the deposit queue is not empty. * 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty. The 32 byte witness hashes are concatenated together starting from the leaf node. - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the first pending deposit's validator to the beacon block root. This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node. - validatorPubKeyProof The merkle proof for the public key of the first pending deposit's validator to the this witness hash of withdrawableEpochProof. This is 2 witness hashes of 32 bytes each concatenated together starting from the leaf node.", + "validatorVerificationBlockTimestamp": "next block's timestamp of a slot that has the first pending deposit already applied to the validator." + } + }, + "verifyDeposit(uint256,uint64,(uint64,bytes),(uint64,bytes))": { + "params": { + "depositID": "The deposit ID emitted in `ETHStaked` from the `stakeEth` function.", + "depositProcessedSlot": "Any slot on or after the strategy's deposit was processed on the beacon chain. Can not be a slot with pending deposits with the same slot as the deposit being verified. Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root set for the next block timestamp in 12 seconds time.", + "firstPendingDeposit": "a `FirstPendingDepositSlotProofData` struct containing: - slot: The beacon chain slot of the first deposit in the beacon chain's deposit queue. Can be anything if the deposit queue is empty, but zero is a good choice. - proof: The merkle proof of the first pending deposit's slot to the beacon block root. Can be either: * 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty. * 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty. The 32 byte witness hashes are concatenated together starting from the leaf node.", + "strategyValidatorData": "a `StrategyValidatorProofData` struct containing: - withdrawableEpoch: The withdrawable epoch of the validator the strategy is depositing to. - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the validator the strategy is depositing to, to the beacon block root. This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node." + } + }, + "verifyValidator(uint64,uint40,bytes32,address,bytes)": { + "params": { + "nextBlockTimestamp": "The timestamp of the execution layer block after the beacon chain slot we are verifying. The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp, which is the beacon block root of the previous block.", + "pubKeyHash": "The hash of the validator's public key using the Beacon Chain's format", + "validatorIndex": "The index of the validator on the beacon chain.", + "validatorPubKeyProof": "The merkle proof for the validator public key to the beacon block root. This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node. BeaconBlock.state.validators[validatorIndex].pubkey", + "withdrawalAddress": "The withdrawal address of the validator which should be this strategy's address. If the withdrawal address is not this strategy's address, the initial deposit was front-run and the validator is marked as invalid." + } + }, + "withdraw(address,address,uint256)": { + "params": { + "_amount": "Amount of WETH to withdraw.", + "_asset": "Address of the WETH token.", + "_recipient": "Address to receive withdrawn assets." + } + }, + "withdrawSSV(uint64[],uint256,(uint32,uint64,uint64,bool,uint256))": { + "details": "A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.", + "params": { + "cluster": "The SSV cluster details including the validator count and SSV balance", + "operatorIds": "The operator IDs of the SSV Cluster", + "ssvAmount": "The amount of SSV tokens to be withdrawn from the SSV cluster" + } + } + }, + "title": "Compounding Staking SSV Strategy", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "BEACON_CHAIN_DEPOSIT_CONTRACT()": { + "notice": "The address of the beacon chain deposit contract" + }, + "BEACON_GENESIS_TIMESTAMP()": { + "notice": "The timestamp of the Beacon chain genesis." + }, + "BEACON_PROOFS()": { + "notice": "Address of the Beacon Proofs contract that verifies beacon chain data" + }, + "SSV_NETWORK()": { + "notice": "The address of the SSV Network contract used to interface with" + }, + "SSV_TOKEN()": { + "notice": "SSV ERC20 token that serves as a payment for operating SSV validators" + }, + "VAULT_ADDRESS()": { + "notice": "Address of the OETH Vault proxy contract" + }, + "WETH()": { + "notice": "The address of the Wrapped ETH (WETH) token contract" + }, + "assetToPToken(address)": { + "notice": "asset => pToken (Platform Specific Token Address)" + }, + "checkBalance(address)": { + "notice": "Accounts for all the assets managed by this strategy which includes: 1. The current WETH in this strategy contract 2. The last verified ETH balance, total deposits and total validator balances" + }, + "claimGovernance()": { + "notice": "Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor." + }, + "collectRewardTokens()": { + "notice": "Collect accumulated reward token and send to Vault." + }, + "deposit(address,uint256)": { + "notice": "Unlike other strategies, this does not deposit assets into the underlying platform. It just checks the asset is WETH and emits the Deposit event. To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used." + }, + "depositAll()": { + "notice": "Unlike other strategies, this does not deposit assets into the underlying platform. It just emits the Deposit event. To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used." + }, + "depositList(uint256)": { + "notice": "List of strategy deposit IDs to a validator. The list can be for deposits waiting to be verified as processed on the beacon chain, or deposits that have been verified to an exiting validator and is now waiting for the validator's balance to be swept. The list may not be ordered by time of deposit. Removed deposits will move the last deposit to the removed index." + }, + "depositListLength()": { + "notice": "Returns the number of deposits waiting to be verified as processed on the beacon chain, or deposits that have been verified to an exiting validator and is now waiting for the validator's balance to be swept." + }, + "deposits(uint256)": { + "notice": "Mapping of the deposit ID to the deposit data" + }, + "firstDeposit()": { + "notice": "Restricts to only one deposit to an unverified validator at a time. This is to limit front-running attacks of deposits to the beacon chain contract." + }, + "getRewardTokenAddresses()": { + "notice": "Get the reward token addresses." + }, + "governor()": { + "notice": "Returns the address of the current Governor." + }, + "harvesterAddress()": { + "notice": "Address of the Harvester contract allowed to collect reward tokens" + }, + "initialize(address[],address[],address[])": { + "notice": "Set up initial internal state including 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract" + }, + "isGovernor()": { + "notice": "Returns true if the caller is the current Governor." + }, + "lastVerifiedEthBalance()": { + "notice": "The last verified ETH balance of the strategy" + }, + "nextDepositID()": { + "notice": "Unique identifier of the next validator deposit." + }, + "platformAddress()": { + "notice": "Address of the underlying platform" + }, + "registerSsvValidator(bytes,uint64[],bytes,uint256,(uint32,uint64,uint64,bool,uint256))": { + "notice": "Registers a single validator in a SSV Cluster. Only the Registrator can call this function." + }, + "removePToken(uint256)": { + "notice": "is not supported for this strategy as there is no platform token." + }, + "removeSsvValidator(bytes,uint64[],(uint32,uint64,uint64,bool,uint256))": { + "notice": "Remove the validator from the SSV Cluster after: - the validator has been exited from `validatorWithdrawal` or slashed - the validator has incorrectly registered and can not be staked to - the initial deposit was front-run and the withdrawal address is not this strategy's address. Make sure `validatorWithdrawal` is called with a zero amount and the validator has exited the Beacon chain. If removed before the validator has exited the beacon chain will result in the validator being slashed. Only the registrator can call this function." + }, + "resetFirstDeposit()": { + "notice": "Reset the `firstDeposit` flag to false so deposits to unverified validators can be made again." + }, + "rewardTokenAddresses(uint256)": { + "notice": "Address of the reward tokens. eg CRV, BAL, CVX, AURA" + }, + "safeApproveAllTokens()": { + "notice": "Approves the SSV Network contract to transfer SSV tokens for validator registration." + }, + "setHarvesterAddress(address)": { + "notice": "Set the Harvester contract that can collect rewards." + }, + "setPTokenAddress(address,address)": { + "notice": "is not supported for this strategy as there is no platform token." + }, + "setRegistrator(address)": { + "notice": "Set the address of the registrator which can register, exit and remove validators" + }, + "setRewardTokenAddresses(address[])": { + "notice": "Set the reward token addresses. Any old addresses will be overwritten." + }, + "snapBalances()": { + "notice": "Stores the current ETH balance at the current block and beacon block root of the slot that is associated with the previous block. When snapping / verifying balance it is of a high importance that there is no miss-match in respect to ETH that is held by the contract and balances that are verified on the validators. First some context on the beacon-chain block building behaviour. Relevant parts of constructing a block on the beacon chain consist of: - process_withdrawals: ETH is deducted from the validator's balance - process_execution_payload: immediately after the previous step executing all the transactions - apply the withdrawals: adding ETH to the recipient which is the withdrawal address contained in the withdrawal credentials of the exited validators That means that balance increases which are part of the post-block execution state are done within the block, but the transaction that are contained within that block can not see / interact with the balance from the exited validators. Only transactions in the next block can do that. When snap balances is performed the state of the chain is snapped across 2 separate chain states: - ETH balance of the contract is recorded on block X -> and corresponding slot Y - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1 given there were no missed slots. It could also be Y - 2, Y - 3 depending on how many slots have not managed to propose a block. For the sake of simplicity this slot will be referred to as Y - 1 as it makes no difference in the argument Given these 2 separate chain states it is paramount that verify balances can not experience miss-counting ETH or much more dangerous double counting of the ETH. When verifyBalances is called it is performed on the current block Z where Z > X. Verify balances adds up all the ETH (omitting WETH) controlled by this contract: - ETH balance in the contract on block X - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1 - ETH balance in validators that are active in slot Y - 1 - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner) and have their balance visible to transactions in slot Y and corresponding block X (or sooner) Lets verify the correctness of ETH accounting given the above described behaviour. *ETH balance in the contract on block X* This is an ETH balance of the contract on a non current X block. Any ETH leaving the contract as a result of a withdrawal subtracts from the ETH accounted for on block X if `verifyBalances` has already been called. It also invalidates a `snapBalances` in case `verifyBalances` has not been called yet. Not performing this would result in not accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z]. Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH accounted for since the last `verifyBalances` has been called. And it invalidates the `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this would result in double counting the `stakedEth` since it would be present once in the snapped contract balance and the second time in deposit storage variables. This behaviour is correct. *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1* The contract sums up all the ETH that has been deposited to the Beacon chain deposit contract at block Z. The execution layer doesn't have direct access to the state of deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be deposited it needs to be sure to not double count ETH that is in deposits (storage vars) and could also be part of the validator balances. It does that by verifying that at slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since the last snap till now all are still in queue. Which ensures they can not be part of the validator balances in later steps. This behaviour is correct. *ETH balance in validators that are active in slot Y - 1* The contract is verifying none of the deposits on Y - 1 slot have been processed and for that reason it checks the validator balances in the same slot. Ensuring accounting correctness. This behaviour is correct. *The withdrawn validators* The withdrawn validators could have their balances deducted in any slot before slot Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets look at the \"worst case scenario\" where the validator withdrawal is processed in the slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot Y -> block X. The ETH balance on the contract is snapped at block X meaning that even if the validator exits at the latest possible time it is paramount that the ETH balance on the execution layer is recorded in the next block. Correctly accounting for the withdrawn ETH. Worth mentioning if the validator exit is processed by the slot Y and balance increase seen on the execution layer on block X + 1 the withdrawal is ignored by both the validator balance verification as well as execution layer contract balance snap. This behaviour is correct. The validator balances on the beacon chain can then be proved with `verifyBalances`." + }, + "snappedBalance()": { + "notice": "Mapping of the block root to the balances at that slot" + }, + "stakeEth((bytes,bytes,bytes32),uint64)": { + "notice": "Stakes WETH in this strategy to a compounding validator. Does not convert any ETH sitting in this strategy to WETH." + }, + "supportsAsset(address)": { + "notice": "Returns bool indicating whether asset is supported by the strategy." + }, + "transferGovernance(address)": { + "notice": "Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete" + }, + "transferToken(address,uint256)": { + "notice": "Transfer token to governor. Intended for recovering tokens stuck in strategy contracts, i.e. mistaken sends." + }, + "validator(bytes32)": { + "notice": "Mapping of the hash of the validator's public key to the validator state and index. Uses the Beacon chain hashing for BLSPubkey which is sha256(abi.encodePacked(validator.pubkey, bytes16(0)))" + }, + "validatorRegistrator()": { + "notice": "Address of the registrator - allowed to register, withdraw, exit and remove validators" + }, + "validatorWithdrawal(bytes,uint64)": { + "notice": "Request a full or partial withdrawal from a validator. A zero amount will trigger a full withdrawal. If the remaining balance is < 32 ETH then only the amount in excess of 32 ETH will be withdrawn. Only the Registrator can call this function. 1 wei of value should be sent with the tx to pay for the withdrawal request fee. If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any. If no ETH balance, the tx will revert." + }, + "vaultAddress()": { + "notice": "Address of the OToken vault" + }, + "verifiedValidators(uint256)": { + "notice": "List of validator public key hashes that have been verified to exist on the beacon chain. These have had a deposit processed and the validator's balance increased. Validators will be removed from this list when its verified they have a zero balance." + }, + "verifiedValidatorsLength()": { + "notice": "Returns the number of verified validators." + }, + "verifyBalances(uint64,(uint64,uint40,bytes32,bytes,bytes,bytes),(bytes32,bytes,bytes32[],bytes[]))": { + "notice": "Verifies the balances of all active validators on the beacon chain and checks no pending deposits have been processed by the beacon chain." + }, + "verifyDeposit(uint256,uint64,(uint64,bytes),(uint64,bytes))": { + "notice": "Verifies a deposit on the execution layer has been processed by the beacon chain. This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance. Important: this function has a limitation where `depositProcessedSlot` and `firstDepositValidatorCreatedSlot` that is passed by the off-chain verifier requires a slot immediately after them to propose a block otherwise the `BeaconRoots.parentBlockRoot` will fail. This shouldn't be a problem, since by the current behaviour of beacon chain only 1%-3% slots don't propose a block." + }, + "verifyValidator(uint64,uint40,bytes32,address,bytes)": { + "notice": "Verifies a validator's index to its public key. Adds to the list of verified validators if the validator's withdrawal address is this strategy's address. Marks the validator as invalid and removes the deposit if the withdrawal address is not this strategy's address." + }, + "withdraw(address,address,uint256)": { + "notice": "Withdraw ETH and WETH from this strategy contract." + }, + "withdrawAll()": { + "notice": "Transfer all WETH deposits, ETH from validator withdrawals and ETH from execution rewards in this strategy to the vault. This does not withdraw from the validators. That has to be done separately with the `validatorWithdrawal` operation." + }, + "withdrawSSV(uint64[],uint256,(uint32,uint64,uint64,bool,uint256))": { + "notice": "Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators." + } + }, + "notice": "Strategy to deploy funds into DVT validators powered by the SSV Network", + "version": 1 + }, + "storageLayout": { + "storage": [ + { + "astId": 8091, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "initialized", + "offset": 0, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 8094, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "initializing", + "offset": 1, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 8134, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "______gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage" + }, + { + "astId": 4692, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "validatorRegistrator", + "offset": 0, + "slot": "51", + "type": "t_address" + }, + { + "astId": 4715, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "firstDeposit", + "offset": 20, + "slot": "51", + "type": "t_bool" + }, + { + "astId": 4718, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "nextDepositID", + "offset": 0, + "slot": "52", + "type": "t_uint128" + }, + { + "astId": 4724, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "deposits", + "offset": 0, + "slot": "53", + "type": "t_mapping(t_uint256,t_struct(DepositData)4712_storage)" + }, + { + "astId": 4728, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "depositList", + "offset": 0, + "slot": "54", + "type": "t_array(t_uint256)dyn_storage" + }, + { + "astId": 4747, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "verifiedValidators", + "offset": 0, + "slot": "55", + "type": "t_array(t_bytes32)dyn_storage" + }, + { + "astId": 4753, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "validator", + "offset": 0, + "slot": "56", + "type": "t_mapping(t_bytes32,t_struct(ValidatorData)4743_storage)" + }, + { + "astId": 4765, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "snappedBalance", + "offset": 0, + "slot": "57", + "type": "t_struct(Balances)4761_storage" + }, + { + "astId": 4768, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "lastVerifiedEthBalance", + "offset": 0, + "slot": "59", + "type": "t_uint256" + }, + { + "astId": 4771, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "depositedWethAccountedFor", + "offset": 0, + "slot": "60", + "type": "t_uint256" + }, + { + "astId": 4775, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "__gap", + "offset": 0, + "slot": "61", + "type": "t_array(t_uint256)50_storage" + }, + { + "astId": 8214, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "_deprecated_platformAddress", + "offset": 0, + "slot": "111", + "type": "t_address" + }, + { + "astId": 8217, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "_deprecated_vaultAddress", + "offset": 0, + "slot": "112", + "type": "t_address" + }, + { + "astId": 8222, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "assetToPToken", + "offset": 0, + "slot": "113", + "type": "t_mapping(t_address,t_address)" + }, + { + "astId": 8226, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "assetsMapped", + "offset": 0, + "slot": "114", + "type": "t_array(t_address)dyn_storage" + }, + { + "astId": 8228, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "_deprecated_rewardTokenAddress", + "offset": 0, + "slot": "115", + "type": "t_address" + }, + { + "astId": 8230, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "_deprecated_rewardLiquidationThreshold", + "offset": 0, + "slot": "116", + "type": "t_uint256" + }, + { + "astId": 8233, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "harvesterAddress", + "offset": 0, + "slot": "117", + "type": "t_address" + }, + { + "astId": 8237, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "rewardTokenAddresses", + "offset": 0, + "slot": "118", + "type": "t_array(t_address)dyn_storage" + }, + { + "astId": 8241, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "_reserved", + "offset": 0, + "slot": "119", + "type": "t_array(t_int256)98_storage" + }, + { + "astId": 4033, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "__gap", + "offset": 0, + "slot": "217", + "type": "t_array(t_uint256)50_storage" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_address)dyn_storage": { + "base": "t_address", + "encoding": "dynamic_array", + "label": "address[]", + "numberOfBytes": "32" + }, + "t_array(t_bytes32)dyn_storage": { + "base": "t_bytes32", + "encoding": "dynamic_array", + "label": "bytes32[]", + "numberOfBytes": "32" + }, + "t_array(t_int256)98_storage": { + "base": "t_int256", + "encoding": "inplace", + "label": "int256[98]", + "numberOfBytes": "3136" + }, + "t_array(t_uint256)50_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_array(t_uint256)dyn_storage": { + "base": "t_uint256", + "encoding": "dynamic_array", + "label": "uint256[]", + "numberOfBytes": "32" + }, + "t_bool": { + "encoding": "inplace", + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes32": { + "encoding": "inplace", + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_enum(DepositStatus)4697": { + "encoding": "inplace", + "label": "enum CompoundingValidatorManager.DepositStatus", + "numberOfBytes": "1" + }, + "t_enum(ValidatorState)4737": { + "encoding": "inplace", + "label": "enum CompoundingValidatorManager.ValidatorState", + "numberOfBytes": "1" + }, + "t_int256": { + "encoding": "inplace", + "label": "int256", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_address)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => address)", + "numberOfBytes": "32", + "value": "t_address" + }, + "t_mapping(t_bytes32,t_struct(ValidatorData)4743_storage)": { + "encoding": "mapping", + "key": "t_bytes32", + "label": "mapping(bytes32 => struct CompoundingValidatorManager.ValidatorData)", + "numberOfBytes": "32", + "value": "t_struct(ValidatorData)4743_storage" + }, + "t_mapping(t_uint256,t_struct(DepositData)4712_storage)": { + "encoding": "mapping", + "key": "t_uint256", + "label": "mapping(uint256 => struct CompoundingValidatorManager.DepositData)", + "numberOfBytes": "32", + "value": "t_struct(DepositData)4712_storage" + }, + "t_struct(Balances)4761_storage": { + "encoding": "inplace", + "label": "struct CompoundingValidatorManager.Balances", + "members": [ + { + "astId": 4756, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "blockRoot", + "offset": 0, + "slot": "0", + "type": "t_bytes32" + }, + { + "astId": 4758, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "timestamp", + "offset": 0, + "slot": "1", + "type": "t_uint64" + }, + { + "astId": 4760, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "ethBalance", + "offset": 8, + "slot": "1", + "type": "t_uint128" + } + ], + "numberOfBytes": "64" + }, + "t_struct(DepositData)4712_storage": { + "encoding": "inplace", + "label": "struct CompoundingValidatorManager.DepositData", + "members": [ + { + "astId": 4700, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "pubKeyHash", + "offset": 0, + "slot": "0", + "type": "t_bytes32" + }, + { + "astId": 4702, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "amountGwei", + "offset": 0, + "slot": "1", + "type": "t_uint64" + }, + { + "astId": 4704, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "slot", + "offset": 8, + "slot": "1", + "type": "t_uint64" + }, + { + "astId": 4706, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "depositIndex", + "offset": 16, + "slot": "1", + "type": "t_uint32" + }, + { + "astId": 4709, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "status", + "offset": 20, + "slot": "1", + "type": "t_enum(DepositStatus)4697" + }, + { + "astId": 4711, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "withdrawableEpoch", + "offset": 21, + "slot": "1", + "type": "t_uint64" + } + ], + "numberOfBytes": "64" + }, + "t_struct(ValidatorData)4743_storage": { + "encoding": "inplace", + "label": "struct CompoundingValidatorManager.ValidatorData", + "members": [ + { + "astId": 4740, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "state", + "offset": 0, + "slot": "0", + "type": "t_enum(ValidatorState)4737" + }, + { + "astId": 4742, + "contract": "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol:CompoundingStakingSSVStrategy", + "label": "index", + "offset": 1, + "slot": "0", + "type": "t_uint40" + } + ], + "numberOfBytes": "32" + }, + "t_uint128": { + "encoding": "inplace", + "label": "uint128", + "numberOfBytes": "16" + }, + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint32": { + "encoding": "inplace", + "label": "uint32", + "numberOfBytes": "4" + }, + "t_uint40": { + "encoding": "inplace", + "label": "uint40", + "numberOfBytes": "5" + }, + "t_uint64": { + "encoding": "inplace", + "label": "uint64", + "numberOfBytes": "8" + } + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/CompoundingStakingSSVStrategyP2PProxy.json b/contracts/deployments/hoodi/CompoundingStakingSSVStrategyP2PProxy.json new file mode 100644 index 0000000000..1bb7db354f --- /dev/null +++ b/contracts/deployments/hoodi/CompoundingStakingSSVStrategyP2PProxy.json @@ -0,0 +1,297 @@ +{ + "address": "0xE0c7B62f75c54Cc75573965C566f37B266F216FB", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "GovernorshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "PendingGovernorshipTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "claimGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "governor", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "implementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_logic", + "type": "address" + }, + { + "internalType": "address", + "name": "_initGovernor", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "isGovernor", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newGovernor", + "type": "address" + } + ], + "name": "transferGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } + ], + "transactionHash": "0x97e6c7fafdb9762a0b471e4ae036ab52cb36e827c5d7d45c50741ddebedfcf5a", + "receipt": { + "to": null, + "from": "0xf7749B41db006860cEc0650D18b8013d69C44Eeb", + "contractAddress": "0xE0c7B62f75c54Cc75573965C566f37B266F216FB", + "transactionIndex": 11, + "gasUsed": "599335", + "logsBloom": "0x00000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000200800000000000000000000000040000000000004000000000000000000000000000000000000000000000000000000000000000000000000000100000000000020000000000000000000000000000000000000000000000000010010000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x8ae6e4386b469b2710ecfa69c80d8a31b9b68328bc026006acd897cc732bda94", + "transactionHash": "0x97e6c7fafdb9762a0b471e4ae036ab52cb36e827c5d7d45c50741ddebedfcf5a", + "logs": [ + { + "transactionIndex": 11, + "blockNumber": 1068399, + "transactionHash": "0x97e6c7fafdb9762a0b471e4ae036ab52cb36e827c5d7d45c50741ddebedfcf5a", + "address": "0xE0c7B62f75c54Cc75573965C566f37B266F216FB", + "topics": [ + "0xc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000f7749b41db006860cec0650d18b8013d69c44eeb" + ], + "data": "0x", + "logIndex": 31, + "blockHash": "0x8ae6e4386b469b2710ecfa69c80d8a31b9b68328bc026006acd897cc732bda94" + } + ], + "blockNumber": 1068399, + "cumulativeGasUsed": "7690588", + "status": 1, + "byzantium": true + }, + "args": [], + "numDeployments": 1, + "solcInputHash": "3152748504b6a5234f27bb6386288929", + "metadata": "{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"GovernorshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"PendingGovernorshipTransfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"implementation\",\"type\":\"address\"}],\"name\":\"Upgraded\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"governor\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"implementation\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_logic\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_initGovernor\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isGovernor\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newGovernor\",\"type\":\"address\"}],\"name\":\"transferGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newImplementation\",\"type\":\"address\"}],\"name\":\"upgradeTo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"upgradeToAndCall\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"}],\"devdoc\":{\"events\":{\"Upgraded(address)\":{\"details\":\"Emitted when the implementation is upgraded.\",\"params\":{\"implementation\":\"Address of the new implementation.\"}}},\"kind\":\"dev\",\"methods\":{\"admin()\":{\"returns\":{\"_0\":\"The address of the proxy admin/it's also the governor.\"}},\"implementation()\":{\"returns\":{\"_0\":\"The address of the implementation.\"}},\"initialize(address,address,bytes)\":{\"details\":\"Contract initializer with Governor enforcement\",\"params\":{\"_data\":\"Data to send as msg.data to the implementation to initialize the proxied contract. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.\",\"_initGovernor\":\"Address of the initial Governor.\",\"_logic\":\"Address of the initial implementation.\"}},\"transferGovernance(address)\":{\"params\":{\"_newGovernor\":\"Address of the new Governor\"}},\"upgradeTo(address)\":{\"details\":\"Upgrade the backing implementation of the proxy. Only the admin can call this function.\",\"params\":{\"_newImplementation\":\"Address of the new implementation.\"}},\"upgradeToAndCall(address,bytes)\":{\"details\":\"Upgrade the backing implementation of the proxy and call a function on the new implementation. This is useful to initialize the proxied contract.\",\"params\":{\"data\":\"Data to send as msg.data in the low level call. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\",\"newImplementation\":\"Address of the new implementation.\"}}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"claimGovernance()\":{\"notice\":\"Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor.\"},\"governor()\":{\"notice\":\"Returns the address of the current Governor.\"},\"isGovernor()\":{\"notice\":\"Returns true if the caller is the current Governor.\"},\"transferGovernance(address)\":{\"notice\":\"Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete\"}},\"notice\":\"CompoundingStakingSSVStrategyProxy delegates calls to a CompoundingStakingSSVStrategy implementation\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/proxies/Proxies.sol\":\"CompoundingStakingSSVStrategyProxy\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize, which returns 0 for contracts in\\n // construction, since the code is only stored at the end of the\\n // constructor execution.\\n\\n uint256 size;\\n assembly {\\n size := extcodesize(account)\\n }\\n return size > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCall(target, data, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n require(isContract(target), \\\"Address: static call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(isContract(target), \\\"Address: delegate call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x51b758a8815ecc9596c66c37d56b1d33883a444631a3f916b9fe65cb863ef7c4\",\"license\":\"MIT\"},\"contracts/governance/Governable.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\\n * from owner to governor and renounce methods removed. Does not use\\n * Context.sol like Ownable.sol does for simplification.\\n * @author Origin Protocol Inc\\n */\\nabstract contract Governable {\\n // Storage position of the owner and pendingOwner of the contract\\n // keccak256(\\\"OUSD.governor\\\");\\n bytes32 private constant governorPosition =\\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\\n\\n // keccak256(\\\"OUSD.pending.governor\\\");\\n bytes32 private constant pendingGovernorPosition =\\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\\n\\n // keccak256(\\\"OUSD.reentry.status\\\");\\n bytes32 private constant reentryStatusPosition =\\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\\n\\n // See OpenZeppelin ReentrancyGuard implementation\\n uint256 constant _NOT_ENTERED = 1;\\n uint256 constant _ENTERED = 2;\\n\\n event PendingGovernorshipTransfer(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n event GovernorshipTransferred(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n /**\\n * @notice Returns the address of the current Governor.\\n */\\n function governor() public view returns (address) {\\n return _governor();\\n }\\n\\n /**\\n * @dev Returns the address of the current Governor.\\n */\\n function _governor() internal view returns (address governorOut) {\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n governorOut := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Returns the address of the pending Governor.\\n */\\n function _pendingGovernor()\\n internal\\n view\\n returns (address pendingGovernor)\\n {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n pendingGovernor := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the Governor.\\n */\\n modifier onlyGovernor() {\\n require(isGovernor(), \\\"Caller is not the Governor\\\");\\n _;\\n }\\n\\n /**\\n * @notice Returns true if the caller is the current Governor.\\n */\\n function isGovernor() public view returns (bool) {\\n return msg.sender == _governor();\\n }\\n\\n function _setGovernor(address newGovernor) internal {\\n emit GovernorshipTransferred(_governor(), newGovernor);\\n\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @dev Prevents a contract from calling itself, directly or indirectly.\\n * Calling a `nonReentrant` function from another `nonReentrant`\\n * function is not supported. It is possible to prevent this from happening\\n * by making the `nonReentrant` function external, and make it call a\\n * `private` function that does the actual work.\\n */\\n modifier nonReentrant() {\\n bytes32 position = reentryStatusPosition;\\n uint256 _reentry_status;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n _reentry_status := sload(position)\\n }\\n\\n // On the first call to nonReentrant, _notEntered will be true\\n require(_reentry_status != _ENTERED, \\\"Reentrant call\\\");\\n\\n // Any calls to nonReentrant after this point will fail\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _ENTERED)\\n }\\n\\n _;\\n\\n // By storing the original value once again, a refund is triggered (see\\n // https://eips.ethereum.org/EIPS/eip-2200)\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _NOT_ENTERED)\\n }\\n }\\n\\n function _setPendingGovernor(address newGovernor) internal {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the current Governor. Must be claimed for this to complete\\n * @param _newGovernor Address of the new Governor\\n */\\n function transferGovernance(address _newGovernor) external onlyGovernor {\\n _setPendingGovernor(_newGovernor);\\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\\n }\\n\\n /**\\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the new Governor.\\n */\\n function claimGovernance() external {\\n require(\\n msg.sender == _pendingGovernor(),\\n \\\"Only the pending Governor can complete the claim\\\"\\n );\\n _changeGovernor(msg.sender);\\n }\\n\\n /**\\n * @dev Change Governance of the contract to a new account (`newGovernor`).\\n * @param _newGovernor Address of the new Governor\\n */\\n function _changeGovernor(address _newGovernor) internal {\\n require(_newGovernor != address(0), \\\"New Governor is address(0)\\\");\\n _setGovernor(_newGovernor);\\n }\\n}\\n\",\"keccak256\":\"0xf32f873c8bfbacf2e5f01d0cf37bc7f54fbd5aa656e95c8a599114229946f107\",\"license\":\"BUSL-1.1\"},\"contracts/proxies/InitializeGovernedUpgradeabilityProxy.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { Address } from \\\"@openzeppelin/contracts/utils/Address.sol\\\";\\n\\nimport { Governable } from \\\"../governance/Governable.sol\\\";\\n\\n/**\\n * @title BaseGovernedUpgradeabilityProxy\\n * @dev This contract combines an upgradeability proxy with our governor system.\\n * It is based on an older version of OpenZeppelins BaseUpgradeabilityProxy\\n * with Solidity ^0.8.0.\\n * @author Origin Protocol Inc\\n */\\ncontract InitializeGovernedUpgradeabilityProxy is Governable {\\n /**\\n * @dev Emitted when the implementation is upgraded.\\n * @param implementation Address of the new implementation.\\n */\\n event Upgraded(address indexed implementation);\\n\\n constructor() {\\n _setGovernor(msg.sender);\\n }\\n\\n /**\\n * @dev Contract initializer with Governor enforcement\\n * @param _logic Address of the initial implementation.\\n * @param _initGovernor Address of the initial Governor.\\n * @param _data Data to send as msg.data to the implementation to initialize\\n * the proxied contract.\\n * It should include the signature and the parameters of the function to be\\n * called, as described in\\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\\n * This parameter is optional, if no data is given the initialization call\\n * to proxied contract will be skipped.\\n */\\n function initialize(\\n address _logic,\\n address _initGovernor,\\n bytes calldata _data\\n ) public payable onlyGovernor {\\n require(_implementation() == address(0));\\n require(_logic != address(0), \\\"Implementation not set\\\");\\n assert(\\n IMPLEMENTATION_SLOT ==\\n bytes32(uint256(keccak256(\\\"eip1967.proxy.implementation\\\")) - 1)\\n );\\n _setImplementation(_logic);\\n if (_data.length > 0) {\\n (bool success, ) = _logic.delegatecall(_data);\\n require(success);\\n }\\n _changeGovernor(_initGovernor);\\n }\\n\\n /**\\n * @return The address of the proxy admin/it's also the governor.\\n */\\n function admin() external view returns (address) {\\n return _governor();\\n }\\n\\n /**\\n * @return The address of the implementation.\\n */\\n function implementation() external view returns (address) {\\n return _implementation();\\n }\\n\\n /**\\n * @dev Upgrade the backing implementation of the proxy.\\n * Only the admin can call this function.\\n * @param _newImplementation Address of the new implementation.\\n */\\n function upgradeTo(address _newImplementation) external onlyGovernor {\\n _upgradeTo(_newImplementation);\\n }\\n\\n /**\\n * @dev Upgrade the backing implementation of the proxy and call a function\\n * on the new implementation.\\n * This is useful to initialize the proxied contract.\\n * @param newImplementation Address of the new implementation.\\n * @param data Data to send as msg.data in the low level call.\\n * It should include the signature and the parameters of the function to be called, as described in\\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\\n */\\n function upgradeToAndCall(address newImplementation, bytes calldata data)\\n external\\n payable\\n onlyGovernor\\n {\\n _upgradeTo(newImplementation);\\n (bool success, ) = newImplementation.delegatecall(data);\\n require(success);\\n }\\n\\n /**\\n * @dev Fallback function.\\n * Implemented entirely in `_fallback`.\\n */\\n fallback() external payable {\\n _fallback();\\n }\\n\\n /**\\n * @dev Delegates execution to an implementation contract.\\n * This is a low level function that doesn't return to its internal call site.\\n * It will return to the external caller whatever the implementation returns.\\n * @param _impl Address to delegate.\\n */\\n function _delegate(address _impl) internal {\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n // Copy msg.data. We take full control of memory in this inline assembly\\n // block because it will not return to Solidity code. We overwrite the\\n // Solidity scratch pad at memory position 0.\\n calldatacopy(0, 0, calldatasize())\\n\\n // Call the implementation.\\n // out and outsize are 0 because we don't know the size yet.\\n let result := delegatecall(gas(), _impl, 0, calldatasize(), 0, 0)\\n\\n // Copy the returned data.\\n returndatacopy(0, 0, returndatasize())\\n\\n switch result\\n // delegatecall returns 0 on error.\\n case 0 {\\n revert(0, returndatasize())\\n }\\n default {\\n return(0, returndatasize())\\n }\\n }\\n }\\n\\n /**\\n * @dev Function that is run as the first thing in the fallback function.\\n * Can be redefined in derived contracts to add functionality.\\n * Redefinitions must call super._willFallback().\\n */\\n function _willFallback() internal {}\\n\\n /**\\n * @dev fallback implementation.\\n * Extracted to enable manual triggering.\\n */\\n function _fallback() internal {\\n _willFallback();\\n _delegate(_implementation());\\n }\\n\\n /**\\n * @dev Storage slot with the address of the current implementation.\\n * This is the keccak-256 hash of \\\"eip1967.proxy.implementation\\\" subtracted by 1, and is\\n * validated in the constructor.\\n */\\n bytes32 internal constant IMPLEMENTATION_SLOT =\\n 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\\n\\n /**\\n * @dev Returns the current implementation.\\n * @return impl Address of the current implementation\\n */\\n function _implementation() internal view returns (address impl) {\\n bytes32 slot = IMPLEMENTATION_SLOT;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n impl := sload(slot)\\n }\\n }\\n\\n /**\\n * @dev Upgrades the proxy to a new implementation.\\n * @param newImplementation Address of the new implementation.\\n */\\n function _upgradeTo(address newImplementation) internal {\\n _setImplementation(newImplementation);\\n emit Upgraded(newImplementation);\\n }\\n\\n /**\\n * @dev Sets the implementation address of the proxy.\\n * @param newImplementation Address of the new implementation.\\n */\\n function _setImplementation(address newImplementation) internal {\\n require(\\n Address.isContract(newImplementation),\\n \\\"Cannot set a proxy implementation to a non-contract address\\\"\\n );\\n\\n bytes32 slot = IMPLEMENTATION_SLOT;\\n\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(slot, newImplementation)\\n }\\n }\\n}\\n\",\"keccak256\":\"0x3f46ae39dced6fa90d8b65aa31a0a331438544ec876e2ec961a8e6b22e2e06c7\",\"license\":\"BUSL-1.1\"},\"contracts/proxies/Proxies.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { InitializeGovernedUpgradeabilityProxy } from \\\"./InitializeGovernedUpgradeabilityProxy.sol\\\";\\n\\n/**\\n * @notice OUSDProxy delegates calls to an OUSD implementation\\n */\\ncontract OUSDProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice WrappedOUSDProxy delegates calls to a WrappedOUSD implementation\\n */\\ncontract WrappedOUSDProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice VaultProxy delegates calls to a Vault implementation\\n */\\ncontract VaultProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice CompoundStrategyProxy delegates calls to a CompoundStrategy implementation\\n */\\ncontract CompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice AaveStrategyProxy delegates calls to a AaveStrategy implementation\\n */\\ncontract AaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice ConvexStrategyProxy delegates calls to a ConvexStrategy implementation\\n */\\ncontract ConvexStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice HarvesterProxy delegates calls to a Harvester implementation\\n */\\ncontract HarvesterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice DripperProxy delegates calls to a Dripper implementation\\n */\\ncontract DripperProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice MorphoCompoundStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\\n */\\ncontract MorphoCompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice ConvexOUSDMetaStrategyProxy delegates calls to a ConvexOUSDMetaStrategy implementation\\n */\\ncontract ConvexOUSDMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice MorphoAaveStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\\n */\\ncontract MorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHProxy delegates calls to nowhere for now\\n */\\ncontract OETHProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice WOETHProxy delegates calls to nowhere for now\\n */\\ncontract WOETHProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHVaultProxy delegates calls to a Vault implementation\\n */\\ncontract OETHVaultProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHDripperProxy delegates calls to a OETHDripper implementation\\n */\\ncontract OETHDripperProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHHarvesterProxy delegates calls to a Harvester implementation\\n */\\ncontract OETHHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice CurveEthStrategyProxy delegates calls to a CurveEthStrategy implementation\\n */\\ncontract ConvexEthMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice BuybackProxy delegates calls to Buyback implementation\\n */\\ncontract BuybackProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHMorphoAaveStrategyProxy delegates calls to a MorphoAaveStrategy implementation\\n */\\ncontract OETHMorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHBalancerMetaPoolrEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\\n */\\ncontract OETHBalancerMetaPoolrEthStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice OETHBalancerMetaPoolwstEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\\n */\\ncontract OETHBalancerMetaPoolwstEthStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MakerDsrStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MakerDsrStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHBuybackProxy delegates calls to Buyback implementation\\n */\\ncontract OETHBuybackProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice BridgedWOETHProxy delegates calls to BridgedWOETH implementation\\n */\\ncontract BridgedWOETHProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice NativeStakingSSVStrategyProxy delegates calls to NativeStakingSSVStrategy implementation\\n */\\ncontract NativeStakingSSVStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingFeeAccumulatorProxy delegates calls to FeeAccumulator implementation\\n */\\ncontract NativeStakingFeeAccumulatorProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingSSVStrategy2Proxy delegates calls to NativeStakingSSVStrategy implementation\\n */\\ncontract NativeStakingSSVStrategy2Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingFeeAccumulator2Proxy delegates calls to FeeAccumulator implementation\\n */\\ncontract NativeStakingFeeAccumulator2Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingSSVStrategy3Proxy delegates calls to NativeStakingSSVStrategy implementation\\n */\\ncontract NativeStakingSSVStrategy3Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingFeeAccumulator3Proxy delegates calls to FeeAccumulator implementation\\n */\\ncontract NativeStakingFeeAccumulator3Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MetaMorphoStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MetaMorphoStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice ARMBuybackProxy delegates calls to Buyback implementation\\n */\\ncontract ARMBuybackProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice MorphoGauntletPrimeUSDCStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MorphoGauntletPrimeUSDCStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MorphoGauntletPrimeUSDTStrategyProxy delegates calls to a Generalized4626USDTStrategy implementation\\n */\\ncontract MorphoGauntletPrimeUSDTStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice CurvePoolBoosterProxy delegates calls to a CurvePoolBooster implementation\\n */\\ncontract CurvePoolBoosterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHFixedRateDripperProxy delegates calls to a OETHFixedRateDripper implementation\\n */\\ncontract OETHFixedRateDripperProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHSimpleHarvesterProxy delegates calls to a OETHSimpleHarvester implementation\\n */\\ncontract OETHSimpleHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice PoolBoostCentralRegistryProxy delegates calls to the PoolBoostCentralRegistry implementation\\n */\\ncontract PoolBoostCentralRegistryProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MakerSSRStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MakerSSRStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OUSDCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\\n */\\ncontract OUSDCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\\n */\\ncontract OETHCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice CompoundingStakingSSVStrategyProxy delegates calls to a CompoundingStakingSSVStrategy implementation\\n */\\ncontract CompoundingStakingSSVStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\",\"keccak256\":\"0x71e10cf279337e54682c7b0132825fa307992782367bd296c44b664b7b705db4\",\"license\":\"BUSL-1.1\"}},\"version\":1}", + "bytecode": "0x6080604052348015600f57600080fd5b50601733601b565b6081565b6001600160a01b038116603a6000805160206109f98339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a36000805160206109f983398151915255565b610969806100906000396000f3fe6080604052600436106100865760003560e01c80635d36b190116100595780635d36b1901461010a578063c7af33521461011f578063cf7a1d7714610144578063d38bfff414610157578063f851a4401461009057610086565b80630c340a24146100905780633659cfe6146100c25780634f1ef286146100e25780635c60da1b146100f5575b61008e610177565b005b34801561009c57600080fd5b506100a5610197565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100ce57600080fd5b5061008e6100dd366004610750565b6101b4565b61008e6100f03660046107bb565b6101ed565b34801561010157600080fd5b506100a561028a565b34801561011657600080fd5b5061008e6102a2565b34801561012b57600080fd5b50610134610346565b60405190151581526020016100b9565b61008e61015236600461080e565b610377565b34801561016357600080fd5b5061008e610172366004610750565b6104e0565b6101956101906000805160206108f48339815191525490565b610584565b565b60006101af6000805160206109148339815191525490565b905090565b6101bc610346565b6101e15760405162461bcd60e51b81526004016101d89061086f565b60405180910390fd5b6101ea816105a8565b50565b6101f5610346565b6102115760405162461bcd60e51b81526004016101d89061086f565b61021a836105a8565b6000836001600160a01b031683836040516102369291906108a6565b600060405180830381855af49150503d8060008114610271576040519150601f19603f3d011682016040523d82523d6000602084013e610276565b606091505b505090508061028457600080fd5b50505050565b60006101af6000805160206108f48339815191525490565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b03161461033d5760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b60648201526084016101d8565b610195336105e8565b600061035e6000805160206109148339815191525490565b6001600160a01b0316336001600160a01b031614905090565b61037f610346565b61039b5760405162461bcd60e51b81526004016101d89061086f565b60006103b36000805160206108f48339815191525490565b6001600160a01b0316146103c657600080fd5b6001600160a01b0384166104155760405162461bcd60e51b8152602060048201526016602482015275125b5c1b195b595b9d185d1a5bdb881b9bdd081cd95d60521b60448201526064016101d8565b61044060017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd6108b6565b6000805160206108f48339815191521461045c5761045c6108dd565b61046584610647565b80156104d7576000846001600160a01b031683836040516104879291906108a6565b600060405180830381855af49150503d80600081146104c2576040519150601f19603f3d011682016040523d82523d6000602084013e6104c7565b606091505b50509050806104d557600080fd5b505b610284836105e8565b6104e8610346565b6105045760405162461bcd60e51b81526004016101d89061086f565b61052c817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b031661054c6000805160206109148339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b3660008037600080366000845af43d6000803e8080156105a3573d6000f35b3d6000fd5b6105b181610647565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b03811661063e5760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f72206973206164647265737328302900000000000060448201526064016101d8565b6101ea816106cd565b803b6106bb5760405162461bcd60e51b815260206004820152603b60248201527f43616e6e6f742073657420612070726f787920696d706c656d656e746174696f60448201527f6e20746f2061206e6f6e2d636f6e74726163742061646472657373000000000060648201526084016101d8565b6000805160206108f483398151915255565b806001600160a01b03166106ed6000805160206109148339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a360008051602061091483398151915255565b80356001600160a01b038116811461074b57600080fd5b919050565b60006020828403121561076257600080fd5b61076b82610734565b9392505050565b60008083601f84011261078457600080fd5b50813567ffffffffffffffff81111561079c57600080fd5b6020830191508360208285010111156107b457600080fd5b9250929050565b6000806000604084860312156107d057600080fd5b6107d984610734565b9250602084013567ffffffffffffffff8111156107f557600080fd5b61080186828701610772565b9497909650939450505050565b6000806000806060858703121561082457600080fd5b61082d85610734565b935061083b60208601610734565b9250604085013567ffffffffffffffff81111561085757600080fd5b61086387828801610772565b95989497509550505050565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b8183823760009101908152919050565b818103818111156108d757634e487b7160e01b600052601160045260246000fd5b92915050565b634e487b7160e01b600052600160045260246000fdfe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa26469706673582212201470248b6a7781c52414dc405e61552d2cd5dd8dfdfcd802c4093a152bd983b664736f6c634300081c00337bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a", + "deployedBytecode": "0x6080604052600436106100865760003560e01c80635d36b190116100595780635d36b1901461010a578063c7af33521461011f578063cf7a1d7714610144578063d38bfff414610157578063f851a4401461009057610086565b80630c340a24146100905780633659cfe6146100c25780634f1ef286146100e25780635c60da1b146100f5575b61008e610177565b005b34801561009c57600080fd5b506100a5610197565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100ce57600080fd5b5061008e6100dd366004610750565b6101b4565b61008e6100f03660046107bb565b6101ed565b34801561010157600080fd5b506100a561028a565b34801561011657600080fd5b5061008e6102a2565b34801561012b57600080fd5b50610134610346565b60405190151581526020016100b9565b61008e61015236600461080e565b610377565b34801561016357600080fd5b5061008e610172366004610750565b6104e0565b6101956101906000805160206108f48339815191525490565b610584565b565b60006101af6000805160206109148339815191525490565b905090565b6101bc610346565b6101e15760405162461bcd60e51b81526004016101d89061086f565b60405180910390fd5b6101ea816105a8565b50565b6101f5610346565b6102115760405162461bcd60e51b81526004016101d89061086f565b61021a836105a8565b6000836001600160a01b031683836040516102369291906108a6565b600060405180830381855af49150503d8060008114610271576040519150601f19603f3d011682016040523d82523d6000602084013e610276565b606091505b505090508061028457600080fd5b50505050565b60006101af6000805160206108f48339815191525490565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b03161461033d5760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b60648201526084016101d8565b610195336105e8565b600061035e6000805160206109148339815191525490565b6001600160a01b0316336001600160a01b031614905090565b61037f610346565b61039b5760405162461bcd60e51b81526004016101d89061086f565b60006103b36000805160206108f48339815191525490565b6001600160a01b0316146103c657600080fd5b6001600160a01b0384166104155760405162461bcd60e51b8152602060048201526016602482015275125b5c1b195b595b9d185d1a5bdb881b9bdd081cd95d60521b60448201526064016101d8565b61044060017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd6108b6565b6000805160206108f48339815191521461045c5761045c6108dd565b61046584610647565b80156104d7576000846001600160a01b031683836040516104879291906108a6565b600060405180830381855af49150503d80600081146104c2576040519150601f19603f3d011682016040523d82523d6000602084013e6104c7565b606091505b50509050806104d557600080fd5b505b610284836105e8565b6104e8610346565b6105045760405162461bcd60e51b81526004016101d89061086f565b61052c817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b031661054c6000805160206109148339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b3660008037600080366000845af43d6000803e8080156105a3573d6000f35b3d6000fd5b6105b181610647565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b03811661063e5760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f72206973206164647265737328302900000000000060448201526064016101d8565b6101ea816106cd565b803b6106bb5760405162461bcd60e51b815260206004820152603b60248201527f43616e6e6f742073657420612070726f787920696d706c656d656e746174696f60448201527f6e20746f2061206e6f6e2d636f6e74726163742061646472657373000000000060648201526084016101d8565b6000805160206108f483398151915255565b806001600160a01b03166106ed6000805160206109148339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a360008051602061091483398151915255565b80356001600160a01b038116811461074b57600080fd5b919050565b60006020828403121561076257600080fd5b61076b82610734565b9392505050565b60008083601f84011261078457600080fd5b50813567ffffffffffffffff81111561079c57600080fd5b6020830191508360208285010111156107b457600080fd5b9250929050565b6000806000604084860312156107d057600080fd5b6107d984610734565b9250602084013567ffffffffffffffff8111156107f557600080fd5b61080186828701610772565b9497909650939450505050565b6000806000806060858703121561082457600080fd5b61082d85610734565b935061083b60208601610734565b9250604085013567ffffffffffffffff81111561085757600080fd5b61086387828801610772565b95989497509550505050565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b8183823760009101908152919050565b818103818111156108d757634e487b7160e01b600052601160045260246000fd5b92915050565b634e487b7160e01b600052600160045260246000fdfe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa26469706673582212201470248b6a7781c52414dc405e61552d2cd5dd8dfdfcd802c4093a152bd983b664736f6c634300081c0033", + "libraries": {}, + "devdoc": { + "events": { + "Upgraded(address)": { + "details": "Emitted when the implementation is upgraded.", + "params": { + "implementation": "Address of the new implementation." + } + } + }, + "kind": "dev", + "methods": { + "admin()": { + "returns": { + "_0": "The address of the proxy admin/it's also the governor." + } + }, + "implementation()": { + "returns": { + "_0": "The address of the implementation." + } + }, + "initialize(address,address,bytes)": { + "details": "Contract initializer with Governor enforcement", + "params": { + "_data": "Data to send as msg.data to the implementation to initialize the proxied contract. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.", + "_initGovernor": "Address of the initial Governor.", + "_logic": "Address of the initial implementation." + } + }, + "transferGovernance(address)": { + "params": { + "_newGovernor": "Address of the new Governor" + } + }, + "upgradeTo(address)": { + "details": "Upgrade the backing implementation of the proxy. Only the admin can call this function.", + "params": { + "_newImplementation": "Address of the new implementation." + } + }, + "upgradeToAndCall(address,bytes)": { + "details": "Upgrade the backing implementation of the proxy and call a function on the new implementation. This is useful to initialize the proxied contract.", + "params": { + "data": "Data to send as msg.data in the low level call. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.", + "newImplementation": "Address of the new implementation." + } + } + }, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "claimGovernance()": { + "notice": "Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor." + }, + "governor()": { + "notice": "Returns the address of the current Governor." + }, + "isGovernor()": { + "notice": "Returns true if the caller is the current Governor." + }, + "transferGovernance(address)": { + "notice": "Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete" + } + }, + "notice": "CompoundingStakingSSVStrategyProxy delegates calls to a CompoundingStakingSSVStrategy implementation", + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/CompoundingStakingSSVStrategyProxy.json b/contracts/deployments/hoodi/CompoundingStakingSSVStrategyProxy.json new file mode 100644 index 0000000000..8ce79fe601 --- /dev/null +++ b/contracts/deployments/hoodi/CompoundingStakingSSVStrategyProxy.json @@ -0,0 +1,297 @@ +{ + "address": "0xb5B92DEFC0a9623E31e10Fe0BaE02610bf76fd09", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "GovernorshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "PendingGovernorshipTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "claimGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "governor", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "implementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_logic", + "type": "address" + }, + { + "internalType": "address", + "name": "_initGovernor", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "isGovernor", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newGovernor", + "type": "address" + } + ], + "name": "transferGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } + ], + "transactionHash": "0x727ac86d3c37a3fd238721336387e7fcc1df655b3e711784d77dbc2851459005", + "receipt": { + "to": null, + "from": "0xf7749B41db006860cEc0650D18b8013d69C44Eeb", + "contractAddress": "0xeE45C342E7A183B1C2DEE96c7278aB3beCe36DEc", + "transactionIndex": 19, + "gasUsed": "599335", + "logsBloom": "0x00000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000200800000000000000000000000040000000000004000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000010000000000000000100000000000000000000000020000000100000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x6a385d0c79587ebf31334a339cc25abf952cc89bdb30c036fab87ef742ad6665", + "transactionHash": "0x727ac86d3c37a3fd238721336387e7fcc1df655b3e711784d77dbc2851459005", + "logs": [ + { + "transactionIndex": 19, + "blockNumber": 1120792, + "transactionHash": "0x727ac86d3c37a3fd238721336387e7fcc1df655b3e711784d77dbc2851459005", + "address": "0xeE45C342E7A183B1C2DEE96c7278aB3beCe36DEc", + "topics": [ + "0xc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000f7749b41db006860cec0650d18b8013d69c44eeb" + ], + "data": "0x", + "logIndex": 52, + "blockHash": "0x6a385d0c79587ebf31334a339cc25abf952cc89bdb30c036fab87ef742ad6665" + } + ], + "blockNumber": 1120792, + "cumulativeGasUsed": "9315279", + "status": 1, + "byzantium": true + }, + "args": [], + "numDeployments": 1, + "solcInputHash": "d6153015beb2dbf35534a30be4430611", + "metadata": "{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"GovernorshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"PendingGovernorshipTransfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"implementation\",\"type\":\"address\"}],\"name\":\"Upgraded\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"governor\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"implementation\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_logic\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_initGovernor\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isGovernor\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newGovernor\",\"type\":\"address\"}],\"name\":\"transferGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newImplementation\",\"type\":\"address\"}],\"name\":\"upgradeTo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"upgradeToAndCall\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"}],\"devdoc\":{\"events\":{\"Upgraded(address)\":{\"details\":\"Emitted when the implementation is upgraded.\",\"params\":{\"implementation\":\"Address of the new implementation.\"}}},\"kind\":\"dev\",\"methods\":{\"admin()\":{\"returns\":{\"_0\":\"The address of the proxy admin/it's also the governor.\"}},\"implementation()\":{\"returns\":{\"_0\":\"The address of the implementation.\"}},\"initialize(address,address,bytes)\":{\"details\":\"Contract initializer with Governor enforcement\",\"params\":{\"_data\":\"Data to send as msg.data to the implementation to initialize the proxied contract. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.\",\"_initGovernor\":\"Address of the initial Governor.\",\"_logic\":\"Address of the initial implementation.\"}},\"transferGovernance(address)\":{\"params\":{\"_newGovernor\":\"Address of the new Governor\"}},\"upgradeTo(address)\":{\"details\":\"Upgrade the backing implementation of the proxy. Only the admin can call this function.\",\"params\":{\"_newImplementation\":\"Address of the new implementation.\"}},\"upgradeToAndCall(address,bytes)\":{\"details\":\"Upgrade the backing implementation of the proxy and call a function on the new implementation. This is useful to initialize the proxied contract.\",\"params\":{\"data\":\"Data to send as msg.data in the low level call. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\",\"newImplementation\":\"Address of the new implementation.\"}}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"claimGovernance()\":{\"notice\":\"Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor.\"},\"governor()\":{\"notice\":\"Returns the address of the current Governor.\"},\"isGovernor()\":{\"notice\":\"Returns true if the caller is the current Governor.\"},\"transferGovernance(address)\":{\"notice\":\"Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete\"}},\"notice\":\"CompoundingStakingSSVStrategyProxy delegates calls to a CompoundingStakingSSVStrategy implementation\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/proxies/Proxies.sol\":\"CompoundingStakingSSVStrategyProxy\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize, which returns 0 for contracts in\\n // construction, since the code is only stored at the end of the\\n // constructor execution.\\n\\n uint256 size;\\n assembly {\\n size := extcodesize(account)\\n }\\n return size > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCall(target, data, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n require(isContract(target), \\\"Address: static call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(isContract(target), \\\"Address: delegate call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x51b758a8815ecc9596c66c37d56b1d33883a444631a3f916b9fe65cb863ef7c4\",\"license\":\"MIT\"},\"contracts/governance/Governable.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\\n * from owner to governor and renounce methods removed. Does not use\\n * Context.sol like Ownable.sol does for simplification.\\n * @author Origin Protocol Inc\\n */\\nabstract contract Governable {\\n // Storage position of the owner and pendingOwner of the contract\\n // keccak256(\\\"OUSD.governor\\\");\\n bytes32 private constant governorPosition =\\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\\n\\n // keccak256(\\\"OUSD.pending.governor\\\");\\n bytes32 private constant pendingGovernorPosition =\\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\\n\\n // keccak256(\\\"OUSD.reentry.status\\\");\\n bytes32 private constant reentryStatusPosition =\\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\\n\\n // See OpenZeppelin ReentrancyGuard implementation\\n uint256 constant _NOT_ENTERED = 1;\\n uint256 constant _ENTERED = 2;\\n\\n event PendingGovernorshipTransfer(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n event GovernorshipTransferred(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n /**\\n * @notice Returns the address of the current Governor.\\n */\\n function governor() public view returns (address) {\\n return _governor();\\n }\\n\\n /**\\n * @dev Returns the address of the current Governor.\\n */\\n function _governor() internal view returns (address governorOut) {\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n governorOut := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Returns the address of the pending Governor.\\n */\\n function _pendingGovernor()\\n internal\\n view\\n returns (address pendingGovernor)\\n {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n pendingGovernor := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the Governor.\\n */\\n modifier onlyGovernor() {\\n require(isGovernor(), \\\"Caller is not the Governor\\\");\\n _;\\n }\\n\\n /**\\n * @notice Returns true if the caller is the current Governor.\\n */\\n function isGovernor() public view returns (bool) {\\n return msg.sender == _governor();\\n }\\n\\n function _setGovernor(address newGovernor) internal {\\n emit GovernorshipTransferred(_governor(), newGovernor);\\n\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @dev Prevents a contract from calling itself, directly or indirectly.\\n * Calling a `nonReentrant` function from another `nonReentrant`\\n * function is not supported. It is possible to prevent this from happening\\n * by making the `nonReentrant` function external, and make it call a\\n * `private` function that does the actual work.\\n */\\n modifier nonReentrant() {\\n bytes32 position = reentryStatusPosition;\\n uint256 _reentry_status;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n _reentry_status := sload(position)\\n }\\n\\n // On the first call to nonReentrant, _notEntered will be true\\n require(_reentry_status != _ENTERED, \\\"Reentrant call\\\");\\n\\n // Any calls to nonReentrant after this point will fail\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _ENTERED)\\n }\\n\\n _;\\n\\n // By storing the original value once again, a refund is triggered (see\\n // https://eips.ethereum.org/EIPS/eip-2200)\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _NOT_ENTERED)\\n }\\n }\\n\\n function _setPendingGovernor(address newGovernor) internal {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the current Governor. Must be claimed for this to complete\\n * @param _newGovernor Address of the new Governor\\n */\\n function transferGovernance(address _newGovernor) external onlyGovernor {\\n _setPendingGovernor(_newGovernor);\\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\\n }\\n\\n /**\\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the new Governor.\\n */\\n function claimGovernance() external {\\n require(\\n msg.sender == _pendingGovernor(),\\n \\\"Only the pending Governor can complete the claim\\\"\\n );\\n _changeGovernor(msg.sender);\\n }\\n\\n /**\\n * @dev Change Governance of the contract to a new account (`newGovernor`).\\n * @param _newGovernor Address of the new Governor\\n */\\n function _changeGovernor(address _newGovernor) internal {\\n require(_newGovernor != address(0), \\\"New Governor is address(0)\\\");\\n _setGovernor(_newGovernor);\\n }\\n}\\n\",\"keccak256\":\"0xf32f873c8bfbacf2e5f01d0cf37bc7f54fbd5aa656e95c8a599114229946f107\",\"license\":\"BUSL-1.1\"},\"contracts/proxies/InitializeGovernedUpgradeabilityProxy.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { Address } from \\\"@openzeppelin/contracts/utils/Address.sol\\\";\\n\\nimport { Governable } from \\\"../governance/Governable.sol\\\";\\n\\n/**\\n * @title BaseGovernedUpgradeabilityProxy\\n * @dev This contract combines an upgradeability proxy with our governor system.\\n * It is based on an older version of OpenZeppelins BaseUpgradeabilityProxy\\n * with Solidity ^0.8.0.\\n * @author Origin Protocol Inc\\n */\\ncontract InitializeGovernedUpgradeabilityProxy is Governable {\\n /**\\n * @dev Emitted when the implementation is upgraded.\\n * @param implementation Address of the new implementation.\\n */\\n event Upgraded(address indexed implementation);\\n\\n constructor() {\\n _setGovernor(msg.sender);\\n }\\n\\n /**\\n * @dev Contract initializer with Governor enforcement\\n * @param _logic Address of the initial implementation.\\n * @param _initGovernor Address of the initial Governor.\\n * @param _data Data to send as msg.data to the implementation to initialize\\n * the proxied contract.\\n * It should include the signature and the parameters of the function to be\\n * called, as described in\\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\\n * This parameter is optional, if no data is given the initialization call\\n * to proxied contract will be skipped.\\n */\\n function initialize(\\n address _logic,\\n address _initGovernor,\\n bytes calldata _data\\n ) public payable onlyGovernor {\\n require(_implementation() == address(0));\\n require(_logic != address(0), \\\"Implementation not set\\\");\\n assert(\\n IMPLEMENTATION_SLOT ==\\n bytes32(uint256(keccak256(\\\"eip1967.proxy.implementation\\\")) - 1)\\n );\\n _setImplementation(_logic);\\n if (_data.length > 0) {\\n (bool success, ) = _logic.delegatecall(_data);\\n require(success);\\n }\\n _changeGovernor(_initGovernor);\\n }\\n\\n /**\\n * @return The address of the proxy admin/it's also the governor.\\n */\\n function admin() external view returns (address) {\\n return _governor();\\n }\\n\\n /**\\n * @return The address of the implementation.\\n */\\n function implementation() external view returns (address) {\\n return _implementation();\\n }\\n\\n /**\\n * @dev Upgrade the backing implementation of the proxy.\\n * Only the admin can call this function.\\n * @param _newImplementation Address of the new implementation.\\n */\\n function upgradeTo(address _newImplementation) external onlyGovernor {\\n _upgradeTo(_newImplementation);\\n }\\n\\n /**\\n * @dev Upgrade the backing implementation of the proxy and call a function\\n * on the new implementation.\\n * This is useful to initialize the proxied contract.\\n * @param newImplementation Address of the new implementation.\\n * @param data Data to send as msg.data in the low level call.\\n * It should include the signature and the parameters of the function to be called, as described in\\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\\n */\\n function upgradeToAndCall(address newImplementation, bytes calldata data)\\n external\\n payable\\n onlyGovernor\\n {\\n _upgradeTo(newImplementation);\\n (bool success, ) = newImplementation.delegatecall(data);\\n require(success);\\n }\\n\\n /**\\n * @dev Fallback function.\\n * Implemented entirely in `_fallback`.\\n */\\n fallback() external payable {\\n _fallback();\\n }\\n\\n /**\\n * @dev Delegates execution to an implementation contract.\\n * This is a low level function that doesn't return to its internal call site.\\n * It will return to the external caller whatever the implementation returns.\\n * @param _impl Address to delegate.\\n */\\n function _delegate(address _impl) internal {\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n // Copy msg.data. We take full control of memory in this inline assembly\\n // block because it will not return to Solidity code. We overwrite the\\n // Solidity scratch pad at memory position 0.\\n calldatacopy(0, 0, calldatasize())\\n\\n // Call the implementation.\\n // out and outsize are 0 because we don't know the size yet.\\n let result := delegatecall(gas(), _impl, 0, calldatasize(), 0, 0)\\n\\n // Copy the returned data.\\n returndatacopy(0, 0, returndatasize())\\n\\n switch result\\n // delegatecall returns 0 on error.\\n case 0 {\\n revert(0, returndatasize())\\n }\\n default {\\n return(0, returndatasize())\\n }\\n }\\n }\\n\\n /**\\n * @dev Function that is run as the first thing in the fallback function.\\n * Can be redefined in derived contracts to add functionality.\\n * Redefinitions must call super._willFallback().\\n */\\n function _willFallback() internal {}\\n\\n /**\\n * @dev fallback implementation.\\n * Extracted to enable manual triggering.\\n */\\n function _fallback() internal {\\n _willFallback();\\n _delegate(_implementation());\\n }\\n\\n /**\\n * @dev Storage slot with the address of the current implementation.\\n * This is the keccak-256 hash of \\\"eip1967.proxy.implementation\\\" subtracted by 1, and is\\n * validated in the constructor.\\n */\\n bytes32 internal constant IMPLEMENTATION_SLOT =\\n 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\\n\\n /**\\n * @dev Returns the current implementation.\\n * @return impl Address of the current implementation\\n */\\n function _implementation() internal view returns (address impl) {\\n bytes32 slot = IMPLEMENTATION_SLOT;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n impl := sload(slot)\\n }\\n }\\n\\n /**\\n * @dev Upgrades the proxy to a new implementation.\\n * @param newImplementation Address of the new implementation.\\n */\\n function _upgradeTo(address newImplementation) internal {\\n _setImplementation(newImplementation);\\n emit Upgraded(newImplementation);\\n }\\n\\n /**\\n * @dev Sets the implementation address of the proxy.\\n * @param newImplementation Address of the new implementation.\\n */\\n function _setImplementation(address newImplementation) internal {\\n require(\\n Address.isContract(newImplementation),\\n \\\"Cannot set a proxy implementation to a non-contract address\\\"\\n );\\n\\n bytes32 slot = IMPLEMENTATION_SLOT;\\n\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(slot, newImplementation)\\n }\\n }\\n}\\n\",\"keccak256\":\"0x3f46ae39dced6fa90d8b65aa31a0a331438544ec876e2ec961a8e6b22e2e06c7\",\"license\":\"BUSL-1.1\"},\"contracts/proxies/Proxies.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { InitializeGovernedUpgradeabilityProxy } from \\\"./InitializeGovernedUpgradeabilityProxy.sol\\\";\\n\\n/**\\n * @notice OUSDProxy delegates calls to an OUSD implementation\\n */\\ncontract OUSDProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice WrappedOUSDProxy delegates calls to a WrappedOUSD implementation\\n */\\ncontract WrappedOUSDProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice VaultProxy delegates calls to a Vault implementation\\n */\\ncontract VaultProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice CompoundStrategyProxy delegates calls to a CompoundStrategy implementation\\n */\\ncontract CompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice AaveStrategyProxy delegates calls to a AaveStrategy implementation\\n */\\ncontract AaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice ConvexStrategyProxy delegates calls to a ConvexStrategy implementation\\n */\\ncontract ConvexStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice HarvesterProxy delegates calls to a Harvester implementation\\n */\\ncontract HarvesterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice DripperProxy delegates calls to a Dripper implementation\\n */\\ncontract DripperProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice MorphoCompoundStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\\n */\\ncontract MorphoCompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice ConvexOUSDMetaStrategyProxy delegates calls to a ConvexOUSDMetaStrategy implementation\\n */\\ncontract ConvexOUSDMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice MorphoAaveStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\\n */\\ncontract MorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHProxy delegates calls to nowhere for now\\n */\\ncontract OETHProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice WOETHProxy delegates calls to nowhere for now\\n */\\ncontract WOETHProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHVaultProxy delegates calls to a Vault implementation\\n */\\ncontract OETHVaultProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHDripperProxy delegates calls to a OETHDripper implementation\\n */\\ncontract OETHDripperProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHHarvesterProxy delegates calls to a Harvester implementation\\n */\\ncontract OETHHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice CurveEthStrategyProxy delegates calls to a CurveEthStrategy implementation\\n */\\ncontract ConvexEthMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice BuybackProxy delegates calls to Buyback implementation\\n */\\ncontract BuybackProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHMorphoAaveStrategyProxy delegates calls to a MorphoAaveStrategy implementation\\n */\\ncontract OETHMorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHBalancerMetaPoolrEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\\n */\\ncontract OETHBalancerMetaPoolrEthStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice OETHBalancerMetaPoolwstEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\\n */\\ncontract OETHBalancerMetaPoolwstEthStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MakerDsrStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MakerDsrStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHBuybackProxy delegates calls to Buyback implementation\\n */\\ncontract OETHBuybackProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice BridgedWOETHProxy delegates calls to BridgedWOETH implementation\\n */\\ncontract BridgedWOETHProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice NativeStakingSSVStrategyProxy delegates calls to NativeStakingSSVStrategy implementation\\n */\\ncontract NativeStakingSSVStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingFeeAccumulatorProxy delegates calls to FeeAccumulator implementation\\n */\\ncontract NativeStakingFeeAccumulatorProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingSSVStrategy2Proxy delegates calls to NativeStakingSSVStrategy implementation\\n */\\ncontract NativeStakingSSVStrategy2Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingFeeAccumulator2Proxy delegates calls to FeeAccumulator implementation\\n */\\ncontract NativeStakingFeeAccumulator2Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingSSVStrategy3Proxy delegates calls to NativeStakingSSVStrategy implementation\\n */\\ncontract NativeStakingSSVStrategy3Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingFeeAccumulator3Proxy delegates calls to FeeAccumulator implementation\\n */\\ncontract NativeStakingFeeAccumulator3Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MetaMorphoStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MetaMorphoStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice ARMBuybackProxy delegates calls to Buyback implementation\\n */\\ncontract ARMBuybackProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice MorphoGauntletPrimeUSDCStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MorphoGauntletPrimeUSDCStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MorphoGauntletPrimeUSDTStrategyProxy delegates calls to a Generalized4626USDTStrategy implementation\\n */\\ncontract MorphoGauntletPrimeUSDTStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice CurvePoolBoosterProxy delegates calls to a CurvePoolBooster implementation\\n */\\ncontract CurvePoolBoosterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHFixedRateDripperProxy delegates calls to a OETHFixedRateDripper implementation\\n */\\ncontract OETHFixedRateDripperProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHSimpleHarvesterProxy delegates calls to a OETHSimpleHarvester implementation\\n */\\ncontract OETHSimpleHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice PoolBoostCentralRegistryProxy delegates calls to the PoolBoostCentralRegistry implementation\\n */\\ncontract PoolBoostCentralRegistryProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MakerSSRStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MakerSSRStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OUSDCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\\n */\\ncontract OUSDCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\\n */\\ncontract OETHCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice CompoundingStakingSSVStrategyProxy delegates calls to a CompoundingStakingSSVStrategy implementation\\n */\\ncontract CompoundingStakingSSVStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\",\"keccak256\":\"0x71e10cf279337e54682c7b0132825fa307992782367bd296c44b664b7b705db4\",\"license\":\"BUSL-1.1\"}},\"version\":1}", + "bytecode": "0x6080604052348015600f57600080fd5b50601733601b565b6081565b6001600160a01b038116603a6000805160206109f98339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a36000805160206109f983398151915255565b610969806100906000396000f3fe6080604052600436106100865760003560e01c80635d36b190116100595780635d36b1901461010a578063c7af33521461011f578063cf7a1d7714610144578063d38bfff414610157578063f851a4401461009057610086565b80630c340a24146100905780633659cfe6146100c25780634f1ef286146100e25780635c60da1b146100f5575b61008e610177565b005b34801561009c57600080fd5b506100a5610197565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100ce57600080fd5b5061008e6100dd366004610750565b6101b4565b61008e6100f03660046107bb565b6101ed565b34801561010157600080fd5b506100a561028a565b34801561011657600080fd5b5061008e6102a2565b34801561012b57600080fd5b50610134610346565b60405190151581526020016100b9565b61008e61015236600461080e565b610377565b34801561016357600080fd5b5061008e610172366004610750565b6104e0565b6101956101906000805160206108f48339815191525490565b610584565b565b60006101af6000805160206109148339815191525490565b905090565b6101bc610346565b6101e15760405162461bcd60e51b81526004016101d89061086f565b60405180910390fd5b6101ea816105a8565b50565b6101f5610346565b6102115760405162461bcd60e51b81526004016101d89061086f565b61021a836105a8565b6000836001600160a01b031683836040516102369291906108a6565b600060405180830381855af49150503d8060008114610271576040519150601f19603f3d011682016040523d82523d6000602084013e610276565b606091505b505090508061028457600080fd5b50505050565b60006101af6000805160206108f48339815191525490565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b03161461033d5760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b60648201526084016101d8565b610195336105e8565b600061035e6000805160206109148339815191525490565b6001600160a01b0316336001600160a01b031614905090565b61037f610346565b61039b5760405162461bcd60e51b81526004016101d89061086f565b60006103b36000805160206108f48339815191525490565b6001600160a01b0316146103c657600080fd5b6001600160a01b0384166104155760405162461bcd60e51b8152602060048201526016602482015275125b5c1b195b595b9d185d1a5bdb881b9bdd081cd95d60521b60448201526064016101d8565b61044060017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd6108b6565b6000805160206108f48339815191521461045c5761045c6108dd565b61046584610647565b80156104d7576000846001600160a01b031683836040516104879291906108a6565b600060405180830381855af49150503d80600081146104c2576040519150601f19603f3d011682016040523d82523d6000602084013e6104c7565b606091505b50509050806104d557600080fd5b505b610284836105e8565b6104e8610346565b6105045760405162461bcd60e51b81526004016101d89061086f565b61052c817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b031661054c6000805160206109148339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b3660008037600080366000845af43d6000803e8080156105a3573d6000f35b3d6000fd5b6105b181610647565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b03811661063e5760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f72206973206164647265737328302900000000000060448201526064016101d8565b6101ea816106cd565b803b6106bb5760405162461bcd60e51b815260206004820152603b60248201527f43616e6e6f742073657420612070726f787920696d706c656d656e746174696f60448201527f6e20746f2061206e6f6e2d636f6e74726163742061646472657373000000000060648201526084016101d8565b6000805160206108f483398151915255565b806001600160a01b03166106ed6000805160206109148339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a360008051602061091483398151915255565b80356001600160a01b038116811461074b57600080fd5b919050565b60006020828403121561076257600080fd5b61076b82610734565b9392505050565b60008083601f84011261078457600080fd5b50813567ffffffffffffffff81111561079c57600080fd5b6020830191508360208285010111156107b457600080fd5b9250929050565b6000806000604084860312156107d057600080fd5b6107d984610734565b9250602084013567ffffffffffffffff8111156107f557600080fd5b61080186828701610772565b9497909650939450505050565b6000806000806060858703121561082457600080fd5b61082d85610734565b935061083b60208601610734565b9250604085013567ffffffffffffffff81111561085757600080fd5b61086387828801610772565b95989497509550505050565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b8183823760009101908152919050565b818103818111156108d757634e487b7160e01b600052601160045260246000fd5b92915050565b634e487b7160e01b600052600160045260246000fdfe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa26469706673582212201470248b6a7781c52414dc405e61552d2cd5dd8dfdfcd802c4093a152bd983b664736f6c634300081c00337bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a", + "deployedBytecode": "0x6080604052600436106100865760003560e01c80635d36b190116100595780635d36b1901461010a578063c7af33521461011f578063cf7a1d7714610144578063d38bfff414610157578063f851a4401461009057610086565b80630c340a24146100905780633659cfe6146100c25780634f1ef286146100e25780635c60da1b146100f5575b61008e610177565b005b34801561009c57600080fd5b506100a5610197565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100ce57600080fd5b5061008e6100dd366004610750565b6101b4565b61008e6100f03660046107bb565b6101ed565b34801561010157600080fd5b506100a561028a565b34801561011657600080fd5b5061008e6102a2565b34801561012b57600080fd5b50610134610346565b60405190151581526020016100b9565b61008e61015236600461080e565b610377565b34801561016357600080fd5b5061008e610172366004610750565b6104e0565b6101956101906000805160206108f48339815191525490565b610584565b565b60006101af6000805160206109148339815191525490565b905090565b6101bc610346565b6101e15760405162461bcd60e51b81526004016101d89061086f565b60405180910390fd5b6101ea816105a8565b50565b6101f5610346565b6102115760405162461bcd60e51b81526004016101d89061086f565b61021a836105a8565b6000836001600160a01b031683836040516102369291906108a6565b600060405180830381855af49150503d8060008114610271576040519150601f19603f3d011682016040523d82523d6000602084013e610276565b606091505b505090508061028457600080fd5b50505050565b60006101af6000805160206108f48339815191525490565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b03161461033d5760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b60648201526084016101d8565b610195336105e8565b600061035e6000805160206109148339815191525490565b6001600160a01b0316336001600160a01b031614905090565b61037f610346565b61039b5760405162461bcd60e51b81526004016101d89061086f565b60006103b36000805160206108f48339815191525490565b6001600160a01b0316146103c657600080fd5b6001600160a01b0384166104155760405162461bcd60e51b8152602060048201526016602482015275125b5c1b195b595b9d185d1a5bdb881b9bdd081cd95d60521b60448201526064016101d8565b61044060017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd6108b6565b6000805160206108f48339815191521461045c5761045c6108dd565b61046584610647565b80156104d7576000846001600160a01b031683836040516104879291906108a6565b600060405180830381855af49150503d80600081146104c2576040519150601f19603f3d011682016040523d82523d6000602084013e6104c7565b606091505b50509050806104d557600080fd5b505b610284836105e8565b6104e8610346565b6105045760405162461bcd60e51b81526004016101d89061086f565b61052c817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b031661054c6000805160206109148339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b3660008037600080366000845af43d6000803e8080156105a3573d6000f35b3d6000fd5b6105b181610647565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b03811661063e5760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f72206973206164647265737328302900000000000060448201526064016101d8565b6101ea816106cd565b803b6106bb5760405162461bcd60e51b815260206004820152603b60248201527f43616e6e6f742073657420612070726f787920696d706c656d656e746174696f60448201527f6e20746f2061206e6f6e2d636f6e74726163742061646472657373000000000060648201526084016101d8565b6000805160206108f483398151915255565b806001600160a01b03166106ed6000805160206109148339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a360008051602061091483398151915255565b80356001600160a01b038116811461074b57600080fd5b919050565b60006020828403121561076257600080fd5b61076b82610734565b9392505050565b60008083601f84011261078457600080fd5b50813567ffffffffffffffff81111561079c57600080fd5b6020830191508360208285010111156107b457600080fd5b9250929050565b6000806000604084860312156107d057600080fd5b6107d984610734565b9250602084013567ffffffffffffffff8111156107f557600080fd5b61080186828701610772565b9497909650939450505050565b6000806000806060858703121561082457600080fd5b61082d85610734565b935061083b60208601610734565b9250604085013567ffffffffffffffff81111561085757600080fd5b61086387828801610772565b95989497509550505050565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b8183823760009101908152919050565b818103818111156108d757634e487b7160e01b600052601160045260246000fd5b92915050565b634e487b7160e01b600052600160045260246000fdfe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa26469706673582212201470248b6a7781c52414dc405e61552d2cd5dd8dfdfcd802c4093a152bd983b664736f6c634300081c0033", + "libraries": {}, + "devdoc": { + "events": { + "Upgraded(address)": { + "details": "Emitted when the implementation is upgraded.", + "params": { + "implementation": "Address of the new implementation." + } + } + }, + "kind": "dev", + "methods": { + "admin()": { + "returns": { + "_0": "The address of the proxy admin/it's also the governor." + } + }, + "implementation()": { + "returns": { + "_0": "The address of the implementation." + } + }, + "initialize(address,address,bytes)": { + "details": "Contract initializer with Governor enforcement", + "params": { + "_data": "Data to send as msg.data to the implementation to initialize the proxied contract. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.", + "_initGovernor": "Address of the initial Governor.", + "_logic": "Address of the initial implementation." + } + }, + "transferGovernance(address)": { + "params": { + "_newGovernor": "Address of the new Governor" + } + }, + "upgradeTo(address)": { + "details": "Upgrade the backing implementation of the proxy. Only the admin can call this function.", + "params": { + "_newImplementation": "Address of the new implementation." + } + }, + "upgradeToAndCall(address,bytes)": { + "details": "Upgrade the backing implementation of the proxy and call a function on the new implementation. This is useful to initialize the proxied contract.", + "params": { + "data": "Data to send as msg.data in the low level call. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.", + "newImplementation": "Address of the new implementation." + } + } + }, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "claimGovernance()": { + "notice": "Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor." + }, + "governor()": { + "notice": "Returns the address of the current Governor." + }, + "isGovernor()": { + "notice": "Returns true if the caller is the current Governor." + }, + "transferGovernance(address)": { + "notice": "Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete" + } + }, + "notice": "CompoundingStakingSSVStrategyProxy delegates calls to a CompoundingStakingSSVStrategy implementation", + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/CompoundingStakingStrategyP2PView.json b/contracts/deployments/hoodi/CompoundingStakingStrategyP2PView.json new file mode 100644 index 0000000000..d2fbc44f04 --- /dev/null +++ b/contracts/deployments/hoodi/CompoundingStakingStrategyP2PView.json @@ -0,0 +1,161 @@ +{ + "address": "0x7476F56EB07e71CeD2fB417D1Aaa925549B2a7f5", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_stakingStrategy", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "getPendingDeposits", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "depositID", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "amountGwei", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "slot", + "type": "uint64" + }, + { + "internalType": "uint256", + "name": "withdrawableEpoch", + "type": "uint256" + } + ], + "internalType": "struct CompoundingStakingStrategyView.DepositView[]", + "name": "pendingDeposits", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getVerifiedValidators", + "outputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "enum CompoundingValidatorManager.ValidatorState", + "name": "state", + "type": "uint8" + } + ], + "internalType": "struct CompoundingStakingStrategyView.ValidatorView[]", + "name": "validators", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "stakingStrategy", + "outputs": [ + { + "internalType": "contract CompoundingValidatorManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "transactionHash": "0x1e8ec0561c1bf3fb2068844e7d7f595422ddac84b866763a183867c4aa1da0d9", + "receipt": { + "to": null, + "from": "0xf7749B41db006860cEc0650D18b8013d69C44Eeb", + "contractAddress": "0x7476F56EB07e71CeD2fB417D1Aaa925549B2a7f5", + "transactionIndex": 20, + "gasUsed": "542101", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x4c7b25577f26d5217fc74bac057c8d0280d1dbc7bda7765fd74542882ce3cc6a", + "transactionHash": "0x1e8ec0561c1bf3fb2068844e7d7f595422ddac84b866763a183867c4aa1da0d9", + "logs": [], + "blockNumber": 1068404, + "cumulativeGasUsed": "8953289", + "status": 1, + "byzantium": true + }, + "args": [ + "0xE0c7B62f75c54Cc75573965C566f37B266F216FB" + ], + "numDeployments": 2, + "solcInputHash": "df854a646f7e413f41affca8d5e99ba6", + "metadata": "{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_stakingStrategy\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"getPendingDeposits\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"depositID\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"amountGwei\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"slot\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"withdrawableEpoch\",\"type\":\"uint256\"}],\"internalType\":\"struct CompoundingStakingStrategyView.DepositView[]\",\"name\":\"pendingDeposits\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getVerifiedValidators\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"index\",\"type\":\"uint64\"},{\"internalType\":\"enum CompoundingValidatorManager.ValidatorState\",\"name\":\"state\",\"type\":\"uint8\"}],\"internalType\":\"struct CompoundingStakingStrategyView.ValidatorView[]\",\"name\":\"validators\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"stakingStrategy\",\"outputs\":[{\"internalType\":\"contract CompoundingValidatorManager\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"author\":\"Origin Protocol Inc\",\"kind\":\"dev\",\"methods\":{\"getPendingDeposits()\":{\"returns\":{\"pendingDeposits\":\"An array of `DepositView` containing the deposit ID, public key hash, amount in Gwei and the slot of the deposit.\"}},\"getVerifiedValidators()\":{\"returns\":{\"validators\":\"An array of `ValidatorView` containing the public key hash, validator index and state.\"}}},\"title\":\"Viewing contract for the Compounding Staking Strategy.\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"getPendingDeposits()\":{\"notice\":\"Returns the deposits that are still to be verified. These may or may not have been processed by the beacon chain.\"},\"getVerifiedValidators()\":{\"notice\":\"Returns the strategy's active validators. These are the ones that have been verified and have a non-zero balance.\"},\"stakingStrategy()\":{\"notice\":\"The address of the Compounding Staking Strategy contract\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/strategies/NativeStaking/CompoundingStakingView.sol\":\"CompoundingStakingStrategyView\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/token/ERC20/IERC20.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Interface of the ERC20 standard as defined in the EIP.\\n */\\ninterface IERC20 {\\n /**\\n * @dev Returns the amount of tokens in existence.\\n */\\n function totalSupply() external view returns (uint256);\\n\\n /**\\n * @dev Returns the amount of tokens owned by `account`.\\n */\\n function balanceOf(address account) external view returns (uint256);\\n\\n /**\\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transfer(address recipient, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Returns the remaining number of tokens that `spender` will be\\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\\n * zero by default.\\n *\\n * This value changes when {approve} or {transferFrom} are called.\\n */\\n function allowance(address owner, address spender) external view returns (uint256);\\n\\n /**\\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\\n * that someone may use both the old and the new allowance by unfortunate\\n * transaction ordering. One possible solution to mitigate this race\\n * condition is to first reduce the spender's allowance to 0 and set the\\n * desired value afterwards:\\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\\n *\\n * Emits an {Approval} event.\\n */\\n function approve(address spender, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\\n * allowance mechanism. `amount` is then deducted from the caller's\\n * allowance.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transferFrom(\\n address sender,\\n address recipient,\\n uint256 amount\\n ) external returns (bool);\\n\\n /**\\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\\n * another (`to`).\\n *\\n * Note that `value` may be zero.\\n */\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n\\n /**\\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n * a call to {approve}. `value` is the new allowance.\\n */\\n event Approval(address indexed owner, address indexed spender, uint256 value);\\n}\\n\",\"keccak256\":\"0x61437cb513a887a1bbad006e7b1c8b414478427d33de47c5600af3c748f108da\",\"license\":\"MIT\"},\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../IERC20.sol\\\";\\nimport \\\"../../../utils/Address.sol\\\";\\n\\n/**\\n * @title SafeERC20\\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\\n * contract returns false). Tokens that return no value (and instead revert or\\n * throw on failure) are also supported, non-reverting calls are assumed to be\\n * successful.\\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\\n */\\nlibrary SafeERC20 {\\n using Address for address;\\n\\n function safeTransfer(\\n IERC20 token,\\n address to,\\n uint256 value\\n ) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\\n }\\n\\n function safeTransferFrom(\\n IERC20 token,\\n address from,\\n address to,\\n uint256 value\\n ) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\\n }\\n\\n /**\\n * @dev Deprecated. This function has issues similar to the ones found in\\n * {IERC20-approve}, and its usage is discouraged.\\n *\\n * Whenever possible, use {safeIncreaseAllowance} and\\n * {safeDecreaseAllowance} instead.\\n */\\n function safeApprove(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n // safeApprove should only be called when setting an initial allowance,\\n // or when resetting it to zero. To increase and decrease it, use\\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\\n require(\\n (value == 0) || (token.allowance(address(this), spender) == 0),\\n \\\"SafeERC20: approve from non-zero to non-zero allowance\\\"\\n );\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\\n }\\n\\n function safeIncreaseAllowance(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n uint256 newAllowance = token.allowance(address(this), spender) + value;\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\\n }\\n\\n function safeDecreaseAllowance(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n unchecked {\\n uint256 oldAllowance = token.allowance(address(this), spender);\\n require(oldAllowance >= value, \\\"SafeERC20: decreased allowance below zero\\\");\\n uint256 newAllowance = oldAllowance - value;\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\\n }\\n }\\n\\n /**\\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\\n * on the return value: the return value is optional (but if data is returned, it must not be false).\\n * @param token The token targeted by the call.\\n * @param data The call data (encoded using abi.encode or one of its variants).\\n */\\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\\n // the target address contains contract code and also asserts for success in the low-level call.\\n\\n bytes memory returndata = address(token).functionCall(data, \\\"SafeERC20: low-level call failed\\\");\\n if (returndata.length > 0) {\\n // Return data is optional\\n require(abi.decode(returndata, (bool)), \\\"SafeERC20: ERC20 operation did not succeed\\\");\\n }\\n }\\n}\\n\",\"keccak256\":\"0xc3d946432c0ddbb1f846a0d3985be71299df331b91d06732152117f62f0be2b5\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize, which returns 0 for contracts in\\n // construction, since the code is only stored at the end of the\\n // constructor execution.\\n\\n uint256 size;\\n assembly {\\n size := extcodesize(account)\\n }\\n return size > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCall(target, data, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n require(isContract(target), \\\"Address: static call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(isContract(target), \\\"Address: delegate call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x51b758a8815ecc9596c66c37d56b1d33883a444631a3f916b9fe65cb863ef7c4\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/math/Math.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Standard math utilities missing in the Solidity language.\\n */\\nlibrary Math {\\n /**\\n * @dev Returns the largest of two numbers.\\n */\\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a >= b ? a : b;\\n }\\n\\n /**\\n * @dev Returns the smallest of two numbers.\\n */\\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a < b ? a : b;\\n }\\n\\n /**\\n * @dev Returns the average of two numbers. The result is rounded towards\\n * zero.\\n */\\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\\n // (a + b) / 2 can overflow.\\n return (a & b) + (a ^ b) / 2;\\n }\\n\\n /**\\n * @dev Returns the ceiling of the division of two numbers.\\n *\\n * This differs from standard division with `/` in that it rounds up instead\\n * of rounding down.\\n */\\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\\n // (a + b - 1) / b can overflow on addition, so we distribute.\\n return a / b + (a % b == 0 ? 0 : 1);\\n }\\n}\\n\",\"keccak256\":\"0xfaad496c1c944b6259b7dc70b4865eb1775d6402bc0c81b38a0b24d9f525ae37\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/math/SafeCast.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\\n * checks.\\n *\\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\\n * easily result in undesired exploitation or bugs, since developers usually\\n * assume that overflows raise errors. `SafeCast` restores this intuition by\\n * reverting the transaction when such an operation overflows.\\n *\\n * Using this library instead of the unchecked operations eliminates an entire\\n * class of bugs, so it's recommended to use it always.\\n *\\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\\n * all math on `uint256` and `int256` and then downcasting.\\n */\\nlibrary SafeCast {\\n /**\\n * @dev Returns the downcasted uint224 from uint256, reverting on\\n * overflow (when the input is greater than largest uint224).\\n *\\n * Counterpart to Solidity's `uint224` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 224 bits\\n */\\n function toUint224(uint256 value) internal pure returns (uint224) {\\n require(value <= type(uint224).max, \\\"SafeCast: value doesn't fit in 224 bits\\\");\\n return uint224(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint128 from uint256, reverting on\\n * overflow (when the input is greater than largest uint128).\\n *\\n * Counterpart to Solidity's `uint128` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 128 bits\\n */\\n function toUint128(uint256 value) internal pure returns (uint128) {\\n require(value <= type(uint128).max, \\\"SafeCast: value doesn't fit in 128 bits\\\");\\n return uint128(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint96 from uint256, reverting on\\n * overflow (when the input is greater than largest uint96).\\n *\\n * Counterpart to Solidity's `uint96` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 96 bits\\n */\\n function toUint96(uint256 value) internal pure returns (uint96) {\\n require(value <= type(uint96).max, \\\"SafeCast: value doesn't fit in 96 bits\\\");\\n return uint96(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint64 from uint256, reverting on\\n * overflow (when the input is greater than largest uint64).\\n *\\n * Counterpart to Solidity's `uint64` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 64 bits\\n */\\n function toUint64(uint256 value) internal pure returns (uint64) {\\n require(value <= type(uint64).max, \\\"SafeCast: value doesn't fit in 64 bits\\\");\\n return uint64(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint32 from uint256, reverting on\\n * overflow (when the input is greater than largest uint32).\\n *\\n * Counterpart to Solidity's `uint32` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 32 bits\\n */\\n function toUint32(uint256 value) internal pure returns (uint32) {\\n require(value <= type(uint32).max, \\\"SafeCast: value doesn't fit in 32 bits\\\");\\n return uint32(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint16 from uint256, reverting on\\n * overflow (when the input is greater than largest uint16).\\n *\\n * Counterpart to Solidity's `uint16` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 16 bits\\n */\\n function toUint16(uint256 value) internal pure returns (uint16) {\\n require(value <= type(uint16).max, \\\"SafeCast: value doesn't fit in 16 bits\\\");\\n return uint16(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint8 from uint256, reverting on\\n * overflow (when the input is greater than largest uint8).\\n *\\n * Counterpart to Solidity's `uint8` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 8 bits.\\n */\\n function toUint8(uint256 value) internal pure returns (uint8) {\\n require(value <= type(uint8).max, \\\"SafeCast: value doesn't fit in 8 bits\\\");\\n return uint8(value);\\n }\\n\\n /**\\n * @dev Converts a signed int256 into an unsigned uint256.\\n *\\n * Requirements:\\n *\\n * - input must be greater than or equal to 0.\\n */\\n function toUint256(int256 value) internal pure returns (uint256) {\\n require(value >= 0, \\\"SafeCast: value must be positive\\\");\\n return uint256(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int128 from int256, reverting on\\n * overflow (when the input is less than smallest int128 or\\n * greater than largest int128).\\n *\\n * Counterpart to Solidity's `int128` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 128 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt128(int256 value) internal pure returns (int128) {\\n require(value >= type(int128).min && value <= type(int128).max, \\\"SafeCast: value doesn't fit in 128 bits\\\");\\n return int128(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int64 from int256, reverting on\\n * overflow (when the input is less than smallest int64 or\\n * greater than largest int64).\\n *\\n * Counterpart to Solidity's `int64` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 64 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt64(int256 value) internal pure returns (int64) {\\n require(value >= type(int64).min && value <= type(int64).max, \\\"SafeCast: value doesn't fit in 64 bits\\\");\\n return int64(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int32 from int256, reverting on\\n * overflow (when the input is less than smallest int32 or\\n * greater than largest int32).\\n *\\n * Counterpart to Solidity's `int32` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 32 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt32(int256 value) internal pure returns (int32) {\\n require(value >= type(int32).min && value <= type(int32).max, \\\"SafeCast: value doesn't fit in 32 bits\\\");\\n return int32(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int16 from int256, reverting on\\n * overflow (when the input is less than smallest int16 or\\n * greater than largest int16).\\n *\\n * Counterpart to Solidity's `int16` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 16 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt16(int256 value) internal pure returns (int16) {\\n require(value >= type(int16).min && value <= type(int16).max, \\\"SafeCast: value doesn't fit in 16 bits\\\");\\n return int16(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int8 from int256, reverting on\\n * overflow (when the input is less than smallest int8 or\\n * greater than largest int8).\\n *\\n * Counterpart to Solidity's `int8` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 8 bits.\\n *\\n * _Available since v3.1._\\n */\\n function toInt8(int256 value) internal pure returns (int8) {\\n require(value >= type(int8).min && value <= type(int8).max, \\\"SafeCast: value doesn't fit in 8 bits\\\");\\n return int8(value);\\n }\\n\\n /**\\n * @dev Converts an unsigned uint256 into a signed int256.\\n *\\n * Requirements:\\n *\\n * - input must be less than or equal to maxInt256.\\n */\\n function toInt256(uint256 value) internal pure returns (int256) {\\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\\n require(value <= uint256(type(int256).max), \\\"SafeCast: value doesn't fit in an int256\\\");\\n return int256(value);\\n }\\n}\\n\",\"keccak256\":\"0x5c6caab697d302ad7eb59c234a4d2dbc965c1bae87709bd2850060b7695b28c7\",\"license\":\"MIT\"},\"contracts/beacon/BeaconRoots.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Library to retrieve beacon block roots.\\n * @author Origin Protocol Inc\\n */\\nlibrary BeaconRoots {\\n /// @notice The address of beacon block roots oracle\\n /// See https://eips.ethereum.org/EIPS/eip-4788\\n address internal constant BEACON_ROOTS_ADDRESS =\\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\\n\\n /// @notice Returns the beacon block root for the previous block.\\n /// This comes from the Beacon Roots contract defined in EIP-4788.\\n /// This will revert if the block is more than 8,191 blocks old as\\n /// that is the size of the beacon root's ring buffer.\\n /// @param timestamp The timestamp of the block for which to get the parent root.\\n /// @return parentRoot The parent block root for the given timestamp.\\n function parentBlockRoot(uint64 timestamp)\\n internal\\n view\\n returns (bytes32 parentRoot)\\n {\\n // Call the Beacon Roots contract to get the parent block root.\\n // This does not have a function signature, so we use a staticcall.\\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\\n abi.encode(timestamp)\\n );\\n\\n require(success && result.length > 0, \\\"Invalid beacon timestamp\\\");\\n parentRoot = abi.decode(result, (bytes32));\\n }\\n}\\n\",\"keccak256\":\"0x4005989f852a68bbcdc1cdc3472ebd3911395e75b4e6366ffcaae4d1c128691e\",\"license\":\"BUSL-1.1\"},\"contracts/beacon/PartialWithdrawal.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Library to request full or partial withdrawals from validators on the beacon chain.\\n * @author Origin Protocol Inc\\n */\\nlibrary PartialWithdrawal {\\n /// @notice The address where the withdrawal request is sent to\\n /// See https://eips.ethereum.org/EIPS/eip-7002\\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\\n\\n /// @notice Requests a partial withdrawal for a given validator public key and amount.\\n /// @param validatorPubKey The public key of the validator to withdraw from\\n /// @param amount The amount of ETH to withdraw\\n function request(bytes calldata validatorPubKey, uint64 amount)\\n internal\\n returns (uint256 fee_)\\n {\\n require(validatorPubKey.length == 48, \\\"Invalid validator byte length\\\");\\n fee_ = fee();\\n\\n // Call the Withdrawal Request contract with the validator public key\\n // and amount to be withdrawn packed together\\n\\n // This is a general purpose EL to CL request:\\n // https://eips.ethereum.org/EIPS/eip-7685\\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\\n abi.encodePacked(validatorPubKey, amount)\\n );\\n\\n require(success, \\\"Withdrawal request failed\\\");\\n }\\n\\n /// @notice Gets fee for withdrawal requests contract on Beacon chain\\n function fee() internal view returns (uint256) {\\n // Get fee from the withdrawal request contract\\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\\n .staticcall(\\\"\\\");\\n\\n require(success && result.length > 0, \\\"Failed to get fee\\\");\\n return abi.decode(result, (uint256));\\n }\\n}\\n\",\"keccak256\":\"0x80d29153ff7eb5c6841692aca98eb0cc14ac43ad2d8e402890b6c6b6e4a9719d\",\"license\":\"BUSL-1.1\"},\"contracts/governance/Governable.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\\n * from owner to governor and renounce methods removed. Does not use\\n * Context.sol like Ownable.sol does for simplification.\\n * @author Origin Protocol Inc\\n */\\nabstract contract Governable {\\n // Storage position of the owner and pendingOwner of the contract\\n // keccak256(\\\"OUSD.governor\\\");\\n bytes32 private constant governorPosition =\\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\\n\\n // keccak256(\\\"OUSD.pending.governor\\\");\\n bytes32 private constant pendingGovernorPosition =\\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\\n\\n // keccak256(\\\"OUSD.reentry.status\\\");\\n bytes32 private constant reentryStatusPosition =\\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\\n\\n // See OpenZeppelin ReentrancyGuard implementation\\n uint256 constant _NOT_ENTERED = 1;\\n uint256 constant _ENTERED = 2;\\n\\n event PendingGovernorshipTransfer(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n event GovernorshipTransferred(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n /**\\n * @notice Returns the address of the current Governor.\\n */\\n function governor() public view returns (address) {\\n return _governor();\\n }\\n\\n /**\\n * @dev Returns the address of the current Governor.\\n */\\n function _governor() internal view returns (address governorOut) {\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n governorOut := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Returns the address of the pending Governor.\\n */\\n function _pendingGovernor()\\n internal\\n view\\n returns (address pendingGovernor)\\n {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n pendingGovernor := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the Governor.\\n */\\n modifier onlyGovernor() {\\n require(isGovernor(), \\\"Caller is not the Governor\\\");\\n _;\\n }\\n\\n /**\\n * @notice Returns true if the caller is the current Governor.\\n */\\n function isGovernor() public view returns (bool) {\\n return msg.sender == _governor();\\n }\\n\\n function _setGovernor(address newGovernor) internal {\\n emit GovernorshipTransferred(_governor(), newGovernor);\\n\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @dev Prevents a contract from calling itself, directly or indirectly.\\n * Calling a `nonReentrant` function from another `nonReentrant`\\n * function is not supported. It is possible to prevent this from happening\\n * by making the `nonReentrant` function external, and make it call a\\n * `private` function that does the actual work.\\n */\\n modifier nonReentrant() {\\n bytes32 position = reentryStatusPosition;\\n uint256 _reentry_status;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n _reentry_status := sload(position)\\n }\\n\\n // On the first call to nonReentrant, _notEntered will be true\\n require(_reentry_status != _ENTERED, \\\"Reentrant call\\\");\\n\\n // Any calls to nonReentrant after this point will fail\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _ENTERED)\\n }\\n\\n _;\\n\\n // By storing the original value once again, a refund is triggered (see\\n // https://eips.ethereum.org/EIPS/eip-2200)\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _NOT_ENTERED)\\n }\\n }\\n\\n function _setPendingGovernor(address newGovernor) internal {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the current Governor. Must be claimed for this to complete\\n * @param _newGovernor Address of the new Governor\\n */\\n function transferGovernance(address _newGovernor) external onlyGovernor {\\n _setPendingGovernor(_newGovernor);\\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\\n }\\n\\n /**\\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the new Governor.\\n */\\n function claimGovernance() external {\\n require(\\n msg.sender == _pendingGovernor(),\\n \\\"Only the pending Governor can complete the claim\\\"\\n );\\n _changeGovernor(msg.sender);\\n }\\n\\n /**\\n * @dev Change Governance of the contract to a new account (`newGovernor`).\\n * @param _newGovernor Address of the new Governor\\n */\\n function _changeGovernor(address _newGovernor) internal {\\n require(_newGovernor != address(0), \\\"New Governor is address(0)\\\");\\n _setGovernor(_newGovernor);\\n }\\n}\\n\",\"keccak256\":\"0xf32f873c8bfbacf2e5f01d0cf37bc7f54fbd5aa656e95c8a599114229946f107\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IBeaconProofs.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\ninterface IBeaconProofs {\\n function verifyValidator(\\n bytes32 beaconBlockRoot,\\n bytes32 pubKeyHash,\\n bytes calldata validatorPubKeyProof,\\n uint64 validatorIndex,\\n address withdrawalAddress\\n ) external view;\\n\\n function verifyValidatorWithdrawable(\\n bytes32 beaconBlockRoot,\\n uint64 validatorIndex,\\n bytes32 pubKeyHash,\\n uint64 withdrawableEpoch,\\n bytes calldata withdrawableEpochProof,\\n bytes calldata validatorPubKeyProof\\n ) external view;\\n\\n function verifyValidatorWithdrawable(\\n bytes32 beaconBlockRoot,\\n uint64 validatorIndex,\\n uint64 withdrawableEpoch,\\n bytes calldata withdrawableEpochProof\\n ) external view;\\n\\n function verifyBalancesContainer(\\n bytes32 beaconBlockRoot,\\n bytes32 balancesContainerLeaf,\\n bytes calldata balancesContainerProof\\n ) external view;\\n\\n function verifyValidatorBalance(\\n bytes32 balancesContainerRoot,\\n bytes32 validatorBalanceLeaf,\\n bytes calldata balanceProof,\\n uint64 validatorIndex\\n ) external view returns (uint256 validatorBalance);\\n\\n function verifyFirstPendingDeposit(\\n bytes32 beaconBlockRoot,\\n uint64 slot,\\n bytes32 pubKeyHash,\\n bytes calldata firstPendingDepositProof\\n ) external view returns (bool isEmptyDepositQueue);\\n}\\n\",\"keccak256\":\"0x0dfb664d21c67f8ad5b7e52262c52e1dd9556f3683d48503e7e0f902c036f5c3\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IDepositContract.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\ninterface IDepositContract {\\n /// @notice A processed deposit event.\\n event DepositEvent(\\n bytes pubkey,\\n bytes withdrawal_credentials,\\n bytes amount,\\n bytes signature,\\n bytes index\\n );\\n\\n /// @notice Submit a Phase 0 DepositData object.\\n /// @param pubkey A BLS12-381 public key.\\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\\n /// @param signature A BLS12-381 signature.\\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\\n /// Used as a protection against malformed input.\\n function deposit(\\n bytes calldata pubkey,\\n bytes calldata withdrawal_credentials,\\n bytes calldata signature,\\n bytes32 deposit_data_root\\n ) external payable;\\n\\n /// @notice Query the current deposit root hash.\\n /// @return The deposit root hash.\\n function get_deposit_root() external view returns (bytes32);\\n\\n /// @notice Query the current deposit count.\\n /// @return The deposit count encoded as a little endian 64-bit number.\\n function get_deposit_count() external view returns (bytes memory);\\n}\\n\",\"keccak256\":\"0x598f90bdbc854250bbd5991426bfb43367207e64e33109c41aa8b54323fd8d8e\",\"license\":\"MIT\"},\"contracts/interfaces/ISSVNetwork.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\nstruct Cluster {\\n uint32 validatorCount;\\n uint64 networkFeeIndex;\\n uint64 index;\\n bool active;\\n uint256 balance;\\n}\\n\\ninterface ISSVNetwork {\\n /**********/\\n /* Errors */\\n /**********/\\n\\n error CallerNotOwner(); // 0x5cd83192\\n error CallerNotWhitelisted(); // 0x8c6e5d71\\n error FeeTooLow(); // 0x732f9413\\n error FeeExceedsIncreaseLimit(); // 0x958065d9\\n error NoFeeDeclared(); // 0x1d226c30\\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\\n error OperatorDoesNotExist(); // 0x961e3e8c\\n error InsufficientBalance(); // 0xf4d678b8\\n error ValidatorDoesNotExist(); // 0xe51315d2\\n error ClusterNotLiquidatable(); // 0x60300a8d\\n error InvalidPublicKeyLength(); // 0x637297a4\\n error InvalidOperatorIdsLength(); // 0x38186224\\n error ClusterAlreadyEnabled(); // 0x3babafd2\\n error ClusterIsLiquidated(); // 0x95a0cf33\\n error ClusterDoesNotExists(); // 0x185e2b16\\n error IncorrectClusterState(); // 0x12e04c87\\n error UnsortedOperatorsList(); // 0xdd020e25\\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\\n error ExceedValidatorLimit(); // 0x6df5ab76\\n error TokenTransferFailed(); // 0x045c4b02\\n error SameFeeChangeNotAllowed(); // 0xc81272f8\\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\\n error NotAuthorized(); // 0xea8e4eb5\\n error OperatorsListNotUnique(); // 0xa5a1ff5d\\n error OperatorAlreadyExists(); // 0x289c9494\\n error TargetModuleDoesNotExist(); // 0x8f9195fb\\n error MaxValueExceeded(); // 0x91aa3017\\n error FeeTooHigh(); // 0xcd4e6167\\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\\n error EmptyPublicKeysList(); // df83e679\\n\\n // legacy errors\\n error ValidatorAlreadyExists(); // 0x8d09a73e\\n error IncorrectValidatorState(); // 0x2feda3c1\\n\\n event AdminChanged(address previousAdmin, address newAdmin);\\n event BeaconUpgraded(address indexed beacon);\\n event ClusterDeposited(\\n address indexed owner,\\n uint64[] operatorIds,\\n uint256 value,\\n Cluster cluster\\n );\\n event ClusterLiquidated(\\n address indexed owner,\\n uint64[] operatorIds,\\n Cluster cluster\\n );\\n event ClusterReactivated(\\n address indexed owner,\\n uint64[] operatorIds,\\n Cluster cluster\\n );\\n event ClusterWithdrawn(\\n address indexed owner,\\n uint64[] operatorIds,\\n uint256 value,\\n Cluster cluster\\n );\\n event DeclareOperatorFeePeriodUpdated(uint64 value);\\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\\n event FeeRecipientAddressUpdated(\\n address indexed owner,\\n address recipientAddress\\n );\\n event Initialized(uint8 version);\\n event LiquidationThresholdPeriodUpdated(uint64 value);\\n event MinimumLiquidationCollateralUpdated(uint256 value);\\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\\n event OperatorAdded(\\n uint64 indexed operatorId,\\n address indexed owner,\\n bytes publicKey,\\n uint256 fee\\n );\\n event OperatorFeeDeclarationCancelled(\\n address indexed owner,\\n uint64 indexed operatorId\\n );\\n event OperatorFeeDeclared(\\n address indexed owner,\\n uint64 indexed operatorId,\\n uint256 blockNumber,\\n uint256 fee\\n );\\n event OperatorFeeExecuted(\\n address indexed owner,\\n uint64 indexed operatorId,\\n uint256 blockNumber,\\n uint256 fee\\n );\\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\\n event OperatorMaximumFeeUpdated(uint64 maxFee);\\n event OperatorRemoved(uint64 indexed operatorId);\\n event OperatorWhitelistUpdated(\\n uint64 indexed operatorId,\\n address whitelisted\\n );\\n event OperatorWithdrawn(\\n address indexed owner,\\n uint64 indexed operatorId,\\n uint256 value\\n );\\n event OwnershipTransferStarted(\\n address indexed previousOwner,\\n address indexed newOwner\\n );\\n event OwnershipTransferred(\\n address indexed previousOwner,\\n address indexed newOwner\\n );\\n event Upgraded(address indexed implementation);\\n event ValidatorAdded(\\n address indexed owner,\\n uint64[] operatorIds,\\n bytes publicKey,\\n bytes shares,\\n Cluster cluster\\n );\\n event ValidatorExited(\\n address indexed owner,\\n uint64[] operatorIds,\\n bytes publicKey\\n );\\n event ValidatorRemoved(\\n address indexed owner,\\n uint64[] operatorIds,\\n bytes publicKey,\\n Cluster cluster\\n );\\n\\n fallback() external;\\n\\n function acceptOwnership() external;\\n\\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\\n\\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\\n\\n function deposit(\\n address clusterOwner,\\n uint64[] memory operatorIds,\\n uint256 amount,\\n Cluster memory cluster\\n ) external;\\n\\n function executeOperatorFee(uint64 operatorId) external;\\n\\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\\n external;\\n\\n function bulkExitValidator(\\n bytes[] calldata publicKeys,\\n uint64[] calldata operatorIds\\n ) external;\\n\\n function getVersion() external pure returns (string memory version);\\n\\n function initialize(\\n address token_,\\n address ssvOperators_,\\n address ssvClusters_,\\n address ssvDAO_,\\n address ssvViews_,\\n uint64 minimumBlocksBeforeLiquidation_,\\n uint256 minimumLiquidationCollateral_,\\n uint32 validatorsPerOperatorLimit_,\\n uint64 declareOperatorFeePeriod_,\\n uint64 executeOperatorFeePeriod_,\\n uint64 operatorMaxFeeIncrease_\\n ) external;\\n\\n function liquidate(\\n address clusterOwner,\\n uint64[] memory operatorIds,\\n Cluster memory cluster\\n ) external;\\n\\n function owner() external view returns (address);\\n\\n function pendingOwner() external view returns (address);\\n\\n function proxiableUUID() external view returns (bytes32);\\n\\n function reactivate(\\n uint64[] memory operatorIds,\\n uint256 amount,\\n Cluster memory cluster\\n ) external;\\n\\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\\n\\n function registerOperator(bytes memory publicKey, uint256 fee)\\n external\\n returns (uint64 id);\\n\\n function registerValidator(\\n bytes memory publicKey,\\n uint64[] memory operatorIds,\\n bytes memory sharesData,\\n uint256 amount,\\n Cluster memory cluster\\n ) external;\\n\\n function bulkRegisterValidator(\\n bytes[] calldata publicKeys,\\n uint64[] calldata operatorIds,\\n bytes[] calldata sharesData,\\n uint256 amount,\\n Cluster memory cluster\\n ) external;\\n\\n function removeOperator(uint64 operatorId) external;\\n\\n function removeValidator(\\n bytes memory publicKey,\\n uint64[] memory operatorIds,\\n Cluster memory cluster\\n ) external;\\n\\n function bulkRemoveValidator(\\n bytes[] calldata publicKeys,\\n uint64[] calldata operatorIds,\\n Cluster memory cluster\\n ) external;\\n\\n function renounceOwnership() external;\\n\\n function setFeeRecipientAddress(address recipientAddress) external;\\n\\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\\n external;\\n\\n function transferOwnership(address newOwner) external;\\n\\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\\n\\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\\n\\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\\n\\n function updateMaximumOperatorFee(uint64 maxFee) external;\\n\\n function updateMinimumLiquidationCollateral(uint256 amount) external;\\n\\n function updateModule(uint8 moduleId, address moduleAddress) external;\\n\\n function updateNetworkFee(uint256 fee) external;\\n\\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\\n\\n function upgradeTo(address newImplementation) external;\\n\\n function upgradeToAndCall(address newImplementation, bytes memory data)\\n external\\n payable;\\n\\n function withdraw(\\n uint64[] memory operatorIds,\\n uint256 amount,\\n Cluster memory cluster\\n ) external;\\n\\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\\n\\n function withdrawNetworkEarnings(uint256 amount) external;\\n\\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\\n external;\\n}\\n\",\"keccak256\":\"0xbd86cb74702aebc5b53c8fc738a2e3ad1b410583460617be84b22ce922af12a7\",\"license\":\"MIT\"},\"contracts/interfaces/IWETH9.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\ninterface IWETH9 {\\n event Approval(address indexed src, address indexed guy, uint256 wad);\\n event Deposit(address indexed dst, uint256 wad);\\n event Transfer(address indexed src, address indexed dst, uint256 wad);\\n event Withdrawal(address indexed src, uint256 wad);\\n\\n function allowance(address, address) external view returns (uint256);\\n\\n function approve(address guy, uint256 wad) external returns (bool);\\n\\n function balanceOf(address) external view returns (uint256);\\n\\n function decimals() external view returns (uint8);\\n\\n function deposit() external payable;\\n\\n function name() external view returns (string memory);\\n\\n function symbol() external view returns (string memory);\\n\\n function totalSupply() external view returns (uint256);\\n\\n function transfer(address dst, uint256 wad) external returns (bool);\\n\\n function transferFrom(\\n address src,\\n address dst,\\n uint256 wad\\n ) external returns (bool);\\n\\n function withdraw(uint256 wad) external;\\n}\\n\",\"keccak256\":\"0x05b7dce6c24d3cd4e48b5c6346d86e5e40ecc3291bcdf3f3ef091c98fc826519\",\"license\":\"MIT\"},\"contracts/strategies/NativeStaking/CompoundingStakingView.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { CompoundingValidatorManager } from \\\"./CompoundingValidatorManager.sol\\\";\\n\\n/**\\n * @title Viewing contract for the Compounding Staking Strategy.\\n * @author Origin Protocol Inc\\n */\\ncontract CompoundingStakingStrategyView {\\n /// @notice The address of the Compounding Staking Strategy contract\\n CompoundingValidatorManager public immutable stakingStrategy;\\n\\n constructor(address _stakingStrategy) {\\n stakingStrategy = CompoundingValidatorManager(_stakingStrategy);\\n }\\n\\n struct ValidatorView {\\n bytes32 pubKeyHash;\\n uint64 index;\\n CompoundingValidatorManager.ValidatorState state;\\n }\\n\\n struct DepositView {\\n uint256 depositID;\\n bytes32 pubKeyHash;\\n uint64 amountGwei;\\n uint64 slot;\\n uint256 withdrawableEpoch;\\n }\\n\\n /// @notice Returns the strategy's active validators.\\n /// These are the ones that have been verified and have a non-zero balance.\\n /// @return validators An array of `ValidatorView` containing the public key hash, validator index and state.\\n function getVerifiedValidators()\\n external\\n view\\n returns (ValidatorView[] memory validators)\\n {\\n uint256 validatorCount = stakingStrategy.verifiedValidatorsLength();\\n validators = new ValidatorView[](validatorCount);\\n for (uint256 i = 0; i < validatorCount; ++i) {\\n bytes32 pubKeyHash = stakingStrategy.verifiedValidators(i);\\n (\\n CompoundingValidatorManager.ValidatorState state,\\n uint64 index\\n ) = stakingStrategy.validator(pubKeyHash);\\n validators[i] = ValidatorView({\\n pubKeyHash: pubKeyHash,\\n index: index,\\n state: state\\n });\\n }\\n }\\n\\n /// @notice Returns the deposits that are still to be verified.\\n /// These may or may not have been processed by the beacon chain.\\n /// @return pendingDeposits An array of `DepositView` containing the deposit ID, public key hash,\\n /// amount in Gwei and the slot of the deposit.\\n function getPendingDeposits()\\n external\\n view\\n returns (DepositView[] memory pendingDeposits)\\n {\\n uint256 depositsCount = stakingStrategy.depositListLength();\\n pendingDeposits = new DepositView[](depositsCount);\\n for (uint256 i = 0; i < depositsCount; ++i) {\\n (\\n bytes32 pubKeyHash,\\n uint64 amountGwei,\\n uint64 slot,\\n ,\\n ,\\n uint256 withdrawableEpoch\\n ) = stakingStrategy.deposits(stakingStrategy.depositList(i));\\n pendingDeposits[i] = DepositView({\\n depositID: stakingStrategy.depositList(i),\\n pubKeyHash: pubKeyHash,\\n amountGwei: amountGwei,\\n slot: slot,\\n withdrawableEpoch: withdrawableEpoch\\n });\\n }\\n }\\n}\\n\",\"keccak256\":\"0xa291502fd2adfb2378ad70325ded8d2f52809347c91af4b30c33541559656bf3\",\"license\":\"BUSL-1.1\"},\"contracts/strategies/NativeStaking/CompoundingValidatorManager.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { SafeERC20 } from \\\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\\\";\\nimport { SafeCast } from \\\"@openzeppelin/contracts/utils/math/SafeCast.sol\\\";\\nimport { Math } from \\\"@openzeppelin/contracts/utils/math/Math.sol\\\";\\nimport { IERC20 } from \\\"@openzeppelin/contracts/token/ERC20/IERC20.sol\\\";\\nimport { Governable } from \\\"../../governance/Governable.sol\\\";\\nimport { IDepositContract } from \\\"../../interfaces/IDepositContract.sol\\\";\\nimport { IWETH9 } from \\\"../../interfaces/IWETH9.sol\\\";\\nimport { ISSVNetwork, Cluster } from \\\"../../interfaces/ISSVNetwork.sol\\\";\\nimport { BeaconRoots } from \\\"../../beacon/BeaconRoots.sol\\\";\\nimport { PartialWithdrawal } from \\\"../../beacon/PartialWithdrawal.sol\\\";\\nimport { IBeaconProofs } from \\\"../../interfaces/IBeaconProofs.sol\\\";\\n\\n/**\\n * @title Validator lifecycle management contract\\n * @notice This contract implements all the required functionality to\\n * register, deposit, withdraw, exit and remove validators.\\n * @author Origin Protocol Inc\\n */\\nabstract contract CompoundingValidatorManager is Governable {\\n using SafeERC20 for IERC20;\\n\\n /// @dev The amount of ETH in wei that is required for a deposit to a new validator.\\n /// Initially this is 32 ETH, but will be reduced to 1 ETH after P2P's APIs have been updated\\n /// to support deposits of 1 ETH.\\n uint256 internal constant DEPOSIT_AMOUNT_WEI = 32 ether;\\n /// @dev The maximum number of deposits that are waiting to be verified as processed on the beacon chain.\\n uint256 internal constant MAX_DEPOSITS = 12;\\n /// @dev The maximum number of validators that can be verified.\\n uint256 internal constant MAX_VERIFIED_VALIDATORS = 48;\\n /// @dev The default withdrawable epoch value on the Beacon chain.\\n /// A value in the far future means the validator is not exiting.\\n uint64 internal constant FAR_FUTURE_EPOCH = type(uint64).max;\\n /// @dev The number of seconds between each beacon chain slot.\\n uint64 internal constant SLOT_DURATION = 12;\\n /// @dev The number of slots in each beacon chain epoch.\\n uint64 internal constant SLOTS_PER_EPOCH = 32;\\n /// @dev Minimum time in seconds to allow snapped balances to be verified.\\n /// Set to 1 epoch as the pending deposits only changes every epoch.\\n /// That's also enough time to generate the proofs and call `verifyBalances`.\\n uint64 internal constant SNAP_BALANCES_DELAY =\\n SLOTS_PER_EPOCH * SLOT_DURATION;\\n\\n /// @notice The address of the Wrapped ETH (WETH) token contract\\n address public immutable WETH;\\n /// @notice The address of the beacon chain deposit contract\\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\\n /// @notice The address of the SSV Network contract used to interface with\\n address public immutable SSV_NETWORK;\\n /// @notice Address of the OETH Vault proxy contract\\n address public immutable VAULT_ADDRESS;\\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\\n address public immutable BEACON_PROOFS;\\n /// @notice The timestamp of the Beacon chain genesis.\\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\\n uint64 public immutable BEACON_GENESIS_TIMESTAMP;\\n\\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\\n address public validatorRegistrator;\\n\\n /// Deposit data for new compounding validators.\\n enum DepositStatus {\\n UNKNOWN, // default value\\n PENDING, // deposit is pending and waiting to be verified\\n VERIFIED // deposit has been verified and is ready to be staked\\n }\\n\\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\\n /// @param amountWei Amount of ETH in wei that has been deposited to the beacon chain deposit contract\\n /// @param blockNumber Block number when the deposit was made\\n /// @param depositIndex The index of the deposit in the list of active deposits\\n /// @param status The status of the deposit, either PENDING or VERIFIED\\n struct DepositData {\\n bytes32 pubKeyHash;\\n uint64 amountGwei;\\n uint64 slot;\\n uint32 depositIndex;\\n DepositStatus status;\\n uint64 withdrawableEpoch;\\n }\\n /// @notice Restricts to only one deposit to an unverified validator at a time.\\n /// This is to limit front-running attacks of deposits to the beacon chain contract.\\n bool public firstDeposit;\\n /// @notice Unique identifier of the next validator deposit.\\n uint128 public nextDepositID;\\n /// @notice Mapping of the deposit ID to the deposit data\\n mapping(uint256 => DepositData) public deposits;\\n /// @notice List of strategy deposit IDs to a validator.\\n /// The list can be for deposits waiting to be verified as processed on the beacon chain,\\n /// or deposits that have been verified to an exiting validator and is now waiting for the\\n /// validator's balance to be swept.\\n /// The list may not be ordered by time of deposit.\\n /// Removed deposits will move the last deposit to the removed index.\\n uint256[] public depositList;\\n\\n // Validator data\\n enum ValidatorState {\\n NON_REGISTERED, // validator is not registered on the SSV network\\n REGISTERED, // validator is registered on the SSV network\\n STAKED, // validator has funds staked\\n VERIFIED, // validator has been verified to exist on the beacon chain\\n EXITING, // The validator has been requested to exit or has been verified as forced exit\\n EXITED, // The validator has been verified to have a zero balance\\n REMOVED, // validator has funds withdrawn to the EigenPod and is removed from the SSV\\n INVALID // The validator has been front-run and the withdrawal address is not this strategy\\n }\\n\\n struct ValidatorData {\\n ValidatorState state;\\n uint64 index; // The index of the validator on the beacon chain\\n }\\n /// @notice List of validator public key hashes that have been verified to exist on the beacon chain.\\n /// These have had a deposit processed and the validator's balance increased.\\n /// Validators will be removed from this list when its verified they have a zero balance.\\n bytes32[] public verifiedValidators;\\n /// @notice Mapping of the hash of the validator's public key to the validator state and index.\\n /// Uses the Beacon chain hashing for BLSPubkey which is sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\\n mapping(bytes32 => ValidatorData) public validator;\\n\\n /// @param timestamp Timestamp of the snapshot\\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\\n struct Balances {\\n uint64 timestamp;\\n uint128 ethBalance;\\n }\\n /// @notice Mapping of the block root to the balances at that slot\\n mapping(bytes32 => Balances) public snappedBalances;\\n /// @notice The timestamp of the last snapshot taken\\n uint64 public lastSnapTimestamp;\\n /// @notice The last verified ETH balance of the strategy\\n uint128 public lastVerifiedEthBalance;\\n\\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\\n /// of WETH that has already been accounted for.\\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\\n /// deposit events.\\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\\n /// be staked.\\n uint256 public depositedWethAccountedFor;\\n\\n // For future use\\n uint256[50] private __gap;\\n\\n event RegistratorChanged(address indexed newAddress);\\n event StakingMonitorChanged(address indexed newAddress);\\n event FirstDepositReset();\\n event SSVValidatorRegistered(\\n bytes32 indexed pubKeyHash,\\n uint64[] operatorIds\\n );\\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\\n event ETHStaked(\\n bytes32 indexed pubKeyHash,\\n uint256 indexed depositID,\\n bytes pubKey,\\n uint256 amountWei\\n );\\n event ValidatorVerified(\\n bytes32 indexed pubKeyHash,\\n uint64 indexed validatorIndex\\n );\\n event ValidatorInvalid(bytes32 indexed pubKeyHash);\\n event DepositVerified(uint256 indexed depositID, uint256 amountWei);\\n event DepositToValidatorExiting(\\n uint256 indexed depositID,\\n uint256 amountWei,\\n uint64 withdrawableEpoch\\n );\\n event DepositValidatorExited(uint256 indexed depositID, uint256 amountWei);\\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\\n event BalancesSnapped(bytes32 indexed blockRoot, uint256 ethBalance);\\n event BalancesVerified(\\n uint64 indexed timestamp,\\n uint256 totalDepositsWei,\\n uint256 totalValidatorBalance,\\n uint256 ethBalance\\n );\\n\\n /// @dev Throws if called by any account other than the Registrator\\n modifier onlyRegistrator() {\\n require(msg.sender == validatorRegistrator, \\\"Not Registrator\\\");\\n _;\\n }\\n\\n /// @param _wethAddress Address of the Erc20 WETH Token contract\\n /// @param _vaultAddress Address of the Vault\\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\\n /// @param _ssvNetwork Address of the SSV Network contract\\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\\n constructor(\\n address _wethAddress,\\n address _vaultAddress,\\n address _beaconChainDepositContract,\\n address _ssvNetwork,\\n address _beaconProofs,\\n uint64 _beaconGenesisTimestamp\\n ) {\\n WETH = _wethAddress;\\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\\n SSV_NETWORK = _ssvNetwork;\\n VAULT_ADDRESS = _vaultAddress;\\n BEACON_PROOFS = _beaconProofs;\\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\\n\\n require(\\n block.timestamp > _beaconGenesisTimestamp,\\n \\\"Invalid genesis timestamp\\\"\\n );\\n }\\n\\n /**\\n *\\n * Admin Functions\\n *\\n */\\n\\n /// @notice Set the address of the registrator which can register, exit and remove validators\\n function setRegistrator(address _address) external onlyGovernor {\\n validatorRegistrator = _address;\\n emit RegistratorChanged(_address);\\n }\\n\\n /// @notice Reset the `firstDeposit` flag to false so deposits to unverified validators can be made again.\\n function resetFirstDeposit() external onlyGovernor {\\n require(firstDeposit, \\\"No first deposit\\\");\\n\\n firstDeposit = false;\\n\\n emit FirstDepositReset();\\n }\\n\\n /**\\n *\\n * Validator Management\\n *\\n */\\n\\n /// @notice Registers a single validator in a SSV Cluster.\\n /// Only the Registrator can call this function.\\n /// @param publicKey The public key of the validator\\n /// @param operatorIds The operator IDs of the SSV Cluster\\n /// @param sharesData The shares data for the validator\\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\\n /// @param cluster The SSV cluster details including the validator count and SSV balance\\n // slither-disable-start reentrancy-no-eth\\n function registerSsvValidator(\\n bytes calldata publicKey,\\n uint64[] calldata operatorIds,\\n bytes calldata sharesData,\\n uint256 ssvAmount,\\n Cluster calldata cluster\\n ) external onlyRegistrator {\\n // Hash the public key using the Beacon Chain's format\\n bytes32 pubKeyHash = _hashPubKey(publicKey);\\n // Check each public key has not already been used\\n require(\\n validator[pubKeyHash].state == ValidatorState.NON_REGISTERED,\\n \\\"Validator already registered\\\"\\n );\\n\\n // Store the validator state as registered\\n validator[pubKeyHash].state = ValidatorState.REGISTERED;\\n\\n ISSVNetwork(SSV_NETWORK).registerValidator(\\n publicKey,\\n operatorIds,\\n sharesData,\\n ssvAmount,\\n cluster\\n );\\n\\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\\n }\\n\\n // slither-disable-end reentrancy-no-eth\\n\\n struct ValidatorStakeData {\\n bytes pubkey;\\n bytes signature;\\n bytes32 depositDataRoot;\\n }\\n\\n /// @notice Stakes WETH in this strategy to a compounding validator.\\n /// Does not convert any ETH sitting in this strategy to WETH.\\n /// @param validatorStakeData validator data needed to stake.\\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\\n /// Only the registrator can call this function.\\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\\n // slither-disable-start reentrancy-eth\\n function stakeEth(\\n ValidatorStakeData calldata validatorStakeData,\\n uint64 depositAmountGwei\\n ) external onlyRegistrator {\\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\\n // Check there is enough WETH from the deposits sitting in this strategy contract\\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\\n // the ETH can be withdrawn and then deposited back to the strategy.\\n require(\\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\\n \\\"Insufficient WETH\\\"\\n );\\n require(depositList.length < MAX_DEPOSITS, \\\"Max deposits\\\");\\n\\n // Convert required ETH from WETH and do the necessary accounting\\n _convertWethToEth(depositAmountWei);\\n\\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\\n bytes32 pubKeyHash = _hashPubKey(validatorStakeData.pubkey);\\n ValidatorState currentState = validator[pubKeyHash].state;\\n // Can only stake to a validator has have been registered or verified.\\n // Can not stake to a validator that has been staked but not yet verified.\\n require(\\n (currentState == ValidatorState.REGISTERED ||\\n currentState == ValidatorState.VERIFIED),\\n \\\"Not registered or verified\\\"\\n );\\n require(depositAmountWei >= 1 ether, \\\"Deposit too small\\\");\\n if (currentState == ValidatorState.REGISTERED) {\\n // Can only have one pending deposit to an unverified validator at a time.\\n // This is to limit front-running deposit attacks to a single deposit.\\n // The exiting deposit needs to be verified before another deposit can be made.\\n // If there was a front-running attack, the validator needs to be verified as invalid\\n // and the Governor calls `resetFirstDeposit` to set `firstDeposit` to false.\\n require(!firstDeposit, \\\"Existing first deposit\\\");\\n // Limits the amount of ETH that can be at risk from a front-running deposit attack.\\n require(\\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\\n \\\"Invalid first deposit amount\\\"\\n );\\n // Limits the number of validator balance proofs to verifyBalances\\n require(\\n verifiedValidators.length + 1 < MAX_VERIFIED_VALIDATORS,\\n \\\"Max validators\\\"\\n );\\n\\n // Flag a deposit to an unverified validator so only no other deposits can be made\\n // to an unverified validator.\\n firstDeposit = true;\\n }\\n\\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\\n * that was introduced with the Pectra upgrade.\\n * bytes11(0) to fill up the required zeros\\n * remaining bytes20 are for the address\\n */\\n bytes memory withdrawalCredentials = abi.encodePacked(\\n bytes1(0x02),\\n bytes11(0),\\n address(this)\\n );\\n\\n // Deposit to the Beacon Chain deposit contract.\\n // This will create a deposit in the beacon chain's pending deposit queue.\\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\\n value: depositAmountWei\\n }(\\n validatorStakeData.pubkey,\\n withdrawalCredentials,\\n validatorStakeData.signature,\\n validatorStakeData.depositDataRoot\\n );\\n\\n //// Update contract storage\\n // Store the validator state if needed\\n if (currentState == ValidatorState.REGISTERED) {\\n validator[pubKeyHash].state = ValidatorState.STAKED;\\n }\\n\\n /// After the Pectra upgrade the validators have a new restriction when proposing\\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\\n /// forward. Each slot is created at strict 12 second intervals and those slots can\\n /// either have blocks attached to them or not. This way using the block.timestamp\\n /// the slot number can easily be calculated.\\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\\n BEACON_GENESIS_TIMESTAMP) / SLOT_DURATION;\\n\\n // Store the deposit data for verifyDeposit and verifyBalances\\n uint256 depositID = nextDepositID++;\\n deposits[depositID] = DepositData({\\n pubKeyHash: pubKeyHash,\\n amountGwei: depositAmountGwei,\\n slot: depositSlot,\\n depositIndex: SafeCast.toUint32(depositList.length),\\n status: DepositStatus.PENDING,\\n withdrawableEpoch: FAR_FUTURE_EPOCH\\n });\\n depositList.push(depositID);\\n\\n emit ETHStaked(\\n pubKeyHash,\\n depositID,\\n validatorStakeData.pubkey,\\n depositAmountWei\\n );\\n }\\n\\n // slither-disable-end reentrancy-eth\\n\\n /// @notice Request a full or partial withdrawal from a validator.\\n /// A zero amount will trigger a full withdrawal.\\n /// If the remaining balance is < 32 ETH then only the amount in excess of 32 ETH will be withdrawn.\\n /// Only the Registrator can call this function.\\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\\n /// If no ETH balance, the tx will revert.\\n /// @param publicKey The public key of the validator\\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\\n /// A zero amount will trigger a full withdrawal.\\n // slither-disable-start reentrancy-no-eth\\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\\n external\\n payable\\n onlyRegistrator\\n {\\n // Hash the public key using the Beacon Chain's format\\n bytes32 pubKeyHash = _hashPubKey(publicKey);\\n ValidatorState currentState = validator[pubKeyHash].state;\\n require(\\n currentState == ValidatorState.VERIFIED,\\n \\\"Validator not verified\\\"\\n );\\n\\n PartialWithdrawal.request(publicKey, amountGwei);\\n\\n // If a full withdrawal (validator exit)\\n if (amountGwei == 0) {\\n // Store the validator state as exiting so no more deposits can be made to it.\\n validator[pubKeyHash].state = ValidatorState.EXITING;\\n }\\n\\n // Do not remove from the list of verified validators.\\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\\n // The validator state will be set to EXITED in the verifyBalances function.\\n\\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\\n }\\n\\n // slither-disable-end reentrancy-no-eth\\n\\n /// @notice Remove the validator from the SSV Cluster after:\\n /// - the validator has been exited from `validatorWithdrawal` or slashed\\n /// - the validator has incorrectly registered and can not be staked to\\n /// - the initial deposit was front-run and the withdrawal address is not this strategy's address.\\n /// Make sure `validatorWithdrawal` is called with a zero amount and the validator has exited the Beacon chain.\\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\\n /// Only the registrator can call this function.\\n /// @param publicKey The public key of the validator\\n /// @param operatorIds The operator IDs of the SSV Cluster\\n /// @param cluster The SSV cluster details including the validator count and SSV balance\\n // slither-disable-start reentrancy-no-eth\\n function removeSsvValidator(\\n bytes calldata publicKey,\\n uint64[] calldata operatorIds,\\n Cluster calldata cluster\\n ) external onlyRegistrator {\\n // Hash the public key using the Beacon Chain's format\\n bytes32 pubKeyHash = _hashPubKey(publicKey);\\n ValidatorState currentState = validator[pubKeyHash].state;\\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\\n require(\\n currentState == ValidatorState.REGISTERED ||\\n currentState == ValidatorState.EXITED ||\\n currentState == ValidatorState.INVALID,\\n \\\"Validator not regd or exited\\\"\\n );\\n\\n ISSVNetwork(SSV_NETWORK).removeValidator(\\n publicKey,\\n operatorIds,\\n cluster\\n );\\n\\n validator[pubKeyHash].state = ValidatorState.REMOVED;\\n\\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\\n }\\n\\n /**\\n *\\n * SSV Management\\n *\\n */\\n\\n // slither-disable-end reentrancy-no-eth\\n\\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\\n /// by the Strategist which is already holding SSV tokens.\\n\\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\\n /// @param operatorIds The operator IDs of the SSV Cluster\\n /// @param ssvAmount The amount of SSV tokens to be withdrawn from the SSV cluster\\n /// @param cluster The SSV cluster details including the validator count and SSV balance\\n function withdrawSSV(\\n uint64[] memory operatorIds,\\n uint256 ssvAmount,\\n Cluster memory cluster\\n ) external onlyGovernor {\\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\\n }\\n\\n /**\\n *\\n * Beacon Chain Proofs\\n *\\n */\\n\\n /// @notice Verifies a validator's index to its public key.\\n /// Adds to the list of verified validators if the validator's withdrawal address is this strategy's address.\\n /// Marks the validator as invalid and removes the deposit if the withdrawal address is not this strategy's address.\\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\\n /// we are verifying.\\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\\n /// which is the beacon block root of the previous block.\\n /// @param validatorIndex The index of the validator on the beacon chain.\\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\\n /// @param withdrawalAddress The withdrawal address of the validator which should be this strategy's address.\\n /// If the withdrawal address is not this strategy's address, the initial deposit was front-run\\n /// and the validator is marked as invalid.\\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n /// BeaconBlock.state.validators[validatorIndex].pubkey\\n function verifyValidator(\\n uint64 nextBlockTimestamp,\\n uint64 validatorIndex,\\n bytes32 pubKeyHash,\\n address withdrawalAddress,\\n bytes calldata validatorPubKeyProof\\n ) external {\\n require(\\n validator[pubKeyHash].state == ValidatorState.STAKED,\\n \\\"Validator not staked\\\"\\n );\\n\\n // Get the beacon block root of the slot we are verifying the validator in.\\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\\n\\n // Verify the validator index is for the validator with the given public key.\\n // Also verify the validator's withdrawal credential points to the `withdrawalAddress`.\\n IBeaconProofs(BEACON_PROOFS).verifyValidator(\\n blockRoot,\\n pubKeyHash,\\n validatorPubKeyProof,\\n validatorIndex,\\n withdrawalAddress\\n );\\n\\n // If the initial deposit was front-run and the withdrawal address is not this strategy\\n if (withdrawalAddress != address(this)) {\\n validator[pubKeyHash] = ValidatorData({\\n state: ValidatorState.INVALID,\\n index: validatorIndex\\n });\\n\\n // Find and remove the deposit as the funds can not be recovered\\n uint256 depositCount = depositList.length;\\n for (uint256 i = 0; i < depositCount; i++) {\\n DepositData memory deposit = deposits[depositList[i]];\\n if (deposit.pubKeyHash == pubKeyHash) {\\n _removeDeposit(depositList[i], deposit);\\n break;\\n }\\n }\\n\\n // Leave the `firstDeposit` flag as true so no more deposits to unverified validators can be made.\\n // The Governor has to reset the `firstDeposit` to false before another deposit to\\n // an unverified validator can be made.\\n // The Governor can set a new `validatorRegistrator` if they suspect it has been compromised.\\n\\n emit ValidatorInvalid(pubKeyHash);\\n return;\\n }\\n\\n // Store the validator state as verified\\n validator[pubKeyHash] = ValidatorData({\\n state: ValidatorState.VERIFIED,\\n index: validatorIndex\\n });\\n\\n // Add the new validator to the list of verified validators\\n verifiedValidators.push(pubKeyHash);\\n\\n // Reset the firstDeposit flag as the first deposit to an unverified validator has been verified.\\n firstDeposit = false;\\n\\n emit ValidatorVerified(pubKeyHash, validatorIndex);\\n }\\n\\n struct FirstPendingDepositProofData {\\n uint64 slot;\\n uint64 validatorIndex;\\n bytes32 pubKeyHash;\\n bytes pendingDepositPubKeyProof;\\n bytes withdrawableEpochProof;\\n bytes validatorPubKeyProof;\\n }\\n\\n struct DepositValidatorProofData {\\n uint64 withdrawableEpoch;\\n bytes withdrawableEpochProof;\\n }\\n\\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\\n ///\\n /// Important: this function has a limitation where the `verificationSlot` that is passed by the off-chain\\n /// verifier requires a slot immediately after it to propose a block otherwise the `BeaconRoots.parentBlockRoot`\\n /// will fail. This shouldn't be a problem, since by the current behaviour of beacon chain only 1%-3% slots\\n /// don't propose a block.\\n /// @param depositID The deposit ID emitted in `ETHStaked` from the `stakeEth` function.\\n /// @param depositProcessedSlot Any slot on or after the strategy's deposit was processed on the beacon chain.\\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\\n /// set for the next block timestamp in 12 seconds time.\\n /// @param firstDepositValidatorCreatedSlot The slot on or after when the validator of the first pending deposit\\n /// was created on the beacon chain. This is used to verify the validator has not exited.\\n // slither-disable-start reentrancy-no-eth\\n function verifyDeposit(\\n uint256 depositID,\\n uint64 depositProcessedSlot,\\n uint64 firstDepositValidatorCreatedSlot,\\n FirstPendingDepositProofData calldata firstPendingDeposit,\\n DepositValidatorProofData calldata strategyValidatorData\\n ) external {\\n // Load into memory the previously saved deposit data\\n DepositData memory deposit = deposits[depositID];\\n ValidatorData memory strategyValidator = validator[deposit.pubKeyHash];\\n require(deposit.status == DepositStatus.PENDING, \\\"Deposit not pending\\\");\\n require(\\n strategyValidator.state == ValidatorState.VERIFIED,\\n \\\"Validator not verified\\\"\\n );\\n // The verification slot must be after the deposit's slot.\\n // This is needed for when the deposit queue is empty.\\n require(deposit.slot < depositProcessedSlot, \\\"Slot not after deposit\\\");\\n require(\\n depositProcessedSlot <= firstDepositValidatorCreatedSlot,\\n \\\"Invalid verification slots\\\"\\n );\\n\\n // Get the parent beacon block root of the next block which is the block root of the deposit verification slot.\\n // This will revert if the slot after the verification slot was missed.\\n bytes32 depositBlockRoot = BeaconRoots.parentBlockRoot(\\n _calcNextBlockTimestamp(depositProcessedSlot)\\n );\\n\\n // Verify the slot of the first pending deposit matches the beacon chain\\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\\n .verifyFirstPendingDeposit(\\n depositBlockRoot,\\n firstPendingDeposit.slot,\\n firstPendingDeposit.pubKeyHash,\\n firstPendingDeposit.pendingDepositPubKeyProof\\n );\\n\\n // If the deposit queue is not empty\\n if (!isDepositQueueEmpty) {\\n // Get the parent beacon block root of the next block which is\\n // the block root of the validator verification slot.\\n // This will revert if the slot after the verification slot was missed.\\n bytes32 validatorBlockRoot = BeaconRoots.parentBlockRoot(\\n _calcNextBlockTimestamp(firstDepositValidatorCreatedSlot)\\n );\\n\\n // Verify the validator of the first pending deposit is not exiting.\\n // If it is exiting we can't be sure this deposit has not been postponed in the deposit queue.\\n // Hence we can not verify if the strategy's deposit has been processed or not.\\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\\n validatorBlockRoot,\\n firstPendingDeposit.validatorIndex,\\n firstPendingDeposit.pubKeyHash,\\n FAR_FUTURE_EPOCH,\\n firstPendingDeposit.withdrawableEpochProof,\\n firstPendingDeposit.validatorPubKeyProof\\n );\\n }\\n\\n // Verify the withdrawableEpoch on the validator of the strategy's deposit\\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\\n depositBlockRoot,\\n strategyValidator.index,\\n strategyValidatorData.withdrawableEpoch,\\n strategyValidatorData.withdrawableEpochProof\\n );\\n\\n // If the validator is exiting because it has been slashed\\n if (strategyValidatorData.withdrawableEpoch != FAR_FUTURE_EPOCH) {\\n // Store the exit epoch in the deposit data\\n deposit.withdrawableEpoch = strategyValidatorData.withdrawableEpoch;\\n\\n emit DepositToValidatorExiting(\\n depositID,\\n uint256(deposit.amountGwei) * 1 gwei,\\n strategyValidatorData.withdrawableEpoch\\n );\\n\\n validator[deposit.pubKeyHash].state = ValidatorState.EXITING;\\n\\n // Leave the deposit status as PENDING\\n return;\\n }\\n\\n // solhint-disable max-line-length\\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\\n // many deposits in the same block, hence have the same pending deposit slot.\\n // If the deposit queue is empty then our deposit must have been processed on the beacon chain.\\n // The deposit slot can be zero for validators consolidating to a compounding validator or 0x01 validator\\n // being promoted to a compounding one. Reference:\\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\\n // We can not guarantee that the deposit has been processed in that case.\\n // solhint-enable max-line-length\\n require(\\n deposit.slot < firstPendingDeposit.slot || isDepositQueueEmpty,\\n \\\"Deposit likely not processed\\\"\\n );\\n\\n // Remove the deposit now it has been verified as processed on the beacon chain.\\n _removeDeposit(depositID, deposit);\\n\\n emit DepositVerified(depositID, uint256(deposit.amountGwei) * 1 gwei);\\n }\\n\\n function _removeDeposit(uint256 depositID, DepositData memory deposit)\\n internal\\n {\\n // After verifying the proof, update the contract storage\\n deposits[depositID].status = DepositStatus.VERIFIED;\\n // Move the last deposit to the index of the verified deposit\\n uint256 lastDeposit = depositList[depositList.length - 1];\\n depositList[deposit.depositIndex] = lastDeposit;\\n deposits[lastDeposit].depositIndex = deposit.depositIndex;\\n // Delete the last deposit from the list\\n depositList.pop();\\n }\\n\\n /// @dev Calculates the timestamp of the next execution block from the given slot.\\n /// @param slot The beacon chain slot number used for merkle proof verification.\\n function _calcNextBlockTimestamp(uint64 slot)\\n internal\\n view\\n returns (uint64)\\n {\\n // Calculate the next block timestamp from the slot.\\n return SLOT_DURATION * slot + BEACON_GENESIS_TIMESTAMP + SLOT_DURATION;\\n }\\n\\n // slither-disable-end reentrancy-no-eth\\n\\n /// @notice Stores the current ETH balance at the current block and beacon block root\\n /// of the slot that is associated with the previous block.\\n ///\\n /// When snapping / verifying balance it is of a high importance that there is no\\n /// miss-match in respect to ETH that is held by the contract and balances that are\\n /// verified on the validators.\\n ///\\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\\n /// constructing a block on the beacon chain consist of:\\n /// - process_withdrawals: ETH is deducted from the validator's balance\\n /// - process_execution_payload: immediately after the previous step executing all the\\n /// transactions\\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\\n /// contained in the withdrawal credentials of the exited validators\\n ///\\n /// That means that balance increases which are part of the post-block execution state are\\n /// done within the block, but the transaction that are contained within that block can not\\n /// see / interact with the balance from the exited validators. Only transactions in the\\n /// next block can do that.\\n ///\\n /// When snap balances is performed the state of the chain is snapped across 2 separate\\n /// chain states:\\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\\n /// will be referred to as Y - 1 as it makes no difference in the argument\\n ///\\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\\n /// miss-counting ETH or much more dangerous double counting of the ETH.\\n ///\\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\\n /// - ETH balance in the contract on block X\\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\\n /// - ETH balance in validators that are active in slot Y - 1\\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\\n /// and have their balance visible to transactions in slot Y and corresponding block X\\n /// (or sooner)\\n ///\\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\\n ///\\n /// *ETH balance in the contract on block X*\\n ///\\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\\n ///\\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\\n /// would result in double counting the `stakedEth` since it would be present once in the\\n /// snapped contract balance and the second time in deposit storage variables.\\n ///\\n /// This behaviour is correct.\\n ///\\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\\n ///\\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\\n /// contract at block Z. The execution layer doesn't have direct access to the state of\\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\\n /// and could also be part of the validator balances. It does that by verifying that at\\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\\n /// the last snap till now all are still in queue. Which ensures they can not be part of\\n /// the validator balances in later steps.\\n ///\\n /// This behaviour is correct.\\n ///\\n /// *ETH balance in validators that are active in slot Y - 1*\\n ///\\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\\n /// correctness.\\n ///\\n /// This behaviour is correct.\\n ///\\n /// *The withdrawn validators*\\n ///\\n /// The withdrawn validators could have their balances deducted in any slot before slot\\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\\n /// look at the \\\"worst case scenario\\\" where the validator withdrawal is processed in the\\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\\n /// even if the validator exits at the latest possible time it is paramount that the ETH\\n /// balance on the execution layer is recorded in the next block. Correctly accounting\\n /// for the withdrawn ETH.\\n ///\\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\\n /// validator balance verification as well as execution layer contract balance snap.\\n ///\\n /// This behaviour is correct.\\n ///\\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\\n function snapBalances() external {\\n uint64 currentTimestamp = SafeCast.toUint64(block.timestamp);\\n require(\\n lastSnapTimestamp + SNAP_BALANCES_DELAY < currentTimestamp,\\n \\\"Snap too soon\\\"\\n );\\n\\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(currentTimestamp);\\n // Get the current ETH balance\\n uint256 ethBalance = address(this).balance;\\n\\n // Store the balances in the mapping\\n snappedBalances[blockRoot] = Balances({\\n timestamp: currentTimestamp,\\n ethBalance: SafeCast.toUint128(ethBalance)\\n });\\n\\n // Store the snapped timestamp\\n lastSnapTimestamp = currentTimestamp;\\n\\n emit BalancesSnapped(blockRoot, ethBalance);\\n }\\n\\n // A struct is used to avoid stack too deep errors\\n struct BalanceProofs {\\n // BeaconBlock.state.balances\\n bytes32 balancesContainerRoot;\\n bytes balancesContainerProof;\\n // BeaconBlock.state.balances[validatorIndex]\\n bytes32[] validatorBalanceLeaves;\\n bytes[] validatorBalanceProofs;\\n }\\n\\n /// @notice Verifies the balances of all active validators on the beacon chain\\n /// and checks no pending deposits have been processed by the beacon chain.\\n /// @param snapBlockRoot The beacon block root emitted from `snapBalance` in `BalancesSnapped`.\\n /// @param balanceProofs a `BalanceProofs` struct containing the following:\\n /// balancesContainerRoot - the merkle root of the balances container\\n /// balancesContainerProof - The merkle proof for the balances container to the beacon block root.\\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n /// validatorBalanceLeaves - Array of leaf nodes containing the validator balance with three other balances.\\n /// validatorBalanceProofs - Array of merkle proofs for the validator balance to the Balances container root.\\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n // slither-disable-start reentrancy-no-eth\\n function verifyBalances(\\n bytes32 snapBlockRoot,\\n uint64 validatorVerificationBlockTimestamp,\\n FirstPendingDepositProofData calldata firstPendingDeposit,\\n BalanceProofs calldata balanceProofs\\n ) external {\\n // Load previously snapped balances for the given block root\\n Balances memory balancesMem = snappedBalances[snapBlockRoot];\\n // Check the balances are the latest\\n require(lastSnapTimestamp > 0, \\\"No snapped balances\\\");\\n require(balancesMem.timestamp == lastSnapTimestamp, \\\"Stale snap\\\");\\n\\n uint256 verifiedValidatorsCount = verifiedValidators.length;\\n uint256 totalValidatorBalance = 0;\\n\\n // If there are no verified validators then we can skip the balance verification\\n if (verifiedValidatorsCount > 0) {\\n require(\\n balanceProofs.validatorBalanceProofs.length ==\\n verifiedValidatorsCount,\\n \\\"Invalid balance proofs\\\"\\n );\\n require(\\n balanceProofs.validatorBalanceLeaves.length ==\\n verifiedValidatorsCount,\\n \\\"Invalid balance leaves\\\"\\n );\\n // verify beaconBlock.state.balances root to beacon block root\\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\\n snapBlockRoot,\\n balanceProofs.balancesContainerRoot,\\n balanceProofs.balancesContainerProof\\n );\\n\\n // for each validator in reserve order so we can pop off exited validators at the end\\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\\n --i;\\n // verify validator's balance in beaconBlock.state.balances to the\\n // beaconBlock.state.balances container root\\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\\n .verifyValidatorBalance(\\n balanceProofs.balancesContainerRoot,\\n balanceProofs.validatorBalanceLeaves[i],\\n balanceProofs.validatorBalanceProofs[i],\\n validator[verifiedValidators[i]].index\\n );\\n\\n // If the validator balance is zero\\n if (validatorBalanceGwei == 0) {\\n // Store the validator state as exited\\n // This could have been in VERIFIED or EXITING state\\n validator[verifiedValidators[i]].state = ValidatorState\\n .EXITED;\\n\\n // Remove the validator with a zero balance from the list of verified validators\\n\\n // Reduce the count of verified validators which is the last index before the pop removes it.\\n verifiedValidatorsCount -= 1;\\n\\n // Move the last validator that has already been verified to the current index.\\n // There's an extra SSTORE if i is the last active validator but that's fine,\\n // It's not a common case and the code is simpler this way.\\n verifiedValidators[i] = verifiedValidators[\\n verifiedValidatorsCount\\n ];\\n // Delete the last validator from the list\\n verifiedValidators.pop();\\n\\n // The validator balance is zero so not need to add to totalValidatorBalance\\n continue;\\n }\\n\\n // convert Gwei balance to Wei and add to the total validator balance\\n totalValidatorBalance += validatorBalanceGwei * 1 gwei;\\n }\\n }\\n\\n uint256 depositsCount = depositList.length;\\n uint256 totalDepositsWei = 0;\\n\\n // If there are no deposits then we can skip the deposit verification.\\n // This section is after the validator balance verifications so an exited validator will be marked\\n // as EXITED before the deposits are verified. If there was a deposit to an exited validator\\n // then the deposit can only be removed once the validator is fully exited.\\n if (depositsCount > 0) {\\n // Verify the slot of the first pending deposit matches the beacon chain\\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\\n .verifyFirstPendingDeposit(\\n snapBlockRoot,\\n firstPendingDeposit.slot,\\n firstPendingDeposit.pubKeyHash,\\n firstPendingDeposit.pendingDepositPubKeyProof\\n );\\n\\n // If there are no deposits in the beacon chain queue then our deposits must have been processed.\\n // If the deposits have been processed, each deposit will need to be verified with `verifyDeposit`\\n require(!isDepositQueueEmpty, \\\"Deposits have been processed\\\");\\n\\n // The verification of the validator the first pending deposit is for must be on or after when\\n // `snapBalances` was called.\\n require(\\n balancesMem.timestamp <= validatorVerificationBlockTimestamp,\\n \\\"Invalid validator timestamp\\\"\\n );\\n\\n // Verify the validator of the first pending deposit is not exiting by checking\\n // the withdrawable epoch is far into the future.\\n // If it is exiting we can't be sure this deposit has not been postponed in the deposit queue.\\n // Hence we can not verify if the strategy's deposit has been processed or not.\\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\\n // Get the parent beacon block root of the next block which is\\n // the block root of the validator verification slot.\\n // This will revert if the slot after the verification slot was missed.\\n BeaconRoots.parentBlockRoot(\\n validatorVerificationBlockTimestamp\\n ),\\n firstPendingDeposit.validatorIndex,\\n firstPendingDeposit.pubKeyHash,\\n // Validator is not exiting\\n FAR_FUTURE_EPOCH,\\n firstPendingDeposit.withdrawableEpochProof,\\n firstPendingDeposit.validatorPubKeyProof\\n );\\n\\n // solhint-disable max-line-length\\n // If a validator is converted from a sweeping validator to a compounding validator, any balance in excess\\n // of the min 32 ETH is put in the pending deposit queue. Reference:\\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\\n // This will have a slot value of zero unfortunately.\\n // We can not prove the strategy's deposits are still pending with a zero slot value so revert the tx.\\n // Another snapBalances will need to be taken that does not have consolidation deposits at the front of the\\n // beacon chain deposit queue.\\n // solhint-enable max-line-length\\n require(\\n firstPendingDeposit.slot > 0,\\n \\\"Invalid first pending deposit\\\"\\n );\\n\\n // Calculate the epoch at the time of the snapBalances\\n uint64 verificationEpoch = (SafeCast.toUint64(\\n balancesMem.timestamp\\n ) - BEACON_GENESIS_TIMESTAMP) / (SLOT_DURATION * SLOTS_PER_EPOCH);\\n\\n // For each staking strategy's deposits\\n for (uint256 i = 0; i < depositsCount; ++i) {\\n uint256 depositID = depositList[i];\\n DepositData memory depositData = deposits[depositID];\\n\\n // Check the stored deposit is still waiting to be processed on the beacon chain.\\n // That is, the first pending deposit slot is before the slot of the staking strategy's deposit.\\n // If the deposit has been processed, it will need to be verified with `verifyDeposit`.\\n // OR the deposit is to an exiting validator so check it is still not withdrawable.\\n // If the validator is not withdrawable, then the deposit can not have been processed yet.\\n // If the validator is now withdrawable, then the deposit may have been processed. The strategy\\n // now has to wait until the validator's balance is verified to be zero.\\n // OR the validator has exited and the deposit is now verified as processed.\\n require(\\n firstPendingDeposit.slot < depositData.slot ||\\n (verificationEpoch < depositData.withdrawableEpoch &&\\n depositData.withdrawableEpoch !=\\n FAR_FUTURE_EPOCH) ||\\n validator[depositData.pubKeyHash].state ==\\n ValidatorState.EXITED,\\n \\\"Deposit likely processed\\\"\\n );\\n\\n // Convert the deposit amount from Gwei to Wei and add to the total\\n totalDepositsWei += uint256(depositData.amountGwei) * 1 gwei;\\n\\n // Remove the deposit if the validator has exited.\\n if (\\n validator[depositData.pubKeyHash].state ==\\n ValidatorState.EXITED\\n ) {\\n _removeDeposit(depositID, depositData);\\n\\n emit DepositValidatorExited(\\n depositID,\\n uint256(depositData.amountGwei) * 1 gwei\\n );\\n }\\n }\\n }\\n\\n // Store the verified balance in storage\\n lastVerifiedEthBalance = SafeCast.toUint128(\\n totalDepositsWei + totalValidatorBalance + balancesMem.ethBalance\\n );\\n // Reset the last snap timestamp so a new snapBalances has to be made\\n lastSnapTimestamp = 0;\\n\\n emit BalancesVerified(\\n balancesMem.timestamp,\\n totalDepositsWei,\\n totalValidatorBalance,\\n balancesMem.ethBalance\\n );\\n }\\n\\n // slither-disable-end reentrancy-no-eth\\n\\n /// @notice Hash a validator public key using the Beacon Chain's format\\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\\n require(pubKey.length == 48, \\\"Invalid public key length\\\");\\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\\n }\\n\\n /**\\n *\\n * WETH and ETH Accounting\\n *\\n */\\n\\n /// @dev Called when WETH is transferred out of the strategy so\\n /// the strategy knows how much WETH it has on deposit.\\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\\n function _transferWeth(uint256 _amount, address _recipient) internal {\\n IERC20(WETH).safeTransfer(_recipient, _amount);\\n\\n // The min is required as more WETH can be withdrawn than deposited\\n // as the strategy earns consensus and execution rewards.\\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\\n depositedWethAccountedFor -= deductAmount;\\n\\n // No change in ETH balance so no need to snapshot the balances\\n }\\n\\n /// @dev Converts ETH to WETH and updates the accounting.\\n /// @param _ethAmount The amount of ETH in wei.\\n function _convertEthToWeth(uint256 _ethAmount) internal {\\n // slither-disable-next-line arbitrary-send-eth\\n IWETH9(WETH).deposit{ value: _ethAmount }();\\n\\n depositedWethAccountedFor += _ethAmount;\\n\\n // Store the reduced ETH balance.\\n // The ETH balance in this strategy contract can be more than the last verified ETH balance\\n // due to partial withdrawals or full exits being processed by the beacon chain since the last snapBalances.\\n // It can also happen from execution rewards (MEV) or ETH donations.\\n lastVerifiedEthBalance -= SafeCast.toUint128(\\n Math.min(uint256(lastVerifiedEthBalance), _ethAmount)\\n );\\n\\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\\n lastSnapTimestamp = 0;\\n }\\n\\n /// @dev Converts WETH to ETH and updates the accounting.\\n /// @param _wethAmount The amount of WETH in wei.\\n function _convertWethToEth(uint256 _wethAmount) internal {\\n IWETH9(WETH).withdraw(_wethAmount);\\n\\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\\n depositedWethAccountedFor -= deductAmount;\\n\\n // Store the increased ETH balance\\n lastVerifiedEthBalance += SafeCast.toUint128(_wethAmount);\\n\\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\\n lastSnapTimestamp = 0;\\n }\\n\\n /**\\n *\\n * View Functions\\n *\\n */\\n\\n /// @notice Returns the number of deposits waiting to be verified as processed on the beacon chain,\\n /// or deposits that have been verified to an exiting validator and is now waiting for the\\n /// validator's balance to be swept.\\n function depositListLength() external view returns (uint256) {\\n return depositList.length;\\n }\\n\\n /// @notice Returns the number of verified validators.\\n function verifiedValidatorsLength() external view returns (uint256) {\\n return verifiedValidators.length;\\n }\\n}\\n\",\"keccak256\":\"0xf190f380916aaef1bee329bf26a708f3718f1c3f87117d2a392feddc00efd7c2\",\"license\":\"BUSL-1.1\"}},\"version\":1}", + "bytecode": "0x60a060405234801561001057600080fd5b5060405161099138038061099183398101604081905261002f91610040565b6001600160a01b0316608052610070565b60006020828403121561005257600080fd5b81516001600160a01b038116811461006957600080fd5b9392505050565b6080516108d76100ba60003960008181604b0152818160ba015281816101ce01528181610248015281816103440152818161044e0152818161047d015261058a01526108d76000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80632e4aee1f146100465780635e1c519b1461008a578063d1699c261461009f575b600080fd5b61006d7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b6100926100b4565b6040516100819190610688565b6100a761033e565b604051610081919061070b565b606060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d79e40326040518163ffffffff1660e01b8152600401602060405180830381865afa158015610116573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061013a9190610787565b90508067ffffffffffffffff811115610155576101556107a0565b6040519080825280602002602001820160405280156101a757816020015b61019460408051606081018252600080825260208201819052909182015290565b8152602001906001900390816101735790505b50915060005b8181101561033957604051630ef9985560e01b8152600481018290526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690630ef9985590602401602060405180830381865afa15801561021d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102419190610787565b90506000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166398245f1b846040518263ffffffff1660e01b815260040161029491815260200190565b6040805180830381865afa1580156102b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102d491906107d3565b9150915060405180606001604052808481526020018267ffffffffffffffff16815260200183600781111561030b5761030b610672565b8152508685815181106103205761032061080c565b60200260200101819052505050508060010190506101ad565b505090565b606060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634896b31a6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103c49190610787565b90508067ffffffffffffffff8111156103df576103df6107a0565b60405190808252806020026020018201604052801561043857816020015b6040805160a0810182526000808252602080830182905292820181905260608201819052608082015282526000199092019101816103fd5790505b50915060005b81811015610339576000806000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b02c43d07f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b8ec6678886040518263ffffffff1660e01b81526004016104c991815260200190565b602060405180830381865afa1580156104e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061050a9190610787565b6040518263ffffffff1660e01b815260040161052891815260200190565b60c060405180830381865afa158015610545573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105699190610822565b67ffffffffffffffff16955050509350935093506040518060a001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b8ec6678886040518263ffffffff1660e01b81526004016105d691815260200190565b602060405180830381865afa1580156105f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106179190610787565b81526020018581526020018467ffffffffffffffff1681526020018367ffffffffffffffff168152602001828152508786815181106106585761065861080c565b60200260200101819052505050505080600101905061043e565b634e487b7160e01b600052602160045260246000fd5b602080825282518282018190526000918401906040840190835b818110156107005783518051845260208082015167ffffffffffffffff169085015260400151600881106106e657634e487b7160e01b600052602160045260246000fd5b6040840152602093909301926060909201916001016106a2565b509095945050505050565b602080825282518282018190526000918401906040840190835b81811015610700578351805184526020810151602085015267ffffffffffffffff604082015116604085015267ffffffffffffffff6060820151166060850152608081015160808501525060a083019250602084019350600181019050610725565b60006020828403121561079957600080fd5b5051919050565b634e487b7160e01b600052604160045260246000fd5b805167ffffffffffffffff811681146107ce57600080fd5b919050565b600080604083850312156107e657600080fd5b8251600881106107f557600080fd5b9150610803602084016107b6565b90509250929050565b634e487b7160e01b600052603260045260246000fd5b60008060008060008060c0878903121561083b57600080fd5b8651955061084b602088016107b6565b9450610859604088016107b6565b9350606087015163ffffffff8116811461087257600080fd5b60808801519093506003811061088757600080fd5b915061089560a088016107b6565b9050929550929550929556fea2646970667358221220b66db0bfafe8ca64b4b2d898b3f8529d9f89c4798781e96df855edc4ea42333964736f6c634300081c0033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100415760003560e01c80632e4aee1f146100465780635e1c519b1461008a578063d1699c261461009f575b600080fd5b61006d7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b6100926100b4565b6040516100819190610688565b6100a761033e565b604051610081919061070b565b606060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d79e40326040518163ffffffff1660e01b8152600401602060405180830381865afa158015610116573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061013a9190610787565b90508067ffffffffffffffff811115610155576101556107a0565b6040519080825280602002602001820160405280156101a757816020015b61019460408051606081018252600080825260208201819052909182015290565b8152602001906001900390816101735790505b50915060005b8181101561033957604051630ef9985560e01b8152600481018290526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690630ef9985590602401602060405180830381865afa15801561021d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102419190610787565b90506000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166398245f1b846040518263ffffffff1660e01b815260040161029491815260200190565b6040805180830381865afa1580156102b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102d491906107d3565b9150915060405180606001604052808481526020018267ffffffffffffffff16815260200183600781111561030b5761030b610672565b8152508685815181106103205761032061080c565b60200260200101819052505050508060010190506101ad565b505090565b606060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634896b31a6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103c49190610787565b90508067ffffffffffffffff8111156103df576103df6107a0565b60405190808252806020026020018201604052801561043857816020015b6040805160a0810182526000808252602080830182905292820181905260608201819052608082015282526000199092019101816103fd5790505b50915060005b81811015610339576000806000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b02c43d07f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b8ec6678886040518263ffffffff1660e01b81526004016104c991815260200190565b602060405180830381865afa1580156104e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061050a9190610787565b6040518263ffffffff1660e01b815260040161052891815260200190565b60c060405180830381865afa158015610545573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105699190610822565b67ffffffffffffffff16955050509350935093506040518060a001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b8ec6678886040518263ffffffff1660e01b81526004016105d691815260200190565b602060405180830381865afa1580156105f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106179190610787565b81526020018581526020018467ffffffffffffffff1681526020018367ffffffffffffffff168152602001828152508786815181106106585761065861080c565b60200260200101819052505050505080600101905061043e565b634e487b7160e01b600052602160045260246000fd5b602080825282518282018190526000918401906040840190835b818110156107005783518051845260208082015167ffffffffffffffff169085015260400151600881106106e657634e487b7160e01b600052602160045260246000fd5b6040840152602093909301926060909201916001016106a2565b509095945050505050565b602080825282518282018190526000918401906040840190835b81811015610700578351805184526020810151602085015267ffffffffffffffff604082015116604085015267ffffffffffffffff6060820151166060850152608081015160808501525060a083019250602084019350600181019050610725565b60006020828403121561079957600080fd5b5051919050565b634e487b7160e01b600052604160045260246000fd5b805167ffffffffffffffff811681146107ce57600080fd5b919050565b600080604083850312156107e657600080fd5b8251600881106107f557600080fd5b9150610803602084016107b6565b90509250929050565b634e487b7160e01b600052603260045260246000fd5b60008060008060008060c0878903121561083b57600080fd5b8651955061084b602088016107b6565b9450610859604088016107b6565b9350606087015163ffffffff8116811461087257600080fd5b60808801519093506003811061088757600080fd5b915061089560a088016107b6565b9050929550929550929556fea2646970667358221220b66db0bfafe8ca64b4b2d898b3f8529d9f89c4798781e96df855edc4ea42333964736f6c634300081c0033", + "libraries": {}, + "devdoc": { + "author": "Origin Protocol Inc", + "kind": "dev", + "methods": { + "getPendingDeposits()": { + "returns": { + "pendingDeposits": "An array of `DepositView` containing the deposit ID, public key hash, amount in Gwei and the slot of the deposit." + } + }, + "getVerifiedValidators()": { + "returns": { + "validators": "An array of `ValidatorView` containing the public key hash, validator index and state." + } + } + }, + "title": "Viewing contract for the Compounding Staking Strategy.", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "getPendingDeposits()": { + "notice": "Returns the deposits that are still to be verified. These may or may not have been processed by the beacon chain." + }, + "getVerifiedValidators()": { + "notice": "Returns the strategy's active validators. These are the ones that have been verified and have a non-zero balance." + }, + "stakingStrategy()": { + "notice": "The address of the Compounding Staking Strategy contract" + } + }, + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/CompoundingStakingStrategyView.json b/contracts/deployments/hoodi/CompoundingStakingStrategyView.json new file mode 100644 index 0000000000..495d403b57 --- /dev/null +++ b/contracts/deployments/hoodi/CompoundingStakingStrategyView.json @@ -0,0 +1,156 @@ +{ + "address": "0x13eDDe0650E41f3B54E43f6783EA6eFD49F0C804", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_stakingStrategy", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "getPendingDeposits", + "outputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "pendingDepositRoot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "amountGwei", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "slot", + "type": "uint64" + } + ], + "internalType": "struct CompoundingStakingStrategyView.DepositView[]", + "name": "pendingDeposits", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getVerifiedValidators", + "outputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "enum CompoundingValidatorManager.ValidatorState", + "name": "state", + "type": "uint8" + } + ], + "internalType": "struct CompoundingStakingStrategyView.ValidatorView[]", + "name": "validators", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "stakingStrategy", + "outputs": [ + { + "internalType": "contract CompoundingValidatorManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "transactionHash": "0x79ddfab057b40c1dd914c9fca7efb9b4f4cbca854b7dde7d6ac00c067c332bed", + "receipt": { + "to": null, + "from": "0xf7749B41db006860cEc0650D18b8013d69C44Eeb", + "contractAddress": "0x13eDDe0650E41f3B54E43f6783EA6eFD49F0C804", + "transactionIndex": 40, + "gasUsed": "534977", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x94fef1970b71fc744b33dde9a4a264dcb4519d8bca4e9d426a4ef3bd45800387", + "transactionHash": "0x79ddfab057b40c1dd914c9fca7efb9b4f4cbca854b7dde7d6ac00c067c332bed", + "logs": [], + "blockNumber": 1217624, + "cumulativeGasUsed": "59410175", + "status": 1, + "byzantium": true + }, + "args": [ + "0xb5B92DEFC0a9623E31e10Fe0BaE02610bf76fd09" + ], + "numDeployments": 3, + "solcInputHash": "7fa100e84a92f118bfb2e3f652f49c29", + "metadata": "{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_stakingStrategy\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"getPendingDeposits\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"pendingDepositRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"amountGwei\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"slot\",\"type\":\"uint64\"}],\"internalType\":\"struct CompoundingStakingStrategyView.DepositView[]\",\"name\":\"pendingDeposits\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getVerifiedValidators\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"index\",\"type\":\"uint64\"},{\"internalType\":\"enum CompoundingValidatorManager.ValidatorState\",\"name\":\"state\",\"type\":\"uint8\"}],\"internalType\":\"struct CompoundingStakingStrategyView.ValidatorView[]\",\"name\":\"validators\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"stakingStrategy\",\"outputs\":[{\"internalType\":\"contract CompoundingValidatorManager\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"author\":\"Origin Protocol Inc\",\"kind\":\"dev\",\"methods\":{\"getPendingDeposits()\":{\"returns\":{\"pendingDeposits\":\"An array of `DepositView` containing the deposit ID, public key hash, amount in Gwei and the slot of the deposit.\"}},\"getVerifiedValidators()\":{\"returns\":{\"validators\":\"An array of `ValidatorView` containing the public key hash, validator index and state.\"}}},\"title\":\"Viewing contract for the Compounding Staking Strategy.\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"getPendingDeposits()\":{\"notice\":\"Returns the deposits that are still to be verified. These may or may not have been processed by the beacon chain.\"},\"getVerifiedValidators()\":{\"notice\":\"Returns the strategy's active validators. These are the ones that have been verified and have a non-zero balance.\"},\"stakingStrategy()\":{\"notice\":\"The address of the Compounding Staking Strategy contract\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/strategies/NativeStaking/CompoundingStakingView.sol\":\"CompoundingStakingStrategyView\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/security/Pausable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../utils/Context.sol\\\";\\n\\n/**\\n * @dev Contract module which allows children to implement an emergency stop\\n * mechanism that can be triggered by an authorized account.\\n *\\n * This module is used through inheritance. It will make available the\\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\\n * the functions of your contract. Note that they will not be pausable by\\n * simply including this module, only once the modifiers are put in place.\\n */\\nabstract contract Pausable is Context {\\n /**\\n * @dev Emitted when the pause is triggered by `account`.\\n */\\n event Paused(address account);\\n\\n /**\\n * @dev Emitted when the pause is lifted by `account`.\\n */\\n event Unpaused(address account);\\n\\n bool private _paused;\\n\\n /**\\n * @dev Initializes the contract in unpaused state.\\n */\\n constructor() {\\n _paused = false;\\n }\\n\\n /**\\n * @dev Returns true if the contract is paused, and false otherwise.\\n */\\n function paused() public view virtual returns (bool) {\\n return _paused;\\n }\\n\\n /**\\n * @dev Modifier to make a function callable only when the contract is not paused.\\n *\\n * Requirements:\\n *\\n * - The contract must not be paused.\\n */\\n modifier whenNotPaused() {\\n require(!paused(), \\\"Pausable: paused\\\");\\n _;\\n }\\n\\n /**\\n * @dev Modifier to make a function callable only when the contract is paused.\\n *\\n * Requirements:\\n *\\n * - The contract must be paused.\\n */\\n modifier whenPaused() {\\n require(paused(), \\\"Pausable: not paused\\\");\\n _;\\n }\\n\\n /**\\n * @dev Triggers stopped state.\\n *\\n * Requirements:\\n *\\n * - The contract must not be paused.\\n */\\n function _pause() internal virtual whenNotPaused {\\n _paused = true;\\n emit Paused(_msgSender());\\n }\\n\\n /**\\n * @dev Returns to normal state.\\n *\\n * Requirements:\\n *\\n * - The contract must be paused.\\n */\\n function _unpause() internal virtual whenPaused {\\n _paused = false;\\n emit Unpaused(_msgSender());\\n }\\n}\\n\",\"keccak256\":\"0xe68ed7fb8766ed1e888291f881e36b616037f852b37d96877045319ad298ba87\",\"license\":\"MIT\"},\"@openzeppelin/contracts/token/ERC20/IERC20.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Interface of the ERC20 standard as defined in the EIP.\\n */\\ninterface IERC20 {\\n /**\\n * @dev Returns the amount of tokens in existence.\\n */\\n function totalSupply() external view returns (uint256);\\n\\n /**\\n * @dev Returns the amount of tokens owned by `account`.\\n */\\n function balanceOf(address account) external view returns (uint256);\\n\\n /**\\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transfer(address recipient, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Returns the remaining number of tokens that `spender` will be\\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\\n * zero by default.\\n *\\n * This value changes when {approve} or {transferFrom} are called.\\n */\\n function allowance(address owner, address spender) external view returns (uint256);\\n\\n /**\\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\\n * that someone may use both the old and the new allowance by unfortunate\\n * transaction ordering. One possible solution to mitigate this race\\n * condition is to first reduce the spender's allowance to 0 and set the\\n * desired value afterwards:\\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\\n *\\n * Emits an {Approval} event.\\n */\\n function approve(address spender, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\\n * allowance mechanism. `amount` is then deducted from the caller's\\n * allowance.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transferFrom(\\n address sender,\\n address recipient,\\n uint256 amount\\n ) external returns (bool);\\n\\n /**\\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\\n * another (`to`).\\n *\\n * Note that `value` may be zero.\\n */\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n\\n /**\\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n * a call to {approve}. `value` is the new allowance.\\n */\\n event Approval(address indexed owner, address indexed spender, uint256 value);\\n}\\n\",\"keccak256\":\"0x61437cb513a887a1bbad006e7b1c8b414478427d33de47c5600af3c748f108da\",\"license\":\"MIT\"},\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../IERC20.sol\\\";\\nimport \\\"../../../utils/Address.sol\\\";\\n\\n/**\\n * @title SafeERC20\\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\\n * contract returns false). Tokens that return no value (and instead revert or\\n * throw on failure) are also supported, non-reverting calls are assumed to be\\n * successful.\\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\\n */\\nlibrary SafeERC20 {\\n using Address for address;\\n\\n function safeTransfer(\\n IERC20 token,\\n address to,\\n uint256 value\\n ) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\\n }\\n\\n function safeTransferFrom(\\n IERC20 token,\\n address from,\\n address to,\\n uint256 value\\n ) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\\n }\\n\\n /**\\n * @dev Deprecated. This function has issues similar to the ones found in\\n * {IERC20-approve}, and its usage is discouraged.\\n *\\n * Whenever possible, use {safeIncreaseAllowance} and\\n * {safeDecreaseAllowance} instead.\\n */\\n function safeApprove(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n // safeApprove should only be called when setting an initial allowance,\\n // or when resetting it to zero. To increase and decrease it, use\\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\\n require(\\n (value == 0) || (token.allowance(address(this), spender) == 0),\\n \\\"SafeERC20: approve from non-zero to non-zero allowance\\\"\\n );\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\\n }\\n\\n function safeIncreaseAllowance(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n uint256 newAllowance = token.allowance(address(this), spender) + value;\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\\n }\\n\\n function safeDecreaseAllowance(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n unchecked {\\n uint256 oldAllowance = token.allowance(address(this), spender);\\n require(oldAllowance >= value, \\\"SafeERC20: decreased allowance below zero\\\");\\n uint256 newAllowance = oldAllowance - value;\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\\n }\\n }\\n\\n /**\\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\\n * on the return value: the return value is optional (but if data is returned, it must not be false).\\n * @param token The token targeted by the call.\\n * @param data The call data (encoded using abi.encode or one of its variants).\\n */\\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\\n // the target address contains contract code and also asserts for success in the low-level call.\\n\\n bytes memory returndata = address(token).functionCall(data, \\\"SafeERC20: low-level call failed\\\");\\n if (returndata.length > 0) {\\n // Return data is optional\\n require(abi.decode(returndata, (bool)), \\\"SafeERC20: ERC20 operation did not succeed\\\");\\n }\\n }\\n}\\n\",\"keccak256\":\"0xc3d946432c0ddbb1f846a0d3985be71299df331b91d06732152117f62f0be2b5\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize, which returns 0 for contracts in\\n // construction, since the code is only stored at the end of the\\n // constructor execution.\\n\\n uint256 size;\\n assembly {\\n size := extcodesize(account)\\n }\\n return size > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCall(target, data, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n require(isContract(target), \\\"Address: static call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(isContract(target), \\\"Address: delegate call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x51b758a8815ecc9596c66c37d56b1d33883a444631a3f916b9fe65cb863ef7c4\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Context.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Provides information about the current execution context, including the\\n * sender of the transaction and its data. While these are generally available\\n * via msg.sender and msg.data, they should not be accessed in such a direct\\n * manner, since when dealing with meta-transactions the account sending and\\n * paying for execution may not be the actual sender (as far as an application\\n * is concerned).\\n *\\n * This contract is only required for intermediate, library-like contracts.\\n */\\nabstract contract Context {\\n function _msgSender() internal view virtual returns (address) {\\n return msg.sender;\\n }\\n\\n function _msgData() internal view virtual returns (bytes calldata) {\\n return msg.data;\\n }\\n}\\n\",\"keccak256\":\"0xe2e337e6dde9ef6b680e07338c493ebea1b5fd09b43424112868e9cc1706bca7\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/math/Math.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Standard math utilities missing in the Solidity language.\\n */\\nlibrary Math {\\n /**\\n * @dev Returns the largest of two numbers.\\n */\\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a >= b ? a : b;\\n }\\n\\n /**\\n * @dev Returns the smallest of two numbers.\\n */\\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a < b ? a : b;\\n }\\n\\n /**\\n * @dev Returns the average of two numbers. The result is rounded towards\\n * zero.\\n */\\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\\n // (a + b) / 2 can overflow.\\n return (a & b) + (a ^ b) / 2;\\n }\\n\\n /**\\n * @dev Returns the ceiling of the division of two numbers.\\n *\\n * This differs from standard division with `/` in that it rounds up instead\\n * of rounding down.\\n */\\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\\n // (a + b - 1) / b can overflow on addition, so we distribute.\\n return a / b + (a % b == 0 ? 0 : 1);\\n }\\n}\\n\",\"keccak256\":\"0xfaad496c1c944b6259b7dc70b4865eb1775d6402bc0c81b38a0b24d9f525ae37\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/math/SafeCast.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\\n * checks.\\n *\\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\\n * easily result in undesired exploitation or bugs, since developers usually\\n * assume that overflows raise errors. `SafeCast` restores this intuition by\\n * reverting the transaction when such an operation overflows.\\n *\\n * Using this library instead of the unchecked operations eliminates an entire\\n * class of bugs, so it's recommended to use it always.\\n *\\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\\n * all math on `uint256` and `int256` and then downcasting.\\n */\\nlibrary SafeCast {\\n /**\\n * @dev Returns the downcasted uint224 from uint256, reverting on\\n * overflow (when the input is greater than largest uint224).\\n *\\n * Counterpart to Solidity's `uint224` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 224 bits\\n */\\n function toUint224(uint256 value) internal pure returns (uint224) {\\n require(value <= type(uint224).max, \\\"SafeCast: value doesn't fit in 224 bits\\\");\\n return uint224(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint128 from uint256, reverting on\\n * overflow (when the input is greater than largest uint128).\\n *\\n * Counterpart to Solidity's `uint128` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 128 bits\\n */\\n function toUint128(uint256 value) internal pure returns (uint128) {\\n require(value <= type(uint128).max, \\\"SafeCast: value doesn't fit in 128 bits\\\");\\n return uint128(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint96 from uint256, reverting on\\n * overflow (when the input is greater than largest uint96).\\n *\\n * Counterpart to Solidity's `uint96` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 96 bits\\n */\\n function toUint96(uint256 value) internal pure returns (uint96) {\\n require(value <= type(uint96).max, \\\"SafeCast: value doesn't fit in 96 bits\\\");\\n return uint96(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint64 from uint256, reverting on\\n * overflow (when the input is greater than largest uint64).\\n *\\n * Counterpart to Solidity's `uint64` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 64 bits\\n */\\n function toUint64(uint256 value) internal pure returns (uint64) {\\n require(value <= type(uint64).max, \\\"SafeCast: value doesn't fit in 64 bits\\\");\\n return uint64(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint32 from uint256, reverting on\\n * overflow (when the input is greater than largest uint32).\\n *\\n * Counterpart to Solidity's `uint32` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 32 bits\\n */\\n function toUint32(uint256 value) internal pure returns (uint32) {\\n require(value <= type(uint32).max, \\\"SafeCast: value doesn't fit in 32 bits\\\");\\n return uint32(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint16 from uint256, reverting on\\n * overflow (when the input is greater than largest uint16).\\n *\\n * Counterpart to Solidity's `uint16` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 16 bits\\n */\\n function toUint16(uint256 value) internal pure returns (uint16) {\\n require(value <= type(uint16).max, \\\"SafeCast: value doesn't fit in 16 bits\\\");\\n return uint16(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint8 from uint256, reverting on\\n * overflow (when the input is greater than largest uint8).\\n *\\n * Counterpart to Solidity's `uint8` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 8 bits.\\n */\\n function toUint8(uint256 value) internal pure returns (uint8) {\\n require(value <= type(uint8).max, \\\"SafeCast: value doesn't fit in 8 bits\\\");\\n return uint8(value);\\n }\\n\\n /**\\n * @dev Converts a signed int256 into an unsigned uint256.\\n *\\n * Requirements:\\n *\\n * - input must be greater than or equal to 0.\\n */\\n function toUint256(int256 value) internal pure returns (uint256) {\\n require(value >= 0, \\\"SafeCast: value must be positive\\\");\\n return uint256(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int128 from int256, reverting on\\n * overflow (when the input is less than smallest int128 or\\n * greater than largest int128).\\n *\\n * Counterpart to Solidity's `int128` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 128 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt128(int256 value) internal pure returns (int128) {\\n require(value >= type(int128).min && value <= type(int128).max, \\\"SafeCast: value doesn't fit in 128 bits\\\");\\n return int128(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int64 from int256, reverting on\\n * overflow (when the input is less than smallest int64 or\\n * greater than largest int64).\\n *\\n * Counterpart to Solidity's `int64` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 64 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt64(int256 value) internal pure returns (int64) {\\n require(value >= type(int64).min && value <= type(int64).max, \\\"SafeCast: value doesn't fit in 64 bits\\\");\\n return int64(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int32 from int256, reverting on\\n * overflow (when the input is less than smallest int32 or\\n * greater than largest int32).\\n *\\n * Counterpart to Solidity's `int32` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 32 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt32(int256 value) internal pure returns (int32) {\\n require(value >= type(int32).min && value <= type(int32).max, \\\"SafeCast: value doesn't fit in 32 bits\\\");\\n return int32(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int16 from int256, reverting on\\n * overflow (when the input is less than smallest int16 or\\n * greater than largest int16).\\n *\\n * Counterpart to Solidity's `int16` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 16 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt16(int256 value) internal pure returns (int16) {\\n require(value >= type(int16).min && value <= type(int16).max, \\\"SafeCast: value doesn't fit in 16 bits\\\");\\n return int16(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int8 from int256, reverting on\\n * overflow (when the input is less than smallest int8 or\\n * greater than largest int8).\\n *\\n * Counterpart to Solidity's `int8` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 8 bits.\\n *\\n * _Available since v3.1._\\n */\\n function toInt8(int256 value) internal pure returns (int8) {\\n require(value >= type(int8).min && value <= type(int8).max, \\\"SafeCast: value doesn't fit in 8 bits\\\");\\n return int8(value);\\n }\\n\\n /**\\n * @dev Converts an unsigned uint256 into a signed int256.\\n *\\n * Requirements:\\n *\\n * - input must be less than or equal to maxInt256.\\n */\\n function toInt256(uint256 value) internal pure returns (int256) {\\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\\n require(value <= uint256(type(int256).max), \\\"SafeCast: value doesn't fit in an int256\\\");\\n return int256(value);\\n }\\n}\\n\",\"keccak256\":\"0x5c6caab697d302ad7eb59c234a4d2dbc965c1bae87709bd2850060b7695b28c7\",\"license\":\"MIT\"},\"contracts/beacon/BeaconRoots.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Library to retrieve beacon block roots.\\n * @author Origin Protocol Inc\\n */\\nlibrary BeaconRoots {\\n /// @notice The address of beacon block roots oracle\\n /// See https://eips.ethereum.org/EIPS/eip-4788\\n address internal constant BEACON_ROOTS_ADDRESS =\\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\\n\\n /// @notice Returns the beacon block root for the previous block.\\n /// This comes from the Beacon Roots contract defined in EIP-4788.\\n /// This will revert if the block is more than 8,191 blocks old as\\n /// that is the size of the beacon root's ring buffer.\\n /// @param timestamp The timestamp of the block for which to get the parent root.\\n /// @return parentRoot The parent block root for the given timestamp.\\n function parentBlockRoot(uint64 timestamp)\\n internal\\n view\\n returns (bytes32 parentRoot)\\n {\\n // Call the Beacon Roots contract to get the parent block root.\\n // This does not have a function signature, so we use a staticcall.\\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\\n abi.encode(timestamp)\\n );\\n\\n require(success && result.length > 0, \\\"Invalid beacon timestamp\\\");\\n parentRoot = abi.decode(result, (bytes32));\\n }\\n}\\n\",\"keccak256\":\"0x4005989f852a68bbcdc1cdc3472ebd3911395e75b4e6366ffcaae4d1c128691e\",\"license\":\"BUSL-1.1\"},\"contracts/beacon/PartialWithdrawal.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Library to request full or partial withdrawals from validators on the beacon chain.\\n * @author Origin Protocol Inc\\n */\\nlibrary PartialWithdrawal {\\n /// @notice The address where the withdrawal request is sent to\\n /// See https://eips.ethereum.org/EIPS/eip-7002\\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\\n\\n /// @notice Requests a partial withdrawal for a given validator public key and amount.\\n /// @param validatorPubKey The public key of the validator to withdraw from\\n /// @param amount The amount of ETH to withdraw\\n function request(bytes calldata validatorPubKey, uint64 amount)\\n internal\\n returns (uint256 fee_)\\n {\\n require(validatorPubKey.length == 48, \\\"Invalid validator byte length\\\");\\n fee_ = fee();\\n\\n // Call the Withdrawal Request contract with the validator public key\\n // and amount to be withdrawn packed together\\n\\n // This is a general purpose EL to CL request:\\n // https://eips.ethereum.org/EIPS/eip-7685\\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\\n abi.encodePacked(validatorPubKey, amount)\\n );\\n\\n require(success, \\\"Withdrawal request failed\\\");\\n }\\n\\n /// @notice Gets fee for withdrawal requests contract on Beacon chain\\n function fee() internal view returns (uint256) {\\n // Get fee from the withdrawal request contract\\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\\n .staticcall(\\\"\\\");\\n\\n require(success && result.length > 0, \\\"Failed to get fee\\\");\\n return abi.decode(result, (uint256));\\n }\\n}\\n\",\"keccak256\":\"0x80d29153ff7eb5c6841692aca98eb0cc14ac43ad2d8e402890b6c6b6e4a9719d\",\"license\":\"BUSL-1.1\"},\"contracts/governance/Governable.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\\n * from owner to governor and renounce methods removed. Does not use\\n * Context.sol like Ownable.sol does for simplification.\\n * @author Origin Protocol Inc\\n */\\nabstract contract Governable {\\n // Storage position of the owner and pendingOwner of the contract\\n // keccak256(\\\"OUSD.governor\\\");\\n bytes32 private constant governorPosition =\\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\\n\\n // keccak256(\\\"OUSD.pending.governor\\\");\\n bytes32 private constant pendingGovernorPosition =\\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\\n\\n // keccak256(\\\"OUSD.reentry.status\\\");\\n bytes32 private constant reentryStatusPosition =\\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\\n\\n // See OpenZeppelin ReentrancyGuard implementation\\n uint256 constant _NOT_ENTERED = 1;\\n uint256 constant _ENTERED = 2;\\n\\n event PendingGovernorshipTransfer(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n event GovernorshipTransferred(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n /**\\n * @notice Returns the address of the current Governor.\\n */\\n function governor() public view returns (address) {\\n return _governor();\\n }\\n\\n /**\\n * @dev Returns the address of the current Governor.\\n */\\n function _governor() internal view returns (address governorOut) {\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n governorOut := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Returns the address of the pending Governor.\\n */\\n function _pendingGovernor()\\n internal\\n view\\n returns (address pendingGovernor)\\n {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n pendingGovernor := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the Governor.\\n */\\n modifier onlyGovernor() {\\n require(isGovernor(), \\\"Caller is not the Governor\\\");\\n _;\\n }\\n\\n /**\\n * @notice Returns true if the caller is the current Governor.\\n */\\n function isGovernor() public view returns (bool) {\\n return msg.sender == _governor();\\n }\\n\\n function _setGovernor(address newGovernor) internal {\\n emit GovernorshipTransferred(_governor(), newGovernor);\\n\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @dev Prevents a contract from calling itself, directly or indirectly.\\n * Calling a `nonReentrant` function from another `nonReentrant`\\n * function is not supported. It is possible to prevent this from happening\\n * by making the `nonReentrant` function external, and make it call a\\n * `private` function that does the actual work.\\n */\\n modifier nonReentrant() {\\n bytes32 position = reentryStatusPosition;\\n uint256 _reentry_status;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n _reentry_status := sload(position)\\n }\\n\\n // On the first call to nonReentrant, _notEntered will be true\\n require(_reentry_status != _ENTERED, \\\"Reentrant call\\\");\\n\\n // Any calls to nonReentrant after this point will fail\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _ENTERED)\\n }\\n\\n _;\\n\\n // By storing the original value once again, a refund is triggered (see\\n // https://eips.ethereum.org/EIPS/eip-2200)\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _NOT_ENTERED)\\n }\\n }\\n\\n function _setPendingGovernor(address newGovernor) internal {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the current Governor. Must be claimed for this to complete\\n * @param _newGovernor Address of the new Governor\\n */\\n function transferGovernance(address _newGovernor) external onlyGovernor {\\n _setPendingGovernor(_newGovernor);\\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\\n }\\n\\n /**\\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the new Governor.\\n */\\n function claimGovernance() external {\\n require(\\n msg.sender == _pendingGovernor(),\\n \\\"Only the pending Governor can complete the claim\\\"\\n );\\n _changeGovernor(msg.sender);\\n }\\n\\n /**\\n * @dev Change Governance of the contract to a new account (`newGovernor`).\\n * @param _newGovernor Address of the new Governor\\n */\\n function _changeGovernor(address _newGovernor) internal {\\n require(_newGovernor != address(0), \\\"New Governor is address(0)\\\");\\n _setGovernor(_newGovernor);\\n }\\n}\\n\",\"keccak256\":\"0xf32f873c8bfbacf2e5f01d0cf37bc7f54fbd5aa656e95c8a599114229946f107\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IBeaconProofs.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\ninterface IBeaconProofs {\\n function verifyValidator(\\n bytes32 beaconBlockRoot,\\n bytes32 pubKeyHash,\\n bytes calldata validatorPubKeyProof,\\n uint40 validatorIndex,\\n address withdrawalAddress\\n ) external view;\\n\\n function verifyValidatorWithdrawable(\\n bytes32 beaconBlockRoot,\\n uint40 validatorIndex,\\n uint64 withdrawableEpoch,\\n bytes calldata withdrawableEpochProof\\n ) external view;\\n\\n function verifyBalancesContainer(\\n bytes32 beaconBlockRoot,\\n bytes32 balancesContainerLeaf,\\n bytes calldata balancesContainerProof\\n ) external view;\\n\\n function verifyValidatorBalance(\\n bytes32 balancesContainerRoot,\\n bytes32 validatorBalanceLeaf,\\n bytes calldata balanceProof,\\n uint40 validatorIndex\\n ) external view returns (uint256 validatorBalance);\\n\\n function verifyPendingDepositsContainer(\\n bytes32 beaconBlockRoot,\\n bytes32 pendingDepositsContainerRoot,\\n bytes calldata proof\\n ) external view;\\n\\n function verifyPendingDeposit(\\n bytes32 pendingDepositsContainerRoot,\\n bytes32 pendingDepositRoot,\\n bytes calldata proof,\\n uint64 depositIndex\\n ) external view;\\n\\n function verifyFirstPendingDeposit(\\n bytes32 beaconBlockRoot,\\n uint64 slot,\\n bytes calldata firstPendingDepositSlotProof\\n ) external view returns (bool isEmptyDepositQueue);\\n\\n function merkleizePendingDeposit(\\n bytes32 pubKeyHash,\\n bytes calldata withdrawalCredentials,\\n uint64 amountGwei,\\n bytes calldata signature,\\n uint64 slot\\n ) external pure returns (bytes32 root);\\n\\n function merkleizeSignature(bytes calldata signature)\\n external\\n pure\\n returns (bytes32 root);\\n}\\n\",\"keccak256\":\"0x136d7d0a1a8a06eb9e04244ef2b503c0716610a499b5b5c37031f75e803629a3\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IDepositContract.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\ninterface IDepositContract {\\n /// @notice A processed deposit event.\\n event DepositEvent(\\n bytes pubkey,\\n bytes withdrawal_credentials,\\n bytes amount,\\n bytes signature,\\n bytes index\\n );\\n\\n /// @notice Submit a Phase 0 DepositData object.\\n /// @param pubkey A BLS12-381 public key.\\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\\n /// @param signature A BLS12-381 signature.\\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\\n /// Used as a protection against malformed input.\\n function deposit(\\n bytes calldata pubkey,\\n bytes calldata withdrawal_credentials,\\n bytes calldata signature,\\n bytes32 deposit_data_root\\n ) external payable;\\n\\n /// @notice Query the current deposit root hash.\\n /// @return The deposit root hash.\\n function get_deposit_root() external view returns (bytes32);\\n\\n /// @notice Query the current deposit count.\\n /// @return The deposit count encoded as a little endian 64-bit number.\\n function get_deposit_count() external view returns (bytes memory);\\n}\\n\",\"keccak256\":\"0x598f90bdbc854250bbd5991426bfb43367207e64e33109c41aa8b54323fd8d8e\",\"license\":\"MIT\"},\"contracts/interfaces/ISSVNetwork.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\nstruct Cluster {\\n uint32 validatorCount;\\n uint64 networkFeeIndex;\\n uint64 index;\\n bool active;\\n uint256 balance;\\n}\\n\\ninterface ISSVNetwork {\\n /**********/\\n /* Errors */\\n /**********/\\n\\n error CallerNotOwner(); // 0x5cd83192\\n error CallerNotWhitelisted(); // 0x8c6e5d71\\n error FeeTooLow(); // 0x732f9413\\n error FeeExceedsIncreaseLimit(); // 0x958065d9\\n error NoFeeDeclared(); // 0x1d226c30\\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\\n error OperatorDoesNotExist(); // 0x961e3e8c\\n error InsufficientBalance(); // 0xf4d678b8\\n error ValidatorDoesNotExist(); // 0xe51315d2\\n error ClusterNotLiquidatable(); // 0x60300a8d\\n error InvalidPublicKeyLength(); // 0x637297a4\\n error InvalidOperatorIdsLength(); // 0x38186224\\n error ClusterAlreadyEnabled(); // 0x3babafd2\\n error ClusterIsLiquidated(); // 0x95a0cf33\\n error ClusterDoesNotExists(); // 0x185e2b16\\n error IncorrectClusterState(); // 0x12e04c87\\n error UnsortedOperatorsList(); // 0xdd020e25\\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\\n error ExceedValidatorLimit(); // 0x6df5ab76\\n error TokenTransferFailed(); // 0x045c4b02\\n error SameFeeChangeNotAllowed(); // 0xc81272f8\\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\\n error NotAuthorized(); // 0xea8e4eb5\\n error OperatorsListNotUnique(); // 0xa5a1ff5d\\n error OperatorAlreadyExists(); // 0x289c9494\\n error TargetModuleDoesNotExist(); // 0x8f9195fb\\n error MaxValueExceeded(); // 0x91aa3017\\n error FeeTooHigh(); // 0xcd4e6167\\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\\n error EmptyPublicKeysList(); // df83e679\\n\\n // legacy errors\\n error ValidatorAlreadyExists(); // 0x8d09a73e\\n error IncorrectValidatorState(); // 0x2feda3c1\\n\\n event AdminChanged(address previousAdmin, address newAdmin);\\n event BeaconUpgraded(address indexed beacon);\\n event ClusterDeposited(\\n address indexed owner,\\n uint64[] operatorIds,\\n uint256 value,\\n Cluster cluster\\n );\\n event ClusterLiquidated(\\n address indexed owner,\\n uint64[] operatorIds,\\n Cluster cluster\\n );\\n event ClusterReactivated(\\n address indexed owner,\\n uint64[] operatorIds,\\n Cluster cluster\\n );\\n event ClusterWithdrawn(\\n address indexed owner,\\n uint64[] operatorIds,\\n uint256 value,\\n Cluster cluster\\n );\\n event DeclareOperatorFeePeriodUpdated(uint64 value);\\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\\n event FeeRecipientAddressUpdated(\\n address indexed owner,\\n address recipientAddress\\n );\\n event Initialized(uint8 version);\\n event LiquidationThresholdPeriodUpdated(uint64 value);\\n event MinimumLiquidationCollateralUpdated(uint256 value);\\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\\n event OperatorAdded(\\n uint64 indexed operatorId,\\n address indexed owner,\\n bytes publicKey,\\n uint256 fee\\n );\\n event OperatorFeeDeclarationCancelled(\\n address indexed owner,\\n uint64 indexed operatorId\\n );\\n event OperatorFeeDeclared(\\n address indexed owner,\\n uint64 indexed operatorId,\\n uint256 blockNumber,\\n uint256 fee\\n );\\n event OperatorFeeExecuted(\\n address indexed owner,\\n uint64 indexed operatorId,\\n uint256 blockNumber,\\n uint256 fee\\n );\\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\\n event OperatorMaximumFeeUpdated(uint64 maxFee);\\n event OperatorRemoved(uint64 indexed operatorId);\\n event OperatorWhitelistUpdated(\\n uint64 indexed operatorId,\\n address whitelisted\\n );\\n event OperatorWithdrawn(\\n address indexed owner,\\n uint64 indexed operatorId,\\n uint256 value\\n );\\n event OwnershipTransferStarted(\\n address indexed previousOwner,\\n address indexed newOwner\\n );\\n event OwnershipTransferred(\\n address indexed previousOwner,\\n address indexed newOwner\\n );\\n event Upgraded(address indexed implementation);\\n event ValidatorAdded(\\n address indexed owner,\\n uint64[] operatorIds,\\n bytes publicKey,\\n bytes shares,\\n Cluster cluster\\n );\\n event ValidatorExited(\\n address indexed owner,\\n uint64[] operatorIds,\\n bytes publicKey\\n );\\n event ValidatorRemoved(\\n address indexed owner,\\n uint64[] operatorIds,\\n bytes publicKey,\\n Cluster cluster\\n );\\n\\n fallback() external;\\n\\n function acceptOwnership() external;\\n\\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\\n\\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\\n\\n function deposit(\\n address clusterOwner,\\n uint64[] memory operatorIds,\\n uint256 amount,\\n Cluster memory cluster\\n ) external;\\n\\n function executeOperatorFee(uint64 operatorId) external;\\n\\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\\n external;\\n\\n function bulkExitValidator(\\n bytes[] calldata publicKeys,\\n uint64[] calldata operatorIds\\n ) external;\\n\\n function getVersion() external pure returns (string memory version);\\n\\n function initialize(\\n address token_,\\n address ssvOperators_,\\n address ssvClusters_,\\n address ssvDAO_,\\n address ssvViews_,\\n uint64 minimumBlocksBeforeLiquidation_,\\n uint256 minimumLiquidationCollateral_,\\n uint32 validatorsPerOperatorLimit_,\\n uint64 declareOperatorFeePeriod_,\\n uint64 executeOperatorFeePeriod_,\\n uint64 operatorMaxFeeIncrease_\\n ) external;\\n\\n function liquidate(\\n address clusterOwner,\\n uint64[] memory operatorIds,\\n Cluster memory cluster\\n ) external;\\n\\n function owner() external view returns (address);\\n\\n function pendingOwner() external view returns (address);\\n\\n function proxiableUUID() external view returns (bytes32);\\n\\n function reactivate(\\n uint64[] memory operatorIds,\\n uint256 amount,\\n Cluster memory cluster\\n ) external;\\n\\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\\n\\n function registerOperator(bytes memory publicKey, uint256 fee)\\n external\\n returns (uint64 id);\\n\\n function registerValidator(\\n bytes memory publicKey,\\n uint64[] memory operatorIds,\\n bytes memory sharesData,\\n uint256 amount,\\n Cluster memory cluster\\n ) external;\\n\\n function bulkRegisterValidator(\\n bytes[] calldata publicKeys,\\n uint64[] calldata operatorIds,\\n bytes[] calldata sharesData,\\n uint256 amount,\\n Cluster memory cluster\\n ) external;\\n\\n function removeOperator(uint64 operatorId) external;\\n\\n function removeValidator(\\n bytes memory publicKey,\\n uint64[] memory operatorIds,\\n Cluster memory cluster\\n ) external;\\n\\n function bulkRemoveValidator(\\n bytes[] calldata publicKeys,\\n uint64[] calldata operatorIds,\\n Cluster memory cluster\\n ) external;\\n\\n function renounceOwnership() external;\\n\\n function setFeeRecipientAddress(address recipientAddress) external;\\n\\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\\n external;\\n\\n function transferOwnership(address newOwner) external;\\n\\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\\n\\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\\n\\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\\n\\n function updateMaximumOperatorFee(uint64 maxFee) external;\\n\\n function updateMinimumLiquidationCollateral(uint256 amount) external;\\n\\n function updateModule(uint8 moduleId, address moduleAddress) external;\\n\\n function updateNetworkFee(uint256 fee) external;\\n\\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\\n\\n function upgradeTo(address newImplementation) external;\\n\\n function upgradeToAndCall(address newImplementation, bytes memory data)\\n external\\n payable;\\n\\n function withdraw(\\n uint64[] memory operatorIds,\\n uint256 amount,\\n Cluster memory cluster\\n ) external;\\n\\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\\n\\n function withdrawNetworkEarnings(uint256 amount) external;\\n\\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\\n external;\\n}\\n\",\"keccak256\":\"0xbd86cb74702aebc5b53c8fc738a2e3ad1b410583460617be84b22ce922af12a7\",\"license\":\"MIT\"},\"contracts/interfaces/IWETH9.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\ninterface IWETH9 {\\n event Approval(address indexed src, address indexed guy, uint256 wad);\\n event Deposit(address indexed dst, uint256 wad);\\n event Transfer(address indexed src, address indexed dst, uint256 wad);\\n event Withdrawal(address indexed src, uint256 wad);\\n\\n function allowance(address, address) external view returns (uint256);\\n\\n function approve(address guy, uint256 wad) external returns (bool);\\n\\n function balanceOf(address) external view returns (uint256);\\n\\n function decimals() external view returns (uint8);\\n\\n function deposit() external payable;\\n\\n function name() external view returns (string memory);\\n\\n function symbol() external view returns (string memory);\\n\\n function totalSupply() external view returns (uint256);\\n\\n function transfer(address dst, uint256 wad) external returns (bool);\\n\\n function transferFrom(\\n address src,\\n address dst,\\n uint256 wad\\n ) external returns (bool);\\n\\n function withdraw(uint256 wad) external;\\n}\\n\",\"keccak256\":\"0x05b7dce6c24d3cd4e48b5c6346d86e5e40ecc3291bcdf3f3ef091c98fc826519\",\"license\":\"MIT\"},\"contracts/strategies/NativeStaking/CompoundingStakingView.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { CompoundingValidatorManager } from \\\"./CompoundingValidatorManager.sol\\\";\\n\\n/**\\n * @title Viewing contract for the Compounding Staking Strategy.\\n * @author Origin Protocol Inc\\n */\\ncontract CompoundingStakingStrategyView {\\n /// @notice The address of the Compounding Staking Strategy contract\\n CompoundingValidatorManager public immutable stakingStrategy;\\n\\n constructor(address _stakingStrategy) {\\n stakingStrategy = CompoundingValidatorManager(_stakingStrategy);\\n }\\n\\n struct ValidatorView {\\n bytes32 pubKeyHash;\\n uint64 index;\\n CompoundingValidatorManager.ValidatorState state;\\n }\\n\\n struct DepositView {\\n bytes32 pendingDepositRoot;\\n bytes32 pubKeyHash;\\n uint64 amountGwei;\\n uint64 slot;\\n }\\n\\n /// @notice Returns the strategy's active validators.\\n /// These are the ones that have been verified and have a non-zero balance.\\n /// @return validators An array of `ValidatorView` containing the public key hash, validator index and state.\\n function getVerifiedValidators()\\n external\\n view\\n returns (ValidatorView[] memory validators)\\n {\\n uint256 validatorCount = stakingStrategy.verifiedValidatorsLength();\\n validators = new ValidatorView[](validatorCount);\\n for (uint256 i = 0; i < validatorCount; ++i) {\\n bytes32 pubKeyHash = stakingStrategy.verifiedValidators(i);\\n (\\n CompoundingValidatorManager.ValidatorState state,\\n uint64 index\\n ) = stakingStrategy.validator(pubKeyHash);\\n validators[i] = ValidatorView({\\n pubKeyHash: pubKeyHash,\\n index: index,\\n state: state\\n });\\n }\\n }\\n\\n /// @notice Returns the deposits that are still to be verified.\\n /// These may or may not have been processed by the beacon chain.\\n /// @return pendingDeposits An array of `DepositView` containing the deposit ID, public key hash,\\n /// amount in Gwei and the slot of the deposit.\\n function getPendingDeposits()\\n external\\n view\\n returns (DepositView[] memory pendingDeposits)\\n {\\n uint256 depositsCount = stakingStrategy.depositListLength();\\n pendingDeposits = new DepositView[](depositsCount);\\n for (uint256 i = 0; i < depositsCount; ++i) {\\n (\\n bytes32 pubKeyHash,\\n uint64 amountGwei,\\n uint64 slot,\\n ,\\n\\n ) = stakingStrategy.deposits(stakingStrategy.depositList(i));\\n pendingDeposits[i] = DepositView({\\n pendingDepositRoot: stakingStrategy.depositList(i),\\n pubKeyHash: pubKeyHash,\\n amountGwei: amountGwei,\\n slot: slot\\n });\\n }\\n }\\n}\\n\",\"keccak256\":\"0x34d802a22f83c82e40a166d6589e8a471137971d2c5a5b302e579d245beac2c2\",\"license\":\"BUSL-1.1\"},\"contracts/strategies/NativeStaking/CompoundingValidatorManager.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { SafeERC20 } from \\\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\\\";\\nimport { SafeCast } from \\\"@openzeppelin/contracts/utils/math/SafeCast.sol\\\";\\nimport { Math } from \\\"@openzeppelin/contracts/utils/math/Math.sol\\\";\\nimport { IERC20 } from \\\"@openzeppelin/contracts/token/ERC20/IERC20.sol\\\";\\nimport { Pausable } from \\\"@openzeppelin/contracts/security/Pausable.sol\\\";\\nimport { Governable } from \\\"../../governance/Governable.sol\\\";\\nimport { IDepositContract } from \\\"../../interfaces/IDepositContract.sol\\\";\\nimport { IWETH9 } from \\\"../../interfaces/IWETH9.sol\\\";\\nimport { ISSVNetwork, Cluster } from \\\"../../interfaces/ISSVNetwork.sol\\\";\\nimport { BeaconRoots } from \\\"../../beacon/BeaconRoots.sol\\\";\\nimport { PartialWithdrawal } from \\\"../../beacon/PartialWithdrawal.sol\\\";\\nimport { IBeaconProofs } from \\\"../../interfaces/IBeaconProofs.sol\\\";\\n\\n/**\\n * @title Validator lifecycle management contract\\n * @notice This contract implements all the required functionality to\\n * register, deposit, withdraw, exit and remove validators.\\n * @author Origin Protocol Inc\\n */\\nabstract contract CompoundingValidatorManager is Governable, Pausable {\\n using SafeERC20 for IERC20;\\n\\n /// @dev The amount of ETH in wei that is required for a deposit to a new validator.\\n uint256 internal constant DEPOSIT_AMOUNT_WEI = 1 ether;\\n /// @dev The amount of ETH balance in validator required for validator activation\\n uint256 internal constant MIN_ACTIVATION_BALANCE_GWEI = 32 ether / 1e9;\\n /// @dev The maximum number of deposits that are waiting to be verified as processed on the beacon chain.\\n uint256 internal constant MAX_DEPOSITS = 12;\\n /// @dev The maximum number of validators that can be verified.\\n uint256 internal constant MAX_VERIFIED_VALIDATORS = 48;\\n /// @dev The default withdrawable epoch value on the Beacon chain.\\n /// A value in the far future means the validator is not exiting.\\n uint64 internal constant FAR_FUTURE_EPOCH = type(uint64).max;\\n /// @dev The number of seconds between each beacon chain slot.\\n uint64 internal constant SLOT_DURATION = 12;\\n /// @dev The number of slots in each beacon chain epoch.\\n uint64 internal constant SLOTS_PER_EPOCH = 32;\\n /// @dev Minimum time in seconds to allow snapped balances to be verified.\\n /// Set to 35 slots which is 3 slots more than 1 epoch (32 slots). Deposits get processed\\n /// once per epoch. This larger than 1 epoch delay should achieve that `snapBalances` sometimes\\n /// get called in the middle (or towards the end) of the epoch. Giving the off-chain script\\n /// sufficient time after the end of the epoch to prepare the proofs and call `verifyBalances`.\\n /// This is considering a malicious actor would keep calling `snapBalances` as frequent as possible\\n /// to disturb our operations.\\n uint64 internal constant SNAP_BALANCES_DELAY = 35 * SLOT_DURATION;\\n\\n /// @notice The address of the Wrapped ETH (WETH) token contract\\n address internal immutable WETH;\\n /// @notice The address of the beacon chain deposit contract\\n address internal immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\\n /// @notice The address of the SSV Network contract used to interface with\\n address public immutable SSV_NETWORK;\\n /// @notice Address of the OETH Vault proxy contract\\n address internal immutable VAULT_ADDRESS;\\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\\n address public immutable BEACON_PROOFS;\\n /// @notice The timestamp of the Beacon chain genesis.\\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\\n uint64 internal immutable BEACON_GENESIS_TIMESTAMP;\\n\\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\\n address public validatorRegistrator;\\n\\n /// @notice Deposit data for new compounding validators.\\n /// @dev A `VERIFIED` deposit can mean 3 separate things:\\n /// - a deposit has been processed by the beacon chain and shall be included in the\\n /// balance of the next verifyBalances call\\n /// - a deposit has been done to a slashed validator and has probably been recovered\\n /// back to this strategy. Probably because we can not know for certain. This contract\\n /// only detects when the validator has passed its withdrawal epoch. It is close to impossible\\n /// to prove with Merkle Proofs that the postponed deposit this contract is responsible for\\n /// creating is not present anymore in BeaconChain.state.pending_deposits. This in effect\\n /// means that there might be a period where this contract thinks the deposit has been already\\n /// returned as ETH balance before it happens. This will result in some days (or weeks)\\n /// -> depending on the size of deposit queue of showing a deficit when calling `checkBalance`.\\n /// As this only offsets the yield and doesn't cause a critical double-counting we are not addressing\\n /// this issue.\\n /// - A deposit has been done to the validator, but our deposit has been front run by a malicious\\n /// actor. Funds in the deposit this contract makes are not recoverable.\\n enum DepositStatus {\\n UNKNOWN, // default value\\n PENDING, // deposit is pending and waiting to be verified\\n VERIFIED // deposit has been verified\\n }\\n\\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\\n /// @param amountGwei Amount of ETH in gwei that has been deposited to the beacon chain deposit contract\\n /// @param slot The beacon chain slot number when the deposit has been made\\n /// @param depositIndex The index of the deposit in the list of active deposits\\n /// @param status The status of the deposit, either UNKNOWN, PENDING or VERIFIED\\n struct DepositData {\\n bytes32 pubKeyHash;\\n uint64 amountGwei;\\n uint64 slot;\\n uint32 depositIndex;\\n DepositStatus status;\\n }\\n /// @notice Restricts to only one deposit to an unverified validator at a time.\\n /// This is to limit front-running attacks of deposits to the beacon chain contract.\\n ///\\n /// @dev The value is set to true when a deposit to a new validator has been done that has\\n /// not yet be verified.\\n bool public firstDeposit;\\n /// @notice Mapping of the pending deposit roots to the deposit data\\n mapping(bytes32 => DepositData) public deposits;\\n /// @notice List of strategy deposit IDs to a validator.\\n /// The ID is the merkle root of the pending deposit data which is unique for each validator, amount and block.\\n /// Duplicate pending deposit roots are prevented so can be used as an identifier to each strategy deposit.\\n /// The list can be for deposits waiting to be verified as processed on the beacon chain,\\n /// or deposits that have been verified to an exiting validator and is now waiting for the\\n /// validator's balance to be swept.\\n /// The list may not be ordered by time of deposit.\\n /// Removed deposits will move the last deposit to the removed index.\\n bytes32[] public depositList;\\n\\n enum ValidatorState {\\n NON_REGISTERED, // validator is not registered on the SSV network\\n REGISTERED, // validator is registered on the SSV network\\n STAKED, // validator has funds staked\\n VERIFIED, // validator has been verified to exist on the beacon chain\\n ACTIVE, // The validator balance is at least 32 ETH\\n EXITING, // The validator has been requested to exit or has been verified as forced exit\\n EXITED, // The validator has been verified to have a zero balance\\n REMOVED, // validator has funds withdrawn to this strategy contract and is removed from the SSV\\n INVALID // The validator has been front-run and the withdrawal address is not this strategy\\n }\\n\\n // Validator data\\n struct ValidatorData {\\n ValidatorState state; // The state of the validator known to this contract\\n uint40 index; // The index of the validator on the beacon chain\\n }\\n /// @notice List of validator public key hashes that have been verified to exist on the beacon chain.\\n /// These have had a deposit processed and the validator's balance increased.\\n /// Validators will be removed from this list when its verified they have a zero balance.\\n bytes32[] public verifiedValidators;\\n /// @notice Mapping of the hash of the validator's public key to the validator state and index.\\n /// Uses the Beacon chain hashing for BLSPubkey which is sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\\n mapping(bytes32 => ValidatorData) public validator;\\n\\n /// @param blockRoot Beacon chain block root of the snapshot\\n /// @param timestamp Timestamp of the snapshot\\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\\n struct Balances {\\n bytes32 blockRoot;\\n uint64 timestamp;\\n uint128 ethBalance;\\n }\\n /// @notice Mapping of the block root to the balances at that slot\\n Balances public snappedBalance;\\n /// @notice The last verified ETH balance of the strategy\\n uint256 public lastVerifiedEthBalance;\\n\\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\\n /// of WETH that has already been accounted for.\\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\\n /// deposit events.\\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\\n /// be staked.\\n uint256 public depositedWethAccountedFor;\\n\\n // For future use\\n uint256[41] private __gap;\\n\\n event RegistratorChanged(address indexed newAddress);\\n event StakingMonitorChanged(address indexed newAddress);\\n event FirstDepositReset();\\n event SSVValidatorRegistered(\\n bytes32 indexed pubKeyHash,\\n uint64[] operatorIds\\n );\\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\\n event ETHStaked(\\n bytes32 indexed pubKeyHash,\\n bytes32 indexed pendingDepositRoot,\\n bytes pubKey,\\n uint256 amountWei\\n );\\n event ValidatorVerified(\\n bytes32 indexed pubKeyHash,\\n uint40 indexed validatorIndex\\n );\\n event ValidatorInvalid(bytes32 indexed pubKeyHash);\\n event DepositVerified(\\n bytes32 indexed pendingDepositRoot,\\n uint256 amountWei\\n );\\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\\n event BalancesSnapped(bytes32 indexed blockRoot, uint256 ethBalance);\\n event BalancesVerified(\\n uint64 indexed timestamp,\\n uint256 totalDepositsWei,\\n uint256 totalValidatorBalance,\\n uint256 ethBalance\\n );\\n\\n /// @dev Throws if called by any account other than the Registrator\\n modifier onlyRegistrator() {\\n require(msg.sender == validatorRegistrator, \\\"Not Registrator\\\");\\n _;\\n }\\n\\n /// @dev Throws if called by any account other than the Registrator or Governor\\n modifier onlyRegistratorOrGovernor() {\\n require(\\n msg.sender == validatorRegistrator || isGovernor(),\\n \\\"Not Registrator or Governor\\\"\\n );\\n _;\\n }\\n\\n /// @param _wethAddress Address of the Erc20 WETH Token contract\\n /// @param _vaultAddress Address of the Vault\\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\\n /// @param _ssvNetwork Address of the SSV Network contract\\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\\n constructor(\\n address _wethAddress,\\n address _vaultAddress,\\n address _beaconChainDepositContract,\\n address _ssvNetwork,\\n address _beaconProofs,\\n uint64 _beaconGenesisTimestamp\\n ) {\\n WETH = _wethAddress;\\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\\n SSV_NETWORK = _ssvNetwork;\\n VAULT_ADDRESS = _vaultAddress;\\n BEACON_PROOFS = _beaconProofs;\\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\\n\\n require(\\n block.timestamp > _beaconGenesisTimestamp,\\n \\\"Invalid genesis timestamp\\\"\\n );\\n }\\n\\n /**\\n *\\n * Admin Functions\\n *\\n */\\n\\n /// @notice Set the address of the registrator which can register, exit and remove validators\\n function setRegistrator(address _address) external onlyGovernor {\\n validatorRegistrator = _address;\\n emit RegistratorChanged(_address);\\n }\\n\\n /// @notice Reset the `firstDeposit` flag to false so deposits to unverified validators can be made again.\\n function resetFirstDeposit() external onlyGovernor {\\n require(firstDeposit, \\\"No first deposit\\\");\\n\\n firstDeposit = false;\\n\\n emit FirstDepositReset();\\n }\\n\\n function pause() external onlyRegistratorOrGovernor {\\n _pause();\\n }\\n\\n function unPause() external onlyGovernor {\\n _unpause();\\n }\\n\\n /**\\n *\\n * Validator Management\\n *\\n */\\n\\n /// @notice Registers a single validator in a SSV Cluster.\\n /// Only the Registrator can call this function.\\n /// @param publicKey The public key of the validator\\n /// @param operatorIds The operator IDs of the SSV Cluster\\n /// @param sharesData The shares data for the validator\\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\\n /// @param cluster The SSV cluster details including the validator count and SSV balance\\n // slither-disable-start reentrancy-no-eth\\n function registerSsvValidator(\\n bytes calldata publicKey,\\n uint64[] calldata operatorIds,\\n bytes calldata sharesData,\\n uint256 ssvAmount,\\n Cluster calldata cluster\\n ) external onlyRegistrator whenNotPaused {\\n // Hash the public key using the Beacon Chain's format\\n bytes32 pubKeyHash = _hashPubKey(publicKey);\\n // Check each public key has not already been used\\n require(\\n validator[pubKeyHash].state == ValidatorState.NON_REGISTERED,\\n \\\"Validator already registered\\\"\\n );\\n\\n // Store the validator state as registered\\n validator[pubKeyHash].state = ValidatorState.REGISTERED;\\n\\n ISSVNetwork(SSV_NETWORK).registerValidator(\\n publicKey,\\n operatorIds,\\n sharesData,\\n ssvAmount,\\n cluster\\n );\\n\\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\\n }\\n\\n // slither-disable-end reentrancy-no-eth\\n\\n struct ValidatorStakeData {\\n bytes pubkey;\\n bytes signature;\\n bytes32 depositDataRoot;\\n }\\n\\n /// @notice Stakes WETH in this strategy to a compounding validator.\\n /// The the first deposit to a new validator, the amount must be 1 ETH.\\n /// Another deposit of at least 31 ETH is required for the validator to be activated.\\n /// This second deposit has to be done after the validator has been verified.\\n /// Does not convert any ETH sitting in this strategy to WETH.\\n /// There can not be two deposits to the same validator in the same block for the same amount.\\n /// Function is pausable so in case a run-away Registrator can be prevented from continuing\\n /// to deposit funds to slashed or undesired validators.\\n /// @param validatorStakeData validator data needed to stake.\\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\\n /// Only the registrator can call this function.\\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\\n // slither-disable-start reentrancy-eth,reentrancy-no-eth\\n function stakeEth(\\n ValidatorStakeData calldata validatorStakeData,\\n uint64 depositAmountGwei\\n ) external onlyRegistrator whenNotPaused {\\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\\n // Check there is enough WETH from the deposits sitting in this strategy contract\\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\\n // the ETH can be withdrawn and then deposited back to the strategy.\\n require(\\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\\n \\\"Insufficient WETH\\\"\\n );\\n require(depositList.length < MAX_DEPOSITS, \\\"Max deposits\\\");\\n\\n // Convert required ETH from WETH and do the necessary accounting\\n _convertWethToEth(depositAmountWei);\\n\\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\\n bytes32 pubKeyHash = _hashPubKey(validatorStakeData.pubkey);\\n ValidatorState currentState = validator[pubKeyHash].state;\\n // Can only stake to a validator has have been registered or verified.\\n // Can not stake to a validator that has been staked but not yet verified.\\n require(\\n (currentState == ValidatorState.REGISTERED ||\\n currentState == ValidatorState.VERIFIED ||\\n currentState == ValidatorState.ACTIVE),\\n \\\"Not registered or verified\\\"\\n );\\n require(depositAmountWei >= 1 ether, \\\"Deposit too small\\\");\\n if (currentState == ValidatorState.REGISTERED) {\\n // Can only have one pending deposit to an unverified validator at a time.\\n // This is to limit front-running deposit attacks to a single deposit.\\n // The exiting deposit needs to be verified before another deposit can be made.\\n // If there was a front-running attack, the validator needs to be verified as invalid\\n // and the Governor calls `resetFirstDeposit` to set `firstDeposit` to false.\\n require(!firstDeposit, \\\"Existing first deposit\\\");\\n // Limits the amount of ETH that can be at risk from a front-running deposit attack.\\n require(\\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\\n \\\"Invalid first deposit amount\\\"\\n );\\n // Limits the number of validator balance proofs to verifyBalances\\n require(\\n verifiedValidators.length + 1 < MAX_VERIFIED_VALIDATORS,\\n \\\"Max validators\\\"\\n );\\n\\n // Flag a deposit to an unverified validator so only no other deposits can be made\\n // to an unverified validator.\\n firstDeposit = true;\\n validator[pubKeyHash].state = ValidatorState.STAKED;\\n }\\n\\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\\n * that was introduced with the Pectra upgrade.\\n * bytes11(0) to fill up the required zeros\\n * remaining bytes20 are for the address\\n */\\n bytes memory withdrawalCredentials = abi.encodePacked(\\n bytes1(0x02),\\n bytes11(0),\\n address(this)\\n );\\n\\n /// After the Pectra upgrade the validators have a new restriction when proposing\\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\\n /// forward. Each slot is created at strict 12 second intervals and those slots can\\n /// either have blocks attached to them or not. This way using the block.timestamp\\n /// the slot number can easily be calculated.\\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\\n BEACON_GENESIS_TIMESTAMP) / SLOT_DURATION;\\n\\n // Calculate the merkle root of the beacon chain pending deposit data.\\n // This is used as the unique ID of the deposit.\\n bytes32 pendingDepositRoot = IBeaconProofs(BEACON_PROOFS)\\n .merkleizePendingDeposit(\\n pubKeyHash,\\n withdrawalCredentials,\\n depositAmountGwei,\\n validatorStakeData.signature,\\n depositSlot\\n );\\n require(\\n deposits[pendingDepositRoot].status == DepositStatus.UNKNOWN,\\n \\\"Duplicate deposit\\\"\\n );\\n\\n // Store the deposit data for verifyDeposit and verifyBalances\\n deposits[pendingDepositRoot] = DepositData({\\n pubKeyHash: pubKeyHash,\\n amountGwei: depositAmountGwei,\\n slot: depositSlot,\\n depositIndex: SafeCast.toUint32(depositList.length),\\n status: DepositStatus.PENDING\\n });\\n depositList.push(pendingDepositRoot);\\n\\n // Deposit to the Beacon Chain deposit contract.\\n // This will create a deposit in the beacon chain's pending deposit queue.\\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\\n value: depositAmountWei\\n }(\\n validatorStakeData.pubkey,\\n withdrawalCredentials,\\n validatorStakeData.signature,\\n validatorStakeData.depositDataRoot\\n );\\n\\n emit ETHStaked(\\n pubKeyHash,\\n pendingDepositRoot,\\n validatorStakeData.pubkey,\\n depositAmountWei\\n );\\n }\\n\\n // slither-disable-end reentrancy-eth,reentrancy-no-eth\\n\\n /// @notice Request a full or partial withdrawal from a validator.\\n /// A zero amount will trigger a full withdrawal.\\n /// If the remaining balance is < 32 ETH then only the amount in excess of 32 ETH will be withdrawn.\\n /// Only the Registrator can call this function.\\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\\n /// If no ETH balance, the tx will revert.\\n /// @param publicKey The public key of the validator\\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\\n /// A zero amount will trigger a full withdrawal.\\n // slither-disable-start reentrancy-no-eth\\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\\n external\\n payable\\n onlyRegistrator\\n {\\n // Hash the public key using the Beacon Chain's format\\n bytes32 pubKeyHash = _hashPubKey(publicKey);\\n ValidatorData memory validatorDataMem = validator[pubKeyHash];\\n // Validator full withdrawal could be denied due to multiple reasons:\\n // - the validator has not been activated or active long enough\\n // (current_epoch < activation_epoch + SHARD_COMMITTEE_PERIOD)\\n // - the validator has pending balance to withdraw from a previous partial withdrawal request\\n //\\n // Meaning that the on-chain to beacon chain full withdrawal request could fail. Instead\\n // of adding complexity of verifying if a validator is eligible for a full exit, we allow\\n // multiple full withdrawal requests per validator.\\n require(\\n validatorDataMem.state == ValidatorState.ACTIVE ||\\n validatorDataMem.state == ValidatorState.EXITING,\\n \\\"Validator not active/exiting\\\"\\n );\\n\\n // If a full withdrawal (validator exit)\\n if (amountGwei == 0) {\\n // For each staking strategy's deposits\\n uint256 depositsCount = depositList.length;\\n for (uint256 i = 0; i < depositsCount; ++i) {\\n bytes32 pendingDepositRoot = depositList[i];\\n // Check there is no pending deposits to the exiting validator\\n require(\\n pubKeyHash != deposits[pendingDepositRoot].pubKeyHash,\\n \\\"Pending deposit\\\"\\n );\\n }\\n\\n // A validator that never had the minimal activation balance can not activate\\n // and thus can not perform a full exit\\n require(\\n validatorDataMem.state == ValidatorState.ACTIVE,\\n \\\"Validator not active\\\"\\n );\\n\\n // Store the validator state as exiting so no more deposits can be made to it.\\n validator[pubKeyHash].state = ValidatorState.EXITING;\\n }\\n\\n // Do not remove from the list of verified validators.\\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\\n // The validator state will be set to EXITED in the verifyBalances function.\\n\\n PartialWithdrawal.request(publicKey, amountGwei);\\n\\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\\n }\\n\\n // slither-disable-end reentrancy-no-eth\\n\\n /// @notice Remove the validator from the SSV Cluster after:\\n /// - the validator has been exited from `validatorWithdrawal` or slashed\\n /// - the validator has incorrectly registered and can not be staked to\\n /// - the initial deposit was front-run and the withdrawal address is not this strategy's address.\\n /// Make sure `validatorWithdrawal` is called with a zero amount and the validator has exited the Beacon chain.\\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\\n /// Only the registrator can call this function.\\n /// @param publicKey The public key of the validator\\n /// @param operatorIds The operator IDs of the SSV Cluster\\n /// @param cluster The SSV cluster details including the validator count and SSV balance\\n // slither-disable-start reentrancy-no-eth\\n function removeSsvValidator(\\n bytes calldata publicKey,\\n uint64[] calldata operatorIds,\\n Cluster calldata cluster\\n ) external onlyRegistrator {\\n // Hash the public key using the Beacon Chain's format\\n bytes32 pubKeyHash = _hashPubKey(publicKey);\\n ValidatorState currentState = validator[pubKeyHash].state;\\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\\n require(\\n currentState == ValidatorState.REGISTERED ||\\n currentState == ValidatorState.EXITED ||\\n currentState == ValidatorState.INVALID,\\n \\\"Validator not regd or exited\\\"\\n );\\n\\n validator[pubKeyHash].state = ValidatorState.REMOVED;\\n\\n ISSVNetwork(SSV_NETWORK).removeValidator(\\n publicKey,\\n operatorIds,\\n cluster\\n );\\n\\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\\n }\\n\\n /**\\n *\\n * SSV Management\\n *\\n */\\n\\n // slither-disable-end reentrancy-no-eth\\n\\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\\n /// by the Strategist which is already holding SSV tokens.\\n\\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\\n /// @param operatorIds The operator IDs of the SSV Cluster\\n /// @param ssvAmount The amount of SSV tokens to be withdrawn from the SSV cluster\\n /// @param cluster The SSV cluster details including the validator count and SSV balance\\n function withdrawSSV(\\n uint64[] memory operatorIds,\\n uint256 ssvAmount,\\n Cluster memory cluster\\n ) external onlyGovernor {\\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\\n }\\n\\n /**\\n *\\n * Beacon Chain Proofs\\n *\\n */\\n\\n /// @notice Verifies a validator's index to its public key.\\n /// Adds to the list of verified validators if the validator's withdrawal address is this strategy's address.\\n /// Marks the validator as invalid and removes the deposit if the withdrawal address is not this strategy's address.\\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\\n /// we are verifying.\\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\\n /// which is the beacon block root of the previous block.\\n /// @param validatorIndex The index of the validator on the beacon chain.\\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\\n /// @param withdrawalAddress The withdrawal address of the validator which should be this strategy's address.\\n /// If the withdrawal address is not this strategy's address, the initial deposit was front-run\\n /// and the validator is marked as invalid.\\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n /// BeaconBlock.state.validators[validatorIndex].pubkey\\n function verifyValidator(\\n uint64 nextBlockTimestamp,\\n uint40 validatorIndex,\\n bytes32 pubKeyHash,\\n address withdrawalAddress,\\n bytes calldata validatorPubKeyProof\\n ) external {\\n require(\\n validator[pubKeyHash].state == ValidatorState.STAKED,\\n \\\"Validator not staked\\\"\\n );\\n\\n // Get the beacon block root of the slot we are verifying the validator in.\\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\\n\\n // Verify the validator index is for the validator with the given public key.\\n // Also verify the validator's withdrawal credential points to the `withdrawalAddress`.\\n IBeaconProofs(BEACON_PROOFS).verifyValidator(\\n blockRoot,\\n pubKeyHash,\\n validatorPubKeyProof,\\n validatorIndex,\\n withdrawalAddress\\n );\\n\\n // Store the validator state as verified\\n validator[pubKeyHash] = ValidatorData({\\n state: ValidatorState.VERIFIED,\\n index: validatorIndex\\n });\\n\\n // If the initial deposit was front-run and the withdrawal address is not this strategy\\n if (withdrawalAddress != address(this)) {\\n // override the validator state\\n validator[pubKeyHash].state = ValidatorState.INVALID;\\n\\n // Find and remove the deposit as the funds can not be recovered\\n uint256 depositCount = depositList.length;\\n for (uint256 i = 0; i < depositCount; i++) {\\n DepositData memory deposit = deposits[depositList[i]];\\n if (deposit.pubKeyHash == pubKeyHash) {\\n // next verifyBalances will correctly account for the loss of a front-run\\n // deposit. Doing it here accounts for the loss as soon as possible\\n lastVerifiedEthBalance -= Math.min(\\n lastVerifiedEthBalance,\\n uint256(deposit.amountGwei) * 1 gwei\\n );\\n _removeDeposit(depositList[i], deposit);\\n break;\\n }\\n }\\n\\n // Leave the `firstDeposit` flag as true so no more deposits to unverified validators can be made.\\n // The Governor has to reset the `firstDeposit` to false before another deposit to\\n // an unverified validator can be made.\\n // The Governor can set a new `validatorRegistrator` if they suspect it has been compromised.\\n\\n emit ValidatorInvalid(pubKeyHash);\\n return;\\n }\\n\\n // Add the new validator to the list of verified validators\\n verifiedValidators.push(pubKeyHash);\\n\\n // Reset the firstDeposit flag as the first deposit to an unverified validator has been verified.\\n firstDeposit = false;\\n\\n emit ValidatorVerified(pubKeyHash, validatorIndex);\\n }\\n\\n struct FirstPendingDepositSlotProofData {\\n uint64 slot;\\n bytes proof;\\n }\\n\\n struct StrategyValidatorProofData {\\n uint64 withdrawableEpoch;\\n bytes withdrawableEpochProof;\\n }\\n\\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\\n ///\\n /// Important: this function has a limitation where `depositProcessedSlot` that is passed by the off-chain\\n /// verifier requires a slot immediately after it to propose a block otherwise the `BeaconRoots.parentBlockRoot`\\n /// will fail. This shouldn't be a problem, since by the current behaviour of beacon chain only 1%-3% slots\\n /// don't propose a block.\\n /// @param pendingDepositRoot The unique identifier of the deposit emitted in `ETHStaked` from\\n /// the `stakeEth` function.\\n /// @param depositProcessedSlot Any slot on or after the strategy's deposit was processed on the beacon chain.\\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\\n /// set for the next block timestamp in 12 seconds time.\\n /// @param firstPendingDeposit a `FirstPendingDepositSlotProofData` struct containing:\\n /// - slot: The beacon chain slot of the first deposit in the beacon chain's deposit queue.\\n /// Can be any non-zero value if the deposit queue is empty.\\n /// - proof: The merkle proof of the first pending deposit's slot to the beacon block root.\\n /// Can be either:\\n /// * 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\\n /// * 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\\n /// @param strategyValidatorData a `StrategyValidatorProofData` struct containing:\\n /// - withdrawableEpoch: The withdrawable epoch of the validator the strategy is depositing to.\\n /// - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the validator the strategy\\n /// is depositing to, to the beacon block root.\\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n // slither-disable-start reentrancy-no-eth\\n function verifyDeposit(\\n bytes32 pendingDepositRoot,\\n uint64 depositProcessedSlot,\\n FirstPendingDepositSlotProofData calldata firstPendingDeposit,\\n StrategyValidatorProofData calldata strategyValidatorData\\n ) external {\\n // Load into memory the previously saved deposit data\\n DepositData memory deposit = deposits[pendingDepositRoot];\\n ValidatorData memory strategyValidator = validator[deposit.pubKeyHash];\\n require(deposit.status == DepositStatus.PENDING, \\\"Deposit not pending\\\");\\n require(firstPendingDeposit.slot != 0, \\\"Zero 1st pending deposit slot\\\");\\n\\n // We should allow the verification of deposits for validators that have been marked as exiting\\n // to cover this situation:\\n // - there are 2 pending deposits\\n // - beacon chain has slashed the validator\\n // - when verifyDeposit is called for the first deposit it sets the Validator state to EXITING\\n // - verifyDeposit should allow a secondary call for the other deposit to a slashed validator\\n require(\\n strategyValidator.state == ValidatorState.VERIFIED ||\\n strategyValidator.state == ValidatorState.ACTIVE ||\\n strategyValidator.state == ValidatorState.EXITING,\\n \\\"Not verified/active/exiting\\\"\\n );\\n // The verification slot must be after the deposit's slot.\\n // This is needed for when the deposit queue is empty.\\n require(deposit.slot < depositProcessedSlot, \\\"Slot not after deposit\\\");\\n\\n uint64 snapTimestamp = snappedBalance.timestamp;\\n\\n // This check prevents an accounting error that can happen if:\\n // - snapBalances are snapped at the time of T\\n // - deposit is processed on the beacon chain after time T and before verifyBalances()\\n // - verifyDeposit is called before verifyBalances which removes a deposit from depositList\\n // and deposit balance from totalDepositsWei\\n // - verifyBalances is called under-reporting the strategy's balance\\n require(\\n (_calcNextBlockTimestamp(depositProcessedSlot) <= snapTimestamp) ||\\n snapTimestamp == 0,\\n \\\"Deposit after balance snapshot\\\"\\n );\\n\\n // Get the parent beacon block root of the next block which is the block root of the deposit verification slot.\\n // This will revert if the slot after the verification slot was missed.\\n bytes32 depositBlockRoot = BeaconRoots.parentBlockRoot(\\n _calcNextBlockTimestamp(depositProcessedSlot)\\n );\\n\\n // Verify the slot of the first pending deposit matches the beacon chain\\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\\n .verifyFirstPendingDeposit(\\n depositBlockRoot,\\n firstPendingDeposit.slot,\\n firstPendingDeposit.proof\\n );\\n\\n // Verify the withdrawableEpoch on the validator of the strategy's deposit\\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\\n depositBlockRoot,\\n strategyValidator.index,\\n strategyValidatorData.withdrawableEpoch,\\n strategyValidatorData.withdrawableEpochProof\\n );\\n\\n uint64 firstPendingDepositEpoch = firstPendingDeposit.slot /\\n SLOTS_PER_EPOCH;\\n\\n // Validator can either be not exiting and no further checks are required\\n // Or a validator is exiting then this function needs to make sure that the\\n // pending deposit to an exited validator has certainly been processed. The\\n // slot/epoch of first pending deposit is the one that contains the transaction\\n // where the deposit to the ETH Deposit Contract has been made.\\n //\\n // Once the firstPendingDepositEpoch becomes greater than the withdrawableEpoch of\\n // the slashed validator then the deposit has certainly been processed. When the beacon\\n // chain reaches the withdrawableEpoch of the validator the deposit will no longer be\\n // postponed. And any new deposits created (and present in the deposit queue)\\n // will have an equal or larger withdrawableEpoch.\\n require(\\n strategyValidatorData.withdrawableEpoch == FAR_FUTURE_EPOCH ||\\n strategyValidatorData.withdrawableEpoch <=\\n firstPendingDepositEpoch,\\n \\\"Exit Deposit likely not proc.\\\"\\n );\\n\\n // solhint-disable max-line-length\\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\\n // many deposits in the same block, hence have the same pending deposit slot.\\n // If the deposit queue is empty then our deposit must have been processed on the beacon chain.\\n // The deposit slot can be zero for validators consolidating to a compounding validator or 0x01 validator\\n // being promoted to a compounding one. Reference:\\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\\n // We can not guarantee that the deposit has been processed in that case.\\n // solhint-enable max-line-length\\n require(\\n deposit.slot < firstPendingDeposit.slot || isDepositQueueEmpty,\\n \\\"Deposit likely not processed\\\"\\n );\\n\\n // Remove the deposit now it has been verified as processed on the beacon chain.\\n _removeDeposit(pendingDepositRoot, deposit);\\n\\n emit DepositVerified(\\n pendingDepositRoot,\\n uint256(deposit.amountGwei) * 1 gwei\\n );\\n }\\n\\n function _removeDeposit(\\n bytes32 pendingDepositRoot,\\n DepositData memory deposit\\n ) internal {\\n // After verifying the proof, update the contract storage\\n deposits[pendingDepositRoot].status = DepositStatus.VERIFIED;\\n // Move the last deposit to the index of the verified deposit\\n bytes32 lastDeposit = depositList[depositList.length - 1];\\n depositList[deposit.depositIndex] = lastDeposit;\\n deposits[lastDeposit].depositIndex = deposit.depositIndex;\\n // Delete the last deposit from the list\\n depositList.pop();\\n }\\n\\n /// @dev Calculates the timestamp of the next execution block from the given slot.\\n /// @param slot The beacon chain slot number used for merkle proof verification.\\n function _calcNextBlockTimestamp(uint64 slot)\\n internal\\n view\\n returns (uint64)\\n {\\n // Calculate the next block timestamp from the slot.\\n return SLOT_DURATION * slot + BEACON_GENESIS_TIMESTAMP + SLOT_DURATION;\\n }\\n\\n // slither-disable-end reentrancy-no-eth\\n\\n /// @notice Stores the current ETH balance at the current block and beacon block root\\n /// of the slot that is associated with the previous block.\\n ///\\n /// When snapping / verifying balance it is of a high importance that there is no\\n /// miss-match in respect to ETH that is held by the contract and balances that are\\n /// verified on the validators.\\n ///\\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\\n /// constructing a block on the beacon chain consist of:\\n /// - process_withdrawals: ETH is deducted from the validator's balance\\n /// - process_execution_payload: immediately after the previous step executing all the\\n /// transactions\\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\\n /// contained in the withdrawal credentials of the exited validators\\n ///\\n /// That means that balance increases which are part of the post-block execution state are\\n /// done within the block, but the transaction that are contained within that block can not\\n /// see / interact with the balance from the exited validators. Only transactions in the\\n /// next block can do that.\\n ///\\n /// When snap balances is performed the state of the chain is snapped across 2 separate\\n /// chain states:\\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\\n /// will be referred to as Y - 1 as it makes no difference in the argument\\n ///\\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\\n /// miss-counting ETH or much more dangerous double counting of the ETH.\\n ///\\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\\n /// - ETH balance in the contract on block X\\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\\n /// - ETH balance in validators that are active in slot Y - 1\\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\\n /// and have their balance visible to transactions in slot Y and corresponding block X\\n /// (or sooner)\\n ///\\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\\n ///\\n /// *ETH balance in the contract on block X*\\n ///\\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\\n ///\\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\\n /// would result in double counting the `stakedEth` since it would be present once in the\\n /// snapped contract balance and the second time in deposit storage variables.\\n ///\\n /// This behaviour is correct.\\n ///\\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\\n ///\\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\\n /// contract at block Z. The execution layer doesn't have direct access to the state of\\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\\n /// and could also be part of the validator balances. It does that by verifying that at\\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\\n /// the last snap till now all are still in queue. Which ensures they can not be part of\\n /// the validator balances in later steps.\\n ///\\n /// This behaviour is correct.\\n ///\\n /// *ETH balance in validators that are active in slot Y - 1*\\n ///\\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\\n /// correctness.\\n ///\\n /// This behaviour is correct.\\n ///\\n /// *The withdrawn validators*\\n ///\\n /// The withdrawn validators could have their balances deducted in any slot before slot\\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\\n /// look at the \\\"worst case scenario\\\" where the validator withdrawal is processed in the\\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\\n /// even if the validator exits at the latest possible time it is paramount that the ETH\\n /// balance on the execution layer is recorded in the next block. Correctly accounting\\n /// for the withdrawn ETH.\\n ///\\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\\n /// validator balance verification as well as execution layer contract balance snap.\\n ///\\n /// This behaviour is correct.\\n ///\\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\\n function snapBalances() external {\\n uint64 currentTimestamp = SafeCast.toUint64(block.timestamp);\\n require(\\n snappedBalance.timestamp + SNAP_BALANCES_DELAY < currentTimestamp,\\n \\\"Snap too soon\\\"\\n );\\n\\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(currentTimestamp);\\n // Get the current ETH balance\\n uint256 ethBalance = address(this).balance;\\n\\n // Store the snapped balance\\n snappedBalance = Balances({\\n blockRoot: blockRoot,\\n timestamp: currentTimestamp,\\n ethBalance: SafeCast.toUint128(ethBalance)\\n });\\n\\n emit BalancesSnapped(blockRoot, ethBalance);\\n }\\n\\n // A struct is used to avoid stack too deep errors\\n struct BalanceProofs {\\n // BeaconBlock.state.balances\\n bytes32 balancesContainerRoot;\\n bytes balancesContainerProof;\\n // BeaconBlock.state.balances[validatorIndex]\\n bytes32[] validatorBalanceLeaves;\\n bytes[] validatorBalanceProofs;\\n }\\n\\n struct PendingDepositProofs {\\n bytes32 pendingDepositContainerRoot;\\n bytes pendingDepositContainerProof;\\n uint40[] pendingDepositIndexes;\\n bytes[] pendingDepositProofs;\\n }\\n\\n /// @notice Verifies the balances of all active validators on the beacon chain\\n /// and checks each of the strategy's deposits are still to be processed by the beacon chain.\\n /// @param balanceProofs a `BalanceProofs` struct containing the following:\\n /// - balancesContainerRoot: The merkle root of the balances container\\n /// - balancesContainerProof: The merkle proof for the balances container to the beacon block root.\\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n /// - validatorBalanceLeaves: Array of leaf nodes containing the validator balance with three other balances.\\n /// - validatorBalanceProofs: Array of merkle proofs for the validator balance to the Balances container root.\\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n /// @param pendingDepositProofs a `PendingDepositProofs` struct containing the following:\\n /// - pendingDepositContainerRoot: The merkle root of the pending deposits list container\\n /// - pendingDepositContainerProof: The merkle proof from the pending deposits list container\\n /// to the beacon block root.\\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n /// - pendingDepositIndexes: Array of indexes in the pending deposits list container for each\\n /// of the strategy's deposits.\\n /// - pendingDepositProofs: Array of merkle proofs for each strategy deposit in the\\n /// beacon chain's pending deposit list container to the pending deposits list container root.\\n /// These are 28 witness hashes of 32 bytes each concatenated together starting from the leaf node.\\n // slither-disable-start reentrancy-no-eth\\n function verifyBalances(\\n BalanceProofs calldata balanceProofs,\\n PendingDepositProofs calldata pendingDepositProofs\\n ) external {\\n // Load previously snapped balances for the given block root\\n Balances memory balancesMem = snappedBalance;\\n // Check the balances are the latest\\n require(balancesMem.timestamp > 0, \\\"No snapped balances\\\");\\n\\n uint256 verifiedValidatorsCount = verifiedValidators.length;\\n uint256 totalValidatorBalance = 0;\\n uint256 depositsCount = depositList.length;\\n\\n // If there are no verified validators then we can skip the balance verification\\n if (verifiedValidatorsCount > 0) {\\n require(\\n balanceProofs.validatorBalanceProofs.length ==\\n verifiedValidatorsCount,\\n \\\"Invalid balance proofs\\\"\\n );\\n require(\\n balanceProofs.validatorBalanceLeaves.length ==\\n verifiedValidatorsCount,\\n \\\"Invalid balance leaves\\\"\\n );\\n // verify beaconBlock.state.balances root to beacon block root\\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\\n balancesMem.blockRoot,\\n balanceProofs.balancesContainerRoot,\\n balanceProofs.balancesContainerProof\\n );\\n\\n bytes32[]\\n memory validatorHashesMem = _getPendingDepositValidatorHashes(\\n depositsCount\\n );\\n\\n // for each validator in reverse order so we can pop off exited validators at the end\\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\\n --i;\\n ValidatorData memory validatorDataMem = validator[\\n verifiedValidators[i]\\n ];\\n // verify validator's balance in beaconBlock.state.balances to the\\n // beaconBlock.state.balances container root\\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\\n .verifyValidatorBalance(\\n balanceProofs.balancesContainerRoot,\\n balanceProofs.validatorBalanceLeaves[i],\\n balanceProofs.validatorBalanceProofs[i],\\n validatorDataMem.index\\n );\\n\\n bool depositPending = false;\\n for (uint256 j = 0; j < validatorHashesMem.length; j++) {\\n if (validatorHashesMem[j] == verifiedValidators[i]) {\\n depositPending = true;\\n break;\\n }\\n }\\n\\n // If validator has a pending deposit we can not remove due to\\n // the following situation:\\n // - validator has a pending deposit\\n // - validator has been slashed\\n // - sweep cycle has withdrawn all ETH from the validator. Balance is 0\\n // - beacon chain has processed the deposit and set the validator balance\\n // to deposit amount\\n // - if validator is no longer in the list of verifiedValidators its\\n // balance will not be considered and be under-counted.\\n if (validatorBalanceGwei == 0 && !depositPending) {\\n // Store the validator state as exited\\n // This could have been in VERIFIED, ACTIVE or EXITING state\\n validator[verifiedValidators[i]].state = ValidatorState\\n .EXITED;\\n\\n // Remove the validator with a zero balance from the list of verified validators\\n\\n // Reduce the count of verified validators which is the last index before the pop removes it.\\n verifiedValidatorsCount -= 1;\\n\\n // Move the last validator that has already been verified to the current index.\\n // There's an extra SSTORE if i is the last active validator but that's fine,\\n // It's not a common case and the code is simpler this way.\\n verifiedValidators[i] = verifiedValidators[\\n verifiedValidatorsCount\\n ];\\n // Delete the last validator from the list\\n verifiedValidators.pop();\\n\\n // The validator balance is zero so not need to add to totalValidatorBalance\\n continue;\\n } else if (\\n validatorDataMem.state == ValidatorState.VERIFIED &&\\n validatorBalanceGwei >= MIN_ACTIVATION_BALANCE_GWEI\\n ) {\\n validator[verifiedValidators[i]].state = ValidatorState\\n .ACTIVE;\\n }\\n\\n // convert Gwei balance to Wei and add to the total validator balance\\n totalValidatorBalance += validatorBalanceGwei * 1 gwei;\\n }\\n }\\n\\n uint256 totalDepositsWei = 0;\\n\\n // If there are no deposits then we can skip the deposit verification.\\n // This section is after the validator balance verifications so an exited validator will be marked\\n // as EXITED before the deposits are verified. If there was a deposit to an exited validator\\n // then the deposit can only be removed once the validator is fully exited.\\n // It is possible that validator fully exits and a postponed deposit to an exited validator increases\\n // its balance again. In such case the contract will erroneously consider a deposit applied before it\\n // has been applied on the beacon chain showing a smaller than real `totalValidatorBalance`.\\n if (depositsCount > 0) {\\n require(\\n pendingDepositProofs.pendingDepositProofs.length ==\\n depositsCount,\\n \\\"Invalid deposit proofs\\\"\\n );\\n require(\\n pendingDepositProofs.pendingDepositIndexes.length ==\\n depositsCount,\\n \\\"Invalid deposit indexes\\\"\\n );\\n\\n // Verify from the root of the pending deposit list container to the beacon block root\\n IBeaconProofs(BEACON_PROOFS).verifyPendingDepositsContainer(\\n balancesMem.blockRoot,\\n pendingDepositProofs.pendingDepositContainerRoot,\\n pendingDepositProofs.pendingDepositContainerProof\\n );\\n\\n // For each staking strategy's deposit.\\n for (uint256 i = 0; i < depositsCount; ++i) {\\n bytes32 pendingDepositRoot = depositList[i];\\n\\n // Verify the strategy's deposit is still pending on the beacon chain.\\n IBeaconProofs(BEACON_PROOFS).verifyPendingDeposit(\\n pendingDepositProofs.pendingDepositContainerRoot,\\n pendingDepositRoot,\\n pendingDepositProofs.pendingDepositProofs[i],\\n pendingDepositProofs.pendingDepositIndexes[i]\\n );\\n\\n // Convert the deposit amount from Gwei to Wei and add to the total\\n totalDepositsWei +=\\n uint256(deposits[pendingDepositRoot].amountGwei) *\\n 1 gwei;\\n }\\n }\\n\\n // Store the verified balance in storage\\n lastVerifiedEthBalance =\\n totalDepositsWei +\\n totalValidatorBalance +\\n balancesMem.ethBalance;\\n // Reset the last snap timestamp so a new snapBalances has to be made\\n snappedBalance.timestamp = 0;\\n\\n emit BalancesVerified(\\n balancesMem.timestamp,\\n totalDepositsWei,\\n totalValidatorBalance,\\n balancesMem.ethBalance\\n );\\n }\\n\\n // slither-disable-end reentrancy-no-eth\\n\\n /// @notice get a list of all validator hashes present in the pending deposits\\n /// list can have duplicate entries\\n function _getPendingDepositValidatorHashes(uint256 depositsCount)\\n internal\\n view\\n returns (bytes32[] memory validatorHashes)\\n {\\n validatorHashes = new bytes32[](depositsCount);\\n for (uint256 i = 0; i < depositsCount; i++) {\\n validatorHashes[i] = deposits[depositList[i]].pubKeyHash;\\n }\\n }\\n\\n /// @notice Hash a validator public key using the Beacon Chain's format\\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\\n require(pubKey.length == 48, \\\"Invalid public key\\\");\\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\\n }\\n\\n /**\\n *\\n * WETH and ETH Accounting\\n *\\n */\\n\\n /// @dev Called when WETH is transferred out of the strategy so\\n /// the strategy knows how much WETH it has on deposit.\\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\\n function _transferWeth(uint256 _amount, address _recipient) internal {\\n IERC20(WETH).safeTransfer(_recipient, _amount);\\n\\n // The min is required as more WETH can be withdrawn than deposited\\n // as the strategy earns consensus and execution rewards.\\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\\n depositedWethAccountedFor -= deductAmount;\\n\\n // No change in ETH balance so no need to snapshot the balances\\n }\\n\\n /// @dev Converts ETH to WETH and updates the accounting.\\n /// @param _ethAmount The amount of ETH in wei.\\n function _convertEthToWeth(uint256 _ethAmount) internal {\\n // slither-disable-next-line arbitrary-send-eth\\n IWETH9(WETH).deposit{ value: _ethAmount }();\\n\\n depositedWethAccountedFor += _ethAmount;\\n\\n // Store the reduced ETH balance.\\n // The ETH balance in this strategy contract can be more than the last verified ETH balance\\n // due to partial withdrawals or full exits being processed by the beacon chain since the last snapBalances.\\n // It can also happen from execution rewards (MEV) or ETH donations.\\n lastVerifiedEthBalance -= Math.min(lastVerifiedEthBalance, _ethAmount);\\n\\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\\n snappedBalance.timestamp = 0;\\n }\\n\\n /// @dev Converts WETH to ETH and updates the accounting.\\n /// @param _wethAmount The amount of WETH in wei.\\n function _convertWethToEth(uint256 _wethAmount) internal {\\n IWETH9(WETH).withdraw(_wethAmount);\\n\\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\\n depositedWethAccountedFor -= deductAmount;\\n\\n // Store the increased ETH balance\\n lastVerifiedEthBalance += _wethAmount;\\n\\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\\n snappedBalance.timestamp = 0;\\n }\\n\\n /**\\n *\\n * View Functions\\n *\\n */\\n\\n /// @notice Returns the number of deposits waiting to be verified as processed on the beacon chain,\\n /// or deposits that have been verified to an exiting validator and is now waiting for the\\n /// validator's balance to be swept.\\n function depositListLength() external view returns (uint256) {\\n return depositList.length;\\n }\\n\\n /// @notice Returns the number of verified validators.\\n function verifiedValidatorsLength() external view returns (uint256) {\\n return verifiedValidators.length;\\n }\\n}\\n\",\"keccak256\":\"0x8044cb8edf80a317139db5f26281e9c925ba6ba50c9a249eed3a83e82aa689ac\",\"license\":\"BUSL-1.1\"}},\"version\":1}", + "bytecode": "0x60a060405234801561001057600080fd5b5060405161097038038061097083398101604081905261002f91610040565b6001600160a01b0316608052610070565b60006020828403121561005257600080fd5b81516001600160a01b038116811461006957600080fd5b9392505050565b6080516108b66100ba60003960008181604b0152818160ba015281816101ce015281816102480152818161034b0152818161044d0152818161047c015261059301526108b66000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80632e4aee1f146100465780635e1c519b1461008a578063d1699c261461009f575b600080fd5b61006d7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b6100926100b4565b604051610081919061066f565b6100a7610345565b60405161008191906106f2565b606060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d79e40326040518163ffffffff1660e01b8152600401602060405180830381865afa158015610116573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061013a9190610764565b90508067ffffffffffffffff8111156101555761015561077d565b6040519080825280602002602001820160405280156101a757816020015b61019460408051606081018252600080825260208201819052909182015290565b8152602001906001900390816101735790505b50915060005b8181101561034057604051630ef9985560e01b8152600481018290526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690630ef9985590602401602060405180830381865afa15801561021d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102419190610764565b90506000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166398245f1b846040518263ffffffff1660e01b815260040161029491815260200190565b6040805180830381865afa1580156102b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102d49190610793565b64ffffffffff169150915060405180606001604052808481526020018267ffffffffffffffff16815260200183600881111561031257610312610659565b815250868581518110610327576103276107db565b60200260200101819052505050508060010190506101ad565b505090565b606060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634896b31a6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103cb9190610764565b90508067ffffffffffffffff8111156103e6576103e661077d565b60405190808252806020026020018201604052801561043857816020015b6040805160808101825260008082526020808301829052928201819052606082015282526000199092019101816104045790505b50915060005b818110156103405760008060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316633d4dff7b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b8ec6678876040518263ffffffff1660e01b81526004016104c891815260200190565b602060405180830381865afa1580156104e5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105099190610764565b6040518263ffffffff1660e01b815260040161052791815260200190565b60a060405180830381865afa158015610544573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610568919061080e565b505060408051608081019182905263171d8ccf60e31b909152608481018890529295509093509150807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b8ec667860a48301602060405180830381865afa1580156105e1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106059190610764565b81526020018481526020018367ffffffffffffffff1681526020018267ffffffffffffffff16815250868581518110610640576106406107db565b602002602001018190525050505080600101905061043e565b634e487b7160e01b600052602160045260246000fd5b602080825282518282018190526000918401906040840190835b818110156106e75783518051845260208082015167ffffffffffffffff169085015260400151600981106106cd57634e487b7160e01b600052602160045260246000fd5b604084015260209390930192606090920191600101610689565b509095945050505050565b602080825282518282018190526000918401906040840190835b818110156106e7578351805184526020810151602085015267ffffffffffffffff604082015116604085015267ffffffffffffffff60608201511660608501525060808301925060208401935060018101905061070c565b60006020828403121561077657600080fd5b5051919050565b634e487b7160e01b600052604160045260246000fd5b600080604083850312156107a657600080fd5b8251600981106107b557600080fd5b602084015190925064ffffffffff811681146107d057600080fd5b809150509250929050565b634e487b7160e01b600052603260045260246000fd5b805167ffffffffffffffff8116811461080957600080fd5b919050565b600080600080600060a0868803121561082657600080fd5b85519450610836602087016107f1565b9350610844604087016107f1565b9250606086015163ffffffff8116811461085d57600080fd5b60808701519092506003811061087257600080fd5b80915050929550929590935056fea26469706673582212203f6388a1cbaede7d6c44a507349408982c9bc275d7328bee39535c6f72e4d12764736f6c634300081c0033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100415760003560e01c80632e4aee1f146100465780635e1c519b1461008a578063d1699c261461009f575b600080fd5b61006d7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b6100926100b4565b604051610081919061066f565b6100a7610345565b60405161008191906106f2565b606060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d79e40326040518163ffffffff1660e01b8152600401602060405180830381865afa158015610116573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061013a9190610764565b90508067ffffffffffffffff8111156101555761015561077d565b6040519080825280602002602001820160405280156101a757816020015b61019460408051606081018252600080825260208201819052909182015290565b8152602001906001900390816101735790505b50915060005b8181101561034057604051630ef9985560e01b8152600481018290526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690630ef9985590602401602060405180830381865afa15801561021d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102419190610764565b90506000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166398245f1b846040518263ffffffff1660e01b815260040161029491815260200190565b6040805180830381865afa1580156102b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102d49190610793565b64ffffffffff169150915060405180606001604052808481526020018267ffffffffffffffff16815260200183600881111561031257610312610659565b815250868581518110610327576103276107db565b60200260200101819052505050508060010190506101ad565b505090565b606060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634896b31a6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103cb9190610764565b90508067ffffffffffffffff8111156103e6576103e661077d565b60405190808252806020026020018201604052801561043857816020015b6040805160808101825260008082526020808301829052928201819052606082015282526000199092019101816104045790505b50915060005b818110156103405760008060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316633d4dff7b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b8ec6678876040518263ffffffff1660e01b81526004016104c891815260200190565b602060405180830381865afa1580156104e5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105099190610764565b6040518263ffffffff1660e01b815260040161052791815260200190565b60a060405180830381865afa158015610544573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610568919061080e565b505060408051608081019182905263171d8ccf60e31b909152608481018890529295509093509150807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b8ec667860a48301602060405180830381865afa1580156105e1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106059190610764565b81526020018481526020018367ffffffffffffffff1681526020018267ffffffffffffffff16815250868581518110610640576106406107db565b602002602001018190525050505080600101905061043e565b634e487b7160e01b600052602160045260246000fd5b602080825282518282018190526000918401906040840190835b818110156106e75783518051845260208082015167ffffffffffffffff169085015260400151600981106106cd57634e487b7160e01b600052602160045260246000fd5b604084015260209390930192606090920191600101610689565b509095945050505050565b602080825282518282018190526000918401906040840190835b818110156106e7578351805184526020810151602085015267ffffffffffffffff604082015116604085015267ffffffffffffffff60608201511660608501525060808301925060208401935060018101905061070c565b60006020828403121561077657600080fd5b5051919050565b634e487b7160e01b600052604160045260246000fd5b600080604083850312156107a657600080fd5b8251600981106107b557600080fd5b602084015190925064ffffffffff811681146107d057600080fd5b809150509250929050565b634e487b7160e01b600052603260045260246000fd5b805167ffffffffffffffff8116811461080957600080fd5b919050565b600080600080600060a0868803121561082657600080fd5b85519450610836602087016107f1565b9350610844604087016107f1565b9250606086015163ffffffff8116811461085d57600080fd5b60808701519092506003811061087257600080fd5b80915050929550929590935056fea26469706673582212203f6388a1cbaede7d6c44a507349408982c9bc275d7328bee39535c6f72e4d12764736f6c634300081c0033", + "libraries": {}, + "devdoc": { + "author": "Origin Protocol Inc", + "kind": "dev", + "methods": { + "getPendingDeposits()": { + "returns": { + "pendingDeposits": "An array of `DepositView` containing the deposit ID, public key hash, amount in Gwei and the slot of the deposit." + } + }, + "getVerifiedValidators()": { + "returns": { + "validators": "An array of `ValidatorView` containing the public key hash, validator index and state." + } + } + }, + "title": "Viewing contract for the Compounding Staking Strategy.", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "getPendingDeposits()": { + "notice": "Returns the deposits that are still to be verified. These may or may not have been processed by the beacon chain." + }, + "getVerifiedValidators()": { + "notice": "Returns the strategy's active validators. These are the ones that have been verified and have a non-zero balance." + }, + "stakingStrategy()": { + "notice": "The address of the Compounding Staking Strategy contract" + } + }, + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/FeeAccumulator.json b/contracts/deployments/hoodi/FeeAccumulator.json new file mode 100644 index 0000000000..32afad4583 --- /dev/null +++ b/contracts/deployments/hoodi/FeeAccumulator.json @@ -0,0 +1,125 @@ +{ + "address": "0xb2e9D0D8cADB30Bb66929996121cbd8Bf4C3fFe7", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_strategy", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "strategy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ExecutionRewardsCollected", + "type": "event" + }, + { + "inputs": [], + "name": "STRATEGY", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "collect", + "outputs": [ + { + "internalType": "uint256", + "name": "eth", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "transactionHash": "0x41038717684e5cff13ab9e24474a5d4223c4a46666c040cbb97ffbfe7bb7ea12", + "receipt": { + "to": null, + "from": "0xFD9E6005187F448957a0972a7d0C0A6dA2911236", + "contractAddress": "0xb2e9D0D8cADB30Bb66929996121cbd8Bf4C3fFe7", + "transactionIndex": 2, + "gasUsed": "225555", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x18354b0b010bb9df164bffe93e7117b310255e1734e308887472ba38c6fcfcd0", + "transactionHash": "0x41038717684e5cff13ab9e24474a5d4223c4a46666c040cbb97ffbfe7bb7ea12", + "logs": [], + "blockNumber": 828251, + "cumulativeGasUsed": "267555", + "status": 1, + "byzantium": true + }, + "args": [ + "0x95DdB082cf2b8Bb4a4A358A8817a848cE32e446b" + ], + "numDeployments": 1, + "solcInputHash": "294c99ebae323732f7829a948fbd35c6", + "metadata": "{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_strategy\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"strategy\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"ExecutionRewardsCollected\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"STRATEGY\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"collect\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"eth\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}],\"devdoc\":{\"author\":\"Origin Protocol Inc\",\"kind\":\"dev\",\"methods\":{\"collect()\":{\"returns\":{\"eth\":\"The amount of execution rewards that were sent to the Native Staking Strategy\"}},\"constructor\":{\"params\":{\"_strategy\":\"Address of the Native Staking Strategy\"}}},\"title\":\"Fee Accumulator for Native Staking SSV Strategy\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"STRATEGY()\":{\"notice\":\"The address of the Native Staking Strategy\"},\"collect()\":{\"notice\":\"sends all ETH in this FeeAccumulator contract to the Native Staking Strategy.\"}},\"notice\":\"Receives execution rewards which includes tx fees and MEV rewards like tx priority and tx ordering. It does NOT include swept ETH from beacon chain consensus rewards or full validator withdrawals.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/strategies/NativeStaking/FeeAccumulator.sol\":\"FeeAccumulator\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize, which returns 0 for contracts in\\n // construction, since the code is only stored at the end of the\\n // constructor execution.\\n\\n uint256 size;\\n assembly {\\n size := extcodesize(account)\\n }\\n return size > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCall(target, data, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n require(isContract(target), \\\"Address: static call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(isContract(target), \\\"Address: delegate call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x51b758a8815ecc9596c66c37d56b1d33883a444631a3f916b9fe65cb863ef7c4\",\"license\":\"MIT\"},\"contracts/strategies/NativeStaking/FeeAccumulator.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { Address } from \\\"@openzeppelin/contracts/utils/Address.sol\\\";\\n\\n/**\\n * @title Fee Accumulator for Native Staking SSV Strategy\\n * @notice Receives execution rewards which includes tx fees and\\n * MEV rewards like tx priority and tx ordering.\\n * It does NOT include swept ETH from beacon chain consensus rewards or full validator withdrawals.\\n * @author Origin Protocol Inc\\n */\\ncontract FeeAccumulator {\\n /// @notice The address of the Native Staking Strategy\\n address public immutable STRATEGY;\\n\\n event ExecutionRewardsCollected(address indexed strategy, uint256 amount);\\n\\n /**\\n * @param _strategy Address of the Native Staking Strategy\\n */\\n constructor(address _strategy) {\\n STRATEGY = _strategy;\\n }\\n\\n /**\\n * @notice sends all ETH in this FeeAccumulator contract to the Native Staking Strategy.\\n * @return eth The amount of execution rewards that were sent to the Native Staking Strategy\\n */\\n function collect() external returns (uint256 eth) {\\n require(msg.sender == STRATEGY, \\\"Caller is not the Strategy\\\");\\n\\n eth = address(this).balance;\\n if (eth > 0) {\\n // Send the ETH to the Native Staking Strategy\\n Address.sendValue(payable(STRATEGY), eth);\\n\\n emit ExecutionRewardsCollected(STRATEGY, eth);\\n }\\n }\\n\\n /**\\n * @dev Accept ETH\\n */\\n receive() external payable {}\\n}\\n\",\"keccak256\":\"0x153897222930c7f8a7b424620d5bfef3f7e57fa5f909d022b34302412a6324dc\",\"license\":\"BUSL-1.1\"}},\"version\":1}", + "bytecode": "0x60a060405234801561001057600080fd5b506040516103b73803806103b783398101604081905261002f91610040565b6001600160a01b0316608052610070565b60006020828403121561005257600080fd5b81516001600160a01b038116811461006957600080fd5b9392505050565b60805161031961009e60003960008181604b0152818160ba01528181610139015261016001526103196000f3fe60806040526004361061002d5760003560e01c8063185025ef14610039578063e52253811461008a57600080fd5b3661003457005b600080fd5b34801561004557600080fd5b5061006d7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561009657600080fd5b5061009f6100ad565b604051908152602001610081565b6000336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461012c5760405162461bcd60e51b815260206004820152601a60248201527f43616c6c6572206973206e6f742074686520537472617465677900000000000060448201526064015b60405180910390fd5b504780156101c25761015e7f0000000000000000000000000000000000000000000000000000000000000000826101c5565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03167fc2acb502a0dc166a61cd83b914b480d76050e91a6797d7a833be84c4eace1dfe826040516101b991815260200190565b60405180910390a25b90565b804710156102155760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610123565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114610262576040519150601f19603f3d011682016040523d82523d6000602084013e610267565b606091505b50509050806102de5760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610123565b50505056fea26469706673582212201572ccf042be975493c9c9c760835e92ffcf6bfb98faaf78ebc941900882db1864736f6c634300081c0033", + "deployedBytecode": "0x60806040526004361061002d5760003560e01c8063185025ef14610039578063e52253811461008a57600080fd5b3661003457005b600080fd5b34801561004557600080fd5b5061006d7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561009657600080fd5b5061009f6100ad565b604051908152602001610081565b6000336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461012c5760405162461bcd60e51b815260206004820152601a60248201527f43616c6c6572206973206e6f742074686520537472617465677900000000000060448201526064015b60405180910390fd5b504780156101c25761015e7f0000000000000000000000000000000000000000000000000000000000000000826101c5565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03167fc2acb502a0dc166a61cd83b914b480d76050e91a6797d7a833be84c4eace1dfe826040516101b991815260200190565b60405180910390a25b90565b804710156102155760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610123565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114610262576040519150601f19603f3d011682016040523d82523d6000602084013e610267565b606091505b50509050806102de5760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610123565b50505056fea26469706673582212201572ccf042be975493c9c9c760835e92ffcf6bfb98faaf78ebc941900882db1864736f6c634300081c0033", + "libraries": {}, + "devdoc": { + "author": "Origin Protocol Inc", + "kind": "dev", + "methods": { + "collect()": { + "returns": { + "eth": "The amount of execution rewards that were sent to the Native Staking Strategy" + } + }, + "constructor": { + "params": { + "_strategy": "Address of the Native Staking Strategy" + } + } + }, + "title": "Fee Accumulator for Native Staking SSV Strategy", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "STRATEGY()": { + "notice": "The address of the Native Staking Strategy" + }, + "collect()": { + "notice": "sends all ETH in this FeeAccumulator contract to the Native Staking Strategy." + } + }, + "notice": "Receives execution rewards which includes tx fees and MEV rewards like tx priority and tx ordering. It does NOT include swept ETH from beacon chain consensus rewards or full validator withdrawals.", + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/MockBeaconRoots.json b/contracts/deployments/hoodi/MockBeaconRoots.json new file mode 100644 index 0000000000..3d4608373e --- /dev/null +++ b/contracts/deployments/hoodi/MockBeaconRoots.json @@ -0,0 +1,160 @@ +{ + "address": "0xdCfcAE4A084AA843eE446f400B23aA7B6340484b", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "root", + "type": "bytes32" + } + ], + "name": "RootSet", + "type": "event" + }, + { + "stateMutability": "nonpayable", + "type": "fallback" + }, + { + "inputs": [], + "name": "latestBlockRoot", + "outputs": [ + { + "internalType": "bytes32", + "name": "parentRoot", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "timestamp", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "timestamp", + "type": "uint64" + } + ], + "name": "parentBlockRoot", + "outputs": [ + { + "internalType": "bytes32", + "name": "parentRoot", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "root", + "type": "bytes32" + } + ], + "name": "setBeaconRoot", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "root", + "type": "bytes32" + } + ], + "name": "setBeaconRoot", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "transactionHash": "0x36a08c5d2aa747cdedb936d78649a4a70a5cc67aaebe0e091d5e183159e3b625", + "receipt": { + "to": null, + "from": "0xFD9E6005187F448957a0972a7d0C0A6dA2911236", + "contractAddress": "0xdCfcAE4A084AA843eE446f400B23aA7B6340484b", + "transactionIndex": 20, + "gasUsed": "305655", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x6b73e3b45af0a3c988b9088e927bd5f48a4cb7a3190d7267980356b5a11ed33d", + "transactionHash": "0x36a08c5d2aa747cdedb936d78649a4a70a5cc67aaebe0e091d5e183159e3b625", + "logs": [], + "blockNumber": 863048, + "cumulativeGasUsed": "41063495", + "status": 1, + "byzantium": true + }, + "args": [], + "numDeployments": 1, + "solcInputHash": "e971b2fb0f925bb0e94e79f6b234f436", + "metadata": "{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"name\":\"RootSet\",\"type\":\"event\"},{\"stateMutability\":\"nonpayable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"latestBlockRoot\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"parentRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"}],\"name\":\"parentBlockRoot\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"parentRoot\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"name\":\"setBeaconRoot\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"name\":\"setBeaconRoot\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/mocks/MockBeaconRoots.sol\":\"MockBeaconRoots\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"contracts/beacon/BeaconRoots.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nlibrary BeaconRoots {\\n /// @notice The address of beacon block roots oracle\\n /// See https://eips.ethereum.org/EIPS/eip-4788\\n address internal constant BEACON_ROOTS_ADDRESS =\\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\\n\\n /// @notice The length of the beacon block root ring buffer\\n uint256 internal constant BEACON_ROOTS_HISTORY_BUFFER_LENGTH = 8191;\\n\\n /// @notice Returns the Beacon Block Root for the previous block.\\n /// This comes from the Beacon Roots contract defined in EIP-4788\\n /// @param timestamp The timestamp of the block for which to get the parent root.\\n /// @return parentRoot The parent block root for the given timestamp.\\n function parentBlockRoot(uint64 timestamp)\\n internal\\n view\\n returns (bytes32 parentRoot)\\n {\\n // Commented out the following checks as it makes unit and fork testing very difficult.\\n // require(block.timestamp >= timestamp, \\\"Timestamp in future\\\");\\n // require(\\n // block.timestamp - timestamp <\\n // BEACON_ROOTS_HISTORY_BUFFER_LENGTH * 12,\\n // \\\"Timestamp too old\\\"\\n // );\\n\\n // Call the Beacon Block Root Oracle to get the parent block root\\n // This does not have a function signature, so we use a staticcall\\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\\n abi.encode(timestamp)\\n );\\n\\n require(success && result.length > 0, \\\"Invalid beacon timestamp\\\");\\n parentRoot = abi.decode(result, (bytes32));\\n }\\n}\\n\",\"keccak256\":\"0xdbf9d944c36476ef702716288f425eb0236a51aa2bfca150ead2a59495beb422\",\"license\":\"BUSL-1.1\"},\"contracts/mocks/MockBeaconRoots.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { BeaconRoots } from \\\"../beacon/BeaconRoots.sol\\\";\\n\\ncontract MockBeaconRoots {\\n // Mapping to simulate the ring buffer: timestamp => beacon block root\\n mapping(uint256 => bytes32) internal _beaconRoots;\\n\\n // Event to log when a new root is set (for testing)\\n event RootSet(uint256 indexed timestamp, bytes32 root);\\n\\n // Fallback function to handle raw 32-byte timestamp input\\n // solhint-disable no-complex-fallback\\n fallback() external {\\n // Ensure input is exactly 32 bytes (big-endian encoded timestamp)\\n require(msg.data.length == 32, \\\"Input must be 32 bytes\\\");\\n\\n // Decode the 32-byte input as a uint256 timestamp (big-endian)\\n uint256 timestamp = abi.decode(msg.data, (uint256));\\n\\n // Don't do any validation of timestamp so we can test any block\\n\\n // Retrieve the root. Will return bytes32(0) if not set.\\n bytes32 root = _beaconRoots[timestamp];\\n\\n // Return the 32-byte root directly\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n mstore(0, root)\\n return(0, 32)\\n }\\n }\\n\\n // Mock function to set a beacon block root (for testing)\\n function setBeaconRoot(uint256 timestamp, bytes32 root) external {\\n require(timestamp > 0, \\\"Invalid timestamp\\\");\\n require(root != bytes32(0), \\\"Invalid root\\\");\\n\\n // Store the root at the given timestamp\\n _beaconRoots[timestamp] = root;\\n\\n emit RootSet(timestamp, root);\\n }\\n\\n function setBeaconRoot(bytes32 root) external {\\n require(root != bytes32(0), \\\"Invalid root\\\");\\n\\n // Store the root at the given timestamp\\n _beaconRoots[block.timestamp] = root;\\n\\n emit RootSet(block.timestamp, root);\\n }\\n\\n function parentBlockRoot(uint64 timestamp)\\n external\\n view\\n returns (bytes32 parentRoot)\\n {\\n return BeaconRoots.parentBlockRoot(timestamp);\\n }\\n\\n function latestBlockRoot()\\n external\\n view\\n returns (bytes32 parentRoot, uint64 timestamp)\\n {\\n timestamp = uint64(block.timestamp);\\n parentRoot = BeaconRoots.parentBlockRoot(timestamp);\\n }\\n}\\n\",\"keccak256\":\"0xbef3bd6e254e9f3ac8174ad1c62610d137c08589c3b57846ecc8c31b0eadb96c\",\"license\":\"BUSL-1.1\"}},\"version\":1}", + "bytecode": "0x6080604052348015600f57600080fd5b506104918061001f6000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80636370dd3b146100be57806365190fad146100d3578063aa24b3c4146100e6578063be7c629b1461010c575b6020361461009a5760405162461bcd60e51b8152602060048201526016602482015275496e707574206d75737420626520333220627974657360501b60448201526064015b60405180910390fd5b60006100a636826103a7565b60008181526020818152604082205480835292935090f35b6100d16100cc3660046103c0565b610132565b005b6100d16100e13660046103a7565b6101fb565b6100f96100f43660046103e2565b61027e565b6040519081526020015b60405180910390f35b61011461028f565b6040805192835267ffffffffffffffff909116602083015201610103565b600082116101765760405162461bcd60e51b81526020600482015260116024820152700496e76616c69642074696d657374616d7607c1b6044820152606401610091565b806101b25760405162461bcd60e51b815260206004820152600c60248201526b125b9d985b1a59081c9bdbdd60a21b6044820152606401610091565b60008281526020818152604091829020839055905182815283917f1287e38cf3268df2db9ff3d0625e53548664e304cae1349b9575b02418226a0b910160405180910390a25050565b806102375760405162461bcd60e51b815260206004820152600c60248201526b125b9d985b1a59081c9bdbdd60a21b6044820152606401610091565b426000818152602081815260409182902084905590518381527f1287e38cf3268df2db9ff3d0625e53548664e304cae1349b9575b02418226a0b910160405180910390a250565b6000610289826102a1565b92915050565b60004261029b816102a1565b91509091565b6040805167ffffffffffffffff8316602082015260009182918291720f3df6d732807ef1319fb7b8bb8522d0beac02910160408051601f19818403018152908290526102ec91610413565b600060405180830381855afa9150503d8060008114610327576040519150601f19603f3d011682016040523d82523d6000602084013e61032c565b606091505b509150915081801561033f575060008151115b61038b5760405162461bcd60e51b815260206004820152601860248201527f496e76616c696420626561636f6e2074696d657374616d7000000000000000006044820152606401610091565b8080602001905181019061039f9190610442565b949350505050565b6000602082840312156103b957600080fd5b5035919050565b600080604083850312156103d357600080fd5b50508035926020909101359150565b6000602082840312156103f457600080fd5b813567ffffffffffffffff8116811461040c57600080fd5b9392505050565b6000825160005b81811015610434576020818601810151858301520161041a565b506000920191825250919050565b60006020828403121561045457600080fd5b505191905056fea2646970667358221220aa2d6c6f2add5ea2154c4751e4142a4cc61a0fa24166e0cdd56f8d7bf7f748e964736f6c634300081c0033", + "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061004c5760003560e01c80636370dd3b146100be57806365190fad146100d3578063aa24b3c4146100e6578063be7c629b1461010c575b6020361461009a5760405162461bcd60e51b8152602060048201526016602482015275496e707574206d75737420626520333220627974657360501b60448201526064015b60405180910390fd5b60006100a636826103a7565b60008181526020818152604082205480835292935090f35b6100d16100cc3660046103c0565b610132565b005b6100d16100e13660046103a7565b6101fb565b6100f96100f43660046103e2565b61027e565b6040519081526020015b60405180910390f35b61011461028f565b6040805192835267ffffffffffffffff909116602083015201610103565b600082116101765760405162461bcd60e51b81526020600482015260116024820152700496e76616c69642074696d657374616d7607c1b6044820152606401610091565b806101b25760405162461bcd60e51b815260206004820152600c60248201526b125b9d985b1a59081c9bdbdd60a21b6044820152606401610091565b60008281526020818152604091829020839055905182815283917f1287e38cf3268df2db9ff3d0625e53548664e304cae1349b9575b02418226a0b910160405180910390a25050565b806102375760405162461bcd60e51b815260206004820152600c60248201526b125b9d985b1a59081c9bdbdd60a21b6044820152606401610091565b426000818152602081815260409182902084905590518381527f1287e38cf3268df2db9ff3d0625e53548664e304cae1349b9575b02418226a0b910160405180910390a250565b6000610289826102a1565b92915050565b60004261029b816102a1565b91509091565b6040805167ffffffffffffffff8316602082015260009182918291720f3df6d732807ef1319fb7b8bb8522d0beac02910160408051601f19818403018152908290526102ec91610413565b600060405180830381855afa9150503d8060008114610327576040519150601f19603f3d011682016040523d82523d6000602084013e61032c565b606091505b509150915081801561033f575060008151115b61038b5760405162461bcd60e51b815260206004820152601860248201527f496e76616c696420626561636f6e2074696d657374616d7000000000000000006044820152606401610091565b8080602001905181019061039f9190610442565b949350505050565b6000602082840312156103b957600080fd5b5035919050565b600080604083850312156103d357600080fd5b50508035926020909101359150565b6000602082840312156103f457600080fd5b813567ffffffffffffffff8116811461040c57600080fd5b9392505050565b6000825160005b81811015610434576020818601810151858301520161041a565b506000920191825250919050565b60006020828403121561045457600080fd5b505191905056fea2646970667358221220aa2d6c6f2add5ea2154c4751e4142a4cc61a0fa24166e0cdd56f8d7bf7f748e964736f6c634300081c0033", + "libraries": {}, + "devdoc": { + "kind": "dev", + "methods": {}, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": {}, + "version": 1 + }, + "storageLayout": { + "storage": [ + { + "astId": 3969, + "contract": "contracts/mocks/MockBeaconRoots.sol:MockBeaconRoots", + "label": "_beaconRoots", + "offset": 0, + "slot": "0", + "type": "t_mapping(t_uint256,t_bytes32)" + } + ], + "types": { + "t_bytes32": { + "encoding": "inplace", + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_bytes32)": { + "encoding": "mapping", + "key": "t_uint256", + "label": "mapping(uint256 => bytes32)", + "numberOfBytes": "32", + "value": "t_bytes32" + }, + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/NativeStakingFeeAccumulatorProxy.json b/contracts/deployments/hoodi/NativeStakingFeeAccumulatorProxy.json new file mode 100644 index 0000000000..c1041703d2 --- /dev/null +++ b/contracts/deployments/hoodi/NativeStakingFeeAccumulatorProxy.json @@ -0,0 +1,297 @@ +{ + "address": "0x27281CE00322Ee8b7C078788Fb624D051F5F7689", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "GovernorshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "PendingGovernorshipTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "claimGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "governor", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "implementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_logic", + "type": "address" + }, + { + "internalType": "address", + "name": "_initGovernor", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "isGovernor", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newGovernor", + "type": "address" + } + ], + "name": "transferGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } + ], + "transactionHash": "0xabbe29738b581709ae446a77899c458ff86bad03d31cb77cfc5c45ea98678bef", + "receipt": { + "to": null, + "from": "0xFD9E6005187F448957a0972a7d0C0A6dA2911236", + "contractAddress": "0x27281CE00322Ee8b7C078788Fb624D051F5F7689", + "transactionIndex": 8, + "gasUsed": "599335", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000008000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000100000000100000000000000000000000000010000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x51b05ded781df237a31332e96e0cc701f980ce19f51f6bf3713c52d423f8818a", + "transactionHash": "0xabbe29738b581709ae446a77899c458ff86bad03d31cb77cfc5c45ea98678bef", + "logs": [ + { + "transactionIndex": 8, + "blockNumber": 828245, + "transactionHash": "0xabbe29738b581709ae446a77899c458ff86bad03d31cb77cfc5c45ea98678bef", + "address": "0x27281CE00322Ee8b7C078788Fb624D051F5F7689", + "topics": [ + "0xc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000fd9e6005187f448957a0972a7d0c0a6da2911236" + ], + "data": "0x", + "logIndex": 9, + "blockHash": "0x51b05ded781df237a31332e96e0cc701f980ce19f51f6bf3713c52d423f8818a" + } + ], + "blockNumber": 828245, + "cumulativeGasUsed": "15896948", + "status": 1, + "byzantium": true + }, + "args": [], + "numDeployments": 1, + "solcInputHash": "5518586a577179807341642341c56a4f", + "metadata": "{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"GovernorshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"PendingGovernorshipTransfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"implementation\",\"type\":\"address\"}],\"name\":\"Upgraded\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"governor\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"implementation\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_logic\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_initGovernor\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isGovernor\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newGovernor\",\"type\":\"address\"}],\"name\":\"transferGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newImplementation\",\"type\":\"address\"}],\"name\":\"upgradeTo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"upgradeToAndCall\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"}],\"devdoc\":{\"events\":{\"Upgraded(address)\":{\"details\":\"Emitted when the implementation is upgraded.\",\"params\":{\"implementation\":\"Address of the new implementation.\"}}},\"kind\":\"dev\",\"methods\":{\"admin()\":{\"returns\":{\"_0\":\"The address of the proxy admin/it's also the governor.\"}},\"implementation()\":{\"returns\":{\"_0\":\"The address of the implementation.\"}},\"initialize(address,address,bytes)\":{\"details\":\"Contract initializer with Governor enforcement\",\"params\":{\"_data\":\"Data to send as msg.data to the implementation to initialize the proxied contract. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.\",\"_initGovernor\":\"Address of the initial Governor.\",\"_logic\":\"Address of the initial implementation.\"}},\"transferGovernance(address)\":{\"params\":{\"_newGovernor\":\"Address of the new Governor\"}},\"upgradeTo(address)\":{\"details\":\"Upgrade the backing implementation of the proxy. Only the admin can call this function.\",\"params\":{\"_newImplementation\":\"Address of the new implementation.\"}},\"upgradeToAndCall(address,bytes)\":{\"details\":\"Upgrade the backing implementation of the proxy and call a function on the new implementation. This is useful to initialize the proxied contract.\",\"params\":{\"data\":\"Data to send as msg.data in the low level call. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\",\"newImplementation\":\"Address of the new implementation.\"}}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"claimGovernance()\":{\"notice\":\"Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor.\"},\"governor()\":{\"notice\":\"Returns the address of the current Governor.\"},\"isGovernor()\":{\"notice\":\"Returns true if the caller is the current Governor.\"},\"transferGovernance(address)\":{\"notice\":\"Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete\"}},\"notice\":\"NativeStakingFeeAccumulatorProxy delegates calls to FeeAccumulator implementation\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/proxies/Proxies.sol\":\"NativeStakingFeeAccumulatorProxy\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize, which returns 0 for contracts in\\n // construction, since the code is only stored at the end of the\\n // constructor execution.\\n\\n uint256 size;\\n assembly {\\n size := extcodesize(account)\\n }\\n return size > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCall(target, data, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n require(isContract(target), \\\"Address: static call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(isContract(target), \\\"Address: delegate call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x51b758a8815ecc9596c66c37d56b1d33883a444631a3f916b9fe65cb863ef7c4\",\"license\":\"MIT\"},\"contracts/governance/Governable.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\\n * from owner to governor and renounce methods removed. Does not use\\n * Context.sol like Ownable.sol does for simplification.\\n * @author Origin Protocol Inc\\n */\\nabstract contract Governable {\\n // Storage position of the owner and pendingOwner of the contract\\n // keccak256(\\\"OUSD.governor\\\");\\n bytes32 private constant governorPosition =\\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\\n\\n // keccak256(\\\"OUSD.pending.governor\\\");\\n bytes32 private constant pendingGovernorPosition =\\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\\n\\n // keccak256(\\\"OUSD.reentry.status\\\");\\n bytes32 private constant reentryStatusPosition =\\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\\n\\n // See OpenZeppelin ReentrancyGuard implementation\\n uint256 constant _NOT_ENTERED = 1;\\n uint256 constant _ENTERED = 2;\\n\\n event PendingGovernorshipTransfer(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n event GovernorshipTransferred(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n /**\\n * @notice Returns the address of the current Governor.\\n */\\n function governor() public view returns (address) {\\n return _governor();\\n }\\n\\n /**\\n * @dev Returns the address of the current Governor.\\n */\\n function _governor() internal view returns (address governorOut) {\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n governorOut := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Returns the address of the pending Governor.\\n */\\n function _pendingGovernor()\\n internal\\n view\\n returns (address pendingGovernor)\\n {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n pendingGovernor := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the Governor.\\n */\\n modifier onlyGovernor() {\\n require(isGovernor(), \\\"Caller is not the Governor\\\");\\n _;\\n }\\n\\n /**\\n * @notice Returns true if the caller is the current Governor.\\n */\\n function isGovernor() public view returns (bool) {\\n return msg.sender == _governor();\\n }\\n\\n function _setGovernor(address newGovernor) internal {\\n emit GovernorshipTransferred(_governor(), newGovernor);\\n\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @dev Prevents a contract from calling itself, directly or indirectly.\\n * Calling a `nonReentrant` function from another `nonReentrant`\\n * function is not supported. It is possible to prevent this from happening\\n * by making the `nonReentrant` function external, and make it call a\\n * `private` function that does the actual work.\\n */\\n modifier nonReentrant() {\\n bytes32 position = reentryStatusPosition;\\n uint256 _reentry_status;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n _reentry_status := sload(position)\\n }\\n\\n // On the first call to nonReentrant, _notEntered will be true\\n require(_reentry_status != _ENTERED, \\\"Reentrant call\\\");\\n\\n // Any calls to nonReentrant after this point will fail\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _ENTERED)\\n }\\n\\n _;\\n\\n // By storing the original value once again, a refund is triggered (see\\n // https://eips.ethereum.org/EIPS/eip-2200)\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _NOT_ENTERED)\\n }\\n }\\n\\n function _setPendingGovernor(address newGovernor) internal {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the current Governor. Must be claimed for this to complete\\n * @param _newGovernor Address of the new Governor\\n */\\n function transferGovernance(address _newGovernor) external onlyGovernor {\\n _setPendingGovernor(_newGovernor);\\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\\n }\\n\\n /**\\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the new Governor.\\n */\\n function claimGovernance() external {\\n require(\\n msg.sender == _pendingGovernor(),\\n \\\"Only the pending Governor can complete the claim\\\"\\n );\\n _changeGovernor(msg.sender);\\n }\\n\\n /**\\n * @dev Change Governance of the contract to a new account (`newGovernor`).\\n * @param _newGovernor Address of the new Governor\\n */\\n function _changeGovernor(address _newGovernor) internal {\\n require(_newGovernor != address(0), \\\"New Governor is address(0)\\\");\\n _setGovernor(_newGovernor);\\n }\\n}\\n\",\"keccak256\":\"0xf32f873c8bfbacf2e5f01d0cf37bc7f54fbd5aa656e95c8a599114229946f107\",\"license\":\"BUSL-1.1\"},\"contracts/proxies/InitializeGovernedUpgradeabilityProxy.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { Address } from \\\"@openzeppelin/contracts/utils/Address.sol\\\";\\n\\nimport { Governable } from \\\"../governance/Governable.sol\\\";\\n\\n/**\\n * @title BaseGovernedUpgradeabilityProxy\\n * @dev This contract combines an upgradeability proxy with our governor system.\\n * It is based on an older version of OpenZeppelins BaseUpgradeabilityProxy\\n * with Solidity ^0.8.0.\\n * @author Origin Protocol Inc\\n */\\ncontract InitializeGovernedUpgradeabilityProxy is Governable {\\n /**\\n * @dev Emitted when the implementation is upgraded.\\n * @param implementation Address of the new implementation.\\n */\\n event Upgraded(address indexed implementation);\\n\\n constructor() {\\n _setGovernor(msg.sender);\\n }\\n\\n /**\\n * @dev Contract initializer with Governor enforcement\\n * @param _logic Address of the initial implementation.\\n * @param _initGovernor Address of the initial Governor.\\n * @param _data Data to send as msg.data to the implementation to initialize\\n * the proxied contract.\\n * It should include the signature and the parameters of the function to be\\n * called, as described in\\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\\n * This parameter is optional, if no data is given the initialization call\\n * to proxied contract will be skipped.\\n */\\n function initialize(\\n address _logic,\\n address _initGovernor,\\n bytes calldata _data\\n ) public payable onlyGovernor {\\n require(_implementation() == address(0));\\n require(_logic != address(0), \\\"Implementation not set\\\");\\n assert(\\n IMPLEMENTATION_SLOT ==\\n bytes32(uint256(keccak256(\\\"eip1967.proxy.implementation\\\")) - 1)\\n );\\n _setImplementation(_logic);\\n if (_data.length > 0) {\\n (bool success, ) = _logic.delegatecall(_data);\\n require(success);\\n }\\n _changeGovernor(_initGovernor);\\n }\\n\\n /**\\n * @return The address of the proxy admin/it's also the governor.\\n */\\n function admin() external view returns (address) {\\n return _governor();\\n }\\n\\n /**\\n * @return The address of the implementation.\\n */\\n function implementation() external view returns (address) {\\n return _implementation();\\n }\\n\\n /**\\n * @dev Upgrade the backing implementation of the proxy.\\n * Only the admin can call this function.\\n * @param _newImplementation Address of the new implementation.\\n */\\n function upgradeTo(address _newImplementation) external onlyGovernor {\\n _upgradeTo(_newImplementation);\\n }\\n\\n /**\\n * @dev Upgrade the backing implementation of the proxy and call a function\\n * on the new implementation.\\n * This is useful to initialize the proxied contract.\\n * @param newImplementation Address of the new implementation.\\n * @param data Data to send as msg.data in the low level call.\\n * It should include the signature and the parameters of the function to be called, as described in\\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\\n */\\n function upgradeToAndCall(address newImplementation, bytes calldata data)\\n external\\n payable\\n onlyGovernor\\n {\\n _upgradeTo(newImplementation);\\n (bool success, ) = newImplementation.delegatecall(data);\\n require(success);\\n }\\n\\n /**\\n * @dev Fallback function.\\n * Implemented entirely in `_fallback`.\\n */\\n fallback() external payable {\\n _fallback();\\n }\\n\\n /**\\n * @dev Delegates execution to an implementation contract.\\n * This is a low level function that doesn't return to its internal call site.\\n * It will return to the external caller whatever the implementation returns.\\n * @param _impl Address to delegate.\\n */\\n function _delegate(address _impl) internal {\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n // Copy msg.data. We take full control of memory in this inline assembly\\n // block because it will not return to Solidity code. We overwrite the\\n // Solidity scratch pad at memory position 0.\\n calldatacopy(0, 0, calldatasize())\\n\\n // Call the implementation.\\n // out and outsize are 0 because we don't know the size yet.\\n let result := delegatecall(gas(), _impl, 0, calldatasize(), 0, 0)\\n\\n // Copy the returned data.\\n returndatacopy(0, 0, returndatasize())\\n\\n switch result\\n // delegatecall returns 0 on error.\\n case 0 {\\n revert(0, returndatasize())\\n }\\n default {\\n return(0, returndatasize())\\n }\\n }\\n }\\n\\n /**\\n * @dev Function that is run as the first thing in the fallback function.\\n * Can be redefined in derived contracts to add functionality.\\n * Redefinitions must call super._willFallback().\\n */\\n function _willFallback() internal {}\\n\\n /**\\n * @dev fallback implementation.\\n * Extracted to enable manual triggering.\\n */\\n function _fallback() internal {\\n _willFallback();\\n _delegate(_implementation());\\n }\\n\\n /**\\n * @dev Storage slot with the address of the current implementation.\\n * This is the keccak-256 hash of \\\"eip1967.proxy.implementation\\\" subtracted by 1, and is\\n * validated in the constructor.\\n */\\n bytes32 internal constant IMPLEMENTATION_SLOT =\\n 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\\n\\n /**\\n * @dev Returns the current implementation.\\n * @return impl Address of the current implementation\\n */\\n function _implementation() internal view returns (address impl) {\\n bytes32 slot = IMPLEMENTATION_SLOT;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n impl := sload(slot)\\n }\\n }\\n\\n /**\\n * @dev Upgrades the proxy to a new implementation.\\n * @param newImplementation Address of the new implementation.\\n */\\n function _upgradeTo(address newImplementation) internal {\\n _setImplementation(newImplementation);\\n emit Upgraded(newImplementation);\\n }\\n\\n /**\\n * @dev Sets the implementation address of the proxy.\\n * @param newImplementation Address of the new implementation.\\n */\\n function _setImplementation(address newImplementation) internal {\\n require(\\n Address.isContract(newImplementation),\\n \\\"Cannot set a proxy implementation to a non-contract address\\\"\\n );\\n\\n bytes32 slot = IMPLEMENTATION_SLOT;\\n\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(slot, newImplementation)\\n }\\n }\\n}\\n\",\"keccak256\":\"0x3f46ae39dced6fa90d8b65aa31a0a331438544ec876e2ec961a8e6b22e2e06c7\",\"license\":\"BUSL-1.1\"},\"contracts/proxies/Proxies.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { InitializeGovernedUpgradeabilityProxy } from \\\"./InitializeGovernedUpgradeabilityProxy.sol\\\";\\n\\n/**\\n * @notice OUSDProxy delegates calls to an OUSD implementation\\n */\\ncontract OUSDProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice WrappedOUSDProxy delegates calls to a WrappedOUSD implementation\\n */\\ncontract WrappedOUSDProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice VaultProxy delegates calls to a Vault implementation\\n */\\ncontract VaultProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice CompoundStrategyProxy delegates calls to a CompoundStrategy implementation\\n */\\ncontract CompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice AaveStrategyProxy delegates calls to a AaveStrategy implementation\\n */\\ncontract AaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice ConvexStrategyProxy delegates calls to a ConvexStrategy implementation\\n */\\ncontract ConvexStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice HarvesterProxy delegates calls to a Harvester implementation\\n */\\ncontract HarvesterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice DripperProxy delegates calls to a Dripper implementation\\n */\\ncontract DripperProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice MorphoCompoundStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\\n */\\ncontract MorphoCompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice ConvexOUSDMetaStrategyProxy delegates calls to a ConvexOUSDMetaStrategy implementation\\n */\\ncontract ConvexOUSDMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice MorphoAaveStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\\n */\\ncontract MorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHProxy delegates calls to nowhere for now\\n */\\ncontract OETHProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice WOETHProxy delegates calls to nowhere for now\\n */\\ncontract WOETHProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHVaultProxy delegates calls to a Vault implementation\\n */\\ncontract OETHVaultProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHDripperProxy delegates calls to a OETHDripper implementation\\n */\\ncontract OETHDripperProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHHarvesterProxy delegates calls to a Harvester implementation\\n */\\ncontract OETHHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice CurveEthStrategyProxy delegates calls to a CurveEthStrategy implementation\\n */\\ncontract ConvexEthMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice BuybackProxy delegates calls to Buyback implementation\\n */\\ncontract BuybackProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHMorphoAaveStrategyProxy delegates calls to a MorphoAaveStrategy implementation\\n */\\ncontract OETHMorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHBalancerMetaPoolrEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\\n */\\ncontract OETHBalancerMetaPoolrEthStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice OETHBalancerMetaPoolwstEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\\n */\\ncontract OETHBalancerMetaPoolwstEthStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MakerDsrStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MakerDsrStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHBuybackProxy delegates calls to Buyback implementation\\n */\\ncontract OETHBuybackProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice BridgedWOETHProxy delegates calls to BridgedWOETH implementation\\n */\\ncontract BridgedWOETHProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice NativeStakingSSVStrategyProxy delegates calls to NativeStakingSSVStrategy implementation\\n */\\ncontract NativeStakingSSVStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingFeeAccumulatorProxy delegates calls to FeeAccumulator implementation\\n */\\ncontract NativeStakingFeeAccumulatorProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingSSVStrategy2Proxy delegates calls to NativeStakingSSVStrategy implementation\\n */\\ncontract NativeStakingSSVStrategy2Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingFeeAccumulator2Proxy delegates calls to FeeAccumulator implementation\\n */\\ncontract NativeStakingFeeAccumulator2Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingSSVStrategy3Proxy delegates calls to NativeStakingSSVStrategy implementation\\n */\\ncontract NativeStakingSSVStrategy3Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingFeeAccumulator3Proxy delegates calls to FeeAccumulator implementation\\n */\\ncontract NativeStakingFeeAccumulator3Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MetaMorphoStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MetaMorphoStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice ARMBuybackProxy delegates calls to Buyback implementation\\n */\\ncontract ARMBuybackProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice MorphoGauntletPrimeUSDCStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MorphoGauntletPrimeUSDCStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MorphoGauntletPrimeUSDTStrategyProxy delegates calls to a Generalized4626USDTStrategy implementation\\n */\\ncontract MorphoGauntletPrimeUSDTStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice CurvePoolBoosterProxy delegates calls to a CurvePoolBooster implementation\\n */\\ncontract CurvePoolBoosterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHFixedRateDripperProxy delegates calls to a OETHFixedRateDripper implementation\\n */\\ncontract OETHFixedRateDripperProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHSimpleHarvesterProxy delegates calls to a OETHSimpleHarvester implementation\\n */\\ncontract OETHSimpleHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice PoolBoostCentralRegistryProxy delegates calls to the PoolBoostCentralRegistry implementation\\n */\\ncontract PoolBoostCentralRegistryProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MakerSSRStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MakerSSRStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OUSDCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\\n */\\ncontract OUSDCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\\n */\\ncontract OETHCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice CompoundingStakingSSVStrategyProxy delegates calls to a CompoundingStakingSSVStrategy implementation\\n */\\ncontract CompoundingStakingSSVStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\",\"keccak256\":\"0x71e10cf279337e54682c7b0132825fa307992782367bd296c44b664b7b705db4\",\"license\":\"BUSL-1.1\"}},\"version\":1}", + "bytecode": "0x6080604052348015600f57600080fd5b50601733601b565b6081565b6001600160a01b038116603a6000805160206109f98339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a36000805160206109f983398151915255565b610969806100906000396000f3fe6080604052600436106100865760003560e01c80635d36b190116100595780635d36b1901461010a578063c7af33521461011f578063cf7a1d7714610144578063d38bfff414610157578063f851a4401461009057610086565b80630c340a24146100905780633659cfe6146100c25780634f1ef286146100e25780635c60da1b146100f5575b61008e610177565b005b34801561009c57600080fd5b506100a5610197565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100ce57600080fd5b5061008e6100dd366004610750565b6101b4565b61008e6100f03660046107bb565b6101ed565b34801561010157600080fd5b506100a561028a565b34801561011657600080fd5b5061008e6102a2565b34801561012b57600080fd5b50610134610346565b60405190151581526020016100b9565b61008e61015236600461080e565b610377565b34801561016357600080fd5b5061008e610172366004610750565b6104e0565b6101956101906000805160206108f48339815191525490565b610584565b565b60006101af6000805160206109148339815191525490565b905090565b6101bc610346565b6101e15760405162461bcd60e51b81526004016101d89061086f565b60405180910390fd5b6101ea816105a8565b50565b6101f5610346565b6102115760405162461bcd60e51b81526004016101d89061086f565b61021a836105a8565b6000836001600160a01b031683836040516102369291906108a6565b600060405180830381855af49150503d8060008114610271576040519150601f19603f3d011682016040523d82523d6000602084013e610276565b606091505b505090508061028457600080fd5b50505050565b60006101af6000805160206108f48339815191525490565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b03161461033d5760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b60648201526084016101d8565b610195336105e8565b600061035e6000805160206109148339815191525490565b6001600160a01b0316336001600160a01b031614905090565b61037f610346565b61039b5760405162461bcd60e51b81526004016101d89061086f565b60006103b36000805160206108f48339815191525490565b6001600160a01b0316146103c657600080fd5b6001600160a01b0384166104155760405162461bcd60e51b8152602060048201526016602482015275125b5c1b195b595b9d185d1a5bdb881b9bdd081cd95d60521b60448201526064016101d8565b61044060017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd6108b6565b6000805160206108f48339815191521461045c5761045c6108dd565b61046584610647565b80156104d7576000846001600160a01b031683836040516104879291906108a6565b600060405180830381855af49150503d80600081146104c2576040519150601f19603f3d011682016040523d82523d6000602084013e6104c7565b606091505b50509050806104d557600080fd5b505b610284836105e8565b6104e8610346565b6105045760405162461bcd60e51b81526004016101d89061086f565b61052c817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b031661054c6000805160206109148339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b3660008037600080366000845af43d6000803e8080156105a3573d6000f35b3d6000fd5b6105b181610647565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b03811661063e5760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f72206973206164647265737328302900000000000060448201526064016101d8565b6101ea816106cd565b803b6106bb5760405162461bcd60e51b815260206004820152603b60248201527f43616e6e6f742073657420612070726f787920696d706c656d656e746174696f60448201527f6e20746f2061206e6f6e2d636f6e74726163742061646472657373000000000060648201526084016101d8565b6000805160206108f483398151915255565b806001600160a01b03166106ed6000805160206109148339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a360008051602061091483398151915255565b80356001600160a01b038116811461074b57600080fd5b919050565b60006020828403121561076257600080fd5b61076b82610734565b9392505050565b60008083601f84011261078457600080fd5b50813567ffffffffffffffff81111561079c57600080fd5b6020830191508360208285010111156107b457600080fd5b9250929050565b6000806000604084860312156107d057600080fd5b6107d984610734565b9250602084013567ffffffffffffffff8111156107f557600080fd5b61080186828701610772565b9497909650939450505050565b6000806000806060858703121561082457600080fd5b61082d85610734565b935061083b60208601610734565b9250604085013567ffffffffffffffff81111561085757600080fd5b61086387828801610772565b95989497509550505050565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b8183823760009101908152919050565b818103818111156108d757634e487b7160e01b600052601160045260246000fd5b92915050565b634e487b7160e01b600052600160045260246000fdfe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa26469706673582212209321b6440c8a9d2702cf1683f964031d4ce44fbe22183bb34129b5f3bd59c7a264736f6c634300081c00337bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a", + "deployedBytecode": "0x6080604052600436106100865760003560e01c80635d36b190116100595780635d36b1901461010a578063c7af33521461011f578063cf7a1d7714610144578063d38bfff414610157578063f851a4401461009057610086565b80630c340a24146100905780633659cfe6146100c25780634f1ef286146100e25780635c60da1b146100f5575b61008e610177565b005b34801561009c57600080fd5b506100a5610197565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100ce57600080fd5b5061008e6100dd366004610750565b6101b4565b61008e6100f03660046107bb565b6101ed565b34801561010157600080fd5b506100a561028a565b34801561011657600080fd5b5061008e6102a2565b34801561012b57600080fd5b50610134610346565b60405190151581526020016100b9565b61008e61015236600461080e565b610377565b34801561016357600080fd5b5061008e610172366004610750565b6104e0565b6101956101906000805160206108f48339815191525490565b610584565b565b60006101af6000805160206109148339815191525490565b905090565b6101bc610346565b6101e15760405162461bcd60e51b81526004016101d89061086f565b60405180910390fd5b6101ea816105a8565b50565b6101f5610346565b6102115760405162461bcd60e51b81526004016101d89061086f565b61021a836105a8565b6000836001600160a01b031683836040516102369291906108a6565b600060405180830381855af49150503d8060008114610271576040519150601f19603f3d011682016040523d82523d6000602084013e610276565b606091505b505090508061028457600080fd5b50505050565b60006101af6000805160206108f48339815191525490565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b03161461033d5760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b60648201526084016101d8565b610195336105e8565b600061035e6000805160206109148339815191525490565b6001600160a01b0316336001600160a01b031614905090565b61037f610346565b61039b5760405162461bcd60e51b81526004016101d89061086f565b60006103b36000805160206108f48339815191525490565b6001600160a01b0316146103c657600080fd5b6001600160a01b0384166104155760405162461bcd60e51b8152602060048201526016602482015275125b5c1b195b595b9d185d1a5bdb881b9bdd081cd95d60521b60448201526064016101d8565b61044060017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd6108b6565b6000805160206108f48339815191521461045c5761045c6108dd565b61046584610647565b80156104d7576000846001600160a01b031683836040516104879291906108a6565b600060405180830381855af49150503d80600081146104c2576040519150601f19603f3d011682016040523d82523d6000602084013e6104c7565b606091505b50509050806104d557600080fd5b505b610284836105e8565b6104e8610346565b6105045760405162461bcd60e51b81526004016101d89061086f565b61052c817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b031661054c6000805160206109148339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b3660008037600080366000845af43d6000803e8080156105a3573d6000f35b3d6000fd5b6105b181610647565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b03811661063e5760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f72206973206164647265737328302900000000000060448201526064016101d8565b6101ea816106cd565b803b6106bb5760405162461bcd60e51b815260206004820152603b60248201527f43616e6e6f742073657420612070726f787920696d706c656d656e746174696f60448201527f6e20746f2061206e6f6e2d636f6e74726163742061646472657373000000000060648201526084016101d8565b6000805160206108f483398151915255565b806001600160a01b03166106ed6000805160206109148339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a360008051602061091483398151915255565b80356001600160a01b038116811461074b57600080fd5b919050565b60006020828403121561076257600080fd5b61076b82610734565b9392505050565b60008083601f84011261078457600080fd5b50813567ffffffffffffffff81111561079c57600080fd5b6020830191508360208285010111156107b457600080fd5b9250929050565b6000806000604084860312156107d057600080fd5b6107d984610734565b9250602084013567ffffffffffffffff8111156107f557600080fd5b61080186828701610772565b9497909650939450505050565b6000806000806060858703121561082457600080fd5b61082d85610734565b935061083b60208601610734565b9250604085013567ffffffffffffffff81111561085757600080fd5b61086387828801610772565b95989497509550505050565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b8183823760009101908152919050565b818103818111156108d757634e487b7160e01b600052601160045260246000fd5b92915050565b634e487b7160e01b600052600160045260246000fdfe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa26469706673582212209321b6440c8a9d2702cf1683f964031d4ce44fbe22183bb34129b5f3bd59c7a264736f6c634300081c0033", + "libraries": {}, + "devdoc": { + "events": { + "Upgraded(address)": { + "details": "Emitted when the implementation is upgraded.", + "params": { + "implementation": "Address of the new implementation." + } + } + }, + "kind": "dev", + "methods": { + "admin()": { + "returns": { + "_0": "The address of the proxy admin/it's also the governor." + } + }, + "implementation()": { + "returns": { + "_0": "The address of the implementation." + } + }, + "initialize(address,address,bytes)": { + "details": "Contract initializer with Governor enforcement", + "params": { + "_data": "Data to send as msg.data to the implementation to initialize the proxied contract. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.", + "_initGovernor": "Address of the initial Governor.", + "_logic": "Address of the initial implementation." + } + }, + "transferGovernance(address)": { + "params": { + "_newGovernor": "Address of the new Governor" + } + }, + "upgradeTo(address)": { + "details": "Upgrade the backing implementation of the proxy. Only the admin can call this function.", + "params": { + "_newImplementation": "Address of the new implementation." + } + }, + "upgradeToAndCall(address,bytes)": { + "details": "Upgrade the backing implementation of the proxy and call a function on the new implementation. This is useful to initialize the proxied contract.", + "params": { + "data": "Data to send as msg.data in the low level call. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.", + "newImplementation": "Address of the new implementation." + } + } + }, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "claimGovernance()": { + "notice": "Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor." + }, + "governor()": { + "notice": "Returns the address of the current Governor." + }, + "isGovernor()": { + "notice": "Returns true if the caller is the current Governor." + }, + "transferGovernance(address)": { + "notice": "Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete" + } + }, + "notice": "NativeStakingFeeAccumulatorProxy delegates calls to FeeAccumulator implementation", + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/NativeStakingSSVStrategy.json b/contracts/deployments/hoodi/NativeStakingSSVStrategy.json new file mode 100644 index 0000000000..8d4647adf5 --- /dev/null +++ b/contracts/deployments/hoodi/NativeStakingSSVStrategy.json @@ -0,0 +1,2386 @@ +{ + "address": "0x5323142d68e11508Af72e5AAd1ce4385CB6d3cE0", + "abi": [ + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "platformAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "vaultAddress", + "type": "address" + } + ], + "internalType": "struct InitializableAbstractStrategy.BaseStrategyConfig", + "name": "_baseConfig", + "type": "tuple" + }, + { + "internalType": "address", + "name": "_wethAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "_ssvToken", + "type": "address" + }, + { + "internalType": "address", + "name": "_ssvNetwork", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_maxValidators", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_feeAccumulator", + "type": "address" + }, + { + "internalType": "address", + "name": "_beaconChainDepositContract", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "AccountingConsensusRewards", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "noOfValidators", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "remainingValidators", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "wethSentToVault", + "type": "uint256" + } + ], + "name": "AccountingFullyWithdrawnValidator", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "int256", + "name": "validatorsDelta", + "type": "int256" + }, + { + "indexed": false, + "internalType": "int256", + "name": "consensusRewardsDelta", + "type": "int256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "wethToVault", + "type": "uint256" + } + ], + "name": "AccountingManuallyFixed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "remainingValidators", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "wethSentToVault", + "type": "uint256" + } + ], + "name": "AccountingValidatorSlashed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "consolidationCount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "activeDepositedValidators", + "type": "uint256" + } + ], + "name": "ConsolidationConfirmed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes[]", + "name": "sourcePubKeys", + "type": "bytes[]" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "targetPubKey", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "address", + "name": "targetStakingStrategy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "consolidationCount", + "type": "uint256" + } + ], + "name": "ConsolidationRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_pToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "pubKey", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ETHStaked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "start", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "end", + "type": "uint256" + } + ], + "name": "FuseIntervalUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "GovernorshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_oldHarvesterAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_newHarvesterAddress", + "type": "address" + } + ], + "name": "HarvesterAddressesUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_pToken", + "type": "address" + } + ], + "name": "PTokenAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_pToken", + "type": "address" + } + ], + "name": "PTokenRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "PendingGovernorshipTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "newAddress", + "type": "address" + } + ], + "name": "RegistratorChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "_oldAddresses", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "_newAddresses", + "type": "address[]" + } + ], + "name": "RewardTokenAddressesUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "rewardToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "RewardTokenCollected", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "pubKey", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + } + ], + "name": "SSVValidatorExitCompleted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "pubKey", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + } + ], + "name": "SSVValidatorExitInitiated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "pubKeyHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "pubKey", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + } + ], + "name": "SSVValidatorRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "StakeETHTallyReset", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "StakeETHThresholdChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "newAddress", + "type": "address" + } + ], + "name": "StakingMonitorChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "strategy", + "type": "address" + } + ], + "name": "TargetStrategyAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_pToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "Withdrawal", + "type": "event" + }, + { + "inputs": [], + "name": "BEACON_CHAIN_DEPOSIT_CONTRACT", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "FEE_ACCUMULATOR_ADDRESS", + "outputs": [ + { + "internalType": "address payable", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "FULL_STAKE", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_VALIDATORS", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MIN_FIX_ACCOUNTING_CADENCE", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "SSV_NETWORK", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "SSV_TOKEN", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "VAULT_ADDRESS", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "WETH", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "activeDepositedValidators", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_strategy", + "type": "address" + } + ], + "name": "addTargetStrategy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "assetToPToken", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + } + ], + "name": "checkBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "claimGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "collectRewardTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "confirmConsolidation", + "outputs": [ + { + "internalType": "uint256", + "name": "consolidationCount_", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "consensusRewards", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "consolidationCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "consolidationTargetStrategies", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "consolidationTargetStrategy", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "deposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "depositAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "internalType": "uint256", + "name": "ssvAmount", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "internalType": "struct Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "depositSSV", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "depositedWethAccountedFor", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "doAccounting", + "outputs": [ + { + "internalType": "bool", + "name": "accountingValid", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes[]", + "name": "publicKeys", + "type": "bytes[]" + }, + { + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + } + ], + "name": "exitSsvValidators", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "fuseIntervalEnd", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "fuseIntervalStart", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRewardTokenAddresses", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "governor", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "harvesterAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_rewardTokenAddresses", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "_assets", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "_pTokens", + "type": "address[]" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isGovernor", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastFixAccountingBlockNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int256", + "name": "_validatorsDelta", + "type": "int256" + }, + { + "internalType": "int256", + "name": "_consensusRewardsDelta", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "_ethToVaultAmount", + "type": "uint256" + } + ], + "name": "manuallyFixAccounting", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "platformAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes[]", + "name": "publicKeys", + "type": "bytes[]" + }, + { + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "internalType": "bytes[]", + "name": "sharesData", + "type": "bytes[]" + }, + { + "internalType": "uint256", + "name": "ssvAmount", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "internalType": "struct Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "registerSsvValidators", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_assetIndex", + "type": "uint256" + } + ], + "name": "removePToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes[]", + "name": "publicKeys", + "type": "bytes[]" + }, + { + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "internalType": "struct Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "removeSsvValidators", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes[]", + "name": "sourcePubKeys", + "type": "bytes[]" + }, + { + "internalType": "bytes", + "name": "targetPubKey", + "type": "bytes" + }, + { + "internalType": "address", + "name": "targetStakingStrategy", + "type": "address" + } + ], + "name": "requestConsolidation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "resetStakeETHTally", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "rewardTokenAddresses", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "safeApproveAllTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "setFeeRecipient", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_fuseIntervalStart", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_fuseIntervalEnd", + "type": "uint256" + } + ], + "name": "setFuseInterval", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_harvesterAddress", + "type": "address" + } + ], + "name": "setHarvesterAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "internalType": "address", + "name": "_pToken", + "type": "address" + } + ], + "name": "setPTokenAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "setRegistrator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_rewardTokenAddresses", + "type": "address[]" + } + ], + "name": "setRewardTokenAddresses", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "setStakeETHThreshold", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "setStakingMonitor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "stakeETHTally", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "stakeETHThreshold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes", + "name": "pubkey", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "depositDataRoot", + "type": "bytes32" + } + ], + "internalType": "struct ValidatorStakeData[]", + "name": "validators", + "type": "tuple[]" + } + ], + "name": "stakeEth", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "stakingMonitor", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + } + ], + "name": "supportsAsset", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newGovernor", + "type": "address" + } + ], + "name": "transferGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "transferToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "validatorRegistrator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "validatorsStates", + "outputs": [ + { + "internalType": "enum ValidatorRegistrator.VALIDATOR_STATE", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "vaultAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_recipient", + "type": "address" + }, + { + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "internalType": "uint256", + "name": "ssvAmount", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "internalType": "struct Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "withdrawSSV", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "transactionHash": "0xeb3dfb6fcb974cceca662bd1c479886f7af8d5a8804c6c5c32961e9ec876950b", + "receipt": { + "to": null, + "from": "0xf7749B41db006860cEc0650D18b8013d69C44Eeb", + "contractAddress": "0x5323142d68e11508Af72e5AAd1ce4385CB6d3cE0", + "transactionIndex": 8, + "gasUsed": "5248834", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0xef41a0613af9f430cefc0cbbd250910732705f184da243026d85f6a6c0f53341", + "transactionHash": "0xeb3dfb6fcb974cceca662bd1c479886f7af8d5a8804c6c5c32961e9ec876950b", + "logs": [], + "blockNumber": 914105, + "cumulativeGasUsed": "6840822", + "status": 1, + "byzantium": true + }, + "args": [ + [ + "0x0000000000000000000000000000000000000000", + "0xD0cC28bc8F4666286F3211e465ecF1fe5c72AC8B" + ], + "0x2387fD72C1DA19f6486B843F5da562679FbB4057", + "0x9F5d4Ec84fC4785788aB44F9de973cF34F7A038e", + "0x58410Bef803ECd7E63B23664C586A6DB72DAf59c", + 500, + "0x27281CE00322Ee8b7C078788Fb624D051F5F7689", + "0x00000000219ab540356cBB839Cbe05303d7705Fa" + ], + "numDeployments": 3, + "solcInputHash": "0e4db469ee96659b1c00ca00b9b8648c", + "metadata": "{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"platformAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"vaultAddress\",\"type\":\"address\"}],\"internalType\":\"struct InitializableAbstractStrategy.BaseStrategyConfig\",\"name\":\"_baseConfig\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"_wethAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_ssvToken\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_ssvNetwork\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_maxValidators\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_feeAccumulator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_beaconChainDepositContract\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"AccountingConsensusRewards\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"noOfValidators\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"remainingValidators\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"wethSentToVault\",\"type\":\"uint256\"}],\"name\":\"AccountingFullyWithdrawnValidator\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"validatorsDelta\",\"type\":\"int256\"},{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"consensusRewardsDelta\",\"type\":\"int256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"wethToVault\",\"type\":\"uint256\"}],\"name\":\"AccountingManuallyFixed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"remainingValidators\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"wethSentToVault\",\"type\":\"uint256\"}],\"name\":\"AccountingValidatorSlashed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"consolidationCount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"activeDepositedValidators\",\"type\":\"uint256\"}],\"name\":\"ConsolidationConfirmed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes[]\",\"name\":\"sourcePubKeys\",\"type\":\"bytes[]\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"targetPubKey\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"targetStakingStrategy\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"consolidationCount\",\"type\":\"uint256\"}],\"name\":\"ConsolidationRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_pToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"Deposit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"pubKey\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"ETHStaked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"start\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"end\",\"type\":\"uint256\"}],\"name\":\"FuseIntervalUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"GovernorshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_oldHarvesterAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_newHarvesterAddress\",\"type\":\"address\"}],\"name\":\"HarvesterAddressesUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_pToken\",\"type\":\"address\"}],\"name\":\"PTokenAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_pToken\",\"type\":\"address\"}],\"name\":\"PTokenRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"PendingGovernorshipTransfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newAddress\",\"type\":\"address\"}],\"name\":\"RegistratorChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"_oldAddresses\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"_newAddresses\",\"type\":\"address[]\"}],\"name\":\"RewardTokenAddressesUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"rewardToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"RewardTokenCollected\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"pubKey\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint64[]\",\"name\":\"operatorIds\",\"type\":\"uint64[]\"}],\"name\":\"SSVValidatorExitCompleted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"pubKey\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint64[]\",\"name\":\"operatorIds\",\"type\":\"uint64[]\"}],\"name\":\"SSVValidatorExitInitiated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"pubKeyHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"pubKey\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint64[]\",\"name\":\"operatorIds\",\"type\":\"uint64[]\"}],\"name\":\"SSVValidatorRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"StakeETHTallyReset\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"StakeETHThresholdChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newAddress\",\"type\":\"address\"}],\"name\":\"StakingMonitorChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"strategy\",\"type\":\"address\"}],\"name\":\"TargetStrategyAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_pToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"Withdrawal\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BEACON_CHAIN_DEPOSIT_CONTRACT\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"FEE_ACCUMULATOR_ADDRESS\",\"outputs\":[{\"internalType\":\"address payable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"FULL_STAKE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_VALIDATORS\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MIN_FIX_ACCOUNTING_CADENCE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SSV_NETWORK\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SSV_TOKEN\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"VAULT_ADDRESS\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"WETH\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"activeDepositedValidators\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_strategy\",\"type\":\"address\"}],\"name\":\"addTargetStrategy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"assetToPToken\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"}],\"name\":\"checkBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"collectRewardTokens\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"confirmConsolidation\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"consolidationCount_\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"consensusRewards\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"consolidationCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"consolidationTargetStrategies\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"consolidationTargetStrategy\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"deposit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"depositAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64[]\",\"name\":\"operatorIds\",\"type\":\"uint64[]\"},{\"internalType\":\"uint256\",\"name\":\"ssvAmount\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"validatorCount\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"networkFeeIndex\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"index\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"internalType\":\"struct Cluster\",\"name\":\"cluster\",\"type\":\"tuple\"}],\"name\":\"depositSSV\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"depositedWethAccountedFor\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"doAccounting\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"accountingValid\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes[]\",\"name\":\"publicKeys\",\"type\":\"bytes[]\"},{\"internalType\":\"uint64[]\",\"name\":\"operatorIds\",\"type\":\"uint64[]\"}],\"name\":\"exitSsvValidators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"fuseIntervalEnd\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"fuseIntervalStart\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRewardTokenAddresses\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"governor\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"harvesterAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_rewardTokenAddresses\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"_assets\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"_pTokens\",\"type\":\"address[]\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isGovernor\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastFixAccountingBlockNumber\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"_validatorsDelta\",\"type\":\"int256\"},{\"internalType\":\"int256\",\"name\":\"_consensusRewardsDelta\",\"type\":\"int256\"},{\"internalType\":\"uint256\",\"name\":\"_ethToVaultAmount\",\"type\":\"uint256\"}],\"name\":\"manuallyFixAccounting\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"platformAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes[]\",\"name\":\"publicKeys\",\"type\":\"bytes[]\"},{\"internalType\":\"uint64[]\",\"name\":\"operatorIds\",\"type\":\"uint64[]\"},{\"internalType\":\"bytes[]\",\"name\":\"sharesData\",\"type\":\"bytes[]\"},{\"internalType\":\"uint256\",\"name\":\"ssvAmount\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"validatorCount\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"networkFeeIndex\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"index\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"internalType\":\"struct Cluster\",\"name\":\"cluster\",\"type\":\"tuple\"}],\"name\":\"registerSsvValidators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_assetIndex\",\"type\":\"uint256\"}],\"name\":\"removePToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes[]\",\"name\":\"publicKeys\",\"type\":\"bytes[]\"},{\"internalType\":\"uint64[]\",\"name\":\"operatorIds\",\"type\":\"uint64[]\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"validatorCount\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"networkFeeIndex\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"index\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"internalType\":\"struct Cluster\",\"name\":\"cluster\",\"type\":\"tuple\"}],\"name\":\"removeSsvValidators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes[]\",\"name\":\"sourcePubKeys\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes\",\"name\":\"targetPubKey\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"targetStakingStrategy\",\"type\":\"address\"}],\"name\":\"requestConsolidation\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"resetStakeETHTally\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"rewardTokenAddresses\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"safeApproveAllTokens\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"setFeeRecipient\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_fuseIntervalStart\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_fuseIntervalEnd\",\"type\":\"uint256\"}],\"name\":\"setFuseInterval\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_harvesterAddress\",\"type\":\"address\"}],\"name\":\"setHarvesterAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_pToken\",\"type\":\"address\"}],\"name\":\"setPTokenAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_address\",\"type\":\"address\"}],\"name\":\"setRegistrator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_rewardTokenAddresses\",\"type\":\"address[]\"}],\"name\":\"setRewardTokenAddresses\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"setStakeETHThreshold\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_address\",\"type\":\"address\"}],\"name\":\"setStakingMonitor\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"stakeETHTally\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"stakeETHThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"pubkey\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"},{\"internalType\":\"bytes32\",\"name\":\"depositDataRoot\",\"type\":\"bytes32\"}],\"internalType\":\"struct ValidatorStakeData[]\",\"name\":\"validators\",\"type\":\"tuple[]\"}],\"name\":\"stakeEth\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"stakingMonitor\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"}],\"name\":\"supportsAsset\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newGovernor\",\"type\":\"address\"}],\"name\":\"transferGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"transferToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"validatorRegistrator\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"validatorsStates\",\"outputs\":[{\"internalType\":\"enum ValidatorRegistrator.VALIDATOR_STATE\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"vaultAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_recipient\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"withdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64[]\",\"name\":\"operatorIds\",\"type\":\"uint64[]\"},{\"internalType\":\"uint256\",\"name\":\"ssvAmount\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"validatorCount\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"networkFeeIndex\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"index\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"internalType\":\"struct Cluster\",\"name\":\"cluster\",\"type\":\"tuple\"}],\"name\":\"withdrawSSV\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}],\"devdoc\":{\"author\":\"Origin Protocol Inc\",\"details\":\"This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that is on the contract across multiple blocks (and not just transitory within a transaction) is considered an asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is required since the rewards (reward token) is also in ETH. To simplify the accounting of WETH there is another difference in behavior compared to the other strategies. To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant immediately wraps ETH to WETH and sends it to the Vault. On the other hand any ETH on the contract (across multiple blocks) is there either: - as a result of already accounted for consensus rewards - as a result of not yet accounted for consensus rewards - as a results of not yet accounted for full validator withdrawals (or validator slashes) Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time interval and not immediately.\",\"events\":{\"Paused(address)\":{\"details\":\"Emitted when the pause is triggered by `account`.\"},\"Unpaused(address)\":{\"details\":\"Emitted when the pause is lifted by `account`.\"}},\"kind\":\"dev\",\"methods\":{\"checkBalance(address)\":{\"params\":{\"_asset\":\"Address of weth asset\"},\"returns\":{\"balance\":\" Total value of (W)ETH\"}},\"constructor\":{\"params\":{\"_baseConfig\":\"Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI, and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\",\"_beaconChainDepositContract\":\"Address of the beacon chain deposit contract\",\"_feeAccumulator\":\"Address of the fee accumulator receiving execution layer validator rewards\",\"_maxValidators\":\"Maximum number of validators that can be registered in the strategy\",\"_ssvNetwork\":\"Address of the SSV Network contract\",\"_ssvToken\":\"Address of the Erc20 SSV Token contract\",\"_wethAddress\":\"Address of the Erc20 WETH Token contract\"}},\"deposit(address,uint256)\":{\"params\":{\"_amount\":\"Amount of assets that were transferred to the strategy by the vault.\",\"_asset\":\"Address of asset to deposit. Has to be WETH.\"}},\"depositSSV(uint64[],uint256,(uint32,uint64,uint64,bool,uint256))\":{\"details\":\"A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds. uses \\\"onlyStrategist\\\" modifier so continuous front-running can't DOS our maintenance service that tries to top up SSV tokens.\",\"params\":{\"cluster\":\"The SSV cluster details including the validator count and SSV balance\",\"operatorIds\":\"The operator IDs of the SSV Cluster\",\"ssvAmount\":\"The amount of SSV tokens to be deposited to the SSV cluster\"}},\"doAccounting()\":{\"details\":\"This function could in theory be permission-less but lets allow only the Registrator (Defender Action) to call it for now.\",\"returns\":{\"accountingValid\":\"true if accounting was successful, false if fuse is blown\"}},\"exitSsvValidators(bytes[],uint64[])\":{\"params\":{\"operatorIds\":\"The operator IDs of the SSV Cluster\",\"publicKeys\":\"List of SSV validator public keys\"}},\"getRewardTokenAddresses()\":{\"returns\":{\"_0\":\"address[] the reward token addresses.\"}},\"initialize(address[],address[],address[])\":{\"params\":{\"_assets\":\"Addresses of initial supported assets\",\"_pTokens\":\"Platform Token corresponding addresses\",\"_rewardTokenAddresses\":\"Address of reward token for platform\"}},\"manuallyFixAccounting(int256,int256,uint256)\":{\"details\":\"There is a case when a validator(s) gets slashed so much that the eth swept from the beacon chain enters the fuse area and there are no consensus rewards on the contract to \\\"dip into\\\"/use. To increase the amount of unaccounted ETH over the fuse end interval we need to reduce the amount of active deposited validators and immediately send WETH to the vault, so it doesn't interfere with further accounting.\",\"params\":{\"_consensusRewardsDelta\":\"adjust the accounted for consensus rewards up or down\",\"_ethToVaultAmount\":\"the amount of ETH that gets wrapped into WETH and sent to the Vault\",\"_validatorsDelta\":\"adjust the active validators by up to plus three or minus three\"}},\"paused()\":{\"details\":\"Returns true if the contract is paused, and false otherwise.\"},\"registerSsvValidators(bytes[],uint64[],bytes[],uint256,(uint32,uint64,uint64,bool,uint256))\":{\"params\":{\"cluster\":\"The SSV cluster details including the validator count and SSV balance\",\"operatorIds\":\"The operator IDs of the SSV Cluster\",\"publicKeys\":\"The public keys of the validators\",\"sharesData\":\"The shares data for each validator\",\"ssvAmount\":\"The amount of SSV tokens to be deposited to the SSV cluster\"}},\"removePToken(uint256)\":{\"params\":{\"_assetIndex\":\"Index of the asset to be removed\"}},\"removeSsvValidators(bytes[],uint64[],(uint32,uint64,uint64,bool,uint256))\":{\"params\":{\"cluster\":\"The SSV cluster details including the validator count and SSV balance\",\"operatorIds\":\"The operator IDs of the SSV Cluster\",\"publicKeys\":\"List of SSV validator public keys\"}},\"setHarvesterAddress(address)\":{\"params\":{\"_harvesterAddress\":\"Address of the harvester contract.\"}},\"setPTokenAddress(address,address)\":{\"params\":{\"_asset\":\"Address for the asset\",\"_pToken\":\"Address for the corresponding platform token\"}},\"setRewardTokenAddresses(address[])\":{\"params\":{\"_rewardTokenAddresses\":\"Array of reward token addresses\"}},\"stakeEth((bytes,bytes,bytes32)[])\":{\"params\":{\"validators\":\"A list of validator data needed to stake. The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot. Only the registrator can call this function.\"}},\"supportsAsset(address)\":{\"params\":{\"_asset\":\"The address of the asset token.\"}},\"transferGovernance(address)\":{\"params\":{\"_newGovernor\":\"Address of the new Governor\"}},\"transferToken(address,uint256)\":{\"params\":{\"_amount\":\"Amount of the asset to transfer\",\"_asset\":\"Address for the asset\"}},\"withdraw(address,address,uint256)\":{\"params\":{\"_amount\":\"Amount of WETH to withdraw\",\"_asset\":\"WETH to withdraw\",\"_recipient\":\"Address to receive withdrawn assets\"}},\"withdrawSSV(uint64[],uint256,(uint32,uint64,uint64,bool,uint256))\":{\"details\":\"A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\",\"params\":{\"cluster\":\"The SSV cluster details including the validator count and SSV balance\",\"operatorIds\":\"The operator IDs of the SSV Cluster\",\"ssvAmount\":\"The amount of SSV tokens to be deposited to the SSV cluster\"}}},\"stateVariables\":{\"FEE_ACCUMULATOR_ADDRESS\":{\"details\":\"this address will receive maximal extractable value (MEV) rewards. These are rewards for arranging transactions in a way that benefits the validator.\"},\"depositedWethAccountedFor\":{\"details\":\"This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately deposit it to an underlying platform. Rather a special privilege account stakes it to the validators. For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track of WETH that has already been accounted for. This value represents the amount of WETH balance of this contract that has already been accounted for by the deposit events. It is important to note that this variable is not concerned with WETH that is a result of full/partial withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to be staked.\"}},\"title\":\"Native Staking SSV Strategy\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"BEACON_CHAIN_DEPOSIT_CONTRACT()\":{\"notice\":\"The address of the beacon chain deposit contract\"},\"FEE_ACCUMULATOR_ADDRESS()\":{\"notice\":\"Fee collector address\"},\"FULL_STAKE()\":{\"notice\":\"The maximum amount of ETH that can be staked by a validator\"},\"MAX_VALIDATORS()\":{\"notice\":\"Maximum number of validators that can be registered in this strategy\"},\"MIN_FIX_ACCOUNTING_CADENCE()\":{\"notice\":\"The minimum amount of blocks that need to pass between two calls to manuallyFixAccounting\"},\"SSV_NETWORK()\":{\"notice\":\"The address of the SSV Network contract used to interface with\"},\"SSV_TOKEN()\":{\"notice\":\"SSV ERC20 token that serves as a payment for operating SSV validators\"},\"VAULT_ADDRESS()\":{\"notice\":\"Address of the OETH Vault proxy contract\"},\"WETH()\":{\"notice\":\"The address of the Wrapped ETH (WETH) token contract\"},\"activeDepositedValidators()\":{\"notice\":\"The number of validators that have 32 (!) ETH actively deposited. When a new deposit to a validator happens this number increases, when a validator exit is detected this number decreases.\"},\"addTargetStrategy(address)\":{\"notice\":\"Adds support for a new staking strategy that can be used for consolidation.\"},\"assetToPToken(address)\":{\"notice\":\"asset => pToken (Platform Specific Token Address)\"},\"checkBalance(address)\":{\"notice\":\"Returns the total value of (W)ETH that is staked to the validators and WETH deposits that are still to be staked. This does not include ETH from consensus rewards sitting in this strategy or ETH from MEV rewards in the FeeAccumulator. These rewards are harvested and sent to the Dripper so will eventually be sent to the Vault as WETH.\"},\"claimGovernance()\":{\"notice\":\"Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor.\"},\"collectRewardTokens()\":{\"notice\":\"Collect accumulated reward token and send to Vault.\"},\"consensusRewards()\":{\"notice\":\"Keeps track of the total consensus rewards swept from the beacon chain\"},\"consolidationCount()\":{\"notice\":\"Number of validators currently being consolidated\"},\"consolidationTargetStrategies(address)\":{\"notice\":\"Mapping of support target staking strategies that can be used for consolidation\"},\"deposit(address,uint256)\":{\"notice\":\"Unlike other strategies, this does not deposit assets into the underlying platform. It just checks the asset is WETH and emits the Deposit event. To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used. Will NOT revert if the strategy is paused from an accounting failure.\"},\"depositAll()\":{\"notice\":\"Unlike other strategies, this does not deposit assets into the underlying platform. It just emits the Deposit event. To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used. Will NOT revert if the strategy is paused from an accounting failure.\"},\"depositSSV(uint64[],uint256,(uint32,uint64,uint64,bool,uint256))\":{\"notice\":\"Deposits more SSV Tokens to the SSV Network contract which is used to pay the SSV Operators.\"},\"doAccounting()\":{\"notice\":\"This notion page offers a good explanation of how the accounting functions https://www.notion.so/originprotocol/Limited-simplified-native-staking-accounting-67a217c8420d40678eb943b9da0ee77d In short, after dividing by 32, if the ETH remaining on the contract falls between 0 and fuseIntervalStart, the accounting function will treat that ETH as Beacon chain consensus rewards. On the contrary, if after dividing by 32, the ETH remaining on the contract falls between fuseIntervalEnd and 32, the accounting function will treat that as a validator slashing.Perform the accounting attributing beacon chain ETH to either full or partial withdrawals. Returns true when accounting is valid and fuse isn't \\\"blown\\\". Returns false when fuse is blown.\"},\"exitSsvValidators(bytes[],uint64[])\":{\"notice\":\"Exit validators from the Beacon chain. The staked ETH will eventually swept to this native staking strategy. Only the registrator can call this function.\"},\"fuseIntervalEnd()\":{\"notice\":\"end of fuse interval\"},\"fuseIntervalStart()\":{\"notice\":\"start of fuse interval\"},\"getRewardTokenAddresses()\":{\"notice\":\"Get the reward token addresses.\"},\"governor()\":{\"notice\":\"Returns the address of the current Governor.\"},\"harvesterAddress()\":{\"notice\":\"Address of the Harvester contract allowed to collect reward tokens\"},\"initialize(address[],address[],address[])\":{\"notice\":\"Set up initial internal state including 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract.\"},\"isGovernor()\":{\"notice\":\"Returns true if the caller is the current Governor.\"},\"lastFixAccountingBlockNumber()\":{\"notice\":\"last block number manuallyFixAccounting has been called\"},\"manuallyFixAccounting(int256,int256,uint256)\":{\"notice\":\"Allow the Strategist to fix the accounting of this strategy and unpause.\"},\"platformAddress()\":{\"notice\":\"Address of the underlying platform\"},\"registerSsvValidators(bytes[],uint64[],bytes[],uint256,(uint32,uint64,uint64,bool,uint256))\":{\"notice\":\"Registers a new validator in the SSV Cluster. Only the registrator can call this function.\"},\"removePToken(uint256)\":{\"notice\":\"Remove a supported asset by passing its index. This method can only be called by the system Governor\"},\"removeSsvValidators(bytes[],uint64[],(uint32,uint64,uint64,bool,uint256))\":{\"notice\":\"Remove validators from the SSV Cluster. Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain. If removed before the validator has exited the beacon chain will result in the validator being slashed. Only the registrator can call this function.\"},\"resetStakeETHTally()\":{\"notice\":\"Reset the stakeETHTally\"},\"rewardTokenAddresses(uint256)\":{\"notice\":\"Address of the reward tokens. eg CRV, BAL, CVX, AURA\"},\"safeApproveAllTokens()\":{\"notice\":\"Approves the SSV Network contract to transfer SSV tokens for deposits\"},\"setFeeRecipient()\":{\"notice\":\"Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\"},\"setFuseInterval(uint256,uint256)\":{\"notice\":\"set fuse interval values\"},\"setHarvesterAddress(address)\":{\"notice\":\"Set the Harvester contract that can collect rewards.\"},\"setPTokenAddress(address,address)\":{\"notice\":\"Provide support for asset by passing its pToken address. This method can only be called by the system Governor\"},\"setRegistrator(address)\":{\"notice\":\"Set the address of the registrator which can register, exit and remove validators\"},\"setRewardTokenAddresses(address[])\":{\"notice\":\"Set the reward token addresses. Any old addresses will be overwritten.\"},\"setStakeETHThreshold(uint256)\":{\"notice\":\"Set the amount of ETH that can be staked before staking monitor\"},\"setStakingMonitor(address)\":{\"notice\":\"Set the address of the staking monitor that is allowed to reset stakeETHTally\"},\"stakeETHTally()\":{\"notice\":\"Amount of ETH that has been staked since the `stakingMonitor` last called `resetStakeETHTally`. This can not go above `stakeETHThreshold`.\"},\"stakeETHThreshold()\":{\"notice\":\"Amount of ETH that can be staked before staking on the contract is suspended and the `stakingMonitor` needs to approve further staking by calling `resetStakeETHTally`\"},\"stakeEth((bytes,bytes,bytes32)[])\":{\"notice\":\"Stakes WETH to the node validators\"},\"stakingMonitor()\":{\"notice\":\"The account that is allowed to modify stakeETHThreshold and reset stakeETHTally\"},\"supportsAsset(address)\":{\"notice\":\"Returns bool indicating whether asset is supported by strategy.\"},\"transferGovernance(address)\":{\"notice\":\"Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete\"},\"transferToken(address,uint256)\":{\"notice\":\"Transfer token to governor. Intended for recovering tokens stuck in strategy contracts, i.e. mistaken sends.\"},\"validatorRegistrator()\":{\"notice\":\"Address of the registrator - allowed to register, exit and remove validators\"},\"validatorsStates(bytes32)\":{\"notice\":\"State of the validators keccak256(pubKey) => state\"},\"vaultAddress()\":{\"notice\":\"Address of the OToken vault\"},\"withdraw(address,address,uint256)\":{\"notice\":\"Withdraw WETH from this contract. Used only if some WETH for is lingering on the contract. That can happen when: - after mints if the strategy is the default - time between depositToStrategy and stakeEth - the deposit was not a multiple of 32 WETH - someone sent WETH directly to this contract Will NOT revert if the strategy is paused from an accounting failure.\"},\"withdrawAll()\":{\"notice\":\"transfer all WETH deposits back to the vault. This does not withdraw from the validators. That has to be done separately with the `exitSsvValidator` and `removeSsvValidator` operations. This does not withdraw any execution rewards from the FeeAccumulator or consensus rewards in this strategy. Any ETH in this strategy that was swept from a full validator withdrawal will not be withdrawn. ETH from full validator withdrawals is sent to the Vault using `doAccounting`. Will NOT revert if the strategy is paused from an accounting failure.\"},\"withdrawSSV(uint64[],uint256,(uint32,uint64,uint64,bool,uint256))\":{\"notice\":\"Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\"}},\"notice\":\"Strategy to deploy funds into DVT validators powered by the SSV Network\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol\":\"NativeStakingSSVStrategy\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/security/Pausable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../utils/Context.sol\\\";\\n\\n/**\\n * @dev Contract module which allows children to implement an emergency stop\\n * mechanism that can be triggered by an authorized account.\\n *\\n * This module is used through inheritance. It will make available the\\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\\n * the functions of your contract. Note that they will not be pausable by\\n * simply including this module, only once the modifiers are put in place.\\n */\\nabstract contract Pausable is Context {\\n /**\\n * @dev Emitted when the pause is triggered by `account`.\\n */\\n event Paused(address account);\\n\\n /**\\n * @dev Emitted when the pause is lifted by `account`.\\n */\\n event Unpaused(address account);\\n\\n bool private _paused;\\n\\n /**\\n * @dev Initializes the contract in unpaused state.\\n */\\n constructor() {\\n _paused = false;\\n }\\n\\n /**\\n * @dev Returns true if the contract is paused, and false otherwise.\\n */\\n function paused() public view virtual returns (bool) {\\n return _paused;\\n }\\n\\n /**\\n * @dev Modifier to make a function callable only when the contract is not paused.\\n *\\n * Requirements:\\n *\\n * - The contract must not be paused.\\n */\\n modifier whenNotPaused() {\\n require(!paused(), \\\"Pausable: paused\\\");\\n _;\\n }\\n\\n /**\\n * @dev Modifier to make a function callable only when the contract is paused.\\n *\\n * Requirements:\\n *\\n * - The contract must be paused.\\n */\\n modifier whenPaused() {\\n require(paused(), \\\"Pausable: not paused\\\");\\n _;\\n }\\n\\n /**\\n * @dev Triggers stopped state.\\n *\\n * Requirements:\\n *\\n * - The contract must not be paused.\\n */\\n function _pause() internal virtual whenNotPaused {\\n _paused = true;\\n emit Paused(_msgSender());\\n }\\n\\n /**\\n * @dev Returns to normal state.\\n *\\n * Requirements:\\n *\\n * - The contract must be paused.\\n */\\n function _unpause() internal virtual whenPaused {\\n _paused = false;\\n emit Unpaused(_msgSender());\\n }\\n}\\n\",\"keccak256\":\"0xe68ed7fb8766ed1e888291f881e36b616037f852b37d96877045319ad298ba87\",\"license\":\"MIT\"},\"@openzeppelin/contracts/token/ERC20/IERC20.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Interface of the ERC20 standard as defined in the EIP.\\n */\\ninterface IERC20 {\\n /**\\n * @dev Returns the amount of tokens in existence.\\n */\\n function totalSupply() external view returns (uint256);\\n\\n /**\\n * @dev Returns the amount of tokens owned by `account`.\\n */\\n function balanceOf(address account) external view returns (uint256);\\n\\n /**\\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transfer(address recipient, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Returns the remaining number of tokens that `spender` will be\\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\\n * zero by default.\\n *\\n * This value changes when {approve} or {transferFrom} are called.\\n */\\n function allowance(address owner, address spender) external view returns (uint256);\\n\\n /**\\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\\n * that someone may use both the old and the new allowance by unfortunate\\n * transaction ordering. One possible solution to mitigate this race\\n * condition is to first reduce the spender's allowance to 0 and set the\\n * desired value afterwards:\\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\\n *\\n * Emits an {Approval} event.\\n */\\n function approve(address spender, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\\n * allowance mechanism. `amount` is then deducted from the caller's\\n * allowance.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transferFrom(\\n address sender,\\n address recipient,\\n uint256 amount\\n ) external returns (bool);\\n\\n /**\\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\\n * another (`to`).\\n *\\n * Note that `value` may be zero.\\n */\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n\\n /**\\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n * a call to {approve}. `value` is the new allowance.\\n */\\n event Approval(address indexed owner, address indexed spender, uint256 value);\\n}\\n\",\"keccak256\":\"0x61437cb513a887a1bbad006e7b1c8b414478427d33de47c5600af3c748f108da\",\"license\":\"MIT\"},\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../IERC20.sol\\\";\\nimport \\\"../../../utils/Address.sol\\\";\\n\\n/**\\n * @title SafeERC20\\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\\n * contract returns false). Tokens that return no value (and instead revert or\\n * throw on failure) are also supported, non-reverting calls are assumed to be\\n * successful.\\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\\n */\\nlibrary SafeERC20 {\\n using Address for address;\\n\\n function safeTransfer(\\n IERC20 token,\\n address to,\\n uint256 value\\n ) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\\n }\\n\\n function safeTransferFrom(\\n IERC20 token,\\n address from,\\n address to,\\n uint256 value\\n ) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\\n }\\n\\n /**\\n * @dev Deprecated. This function has issues similar to the ones found in\\n * {IERC20-approve}, and its usage is discouraged.\\n *\\n * Whenever possible, use {safeIncreaseAllowance} and\\n * {safeDecreaseAllowance} instead.\\n */\\n function safeApprove(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n // safeApprove should only be called when setting an initial allowance,\\n // or when resetting it to zero. To increase and decrease it, use\\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\\n require(\\n (value == 0) || (token.allowance(address(this), spender) == 0),\\n \\\"SafeERC20: approve from non-zero to non-zero allowance\\\"\\n );\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\\n }\\n\\n function safeIncreaseAllowance(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n uint256 newAllowance = token.allowance(address(this), spender) + value;\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\\n }\\n\\n function safeDecreaseAllowance(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n unchecked {\\n uint256 oldAllowance = token.allowance(address(this), spender);\\n require(oldAllowance >= value, \\\"SafeERC20: decreased allowance below zero\\\");\\n uint256 newAllowance = oldAllowance - value;\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\\n }\\n }\\n\\n /**\\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\\n * on the return value: the return value is optional (but if data is returned, it must not be false).\\n * @param token The token targeted by the call.\\n * @param data The call data (encoded using abi.encode or one of its variants).\\n */\\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\\n // the target address contains contract code and also asserts for success in the low-level call.\\n\\n bytes memory returndata = address(token).functionCall(data, \\\"SafeERC20: low-level call failed\\\");\\n if (returndata.length > 0) {\\n // Return data is optional\\n require(abi.decode(returndata, (bool)), \\\"SafeERC20: ERC20 operation did not succeed\\\");\\n }\\n }\\n}\\n\",\"keccak256\":\"0xc3d946432c0ddbb1f846a0d3985be71299df331b91d06732152117f62f0be2b5\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize, which returns 0 for contracts in\\n // construction, since the code is only stored at the end of the\\n // constructor execution.\\n\\n uint256 size;\\n assembly {\\n size := extcodesize(account)\\n }\\n return size > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCall(target, data, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n require(isContract(target), \\\"Address: static call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(isContract(target), \\\"Address: delegate call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x51b758a8815ecc9596c66c37d56b1d33883a444631a3f916b9fe65cb863ef7c4\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Context.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Provides information about the current execution context, including the\\n * sender of the transaction and its data. While these are generally available\\n * via msg.sender and msg.data, they should not be accessed in such a direct\\n * manner, since when dealing with meta-transactions the account sending and\\n * paying for execution may not be the actual sender (as far as an application\\n * is concerned).\\n *\\n * This contract is only required for intermediate, library-like contracts.\\n */\\nabstract contract Context {\\n function _msgSender() internal view virtual returns (address) {\\n return msg.sender;\\n }\\n\\n function _msgData() internal view virtual returns (bytes calldata) {\\n return msg.data;\\n }\\n}\\n\",\"keccak256\":\"0xe2e337e6dde9ef6b680e07338c493ebea1b5fd09b43424112868e9cc1706bca7\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/math/Math.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Standard math utilities missing in the Solidity language.\\n */\\nlibrary Math {\\n /**\\n * @dev Returns the largest of two numbers.\\n */\\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a >= b ? a : b;\\n }\\n\\n /**\\n * @dev Returns the smallest of two numbers.\\n */\\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a < b ? a : b;\\n }\\n\\n /**\\n * @dev Returns the average of two numbers. The result is rounded towards\\n * zero.\\n */\\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\\n // (a + b) / 2 can overflow.\\n return (a & b) + (a ^ b) / 2;\\n }\\n\\n /**\\n * @dev Returns the ceiling of the division of two numbers.\\n *\\n * This differs from standard division with `/` in that it rounds up instead\\n * of rounding down.\\n */\\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\\n // (a + b - 1) / b can overflow on addition, so we distribute.\\n return a / b + (a % b == 0 ? 0 : 1);\\n }\\n}\\n\",\"keccak256\":\"0xfaad496c1c944b6259b7dc70b4865eb1775d6402bc0c81b38a0b24d9f525ae37\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/math/SafeCast.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\\n * checks.\\n *\\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\\n * easily result in undesired exploitation or bugs, since developers usually\\n * assume that overflows raise errors. `SafeCast` restores this intuition by\\n * reverting the transaction when such an operation overflows.\\n *\\n * Using this library instead of the unchecked operations eliminates an entire\\n * class of bugs, so it's recommended to use it always.\\n *\\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\\n * all math on `uint256` and `int256` and then downcasting.\\n */\\nlibrary SafeCast {\\n /**\\n * @dev Returns the downcasted uint224 from uint256, reverting on\\n * overflow (when the input is greater than largest uint224).\\n *\\n * Counterpart to Solidity's `uint224` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 224 bits\\n */\\n function toUint224(uint256 value) internal pure returns (uint224) {\\n require(value <= type(uint224).max, \\\"SafeCast: value doesn't fit in 224 bits\\\");\\n return uint224(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint128 from uint256, reverting on\\n * overflow (when the input is greater than largest uint128).\\n *\\n * Counterpart to Solidity's `uint128` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 128 bits\\n */\\n function toUint128(uint256 value) internal pure returns (uint128) {\\n require(value <= type(uint128).max, \\\"SafeCast: value doesn't fit in 128 bits\\\");\\n return uint128(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint96 from uint256, reverting on\\n * overflow (when the input is greater than largest uint96).\\n *\\n * Counterpart to Solidity's `uint96` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 96 bits\\n */\\n function toUint96(uint256 value) internal pure returns (uint96) {\\n require(value <= type(uint96).max, \\\"SafeCast: value doesn't fit in 96 bits\\\");\\n return uint96(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint64 from uint256, reverting on\\n * overflow (when the input is greater than largest uint64).\\n *\\n * Counterpart to Solidity's `uint64` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 64 bits\\n */\\n function toUint64(uint256 value) internal pure returns (uint64) {\\n require(value <= type(uint64).max, \\\"SafeCast: value doesn't fit in 64 bits\\\");\\n return uint64(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint32 from uint256, reverting on\\n * overflow (when the input is greater than largest uint32).\\n *\\n * Counterpart to Solidity's `uint32` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 32 bits\\n */\\n function toUint32(uint256 value) internal pure returns (uint32) {\\n require(value <= type(uint32).max, \\\"SafeCast: value doesn't fit in 32 bits\\\");\\n return uint32(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint16 from uint256, reverting on\\n * overflow (when the input is greater than largest uint16).\\n *\\n * Counterpart to Solidity's `uint16` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 16 bits\\n */\\n function toUint16(uint256 value) internal pure returns (uint16) {\\n require(value <= type(uint16).max, \\\"SafeCast: value doesn't fit in 16 bits\\\");\\n return uint16(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint8 from uint256, reverting on\\n * overflow (when the input is greater than largest uint8).\\n *\\n * Counterpart to Solidity's `uint8` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 8 bits.\\n */\\n function toUint8(uint256 value) internal pure returns (uint8) {\\n require(value <= type(uint8).max, \\\"SafeCast: value doesn't fit in 8 bits\\\");\\n return uint8(value);\\n }\\n\\n /**\\n * @dev Converts a signed int256 into an unsigned uint256.\\n *\\n * Requirements:\\n *\\n * - input must be greater than or equal to 0.\\n */\\n function toUint256(int256 value) internal pure returns (uint256) {\\n require(value >= 0, \\\"SafeCast: value must be positive\\\");\\n return uint256(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int128 from int256, reverting on\\n * overflow (when the input is less than smallest int128 or\\n * greater than largest int128).\\n *\\n * Counterpart to Solidity's `int128` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 128 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt128(int256 value) internal pure returns (int128) {\\n require(value >= type(int128).min && value <= type(int128).max, \\\"SafeCast: value doesn't fit in 128 bits\\\");\\n return int128(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int64 from int256, reverting on\\n * overflow (when the input is less than smallest int64 or\\n * greater than largest int64).\\n *\\n * Counterpart to Solidity's `int64` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 64 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt64(int256 value) internal pure returns (int64) {\\n require(value >= type(int64).min && value <= type(int64).max, \\\"SafeCast: value doesn't fit in 64 bits\\\");\\n return int64(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int32 from int256, reverting on\\n * overflow (when the input is less than smallest int32 or\\n * greater than largest int32).\\n *\\n * Counterpart to Solidity's `int32` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 32 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt32(int256 value) internal pure returns (int32) {\\n require(value >= type(int32).min && value <= type(int32).max, \\\"SafeCast: value doesn't fit in 32 bits\\\");\\n return int32(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int16 from int256, reverting on\\n * overflow (when the input is less than smallest int16 or\\n * greater than largest int16).\\n *\\n * Counterpart to Solidity's `int16` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 16 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt16(int256 value) internal pure returns (int16) {\\n require(value >= type(int16).min && value <= type(int16).max, \\\"SafeCast: value doesn't fit in 16 bits\\\");\\n return int16(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int8 from int256, reverting on\\n * overflow (when the input is less than smallest int8 or\\n * greater than largest int8).\\n *\\n * Counterpart to Solidity's `int8` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 8 bits.\\n *\\n * _Available since v3.1._\\n */\\n function toInt8(int256 value) internal pure returns (int8) {\\n require(value >= type(int8).min && value <= type(int8).max, \\\"SafeCast: value doesn't fit in 8 bits\\\");\\n return int8(value);\\n }\\n\\n /**\\n * @dev Converts an unsigned uint256 into a signed int256.\\n *\\n * Requirements:\\n *\\n * - input must be less than or equal to maxInt256.\\n */\\n function toInt256(uint256 value) internal pure returns (int256) {\\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\\n require(value <= uint256(type(int256).max), \\\"SafeCast: value doesn't fit in an int256\\\");\\n return int256(value);\\n }\\n}\\n\",\"keccak256\":\"0x5c6caab697d302ad7eb59c234a4d2dbc965c1bae87709bd2850060b7695b28c7\",\"license\":\"MIT\"},\"contracts/beacon/BeaconConsolidation.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nlibrary BeaconConsolidation {\\n /// @notice The address the validator consolidation requests are sent\\n /// See https://eips.ethereum.org/EIPS/eip-7251\\n address internal constant CONSOLIDATION_REQUEST_ADDRESS =\\n 0x0000BBdDc7CE488642fb579F8B00f3a590007251;\\n\\n function request(bytes calldata source, bytes calldata target)\\n internal\\n returns (uint256 fee_)\\n {\\n require(source.length == 48, \\\"Invalid source byte length\\\");\\n require(target.length == 48, \\\"Invalid target byte length\\\");\\n\\n fee_ = fee();\\n\\n // Call the Consolidation Request contract with the public keys of the source and target\\n // validators packed together.\\n // This does not have a function signature, so we use a call\\n (bool success, ) = CONSOLIDATION_REQUEST_ADDRESS.call{ value: fee_ }(\\n abi.encodePacked(source, target)\\n );\\n\\n require(success, \\\"Consolidation request failed\\\");\\n }\\n\\n function fee() internal view returns (uint256) {\\n // Get fee from the consolidation request contract\\n (bool success, bytes memory result) = CONSOLIDATION_REQUEST_ADDRESS\\n .staticcall(\\\"\\\");\\n\\n require(success && result.length > 0, \\\"Failed to get fee\\\");\\n return abi.decode(result, (uint256));\\n }\\n}\\n\",\"keccak256\":\"0xc5f6f6478f4088dae2d1f08ae65941b7925fc098270b1c63e72b25718be2f023\",\"license\":\"BUSL-1.1\"},\"contracts/governance/Governable.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\\n * from owner to governor and renounce methods removed. Does not use\\n * Context.sol like Ownable.sol does for simplification.\\n * @author Origin Protocol Inc\\n */\\nabstract contract Governable {\\n // Storage position of the owner and pendingOwner of the contract\\n // keccak256(\\\"OUSD.governor\\\");\\n bytes32 private constant governorPosition =\\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\\n\\n // keccak256(\\\"OUSD.pending.governor\\\");\\n bytes32 private constant pendingGovernorPosition =\\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\\n\\n // keccak256(\\\"OUSD.reentry.status\\\");\\n bytes32 private constant reentryStatusPosition =\\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\\n\\n // See OpenZeppelin ReentrancyGuard implementation\\n uint256 constant _NOT_ENTERED = 1;\\n uint256 constant _ENTERED = 2;\\n\\n event PendingGovernorshipTransfer(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n event GovernorshipTransferred(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n /**\\n * @notice Returns the address of the current Governor.\\n */\\n function governor() public view returns (address) {\\n return _governor();\\n }\\n\\n /**\\n * @dev Returns the address of the current Governor.\\n */\\n function _governor() internal view returns (address governorOut) {\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n governorOut := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Returns the address of the pending Governor.\\n */\\n function _pendingGovernor()\\n internal\\n view\\n returns (address pendingGovernor)\\n {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n pendingGovernor := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the Governor.\\n */\\n modifier onlyGovernor() {\\n require(isGovernor(), \\\"Caller is not the Governor\\\");\\n _;\\n }\\n\\n /**\\n * @notice Returns true if the caller is the current Governor.\\n */\\n function isGovernor() public view returns (bool) {\\n return msg.sender == _governor();\\n }\\n\\n function _setGovernor(address newGovernor) internal {\\n emit GovernorshipTransferred(_governor(), newGovernor);\\n\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @dev Prevents a contract from calling itself, directly or indirectly.\\n * Calling a `nonReentrant` function from another `nonReentrant`\\n * function is not supported. It is possible to prevent this from happening\\n * by making the `nonReentrant` function external, and make it call a\\n * `private` function that does the actual work.\\n */\\n modifier nonReentrant() {\\n bytes32 position = reentryStatusPosition;\\n uint256 _reentry_status;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n _reentry_status := sload(position)\\n }\\n\\n // On the first call to nonReentrant, _notEntered will be true\\n require(_reentry_status != _ENTERED, \\\"Reentrant call\\\");\\n\\n // Any calls to nonReentrant after this point will fail\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _ENTERED)\\n }\\n\\n _;\\n\\n // By storing the original value once again, a refund is triggered (see\\n // https://eips.ethereum.org/EIPS/eip-2200)\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _NOT_ENTERED)\\n }\\n }\\n\\n function _setPendingGovernor(address newGovernor) internal {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the current Governor. Must be claimed for this to complete\\n * @param _newGovernor Address of the new Governor\\n */\\n function transferGovernance(address _newGovernor) external onlyGovernor {\\n _setPendingGovernor(_newGovernor);\\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\\n }\\n\\n /**\\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the new Governor.\\n */\\n function claimGovernance() external {\\n require(\\n msg.sender == _pendingGovernor(),\\n \\\"Only the pending Governor can complete the claim\\\"\\n );\\n _changeGovernor(msg.sender);\\n }\\n\\n /**\\n * @dev Change Governance of the contract to a new account (`newGovernor`).\\n * @param _newGovernor Address of the new Governor\\n */\\n function _changeGovernor(address _newGovernor) internal {\\n require(_newGovernor != address(0), \\\"New Governor is address(0)\\\");\\n _setGovernor(_newGovernor);\\n }\\n}\\n\",\"keccak256\":\"0xf32f873c8bfbacf2e5f01d0cf37bc7f54fbd5aa656e95c8a599114229946f107\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IBasicToken.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\ninterface IBasicToken {\\n function symbol() external view returns (string memory);\\n\\n function decimals() external view returns (uint8);\\n}\\n\",\"keccak256\":\"0xa562062698aa12572123b36dfd2072f1a39e44fed2031cc19c2c9fd522f96ec2\",\"license\":\"MIT\"},\"contracts/interfaces/IConsolidation.sol\":{\"content\":\"// SPDX-License-Identifier: UNLICENSED\\npragma solidity ^0.8.4;\\n\\ninterface IConsolidationSource {\\n function confirmConsolidation()\\n external\\n returns (uint256 consolidationCount);\\n}\\n\\ninterface IConsolidationTarget {\\n function requestConsolidation(\\n bytes32 lastSourcePubKeyHash,\\n bytes32 targetPubKeyHash\\n ) external;\\n}\\n\",\"keccak256\":\"0xcaedcc196d95b91eff3c08b368a0fdeabfeb85be9532ff41803b6dff5c89d969\",\"license\":\"UNLICENSED\"},\"contracts/interfaces/IDepositContract.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\ninterface IDepositContract {\\n /// @notice A processed deposit event.\\n event DepositEvent(\\n bytes pubkey,\\n bytes withdrawal_credentials,\\n bytes amount,\\n bytes signature,\\n bytes index\\n );\\n\\n /// @notice Submit a Phase 0 DepositData object.\\n /// @param pubkey A BLS12-381 public key.\\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\\n /// @param signature A BLS12-381 signature.\\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\\n /// Used as a protection against malformed input.\\n function deposit(\\n bytes calldata pubkey,\\n bytes calldata withdrawal_credentials,\\n bytes calldata signature,\\n bytes32 deposit_data_root\\n ) external payable;\\n\\n /// @notice Query the current deposit root hash.\\n /// @return The deposit root hash.\\n function get_deposit_root() external view returns (bytes32);\\n\\n /// @notice Query the current deposit count.\\n /// @return The deposit count encoded as a little endian 64-bit number.\\n function get_deposit_count() external view returns (bytes memory);\\n}\\n\",\"keccak256\":\"0x598f90bdbc854250bbd5991426bfb43367207e64e33109c41aa8b54323fd8d8e\",\"license\":\"MIT\"},\"contracts/interfaces/ISSVNetwork.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\nstruct Cluster {\\n uint32 validatorCount;\\n uint64 networkFeeIndex;\\n uint64 index;\\n bool active;\\n uint256 balance;\\n}\\n\\ninterface ISSVNetwork {\\n /**********/\\n /* Errors */\\n /**********/\\n\\n error CallerNotOwner(); // 0x5cd83192\\n error CallerNotWhitelisted(); // 0x8c6e5d71\\n error FeeTooLow(); // 0x732f9413\\n error FeeExceedsIncreaseLimit(); // 0x958065d9\\n error NoFeeDeclared(); // 0x1d226c30\\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\\n error OperatorDoesNotExist(); // 0x961e3e8c\\n error InsufficientBalance(); // 0xf4d678b8\\n error ValidatorDoesNotExist(); // 0xe51315d2\\n error ClusterNotLiquidatable(); // 0x60300a8d\\n error InvalidPublicKeyLength(); // 0x637297a4\\n error InvalidOperatorIdsLength(); // 0x38186224\\n error ClusterAlreadyEnabled(); // 0x3babafd2\\n error ClusterIsLiquidated(); // 0x95a0cf33\\n error ClusterDoesNotExists(); // 0x185e2b16\\n error IncorrectClusterState(); // 0x12e04c87\\n error UnsortedOperatorsList(); // 0xdd020e25\\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\\n error ExceedValidatorLimit(); // 0x6df5ab76\\n error TokenTransferFailed(); // 0x045c4b02\\n error SameFeeChangeNotAllowed(); // 0xc81272f8\\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\\n error NotAuthorized(); // 0xea8e4eb5\\n error OperatorsListNotUnique(); // 0xa5a1ff5d\\n error OperatorAlreadyExists(); // 0x289c9494\\n error TargetModuleDoesNotExist(); // 0x8f9195fb\\n error MaxValueExceeded(); // 0x91aa3017\\n error FeeTooHigh(); // 0xcd4e6167\\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\\n error EmptyPublicKeysList(); // df83e679\\n\\n // legacy errors\\n error ValidatorAlreadyExists(); // 0x8d09a73e\\n error IncorrectValidatorState(); // 0x2feda3c1\\n\\n event AdminChanged(address previousAdmin, address newAdmin);\\n event BeaconUpgraded(address indexed beacon);\\n event ClusterDeposited(\\n address indexed owner,\\n uint64[] operatorIds,\\n uint256 value,\\n Cluster cluster\\n );\\n event ClusterLiquidated(\\n address indexed owner,\\n uint64[] operatorIds,\\n Cluster cluster\\n );\\n event ClusterReactivated(\\n address indexed owner,\\n uint64[] operatorIds,\\n Cluster cluster\\n );\\n event ClusterWithdrawn(\\n address indexed owner,\\n uint64[] operatorIds,\\n uint256 value,\\n Cluster cluster\\n );\\n event DeclareOperatorFeePeriodUpdated(uint64 value);\\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\\n event FeeRecipientAddressUpdated(\\n address indexed owner,\\n address recipientAddress\\n );\\n event Initialized(uint8 version);\\n event LiquidationThresholdPeriodUpdated(uint64 value);\\n event MinimumLiquidationCollateralUpdated(uint256 value);\\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\\n event OperatorAdded(\\n uint64 indexed operatorId,\\n address indexed owner,\\n bytes publicKey,\\n uint256 fee\\n );\\n event OperatorFeeDeclarationCancelled(\\n address indexed owner,\\n uint64 indexed operatorId\\n );\\n event OperatorFeeDeclared(\\n address indexed owner,\\n uint64 indexed operatorId,\\n uint256 blockNumber,\\n uint256 fee\\n );\\n event OperatorFeeExecuted(\\n address indexed owner,\\n uint64 indexed operatorId,\\n uint256 blockNumber,\\n uint256 fee\\n );\\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\\n event OperatorMaximumFeeUpdated(uint64 maxFee);\\n event OperatorRemoved(uint64 indexed operatorId);\\n event OperatorWhitelistUpdated(\\n uint64 indexed operatorId,\\n address whitelisted\\n );\\n event OperatorWithdrawn(\\n address indexed owner,\\n uint64 indexed operatorId,\\n uint256 value\\n );\\n event OwnershipTransferStarted(\\n address indexed previousOwner,\\n address indexed newOwner\\n );\\n event OwnershipTransferred(\\n address indexed previousOwner,\\n address indexed newOwner\\n );\\n event Upgraded(address indexed implementation);\\n event ValidatorAdded(\\n address indexed owner,\\n uint64[] operatorIds,\\n bytes publicKey,\\n bytes shares,\\n Cluster cluster\\n );\\n event ValidatorExited(\\n address indexed owner,\\n uint64[] operatorIds,\\n bytes publicKey\\n );\\n event ValidatorRemoved(\\n address indexed owner,\\n uint64[] operatorIds,\\n bytes publicKey,\\n Cluster cluster\\n );\\n\\n fallback() external;\\n\\n function acceptOwnership() external;\\n\\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\\n\\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\\n\\n function deposit(\\n address clusterOwner,\\n uint64[] memory operatorIds,\\n uint256 amount,\\n Cluster memory cluster\\n ) external;\\n\\n function executeOperatorFee(uint64 operatorId) external;\\n\\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\\n external;\\n\\n function bulkExitValidator(\\n bytes[] calldata publicKeys,\\n uint64[] calldata operatorIds\\n ) external;\\n\\n function getVersion() external pure returns (string memory version);\\n\\n function initialize(\\n address token_,\\n address ssvOperators_,\\n address ssvClusters_,\\n address ssvDAO_,\\n address ssvViews_,\\n uint64 minimumBlocksBeforeLiquidation_,\\n uint256 minimumLiquidationCollateral_,\\n uint32 validatorsPerOperatorLimit_,\\n uint64 declareOperatorFeePeriod_,\\n uint64 executeOperatorFeePeriod_,\\n uint64 operatorMaxFeeIncrease_\\n ) external;\\n\\n function liquidate(\\n address clusterOwner,\\n uint64[] memory operatorIds,\\n Cluster memory cluster\\n ) external;\\n\\n function owner() external view returns (address);\\n\\n function pendingOwner() external view returns (address);\\n\\n function proxiableUUID() external view returns (bytes32);\\n\\n function reactivate(\\n uint64[] memory operatorIds,\\n uint256 amount,\\n Cluster memory cluster\\n ) external;\\n\\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\\n\\n function registerOperator(bytes memory publicKey, uint256 fee)\\n external\\n returns (uint64 id);\\n\\n function registerValidator(\\n bytes memory publicKey,\\n uint64[] memory operatorIds,\\n bytes memory sharesData,\\n uint256 amount,\\n Cluster memory cluster\\n ) external;\\n\\n function bulkRegisterValidator(\\n bytes[] calldata publicKeys,\\n uint64[] calldata operatorIds,\\n bytes[] calldata sharesData,\\n uint256 amount,\\n Cluster memory cluster\\n ) external;\\n\\n function removeOperator(uint64 operatorId) external;\\n\\n function removeValidator(\\n bytes memory publicKey,\\n uint64[] memory operatorIds,\\n Cluster memory cluster\\n ) external;\\n\\n function bulkRemoveValidator(\\n bytes[] calldata publicKeys,\\n uint64[] calldata operatorIds,\\n Cluster memory cluster\\n ) external;\\n\\n function renounceOwnership() external;\\n\\n function setFeeRecipientAddress(address recipientAddress) external;\\n\\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\\n external;\\n\\n function transferOwnership(address newOwner) external;\\n\\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\\n\\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\\n\\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\\n\\n function updateMaximumOperatorFee(uint64 maxFee) external;\\n\\n function updateMinimumLiquidationCollateral(uint256 amount) external;\\n\\n function updateModule(uint8 moduleId, address moduleAddress) external;\\n\\n function updateNetworkFee(uint256 fee) external;\\n\\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\\n\\n function upgradeTo(address newImplementation) external;\\n\\n function upgradeToAndCall(address newImplementation, bytes memory data)\\n external\\n payable;\\n\\n function withdraw(\\n uint64[] memory operatorIds,\\n uint256 amount,\\n Cluster memory cluster\\n ) external;\\n\\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\\n\\n function withdrawNetworkEarnings(uint256 amount) external;\\n\\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\\n external;\\n}\\n\",\"keccak256\":\"0xbd86cb74702aebc5b53c8fc738a2e3ad1b410583460617be84b22ce922af12a7\",\"license\":\"MIT\"},\"contracts/interfaces/IStrategy.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\\n */\\ninterface IStrategy {\\n /**\\n * @dev Deposit the given asset to platform\\n * @param _asset asset address\\n * @param _amount Amount to deposit\\n */\\n function deposit(address _asset, uint256 _amount) external;\\n\\n /**\\n * @dev Deposit the entire balance of all supported assets in the Strategy\\n * to the platform\\n */\\n function depositAll() external;\\n\\n /**\\n * @dev Withdraw given asset from Lending platform\\n */\\n function withdraw(\\n address _recipient,\\n address _asset,\\n uint256 _amount\\n ) external;\\n\\n /**\\n * @dev Liquidate all assets in strategy and return them to Vault.\\n */\\n function withdrawAll() external;\\n\\n /**\\n * @dev Returns the current balance of the given asset.\\n */\\n function checkBalance(address _asset)\\n external\\n view\\n returns (uint256 balance);\\n\\n /**\\n * @dev Returns bool indicating whether strategy supports asset.\\n */\\n function supportsAsset(address _asset) external view returns (bool);\\n\\n /**\\n * @dev Collect reward tokens from the Strategy.\\n */\\n function collectRewardTokens() external;\\n\\n /**\\n * @dev The address array of the reward tokens for the Strategy.\\n */\\n function getRewardTokenAddresses() external view returns (address[] memory);\\n\\n function harvesterAddress() external view returns (address);\\n\\n function transferToken(address token, uint256 amount) external;\\n\\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\\n external;\\n}\\n\",\"keccak256\":\"0x79ca47defb3b5a56bba13f14c440838152fd1c1aa640476154516a16da4da8ba\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IVault.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { VaultStorage } from \\\"../vault/VaultStorage.sol\\\";\\n\\ninterface IVault {\\n // slither-disable-start constable-states\\n\\n event AssetSupported(address _asset);\\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\\n event StrategyApproved(address _addr);\\n event StrategyRemoved(address _addr);\\n event Mint(address _addr, uint256 _value);\\n event Redeem(address _addr, uint256 _value);\\n event CapitalPaused();\\n event CapitalUnpaused();\\n event RebasePaused();\\n event RebaseUnpaused();\\n event VaultBufferUpdated(uint256 _vaultBuffer);\\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\\n event PriceProviderUpdated(address _priceProvider);\\n event AllocateThresholdUpdated(uint256 _threshold);\\n event RebaseThresholdUpdated(uint256 _threshold);\\n event StrategistUpdated(address _address);\\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\\n event TrusteeFeeBpsChanged(uint256 _basis);\\n event TrusteeAddressChanged(address _address);\\n event SwapperChanged(address _address);\\n event SwapAllowedUndervalueChanged(uint256 _basis);\\n event SwapSlippageChanged(address _asset, uint256 _basis);\\n event Swapped(\\n address indexed _fromAsset,\\n address indexed _toAsset,\\n uint256 _fromAssetAmount,\\n uint256 _toAssetAmount\\n );\\n event StrategyAddedToMintWhitelist(address indexed strategy);\\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\\n event DripperChanged(address indexed _dripper);\\n event WithdrawalRequested(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount,\\n uint256 _queued\\n );\\n event WithdrawalClaimed(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount\\n );\\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\\n\\n // Governable.sol\\n function transferGovernance(address _newGovernor) external;\\n\\n function claimGovernance() external;\\n\\n function governor() external view returns (address);\\n\\n function ADMIN_IMPLEMENTATION() external view returns (address);\\n\\n // VaultAdmin.sol\\n function setPriceProvider(address _priceProvider) external;\\n\\n function priceProvider() external view returns (address);\\n\\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\\n\\n function redeemFeeBps() external view returns (uint256);\\n\\n function setVaultBuffer(uint256 _vaultBuffer) external;\\n\\n function vaultBuffer() external view returns (uint256);\\n\\n function setAutoAllocateThreshold(uint256 _threshold) external;\\n\\n function autoAllocateThreshold() external view returns (uint256);\\n\\n function setRebaseThreshold(uint256 _threshold) external;\\n\\n function rebaseThreshold() external view returns (uint256);\\n\\n function setStrategistAddr(address _address) external;\\n\\n function strategistAddr() external view returns (address);\\n\\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\\n\\n function maxSupplyDiff() external view returns (uint256);\\n\\n function setTrusteeAddress(address _address) external;\\n\\n function trusteeAddress() external view returns (address);\\n\\n function setTrusteeFeeBps(uint256 _basis) external;\\n\\n function trusteeFeeBps() external view returns (uint256);\\n\\n function ousdMetaStrategy() external view returns (address);\\n\\n function setSwapper(address _swapperAddr) external;\\n\\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\\n\\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\\n external;\\n\\n function supportAsset(address _asset, uint8 _unitConversion) external;\\n\\n function approveStrategy(address _addr) external;\\n\\n function removeStrategy(address _addr) external;\\n\\n function setAssetDefaultStrategy(address _asset, address _strategy)\\n external;\\n\\n function assetDefaultStrategies(address _asset)\\n external\\n view\\n returns (address);\\n\\n function pauseRebase() external;\\n\\n function unpauseRebase() external;\\n\\n function rebasePaused() external view returns (bool);\\n\\n function pauseCapital() external;\\n\\n function unpauseCapital() external;\\n\\n function capitalPaused() external view returns (bool);\\n\\n function transferToken(address _asset, uint256 _amount) external;\\n\\n function priceUnitMint(address asset) external view returns (uint256);\\n\\n function priceUnitRedeem(address asset) external view returns (uint256);\\n\\n function withdrawAllFromStrategy(address _strategyAddr) external;\\n\\n function withdrawAllFromStrategies() external;\\n\\n function withdrawFromStrategy(\\n address _strategyFromAddress,\\n address[] calldata _assets,\\n uint256[] calldata _amounts\\n ) external;\\n\\n function depositToStrategy(\\n address _strategyToAddress,\\n address[] calldata _assets,\\n uint256[] calldata _amounts\\n ) external;\\n\\n // VaultCore.sol\\n function mint(\\n address _asset,\\n uint256 _amount,\\n uint256 _minimumOusdAmount\\n ) external;\\n\\n function mintForStrategy(uint256 _amount) external;\\n\\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\\n\\n function burnForStrategy(uint256 _amount) external;\\n\\n function allocate() external;\\n\\n function rebase() external;\\n\\n function swapCollateral(\\n address fromAsset,\\n address toAsset,\\n uint256 fromAssetAmount,\\n uint256 minToAssetAmount,\\n bytes calldata data\\n ) external returns (uint256 toAssetAmount);\\n\\n function totalValue() external view returns (uint256 value);\\n\\n function checkBalance(address _asset) external view returns (uint256);\\n\\n function calculateRedeemOutputs(uint256 _amount)\\n external\\n view\\n returns (uint256[] memory);\\n\\n function getAssetCount() external view returns (uint256);\\n\\n function getAssetConfig(address _asset)\\n external\\n view\\n returns (VaultStorage.Asset memory config);\\n\\n function getAllAssets() external view returns (address[] memory);\\n\\n function getStrategyCount() external view returns (uint256);\\n\\n function swapper() external view returns (address);\\n\\n function allowedSwapUndervalue() external view returns (uint256);\\n\\n function getAllStrategies() external view returns (address[] memory);\\n\\n function isSupportedAsset(address _asset) external view returns (bool);\\n\\n function netOusdMintForStrategyThreshold() external view returns (uint256);\\n\\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\\n\\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\\n\\n function netOusdMintedForStrategy() external view returns (int256);\\n\\n function setDripper(address _dripper) external;\\n\\n function dripper() external view returns (address);\\n\\n function weth() external view returns (address);\\n\\n function cacheWETHAssetIndex() external;\\n\\n function wethAssetIndex() external view returns (uint256);\\n\\n function initialize(address, address) external;\\n\\n function setAdminImpl(address) external;\\n\\n function removeAsset(address _asset) external;\\n\\n // These are OETH specific functions\\n function addWithdrawalQueueLiquidity() external;\\n\\n function requestWithdrawal(uint256 _amount)\\n external\\n returns (uint256 requestId, uint256 queued);\\n\\n function claimWithdrawal(uint256 requestId)\\n external\\n returns (uint256 amount);\\n\\n function claimWithdrawals(uint256[] memory requestIds)\\n external\\n returns (uint256[] memory amounts, uint256 totalAmount);\\n\\n function withdrawalQueueMetadata()\\n external\\n view\\n returns (VaultStorage.WithdrawalQueueMetadata memory);\\n\\n function withdrawalRequests(uint256 requestId)\\n external\\n view\\n returns (VaultStorage.WithdrawalRequest memory);\\n\\n // OETHb specific functions\\n function addStrategyToMintWhitelist(address strategyAddr) external;\\n\\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\\n\\n function isMintWhitelistedStrategy(address strategyAddr)\\n external\\n view\\n returns (bool);\\n\\n function withdrawalClaimDelay() external view returns (uint256);\\n\\n function setWithdrawalClaimDelay(uint256 newDelay) external;\\n\\n function lastRebase() external view returns (uint64);\\n\\n function dripDuration() external view returns (uint64);\\n\\n function setDripDuration(uint256 _dripDuration) external;\\n\\n function rebasePerSecondMax() external view returns (uint64);\\n\\n function setRebaseRateMax(uint256 yearlyApr) external;\\n\\n function rebasePerSecondTarget() external view returns (uint64);\\n\\n function previewYield() external view returns (uint256 yield);\\n\\n // slither-disable-end constable-states\\n}\\n\",\"keccak256\":\"0x8d0a60f594d97578b0513b4da3d8fcafaa601950c6c4c016bf60b1344733269c\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IWETH9.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\ninterface IWETH9 {\\n event Approval(address indexed src, address indexed guy, uint256 wad);\\n event Deposit(address indexed dst, uint256 wad);\\n event Transfer(address indexed src, address indexed dst, uint256 wad);\\n event Withdrawal(address indexed src, uint256 wad);\\n\\n function allowance(address, address) external view returns (uint256);\\n\\n function approve(address guy, uint256 wad) external returns (bool);\\n\\n function balanceOf(address) external view returns (uint256);\\n\\n function decimals() external view returns (uint8);\\n\\n function deposit() external payable;\\n\\n function name() external view returns (string memory);\\n\\n function symbol() external view returns (string memory);\\n\\n function totalSupply() external view returns (uint256);\\n\\n function transfer(address dst, uint256 wad) external returns (bool);\\n\\n function transferFrom(\\n address src,\\n address dst,\\n uint256 wad\\n ) external returns (bool);\\n\\n function withdraw(uint256 wad) external;\\n}\\n\",\"keccak256\":\"0x05b7dce6c24d3cd4e48b5c6346d86e5e40ecc3291bcdf3f3ef091c98fc826519\",\"license\":\"MIT\"},\"contracts/strategies/NativeStaking/FeeAccumulator.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { Address } from \\\"@openzeppelin/contracts/utils/Address.sol\\\";\\n\\n/**\\n * @title Fee Accumulator for Native Staking SSV Strategy\\n * @notice Receives execution rewards which includes tx fees and\\n * MEV rewards like tx priority and tx ordering.\\n * It does NOT include swept ETH from beacon chain consensus rewards or full validator withdrawals.\\n * @author Origin Protocol Inc\\n */\\ncontract FeeAccumulator {\\n /// @notice The address of the Native Staking Strategy\\n address public immutable STRATEGY;\\n\\n event ExecutionRewardsCollected(address indexed strategy, uint256 amount);\\n\\n /**\\n * @param _strategy Address of the Native Staking Strategy\\n */\\n constructor(address _strategy) {\\n STRATEGY = _strategy;\\n }\\n\\n /**\\n * @notice sends all ETH in this FeeAccumulator contract to the Native Staking Strategy.\\n * @return eth The amount of execution rewards that were sent to the Native Staking Strategy\\n */\\n function collect() external returns (uint256 eth) {\\n require(msg.sender == STRATEGY, \\\"Caller is not the Strategy\\\");\\n\\n eth = address(this).balance;\\n if (eth > 0) {\\n // Send the ETH to the Native Staking Strategy\\n Address.sendValue(payable(STRATEGY), eth);\\n\\n emit ExecutionRewardsCollected(STRATEGY, eth);\\n }\\n }\\n\\n /**\\n * @dev Accept ETH\\n */\\n receive() external payable {}\\n}\\n\",\"keccak256\":\"0x153897222930c7f8a7b424620d5bfef3f7e57fa5f909d022b34302412a6324dc\",\"license\":\"BUSL-1.1\"},\"contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { IERC20 } from \\\"@openzeppelin/contracts/token/ERC20/IERC20.sol\\\";\\nimport { SafeERC20 } from \\\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\\\";\\nimport \\\"@openzeppelin/contracts/utils/math/Math.sol\\\";\\n\\nimport { InitializableAbstractStrategy } from \\\"../../utils/InitializableAbstractStrategy.sol\\\";\\nimport { IWETH9 } from \\\"../../interfaces/IWETH9.sol\\\";\\nimport { FeeAccumulator } from \\\"./FeeAccumulator.sol\\\";\\nimport { ValidatorAccountant } from \\\"./ValidatorAccountant.sol\\\";\\nimport { ISSVNetwork } from \\\"../../interfaces/ISSVNetwork.sol\\\";\\n\\nstruct ValidatorStakeData {\\n bytes pubkey;\\n bytes signature;\\n bytes32 depositDataRoot;\\n}\\n\\n/// @title Native Staking SSV Strategy\\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\\n/// @author Origin Protocol Inc\\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\\n/// required since the rewards (reward token) is also in ETH.\\n///\\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\\n/// immediately wraps ETH to WETH and sends it to the Vault.\\n///\\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\\n/// - as a result of already accounted for consensus rewards\\n/// - as a result of not yet accounted for consensus rewards\\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\\n///\\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\\n/// interval and not immediately.\\ncontract NativeStakingSSVStrategy is\\n ValidatorAccountant,\\n InitializableAbstractStrategy\\n{\\n using SafeERC20 for IERC20;\\n\\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\\n address public immutable SSV_TOKEN;\\n /// @notice Fee collector address\\n /// @dev this address will receive maximal extractable value (MEV) rewards. These are\\n /// rewards for arranging transactions in a way that benefits the validator.\\n address payable public immutable FEE_ACCUMULATOR_ADDRESS;\\n\\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\\n /// of WETH that has already been accounted for.\\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\\n /// deposit events.\\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\\n /// be staked.\\n uint256 public depositedWethAccountedFor;\\n\\n // For future use\\n uint256[49] private __gap;\\n\\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\\n /// @param _wethAddress Address of the Erc20 WETH Token contract\\n /// @param _ssvToken Address of the Erc20 SSV Token contract\\n /// @param _ssvNetwork Address of the SSV Network contract\\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\\n /// @param _feeAccumulator Address of the fee accumulator receiving execution layer validator rewards\\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\\n constructor(\\n BaseStrategyConfig memory _baseConfig,\\n address _wethAddress,\\n address _ssvToken,\\n address _ssvNetwork,\\n uint256 _maxValidators,\\n address _feeAccumulator,\\n address _beaconChainDepositContract\\n )\\n InitializableAbstractStrategy(_baseConfig)\\n ValidatorAccountant(\\n _wethAddress,\\n _baseConfig.vaultAddress,\\n _beaconChainDepositContract,\\n _ssvNetwork,\\n _maxValidators\\n )\\n {\\n SSV_TOKEN = _ssvToken;\\n FEE_ACCUMULATOR_ADDRESS = payable(_feeAccumulator);\\n }\\n\\n /// @notice Set up initial internal state including\\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\\n /// 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract.\\n /// @param _rewardTokenAddresses Address of reward token for platform\\n /// @param _assets Addresses of initial supported assets\\n /// @param _pTokens Platform Token corresponding addresses\\n function initialize(\\n address[] memory _rewardTokenAddresses,\\n address[] memory _assets,\\n address[] memory _pTokens\\n ) external onlyGovernor initializer {\\n InitializableAbstractStrategy._initialize(\\n _rewardTokenAddresses,\\n _assets,\\n _pTokens\\n );\\n\\n // Approves the SSV Network contract to transfer SSV tokens for deposits\\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\\n\\n // Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\\n FEE_ACCUMULATOR_ADDRESS\\n );\\n }\\n\\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\\n /// It just checks the asset is WETH and emits the Deposit event.\\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\\n /// Will NOT revert if the strategy is paused from an accounting failure.\\n /// @param _asset Address of asset to deposit. Has to be WETH.\\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\\n function deposit(address _asset, uint256 _amount)\\n external\\n override\\n onlyVault\\n nonReentrant\\n {\\n require(_asset == WETH, \\\"Unsupported asset\\\");\\n depositedWethAccountedFor += _amount;\\n _deposit(_asset, _amount);\\n }\\n\\n /// @dev Deposit WETH to this strategy so it can later be staked into a validator.\\n /// @param _asset Address of WETH\\n /// @param _amount Amount of WETH to deposit\\n function _deposit(address _asset, uint256 _amount) internal {\\n require(_amount > 0, \\\"Must deposit something\\\");\\n /*\\n * We could do a check here that would revert when \\\"_amount % 32 ether != 0\\\". With the idea of\\n * not allowing deposits that will result in WETH sitting on the strategy after all the possible batches\\n * of 32ETH have been staked.\\n * But someone could mess with our strategy by sending some WETH to it. And we might want to deposit just\\n * enough WETH to add it up to 32 so it can be staked. For that reason the check is left out.\\n *\\n * WETH sitting on the strategy won't interfere with the accounting since accounting only operates on ETH.\\n */\\n emit Deposit(_asset, address(0), _amount);\\n }\\n\\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\\n /// It just emits the Deposit event.\\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\\n /// Will NOT revert if the strategy is paused from an accounting failure.\\n function depositAll() external override onlyVault nonReentrant {\\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\\n\\n if (newWeth > 0) {\\n depositedWethAccountedFor = wethBalance;\\n\\n _deposit(WETH, newWeth);\\n }\\n }\\n\\n /// @notice Withdraw WETH from this contract. Used only if some WETH for is lingering on the contract.\\n /// That can happen when:\\n /// - after mints if the strategy is the default\\n /// - time between depositToStrategy and stakeEth\\n /// - the deposit was not a multiple of 32 WETH\\n /// - someone sent WETH directly to this contract\\n /// Will NOT revert if the strategy is paused from an accounting failure.\\n /// @param _recipient Address to receive withdrawn assets\\n /// @param _asset WETH to withdraw\\n /// @param _amount Amount of WETH to withdraw\\n function withdraw(\\n address _recipient,\\n address _asset,\\n uint256 _amount\\n ) external override onlyVault nonReentrant {\\n require(_asset == WETH, \\\"Unsupported asset\\\");\\n _withdraw(_recipient, _asset, _amount);\\n }\\n\\n function _withdraw(\\n address _recipient,\\n address _asset,\\n uint256 _amount\\n ) internal {\\n require(_amount > 0, \\\"Must withdraw something\\\");\\n require(_recipient != address(0), \\\"Must specify recipient\\\");\\n\\n _wethWithdrawn(_amount);\\n\\n IERC20(_asset).safeTransfer(_recipient, _amount);\\n emit Withdrawal(_asset, address(0), _amount);\\n }\\n\\n /// @notice transfer all WETH deposits back to the vault.\\n /// This does not withdraw from the validators. That has to be done separately with the\\n /// `exitSsvValidator` and `removeSsvValidator` operations.\\n /// This does not withdraw any execution rewards from the FeeAccumulator or\\n /// consensus rewards in this strategy.\\n /// Any ETH in this strategy that was swept from a full validator withdrawal will not be withdrawn.\\n /// ETH from full validator withdrawals is sent to the Vault using `doAccounting`.\\n /// Will NOT revert if the strategy is paused from an accounting failure.\\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\\n if (wethBalance > 0) {\\n _withdraw(vaultAddress, WETH, wethBalance);\\n }\\n }\\n\\n /// @notice Returns the total value of (W)ETH that is staked to the validators\\n /// and WETH deposits that are still to be staked.\\n /// This does not include ETH from consensus rewards sitting in this strategy\\n /// or ETH from MEV rewards in the FeeAccumulator. These rewards are harvested\\n /// and sent to the Dripper so will eventually be sent to the Vault as WETH.\\n /// @param _asset Address of weth asset\\n /// @return balance Total value of (W)ETH\\n function checkBalance(address _asset)\\n external\\n view\\n override\\n returns (uint256 balance)\\n {\\n require(_asset == WETH, \\\"Unsupported asset\\\");\\n\\n balance =\\n // add the ETH that has been staked in validators\\n activeDepositedValidators *\\n FULL_STAKE +\\n // add the WETH in the strategy from deposits that are still to be staked\\n IERC20(WETH).balanceOf(address(this));\\n }\\n\\n function pause() external onlyStrategist {\\n _pause();\\n }\\n\\n /// @notice Returns bool indicating whether asset is supported by strategy.\\n /// @param _asset The address of the asset token.\\n function supportsAsset(address _asset) public view override returns (bool) {\\n return _asset == WETH;\\n }\\n\\n /// @notice Approves the SSV Network contract to transfer SSV tokens for deposits\\n function safeApproveAllTokens() external override {\\n // Approves the SSV Network contract to transfer SSV tokens for deposits\\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\\n }\\n\\n /// @notice Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\\n function setFeeRecipient() external {\\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\\n FEE_ACCUMULATOR_ADDRESS\\n );\\n }\\n\\n /**\\n * @notice Only accept ETH from the FeeAccumulator and the WETH contract - required when\\n * unwrapping WETH just before staking it to the validator.\\n * The strategy will also receive ETH from the priority fees of transactions when producing blocks\\n * as defined in EIP-1559.\\n * The tx fees come from the Beacon chain so do not need any EVM level permissions to receive ETH.\\n * The tx fees are paid with each block produced. They are not included in the consensus rewards\\n * which are periodically swept from the validators to this strategy.\\n * For accounting purposes, the priority fees of transactions will be considered consensus rewards\\n * and will be included in the AccountingConsensusRewards event.\\n * @dev don't want to receive donations from anyone else as donations over the fuse limits will\\n * mess with the accounting of the consensus rewards and validator full withdrawals.\\n */\\n receive() external payable {\\n require(\\n msg.sender == FEE_ACCUMULATOR_ADDRESS || msg.sender == WETH,\\n \\\"Eth not from allowed contracts\\\"\\n );\\n }\\n\\n /***************************************\\n Internal functions\\n ****************************************/\\n\\n function _abstractSetPToken(address _asset, address) internal override {}\\n\\n /// @dev Convert accumulated ETH to WETH and send to the Harvester.\\n /// Will revert if the strategy is paused for accounting.\\n function _collectRewardTokens() internal override whenNotPaused {\\n // collect ETH from execution rewards from the fee accumulator\\n uint256 executionRewards = FeeAccumulator(FEE_ACCUMULATOR_ADDRESS)\\n .collect();\\n\\n // total ETH rewards to be harvested = execution rewards + consensus rewards\\n uint256 ethRewards = executionRewards + consensusRewards;\\n\\n require(\\n address(this).balance >= ethRewards,\\n \\\"Insufficient eth balance\\\"\\n );\\n\\n if (ethRewards > 0) {\\n // reset the counter keeping track of beacon chain consensus rewards\\n consensusRewards = 0;\\n\\n // Convert ETH rewards to WETH\\n IWETH9(WETH).deposit{ value: ethRewards }();\\n\\n IERC20(WETH).safeTransfer(harvesterAddress, ethRewards);\\n emit RewardTokenCollected(harvesterAddress, WETH, ethRewards);\\n }\\n }\\n\\n /// @dev emits Withdrawal event from NativeStakingSSVStrategy\\n function _wethWithdrawnToVault(uint256 _amount) internal override {\\n emit Withdrawal(WETH, address(0), _amount);\\n }\\n\\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\\n /// the strategy knows how much WETH it has on deposit.\\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\\n function _wethWithdrawn(uint256 _amount) internal override {\\n /* In an ideal world we wouldn't need to reduce the deduction amount when the\\n * depositedWethAccountedFor is smaller than the _amount.\\n *\\n * The reason this is required is that a malicious actor could sent WETH directly\\n * to this contract and that would circumvent the increase of depositedWethAccountedFor\\n * property. When the ETH would be staked the depositedWethAccountedFor amount could\\n * be deducted so much that it would be negative.\\n */\\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\\n depositedWethAccountedFor -= deductAmount;\\n }\\n}\\n\",\"keccak256\":\"0x3776ae2cabe8d481d31037279f18a4d36121686c2dfcf53b43d8f4fb96dc8f32\",\"license\":\"BUSL-1.1\"},\"contracts/strategies/NativeStaking/ValidatorAccountant.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { ValidatorRegistrator } from \\\"./ValidatorRegistrator.sol\\\";\\nimport { IWETH9 } from \\\"../../interfaces/IWETH9.sol\\\";\\n\\n/// @title Validator Accountant\\n/// @notice Attributes the ETH swept from beacon chain validators to this strategy contract\\n/// as either full or partial withdrawals. Partial withdrawals being consensus rewards.\\n/// Full withdrawals are from exited validators.\\n/// @author Origin Protocol Inc\\nabstract contract ValidatorAccountant is ValidatorRegistrator {\\n /// @notice The minimum amount of blocks that need to pass between two calls to manuallyFixAccounting\\n uint256 public constant MIN_FIX_ACCOUNTING_CADENCE = 7200; // 1 day\\n\\n /// @notice Keeps track of the total consensus rewards swept from the beacon chain\\n uint256 public consensusRewards;\\n\\n /// @notice start of fuse interval\\n uint256 public fuseIntervalStart;\\n /// @notice end of fuse interval\\n uint256 public fuseIntervalEnd;\\n /// @notice last block number manuallyFixAccounting has been called\\n uint256 public lastFixAccountingBlockNumber;\\n\\n uint256[49] private __gap;\\n\\n event FuseIntervalUpdated(uint256 start, uint256 end);\\n event AccountingFullyWithdrawnValidator(\\n uint256 noOfValidators,\\n uint256 remainingValidators,\\n uint256 wethSentToVault\\n );\\n event AccountingValidatorSlashed(\\n uint256 remainingValidators,\\n uint256 wethSentToVault\\n );\\n event AccountingConsensusRewards(uint256 amount);\\n\\n event AccountingManuallyFixed(\\n int256 validatorsDelta,\\n int256 consensusRewardsDelta,\\n uint256 wethToVault\\n );\\n\\n /// @param _wethAddress Address of the Erc20 WETH Token contract\\n /// @param _vaultAddress Address of the Vault\\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\\n /// @param _ssvNetwork Address of the SSV Network contract\\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\\n constructor(\\n address _wethAddress,\\n address _vaultAddress,\\n address _beaconChainDepositContract,\\n address _ssvNetwork,\\n uint256 _maxValidators\\n )\\n ValidatorRegistrator(\\n _wethAddress,\\n _vaultAddress,\\n _beaconChainDepositContract,\\n _ssvNetwork,\\n _maxValidators\\n )\\n {}\\n\\n /// @notice set fuse interval values\\n function setFuseInterval(\\n uint256 _fuseIntervalStart,\\n uint256 _fuseIntervalEnd\\n ) external onlyGovernor {\\n require(\\n _fuseIntervalStart < _fuseIntervalEnd &&\\n _fuseIntervalEnd < 32 ether &&\\n _fuseIntervalEnd - _fuseIntervalStart >= 4 ether,\\n \\\"Incorrect fuse interval\\\"\\n );\\n\\n fuseIntervalStart = _fuseIntervalStart;\\n fuseIntervalEnd = _fuseIntervalEnd;\\n\\n emit FuseIntervalUpdated(_fuseIntervalStart, _fuseIntervalEnd);\\n }\\n\\n /* solhint-disable max-line-length */\\n /// This notion page offers a good explanation of how the accounting functions\\n /// https://www.notion.so/originprotocol/Limited-simplified-native-staking-accounting-67a217c8420d40678eb943b9da0ee77d\\n /// In short, after dividing by 32, if the ETH remaining on the contract falls between 0 and fuseIntervalStart,\\n /// the accounting function will treat that ETH as Beacon chain consensus rewards.\\n /// On the contrary, if after dividing by 32, the ETH remaining on the contract falls between fuseIntervalEnd and 32,\\n /// the accounting function will treat that as a validator slashing.\\n /// @notice Perform the accounting attributing beacon chain ETH to either full or partial withdrawals. Returns true when\\n /// accounting is valid and fuse isn't \\\"blown\\\". Returns false when fuse is blown.\\n /// @dev This function could in theory be permission-less but lets allow only the Registrator (Defender Action) to call it\\n /// for now.\\n /// @return accountingValid true if accounting was successful, false if fuse is blown\\n /* solhint-enable max-line-length */\\n function doAccounting()\\n external\\n onlyRegistrator\\n whenNotPaused\\n nonReentrant\\n returns (bool accountingValid)\\n {\\n // pause the accounting on failure\\n accountingValid = _doAccounting(true);\\n }\\n\\n // slither-disable-start reentrancy-eth\\n function _doAccounting(bool pauseOnFail)\\n internal\\n returns (bool accountingValid)\\n {\\n if (address(this).balance < consensusRewards) {\\n return _failAccounting(pauseOnFail);\\n }\\n\\n // Calculate all the new ETH that has been swept to the contract since the last accounting\\n uint256 newSweptETH = address(this).balance - consensusRewards;\\n accountingValid = true;\\n\\n // send the ETH that is from fully withdrawn validators to the Vault\\n if (newSweptETH >= FULL_STAKE) {\\n uint256 fullyWithdrawnValidators;\\n // explicitly cast to uint256 as we want to round to a whole number of validators\\n fullyWithdrawnValidators = uint256(newSweptETH / FULL_STAKE);\\n activeDepositedValidators -= fullyWithdrawnValidators;\\n\\n uint256 wethToVault = FULL_STAKE * fullyWithdrawnValidators;\\n IWETH9(WETH).deposit{ value: wethToVault }();\\n // slither-disable-next-line unchecked-transfer\\n IWETH9(WETH).transfer(VAULT_ADDRESS, wethToVault);\\n _wethWithdrawnToVault(wethToVault);\\n\\n emit AccountingFullyWithdrawnValidator(\\n fullyWithdrawnValidators,\\n activeDepositedValidators,\\n wethToVault\\n );\\n }\\n\\n uint256 ethRemaining = address(this).balance - consensusRewards;\\n // should be less than a whole validator stake\\n require(ethRemaining < FULL_STAKE, \\\"Unexpected accounting\\\");\\n\\n // If no Beacon chain consensus rewards swept\\n if (ethRemaining == 0) {\\n // do nothing\\n return accountingValid;\\n } else if (ethRemaining < fuseIntervalStart) {\\n // Beacon chain consensus rewards swept (partial validator withdrawals)\\n // solhint-disable-next-line reentrancy\\n consensusRewards += ethRemaining;\\n emit AccountingConsensusRewards(ethRemaining);\\n } else if (ethRemaining > fuseIntervalEnd) {\\n // Beacon chain consensus rewards swept but also a slashed validator fully exited\\n IWETH9(WETH).deposit{ value: ethRemaining }();\\n // slither-disable-next-line unchecked-transfer\\n IWETH9(WETH).transfer(VAULT_ADDRESS, ethRemaining);\\n activeDepositedValidators -= 1;\\n\\n _wethWithdrawnToVault(ethRemaining);\\n\\n emit AccountingValidatorSlashed(\\n activeDepositedValidators,\\n ethRemaining\\n );\\n }\\n // Oh no... Fuse is blown. The Strategist needs to adjust the accounting values.\\n else {\\n return _failAccounting(pauseOnFail);\\n }\\n }\\n\\n // slither-disable-end reentrancy-eth\\n\\n /// @dev pause any further accounting if required and return false\\n function _failAccounting(bool pauseOnFail)\\n internal\\n returns (bool accountingValid)\\n {\\n // pause if not already\\n if (pauseOnFail) {\\n _pause();\\n }\\n // fail the accounting\\n accountingValid = false;\\n }\\n\\n /// @notice Allow the Strategist to fix the accounting of this strategy and unpause.\\n /// @param _validatorsDelta adjust the active validators by up to plus three or minus three\\n /// @param _consensusRewardsDelta adjust the accounted for consensus rewards up or down\\n /// @param _ethToVaultAmount the amount of ETH that gets wrapped into WETH and sent to the Vault\\n /// @dev There is a case when a validator(s) gets slashed so much that the eth swept from\\n /// the beacon chain enters the fuse area and there are no consensus rewards on the contract\\n /// to \\\"dip into\\\"/use. To increase the amount of unaccounted ETH over the fuse end interval\\n /// we need to reduce the amount of active deposited validators and immediately send WETH\\n /// to the vault, so it doesn't interfere with further accounting.\\n function manuallyFixAccounting(\\n int256 _validatorsDelta,\\n int256 _consensusRewardsDelta,\\n uint256 _ethToVaultAmount\\n ) external onlyStrategist whenPaused nonReentrant {\\n require(\\n lastFixAccountingBlockNumber + MIN_FIX_ACCOUNTING_CADENCE <\\n block.number,\\n \\\"Fix accounting called too soon\\\"\\n );\\n require(\\n _validatorsDelta >= -3 &&\\n _validatorsDelta <= 3 &&\\n // new value must be positive\\n int256(activeDepositedValidators) + _validatorsDelta >= 0,\\n \\\"Invalid validatorsDelta\\\"\\n );\\n require(\\n _consensusRewardsDelta >= -332 ether &&\\n _consensusRewardsDelta <= 332 ether &&\\n // new value must be positive\\n int256(consensusRewards) + _consensusRewardsDelta >= 0,\\n \\\"Invalid consensusRewardsDelta\\\"\\n );\\n require(_ethToVaultAmount <= 32 ether * 3, \\\"Invalid wethToVaultAmount\\\");\\n\\n activeDepositedValidators = uint256(\\n int256(activeDepositedValidators) + _validatorsDelta\\n );\\n consensusRewards = uint256(\\n int256(consensusRewards) + _consensusRewardsDelta\\n );\\n lastFixAccountingBlockNumber = block.number;\\n if (_ethToVaultAmount > 0) {\\n IWETH9(WETH).deposit{ value: _ethToVaultAmount }();\\n // slither-disable-next-line unchecked-transfer\\n IWETH9(WETH).transfer(VAULT_ADDRESS, _ethToVaultAmount);\\n _wethWithdrawnToVault(_ethToVaultAmount);\\n }\\n\\n emit AccountingManuallyFixed(\\n _validatorsDelta,\\n _consensusRewardsDelta,\\n _ethToVaultAmount\\n );\\n\\n // rerun the accounting to see if it has now been fixed.\\n // Do not pause the accounting on failure as it is already paused\\n require(_doAccounting(false), \\\"Fuse still blown\\\");\\n\\n // unpause since doAccounting was successful\\n _unpause();\\n }\\n\\n /***************************************\\n Abstract\\n ****************************************/\\n\\n /// @dev allows for NativeStakingSSVStrategy contract to emit the Withdrawal event\\n function _wethWithdrawnToVault(uint256 _amount) internal virtual;\\n}\\n\",\"keccak256\":\"0x1d0ba8e829bb127b2d7eb969bbe6d577b056fa05857bf4da1d3aa781e824c72c\",\"license\":\"BUSL-1.1\"},\"contracts/strategies/NativeStaking/ValidatorRegistrator.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { Pausable } from \\\"@openzeppelin/contracts/security/Pausable.sol\\\";\\nimport { Governable } from \\\"../../governance/Governable.sol\\\";\\nimport { IConsolidationTarget } from \\\"../../interfaces/IConsolidation.sol\\\";\\nimport { IDepositContract } from \\\"../../interfaces/IDepositContract.sol\\\";\\nimport { IVault } from \\\"../../interfaces/IVault.sol\\\";\\nimport { IWETH9 } from \\\"../../interfaces/IWETH9.sol\\\";\\nimport { ISSVNetwork, Cluster } from \\\"../../interfaces/ISSVNetwork.sol\\\";\\nimport { BeaconConsolidation } from \\\"../../beacon/BeaconConsolidation.sol\\\";\\n\\nstruct ValidatorStakeData {\\n bytes pubkey;\\n bytes signature;\\n bytes32 depositDataRoot;\\n}\\n\\n/**\\n * @title Registrator of the validators\\n * @notice This contract implements all the required functionality to register, exit and remove validators.\\n * @author Origin Protocol Inc\\n */\\nabstract contract ValidatorRegistrator is Governable, Pausable {\\n /// @notice The maximum amount of ETH that can be staked by a validator\\n /// @dev this can change in the future with EIP-7251, Increase the MAX_EFFECTIVE_BALANCE\\n uint256 public constant FULL_STAKE = 32 ether;\\n\\n /// @notice The address of the Wrapped ETH (WETH) token contract\\n address public immutable WETH;\\n /// @notice The address of the beacon chain deposit contract\\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\\n /// @notice The address of the SSV Network contract used to interface with\\n address public immutable SSV_NETWORK;\\n /// @notice Address of the OETH Vault proxy contract\\n address public immutable VAULT_ADDRESS;\\n /// @notice Maximum number of validators that can be registered in this strategy\\n uint256 public immutable MAX_VALIDATORS;\\n\\n /// @notice Address of the registrator - allowed to register, exit and remove validators\\n address public validatorRegistrator;\\n /// @notice The number of validators that have 32 (!) ETH actively deposited. When a new deposit\\n /// to a validator happens this number increases, when a validator exit is detected this number\\n /// decreases.\\n uint256 public activeDepositedValidators;\\n /// @notice State of the validators keccak256(pubKey) => state\\n mapping(bytes32 => VALIDATOR_STATE) public validatorsStates;\\n /// @notice The account that is allowed to modify stakeETHThreshold and reset stakeETHTally\\n address public stakingMonitor;\\n /// @notice Amount of ETH that can be staked before staking on the contract is suspended\\n /// and the `stakingMonitor` needs to approve further staking by calling `resetStakeETHTally`\\n uint256 public stakeETHThreshold;\\n /// @notice Amount of ETH that has been staked since the `stakingMonitor` last called `resetStakeETHTally`.\\n /// This can not go above `stakeETHThreshold`.\\n uint256 public stakeETHTally;\\n\\n /// @notice Number of validators currently being consolidated\\n uint256 public consolidationCount;\\n address public consolidationTargetStrategy;\\n /// @notice Mapping of support target staking strategies that can be used for consolidation\\n mapping(address => bool) public consolidationTargetStrategies;\\n\\n // For future use\\n uint256[44] private __gap;\\n\\n enum VALIDATOR_STATE {\\n NON_REGISTERED, // validator is not registered on the SSV network\\n REGISTERED, // validator is registered on the SSV network\\n STAKED, // validator has funds staked\\n EXITING, // exit message has been posted and validator is in the process of exiting\\n EXIT_COMPLETE // validator has funds withdrawn to the EigenPod and is removed from the SSV\\n }\\n\\n event RegistratorChanged(address indexed newAddress);\\n event StakingMonitorChanged(address indexed newAddress);\\n event ETHStaked(bytes32 indexed pubKeyHash, bytes pubKey, uint256 amount);\\n event SSVValidatorRegistered(\\n bytes32 indexed pubKeyHash,\\n bytes pubKey,\\n uint64[] operatorIds\\n );\\n event SSVValidatorExitInitiated(\\n bytes32 indexed pubKeyHash,\\n bytes pubKey,\\n uint64[] operatorIds\\n );\\n event SSVValidatorExitCompleted(\\n bytes32 indexed pubKeyHash,\\n bytes pubKey,\\n uint64[] operatorIds\\n );\\n event ConsolidationRequested(\\n bytes[] sourcePubKeys,\\n bytes targetPubKey,\\n address targetStakingStrategy,\\n uint256 consolidationCount\\n );\\n event ConsolidationConfirmed(\\n uint256 consolidationCount,\\n uint256 activeDepositedValidators\\n );\\n event StakeETHThresholdChanged(uint256 amount);\\n event StakeETHTallyReset();\\n event TargetStrategyAdded(address indexed strategy);\\n\\n /// @dev Throws if called by any account other than the Registrator\\n modifier onlyRegistrator() {\\n require(\\n msg.sender == validatorRegistrator,\\n \\\"Caller is not the Registrator\\\"\\n );\\n _;\\n }\\n\\n /// @dev Throws if called by any account other than the Staking monitor\\n modifier onlyStakingMonitor() {\\n require(msg.sender == stakingMonitor, \\\"Caller is not the Monitor\\\");\\n _;\\n }\\n\\n /// @dev Throws if called by any account other than the Strategist\\n modifier onlyStrategist() {\\n require(\\n msg.sender == IVault(VAULT_ADDRESS).strategistAddr(),\\n \\\"Caller is not the Strategist\\\"\\n );\\n _;\\n }\\n\\n /// @param _wethAddress Address of the Erc20 WETH Token contract\\n /// @param _vaultAddress Address of the Vault\\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\\n /// @param _ssvNetwork Address of the SSV Network contract\\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\\n constructor(\\n address _wethAddress,\\n address _vaultAddress,\\n address _beaconChainDepositContract,\\n address _ssvNetwork,\\n uint256 _maxValidators\\n ) {\\n WETH = _wethAddress;\\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\\n SSV_NETWORK = _ssvNetwork;\\n VAULT_ADDRESS = _vaultAddress;\\n MAX_VALIDATORS = _maxValidators;\\n }\\n\\n /// @notice Set the address of the registrator which can register, exit and remove validators\\n function setRegistrator(address _address) external onlyGovernor {\\n validatorRegistrator = _address;\\n emit RegistratorChanged(_address);\\n }\\n\\n /// @notice Set the address of the staking monitor that is allowed to reset stakeETHTally\\n function setStakingMonitor(address _address) external onlyGovernor {\\n stakingMonitor = _address;\\n emit StakingMonitorChanged(_address);\\n }\\n\\n /// @notice Set the amount of ETH that can be staked before staking monitor\\n // needs to a approve further staking by resetting the stake ETH tally\\n function setStakeETHThreshold(uint256 _amount) external onlyGovernor {\\n stakeETHThreshold = _amount;\\n emit StakeETHThresholdChanged(_amount);\\n }\\n\\n /// @notice Reset the stakeETHTally\\n function resetStakeETHTally() external onlyStakingMonitor {\\n stakeETHTally = 0;\\n emit StakeETHTallyReset();\\n }\\n\\n /// @notice Adds support for a new staking strategy that can be used for consolidation.\\n function addTargetStrategy(address _strategy) external onlyGovernor {\\n consolidationTargetStrategies[_strategy] = true;\\n\\n emit TargetStrategyAdded(_strategy);\\n }\\n\\n /// @notice Stakes WETH to the node validators\\n /// @param validators A list of validator data needed to stake.\\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\\n /// Only the registrator can call this function.\\n // slither-disable-start reentrancy-eth\\n function stakeEth(ValidatorStakeData[] calldata validators)\\n external\\n onlyRegistrator\\n whenNotPaused\\n nonReentrant\\n {\\n uint256 requiredETH = validators.length * FULL_STAKE;\\n\\n // Check there is enough WETH from the deposits sitting in this strategy contract\\n require(\\n requiredETH <= IWETH9(WETH).balanceOf(address(this)),\\n \\\"Insufficient WETH\\\"\\n );\\n require(\\n activeDepositedValidators + validators.length <= MAX_VALIDATORS,\\n \\\"Max validators reached\\\"\\n );\\n\\n require(\\n stakeETHTally + requiredETH <= stakeETHThreshold,\\n \\\"Staking ETH over threshold\\\"\\n );\\n stakeETHTally += requiredETH;\\n\\n // Convert required ETH from WETH\\n IWETH9(WETH).withdraw(requiredETH);\\n _wethWithdrawn(requiredETH);\\n\\n /* 0x01 to indicate that withdrawal credentials will contain an EOA address that the sweeping function\\n * can sweep funds to.\\n * bytes11(0) to fill up the required zeros\\n * remaining bytes20 are for the address\\n */\\n bytes memory withdrawalCredentials = abi.encodePacked(\\n bytes1(0x01),\\n bytes11(0),\\n address(this)\\n );\\n\\n // For each validator\\n for (uint256 i = 0; i < validators.length; ++i) {\\n bytes32 pubKeyHash = keccak256(validators[i].pubkey);\\n\\n require(\\n validatorsStates[pubKeyHash] == VALIDATOR_STATE.REGISTERED,\\n \\\"Validator not registered\\\"\\n );\\n\\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\\n value: FULL_STAKE\\n }(\\n validators[i].pubkey,\\n withdrawalCredentials,\\n validators[i].signature,\\n validators[i].depositDataRoot\\n );\\n\\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.STAKED;\\n\\n emit ETHStaked(pubKeyHash, validators[i].pubkey, FULL_STAKE);\\n }\\n // save gas by changing this storage variable only once rather each time in the loop.\\n activeDepositedValidators += validators.length;\\n }\\n\\n // slither-disable-end reentrancy-eth\\n\\n /// @notice Registers a new validator in the SSV Cluster.\\n /// Only the registrator can call this function.\\n /// @param publicKeys The public keys of the validators\\n /// @param operatorIds The operator IDs of the SSV Cluster\\n /// @param sharesData The shares data for each validator\\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\\n /// @param cluster The SSV cluster details including the validator count and SSV balance\\n // slither-disable-start reentrancy-no-eth\\n function registerSsvValidators(\\n bytes[] calldata publicKeys,\\n uint64[] calldata operatorIds,\\n bytes[] calldata sharesData,\\n uint256 ssvAmount,\\n Cluster calldata cluster\\n ) external onlyRegistrator whenNotPaused {\\n require(\\n publicKeys.length == sharesData.length,\\n \\\"Pubkey sharesData mismatch\\\"\\n );\\n // Check each public key has not already been used\\n bytes32 pubKeyHash;\\n VALIDATOR_STATE currentState;\\n for (uint256 i = 0; i < publicKeys.length; ++i) {\\n pubKeyHash = keccak256(publicKeys[i]);\\n currentState = validatorsStates[pubKeyHash];\\n require(\\n currentState == VALIDATOR_STATE.NON_REGISTERED,\\n \\\"Validator already registered\\\"\\n );\\n\\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\\n\\n emit SSVValidatorRegistered(pubKeyHash, publicKeys[i], operatorIds);\\n }\\n\\n ISSVNetwork(SSV_NETWORK).bulkRegisterValidator(\\n publicKeys,\\n operatorIds,\\n sharesData,\\n ssvAmount,\\n cluster\\n );\\n }\\n\\n /// @notice Exit validators from the Beacon chain.\\n /// The staked ETH will eventually swept to this native staking strategy.\\n /// Only the registrator can call this function.\\n /// @param publicKeys List of SSV validator public keys\\n /// @param operatorIds The operator IDs of the SSV Cluster\\n // slither-disable-start reentrancy-no-eth\\n function exitSsvValidators(\\n bytes[] calldata publicKeys,\\n uint64[] calldata operatorIds\\n ) external onlyRegistrator whenNotPaused nonReentrant {\\n ISSVNetwork(SSV_NETWORK).bulkExitValidator(publicKeys, operatorIds);\\n\\n bytes32 pubKeyHash;\\n VALIDATOR_STATE currentState;\\n for (uint256 i = 0; i < publicKeys.length; ++i) {\\n pubKeyHash = keccak256(publicKeys[i]);\\n currentState = validatorsStates[pubKeyHash];\\n\\n // Check each validator has not already been staked.\\n // This would normally be done before the external call but is after\\n // so only one for loop of the validators is needed.\\n require(\\n currentState == VALIDATOR_STATE.STAKED,\\n \\\"Validator not staked\\\"\\n );\\n\\n // Store the new validator state\\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXITING;\\n\\n emit SSVValidatorExitInitiated(\\n pubKeyHash,\\n publicKeys[i],\\n operatorIds\\n );\\n }\\n }\\n\\n /// @notice Remove validators from the SSV Cluster.\\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\\n /// Only the registrator can call this function.\\n /// @param publicKeys List of SSV validator public keys\\n /// @param operatorIds The operator IDs of the SSV Cluster\\n /// @param cluster The SSV cluster details including the validator count and SSV balance\\n function removeSsvValidators(\\n bytes[] calldata publicKeys,\\n uint64[] calldata operatorIds,\\n Cluster calldata cluster\\n ) external onlyRegistrator whenNotPaused nonReentrant {\\n ISSVNetwork(SSV_NETWORK).bulkRemoveValidator(\\n publicKeys,\\n operatorIds,\\n cluster\\n );\\n\\n bytes32 pubKeyHash;\\n VALIDATOR_STATE currentState;\\n for (uint256 i = 0; i < publicKeys.length; ++i) {\\n pubKeyHash = keccak256(publicKeys[i]);\\n currentState = validatorsStates[pubKeyHash];\\n\\n // Check each validator is either registered or exited.\\n // This would normally be done before the external call but is after\\n // so only one for loop of the validators is needed.\\n require(\\n currentState == VALIDATOR_STATE.EXITING ||\\n currentState == VALIDATOR_STATE.REGISTERED,\\n \\\"Validator not regd or exiting\\\"\\n );\\n\\n // Store the new validator state\\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXIT_COMPLETE;\\n\\n emit SSVValidatorExitCompleted(\\n pubKeyHash,\\n publicKeys[i],\\n operatorIds\\n );\\n }\\n }\\n\\n /// @notice Deposits more SSV Tokens to the SSV Network contract which is used to pay the SSV Operators.\\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\\n /// uses \\\"onlyStrategist\\\" modifier so continuous front-running can't DOS our maintenance service\\n /// that tries to top up SSV tokens.\\n /// @param operatorIds The operator IDs of the SSV Cluster\\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\\n /// @param cluster The SSV cluster details including the validator count and SSV balance\\n function depositSSV(\\n uint64[] memory operatorIds,\\n uint256 ssvAmount,\\n Cluster memory cluster\\n ) external onlyStrategist {\\n ISSVNetwork(SSV_NETWORK).deposit(\\n address(this),\\n operatorIds,\\n ssvAmount,\\n cluster\\n );\\n }\\n\\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\\n /// @param operatorIds The operator IDs of the SSV Cluster\\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\\n /// @param cluster The SSV cluster details including the validator count and SSV balance\\n function withdrawSSV(\\n uint64[] memory operatorIds,\\n uint256 ssvAmount,\\n Cluster memory cluster\\n ) external onlyGovernor {\\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\\n }\\n\\n /***************************************\\n New Consolidation\\n ****************************************/\\n\\n function requestConsolidation(\\n bytes[] calldata sourcePubKeys,\\n bytes calldata targetPubKey,\\n address targetStakingStrategy\\n ) external nonReentrant whenNotPaused onlyGovernor {\\n require(\\n consolidationTargetStrategies[targetStakingStrategy],\\n \\\"Invalid target strategy\\\"\\n );\\n\\n bytes32 targetPubKeyHash = keccak256(targetPubKey);\\n bytes32 sourcePubKeyHash;\\n for (uint256 i = 0; i < sourcePubKeys.length; ++i) {\\n // hash the source validator's public key using the Beacon Chain's format\\n sourcePubKeyHash = keccak256(sourcePubKeys[i]);\\n require(sourcePubKeyHash != targetPubKeyHash, \\\"Self consolidation\\\");\\n require(\\n validatorsStates[sourcePubKeyHash] == VALIDATOR_STATE.STAKED,\\n \\\"Source validator not staked\\\"\\n );\\n\\n // Request consolidation from source to target validator\\n BeaconConsolidation.request(sourcePubKeys[i], targetPubKey);\\n\\n // Store the state of the source validator as exiting so it can be removed\\n // after the consolidation is confirmed\\n validatorsStates[sourcePubKeyHash] == VALIDATOR_STATE.EXITING;\\n }\\n\\n // Hash using the Beacon Chain's format\\n bytes32 lastSourcePubKeyHash = _hashPubKey(\\n sourcePubKeys[sourcePubKeys.length - 1]\\n );\\n // Call the new compounding staking strategy to validate the target validator\\n IConsolidationTarget(targetStakingStrategy).requestConsolidation(\\n lastSourcePubKeyHash,\\n targetPubKeyHash\\n );\\n\\n // Store the consolidation state\\n consolidationCount = sourcePubKeys.length;\\n consolidationTargetStrategy = targetStakingStrategy;\\n\\n // Pause the strategy to prevent further consolidations or validator exits\\n _pause();\\n\\n emit ConsolidationRequested(\\n sourcePubKeys,\\n targetPubKey,\\n targetStakingStrategy,\\n sourcePubKeys.length\\n );\\n }\\n\\n function confirmConsolidation()\\n external\\n nonReentrant\\n whenPaused\\n returns (uint256 consolidationCount_)\\n {\\n // Check the caller is the target staking strategy\\n require(\\n msg.sender == consolidationTargetStrategy,\\n \\\"Not target strategy\\\"\\n );\\n\\n // Load the number of validators being consolidated into memory\\n consolidationCount_ = consolidationCount;\\n\\n // Need to check this is from the new staking strategy\\n require(consolidationCount_ > 0, \\\"No consolidation in progress\\\");\\n\\n // Store the reduced number of active deposited validators\\n // managed by this strategy\\n activeDepositedValidators -= consolidationCount_;\\n\\n // Reset the consolidation count\\n consolidationCount = 0;\\n consolidationTargetStrategy = address(0);\\n\\n // Unpause the strategy to allow further operations\\n _unpause();\\n\\n emit ConsolidationConfirmed(\\n consolidationCount_,\\n activeDepositedValidators\\n );\\n }\\n\\n /// @notice Hash a validator public key using the Beacon Chain's format\\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\\n }\\n\\n /***************************************\\n Abstract\\n ****************************************/\\n\\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\\n /// the strategy knows how much WETH it has on deposit.\\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\\n function _wethWithdrawn(uint256 _amount) internal virtual;\\n}\\n\",\"keccak256\":\"0x845d2f1c0cb72aa79dbfb059573680e6546aaec9519e7ccfcfebd8aaacd7bcc4\",\"license\":\"BUSL-1.1\"},\"contracts/token/OUSD.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title OUSD Token Contract\\n * @dev ERC20 compatible contract for OUSD\\n * @dev Implements an elastic supply\\n * @author Origin Protocol Inc\\n */\\nimport { IVault } from \\\"../interfaces/IVault.sol\\\";\\nimport { Governable } from \\\"../governance/Governable.sol\\\";\\nimport { SafeCast } from \\\"@openzeppelin/contracts/utils/math/SafeCast.sol\\\";\\n\\ncontract OUSD is Governable {\\n using SafeCast for int256;\\n using SafeCast for uint256;\\n\\n /// @dev Event triggered when the supply changes\\n /// @param totalSupply Updated token total supply\\n /// @param rebasingCredits Updated token rebasing credits\\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\\n event TotalSupplyUpdatedHighres(\\n uint256 totalSupply,\\n uint256 rebasingCredits,\\n uint256 rebasingCreditsPerToken\\n );\\n /// @dev Event triggered when an account opts in for rebasing\\n /// @param account Address of the account\\n event AccountRebasingEnabled(address account);\\n /// @dev Event triggered when an account opts out of rebasing\\n /// @param account Address of the account\\n event AccountRebasingDisabled(address account);\\n /// @dev Emitted when `value` tokens are moved from one account `from` to\\n /// another `to`.\\n /// @param from Address of the account tokens are moved from\\n /// @param to Address of the account tokens are moved to\\n /// @param value Amount of tokens transferred\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n /// a call to {approve}. `value` is the new allowance.\\n /// @param owner Address of the owner approving allowance\\n /// @param spender Address of the spender allowance is granted to\\n /// @param value Amount of tokens spender can transfer\\n event Approval(\\n address indexed owner,\\n address indexed spender,\\n uint256 value\\n );\\n /// @dev Yield resulting from {changeSupply} that a `source` account would\\n /// receive is directed to `target` account.\\n /// @param source Address of the source forwarding the yield\\n /// @param target Address of the target receiving the yield\\n event YieldDelegated(address source, address target);\\n /// @dev Yield delegation from `source` account to the `target` account is\\n /// suspended.\\n /// @param source Address of the source suspending yield forwarding\\n /// @param target Address of the target no longer receiving yield from `source`\\n /// account\\n event YieldUndelegated(address source, address target);\\n\\n enum RebaseOptions {\\n NotSet,\\n StdNonRebasing,\\n StdRebasing,\\n YieldDelegationSource,\\n YieldDelegationTarget\\n }\\n\\n uint256[154] private _gap; // Slots to align with deployed contract\\n uint256 private constant MAX_SUPPLY = type(uint128).max;\\n /// @dev The amount of tokens in existence\\n uint256 public totalSupply;\\n mapping(address => mapping(address => uint256)) private allowances;\\n /// @dev The vault with privileges to execute {mint}, {burn}\\n /// and {changeSupply}\\n address public vaultAddress;\\n mapping(address => uint256) internal creditBalances;\\n // the 2 storage variables below need trailing underscores to not name collide with public functions\\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\\n uint256 private rebasingCreditsPerToken_;\\n /// @dev The amount of tokens that are not rebasing - receiving yield\\n uint256 public nonRebasingSupply;\\n mapping(address => uint256) internal alternativeCreditsPerToken;\\n /// @dev A map of all addresses and their respective RebaseOptions\\n mapping(address => RebaseOptions) public rebaseState;\\n mapping(address => uint256) private __deprecated_isUpgraded;\\n /// @dev A map of addresses that have yields forwarded to. This is an\\n /// inverse mapping of {yieldFrom}\\n /// Key Account forwarding yield\\n /// Value Account receiving yield\\n mapping(address => address) public yieldTo;\\n /// @dev A map of addresses that are receiving the yield. This is an\\n /// inverse mapping of {yieldTo}\\n /// Key Account receiving yield\\n /// Value Account forwarding yield\\n mapping(address => address) public yieldFrom;\\n\\n uint256 private constant RESOLUTION_INCREASE = 1e9;\\n uint256[34] private __gap; // including below gap totals up to 200\\n\\n /// @dev Verifies that the caller is the Governor or Strategist.\\n modifier onlyGovernorOrStrategist() {\\n require(\\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\\n \\\"Caller is not the Strategist or Governor\\\"\\n );\\n _;\\n }\\n\\n /// @dev Initializes the contract and sets necessary variables.\\n /// @param _vaultAddress Address of the vault contract\\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\\n external\\n onlyGovernor\\n {\\n require(_vaultAddress != address(0), \\\"Zero vault address\\\");\\n require(vaultAddress == address(0), \\\"Already initialized\\\");\\n\\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\\n vaultAddress = _vaultAddress;\\n }\\n\\n /// @dev Returns the symbol of the token, a shorter version\\n /// of the name.\\n function symbol() external pure virtual returns (string memory) {\\n return \\\"OUSD\\\";\\n }\\n\\n /// @dev Returns the name of the token.\\n function name() external pure virtual returns (string memory) {\\n return \\\"Origin Dollar\\\";\\n }\\n\\n /// @dev Returns the number of decimals used to get its user representation.\\n function decimals() external pure virtual returns (uint8) {\\n return 18;\\n }\\n\\n /**\\n * @dev Verifies that the caller is the Vault contract\\n */\\n modifier onlyVault() {\\n require(vaultAddress == msg.sender, \\\"Caller is not the Vault\\\");\\n _;\\n }\\n\\n /**\\n * @return High resolution rebasingCreditsPerToken\\n */\\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\\n return rebasingCreditsPerToken_;\\n }\\n\\n /**\\n * @return Low resolution rebasingCreditsPerToken\\n */\\n function rebasingCreditsPerToken() external view returns (uint256) {\\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\\n }\\n\\n /**\\n * @return High resolution total number of rebasing credits\\n */\\n function rebasingCreditsHighres() external view returns (uint256) {\\n return rebasingCredits_;\\n }\\n\\n /**\\n * @return Low resolution total number of rebasing credits\\n */\\n function rebasingCredits() external view returns (uint256) {\\n return rebasingCredits_ / RESOLUTION_INCREASE;\\n }\\n\\n /**\\n * @notice Gets the balance of the specified address.\\n * @param _account Address to query the balance of.\\n * @return A uint256 representing the amount of base units owned by the\\n * specified address.\\n */\\n function balanceOf(address _account) public view returns (uint256) {\\n RebaseOptions state = rebaseState[_account];\\n if (state == RebaseOptions.YieldDelegationSource) {\\n // Saves a slot read when transferring to or from a yield delegating source\\n // since we know creditBalances equals the balance.\\n return creditBalances[_account];\\n }\\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\\n _creditsPerToken(_account);\\n if (state == RebaseOptions.YieldDelegationTarget) {\\n // creditBalances of yieldFrom accounts equals token balances\\n return baseBalance - creditBalances[yieldFrom[_account]];\\n }\\n return baseBalance;\\n }\\n\\n /**\\n * @notice Gets the credits balance of the specified address.\\n * @dev Backwards compatible with old low res credits per token.\\n * @param _account The address to query the balance of.\\n * @return (uint256, uint256) Credit balance and credits per token of the\\n * address\\n */\\n function creditsBalanceOf(address _account)\\n external\\n view\\n returns (uint256, uint256)\\n {\\n uint256 cpt = _creditsPerToken(_account);\\n if (cpt == 1e27) {\\n // For a period before the resolution upgrade, we created all new\\n // contract accounts at high resolution. Since they are not changing\\n // as a result of this upgrade, we will return their true values\\n return (creditBalances[_account], cpt);\\n } else {\\n return (\\n creditBalances[_account] / RESOLUTION_INCREASE,\\n cpt / RESOLUTION_INCREASE\\n );\\n }\\n }\\n\\n /**\\n * @notice Gets the credits balance of the specified address.\\n * @param _account The address to query the balance of.\\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\\n * address, and isUpgraded\\n */\\n function creditsBalanceOfHighres(address _account)\\n external\\n view\\n returns (\\n uint256,\\n uint256,\\n bool\\n )\\n {\\n return (\\n creditBalances[_account],\\n _creditsPerToken(_account),\\n true // all accounts have their resolution \\\"upgraded\\\"\\n );\\n }\\n\\n // Backwards compatible view\\n function nonRebasingCreditsPerToken(address _account)\\n external\\n view\\n returns (uint256)\\n {\\n return alternativeCreditsPerToken[_account];\\n }\\n\\n /**\\n * @notice Transfer tokens to a specified address.\\n * @param _to the address to transfer to.\\n * @param _value the amount to be transferred.\\n * @return true on success.\\n */\\n function transfer(address _to, uint256 _value) external returns (bool) {\\n require(_to != address(0), \\\"Transfer to zero address\\\");\\n\\n _executeTransfer(msg.sender, _to, _value);\\n\\n emit Transfer(msg.sender, _to, _value);\\n return true;\\n }\\n\\n /**\\n * @notice Transfer tokens from one address to another.\\n * @param _from The address you want to send tokens from.\\n * @param _to The address you want to transfer to.\\n * @param _value The amount of tokens to be transferred.\\n * @return true on success.\\n */\\n function transferFrom(\\n address _from,\\n address _to,\\n uint256 _value\\n ) external returns (bool) {\\n require(_to != address(0), \\\"Transfer to zero address\\\");\\n uint256 userAllowance = allowances[_from][msg.sender];\\n require(_value <= userAllowance, \\\"Allowance exceeded\\\");\\n\\n unchecked {\\n allowances[_from][msg.sender] = userAllowance - _value;\\n }\\n\\n _executeTransfer(_from, _to, _value);\\n\\n emit Transfer(_from, _to, _value);\\n return true;\\n }\\n\\n function _executeTransfer(\\n address _from,\\n address _to,\\n uint256 _value\\n ) internal {\\n (\\n int256 fromRebasingCreditsDiff,\\n int256 fromNonRebasingSupplyDiff\\n ) = _adjustAccount(_from, -_value.toInt256());\\n (\\n int256 toRebasingCreditsDiff,\\n int256 toNonRebasingSupplyDiff\\n ) = _adjustAccount(_to, _value.toInt256());\\n\\n _adjustGlobals(\\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\\n );\\n }\\n\\n function _adjustAccount(address _account, int256 _balanceChange)\\n internal\\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\\n {\\n RebaseOptions state = rebaseState[_account];\\n int256 currentBalance = balanceOf(_account).toInt256();\\n if (currentBalance + _balanceChange < 0) {\\n revert(\\\"Transfer amount exceeds balance\\\");\\n }\\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\\n\\n if (state == RebaseOptions.YieldDelegationSource) {\\n address target = yieldTo[_account];\\n uint256 targetOldBalance = balanceOf(target);\\n uint256 targetNewCredits = _balanceToRebasingCredits(\\n targetOldBalance + newBalance\\n );\\n rebasingCreditsDiff =\\n targetNewCredits.toInt256() -\\n creditBalances[target].toInt256();\\n\\n creditBalances[_account] = newBalance;\\n creditBalances[target] = targetNewCredits;\\n } else if (state == RebaseOptions.YieldDelegationTarget) {\\n uint256 newCredits = _balanceToRebasingCredits(\\n newBalance + creditBalances[yieldFrom[_account]]\\n );\\n rebasingCreditsDiff =\\n newCredits.toInt256() -\\n creditBalances[_account].toInt256();\\n creditBalances[_account] = newCredits;\\n } else {\\n _autoMigrate(_account);\\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\\n _account\\n ];\\n if (alternativeCreditsPerTokenMem > 0) {\\n nonRebasingSupplyDiff = _balanceChange;\\n if (alternativeCreditsPerTokenMem != 1e18) {\\n alternativeCreditsPerToken[_account] = 1e18;\\n }\\n creditBalances[_account] = newBalance;\\n } else {\\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\\n rebasingCreditsDiff =\\n newCredits.toInt256() -\\n creditBalances[_account].toInt256();\\n creditBalances[_account] = newCredits;\\n }\\n }\\n }\\n\\n function _adjustGlobals(\\n int256 _rebasingCreditsDiff,\\n int256 _nonRebasingSupplyDiff\\n ) internal {\\n if (_rebasingCreditsDiff != 0) {\\n rebasingCredits_ = (rebasingCredits_.toInt256() +\\n _rebasingCreditsDiff).toUint256();\\n }\\n if (_nonRebasingSupplyDiff != 0) {\\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\\n _nonRebasingSupplyDiff).toUint256();\\n }\\n }\\n\\n /**\\n * @notice Function to check the amount of tokens that _owner has allowed\\n * to `_spender`.\\n * @param _owner The address which owns the funds.\\n * @param _spender The address which will spend the funds.\\n * @return The number of tokens still available for the _spender.\\n */\\n function allowance(address _owner, address _spender)\\n external\\n view\\n returns (uint256)\\n {\\n return allowances[_owner][_spender];\\n }\\n\\n /**\\n * @notice Approve the passed address to spend the specified amount of\\n * tokens on behalf of msg.sender.\\n * @param _spender The address which will spend the funds.\\n * @param _value The amount of tokens to be spent.\\n * @return true on success.\\n */\\n function approve(address _spender, uint256 _value) external returns (bool) {\\n allowances[msg.sender][_spender] = _value;\\n emit Approval(msg.sender, _spender, _value);\\n return true;\\n }\\n\\n /**\\n * @notice Creates `_amount` tokens and assigns them to `_account`,\\n * increasing the total supply.\\n */\\n function mint(address _account, uint256 _amount) external onlyVault {\\n require(_account != address(0), \\\"Mint to the zero address\\\");\\n\\n // Account\\n (\\n int256 toRebasingCreditsDiff,\\n int256 toNonRebasingSupplyDiff\\n ) = _adjustAccount(_account, _amount.toInt256());\\n // Globals\\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\\n totalSupply = totalSupply + _amount;\\n\\n require(totalSupply < MAX_SUPPLY, \\\"Max supply\\\");\\n emit Transfer(address(0), _account, _amount);\\n }\\n\\n /**\\n * @notice Destroys `_amount` tokens from `_account`,\\n * reducing the total supply.\\n */\\n function burn(address _account, uint256 _amount) external onlyVault {\\n require(_account != address(0), \\\"Burn from the zero address\\\");\\n if (_amount == 0) {\\n return;\\n }\\n\\n // Account\\n (\\n int256 toRebasingCreditsDiff,\\n int256 toNonRebasingSupplyDiff\\n ) = _adjustAccount(_account, -_amount.toInt256());\\n // Globals\\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\\n totalSupply = totalSupply - _amount;\\n\\n emit Transfer(_account, address(0), _amount);\\n }\\n\\n /**\\n * @dev Get the credits per token for an account. Returns a fixed amount\\n * if the account is non-rebasing.\\n * @param _account Address of the account.\\n */\\n function _creditsPerToken(address _account)\\n internal\\n view\\n returns (uint256)\\n {\\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\\n _account\\n ];\\n if (alternativeCreditsPerTokenMem != 0) {\\n return alternativeCreditsPerTokenMem;\\n } else {\\n return rebasingCreditsPerToken_;\\n }\\n }\\n\\n /**\\n * @dev Auto migrate contracts to be non rebasing,\\n * unless they have opted into yield.\\n * @param _account Address of the account.\\n */\\n function _autoMigrate(address _account) internal {\\n uint256 codeLen = _account.code.length;\\n bool isEOA = (codeLen == 0) ||\\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\\n // In previous code versions, contracts would not have had their\\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\\n // therefore we check the actual accounting used on the account as well.\\n if (\\n (!isEOA) &&\\n rebaseState[_account] == RebaseOptions.NotSet &&\\n alternativeCreditsPerToken[_account] == 0\\n ) {\\n _rebaseOptOut(_account);\\n }\\n }\\n\\n /**\\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\\n * also balance that corresponds to those credits. The latter is important\\n * when adjusting the contract's global nonRebasingSupply to circumvent any\\n * possible rounding errors.\\n *\\n * @param _balance Balance of the account.\\n */\\n function _balanceToRebasingCredits(uint256 _balance)\\n internal\\n view\\n returns (uint256 rebasingCredits)\\n {\\n // Rounds up, because we need to ensure that accounts always have\\n // at least the balance that they should have.\\n // Note this should always be used on an absolute account value,\\n // not on a possibly negative diff, because then the rounding would be wrong.\\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\\n }\\n\\n /**\\n * @notice The calling account will start receiving yield after a successful call.\\n * @param _account Address of the account.\\n */\\n function governanceRebaseOptIn(address _account) external onlyGovernor {\\n require(_account != address(0), \\\"Zero address not allowed\\\");\\n _rebaseOptIn(_account);\\n }\\n\\n /**\\n * @notice The calling account will start receiving yield after a successful call.\\n */\\n function rebaseOptIn() external {\\n _rebaseOptIn(msg.sender);\\n }\\n\\n function _rebaseOptIn(address _account) internal {\\n uint256 balance = balanceOf(_account);\\n\\n // prettier-ignore\\n require(\\n alternativeCreditsPerToken[_account] > 0 ||\\n // Accounts may explicitly `rebaseOptIn` regardless of\\n // accounting if they have a 0 balance.\\n creditBalances[_account] == 0\\n ,\\n \\\"Account must be non-rebasing\\\"\\n );\\n RebaseOptions state = rebaseState[_account];\\n // prettier-ignore\\n require(\\n state == RebaseOptions.StdNonRebasing ||\\n state == RebaseOptions.NotSet,\\n \\\"Only standard non-rebasing accounts can opt in\\\"\\n );\\n\\n uint256 newCredits = _balanceToRebasingCredits(balance);\\n\\n // Account\\n rebaseState[_account] = RebaseOptions.StdRebasing;\\n alternativeCreditsPerToken[_account] = 0;\\n creditBalances[_account] = newCredits;\\n // Globals\\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\\n\\n emit AccountRebasingEnabled(_account);\\n }\\n\\n /**\\n * @notice The calling account will no longer receive yield\\n */\\n function rebaseOptOut() external {\\n _rebaseOptOut(msg.sender);\\n }\\n\\n function _rebaseOptOut(address _account) internal {\\n require(\\n alternativeCreditsPerToken[_account] == 0,\\n \\\"Account must be rebasing\\\"\\n );\\n RebaseOptions state = rebaseState[_account];\\n require(\\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\\n \\\"Only standard rebasing accounts can opt out\\\"\\n );\\n\\n uint256 oldCredits = creditBalances[_account];\\n uint256 balance = balanceOf(_account);\\n\\n // Account\\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\\n alternativeCreditsPerToken[_account] = 1e18;\\n creditBalances[_account] = balance;\\n // Globals\\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\\n\\n emit AccountRebasingDisabled(_account);\\n }\\n\\n /**\\n * @notice Distribute yield to users. This changes the exchange rate\\n * between \\\"credits\\\" and OUSD tokens to change rebasing user's balances.\\n * @param _newTotalSupply New total supply of OUSD.\\n */\\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\\n require(totalSupply > 0, \\\"Cannot increase 0 supply\\\");\\n\\n if (totalSupply == _newTotalSupply) {\\n emit TotalSupplyUpdatedHighres(\\n totalSupply,\\n rebasingCredits_,\\n rebasingCreditsPerToken_\\n );\\n return;\\n }\\n\\n totalSupply = _newTotalSupply > MAX_SUPPLY\\n ? MAX_SUPPLY\\n : _newTotalSupply;\\n\\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\\n // round up in the favour of the protocol\\n rebasingCreditsPerToken_ =\\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\\n rebasingSupply;\\n\\n require(rebasingCreditsPerToken_ > 0, \\\"Invalid change in supply\\\");\\n\\n emit TotalSupplyUpdatedHighres(\\n totalSupply,\\n rebasingCredits_,\\n rebasingCreditsPerToken_\\n );\\n }\\n\\n /*\\n * @notice Send the yield from one account to another account.\\n * Each account keeps its own balances.\\n */\\n function delegateYield(address _from, address _to)\\n external\\n onlyGovernorOrStrategist\\n {\\n require(_from != address(0), \\\"Zero from address not allowed\\\");\\n require(_to != address(0), \\\"Zero to address not allowed\\\");\\n\\n require(_from != _to, \\\"Cannot delegate to self\\\");\\n require(\\n yieldFrom[_to] == address(0) &&\\n yieldTo[_to] == address(0) &&\\n yieldFrom[_from] == address(0) &&\\n yieldTo[_from] == address(0),\\n \\\"Blocked by existing yield delegation\\\"\\n );\\n RebaseOptions stateFrom = rebaseState[_from];\\n RebaseOptions stateTo = rebaseState[_to];\\n\\n require(\\n stateFrom == RebaseOptions.NotSet ||\\n stateFrom == RebaseOptions.StdNonRebasing ||\\n stateFrom == RebaseOptions.StdRebasing,\\n \\\"Invalid rebaseState from\\\"\\n );\\n\\n require(\\n stateTo == RebaseOptions.NotSet ||\\n stateTo == RebaseOptions.StdNonRebasing ||\\n stateTo == RebaseOptions.StdRebasing,\\n \\\"Invalid rebaseState to\\\"\\n );\\n\\n if (alternativeCreditsPerToken[_from] == 0) {\\n _rebaseOptOut(_from);\\n }\\n if (alternativeCreditsPerToken[_to] > 0) {\\n _rebaseOptIn(_to);\\n }\\n\\n uint256 fromBalance = balanceOf(_from);\\n uint256 toBalance = balanceOf(_to);\\n uint256 oldToCredits = creditBalances[_to];\\n uint256 newToCredits = _balanceToRebasingCredits(\\n fromBalance + toBalance\\n );\\n\\n // Set up the bidirectional links\\n yieldTo[_from] = _to;\\n yieldFrom[_to] = _from;\\n\\n // Local\\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\\n alternativeCreditsPerToken[_from] = 1e18;\\n creditBalances[_from] = fromBalance;\\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\\n creditBalances[_to] = newToCredits;\\n\\n // Global\\n int256 creditsChange = newToCredits.toInt256() -\\n oldToCredits.toInt256();\\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\\n emit YieldDelegated(_from, _to);\\n }\\n\\n /*\\n * @notice Stop sending the yield from one account to another account.\\n */\\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\\n // Require a delegation, which will also ensure a valid delegation\\n require(yieldTo[_from] != address(0), \\\"Zero address not allowed\\\");\\n\\n address to = yieldTo[_from];\\n uint256 fromBalance = balanceOf(_from);\\n uint256 toBalance = balanceOf(to);\\n uint256 oldToCredits = creditBalances[to];\\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\\n\\n // Remove the bidirectional links\\n yieldFrom[to] = address(0);\\n yieldTo[_from] = address(0);\\n\\n // Local\\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\\n creditBalances[_from] = fromBalance;\\n rebaseState[to] = RebaseOptions.StdRebasing;\\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\\n creditBalances[to] = newToCredits;\\n\\n // Global\\n int256 creditsChange = newToCredits.toInt256() -\\n oldToCredits.toInt256();\\n _adjustGlobals(creditsChange, fromBalance.toInt256());\\n emit YieldUndelegated(_from, to);\\n }\\n}\\n\",\"keccak256\":\"0x73439bef6569f5adf6f5ce2cb54a5f0d3109d4819457532236e172a7091980a9\",\"license\":\"BUSL-1.1\"},\"contracts/utils/Helpers.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { IBasicToken } from \\\"../interfaces/IBasicToken.sol\\\";\\n\\nlibrary Helpers {\\n /**\\n * @notice Fetch the `symbol()` from an ERC20 token\\n * @dev Grabs the `symbol()` from a contract\\n * @param _token Address of the ERC20 token\\n * @return string Symbol of the ERC20 token\\n */\\n function getSymbol(address _token) internal view returns (string memory) {\\n string memory symbol = IBasicToken(_token).symbol();\\n return symbol;\\n }\\n\\n /**\\n * @notice Fetch the `decimals()` from an ERC20 token\\n * @dev Grabs the `decimals()` from a contract and fails if\\n * the decimal value does not live within a certain range\\n * @param _token Address of the ERC20 token\\n * @return uint256 Decimals of the ERC20 token\\n */\\n function getDecimals(address _token) internal view returns (uint256) {\\n uint256 decimals = IBasicToken(_token).decimals();\\n require(\\n decimals >= 4 && decimals <= 18,\\n \\\"Token must have sufficient decimal places\\\"\\n );\\n\\n return decimals;\\n }\\n}\\n\",\"keccak256\":\"0x4366f8d90b34c1eef8bbaaf369b1e5cd59f04027bb3c111f208eaee65bbc0346\",\"license\":\"BUSL-1.1\"},\"contracts/utils/Initializable.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base contract any contracts that need to initialize state after deployment.\\n * @author Origin Protocol Inc\\n */\\nabstract contract Initializable {\\n /**\\n * @dev Indicates that the contract has been initialized.\\n */\\n bool private initialized;\\n\\n /**\\n * @dev Indicates that the contract is in the process of being initialized.\\n */\\n bool private initializing;\\n\\n /**\\n * @dev Modifier to protect an initializer function from being invoked twice.\\n */\\n modifier initializer() {\\n require(\\n initializing || !initialized,\\n \\\"Initializable: contract is already initialized\\\"\\n );\\n\\n bool isTopLevelCall = !initializing;\\n if (isTopLevelCall) {\\n initializing = true;\\n initialized = true;\\n }\\n\\n _;\\n\\n if (isTopLevelCall) {\\n initializing = false;\\n }\\n }\\n\\n uint256[50] private ______gap;\\n}\\n\",\"keccak256\":\"0x50d39ebf38a3d3111f2b77a6c75ece1d4ae731552fec4697ab16fcf6c0d4d5e8\",\"license\":\"BUSL-1.1\"},\"contracts/utils/InitializableAbstractStrategy.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base contract for vault strategies.\\n * @author Origin Protocol Inc\\n */\\nimport { IERC20 } from \\\"@openzeppelin/contracts/token/ERC20/IERC20.sol\\\";\\nimport { SafeERC20 } from \\\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\\\";\\n\\nimport { Initializable } from \\\"../utils/Initializable.sol\\\";\\nimport { Governable } from \\\"../governance/Governable.sol\\\";\\nimport { IVault } from \\\"../interfaces/IVault.sol\\\";\\n\\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\\n using SafeERC20 for IERC20;\\n\\n event PTokenAdded(address indexed _asset, address _pToken);\\n event PTokenRemoved(address indexed _asset, address _pToken);\\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\\n event RewardTokenCollected(\\n address recipient,\\n address rewardToken,\\n uint256 amount\\n );\\n event RewardTokenAddressesUpdated(\\n address[] _oldAddresses,\\n address[] _newAddresses\\n );\\n event HarvesterAddressesUpdated(\\n address _oldHarvesterAddress,\\n address _newHarvesterAddress\\n );\\n\\n /// @notice Address of the underlying platform\\n address public immutable platformAddress;\\n /// @notice Address of the OToken vault\\n address public immutable vaultAddress;\\n\\n /// @dev Replaced with an immutable variable\\n // slither-disable-next-line constable-states\\n address private _deprecated_platformAddress;\\n\\n /// @dev Replaced with an immutable\\n // slither-disable-next-line constable-states\\n address private _deprecated_vaultAddress;\\n\\n /// @notice asset => pToken (Platform Specific Token Address)\\n mapping(address => address) public assetToPToken;\\n\\n /// @notice Full list of all assets supported by the strategy\\n address[] internal assetsMapped;\\n\\n // Deprecated: Reward token address\\n // slither-disable-next-line constable-states\\n address private _deprecated_rewardTokenAddress;\\n\\n // Deprecated: now resides in Harvester's rewardTokenConfigs\\n // slither-disable-next-line constable-states\\n uint256 private _deprecated_rewardLiquidationThreshold;\\n\\n /// @notice Address of the Harvester contract allowed to collect reward tokens\\n address public harvesterAddress;\\n\\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\\n address[] public rewardTokenAddresses;\\n\\n /* Reserved for future expansion. Used to be 100 storage slots\\n * and has decreased to accommodate:\\n * - harvesterAddress\\n * - rewardTokenAddresses\\n */\\n int256[98] private _reserved;\\n\\n struct BaseStrategyConfig {\\n address platformAddress; // Address of the underlying platform\\n address vaultAddress; // Address of the OToken's Vault\\n }\\n\\n /**\\n * @dev Verifies that the caller is the Governor or Strategist.\\n */\\n modifier onlyGovernorOrStrategist() {\\n require(\\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\\n \\\"Caller is not the Strategist or Governor\\\"\\n );\\n _;\\n }\\n\\n /**\\n * @param _config The platform and OToken vault addresses\\n */\\n constructor(BaseStrategyConfig memory _config) {\\n platformAddress = _config.platformAddress;\\n vaultAddress = _config.vaultAddress;\\n }\\n\\n /**\\n * @dev Internal initialize function, to set up initial internal state\\n * @param _rewardTokenAddresses Address of reward token for platform\\n * @param _assets Addresses of initial supported assets\\n * @param _pTokens Platform Token corresponding addresses\\n */\\n function _initialize(\\n address[] memory _rewardTokenAddresses,\\n address[] memory _assets,\\n address[] memory _pTokens\\n ) internal {\\n rewardTokenAddresses = _rewardTokenAddresses;\\n\\n uint256 assetCount = _assets.length;\\n require(assetCount == _pTokens.length, \\\"Invalid input arrays\\\");\\n for (uint256 i = 0; i < assetCount; ++i) {\\n _setPTokenAddress(_assets[i], _pTokens[i]);\\n }\\n }\\n\\n /**\\n * @notice Collect accumulated reward token and send to Vault.\\n */\\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\\n _collectRewardTokens();\\n }\\n\\n /**\\n * @dev Default implementation that transfers reward tokens to the Harvester\\n * Implementing strategies need to add custom logic to collect the rewards.\\n */\\n function _collectRewardTokens() internal virtual {\\n uint256 rewardTokenCount = rewardTokenAddresses.length;\\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\\n uint256 balance = rewardToken.balanceOf(address(this));\\n if (balance > 0) {\\n emit RewardTokenCollected(\\n harvesterAddress,\\n address(rewardToken),\\n balance\\n );\\n rewardToken.safeTransfer(harvesterAddress, balance);\\n }\\n }\\n }\\n\\n /**\\n * @dev Verifies that the caller is the Vault.\\n */\\n modifier onlyVault() {\\n require(msg.sender == vaultAddress, \\\"Caller is not the Vault\\\");\\n _;\\n }\\n\\n /**\\n * @dev Verifies that the caller is the Harvester.\\n */\\n modifier onlyHarvester() {\\n require(msg.sender == harvesterAddress, \\\"Caller is not the Harvester\\\");\\n _;\\n }\\n\\n /**\\n * @dev Verifies that the caller is the Vault or Governor.\\n */\\n modifier onlyVaultOrGovernor() {\\n require(\\n msg.sender == vaultAddress || msg.sender == governor(),\\n \\\"Caller is not the Vault or Governor\\\"\\n );\\n _;\\n }\\n\\n /**\\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\\n */\\n modifier onlyVaultOrGovernorOrStrategist() {\\n require(\\n msg.sender == vaultAddress ||\\n msg.sender == governor() ||\\n msg.sender == IVault(vaultAddress).strategistAddr(),\\n \\\"Caller is not the Vault, Governor, or Strategist\\\"\\n );\\n _;\\n }\\n\\n /**\\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\\n * @param _rewardTokenAddresses Array of reward token addresses\\n */\\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\\n external\\n onlyGovernor\\n {\\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\\n require(\\n _rewardTokenAddresses[i] != address(0),\\n \\\"Can not set an empty address as a reward token\\\"\\n );\\n }\\n\\n emit RewardTokenAddressesUpdated(\\n rewardTokenAddresses,\\n _rewardTokenAddresses\\n );\\n rewardTokenAddresses = _rewardTokenAddresses;\\n }\\n\\n /**\\n * @notice Get the reward token addresses.\\n * @return address[] the reward token addresses.\\n */\\n function getRewardTokenAddresses()\\n external\\n view\\n returns (address[] memory)\\n {\\n return rewardTokenAddresses;\\n }\\n\\n /**\\n * @notice Provide support for asset by passing its pToken address.\\n * This method can only be called by the system Governor\\n * @param _asset Address for the asset\\n * @param _pToken Address for the corresponding platform token\\n */\\n function setPTokenAddress(address _asset, address _pToken)\\n external\\n virtual\\n onlyGovernor\\n {\\n _setPTokenAddress(_asset, _pToken);\\n }\\n\\n /**\\n * @notice Remove a supported asset by passing its index.\\n * This method can only be called by the system Governor\\n * @param _assetIndex Index of the asset to be removed\\n */\\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\\n require(_assetIndex < assetsMapped.length, \\\"Invalid index\\\");\\n address asset = assetsMapped[_assetIndex];\\n address pToken = assetToPToken[asset];\\n\\n if (_assetIndex < assetsMapped.length - 1) {\\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\\n }\\n assetsMapped.pop();\\n assetToPToken[asset] = address(0);\\n\\n emit PTokenRemoved(asset, pToken);\\n }\\n\\n /**\\n * @notice Provide support for asset by passing its pToken address.\\n * Add to internal mappings and execute the platform specific,\\n * abstract method `_abstractSetPToken`\\n * @param _asset Address for the asset\\n * @param _pToken Address for the corresponding platform token\\n */\\n function _setPTokenAddress(address _asset, address _pToken) internal {\\n require(assetToPToken[_asset] == address(0), \\\"pToken already set\\\");\\n require(\\n _asset != address(0) && _pToken != address(0),\\n \\\"Invalid addresses\\\"\\n );\\n\\n assetToPToken[_asset] = _pToken;\\n assetsMapped.push(_asset);\\n\\n emit PTokenAdded(_asset, _pToken);\\n\\n _abstractSetPToken(_asset, _pToken);\\n }\\n\\n /**\\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\\n * strategy contracts, i.e. mistaken sends.\\n * @param _asset Address for the asset\\n * @param _amount Amount of the asset to transfer\\n */\\n function transferToken(address _asset, uint256 _amount)\\n public\\n virtual\\n onlyGovernor\\n {\\n require(!supportsAsset(_asset), \\\"Cannot transfer supported asset\\\");\\n IERC20(_asset).safeTransfer(governor(), _amount);\\n }\\n\\n /**\\n * @notice Set the Harvester contract that can collect rewards.\\n * @param _harvesterAddress Address of the harvester contract.\\n */\\n function setHarvesterAddress(address _harvesterAddress)\\n external\\n onlyGovernor\\n {\\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\\n harvesterAddress = _harvesterAddress;\\n }\\n\\n /***************************************\\n Abstract\\n ****************************************/\\n\\n function _abstractSetPToken(address _asset, address _pToken)\\n internal\\n virtual;\\n\\n function safeApproveAllTokens() external virtual;\\n\\n /**\\n * @notice Deposit an amount of assets into the platform\\n * @param _asset Address for the asset\\n * @param _amount Units of asset to deposit\\n */\\n function deposit(address _asset, uint256 _amount) external virtual;\\n\\n /**\\n * @notice Deposit all supported assets in this strategy contract to the platform\\n */\\n function depositAll() external virtual;\\n\\n /**\\n * @notice Withdraw an `amount` of assets from the platform and\\n * send to the `_recipient`.\\n * @param _recipient Address to which the asset should be sent\\n * @param _asset Address of the asset\\n * @param _amount Units of asset to withdraw\\n */\\n function withdraw(\\n address _recipient,\\n address _asset,\\n uint256 _amount\\n ) external virtual;\\n\\n /**\\n * @notice Withdraw all supported assets from platform and\\n * sends to the OToken's Vault.\\n */\\n function withdrawAll() external virtual;\\n\\n /**\\n * @notice Get the total asset value held in the platform.\\n * This includes any interest that was generated since depositing.\\n * @param _asset Address of the asset\\n * @return balance Total value of the asset in the platform\\n */\\n function checkBalance(address _asset)\\n external\\n view\\n virtual\\n returns (uint256 balance);\\n\\n /**\\n * @notice Check if an asset is supported.\\n * @param _asset Address of the asset\\n * @return bool Whether asset is supported\\n */\\n function supportsAsset(address _asset) public view virtual returns (bool);\\n}\\n\",\"keccak256\":\"0x0160d435384d75e8764f4a916764ba47c87fda46872ca5900d46e5e80e956ff9\",\"license\":\"BUSL-1.1\"},\"contracts/vault/VaultStorage.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title OToken VaultStorage contract\\n * @notice The VaultStorage contract defines the storage for the Vault contracts\\n * @author Origin Protocol Inc\\n */\\n\\nimport { IERC20 } from \\\"@openzeppelin/contracts/token/ERC20/IERC20.sol\\\";\\nimport { SafeERC20 } from \\\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\\\";\\nimport { Address } from \\\"@openzeppelin/contracts/utils/Address.sol\\\";\\n\\nimport { IStrategy } from \\\"../interfaces/IStrategy.sol\\\";\\nimport { Governable } from \\\"../governance/Governable.sol\\\";\\nimport { OUSD } from \\\"../token/OUSD.sol\\\";\\nimport { Initializable } from \\\"../utils/Initializable.sol\\\";\\nimport \\\"../utils/Helpers.sol\\\";\\n\\ncontract VaultStorage is Initializable, Governable {\\n using SafeERC20 for IERC20;\\n\\n event AssetSupported(address _asset);\\n event AssetRemoved(address _asset);\\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\\n event StrategyApproved(address _addr);\\n event StrategyRemoved(address _addr);\\n event Mint(address _addr, uint256 _value);\\n event Redeem(address _addr, uint256 _value);\\n event CapitalPaused();\\n event CapitalUnpaused();\\n event RebasePaused();\\n event RebaseUnpaused();\\n event VaultBufferUpdated(uint256 _vaultBuffer);\\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\\n event PriceProviderUpdated(address _priceProvider);\\n event AllocateThresholdUpdated(uint256 _threshold);\\n event RebaseThresholdUpdated(uint256 _threshold);\\n event StrategistUpdated(address _address);\\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\\n event TrusteeFeeBpsChanged(uint256 _basis);\\n event TrusteeAddressChanged(address _address);\\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\\n event SwapperChanged(address _address);\\n event SwapAllowedUndervalueChanged(uint256 _basis);\\n event SwapSlippageChanged(address _asset, uint256 _basis);\\n event Swapped(\\n address indexed _fromAsset,\\n address indexed _toAsset,\\n uint256 _fromAssetAmount,\\n uint256 _toAssetAmount\\n );\\n event StrategyAddedToMintWhitelist(address indexed strategy);\\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\\n event DripDurationChanged(uint256 dripDuration);\\n event WithdrawalRequested(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount,\\n uint256 _queued\\n );\\n event WithdrawalClaimed(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount\\n );\\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\\n\\n // Since we are proxy, all state should be uninitalized.\\n // Since this storage contract does not have logic directly on it\\n // we should not be checking for to see if these variables can be constant.\\n // slither-disable-start uninitialized-state\\n // slither-disable-start constable-states\\n\\n // Assets supported by the Vault, i.e. Stablecoins\\n enum UnitConversion {\\n DECIMALS,\\n GETEXCHANGERATE\\n }\\n // Changed to fit into a single storage slot so the decimals needs to be recached\\n struct Asset {\\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\\n // redeeming or checking balance of assets.\\n bool isSupported;\\n UnitConversion unitConversion;\\n uint8 decimals;\\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\\n // For example 40 == 0.4% slippage\\n uint16 allowedOracleSlippageBps;\\n }\\n\\n /// @dev mapping of supported vault assets to their configuration\\n mapping(address => Asset) internal assets;\\n /// @dev list of all assets supported by the vault.\\n address[] internal allAssets;\\n\\n // Strategies approved for use by the Vault\\n struct Strategy {\\n bool isSupported;\\n uint256 _deprecated; // Deprecated storage slot\\n }\\n /// @dev mapping of strategy contracts to their configuration\\n mapping(address => Strategy) public strategies;\\n /// @dev list of all vault strategies\\n address[] internal allStrategies;\\n\\n /// @notice Address of the Oracle price provider contract\\n address public priceProvider;\\n /// @notice pause rebasing if true\\n bool public rebasePaused;\\n /// @notice pause operations that change the OToken supply.\\n /// eg mint, redeem, allocate, mint/burn for strategy\\n bool public capitalPaused;\\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\\n uint256 public redeemFeeBps;\\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\\n uint256 public vaultBuffer;\\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\\n uint256 public autoAllocateThreshold;\\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\\n uint256 public rebaseThreshold;\\n\\n /// @dev Address of the OToken token. eg OUSD or OETH.\\n OUSD public oUSD;\\n\\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\\n // keccak256(\\\"OUSD.vault.governor.admin.impl\\\");\\n bytes32 public constant adminImplPosition =\\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\\n\\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\\n address private _deprecated_rebaseHooksAddr = address(0);\\n\\n /// @dev Deprecated: Address of Uniswap\\n address private _deprecated_uniswapAddr = address(0);\\n\\n /// @notice Address of the Strategist\\n address public strategistAddr = address(0);\\n\\n /// @notice Mapping of asset address to the Strategy that they should automatically\\n // be allocated to\\n mapping(address => address) public assetDefaultStrategies;\\n\\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\\n uint256 public maxSupplyDiff;\\n\\n /// @notice Trustee contract that can collect a percentage of yield\\n address public trusteeAddress;\\n\\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\\n uint256 public trusteeFeeBps;\\n\\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\\n address[] private _deprecated_swapTokens;\\n\\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\\n\\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\\n\\n address public ousdMetaStrategy;\\n\\n /// @notice How much OTokens are currently minted by the strategy\\n int256 public netOusdMintedForStrategy;\\n\\n /// @notice How much net total OTokens are allowed to be minted by all strategies\\n uint256 public netOusdMintForStrategyThreshold;\\n\\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\\n\\n /// @notice Collateral swap configuration.\\n /// @dev is packed into a single storage slot to save gas.\\n struct SwapConfig {\\n // Contract that swaps the vault's collateral assets\\n address swapper;\\n // Max allowed percentage the total value can drop below the total supply in basis points.\\n // For example 100 == 1%\\n uint16 allowedUndervalueBps;\\n }\\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\\n\\n // List of strategies that can mint oTokens directly\\n // Used in OETHBaseVaultCore\\n mapping(address => bool) public isMintWhitelistedStrategy;\\n\\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\\n address private _deprecated_dripper;\\n\\n /// Withdrawal Queue Storage /////\\n\\n struct WithdrawalQueueMetadata {\\n // cumulative total of all withdrawal requests included the ones that have already been claimed\\n uint128 queued;\\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\\n uint128 claimable;\\n // total of all the requests that have been claimed\\n uint128 claimed;\\n // index of the next withdrawal request starting at 0\\n uint128 nextWithdrawalIndex;\\n }\\n\\n /// @notice Global metadata for the withdrawal queue including:\\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\\n /// claimed - total of all the requests that have been claimed\\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\\n\\n struct WithdrawalRequest {\\n address withdrawer;\\n bool claimed;\\n uint40 timestamp; // timestamp of the withdrawal request\\n // Amount of oTokens to redeem. eg OETH\\n uint128 amount;\\n // cumulative total of all withdrawal requests including this one.\\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\\n uint128 queued;\\n }\\n\\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\\n\\n /// @notice Sets a minimum delay that is required to elapse between\\n /// requesting async withdrawals and claiming the request.\\n /// When set to 0 async withdrawals are disabled.\\n uint256 public withdrawalClaimDelay;\\n\\n /// @notice Time in seconds that the vault last rebased yield.\\n uint64 public lastRebase;\\n\\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\\n uint64 public dripDuration;\\n\\n /// @notice max rebase percentage per second\\n /// Can be used to set maximum yield of the protocol,\\n /// spreading out yield over time\\n uint64 public rebasePerSecondMax;\\n\\n /// @notice target rebase rate limit, based on past rates and funds available.\\n uint64 public rebasePerSecondTarget;\\n\\n uint256 internal constant MAX_REBASE = 0.02 ether;\\n uint256 internal constant MAX_REBASE_PER_SECOND =\\n uint256(0.05 ether) / 1 days;\\n\\n // For future use\\n uint256[43] private __gap;\\n\\n // slither-disable-end constable-states\\n // slither-disable-end uninitialized-state\\n\\n /**\\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\\n * @param newImpl address of the implementation\\n */\\n function setAdminImpl(address newImpl) external onlyGovernor {\\n require(\\n Address.isContract(newImpl),\\n \\\"new implementation is not a contract\\\"\\n );\\n bytes32 position = adminImplPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newImpl)\\n }\\n }\\n}\\n\",\"keccak256\":\"0xe8c1056879e4d67e0085a30a525a4cb23b954ade0f22fce502278f35b9c69d3b\",\"license\":\"BUSL-1.1\"}},\"version\":1}", + "bytecode": "0x6101a060405234801561001157600080fd5b50604051616156380380616156833981016040819052610030916100a1565b6020870180516033805460ff191690556001600160a01b0397881660805291871660a05293861660c052851660e052610100919091529351831661012052518216610140528116610160521661018052610178565b80516001600160a01b038116811461009c57600080fd5b919050565b60008060008060008060008789036101008112156100be57600080fd5b60408112156100cc57600080fd5b50604080519081016001600160401b03811182821017156100fd57634e487b7160e01b600052604160045260246000fd5b60405261010989610085565b815261011760208a01610085565b6020820152965061012a60408901610085565b955061013860608901610085565b945061014660808901610085565b60a0890151909450925061015c60c08901610085565b915061016a60e08901610085565b905092959891949750929550565b60805160a05160c05160e0516101005161012051610140516101605161018051615e0961034d600039600081816103ed01528181610c7f01528181610e6a0152818161133e0152613e8c0152600081816104fd015281816112b901526133160152600081816106150152818161163a01528181612363015281816124cd015281816137e501526138bf01526000610c4b01526000818161080f0152611e3101526000818161093e01528181610ef2015281816122a00152818161258d01528181612902015281816143ee015261463c01526000818161099201528181610e9201528181610fb901528181611289015281816113660152818161147801528181611983015281816132e6015281816133c101526135fb015260008181610ba001526120a601526000818161041f01528181610a8301528181610afa01528181610dc6015281816116af01528181611b4d01528181611bb101528181611d7601528181611f2f0152818161244d015281816124ee0152818161287c015281816129310152818161385a0152818161394a015281816139e301528181613f82015281816140030152818161404501528181614296015281816143680152818161441d015281816145b6015261466b0152615e096000f3fe6080604052600436106103dd5760003560e01c8063853828b6116101fd578063b16b7d0b11610118578063d9caed12116100ab578063de5f62681161007a578063de5f626814610cb7578063e0abfca614610ccc578063e752923914610cfc578063ee7afe2d14610d12578063f6ca71b014610d2757600080fd5b8063d9caed1214610c19578063dbe55e5614610c39578063dd505df614610c6d578063de34d71314610ca157600080fd5b8063cceab750116100e7578063cceab75014610b8e578063d059f6ef14610bc2578063d38bfff414610bd9578063d7c3003514610bf957600080fd5b8063b16b7d0b14610b1c578063b6e2b52014610b39578063c2e1e3f414610b59578063c7af335214610b7957600080fd5b806396d538bb11610190578063aa388af61161015f578063aa388af614610a66578063ab12edf514610ab3578063ad1728cb14610ad3578063ad5c464814610ae857600080fd5b806396d538bb146109d45780639da0e462146109f4578063a3b81e7314610a31578063a4f98af414610a5157600080fd5b80639092c31c116101cc5780639092c31c1461092c5780639136616a14610960578063916497511461098057806392555aea146109b457600080fd5b8063853828b6146108b257806387bae867146108c75780638ae99cea146108ec5780638d7c0e461461090c57600080fd5b80635205c380116102f857806367c7066c1161028b5780637260f8261161025a5780637260f826146108315780637b2d9b2c146108515780637b8962f714610871578063842f5c46146108875780638456cb591461089d57600080fd5b806367c7066c1461079d5780636e811d38146107bd5780636ef38795146107dd578063714897df146107fd57600080fd5b80635d36b190116102c75780635d36b1901461073c5780635f51522614610751578063630923831461077157806366e3667e1461078757600080fd5b80635205c380146106c357806359b80c0a146106e35780635a063f63146107035780635c975abb1461071857600080fd5b80633434c8a41161037057806343be647e1161033f57806343be647e1461065757806347e7ef2414610677578063484be812146106975780634a675f31146106ad57600080fd5b80633434c8a4146105ca5780633c864959146105ed578063430bf08a14610603578063435356d11461063757600080fd5b80630fc3b4c4116103ac5780630fc3b4c41461053f5780631072cbea1461057557806313cf69dd1461059557806322495dc8146105aa57600080fd5b806307a7d4ac146104995780630c340a24146104d65780630df1ecfd146104eb5780630ed57b3a1461051f57600080fd5b3661049457336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614806104415750336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016145b6104925760405162461bcd60e51b815260206004820152601e60248201527f457468206e6f742066726f6d20616c6c6f77656420636f6e747261637473000060448201526064015b60405180910390fd5b005b600080fd5b3480156104a557600080fd5b50603a546104b9906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156104e257600080fd5b506104b9610d49565b3480156104f757600080fd5b506104b97f000000000000000000000000000000000000000000000000000000000000000081565b34801561052b57600080fd5b5061049261053a366004614d8a565b610d66565b34801561054b57600080fd5b506104b961055a366004614dc3565b609f602052600090815260409020546001600160a01b031681565b34801561058157600080fd5b50610492610590366004614de0565b610d98565b3480156105a157600080fd5b50610492610e53565b3480156105b657600080fd5b506104926105c5366004614f39565b610ef0565b3480156105d657600080fd5b506105df61102b565b6040519081526020016104cd565b3480156105f957600080fd5b506105df60695481565b34801561060f57600080fd5b506104b97f000000000000000000000000000000000000000000000000000000000000000081565b34801561064357600080fd5b50610492610652366004615066565b6111a5565b34801561066357600080fd5b50610492610672366004615142565b6113d9565b34801561068357600080fd5b50610492610692366004614de0565b61162f565b3480156106a357600080fd5b506105df606a5481565b3480156106b957600080fd5b506105df60395481565b3480156106cf57600080fd5b506104926106de3660046151b1565b61172a565b3480156106ef57600080fd5b506104926106fe3660046151e2565b611789565b34801561070f57600080fd5b50610492611a04565b34801561072457600080fd5b5060335460ff165b60405190151581526020016104cd565b34801561074857600080fd5b50610492611aa3565b34801561075d57600080fd5b506105df61076c366004614dc3565b611b49565b34801561077d57600080fd5b506105df611c2081565b34801561079357600080fd5b506105df60345481565b3480156107a957600080fd5b5060a3546104b9906001600160a01b031681565b3480156107c957600080fd5b506104926107d8366004614dc3565b611c4b565b3480156107e957600080fd5b506104926107f83660046152a0565b611cc1565b34801561080957600080fd5b506105df7f000000000000000000000000000000000000000000000000000000000000000081565b34801561083d57600080fd5b506036546104b9906001600160a01b031681565b34801561085d57600080fd5b506104b961086c3660046151b1565b612274565b34801561087d57600080fd5b506105df60375481565b34801561089357600080fd5b506105df60685481565b3480156108a957600080fd5b5061049261229e565b3480156108be57600080fd5b50610492612358565b3480156108d357600080fd5b506033546104b99061010090046001600160a01b031681565b3480156108f857600080fd5b50610492610907366004614dc3565b61251b565b34801561091857600080fd5b506104926109273660046152e1565b61258b565b34801561093857600080fd5b506104b97f000000000000000000000000000000000000000000000000000000000000000081565b34801561096c57600080fd5b5061049261097b3660046151b1565b612a48565b34801561098c57600080fd5b506104b97f000000000000000000000000000000000000000000000000000000000000000081565b3480156109c057600080fd5b506104926109cf36600461530d565b612c14565b3480156109e057600080fd5b506104926109ef3660046152a0565b612fbe565b348015610a0057600080fd5b50610a24610a0f3660046151b1565b60356020526000908152604090205460ff1681565b6040516104cd91906153d7565b348015610a3d57600080fd5b50610492610a4c366004614dc3565b6130d5565b348015610a5d57600080fd5b5061072c613143565b348015610a7257600080fd5b5061072c610a81366004614dc3565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811691161490565b348015610abf57600080fd5b50610492610ace3660046153ff565b6131e3565b348015610adf57600080fd5b506104926132cf565b348015610af457600080fd5b506104b97f000000000000000000000000000000000000000000000000000000000000000081565b348015610b2857600080fd5b506105df6801bc16d674ec80000081565b348015610b4557600080fd5b50610492610b54366004614f39565b613386565b348015610b6557600080fd5b50610492610b74366004614dc3565b6133fa565b348015610b8557600080fd5b5061072c613487565b348015610b9a57600080fd5b506104b97f000000000000000000000000000000000000000000000000000000000000000081565b348015610bce57600080fd5b506105df6101075481565b348015610be557600080fd5b50610492610bf4366004614dc3565b6134b8565b348015610c0557600080fd5b50610492610c14366004615421565b61355c565b348015610c2557600080fd5b50610492610c3436600461549a565b6137da565b348015610c4557600080fd5b506104b97f000000000000000000000000000000000000000000000000000000000000000081565b348015610c7957600080fd5b506104b97f000000000000000000000000000000000000000000000000000000000000000081565b348015610cad57600080fd5b506105df60385481565b348015610cc357600080fd5b506104926138b4565b348015610cd857600080fd5b5061072c610ce7366004614dc3565b603b6020526000908152604090205460ff1681565b348015610d0857600080fd5b506105df606b5481565b348015610d1e57600080fd5b50610492613a12565b348015610d3357600080fd5b50610d3c613a9c565b6040516104cd91906154db565b6000610d61600080516020615db48339815191525490565b905090565b610d6e613487565b610d8a5760405162461bcd60e51b815260040161048990615527565b610d948282613afe565b5050565b610da0613487565b610dbc5760405162461bcd60e51b815260040161048990615527565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811690831603610e375760405162461bcd60e51b815260206004820152601f60248201527f43616e6e6f74207472616e7366657220737570706f72746564206173736574006044820152606401610489565b610d94610e42610d49565b6001600160a01b0384169083613c5d565b6040516336f370b360e21b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063dbcdc2cc90602401600060405180830381600087803b158015610ed657600080fd5b505af1158015610eea573d6000803e3d6000fd5b50505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663570d8e1d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f72919061555e565b6001600160a01b0316336001600160a01b031614610fa25760405162461bcd60e51b81526004016104899061557b565b60405163bc26e7e560e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063bc26e7e590610ff4903090879087908790600401615641565b600060405180830381600087803b15801561100e57600080fd5b505af1158015611022573d6000803e3d6000fd5b50505050505050565b600080516020615d9483398151915280546000919060011981016110615760405162461bcd60e51b815260040161048990615684565b6002825560335460ff166110875760405162461bcd60e51b8152600401610489906156ac565b603a546001600160a01b031633146110d75760405162461bcd60e51b81526020600482015260136024820152724e6f742074617267657420737472617465677960681b6044820152606401610489565b60395492506000831161112c5760405162461bcd60e51b815260206004820152601c60248201527f4e6f20636f6e736f6c69646174696f6e20696e2070726f6772657373000000006044820152606401610489565b826034600082825461113e91906156f0565b90915550506000603955603a80546001600160a01b0319169055611160613cb4565b6034546040805185815260208101929092527fb7f9b24f2efc7c0499fca5fd498666e42547910efe905fd5c16f835af7781990910160405180910390a1506001905590565b6111ad613487565b6111c95760405162461bcd60e51b815260040161048990615527565b600054610100900460ff16806111e2575060005460ff16155b6112455760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610489565b600054610100900460ff16158015611267576000805461ffff19166101011790555b611272848484613d20565b60405163095ea7b360e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015260001960248301527f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b3906044016020604051808303816000875af1158015611302573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113269190615703565b506040516336f370b360e21b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063dbcdc2cc90602401600060405180830381600087803b1580156113aa57600080fd5b505af11580156113be573d6000803e3d6000fd5b505050508015610eea576000805461ff001916905550505050565b60335461010090046001600160a01b031633146114085760405162461bcd60e51b815260040161048990615720565b60335460ff161561142b5760405162461bcd60e51b815260040161048990615757565b600080516020615d948339815191528054600119810161145d5760405162461bcd60e51b815260040161048990615684565b600282556040516332afd02f60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906332afd02f906114b3908990899089908990600401615881565b600060405180830381600087803b1580156114cd57600080fd5b505af11580156114e1573d6000803e3d6000fd5b5050505060008060005b8781101561162057888882818110611505576115056158a8565b905060200281019061151791906158be565b604051611525929190615904565b604080519182900390912060008181526035602052919091205490935060ff169150600282600481111561155b5761155b6153c1565b1461159f5760405162461bcd60e51b815260206004820152601460248201527315985b1a59185d1bdc881b9bdd081cdd185ad95960621b6044820152606401610489565b6000838152603560205260409020805460ff19166003179055827f8c2e15303eb94e531acc988c2a01d1193bdaaa15eda7f16dda85316ed463578d8a8a848181106115ec576115ec6158a8565b90506020028101906115fe91906158be565b8a8a6040516116109493929190615914565b60405180910390a26001016114eb565b50505060018255505050505050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146116775760405162461bcd60e51b815260040161048990615928565b600080516020615d94833981519152805460011981016116a95760405162461bcd60e51b815260040161048990615684565b600282557f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b0316146116fe5760405162461bcd60e51b81526004016104899061595f565b826101076000828254611711919061598a565b9091555061172190508484613dd3565b50600190555050565b611732613487565b61174e5760405162461bcd60e51b815260040161048990615527565b60378190556040518181527fe26b067424903962f951f568e52ec9a3bbe1589526ea54a4e69ca6eaae1a4c779060200160405180910390a150565b60335461010090046001600160a01b031633146117b85760405162461bcd60e51b815260040161048990615720565b60335460ff16156117db5760405162461bcd60e51b815260040161048990615757565b86831461182a5760405162461bcd60e51b815260206004820152601a60248201527f5075626b65792073686172657344617461206d69736d617463680000000000006044820152606401610489565b60008060005b8981101561196b578a8a8281811061184a5761184a6158a8565b905060200281019061185c91906158be565b60405161186a929190615904565b6040805191829003909120600081815260356020529182205490945060ff16925082600481111561189d5761189d6153c1565b146118ea5760405162461bcd60e51b815260206004820152601c60248201527f56616c696461746f7220616c72656164792072656769737465726564000000006044820152606401610489565b6000838152603560205260409020805460ff19166001179055827facd38e900350661e325d592c959664c0000a306efb2004e7dc283f44e0ea04238c8c84818110611937576119376158a8565b905060200281019061194991906158be565b8c8c60405161195b9493929190615914565b60405180910390a2600101611830565b506040516322f18bf560e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906322f18bf5906119c6908d908d908d908d908d908d908d908d90600401615a02565b600060405180830381600087803b1580156119e057600080fd5b505af11580156119f4573d6000803e3d6000fd5b5050505050505050505050505050565b60a3546001600160a01b03163314611a5e5760405162461bcd60e51b815260206004820152601b60248201527f43616c6c6572206973206e6f74207468652048617276657374657200000000006044820152606401610489565b600080516020615d9483398151915280546001198101611a905760405162461bcd60e51b815260040161048990615684565b60028255611a9c613e65565b5060019055565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b031614611b3e5760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b6064820152608401610489565b611b473361409b565b565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b031614611b9c5760405162461bcd60e51b81526004016104899061595f565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611c00573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c249190615a63565b6801bc16d674ec800000603454611c3b9190615a7c565b611c45919061598a565b92915050565b611c53613487565b611c6f5760405162461bcd60e51b815260040161048990615527565b60338054610100600160a81b0319166101006001600160a01b038416908102919091179091556040517f83f29c79feb71f8fba9d0fbc4ba5f0982a28b6b1e868b3fc50e6400d100bca0f90600090a250565b60335461010090046001600160a01b03163314611cf05760405162461bcd60e51b815260040161048990615720565b60335460ff1615611d135760405162461bcd60e51b815260040161048990615757565b600080516020615d9483398151915280546001198101611d455760405162461bcd60e51b815260040161048990615684565b600282556000611d5e6801bc16d674ec80000085615a7c565b6040516370a0823160e01b81523060048201529091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611dc5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611de99190615a63565b811115611e2c5760405162461bcd60e51b8152602060048201526011602482015270092dce6eaccccd2c6d2cadce840ae8aa89607b1b6044820152606401610489565b6034547f000000000000000000000000000000000000000000000000000000000000000090611e5c90869061598a565b1115611ea35760405162461bcd60e51b815260206004820152601660248201527513585e081d985b1a59185d1bdc9cc81c995858da195960521b6044820152606401610489565b60375481603854611eb4919061598a565b1115611f025760405162461bcd60e51b815260206004820152601a60248201527f5374616b696e6720455448206f766572207468726573686f6c640000000000006044820152606401610489565b8060386000828254611f14919061598a565b9091555050604051632e1a7d4d60e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b158015611f7b57600080fd5b505af1158015611f8f573d6000803e3d6000fd5b50505050611f9c816140fa565b60408051600160f81b60208201526000602182018190526bffffffffffffffffffffffff193060601b16602c8301529101604051602081830303815290604052905060005b8581101561224d576000878783818110611ffd57611ffd6158a8565b905060200281019061200f9190615a93565b61201990806158be565b604051612027929190615904565b6040519081900390209050600160008281526035602052604090205460ff166004811115612057576120576153c1565b146120a45760405162461bcd60e51b815260206004820152601860248201527f56616c696461746f72206e6f74207265676973746572656400000000000000006044820152606401610489565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663228951186801bc16d674ec8000008a8a868181106120ef576120ef6158a8565b90506020028101906121019190615a93565b61210b90806158be565b878d8d8981811061211e5761211e6158a8565b90506020028101906121309190615a93565b61213e9060208101906158be565b8f8f8b818110612150576121506158a8565b90506020028101906121629190615a93565b604001356040518863ffffffff1660e01b815260040161218796959493929190615b03565b6000604051808303818588803b1580156121a057600080fd5b505af11580156121b4573d6000803e3d6000fd5b5050506000838152603560205260409020805460ff19166002179055508190507f958934bb53d6b4dc911b6173e586864efbc8076684a31f752c53d5778340b37f898985818110612207576122076158a8565b90506020028101906122199190615a93565b61222390806158be565b6801bc16d674ec80000060405161223c93929190615b52565b60405180910390a250600101611fe1565b508585905060346000828254612263919061598a565b909155505060019093555050505050565b60a4818154811061228457600080fd5b6000918252602090912001546001600160a01b0316905081565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663570d8e1d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156122fc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612320919061555e565b6001600160a01b0316336001600160a01b0316146123505760405162461bcd60e51b81526004016104899061557b565b611b47614127565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614806123a75750612392610d49565b6001600160a01b0316336001600160a01b0316145b6123ff5760405162461bcd60e51b815260206004820152602360248201527f43616c6c6572206973206e6f7420746865205661756c74206f7220476f7665726044820152623737b960e91b6064820152608401610489565b600080516020615d94833981519152805460011981016124315760405162461bcd60e51b815260040161048990615684565b600282556040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa15801561249c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124c09190615a63565b90508015612513576125137f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000008361417f565b505060019055565b612523613487565b61253f5760405162461bcd60e51b815260040161048990615527565b6001600160a01b0381166000818152603b6020526040808220805460ff19166001179055517fa7cbf7d59839feb19fb25e1b967d1d0f87cbcae2c1921a448a2d3ecd8be10f089190a250565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663570d8e1d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156125e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061260d919061555e565b6001600160a01b0316336001600160a01b03161461263d5760405162461bcd60e51b81526004016104899061557b565b60335460ff1661265f5760405162461bcd60e51b8152600401610489906156ac565b600080516020615d94833981519152805460011981016126915760405162461bcd60e51b815260040161048990615684565b6002825543611c20606b546126a6919061598a565b106126f35760405162461bcd60e51b815260206004820152601e60248201527f466978206163636f756e74696e672063616c6c656420746f6f20736f6f6e00006044820152606401610489565b6002198512158015612706575060038513155b8015612720575060008560345461271d9190615b76565b12155b61276c5760405162461bcd60e51b815260206004820152601760248201527f496e76616c69642076616c696461746f727344656c74610000000000000000006044820152606401610489565b6811ff6cf0fd15afffff19841215801561278f57506811ff6cf0fd15b000008413155b80156127a957506000846068546127a69190615b76565b12155b6127f55760405162461bcd60e51b815260206004820152601d60248201527f496e76616c696420636f6e73656e7375735265776172647344656c74610000006044820152606401610489565b68053444835ec580000083111561284e5760405162461bcd60e51b815260206004820152601960248201527f496e76616c69642077657468546f5661756c74416d6f756e74000000000000006044820152606401610489565b8460345461285c9190615b76565b60345560685461286d908590615b76565b60685543606b5582156129ac577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0846040518263ffffffff1660e01b81526004016000604051808303818588803b1580156128d557600080fd5b505af11580156128e9573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018890527f000000000000000000000000000000000000000000000000000000000000000016935063a9059cbb925060440190506020604051808303816000875af115801561297e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129a29190615703565b506129ac8361427d565b60408051868152602081018690529081018490527f80d022717ea022455c5886b8dd8a29c037570aae58aeb4d7b136d7a10ec2e4319060600160405180910390a16129f760006142e5565b612a365760405162461bcd60e51b815260206004820152601060248201526f233ab9b29039ba34b63610313637bbb760811b6044820152606401610489565b612a3e613cb4565b5060019055505050565b612a50613487565b612a6c5760405162461bcd60e51b815260040161048990615527565b60a0548110612aad5760405162461bcd60e51b815260206004820152600d60248201526c092dcecc2d8d2c840d2dcc8caf609b1b6044820152606401610489565b600060a08281548110612ac257612ac26158a8565b60009182526020808320909101546001600160a01b03908116808452609f90925260409092205460a05491935090911690612aff906001906156f0565b831015612b815760a08054612b16906001906156f0565b81548110612b2657612b266158a8565b60009182526020909120015460a080546001600160a01b039092169185908110612b5257612b526158a8565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b60a0805480612b9257612b92615b9e565b60008281526020808220600019908401810180546001600160a01b031990811690915593019093556001600160a01b03858116808352609f855260409283902080549094169093559051908416815290917f16b7600acff27e39a8a96056b3d533045298de927507f5c1d97e4accde60488c91015b60405180910390a2505050565b600080516020615d9483398151915280546001198101612c465760405162461bcd60e51b815260040161048990615684565b6002825560335460ff1615612c6d5760405162461bcd60e51b815260040161048990615757565b612c75613487565b612c915760405162461bcd60e51b815260040161048990615527565b6001600160a01b0383166000908152603b602052604090205460ff16612cf95760405162461bcd60e51b815260206004820152601760248201527f496e76616c6964207461726765742073747261746567790000000000000000006044820152606401610489565b60008585604051612d0b929190615904565b60405190819003902090506000805b88811015612e7257898982818110612d3457612d346158a8565b9050602002810190612d4691906158be565b604051612d54929190615904565b60405180910390209150828203612da25760405162461bcd60e51b815260206004820152601260248201527129b2b6331031b7b739b7b634b230ba34b7b760711b6044820152606401610489565b600260008381526035602052604090205460ff166004811115612dc757612dc76153c1565b14612e145760405162461bcd60e51b815260206004820152601b60248201527f536f757263652076616c696461746f72206e6f74207374616b656400000000006044820152606401610489565b612e428a8a83818110612e2957612e296158a8565b9050602002810190612e3b91906158be565b8a8a614750565b50600360008381526035602052604090205460ff166004811115612e6857612e686153c1565b5050600101612d1a565b506000612ee08a8a612e856001826156f0565b818110612e9457612e946158a8565b9050602002810190612ea691906158be565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506148e892505050565b60405163052a332f60e41b815260048101829052602481018590529091506001600160a01b038716906352a332f090604401600060405180830381600087803b158015612f2c57600080fd5b505af1158015612f40573d6000803e3d6000fd5b50505060398a905550603a80546001600160a01b0319166001600160a01b038816179055612f6c614127565b6040517f4beca97c57e00ba8aa7e925f6f2959ad978b1c56108d88f9f9dab1f3246e439f90612fa6908c908c908c908c908c908490615bb4565b60405180910390a15050506001825550505050505050565b612fc6613487565b612fe25760405162461bcd60e51b815260040161048990615527565b8060005b8181101561308c576000848483818110613002576130026158a8565b90506020020160208101906130179190614dc3565b6001600160a01b0316036130845760405162461bcd60e51b815260206004820152602e60248201527f43616e206e6f742073657420616e20656d70747920616464726573732061732060448201526d30903932bbb0b932103a37b5b2b760911b6064820152608401610489565b600101612fe6565b507f04c0b9649497d316554306e53678d5f5f5dbc3a06f97dec13ff4cfe98b986bbc60a484846040516130c193929190615bfa565b60405180910390a1610eea60a48484614c98565b6130dd613487565b6130f95760405162461bcd60e51b815260040161048990615527565b603680546001600160a01b0319166001600160a01b0383169081179091556040517f3329861a0008b3348767567d2405492b997abd79a088d0f2cef6b1a09a8e7ff790600090a250565b60335460009061010090046001600160a01b031633146131755760405162461bcd60e51b815260040161048990615720565b60335460ff16156131985760405162461bcd60e51b815260040161048990615757565b600080516020615d94833981519152805460011981016131ca5760405162461bcd60e51b815260040161048990615684565b600282556131d860016142e5565b925060018255505090565b6131eb613487565b6132075760405162461bcd60e51b815260040161048990615527565b808210801561321e57506801bc16d674ec80000081105b801561323b5750673782dace9d90000061323883836156f0565b10155b6132875760405162461bcd60e51b815260206004820152601760248201527f496e636f7272656374206675736520696e74657276616c0000000000000000006044820152606401610489565b6069829055606a81905560408051838152602081018390527fcb8d24e46eb3c402bf344ee60a6576cba9ef2f59ea1af3b311520704924e901a91015b60405180910390a15050565b60405163095ea7b360e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015260001960248301527f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b3906044016020604051808303816000875af115801561335f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133839190615703565b50565b61338e613487565b6133aa5760405162461bcd60e51b815260040161048990615527565b604051631a1b9a0b60e21b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063686e682c90610ff490869086908690600401615c89565b613402613487565b61341e5760405162461bcd60e51b815260040161048990615527565b60a354604080516001600160a01b03928316815291831660208301527fe48386b84419f4d36e0f96c10cc3510b6fb1a33795620c5098b22472bbe90796910160405180910390a160a380546001600160a01b0319166001600160a01b0392909216919091179055565b600061349f600080516020615db48339815191525490565b6001600160a01b0316336001600160a01b031614905090565b6134c0613487565b6134dc5760405162461bcd60e51b815260040161048990615527565b613504817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b0316613524600080516020615db48339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b60335461010090046001600160a01b0316331461358b5760405162461bcd60e51b815260040161048990615720565b60335460ff16156135ae5760405162461bcd60e51b815260040161048990615757565b600080516020615d94833981519152805460011981016135e05760405162461bcd60e51b815260040161048990615684565b60028255604051632d7688a160e11b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690635aed114290613638908a908a908a908a908a90600401615cb1565b600060405180830381600087803b15801561365257600080fd5b505af1158015613666573d6000803e3d6000fd5b5050505060008060005b888110156137ca5789898281811061368a5761368a6158a8565b905060200281019061369c91906158be565b6040516136aa929190615904565b604080519182900390912060008181526035602052919091205490935060ff16915060038260048111156136e0576136e06153c1565b14806136fd575060018260048111156136fb576136fb6153c1565b145b6137495760405162461bcd60e51b815260206004820152601d60248201527f56616c696461746f72206e6f742072656764206f722065786974696e670000006044820152606401610489565b6000838152603560205260409020805460ff19166004179055827f6aecca20726a17c1b81989b2fd09dfdf636bae9e564d4066ca18df62dc1f3dc28b8b84818110613796576137966158a8565b90506020028101906137a891906158be565b8b8b6040516137ba9493929190615914565b60405180910390a2600101613670565b5050506001825550505050505050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146138225760405162461bcd60e51b815260040161048990615928565b600080516020615d94833981519152805460011981016138545760405162461bcd60e51b815260040161048990615684565b600282557f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b0316146138a95760405162461bcd60e51b81526004016104899061595f565b612a3e85858561417f565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146138fc5760405162461bcd60e51b815260040161048990615928565b600080516020615d948339815191528054600119810161392e5760405162461bcd60e51b815260040161048990615684565b600282556040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015613999573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139bd9190615a63565b9050600061010754826139d091906156f0565b90508015613a0857610107829055613a087f000000000000000000000000000000000000000000000000000000000000000082613dd3565b5050600182555050565b6036546001600160a01b03163314613a6c5760405162461bcd60e51b815260206004820152601960248201527f43616c6c6572206973206e6f7420746865204d6f6e69746f72000000000000006044820152606401610489565b600060388190556040517fe765a88a37047c5d793dce22b9ceb5a0f5039d276da139b4c7d29613f341f1109190a1565b606060a4805480602002602001604051908101604052809291908181526020018280548015613af457602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613ad6575b5050505050905090565b6001600160a01b038281166000908152609f60205260409020541615613b5b5760405162461bcd60e51b81526020600482015260126024820152711c151bdad95b88185b1c9958591e481cd95d60721b6044820152606401610489565b6001600160a01b03821615801590613b7b57506001600160a01b03811615155b613bbb5760405162461bcd60e51b8152602060048201526011602482015270496e76616c69642061646472657373657360781b6044820152606401610489565b6001600160a01b038281166000818152609f6020908152604080832080549587166001600160a01b0319968716811790915560a0805460018101825594527f78fdc8d422c49ced035a9edf18d00d3c6a8d81df210f3e5e448e045e77b41e8890930180549095168417909455925190815290917fef6485b84315f9b1483beffa32aae9a0596890395e3d7521f1c5fbb51790e765910160405180910390a25050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052613caf90849061495d565b505050565b60335460ff16613cd65760405162461bcd60e51b8152600401610489906156ac565b6033805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b8251613d339060a4906020860190614cfb565b50815181518114613d7d5760405162461bcd60e51b8152602060048201526014602482015273496e76616c696420696e7075742061727261797360601b6044820152606401610489565b60005b81811015613dcc57613dc4848281518110613d9d57613d9d6158a8565b6020026020010151848381518110613db757613db76158a8565b6020026020010151613afe565b600101613d80565b5050505050565b60008111613e1c5760405162461bcd60e51b81526020600482015260166024820152754d757374206465706f73697420736f6d657468696e6760501b6044820152606401610489565b6040805160008152602081018390526001600160a01b038416917f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f62910160405180910390a25050565b60335460ff1615613e885760405162461bcd60e51b815260040161048990615757565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663e52253816040518163ffffffff1660e01b81526004016020604051808303816000875af1158015613eea573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f0e9190615a63565b9050600060685482613f20919061598a565b905080471015613f725760405162461bcd60e51b815260206004820152601860248201527f496e73756666696369656e74206574682062616c616e636500000000000000006044820152606401610489565b8015610d945760006068819055507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015613fdb57600080fd5b505af1158015613fef573d6000803e3d6000fd5b505060a35461402f93506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116935016905083613c5d565b60a354604080516001600160a01b0392831681527f0000000000000000000000000000000000000000000000000000000000000000909216602083015281018290527ff6c07a063ed4e63808eb8da7112d46dbcd38de2b40a73dbcc9353c5a94c72353906060016132c3565b6001600160a01b0381166140f15760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f7220697320616464726573732830290000000000006044820152606401610489565b61338381614a2f565b60006141098261010754614a96565b905080610107600082825461411e91906156f0565b90915550505050565b60335460ff161561414a5760405162461bcd60e51b815260040161048990615757565b6033805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258613d033390565b600081116141cf5760405162461bcd60e51b815260206004820152601760248201527f4d75737420776974686472617720736f6d657468696e670000000000000000006044820152606401610489565b6001600160a01b03831661421e5760405162461bcd60e51b8152602060048201526016602482015275135d5cdd081cdc1958da599e481c9958da5c1a595b9d60521b6044820152606401610489565b614227816140fa565b61423b6001600160a01b0383168483613c5d565b6040805160008152602081018390526001600160a01b038416917f2717ead6b9200dd235aad468c9809ea400fe33ac69b5bfaa6d3e90fc922b63989101612c07565b6040805160008152602081018390526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016917f2717ead6b9200dd235aad468c9809ea400fe33ac69b5bfaa6d3e90fc922b6398910160405180910390a250565b60006068544710156142fa57611c4582614aae565b60006068544761430a91906156f0565b9050600191506801bc16d674ec80000081106144df5760006143356801bc16d674ec80000083615cf2565b9050806034600082825461434991906156f0565b9091555060009050614364826801bc16d674ec800000615a7c565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b1580156143c157600080fd5b505af11580156143d5573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018690527f000000000000000000000000000000000000000000000000000000000000000016935063a9059cbb925060440190506020604051808303816000875af115801561446a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061448e9190615703565b506144988161427d565b60345460408051848152602081019290925281018290527fbe7040030ff7b347853214bf49820c6d455fedf58f3815f85c7bc5216993682b9060600160405180910390a150505b6000606854476144ef91906156f0565b90506801bc16d674ec80000081106145415760405162461bcd60e51b8152602060048201526015602482015274556e6578706563746564206163636f756e74696e6760581b6044820152606401610489565b80600003614550575050919050565b6069548110156145aa57806068600082825461456c919061598a565b90915550506040518181527f7a745a2c63a535068f52ceca27debd5297bbad5f7f37ec53d044a59d0362445d906020015b60405180910390a1614749565b606a54811115614738577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561460f57600080fd5b505af1158015614623573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018690527f000000000000000000000000000000000000000000000000000000000000000016935063a9059cbb925060440190506020604051808303816000875af11580156146b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146dc9190615703565b506001603460008282546146f091906156f0565b909155506146ff90508161427d565b60345460408051918252602082018390527f6aa7e30787b26429ced603a7aba8b19c4b5d5bcf29a3257da953c8d53bcaa3a6910161459d565b61474184614aae565b949350505050565b5050919050565b6000603084146147a25760405162461bcd60e51b815260206004820152601a60248201527f496e76616c696420736f757263652062797465206c656e6774680000000000006044820152606401610489565b603082146147f25760405162461bcd60e51b815260206004820152601a60248201527f496e76616c6964207461726765742062797465206c656e6774680000000000006044820152606401610489565b6147fa614ac6565b9050600071bbddc7ce488642fb579f8b00f3a5900072516001600160a01b031682878787876040516020016148329493929190615d14565b60408051601f198184030181529082905261484c91615d36565b60006040518083038185875af1925050503d8060008114614889576040519150601f19603f3d011682016040523d82523d6000602084013e61488e565b606091505b50509050806148df5760405162461bcd60e51b815260206004820152601c60248201527f436f6e736f6c69646174696f6e2072657175657374206661696c6564000000006044820152606401610489565b50949350505050565b6000600282600060801b604051602001614903929190615d48565b60408051601f198184030181529082905261491d91615d36565b602060405180830381855afa15801561493a573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190611c459190615a63565b60006149b2826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614b8d9092919063ffffffff16565b805190915015613caf57808060200190518101906149d09190615703565b613caf5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610489565b806001600160a01b0316614a4f600080516020615db48339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a3600080516020615db483398151915255565b6000818310614aa55781614aa7565b825b9392505050565b60008115614abe57614abe614127565b506000919050565b6040516000908190819071bbddc7ce488642fb579f8b00f3a5900072519082818181855afa9150503d8060008114614b1a576040519150601f19603f3d011682016040523d82523d6000602084013e614b1f565b606091505b5091509150818015614b32575060008151115b614b725760405162461bcd60e51b81526020600482015260116024820152704661696c656420746f206765742066656560781b6044820152606401610489565b80806020019051810190614b869190615a63565b9250505090565b6060614741848460008585843b614be65760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610489565b600080866001600160a01b03168587604051614c029190615d36565b60006040518083038185875af1925050503d8060008114614c3f576040519150601f19603f3d011682016040523d82523d6000602084013e614c44565b606091505b5091509150614c54828286614c5f565b979650505050505050565b60608315614c6e575081614aa7565b825115614c7e5782518084602001fd5b8160405162461bcd60e51b81526004016104899190615d80565b828054828255906000526020600020908101928215614ceb579160200282015b82811115614ceb5781546001600160a01b0319166001600160a01b03843516178255602090920191600190910190614cb8565b50614cf7929150614d50565b5090565b828054828255906000526020600020908101928215614ceb579160200282015b82811115614ceb57825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190614d1b565b5b80821115614cf75760008155600101614d51565b6001600160a01b038116811461338357600080fd5b8035614d8581614d65565b919050565b60008060408385031215614d9d57600080fd5b8235614da881614d65565b91506020830135614db881614d65565b809150509250929050565b600060208284031215614dd557600080fd5b8135614aa781614d65565b60008060408385031215614df357600080fd5b8235614dfe81614d65565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715614e4a57614e4a614e0c565b604052919050565b60006001600160401b03821115614e6b57614e6b614e0c565b5060051b60200190565b80356001600160401b0381168114614d8557600080fd5b803563ffffffff81168114614d8557600080fd5b801515811461338357600080fd5b600060a08284031215614ec057600080fd5b60405160a081016001600160401b0381118282101715614ee257614ee2614e0c565b604052905080614ef183614e8c565b8152614eff60208401614e75565b6020820152614f1060408401614e75565b60408201526060830135614f2381614ea0565b6060820152608092830135920191909152919050565b600080600060e08486031215614f4e57600080fd5b83356001600160401b03811115614f6457600080fd5b8401601f81018613614f7557600080fd5b8035614f88614f8382614e52565b614e22565b8082825260208201915060208360051b850101925088831115614faa57600080fd5b6020840193505b82841015614fd357614fc284614e75565b825260209384019390910190614fb1565b955050505060208401359150614fec8560408601614eae565b90509250925092565b600082601f83011261500657600080fd5b8135615014614f8382614e52565b8082825260208201915060208360051b86010192508583111561503657600080fd5b602085015b8381101561505c57803561504e81614d65565b83526020928301920161503b565b5095945050505050565b60008060006060848603121561507b57600080fd5b83356001600160401b0381111561509157600080fd5b61509d86828701614ff5565b93505060208401356001600160401b038111156150b957600080fd5b6150c586828701614ff5565b92505060408401356001600160401b038111156150e157600080fd5b6150ed86828701614ff5565b9150509250925092565b60008083601f84011261510957600080fd5b5081356001600160401b0381111561512057600080fd5b6020830191508360208260051b850101111561513b57600080fd5b9250929050565b6000806000806040858703121561515857600080fd5b84356001600160401b0381111561516e57600080fd5b61517a878288016150f7565b90955093505060208501356001600160401b0381111561519957600080fd5b6151a5878288016150f7565b95989497509550505050565b6000602082840312156151c357600080fd5b5035919050565b600060a082840312156151dc57600080fd5b50919050565b600080600080600080600080610120898b0312156151ff57600080fd5b88356001600160401b0381111561521557600080fd5b6152218b828c016150f7565b90995097505060208901356001600160401b0381111561524057600080fd5b61524c8b828c016150f7565b90975095505060408901356001600160401b0381111561526b57600080fd5b6152778b828c016150f7565b909550935050606089013591506152918a60808b016151ca565b90509295985092959890939650565b600080602083850312156152b357600080fd5b82356001600160401b038111156152c957600080fd5b6152d5858286016150f7565b90969095509350505050565b6000806000606084860312156152f657600080fd5b505081359360208301359350604090920135919050565b60008060008060006060868803121561532557600080fd5b85356001600160401b0381111561533b57600080fd5b615347888289016150f7565b90965094505060208601356001600160401b0381111561536657600080fd5b8601601f8101881361537757600080fd5b80356001600160401b0381111561538d57600080fd5b88602082840101111561539f57600080fd5b602091909101935091506153b560408701614d7a565b90509295509295909350565b634e487b7160e01b600052602160045260246000fd5b60208101600583106153f957634e487b7160e01b600052602160045260246000fd5b91905290565b6000806040838503121561541257600080fd5b50508035926020909101359150565b600080600080600060e0868803121561543957600080fd5b85356001600160401b0381111561544f57600080fd5b61545b888289016150f7565b90965094505060208601356001600160401b0381111561547a57600080fd5b615486888289016150f7565b90945092506153b5905087604088016151ca565b6000806000606084860312156154af57600080fd5b83356154ba81614d65565b925060208401356154ca81614d65565b929592945050506040919091013590565b602080825282518282018190526000918401906040840190835b8181101561551c5783516001600160a01b03168352602093840193909201916001016154f5565b509095945050505050565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b60006020828403121561557057600080fd5b8151614aa781614d65565b6020808252601c908201527f43616c6c6572206973206e6f7420746865205374726174656769737400000000604082015260600190565b600081518084526020840193506020830160005b828110156155ed5781516001600160401b03168652602095860195909101906001016155c6565b5093949350505050565b63ffffffff81511682526001600160401b0360208201511660208301526001600160401b036040820151166040830152606081015115156060830152608081015160808301525050565b6001600160a01b038516815261010060208201819052600090615666908301866155b2565b905083604083015261567b60608301846155f7565b95945050505050565b6020808252600e908201526d1499595b9d1c985b9d0818d85b1b60921b604082015260600190565b60208082526014908201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b81810381811115611c4557611c456156da565b60006020828403121561571557600080fd5b8151614aa781614ea0565b6020808252601d908201527f43616c6c6572206973206e6f7420746865205265676973747261746f72000000604082015260600190565b60208082526010908201526f14185d5cd8589b194e881c185d5cd95960821b604082015260600190565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60008383855260208501945060208460051b8201018360005b8681101561583757838303601f19018852813536879003601e190181126157e957600080fd5b86016020810190356001600160401b0381111561580557600080fd5b80360382131561581457600080fd5b61581f858284615781565b60209a8b019a909550939093019250506001016157c3565b50909695505050505050565b81835260208301925060008160005b848110156155ed576001600160401b0361586b83614e75565b1686526020958601959190910190600101615852565b6040815260006158956040830186886157aa565b8281036020840152614c54818587615843565b634e487b7160e01b600052603260045260246000fd5b6000808335601e198436030181126158d557600080fd5b8301803591506001600160401b038211156158ef57600080fd5b60200191503681900382131561513b57600080fd5b8183823760009101908152919050565b604081526000615895604083018688615781565b60208082526017908201527f43616c6c6572206973206e6f7420746865205661756c74000000000000000000604082015260600190565b602080825260119082015270155b9cdd5c1c1bdc9d195908185cdcd95d607a1b604082015260600190565b80820180821115611c4557611c456156da565b63ffffffff6159ab82614e8c565b1682526001600160401b036159c260208301614e75565b1660208301526001600160401b036159dc60408301614e75565b16604083015260608101356159f081614ea0565b15156060830152608090810135910152565b61012081526000615a1861012083018a8c6157aa565b8281036020840152615a2b81898b615843565b90508281036040840152615a408187896157aa565b915050836060830152615a56608083018461599d565b9998505050505050505050565b600060208284031215615a7557600080fd5b5051919050565b8082028115828204841417611c4557611c456156da565b60008235605e19833603018112615aa957600080fd5b9190910192915050565b60005b83811015615ace578181015183820152602001615ab6565b50506000910152565b60008151808452615aef816020860160208601615ab3565b601f01601f19169290920160200192915050565b608081526000615b1760808301888a615781565b8281036020840152615b298188615ad7565b90508281036040840152615b3e818688615781565b915050826060830152979650505050505050565b604081526000615b66604083018587615781565b9050826020830152949350505050565b8082018281126000831280158216821582161715615b9657615b966156da565b505092915050565b634e487b7160e01b600052603160045260246000fd5b608081526000615bc860808301888a6157aa565b8281036020840152615bdb818789615781565b6001600160a01b03959095166040840152505060600152949350505050565b6040808252845490820181905260008581526020812090916060840190835b81811015615c405783546001600160a01b0316835260019384019360209093019201615c19565b50508381036020808601919091528582520190508460005b85811015615837578135615c6b81614d65565b6001600160a01b031683526020928301929190910190600101615c58565b60e081526000615c9c60e08301866155b2565b905083602083015261474160408301846155f7565b60e081526000615cc560e0830187896157aa565b8281036020840152615cd8818688615843565b915050615ce8604083018461599d565b9695505050505050565b600082615d0f57634e487b7160e01b600052601260045260246000fd5b500490565b8385823760008482016000815283858237600093019283525090949350505050565b60008251615aa9818460208701615ab3565b60008351615d5a818460208801615ab3565b6fffffffffffffffffffffffffffffffff19939093169190920190815260100192915050565b602081526000614aa76020830184615ad756fe53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac45357bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa264697066735822122041f94eda470174d841f7b4a3b5ea3d6671f4ce7dab7623e2c03c271bb943c9e364736f6c634300081c0033", + "deployedBytecode": "0x6080604052600436106103dd5760003560e01c8063853828b6116101fd578063b16b7d0b11610118578063d9caed12116100ab578063de5f62681161007a578063de5f626814610cb7578063e0abfca614610ccc578063e752923914610cfc578063ee7afe2d14610d12578063f6ca71b014610d2757600080fd5b8063d9caed1214610c19578063dbe55e5614610c39578063dd505df614610c6d578063de34d71314610ca157600080fd5b8063cceab750116100e7578063cceab75014610b8e578063d059f6ef14610bc2578063d38bfff414610bd9578063d7c3003514610bf957600080fd5b8063b16b7d0b14610b1c578063b6e2b52014610b39578063c2e1e3f414610b59578063c7af335214610b7957600080fd5b806396d538bb11610190578063aa388af61161015f578063aa388af614610a66578063ab12edf514610ab3578063ad1728cb14610ad3578063ad5c464814610ae857600080fd5b806396d538bb146109d45780639da0e462146109f4578063a3b81e7314610a31578063a4f98af414610a5157600080fd5b80639092c31c116101cc5780639092c31c1461092c5780639136616a14610960578063916497511461098057806392555aea146109b457600080fd5b8063853828b6146108b257806387bae867146108c75780638ae99cea146108ec5780638d7c0e461461090c57600080fd5b80635205c380116102f857806367c7066c1161028b5780637260f8261161025a5780637260f826146108315780637b2d9b2c146108515780637b8962f714610871578063842f5c46146108875780638456cb591461089d57600080fd5b806367c7066c1461079d5780636e811d38146107bd5780636ef38795146107dd578063714897df146107fd57600080fd5b80635d36b190116102c75780635d36b1901461073c5780635f51522614610751578063630923831461077157806366e3667e1461078757600080fd5b80635205c380146106c357806359b80c0a146106e35780635a063f63146107035780635c975abb1461071857600080fd5b80633434c8a41161037057806343be647e1161033f57806343be647e1461065757806347e7ef2414610677578063484be812146106975780634a675f31146106ad57600080fd5b80633434c8a4146105ca5780633c864959146105ed578063430bf08a14610603578063435356d11461063757600080fd5b80630fc3b4c4116103ac5780630fc3b4c41461053f5780631072cbea1461057557806313cf69dd1461059557806322495dc8146105aa57600080fd5b806307a7d4ac146104995780630c340a24146104d65780630df1ecfd146104eb5780630ed57b3a1461051f57600080fd5b3661049457336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614806104415750336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016145b6104925760405162461bcd60e51b815260206004820152601e60248201527f457468206e6f742066726f6d20616c6c6f77656420636f6e747261637473000060448201526064015b60405180910390fd5b005b600080fd5b3480156104a557600080fd5b50603a546104b9906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156104e257600080fd5b506104b9610d49565b3480156104f757600080fd5b506104b97f000000000000000000000000000000000000000000000000000000000000000081565b34801561052b57600080fd5b5061049261053a366004614d8a565b610d66565b34801561054b57600080fd5b506104b961055a366004614dc3565b609f602052600090815260409020546001600160a01b031681565b34801561058157600080fd5b50610492610590366004614de0565b610d98565b3480156105a157600080fd5b50610492610e53565b3480156105b657600080fd5b506104926105c5366004614f39565b610ef0565b3480156105d657600080fd5b506105df61102b565b6040519081526020016104cd565b3480156105f957600080fd5b506105df60695481565b34801561060f57600080fd5b506104b97f000000000000000000000000000000000000000000000000000000000000000081565b34801561064357600080fd5b50610492610652366004615066565b6111a5565b34801561066357600080fd5b50610492610672366004615142565b6113d9565b34801561068357600080fd5b50610492610692366004614de0565b61162f565b3480156106a357600080fd5b506105df606a5481565b3480156106b957600080fd5b506105df60395481565b3480156106cf57600080fd5b506104926106de3660046151b1565b61172a565b3480156106ef57600080fd5b506104926106fe3660046151e2565b611789565b34801561070f57600080fd5b50610492611a04565b34801561072457600080fd5b5060335460ff165b60405190151581526020016104cd565b34801561074857600080fd5b50610492611aa3565b34801561075d57600080fd5b506105df61076c366004614dc3565b611b49565b34801561077d57600080fd5b506105df611c2081565b34801561079357600080fd5b506105df60345481565b3480156107a957600080fd5b5060a3546104b9906001600160a01b031681565b3480156107c957600080fd5b506104926107d8366004614dc3565b611c4b565b3480156107e957600080fd5b506104926107f83660046152a0565b611cc1565b34801561080957600080fd5b506105df7f000000000000000000000000000000000000000000000000000000000000000081565b34801561083d57600080fd5b506036546104b9906001600160a01b031681565b34801561085d57600080fd5b506104b961086c3660046151b1565b612274565b34801561087d57600080fd5b506105df60375481565b34801561089357600080fd5b506105df60685481565b3480156108a957600080fd5b5061049261229e565b3480156108be57600080fd5b50610492612358565b3480156108d357600080fd5b506033546104b99061010090046001600160a01b031681565b3480156108f857600080fd5b50610492610907366004614dc3565b61251b565b34801561091857600080fd5b506104926109273660046152e1565b61258b565b34801561093857600080fd5b506104b97f000000000000000000000000000000000000000000000000000000000000000081565b34801561096c57600080fd5b5061049261097b3660046151b1565b612a48565b34801561098c57600080fd5b506104b97f000000000000000000000000000000000000000000000000000000000000000081565b3480156109c057600080fd5b506104926109cf36600461530d565b612c14565b3480156109e057600080fd5b506104926109ef3660046152a0565b612fbe565b348015610a0057600080fd5b50610a24610a0f3660046151b1565b60356020526000908152604090205460ff1681565b6040516104cd91906153d7565b348015610a3d57600080fd5b50610492610a4c366004614dc3565b6130d5565b348015610a5d57600080fd5b5061072c613143565b348015610a7257600080fd5b5061072c610a81366004614dc3565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811691161490565b348015610abf57600080fd5b50610492610ace3660046153ff565b6131e3565b348015610adf57600080fd5b506104926132cf565b348015610af457600080fd5b506104b97f000000000000000000000000000000000000000000000000000000000000000081565b348015610b2857600080fd5b506105df6801bc16d674ec80000081565b348015610b4557600080fd5b50610492610b54366004614f39565b613386565b348015610b6557600080fd5b50610492610b74366004614dc3565b6133fa565b348015610b8557600080fd5b5061072c613487565b348015610b9a57600080fd5b506104b97f000000000000000000000000000000000000000000000000000000000000000081565b348015610bce57600080fd5b506105df6101075481565b348015610be557600080fd5b50610492610bf4366004614dc3565b6134b8565b348015610c0557600080fd5b50610492610c14366004615421565b61355c565b348015610c2557600080fd5b50610492610c3436600461549a565b6137da565b348015610c4557600080fd5b506104b97f000000000000000000000000000000000000000000000000000000000000000081565b348015610c7957600080fd5b506104b97f000000000000000000000000000000000000000000000000000000000000000081565b348015610cad57600080fd5b506105df60385481565b348015610cc357600080fd5b506104926138b4565b348015610cd857600080fd5b5061072c610ce7366004614dc3565b603b6020526000908152604090205460ff1681565b348015610d0857600080fd5b506105df606b5481565b348015610d1e57600080fd5b50610492613a12565b348015610d3357600080fd5b50610d3c613a9c565b6040516104cd91906154db565b6000610d61600080516020615db48339815191525490565b905090565b610d6e613487565b610d8a5760405162461bcd60e51b815260040161048990615527565b610d948282613afe565b5050565b610da0613487565b610dbc5760405162461bcd60e51b815260040161048990615527565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811690831603610e375760405162461bcd60e51b815260206004820152601f60248201527f43616e6e6f74207472616e7366657220737570706f72746564206173736574006044820152606401610489565b610d94610e42610d49565b6001600160a01b0384169083613c5d565b6040516336f370b360e21b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063dbcdc2cc90602401600060405180830381600087803b158015610ed657600080fd5b505af1158015610eea573d6000803e3d6000fd5b50505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663570d8e1d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f72919061555e565b6001600160a01b0316336001600160a01b031614610fa25760405162461bcd60e51b81526004016104899061557b565b60405163bc26e7e560e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063bc26e7e590610ff4903090879087908790600401615641565b600060405180830381600087803b15801561100e57600080fd5b505af1158015611022573d6000803e3d6000fd5b50505050505050565b600080516020615d9483398151915280546000919060011981016110615760405162461bcd60e51b815260040161048990615684565b6002825560335460ff166110875760405162461bcd60e51b8152600401610489906156ac565b603a546001600160a01b031633146110d75760405162461bcd60e51b81526020600482015260136024820152724e6f742074617267657420737472617465677960681b6044820152606401610489565b60395492506000831161112c5760405162461bcd60e51b815260206004820152601c60248201527f4e6f20636f6e736f6c69646174696f6e20696e2070726f6772657373000000006044820152606401610489565b826034600082825461113e91906156f0565b90915550506000603955603a80546001600160a01b0319169055611160613cb4565b6034546040805185815260208101929092527fb7f9b24f2efc7c0499fca5fd498666e42547910efe905fd5c16f835af7781990910160405180910390a1506001905590565b6111ad613487565b6111c95760405162461bcd60e51b815260040161048990615527565b600054610100900460ff16806111e2575060005460ff16155b6112455760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610489565b600054610100900460ff16158015611267576000805461ffff19166101011790555b611272848484613d20565b60405163095ea7b360e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015260001960248301527f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b3906044016020604051808303816000875af1158015611302573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113269190615703565b506040516336f370b360e21b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063dbcdc2cc90602401600060405180830381600087803b1580156113aa57600080fd5b505af11580156113be573d6000803e3d6000fd5b505050508015610eea576000805461ff001916905550505050565b60335461010090046001600160a01b031633146114085760405162461bcd60e51b815260040161048990615720565b60335460ff161561142b5760405162461bcd60e51b815260040161048990615757565b600080516020615d948339815191528054600119810161145d5760405162461bcd60e51b815260040161048990615684565b600282556040516332afd02f60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906332afd02f906114b3908990899089908990600401615881565b600060405180830381600087803b1580156114cd57600080fd5b505af11580156114e1573d6000803e3d6000fd5b5050505060008060005b8781101561162057888882818110611505576115056158a8565b905060200281019061151791906158be565b604051611525929190615904565b604080519182900390912060008181526035602052919091205490935060ff169150600282600481111561155b5761155b6153c1565b1461159f5760405162461bcd60e51b815260206004820152601460248201527315985b1a59185d1bdc881b9bdd081cdd185ad95960621b6044820152606401610489565b6000838152603560205260409020805460ff19166003179055827f8c2e15303eb94e531acc988c2a01d1193bdaaa15eda7f16dda85316ed463578d8a8a848181106115ec576115ec6158a8565b90506020028101906115fe91906158be565b8a8a6040516116109493929190615914565b60405180910390a26001016114eb565b50505060018255505050505050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146116775760405162461bcd60e51b815260040161048990615928565b600080516020615d94833981519152805460011981016116a95760405162461bcd60e51b815260040161048990615684565b600282557f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b0316146116fe5760405162461bcd60e51b81526004016104899061595f565b826101076000828254611711919061598a565b9091555061172190508484613dd3565b50600190555050565b611732613487565b61174e5760405162461bcd60e51b815260040161048990615527565b60378190556040518181527fe26b067424903962f951f568e52ec9a3bbe1589526ea54a4e69ca6eaae1a4c779060200160405180910390a150565b60335461010090046001600160a01b031633146117b85760405162461bcd60e51b815260040161048990615720565b60335460ff16156117db5760405162461bcd60e51b815260040161048990615757565b86831461182a5760405162461bcd60e51b815260206004820152601a60248201527f5075626b65792073686172657344617461206d69736d617463680000000000006044820152606401610489565b60008060005b8981101561196b578a8a8281811061184a5761184a6158a8565b905060200281019061185c91906158be565b60405161186a929190615904565b6040805191829003909120600081815260356020529182205490945060ff16925082600481111561189d5761189d6153c1565b146118ea5760405162461bcd60e51b815260206004820152601c60248201527f56616c696461746f7220616c72656164792072656769737465726564000000006044820152606401610489565b6000838152603560205260409020805460ff19166001179055827facd38e900350661e325d592c959664c0000a306efb2004e7dc283f44e0ea04238c8c84818110611937576119376158a8565b905060200281019061194991906158be565b8c8c60405161195b9493929190615914565b60405180910390a2600101611830565b506040516322f18bf560e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906322f18bf5906119c6908d908d908d908d908d908d908d908d90600401615a02565b600060405180830381600087803b1580156119e057600080fd5b505af11580156119f4573d6000803e3d6000fd5b5050505050505050505050505050565b60a3546001600160a01b03163314611a5e5760405162461bcd60e51b815260206004820152601b60248201527f43616c6c6572206973206e6f74207468652048617276657374657200000000006044820152606401610489565b600080516020615d9483398151915280546001198101611a905760405162461bcd60e51b815260040161048990615684565b60028255611a9c613e65565b5060019055565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b031614611b3e5760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b6064820152608401610489565b611b473361409b565b565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b031614611b9c5760405162461bcd60e51b81526004016104899061595f565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611c00573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c249190615a63565b6801bc16d674ec800000603454611c3b9190615a7c565b611c45919061598a565b92915050565b611c53613487565b611c6f5760405162461bcd60e51b815260040161048990615527565b60338054610100600160a81b0319166101006001600160a01b038416908102919091179091556040517f83f29c79feb71f8fba9d0fbc4ba5f0982a28b6b1e868b3fc50e6400d100bca0f90600090a250565b60335461010090046001600160a01b03163314611cf05760405162461bcd60e51b815260040161048990615720565b60335460ff1615611d135760405162461bcd60e51b815260040161048990615757565b600080516020615d9483398151915280546001198101611d455760405162461bcd60e51b815260040161048990615684565b600282556000611d5e6801bc16d674ec80000085615a7c565b6040516370a0823160e01b81523060048201529091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611dc5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611de99190615a63565b811115611e2c5760405162461bcd60e51b8152602060048201526011602482015270092dce6eaccccd2c6d2cadce840ae8aa89607b1b6044820152606401610489565b6034547f000000000000000000000000000000000000000000000000000000000000000090611e5c90869061598a565b1115611ea35760405162461bcd60e51b815260206004820152601660248201527513585e081d985b1a59185d1bdc9cc81c995858da195960521b6044820152606401610489565b60375481603854611eb4919061598a565b1115611f025760405162461bcd60e51b815260206004820152601a60248201527f5374616b696e6720455448206f766572207468726573686f6c640000000000006044820152606401610489565b8060386000828254611f14919061598a565b9091555050604051632e1a7d4d60e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b158015611f7b57600080fd5b505af1158015611f8f573d6000803e3d6000fd5b50505050611f9c816140fa565b60408051600160f81b60208201526000602182018190526bffffffffffffffffffffffff193060601b16602c8301529101604051602081830303815290604052905060005b8581101561224d576000878783818110611ffd57611ffd6158a8565b905060200281019061200f9190615a93565b61201990806158be565b604051612027929190615904565b6040519081900390209050600160008281526035602052604090205460ff166004811115612057576120576153c1565b146120a45760405162461bcd60e51b815260206004820152601860248201527f56616c696461746f72206e6f74207265676973746572656400000000000000006044820152606401610489565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663228951186801bc16d674ec8000008a8a868181106120ef576120ef6158a8565b90506020028101906121019190615a93565b61210b90806158be565b878d8d8981811061211e5761211e6158a8565b90506020028101906121309190615a93565b61213e9060208101906158be565b8f8f8b818110612150576121506158a8565b90506020028101906121629190615a93565b604001356040518863ffffffff1660e01b815260040161218796959493929190615b03565b6000604051808303818588803b1580156121a057600080fd5b505af11580156121b4573d6000803e3d6000fd5b5050506000838152603560205260409020805460ff19166002179055508190507f958934bb53d6b4dc911b6173e586864efbc8076684a31f752c53d5778340b37f898985818110612207576122076158a8565b90506020028101906122199190615a93565b61222390806158be565b6801bc16d674ec80000060405161223c93929190615b52565b60405180910390a250600101611fe1565b508585905060346000828254612263919061598a565b909155505060019093555050505050565b60a4818154811061228457600080fd5b6000918252602090912001546001600160a01b0316905081565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663570d8e1d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156122fc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612320919061555e565b6001600160a01b0316336001600160a01b0316146123505760405162461bcd60e51b81526004016104899061557b565b611b47614127565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614806123a75750612392610d49565b6001600160a01b0316336001600160a01b0316145b6123ff5760405162461bcd60e51b815260206004820152602360248201527f43616c6c6572206973206e6f7420746865205661756c74206f7220476f7665726044820152623737b960e91b6064820152608401610489565b600080516020615d94833981519152805460011981016124315760405162461bcd60e51b815260040161048990615684565b600282556040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa15801561249c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124c09190615a63565b90508015612513576125137f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000008361417f565b505060019055565b612523613487565b61253f5760405162461bcd60e51b815260040161048990615527565b6001600160a01b0381166000818152603b6020526040808220805460ff19166001179055517fa7cbf7d59839feb19fb25e1b967d1d0f87cbcae2c1921a448a2d3ecd8be10f089190a250565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663570d8e1d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156125e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061260d919061555e565b6001600160a01b0316336001600160a01b03161461263d5760405162461bcd60e51b81526004016104899061557b565b60335460ff1661265f5760405162461bcd60e51b8152600401610489906156ac565b600080516020615d94833981519152805460011981016126915760405162461bcd60e51b815260040161048990615684565b6002825543611c20606b546126a6919061598a565b106126f35760405162461bcd60e51b815260206004820152601e60248201527f466978206163636f756e74696e672063616c6c656420746f6f20736f6f6e00006044820152606401610489565b6002198512158015612706575060038513155b8015612720575060008560345461271d9190615b76565b12155b61276c5760405162461bcd60e51b815260206004820152601760248201527f496e76616c69642076616c696461746f727344656c74610000000000000000006044820152606401610489565b6811ff6cf0fd15afffff19841215801561278f57506811ff6cf0fd15b000008413155b80156127a957506000846068546127a69190615b76565b12155b6127f55760405162461bcd60e51b815260206004820152601d60248201527f496e76616c696420636f6e73656e7375735265776172647344656c74610000006044820152606401610489565b68053444835ec580000083111561284e5760405162461bcd60e51b815260206004820152601960248201527f496e76616c69642077657468546f5661756c74416d6f756e74000000000000006044820152606401610489565b8460345461285c9190615b76565b60345560685461286d908590615b76565b60685543606b5582156129ac577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0846040518263ffffffff1660e01b81526004016000604051808303818588803b1580156128d557600080fd5b505af11580156128e9573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018890527f000000000000000000000000000000000000000000000000000000000000000016935063a9059cbb925060440190506020604051808303816000875af115801561297e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129a29190615703565b506129ac8361427d565b60408051868152602081018690529081018490527f80d022717ea022455c5886b8dd8a29c037570aae58aeb4d7b136d7a10ec2e4319060600160405180910390a16129f760006142e5565b612a365760405162461bcd60e51b815260206004820152601060248201526f233ab9b29039ba34b63610313637bbb760811b6044820152606401610489565b612a3e613cb4565b5060019055505050565b612a50613487565b612a6c5760405162461bcd60e51b815260040161048990615527565b60a0548110612aad5760405162461bcd60e51b815260206004820152600d60248201526c092dcecc2d8d2c840d2dcc8caf609b1b6044820152606401610489565b600060a08281548110612ac257612ac26158a8565b60009182526020808320909101546001600160a01b03908116808452609f90925260409092205460a05491935090911690612aff906001906156f0565b831015612b815760a08054612b16906001906156f0565b81548110612b2657612b266158a8565b60009182526020909120015460a080546001600160a01b039092169185908110612b5257612b526158a8565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b60a0805480612b9257612b92615b9e565b60008281526020808220600019908401810180546001600160a01b031990811690915593019093556001600160a01b03858116808352609f855260409283902080549094169093559051908416815290917f16b7600acff27e39a8a96056b3d533045298de927507f5c1d97e4accde60488c91015b60405180910390a2505050565b600080516020615d9483398151915280546001198101612c465760405162461bcd60e51b815260040161048990615684565b6002825560335460ff1615612c6d5760405162461bcd60e51b815260040161048990615757565b612c75613487565b612c915760405162461bcd60e51b815260040161048990615527565b6001600160a01b0383166000908152603b602052604090205460ff16612cf95760405162461bcd60e51b815260206004820152601760248201527f496e76616c6964207461726765742073747261746567790000000000000000006044820152606401610489565b60008585604051612d0b929190615904565b60405190819003902090506000805b88811015612e7257898982818110612d3457612d346158a8565b9050602002810190612d4691906158be565b604051612d54929190615904565b60405180910390209150828203612da25760405162461bcd60e51b815260206004820152601260248201527129b2b6331031b7b739b7b634b230ba34b7b760711b6044820152606401610489565b600260008381526035602052604090205460ff166004811115612dc757612dc76153c1565b14612e145760405162461bcd60e51b815260206004820152601b60248201527f536f757263652076616c696461746f72206e6f74207374616b656400000000006044820152606401610489565b612e428a8a83818110612e2957612e296158a8565b9050602002810190612e3b91906158be565b8a8a614750565b50600360008381526035602052604090205460ff166004811115612e6857612e686153c1565b5050600101612d1a565b506000612ee08a8a612e856001826156f0565b818110612e9457612e946158a8565b9050602002810190612ea691906158be565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506148e892505050565b60405163052a332f60e41b815260048101829052602481018590529091506001600160a01b038716906352a332f090604401600060405180830381600087803b158015612f2c57600080fd5b505af1158015612f40573d6000803e3d6000fd5b50505060398a905550603a80546001600160a01b0319166001600160a01b038816179055612f6c614127565b6040517f4beca97c57e00ba8aa7e925f6f2959ad978b1c56108d88f9f9dab1f3246e439f90612fa6908c908c908c908c908c908490615bb4565b60405180910390a15050506001825550505050505050565b612fc6613487565b612fe25760405162461bcd60e51b815260040161048990615527565b8060005b8181101561308c576000848483818110613002576130026158a8565b90506020020160208101906130179190614dc3565b6001600160a01b0316036130845760405162461bcd60e51b815260206004820152602e60248201527f43616e206e6f742073657420616e20656d70747920616464726573732061732060448201526d30903932bbb0b932103a37b5b2b760911b6064820152608401610489565b600101612fe6565b507f04c0b9649497d316554306e53678d5f5f5dbc3a06f97dec13ff4cfe98b986bbc60a484846040516130c193929190615bfa565b60405180910390a1610eea60a48484614c98565b6130dd613487565b6130f95760405162461bcd60e51b815260040161048990615527565b603680546001600160a01b0319166001600160a01b0383169081179091556040517f3329861a0008b3348767567d2405492b997abd79a088d0f2cef6b1a09a8e7ff790600090a250565b60335460009061010090046001600160a01b031633146131755760405162461bcd60e51b815260040161048990615720565b60335460ff16156131985760405162461bcd60e51b815260040161048990615757565b600080516020615d94833981519152805460011981016131ca5760405162461bcd60e51b815260040161048990615684565b600282556131d860016142e5565b925060018255505090565b6131eb613487565b6132075760405162461bcd60e51b815260040161048990615527565b808210801561321e57506801bc16d674ec80000081105b801561323b5750673782dace9d90000061323883836156f0565b10155b6132875760405162461bcd60e51b815260206004820152601760248201527f496e636f7272656374206675736520696e74657276616c0000000000000000006044820152606401610489565b6069829055606a81905560408051838152602081018390527fcb8d24e46eb3c402bf344ee60a6576cba9ef2f59ea1af3b311520704924e901a91015b60405180910390a15050565b60405163095ea7b360e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015260001960248301527f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b3906044016020604051808303816000875af115801561335f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133839190615703565b50565b61338e613487565b6133aa5760405162461bcd60e51b815260040161048990615527565b604051631a1b9a0b60e21b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063686e682c90610ff490869086908690600401615c89565b613402613487565b61341e5760405162461bcd60e51b815260040161048990615527565b60a354604080516001600160a01b03928316815291831660208301527fe48386b84419f4d36e0f96c10cc3510b6fb1a33795620c5098b22472bbe90796910160405180910390a160a380546001600160a01b0319166001600160a01b0392909216919091179055565b600061349f600080516020615db48339815191525490565b6001600160a01b0316336001600160a01b031614905090565b6134c0613487565b6134dc5760405162461bcd60e51b815260040161048990615527565b613504817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b0316613524600080516020615db48339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b60335461010090046001600160a01b0316331461358b5760405162461bcd60e51b815260040161048990615720565b60335460ff16156135ae5760405162461bcd60e51b815260040161048990615757565b600080516020615d94833981519152805460011981016135e05760405162461bcd60e51b815260040161048990615684565b60028255604051632d7688a160e11b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690635aed114290613638908a908a908a908a908a90600401615cb1565b600060405180830381600087803b15801561365257600080fd5b505af1158015613666573d6000803e3d6000fd5b5050505060008060005b888110156137ca5789898281811061368a5761368a6158a8565b905060200281019061369c91906158be565b6040516136aa929190615904565b604080519182900390912060008181526035602052919091205490935060ff16915060038260048111156136e0576136e06153c1565b14806136fd575060018260048111156136fb576136fb6153c1565b145b6137495760405162461bcd60e51b815260206004820152601d60248201527f56616c696461746f72206e6f742072656764206f722065786974696e670000006044820152606401610489565b6000838152603560205260409020805460ff19166004179055827f6aecca20726a17c1b81989b2fd09dfdf636bae9e564d4066ca18df62dc1f3dc28b8b84818110613796576137966158a8565b90506020028101906137a891906158be565b8b8b6040516137ba9493929190615914565b60405180910390a2600101613670565b5050506001825550505050505050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146138225760405162461bcd60e51b815260040161048990615928565b600080516020615d94833981519152805460011981016138545760405162461bcd60e51b815260040161048990615684565b600282557f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b0316146138a95760405162461bcd60e51b81526004016104899061595f565b612a3e85858561417f565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146138fc5760405162461bcd60e51b815260040161048990615928565b600080516020615d948339815191528054600119810161392e5760405162461bcd60e51b815260040161048990615684565b600282556040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015613999573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139bd9190615a63565b9050600061010754826139d091906156f0565b90508015613a0857610107829055613a087f000000000000000000000000000000000000000000000000000000000000000082613dd3565b5050600182555050565b6036546001600160a01b03163314613a6c5760405162461bcd60e51b815260206004820152601960248201527f43616c6c6572206973206e6f7420746865204d6f6e69746f72000000000000006044820152606401610489565b600060388190556040517fe765a88a37047c5d793dce22b9ceb5a0f5039d276da139b4c7d29613f341f1109190a1565b606060a4805480602002602001604051908101604052809291908181526020018280548015613af457602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613ad6575b5050505050905090565b6001600160a01b038281166000908152609f60205260409020541615613b5b5760405162461bcd60e51b81526020600482015260126024820152711c151bdad95b88185b1c9958591e481cd95d60721b6044820152606401610489565b6001600160a01b03821615801590613b7b57506001600160a01b03811615155b613bbb5760405162461bcd60e51b8152602060048201526011602482015270496e76616c69642061646472657373657360781b6044820152606401610489565b6001600160a01b038281166000818152609f6020908152604080832080549587166001600160a01b0319968716811790915560a0805460018101825594527f78fdc8d422c49ced035a9edf18d00d3c6a8d81df210f3e5e448e045e77b41e8890930180549095168417909455925190815290917fef6485b84315f9b1483beffa32aae9a0596890395e3d7521f1c5fbb51790e765910160405180910390a25050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052613caf90849061495d565b505050565b60335460ff16613cd65760405162461bcd60e51b8152600401610489906156ac565b6033805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b8251613d339060a4906020860190614cfb565b50815181518114613d7d5760405162461bcd60e51b8152602060048201526014602482015273496e76616c696420696e7075742061727261797360601b6044820152606401610489565b60005b81811015613dcc57613dc4848281518110613d9d57613d9d6158a8565b6020026020010151848381518110613db757613db76158a8565b6020026020010151613afe565b600101613d80565b5050505050565b60008111613e1c5760405162461bcd60e51b81526020600482015260166024820152754d757374206465706f73697420736f6d657468696e6760501b6044820152606401610489565b6040805160008152602081018390526001600160a01b038416917f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f62910160405180910390a25050565b60335460ff1615613e885760405162461bcd60e51b815260040161048990615757565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663e52253816040518163ffffffff1660e01b81526004016020604051808303816000875af1158015613eea573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f0e9190615a63565b9050600060685482613f20919061598a565b905080471015613f725760405162461bcd60e51b815260206004820152601860248201527f496e73756666696369656e74206574682062616c616e636500000000000000006044820152606401610489565b8015610d945760006068819055507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015613fdb57600080fd5b505af1158015613fef573d6000803e3d6000fd5b505060a35461402f93506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116935016905083613c5d565b60a354604080516001600160a01b0392831681527f0000000000000000000000000000000000000000000000000000000000000000909216602083015281018290527ff6c07a063ed4e63808eb8da7112d46dbcd38de2b40a73dbcc9353c5a94c72353906060016132c3565b6001600160a01b0381166140f15760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f7220697320616464726573732830290000000000006044820152606401610489565b61338381614a2f565b60006141098261010754614a96565b905080610107600082825461411e91906156f0565b90915550505050565b60335460ff161561414a5760405162461bcd60e51b815260040161048990615757565b6033805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258613d033390565b600081116141cf5760405162461bcd60e51b815260206004820152601760248201527f4d75737420776974686472617720736f6d657468696e670000000000000000006044820152606401610489565b6001600160a01b03831661421e5760405162461bcd60e51b8152602060048201526016602482015275135d5cdd081cdc1958da599e481c9958da5c1a595b9d60521b6044820152606401610489565b614227816140fa565b61423b6001600160a01b0383168483613c5d565b6040805160008152602081018390526001600160a01b038416917f2717ead6b9200dd235aad468c9809ea400fe33ac69b5bfaa6d3e90fc922b63989101612c07565b6040805160008152602081018390526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016917f2717ead6b9200dd235aad468c9809ea400fe33ac69b5bfaa6d3e90fc922b6398910160405180910390a250565b60006068544710156142fa57611c4582614aae565b60006068544761430a91906156f0565b9050600191506801bc16d674ec80000081106144df5760006143356801bc16d674ec80000083615cf2565b9050806034600082825461434991906156f0565b9091555060009050614364826801bc16d674ec800000615a7c565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b1580156143c157600080fd5b505af11580156143d5573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018690527f000000000000000000000000000000000000000000000000000000000000000016935063a9059cbb925060440190506020604051808303816000875af115801561446a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061448e9190615703565b506144988161427d565b60345460408051848152602081019290925281018290527fbe7040030ff7b347853214bf49820c6d455fedf58f3815f85c7bc5216993682b9060600160405180910390a150505b6000606854476144ef91906156f0565b90506801bc16d674ec80000081106145415760405162461bcd60e51b8152602060048201526015602482015274556e6578706563746564206163636f756e74696e6760581b6044820152606401610489565b80600003614550575050919050565b6069548110156145aa57806068600082825461456c919061598a565b90915550506040518181527f7a745a2c63a535068f52ceca27debd5297bbad5f7f37ec53d044a59d0362445d906020015b60405180910390a1614749565b606a54811115614738577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561460f57600080fd5b505af1158015614623573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018690527f000000000000000000000000000000000000000000000000000000000000000016935063a9059cbb925060440190506020604051808303816000875af11580156146b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146dc9190615703565b506001603460008282546146f091906156f0565b909155506146ff90508161427d565b60345460408051918252602082018390527f6aa7e30787b26429ced603a7aba8b19c4b5d5bcf29a3257da953c8d53bcaa3a6910161459d565b61474184614aae565b949350505050565b5050919050565b6000603084146147a25760405162461bcd60e51b815260206004820152601a60248201527f496e76616c696420736f757263652062797465206c656e6774680000000000006044820152606401610489565b603082146147f25760405162461bcd60e51b815260206004820152601a60248201527f496e76616c6964207461726765742062797465206c656e6774680000000000006044820152606401610489565b6147fa614ac6565b9050600071bbddc7ce488642fb579f8b00f3a5900072516001600160a01b031682878787876040516020016148329493929190615d14565b60408051601f198184030181529082905261484c91615d36565b60006040518083038185875af1925050503d8060008114614889576040519150601f19603f3d011682016040523d82523d6000602084013e61488e565b606091505b50509050806148df5760405162461bcd60e51b815260206004820152601c60248201527f436f6e736f6c69646174696f6e2072657175657374206661696c6564000000006044820152606401610489565b50949350505050565b6000600282600060801b604051602001614903929190615d48565b60408051601f198184030181529082905261491d91615d36565b602060405180830381855afa15801561493a573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190611c459190615a63565b60006149b2826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614b8d9092919063ffffffff16565b805190915015613caf57808060200190518101906149d09190615703565b613caf5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610489565b806001600160a01b0316614a4f600080516020615db48339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a3600080516020615db483398151915255565b6000818310614aa55781614aa7565b825b9392505050565b60008115614abe57614abe614127565b506000919050565b6040516000908190819071bbddc7ce488642fb579f8b00f3a5900072519082818181855afa9150503d8060008114614b1a576040519150601f19603f3d011682016040523d82523d6000602084013e614b1f565b606091505b5091509150818015614b32575060008151115b614b725760405162461bcd60e51b81526020600482015260116024820152704661696c656420746f206765742066656560781b6044820152606401610489565b80806020019051810190614b869190615a63565b9250505090565b6060614741848460008585843b614be65760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610489565b600080866001600160a01b03168587604051614c029190615d36565b60006040518083038185875af1925050503d8060008114614c3f576040519150601f19603f3d011682016040523d82523d6000602084013e614c44565b606091505b5091509150614c54828286614c5f565b979650505050505050565b60608315614c6e575081614aa7565b825115614c7e5782518084602001fd5b8160405162461bcd60e51b81526004016104899190615d80565b828054828255906000526020600020908101928215614ceb579160200282015b82811115614ceb5781546001600160a01b0319166001600160a01b03843516178255602090920191600190910190614cb8565b50614cf7929150614d50565b5090565b828054828255906000526020600020908101928215614ceb579160200282015b82811115614ceb57825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190614d1b565b5b80821115614cf75760008155600101614d51565b6001600160a01b038116811461338357600080fd5b8035614d8581614d65565b919050565b60008060408385031215614d9d57600080fd5b8235614da881614d65565b91506020830135614db881614d65565b809150509250929050565b600060208284031215614dd557600080fd5b8135614aa781614d65565b60008060408385031215614df357600080fd5b8235614dfe81614d65565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715614e4a57614e4a614e0c565b604052919050565b60006001600160401b03821115614e6b57614e6b614e0c565b5060051b60200190565b80356001600160401b0381168114614d8557600080fd5b803563ffffffff81168114614d8557600080fd5b801515811461338357600080fd5b600060a08284031215614ec057600080fd5b60405160a081016001600160401b0381118282101715614ee257614ee2614e0c565b604052905080614ef183614e8c565b8152614eff60208401614e75565b6020820152614f1060408401614e75565b60408201526060830135614f2381614ea0565b6060820152608092830135920191909152919050565b600080600060e08486031215614f4e57600080fd5b83356001600160401b03811115614f6457600080fd5b8401601f81018613614f7557600080fd5b8035614f88614f8382614e52565b614e22565b8082825260208201915060208360051b850101925088831115614faa57600080fd5b6020840193505b82841015614fd357614fc284614e75565b825260209384019390910190614fb1565b955050505060208401359150614fec8560408601614eae565b90509250925092565b600082601f83011261500657600080fd5b8135615014614f8382614e52565b8082825260208201915060208360051b86010192508583111561503657600080fd5b602085015b8381101561505c57803561504e81614d65565b83526020928301920161503b565b5095945050505050565b60008060006060848603121561507b57600080fd5b83356001600160401b0381111561509157600080fd5b61509d86828701614ff5565b93505060208401356001600160401b038111156150b957600080fd5b6150c586828701614ff5565b92505060408401356001600160401b038111156150e157600080fd5b6150ed86828701614ff5565b9150509250925092565b60008083601f84011261510957600080fd5b5081356001600160401b0381111561512057600080fd5b6020830191508360208260051b850101111561513b57600080fd5b9250929050565b6000806000806040858703121561515857600080fd5b84356001600160401b0381111561516e57600080fd5b61517a878288016150f7565b90955093505060208501356001600160401b0381111561519957600080fd5b6151a5878288016150f7565b95989497509550505050565b6000602082840312156151c357600080fd5b5035919050565b600060a082840312156151dc57600080fd5b50919050565b600080600080600080600080610120898b0312156151ff57600080fd5b88356001600160401b0381111561521557600080fd5b6152218b828c016150f7565b90995097505060208901356001600160401b0381111561524057600080fd5b61524c8b828c016150f7565b90975095505060408901356001600160401b0381111561526b57600080fd5b6152778b828c016150f7565b909550935050606089013591506152918a60808b016151ca565b90509295985092959890939650565b600080602083850312156152b357600080fd5b82356001600160401b038111156152c957600080fd5b6152d5858286016150f7565b90969095509350505050565b6000806000606084860312156152f657600080fd5b505081359360208301359350604090920135919050565b60008060008060006060868803121561532557600080fd5b85356001600160401b0381111561533b57600080fd5b615347888289016150f7565b90965094505060208601356001600160401b0381111561536657600080fd5b8601601f8101881361537757600080fd5b80356001600160401b0381111561538d57600080fd5b88602082840101111561539f57600080fd5b602091909101935091506153b560408701614d7a565b90509295509295909350565b634e487b7160e01b600052602160045260246000fd5b60208101600583106153f957634e487b7160e01b600052602160045260246000fd5b91905290565b6000806040838503121561541257600080fd5b50508035926020909101359150565b600080600080600060e0868803121561543957600080fd5b85356001600160401b0381111561544f57600080fd5b61545b888289016150f7565b90965094505060208601356001600160401b0381111561547a57600080fd5b615486888289016150f7565b90945092506153b5905087604088016151ca565b6000806000606084860312156154af57600080fd5b83356154ba81614d65565b925060208401356154ca81614d65565b929592945050506040919091013590565b602080825282518282018190526000918401906040840190835b8181101561551c5783516001600160a01b03168352602093840193909201916001016154f5565b509095945050505050565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b60006020828403121561557057600080fd5b8151614aa781614d65565b6020808252601c908201527f43616c6c6572206973206e6f7420746865205374726174656769737400000000604082015260600190565b600081518084526020840193506020830160005b828110156155ed5781516001600160401b03168652602095860195909101906001016155c6565b5093949350505050565b63ffffffff81511682526001600160401b0360208201511660208301526001600160401b036040820151166040830152606081015115156060830152608081015160808301525050565b6001600160a01b038516815261010060208201819052600090615666908301866155b2565b905083604083015261567b60608301846155f7565b95945050505050565b6020808252600e908201526d1499595b9d1c985b9d0818d85b1b60921b604082015260600190565b60208082526014908201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b81810381811115611c4557611c456156da565b60006020828403121561571557600080fd5b8151614aa781614ea0565b6020808252601d908201527f43616c6c6572206973206e6f7420746865205265676973747261746f72000000604082015260600190565b60208082526010908201526f14185d5cd8589b194e881c185d5cd95960821b604082015260600190565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60008383855260208501945060208460051b8201018360005b8681101561583757838303601f19018852813536879003601e190181126157e957600080fd5b86016020810190356001600160401b0381111561580557600080fd5b80360382131561581457600080fd5b61581f858284615781565b60209a8b019a909550939093019250506001016157c3565b50909695505050505050565b81835260208301925060008160005b848110156155ed576001600160401b0361586b83614e75565b1686526020958601959190910190600101615852565b6040815260006158956040830186886157aa565b8281036020840152614c54818587615843565b634e487b7160e01b600052603260045260246000fd5b6000808335601e198436030181126158d557600080fd5b8301803591506001600160401b038211156158ef57600080fd5b60200191503681900382131561513b57600080fd5b8183823760009101908152919050565b604081526000615895604083018688615781565b60208082526017908201527f43616c6c6572206973206e6f7420746865205661756c74000000000000000000604082015260600190565b602080825260119082015270155b9cdd5c1c1bdc9d195908185cdcd95d607a1b604082015260600190565b80820180821115611c4557611c456156da565b63ffffffff6159ab82614e8c565b1682526001600160401b036159c260208301614e75565b1660208301526001600160401b036159dc60408301614e75565b16604083015260608101356159f081614ea0565b15156060830152608090810135910152565b61012081526000615a1861012083018a8c6157aa565b8281036020840152615a2b81898b615843565b90508281036040840152615a408187896157aa565b915050836060830152615a56608083018461599d565b9998505050505050505050565b600060208284031215615a7557600080fd5b5051919050565b8082028115828204841417611c4557611c456156da565b60008235605e19833603018112615aa957600080fd5b9190910192915050565b60005b83811015615ace578181015183820152602001615ab6565b50506000910152565b60008151808452615aef816020860160208601615ab3565b601f01601f19169290920160200192915050565b608081526000615b1760808301888a615781565b8281036020840152615b298188615ad7565b90508281036040840152615b3e818688615781565b915050826060830152979650505050505050565b604081526000615b66604083018587615781565b9050826020830152949350505050565b8082018281126000831280158216821582161715615b9657615b966156da565b505092915050565b634e487b7160e01b600052603160045260246000fd5b608081526000615bc860808301888a6157aa565b8281036020840152615bdb818789615781565b6001600160a01b03959095166040840152505060600152949350505050565b6040808252845490820181905260008581526020812090916060840190835b81811015615c405783546001600160a01b0316835260019384019360209093019201615c19565b50508381036020808601919091528582520190508460005b85811015615837578135615c6b81614d65565b6001600160a01b031683526020928301929190910190600101615c58565b60e081526000615c9c60e08301866155b2565b905083602083015261474160408301846155f7565b60e081526000615cc560e0830187896157aa565b8281036020840152615cd8818688615843565b915050615ce8604083018461599d565b9695505050505050565b600082615d0f57634e487b7160e01b600052601260045260246000fd5b500490565b8385823760008482016000815283858237600093019283525090949350505050565b60008251615aa9818460208701615ab3565b60008351615d5a818460208801615ab3565b6fffffffffffffffffffffffffffffffff19939093169190920190815260100192915050565b602081526000614aa76020830184615ad756fe53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac45357bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa264697066735822122041f94eda470174d841f7b4a3b5ea3d6671f4ce7dab7623e2c03c271bb943c9e364736f6c634300081c0033", + "libraries": {}, + "devdoc": { + "author": "Origin Protocol Inc", + "details": "This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that is on the contract across multiple blocks (and not just transitory within a transaction) is considered an asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is required since the rewards (reward token) is also in ETH. To simplify the accounting of WETH there is another difference in behavior compared to the other strategies. To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant immediately wraps ETH to WETH and sends it to the Vault. On the other hand any ETH on the contract (across multiple blocks) is there either: - as a result of already accounted for consensus rewards - as a result of not yet accounted for consensus rewards - as a results of not yet accounted for full validator withdrawals (or validator slashes) Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time interval and not immediately.", + "events": { + "Paused(address)": { + "details": "Emitted when the pause is triggered by `account`." + }, + "Unpaused(address)": { + "details": "Emitted when the pause is lifted by `account`." + } + }, + "kind": "dev", + "methods": { + "checkBalance(address)": { + "params": { + "_asset": "Address of weth asset" + }, + "returns": { + "balance": " Total value of (W)ETH" + } + }, + "constructor": { + "params": { + "_baseConfig": "Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI, and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy", + "_beaconChainDepositContract": "Address of the beacon chain deposit contract", + "_feeAccumulator": "Address of the fee accumulator receiving execution layer validator rewards", + "_maxValidators": "Maximum number of validators that can be registered in the strategy", + "_ssvNetwork": "Address of the SSV Network contract", + "_ssvToken": "Address of the Erc20 SSV Token contract", + "_wethAddress": "Address of the Erc20 WETH Token contract" + } + }, + "deposit(address,uint256)": { + "params": { + "_amount": "Amount of assets that were transferred to the strategy by the vault.", + "_asset": "Address of asset to deposit. Has to be WETH." + } + }, + "depositSSV(uint64[],uint256,(uint32,uint64,uint64,bool,uint256))": { + "details": "A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds. uses \"onlyStrategist\" modifier so continuous front-running can't DOS our maintenance service that tries to top up SSV tokens.", + "params": { + "cluster": "The SSV cluster details including the validator count and SSV balance", + "operatorIds": "The operator IDs of the SSV Cluster", + "ssvAmount": "The amount of SSV tokens to be deposited to the SSV cluster" + } + }, + "doAccounting()": { + "details": "This function could in theory be permission-less but lets allow only the Registrator (Defender Action) to call it for now.", + "returns": { + "accountingValid": "true if accounting was successful, false if fuse is blown" + } + }, + "exitSsvValidators(bytes[],uint64[])": { + "params": { + "operatorIds": "The operator IDs of the SSV Cluster", + "publicKeys": "List of SSV validator public keys" + } + }, + "getRewardTokenAddresses()": { + "returns": { + "_0": "address[] the reward token addresses." + } + }, + "initialize(address[],address[],address[])": { + "params": { + "_assets": "Addresses of initial supported assets", + "_pTokens": "Platform Token corresponding addresses", + "_rewardTokenAddresses": "Address of reward token for platform" + } + }, + "manuallyFixAccounting(int256,int256,uint256)": { + "details": "There is a case when a validator(s) gets slashed so much that the eth swept from the beacon chain enters the fuse area and there are no consensus rewards on the contract to \"dip into\"/use. To increase the amount of unaccounted ETH over the fuse end interval we need to reduce the amount of active deposited validators and immediately send WETH to the vault, so it doesn't interfere with further accounting.", + "params": { + "_consensusRewardsDelta": "adjust the accounted for consensus rewards up or down", + "_ethToVaultAmount": "the amount of ETH that gets wrapped into WETH and sent to the Vault", + "_validatorsDelta": "adjust the active validators by up to plus three or minus three" + } + }, + "paused()": { + "details": "Returns true if the contract is paused, and false otherwise." + }, + "registerSsvValidators(bytes[],uint64[],bytes[],uint256,(uint32,uint64,uint64,bool,uint256))": { + "params": { + "cluster": "The SSV cluster details including the validator count and SSV balance", + "operatorIds": "The operator IDs of the SSV Cluster", + "publicKeys": "The public keys of the validators", + "sharesData": "The shares data for each validator", + "ssvAmount": "The amount of SSV tokens to be deposited to the SSV cluster" + } + }, + "removePToken(uint256)": { + "params": { + "_assetIndex": "Index of the asset to be removed" + } + }, + "removeSsvValidators(bytes[],uint64[],(uint32,uint64,uint64,bool,uint256))": { + "params": { + "cluster": "The SSV cluster details including the validator count and SSV balance", + "operatorIds": "The operator IDs of the SSV Cluster", + "publicKeys": "List of SSV validator public keys" + } + }, + "setHarvesterAddress(address)": { + "params": { + "_harvesterAddress": "Address of the harvester contract." + } + }, + "setPTokenAddress(address,address)": { + "params": { + "_asset": "Address for the asset", + "_pToken": "Address for the corresponding platform token" + } + }, + "setRewardTokenAddresses(address[])": { + "params": { + "_rewardTokenAddresses": "Array of reward token addresses" + } + }, + "stakeEth((bytes,bytes,bytes32)[])": { + "params": { + "validators": "A list of validator data needed to stake. The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot. Only the registrator can call this function." + } + }, + "supportsAsset(address)": { + "params": { + "_asset": "The address of the asset token." + } + }, + "transferGovernance(address)": { + "params": { + "_newGovernor": "Address of the new Governor" + } + }, + "transferToken(address,uint256)": { + "params": { + "_amount": "Amount of the asset to transfer", + "_asset": "Address for the asset" + } + }, + "withdraw(address,address,uint256)": { + "params": { + "_amount": "Amount of WETH to withdraw", + "_asset": "WETH to withdraw", + "_recipient": "Address to receive withdrawn assets" + } + }, + "withdrawSSV(uint64[],uint256,(uint32,uint64,uint64,bool,uint256))": { + "details": "A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.", + "params": { + "cluster": "The SSV cluster details including the validator count and SSV balance", + "operatorIds": "The operator IDs of the SSV Cluster", + "ssvAmount": "The amount of SSV tokens to be deposited to the SSV cluster" + } + } + }, + "stateVariables": { + "FEE_ACCUMULATOR_ADDRESS": { + "details": "this address will receive maximal extractable value (MEV) rewards. These are rewards for arranging transactions in a way that benefits the validator." + }, + "depositedWethAccountedFor": { + "details": "This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately deposit it to an underlying platform. Rather a special privilege account stakes it to the validators. For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track of WETH that has already been accounted for. This value represents the amount of WETH balance of this contract that has already been accounted for by the deposit events. It is important to note that this variable is not concerned with WETH that is a result of full/partial withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to be staked." + } + }, + "title": "Native Staking SSV Strategy", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "BEACON_CHAIN_DEPOSIT_CONTRACT()": { + "notice": "The address of the beacon chain deposit contract" + }, + "FEE_ACCUMULATOR_ADDRESS()": { + "notice": "Fee collector address" + }, + "FULL_STAKE()": { + "notice": "The maximum amount of ETH that can be staked by a validator" + }, + "MAX_VALIDATORS()": { + "notice": "Maximum number of validators that can be registered in this strategy" + }, + "MIN_FIX_ACCOUNTING_CADENCE()": { + "notice": "The minimum amount of blocks that need to pass between two calls to manuallyFixAccounting" + }, + "SSV_NETWORK()": { + "notice": "The address of the SSV Network contract used to interface with" + }, + "SSV_TOKEN()": { + "notice": "SSV ERC20 token that serves as a payment for operating SSV validators" + }, + "VAULT_ADDRESS()": { + "notice": "Address of the OETH Vault proxy contract" + }, + "WETH()": { + "notice": "The address of the Wrapped ETH (WETH) token contract" + }, + "activeDepositedValidators()": { + "notice": "The number of validators that have 32 (!) ETH actively deposited. When a new deposit to a validator happens this number increases, when a validator exit is detected this number decreases." + }, + "addTargetStrategy(address)": { + "notice": "Adds support for a new staking strategy that can be used for consolidation." + }, + "assetToPToken(address)": { + "notice": "asset => pToken (Platform Specific Token Address)" + }, + "checkBalance(address)": { + "notice": "Returns the total value of (W)ETH that is staked to the validators and WETH deposits that are still to be staked. This does not include ETH from consensus rewards sitting in this strategy or ETH from MEV rewards in the FeeAccumulator. These rewards are harvested and sent to the Dripper so will eventually be sent to the Vault as WETH." + }, + "claimGovernance()": { + "notice": "Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor." + }, + "collectRewardTokens()": { + "notice": "Collect accumulated reward token and send to Vault." + }, + "consensusRewards()": { + "notice": "Keeps track of the total consensus rewards swept from the beacon chain" + }, + "consolidationCount()": { + "notice": "Number of validators currently being consolidated" + }, + "consolidationTargetStrategies(address)": { + "notice": "Mapping of support target staking strategies that can be used for consolidation" + }, + "deposit(address,uint256)": { + "notice": "Unlike other strategies, this does not deposit assets into the underlying platform. It just checks the asset is WETH and emits the Deposit event. To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used. Will NOT revert if the strategy is paused from an accounting failure." + }, + "depositAll()": { + "notice": "Unlike other strategies, this does not deposit assets into the underlying platform. It just emits the Deposit event. To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used. Will NOT revert if the strategy is paused from an accounting failure." + }, + "depositSSV(uint64[],uint256,(uint32,uint64,uint64,bool,uint256))": { + "notice": "Deposits more SSV Tokens to the SSV Network contract which is used to pay the SSV Operators." + }, + "doAccounting()": { + "notice": "This notion page offers a good explanation of how the accounting functions https://www.notion.so/originprotocol/Limited-simplified-native-staking-accounting-67a217c8420d40678eb943b9da0ee77d In short, after dividing by 32, if the ETH remaining on the contract falls between 0 and fuseIntervalStart, the accounting function will treat that ETH as Beacon chain consensus rewards. On the contrary, if after dividing by 32, the ETH remaining on the contract falls between fuseIntervalEnd and 32, the accounting function will treat that as a validator slashing.Perform the accounting attributing beacon chain ETH to either full or partial withdrawals. Returns true when accounting is valid and fuse isn't \"blown\". Returns false when fuse is blown." + }, + "exitSsvValidators(bytes[],uint64[])": { + "notice": "Exit validators from the Beacon chain. The staked ETH will eventually swept to this native staking strategy. Only the registrator can call this function." + }, + "fuseIntervalEnd()": { + "notice": "end of fuse interval" + }, + "fuseIntervalStart()": { + "notice": "start of fuse interval" + }, + "getRewardTokenAddresses()": { + "notice": "Get the reward token addresses." + }, + "governor()": { + "notice": "Returns the address of the current Governor." + }, + "harvesterAddress()": { + "notice": "Address of the Harvester contract allowed to collect reward tokens" + }, + "initialize(address[],address[],address[])": { + "notice": "Set up initial internal state including 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract." + }, + "isGovernor()": { + "notice": "Returns true if the caller is the current Governor." + }, + "lastFixAccountingBlockNumber()": { + "notice": "last block number manuallyFixAccounting has been called" + }, + "manuallyFixAccounting(int256,int256,uint256)": { + "notice": "Allow the Strategist to fix the accounting of this strategy and unpause." + }, + "platformAddress()": { + "notice": "Address of the underlying platform" + }, + "registerSsvValidators(bytes[],uint64[],bytes[],uint256,(uint32,uint64,uint64,bool,uint256))": { + "notice": "Registers a new validator in the SSV Cluster. Only the registrator can call this function." + }, + "removePToken(uint256)": { + "notice": "Remove a supported asset by passing its index. This method can only be called by the system Governor" + }, + "removeSsvValidators(bytes[],uint64[],(uint32,uint64,uint64,bool,uint256))": { + "notice": "Remove validators from the SSV Cluster. Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain. If removed before the validator has exited the beacon chain will result in the validator being slashed. Only the registrator can call this function." + }, + "resetStakeETHTally()": { + "notice": "Reset the stakeETHTally" + }, + "rewardTokenAddresses(uint256)": { + "notice": "Address of the reward tokens. eg CRV, BAL, CVX, AURA" + }, + "safeApproveAllTokens()": { + "notice": "Approves the SSV Network contract to transfer SSV tokens for deposits" + }, + "setFeeRecipient()": { + "notice": "Set the FeeAccumulator as the address for SSV validators to send MEV rewards to" + }, + "setFuseInterval(uint256,uint256)": { + "notice": "set fuse interval values" + }, + "setHarvesterAddress(address)": { + "notice": "Set the Harvester contract that can collect rewards." + }, + "setPTokenAddress(address,address)": { + "notice": "Provide support for asset by passing its pToken address. This method can only be called by the system Governor" + }, + "setRegistrator(address)": { + "notice": "Set the address of the registrator which can register, exit and remove validators" + }, + "setRewardTokenAddresses(address[])": { + "notice": "Set the reward token addresses. Any old addresses will be overwritten." + }, + "setStakeETHThreshold(uint256)": { + "notice": "Set the amount of ETH that can be staked before staking monitor" + }, + "setStakingMonitor(address)": { + "notice": "Set the address of the staking monitor that is allowed to reset stakeETHTally" + }, + "stakeETHTally()": { + "notice": "Amount of ETH that has been staked since the `stakingMonitor` last called `resetStakeETHTally`. This can not go above `stakeETHThreshold`." + }, + "stakeETHThreshold()": { + "notice": "Amount of ETH that can be staked before staking on the contract is suspended and the `stakingMonitor` needs to approve further staking by calling `resetStakeETHTally`" + }, + "stakeEth((bytes,bytes,bytes32)[])": { + "notice": "Stakes WETH to the node validators" + }, + "stakingMonitor()": { + "notice": "The account that is allowed to modify stakeETHThreshold and reset stakeETHTally" + }, + "supportsAsset(address)": { + "notice": "Returns bool indicating whether asset is supported by strategy." + }, + "transferGovernance(address)": { + "notice": "Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete" + }, + "transferToken(address,uint256)": { + "notice": "Transfer token to governor. Intended for recovering tokens stuck in strategy contracts, i.e. mistaken sends." + }, + "validatorRegistrator()": { + "notice": "Address of the registrator - allowed to register, exit and remove validators" + }, + "validatorsStates(bytes32)": { + "notice": "State of the validators keccak256(pubKey) => state" + }, + "vaultAddress()": { + "notice": "Address of the OToken vault" + }, + "withdraw(address,address,uint256)": { + "notice": "Withdraw WETH from this contract. Used only if some WETH for is lingering on the contract. That can happen when: - after mints if the strategy is the default - time between depositToStrategy and stakeEth - the deposit was not a multiple of 32 WETH - someone sent WETH directly to this contract Will NOT revert if the strategy is paused from an accounting failure." + }, + "withdrawAll()": { + "notice": "transfer all WETH deposits back to the vault. This does not withdraw from the validators. That has to be done separately with the `exitSsvValidator` and `removeSsvValidator` operations. This does not withdraw any execution rewards from the FeeAccumulator or consensus rewards in this strategy. Any ETH in this strategy that was swept from a full validator withdrawal will not be withdrawn. ETH from full validator withdrawals is sent to the Vault using `doAccounting`. Will NOT revert if the strategy is paused from an accounting failure." + }, + "withdrawSSV(uint64[],uint256,(uint32,uint64,uint64,bool,uint256))": { + "notice": "Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators." + } + }, + "notice": "Strategy to deploy funds into DVT validators powered by the SSV Network", + "version": 1 + }, + "storageLayout": { + "storage": [ + { + "astId": 6858, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "initialized", + "offset": 0, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 6861, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "initializing", + "offset": 1, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 6901, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "______gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage" + }, + { + "astId": 17, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "_paused", + "offset": 0, + "slot": "51", + "type": "t_bool" + }, + { + "astId": 4149, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "validatorRegistrator", + "offset": 1, + "slot": "51", + "type": "t_address" + }, + { + "astId": 4152, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "activeDepositedValidators", + "offset": 0, + "slot": "52", + "type": "t_uint256" + }, + { + "astId": 4158, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "validatorsStates", + "offset": 0, + "slot": "53", + "type": "t_mapping(t_bytes32,t_enum(VALIDATOR_STATE)4187)" + }, + { + "astId": 4161, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "stakingMonitor", + "offset": 0, + "slot": "54", + "type": "t_address" + }, + { + "astId": 4164, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "stakeETHThreshold", + "offset": 0, + "slot": "55", + "type": "t_uint256" + }, + { + "astId": 4167, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "stakeETHTally", + "offset": 0, + "slot": "56", + "type": "t_uint256" + }, + { + "astId": 4170, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "consolidationCount", + "offset": 0, + "slot": "57", + "type": "t_uint256" + }, + { + "astId": 4172, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "consolidationTargetStrategy", + "offset": 0, + "slot": "58", + "type": "t_address" + }, + { + "astId": 4177, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "consolidationTargetStrategies", + "offset": 0, + "slot": "59", + "type": "t_mapping(t_address,t_bool)" + }, + { + "astId": 4181, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "__gap", + "offset": 0, + "slot": "60", + "type": "t_array(t_uint256)44_storage" + }, + { + "astId": 3631, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "consensusRewards", + "offset": 0, + "slot": "104", + "type": "t_uint256" + }, + { + "astId": 3634, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "fuseIntervalStart", + "offset": 0, + "slot": "105", + "type": "t_uint256" + }, + { + "astId": 3637, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "fuseIntervalEnd", + "offset": 0, + "slot": "106", + "type": "t_uint256" + }, + { + "astId": 3640, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "lastFixAccountingBlockNumber", + "offset": 0, + "slot": "107", + "type": "t_uint256" + }, + { + "astId": 3644, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "__gap", + "offset": 0, + "slot": "108", + "type": "t_array(t_uint256)49_storage" + }, + { + "astId": 6981, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "_deprecated_platformAddress", + "offset": 0, + "slot": "157", + "type": "t_address" + }, + { + "astId": 6984, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "_deprecated_vaultAddress", + "offset": 0, + "slot": "158", + "type": "t_address" + }, + { + "astId": 6989, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "assetToPToken", + "offset": 0, + "slot": "159", + "type": "t_mapping(t_address,t_address)" + }, + { + "astId": 6993, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "assetsMapped", + "offset": 0, + "slot": "160", + "type": "t_array(t_address)dyn_storage" + }, + { + "astId": 6995, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "_deprecated_rewardTokenAddress", + "offset": 0, + "slot": "161", + "type": "t_address" + }, + { + "astId": 6997, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "_deprecated_rewardLiquidationThreshold", + "offset": 0, + "slot": "162", + "type": "t_uint256" + }, + { + "astId": 7000, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "harvesterAddress", + "offset": 0, + "slot": "163", + "type": "t_address" + }, + { + "astId": 7004, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "rewardTokenAddresses", + "offset": 0, + "slot": "164", + "type": "t_array(t_address)dyn_storage" + }, + { + "astId": 7008, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "_reserved", + "offset": 0, + "slot": "165", + "type": "t_array(t_int256)98_storage" + }, + { + "astId": 3102, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "depositedWethAccountedFor", + "offset": 0, + "slot": "263", + "type": "t_uint256" + }, + { + "astId": 3106, + "contract": "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol:NativeStakingSSVStrategy", + "label": "__gap", + "offset": 0, + "slot": "264", + "type": "t_array(t_uint256)49_storage" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_address)dyn_storage": { + "base": "t_address", + "encoding": "dynamic_array", + "label": "address[]", + "numberOfBytes": "32" + }, + "t_array(t_int256)98_storage": { + "base": "t_int256", + "encoding": "inplace", + "label": "int256[98]", + "numberOfBytes": "3136" + }, + "t_array(t_uint256)44_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[44]", + "numberOfBytes": "1408" + }, + "t_array(t_uint256)49_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)50_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "encoding": "inplace", + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes32": { + "encoding": "inplace", + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_enum(VALIDATOR_STATE)4187": { + "encoding": "inplace", + "label": "enum ValidatorRegistrator.VALIDATOR_STATE", + "numberOfBytes": "1" + }, + "t_int256": { + "encoding": "inplace", + "label": "int256", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_address)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => address)", + "numberOfBytes": "32", + "value": "t_address" + }, + "t_mapping(t_address,t_bool)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => bool)", + "numberOfBytes": "32", + "value": "t_bool" + }, + "t_mapping(t_bytes32,t_enum(VALIDATOR_STATE)4187)": { + "encoding": "mapping", + "key": "t_bytes32", + "label": "mapping(bytes32 => enum ValidatorRegistrator.VALIDATOR_STATE)", + "numberOfBytes": "32", + "value": "t_enum(VALIDATOR_STATE)4187" + }, + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/NativeStakingSSVStrategyProxy.json b/contracts/deployments/hoodi/NativeStakingSSVStrategyProxy.json new file mode 100644 index 0000000000..2594ed6a15 --- /dev/null +++ b/contracts/deployments/hoodi/NativeStakingSSVStrategyProxy.json @@ -0,0 +1,297 @@ +{ + "address": "0x95DdB082cf2b8Bb4a4A358A8817a848cE32e446b", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "GovernorshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "PendingGovernorshipTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "claimGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "governor", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "implementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_logic", + "type": "address" + }, + { + "internalType": "address", + "name": "_initGovernor", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "isGovernor", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newGovernor", + "type": "address" + } + ], + "name": "transferGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } + ], + "transactionHash": "0x1936ed14a51c49c12d98269af7adc8bad5b3122ee1c09a0745d77823125251ad", + "receipt": { + "to": null, + "from": "0xFD9E6005187F448957a0972a7d0C0A6dA2911236", + "contractAddress": "0x95DdB082cf2b8Bb4a4A358A8817a848cE32e446b", + "transactionIndex": 2, + "gasUsed": "599335", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000008000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000004040000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000200000000000000000000000000000000000000000000010000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0xd5114d0e256d153e402f8340ff37537e118080e6927e4ac9db738974f1e426ed", + "transactionHash": "0x1936ed14a51c49c12d98269af7adc8bad5b3122ee1c09a0745d77823125251ad", + "logs": [ + { + "transactionIndex": 2, + "blockNumber": 828243, + "transactionHash": "0x1936ed14a51c49c12d98269af7adc8bad5b3122ee1c09a0745d77823125251ad", + "address": "0x95DdB082cf2b8Bb4a4A358A8817a848cE32e446b", + "topics": [ + "0xc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000fd9e6005187f448957a0972a7d0c0a6da2911236" + ], + "data": "0x", + "logIndex": 0, + "blockHash": "0xd5114d0e256d153e402f8340ff37537e118080e6927e4ac9db738974f1e426ed" + } + ], + "blockNumber": 828243, + "cumulativeGasUsed": "641335", + "status": 1, + "byzantium": true + }, + "args": [], + "numDeployments": 1, + "solcInputHash": "5518586a577179807341642341c56a4f", + "metadata": "{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"GovernorshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"PendingGovernorshipTransfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"implementation\",\"type\":\"address\"}],\"name\":\"Upgraded\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"governor\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"implementation\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_logic\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_initGovernor\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isGovernor\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newGovernor\",\"type\":\"address\"}],\"name\":\"transferGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newImplementation\",\"type\":\"address\"}],\"name\":\"upgradeTo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"upgradeToAndCall\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"}],\"devdoc\":{\"events\":{\"Upgraded(address)\":{\"details\":\"Emitted when the implementation is upgraded.\",\"params\":{\"implementation\":\"Address of the new implementation.\"}}},\"kind\":\"dev\",\"methods\":{\"admin()\":{\"returns\":{\"_0\":\"The address of the proxy admin/it's also the governor.\"}},\"implementation()\":{\"returns\":{\"_0\":\"The address of the implementation.\"}},\"initialize(address,address,bytes)\":{\"details\":\"Contract initializer with Governor enforcement\",\"params\":{\"_data\":\"Data to send as msg.data to the implementation to initialize the proxied contract. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.\",\"_initGovernor\":\"Address of the initial Governor.\",\"_logic\":\"Address of the initial implementation.\"}},\"transferGovernance(address)\":{\"params\":{\"_newGovernor\":\"Address of the new Governor\"}},\"upgradeTo(address)\":{\"details\":\"Upgrade the backing implementation of the proxy. Only the admin can call this function.\",\"params\":{\"_newImplementation\":\"Address of the new implementation.\"}},\"upgradeToAndCall(address,bytes)\":{\"details\":\"Upgrade the backing implementation of the proxy and call a function on the new implementation. This is useful to initialize the proxied contract.\",\"params\":{\"data\":\"Data to send as msg.data in the low level call. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\",\"newImplementation\":\"Address of the new implementation.\"}}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"claimGovernance()\":{\"notice\":\"Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor.\"},\"governor()\":{\"notice\":\"Returns the address of the current Governor.\"},\"isGovernor()\":{\"notice\":\"Returns true if the caller is the current Governor.\"},\"transferGovernance(address)\":{\"notice\":\"Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete\"}},\"notice\":\"NativeStakingSSVStrategyProxy delegates calls to NativeStakingSSVStrategy implementation\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/proxies/Proxies.sol\":\"NativeStakingSSVStrategyProxy\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize, which returns 0 for contracts in\\n // construction, since the code is only stored at the end of the\\n // constructor execution.\\n\\n uint256 size;\\n assembly {\\n size := extcodesize(account)\\n }\\n return size > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCall(target, data, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n require(isContract(target), \\\"Address: static call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(isContract(target), \\\"Address: delegate call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x51b758a8815ecc9596c66c37d56b1d33883a444631a3f916b9fe65cb863ef7c4\",\"license\":\"MIT\"},\"contracts/governance/Governable.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\\n * from owner to governor and renounce methods removed. Does not use\\n * Context.sol like Ownable.sol does for simplification.\\n * @author Origin Protocol Inc\\n */\\nabstract contract Governable {\\n // Storage position of the owner and pendingOwner of the contract\\n // keccak256(\\\"OUSD.governor\\\");\\n bytes32 private constant governorPosition =\\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\\n\\n // keccak256(\\\"OUSD.pending.governor\\\");\\n bytes32 private constant pendingGovernorPosition =\\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\\n\\n // keccak256(\\\"OUSD.reentry.status\\\");\\n bytes32 private constant reentryStatusPosition =\\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\\n\\n // See OpenZeppelin ReentrancyGuard implementation\\n uint256 constant _NOT_ENTERED = 1;\\n uint256 constant _ENTERED = 2;\\n\\n event PendingGovernorshipTransfer(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n event GovernorshipTransferred(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n /**\\n * @notice Returns the address of the current Governor.\\n */\\n function governor() public view returns (address) {\\n return _governor();\\n }\\n\\n /**\\n * @dev Returns the address of the current Governor.\\n */\\n function _governor() internal view returns (address governorOut) {\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n governorOut := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Returns the address of the pending Governor.\\n */\\n function _pendingGovernor()\\n internal\\n view\\n returns (address pendingGovernor)\\n {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n pendingGovernor := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the Governor.\\n */\\n modifier onlyGovernor() {\\n require(isGovernor(), \\\"Caller is not the Governor\\\");\\n _;\\n }\\n\\n /**\\n * @notice Returns true if the caller is the current Governor.\\n */\\n function isGovernor() public view returns (bool) {\\n return msg.sender == _governor();\\n }\\n\\n function _setGovernor(address newGovernor) internal {\\n emit GovernorshipTransferred(_governor(), newGovernor);\\n\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @dev Prevents a contract from calling itself, directly or indirectly.\\n * Calling a `nonReentrant` function from another `nonReentrant`\\n * function is not supported. It is possible to prevent this from happening\\n * by making the `nonReentrant` function external, and make it call a\\n * `private` function that does the actual work.\\n */\\n modifier nonReentrant() {\\n bytes32 position = reentryStatusPosition;\\n uint256 _reentry_status;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n _reentry_status := sload(position)\\n }\\n\\n // On the first call to nonReentrant, _notEntered will be true\\n require(_reentry_status != _ENTERED, \\\"Reentrant call\\\");\\n\\n // Any calls to nonReentrant after this point will fail\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _ENTERED)\\n }\\n\\n _;\\n\\n // By storing the original value once again, a refund is triggered (see\\n // https://eips.ethereum.org/EIPS/eip-2200)\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _NOT_ENTERED)\\n }\\n }\\n\\n function _setPendingGovernor(address newGovernor) internal {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the current Governor. Must be claimed for this to complete\\n * @param _newGovernor Address of the new Governor\\n */\\n function transferGovernance(address _newGovernor) external onlyGovernor {\\n _setPendingGovernor(_newGovernor);\\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\\n }\\n\\n /**\\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the new Governor.\\n */\\n function claimGovernance() external {\\n require(\\n msg.sender == _pendingGovernor(),\\n \\\"Only the pending Governor can complete the claim\\\"\\n );\\n _changeGovernor(msg.sender);\\n }\\n\\n /**\\n * @dev Change Governance of the contract to a new account (`newGovernor`).\\n * @param _newGovernor Address of the new Governor\\n */\\n function _changeGovernor(address _newGovernor) internal {\\n require(_newGovernor != address(0), \\\"New Governor is address(0)\\\");\\n _setGovernor(_newGovernor);\\n }\\n}\\n\",\"keccak256\":\"0xf32f873c8bfbacf2e5f01d0cf37bc7f54fbd5aa656e95c8a599114229946f107\",\"license\":\"BUSL-1.1\"},\"contracts/proxies/InitializeGovernedUpgradeabilityProxy.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { Address } from \\\"@openzeppelin/contracts/utils/Address.sol\\\";\\n\\nimport { Governable } from \\\"../governance/Governable.sol\\\";\\n\\n/**\\n * @title BaseGovernedUpgradeabilityProxy\\n * @dev This contract combines an upgradeability proxy with our governor system.\\n * It is based on an older version of OpenZeppelins BaseUpgradeabilityProxy\\n * with Solidity ^0.8.0.\\n * @author Origin Protocol Inc\\n */\\ncontract InitializeGovernedUpgradeabilityProxy is Governable {\\n /**\\n * @dev Emitted when the implementation is upgraded.\\n * @param implementation Address of the new implementation.\\n */\\n event Upgraded(address indexed implementation);\\n\\n constructor() {\\n _setGovernor(msg.sender);\\n }\\n\\n /**\\n * @dev Contract initializer with Governor enforcement\\n * @param _logic Address of the initial implementation.\\n * @param _initGovernor Address of the initial Governor.\\n * @param _data Data to send as msg.data to the implementation to initialize\\n * the proxied contract.\\n * It should include the signature and the parameters of the function to be\\n * called, as described in\\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\\n * This parameter is optional, if no data is given the initialization call\\n * to proxied contract will be skipped.\\n */\\n function initialize(\\n address _logic,\\n address _initGovernor,\\n bytes calldata _data\\n ) public payable onlyGovernor {\\n require(_implementation() == address(0));\\n require(_logic != address(0), \\\"Implementation not set\\\");\\n assert(\\n IMPLEMENTATION_SLOT ==\\n bytes32(uint256(keccak256(\\\"eip1967.proxy.implementation\\\")) - 1)\\n );\\n _setImplementation(_logic);\\n if (_data.length > 0) {\\n (bool success, ) = _logic.delegatecall(_data);\\n require(success);\\n }\\n _changeGovernor(_initGovernor);\\n }\\n\\n /**\\n * @return The address of the proxy admin/it's also the governor.\\n */\\n function admin() external view returns (address) {\\n return _governor();\\n }\\n\\n /**\\n * @return The address of the implementation.\\n */\\n function implementation() external view returns (address) {\\n return _implementation();\\n }\\n\\n /**\\n * @dev Upgrade the backing implementation of the proxy.\\n * Only the admin can call this function.\\n * @param _newImplementation Address of the new implementation.\\n */\\n function upgradeTo(address _newImplementation) external onlyGovernor {\\n _upgradeTo(_newImplementation);\\n }\\n\\n /**\\n * @dev Upgrade the backing implementation of the proxy and call a function\\n * on the new implementation.\\n * This is useful to initialize the proxied contract.\\n * @param newImplementation Address of the new implementation.\\n * @param data Data to send as msg.data in the low level call.\\n * It should include the signature and the parameters of the function to be called, as described in\\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\\n */\\n function upgradeToAndCall(address newImplementation, bytes calldata data)\\n external\\n payable\\n onlyGovernor\\n {\\n _upgradeTo(newImplementation);\\n (bool success, ) = newImplementation.delegatecall(data);\\n require(success);\\n }\\n\\n /**\\n * @dev Fallback function.\\n * Implemented entirely in `_fallback`.\\n */\\n fallback() external payable {\\n _fallback();\\n }\\n\\n /**\\n * @dev Delegates execution to an implementation contract.\\n * This is a low level function that doesn't return to its internal call site.\\n * It will return to the external caller whatever the implementation returns.\\n * @param _impl Address to delegate.\\n */\\n function _delegate(address _impl) internal {\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n // Copy msg.data. We take full control of memory in this inline assembly\\n // block because it will not return to Solidity code. We overwrite the\\n // Solidity scratch pad at memory position 0.\\n calldatacopy(0, 0, calldatasize())\\n\\n // Call the implementation.\\n // out and outsize are 0 because we don't know the size yet.\\n let result := delegatecall(gas(), _impl, 0, calldatasize(), 0, 0)\\n\\n // Copy the returned data.\\n returndatacopy(0, 0, returndatasize())\\n\\n switch result\\n // delegatecall returns 0 on error.\\n case 0 {\\n revert(0, returndatasize())\\n }\\n default {\\n return(0, returndatasize())\\n }\\n }\\n }\\n\\n /**\\n * @dev Function that is run as the first thing in the fallback function.\\n * Can be redefined in derived contracts to add functionality.\\n * Redefinitions must call super._willFallback().\\n */\\n function _willFallback() internal {}\\n\\n /**\\n * @dev fallback implementation.\\n * Extracted to enable manual triggering.\\n */\\n function _fallback() internal {\\n _willFallback();\\n _delegate(_implementation());\\n }\\n\\n /**\\n * @dev Storage slot with the address of the current implementation.\\n * This is the keccak-256 hash of \\\"eip1967.proxy.implementation\\\" subtracted by 1, and is\\n * validated in the constructor.\\n */\\n bytes32 internal constant IMPLEMENTATION_SLOT =\\n 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\\n\\n /**\\n * @dev Returns the current implementation.\\n * @return impl Address of the current implementation\\n */\\n function _implementation() internal view returns (address impl) {\\n bytes32 slot = IMPLEMENTATION_SLOT;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n impl := sload(slot)\\n }\\n }\\n\\n /**\\n * @dev Upgrades the proxy to a new implementation.\\n * @param newImplementation Address of the new implementation.\\n */\\n function _upgradeTo(address newImplementation) internal {\\n _setImplementation(newImplementation);\\n emit Upgraded(newImplementation);\\n }\\n\\n /**\\n * @dev Sets the implementation address of the proxy.\\n * @param newImplementation Address of the new implementation.\\n */\\n function _setImplementation(address newImplementation) internal {\\n require(\\n Address.isContract(newImplementation),\\n \\\"Cannot set a proxy implementation to a non-contract address\\\"\\n );\\n\\n bytes32 slot = IMPLEMENTATION_SLOT;\\n\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(slot, newImplementation)\\n }\\n }\\n}\\n\",\"keccak256\":\"0x3f46ae39dced6fa90d8b65aa31a0a331438544ec876e2ec961a8e6b22e2e06c7\",\"license\":\"BUSL-1.1\"},\"contracts/proxies/Proxies.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { InitializeGovernedUpgradeabilityProxy } from \\\"./InitializeGovernedUpgradeabilityProxy.sol\\\";\\n\\n/**\\n * @notice OUSDProxy delegates calls to an OUSD implementation\\n */\\ncontract OUSDProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice WrappedOUSDProxy delegates calls to a WrappedOUSD implementation\\n */\\ncontract WrappedOUSDProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice VaultProxy delegates calls to a Vault implementation\\n */\\ncontract VaultProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice CompoundStrategyProxy delegates calls to a CompoundStrategy implementation\\n */\\ncontract CompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice AaveStrategyProxy delegates calls to a AaveStrategy implementation\\n */\\ncontract AaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice ConvexStrategyProxy delegates calls to a ConvexStrategy implementation\\n */\\ncontract ConvexStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice HarvesterProxy delegates calls to a Harvester implementation\\n */\\ncontract HarvesterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice DripperProxy delegates calls to a Dripper implementation\\n */\\ncontract DripperProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice MorphoCompoundStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\\n */\\ncontract MorphoCompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice ConvexOUSDMetaStrategyProxy delegates calls to a ConvexOUSDMetaStrategy implementation\\n */\\ncontract ConvexOUSDMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice MorphoAaveStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\\n */\\ncontract MorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHProxy delegates calls to nowhere for now\\n */\\ncontract OETHProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice WOETHProxy delegates calls to nowhere for now\\n */\\ncontract WOETHProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHVaultProxy delegates calls to a Vault implementation\\n */\\ncontract OETHVaultProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHDripperProxy delegates calls to a OETHDripper implementation\\n */\\ncontract OETHDripperProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHHarvesterProxy delegates calls to a Harvester implementation\\n */\\ncontract OETHHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice CurveEthStrategyProxy delegates calls to a CurveEthStrategy implementation\\n */\\ncontract ConvexEthMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice BuybackProxy delegates calls to Buyback implementation\\n */\\ncontract BuybackProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHMorphoAaveStrategyProxy delegates calls to a MorphoAaveStrategy implementation\\n */\\ncontract OETHMorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHBalancerMetaPoolrEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\\n */\\ncontract OETHBalancerMetaPoolrEthStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice OETHBalancerMetaPoolwstEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\\n */\\ncontract OETHBalancerMetaPoolwstEthStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MakerDsrStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MakerDsrStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHBuybackProxy delegates calls to Buyback implementation\\n */\\ncontract OETHBuybackProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice BridgedWOETHProxy delegates calls to BridgedWOETH implementation\\n */\\ncontract BridgedWOETHProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice NativeStakingSSVStrategyProxy delegates calls to NativeStakingSSVStrategy implementation\\n */\\ncontract NativeStakingSSVStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingFeeAccumulatorProxy delegates calls to FeeAccumulator implementation\\n */\\ncontract NativeStakingFeeAccumulatorProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingSSVStrategy2Proxy delegates calls to NativeStakingSSVStrategy implementation\\n */\\ncontract NativeStakingSSVStrategy2Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingFeeAccumulator2Proxy delegates calls to FeeAccumulator implementation\\n */\\ncontract NativeStakingFeeAccumulator2Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingSSVStrategy3Proxy delegates calls to NativeStakingSSVStrategy implementation\\n */\\ncontract NativeStakingSSVStrategy3Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingFeeAccumulator3Proxy delegates calls to FeeAccumulator implementation\\n */\\ncontract NativeStakingFeeAccumulator3Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MetaMorphoStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MetaMorphoStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice ARMBuybackProxy delegates calls to Buyback implementation\\n */\\ncontract ARMBuybackProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice MorphoGauntletPrimeUSDCStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MorphoGauntletPrimeUSDCStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MorphoGauntletPrimeUSDTStrategyProxy delegates calls to a Generalized4626USDTStrategy implementation\\n */\\ncontract MorphoGauntletPrimeUSDTStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice CurvePoolBoosterProxy delegates calls to a CurvePoolBooster implementation\\n */\\ncontract CurvePoolBoosterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHFixedRateDripperProxy delegates calls to a OETHFixedRateDripper implementation\\n */\\ncontract OETHFixedRateDripperProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHSimpleHarvesterProxy delegates calls to a OETHSimpleHarvester implementation\\n */\\ncontract OETHSimpleHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice PoolBoostCentralRegistryProxy delegates calls to the PoolBoostCentralRegistry implementation\\n */\\ncontract PoolBoostCentralRegistryProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MakerSSRStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MakerSSRStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OUSDCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\\n */\\ncontract OUSDCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\\n */\\ncontract OETHCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice CompoundingStakingSSVStrategyProxy delegates calls to a CompoundingStakingSSVStrategy implementation\\n */\\ncontract CompoundingStakingSSVStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\",\"keccak256\":\"0x71e10cf279337e54682c7b0132825fa307992782367bd296c44b664b7b705db4\",\"license\":\"BUSL-1.1\"}},\"version\":1}", + "bytecode": "0x6080604052348015600f57600080fd5b50601733601b565b6081565b6001600160a01b038116603a6000805160206109f98339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a36000805160206109f983398151915255565b610969806100906000396000f3fe6080604052600436106100865760003560e01c80635d36b190116100595780635d36b1901461010a578063c7af33521461011f578063cf7a1d7714610144578063d38bfff414610157578063f851a4401461009057610086565b80630c340a24146100905780633659cfe6146100c25780634f1ef286146100e25780635c60da1b146100f5575b61008e610177565b005b34801561009c57600080fd5b506100a5610197565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100ce57600080fd5b5061008e6100dd366004610750565b6101b4565b61008e6100f03660046107bb565b6101ed565b34801561010157600080fd5b506100a561028a565b34801561011657600080fd5b5061008e6102a2565b34801561012b57600080fd5b50610134610346565b60405190151581526020016100b9565b61008e61015236600461080e565b610377565b34801561016357600080fd5b5061008e610172366004610750565b6104e0565b6101956101906000805160206108f48339815191525490565b610584565b565b60006101af6000805160206109148339815191525490565b905090565b6101bc610346565b6101e15760405162461bcd60e51b81526004016101d89061086f565b60405180910390fd5b6101ea816105a8565b50565b6101f5610346565b6102115760405162461bcd60e51b81526004016101d89061086f565b61021a836105a8565b6000836001600160a01b031683836040516102369291906108a6565b600060405180830381855af49150503d8060008114610271576040519150601f19603f3d011682016040523d82523d6000602084013e610276565b606091505b505090508061028457600080fd5b50505050565b60006101af6000805160206108f48339815191525490565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b03161461033d5760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b60648201526084016101d8565b610195336105e8565b600061035e6000805160206109148339815191525490565b6001600160a01b0316336001600160a01b031614905090565b61037f610346565b61039b5760405162461bcd60e51b81526004016101d89061086f565b60006103b36000805160206108f48339815191525490565b6001600160a01b0316146103c657600080fd5b6001600160a01b0384166104155760405162461bcd60e51b8152602060048201526016602482015275125b5c1b195b595b9d185d1a5bdb881b9bdd081cd95d60521b60448201526064016101d8565b61044060017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd6108b6565b6000805160206108f48339815191521461045c5761045c6108dd565b61046584610647565b80156104d7576000846001600160a01b031683836040516104879291906108a6565b600060405180830381855af49150503d80600081146104c2576040519150601f19603f3d011682016040523d82523d6000602084013e6104c7565b606091505b50509050806104d557600080fd5b505b610284836105e8565b6104e8610346565b6105045760405162461bcd60e51b81526004016101d89061086f565b61052c817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b031661054c6000805160206109148339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b3660008037600080366000845af43d6000803e8080156105a3573d6000f35b3d6000fd5b6105b181610647565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b03811661063e5760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f72206973206164647265737328302900000000000060448201526064016101d8565b6101ea816106cd565b803b6106bb5760405162461bcd60e51b815260206004820152603b60248201527f43616e6e6f742073657420612070726f787920696d706c656d656e746174696f60448201527f6e20746f2061206e6f6e2d636f6e74726163742061646472657373000000000060648201526084016101d8565b6000805160206108f483398151915255565b806001600160a01b03166106ed6000805160206109148339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a360008051602061091483398151915255565b80356001600160a01b038116811461074b57600080fd5b919050565b60006020828403121561076257600080fd5b61076b82610734565b9392505050565b60008083601f84011261078457600080fd5b50813567ffffffffffffffff81111561079c57600080fd5b6020830191508360208285010111156107b457600080fd5b9250929050565b6000806000604084860312156107d057600080fd5b6107d984610734565b9250602084013567ffffffffffffffff8111156107f557600080fd5b61080186828701610772565b9497909650939450505050565b6000806000806060858703121561082457600080fd5b61082d85610734565b935061083b60208601610734565b9250604085013567ffffffffffffffff81111561085757600080fd5b61086387828801610772565b95989497509550505050565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b8183823760009101908152919050565b818103818111156108d757634e487b7160e01b600052601160045260246000fd5b92915050565b634e487b7160e01b600052600160045260246000fdfe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa2646970667358221220e2bce42933cc9ebca4a8a920307dac15c1a6599c7178087a390aa2fa73ca2ff964736f6c634300081c00337bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a", + "deployedBytecode": "0x6080604052600436106100865760003560e01c80635d36b190116100595780635d36b1901461010a578063c7af33521461011f578063cf7a1d7714610144578063d38bfff414610157578063f851a4401461009057610086565b80630c340a24146100905780633659cfe6146100c25780634f1ef286146100e25780635c60da1b146100f5575b61008e610177565b005b34801561009c57600080fd5b506100a5610197565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100ce57600080fd5b5061008e6100dd366004610750565b6101b4565b61008e6100f03660046107bb565b6101ed565b34801561010157600080fd5b506100a561028a565b34801561011657600080fd5b5061008e6102a2565b34801561012b57600080fd5b50610134610346565b60405190151581526020016100b9565b61008e61015236600461080e565b610377565b34801561016357600080fd5b5061008e610172366004610750565b6104e0565b6101956101906000805160206108f48339815191525490565b610584565b565b60006101af6000805160206109148339815191525490565b905090565b6101bc610346565b6101e15760405162461bcd60e51b81526004016101d89061086f565b60405180910390fd5b6101ea816105a8565b50565b6101f5610346565b6102115760405162461bcd60e51b81526004016101d89061086f565b61021a836105a8565b6000836001600160a01b031683836040516102369291906108a6565b600060405180830381855af49150503d8060008114610271576040519150601f19603f3d011682016040523d82523d6000602084013e610276565b606091505b505090508061028457600080fd5b50505050565b60006101af6000805160206108f48339815191525490565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b03161461033d5760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b60648201526084016101d8565b610195336105e8565b600061035e6000805160206109148339815191525490565b6001600160a01b0316336001600160a01b031614905090565b61037f610346565b61039b5760405162461bcd60e51b81526004016101d89061086f565b60006103b36000805160206108f48339815191525490565b6001600160a01b0316146103c657600080fd5b6001600160a01b0384166104155760405162461bcd60e51b8152602060048201526016602482015275125b5c1b195b595b9d185d1a5bdb881b9bdd081cd95d60521b60448201526064016101d8565b61044060017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd6108b6565b6000805160206108f48339815191521461045c5761045c6108dd565b61046584610647565b80156104d7576000846001600160a01b031683836040516104879291906108a6565b600060405180830381855af49150503d80600081146104c2576040519150601f19603f3d011682016040523d82523d6000602084013e6104c7565b606091505b50509050806104d557600080fd5b505b610284836105e8565b6104e8610346565b6105045760405162461bcd60e51b81526004016101d89061086f565b61052c817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b031661054c6000805160206109148339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b3660008037600080366000845af43d6000803e8080156105a3573d6000f35b3d6000fd5b6105b181610647565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b03811661063e5760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f72206973206164647265737328302900000000000060448201526064016101d8565b6101ea816106cd565b803b6106bb5760405162461bcd60e51b815260206004820152603b60248201527f43616e6e6f742073657420612070726f787920696d706c656d656e746174696f60448201527f6e20746f2061206e6f6e2d636f6e74726163742061646472657373000000000060648201526084016101d8565b6000805160206108f483398151915255565b806001600160a01b03166106ed6000805160206109148339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a360008051602061091483398151915255565b80356001600160a01b038116811461074b57600080fd5b919050565b60006020828403121561076257600080fd5b61076b82610734565b9392505050565b60008083601f84011261078457600080fd5b50813567ffffffffffffffff81111561079c57600080fd5b6020830191508360208285010111156107b457600080fd5b9250929050565b6000806000604084860312156107d057600080fd5b6107d984610734565b9250602084013567ffffffffffffffff8111156107f557600080fd5b61080186828701610772565b9497909650939450505050565b6000806000806060858703121561082457600080fd5b61082d85610734565b935061083b60208601610734565b9250604085013567ffffffffffffffff81111561085757600080fd5b61086387828801610772565b95989497509550505050565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b8183823760009101908152919050565b818103818111156108d757634e487b7160e01b600052601160045260246000fd5b92915050565b634e487b7160e01b600052600160045260246000fdfe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa2646970667358221220e2bce42933cc9ebca4a8a920307dac15c1a6599c7178087a390aa2fa73ca2ff964736f6c634300081c0033", + "libraries": {}, + "devdoc": { + "events": { + "Upgraded(address)": { + "details": "Emitted when the implementation is upgraded.", + "params": { + "implementation": "Address of the new implementation." + } + } + }, + "kind": "dev", + "methods": { + "admin()": { + "returns": { + "_0": "The address of the proxy admin/it's also the governor." + } + }, + "implementation()": { + "returns": { + "_0": "The address of the implementation." + } + }, + "initialize(address,address,bytes)": { + "details": "Contract initializer with Governor enforcement", + "params": { + "_data": "Data to send as msg.data to the implementation to initialize the proxied contract. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.", + "_initGovernor": "Address of the initial Governor.", + "_logic": "Address of the initial implementation." + } + }, + "transferGovernance(address)": { + "params": { + "_newGovernor": "Address of the new Governor" + } + }, + "upgradeTo(address)": { + "details": "Upgrade the backing implementation of the proxy. Only the admin can call this function.", + "params": { + "_newImplementation": "Address of the new implementation." + } + }, + "upgradeToAndCall(address,bytes)": { + "details": "Upgrade the backing implementation of the proxy and call a function on the new implementation. This is useful to initialize the proxied contract.", + "params": { + "data": "Data to send as msg.data in the low level call. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.", + "newImplementation": "Address of the new implementation." + } + } + }, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "claimGovernance()": { + "notice": "Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor." + }, + "governor()": { + "notice": "Returns the address of the current Governor." + }, + "isGovernor()": { + "notice": "Returns true if the caller is the current Governor." + }, + "transferGovernance(address)": { + "notice": "Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete" + } + }, + "notice": "NativeStakingSSVStrategyProxy delegates calls to NativeStakingSSVStrategy implementation", + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/OETH.json b/contracts/deployments/hoodi/OETH.json new file mode 100644 index 0000000000..5c6f6ed253 --- /dev/null +++ b/contracts/deployments/hoodi/OETH.json @@ -0,0 +1,1158 @@ +{ + "address": "0x63cDe3525fD246f7EF9b867fA07c075fae5ce654", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "AccountRebasingDisabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "AccountRebasingEnabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "GovernorshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "PendingGovernorshipTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "rebasingCredits", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "rebasingCreditsPerToken", + "type": "uint256" + } + ], + "name": "TotalSupplyUpdatedHighres", + "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": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "source", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "YieldDelegated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "source", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "YieldUndelegated", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "address", + "name": "_spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newTotalSupply", + "type": "uint256" + } + ], + "name": "changeSupply", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "claimGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "creditsBalanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "creditsBalanceOfHighres", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + } + ], + "name": "delegateYield", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "governanceRebaseOptIn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "governor", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_vaultAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_initialCreditsPerToken", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isGovernor", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "nonRebasingCreditsPerToken", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "nonRebasingSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rebaseOptIn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "rebaseOptOut", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "rebaseState", + "outputs": [ + { + "internalType": "enum OUSD.RebaseOptions", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rebasingCredits", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rebasingCreditsHighres", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rebasingCreditsPerToken", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rebasingCreditsPerTokenHighres", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "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": "_value", + "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": "_value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newGovernor", + "type": "address" + } + ], + "name": "transferGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_from", + "type": "address" + } + ], + "name": "undelegateYield", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "vaultAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "yieldFrom", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "yieldTo", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "transactionHash": "0x8983acfd8286461ed3ce7fde127735af3766a6afd2cc11550a5ee48255c01847", + "receipt": { + "to": null, + "from": "0xFD9E6005187F448957a0972a7d0C0A6dA2911236", + "contractAddress": "0x63cDe3525fD246f7EF9b867fA07c075fae5ce654", + "transactionIndex": 0, + "gasUsed": "2163655", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0xb43d056b9ba0f6e71eb10a80b7c68c59099fab0ab4e0d1cabcfb6ecb1bbeb569", + "transactionHash": "0x8983acfd8286461ed3ce7fde127735af3766a6afd2cc11550a5ee48255c01847", + "logs": [], + "blockNumber": 828230, + "cumulativeGasUsed": "2163655", + "status": 1, + "byzantium": true + }, + "args": [], + "numDeployments": 1, + "solcInputHash": "5518586a577179807341642341c56a4f", + "metadata": "{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"AccountRebasingDisabled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"AccountRebasingEnabled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"GovernorshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"PendingGovernorshipTransfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"totalSupply\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"rebasingCredits\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"rebasingCreditsPerToken\",\"type\":\"uint256\"}],\"name\":\"TotalSupplyUpdatedHighres\",\"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\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"source\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"}],\"name\":\"YieldDelegated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"source\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"}],\"name\":\"YieldUndelegated\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_newTotalSupply\",\"type\":\"uint256\"}],\"name\":\"changeSupply\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_account\",\"type\":\"address\"}],\"name\":\"creditsBalanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_account\",\"type\":\"address\"}],\"name\":\"creditsBalanceOfHighres\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"}],\"name\":\"delegateYield\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_account\",\"type\":\"address\"}],\"name\":\"governanceRebaseOptIn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"governor\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_vaultAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_initialCreditsPerToken\",\"type\":\"uint256\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isGovernor\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_account\",\"type\":\"address\"}],\"name\":\"nonRebasingCreditsPerToken\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"nonRebasingSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rebaseOptIn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rebaseOptOut\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"rebaseState\",\"outputs\":[{\"internalType\":\"enum OUSD.RebaseOptions\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rebasingCredits\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rebasingCreditsHighres\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rebasingCreditsPerToken\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rebasingCreditsPerTokenHighres\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"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\":\"_value\",\"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\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newGovernor\",\"type\":\"address\"}],\"name\":\"transferGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_from\",\"type\":\"address\"}],\"name\":\"undelegateYield\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"vaultAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"yieldFrom\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"yieldTo\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"author\":\"Origin Protocol Inc\",\"events\":{\"AccountRebasingDisabled(address)\":{\"details\":\"Event triggered when an account opts out of rebasing\",\"params\":{\"account\":\"Address of the account\"}},\"AccountRebasingEnabled(address)\":{\"details\":\"Event triggered when an account opts in for rebasing\",\"params\":{\"account\":\"Address of the account\"}},\"Approval(address,address,uint256)\":{\"details\":\"Emitted when the allowance of a `spender` for an `owner` is set by a call to {approve}. `value` is the new allowance.\",\"params\":{\"owner\":\"Address of the owner approving allowance\",\"spender\":\"Address of the spender allowance is granted to\",\"value\":\"Amount of tokens spender can transfer\"}},\"TotalSupplyUpdatedHighres(uint256,uint256,uint256)\":{\"details\":\"Event triggered when the supply changes\",\"params\":{\"rebasingCredits\":\"Updated token rebasing credits\",\"rebasingCreditsPerToken\":\"Updated token rebasing credits per token\",\"totalSupply\":\"Updated token total supply\"}},\"Transfer(address,address,uint256)\":{\"details\":\"Emitted when `value` tokens are moved from one account `from` to another `to`.\",\"params\":{\"from\":\"Address of the account tokens are moved from\",\"to\":\"Address of the account tokens are moved to\",\"value\":\"Amount of tokens transferred\"}},\"YieldDelegated(address,address)\":{\"details\":\"Yield resulting from {changeSupply} that a `source` account would receive is directed to `target` account.\",\"params\":{\"source\":\"Address of the source forwarding the yield\",\"target\":\"Address of the target receiving the yield\"}},\"YieldUndelegated(address,address)\":{\"details\":\"Yield delegation from `source` account to the `target` account is suspended.\",\"params\":{\"source\":\"Address of the source suspending yield forwarding\",\"target\":\"Address of the target no longer receiving yield from `source` account\"}}},\"kind\":\"dev\",\"methods\":{\"allowance(address,address)\":{\"params\":{\"_owner\":\"The address which owns the funds.\",\"_spender\":\"The address which will spend the funds.\"},\"returns\":{\"_0\":\"The number of tokens still available for the _spender.\"}},\"approve(address,uint256)\":{\"params\":{\"_spender\":\"The address which will spend the funds.\",\"_value\":\"The amount of tokens to be spent.\"},\"returns\":{\"_0\":\"true on success.\"}},\"balanceOf(address)\":{\"params\":{\"_account\":\"Address to query the balance of.\"},\"returns\":{\"_0\":\"A uint256 representing the amount of base units owned by the specified address.\"}},\"changeSupply(uint256)\":{\"params\":{\"_newTotalSupply\":\"New total supply of OUSD.\"}},\"creditsBalanceOf(address)\":{\"details\":\"Backwards compatible with old low res credits per token.\",\"params\":{\"_account\":\"The address to query the balance of.\"},\"returns\":{\"_0\":\"(uint256, uint256) Credit balance and credits per token of the address\"}},\"creditsBalanceOfHighres(address)\":{\"params\":{\"_account\":\"The address to query the balance of.\"},\"returns\":{\"_0\":\"(uint256, uint256, bool) Credit balance, credits per token of the address, and isUpgraded\"}},\"decimals()\":{\"details\":\"Returns the number of decimals used to get its user representation.\"},\"governanceRebaseOptIn(address)\":{\"params\":{\"_account\":\"Address of the account.\"}},\"initialize(address,uint256)\":{\"details\":\"Initializes the contract and sets necessary variables.\",\"params\":{\"_initialCreditsPerToken\":\"The starting rebasing credits per token.\",\"_vaultAddress\":\"Address of the vault contract\"}},\"name()\":{\"details\":\"Returns the name of the token.\"},\"rebasingCredits()\":{\"returns\":{\"_0\":\"Low resolution total number of rebasing credits\"}},\"rebasingCreditsHighres()\":{\"returns\":{\"_0\":\"High resolution total number of rebasing credits\"}},\"rebasingCreditsPerToken()\":{\"returns\":{\"_0\":\"Low resolution rebasingCreditsPerToken\"}},\"rebasingCreditsPerTokenHighres()\":{\"returns\":{\"_0\":\"High resolution rebasingCreditsPerToken\"}},\"symbol()\":{\"details\":\"Returns the symbol of the token, a shorter version of the name.\"},\"transfer(address,uint256)\":{\"params\":{\"_to\":\"the address to transfer to.\",\"_value\":\"the amount to be transferred.\"},\"returns\":{\"_0\":\"true on success.\"}},\"transferFrom(address,address,uint256)\":{\"params\":{\"_from\":\"The address you want to send tokens from.\",\"_to\":\"The address you want to transfer to.\",\"_value\":\"The amount of tokens to be transferred.\"},\"returns\":{\"_0\":\"true on success.\"}},\"transferGovernance(address)\":{\"params\":{\"_newGovernor\":\"Address of the new Governor\"}}},\"title\":\"OETH Token Contract\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"allowance(address,address)\":{\"notice\":\"Function to check the amount of tokens that _owner has allowed to `_spender`.\"},\"approve(address,uint256)\":{\"notice\":\"Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.\"},\"balanceOf(address)\":{\"notice\":\"Gets the balance of the specified address.\"},\"burn(address,uint256)\":{\"notice\":\"Destroys `_amount` tokens from `_account`, reducing the total supply.\"},\"changeSupply(uint256)\":{\"notice\":\"Distribute yield to users. This changes the exchange rate between \\\"credits\\\" and OUSD tokens to change rebasing user's balances.\"},\"claimGovernance()\":{\"notice\":\"Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor.\"},\"creditsBalanceOf(address)\":{\"notice\":\"Gets the credits balance of the specified address.\"},\"creditsBalanceOfHighres(address)\":{\"notice\":\"Gets the credits balance of the specified address.\"},\"governanceRebaseOptIn(address)\":{\"notice\":\"The calling account will start receiving yield after a successful call.\"},\"governor()\":{\"notice\":\"Returns the address of the current Governor.\"},\"isGovernor()\":{\"notice\":\"Returns true if the caller is the current Governor.\"},\"mint(address,uint256)\":{\"notice\":\"Creates `_amount` tokens and assigns them to `_account`, increasing the total supply.\"},\"rebaseOptIn()\":{\"notice\":\"The calling account will start receiving yield after a successful call.\"},\"rebaseOptOut()\":{\"notice\":\"The calling account will no longer receive yield\"},\"transfer(address,uint256)\":{\"notice\":\"Transfer tokens to a specified address.\"},\"transferFrom(address,address,uint256)\":{\"notice\":\"Transfer tokens from one address to another.\"},\"transferGovernance(address)\":{\"notice\":\"Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/token/OETH.sol\":\"OETH\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/token/ERC20/IERC20.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Interface of the ERC20 standard as defined in the EIP.\\n */\\ninterface IERC20 {\\n /**\\n * @dev Returns the amount of tokens in existence.\\n */\\n function totalSupply() external view returns (uint256);\\n\\n /**\\n * @dev Returns the amount of tokens owned by `account`.\\n */\\n function balanceOf(address account) external view returns (uint256);\\n\\n /**\\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transfer(address recipient, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Returns the remaining number of tokens that `spender` will be\\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\\n * zero by default.\\n *\\n * This value changes when {approve} or {transferFrom} are called.\\n */\\n function allowance(address owner, address spender) external view returns (uint256);\\n\\n /**\\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\\n * that someone may use both the old and the new allowance by unfortunate\\n * transaction ordering. One possible solution to mitigate this race\\n * condition is to first reduce the spender's allowance to 0 and set the\\n * desired value afterwards:\\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\\n *\\n * Emits an {Approval} event.\\n */\\n function approve(address spender, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\\n * allowance mechanism. `amount` is then deducted from the caller's\\n * allowance.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transferFrom(\\n address sender,\\n address recipient,\\n uint256 amount\\n ) external returns (bool);\\n\\n /**\\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\\n * another (`to`).\\n *\\n * Note that `value` may be zero.\\n */\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n\\n /**\\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n * a call to {approve}. `value` is the new allowance.\\n */\\n event Approval(address indexed owner, address indexed spender, uint256 value);\\n}\\n\",\"keccak256\":\"0x61437cb513a887a1bbad006e7b1c8b414478427d33de47c5600af3c748f108da\",\"license\":\"MIT\"},\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../IERC20.sol\\\";\\nimport \\\"../../../utils/Address.sol\\\";\\n\\n/**\\n * @title SafeERC20\\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\\n * contract returns false). Tokens that return no value (and instead revert or\\n * throw on failure) are also supported, non-reverting calls are assumed to be\\n * successful.\\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\\n */\\nlibrary SafeERC20 {\\n using Address for address;\\n\\n function safeTransfer(\\n IERC20 token,\\n address to,\\n uint256 value\\n ) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\\n }\\n\\n function safeTransferFrom(\\n IERC20 token,\\n address from,\\n address to,\\n uint256 value\\n ) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\\n }\\n\\n /**\\n * @dev Deprecated. This function has issues similar to the ones found in\\n * {IERC20-approve}, and its usage is discouraged.\\n *\\n * Whenever possible, use {safeIncreaseAllowance} and\\n * {safeDecreaseAllowance} instead.\\n */\\n function safeApprove(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n // safeApprove should only be called when setting an initial allowance,\\n // or when resetting it to zero. To increase and decrease it, use\\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\\n require(\\n (value == 0) || (token.allowance(address(this), spender) == 0),\\n \\\"SafeERC20: approve from non-zero to non-zero allowance\\\"\\n );\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\\n }\\n\\n function safeIncreaseAllowance(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n uint256 newAllowance = token.allowance(address(this), spender) + value;\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\\n }\\n\\n function safeDecreaseAllowance(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n unchecked {\\n uint256 oldAllowance = token.allowance(address(this), spender);\\n require(oldAllowance >= value, \\\"SafeERC20: decreased allowance below zero\\\");\\n uint256 newAllowance = oldAllowance - value;\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\\n }\\n }\\n\\n /**\\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\\n * on the return value: the return value is optional (but if data is returned, it must not be false).\\n * @param token The token targeted by the call.\\n * @param data The call data (encoded using abi.encode or one of its variants).\\n */\\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\\n // the target address contains contract code and also asserts for success in the low-level call.\\n\\n bytes memory returndata = address(token).functionCall(data, \\\"SafeERC20: low-level call failed\\\");\\n if (returndata.length > 0) {\\n // Return data is optional\\n require(abi.decode(returndata, (bool)), \\\"SafeERC20: ERC20 operation did not succeed\\\");\\n }\\n }\\n}\\n\",\"keccak256\":\"0xc3d946432c0ddbb1f846a0d3985be71299df331b91d06732152117f62f0be2b5\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize, which returns 0 for contracts in\\n // construction, since the code is only stored at the end of the\\n // constructor execution.\\n\\n uint256 size;\\n assembly {\\n size := extcodesize(account)\\n }\\n return size > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCall(target, data, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n require(isContract(target), \\\"Address: static call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(isContract(target), \\\"Address: delegate call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x51b758a8815ecc9596c66c37d56b1d33883a444631a3f916b9fe65cb863ef7c4\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/math/SafeCast.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\\n * checks.\\n *\\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\\n * easily result in undesired exploitation or bugs, since developers usually\\n * assume that overflows raise errors. `SafeCast` restores this intuition by\\n * reverting the transaction when such an operation overflows.\\n *\\n * Using this library instead of the unchecked operations eliminates an entire\\n * class of bugs, so it's recommended to use it always.\\n *\\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\\n * all math on `uint256` and `int256` and then downcasting.\\n */\\nlibrary SafeCast {\\n /**\\n * @dev Returns the downcasted uint224 from uint256, reverting on\\n * overflow (when the input is greater than largest uint224).\\n *\\n * Counterpart to Solidity's `uint224` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 224 bits\\n */\\n function toUint224(uint256 value) internal pure returns (uint224) {\\n require(value <= type(uint224).max, \\\"SafeCast: value doesn't fit in 224 bits\\\");\\n return uint224(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint128 from uint256, reverting on\\n * overflow (when the input is greater than largest uint128).\\n *\\n * Counterpart to Solidity's `uint128` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 128 bits\\n */\\n function toUint128(uint256 value) internal pure returns (uint128) {\\n require(value <= type(uint128).max, \\\"SafeCast: value doesn't fit in 128 bits\\\");\\n return uint128(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint96 from uint256, reverting on\\n * overflow (when the input is greater than largest uint96).\\n *\\n * Counterpart to Solidity's `uint96` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 96 bits\\n */\\n function toUint96(uint256 value) internal pure returns (uint96) {\\n require(value <= type(uint96).max, \\\"SafeCast: value doesn't fit in 96 bits\\\");\\n return uint96(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint64 from uint256, reverting on\\n * overflow (when the input is greater than largest uint64).\\n *\\n * Counterpart to Solidity's `uint64` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 64 bits\\n */\\n function toUint64(uint256 value) internal pure returns (uint64) {\\n require(value <= type(uint64).max, \\\"SafeCast: value doesn't fit in 64 bits\\\");\\n return uint64(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint32 from uint256, reverting on\\n * overflow (when the input is greater than largest uint32).\\n *\\n * Counterpart to Solidity's `uint32` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 32 bits\\n */\\n function toUint32(uint256 value) internal pure returns (uint32) {\\n require(value <= type(uint32).max, \\\"SafeCast: value doesn't fit in 32 bits\\\");\\n return uint32(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint16 from uint256, reverting on\\n * overflow (when the input is greater than largest uint16).\\n *\\n * Counterpart to Solidity's `uint16` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 16 bits\\n */\\n function toUint16(uint256 value) internal pure returns (uint16) {\\n require(value <= type(uint16).max, \\\"SafeCast: value doesn't fit in 16 bits\\\");\\n return uint16(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint8 from uint256, reverting on\\n * overflow (when the input is greater than largest uint8).\\n *\\n * Counterpart to Solidity's `uint8` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 8 bits.\\n */\\n function toUint8(uint256 value) internal pure returns (uint8) {\\n require(value <= type(uint8).max, \\\"SafeCast: value doesn't fit in 8 bits\\\");\\n return uint8(value);\\n }\\n\\n /**\\n * @dev Converts a signed int256 into an unsigned uint256.\\n *\\n * Requirements:\\n *\\n * - input must be greater than or equal to 0.\\n */\\n function toUint256(int256 value) internal pure returns (uint256) {\\n require(value >= 0, \\\"SafeCast: value must be positive\\\");\\n return uint256(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int128 from int256, reverting on\\n * overflow (when the input is less than smallest int128 or\\n * greater than largest int128).\\n *\\n * Counterpart to Solidity's `int128` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 128 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt128(int256 value) internal pure returns (int128) {\\n require(value >= type(int128).min && value <= type(int128).max, \\\"SafeCast: value doesn't fit in 128 bits\\\");\\n return int128(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int64 from int256, reverting on\\n * overflow (when the input is less than smallest int64 or\\n * greater than largest int64).\\n *\\n * Counterpart to Solidity's `int64` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 64 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt64(int256 value) internal pure returns (int64) {\\n require(value >= type(int64).min && value <= type(int64).max, \\\"SafeCast: value doesn't fit in 64 bits\\\");\\n return int64(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int32 from int256, reverting on\\n * overflow (when the input is less than smallest int32 or\\n * greater than largest int32).\\n *\\n * Counterpart to Solidity's `int32` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 32 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt32(int256 value) internal pure returns (int32) {\\n require(value >= type(int32).min && value <= type(int32).max, \\\"SafeCast: value doesn't fit in 32 bits\\\");\\n return int32(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int16 from int256, reverting on\\n * overflow (when the input is less than smallest int16 or\\n * greater than largest int16).\\n *\\n * Counterpart to Solidity's `int16` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 16 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt16(int256 value) internal pure returns (int16) {\\n require(value >= type(int16).min && value <= type(int16).max, \\\"SafeCast: value doesn't fit in 16 bits\\\");\\n return int16(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int8 from int256, reverting on\\n * overflow (when the input is less than smallest int8 or\\n * greater than largest int8).\\n *\\n * Counterpart to Solidity's `int8` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 8 bits.\\n *\\n * _Available since v3.1._\\n */\\n function toInt8(int256 value) internal pure returns (int8) {\\n require(value >= type(int8).min && value <= type(int8).max, \\\"SafeCast: value doesn't fit in 8 bits\\\");\\n return int8(value);\\n }\\n\\n /**\\n * @dev Converts an unsigned uint256 into a signed int256.\\n *\\n * Requirements:\\n *\\n * - input must be less than or equal to maxInt256.\\n */\\n function toInt256(uint256 value) internal pure returns (int256) {\\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\\n require(value <= uint256(type(int256).max), \\\"SafeCast: value doesn't fit in an int256\\\");\\n return int256(value);\\n }\\n}\\n\",\"keccak256\":\"0x5c6caab697d302ad7eb59c234a4d2dbc965c1bae87709bd2850060b7695b28c7\",\"license\":\"MIT\"},\"contracts/governance/Governable.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\\n * from owner to governor and renounce methods removed. Does not use\\n * Context.sol like Ownable.sol does for simplification.\\n * @author Origin Protocol Inc\\n */\\nabstract contract Governable {\\n // Storage position of the owner and pendingOwner of the contract\\n // keccak256(\\\"OUSD.governor\\\");\\n bytes32 private constant governorPosition =\\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\\n\\n // keccak256(\\\"OUSD.pending.governor\\\");\\n bytes32 private constant pendingGovernorPosition =\\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\\n\\n // keccak256(\\\"OUSD.reentry.status\\\");\\n bytes32 private constant reentryStatusPosition =\\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\\n\\n // See OpenZeppelin ReentrancyGuard implementation\\n uint256 constant _NOT_ENTERED = 1;\\n uint256 constant _ENTERED = 2;\\n\\n event PendingGovernorshipTransfer(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n event GovernorshipTransferred(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n /**\\n * @notice Returns the address of the current Governor.\\n */\\n function governor() public view returns (address) {\\n return _governor();\\n }\\n\\n /**\\n * @dev Returns the address of the current Governor.\\n */\\n function _governor() internal view returns (address governorOut) {\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n governorOut := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Returns the address of the pending Governor.\\n */\\n function _pendingGovernor()\\n internal\\n view\\n returns (address pendingGovernor)\\n {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n pendingGovernor := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the Governor.\\n */\\n modifier onlyGovernor() {\\n require(isGovernor(), \\\"Caller is not the Governor\\\");\\n _;\\n }\\n\\n /**\\n * @notice Returns true if the caller is the current Governor.\\n */\\n function isGovernor() public view returns (bool) {\\n return msg.sender == _governor();\\n }\\n\\n function _setGovernor(address newGovernor) internal {\\n emit GovernorshipTransferred(_governor(), newGovernor);\\n\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @dev Prevents a contract from calling itself, directly or indirectly.\\n * Calling a `nonReentrant` function from another `nonReentrant`\\n * function is not supported. It is possible to prevent this from happening\\n * by making the `nonReentrant` function external, and make it call a\\n * `private` function that does the actual work.\\n */\\n modifier nonReentrant() {\\n bytes32 position = reentryStatusPosition;\\n uint256 _reentry_status;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n _reentry_status := sload(position)\\n }\\n\\n // On the first call to nonReentrant, _notEntered will be true\\n require(_reentry_status != _ENTERED, \\\"Reentrant call\\\");\\n\\n // Any calls to nonReentrant after this point will fail\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _ENTERED)\\n }\\n\\n _;\\n\\n // By storing the original value once again, a refund is triggered (see\\n // https://eips.ethereum.org/EIPS/eip-2200)\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _NOT_ENTERED)\\n }\\n }\\n\\n function _setPendingGovernor(address newGovernor) internal {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the current Governor. Must be claimed for this to complete\\n * @param _newGovernor Address of the new Governor\\n */\\n function transferGovernance(address _newGovernor) external onlyGovernor {\\n _setPendingGovernor(_newGovernor);\\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\\n }\\n\\n /**\\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the new Governor.\\n */\\n function claimGovernance() external {\\n require(\\n msg.sender == _pendingGovernor(),\\n \\\"Only the pending Governor can complete the claim\\\"\\n );\\n _changeGovernor(msg.sender);\\n }\\n\\n /**\\n * @dev Change Governance of the contract to a new account (`newGovernor`).\\n * @param _newGovernor Address of the new Governor\\n */\\n function _changeGovernor(address _newGovernor) internal {\\n require(_newGovernor != address(0), \\\"New Governor is address(0)\\\");\\n _setGovernor(_newGovernor);\\n }\\n}\\n\",\"keccak256\":\"0xf32f873c8bfbacf2e5f01d0cf37bc7f54fbd5aa656e95c8a599114229946f107\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IBasicToken.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\ninterface IBasicToken {\\n function symbol() external view returns (string memory);\\n\\n function decimals() external view returns (uint8);\\n}\\n\",\"keccak256\":\"0xa562062698aa12572123b36dfd2072f1a39e44fed2031cc19c2c9fd522f96ec2\",\"license\":\"MIT\"},\"contracts/interfaces/IStrategy.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\\n */\\ninterface IStrategy {\\n /**\\n * @dev Deposit the given asset to platform\\n * @param _asset asset address\\n * @param _amount Amount to deposit\\n */\\n function deposit(address _asset, uint256 _amount) external;\\n\\n /**\\n * @dev Deposit the entire balance of all supported assets in the Strategy\\n * to the platform\\n */\\n function depositAll() external;\\n\\n /**\\n * @dev Withdraw given asset from Lending platform\\n */\\n function withdraw(\\n address _recipient,\\n address _asset,\\n uint256 _amount\\n ) external;\\n\\n /**\\n * @dev Liquidate all assets in strategy and return them to Vault.\\n */\\n function withdrawAll() external;\\n\\n /**\\n * @dev Returns the current balance of the given asset.\\n */\\n function checkBalance(address _asset)\\n external\\n view\\n returns (uint256 balance);\\n\\n /**\\n * @dev Returns bool indicating whether strategy supports asset.\\n */\\n function supportsAsset(address _asset) external view returns (bool);\\n\\n /**\\n * @dev Collect reward tokens from the Strategy.\\n */\\n function collectRewardTokens() external;\\n\\n /**\\n * @dev The address array of the reward tokens for the Strategy.\\n */\\n function getRewardTokenAddresses() external view returns (address[] memory);\\n\\n function harvesterAddress() external view returns (address);\\n\\n function transferToken(address token, uint256 amount) external;\\n\\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\\n external;\\n}\\n\",\"keccak256\":\"0x79ca47defb3b5a56bba13f14c440838152fd1c1aa640476154516a16da4da8ba\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IVault.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { VaultStorage } from \\\"../vault/VaultStorage.sol\\\";\\n\\ninterface IVault {\\n // slither-disable-start constable-states\\n\\n event AssetSupported(address _asset);\\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\\n event StrategyApproved(address _addr);\\n event StrategyRemoved(address _addr);\\n event Mint(address _addr, uint256 _value);\\n event Redeem(address _addr, uint256 _value);\\n event CapitalPaused();\\n event CapitalUnpaused();\\n event RebasePaused();\\n event RebaseUnpaused();\\n event VaultBufferUpdated(uint256 _vaultBuffer);\\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\\n event PriceProviderUpdated(address _priceProvider);\\n event AllocateThresholdUpdated(uint256 _threshold);\\n event RebaseThresholdUpdated(uint256 _threshold);\\n event StrategistUpdated(address _address);\\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\\n event TrusteeFeeBpsChanged(uint256 _basis);\\n event TrusteeAddressChanged(address _address);\\n event SwapperChanged(address _address);\\n event SwapAllowedUndervalueChanged(uint256 _basis);\\n event SwapSlippageChanged(address _asset, uint256 _basis);\\n event Swapped(\\n address indexed _fromAsset,\\n address indexed _toAsset,\\n uint256 _fromAssetAmount,\\n uint256 _toAssetAmount\\n );\\n event StrategyAddedToMintWhitelist(address indexed strategy);\\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\\n event DripperChanged(address indexed _dripper);\\n event WithdrawalRequested(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount,\\n uint256 _queued\\n );\\n event WithdrawalClaimed(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount\\n );\\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\\n\\n // Governable.sol\\n function transferGovernance(address _newGovernor) external;\\n\\n function claimGovernance() external;\\n\\n function governor() external view returns (address);\\n\\n function ADMIN_IMPLEMENTATION() external view returns (address);\\n\\n // VaultAdmin.sol\\n function setPriceProvider(address _priceProvider) external;\\n\\n function priceProvider() external view returns (address);\\n\\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\\n\\n function redeemFeeBps() external view returns (uint256);\\n\\n function setVaultBuffer(uint256 _vaultBuffer) external;\\n\\n function vaultBuffer() external view returns (uint256);\\n\\n function setAutoAllocateThreshold(uint256 _threshold) external;\\n\\n function autoAllocateThreshold() external view returns (uint256);\\n\\n function setRebaseThreshold(uint256 _threshold) external;\\n\\n function rebaseThreshold() external view returns (uint256);\\n\\n function setStrategistAddr(address _address) external;\\n\\n function strategistAddr() external view returns (address);\\n\\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\\n\\n function maxSupplyDiff() external view returns (uint256);\\n\\n function setTrusteeAddress(address _address) external;\\n\\n function trusteeAddress() external view returns (address);\\n\\n function setTrusteeFeeBps(uint256 _basis) external;\\n\\n function trusteeFeeBps() external view returns (uint256);\\n\\n function ousdMetaStrategy() external view returns (address);\\n\\n function setSwapper(address _swapperAddr) external;\\n\\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\\n\\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\\n external;\\n\\n function supportAsset(address _asset, uint8 _unitConversion) external;\\n\\n function approveStrategy(address _addr) external;\\n\\n function removeStrategy(address _addr) external;\\n\\n function setAssetDefaultStrategy(address _asset, address _strategy)\\n external;\\n\\n function assetDefaultStrategies(address _asset)\\n external\\n view\\n returns (address);\\n\\n function pauseRebase() external;\\n\\n function unpauseRebase() external;\\n\\n function rebasePaused() external view returns (bool);\\n\\n function pauseCapital() external;\\n\\n function unpauseCapital() external;\\n\\n function capitalPaused() external view returns (bool);\\n\\n function transferToken(address _asset, uint256 _amount) external;\\n\\n function priceUnitMint(address asset) external view returns (uint256);\\n\\n function priceUnitRedeem(address asset) external view returns (uint256);\\n\\n function withdrawAllFromStrategy(address _strategyAddr) external;\\n\\n function withdrawAllFromStrategies() external;\\n\\n function withdrawFromStrategy(\\n address _strategyFromAddress,\\n address[] calldata _assets,\\n uint256[] calldata _amounts\\n ) external;\\n\\n function depositToStrategy(\\n address _strategyToAddress,\\n address[] calldata _assets,\\n uint256[] calldata _amounts\\n ) external;\\n\\n // VaultCore.sol\\n function mint(\\n address _asset,\\n uint256 _amount,\\n uint256 _minimumOusdAmount\\n ) external;\\n\\n function mintForStrategy(uint256 _amount) external;\\n\\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\\n\\n function burnForStrategy(uint256 _amount) external;\\n\\n function allocate() external;\\n\\n function rebase() external;\\n\\n function swapCollateral(\\n address fromAsset,\\n address toAsset,\\n uint256 fromAssetAmount,\\n uint256 minToAssetAmount,\\n bytes calldata data\\n ) external returns (uint256 toAssetAmount);\\n\\n function totalValue() external view returns (uint256 value);\\n\\n function checkBalance(address _asset) external view returns (uint256);\\n\\n function calculateRedeemOutputs(uint256 _amount)\\n external\\n view\\n returns (uint256[] memory);\\n\\n function getAssetCount() external view returns (uint256);\\n\\n function getAssetConfig(address _asset)\\n external\\n view\\n returns (VaultStorage.Asset memory config);\\n\\n function getAllAssets() external view returns (address[] memory);\\n\\n function getStrategyCount() external view returns (uint256);\\n\\n function swapper() external view returns (address);\\n\\n function allowedSwapUndervalue() external view returns (uint256);\\n\\n function getAllStrategies() external view returns (address[] memory);\\n\\n function isSupportedAsset(address _asset) external view returns (bool);\\n\\n function netOusdMintForStrategyThreshold() external view returns (uint256);\\n\\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\\n\\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\\n\\n function netOusdMintedForStrategy() external view returns (int256);\\n\\n function setDripper(address _dripper) external;\\n\\n function dripper() external view returns (address);\\n\\n function weth() external view returns (address);\\n\\n function cacheWETHAssetIndex() external;\\n\\n function wethAssetIndex() external view returns (uint256);\\n\\n function initialize(address, address) external;\\n\\n function setAdminImpl(address) external;\\n\\n function removeAsset(address _asset) external;\\n\\n // These are OETH specific functions\\n function addWithdrawalQueueLiquidity() external;\\n\\n function requestWithdrawal(uint256 _amount)\\n external\\n returns (uint256 requestId, uint256 queued);\\n\\n function claimWithdrawal(uint256 requestId)\\n external\\n returns (uint256 amount);\\n\\n function claimWithdrawals(uint256[] memory requestIds)\\n external\\n returns (uint256[] memory amounts, uint256 totalAmount);\\n\\n function withdrawalQueueMetadata()\\n external\\n view\\n returns (VaultStorage.WithdrawalQueueMetadata memory);\\n\\n function withdrawalRequests(uint256 requestId)\\n external\\n view\\n returns (VaultStorage.WithdrawalRequest memory);\\n\\n // OETHb specific functions\\n function addStrategyToMintWhitelist(address strategyAddr) external;\\n\\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\\n\\n function isMintWhitelistedStrategy(address strategyAddr)\\n external\\n view\\n returns (bool);\\n\\n function withdrawalClaimDelay() external view returns (uint256);\\n\\n function setWithdrawalClaimDelay(uint256 newDelay) external;\\n\\n function lastRebase() external view returns (uint64);\\n\\n function dripDuration() external view returns (uint64);\\n\\n function setDripDuration(uint256 _dripDuration) external;\\n\\n function rebasePerSecondMax() external view returns (uint64);\\n\\n function setRebaseRateMax(uint256 yearlyApr) external;\\n\\n function rebasePerSecondTarget() external view returns (uint64);\\n\\n function previewYield() external view returns (uint256 yield);\\n\\n // slither-disable-end constable-states\\n}\\n\",\"keccak256\":\"0x8d0a60f594d97578b0513b4da3d8fcafaa601950c6c4c016bf60b1344733269c\",\"license\":\"BUSL-1.1\"},\"contracts/token/OETH.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { OUSD } from \\\"./OUSD.sol\\\";\\n\\n/**\\n * @title OETH Token Contract\\n * @author Origin Protocol Inc\\n */\\ncontract OETH is OUSD {\\n function symbol() external pure override returns (string memory) {\\n return \\\"OETH\\\";\\n }\\n\\n function name() external pure override returns (string memory) {\\n return \\\"Origin Ether\\\";\\n }\\n\\n function decimals() external pure override returns (uint8) {\\n return 18;\\n }\\n}\\n\",\"keccak256\":\"0x7c8579044be870836558b1ff181f6419492936fcb2edad2161863b91090a9581\",\"license\":\"BUSL-1.1\"},\"contracts/token/OUSD.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title OUSD Token Contract\\n * @dev ERC20 compatible contract for OUSD\\n * @dev Implements an elastic supply\\n * @author Origin Protocol Inc\\n */\\nimport { IVault } from \\\"../interfaces/IVault.sol\\\";\\nimport { Governable } from \\\"../governance/Governable.sol\\\";\\nimport { SafeCast } from \\\"@openzeppelin/contracts/utils/math/SafeCast.sol\\\";\\n\\ncontract OUSD is Governable {\\n using SafeCast for int256;\\n using SafeCast for uint256;\\n\\n /// @dev Event triggered when the supply changes\\n /// @param totalSupply Updated token total supply\\n /// @param rebasingCredits Updated token rebasing credits\\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\\n event TotalSupplyUpdatedHighres(\\n uint256 totalSupply,\\n uint256 rebasingCredits,\\n uint256 rebasingCreditsPerToken\\n );\\n /// @dev Event triggered when an account opts in for rebasing\\n /// @param account Address of the account\\n event AccountRebasingEnabled(address account);\\n /// @dev Event triggered when an account opts out of rebasing\\n /// @param account Address of the account\\n event AccountRebasingDisabled(address account);\\n /// @dev Emitted when `value` tokens are moved from one account `from` to\\n /// another `to`.\\n /// @param from Address of the account tokens are moved from\\n /// @param to Address of the account tokens are moved to\\n /// @param value Amount of tokens transferred\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n /// a call to {approve}. `value` is the new allowance.\\n /// @param owner Address of the owner approving allowance\\n /// @param spender Address of the spender allowance is granted to\\n /// @param value Amount of tokens spender can transfer\\n event Approval(\\n address indexed owner,\\n address indexed spender,\\n uint256 value\\n );\\n /// @dev Yield resulting from {changeSupply} that a `source` account would\\n /// receive is directed to `target` account.\\n /// @param source Address of the source forwarding the yield\\n /// @param target Address of the target receiving the yield\\n event YieldDelegated(address source, address target);\\n /// @dev Yield delegation from `source` account to the `target` account is\\n /// suspended.\\n /// @param source Address of the source suspending yield forwarding\\n /// @param target Address of the target no longer receiving yield from `source`\\n /// account\\n event YieldUndelegated(address source, address target);\\n\\n enum RebaseOptions {\\n NotSet,\\n StdNonRebasing,\\n StdRebasing,\\n YieldDelegationSource,\\n YieldDelegationTarget\\n }\\n\\n uint256[154] private _gap; // Slots to align with deployed contract\\n uint256 private constant MAX_SUPPLY = type(uint128).max;\\n /// @dev The amount of tokens in existence\\n uint256 public totalSupply;\\n mapping(address => mapping(address => uint256)) private allowances;\\n /// @dev The vault with privileges to execute {mint}, {burn}\\n /// and {changeSupply}\\n address public vaultAddress;\\n mapping(address => uint256) internal creditBalances;\\n // the 2 storage variables below need trailing underscores to not name collide with public functions\\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\\n uint256 private rebasingCreditsPerToken_;\\n /// @dev The amount of tokens that are not rebasing - receiving yield\\n uint256 public nonRebasingSupply;\\n mapping(address => uint256) internal alternativeCreditsPerToken;\\n /// @dev A map of all addresses and their respective RebaseOptions\\n mapping(address => RebaseOptions) public rebaseState;\\n mapping(address => uint256) private __deprecated_isUpgraded;\\n /// @dev A map of addresses that have yields forwarded to. This is an\\n /// inverse mapping of {yieldFrom}\\n /// Key Account forwarding yield\\n /// Value Account receiving yield\\n mapping(address => address) public yieldTo;\\n /// @dev A map of addresses that are receiving the yield. This is an\\n /// inverse mapping of {yieldTo}\\n /// Key Account receiving yield\\n /// Value Account forwarding yield\\n mapping(address => address) public yieldFrom;\\n\\n uint256 private constant RESOLUTION_INCREASE = 1e9;\\n uint256[34] private __gap; // including below gap totals up to 200\\n\\n /// @dev Verifies that the caller is the Governor or Strategist.\\n modifier onlyGovernorOrStrategist() {\\n require(\\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\\n \\\"Caller is not the Strategist or Governor\\\"\\n );\\n _;\\n }\\n\\n /// @dev Initializes the contract and sets necessary variables.\\n /// @param _vaultAddress Address of the vault contract\\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\\n external\\n onlyGovernor\\n {\\n require(_vaultAddress != address(0), \\\"Zero vault address\\\");\\n require(vaultAddress == address(0), \\\"Already initialized\\\");\\n\\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\\n vaultAddress = _vaultAddress;\\n }\\n\\n /// @dev Returns the symbol of the token, a shorter version\\n /// of the name.\\n function symbol() external pure virtual returns (string memory) {\\n return \\\"OUSD\\\";\\n }\\n\\n /// @dev Returns the name of the token.\\n function name() external pure virtual returns (string memory) {\\n return \\\"Origin Dollar\\\";\\n }\\n\\n /// @dev Returns the number of decimals used to get its user representation.\\n function decimals() external pure virtual returns (uint8) {\\n return 18;\\n }\\n\\n /**\\n * @dev Verifies that the caller is the Vault contract\\n */\\n modifier onlyVault() {\\n require(vaultAddress == msg.sender, \\\"Caller is not the Vault\\\");\\n _;\\n }\\n\\n /**\\n * @return High resolution rebasingCreditsPerToken\\n */\\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\\n return rebasingCreditsPerToken_;\\n }\\n\\n /**\\n * @return Low resolution rebasingCreditsPerToken\\n */\\n function rebasingCreditsPerToken() external view returns (uint256) {\\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\\n }\\n\\n /**\\n * @return High resolution total number of rebasing credits\\n */\\n function rebasingCreditsHighres() external view returns (uint256) {\\n return rebasingCredits_;\\n }\\n\\n /**\\n * @return Low resolution total number of rebasing credits\\n */\\n function rebasingCredits() external view returns (uint256) {\\n return rebasingCredits_ / RESOLUTION_INCREASE;\\n }\\n\\n /**\\n * @notice Gets the balance of the specified address.\\n * @param _account Address to query the balance of.\\n * @return A uint256 representing the amount of base units owned by the\\n * specified address.\\n */\\n function balanceOf(address _account) public view returns (uint256) {\\n RebaseOptions state = rebaseState[_account];\\n if (state == RebaseOptions.YieldDelegationSource) {\\n // Saves a slot read when transferring to or from a yield delegating source\\n // since we know creditBalances equals the balance.\\n return creditBalances[_account];\\n }\\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\\n _creditsPerToken(_account);\\n if (state == RebaseOptions.YieldDelegationTarget) {\\n // creditBalances of yieldFrom accounts equals token balances\\n return baseBalance - creditBalances[yieldFrom[_account]];\\n }\\n return baseBalance;\\n }\\n\\n /**\\n * @notice Gets the credits balance of the specified address.\\n * @dev Backwards compatible with old low res credits per token.\\n * @param _account The address to query the balance of.\\n * @return (uint256, uint256) Credit balance and credits per token of the\\n * address\\n */\\n function creditsBalanceOf(address _account)\\n external\\n view\\n returns (uint256, uint256)\\n {\\n uint256 cpt = _creditsPerToken(_account);\\n if (cpt == 1e27) {\\n // For a period before the resolution upgrade, we created all new\\n // contract accounts at high resolution. Since they are not changing\\n // as a result of this upgrade, we will return their true values\\n return (creditBalances[_account], cpt);\\n } else {\\n return (\\n creditBalances[_account] / RESOLUTION_INCREASE,\\n cpt / RESOLUTION_INCREASE\\n );\\n }\\n }\\n\\n /**\\n * @notice Gets the credits balance of the specified address.\\n * @param _account The address to query the balance of.\\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\\n * address, and isUpgraded\\n */\\n function creditsBalanceOfHighres(address _account)\\n external\\n view\\n returns (\\n uint256,\\n uint256,\\n bool\\n )\\n {\\n return (\\n creditBalances[_account],\\n _creditsPerToken(_account),\\n true // all accounts have their resolution \\\"upgraded\\\"\\n );\\n }\\n\\n // Backwards compatible view\\n function nonRebasingCreditsPerToken(address _account)\\n external\\n view\\n returns (uint256)\\n {\\n return alternativeCreditsPerToken[_account];\\n }\\n\\n /**\\n * @notice Transfer tokens to a specified address.\\n * @param _to the address to transfer to.\\n * @param _value the amount to be transferred.\\n * @return true on success.\\n */\\n function transfer(address _to, uint256 _value) external returns (bool) {\\n require(_to != address(0), \\\"Transfer to zero address\\\");\\n\\n _executeTransfer(msg.sender, _to, _value);\\n\\n emit Transfer(msg.sender, _to, _value);\\n return true;\\n }\\n\\n /**\\n * @notice Transfer tokens from one address to another.\\n * @param _from The address you want to send tokens from.\\n * @param _to The address you want to transfer to.\\n * @param _value The amount of tokens to be transferred.\\n * @return true on success.\\n */\\n function transferFrom(\\n address _from,\\n address _to,\\n uint256 _value\\n ) external returns (bool) {\\n require(_to != address(0), \\\"Transfer to zero address\\\");\\n uint256 userAllowance = allowances[_from][msg.sender];\\n require(_value <= userAllowance, \\\"Allowance exceeded\\\");\\n\\n unchecked {\\n allowances[_from][msg.sender] = userAllowance - _value;\\n }\\n\\n _executeTransfer(_from, _to, _value);\\n\\n emit Transfer(_from, _to, _value);\\n return true;\\n }\\n\\n function _executeTransfer(\\n address _from,\\n address _to,\\n uint256 _value\\n ) internal {\\n (\\n int256 fromRebasingCreditsDiff,\\n int256 fromNonRebasingSupplyDiff\\n ) = _adjustAccount(_from, -_value.toInt256());\\n (\\n int256 toRebasingCreditsDiff,\\n int256 toNonRebasingSupplyDiff\\n ) = _adjustAccount(_to, _value.toInt256());\\n\\n _adjustGlobals(\\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\\n );\\n }\\n\\n function _adjustAccount(address _account, int256 _balanceChange)\\n internal\\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\\n {\\n RebaseOptions state = rebaseState[_account];\\n int256 currentBalance = balanceOf(_account).toInt256();\\n if (currentBalance + _balanceChange < 0) {\\n revert(\\\"Transfer amount exceeds balance\\\");\\n }\\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\\n\\n if (state == RebaseOptions.YieldDelegationSource) {\\n address target = yieldTo[_account];\\n uint256 targetOldBalance = balanceOf(target);\\n uint256 targetNewCredits = _balanceToRebasingCredits(\\n targetOldBalance + newBalance\\n );\\n rebasingCreditsDiff =\\n targetNewCredits.toInt256() -\\n creditBalances[target].toInt256();\\n\\n creditBalances[_account] = newBalance;\\n creditBalances[target] = targetNewCredits;\\n } else if (state == RebaseOptions.YieldDelegationTarget) {\\n uint256 newCredits = _balanceToRebasingCredits(\\n newBalance + creditBalances[yieldFrom[_account]]\\n );\\n rebasingCreditsDiff =\\n newCredits.toInt256() -\\n creditBalances[_account].toInt256();\\n creditBalances[_account] = newCredits;\\n } else {\\n _autoMigrate(_account);\\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\\n _account\\n ];\\n if (alternativeCreditsPerTokenMem > 0) {\\n nonRebasingSupplyDiff = _balanceChange;\\n if (alternativeCreditsPerTokenMem != 1e18) {\\n alternativeCreditsPerToken[_account] = 1e18;\\n }\\n creditBalances[_account] = newBalance;\\n } else {\\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\\n rebasingCreditsDiff =\\n newCredits.toInt256() -\\n creditBalances[_account].toInt256();\\n creditBalances[_account] = newCredits;\\n }\\n }\\n }\\n\\n function _adjustGlobals(\\n int256 _rebasingCreditsDiff,\\n int256 _nonRebasingSupplyDiff\\n ) internal {\\n if (_rebasingCreditsDiff != 0) {\\n rebasingCredits_ = (rebasingCredits_.toInt256() +\\n _rebasingCreditsDiff).toUint256();\\n }\\n if (_nonRebasingSupplyDiff != 0) {\\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\\n _nonRebasingSupplyDiff).toUint256();\\n }\\n }\\n\\n /**\\n * @notice Function to check the amount of tokens that _owner has allowed\\n * to `_spender`.\\n * @param _owner The address which owns the funds.\\n * @param _spender The address which will spend the funds.\\n * @return The number of tokens still available for the _spender.\\n */\\n function allowance(address _owner, address _spender)\\n external\\n view\\n returns (uint256)\\n {\\n return allowances[_owner][_spender];\\n }\\n\\n /**\\n * @notice Approve the passed address to spend the specified amount of\\n * tokens on behalf of msg.sender.\\n * @param _spender The address which will spend the funds.\\n * @param _value The amount of tokens to be spent.\\n * @return true on success.\\n */\\n function approve(address _spender, uint256 _value) external returns (bool) {\\n allowances[msg.sender][_spender] = _value;\\n emit Approval(msg.sender, _spender, _value);\\n return true;\\n }\\n\\n /**\\n * @notice Creates `_amount` tokens and assigns them to `_account`,\\n * increasing the total supply.\\n */\\n function mint(address _account, uint256 _amount) external onlyVault {\\n require(_account != address(0), \\\"Mint to the zero address\\\");\\n\\n // Account\\n (\\n int256 toRebasingCreditsDiff,\\n int256 toNonRebasingSupplyDiff\\n ) = _adjustAccount(_account, _amount.toInt256());\\n // Globals\\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\\n totalSupply = totalSupply + _amount;\\n\\n require(totalSupply < MAX_SUPPLY, \\\"Max supply\\\");\\n emit Transfer(address(0), _account, _amount);\\n }\\n\\n /**\\n * @notice Destroys `_amount` tokens from `_account`,\\n * reducing the total supply.\\n */\\n function burn(address _account, uint256 _amount) external onlyVault {\\n require(_account != address(0), \\\"Burn from the zero address\\\");\\n if (_amount == 0) {\\n return;\\n }\\n\\n // Account\\n (\\n int256 toRebasingCreditsDiff,\\n int256 toNonRebasingSupplyDiff\\n ) = _adjustAccount(_account, -_amount.toInt256());\\n // Globals\\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\\n totalSupply = totalSupply - _amount;\\n\\n emit Transfer(_account, address(0), _amount);\\n }\\n\\n /**\\n * @dev Get the credits per token for an account. Returns a fixed amount\\n * if the account is non-rebasing.\\n * @param _account Address of the account.\\n */\\n function _creditsPerToken(address _account)\\n internal\\n view\\n returns (uint256)\\n {\\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\\n _account\\n ];\\n if (alternativeCreditsPerTokenMem != 0) {\\n return alternativeCreditsPerTokenMem;\\n } else {\\n return rebasingCreditsPerToken_;\\n }\\n }\\n\\n /**\\n * @dev Auto migrate contracts to be non rebasing,\\n * unless they have opted into yield.\\n * @param _account Address of the account.\\n */\\n function _autoMigrate(address _account) internal {\\n uint256 codeLen = _account.code.length;\\n bool isEOA = (codeLen == 0) ||\\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\\n // In previous code versions, contracts would not have had their\\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\\n // therefore we check the actual accounting used on the account as well.\\n if (\\n (!isEOA) &&\\n rebaseState[_account] == RebaseOptions.NotSet &&\\n alternativeCreditsPerToken[_account] == 0\\n ) {\\n _rebaseOptOut(_account);\\n }\\n }\\n\\n /**\\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\\n * also balance that corresponds to those credits. The latter is important\\n * when adjusting the contract's global nonRebasingSupply to circumvent any\\n * possible rounding errors.\\n *\\n * @param _balance Balance of the account.\\n */\\n function _balanceToRebasingCredits(uint256 _balance)\\n internal\\n view\\n returns (uint256 rebasingCredits)\\n {\\n // Rounds up, because we need to ensure that accounts always have\\n // at least the balance that they should have.\\n // Note this should always be used on an absolute account value,\\n // not on a possibly negative diff, because then the rounding would be wrong.\\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\\n }\\n\\n /**\\n * @notice The calling account will start receiving yield after a successful call.\\n * @param _account Address of the account.\\n */\\n function governanceRebaseOptIn(address _account) external onlyGovernor {\\n require(_account != address(0), \\\"Zero address not allowed\\\");\\n _rebaseOptIn(_account);\\n }\\n\\n /**\\n * @notice The calling account will start receiving yield after a successful call.\\n */\\n function rebaseOptIn() external {\\n _rebaseOptIn(msg.sender);\\n }\\n\\n function _rebaseOptIn(address _account) internal {\\n uint256 balance = balanceOf(_account);\\n\\n // prettier-ignore\\n require(\\n alternativeCreditsPerToken[_account] > 0 ||\\n // Accounts may explicitly `rebaseOptIn` regardless of\\n // accounting if they have a 0 balance.\\n creditBalances[_account] == 0\\n ,\\n \\\"Account must be non-rebasing\\\"\\n );\\n RebaseOptions state = rebaseState[_account];\\n // prettier-ignore\\n require(\\n state == RebaseOptions.StdNonRebasing ||\\n state == RebaseOptions.NotSet,\\n \\\"Only standard non-rebasing accounts can opt in\\\"\\n );\\n\\n uint256 newCredits = _balanceToRebasingCredits(balance);\\n\\n // Account\\n rebaseState[_account] = RebaseOptions.StdRebasing;\\n alternativeCreditsPerToken[_account] = 0;\\n creditBalances[_account] = newCredits;\\n // Globals\\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\\n\\n emit AccountRebasingEnabled(_account);\\n }\\n\\n /**\\n * @notice The calling account will no longer receive yield\\n */\\n function rebaseOptOut() external {\\n _rebaseOptOut(msg.sender);\\n }\\n\\n function _rebaseOptOut(address _account) internal {\\n require(\\n alternativeCreditsPerToken[_account] == 0,\\n \\\"Account must be rebasing\\\"\\n );\\n RebaseOptions state = rebaseState[_account];\\n require(\\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\\n \\\"Only standard rebasing accounts can opt out\\\"\\n );\\n\\n uint256 oldCredits = creditBalances[_account];\\n uint256 balance = balanceOf(_account);\\n\\n // Account\\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\\n alternativeCreditsPerToken[_account] = 1e18;\\n creditBalances[_account] = balance;\\n // Globals\\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\\n\\n emit AccountRebasingDisabled(_account);\\n }\\n\\n /**\\n * @notice Distribute yield to users. This changes the exchange rate\\n * between \\\"credits\\\" and OUSD tokens to change rebasing user's balances.\\n * @param _newTotalSupply New total supply of OUSD.\\n */\\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\\n require(totalSupply > 0, \\\"Cannot increase 0 supply\\\");\\n\\n if (totalSupply == _newTotalSupply) {\\n emit TotalSupplyUpdatedHighres(\\n totalSupply,\\n rebasingCredits_,\\n rebasingCreditsPerToken_\\n );\\n return;\\n }\\n\\n totalSupply = _newTotalSupply > MAX_SUPPLY\\n ? MAX_SUPPLY\\n : _newTotalSupply;\\n\\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\\n // round up in the favour of the protocol\\n rebasingCreditsPerToken_ =\\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\\n rebasingSupply;\\n\\n require(rebasingCreditsPerToken_ > 0, \\\"Invalid change in supply\\\");\\n\\n emit TotalSupplyUpdatedHighres(\\n totalSupply,\\n rebasingCredits_,\\n rebasingCreditsPerToken_\\n );\\n }\\n\\n /*\\n * @notice Send the yield from one account to another account.\\n * Each account keeps its own balances.\\n */\\n function delegateYield(address _from, address _to)\\n external\\n onlyGovernorOrStrategist\\n {\\n require(_from != address(0), \\\"Zero from address not allowed\\\");\\n require(_to != address(0), \\\"Zero to address not allowed\\\");\\n\\n require(_from != _to, \\\"Cannot delegate to self\\\");\\n require(\\n yieldFrom[_to] == address(0) &&\\n yieldTo[_to] == address(0) &&\\n yieldFrom[_from] == address(0) &&\\n yieldTo[_from] == address(0),\\n \\\"Blocked by existing yield delegation\\\"\\n );\\n RebaseOptions stateFrom = rebaseState[_from];\\n RebaseOptions stateTo = rebaseState[_to];\\n\\n require(\\n stateFrom == RebaseOptions.NotSet ||\\n stateFrom == RebaseOptions.StdNonRebasing ||\\n stateFrom == RebaseOptions.StdRebasing,\\n \\\"Invalid rebaseState from\\\"\\n );\\n\\n require(\\n stateTo == RebaseOptions.NotSet ||\\n stateTo == RebaseOptions.StdNonRebasing ||\\n stateTo == RebaseOptions.StdRebasing,\\n \\\"Invalid rebaseState to\\\"\\n );\\n\\n if (alternativeCreditsPerToken[_from] == 0) {\\n _rebaseOptOut(_from);\\n }\\n if (alternativeCreditsPerToken[_to] > 0) {\\n _rebaseOptIn(_to);\\n }\\n\\n uint256 fromBalance = balanceOf(_from);\\n uint256 toBalance = balanceOf(_to);\\n uint256 oldToCredits = creditBalances[_to];\\n uint256 newToCredits = _balanceToRebasingCredits(\\n fromBalance + toBalance\\n );\\n\\n // Set up the bidirectional links\\n yieldTo[_from] = _to;\\n yieldFrom[_to] = _from;\\n\\n // Local\\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\\n alternativeCreditsPerToken[_from] = 1e18;\\n creditBalances[_from] = fromBalance;\\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\\n creditBalances[_to] = newToCredits;\\n\\n // Global\\n int256 creditsChange = newToCredits.toInt256() -\\n oldToCredits.toInt256();\\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\\n emit YieldDelegated(_from, _to);\\n }\\n\\n /*\\n * @notice Stop sending the yield from one account to another account.\\n */\\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\\n // Require a delegation, which will also ensure a valid delegation\\n require(yieldTo[_from] != address(0), \\\"Zero address not allowed\\\");\\n\\n address to = yieldTo[_from];\\n uint256 fromBalance = balanceOf(_from);\\n uint256 toBalance = balanceOf(to);\\n uint256 oldToCredits = creditBalances[to];\\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\\n\\n // Remove the bidirectional links\\n yieldFrom[to] = address(0);\\n yieldTo[_from] = address(0);\\n\\n // Local\\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\\n creditBalances[_from] = fromBalance;\\n rebaseState[to] = RebaseOptions.StdRebasing;\\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\\n creditBalances[to] = newToCredits;\\n\\n // Global\\n int256 creditsChange = newToCredits.toInt256() -\\n oldToCredits.toInt256();\\n _adjustGlobals(creditsChange, fromBalance.toInt256());\\n emit YieldUndelegated(_from, to);\\n }\\n}\\n\",\"keccak256\":\"0x73439bef6569f5adf6f5ce2cb54a5f0d3109d4819457532236e172a7091980a9\",\"license\":\"BUSL-1.1\"},\"contracts/utils/Helpers.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { IBasicToken } from \\\"../interfaces/IBasicToken.sol\\\";\\n\\nlibrary Helpers {\\n /**\\n * @notice Fetch the `symbol()` from an ERC20 token\\n * @dev Grabs the `symbol()` from a contract\\n * @param _token Address of the ERC20 token\\n * @return string Symbol of the ERC20 token\\n */\\n function getSymbol(address _token) internal view returns (string memory) {\\n string memory symbol = IBasicToken(_token).symbol();\\n return symbol;\\n }\\n\\n /**\\n * @notice Fetch the `decimals()` from an ERC20 token\\n * @dev Grabs the `decimals()` from a contract and fails if\\n * the decimal value does not live within a certain range\\n * @param _token Address of the ERC20 token\\n * @return uint256 Decimals of the ERC20 token\\n */\\n function getDecimals(address _token) internal view returns (uint256) {\\n uint256 decimals = IBasicToken(_token).decimals();\\n require(\\n decimals >= 4 && decimals <= 18,\\n \\\"Token must have sufficient decimal places\\\"\\n );\\n\\n return decimals;\\n }\\n}\\n\",\"keccak256\":\"0x4366f8d90b34c1eef8bbaaf369b1e5cd59f04027bb3c111f208eaee65bbc0346\",\"license\":\"BUSL-1.1\"},\"contracts/utils/Initializable.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base contract any contracts that need to initialize state after deployment.\\n * @author Origin Protocol Inc\\n */\\nabstract contract Initializable {\\n /**\\n * @dev Indicates that the contract has been initialized.\\n */\\n bool private initialized;\\n\\n /**\\n * @dev Indicates that the contract is in the process of being initialized.\\n */\\n bool private initializing;\\n\\n /**\\n * @dev Modifier to protect an initializer function from being invoked twice.\\n */\\n modifier initializer() {\\n require(\\n initializing || !initialized,\\n \\\"Initializable: contract is already initialized\\\"\\n );\\n\\n bool isTopLevelCall = !initializing;\\n if (isTopLevelCall) {\\n initializing = true;\\n initialized = true;\\n }\\n\\n _;\\n\\n if (isTopLevelCall) {\\n initializing = false;\\n }\\n }\\n\\n uint256[50] private ______gap;\\n}\\n\",\"keccak256\":\"0x50d39ebf38a3d3111f2b77a6c75ece1d4ae731552fec4697ab16fcf6c0d4d5e8\",\"license\":\"BUSL-1.1\"},\"contracts/vault/VaultStorage.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title OToken VaultStorage contract\\n * @notice The VaultStorage contract defines the storage for the Vault contracts\\n * @author Origin Protocol Inc\\n */\\n\\nimport { IERC20 } from \\\"@openzeppelin/contracts/token/ERC20/IERC20.sol\\\";\\nimport { SafeERC20 } from \\\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\\\";\\nimport { Address } from \\\"@openzeppelin/contracts/utils/Address.sol\\\";\\n\\nimport { IStrategy } from \\\"../interfaces/IStrategy.sol\\\";\\nimport { Governable } from \\\"../governance/Governable.sol\\\";\\nimport { OUSD } from \\\"../token/OUSD.sol\\\";\\nimport { Initializable } from \\\"../utils/Initializable.sol\\\";\\nimport \\\"../utils/Helpers.sol\\\";\\n\\ncontract VaultStorage is Initializable, Governable {\\n using SafeERC20 for IERC20;\\n\\n event AssetSupported(address _asset);\\n event AssetRemoved(address _asset);\\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\\n event StrategyApproved(address _addr);\\n event StrategyRemoved(address _addr);\\n event Mint(address _addr, uint256 _value);\\n event Redeem(address _addr, uint256 _value);\\n event CapitalPaused();\\n event CapitalUnpaused();\\n event RebasePaused();\\n event RebaseUnpaused();\\n event VaultBufferUpdated(uint256 _vaultBuffer);\\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\\n event PriceProviderUpdated(address _priceProvider);\\n event AllocateThresholdUpdated(uint256 _threshold);\\n event RebaseThresholdUpdated(uint256 _threshold);\\n event StrategistUpdated(address _address);\\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\\n event TrusteeFeeBpsChanged(uint256 _basis);\\n event TrusteeAddressChanged(address _address);\\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\\n event SwapperChanged(address _address);\\n event SwapAllowedUndervalueChanged(uint256 _basis);\\n event SwapSlippageChanged(address _asset, uint256 _basis);\\n event Swapped(\\n address indexed _fromAsset,\\n address indexed _toAsset,\\n uint256 _fromAssetAmount,\\n uint256 _toAssetAmount\\n );\\n event StrategyAddedToMintWhitelist(address indexed strategy);\\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\\n event DripperChanged(address indexed _dripper);\\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\\n event DripDurationChanged(uint256 dripDuration);\\n event WithdrawalRequested(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount,\\n uint256 _queued\\n );\\n event WithdrawalClaimed(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount\\n );\\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\\n\\n // Since we are proxy, all state should be uninitalized.\\n // Since this storage contract does not have logic directly on it\\n // we should not be checking for to see if these variables can be constant.\\n // slither-disable-start uninitialized-state\\n // slither-disable-start constable-states\\n\\n // Assets supported by the Vault, i.e. Stablecoins\\n enum UnitConversion {\\n DECIMALS,\\n GETEXCHANGERATE\\n }\\n // Changed to fit into a single storage slot so the decimals needs to be recached\\n struct Asset {\\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\\n // redeeming or checking balance of assets.\\n bool isSupported;\\n UnitConversion unitConversion;\\n uint8 decimals;\\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\\n // For example 40 == 0.4% slippage\\n uint16 allowedOracleSlippageBps;\\n }\\n\\n /// @dev mapping of supported vault assets to their configuration\\n mapping(address => Asset) internal assets;\\n /// @dev list of all assets supported by the vault.\\n address[] internal allAssets;\\n\\n // Strategies approved for use by the Vault\\n struct Strategy {\\n bool isSupported;\\n uint256 _deprecated; // Deprecated storage slot\\n }\\n /// @dev mapping of strategy contracts to their configuration\\n mapping(address => Strategy) public strategies;\\n /// @dev list of all vault strategies\\n address[] internal allStrategies;\\n\\n /// @notice Address of the Oracle price provider contract\\n address public priceProvider;\\n /// @notice pause rebasing if true\\n bool public rebasePaused;\\n /// @notice pause operations that change the OToken supply.\\n /// eg mint, redeem, allocate, mint/burn for strategy\\n bool public capitalPaused;\\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\\n uint256 public redeemFeeBps;\\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\\n uint256 public vaultBuffer;\\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\\n uint256 public autoAllocateThreshold;\\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\\n uint256 public rebaseThreshold;\\n\\n /// @dev Address of the OToken token. eg OUSD or OETH.\\n OUSD public oUSD;\\n\\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\\n // keccak256(\\\"OUSD.vault.governor.admin.impl\\\");\\n bytes32 public constant adminImplPosition =\\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\\n\\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\\n address private _deprecated_rebaseHooksAddr = address(0);\\n\\n /// @dev Deprecated: Address of Uniswap\\n address private _deprecated_uniswapAddr = address(0);\\n\\n /// @notice Address of the Strategist\\n address public strategistAddr = address(0);\\n\\n /// @notice Mapping of asset address to the Strategy that they should automatically\\n // be allocated to\\n mapping(address => address) public assetDefaultStrategies;\\n\\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\\n uint256 public maxSupplyDiff;\\n\\n /// @notice Trustee contract that can collect a percentage of yield\\n address public trusteeAddress;\\n\\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\\n uint256 public trusteeFeeBps;\\n\\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\\n address[] private _deprecated_swapTokens;\\n\\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\\n\\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\\n\\n address public ousdMetaStrategy;\\n\\n /// @notice How much OTokens are currently minted by the strategy\\n int256 public netOusdMintedForStrategy;\\n\\n /// @notice How much net total OTokens are allowed to be minted by all strategies\\n uint256 public netOusdMintForStrategyThreshold;\\n\\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\\n\\n /// @notice Collateral swap configuration.\\n /// @dev is packed into a single storage slot to save gas.\\n struct SwapConfig {\\n // Contract that swaps the vault's collateral assets\\n address swapper;\\n // Max allowed percentage the total value can drop below the total supply in basis points.\\n // For example 100 == 1%\\n uint16 allowedUndervalueBps;\\n }\\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\\n\\n // List of strategies that can mint oTokens directly\\n // Used in OETHBaseVaultCore\\n mapping(address => bool) public isMintWhitelistedStrategy;\\n\\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\\n address public dripper;\\n\\n /// Withdrawal Queue Storage /////\\n\\n struct WithdrawalQueueMetadata {\\n // cumulative total of all withdrawal requests included the ones that have already been claimed\\n uint128 queued;\\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\\n uint128 claimable;\\n // total of all the requests that have been claimed\\n uint128 claimed;\\n // index of the next withdrawal request starting at 0\\n uint128 nextWithdrawalIndex;\\n }\\n\\n /// @notice Global metadata for the withdrawal queue including:\\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\\n /// claimed - total of all the requests that have been claimed\\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\\n\\n struct WithdrawalRequest {\\n address withdrawer;\\n bool claimed;\\n uint40 timestamp; // timestamp of the withdrawal request\\n // Amount of oTokens to redeem. eg OETH\\n uint128 amount;\\n // cumulative total of all withdrawal requests including this one.\\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\\n uint128 queued;\\n }\\n\\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\\n\\n /// @notice Sets a minimum delay that is required to elapse between\\n /// requesting async withdrawals and claiming the request.\\n /// When set to 0 async withdrawals are disabled.\\n uint256 public withdrawalClaimDelay;\\n\\n /// @notice Time in seconds that the vault last rebased yield.\\n uint64 public lastRebase;\\n\\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\\n uint64 public dripDuration;\\n\\n /// @notice max rebase percentage per second\\n /// Can be used to set maximum yield of the protocol,\\n /// spreading out yield over time\\n uint64 public rebasePerSecondMax;\\n\\n /// @notice target rebase rate limit, based on past rates and funds available.\\n uint64 public rebasePerSecondTarget;\\n\\n uint256 internal constant MAX_REBASE = 0.02 ether;\\n uint256 internal constant MAX_REBASE_PER_SECOND =\\n uint256(0.05 ether) / 1 days;\\n\\n // For future use\\n uint256[43] private __gap;\\n\\n // slither-disable-end constable-states\\n // slither-disable-end uninitialized-state\\n\\n /**\\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\\n * @param newImpl address of the implementation\\n */\\n function setAdminImpl(address newImpl) external onlyGovernor {\\n require(\\n Address.isContract(newImpl),\\n \\\"new implementation is not a contract\\\"\\n );\\n bytes32 position = adminImplPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newImpl)\\n }\\n }\\n}\\n\",\"keccak256\":\"0x58f3046a1b219fd43d234f5374a4ce6ec1d8cb51a837e06eafb5532e05b0f6c1\",\"license\":\"BUSL-1.1\"}},\"version\":1}", + "bytecode": "0x6080604052348015600f57600080fd5b5061262c8061001f6000396000f3fe608060405234801561001057600080fd5b50600436106102065760003560e01c806370a082311161011a578063c2376dff116100ad578063dd62ed3e1161007c578063dd62ed3e14610497578063e5c4fffe146104d0578063e696393a14610500578063f51b0fd414610509578063f9854bfc1461051157600080fd5b8063c2376dff14610461578063c7af335214610469578063cd6dc68714610471578063d38bfff41461048457600080fd5b80639d01fc72116100e95780639d01fc72146104155780639dc29fac14610428578063a9059cbb1461043b578063baa9c9db1461044e57600080fd5b806370a08231146103d25780637a46a9c5146103e55780637d0d66ff146103ed57806395d89b41146103f557600080fd5b806339a7919f1161019d5780635d36b1901161016c5780635d36b190146103475780635f5a85771461034f578063609350cd146103785780636691cb3d146103a15780636b96be39146103a957600080fd5b806339a7919f146102de57806340c10f19146102f1578063430bf08a14610304578063456ee2861461031757600080fd5b80630c340a24116101d95780630c340a241461029357806318160ddd146102b357806323b872dd146102bc578063313ce567146102cf57600080fd5b806306a2da3d1461020b57806306fdde0314610220578063077f22b71461025a578063095ea7b314610270575b600080fd5b61021e61021936600461225e565b610539565b005b60408051808201909152600c81526b27b934b3b4b71022ba3432b960a11b60208201525b604051610251919061227b565b60405180910390f35b6102626107b1565b604051908152602001610251565b61028361027e3660046122c9565b6107ca565b6040519015158152602001610251565b61029b610837565b6040516001600160a01b039091168152602001610251565b610262609a5481565b6102836102ca3660046122f5565b61084f565b60405160128152602001610251565b61021e6102ec366004612336565b610984565b61021e6102ff3660046122c9565b610b5c565b609c5461029b906001600160a01b031681565b61033a61032536600461225e565b60a26020526000908152604090205460ff1681565b6040516102519190612365565b61021e610c8a565b61029b61035d36600461225e565b60a4602052600090815260409020546001600160a01b031681565b61026261038636600461225e565b6001600160a01b0316600090815260a1602052604090205490565b610262610d30565b61029b6103b736600461225e565b60a5602052600090815260409020546001600160a01b031681565b6102626103e036600461225e565b610d44565b609f54610262565b609e54610262565b60408051808201909152600481526309e8aa8960e31b6020820152610244565b61021e61042336600461238d565b610e32565b61021e6104363660046122c9565b6113db565b6102836104493660046122c9565b6114c9565b61021e61045c36600461225e565b611552565b61021e6115d0565b6102836115d9565b61021e61047f3660046122c9565b61160a565b61021e61049236600461225e565b6116ed565b6102626104a536600461238d565b6001600160a01b039182166000908152609b6020908152604080832093909416825291909152205490565b6104e36104de36600461225e565b611791565b604080519384526020840192909252151590820152606001610251565b61026260a05481565b61021e6117c5565b61052461051f36600461225e565b6117ce565b60408051928352602083019190915201610251565b6105416115d9565b806105d35750609c60009054906101000a90046001600160a01b03166001600160a01b031663570d8e1d6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561059a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105be91906123c6565b6001600160a01b0316336001600160a01b0316145b6105f85760405162461bcd60e51b81526004016105ef906123e3565b60405180910390fd5b6001600160a01b03818116600090815260a460205260409020541661065a5760405162461bcd60e51b815260206004820152601860248201527716995c9bc81859191c995cdcc81b9bdd08185b1b1bddd95960421b60448201526064016105ef565b6001600160a01b03808216600090815260a460205260408120549091169061068183610d44565b9050600061068e83610d44565b6001600160a01b0384166000908152609d60205260408120549192506106b383611851565b6001600160a01b03868116600081815260a56020908152604080832080546001600160a01b0319908116909155948c16835260a48252808320805490951690945560a2808252848320805460ff19908116600117909155609d8084528685208c9055948452908252848320805490911660021790559190915290812082905590915061073e83611892565b61074783611892565b6107519190612441565b90506107658161076087611892565b611900565b604080516001600160a01b03808a168252881660208201527fa4fa89dd14422a509261a2d4d459f93abdc84c6e92c5e7ae5448243713967bd3910160405180910390a150505050505050565b6000633b9aca00609e546107c59190612468565b905090565b336000818152609b602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906108259086815260200190565b60405180910390a35060015b92915050565b60006107c56000805160206125d78339815191525490565b60006001600160a01b0383166108a25760405162461bcd60e51b81526020600482015260186024820152775472616e7366657220746f207a65726f206164647265737360401b60448201526064016105ef565b6001600160a01b0384166000908152609b602090815260408083203384529091529020548083111561090b5760405162461bcd60e51b8152602060048201526012602482015271105b1b1bddd85b98d948195e18d95959195960721b60448201526064016105ef565b6001600160a01b0385166000908152609b602090815260408083203384529091529020838203905561093e858585611944565b836001600160a01b0316856001600160a01b03166000805160206125b78339815191528560405161097191815260200190565b60405180910390a3506001949350505050565b609c546001600160a01b031633146109ae5760405162461bcd60e51b81526004016105ef9061248a565b6000609a5411610a005760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420696e637265617365203020737570706c79000000000000000060448201526064016105ef565b80609a5403610a5357609a54609e54609f5460408051938452602084019290925282820152517f41645eb819d3011b13f97696a8109d14bfcddfaca7d063ec0564d62a3e2572359181900360600190a150565b6001600160801b038111610a675780610a70565b6001600160801b035b609a81905560a054600091610a8591906124c1565b905080600182609e54670de0b6b3a7640000610aa191906124d4565b610aab91906124eb565b610ab591906124c1565b610abf9190612468565b609f819055610b105760405162461bcd60e51b815260206004820152601860248201527f496e76616c6964206368616e676520696e20737570706c79000000000000000060448201526064016105ef565b609a54609e54609f5460408051938452602084019290925282820152517f41645eb819d3011b13f97696a8109d14bfcddfaca7d063ec0564d62a3e2572359181900360600190a1505b50565b609c546001600160a01b03163314610b865760405162461bcd60e51b81526004016105ef9061248a565b6001600160a01b038216610bdc5760405162461bcd60e51b815260206004820152601860248201527f4d696e7420746f20746865207a65726f2061646472657373000000000000000060448201526064016105ef565b600080610bf184610bec85611892565b61198d565b91509150610bff8282611900565b82609a54610c0d91906124eb565b609a8190556001600160801b0311610c545760405162461bcd60e51b815260206004820152600a6024820152694d617820737570706c7960b01b60448201526064016105ef565b6040518381526001600160a01b038516906000906000805160206125b7833981519152906020015b60405180910390a350505050565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b031614610d255760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b60648201526084016105ef565b610d2e33611c7b565b565b6000633b9aca00609f546107c59190612468565b6001600160a01b038116600090815260a2602052604081205460ff166003816004811115610d7457610d7461234f565b03610d965750506001600160a01b03166000908152609d602052604090205490565b6000610da184611cda565b6001600160a01b0385166000908152609d6020526040902054610dcc90670de0b6b3a76400006124d4565b610dd69190612468565b90506004826004811115610dec57610dec61234f565b03610e2b576001600160a01b03808516600090815260a560209081526040808320549093168252609d90522054610e2390826124c1565b949350505050565b9392505050565b610e3a6115d9565b80610ecc5750609c60009054906101000a90046001600160a01b03166001600160a01b031663570d8e1d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e93573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eb791906123c6565b6001600160a01b0316336001600160a01b0316145b610ee85760405162461bcd60e51b81526004016105ef906123e3565b6001600160a01b038216610f3e5760405162461bcd60e51b815260206004820152601d60248201527f5a65726f2066726f6d2061646472657373206e6f7420616c6c6f77656400000060448201526064016105ef565b6001600160a01b038116610f945760405162461bcd60e51b815260206004820152601b60248201527f5a65726f20746f2061646472657373206e6f7420616c6c6f776564000000000060448201526064016105ef565b806001600160a01b0316826001600160a01b031603610ff55760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f742064656c656761746520746f2073656c6600000000000000000060448201526064016105ef565b6001600160a01b03818116600090815260a560205260409020541615801561103557506001600160a01b03818116600090815260a4602052604090205416155b801561105957506001600160a01b03828116600090815260a5602052604090205416155b801561107d57506001600160a01b03828116600090815260a4602052604090205416155b6110d55760405162461bcd60e51b8152602060048201526024808201527f426c6f636b6564206279206578697374696e67207969656c642064656c6567616044820152633a34b7b760e11b60648201526084016105ef565b6001600160a01b03808316600090815260a2602052604080822054928416825281205460ff9283169216908260048111156111125761111261234f565b148061112f5750600182600481111561112d5761112d61234f565b145b8061114b575060028260048111156111495761114961234f565b145b6111975760405162461bcd60e51b815260206004820152601860248201527f496e76616c69642072656261736553746174652066726f6d000000000000000060448201526064016105ef565b60008160048111156111ab576111ab61234f565b14806111c8575060018160048111156111c6576111c661234f565b145b806111e4575060028160048111156111e2576111e261234f565b145b6112295760405162461bcd60e51b8152602060048201526016602482015275496e76616c696420726562617365537461746520746f60501b60448201526064016105ef565b6001600160a01b038416600090815260a1602052604081205490036112515761125184611d09565b6001600160a01b038316600090815260a16020526040902054156112785761127883611ee4565b600061128385610d44565b9050600061129085610d44565b6001600160a01b0386166000908152609d60205260408120549192506112be6112b984866124eb565b611851565b6001600160a01b03898116600081815260a46020908152604080832080546001600160a01b0319908116968f1696871790915585845260a5835281842080549091168517905592825260a2808252838320805460ff1990811660031790915560a18352848420670de0b6b3a76400009055609d8084528585208c90559584529082528383208054909116600417905592909252812082905590915061136283611892565b61136b83611892565b6113759190612441565b905061138d8161138487611892565b610760906124fe565b604080516001600160a01b03808c1682528a1660208201527f31e39e4bb9df9d4143551c8a3a4798fc19b0854768570ce84d966f6adffd01a3910160405180910390a1505050505050505050565b609c546001600160a01b031633146114055760405162461bcd60e51b81526004016105ef9061248a565b6001600160a01b03821661145b5760405162461bcd60e51b815260206004820152601a60248201527f4275726e2066726f6d20746865207a65726f206164647265737300000000000060448201526064016105ef565b80156114c55760008061147a8461147185611892565b610bec906124fe565b915091506114888282611900565b82609a5461149691906124c1565b609a556040518381526000906001600160a01b038616906000805160206125b783398151915290602001610c7c565b5050565b60006001600160a01b03831661151c5760405162461bcd60e51b81526020600482015260186024820152775472616e7366657220746f207a65726f206164647265737360401b60448201526064016105ef565b611527338484611944565b6040518281526001600160a01b0384169033906000805160206125b783398151915290602001610825565b61155a6115d9565b6115765760405162461bcd60e51b81526004016105ef9061251a565b6001600160a01b0381166115c75760405162461bcd60e51b815260206004820152601860248201527716995c9bc81859191c995cdcc81b9bdd08185b1b1bddd95960421b60448201526064016105ef565b610b5981611ee4565b610d2e33611d09565b60006115f16000805160206125d78339815191525490565b6001600160a01b0316336001600160a01b031614905090565b6116126115d9565b61162e5760405162461bcd60e51b81526004016105ef9061251a565b6001600160a01b0382166116795760405162461bcd60e51b81526020600482015260126024820152715a65726f207661756c74206164647265737360701b60448201526064016105ef565b609c546001600160a01b0316156116c85760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481a5b9a5d1a585b1a5e9959606a1b60448201526064016105ef565b609f55609c80546001600160a01b0319166001600160a01b0392909216919091179055565b6116f56115d9565b6117115760405162461bcd60e51b81526004016105ef9061251a565b611739817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b03166117596000805160206125d78339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b6001600160a01b0381166000908152609d6020526040812054819081906117b785611cda565b909590945060019350915050565b610d2e33611ee4565b60008060006117dc84611cda565b9050806b033b2e3c9fd0803ce800000003611811576001600160a01b039093166000908152609d602052604090205493915050565b6001600160a01b0384166000908152609d602052604090205461183990633b9aca0090612468565b611847633b9aca0083612468565b9250925050915091565b6000670de0b6b3a76400006001609f548461186c91906124d4565b61187e90670de0b6b3a76400006124eb565b61188891906124c1565b6108319190612468565b60006001600160ff1b038211156118fc5760405162461bcd60e51b815260206004820152602860248201527f53616665436173743a2076616c756520646f65736e27742066697420696e2061604482015267371034b73a191a9b60c11b60648201526084016105ef565b5090565b81156119285761192482611915609e54611892565b61191f9190612551565b6120bf565b609e555b80156114c55761193d8161191560a054611892565b60a0555050565b6000806119548561147185611892565b9150915060008061196886610bec87611892565b909250905061198461197a8386612551565b6107608386612551565b50505050505050565b6001600160a01b038216600090815260a26020526040812054819060ff16816119bd6119b887610d44565b611892565b905060006119cb8683612551565b1215611a195760405162461bcd60e51b815260206004820152601f60248201527f5472616e7366657220616d6f756e7420657863656564732062616c616e63650060448201526064016105ef565b6000611a2861191f8784612551565b90506003836004811115611a3e57611a3e61234f565b03611ae3576001600160a01b03808816600090815260a4602052604081205490911690611a6a82610d44565b90506000611a7b6112b985846124eb565b6001600160a01b0384166000908152609d6020526040902054909150611aa090611892565b611aa982611892565b611ab39190612441565b6001600160a01b03808c166000908152609d60205260408082208890559590911681529390932055509450611c71565b6004836004811115611af757611af761234f565b03611b8f576001600160a01b03808816600090815260a560209081526040808320549093168252609d905290812054611b34906112b990846124eb565b6001600160a01b0389166000908152609d6020526040902054909150611b5990611892565b611b6282611892565b611b6c9190612441565b6001600160a01b0389166000908152609d60205260409020919091559450611c71565b611b9887612111565b6001600160a01b038716600090815260a160205260409020548015611c0d5786945080670de0b6b3a764000014611bed576001600160a01b038816600090815260a160205260409020670de0b6b3a764000090555b6001600160a01b0388166000908152609d60205260409020829055611c6f565b6000611c1883611851565b6001600160a01b038a166000908152609d6020526040902054909150611c3d90611892565b611c4682611892565b611c509190612441565b6001600160a01b038a166000908152609d602052604090209190915595505b505b5050509250929050565b6001600160a01b038116611cd15760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f72206973206164647265737328302900000000000060448201526064016105ef565b610b59816121e2565b6001600160a01b038116600090815260a160205260408120548015611cff5792915050565b5050609f54919050565b6001600160a01b038116600090815260a1602052604090205415611d6f5760405162461bcd60e51b815260206004820152601860248201527f4163636f756e74206d757374206265207265626173696e67000000000000000060448201526064016105ef565b6001600160a01b038116600090815260a2602052604090205460ff166002816004811115611d9f57611d9f61234f565b1480611dbc57506000816004811115611dba57611dba61234f565b145b611e1c5760405162461bcd60e51b815260206004820152602b60248201527f4f6e6c79207374616e64617264207265626173696e67206163636f756e74732060448201526a18d85b881bdc1d081bdd5d60aa1b60648201526084016105ef565b6001600160a01b0382166000908152609d602052604081205490611e3f84610d44565b6001600160a01b038516600090815260a260209081526040808320805460ff1916600117905560a18252808320670de0b6b3a76400009055609d90915290208190559050611ea1611e8f83611892565b611e98906124fe565b61076083611892565b6040516001600160a01b03851681527f201ace89ad3f5ab7428b91989f6a50d1998791c7b94a0fa812fd64a57687165e906020015b60405180910390a150505050565b6000611eef82610d44565b6001600160a01b038316600090815260a16020526040902054909150151580611f2e57506001600160a01b0382166000908152609d6020526040902054155b611f7a5760405162461bcd60e51b815260206004820152601c60248201527f4163636f756e74206d757374206265206e6f6e2d7265626173696e670000000060448201526064016105ef565b6001600160a01b038216600090815260a2602052604090205460ff166001816004811115611faa57611faa61234f565b1480611fc757506000816004811115611fc557611fc561234f565b145b61202a5760405162461bcd60e51b815260206004820152602e60248201527f4f6e6c79207374616e64617264206e6f6e2d7265626173696e67206163636f7560448201526d373a399031b0b71037b83a1034b760911b60648201526084016105ef565b600061203583611851565b6001600160a01b038516600090815260a260209081526040808320805460ff1916600217905560a18252808320839055609d9091529020819055905061208661207d82611892565b61138485611892565b6040516001600160a01b03851681527f19a249fa2050bac8314ac10e3ad420bd9825574bf750f58810c3c7adfc7b1c6f90602001611ed6565b6000808212156118fc5760405162461bcd60e51b815260206004820181905260248201527f53616665436173743a2076616c7565206d75737420626520706f73697469766560448201526064016105ef565b6001600160a01b0381163b600081158061217357508160171480156121735750826001600160a01b0316803b806020016040519081016040528181526000908060200190933c61216090612579565b6001600160e81b03191662ef010060e81b145b9050801580156121ad57506001600160a01b038316600090815260a2602052604081205460ff1660048111156121ab576121ab61234f565b145b80156121cf57506001600160a01b038316600090815260a16020526040902054155b156121dd576121dd83611d09565b505050565b806001600160a01b03166122026000805160206125d78339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a36000805160206125d783398151915255565b6001600160a01b0381168114610b5957600080fd5b60006020828403121561227057600080fd5b8135610e2b81612249565b602081526000825180602084015260005b818110156122a9576020818601810151604086840101520161228c565b506000604082850101526040601f19601f83011684010191505092915050565b600080604083850312156122dc57600080fd5b82356122e781612249565b946020939093013593505050565b60008060006060848603121561230a57600080fd5b833561231581612249565b9250602084013561232581612249565b929592945050506040919091013590565b60006020828403121561234857600080fd5b5035919050565b634e487b7160e01b600052602160045260246000fd5b602081016005831061238757634e487b7160e01b600052602160045260246000fd5b91905290565b600080604083850312156123a057600080fd5b82356123ab81612249565b915060208301356123bb81612249565b809150509250929050565b6000602082840312156123d857600080fd5b8151610e2b81612249565b60208082526028908201527f43616c6c6572206973206e6f74207468652053747261746567697374206f722060408201526723b7bb32b93737b960c11b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b81810360008312801583831316838312821617156124615761246161242b565b5092915050565b60008261248557634e487b7160e01b600052601260045260246000fd5b500490565b60208082526017908201527f43616c6c6572206973206e6f7420746865205661756c74000000000000000000604082015260600190565b818103818111156108315761083161242b565b80820281158282048414176108315761083161242b565b808201808211156108315761083161242b565b6000600160ff1b82016125135761251361242b565b5060000390565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b80820182811260008312801582168215821617156125715761257161242b565b505092915050565b805160208201516001600160e81b03198116919060038210156125af576001600160e81b03196003838103901b81901b82161692505b505091905056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa26469706673582212201a9a1c4f34825decd1178234cd306b080849421714a690ef9c446e09b98d480364736f6c634300081c0033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106102065760003560e01c806370a082311161011a578063c2376dff116100ad578063dd62ed3e1161007c578063dd62ed3e14610497578063e5c4fffe146104d0578063e696393a14610500578063f51b0fd414610509578063f9854bfc1461051157600080fd5b8063c2376dff14610461578063c7af335214610469578063cd6dc68714610471578063d38bfff41461048457600080fd5b80639d01fc72116100e95780639d01fc72146104155780639dc29fac14610428578063a9059cbb1461043b578063baa9c9db1461044e57600080fd5b806370a08231146103d25780637a46a9c5146103e55780637d0d66ff146103ed57806395d89b41146103f557600080fd5b806339a7919f1161019d5780635d36b1901161016c5780635d36b190146103475780635f5a85771461034f578063609350cd146103785780636691cb3d146103a15780636b96be39146103a957600080fd5b806339a7919f146102de57806340c10f19146102f1578063430bf08a14610304578063456ee2861461031757600080fd5b80630c340a24116101d95780630c340a241461029357806318160ddd146102b357806323b872dd146102bc578063313ce567146102cf57600080fd5b806306a2da3d1461020b57806306fdde0314610220578063077f22b71461025a578063095ea7b314610270575b600080fd5b61021e61021936600461225e565b610539565b005b60408051808201909152600c81526b27b934b3b4b71022ba3432b960a11b60208201525b604051610251919061227b565b60405180910390f35b6102626107b1565b604051908152602001610251565b61028361027e3660046122c9565b6107ca565b6040519015158152602001610251565b61029b610837565b6040516001600160a01b039091168152602001610251565b610262609a5481565b6102836102ca3660046122f5565b61084f565b60405160128152602001610251565b61021e6102ec366004612336565b610984565b61021e6102ff3660046122c9565b610b5c565b609c5461029b906001600160a01b031681565b61033a61032536600461225e565b60a26020526000908152604090205460ff1681565b6040516102519190612365565b61021e610c8a565b61029b61035d36600461225e565b60a4602052600090815260409020546001600160a01b031681565b61026261038636600461225e565b6001600160a01b0316600090815260a1602052604090205490565b610262610d30565b61029b6103b736600461225e565b60a5602052600090815260409020546001600160a01b031681565b6102626103e036600461225e565b610d44565b609f54610262565b609e54610262565b60408051808201909152600481526309e8aa8960e31b6020820152610244565b61021e61042336600461238d565b610e32565b61021e6104363660046122c9565b6113db565b6102836104493660046122c9565b6114c9565b61021e61045c36600461225e565b611552565b61021e6115d0565b6102836115d9565b61021e61047f3660046122c9565b61160a565b61021e61049236600461225e565b6116ed565b6102626104a536600461238d565b6001600160a01b039182166000908152609b6020908152604080832093909416825291909152205490565b6104e36104de36600461225e565b611791565b604080519384526020840192909252151590820152606001610251565b61026260a05481565b61021e6117c5565b61052461051f36600461225e565b6117ce565b60408051928352602083019190915201610251565b6105416115d9565b806105d35750609c60009054906101000a90046001600160a01b03166001600160a01b031663570d8e1d6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561059a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105be91906123c6565b6001600160a01b0316336001600160a01b0316145b6105f85760405162461bcd60e51b81526004016105ef906123e3565b60405180910390fd5b6001600160a01b03818116600090815260a460205260409020541661065a5760405162461bcd60e51b815260206004820152601860248201527716995c9bc81859191c995cdcc81b9bdd08185b1b1bddd95960421b60448201526064016105ef565b6001600160a01b03808216600090815260a460205260408120549091169061068183610d44565b9050600061068e83610d44565b6001600160a01b0384166000908152609d60205260408120549192506106b383611851565b6001600160a01b03868116600081815260a56020908152604080832080546001600160a01b0319908116909155948c16835260a48252808320805490951690945560a2808252848320805460ff19908116600117909155609d8084528685208c9055948452908252848320805490911660021790559190915290812082905590915061073e83611892565b61074783611892565b6107519190612441565b90506107658161076087611892565b611900565b604080516001600160a01b03808a168252881660208201527fa4fa89dd14422a509261a2d4d459f93abdc84c6e92c5e7ae5448243713967bd3910160405180910390a150505050505050565b6000633b9aca00609e546107c59190612468565b905090565b336000818152609b602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906108259086815260200190565b60405180910390a35060015b92915050565b60006107c56000805160206125d78339815191525490565b60006001600160a01b0383166108a25760405162461bcd60e51b81526020600482015260186024820152775472616e7366657220746f207a65726f206164647265737360401b60448201526064016105ef565b6001600160a01b0384166000908152609b602090815260408083203384529091529020548083111561090b5760405162461bcd60e51b8152602060048201526012602482015271105b1b1bddd85b98d948195e18d95959195960721b60448201526064016105ef565b6001600160a01b0385166000908152609b602090815260408083203384529091529020838203905561093e858585611944565b836001600160a01b0316856001600160a01b03166000805160206125b78339815191528560405161097191815260200190565b60405180910390a3506001949350505050565b609c546001600160a01b031633146109ae5760405162461bcd60e51b81526004016105ef9061248a565b6000609a5411610a005760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420696e637265617365203020737570706c79000000000000000060448201526064016105ef565b80609a5403610a5357609a54609e54609f5460408051938452602084019290925282820152517f41645eb819d3011b13f97696a8109d14bfcddfaca7d063ec0564d62a3e2572359181900360600190a150565b6001600160801b038111610a675780610a70565b6001600160801b035b609a81905560a054600091610a8591906124c1565b905080600182609e54670de0b6b3a7640000610aa191906124d4565b610aab91906124eb565b610ab591906124c1565b610abf9190612468565b609f819055610b105760405162461bcd60e51b815260206004820152601860248201527f496e76616c6964206368616e676520696e20737570706c79000000000000000060448201526064016105ef565b609a54609e54609f5460408051938452602084019290925282820152517f41645eb819d3011b13f97696a8109d14bfcddfaca7d063ec0564d62a3e2572359181900360600190a1505b50565b609c546001600160a01b03163314610b865760405162461bcd60e51b81526004016105ef9061248a565b6001600160a01b038216610bdc5760405162461bcd60e51b815260206004820152601860248201527f4d696e7420746f20746865207a65726f2061646472657373000000000000000060448201526064016105ef565b600080610bf184610bec85611892565b61198d565b91509150610bff8282611900565b82609a54610c0d91906124eb565b609a8190556001600160801b0311610c545760405162461bcd60e51b815260206004820152600a6024820152694d617820737570706c7960b01b60448201526064016105ef565b6040518381526001600160a01b038516906000906000805160206125b7833981519152906020015b60405180910390a350505050565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b031614610d255760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b60648201526084016105ef565b610d2e33611c7b565b565b6000633b9aca00609f546107c59190612468565b6001600160a01b038116600090815260a2602052604081205460ff166003816004811115610d7457610d7461234f565b03610d965750506001600160a01b03166000908152609d602052604090205490565b6000610da184611cda565b6001600160a01b0385166000908152609d6020526040902054610dcc90670de0b6b3a76400006124d4565b610dd69190612468565b90506004826004811115610dec57610dec61234f565b03610e2b576001600160a01b03808516600090815260a560209081526040808320549093168252609d90522054610e2390826124c1565b949350505050565b9392505050565b610e3a6115d9565b80610ecc5750609c60009054906101000a90046001600160a01b03166001600160a01b031663570d8e1d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e93573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eb791906123c6565b6001600160a01b0316336001600160a01b0316145b610ee85760405162461bcd60e51b81526004016105ef906123e3565b6001600160a01b038216610f3e5760405162461bcd60e51b815260206004820152601d60248201527f5a65726f2066726f6d2061646472657373206e6f7420616c6c6f77656400000060448201526064016105ef565b6001600160a01b038116610f945760405162461bcd60e51b815260206004820152601b60248201527f5a65726f20746f2061646472657373206e6f7420616c6c6f776564000000000060448201526064016105ef565b806001600160a01b0316826001600160a01b031603610ff55760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f742064656c656761746520746f2073656c6600000000000000000060448201526064016105ef565b6001600160a01b03818116600090815260a560205260409020541615801561103557506001600160a01b03818116600090815260a4602052604090205416155b801561105957506001600160a01b03828116600090815260a5602052604090205416155b801561107d57506001600160a01b03828116600090815260a4602052604090205416155b6110d55760405162461bcd60e51b8152602060048201526024808201527f426c6f636b6564206279206578697374696e67207969656c642064656c6567616044820152633a34b7b760e11b60648201526084016105ef565b6001600160a01b03808316600090815260a2602052604080822054928416825281205460ff9283169216908260048111156111125761111261234f565b148061112f5750600182600481111561112d5761112d61234f565b145b8061114b575060028260048111156111495761114961234f565b145b6111975760405162461bcd60e51b815260206004820152601860248201527f496e76616c69642072656261736553746174652066726f6d000000000000000060448201526064016105ef565b60008160048111156111ab576111ab61234f565b14806111c8575060018160048111156111c6576111c661234f565b145b806111e4575060028160048111156111e2576111e261234f565b145b6112295760405162461bcd60e51b8152602060048201526016602482015275496e76616c696420726562617365537461746520746f60501b60448201526064016105ef565b6001600160a01b038416600090815260a1602052604081205490036112515761125184611d09565b6001600160a01b038316600090815260a16020526040902054156112785761127883611ee4565b600061128385610d44565b9050600061129085610d44565b6001600160a01b0386166000908152609d60205260408120549192506112be6112b984866124eb565b611851565b6001600160a01b03898116600081815260a46020908152604080832080546001600160a01b0319908116968f1696871790915585845260a5835281842080549091168517905592825260a2808252838320805460ff1990811660031790915560a18352848420670de0b6b3a76400009055609d8084528585208c90559584529082528383208054909116600417905592909252812082905590915061136283611892565b61136b83611892565b6113759190612441565b905061138d8161138487611892565b610760906124fe565b604080516001600160a01b03808c1682528a1660208201527f31e39e4bb9df9d4143551c8a3a4798fc19b0854768570ce84d966f6adffd01a3910160405180910390a1505050505050505050565b609c546001600160a01b031633146114055760405162461bcd60e51b81526004016105ef9061248a565b6001600160a01b03821661145b5760405162461bcd60e51b815260206004820152601a60248201527f4275726e2066726f6d20746865207a65726f206164647265737300000000000060448201526064016105ef565b80156114c55760008061147a8461147185611892565b610bec906124fe565b915091506114888282611900565b82609a5461149691906124c1565b609a556040518381526000906001600160a01b038616906000805160206125b783398151915290602001610c7c565b5050565b60006001600160a01b03831661151c5760405162461bcd60e51b81526020600482015260186024820152775472616e7366657220746f207a65726f206164647265737360401b60448201526064016105ef565b611527338484611944565b6040518281526001600160a01b0384169033906000805160206125b783398151915290602001610825565b61155a6115d9565b6115765760405162461bcd60e51b81526004016105ef9061251a565b6001600160a01b0381166115c75760405162461bcd60e51b815260206004820152601860248201527716995c9bc81859191c995cdcc81b9bdd08185b1b1bddd95960421b60448201526064016105ef565b610b5981611ee4565b610d2e33611d09565b60006115f16000805160206125d78339815191525490565b6001600160a01b0316336001600160a01b031614905090565b6116126115d9565b61162e5760405162461bcd60e51b81526004016105ef9061251a565b6001600160a01b0382166116795760405162461bcd60e51b81526020600482015260126024820152715a65726f207661756c74206164647265737360701b60448201526064016105ef565b609c546001600160a01b0316156116c85760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481a5b9a5d1a585b1a5e9959606a1b60448201526064016105ef565b609f55609c80546001600160a01b0319166001600160a01b0392909216919091179055565b6116f56115d9565b6117115760405162461bcd60e51b81526004016105ef9061251a565b611739817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b03166117596000805160206125d78339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b6001600160a01b0381166000908152609d6020526040812054819081906117b785611cda565b909590945060019350915050565b610d2e33611ee4565b60008060006117dc84611cda565b9050806b033b2e3c9fd0803ce800000003611811576001600160a01b039093166000908152609d602052604090205493915050565b6001600160a01b0384166000908152609d602052604090205461183990633b9aca0090612468565b611847633b9aca0083612468565b9250925050915091565b6000670de0b6b3a76400006001609f548461186c91906124d4565b61187e90670de0b6b3a76400006124eb565b61188891906124c1565b6108319190612468565b60006001600160ff1b038211156118fc5760405162461bcd60e51b815260206004820152602860248201527f53616665436173743a2076616c756520646f65736e27742066697420696e2061604482015267371034b73a191a9b60c11b60648201526084016105ef565b5090565b81156119285761192482611915609e54611892565b61191f9190612551565b6120bf565b609e555b80156114c55761193d8161191560a054611892565b60a0555050565b6000806119548561147185611892565b9150915060008061196886610bec87611892565b909250905061198461197a8386612551565b6107608386612551565b50505050505050565b6001600160a01b038216600090815260a26020526040812054819060ff16816119bd6119b887610d44565b611892565b905060006119cb8683612551565b1215611a195760405162461bcd60e51b815260206004820152601f60248201527f5472616e7366657220616d6f756e7420657863656564732062616c616e63650060448201526064016105ef565b6000611a2861191f8784612551565b90506003836004811115611a3e57611a3e61234f565b03611ae3576001600160a01b03808816600090815260a4602052604081205490911690611a6a82610d44565b90506000611a7b6112b985846124eb565b6001600160a01b0384166000908152609d6020526040902054909150611aa090611892565b611aa982611892565b611ab39190612441565b6001600160a01b03808c166000908152609d60205260408082208890559590911681529390932055509450611c71565b6004836004811115611af757611af761234f565b03611b8f576001600160a01b03808816600090815260a560209081526040808320549093168252609d905290812054611b34906112b990846124eb565b6001600160a01b0389166000908152609d6020526040902054909150611b5990611892565b611b6282611892565b611b6c9190612441565b6001600160a01b0389166000908152609d60205260409020919091559450611c71565b611b9887612111565b6001600160a01b038716600090815260a160205260409020548015611c0d5786945080670de0b6b3a764000014611bed576001600160a01b038816600090815260a160205260409020670de0b6b3a764000090555b6001600160a01b0388166000908152609d60205260409020829055611c6f565b6000611c1883611851565b6001600160a01b038a166000908152609d6020526040902054909150611c3d90611892565b611c4682611892565b611c509190612441565b6001600160a01b038a166000908152609d602052604090209190915595505b505b5050509250929050565b6001600160a01b038116611cd15760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f72206973206164647265737328302900000000000060448201526064016105ef565b610b59816121e2565b6001600160a01b038116600090815260a160205260408120548015611cff5792915050565b5050609f54919050565b6001600160a01b038116600090815260a1602052604090205415611d6f5760405162461bcd60e51b815260206004820152601860248201527f4163636f756e74206d757374206265207265626173696e67000000000000000060448201526064016105ef565b6001600160a01b038116600090815260a2602052604090205460ff166002816004811115611d9f57611d9f61234f565b1480611dbc57506000816004811115611dba57611dba61234f565b145b611e1c5760405162461bcd60e51b815260206004820152602b60248201527f4f6e6c79207374616e64617264207265626173696e67206163636f756e74732060448201526a18d85b881bdc1d081bdd5d60aa1b60648201526084016105ef565b6001600160a01b0382166000908152609d602052604081205490611e3f84610d44565b6001600160a01b038516600090815260a260209081526040808320805460ff1916600117905560a18252808320670de0b6b3a76400009055609d90915290208190559050611ea1611e8f83611892565b611e98906124fe565b61076083611892565b6040516001600160a01b03851681527f201ace89ad3f5ab7428b91989f6a50d1998791c7b94a0fa812fd64a57687165e906020015b60405180910390a150505050565b6000611eef82610d44565b6001600160a01b038316600090815260a16020526040902054909150151580611f2e57506001600160a01b0382166000908152609d6020526040902054155b611f7a5760405162461bcd60e51b815260206004820152601c60248201527f4163636f756e74206d757374206265206e6f6e2d7265626173696e670000000060448201526064016105ef565b6001600160a01b038216600090815260a2602052604090205460ff166001816004811115611faa57611faa61234f565b1480611fc757506000816004811115611fc557611fc561234f565b145b61202a5760405162461bcd60e51b815260206004820152602e60248201527f4f6e6c79207374616e64617264206e6f6e2d7265626173696e67206163636f7560448201526d373a399031b0b71037b83a1034b760911b60648201526084016105ef565b600061203583611851565b6001600160a01b038516600090815260a260209081526040808320805460ff1916600217905560a18252808320839055609d9091529020819055905061208661207d82611892565b61138485611892565b6040516001600160a01b03851681527f19a249fa2050bac8314ac10e3ad420bd9825574bf750f58810c3c7adfc7b1c6f90602001611ed6565b6000808212156118fc5760405162461bcd60e51b815260206004820181905260248201527f53616665436173743a2076616c7565206d75737420626520706f73697469766560448201526064016105ef565b6001600160a01b0381163b600081158061217357508160171480156121735750826001600160a01b0316803b806020016040519081016040528181526000908060200190933c61216090612579565b6001600160e81b03191662ef010060e81b145b9050801580156121ad57506001600160a01b038316600090815260a2602052604081205460ff1660048111156121ab576121ab61234f565b145b80156121cf57506001600160a01b038316600090815260a16020526040902054155b156121dd576121dd83611d09565b505050565b806001600160a01b03166122026000805160206125d78339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a36000805160206125d783398151915255565b6001600160a01b0381168114610b5957600080fd5b60006020828403121561227057600080fd5b8135610e2b81612249565b602081526000825180602084015260005b818110156122a9576020818601810151604086840101520161228c565b506000604082850101526040601f19601f83011684010191505092915050565b600080604083850312156122dc57600080fd5b82356122e781612249565b946020939093013593505050565b60008060006060848603121561230a57600080fd5b833561231581612249565b9250602084013561232581612249565b929592945050506040919091013590565b60006020828403121561234857600080fd5b5035919050565b634e487b7160e01b600052602160045260246000fd5b602081016005831061238757634e487b7160e01b600052602160045260246000fd5b91905290565b600080604083850312156123a057600080fd5b82356123ab81612249565b915060208301356123bb81612249565b809150509250929050565b6000602082840312156123d857600080fd5b8151610e2b81612249565b60208082526028908201527f43616c6c6572206973206e6f74207468652053747261746567697374206f722060408201526723b7bb32b93737b960c11b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b81810360008312801583831316838312821617156124615761246161242b565b5092915050565b60008261248557634e487b7160e01b600052601260045260246000fd5b500490565b60208082526017908201527f43616c6c6572206973206e6f7420746865205661756c74000000000000000000604082015260600190565b818103818111156108315761083161242b565b80820281158282048414176108315761083161242b565b808201808211156108315761083161242b565b6000600160ff1b82016125135761251361242b565b5060000390565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b80820182811260008312801582168215821617156125715761257161242b565b505092915050565b805160208201516001600160e81b03198116919060038210156125af576001600160e81b03196003838103901b81901b82161692505b505091905056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa26469706673582212201a9a1c4f34825decd1178234cd306b080849421714a690ef9c446e09b98d480364736f6c634300081c0033", + "libraries": {}, + "devdoc": { + "author": "Origin Protocol Inc", + "events": { + "AccountRebasingDisabled(address)": { + "details": "Event triggered when an account opts out of rebasing", + "params": { + "account": "Address of the account" + } + }, + "AccountRebasingEnabled(address)": { + "details": "Event triggered when an account opts in for rebasing", + "params": { + "account": "Address of the account" + } + }, + "Approval(address,address,uint256)": { + "details": "Emitted when the allowance of a `spender` for an `owner` is set by a call to {approve}. `value` is the new allowance.", + "params": { + "owner": "Address of the owner approving allowance", + "spender": "Address of the spender allowance is granted to", + "value": "Amount of tokens spender can transfer" + } + }, + "TotalSupplyUpdatedHighres(uint256,uint256,uint256)": { + "details": "Event triggered when the supply changes", + "params": { + "rebasingCredits": "Updated token rebasing credits", + "rebasingCreditsPerToken": "Updated token rebasing credits per token", + "totalSupply": "Updated token total supply" + } + }, + "Transfer(address,address,uint256)": { + "details": "Emitted when `value` tokens are moved from one account `from` to another `to`.", + "params": { + "from": "Address of the account tokens are moved from", + "to": "Address of the account tokens are moved to", + "value": "Amount of tokens transferred" + } + }, + "YieldDelegated(address,address)": { + "details": "Yield resulting from {changeSupply} that a `source` account would receive is directed to `target` account.", + "params": { + "source": "Address of the source forwarding the yield", + "target": "Address of the target receiving the yield" + } + }, + "YieldUndelegated(address,address)": { + "details": "Yield delegation from `source` account to the `target` account is suspended.", + "params": { + "source": "Address of the source suspending yield forwarding", + "target": "Address of the target no longer receiving yield from `source` account" + } + } + }, + "kind": "dev", + "methods": { + "allowance(address,address)": { + "params": { + "_owner": "The address which owns the funds.", + "_spender": "The address which will spend the funds." + }, + "returns": { + "_0": "The number of tokens still available for the _spender." + } + }, + "approve(address,uint256)": { + "params": { + "_spender": "The address which will spend the funds.", + "_value": "The amount of tokens to be spent." + }, + "returns": { + "_0": "true on success." + } + }, + "balanceOf(address)": { + "params": { + "_account": "Address to query the balance of." + }, + "returns": { + "_0": "A uint256 representing the amount of base units owned by the specified address." + } + }, + "changeSupply(uint256)": { + "params": { + "_newTotalSupply": "New total supply of OUSD." + } + }, + "creditsBalanceOf(address)": { + "details": "Backwards compatible with old low res credits per token.", + "params": { + "_account": "The address to query the balance of." + }, + "returns": { + "_0": "(uint256, uint256) Credit balance and credits per token of the address" + } + }, + "creditsBalanceOfHighres(address)": { + "params": { + "_account": "The address to query the balance of." + }, + "returns": { + "_0": "(uint256, uint256, bool) Credit balance, credits per token of the address, and isUpgraded" + } + }, + "decimals()": { + "details": "Returns the number of decimals used to get its user representation." + }, + "governanceRebaseOptIn(address)": { + "params": { + "_account": "Address of the account." + } + }, + "initialize(address,uint256)": { + "details": "Initializes the contract and sets necessary variables.", + "params": { + "_initialCreditsPerToken": "The starting rebasing credits per token.", + "_vaultAddress": "Address of the vault contract" + } + }, + "name()": { + "details": "Returns the name of the token." + }, + "rebasingCredits()": { + "returns": { + "_0": "Low resolution total number of rebasing credits" + } + }, + "rebasingCreditsHighres()": { + "returns": { + "_0": "High resolution total number of rebasing credits" + } + }, + "rebasingCreditsPerToken()": { + "returns": { + "_0": "Low resolution rebasingCreditsPerToken" + } + }, + "rebasingCreditsPerTokenHighres()": { + "returns": { + "_0": "High resolution rebasingCreditsPerToken" + } + }, + "symbol()": { + "details": "Returns the symbol of the token, a shorter version of the name." + }, + "transfer(address,uint256)": { + "params": { + "_to": "the address to transfer to.", + "_value": "the amount to be transferred." + }, + "returns": { + "_0": "true on success." + } + }, + "transferFrom(address,address,uint256)": { + "params": { + "_from": "The address you want to send tokens from.", + "_to": "The address you want to transfer to.", + "_value": "The amount of tokens to be transferred." + }, + "returns": { + "_0": "true on success." + } + }, + "transferGovernance(address)": { + "params": { + "_newGovernor": "Address of the new Governor" + } + } + }, + "title": "OETH Token Contract", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "allowance(address,address)": { + "notice": "Function to check the amount of tokens that _owner has allowed to `_spender`." + }, + "approve(address,uint256)": { + "notice": "Approve the passed address to spend the specified amount of tokens on behalf of msg.sender." + }, + "balanceOf(address)": { + "notice": "Gets the balance of the specified address." + }, + "burn(address,uint256)": { + "notice": "Destroys `_amount` tokens from `_account`, reducing the total supply." + }, + "changeSupply(uint256)": { + "notice": "Distribute yield to users. This changes the exchange rate between \"credits\" and OUSD tokens to change rebasing user's balances." + }, + "claimGovernance()": { + "notice": "Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor." + }, + "creditsBalanceOf(address)": { + "notice": "Gets the credits balance of the specified address." + }, + "creditsBalanceOfHighres(address)": { + "notice": "Gets the credits balance of the specified address." + }, + "governanceRebaseOptIn(address)": { + "notice": "The calling account will start receiving yield after a successful call." + }, + "governor()": { + "notice": "Returns the address of the current Governor." + }, + "isGovernor()": { + "notice": "Returns true if the caller is the current Governor." + }, + "mint(address,uint256)": { + "notice": "Creates `_amount` tokens and assigns them to `_account`, increasing the total supply." + }, + "rebaseOptIn()": { + "notice": "The calling account will start receiving yield after a successful call." + }, + "rebaseOptOut()": { + "notice": "The calling account will no longer receive yield" + }, + "transfer(address,uint256)": { + "notice": "Transfer tokens to a specified address." + }, + "transferFrom(address,address,uint256)": { + "notice": "Transfer tokens from one address to another." + }, + "transferGovernance(address)": { + "notice": "Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete" + } + }, + "version": 1 + }, + "storageLayout": { + "storage": [ + { + "astId": 50611, + "contract": "contracts/token/OETH.sol:OETH", + "label": "_gap", + "offset": 0, + "slot": "0", + "type": "t_array(t_uint256)154_storage" + }, + { + "astId": 50621, + "contract": "contracts/token/OETH.sol:OETH", + "label": "totalSupply", + "offset": 0, + "slot": "154", + "type": "t_uint256" + }, + { + "astId": 50627, + "contract": "contracts/token/OETH.sol:OETH", + "label": "allowances", + "offset": 0, + "slot": "155", + "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))" + }, + { + "astId": 50630, + "contract": "contracts/token/OETH.sol:OETH", + "label": "vaultAddress", + "offset": 0, + "slot": "156", + "type": "t_address" + }, + { + "astId": 50634, + "contract": "contracts/token/OETH.sol:OETH", + "label": "creditBalances", + "offset": 0, + "slot": "157", + "type": "t_mapping(t_address,t_uint256)" + }, + { + "astId": 50636, + "contract": "contracts/token/OETH.sol:OETH", + "label": "rebasingCredits_", + "offset": 0, + "slot": "158", + "type": "t_uint256" + }, + { + "astId": 50638, + "contract": "contracts/token/OETH.sol:OETH", + "label": "rebasingCreditsPerToken_", + "offset": 0, + "slot": "159", + "type": "t_uint256" + }, + { + "astId": 50641, + "contract": "contracts/token/OETH.sol:OETH", + "label": "nonRebasingSupply", + "offset": 0, + "slot": "160", + "type": "t_uint256" + }, + { + "astId": 50645, + "contract": "contracts/token/OETH.sol:OETH", + "label": "alternativeCreditsPerToken", + "offset": 0, + "slot": "161", + "type": "t_mapping(t_address,t_uint256)" + }, + { + "astId": 50651, + "contract": "contracts/token/OETH.sol:OETH", + "label": "rebaseState", + "offset": 0, + "slot": "162", + "type": "t_mapping(t_address,t_enum(RebaseOptions)50607)" + }, + { + "astId": 50655, + "contract": "contracts/token/OETH.sol:OETH", + "label": "__deprecated_isUpgraded", + "offset": 0, + "slot": "163", + "type": "t_mapping(t_address,t_uint256)" + }, + { + "astId": 50660, + "contract": "contracts/token/OETH.sol:OETH", + "label": "yieldTo", + "offset": 0, + "slot": "164", + "type": "t_mapping(t_address,t_address)" + }, + { + "astId": 50665, + "contract": "contracts/token/OETH.sol:OETH", + "label": "yieldFrom", + "offset": 0, + "slot": "165", + "type": "t_mapping(t_address,t_address)" + }, + { + "astId": 50672, + "contract": "contracts/token/OETH.sol:OETH", + "label": "__gap", + "offset": 0, + "slot": "166", + "type": "t_array(t_uint256)34_storage" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_uint256)154_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[154]", + "numberOfBytes": "4928" + }, + "t_array(t_uint256)34_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[34]", + "numberOfBytes": "1088" + }, + "t_enum(RebaseOptions)50607": { + "encoding": "inplace", + "label": "enum OUSD.RebaseOptions", + "numberOfBytes": "1" + }, + "t_mapping(t_address,t_address)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => address)", + "numberOfBytes": "32", + "value": "t_address" + }, + "t_mapping(t_address,t_enum(RebaseOptions)50607)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => enum OUSD.RebaseOptions)", + "numberOfBytes": "32", + "value": "t_enum(RebaseOptions)50607" + }, + "t_mapping(t_address,t_mapping(t_address,t_uint256))": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => mapping(address => uint256))", + "numberOfBytes": "32", + "value": "t_mapping(t_address,t_uint256)" + }, + "t_mapping(t_address,t_uint256)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": "t_uint256" + }, + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/OETHOracleRouter.json b/contracts/deployments/hoodi/OETHOracleRouter.json new file mode 100644 index 0000000000..0362b24928 --- /dev/null +++ b/contracts/deployments/hoodi/OETHOracleRouter.json @@ -0,0 +1,136 @@ +{ + "address": "0x1525e719dEa297524Ad535A558497FfeB18C458a", + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "cacheDecimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "price", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "transactionHash": "0x27d4dbd12b5c02dc977797cdd4e024142abf4812c3ce5d9428c02c45da25c4a7", + "receipt": { + "to": null, + "from": "0xFD9E6005187F448957a0972a7d0C0A6dA2911236", + "contractAddress": "0x1525e719dEa297524Ad535A558497FfeB18C458a", + "transactionIndex": 0, + "gasUsed": "186729", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0xe6f1b99d8e832283d11835b51005d5c35e7bb79b0782ac577d6e0a876f10393a", + "transactionHash": "0x27d4dbd12b5c02dc977797cdd4e024142abf4812c3ce5d9428c02c45da25c4a7", + "logs": [], + "blockNumber": 828224, + "cumulativeGasUsed": "186729", + "status": 1, + "byzantium": true + }, + "args": [], + "numDeployments": 1, + "solcInputHash": "294c99ebae323732f7829a948fbd35c6", + "metadata": "{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"}],\"name\":\"cacheDecimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"}],\"name\":\"price\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"cacheDecimals(address)\":{\"params\":{\"asset\":\"address of the asset\"},\"returns\":{\"_0\":\"uint8 corresponding asset decimals\"}},\"price(address)\":{\"params\":{\"asset\":\"address of the asset\"},\"returns\":{\"_0\":\"uint256 unit price for 1 asset unit, in 18 decimal fixed\"}}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"cacheDecimals(address)\":{\"notice\":\"Before an asset/feed price is fetches for the first time the decimals need to be cached. This is a gas optimization\"},\"price(address)\":{\"notice\":\"Returns the total price in 18 digit units for a given asset. This implementation does not (!) do range checks as the parent OracleRouter does.\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/oracle/OETHFixedOracle.sol\":\"OETHFixedOracle\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/utils/math/SafeCast.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\\n * checks.\\n *\\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\\n * easily result in undesired exploitation or bugs, since developers usually\\n * assume that overflows raise errors. `SafeCast` restores this intuition by\\n * reverting the transaction when such an operation overflows.\\n *\\n * Using this library instead of the unchecked operations eliminates an entire\\n * class of bugs, so it's recommended to use it always.\\n *\\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\\n * all math on `uint256` and `int256` and then downcasting.\\n */\\nlibrary SafeCast {\\n /**\\n * @dev Returns the downcasted uint224 from uint256, reverting on\\n * overflow (when the input is greater than largest uint224).\\n *\\n * Counterpart to Solidity's `uint224` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 224 bits\\n */\\n function toUint224(uint256 value) internal pure returns (uint224) {\\n require(value <= type(uint224).max, \\\"SafeCast: value doesn't fit in 224 bits\\\");\\n return uint224(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint128 from uint256, reverting on\\n * overflow (when the input is greater than largest uint128).\\n *\\n * Counterpart to Solidity's `uint128` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 128 bits\\n */\\n function toUint128(uint256 value) internal pure returns (uint128) {\\n require(value <= type(uint128).max, \\\"SafeCast: value doesn't fit in 128 bits\\\");\\n return uint128(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint96 from uint256, reverting on\\n * overflow (when the input is greater than largest uint96).\\n *\\n * Counterpart to Solidity's `uint96` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 96 bits\\n */\\n function toUint96(uint256 value) internal pure returns (uint96) {\\n require(value <= type(uint96).max, \\\"SafeCast: value doesn't fit in 96 bits\\\");\\n return uint96(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint64 from uint256, reverting on\\n * overflow (when the input is greater than largest uint64).\\n *\\n * Counterpart to Solidity's `uint64` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 64 bits\\n */\\n function toUint64(uint256 value) internal pure returns (uint64) {\\n require(value <= type(uint64).max, \\\"SafeCast: value doesn't fit in 64 bits\\\");\\n return uint64(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint32 from uint256, reverting on\\n * overflow (when the input is greater than largest uint32).\\n *\\n * Counterpart to Solidity's `uint32` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 32 bits\\n */\\n function toUint32(uint256 value) internal pure returns (uint32) {\\n require(value <= type(uint32).max, \\\"SafeCast: value doesn't fit in 32 bits\\\");\\n return uint32(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint16 from uint256, reverting on\\n * overflow (when the input is greater than largest uint16).\\n *\\n * Counterpart to Solidity's `uint16` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 16 bits\\n */\\n function toUint16(uint256 value) internal pure returns (uint16) {\\n require(value <= type(uint16).max, \\\"SafeCast: value doesn't fit in 16 bits\\\");\\n return uint16(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint8 from uint256, reverting on\\n * overflow (when the input is greater than largest uint8).\\n *\\n * Counterpart to Solidity's `uint8` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 8 bits.\\n */\\n function toUint8(uint256 value) internal pure returns (uint8) {\\n require(value <= type(uint8).max, \\\"SafeCast: value doesn't fit in 8 bits\\\");\\n return uint8(value);\\n }\\n\\n /**\\n * @dev Converts a signed int256 into an unsigned uint256.\\n *\\n * Requirements:\\n *\\n * - input must be greater than or equal to 0.\\n */\\n function toUint256(int256 value) internal pure returns (uint256) {\\n require(value >= 0, \\\"SafeCast: value must be positive\\\");\\n return uint256(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int128 from int256, reverting on\\n * overflow (when the input is less than smallest int128 or\\n * greater than largest int128).\\n *\\n * Counterpart to Solidity's `int128` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 128 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt128(int256 value) internal pure returns (int128) {\\n require(value >= type(int128).min && value <= type(int128).max, \\\"SafeCast: value doesn't fit in 128 bits\\\");\\n return int128(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int64 from int256, reverting on\\n * overflow (when the input is less than smallest int64 or\\n * greater than largest int64).\\n *\\n * Counterpart to Solidity's `int64` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 64 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt64(int256 value) internal pure returns (int64) {\\n require(value >= type(int64).min && value <= type(int64).max, \\\"SafeCast: value doesn't fit in 64 bits\\\");\\n return int64(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int32 from int256, reverting on\\n * overflow (when the input is less than smallest int32 or\\n * greater than largest int32).\\n *\\n * Counterpart to Solidity's `int32` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 32 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt32(int256 value) internal pure returns (int32) {\\n require(value >= type(int32).min && value <= type(int32).max, \\\"SafeCast: value doesn't fit in 32 bits\\\");\\n return int32(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int16 from int256, reverting on\\n * overflow (when the input is less than smallest int16 or\\n * greater than largest int16).\\n *\\n * Counterpart to Solidity's `int16` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 16 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt16(int256 value) internal pure returns (int16) {\\n require(value >= type(int16).min && value <= type(int16).max, \\\"SafeCast: value doesn't fit in 16 bits\\\");\\n return int16(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int8 from int256, reverting on\\n * overflow (when the input is less than smallest int8 or\\n * greater than largest int8).\\n *\\n * Counterpart to Solidity's `int8` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 8 bits.\\n *\\n * _Available since v3.1._\\n */\\n function toInt8(int256 value) internal pure returns (int8) {\\n require(value >= type(int8).min && value <= type(int8).max, \\\"SafeCast: value doesn't fit in 8 bits\\\");\\n return int8(value);\\n }\\n\\n /**\\n * @dev Converts an unsigned uint256 into a signed int256.\\n *\\n * Requirements:\\n *\\n * - input must be less than or equal to maxInt256.\\n */\\n function toInt256(uint256 value) internal pure returns (int256) {\\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\\n require(value <= uint256(type(int256).max), \\\"SafeCast: value doesn't fit in an int256\\\");\\n return int256(value);\\n }\\n}\\n\",\"keccak256\":\"0x5c6caab697d302ad7eb59c234a4d2dbc965c1bae87709bd2850060b7695b28c7\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/math/SafeMath.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)\\n\\npragma solidity ^0.8.0;\\n\\n// CAUTION\\n// This version of SafeMath should only be used with Solidity 0.8 or later,\\n// because it relies on the compiler's built in overflow checks.\\n\\n/**\\n * @dev Wrappers over Solidity's arithmetic operations.\\n *\\n * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler\\n * now has built in overflow checking.\\n */\\nlibrary SafeMath {\\n /**\\n * @dev Returns the addition of two unsigned integers, with an overflow flag.\\n *\\n * _Available since v3.4._\\n */\\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\\n unchecked {\\n uint256 c = a + b;\\n if (c < a) return (false, 0);\\n return (true, c);\\n }\\n }\\n\\n /**\\n * @dev Returns the substraction of two unsigned integers, with an overflow flag.\\n *\\n * _Available since v3.4._\\n */\\n function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\\n unchecked {\\n if (b > a) return (false, 0);\\n return (true, a - b);\\n }\\n }\\n\\n /**\\n * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\\n *\\n * _Available since v3.4._\\n */\\n function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\\n unchecked {\\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\\n // benefit is lost if 'b' is also tested.\\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\\n if (a == 0) return (true, 0);\\n uint256 c = a * b;\\n if (c / a != b) return (false, 0);\\n return (true, c);\\n }\\n }\\n\\n /**\\n * @dev Returns the division of two unsigned integers, with a division by zero flag.\\n *\\n * _Available since v3.4._\\n */\\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\\n unchecked {\\n if (b == 0) return (false, 0);\\n return (true, a / b);\\n }\\n }\\n\\n /**\\n * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\\n *\\n * _Available since v3.4._\\n */\\n function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\\n unchecked {\\n if (b == 0) return (false, 0);\\n return (true, a % b);\\n }\\n }\\n\\n /**\\n * @dev Returns the addition of two unsigned integers, reverting on\\n * overflow.\\n *\\n * Counterpart to Solidity's `+` operator.\\n *\\n * Requirements:\\n *\\n * - Addition cannot overflow.\\n */\\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a + b;\\n }\\n\\n /**\\n * @dev Returns the subtraction of two unsigned integers, reverting on\\n * overflow (when the result is negative).\\n *\\n * Counterpart to Solidity's `-` operator.\\n *\\n * Requirements:\\n *\\n * - Subtraction cannot overflow.\\n */\\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a - b;\\n }\\n\\n /**\\n * @dev Returns the multiplication of two unsigned integers, reverting on\\n * overflow.\\n *\\n * Counterpart to Solidity's `*` operator.\\n *\\n * Requirements:\\n *\\n * - Multiplication cannot overflow.\\n */\\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a * b;\\n }\\n\\n /**\\n * @dev Returns the integer division of two unsigned integers, reverting on\\n * division by zero. The result is rounded towards zero.\\n *\\n * Counterpart to Solidity's `/` operator.\\n *\\n * Requirements:\\n *\\n * - The divisor cannot be zero.\\n */\\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a / b;\\n }\\n\\n /**\\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\\n * reverting when dividing by zero.\\n *\\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\\n * opcode (which leaves remaining gas untouched) while Solidity uses an\\n * invalid opcode to revert (consuming all remaining gas).\\n *\\n * Requirements:\\n *\\n * - The divisor cannot be zero.\\n */\\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a % b;\\n }\\n\\n /**\\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\\n * overflow (when the result is negative).\\n *\\n * CAUTION: This function is deprecated because it requires allocating memory for the error\\n * message unnecessarily. For custom revert reasons use {trySub}.\\n *\\n * Counterpart to Solidity's `-` operator.\\n *\\n * Requirements:\\n *\\n * - Subtraction cannot overflow.\\n */\\n function sub(\\n uint256 a,\\n uint256 b,\\n string memory errorMessage\\n ) internal pure returns (uint256) {\\n unchecked {\\n require(b <= a, errorMessage);\\n return a - b;\\n }\\n }\\n\\n /**\\n * @dev Returns the integer division of two unsigned integers, reverting with custom message on\\n * division by zero. The result is rounded towards zero.\\n *\\n * Counterpart to Solidity's `/` operator. Note: this function uses a\\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\\n * uses an invalid opcode to revert (consuming all remaining gas).\\n *\\n * Requirements:\\n *\\n * - The divisor cannot be zero.\\n */\\n function div(\\n uint256 a,\\n uint256 b,\\n string memory errorMessage\\n ) internal pure returns (uint256) {\\n unchecked {\\n require(b > 0, errorMessage);\\n return a / b;\\n }\\n }\\n\\n /**\\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\\n * reverting with custom message when dividing by zero.\\n *\\n * CAUTION: This function is deprecated because it requires allocating memory for the error\\n * message unnecessarily. For custom revert reasons use {tryMod}.\\n *\\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\\n * opcode (which leaves remaining gas untouched) while Solidity uses an\\n * invalid opcode to revert (consuming all remaining gas).\\n *\\n * Requirements:\\n *\\n * - The divisor cannot be zero.\\n */\\n function mod(\\n uint256 a,\\n uint256 b,\\n string memory errorMessage\\n ) internal pure returns (uint256) {\\n unchecked {\\n require(b > 0, errorMessage);\\n return a % b;\\n }\\n }\\n}\\n\",\"keccak256\":\"0xa2f576be637946f767aa56601c26d717f48a0aff44f82e46f13807eea1009a21\",\"license\":\"MIT\"},\"contracts/interfaces/IBasicToken.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\ninterface IBasicToken {\\n function symbol() external view returns (string memory);\\n\\n function decimals() external view returns (uint8);\\n}\\n\",\"keccak256\":\"0xa562062698aa12572123b36dfd2072f1a39e44fed2031cc19c2c9fd522f96ec2\",\"license\":\"MIT\"},\"contracts/interfaces/IOracle.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\ninterface IOracle {\\n /**\\n * @dev returns the asset price in USD, in 8 decimal digits.\\n *\\n * The version of priceProvider deployed for OETH has 18 decimal digits\\n */\\n function price(address asset) external view returns (uint256);\\n}\\n\",\"keccak256\":\"0xa5f765f5b22cd5426803b22a7344d4c34c4d4016a0b6e9d799862133253f77b2\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/chainlink/AggregatorV3Interface.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\ninterface AggregatorV3Interface {\\n function decimals() external view returns (uint8);\\n\\n function description() external view returns (string memory);\\n\\n function version() external view returns (uint256);\\n\\n // getRoundData and latestRoundData should both raise \\\"No data present\\\"\\n // if they do not have data to report, instead of returning unset values\\n // which could be misinterpreted as actual reported values.\\n function getRoundData(uint80 _roundId)\\n external\\n view\\n returns (\\n uint80 roundId,\\n int256 answer,\\n uint256 startedAt,\\n uint256 updatedAt,\\n uint80 answeredInRound\\n );\\n\\n function latestRoundData()\\n external\\n view\\n returns (\\n uint80 roundId,\\n int256 answer,\\n uint256 startedAt,\\n uint256 updatedAt,\\n uint80 answeredInRound\\n );\\n}\\n\",\"keccak256\":\"0x18fb68de95136c49f3874fe7795a7bda730339198b2816690ddbdf1eacd4e273\",\"license\":\"MIT\"},\"contracts/oracle/AbstractOracleRouter.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport \\\"../interfaces/chainlink/AggregatorV3Interface.sol\\\";\\nimport { IOracle } from \\\"../interfaces/IOracle.sol\\\";\\nimport { Helpers } from \\\"../utils/Helpers.sol\\\";\\nimport { StableMath } from \\\"../utils/StableMath.sol\\\";\\nimport { SafeCast } from \\\"@openzeppelin/contracts/utils/math/SafeCast.sol\\\";\\n\\n// @notice Abstract functionality that is shared between various Oracle Routers\\nabstract contract AbstractOracleRouter is IOracle {\\n using StableMath for uint256;\\n using SafeCast for int256;\\n\\n uint256 internal constant MIN_DRIFT = 0.7e18;\\n uint256 internal constant MAX_DRIFT = 1.3e18;\\n address internal constant FIXED_PRICE =\\n 0x0000000000000000000000000000000000000001;\\n // Maximum allowed staleness buffer above normal Oracle maximum staleness\\n uint256 internal constant STALENESS_BUFFER = 1 days;\\n mapping(address => uint8) internal decimalsCache;\\n\\n /**\\n * @dev The price feed contract to use for a particular asset along with\\n * maximum data staleness\\n * @param asset address of the asset\\n * @return feedAddress address of the price feed for the asset\\n * @return maxStaleness maximum acceptable data staleness duration\\n */\\n function feedMetadata(address asset)\\n internal\\n view\\n virtual\\n returns (address feedAddress, uint256 maxStaleness);\\n\\n /**\\n * @notice Returns the total price in 18 digit unit for a given asset.\\n * @param asset address of the asset\\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\\n */\\n function price(address asset)\\n external\\n view\\n virtual\\n override\\n returns (uint256)\\n {\\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\\n require(_feed != address(0), \\\"Asset not available\\\");\\n require(_feed != FIXED_PRICE, \\\"Fixed price feeds not supported\\\");\\n\\n // slither-disable-next-line unused-return\\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\\n .latestRoundData();\\n\\n require(\\n updatedAt + maxStaleness >= block.timestamp,\\n \\\"Oracle price too old\\\"\\n );\\n\\n uint8 decimals = getDecimals(_feed);\\n\\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\\n if (shouldBePegged(asset)) {\\n require(_price <= MAX_DRIFT, \\\"Oracle: Price exceeds max\\\");\\n require(_price >= MIN_DRIFT, \\\"Oracle: Price under min\\\");\\n }\\n return _price;\\n }\\n\\n function getDecimals(address _feed) internal view virtual returns (uint8) {\\n uint8 decimals = decimalsCache[_feed];\\n require(decimals > 0, \\\"Oracle: Decimals not cached\\\");\\n return decimals;\\n }\\n\\n /**\\n * @notice Before an asset/feed price is fetches for the first time the\\n * decimals need to be cached. This is a gas optimization\\n * @param asset address of the asset\\n * @return uint8 corresponding asset decimals\\n */\\n function cacheDecimals(address asset) external returns (uint8) {\\n (address _feed, ) = feedMetadata(asset);\\n require(_feed != address(0), \\\"Asset not available\\\");\\n require(_feed != FIXED_PRICE, \\\"Fixed price feeds not supported\\\");\\n\\n uint8 decimals = AggregatorV3Interface(_feed).decimals();\\n decimalsCache[_feed] = decimals;\\n return decimals;\\n }\\n\\n function shouldBePegged(address _asset) internal view returns (bool) {\\n string memory symbol = Helpers.getSymbol(_asset);\\n bytes32 symbolHash = keccak256(abi.encodePacked(symbol));\\n return\\n symbolHash == keccak256(abi.encodePacked(\\\"DAI\\\")) ||\\n symbolHash == keccak256(abi.encodePacked(\\\"USDC\\\")) ||\\n symbolHash == keccak256(abi.encodePacked(\\\"USDT\\\"));\\n }\\n}\\n\",\"keccak256\":\"0xc0e577397b47443a2b2cd570c9d2cdbc1ce08cbd97b774309e9b3059c8041ab7\",\"license\":\"BUSL-1.1\"},\"contracts/oracle/OETHFixedOracle.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { OETHOracleRouter } from \\\"./OETHOracleRouter.sol\\\";\\n\\n// @notice Oracle Router that returns 1e18 for all prices\\n// used solely for deployment to testnets\\ncontract OETHFixedOracle is OETHOracleRouter {\\n constructor() OETHOracleRouter() {}\\n\\n /**\\n * @dev The price feed contract to use for a particular asset along with\\n * maximum data staleness\\n * @param asset address of the asset\\n * @return feedAddress address of the price feed for the asset\\n * @return maxStaleness maximum acceptable data staleness duration\\n */\\n // solhint-disable-next-line no-unused-vars\\n function feedMetadata(address asset)\\n internal\\n view\\n virtual\\n override\\n returns (address feedAddress, uint256 maxStaleness)\\n {\\n // fixes price for all of the assets\\n feedAddress = FIXED_PRICE;\\n maxStaleness = 0;\\n }\\n}\\n\",\"keccak256\":\"0xd26329b6b78b40d48cdb3dc1d07195d64ab25ad1dd170913774e22c8a442d9bc\",\"license\":\"BUSL-1.1\"},\"contracts/oracle/OETHOracleRouter.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport \\\"../interfaces/chainlink/AggregatorV3Interface.sol\\\";\\nimport { AbstractOracleRouter } from \\\"./AbstractOracleRouter.sol\\\";\\nimport { StableMath } from \\\"../utils/StableMath.sol\\\";\\n\\n// @notice Oracle Router that denominates all prices in ETH\\ncontract OETHOracleRouter is AbstractOracleRouter {\\n using StableMath for uint256;\\n\\n constructor() {}\\n\\n /**\\n * @notice Returns the total price in 18 digit units for a given asset.\\n * This implementation does not (!) do range checks as the\\n * parent OracleRouter does.\\n * @param asset address of the asset\\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\\n */\\n function price(address asset)\\n external\\n view\\n virtual\\n override\\n returns (uint256)\\n {\\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\\n if (_feed == FIXED_PRICE) {\\n return 1e18;\\n }\\n require(_feed != address(0), \\\"Asset not available\\\");\\n\\n // slither-disable-next-line unused-return\\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\\n .latestRoundData();\\n\\n require(\\n updatedAt + maxStaleness >= block.timestamp,\\n \\\"Oracle price too old\\\"\\n );\\n\\n uint8 decimals = getDecimals(_feed);\\n uint256 _price = uint256(_iprice).scaleBy(18, decimals);\\n return _price;\\n }\\n\\n /**\\n * @dev The price feed contract to use for a particular asset along with\\n * maximum data staleness\\n * @param asset address of the asset\\n * @return feedAddress address of the price feed for the asset\\n * @return maxStaleness maximum acceptable data staleness duration\\n */\\n function feedMetadata(address asset)\\n internal\\n view\\n virtual\\n override\\n returns (address feedAddress, uint256 maxStaleness)\\n {\\n if (asset == 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) {\\n // FIXED_PRICE: WETH/ETH\\n feedAddress = FIXED_PRICE;\\n maxStaleness = 0;\\n } else if (asset == 0x5E8422345238F34275888049021821E8E08CAa1f) {\\n // frxETH/ETH\\n feedAddress = 0xC58F3385FBc1C8AD2c0C9a061D7c13b141D7A5Df;\\n maxStaleness = 18 hours + STALENESS_BUFFER;\\n } else if (asset == 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84) {\\n // https://data.chain.link/ethereum/mainnet/crypto-eth/steth-eth\\n // Chainlink: stETH/ETH\\n feedAddress = 0x86392dC19c0b719886221c78AB11eb8Cf5c52812;\\n maxStaleness = 1 days + STALENESS_BUFFER;\\n } else if (asset == 0xae78736Cd615f374D3085123A210448E74Fc6393) {\\n // https://data.chain.link/ethereum/mainnet/crypto-eth/reth-eth\\n // Chainlink: rETH/ETH\\n feedAddress = 0x536218f9E9Eb48863970252233c8F271f554C2d0;\\n maxStaleness = 1 days + STALENESS_BUFFER;\\n } else if (asset == 0xD533a949740bb3306d119CC777fa900bA034cd52) {\\n // https://data.chain.link/ethereum/mainnet/crypto-eth/crv-eth\\n // Chainlink: CRV/ETH\\n feedAddress = 0x8a12Be339B0cD1829b91Adc01977caa5E9ac121e;\\n maxStaleness = 1 days + STALENESS_BUFFER;\\n } else if (asset == 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B) {\\n // https://data.chain.link/ethereum/mainnet/crypto-eth/cvx-eth\\n // Chainlink: CVX/ETH\\n feedAddress = 0xC9CbF687f43176B302F03f5e58470b77D07c61c6;\\n maxStaleness = 1 days + STALENESS_BUFFER;\\n } else if (asset == 0xBe9895146f7AF43049ca1c1AE358B0541Ea49704) {\\n // https://data.chain.link/ethereum/mainnet/crypto-eth/cbeth-eth\\n // Chainlink: cbETH/ETH\\n feedAddress = 0xF017fcB346A1885194689bA23Eff2fE6fA5C483b;\\n maxStaleness = 1 days + STALENESS_BUFFER;\\n } else if (asset == 0xba100000625a3754423978a60c9317c58a424e3D) {\\n // https://data.chain.link/ethereum/mainnet/crypto-eth/bal-eth\\n // Chainlink: BAL/ETH\\n feedAddress = 0xC1438AA3823A6Ba0C159CfA8D98dF5A994bA120b;\\n maxStaleness = 1 days + STALENESS_BUFFER;\\n } else {\\n revert(\\\"Asset not available\\\");\\n }\\n }\\n}\\n\",\"keccak256\":\"0x81c073d7ab873c83265c53ce8b6a99044efe5287edaf5648d35ddd72edf6e0d3\",\"license\":\"BUSL-1.1\"},\"contracts/utils/Helpers.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { IBasicToken } from \\\"../interfaces/IBasicToken.sol\\\";\\n\\nlibrary Helpers {\\n /**\\n * @notice Fetch the `symbol()` from an ERC20 token\\n * @dev Grabs the `symbol()` from a contract\\n * @param _token Address of the ERC20 token\\n * @return string Symbol of the ERC20 token\\n */\\n function getSymbol(address _token) internal view returns (string memory) {\\n string memory symbol = IBasicToken(_token).symbol();\\n return symbol;\\n }\\n\\n /**\\n * @notice Fetch the `decimals()` from an ERC20 token\\n * @dev Grabs the `decimals()` from a contract and fails if\\n * the decimal value does not live within a certain range\\n * @param _token Address of the ERC20 token\\n * @return uint256 Decimals of the ERC20 token\\n */\\n function getDecimals(address _token) internal view returns (uint256) {\\n uint256 decimals = IBasicToken(_token).decimals();\\n require(\\n decimals >= 4 && decimals <= 18,\\n \\\"Token must have sufficient decimal places\\\"\\n );\\n\\n return decimals;\\n }\\n}\\n\",\"keccak256\":\"0x4366f8d90b34c1eef8bbaaf369b1e5cd59f04027bb3c111f208eaee65bbc0346\",\"license\":\"BUSL-1.1\"},\"contracts/utils/StableMath.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { SafeMath } from \\\"@openzeppelin/contracts/utils/math/SafeMath.sol\\\";\\n\\n// Based on StableMath from Stability Labs Pty. Ltd.\\n// https://github.com/mstable/mStable-contracts/blob/master/contracts/shared/StableMath.sol\\n\\nlibrary StableMath {\\n using SafeMath for uint256;\\n\\n /**\\n * @dev Scaling unit for use in specific calculations,\\n * where 1 * 10**18, or 1e18 represents a unit '1'\\n */\\n uint256 private constant FULL_SCALE = 1e18;\\n\\n /***************************************\\n Helpers\\n ****************************************/\\n\\n /**\\n * @dev Adjust the scale of an integer\\n * @param to Decimals to scale to\\n * @param from Decimals to scale from\\n */\\n function scaleBy(\\n uint256 x,\\n uint256 to,\\n uint256 from\\n ) internal pure returns (uint256) {\\n if (to > from) {\\n x = x.mul(10**(to - from));\\n } else if (to < from) {\\n // slither-disable-next-line divide-before-multiply\\n x = x.div(10**(from - to));\\n }\\n return x;\\n }\\n\\n /***************************************\\n Precise Arithmetic\\n ****************************************/\\n\\n /**\\n * @dev Multiplies two precise units, and then truncates by the full scale\\n * @param x Left hand input to multiplication\\n * @param y Right hand input to multiplication\\n * @return Result after multiplying the two inputs and then dividing by the shared\\n * scale unit\\n */\\n function mulTruncate(uint256 x, uint256 y) internal pure returns (uint256) {\\n return mulTruncateScale(x, y, FULL_SCALE);\\n }\\n\\n /**\\n * @dev Multiplies two precise units, and then truncates by the given scale. For example,\\n * when calculating 90% of 10e18, (10e18 * 9e17) / 1e18 = (9e36) / 1e18 = 9e18\\n * @param x Left hand input to multiplication\\n * @param y Right hand input to multiplication\\n * @param scale Scale unit\\n * @return Result after multiplying the two inputs and then dividing by the shared\\n * scale unit\\n */\\n function mulTruncateScale(\\n uint256 x,\\n uint256 y,\\n uint256 scale\\n ) internal pure returns (uint256) {\\n // e.g. assume scale = fullScale\\n // z = 10e18 * 9e17 = 9e36\\n uint256 z = x.mul(y);\\n // return 9e36 / 1e18 = 9e18\\n return z.div(scale);\\n }\\n\\n /**\\n * @dev Multiplies two precise units, and then truncates by the full scale, rounding up the result\\n * @param x Left hand input to multiplication\\n * @param y Right hand input to multiplication\\n * @return Result after multiplying the two inputs and then dividing by the shared\\n * scale unit, rounded up to the closest base unit.\\n */\\n function mulTruncateCeil(uint256 x, uint256 y)\\n internal\\n pure\\n returns (uint256)\\n {\\n // e.g. 8e17 * 17268172638 = 138145381104e17\\n uint256 scaled = x.mul(y);\\n // e.g. 138145381104e17 + 9.99...e17 = 138145381113.99...e17\\n uint256 ceil = scaled.add(FULL_SCALE.sub(1));\\n // e.g. 13814538111.399...e18 / 1e18 = 13814538111\\n return ceil.div(FULL_SCALE);\\n }\\n\\n /**\\n * @dev Precisely divides two units, by first scaling the left hand operand. Useful\\n * for finding percentage weightings, i.e. 8e18/10e18 = 80% (or 8e17)\\n * @param x Left hand input to division\\n * @param y Right hand input to division\\n * @return Result after multiplying the left operand by the scale, and\\n * executing the division on the right hand input.\\n */\\n function divPrecisely(uint256 x, uint256 y)\\n internal\\n pure\\n returns (uint256)\\n {\\n // e.g. 8e18 * 1e18 = 8e36\\n uint256 z = x.mul(FULL_SCALE);\\n // e.g. 8e36 / 10e18 = 8e17\\n return z.div(y);\\n }\\n}\\n\",\"keccak256\":\"0x71d6ed0053a1e5ef018d27c3b6d024f336d8157ab6f6859e400b3243a50a71b7\",\"license\":\"BUSL-1.1\"}},\"version\":1}", + "bytecode": "0x6080604052348015600f57600080fd5b5061026a8061001f6000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806336b6d9441461003b578063aea9107814610065575b600080fd5b61004e610049366004610193565b61008e565b60405160ff90911681526020015b60405180910390f35b610080610073366004610193565b50670de0b6b3a764000090565b60405190815260200161005c565b600060016100a0565b60405180910390fd5b6000196001600160a01b038216016100fa5760405162461bcd60e51b815260206004820152601f60248201527f4669786564207072696365206665656473206e6f7420737570706f72746564006044820152606401610097565b6000816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561013a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061015e91906101bc565b6001600160a01b03929092166000908152602081905260409020805460ff191660ff84161790555092915050565b9392505050565b6000602082840312156101a557600080fd5b81356001600160a01b038116811461018c57600080fd5b6000602082840312156101ce57600080fd5b815160ff8116811461018c57600080fd5b634e487b7160e01b600052601160045260246000fd5b600184111561022c57808504811115610210576102106101df565b600184161561021e57908102905b60019390931c9280026101f5565b93509391505056fea26469706673582212206e813b0b74d18c9ed5f2926f0a09b85f54289ec634f84a03a62694097450e5d064736f6c634300081c0033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806336b6d9441461003b578063aea9107814610065575b600080fd5b61004e610049366004610193565b61008e565b60405160ff90911681526020015b60405180910390f35b610080610073366004610193565b50670de0b6b3a764000090565b60405190815260200161005c565b600060016100a0565b60405180910390fd5b6000196001600160a01b038216016100fa5760405162461bcd60e51b815260206004820152601f60248201527f4669786564207072696365206665656473206e6f7420737570706f72746564006044820152606401610097565b6000816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561013a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061015e91906101bc565b6001600160a01b03929092166000908152602081905260409020805460ff191660ff84161790555092915050565b9392505050565b6000602082840312156101a557600080fd5b81356001600160a01b038116811461018c57600080fd5b6000602082840312156101ce57600080fd5b815160ff8116811461018c57600080fd5b634e487b7160e01b600052601160045260246000fd5b600184111561022c57808504811115610210576102106101df565b600184161561021e57908102905b60019390931c9280026101f5565b93509391505056fea26469706673582212206e813b0b74d18c9ed5f2926f0a09b85f54289ec634f84a03a62694097450e5d064736f6c634300081c0033", + "libraries": {}, + "devdoc": { + "kind": "dev", + "methods": { + "cacheDecimals(address)": { + "params": { + "asset": "address of the asset" + }, + "returns": { + "_0": "uint8 corresponding asset decimals" + } + }, + "price(address)": { + "params": { + "asset": "address of the asset" + }, + "returns": { + "_0": "uint256 unit price for 1 asset unit, in 18 decimal fixed" + } + } + }, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "cacheDecimals(address)": { + "notice": "Before an asset/feed price is fetches for the first time the decimals need to be cached. This is a gas optimization" + }, + "price(address)": { + "notice": "Returns the total price in 18 digit units for a given asset. This implementation does not (!) do range checks as the parent OracleRouter does." + } + }, + "version": 1 + }, + "storageLayout": { + "storage": [ + { + "astId": 36711, + "contract": "contracts/oracle/OETHFixedOracle.sol:OETHFixedOracle", + "label": "decimalsCache", + "offset": 0, + "slot": "0", + "type": "t_mapping(t_address,t_uint8)" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "t_mapping(t_address,t_uint8)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => uint8)", + "numberOfBytes": "32", + "value": "t_uint8" + }, + "t_uint8": { + "encoding": "inplace", + "label": "uint8", + "numberOfBytes": "1" + } + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/OETHProxy.json b/contracts/deployments/hoodi/OETHProxy.json new file mode 100644 index 0000000000..95db837276 --- /dev/null +++ b/contracts/deployments/hoodi/OETHProxy.json @@ -0,0 +1,297 @@ +{ + "address": "0xbebDca6eF7452953e0AB5cebE2A174B71208B13a", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "GovernorshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "PendingGovernorshipTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "claimGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "governor", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "implementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_logic", + "type": "address" + }, + { + "internalType": "address", + "name": "_initGovernor", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "isGovernor", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newGovernor", + "type": "address" + } + ], + "name": "transferGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } + ], + "transactionHash": "0xf5606cbcd09f5cd1482c84f2f10991014e011b43bfcb6d656f32cfa257a7f5a4", + "receipt": { + "to": null, + "from": "0xFD9E6005187F448957a0972a7d0C0A6dA2911236", + "contractAddress": "0xbebDca6eF7452953e0AB5cebE2A174B71208B13a", + "transactionIndex": 0, + "gasUsed": "599335", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000004000000000000004000000000000000020000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000010000000000020000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x04d7ad8eaa79d52fc18c967a94e818879b12579fb43389d9ecb1a9913d735f56", + "transactionHash": "0xf5606cbcd09f5cd1482c84f2f10991014e011b43bfcb6d656f32cfa257a7f5a4", + "logs": [ + { + "transactionIndex": 0, + "blockNumber": 828226, + "transactionHash": "0xf5606cbcd09f5cd1482c84f2f10991014e011b43bfcb6d656f32cfa257a7f5a4", + "address": "0xbebDca6eF7452953e0AB5cebE2A174B71208B13a", + "topics": [ + "0xc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000fd9e6005187f448957a0972a7d0c0a6da2911236" + ], + "data": "0x", + "logIndex": 0, + "blockHash": "0x04d7ad8eaa79d52fc18c967a94e818879b12579fb43389d9ecb1a9913d735f56" + } + ], + "blockNumber": 828226, + "cumulativeGasUsed": "599335", + "status": 1, + "byzantium": true + }, + "args": [], + "numDeployments": 1, + "solcInputHash": "5518586a577179807341642341c56a4f", + "metadata": "{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"GovernorshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"PendingGovernorshipTransfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"implementation\",\"type\":\"address\"}],\"name\":\"Upgraded\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"governor\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"implementation\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_logic\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_initGovernor\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isGovernor\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newGovernor\",\"type\":\"address\"}],\"name\":\"transferGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newImplementation\",\"type\":\"address\"}],\"name\":\"upgradeTo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"upgradeToAndCall\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"}],\"devdoc\":{\"events\":{\"Upgraded(address)\":{\"details\":\"Emitted when the implementation is upgraded.\",\"params\":{\"implementation\":\"Address of the new implementation.\"}}},\"kind\":\"dev\",\"methods\":{\"admin()\":{\"returns\":{\"_0\":\"The address of the proxy admin/it's also the governor.\"}},\"implementation()\":{\"returns\":{\"_0\":\"The address of the implementation.\"}},\"initialize(address,address,bytes)\":{\"details\":\"Contract initializer with Governor enforcement\",\"params\":{\"_data\":\"Data to send as msg.data to the implementation to initialize the proxied contract. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.\",\"_initGovernor\":\"Address of the initial Governor.\",\"_logic\":\"Address of the initial implementation.\"}},\"transferGovernance(address)\":{\"params\":{\"_newGovernor\":\"Address of the new Governor\"}},\"upgradeTo(address)\":{\"details\":\"Upgrade the backing implementation of the proxy. Only the admin can call this function.\",\"params\":{\"_newImplementation\":\"Address of the new implementation.\"}},\"upgradeToAndCall(address,bytes)\":{\"details\":\"Upgrade the backing implementation of the proxy and call a function on the new implementation. This is useful to initialize the proxied contract.\",\"params\":{\"data\":\"Data to send as msg.data in the low level call. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\",\"newImplementation\":\"Address of the new implementation.\"}}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"claimGovernance()\":{\"notice\":\"Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor.\"},\"governor()\":{\"notice\":\"Returns the address of the current Governor.\"},\"isGovernor()\":{\"notice\":\"Returns true if the caller is the current Governor.\"},\"transferGovernance(address)\":{\"notice\":\"Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete\"}},\"notice\":\"OETHProxy delegates calls to nowhere for now\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/proxies/Proxies.sol\":\"OETHProxy\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize, which returns 0 for contracts in\\n // construction, since the code is only stored at the end of the\\n // constructor execution.\\n\\n uint256 size;\\n assembly {\\n size := extcodesize(account)\\n }\\n return size > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCall(target, data, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n require(isContract(target), \\\"Address: static call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(isContract(target), \\\"Address: delegate call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x51b758a8815ecc9596c66c37d56b1d33883a444631a3f916b9fe65cb863ef7c4\",\"license\":\"MIT\"},\"contracts/governance/Governable.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\\n * from owner to governor and renounce methods removed. Does not use\\n * Context.sol like Ownable.sol does for simplification.\\n * @author Origin Protocol Inc\\n */\\nabstract contract Governable {\\n // Storage position of the owner and pendingOwner of the contract\\n // keccak256(\\\"OUSD.governor\\\");\\n bytes32 private constant governorPosition =\\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\\n\\n // keccak256(\\\"OUSD.pending.governor\\\");\\n bytes32 private constant pendingGovernorPosition =\\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\\n\\n // keccak256(\\\"OUSD.reentry.status\\\");\\n bytes32 private constant reentryStatusPosition =\\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\\n\\n // See OpenZeppelin ReentrancyGuard implementation\\n uint256 constant _NOT_ENTERED = 1;\\n uint256 constant _ENTERED = 2;\\n\\n event PendingGovernorshipTransfer(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n event GovernorshipTransferred(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n /**\\n * @notice Returns the address of the current Governor.\\n */\\n function governor() public view returns (address) {\\n return _governor();\\n }\\n\\n /**\\n * @dev Returns the address of the current Governor.\\n */\\n function _governor() internal view returns (address governorOut) {\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n governorOut := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Returns the address of the pending Governor.\\n */\\n function _pendingGovernor()\\n internal\\n view\\n returns (address pendingGovernor)\\n {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n pendingGovernor := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the Governor.\\n */\\n modifier onlyGovernor() {\\n require(isGovernor(), \\\"Caller is not the Governor\\\");\\n _;\\n }\\n\\n /**\\n * @notice Returns true if the caller is the current Governor.\\n */\\n function isGovernor() public view returns (bool) {\\n return msg.sender == _governor();\\n }\\n\\n function _setGovernor(address newGovernor) internal {\\n emit GovernorshipTransferred(_governor(), newGovernor);\\n\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @dev Prevents a contract from calling itself, directly or indirectly.\\n * Calling a `nonReentrant` function from another `nonReentrant`\\n * function is not supported. It is possible to prevent this from happening\\n * by making the `nonReentrant` function external, and make it call a\\n * `private` function that does the actual work.\\n */\\n modifier nonReentrant() {\\n bytes32 position = reentryStatusPosition;\\n uint256 _reentry_status;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n _reentry_status := sload(position)\\n }\\n\\n // On the first call to nonReentrant, _notEntered will be true\\n require(_reentry_status != _ENTERED, \\\"Reentrant call\\\");\\n\\n // Any calls to nonReentrant after this point will fail\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _ENTERED)\\n }\\n\\n _;\\n\\n // By storing the original value once again, a refund is triggered (see\\n // https://eips.ethereum.org/EIPS/eip-2200)\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _NOT_ENTERED)\\n }\\n }\\n\\n function _setPendingGovernor(address newGovernor) internal {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the current Governor. Must be claimed for this to complete\\n * @param _newGovernor Address of the new Governor\\n */\\n function transferGovernance(address _newGovernor) external onlyGovernor {\\n _setPendingGovernor(_newGovernor);\\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\\n }\\n\\n /**\\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the new Governor.\\n */\\n function claimGovernance() external {\\n require(\\n msg.sender == _pendingGovernor(),\\n \\\"Only the pending Governor can complete the claim\\\"\\n );\\n _changeGovernor(msg.sender);\\n }\\n\\n /**\\n * @dev Change Governance of the contract to a new account (`newGovernor`).\\n * @param _newGovernor Address of the new Governor\\n */\\n function _changeGovernor(address _newGovernor) internal {\\n require(_newGovernor != address(0), \\\"New Governor is address(0)\\\");\\n _setGovernor(_newGovernor);\\n }\\n}\\n\",\"keccak256\":\"0xf32f873c8bfbacf2e5f01d0cf37bc7f54fbd5aa656e95c8a599114229946f107\",\"license\":\"BUSL-1.1\"},\"contracts/proxies/InitializeGovernedUpgradeabilityProxy.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { Address } from \\\"@openzeppelin/contracts/utils/Address.sol\\\";\\n\\nimport { Governable } from \\\"../governance/Governable.sol\\\";\\n\\n/**\\n * @title BaseGovernedUpgradeabilityProxy\\n * @dev This contract combines an upgradeability proxy with our governor system.\\n * It is based on an older version of OpenZeppelins BaseUpgradeabilityProxy\\n * with Solidity ^0.8.0.\\n * @author Origin Protocol Inc\\n */\\ncontract InitializeGovernedUpgradeabilityProxy is Governable {\\n /**\\n * @dev Emitted when the implementation is upgraded.\\n * @param implementation Address of the new implementation.\\n */\\n event Upgraded(address indexed implementation);\\n\\n constructor() {\\n _setGovernor(msg.sender);\\n }\\n\\n /**\\n * @dev Contract initializer with Governor enforcement\\n * @param _logic Address of the initial implementation.\\n * @param _initGovernor Address of the initial Governor.\\n * @param _data Data to send as msg.data to the implementation to initialize\\n * the proxied contract.\\n * It should include the signature and the parameters of the function to be\\n * called, as described in\\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\\n * This parameter is optional, if no data is given the initialization call\\n * to proxied contract will be skipped.\\n */\\n function initialize(\\n address _logic,\\n address _initGovernor,\\n bytes calldata _data\\n ) public payable onlyGovernor {\\n require(_implementation() == address(0));\\n require(_logic != address(0), \\\"Implementation not set\\\");\\n assert(\\n IMPLEMENTATION_SLOT ==\\n bytes32(uint256(keccak256(\\\"eip1967.proxy.implementation\\\")) - 1)\\n );\\n _setImplementation(_logic);\\n if (_data.length > 0) {\\n (bool success, ) = _logic.delegatecall(_data);\\n require(success);\\n }\\n _changeGovernor(_initGovernor);\\n }\\n\\n /**\\n * @return The address of the proxy admin/it's also the governor.\\n */\\n function admin() external view returns (address) {\\n return _governor();\\n }\\n\\n /**\\n * @return The address of the implementation.\\n */\\n function implementation() external view returns (address) {\\n return _implementation();\\n }\\n\\n /**\\n * @dev Upgrade the backing implementation of the proxy.\\n * Only the admin can call this function.\\n * @param _newImplementation Address of the new implementation.\\n */\\n function upgradeTo(address _newImplementation) external onlyGovernor {\\n _upgradeTo(_newImplementation);\\n }\\n\\n /**\\n * @dev Upgrade the backing implementation of the proxy and call a function\\n * on the new implementation.\\n * This is useful to initialize the proxied contract.\\n * @param newImplementation Address of the new implementation.\\n * @param data Data to send as msg.data in the low level call.\\n * It should include the signature and the parameters of the function to be called, as described in\\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\\n */\\n function upgradeToAndCall(address newImplementation, bytes calldata data)\\n external\\n payable\\n onlyGovernor\\n {\\n _upgradeTo(newImplementation);\\n (bool success, ) = newImplementation.delegatecall(data);\\n require(success);\\n }\\n\\n /**\\n * @dev Fallback function.\\n * Implemented entirely in `_fallback`.\\n */\\n fallback() external payable {\\n _fallback();\\n }\\n\\n /**\\n * @dev Delegates execution to an implementation contract.\\n * This is a low level function that doesn't return to its internal call site.\\n * It will return to the external caller whatever the implementation returns.\\n * @param _impl Address to delegate.\\n */\\n function _delegate(address _impl) internal {\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n // Copy msg.data. We take full control of memory in this inline assembly\\n // block because it will not return to Solidity code. We overwrite the\\n // Solidity scratch pad at memory position 0.\\n calldatacopy(0, 0, calldatasize())\\n\\n // Call the implementation.\\n // out and outsize are 0 because we don't know the size yet.\\n let result := delegatecall(gas(), _impl, 0, calldatasize(), 0, 0)\\n\\n // Copy the returned data.\\n returndatacopy(0, 0, returndatasize())\\n\\n switch result\\n // delegatecall returns 0 on error.\\n case 0 {\\n revert(0, returndatasize())\\n }\\n default {\\n return(0, returndatasize())\\n }\\n }\\n }\\n\\n /**\\n * @dev Function that is run as the first thing in the fallback function.\\n * Can be redefined in derived contracts to add functionality.\\n * Redefinitions must call super._willFallback().\\n */\\n function _willFallback() internal {}\\n\\n /**\\n * @dev fallback implementation.\\n * Extracted to enable manual triggering.\\n */\\n function _fallback() internal {\\n _willFallback();\\n _delegate(_implementation());\\n }\\n\\n /**\\n * @dev Storage slot with the address of the current implementation.\\n * This is the keccak-256 hash of \\\"eip1967.proxy.implementation\\\" subtracted by 1, and is\\n * validated in the constructor.\\n */\\n bytes32 internal constant IMPLEMENTATION_SLOT =\\n 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\\n\\n /**\\n * @dev Returns the current implementation.\\n * @return impl Address of the current implementation\\n */\\n function _implementation() internal view returns (address impl) {\\n bytes32 slot = IMPLEMENTATION_SLOT;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n impl := sload(slot)\\n }\\n }\\n\\n /**\\n * @dev Upgrades the proxy to a new implementation.\\n * @param newImplementation Address of the new implementation.\\n */\\n function _upgradeTo(address newImplementation) internal {\\n _setImplementation(newImplementation);\\n emit Upgraded(newImplementation);\\n }\\n\\n /**\\n * @dev Sets the implementation address of the proxy.\\n * @param newImplementation Address of the new implementation.\\n */\\n function _setImplementation(address newImplementation) internal {\\n require(\\n Address.isContract(newImplementation),\\n \\\"Cannot set a proxy implementation to a non-contract address\\\"\\n );\\n\\n bytes32 slot = IMPLEMENTATION_SLOT;\\n\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(slot, newImplementation)\\n }\\n }\\n}\\n\",\"keccak256\":\"0x3f46ae39dced6fa90d8b65aa31a0a331438544ec876e2ec961a8e6b22e2e06c7\",\"license\":\"BUSL-1.1\"},\"contracts/proxies/Proxies.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { InitializeGovernedUpgradeabilityProxy } from \\\"./InitializeGovernedUpgradeabilityProxy.sol\\\";\\n\\n/**\\n * @notice OUSDProxy delegates calls to an OUSD implementation\\n */\\ncontract OUSDProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice WrappedOUSDProxy delegates calls to a WrappedOUSD implementation\\n */\\ncontract WrappedOUSDProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice VaultProxy delegates calls to a Vault implementation\\n */\\ncontract VaultProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice CompoundStrategyProxy delegates calls to a CompoundStrategy implementation\\n */\\ncontract CompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice AaveStrategyProxy delegates calls to a AaveStrategy implementation\\n */\\ncontract AaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice ConvexStrategyProxy delegates calls to a ConvexStrategy implementation\\n */\\ncontract ConvexStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice HarvesterProxy delegates calls to a Harvester implementation\\n */\\ncontract HarvesterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice DripperProxy delegates calls to a Dripper implementation\\n */\\ncontract DripperProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice MorphoCompoundStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\\n */\\ncontract MorphoCompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice ConvexOUSDMetaStrategyProxy delegates calls to a ConvexOUSDMetaStrategy implementation\\n */\\ncontract ConvexOUSDMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice MorphoAaveStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\\n */\\ncontract MorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHProxy delegates calls to nowhere for now\\n */\\ncontract OETHProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice WOETHProxy delegates calls to nowhere for now\\n */\\ncontract WOETHProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHVaultProxy delegates calls to a Vault implementation\\n */\\ncontract OETHVaultProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHDripperProxy delegates calls to a OETHDripper implementation\\n */\\ncontract OETHDripperProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHHarvesterProxy delegates calls to a Harvester implementation\\n */\\ncontract OETHHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice CurveEthStrategyProxy delegates calls to a CurveEthStrategy implementation\\n */\\ncontract ConvexEthMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice BuybackProxy delegates calls to Buyback implementation\\n */\\ncontract BuybackProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHMorphoAaveStrategyProxy delegates calls to a MorphoAaveStrategy implementation\\n */\\ncontract OETHMorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHBalancerMetaPoolrEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\\n */\\ncontract OETHBalancerMetaPoolrEthStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice OETHBalancerMetaPoolwstEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\\n */\\ncontract OETHBalancerMetaPoolwstEthStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MakerDsrStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MakerDsrStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHBuybackProxy delegates calls to Buyback implementation\\n */\\ncontract OETHBuybackProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice BridgedWOETHProxy delegates calls to BridgedWOETH implementation\\n */\\ncontract BridgedWOETHProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice NativeStakingSSVStrategyProxy delegates calls to NativeStakingSSVStrategy implementation\\n */\\ncontract NativeStakingSSVStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingFeeAccumulatorProxy delegates calls to FeeAccumulator implementation\\n */\\ncontract NativeStakingFeeAccumulatorProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingSSVStrategy2Proxy delegates calls to NativeStakingSSVStrategy implementation\\n */\\ncontract NativeStakingSSVStrategy2Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingFeeAccumulator2Proxy delegates calls to FeeAccumulator implementation\\n */\\ncontract NativeStakingFeeAccumulator2Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingSSVStrategy3Proxy delegates calls to NativeStakingSSVStrategy implementation\\n */\\ncontract NativeStakingSSVStrategy3Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingFeeAccumulator3Proxy delegates calls to FeeAccumulator implementation\\n */\\ncontract NativeStakingFeeAccumulator3Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MetaMorphoStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MetaMorphoStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice ARMBuybackProxy delegates calls to Buyback implementation\\n */\\ncontract ARMBuybackProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice MorphoGauntletPrimeUSDCStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MorphoGauntletPrimeUSDCStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MorphoGauntletPrimeUSDTStrategyProxy delegates calls to a Generalized4626USDTStrategy implementation\\n */\\ncontract MorphoGauntletPrimeUSDTStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice CurvePoolBoosterProxy delegates calls to a CurvePoolBooster implementation\\n */\\ncontract CurvePoolBoosterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHFixedRateDripperProxy delegates calls to a OETHFixedRateDripper implementation\\n */\\ncontract OETHFixedRateDripperProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHSimpleHarvesterProxy delegates calls to a OETHSimpleHarvester implementation\\n */\\ncontract OETHSimpleHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice PoolBoostCentralRegistryProxy delegates calls to the PoolBoostCentralRegistry implementation\\n */\\ncontract PoolBoostCentralRegistryProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MakerSSRStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MakerSSRStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OUSDCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\\n */\\ncontract OUSDCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\\n */\\ncontract OETHCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice CompoundingStakingSSVStrategyProxy delegates calls to a CompoundingStakingSSVStrategy implementation\\n */\\ncontract CompoundingStakingSSVStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\",\"keccak256\":\"0x71e10cf279337e54682c7b0132825fa307992782367bd296c44b664b7b705db4\",\"license\":\"BUSL-1.1\"}},\"version\":1}", + "bytecode": "0x6080604052348015600f57600080fd5b50601733601b565b6081565b6001600160a01b038116603a6000805160206109f98339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a36000805160206109f983398151915255565b610969806100906000396000f3fe6080604052600436106100865760003560e01c80635d36b190116100595780635d36b1901461010a578063c7af33521461011f578063cf7a1d7714610144578063d38bfff414610157578063f851a4401461009057610086565b80630c340a24146100905780633659cfe6146100c25780634f1ef286146100e25780635c60da1b146100f5575b61008e610177565b005b34801561009c57600080fd5b506100a5610197565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100ce57600080fd5b5061008e6100dd366004610750565b6101b4565b61008e6100f03660046107bb565b6101ed565b34801561010157600080fd5b506100a561028a565b34801561011657600080fd5b5061008e6102a2565b34801561012b57600080fd5b50610134610346565b60405190151581526020016100b9565b61008e61015236600461080e565b610377565b34801561016357600080fd5b5061008e610172366004610750565b6104e0565b6101956101906000805160206108f48339815191525490565b610584565b565b60006101af6000805160206109148339815191525490565b905090565b6101bc610346565b6101e15760405162461bcd60e51b81526004016101d89061086f565b60405180910390fd5b6101ea816105a8565b50565b6101f5610346565b6102115760405162461bcd60e51b81526004016101d89061086f565b61021a836105a8565b6000836001600160a01b031683836040516102369291906108a6565b600060405180830381855af49150503d8060008114610271576040519150601f19603f3d011682016040523d82523d6000602084013e610276565b606091505b505090508061028457600080fd5b50505050565b60006101af6000805160206108f48339815191525490565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b03161461033d5760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b60648201526084016101d8565b610195336105e8565b600061035e6000805160206109148339815191525490565b6001600160a01b0316336001600160a01b031614905090565b61037f610346565b61039b5760405162461bcd60e51b81526004016101d89061086f565b60006103b36000805160206108f48339815191525490565b6001600160a01b0316146103c657600080fd5b6001600160a01b0384166104155760405162461bcd60e51b8152602060048201526016602482015275125b5c1b195b595b9d185d1a5bdb881b9bdd081cd95d60521b60448201526064016101d8565b61044060017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd6108b6565b6000805160206108f48339815191521461045c5761045c6108dd565b61046584610647565b80156104d7576000846001600160a01b031683836040516104879291906108a6565b600060405180830381855af49150503d80600081146104c2576040519150601f19603f3d011682016040523d82523d6000602084013e6104c7565b606091505b50509050806104d557600080fd5b505b610284836105e8565b6104e8610346565b6105045760405162461bcd60e51b81526004016101d89061086f565b61052c817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b031661054c6000805160206109148339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b3660008037600080366000845af43d6000803e8080156105a3573d6000f35b3d6000fd5b6105b181610647565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b03811661063e5760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f72206973206164647265737328302900000000000060448201526064016101d8565b6101ea816106cd565b803b6106bb5760405162461bcd60e51b815260206004820152603b60248201527f43616e6e6f742073657420612070726f787920696d706c656d656e746174696f60448201527f6e20746f2061206e6f6e2d636f6e74726163742061646472657373000000000060648201526084016101d8565b6000805160206108f483398151915255565b806001600160a01b03166106ed6000805160206109148339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a360008051602061091483398151915255565b80356001600160a01b038116811461074b57600080fd5b919050565b60006020828403121561076257600080fd5b61076b82610734565b9392505050565b60008083601f84011261078457600080fd5b50813567ffffffffffffffff81111561079c57600080fd5b6020830191508360208285010111156107b457600080fd5b9250929050565b6000806000604084860312156107d057600080fd5b6107d984610734565b9250602084013567ffffffffffffffff8111156107f557600080fd5b61080186828701610772565b9497909650939450505050565b6000806000806060858703121561082457600080fd5b61082d85610734565b935061083b60208601610734565b9250604085013567ffffffffffffffff81111561085757600080fd5b61086387828801610772565b95989497509550505050565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b8183823760009101908152919050565b818103818111156108d757634e487b7160e01b600052601160045260246000fd5b92915050565b634e487b7160e01b600052600160045260246000fdfe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa264697066735822122077accfaf2de140515db4147b246484fbabe3d80542a2fc028da5efc59180dc7464736f6c634300081c00337bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a", + "deployedBytecode": "0x6080604052600436106100865760003560e01c80635d36b190116100595780635d36b1901461010a578063c7af33521461011f578063cf7a1d7714610144578063d38bfff414610157578063f851a4401461009057610086565b80630c340a24146100905780633659cfe6146100c25780634f1ef286146100e25780635c60da1b146100f5575b61008e610177565b005b34801561009c57600080fd5b506100a5610197565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100ce57600080fd5b5061008e6100dd366004610750565b6101b4565b61008e6100f03660046107bb565b6101ed565b34801561010157600080fd5b506100a561028a565b34801561011657600080fd5b5061008e6102a2565b34801561012b57600080fd5b50610134610346565b60405190151581526020016100b9565b61008e61015236600461080e565b610377565b34801561016357600080fd5b5061008e610172366004610750565b6104e0565b6101956101906000805160206108f48339815191525490565b610584565b565b60006101af6000805160206109148339815191525490565b905090565b6101bc610346565b6101e15760405162461bcd60e51b81526004016101d89061086f565b60405180910390fd5b6101ea816105a8565b50565b6101f5610346565b6102115760405162461bcd60e51b81526004016101d89061086f565b61021a836105a8565b6000836001600160a01b031683836040516102369291906108a6565b600060405180830381855af49150503d8060008114610271576040519150601f19603f3d011682016040523d82523d6000602084013e610276565b606091505b505090508061028457600080fd5b50505050565b60006101af6000805160206108f48339815191525490565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b03161461033d5760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b60648201526084016101d8565b610195336105e8565b600061035e6000805160206109148339815191525490565b6001600160a01b0316336001600160a01b031614905090565b61037f610346565b61039b5760405162461bcd60e51b81526004016101d89061086f565b60006103b36000805160206108f48339815191525490565b6001600160a01b0316146103c657600080fd5b6001600160a01b0384166104155760405162461bcd60e51b8152602060048201526016602482015275125b5c1b195b595b9d185d1a5bdb881b9bdd081cd95d60521b60448201526064016101d8565b61044060017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd6108b6565b6000805160206108f48339815191521461045c5761045c6108dd565b61046584610647565b80156104d7576000846001600160a01b031683836040516104879291906108a6565b600060405180830381855af49150503d80600081146104c2576040519150601f19603f3d011682016040523d82523d6000602084013e6104c7565b606091505b50509050806104d557600080fd5b505b610284836105e8565b6104e8610346565b6105045760405162461bcd60e51b81526004016101d89061086f565b61052c817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b031661054c6000805160206109148339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b3660008037600080366000845af43d6000803e8080156105a3573d6000f35b3d6000fd5b6105b181610647565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b03811661063e5760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f72206973206164647265737328302900000000000060448201526064016101d8565b6101ea816106cd565b803b6106bb5760405162461bcd60e51b815260206004820152603b60248201527f43616e6e6f742073657420612070726f787920696d706c656d656e746174696f60448201527f6e20746f2061206e6f6e2d636f6e74726163742061646472657373000000000060648201526084016101d8565b6000805160206108f483398151915255565b806001600160a01b03166106ed6000805160206109148339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a360008051602061091483398151915255565b80356001600160a01b038116811461074b57600080fd5b919050565b60006020828403121561076257600080fd5b61076b82610734565b9392505050565b60008083601f84011261078457600080fd5b50813567ffffffffffffffff81111561079c57600080fd5b6020830191508360208285010111156107b457600080fd5b9250929050565b6000806000604084860312156107d057600080fd5b6107d984610734565b9250602084013567ffffffffffffffff8111156107f557600080fd5b61080186828701610772565b9497909650939450505050565b6000806000806060858703121561082457600080fd5b61082d85610734565b935061083b60208601610734565b9250604085013567ffffffffffffffff81111561085757600080fd5b61086387828801610772565b95989497509550505050565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b8183823760009101908152919050565b818103818111156108d757634e487b7160e01b600052601160045260246000fd5b92915050565b634e487b7160e01b600052600160045260246000fdfe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa264697066735822122077accfaf2de140515db4147b246484fbabe3d80542a2fc028da5efc59180dc7464736f6c634300081c0033", + "libraries": {}, + "devdoc": { + "events": { + "Upgraded(address)": { + "details": "Emitted when the implementation is upgraded.", + "params": { + "implementation": "Address of the new implementation." + } + } + }, + "kind": "dev", + "methods": { + "admin()": { + "returns": { + "_0": "The address of the proxy admin/it's also the governor." + } + }, + "implementation()": { + "returns": { + "_0": "The address of the implementation." + } + }, + "initialize(address,address,bytes)": { + "details": "Contract initializer with Governor enforcement", + "params": { + "_data": "Data to send as msg.data to the implementation to initialize the proxied contract. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.", + "_initGovernor": "Address of the initial Governor.", + "_logic": "Address of the initial implementation." + } + }, + "transferGovernance(address)": { + "params": { + "_newGovernor": "Address of the new Governor" + } + }, + "upgradeTo(address)": { + "details": "Upgrade the backing implementation of the proxy. Only the admin can call this function.", + "params": { + "_newImplementation": "Address of the new implementation." + } + }, + "upgradeToAndCall(address,bytes)": { + "details": "Upgrade the backing implementation of the proxy and call a function on the new implementation. This is useful to initialize the proxied contract.", + "params": { + "data": "Data to send as msg.data in the low level call. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.", + "newImplementation": "Address of the new implementation." + } + } + }, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "claimGovernance()": { + "notice": "Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor." + }, + "governor()": { + "notice": "Returns the address of the current Governor." + }, + "isGovernor()": { + "notice": "Returns true if the caller is the current Governor." + }, + "transferGovernance(address)": { + "notice": "Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete" + } + }, + "notice": "OETHProxy delegates calls to nowhere for now", + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/OETHVault.json b/contracts/deployments/hoodi/OETHVault.json new file mode 100644 index 0000000000..1467aa63ca --- /dev/null +++ b/contracts/deployments/hoodi/OETHVault.json @@ -0,0 +1,2563 @@ +{ + "address": "0x7d0810eF5fc224EfcD6347d4F7A640b2D3928054", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_threshold", + "type": "uint256" + } + ], + "name": "AllocateThresholdUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_strategy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "AssetAllocated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_strategy", + "type": "address" + } + ], + "name": "AssetDefaultStrategyUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_asset", + "type": "address" + } + ], + "name": "AssetRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_asset", + "type": "address" + } + ], + "name": "AssetSupported", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CapitalPaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CapitalUnpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "dripDuration", + "type": "uint256" + } + ], + "name": "DripDurationChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_dripper", + "type": "address" + } + ], + "name": "DripperChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "GovernorshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "maxSupplyDiff", + "type": "uint256" + } + ], + "name": "MaxSupplyDiffChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_addr", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_threshold", + "type": "uint256" + } + ], + "name": "NetOusdMintForStrategyThresholdChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_ousdMetaStrategy", + "type": "address" + } + ], + "name": "OusdMetaStrategyUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "PendingGovernorshipTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_priceProvider", + "type": "address" + } + ], + "name": "PriceProviderUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "RebasePaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "rebaseRatePerSecond", + "type": "uint256" + } + ], + "name": "RebasePerSecondMaxChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_threshold", + "type": "uint256" + } + ], + "name": "RebaseThresholdUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "RebaseUnpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_addr", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "Redeem", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_redeemFeeBps", + "type": "uint256" + } + ], + "name": "RedeemFeeUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "StrategistUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "strategy", + "type": "address" + } + ], + "name": "StrategyAddedToMintWhitelist", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_addr", + "type": "address" + } + ], + "name": "StrategyApproved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_addr", + "type": "address" + } + ], + "name": "StrategyRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "strategy", + "type": "address" + } + ], + "name": "StrategyRemovedFromMintWhitelist", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_basis", + "type": "uint256" + } + ], + "name": "SwapAllowedUndervalueChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_basis", + "type": "uint256" + } + ], + "name": "SwapSlippageChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_fromAsset", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_toAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_fromAssetAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_toAssetAmount", + "type": "uint256" + } + ], + "name": "Swapped", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "SwapperChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "TrusteeAddressChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_basis", + "type": "uint256" + } + ], + "name": "TrusteeFeeBpsChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_vaultBuffer", + "type": "uint256" + } + ], + "name": "VaultBufferUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_newDelay", + "type": "uint256" + } + ], + "name": "WithdrawalClaimDelayUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_claimable", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_newClaimable", + "type": "uint256" + } + ], + "name": "WithdrawalClaimable", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_withdrawer", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "_requestId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "WithdrawalClaimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_withdrawer", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "_requestId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_queued", + "type": "uint256" + } + ], + "name": "WithdrawalRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_yield", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_fee", + "type": "uint256" + } + ], + "name": "YieldDistribution", + "type": "event" + }, + { + "inputs": [], + "name": "adminImplPosition", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "allowedSwapUndervalue", + "outputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_addr", + "type": "address" + } + ], + "name": "approveStrategy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "assetDefaultStrategies", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "autoAllocateThreshold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + } + ], + "name": "cacheDecimals", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "capitalPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "claimGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_strategyToAddress", + "type": "address" + }, + { + "internalType": "address[]", + "name": "_assets", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "_amounts", + "type": "uint256[]" + } + ], + "name": "depositToStrategy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "dripDuration", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "dripper", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "governor", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_priceProvider", + "type": "address" + }, + { + "internalType": "address", + "name": "_oToken", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isGovernor", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isMintWhitelistedStrategy", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastRebase", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxSupplyDiff", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "netOusdMintForStrategyThreshold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "netOusdMintedForStrategy", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "oUSD", + "outputs": [ + { + "internalType": "contract OUSD", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ousdMetaStrategy", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pauseCapital", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "pauseRebase", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "priceProvider", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rebasePaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rebasePerSecondMax", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rebasePerSecondTarget", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rebaseThreshold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "redeemFeeBps", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + } + ], + "name": "removeAsset", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_addr", + "type": "address" + } + ], + "name": "removeStrategy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImpl", + "type": "address" + } + ], + "name": "setAdminImpl", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "internalType": "address", + "name": "_strategy", + "type": "address" + } + ], + "name": "setAssetDefaultStrategy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_threshold", + "type": "uint256" + } + ], + "name": "setAutoAllocateThreshold", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_dripDuration", + "type": "uint256" + } + ], + "name": "setDripDuration", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_dripper", + "type": "address" + } + ], + "name": "setDripper", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_maxSupplyDiff", + "type": "uint256" + } + ], + "name": "setMaxSupplyDiff", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_threshold", + "type": "uint256" + } + ], + "name": "setNetOusdMintForStrategyThreshold", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "internalType": "uint16", + "name": "_allowedOracleSlippageBps", + "type": "uint16" + } + ], + "name": "setOracleSlippage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_ousdMetaStrategy", + "type": "address" + } + ], + "name": "setOusdMetaStrategy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_priceProvider", + "type": "address" + } + ], + "name": "setPriceProvider", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "yearlyApr", + "type": "uint256" + } + ], + "name": "setRebaseRateMax", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_threshold", + "type": "uint256" + } + ], + "name": "setRebaseThreshold", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_redeemFeeBps", + "type": "uint256" + } + ], + "name": "setRedeemFeeBps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "setStrategistAddr", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "_basis", + "type": "uint16" + } + ], + "name": "setSwapAllowedUndervalue", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_swapperAddr", + "type": "address" + } + ], + "name": "setSwapper", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "setTrusteeAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_basis", + "type": "uint256" + } + ], + "name": "setTrusteeFeeBps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_vaultBuffer", + "type": "uint256" + } + ], + "name": "setVaultBuffer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_delay", + "type": "uint256" + } + ], + "name": "setWithdrawalClaimDelay", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "strategies", + "outputs": [ + { + "internalType": "bool", + "name": "isSupported", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "_deprecated", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "strategistAddr", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "internalType": "uint8", + "name": "_unitConversion", + "type": "uint8" + } + ], + "name": "supportAsset", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_fromAsset", + "type": "address" + }, + { + "internalType": "address", + "name": "_toAsset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_fromAssetAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minToAssetAmount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "swapCollateral", + "outputs": [ + { + "internalType": "uint256", + "name": "toAssetAmount", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "swapper", + "outputs": [ + { + "internalType": "address", + "name": "swapper_", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newGovernor", + "type": "address" + } + ], + "name": "transferGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "transferToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "trusteeAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "trusteeFeeBps", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "unpauseCapital", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpauseRebase", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "vaultBuffer", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawAllFromStrategies", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_strategyAddr", + "type": "address" + } + ], + "name": "withdrawAllFromStrategy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_strategyFromAddress", + "type": "address" + }, + { + "internalType": "address[]", + "name": "_assets", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "_amounts", + "type": "uint256[]" + } + ], + "name": "withdrawFromStrategy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawalClaimDelay", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawalQueueMetadata", + "outputs": [ + { + "internalType": "uint128", + "name": "queued", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "claimable", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "claimed", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "nextWithdrawalIndex", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "withdrawalRequests", + "outputs": [ + { + "internalType": "address", + "name": "withdrawer", + "type": "address" + }, + { + "internalType": "bool", + "name": "claimed", + "type": "bool" + }, + { + "internalType": "uint40", + "name": "timestamp", + "type": "uint40" + }, + { + "internalType": "uint128", + "name": "amount", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "queued", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "transactionHash": "0xfd481cc4910bc1fcddf354bd360275092f9f4152ef8a57dfe2dee2e919acf07c", + "receipt": { + "to": null, + "from": "0xFD9E6005187F448957a0972a7d0C0A6dA2911236", + "contractAddress": "0x7d0810eF5fc224EfcD6347d4F7A640b2D3928054", + "transactionIndex": 4, + "gasUsed": "3775999", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0xff0a41ca498220037e1997135e1c0e80763b0daae684c7436cc12f40bf25938d", + "transactionHash": "0xfd481cc4910bc1fcddf354bd360275092f9f4152ef8a57dfe2dee2e919acf07c", + "logs": [], + "blockNumber": 828231, + "cumulativeGasUsed": "3859999", + "status": 1, + "byzantium": true + }, + "args": [], + "numDeployments": 1, + "solcInputHash": "5518586a577179807341642341c56a4f", + "metadata": "{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_threshold\",\"type\":\"uint256\"}],\"name\":\"AllocateThresholdUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_strategy\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"AssetAllocated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_strategy\",\"type\":\"address\"}],\"name\":\"AssetDefaultStrategyUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"}],\"name\":\"AssetRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"}],\"name\":\"AssetSupported\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"CapitalPaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"CapitalUnpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"dripDuration\",\"type\":\"uint256\"}],\"name\":\"DripDurationChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_dripper\",\"type\":\"address\"}],\"name\":\"DripperChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"GovernorshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"maxSupplyDiff\",\"type\":\"uint256\"}],\"name\":\"MaxSupplyDiffChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Mint\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_threshold\",\"type\":\"uint256\"}],\"name\":\"NetOusdMintForStrategyThresholdChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_ousdMetaStrategy\",\"type\":\"address\"}],\"name\":\"OusdMetaStrategyUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"PendingGovernorshipTransfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_priceProvider\",\"type\":\"address\"}],\"name\":\"PriceProviderUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"RebasePaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"rebaseRatePerSecond\",\"type\":\"uint256\"}],\"name\":\"RebasePerSecondMaxChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_threshold\",\"type\":\"uint256\"}],\"name\":\"RebaseThresholdUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"RebaseUnpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Redeem\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_redeemFeeBps\",\"type\":\"uint256\"}],\"name\":\"RedeemFeeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_address\",\"type\":\"address\"}],\"name\":\"StrategistUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"strategy\",\"type\":\"address\"}],\"name\":\"StrategyAddedToMintWhitelist\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"StrategyApproved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"StrategyRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"strategy\",\"type\":\"address\"}],\"name\":\"StrategyRemovedFromMintWhitelist\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_basis\",\"type\":\"uint256\"}],\"name\":\"SwapAllowedUndervalueChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_basis\",\"type\":\"uint256\"}],\"name\":\"SwapSlippageChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_fromAsset\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_toAsset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_fromAssetAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_toAssetAmount\",\"type\":\"uint256\"}],\"name\":\"Swapped\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_address\",\"type\":\"address\"}],\"name\":\"SwapperChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_address\",\"type\":\"address\"}],\"name\":\"TrusteeAddressChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_basis\",\"type\":\"uint256\"}],\"name\":\"TrusteeFeeBpsChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_vaultBuffer\",\"type\":\"uint256\"}],\"name\":\"VaultBufferUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_newDelay\",\"type\":\"uint256\"}],\"name\":\"WithdrawalClaimDelayUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_claimable\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_newClaimable\",\"type\":\"uint256\"}],\"name\":\"WithdrawalClaimable\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_withdrawer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"WithdrawalClaimed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_withdrawer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_queued\",\"type\":\"uint256\"}],\"name\":\"WithdrawalRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_yield\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_fee\",\"type\":\"uint256\"}],\"name\":\"YieldDistribution\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"adminImplPosition\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"allowedSwapUndervalue\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"approveStrategy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"assetDefaultStrategies\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"autoAllocateThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"}],\"name\":\"cacheDecimals\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"capitalPaused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_strategyToAddress\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"_assets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"_amounts\",\"type\":\"uint256[]\"}],\"name\":\"depositToStrategy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dripDuration\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dripper\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"governor\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_priceProvider\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_oToken\",\"type\":\"address\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isGovernor\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"isMintWhitelistedStrategy\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastRebase\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"maxSupplyDiff\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"netOusdMintForStrategyThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"netOusdMintedForStrategy\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"oUSD\",\"outputs\":[{\"internalType\":\"contract OUSD\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"ousdMetaStrategy\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pauseCapital\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pauseRebase\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"priceProvider\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rebasePaused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rebasePerSecondMax\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rebasePerSecondTarget\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rebaseThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"redeemFeeBps\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"}],\"name\":\"removeAsset\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"removeStrategy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImpl\",\"type\":\"address\"}],\"name\":\"setAdminImpl\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_strategy\",\"type\":\"address\"}],\"name\":\"setAssetDefaultStrategy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_threshold\",\"type\":\"uint256\"}],\"name\":\"setAutoAllocateThreshold\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_dripDuration\",\"type\":\"uint256\"}],\"name\":\"setDripDuration\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_dripper\",\"type\":\"address\"}],\"name\":\"setDripper\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_maxSupplyDiff\",\"type\":\"uint256\"}],\"name\":\"setMaxSupplyDiff\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_threshold\",\"type\":\"uint256\"}],\"name\":\"setNetOusdMintForStrategyThreshold\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"_allowedOracleSlippageBps\",\"type\":\"uint16\"}],\"name\":\"setOracleSlippage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_ousdMetaStrategy\",\"type\":\"address\"}],\"name\":\"setOusdMetaStrategy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_priceProvider\",\"type\":\"address\"}],\"name\":\"setPriceProvider\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"yearlyApr\",\"type\":\"uint256\"}],\"name\":\"setRebaseRateMax\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_threshold\",\"type\":\"uint256\"}],\"name\":\"setRebaseThreshold\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_redeemFeeBps\",\"type\":\"uint256\"}],\"name\":\"setRedeemFeeBps\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_address\",\"type\":\"address\"}],\"name\":\"setStrategistAddr\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"_basis\",\"type\":\"uint16\"}],\"name\":\"setSwapAllowedUndervalue\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_swapperAddr\",\"type\":\"address\"}],\"name\":\"setSwapper\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_address\",\"type\":\"address\"}],\"name\":\"setTrusteeAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_basis\",\"type\":\"uint256\"}],\"name\":\"setTrusteeFeeBps\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_vaultBuffer\",\"type\":\"uint256\"}],\"name\":\"setVaultBuffer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_delay\",\"type\":\"uint256\"}],\"name\":\"setWithdrawalClaimDelay\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"strategies\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"isSupported\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"_deprecated\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"strategistAddr\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"_unitConversion\",\"type\":\"uint8\"}],\"name\":\"supportAsset\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_fromAsset\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_toAsset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_fromAssetAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_minToAssetAmount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"swapCollateral\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"toAssetAmount\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"swapper\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"swapper_\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newGovernor\",\"type\":\"address\"}],\"name\":\"transferGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"transferToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"trusteeAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"trusteeFeeBps\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpauseCapital\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpauseRebase\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"vaultBuffer\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawAllFromStrategies\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_strategyAddr\",\"type\":\"address\"}],\"name\":\"withdrawAllFromStrategy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_strategyFromAddress\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"_assets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"_amounts\",\"type\":\"uint256[]\"}],\"name\":\"withdrawFromStrategy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawalClaimDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawalQueueMetadata\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"queued\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"claimable\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"claimed\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"nextWithdrawalIndex\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"withdrawalRequests\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"withdrawer\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"claimed\",\"type\":\"bool\"},{\"internalType\":\"uint40\",\"name\":\"timestamp\",\"type\":\"uint40\"},{\"internalType\":\"uint128\",\"name\":\"amount\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"queued\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"author\":\"Origin Protocol Inc\",\"kind\":\"dev\",\"methods\":{\"allowedSwapUndervalue()\":{\"returns\":{\"value\":\"Percentage in basis points.\"}},\"approveStrategy(address)\":{\"params\":{\"_addr\":\"Address of the strategy to add\"}},\"cacheDecimals(address)\":{\"params\":{\"_asset\":\"Address of asset token\"}},\"depositToStrategy(address,address[],uint256[])\":{\"params\":{\"_amounts\":\"Array of amounts of each corresponding asset to deposit.\",\"_assets\":\"Array of asset address that will be deposited into the strategy.\",\"_strategyToAddress\":\"Address of the Strategy to deposit assets into.\"}},\"removeAsset(address)\":{\"params\":{\"_asset\":\"Address of asset\"}},\"removeStrategy(address)\":{\"params\":{\"_addr\":\"Address of the strategy to remove\"}},\"setAdminImpl(address)\":{\"params\":{\"newImpl\":\"address of the implementation\"}},\"setAssetDefaultStrategy(address,address)\":{\"params\":{\"_asset\":\"Address of the asset\",\"_strategy\":\"Address of the Strategy\"}},\"setAutoAllocateThreshold(uint256)\":{\"params\":{\"_threshold\":\"OToken amount with 18 fixed decimals.\"}},\"setDripDuration(uint256)\":{\"params\":{\"_dripDuration\":\"Time in seconds to target a constant yield rate\"}},\"setDripper(address)\":{\"params\":{\"_dripper\":\"Address of the Dripper contract.\"}},\"setNetOusdMintForStrategyThreshold(uint256)\":{\"params\":{\"_threshold\":\"OToken amount with 18 fixed decimals.\"}},\"setOracleSlippage(address,uint16)\":{\"params\":{\"_allowedOracleSlippageBps\":\"allowed slippage from Oracle in basis points. eg 20 = 0.2%. Max 10%.\",\"_asset\":\"Address of the asset token.\"}},\"setOusdMetaStrategy(address)\":{\"params\":{\"_ousdMetaStrategy\":\"Address of OToken metapool strategy\"}},\"setPriceProvider(address)\":{\"params\":{\"_priceProvider\":\"Address of price provider\"}},\"setRebaseRateMax(uint256)\":{\"params\":{\"yearlyApr\":\"in 1e18 notation. 3 * 1e18 = 3% APR\"}},\"setRebaseThreshold(uint256)\":{\"params\":{\"_threshold\":\"OToken amount with 18 fixed decimals.\"}},\"setRedeemFeeBps(uint256)\":{\"params\":{\"_redeemFeeBps\":\"Basis point fee to be charged\"}},\"setStrategistAddr(address)\":{\"params\":{\"_address\":\"Address of Strategist\"}},\"setSwapAllowedUndervalue(uint16)\":{\"params\":{\"_basis\":\"Percentage in basis points. eg 100 == 1%\"}},\"setSwapper(address)\":{\"params\":{\"_swapperAddr\":\"Address of the Swapper contract that implements the ISwapper interface.\"}},\"setVaultBuffer(uint256)\":{\"params\":{\"_vaultBuffer\":\"Percentage using 18 decimals. 100% = 1e18.\"}},\"setWithdrawalClaimDelay(uint256)\":{\"params\":{\"_delay\":\"Delay period (should be between 10 mins to 7 days). Set to 0 to disable async withdrawals\"}},\"supportAsset(address,uint8)\":{\"params\":{\"_asset\":\"Address of asset\"}},\"swapCollateral(address,address,uint256,uint256,bytes)\":{\"params\":{\"_data\":\"implementation specific data. eg 1Inch swap data\",\"_fromAsset\":\"The token address of the asset being sold by the vault.\",\"_fromAssetAmount\":\"The amount of assets being sold by the vault.\",\"_minToAssetAmount\":\"The minimum amount of assets to be purchased.\",\"_toAsset\":\"The token address of the asset being purchased by the vault.\"},\"returns\":{\"toAssetAmount\":\"The amount of toAssets that was received from the swap\"}},\"transferGovernance(address)\":{\"params\":{\"_newGovernor\":\"Address of the new Governor\"}},\"transferToken(address,uint256)\":{\"params\":{\"_amount\":\"Amount of the asset to transfer\",\"_asset\":\"Address for the asset\"}},\"withdrawAllFromStrategy(address)\":{\"params\":{\"_strategyAddr\":\"Strategy address.\"}},\"withdrawFromStrategy(address,address[],uint256[])\":{\"params\":{\"_amounts\":\"Array of amounts of each corresponding asset to withdraw.\",\"_assets\":\"Array of asset address that will be withdrawn from the strategy.\",\"_strategyFromAddress\":\"Address of the Strategy to withdraw assets from.\"}}},\"title\":\"OETH Vault Contract\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"allowedSwapUndervalue()\":{\"notice\":\"Max allowed percentage the vault total value can drop below the OToken total supply in basis points when executing a collateral swap. For example 100 == 1%\"},\"approveStrategy(address)\":{\"notice\":\"Add a strategy to the Vault.\"},\"assetDefaultStrategies(address)\":{\"notice\":\"Mapping of asset address to the Strategy that they should automatically\"},\"autoAllocateThreshold()\":{\"notice\":\"OToken mints over this amount automatically allocate funds. 18 decimals.\"},\"cacheDecimals(address)\":{\"notice\":\"Cache decimals on OracleRouter for a particular asset. This action is required before that asset's price can be accessed.\"},\"capitalPaused()\":{\"notice\":\"pause operations that change the OToken supply. eg mint, redeem, allocate, mint/burn for strategy\"},\"claimGovernance()\":{\"notice\":\"Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor.\"},\"depositToStrategy(address,address[],uint256[])\":{\"notice\":\"Deposit multiple assets from the vault into the strategy.\"},\"dripDuration()\":{\"notice\":\"Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\"},\"dripper()\":{\"notice\":\"Address of the Dripper contract that streams harvested rewards to the Vault\"},\"governor()\":{\"notice\":\"Returns the address of the current Governor.\"},\"isGovernor()\":{\"notice\":\"Returns true if the caller is the current Governor.\"},\"lastRebase()\":{\"notice\":\"Time in seconds that the vault last rebased yield.\"},\"maxSupplyDiff()\":{\"notice\":\"Max difference between total supply and total value of assets. 18 decimals.\"},\"netOusdMintForStrategyThreshold()\":{\"notice\":\"How much net total OTokens are allowed to be minted by all strategies\"},\"netOusdMintedForStrategy()\":{\"notice\":\"How much OTokens are currently minted by the strategy\"},\"ousdMetaStrategy()\":{\"notice\":\"Metapool strategy that is allowed to mint/burn OTokens without changing collateral\"},\"pauseCapital()\":{\"notice\":\"Set the deposit paused flag to true to prevent capital movement.\"},\"pauseRebase()\":{\"notice\":\"Set the deposit paused flag to true to prevent rebasing.\"},\"priceProvider()\":{\"notice\":\"Address of the Oracle price provider contract\"},\"rebasePaused()\":{\"notice\":\"pause rebasing if true\"},\"rebasePerSecondMax()\":{\"notice\":\"max rebase percentage per second Can be used to set maximum yield of the protocol, spreading out yield over time\"},\"rebasePerSecondTarget()\":{\"notice\":\"target rebase rate limit, based on past rates and funds available.\"},\"rebaseThreshold()\":{\"notice\":\"OToken mints over this amount automatically rebase. 18 decimals.\"},\"redeemFeeBps()\":{\"notice\":\"Redemption fee in basis points. eg 50 = 0.5%\"},\"removeAsset(address)\":{\"notice\":\"Remove a supported asset from the Vault\"},\"removeStrategy(address)\":{\"notice\":\"Remove a strategy from the Vault.\"},\"setAdminImpl(address)\":{\"notice\":\"set the implementation for the admin, this needs to be in a base class else we cannot set it\"},\"setAssetDefaultStrategy(address,address)\":{\"notice\":\"Set the default Strategy for an asset, i.e. the one which the asset will be automatically allocated to and withdrawn from\"},\"setAutoAllocateThreshold(uint256)\":{\"notice\":\"Sets the minimum amount of OTokens in a mint to trigger an automatic allocation of funds afterwords.\"},\"setDripDuration(uint256)\":{\"notice\":\"Set the drip duration period\"},\"setDripper(address)\":{\"notice\":\"Set the Dripper contract that streams harvested rewards to the vault.\"},\"setMaxSupplyDiff(uint256)\":{\"notice\":\"Sets the maximum allowable difference between total supply and backing assets' value.\"},\"setNetOusdMintForStrategyThreshold(uint256)\":{\"notice\":\"Set maximum amount of OTokens that can at any point be minted and deployed to strategy (used only by ConvexOUSDMetaStrategy for now).\"},\"setOracleSlippage(address,uint16)\":{\"notice\":\"Set the allowed slippage from the Oracle price for collateral asset swaps.\"},\"setOusdMetaStrategy(address)\":{\"notice\":\"Set OToken Metapool strategy\"},\"setPriceProvider(address)\":{\"notice\":\"Set address of price provider.\"},\"setRebaseRateMax(uint256)\":{\"notice\":\"Set a yield streaming max rate. This spreads yield over time if it is above the max rate.\"},\"setRebaseThreshold(uint256)\":{\"notice\":\"Set a minimum amount of OTokens in a mint or redeem that triggers a rebase\"},\"setRedeemFeeBps(uint256)\":{\"notice\":\"Set a fee in basis points to be charged for a redeem.\"},\"setStrategistAddr(address)\":{\"notice\":\"Set address of Strategist\"},\"setSwapAllowedUndervalue(uint16)\":{\"notice\":\"Set max allowed percentage the vault total value can drop below the OToken total supply in basis points when executing collateral swaps.\"},\"setSwapper(address)\":{\"notice\":\"Set the contract the performs swaps of collateral assets.\"},\"setTrusteeAddress(address)\":{\"notice\":\"Sets the trusteeAddress that can receive a portion of yield. Setting to the zero address disables this feature.\"},\"setTrusteeFeeBps(uint256)\":{\"notice\":\"Sets the TrusteeFeeBps to the percentage of yield that should be received in basis points.\"},\"setVaultBuffer(uint256)\":{\"notice\":\"Set a buffer of assets to keep in the Vault to handle most redemptions without needing to spend gas unwinding assets from a Strategy.\"},\"setWithdrawalClaimDelay(uint256)\":{\"notice\":\"Changes the async withdrawal claim period for OETH & superOETHb\"},\"strategistAddr()\":{\"notice\":\"Address of the Strategist\"},\"supportAsset(address,uint8)\":{\"notice\":\"Add a supported asset to the contract, i.e. one that can be to mint OTokens.\"},\"swapCollateral(address,address,uint256,uint256,bytes)\":{\"notice\":\"Strategist swaps collateral assets sitting in the vault.\"},\"swapper()\":{\"notice\":\"Contract that swaps the vault's collateral assets\"},\"transferGovernance(address)\":{\"notice\":\"Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete\"},\"transferToken(address,uint256)\":{\"notice\":\"Transfer token to governor. Intended for recovering tokens stuck in contract, i.e. mistaken sends.\"},\"trusteeAddress()\":{\"notice\":\"Trustee contract that can collect a percentage of yield\"},\"trusteeFeeBps()\":{\"notice\":\"Amount of yield collected in basis points. eg 2000 = 20%\"},\"unpauseCapital()\":{\"notice\":\"Set the deposit paused flag to false to enable capital movement.\"},\"unpauseRebase()\":{\"notice\":\"Set the deposit paused flag to true to allow rebasing.\"},\"vaultBuffer()\":{\"notice\":\"Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\"},\"withdrawAllFromStrategies()\":{\"notice\":\"Withdraws all assets from all the strategies and sends assets to the Vault.\"},\"withdrawAllFromStrategy(address)\":{\"notice\":\"Withdraws all assets from the strategy and sends assets to the Vault.\"},\"withdrawFromStrategy(address,address[],uint256[])\":{\"notice\":\"Withdraw multiple assets from the strategy to the vault.\"},\"withdrawalClaimDelay()\":{\"notice\":\"Sets a minimum delay that is required to elapse between requesting async withdrawals and claiming the request. When set to 0 async withdrawals are disabled.\"},\"withdrawalQueueMetadata()\":{\"notice\":\"Global metadata for the withdrawal queue including: queued - cumulative total of all withdrawal requests included the ones that have already been claimed claimable - cumulative total of all the requests that can be claimed including the ones already claimed claimed - total of all the requests that have been claimed nextWithdrawalIndex - index of the next withdrawal request starting at 0\"},\"withdrawalRequests(uint256)\":{\"notice\":\"Mapping of withdrawal request indices to the user withdrawal request data\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/vault/OETHVault.sol\":\"OETHVault\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/token/ERC20/IERC20.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Interface of the ERC20 standard as defined in the EIP.\\n */\\ninterface IERC20 {\\n /**\\n * @dev Returns the amount of tokens in existence.\\n */\\n function totalSupply() external view returns (uint256);\\n\\n /**\\n * @dev Returns the amount of tokens owned by `account`.\\n */\\n function balanceOf(address account) external view returns (uint256);\\n\\n /**\\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transfer(address recipient, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Returns the remaining number of tokens that `spender` will be\\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\\n * zero by default.\\n *\\n * This value changes when {approve} or {transferFrom} are called.\\n */\\n function allowance(address owner, address spender) external view returns (uint256);\\n\\n /**\\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\\n * that someone may use both the old and the new allowance by unfortunate\\n * transaction ordering. One possible solution to mitigate this race\\n * condition is to first reduce the spender's allowance to 0 and set the\\n * desired value afterwards:\\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\\n *\\n * Emits an {Approval} event.\\n */\\n function approve(address spender, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\\n * allowance mechanism. `amount` is then deducted from the caller's\\n * allowance.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transferFrom(\\n address sender,\\n address recipient,\\n uint256 amount\\n ) external returns (bool);\\n\\n /**\\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\\n * another (`to`).\\n *\\n * Note that `value` may be zero.\\n */\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n\\n /**\\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n * a call to {approve}. `value` is the new allowance.\\n */\\n event Approval(address indexed owner, address indexed spender, uint256 value);\\n}\\n\",\"keccak256\":\"0x61437cb513a887a1bbad006e7b1c8b414478427d33de47c5600af3c748f108da\",\"license\":\"MIT\"},\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../IERC20.sol\\\";\\nimport \\\"../../../utils/Address.sol\\\";\\n\\n/**\\n * @title SafeERC20\\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\\n * contract returns false). Tokens that return no value (and instead revert or\\n * throw on failure) are also supported, non-reverting calls are assumed to be\\n * successful.\\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\\n */\\nlibrary SafeERC20 {\\n using Address for address;\\n\\n function safeTransfer(\\n IERC20 token,\\n address to,\\n uint256 value\\n ) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\\n }\\n\\n function safeTransferFrom(\\n IERC20 token,\\n address from,\\n address to,\\n uint256 value\\n ) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\\n }\\n\\n /**\\n * @dev Deprecated. This function has issues similar to the ones found in\\n * {IERC20-approve}, and its usage is discouraged.\\n *\\n * Whenever possible, use {safeIncreaseAllowance} and\\n * {safeDecreaseAllowance} instead.\\n */\\n function safeApprove(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n // safeApprove should only be called when setting an initial allowance,\\n // or when resetting it to zero. To increase and decrease it, use\\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\\n require(\\n (value == 0) || (token.allowance(address(this), spender) == 0),\\n \\\"SafeERC20: approve from non-zero to non-zero allowance\\\"\\n );\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\\n }\\n\\n function safeIncreaseAllowance(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n uint256 newAllowance = token.allowance(address(this), spender) + value;\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\\n }\\n\\n function safeDecreaseAllowance(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n unchecked {\\n uint256 oldAllowance = token.allowance(address(this), spender);\\n require(oldAllowance >= value, \\\"SafeERC20: decreased allowance below zero\\\");\\n uint256 newAllowance = oldAllowance - value;\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\\n }\\n }\\n\\n /**\\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\\n * on the return value: the return value is optional (but if data is returned, it must not be false).\\n * @param token The token targeted by the call.\\n * @param data The call data (encoded using abi.encode or one of its variants).\\n */\\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\\n // the target address contains contract code and also asserts for success in the low-level call.\\n\\n bytes memory returndata = address(token).functionCall(data, \\\"SafeERC20: low-level call failed\\\");\\n if (returndata.length > 0) {\\n // Return data is optional\\n require(abi.decode(returndata, (bool)), \\\"SafeERC20: ERC20 operation did not succeed\\\");\\n }\\n }\\n}\\n\",\"keccak256\":\"0xc3d946432c0ddbb1f846a0d3985be71299df331b91d06732152117f62f0be2b5\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize, which returns 0 for contracts in\\n // construction, since the code is only stored at the end of the\\n // constructor execution.\\n\\n uint256 size;\\n assembly {\\n size := extcodesize(account)\\n }\\n return size > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCall(target, data, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n require(isContract(target), \\\"Address: static call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(isContract(target), \\\"Address: delegate call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x51b758a8815ecc9596c66c37d56b1d33883a444631a3f916b9fe65cb863ef7c4\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/math/SafeCast.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\\n * checks.\\n *\\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\\n * easily result in undesired exploitation or bugs, since developers usually\\n * assume that overflows raise errors. `SafeCast` restores this intuition by\\n * reverting the transaction when such an operation overflows.\\n *\\n * Using this library instead of the unchecked operations eliminates an entire\\n * class of bugs, so it's recommended to use it always.\\n *\\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\\n * all math on `uint256` and `int256` and then downcasting.\\n */\\nlibrary SafeCast {\\n /**\\n * @dev Returns the downcasted uint224 from uint256, reverting on\\n * overflow (when the input is greater than largest uint224).\\n *\\n * Counterpart to Solidity's `uint224` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 224 bits\\n */\\n function toUint224(uint256 value) internal pure returns (uint224) {\\n require(value <= type(uint224).max, \\\"SafeCast: value doesn't fit in 224 bits\\\");\\n return uint224(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint128 from uint256, reverting on\\n * overflow (when the input is greater than largest uint128).\\n *\\n * Counterpart to Solidity's `uint128` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 128 bits\\n */\\n function toUint128(uint256 value) internal pure returns (uint128) {\\n require(value <= type(uint128).max, \\\"SafeCast: value doesn't fit in 128 bits\\\");\\n return uint128(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint96 from uint256, reverting on\\n * overflow (when the input is greater than largest uint96).\\n *\\n * Counterpart to Solidity's `uint96` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 96 bits\\n */\\n function toUint96(uint256 value) internal pure returns (uint96) {\\n require(value <= type(uint96).max, \\\"SafeCast: value doesn't fit in 96 bits\\\");\\n return uint96(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint64 from uint256, reverting on\\n * overflow (when the input is greater than largest uint64).\\n *\\n * Counterpart to Solidity's `uint64` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 64 bits\\n */\\n function toUint64(uint256 value) internal pure returns (uint64) {\\n require(value <= type(uint64).max, \\\"SafeCast: value doesn't fit in 64 bits\\\");\\n return uint64(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint32 from uint256, reverting on\\n * overflow (when the input is greater than largest uint32).\\n *\\n * Counterpart to Solidity's `uint32` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 32 bits\\n */\\n function toUint32(uint256 value) internal pure returns (uint32) {\\n require(value <= type(uint32).max, \\\"SafeCast: value doesn't fit in 32 bits\\\");\\n return uint32(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint16 from uint256, reverting on\\n * overflow (when the input is greater than largest uint16).\\n *\\n * Counterpart to Solidity's `uint16` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 16 bits\\n */\\n function toUint16(uint256 value) internal pure returns (uint16) {\\n require(value <= type(uint16).max, \\\"SafeCast: value doesn't fit in 16 bits\\\");\\n return uint16(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint8 from uint256, reverting on\\n * overflow (when the input is greater than largest uint8).\\n *\\n * Counterpart to Solidity's `uint8` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 8 bits.\\n */\\n function toUint8(uint256 value) internal pure returns (uint8) {\\n require(value <= type(uint8).max, \\\"SafeCast: value doesn't fit in 8 bits\\\");\\n return uint8(value);\\n }\\n\\n /**\\n * @dev Converts a signed int256 into an unsigned uint256.\\n *\\n * Requirements:\\n *\\n * - input must be greater than or equal to 0.\\n */\\n function toUint256(int256 value) internal pure returns (uint256) {\\n require(value >= 0, \\\"SafeCast: value must be positive\\\");\\n return uint256(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int128 from int256, reverting on\\n * overflow (when the input is less than smallest int128 or\\n * greater than largest int128).\\n *\\n * Counterpart to Solidity's `int128` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 128 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt128(int256 value) internal pure returns (int128) {\\n require(value >= type(int128).min && value <= type(int128).max, \\\"SafeCast: value doesn't fit in 128 bits\\\");\\n return int128(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int64 from int256, reverting on\\n * overflow (when the input is less than smallest int64 or\\n * greater than largest int64).\\n *\\n * Counterpart to Solidity's `int64` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 64 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt64(int256 value) internal pure returns (int64) {\\n require(value >= type(int64).min && value <= type(int64).max, \\\"SafeCast: value doesn't fit in 64 bits\\\");\\n return int64(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int32 from int256, reverting on\\n * overflow (when the input is less than smallest int32 or\\n * greater than largest int32).\\n *\\n * Counterpart to Solidity's `int32` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 32 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt32(int256 value) internal pure returns (int32) {\\n require(value >= type(int32).min && value <= type(int32).max, \\\"SafeCast: value doesn't fit in 32 bits\\\");\\n return int32(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int16 from int256, reverting on\\n * overflow (when the input is less than smallest int16 or\\n * greater than largest int16).\\n *\\n * Counterpart to Solidity's `int16` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 16 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt16(int256 value) internal pure returns (int16) {\\n require(value >= type(int16).min && value <= type(int16).max, \\\"SafeCast: value doesn't fit in 16 bits\\\");\\n return int16(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int8 from int256, reverting on\\n * overflow (when the input is less than smallest int8 or\\n * greater than largest int8).\\n *\\n * Counterpart to Solidity's `int8` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 8 bits.\\n *\\n * _Available since v3.1._\\n */\\n function toInt8(int256 value) internal pure returns (int8) {\\n require(value >= type(int8).min && value <= type(int8).max, \\\"SafeCast: value doesn't fit in 8 bits\\\");\\n return int8(value);\\n }\\n\\n /**\\n * @dev Converts an unsigned uint256 into a signed int256.\\n *\\n * Requirements:\\n *\\n * - input must be less than or equal to maxInt256.\\n */\\n function toInt256(uint256 value) internal pure returns (int256) {\\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\\n require(value <= uint256(type(int256).max), \\\"SafeCast: value doesn't fit in an int256\\\");\\n return int256(value);\\n }\\n}\\n\",\"keccak256\":\"0x5c6caab697d302ad7eb59c234a4d2dbc965c1bae87709bd2850060b7695b28c7\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/math/SafeMath.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)\\n\\npragma solidity ^0.8.0;\\n\\n// CAUTION\\n// This version of SafeMath should only be used with Solidity 0.8 or later,\\n// because it relies on the compiler's built in overflow checks.\\n\\n/**\\n * @dev Wrappers over Solidity's arithmetic operations.\\n *\\n * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler\\n * now has built in overflow checking.\\n */\\nlibrary SafeMath {\\n /**\\n * @dev Returns the addition of two unsigned integers, with an overflow flag.\\n *\\n * _Available since v3.4._\\n */\\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\\n unchecked {\\n uint256 c = a + b;\\n if (c < a) return (false, 0);\\n return (true, c);\\n }\\n }\\n\\n /**\\n * @dev Returns the substraction of two unsigned integers, with an overflow flag.\\n *\\n * _Available since v3.4._\\n */\\n function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\\n unchecked {\\n if (b > a) return (false, 0);\\n return (true, a - b);\\n }\\n }\\n\\n /**\\n * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\\n *\\n * _Available since v3.4._\\n */\\n function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\\n unchecked {\\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\\n // benefit is lost if 'b' is also tested.\\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\\n if (a == 0) return (true, 0);\\n uint256 c = a * b;\\n if (c / a != b) return (false, 0);\\n return (true, c);\\n }\\n }\\n\\n /**\\n * @dev Returns the division of two unsigned integers, with a division by zero flag.\\n *\\n * _Available since v3.4._\\n */\\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\\n unchecked {\\n if (b == 0) return (false, 0);\\n return (true, a / b);\\n }\\n }\\n\\n /**\\n * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\\n *\\n * _Available since v3.4._\\n */\\n function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\\n unchecked {\\n if (b == 0) return (false, 0);\\n return (true, a % b);\\n }\\n }\\n\\n /**\\n * @dev Returns the addition of two unsigned integers, reverting on\\n * overflow.\\n *\\n * Counterpart to Solidity's `+` operator.\\n *\\n * Requirements:\\n *\\n * - Addition cannot overflow.\\n */\\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a + b;\\n }\\n\\n /**\\n * @dev Returns the subtraction of two unsigned integers, reverting on\\n * overflow (when the result is negative).\\n *\\n * Counterpart to Solidity's `-` operator.\\n *\\n * Requirements:\\n *\\n * - Subtraction cannot overflow.\\n */\\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a - b;\\n }\\n\\n /**\\n * @dev Returns the multiplication of two unsigned integers, reverting on\\n * overflow.\\n *\\n * Counterpart to Solidity's `*` operator.\\n *\\n * Requirements:\\n *\\n * - Multiplication cannot overflow.\\n */\\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a * b;\\n }\\n\\n /**\\n * @dev Returns the integer division of two unsigned integers, reverting on\\n * division by zero. The result is rounded towards zero.\\n *\\n * Counterpart to Solidity's `/` operator.\\n *\\n * Requirements:\\n *\\n * - The divisor cannot be zero.\\n */\\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a / b;\\n }\\n\\n /**\\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\\n * reverting when dividing by zero.\\n *\\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\\n * opcode (which leaves remaining gas untouched) while Solidity uses an\\n * invalid opcode to revert (consuming all remaining gas).\\n *\\n * Requirements:\\n *\\n * - The divisor cannot be zero.\\n */\\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a % b;\\n }\\n\\n /**\\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\\n * overflow (when the result is negative).\\n *\\n * CAUTION: This function is deprecated because it requires allocating memory for the error\\n * message unnecessarily. For custom revert reasons use {trySub}.\\n *\\n * Counterpart to Solidity's `-` operator.\\n *\\n * Requirements:\\n *\\n * - Subtraction cannot overflow.\\n */\\n function sub(\\n uint256 a,\\n uint256 b,\\n string memory errorMessage\\n ) internal pure returns (uint256) {\\n unchecked {\\n require(b <= a, errorMessage);\\n return a - b;\\n }\\n }\\n\\n /**\\n * @dev Returns the integer division of two unsigned integers, reverting with custom message on\\n * division by zero. The result is rounded towards zero.\\n *\\n * Counterpart to Solidity's `/` operator. Note: this function uses a\\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\\n * uses an invalid opcode to revert (consuming all remaining gas).\\n *\\n * Requirements:\\n *\\n * - The divisor cannot be zero.\\n */\\n function div(\\n uint256 a,\\n uint256 b,\\n string memory errorMessage\\n ) internal pure returns (uint256) {\\n unchecked {\\n require(b > 0, errorMessage);\\n return a / b;\\n }\\n }\\n\\n /**\\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\\n * reverting with custom message when dividing by zero.\\n *\\n * CAUTION: This function is deprecated because it requires allocating memory for the error\\n * message unnecessarily. For custom revert reasons use {tryMod}.\\n *\\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\\n * opcode (which leaves remaining gas untouched) while Solidity uses an\\n * invalid opcode to revert (consuming all remaining gas).\\n *\\n * Requirements:\\n *\\n * - The divisor cannot be zero.\\n */\\n function mod(\\n uint256 a,\\n uint256 b,\\n string memory errorMessage\\n ) internal pure returns (uint256) {\\n unchecked {\\n require(b > 0, errorMessage);\\n return a % b;\\n }\\n }\\n}\\n\",\"keccak256\":\"0xa2f576be637946f767aa56601c26d717f48a0aff44f82e46f13807eea1009a21\",\"license\":\"MIT\"},\"contracts/governance/Governable.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\\n * from owner to governor and renounce methods removed. Does not use\\n * Context.sol like Ownable.sol does for simplification.\\n * @author Origin Protocol Inc\\n */\\nabstract contract Governable {\\n // Storage position of the owner and pendingOwner of the contract\\n // keccak256(\\\"OUSD.governor\\\");\\n bytes32 private constant governorPosition =\\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\\n\\n // keccak256(\\\"OUSD.pending.governor\\\");\\n bytes32 private constant pendingGovernorPosition =\\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\\n\\n // keccak256(\\\"OUSD.reentry.status\\\");\\n bytes32 private constant reentryStatusPosition =\\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\\n\\n // See OpenZeppelin ReentrancyGuard implementation\\n uint256 constant _NOT_ENTERED = 1;\\n uint256 constant _ENTERED = 2;\\n\\n event PendingGovernorshipTransfer(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n event GovernorshipTransferred(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n /**\\n * @notice Returns the address of the current Governor.\\n */\\n function governor() public view returns (address) {\\n return _governor();\\n }\\n\\n /**\\n * @dev Returns the address of the current Governor.\\n */\\n function _governor() internal view returns (address governorOut) {\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n governorOut := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Returns the address of the pending Governor.\\n */\\n function _pendingGovernor()\\n internal\\n view\\n returns (address pendingGovernor)\\n {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n pendingGovernor := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the Governor.\\n */\\n modifier onlyGovernor() {\\n require(isGovernor(), \\\"Caller is not the Governor\\\");\\n _;\\n }\\n\\n /**\\n * @notice Returns true if the caller is the current Governor.\\n */\\n function isGovernor() public view returns (bool) {\\n return msg.sender == _governor();\\n }\\n\\n function _setGovernor(address newGovernor) internal {\\n emit GovernorshipTransferred(_governor(), newGovernor);\\n\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @dev Prevents a contract from calling itself, directly or indirectly.\\n * Calling a `nonReentrant` function from another `nonReentrant`\\n * function is not supported. It is possible to prevent this from happening\\n * by making the `nonReentrant` function external, and make it call a\\n * `private` function that does the actual work.\\n */\\n modifier nonReentrant() {\\n bytes32 position = reentryStatusPosition;\\n uint256 _reentry_status;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n _reentry_status := sload(position)\\n }\\n\\n // On the first call to nonReentrant, _notEntered will be true\\n require(_reentry_status != _ENTERED, \\\"Reentrant call\\\");\\n\\n // Any calls to nonReentrant after this point will fail\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _ENTERED)\\n }\\n\\n _;\\n\\n // By storing the original value once again, a refund is triggered (see\\n // https://eips.ethereum.org/EIPS/eip-2200)\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _NOT_ENTERED)\\n }\\n }\\n\\n function _setPendingGovernor(address newGovernor) internal {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the current Governor. Must be claimed for this to complete\\n * @param _newGovernor Address of the new Governor\\n */\\n function transferGovernance(address _newGovernor) external onlyGovernor {\\n _setPendingGovernor(_newGovernor);\\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\\n }\\n\\n /**\\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the new Governor.\\n */\\n function claimGovernance() external {\\n require(\\n msg.sender == _pendingGovernor(),\\n \\\"Only the pending Governor can complete the claim\\\"\\n );\\n _changeGovernor(msg.sender);\\n }\\n\\n /**\\n * @dev Change Governance of the contract to a new account (`newGovernor`).\\n * @param _newGovernor Address of the new Governor\\n */\\n function _changeGovernor(address _newGovernor) internal {\\n require(_newGovernor != address(0), \\\"New Governor is address(0)\\\");\\n _setGovernor(_newGovernor);\\n }\\n}\\n\",\"keccak256\":\"0xf32f873c8bfbacf2e5f01d0cf37bc7f54fbd5aa656e95c8a599114229946f107\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IBasicToken.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\ninterface IBasicToken {\\n function symbol() external view returns (string memory);\\n\\n function decimals() external view returns (uint8);\\n}\\n\",\"keccak256\":\"0xa562062698aa12572123b36dfd2072f1a39e44fed2031cc19c2c9fd522f96ec2\",\"license\":\"MIT\"},\"contracts/interfaces/IOracle.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\ninterface IOracle {\\n /**\\n * @dev returns the asset price in USD, in 8 decimal digits.\\n *\\n * The version of priceProvider deployed for OETH has 18 decimal digits\\n */\\n function price(address asset) external view returns (uint256);\\n}\\n\",\"keccak256\":\"0xa5f765f5b22cd5426803b22a7344d4c34c4d4016a0b6e9d799862133253f77b2\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IStrategy.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\\n */\\ninterface IStrategy {\\n /**\\n * @dev Deposit the given asset to platform\\n * @param _asset asset address\\n * @param _amount Amount to deposit\\n */\\n function deposit(address _asset, uint256 _amount) external;\\n\\n /**\\n * @dev Deposit the entire balance of all supported assets in the Strategy\\n * to the platform\\n */\\n function depositAll() external;\\n\\n /**\\n * @dev Withdraw given asset from Lending platform\\n */\\n function withdraw(\\n address _recipient,\\n address _asset,\\n uint256 _amount\\n ) external;\\n\\n /**\\n * @dev Liquidate all assets in strategy and return them to Vault.\\n */\\n function withdrawAll() external;\\n\\n /**\\n * @dev Returns the current balance of the given asset.\\n */\\n function checkBalance(address _asset)\\n external\\n view\\n returns (uint256 balance);\\n\\n /**\\n * @dev Returns bool indicating whether strategy supports asset.\\n */\\n function supportsAsset(address _asset) external view returns (bool);\\n\\n /**\\n * @dev Collect reward tokens from the Strategy.\\n */\\n function collectRewardTokens() external;\\n\\n /**\\n * @dev The address array of the reward tokens for the Strategy.\\n */\\n function getRewardTokenAddresses() external view returns (address[] memory);\\n\\n function harvesterAddress() external view returns (address);\\n\\n function transferToken(address token, uint256 amount) external;\\n\\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\\n external;\\n}\\n\",\"keccak256\":\"0x79ca47defb3b5a56bba13f14c440838152fd1c1aa640476154516a16da4da8ba\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/ISwapper.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\ninterface ISwapper {\\n /**\\n * @param fromAsset The token address of the asset being sold.\\n * @param toAsset The token address of the asset being purchased.\\n * @param fromAssetAmount The amount of assets being sold.\\n * @param minToAssetAmmount The minimum amount of assets to be purchased.\\n * @param data tx.data returned from 1Inch's /v5.0/1/swap API\\n */\\n function swap(\\n address fromAsset,\\n address toAsset,\\n uint256 fromAssetAmount,\\n uint256 minToAssetAmmount,\\n bytes calldata data\\n ) external returns (uint256 toAssetAmount);\\n}\\n\",\"keccak256\":\"0x70546d5e20c833bcd261ca3a4349c747e3fb3b44e1dd0fce4d2eaec80ff74379\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IVault.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { VaultStorage } from \\\"../vault/VaultStorage.sol\\\";\\n\\ninterface IVault {\\n // slither-disable-start constable-states\\n\\n event AssetSupported(address _asset);\\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\\n event StrategyApproved(address _addr);\\n event StrategyRemoved(address _addr);\\n event Mint(address _addr, uint256 _value);\\n event Redeem(address _addr, uint256 _value);\\n event CapitalPaused();\\n event CapitalUnpaused();\\n event RebasePaused();\\n event RebaseUnpaused();\\n event VaultBufferUpdated(uint256 _vaultBuffer);\\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\\n event PriceProviderUpdated(address _priceProvider);\\n event AllocateThresholdUpdated(uint256 _threshold);\\n event RebaseThresholdUpdated(uint256 _threshold);\\n event StrategistUpdated(address _address);\\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\\n event TrusteeFeeBpsChanged(uint256 _basis);\\n event TrusteeAddressChanged(address _address);\\n event SwapperChanged(address _address);\\n event SwapAllowedUndervalueChanged(uint256 _basis);\\n event SwapSlippageChanged(address _asset, uint256 _basis);\\n event Swapped(\\n address indexed _fromAsset,\\n address indexed _toAsset,\\n uint256 _fromAssetAmount,\\n uint256 _toAssetAmount\\n );\\n event StrategyAddedToMintWhitelist(address indexed strategy);\\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\\n event DripperChanged(address indexed _dripper);\\n event WithdrawalRequested(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount,\\n uint256 _queued\\n );\\n event WithdrawalClaimed(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount\\n );\\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\\n\\n // Governable.sol\\n function transferGovernance(address _newGovernor) external;\\n\\n function claimGovernance() external;\\n\\n function governor() external view returns (address);\\n\\n function ADMIN_IMPLEMENTATION() external view returns (address);\\n\\n // VaultAdmin.sol\\n function setPriceProvider(address _priceProvider) external;\\n\\n function priceProvider() external view returns (address);\\n\\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\\n\\n function redeemFeeBps() external view returns (uint256);\\n\\n function setVaultBuffer(uint256 _vaultBuffer) external;\\n\\n function vaultBuffer() external view returns (uint256);\\n\\n function setAutoAllocateThreshold(uint256 _threshold) external;\\n\\n function autoAllocateThreshold() external view returns (uint256);\\n\\n function setRebaseThreshold(uint256 _threshold) external;\\n\\n function rebaseThreshold() external view returns (uint256);\\n\\n function setStrategistAddr(address _address) external;\\n\\n function strategistAddr() external view returns (address);\\n\\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\\n\\n function maxSupplyDiff() external view returns (uint256);\\n\\n function setTrusteeAddress(address _address) external;\\n\\n function trusteeAddress() external view returns (address);\\n\\n function setTrusteeFeeBps(uint256 _basis) external;\\n\\n function trusteeFeeBps() external view returns (uint256);\\n\\n function ousdMetaStrategy() external view returns (address);\\n\\n function setSwapper(address _swapperAddr) external;\\n\\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\\n\\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\\n external;\\n\\n function supportAsset(address _asset, uint8 _unitConversion) external;\\n\\n function approveStrategy(address _addr) external;\\n\\n function removeStrategy(address _addr) external;\\n\\n function setAssetDefaultStrategy(address _asset, address _strategy)\\n external;\\n\\n function assetDefaultStrategies(address _asset)\\n external\\n view\\n returns (address);\\n\\n function pauseRebase() external;\\n\\n function unpauseRebase() external;\\n\\n function rebasePaused() external view returns (bool);\\n\\n function pauseCapital() external;\\n\\n function unpauseCapital() external;\\n\\n function capitalPaused() external view returns (bool);\\n\\n function transferToken(address _asset, uint256 _amount) external;\\n\\n function priceUnitMint(address asset) external view returns (uint256);\\n\\n function priceUnitRedeem(address asset) external view returns (uint256);\\n\\n function withdrawAllFromStrategy(address _strategyAddr) external;\\n\\n function withdrawAllFromStrategies() external;\\n\\n function withdrawFromStrategy(\\n address _strategyFromAddress,\\n address[] calldata _assets,\\n uint256[] calldata _amounts\\n ) external;\\n\\n function depositToStrategy(\\n address _strategyToAddress,\\n address[] calldata _assets,\\n uint256[] calldata _amounts\\n ) external;\\n\\n // VaultCore.sol\\n function mint(\\n address _asset,\\n uint256 _amount,\\n uint256 _minimumOusdAmount\\n ) external;\\n\\n function mintForStrategy(uint256 _amount) external;\\n\\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\\n\\n function burnForStrategy(uint256 _amount) external;\\n\\n function allocate() external;\\n\\n function rebase() external;\\n\\n function swapCollateral(\\n address fromAsset,\\n address toAsset,\\n uint256 fromAssetAmount,\\n uint256 minToAssetAmount,\\n bytes calldata data\\n ) external returns (uint256 toAssetAmount);\\n\\n function totalValue() external view returns (uint256 value);\\n\\n function checkBalance(address _asset) external view returns (uint256);\\n\\n function calculateRedeemOutputs(uint256 _amount)\\n external\\n view\\n returns (uint256[] memory);\\n\\n function getAssetCount() external view returns (uint256);\\n\\n function getAssetConfig(address _asset)\\n external\\n view\\n returns (VaultStorage.Asset memory config);\\n\\n function getAllAssets() external view returns (address[] memory);\\n\\n function getStrategyCount() external view returns (uint256);\\n\\n function swapper() external view returns (address);\\n\\n function allowedSwapUndervalue() external view returns (uint256);\\n\\n function getAllStrategies() external view returns (address[] memory);\\n\\n function isSupportedAsset(address _asset) external view returns (bool);\\n\\n function netOusdMintForStrategyThreshold() external view returns (uint256);\\n\\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\\n\\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\\n\\n function netOusdMintedForStrategy() external view returns (int256);\\n\\n function setDripper(address _dripper) external;\\n\\n function dripper() external view returns (address);\\n\\n function weth() external view returns (address);\\n\\n function cacheWETHAssetIndex() external;\\n\\n function wethAssetIndex() external view returns (uint256);\\n\\n function initialize(address, address) external;\\n\\n function setAdminImpl(address) external;\\n\\n function removeAsset(address _asset) external;\\n\\n // These are OETH specific functions\\n function addWithdrawalQueueLiquidity() external;\\n\\n function requestWithdrawal(uint256 _amount)\\n external\\n returns (uint256 requestId, uint256 queued);\\n\\n function claimWithdrawal(uint256 requestId)\\n external\\n returns (uint256 amount);\\n\\n function claimWithdrawals(uint256[] memory requestIds)\\n external\\n returns (uint256[] memory amounts, uint256 totalAmount);\\n\\n function withdrawalQueueMetadata()\\n external\\n view\\n returns (VaultStorage.WithdrawalQueueMetadata memory);\\n\\n function withdrawalRequests(uint256 requestId)\\n external\\n view\\n returns (VaultStorage.WithdrawalRequest memory);\\n\\n // OETHb specific functions\\n function addStrategyToMintWhitelist(address strategyAddr) external;\\n\\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\\n\\n function isMintWhitelistedStrategy(address strategyAddr)\\n external\\n view\\n returns (bool);\\n\\n function withdrawalClaimDelay() external view returns (uint256);\\n\\n function setWithdrawalClaimDelay(uint256 newDelay) external;\\n\\n function lastRebase() external view returns (uint64);\\n\\n function dripDuration() external view returns (uint64);\\n\\n function setDripDuration(uint256 _dripDuration) external;\\n\\n function rebasePerSecondMax() external view returns (uint64);\\n\\n function setRebaseRateMax(uint256 yearlyApr) external;\\n\\n function rebasePerSecondTarget() external view returns (uint64);\\n\\n function previewYield() external view returns (uint256 yield);\\n\\n // slither-disable-end constable-states\\n}\\n\",\"keccak256\":\"0x8d0a60f594d97578b0513b4da3d8fcafaa601950c6c4c016bf60b1344733269c\",\"license\":\"BUSL-1.1\"},\"contracts/token/OUSD.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title OUSD Token Contract\\n * @dev ERC20 compatible contract for OUSD\\n * @dev Implements an elastic supply\\n * @author Origin Protocol Inc\\n */\\nimport { IVault } from \\\"../interfaces/IVault.sol\\\";\\nimport { Governable } from \\\"../governance/Governable.sol\\\";\\nimport { SafeCast } from \\\"@openzeppelin/contracts/utils/math/SafeCast.sol\\\";\\n\\ncontract OUSD is Governable {\\n using SafeCast for int256;\\n using SafeCast for uint256;\\n\\n /// @dev Event triggered when the supply changes\\n /// @param totalSupply Updated token total supply\\n /// @param rebasingCredits Updated token rebasing credits\\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\\n event TotalSupplyUpdatedHighres(\\n uint256 totalSupply,\\n uint256 rebasingCredits,\\n uint256 rebasingCreditsPerToken\\n );\\n /// @dev Event triggered when an account opts in for rebasing\\n /// @param account Address of the account\\n event AccountRebasingEnabled(address account);\\n /// @dev Event triggered when an account opts out of rebasing\\n /// @param account Address of the account\\n event AccountRebasingDisabled(address account);\\n /// @dev Emitted when `value` tokens are moved from one account `from` to\\n /// another `to`.\\n /// @param from Address of the account tokens are moved from\\n /// @param to Address of the account tokens are moved to\\n /// @param value Amount of tokens transferred\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n /// a call to {approve}. `value` is the new allowance.\\n /// @param owner Address of the owner approving allowance\\n /// @param spender Address of the spender allowance is granted to\\n /// @param value Amount of tokens spender can transfer\\n event Approval(\\n address indexed owner,\\n address indexed spender,\\n uint256 value\\n );\\n /// @dev Yield resulting from {changeSupply} that a `source` account would\\n /// receive is directed to `target` account.\\n /// @param source Address of the source forwarding the yield\\n /// @param target Address of the target receiving the yield\\n event YieldDelegated(address source, address target);\\n /// @dev Yield delegation from `source` account to the `target` account is\\n /// suspended.\\n /// @param source Address of the source suspending yield forwarding\\n /// @param target Address of the target no longer receiving yield from `source`\\n /// account\\n event YieldUndelegated(address source, address target);\\n\\n enum RebaseOptions {\\n NotSet,\\n StdNonRebasing,\\n StdRebasing,\\n YieldDelegationSource,\\n YieldDelegationTarget\\n }\\n\\n uint256[154] private _gap; // Slots to align with deployed contract\\n uint256 private constant MAX_SUPPLY = type(uint128).max;\\n /// @dev The amount of tokens in existence\\n uint256 public totalSupply;\\n mapping(address => mapping(address => uint256)) private allowances;\\n /// @dev The vault with privileges to execute {mint}, {burn}\\n /// and {changeSupply}\\n address public vaultAddress;\\n mapping(address => uint256) internal creditBalances;\\n // the 2 storage variables below need trailing underscores to not name collide with public functions\\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\\n uint256 private rebasingCreditsPerToken_;\\n /// @dev The amount of tokens that are not rebasing - receiving yield\\n uint256 public nonRebasingSupply;\\n mapping(address => uint256) internal alternativeCreditsPerToken;\\n /// @dev A map of all addresses and their respective RebaseOptions\\n mapping(address => RebaseOptions) public rebaseState;\\n mapping(address => uint256) private __deprecated_isUpgraded;\\n /// @dev A map of addresses that have yields forwarded to. This is an\\n /// inverse mapping of {yieldFrom}\\n /// Key Account forwarding yield\\n /// Value Account receiving yield\\n mapping(address => address) public yieldTo;\\n /// @dev A map of addresses that are receiving the yield. This is an\\n /// inverse mapping of {yieldTo}\\n /// Key Account receiving yield\\n /// Value Account forwarding yield\\n mapping(address => address) public yieldFrom;\\n\\n uint256 private constant RESOLUTION_INCREASE = 1e9;\\n uint256[34] private __gap; // including below gap totals up to 200\\n\\n /// @dev Verifies that the caller is the Governor or Strategist.\\n modifier onlyGovernorOrStrategist() {\\n require(\\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\\n \\\"Caller is not the Strategist or Governor\\\"\\n );\\n _;\\n }\\n\\n /// @dev Initializes the contract and sets necessary variables.\\n /// @param _vaultAddress Address of the vault contract\\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\\n external\\n onlyGovernor\\n {\\n require(_vaultAddress != address(0), \\\"Zero vault address\\\");\\n require(vaultAddress == address(0), \\\"Already initialized\\\");\\n\\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\\n vaultAddress = _vaultAddress;\\n }\\n\\n /// @dev Returns the symbol of the token, a shorter version\\n /// of the name.\\n function symbol() external pure virtual returns (string memory) {\\n return \\\"OUSD\\\";\\n }\\n\\n /// @dev Returns the name of the token.\\n function name() external pure virtual returns (string memory) {\\n return \\\"Origin Dollar\\\";\\n }\\n\\n /// @dev Returns the number of decimals used to get its user representation.\\n function decimals() external pure virtual returns (uint8) {\\n return 18;\\n }\\n\\n /**\\n * @dev Verifies that the caller is the Vault contract\\n */\\n modifier onlyVault() {\\n require(vaultAddress == msg.sender, \\\"Caller is not the Vault\\\");\\n _;\\n }\\n\\n /**\\n * @return High resolution rebasingCreditsPerToken\\n */\\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\\n return rebasingCreditsPerToken_;\\n }\\n\\n /**\\n * @return Low resolution rebasingCreditsPerToken\\n */\\n function rebasingCreditsPerToken() external view returns (uint256) {\\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\\n }\\n\\n /**\\n * @return High resolution total number of rebasing credits\\n */\\n function rebasingCreditsHighres() external view returns (uint256) {\\n return rebasingCredits_;\\n }\\n\\n /**\\n * @return Low resolution total number of rebasing credits\\n */\\n function rebasingCredits() external view returns (uint256) {\\n return rebasingCredits_ / RESOLUTION_INCREASE;\\n }\\n\\n /**\\n * @notice Gets the balance of the specified address.\\n * @param _account Address to query the balance of.\\n * @return A uint256 representing the amount of base units owned by the\\n * specified address.\\n */\\n function balanceOf(address _account) public view returns (uint256) {\\n RebaseOptions state = rebaseState[_account];\\n if (state == RebaseOptions.YieldDelegationSource) {\\n // Saves a slot read when transferring to or from a yield delegating source\\n // since we know creditBalances equals the balance.\\n return creditBalances[_account];\\n }\\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\\n _creditsPerToken(_account);\\n if (state == RebaseOptions.YieldDelegationTarget) {\\n // creditBalances of yieldFrom accounts equals token balances\\n return baseBalance - creditBalances[yieldFrom[_account]];\\n }\\n return baseBalance;\\n }\\n\\n /**\\n * @notice Gets the credits balance of the specified address.\\n * @dev Backwards compatible with old low res credits per token.\\n * @param _account The address to query the balance of.\\n * @return (uint256, uint256) Credit balance and credits per token of the\\n * address\\n */\\n function creditsBalanceOf(address _account)\\n external\\n view\\n returns (uint256, uint256)\\n {\\n uint256 cpt = _creditsPerToken(_account);\\n if (cpt == 1e27) {\\n // For a period before the resolution upgrade, we created all new\\n // contract accounts at high resolution. Since they are not changing\\n // as a result of this upgrade, we will return their true values\\n return (creditBalances[_account], cpt);\\n } else {\\n return (\\n creditBalances[_account] / RESOLUTION_INCREASE,\\n cpt / RESOLUTION_INCREASE\\n );\\n }\\n }\\n\\n /**\\n * @notice Gets the credits balance of the specified address.\\n * @param _account The address to query the balance of.\\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\\n * address, and isUpgraded\\n */\\n function creditsBalanceOfHighres(address _account)\\n external\\n view\\n returns (\\n uint256,\\n uint256,\\n bool\\n )\\n {\\n return (\\n creditBalances[_account],\\n _creditsPerToken(_account),\\n true // all accounts have their resolution \\\"upgraded\\\"\\n );\\n }\\n\\n // Backwards compatible view\\n function nonRebasingCreditsPerToken(address _account)\\n external\\n view\\n returns (uint256)\\n {\\n return alternativeCreditsPerToken[_account];\\n }\\n\\n /**\\n * @notice Transfer tokens to a specified address.\\n * @param _to the address to transfer to.\\n * @param _value the amount to be transferred.\\n * @return true on success.\\n */\\n function transfer(address _to, uint256 _value) external returns (bool) {\\n require(_to != address(0), \\\"Transfer to zero address\\\");\\n\\n _executeTransfer(msg.sender, _to, _value);\\n\\n emit Transfer(msg.sender, _to, _value);\\n return true;\\n }\\n\\n /**\\n * @notice Transfer tokens from one address to another.\\n * @param _from The address you want to send tokens from.\\n * @param _to The address you want to transfer to.\\n * @param _value The amount of tokens to be transferred.\\n * @return true on success.\\n */\\n function transferFrom(\\n address _from,\\n address _to,\\n uint256 _value\\n ) external returns (bool) {\\n require(_to != address(0), \\\"Transfer to zero address\\\");\\n uint256 userAllowance = allowances[_from][msg.sender];\\n require(_value <= userAllowance, \\\"Allowance exceeded\\\");\\n\\n unchecked {\\n allowances[_from][msg.sender] = userAllowance - _value;\\n }\\n\\n _executeTransfer(_from, _to, _value);\\n\\n emit Transfer(_from, _to, _value);\\n return true;\\n }\\n\\n function _executeTransfer(\\n address _from,\\n address _to,\\n uint256 _value\\n ) internal {\\n (\\n int256 fromRebasingCreditsDiff,\\n int256 fromNonRebasingSupplyDiff\\n ) = _adjustAccount(_from, -_value.toInt256());\\n (\\n int256 toRebasingCreditsDiff,\\n int256 toNonRebasingSupplyDiff\\n ) = _adjustAccount(_to, _value.toInt256());\\n\\n _adjustGlobals(\\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\\n );\\n }\\n\\n function _adjustAccount(address _account, int256 _balanceChange)\\n internal\\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\\n {\\n RebaseOptions state = rebaseState[_account];\\n int256 currentBalance = balanceOf(_account).toInt256();\\n if (currentBalance + _balanceChange < 0) {\\n revert(\\\"Transfer amount exceeds balance\\\");\\n }\\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\\n\\n if (state == RebaseOptions.YieldDelegationSource) {\\n address target = yieldTo[_account];\\n uint256 targetOldBalance = balanceOf(target);\\n uint256 targetNewCredits = _balanceToRebasingCredits(\\n targetOldBalance + newBalance\\n );\\n rebasingCreditsDiff =\\n targetNewCredits.toInt256() -\\n creditBalances[target].toInt256();\\n\\n creditBalances[_account] = newBalance;\\n creditBalances[target] = targetNewCredits;\\n } else if (state == RebaseOptions.YieldDelegationTarget) {\\n uint256 newCredits = _balanceToRebasingCredits(\\n newBalance + creditBalances[yieldFrom[_account]]\\n );\\n rebasingCreditsDiff =\\n newCredits.toInt256() -\\n creditBalances[_account].toInt256();\\n creditBalances[_account] = newCredits;\\n } else {\\n _autoMigrate(_account);\\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\\n _account\\n ];\\n if (alternativeCreditsPerTokenMem > 0) {\\n nonRebasingSupplyDiff = _balanceChange;\\n if (alternativeCreditsPerTokenMem != 1e18) {\\n alternativeCreditsPerToken[_account] = 1e18;\\n }\\n creditBalances[_account] = newBalance;\\n } else {\\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\\n rebasingCreditsDiff =\\n newCredits.toInt256() -\\n creditBalances[_account].toInt256();\\n creditBalances[_account] = newCredits;\\n }\\n }\\n }\\n\\n function _adjustGlobals(\\n int256 _rebasingCreditsDiff,\\n int256 _nonRebasingSupplyDiff\\n ) internal {\\n if (_rebasingCreditsDiff != 0) {\\n rebasingCredits_ = (rebasingCredits_.toInt256() +\\n _rebasingCreditsDiff).toUint256();\\n }\\n if (_nonRebasingSupplyDiff != 0) {\\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\\n _nonRebasingSupplyDiff).toUint256();\\n }\\n }\\n\\n /**\\n * @notice Function to check the amount of tokens that _owner has allowed\\n * to `_spender`.\\n * @param _owner The address which owns the funds.\\n * @param _spender The address which will spend the funds.\\n * @return The number of tokens still available for the _spender.\\n */\\n function allowance(address _owner, address _spender)\\n external\\n view\\n returns (uint256)\\n {\\n return allowances[_owner][_spender];\\n }\\n\\n /**\\n * @notice Approve the passed address to spend the specified amount of\\n * tokens on behalf of msg.sender.\\n * @param _spender The address which will spend the funds.\\n * @param _value The amount of tokens to be spent.\\n * @return true on success.\\n */\\n function approve(address _spender, uint256 _value) external returns (bool) {\\n allowances[msg.sender][_spender] = _value;\\n emit Approval(msg.sender, _spender, _value);\\n return true;\\n }\\n\\n /**\\n * @notice Creates `_amount` tokens and assigns them to `_account`,\\n * increasing the total supply.\\n */\\n function mint(address _account, uint256 _amount) external onlyVault {\\n require(_account != address(0), \\\"Mint to the zero address\\\");\\n\\n // Account\\n (\\n int256 toRebasingCreditsDiff,\\n int256 toNonRebasingSupplyDiff\\n ) = _adjustAccount(_account, _amount.toInt256());\\n // Globals\\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\\n totalSupply = totalSupply + _amount;\\n\\n require(totalSupply < MAX_SUPPLY, \\\"Max supply\\\");\\n emit Transfer(address(0), _account, _amount);\\n }\\n\\n /**\\n * @notice Destroys `_amount` tokens from `_account`,\\n * reducing the total supply.\\n */\\n function burn(address _account, uint256 _amount) external onlyVault {\\n require(_account != address(0), \\\"Burn from the zero address\\\");\\n if (_amount == 0) {\\n return;\\n }\\n\\n // Account\\n (\\n int256 toRebasingCreditsDiff,\\n int256 toNonRebasingSupplyDiff\\n ) = _adjustAccount(_account, -_amount.toInt256());\\n // Globals\\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\\n totalSupply = totalSupply - _amount;\\n\\n emit Transfer(_account, address(0), _amount);\\n }\\n\\n /**\\n * @dev Get the credits per token for an account. Returns a fixed amount\\n * if the account is non-rebasing.\\n * @param _account Address of the account.\\n */\\n function _creditsPerToken(address _account)\\n internal\\n view\\n returns (uint256)\\n {\\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\\n _account\\n ];\\n if (alternativeCreditsPerTokenMem != 0) {\\n return alternativeCreditsPerTokenMem;\\n } else {\\n return rebasingCreditsPerToken_;\\n }\\n }\\n\\n /**\\n * @dev Auto migrate contracts to be non rebasing,\\n * unless they have opted into yield.\\n * @param _account Address of the account.\\n */\\n function _autoMigrate(address _account) internal {\\n uint256 codeLen = _account.code.length;\\n bool isEOA = (codeLen == 0) ||\\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\\n // In previous code versions, contracts would not have had their\\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\\n // therefore we check the actual accounting used on the account as well.\\n if (\\n (!isEOA) &&\\n rebaseState[_account] == RebaseOptions.NotSet &&\\n alternativeCreditsPerToken[_account] == 0\\n ) {\\n _rebaseOptOut(_account);\\n }\\n }\\n\\n /**\\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\\n * also balance that corresponds to those credits. The latter is important\\n * when adjusting the contract's global nonRebasingSupply to circumvent any\\n * possible rounding errors.\\n *\\n * @param _balance Balance of the account.\\n */\\n function _balanceToRebasingCredits(uint256 _balance)\\n internal\\n view\\n returns (uint256 rebasingCredits)\\n {\\n // Rounds up, because we need to ensure that accounts always have\\n // at least the balance that they should have.\\n // Note this should always be used on an absolute account value,\\n // not on a possibly negative diff, because then the rounding would be wrong.\\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\\n }\\n\\n /**\\n * @notice The calling account will start receiving yield after a successful call.\\n * @param _account Address of the account.\\n */\\n function governanceRebaseOptIn(address _account) external onlyGovernor {\\n require(_account != address(0), \\\"Zero address not allowed\\\");\\n _rebaseOptIn(_account);\\n }\\n\\n /**\\n * @notice The calling account will start receiving yield after a successful call.\\n */\\n function rebaseOptIn() external {\\n _rebaseOptIn(msg.sender);\\n }\\n\\n function _rebaseOptIn(address _account) internal {\\n uint256 balance = balanceOf(_account);\\n\\n // prettier-ignore\\n require(\\n alternativeCreditsPerToken[_account] > 0 ||\\n // Accounts may explicitly `rebaseOptIn` regardless of\\n // accounting if they have a 0 balance.\\n creditBalances[_account] == 0\\n ,\\n \\\"Account must be non-rebasing\\\"\\n );\\n RebaseOptions state = rebaseState[_account];\\n // prettier-ignore\\n require(\\n state == RebaseOptions.StdNonRebasing ||\\n state == RebaseOptions.NotSet,\\n \\\"Only standard non-rebasing accounts can opt in\\\"\\n );\\n\\n uint256 newCredits = _balanceToRebasingCredits(balance);\\n\\n // Account\\n rebaseState[_account] = RebaseOptions.StdRebasing;\\n alternativeCreditsPerToken[_account] = 0;\\n creditBalances[_account] = newCredits;\\n // Globals\\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\\n\\n emit AccountRebasingEnabled(_account);\\n }\\n\\n /**\\n * @notice The calling account will no longer receive yield\\n */\\n function rebaseOptOut() external {\\n _rebaseOptOut(msg.sender);\\n }\\n\\n function _rebaseOptOut(address _account) internal {\\n require(\\n alternativeCreditsPerToken[_account] == 0,\\n \\\"Account must be rebasing\\\"\\n );\\n RebaseOptions state = rebaseState[_account];\\n require(\\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\\n \\\"Only standard rebasing accounts can opt out\\\"\\n );\\n\\n uint256 oldCredits = creditBalances[_account];\\n uint256 balance = balanceOf(_account);\\n\\n // Account\\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\\n alternativeCreditsPerToken[_account] = 1e18;\\n creditBalances[_account] = balance;\\n // Globals\\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\\n\\n emit AccountRebasingDisabled(_account);\\n }\\n\\n /**\\n * @notice Distribute yield to users. This changes the exchange rate\\n * between \\\"credits\\\" and OUSD tokens to change rebasing user's balances.\\n * @param _newTotalSupply New total supply of OUSD.\\n */\\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\\n require(totalSupply > 0, \\\"Cannot increase 0 supply\\\");\\n\\n if (totalSupply == _newTotalSupply) {\\n emit TotalSupplyUpdatedHighres(\\n totalSupply,\\n rebasingCredits_,\\n rebasingCreditsPerToken_\\n );\\n return;\\n }\\n\\n totalSupply = _newTotalSupply > MAX_SUPPLY\\n ? MAX_SUPPLY\\n : _newTotalSupply;\\n\\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\\n // round up in the favour of the protocol\\n rebasingCreditsPerToken_ =\\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\\n rebasingSupply;\\n\\n require(rebasingCreditsPerToken_ > 0, \\\"Invalid change in supply\\\");\\n\\n emit TotalSupplyUpdatedHighres(\\n totalSupply,\\n rebasingCredits_,\\n rebasingCreditsPerToken_\\n );\\n }\\n\\n /*\\n * @notice Send the yield from one account to another account.\\n * Each account keeps its own balances.\\n */\\n function delegateYield(address _from, address _to)\\n external\\n onlyGovernorOrStrategist\\n {\\n require(_from != address(0), \\\"Zero from address not allowed\\\");\\n require(_to != address(0), \\\"Zero to address not allowed\\\");\\n\\n require(_from != _to, \\\"Cannot delegate to self\\\");\\n require(\\n yieldFrom[_to] == address(0) &&\\n yieldTo[_to] == address(0) &&\\n yieldFrom[_from] == address(0) &&\\n yieldTo[_from] == address(0),\\n \\\"Blocked by existing yield delegation\\\"\\n );\\n RebaseOptions stateFrom = rebaseState[_from];\\n RebaseOptions stateTo = rebaseState[_to];\\n\\n require(\\n stateFrom == RebaseOptions.NotSet ||\\n stateFrom == RebaseOptions.StdNonRebasing ||\\n stateFrom == RebaseOptions.StdRebasing,\\n \\\"Invalid rebaseState from\\\"\\n );\\n\\n require(\\n stateTo == RebaseOptions.NotSet ||\\n stateTo == RebaseOptions.StdNonRebasing ||\\n stateTo == RebaseOptions.StdRebasing,\\n \\\"Invalid rebaseState to\\\"\\n );\\n\\n if (alternativeCreditsPerToken[_from] == 0) {\\n _rebaseOptOut(_from);\\n }\\n if (alternativeCreditsPerToken[_to] > 0) {\\n _rebaseOptIn(_to);\\n }\\n\\n uint256 fromBalance = balanceOf(_from);\\n uint256 toBalance = balanceOf(_to);\\n uint256 oldToCredits = creditBalances[_to];\\n uint256 newToCredits = _balanceToRebasingCredits(\\n fromBalance + toBalance\\n );\\n\\n // Set up the bidirectional links\\n yieldTo[_from] = _to;\\n yieldFrom[_to] = _from;\\n\\n // Local\\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\\n alternativeCreditsPerToken[_from] = 1e18;\\n creditBalances[_from] = fromBalance;\\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\\n creditBalances[_to] = newToCredits;\\n\\n // Global\\n int256 creditsChange = newToCredits.toInt256() -\\n oldToCredits.toInt256();\\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\\n emit YieldDelegated(_from, _to);\\n }\\n\\n /*\\n * @notice Stop sending the yield from one account to another account.\\n */\\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\\n // Require a delegation, which will also ensure a valid delegation\\n require(yieldTo[_from] != address(0), \\\"Zero address not allowed\\\");\\n\\n address to = yieldTo[_from];\\n uint256 fromBalance = balanceOf(_from);\\n uint256 toBalance = balanceOf(to);\\n uint256 oldToCredits = creditBalances[to];\\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\\n\\n // Remove the bidirectional links\\n yieldFrom[to] = address(0);\\n yieldTo[_from] = address(0);\\n\\n // Local\\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\\n creditBalances[_from] = fromBalance;\\n rebaseState[to] = RebaseOptions.StdRebasing;\\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\\n creditBalances[to] = newToCredits;\\n\\n // Global\\n int256 creditsChange = newToCredits.toInt256() -\\n oldToCredits.toInt256();\\n _adjustGlobals(creditsChange, fromBalance.toInt256());\\n emit YieldUndelegated(_from, to);\\n }\\n}\\n\",\"keccak256\":\"0x73439bef6569f5adf6f5ce2cb54a5f0d3109d4819457532236e172a7091980a9\",\"license\":\"BUSL-1.1\"},\"contracts/utils/Helpers.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { IBasicToken } from \\\"../interfaces/IBasicToken.sol\\\";\\n\\nlibrary Helpers {\\n /**\\n * @notice Fetch the `symbol()` from an ERC20 token\\n * @dev Grabs the `symbol()` from a contract\\n * @param _token Address of the ERC20 token\\n * @return string Symbol of the ERC20 token\\n */\\n function getSymbol(address _token) internal view returns (string memory) {\\n string memory symbol = IBasicToken(_token).symbol();\\n return symbol;\\n }\\n\\n /**\\n * @notice Fetch the `decimals()` from an ERC20 token\\n * @dev Grabs the `decimals()` from a contract and fails if\\n * the decimal value does not live within a certain range\\n * @param _token Address of the ERC20 token\\n * @return uint256 Decimals of the ERC20 token\\n */\\n function getDecimals(address _token) internal view returns (uint256) {\\n uint256 decimals = IBasicToken(_token).decimals();\\n require(\\n decimals >= 4 && decimals <= 18,\\n \\\"Token must have sufficient decimal places\\\"\\n );\\n\\n return decimals;\\n }\\n}\\n\",\"keccak256\":\"0x4366f8d90b34c1eef8bbaaf369b1e5cd59f04027bb3c111f208eaee65bbc0346\",\"license\":\"BUSL-1.1\"},\"contracts/utils/Initializable.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base contract any contracts that need to initialize state after deployment.\\n * @author Origin Protocol Inc\\n */\\nabstract contract Initializable {\\n /**\\n * @dev Indicates that the contract has been initialized.\\n */\\n bool private initialized;\\n\\n /**\\n * @dev Indicates that the contract is in the process of being initialized.\\n */\\n bool private initializing;\\n\\n /**\\n * @dev Modifier to protect an initializer function from being invoked twice.\\n */\\n modifier initializer() {\\n require(\\n initializing || !initialized,\\n \\\"Initializable: contract is already initialized\\\"\\n );\\n\\n bool isTopLevelCall = !initializing;\\n if (isTopLevelCall) {\\n initializing = true;\\n initialized = true;\\n }\\n\\n _;\\n\\n if (isTopLevelCall) {\\n initializing = false;\\n }\\n }\\n\\n uint256[50] private ______gap;\\n}\\n\",\"keccak256\":\"0x50d39ebf38a3d3111f2b77a6c75ece1d4ae731552fec4697ab16fcf6c0d4d5e8\",\"license\":\"BUSL-1.1\"},\"contracts/utils/StableMath.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { SafeMath } from \\\"@openzeppelin/contracts/utils/math/SafeMath.sol\\\";\\n\\n// Based on StableMath from Stability Labs Pty. Ltd.\\n// https://github.com/mstable/mStable-contracts/blob/master/contracts/shared/StableMath.sol\\n\\nlibrary StableMath {\\n using SafeMath for uint256;\\n\\n /**\\n * @dev Scaling unit for use in specific calculations,\\n * where 1 * 10**18, or 1e18 represents a unit '1'\\n */\\n uint256 private constant FULL_SCALE = 1e18;\\n\\n /***************************************\\n Helpers\\n ****************************************/\\n\\n /**\\n * @dev Adjust the scale of an integer\\n * @param to Decimals to scale to\\n * @param from Decimals to scale from\\n */\\n function scaleBy(\\n uint256 x,\\n uint256 to,\\n uint256 from\\n ) internal pure returns (uint256) {\\n if (to > from) {\\n x = x.mul(10**(to - from));\\n } else if (to < from) {\\n // slither-disable-next-line divide-before-multiply\\n x = x.div(10**(from - to));\\n }\\n return x;\\n }\\n\\n /***************************************\\n Precise Arithmetic\\n ****************************************/\\n\\n /**\\n * @dev Multiplies two precise units, and then truncates by the full scale\\n * @param x Left hand input to multiplication\\n * @param y Right hand input to multiplication\\n * @return Result after multiplying the two inputs and then dividing by the shared\\n * scale unit\\n */\\n function mulTruncate(uint256 x, uint256 y) internal pure returns (uint256) {\\n return mulTruncateScale(x, y, FULL_SCALE);\\n }\\n\\n /**\\n * @dev Multiplies two precise units, and then truncates by the given scale. For example,\\n * when calculating 90% of 10e18, (10e18 * 9e17) / 1e18 = (9e36) / 1e18 = 9e18\\n * @param x Left hand input to multiplication\\n * @param y Right hand input to multiplication\\n * @param scale Scale unit\\n * @return Result after multiplying the two inputs and then dividing by the shared\\n * scale unit\\n */\\n function mulTruncateScale(\\n uint256 x,\\n uint256 y,\\n uint256 scale\\n ) internal pure returns (uint256) {\\n // e.g. assume scale = fullScale\\n // z = 10e18 * 9e17 = 9e36\\n uint256 z = x.mul(y);\\n // return 9e36 / 1e18 = 9e18\\n return z.div(scale);\\n }\\n\\n /**\\n * @dev Multiplies two precise units, and then truncates by the full scale, rounding up the result\\n * @param x Left hand input to multiplication\\n * @param y Right hand input to multiplication\\n * @return Result after multiplying the two inputs and then dividing by the shared\\n * scale unit, rounded up to the closest base unit.\\n */\\n function mulTruncateCeil(uint256 x, uint256 y)\\n internal\\n pure\\n returns (uint256)\\n {\\n // e.g. 8e17 * 17268172638 = 138145381104e17\\n uint256 scaled = x.mul(y);\\n // e.g. 138145381104e17 + 9.99...e17 = 138145381113.99...e17\\n uint256 ceil = scaled.add(FULL_SCALE.sub(1));\\n // e.g. 13814538111.399...e18 / 1e18 = 13814538111\\n return ceil.div(FULL_SCALE);\\n }\\n\\n /**\\n * @dev Precisely divides two units, by first scaling the left hand operand. Useful\\n * for finding percentage weightings, i.e. 8e18/10e18 = 80% (or 8e17)\\n * @param x Left hand input to division\\n * @param y Right hand input to division\\n * @return Result after multiplying the left operand by the scale, and\\n * executing the division on the right hand input.\\n */\\n function divPrecisely(uint256 x, uint256 y)\\n internal\\n pure\\n returns (uint256)\\n {\\n // e.g. 8e18 * 1e18 = 8e36\\n uint256 z = x.mul(FULL_SCALE);\\n // e.g. 8e36 / 10e18 = 8e17\\n return z.div(y);\\n }\\n}\\n\",\"keccak256\":\"0x71d6ed0053a1e5ef018d27c3b6d024f336d8157ab6f6859e400b3243a50a71b7\",\"license\":\"BUSL-1.1\"},\"contracts/vault/OETHVault.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { Vault } from \\\"./Vault.sol\\\";\\n\\n/**\\n * @title OETH Vault Contract\\n * @author Origin Protocol Inc\\n */\\ncontract OETHVault is Vault {\\n\\n}\\n\",\"keccak256\":\"0x7115af9a6e54794cf7be2ee68b5772f6de2aea8b9a35cb69d9fff1f26a0b8a79\",\"license\":\"BUSL-1.1\"},\"contracts/vault/Vault.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title OUSD VaultInitializer Contract\\n * @notice The VaultInitializer sets up the initial contract.\\n * @author Origin Protocol Inc\\n */\\nimport { VaultInitializer } from \\\"./VaultInitializer.sol\\\";\\nimport { VaultAdmin } from \\\"./VaultAdmin.sol\\\";\\n\\ncontract Vault is VaultInitializer, VaultAdmin {}\\n\",\"keccak256\":\"0xf5338a934ebc1d2cc2d8f315c244be65a8914e0bf8a40e2a1d28b01157abab00\",\"license\":\"BUSL-1.1\"},\"contracts/vault/VaultAdmin.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title OToken VaultAdmin contract\\n * @notice The VaultAdmin contract makes configuration and admin calls on the vault.\\n * @author Origin Protocol Inc\\n */\\n\\nimport { SafeERC20 } from \\\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\\\";\\n\\nimport { IOracle } from \\\"../interfaces/IOracle.sol\\\";\\nimport { ISwapper } from \\\"../interfaces/ISwapper.sol\\\";\\nimport { IVault } from \\\"../interfaces/IVault.sol\\\";\\nimport { StableMath } from \\\"../utils/StableMath.sol\\\";\\nimport { SafeCast } from \\\"@openzeppelin/contracts/utils/math/SafeCast.sol\\\";\\n\\nimport \\\"./VaultStorage.sol\\\";\\n\\ncontract VaultAdmin is VaultStorage {\\n using SafeERC20 for IERC20;\\n using StableMath for uint256;\\n using SafeCast for uint256;\\n\\n /**\\n * @dev Verifies that the caller is the Governor or Strategist.\\n */\\n modifier onlyGovernorOrStrategist() {\\n require(\\n msg.sender == strategistAddr || isGovernor(),\\n \\\"Caller is not the Strategist or Governor\\\"\\n );\\n _;\\n }\\n\\n /***************************************\\n Configuration\\n ****************************************/\\n\\n /**\\n * @notice Set address of price provider.\\n * @param _priceProvider Address of price provider\\n */\\n function setPriceProvider(address _priceProvider) external onlyGovernor {\\n priceProvider = _priceProvider;\\n emit PriceProviderUpdated(_priceProvider);\\n }\\n\\n /**\\n * @notice Set a fee in basis points to be charged for a redeem.\\n * @param _redeemFeeBps Basis point fee to be charged\\n */\\n function setRedeemFeeBps(uint256 _redeemFeeBps) external onlyGovernor {\\n require(_redeemFeeBps <= 1000, \\\"Redeem fee should not be over 10%\\\");\\n redeemFeeBps = _redeemFeeBps;\\n emit RedeemFeeUpdated(_redeemFeeBps);\\n }\\n\\n /**\\n * @notice Set a buffer of assets to keep in the Vault to handle most\\n * redemptions without needing to spend gas unwinding assets from a Strategy.\\n * @param _vaultBuffer Percentage using 18 decimals. 100% = 1e18.\\n */\\n function setVaultBuffer(uint256 _vaultBuffer)\\n external\\n onlyGovernorOrStrategist\\n {\\n require(_vaultBuffer <= 1e18, \\\"Invalid value\\\");\\n vaultBuffer = _vaultBuffer;\\n emit VaultBufferUpdated(_vaultBuffer);\\n }\\n\\n /**\\n * @notice Sets the minimum amount of OTokens in a mint to trigger an\\n * automatic allocation of funds afterwords.\\n * @param _threshold OToken amount with 18 fixed decimals.\\n */\\n function setAutoAllocateThreshold(uint256 _threshold)\\n external\\n onlyGovernor\\n {\\n autoAllocateThreshold = _threshold;\\n emit AllocateThresholdUpdated(_threshold);\\n }\\n\\n /**\\n * @notice Set a minimum amount of OTokens in a mint or redeem that triggers a\\n * rebase\\n * @param _threshold OToken amount with 18 fixed decimals.\\n */\\n function setRebaseThreshold(uint256 _threshold) external onlyGovernor {\\n rebaseThreshold = _threshold;\\n emit RebaseThresholdUpdated(_threshold);\\n }\\n\\n /**\\n * @notice Set address of Strategist\\n * @param _address Address of Strategist\\n */\\n function setStrategistAddr(address _address) external onlyGovernor {\\n strategistAddr = _address;\\n emit StrategistUpdated(_address);\\n }\\n\\n /**\\n * @notice Set the default Strategy for an asset, i.e. the one which the asset\\n will be automatically allocated to and withdrawn from\\n * @param _asset Address of the asset\\n * @param _strategy Address of the Strategy\\n */\\n function setAssetDefaultStrategy(address _asset, address _strategy)\\n external\\n onlyGovernorOrStrategist\\n {\\n emit AssetDefaultStrategyUpdated(_asset, _strategy);\\n // If its a zero address being passed for the strategy we are removing\\n // the default strategy\\n if (_strategy != address(0)) {\\n // Make sure the strategy meets some criteria\\n require(strategies[_strategy].isSupported, \\\"Strategy not approved\\\");\\n IStrategy strategy = IStrategy(_strategy);\\n require(assets[_asset].isSupported, \\\"Asset is not supported\\\");\\n require(\\n strategy.supportsAsset(_asset),\\n \\\"Asset not supported by Strategy\\\"\\n );\\n }\\n assetDefaultStrategies[_asset] = _strategy;\\n }\\n\\n /**\\n * @notice Set maximum amount of OTokens that can at any point be minted and deployed\\n * to strategy (used only by ConvexOUSDMetaStrategy for now).\\n * @param _threshold OToken amount with 18 fixed decimals.\\n */\\n function setNetOusdMintForStrategyThreshold(uint256 _threshold)\\n external\\n onlyGovernor\\n {\\n /**\\n * Because `netOusdMintedForStrategy` check in vault core works both ways\\n * (positive and negative) the actual impact of the amount of OToken minted\\n * could be double the threshold. E.g.:\\n * - contract has threshold set to 100\\n * - state of netOusdMinted is -90\\n * - in effect it can mint 190 OToken and still be within limits\\n *\\n * We are somewhat mitigating this behaviour by resetting the netOusdMinted\\n * counter whenever new threshold is set. So it can only move one threshold\\n * amount in each direction. This also enables us to reduce the threshold\\n * amount and not have problems with current netOusdMinted being near\\n * limits on either side.\\n */\\n netOusdMintedForStrategy = 0;\\n netOusdMintForStrategyThreshold = _threshold;\\n emit NetOusdMintForStrategyThresholdChanged(_threshold);\\n }\\n\\n /**\\n * @notice Set the Dripper contract that streams harvested rewards to the vault.\\n * @param _dripper Address of the Dripper contract.\\n */\\n function setDripper(address _dripper) external onlyGovernor {\\n dripper = _dripper;\\n emit DripperChanged(_dripper);\\n }\\n\\n /**\\n * @notice Changes the async withdrawal claim period for OETH & superOETHb\\n * @param _delay Delay period (should be between 10 mins to 7 days).\\n * Set to 0 to disable async withdrawals\\n */\\n function setWithdrawalClaimDelay(uint256 _delay) external onlyGovernor {\\n require(\\n _delay == 0 || (_delay >= 10 minutes && _delay <= 15 days),\\n \\\"Invalid claim delay period\\\"\\n );\\n withdrawalClaimDelay = _delay;\\n emit WithdrawalClaimDelayUpdated(_delay);\\n }\\n\\n /**\\n * @notice Set a yield streaming max rate. This spreads yield over\\n * time if it is above the max rate.\\n * @param yearlyApr in 1e18 notation. 3 * 1e18 = 3% APR\\n */\\n function setRebaseRateMax(uint256 yearlyApr)\\n external\\n onlyGovernorOrStrategist\\n {\\n // The old yield will be at the old rate\\n IVault(address(this)).rebase();\\n // Change the rate\\n uint256 newPerSecond = yearlyApr / 100 / 365 days;\\n require(newPerSecond <= MAX_REBASE_PER_SECOND, \\\"Rate too high\\\");\\n rebasePerSecondMax = newPerSecond.toUint64();\\n emit RebasePerSecondMaxChanged(newPerSecond);\\n }\\n\\n /**\\n * @notice Set the drip duration period\\n * @param _dripDuration Time in seconds to target a constant yield rate\\n */\\n function setDripDuration(uint256 _dripDuration)\\n external\\n onlyGovernorOrStrategist\\n {\\n // The old yield will be at the old rate\\n IVault(address(this)).rebase();\\n dripDuration = _dripDuration.toUint64();\\n emit DripDurationChanged(_dripDuration);\\n }\\n\\n /***************************************\\n Swaps\\n ****************************************/\\n\\n /**\\n * @notice Strategist swaps collateral assets sitting in the vault.\\n * @param _fromAsset The token address of the asset being sold by the vault.\\n * @param _toAsset The token address of the asset being purchased by the vault.\\n * @param _fromAssetAmount The amount of assets being sold by the vault.\\n * @param _minToAssetAmount The minimum amount of assets to be purchased.\\n * @param _data implementation specific data. eg 1Inch swap data\\n * @return toAssetAmount The amount of toAssets that was received from the swap\\n */\\n function swapCollateral(\\n address _fromAsset,\\n address _toAsset,\\n uint256 _fromAssetAmount,\\n uint256 _minToAssetAmount,\\n bytes calldata _data\\n )\\n external\\n nonReentrant\\n onlyGovernorOrStrategist\\n returns (uint256 toAssetAmount)\\n {\\n toAssetAmount = _swapCollateral(\\n _fromAsset,\\n _toAsset,\\n _fromAssetAmount,\\n _minToAssetAmount,\\n _data\\n );\\n }\\n\\n function _swapCollateral(\\n address _fromAsset,\\n address _toAsset,\\n uint256 _fromAssetAmount,\\n uint256 _minToAssetAmount,\\n bytes calldata _data\\n ) internal virtual returns (uint256 toAssetAmount) {\\n // Check fromAsset and toAsset are valid\\n Asset memory fromAssetConfig = assets[_fromAsset];\\n Asset memory toAssetConfig = assets[_toAsset];\\n require(fromAssetConfig.isSupported, \\\"From asset is not supported\\\");\\n require(toAssetConfig.isSupported, \\\"To asset is not supported\\\");\\n\\n // Load swap config into memory to avoid separate SLOADs\\n SwapConfig memory config = swapConfig;\\n\\n // Scope a new block to remove toAssetBalBefore from the scope of swapCollateral.\\n // This avoids a stack too deep error.\\n {\\n uint256 toAssetBalBefore = IERC20(_toAsset).balanceOf(\\n address(this)\\n );\\n\\n // Transfer from assets to the swapper contract\\n IERC20(_fromAsset).safeTransfer(config.swapper, _fromAssetAmount);\\n\\n // Call to the Swapper contract to do the actual swap\\n // The -1 is required for stETH which sometimes transfers 1 wei less than what was specified.\\n // slither-disable-next-line unused-return\\n ISwapper(config.swapper).swap(\\n _fromAsset,\\n _toAsset,\\n _fromAssetAmount - 1,\\n _minToAssetAmount,\\n _data\\n );\\n\\n // Compute the change in asset balance held by the Vault\\n toAssetAmount =\\n IERC20(_toAsset).balanceOf(address(this)) -\\n toAssetBalBefore;\\n }\\n\\n // Check the to assets returned is above slippage amount specified by the strategist\\n require(\\n toAssetAmount >= _minToAssetAmount,\\n \\\"Strategist slippage limit\\\"\\n );\\n\\n // Scope a new block to remove minOracleToAssetAmount from the scope of swapCollateral.\\n // This avoids a stack too deep error.\\n {\\n // Check the slippage against the Oracle in case the strategist made a mistake or has become malicious.\\n // to asset amount = from asset amount * from asset price / to asset price\\n uint256 minOracleToAssetAmount = (_fromAssetAmount *\\n (1e4 - fromAssetConfig.allowedOracleSlippageBps) *\\n IOracle(priceProvider).price(_fromAsset)) /\\n (IOracle(priceProvider).price(_toAsset) *\\n (1e4 + toAssetConfig.allowedOracleSlippageBps));\\n\\n // Scale both sides up to 18 decimals to compare\\n require(\\n toAssetAmount.scaleBy(18, toAssetConfig.decimals) >=\\n minOracleToAssetAmount.scaleBy(\\n 18,\\n fromAssetConfig.decimals\\n ),\\n \\\"Oracle slippage limit exceeded\\\"\\n );\\n }\\n\\n // Check the vault's total value hasn't gone below the OToken total supply\\n // by more than the allowed percentage.\\n require(\\n IVault(address(this)).totalValue() >=\\n (oUSD.totalSupply() * ((1e4 - config.allowedUndervalueBps))) /\\n 1e4,\\n \\\"Allowed value < supply\\\"\\n );\\n\\n emit Swapped(_fromAsset, _toAsset, _fromAssetAmount, toAssetAmount);\\n }\\n\\n /***************************************\\n Swap Config\\n ****************************************/\\n\\n /**\\n * @notice Set the contract the performs swaps of collateral assets.\\n * @param _swapperAddr Address of the Swapper contract that implements the ISwapper interface.\\n */\\n function setSwapper(address _swapperAddr) external onlyGovernor {\\n swapConfig.swapper = _swapperAddr;\\n emit SwapperChanged(_swapperAddr);\\n }\\n\\n /// @notice Contract that swaps the vault's collateral assets\\n function swapper() external view returns (address swapper_) {\\n swapper_ = swapConfig.swapper;\\n }\\n\\n /**\\n * @notice Set max allowed percentage the vault total value can drop below the OToken total supply in basis points\\n * when executing collateral swaps.\\n * @param _basis Percentage in basis points. eg 100 == 1%\\n */\\n function setSwapAllowedUndervalue(uint16 _basis) external onlyGovernor {\\n require(_basis < 10001, \\\"Invalid basis points\\\");\\n swapConfig.allowedUndervalueBps = _basis;\\n emit SwapAllowedUndervalueChanged(_basis);\\n }\\n\\n /**\\n * @notice Max allowed percentage the vault total value can drop below the OToken total supply in basis points\\n * when executing a collateral swap.\\n * For example 100 == 1%\\n * @return value Percentage in basis points.\\n */\\n function allowedSwapUndervalue() external view returns (uint256 value) {\\n value = swapConfig.allowedUndervalueBps;\\n }\\n\\n /**\\n * @notice Set the allowed slippage from the Oracle price for collateral asset swaps.\\n * @param _asset Address of the asset token.\\n * @param _allowedOracleSlippageBps allowed slippage from Oracle in basis points. eg 20 = 0.2%. Max 10%.\\n */\\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\\n external\\n onlyGovernor\\n {\\n require(assets[_asset].isSupported, \\\"Asset not supported\\\");\\n require(_allowedOracleSlippageBps < 1000, \\\"Slippage too high\\\");\\n\\n assets[_asset].allowedOracleSlippageBps = _allowedOracleSlippageBps;\\n\\n emit SwapSlippageChanged(_asset, _allowedOracleSlippageBps);\\n }\\n\\n /***************************************\\n Asset Config\\n ****************************************/\\n\\n /**\\n * @notice Add a supported asset to the contract, i.e. one that can be\\n * to mint OTokens.\\n * @param _asset Address of asset\\n */\\n function supportAsset(address _asset, uint8 _unitConversion)\\n external\\n virtual\\n onlyGovernor\\n {\\n require(!assets[_asset].isSupported, \\\"Asset already supported\\\");\\n\\n assets[_asset] = Asset({\\n isSupported: true,\\n unitConversion: UnitConversion(_unitConversion),\\n decimals: 0, // will be overridden in _cacheDecimals\\n allowedOracleSlippageBps: 0 // 0% by default\\n });\\n\\n _cacheDecimals(_asset);\\n allAssets.push(_asset);\\n\\n // Verify that our oracle supports the asset\\n // slither-disable-next-line unused-return\\n IOracle(priceProvider).price(_asset);\\n\\n emit AssetSupported(_asset);\\n }\\n\\n /**\\n * @notice Remove a supported asset from the Vault\\n * @param _asset Address of asset\\n */\\n function removeAsset(address _asset) external onlyGovernor {\\n require(assets[_asset].isSupported, \\\"Asset not supported\\\");\\n\\n // 1e13 for 18 decimals. And 10 for 6 decimals\\n uint256 maxDustBalance = uint256(1e13).scaleBy(\\n assets[_asset].decimals,\\n 18\\n );\\n\\n require(\\n IVault(address(this)).checkBalance(_asset) <= maxDustBalance,\\n \\\"Vault still holds asset\\\"\\n );\\n\\n uint256 assetsCount = allAssets.length;\\n uint256 assetIndex = assetsCount; // initialize at invalid index\\n for (uint256 i = 0; i < assetsCount; ++i) {\\n if (allAssets[i] == _asset) {\\n assetIndex = i;\\n break;\\n }\\n }\\n\\n // Note: If asset is not found in `allAssets`, the following line\\n // will revert with an out-of-bound error. However, there's no\\n // reason why an asset would have `Asset.isSupported = true` but\\n // not exist in `allAssets`.\\n\\n // Update allAssets array\\n allAssets[assetIndex] = allAssets[assetsCount - 1];\\n allAssets.pop();\\n\\n // Reset default strategy\\n assetDefaultStrategies[_asset] = address(0);\\n emit AssetDefaultStrategyUpdated(_asset, address(0));\\n\\n // Remove asset from storage\\n delete assets[_asset];\\n\\n emit AssetRemoved(_asset);\\n }\\n\\n /**\\n * @notice Cache decimals on OracleRouter for a particular asset. This action\\n * is required before that asset's price can be accessed.\\n * @param _asset Address of asset token\\n */\\n function cacheDecimals(address _asset) external onlyGovernor {\\n _cacheDecimals(_asset);\\n }\\n\\n /***************************************\\n Strategy Config\\n ****************************************/\\n\\n /**\\n * @notice Add a strategy to the Vault.\\n * @param _addr Address of the strategy to add\\n */\\n function approveStrategy(address _addr) external onlyGovernor {\\n require(!strategies[_addr].isSupported, \\\"Strategy already approved\\\");\\n strategies[_addr] = Strategy({ isSupported: true, _deprecated: 0 });\\n allStrategies.push(_addr);\\n emit StrategyApproved(_addr);\\n }\\n\\n /**\\n * @notice Remove a strategy from the Vault.\\n * @param _addr Address of the strategy to remove\\n */\\n\\n function removeStrategy(address _addr) external onlyGovernor {\\n require(strategies[_addr].isSupported, \\\"Strategy not approved\\\");\\n\\n uint256 assetCount = allAssets.length;\\n for (uint256 i = 0; i < assetCount; ++i) {\\n require(\\n assetDefaultStrategies[allAssets[i]] != _addr,\\n \\\"Strategy is default for an asset\\\"\\n );\\n }\\n\\n // Initialize strategyIndex with out of bounds result so function will\\n // revert if no valid index found\\n uint256 stratCount = allStrategies.length;\\n uint256 strategyIndex = stratCount;\\n for (uint256 i = 0; i < stratCount; ++i) {\\n if (allStrategies[i] == _addr) {\\n strategyIndex = i;\\n break;\\n }\\n }\\n\\n if (strategyIndex < stratCount) {\\n allStrategies[strategyIndex] = allStrategies[stratCount - 1];\\n allStrategies.pop();\\n\\n // Mark the strategy as not supported\\n strategies[_addr].isSupported = false;\\n\\n // Withdraw all assets\\n IStrategy strategy = IStrategy(_addr);\\n strategy.withdrawAll();\\n\\n emit StrategyRemoved(_addr);\\n }\\n }\\n\\n /***************************************\\n Strategies\\n ****************************************/\\n\\n /**\\n * @notice Deposit multiple assets from the vault into the strategy.\\n * @param _strategyToAddress Address of the Strategy to deposit assets into.\\n * @param _assets Array of asset address that will be deposited into the strategy.\\n * @param _amounts Array of amounts of each corresponding asset to deposit.\\n */\\n function depositToStrategy(\\n address _strategyToAddress,\\n address[] calldata _assets,\\n uint256[] calldata _amounts\\n ) external onlyGovernorOrStrategist nonReentrant {\\n _depositToStrategy(_strategyToAddress, _assets, _amounts);\\n }\\n\\n function _depositToStrategy(\\n address _strategyToAddress,\\n address[] calldata _assets,\\n uint256[] calldata _amounts\\n ) internal virtual {\\n require(\\n strategies[_strategyToAddress].isSupported,\\n \\\"Invalid to Strategy\\\"\\n );\\n require(_assets.length == _amounts.length, \\\"Parameter length mismatch\\\");\\n\\n uint256 assetCount = _assets.length;\\n for (uint256 i = 0; i < assetCount; ++i) {\\n address assetAddr = _assets[i];\\n require(\\n IStrategy(_strategyToAddress).supportsAsset(assetAddr),\\n \\\"Asset unsupported\\\"\\n );\\n // Send required amount of funds to the strategy\\n IERC20(assetAddr).safeTransfer(_strategyToAddress, _amounts[i]);\\n }\\n\\n // Deposit all the funds that have been sent to the strategy\\n IStrategy(_strategyToAddress).depositAll();\\n }\\n\\n /**\\n * @notice Withdraw multiple assets from the strategy to the vault.\\n * @param _strategyFromAddress Address of the Strategy to withdraw assets from.\\n * @param _assets Array of asset address that will be withdrawn from the strategy.\\n * @param _amounts Array of amounts of each corresponding asset to withdraw.\\n */\\n function withdrawFromStrategy(\\n address _strategyFromAddress,\\n address[] calldata _assets,\\n uint256[] calldata _amounts\\n ) external onlyGovernorOrStrategist nonReentrant {\\n _withdrawFromStrategy(\\n address(this),\\n _strategyFromAddress,\\n _assets,\\n _amounts\\n );\\n }\\n\\n /**\\n * @param _recipient can either be a strategy or the Vault\\n */\\n function _withdrawFromStrategy(\\n address _recipient,\\n address _strategyFromAddress,\\n address[] calldata _assets,\\n uint256[] calldata _amounts\\n ) internal virtual {\\n require(\\n strategies[_strategyFromAddress].isSupported,\\n \\\"Invalid from Strategy\\\"\\n );\\n require(_assets.length == _amounts.length, \\\"Parameter length mismatch\\\");\\n\\n uint256 assetCount = _assets.length;\\n for (uint256 i = 0; i < assetCount; ++i) {\\n // Withdraw from Strategy to the recipient\\n IStrategy(_strategyFromAddress).withdraw(\\n _recipient,\\n _assets[i],\\n _amounts[i]\\n );\\n }\\n }\\n\\n /**\\n * @notice Sets the maximum allowable difference between\\n * total supply and backing assets' value.\\n */\\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external onlyGovernor {\\n maxSupplyDiff = _maxSupplyDiff;\\n emit MaxSupplyDiffChanged(_maxSupplyDiff);\\n }\\n\\n /**\\n * @notice Sets the trusteeAddress that can receive a portion of yield.\\n * Setting to the zero address disables this feature.\\n */\\n function setTrusteeAddress(address _address) external onlyGovernor {\\n trusteeAddress = _address;\\n emit TrusteeAddressChanged(_address);\\n }\\n\\n /**\\n * @notice Sets the TrusteeFeeBps to the percentage of yield that should be\\n * received in basis points.\\n */\\n function setTrusteeFeeBps(uint256 _basis) external onlyGovernor {\\n require(_basis <= 5000, \\\"basis cannot exceed 50%\\\");\\n trusteeFeeBps = _basis;\\n emit TrusteeFeeBpsChanged(_basis);\\n }\\n\\n /**\\n * @notice Set OToken Metapool strategy\\n * @param _ousdMetaStrategy Address of OToken metapool strategy\\n */\\n function setOusdMetaStrategy(address _ousdMetaStrategy)\\n external\\n onlyGovernor\\n {\\n ousdMetaStrategy = _ousdMetaStrategy;\\n emit OusdMetaStrategyUpdated(_ousdMetaStrategy);\\n }\\n\\n /***************************************\\n Pause\\n ****************************************/\\n\\n /**\\n * @notice Set the deposit paused flag to true to prevent rebasing.\\n */\\n function pauseRebase() external onlyGovernorOrStrategist {\\n rebasePaused = true;\\n emit RebasePaused();\\n }\\n\\n /**\\n * @notice Set the deposit paused flag to true to allow rebasing.\\n */\\n function unpauseRebase() external onlyGovernorOrStrategist {\\n rebasePaused = false;\\n emit RebaseUnpaused();\\n }\\n\\n /**\\n * @notice Set the deposit paused flag to true to prevent capital movement.\\n */\\n function pauseCapital() external onlyGovernorOrStrategist {\\n capitalPaused = true;\\n emit CapitalPaused();\\n }\\n\\n /**\\n * @notice Set the deposit paused flag to false to enable capital movement.\\n */\\n function unpauseCapital() external onlyGovernorOrStrategist {\\n capitalPaused = false;\\n emit CapitalUnpaused();\\n }\\n\\n /***************************************\\n Utils\\n ****************************************/\\n\\n /**\\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\\n * contract, i.e. mistaken sends.\\n * @param _asset Address for the asset\\n * @param _amount Amount of the asset to transfer\\n */\\n function transferToken(address _asset, uint256 _amount)\\n external\\n onlyGovernor\\n {\\n require(!assets[_asset].isSupported, \\\"Only unsupported assets\\\");\\n IERC20(_asset).safeTransfer(governor(), _amount);\\n }\\n\\n /***************************************\\n Strategies Admin\\n ****************************************/\\n\\n /**\\n * @notice Withdraws all assets from the strategy and sends assets to the Vault.\\n * @param _strategyAddr Strategy address.\\n */\\n function withdrawAllFromStrategy(address _strategyAddr)\\n external\\n onlyGovernorOrStrategist\\n {\\n _withdrawAllFromStrategy(_strategyAddr);\\n }\\n\\n function _withdrawAllFromStrategy(address _strategyAddr) internal virtual {\\n require(\\n strategies[_strategyAddr].isSupported,\\n \\\"Strategy is not supported\\\"\\n );\\n IStrategy strategy = IStrategy(_strategyAddr);\\n strategy.withdrawAll();\\n }\\n\\n /**\\n * @notice Withdraws all assets from all the strategies and sends assets to the Vault.\\n */\\n function withdrawAllFromStrategies() external onlyGovernorOrStrategist {\\n _withdrawAllFromStrategies();\\n }\\n\\n function _withdrawAllFromStrategies() internal virtual {\\n uint256 stratCount = allStrategies.length;\\n for (uint256 i = 0; i < stratCount; ++i) {\\n IStrategy(allStrategies[i]).withdrawAll();\\n }\\n }\\n\\n /***************************************\\n Utils\\n ****************************************/\\n\\n function _cacheDecimals(address token) internal {\\n Asset storage tokenAsset = assets[token];\\n if (tokenAsset.decimals != 0) {\\n return;\\n }\\n uint8 decimals = IBasicToken(token).decimals();\\n require(decimals >= 6 && decimals <= 18, \\\"Unexpected precision\\\");\\n tokenAsset.decimals = decimals;\\n }\\n}\\n\",\"keccak256\":\"0xff7cbfd07b4262dad7eaac9bb56ade41e36097feb781b01a55a7982b9d023fd6\",\"license\":\"BUSL-1.1\"},\"contracts/vault/VaultInitializer.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title OToken VaultInitializer contract\\n * @notice The Vault contract initializes the vault.\\n * @author Origin Protocol Inc\\n */\\n\\nimport \\\"./VaultStorage.sol\\\";\\n\\ncontract VaultInitializer is VaultStorage {\\n function initialize(address _priceProvider, address _oToken)\\n external\\n onlyGovernor\\n initializer\\n {\\n require(_priceProvider != address(0), \\\"PriceProvider address is zero\\\");\\n require(_oToken != address(0), \\\"oToken address is zero\\\");\\n\\n oUSD = OUSD(_oToken);\\n\\n priceProvider = _priceProvider;\\n\\n rebasePaused = false;\\n capitalPaused = true;\\n\\n // Initial redeem fee of 0 basis points\\n redeemFeeBps = 0;\\n // Initial Vault buffer of 0%\\n vaultBuffer = 0;\\n // Initial allocate threshold of 25,000 OUSD\\n autoAllocateThreshold = 25000e18;\\n // Threshold for rebasing\\n rebaseThreshold = 1000e18;\\n // Initialize all strategies\\n allStrategies = new address[](0);\\n // Start with drip duration disabled\\n dripDuration = 1;\\n }\\n}\\n\",\"keccak256\":\"0x5555eb1e24efd4b14a7c46780e02ff58d6bdd063801a1f74715ea8a5426c634a\",\"license\":\"BUSL-1.1\"},\"contracts/vault/VaultStorage.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title OToken VaultStorage contract\\n * @notice The VaultStorage contract defines the storage for the Vault contracts\\n * @author Origin Protocol Inc\\n */\\n\\nimport { IERC20 } from \\\"@openzeppelin/contracts/token/ERC20/IERC20.sol\\\";\\nimport { SafeERC20 } from \\\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\\\";\\nimport { Address } from \\\"@openzeppelin/contracts/utils/Address.sol\\\";\\n\\nimport { IStrategy } from \\\"../interfaces/IStrategy.sol\\\";\\nimport { Governable } from \\\"../governance/Governable.sol\\\";\\nimport { OUSD } from \\\"../token/OUSD.sol\\\";\\nimport { Initializable } from \\\"../utils/Initializable.sol\\\";\\nimport \\\"../utils/Helpers.sol\\\";\\n\\ncontract VaultStorage is Initializable, Governable {\\n using SafeERC20 for IERC20;\\n\\n event AssetSupported(address _asset);\\n event AssetRemoved(address _asset);\\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\\n event StrategyApproved(address _addr);\\n event StrategyRemoved(address _addr);\\n event Mint(address _addr, uint256 _value);\\n event Redeem(address _addr, uint256 _value);\\n event CapitalPaused();\\n event CapitalUnpaused();\\n event RebasePaused();\\n event RebaseUnpaused();\\n event VaultBufferUpdated(uint256 _vaultBuffer);\\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\\n event PriceProviderUpdated(address _priceProvider);\\n event AllocateThresholdUpdated(uint256 _threshold);\\n event RebaseThresholdUpdated(uint256 _threshold);\\n event StrategistUpdated(address _address);\\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\\n event TrusteeFeeBpsChanged(uint256 _basis);\\n event TrusteeAddressChanged(address _address);\\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\\n event SwapperChanged(address _address);\\n event SwapAllowedUndervalueChanged(uint256 _basis);\\n event SwapSlippageChanged(address _asset, uint256 _basis);\\n event Swapped(\\n address indexed _fromAsset,\\n address indexed _toAsset,\\n uint256 _fromAssetAmount,\\n uint256 _toAssetAmount\\n );\\n event StrategyAddedToMintWhitelist(address indexed strategy);\\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\\n event DripperChanged(address indexed _dripper);\\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\\n event DripDurationChanged(uint256 dripDuration);\\n event WithdrawalRequested(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount,\\n uint256 _queued\\n );\\n event WithdrawalClaimed(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount\\n );\\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\\n\\n // Since we are proxy, all state should be uninitalized.\\n // Since this storage contract does not have logic directly on it\\n // we should not be checking for to see if these variables can be constant.\\n // slither-disable-start uninitialized-state\\n // slither-disable-start constable-states\\n\\n // Assets supported by the Vault, i.e. Stablecoins\\n enum UnitConversion {\\n DECIMALS,\\n GETEXCHANGERATE\\n }\\n // Changed to fit into a single storage slot so the decimals needs to be recached\\n struct Asset {\\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\\n // redeeming or checking balance of assets.\\n bool isSupported;\\n UnitConversion unitConversion;\\n uint8 decimals;\\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\\n // For example 40 == 0.4% slippage\\n uint16 allowedOracleSlippageBps;\\n }\\n\\n /// @dev mapping of supported vault assets to their configuration\\n mapping(address => Asset) internal assets;\\n /// @dev list of all assets supported by the vault.\\n address[] internal allAssets;\\n\\n // Strategies approved for use by the Vault\\n struct Strategy {\\n bool isSupported;\\n uint256 _deprecated; // Deprecated storage slot\\n }\\n /// @dev mapping of strategy contracts to their configuration\\n mapping(address => Strategy) public strategies;\\n /// @dev list of all vault strategies\\n address[] internal allStrategies;\\n\\n /// @notice Address of the Oracle price provider contract\\n address public priceProvider;\\n /// @notice pause rebasing if true\\n bool public rebasePaused;\\n /// @notice pause operations that change the OToken supply.\\n /// eg mint, redeem, allocate, mint/burn for strategy\\n bool public capitalPaused;\\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\\n uint256 public redeemFeeBps;\\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\\n uint256 public vaultBuffer;\\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\\n uint256 public autoAllocateThreshold;\\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\\n uint256 public rebaseThreshold;\\n\\n /// @dev Address of the OToken token. eg OUSD or OETH.\\n OUSD public oUSD;\\n\\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\\n // keccak256(\\\"OUSD.vault.governor.admin.impl\\\");\\n bytes32 public constant adminImplPosition =\\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\\n\\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\\n address private _deprecated_rebaseHooksAddr = address(0);\\n\\n /// @dev Deprecated: Address of Uniswap\\n address private _deprecated_uniswapAddr = address(0);\\n\\n /// @notice Address of the Strategist\\n address public strategistAddr = address(0);\\n\\n /// @notice Mapping of asset address to the Strategy that they should automatically\\n // be allocated to\\n mapping(address => address) public assetDefaultStrategies;\\n\\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\\n uint256 public maxSupplyDiff;\\n\\n /// @notice Trustee contract that can collect a percentage of yield\\n address public trusteeAddress;\\n\\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\\n uint256 public trusteeFeeBps;\\n\\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\\n address[] private _deprecated_swapTokens;\\n\\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\\n\\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\\n\\n address public ousdMetaStrategy;\\n\\n /// @notice How much OTokens are currently minted by the strategy\\n int256 public netOusdMintedForStrategy;\\n\\n /// @notice How much net total OTokens are allowed to be minted by all strategies\\n uint256 public netOusdMintForStrategyThreshold;\\n\\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\\n\\n /// @notice Collateral swap configuration.\\n /// @dev is packed into a single storage slot to save gas.\\n struct SwapConfig {\\n // Contract that swaps the vault's collateral assets\\n address swapper;\\n // Max allowed percentage the total value can drop below the total supply in basis points.\\n // For example 100 == 1%\\n uint16 allowedUndervalueBps;\\n }\\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\\n\\n // List of strategies that can mint oTokens directly\\n // Used in OETHBaseVaultCore\\n mapping(address => bool) public isMintWhitelistedStrategy;\\n\\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\\n address public dripper;\\n\\n /// Withdrawal Queue Storage /////\\n\\n struct WithdrawalQueueMetadata {\\n // cumulative total of all withdrawal requests included the ones that have already been claimed\\n uint128 queued;\\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\\n uint128 claimable;\\n // total of all the requests that have been claimed\\n uint128 claimed;\\n // index of the next withdrawal request starting at 0\\n uint128 nextWithdrawalIndex;\\n }\\n\\n /// @notice Global metadata for the withdrawal queue including:\\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\\n /// claimed - total of all the requests that have been claimed\\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\\n\\n struct WithdrawalRequest {\\n address withdrawer;\\n bool claimed;\\n uint40 timestamp; // timestamp of the withdrawal request\\n // Amount of oTokens to redeem. eg OETH\\n uint128 amount;\\n // cumulative total of all withdrawal requests including this one.\\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\\n uint128 queued;\\n }\\n\\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\\n\\n /// @notice Sets a minimum delay that is required to elapse between\\n /// requesting async withdrawals and claiming the request.\\n /// When set to 0 async withdrawals are disabled.\\n uint256 public withdrawalClaimDelay;\\n\\n /// @notice Time in seconds that the vault last rebased yield.\\n uint64 public lastRebase;\\n\\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\\n uint64 public dripDuration;\\n\\n /// @notice max rebase percentage per second\\n /// Can be used to set maximum yield of the protocol,\\n /// spreading out yield over time\\n uint64 public rebasePerSecondMax;\\n\\n /// @notice target rebase rate limit, based on past rates and funds available.\\n uint64 public rebasePerSecondTarget;\\n\\n uint256 internal constant MAX_REBASE = 0.02 ether;\\n uint256 internal constant MAX_REBASE_PER_SECOND =\\n uint256(0.05 ether) / 1 days;\\n\\n // For future use\\n uint256[43] private __gap;\\n\\n // slither-disable-end constable-states\\n // slither-disable-end uninitialized-state\\n\\n /**\\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\\n * @param newImpl address of the implementation\\n */\\n function setAdminImpl(address newImpl) external onlyGovernor {\\n require(\\n Address.isContract(newImpl),\\n \\\"new implementation is not a contract\\\"\\n );\\n bytes32 position = adminImplPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newImpl)\\n }\\n }\\n}\\n\",\"keccak256\":\"0x58f3046a1b219fd43d234f5374a4ce6ec1d8cb51a837e06eafb5532e05b0f6c1\",\"license\":\"BUSL-1.1\"}},\"version\":1}", + "bytecode": "0x603d80546001600160a01b0319908116909155603e805482169055603f8054909116905560c06040526000608081905260a052604880546001600160b01b0319169055348015604d57600080fd5b506143238061005d6000396000f3fe608060405234801561001057600080fd5b50600436106104075760003560e01c80635d36b19011610220578063ae69f3cb11610130578063c9919112116100b8578063e6cc543211610087578063e6cc5432146109bb578063e829cc16146109cf578063eb03654b146109e2578063ef08edc2146109f5578063fc0cfeee14610a1c57600080fd5b8063c991911214610984578063d38bfff41461098c578063d58e3b3a1461099f578063e45cc9f0146109b257600080fd5b8063b890ebf6116100ff578063b890ebf614610934578063bb7a632e14610947578063bc90106b14610961578063c5f0084114610974578063c7af33521461097c57600080fd5b8063ae69f3cb146108e8578063b2c9336d146108fb578063b4925a201461090e578063b888879e1461092157600080fd5b80637b9a7096116101b3578063937b258111610182578063937b2581146107f357806394828ffd1461089b5780639c82f2a4146108a35780639fa1826e146108b6578063a403e4d5146108bf57600080fd5b80637b9a7096146107b1578063840c4c7a146107c45780638e510b52146107d75780638ec489a2146107e057600080fd5b80636c7561e8116101ef5780636c7561e81461076f578063773540b31461078257806378f353a1146107955780637a2202f3146107a857600080fd5b80635d36b1901461072e578063603ea03b14610736578063636e6c4014610749578063663e64ce1461075c57600080fd5b806339ebf8231161031b5780634bed3bc0116102ae57806352d38e5d1161027d57806352d38e5d146106d857806353ca9f24146106e1578063570d8e1d146106f55780635802a17214610708578063597c89101461071b57600080fd5b80634bed3bc0146106665780634d5f46291461067957806350ba711c146106ab578063527e83a8146106be57600080fd5b806345e4213b116102ea57806345e4213b14610624578063485cc9551461062d57806349c1d54d146106405780634a5e42b11461065357600080fd5b806339ebf823146105925780633b8ae397146105d65780633dbc911f146105e95780634530820a146105f157600080fd5b80631cfbe7bc1161039e5780632da845a81161036d5780632da845a8146104e75780632e9958ab146104fa578063362bd1a31461050d57806336b6d9441461056c578063372aa2241461057f57600080fd5b80631cfbe7bc146104b15780631edfe3da146104c4578063207134b0146104cd5780632b3297f9146104d657600080fd5b80630c340a24116103da5780630c340a24146104585780631072cbea14610478578063175188e81461048b57806318ce56bd1461049e57600080fd5b80630493a0fa1461040c57806309f49bf51461042157806309f6442c146104295780630acbda7514610445575b600080fd5b61041f61041a366004613c77565b610a2f565b005b61041f610b2f565b61043260385481565b6040519081526020015b60405180910390f35b61041f610453366004613c77565b610b9f565b610460610c4a565b6040516001600160a01b03909116815260200161043c565b61041f610486366004613cac565b610c67565b61041f610499366004613cd6565b610d14565b604554610460906001600160a01b031681565b61041f6104bf366004613c77565b611003565b61043260395481565b61043260435481565b6048546001600160a01b0316610460565b61041f6104f5366004613cd6565b6110c5565b61041f610508366004613cd6565b611137565b604b54604c54610539916001600160801b0380821692600160801b928390048216928183169291041684565b604080516001600160801b039586168152938516602085015291841691830191909152909116606082015260800161043c565b61041f61057a366004613cd6565b6111a5565b61041f61058d366004613cd6565b6111d5565b6105bf6105a0366004613cd6565b6035602052600090815260409020805460019091015460ff9091169082565b60408051921515835260208301919091520161043c565b61041f6105e4366004613cd6565b611247565b61041f611384565b6106146105ff366004613cd6565b60496020526000908152604090205460ff1681565b604051901515815260200161043c565b610432604e5481565b61041f61063b366004613cf1565b6113fa565b604254610460906001600160a01b031681565b61041f610661366004613cd6565b611616565b604854600160a01b900461ffff16610432565b604f5461069390600160c01b90046001600160401b031681565b6040516001600160401b03909116815260200161043c565b6104326106b9366004613d24565b61193e565b604f5461069390600160801b90046001600160401b031681565b610432603b5481565b60375461061490600160a01b900460ff1681565b603f54610460906001600160a01b031681565b603c54610460906001600160a01b031681565b61041f610729366004613cd6565b6119e1565b61041f611a22565b604a54610460906001600160a01b031681565b61041f610757366004613c77565b611ac8565b61041f61076a366004613c77565b611b26565b61041f61077d366004613dda565b611b7f565b61041f610790366004613cd6565b611dee565b604f54610693906001600160401b031681565b61043260475481565b61041f6107bf366004613e23565b611e60565b61041f6107d2366004613e98565b611f96565b61043260415481565b61041f6107ee366004613c77565b61202f565b610854610801366004613c77565b604d60205260009081526040902080546001909101546001600160a01b03821691600160a01b810460ff1691600160a81b90910464ffffffffff16906001600160801b0380821691600160801b90041685565b604080516001600160a01b039096168652931515602086015264ffffffffff909216928401929092526001600160801b03918216606084015216608082015260a00161043c565b61041f6120e4565b61041f6108b1366004613cd6565b612154565b610432603a5481565b6104606108cd366004613cd6565b6040602081905260009182529020546001600160a01b031681565b61041f6108f6366004613e98565b6121c6565b61041f610909366004613c77565b612254565b61041f61091c366004613c77565b6122ad565b603754610460906001600160a01b031681565b61041f610942366004613c77565b612410565b604f5461069390600160401b90046001600160401b031681565b61041f61096f366004613cf1565b612469565b61041f61269c565b610614612712565b61041f612743565b61041f61099a366004613cd6565b612783565b61041f6109ad366004613cd6565b612827565b61043260465481565b60375461061490600160a81b900460ff1681565b61041f6109dd366004613f1c565b612899565b61041f6109f0366004613c77565b612959565b6104327fa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd981565b61041f610a2a366004613cd6565b612a0e565b603f546001600160a01b0316331480610a4b5750610a4b612712565b610a705760405162461bcd60e51b8152600401610a6790613f37565b60405180910390fd5b306001600160a01b031663af14052c6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610aab57600080fd5b505af1158015610abf573d6000803e3d6000fd5b50505050610acc81612ab0565b604f80546001600160401b0392909216600160401b0267ffffffffffffffff60401b199092169190911790556040518181527f406e15fbca1d8ded2dbb06765fea3a54f18395c54125a4c9916dd00ea14ee15e906020015b60405180910390a150565b603f546001600160a01b0316331480610b4b5750610b4b612712565b610b675760405162461bcd60e51b8152600401610a6790613f37565b6037805460ff60a01b191690556040517fbc044409505c95b6b851433df96e1beae715c909d8e7c1d6d7ab783300d4e3b990600090a1565b610ba7612712565b610bc35760405162461bcd60e51b8152600401610a6790613f7f565b611388811115610c155760405162461bcd60e51b815260206004820152601760248201527f62617369732063616e6e6f7420657863656564203530250000000000000000006044820152606401610a67565b60438190556040518181527f56287a45051933ea374811b3d5d165033047be5572cac676f7c28b8be4f746c790602001610b24565b6000610c626000805160206142ce8339815191525490565b905090565b610c6f612712565b610c8b5760405162461bcd60e51b8152600401610a6790613f7f565b6001600160a01b03821660009081526033602052604090205460ff1615610cf45760405162461bcd60e51b815260206004820152601760248201527f4f6e6c7920756e737570706f72746564206173736574730000000000000000006044820152606401610a67565b610d10610cff610c4a565b6001600160a01b0384169083612b1c565b5050565b610d1c612712565b610d385760405162461bcd60e51b8152600401610a6790613f7f565b6001600160a01b03811660009081526035602052604090205460ff16610d985760405162461bcd60e51b815260206004820152601560248201527414dd1c985d1959de481b9bdd08185c1c1c9bdd9959605a1b6044820152606401610a67565b60345460005b81811015610e4757826001600160a01b03166040600060348481548110610dc757610dc7613fb6565b60009182526020808320909101546001600160a01b0390811684529083019390935260409091019020541603610e3f5760405162461bcd60e51b815260206004820181905260248201527f53747261746567792069732064656661756c7420666f7220616e2061737365746044820152606401610a67565b600101610d9e565b506036548060005b82811015610e9e57846001600160a01b031660368281548110610e7457610e74613fb6565b6000918252602090912001546001600160a01b031603610e9657809150610e9e565b600101610e4f565b5081811015610ffd576036610eb4600184613fe2565b81548110610ec457610ec4613fb6565b600091825260209091200154603680546001600160a01b039092169183908110610ef057610ef0613fb6565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506036805480610f2f57610f2f613ff5565b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b03861680835260359091526040808320805460ff19169055805163429c145b60e11b81529051879363853828b6926004808201939182900301818387803b158015610fa657600080fd5b505af1158015610fba573d6000803e3d6000fd5b50506040516001600160a01b03881681527f09a1db4b80c32706328728508c941a6b954f31eb5affd32f236c1fd405f8fea49250602001905060405180910390a1505b50505050565b61100b612712565b6110275760405162461bcd60e51b8152600401610a6790613f7f565b8015806110445750610258811015801561104457506213c6808111155b6110905760405162461bcd60e51b815260206004820152601a60248201527f496e76616c696420636c61696d2064656c617920706572696f640000000000006044820152606401610a67565b604e8190556040518181527fc59f5e32049abab44ddea11021f5abb89422a2f550837afcf25df9fc8d0db6b090602001610b24565b6110cd612712565b6110e95760405162461bcd60e51b8152600401610a6790613f7f565b604280546001600160a01b0319166001600160a01b0383169081179091556040519081527f1e4af5ac389e8cde1bdaa6830881b6c987c62a45cfb3b33d27d805cde3b5775090602001610b24565b61113f612712565b61115b5760405162461bcd60e51b8152600401610a6790613f7f565b604a80546001600160a01b0319166001600160a01b0383169081179091556040517faf2910d9759321733de15af1827a49830692912adeb2b3646334861f2cd2eed490600090a250565b6111ad612712565b6111c95760405162461bcd60e51b8152600401610a6790613f7f565b6111d281612b6e565b50565b6111dd612712565b6111f95760405162461bcd60e51b8152600401610a6790613f7f565b603780546001600160a01b0319166001600160a01b0383169081179091556040519081527fb266add5f3044b17d27db796af992cecbe413921b4e8aaaee03c719e16b9806a90602001610b24565b61124f612712565b61126b5760405162461bcd60e51b8152600401610a6790613f7f565b6001600160a01b03811660009081526035602052604090205460ff16156112d45760405162461bcd60e51b815260206004820152601960248201527f537472617465677920616c726561647920617070726f766564000000000000006044820152606401610a67565b6040805180820182526001808252600060208084018281526001600160a01b038716808452603583528684209551865460ff19169015151786559051948401949094556036805493840181559091527f4a11f94e20a93c79f6ec743a1954ec4fc2c08429ae2122118bf234b2185c81b890910180546001600160a01b0319168317905591519081527f960dd94cbb79169f09a4e445d58b895df2d9bffa5b31055d0932d801724a20d19101610b24565b603f546001600160a01b03163314806113a057506113a0612712565b6113bc5760405162461bcd60e51b8152600401610a6790613f37565b6037805460ff60a81b1916600160a81b1790556040517f71f0e5b62f846a22e0b4d159e516e62fa9c2b8eb570be15f83e67d98a2ee51e090600090a1565b611402612712565b61141e5760405162461bcd60e51b8152600401610a6790613f7f565b600054610100900460ff1680611437575060005460ff16155b61149a5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610a67565b600054610100900460ff161580156114bc576000805461ffff19166101011790555b6001600160a01b0383166115125760405162461bcd60e51b815260206004820152601d60248201527f507269636550726f76696465722061646472657373206973207a65726f0000006044820152606401610a67565b6001600160a01b0382166115615760405162461bcd60e51b81526020600482015260166024820152756f546f6b656e2061646472657373206973207a65726f60501b6044820152606401610a67565b603c80546001600160a01b038481166001600160a01b031990921691909117909155603780546001600160b01b03191691851691909117600160a81b17905560006038819055603981905569054b40b1f852bda00000603a55683635c9adc5dea00000603b5560408051918252602082019081905290516115e491603691613c06565b50604f805467ffffffffffffffff60401b1916600160401b1790558015611611576000805461ff00191690555b505050565b61161e612712565b61163a5760405162461bcd60e51b8152600401610a6790613f7f565b6001600160a01b03811660009081526033602052604090205460ff166116985760405162461bcd60e51b8152602060048201526013602482015272105cdcd95d081b9bdd081cdd5c1c1bdc9d1959606a1b6044820152606401610a67565b6001600160a01b0381166000908152603360205260408120546116cd906509184e72a0009062010000900460ff166012612c75565b604051632fa8a91360e11b81526001600160a01b038416600482015290915081903090635f51522690602401602060405180830381865afa158015611716573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061173a919061400b565b11156117885760405162461bcd60e51b815260206004820152601760248201527f5661756c74207374696c6c20686f6c64732061737365740000000000000000006044820152606401610a67565b6034548060005b828110156117de57846001600160a01b0316603482815481106117b4576117b4613fb6565b6000918252602090912001546001600160a01b0316036117d6578091506117de565b60010161178f565b5060346117ec600184613fe2565b815481106117fc576117fc613fb6565b600091825260209091200154603480546001600160a01b03909216918390811061182857611828613fb6565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550603480548061186757611867613ff5565b60008281526020808220600019908401810180546001600160a01b031990811690915593019093556001600160a01b038716808252604080855280832080549094169093558251908152928301527fba58ce12801c949fa65f41c46ed108671c219baf945fa48d21026cea99ff252a910160405180910390a16001600160a01b038416600081815260336020908152604091829020805464ffffffffff1916905590519182527f37803e2125c48ee96c38ddf04e826daf335b0e1603579040fd275aba6d06b6fc910160405180910390a150505050565b7f53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac453580546000919060011981016119865760405162461bcd60e51b8152600401610a6790614024565b60028255603f546001600160a01b03163314806119a657506119a6612712565b6119c25760405162461bcd60e51b8152600401610a6790613f37565b6119d0898989898989612cd9565b600190925550979650505050505050565b603f546001600160a01b03163314806119fd57506119fd612712565b611a195760405162461bcd60e51b8152600401610a6790613f37565b6111d2816133da565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b031614611abd5760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b6064820152608401610a67565b611ac63361349e565b565b611ad0612712565b611aec5760405162461bcd60e51b8152600401610a6790613f7f565b600060465560478190556040518181527fc29d6fedbc6bdf267a08166c2b976fbd72aca5d6a769528616f8b9224c8f197f90602001610b24565b611b2e612712565b611b4a5760405162461bcd60e51b8152600401610a6790613f7f565b60418190556040518181527f95201f9c21f26877223b1ff4073936a6484c35495649e60e55730497aeb60d9390602001610b24565b611b87612712565b611ba35760405162461bcd60e51b8152600401610a6790613f7f565b6001600160a01b03821660009081526033602052604090205460ff1615611c0c5760405162461bcd60e51b815260206004820152601760248201527f417373657420616c726561647920737570706f727465640000000000000000006044820152606401610a67565b60405180608001604052806001151581526020018260ff166001811115611c3557611c3561404c565b6001811115611c4657611c4661404c565b81526000602080830182905260409283018290526001600160a01b0386168252603381529190208251815490151560ff19821681178355928401519192839161ff001990911661ffff1990911617610100836001811115611ca957611ca961404c565b02179055506040820151815460609093015161ffff1663010000000264ffff0000001960ff90921662010000029190911664ffffff00001990931692909217919091179055611cf782612b6e565b603480546001810182556000919091527f46bddb1178e94d7f2892ff5f366840eb658911794f2c3a44c450aa2c505186c10180546001600160a01b0319166001600160a01b038481169182179092556037546040516315d5220f60e31b815260048101929092529091169063aea9107890602401602060405180830381865afa158015611d88573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dac919061400b565b506040516001600160a01b03831681527f4f1ac48525e50059cc1cc6e0e1940ece0dd653a4db4841538d6aef036be2fb7b906020015b60405180910390a15050565b611df6612712565b611e125760405162461bcd60e51b8152600401610a6790613f7f565b603f80546001600160a01b0319166001600160a01b0383169081179091556040519081527f869e0abd13cc3a975de7b93be3df1cb2255c802b1cead85963cc79d99f131bee90602001610b24565b611e68612712565b611e845760405162461bcd60e51b8152600401610a6790613f7f565b6001600160a01b03821660009081526033602052604090205460ff16611ee25760405162461bcd60e51b8152602060048201526013602482015272105cdcd95d081b9bdd081cdd5c1c1bdc9d1959606a1b6044820152606401610a67565b6103e88161ffff1610611f2b5760405162461bcd60e51b81526020600482015260116024820152700a6d8d2e0e0c2ceca40e8dede40d0d2ced607b1b6044820152606401610a67565b6001600160a01b038216600081815260336020908152604091829020805464ffff0000001916630100000061ffff8716908102919091179091558251938452908301527f8d22e9d2cbe8bb65a3c4412bd8970743864512a1a0e004e8d00fb96277b78b949101611de2565b603f546001600160a01b0316331480611fb25750611fb2612712565b611fce5760405162461bcd60e51b8152600401610a6790613f37565b7f53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535805460011981016120125760405162461bcd60e51b8152600401610a6790614024565b6002825561202387878787876134fd565b50600190555050505050565b603f546001600160a01b031633148061204b575061204b612712565b6120675760405162461bcd60e51b8152600401610a6790613f37565b670de0b6b3a76400008111156120af5760405162461bcd60e51b815260206004820152600d60248201526c496e76616c69642076616c756560981b6044820152606401610a67565b60398190556040518181527f41ecb23a0e7865b25f38c268b7c3012220d822929e9edff07326e89d5bb822b590602001610b24565b603f546001600160a01b03163314806121005750612100612712565b61211c5760405162461bcd60e51b8152600401610a6790613f37565b6037805460ff60a81b191690556040517f891ebab18da80ebeeea06b1b1cede098329c4c008906a98370c2ac7a80b571cb90600090a1565b61215c612712565b6121785760405162461bcd60e51b8152600401610a6790613f7f565b604880546001600160a01b0319166001600160a01b0383169081179091556040519081527f7d7719313229e558c5a3893cad2eb86a86a049156d1d9ebd5c63a8eedefd1c0390602001610b24565b603f546001600160a01b03163314806121e257506121e2612712565b6121fe5760405162461bcd60e51b8152600401610a6790613f37565b7f53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535805460011981016122425760405162461bcd60e51b8152600401610a6790614024565b60028255612023308888888888613724565b61225c612712565b6122785760405162461bcd60e51b8152600401610a6790613f7f565b603a8190556040518181527f2ec5fb5a3d2703edc461252d92ccd2799c3c74f01d97212b20388207fa17ae4590602001610b24565b603f546001600160a01b03163314806122c957506122c9612712565b6122e55760405162461bcd60e51b8152600401610a6790613f37565b306001600160a01b031663af14052c6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561232057600080fd5b505af1158015612334573d6000803e3d6000fd5b5050505060006301e1338060648361234c9190614062565b6123569190614062565b905061236c6201518066b1a2bc2ec50000614062565b8111156123ab5760405162461bcd60e51b815260206004820152600d60248201526c0a4c2e8ca40e8dede40d0d2ced609b1b6044820152606401610a67565b6123b481612ab0565b604f80546001600160401b0392909216600160801b0267ffffffffffffffff60801b199092169190911790556040518181527fef46f143af5fead0010484fe7d6ec2e2972420faa76157f5a6075aa72e614cb590602001611de2565b612418612712565b6124345760405162461bcd60e51b8152600401610a6790613f7f565b603b8190556040518181527f39367850377ac04920a9a670f2180e7a94d83b15ad302e59875ec58fd10bd37d90602001610b24565b603f546001600160a01b03163314806124855750612485612712565b6124a15760405162461bcd60e51b8152600401610a6790613f37565b604080516001600160a01b038085168252831660208201527fba58ce12801c949fa65f41c46ed108671c219baf945fa48d21026cea99ff252a910160405180910390a16001600160a01b0381161561266e576001600160a01b03811660009081526035602052604090205460ff166125535760405162461bcd60e51b815260206004820152601560248201527414dd1c985d1959de481b9bdd08185c1c1c9bdd9959605a1b6044820152606401610a67565b6001600160a01b038216600090815260336020526040902054819060ff166125b65760405162461bcd60e51b8152602060048201526016602482015275105cdcd95d081a5cc81b9bdd081cdd5c1c1bdc9d195960521b6044820152606401610a67565b60405163551c457b60e11b81526001600160a01b03848116600483015282169063aa388af690602401602060405180830381865afa1580156125fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126209190614084565b61266c5760405162461bcd60e51b815260206004820152601f60248201527f4173736574206e6f7420737570706f72746564206279205374726174656779006044820152606401610a67565b505b6001600160a01b03918216600090815260406020819052902080546001600160a01b03191691909216179055565b603f546001600160a01b03163314806126b857506126b8612712565b6126d45760405162461bcd60e51b8152600401610a6790613f37565b6037805460ff60a01b1916600160a01b1790556040517f8cff26a5985614b3d30629cc4ab83824bf115aec971b718d8f2f99562032e97290600090a1565b600061272a6000805160206142ce8339815191525490565b6001600160a01b0316336001600160a01b031614905090565b603f546001600160a01b031633148061275f575061275f612712565b61277b5760405162461bcd60e51b8152600401610a6790613f37565b611ac66138a8565b61278b612712565b6127a75760405162461bcd60e51b8152600401610a6790613f7f565b6127cf817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b03166127ef6000805160206142ce8339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b61282f612712565b61284b5760405162461bcd60e51b8152600401610a6790613f7f565b604580546001600160a01b0319166001600160a01b0383169081179091556040519081527fa12850fb726e0b2b7b3c9a9342031e1268a8148d0eb06b4bea8613204ffcd2b890602001610b24565b6128a1612712565b6128bd5760405162461bcd60e51b8152600401610a6790613f7f565b6127118161ffff16106129095760405162461bcd60e51b8152602060048201526014602482015273496e76616c696420626173697320706f696e747360601b6044820152606401610a67565b6048805461ffff60a01b1916600160a01b61ffff8416908102919091179091556040519081527ff12c00256bee2b6facb111a88a9b1cff86e79132939b44f1353212d6f746955790602001610b24565b612961612712565b61297d5760405162461bcd60e51b8152600401610a6790613f7f565b6103e88111156129d95760405162461bcd60e51b815260206004820152602160248201527f52656465656d206665652073686f756c64206e6f74206265206f7665722031306044820152602560f81b6064820152608401610a67565b60388190556040518181527fd6c7508d6658ccee36b7b7d7fd72e5cbaeefb40c64eff24e9ae7470e846304ee90602001610b24565b612a16612712565b612a325760405162461bcd60e51b8152600401610a6790613f7f565b803b612a8c5760405162461bcd60e51b8152602060048201526024808201527f6e657720696d706c656d656e746174696f6e206973206e6f74206120636f6e746044820152631c9858dd60e21b6064820152608401610a67565b7fa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd955565b60006001600160401b03821115612b185760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203660448201526534206269747360d01b6064820152608401610a67565b5090565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052611611908490613934565b6001600160a01b0381166000908152603360205260409020805462010000900460ff1615612b9a575050565b6000826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612bda573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bfe91906140a6565b905060068160ff1610158015612c18575060128160ff1611155b612c5b5760405162461bcd60e51b81526020600482015260146024820152732ab732bc3832b1ba32b210383932b1b4b9b4b7b760611b6044820152606401610a67565b815460ff909116620100000262ff00001990911617905550565b600081831115612ca557612c9e612c8c8385613fe2565b612c9790600a6141aa565b8590613a06565b9350612ccf565b81831015612ccf57612ccc612cba8484613fe2565b612cc590600a6141aa565b8590613a1b565b93505b50825b9392505050565b6001600160a01b0386166000908152603360209081526040808320815160808101909252805460ff8082161515845285948401916101009004166001811115612d2457612d2461404c565b6001811115612d3557612d3561404c565b8152905462010000810460ff908116602080850191909152630100000090920461ffff166040938401526001600160a01b038b1660009081526033835283812084516080810190955280548084161515865295965090949092840191610100909104166001811115612da957612da961404c565b6001811115612dba57612dba61404c565b8152905462010000810460ff1660208301526301000000900461ffff166040909101528251909150612e2e5760405162461bcd60e51b815260206004820152601b60248201527f46726f6d206173736574206973206e6f7420737570706f7274656400000000006044820152606401610a67565b8051612e7c5760405162461bcd60e51b815260206004820152601960248201527f546f206173736574206973206e6f7420737570706f72746564000000000000006044820152606401610a67565b6040805180820182526048546001600160a01b038082168352600160a01b90910461ffff16602083015291516370a0823160e01b81523060048201529091600091908b16906370a0823190602401602060405180830381865afa158015612ee7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f0b919061400b565b8251909150612f25906001600160a01b038d16908b612b1c565b81516001600160a01b0316632506c0188c8c612f4260018e613fe2565b8c8c8c6040518763ffffffff1660e01b8152600401612f66969594939291906141b6565b6020604051808303816000875af1158015612f85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fa9919061400b565b506040516370a0823160e01b815230600482015281906001600160a01b038c16906370a0823190602401602060405180830381865afa158015612ff0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613014919061400b565b61301e9190613fe2565b945050868410156130715760405162461bcd60e51b815260206004820152601960248201527f5374726174656769737420736c697070616765206c696d6974000000000000006044820152606401610a67565b60008260600151612710613085919061420f565b6037546040516315d5220f60e31b81526001600160a01b038d8116600483015261ffff93909316929091169063aea9107890602401602060405180830381865afa1580156130d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130fb919061400b565b6131059190614229565b6037546040516315d5220f60e31b81526001600160a01b038e811660048301529091169063aea9107890602401602060405180830381865afa15801561314f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613173919061400b565b606086015161318490612710614240565b6131929061ffff168c614229565b61319c9190614229565b6131a69190614062565b90506131c56012856040015160ff1683612c759092919063ffffffff16565b60408401516131db90879060129060ff16612c75565b10156132295760405162461bcd60e51b815260206004820152601e60248201527f4f7261636c6520736c697070616765206c696d697420657863656564656400006044820152606401610a67565b50612710816020015161271061323f9190614240565b61ffff16603c60009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613296573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132ba919061400b565b6132c49190614229565b6132ce9190614062565b306001600160a01b031663d4c3eea06040518163ffffffff1660e01b8152600401602060405180830381865afa15801561330c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613330919061400b565b10156133775760405162461bcd60e51b8152602060048201526016602482015275416c6c6f7765642076616c7565203c20737570706c7960501b6044820152606401610a67565b886001600160a01b03168a6001600160a01b03167fa078c4190abe07940190effc1846be0ccf03ad6007bc9e93f9697d0b460befbb8a876040516133c5929190918252602082015260400190565b60405180910390a35050509695505050505050565b6001600160a01b03811660009081526035602052604090205460ff166134425760405162461bcd60e51b815260206004820152601960248201527f5374726174656779206973206e6f7420737570706f72746564000000000000006044820152606401610a67565b6000819050806001600160a01b031663853828b66040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561348257600080fd5b505af1158015613496573d6000803e3d6000fd5b505050505050565b6001600160a01b0381166134f45760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f7220697320616464726573732830290000000000006044820152606401610a67565b6111d281613a27565b6001600160a01b03851660009081526035602052604090205460ff1661355b5760405162461bcd60e51b8152602060048201526013602482015272496e76616c696420746f20537472617465677960681b6044820152606401610a67565b8281146135a65760405162461bcd60e51b81526020600482015260196024820152780a0c2e4c2dacae8cae440d8cadccee8d040dad2e6dac2e8c6d603b1b6044820152606401610a67565b8260005b818110156136c85760008686838181106135c6576135c6613fb6565b90506020020160208101906135db9190613cd6565b60405163551c457b60e11b81526001600160a01b0380831660048301529192509089169063aa388af690602401602060405180830381865afa158015613625573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136499190614084565b6136895760405162461bcd60e51b8152602060048201526011602482015270105cdcd95d081d5b9cdd5c1c1bdc9d1959607a1b6044820152606401610a67565b6136bf8886868581811061369f5761369f613fb6565b90506020020135836001600160a01b0316612b1c9092919063ffffffff16565b506001016135aa565b50856001600160a01b031663de5f62686040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561370457600080fd5b505af1158015613718573d6000803e3d6000fd5b50505050505050505050565b6001600160a01b03851660009081526035602052604090205460ff166137845760405162461bcd60e51b8152602060048201526015602482015274496e76616c69642066726f6d20537472617465677960581b6044820152606401610a67565b8281146137cf5760405162461bcd60e51b81526020600482015260196024820152780a0c2e4c2dacae8cae440d8cadccee8d040dad2e6dac2e8c6d603b1b6044820152606401610a67565b8260005b8181101561389e57866001600160a01b031663d9caed12898888858181106137fd576137fd613fb6565b90506020020160208101906138129190613cd6565b87878681811061382457613824613fb6565b6040516001600160e01b031960e088901b1681526001600160a01b03958616600482015294909316602485015250602090910201356044820152606401600060405180830381600087803b15801561387b57600080fd5b505af115801561388f573d6000803e3d6000fd5b505050508060010190506137d3565b5050505050505050565b60365460005b81811015610d1057603681815481106138c9576138c9613fb6565b60009182526020822001546040805163429c145b60e11b815290516001600160a01b039092169263853828b69260048084019382900301818387803b15801561391157600080fd5b505af1158015613925573d6000803e3d6000fd5b505050508060010190506138ae565b6000613989826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613a8e9092919063ffffffff16565b80519091501561161157808060200190518101906139a79190614084565b6116115760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610a67565b6000613a128284614229565b90505b92915050565b6000613a128284614062565b806001600160a01b0316613a476000805160206142ce8339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a36000805160206142ce83398151915255565b6060613a9d8484600085613aa5565b949350505050565b606082471015613b065760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610a67565b843b613b545760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610a67565b600080866001600160a01b03168587604051613b70919061427e565b60006040518083038185875af1925050503d8060008114613bad576040519150601f19603f3d011682016040523d82523d6000602084013e613bb2565b606091505b5091509150613bc2828286613bcd565b979650505050505050565b60608315613bdc575081612cd2565b825115613bec5782518084602001fd5b8160405162461bcd60e51b8152600401610a67919061429a565b828054828255906000526020600020908101928215613c5b579160200282015b82811115613c5b57825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190613c26565b50612b189291505b80821115612b185760008155600101613c63565b600060208284031215613c8957600080fd5b5035919050565b80356001600160a01b0381168114613ca757600080fd5b919050565b60008060408385031215613cbf57600080fd5b613cc883613c90565b946020939093013593505050565b600060208284031215613ce857600080fd5b613a1282613c90565b60008060408385031215613d0457600080fd5b613d0d83613c90565b9150613d1b60208401613c90565b90509250929050565b60008060008060008060a08789031215613d3d57600080fd5b613d4687613c90565b9550613d5460208801613c90565b9450604087013593506060870135925060808701356001600160401b03811115613d7d57600080fd5b8701601f81018913613d8e57600080fd5b80356001600160401b03811115613da457600080fd5b896020828401011115613db657600080fd5b60208201935080925050509295509295509295565b60ff811681146111d257600080fd5b60008060408385031215613ded57600080fd5b613df683613c90565b91506020830135613e0681613dcb565b809150509250929050565b803561ffff81168114613ca757600080fd5b60008060408385031215613e3657600080fd5b613e3f83613c90565b9150613d1b60208401613e11565b60008083601f840112613e5f57600080fd5b5081356001600160401b03811115613e7657600080fd5b6020830191508360208260051b8501011115613e9157600080fd5b9250929050565b600080600080600060608688031215613eb057600080fd5b613eb986613c90565b945060208601356001600160401b03811115613ed457600080fd5b613ee088828901613e4d565b90955093505060408601356001600160401b03811115613eff57600080fd5b613f0b88828901613e4d565b969995985093965092949392505050565b600060208284031215613f2e57600080fd5b613a1282613e11565b60208082526028908201527f43616c6c6572206973206e6f74207468652053747261746567697374206f722060408201526723b7bb32b93737b960c11b606082015260800190565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b81810381811115613a1557613a15613fcc565b634e487b7160e01b600052603160045260246000fd5b60006020828403121561401d57600080fd5b5051919050565b6020808252600e908201526d1499595b9d1c985b9d0818d85b1b60921b604082015260600190565b634e487b7160e01b600052602160045260246000fd5b60008261407f57634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561409657600080fd5b81518015158114612cd257600080fd5b6000602082840312156140b857600080fd5b8151612cd281613dcb565b6001815b60018411156140fe578085048111156140e2576140e2613fcc565b60018416156140f057908102905b60019390931c9280026140c7565b935093915050565b60008261411557506001613a15565b8161412257506000613a15565b816001811461413857600281146141425761415e565b6001915050613a15565b60ff84111561415357614153613fcc565b50506001821b613a15565b5060208310610133831016604e8410600b8410161715614181575081810a613a15565b61418e60001984846140c3565b80600019048211156141a2576141a2613fcc565b029392505050565b6000613a128383614106565b6001600160a01b03878116825286166020820152604081018590526060810184905260a0608082018190528101829052818360c0830137600081830160c090810191909152601f909201601f1916010195945050505050565b61ffff8181168382160190811115613a1557613a15613fcc565b8082028115828204841417613a1557613a15613fcc565b61ffff8281168282160390811115613a1557613a15613fcc565b60005b8381101561427557818101518382015260200161425d565b50506000910152565b6000825161429081846020870161425a565b9190910192915050565b60208152600082518060208401526142b981604085016020870161425a565b601f01601f1916919091016040019291505056fe7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa2646970667358221220def484d631fc2a0160a72b9d28868823bec0086bf2972aeb9358c455da27103564736f6c634300081c0033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106104075760003560e01c80635d36b19011610220578063ae69f3cb11610130578063c9919112116100b8578063e6cc543211610087578063e6cc5432146109bb578063e829cc16146109cf578063eb03654b146109e2578063ef08edc2146109f5578063fc0cfeee14610a1c57600080fd5b8063c991911214610984578063d38bfff41461098c578063d58e3b3a1461099f578063e45cc9f0146109b257600080fd5b8063b890ebf6116100ff578063b890ebf614610934578063bb7a632e14610947578063bc90106b14610961578063c5f0084114610974578063c7af33521461097c57600080fd5b8063ae69f3cb146108e8578063b2c9336d146108fb578063b4925a201461090e578063b888879e1461092157600080fd5b80637b9a7096116101b3578063937b258111610182578063937b2581146107f357806394828ffd1461089b5780639c82f2a4146108a35780639fa1826e146108b6578063a403e4d5146108bf57600080fd5b80637b9a7096146107b1578063840c4c7a146107c45780638e510b52146107d75780638ec489a2146107e057600080fd5b80636c7561e8116101ef5780636c7561e81461076f578063773540b31461078257806378f353a1146107955780637a2202f3146107a857600080fd5b80635d36b1901461072e578063603ea03b14610736578063636e6c4014610749578063663e64ce1461075c57600080fd5b806339ebf8231161031b5780634bed3bc0116102ae57806352d38e5d1161027d57806352d38e5d146106d857806353ca9f24146106e1578063570d8e1d146106f55780635802a17214610708578063597c89101461071b57600080fd5b80634bed3bc0146106665780634d5f46291461067957806350ba711c146106ab578063527e83a8146106be57600080fd5b806345e4213b116102ea57806345e4213b14610624578063485cc9551461062d57806349c1d54d146106405780634a5e42b11461065357600080fd5b806339ebf823146105925780633b8ae397146105d65780633dbc911f146105e95780634530820a146105f157600080fd5b80631cfbe7bc1161039e5780632da845a81161036d5780632da845a8146104e75780632e9958ab146104fa578063362bd1a31461050d57806336b6d9441461056c578063372aa2241461057f57600080fd5b80631cfbe7bc146104b15780631edfe3da146104c4578063207134b0146104cd5780632b3297f9146104d657600080fd5b80630c340a24116103da5780630c340a24146104585780631072cbea14610478578063175188e81461048b57806318ce56bd1461049e57600080fd5b80630493a0fa1461040c57806309f49bf51461042157806309f6442c146104295780630acbda7514610445575b600080fd5b61041f61041a366004613c77565b610a2f565b005b61041f610b2f565b61043260385481565b6040519081526020015b60405180910390f35b61041f610453366004613c77565b610b9f565b610460610c4a565b6040516001600160a01b03909116815260200161043c565b61041f610486366004613cac565b610c67565b61041f610499366004613cd6565b610d14565b604554610460906001600160a01b031681565b61041f6104bf366004613c77565b611003565b61043260395481565b61043260435481565b6048546001600160a01b0316610460565b61041f6104f5366004613cd6565b6110c5565b61041f610508366004613cd6565b611137565b604b54604c54610539916001600160801b0380821692600160801b928390048216928183169291041684565b604080516001600160801b039586168152938516602085015291841691830191909152909116606082015260800161043c565b61041f61057a366004613cd6565b6111a5565b61041f61058d366004613cd6565b6111d5565b6105bf6105a0366004613cd6565b6035602052600090815260409020805460019091015460ff9091169082565b60408051921515835260208301919091520161043c565b61041f6105e4366004613cd6565b611247565b61041f611384565b6106146105ff366004613cd6565b60496020526000908152604090205460ff1681565b604051901515815260200161043c565b610432604e5481565b61041f61063b366004613cf1565b6113fa565b604254610460906001600160a01b031681565b61041f610661366004613cd6565b611616565b604854600160a01b900461ffff16610432565b604f5461069390600160c01b90046001600160401b031681565b6040516001600160401b03909116815260200161043c565b6104326106b9366004613d24565b61193e565b604f5461069390600160801b90046001600160401b031681565b610432603b5481565b60375461061490600160a01b900460ff1681565b603f54610460906001600160a01b031681565b603c54610460906001600160a01b031681565b61041f610729366004613cd6565b6119e1565b61041f611a22565b604a54610460906001600160a01b031681565b61041f610757366004613c77565b611ac8565b61041f61076a366004613c77565b611b26565b61041f61077d366004613dda565b611b7f565b61041f610790366004613cd6565b611dee565b604f54610693906001600160401b031681565b61043260475481565b61041f6107bf366004613e23565b611e60565b61041f6107d2366004613e98565b611f96565b61043260415481565b61041f6107ee366004613c77565b61202f565b610854610801366004613c77565b604d60205260009081526040902080546001909101546001600160a01b03821691600160a01b810460ff1691600160a81b90910464ffffffffff16906001600160801b0380821691600160801b90041685565b604080516001600160a01b039096168652931515602086015264ffffffffff909216928401929092526001600160801b03918216606084015216608082015260a00161043c565b61041f6120e4565b61041f6108b1366004613cd6565b612154565b610432603a5481565b6104606108cd366004613cd6565b6040602081905260009182529020546001600160a01b031681565b61041f6108f6366004613e98565b6121c6565b61041f610909366004613c77565b612254565b61041f61091c366004613c77565b6122ad565b603754610460906001600160a01b031681565b61041f610942366004613c77565b612410565b604f5461069390600160401b90046001600160401b031681565b61041f61096f366004613cf1565b612469565b61041f61269c565b610614612712565b61041f612743565b61041f61099a366004613cd6565b612783565b61041f6109ad366004613cd6565b612827565b61043260465481565b60375461061490600160a81b900460ff1681565b61041f6109dd366004613f1c565b612899565b61041f6109f0366004613c77565b612959565b6104327fa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd981565b61041f610a2a366004613cd6565b612a0e565b603f546001600160a01b0316331480610a4b5750610a4b612712565b610a705760405162461bcd60e51b8152600401610a6790613f37565b60405180910390fd5b306001600160a01b031663af14052c6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610aab57600080fd5b505af1158015610abf573d6000803e3d6000fd5b50505050610acc81612ab0565b604f80546001600160401b0392909216600160401b0267ffffffffffffffff60401b199092169190911790556040518181527f406e15fbca1d8ded2dbb06765fea3a54f18395c54125a4c9916dd00ea14ee15e906020015b60405180910390a150565b603f546001600160a01b0316331480610b4b5750610b4b612712565b610b675760405162461bcd60e51b8152600401610a6790613f37565b6037805460ff60a01b191690556040517fbc044409505c95b6b851433df96e1beae715c909d8e7c1d6d7ab783300d4e3b990600090a1565b610ba7612712565b610bc35760405162461bcd60e51b8152600401610a6790613f7f565b611388811115610c155760405162461bcd60e51b815260206004820152601760248201527f62617369732063616e6e6f7420657863656564203530250000000000000000006044820152606401610a67565b60438190556040518181527f56287a45051933ea374811b3d5d165033047be5572cac676f7c28b8be4f746c790602001610b24565b6000610c626000805160206142ce8339815191525490565b905090565b610c6f612712565b610c8b5760405162461bcd60e51b8152600401610a6790613f7f565b6001600160a01b03821660009081526033602052604090205460ff1615610cf45760405162461bcd60e51b815260206004820152601760248201527f4f6e6c7920756e737570706f72746564206173736574730000000000000000006044820152606401610a67565b610d10610cff610c4a565b6001600160a01b0384169083612b1c565b5050565b610d1c612712565b610d385760405162461bcd60e51b8152600401610a6790613f7f565b6001600160a01b03811660009081526035602052604090205460ff16610d985760405162461bcd60e51b815260206004820152601560248201527414dd1c985d1959de481b9bdd08185c1c1c9bdd9959605a1b6044820152606401610a67565b60345460005b81811015610e4757826001600160a01b03166040600060348481548110610dc757610dc7613fb6565b60009182526020808320909101546001600160a01b0390811684529083019390935260409091019020541603610e3f5760405162461bcd60e51b815260206004820181905260248201527f53747261746567792069732064656661756c7420666f7220616e2061737365746044820152606401610a67565b600101610d9e565b506036548060005b82811015610e9e57846001600160a01b031660368281548110610e7457610e74613fb6565b6000918252602090912001546001600160a01b031603610e9657809150610e9e565b600101610e4f565b5081811015610ffd576036610eb4600184613fe2565b81548110610ec457610ec4613fb6565b600091825260209091200154603680546001600160a01b039092169183908110610ef057610ef0613fb6565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506036805480610f2f57610f2f613ff5565b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b03861680835260359091526040808320805460ff19169055805163429c145b60e11b81529051879363853828b6926004808201939182900301818387803b158015610fa657600080fd5b505af1158015610fba573d6000803e3d6000fd5b50506040516001600160a01b03881681527f09a1db4b80c32706328728508c941a6b954f31eb5affd32f236c1fd405f8fea49250602001905060405180910390a1505b50505050565b61100b612712565b6110275760405162461bcd60e51b8152600401610a6790613f7f565b8015806110445750610258811015801561104457506213c6808111155b6110905760405162461bcd60e51b815260206004820152601a60248201527f496e76616c696420636c61696d2064656c617920706572696f640000000000006044820152606401610a67565b604e8190556040518181527fc59f5e32049abab44ddea11021f5abb89422a2f550837afcf25df9fc8d0db6b090602001610b24565b6110cd612712565b6110e95760405162461bcd60e51b8152600401610a6790613f7f565b604280546001600160a01b0319166001600160a01b0383169081179091556040519081527f1e4af5ac389e8cde1bdaa6830881b6c987c62a45cfb3b33d27d805cde3b5775090602001610b24565b61113f612712565b61115b5760405162461bcd60e51b8152600401610a6790613f7f565b604a80546001600160a01b0319166001600160a01b0383169081179091556040517faf2910d9759321733de15af1827a49830692912adeb2b3646334861f2cd2eed490600090a250565b6111ad612712565b6111c95760405162461bcd60e51b8152600401610a6790613f7f565b6111d281612b6e565b50565b6111dd612712565b6111f95760405162461bcd60e51b8152600401610a6790613f7f565b603780546001600160a01b0319166001600160a01b0383169081179091556040519081527fb266add5f3044b17d27db796af992cecbe413921b4e8aaaee03c719e16b9806a90602001610b24565b61124f612712565b61126b5760405162461bcd60e51b8152600401610a6790613f7f565b6001600160a01b03811660009081526035602052604090205460ff16156112d45760405162461bcd60e51b815260206004820152601960248201527f537472617465677920616c726561647920617070726f766564000000000000006044820152606401610a67565b6040805180820182526001808252600060208084018281526001600160a01b038716808452603583528684209551865460ff19169015151786559051948401949094556036805493840181559091527f4a11f94e20a93c79f6ec743a1954ec4fc2c08429ae2122118bf234b2185c81b890910180546001600160a01b0319168317905591519081527f960dd94cbb79169f09a4e445d58b895df2d9bffa5b31055d0932d801724a20d19101610b24565b603f546001600160a01b03163314806113a057506113a0612712565b6113bc5760405162461bcd60e51b8152600401610a6790613f37565b6037805460ff60a81b1916600160a81b1790556040517f71f0e5b62f846a22e0b4d159e516e62fa9c2b8eb570be15f83e67d98a2ee51e090600090a1565b611402612712565b61141e5760405162461bcd60e51b8152600401610a6790613f7f565b600054610100900460ff1680611437575060005460ff16155b61149a5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610a67565b600054610100900460ff161580156114bc576000805461ffff19166101011790555b6001600160a01b0383166115125760405162461bcd60e51b815260206004820152601d60248201527f507269636550726f76696465722061646472657373206973207a65726f0000006044820152606401610a67565b6001600160a01b0382166115615760405162461bcd60e51b81526020600482015260166024820152756f546f6b656e2061646472657373206973207a65726f60501b6044820152606401610a67565b603c80546001600160a01b038481166001600160a01b031990921691909117909155603780546001600160b01b03191691851691909117600160a81b17905560006038819055603981905569054b40b1f852bda00000603a55683635c9adc5dea00000603b5560408051918252602082019081905290516115e491603691613c06565b50604f805467ffffffffffffffff60401b1916600160401b1790558015611611576000805461ff00191690555b505050565b61161e612712565b61163a5760405162461bcd60e51b8152600401610a6790613f7f565b6001600160a01b03811660009081526033602052604090205460ff166116985760405162461bcd60e51b8152602060048201526013602482015272105cdcd95d081b9bdd081cdd5c1c1bdc9d1959606a1b6044820152606401610a67565b6001600160a01b0381166000908152603360205260408120546116cd906509184e72a0009062010000900460ff166012612c75565b604051632fa8a91360e11b81526001600160a01b038416600482015290915081903090635f51522690602401602060405180830381865afa158015611716573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061173a919061400b565b11156117885760405162461bcd60e51b815260206004820152601760248201527f5661756c74207374696c6c20686f6c64732061737365740000000000000000006044820152606401610a67565b6034548060005b828110156117de57846001600160a01b0316603482815481106117b4576117b4613fb6565b6000918252602090912001546001600160a01b0316036117d6578091506117de565b60010161178f565b5060346117ec600184613fe2565b815481106117fc576117fc613fb6565b600091825260209091200154603480546001600160a01b03909216918390811061182857611828613fb6565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550603480548061186757611867613ff5565b60008281526020808220600019908401810180546001600160a01b031990811690915593019093556001600160a01b038716808252604080855280832080549094169093558251908152928301527fba58ce12801c949fa65f41c46ed108671c219baf945fa48d21026cea99ff252a910160405180910390a16001600160a01b038416600081815260336020908152604091829020805464ffffffffff1916905590519182527f37803e2125c48ee96c38ddf04e826daf335b0e1603579040fd275aba6d06b6fc910160405180910390a150505050565b7f53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac453580546000919060011981016119865760405162461bcd60e51b8152600401610a6790614024565b60028255603f546001600160a01b03163314806119a657506119a6612712565b6119c25760405162461bcd60e51b8152600401610a6790613f37565b6119d0898989898989612cd9565b600190925550979650505050505050565b603f546001600160a01b03163314806119fd57506119fd612712565b611a195760405162461bcd60e51b8152600401610a6790613f37565b6111d2816133da565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b031614611abd5760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b6064820152608401610a67565b611ac63361349e565b565b611ad0612712565b611aec5760405162461bcd60e51b8152600401610a6790613f7f565b600060465560478190556040518181527fc29d6fedbc6bdf267a08166c2b976fbd72aca5d6a769528616f8b9224c8f197f90602001610b24565b611b2e612712565b611b4a5760405162461bcd60e51b8152600401610a6790613f7f565b60418190556040518181527f95201f9c21f26877223b1ff4073936a6484c35495649e60e55730497aeb60d9390602001610b24565b611b87612712565b611ba35760405162461bcd60e51b8152600401610a6790613f7f565b6001600160a01b03821660009081526033602052604090205460ff1615611c0c5760405162461bcd60e51b815260206004820152601760248201527f417373657420616c726561647920737570706f727465640000000000000000006044820152606401610a67565b60405180608001604052806001151581526020018260ff166001811115611c3557611c3561404c565b6001811115611c4657611c4661404c565b81526000602080830182905260409283018290526001600160a01b0386168252603381529190208251815490151560ff19821681178355928401519192839161ff001990911661ffff1990911617610100836001811115611ca957611ca961404c565b02179055506040820151815460609093015161ffff1663010000000264ffff0000001960ff90921662010000029190911664ffffff00001990931692909217919091179055611cf782612b6e565b603480546001810182556000919091527f46bddb1178e94d7f2892ff5f366840eb658911794f2c3a44c450aa2c505186c10180546001600160a01b0319166001600160a01b038481169182179092556037546040516315d5220f60e31b815260048101929092529091169063aea9107890602401602060405180830381865afa158015611d88573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dac919061400b565b506040516001600160a01b03831681527f4f1ac48525e50059cc1cc6e0e1940ece0dd653a4db4841538d6aef036be2fb7b906020015b60405180910390a15050565b611df6612712565b611e125760405162461bcd60e51b8152600401610a6790613f7f565b603f80546001600160a01b0319166001600160a01b0383169081179091556040519081527f869e0abd13cc3a975de7b93be3df1cb2255c802b1cead85963cc79d99f131bee90602001610b24565b611e68612712565b611e845760405162461bcd60e51b8152600401610a6790613f7f565b6001600160a01b03821660009081526033602052604090205460ff16611ee25760405162461bcd60e51b8152602060048201526013602482015272105cdcd95d081b9bdd081cdd5c1c1bdc9d1959606a1b6044820152606401610a67565b6103e88161ffff1610611f2b5760405162461bcd60e51b81526020600482015260116024820152700a6d8d2e0e0c2ceca40e8dede40d0d2ced607b1b6044820152606401610a67565b6001600160a01b038216600081815260336020908152604091829020805464ffff0000001916630100000061ffff8716908102919091179091558251938452908301527f8d22e9d2cbe8bb65a3c4412bd8970743864512a1a0e004e8d00fb96277b78b949101611de2565b603f546001600160a01b0316331480611fb25750611fb2612712565b611fce5760405162461bcd60e51b8152600401610a6790613f37565b7f53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535805460011981016120125760405162461bcd60e51b8152600401610a6790614024565b6002825561202387878787876134fd565b50600190555050505050565b603f546001600160a01b031633148061204b575061204b612712565b6120675760405162461bcd60e51b8152600401610a6790613f37565b670de0b6b3a76400008111156120af5760405162461bcd60e51b815260206004820152600d60248201526c496e76616c69642076616c756560981b6044820152606401610a67565b60398190556040518181527f41ecb23a0e7865b25f38c268b7c3012220d822929e9edff07326e89d5bb822b590602001610b24565b603f546001600160a01b03163314806121005750612100612712565b61211c5760405162461bcd60e51b8152600401610a6790613f37565b6037805460ff60a81b191690556040517f891ebab18da80ebeeea06b1b1cede098329c4c008906a98370c2ac7a80b571cb90600090a1565b61215c612712565b6121785760405162461bcd60e51b8152600401610a6790613f7f565b604880546001600160a01b0319166001600160a01b0383169081179091556040519081527f7d7719313229e558c5a3893cad2eb86a86a049156d1d9ebd5c63a8eedefd1c0390602001610b24565b603f546001600160a01b03163314806121e257506121e2612712565b6121fe5760405162461bcd60e51b8152600401610a6790613f37565b7f53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535805460011981016122425760405162461bcd60e51b8152600401610a6790614024565b60028255612023308888888888613724565b61225c612712565b6122785760405162461bcd60e51b8152600401610a6790613f7f565b603a8190556040518181527f2ec5fb5a3d2703edc461252d92ccd2799c3c74f01d97212b20388207fa17ae4590602001610b24565b603f546001600160a01b03163314806122c957506122c9612712565b6122e55760405162461bcd60e51b8152600401610a6790613f37565b306001600160a01b031663af14052c6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561232057600080fd5b505af1158015612334573d6000803e3d6000fd5b5050505060006301e1338060648361234c9190614062565b6123569190614062565b905061236c6201518066b1a2bc2ec50000614062565b8111156123ab5760405162461bcd60e51b815260206004820152600d60248201526c0a4c2e8ca40e8dede40d0d2ced609b1b6044820152606401610a67565b6123b481612ab0565b604f80546001600160401b0392909216600160801b0267ffffffffffffffff60801b199092169190911790556040518181527fef46f143af5fead0010484fe7d6ec2e2972420faa76157f5a6075aa72e614cb590602001611de2565b612418612712565b6124345760405162461bcd60e51b8152600401610a6790613f7f565b603b8190556040518181527f39367850377ac04920a9a670f2180e7a94d83b15ad302e59875ec58fd10bd37d90602001610b24565b603f546001600160a01b03163314806124855750612485612712565b6124a15760405162461bcd60e51b8152600401610a6790613f37565b604080516001600160a01b038085168252831660208201527fba58ce12801c949fa65f41c46ed108671c219baf945fa48d21026cea99ff252a910160405180910390a16001600160a01b0381161561266e576001600160a01b03811660009081526035602052604090205460ff166125535760405162461bcd60e51b815260206004820152601560248201527414dd1c985d1959de481b9bdd08185c1c1c9bdd9959605a1b6044820152606401610a67565b6001600160a01b038216600090815260336020526040902054819060ff166125b65760405162461bcd60e51b8152602060048201526016602482015275105cdcd95d081a5cc81b9bdd081cdd5c1c1bdc9d195960521b6044820152606401610a67565b60405163551c457b60e11b81526001600160a01b03848116600483015282169063aa388af690602401602060405180830381865afa1580156125fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126209190614084565b61266c5760405162461bcd60e51b815260206004820152601f60248201527f4173736574206e6f7420737570706f72746564206279205374726174656779006044820152606401610a67565b505b6001600160a01b03918216600090815260406020819052902080546001600160a01b03191691909216179055565b603f546001600160a01b03163314806126b857506126b8612712565b6126d45760405162461bcd60e51b8152600401610a6790613f37565b6037805460ff60a01b1916600160a01b1790556040517f8cff26a5985614b3d30629cc4ab83824bf115aec971b718d8f2f99562032e97290600090a1565b600061272a6000805160206142ce8339815191525490565b6001600160a01b0316336001600160a01b031614905090565b603f546001600160a01b031633148061275f575061275f612712565b61277b5760405162461bcd60e51b8152600401610a6790613f37565b611ac66138a8565b61278b612712565b6127a75760405162461bcd60e51b8152600401610a6790613f7f565b6127cf817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b03166127ef6000805160206142ce8339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b61282f612712565b61284b5760405162461bcd60e51b8152600401610a6790613f7f565b604580546001600160a01b0319166001600160a01b0383169081179091556040519081527fa12850fb726e0b2b7b3c9a9342031e1268a8148d0eb06b4bea8613204ffcd2b890602001610b24565b6128a1612712565b6128bd5760405162461bcd60e51b8152600401610a6790613f7f565b6127118161ffff16106129095760405162461bcd60e51b8152602060048201526014602482015273496e76616c696420626173697320706f696e747360601b6044820152606401610a67565b6048805461ffff60a01b1916600160a01b61ffff8416908102919091179091556040519081527ff12c00256bee2b6facb111a88a9b1cff86e79132939b44f1353212d6f746955790602001610b24565b612961612712565b61297d5760405162461bcd60e51b8152600401610a6790613f7f565b6103e88111156129d95760405162461bcd60e51b815260206004820152602160248201527f52656465656d206665652073686f756c64206e6f74206265206f7665722031306044820152602560f81b6064820152608401610a67565b60388190556040518181527fd6c7508d6658ccee36b7b7d7fd72e5cbaeefb40c64eff24e9ae7470e846304ee90602001610b24565b612a16612712565b612a325760405162461bcd60e51b8152600401610a6790613f7f565b803b612a8c5760405162461bcd60e51b8152602060048201526024808201527f6e657720696d706c656d656e746174696f6e206973206e6f74206120636f6e746044820152631c9858dd60e21b6064820152608401610a67565b7fa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd955565b60006001600160401b03821115612b185760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203660448201526534206269747360d01b6064820152608401610a67565b5090565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052611611908490613934565b6001600160a01b0381166000908152603360205260409020805462010000900460ff1615612b9a575050565b6000826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612bda573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bfe91906140a6565b905060068160ff1610158015612c18575060128160ff1611155b612c5b5760405162461bcd60e51b81526020600482015260146024820152732ab732bc3832b1ba32b210383932b1b4b9b4b7b760611b6044820152606401610a67565b815460ff909116620100000262ff00001990911617905550565b600081831115612ca557612c9e612c8c8385613fe2565b612c9790600a6141aa565b8590613a06565b9350612ccf565b81831015612ccf57612ccc612cba8484613fe2565b612cc590600a6141aa565b8590613a1b565b93505b50825b9392505050565b6001600160a01b0386166000908152603360209081526040808320815160808101909252805460ff8082161515845285948401916101009004166001811115612d2457612d2461404c565b6001811115612d3557612d3561404c565b8152905462010000810460ff908116602080850191909152630100000090920461ffff166040938401526001600160a01b038b1660009081526033835283812084516080810190955280548084161515865295965090949092840191610100909104166001811115612da957612da961404c565b6001811115612dba57612dba61404c565b8152905462010000810460ff1660208301526301000000900461ffff166040909101528251909150612e2e5760405162461bcd60e51b815260206004820152601b60248201527f46726f6d206173736574206973206e6f7420737570706f7274656400000000006044820152606401610a67565b8051612e7c5760405162461bcd60e51b815260206004820152601960248201527f546f206173736574206973206e6f7420737570706f72746564000000000000006044820152606401610a67565b6040805180820182526048546001600160a01b038082168352600160a01b90910461ffff16602083015291516370a0823160e01b81523060048201529091600091908b16906370a0823190602401602060405180830381865afa158015612ee7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f0b919061400b565b8251909150612f25906001600160a01b038d16908b612b1c565b81516001600160a01b0316632506c0188c8c612f4260018e613fe2565b8c8c8c6040518763ffffffff1660e01b8152600401612f66969594939291906141b6565b6020604051808303816000875af1158015612f85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fa9919061400b565b506040516370a0823160e01b815230600482015281906001600160a01b038c16906370a0823190602401602060405180830381865afa158015612ff0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613014919061400b565b61301e9190613fe2565b945050868410156130715760405162461bcd60e51b815260206004820152601960248201527f5374726174656769737420736c697070616765206c696d6974000000000000006044820152606401610a67565b60008260600151612710613085919061420f565b6037546040516315d5220f60e31b81526001600160a01b038d8116600483015261ffff93909316929091169063aea9107890602401602060405180830381865afa1580156130d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130fb919061400b565b6131059190614229565b6037546040516315d5220f60e31b81526001600160a01b038e811660048301529091169063aea9107890602401602060405180830381865afa15801561314f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613173919061400b565b606086015161318490612710614240565b6131929061ffff168c614229565b61319c9190614229565b6131a69190614062565b90506131c56012856040015160ff1683612c759092919063ffffffff16565b60408401516131db90879060129060ff16612c75565b10156132295760405162461bcd60e51b815260206004820152601e60248201527f4f7261636c6520736c697070616765206c696d697420657863656564656400006044820152606401610a67565b50612710816020015161271061323f9190614240565b61ffff16603c60009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613296573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132ba919061400b565b6132c49190614229565b6132ce9190614062565b306001600160a01b031663d4c3eea06040518163ffffffff1660e01b8152600401602060405180830381865afa15801561330c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613330919061400b565b10156133775760405162461bcd60e51b8152602060048201526016602482015275416c6c6f7765642076616c7565203c20737570706c7960501b6044820152606401610a67565b886001600160a01b03168a6001600160a01b03167fa078c4190abe07940190effc1846be0ccf03ad6007bc9e93f9697d0b460befbb8a876040516133c5929190918252602082015260400190565b60405180910390a35050509695505050505050565b6001600160a01b03811660009081526035602052604090205460ff166134425760405162461bcd60e51b815260206004820152601960248201527f5374726174656779206973206e6f7420737570706f72746564000000000000006044820152606401610a67565b6000819050806001600160a01b031663853828b66040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561348257600080fd5b505af1158015613496573d6000803e3d6000fd5b505050505050565b6001600160a01b0381166134f45760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f7220697320616464726573732830290000000000006044820152606401610a67565b6111d281613a27565b6001600160a01b03851660009081526035602052604090205460ff1661355b5760405162461bcd60e51b8152602060048201526013602482015272496e76616c696420746f20537472617465677960681b6044820152606401610a67565b8281146135a65760405162461bcd60e51b81526020600482015260196024820152780a0c2e4c2dacae8cae440d8cadccee8d040dad2e6dac2e8c6d603b1b6044820152606401610a67565b8260005b818110156136c85760008686838181106135c6576135c6613fb6565b90506020020160208101906135db9190613cd6565b60405163551c457b60e11b81526001600160a01b0380831660048301529192509089169063aa388af690602401602060405180830381865afa158015613625573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136499190614084565b6136895760405162461bcd60e51b8152602060048201526011602482015270105cdcd95d081d5b9cdd5c1c1bdc9d1959607a1b6044820152606401610a67565b6136bf8886868581811061369f5761369f613fb6565b90506020020135836001600160a01b0316612b1c9092919063ffffffff16565b506001016135aa565b50856001600160a01b031663de5f62686040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561370457600080fd5b505af1158015613718573d6000803e3d6000fd5b50505050505050505050565b6001600160a01b03851660009081526035602052604090205460ff166137845760405162461bcd60e51b8152602060048201526015602482015274496e76616c69642066726f6d20537472617465677960581b6044820152606401610a67565b8281146137cf5760405162461bcd60e51b81526020600482015260196024820152780a0c2e4c2dacae8cae440d8cadccee8d040dad2e6dac2e8c6d603b1b6044820152606401610a67565b8260005b8181101561389e57866001600160a01b031663d9caed12898888858181106137fd576137fd613fb6565b90506020020160208101906138129190613cd6565b87878681811061382457613824613fb6565b6040516001600160e01b031960e088901b1681526001600160a01b03958616600482015294909316602485015250602090910201356044820152606401600060405180830381600087803b15801561387b57600080fd5b505af115801561388f573d6000803e3d6000fd5b505050508060010190506137d3565b5050505050505050565b60365460005b81811015610d1057603681815481106138c9576138c9613fb6565b60009182526020822001546040805163429c145b60e11b815290516001600160a01b039092169263853828b69260048084019382900301818387803b15801561391157600080fd5b505af1158015613925573d6000803e3d6000fd5b505050508060010190506138ae565b6000613989826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613a8e9092919063ffffffff16565b80519091501561161157808060200190518101906139a79190614084565b6116115760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610a67565b6000613a128284614229565b90505b92915050565b6000613a128284614062565b806001600160a01b0316613a476000805160206142ce8339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a36000805160206142ce83398151915255565b6060613a9d8484600085613aa5565b949350505050565b606082471015613b065760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610a67565b843b613b545760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610a67565b600080866001600160a01b03168587604051613b70919061427e565b60006040518083038185875af1925050503d8060008114613bad576040519150601f19603f3d011682016040523d82523d6000602084013e613bb2565b606091505b5091509150613bc2828286613bcd565b979650505050505050565b60608315613bdc575081612cd2565b825115613bec5782518084602001fd5b8160405162461bcd60e51b8152600401610a67919061429a565b828054828255906000526020600020908101928215613c5b579160200282015b82811115613c5b57825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190613c26565b50612b189291505b80821115612b185760008155600101613c63565b600060208284031215613c8957600080fd5b5035919050565b80356001600160a01b0381168114613ca757600080fd5b919050565b60008060408385031215613cbf57600080fd5b613cc883613c90565b946020939093013593505050565b600060208284031215613ce857600080fd5b613a1282613c90565b60008060408385031215613d0457600080fd5b613d0d83613c90565b9150613d1b60208401613c90565b90509250929050565b60008060008060008060a08789031215613d3d57600080fd5b613d4687613c90565b9550613d5460208801613c90565b9450604087013593506060870135925060808701356001600160401b03811115613d7d57600080fd5b8701601f81018913613d8e57600080fd5b80356001600160401b03811115613da457600080fd5b896020828401011115613db657600080fd5b60208201935080925050509295509295509295565b60ff811681146111d257600080fd5b60008060408385031215613ded57600080fd5b613df683613c90565b91506020830135613e0681613dcb565b809150509250929050565b803561ffff81168114613ca757600080fd5b60008060408385031215613e3657600080fd5b613e3f83613c90565b9150613d1b60208401613e11565b60008083601f840112613e5f57600080fd5b5081356001600160401b03811115613e7657600080fd5b6020830191508360208260051b8501011115613e9157600080fd5b9250929050565b600080600080600060608688031215613eb057600080fd5b613eb986613c90565b945060208601356001600160401b03811115613ed457600080fd5b613ee088828901613e4d565b90955093505060408601356001600160401b03811115613eff57600080fd5b613f0b88828901613e4d565b969995985093965092949392505050565b600060208284031215613f2e57600080fd5b613a1282613e11565b60208082526028908201527f43616c6c6572206973206e6f74207468652053747261746567697374206f722060408201526723b7bb32b93737b960c11b606082015260800190565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b81810381811115613a1557613a15613fcc565b634e487b7160e01b600052603160045260246000fd5b60006020828403121561401d57600080fd5b5051919050565b6020808252600e908201526d1499595b9d1c985b9d0818d85b1b60921b604082015260600190565b634e487b7160e01b600052602160045260246000fd5b60008261407f57634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561409657600080fd5b81518015158114612cd257600080fd5b6000602082840312156140b857600080fd5b8151612cd281613dcb565b6001815b60018411156140fe578085048111156140e2576140e2613fcc565b60018416156140f057908102905b60019390931c9280026140c7565b935093915050565b60008261411557506001613a15565b8161412257506000613a15565b816001811461413857600281146141425761415e565b6001915050613a15565b60ff84111561415357614153613fcc565b50506001821b613a15565b5060208310610133831016604e8410600b8410161715614181575081810a613a15565b61418e60001984846140c3565b80600019048211156141a2576141a2613fcc565b029392505050565b6000613a128383614106565b6001600160a01b03878116825286166020820152604081018590526060810184905260a0608082018190528101829052818360c0830137600081830160c090810191909152601f909201601f1916010195945050505050565b61ffff8181168382160190811115613a1557613a15613fcc565b8082028115828204841417613a1557613a15613fcc565b61ffff8281168282160390811115613a1557613a15613fcc565b60005b8381101561427557818101518382015260200161425d565b50506000910152565b6000825161429081846020870161425a565b9190910192915050565b60208152600082518060208401526142b981604085016020870161425a565b601f01601f1916919091016040019291505056fe7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa2646970667358221220def484d631fc2a0160a72b9d28868823bec0086bf2972aeb9358c455da27103564736f6c634300081c0033", + "libraries": {}, + "devdoc": { + "author": "Origin Protocol Inc", + "kind": "dev", + "methods": { + "allowedSwapUndervalue()": { + "returns": { + "value": "Percentage in basis points." + } + }, + "approveStrategy(address)": { + "params": { + "_addr": "Address of the strategy to add" + } + }, + "cacheDecimals(address)": { + "params": { + "_asset": "Address of asset token" + } + }, + "depositToStrategy(address,address[],uint256[])": { + "params": { + "_amounts": "Array of amounts of each corresponding asset to deposit.", + "_assets": "Array of asset address that will be deposited into the strategy.", + "_strategyToAddress": "Address of the Strategy to deposit assets into." + } + }, + "removeAsset(address)": { + "params": { + "_asset": "Address of asset" + } + }, + "removeStrategy(address)": { + "params": { + "_addr": "Address of the strategy to remove" + } + }, + "setAdminImpl(address)": { + "params": { + "newImpl": "address of the implementation" + } + }, + "setAssetDefaultStrategy(address,address)": { + "params": { + "_asset": "Address of the asset", + "_strategy": "Address of the Strategy" + } + }, + "setAutoAllocateThreshold(uint256)": { + "params": { + "_threshold": "OToken amount with 18 fixed decimals." + } + }, + "setDripDuration(uint256)": { + "params": { + "_dripDuration": "Time in seconds to target a constant yield rate" + } + }, + "setDripper(address)": { + "params": { + "_dripper": "Address of the Dripper contract." + } + }, + "setNetOusdMintForStrategyThreshold(uint256)": { + "params": { + "_threshold": "OToken amount with 18 fixed decimals." + } + }, + "setOracleSlippage(address,uint16)": { + "params": { + "_allowedOracleSlippageBps": "allowed slippage from Oracle in basis points. eg 20 = 0.2%. Max 10%.", + "_asset": "Address of the asset token." + } + }, + "setOusdMetaStrategy(address)": { + "params": { + "_ousdMetaStrategy": "Address of OToken metapool strategy" + } + }, + "setPriceProvider(address)": { + "params": { + "_priceProvider": "Address of price provider" + } + }, + "setRebaseRateMax(uint256)": { + "params": { + "yearlyApr": "in 1e18 notation. 3 * 1e18 = 3% APR" + } + }, + "setRebaseThreshold(uint256)": { + "params": { + "_threshold": "OToken amount with 18 fixed decimals." + } + }, + "setRedeemFeeBps(uint256)": { + "params": { + "_redeemFeeBps": "Basis point fee to be charged" + } + }, + "setStrategistAddr(address)": { + "params": { + "_address": "Address of Strategist" + } + }, + "setSwapAllowedUndervalue(uint16)": { + "params": { + "_basis": "Percentage in basis points. eg 100 == 1%" + } + }, + "setSwapper(address)": { + "params": { + "_swapperAddr": "Address of the Swapper contract that implements the ISwapper interface." + } + }, + "setVaultBuffer(uint256)": { + "params": { + "_vaultBuffer": "Percentage using 18 decimals. 100% = 1e18." + } + }, + "setWithdrawalClaimDelay(uint256)": { + "params": { + "_delay": "Delay period (should be between 10 mins to 7 days). Set to 0 to disable async withdrawals" + } + }, + "supportAsset(address,uint8)": { + "params": { + "_asset": "Address of asset" + } + }, + "swapCollateral(address,address,uint256,uint256,bytes)": { + "params": { + "_data": "implementation specific data. eg 1Inch swap data", + "_fromAsset": "The token address of the asset being sold by the vault.", + "_fromAssetAmount": "The amount of assets being sold by the vault.", + "_minToAssetAmount": "The minimum amount of assets to be purchased.", + "_toAsset": "The token address of the asset being purchased by the vault." + }, + "returns": { + "toAssetAmount": "The amount of toAssets that was received from the swap" + } + }, + "transferGovernance(address)": { + "params": { + "_newGovernor": "Address of the new Governor" + } + }, + "transferToken(address,uint256)": { + "params": { + "_amount": "Amount of the asset to transfer", + "_asset": "Address for the asset" + } + }, + "withdrawAllFromStrategy(address)": { + "params": { + "_strategyAddr": "Strategy address." + } + }, + "withdrawFromStrategy(address,address[],uint256[])": { + "params": { + "_amounts": "Array of amounts of each corresponding asset to withdraw.", + "_assets": "Array of asset address that will be withdrawn from the strategy.", + "_strategyFromAddress": "Address of the Strategy to withdraw assets from." + } + } + }, + "title": "OETH Vault Contract", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "allowedSwapUndervalue()": { + "notice": "Max allowed percentage the vault total value can drop below the OToken total supply in basis points when executing a collateral swap. For example 100 == 1%" + }, + "approveStrategy(address)": { + "notice": "Add a strategy to the Vault." + }, + "assetDefaultStrategies(address)": { + "notice": "Mapping of asset address to the Strategy that they should automatically" + }, + "autoAllocateThreshold()": { + "notice": "OToken mints over this amount automatically allocate funds. 18 decimals." + }, + "cacheDecimals(address)": { + "notice": "Cache decimals on OracleRouter for a particular asset. This action is required before that asset's price can be accessed." + }, + "capitalPaused()": { + "notice": "pause operations that change the OToken supply. eg mint, redeem, allocate, mint/burn for strategy" + }, + "claimGovernance()": { + "notice": "Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor." + }, + "depositToStrategy(address,address[],uint256[])": { + "notice": "Deposit multiple assets from the vault into the strategy." + }, + "dripDuration()": { + "notice": "Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable." + }, + "dripper()": { + "notice": "Address of the Dripper contract that streams harvested rewards to the Vault" + }, + "governor()": { + "notice": "Returns the address of the current Governor." + }, + "isGovernor()": { + "notice": "Returns true if the caller is the current Governor." + }, + "lastRebase()": { + "notice": "Time in seconds that the vault last rebased yield." + }, + "maxSupplyDiff()": { + "notice": "Max difference between total supply and total value of assets. 18 decimals." + }, + "netOusdMintForStrategyThreshold()": { + "notice": "How much net total OTokens are allowed to be minted by all strategies" + }, + "netOusdMintedForStrategy()": { + "notice": "How much OTokens are currently minted by the strategy" + }, + "ousdMetaStrategy()": { + "notice": "Metapool strategy that is allowed to mint/burn OTokens without changing collateral" + }, + "pauseCapital()": { + "notice": "Set the deposit paused flag to true to prevent capital movement." + }, + "pauseRebase()": { + "notice": "Set the deposit paused flag to true to prevent rebasing." + }, + "priceProvider()": { + "notice": "Address of the Oracle price provider contract" + }, + "rebasePaused()": { + "notice": "pause rebasing if true" + }, + "rebasePerSecondMax()": { + "notice": "max rebase percentage per second Can be used to set maximum yield of the protocol, spreading out yield over time" + }, + "rebasePerSecondTarget()": { + "notice": "target rebase rate limit, based on past rates and funds available." + }, + "rebaseThreshold()": { + "notice": "OToken mints over this amount automatically rebase. 18 decimals." + }, + "redeemFeeBps()": { + "notice": "Redemption fee in basis points. eg 50 = 0.5%" + }, + "removeAsset(address)": { + "notice": "Remove a supported asset from the Vault" + }, + "removeStrategy(address)": { + "notice": "Remove a strategy from the Vault." + }, + "setAdminImpl(address)": { + "notice": "set the implementation for the admin, this needs to be in a base class else we cannot set it" + }, + "setAssetDefaultStrategy(address,address)": { + "notice": "Set the default Strategy for an asset, i.e. the one which the asset will be automatically allocated to and withdrawn from" + }, + "setAutoAllocateThreshold(uint256)": { + "notice": "Sets the minimum amount of OTokens in a mint to trigger an automatic allocation of funds afterwords." + }, + "setDripDuration(uint256)": { + "notice": "Set the drip duration period" + }, + "setDripper(address)": { + "notice": "Set the Dripper contract that streams harvested rewards to the vault." + }, + "setMaxSupplyDiff(uint256)": { + "notice": "Sets the maximum allowable difference between total supply and backing assets' value." + }, + "setNetOusdMintForStrategyThreshold(uint256)": { + "notice": "Set maximum amount of OTokens that can at any point be minted and deployed to strategy (used only by ConvexOUSDMetaStrategy for now)." + }, + "setOracleSlippage(address,uint16)": { + "notice": "Set the allowed slippage from the Oracle price for collateral asset swaps." + }, + "setOusdMetaStrategy(address)": { + "notice": "Set OToken Metapool strategy" + }, + "setPriceProvider(address)": { + "notice": "Set address of price provider." + }, + "setRebaseRateMax(uint256)": { + "notice": "Set a yield streaming max rate. This spreads yield over time if it is above the max rate." + }, + "setRebaseThreshold(uint256)": { + "notice": "Set a minimum amount of OTokens in a mint or redeem that triggers a rebase" + }, + "setRedeemFeeBps(uint256)": { + "notice": "Set a fee in basis points to be charged for a redeem." + }, + "setStrategistAddr(address)": { + "notice": "Set address of Strategist" + }, + "setSwapAllowedUndervalue(uint16)": { + "notice": "Set max allowed percentage the vault total value can drop below the OToken total supply in basis points when executing collateral swaps." + }, + "setSwapper(address)": { + "notice": "Set the contract the performs swaps of collateral assets." + }, + "setTrusteeAddress(address)": { + "notice": "Sets the trusteeAddress that can receive a portion of yield. Setting to the zero address disables this feature." + }, + "setTrusteeFeeBps(uint256)": { + "notice": "Sets the TrusteeFeeBps to the percentage of yield that should be received in basis points." + }, + "setVaultBuffer(uint256)": { + "notice": "Set a buffer of assets to keep in the Vault to handle most redemptions without needing to spend gas unwinding assets from a Strategy." + }, + "setWithdrawalClaimDelay(uint256)": { + "notice": "Changes the async withdrawal claim period for OETH & superOETHb" + }, + "strategistAddr()": { + "notice": "Address of the Strategist" + }, + "supportAsset(address,uint8)": { + "notice": "Add a supported asset to the contract, i.e. one that can be to mint OTokens." + }, + "swapCollateral(address,address,uint256,uint256,bytes)": { + "notice": "Strategist swaps collateral assets sitting in the vault." + }, + "swapper()": { + "notice": "Contract that swaps the vault's collateral assets" + }, + "transferGovernance(address)": { + "notice": "Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete" + }, + "transferToken(address,uint256)": { + "notice": "Transfer token to governor. Intended for recovering tokens stuck in contract, i.e. mistaken sends." + }, + "trusteeAddress()": { + "notice": "Trustee contract that can collect a percentage of yield" + }, + "trusteeFeeBps()": { + "notice": "Amount of yield collected in basis points. eg 2000 = 20%" + }, + "unpauseCapital()": { + "notice": "Set the deposit paused flag to false to enable capital movement." + }, + "unpauseRebase()": { + "notice": "Set the deposit paused flag to true to allow rebasing." + }, + "vaultBuffer()": { + "notice": "Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18." + }, + "withdrawAllFromStrategies()": { + "notice": "Withdraws all assets from all the strategies and sends assets to the Vault." + }, + "withdrawAllFromStrategy(address)": { + "notice": "Withdraws all assets from the strategy and sends assets to the Vault." + }, + "withdrawFromStrategy(address,address[],uint256[])": { + "notice": "Withdraw multiple assets from the strategy to the vault." + }, + "withdrawalClaimDelay()": { + "notice": "Sets a minimum delay that is required to elapse between requesting async withdrawals and claiming the request. When set to 0 async withdrawals are disabled." + }, + "withdrawalQueueMetadata()": { + "notice": "Global metadata for the withdrawal queue including: queued - cumulative total of all withdrawal requests included the ones that have already been claimed claimable - cumulative total of all the requests that can be claimed including the ones already claimed claimed - total of all the requests that have been claimed nextWithdrawalIndex - index of the next withdrawal request starting at 0" + }, + "withdrawalRequests(uint256)": { + "notice": "Mapping of withdrawal request indices to the user withdrawal request data" + } + }, + "version": 1 + }, + "storageLayout": { + "storage": [ + { + "astId": 53250, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "initialized", + "offset": 0, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 53253, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "initializing", + "offset": 1, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 53293, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "______gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage" + }, + { + "astId": 60484, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "assets", + "offset": 0, + "slot": "51", + "type": "t_mapping(t_address,t_struct(Asset)60478_storage)" + }, + { + "astId": 60488, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "allAssets", + "offset": 0, + "slot": "52", + "type": "t_array(t_address)dyn_storage" + }, + { + "astId": 60499, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "strategies", + "offset": 0, + "slot": "53", + "type": "t_mapping(t_address,t_struct(Strategy)60493_storage)" + }, + { + "astId": 60503, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "allStrategies", + "offset": 0, + "slot": "54", + "type": "t_array(t_address)dyn_storage" + }, + { + "astId": 60506, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "priceProvider", + "offset": 0, + "slot": "55", + "type": "t_address" + }, + { + "astId": 60509, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "rebasePaused", + "offset": 20, + "slot": "55", + "type": "t_bool" + }, + { + "astId": 60512, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "capitalPaused", + "offset": 21, + "slot": "55", + "type": "t_bool" + }, + { + "astId": 60515, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "redeemFeeBps", + "offset": 0, + "slot": "56", + "type": "t_uint256" + }, + { + "astId": 60518, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "vaultBuffer", + "offset": 0, + "slot": "57", + "type": "t_uint256" + }, + { + "astId": 60521, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "autoAllocateThreshold", + "offset": 0, + "slot": "58", + "type": "t_uint256" + }, + { + "astId": 60524, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "rebaseThreshold", + "offset": 0, + "slot": "59", + "type": "t_uint256" + }, + { + "astId": 60528, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "oUSD", + "offset": 0, + "slot": "60", + "type": "t_contract(OUSD)52220" + }, + { + "astId": 60539, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "_deprecated_rebaseHooksAddr", + "offset": 0, + "slot": "61", + "type": "t_address" + }, + { + "astId": 60546, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "_deprecated_uniswapAddr", + "offset": 0, + "slot": "62", + "type": "t_address" + }, + { + "astId": 60553, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "strategistAddr", + "offset": 0, + "slot": "63", + "type": "t_address" + }, + { + "astId": 60558, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "assetDefaultStrategies", + "offset": 0, + "slot": "64", + "type": "t_mapping(t_address,t_address)" + }, + { + "astId": 60561, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "maxSupplyDiff", + "offset": 0, + "slot": "65", + "type": "t_uint256" + }, + { + "astId": 60564, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "trusteeAddress", + "offset": 0, + "slot": "66", + "type": "t_address" + }, + { + "astId": 60567, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "trusteeFeeBps", + "offset": 0, + "slot": "67", + "type": "t_uint256" + }, + { + "astId": 60571, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "_deprecated_swapTokens", + "offset": 0, + "slot": "68", + "type": "t_array(t_address)dyn_storage" + }, + { + "astId": 60577, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "ousdMetaStrategy", + "offset": 0, + "slot": "69", + "type": "t_address" + }, + { + "astId": 60580, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "netOusdMintedForStrategy", + "offset": 0, + "slot": "70", + "type": "t_int256" + }, + { + "astId": 60583, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "netOusdMintForStrategyThreshold", + "offset": 0, + "slot": "71", + "type": "t_uint256" + }, + { + "astId": 60605, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "swapConfig", + "offset": 0, + "slot": "72", + "type": "t_struct(SwapConfig)60595_storage" + }, + { + "astId": 60609, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "isMintWhitelistedStrategy", + "offset": 0, + "slot": "73", + "type": "t_mapping(t_address,t_bool)" + }, + { + "astId": 60612, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "dripper", + "offset": 0, + "slot": "74", + "type": "t_address" + }, + { + "astId": 60626, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "withdrawalQueueMetadata", + "offset": 0, + "slot": "75", + "type": "t_struct(WithdrawalQueueMetadata)60622_storage" + }, + { + "astId": 60643, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "withdrawalRequests", + "offset": 0, + "slot": "77", + "type": "t_mapping(t_uint256,t_struct(WithdrawalRequest)60637_storage)" + }, + { + "astId": 60646, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "withdrawalClaimDelay", + "offset": 0, + "slot": "78", + "type": "t_uint256" + }, + { + "astId": 60649, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "lastRebase", + "offset": 0, + "slot": "79", + "type": "t_uint64" + }, + { + "astId": 60652, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "dripDuration", + "offset": 8, + "slot": "79", + "type": "t_uint64" + }, + { + "astId": 60655, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "rebasePerSecondMax", + "offset": 16, + "slot": "79", + "type": "t_uint64" + }, + { + "astId": 60658, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "rebasePerSecondTarget", + "offset": 24, + "slot": "79", + "type": "t_uint64" + }, + { + "astId": 60673, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "__gap", + "offset": 0, + "slot": "80", + "type": "t_array(t_uint256)43_storage" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_address)dyn_storage": { + "base": "t_address", + "encoding": "dynamic_array", + "label": "address[]", + "numberOfBytes": "32" + }, + "t_array(t_uint256)43_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[43]", + "numberOfBytes": "1376" + }, + "t_array(t_uint256)50_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "encoding": "inplace", + "label": "bool", + "numberOfBytes": "1" + }, + "t_contract(OUSD)52220": { + "encoding": "inplace", + "label": "contract OUSD", + "numberOfBytes": "20" + }, + "t_enum(UnitConversion)60468": { + "encoding": "inplace", + "label": "enum VaultStorage.UnitConversion", + "numberOfBytes": "1" + }, + "t_int256": { + "encoding": "inplace", + "label": "int256", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_address)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => address)", + "numberOfBytes": "32", + "value": "t_address" + }, + "t_mapping(t_address,t_bool)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => bool)", + "numberOfBytes": "32", + "value": "t_bool" + }, + "t_mapping(t_address,t_struct(Asset)60478_storage)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => struct VaultStorage.Asset)", + "numberOfBytes": "32", + "value": "t_struct(Asset)60478_storage" + }, + "t_mapping(t_address,t_struct(Strategy)60493_storage)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => struct VaultStorage.Strategy)", + "numberOfBytes": "32", + "value": "t_struct(Strategy)60493_storage" + }, + "t_mapping(t_uint256,t_struct(WithdrawalRequest)60637_storage)": { + "encoding": "mapping", + "key": "t_uint256", + "label": "mapping(uint256 => struct VaultStorage.WithdrawalRequest)", + "numberOfBytes": "32", + "value": "t_struct(WithdrawalRequest)60637_storage" + }, + "t_struct(Asset)60478_storage": { + "encoding": "inplace", + "label": "struct VaultStorage.Asset", + "members": [ + { + "astId": 60470, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "isSupported", + "offset": 0, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 60473, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "unitConversion", + "offset": 1, + "slot": "0", + "type": "t_enum(UnitConversion)60468" + }, + { + "astId": 60475, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "decimals", + "offset": 2, + "slot": "0", + "type": "t_uint8" + }, + { + "astId": 60477, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "allowedOracleSlippageBps", + "offset": 3, + "slot": "0", + "type": "t_uint16" + } + ], + "numberOfBytes": "32" + }, + "t_struct(Strategy)60493_storage": { + "encoding": "inplace", + "label": "struct VaultStorage.Strategy", + "members": [ + { + "astId": 60490, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "isSupported", + "offset": 0, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 60492, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "_deprecated", + "offset": 0, + "slot": "1", + "type": "t_uint256" + } + ], + "numberOfBytes": "64" + }, + "t_struct(SwapConfig)60595_storage": { + "encoding": "inplace", + "label": "struct VaultStorage.SwapConfig", + "members": [ + { + "astId": 60592, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "swapper", + "offset": 0, + "slot": "0", + "type": "t_address" + }, + { + "astId": 60594, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "allowedUndervalueBps", + "offset": 20, + "slot": "0", + "type": "t_uint16" + } + ], + "numberOfBytes": "32" + }, + "t_struct(WithdrawalQueueMetadata)60622_storage": { + "encoding": "inplace", + "label": "struct VaultStorage.WithdrawalQueueMetadata", + "members": [ + { + "astId": 60615, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "queued", + "offset": 0, + "slot": "0", + "type": "t_uint128" + }, + { + "astId": 60617, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "claimable", + "offset": 16, + "slot": "0", + "type": "t_uint128" + }, + { + "astId": 60619, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "claimed", + "offset": 0, + "slot": "1", + "type": "t_uint128" + }, + { + "astId": 60621, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "nextWithdrawalIndex", + "offset": 16, + "slot": "1", + "type": "t_uint128" + } + ], + "numberOfBytes": "64" + }, + "t_struct(WithdrawalRequest)60637_storage": { + "encoding": "inplace", + "label": "struct VaultStorage.WithdrawalRequest", + "members": [ + { + "astId": 60628, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "withdrawer", + "offset": 0, + "slot": "0", + "type": "t_address" + }, + { + "astId": 60630, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "claimed", + "offset": 20, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 60632, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "timestamp", + "offset": 21, + "slot": "0", + "type": "t_uint40" + }, + { + "astId": 60634, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "amount", + "offset": 0, + "slot": "1", + "type": "t_uint128" + }, + { + "astId": 60636, + "contract": "contracts/vault/OETHVault.sol:OETHVault", + "label": "queued", + "offset": 16, + "slot": "1", + "type": "t_uint128" + } + ], + "numberOfBytes": "64" + }, + "t_uint128": { + "encoding": "inplace", + "label": "uint128", + "numberOfBytes": "16" + }, + "t_uint16": { + "encoding": "inplace", + "label": "uint16", + "numberOfBytes": "2" + }, + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint40": { + "encoding": "inplace", + "label": "uint40", + "numberOfBytes": "5" + }, + "t_uint64": { + "encoding": "inplace", + "label": "uint64", + "numberOfBytes": "8" + }, + "t_uint8": { + "encoding": "inplace", + "label": "uint8", + "numberOfBytes": "1" + } + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/OETHVaultAdmin.json b/contracts/deployments/hoodi/OETHVaultAdmin.json new file mode 100644 index 0000000000..2a12a2b068 --- /dev/null +++ b/contracts/deployments/hoodi/OETHVaultAdmin.json @@ -0,0 +1,2563 @@ +{ + "address": "0x43B3BCe874EC872EFbCC784c1e3CD03005E529a9", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_weth", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_threshold", + "type": "uint256" + } + ], + "name": "AllocateThresholdUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_strategy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "AssetAllocated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_strategy", + "type": "address" + } + ], + "name": "AssetDefaultStrategyUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_asset", + "type": "address" + } + ], + "name": "AssetRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_asset", + "type": "address" + } + ], + "name": "AssetSupported", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CapitalPaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CapitalUnpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "dripDuration", + "type": "uint256" + } + ], + "name": "DripDurationChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "GovernorshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "maxSupplyDiff", + "type": "uint256" + } + ], + "name": "MaxSupplyDiffChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_addr", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_threshold", + "type": "uint256" + } + ], + "name": "NetOusdMintForStrategyThresholdChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_ousdMetaStrategy", + "type": "address" + } + ], + "name": "OusdMetaStrategyUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "PendingGovernorshipTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_priceProvider", + "type": "address" + } + ], + "name": "PriceProviderUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "RebasePaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "rebaseRatePerSecond", + "type": "uint256" + } + ], + "name": "RebasePerSecondMaxChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_threshold", + "type": "uint256" + } + ], + "name": "RebaseThresholdUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "RebaseUnpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_addr", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "Redeem", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_redeemFeeBps", + "type": "uint256" + } + ], + "name": "RedeemFeeUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "StrategistUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "strategy", + "type": "address" + } + ], + "name": "StrategyAddedToMintWhitelist", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_addr", + "type": "address" + } + ], + "name": "StrategyApproved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_addr", + "type": "address" + } + ], + "name": "StrategyRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "strategy", + "type": "address" + } + ], + "name": "StrategyRemovedFromMintWhitelist", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_basis", + "type": "uint256" + } + ], + "name": "SwapAllowedUndervalueChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_basis", + "type": "uint256" + } + ], + "name": "SwapSlippageChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_fromAsset", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_toAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_fromAssetAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_toAssetAmount", + "type": "uint256" + } + ], + "name": "Swapped", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "SwapperChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "TrusteeAddressChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_basis", + "type": "uint256" + } + ], + "name": "TrusteeFeeBpsChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_vaultBuffer", + "type": "uint256" + } + ], + "name": "VaultBufferUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_newDelay", + "type": "uint256" + } + ], + "name": "WithdrawalClaimDelayUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_claimable", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_newClaimable", + "type": "uint256" + } + ], + "name": "WithdrawalClaimable", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_withdrawer", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "_requestId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "WithdrawalClaimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_withdrawer", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "_requestId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_queued", + "type": "uint256" + } + ], + "name": "WithdrawalRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_yield", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_fee", + "type": "uint256" + } + ], + "name": "YieldDistribution", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "strategyAddr", + "type": "address" + } + ], + "name": "addStrategyToMintWhitelist", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "adminImplPosition", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "allowedSwapUndervalue", + "outputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_addr", + "type": "address" + } + ], + "name": "approveStrategy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "assetDefaultStrategies", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "autoAllocateThreshold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + } + ], + "name": "cacheDecimals", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "capitalPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "claimGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_strategyToAddress", + "type": "address" + }, + { + "internalType": "address[]", + "name": "_assets", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "_amounts", + "type": "uint256[]" + } + ], + "name": "depositToStrategy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "dripDuration", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "governor", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isGovernor", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isMintWhitelistedStrategy", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastRebase", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxSupplyDiff", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "netOusdMintForStrategyThreshold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "netOusdMintedForStrategy", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "oUSD", + "outputs": [ + { + "internalType": "contract OUSD", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ousdMetaStrategy", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pauseCapital", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "pauseRebase", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "priceProvider", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rebasePaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rebasePerSecondMax", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rebasePerSecondTarget", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rebaseThreshold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "redeemFeeBps", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + } + ], + "name": "removeAsset", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_addr", + "type": "address" + } + ], + "name": "removeStrategy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "strategyAddr", + "type": "address" + } + ], + "name": "removeStrategyFromMintWhitelist", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImpl", + "type": "address" + } + ], + "name": "setAdminImpl", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "internalType": "address", + "name": "_strategy", + "type": "address" + } + ], + "name": "setAssetDefaultStrategy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_threshold", + "type": "uint256" + } + ], + "name": "setAutoAllocateThreshold", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_dripDuration", + "type": "uint256" + } + ], + "name": "setDripDuration", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_maxSupplyDiff", + "type": "uint256" + } + ], + "name": "setMaxSupplyDiff", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_threshold", + "type": "uint256" + } + ], + "name": "setNetOusdMintForStrategyThreshold", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "internalType": "uint16", + "name": "_allowedOracleSlippageBps", + "type": "uint16" + } + ], + "name": "setOracleSlippage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_ousdMetaStrategy", + "type": "address" + } + ], + "name": "setOusdMetaStrategy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_priceProvider", + "type": "address" + } + ], + "name": "setPriceProvider", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "yearlyApr", + "type": "uint256" + } + ], + "name": "setRebaseRateMax", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_threshold", + "type": "uint256" + } + ], + "name": "setRebaseThreshold", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_redeemFeeBps", + "type": "uint256" + } + ], + "name": "setRedeemFeeBps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "setStrategistAddr", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "_basis", + "type": "uint16" + } + ], + "name": "setSwapAllowedUndervalue", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_swapperAddr", + "type": "address" + } + ], + "name": "setSwapper", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "setTrusteeAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_basis", + "type": "uint256" + } + ], + "name": "setTrusteeFeeBps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_vaultBuffer", + "type": "uint256" + } + ], + "name": "setVaultBuffer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_delay", + "type": "uint256" + } + ], + "name": "setWithdrawalClaimDelay", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "strategies", + "outputs": [ + { + "internalType": "bool", + "name": "isSupported", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "_deprecated", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "strategistAddr", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "internalType": "uint8", + "name": "_unitConversion", + "type": "uint8" + } + ], + "name": "supportAsset", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_fromAsset", + "type": "address" + }, + { + "internalType": "address", + "name": "_toAsset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_fromAssetAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minToAssetAmount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "swapCollateral", + "outputs": [ + { + "internalType": "uint256", + "name": "toAssetAmount", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "swapper", + "outputs": [ + { + "internalType": "address", + "name": "swapper_", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newGovernor", + "type": "address" + } + ], + "name": "transferGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "transferToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "trusteeAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "trusteeFeeBps", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "unpauseCapital", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpauseRebase", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "vaultBuffer", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "weth", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawAllFromStrategies", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_strategyAddr", + "type": "address" + } + ], + "name": "withdrawAllFromStrategy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_strategyFromAddress", + "type": "address" + }, + { + "internalType": "address[]", + "name": "_assets", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "_amounts", + "type": "uint256[]" + } + ], + "name": "withdrawFromStrategy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawalClaimDelay", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawalQueueMetadata", + "outputs": [ + { + "internalType": "uint128", + "name": "queued", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "claimable", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "claimed", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "nextWithdrawalIndex", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "withdrawalRequests", + "outputs": [ + { + "internalType": "address", + "name": "withdrawer", + "type": "address" + }, + { + "internalType": "bool", + "name": "claimed", + "type": "bool" + }, + { + "internalType": "uint40", + "name": "timestamp", + "type": "uint40" + }, + { + "internalType": "uint128", + "name": "amount", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "queued", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "transactionHash": "0x394c3704632d244cbf6bde7d4c37c693f33450f6a73e7bd181a1c0278a939de7", + "receipt": { + "to": null, + "from": "0xf7749B41db006860cEc0650D18b8013d69C44Eeb", + "contractAddress": "0x43B3BCe874EC872EFbCC784c1e3CD03005E529a9", + "transactionIndex": 23, + "gasUsed": "3441200", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0xad4df9f590d147c83ab68fad46ba87144cab43e76d5d8f163fb5a50c409fe6c1", + "transactionHash": "0x394c3704632d244cbf6bde7d4c37c693f33450f6a73e7bd181a1c0278a939de7", + "logs": [], + "blockNumber": 904839, + "cumulativeGasUsed": "26526683", + "status": 1, + "byzantium": true + }, + "args": [ + "0x2387fD72C1DA19f6486B843F5da562679FbB4057" + ], + "numDeployments": 2, + "solcInputHash": "3de98f56666780054699342674f08b14", + "metadata": "{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_weth\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_threshold\",\"type\":\"uint256\"}],\"name\":\"AllocateThresholdUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_strategy\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"AssetAllocated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_strategy\",\"type\":\"address\"}],\"name\":\"AssetDefaultStrategyUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"}],\"name\":\"AssetRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"}],\"name\":\"AssetSupported\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"CapitalPaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"CapitalUnpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"dripDuration\",\"type\":\"uint256\"}],\"name\":\"DripDurationChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"GovernorshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"maxSupplyDiff\",\"type\":\"uint256\"}],\"name\":\"MaxSupplyDiffChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Mint\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_threshold\",\"type\":\"uint256\"}],\"name\":\"NetOusdMintForStrategyThresholdChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_ousdMetaStrategy\",\"type\":\"address\"}],\"name\":\"OusdMetaStrategyUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"PendingGovernorshipTransfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_priceProvider\",\"type\":\"address\"}],\"name\":\"PriceProviderUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"RebasePaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"rebaseRatePerSecond\",\"type\":\"uint256\"}],\"name\":\"RebasePerSecondMaxChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_threshold\",\"type\":\"uint256\"}],\"name\":\"RebaseThresholdUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"RebaseUnpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Redeem\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_redeemFeeBps\",\"type\":\"uint256\"}],\"name\":\"RedeemFeeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_address\",\"type\":\"address\"}],\"name\":\"StrategistUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"strategy\",\"type\":\"address\"}],\"name\":\"StrategyAddedToMintWhitelist\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"StrategyApproved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"StrategyRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"strategy\",\"type\":\"address\"}],\"name\":\"StrategyRemovedFromMintWhitelist\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_basis\",\"type\":\"uint256\"}],\"name\":\"SwapAllowedUndervalueChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_basis\",\"type\":\"uint256\"}],\"name\":\"SwapSlippageChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_fromAsset\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_toAsset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_fromAssetAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_toAssetAmount\",\"type\":\"uint256\"}],\"name\":\"Swapped\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_address\",\"type\":\"address\"}],\"name\":\"SwapperChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_address\",\"type\":\"address\"}],\"name\":\"TrusteeAddressChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_basis\",\"type\":\"uint256\"}],\"name\":\"TrusteeFeeBpsChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_vaultBuffer\",\"type\":\"uint256\"}],\"name\":\"VaultBufferUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_newDelay\",\"type\":\"uint256\"}],\"name\":\"WithdrawalClaimDelayUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_claimable\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_newClaimable\",\"type\":\"uint256\"}],\"name\":\"WithdrawalClaimable\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_withdrawer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"WithdrawalClaimed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_withdrawer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_queued\",\"type\":\"uint256\"}],\"name\":\"WithdrawalRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_yield\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_fee\",\"type\":\"uint256\"}],\"name\":\"YieldDistribution\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"strategyAddr\",\"type\":\"address\"}],\"name\":\"addStrategyToMintWhitelist\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"adminImplPosition\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"allowedSwapUndervalue\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"approveStrategy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"assetDefaultStrategies\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"autoAllocateThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"}],\"name\":\"cacheDecimals\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"capitalPaused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_strategyToAddress\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"_assets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"_amounts\",\"type\":\"uint256[]\"}],\"name\":\"depositToStrategy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dripDuration\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"governor\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isGovernor\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"isMintWhitelistedStrategy\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastRebase\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"maxSupplyDiff\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"netOusdMintForStrategyThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"netOusdMintedForStrategy\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"oUSD\",\"outputs\":[{\"internalType\":\"contract OUSD\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"ousdMetaStrategy\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pauseCapital\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pauseRebase\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"priceProvider\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rebasePaused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rebasePerSecondMax\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rebasePerSecondTarget\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rebaseThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"redeemFeeBps\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"}],\"name\":\"removeAsset\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"removeStrategy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"strategyAddr\",\"type\":\"address\"}],\"name\":\"removeStrategyFromMintWhitelist\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImpl\",\"type\":\"address\"}],\"name\":\"setAdminImpl\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_strategy\",\"type\":\"address\"}],\"name\":\"setAssetDefaultStrategy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_threshold\",\"type\":\"uint256\"}],\"name\":\"setAutoAllocateThreshold\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_dripDuration\",\"type\":\"uint256\"}],\"name\":\"setDripDuration\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_maxSupplyDiff\",\"type\":\"uint256\"}],\"name\":\"setMaxSupplyDiff\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_threshold\",\"type\":\"uint256\"}],\"name\":\"setNetOusdMintForStrategyThreshold\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"_allowedOracleSlippageBps\",\"type\":\"uint16\"}],\"name\":\"setOracleSlippage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_ousdMetaStrategy\",\"type\":\"address\"}],\"name\":\"setOusdMetaStrategy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_priceProvider\",\"type\":\"address\"}],\"name\":\"setPriceProvider\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"yearlyApr\",\"type\":\"uint256\"}],\"name\":\"setRebaseRateMax\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_threshold\",\"type\":\"uint256\"}],\"name\":\"setRebaseThreshold\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_redeemFeeBps\",\"type\":\"uint256\"}],\"name\":\"setRedeemFeeBps\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_address\",\"type\":\"address\"}],\"name\":\"setStrategistAddr\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"_basis\",\"type\":\"uint16\"}],\"name\":\"setSwapAllowedUndervalue\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_swapperAddr\",\"type\":\"address\"}],\"name\":\"setSwapper\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_address\",\"type\":\"address\"}],\"name\":\"setTrusteeAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_basis\",\"type\":\"uint256\"}],\"name\":\"setTrusteeFeeBps\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_vaultBuffer\",\"type\":\"uint256\"}],\"name\":\"setVaultBuffer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_delay\",\"type\":\"uint256\"}],\"name\":\"setWithdrawalClaimDelay\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"strategies\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"isSupported\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"_deprecated\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"strategistAddr\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"_unitConversion\",\"type\":\"uint8\"}],\"name\":\"supportAsset\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_fromAsset\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_toAsset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_fromAssetAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_minToAssetAmount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"swapCollateral\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"toAssetAmount\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"swapper\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"swapper_\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newGovernor\",\"type\":\"address\"}],\"name\":\"transferGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"transferToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"trusteeAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"trusteeFeeBps\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpauseCapital\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpauseRebase\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"vaultBuffer\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"weth\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawAllFromStrategies\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_strategyAddr\",\"type\":\"address\"}],\"name\":\"withdrawAllFromStrategy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_strategyFromAddress\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"_assets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"_amounts\",\"type\":\"uint256[]\"}],\"name\":\"withdrawFromStrategy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawalClaimDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawalQueueMetadata\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"queued\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"claimable\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"claimed\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"nextWithdrawalIndex\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"withdrawalRequests\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"withdrawer\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"claimed\",\"type\":\"bool\"},{\"internalType\":\"uint40\",\"name\":\"timestamp\",\"type\":\"uint40\"},{\"internalType\":\"uint128\",\"name\":\"amount\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"queued\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"author\":\"Origin Protocol Inc\",\"kind\":\"dev\",\"methods\":{\"addStrategyToMintWhitelist(address)\":{\"params\":{\"strategyAddr\":\"Strategy address\"}},\"allowedSwapUndervalue()\":{\"returns\":{\"value\":\"Percentage in basis points.\"}},\"approveStrategy(address)\":{\"params\":{\"_addr\":\"Address of the strategy to add\"}},\"cacheDecimals(address)\":{\"params\":{\"_asset\":\"Address of asset token\"}},\"depositToStrategy(address,address[],uint256[])\":{\"params\":{\"_amounts\":\"Array of amounts of each corresponding asset to deposit.\",\"_assets\":\"Array of asset address that will be deposited into the strategy.\",\"_strategyToAddress\":\"Address of the Strategy to deposit assets into.\"}},\"removeAsset(address)\":{\"params\":{\"_asset\":\"Address of asset\"}},\"removeStrategy(address)\":{\"params\":{\"_addr\":\"Address of the strategy to remove\"}},\"removeStrategyFromMintWhitelist(address)\":{\"params\":{\"strategyAddr\":\"Strategy address\"}},\"setAdminImpl(address)\":{\"params\":{\"newImpl\":\"address of the implementation\"}},\"setAssetDefaultStrategy(address,address)\":{\"params\":{\"_asset\":\"Address of the asset\",\"_strategy\":\"Address of the Strategy\"}},\"setAutoAllocateThreshold(uint256)\":{\"params\":{\"_threshold\":\"OToken amount with 18 fixed decimals.\"}},\"setDripDuration(uint256)\":{\"params\":{\"_dripDuration\":\"Time in seconds to target a constant yield rate\"}},\"setNetOusdMintForStrategyThreshold(uint256)\":{\"params\":{\"_threshold\":\"OToken amount with 18 fixed decimals.\"}},\"setOracleSlippage(address,uint16)\":{\"params\":{\"_allowedOracleSlippageBps\":\"allowed slippage from Oracle in basis points. eg 20 = 0.2%. Max 10%.\",\"_asset\":\"Address of the asset token.\"}},\"setOusdMetaStrategy(address)\":{\"params\":{\"_ousdMetaStrategy\":\"Address of OToken metapool strategy\"}},\"setPriceProvider(address)\":{\"params\":{\"_priceProvider\":\"Address of price provider\"}},\"setRebaseRateMax(uint256)\":{\"params\":{\"yearlyApr\":\"in 1e18 notation. 3 * 1e18 = 3% APR\"}},\"setRebaseThreshold(uint256)\":{\"params\":{\"_threshold\":\"OToken amount with 18 fixed decimals.\"}},\"setRedeemFeeBps(uint256)\":{\"params\":{\"_redeemFeeBps\":\"Basis point fee to be charged\"}},\"setStrategistAddr(address)\":{\"params\":{\"_address\":\"Address of Strategist\"}},\"setSwapAllowedUndervalue(uint16)\":{\"params\":{\"_basis\":\"Percentage in basis points. eg 100 == 1%\"}},\"setSwapper(address)\":{\"params\":{\"_swapperAddr\":\"Address of the Swapper contract that implements the ISwapper interface.\"}},\"setVaultBuffer(uint256)\":{\"params\":{\"_vaultBuffer\":\"Percentage using 18 decimals. 100% = 1e18.\"}},\"setWithdrawalClaimDelay(uint256)\":{\"params\":{\"_delay\":\"Delay period (should be between 10 mins to 7 days). Set to 0 to disable async withdrawals\"}},\"supportAsset(address,uint8)\":{\"params\":{\"_asset\":\"Address of asset\"}},\"swapCollateral(address,address,uint256,uint256,bytes)\":{\"params\":{\"_data\":\"implementation specific data. eg 1Inch swap data\",\"_fromAsset\":\"The token address of the asset being sold by the vault.\",\"_fromAssetAmount\":\"The amount of assets being sold by the vault.\",\"_minToAssetAmount\":\"The minimum amount of assets to be purchased.\",\"_toAsset\":\"The token address of the asset being purchased by the vault.\"},\"returns\":{\"toAssetAmount\":\"The amount of toAssets that was received from the swap\"}},\"transferGovernance(address)\":{\"params\":{\"_newGovernor\":\"Address of the new Governor\"}},\"transferToken(address,uint256)\":{\"params\":{\"_amount\":\"Amount of the asset to transfer\",\"_asset\":\"Address for the asset\"}},\"withdrawAllFromStrategy(address)\":{\"params\":{\"_strategyAddr\":\"Strategy address.\"}},\"withdrawFromStrategy(address,address[],uint256[])\":{\"params\":{\"_amounts\":\"Array of amounts of each corresponding asset to withdraw.\",\"_assets\":\"Array of asset address that will be withdrawn from the strategy.\",\"_strategyFromAddress\":\"Address of the Strategy to withdraw assets from.\"}}},\"title\":\"OETH VaultAdmin Contract\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"addStrategyToMintWhitelist(address)\":{\"notice\":\"Adds a strategy to the mint whitelist. Reverts if strategy isn't approved on Vault.\"},\"allowedSwapUndervalue()\":{\"notice\":\"Max allowed percentage the vault total value can drop below the OToken total supply in basis points when executing a collateral swap. For example 100 == 1%\"},\"approveStrategy(address)\":{\"notice\":\"Add a strategy to the Vault.\"},\"assetDefaultStrategies(address)\":{\"notice\":\"Mapping of asset address to the Strategy that they should automatically\"},\"autoAllocateThreshold()\":{\"notice\":\"OToken mints over this amount automatically allocate funds. 18 decimals.\"},\"cacheDecimals(address)\":{\"notice\":\"Cache decimals on OracleRouter for a particular asset. This action is required before that asset's price can be accessed.\"},\"capitalPaused()\":{\"notice\":\"pause operations that change the OToken supply. eg mint, redeem, allocate, mint/burn for strategy\"},\"claimGovernance()\":{\"notice\":\"Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor.\"},\"depositToStrategy(address,address[],uint256[])\":{\"notice\":\"Deposit multiple assets from the vault into the strategy.\"},\"dripDuration()\":{\"notice\":\"Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\"},\"governor()\":{\"notice\":\"Returns the address of the current Governor.\"},\"isGovernor()\":{\"notice\":\"Returns true if the caller is the current Governor.\"},\"lastRebase()\":{\"notice\":\"Time in seconds that the vault last rebased yield.\"},\"maxSupplyDiff()\":{\"notice\":\"Max difference between total supply and total value of assets. 18 decimals.\"},\"netOusdMintForStrategyThreshold()\":{\"notice\":\"How much net total OTokens are allowed to be minted by all strategies\"},\"netOusdMintedForStrategy()\":{\"notice\":\"How much OTokens are currently minted by the strategy\"},\"ousdMetaStrategy()\":{\"notice\":\"Metapool strategy that is allowed to mint/burn OTokens without changing collateral\"},\"pauseCapital()\":{\"notice\":\"Set the deposit paused flag to true to prevent capital movement.\"},\"pauseRebase()\":{\"notice\":\"Set the deposit paused flag to true to prevent rebasing.\"},\"priceProvider()\":{\"notice\":\"Address of the Oracle price provider contract\"},\"rebasePaused()\":{\"notice\":\"pause rebasing if true\"},\"rebasePerSecondMax()\":{\"notice\":\"max rebase percentage per second Can be used to set maximum yield of the protocol, spreading out yield over time\"},\"rebasePerSecondTarget()\":{\"notice\":\"target rebase rate limit, based on past rates and funds available.\"},\"rebaseThreshold()\":{\"notice\":\"OToken mints over this amount automatically rebase. 18 decimals.\"},\"redeemFeeBps()\":{\"notice\":\"Redemption fee in basis points. eg 50 = 0.5%\"},\"removeAsset(address)\":{\"notice\":\"Remove a supported asset from the Vault\"},\"removeStrategy(address)\":{\"notice\":\"Remove a strategy from the Vault.\"},\"removeStrategyFromMintWhitelist(address)\":{\"notice\":\"Removes a strategy from the mint whitelist.\"},\"setAdminImpl(address)\":{\"notice\":\"set the implementation for the admin, this needs to be in a base class else we cannot set it\"},\"setAssetDefaultStrategy(address,address)\":{\"notice\":\"Set the default Strategy for an asset, i.e. the one which the asset will be automatically allocated to and withdrawn from\"},\"setAutoAllocateThreshold(uint256)\":{\"notice\":\"Sets the minimum amount of OTokens in a mint to trigger an automatic allocation of funds afterwords.\"},\"setDripDuration(uint256)\":{\"notice\":\"Set the drip duration period\"},\"setMaxSupplyDiff(uint256)\":{\"notice\":\"Sets the maximum allowable difference between total supply and backing assets' value.\"},\"setNetOusdMintForStrategyThreshold(uint256)\":{\"notice\":\"Set maximum amount of OTokens that can at any point be minted and deployed to strategy (used only by ConvexOUSDMetaStrategy for now).\"},\"setOracleSlippage(address,uint16)\":{\"notice\":\"Set the allowed slippage from the Oracle price for collateral asset swaps.\"},\"setOusdMetaStrategy(address)\":{\"notice\":\"Set OToken Metapool strategy\"},\"setPriceProvider(address)\":{\"notice\":\"Set address of price provider.\"},\"setRebaseRateMax(uint256)\":{\"notice\":\"Set a yield streaming max rate. This spreads yield over time if it is above the max rate.\"},\"setRebaseThreshold(uint256)\":{\"notice\":\"Set a minimum amount of OTokens in a mint or redeem that triggers a rebase\"},\"setRedeemFeeBps(uint256)\":{\"notice\":\"Set a fee in basis points to be charged for a redeem.\"},\"setStrategistAddr(address)\":{\"notice\":\"Set address of Strategist\"},\"setSwapAllowedUndervalue(uint16)\":{\"notice\":\"Set max allowed percentage the vault total value can drop below the OToken total supply in basis points when executing collateral swaps.\"},\"setSwapper(address)\":{\"notice\":\"Set the contract the performs swaps of collateral assets.\"},\"setTrusteeAddress(address)\":{\"notice\":\"Sets the trusteeAddress that can receive a portion of yield. Setting to the zero address disables this feature.\"},\"setTrusteeFeeBps(uint256)\":{\"notice\":\"Sets the TrusteeFeeBps to the percentage of yield that should be received in basis points.\"},\"setVaultBuffer(uint256)\":{\"notice\":\"Set a buffer of assets to keep in the Vault to handle most redemptions without needing to spend gas unwinding assets from a Strategy.\"},\"setWithdrawalClaimDelay(uint256)\":{\"notice\":\"Changes the async withdrawal claim period for OETH & superOETHb\"},\"strategistAddr()\":{\"notice\":\"Address of the Strategist\"},\"supportAsset(address,uint8)\":{\"notice\":\"Add a supported asset to the contract, i.e. one that can be to mint OTokens.\"},\"swapCollateral(address,address,uint256,uint256,bytes)\":{\"notice\":\"Strategist swaps collateral assets sitting in the vault.\"},\"swapper()\":{\"notice\":\"Contract that swaps the vault's collateral assets\"},\"transferGovernance(address)\":{\"notice\":\"Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete\"},\"transferToken(address,uint256)\":{\"notice\":\"Transfer token to governor. Intended for recovering tokens stuck in contract, i.e. mistaken sends.\"},\"trusteeAddress()\":{\"notice\":\"Trustee contract that can collect a percentage of yield\"},\"trusteeFeeBps()\":{\"notice\":\"Amount of yield collected in basis points. eg 2000 = 20%\"},\"unpauseCapital()\":{\"notice\":\"Set the deposit paused flag to false to enable capital movement.\"},\"unpauseRebase()\":{\"notice\":\"Set the deposit paused flag to true to allow rebasing.\"},\"vaultBuffer()\":{\"notice\":\"Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\"},\"withdrawAllFromStrategies()\":{\"notice\":\"Withdraws all assets from all the strategies and sends assets to the Vault.\"},\"withdrawAllFromStrategy(address)\":{\"notice\":\"Withdraws all assets from the strategy and sends assets to the Vault.\"},\"withdrawFromStrategy(address,address[],uint256[])\":{\"notice\":\"Withdraw multiple assets from the strategy to the vault.\"},\"withdrawalClaimDelay()\":{\"notice\":\"Sets a minimum delay that is required to elapse between requesting async withdrawals and claiming the request. When set to 0 async withdrawals are disabled.\"},\"withdrawalQueueMetadata()\":{\"notice\":\"Global metadata for the withdrawal queue including: queued - cumulative total of all withdrawal requests included the ones that have already been claimed claimable - cumulative total of all the requests that can be claimed including the ones already claimed claimed - total of all the requests that have been claimed nextWithdrawalIndex - index of the next withdrawal request starting at 0\"},\"withdrawalRequests(uint256)\":{\"notice\":\"Mapping of withdrawal request indices to the user withdrawal request data\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/vault/OETHVaultAdmin.sol\":\"OETHVaultAdmin\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/token/ERC20/IERC20.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Interface of the ERC20 standard as defined in the EIP.\\n */\\ninterface IERC20 {\\n /**\\n * @dev Returns the amount of tokens in existence.\\n */\\n function totalSupply() external view returns (uint256);\\n\\n /**\\n * @dev Returns the amount of tokens owned by `account`.\\n */\\n function balanceOf(address account) external view returns (uint256);\\n\\n /**\\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transfer(address recipient, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Returns the remaining number of tokens that `spender` will be\\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\\n * zero by default.\\n *\\n * This value changes when {approve} or {transferFrom} are called.\\n */\\n function allowance(address owner, address spender) external view returns (uint256);\\n\\n /**\\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\\n * that someone may use both the old and the new allowance by unfortunate\\n * transaction ordering. One possible solution to mitigate this race\\n * condition is to first reduce the spender's allowance to 0 and set the\\n * desired value afterwards:\\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\\n *\\n * Emits an {Approval} event.\\n */\\n function approve(address spender, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\\n * allowance mechanism. `amount` is then deducted from the caller's\\n * allowance.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transferFrom(\\n address sender,\\n address recipient,\\n uint256 amount\\n ) external returns (bool);\\n\\n /**\\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\\n * another (`to`).\\n *\\n * Note that `value` may be zero.\\n */\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n\\n /**\\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n * a call to {approve}. `value` is the new allowance.\\n */\\n event Approval(address indexed owner, address indexed spender, uint256 value);\\n}\\n\",\"keccak256\":\"0x61437cb513a887a1bbad006e7b1c8b414478427d33de47c5600af3c748f108da\",\"license\":\"MIT\"},\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../IERC20.sol\\\";\\nimport \\\"../../../utils/Address.sol\\\";\\n\\n/**\\n * @title SafeERC20\\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\\n * contract returns false). Tokens that return no value (and instead revert or\\n * throw on failure) are also supported, non-reverting calls are assumed to be\\n * successful.\\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\\n */\\nlibrary SafeERC20 {\\n using Address for address;\\n\\n function safeTransfer(\\n IERC20 token,\\n address to,\\n uint256 value\\n ) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\\n }\\n\\n function safeTransferFrom(\\n IERC20 token,\\n address from,\\n address to,\\n uint256 value\\n ) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\\n }\\n\\n /**\\n * @dev Deprecated. This function has issues similar to the ones found in\\n * {IERC20-approve}, and its usage is discouraged.\\n *\\n * Whenever possible, use {safeIncreaseAllowance} and\\n * {safeDecreaseAllowance} instead.\\n */\\n function safeApprove(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n // safeApprove should only be called when setting an initial allowance,\\n // or when resetting it to zero. To increase and decrease it, use\\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\\n require(\\n (value == 0) || (token.allowance(address(this), spender) == 0),\\n \\\"SafeERC20: approve from non-zero to non-zero allowance\\\"\\n );\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\\n }\\n\\n function safeIncreaseAllowance(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n uint256 newAllowance = token.allowance(address(this), spender) + value;\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\\n }\\n\\n function safeDecreaseAllowance(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n unchecked {\\n uint256 oldAllowance = token.allowance(address(this), spender);\\n require(oldAllowance >= value, \\\"SafeERC20: decreased allowance below zero\\\");\\n uint256 newAllowance = oldAllowance - value;\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\\n }\\n }\\n\\n /**\\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\\n * on the return value: the return value is optional (but if data is returned, it must not be false).\\n * @param token The token targeted by the call.\\n * @param data The call data (encoded using abi.encode or one of its variants).\\n */\\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\\n // the target address contains contract code and also asserts for success in the low-level call.\\n\\n bytes memory returndata = address(token).functionCall(data, \\\"SafeERC20: low-level call failed\\\");\\n if (returndata.length > 0) {\\n // Return data is optional\\n require(abi.decode(returndata, (bool)), \\\"SafeERC20: ERC20 operation did not succeed\\\");\\n }\\n }\\n}\\n\",\"keccak256\":\"0xc3d946432c0ddbb1f846a0d3985be71299df331b91d06732152117f62f0be2b5\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize, which returns 0 for contracts in\\n // construction, since the code is only stored at the end of the\\n // constructor execution.\\n\\n uint256 size;\\n assembly {\\n size := extcodesize(account)\\n }\\n return size > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCall(target, data, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n require(isContract(target), \\\"Address: static call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(isContract(target), \\\"Address: delegate call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x51b758a8815ecc9596c66c37d56b1d33883a444631a3f916b9fe65cb863ef7c4\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/math/SafeCast.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\\n * checks.\\n *\\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\\n * easily result in undesired exploitation or bugs, since developers usually\\n * assume that overflows raise errors. `SafeCast` restores this intuition by\\n * reverting the transaction when such an operation overflows.\\n *\\n * Using this library instead of the unchecked operations eliminates an entire\\n * class of bugs, so it's recommended to use it always.\\n *\\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\\n * all math on `uint256` and `int256` and then downcasting.\\n */\\nlibrary SafeCast {\\n /**\\n * @dev Returns the downcasted uint224 from uint256, reverting on\\n * overflow (when the input is greater than largest uint224).\\n *\\n * Counterpart to Solidity's `uint224` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 224 bits\\n */\\n function toUint224(uint256 value) internal pure returns (uint224) {\\n require(value <= type(uint224).max, \\\"SafeCast: value doesn't fit in 224 bits\\\");\\n return uint224(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint128 from uint256, reverting on\\n * overflow (when the input is greater than largest uint128).\\n *\\n * Counterpart to Solidity's `uint128` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 128 bits\\n */\\n function toUint128(uint256 value) internal pure returns (uint128) {\\n require(value <= type(uint128).max, \\\"SafeCast: value doesn't fit in 128 bits\\\");\\n return uint128(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint96 from uint256, reverting on\\n * overflow (when the input is greater than largest uint96).\\n *\\n * Counterpart to Solidity's `uint96` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 96 bits\\n */\\n function toUint96(uint256 value) internal pure returns (uint96) {\\n require(value <= type(uint96).max, \\\"SafeCast: value doesn't fit in 96 bits\\\");\\n return uint96(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint64 from uint256, reverting on\\n * overflow (when the input is greater than largest uint64).\\n *\\n * Counterpart to Solidity's `uint64` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 64 bits\\n */\\n function toUint64(uint256 value) internal pure returns (uint64) {\\n require(value <= type(uint64).max, \\\"SafeCast: value doesn't fit in 64 bits\\\");\\n return uint64(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint32 from uint256, reverting on\\n * overflow (when the input is greater than largest uint32).\\n *\\n * Counterpart to Solidity's `uint32` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 32 bits\\n */\\n function toUint32(uint256 value) internal pure returns (uint32) {\\n require(value <= type(uint32).max, \\\"SafeCast: value doesn't fit in 32 bits\\\");\\n return uint32(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint16 from uint256, reverting on\\n * overflow (when the input is greater than largest uint16).\\n *\\n * Counterpart to Solidity's `uint16` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 16 bits\\n */\\n function toUint16(uint256 value) internal pure returns (uint16) {\\n require(value <= type(uint16).max, \\\"SafeCast: value doesn't fit in 16 bits\\\");\\n return uint16(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint8 from uint256, reverting on\\n * overflow (when the input is greater than largest uint8).\\n *\\n * Counterpart to Solidity's `uint8` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 8 bits.\\n */\\n function toUint8(uint256 value) internal pure returns (uint8) {\\n require(value <= type(uint8).max, \\\"SafeCast: value doesn't fit in 8 bits\\\");\\n return uint8(value);\\n }\\n\\n /**\\n * @dev Converts a signed int256 into an unsigned uint256.\\n *\\n * Requirements:\\n *\\n * - input must be greater than or equal to 0.\\n */\\n function toUint256(int256 value) internal pure returns (uint256) {\\n require(value >= 0, \\\"SafeCast: value must be positive\\\");\\n return uint256(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int128 from int256, reverting on\\n * overflow (when the input is less than smallest int128 or\\n * greater than largest int128).\\n *\\n * Counterpart to Solidity's `int128` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 128 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt128(int256 value) internal pure returns (int128) {\\n require(value >= type(int128).min && value <= type(int128).max, \\\"SafeCast: value doesn't fit in 128 bits\\\");\\n return int128(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int64 from int256, reverting on\\n * overflow (when the input is less than smallest int64 or\\n * greater than largest int64).\\n *\\n * Counterpart to Solidity's `int64` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 64 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt64(int256 value) internal pure returns (int64) {\\n require(value >= type(int64).min && value <= type(int64).max, \\\"SafeCast: value doesn't fit in 64 bits\\\");\\n return int64(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int32 from int256, reverting on\\n * overflow (when the input is less than smallest int32 or\\n * greater than largest int32).\\n *\\n * Counterpart to Solidity's `int32` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 32 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt32(int256 value) internal pure returns (int32) {\\n require(value >= type(int32).min && value <= type(int32).max, \\\"SafeCast: value doesn't fit in 32 bits\\\");\\n return int32(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int16 from int256, reverting on\\n * overflow (when the input is less than smallest int16 or\\n * greater than largest int16).\\n *\\n * Counterpart to Solidity's `int16` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 16 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt16(int256 value) internal pure returns (int16) {\\n require(value >= type(int16).min && value <= type(int16).max, \\\"SafeCast: value doesn't fit in 16 bits\\\");\\n return int16(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int8 from int256, reverting on\\n * overflow (when the input is less than smallest int8 or\\n * greater than largest int8).\\n *\\n * Counterpart to Solidity's `int8` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 8 bits.\\n *\\n * _Available since v3.1._\\n */\\n function toInt8(int256 value) internal pure returns (int8) {\\n require(value >= type(int8).min && value <= type(int8).max, \\\"SafeCast: value doesn't fit in 8 bits\\\");\\n return int8(value);\\n }\\n\\n /**\\n * @dev Converts an unsigned uint256 into a signed int256.\\n *\\n * Requirements:\\n *\\n * - input must be less than or equal to maxInt256.\\n */\\n function toInt256(uint256 value) internal pure returns (int256) {\\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\\n require(value <= uint256(type(int256).max), \\\"SafeCast: value doesn't fit in an int256\\\");\\n return int256(value);\\n }\\n}\\n\",\"keccak256\":\"0x5c6caab697d302ad7eb59c234a4d2dbc965c1bae87709bd2850060b7695b28c7\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/math/SafeMath.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)\\n\\npragma solidity ^0.8.0;\\n\\n// CAUTION\\n// This version of SafeMath should only be used with Solidity 0.8 or later,\\n// because it relies on the compiler's built in overflow checks.\\n\\n/**\\n * @dev Wrappers over Solidity's arithmetic operations.\\n *\\n * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler\\n * now has built in overflow checking.\\n */\\nlibrary SafeMath {\\n /**\\n * @dev Returns the addition of two unsigned integers, with an overflow flag.\\n *\\n * _Available since v3.4._\\n */\\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\\n unchecked {\\n uint256 c = a + b;\\n if (c < a) return (false, 0);\\n return (true, c);\\n }\\n }\\n\\n /**\\n * @dev Returns the substraction of two unsigned integers, with an overflow flag.\\n *\\n * _Available since v3.4._\\n */\\n function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\\n unchecked {\\n if (b > a) return (false, 0);\\n return (true, a - b);\\n }\\n }\\n\\n /**\\n * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\\n *\\n * _Available since v3.4._\\n */\\n function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\\n unchecked {\\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\\n // benefit is lost if 'b' is also tested.\\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\\n if (a == 0) return (true, 0);\\n uint256 c = a * b;\\n if (c / a != b) return (false, 0);\\n return (true, c);\\n }\\n }\\n\\n /**\\n * @dev Returns the division of two unsigned integers, with a division by zero flag.\\n *\\n * _Available since v3.4._\\n */\\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\\n unchecked {\\n if (b == 0) return (false, 0);\\n return (true, a / b);\\n }\\n }\\n\\n /**\\n * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\\n *\\n * _Available since v3.4._\\n */\\n function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\\n unchecked {\\n if (b == 0) return (false, 0);\\n return (true, a % b);\\n }\\n }\\n\\n /**\\n * @dev Returns the addition of two unsigned integers, reverting on\\n * overflow.\\n *\\n * Counterpart to Solidity's `+` operator.\\n *\\n * Requirements:\\n *\\n * - Addition cannot overflow.\\n */\\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a + b;\\n }\\n\\n /**\\n * @dev Returns the subtraction of two unsigned integers, reverting on\\n * overflow (when the result is negative).\\n *\\n * Counterpart to Solidity's `-` operator.\\n *\\n * Requirements:\\n *\\n * - Subtraction cannot overflow.\\n */\\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a - b;\\n }\\n\\n /**\\n * @dev Returns the multiplication of two unsigned integers, reverting on\\n * overflow.\\n *\\n * Counterpart to Solidity's `*` operator.\\n *\\n * Requirements:\\n *\\n * - Multiplication cannot overflow.\\n */\\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a * b;\\n }\\n\\n /**\\n * @dev Returns the integer division of two unsigned integers, reverting on\\n * division by zero. The result is rounded towards zero.\\n *\\n * Counterpart to Solidity's `/` operator.\\n *\\n * Requirements:\\n *\\n * - The divisor cannot be zero.\\n */\\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a / b;\\n }\\n\\n /**\\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\\n * reverting when dividing by zero.\\n *\\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\\n * opcode (which leaves remaining gas untouched) while Solidity uses an\\n * invalid opcode to revert (consuming all remaining gas).\\n *\\n * Requirements:\\n *\\n * - The divisor cannot be zero.\\n */\\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a % b;\\n }\\n\\n /**\\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\\n * overflow (when the result is negative).\\n *\\n * CAUTION: This function is deprecated because it requires allocating memory for the error\\n * message unnecessarily. For custom revert reasons use {trySub}.\\n *\\n * Counterpart to Solidity's `-` operator.\\n *\\n * Requirements:\\n *\\n * - Subtraction cannot overflow.\\n */\\n function sub(\\n uint256 a,\\n uint256 b,\\n string memory errorMessage\\n ) internal pure returns (uint256) {\\n unchecked {\\n require(b <= a, errorMessage);\\n return a - b;\\n }\\n }\\n\\n /**\\n * @dev Returns the integer division of two unsigned integers, reverting with custom message on\\n * division by zero. The result is rounded towards zero.\\n *\\n * Counterpart to Solidity's `/` operator. Note: this function uses a\\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\\n * uses an invalid opcode to revert (consuming all remaining gas).\\n *\\n * Requirements:\\n *\\n * - The divisor cannot be zero.\\n */\\n function div(\\n uint256 a,\\n uint256 b,\\n string memory errorMessage\\n ) internal pure returns (uint256) {\\n unchecked {\\n require(b > 0, errorMessage);\\n return a / b;\\n }\\n }\\n\\n /**\\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\\n * reverting with custom message when dividing by zero.\\n *\\n * CAUTION: This function is deprecated because it requires allocating memory for the error\\n * message unnecessarily. For custom revert reasons use {tryMod}.\\n *\\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\\n * opcode (which leaves remaining gas untouched) while Solidity uses an\\n * invalid opcode to revert (consuming all remaining gas).\\n *\\n * Requirements:\\n *\\n * - The divisor cannot be zero.\\n */\\n function mod(\\n uint256 a,\\n uint256 b,\\n string memory errorMessage\\n ) internal pure returns (uint256) {\\n unchecked {\\n require(b > 0, errorMessage);\\n return a % b;\\n }\\n }\\n}\\n\",\"keccak256\":\"0xa2f576be637946f767aa56601c26d717f48a0aff44f82e46f13807eea1009a21\",\"license\":\"MIT\"},\"contracts/governance/Governable.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\\n * from owner to governor and renounce methods removed. Does not use\\n * Context.sol like Ownable.sol does for simplification.\\n * @author Origin Protocol Inc\\n */\\nabstract contract Governable {\\n // Storage position of the owner and pendingOwner of the contract\\n // keccak256(\\\"OUSD.governor\\\");\\n bytes32 private constant governorPosition =\\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\\n\\n // keccak256(\\\"OUSD.pending.governor\\\");\\n bytes32 private constant pendingGovernorPosition =\\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\\n\\n // keccak256(\\\"OUSD.reentry.status\\\");\\n bytes32 private constant reentryStatusPosition =\\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\\n\\n // See OpenZeppelin ReentrancyGuard implementation\\n uint256 constant _NOT_ENTERED = 1;\\n uint256 constant _ENTERED = 2;\\n\\n event PendingGovernorshipTransfer(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n event GovernorshipTransferred(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n /**\\n * @notice Returns the address of the current Governor.\\n */\\n function governor() public view returns (address) {\\n return _governor();\\n }\\n\\n /**\\n * @dev Returns the address of the current Governor.\\n */\\n function _governor() internal view returns (address governorOut) {\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n governorOut := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Returns the address of the pending Governor.\\n */\\n function _pendingGovernor()\\n internal\\n view\\n returns (address pendingGovernor)\\n {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n pendingGovernor := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the Governor.\\n */\\n modifier onlyGovernor() {\\n require(isGovernor(), \\\"Caller is not the Governor\\\");\\n _;\\n }\\n\\n /**\\n * @notice Returns true if the caller is the current Governor.\\n */\\n function isGovernor() public view returns (bool) {\\n return msg.sender == _governor();\\n }\\n\\n function _setGovernor(address newGovernor) internal {\\n emit GovernorshipTransferred(_governor(), newGovernor);\\n\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @dev Prevents a contract from calling itself, directly or indirectly.\\n * Calling a `nonReentrant` function from another `nonReentrant`\\n * function is not supported. It is possible to prevent this from happening\\n * by making the `nonReentrant` function external, and make it call a\\n * `private` function that does the actual work.\\n */\\n modifier nonReentrant() {\\n bytes32 position = reentryStatusPosition;\\n uint256 _reentry_status;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n _reentry_status := sload(position)\\n }\\n\\n // On the first call to nonReentrant, _notEntered will be true\\n require(_reentry_status != _ENTERED, \\\"Reentrant call\\\");\\n\\n // Any calls to nonReentrant after this point will fail\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _ENTERED)\\n }\\n\\n _;\\n\\n // By storing the original value once again, a refund is triggered (see\\n // https://eips.ethereum.org/EIPS/eip-2200)\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _NOT_ENTERED)\\n }\\n }\\n\\n function _setPendingGovernor(address newGovernor) internal {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the current Governor. Must be claimed for this to complete\\n * @param _newGovernor Address of the new Governor\\n */\\n function transferGovernance(address _newGovernor) external onlyGovernor {\\n _setPendingGovernor(_newGovernor);\\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\\n }\\n\\n /**\\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the new Governor.\\n */\\n function claimGovernance() external {\\n require(\\n msg.sender == _pendingGovernor(),\\n \\\"Only the pending Governor can complete the claim\\\"\\n );\\n _changeGovernor(msg.sender);\\n }\\n\\n /**\\n * @dev Change Governance of the contract to a new account (`newGovernor`).\\n * @param _newGovernor Address of the new Governor\\n */\\n function _changeGovernor(address _newGovernor) internal {\\n require(_newGovernor != address(0), \\\"New Governor is address(0)\\\");\\n _setGovernor(_newGovernor);\\n }\\n}\\n\",\"keccak256\":\"0xf32f873c8bfbacf2e5f01d0cf37bc7f54fbd5aa656e95c8a599114229946f107\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IBasicToken.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\ninterface IBasicToken {\\n function symbol() external view returns (string memory);\\n\\n function decimals() external view returns (uint8);\\n}\\n\",\"keccak256\":\"0xa562062698aa12572123b36dfd2072f1a39e44fed2031cc19c2c9fd522f96ec2\",\"license\":\"MIT\"},\"contracts/interfaces/IOracle.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\ninterface IOracle {\\n /**\\n * @dev returns the asset price in USD, in 8 decimal digits.\\n *\\n * The version of priceProvider deployed for OETH has 18 decimal digits\\n */\\n function price(address asset) external view returns (uint256);\\n}\\n\",\"keccak256\":\"0xa5f765f5b22cd5426803b22a7344d4c34c4d4016a0b6e9d799862133253f77b2\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IStrategy.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\\n */\\ninterface IStrategy {\\n /**\\n * @dev Deposit the given asset to platform\\n * @param _asset asset address\\n * @param _amount Amount to deposit\\n */\\n function deposit(address _asset, uint256 _amount) external;\\n\\n /**\\n * @dev Deposit the entire balance of all supported assets in the Strategy\\n * to the platform\\n */\\n function depositAll() external;\\n\\n /**\\n * @dev Withdraw given asset from Lending platform\\n */\\n function withdraw(\\n address _recipient,\\n address _asset,\\n uint256 _amount\\n ) external;\\n\\n /**\\n * @dev Liquidate all assets in strategy and return them to Vault.\\n */\\n function withdrawAll() external;\\n\\n /**\\n * @dev Returns the current balance of the given asset.\\n */\\n function checkBalance(address _asset)\\n external\\n view\\n returns (uint256 balance);\\n\\n /**\\n * @dev Returns bool indicating whether strategy supports asset.\\n */\\n function supportsAsset(address _asset) external view returns (bool);\\n\\n /**\\n * @dev Collect reward tokens from the Strategy.\\n */\\n function collectRewardTokens() external;\\n\\n /**\\n * @dev The address array of the reward tokens for the Strategy.\\n */\\n function getRewardTokenAddresses() external view returns (address[] memory);\\n\\n function harvesterAddress() external view returns (address);\\n\\n function transferToken(address token, uint256 amount) external;\\n\\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\\n external;\\n}\\n\",\"keccak256\":\"0x79ca47defb3b5a56bba13f14c440838152fd1c1aa640476154516a16da4da8ba\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/ISwapper.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\ninterface ISwapper {\\n /**\\n * @param fromAsset The token address of the asset being sold.\\n * @param toAsset The token address of the asset being purchased.\\n * @param fromAssetAmount The amount of assets being sold.\\n * @param minToAssetAmmount The minimum amount of assets to be purchased.\\n * @param data tx.data returned from 1Inch's /v5.0/1/swap API\\n */\\n function swap(\\n address fromAsset,\\n address toAsset,\\n uint256 fromAssetAmount,\\n uint256 minToAssetAmmount,\\n bytes calldata data\\n ) external returns (uint256 toAssetAmount);\\n}\\n\",\"keccak256\":\"0x70546d5e20c833bcd261ca3a4349c747e3fb3b44e1dd0fce4d2eaec80ff74379\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IVault.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { VaultStorage } from \\\"../vault/VaultStorage.sol\\\";\\n\\ninterface IVault {\\n // slither-disable-start constable-states\\n\\n event AssetSupported(address _asset);\\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\\n event StrategyApproved(address _addr);\\n event StrategyRemoved(address _addr);\\n event Mint(address _addr, uint256 _value);\\n event Redeem(address _addr, uint256 _value);\\n event CapitalPaused();\\n event CapitalUnpaused();\\n event RebasePaused();\\n event RebaseUnpaused();\\n event VaultBufferUpdated(uint256 _vaultBuffer);\\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\\n event PriceProviderUpdated(address _priceProvider);\\n event AllocateThresholdUpdated(uint256 _threshold);\\n event RebaseThresholdUpdated(uint256 _threshold);\\n event StrategistUpdated(address _address);\\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\\n event TrusteeFeeBpsChanged(uint256 _basis);\\n event TrusteeAddressChanged(address _address);\\n event SwapperChanged(address _address);\\n event SwapAllowedUndervalueChanged(uint256 _basis);\\n event SwapSlippageChanged(address _asset, uint256 _basis);\\n event Swapped(\\n address indexed _fromAsset,\\n address indexed _toAsset,\\n uint256 _fromAssetAmount,\\n uint256 _toAssetAmount\\n );\\n event StrategyAddedToMintWhitelist(address indexed strategy);\\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\\n event DripperChanged(address indexed _dripper);\\n event WithdrawalRequested(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount,\\n uint256 _queued\\n );\\n event WithdrawalClaimed(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount\\n );\\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\\n\\n // Governable.sol\\n function transferGovernance(address _newGovernor) external;\\n\\n function claimGovernance() external;\\n\\n function governor() external view returns (address);\\n\\n function ADMIN_IMPLEMENTATION() external view returns (address);\\n\\n // VaultAdmin.sol\\n function setPriceProvider(address _priceProvider) external;\\n\\n function priceProvider() external view returns (address);\\n\\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\\n\\n function redeemFeeBps() external view returns (uint256);\\n\\n function setVaultBuffer(uint256 _vaultBuffer) external;\\n\\n function vaultBuffer() external view returns (uint256);\\n\\n function setAutoAllocateThreshold(uint256 _threshold) external;\\n\\n function autoAllocateThreshold() external view returns (uint256);\\n\\n function setRebaseThreshold(uint256 _threshold) external;\\n\\n function rebaseThreshold() external view returns (uint256);\\n\\n function setStrategistAddr(address _address) external;\\n\\n function strategistAddr() external view returns (address);\\n\\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\\n\\n function maxSupplyDiff() external view returns (uint256);\\n\\n function setTrusteeAddress(address _address) external;\\n\\n function trusteeAddress() external view returns (address);\\n\\n function setTrusteeFeeBps(uint256 _basis) external;\\n\\n function trusteeFeeBps() external view returns (uint256);\\n\\n function ousdMetaStrategy() external view returns (address);\\n\\n function setSwapper(address _swapperAddr) external;\\n\\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\\n\\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\\n external;\\n\\n function supportAsset(address _asset, uint8 _unitConversion) external;\\n\\n function approveStrategy(address _addr) external;\\n\\n function removeStrategy(address _addr) external;\\n\\n function setAssetDefaultStrategy(address _asset, address _strategy)\\n external;\\n\\n function assetDefaultStrategies(address _asset)\\n external\\n view\\n returns (address);\\n\\n function pauseRebase() external;\\n\\n function unpauseRebase() external;\\n\\n function rebasePaused() external view returns (bool);\\n\\n function pauseCapital() external;\\n\\n function unpauseCapital() external;\\n\\n function capitalPaused() external view returns (bool);\\n\\n function transferToken(address _asset, uint256 _amount) external;\\n\\n function priceUnitMint(address asset) external view returns (uint256);\\n\\n function priceUnitRedeem(address asset) external view returns (uint256);\\n\\n function withdrawAllFromStrategy(address _strategyAddr) external;\\n\\n function withdrawAllFromStrategies() external;\\n\\n function withdrawFromStrategy(\\n address _strategyFromAddress,\\n address[] calldata _assets,\\n uint256[] calldata _amounts\\n ) external;\\n\\n function depositToStrategy(\\n address _strategyToAddress,\\n address[] calldata _assets,\\n uint256[] calldata _amounts\\n ) external;\\n\\n // VaultCore.sol\\n function mint(\\n address _asset,\\n uint256 _amount,\\n uint256 _minimumOusdAmount\\n ) external;\\n\\n function mintForStrategy(uint256 _amount) external;\\n\\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\\n\\n function burnForStrategy(uint256 _amount) external;\\n\\n function allocate() external;\\n\\n function rebase() external;\\n\\n function swapCollateral(\\n address fromAsset,\\n address toAsset,\\n uint256 fromAssetAmount,\\n uint256 minToAssetAmount,\\n bytes calldata data\\n ) external returns (uint256 toAssetAmount);\\n\\n function totalValue() external view returns (uint256 value);\\n\\n function checkBalance(address _asset) external view returns (uint256);\\n\\n function calculateRedeemOutputs(uint256 _amount)\\n external\\n view\\n returns (uint256[] memory);\\n\\n function getAssetCount() external view returns (uint256);\\n\\n function getAssetConfig(address _asset)\\n external\\n view\\n returns (VaultStorage.Asset memory config);\\n\\n function getAllAssets() external view returns (address[] memory);\\n\\n function getStrategyCount() external view returns (uint256);\\n\\n function swapper() external view returns (address);\\n\\n function allowedSwapUndervalue() external view returns (uint256);\\n\\n function getAllStrategies() external view returns (address[] memory);\\n\\n function isSupportedAsset(address _asset) external view returns (bool);\\n\\n function netOusdMintForStrategyThreshold() external view returns (uint256);\\n\\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\\n\\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\\n\\n function netOusdMintedForStrategy() external view returns (int256);\\n\\n function setDripper(address _dripper) external;\\n\\n function dripper() external view returns (address);\\n\\n function weth() external view returns (address);\\n\\n function cacheWETHAssetIndex() external;\\n\\n function wethAssetIndex() external view returns (uint256);\\n\\n function initialize(address, address) external;\\n\\n function setAdminImpl(address) external;\\n\\n function removeAsset(address _asset) external;\\n\\n // These are OETH specific functions\\n function addWithdrawalQueueLiquidity() external;\\n\\n function requestWithdrawal(uint256 _amount)\\n external\\n returns (uint256 requestId, uint256 queued);\\n\\n function claimWithdrawal(uint256 requestId)\\n external\\n returns (uint256 amount);\\n\\n function claimWithdrawals(uint256[] memory requestIds)\\n external\\n returns (uint256[] memory amounts, uint256 totalAmount);\\n\\n function withdrawalQueueMetadata()\\n external\\n view\\n returns (VaultStorage.WithdrawalQueueMetadata memory);\\n\\n function withdrawalRequests(uint256 requestId)\\n external\\n view\\n returns (VaultStorage.WithdrawalRequest memory);\\n\\n // OETHb specific functions\\n function addStrategyToMintWhitelist(address strategyAddr) external;\\n\\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\\n\\n function isMintWhitelistedStrategy(address strategyAddr)\\n external\\n view\\n returns (bool);\\n\\n function withdrawalClaimDelay() external view returns (uint256);\\n\\n function setWithdrawalClaimDelay(uint256 newDelay) external;\\n\\n function lastRebase() external view returns (uint64);\\n\\n function dripDuration() external view returns (uint64);\\n\\n function setDripDuration(uint256 _dripDuration) external;\\n\\n function rebasePerSecondMax() external view returns (uint64);\\n\\n function setRebaseRateMax(uint256 yearlyApr) external;\\n\\n function rebasePerSecondTarget() external view returns (uint64);\\n\\n function previewYield() external view returns (uint256 yield);\\n\\n // slither-disable-end constable-states\\n}\\n\",\"keccak256\":\"0x8d0a60f594d97578b0513b4da3d8fcafaa601950c6c4c016bf60b1344733269c\",\"license\":\"BUSL-1.1\"},\"contracts/token/OUSD.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title OUSD Token Contract\\n * @dev ERC20 compatible contract for OUSD\\n * @dev Implements an elastic supply\\n * @author Origin Protocol Inc\\n */\\nimport { IVault } from \\\"../interfaces/IVault.sol\\\";\\nimport { Governable } from \\\"../governance/Governable.sol\\\";\\nimport { SafeCast } from \\\"@openzeppelin/contracts/utils/math/SafeCast.sol\\\";\\n\\ncontract OUSD is Governable {\\n using SafeCast for int256;\\n using SafeCast for uint256;\\n\\n /// @dev Event triggered when the supply changes\\n /// @param totalSupply Updated token total supply\\n /// @param rebasingCredits Updated token rebasing credits\\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\\n event TotalSupplyUpdatedHighres(\\n uint256 totalSupply,\\n uint256 rebasingCredits,\\n uint256 rebasingCreditsPerToken\\n );\\n /// @dev Event triggered when an account opts in for rebasing\\n /// @param account Address of the account\\n event AccountRebasingEnabled(address account);\\n /// @dev Event triggered when an account opts out of rebasing\\n /// @param account Address of the account\\n event AccountRebasingDisabled(address account);\\n /// @dev Emitted when `value` tokens are moved from one account `from` to\\n /// another `to`.\\n /// @param from Address of the account tokens are moved from\\n /// @param to Address of the account tokens are moved to\\n /// @param value Amount of tokens transferred\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n /// a call to {approve}. `value` is the new allowance.\\n /// @param owner Address of the owner approving allowance\\n /// @param spender Address of the spender allowance is granted to\\n /// @param value Amount of tokens spender can transfer\\n event Approval(\\n address indexed owner,\\n address indexed spender,\\n uint256 value\\n );\\n /// @dev Yield resulting from {changeSupply} that a `source` account would\\n /// receive is directed to `target` account.\\n /// @param source Address of the source forwarding the yield\\n /// @param target Address of the target receiving the yield\\n event YieldDelegated(address source, address target);\\n /// @dev Yield delegation from `source` account to the `target` account is\\n /// suspended.\\n /// @param source Address of the source suspending yield forwarding\\n /// @param target Address of the target no longer receiving yield from `source`\\n /// account\\n event YieldUndelegated(address source, address target);\\n\\n enum RebaseOptions {\\n NotSet,\\n StdNonRebasing,\\n StdRebasing,\\n YieldDelegationSource,\\n YieldDelegationTarget\\n }\\n\\n uint256[154] private _gap; // Slots to align with deployed contract\\n uint256 private constant MAX_SUPPLY = type(uint128).max;\\n /// @dev The amount of tokens in existence\\n uint256 public totalSupply;\\n mapping(address => mapping(address => uint256)) private allowances;\\n /// @dev The vault with privileges to execute {mint}, {burn}\\n /// and {changeSupply}\\n address public vaultAddress;\\n mapping(address => uint256) internal creditBalances;\\n // the 2 storage variables below need trailing underscores to not name collide with public functions\\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\\n uint256 private rebasingCreditsPerToken_;\\n /// @dev The amount of tokens that are not rebasing - receiving yield\\n uint256 public nonRebasingSupply;\\n mapping(address => uint256) internal alternativeCreditsPerToken;\\n /// @dev A map of all addresses and their respective RebaseOptions\\n mapping(address => RebaseOptions) public rebaseState;\\n mapping(address => uint256) private __deprecated_isUpgraded;\\n /// @dev A map of addresses that have yields forwarded to. This is an\\n /// inverse mapping of {yieldFrom}\\n /// Key Account forwarding yield\\n /// Value Account receiving yield\\n mapping(address => address) public yieldTo;\\n /// @dev A map of addresses that are receiving the yield. This is an\\n /// inverse mapping of {yieldTo}\\n /// Key Account receiving yield\\n /// Value Account forwarding yield\\n mapping(address => address) public yieldFrom;\\n\\n uint256 private constant RESOLUTION_INCREASE = 1e9;\\n uint256[34] private __gap; // including below gap totals up to 200\\n\\n /// @dev Verifies that the caller is the Governor or Strategist.\\n modifier onlyGovernorOrStrategist() {\\n require(\\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\\n \\\"Caller is not the Strategist or Governor\\\"\\n );\\n _;\\n }\\n\\n /// @dev Initializes the contract and sets necessary variables.\\n /// @param _vaultAddress Address of the vault contract\\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\\n external\\n onlyGovernor\\n {\\n require(_vaultAddress != address(0), \\\"Zero vault address\\\");\\n require(vaultAddress == address(0), \\\"Already initialized\\\");\\n\\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\\n vaultAddress = _vaultAddress;\\n }\\n\\n /// @dev Returns the symbol of the token, a shorter version\\n /// of the name.\\n function symbol() external pure virtual returns (string memory) {\\n return \\\"OUSD\\\";\\n }\\n\\n /// @dev Returns the name of the token.\\n function name() external pure virtual returns (string memory) {\\n return \\\"Origin Dollar\\\";\\n }\\n\\n /// @dev Returns the number of decimals used to get its user representation.\\n function decimals() external pure virtual returns (uint8) {\\n return 18;\\n }\\n\\n /**\\n * @dev Verifies that the caller is the Vault contract\\n */\\n modifier onlyVault() {\\n require(vaultAddress == msg.sender, \\\"Caller is not the Vault\\\");\\n _;\\n }\\n\\n /**\\n * @return High resolution rebasingCreditsPerToken\\n */\\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\\n return rebasingCreditsPerToken_;\\n }\\n\\n /**\\n * @return Low resolution rebasingCreditsPerToken\\n */\\n function rebasingCreditsPerToken() external view returns (uint256) {\\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\\n }\\n\\n /**\\n * @return High resolution total number of rebasing credits\\n */\\n function rebasingCreditsHighres() external view returns (uint256) {\\n return rebasingCredits_;\\n }\\n\\n /**\\n * @return Low resolution total number of rebasing credits\\n */\\n function rebasingCredits() external view returns (uint256) {\\n return rebasingCredits_ / RESOLUTION_INCREASE;\\n }\\n\\n /**\\n * @notice Gets the balance of the specified address.\\n * @param _account Address to query the balance of.\\n * @return A uint256 representing the amount of base units owned by the\\n * specified address.\\n */\\n function balanceOf(address _account) public view returns (uint256) {\\n RebaseOptions state = rebaseState[_account];\\n if (state == RebaseOptions.YieldDelegationSource) {\\n // Saves a slot read when transferring to or from a yield delegating source\\n // since we know creditBalances equals the balance.\\n return creditBalances[_account];\\n }\\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\\n _creditsPerToken(_account);\\n if (state == RebaseOptions.YieldDelegationTarget) {\\n // creditBalances of yieldFrom accounts equals token balances\\n return baseBalance - creditBalances[yieldFrom[_account]];\\n }\\n return baseBalance;\\n }\\n\\n /**\\n * @notice Gets the credits balance of the specified address.\\n * @dev Backwards compatible with old low res credits per token.\\n * @param _account The address to query the balance of.\\n * @return (uint256, uint256) Credit balance and credits per token of the\\n * address\\n */\\n function creditsBalanceOf(address _account)\\n external\\n view\\n returns (uint256, uint256)\\n {\\n uint256 cpt = _creditsPerToken(_account);\\n if (cpt == 1e27) {\\n // For a period before the resolution upgrade, we created all new\\n // contract accounts at high resolution. Since they are not changing\\n // as a result of this upgrade, we will return their true values\\n return (creditBalances[_account], cpt);\\n } else {\\n return (\\n creditBalances[_account] / RESOLUTION_INCREASE,\\n cpt / RESOLUTION_INCREASE\\n );\\n }\\n }\\n\\n /**\\n * @notice Gets the credits balance of the specified address.\\n * @param _account The address to query the balance of.\\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\\n * address, and isUpgraded\\n */\\n function creditsBalanceOfHighres(address _account)\\n external\\n view\\n returns (\\n uint256,\\n uint256,\\n bool\\n )\\n {\\n return (\\n creditBalances[_account],\\n _creditsPerToken(_account),\\n true // all accounts have their resolution \\\"upgraded\\\"\\n );\\n }\\n\\n // Backwards compatible view\\n function nonRebasingCreditsPerToken(address _account)\\n external\\n view\\n returns (uint256)\\n {\\n return alternativeCreditsPerToken[_account];\\n }\\n\\n /**\\n * @notice Transfer tokens to a specified address.\\n * @param _to the address to transfer to.\\n * @param _value the amount to be transferred.\\n * @return true on success.\\n */\\n function transfer(address _to, uint256 _value) external returns (bool) {\\n require(_to != address(0), \\\"Transfer to zero address\\\");\\n\\n _executeTransfer(msg.sender, _to, _value);\\n\\n emit Transfer(msg.sender, _to, _value);\\n return true;\\n }\\n\\n /**\\n * @notice Transfer tokens from one address to another.\\n * @param _from The address you want to send tokens from.\\n * @param _to The address you want to transfer to.\\n * @param _value The amount of tokens to be transferred.\\n * @return true on success.\\n */\\n function transferFrom(\\n address _from,\\n address _to,\\n uint256 _value\\n ) external returns (bool) {\\n require(_to != address(0), \\\"Transfer to zero address\\\");\\n uint256 userAllowance = allowances[_from][msg.sender];\\n require(_value <= userAllowance, \\\"Allowance exceeded\\\");\\n\\n unchecked {\\n allowances[_from][msg.sender] = userAllowance - _value;\\n }\\n\\n _executeTransfer(_from, _to, _value);\\n\\n emit Transfer(_from, _to, _value);\\n return true;\\n }\\n\\n function _executeTransfer(\\n address _from,\\n address _to,\\n uint256 _value\\n ) internal {\\n (\\n int256 fromRebasingCreditsDiff,\\n int256 fromNonRebasingSupplyDiff\\n ) = _adjustAccount(_from, -_value.toInt256());\\n (\\n int256 toRebasingCreditsDiff,\\n int256 toNonRebasingSupplyDiff\\n ) = _adjustAccount(_to, _value.toInt256());\\n\\n _adjustGlobals(\\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\\n );\\n }\\n\\n function _adjustAccount(address _account, int256 _balanceChange)\\n internal\\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\\n {\\n RebaseOptions state = rebaseState[_account];\\n int256 currentBalance = balanceOf(_account).toInt256();\\n if (currentBalance + _balanceChange < 0) {\\n revert(\\\"Transfer amount exceeds balance\\\");\\n }\\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\\n\\n if (state == RebaseOptions.YieldDelegationSource) {\\n address target = yieldTo[_account];\\n uint256 targetOldBalance = balanceOf(target);\\n uint256 targetNewCredits = _balanceToRebasingCredits(\\n targetOldBalance + newBalance\\n );\\n rebasingCreditsDiff =\\n targetNewCredits.toInt256() -\\n creditBalances[target].toInt256();\\n\\n creditBalances[_account] = newBalance;\\n creditBalances[target] = targetNewCredits;\\n } else if (state == RebaseOptions.YieldDelegationTarget) {\\n uint256 newCredits = _balanceToRebasingCredits(\\n newBalance + creditBalances[yieldFrom[_account]]\\n );\\n rebasingCreditsDiff =\\n newCredits.toInt256() -\\n creditBalances[_account].toInt256();\\n creditBalances[_account] = newCredits;\\n } else {\\n _autoMigrate(_account);\\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\\n _account\\n ];\\n if (alternativeCreditsPerTokenMem > 0) {\\n nonRebasingSupplyDiff = _balanceChange;\\n if (alternativeCreditsPerTokenMem != 1e18) {\\n alternativeCreditsPerToken[_account] = 1e18;\\n }\\n creditBalances[_account] = newBalance;\\n } else {\\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\\n rebasingCreditsDiff =\\n newCredits.toInt256() -\\n creditBalances[_account].toInt256();\\n creditBalances[_account] = newCredits;\\n }\\n }\\n }\\n\\n function _adjustGlobals(\\n int256 _rebasingCreditsDiff,\\n int256 _nonRebasingSupplyDiff\\n ) internal {\\n if (_rebasingCreditsDiff != 0) {\\n rebasingCredits_ = (rebasingCredits_.toInt256() +\\n _rebasingCreditsDiff).toUint256();\\n }\\n if (_nonRebasingSupplyDiff != 0) {\\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\\n _nonRebasingSupplyDiff).toUint256();\\n }\\n }\\n\\n /**\\n * @notice Function to check the amount of tokens that _owner has allowed\\n * to `_spender`.\\n * @param _owner The address which owns the funds.\\n * @param _spender The address which will spend the funds.\\n * @return The number of tokens still available for the _spender.\\n */\\n function allowance(address _owner, address _spender)\\n external\\n view\\n returns (uint256)\\n {\\n return allowances[_owner][_spender];\\n }\\n\\n /**\\n * @notice Approve the passed address to spend the specified amount of\\n * tokens on behalf of msg.sender.\\n * @param _spender The address which will spend the funds.\\n * @param _value The amount of tokens to be spent.\\n * @return true on success.\\n */\\n function approve(address _spender, uint256 _value) external returns (bool) {\\n allowances[msg.sender][_spender] = _value;\\n emit Approval(msg.sender, _spender, _value);\\n return true;\\n }\\n\\n /**\\n * @notice Creates `_amount` tokens and assigns them to `_account`,\\n * increasing the total supply.\\n */\\n function mint(address _account, uint256 _amount) external onlyVault {\\n require(_account != address(0), \\\"Mint to the zero address\\\");\\n\\n // Account\\n (\\n int256 toRebasingCreditsDiff,\\n int256 toNonRebasingSupplyDiff\\n ) = _adjustAccount(_account, _amount.toInt256());\\n // Globals\\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\\n totalSupply = totalSupply + _amount;\\n\\n require(totalSupply < MAX_SUPPLY, \\\"Max supply\\\");\\n emit Transfer(address(0), _account, _amount);\\n }\\n\\n /**\\n * @notice Destroys `_amount` tokens from `_account`,\\n * reducing the total supply.\\n */\\n function burn(address _account, uint256 _amount) external onlyVault {\\n require(_account != address(0), \\\"Burn from the zero address\\\");\\n if (_amount == 0) {\\n return;\\n }\\n\\n // Account\\n (\\n int256 toRebasingCreditsDiff,\\n int256 toNonRebasingSupplyDiff\\n ) = _adjustAccount(_account, -_amount.toInt256());\\n // Globals\\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\\n totalSupply = totalSupply - _amount;\\n\\n emit Transfer(_account, address(0), _amount);\\n }\\n\\n /**\\n * @dev Get the credits per token for an account. Returns a fixed amount\\n * if the account is non-rebasing.\\n * @param _account Address of the account.\\n */\\n function _creditsPerToken(address _account)\\n internal\\n view\\n returns (uint256)\\n {\\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\\n _account\\n ];\\n if (alternativeCreditsPerTokenMem != 0) {\\n return alternativeCreditsPerTokenMem;\\n } else {\\n return rebasingCreditsPerToken_;\\n }\\n }\\n\\n /**\\n * @dev Auto migrate contracts to be non rebasing,\\n * unless they have opted into yield.\\n * @param _account Address of the account.\\n */\\n function _autoMigrate(address _account) internal {\\n uint256 codeLen = _account.code.length;\\n bool isEOA = (codeLen == 0) ||\\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\\n // In previous code versions, contracts would not have had their\\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\\n // therefore we check the actual accounting used on the account as well.\\n if (\\n (!isEOA) &&\\n rebaseState[_account] == RebaseOptions.NotSet &&\\n alternativeCreditsPerToken[_account] == 0\\n ) {\\n _rebaseOptOut(_account);\\n }\\n }\\n\\n /**\\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\\n * also balance that corresponds to those credits. The latter is important\\n * when adjusting the contract's global nonRebasingSupply to circumvent any\\n * possible rounding errors.\\n *\\n * @param _balance Balance of the account.\\n */\\n function _balanceToRebasingCredits(uint256 _balance)\\n internal\\n view\\n returns (uint256 rebasingCredits)\\n {\\n // Rounds up, because we need to ensure that accounts always have\\n // at least the balance that they should have.\\n // Note this should always be used on an absolute account value,\\n // not on a possibly negative diff, because then the rounding would be wrong.\\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\\n }\\n\\n /**\\n * @notice The calling account will start receiving yield after a successful call.\\n * @param _account Address of the account.\\n */\\n function governanceRebaseOptIn(address _account) external onlyGovernor {\\n require(_account != address(0), \\\"Zero address not allowed\\\");\\n _rebaseOptIn(_account);\\n }\\n\\n /**\\n * @notice The calling account will start receiving yield after a successful call.\\n */\\n function rebaseOptIn() external {\\n _rebaseOptIn(msg.sender);\\n }\\n\\n function _rebaseOptIn(address _account) internal {\\n uint256 balance = balanceOf(_account);\\n\\n // prettier-ignore\\n require(\\n alternativeCreditsPerToken[_account] > 0 ||\\n // Accounts may explicitly `rebaseOptIn` regardless of\\n // accounting if they have a 0 balance.\\n creditBalances[_account] == 0\\n ,\\n \\\"Account must be non-rebasing\\\"\\n );\\n RebaseOptions state = rebaseState[_account];\\n // prettier-ignore\\n require(\\n state == RebaseOptions.StdNonRebasing ||\\n state == RebaseOptions.NotSet,\\n \\\"Only standard non-rebasing accounts can opt in\\\"\\n );\\n\\n uint256 newCredits = _balanceToRebasingCredits(balance);\\n\\n // Account\\n rebaseState[_account] = RebaseOptions.StdRebasing;\\n alternativeCreditsPerToken[_account] = 0;\\n creditBalances[_account] = newCredits;\\n // Globals\\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\\n\\n emit AccountRebasingEnabled(_account);\\n }\\n\\n /**\\n * @notice The calling account will no longer receive yield\\n */\\n function rebaseOptOut() external {\\n _rebaseOptOut(msg.sender);\\n }\\n\\n function _rebaseOptOut(address _account) internal {\\n require(\\n alternativeCreditsPerToken[_account] == 0,\\n \\\"Account must be rebasing\\\"\\n );\\n RebaseOptions state = rebaseState[_account];\\n require(\\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\\n \\\"Only standard rebasing accounts can opt out\\\"\\n );\\n\\n uint256 oldCredits = creditBalances[_account];\\n uint256 balance = balanceOf(_account);\\n\\n // Account\\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\\n alternativeCreditsPerToken[_account] = 1e18;\\n creditBalances[_account] = balance;\\n // Globals\\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\\n\\n emit AccountRebasingDisabled(_account);\\n }\\n\\n /**\\n * @notice Distribute yield to users. This changes the exchange rate\\n * between \\\"credits\\\" and OUSD tokens to change rebasing user's balances.\\n * @param _newTotalSupply New total supply of OUSD.\\n */\\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\\n require(totalSupply > 0, \\\"Cannot increase 0 supply\\\");\\n\\n if (totalSupply == _newTotalSupply) {\\n emit TotalSupplyUpdatedHighres(\\n totalSupply,\\n rebasingCredits_,\\n rebasingCreditsPerToken_\\n );\\n return;\\n }\\n\\n totalSupply = _newTotalSupply > MAX_SUPPLY\\n ? MAX_SUPPLY\\n : _newTotalSupply;\\n\\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\\n // round up in the favour of the protocol\\n rebasingCreditsPerToken_ =\\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\\n rebasingSupply;\\n\\n require(rebasingCreditsPerToken_ > 0, \\\"Invalid change in supply\\\");\\n\\n emit TotalSupplyUpdatedHighres(\\n totalSupply,\\n rebasingCredits_,\\n rebasingCreditsPerToken_\\n );\\n }\\n\\n /*\\n * @notice Send the yield from one account to another account.\\n * Each account keeps its own balances.\\n */\\n function delegateYield(address _from, address _to)\\n external\\n onlyGovernorOrStrategist\\n {\\n require(_from != address(0), \\\"Zero from address not allowed\\\");\\n require(_to != address(0), \\\"Zero to address not allowed\\\");\\n\\n require(_from != _to, \\\"Cannot delegate to self\\\");\\n require(\\n yieldFrom[_to] == address(0) &&\\n yieldTo[_to] == address(0) &&\\n yieldFrom[_from] == address(0) &&\\n yieldTo[_from] == address(0),\\n \\\"Blocked by existing yield delegation\\\"\\n );\\n RebaseOptions stateFrom = rebaseState[_from];\\n RebaseOptions stateTo = rebaseState[_to];\\n\\n require(\\n stateFrom == RebaseOptions.NotSet ||\\n stateFrom == RebaseOptions.StdNonRebasing ||\\n stateFrom == RebaseOptions.StdRebasing,\\n \\\"Invalid rebaseState from\\\"\\n );\\n\\n require(\\n stateTo == RebaseOptions.NotSet ||\\n stateTo == RebaseOptions.StdNonRebasing ||\\n stateTo == RebaseOptions.StdRebasing,\\n \\\"Invalid rebaseState to\\\"\\n );\\n\\n if (alternativeCreditsPerToken[_from] == 0) {\\n _rebaseOptOut(_from);\\n }\\n if (alternativeCreditsPerToken[_to] > 0) {\\n _rebaseOptIn(_to);\\n }\\n\\n uint256 fromBalance = balanceOf(_from);\\n uint256 toBalance = balanceOf(_to);\\n uint256 oldToCredits = creditBalances[_to];\\n uint256 newToCredits = _balanceToRebasingCredits(\\n fromBalance + toBalance\\n );\\n\\n // Set up the bidirectional links\\n yieldTo[_from] = _to;\\n yieldFrom[_to] = _from;\\n\\n // Local\\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\\n alternativeCreditsPerToken[_from] = 1e18;\\n creditBalances[_from] = fromBalance;\\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\\n creditBalances[_to] = newToCredits;\\n\\n // Global\\n int256 creditsChange = newToCredits.toInt256() -\\n oldToCredits.toInt256();\\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\\n emit YieldDelegated(_from, _to);\\n }\\n\\n /*\\n * @notice Stop sending the yield from one account to another account.\\n */\\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\\n // Require a delegation, which will also ensure a valid delegation\\n require(yieldTo[_from] != address(0), \\\"Zero address not allowed\\\");\\n\\n address to = yieldTo[_from];\\n uint256 fromBalance = balanceOf(_from);\\n uint256 toBalance = balanceOf(to);\\n uint256 oldToCredits = creditBalances[to];\\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\\n\\n // Remove the bidirectional links\\n yieldFrom[to] = address(0);\\n yieldTo[_from] = address(0);\\n\\n // Local\\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\\n creditBalances[_from] = fromBalance;\\n rebaseState[to] = RebaseOptions.StdRebasing;\\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\\n creditBalances[to] = newToCredits;\\n\\n // Global\\n int256 creditsChange = newToCredits.toInt256() -\\n oldToCredits.toInt256();\\n _adjustGlobals(creditsChange, fromBalance.toInt256());\\n emit YieldUndelegated(_from, to);\\n }\\n}\\n\",\"keccak256\":\"0x73439bef6569f5adf6f5ce2cb54a5f0d3109d4819457532236e172a7091980a9\",\"license\":\"BUSL-1.1\"},\"contracts/utils/Helpers.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { IBasicToken } from \\\"../interfaces/IBasicToken.sol\\\";\\n\\nlibrary Helpers {\\n /**\\n * @notice Fetch the `symbol()` from an ERC20 token\\n * @dev Grabs the `symbol()` from a contract\\n * @param _token Address of the ERC20 token\\n * @return string Symbol of the ERC20 token\\n */\\n function getSymbol(address _token) internal view returns (string memory) {\\n string memory symbol = IBasicToken(_token).symbol();\\n return symbol;\\n }\\n\\n /**\\n * @notice Fetch the `decimals()` from an ERC20 token\\n * @dev Grabs the `decimals()` from a contract and fails if\\n * the decimal value does not live within a certain range\\n * @param _token Address of the ERC20 token\\n * @return uint256 Decimals of the ERC20 token\\n */\\n function getDecimals(address _token) internal view returns (uint256) {\\n uint256 decimals = IBasicToken(_token).decimals();\\n require(\\n decimals >= 4 && decimals <= 18,\\n \\\"Token must have sufficient decimal places\\\"\\n );\\n\\n return decimals;\\n }\\n}\\n\",\"keccak256\":\"0x4366f8d90b34c1eef8bbaaf369b1e5cd59f04027bb3c111f208eaee65bbc0346\",\"license\":\"BUSL-1.1\"},\"contracts/utils/Initializable.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base contract any contracts that need to initialize state after deployment.\\n * @author Origin Protocol Inc\\n */\\nabstract contract Initializable {\\n /**\\n * @dev Indicates that the contract has been initialized.\\n */\\n bool private initialized;\\n\\n /**\\n * @dev Indicates that the contract is in the process of being initialized.\\n */\\n bool private initializing;\\n\\n /**\\n * @dev Modifier to protect an initializer function from being invoked twice.\\n */\\n modifier initializer() {\\n require(\\n initializing || !initialized,\\n \\\"Initializable: contract is already initialized\\\"\\n );\\n\\n bool isTopLevelCall = !initializing;\\n if (isTopLevelCall) {\\n initializing = true;\\n initialized = true;\\n }\\n\\n _;\\n\\n if (isTopLevelCall) {\\n initializing = false;\\n }\\n }\\n\\n uint256[50] private ______gap;\\n}\\n\",\"keccak256\":\"0x50d39ebf38a3d3111f2b77a6c75ece1d4ae731552fec4697ab16fcf6c0d4d5e8\",\"license\":\"BUSL-1.1\"},\"contracts/utils/StableMath.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { SafeMath } from \\\"@openzeppelin/contracts/utils/math/SafeMath.sol\\\";\\n\\n// Based on StableMath from Stability Labs Pty. Ltd.\\n// https://github.com/mstable/mStable-contracts/blob/master/contracts/shared/StableMath.sol\\n\\nlibrary StableMath {\\n using SafeMath for uint256;\\n\\n /**\\n * @dev Scaling unit for use in specific calculations,\\n * where 1 * 10**18, or 1e18 represents a unit '1'\\n */\\n uint256 private constant FULL_SCALE = 1e18;\\n\\n /***************************************\\n Helpers\\n ****************************************/\\n\\n /**\\n * @dev Adjust the scale of an integer\\n * @param to Decimals to scale to\\n * @param from Decimals to scale from\\n */\\n function scaleBy(\\n uint256 x,\\n uint256 to,\\n uint256 from\\n ) internal pure returns (uint256) {\\n if (to > from) {\\n x = x.mul(10**(to - from));\\n } else if (to < from) {\\n // slither-disable-next-line divide-before-multiply\\n x = x.div(10**(from - to));\\n }\\n return x;\\n }\\n\\n /***************************************\\n Precise Arithmetic\\n ****************************************/\\n\\n /**\\n * @dev Multiplies two precise units, and then truncates by the full scale\\n * @param x Left hand input to multiplication\\n * @param y Right hand input to multiplication\\n * @return Result after multiplying the two inputs and then dividing by the shared\\n * scale unit\\n */\\n function mulTruncate(uint256 x, uint256 y) internal pure returns (uint256) {\\n return mulTruncateScale(x, y, FULL_SCALE);\\n }\\n\\n /**\\n * @dev Multiplies two precise units, and then truncates by the given scale. For example,\\n * when calculating 90% of 10e18, (10e18 * 9e17) / 1e18 = (9e36) / 1e18 = 9e18\\n * @param x Left hand input to multiplication\\n * @param y Right hand input to multiplication\\n * @param scale Scale unit\\n * @return Result after multiplying the two inputs and then dividing by the shared\\n * scale unit\\n */\\n function mulTruncateScale(\\n uint256 x,\\n uint256 y,\\n uint256 scale\\n ) internal pure returns (uint256) {\\n // e.g. assume scale = fullScale\\n // z = 10e18 * 9e17 = 9e36\\n uint256 z = x.mul(y);\\n // return 9e36 / 1e18 = 9e18\\n return z.div(scale);\\n }\\n\\n /**\\n * @dev Multiplies two precise units, and then truncates by the full scale, rounding up the result\\n * @param x Left hand input to multiplication\\n * @param y Right hand input to multiplication\\n * @return Result after multiplying the two inputs and then dividing by the shared\\n * scale unit, rounded up to the closest base unit.\\n */\\n function mulTruncateCeil(uint256 x, uint256 y)\\n internal\\n pure\\n returns (uint256)\\n {\\n // e.g. 8e17 * 17268172638 = 138145381104e17\\n uint256 scaled = x.mul(y);\\n // e.g. 138145381104e17 + 9.99...e17 = 138145381113.99...e17\\n uint256 ceil = scaled.add(FULL_SCALE.sub(1));\\n // e.g. 13814538111.399...e18 / 1e18 = 13814538111\\n return ceil.div(FULL_SCALE);\\n }\\n\\n /**\\n * @dev Precisely divides two units, by first scaling the left hand operand. Useful\\n * for finding percentage weightings, i.e. 8e18/10e18 = 80% (or 8e17)\\n * @param x Left hand input to division\\n * @param y Right hand input to division\\n * @return Result after multiplying the left operand by the scale, and\\n * executing the division on the right hand input.\\n */\\n function divPrecisely(uint256 x, uint256 y)\\n internal\\n pure\\n returns (uint256)\\n {\\n // e.g. 8e18 * 1e18 = 8e36\\n uint256 z = x.mul(FULL_SCALE);\\n // e.g. 8e36 / 10e18 = 8e17\\n return z.div(y);\\n }\\n}\\n\",\"keccak256\":\"0x71d6ed0053a1e5ef018d27c3b6d024f336d8157ab6f6859e400b3243a50a71b7\",\"license\":\"BUSL-1.1\"},\"contracts/vault/OETHVaultAdmin.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { SafeERC20 } from \\\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\\\";\\nimport { IERC20 } from \\\"@openzeppelin/contracts/token/ERC20/IERC20.sol\\\";\\n\\nimport { IStrategy } from \\\"../interfaces/IStrategy.sol\\\";\\nimport { IVault } from \\\"../interfaces/IVault.sol\\\";\\nimport { VaultAdmin } from \\\"./VaultAdmin.sol\\\";\\n\\n/**\\n * @title OETH VaultAdmin Contract\\n * @author Origin Protocol Inc\\n */\\ncontract OETHVaultAdmin is VaultAdmin {\\n using SafeERC20 for IERC20;\\n\\n address public immutable weth;\\n\\n constructor(address _weth) {\\n weth = _weth;\\n }\\n\\n /**\\n * @notice Adds a strategy to the mint whitelist.\\n * Reverts if strategy isn't approved on Vault.\\n * @param strategyAddr Strategy address\\n */\\n function addStrategyToMintWhitelist(address strategyAddr)\\n external\\n onlyGovernor\\n {\\n require(strategies[strategyAddr].isSupported, \\\"Strategy not approved\\\");\\n\\n require(\\n !isMintWhitelistedStrategy[strategyAddr],\\n \\\"Already whitelisted\\\"\\n );\\n\\n isMintWhitelistedStrategy[strategyAddr] = true;\\n\\n emit StrategyAddedToMintWhitelist(strategyAddr);\\n }\\n\\n /**\\n * @notice Removes a strategy from the mint whitelist.\\n * @param strategyAddr Strategy address\\n */\\n function removeStrategyFromMintWhitelist(address strategyAddr)\\n external\\n onlyGovernor\\n {\\n // Intentionally skipping `strategies.isSupported` check since\\n // we may wanna remove an address even after removing the strategy\\n\\n require(isMintWhitelistedStrategy[strategyAddr], \\\"Not whitelisted\\\");\\n\\n isMintWhitelistedStrategy[strategyAddr] = false;\\n\\n emit StrategyRemovedFromMintWhitelist(strategyAddr);\\n }\\n\\n /// @dev Simplified version of the deposit function as WETH is the only supported asset.\\n function _depositToStrategy(\\n address _strategyToAddress,\\n address[] calldata _assets,\\n uint256[] calldata _amounts\\n ) internal override {\\n require(\\n strategies[_strategyToAddress].isSupported,\\n \\\"Invalid to Strategy\\\"\\n );\\n require(\\n _assets.length == 1 && _amounts.length == 1 && _assets[0] == weth,\\n \\\"Only WETH is supported\\\"\\n );\\n\\n // Check the there is enough WETH to transfer once the WETH reserved for the withdrawal queue is accounted for\\n require(_amounts[0] <= _wethAvailable(), \\\"Not enough WETH available\\\");\\n\\n // Send required amount of funds to the strategy\\n IERC20(weth).safeTransfer(_strategyToAddress, _amounts[0]);\\n\\n // Deposit all the funds that have been sent to the strategy\\n IStrategy(_strategyToAddress).depositAll();\\n }\\n\\n function _withdrawFromStrategy(\\n address _recipient,\\n address _strategyFromAddress,\\n address[] calldata _assets,\\n uint256[] calldata _amounts\\n ) internal override {\\n super._withdrawFromStrategy(\\n _recipient,\\n _strategyFromAddress,\\n _assets,\\n _amounts\\n );\\n\\n IVault(address(this)).addWithdrawalQueueLiquidity();\\n }\\n\\n function _withdrawAllFromStrategy(address _strategyAddr) internal override {\\n super._withdrawAllFromStrategy(_strategyAddr);\\n\\n IVault(address(this)).addWithdrawalQueueLiquidity();\\n }\\n\\n function _withdrawAllFromStrategies() internal override {\\n super._withdrawAllFromStrategies();\\n\\n IVault(address(this)).addWithdrawalQueueLiquidity();\\n }\\n\\n /// @dev Calculate how much WETH in the vault is not reserved for the withdrawal queue.\\n // That is, it is available to be redeemed or deposited into a strategy.\\n function _wethAvailable() internal view returns (uint256 wethAvailable) {\\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\\n\\n // The amount of WETH that is still to be claimed in the withdrawal queue\\n uint256 outstandingWithdrawals = queue.queued - queue.claimed;\\n\\n // The amount of sitting in WETH in the vault\\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\\n\\n // If there is not enough WETH in the vault to cover the outstanding withdrawals\\n if (wethBalance <= outstandingWithdrawals) {\\n return 0;\\n }\\n\\n return wethBalance - outstandingWithdrawals;\\n }\\n\\n function _swapCollateral(\\n address,\\n address,\\n uint256,\\n uint256,\\n bytes calldata\\n ) internal pure override returns (uint256) {\\n revert(\\\"Collateral swap not supported\\\");\\n }\\n}\\n\",\"keccak256\":\"0x3e67589f6ec16a52f4df878cfd44d03753dabd490dcd0868524f1e87c056e545\",\"license\":\"BUSL-1.1\"},\"contracts/vault/VaultAdmin.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title OToken VaultAdmin contract\\n * @notice The VaultAdmin contract makes configuration and admin calls on the vault.\\n * @author Origin Protocol Inc\\n */\\n\\nimport { SafeERC20 } from \\\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\\\";\\n\\nimport { IOracle } from \\\"../interfaces/IOracle.sol\\\";\\nimport { ISwapper } from \\\"../interfaces/ISwapper.sol\\\";\\nimport { IVault } from \\\"../interfaces/IVault.sol\\\";\\nimport { StableMath } from \\\"../utils/StableMath.sol\\\";\\nimport { SafeCast } from \\\"@openzeppelin/contracts/utils/math/SafeCast.sol\\\";\\n\\nimport \\\"./VaultStorage.sol\\\";\\n\\ncontract VaultAdmin is VaultStorage {\\n using SafeERC20 for IERC20;\\n using StableMath for uint256;\\n using SafeCast for uint256;\\n\\n /**\\n * @dev Verifies that the caller is the Governor or Strategist.\\n */\\n modifier onlyGovernorOrStrategist() {\\n require(\\n msg.sender == strategistAddr || isGovernor(),\\n \\\"Caller is not the Strategist or Governor\\\"\\n );\\n _;\\n }\\n\\n /***************************************\\n Configuration\\n ****************************************/\\n\\n /**\\n * @notice Set address of price provider.\\n * @param _priceProvider Address of price provider\\n */\\n function setPriceProvider(address _priceProvider) external onlyGovernor {\\n priceProvider = _priceProvider;\\n emit PriceProviderUpdated(_priceProvider);\\n }\\n\\n /**\\n * @notice Set a fee in basis points to be charged for a redeem.\\n * @param _redeemFeeBps Basis point fee to be charged\\n */\\n function setRedeemFeeBps(uint256 _redeemFeeBps) external onlyGovernor {\\n require(_redeemFeeBps <= 1000, \\\"Redeem fee should not be over 10%\\\");\\n redeemFeeBps = _redeemFeeBps;\\n emit RedeemFeeUpdated(_redeemFeeBps);\\n }\\n\\n /**\\n * @notice Set a buffer of assets to keep in the Vault to handle most\\n * redemptions without needing to spend gas unwinding assets from a Strategy.\\n * @param _vaultBuffer Percentage using 18 decimals. 100% = 1e18.\\n */\\n function setVaultBuffer(uint256 _vaultBuffer)\\n external\\n onlyGovernorOrStrategist\\n {\\n require(_vaultBuffer <= 1e18, \\\"Invalid value\\\");\\n vaultBuffer = _vaultBuffer;\\n emit VaultBufferUpdated(_vaultBuffer);\\n }\\n\\n /**\\n * @notice Sets the minimum amount of OTokens in a mint to trigger an\\n * automatic allocation of funds afterwords.\\n * @param _threshold OToken amount with 18 fixed decimals.\\n */\\n function setAutoAllocateThreshold(uint256 _threshold)\\n external\\n onlyGovernor\\n {\\n autoAllocateThreshold = _threshold;\\n emit AllocateThresholdUpdated(_threshold);\\n }\\n\\n /**\\n * @notice Set a minimum amount of OTokens in a mint or redeem that triggers a\\n * rebase\\n * @param _threshold OToken amount with 18 fixed decimals.\\n */\\n function setRebaseThreshold(uint256 _threshold) external onlyGovernor {\\n rebaseThreshold = _threshold;\\n emit RebaseThresholdUpdated(_threshold);\\n }\\n\\n /**\\n * @notice Set address of Strategist\\n * @param _address Address of Strategist\\n */\\n function setStrategistAddr(address _address) external onlyGovernor {\\n strategistAddr = _address;\\n emit StrategistUpdated(_address);\\n }\\n\\n /**\\n * @notice Set the default Strategy for an asset, i.e. the one which the asset\\n will be automatically allocated to and withdrawn from\\n * @param _asset Address of the asset\\n * @param _strategy Address of the Strategy\\n */\\n function setAssetDefaultStrategy(address _asset, address _strategy)\\n external\\n onlyGovernorOrStrategist\\n {\\n emit AssetDefaultStrategyUpdated(_asset, _strategy);\\n // If its a zero address being passed for the strategy we are removing\\n // the default strategy\\n if (_strategy != address(0)) {\\n // Make sure the strategy meets some criteria\\n require(strategies[_strategy].isSupported, \\\"Strategy not approved\\\");\\n IStrategy strategy = IStrategy(_strategy);\\n require(assets[_asset].isSupported, \\\"Asset is not supported\\\");\\n require(\\n strategy.supportsAsset(_asset),\\n \\\"Asset not supported by Strategy\\\"\\n );\\n }\\n assetDefaultStrategies[_asset] = _strategy;\\n }\\n\\n /**\\n * @notice Set maximum amount of OTokens that can at any point be minted and deployed\\n * to strategy (used only by ConvexOUSDMetaStrategy for now).\\n * @param _threshold OToken amount with 18 fixed decimals.\\n */\\n function setNetOusdMintForStrategyThreshold(uint256 _threshold)\\n external\\n onlyGovernor\\n {\\n /**\\n * Because `netOusdMintedForStrategy` check in vault core works both ways\\n * (positive and negative) the actual impact of the amount of OToken minted\\n * could be double the threshold. E.g.:\\n * - contract has threshold set to 100\\n * - state of netOusdMinted is -90\\n * - in effect it can mint 190 OToken and still be within limits\\n *\\n * We are somewhat mitigating this behaviour by resetting the netOusdMinted\\n * counter whenever new threshold is set. So it can only move one threshold\\n * amount in each direction. This also enables us to reduce the threshold\\n * amount and not have problems with current netOusdMinted being near\\n * limits on either side.\\n */\\n netOusdMintedForStrategy = 0;\\n netOusdMintForStrategyThreshold = _threshold;\\n emit NetOusdMintForStrategyThresholdChanged(_threshold);\\n }\\n\\n /**\\n * @notice Changes the async withdrawal claim period for OETH & superOETHb\\n * @param _delay Delay period (should be between 10 mins to 7 days).\\n * Set to 0 to disable async withdrawals\\n */\\n function setWithdrawalClaimDelay(uint256 _delay) external onlyGovernor {\\n require(\\n _delay == 0 || (_delay >= 10 minutes && _delay <= 15 days),\\n \\\"Invalid claim delay period\\\"\\n );\\n withdrawalClaimDelay = _delay;\\n emit WithdrawalClaimDelayUpdated(_delay);\\n }\\n\\n /**\\n * @notice Set a yield streaming max rate. This spreads yield over\\n * time if it is above the max rate.\\n * @param yearlyApr in 1e18 notation. 3 * 1e18 = 3% APR\\n */\\n function setRebaseRateMax(uint256 yearlyApr)\\n external\\n onlyGovernorOrStrategist\\n {\\n // The old yield will be at the old rate\\n IVault(address(this)).rebase();\\n // Change the rate\\n uint256 newPerSecond = yearlyApr / 100 / 365 days;\\n require(newPerSecond <= MAX_REBASE_PER_SECOND, \\\"Rate too high\\\");\\n rebasePerSecondMax = newPerSecond.toUint64();\\n emit RebasePerSecondMaxChanged(newPerSecond);\\n }\\n\\n /**\\n * @notice Set the drip duration period\\n * @param _dripDuration Time in seconds to target a constant yield rate\\n */\\n function setDripDuration(uint256 _dripDuration)\\n external\\n onlyGovernorOrStrategist\\n {\\n // The old yield will be at the old rate\\n IVault(address(this)).rebase();\\n dripDuration = _dripDuration.toUint64();\\n emit DripDurationChanged(_dripDuration);\\n }\\n\\n /***************************************\\n Swaps\\n ****************************************/\\n\\n /**\\n * @notice Strategist swaps collateral assets sitting in the vault.\\n * @param _fromAsset The token address of the asset being sold by the vault.\\n * @param _toAsset The token address of the asset being purchased by the vault.\\n * @param _fromAssetAmount The amount of assets being sold by the vault.\\n * @param _minToAssetAmount The minimum amount of assets to be purchased.\\n * @param _data implementation specific data. eg 1Inch swap data\\n * @return toAssetAmount The amount of toAssets that was received from the swap\\n */\\n function swapCollateral(\\n address _fromAsset,\\n address _toAsset,\\n uint256 _fromAssetAmount,\\n uint256 _minToAssetAmount,\\n bytes calldata _data\\n )\\n external\\n nonReentrant\\n onlyGovernorOrStrategist\\n returns (uint256 toAssetAmount)\\n {\\n toAssetAmount = _swapCollateral(\\n _fromAsset,\\n _toAsset,\\n _fromAssetAmount,\\n _minToAssetAmount,\\n _data\\n );\\n }\\n\\n function _swapCollateral(\\n address _fromAsset,\\n address _toAsset,\\n uint256 _fromAssetAmount,\\n uint256 _minToAssetAmount,\\n bytes calldata _data\\n ) internal virtual returns (uint256 toAssetAmount) {\\n // Check fromAsset and toAsset are valid\\n Asset memory fromAssetConfig = assets[_fromAsset];\\n Asset memory toAssetConfig = assets[_toAsset];\\n require(fromAssetConfig.isSupported, \\\"From asset is not supported\\\");\\n require(toAssetConfig.isSupported, \\\"To asset is not supported\\\");\\n\\n // Load swap config into memory to avoid separate SLOADs\\n SwapConfig memory config = swapConfig;\\n\\n // Scope a new block to remove toAssetBalBefore from the scope of swapCollateral.\\n // This avoids a stack too deep error.\\n {\\n uint256 toAssetBalBefore = IERC20(_toAsset).balanceOf(\\n address(this)\\n );\\n\\n // Transfer from assets to the swapper contract\\n IERC20(_fromAsset).safeTransfer(config.swapper, _fromAssetAmount);\\n\\n // Call to the Swapper contract to do the actual swap\\n // The -1 is required for stETH which sometimes transfers 1 wei less than what was specified.\\n // slither-disable-next-line unused-return\\n ISwapper(config.swapper).swap(\\n _fromAsset,\\n _toAsset,\\n _fromAssetAmount - 1,\\n _minToAssetAmount,\\n _data\\n );\\n\\n // Compute the change in asset balance held by the Vault\\n toAssetAmount =\\n IERC20(_toAsset).balanceOf(address(this)) -\\n toAssetBalBefore;\\n }\\n\\n // Check the to assets returned is above slippage amount specified by the strategist\\n require(\\n toAssetAmount >= _minToAssetAmount,\\n \\\"Strategist slippage limit\\\"\\n );\\n\\n // Scope a new block to remove minOracleToAssetAmount from the scope of swapCollateral.\\n // This avoids a stack too deep error.\\n {\\n // Check the slippage against the Oracle in case the strategist made a mistake or has become malicious.\\n // to asset amount = from asset amount * from asset price / to asset price\\n uint256 minOracleToAssetAmount = (_fromAssetAmount *\\n (1e4 - fromAssetConfig.allowedOracleSlippageBps) *\\n IOracle(priceProvider).price(_fromAsset)) /\\n (IOracle(priceProvider).price(_toAsset) *\\n (1e4 + toAssetConfig.allowedOracleSlippageBps));\\n\\n // Scale both sides up to 18 decimals to compare\\n require(\\n toAssetAmount.scaleBy(18, toAssetConfig.decimals) >=\\n minOracleToAssetAmount.scaleBy(\\n 18,\\n fromAssetConfig.decimals\\n ),\\n \\\"Oracle slippage limit exceeded\\\"\\n );\\n }\\n\\n // Check the vault's total value hasn't gone below the OToken total supply\\n // by more than the allowed percentage.\\n require(\\n IVault(address(this)).totalValue() >=\\n (oUSD.totalSupply() * ((1e4 - config.allowedUndervalueBps))) /\\n 1e4,\\n \\\"Allowed value < supply\\\"\\n );\\n\\n emit Swapped(_fromAsset, _toAsset, _fromAssetAmount, toAssetAmount);\\n }\\n\\n /***************************************\\n Swap Config\\n ****************************************/\\n\\n /**\\n * @notice Set the contract the performs swaps of collateral assets.\\n * @param _swapperAddr Address of the Swapper contract that implements the ISwapper interface.\\n */\\n function setSwapper(address _swapperAddr) external onlyGovernor {\\n swapConfig.swapper = _swapperAddr;\\n emit SwapperChanged(_swapperAddr);\\n }\\n\\n /// @notice Contract that swaps the vault's collateral assets\\n function swapper() external view returns (address swapper_) {\\n swapper_ = swapConfig.swapper;\\n }\\n\\n /**\\n * @notice Set max allowed percentage the vault total value can drop below the OToken total supply in basis points\\n * when executing collateral swaps.\\n * @param _basis Percentage in basis points. eg 100 == 1%\\n */\\n function setSwapAllowedUndervalue(uint16 _basis) external onlyGovernor {\\n require(_basis < 10001, \\\"Invalid basis points\\\");\\n swapConfig.allowedUndervalueBps = _basis;\\n emit SwapAllowedUndervalueChanged(_basis);\\n }\\n\\n /**\\n * @notice Max allowed percentage the vault total value can drop below the OToken total supply in basis points\\n * when executing a collateral swap.\\n * For example 100 == 1%\\n * @return value Percentage in basis points.\\n */\\n function allowedSwapUndervalue() external view returns (uint256 value) {\\n value = swapConfig.allowedUndervalueBps;\\n }\\n\\n /**\\n * @notice Set the allowed slippage from the Oracle price for collateral asset swaps.\\n * @param _asset Address of the asset token.\\n * @param _allowedOracleSlippageBps allowed slippage from Oracle in basis points. eg 20 = 0.2%. Max 10%.\\n */\\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\\n external\\n onlyGovernor\\n {\\n require(assets[_asset].isSupported, \\\"Asset not supported\\\");\\n require(_allowedOracleSlippageBps < 1000, \\\"Slippage too high\\\");\\n\\n assets[_asset].allowedOracleSlippageBps = _allowedOracleSlippageBps;\\n\\n emit SwapSlippageChanged(_asset, _allowedOracleSlippageBps);\\n }\\n\\n /***************************************\\n Asset Config\\n ****************************************/\\n\\n /**\\n * @notice Add a supported asset to the contract, i.e. one that can be\\n * to mint OTokens.\\n * @param _asset Address of asset\\n */\\n function supportAsset(address _asset, uint8 _unitConversion)\\n external\\n virtual\\n onlyGovernor\\n {\\n require(!assets[_asset].isSupported, \\\"Asset already supported\\\");\\n\\n assets[_asset] = Asset({\\n isSupported: true,\\n unitConversion: UnitConversion(_unitConversion),\\n decimals: 0, // will be overridden in _cacheDecimals\\n allowedOracleSlippageBps: 0 // 0% by default\\n });\\n\\n _cacheDecimals(_asset);\\n allAssets.push(_asset);\\n\\n // Verify that our oracle supports the asset\\n // slither-disable-next-line unused-return\\n IOracle(priceProvider).price(_asset);\\n\\n emit AssetSupported(_asset);\\n }\\n\\n /**\\n * @notice Remove a supported asset from the Vault\\n * @param _asset Address of asset\\n */\\n function removeAsset(address _asset) external onlyGovernor {\\n require(assets[_asset].isSupported, \\\"Asset not supported\\\");\\n\\n // 1e13 for 18 decimals. And 10 for 6 decimals\\n uint256 maxDustBalance = uint256(1e13).scaleBy(\\n assets[_asset].decimals,\\n 18\\n );\\n\\n require(\\n IVault(address(this)).checkBalance(_asset) <= maxDustBalance,\\n \\\"Vault still holds asset\\\"\\n );\\n\\n uint256 assetsCount = allAssets.length;\\n uint256 assetIndex = assetsCount; // initialize at invalid index\\n for (uint256 i = 0; i < assetsCount; ++i) {\\n if (allAssets[i] == _asset) {\\n assetIndex = i;\\n break;\\n }\\n }\\n\\n // Note: If asset is not found in `allAssets`, the following line\\n // will revert with an out-of-bound error. However, there's no\\n // reason why an asset would have `Asset.isSupported = true` but\\n // not exist in `allAssets`.\\n\\n // Update allAssets array\\n allAssets[assetIndex] = allAssets[assetsCount - 1];\\n allAssets.pop();\\n\\n // Reset default strategy\\n assetDefaultStrategies[_asset] = address(0);\\n emit AssetDefaultStrategyUpdated(_asset, address(0));\\n\\n // Remove asset from storage\\n delete assets[_asset];\\n\\n emit AssetRemoved(_asset);\\n }\\n\\n /**\\n * @notice Cache decimals on OracleRouter for a particular asset. This action\\n * is required before that asset's price can be accessed.\\n * @param _asset Address of asset token\\n */\\n function cacheDecimals(address _asset) external onlyGovernor {\\n _cacheDecimals(_asset);\\n }\\n\\n /***************************************\\n Strategy Config\\n ****************************************/\\n\\n /**\\n * @notice Add a strategy to the Vault.\\n * @param _addr Address of the strategy to add\\n */\\n function approveStrategy(address _addr) external onlyGovernor {\\n require(!strategies[_addr].isSupported, \\\"Strategy already approved\\\");\\n strategies[_addr] = Strategy({ isSupported: true, _deprecated: 0 });\\n allStrategies.push(_addr);\\n emit StrategyApproved(_addr);\\n }\\n\\n /**\\n * @notice Remove a strategy from the Vault.\\n * @param _addr Address of the strategy to remove\\n */\\n\\n function removeStrategy(address _addr) external onlyGovernor {\\n require(strategies[_addr].isSupported, \\\"Strategy not approved\\\");\\n\\n uint256 assetCount = allAssets.length;\\n for (uint256 i = 0; i < assetCount; ++i) {\\n require(\\n assetDefaultStrategies[allAssets[i]] != _addr,\\n \\\"Strategy is default for an asset\\\"\\n );\\n }\\n\\n // Initialize strategyIndex with out of bounds result so function will\\n // revert if no valid index found\\n uint256 stratCount = allStrategies.length;\\n uint256 strategyIndex = stratCount;\\n for (uint256 i = 0; i < stratCount; ++i) {\\n if (allStrategies[i] == _addr) {\\n strategyIndex = i;\\n break;\\n }\\n }\\n\\n if (strategyIndex < stratCount) {\\n allStrategies[strategyIndex] = allStrategies[stratCount - 1];\\n allStrategies.pop();\\n\\n // Mark the strategy as not supported\\n strategies[_addr].isSupported = false;\\n\\n // Withdraw all assets\\n IStrategy strategy = IStrategy(_addr);\\n strategy.withdrawAll();\\n\\n emit StrategyRemoved(_addr);\\n }\\n }\\n\\n /***************************************\\n Strategies\\n ****************************************/\\n\\n /**\\n * @notice Deposit multiple assets from the vault into the strategy.\\n * @param _strategyToAddress Address of the Strategy to deposit assets into.\\n * @param _assets Array of asset address that will be deposited into the strategy.\\n * @param _amounts Array of amounts of each corresponding asset to deposit.\\n */\\n function depositToStrategy(\\n address _strategyToAddress,\\n address[] calldata _assets,\\n uint256[] calldata _amounts\\n ) external onlyGovernorOrStrategist nonReentrant {\\n _depositToStrategy(_strategyToAddress, _assets, _amounts);\\n }\\n\\n function _depositToStrategy(\\n address _strategyToAddress,\\n address[] calldata _assets,\\n uint256[] calldata _amounts\\n ) internal virtual {\\n require(\\n strategies[_strategyToAddress].isSupported,\\n \\\"Invalid to Strategy\\\"\\n );\\n require(_assets.length == _amounts.length, \\\"Parameter length mismatch\\\");\\n\\n uint256 assetCount = _assets.length;\\n for (uint256 i = 0; i < assetCount; ++i) {\\n address assetAddr = _assets[i];\\n require(\\n IStrategy(_strategyToAddress).supportsAsset(assetAddr),\\n \\\"Asset unsupported\\\"\\n );\\n // Send required amount of funds to the strategy\\n IERC20(assetAddr).safeTransfer(_strategyToAddress, _amounts[i]);\\n }\\n\\n // Deposit all the funds that have been sent to the strategy\\n IStrategy(_strategyToAddress).depositAll();\\n }\\n\\n /**\\n * @notice Withdraw multiple assets from the strategy to the vault.\\n * @param _strategyFromAddress Address of the Strategy to withdraw assets from.\\n * @param _assets Array of asset address that will be withdrawn from the strategy.\\n * @param _amounts Array of amounts of each corresponding asset to withdraw.\\n */\\n function withdrawFromStrategy(\\n address _strategyFromAddress,\\n address[] calldata _assets,\\n uint256[] calldata _amounts\\n ) external onlyGovernorOrStrategist nonReentrant {\\n _withdrawFromStrategy(\\n address(this),\\n _strategyFromAddress,\\n _assets,\\n _amounts\\n );\\n }\\n\\n /**\\n * @param _recipient can either be a strategy or the Vault\\n */\\n function _withdrawFromStrategy(\\n address _recipient,\\n address _strategyFromAddress,\\n address[] calldata _assets,\\n uint256[] calldata _amounts\\n ) internal virtual {\\n require(\\n strategies[_strategyFromAddress].isSupported,\\n \\\"Invalid from Strategy\\\"\\n );\\n require(_assets.length == _amounts.length, \\\"Parameter length mismatch\\\");\\n\\n uint256 assetCount = _assets.length;\\n for (uint256 i = 0; i < assetCount; ++i) {\\n // Withdraw from Strategy to the recipient\\n IStrategy(_strategyFromAddress).withdraw(\\n _recipient,\\n _assets[i],\\n _amounts[i]\\n );\\n }\\n }\\n\\n /**\\n * @notice Sets the maximum allowable difference between\\n * total supply and backing assets' value.\\n */\\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external onlyGovernor {\\n maxSupplyDiff = _maxSupplyDiff;\\n emit MaxSupplyDiffChanged(_maxSupplyDiff);\\n }\\n\\n /**\\n * @notice Sets the trusteeAddress that can receive a portion of yield.\\n * Setting to the zero address disables this feature.\\n */\\n function setTrusteeAddress(address _address) external onlyGovernor {\\n trusteeAddress = _address;\\n emit TrusteeAddressChanged(_address);\\n }\\n\\n /**\\n * @notice Sets the TrusteeFeeBps to the percentage of yield that should be\\n * received in basis points.\\n */\\n function setTrusteeFeeBps(uint256 _basis) external onlyGovernor {\\n require(_basis <= 5000, \\\"basis cannot exceed 50%\\\");\\n trusteeFeeBps = _basis;\\n emit TrusteeFeeBpsChanged(_basis);\\n }\\n\\n /**\\n * @notice Set OToken Metapool strategy\\n * @param _ousdMetaStrategy Address of OToken metapool strategy\\n */\\n function setOusdMetaStrategy(address _ousdMetaStrategy)\\n external\\n onlyGovernor\\n {\\n ousdMetaStrategy = _ousdMetaStrategy;\\n emit OusdMetaStrategyUpdated(_ousdMetaStrategy);\\n }\\n\\n /***************************************\\n Pause\\n ****************************************/\\n\\n /**\\n * @notice Set the deposit paused flag to true to prevent rebasing.\\n */\\n function pauseRebase() external onlyGovernorOrStrategist {\\n rebasePaused = true;\\n emit RebasePaused();\\n }\\n\\n /**\\n * @notice Set the deposit paused flag to true to allow rebasing.\\n */\\n function unpauseRebase() external onlyGovernorOrStrategist {\\n rebasePaused = false;\\n emit RebaseUnpaused();\\n }\\n\\n /**\\n * @notice Set the deposit paused flag to true to prevent capital movement.\\n */\\n function pauseCapital() external onlyGovernorOrStrategist {\\n capitalPaused = true;\\n emit CapitalPaused();\\n }\\n\\n /**\\n * @notice Set the deposit paused flag to false to enable capital movement.\\n */\\n function unpauseCapital() external onlyGovernorOrStrategist {\\n capitalPaused = false;\\n emit CapitalUnpaused();\\n }\\n\\n /***************************************\\n Utils\\n ****************************************/\\n\\n /**\\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\\n * contract, i.e. mistaken sends.\\n * @param _asset Address for the asset\\n * @param _amount Amount of the asset to transfer\\n */\\n function transferToken(address _asset, uint256 _amount)\\n external\\n onlyGovernor\\n {\\n require(!assets[_asset].isSupported, \\\"Only unsupported assets\\\");\\n IERC20(_asset).safeTransfer(governor(), _amount);\\n }\\n\\n /***************************************\\n Strategies Admin\\n ****************************************/\\n\\n /**\\n * @notice Withdraws all assets from the strategy and sends assets to the Vault.\\n * @param _strategyAddr Strategy address.\\n */\\n function withdrawAllFromStrategy(address _strategyAddr)\\n external\\n onlyGovernorOrStrategist\\n {\\n _withdrawAllFromStrategy(_strategyAddr);\\n }\\n\\n function _withdrawAllFromStrategy(address _strategyAddr) internal virtual {\\n require(\\n strategies[_strategyAddr].isSupported,\\n \\\"Strategy is not supported\\\"\\n );\\n IStrategy strategy = IStrategy(_strategyAddr);\\n strategy.withdrawAll();\\n }\\n\\n /**\\n * @notice Withdraws all assets from all the strategies and sends assets to the Vault.\\n */\\n function withdrawAllFromStrategies() external onlyGovernorOrStrategist {\\n _withdrawAllFromStrategies();\\n }\\n\\n function _withdrawAllFromStrategies() internal virtual {\\n uint256 stratCount = allStrategies.length;\\n for (uint256 i = 0; i < stratCount; ++i) {\\n IStrategy(allStrategies[i]).withdrawAll();\\n }\\n }\\n\\n /***************************************\\n Utils\\n ****************************************/\\n\\n function _cacheDecimals(address token) internal {\\n Asset storage tokenAsset = assets[token];\\n if (tokenAsset.decimals != 0) {\\n return;\\n }\\n uint8 decimals = IBasicToken(token).decimals();\\n require(decimals >= 6 && decimals <= 18, \\\"Unexpected precision\\\");\\n tokenAsset.decimals = decimals;\\n }\\n}\\n\",\"keccak256\":\"0xfbb2e3123a069feb16f5efa247e04d678289e94f1d32dbe7f198724e97080e85\",\"license\":\"BUSL-1.1\"},\"contracts/vault/VaultStorage.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title OToken VaultStorage contract\\n * @notice The VaultStorage contract defines the storage for the Vault contracts\\n * @author Origin Protocol Inc\\n */\\n\\nimport { IERC20 } from \\\"@openzeppelin/contracts/token/ERC20/IERC20.sol\\\";\\nimport { SafeERC20 } from \\\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\\\";\\nimport { Address } from \\\"@openzeppelin/contracts/utils/Address.sol\\\";\\n\\nimport { IStrategy } from \\\"../interfaces/IStrategy.sol\\\";\\nimport { Governable } from \\\"../governance/Governable.sol\\\";\\nimport { OUSD } from \\\"../token/OUSD.sol\\\";\\nimport { Initializable } from \\\"../utils/Initializable.sol\\\";\\nimport \\\"../utils/Helpers.sol\\\";\\n\\ncontract VaultStorage is Initializable, Governable {\\n using SafeERC20 for IERC20;\\n\\n event AssetSupported(address _asset);\\n event AssetRemoved(address _asset);\\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\\n event StrategyApproved(address _addr);\\n event StrategyRemoved(address _addr);\\n event Mint(address _addr, uint256 _value);\\n event Redeem(address _addr, uint256 _value);\\n event CapitalPaused();\\n event CapitalUnpaused();\\n event RebasePaused();\\n event RebaseUnpaused();\\n event VaultBufferUpdated(uint256 _vaultBuffer);\\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\\n event PriceProviderUpdated(address _priceProvider);\\n event AllocateThresholdUpdated(uint256 _threshold);\\n event RebaseThresholdUpdated(uint256 _threshold);\\n event StrategistUpdated(address _address);\\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\\n event TrusteeFeeBpsChanged(uint256 _basis);\\n event TrusteeAddressChanged(address _address);\\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\\n event SwapperChanged(address _address);\\n event SwapAllowedUndervalueChanged(uint256 _basis);\\n event SwapSlippageChanged(address _asset, uint256 _basis);\\n event Swapped(\\n address indexed _fromAsset,\\n address indexed _toAsset,\\n uint256 _fromAssetAmount,\\n uint256 _toAssetAmount\\n );\\n event StrategyAddedToMintWhitelist(address indexed strategy);\\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\\n event DripDurationChanged(uint256 dripDuration);\\n event WithdrawalRequested(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount,\\n uint256 _queued\\n );\\n event WithdrawalClaimed(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount\\n );\\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\\n\\n // Since we are proxy, all state should be uninitalized.\\n // Since this storage contract does not have logic directly on it\\n // we should not be checking for to see if these variables can be constant.\\n // slither-disable-start uninitialized-state\\n // slither-disable-start constable-states\\n\\n // Assets supported by the Vault, i.e. Stablecoins\\n enum UnitConversion {\\n DECIMALS,\\n GETEXCHANGERATE\\n }\\n // Changed to fit into a single storage slot so the decimals needs to be recached\\n struct Asset {\\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\\n // redeeming or checking balance of assets.\\n bool isSupported;\\n UnitConversion unitConversion;\\n uint8 decimals;\\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\\n // For example 40 == 0.4% slippage\\n uint16 allowedOracleSlippageBps;\\n }\\n\\n /// @dev mapping of supported vault assets to their configuration\\n mapping(address => Asset) internal assets;\\n /// @dev list of all assets supported by the vault.\\n address[] internal allAssets;\\n\\n // Strategies approved for use by the Vault\\n struct Strategy {\\n bool isSupported;\\n uint256 _deprecated; // Deprecated storage slot\\n }\\n /// @dev mapping of strategy contracts to their configuration\\n mapping(address => Strategy) public strategies;\\n /// @dev list of all vault strategies\\n address[] internal allStrategies;\\n\\n /// @notice Address of the Oracle price provider contract\\n address public priceProvider;\\n /// @notice pause rebasing if true\\n bool public rebasePaused;\\n /// @notice pause operations that change the OToken supply.\\n /// eg mint, redeem, allocate, mint/burn for strategy\\n bool public capitalPaused;\\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\\n uint256 public redeemFeeBps;\\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\\n uint256 public vaultBuffer;\\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\\n uint256 public autoAllocateThreshold;\\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\\n uint256 public rebaseThreshold;\\n\\n /// @dev Address of the OToken token. eg OUSD or OETH.\\n OUSD public oUSD;\\n\\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\\n // keccak256(\\\"OUSD.vault.governor.admin.impl\\\");\\n bytes32 public constant adminImplPosition =\\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\\n\\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\\n address private _deprecated_rebaseHooksAddr = address(0);\\n\\n /// @dev Deprecated: Address of Uniswap\\n address private _deprecated_uniswapAddr = address(0);\\n\\n /// @notice Address of the Strategist\\n address public strategistAddr = address(0);\\n\\n /// @notice Mapping of asset address to the Strategy that they should automatically\\n // be allocated to\\n mapping(address => address) public assetDefaultStrategies;\\n\\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\\n uint256 public maxSupplyDiff;\\n\\n /// @notice Trustee contract that can collect a percentage of yield\\n address public trusteeAddress;\\n\\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\\n uint256 public trusteeFeeBps;\\n\\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\\n address[] private _deprecated_swapTokens;\\n\\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\\n\\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\\n\\n address public ousdMetaStrategy;\\n\\n /// @notice How much OTokens are currently minted by the strategy\\n int256 public netOusdMintedForStrategy;\\n\\n /// @notice How much net total OTokens are allowed to be minted by all strategies\\n uint256 public netOusdMintForStrategyThreshold;\\n\\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\\n\\n /// @notice Collateral swap configuration.\\n /// @dev is packed into a single storage slot to save gas.\\n struct SwapConfig {\\n // Contract that swaps the vault's collateral assets\\n address swapper;\\n // Max allowed percentage the total value can drop below the total supply in basis points.\\n // For example 100 == 1%\\n uint16 allowedUndervalueBps;\\n }\\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\\n\\n // List of strategies that can mint oTokens directly\\n // Used in OETHBaseVaultCore\\n mapping(address => bool) public isMintWhitelistedStrategy;\\n\\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\\n address private _deprecated_dripper;\\n\\n /// Withdrawal Queue Storage /////\\n\\n struct WithdrawalQueueMetadata {\\n // cumulative total of all withdrawal requests included the ones that have already been claimed\\n uint128 queued;\\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\\n uint128 claimable;\\n // total of all the requests that have been claimed\\n uint128 claimed;\\n // index of the next withdrawal request starting at 0\\n uint128 nextWithdrawalIndex;\\n }\\n\\n /// @notice Global metadata for the withdrawal queue including:\\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\\n /// claimed - total of all the requests that have been claimed\\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\\n\\n struct WithdrawalRequest {\\n address withdrawer;\\n bool claimed;\\n uint40 timestamp; // timestamp of the withdrawal request\\n // Amount of oTokens to redeem. eg OETH\\n uint128 amount;\\n // cumulative total of all withdrawal requests including this one.\\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\\n uint128 queued;\\n }\\n\\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\\n\\n /// @notice Sets a minimum delay that is required to elapse between\\n /// requesting async withdrawals and claiming the request.\\n /// When set to 0 async withdrawals are disabled.\\n uint256 public withdrawalClaimDelay;\\n\\n /// @notice Time in seconds that the vault last rebased yield.\\n uint64 public lastRebase;\\n\\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\\n uint64 public dripDuration;\\n\\n /// @notice max rebase percentage per second\\n /// Can be used to set maximum yield of the protocol,\\n /// spreading out yield over time\\n uint64 public rebasePerSecondMax;\\n\\n /// @notice target rebase rate limit, based on past rates and funds available.\\n uint64 public rebasePerSecondTarget;\\n\\n uint256 internal constant MAX_REBASE = 0.02 ether;\\n uint256 internal constant MAX_REBASE_PER_SECOND =\\n uint256(0.05 ether) / 1 days;\\n\\n // For future use\\n uint256[43] private __gap;\\n\\n // slither-disable-end constable-states\\n // slither-disable-end uninitialized-state\\n\\n /**\\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\\n * @param newImpl address of the implementation\\n */\\n function setAdminImpl(address newImpl) external onlyGovernor {\\n require(\\n Address.isContract(newImpl),\\n \\\"new implementation is not a contract\\\"\\n );\\n bytes32 position = adminImplPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newImpl)\\n }\\n }\\n}\\n\",\"keccak256\":\"0xe8c1056879e4d67e0085a30a525a4cb23b954ade0f22fce502278f35b9c69d3b\",\"license\":\"BUSL-1.1\"}},\"version\":1}", + "bytecode": "0x603d80546001600160a01b0319908116909155603e805482169055603f8054909116905560e0604052600060a081905260c052604880546001600160b01b031916905534801561004e57600080fd5b50604051613def380380613def83398101604081905261006d9161007e565b6001600160a01b03166080526100ae565b60006020828403121561009057600080fd5b81516001600160a01b03811681146100a757600080fd5b9392505050565b608051613d116100de600039600081816105f601528181612d6b01528181612e9b01526132880152613d116000f3fe608060405234801561001057600080fd5b50600436106104075760003560e01c80635d36b19011610220578063ae69f3cb11610130578063c9919112116100b8578063e6cc543211610087578063e6cc5432146109cf578063e829cc16146109e3578063eb03654b146109f6578063ef08edc214610a09578063fc0cfeee14610a3057600080fd5b8063c991911214610998578063d38bfff4146109a0578063d58e3b3a146109b3578063e45cc9f0146109c657600080fd5b8063b890ebf6116100ff578063b890ebf614610948578063bb7a632e1461095b578063bc90106b14610975578063c5f0084114610988578063c7af33521461099057600080fd5b8063ae69f3cb146108fc578063b2c9336d1461090f578063b4925a2014610922578063b888879e1461093557600080fd5b8063840c4c7a116101b357806394828ffd1161018257806394828ffd1461089c57806395b166bc146108a45780639c82f2a4146108b75780639fa1826e146108ca578063a403e4d5146108d357600080fd5b8063840c4c7a146107c55780638e510b52146107d85780638ec489a2146107e1578063937b2581146107f457600080fd5b8063773540b3116101ef578063773540b31461078357806378f353a1146107965780637a2202f3146107a95780637b9a7096146107b257600080fd5b80635d36b19014610742578063636e6c401461074a578063663e64ce1461075d5780636c7561e81461077057600080fd5b806339ebf8231161031b5780634bed3bc0116102ae57806352d38e5d1161027d57806352d38e5d146106ec57806353ca9f24146106f5578063570d8e1d146107095780635802a1721461071c578063597c89101461072f57600080fd5b80634bed3bc01461067a5780634d5f46291461068d57806350ba711c146106bf578063527e83a8146106d257600080fd5b80634530820a116102ea5780634530820a1461061857806345e4213b1461064b57806349c1d54d146106545780634a5e42b11461066757600080fd5b806339ebf823146105925780633b8ae397146105d65780633dbc911f146105e95780633fc8cef3146105f157600080fd5b806318ce56bd1161039e5780632b3297f91161036d5780632b3297f9146104e95780632da845a8146104fa578063362bd1a31461050d57806336b6d9441461056c578063372aa2241461057f57600080fd5b806318ce56bd146104b15780631cfbe7bc146104c45780631edfe3da146104d7578063207134b0146104e057600080fd5b80630c340a24116103da5780630c340a24146104585780631072cbea14610478578063175188e81461048b5780631816dd4a1461049e57600080fd5b80630493a0fa1461040c57806309f49bf51461042157806309f6442c146104295780630acbda7514610445575b600080fd5b61041f61041a3660046136a4565b610a43565b005b61041f610b48565b61043260385481565b6040519081526020015b60405180910390f35b61041f6104533660046136a4565b610bb8565b610460610c63565b6040516001600160a01b03909116815260200161043c565b61041f6104863660046136d9565b610c80565b61041f610499366004613703565b610d2d565b61041f6104ac366004613703565b610ff4565b604554610460906001600160a01b031681565b61041f6104d23660046136a4565b6110fb565b61043260395481565b61043260435481565b6048546001600160a01b0316610460565b61041f610508366004613703565b6111bd565b604b54604c54610539916001600160801b0380821692600160801b928390048216928183169291041684565b604080516001600160801b039586168152938516602085015291841691830191909152909116606082015260800161043c565b61041f61057a366004613703565b61122f565b61041f61058d366004613703565b61125f565b6105bf6105a0366004613703565b6035602052600090815260409020805460019091015460ff9091169082565b60408051921515835260208301919091520161043c565b61041f6105e4366004613703565b6112d1565b61041f61140e565b6104607f000000000000000000000000000000000000000000000000000000000000000081565b61063b610626366004613703565b60496020526000908152604090205460ff1681565b604051901515815260200161043c565b610432604e5481565b604254610460906001600160a01b031681565b61041f610675366004613703565b611484565b604854600160a01b900461ffff16610432565b604f546106a790600160c01b90046001600160401b031681565b6040516001600160401b03909116815260200161043c565b6104326106cd36600461371e565b6117ac565b604f546106a790600160801b90046001600160401b031681565b610432603b5481565b60375461063b90600160a01b900460ff1681565b603f54610460906001600160a01b031681565b603c54610460906001600160a01b031681565b61041f61073d366004613703565b61184f565b61041f611890565b61041f6107583660046136a4565b611936565b61041f61076b3660046136a4565b611994565b61041f61077e3660046137d4565b6119ed565b61041f610791366004613703565b611c5c565b604f546106a7906001600160401b031681565b61043260475481565b61041f6107c036600461381d565b611cce565b61041f6107d336600461389b565b611e04565b61043260415481565b61041f6107ef3660046136a4565b611e9d565b6108556108023660046136a4565b604d60205260009081526040902080546001909101546001600160a01b03821691600160a01b810460ff1691600160a81b90910464ffffffffff16906001600160801b0380821691600160801b90041685565b604080516001600160a01b039096168652931515602086015264ffffffffff909216928401929092526001600160801b03918216606084015216608082015260a00161043c565b61041f611f52565b61041f6108b2366004613703565b611fc2565b61041f6108c5366004613703565b612089565b610432603a5481565b6104606108e1366004613703565b6040602081905260009182529020546001600160a01b031681565b61041f61090a36600461389b565b6120fb565b61041f61091d3660046136a4565b612189565b61041f6109303660046136a4565b6121e2565b603754610460906001600160a01b031681565b61041f6109563660046136a4565b612345565b604f546106a790600160401b90046001600160401b031681565b61041f61098336600461391f565b61239e565b61041f6125a9565b61063b61261f565b61041f612650565b61041f6109ae366004613703565b612690565b61041f6109c1366004613703565b612734565b61043260465481565b60375461063b90600160a81b900460ff1681565b61041f6109f1366004613949565b6127a6565b61041f610a043660046136a4565b612866565b6104327fa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd981565b61041f610a3e366004613703565b61291b565b603f546001600160a01b0316331480610a5f5750610a5f61261f565b610a845760405162461bcd60e51b8152600401610a7b90613964565b60405180910390fd5b306001600160a01b031663af14052c6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610abf57600080fd5b505af1158015610ad3573d6000803e3d6000fd5b50505050610ae0816129bd565b604f80546001600160401b0392909216600160401b026fffffffffffffffff0000000000000000199092169190911790556040518181527f406e15fbca1d8ded2dbb06765fea3a54f18395c54125a4c9916dd00ea14ee15e906020015b60405180910390a150565b603f546001600160a01b0316331480610b645750610b6461261f565b610b805760405162461bcd60e51b8152600401610a7b90613964565b6037805460ff60a01b191690556040517fbc044409505c95b6b851433df96e1beae715c909d8e7c1d6d7ab783300d4e3b990600090a1565b610bc061261f565b610bdc5760405162461bcd60e51b8152600401610a7b906139ac565b611388811115610c2e5760405162461bcd60e51b815260206004820152601760248201527f62617369732063616e6e6f7420657863656564203530250000000000000000006044820152606401610a7b565b60438190556040518181527f56287a45051933ea374811b3d5d165033047be5572cac676f7c28b8be4f746c790602001610b3d565b6000610c7b600080516020613cbc8339815191525490565b905090565b610c8861261f565b610ca45760405162461bcd60e51b8152600401610a7b906139ac565b6001600160a01b03821660009081526033602052604090205460ff1615610d0d5760405162461bcd60e51b815260206004820152601760248201527f4f6e6c7920756e737570706f72746564206173736574730000000000000000006044820152606401610a7b565b610d29610d18610c63565b6001600160a01b0384169083612a29565b5050565b610d3561261f565b610d515760405162461bcd60e51b8152600401610a7b906139ac565b6001600160a01b03811660009081526035602052604090205460ff16610d895760405162461bcd60e51b8152600401610a7b906139e3565b60345460005b81811015610e3857826001600160a01b03166040600060348481548110610db857610db8613a12565b60009182526020808320909101546001600160a01b0390811684529083019390935260409091019020541603610e305760405162461bcd60e51b815260206004820181905260248201527f53747261746567792069732064656661756c7420666f7220616e2061737365746044820152606401610a7b565b600101610d8f565b506036548060005b82811015610e8f57846001600160a01b031660368281548110610e6557610e65613a12565b6000918252602090912001546001600160a01b031603610e8757809150610e8f565b600101610e40565b5081811015610fee576036610ea5600184613a3e565b81548110610eb557610eb5613a12565b600091825260209091200154603680546001600160a01b039092169183908110610ee157610ee1613a12565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506036805480610f2057610f20613a51565b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b03861680835260359091526040808320805460ff19169055805163429c145b60e11b81529051879363853828b6926004808201939182900301818387803b158015610f9757600080fd5b505af1158015610fab573d6000803e3d6000fd5b50506040516001600160a01b03881681527f09a1db4b80c32706328728508c941a6b954f31eb5affd32f236c1fd405f8fea49250602001905060405180910390a1505b50505050565b610ffc61261f565b6110185760405162461bcd60e51b8152600401610a7b906139ac565b6001600160a01b03811660009081526035602052604090205460ff166110505760405162461bcd60e51b8152600401610a7b906139e3565b6001600160a01b03811660009081526049602052604090205460ff16156110af5760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481dda1a5d195b1a5cdd1959606a1b6044820152606401610a7b565b6001600160a01b038116600081815260496020526040808220805460ff19166001179055517f47c8c96a5942f094264111c5fe7f6a4fe86efe63254a6fa7afa5fc84f07d58e89190a250565b61110361261f565b61111f5760405162461bcd60e51b8152600401610a7b906139ac565b80158061113c5750610258811015801561113c57506213c6808111155b6111885760405162461bcd60e51b815260206004820152601a60248201527f496e76616c696420636c61696d2064656c617920706572696f640000000000006044820152606401610a7b565b604e8190556040518181527fc59f5e32049abab44ddea11021f5abb89422a2f550837afcf25df9fc8d0db6b090602001610b3d565b6111c561261f565b6111e15760405162461bcd60e51b8152600401610a7b906139ac565b604280546001600160a01b0319166001600160a01b0383169081179091556040519081527f1e4af5ac389e8cde1bdaa6830881b6c987c62a45cfb3b33d27d805cde3b5775090602001610b3d565b61123761261f565b6112535760405162461bcd60e51b8152600401610a7b906139ac565b61125c81612a80565b50565b61126761261f565b6112835760405162461bcd60e51b8152600401610a7b906139ac565b603780546001600160a01b0319166001600160a01b0383169081179091556040519081527fb266add5f3044b17d27db796af992cecbe413921b4e8aaaee03c719e16b9806a90602001610b3d565b6112d961261f565b6112f55760405162461bcd60e51b8152600401610a7b906139ac565b6001600160a01b03811660009081526035602052604090205460ff161561135e5760405162461bcd60e51b815260206004820152601960248201527f537472617465677920616c726561647920617070726f766564000000000000006044820152606401610a7b565b6040805180820182526001808252600060208084018281526001600160a01b038716808452603583528684209551865460ff19169015151786559051948401949094556036805493840181559091527f4a11f94e20a93c79f6ec743a1954ec4fc2c08429ae2122118bf234b2185c81b890910180546001600160a01b0319168317905591519081527f960dd94cbb79169f09a4e445d58b895df2d9bffa5b31055d0932d801724a20d19101610b3d565b603f546001600160a01b031633148061142a575061142a61261f565b6114465760405162461bcd60e51b8152600401610a7b90613964565b6037805460ff60a81b1916600160a81b1790556040517f71f0e5b62f846a22e0b4d159e516e62fa9c2b8eb570be15f83e67d98a2ee51e090600090a1565b61148c61261f565b6114a85760405162461bcd60e51b8152600401610a7b906139ac565b6001600160a01b03811660009081526033602052604090205460ff166115065760405162461bcd60e51b8152602060048201526013602482015272105cdcd95d081b9bdd081cdd5c1c1bdc9d1959606a1b6044820152606401610a7b565b6001600160a01b03811660009081526033602052604081205461153b906509184e72a0009062010000900460ff166012612b87565b604051632fa8a91360e11b81526001600160a01b038416600482015290915081903090635f51522690602401602060405180830381865afa158015611584573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115a89190613a67565b11156115f65760405162461bcd60e51b815260206004820152601760248201527f5661756c74207374696c6c20686f6c64732061737365740000000000000000006044820152606401610a7b565b6034548060005b8281101561164c57846001600160a01b03166034828154811061162257611622613a12565b6000918252602090912001546001600160a01b0316036116445780915061164c565b6001016115fd565b50603461165a600184613a3e565b8154811061166a5761166a613a12565b600091825260209091200154603480546001600160a01b03909216918390811061169657611696613a12565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060348054806116d5576116d5613a51565b60008281526020808220600019908401810180546001600160a01b031990811690915593019093556001600160a01b038716808252604080855280832080549094169093558251908152928301527fba58ce12801c949fa65f41c46ed108671c219baf945fa48d21026cea99ff252a910160405180910390a16001600160a01b038416600081815260336020908152604091829020805464ffffffffff1916905590519182527f37803e2125c48ee96c38ddf04e826daf335b0e1603579040fd275aba6d06b6fc910160405180910390a150505050565b7f53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac453580546000919060011981016117f45760405162461bcd60e51b8152600401610a7b90613a80565b60028255603f546001600160a01b0316331480611814575061181461261f565b6118305760405162461bcd60e51b8152600401610a7b90613964565b61183e898989898989612beb565b600190925550979650505050505050565b603f546001600160a01b031633148061186b575061186b61261f565b6118875760405162461bcd60e51b8152600401610a7b90613964565b61125c81612c36565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b03161461192b5760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b6064820152608401610a7b565b61193433612c95565b565b61193e61261f565b61195a5760405162461bcd60e51b8152600401610a7b906139ac565b600060465560478190556040518181527fc29d6fedbc6bdf267a08166c2b976fbd72aca5d6a769528616f8b9224c8f197f90602001610b3d565b61199c61261f565b6119b85760405162461bcd60e51b8152600401610a7b906139ac565b60418190556040518181527f95201f9c21f26877223b1ff4073936a6484c35495649e60e55730497aeb60d9390602001610b3d565b6119f561261f565b611a115760405162461bcd60e51b8152600401610a7b906139ac565b6001600160a01b03821660009081526033602052604090205460ff1615611a7a5760405162461bcd60e51b815260206004820152601760248201527f417373657420616c726561647920737570706f727465640000000000000000006044820152606401610a7b565b60405180608001604052806001151581526020018260ff166001811115611aa357611aa3613aa8565b6001811115611ab457611ab4613aa8565b81526000602080830182905260409283018290526001600160a01b0386168252603381529190208251815490151560ff19821681178355928401519192839161ff001990911661ffff1990911617610100836001811115611b1757611b17613aa8565b02179055506040820151815460609093015161ffff1663010000000264ffff0000001960ff90921662010000029190911664ffffff00001990931692909217919091179055611b6582612a80565b603480546001810182556000919091527f46bddb1178e94d7f2892ff5f366840eb658911794f2c3a44c450aa2c505186c10180546001600160a01b0319166001600160a01b038481169182179092556037546040516315d5220f60e31b815260048101929092529091169063aea9107890602401602060405180830381865afa158015611bf6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c1a9190613a67565b506040516001600160a01b03831681527f4f1ac48525e50059cc1cc6e0e1940ece0dd653a4db4841538d6aef036be2fb7b906020015b60405180910390a15050565b611c6461261f565b611c805760405162461bcd60e51b8152600401610a7b906139ac565b603f80546001600160a01b0319166001600160a01b0383169081179091556040519081527f869e0abd13cc3a975de7b93be3df1cb2255c802b1cead85963cc79d99f131bee90602001610b3d565b611cd661261f565b611cf25760405162461bcd60e51b8152600401610a7b906139ac565b6001600160a01b03821660009081526033602052604090205460ff16611d505760405162461bcd60e51b8152602060048201526013602482015272105cdcd95d081b9bdd081cdd5c1c1bdc9d1959606a1b6044820152606401610a7b565b6103e88161ffff1610611d995760405162461bcd60e51b81526020600482015260116024820152700a6d8d2e0e0c2ceca40e8dede40d0d2ced607b1b6044820152606401610a7b565b6001600160a01b038216600081815260336020908152604091829020805464ffff0000001916630100000061ffff8716908102919091179091558251938452908301527f8d22e9d2cbe8bb65a3c4412bd8970743864512a1a0e004e8d00fb96277b78b949101611c50565b603f546001600160a01b0316331480611e205750611e2061261f565b611e3c5760405162461bcd60e51b8152600401610a7b90613964565b7f53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac453580546001198101611e805760405162461bcd60e51b8152600401610a7b90613a80565b60028255611e918787878787612cf4565b50600190555050505050565b603f546001600160a01b0316331480611eb95750611eb961261f565b611ed55760405162461bcd60e51b8152600401610a7b90613964565b670de0b6b3a7640000811115611f1d5760405162461bcd60e51b815260206004820152600d60248201526c496e76616c69642076616c756560981b6044820152606401610a7b565b60398190556040518181527f41ecb23a0e7865b25f38c268b7c3012220d822929e9edff07326e89d5bb822b590602001610b3d565b603f546001600160a01b0316331480611f6e5750611f6e61261f565b611f8a5760405162461bcd60e51b8152600401610a7b90613964565b6037805460ff60a81b191690556040517f891ebab18da80ebeeea06b1b1cede098329c4c008906a98370c2ac7a80b571cb90600090a1565b611fca61261f565b611fe65760405162461bcd60e51b8152600401610a7b906139ac565b6001600160a01b03811660009081526049602052604090205460ff166120405760405162461bcd60e51b815260206004820152600f60248201526e139bdd081dda1a5d195b1a5cdd1959608a1b6044820152606401610a7b565b6001600160a01b038116600081815260496020526040808220805460ff19169055517f0ec40967a61509853550658e51d0e4297f7cba244fe4adc8ba18b5631ac20e2f9190a250565b61209161261f565b6120ad5760405162461bcd60e51b8152600401610a7b906139ac565b604880546001600160a01b0319166001600160a01b0383169081179091556040519081527f7d7719313229e558c5a3893cad2eb86a86a049156d1d9ebd5c63a8eedefd1c0390602001610b3d565b603f546001600160a01b0316331480612117575061211761261f565b6121335760405162461bcd60e51b8152600401610a7b90613964565b7f53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535805460011981016121775760405162461bcd60e51b8152600401610a7b90613a80565b60028255611e91308888888888612f2c565b61219161261f565b6121ad5760405162461bcd60e51b8152600401610a7b906139ac565b603a8190556040518181527f2ec5fb5a3d2703edc461252d92ccd2799c3c74f01d97212b20388207fa17ae4590602001610b3d565b603f546001600160a01b03163314806121fe57506121fe61261f565b61221a5760405162461bcd60e51b8152600401610a7b90613964565b306001600160a01b031663af14052c6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561225557600080fd5b505af1158015612269573d6000803e3d6000fd5b5050505060006301e133806064836122819190613abe565b61228b9190613abe565b90506122a16201518066b1a2bc2ec50000613abe565b8111156122e05760405162461bcd60e51b815260206004820152600d60248201526c0a4c2e8ca40e8dede40d0d2ced609b1b6044820152606401610a7b565b6122e9816129bd565b604f80546001600160401b0392909216600160801b0267ffffffffffffffff60801b199092169190911790556040518181527fef46f143af5fead0010484fe7d6ec2e2972420faa76157f5a6075aa72e614cb590602001611c50565b61234d61261f565b6123695760405162461bcd60e51b8152600401610a7b906139ac565b603b8190556040518181527f39367850377ac04920a9a670f2180e7a94d83b15ad302e59875ec58fd10bd37d90602001610b3d565b603f546001600160a01b03163314806123ba57506123ba61261f565b6123d65760405162461bcd60e51b8152600401610a7b90613964565b604080516001600160a01b038085168252831660208201527fba58ce12801c949fa65f41c46ed108671c219baf945fa48d21026cea99ff252a910160405180910390a16001600160a01b0381161561257b576001600160a01b03811660009081526035602052604090205460ff166124605760405162461bcd60e51b8152600401610a7b906139e3565b6001600160a01b038216600090815260336020526040902054819060ff166124c35760405162461bcd60e51b8152602060048201526016602482015275105cdcd95d081a5cc81b9bdd081cdd5c1c1bdc9d195960521b6044820152606401610a7b565b60405163551c457b60e11b81526001600160a01b03848116600483015282169063aa388af690602401602060405180830381865afa158015612509573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061252d9190613ae0565b6125795760405162461bcd60e51b815260206004820152601f60248201527f4173736574206e6f7420737570706f72746564206279205374726174656779006044820152606401610a7b565b505b6001600160a01b03918216600090815260406020819052902080546001600160a01b03191691909216179055565b603f546001600160a01b03163314806125c557506125c561261f565b6125e15760405162461bcd60e51b8152600401610a7b90613964565b6037805460ff60a01b1916600160a01b1790556040517f8cff26a5985614b3d30629cc4ab83824bf115aec971b718d8f2f99562032e97290600090a1565b6000612637600080516020613cbc8339815191525490565b6001600160a01b0316336001600160a01b031614905090565b603f546001600160a01b031633148061266c575061266c61261f565b6126885760405162461bcd60e51b8152600401610a7b90613964565b611934612f95565b61269861261f565b6126b45760405162461bcd60e51b8152600401610a7b906139ac565b6126dc817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b03166126fc600080516020613cbc8339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b61273c61261f565b6127585760405162461bcd60e51b8152600401610a7b906139ac565b604580546001600160a01b0319166001600160a01b0383169081179091556040519081527fa12850fb726e0b2b7b3c9a9342031e1268a8148d0eb06b4bea8613204ffcd2b890602001610b3d565b6127ae61261f565b6127ca5760405162461bcd60e51b8152600401610a7b906139ac565b6127118161ffff16106128165760405162461bcd60e51b8152602060048201526014602482015273496e76616c696420626173697320706f696e747360601b6044820152606401610a7b565b6048805461ffff60a01b1916600160a01b61ffff8416908102919091179091556040519081527ff12c00256bee2b6facb111a88a9b1cff86e79132939b44f1353212d6f746955790602001610b3d565b61286e61261f565b61288a5760405162461bcd60e51b8152600401610a7b906139ac565b6103e88111156128e65760405162461bcd60e51b815260206004820152602160248201527f52656465656d206665652073686f756c64206e6f74206265206f7665722031306044820152602560f81b6064820152608401610a7b565b60388190556040518181527fd6c7508d6658ccee36b7b7d7fd72e5cbaeefb40c64eff24e9ae7470e846304ee90602001610b3d565b61292361261f565b61293f5760405162461bcd60e51b8152600401610a7b906139ac565b803b6129995760405162461bcd60e51b8152602060048201526024808201527f6e657720696d706c656d656e746174696f6e206973206e6f74206120636f6e746044820152631c9858dd60e21b6064820152608401610a7b565b7fa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd955565b60006001600160401b03821115612a255760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203660448201526534206269747360d01b6064820152608401610a7b565b5090565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052612a7b908490612fec565b505050565b6001600160a01b0381166000908152603360205260409020805462010000900460ff1615612aac575050565b6000826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612aec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b109190613b02565b905060068160ff1610158015612b2a575060128160ff1611155b612b6d5760405162461bcd60e51b81526020600482015260146024820152732ab732bc3832b1ba32b210383932b1b4b9b4b7b760611b6044820152606401610a7b565b815460ff909116620100000262ff00001990911617905550565b600081831115612bb757612bb0612b9e8385613a3e565b612ba990600a613c06565b85906130be565b9350612be1565b81831015612be157612bde612bcc8484613a3e565b612bd790600a613c06565b85906130d3565b93505b50825b9392505050565b60405162461bcd60e51b815260206004820152601d60248201527f436f6c6c61746572616c2073776170206e6f7420737570706f727465640000006044820152600090606401610a7b565b612c3f816130df565b306001600160a01b031663b9b17f9f6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015612c7a57600080fd5b505af1158015612c8e573d6000803e3d6000fd5b5050505050565b6001600160a01b038116612ceb5760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f7220697320616464726573732830290000000000006044820152606401610a7b565b61125c816131a3565b6001600160a01b03851660009081526035602052604090205460ff16612d525760405162461bcd60e51b8152602060048201526013602482015272496e76616c696420746f20537472617465677960681b6044820152606401610a7b565b600183148015612d625750600181145b8015612dc657507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031684846000818110612da657612da6613a12565b9050602002016020810190612dbb9190613703565b6001600160a01b0316145b612e0b5760405162461bcd60e51b815260206004820152601660248201527513db9b1e4815d15512081a5cc81cdd5c1c1bdc9d195960521b6044820152606401610a7b565b612e1361320a565b82826000818110612e2657612e26613a12565b905060200201351115612e7b5760405162461bcd60e51b815260206004820152601960248201527f4e6f7420656e6f756768205745544820617661696c61626c65000000000000006044820152606401610a7b565b612ed28583836000818110612e9257612e92613a12565b905060200201357f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316612a299092919063ffffffff16565b846001600160a01b031663de5f62686040518163ffffffff1660e01b8152600401600060405180830381600087803b158015612f0d57600080fd5b505af1158015612f21573d6000803e3d6000fd5b505050505050505050565b612f3a868686868686613318565b306001600160a01b031663b9b17f9f6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015612f7557600080fd5b505af1158015612f89573d6000803e3d6000fd5b50505050505050505050565b612f9d6134a0565b306001600160a01b031663b9b17f9f6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015612fd857600080fd5b505af1158015610fee573d6000803e3d6000fd5b6000613041826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661352c9092919063ffffffff16565b805190915015612a7b578080602001905181019061305f9190613ae0565b612a7b5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610a7b565b60006130ca8284613c12565b90505b92915050565b60006130ca8284613abe565b6001600160a01b03811660009081526035602052604090205460ff166131475760405162461bcd60e51b815260206004820152601960248201527f5374726174656779206973206e6f7420737570706f72746564000000000000006044820152606401610a7b565b6000819050806001600160a01b031663853828b66040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561318757600080fd5b505af115801561319b573d6000803e3d6000fd5b505050505050565b806001600160a01b03166131c3600080516020613cbc8339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a3600080516020613cbc83398151915255565b60408051608081018252604b546001600160801b03808216808452600160801b9283900482166020850152604c5480831695850186905292909204166060830152600092839161325a9190613c29565b6040516370a0823160e01b81523060048201526001600160801b039190911691506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa1580156132cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132f39190613a67565b9050818111613306576000935050505090565b6133108282613a3e565b935050505090565b6001600160a01b03851660009081526035602052604090205460ff166133785760405162461bcd60e51b8152602060048201526015602482015274496e76616c69642066726f6d20537472617465677960581b6044820152606401610a7b565b8281146133c75760405162461bcd60e51b815260206004820152601960248201527f506172616d65746572206c656e677468206d69736d61746368000000000000006044820152606401610a7b565b8260005b8181101561349657866001600160a01b031663d9caed12898888858181106133f5576133f5613a12565b905060200201602081019061340a9190613703565b87878681811061341c5761341c613a12565b6040516001600160e01b031960e088901b1681526001600160a01b03958616600482015294909316602485015250602090910201356044820152606401600060405180830381600087803b15801561347357600080fd5b505af1158015613487573d6000803e3d6000fd5b505050508060010190506133cb565b5050505050505050565b60365460005b81811015610d2957603681815481106134c1576134c1613a12565b60009182526020822001546040805163429c145b60e11b815290516001600160a01b039092169263853828b69260048084019382900301818387803b15801561350957600080fd5b505af115801561351d573d6000803e3d6000fd5b505050508060010190506134a6565b606061353b8484600085613543565b949350505050565b6060824710156135a45760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610a7b565b843b6135f25760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610a7b565b600080866001600160a01b0316858760405161360e9190613c6c565b60006040518083038185875af1925050503d806000811461364b576040519150601f19603f3d011682016040523d82523d6000602084013e613650565b606091505b509150915061366082828661366b565b979650505050505050565b6060831561367a575081612be4565b82511561368a5782518084602001fd5b8160405162461bcd60e51b8152600401610a7b9190613c88565b6000602082840312156136b657600080fd5b5035919050565b80356001600160a01b03811681146136d457600080fd5b919050565b600080604083850312156136ec57600080fd5b6136f5836136bd565b946020939093013593505050565b60006020828403121561371557600080fd5b6130ca826136bd565b60008060008060008060a0878903121561373757600080fd5b613740876136bd565b955061374e602088016136bd565b9450604087013593506060870135925060808701356001600160401b0381111561377757600080fd5b8701601f8101891361378857600080fd5b80356001600160401b0381111561379e57600080fd5b8960208284010111156137b057600080fd5b60208201935080925050509295509295509295565b60ff8116811461125c57600080fd5b600080604083850312156137e757600080fd5b6137f0836136bd565b91506020830135613800816137c5565b809150509250929050565b803561ffff811681146136d457600080fd5b6000806040838503121561383057600080fd5b613839836136bd565b91506138476020840161380b565b90509250929050565b60008083601f84011261386257600080fd5b5081356001600160401b0381111561387957600080fd5b6020830191508360208260051b850101111561389457600080fd5b9250929050565b6000806000806000606086880312156138b357600080fd5b6138bc866136bd565b945060208601356001600160401b038111156138d757600080fd5b6138e388828901613850565b90955093505060408601356001600160401b0381111561390257600080fd5b61390e88828901613850565b969995985093965092949392505050565b6000806040838503121561393257600080fd5b61393b836136bd565b9150613847602084016136bd565b60006020828403121561395b57600080fd5b6130ca8261380b565b60208082526028908201527f43616c6c6572206973206e6f74207468652053747261746567697374206f722060408201526723b7bb32b93737b960c11b606082015260800190565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b60208082526015908201527414dd1c985d1959de481b9bdd08185c1c1c9bdd9959605a1b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b818103818111156130cd576130cd613a28565b634e487b7160e01b600052603160045260246000fd5b600060208284031215613a7957600080fd5b5051919050565b6020808252600e908201526d1499595b9d1c985b9d0818d85b1b60921b604082015260600190565b634e487b7160e01b600052602160045260246000fd5b600082613adb57634e487b7160e01b600052601260045260246000fd5b500490565b600060208284031215613af257600080fd5b81518015158114612be457600080fd5b600060208284031215613b1457600080fd5b8151612be4816137c5565b6001815b6001841115613b5a57808504811115613b3e57613b3e613a28565b6001841615613b4c57908102905b60019390931c928002613b23565b935093915050565b600082613b71575060016130cd565b81613b7e575060006130cd565b8160018114613b945760028114613b9e57613bba565b60019150506130cd565b60ff841115613baf57613baf613a28565b50506001821b6130cd565b5060208310610133831016604e8410600b8410161715613bdd575081810a6130cd565b613bea6000198484613b1f565b8060001904821115613bfe57613bfe613a28565b029392505050565b60006130ca8383613b62565b80820281158282048414176130cd576130cd613a28565b6001600160801b0382811682821603908111156130cd576130cd613a28565b60005b83811015613c63578181015183820152602001613c4b565b50506000910152565b60008251613c7e818460208701613c48565b9190910192915050565b6020815260008251806020840152613ca7816040850160208701613c48565b601f01601f1916919091016040019291505056fe7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa2646970667358221220f66ef6d77c6c8db269e1abfd6a11ef140019a19b196946d407b669850fff65d564736f6c634300081c0033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106104075760003560e01c80635d36b19011610220578063ae69f3cb11610130578063c9919112116100b8578063e6cc543211610087578063e6cc5432146109cf578063e829cc16146109e3578063eb03654b146109f6578063ef08edc214610a09578063fc0cfeee14610a3057600080fd5b8063c991911214610998578063d38bfff4146109a0578063d58e3b3a146109b3578063e45cc9f0146109c657600080fd5b8063b890ebf6116100ff578063b890ebf614610948578063bb7a632e1461095b578063bc90106b14610975578063c5f0084114610988578063c7af33521461099057600080fd5b8063ae69f3cb146108fc578063b2c9336d1461090f578063b4925a2014610922578063b888879e1461093557600080fd5b8063840c4c7a116101b357806394828ffd1161018257806394828ffd1461089c57806395b166bc146108a45780639c82f2a4146108b75780639fa1826e146108ca578063a403e4d5146108d357600080fd5b8063840c4c7a146107c55780638e510b52146107d85780638ec489a2146107e1578063937b2581146107f457600080fd5b8063773540b3116101ef578063773540b31461078357806378f353a1146107965780637a2202f3146107a95780637b9a7096146107b257600080fd5b80635d36b19014610742578063636e6c401461074a578063663e64ce1461075d5780636c7561e81461077057600080fd5b806339ebf8231161031b5780634bed3bc0116102ae57806352d38e5d1161027d57806352d38e5d146106ec57806353ca9f24146106f5578063570d8e1d146107095780635802a1721461071c578063597c89101461072f57600080fd5b80634bed3bc01461067a5780634d5f46291461068d57806350ba711c146106bf578063527e83a8146106d257600080fd5b80634530820a116102ea5780634530820a1461061857806345e4213b1461064b57806349c1d54d146106545780634a5e42b11461066757600080fd5b806339ebf823146105925780633b8ae397146105d65780633dbc911f146105e95780633fc8cef3146105f157600080fd5b806318ce56bd1161039e5780632b3297f91161036d5780632b3297f9146104e95780632da845a8146104fa578063362bd1a31461050d57806336b6d9441461056c578063372aa2241461057f57600080fd5b806318ce56bd146104b15780631cfbe7bc146104c45780631edfe3da146104d7578063207134b0146104e057600080fd5b80630c340a24116103da5780630c340a24146104585780631072cbea14610478578063175188e81461048b5780631816dd4a1461049e57600080fd5b80630493a0fa1461040c57806309f49bf51461042157806309f6442c146104295780630acbda7514610445575b600080fd5b61041f61041a3660046136a4565b610a43565b005b61041f610b48565b61043260385481565b6040519081526020015b60405180910390f35b61041f6104533660046136a4565b610bb8565b610460610c63565b6040516001600160a01b03909116815260200161043c565b61041f6104863660046136d9565b610c80565b61041f610499366004613703565b610d2d565b61041f6104ac366004613703565b610ff4565b604554610460906001600160a01b031681565b61041f6104d23660046136a4565b6110fb565b61043260395481565b61043260435481565b6048546001600160a01b0316610460565b61041f610508366004613703565b6111bd565b604b54604c54610539916001600160801b0380821692600160801b928390048216928183169291041684565b604080516001600160801b039586168152938516602085015291841691830191909152909116606082015260800161043c565b61041f61057a366004613703565b61122f565b61041f61058d366004613703565b61125f565b6105bf6105a0366004613703565b6035602052600090815260409020805460019091015460ff9091169082565b60408051921515835260208301919091520161043c565b61041f6105e4366004613703565b6112d1565b61041f61140e565b6104607f000000000000000000000000000000000000000000000000000000000000000081565b61063b610626366004613703565b60496020526000908152604090205460ff1681565b604051901515815260200161043c565b610432604e5481565b604254610460906001600160a01b031681565b61041f610675366004613703565b611484565b604854600160a01b900461ffff16610432565b604f546106a790600160c01b90046001600160401b031681565b6040516001600160401b03909116815260200161043c565b6104326106cd36600461371e565b6117ac565b604f546106a790600160801b90046001600160401b031681565b610432603b5481565b60375461063b90600160a01b900460ff1681565b603f54610460906001600160a01b031681565b603c54610460906001600160a01b031681565b61041f61073d366004613703565b61184f565b61041f611890565b61041f6107583660046136a4565b611936565b61041f61076b3660046136a4565b611994565b61041f61077e3660046137d4565b6119ed565b61041f610791366004613703565b611c5c565b604f546106a7906001600160401b031681565b61043260475481565b61041f6107c036600461381d565b611cce565b61041f6107d336600461389b565b611e04565b61043260415481565b61041f6107ef3660046136a4565b611e9d565b6108556108023660046136a4565b604d60205260009081526040902080546001909101546001600160a01b03821691600160a01b810460ff1691600160a81b90910464ffffffffff16906001600160801b0380821691600160801b90041685565b604080516001600160a01b039096168652931515602086015264ffffffffff909216928401929092526001600160801b03918216606084015216608082015260a00161043c565b61041f611f52565b61041f6108b2366004613703565b611fc2565b61041f6108c5366004613703565b612089565b610432603a5481565b6104606108e1366004613703565b6040602081905260009182529020546001600160a01b031681565b61041f61090a36600461389b565b6120fb565b61041f61091d3660046136a4565b612189565b61041f6109303660046136a4565b6121e2565b603754610460906001600160a01b031681565b61041f6109563660046136a4565b612345565b604f546106a790600160401b90046001600160401b031681565b61041f61098336600461391f565b61239e565b61041f6125a9565b61063b61261f565b61041f612650565b61041f6109ae366004613703565b612690565b61041f6109c1366004613703565b612734565b61043260465481565b60375461063b90600160a81b900460ff1681565b61041f6109f1366004613949565b6127a6565b61041f610a043660046136a4565b612866565b6104327fa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd981565b61041f610a3e366004613703565b61291b565b603f546001600160a01b0316331480610a5f5750610a5f61261f565b610a845760405162461bcd60e51b8152600401610a7b90613964565b60405180910390fd5b306001600160a01b031663af14052c6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610abf57600080fd5b505af1158015610ad3573d6000803e3d6000fd5b50505050610ae0816129bd565b604f80546001600160401b0392909216600160401b026fffffffffffffffff0000000000000000199092169190911790556040518181527f406e15fbca1d8ded2dbb06765fea3a54f18395c54125a4c9916dd00ea14ee15e906020015b60405180910390a150565b603f546001600160a01b0316331480610b645750610b6461261f565b610b805760405162461bcd60e51b8152600401610a7b90613964565b6037805460ff60a01b191690556040517fbc044409505c95b6b851433df96e1beae715c909d8e7c1d6d7ab783300d4e3b990600090a1565b610bc061261f565b610bdc5760405162461bcd60e51b8152600401610a7b906139ac565b611388811115610c2e5760405162461bcd60e51b815260206004820152601760248201527f62617369732063616e6e6f7420657863656564203530250000000000000000006044820152606401610a7b565b60438190556040518181527f56287a45051933ea374811b3d5d165033047be5572cac676f7c28b8be4f746c790602001610b3d565b6000610c7b600080516020613cbc8339815191525490565b905090565b610c8861261f565b610ca45760405162461bcd60e51b8152600401610a7b906139ac565b6001600160a01b03821660009081526033602052604090205460ff1615610d0d5760405162461bcd60e51b815260206004820152601760248201527f4f6e6c7920756e737570706f72746564206173736574730000000000000000006044820152606401610a7b565b610d29610d18610c63565b6001600160a01b0384169083612a29565b5050565b610d3561261f565b610d515760405162461bcd60e51b8152600401610a7b906139ac565b6001600160a01b03811660009081526035602052604090205460ff16610d895760405162461bcd60e51b8152600401610a7b906139e3565b60345460005b81811015610e3857826001600160a01b03166040600060348481548110610db857610db8613a12565b60009182526020808320909101546001600160a01b0390811684529083019390935260409091019020541603610e305760405162461bcd60e51b815260206004820181905260248201527f53747261746567792069732064656661756c7420666f7220616e2061737365746044820152606401610a7b565b600101610d8f565b506036548060005b82811015610e8f57846001600160a01b031660368281548110610e6557610e65613a12565b6000918252602090912001546001600160a01b031603610e8757809150610e8f565b600101610e40565b5081811015610fee576036610ea5600184613a3e565b81548110610eb557610eb5613a12565b600091825260209091200154603680546001600160a01b039092169183908110610ee157610ee1613a12565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506036805480610f2057610f20613a51565b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b03861680835260359091526040808320805460ff19169055805163429c145b60e11b81529051879363853828b6926004808201939182900301818387803b158015610f9757600080fd5b505af1158015610fab573d6000803e3d6000fd5b50506040516001600160a01b03881681527f09a1db4b80c32706328728508c941a6b954f31eb5affd32f236c1fd405f8fea49250602001905060405180910390a1505b50505050565b610ffc61261f565b6110185760405162461bcd60e51b8152600401610a7b906139ac565b6001600160a01b03811660009081526035602052604090205460ff166110505760405162461bcd60e51b8152600401610a7b906139e3565b6001600160a01b03811660009081526049602052604090205460ff16156110af5760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481dda1a5d195b1a5cdd1959606a1b6044820152606401610a7b565b6001600160a01b038116600081815260496020526040808220805460ff19166001179055517f47c8c96a5942f094264111c5fe7f6a4fe86efe63254a6fa7afa5fc84f07d58e89190a250565b61110361261f565b61111f5760405162461bcd60e51b8152600401610a7b906139ac565b80158061113c5750610258811015801561113c57506213c6808111155b6111885760405162461bcd60e51b815260206004820152601a60248201527f496e76616c696420636c61696d2064656c617920706572696f640000000000006044820152606401610a7b565b604e8190556040518181527fc59f5e32049abab44ddea11021f5abb89422a2f550837afcf25df9fc8d0db6b090602001610b3d565b6111c561261f565b6111e15760405162461bcd60e51b8152600401610a7b906139ac565b604280546001600160a01b0319166001600160a01b0383169081179091556040519081527f1e4af5ac389e8cde1bdaa6830881b6c987c62a45cfb3b33d27d805cde3b5775090602001610b3d565b61123761261f565b6112535760405162461bcd60e51b8152600401610a7b906139ac565b61125c81612a80565b50565b61126761261f565b6112835760405162461bcd60e51b8152600401610a7b906139ac565b603780546001600160a01b0319166001600160a01b0383169081179091556040519081527fb266add5f3044b17d27db796af992cecbe413921b4e8aaaee03c719e16b9806a90602001610b3d565b6112d961261f565b6112f55760405162461bcd60e51b8152600401610a7b906139ac565b6001600160a01b03811660009081526035602052604090205460ff161561135e5760405162461bcd60e51b815260206004820152601960248201527f537472617465677920616c726561647920617070726f766564000000000000006044820152606401610a7b565b6040805180820182526001808252600060208084018281526001600160a01b038716808452603583528684209551865460ff19169015151786559051948401949094556036805493840181559091527f4a11f94e20a93c79f6ec743a1954ec4fc2c08429ae2122118bf234b2185c81b890910180546001600160a01b0319168317905591519081527f960dd94cbb79169f09a4e445d58b895df2d9bffa5b31055d0932d801724a20d19101610b3d565b603f546001600160a01b031633148061142a575061142a61261f565b6114465760405162461bcd60e51b8152600401610a7b90613964565b6037805460ff60a81b1916600160a81b1790556040517f71f0e5b62f846a22e0b4d159e516e62fa9c2b8eb570be15f83e67d98a2ee51e090600090a1565b61148c61261f565b6114a85760405162461bcd60e51b8152600401610a7b906139ac565b6001600160a01b03811660009081526033602052604090205460ff166115065760405162461bcd60e51b8152602060048201526013602482015272105cdcd95d081b9bdd081cdd5c1c1bdc9d1959606a1b6044820152606401610a7b565b6001600160a01b03811660009081526033602052604081205461153b906509184e72a0009062010000900460ff166012612b87565b604051632fa8a91360e11b81526001600160a01b038416600482015290915081903090635f51522690602401602060405180830381865afa158015611584573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115a89190613a67565b11156115f65760405162461bcd60e51b815260206004820152601760248201527f5661756c74207374696c6c20686f6c64732061737365740000000000000000006044820152606401610a7b565b6034548060005b8281101561164c57846001600160a01b03166034828154811061162257611622613a12565b6000918252602090912001546001600160a01b0316036116445780915061164c565b6001016115fd565b50603461165a600184613a3e565b8154811061166a5761166a613a12565b600091825260209091200154603480546001600160a01b03909216918390811061169657611696613a12565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060348054806116d5576116d5613a51565b60008281526020808220600019908401810180546001600160a01b031990811690915593019093556001600160a01b038716808252604080855280832080549094169093558251908152928301527fba58ce12801c949fa65f41c46ed108671c219baf945fa48d21026cea99ff252a910160405180910390a16001600160a01b038416600081815260336020908152604091829020805464ffffffffff1916905590519182527f37803e2125c48ee96c38ddf04e826daf335b0e1603579040fd275aba6d06b6fc910160405180910390a150505050565b7f53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac453580546000919060011981016117f45760405162461bcd60e51b8152600401610a7b90613a80565b60028255603f546001600160a01b0316331480611814575061181461261f565b6118305760405162461bcd60e51b8152600401610a7b90613964565b61183e898989898989612beb565b600190925550979650505050505050565b603f546001600160a01b031633148061186b575061186b61261f565b6118875760405162461bcd60e51b8152600401610a7b90613964565b61125c81612c36565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b03161461192b5760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b6064820152608401610a7b565b61193433612c95565b565b61193e61261f565b61195a5760405162461bcd60e51b8152600401610a7b906139ac565b600060465560478190556040518181527fc29d6fedbc6bdf267a08166c2b976fbd72aca5d6a769528616f8b9224c8f197f90602001610b3d565b61199c61261f565b6119b85760405162461bcd60e51b8152600401610a7b906139ac565b60418190556040518181527f95201f9c21f26877223b1ff4073936a6484c35495649e60e55730497aeb60d9390602001610b3d565b6119f561261f565b611a115760405162461bcd60e51b8152600401610a7b906139ac565b6001600160a01b03821660009081526033602052604090205460ff1615611a7a5760405162461bcd60e51b815260206004820152601760248201527f417373657420616c726561647920737570706f727465640000000000000000006044820152606401610a7b565b60405180608001604052806001151581526020018260ff166001811115611aa357611aa3613aa8565b6001811115611ab457611ab4613aa8565b81526000602080830182905260409283018290526001600160a01b0386168252603381529190208251815490151560ff19821681178355928401519192839161ff001990911661ffff1990911617610100836001811115611b1757611b17613aa8565b02179055506040820151815460609093015161ffff1663010000000264ffff0000001960ff90921662010000029190911664ffffff00001990931692909217919091179055611b6582612a80565b603480546001810182556000919091527f46bddb1178e94d7f2892ff5f366840eb658911794f2c3a44c450aa2c505186c10180546001600160a01b0319166001600160a01b038481169182179092556037546040516315d5220f60e31b815260048101929092529091169063aea9107890602401602060405180830381865afa158015611bf6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c1a9190613a67565b506040516001600160a01b03831681527f4f1ac48525e50059cc1cc6e0e1940ece0dd653a4db4841538d6aef036be2fb7b906020015b60405180910390a15050565b611c6461261f565b611c805760405162461bcd60e51b8152600401610a7b906139ac565b603f80546001600160a01b0319166001600160a01b0383169081179091556040519081527f869e0abd13cc3a975de7b93be3df1cb2255c802b1cead85963cc79d99f131bee90602001610b3d565b611cd661261f565b611cf25760405162461bcd60e51b8152600401610a7b906139ac565b6001600160a01b03821660009081526033602052604090205460ff16611d505760405162461bcd60e51b8152602060048201526013602482015272105cdcd95d081b9bdd081cdd5c1c1bdc9d1959606a1b6044820152606401610a7b565b6103e88161ffff1610611d995760405162461bcd60e51b81526020600482015260116024820152700a6d8d2e0e0c2ceca40e8dede40d0d2ced607b1b6044820152606401610a7b565b6001600160a01b038216600081815260336020908152604091829020805464ffff0000001916630100000061ffff8716908102919091179091558251938452908301527f8d22e9d2cbe8bb65a3c4412bd8970743864512a1a0e004e8d00fb96277b78b949101611c50565b603f546001600160a01b0316331480611e205750611e2061261f565b611e3c5760405162461bcd60e51b8152600401610a7b90613964565b7f53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac453580546001198101611e805760405162461bcd60e51b8152600401610a7b90613a80565b60028255611e918787878787612cf4565b50600190555050505050565b603f546001600160a01b0316331480611eb95750611eb961261f565b611ed55760405162461bcd60e51b8152600401610a7b90613964565b670de0b6b3a7640000811115611f1d5760405162461bcd60e51b815260206004820152600d60248201526c496e76616c69642076616c756560981b6044820152606401610a7b565b60398190556040518181527f41ecb23a0e7865b25f38c268b7c3012220d822929e9edff07326e89d5bb822b590602001610b3d565b603f546001600160a01b0316331480611f6e5750611f6e61261f565b611f8a5760405162461bcd60e51b8152600401610a7b90613964565b6037805460ff60a81b191690556040517f891ebab18da80ebeeea06b1b1cede098329c4c008906a98370c2ac7a80b571cb90600090a1565b611fca61261f565b611fe65760405162461bcd60e51b8152600401610a7b906139ac565b6001600160a01b03811660009081526049602052604090205460ff166120405760405162461bcd60e51b815260206004820152600f60248201526e139bdd081dda1a5d195b1a5cdd1959608a1b6044820152606401610a7b565b6001600160a01b038116600081815260496020526040808220805460ff19169055517f0ec40967a61509853550658e51d0e4297f7cba244fe4adc8ba18b5631ac20e2f9190a250565b61209161261f565b6120ad5760405162461bcd60e51b8152600401610a7b906139ac565b604880546001600160a01b0319166001600160a01b0383169081179091556040519081527f7d7719313229e558c5a3893cad2eb86a86a049156d1d9ebd5c63a8eedefd1c0390602001610b3d565b603f546001600160a01b0316331480612117575061211761261f565b6121335760405162461bcd60e51b8152600401610a7b90613964565b7f53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535805460011981016121775760405162461bcd60e51b8152600401610a7b90613a80565b60028255611e91308888888888612f2c565b61219161261f565b6121ad5760405162461bcd60e51b8152600401610a7b906139ac565b603a8190556040518181527f2ec5fb5a3d2703edc461252d92ccd2799c3c74f01d97212b20388207fa17ae4590602001610b3d565b603f546001600160a01b03163314806121fe57506121fe61261f565b61221a5760405162461bcd60e51b8152600401610a7b90613964565b306001600160a01b031663af14052c6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561225557600080fd5b505af1158015612269573d6000803e3d6000fd5b5050505060006301e133806064836122819190613abe565b61228b9190613abe565b90506122a16201518066b1a2bc2ec50000613abe565b8111156122e05760405162461bcd60e51b815260206004820152600d60248201526c0a4c2e8ca40e8dede40d0d2ced609b1b6044820152606401610a7b565b6122e9816129bd565b604f80546001600160401b0392909216600160801b0267ffffffffffffffff60801b199092169190911790556040518181527fef46f143af5fead0010484fe7d6ec2e2972420faa76157f5a6075aa72e614cb590602001611c50565b61234d61261f565b6123695760405162461bcd60e51b8152600401610a7b906139ac565b603b8190556040518181527f39367850377ac04920a9a670f2180e7a94d83b15ad302e59875ec58fd10bd37d90602001610b3d565b603f546001600160a01b03163314806123ba57506123ba61261f565b6123d65760405162461bcd60e51b8152600401610a7b90613964565b604080516001600160a01b038085168252831660208201527fba58ce12801c949fa65f41c46ed108671c219baf945fa48d21026cea99ff252a910160405180910390a16001600160a01b0381161561257b576001600160a01b03811660009081526035602052604090205460ff166124605760405162461bcd60e51b8152600401610a7b906139e3565b6001600160a01b038216600090815260336020526040902054819060ff166124c35760405162461bcd60e51b8152602060048201526016602482015275105cdcd95d081a5cc81b9bdd081cdd5c1c1bdc9d195960521b6044820152606401610a7b565b60405163551c457b60e11b81526001600160a01b03848116600483015282169063aa388af690602401602060405180830381865afa158015612509573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061252d9190613ae0565b6125795760405162461bcd60e51b815260206004820152601f60248201527f4173736574206e6f7420737570706f72746564206279205374726174656779006044820152606401610a7b565b505b6001600160a01b03918216600090815260406020819052902080546001600160a01b03191691909216179055565b603f546001600160a01b03163314806125c557506125c561261f565b6125e15760405162461bcd60e51b8152600401610a7b90613964565b6037805460ff60a01b1916600160a01b1790556040517f8cff26a5985614b3d30629cc4ab83824bf115aec971b718d8f2f99562032e97290600090a1565b6000612637600080516020613cbc8339815191525490565b6001600160a01b0316336001600160a01b031614905090565b603f546001600160a01b031633148061266c575061266c61261f565b6126885760405162461bcd60e51b8152600401610a7b90613964565b611934612f95565b61269861261f565b6126b45760405162461bcd60e51b8152600401610a7b906139ac565b6126dc817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b03166126fc600080516020613cbc8339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b61273c61261f565b6127585760405162461bcd60e51b8152600401610a7b906139ac565b604580546001600160a01b0319166001600160a01b0383169081179091556040519081527fa12850fb726e0b2b7b3c9a9342031e1268a8148d0eb06b4bea8613204ffcd2b890602001610b3d565b6127ae61261f565b6127ca5760405162461bcd60e51b8152600401610a7b906139ac565b6127118161ffff16106128165760405162461bcd60e51b8152602060048201526014602482015273496e76616c696420626173697320706f696e747360601b6044820152606401610a7b565b6048805461ffff60a01b1916600160a01b61ffff8416908102919091179091556040519081527ff12c00256bee2b6facb111a88a9b1cff86e79132939b44f1353212d6f746955790602001610b3d565b61286e61261f565b61288a5760405162461bcd60e51b8152600401610a7b906139ac565b6103e88111156128e65760405162461bcd60e51b815260206004820152602160248201527f52656465656d206665652073686f756c64206e6f74206265206f7665722031306044820152602560f81b6064820152608401610a7b565b60388190556040518181527fd6c7508d6658ccee36b7b7d7fd72e5cbaeefb40c64eff24e9ae7470e846304ee90602001610b3d565b61292361261f565b61293f5760405162461bcd60e51b8152600401610a7b906139ac565b803b6129995760405162461bcd60e51b8152602060048201526024808201527f6e657720696d706c656d656e746174696f6e206973206e6f74206120636f6e746044820152631c9858dd60e21b6064820152608401610a7b565b7fa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd955565b60006001600160401b03821115612a255760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203660448201526534206269747360d01b6064820152608401610a7b565b5090565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052612a7b908490612fec565b505050565b6001600160a01b0381166000908152603360205260409020805462010000900460ff1615612aac575050565b6000826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612aec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b109190613b02565b905060068160ff1610158015612b2a575060128160ff1611155b612b6d5760405162461bcd60e51b81526020600482015260146024820152732ab732bc3832b1ba32b210383932b1b4b9b4b7b760611b6044820152606401610a7b565b815460ff909116620100000262ff00001990911617905550565b600081831115612bb757612bb0612b9e8385613a3e565b612ba990600a613c06565b85906130be565b9350612be1565b81831015612be157612bde612bcc8484613a3e565b612bd790600a613c06565b85906130d3565b93505b50825b9392505050565b60405162461bcd60e51b815260206004820152601d60248201527f436f6c6c61746572616c2073776170206e6f7420737570706f727465640000006044820152600090606401610a7b565b612c3f816130df565b306001600160a01b031663b9b17f9f6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015612c7a57600080fd5b505af1158015612c8e573d6000803e3d6000fd5b5050505050565b6001600160a01b038116612ceb5760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f7220697320616464726573732830290000000000006044820152606401610a7b565b61125c816131a3565b6001600160a01b03851660009081526035602052604090205460ff16612d525760405162461bcd60e51b8152602060048201526013602482015272496e76616c696420746f20537472617465677960681b6044820152606401610a7b565b600183148015612d625750600181145b8015612dc657507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031684846000818110612da657612da6613a12565b9050602002016020810190612dbb9190613703565b6001600160a01b0316145b612e0b5760405162461bcd60e51b815260206004820152601660248201527513db9b1e4815d15512081a5cc81cdd5c1c1bdc9d195960521b6044820152606401610a7b565b612e1361320a565b82826000818110612e2657612e26613a12565b905060200201351115612e7b5760405162461bcd60e51b815260206004820152601960248201527f4e6f7420656e6f756768205745544820617661696c61626c65000000000000006044820152606401610a7b565b612ed28583836000818110612e9257612e92613a12565b905060200201357f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316612a299092919063ffffffff16565b846001600160a01b031663de5f62686040518163ffffffff1660e01b8152600401600060405180830381600087803b158015612f0d57600080fd5b505af1158015612f21573d6000803e3d6000fd5b505050505050505050565b612f3a868686868686613318565b306001600160a01b031663b9b17f9f6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015612f7557600080fd5b505af1158015612f89573d6000803e3d6000fd5b50505050505050505050565b612f9d6134a0565b306001600160a01b031663b9b17f9f6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015612fd857600080fd5b505af1158015610fee573d6000803e3d6000fd5b6000613041826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661352c9092919063ffffffff16565b805190915015612a7b578080602001905181019061305f9190613ae0565b612a7b5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610a7b565b60006130ca8284613c12565b90505b92915050565b60006130ca8284613abe565b6001600160a01b03811660009081526035602052604090205460ff166131475760405162461bcd60e51b815260206004820152601960248201527f5374726174656779206973206e6f7420737570706f72746564000000000000006044820152606401610a7b565b6000819050806001600160a01b031663853828b66040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561318757600080fd5b505af115801561319b573d6000803e3d6000fd5b505050505050565b806001600160a01b03166131c3600080516020613cbc8339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a3600080516020613cbc83398151915255565b60408051608081018252604b546001600160801b03808216808452600160801b9283900482166020850152604c5480831695850186905292909204166060830152600092839161325a9190613c29565b6040516370a0823160e01b81523060048201526001600160801b039190911691506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa1580156132cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132f39190613a67565b9050818111613306576000935050505090565b6133108282613a3e565b935050505090565b6001600160a01b03851660009081526035602052604090205460ff166133785760405162461bcd60e51b8152602060048201526015602482015274496e76616c69642066726f6d20537472617465677960581b6044820152606401610a7b565b8281146133c75760405162461bcd60e51b815260206004820152601960248201527f506172616d65746572206c656e677468206d69736d61746368000000000000006044820152606401610a7b565b8260005b8181101561349657866001600160a01b031663d9caed12898888858181106133f5576133f5613a12565b905060200201602081019061340a9190613703565b87878681811061341c5761341c613a12565b6040516001600160e01b031960e088901b1681526001600160a01b03958616600482015294909316602485015250602090910201356044820152606401600060405180830381600087803b15801561347357600080fd5b505af1158015613487573d6000803e3d6000fd5b505050508060010190506133cb565b5050505050505050565b60365460005b81811015610d2957603681815481106134c1576134c1613a12565b60009182526020822001546040805163429c145b60e11b815290516001600160a01b039092169263853828b69260048084019382900301818387803b15801561350957600080fd5b505af115801561351d573d6000803e3d6000fd5b505050508060010190506134a6565b606061353b8484600085613543565b949350505050565b6060824710156135a45760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610a7b565b843b6135f25760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610a7b565b600080866001600160a01b0316858760405161360e9190613c6c565b60006040518083038185875af1925050503d806000811461364b576040519150601f19603f3d011682016040523d82523d6000602084013e613650565b606091505b509150915061366082828661366b565b979650505050505050565b6060831561367a575081612be4565b82511561368a5782518084602001fd5b8160405162461bcd60e51b8152600401610a7b9190613c88565b6000602082840312156136b657600080fd5b5035919050565b80356001600160a01b03811681146136d457600080fd5b919050565b600080604083850312156136ec57600080fd5b6136f5836136bd565b946020939093013593505050565b60006020828403121561371557600080fd5b6130ca826136bd565b60008060008060008060a0878903121561373757600080fd5b613740876136bd565b955061374e602088016136bd565b9450604087013593506060870135925060808701356001600160401b0381111561377757600080fd5b8701601f8101891361378857600080fd5b80356001600160401b0381111561379e57600080fd5b8960208284010111156137b057600080fd5b60208201935080925050509295509295509295565b60ff8116811461125c57600080fd5b600080604083850312156137e757600080fd5b6137f0836136bd565b91506020830135613800816137c5565b809150509250929050565b803561ffff811681146136d457600080fd5b6000806040838503121561383057600080fd5b613839836136bd565b91506138476020840161380b565b90509250929050565b60008083601f84011261386257600080fd5b5081356001600160401b0381111561387957600080fd5b6020830191508360208260051b850101111561389457600080fd5b9250929050565b6000806000806000606086880312156138b357600080fd5b6138bc866136bd565b945060208601356001600160401b038111156138d757600080fd5b6138e388828901613850565b90955093505060408601356001600160401b0381111561390257600080fd5b61390e88828901613850565b969995985093965092949392505050565b6000806040838503121561393257600080fd5b61393b836136bd565b9150613847602084016136bd565b60006020828403121561395b57600080fd5b6130ca8261380b565b60208082526028908201527f43616c6c6572206973206e6f74207468652053747261746567697374206f722060408201526723b7bb32b93737b960c11b606082015260800190565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b60208082526015908201527414dd1c985d1959de481b9bdd08185c1c1c9bdd9959605a1b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b818103818111156130cd576130cd613a28565b634e487b7160e01b600052603160045260246000fd5b600060208284031215613a7957600080fd5b5051919050565b6020808252600e908201526d1499595b9d1c985b9d0818d85b1b60921b604082015260600190565b634e487b7160e01b600052602160045260246000fd5b600082613adb57634e487b7160e01b600052601260045260246000fd5b500490565b600060208284031215613af257600080fd5b81518015158114612be457600080fd5b600060208284031215613b1457600080fd5b8151612be4816137c5565b6001815b6001841115613b5a57808504811115613b3e57613b3e613a28565b6001841615613b4c57908102905b60019390931c928002613b23565b935093915050565b600082613b71575060016130cd565b81613b7e575060006130cd565b8160018114613b945760028114613b9e57613bba565b60019150506130cd565b60ff841115613baf57613baf613a28565b50506001821b6130cd565b5060208310610133831016604e8410600b8410161715613bdd575081810a6130cd565b613bea6000198484613b1f565b8060001904821115613bfe57613bfe613a28565b029392505050565b60006130ca8383613b62565b80820281158282048414176130cd576130cd613a28565b6001600160801b0382811682821603908111156130cd576130cd613a28565b60005b83811015613c63578181015183820152602001613c4b565b50506000910152565b60008251613c7e818460208701613c48565b9190910192915050565b6020815260008251806020840152613ca7816040850160208701613c48565b601f01601f1916919091016040019291505056fe7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa2646970667358221220f66ef6d77c6c8db269e1abfd6a11ef140019a19b196946d407b669850fff65d564736f6c634300081c0033", + "libraries": {}, + "devdoc": { + "author": "Origin Protocol Inc", + "kind": "dev", + "methods": { + "addStrategyToMintWhitelist(address)": { + "params": { + "strategyAddr": "Strategy address" + } + }, + "allowedSwapUndervalue()": { + "returns": { + "value": "Percentage in basis points." + } + }, + "approveStrategy(address)": { + "params": { + "_addr": "Address of the strategy to add" + } + }, + "cacheDecimals(address)": { + "params": { + "_asset": "Address of asset token" + } + }, + "depositToStrategy(address,address[],uint256[])": { + "params": { + "_amounts": "Array of amounts of each corresponding asset to deposit.", + "_assets": "Array of asset address that will be deposited into the strategy.", + "_strategyToAddress": "Address of the Strategy to deposit assets into." + } + }, + "removeAsset(address)": { + "params": { + "_asset": "Address of asset" + } + }, + "removeStrategy(address)": { + "params": { + "_addr": "Address of the strategy to remove" + } + }, + "removeStrategyFromMintWhitelist(address)": { + "params": { + "strategyAddr": "Strategy address" + } + }, + "setAdminImpl(address)": { + "params": { + "newImpl": "address of the implementation" + } + }, + "setAssetDefaultStrategy(address,address)": { + "params": { + "_asset": "Address of the asset", + "_strategy": "Address of the Strategy" + } + }, + "setAutoAllocateThreshold(uint256)": { + "params": { + "_threshold": "OToken amount with 18 fixed decimals." + } + }, + "setDripDuration(uint256)": { + "params": { + "_dripDuration": "Time in seconds to target a constant yield rate" + } + }, + "setNetOusdMintForStrategyThreshold(uint256)": { + "params": { + "_threshold": "OToken amount with 18 fixed decimals." + } + }, + "setOracleSlippage(address,uint16)": { + "params": { + "_allowedOracleSlippageBps": "allowed slippage from Oracle in basis points. eg 20 = 0.2%. Max 10%.", + "_asset": "Address of the asset token." + } + }, + "setOusdMetaStrategy(address)": { + "params": { + "_ousdMetaStrategy": "Address of OToken metapool strategy" + } + }, + "setPriceProvider(address)": { + "params": { + "_priceProvider": "Address of price provider" + } + }, + "setRebaseRateMax(uint256)": { + "params": { + "yearlyApr": "in 1e18 notation. 3 * 1e18 = 3% APR" + } + }, + "setRebaseThreshold(uint256)": { + "params": { + "_threshold": "OToken amount with 18 fixed decimals." + } + }, + "setRedeemFeeBps(uint256)": { + "params": { + "_redeemFeeBps": "Basis point fee to be charged" + } + }, + "setStrategistAddr(address)": { + "params": { + "_address": "Address of Strategist" + } + }, + "setSwapAllowedUndervalue(uint16)": { + "params": { + "_basis": "Percentage in basis points. eg 100 == 1%" + } + }, + "setSwapper(address)": { + "params": { + "_swapperAddr": "Address of the Swapper contract that implements the ISwapper interface." + } + }, + "setVaultBuffer(uint256)": { + "params": { + "_vaultBuffer": "Percentage using 18 decimals. 100% = 1e18." + } + }, + "setWithdrawalClaimDelay(uint256)": { + "params": { + "_delay": "Delay period (should be between 10 mins to 7 days). Set to 0 to disable async withdrawals" + } + }, + "supportAsset(address,uint8)": { + "params": { + "_asset": "Address of asset" + } + }, + "swapCollateral(address,address,uint256,uint256,bytes)": { + "params": { + "_data": "implementation specific data. eg 1Inch swap data", + "_fromAsset": "The token address of the asset being sold by the vault.", + "_fromAssetAmount": "The amount of assets being sold by the vault.", + "_minToAssetAmount": "The minimum amount of assets to be purchased.", + "_toAsset": "The token address of the asset being purchased by the vault." + }, + "returns": { + "toAssetAmount": "The amount of toAssets that was received from the swap" + } + }, + "transferGovernance(address)": { + "params": { + "_newGovernor": "Address of the new Governor" + } + }, + "transferToken(address,uint256)": { + "params": { + "_amount": "Amount of the asset to transfer", + "_asset": "Address for the asset" + } + }, + "withdrawAllFromStrategy(address)": { + "params": { + "_strategyAddr": "Strategy address." + } + }, + "withdrawFromStrategy(address,address[],uint256[])": { + "params": { + "_amounts": "Array of amounts of each corresponding asset to withdraw.", + "_assets": "Array of asset address that will be withdrawn from the strategy.", + "_strategyFromAddress": "Address of the Strategy to withdraw assets from." + } + } + }, + "title": "OETH VaultAdmin Contract", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "addStrategyToMintWhitelist(address)": { + "notice": "Adds a strategy to the mint whitelist. Reverts if strategy isn't approved on Vault." + }, + "allowedSwapUndervalue()": { + "notice": "Max allowed percentage the vault total value can drop below the OToken total supply in basis points when executing a collateral swap. For example 100 == 1%" + }, + "approveStrategy(address)": { + "notice": "Add a strategy to the Vault." + }, + "assetDefaultStrategies(address)": { + "notice": "Mapping of asset address to the Strategy that they should automatically" + }, + "autoAllocateThreshold()": { + "notice": "OToken mints over this amount automatically allocate funds. 18 decimals." + }, + "cacheDecimals(address)": { + "notice": "Cache decimals on OracleRouter for a particular asset. This action is required before that asset's price can be accessed." + }, + "capitalPaused()": { + "notice": "pause operations that change the OToken supply. eg mint, redeem, allocate, mint/burn for strategy" + }, + "claimGovernance()": { + "notice": "Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor." + }, + "depositToStrategy(address,address[],uint256[])": { + "notice": "Deposit multiple assets from the vault into the strategy." + }, + "dripDuration()": { + "notice": "Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable." + }, + "governor()": { + "notice": "Returns the address of the current Governor." + }, + "isGovernor()": { + "notice": "Returns true if the caller is the current Governor." + }, + "lastRebase()": { + "notice": "Time in seconds that the vault last rebased yield." + }, + "maxSupplyDiff()": { + "notice": "Max difference between total supply and total value of assets. 18 decimals." + }, + "netOusdMintForStrategyThreshold()": { + "notice": "How much net total OTokens are allowed to be minted by all strategies" + }, + "netOusdMintedForStrategy()": { + "notice": "How much OTokens are currently minted by the strategy" + }, + "ousdMetaStrategy()": { + "notice": "Metapool strategy that is allowed to mint/burn OTokens without changing collateral" + }, + "pauseCapital()": { + "notice": "Set the deposit paused flag to true to prevent capital movement." + }, + "pauseRebase()": { + "notice": "Set the deposit paused flag to true to prevent rebasing." + }, + "priceProvider()": { + "notice": "Address of the Oracle price provider contract" + }, + "rebasePaused()": { + "notice": "pause rebasing if true" + }, + "rebasePerSecondMax()": { + "notice": "max rebase percentage per second Can be used to set maximum yield of the protocol, spreading out yield over time" + }, + "rebasePerSecondTarget()": { + "notice": "target rebase rate limit, based on past rates and funds available." + }, + "rebaseThreshold()": { + "notice": "OToken mints over this amount automatically rebase. 18 decimals." + }, + "redeemFeeBps()": { + "notice": "Redemption fee in basis points. eg 50 = 0.5%" + }, + "removeAsset(address)": { + "notice": "Remove a supported asset from the Vault" + }, + "removeStrategy(address)": { + "notice": "Remove a strategy from the Vault." + }, + "removeStrategyFromMintWhitelist(address)": { + "notice": "Removes a strategy from the mint whitelist." + }, + "setAdminImpl(address)": { + "notice": "set the implementation for the admin, this needs to be in a base class else we cannot set it" + }, + "setAssetDefaultStrategy(address,address)": { + "notice": "Set the default Strategy for an asset, i.e. the one which the asset will be automatically allocated to and withdrawn from" + }, + "setAutoAllocateThreshold(uint256)": { + "notice": "Sets the minimum amount of OTokens in a mint to trigger an automatic allocation of funds afterwords." + }, + "setDripDuration(uint256)": { + "notice": "Set the drip duration period" + }, + "setMaxSupplyDiff(uint256)": { + "notice": "Sets the maximum allowable difference between total supply and backing assets' value." + }, + "setNetOusdMintForStrategyThreshold(uint256)": { + "notice": "Set maximum amount of OTokens that can at any point be minted and deployed to strategy (used only by ConvexOUSDMetaStrategy for now)." + }, + "setOracleSlippage(address,uint16)": { + "notice": "Set the allowed slippage from the Oracle price for collateral asset swaps." + }, + "setOusdMetaStrategy(address)": { + "notice": "Set OToken Metapool strategy" + }, + "setPriceProvider(address)": { + "notice": "Set address of price provider." + }, + "setRebaseRateMax(uint256)": { + "notice": "Set a yield streaming max rate. This spreads yield over time if it is above the max rate." + }, + "setRebaseThreshold(uint256)": { + "notice": "Set a minimum amount of OTokens in a mint or redeem that triggers a rebase" + }, + "setRedeemFeeBps(uint256)": { + "notice": "Set a fee in basis points to be charged for a redeem." + }, + "setStrategistAddr(address)": { + "notice": "Set address of Strategist" + }, + "setSwapAllowedUndervalue(uint16)": { + "notice": "Set max allowed percentage the vault total value can drop below the OToken total supply in basis points when executing collateral swaps." + }, + "setSwapper(address)": { + "notice": "Set the contract the performs swaps of collateral assets." + }, + "setTrusteeAddress(address)": { + "notice": "Sets the trusteeAddress that can receive a portion of yield. Setting to the zero address disables this feature." + }, + "setTrusteeFeeBps(uint256)": { + "notice": "Sets the TrusteeFeeBps to the percentage of yield that should be received in basis points." + }, + "setVaultBuffer(uint256)": { + "notice": "Set a buffer of assets to keep in the Vault to handle most redemptions without needing to spend gas unwinding assets from a Strategy." + }, + "setWithdrawalClaimDelay(uint256)": { + "notice": "Changes the async withdrawal claim period for OETH & superOETHb" + }, + "strategistAddr()": { + "notice": "Address of the Strategist" + }, + "supportAsset(address,uint8)": { + "notice": "Add a supported asset to the contract, i.e. one that can be to mint OTokens." + }, + "swapCollateral(address,address,uint256,uint256,bytes)": { + "notice": "Strategist swaps collateral assets sitting in the vault." + }, + "swapper()": { + "notice": "Contract that swaps the vault's collateral assets" + }, + "transferGovernance(address)": { + "notice": "Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete" + }, + "transferToken(address,uint256)": { + "notice": "Transfer token to governor. Intended for recovering tokens stuck in contract, i.e. mistaken sends." + }, + "trusteeAddress()": { + "notice": "Trustee contract that can collect a percentage of yield" + }, + "trusteeFeeBps()": { + "notice": "Amount of yield collected in basis points. eg 2000 = 20%" + }, + "unpauseCapital()": { + "notice": "Set the deposit paused flag to false to enable capital movement." + }, + "unpauseRebase()": { + "notice": "Set the deposit paused flag to true to allow rebasing." + }, + "vaultBuffer()": { + "notice": "Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18." + }, + "withdrawAllFromStrategies()": { + "notice": "Withdraws all assets from all the strategies and sends assets to the Vault." + }, + "withdrawAllFromStrategy(address)": { + "notice": "Withdraws all assets from the strategy and sends assets to the Vault." + }, + "withdrawFromStrategy(address,address[],uint256[])": { + "notice": "Withdraw multiple assets from the strategy to the vault." + }, + "withdrawalClaimDelay()": { + "notice": "Sets a minimum delay that is required to elapse between requesting async withdrawals and claiming the request. When set to 0 async withdrawals are disabled." + }, + "withdrawalQueueMetadata()": { + "notice": "Global metadata for the withdrawal queue including: queued - cumulative total of all withdrawal requests included the ones that have already been claimed claimable - cumulative total of all the requests that can be claimed including the ones already claimed claimed - total of all the requests that have been claimed nextWithdrawalIndex - index of the next withdrawal request starting at 0" + }, + "withdrawalRequests(uint256)": { + "notice": "Mapping of withdrawal request indices to the user withdrawal request data" + } + }, + "version": 1 + }, + "storageLayout": { + "storage": [ + { + "astId": 72011, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "initialized", + "offset": 0, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 72014, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "initializing", + "offset": 1, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 72054, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "______gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage" + }, + { + "astId": 79250, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "assets", + "offset": 0, + "slot": "51", + "type": "t_mapping(t_address,t_struct(Asset)79244_storage)" + }, + { + "astId": 79254, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "allAssets", + "offset": 0, + "slot": "52", + "type": "t_array(t_address)dyn_storage" + }, + { + "astId": 79265, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "strategies", + "offset": 0, + "slot": "53", + "type": "t_mapping(t_address,t_struct(Strategy)79259_storage)" + }, + { + "astId": 79269, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "allStrategies", + "offset": 0, + "slot": "54", + "type": "t_array(t_address)dyn_storage" + }, + { + "astId": 79272, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "priceProvider", + "offset": 0, + "slot": "55", + "type": "t_address" + }, + { + "astId": 79275, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "rebasePaused", + "offset": 20, + "slot": "55", + "type": "t_bool" + }, + { + "astId": 79278, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "capitalPaused", + "offset": 21, + "slot": "55", + "type": "t_bool" + }, + { + "astId": 79281, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "redeemFeeBps", + "offset": 0, + "slot": "56", + "type": "t_uint256" + }, + { + "astId": 79284, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "vaultBuffer", + "offset": 0, + "slot": "57", + "type": "t_uint256" + }, + { + "astId": 79287, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "autoAllocateThreshold", + "offset": 0, + "slot": "58", + "type": "t_uint256" + }, + { + "astId": 79290, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "rebaseThreshold", + "offset": 0, + "slot": "59", + "type": "t_uint256" + }, + { + "astId": 79294, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "oUSD", + "offset": 0, + "slot": "60", + "type": "t_contract(OUSD)69402" + }, + { + "astId": 79305, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "_deprecated_rebaseHooksAddr", + "offset": 0, + "slot": "61", + "type": "t_address" + }, + { + "astId": 79312, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "_deprecated_uniswapAddr", + "offset": 0, + "slot": "62", + "type": "t_address" + }, + { + "astId": 79319, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "strategistAddr", + "offset": 0, + "slot": "63", + "type": "t_address" + }, + { + "astId": 79324, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "assetDefaultStrategies", + "offset": 0, + "slot": "64", + "type": "t_mapping(t_address,t_address)" + }, + { + "astId": 79327, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "maxSupplyDiff", + "offset": 0, + "slot": "65", + "type": "t_uint256" + }, + { + "astId": 79330, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "trusteeAddress", + "offset": 0, + "slot": "66", + "type": "t_address" + }, + { + "astId": 79333, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "trusteeFeeBps", + "offset": 0, + "slot": "67", + "type": "t_uint256" + }, + { + "astId": 79337, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "_deprecated_swapTokens", + "offset": 0, + "slot": "68", + "type": "t_array(t_address)dyn_storage" + }, + { + "astId": 79343, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "ousdMetaStrategy", + "offset": 0, + "slot": "69", + "type": "t_address" + }, + { + "astId": 79346, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "netOusdMintedForStrategy", + "offset": 0, + "slot": "70", + "type": "t_int256" + }, + { + "astId": 79349, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "netOusdMintForStrategyThreshold", + "offset": 0, + "slot": "71", + "type": "t_uint256" + }, + { + "astId": 79371, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "swapConfig", + "offset": 0, + "slot": "72", + "type": "t_struct(SwapConfig)79361_storage" + }, + { + "astId": 79375, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "isMintWhitelistedStrategy", + "offset": 0, + "slot": "73", + "type": "t_mapping(t_address,t_bool)" + }, + { + "astId": 79378, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "_deprecated_dripper", + "offset": 0, + "slot": "74", + "type": "t_address" + }, + { + "astId": 79392, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "withdrawalQueueMetadata", + "offset": 0, + "slot": "75", + "type": "t_struct(WithdrawalQueueMetadata)79388_storage" + }, + { + "astId": 79409, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "withdrawalRequests", + "offset": 0, + "slot": "77", + "type": "t_mapping(t_uint256,t_struct(WithdrawalRequest)79403_storage)" + }, + { + "astId": 79412, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "withdrawalClaimDelay", + "offset": 0, + "slot": "78", + "type": "t_uint256" + }, + { + "astId": 79415, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "lastRebase", + "offset": 0, + "slot": "79", + "type": "t_uint64" + }, + { + "astId": 79418, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "dripDuration", + "offset": 8, + "slot": "79", + "type": "t_uint64" + }, + { + "astId": 79421, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "rebasePerSecondMax", + "offset": 16, + "slot": "79", + "type": "t_uint64" + }, + { + "astId": 79424, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "rebasePerSecondTarget", + "offset": 24, + "slot": "79", + "type": "t_uint64" + }, + { + "astId": 79439, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "__gap", + "offset": 0, + "slot": "80", + "type": "t_array(t_uint256)43_storage" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_address)dyn_storage": { + "base": "t_address", + "encoding": "dynamic_array", + "label": "address[]", + "numberOfBytes": "32" + }, + "t_array(t_uint256)43_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[43]", + "numberOfBytes": "1376" + }, + "t_array(t_uint256)50_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "encoding": "inplace", + "label": "bool", + "numberOfBytes": "1" + }, + "t_contract(OUSD)69402": { + "encoding": "inplace", + "label": "contract OUSD", + "numberOfBytes": "20" + }, + "t_enum(UnitConversion)79234": { + "encoding": "inplace", + "label": "enum VaultStorage.UnitConversion", + "numberOfBytes": "1" + }, + "t_int256": { + "encoding": "inplace", + "label": "int256", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_address)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => address)", + "numberOfBytes": "32", + "value": "t_address" + }, + "t_mapping(t_address,t_bool)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => bool)", + "numberOfBytes": "32", + "value": "t_bool" + }, + "t_mapping(t_address,t_struct(Asset)79244_storage)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => struct VaultStorage.Asset)", + "numberOfBytes": "32", + "value": "t_struct(Asset)79244_storage" + }, + "t_mapping(t_address,t_struct(Strategy)79259_storage)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => struct VaultStorage.Strategy)", + "numberOfBytes": "32", + "value": "t_struct(Strategy)79259_storage" + }, + "t_mapping(t_uint256,t_struct(WithdrawalRequest)79403_storage)": { + "encoding": "mapping", + "key": "t_uint256", + "label": "mapping(uint256 => struct VaultStorage.WithdrawalRequest)", + "numberOfBytes": "32", + "value": "t_struct(WithdrawalRequest)79403_storage" + }, + "t_struct(Asset)79244_storage": { + "encoding": "inplace", + "label": "struct VaultStorage.Asset", + "members": [ + { + "astId": 79236, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "isSupported", + "offset": 0, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 79239, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "unitConversion", + "offset": 1, + "slot": "0", + "type": "t_enum(UnitConversion)79234" + }, + { + "astId": 79241, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "decimals", + "offset": 2, + "slot": "0", + "type": "t_uint8" + }, + { + "astId": 79243, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "allowedOracleSlippageBps", + "offset": 3, + "slot": "0", + "type": "t_uint16" + } + ], + "numberOfBytes": "32" + }, + "t_struct(Strategy)79259_storage": { + "encoding": "inplace", + "label": "struct VaultStorage.Strategy", + "members": [ + { + "astId": 79256, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "isSupported", + "offset": 0, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 79258, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "_deprecated", + "offset": 0, + "slot": "1", + "type": "t_uint256" + } + ], + "numberOfBytes": "64" + }, + "t_struct(SwapConfig)79361_storage": { + "encoding": "inplace", + "label": "struct VaultStorage.SwapConfig", + "members": [ + { + "astId": 79358, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "swapper", + "offset": 0, + "slot": "0", + "type": "t_address" + }, + { + "astId": 79360, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "allowedUndervalueBps", + "offset": 20, + "slot": "0", + "type": "t_uint16" + } + ], + "numberOfBytes": "32" + }, + "t_struct(WithdrawalQueueMetadata)79388_storage": { + "encoding": "inplace", + "label": "struct VaultStorage.WithdrawalQueueMetadata", + "members": [ + { + "astId": 79381, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "queued", + "offset": 0, + "slot": "0", + "type": "t_uint128" + }, + { + "astId": 79383, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "claimable", + "offset": 16, + "slot": "0", + "type": "t_uint128" + }, + { + "astId": 79385, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "claimed", + "offset": 0, + "slot": "1", + "type": "t_uint128" + }, + { + "astId": 79387, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "nextWithdrawalIndex", + "offset": 16, + "slot": "1", + "type": "t_uint128" + } + ], + "numberOfBytes": "64" + }, + "t_struct(WithdrawalRequest)79403_storage": { + "encoding": "inplace", + "label": "struct VaultStorage.WithdrawalRequest", + "members": [ + { + "astId": 79394, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "withdrawer", + "offset": 0, + "slot": "0", + "type": "t_address" + }, + { + "astId": 79396, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "claimed", + "offset": 20, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 79398, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "timestamp", + "offset": 21, + "slot": "0", + "type": "t_uint40" + }, + { + "astId": 79400, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "amount", + "offset": 0, + "slot": "1", + "type": "t_uint128" + }, + { + "astId": 79402, + "contract": "contracts/vault/OETHVaultAdmin.sol:OETHVaultAdmin", + "label": "queued", + "offset": 16, + "slot": "1", + "type": "t_uint128" + } + ], + "numberOfBytes": "64" + }, + "t_uint128": { + "encoding": "inplace", + "label": "uint128", + "numberOfBytes": "16" + }, + "t_uint16": { + "encoding": "inplace", + "label": "uint16", + "numberOfBytes": "2" + }, + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint40": { + "encoding": "inplace", + "label": "uint40", + "numberOfBytes": "5" + }, + "t_uint64": { + "encoding": "inplace", + "label": "uint64", + "numberOfBytes": "8" + }, + "t_uint8": { + "encoding": "inplace", + "label": "uint8", + "numberOfBytes": "1" + } + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/OETHVaultCore.json b/contracts/deployments/hoodi/OETHVaultCore.json new file mode 100644 index 0000000000..a732e4e583 --- /dev/null +++ b/contracts/deployments/hoodi/OETHVaultCore.json @@ -0,0 +1,2373 @@ +{ + "address": "0xB8F98C34e40E0D201CE2F3440cE92d0B5c5CfFe2", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_weth", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_threshold", + "type": "uint256" + } + ], + "name": "AllocateThresholdUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_strategy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "AssetAllocated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_strategy", + "type": "address" + } + ], + "name": "AssetDefaultStrategyUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_asset", + "type": "address" + } + ], + "name": "AssetRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_asset", + "type": "address" + } + ], + "name": "AssetSupported", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CapitalPaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CapitalUnpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "dripDuration", + "type": "uint256" + } + ], + "name": "DripDurationChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "GovernorshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "maxSupplyDiff", + "type": "uint256" + } + ], + "name": "MaxSupplyDiffChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_addr", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_threshold", + "type": "uint256" + } + ], + "name": "NetOusdMintForStrategyThresholdChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_ousdMetaStrategy", + "type": "address" + } + ], + "name": "OusdMetaStrategyUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "PendingGovernorshipTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_priceProvider", + "type": "address" + } + ], + "name": "PriceProviderUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "RebasePaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "rebaseRatePerSecond", + "type": "uint256" + } + ], + "name": "RebasePerSecondMaxChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_threshold", + "type": "uint256" + } + ], + "name": "RebaseThresholdUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "RebaseUnpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_addr", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "Redeem", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_redeemFeeBps", + "type": "uint256" + } + ], + "name": "RedeemFeeUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "StrategistUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "strategy", + "type": "address" + } + ], + "name": "StrategyAddedToMintWhitelist", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_addr", + "type": "address" + } + ], + "name": "StrategyApproved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_addr", + "type": "address" + } + ], + "name": "StrategyRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "strategy", + "type": "address" + } + ], + "name": "StrategyRemovedFromMintWhitelist", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_basis", + "type": "uint256" + } + ], + "name": "SwapAllowedUndervalueChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_basis", + "type": "uint256" + } + ], + "name": "SwapSlippageChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_fromAsset", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_toAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_fromAssetAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_toAssetAmount", + "type": "uint256" + } + ], + "name": "Swapped", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "SwapperChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "TrusteeAddressChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_basis", + "type": "uint256" + } + ], + "name": "TrusteeFeeBpsChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_vaultBuffer", + "type": "uint256" + } + ], + "name": "VaultBufferUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_newDelay", + "type": "uint256" + } + ], + "name": "WithdrawalClaimDelayUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_claimable", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_newClaimable", + "type": "uint256" + } + ], + "name": "WithdrawalClaimable", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_withdrawer", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "_requestId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "WithdrawalClaimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_withdrawer", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "_requestId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_queued", + "type": "uint256" + } + ], + "name": "WithdrawalRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_yield", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_fee", + "type": "uint256" + } + ], + "name": "YieldDistribution", + "type": "event" + }, + { + "stateMutability": "nonpayable", + "type": "fallback" + }, + { + "inputs": [], + "name": "ADMIN_IMPLEMENTATION", + "outputs": [ + { + "internalType": "address", + "name": "adminImpl", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "addWithdrawalQueueLiquidity", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "adminImplPosition", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "allocate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "assetDefaultStrategies", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "autoAllocateThreshold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burnForStrategy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "cacheWETHAssetIndex", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "calculateRedeemOutputs", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "capitalPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + } + ], + "name": "checkBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "claimGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_requestId", + "type": "uint256" + } + ], + "name": "claimWithdrawal", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "_requestIds", + "type": "uint256[]" + } + ], + "name": "claimWithdrawals", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "totalAmount", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "dripDuration", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAllAssets", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAllStrategies", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + } + ], + "name": "getAssetConfig", + "outputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "isSupported", + "type": "bool" + }, + { + "internalType": "enum VaultStorage.UnitConversion", + "name": "unitConversion", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "allowedOracleSlippageBps", + "type": "uint16" + } + ], + "internalType": "struct VaultStorage.Asset", + "name": "config", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAssetCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getStrategyCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "governor", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_priceProvider", + "type": "address" + }, + { + "internalType": "address", + "name": "_oToken", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isGovernor", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isMintWhitelistedStrategy", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + } + ], + "name": "isSupportedAsset", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastRebase", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxSupplyDiff", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minimumOusdAmount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "mintForStrategy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "netOusdMintForStrategyThreshold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "netOusdMintedForStrategy", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "oUSD", + "outputs": [ + { + "internalType": "contract OUSD", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ousdMetaStrategy", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "previewYield", + "outputs": [ + { + "internalType": "uint256", + "name": "yield", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "priceProvider", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "priceUnitMint", + "outputs": [ + { + "internalType": "uint256", + "name": "price", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "priceUnitRedeem", + "outputs": [ + { + "internalType": "uint256", + "name": "price", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rebase", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "rebasePaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rebasePerSecondMax", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rebasePerSecondTarget", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rebaseThreshold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minimumUnitAmount", + "type": "uint256" + } + ], + "name": "redeem", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "redeemFeeBps", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "requestWithdrawal", + "outputs": [ + { + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "queued", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImpl", + "type": "address" + } + ], + "name": "setAdminImpl", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "strategies", + "outputs": [ + { + "internalType": "bool", + "name": "isSupported", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "_deprecated", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "strategistAddr", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalValue", + "outputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newGovernor", + "type": "address" + } + ], + "name": "transferGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "trusteeAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "trusteeFeeBps", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "vaultBuffer", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "weth", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "wethAssetIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawalClaimDelay", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawalQueueMetadata", + "outputs": [ + { + "internalType": "uint128", + "name": "queued", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "claimable", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "claimed", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "nextWithdrawalIndex", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "withdrawalRequests", + "outputs": [ + { + "internalType": "address", + "name": "withdrawer", + "type": "address" + }, + { + "internalType": "bool", + "name": "claimed", + "type": "bool" + }, + { + "internalType": "uint40", + "name": "timestamp", + "type": "uint40" + }, + { + "internalType": "uint128", + "name": "amount", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "queued", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "transactionHash": "0xa1ea9ba130ec3b537569aaab79bd71e09b050ab3c2386cb57c1c23fd4c21b905", + "receipt": { + "to": null, + "from": "0xf7749B41db006860cEc0650D18b8013d69C44Eeb", + "contractAddress": "0xB8F98C34e40E0D201CE2F3440cE92d0B5c5CfFe2", + "transactionIndex": 5, + "gasUsed": "3694451", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x3e14f452917e2aac626dd649c33ac647df0e32cb6654dbc3b3140312a1152fff", + "transactionHash": "0xa1ea9ba130ec3b537569aaab79bd71e09b050ab3c2386cb57c1c23fd4c21b905", + "logs": [], + "blockNumber": 904838, + "cumulativeGasUsed": "5329107", + "status": 1, + "byzantium": true + }, + "args": [ + "0x2387fD72C1DA19f6486B843F5da562679FbB4057" + ], + "numDeployments": 2, + "solcInputHash": "3de98f56666780054699342674f08b14", + "metadata": "{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_weth\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_threshold\",\"type\":\"uint256\"}],\"name\":\"AllocateThresholdUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_strategy\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"AssetAllocated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_strategy\",\"type\":\"address\"}],\"name\":\"AssetDefaultStrategyUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"}],\"name\":\"AssetRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"}],\"name\":\"AssetSupported\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"CapitalPaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"CapitalUnpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"dripDuration\",\"type\":\"uint256\"}],\"name\":\"DripDurationChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"GovernorshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"maxSupplyDiff\",\"type\":\"uint256\"}],\"name\":\"MaxSupplyDiffChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Mint\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_threshold\",\"type\":\"uint256\"}],\"name\":\"NetOusdMintForStrategyThresholdChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_ousdMetaStrategy\",\"type\":\"address\"}],\"name\":\"OusdMetaStrategyUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"PendingGovernorshipTransfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_priceProvider\",\"type\":\"address\"}],\"name\":\"PriceProviderUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"RebasePaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"rebaseRatePerSecond\",\"type\":\"uint256\"}],\"name\":\"RebasePerSecondMaxChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_threshold\",\"type\":\"uint256\"}],\"name\":\"RebaseThresholdUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"RebaseUnpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Redeem\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_redeemFeeBps\",\"type\":\"uint256\"}],\"name\":\"RedeemFeeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_address\",\"type\":\"address\"}],\"name\":\"StrategistUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"strategy\",\"type\":\"address\"}],\"name\":\"StrategyAddedToMintWhitelist\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"StrategyApproved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"StrategyRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"strategy\",\"type\":\"address\"}],\"name\":\"StrategyRemovedFromMintWhitelist\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_basis\",\"type\":\"uint256\"}],\"name\":\"SwapAllowedUndervalueChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_basis\",\"type\":\"uint256\"}],\"name\":\"SwapSlippageChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_fromAsset\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_toAsset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_fromAssetAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_toAssetAmount\",\"type\":\"uint256\"}],\"name\":\"Swapped\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_address\",\"type\":\"address\"}],\"name\":\"SwapperChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_address\",\"type\":\"address\"}],\"name\":\"TrusteeAddressChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_basis\",\"type\":\"uint256\"}],\"name\":\"TrusteeFeeBpsChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_vaultBuffer\",\"type\":\"uint256\"}],\"name\":\"VaultBufferUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_newDelay\",\"type\":\"uint256\"}],\"name\":\"WithdrawalClaimDelayUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_claimable\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_newClaimable\",\"type\":\"uint256\"}],\"name\":\"WithdrawalClaimable\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_withdrawer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"WithdrawalClaimed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_withdrawer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_queued\",\"type\":\"uint256\"}],\"name\":\"WithdrawalRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_yield\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_fee\",\"type\":\"uint256\"}],\"name\":\"YieldDistribution\",\"type\":\"event\"},{\"stateMutability\":\"nonpayable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"ADMIN_IMPLEMENTATION\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"adminImpl\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"addWithdrawalQueueLiquidity\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"adminImplPosition\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"allocate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"assetDefaultStrategies\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"autoAllocateThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burnForStrategy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"cacheWETHAssetIndex\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"calculateRedeemOutputs\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"capitalPaused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"}],\"name\":\"checkBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_requestId\",\"type\":\"uint256\"}],\"name\":\"claimWithdrawal\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"_requestIds\",\"type\":\"uint256[]\"}],\"name\":\"claimWithdrawals\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"amounts\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256\",\"name\":\"totalAmount\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dripDuration\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllAssets\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllStrategies\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"}],\"name\":\"getAssetConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isSupported\",\"type\":\"bool\"},{\"internalType\":\"enum VaultStorage.UnitConversion\",\"name\":\"unitConversion\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"decimals\",\"type\":\"uint8\"},{\"internalType\":\"uint16\",\"name\":\"allowedOracleSlippageBps\",\"type\":\"uint16\"}],\"internalType\":\"struct VaultStorage.Asset\",\"name\":\"config\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAssetCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStrategyCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"governor\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_priceProvider\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_oToken\",\"type\":\"address\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isGovernor\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"isMintWhitelistedStrategy\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"}],\"name\":\"isSupportedAsset\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastRebase\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"maxSupplyDiff\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_minimumOusdAmount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mintForStrategy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"netOusdMintForStrategyThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"netOusdMintedForStrategy\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"oUSD\",\"outputs\":[{\"internalType\":\"contract OUSD\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"ousdMetaStrategy\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"previewYield\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"yield\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"priceProvider\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"}],\"name\":\"priceUnitMint\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"price\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"}],\"name\":\"priceUnitRedeem\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"price\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rebase\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rebasePaused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rebasePerSecondMax\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rebasePerSecondTarget\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rebaseThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_minimumUnitAmount\",\"type\":\"uint256\"}],\"name\":\"redeem\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"redeemFeeBps\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"requestWithdrawal\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"queued\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImpl\",\"type\":\"address\"}],\"name\":\"setAdminImpl\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"strategies\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"isSupported\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"_deprecated\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"strategistAddr\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalValue\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newGovernor\",\"type\":\"address\"}],\"name\":\"transferGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"trusteeAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"trusteeFeeBps\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"vaultBuffer\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"weth\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"wethAssetIndex\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawalClaimDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawalQueueMetadata\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"queued\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"claimable\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"claimed\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"nextWithdrawalIndex\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"withdrawalRequests\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"withdrawer\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"claimed\",\"type\":\"bool\"},{\"internalType\":\"uint40\",\"name\":\"timestamp\",\"type\":\"uint40\"},{\"internalType\":\"uint128\",\"name\":\"amount\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"queued\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"author\":\"Origin Protocol Inc\",\"kind\":\"dev\",\"methods\":{\"addWithdrawalQueueLiquidity()\":{\"details\":\"is called from the Native Staking strategy when validator withdrawals are processed. It also called before any WETH is allocated to a strategy.\"},\"cacheWETHAssetIndex()\":{\"details\":\"Caches WETH's index in `allAssets` variable. Reduces gas usage by redeem by caching that.\"},\"checkBalance(address)\":{\"params\":{\"_asset\":\"Address of asset\"},\"returns\":{\"_0\":\"uint256 Balance of asset in decimals of asset\"}},\"claimWithdrawal(uint256)\":{\"params\":{\"_requestId\":\"Unique ID for the withdrawal request\"},\"returns\":{\"amount\":\"Amount of WETH transferred to the withdrawer\"}},\"claimWithdrawals(uint256[])\":{\"params\":{\"_requestIds\":\"Unique ID of each withdrawal request\"},\"returns\":{\"amounts\":\"Amount of WETH received for each request\",\"totalAmount\":\"Total amount of WETH transferred to the withdrawer\"}},\"getAssetConfig(address)\":{\"params\":{\"_asset\":\"Address of the token asset\"}},\"isSupportedAsset(address)\":{\"params\":{\"_asset\":\"address of the asset\"},\"returns\":{\"_0\":\"true if supported\"}},\"mint(address,uint256,uint256)\":{\"params\":{\"_amount\":\"Amount of the asset being deposited\",\"_asset\":\"Address of the asset being deposited\",\"_minimumOusdAmount\":\"Minimum OTokens to mint\"}},\"previewYield()\":{\"returns\":{\"yield\":\"amount of expected yield\"}},\"priceUnitMint(address)\":{\"params\":{\"asset\":\"address of the asset\"},\"returns\":{\"price\":\"uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed\"}},\"priceUnitRedeem(address)\":{\"params\":{\"asset\":\"Address of the asset\"},\"returns\":{\"price\":\"uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed\"}},\"redeem(uint256,uint256)\":{\"params\":{\"_amount\":\"Amount of OTokens to burn\",\"_minimumUnitAmount\":\"Minimum stablecoin units to receive in return\"}},\"requestWithdrawal(uint256)\":{\"params\":{\"_amount\":\"Amount of OETH to burn.\"},\"returns\":{\"queued\":\"Cumulative total of all WETH queued including already claimed requests.\",\"requestId\":\"Unique ID for the withdrawal request\"}},\"setAdminImpl(address)\":{\"params\":{\"newImpl\":\"address of the implementation\"}},\"totalValue()\":{\"returns\":{\"value\":\"Total value in USD/ETH (1e18)\"}},\"transferGovernance(address)\":{\"params\":{\"_newGovernor\":\"Address of the new Governor\"}}},\"title\":\"OETH VaultCore Contract\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"addWithdrawalQueueLiquidity()\":{\"notice\":\"Adds WETH to the withdrawal queue if there is a funding shortfall.\"},\"allocate()\":{\"notice\":\"Allocate unallocated funds on Vault to strategies.*\"},\"assetDefaultStrategies(address)\":{\"notice\":\"Mapping of asset address to the Strategy that they should automatically\"},\"autoAllocateThreshold()\":{\"notice\":\"OToken mints over this amount automatically allocate funds. 18 decimals.\"},\"calculateRedeemOutputs(uint256)\":{\"notice\":\"Calculate the outputs for a redeem function, i.e. the mix of coins that will be returned\"},\"capitalPaused()\":{\"notice\":\"pause operations that change the OToken supply. eg mint, redeem, allocate, mint/burn for strategy\"},\"checkBalance(address)\":{\"notice\":\"Get the balance of an asset held in Vault and all strategies.\"},\"claimGovernance()\":{\"notice\":\"Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor.\"},\"claimWithdrawal(uint256)\":{\"notice\":\"Claim a previously requested withdrawal once it is claimable. This request can be claimed once the withdrawal queue's `claimable` amount is greater than or equal this request's `queued` amount and 10 minutes has passed. If the requests is not claimable, the transaction will revert with `Queue pending liquidity`. If the request is not older than 10 minutes, the transaction will revert with `Claim delay not met`. OETH is converted to WETH at 1:1.\"},\"claimWithdrawals(uint256[])\":{\"notice\":\"Claim a previously requested withdrawals once they are claimable. This requests can be claimed once the withdrawal queue's `claimable` amount is greater than or equal each request's `queued` amount and 10 minutes has passed. If one of the requests is not claimable, the whole transaction will revert with `Queue pending liquidity`. If one of the requests is not older than 10 minutes, the whole transaction will revert with `Claim delay not met`.\"},\"dripDuration()\":{\"notice\":\"Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\"},\"getAllAssets()\":{\"notice\":\"Return all vault asset addresses in order\"},\"getAllStrategies()\":{\"notice\":\"Return the array of all strategies\"},\"getAssetConfig(address)\":{\"notice\":\"Gets the vault configuration of a supported asset.\"},\"getAssetCount()\":{\"notice\":\"Return the number of assets supported by the Vault.\"},\"getStrategyCount()\":{\"notice\":\"Return the number of strategies active on the Vault.\"},\"governor()\":{\"notice\":\"Returns the address of the current Governor.\"},\"isGovernor()\":{\"notice\":\"Returns true if the caller is the current Governor.\"},\"isSupportedAsset(address)\":{\"notice\":\"Returns whether the vault supports the asset\"},\"lastRebase()\":{\"notice\":\"Time in seconds that the vault last rebased yield.\"},\"maxSupplyDiff()\":{\"notice\":\"Max difference between total supply and total value of assets. 18 decimals.\"},\"mint(address,uint256,uint256)\":{\"notice\":\"Deposit a supported asset and mint OTokens.\"},\"netOusdMintForStrategyThreshold()\":{\"notice\":\"How much net total OTokens are allowed to be minted by all strategies\"},\"netOusdMintedForStrategy()\":{\"notice\":\"How much OTokens are currently minted by the strategy\"},\"ousdMetaStrategy()\":{\"notice\":\"Metapool strategy that is allowed to mint/burn OTokens without changing collateral\"},\"previewYield()\":{\"notice\":\"Calculates the amount that would rebase at next rebase. This is before any fees.\"},\"priceProvider()\":{\"notice\":\"Address of the Oracle price provider contract\"},\"priceUnitMint(address)\":{\"notice\":\"Returns the total price in 18 digit units for a given asset. Never goes above 1, since that is how we price mints.\"},\"priceUnitRedeem(address)\":{\"notice\":\"Returns the total price in 18 digit unit for a given asset. Never goes below 1, since that is how we price redeems\"},\"rebase()\":{\"notice\":\"Calculate the total value of assets held by the Vault and all strategies and update the supply of OTokens.\"},\"rebasePaused()\":{\"notice\":\"pause rebasing if true\"},\"rebasePerSecondMax()\":{\"notice\":\"max rebase percentage per second Can be used to set maximum yield of the protocol, spreading out yield over time\"},\"rebasePerSecondTarget()\":{\"notice\":\"target rebase rate limit, based on past rates and funds available.\"},\"rebaseThreshold()\":{\"notice\":\"OToken mints over this amount automatically rebase. 18 decimals.\"},\"redeem(uint256,uint256)\":{\"notice\":\"Withdraw a supported asset and burn OTokens.\"},\"redeemFeeBps()\":{\"notice\":\"Redemption fee in basis points. eg 50 = 0.5%\"},\"requestWithdrawal(uint256)\":{\"notice\":\"Request an asynchronous withdrawal of WETH in exchange for OETH. The OETH is burned on request and the WETH is transferred to the withdrawer on claim. This request can be claimed once the withdrawal queue's `claimable` amount is greater than or equal this request's `queued` amount. There is a minimum of 10 minutes before a request can be claimed. After that, the request just needs enough WETH liquidity in the Vault to satisfy all the outstanding requests to that point in the queue. OETH is converted to WETH at 1:1.\"},\"setAdminImpl(address)\":{\"notice\":\"set the implementation for the admin, this needs to be in a base class else we cannot set it\"},\"strategistAddr()\":{\"notice\":\"Address of the Strategist\"},\"totalValue()\":{\"notice\":\"Determine the total value of assets held by the vault and its strategies.\"},\"transferGovernance(address)\":{\"notice\":\"Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete\"},\"trusteeAddress()\":{\"notice\":\"Trustee contract that can collect a percentage of yield\"},\"trusteeFeeBps()\":{\"notice\":\"Amount of yield collected in basis points. eg 2000 = 20%\"},\"vaultBuffer()\":{\"notice\":\"Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\"},\"withdrawalClaimDelay()\":{\"notice\":\"Sets a minimum delay that is required to elapse between requesting async withdrawals and claiming the request. When set to 0 async withdrawals are disabled.\"},\"withdrawalQueueMetadata()\":{\"notice\":\"Global metadata for the withdrawal queue including: queued - cumulative total of all withdrawal requests included the ones that have already been claimed claimable - cumulative total of all the requests that can be claimed including the ones already claimed claimed - total of all the requests that have been claimed nextWithdrawalIndex - index of the next withdrawal request starting at 0\"},\"withdrawalRequests(uint256)\":{\"notice\":\"Mapping of withdrawal request indices to the user withdrawal request data\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/vault/OETHVaultCore.sol\":\"OETHVaultCore\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/token/ERC20/IERC20.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Interface of the ERC20 standard as defined in the EIP.\\n */\\ninterface IERC20 {\\n /**\\n * @dev Returns the amount of tokens in existence.\\n */\\n function totalSupply() external view returns (uint256);\\n\\n /**\\n * @dev Returns the amount of tokens owned by `account`.\\n */\\n function balanceOf(address account) external view returns (uint256);\\n\\n /**\\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transfer(address recipient, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Returns the remaining number of tokens that `spender` will be\\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\\n * zero by default.\\n *\\n * This value changes when {approve} or {transferFrom} are called.\\n */\\n function allowance(address owner, address spender) external view returns (uint256);\\n\\n /**\\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\\n * that someone may use both the old and the new allowance by unfortunate\\n * transaction ordering. One possible solution to mitigate this race\\n * condition is to first reduce the spender's allowance to 0 and set the\\n * desired value afterwards:\\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\\n *\\n * Emits an {Approval} event.\\n */\\n function approve(address spender, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\\n * allowance mechanism. `amount` is then deducted from the caller's\\n * allowance.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transferFrom(\\n address sender,\\n address recipient,\\n uint256 amount\\n ) external returns (bool);\\n\\n /**\\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\\n * another (`to`).\\n *\\n * Note that `value` may be zero.\\n */\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n\\n /**\\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n * a call to {approve}. `value` is the new allowance.\\n */\\n event Approval(address indexed owner, address indexed spender, uint256 value);\\n}\\n\",\"keccak256\":\"0x61437cb513a887a1bbad006e7b1c8b414478427d33de47c5600af3c748f108da\",\"license\":\"MIT\"},\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../IERC20.sol\\\";\\nimport \\\"../../../utils/Address.sol\\\";\\n\\n/**\\n * @title SafeERC20\\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\\n * contract returns false). Tokens that return no value (and instead revert or\\n * throw on failure) are also supported, non-reverting calls are assumed to be\\n * successful.\\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\\n */\\nlibrary SafeERC20 {\\n using Address for address;\\n\\n function safeTransfer(\\n IERC20 token,\\n address to,\\n uint256 value\\n ) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\\n }\\n\\n function safeTransferFrom(\\n IERC20 token,\\n address from,\\n address to,\\n uint256 value\\n ) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\\n }\\n\\n /**\\n * @dev Deprecated. This function has issues similar to the ones found in\\n * {IERC20-approve}, and its usage is discouraged.\\n *\\n * Whenever possible, use {safeIncreaseAllowance} and\\n * {safeDecreaseAllowance} instead.\\n */\\n function safeApprove(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n // safeApprove should only be called when setting an initial allowance,\\n // or when resetting it to zero. To increase and decrease it, use\\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\\n require(\\n (value == 0) || (token.allowance(address(this), spender) == 0),\\n \\\"SafeERC20: approve from non-zero to non-zero allowance\\\"\\n );\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\\n }\\n\\n function safeIncreaseAllowance(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n uint256 newAllowance = token.allowance(address(this), spender) + value;\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\\n }\\n\\n function safeDecreaseAllowance(\\n IERC20 token,\\n address spender,\\n uint256 value\\n ) internal {\\n unchecked {\\n uint256 oldAllowance = token.allowance(address(this), spender);\\n require(oldAllowance >= value, \\\"SafeERC20: decreased allowance below zero\\\");\\n uint256 newAllowance = oldAllowance - value;\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\\n }\\n }\\n\\n /**\\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\\n * on the return value: the return value is optional (but if data is returned, it must not be false).\\n * @param token The token targeted by the call.\\n * @param data The call data (encoded using abi.encode or one of its variants).\\n */\\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\\n // the target address contains contract code and also asserts for success in the low-level call.\\n\\n bytes memory returndata = address(token).functionCall(data, \\\"SafeERC20: low-level call failed\\\");\\n if (returndata.length > 0) {\\n // Return data is optional\\n require(abi.decode(returndata, (bool)), \\\"SafeERC20: ERC20 operation did not succeed\\\");\\n }\\n }\\n}\\n\",\"keccak256\":\"0xc3d946432c0ddbb1f846a0d3985be71299df331b91d06732152117f62f0be2b5\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize, which returns 0 for contracts in\\n // construction, since the code is only stored at the end of the\\n // constructor execution.\\n\\n uint256 size;\\n assembly {\\n size := extcodesize(account)\\n }\\n return size > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCall(target, data, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n require(isContract(target), \\\"Address: static call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(isContract(target), \\\"Address: delegate call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x51b758a8815ecc9596c66c37d56b1d33883a444631a3f916b9fe65cb863ef7c4\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/math/SafeCast.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\\n * checks.\\n *\\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\\n * easily result in undesired exploitation or bugs, since developers usually\\n * assume that overflows raise errors. `SafeCast` restores this intuition by\\n * reverting the transaction when such an operation overflows.\\n *\\n * Using this library instead of the unchecked operations eliminates an entire\\n * class of bugs, so it's recommended to use it always.\\n *\\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\\n * all math on `uint256` and `int256` and then downcasting.\\n */\\nlibrary SafeCast {\\n /**\\n * @dev Returns the downcasted uint224 from uint256, reverting on\\n * overflow (when the input is greater than largest uint224).\\n *\\n * Counterpart to Solidity's `uint224` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 224 bits\\n */\\n function toUint224(uint256 value) internal pure returns (uint224) {\\n require(value <= type(uint224).max, \\\"SafeCast: value doesn't fit in 224 bits\\\");\\n return uint224(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint128 from uint256, reverting on\\n * overflow (when the input is greater than largest uint128).\\n *\\n * Counterpart to Solidity's `uint128` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 128 bits\\n */\\n function toUint128(uint256 value) internal pure returns (uint128) {\\n require(value <= type(uint128).max, \\\"SafeCast: value doesn't fit in 128 bits\\\");\\n return uint128(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint96 from uint256, reverting on\\n * overflow (when the input is greater than largest uint96).\\n *\\n * Counterpart to Solidity's `uint96` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 96 bits\\n */\\n function toUint96(uint256 value) internal pure returns (uint96) {\\n require(value <= type(uint96).max, \\\"SafeCast: value doesn't fit in 96 bits\\\");\\n return uint96(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint64 from uint256, reverting on\\n * overflow (when the input is greater than largest uint64).\\n *\\n * Counterpart to Solidity's `uint64` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 64 bits\\n */\\n function toUint64(uint256 value) internal pure returns (uint64) {\\n require(value <= type(uint64).max, \\\"SafeCast: value doesn't fit in 64 bits\\\");\\n return uint64(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint32 from uint256, reverting on\\n * overflow (when the input is greater than largest uint32).\\n *\\n * Counterpart to Solidity's `uint32` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 32 bits\\n */\\n function toUint32(uint256 value) internal pure returns (uint32) {\\n require(value <= type(uint32).max, \\\"SafeCast: value doesn't fit in 32 bits\\\");\\n return uint32(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint16 from uint256, reverting on\\n * overflow (when the input is greater than largest uint16).\\n *\\n * Counterpart to Solidity's `uint16` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 16 bits\\n */\\n function toUint16(uint256 value) internal pure returns (uint16) {\\n require(value <= type(uint16).max, \\\"SafeCast: value doesn't fit in 16 bits\\\");\\n return uint16(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted uint8 from uint256, reverting on\\n * overflow (when the input is greater than largest uint8).\\n *\\n * Counterpart to Solidity's `uint8` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 8 bits.\\n */\\n function toUint8(uint256 value) internal pure returns (uint8) {\\n require(value <= type(uint8).max, \\\"SafeCast: value doesn't fit in 8 bits\\\");\\n return uint8(value);\\n }\\n\\n /**\\n * @dev Converts a signed int256 into an unsigned uint256.\\n *\\n * Requirements:\\n *\\n * - input must be greater than or equal to 0.\\n */\\n function toUint256(int256 value) internal pure returns (uint256) {\\n require(value >= 0, \\\"SafeCast: value must be positive\\\");\\n return uint256(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int128 from int256, reverting on\\n * overflow (when the input is less than smallest int128 or\\n * greater than largest int128).\\n *\\n * Counterpart to Solidity's `int128` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 128 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt128(int256 value) internal pure returns (int128) {\\n require(value >= type(int128).min && value <= type(int128).max, \\\"SafeCast: value doesn't fit in 128 bits\\\");\\n return int128(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int64 from int256, reverting on\\n * overflow (when the input is less than smallest int64 or\\n * greater than largest int64).\\n *\\n * Counterpart to Solidity's `int64` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 64 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt64(int256 value) internal pure returns (int64) {\\n require(value >= type(int64).min && value <= type(int64).max, \\\"SafeCast: value doesn't fit in 64 bits\\\");\\n return int64(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int32 from int256, reverting on\\n * overflow (when the input is less than smallest int32 or\\n * greater than largest int32).\\n *\\n * Counterpart to Solidity's `int32` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 32 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt32(int256 value) internal pure returns (int32) {\\n require(value >= type(int32).min && value <= type(int32).max, \\\"SafeCast: value doesn't fit in 32 bits\\\");\\n return int32(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int16 from int256, reverting on\\n * overflow (when the input is less than smallest int16 or\\n * greater than largest int16).\\n *\\n * Counterpart to Solidity's `int16` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 16 bits\\n *\\n * _Available since v3.1._\\n */\\n function toInt16(int256 value) internal pure returns (int16) {\\n require(value >= type(int16).min && value <= type(int16).max, \\\"SafeCast: value doesn't fit in 16 bits\\\");\\n return int16(value);\\n }\\n\\n /**\\n * @dev Returns the downcasted int8 from int256, reverting on\\n * overflow (when the input is less than smallest int8 or\\n * greater than largest int8).\\n *\\n * Counterpart to Solidity's `int8` operator.\\n *\\n * Requirements:\\n *\\n * - input must fit into 8 bits.\\n *\\n * _Available since v3.1._\\n */\\n function toInt8(int256 value) internal pure returns (int8) {\\n require(value >= type(int8).min && value <= type(int8).max, \\\"SafeCast: value doesn't fit in 8 bits\\\");\\n return int8(value);\\n }\\n\\n /**\\n * @dev Converts an unsigned uint256 into a signed int256.\\n *\\n * Requirements:\\n *\\n * - input must be less than or equal to maxInt256.\\n */\\n function toInt256(uint256 value) internal pure returns (int256) {\\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\\n require(value <= uint256(type(int256).max), \\\"SafeCast: value doesn't fit in an int256\\\");\\n return int256(value);\\n }\\n}\\n\",\"keccak256\":\"0x5c6caab697d302ad7eb59c234a4d2dbc965c1bae87709bd2850060b7695b28c7\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/math/SafeMath.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)\\n\\npragma solidity ^0.8.0;\\n\\n// CAUTION\\n// This version of SafeMath should only be used with Solidity 0.8 or later,\\n// because it relies on the compiler's built in overflow checks.\\n\\n/**\\n * @dev Wrappers over Solidity's arithmetic operations.\\n *\\n * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler\\n * now has built in overflow checking.\\n */\\nlibrary SafeMath {\\n /**\\n * @dev Returns the addition of two unsigned integers, with an overflow flag.\\n *\\n * _Available since v3.4._\\n */\\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\\n unchecked {\\n uint256 c = a + b;\\n if (c < a) return (false, 0);\\n return (true, c);\\n }\\n }\\n\\n /**\\n * @dev Returns the substraction of two unsigned integers, with an overflow flag.\\n *\\n * _Available since v3.4._\\n */\\n function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\\n unchecked {\\n if (b > a) return (false, 0);\\n return (true, a - b);\\n }\\n }\\n\\n /**\\n * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\\n *\\n * _Available since v3.4._\\n */\\n function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\\n unchecked {\\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\\n // benefit is lost if 'b' is also tested.\\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\\n if (a == 0) return (true, 0);\\n uint256 c = a * b;\\n if (c / a != b) return (false, 0);\\n return (true, c);\\n }\\n }\\n\\n /**\\n * @dev Returns the division of two unsigned integers, with a division by zero flag.\\n *\\n * _Available since v3.4._\\n */\\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\\n unchecked {\\n if (b == 0) return (false, 0);\\n return (true, a / b);\\n }\\n }\\n\\n /**\\n * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\\n *\\n * _Available since v3.4._\\n */\\n function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\\n unchecked {\\n if (b == 0) return (false, 0);\\n return (true, a % b);\\n }\\n }\\n\\n /**\\n * @dev Returns the addition of two unsigned integers, reverting on\\n * overflow.\\n *\\n * Counterpart to Solidity's `+` operator.\\n *\\n * Requirements:\\n *\\n * - Addition cannot overflow.\\n */\\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a + b;\\n }\\n\\n /**\\n * @dev Returns the subtraction of two unsigned integers, reverting on\\n * overflow (when the result is negative).\\n *\\n * Counterpart to Solidity's `-` operator.\\n *\\n * Requirements:\\n *\\n * - Subtraction cannot overflow.\\n */\\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a - b;\\n }\\n\\n /**\\n * @dev Returns the multiplication of two unsigned integers, reverting on\\n * overflow.\\n *\\n * Counterpart to Solidity's `*` operator.\\n *\\n * Requirements:\\n *\\n * - Multiplication cannot overflow.\\n */\\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a * b;\\n }\\n\\n /**\\n * @dev Returns the integer division of two unsigned integers, reverting on\\n * division by zero. The result is rounded towards zero.\\n *\\n * Counterpart to Solidity's `/` operator.\\n *\\n * Requirements:\\n *\\n * - The divisor cannot be zero.\\n */\\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a / b;\\n }\\n\\n /**\\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\\n * reverting when dividing by zero.\\n *\\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\\n * opcode (which leaves remaining gas untouched) while Solidity uses an\\n * invalid opcode to revert (consuming all remaining gas).\\n *\\n * Requirements:\\n *\\n * - The divisor cannot be zero.\\n */\\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a % b;\\n }\\n\\n /**\\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\\n * overflow (when the result is negative).\\n *\\n * CAUTION: This function is deprecated because it requires allocating memory for the error\\n * message unnecessarily. For custom revert reasons use {trySub}.\\n *\\n * Counterpart to Solidity's `-` operator.\\n *\\n * Requirements:\\n *\\n * - Subtraction cannot overflow.\\n */\\n function sub(\\n uint256 a,\\n uint256 b,\\n string memory errorMessage\\n ) internal pure returns (uint256) {\\n unchecked {\\n require(b <= a, errorMessage);\\n return a - b;\\n }\\n }\\n\\n /**\\n * @dev Returns the integer division of two unsigned integers, reverting with custom message on\\n * division by zero. The result is rounded towards zero.\\n *\\n * Counterpart to Solidity's `/` operator. Note: this function uses a\\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\\n * uses an invalid opcode to revert (consuming all remaining gas).\\n *\\n * Requirements:\\n *\\n * - The divisor cannot be zero.\\n */\\n function div(\\n uint256 a,\\n uint256 b,\\n string memory errorMessage\\n ) internal pure returns (uint256) {\\n unchecked {\\n require(b > 0, errorMessage);\\n return a / b;\\n }\\n }\\n\\n /**\\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\\n * reverting with custom message when dividing by zero.\\n *\\n * CAUTION: This function is deprecated because it requires allocating memory for the error\\n * message unnecessarily. For custom revert reasons use {tryMod}.\\n *\\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\\n * opcode (which leaves remaining gas untouched) while Solidity uses an\\n * invalid opcode to revert (consuming all remaining gas).\\n *\\n * Requirements:\\n *\\n * - The divisor cannot be zero.\\n */\\n function mod(\\n uint256 a,\\n uint256 b,\\n string memory errorMessage\\n ) internal pure returns (uint256) {\\n unchecked {\\n require(b > 0, errorMessage);\\n return a % b;\\n }\\n }\\n}\\n\",\"keccak256\":\"0xa2f576be637946f767aa56601c26d717f48a0aff44f82e46f13807eea1009a21\",\"license\":\"MIT\"},\"contracts/governance/Governable.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\\n * from owner to governor and renounce methods removed. Does not use\\n * Context.sol like Ownable.sol does for simplification.\\n * @author Origin Protocol Inc\\n */\\nabstract contract Governable {\\n // Storage position of the owner and pendingOwner of the contract\\n // keccak256(\\\"OUSD.governor\\\");\\n bytes32 private constant governorPosition =\\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\\n\\n // keccak256(\\\"OUSD.pending.governor\\\");\\n bytes32 private constant pendingGovernorPosition =\\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\\n\\n // keccak256(\\\"OUSD.reentry.status\\\");\\n bytes32 private constant reentryStatusPosition =\\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\\n\\n // See OpenZeppelin ReentrancyGuard implementation\\n uint256 constant _NOT_ENTERED = 1;\\n uint256 constant _ENTERED = 2;\\n\\n event PendingGovernorshipTransfer(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n event GovernorshipTransferred(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n /**\\n * @notice Returns the address of the current Governor.\\n */\\n function governor() public view returns (address) {\\n return _governor();\\n }\\n\\n /**\\n * @dev Returns the address of the current Governor.\\n */\\n function _governor() internal view returns (address governorOut) {\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n governorOut := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Returns the address of the pending Governor.\\n */\\n function _pendingGovernor()\\n internal\\n view\\n returns (address pendingGovernor)\\n {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n pendingGovernor := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the Governor.\\n */\\n modifier onlyGovernor() {\\n require(isGovernor(), \\\"Caller is not the Governor\\\");\\n _;\\n }\\n\\n /**\\n * @notice Returns true if the caller is the current Governor.\\n */\\n function isGovernor() public view returns (bool) {\\n return msg.sender == _governor();\\n }\\n\\n function _setGovernor(address newGovernor) internal {\\n emit GovernorshipTransferred(_governor(), newGovernor);\\n\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @dev Prevents a contract from calling itself, directly or indirectly.\\n * Calling a `nonReentrant` function from another `nonReentrant`\\n * function is not supported. It is possible to prevent this from happening\\n * by making the `nonReentrant` function external, and make it call a\\n * `private` function that does the actual work.\\n */\\n modifier nonReentrant() {\\n bytes32 position = reentryStatusPosition;\\n uint256 _reentry_status;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n _reentry_status := sload(position)\\n }\\n\\n // On the first call to nonReentrant, _notEntered will be true\\n require(_reentry_status != _ENTERED, \\\"Reentrant call\\\");\\n\\n // Any calls to nonReentrant after this point will fail\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _ENTERED)\\n }\\n\\n _;\\n\\n // By storing the original value once again, a refund is triggered (see\\n // https://eips.ethereum.org/EIPS/eip-2200)\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _NOT_ENTERED)\\n }\\n }\\n\\n function _setPendingGovernor(address newGovernor) internal {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the current Governor. Must be claimed for this to complete\\n * @param _newGovernor Address of the new Governor\\n */\\n function transferGovernance(address _newGovernor) external onlyGovernor {\\n _setPendingGovernor(_newGovernor);\\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\\n }\\n\\n /**\\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the new Governor.\\n */\\n function claimGovernance() external {\\n require(\\n msg.sender == _pendingGovernor(),\\n \\\"Only the pending Governor can complete the claim\\\"\\n );\\n _changeGovernor(msg.sender);\\n }\\n\\n /**\\n * @dev Change Governance of the contract to a new account (`newGovernor`).\\n * @param _newGovernor Address of the new Governor\\n */\\n function _changeGovernor(address _newGovernor) internal {\\n require(_newGovernor != address(0), \\\"New Governor is address(0)\\\");\\n _setGovernor(_newGovernor);\\n }\\n}\\n\",\"keccak256\":\"0xf32f873c8bfbacf2e5f01d0cf37bc7f54fbd5aa656e95c8a599114229946f107\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IBasicToken.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\ninterface IBasicToken {\\n function symbol() external view returns (string memory);\\n\\n function decimals() external view returns (uint8);\\n}\\n\",\"keccak256\":\"0xa562062698aa12572123b36dfd2072f1a39e44fed2031cc19c2c9fd522f96ec2\",\"license\":\"MIT\"},\"contracts/interfaces/IGetExchangeRateToken.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\ninterface IGetExchangeRateToken {\\n function getExchangeRate() external view returns (uint256 _exchangeRate);\\n}\\n\",\"keccak256\":\"0x243be4dffe1eb453d25cae22b6c172bb64c574d80943cef058fdc30a4b9c9bfd\",\"license\":\"MIT\"},\"contracts/interfaces/IOracle.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\ninterface IOracle {\\n /**\\n * @dev returns the asset price in USD, in 8 decimal digits.\\n *\\n * The version of priceProvider deployed for OETH has 18 decimal digits\\n */\\n function price(address asset) external view returns (uint256);\\n}\\n\",\"keccak256\":\"0xa5f765f5b22cd5426803b22a7344d4c34c4d4016a0b6e9d799862133253f77b2\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IStrategy.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\\n */\\ninterface IStrategy {\\n /**\\n * @dev Deposit the given asset to platform\\n * @param _asset asset address\\n * @param _amount Amount to deposit\\n */\\n function deposit(address _asset, uint256 _amount) external;\\n\\n /**\\n * @dev Deposit the entire balance of all supported assets in the Strategy\\n * to the platform\\n */\\n function depositAll() external;\\n\\n /**\\n * @dev Withdraw given asset from Lending platform\\n */\\n function withdraw(\\n address _recipient,\\n address _asset,\\n uint256 _amount\\n ) external;\\n\\n /**\\n * @dev Liquidate all assets in strategy and return them to Vault.\\n */\\n function withdrawAll() external;\\n\\n /**\\n * @dev Returns the current balance of the given asset.\\n */\\n function checkBalance(address _asset)\\n external\\n view\\n returns (uint256 balance);\\n\\n /**\\n * @dev Returns bool indicating whether strategy supports asset.\\n */\\n function supportsAsset(address _asset) external view returns (bool);\\n\\n /**\\n * @dev Collect reward tokens from the Strategy.\\n */\\n function collectRewardTokens() external;\\n\\n /**\\n * @dev The address array of the reward tokens for the Strategy.\\n */\\n function getRewardTokenAddresses() external view returns (address[] memory);\\n\\n function harvesterAddress() external view returns (address);\\n\\n function transferToken(address token, uint256 amount) external;\\n\\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\\n external;\\n}\\n\",\"keccak256\":\"0x79ca47defb3b5a56bba13f14c440838152fd1c1aa640476154516a16da4da8ba\",\"license\":\"BUSL-1.1\"},\"contracts/interfaces/IVault.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { VaultStorage } from \\\"../vault/VaultStorage.sol\\\";\\n\\ninterface IVault {\\n // slither-disable-start constable-states\\n\\n event AssetSupported(address _asset);\\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\\n event StrategyApproved(address _addr);\\n event StrategyRemoved(address _addr);\\n event Mint(address _addr, uint256 _value);\\n event Redeem(address _addr, uint256 _value);\\n event CapitalPaused();\\n event CapitalUnpaused();\\n event RebasePaused();\\n event RebaseUnpaused();\\n event VaultBufferUpdated(uint256 _vaultBuffer);\\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\\n event PriceProviderUpdated(address _priceProvider);\\n event AllocateThresholdUpdated(uint256 _threshold);\\n event RebaseThresholdUpdated(uint256 _threshold);\\n event StrategistUpdated(address _address);\\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\\n event TrusteeFeeBpsChanged(uint256 _basis);\\n event TrusteeAddressChanged(address _address);\\n event SwapperChanged(address _address);\\n event SwapAllowedUndervalueChanged(uint256 _basis);\\n event SwapSlippageChanged(address _asset, uint256 _basis);\\n event Swapped(\\n address indexed _fromAsset,\\n address indexed _toAsset,\\n uint256 _fromAssetAmount,\\n uint256 _toAssetAmount\\n );\\n event StrategyAddedToMintWhitelist(address indexed strategy);\\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\\n event DripperChanged(address indexed _dripper);\\n event WithdrawalRequested(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount,\\n uint256 _queued\\n );\\n event WithdrawalClaimed(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount\\n );\\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\\n\\n // Governable.sol\\n function transferGovernance(address _newGovernor) external;\\n\\n function claimGovernance() external;\\n\\n function governor() external view returns (address);\\n\\n function ADMIN_IMPLEMENTATION() external view returns (address);\\n\\n // VaultAdmin.sol\\n function setPriceProvider(address _priceProvider) external;\\n\\n function priceProvider() external view returns (address);\\n\\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\\n\\n function redeemFeeBps() external view returns (uint256);\\n\\n function setVaultBuffer(uint256 _vaultBuffer) external;\\n\\n function vaultBuffer() external view returns (uint256);\\n\\n function setAutoAllocateThreshold(uint256 _threshold) external;\\n\\n function autoAllocateThreshold() external view returns (uint256);\\n\\n function setRebaseThreshold(uint256 _threshold) external;\\n\\n function rebaseThreshold() external view returns (uint256);\\n\\n function setStrategistAddr(address _address) external;\\n\\n function strategistAddr() external view returns (address);\\n\\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\\n\\n function maxSupplyDiff() external view returns (uint256);\\n\\n function setTrusteeAddress(address _address) external;\\n\\n function trusteeAddress() external view returns (address);\\n\\n function setTrusteeFeeBps(uint256 _basis) external;\\n\\n function trusteeFeeBps() external view returns (uint256);\\n\\n function ousdMetaStrategy() external view returns (address);\\n\\n function setSwapper(address _swapperAddr) external;\\n\\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\\n\\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\\n external;\\n\\n function supportAsset(address _asset, uint8 _unitConversion) external;\\n\\n function approveStrategy(address _addr) external;\\n\\n function removeStrategy(address _addr) external;\\n\\n function setAssetDefaultStrategy(address _asset, address _strategy)\\n external;\\n\\n function assetDefaultStrategies(address _asset)\\n external\\n view\\n returns (address);\\n\\n function pauseRebase() external;\\n\\n function unpauseRebase() external;\\n\\n function rebasePaused() external view returns (bool);\\n\\n function pauseCapital() external;\\n\\n function unpauseCapital() external;\\n\\n function capitalPaused() external view returns (bool);\\n\\n function transferToken(address _asset, uint256 _amount) external;\\n\\n function priceUnitMint(address asset) external view returns (uint256);\\n\\n function priceUnitRedeem(address asset) external view returns (uint256);\\n\\n function withdrawAllFromStrategy(address _strategyAddr) external;\\n\\n function withdrawAllFromStrategies() external;\\n\\n function withdrawFromStrategy(\\n address _strategyFromAddress,\\n address[] calldata _assets,\\n uint256[] calldata _amounts\\n ) external;\\n\\n function depositToStrategy(\\n address _strategyToAddress,\\n address[] calldata _assets,\\n uint256[] calldata _amounts\\n ) external;\\n\\n // VaultCore.sol\\n function mint(\\n address _asset,\\n uint256 _amount,\\n uint256 _minimumOusdAmount\\n ) external;\\n\\n function mintForStrategy(uint256 _amount) external;\\n\\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\\n\\n function burnForStrategy(uint256 _amount) external;\\n\\n function allocate() external;\\n\\n function rebase() external;\\n\\n function swapCollateral(\\n address fromAsset,\\n address toAsset,\\n uint256 fromAssetAmount,\\n uint256 minToAssetAmount,\\n bytes calldata data\\n ) external returns (uint256 toAssetAmount);\\n\\n function totalValue() external view returns (uint256 value);\\n\\n function checkBalance(address _asset) external view returns (uint256);\\n\\n function calculateRedeemOutputs(uint256 _amount)\\n external\\n view\\n returns (uint256[] memory);\\n\\n function getAssetCount() external view returns (uint256);\\n\\n function getAssetConfig(address _asset)\\n external\\n view\\n returns (VaultStorage.Asset memory config);\\n\\n function getAllAssets() external view returns (address[] memory);\\n\\n function getStrategyCount() external view returns (uint256);\\n\\n function swapper() external view returns (address);\\n\\n function allowedSwapUndervalue() external view returns (uint256);\\n\\n function getAllStrategies() external view returns (address[] memory);\\n\\n function isSupportedAsset(address _asset) external view returns (bool);\\n\\n function netOusdMintForStrategyThreshold() external view returns (uint256);\\n\\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\\n\\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\\n\\n function netOusdMintedForStrategy() external view returns (int256);\\n\\n function setDripper(address _dripper) external;\\n\\n function dripper() external view returns (address);\\n\\n function weth() external view returns (address);\\n\\n function cacheWETHAssetIndex() external;\\n\\n function wethAssetIndex() external view returns (uint256);\\n\\n function initialize(address, address) external;\\n\\n function setAdminImpl(address) external;\\n\\n function removeAsset(address _asset) external;\\n\\n // These are OETH specific functions\\n function addWithdrawalQueueLiquidity() external;\\n\\n function requestWithdrawal(uint256 _amount)\\n external\\n returns (uint256 requestId, uint256 queued);\\n\\n function claimWithdrawal(uint256 requestId)\\n external\\n returns (uint256 amount);\\n\\n function claimWithdrawals(uint256[] memory requestIds)\\n external\\n returns (uint256[] memory amounts, uint256 totalAmount);\\n\\n function withdrawalQueueMetadata()\\n external\\n view\\n returns (VaultStorage.WithdrawalQueueMetadata memory);\\n\\n function withdrawalRequests(uint256 requestId)\\n external\\n view\\n returns (VaultStorage.WithdrawalRequest memory);\\n\\n // OETHb specific functions\\n function addStrategyToMintWhitelist(address strategyAddr) external;\\n\\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\\n\\n function isMintWhitelistedStrategy(address strategyAddr)\\n external\\n view\\n returns (bool);\\n\\n function withdrawalClaimDelay() external view returns (uint256);\\n\\n function setWithdrawalClaimDelay(uint256 newDelay) external;\\n\\n function lastRebase() external view returns (uint64);\\n\\n function dripDuration() external view returns (uint64);\\n\\n function setDripDuration(uint256 _dripDuration) external;\\n\\n function rebasePerSecondMax() external view returns (uint64);\\n\\n function setRebaseRateMax(uint256 yearlyApr) external;\\n\\n function rebasePerSecondTarget() external view returns (uint64);\\n\\n function previewYield() external view returns (uint256 yield);\\n\\n // slither-disable-end constable-states\\n}\\n\",\"keccak256\":\"0x8d0a60f594d97578b0513b4da3d8fcafaa601950c6c4c016bf60b1344733269c\",\"license\":\"BUSL-1.1\"},\"contracts/token/OUSD.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title OUSD Token Contract\\n * @dev ERC20 compatible contract for OUSD\\n * @dev Implements an elastic supply\\n * @author Origin Protocol Inc\\n */\\nimport { IVault } from \\\"../interfaces/IVault.sol\\\";\\nimport { Governable } from \\\"../governance/Governable.sol\\\";\\nimport { SafeCast } from \\\"@openzeppelin/contracts/utils/math/SafeCast.sol\\\";\\n\\ncontract OUSD is Governable {\\n using SafeCast for int256;\\n using SafeCast for uint256;\\n\\n /// @dev Event triggered when the supply changes\\n /// @param totalSupply Updated token total supply\\n /// @param rebasingCredits Updated token rebasing credits\\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\\n event TotalSupplyUpdatedHighres(\\n uint256 totalSupply,\\n uint256 rebasingCredits,\\n uint256 rebasingCreditsPerToken\\n );\\n /// @dev Event triggered when an account opts in for rebasing\\n /// @param account Address of the account\\n event AccountRebasingEnabled(address account);\\n /// @dev Event triggered when an account opts out of rebasing\\n /// @param account Address of the account\\n event AccountRebasingDisabled(address account);\\n /// @dev Emitted when `value` tokens are moved from one account `from` to\\n /// another `to`.\\n /// @param from Address of the account tokens are moved from\\n /// @param to Address of the account tokens are moved to\\n /// @param value Amount of tokens transferred\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n /// a call to {approve}. `value` is the new allowance.\\n /// @param owner Address of the owner approving allowance\\n /// @param spender Address of the spender allowance is granted to\\n /// @param value Amount of tokens spender can transfer\\n event Approval(\\n address indexed owner,\\n address indexed spender,\\n uint256 value\\n );\\n /// @dev Yield resulting from {changeSupply} that a `source` account would\\n /// receive is directed to `target` account.\\n /// @param source Address of the source forwarding the yield\\n /// @param target Address of the target receiving the yield\\n event YieldDelegated(address source, address target);\\n /// @dev Yield delegation from `source` account to the `target` account is\\n /// suspended.\\n /// @param source Address of the source suspending yield forwarding\\n /// @param target Address of the target no longer receiving yield from `source`\\n /// account\\n event YieldUndelegated(address source, address target);\\n\\n enum RebaseOptions {\\n NotSet,\\n StdNonRebasing,\\n StdRebasing,\\n YieldDelegationSource,\\n YieldDelegationTarget\\n }\\n\\n uint256[154] private _gap; // Slots to align with deployed contract\\n uint256 private constant MAX_SUPPLY = type(uint128).max;\\n /// @dev The amount of tokens in existence\\n uint256 public totalSupply;\\n mapping(address => mapping(address => uint256)) private allowances;\\n /// @dev The vault with privileges to execute {mint}, {burn}\\n /// and {changeSupply}\\n address public vaultAddress;\\n mapping(address => uint256) internal creditBalances;\\n // the 2 storage variables below need trailing underscores to not name collide with public functions\\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\\n uint256 private rebasingCreditsPerToken_;\\n /// @dev The amount of tokens that are not rebasing - receiving yield\\n uint256 public nonRebasingSupply;\\n mapping(address => uint256) internal alternativeCreditsPerToken;\\n /// @dev A map of all addresses and their respective RebaseOptions\\n mapping(address => RebaseOptions) public rebaseState;\\n mapping(address => uint256) private __deprecated_isUpgraded;\\n /// @dev A map of addresses that have yields forwarded to. This is an\\n /// inverse mapping of {yieldFrom}\\n /// Key Account forwarding yield\\n /// Value Account receiving yield\\n mapping(address => address) public yieldTo;\\n /// @dev A map of addresses that are receiving the yield. This is an\\n /// inverse mapping of {yieldTo}\\n /// Key Account receiving yield\\n /// Value Account forwarding yield\\n mapping(address => address) public yieldFrom;\\n\\n uint256 private constant RESOLUTION_INCREASE = 1e9;\\n uint256[34] private __gap; // including below gap totals up to 200\\n\\n /// @dev Verifies that the caller is the Governor or Strategist.\\n modifier onlyGovernorOrStrategist() {\\n require(\\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\\n \\\"Caller is not the Strategist or Governor\\\"\\n );\\n _;\\n }\\n\\n /// @dev Initializes the contract and sets necessary variables.\\n /// @param _vaultAddress Address of the vault contract\\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\\n external\\n onlyGovernor\\n {\\n require(_vaultAddress != address(0), \\\"Zero vault address\\\");\\n require(vaultAddress == address(0), \\\"Already initialized\\\");\\n\\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\\n vaultAddress = _vaultAddress;\\n }\\n\\n /// @dev Returns the symbol of the token, a shorter version\\n /// of the name.\\n function symbol() external pure virtual returns (string memory) {\\n return \\\"OUSD\\\";\\n }\\n\\n /// @dev Returns the name of the token.\\n function name() external pure virtual returns (string memory) {\\n return \\\"Origin Dollar\\\";\\n }\\n\\n /// @dev Returns the number of decimals used to get its user representation.\\n function decimals() external pure virtual returns (uint8) {\\n return 18;\\n }\\n\\n /**\\n * @dev Verifies that the caller is the Vault contract\\n */\\n modifier onlyVault() {\\n require(vaultAddress == msg.sender, \\\"Caller is not the Vault\\\");\\n _;\\n }\\n\\n /**\\n * @return High resolution rebasingCreditsPerToken\\n */\\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\\n return rebasingCreditsPerToken_;\\n }\\n\\n /**\\n * @return Low resolution rebasingCreditsPerToken\\n */\\n function rebasingCreditsPerToken() external view returns (uint256) {\\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\\n }\\n\\n /**\\n * @return High resolution total number of rebasing credits\\n */\\n function rebasingCreditsHighres() external view returns (uint256) {\\n return rebasingCredits_;\\n }\\n\\n /**\\n * @return Low resolution total number of rebasing credits\\n */\\n function rebasingCredits() external view returns (uint256) {\\n return rebasingCredits_ / RESOLUTION_INCREASE;\\n }\\n\\n /**\\n * @notice Gets the balance of the specified address.\\n * @param _account Address to query the balance of.\\n * @return A uint256 representing the amount of base units owned by the\\n * specified address.\\n */\\n function balanceOf(address _account) public view returns (uint256) {\\n RebaseOptions state = rebaseState[_account];\\n if (state == RebaseOptions.YieldDelegationSource) {\\n // Saves a slot read when transferring to or from a yield delegating source\\n // since we know creditBalances equals the balance.\\n return creditBalances[_account];\\n }\\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\\n _creditsPerToken(_account);\\n if (state == RebaseOptions.YieldDelegationTarget) {\\n // creditBalances of yieldFrom accounts equals token balances\\n return baseBalance - creditBalances[yieldFrom[_account]];\\n }\\n return baseBalance;\\n }\\n\\n /**\\n * @notice Gets the credits balance of the specified address.\\n * @dev Backwards compatible with old low res credits per token.\\n * @param _account The address to query the balance of.\\n * @return (uint256, uint256) Credit balance and credits per token of the\\n * address\\n */\\n function creditsBalanceOf(address _account)\\n external\\n view\\n returns (uint256, uint256)\\n {\\n uint256 cpt = _creditsPerToken(_account);\\n if (cpt == 1e27) {\\n // For a period before the resolution upgrade, we created all new\\n // contract accounts at high resolution. Since they are not changing\\n // as a result of this upgrade, we will return their true values\\n return (creditBalances[_account], cpt);\\n } else {\\n return (\\n creditBalances[_account] / RESOLUTION_INCREASE,\\n cpt / RESOLUTION_INCREASE\\n );\\n }\\n }\\n\\n /**\\n * @notice Gets the credits balance of the specified address.\\n * @param _account The address to query the balance of.\\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\\n * address, and isUpgraded\\n */\\n function creditsBalanceOfHighres(address _account)\\n external\\n view\\n returns (\\n uint256,\\n uint256,\\n bool\\n )\\n {\\n return (\\n creditBalances[_account],\\n _creditsPerToken(_account),\\n true // all accounts have their resolution \\\"upgraded\\\"\\n );\\n }\\n\\n // Backwards compatible view\\n function nonRebasingCreditsPerToken(address _account)\\n external\\n view\\n returns (uint256)\\n {\\n return alternativeCreditsPerToken[_account];\\n }\\n\\n /**\\n * @notice Transfer tokens to a specified address.\\n * @param _to the address to transfer to.\\n * @param _value the amount to be transferred.\\n * @return true on success.\\n */\\n function transfer(address _to, uint256 _value) external returns (bool) {\\n require(_to != address(0), \\\"Transfer to zero address\\\");\\n\\n _executeTransfer(msg.sender, _to, _value);\\n\\n emit Transfer(msg.sender, _to, _value);\\n return true;\\n }\\n\\n /**\\n * @notice Transfer tokens from one address to another.\\n * @param _from The address you want to send tokens from.\\n * @param _to The address you want to transfer to.\\n * @param _value The amount of tokens to be transferred.\\n * @return true on success.\\n */\\n function transferFrom(\\n address _from,\\n address _to,\\n uint256 _value\\n ) external returns (bool) {\\n require(_to != address(0), \\\"Transfer to zero address\\\");\\n uint256 userAllowance = allowances[_from][msg.sender];\\n require(_value <= userAllowance, \\\"Allowance exceeded\\\");\\n\\n unchecked {\\n allowances[_from][msg.sender] = userAllowance - _value;\\n }\\n\\n _executeTransfer(_from, _to, _value);\\n\\n emit Transfer(_from, _to, _value);\\n return true;\\n }\\n\\n function _executeTransfer(\\n address _from,\\n address _to,\\n uint256 _value\\n ) internal {\\n (\\n int256 fromRebasingCreditsDiff,\\n int256 fromNonRebasingSupplyDiff\\n ) = _adjustAccount(_from, -_value.toInt256());\\n (\\n int256 toRebasingCreditsDiff,\\n int256 toNonRebasingSupplyDiff\\n ) = _adjustAccount(_to, _value.toInt256());\\n\\n _adjustGlobals(\\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\\n );\\n }\\n\\n function _adjustAccount(address _account, int256 _balanceChange)\\n internal\\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\\n {\\n RebaseOptions state = rebaseState[_account];\\n int256 currentBalance = balanceOf(_account).toInt256();\\n if (currentBalance + _balanceChange < 0) {\\n revert(\\\"Transfer amount exceeds balance\\\");\\n }\\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\\n\\n if (state == RebaseOptions.YieldDelegationSource) {\\n address target = yieldTo[_account];\\n uint256 targetOldBalance = balanceOf(target);\\n uint256 targetNewCredits = _balanceToRebasingCredits(\\n targetOldBalance + newBalance\\n );\\n rebasingCreditsDiff =\\n targetNewCredits.toInt256() -\\n creditBalances[target].toInt256();\\n\\n creditBalances[_account] = newBalance;\\n creditBalances[target] = targetNewCredits;\\n } else if (state == RebaseOptions.YieldDelegationTarget) {\\n uint256 newCredits = _balanceToRebasingCredits(\\n newBalance + creditBalances[yieldFrom[_account]]\\n );\\n rebasingCreditsDiff =\\n newCredits.toInt256() -\\n creditBalances[_account].toInt256();\\n creditBalances[_account] = newCredits;\\n } else {\\n _autoMigrate(_account);\\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\\n _account\\n ];\\n if (alternativeCreditsPerTokenMem > 0) {\\n nonRebasingSupplyDiff = _balanceChange;\\n if (alternativeCreditsPerTokenMem != 1e18) {\\n alternativeCreditsPerToken[_account] = 1e18;\\n }\\n creditBalances[_account] = newBalance;\\n } else {\\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\\n rebasingCreditsDiff =\\n newCredits.toInt256() -\\n creditBalances[_account].toInt256();\\n creditBalances[_account] = newCredits;\\n }\\n }\\n }\\n\\n function _adjustGlobals(\\n int256 _rebasingCreditsDiff,\\n int256 _nonRebasingSupplyDiff\\n ) internal {\\n if (_rebasingCreditsDiff != 0) {\\n rebasingCredits_ = (rebasingCredits_.toInt256() +\\n _rebasingCreditsDiff).toUint256();\\n }\\n if (_nonRebasingSupplyDiff != 0) {\\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\\n _nonRebasingSupplyDiff).toUint256();\\n }\\n }\\n\\n /**\\n * @notice Function to check the amount of tokens that _owner has allowed\\n * to `_spender`.\\n * @param _owner The address which owns the funds.\\n * @param _spender The address which will spend the funds.\\n * @return The number of tokens still available for the _spender.\\n */\\n function allowance(address _owner, address _spender)\\n external\\n view\\n returns (uint256)\\n {\\n return allowances[_owner][_spender];\\n }\\n\\n /**\\n * @notice Approve the passed address to spend the specified amount of\\n * tokens on behalf of msg.sender.\\n * @param _spender The address which will spend the funds.\\n * @param _value The amount of tokens to be spent.\\n * @return true on success.\\n */\\n function approve(address _spender, uint256 _value) external returns (bool) {\\n allowances[msg.sender][_spender] = _value;\\n emit Approval(msg.sender, _spender, _value);\\n return true;\\n }\\n\\n /**\\n * @notice Creates `_amount` tokens and assigns them to `_account`,\\n * increasing the total supply.\\n */\\n function mint(address _account, uint256 _amount) external onlyVault {\\n require(_account != address(0), \\\"Mint to the zero address\\\");\\n\\n // Account\\n (\\n int256 toRebasingCreditsDiff,\\n int256 toNonRebasingSupplyDiff\\n ) = _adjustAccount(_account, _amount.toInt256());\\n // Globals\\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\\n totalSupply = totalSupply + _amount;\\n\\n require(totalSupply < MAX_SUPPLY, \\\"Max supply\\\");\\n emit Transfer(address(0), _account, _amount);\\n }\\n\\n /**\\n * @notice Destroys `_amount` tokens from `_account`,\\n * reducing the total supply.\\n */\\n function burn(address _account, uint256 _amount) external onlyVault {\\n require(_account != address(0), \\\"Burn from the zero address\\\");\\n if (_amount == 0) {\\n return;\\n }\\n\\n // Account\\n (\\n int256 toRebasingCreditsDiff,\\n int256 toNonRebasingSupplyDiff\\n ) = _adjustAccount(_account, -_amount.toInt256());\\n // Globals\\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\\n totalSupply = totalSupply - _amount;\\n\\n emit Transfer(_account, address(0), _amount);\\n }\\n\\n /**\\n * @dev Get the credits per token for an account. Returns a fixed amount\\n * if the account is non-rebasing.\\n * @param _account Address of the account.\\n */\\n function _creditsPerToken(address _account)\\n internal\\n view\\n returns (uint256)\\n {\\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\\n _account\\n ];\\n if (alternativeCreditsPerTokenMem != 0) {\\n return alternativeCreditsPerTokenMem;\\n } else {\\n return rebasingCreditsPerToken_;\\n }\\n }\\n\\n /**\\n * @dev Auto migrate contracts to be non rebasing,\\n * unless they have opted into yield.\\n * @param _account Address of the account.\\n */\\n function _autoMigrate(address _account) internal {\\n uint256 codeLen = _account.code.length;\\n bool isEOA = (codeLen == 0) ||\\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\\n // In previous code versions, contracts would not have had their\\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\\n // therefore we check the actual accounting used on the account as well.\\n if (\\n (!isEOA) &&\\n rebaseState[_account] == RebaseOptions.NotSet &&\\n alternativeCreditsPerToken[_account] == 0\\n ) {\\n _rebaseOptOut(_account);\\n }\\n }\\n\\n /**\\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\\n * also balance that corresponds to those credits. The latter is important\\n * when adjusting the contract's global nonRebasingSupply to circumvent any\\n * possible rounding errors.\\n *\\n * @param _balance Balance of the account.\\n */\\n function _balanceToRebasingCredits(uint256 _balance)\\n internal\\n view\\n returns (uint256 rebasingCredits)\\n {\\n // Rounds up, because we need to ensure that accounts always have\\n // at least the balance that they should have.\\n // Note this should always be used on an absolute account value,\\n // not on a possibly negative diff, because then the rounding would be wrong.\\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\\n }\\n\\n /**\\n * @notice The calling account will start receiving yield after a successful call.\\n * @param _account Address of the account.\\n */\\n function governanceRebaseOptIn(address _account) external onlyGovernor {\\n require(_account != address(0), \\\"Zero address not allowed\\\");\\n _rebaseOptIn(_account);\\n }\\n\\n /**\\n * @notice The calling account will start receiving yield after a successful call.\\n */\\n function rebaseOptIn() external {\\n _rebaseOptIn(msg.sender);\\n }\\n\\n function _rebaseOptIn(address _account) internal {\\n uint256 balance = balanceOf(_account);\\n\\n // prettier-ignore\\n require(\\n alternativeCreditsPerToken[_account] > 0 ||\\n // Accounts may explicitly `rebaseOptIn` regardless of\\n // accounting if they have a 0 balance.\\n creditBalances[_account] == 0\\n ,\\n \\\"Account must be non-rebasing\\\"\\n );\\n RebaseOptions state = rebaseState[_account];\\n // prettier-ignore\\n require(\\n state == RebaseOptions.StdNonRebasing ||\\n state == RebaseOptions.NotSet,\\n \\\"Only standard non-rebasing accounts can opt in\\\"\\n );\\n\\n uint256 newCredits = _balanceToRebasingCredits(balance);\\n\\n // Account\\n rebaseState[_account] = RebaseOptions.StdRebasing;\\n alternativeCreditsPerToken[_account] = 0;\\n creditBalances[_account] = newCredits;\\n // Globals\\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\\n\\n emit AccountRebasingEnabled(_account);\\n }\\n\\n /**\\n * @notice The calling account will no longer receive yield\\n */\\n function rebaseOptOut() external {\\n _rebaseOptOut(msg.sender);\\n }\\n\\n function _rebaseOptOut(address _account) internal {\\n require(\\n alternativeCreditsPerToken[_account] == 0,\\n \\\"Account must be rebasing\\\"\\n );\\n RebaseOptions state = rebaseState[_account];\\n require(\\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\\n \\\"Only standard rebasing accounts can opt out\\\"\\n );\\n\\n uint256 oldCredits = creditBalances[_account];\\n uint256 balance = balanceOf(_account);\\n\\n // Account\\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\\n alternativeCreditsPerToken[_account] = 1e18;\\n creditBalances[_account] = balance;\\n // Globals\\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\\n\\n emit AccountRebasingDisabled(_account);\\n }\\n\\n /**\\n * @notice Distribute yield to users. This changes the exchange rate\\n * between \\\"credits\\\" and OUSD tokens to change rebasing user's balances.\\n * @param _newTotalSupply New total supply of OUSD.\\n */\\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\\n require(totalSupply > 0, \\\"Cannot increase 0 supply\\\");\\n\\n if (totalSupply == _newTotalSupply) {\\n emit TotalSupplyUpdatedHighres(\\n totalSupply,\\n rebasingCredits_,\\n rebasingCreditsPerToken_\\n );\\n return;\\n }\\n\\n totalSupply = _newTotalSupply > MAX_SUPPLY\\n ? MAX_SUPPLY\\n : _newTotalSupply;\\n\\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\\n // round up in the favour of the protocol\\n rebasingCreditsPerToken_ =\\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\\n rebasingSupply;\\n\\n require(rebasingCreditsPerToken_ > 0, \\\"Invalid change in supply\\\");\\n\\n emit TotalSupplyUpdatedHighres(\\n totalSupply,\\n rebasingCredits_,\\n rebasingCreditsPerToken_\\n );\\n }\\n\\n /*\\n * @notice Send the yield from one account to another account.\\n * Each account keeps its own balances.\\n */\\n function delegateYield(address _from, address _to)\\n external\\n onlyGovernorOrStrategist\\n {\\n require(_from != address(0), \\\"Zero from address not allowed\\\");\\n require(_to != address(0), \\\"Zero to address not allowed\\\");\\n\\n require(_from != _to, \\\"Cannot delegate to self\\\");\\n require(\\n yieldFrom[_to] == address(0) &&\\n yieldTo[_to] == address(0) &&\\n yieldFrom[_from] == address(0) &&\\n yieldTo[_from] == address(0),\\n \\\"Blocked by existing yield delegation\\\"\\n );\\n RebaseOptions stateFrom = rebaseState[_from];\\n RebaseOptions stateTo = rebaseState[_to];\\n\\n require(\\n stateFrom == RebaseOptions.NotSet ||\\n stateFrom == RebaseOptions.StdNonRebasing ||\\n stateFrom == RebaseOptions.StdRebasing,\\n \\\"Invalid rebaseState from\\\"\\n );\\n\\n require(\\n stateTo == RebaseOptions.NotSet ||\\n stateTo == RebaseOptions.StdNonRebasing ||\\n stateTo == RebaseOptions.StdRebasing,\\n \\\"Invalid rebaseState to\\\"\\n );\\n\\n if (alternativeCreditsPerToken[_from] == 0) {\\n _rebaseOptOut(_from);\\n }\\n if (alternativeCreditsPerToken[_to] > 0) {\\n _rebaseOptIn(_to);\\n }\\n\\n uint256 fromBalance = balanceOf(_from);\\n uint256 toBalance = balanceOf(_to);\\n uint256 oldToCredits = creditBalances[_to];\\n uint256 newToCredits = _balanceToRebasingCredits(\\n fromBalance + toBalance\\n );\\n\\n // Set up the bidirectional links\\n yieldTo[_from] = _to;\\n yieldFrom[_to] = _from;\\n\\n // Local\\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\\n alternativeCreditsPerToken[_from] = 1e18;\\n creditBalances[_from] = fromBalance;\\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\\n creditBalances[_to] = newToCredits;\\n\\n // Global\\n int256 creditsChange = newToCredits.toInt256() -\\n oldToCredits.toInt256();\\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\\n emit YieldDelegated(_from, _to);\\n }\\n\\n /*\\n * @notice Stop sending the yield from one account to another account.\\n */\\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\\n // Require a delegation, which will also ensure a valid delegation\\n require(yieldTo[_from] != address(0), \\\"Zero address not allowed\\\");\\n\\n address to = yieldTo[_from];\\n uint256 fromBalance = balanceOf(_from);\\n uint256 toBalance = balanceOf(to);\\n uint256 oldToCredits = creditBalances[to];\\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\\n\\n // Remove the bidirectional links\\n yieldFrom[to] = address(0);\\n yieldTo[_from] = address(0);\\n\\n // Local\\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\\n creditBalances[_from] = fromBalance;\\n rebaseState[to] = RebaseOptions.StdRebasing;\\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\\n creditBalances[to] = newToCredits;\\n\\n // Global\\n int256 creditsChange = newToCredits.toInt256() -\\n oldToCredits.toInt256();\\n _adjustGlobals(creditsChange, fromBalance.toInt256());\\n emit YieldUndelegated(_from, to);\\n }\\n}\\n\",\"keccak256\":\"0x73439bef6569f5adf6f5ce2cb54a5f0d3109d4819457532236e172a7091980a9\",\"license\":\"BUSL-1.1\"},\"contracts/utils/Helpers.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { IBasicToken } from \\\"../interfaces/IBasicToken.sol\\\";\\n\\nlibrary Helpers {\\n /**\\n * @notice Fetch the `symbol()` from an ERC20 token\\n * @dev Grabs the `symbol()` from a contract\\n * @param _token Address of the ERC20 token\\n * @return string Symbol of the ERC20 token\\n */\\n function getSymbol(address _token) internal view returns (string memory) {\\n string memory symbol = IBasicToken(_token).symbol();\\n return symbol;\\n }\\n\\n /**\\n * @notice Fetch the `decimals()` from an ERC20 token\\n * @dev Grabs the `decimals()` from a contract and fails if\\n * the decimal value does not live within a certain range\\n * @param _token Address of the ERC20 token\\n * @return uint256 Decimals of the ERC20 token\\n */\\n function getDecimals(address _token) internal view returns (uint256) {\\n uint256 decimals = IBasicToken(_token).decimals();\\n require(\\n decimals >= 4 && decimals <= 18,\\n \\\"Token must have sufficient decimal places\\\"\\n );\\n\\n return decimals;\\n }\\n}\\n\",\"keccak256\":\"0x4366f8d90b34c1eef8bbaaf369b1e5cd59f04027bb3c111f208eaee65bbc0346\",\"license\":\"BUSL-1.1\"},\"contracts/utils/Initializable.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base contract any contracts that need to initialize state after deployment.\\n * @author Origin Protocol Inc\\n */\\nabstract contract Initializable {\\n /**\\n * @dev Indicates that the contract has been initialized.\\n */\\n bool private initialized;\\n\\n /**\\n * @dev Indicates that the contract is in the process of being initialized.\\n */\\n bool private initializing;\\n\\n /**\\n * @dev Modifier to protect an initializer function from being invoked twice.\\n */\\n modifier initializer() {\\n require(\\n initializing || !initialized,\\n \\\"Initializable: contract is already initialized\\\"\\n );\\n\\n bool isTopLevelCall = !initializing;\\n if (isTopLevelCall) {\\n initializing = true;\\n initialized = true;\\n }\\n\\n _;\\n\\n if (isTopLevelCall) {\\n initializing = false;\\n }\\n }\\n\\n uint256[50] private ______gap;\\n}\\n\",\"keccak256\":\"0x50d39ebf38a3d3111f2b77a6c75ece1d4ae731552fec4697ab16fcf6c0d4d5e8\",\"license\":\"BUSL-1.1\"},\"contracts/utils/StableMath.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { SafeMath } from \\\"@openzeppelin/contracts/utils/math/SafeMath.sol\\\";\\n\\n// Based on StableMath from Stability Labs Pty. Ltd.\\n// https://github.com/mstable/mStable-contracts/blob/master/contracts/shared/StableMath.sol\\n\\nlibrary StableMath {\\n using SafeMath for uint256;\\n\\n /**\\n * @dev Scaling unit for use in specific calculations,\\n * where 1 * 10**18, or 1e18 represents a unit '1'\\n */\\n uint256 private constant FULL_SCALE = 1e18;\\n\\n /***************************************\\n Helpers\\n ****************************************/\\n\\n /**\\n * @dev Adjust the scale of an integer\\n * @param to Decimals to scale to\\n * @param from Decimals to scale from\\n */\\n function scaleBy(\\n uint256 x,\\n uint256 to,\\n uint256 from\\n ) internal pure returns (uint256) {\\n if (to > from) {\\n x = x.mul(10**(to - from));\\n } else if (to < from) {\\n // slither-disable-next-line divide-before-multiply\\n x = x.div(10**(from - to));\\n }\\n return x;\\n }\\n\\n /***************************************\\n Precise Arithmetic\\n ****************************************/\\n\\n /**\\n * @dev Multiplies two precise units, and then truncates by the full scale\\n * @param x Left hand input to multiplication\\n * @param y Right hand input to multiplication\\n * @return Result after multiplying the two inputs and then dividing by the shared\\n * scale unit\\n */\\n function mulTruncate(uint256 x, uint256 y) internal pure returns (uint256) {\\n return mulTruncateScale(x, y, FULL_SCALE);\\n }\\n\\n /**\\n * @dev Multiplies two precise units, and then truncates by the given scale. For example,\\n * when calculating 90% of 10e18, (10e18 * 9e17) / 1e18 = (9e36) / 1e18 = 9e18\\n * @param x Left hand input to multiplication\\n * @param y Right hand input to multiplication\\n * @param scale Scale unit\\n * @return Result after multiplying the two inputs and then dividing by the shared\\n * scale unit\\n */\\n function mulTruncateScale(\\n uint256 x,\\n uint256 y,\\n uint256 scale\\n ) internal pure returns (uint256) {\\n // e.g. assume scale = fullScale\\n // z = 10e18 * 9e17 = 9e36\\n uint256 z = x.mul(y);\\n // return 9e36 / 1e18 = 9e18\\n return z.div(scale);\\n }\\n\\n /**\\n * @dev Multiplies two precise units, and then truncates by the full scale, rounding up the result\\n * @param x Left hand input to multiplication\\n * @param y Right hand input to multiplication\\n * @return Result after multiplying the two inputs and then dividing by the shared\\n * scale unit, rounded up to the closest base unit.\\n */\\n function mulTruncateCeil(uint256 x, uint256 y)\\n internal\\n pure\\n returns (uint256)\\n {\\n // e.g. 8e17 * 17268172638 = 138145381104e17\\n uint256 scaled = x.mul(y);\\n // e.g. 138145381104e17 + 9.99...e17 = 138145381113.99...e17\\n uint256 ceil = scaled.add(FULL_SCALE.sub(1));\\n // e.g. 13814538111.399...e18 / 1e18 = 13814538111\\n return ceil.div(FULL_SCALE);\\n }\\n\\n /**\\n * @dev Precisely divides two units, by first scaling the left hand operand. Useful\\n * for finding percentage weightings, i.e. 8e18/10e18 = 80% (or 8e17)\\n * @param x Left hand input to division\\n * @param y Right hand input to division\\n * @return Result after multiplying the left operand by the scale, and\\n * executing the division on the right hand input.\\n */\\n function divPrecisely(uint256 x, uint256 y)\\n internal\\n pure\\n returns (uint256)\\n {\\n // e.g. 8e18 * 1e18 = 8e36\\n uint256 z = x.mul(FULL_SCALE);\\n // e.g. 8e36 / 10e18 = 8e17\\n return z.div(y);\\n }\\n}\\n\",\"keccak256\":\"0x71d6ed0053a1e5ef018d27c3b6d024f336d8157ab6f6859e400b3243a50a71b7\",\"license\":\"BUSL-1.1\"},\"contracts/vault/OETHVaultCore.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { SafeERC20 } from \\\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\\\";\\nimport { IERC20 } from \\\"@openzeppelin/contracts/token/ERC20/IERC20.sol\\\";\\nimport { SafeCast } from \\\"@openzeppelin/contracts/utils/math/SafeCast.sol\\\";\\n\\nimport { StableMath } from \\\"../utils/StableMath.sol\\\";\\nimport { VaultCore } from \\\"./VaultCore.sol\\\";\\nimport { IStrategy } from \\\"../interfaces/IStrategy.sol\\\";\\n\\n/**\\n * @title OETH VaultCore Contract\\n * @author Origin Protocol Inc\\n */\\ncontract OETHVaultCore is VaultCore {\\n using SafeERC20 for IERC20;\\n using StableMath for uint256;\\n\\n address public immutable weth;\\n uint256 public wethAssetIndex;\\n\\n // For future use (because OETHBaseVaultCore inherits from this)\\n uint256[50] private __gap;\\n\\n constructor(address _weth) {\\n weth = _weth;\\n }\\n\\n /**\\n * @dev Caches WETH's index in `allAssets` variable.\\n * Reduces gas usage by redeem by caching that.\\n */\\n function cacheWETHAssetIndex() external onlyGovernor {\\n uint256 assetCount = allAssets.length;\\n for (uint256 i; i < assetCount; ++i) {\\n if (allAssets[i] == weth) {\\n wethAssetIndex = i;\\n break;\\n }\\n }\\n\\n require(allAssets[wethAssetIndex] == weth, \\\"Invalid WETH Asset Index\\\");\\n }\\n\\n // @inheritdoc VaultCore\\n function mintForStrategy(uint256 amount)\\n external\\n override\\n whenNotCapitalPaused\\n {\\n require(\\n strategies[msg.sender].isSupported == true,\\n \\\"Unsupported strategy\\\"\\n );\\n require(\\n isMintWhitelistedStrategy[msg.sender] == true,\\n \\\"Not whitelisted strategy\\\"\\n );\\n\\n emit Mint(msg.sender, amount);\\n\\n // Mint matching amount of OTokens\\n oUSD.mint(msg.sender, amount);\\n }\\n\\n // @inheritdoc VaultCore\\n function burnForStrategy(uint256 amount)\\n external\\n override\\n whenNotCapitalPaused\\n {\\n require(\\n strategies[msg.sender].isSupported == true,\\n \\\"Unsupported strategy\\\"\\n );\\n require(\\n isMintWhitelistedStrategy[msg.sender] == true,\\n \\\"Not whitelisted strategy\\\"\\n );\\n\\n emit Redeem(msg.sender, amount);\\n\\n // Burn OTokens\\n oUSD.burn(msg.sender, amount);\\n }\\n\\n // @inheritdoc VaultCore\\n // slither-disable-start reentrancy-no-eth\\n function _mint(\\n address _asset,\\n uint256 _amount,\\n uint256 _minimumOusdAmount\\n ) internal virtual override {\\n require(_asset == weth, \\\"Unsupported asset for minting\\\");\\n require(_amount > 0, \\\"Amount must be greater than 0\\\");\\n require(\\n _amount >= _minimumOusdAmount,\\n \\\"Mint amount lower than minimum\\\"\\n );\\n\\n emit Mint(msg.sender, _amount);\\n\\n // Rebase must happen before any transfers occur.\\n if (!rebasePaused && _amount >= rebaseThreshold) {\\n _rebase();\\n }\\n\\n // Mint oTokens\\n oUSD.mint(msg.sender, _amount);\\n\\n // Transfer the deposited coins to the vault\\n IERC20(_asset).safeTransferFrom(msg.sender, address(this), _amount);\\n\\n // Give priority to the withdrawal queue for the new WETH liquidity\\n _addWithdrawalQueueLiquidity();\\n\\n // Auto-allocate if necessary\\n if (_amount >= autoAllocateThreshold) {\\n _allocate();\\n }\\n }\\n\\n // slither-disable-end reentrancy-no-eth\\n\\n // @inheritdoc VaultCore\\n function _calculateRedeemOutputs(uint256 _amount)\\n internal\\n view\\n virtual\\n override\\n returns (uint256[] memory outputs)\\n {\\n // Overrides `VaultCore._calculateRedeemOutputs` to redeem with only\\n // WETH instead of LST-mix. Doesn't change the function signature\\n // for backward compatibility\\n\\n // Calculate redeem fee\\n if (redeemFeeBps > 0) {\\n uint256 redeemFee = _amount.mulTruncateScale(redeemFeeBps, 1e4);\\n _amount = _amount - redeemFee;\\n }\\n\\n // Ensure that the WETH index is cached\\n uint256 _wethAssetIndex = wethAssetIndex;\\n require(\\n allAssets[_wethAssetIndex] == weth,\\n \\\"WETH Asset index not cached\\\"\\n );\\n\\n outputs = new uint256[](allAssets.length);\\n outputs[_wethAssetIndex] = _amount;\\n }\\n\\n // @inheritdoc VaultCore\\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\\n internal\\n virtual\\n override\\n {\\n // Override `VaultCore._redeem` to simplify it. Gets rid of oracle\\n // usage and looping through all assets for LST-mix redeem. Instead\\n // does a simple WETH-only redeem.\\n emit Redeem(msg.sender, _amount);\\n\\n if (_amount == 0) {\\n return;\\n }\\n\\n // Amount excluding fees\\n // No fee for the strategist or the governor, makes it easier to do operations\\n uint256 amountMinusFee = (msg.sender == strategistAddr || isGovernor())\\n ? _amount\\n : _calculateRedeemOutputs(_amount)[wethAssetIndex];\\n\\n require(\\n amountMinusFee >= _minimumUnitAmount,\\n \\\"Redeem amount lower than minimum\\\"\\n );\\n\\n // Is there enough WETH in the Vault available after accounting for the withdrawal queue\\n require(_wethAvailable() >= amountMinusFee, \\\"Liquidity error\\\");\\n\\n // Transfer WETH minus the fee to the redeemer\\n IERC20(weth).safeTransfer(msg.sender, amountMinusFee);\\n\\n // Burn OETH from user (including fees)\\n oUSD.burn(msg.sender, _amount);\\n\\n // Prevent insolvency\\n _postRedeem(_amount);\\n }\\n\\n /**\\n * @notice Request an asynchronous withdrawal of WETH in exchange for OETH.\\n * The OETH is burned on request and the WETH is transferred to the withdrawer on claim.\\n * This request can be claimed once the withdrawal queue's `claimable` amount\\n * is greater than or equal this request's `queued` amount.\\n * There is a minimum of 10 minutes before a request can be claimed. After that, the request just needs\\n * enough WETH liquidity in the Vault to satisfy all the outstanding requests to that point in the queue.\\n * OETH is converted to WETH at 1:1.\\n * @param _amount Amount of OETH to burn.\\n * @return requestId Unique ID for the withdrawal request\\n * @return queued Cumulative total of all WETH queued including already claimed requests.\\n */\\n function requestWithdrawal(uint256 _amount)\\n external\\n virtual\\n whenNotCapitalPaused\\n nonReentrant\\n returns (uint256 requestId, uint256 queued)\\n {\\n require(withdrawalClaimDelay > 0, \\\"Async withdrawals not enabled\\\");\\n\\n // The check that the requester has enough OETH is done in to later burn call\\n\\n requestId = withdrawalQueueMetadata.nextWithdrawalIndex;\\n queued = withdrawalQueueMetadata.queued + _amount;\\n\\n // Store the next withdrawal request\\n withdrawalQueueMetadata.nextWithdrawalIndex = SafeCast.toUint128(\\n requestId + 1\\n );\\n // Store the updated queued amount which reserves WETH in the withdrawal queue\\n // and reduces the vault's total assets\\n withdrawalQueueMetadata.queued = SafeCast.toUint128(queued);\\n // Store the user's withdrawal request\\n withdrawalRequests[requestId] = WithdrawalRequest({\\n withdrawer: msg.sender,\\n claimed: false,\\n timestamp: uint40(block.timestamp),\\n amount: SafeCast.toUint128(_amount),\\n queued: SafeCast.toUint128(queued)\\n });\\n\\n // Burn the user's OETH\\n oUSD.burn(msg.sender, _amount);\\n\\n // Prevent withdrawal if the vault is solvent by more than the allowed percentage\\n _postRedeem(_amount);\\n\\n emit WithdrawalRequested(msg.sender, requestId, _amount, queued);\\n }\\n\\n // slither-disable-start reentrancy-no-eth\\n /**\\n * @notice Claim a previously requested withdrawal once it is claimable.\\n * This request can be claimed once the withdrawal queue's `claimable` amount\\n * is greater than or equal this request's `queued` amount and 10 minutes has passed.\\n * If the requests is not claimable, the transaction will revert with `Queue pending liquidity`.\\n * If the request is not older than 10 minutes, the transaction will revert with `Claim delay not met`.\\n * OETH is converted to WETH at 1:1.\\n * @param _requestId Unique ID for the withdrawal request\\n * @return amount Amount of WETH transferred to the withdrawer\\n */\\n function claimWithdrawal(uint256 _requestId)\\n external\\n virtual\\n whenNotCapitalPaused\\n nonReentrant\\n returns (uint256 amount)\\n {\\n // Try and get more liquidity if there is not enough available\\n if (\\n withdrawalRequests[_requestId].queued >\\n withdrawalQueueMetadata.claimable\\n ) {\\n // Add any WETH to the withdrawal queue\\n // this needs to remain here as:\\n // - Vault can be funded and `addWithdrawalQueueLiquidity` is not externally called\\n // - funds can be withdrawn from a strategy\\n //\\n // Those funds need to be added to withdrawal queue liquidity\\n _addWithdrawalQueueLiquidity();\\n }\\n\\n amount = _claimWithdrawal(_requestId);\\n\\n // transfer WETH from the vault to the withdrawer\\n IERC20(weth).safeTransfer(msg.sender, amount);\\n\\n // Prevent insolvency\\n _postRedeem(amount);\\n }\\n\\n // slither-disable-end reentrancy-no-eth\\n\\n /**\\n * @notice Claim a previously requested withdrawals once they are claimable.\\n * This requests can be claimed once the withdrawal queue's `claimable` amount\\n * is greater than or equal each request's `queued` amount and 10 minutes has passed.\\n * If one of the requests is not claimable, the whole transaction will revert with `Queue pending liquidity`.\\n * If one of the requests is not older than 10 minutes,\\n * the whole transaction will revert with `Claim delay not met`.\\n * @param _requestIds Unique ID of each withdrawal request\\n * @return amounts Amount of WETH received for each request\\n * @return totalAmount Total amount of WETH transferred to the withdrawer\\n */\\n function claimWithdrawals(uint256[] calldata _requestIds)\\n external\\n virtual\\n whenNotCapitalPaused\\n nonReentrant\\n returns (uint256[] memory amounts, uint256 totalAmount)\\n {\\n // Add any WETH to the withdrawal queue\\n // this needs to remain here as:\\n // - Vault can be funded and `addWithdrawalQueueLiquidity` is not externally called\\n // - funds can be withdrawn from a strategy\\n //\\n // Those funds need to be added to withdrawal queue liquidity\\n _addWithdrawalQueueLiquidity();\\n\\n amounts = new uint256[](_requestIds.length);\\n for (uint256 i; i < _requestIds.length; ++i) {\\n amounts[i] = _claimWithdrawal(_requestIds[i]);\\n totalAmount += amounts[i];\\n }\\n\\n // transfer all the claimed WETH from the vault to the withdrawer\\n IERC20(weth).safeTransfer(msg.sender, totalAmount);\\n\\n // Prevent insolvency\\n _postRedeem(totalAmount);\\n }\\n\\n function _claimWithdrawal(uint256 requestId)\\n internal\\n returns (uint256 amount)\\n {\\n require(withdrawalClaimDelay > 0, \\\"Async withdrawals not enabled\\\");\\n\\n // Load the structs from storage into memory\\n WithdrawalRequest memory request = withdrawalRequests[requestId];\\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\\n\\n require(\\n request.timestamp + withdrawalClaimDelay <= block.timestamp,\\n \\\"Claim delay not met\\\"\\n );\\n // If there isn't enough reserved liquidity in the queue to claim\\n require(request.queued <= queue.claimable, \\\"Queue pending liquidity\\\");\\n require(request.withdrawer == msg.sender, \\\"Not requester\\\");\\n require(request.claimed == false, \\\"Already claimed\\\");\\n\\n // Store the request as claimed\\n withdrawalRequests[requestId].claimed = true;\\n // Store the updated claimed amount\\n withdrawalQueueMetadata.claimed = queue.claimed + request.amount;\\n\\n emit WithdrawalClaimed(msg.sender, requestId, request.amount);\\n\\n return request.amount;\\n }\\n\\n /// @notice Adds WETH to the withdrawal queue if there is a funding shortfall.\\n /// @dev is called from the Native Staking strategy when validator withdrawals are processed.\\n /// It also called before any WETH is allocated to a strategy.\\n function addWithdrawalQueueLiquidity() external {\\n _addWithdrawalQueueLiquidity();\\n }\\n\\n /// @dev Adds WETH to the withdrawal queue if there is a funding shortfall.\\n /// This assumes 1 WETH equal 1 OETH.\\n function _addWithdrawalQueueLiquidity()\\n internal\\n returns (uint256 addedClaimable)\\n {\\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\\n\\n // Check if the claimable WETH is less than the queued amount\\n uint256 queueShortfall = queue.queued - queue.claimable;\\n\\n // No need to do anything is the withdrawal queue is full funded\\n if (queueShortfall == 0) {\\n return 0;\\n }\\n\\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\\n\\n // Of the claimable withdrawal requests, how much is unclaimed?\\n // That is, the amount of WETH that is currently allocated for the withdrawal queue\\n uint256 allocatedWeth = queue.claimable - queue.claimed;\\n\\n // If there is no unallocated WETH then there is nothing to add to the queue\\n if (wethBalance <= allocatedWeth) {\\n return 0;\\n }\\n\\n uint256 unallocatedWeth = wethBalance - allocatedWeth;\\n\\n // the new claimable amount is the smaller of the queue shortfall or unallocated weth\\n addedClaimable = queueShortfall < unallocatedWeth\\n ? queueShortfall\\n : unallocatedWeth;\\n uint256 newClaimable = queue.claimable + addedClaimable;\\n\\n // Store the new claimable amount back to storage\\n withdrawalQueueMetadata.claimable = SafeCast.toUint128(newClaimable);\\n\\n // emit a WithdrawalClaimable event\\n emit WithdrawalClaimable(newClaimable, addedClaimable);\\n }\\n\\n /***************************************\\n View Functions\\n ****************************************/\\n\\n /// @dev Calculate how much WETH in the vault is not reserved for the withdrawal queue.\\n // That is, it is available to be redeemed or deposited into a strategy.\\n function _wethAvailable() internal view returns (uint256 wethAvailable) {\\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\\n\\n // The amount of WETH that is still to be claimed in the withdrawal queue\\n uint256 outstandingWithdrawals = queue.queued - queue.claimed;\\n\\n // The amount of sitting in WETH in the vault\\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\\n\\n // If there is not enough WETH in the vault to cover the outstanding withdrawals\\n if (wethBalance <= outstandingWithdrawals) {\\n return 0;\\n }\\n\\n return wethBalance - outstandingWithdrawals;\\n }\\n\\n /// @dev Get the balance of an asset held in Vault and all strategies\\n /// less any WETH that is reserved for the withdrawal queue.\\n /// WETH is the only asset that can return a non-zero balance.\\n /// All other assets will return 0 even if there is some dust amounts left in the Vault.\\n /// For example, there is 1 wei left of stETH in the OETH Vault but will return 0 in this function.\\n ///\\n /// If there is not enough WETH in the vault and all strategies to cover all outstanding\\n /// withdrawal requests then return a WETH balance of 0\\n function _checkBalance(address _asset)\\n internal\\n view\\n override\\n returns (uint256 balance)\\n {\\n if (_asset != weth) {\\n return 0;\\n }\\n\\n // Get the WETH in the vault and the strategies\\n balance = super._checkBalance(_asset);\\n\\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\\n\\n // If the vault becomes insolvent enough that the total value in the vault and all strategies\\n // is less than the outstanding withdrawals.\\n // For example, there was a mass slashing event and most users request a withdrawal.\\n if (balance + queue.claimed < queue.queued) {\\n return 0;\\n }\\n\\n // Need to remove WETH that is reserved for the withdrawal queue\\n return balance + queue.claimed - queue.queued;\\n }\\n\\n /**\\n * @notice Allocate unallocated funds on Vault to strategies.\\n **/\\n function allocate() external override whenNotCapitalPaused nonReentrant {\\n // Add any unallocated WETH to the withdrawal queue first\\n _addWithdrawalQueueLiquidity();\\n\\n _allocate();\\n }\\n\\n /// @dev Allocate WETH to the default WETH strategy if there is excess to the Vault buffer.\\n /// This is called from either `mint` or `allocate` and assumes `_addWithdrawalQueueLiquidity`\\n /// has been called before this function.\\n function _allocate() internal override {\\n // No need to do anything if no default strategy for WETH\\n address depositStrategyAddr = assetDefaultStrategies[weth];\\n if (depositStrategyAddr == address(0)) return;\\n\\n uint256 wethAvailableInVault = _wethAvailable();\\n // No need to do anything if there isn't any WETH in the vault to allocate\\n if (wethAvailableInVault == 0) return;\\n\\n // Calculate the target buffer for the vault using the total supply\\n uint256 totalSupply = oUSD.totalSupply();\\n uint256 targetBuffer = totalSupply.mulTruncate(vaultBuffer);\\n\\n // If available WETH in the Vault is below or equal the target buffer then there's nothing to allocate\\n if (wethAvailableInVault <= targetBuffer) return;\\n\\n // The amount of assets to allocate to the default strategy\\n uint256 allocateAmount = wethAvailableInVault - targetBuffer;\\n\\n IStrategy strategy = IStrategy(depositStrategyAddr);\\n // Transfer WETH to the strategy and call the strategy's deposit function\\n IERC20(weth).safeTransfer(address(strategy), allocateAmount);\\n strategy.deposit(weth, allocateAmount);\\n\\n emit AssetAllocated(weth, depositStrategyAddr, allocateAmount);\\n }\\n\\n /// @dev The total value of all WETH held by the vault and all its strategies\\n /// less any WETH that is reserved for the withdrawal queue.\\n ///\\n // If there is not enough WETH in the vault and all strategies to cover all outstanding\\n // withdrawal requests then return a total value of 0.\\n function _totalValue() internal view override returns (uint256 value) {\\n // As WETH is the only asset, just return the WETH balance\\n return _checkBalance(weth);\\n }\\n\\n /// @dev Only WETH is supported in the OETH Vault so return the WETH balance only\\n /// Any ETH balances in the Vault will be ignored.\\n /// Amounts from previously supported vault assets will also be ignored.\\n /// For example, there is 1 wei left of stETH in the OETH Vault but is will be ignored.\\n function _totalValueInVault()\\n internal\\n view\\n override\\n returns (uint256 value)\\n {\\n value = IERC20(weth).balanceOf(address(this));\\n }\\n}\\n\",\"keccak256\":\"0x6cd980e5b1771d1b9a3f9f27197f78e58891484bf2bac68953277d8e78ca704c\",\"license\":\"BUSL-1.1\"},\"contracts/vault/VaultCore.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title OToken VaultCore contract\\n * @notice The Vault contract stores assets. On a deposit, OTokens will be minted\\n and sent to the depositor. On a withdrawal, OTokens will be burned and\\n assets will be sent to the withdrawer. The Vault accepts deposits of\\n interest from yield bearing strategies which will modify the supply\\n of OTokens.\\n * @author Origin Protocol Inc\\n */\\n\\nimport { SafeERC20 } from \\\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\\\";\\n\\nimport { StableMath } from \\\"../utils/StableMath.sol\\\";\\nimport { IOracle } from \\\"../interfaces/IOracle.sol\\\";\\nimport { IGetExchangeRateToken } from \\\"../interfaces/IGetExchangeRateToken.sol\\\";\\n\\nimport \\\"./VaultInitializer.sol\\\";\\n\\ncontract VaultCore is VaultInitializer {\\n using SafeERC20 for IERC20;\\n using StableMath for uint256;\\n /// @dev max signed int\\n uint256 internal constant MAX_INT = uint256(type(int256).max);\\n\\n /**\\n * @dev Verifies that the rebasing is not paused.\\n */\\n modifier whenNotRebasePaused() {\\n require(!rebasePaused, \\\"Rebasing paused\\\");\\n _;\\n }\\n\\n /**\\n * @dev Verifies that the deposits are not paused.\\n */\\n modifier whenNotCapitalPaused() {\\n require(!capitalPaused, \\\"Capital paused\\\");\\n _;\\n }\\n\\n /**\\n * @dev Verifies that the caller is the AMO strategy.\\n */\\n modifier onlyOusdMetaStrategy() {\\n require(\\n msg.sender == ousdMetaStrategy,\\n \\\"Caller is not the OUSD meta strategy\\\"\\n );\\n _;\\n }\\n\\n /**\\n * @notice Deposit a supported asset and mint OTokens.\\n * @param _asset Address of the asset being deposited\\n * @param _amount Amount of the asset being deposited\\n * @param _minimumOusdAmount Minimum OTokens to mint\\n */\\n function mint(\\n address _asset,\\n uint256 _amount,\\n uint256 _minimumOusdAmount\\n ) external whenNotCapitalPaused nonReentrant {\\n _mint(_asset, _amount, _minimumOusdAmount);\\n }\\n\\n /**\\n * @dev Deposit a supported asset and mint OTokens.\\n * @param _asset Address of the asset being deposited\\n * @param _amount Amount of the asset being deposited\\n * @param _minimumOusdAmount Minimum OTokens to mint\\n */\\n function _mint(\\n address _asset,\\n uint256 _amount,\\n uint256 _minimumOusdAmount\\n ) internal virtual {\\n require(assets[_asset].isSupported, \\\"Asset is not supported\\\");\\n require(_amount > 0, \\\"Amount must be greater than 0\\\");\\n\\n uint256 units = _toUnits(_amount, _asset);\\n uint256 unitPrice = _toUnitPrice(_asset, true);\\n uint256 priceAdjustedDeposit = (units * unitPrice) / 1e18;\\n\\n if (_minimumOusdAmount > 0) {\\n require(\\n priceAdjustedDeposit >= _minimumOusdAmount,\\n \\\"Mint amount lower than minimum\\\"\\n );\\n }\\n\\n emit Mint(msg.sender, priceAdjustedDeposit);\\n\\n // Rebase must happen before any transfers occur.\\n if (priceAdjustedDeposit >= rebaseThreshold && !rebasePaused) {\\n _rebase();\\n }\\n\\n // Mint matching amount of OTokens\\n oUSD.mint(msg.sender, priceAdjustedDeposit);\\n\\n // Transfer the deposited coins to the vault\\n IERC20 asset = IERC20(_asset);\\n asset.safeTransferFrom(msg.sender, address(this), _amount);\\n\\n if (priceAdjustedDeposit >= autoAllocateThreshold) {\\n _allocate();\\n }\\n }\\n\\n /**\\n * @notice Mint OTokens for a Metapool Strategy\\n * @param _amount Amount of the asset being deposited\\n *\\n * Notice: can't use `nonReentrant` modifier since the `mint` function can\\n * call `allocate`, and that can trigger `ConvexOUSDMetaStrategy` to call this function\\n * while the execution of the `mint` has not yet completed -> causing a `nonReentrant` collision.\\n *\\n * Also important to understand is that this is a limitation imposed by the test suite.\\n * Production / mainnet contracts should never be configured in a way where mint/redeem functions\\n * that are moving funds between the Vault and end user wallets can influence strategies\\n * utilizing this function.\\n */\\n function mintForStrategy(uint256 _amount)\\n external\\n virtual\\n whenNotCapitalPaused\\n onlyOusdMetaStrategy\\n {\\n require(_amount < MAX_INT, \\\"Amount too high\\\");\\n\\n emit Mint(msg.sender, _amount);\\n\\n // safe to cast because of the require check at the beginning of the function\\n netOusdMintedForStrategy += int256(_amount);\\n\\n require(\\n abs(netOusdMintedForStrategy) < netOusdMintForStrategyThreshold,\\n \\\"Minted ousd surpassed netOusdMintForStrategyThreshold.\\\"\\n );\\n\\n // Mint matching amount of OTokens\\n oUSD.mint(msg.sender, _amount);\\n }\\n\\n // In memoriam\\n\\n /**\\n * @notice Withdraw a supported asset and burn OTokens.\\n * @param _amount Amount of OTokens to burn\\n * @param _minimumUnitAmount Minimum stablecoin units to receive in return\\n */\\n function redeem(uint256 _amount, uint256 _minimumUnitAmount)\\n external\\n whenNotCapitalPaused\\n nonReentrant\\n {\\n _redeem(_amount, _minimumUnitAmount);\\n }\\n\\n /**\\n * @notice Withdraw a supported asset and burn OTokens.\\n * @param _amount Amount of OTokens to burn\\n * @param _minimumUnitAmount Minimum stablecoin units to receive in return\\n */\\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\\n internal\\n virtual\\n {\\n // Calculate redemption outputs\\n uint256[] memory outputs = _calculateRedeemOutputs(_amount);\\n\\n emit Redeem(msg.sender, _amount);\\n\\n // Send outputs\\n uint256 assetCount = allAssets.length;\\n for (uint256 i = 0; i < assetCount; ++i) {\\n if (outputs[i] == 0) continue;\\n\\n address assetAddr = allAssets[i];\\n\\n if (IERC20(assetAddr).balanceOf(address(this)) >= outputs[i]) {\\n // Use Vault funds first if sufficient\\n IERC20(assetAddr).safeTransfer(msg.sender, outputs[i]);\\n } else {\\n address strategyAddr = assetDefaultStrategies[assetAddr];\\n if (strategyAddr != address(0)) {\\n // Nothing in Vault, but something in Strategy, send from there\\n IStrategy strategy = IStrategy(strategyAddr);\\n strategy.withdraw(msg.sender, assetAddr, outputs[i]);\\n } else {\\n // Cant find funds anywhere\\n revert(\\\"Liquidity error\\\");\\n }\\n }\\n }\\n\\n if (_minimumUnitAmount > 0) {\\n uint256 unitTotal = 0;\\n for (uint256 i = 0; i < outputs.length; ++i) {\\n unitTotal += _toUnits(outputs[i], allAssets[i]);\\n }\\n require(\\n unitTotal >= _minimumUnitAmount,\\n \\\"Redeem amount lower than minimum\\\"\\n );\\n }\\n\\n oUSD.burn(msg.sender, _amount);\\n\\n _postRedeem(_amount);\\n }\\n\\n function _postRedeem(uint256 _amount) internal {\\n // Until we can prove that we won't affect the prices of our assets\\n // by withdrawing them, this should be here.\\n // It's possible that a strategy was off on its asset total, perhaps\\n // a reward token sold for more or for less than anticipated.\\n uint256 totalUnits = 0;\\n if (_amount >= rebaseThreshold && !rebasePaused) {\\n totalUnits = _rebase();\\n } else {\\n totalUnits = _totalValue();\\n }\\n\\n // Check that the OTokens are backed by enough assets\\n if (maxSupplyDiff > 0) {\\n // If there are more outstanding withdrawal requests than assets in the vault and strategies\\n // then the available assets will be negative and totalUnits will be rounded up to zero.\\n // As we don't know the exact shortfall amount, we will reject all redeem and withdrawals\\n require(totalUnits > 0, \\\"Too many outstanding requests\\\");\\n\\n // Allow a max difference of maxSupplyDiff% between\\n // backing assets value and OUSD total supply\\n uint256 diff = oUSD.totalSupply().divPrecisely(totalUnits);\\n require(\\n (diff > 1e18 ? diff - 1e18 : 1e18 - diff) <= maxSupplyDiff,\\n \\\"Backing supply liquidity error\\\"\\n );\\n }\\n }\\n\\n /**\\n * @notice Burn OTokens for Metapool Strategy\\n * @param _amount Amount of OUSD to burn\\n *\\n * @dev Notice: can't use `nonReentrant` modifier since the `redeem` function could\\n * require withdrawal on `ConvexOUSDMetaStrategy` and that one can call `burnForStrategy`\\n * while the execution of the `redeem` has not yet completed -> causing a `nonReentrant` collision.\\n *\\n * Also important to understand is that this is a limitation imposed by the test suite.\\n * Production / mainnet contracts should never be configured in a way where mint/redeem functions\\n * that are moving funds between the Vault and end user wallets can influence strategies\\n * utilizing this function.\\n */\\n function burnForStrategy(uint256 _amount)\\n external\\n virtual\\n whenNotCapitalPaused\\n onlyOusdMetaStrategy\\n {\\n require(_amount < MAX_INT, \\\"Amount too high\\\");\\n\\n emit Redeem(msg.sender, _amount);\\n\\n // safe to cast because of the require check at the beginning of the function\\n netOusdMintedForStrategy -= int256(_amount);\\n\\n require(\\n abs(netOusdMintedForStrategy) < netOusdMintForStrategyThreshold,\\n \\\"Attempting to burn too much OUSD.\\\"\\n );\\n\\n // Burn OTokens\\n oUSD.burn(msg.sender, _amount);\\n }\\n\\n /**\\n * @notice Allocate unallocated funds on Vault to strategies.\\n **/\\n function allocate() external virtual whenNotCapitalPaused nonReentrant {\\n _allocate();\\n }\\n\\n /**\\n * @dev Allocate unallocated funds on Vault to strategies.\\n **/\\n function _allocate() internal virtual {\\n uint256 vaultValue = _totalValueInVault();\\n // Nothing in vault to allocate\\n if (vaultValue == 0) return;\\n uint256 strategiesValue = _totalValueInStrategies();\\n // We have a method that does the same as this, gas optimisation\\n uint256 calculatedTotalValue = vaultValue + strategiesValue;\\n\\n // We want to maintain a buffer on the Vault so calculate a percentage\\n // modifier to multiply each amount being allocated by to enforce the\\n // vault buffer\\n uint256 vaultBufferModifier;\\n if (strategiesValue == 0) {\\n // Nothing in Strategies, allocate 100% minus the vault buffer to\\n // strategies\\n vaultBufferModifier = uint256(1e18) - vaultBuffer;\\n } else {\\n vaultBufferModifier =\\n (vaultBuffer * calculatedTotalValue) /\\n vaultValue;\\n if (1e18 > vaultBufferModifier) {\\n // E.g. 1e18 - (1e17 * 10e18)/5e18 = 8e17\\n // (5e18 * 8e17) / 1e18 = 4e18 allocated from Vault\\n vaultBufferModifier = uint256(1e18) - vaultBufferModifier;\\n } else {\\n // We need to let the buffer fill\\n return;\\n }\\n }\\n if (vaultBufferModifier == 0) return;\\n\\n // Iterate over all assets in the Vault and allocate to the appropriate\\n // strategy\\n uint256 assetCount = allAssets.length;\\n for (uint256 i = 0; i < assetCount; ++i) {\\n IERC20 asset = IERC20(allAssets[i]);\\n uint256 assetBalance = asset.balanceOf(address(this));\\n // No balance, nothing to do here\\n if (assetBalance == 0) continue;\\n\\n // Multiply the balance by the vault buffer modifier and truncate\\n // to the scale of the asset decimals\\n uint256 allocateAmount = assetBalance.mulTruncate(\\n vaultBufferModifier\\n );\\n\\n address depositStrategyAddr = assetDefaultStrategies[\\n address(asset)\\n ];\\n\\n if (depositStrategyAddr != address(0) && allocateAmount > 0) {\\n IStrategy strategy = IStrategy(depositStrategyAddr);\\n // Transfer asset to Strategy and call deposit method to\\n // mint or take required action\\n asset.safeTransfer(address(strategy), allocateAmount);\\n strategy.deposit(address(asset), allocateAmount);\\n emit AssetAllocated(\\n address(asset),\\n depositStrategyAddr,\\n allocateAmount\\n );\\n }\\n }\\n }\\n\\n /**\\n * @notice Calculate the total value of assets held by the Vault and all\\n * strategies and update the supply of OTokens.\\n */\\n function rebase() external virtual nonReentrant {\\n _rebase();\\n }\\n\\n /**\\n * @dev Calculate the total value of assets held by the Vault and all\\n * strategies and update the supply of OTokens, optionally sending a\\n * portion of the yield to the trustee.\\n * @return totalUnits Total balance of Vault in units\\n */\\n function _rebase() internal whenNotRebasePaused returns (uint256) {\\n uint256 supply = oUSD.totalSupply();\\n uint256 vaultValue = _totalValue();\\n // If no supply yet, do not rebase\\n if (supply == 0) {\\n return vaultValue;\\n }\\n\\n // Calculate yield and new supply\\n (uint256 yield, uint256 targetRate) = _nextYield(supply, vaultValue);\\n uint256 newSupply = supply + yield;\\n // Only rebase upwards and if we have enough backing funds\\n if (newSupply <= supply || newSupply > vaultValue) {\\n return vaultValue;\\n }\\n\\n rebasePerSecondTarget = uint64(_min(targetRate, type(uint64).max));\\n lastRebase = uint64(block.timestamp); // Intentional cast\\n\\n // Fee collection on yield\\n address _trusteeAddress = trusteeAddress; // gas savings\\n uint256 fee = 0;\\n if (_trusteeAddress != address(0)) {\\n fee = (yield * trusteeFeeBps) / 1e4;\\n if (fee > 0) {\\n require(fee < yield, \\\"Fee must not be greater than yield\\\");\\n oUSD.mint(_trusteeAddress, fee);\\n }\\n }\\n emit YieldDistribution(_trusteeAddress, yield, fee);\\n\\n // Only ratchet OToken supply upwards\\n // Final check uses latest totalSupply\\n if (newSupply > oUSD.totalSupply()) {\\n oUSD.changeSupply(newSupply);\\n }\\n return vaultValue;\\n }\\n\\n /**\\n * @notice Calculates the amount that would rebase at next rebase.\\n * This is before any fees.\\n * @return yield amount of expected yield\\n */\\n function previewYield() external view returns (uint256 yield) {\\n (yield, ) = _nextYield(oUSD.totalSupply(), _totalValue());\\n return yield;\\n }\\n\\n function _nextYield(uint256 supply, uint256 vaultValue)\\n internal\\n view\\n virtual\\n returns (uint256 yield, uint256 targetRate)\\n {\\n uint256 nonRebasing = oUSD.nonRebasingSupply();\\n uint256 rebasing = supply - nonRebasing;\\n uint256 elapsed = block.timestamp - lastRebase;\\n targetRate = rebasePerSecondTarget;\\n\\n if (\\n elapsed == 0 || // Yield only once per block.\\n rebasing == 0 || // No yield if there are no rebasing tokens to give it to.\\n supply > vaultValue || // No yield if we do not have yield to give.\\n block.timestamp >= type(uint64).max // No yield if we are too far in the future to calculate it correctly.\\n ) {\\n return (0, targetRate);\\n }\\n\\n // Start with the full difference available\\n yield = vaultValue - supply;\\n\\n // Cap via optional automatic duration smoothing\\n uint256 _dripDuration = dripDuration;\\n if (_dripDuration > 1) {\\n // If we are able to sustain an increased drip rate for\\n // double the duration, then increase the target drip rate\\n targetRate = _max(targetRate, yield / (_dripDuration * 2));\\n // If we cannot sustain the target rate any more,\\n // then rebase what we can, and reduce the target\\n targetRate = _min(targetRate, yield / _dripDuration);\\n // drip at the new target rate\\n yield = _min(yield, targetRate * elapsed);\\n }\\n\\n // Cap per second. elapsed is not 1e18 denominated\\n yield = _min(yield, (rebasing * elapsed * rebasePerSecondMax) / 1e18);\\n\\n // Cap at a hard max per rebase, to avoid long durations resulting in huge rebases\\n yield = _min(yield, (rebasing * MAX_REBASE) / 1e18);\\n\\n return (yield, targetRate);\\n }\\n\\n /**\\n * @notice Determine the total value of assets held by the vault and its\\n * strategies.\\n * @return value Total value in USD/ETH (1e18)\\n */\\n function totalValue() external view virtual returns (uint256 value) {\\n value = _totalValue();\\n }\\n\\n /**\\n * @dev Internal Calculate the total value of the assets held by the\\n * vault and its strategies.\\n * @return value Total value in USD/ETH (1e18)\\n */\\n function _totalValue() internal view virtual returns (uint256 value) {\\n return _totalValueInVault() + _totalValueInStrategies();\\n }\\n\\n /**\\n * @dev Internal to calculate total value of all assets held in Vault.\\n * @return value Total value in USD/ETH (1e18)\\n */\\n function _totalValueInVault()\\n internal\\n view\\n virtual\\n returns (uint256 value)\\n {\\n uint256 assetCount = allAssets.length;\\n for (uint256 y; y < assetCount; ++y) {\\n address assetAddr = allAssets[y];\\n uint256 balance = IERC20(assetAddr).balanceOf(address(this));\\n if (balance > 0) {\\n value += _toUnits(balance, assetAddr);\\n }\\n }\\n }\\n\\n /**\\n * @dev Internal to calculate total value of all assets held in Strategies.\\n * @return value Total value in USD/ETH (1e18)\\n */\\n function _totalValueInStrategies() internal view returns (uint256 value) {\\n uint256 stratCount = allStrategies.length;\\n for (uint256 i = 0; i < stratCount; ++i) {\\n value = value + _totalValueInStrategy(allStrategies[i]);\\n }\\n }\\n\\n /**\\n * @dev Internal to calculate total value of all assets held by strategy.\\n * @param _strategyAddr Address of the strategy\\n * @return value Total value in USD/ETH (1e18)\\n */\\n function _totalValueInStrategy(address _strategyAddr)\\n internal\\n view\\n returns (uint256 value)\\n {\\n IStrategy strategy = IStrategy(_strategyAddr);\\n uint256 assetCount = allAssets.length;\\n for (uint256 y; y < assetCount; ++y) {\\n address assetAddr = allAssets[y];\\n if (strategy.supportsAsset(assetAddr)) {\\n uint256 balance = strategy.checkBalance(assetAddr);\\n if (balance > 0) {\\n value += _toUnits(balance, assetAddr);\\n }\\n }\\n }\\n }\\n\\n /**\\n * @notice Get the balance of an asset held in Vault and all strategies.\\n * @param _asset Address of asset\\n * @return uint256 Balance of asset in decimals of asset\\n */\\n function checkBalance(address _asset) external view returns (uint256) {\\n return _checkBalance(_asset);\\n }\\n\\n /**\\n * @notice Get the balance of an asset held in Vault and all strategies.\\n * @param _asset Address of asset\\n * @return balance Balance of asset in decimals of asset\\n */\\n function _checkBalance(address _asset)\\n internal\\n view\\n virtual\\n returns (uint256 balance)\\n {\\n IERC20 asset = IERC20(_asset);\\n balance = asset.balanceOf(address(this));\\n uint256 stratCount = allStrategies.length;\\n for (uint256 i = 0; i < stratCount; ++i) {\\n IStrategy strategy = IStrategy(allStrategies[i]);\\n if (strategy.supportsAsset(_asset)) {\\n balance = balance + strategy.checkBalance(_asset);\\n }\\n }\\n }\\n\\n /**\\n * @notice Calculate the outputs for a redeem function, i.e. the mix of\\n * coins that will be returned\\n */\\n function calculateRedeemOutputs(uint256 _amount)\\n external\\n view\\n returns (uint256[] memory)\\n {\\n return _calculateRedeemOutputs(_amount);\\n }\\n\\n /**\\n * @dev Calculate the outputs for a redeem function, i.e. the mix of\\n * coins that will be returned.\\n * @return outputs Array of amounts respective to the supported assets\\n */\\n function _calculateRedeemOutputs(uint256 _amount)\\n internal\\n view\\n virtual\\n returns (uint256[] memory outputs)\\n {\\n // We always give out coins in proportion to how many we have,\\n // Now if all coins were the same value, this math would easy,\\n // just take the percentage of each coin, and multiply by the\\n // value to be given out. But if coins are worth more than $1,\\n // then we would end up handing out too many coins. We need to\\n // adjust by the total value of coins.\\n //\\n // To do this, we total up the value of our coins, by their\\n // percentages. Then divide what we would otherwise give out by\\n // this number.\\n //\\n // Let say we have 100 DAI at $1.06 and 200 USDT at $1.00.\\n // So for every 1 DAI we give out, we'll be handing out 2 USDT\\n // Our total output ratio is: 33% * 1.06 + 66% * 1.00 = 1.02\\n //\\n // So when calculating the output, we take the percentage of\\n // each coin, times the desired output value, divided by the\\n // totalOutputRatio.\\n //\\n // For example, withdrawing: 30 OUSD:\\n // DAI 33% * 30 / 1.02 = 9.80 DAI\\n // USDT = 66 % * 30 / 1.02 = 19.60 USDT\\n //\\n // Checking these numbers:\\n // 9.80 DAI * 1.06 = $10.40\\n // 19.60 USDT * 1.00 = $19.60\\n //\\n // And so the user gets $10.40 + $19.60 = $30 worth of value.\\n\\n uint256 assetCount = allAssets.length;\\n uint256[] memory assetUnits = new uint256[](assetCount);\\n uint256[] memory assetBalances = new uint256[](assetCount);\\n outputs = new uint256[](assetCount);\\n\\n // Calculate redeem fee\\n if (redeemFeeBps > 0) {\\n uint256 redeemFee = _amount.mulTruncateScale(redeemFeeBps, 1e4);\\n _amount = _amount - redeemFee;\\n }\\n\\n // Calculate assets balances and decimals once,\\n // for a large gas savings.\\n uint256 totalUnits = 0;\\n for (uint256 i = 0; i < assetCount; ++i) {\\n address assetAddr = allAssets[i];\\n uint256 balance = _checkBalance(assetAddr);\\n assetBalances[i] = balance;\\n assetUnits[i] = _toUnits(balance, assetAddr);\\n totalUnits = totalUnits + assetUnits[i];\\n }\\n // Calculate totalOutputRatio\\n uint256 totalOutputRatio = 0;\\n for (uint256 i = 0; i < assetCount; ++i) {\\n uint256 unitPrice = _toUnitPrice(allAssets[i], false);\\n uint256 ratio = (assetUnits[i] * unitPrice) / totalUnits;\\n totalOutputRatio = totalOutputRatio + ratio;\\n }\\n // Calculate final outputs\\n uint256 factor = _amount.divPrecisely(totalOutputRatio);\\n for (uint256 i = 0; i < assetCount; ++i) {\\n outputs[i] = (assetBalances[i] * factor) / totalUnits;\\n }\\n }\\n\\n /***************************************\\n Pricing\\n ****************************************/\\n\\n /**\\n * @notice Returns the total price in 18 digit units for a given asset.\\n * Never goes above 1, since that is how we price mints.\\n * @param asset address of the asset\\n * @return price uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed\\n */\\n function priceUnitMint(address asset)\\n external\\n view\\n returns (uint256 price)\\n {\\n /* need to supply 1 asset unit in asset's decimals and can not just hard-code\\n * to 1e18 and ignore calling `_toUnits` since we need to consider assets\\n * with the exchange rate\\n */\\n uint256 units = _toUnits(\\n uint256(1e18).scaleBy(_getDecimals(asset), 18),\\n asset\\n );\\n price = (_toUnitPrice(asset, true) * units) / 1e18;\\n }\\n\\n /**\\n * @notice Returns the total price in 18 digit unit for a given asset.\\n * Never goes below 1, since that is how we price redeems\\n * @param asset Address of the asset\\n * @return price uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed\\n */\\n function priceUnitRedeem(address asset)\\n external\\n view\\n returns (uint256 price)\\n {\\n /* need to supply 1 asset unit in asset's decimals and can not just hard-code\\n * to 1e18 and ignore calling `_toUnits` since we need to consider assets\\n * with the exchange rate\\n */\\n uint256 units = _toUnits(\\n uint256(1e18).scaleBy(_getDecimals(asset), 18),\\n asset\\n );\\n price = (_toUnitPrice(asset, false) * units) / 1e18;\\n }\\n\\n /***************************************\\n Utils\\n ****************************************/\\n\\n /**\\n * @dev Convert a quantity of a token into 1e18 fixed decimal \\\"units\\\"\\n * in the underlying base (USD/ETH) used by the vault.\\n * Price is not taken into account, only quantity.\\n *\\n * Examples of this conversion:\\n *\\n * - 1e18 DAI becomes 1e18 units (same decimals)\\n * - 1e6 USDC becomes 1e18 units (decimal conversion)\\n * - 1e18 rETH becomes 1.2e18 units (exchange rate conversion)\\n *\\n * @param _raw Quantity of asset\\n * @param _asset Core Asset address\\n * @return value 1e18 normalized quantity of units\\n */\\n function _toUnits(uint256 _raw, address _asset)\\n internal\\n view\\n returns (uint256)\\n {\\n UnitConversion conversion = assets[_asset].unitConversion;\\n if (conversion == UnitConversion.DECIMALS) {\\n return _raw.scaleBy(18, _getDecimals(_asset));\\n } else if (conversion == UnitConversion.GETEXCHANGERATE) {\\n uint256 exchangeRate = IGetExchangeRateToken(_asset)\\n .getExchangeRate();\\n return (_raw * exchangeRate) / 1e18;\\n } else {\\n revert(\\\"Unsupported conversion type\\\");\\n }\\n }\\n\\n /**\\n * @dev Returns asset's unit price accounting for different asset types\\n * and takes into account the context in which that price exists -\\n * - mint or redeem.\\n *\\n * Note: since we are returning the price of the unit and not the one of the\\n * asset (see comment above how 1 rETH exchanges for 1.2 units) we need\\n * to make the Oracle price adjustment as well since we are pricing the\\n * units and not the assets.\\n *\\n * The price also snaps to a \\\"full unit price\\\" in case a mint or redeem\\n * action would be unfavourable to the protocol.\\n *\\n */\\n function _toUnitPrice(address _asset, bool isMint)\\n internal\\n view\\n returns (uint256 price)\\n {\\n UnitConversion conversion = assets[_asset].unitConversion;\\n price = IOracle(priceProvider).price(_asset);\\n\\n if (conversion == UnitConversion.GETEXCHANGERATE) {\\n uint256 exchangeRate = IGetExchangeRateToken(_asset)\\n .getExchangeRate();\\n price = (price * 1e18) / exchangeRate;\\n } else if (conversion != UnitConversion.DECIMALS) {\\n revert(\\\"Unsupported conversion type\\\");\\n }\\n\\n /* At this stage the price is already adjusted to the unit\\n * so the price checks are agnostic to underlying asset being\\n * pegged to a USD or to an ETH or having a custom exchange rate.\\n */\\n require(price <= MAX_UNIT_PRICE_DRIFT, \\\"Vault: Price exceeds max\\\");\\n require(price >= MIN_UNIT_PRICE_DRIFT, \\\"Vault: Price under min\\\");\\n\\n if (isMint) {\\n /* Never price a normalized unit price for more than one\\n * unit of OETH/OUSD when minting.\\n */\\n if (price > 1e18) {\\n price = 1e18;\\n }\\n require(price >= MINT_MINIMUM_UNIT_PRICE, \\\"Asset price below peg\\\");\\n } else {\\n /* Never give out more than 1 normalized unit amount of assets\\n * for one unit of OETH/OUSD when redeeming.\\n */\\n if (price < 1e18) {\\n price = 1e18;\\n }\\n }\\n }\\n\\n /**\\n * @dev Get the number of decimals of a token asset\\n * @param _asset Address of the asset\\n * @return decimals number of decimals\\n */\\n function _getDecimals(address _asset)\\n internal\\n view\\n returns (uint256 decimals)\\n {\\n decimals = assets[_asset].decimals;\\n require(decimals > 0, \\\"Decimals not cached\\\");\\n }\\n\\n /**\\n * @notice Return the number of assets supported by the Vault.\\n */\\n function getAssetCount() public view returns (uint256) {\\n return allAssets.length;\\n }\\n\\n /**\\n * @notice Gets the vault configuration of a supported asset.\\n * @param _asset Address of the token asset\\n */\\n function getAssetConfig(address _asset)\\n public\\n view\\n returns (Asset memory config)\\n {\\n config = assets[_asset];\\n }\\n\\n /**\\n * @notice Return all vault asset addresses in order\\n */\\n function getAllAssets() external view returns (address[] memory) {\\n return allAssets;\\n }\\n\\n /**\\n * @notice Return the number of strategies active on the Vault.\\n */\\n function getStrategyCount() external view returns (uint256) {\\n return allStrategies.length;\\n }\\n\\n /**\\n * @notice Return the array of all strategies\\n */\\n function getAllStrategies() external view returns (address[] memory) {\\n return allStrategies;\\n }\\n\\n /**\\n * @notice Returns whether the vault supports the asset\\n * @param _asset address of the asset\\n * @return true if supported\\n */\\n function isSupportedAsset(address _asset) external view returns (bool) {\\n return assets[_asset].isSupported;\\n }\\n\\n function ADMIN_IMPLEMENTATION() external view returns (address adminImpl) {\\n bytes32 slot = adminImplPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n adminImpl := sload(slot)\\n }\\n }\\n\\n /**\\n * @dev Falldown to the admin implementation\\n * @notice This is a catch all for all functions not declared in core\\n */\\n // solhint-disable-next-line no-complex-fallback\\n fallback() external {\\n bytes32 slot = adminImplPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n // Copy msg.data. We take full control of memory in this inline assembly\\n // block because it will not return to Solidity code. We overwrite the\\n // Solidity scratch pad at memory position 0.\\n calldatacopy(0, 0, calldatasize())\\n\\n // Call the implementation.\\n // out and outsize are 0 because we don't know the size yet.\\n let result := delegatecall(\\n gas(),\\n sload(slot),\\n 0,\\n calldatasize(),\\n 0,\\n 0\\n )\\n\\n // Copy the returned data.\\n returndatacopy(0, 0, returndatasize())\\n\\n switch result\\n // delegatecall returns 0 on error.\\n case 0 {\\n revert(0, returndatasize())\\n }\\n default {\\n return(0, returndatasize())\\n }\\n }\\n }\\n\\n function abs(int256 x) private pure returns (uint256) {\\n require(x < int256(MAX_INT), \\\"Amount too high\\\");\\n return x >= 0 ? uint256(x) : uint256(-x);\\n }\\n\\n function _min(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a < b ? a : b;\\n }\\n\\n function _max(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a > b ? a : b;\\n }\\n}\\n\",\"keccak256\":\"0xe1ee6be83431d7928dccfe9954793cf33d8e1f3879b53b8ca15b6957eb0a6650\",\"license\":\"BUSL-1.1\"},\"contracts/vault/VaultInitializer.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title OToken VaultInitializer contract\\n * @notice The Vault contract initializes the vault.\\n * @author Origin Protocol Inc\\n */\\n\\nimport \\\"./VaultStorage.sol\\\";\\n\\ncontract VaultInitializer is VaultStorage {\\n function initialize(address _priceProvider, address _oToken)\\n external\\n onlyGovernor\\n initializer\\n {\\n require(_priceProvider != address(0), \\\"PriceProvider address is zero\\\");\\n require(_oToken != address(0), \\\"oToken address is zero\\\");\\n\\n oUSD = OUSD(_oToken);\\n\\n priceProvider = _priceProvider;\\n\\n rebasePaused = false;\\n capitalPaused = true;\\n\\n // Initial redeem fee of 0 basis points\\n redeemFeeBps = 0;\\n // Initial Vault buffer of 0%\\n vaultBuffer = 0;\\n // Initial allocate threshold of 25,000 OUSD\\n autoAllocateThreshold = 25000e18;\\n // Threshold for rebasing\\n rebaseThreshold = 1000e18;\\n // Initialize all strategies\\n allStrategies = new address[](0);\\n // Start with drip duration disabled\\n dripDuration = 1;\\n }\\n}\\n\",\"keccak256\":\"0x5555eb1e24efd4b14a7c46780e02ff58d6bdd063801a1f74715ea8a5426c634a\",\"license\":\"BUSL-1.1\"},\"contracts/vault/VaultStorage.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title OToken VaultStorage contract\\n * @notice The VaultStorage contract defines the storage for the Vault contracts\\n * @author Origin Protocol Inc\\n */\\n\\nimport { IERC20 } from \\\"@openzeppelin/contracts/token/ERC20/IERC20.sol\\\";\\nimport { SafeERC20 } from \\\"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\\\";\\nimport { Address } from \\\"@openzeppelin/contracts/utils/Address.sol\\\";\\n\\nimport { IStrategy } from \\\"../interfaces/IStrategy.sol\\\";\\nimport { Governable } from \\\"../governance/Governable.sol\\\";\\nimport { OUSD } from \\\"../token/OUSD.sol\\\";\\nimport { Initializable } from \\\"../utils/Initializable.sol\\\";\\nimport \\\"../utils/Helpers.sol\\\";\\n\\ncontract VaultStorage is Initializable, Governable {\\n using SafeERC20 for IERC20;\\n\\n event AssetSupported(address _asset);\\n event AssetRemoved(address _asset);\\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\\n event StrategyApproved(address _addr);\\n event StrategyRemoved(address _addr);\\n event Mint(address _addr, uint256 _value);\\n event Redeem(address _addr, uint256 _value);\\n event CapitalPaused();\\n event CapitalUnpaused();\\n event RebasePaused();\\n event RebaseUnpaused();\\n event VaultBufferUpdated(uint256 _vaultBuffer);\\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\\n event PriceProviderUpdated(address _priceProvider);\\n event AllocateThresholdUpdated(uint256 _threshold);\\n event RebaseThresholdUpdated(uint256 _threshold);\\n event StrategistUpdated(address _address);\\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\\n event TrusteeFeeBpsChanged(uint256 _basis);\\n event TrusteeAddressChanged(address _address);\\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\\n event SwapperChanged(address _address);\\n event SwapAllowedUndervalueChanged(uint256 _basis);\\n event SwapSlippageChanged(address _asset, uint256 _basis);\\n event Swapped(\\n address indexed _fromAsset,\\n address indexed _toAsset,\\n uint256 _fromAssetAmount,\\n uint256 _toAssetAmount\\n );\\n event StrategyAddedToMintWhitelist(address indexed strategy);\\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\\n event DripDurationChanged(uint256 dripDuration);\\n event WithdrawalRequested(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount,\\n uint256 _queued\\n );\\n event WithdrawalClaimed(\\n address indexed _withdrawer,\\n uint256 indexed _requestId,\\n uint256 _amount\\n );\\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\\n\\n // Since we are proxy, all state should be uninitalized.\\n // Since this storage contract does not have logic directly on it\\n // we should not be checking for to see if these variables can be constant.\\n // slither-disable-start uninitialized-state\\n // slither-disable-start constable-states\\n\\n // Assets supported by the Vault, i.e. Stablecoins\\n enum UnitConversion {\\n DECIMALS,\\n GETEXCHANGERATE\\n }\\n // Changed to fit into a single storage slot so the decimals needs to be recached\\n struct Asset {\\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\\n // redeeming or checking balance of assets.\\n bool isSupported;\\n UnitConversion unitConversion;\\n uint8 decimals;\\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\\n // For example 40 == 0.4% slippage\\n uint16 allowedOracleSlippageBps;\\n }\\n\\n /// @dev mapping of supported vault assets to their configuration\\n mapping(address => Asset) internal assets;\\n /// @dev list of all assets supported by the vault.\\n address[] internal allAssets;\\n\\n // Strategies approved for use by the Vault\\n struct Strategy {\\n bool isSupported;\\n uint256 _deprecated; // Deprecated storage slot\\n }\\n /// @dev mapping of strategy contracts to their configuration\\n mapping(address => Strategy) public strategies;\\n /// @dev list of all vault strategies\\n address[] internal allStrategies;\\n\\n /// @notice Address of the Oracle price provider contract\\n address public priceProvider;\\n /// @notice pause rebasing if true\\n bool public rebasePaused;\\n /// @notice pause operations that change the OToken supply.\\n /// eg mint, redeem, allocate, mint/burn for strategy\\n bool public capitalPaused;\\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\\n uint256 public redeemFeeBps;\\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\\n uint256 public vaultBuffer;\\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\\n uint256 public autoAllocateThreshold;\\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\\n uint256 public rebaseThreshold;\\n\\n /// @dev Address of the OToken token. eg OUSD or OETH.\\n OUSD public oUSD;\\n\\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\\n // keccak256(\\\"OUSD.vault.governor.admin.impl\\\");\\n bytes32 public constant adminImplPosition =\\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\\n\\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\\n address private _deprecated_rebaseHooksAddr = address(0);\\n\\n /// @dev Deprecated: Address of Uniswap\\n address private _deprecated_uniswapAddr = address(0);\\n\\n /// @notice Address of the Strategist\\n address public strategistAddr = address(0);\\n\\n /// @notice Mapping of asset address to the Strategy that they should automatically\\n // be allocated to\\n mapping(address => address) public assetDefaultStrategies;\\n\\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\\n uint256 public maxSupplyDiff;\\n\\n /// @notice Trustee contract that can collect a percentage of yield\\n address public trusteeAddress;\\n\\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\\n uint256 public trusteeFeeBps;\\n\\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\\n address[] private _deprecated_swapTokens;\\n\\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\\n\\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\\n\\n address public ousdMetaStrategy;\\n\\n /// @notice How much OTokens are currently minted by the strategy\\n int256 public netOusdMintedForStrategy;\\n\\n /// @notice How much net total OTokens are allowed to be minted by all strategies\\n uint256 public netOusdMintForStrategyThreshold;\\n\\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\\n\\n /// @notice Collateral swap configuration.\\n /// @dev is packed into a single storage slot to save gas.\\n struct SwapConfig {\\n // Contract that swaps the vault's collateral assets\\n address swapper;\\n // Max allowed percentage the total value can drop below the total supply in basis points.\\n // For example 100 == 1%\\n uint16 allowedUndervalueBps;\\n }\\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\\n\\n // List of strategies that can mint oTokens directly\\n // Used in OETHBaseVaultCore\\n mapping(address => bool) public isMintWhitelistedStrategy;\\n\\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\\n address private _deprecated_dripper;\\n\\n /// Withdrawal Queue Storage /////\\n\\n struct WithdrawalQueueMetadata {\\n // cumulative total of all withdrawal requests included the ones that have already been claimed\\n uint128 queued;\\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\\n uint128 claimable;\\n // total of all the requests that have been claimed\\n uint128 claimed;\\n // index of the next withdrawal request starting at 0\\n uint128 nextWithdrawalIndex;\\n }\\n\\n /// @notice Global metadata for the withdrawal queue including:\\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\\n /// claimed - total of all the requests that have been claimed\\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\\n\\n struct WithdrawalRequest {\\n address withdrawer;\\n bool claimed;\\n uint40 timestamp; // timestamp of the withdrawal request\\n // Amount of oTokens to redeem. eg OETH\\n uint128 amount;\\n // cumulative total of all withdrawal requests including this one.\\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\\n uint128 queued;\\n }\\n\\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\\n\\n /// @notice Sets a minimum delay that is required to elapse between\\n /// requesting async withdrawals and claiming the request.\\n /// When set to 0 async withdrawals are disabled.\\n uint256 public withdrawalClaimDelay;\\n\\n /// @notice Time in seconds that the vault last rebased yield.\\n uint64 public lastRebase;\\n\\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\\n uint64 public dripDuration;\\n\\n /// @notice max rebase percentage per second\\n /// Can be used to set maximum yield of the protocol,\\n /// spreading out yield over time\\n uint64 public rebasePerSecondMax;\\n\\n /// @notice target rebase rate limit, based on past rates and funds available.\\n uint64 public rebasePerSecondTarget;\\n\\n uint256 internal constant MAX_REBASE = 0.02 ether;\\n uint256 internal constant MAX_REBASE_PER_SECOND =\\n uint256(0.05 ether) / 1 days;\\n\\n // For future use\\n uint256[43] private __gap;\\n\\n // slither-disable-end constable-states\\n // slither-disable-end uninitialized-state\\n\\n /**\\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\\n * @param newImpl address of the implementation\\n */\\n function setAdminImpl(address newImpl) external onlyGovernor {\\n require(\\n Address.isContract(newImpl),\\n \\\"new implementation is not a contract\\\"\\n );\\n bytes32 position = adminImplPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newImpl)\\n }\\n }\\n}\\n\",\"keccak256\":\"0xe8c1056879e4d67e0085a30a525a4cb23b954ade0f22fce502278f35b9c69d3b\",\"license\":\"BUSL-1.1\"}},\"version\":1}", + "bytecode": "0x603d80546001600160a01b0319908116909155603e805482169055603f8054909116905560e0604052600060a081905260c052604880546001600160b01b031916905534801561004e57600080fd5b506040516142e73803806142e783398101604081905261006d9161007e565b6001600160a01b03166080526100ae565b60006020828403121561009057600080fd5b81516001600160a01b03811681146100a757600080fd5b9392505050565b6080516141b56101326000396000818161051001528181610ac801528181610b3201528181610f2f01528181611a4c01528181611b180152818161229f0152818161294b01528181612a5201528181612c9501528181612da001528181612ea301528181612eee01528181612f560152818161335f015261394801526141b56000f3fe608060405234801561001057600080fd5b506004361061038e5760003560e01c806367bd7ba3116101de578063af14052c1161010f578063d38bfff4116100ad578063ea33b8e41161007c578063ea33b8e4146108f9578063ef08edc214610901578063f844443614610916578063fc0cfeee146109295761038e565b8063d38bfff4146108c1578063d4c3eea0146108d4578063e45cc9f0146108dc578063e6cc5432146108e55761038e565b8063bb7a632e116100e9578063bb7a632e14610882578063c3b288641461089c578063c7af3352146108a4578063cc2fe94b146108ac5761038e565b8063af14052c1461085f578063b888879e14610867578063b9b17f9f1461087a5761038e565b80639be918e61161017c578063a0aead4d11610156578063a0aead4d14610813578063a403e4d51461081b578063ab80dafb14610844578063abaa9916146108575761038e565b80639be918e6146107b65780639ee679e8146107e25780639fa1826e1461080a5761038e565b80637a2202f3116101b85780637a2202f3146106e95780637cbc2373146106f25780638e510b5214610705578063937b25811461070e5761038e565b806367bd7ba3146106965780636ec3ab67146106b657806378f353a1146106d65761038e565b806345e4213b116102c357806353ca9f24116102615780635b60f9fc116102305780635b60f9fc146106555780635d36b190146106685780635f515226146106705780636217f3ea146106835761038e565b806353ca9f241461061257806354c6d85814610626578063570d8e1d1461062f5780635802a172146106425761038e565b806349c1d54d1161029d57806349c1d54d146105aa5780634d5f4629146105bd578063527e83a8146105ef57806352d38e5d146106095761038e565b806345e4213b1461056d578063485cc9551461057657806348e30f54146105895761038e565b806331e19cfa116103305780633b8fe28d1161030a5780633b8fe28d146104f85780633fc8cef31461050b57806344c54707146105325780634530820a1461053a5761038e565b806331e19cfa1461044d578063362bd1a31461045557806339ebf823146104b45761038e565b806318ce56bd1161036c57806318ce56bd146104135780631edfe3da14610426578063207134b01461042f5780632acada4d146104385761038e565b806309f6442c146103c45780630c340a24146103e0578063156e29f614610400575b600080516020614140833981519152366000803760008036600084545af43d6000803e8080156103bd573d6000f35b3d6000fd5b005b6103cd60385481565b6040519081526020015b60405180910390f35b6103e861093c565b6040516001600160a01b0390911681526020016103d7565b6103c261040e366004613ba5565b610959565b6045546103e8906001600160a01b031681565b6103cd60395481565b6103cd60435481565b6104406109d7565b6040516103d79190613bd8565b6036546103cd565b604b54604c54610481916001600160801b0380821692600160801b928390048216928183169291041684565b604080516001600160801b03958616815293851660208501529184169183019190915290911660608201526080016103d7565b6104e16104c2366004613c24565b6035602052600090815260409020805460019091015460ff9091169082565b6040805192151583526020830191909152016103d7565b6103cd610506366004613c24565b610a39565b6103e87f000000000000000000000000000000000000000000000000000000000000000081565b6103c2610a94565b61055d610548366004613c24565b60496020526000908152604090205460ff1681565b60405190151581526020016103d7565b6103cd604e5481565b6103c2610584366004613c3f565b610bd4565b61059c610597366004613c72565b610df5565b6040516103d7929190613d23565b6042546103e8906001600160a01b031681565b604f546105d790600160c01b90046001600160401b031681565b6040516001600160401b0390911681526020016103d7565b604f546105d790600160801b90046001600160401b031681565b6103cd603b5481565b60375461055d90600160a01b900460ff1681565b6103cd607b5481565b603f546103e8906001600160a01b031681565b603c546103e8906001600160a01b031681565b6103cd610663366004613c24565b610f6c565b6103c2610f95565b6103cd61067e366004613c24565b61103b565b6103c2610691366004613d45565b61104c565b6106a96106a4366004613d45565b6111d0565b6040516103d79190613d5e565b6106c96106c4366004613c24565b6111db565b6040516103d79190613d87565b604f546105d7906001600160401b031681565b6103cd60475481565b6103c2610700366004613ddd565b611281565b6103cd60415481565b61076f61071c366004613d45565b604d60205260009081526040902080546001909101546001600160a01b03821691600160a01b810460ff1691600160a81b90910464ffffffffff16906001600160801b0380821691600160801b90041685565b604080516001600160a01b039096168652931515602086015264ffffffffff909216928401929092526001600160801b03918216606084015216608082015260a0016103d7565b61055d6107c4366004613c24565b6001600160a01b031660009081526033602052604090205460ff1690565b6107f56107f0366004613d45565b6112f4565b604080519283526020830191909152016103d7565b6103cd603a5481565b6034546103cd565b6103e8610829366004613c24565b6040602081905260009182529020546001600160a01b031681565b6103c2610852366004613d45565b6115b1565b6103c2611700565b6103c2611778565b6037546103e8906001600160a01b031681565b6103c26117be565b604f546105d790600160401b90046001600160401b031681565b6104406117c6565b61055d611826565b600080516020614140833981519152546103e8565b6103c26108cf366004613c24565b611857565b6103cd6118fb565b6103cd60465481565b60375461055d90600160a81b900460ff1681565b6103cd611905565b6103cd60008051602061414083398151915281565b6103cd610924366004613d45565b611994565b6103c2610937366004613c24565b611a86565b60006109546000805160206141608339815191525490565b905090565b603754600160a81b900460ff161561098c5760405162461bcd60e51b815260040161098390613dff565b60405180910390fd5b600080516020614120833981519152805460011981016109be5760405162461bcd60e51b815260040161098390613e27565b600282556109cd858585611b16565b5060019055505050565b60606034805480602002602001604051908101604052809291908181526020018280548015610a2f57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610a11575b5050505050905090565b600080610a62610a5c610a4b85611d2f565b670de0b6b3a7640000906012611d99565b84611dfb565b9050670de0b6b3a764000081610a79856001611f46565b610a839190613e65565b610a8d9190613e7c565b9392505050565b610a9c611826565b610ab85760405162461bcd60e51b815260040161098390613e9e565b60345460005b81811015610b2f577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031660348281548110610b0357610b03613ed5565b6000918252602090912001546001600160a01b031603610b2757607b819055610b2f565b600101610abe565b507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166034607b5481548110610b6f57610b6f613ed5565b6000918252602090912001546001600160a01b031614610bd15760405162461bcd60e51b815260206004820152601860248201527f496e76616c6964205745544820417373657420496e64657800000000000000006044820152606401610983565b50565b610bdc611826565b610bf85760405162461bcd60e51b815260040161098390613e9e565b600054610100900460ff1680610c11575060005460ff16155b610c745760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610983565b600054610100900460ff16158015610c96576000805461ffff19166101011790555b6001600160a01b038316610cec5760405162461bcd60e51b815260206004820152601d60248201527f507269636550726f76696465722061646472657373206973207a65726f0000006044820152606401610983565b6001600160a01b038216610d3b5760405162461bcd60e51b81526020600482015260166024820152756f546f6b656e2061646472657373206973207a65726f60501b6044820152606401610983565b603c80546001600160a01b038481166001600160a01b031990921691909117909155603780546001600160b01b03191691851691909117600160a81b17905560006038819055603981905569054b40b1f852bda00000603a55683635c9adc5dea00000603b556040805191825260208201908190529051610dbe91603691613b1d565b50604f80546fffffffffffffffff00000000000000001916600160401b1790558015610df0576000805461ff00191690555b505050565b603754606090600090600160a81b900460ff1615610e255760405162461bcd60e51b815260040161098390613dff565b60008051602061412083398151915280546001198101610e575760405162461bcd60e51b815260040161098390613e27565b60028255610e63612219565b50846001600160401b03811115610e7c57610e7c613eeb565b604051908082528060200260200182016040528015610ea5578160200160208202803683370190505b50935060005b85811015610f2157610ed4878783818110610ec857610ec8613ed5565b905060200201356123f5565b858281518110610ee657610ee6613ed5565b602002602001018181525050848181518110610f0457610f04613ed5565b602002602001015184610f179190613f01565b9350600101610eab565b50610f566001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633856126eb565b610f5f83612741565b6001825550509250929050565b600080610f7e610a5c610a4b85611d2f565b9050670de0b6b3a764000081610a79856000611f46565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b0316146110305760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b6064820152608401610983565b611039336128e8565b565b600061104682612947565b92915050565b603754600160a81b900460ff16156110765760405162461bcd60e51b815260040161098390613dff565b3360009081526035602052604090205460ff1615156001146110d15760405162461bcd60e51b8152602060048201526014602482015273556e737570706f7274656420737472617465677960601b6044820152606401610983565b3360009081526049602052604090205460ff1615156001146111305760405162461bcd60e51b81526020600482015260186024820152774e6f742077686974656c697374656420737472617465677960401b6044820152606401610983565b7f222838db2794d11532d940e8dec38ae307ed0b63cd97c233322e221f998767a63382604051611161929190613f14565b60405180910390a1603c54604051632770a7eb60e21b81526001600160a01b0390911690639dc29fac9061119b9033908590600401613f14565b600060405180830381600087803b1580156111b557600080fd5b505af11580156111c9573d6000803e3d6000fd5b5050505050565b606061104682612a1a565b604080516080808201835260008083526020808401829052838501829052606084018290526001600160a01b038616825260338152908490208451928301909452835460ff80821615158452939492939184019161010090910416600181111561124757611247613d71565b600181111561125857611258613d71565b8152905462010000810460ff1660208301526301000000900461ffff1660409091015292915050565b603754600160a81b900460ff16156112ab5760405162461bcd60e51b815260040161098390613dff565b600080516020614120833981519152805460011981016112dd5760405162461bcd60e51b815260040161098390613e27565b600282556112eb8484612b5a565b50600190555050565b6037546000908190600160a81b900460ff16156113235760405162461bcd60e51b815260040161098390613dff565b600080516020614120833981519152805460011981016113555760405162461bcd60e51b815260040161098390613e27565b600282556000604e54116113ab5760405162461bcd60e51b815260206004820152601d60248201527f4173796e63207769746864726177616c73206e6f7420656e61626c65640000006044820152606401610983565b604c54604b546001600160801b03600160801b909204821695506113d191879116613f01565b92506113e66113e1856001613f01565b612d29565b604c80546001600160801b03928316600160801b02921691909117905561140c83612d29565b604b80546001600160801b0319166001600160801b03929092169190911790556040805160a081018252338152600060208201524264ffffffffff16918101919091526060810161145c87612d29565b6001600160801b0316815260200161147385612d29565b6001600160801b039081169091526000868152604d602090815260409182902084518154928601518685015164ffffffffff16600160a81b0264ffffffffff60a81b19911515600160a01b026001600160a81b03199095166001600160a01b0393841617949094171692909217815560608501516080909501518416600160801b029490931693909317600190920191909155603c549051632770a7eb60e21b8152911690639dc29fac9061152e9033908990600401613f14565b600060405180830381600087803b15801561154857600080fd5b505af115801561155c573d6000803e3d6000fd5b5050505061156985612741565b6040805186815260208101859052859133917f38e3d972947cfef94205163d483d6287ef27eb312e20cb8e0b13a49989db232e910160405180910390a3600182555050915091565b603754600160a81b900460ff16156115db5760405162461bcd60e51b815260040161098390613dff565b3360009081526035602052604090205460ff1615156001146116365760405162461bcd60e51b8152602060048201526014602482015273556e737570706f7274656420737472617465677960601b6044820152606401610983565b3360009081526049602052604090205460ff1615156001146116955760405162461bcd60e51b81526020600482015260186024820152774e6f742077686974656c697374656420737472617465677960401b6044820152606401610983565b7f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d412139688533826040516116c6929190613f14565b60405180910390a1603c546040516340c10f1960e01b81526001600160a01b03909116906340c10f199061119b9033908590600401613f14565b603754600160a81b900460ff161561172a5760405162461bcd60e51b815260040161098390613dff565b6000805160206141208339815191528054600119810161175c5760405162461bcd60e51b815260040161098390613e27565b60028255611768612219565b50611771612d96565b5060019055565b600080516020614120833981519152805460011981016117aa5760405162461bcd60e51b815260040161098390613e27565b600282556117b6612fbe565b505060019055565b610bd1612219565b60606036805480602002602001604051908101604052809291908181526020018280548015610a2f576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311610a11575050505050905090565b600061183e6000805160206141608339815191525490565b6001600160a01b0316336001600160a01b031614905090565b61185f611826565b61187b5760405162461bcd60e51b815260040161098390613e9e565b6118a3817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b03166118c36000805160206141608339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b6000610954613358565b600061198e603c60009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561195d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119819190613f2d565b611989613358565b613383565b50919050565b603754600090600160a81b900460ff16156119c15760405162461bcd60e51b815260040161098390613dff565b600080516020614120833981519152805460011981016119f35760405162461bcd60e51b815260040161098390613e27565b60028255604b546000858152604d60205260409020600101546001600160801b03600160801b92839004811692909104161115611a3457611a32612219565b505b611a3d846123f5565b9250611a736001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633856126eb565b611a7c83612741565b5060019055919050565b611a8e611826565b611aaa5760405162461bcd60e51b815260040161098390613e9e565b803b611b045760405162461bcd60e51b8152602060048201526024808201527f6e657720696d706c656d656e746174696f6e206973206e6f74206120636f6e746044820152631c9858dd60e21b6064820152608401610983565b60008051602061414083398151915255565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b031614611b975760405162461bcd60e51b815260206004820152601d60248201527f556e737570706f7274656420617373657420666f72206d696e74696e670000006044820152606401610983565b60008211611be75760405162461bcd60e51b815260206004820152601d60248201527f416d6f756e74206d7573742062652067726561746572207468616e20300000006044820152606401610983565b80821015611c375760405162461bcd60e51b815260206004820152601e60248201527f4d696e7420616d6f756e74206c6f776572207468616e206d696e696d756d00006044820152606401610983565b7f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d41213968853383604051611c68929190613f14565b60405180910390a1603754600160a01b900460ff16158015611c8c5750603b548210155b15611c9b57611c99612fbe565b505b603c546040516340c10f1960e01b81526001600160a01b03909116906340c10f1990611ccd9033908690600401613f14565b600060405180830381600087803b158015611ce757600080fd5b505af1158015611cfb573d6000803e3d6000fd5b50611d15925050506001600160a01b038416333085613556565b611d1d612219565b50603a548210610df057610df0612d96565b6001600160a01b03811660009081526033602052604090205462010000900460ff1680611d945760405162461bcd60e51b8152602060048201526013602482015272111958da5b585b1cc81b9bdd0818d858da1959606a1b6044820152606401610983565b919050565b600081831115611dc957611dc2611db08385613f46565b611dbb90600a614040565b8590613594565b9350611df3565b81831015611df357611df0611dde8484613f46565b611de990600a614040565b85906135a0565b93505b509192915050565b6001600160a01b038116600090815260336020526040812054610100900460ff1681816001811115611e2f57611e2f613d71565b03611e5257611e4a6012611e4285611d2f565b869190611d99565b915050611046565b6001816001811115611e6657611e66613d71565b03611ef7576000836001600160a01b031663e6aa216c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611eab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ecf9190613f2d565b9050670de0b6b3a7640000611ee48287613e65565b611eee9190613e7c565b92505050611046565b60405162461bcd60e51b815260206004820152601b60248201527f556e737570706f7274656420636f6e76657273696f6e207479706500000000006044820152606401610983565b5092915050565b6001600160a01b038281166000818152603360205260408082205460375491516315d5220f60e31b81526004810194909452919361010090920460ff169291169063aea9107890602401602060405180830381865afa158015611fad573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fd19190613f2d565b91506001816001811115611fe757611fe7613d71565b03612077576000846001600160a01b031663e6aa216c6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561202c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120509190613f2d565b90508061206584670de0b6b3a7640000613e65565b61206f9190613e7c565b9250506120d8565b600081600181111561208b5761208b613d71565b146120d85760405162461bcd60e51b815260206004820152601b60248201527f556e737570706f7274656420636f6e76657273696f6e207479706500000000006044820152606401610983565b67120a871cc00200008211156121305760405162461bcd60e51b815260206004820152601860248201527f5661756c743a2050726963652065786365656473206d617800000000000000006044820152606401610983565b6709b6e64a8ec600008210156121815760405162461bcd60e51b81526020600482015260166024820152752b30bab63a1d10283934b1b2903ab73232b91036b4b760511b6044820152606401610983565b82156121f857670de0b6b3a76400008211156121a357670de0b6b3a764000091505b670dd99bb65dd700008210156121f35760405162461bcd60e51b815260206004820152601560248201527441737365742070726963652062656c6f772070656760581b6044820152606401610983565b611f3f565b670de0b6b3a7640000821015611f3f5750670de0b6b3a76400009392505050565b60408051608081018252604b546001600160801b03808216808452600160801b92839004821660208501819052604c548084169686019690965292909404166060830152600092839161226b9161404c565b6001600160801b03169050806000036122875760009250505090565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156122ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123129190613f2d565b905060008360400151846020015161232a919061404c565b6001600160801b0316905080821161234757600094505050505090565b60006123538284613f46565b90508084106123625780612364565b835b955060008686602001516001600160801b03166123819190613f01565b905061238c81612d29565b604b80546001600160801b03928316600160801b0292169190911790556040517fee79a0c43d3993055690b54e074b5153e8bae8d1a872b656dedb64aa8f463333906123e49083908a90918252602082015260400190565b60405180910390a150505050505090565b600080604e54116124485760405162461bcd60e51b815260206004820152601d60248201527f4173796e63207769746864726177616c73206e6f7420656e61626c65640000006044820152606401610983565b6000828152604d6020908152604091829020825160a08101845281546001600160a01b038116825260ff600160a01b82041615158285015264ffffffffff600160a81b90910481168286019081526001909301546001600160801b03808216606080860191909152600160801b92839004821660808087019190915288519081018952604b548084168252849004831697810197909752604c54808316988801989098529190960490951694840194909452604e5491519093429261250f92909116613f01565b11156125535760405162461bcd60e51b815260206004820152601360248201527210db185a5b4819195b185e481b9bdd081b595d606a1b6044820152606401610983565b80602001516001600160801b031682608001516001600160801b031611156125bd5760405162461bcd60e51b815260206004820152601760248201527f51756575652070656e64696e67206c69717569646974790000000000000000006044820152606401610983565b81516001600160a01b031633146126065760405162461bcd60e51b815260206004820152600d60248201526c2737ba103932b8bab2b9ba32b960991b6044820152606401610983565b60208201511561264a5760405162461bcd60e51b815260206004820152600f60248201526e105b1c9958591e4818db185a5b5959608a1b6044820152606401610983565b6000848152604d602052604090819020805460ff60a01b1916600160a01b17905560608301519082015161267e919061406b565b604c80546001600160801b0319166001600160801b03928316179055606083015160405191168152849033907f2d43eb174787155132b52ddb6b346e2dca99302eac3df4466dbeff953d3c84d19060200160405180910390a350606001516001600160801b031692915050565b610df08363a9059cbb60e01b848460405160240161270a929190613f14565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526135ac565b6000603b54821015801561275f5750603754600160a01b900460ff16155b156127735761276c612fbe565b905061277e565b61277b613358565b90505b604154156128e457600081116127d65760405162461bcd60e51b815260206004820152601d60248201527f546f6f206d616e79206f75747374616e64696e672072657175657374730000006044820152606401610983565b600061285982603c60009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561282f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128539190613f2d565b9061367e565b9050604154670de0b6b3a764000082116128845761287f82670de0b6b3a7640000613f46565b612896565b612896670de0b6b3a764000083613f46565b1115610df05760405162461bcd60e51b815260206004820152601e60248201527f4261636b696e6720737570706c79206c6971756964697479206572726f7200006044820152606401610983565b5050565b6001600160a01b03811661293e5760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f7220697320616464726573732830290000000000006044820152606401610983565b610bd1816136a7565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b03161461298a57506000919050565b6129938261370e565b60408051608081018252604b546001600160801b03808216808452600160801b9283900482166020850152604c548083169585018690529290920416606083015292935091906129e39084613f01565b10156129f25750600092915050565b805160408201516001600160801b0391821691612a10911684613f01565b610a8d9190613f46565b60385460609015612a4957603854600090612a399084906127106138a8565b9050612a458184613f46565b9250505b6000607b5490507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031660348281548110612a8d57612a8d613ed5565b6000918252602090912001546001600160a01b031614612aef5760405162461bcd60e51b815260206004820152601b60248201527f5745544820417373657420696e646578206e6f742063616368656400000000006044820152606401610983565b6034546001600160401b03811115612b0957612b09613eeb565b604051908082528060200260200182016040528015612b32578160200160208202803683370190505b50915082828281518110612b4857612b48613ed5565b60200260200101818152505050919050565b7f222838db2794d11532d940e8dec38ae307ed0b63cd97c233322e221f998767a63383604051612b8b929190613f14565b60405180910390a181600003612b9f575050565b603f546000906001600160a01b0316331480612bbe5750612bbe611826565b612beb57612bcb83612a1a565b607b5481518110612bde57612bde613ed5565b6020026020010151612bed565b825b905081811015612c3f5760405162461bcd60e51b815260206004820181905260248201527f52656465656d20616d6f756e74206c6f776572207468616e206d696e696d756d6044820152606401610983565b80612c486138ca565b1015612c885760405162461bcd60e51b815260206004820152600f60248201526e2634b8bab4b234ba3c9032b93937b960891b6044820152606401610983565b612cbc6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633836126eb565b603c54604051632770a7eb60e21b81526001600160a01b0390911690639dc29fac90612cee9033908790600401613f14565b600060405180830381600087803b158015612d0857600080fd5b505af1158015612d1c573d6000803e3d6000fd5b50505050610df083612741565b60006001600160801b03821115612d925760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316044820152663238206269747360c81b6064820152608401610983565b5090565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166000908152604060208190529020541680612dd95750565b6000612de36138ca565b905080600003612df1575050565b603c54604080516318160ddd60e01b815290516000926001600160a01b0316916318160ddd9160048083019260209291908290030181865afa158015612e3b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e5f9190613f2d565b90506000612e78603954836139d890919063ffffffff16565b9050808311612e875750505050565b6000612e938285613f46565b905084612eca6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001682846126eb565b6040516311f9fbc960e21b81526001600160a01b038216906347e7ef2490612f18907f0000000000000000000000000000000000000000000000000000000000000000908690600401613f14565b600060405180830381600087803b158015612f3257600080fd5b505af1158015612f46573d6000803e3d6000fd5b5050604080516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811682528a1660208201529081018590527f41b99659f6ba0803f444aff29e5bf6e26dd86a3219aff92119d69710a956ba8d9250606001905060405180910390a1505050505050565b603754600090600160a01b900460ff161561300d5760405162461bcd60e51b815260206004820152600f60248201526e149958985cda5b99c81c185d5cd959608a1b6044820152606401610983565b603c54604080516318160ddd60e01b815290516000926001600160a01b0316916318160ddd9160048083019260209291908290030181865afa158015613057573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061307b9190613f2d565b90506000613087613358565b90508160000361309a5791506133559050565b6000806130a78484613383565b909250905060006130b88386613f01565b905084811115806130c857508381115b156130d7575091949350505050565b6130e8826001600160401b036139ed565b604f805477ffffffffffffffffffffffffffffffff000000000000000016600160c01b6001600160401b039384160267ffffffffffffffff19161742929092169190911790556042546001600160a01b03166000811561322457612710604354866131539190613e65565b61315d9190613e7c565b90508015613224578481106131bf5760405162461bcd60e51b815260206004820152602260248201527f466565206d757374206e6f742062652067726561746572207468616e207969656044820152611b1960f21b6064820152608401610983565b603c546040516340c10f1960e01b81526001600160a01b03909116906340c10f19906131f19085908590600401613f14565b600060405180830381600087803b15801561320b57600080fd5b505af115801561321f573d6000803e3d6000fd5b505050505b604080516001600160a01b0384168152602081018790529081018290527f09516ecf4a8a86e59780a9befc6dee948bc9e60a36e3be68d31ea817ee8d2c809060600160405180910390a1603c60009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156132c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132e59190613f2d565b83111561334b57603c546040516339a7919f60e01b8152600481018590526001600160a01b03909116906339a7919f90602401600060405180830381600087803b15801561333257600080fd5b505af1158015613346573d6000803e3d6000fd5b505050505b5093955050505050505b90565b60006109547f0000000000000000000000000000000000000000000000000000000000000000612947565b6000806000603c60009054906101000a90046001600160a01b03166001600160a01b031663e696393a6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156133db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133ff9190613f2d565b9050600061340d8287613f46565b604f54909150600090613429906001600160401b031642613f46565b604f54600160c01b90046001600160401b03169450905080158061344b575081155b8061345557508587115b8061346757506001600160401b034210155b15613478576000945050505061354f565b6134828787613f46565b604f54909550600160401b90046001600160401b031660018111156134e7576134bf856134b0836002613e65565b6134ba9089613e7c565b613a03565b94506134d4856134cf8389613e7c565b6139ed565b94506134e4866134cf8488613e65565b95505b604f54613528908790670de0b6b3a764000090600160801b90046001600160401b03166135148688613e65565b61351e9190613e65565b6134cf9190613e7c565b955061354886670de0b6b3a764000061351e66470de4df82000087613e65565b9550505050505b9250929050565b6040516001600160a01b038085166024830152831660448201526064810182905261358e9085906323b872dd60e01b9060840161270a565b50505050565b6000610a8d8284613e65565b6000610a8d8284613e7c565b6000613601826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613a129092919063ffffffff16565b805190915015610df0578080602001905181019061361f919061408a565b610df05760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610983565b60008061369384670de0b6b3a7640000613594565b905061369f81846135a0565b949350505050565b806001600160a01b03166136c76000805160206141608339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a360008051602061416083398151915255565b6040516370a0823160e01b815230600482015260009082906001600160a01b038216906370a0823190602401602060405180830381865afa158015613757573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061377b9190613f2d565b60365490925060005b818110156138a0576000603682815481106137a1576137a1613ed5565b60009182526020909120015460405163551c457b60e11b81526001600160a01b0388811660048301529091169150819063aa388af690602401602060405180830381865afa1580156137f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061381b919061408a565b1561389757604051632fa8a91360e11b81526001600160a01b038781166004830152821690635f51522690602401602060405180830381865afa158015613866573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061388a9190613f2d565b6138949086613f01565b94505b50600101613784565b505050919050565b6000806138b58585613594565b90506138c181846135a0565b95945050505050565b60408051608081018252604b546001600160801b03808216808452600160801b9283900482166020850152604c5480831695850186905292909204166060830152600092839161391a919061404c565b6040516370a0823160e01b81523060048201526001600160801b039190911691506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa15801561398f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139b39190613f2d565b90508181116139c6576000935050505090565b6139d08282613f46565b935050505090565b6000610a8d8383670de0b6b3a76400006138a8565b60008183106139fc5781610a8d565b5090919050565b60008183116139fc5781610a8d565b606061369f848460008585843b613a6b5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610983565b600080866001600160a01b03168587604051613a8791906140d0565b60006040518083038185875af1925050503d8060008114613ac4576040519150601f19603f3d011682016040523d82523d6000602084013e613ac9565b606091505b5091509150613ad9828286613ae4565b979650505050505050565b60608315613af3575081610a8d565b825115613b035782518084602001fd5b8160405162461bcd60e51b815260040161098391906140ec565b828054828255906000526020600020908101928215613b72579160200282015b82811115613b7257825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190613b3d565b50612d929291505b80821115612d925760008155600101613b7a565b80356001600160a01b0381168114611d9457600080fd5b600080600060608486031215613bba57600080fd5b613bc384613b8e565b95602085013595506040909401359392505050565b602080825282518282018190526000918401906040840190835b81811015613c195783516001600160a01b0316835260209384019390920191600101613bf2565b509095945050505050565b600060208284031215613c3657600080fd5b610a8d82613b8e565b60008060408385031215613c5257600080fd5b613c5b83613b8e565b9150613c6960208401613b8e565b90509250929050565b60008060208385031215613c8557600080fd5b82356001600160401b03811115613c9b57600080fd5b8301601f81018513613cac57600080fd5b80356001600160401b03811115613cc257600080fd5b8560208260051b8401011115613cd757600080fd5b6020919091019590945092505050565b600081518084526020840193506020830160005b82811015613d19578151865260209586019590910190600101613cfb565b5093949350505050565b604081526000613d366040830185613ce7565b90508260208301529392505050565b600060208284031215613d5757600080fd5b5035919050565b602081526000610a8d6020830184613ce7565b634e487b7160e01b600052602160045260246000fd5b8151151581526020820151608082019060028110613db557634e487b7160e01b600052602160045260246000fd5b8060208401525060ff604084015116604083015261ffff606084015116606083015292915050565b60008060408385031215613df057600080fd5b50508035926020909101359150565b6020808252600e908201526d10d85c1a5d185b081c185d5cd95960921b604082015260600190565b6020808252600e908201526d1499595b9d1c985b9d0818d85b1b60921b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761104657611046613e4f565b600082613e9957634e487b7160e01b600052601260045260246000fd5b500490565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b8082018082111561104657611046613e4f565b6001600160a01b03929092168252602082015260400190565b600060208284031215613f3f57600080fd5b5051919050565b8181038181111561104657611046613e4f565b6001815b6001841115613f9457808504811115613f7857613f78613e4f565b6001841615613f8657908102905b60019390931c928002613f5d565b935093915050565b600082613fab57506001611046565b81613fb857506000611046565b8160018114613fce5760028114613fd857613ff4565b6001915050611046565b60ff841115613fe957613fe9613e4f565b50506001821b611046565b5060208310610133831016604e8410600b8410161715614017575081810a611046565b6140246000198484613f59565b806000190482111561403857614038613e4f565b029392505050565b6000610a8d8383613f9c565b6001600160801b03828116828216039081111561104657611046613e4f565b6001600160801b03818116838216019081111561104657611046613e4f565b60006020828403121561409c57600080fd5b81518015158114610a8d57600080fd5b60005b838110156140c75781810151838201526020016140af565b50506000910152565b600082516140e28184602087016140ac565b9190910192915050565b602081526000825180602084015261410b8160408501602087016140ac565b601f01601f1916919091016040019291505056fe53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535a2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd97bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa26469706673582212202e2f3d139dfae04b221b145d0b0ff42cd1fd4858b28f0a5be451638661e5e47464736f6c634300081c0033", + "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061038e5760003560e01c806367bd7ba3116101de578063af14052c1161010f578063d38bfff4116100ad578063ea33b8e41161007c578063ea33b8e4146108f9578063ef08edc214610901578063f844443614610916578063fc0cfeee146109295761038e565b8063d38bfff4146108c1578063d4c3eea0146108d4578063e45cc9f0146108dc578063e6cc5432146108e55761038e565b8063bb7a632e116100e9578063bb7a632e14610882578063c3b288641461089c578063c7af3352146108a4578063cc2fe94b146108ac5761038e565b8063af14052c1461085f578063b888879e14610867578063b9b17f9f1461087a5761038e565b80639be918e61161017c578063a0aead4d11610156578063a0aead4d14610813578063a403e4d51461081b578063ab80dafb14610844578063abaa9916146108575761038e565b80639be918e6146107b65780639ee679e8146107e25780639fa1826e1461080a5761038e565b80637a2202f3116101b85780637a2202f3146106e95780637cbc2373146106f25780638e510b5214610705578063937b25811461070e5761038e565b806367bd7ba3146106965780636ec3ab67146106b657806378f353a1146106d65761038e565b806345e4213b116102c357806353ca9f24116102615780635b60f9fc116102305780635b60f9fc146106555780635d36b190146106685780635f515226146106705780636217f3ea146106835761038e565b806353ca9f241461061257806354c6d85814610626578063570d8e1d1461062f5780635802a172146106425761038e565b806349c1d54d1161029d57806349c1d54d146105aa5780634d5f4629146105bd578063527e83a8146105ef57806352d38e5d146106095761038e565b806345e4213b1461056d578063485cc9551461057657806348e30f54146105895761038e565b806331e19cfa116103305780633b8fe28d1161030a5780633b8fe28d146104f85780633fc8cef31461050b57806344c54707146105325780634530820a1461053a5761038e565b806331e19cfa1461044d578063362bd1a31461045557806339ebf823146104b45761038e565b806318ce56bd1161036c57806318ce56bd146104135780631edfe3da14610426578063207134b01461042f5780632acada4d146104385761038e565b806309f6442c146103c45780630c340a24146103e0578063156e29f614610400575b600080516020614140833981519152366000803760008036600084545af43d6000803e8080156103bd573d6000f35b3d6000fd5b005b6103cd60385481565b6040519081526020015b60405180910390f35b6103e861093c565b6040516001600160a01b0390911681526020016103d7565b6103c261040e366004613ba5565b610959565b6045546103e8906001600160a01b031681565b6103cd60395481565b6103cd60435481565b6104406109d7565b6040516103d79190613bd8565b6036546103cd565b604b54604c54610481916001600160801b0380821692600160801b928390048216928183169291041684565b604080516001600160801b03958616815293851660208501529184169183019190915290911660608201526080016103d7565b6104e16104c2366004613c24565b6035602052600090815260409020805460019091015460ff9091169082565b6040805192151583526020830191909152016103d7565b6103cd610506366004613c24565b610a39565b6103e87f000000000000000000000000000000000000000000000000000000000000000081565b6103c2610a94565b61055d610548366004613c24565b60496020526000908152604090205460ff1681565b60405190151581526020016103d7565b6103cd604e5481565b6103c2610584366004613c3f565b610bd4565b61059c610597366004613c72565b610df5565b6040516103d7929190613d23565b6042546103e8906001600160a01b031681565b604f546105d790600160c01b90046001600160401b031681565b6040516001600160401b0390911681526020016103d7565b604f546105d790600160801b90046001600160401b031681565b6103cd603b5481565b60375461055d90600160a01b900460ff1681565b6103cd607b5481565b603f546103e8906001600160a01b031681565b603c546103e8906001600160a01b031681565b6103cd610663366004613c24565b610f6c565b6103c2610f95565b6103cd61067e366004613c24565b61103b565b6103c2610691366004613d45565b61104c565b6106a96106a4366004613d45565b6111d0565b6040516103d79190613d5e565b6106c96106c4366004613c24565b6111db565b6040516103d79190613d87565b604f546105d7906001600160401b031681565b6103cd60475481565b6103c2610700366004613ddd565b611281565b6103cd60415481565b61076f61071c366004613d45565b604d60205260009081526040902080546001909101546001600160a01b03821691600160a01b810460ff1691600160a81b90910464ffffffffff16906001600160801b0380821691600160801b90041685565b604080516001600160a01b039096168652931515602086015264ffffffffff909216928401929092526001600160801b03918216606084015216608082015260a0016103d7565b61055d6107c4366004613c24565b6001600160a01b031660009081526033602052604090205460ff1690565b6107f56107f0366004613d45565b6112f4565b604080519283526020830191909152016103d7565b6103cd603a5481565b6034546103cd565b6103e8610829366004613c24565b6040602081905260009182529020546001600160a01b031681565b6103c2610852366004613d45565b6115b1565b6103c2611700565b6103c2611778565b6037546103e8906001600160a01b031681565b6103c26117be565b604f546105d790600160401b90046001600160401b031681565b6104406117c6565b61055d611826565b600080516020614140833981519152546103e8565b6103c26108cf366004613c24565b611857565b6103cd6118fb565b6103cd60465481565b60375461055d90600160a81b900460ff1681565b6103cd611905565b6103cd60008051602061414083398151915281565b6103cd610924366004613d45565b611994565b6103c2610937366004613c24565b611a86565b60006109546000805160206141608339815191525490565b905090565b603754600160a81b900460ff161561098c5760405162461bcd60e51b815260040161098390613dff565b60405180910390fd5b600080516020614120833981519152805460011981016109be5760405162461bcd60e51b815260040161098390613e27565b600282556109cd858585611b16565b5060019055505050565b60606034805480602002602001604051908101604052809291908181526020018280548015610a2f57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610a11575b5050505050905090565b600080610a62610a5c610a4b85611d2f565b670de0b6b3a7640000906012611d99565b84611dfb565b9050670de0b6b3a764000081610a79856001611f46565b610a839190613e65565b610a8d9190613e7c565b9392505050565b610a9c611826565b610ab85760405162461bcd60e51b815260040161098390613e9e565b60345460005b81811015610b2f577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031660348281548110610b0357610b03613ed5565b6000918252602090912001546001600160a01b031603610b2757607b819055610b2f565b600101610abe565b507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166034607b5481548110610b6f57610b6f613ed5565b6000918252602090912001546001600160a01b031614610bd15760405162461bcd60e51b815260206004820152601860248201527f496e76616c6964205745544820417373657420496e64657800000000000000006044820152606401610983565b50565b610bdc611826565b610bf85760405162461bcd60e51b815260040161098390613e9e565b600054610100900460ff1680610c11575060005460ff16155b610c745760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610983565b600054610100900460ff16158015610c96576000805461ffff19166101011790555b6001600160a01b038316610cec5760405162461bcd60e51b815260206004820152601d60248201527f507269636550726f76696465722061646472657373206973207a65726f0000006044820152606401610983565b6001600160a01b038216610d3b5760405162461bcd60e51b81526020600482015260166024820152756f546f6b656e2061646472657373206973207a65726f60501b6044820152606401610983565b603c80546001600160a01b038481166001600160a01b031990921691909117909155603780546001600160b01b03191691851691909117600160a81b17905560006038819055603981905569054b40b1f852bda00000603a55683635c9adc5dea00000603b556040805191825260208201908190529051610dbe91603691613b1d565b50604f80546fffffffffffffffff00000000000000001916600160401b1790558015610df0576000805461ff00191690555b505050565b603754606090600090600160a81b900460ff1615610e255760405162461bcd60e51b815260040161098390613dff565b60008051602061412083398151915280546001198101610e575760405162461bcd60e51b815260040161098390613e27565b60028255610e63612219565b50846001600160401b03811115610e7c57610e7c613eeb565b604051908082528060200260200182016040528015610ea5578160200160208202803683370190505b50935060005b85811015610f2157610ed4878783818110610ec857610ec8613ed5565b905060200201356123f5565b858281518110610ee657610ee6613ed5565b602002602001018181525050848181518110610f0457610f04613ed5565b602002602001015184610f179190613f01565b9350600101610eab565b50610f566001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633856126eb565b610f5f83612741565b6001825550509250929050565b600080610f7e610a5c610a4b85611d2f565b9050670de0b6b3a764000081610a79856000611f46565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b0316146110305760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b6064820152608401610983565b611039336128e8565b565b600061104682612947565b92915050565b603754600160a81b900460ff16156110765760405162461bcd60e51b815260040161098390613dff565b3360009081526035602052604090205460ff1615156001146110d15760405162461bcd60e51b8152602060048201526014602482015273556e737570706f7274656420737472617465677960601b6044820152606401610983565b3360009081526049602052604090205460ff1615156001146111305760405162461bcd60e51b81526020600482015260186024820152774e6f742077686974656c697374656420737472617465677960401b6044820152606401610983565b7f222838db2794d11532d940e8dec38ae307ed0b63cd97c233322e221f998767a63382604051611161929190613f14565b60405180910390a1603c54604051632770a7eb60e21b81526001600160a01b0390911690639dc29fac9061119b9033908590600401613f14565b600060405180830381600087803b1580156111b557600080fd5b505af11580156111c9573d6000803e3d6000fd5b5050505050565b606061104682612a1a565b604080516080808201835260008083526020808401829052838501829052606084018290526001600160a01b038616825260338152908490208451928301909452835460ff80821615158452939492939184019161010090910416600181111561124757611247613d71565b600181111561125857611258613d71565b8152905462010000810460ff1660208301526301000000900461ffff1660409091015292915050565b603754600160a81b900460ff16156112ab5760405162461bcd60e51b815260040161098390613dff565b600080516020614120833981519152805460011981016112dd5760405162461bcd60e51b815260040161098390613e27565b600282556112eb8484612b5a565b50600190555050565b6037546000908190600160a81b900460ff16156113235760405162461bcd60e51b815260040161098390613dff565b600080516020614120833981519152805460011981016113555760405162461bcd60e51b815260040161098390613e27565b600282556000604e54116113ab5760405162461bcd60e51b815260206004820152601d60248201527f4173796e63207769746864726177616c73206e6f7420656e61626c65640000006044820152606401610983565b604c54604b546001600160801b03600160801b909204821695506113d191879116613f01565b92506113e66113e1856001613f01565b612d29565b604c80546001600160801b03928316600160801b02921691909117905561140c83612d29565b604b80546001600160801b0319166001600160801b03929092169190911790556040805160a081018252338152600060208201524264ffffffffff16918101919091526060810161145c87612d29565b6001600160801b0316815260200161147385612d29565b6001600160801b039081169091526000868152604d602090815260409182902084518154928601518685015164ffffffffff16600160a81b0264ffffffffff60a81b19911515600160a01b026001600160a81b03199095166001600160a01b0393841617949094171692909217815560608501516080909501518416600160801b029490931693909317600190920191909155603c549051632770a7eb60e21b8152911690639dc29fac9061152e9033908990600401613f14565b600060405180830381600087803b15801561154857600080fd5b505af115801561155c573d6000803e3d6000fd5b5050505061156985612741565b6040805186815260208101859052859133917f38e3d972947cfef94205163d483d6287ef27eb312e20cb8e0b13a49989db232e910160405180910390a3600182555050915091565b603754600160a81b900460ff16156115db5760405162461bcd60e51b815260040161098390613dff565b3360009081526035602052604090205460ff1615156001146116365760405162461bcd60e51b8152602060048201526014602482015273556e737570706f7274656420737472617465677960601b6044820152606401610983565b3360009081526049602052604090205460ff1615156001146116955760405162461bcd60e51b81526020600482015260186024820152774e6f742077686974656c697374656420737472617465677960401b6044820152606401610983565b7f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d412139688533826040516116c6929190613f14565b60405180910390a1603c546040516340c10f1960e01b81526001600160a01b03909116906340c10f199061119b9033908590600401613f14565b603754600160a81b900460ff161561172a5760405162461bcd60e51b815260040161098390613dff565b6000805160206141208339815191528054600119810161175c5760405162461bcd60e51b815260040161098390613e27565b60028255611768612219565b50611771612d96565b5060019055565b600080516020614120833981519152805460011981016117aa5760405162461bcd60e51b815260040161098390613e27565b600282556117b6612fbe565b505060019055565b610bd1612219565b60606036805480602002602001604051908101604052809291908181526020018280548015610a2f576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311610a11575050505050905090565b600061183e6000805160206141608339815191525490565b6001600160a01b0316336001600160a01b031614905090565b61185f611826565b61187b5760405162461bcd60e51b815260040161098390613e9e565b6118a3817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b03166118c36000805160206141608339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b6000610954613358565b600061198e603c60009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561195d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119819190613f2d565b611989613358565b613383565b50919050565b603754600090600160a81b900460ff16156119c15760405162461bcd60e51b815260040161098390613dff565b600080516020614120833981519152805460011981016119f35760405162461bcd60e51b815260040161098390613e27565b60028255604b546000858152604d60205260409020600101546001600160801b03600160801b92839004811692909104161115611a3457611a32612219565b505b611a3d846123f5565b9250611a736001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633856126eb565b611a7c83612741565b5060019055919050565b611a8e611826565b611aaa5760405162461bcd60e51b815260040161098390613e9e565b803b611b045760405162461bcd60e51b8152602060048201526024808201527f6e657720696d706c656d656e746174696f6e206973206e6f74206120636f6e746044820152631c9858dd60e21b6064820152608401610983565b60008051602061414083398151915255565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b031614611b975760405162461bcd60e51b815260206004820152601d60248201527f556e737570706f7274656420617373657420666f72206d696e74696e670000006044820152606401610983565b60008211611be75760405162461bcd60e51b815260206004820152601d60248201527f416d6f756e74206d7573742062652067726561746572207468616e20300000006044820152606401610983565b80821015611c375760405162461bcd60e51b815260206004820152601e60248201527f4d696e7420616d6f756e74206c6f776572207468616e206d696e696d756d00006044820152606401610983565b7f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d41213968853383604051611c68929190613f14565b60405180910390a1603754600160a01b900460ff16158015611c8c5750603b548210155b15611c9b57611c99612fbe565b505b603c546040516340c10f1960e01b81526001600160a01b03909116906340c10f1990611ccd9033908690600401613f14565b600060405180830381600087803b158015611ce757600080fd5b505af1158015611cfb573d6000803e3d6000fd5b50611d15925050506001600160a01b038416333085613556565b611d1d612219565b50603a548210610df057610df0612d96565b6001600160a01b03811660009081526033602052604090205462010000900460ff1680611d945760405162461bcd60e51b8152602060048201526013602482015272111958da5b585b1cc81b9bdd0818d858da1959606a1b6044820152606401610983565b919050565b600081831115611dc957611dc2611db08385613f46565b611dbb90600a614040565b8590613594565b9350611df3565b81831015611df357611df0611dde8484613f46565b611de990600a614040565b85906135a0565b93505b509192915050565b6001600160a01b038116600090815260336020526040812054610100900460ff1681816001811115611e2f57611e2f613d71565b03611e5257611e4a6012611e4285611d2f565b869190611d99565b915050611046565b6001816001811115611e6657611e66613d71565b03611ef7576000836001600160a01b031663e6aa216c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611eab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ecf9190613f2d565b9050670de0b6b3a7640000611ee48287613e65565b611eee9190613e7c565b92505050611046565b60405162461bcd60e51b815260206004820152601b60248201527f556e737570706f7274656420636f6e76657273696f6e207479706500000000006044820152606401610983565b5092915050565b6001600160a01b038281166000818152603360205260408082205460375491516315d5220f60e31b81526004810194909452919361010090920460ff169291169063aea9107890602401602060405180830381865afa158015611fad573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fd19190613f2d565b91506001816001811115611fe757611fe7613d71565b03612077576000846001600160a01b031663e6aa216c6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561202c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120509190613f2d565b90508061206584670de0b6b3a7640000613e65565b61206f9190613e7c565b9250506120d8565b600081600181111561208b5761208b613d71565b146120d85760405162461bcd60e51b815260206004820152601b60248201527f556e737570706f7274656420636f6e76657273696f6e207479706500000000006044820152606401610983565b67120a871cc00200008211156121305760405162461bcd60e51b815260206004820152601860248201527f5661756c743a2050726963652065786365656473206d617800000000000000006044820152606401610983565b6709b6e64a8ec600008210156121815760405162461bcd60e51b81526020600482015260166024820152752b30bab63a1d10283934b1b2903ab73232b91036b4b760511b6044820152606401610983565b82156121f857670de0b6b3a76400008211156121a357670de0b6b3a764000091505b670dd99bb65dd700008210156121f35760405162461bcd60e51b815260206004820152601560248201527441737365742070726963652062656c6f772070656760581b6044820152606401610983565b611f3f565b670de0b6b3a7640000821015611f3f5750670de0b6b3a76400009392505050565b60408051608081018252604b546001600160801b03808216808452600160801b92839004821660208501819052604c548084169686019690965292909404166060830152600092839161226b9161404c565b6001600160801b03169050806000036122875760009250505090565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156122ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123129190613f2d565b905060008360400151846020015161232a919061404c565b6001600160801b0316905080821161234757600094505050505090565b60006123538284613f46565b90508084106123625780612364565b835b955060008686602001516001600160801b03166123819190613f01565b905061238c81612d29565b604b80546001600160801b03928316600160801b0292169190911790556040517fee79a0c43d3993055690b54e074b5153e8bae8d1a872b656dedb64aa8f463333906123e49083908a90918252602082015260400190565b60405180910390a150505050505090565b600080604e54116124485760405162461bcd60e51b815260206004820152601d60248201527f4173796e63207769746864726177616c73206e6f7420656e61626c65640000006044820152606401610983565b6000828152604d6020908152604091829020825160a08101845281546001600160a01b038116825260ff600160a01b82041615158285015264ffffffffff600160a81b90910481168286019081526001909301546001600160801b03808216606080860191909152600160801b92839004821660808087019190915288519081018952604b548084168252849004831697810197909752604c54808316988801989098529190960490951694840194909452604e5491519093429261250f92909116613f01565b11156125535760405162461bcd60e51b815260206004820152601360248201527210db185a5b4819195b185e481b9bdd081b595d606a1b6044820152606401610983565b80602001516001600160801b031682608001516001600160801b031611156125bd5760405162461bcd60e51b815260206004820152601760248201527f51756575652070656e64696e67206c69717569646974790000000000000000006044820152606401610983565b81516001600160a01b031633146126065760405162461bcd60e51b815260206004820152600d60248201526c2737ba103932b8bab2b9ba32b960991b6044820152606401610983565b60208201511561264a5760405162461bcd60e51b815260206004820152600f60248201526e105b1c9958591e4818db185a5b5959608a1b6044820152606401610983565b6000848152604d602052604090819020805460ff60a01b1916600160a01b17905560608301519082015161267e919061406b565b604c80546001600160801b0319166001600160801b03928316179055606083015160405191168152849033907f2d43eb174787155132b52ddb6b346e2dca99302eac3df4466dbeff953d3c84d19060200160405180910390a350606001516001600160801b031692915050565b610df08363a9059cbb60e01b848460405160240161270a929190613f14565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526135ac565b6000603b54821015801561275f5750603754600160a01b900460ff16155b156127735761276c612fbe565b905061277e565b61277b613358565b90505b604154156128e457600081116127d65760405162461bcd60e51b815260206004820152601d60248201527f546f6f206d616e79206f75747374616e64696e672072657175657374730000006044820152606401610983565b600061285982603c60009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561282f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128539190613f2d565b9061367e565b9050604154670de0b6b3a764000082116128845761287f82670de0b6b3a7640000613f46565b612896565b612896670de0b6b3a764000083613f46565b1115610df05760405162461bcd60e51b815260206004820152601e60248201527f4261636b696e6720737570706c79206c6971756964697479206572726f7200006044820152606401610983565b5050565b6001600160a01b03811661293e5760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f7220697320616464726573732830290000000000006044820152606401610983565b610bd1816136a7565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b03161461298a57506000919050565b6129938261370e565b60408051608081018252604b546001600160801b03808216808452600160801b9283900482166020850152604c548083169585018690529290920416606083015292935091906129e39084613f01565b10156129f25750600092915050565b805160408201516001600160801b0391821691612a10911684613f01565b610a8d9190613f46565b60385460609015612a4957603854600090612a399084906127106138a8565b9050612a458184613f46565b9250505b6000607b5490507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031660348281548110612a8d57612a8d613ed5565b6000918252602090912001546001600160a01b031614612aef5760405162461bcd60e51b815260206004820152601b60248201527f5745544820417373657420696e646578206e6f742063616368656400000000006044820152606401610983565b6034546001600160401b03811115612b0957612b09613eeb565b604051908082528060200260200182016040528015612b32578160200160208202803683370190505b50915082828281518110612b4857612b48613ed5565b60200260200101818152505050919050565b7f222838db2794d11532d940e8dec38ae307ed0b63cd97c233322e221f998767a63383604051612b8b929190613f14565b60405180910390a181600003612b9f575050565b603f546000906001600160a01b0316331480612bbe5750612bbe611826565b612beb57612bcb83612a1a565b607b5481518110612bde57612bde613ed5565b6020026020010151612bed565b825b905081811015612c3f5760405162461bcd60e51b815260206004820181905260248201527f52656465656d20616d6f756e74206c6f776572207468616e206d696e696d756d6044820152606401610983565b80612c486138ca565b1015612c885760405162461bcd60e51b815260206004820152600f60248201526e2634b8bab4b234ba3c9032b93937b960891b6044820152606401610983565b612cbc6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633836126eb565b603c54604051632770a7eb60e21b81526001600160a01b0390911690639dc29fac90612cee9033908790600401613f14565b600060405180830381600087803b158015612d0857600080fd5b505af1158015612d1c573d6000803e3d6000fd5b50505050610df083612741565b60006001600160801b03821115612d925760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316044820152663238206269747360c81b6064820152608401610983565b5090565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166000908152604060208190529020541680612dd95750565b6000612de36138ca565b905080600003612df1575050565b603c54604080516318160ddd60e01b815290516000926001600160a01b0316916318160ddd9160048083019260209291908290030181865afa158015612e3b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e5f9190613f2d565b90506000612e78603954836139d890919063ffffffff16565b9050808311612e875750505050565b6000612e938285613f46565b905084612eca6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001682846126eb565b6040516311f9fbc960e21b81526001600160a01b038216906347e7ef2490612f18907f0000000000000000000000000000000000000000000000000000000000000000908690600401613f14565b600060405180830381600087803b158015612f3257600080fd5b505af1158015612f46573d6000803e3d6000fd5b5050604080516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811682528a1660208201529081018590527f41b99659f6ba0803f444aff29e5bf6e26dd86a3219aff92119d69710a956ba8d9250606001905060405180910390a1505050505050565b603754600090600160a01b900460ff161561300d5760405162461bcd60e51b815260206004820152600f60248201526e149958985cda5b99c81c185d5cd959608a1b6044820152606401610983565b603c54604080516318160ddd60e01b815290516000926001600160a01b0316916318160ddd9160048083019260209291908290030181865afa158015613057573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061307b9190613f2d565b90506000613087613358565b90508160000361309a5791506133559050565b6000806130a78484613383565b909250905060006130b88386613f01565b905084811115806130c857508381115b156130d7575091949350505050565b6130e8826001600160401b036139ed565b604f805477ffffffffffffffffffffffffffffffff000000000000000016600160c01b6001600160401b039384160267ffffffffffffffff19161742929092169190911790556042546001600160a01b03166000811561322457612710604354866131539190613e65565b61315d9190613e7c565b90508015613224578481106131bf5760405162461bcd60e51b815260206004820152602260248201527f466565206d757374206e6f742062652067726561746572207468616e207969656044820152611b1960f21b6064820152608401610983565b603c546040516340c10f1960e01b81526001600160a01b03909116906340c10f19906131f19085908590600401613f14565b600060405180830381600087803b15801561320b57600080fd5b505af115801561321f573d6000803e3d6000fd5b505050505b604080516001600160a01b0384168152602081018790529081018290527f09516ecf4a8a86e59780a9befc6dee948bc9e60a36e3be68d31ea817ee8d2c809060600160405180910390a1603c60009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156132c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132e59190613f2d565b83111561334b57603c546040516339a7919f60e01b8152600481018590526001600160a01b03909116906339a7919f90602401600060405180830381600087803b15801561333257600080fd5b505af1158015613346573d6000803e3d6000fd5b505050505b5093955050505050505b90565b60006109547f0000000000000000000000000000000000000000000000000000000000000000612947565b6000806000603c60009054906101000a90046001600160a01b03166001600160a01b031663e696393a6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156133db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133ff9190613f2d565b9050600061340d8287613f46565b604f54909150600090613429906001600160401b031642613f46565b604f54600160c01b90046001600160401b03169450905080158061344b575081155b8061345557508587115b8061346757506001600160401b034210155b15613478576000945050505061354f565b6134828787613f46565b604f54909550600160401b90046001600160401b031660018111156134e7576134bf856134b0836002613e65565b6134ba9089613e7c565b613a03565b94506134d4856134cf8389613e7c565b6139ed565b94506134e4866134cf8488613e65565b95505b604f54613528908790670de0b6b3a764000090600160801b90046001600160401b03166135148688613e65565b61351e9190613e65565b6134cf9190613e7c565b955061354886670de0b6b3a764000061351e66470de4df82000087613e65565b9550505050505b9250929050565b6040516001600160a01b038085166024830152831660448201526064810182905261358e9085906323b872dd60e01b9060840161270a565b50505050565b6000610a8d8284613e65565b6000610a8d8284613e7c565b6000613601826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613a129092919063ffffffff16565b805190915015610df0578080602001905181019061361f919061408a565b610df05760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610983565b60008061369384670de0b6b3a7640000613594565b905061369f81846135a0565b949350505050565b806001600160a01b03166136c76000805160206141608339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a360008051602061416083398151915255565b6040516370a0823160e01b815230600482015260009082906001600160a01b038216906370a0823190602401602060405180830381865afa158015613757573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061377b9190613f2d565b60365490925060005b818110156138a0576000603682815481106137a1576137a1613ed5565b60009182526020909120015460405163551c457b60e11b81526001600160a01b0388811660048301529091169150819063aa388af690602401602060405180830381865afa1580156137f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061381b919061408a565b1561389757604051632fa8a91360e11b81526001600160a01b038781166004830152821690635f51522690602401602060405180830381865afa158015613866573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061388a9190613f2d565b6138949086613f01565b94505b50600101613784565b505050919050565b6000806138b58585613594565b90506138c181846135a0565b95945050505050565b60408051608081018252604b546001600160801b03808216808452600160801b9283900482166020850152604c5480831695850186905292909204166060830152600092839161391a919061404c565b6040516370a0823160e01b81523060048201526001600160801b039190911691506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa15801561398f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139b39190613f2d565b90508181116139c6576000935050505090565b6139d08282613f46565b935050505090565b6000610a8d8383670de0b6b3a76400006138a8565b60008183106139fc5781610a8d565b5090919050565b60008183116139fc5781610a8d565b606061369f848460008585843b613a6b5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610983565b600080866001600160a01b03168587604051613a8791906140d0565b60006040518083038185875af1925050503d8060008114613ac4576040519150601f19603f3d011682016040523d82523d6000602084013e613ac9565b606091505b5091509150613ad9828286613ae4565b979650505050505050565b60608315613af3575081610a8d565b825115613b035782518084602001fd5b8160405162461bcd60e51b815260040161098391906140ec565b828054828255906000526020600020908101928215613b72579160200282015b82811115613b7257825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190613b3d565b50612d929291505b80821115612d925760008155600101613b7a565b80356001600160a01b0381168114611d9457600080fd5b600080600060608486031215613bba57600080fd5b613bc384613b8e565b95602085013595506040909401359392505050565b602080825282518282018190526000918401906040840190835b81811015613c195783516001600160a01b0316835260209384019390920191600101613bf2565b509095945050505050565b600060208284031215613c3657600080fd5b610a8d82613b8e565b60008060408385031215613c5257600080fd5b613c5b83613b8e565b9150613c6960208401613b8e565b90509250929050565b60008060208385031215613c8557600080fd5b82356001600160401b03811115613c9b57600080fd5b8301601f81018513613cac57600080fd5b80356001600160401b03811115613cc257600080fd5b8560208260051b8401011115613cd757600080fd5b6020919091019590945092505050565b600081518084526020840193506020830160005b82811015613d19578151865260209586019590910190600101613cfb565b5093949350505050565b604081526000613d366040830185613ce7565b90508260208301529392505050565b600060208284031215613d5757600080fd5b5035919050565b602081526000610a8d6020830184613ce7565b634e487b7160e01b600052602160045260246000fd5b8151151581526020820151608082019060028110613db557634e487b7160e01b600052602160045260246000fd5b8060208401525060ff604084015116604083015261ffff606084015116606083015292915050565b60008060408385031215613df057600080fd5b50508035926020909101359150565b6020808252600e908201526d10d85c1a5d185b081c185d5cd95960921b604082015260600190565b6020808252600e908201526d1499595b9d1c985b9d0818d85b1b60921b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761104657611046613e4f565b600082613e9957634e487b7160e01b600052601260045260246000fd5b500490565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b8082018082111561104657611046613e4f565b6001600160a01b03929092168252602082015260400190565b600060208284031215613f3f57600080fd5b5051919050565b8181038181111561104657611046613e4f565b6001815b6001841115613f9457808504811115613f7857613f78613e4f565b6001841615613f8657908102905b60019390931c928002613f5d565b935093915050565b600082613fab57506001611046565b81613fb857506000611046565b8160018114613fce5760028114613fd857613ff4565b6001915050611046565b60ff841115613fe957613fe9613e4f565b50506001821b611046565b5060208310610133831016604e8410600b8410161715614017575081810a611046565b6140246000198484613f59565b806000190482111561403857614038613e4f565b029392505050565b6000610a8d8383613f9c565b6001600160801b03828116828216039081111561104657611046613e4f565b6001600160801b03818116838216019081111561104657611046613e4f565b60006020828403121561409c57600080fd5b81518015158114610a8d57600080fd5b60005b838110156140c75781810151838201526020016140af565b50506000910152565b600082516140e28184602087016140ac565b9190910192915050565b602081526000825180602084015261410b8160408501602087016140ac565b601f01601f1916919091016040019291505056fe53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535a2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd97bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa26469706673582212202e2f3d139dfae04b221b145d0b0ff42cd1fd4858b28f0a5be451638661e5e47464736f6c634300081c0033", + "libraries": {}, + "devdoc": { + "author": "Origin Protocol Inc", + "kind": "dev", + "methods": { + "addWithdrawalQueueLiquidity()": { + "details": "is called from the Native Staking strategy when validator withdrawals are processed. It also called before any WETH is allocated to a strategy." + }, + "cacheWETHAssetIndex()": { + "details": "Caches WETH's index in `allAssets` variable. Reduces gas usage by redeem by caching that." + }, + "checkBalance(address)": { + "params": { + "_asset": "Address of asset" + }, + "returns": { + "_0": "uint256 Balance of asset in decimals of asset" + } + }, + "claimWithdrawal(uint256)": { + "params": { + "_requestId": "Unique ID for the withdrawal request" + }, + "returns": { + "amount": "Amount of WETH transferred to the withdrawer" + } + }, + "claimWithdrawals(uint256[])": { + "params": { + "_requestIds": "Unique ID of each withdrawal request" + }, + "returns": { + "amounts": "Amount of WETH received for each request", + "totalAmount": "Total amount of WETH transferred to the withdrawer" + } + }, + "getAssetConfig(address)": { + "params": { + "_asset": "Address of the token asset" + } + }, + "isSupportedAsset(address)": { + "params": { + "_asset": "address of the asset" + }, + "returns": { + "_0": "true if supported" + } + }, + "mint(address,uint256,uint256)": { + "params": { + "_amount": "Amount of the asset being deposited", + "_asset": "Address of the asset being deposited", + "_minimumOusdAmount": "Minimum OTokens to mint" + } + }, + "previewYield()": { + "returns": { + "yield": "amount of expected yield" + } + }, + "priceUnitMint(address)": { + "params": { + "asset": "address of the asset" + }, + "returns": { + "price": "uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed" + } + }, + "priceUnitRedeem(address)": { + "params": { + "asset": "Address of the asset" + }, + "returns": { + "price": "uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed" + } + }, + "redeem(uint256,uint256)": { + "params": { + "_amount": "Amount of OTokens to burn", + "_minimumUnitAmount": "Minimum stablecoin units to receive in return" + } + }, + "requestWithdrawal(uint256)": { + "params": { + "_amount": "Amount of OETH to burn." + }, + "returns": { + "queued": "Cumulative total of all WETH queued including already claimed requests.", + "requestId": "Unique ID for the withdrawal request" + } + }, + "setAdminImpl(address)": { + "params": { + "newImpl": "address of the implementation" + } + }, + "totalValue()": { + "returns": { + "value": "Total value in USD/ETH (1e18)" + } + }, + "transferGovernance(address)": { + "params": { + "_newGovernor": "Address of the new Governor" + } + } + }, + "title": "OETH VaultCore Contract", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "addWithdrawalQueueLiquidity()": { + "notice": "Adds WETH to the withdrawal queue if there is a funding shortfall." + }, + "allocate()": { + "notice": "Allocate unallocated funds on Vault to strategies.*" + }, + "assetDefaultStrategies(address)": { + "notice": "Mapping of asset address to the Strategy that they should automatically" + }, + "autoAllocateThreshold()": { + "notice": "OToken mints over this amount automatically allocate funds. 18 decimals." + }, + "calculateRedeemOutputs(uint256)": { + "notice": "Calculate the outputs for a redeem function, i.e. the mix of coins that will be returned" + }, + "capitalPaused()": { + "notice": "pause operations that change the OToken supply. eg mint, redeem, allocate, mint/burn for strategy" + }, + "checkBalance(address)": { + "notice": "Get the balance of an asset held in Vault and all strategies." + }, + "claimGovernance()": { + "notice": "Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor." + }, + "claimWithdrawal(uint256)": { + "notice": "Claim a previously requested withdrawal once it is claimable. This request can be claimed once the withdrawal queue's `claimable` amount is greater than or equal this request's `queued` amount and 10 minutes has passed. If the requests is not claimable, the transaction will revert with `Queue pending liquidity`. If the request is not older than 10 minutes, the transaction will revert with `Claim delay not met`. OETH is converted to WETH at 1:1." + }, + "claimWithdrawals(uint256[])": { + "notice": "Claim a previously requested withdrawals once they are claimable. This requests can be claimed once the withdrawal queue's `claimable` amount is greater than or equal each request's `queued` amount and 10 minutes has passed. If one of the requests is not claimable, the whole transaction will revert with `Queue pending liquidity`. If one of the requests is not older than 10 minutes, the whole transaction will revert with `Claim delay not met`." + }, + "dripDuration()": { + "notice": "Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable." + }, + "getAllAssets()": { + "notice": "Return all vault asset addresses in order" + }, + "getAllStrategies()": { + "notice": "Return the array of all strategies" + }, + "getAssetConfig(address)": { + "notice": "Gets the vault configuration of a supported asset." + }, + "getAssetCount()": { + "notice": "Return the number of assets supported by the Vault." + }, + "getStrategyCount()": { + "notice": "Return the number of strategies active on the Vault." + }, + "governor()": { + "notice": "Returns the address of the current Governor." + }, + "isGovernor()": { + "notice": "Returns true if the caller is the current Governor." + }, + "isSupportedAsset(address)": { + "notice": "Returns whether the vault supports the asset" + }, + "lastRebase()": { + "notice": "Time in seconds that the vault last rebased yield." + }, + "maxSupplyDiff()": { + "notice": "Max difference between total supply and total value of assets. 18 decimals." + }, + "mint(address,uint256,uint256)": { + "notice": "Deposit a supported asset and mint OTokens." + }, + "netOusdMintForStrategyThreshold()": { + "notice": "How much net total OTokens are allowed to be minted by all strategies" + }, + "netOusdMintedForStrategy()": { + "notice": "How much OTokens are currently minted by the strategy" + }, + "ousdMetaStrategy()": { + "notice": "Metapool strategy that is allowed to mint/burn OTokens without changing collateral" + }, + "previewYield()": { + "notice": "Calculates the amount that would rebase at next rebase. This is before any fees." + }, + "priceProvider()": { + "notice": "Address of the Oracle price provider contract" + }, + "priceUnitMint(address)": { + "notice": "Returns the total price in 18 digit units for a given asset. Never goes above 1, since that is how we price mints." + }, + "priceUnitRedeem(address)": { + "notice": "Returns the total price in 18 digit unit for a given asset. Never goes below 1, since that is how we price redeems" + }, + "rebase()": { + "notice": "Calculate the total value of assets held by the Vault and all strategies and update the supply of OTokens." + }, + "rebasePaused()": { + "notice": "pause rebasing if true" + }, + "rebasePerSecondMax()": { + "notice": "max rebase percentage per second Can be used to set maximum yield of the protocol, spreading out yield over time" + }, + "rebasePerSecondTarget()": { + "notice": "target rebase rate limit, based on past rates and funds available." + }, + "rebaseThreshold()": { + "notice": "OToken mints over this amount automatically rebase. 18 decimals." + }, + "redeem(uint256,uint256)": { + "notice": "Withdraw a supported asset and burn OTokens." + }, + "redeemFeeBps()": { + "notice": "Redemption fee in basis points. eg 50 = 0.5%" + }, + "requestWithdrawal(uint256)": { + "notice": "Request an asynchronous withdrawal of WETH in exchange for OETH. The OETH is burned on request and the WETH is transferred to the withdrawer on claim. This request can be claimed once the withdrawal queue's `claimable` amount is greater than or equal this request's `queued` amount. There is a minimum of 10 minutes before a request can be claimed. After that, the request just needs enough WETH liquidity in the Vault to satisfy all the outstanding requests to that point in the queue. OETH is converted to WETH at 1:1." + }, + "setAdminImpl(address)": { + "notice": "set the implementation for the admin, this needs to be in a base class else we cannot set it" + }, + "strategistAddr()": { + "notice": "Address of the Strategist" + }, + "totalValue()": { + "notice": "Determine the total value of assets held by the vault and its strategies." + }, + "transferGovernance(address)": { + "notice": "Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete" + }, + "trusteeAddress()": { + "notice": "Trustee contract that can collect a percentage of yield" + }, + "trusteeFeeBps()": { + "notice": "Amount of yield collected in basis points. eg 2000 = 20%" + }, + "vaultBuffer()": { + "notice": "Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18." + }, + "withdrawalClaimDelay()": { + "notice": "Sets a minimum delay that is required to elapse between requesting async withdrawals and claiming the request. When set to 0 async withdrawals are disabled." + }, + "withdrawalQueueMetadata()": { + "notice": "Global metadata for the withdrawal queue including: queued - cumulative total of all withdrawal requests included the ones that have already been claimed claimable - cumulative total of all the requests that can be claimed including the ones already claimed claimed - total of all the requests that have been claimed nextWithdrawalIndex - index of the next withdrawal request starting at 0" + }, + "withdrawalRequests(uint256)": { + "notice": "Mapping of withdrawal request indices to the user withdrawal request data" + } + }, + "version": 1 + }, + "storageLayout": { + "storage": [ + { + "astId": 72011, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "initialized", + "offset": 0, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 72014, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "initializing", + "offset": 1, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 72054, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "______gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage" + }, + { + "astId": 79250, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "assets", + "offset": 0, + "slot": "51", + "type": "t_mapping(t_address,t_struct(Asset)79244_storage)" + }, + { + "astId": 79254, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "allAssets", + "offset": 0, + "slot": "52", + "type": "t_array(t_address)dyn_storage" + }, + { + "astId": 79265, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "strategies", + "offset": 0, + "slot": "53", + "type": "t_mapping(t_address,t_struct(Strategy)79259_storage)" + }, + { + "astId": 79269, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "allStrategies", + "offset": 0, + "slot": "54", + "type": "t_array(t_address)dyn_storage" + }, + { + "astId": 79272, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "priceProvider", + "offset": 0, + "slot": "55", + "type": "t_address" + }, + { + "astId": 79275, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "rebasePaused", + "offset": 20, + "slot": "55", + "type": "t_bool" + }, + { + "astId": 79278, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "capitalPaused", + "offset": 21, + "slot": "55", + "type": "t_bool" + }, + { + "astId": 79281, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "redeemFeeBps", + "offset": 0, + "slot": "56", + "type": "t_uint256" + }, + { + "astId": 79284, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "vaultBuffer", + "offset": 0, + "slot": "57", + "type": "t_uint256" + }, + { + "astId": 79287, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "autoAllocateThreshold", + "offset": 0, + "slot": "58", + "type": "t_uint256" + }, + { + "astId": 79290, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "rebaseThreshold", + "offset": 0, + "slot": "59", + "type": "t_uint256" + }, + { + "astId": 79294, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "oUSD", + "offset": 0, + "slot": "60", + "type": "t_contract(OUSD)69402" + }, + { + "astId": 79305, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "_deprecated_rebaseHooksAddr", + "offset": 0, + "slot": "61", + "type": "t_address" + }, + { + "astId": 79312, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "_deprecated_uniswapAddr", + "offset": 0, + "slot": "62", + "type": "t_address" + }, + { + "astId": 79319, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "strategistAddr", + "offset": 0, + "slot": "63", + "type": "t_address" + }, + { + "astId": 79324, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "assetDefaultStrategies", + "offset": 0, + "slot": "64", + "type": "t_mapping(t_address,t_address)" + }, + { + "astId": 79327, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "maxSupplyDiff", + "offset": 0, + "slot": "65", + "type": "t_uint256" + }, + { + "astId": 79330, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "trusteeAddress", + "offset": 0, + "slot": "66", + "type": "t_address" + }, + { + "astId": 79333, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "trusteeFeeBps", + "offset": 0, + "slot": "67", + "type": "t_uint256" + }, + { + "astId": 79337, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "_deprecated_swapTokens", + "offset": 0, + "slot": "68", + "type": "t_array(t_address)dyn_storage" + }, + { + "astId": 79343, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "ousdMetaStrategy", + "offset": 0, + "slot": "69", + "type": "t_address" + }, + { + "astId": 79346, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "netOusdMintedForStrategy", + "offset": 0, + "slot": "70", + "type": "t_int256" + }, + { + "astId": 79349, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "netOusdMintForStrategyThreshold", + "offset": 0, + "slot": "71", + "type": "t_uint256" + }, + { + "astId": 79371, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "swapConfig", + "offset": 0, + "slot": "72", + "type": "t_struct(SwapConfig)79361_storage" + }, + { + "astId": 79375, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "isMintWhitelistedStrategy", + "offset": 0, + "slot": "73", + "type": "t_mapping(t_address,t_bool)" + }, + { + "astId": 79378, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "_deprecated_dripper", + "offset": 0, + "slot": "74", + "type": "t_address" + }, + { + "astId": 79392, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "withdrawalQueueMetadata", + "offset": 0, + "slot": "75", + "type": "t_struct(WithdrawalQueueMetadata)79388_storage" + }, + { + "astId": 79409, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "withdrawalRequests", + "offset": 0, + "slot": "77", + "type": "t_mapping(t_uint256,t_struct(WithdrawalRequest)79403_storage)" + }, + { + "astId": 79412, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "withdrawalClaimDelay", + "offset": 0, + "slot": "78", + "type": "t_uint256" + }, + { + "astId": 79415, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "lastRebase", + "offset": 0, + "slot": "79", + "type": "t_uint64" + }, + { + "astId": 79418, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "dripDuration", + "offset": 8, + "slot": "79", + "type": "t_uint64" + }, + { + "astId": 79421, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "rebasePerSecondMax", + "offset": 16, + "slot": "79", + "type": "t_uint64" + }, + { + "astId": 79424, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "rebasePerSecondTarget", + "offset": 24, + "slot": "79", + "type": "t_uint64" + }, + { + "astId": 79439, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "__gap", + "offset": 0, + "slot": "80", + "type": "t_array(t_uint256)43_storage" + }, + { + "astId": 73884, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "wethAssetIndex", + "offset": 0, + "slot": "123", + "type": "t_uint256" + }, + { + "astId": 73888, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "__gap", + "offset": 0, + "slot": "124", + "type": "t_array(t_uint256)50_storage" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_address)dyn_storage": { + "base": "t_address", + "encoding": "dynamic_array", + "label": "address[]", + "numberOfBytes": "32" + }, + "t_array(t_uint256)43_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[43]", + "numberOfBytes": "1376" + }, + "t_array(t_uint256)50_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "encoding": "inplace", + "label": "bool", + "numberOfBytes": "1" + }, + "t_contract(OUSD)69402": { + "encoding": "inplace", + "label": "contract OUSD", + "numberOfBytes": "20" + }, + "t_enum(UnitConversion)79234": { + "encoding": "inplace", + "label": "enum VaultStorage.UnitConversion", + "numberOfBytes": "1" + }, + "t_int256": { + "encoding": "inplace", + "label": "int256", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_address)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => address)", + "numberOfBytes": "32", + "value": "t_address" + }, + "t_mapping(t_address,t_bool)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => bool)", + "numberOfBytes": "32", + "value": "t_bool" + }, + "t_mapping(t_address,t_struct(Asset)79244_storage)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => struct VaultStorage.Asset)", + "numberOfBytes": "32", + "value": "t_struct(Asset)79244_storage" + }, + "t_mapping(t_address,t_struct(Strategy)79259_storage)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => struct VaultStorage.Strategy)", + "numberOfBytes": "32", + "value": "t_struct(Strategy)79259_storage" + }, + "t_mapping(t_uint256,t_struct(WithdrawalRequest)79403_storage)": { + "encoding": "mapping", + "key": "t_uint256", + "label": "mapping(uint256 => struct VaultStorage.WithdrawalRequest)", + "numberOfBytes": "32", + "value": "t_struct(WithdrawalRequest)79403_storage" + }, + "t_struct(Asset)79244_storage": { + "encoding": "inplace", + "label": "struct VaultStorage.Asset", + "members": [ + { + "astId": 79236, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "isSupported", + "offset": 0, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 79239, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "unitConversion", + "offset": 1, + "slot": "0", + "type": "t_enum(UnitConversion)79234" + }, + { + "astId": 79241, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "decimals", + "offset": 2, + "slot": "0", + "type": "t_uint8" + }, + { + "astId": 79243, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "allowedOracleSlippageBps", + "offset": 3, + "slot": "0", + "type": "t_uint16" + } + ], + "numberOfBytes": "32" + }, + "t_struct(Strategy)79259_storage": { + "encoding": "inplace", + "label": "struct VaultStorage.Strategy", + "members": [ + { + "astId": 79256, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "isSupported", + "offset": 0, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 79258, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "_deprecated", + "offset": 0, + "slot": "1", + "type": "t_uint256" + } + ], + "numberOfBytes": "64" + }, + "t_struct(SwapConfig)79361_storage": { + "encoding": "inplace", + "label": "struct VaultStorage.SwapConfig", + "members": [ + { + "astId": 79358, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "swapper", + "offset": 0, + "slot": "0", + "type": "t_address" + }, + { + "astId": 79360, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "allowedUndervalueBps", + "offset": 20, + "slot": "0", + "type": "t_uint16" + } + ], + "numberOfBytes": "32" + }, + "t_struct(WithdrawalQueueMetadata)79388_storage": { + "encoding": "inplace", + "label": "struct VaultStorage.WithdrawalQueueMetadata", + "members": [ + { + "astId": 79381, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "queued", + "offset": 0, + "slot": "0", + "type": "t_uint128" + }, + { + "astId": 79383, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "claimable", + "offset": 16, + "slot": "0", + "type": "t_uint128" + }, + { + "astId": 79385, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "claimed", + "offset": 0, + "slot": "1", + "type": "t_uint128" + }, + { + "astId": 79387, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "nextWithdrawalIndex", + "offset": 16, + "slot": "1", + "type": "t_uint128" + } + ], + "numberOfBytes": "64" + }, + "t_struct(WithdrawalRequest)79403_storage": { + "encoding": "inplace", + "label": "struct VaultStorage.WithdrawalRequest", + "members": [ + { + "astId": 79394, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "withdrawer", + "offset": 0, + "slot": "0", + "type": "t_address" + }, + { + "astId": 79396, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "claimed", + "offset": 20, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 79398, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "timestamp", + "offset": 21, + "slot": "0", + "type": "t_uint40" + }, + { + "astId": 79400, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "amount", + "offset": 0, + "slot": "1", + "type": "t_uint128" + }, + { + "astId": 79402, + "contract": "contracts/vault/OETHVaultCore.sol:OETHVaultCore", + "label": "queued", + "offset": 16, + "slot": "1", + "type": "t_uint128" + } + ], + "numberOfBytes": "64" + }, + "t_uint128": { + "encoding": "inplace", + "label": "uint128", + "numberOfBytes": "16" + }, + "t_uint16": { + "encoding": "inplace", + "label": "uint16", + "numberOfBytes": "2" + }, + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint40": { + "encoding": "inplace", + "label": "uint40", + "numberOfBytes": "5" + }, + "t_uint64": { + "encoding": "inplace", + "label": "uint64", + "numberOfBytes": "8" + }, + "t_uint8": { + "encoding": "inplace", + "label": "uint8", + "numberOfBytes": "1" + } + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/OETHVaultProxy.json b/contracts/deployments/hoodi/OETHVaultProxy.json new file mode 100644 index 0000000000..03f8808607 --- /dev/null +++ b/contracts/deployments/hoodi/OETHVaultProxy.json @@ -0,0 +1,297 @@ +{ + "address": "0xD0cC28bc8F4666286F3211e465ecF1fe5c72AC8B", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "GovernorshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "PendingGovernorshipTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "claimGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "governor", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "implementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_logic", + "type": "address" + }, + { + "internalType": "address", + "name": "_initGovernor", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "isGovernor", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newGovernor", + "type": "address" + } + ], + "name": "transferGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } + ], + "transactionHash": "0x9fa49aaf2d8832974f33b72bb2ffee08a019ebbb26270472993ef64dc6e1d84f", + "receipt": { + "to": null, + "from": "0xFD9E6005187F448957a0972a7d0C0A6dA2911236", + "contractAddress": "0xD0cC28bc8F4666286F3211e465ecF1fe5c72AC8B", + "transactionIndex": 0, + "gasUsed": "599335", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000100000002000000000000000000000000000000040000000000020000000000000000000800000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000002020000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0xa712639d07c075ee33e37bd7f1c8dd66e0ec50d982f46eee96d8c885ef38a001", + "transactionHash": "0x9fa49aaf2d8832974f33b72bb2ffee08a019ebbb26270472993ef64dc6e1d84f", + "logs": [ + { + "transactionIndex": 0, + "blockNumber": 828228, + "transactionHash": "0x9fa49aaf2d8832974f33b72bb2ffee08a019ebbb26270472993ef64dc6e1d84f", + "address": "0xD0cC28bc8F4666286F3211e465ecF1fe5c72AC8B", + "topics": [ + "0xc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000fd9e6005187f448957a0972a7d0c0a6da2911236" + ], + "data": "0x", + "logIndex": 0, + "blockHash": "0xa712639d07c075ee33e37bd7f1c8dd66e0ec50d982f46eee96d8c885ef38a001" + } + ], + "blockNumber": 828228, + "cumulativeGasUsed": "599335", + "status": 1, + "byzantium": true + }, + "args": [], + "numDeployments": 1, + "solcInputHash": "5518586a577179807341642341c56a4f", + "metadata": "{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"GovernorshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"PendingGovernorshipTransfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"implementation\",\"type\":\"address\"}],\"name\":\"Upgraded\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"governor\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"implementation\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_logic\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_initGovernor\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isGovernor\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newGovernor\",\"type\":\"address\"}],\"name\":\"transferGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newImplementation\",\"type\":\"address\"}],\"name\":\"upgradeTo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"upgradeToAndCall\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"}],\"devdoc\":{\"events\":{\"Upgraded(address)\":{\"details\":\"Emitted when the implementation is upgraded.\",\"params\":{\"implementation\":\"Address of the new implementation.\"}}},\"kind\":\"dev\",\"methods\":{\"admin()\":{\"returns\":{\"_0\":\"The address of the proxy admin/it's also the governor.\"}},\"implementation()\":{\"returns\":{\"_0\":\"The address of the implementation.\"}},\"initialize(address,address,bytes)\":{\"details\":\"Contract initializer with Governor enforcement\",\"params\":{\"_data\":\"Data to send as msg.data to the implementation to initialize the proxied contract. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.\",\"_initGovernor\":\"Address of the initial Governor.\",\"_logic\":\"Address of the initial implementation.\"}},\"transferGovernance(address)\":{\"params\":{\"_newGovernor\":\"Address of the new Governor\"}},\"upgradeTo(address)\":{\"details\":\"Upgrade the backing implementation of the proxy. Only the admin can call this function.\",\"params\":{\"_newImplementation\":\"Address of the new implementation.\"}},\"upgradeToAndCall(address,bytes)\":{\"details\":\"Upgrade the backing implementation of the proxy and call a function on the new implementation. This is useful to initialize the proxied contract.\",\"params\":{\"data\":\"Data to send as msg.data in the low level call. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\",\"newImplementation\":\"Address of the new implementation.\"}}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"claimGovernance()\":{\"notice\":\"Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor.\"},\"governor()\":{\"notice\":\"Returns the address of the current Governor.\"},\"isGovernor()\":{\"notice\":\"Returns true if the caller is the current Governor.\"},\"transferGovernance(address)\":{\"notice\":\"Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete\"}},\"notice\":\"OETHVaultProxy delegates calls to a Vault implementation\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/proxies/Proxies.sol\":\"OETHVaultProxy\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize, which returns 0 for contracts in\\n // construction, since the code is only stored at the end of the\\n // constructor execution.\\n\\n uint256 size;\\n assembly {\\n size := extcodesize(account)\\n }\\n return size > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCall(target, data, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n require(isContract(target), \\\"Address: static call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(isContract(target), \\\"Address: delegate call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x51b758a8815ecc9596c66c37d56b1d33883a444631a3f916b9fe65cb863ef7c4\",\"license\":\"MIT\"},\"contracts/governance/Governable.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\\n * from owner to governor and renounce methods removed. Does not use\\n * Context.sol like Ownable.sol does for simplification.\\n * @author Origin Protocol Inc\\n */\\nabstract contract Governable {\\n // Storage position of the owner and pendingOwner of the contract\\n // keccak256(\\\"OUSD.governor\\\");\\n bytes32 private constant governorPosition =\\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\\n\\n // keccak256(\\\"OUSD.pending.governor\\\");\\n bytes32 private constant pendingGovernorPosition =\\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\\n\\n // keccak256(\\\"OUSD.reentry.status\\\");\\n bytes32 private constant reentryStatusPosition =\\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\\n\\n // See OpenZeppelin ReentrancyGuard implementation\\n uint256 constant _NOT_ENTERED = 1;\\n uint256 constant _ENTERED = 2;\\n\\n event PendingGovernorshipTransfer(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n event GovernorshipTransferred(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n /**\\n * @notice Returns the address of the current Governor.\\n */\\n function governor() public view returns (address) {\\n return _governor();\\n }\\n\\n /**\\n * @dev Returns the address of the current Governor.\\n */\\n function _governor() internal view returns (address governorOut) {\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n governorOut := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Returns the address of the pending Governor.\\n */\\n function _pendingGovernor()\\n internal\\n view\\n returns (address pendingGovernor)\\n {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n pendingGovernor := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the Governor.\\n */\\n modifier onlyGovernor() {\\n require(isGovernor(), \\\"Caller is not the Governor\\\");\\n _;\\n }\\n\\n /**\\n * @notice Returns true if the caller is the current Governor.\\n */\\n function isGovernor() public view returns (bool) {\\n return msg.sender == _governor();\\n }\\n\\n function _setGovernor(address newGovernor) internal {\\n emit GovernorshipTransferred(_governor(), newGovernor);\\n\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @dev Prevents a contract from calling itself, directly or indirectly.\\n * Calling a `nonReentrant` function from another `nonReentrant`\\n * function is not supported. It is possible to prevent this from happening\\n * by making the `nonReentrant` function external, and make it call a\\n * `private` function that does the actual work.\\n */\\n modifier nonReentrant() {\\n bytes32 position = reentryStatusPosition;\\n uint256 _reentry_status;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n _reentry_status := sload(position)\\n }\\n\\n // On the first call to nonReentrant, _notEntered will be true\\n require(_reentry_status != _ENTERED, \\\"Reentrant call\\\");\\n\\n // Any calls to nonReentrant after this point will fail\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _ENTERED)\\n }\\n\\n _;\\n\\n // By storing the original value once again, a refund is triggered (see\\n // https://eips.ethereum.org/EIPS/eip-2200)\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _NOT_ENTERED)\\n }\\n }\\n\\n function _setPendingGovernor(address newGovernor) internal {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the current Governor. Must be claimed for this to complete\\n * @param _newGovernor Address of the new Governor\\n */\\n function transferGovernance(address _newGovernor) external onlyGovernor {\\n _setPendingGovernor(_newGovernor);\\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\\n }\\n\\n /**\\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the new Governor.\\n */\\n function claimGovernance() external {\\n require(\\n msg.sender == _pendingGovernor(),\\n \\\"Only the pending Governor can complete the claim\\\"\\n );\\n _changeGovernor(msg.sender);\\n }\\n\\n /**\\n * @dev Change Governance of the contract to a new account (`newGovernor`).\\n * @param _newGovernor Address of the new Governor\\n */\\n function _changeGovernor(address _newGovernor) internal {\\n require(_newGovernor != address(0), \\\"New Governor is address(0)\\\");\\n _setGovernor(_newGovernor);\\n }\\n}\\n\",\"keccak256\":\"0xf32f873c8bfbacf2e5f01d0cf37bc7f54fbd5aa656e95c8a599114229946f107\",\"license\":\"BUSL-1.1\"},\"contracts/proxies/InitializeGovernedUpgradeabilityProxy.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { Address } from \\\"@openzeppelin/contracts/utils/Address.sol\\\";\\n\\nimport { Governable } from \\\"../governance/Governable.sol\\\";\\n\\n/**\\n * @title BaseGovernedUpgradeabilityProxy\\n * @dev This contract combines an upgradeability proxy with our governor system.\\n * It is based on an older version of OpenZeppelins BaseUpgradeabilityProxy\\n * with Solidity ^0.8.0.\\n * @author Origin Protocol Inc\\n */\\ncontract InitializeGovernedUpgradeabilityProxy is Governable {\\n /**\\n * @dev Emitted when the implementation is upgraded.\\n * @param implementation Address of the new implementation.\\n */\\n event Upgraded(address indexed implementation);\\n\\n constructor() {\\n _setGovernor(msg.sender);\\n }\\n\\n /**\\n * @dev Contract initializer with Governor enforcement\\n * @param _logic Address of the initial implementation.\\n * @param _initGovernor Address of the initial Governor.\\n * @param _data Data to send as msg.data to the implementation to initialize\\n * the proxied contract.\\n * It should include the signature and the parameters of the function to be\\n * called, as described in\\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\\n * This parameter is optional, if no data is given the initialization call\\n * to proxied contract will be skipped.\\n */\\n function initialize(\\n address _logic,\\n address _initGovernor,\\n bytes calldata _data\\n ) public payable onlyGovernor {\\n require(_implementation() == address(0));\\n require(_logic != address(0), \\\"Implementation not set\\\");\\n assert(\\n IMPLEMENTATION_SLOT ==\\n bytes32(uint256(keccak256(\\\"eip1967.proxy.implementation\\\")) - 1)\\n );\\n _setImplementation(_logic);\\n if (_data.length > 0) {\\n (bool success, ) = _logic.delegatecall(_data);\\n require(success);\\n }\\n _changeGovernor(_initGovernor);\\n }\\n\\n /**\\n * @return The address of the proxy admin/it's also the governor.\\n */\\n function admin() external view returns (address) {\\n return _governor();\\n }\\n\\n /**\\n * @return The address of the implementation.\\n */\\n function implementation() external view returns (address) {\\n return _implementation();\\n }\\n\\n /**\\n * @dev Upgrade the backing implementation of the proxy.\\n * Only the admin can call this function.\\n * @param _newImplementation Address of the new implementation.\\n */\\n function upgradeTo(address _newImplementation) external onlyGovernor {\\n _upgradeTo(_newImplementation);\\n }\\n\\n /**\\n * @dev Upgrade the backing implementation of the proxy and call a function\\n * on the new implementation.\\n * This is useful to initialize the proxied contract.\\n * @param newImplementation Address of the new implementation.\\n * @param data Data to send as msg.data in the low level call.\\n * It should include the signature and the parameters of the function to be called, as described in\\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\\n */\\n function upgradeToAndCall(address newImplementation, bytes calldata data)\\n external\\n payable\\n onlyGovernor\\n {\\n _upgradeTo(newImplementation);\\n (bool success, ) = newImplementation.delegatecall(data);\\n require(success);\\n }\\n\\n /**\\n * @dev Fallback function.\\n * Implemented entirely in `_fallback`.\\n */\\n fallback() external payable {\\n _fallback();\\n }\\n\\n /**\\n * @dev Delegates execution to an implementation contract.\\n * This is a low level function that doesn't return to its internal call site.\\n * It will return to the external caller whatever the implementation returns.\\n * @param _impl Address to delegate.\\n */\\n function _delegate(address _impl) internal {\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n // Copy msg.data. We take full control of memory in this inline assembly\\n // block because it will not return to Solidity code. We overwrite the\\n // Solidity scratch pad at memory position 0.\\n calldatacopy(0, 0, calldatasize())\\n\\n // Call the implementation.\\n // out and outsize are 0 because we don't know the size yet.\\n let result := delegatecall(gas(), _impl, 0, calldatasize(), 0, 0)\\n\\n // Copy the returned data.\\n returndatacopy(0, 0, returndatasize())\\n\\n switch result\\n // delegatecall returns 0 on error.\\n case 0 {\\n revert(0, returndatasize())\\n }\\n default {\\n return(0, returndatasize())\\n }\\n }\\n }\\n\\n /**\\n * @dev Function that is run as the first thing in the fallback function.\\n * Can be redefined in derived contracts to add functionality.\\n * Redefinitions must call super._willFallback().\\n */\\n function _willFallback() internal {}\\n\\n /**\\n * @dev fallback implementation.\\n * Extracted to enable manual triggering.\\n */\\n function _fallback() internal {\\n _willFallback();\\n _delegate(_implementation());\\n }\\n\\n /**\\n * @dev Storage slot with the address of the current implementation.\\n * This is the keccak-256 hash of \\\"eip1967.proxy.implementation\\\" subtracted by 1, and is\\n * validated in the constructor.\\n */\\n bytes32 internal constant IMPLEMENTATION_SLOT =\\n 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\\n\\n /**\\n * @dev Returns the current implementation.\\n * @return impl Address of the current implementation\\n */\\n function _implementation() internal view returns (address impl) {\\n bytes32 slot = IMPLEMENTATION_SLOT;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n impl := sload(slot)\\n }\\n }\\n\\n /**\\n * @dev Upgrades the proxy to a new implementation.\\n * @param newImplementation Address of the new implementation.\\n */\\n function _upgradeTo(address newImplementation) internal {\\n _setImplementation(newImplementation);\\n emit Upgraded(newImplementation);\\n }\\n\\n /**\\n * @dev Sets the implementation address of the proxy.\\n * @param newImplementation Address of the new implementation.\\n */\\n function _setImplementation(address newImplementation) internal {\\n require(\\n Address.isContract(newImplementation),\\n \\\"Cannot set a proxy implementation to a non-contract address\\\"\\n );\\n\\n bytes32 slot = IMPLEMENTATION_SLOT;\\n\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(slot, newImplementation)\\n }\\n }\\n}\\n\",\"keccak256\":\"0x3f46ae39dced6fa90d8b65aa31a0a331438544ec876e2ec961a8e6b22e2e06c7\",\"license\":\"BUSL-1.1\"},\"contracts/proxies/Proxies.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { InitializeGovernedUpgradeabilityProxy } from \\\"./InitializeGovernedUpgradeabilityProxy.sol\\\";\\n\\n/**\\n * @notice OUSDProxy delegates calls to an OUSD implementation\\n */\\ncontract OUSDProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice WrappedOUSDProxy delegates calls to a WrappedOUSD implementation\\n */\\ncontract WrappedOUSDProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice VaultProxy delegates calls to a Vault implementation\\n */\\ncontract VaultProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice CompoundStrategyProxy delegates calls to a CompoundStrategy implementation\\n */\\ncontract CompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice AaveStrategyProxy delegates calls to a AaveStrategy implementation\\n */\\ncontract AaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice ConvexStrategyProxy delegates calls to a ConvexStrategy implementation\\n */\\ncontract ConvexStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice HarvesterProxy delegates calls to a Harvester implementation\\n */\\ncontract HarvesterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice DripperProxy delegates calls to a Dripper implementation\\n */\\ncontract DripperProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice MorphoCompoundStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\\n */\\ncontract MorphoCompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice ConvexOUSDMetaStrategyProxy delegates calls to a ConvexOUSDMetaStrategy implementation\\n */\\ncontract ConvexOUSDMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice MorphoAaveStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\\n */\\ncontract MorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHProxy delegates calls to nowhere for now\\n */\\ncontract OETHProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice WOETHProxy delegates calls to nowhere for now\\n */\\ncontract WOETHProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHVaultProxy delegates calls to a Vault implementation\\n */\\ncontract OETHVaultProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHDripperProxy delegates calls to a OETHDripper implementation\\n */\\ncontract OETHDripperProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHHarvesterProxy delegates calls to a Harvester implementation\\n */\\ncontract OETHHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice CurveEthStrategyProxy delegates calls to a CurveEthStrategy implementation\\n */\\ncontract ConvexEthMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice BuybackProxy delegates calls to Buyback implementation\\n */\\ncontract BuybackProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHMorphoAaveStrategyProxy delegates calls to a MorphoAaveStrategy implementation\\n */\\ncontract OETHMorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHBalancerMetaPoolrEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\\n */\\ncontract OETHBalancerMetaPoolrEthStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice OETHBalancerMetaPoolwstEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\\n */\\ncontract OETHBalancerMetaPoolwstEthStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MakerDsrStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MakerDsrStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHBuybackProxy delegates calls to Buyback implementation\\n */\\ncontract OETHBuybackProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice BridgedWOETHProxy delegates calls to BridgedWOETH implementation\\n */\\ncontract BridgedWOETHProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice NativeStakingSSVStrategyProxy delegates calls to NativeStakingSSVStrategy implementation\\n */\\ncontract NativeStakingSSVStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingFeeAccumulatorProxy delegates calls to FeeAccumulator implementation\\n */\\ncontract NativeStakingFeeAccumulatorProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingSSVStrategy2Proxy delegates calls to NativeStakingSSVStrategy implementation\\n */\\ncontract NativeStakingSSVStrategy2Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingFeeAccumulator2Proxy delegates calls to FeeAccumulator implementation\\n */\\ncontract NativeStakingFeeAccumulator2Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingSSVStrategy3Proxy delegates calls to NativeStakingSSVStrategy implementation\\n */\\ncontract NativeStakingSSVStrategy3Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingFeeAccumulator3Proxy delegates calls to FeeAccumulator implementation\\n */\\ncontract NativeStakingFeeAccumulator3Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MetaMorphoStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MetaMorphoStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice ARMBuybackProxy delegates calls to Buyback implementation\\n */\\ncontract ARMBuybackProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice MorphoGauntletPrimeUSDCStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MorphoGauntletPrimeUSDCStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MorphoGauntletPrimeUSDTStrategyProxy delegates calls to a Generalized4626USDTStrategy implementation\\n */\\ncontract MorphoGauntletPrimeUSDTStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice CurvePoolBoosterProxy delegates calls to a CurvePoolBooster implementation\\n */\\ncontract CurvePoolBoosterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHFixedRateDripperProxy delegates calls to a OETHFixedRateDripper implementation\\n */\\ncontract OETHFixedRateDripperProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHSimpleHarvesterProxy delegates calls to a OETHSimpleHarvester implementation\\n */\\ncontract OETHSimpleHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice PoolBoostCentralRegistryProxy delegates calls to the PoolBoostCentralRegistry implementation\\n */\\ncontract PoolBoostCentralRegistryProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MakerSSRStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MakerSSRStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OUSDCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\\n */\\ncontract OUSDCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\\n */\\ncontract OETHCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice CompoundingStakingSSVStrategyProxy delegates calls to a CompoundingStakingSSVStrategy implementation\\n */\\ncontract CompoundingStakingSSVStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\",\"keccak256\":\"0x71e10cf279337e54682c7b0132825fa307992782367bd296c44b664b7b705db4\",\"license\":\"BUSL-1.1\"}},\"version\":1}", + "bytecode": "0x6080604052348015600f57600080fd5b50601733601b565b6081565b6001600160a01b038116603a6000805160206109f98339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a36000805160206109f983398151915255565b610969806100906000396000f3fe6080604052600436106100865760003560e01c80635d36b190116100595780635d36b1901461010a578063c7af33521461011f578063cf7a1d7714610144578063d38bfff414610157578063f851a4401461009057610086565b80630c340a24146100905780633659cfe6146100c25780634f1ef286146100e25780635c60da1b146100f5575b61008e610177565b005b34801561009c57600080fd5b506100a5610197565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100ce57600080fd5b5061008e6100dd366004610750565b6101b4565b61008e6100f03660046107bb565b6101ed565b34801561010157600080fd5b506100a561028a565b34801561011657600080fd5b5061008e6102a2565b34801561012b57600080fd5b50610134610346565b60405190151581526020016100b9565b61008e61015236600461080e565b610377565b34801561016357600080fd5b5061008e610172366004610750565b6104e0565b6101956101906000805160206108f48339815191525490565b610584565b565b60006101af6000805160206109148339815191525490565b905090565b6101bc610346565b6101e15760405162461bcd60e51b81526004016101d89061086f565b60405180910390fd5b6101ea816105a8565b50565b6101f5610346565b6102115760405162461bcd60e51b81526004016101d89061086f565b61021a836105a8565b6000836001600160a01b031683836040516102369291906108a6565b600060405180830381855af49150503d8060008114610271576040519150601f19603f3d011682016040523d82523d6000602084013e610276565b606091505b505090508061028457600080fd5b50505050565b60006101af6000805160206108f48339815191525490565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b03161461033d5760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b60648201526084016101d8565b610195336105e8565b600061035e6000805160206109148339815191525490565b6001600160a01b0316336001600160a01b031614905090565b61037f610346565b61039b5760405162461bcd60e51b81526004016101d89061086f565b60006103b36000805160206108f48339815191525490565b6001600160a01b0316146103c657600080fd5b6001600160a01b0384166104155760405162461bcd60e51b8152602060048201526016602482015275125b5c1b195b595b9d185d1a5bdb881b9bdd081cd95d60521b60448201526064016101d8565b61044060017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd6108b6565b6000805160206108f48339815191521461045c5761045c6108dd565b61046584610647565b80156104d7576000846001600160a01b031683836040516104879291906108a6565b600060405180830381855af49150503d80600081146104c2576040519150601f19603f3d011682016040523d82523d6000602084013e6104c7565b606091505b50509050806104d557600080fd5b505b610284836105e8565b6104e8610346565b6105045760405162461bcd60e51b81526004016101d89061086f565b61052c817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b031661054c6000805160206109148339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b3660008037600080366000845af43d6000803e8080156105a3573d6000f35b3d6000fd5b6105b181610647565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b03811661063e5760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f72206973206164647265737328302900000000000060448201526064016101d8565b6101ea816106cd565b803b6106bb5760405162461bcd60e51b815260206004820152603b60248201527f43616e6e6f742073657420612070726f787920696d706c656d656e746174696f60448201527f6e20746f2061206e6f6e2d636f6e74726163742061646472657373000000000060648201526084016101d8565b6000805160206108f483398151915255565b806001600160a01b03166106ed6000805160206109148339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a360008051602061091483398151915255565b80356001600160a01b038116811461074b57600080fd5b919050565b60006020828403121561076257600080fd5b61076b82610734565b9392505050565b60008083601f84011261078457600080fd5b50813567ffffffffffffffff81111561079c57600080fd5b6020830191508360208285010111156107b457600080fd5b9250929050565b6000806000604084860312156107d057600080fd5b6107d984610734565b9250602084013567ffffffffffffffff8111156107f557600080fd5b61080186828701610772565b9497909650939450505050565b6000806000806060858703121561082457600080fd5b61082d85610734565b935061083b60208601610734565b9250604085013567ffffffffffffffff81111561085757600080fd5b61086387828801610772565b95989497509550505050565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b8183823760009101908152919050565b818103818111156108d757634e487b7160e01b600052601160045260246000fd5b92915050565b634e487b7160e01b600052600160045260246000fdfe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa2646970667358221220971da59650daa4d8bd13d795c665f4b406a5b986aba7bc11ea5f565170c279a464736f6c634300081c00337bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a", + "deployedBytecode": "0x6080604052600436106100865760003560e01c80635d36b190116100595780635d36b1901461010a578063c7af33521461011f578063cf7a1d7714610144578063d38bfff414610157578063f851a4401461009057610086565b80630c340a24146100905780633659cfe6146100c25780634f1ef286146100e25780635c60da1b146100f5575b61008e610177565b005b34801561009c57600080fd5b506100a5610197565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100ce57600080fd5b5061008e6100dd366004610750565b6101b4565b61008e6100f03660046107bb565b6101ed565b34801561010157600080fd5b506100a561028a565b34801561011657600080fd5b5061008e6102a2565b34801561012b57600080fd5b50610134610346565b60405190151581526020016100b9565b61008e61015236600461080e565b610377565b34801561016357600080fd5b5061008e610172366004610750565b6104e0565b6101956101906000805160206108f48339815191525490565b610584565b565b60006101af6000805160206109148339815191525490565b905090565b6101bc610346565b6101e15760405162461bcd60e51b81526004016101d89061086f565b60405180910390fd5b6101ea816105a8565b50565b6101f5610346565b6102115760405162461bcd60e51b81526004016101d89061086f565b61021a836105a8565b6000836001600160a01b031683836040516102369291906108a6565b600060405180830381855af49150503d8060008114610271576040519150601f19603f3d011682016040523d82523d6000602084013e610276565b606091505b505090508061028457600080fd5b50505050565b60006101af6000805160206108f48339815191525490565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b03161461033d5760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b60648201526084016101d8565b610195336105e8565b600061035e6000805160206109148339815191525490565b6001600160a01b0316336001600160a01b031614905090565b61037f610346565b61039b5760405162461bcd60e51b81526004016101d89061086f565b60006103b36000805160206108f48339815191525490565b6001600160a01b0316146103c657600080fd5b6001600160a01b0384166104155760405162461bcd60e51b8152602060048201526016602482015275125b5c1b195b595b9d185d1a5bdb881b9bdd081cd95d60521b60448201526064016101d8565b61044060017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd6108b6565b6000805160206108f48339815191521461045c5761045c6108dd565b61046584610647565b80156104d7576000846001600160a01b031683836040516104879291906108a6565b600060405180830381855af49150503d80600081146104c2576040519150601f19603f3d011682016040523d82523d6000602084013e6104c7565b606091505b50509050806104d557600080fd5b505b610284836105e8565b6104e8610346565b6105045760405162461bcd60e51b81526004016101d89061086f565b61052c817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b031661054c6000805160206109148339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b3660008037600080366000845af43d6000803e8080156105a3573d6000f35b3d6000fd5b6105b181610647565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b03811661063e5760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f72206973206164647265737328302900000000000060448201526064016101d8565b6101ea816106cd565b803b6106bb5760405162461bcd60e51b815260206004820152603b60248201527f43616e6e6f742073657420612070726f787920696d706c656d656e746174696f60448201527f6e20746f2061206e6f6e2d636f6e74726163742061646472657373000000000060648201526084016101d8565b6000805160206108f483398151915255565b806001600160a01b03166106ed6000805160206109148339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a360008051602061091483398151915255565b80356001600160a01b038116811461074b57600080fd5b919050565b60006020828403121561076257600080fd5b61076b82610734565b9392505050565b60008083601f84011261078457600080fd5b50813567ffffffffffffffff81111561079c57600080fd5b6020830191508360208285010111156107b457600080fd5b9250929050565b6000806000604084860312156107d057600080fd5b6107d984610734565b9250602084013567ffffffffffffffff8111156107f557600080fd5b61080186828701610772565b9497909650939450505050565b6000806000806060858703121561082457600080fd5b61082d85610734565b935061083b60208601610734565b9250604085013567ffffffffffffffff81111561085757600080fd5b61086387828801610772565b95989497509550505050565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b8183823760009101908152919050565b818103818111156108d757634e487b7160e01b600052601160045260246000fd5b92915050565b634e487b7160e01b600052600160045260246000fdfe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa2646970667358221220971da59650daa4d8bd13d795c665f4b406a5b986aba7bc11ea5f565170c279a464736f6c634300081c0033", + "libraries": {}, + "devdoc": { + "events": { + "Upgraded(address)": { + "details": "Emitted when the implementation is upgraded.", + "params": { + "implementation": "Address of the new implementation." + } + } + }, + "kind": "dev", + "methods": { + "admin()": { + "returns": { + "_0": "The address of the proxy admin/it's also the governor." + } + }, + "implementation()": { + "returns": { + "_0": "The address of the implementation." + } + }, + "initialize(address,address,bytes)": { + "details": "Contract initializer with Governor enforcement", + "params": { + "_data": "Data to send as msg.data to the implementation to initialize the proxied contract. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.", + "_initGovernor": "Address of the initial Governor.", + "_logic": "Address of the initial implementation." + } + }, + "transferGovernance(address)": { + "params": { + "_newGovernor": "Address of the new Governor" + } + }, + "upgradeTo(address)": { + "details": "Upgrade the backing implementation of the proxy. Only the admin can call this function.", + "params": { + "_newImplementation": "Address of the new implementation." + } + }, + "upgradeToAndCall(address,bytes)": { + "details": "Upgrade the backing implementation of the proxy and call a function on the new implementation. This is useful to initialize the proxied contract.", + "params": { + "data": "Data to send as msg.data in the low level call. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.", + "newImplementation": "Address of the new implementation." + } + } + }, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "claimGovernance()": { + "notice": "Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor." + }, + "governor()": { + "notice": "Returns the address of the current Governor." + }, + "isGovernor()": { + "notice": "Returns true if the caller is the current Governor." + }, + "transferGovernance(address)": { + "notice": "Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete" + } + }, + "notice": "OETHVaultProxy delegates calls to a Vault implementation", + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/026caa1bd50de21c27308097fbdbdd8e.json b/contracts/deployments/hoodi/solcInputs/026caa1bd50de21c27308097fbdbdd8e.json new file mode 100644 index 0000000000..ee6dedea6f --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/026caa1bd50de21c27308097fbdbdd8e.json @@ -0,0 +1,1149 @@ +{ + "language": "Solidity", + "sources": { + "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {Client} from \"../libraries/Client.sol\";\n\ninterface IRouterClient {\n error UnsupportedDestinationChain(uint64 destChainSelector);\n error InsufficientFeeTokenAmount();\n error InvalidMsgValue();\n\n /// @notice Checks if the given chain ID is supported for sending/receiving.\n /// @param chainSelector The chain to check.\n /// @return supported is true if it is supported, false if not.\n function isChainSupported(uint64 chainSelector) external view returns (bool supported);\n\n /// @notice Gets a list of all supported tokens which can be sent or received\n /// to/from a given chain id.\n /// @param chainSelector The chainSelector.\n /// @return tokens The addresses of all tokens that are supported.\n function getSupportedTokens(uint64 chainSelector) external view returns (address[] memory tokens);\n\n /// @param destinationChainSelector The destination chainSelector\n /// @param message The cross-chain CCIP message including data and/or tokens\n /// @return fee returns execution fee for the message\n /// delivery to destination chain, denominated in the feeToken specified in the message.\n /// @dev Reverts with appropriate reason upon invalid message.\n function getFee(\n uint64 destinationChainSelector,\n Client.EVM2AnyMessage memory message\n ) external view returns (uint256 fee);\n\n /// @notice Request a message to be sent to the destination chain\n /// @param destinationChainSelector The destination chain ID\n /// @param message The cross-chain CCIP message including data and/or tokens\n /// @return messageId The message ID\n /// @dev Note if msg.value is larger than the required fee (from getFee) we accept\n /// the overpayment with no refund.\n /// @dev Reverts with appropriate reason upon invalid message.\n function ccipSend(\n uint64 destinationChainSelector,\n Client.EVM2AnyMessage calldata message\n ) external payable returns (bytes32);\n}\n" + }, + "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// End consumer library.\nlibrary Client {\n /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.\n struct EVMTokenAmount {\n address token; // token address on the local chain.\n uint256 amount; // Amount of tokens.\n }\n\n struct Any2EVMMessage {\n bytes32 messageId; // MessageId corresponding to ccipSend on source.\n uint64 sourceChainSelector; // Source chain selector.\n bytes sender; // abi.decode(sender) if coming from an EVM chain.\n bytes data; // payload sent in original message.\n EVMTokenAmount[] destTokenAmounts; // Tokens and their amounts in their destination chain representation.\n }\n\n // If extraArgs is empty bytes, the default is 200k gas limit.\n struct EVM2AnyMessage {\n bytes receiver; // abi.encode(receiver address) for dest EVM chains\n bytes data; // Data payload\n EVMTokenAmount[] tokenAmounts; // Token transfers\n address feeToken; // Address of feeToken. address(0) means you will send msg.value.\n bytes extraArgs; // Populate this with _argsToBytes(EVMExtraArgsV1)\n }\n\n // bytes4(keccak256(\"CCIP EVMExtraArgsV1\"));\n bytes4 public constant EVM_EXTRA_ARGS_V1_TAG = 0x97a657c9;\n struct EVMExtraArgsV1 {\n uint256 gasLimit;\n }\n\n function _argsToBytes(EVMExtraArgsV1 memory extraArgs) internal pure returns (bytes memory bts) {\n return abi.encodeWithSelector(EVM_EXTRA_ARGS_V1_TAG, extraArgs);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport \"@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol\";\n\nlibrary ExecutorOptions {\n using CalldataBytesLib for bytes;\n\n uint8 internal constant WORKER_ID = 1;\n\n uint8 internal constant OPTION_TYPE_LZRECEIVE = 1;\n uint8 internal constant OPTION_TYPE_NATIVE_DROP = 2;\n uint8 internal constant OPTION_TYPE_LZCOMPOSE = 3;\n uint8 internal constant OPTION_TYPE_ORDERED_EXECUTION = 4;\n uint8 internal constant OPTION_TYPE_LZREAD = 5;\n\n error Executor_InvalidLzReceiveOption();\n error Executor_InvalidNativeDropOption();\n error Executor_InvalidLzComposeOption();\n error Executor_InvalidLzReadOption();\n\n /// @dev decode the next executor option from the options starting from the specified cursor\n /// @param _options [executor_id][executor_option][executor_id][executor_option]...\n /// executor_option = [option_size][option_type][option]\n /// option_size = len(option_type) + len(option)\n /// executor_id: uint8, option_size: uint16, option_type: uint8, option: bytes\n /// @param _cursor the cursor to start decoding from\n /// @return optionType the type of the option\n /// @return option the option of the executor\n /// @return cursor the cursor to start decoding the next executor option\n function nextExecutorOption(\n bytes calldata _options,\n uint256 _cursor\n ) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) {\n unchecked {\n // skip worker id\n cursor = _cursor + 1;\n\n // read option size\n uint16 size = _options.toU16(cursor);\n cursor += 2;\n\n // read option type\n optionType = _options.toU8(cursor);\n\n // startCursor and endCursor are used to slice the option from _options\n uint256 startCursor = cursor + 1; // skip option type\n uint256 endCursor = cursor + size;\n option = _options[startCursor:endCursor];\n cursor += size;\n }\n }\n\n function decodeLzReceiveOption(bytes calldata _option) internal pure returns (uint128 gas, uint128 value) {\n if (_option.length != 16 && _option.length != 32) revert Executor_InvalidLzReceiveOption();\n gas = _option.toU128(0);\n value = _option.length == 32 ? _option.toU128(16) : 0;\n }\n\n function decodeNativeDropOption(bytes calldata _option) internal pure returns (uint128 amount, bytes32 receiver) {\n if (_option.length != 48) revert Executor_InvalidNativeDropOption();\n amount = _option.toU128(0);\n receiver = _option.toB32(16);\n }\n\n function decodeLzComposeOption(\n bytes calldata _option\n ) internal pure returns (uint16 index, uint128 gas, uint128 value) {\n if (_option.length != 18 && _option.length != 34) revert Executor_InvalidLzComposeOption();\n index = _option.toU16(0);\n gas = _option.toU128(2);\n value = _option.length == 34 ? _option.toU128(18) : 0;\n }\n\n function decodeLzReadOption(\n bytes calldata _option\n ) internal pure returns (uint128 gas, uint32 calldataSize, uint128 value) {\n if (_option.length != 20 && _option.length != 36) revert Executor_InvalidLzReadOption();\n gas = _option.toU128(0);\n calldataSize = _option.toU32(16);\n value = _option.length == 36 ? _option.toU128(20) : 0;\n }\n\n function encodeLzReceiveOption(uint128 _gas, uint128 _value) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_gas) : abi.encodePacked(_gas, _value);\n }\n\n function encodeNativeDropOption(uint128 _amount, bytes32 _receiver) internal pure returns (bytes memory) {\n return abi.encodePacked(_amount, _receiver);\n }\n\n function encodeLzComposeOption(uint16 _index, uint128 _gas, uint128 _value) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_index, _gas) : abi.encodePacked(_index, _gas, _value);\n }\n\n function encodeLzReadOption(\n uint128 _gas,\n uint32 _calldataSize,\n uint128 _value\n ) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_gas, _calldataSize) : abi.encodePacked(_gas, _calldataSize, _value);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/DVNOptions.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport { BytesLib } from \"solidity-bytes-utils/contracts/BytesLib.sol\";\n\nimport { BitMap256 } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/BitMaps.sol\";\nimport { CalldataBytesLib } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol\";\n\nlibrary DVNOptions {\n using CalldataBytesLib for bytes;\n using BytesLib for bytes;\n\n uint8 internal constant WORKER_ID = 2;\n uint8 internal constant OPTION_TYPE_PRECRIME = 1;\n\n error DVN_InvalidDVNIdx();\n error DVN_InvalidDVNOptions(uint256 cursor);\n\n /// @dev group dvn options by its idx\n /// @param _options [dvn_id][dvn_option][dvn_id][dvn_option]...\n /// dvn_option = [option_size][dvn_idx][option_type][option]\n /// option_size = len(dvn_idx) + len(option_type) + len(option)\n /// dvn_id: uint8, dvn_idx: uint8, option_size: uint16, option_type: uint8, option: bytes\n /// @return dvnOptions the grouped options, still share the same format of _options\n /// @return dvnIndices the dvn indices\n function groupDVNOptionsByIdx(\n bytes memory _options\n ) internal pure returns (bytes[] memory dvnOptions, uint8[] memory dvnIndices) {\n if (_options.length == 0) return (dvnOptions, dvnIndices);\n\n uint8 numDVNs = getNumDVNs(_options);\n\n // if there is only 1 dvn, we can just return the whole options\n if (numDVNs == 1) {\n dvnOptions = new bytes[](1);\n dvnOptions[0] = _options;\n\n dvnIndices = new uint8[](1);\n dvnIndices[0] = _options.toUint8(3); // dvn idx\n return (dvnOptions, dvnIndices);\n }\n\n // otherwise, we need to group the options by dvn_idx\n dvnIndices = new uint8[](numDVNs);\n dvnOptions = new bytes[](numDVNs);\n unchecked {\n uint256 cursor = 0;\n uint256 start = 0;\n uint8 lastDVNIdx = 255; // 255 is an invalid dvn_idx\n\n while (cursor < _options.length) {\n ++cursor; // skip worker_id\n\n // optionLength asserted in getNumDVNs (skip check)\n uint16 optionLength = _options.toUint16(cursor);\n cursor += 2;\n\n // dvnIdx asserted in getNumDVNs (skip check)\n uint8 dvnIdx = _options.toUint8(cursor);\n\n // dvnIdx must equal to the lastDVNIdx for the first option\n // so it is always skipped in the first option\n // this operation slices out options whenever the scan finds a different lastDVNIdx\n if (lastDVNIdx == 255) {\n lastDVNIdx = dvnIdx;\n } else if (dvnIdx != lastDVNIdx) {\n uint256 len = cursor - start - 3; // 3 is for worker_id and option_length\n bytes memory opt = _options.slice(start, len);\n _insertDVNOptions(dvnOptions, dvnIndices, lastDVNIdx, opt);\n\n // reset the start and lastDVNIdx\n start += len;\n lastDVNIdx = dvnIdx;\n }\n\n cursor += optionLength;\n }\n\n // skip check the cursor here because the cursor is asserted in getNumDVNs\n // if we have reached the end of the options, we need to process the last dvn\n uint256 size = cursor - start;\n bytes memory op = _options.slice(start, size);\n _insertDVNOptions(dvnOptions, dvnIndices, lastDVNIdx, op);\n\n // revert dvnIndices to start from 0\n for (uint8 i = 0; i < numDVNs; ++i) {\n --dvnIndices[i];\n }\n }\n }\n\n function _insertDVNOptions(\n bytes[] memory _dvnOptions,\n uint8[] memory _dvnIndices,\n uint8 _dvnIdx,\n bytes memory _newOptions\n ) internal pure {\n // dvnIdx starts from 0 but default value of dvnIndices is 0,\n // so we tell if the slot is empty by adding 1 to dvnIdx\n if (_dvnIdx == 255) revert DVN_InvalidDVNIdx();\n uint8 dvnIdxAdj = _dvnIdx + 1;\n\n for (uint256 j = 0; j < _dvnIndices.length; ++j) {\n uint8 index = _dvnIndices[j];\n if (dvnIdxAdj == index) {\n _dvnOptions[j] = abi.encodePacked(_dvnOptions[j], _newOptions);\n break;\n } else if (index == 0) {\n // empty slot, that means it is the first time we see this dvn\n _dvnIndices[j] = dvnIdxAdj;\n _dvnOptions[j] = _newOptions;\n break;\n }\n }\n }\n\n /// @dev get the number of unique dvns\n /// @param _options the format is the same as groupDVNOptionsByIdx\n function getNumDVNs(bytes memory _options) internal pure returns (uint8 numDVNs) {\n uint256 cursor = 0;\n BitMap256 bitmap;\n\n // find number of unique dvn_idx\n unchecked {\n while (cursor < _options.length) {\n ++cursor; // skip worker_id\n\n uint16 optionLength = _options.toUint16(cursor);\n cursor += 2;\n if (optionLength < 2) revert DVN_InvalidDVNOptions(cursor); // at least 1 byte for dvn_idx and 1 byte for option_type\n\n uint8 dvnIdx = _options.toUint8(cursor);\n\n // if dvnIdx is not set, increment numDVNs\n // max num of dvns is 255, 255 is an invalid dvn_idx\n // The order of the dvnIdx is not required to be sequential, as enforcing the order may weaken\n // the composability of the options. e.g. if we refrain from enforcing the order, an OApp that has\n // already enforced certain options can append additional options to the end of the enforced\n // ones without restrictions.\n if (dvnIdx == 255) revert DVN_InvalidDVNIdx();\n if (!bitmap.get(dvnIdx)) {\n ++numDVNs;\n bitmap = bitmap.set(dvnIdx);\n }\n\n cursor += optionLength;\n }\n }\n if (cursor != _options.length) revert DVN_InvalidDVNOptions(cursor);\n }\n\n /// @dev decode the next dvn option from _options starting from the specified cursor\n /// @param _options the format is the same as groupDVNOptionsByIdx\n /// @param _cursor the cursor to start decoding\n /// @return optionType the type of the option\n /// @return option the option\n /// @return cursor the cursor to start decoding the next option\n function nextDVNOption(\n bytes calldata _options,\n uint256 _cursor\n ) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) {\n unchecked {\n // skip worker id\n cursor = _cursor + 1;\n\n // read option size\n uint16 size = _options.toU16(cursor);\n cursor += 2;\n\n // read option type\n optionType = _options.toU8(cursor + 1); // skip dvn_idx\n\n // startCursor and endCursor are used to slice the option from _options\n uint256 startCursor = cursor + 2; // skip option type and dvn_idx\n uint256 endCursor = cursor + size;\n option = _options[startCursor:endCursor];\n cursor += size;\n }\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { IMessageLibManager } from \"./IMessageLibManager.sol\";\nimport { IMessagingComposer } from \"./IMessagingComposer.sol\";\nimport { IMessagingChannel } from \"./IMessagingChannel.sol\";\nimport { IMessagingContext } from \"./IMessagingContext.sol\";\n\nstruct MessagingParams {\n uint32 dstEid;\n bytes32 receiver;\n bytes message;\n bytes options;\n bool payInLzToken;\n}\n\nstruct MessagingReceipt {\n bytes32 guid;\n uint64 nonce;\n MessagingFee fee;\n}\n\nstruct MessagingFee {\n uint256 nativeFee;\n uint256 lzTokenFee;\n}\n\nstruct Origin {\n uint32 srcEid;\n bytes32 sender;\n uint64 nonce;\n}\n\ninterface ILayerZeroEndpointV2 is IMessageLibManager, IMessagingComposer, IMessagingChannel, IMessagingContext {\n event PacketSent(bytes encodedPayload, bytes options, address sendLibrary);\n\n event PacketVerified(Origin origin, address receiver, bytes32 payloadHash);\n\n event PacketDelivered(Origin origin, address receiver);\n\n event LzReceiveAlert(\n address indexed receiver,\n address indexed executor,\n Origin origin,\n bytes32 guid,\n uint256 gas,\n uint256 value,\n bytes message,\n bytes extraData,\n bytes reason\n );\n\n event LzTokenSet(address token);\n\n event DelegateSet(address sender, address delegate);\n\n function quote(MessagingParams calldata _params, address _sender) external view returns (MessagingFee memory);\n\n function send(\n MessagingParams calldata _params,\n address _refundAddress\n ) external payable returns (MessagingReceipt memory);\n\n function verify(Origin calldata _origin, address _receiver, bytes32 _payloadHash) external;\n\n function verifiable(Origin calldata _origin, address _receiver) external view returns (bool);\n\n function initializable(Origin calldata _origin, address _receiver) external view returns (bool);\n\n function lzReceive(\n Origin calldata _origin,\n address _receiver,\n bytes32 _guid,\n bytes calldata _message,\n bytes calldata _extraData\n ) external payable;\n\n // oapp can burn messages partially by calling this function with its own business logic if messages are verified in order\n function clear(address _oapp, Origin calldata _origin, bytes32 _guid, bytes calldata _message) external;\n\n function setLzToken(address _lzToken) external;\n\n function lzToken() external view returns (address);\n\n function nativeToken() external view returns (address);\n\n function setDelegate(address _delegate) external;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { Origin } from \"./ILayerZeroEndpointV2.sol\";\n\ninterface ILayerZeroReceiver {\n function allowInitializePath(Origin calldata _origin) external view returns (bool);\n\n function nextNonce(uint32 _eid, bytes32 _sender) external view returns (uint64);\n\n function lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) external payable;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessageLib.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { IERC165 } from \"@openzeppelin/contracts/utils/introspection/IERC165.sol\";\n\nimport { SetConfigParam } from \"./IMessageLibManager.sol\";\n\nenum MessageLibType {\n Send,\n Receive,\n SendAndReceive\n}\n\ninterface IMessageLib is IERC165 {\n function setConfig(address _oapp, SetConfigParam[] calldata _config) external;\n\n function getConfig(uint32 _eid, address _oapp, uint32 _configType) external view returns (bytes memory config);\n\n function isSupportedEid(uint32 _eid) external view returns (bool);\n\n // message libs of same major version are compatible\n function version() external view returns (uint64 major, uint8 minor, uint8 endpointVersion);\n\n function messageLibType() external view returns (MessageLibType);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessageLibManager.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nstruct SetConfigParam {\n uint32 eid;\n uint32 configType;\n bytes config;\n}\n\ninterface IMessageLibManager {\n struct Timeout {\n address lib;\n uint256 expiry;\n }\n\n event LibraryRegistered(address newLib);\n event DefaultSendLibrarySet(uint32 eid, address newLib);\n event DefaultReceiveLibrarySet(uint32 eid, address newLib);\n event DefaultReceiveLibraryTimeoutSet(uint32 eid, address oldLib, uint256 expiry);\n event SendLibrarySet(address sender, uint32 eid, address newLib);\n event ReceiveLibrarySet(address receiver, uint32 eid, address newLib);\n event ReceiveLibraryTimeoutSet(address receiver, uint32 eid, address oldLib, uint256 timeout);\n\n function registerLibrary(address _lib) external;\n\n function isRegisteredLibrary(address _lib) external view returns (bool);\n\n function getRegisteredLibraries() external view returns (address[] memory);\n\n function setDefaultSendLibrary(uint32 _eid, address _newLib) external;\n\n function defaultSendLibrary(uint32 _eid) external view returns (address);\n\n function setDefaultReceiveLibrary(uint32 _eid, address _newLib, uint256 _gracePeriod) external;\n\n function defaultReceiveLibrary(uint32 _eid) external view returns (address);\n\n function setDefaultReceiveLibraryTimeout(uint32 _eid, address _lib, uint256 _expiry) external;\n\n function defaultReceiveLibraryTimeout(uint32 _eid) external view returns (address lib, uint256 expiry);\n\n function isSupportedEid(uint32 _eid) external view returns (bool);\n\n function isValidReceiveLibrary(address _receiver, uint32 _eid, address _lib) external view returns (bool);\n\n /// ------------------- OApp interfaces -------------------\n function setSendLibrary(address _oapp, uint32 _eid, address _newLib) external;\n\n function getSendLibrary(address _sender, uint32 _eid) external view returns (address lib);\n\n function isDefaultSendLibrary(address _sender, uint32 _eid) external view returns (bool);\n\n function setReceiveLibrary(address _oapp, uint32 _eid, address _newLib, uint256 _gracePeriod) external;\n\n function getReceiveLibrary(address _receiver, uint32 _eid) external view returns (address lib, bool isDefault);\n\n function setReceiveLibraryTimeout(address _oapp, uint32 _eid, address _lib, uint256 _expiry) external;\n\n function receiveLibraryTimeout(address _receiver, uint32 _eid) external view returns (address lib, uint256 expiry);\n\n function setConfig(address _oapp, address _lib, SetConfigParam[] calldata _params) external;\n\n function getConfig(\n address _oapp,\n address _lib,\n uint32 _eid,\n uint32 _configType\n ) external view returns (bytes memory config);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingChannel.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingChannel {\n event InboundNonceSkipped(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce);\n event PacketNilified(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);\n event PacketBurnt(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);\n\n function eid() external view returns (uint32);\n\n // this is an emergency function if a message cannot be verified for some reasons\n // required to provide _nextNonce to avoid race condition\n function skip(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce) external;\n\n function nilify(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;\n\n function burn(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;\n\n function nextGuid(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (bytes32);\n\n function inboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);\n\n function outboundNonce(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (uint64);\n\n function inboundPayloadHash(\n address _receiver,\n uint32 _srcEid,\n bytes32 _sender,\n uint64 _nonce\n ) external view returns (bytes32);\n\n function lazyInboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingComposer.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingComposer {\n event ComposeSent(address from, address to, bytes32 guid, uint16 index, bytes message);\n event ComposeDelivered(address from, address to, bytes32 guid, uint16 index);\n event LzComposeAlert(\n address indexed from,\n address indexed to,\n address indexed executor,\n bytes32 guid,\n uint16 index,\n uint256 gas,\n uint256 value,\n bytes message,\n bytes extraData,\n bytes reason\n );\n\n function composeQueue(\n address _from,\n address _to,\n bytes32 _guid,\n uint16 _index\n ) external view returns (bytes32 messageHash);\n\n function sendCompose(address _to, bytes32 _guid, uint16 _index, bytes calldata _message) external;\n\n function lzCompose(\n address _from,\n address _to,\n bytes32 _guid,\n uint16 _index,\n bytes calldata _message,\n bytes calldata _extraData\n ) external payable;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingContext.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingContext {\n function isSendingMessage() external view returns (bool);\n\n function getSendContext() external view returns (uint32 dstEid, address sender);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ISendLib.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { MessagingFee } from \"./ILayerZeroEndpointV2.sol\";\nimport { IMessageLib } from \"./IMessageLib.sol\";\n\nstruct Packet {\n uint64 nonce;\n uint32 srcEid;\n address sender;\n uint32 dstEid;\n bytes32 receiver;\n bytes32 guid;\n bytes message;\n}\n\ninterface ISendLib is IMessageLib {\n function send(\n Packet calldata _packet,\n bytes calldata _options,\n bool _payInLzToken\n ) external returns (MessagingFee memory, bytes memory encodedPacket);\n\n function quote(\n Packet calldata _packet,\n bytes calldata _options,\n bool _payInLzToken\n ) external view returns (MessagingFee memory);\n\n function setTreasury(address _treasury) external;\n\n function withdrawFee(address _to, uint256 _amount) external;\n\n function withdrawLzTokenFee(address _lzToken, address _to, uint256 _amount) external;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/AddressCast.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nlibrary AddressCast {\n error AddressCast_InvalidSizeForAddress();\n error AddressCast_InvalidAddress();\n\n function toBytes32(bytes calldata _addressBytes) internal pure returns (bytes32 result) {\n if (_addressBytes.length > 32) revert AddressCast_InvalidAddress();\n result = bytes32(_addressBytes);\n unchecked {\n uint256 offset = 32 - _addressBytes.length;\n result = result >> (offset * 8);\n }\n }\n\n function toBytes32(address _address) internal pure returns (bytes32 result) {\n result = bytes32(uint256(uint160(_address)));\n }\n\n function toBytes(bytes32 _addressBytes32, uint256 _size) internal pure returns (bytes memory result) {\n if (_size == 0 || _size > 32) revert AddressCast_InvalidSizeForAddress();\n result = new bytes(_size);\n unchecked {\n uint256 offset = 256 - _size * 8;\n assembly {\n mstore(add(result, 32), shl(offset, _addressBytes32))\n }\n }\n }\n\n function toAddress(bytes32 _addressBytes32) internal pure returns (address result) {\n result = address(uint160(uint256(_addressBytes32)));\n }\n\n function toAddress(bytes calldata _addressBytes) internal pure returns (address result) {\n if (_addressBytes.length != 20) revert AddressCast_InvalidAddress();\n result = address(bytes20(_addressBytes));\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nlibrary CalldataBytesLib {\n function toU8(bytes calldata _bytes, uint256 _start) internal pure returns (uint8) {\n return uint8(_bytes[_start]);\n }\n\n function toU16(bytes calldata _bytes, uint256 _start) internal pure returns (uint16) {\n unchecked {\n uint256 end = _start + 2;\n return uint16(bytes2(_bytes[_start:end]));\n }\n }\n\n function toU32(bytes calldata _bytes, uint256 _start) internal pure returns (uint32) {\n unchecked {\n uint256 end = _start + 4;\n return uint32(bytes4(_bytes[_start:end]));\n }\n }\n\n function toU64(bytes calldata _bytes, uint256 _start) internal pure returns (uint64) {\n unchecked {\n uint256 end = _start + 8;\n return uint64(bytes8(_bytes[_start:end]));\n }\n }\n\n function toU128(bytes calldata _bytes, uint256 _start) internal pure returns (uint128) {\n unchecked {\n uint256 end = _start + 16;\n return uint128(bytes16(_bytes[_start:end]));\n }\n }\n\n function toU256(bytes calldata _bytes, uint256 _start) internal pure returns (uint256) {\n unchecked {\n uint256 end = _start + 32;\n return uint256(bytes32(_bytes[_start:end]));\n }\n }\n\n function toAddr(bytes calldata _bytes, uint256 _start) internal pure returns (address) {\n unchecked {\n uint256 end = _start + 20;\n return address(bytes20(_bytes[_start:end]));\n }\n }\n\n function toB32(bytes calldata _bytes, uint256 _start) internal pure returns (bytes32) {\n unchecked {\n uint256 end = _start + 32;\n return bytes32(_bytes[_start:end]);\n }\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/BitMaps.sol": { + "content": "// SPDX-License-Identifier: MIT\n\n// modified from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/structs/BitMaps.sol\npragma solidity ^0.8.20;\n\ntype BitMap256 is uint256;\n\nusing BitMaps for BitMap256 global;\n\nlibrary BitMaps {\n /**\n * @dev Returns whether the bit at `index` is set.\n */\n function get(BitMap256 bitmap, uint8 index) internal pure returns (bool) {\n uint256 mask = 1 << index;\n return BitMap256.unwrap(bitmap) & mask != 0;\n }\n\n /**\n * @dev Sets the bit at `index`.\n */\n function set(BitMap256 bitmap, uint8 index) internal pure returns (BitMap256) {\n uint256 mask = 1 << index;\n return BitMap256.wrap(BitMap256.unwrap(bitmap) | mask);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport { Packet } from \"../../interfaces/ISendLib.sol\";\nimport { AddressCast } from \"../../libs/AddressCast.sol\";\n\nlibrary PacketV1Codec {\n using AddressCast for address;\n using AddressCast for bytes32;\n\n uint8 internal constant PACKET_VERSION = 1;\n\n // header (version + nonce + path)\n // version\n uint256 private constant PACKET_VERSION_OFFSET = 0;\n // nonce\n uint256 private constant NONCE_OFFSET = 1;\n // path\n uint256 private constant SRC_EID_OFFSET = 9;\n uint256 private constant SENDER_OFFSET = 13;\n uint256 private constant DST_EID_OFFSET = 45;\n uint256 private constant RECEIVER_OFFSET = 49;\n // payload (guid + message)\n uint256 private constant GUID_OFFSET = 81; // keccak256(nonce + path)\n uint256 private constant MESSAGE_OFFSET = 113;\n\n function encode(Packet memory _packet) internal pure returns (bytes memory encodedPacket) {\n encodedPacket = abi.encodePacked(\n PACKET_VERSION,\n _packet.nonce,\n _packet.srcEid,\n _packet.sender.toBytes32(),\n _packet.dstEid,\n _packet.receiver,\n _packet.guid,\n _packet.message\n );\n }\n\n function encodePacketHeader(Packet memory _packet) internal pure returns (bytes memory) {\n return\n abi.encodePacked(\n PACKET_VERSION,\n _packet.nonce,\n _packet.srcEid,\n _packet.sender.toBytes32(),\n _packet.dstEid,\n _packet.receiver\n );\n }\n\n function encodePayload(Packet memory _packet) internal pure returns (bytes memory) {\n return abi.encodePacked(_packet.guid, _packet.message);\n }\n\n function header(bytes calldata _packet) internal pure returns (bytes calldata) {\n return _packet[0:GUID_OFFSET];\n }\n\n function version(bytes calldata _packet) internal pure returns (uint8) {\n return uint8(bytes1(_packet[PACKET_VERSION_OFFSET:NONCE_OFFSET]));\n }\n\n function nonce(bytes calldata _packet) internal pure returns (uint64) {\n return uint64(bytes8(_packet[NONCE_OFFSET:SRC_EID_OFFSET]));\n }\n\n function srcEid(bytes calldata _packet) internal pure returns (uint32) {\n return uint32(bytes4(_packet[SRC_EID_OFFSET:SENDER_OFFSET]));\n }\n\n function sender(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[SENDER_OFFSET:DST_EID_OFFSET]);\n }\n\n function senderAddressB20(bytes calldata _packet) internal pure returns (address) {\n return sender(_packet).toAddress();\n }\n\n function dstEid(bytes calldata _packet) internal pure returns (uint32) {\n return uint32(bytes4(_packet[DST_EID_OFFSET:RECEIVER_OFFSET]));\n }\n\n function receiver(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[RECEIVER_OFFSET:GUID_OFFSET]);\n }\n\n function receiverB20(bytes calldata _packet) internal pure returns (address) {\n return receiver(_packet).toAddress();\n }\n\n function guid(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[GUID_OFFSET:MESSAGE_OFFSET]);\n }\n\n function message(bytes calldata _packet) internal pure returns (bytes calldata) {\n return bytes(_packet[MESSAGE_OFFSET:]);\n }\n\n function payload(bytes calldata _packet) internal pure returns (bytes calldata) {\n return bytes(_packet[GUID_OFFSET:]);\n }\n\n function payloadHash(bytes calldata _packet) internal pure returns (bytes32) {\n return keccak256(payload(_packet));\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { ILayerZeroEndpointV2 } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\n\n/**\n * @title IOAppCore\n */\ninterface IOAppCore {\n // Custom error messages\n error OnlyPeer(uint32 eid, bytes32 sender);\n error NoPeer(uint32 eid);\n error InvalidEndpointCall();\n error InvalidDelegate();\n\n // Event emitted when a peer (OApp) is set for a corresponding endpoint\n event PeerSet(uint32 eid, bytes32 peer);\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n */\n function oAppVersion() external view returns (uint64 senderVersion, uint64 receiverVersion);\n\n /**\n * @notice Retrieves the LayerZero endpoint associated with the OApp.\n * @return iEndpoint The LayerZero endpoint as an interface.\n */\n function endpoint() external view returns (ILayerZeroEndpointV2 iEndpoint);\n\n /**\n * @notice Retrieves the peer (OApp) associated with a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @return peer The peer address (OApp instance) associated with the corresponding endpoint.\n */\n function peers(uint32 _eid) external view returns (bytes32 peer);\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n */\n function setPeer(uint32 _eid, bytes32 _peer) external;\n\n /**\n * @notice Sets the delegate address for the OApp Core.\n * @param _delegate The address of the delegate to be set.\n */\n function setDelegate(address _delegate) external;\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppMsgInspector.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n/**\n * @title IOAppMsgInspector\n * @dev Interface for the OApp Message Inspector, allowing examination of message and options contents.\n */\ninterface IOAppMsgInspector {\n // Custom error message for inspection failure\n error InspectionFailed(bytes message, bytes options);\n\n /**\n * @notice Allows the inspector to examine LayerZero message contents and optionally throw a revert if invalid.\n * @param _message The message payload to be inspected.\n * @param _options Additional options or parameters for inspection.\n * @return valid A boolean indicating whether the inspection passed (true) or failed (false).\n *\n * @dev Optionally done as a revert, OR use the boolean provided to handle the failure.\n */\n function inspect(bytes calldata _message, bytes calldata _options) external view returns (bool valid);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppOptionsType3.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Struct representing enforced option parameters.\n */\nstruct EnforcedOptionParam {\n uint32 eid; // Endpoint ID\n uint16 msgType; // Message Type\n bytes options; // Additional options\n}\n\n/**\n * @title IOAppOptionsType3\n * @dev Interface for the OApp with Type 3 Options, allowing the setting and combining of enforced options.\n */\ninterface IOAppOptionsType3 {\n // Custom error message for invalid options\n error InvalidOptions(bytes options);\n\n // Event emitted when enforced options are set\n event EnforcedOptionSet(EnforcedOptionParam[] _enforcedOptions);\n\n /**\n * @notice Sets enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n */\n function setEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) external;\n\n /**\n * @notice Combines options for a given endpoint and message type.\n * @param _eid The endpoint ID.\n * @param _msgType The OApp message type.\n * @param _extraOptions Additional options passed by the caller.\n * @return options The combination of caller specified options AND enforced options.\n */\n function combineOptions(\n uint32 _eid,\n uint16 _msgType,\n bytes calldata _extraOptions\n ) external view returns (bytes memory options);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\nimport { ILayerZeroReceiver, Origin } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroReceiver.sol\";\n\ninterface IOAppReceiver is ILayerZeroReceiver {\n /**\n * @notice Indicates whether an address is an approved composeMsg sender to the Endpoint.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _message The lzReceive payload.\n * @param _sender The sender address.\n * @return isSender Is a valid sender.\n *\n * @dev Applications can optionally choose to implement a separate composeMsg sender that is NOT the bridging layer.\n * @dev The default sender IS the OAppReceiver implementer.\n */\n function isComposeMsgSender(\n Origin calldata _origin,\n bytes calldata _message,\n address _sender\n ) external view returns (bool isSender);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IOAppOptionsType3, EnforcedOptionParam } from \"../interfaces/IOAppOptionsType3.sol\";\n\n/**\n * @title OAppOptionsType3\n * @dev Abstract contract implementing the IOAppOptionsType3 interface with type 3 options.\n */\nabstract contract OAppOptionsType3 is IOAppOptionsType3, Ownable {\n uint16 internal constant OPTION_TYPE_3 = 3;\n\n // @dev The \"msgType\" should be defined in the child contract.\n mapping(uint32 eid => mapping(uint16 msgType => bytes enforcedOption)) public enforcedOptions;\n\n /**\n * @dev Sets the enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Provides a way for the OApp to enforce things like paying for PreCrime, AND/OR minimum dst lzReceive gas amounts etc.\n * @dev These enforced options can vary as the potential options/execution on the remote may differ as per the msgType.\n * eg. Amount of lzReceive() gas necessary to deliver a lzCompose() message adds overhead you dont want to pay\n * if you are only making a standard LayerZero message ie. lzReceive() WITHOUT sendCompose().\n */\n function setEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) public virtual onlyOwner {\n _setEnforcedOptions(_enforcedOptions);\n }\n\n /**\n * @dev Sets the enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n *\n * @dev Provides a way for the OApp to enforce things like paying for PreCrime, AND/OR minimum dst lzReceive gas amounts etc.\n * @dev These enforced options can vary as the potential options/execution on the remote may differ as per the msgType.\n * eg. Amount of lzReceive() gas necessary to deliver a lzCompose() message adds overhead you dont want to pay\n * if you are only making a standard LayerZero message ie. lzReceive() WITHOUT sendCompose().\n */\n function _setEnforcedOptions(EnforcedOptionParam[] memory _enforcedOptions) internal virtual {\n for (uint256 i = 0; i < _enforcedOptions.length; i++) {\n // @dev Enforced options are only available for optionType 3, as type 1 and 2 dont support combining.\n _assertOptionsType3(_enforcedOptions[i].options);\n enforcedOptions[_enforcedOptions[i].eid][_enforcedOptions[i].msgType] = _enforcedOptions[i].options;\n }\n\n emit EnforcedOptionSet(_enforcedOptions);\n }\n\n /**\n * @notice Combines options for a given endpoint and message type.\n * @param _eid The endpoint ID.\n * @param _msgType The OAPP message type.\n * @param _extraOptions Additional options passed by the caller.\n * @return options The combination of caller specified options AND enforced options.\n *\n * @dev If there is an enforced lzReceive option:\n * - {gasLimit: 200k, msg.value: 1 ether} AND a caller supplies a lzReceive option: {gasLimit: 100k, msg.value: 0.5 ether}\n * - The resulting options will be {gasLimit: 300k, msg.value: 1.5 ether} when the message is executed on the remote lzReceive() function.\n * @dev This presence of duplicated options is handled off-chain in the verifier/executor.\n */\n function combineOptions(\n uint32 _eid,\n uint16 _msgType,\n bytes calldata _extraOptions\n ) public view virtual returns (bytes memory) {\n bytes memory enforced = enforcedOptions[_eid][_msgType];\n\n // No enforced options, pass whatever the caller supplied, even if it's empty or legacy type 1/2 options.\n if (enforced.length == 0) return _extraOptions;\n\n // No caller options, return enforced\n if (_extraOptions.length == 0) return enforced;\n\n // @dev If caller provided _extraOptions, must be type 3 as its the ONLY type that can be combined.\n if (_extraOptions.length >= 2) {\n _assertOptionsType3(_extraOptions);\n // @dev Remove the first 2 bytes containing the type from the _extraOptions and combine with enforced.\n return bytes.concat(enforced, _extraOptions[2:]);\n }\n\n // No valid set of options was found.\n revert InvalidOptions(_extraOptions);\n }\n\n /**\n * @dev Internal function to assert that options are of type 3.\n * @param _options The options to be checked.\n */\n function _assertOptionsType3(bytes memory _options) internal pure virtual {\n uint16 optionsType;\n assembly {\n optionsType := mload(add(_options, 2))\n }\n if (optionsType != OPTION_TYPE_3) revert InvalidOptions(_options);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { BytesLib } from \"solidity-bytes-utils/contracts/BytesLib.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { ExecutorOptions } from \"@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol\";\nimport { DVNOptions } from \"@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/DVNOptions.sol\";\n\n/**\n * @title OptionsBuilder\n * @dev Library for building and encoding various message options.\n */\nlibrary OptionsBuilder {\n using SafeCast for uint256;\n using BytesLib for bytes;\n\n // Constants for options types\n uint16 internal constant TYPE_1 = 1; // legacy options type 1\n uint16 internal constant TYPE_2 = 2; // legacy options type 2\n uint16 internal constant TYPE_3 = 3;\n\n // Custom error message\n error InvalidSize(uint256 max, uint256 actual);\n error InvalidOptionType(uint16 optionType);\n\n // Modifier to ensure only options of type 3 are used\n modifier onlyType3(bytes memory _options) {\n if (_options.toUint16(0) != TYPE_3) revert InvalidOptionType(_options.toUint16(0));\n _;\n }\n\n /**\n * @dev Creates a new options container with type 3.\n * @return options The newly created options container.\n */\n function newOptions() internal pure returns (bytes memory) {\n return abi.encodePacked(TYPE_3);\n }\n\n /**\n * @dev Adds an executor LZ receive option to the existing options.\n * @param _options The existing options container.\n * @param _gas The gasLimit used on the lzReceive() function in the OApp.\n * @param _value The msg.value passed to the lzReceive() function in the OApp.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed by the executor\n * eg. if (_gas: 200k, and _value: 1 ether) AND (_gas: 100k, _value: 0.5 ether) are sent in an option to the LayerZeroEndpoint,\n * that becomes (300k, 1.5 ether) when the message is executed on the remote lzReceive() function.\n */\n function addExecutorLzReceiveOption(\n bytes memory _options,\n uint128 _gas,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzReceiveOption(_gas, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZRECEIVE, option);\n }\n\n /**\n * @dev Adds an executor native drop option to the existing options.\n * @param _options The existing options container.\n * @param _amount The amount for the native value that is airdropped to the 'receiver'.\n * @param _receiver The receiver address for the native drop option.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed by the executor on the remote chain.\n */\n function addExecutorNativeDropOption(\n bytes memory _options,\n uint128 _amount,\n bytes32 _receiver\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeNativeDropOption(_amount, _receiver);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_NATIVE_DROP, option);\n }\n\n // /**\n // * @dev Adds an executor native drop option to the existing options.\n // * @param _options The existing options container.\n // * @param _amount The amount for the native value that is airdropped to the 'receiver'.\n // * @param _receiver The receiver address for the native drop option.\n // * @return options The updated options container.\n // *\n // * @dev When multiples of this option are added, they are summed by the executor on the remote chain.\n // */\n function addExecutorLzReadOption(\n bytes memory _options,\n uint128 _gas,\n uint32 _size,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzReadOption(_gas, _size, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZREAD, option);\n }\n\n /**\n * @dev Adds an executor LZ compose option to the existing options.\n * @param _options The existing options container.\n * @param _index The index for the lzCompose() function call.\n * @param _gas The gasLimit for the lzCompose() function call.\n * @param _value The msg.value for the lzCompose() function call.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed PER index by the executor on the remote chain.\n * @dev If the OApp sends N lzCompose calls on the remote, you must provide N incremented indexes starting with 0.\n * ie. When your remote OApp composes (N = 3) messages, you must set this option for index 0,1,2\n */\n function addExecutorLzComposeOption(\n bytes memory _options,\n uint16 _index,\n uint128 _gas,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzComposeOption(_index, _gas, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZCOMPOSE, option);\n }\n\n /**\n * @dev Adds an executor ordered execution option to the existing options.\n * @param _options The existing options container.\n * @return options The updated options container.\n */\n function addExecutorOrderedExecutionOption(\n bytes memory _options\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_ORDERED_EXECUTION, bytes(\"\"));\n }\n\n /**\n * @dev Adds a DVN pre-crime option to the existing options.\n * @param _options The existing options container.\n * @param _dvnIdx The DVN index for the pre-crime option.\n * @return options The updated options container.\n */\n function addDVNPreCrimeOption(\n bytes memory _options,\n uint8 _dvnIdx\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return addDVNOption(_options, _dvnIdx, DVNOptions.OPTION_TYPE_PRECRIME, bytes(\"\"));\n }\n\n /**\n * @dev Adds an executor option to the existing options.\n * @param _options The existing options container.\n * @param _optionType The type of the executor option.\n * @param _option The encoded data for the executor option.\n * @return options The updated options container.\n */\n function addExecutorOption(\n bytes memory _options,\n uint8 _optionType,\n bytes memory _option\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return\n abi.encodePacked(\n _options,\n ExecutorOptions.WORKER_ID,\n _option.length.toUint16() + 1, // +1 for optionType\n _optionType,\n _option\n );\n }\n\n /**\n * @dev Adds a DVN option to the existing options.\n * @param _options The existing options container.\n * @param _dvnIdx The DVN index for the DVN option.\n * @param _optionType The type of the DVN option.\n * @param _option The encoded data for the DVN option.\n * @return options The updated options container.\n */\n function addDVNOption(\n bytes memory _options,\n uint8 _dvnIdx,\n uint8 _optionType,\n bytes memory _option\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return\n abi.encodePacked(\n _options,\n DVNOptions.WORKER_ID,\n _option.length.toUint16() + 2, // +2 for optionType and dvnIdx\n _dvnIdx,\n _optionType,\n _option\n );\n }\n\n /**\n * @dev Encodes legacy options of type 1.\n * @param _executionGas The gasLimit value passed to lzReceive().\n * @return legacyOptions The encoded legacy options.\n */\n function encodeLegacyOptionsType1(uint256 _executionGas) internal pure returns (bytes memory) {\n if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);\n return abi.encodePacked(TYPE_1, _executionGas);\n }\n\n /**\n * @dev Encodes legacy options of type 2.\n * @param _executionGas The gasLimit value passed to lzReceive().\n * @param _nativeForDst The amount of native air dropped to the receiver.\n * @param _receiver The _nativeForDst receiver address.\n * @return legacyOptions The encoded legacy options of type 2.\n */\n function encodeLegacyOptionsType2(\n uint256 _executionGas,\n uint256 _nativeForDst,\n bytes memory _receiver // @dev Use bytes instead of bytes32 in legacy type 2 for _receiver.\n ) internal pure returns (bytes memory) {\n if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);\n if (_nativeForDst > type(uint128).max) revert InvalidSize(type(uint128).max, _nativeForDst);\n if (_receiver.length > 32) revert InvalidSize(32, _receiver.length);\n return abi.encodePacked(TYPE_2, _executionGas, _nativeForDst, _receiver);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n// @dev Import the 'MessagingFee' and 'MessagingReceipt' so it's exposed to OApp implementers\n// solhint-disable-next-line no-unused-import\nimport { OAppSender, MessagingFee, MessagingReceipt } from \"./OAppSender.sol\";\n// @dev Import the 'Origin' so it's exposed to OApp implementers\n// solhint-disable-next-line no-unused-import\nimport { OAppReceiver, Origin } from \"./OAppReceiver.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OApp\n * @dev Abstract contract serving as the base for OApp implementation, combining OAppSender and OAppReceiver functionality.\n */\nabstract contract OApp is OAppSender, OAppReceiver {\n /**\n * @dev Constructor to initialize the OApp with the provided endpoint and owner.\n * @param _endpoint The address of the LOCAL LayerZero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(address _endpoint, address _delegate) OAppCore(_endpoint, _delegate) {}\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol implementation.\n * @return receiverVersion The version of the OAppReceiver.sol implementation.\n */\n function oAppVersion()\n public\n pure\n virtual\n override(OAppSender, OAppReceiver)\n returns (uint64 senderVersion, uint64 receiverVersion)\n {\n return (SENDER_VERSION, RECEIVER_VERSION);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IOAppCore, ILayerZeroEndpointV2 } from \"./interfaces/IOAppCore.sol\";\n\n/**\n * @title OAppCore\n * @dev Abstract contract implementing the IOAppCore interface with basic OApp configurations.\n */\nabstract contract OAppCore is IOAppCore, Ownable {\n // The LayerZero endpoint associated with the given OApp\n ILayerZeroEndpointV2 public immutable endpoint;\n\n // Mapping to store peers associated with corresponding endpoints\n mapping(uint32 eid => bytes32 peer) public peers;\n\n /**\n * @dev Constructor to initialize the OAppCore with the provided endpoint and delegate.\n * @param _endpoint The address of the LOCAL Layer Zero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n *\n * @dev The delegate typically should be set as the owner of the contract.\n */\n constructor(address _endpoint, address _delegate) {\n endpoint = ILayerZeroEndpointV2(_endpoint);\n\n if (_delegate == address(0)) revert InvalidDelegate();\n endpoint.setDelegate(_delegate);\n }\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.\n * @dev Set this to bytes32(0) to remove the peer address.\n * @dev Peer is a bytes32 to accommodate non-evm chains.\n */\n function setPeer(uint32 _eid, bytes32 _peer) public virtual onlyOwner {\n _setPeer(_eid, _peer);\n }\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n *\n * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.\n * @dev Set this to bytes32(0) to remove the peer address.\n * @dev Peer is a bytes32 to accommodate non-evm chains.\n */\n function _setPeer(uint32 _eid, bytes32 _peer) internal virtual {\n peers[_eid] = _peer;\n emit PeerSet(_eid, _peer);\n }\n\n /**\n * @notice Internal function to get the peer address associated with a specific endpoint; reverts if NOT set.\n * ie. the peer is set to bytes32(0).\n * @param _eid The endpoint ID.\n * @return peer The address of the peer associated with the specified endpoint.\n */\n function _getPeerOrRevert(uint32 _eid) internal view virtual returns (bytes32) {\n bytes32 peer = peers[_eid];\n if (peer == bytes32(0)) revert NoPeer(_eid);\n return peer;\n }\n\n /**\n * @notice Sets the delegate address for the OApp.\n * @param _delegate The address of the delegate to be set.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Provides the ability for a delegate to set configs, on behalf of the OApp, directly on the Endpoint contract.\n */\n function setDelegate(address _delegate) public onlyOwner {\n endpoint.setDelegate(_delegate);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IOAppReceiver, Origin } from \"./interfaces/IOAppReceiver.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OAppReceiver\n * @dev Abstract contract implementing the ILayerZeroReceiver interface and extending OAppCore for OApp receivers.\n */\nabstract contract OAppReceiver is IOAppReceiver, OAppCore {\n // Custom error message for when the caller is not the registered endpoint/\n error OnlyEndpoint(address addr);\n\n // @dev The version of the OAppReceiver implementation.\n // @dev Version is bumped when changes are made to this contract.\n uint64 internal constant RECEIVER_VERSION = 2;\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n *\n * @dev Providing 0 as the default for OAppSender version. Indicates that the OAppSender is not implemented.\n * ie. this is a RECEIVE only OApp.\n * @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions.\n */\n function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {\n return (0, RECEIVER_VERSION);\n }\n\n /**\n * @notice Indicates whether an address is an approved composeMsg sender to the Endpoint.\n * @dev _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @dev _message The lzReceive payload.\n * @param _sender The sender address.\n * @return isSender Is a valid sender.\n *\n * @dev Applications can optionally choose to implement separate composeMsg senders that are NOT the bridging layer.\n * @dev The default sender IS the OAppReceiver implementer.\n */\n function isComposeMsgSender(\n Origin calldata /*_origin*/,\n bytes calldata /*_message*/,\n address _sender\n ) public view virtual returns (bool) {\n return _sender == address(this);\n }\n\n /**\n * @notice Checks if the path initialization is allowed based on the provided origin.\n * @param origin The origin information containing the source endpoint and sender address.\n * @return Whether the path has been initialized.\n *\n * @dev This indicates to the endpoint that the OApp has enabled msgs for this particular path to be received.\n * @dev This defaults to assuming if a peer has been set, its initialized.\n * Can be overridden by the OApp if there is other logic to determine this.\n */\n function allowInitializePath(Origin calldata origin) public view virtual returns (bool) {\n return peers[origin.srcEid] == origin.sender;\n }\n\n /**\n * @notice Retrieves the next nonce for a given source endpoint and sender address.\n * @dev _srcEid The source endpoint ID.\n * @dev _sender The sender address.\n * @return nonce The next nonce.\n *\n * @dev The path nonce starts from 1. If 0 is returned it means that there is NO nonce ordered enforcement.\n * @dev Is required by the off-chain executor to determine the OApp expects msg execution is ordered.\n * @dev This is also enforced by the OApp.\n * @dev By default this is NOT enabled. ie. nextNonce is hardcoded to return 0.\n */\n function nextNonce(uint32 /*_srcEid*/, bytes32 /*_sender*/) public view virtual returns (uint64 nonce) {\n return 0;\n }\n\n /**\n * @dev Entry point for receiving messages or packets from the endpoint.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The payload of the received message.\n * @param _executor The address of the executor for the received message.\n * @param _extraData Additional arbitrary data provided by the corresponding executor.\n *\n * @dev Entry point for receiving msg/packet from the LayerZero endpoint.\n */\n function lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) public payable virtual {\n // Ensures that only the endpoint can attempt to lzReceive() messages to this OApp.\n if (address(endpoint) != msg.sender) revert OnlyEndpoint(msg.sender);\n\n // Ensure that the sender matches the expected peer for the source endpoint.\n if (_getPeerOrRevert(_origin.srcEid) != _origin.sender) revert OnlyPeer(_origin.srcEid, _origin.sender);\n\n // Call the internal OApp implementation of lzReceive.\n _lzReceive(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Internal function to implement lzReceive logic without needing to copy the basic parameter validation.\n */\n function _lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual;\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { SafeERC20, IERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { MessagingParams, MessagingFee, MessagingReceipt } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OAppSender\n * @dev Abstract contract implementing the OAppSender functionality for sending messages to a LayerZero endpoint.\n */\nabstract contract OAppSender is OAppCore {\n using SafeERC20 for IERC20;\n\n // Custom error messages\n error NotEnoughNative(uint256 msgValue);\n error LzTokenUnavailable();\n\n // @dev The version of the OAppSender implementation.\n // @dev Version is bumped when changes are made to this contract.\n uint64 internal constant SENDER_VERSION = 1;\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n *\n * @dev Providing 0 as the default for OAppReceiver version. Indicates that the OAppReceiver is not implemented.\n * ie. this is a SEND only OApp.\n * @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions\n */\n function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {\n return (SENDER_VERSION, 0);\n }\n\n /**\n * @dev Internal function to interact with the LayerZero EndpointV2.quote() for fee calculation.\n * @param _dstEid The destination endpoint ID.\n * @param _message The message payload.\n * @param _options Additional options for the message.\n * @param _payInLzToken Flag indicating whether to pay the fee in LZ tokens.\n * @return fee The calculated MessagingFee for the message.\n * - nativeFee: The native fee for the message.\n * - lzTokenFee: The LZ token fee for the message.\n */\n function _quote(\n uint32 _dstEid,\n bytes memory _message,\n bytes memory _options,\n bool _payInLzToken\n ) internal view virtual returns (MessagingFee memory fee) {\n return\n endpoint.quote(\n MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _payInLzToken),\n address(this)\n );\n }\n\n /**\n * @dev Internal function to interact with the LayerZero EndpointV2.send() for sending a message.\n * @param _dstEid The destination endpoint ID.\n * @param _message The message payload.\n * @param _options Additional options for the message.\n * @param _fee The calculated LayerZero fee for the message.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess fee values sent to the endpoint.\n * @return receipt The receipt for the sent message.\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function _lzSend(\n uint32 _dstEid,\n bytes memory _message,\n bytes memory _options,\n MessagingFee memory _fee,\n address _refundAddress\n ) internal virtual returns (MessagingReceipt memory receipt) {\n // @dev Push corresponding fees to the endpoint, any excess is sent back to the _refundAddress from the endpoint.\n uint256 messageValue = _payNative(_fee.nativeFee);\n if (_fee.lzTokenFee > 0) _payLzToken(_fee.lzTokenFee);\n\n return\n // solhint-disable-next-line check-send-result\n endpoint.send{ value: messageValue }(\n MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _fee.lzTokenFee > 0),\n _refundAddress\n );\n }\n\n /**\n * @dev Internal function to pay the native fee associated with the message.\n * @param _nativeFee The native fee to be paid.\n * @return nativeFee The amount of native currency paid.\n *\n * @dev If the OApp needs to initiate MULTIPLE LayerZero messages in a single transaction,\n * this will need to be overridden because msg.value would contain multiple lzFees.\n * @dev Should be overridden in the event the LayerZero endpoint requires a different native currency.\n * @dev Some EVMs use an ERC20 as a method for paying transactions/gasFees.\n * @dev The endpoint is EITHER/OR, ie. it will NOT support both types of native payment at a time.\n */\n function _payNative(uint256 _nativeFee) internal virtual returns (uint256 nativeFee) {\n if (msg.value != _nativeFee) revert NotEnoughNative(msg.value);\n return _nativeFee;\n }\n\n /**\n * @dev Internal function to pay the LZ token fee associated with the message.\n * @param _lzTokenFee The LZ token fee to be paid.\n *\n * @dev If the caller is trying to pay in the specified lzToken, then the lzTokenFee is passed to the endpoint.\n * @dev Any excess sent, is passed back to the specified _refundAddress in the _lzSend().\n */\n function _payLzToken(uint256 _lzTokenFee) internal virtual {\n // @dev Cannot cache the token because it is not immutable in the endpoint.\n address lzToken = endpoint.lzToken();\n if (lzToken == address(0)) revert LzTokenUnavailable();\n\n // Pay LZ token fee by sending tokens to the endpoint.\n IERC20(lzToken).safeTransferFrom(msg.sender, address(endpoint), _lzTokenFee);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/interfaces/IOAppPreCrimeSimulator.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n// @dev Import the Origin so it's exposed to OAppPreCrimeSimulator implementers.\n// solhint-disable-next-line no-unused-import\nimport { InboundPacket, Origin } from \"../libs/Packet.sol\";\n\n/**\n * @title IOAppPreCrimeSimulator Interface\n * @dev Interface for the preCrime simulation functionality in an OApp.\n */\ninterface IOAppPreCrimeSimulator {\n // @dev simulation result used in PreCrime implementation\n error SimulationResult(bytes result);\n error OnlySelf();\n\n /**\n * @dev Emitted when the preCrime contract address is set.\n * @param preCrimeAddress The address of the preCrime contract.\n */\n event PreCrimeSet(address preCrimeAddress);\n\n /**\n * @dev Retrieves the address of the preCrime contract implementation.\n * @return The address of the preCrime contract.\n */\n function preCrime() external view returns (address);\n\n /**\n * @dev Retrieves the address of the OApp contract.\n * @return The address of the OApp contract.\n */\n function oApp() external view returns (address);\n\n /**\n * @dev Sets the preCrime contract address.\n * @param _preCrime The address of the preCrime contract.\n */\n function setPreCrime(address _preCrime) external;\n\n /**\n * @dev Mocks receiving a packet, then reverts with a series of data to infer the state/result.\n * @param _packets An array of LayerZero InboundPacket objects representing received packets.\n */\n function lzReceiveAndRevert(InboundPacket[] calldata _packets) external payable;\n\n /**\n * @dev checks if the specified peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint Id to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n */\n function isPeer(uint32 _eid, bytes32 _peer) external view returns (bool);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/interfaces/IPreCrime.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\nstruct PreCrimePeer {\n uint32 eid;\n bytes32 preCrime;\n bytes32 oApp;\n}\n\n// TODO not done yet\ninterface IPreCrime {\n error OnlyOffChain();\n\n // for simulate()\n error PacketOversize(uint256 max, uint256 actual);\n error PacketUnsorted();\n error SimulationFailed(bytes reason);\n\n // for preCrime()\n error SimulationResultNotFound(uint32 eid);\n error InvalidSimulationResult(uint32 eid, bytes reason);\n error CrimeFound(bytes crime);\n\n function getConfig(bytes[] calldata _packets, uint256[] calldata _packetMsgValues) external returns (bytes memory);\n\n function simulate(\n bytes[] calldata _packets,\n uint256[] calldata _packetMsgValues\n ) external payable returns (bytes memory);\n\n function buildSimulationResult() external view returns (bytes memory);\n\n function preCrime(\n bytes[] calldata _packets,\n uint256[] calldata _packetMsgValues,\n bytes[] calldata _simulations\n ) external;\n\n function version() external view returns (uint64 major, uint8 minor);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/libs/Packet.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Origin } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\nimport { PacketV1Codec } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol\";\n\n/**\n * @title InboundPacket\n * @dev Structure representing an inbound packet received by the contract.\n */\nstruct InboundPacket {\n Origin origin; // Origin information of the packet.\n uint32 dstEid; // Destination endpointId of the packet.\n address receiver; // Receiver address for the packet.\n bytes32 guid; // Unique identifier of the packet.\n uint256 value; // msg.value of the packet.\n address executor; // Executor address for the packet.\n bytes message; // Message payload of the packet.\n bytes extraData; // Additional arbitrary data for the packet.\n}\n\n/**\n * @title PacketDecoder\n * @dev Library for decoding LayerZero packets.\n */\nlibrary PacketDecoder {\n using PacketV1Codec for bytes;\n\n /**\n * @dev Decode an inbound packet from the given packet data.\n * @param _packet The packet data to decode.\n * @return packet An InboundPacket struct representing the decoded packet.\n */\n function decode(bytes calldata _packet) internal pure returns (InboundPacket memory packet) {\n packet.origin = Origin(_packet.srcEid(), _packet.sender(), _packet.nonce());\n packet.dstEid = _packet.dstEid();\n packet.receiver = _packet.receiverB20();\n packet.guid = _packet.guid();\n packet.message = _packet.message();\n }\n\n /**\n * @dev Decode multiple inbound packets from the given packet data and associated message values.\n * @param _packets An array of packet data to decode.\n * @param _packetMsgValues An array of associated message values for each packet.\n * @return packets An array of InboundPacket structs representing the decoded packets.\n */\n function decode(\n bytes[] calldata _packets,\n uint256[] memory _packetMsgValues\n ) internal pure returns (InboundPacket[] memory packets) {\n packets = new InboundPacket[](_packets.length);\n for (uint256 i = 0; i < _packets.length; i++) {\n bytes calldata packet = _packets[i];\n packets[i] = PacketDecoder.decode(packet);\n // @dev Allows the verifier to specify the msg.value that gets passed in lzReceive.\n packets[i].value = _packetMsgValues[i];\n }\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/OAppPreCrimeSimulator.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IPreCrime } from \"./interfaces/IPreCrime.sol\";\nimport { IOAppPreCrimeSimulator, InboundPacket, Origin } from \"./interfaces/IOAppPreCrimeSimulator.sol\";\n\n/**\n * @title OAppPreCrimeSimulator\n * @dev Abstract contract serving as the base for preCrime simulation functionality in an OApp.\n */\nabstract contract OAppPreCrimeSimulator is IOAppPreCrimeSimulator, Ownable {\n // The address of the preCrime implementation.\n address public preCrime;\n\n /**\n * @dev Retrieves the address of the OApp contract.\n * @return The address of the OApp contract.\n *\n * @dev The simulator contract is the base contract for the OApp by default.\n * @dev If the simulator is a separate contract, override this function.\n */\n function oApp() external view virtual returns (address) {\n return address(this);\n }\n\n /**\n * @dev Sets the preCrime contract address.\n * @param _preCrime The address of the preCrime contract.\n */\n function setPreCrime(address _preCrime) public virtual onlyOwner {\n preCrime = _preCrime;\n emit PreCrimeSet(_preCrime);\n }\n\n /**\n * @dev Interface for pre-crime simulations. Always reverts at the end with the simulation results.\n * @param _packets An array of InboundPacket objects representing received packets to be delivered.\n *\n * @dev WARNING: MUST revert at the end with the simulation results.\n * @dev Gives the preCrime implementation the ability to mock sending packets to the lzReceive function,\n * WITHOUT actually executing them.\n */\n function lzReceiveAndRevert(InboundPacket[] calldata _packets) public payable virtual {\n for (uint256 i = 0; i < _packets.length; i++) {\n InboundPacket calldata packet = _packets[i];\n\n // Ignore packets that are not from trusted peers.\n if (!isPeer(packet.origin.srcEid, packet.origin.sender)) continue;\n\n // @dev Because a verifier is calling this function, it doesnt have access to executor params:\n // - address _executor\n // - bytes calldata _extraData\n // preCrime will NOT work for OApps that rely on these two parameters inside of their _lzReceive().\n // They are instead stubbed to default values, address(0) and bytes(\"\")\n // @dev Calling this.lzReceiveSimulate removes ability for assembly return 0 callstack exit,\n // which would cause the revert to be ignored.\n this.lzReceiveSimulate{ value: packet.value }(\n packet.origin,\n packet.guid,\n packet.message,\n packet.executor,\n packet.extraData\n );\n }\n\n // @dev Revert with the simulation results. msg.sender must implement IPreCrime.buildSimulationResult().\n revert SimulationResult(IPreCrime(msg.sender).buildSimulationResult());\n }\n\n /**\n * @dev Is effectively an internal function because msg.sender must be address(this).\n * Allows resetting the call stack for 'internal' calls.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _guid The unique identifier of the packet.\n * @param _message The message payload of the packet.\n * @param _executor The executor address for the packet.\n * @param _extraData Additional data for the packet.\n */\n function lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) external payable virtual {\n // @dev Ensure ONLY can be called 'internally'.\n if (msg.sender != address(this)) revert OnlySelf();\n _lzReceiveSimulate(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Internal function to handle the OAppPreCrimeSimulator simulated receive.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The GUID of the LayerZero message.\n * @param _message The LayerZero message.\n * @param _executor The address of the off-chain executor.\n * @param _extraData Arbitrary data passed by the msg executor.\n *\n * @dev Enables the preCrime simulator to mock sending lzReceive() messages,\n * routes the msg down from the OAppPreCrimeSimulator, and back up to the OAppReceiver.\n */\n function _lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual;\n\n /**\n * @dev checks if the specified peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint Id to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n */\n function isPeer(uint32 _eid, bytes32 _peer) public view virtual returns (bool);\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/interfaces/IMintableBurnable.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\n/// @title Interface for mintable and burnable tokens\ninterface IMintableBurnable {\n \n /**\n * @notice Burns tokens from a specified account\n * @param _from Address from which tokens will be burned\n * @param _amount Amount of tokens to be burned\n * @return success Indicates whether the operation was successful\n */\n function burn(address _from, uint256 _amount) external returns (bool success);\n\n /**\n * @notice Mints tokens to a specified account\n * @param _to Address to which tokens will be minted\n * @param _amount Amount of tokens to be minted\n * @return success Indicates whether the operation was successful\n */\n function mint(address _to, uint256 _amount) external returns (bool success);\n}" + }, + "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { MessagingReceipt, MessagingFee } from \"@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol\";\n\n/**\n * @dev Struct representing token parameters for the OFT send() operation.\n */\nstruct SendParam {\n uint32 dstEid; // Destination endpoint ID.\n bytes32 to; // Recipient address.\n uint256 amountLD; // Amount to send in local decimals.\n uint256 minAmountLD; // Minimum amount to send in local decimals.\n bytes extraOptions; // Additional options supplied by the caller to be used in the LayerZero message.\n bytes composeMsg; // The composed message for the send() operation.\n bytes oftCmd; // The OFT command to be executed, unused in default OFT implementations.\n}\n\n/**\n * @dev Struct representing OFT limit information.\n * @dev These amounts can change dynamically and are up the specific oft implementation.\n */\nstruct OFTLimit {\n uint256 minAmountLD; // Minimum amount in local decimals that can be sent to the recipient.\n uint256 maxAmountLD; // Maximum amount in local decimals that can be sent to the recipient.\n}\n\n/**\n * @dev Struct representing OFT receipt information.\n */\nstruct OFTReceipt {\n uint256 amountSentLD; // Amount of tokens ACTUALLY debited from the sender in local decimals.\n // @dev In non-default implementations, the amountReceivedLD COULD differ from this value.\n uint256 amountReceivedLD; // Amount of tokens to be received on the remote side.\n}\n\n/**\n * @dev Struct representing OFT fee details.\n * @dev Future proof mechanism to provide a standardized way to communicate fees to things like a UI.\n */\nstruct OFTFeeDetail {\n int256 feeAmountLD; // Amount of the fee in local decimals.\n string description; // Description of the fee.\n}\n\n/**\n * @title IOFT\n * @dev Interface for the OftChain (OFT) token.\n * @dev Does not inherit ERC20 to accommodate usage by OFTAdapter as well.\n * @dev This specific interface ID is '0x02e49c2c'.\n */\ninterface IOFT {\n // Custom error messages\n error InvalidLocalDecimals();\n error SlippageExceeded(uint256 amountLD, uint256 minAmountLD);\n\n // Events\n event OFTSent(\n bytes32 indexed guid, // GUID of the OFT message.\n uint32 dstEid, // Destination Endpoint ID.\n address indexed fromAddress, // Address of the sender on the src chain.\n uint256 amountSentLD, // Amount of tokens sent in local decimals.\n uint256 amountReceivedLD // Amount of tokens received in local decimals.\n );\n event OFTReceived(\n bytes32 indexed guid, // GUID of the OFT message.\n uint32 srcEid, // Source Endpoint ID.\n address indexed toAddress, // Address of the recipient on the dst chain.\n uint256 amountReceivedLD // Amount of tokens received in local decimals.\n );\n\n /**\n * @notice Retrieves interfaceID and the version of the OFT.\n * @return interfaceId The interface ID.\n * @return version The version.\n *\n * @dev interfaceId: This specific interface ID is '0x02e49c2c'.\n * @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.\n * @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.\n * ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)\n */\n function oftVersion() external view returns (bytes4 interfaceId, uint64 version);\n\n /**\n * @notice Retrieves the address of the token associated with the OFT.\n * @return token The address of the ERC20 token implementation.\n */\n function token() external view returns (address);\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the 'token()' to send.\n * @return requiresApproval Needs approval of the underlying token implementation.\n *\n * @dev Allows things like wallet implementers to determine integration requirements,\n * without understanding the underlying token implementation.\n */\n function approvalRequired() external view returns (bool);\n\n /**\n * @notice Retrieves the shared decimals of the OFT.\n * @return sharedDecimals The shared decimals of the OFT.\n */\n function sharedDecimals() external view returns (uint8);\n\n /**\n * @notice Provides the fee breakdown and settings data for an OFT. Unused in the default implementation.\n * @param _sendParam The parameters for the send operation.\n * @return limit The OFT limit information.\n * @return oftFeeDetails The details of OFT fees.\n * @return receipt The OFT receipt information.\n */\n function quoteOFT(\n SendParam calldata _sendParam\n ) external view returns (OFTLimit memory, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory);\n\n /**\n * @notice Provides a quote for the send() operation.\n * @param _sendParam The parameters for the send() operation.\n * @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.\n * @return fee The calculated LayerZero messaging fee from the send() operation.\n *\n * @dev MessagingFee: LayerZero msg fee\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n */\n function quoteSend(SendParam calldata _sendParam, bool _payInLzToken) external view returns (MessagingFee memory);\n\n /**\n * @notice Executes the send() operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The fee information supplied by the caller.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds from fees etc. on the src.\n * @return receipt The LayerZero messaging receipt from the send() operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) external payable returns (MessagingReceipt memory, OFTReceipt memory);\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/libs/OFTComposeMsgCodec.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nlibrary OFTComposeMsgCodec {\n // Offset constants for decoding composed messages\n uint8 private constant NONCE_OFFSET = 8;\n uint8 private constant SRC_EID_OFFSET = 12;\n uint8 private constant AMOUNT_LD_OFFSET = 44;\n uint8 private constant COMPOSE_FROM_OFFSET = 76;\n\n /**\n * @dev Encodes a OFT composed message.\n * @param _nonce The nonce value.\n * @param _srcEid The source endpoint ID.\n * @param _amountLD The amount in local decimals.\n * @param _composeMsg The composed message.\n * @return _msg The encoded Composed message.\n */\n function encode(\n uint64 _nonce,\n uint32 _srcEid,\n uint256 _amountLD,\n bytes memory _composeMsg // 0x[composeFrom][composeMsg]\n ) internal pure returns (bytes memory _msg) {\n _msg = abi.encodePacked(_nonce, _srcEid, _amountLD, _composeMsg);\n }\n\n /**\n * @dev Retrieves the nonce for the composed message.\n * @param _msg The message.\n * @return The nonce value.\n */\n function nonce(bytes calldata _msg) internal pure returns (uint64) {\n return uint64(bytes8(_msg[:NONCE_OFFSET]));\n }\n\n /**\n * @dev Retrieves the source endpoint ID for the composed message.\n * @param _msg The message.\n * @return The source endpoint ID.\n */\n function srcEid(bytes calldata _msg) internal pure returns (uint32) {\n return uint32(bytes4(_msg[NONCE_OFFSET:SRC_EID_OFFSET]));\n }\n\n /**\n * @dev Retrieves the amount in local decimals from the composed message.\n * @param _msg The message.\n * @return The amount in local decimals.\n */\n function amountLD(bytes calldata _msg) internal pure returns (uint256) {\n return uint256(bytes32(_msg[SRC_EID_OFFSET:AMOUNT_LD_OFFSET]));\n }\n\n /**\n * @dev Retrieves the composeFrom value from the composed message.\n * @param _msg The message.\n * @return The composeFrom value.\n */\n function composeFrom(bytes calldata _msg) internal pure returns (bytes32) {\n return bytes32(_msg[AMOUNT_LD_OFFSET:COMPOSE_FROM_OFFSET]);\n }\n\n /**\n * @dev Retrieves the composed message.\n * @param _msg The message.\n * @return The composed message.\n */\n function composeMsg(bytes calldata _msg) internal pure returns (bytes memory) {\n return _msg[COMPOSE_FROM_OFFSET:];\n }\n\n /**\n * @dev Converts an address to bytes32.\n * @param _addr The address to convert.\n * @return The bytes32 representation of the address.\n */\n function addressToBytes32(address _addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(_addr)));\n }\n\n /**\n * @dev Converts bytes32 to an address.\n * @param _b The bytes32 value to convert.\n * @return The address representation of bytes32.\n */\n function bytes32ToAddress(bytes32 _b) internal pure returns (address) {\n return address(uint160(uint256(_b)));\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/libs/OFTMsgCodec.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nlibrary OFTMsgCodec {\n // Offset constants for encoding and decoding OFT messages\n uint8 private constant SEND_TO_OFFSET = 32;\n uint8 private constant SEND_AMOUNT_SD_OFFSET = 40;\n\n /**\n * @dev Encodes an OFT LayerZero message.\n * @param _sendTo The recipient address.\n * @param _amountShared The amount in shared decimals.\n * @param _composeMsg The composed message.\n * @return _msg The encoded message.\n * @return hasCompose A boolean indicating whether the message has a composed payload.\n */\n function encode(\n bytes32 _sendTo,\n uint64 _amountShared,\n bytes memory _composeMsg\n ) internal view returns (bytes memory _msg, bool hasCompose) {\n hasCompose = _composeMsg.length > 0;\n // @dev Remote chains will want to know the composed function caller ie. msg.sender on the src.\n _msg = hasCompose\n ? abi.encodePacked(_sendTo, _amountShared, addressToBytes32(msg.sender), _composeMsg)\n : abi.encodePacked(_sendTo, _amountShared);\n }\n\n /**\n * @dev Checks if the OFT message is composed.\n * @param _msg The OFT message.\n * @return A boolean indicating whether the message is composed.\n */\n function isComposed(bytes calldata _msg) internal pure returns (bool) {\n return _msg.length > SEND_AMOUNT_SD_OFFSET;\n }\n\n /**\n * @dev Retrieves the recipient address from the OFT message.\n * @param _msg The OFT message.\n * @return The recipient address.\n */\n function sendTo(bytes calldata _msg) internal pure returns (bytes32) {\n return bytes32(_msg[:SEND_TO_OFFSET]);\n }\n\n /**\n * @dev Retrieves the amount in shared decimals from the OFT message.\n * @param _msg The OFT message.\n * @return The amount in shared decimals.\n */\n function amountSD(bytes calldata _msg) internal pure returns (uint64) {\n return uint64(bytes8(_msg[SEND_TO_OFFSET:SEND_AMOUNT_SD_OFFSET]));\n }\n\n /**\n * @dev Retrieves the composed message from the OFT message.\n * @param _msg The OFT message.\n * @return The composed message.\n */\n function composeMsg(bytes calldata _msg) internal pure returns (bytes memory) {\n return _msg[SEND_AMOUNT_SD_OFFSET:];\n }\n\n /**\n * @dev Converts an address to bytes32.\n * @param _addr The address to convert.\n * @return The bytes32 representation of the address.\n */\n function addressToBytes32(address _addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(_addr)));\n }\n\n /**\n * @dev Converts bytes32 to an address.\n * @param _b The bytes32 value to convert.\n * @return The address representation of bytes32.\n */\n function bytes32ToAddress(bytes32 _b) internal pure returns (address) {\n return address(uint160(uint256(_b)));\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/MintBurnOFTAdapter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\n// External imports\nimport { IERC20, IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\n// Local imports\nimport { IMintableBurnable } from \"./interfaces/IMintableBurnable.sol\";\nimport { OFTCore } from \"./OFTCore.sol\";\n\n/**\n * @title MintBurnOFTAdapter\n * @notice A variant of the standard OFT Adapter that uses an existing ERC20's mint and burn mechanisms for cross-chain transfers.\n *\n * @dev Inherits from OFTCore and provides implementations for _debit and _credit functions using a mintable and burnable token.\n */\nabstract contract MintBurnOFTAdapter is OFTCore {\n /// @dev The underlying ERC20 token.\n IERC20 internal immutable innerToken;\n\n /// @notice The contract responsible for minting and burning tokens.\n IMintableBurnable public immutable minterBurner;\n\n /**\n * @notice Initializes the MintBurnOFTAdapter contract.\n *\n * @param _token The address of the underlying ERC20 token.\n * @param _minterBurner The contract responsible for minting and burning tokens.\n * @param _lzEndpoint The LayerZero endpoint address.\n * @param _delegate The address of the delegate.\n *\n * @dev Calls the OFTCore constructor with the token's decimals, the endpoint, and the delegate.\n */\n constructor(\n address _token,\n IMintableBurnable _minterBurner,\n address _lzEndpoint,\n address _delegate\n ) OFTCore(IERC20Metadata(_token).decimals(), _lzEndpoint, _delegate) {\n innerToken = IERC20(_token);\n minterBurner = _minterBurner;\n }\n\n /**\n * @notice Retrieves the address of the underlying ERC20 token.\n *\n * @return The address of the adapted ERC20 token.\n *\n * @dev In the case of MintBurnOFTAdapter, address(this) and erc20 are NOT the same contract.\n */\n function token() public view returns (address) {\n return address(innerToken);\n }\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the underlying token to send.\n *\n * @return requiresApproval True if approval is required, false otherwise.\n *\n * @dev In this MintBurnOFTAdapter, approval is NOT required because it uses mint and burn privileges.\n */\n function approvalRequired() external pure virtual returns (bool) {\n return false;\n }\n\n /**\n * @notice Burns tokens from the sender's balance to prepare for sending.\n *\n * @param _from The address to debit the tokens from.\n * @param _amountLD The amount of tokens to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination chain ID.\n *\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, i.e., 1 token in, 1 token out.\n * If the 'innerToken' applies something like a transfer fee, the default will NOT work.\n * A pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual override returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n (amountSentLD, amountReceivedLD) = _debitView(_amountLD, _minAmountLD, _dstEid);\n // Burns tokens from the caller.\n minterBurner.burn(_from, amountSentLD);\n }\n\n /**\n * @notice Mints tokens to the specified address upon receiving them.\n *\n * @param _to The address to credit the tokens to.\n * @param _amountLD The amount of tokens to credit in local decimals.\n *\n * @return amountReceivedLD The amount of tokens actually received in local decimals.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, i.e., 1 token in, 1 token out.\n * If the 'innerToken' applies something like a transfer fee, the default will NOT work.\n * A pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /* _srcEid */\n ) internal virtual override returns (uint256 amountReceivedLD) {\n if (_to == address(0x0)) _to = address(0xdead); // _mint(...) does not support address(0x0)\n // Mints the tokens and transfers to the recipient.\n minterBurner.mint(_to, _amountLD);\n // In the case of NON-default OFTAdapter, the amountLD MIGHT not be equal to amountReceivedLD.\n return _amountLD;\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/OFTAdapter.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IERC20Metadata, IERC20 } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IOFT, OFTCore } from \"./OFTCore.sol\";\n\n/**\n * @title OFTAdapter Contract\n * @dev OFTAdapter is a contract that adapts an ERC-20 token to the OFT functionality.\n *\n * @dev For existing ERC20 tokens, this can be used to convert the token to crosschain compatibility.\n * @dev WARNING: ONLY 1 of these should exist for a given global mesh,\n * unless you make a NON-default implementation of OFT and needs to be done very carefully.\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountSentLD/amountReceivedLD.\n */\nabstract contract OFTAdapter is OFTCore {\n using SafeERC20 for IERC20;\n\n IERC20 internal immutable innerToken;\n\n /**\n * @dev Constructor for the OFTAdapter contract.\n * @param _token The address of the ERC-20 token to be adapted.\n * @param _lzEndpoint The LayerZero endpoint address.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(\n address _token,\n address _lzEndpoint,\n address _delegate\n ) OFTCore(IERC20Metadata(_token).decimals(), _lzEndpoint, _delegate) {\n innerToken = IERC20(_token);\n }\n\n /**\n * @dev Retrieves the address of the underlying ERC20 implementation.\n * @return The address of the adapted ERC-20 token.\n *\n * @dev In the case of OFTAdapter, address(this) and erc20 are NOT the same contract.\n */\n function token() public view returns (address) {\n return address(innerToken);\n }\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the 'token()' to send.\n * @return requiresApproval Needs approval of the underlying token implementation.\n *\n * @dev In the case of default OFTAdapter, approval is required.\n * @dev In non-default OFTAdapter contracts with something like mint and burn privileges, it would NOT need approval.\n */\n function approvalRequired() external pure virtual returns (bool) {\n return true;\n }\n\n /**\n * @dev Locks tokens from the sender's specified balance in this contract.\n * @param _from The address to debit from.\n * @param _amountLD The amount of tokens to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination chain ID.\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev msg.sender will need to approve this _amountLD of tokens to be locked inside of the contract.\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual override returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n (amountSentLD, amountReceivedLD) = _debitView(_amountLD, _minAmountLD, _dstEid);\n // @dev Lock tokens by moving them into this contract from the caller.\n innerToken.safeTransferFrom(_from, address(this), amountSentLD);\n }\n\n /**\n * @dev Credits tokens to the specified address.\n * @param _to The address to credit the tokens to.\n * @param _amountLD The amount of tokens to credit in local decimals.\n * @dev _srcEid The source chain ID.\n * @return amountReceivedLD The amount of tokens ACTUALLY received in local decimals.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /*_srcEid*/\n ) internal virtual override returns (uint256 amountReceivedLD) {\n // @dev Unlock the tokens and transfer to the recipient.\n innerToken.safeTransfer(_to, _amountLD);\n // @dev In the case of NON-default OFTAdapter, the amountLD MIGHT not be == amountReceivedLD.\n return _amountLD;\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/OFTCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { OApp, Origin } from \"@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol\";\nimport { OAppOptionsType3 } from \"@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol\";\nimport { IOAppMsgInspector } from \"@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppMsgInspector.sol\";\n\nimport { OAppPreCrimeSimulator } from \"@layerzerolabs/oapp-evm/contracts/precrime/OAppPreCrimeSimulator.sol\";\n\nimport { IOFT, SendParam, OFTLimit, OFTReceipt, OFTFeeDetail, MessagingReceipt, MessagingFee } from \"./interfaces/IOFT.sol\";\nimport { OFTMsgCodec } from \"./libs/OFTMsgCodec.sol\";\nimport { OFTComposeMsgCodec } from \"./libs/OFTComposeMsgCodec.sol\";\n\n/**\n * @title OFTCore\n * @dev Abstract contract for the OftChain (OFT) token.\n */\nabstract contract OFTCore is IOFT, OApp, OAppPreCrimeSimulator, OAppOptionsType3 {\n using OFTMsgCodec for bytes;\n using OFTMsgCodec for bytes32;\n\n // @notice Provides a conversion rate when swapping between denominations of SD and LD\n // - shareDecimals == SD == shared Decimals\n // - localDecimals == LD == local decimals\n // @dev Considers that tokens have different decimal amounts on various chains.\n // @dev eg.\n // For a token\n // - locally with 4 decimals --> 1.2345 => uint(12345)\n // - remotely with 2 decimals --> 1.23 => uint(123)\n // - The conversion rate would be 10 ** (4 - 2) = 100\n // @dev If you want to send 1.2345 -> (uint 12345), you CANNOT represent that value on the remote,\n // you can only display 1.23 -> uint(123).\n // @dev To preserve the dust that would otherwise be lost on that conversion,\n // we need to unify a denomination that can be represented on ALL chains inside of the OFT mesh\n uint256 public immutable decimalConversionRate;\n\n // @notice Msg types that are used to identify the various OFT operations.\n // @dev This can be extended in child contracts for non-default oft operations\n // @dev These values are used in things like combineOptions() in OAppOptionsType3.sol.\n uint16 public constant SEND = 1;\n uint16 public constant SEND_AND_CALL = 2;\n\n // Address of an optional contract to inspect both 'message' and 'options'\n address public msgInspector;\n event MsgInspectorSet(address inspector);\n\n /**\n * @dev Constructor.\n * @param _localDecimals The decimals of the token on the local chain (this chain).\n * @param _endpoint The address of the LayerZero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(uint8 _localDecimals, address _endpoint, address _delegate) OApp(_endpoint, _delegate) {\n if (_localDecimals < sharedDecimals()) revert InvalidLocalDecimals();\n decimalConversionRate = 10 ** (_localDecimals - sharedDecimals());\n }\n\n /**\n * @notice Retrieves interfaceID and the version of the OFT.\n * @return interfaceId The interface ID.\n * @return version The version.\n *\n * @dev interfaceId: This specific interface ID is '0x02e49c2c'.\n * @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.\n * @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.\n * ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)\n */\n function oftVersion() external pure virtual returns (bytes4 interfaceId, uint64 version) {\n return (type(IOFT).interfaceId, 1);\n }\n\n /**\n * @dev Retrieves the shared decimals of the OFT.\n * @return The shared decimals of the OFT.\n *\n * @dev Sets an implicit cap on the amount of tokens, over uint64.max() will need some sort of outbound cap / totalSupply cap\n * Lowest common decimal denominator between chains.\n * Defaults to 6 decimal places to provide up to 18,446,744,073,709.551615 units (max uint64).\n * For tokens exceeding this totalSupply(), they will need to override the sharedDecimals function with something smaller.\n * ie. 4 sharedDecimals would be 1,844,674,407,370,955.1615\n */\n function sharedDecimals() public view virtual returns (uint8) {\n return 6;\n }\n\n /**\n * @dev Sets the message inspector address for the OFT.\n * @param _msgInspector The address of the message inspector.\n *\n * @dev This is an optional contract that can be used to inspect both 'message' and 'options'.\n * @dev Set it to address(0) to disable it, or set it to a contract address to enable it.\n */\n function setMsgInspector(address _msgInspector) public virtual onlyOwner {\n msgInspector = _msgInspector;\n emit MsgInspectorSet(_msgInspector);\n }\n\n /**\n * @notice Provides the fee breakdown and settings data for an OFT. Unused in the default implementation.\n * @param _sendParam The parameters for the send operation.\n * @return oftLimit The OFT limit information.\n * @return oftFeeDetails The details of OFT fees.\n * @return oftReceipt The OFT receipt information.\n */\n function quoteOFT(\n SendParam calldata _sendParam\n )\n external\n view\n virtual\n returns (OFTLimit memory oftLimit, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory oftReceipt)\n {\n uint256 minAmountLD = 0; // Unused in the default implementation.\n uint256 maxAmountLD = IERC20(this.token()).totalSupply(); // Unused in the default implementation.\n oftLimit = OFTLimit(minAmountLD, maxAmountLD);\n\n // Unused in the default implementation; reserved for future complex fee details.\n oftFeeDetails = new OFTFeeDetail[](0);\n\n // @dev This is the same as the send() operation, but without the actual send.\n // - amountSentLD is the amount in local decimals that would be sent from the sender.\n // - amountReceivedLD is the amount in local decimals that will be credited to the recipient on the remote OFT instance.\n // @dev The amountSentLD MIGHT not equal the amount the user actually receives. HOWEVER, the default does.\n (uint256 amountSentLD, uint256 amountReceivedLD) = _debitView(\n _sendParam.amountLD,\n _sendParam.minAmountLD,\n _sendParam.dstEid\n );\n oftReceipt = OFTReceipt(amountSentLD, amountReceivedLD);\n }\n\n /**\n * @notice Provides a quote for the send() operation.\n * @param _sendParam The parameters for the send() operation.\n * @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.\n * @return msgFee The calculated LayerZero messaging fee from the send() operation.\n *\n * @dev MessagingFee: LayerZero msg fee\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n */\n function quoteSend(\n SendParam calldata _sendParam,\n bool _payInLzToken\n ) external view virtual returns (MessagingFee memory msgFee) {\n // @dev mock the amount to receive, this is the same operation used in the send().\n // The quote is as similar as possible to the actual send() operation.\n (, uint256 amountReceivedLD) = _debitView(_sendParam.amountLD, _sendParam.minAmountLD, _sendParam.dstEid);\n\n // @dev Builds the options and OFT message to quote in the endpoint.\n (bytes memory message, bytes memory options) = _buildMsgAndOptions(_sendParam, amountReceivedLD);\n\n // @dev Calculates the LayerZero fee for the send() operation.\n return _quote(_sendParam.dstEid, message, options, _payInLzToken);\n }\n\n /**\n * @dev Executes the send operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The calculated fee for the send() operation.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds.\n * @return msgReceipt The receipt for the send operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) external payable virtual returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) {\n return _send(_sendParam, _fee, _refundAddress);\n }\n\n /**\n * @dev Internal function to execute the send operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The calculated fee for the send() operation.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds.\n * @return msgReceipt The receipt for the send operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function _send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) internal virtual returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) {\n // @dev Applies the token transfers regarding this send() operation.\n // - amountSentLD is the amount in local decimals that was ACTUALLY sent/debited from the sender.\n // - amountReceivedLD is the amount in local decimals that will be received/credited to the recipient on the remote OFT instance.\n (uint256 amountSentLD, uint256 amountReceivedLD) = _debit(\n msg.sender,\n _sendParam.amountLD,\n _sendParam.minAmountLD,\n _sendParam.dstEid\n );\n\n // @dev Builds the options and OFT message to quote in the endpoint.\n (bytes memory message, bytes memory options) = _buildMsgAndOptions(_sendParam, amountReceivedLD);\n\n // @dev Sends the message to the LayerZero endpoint and returns the LayerZero msg receipt.\n msgReceipt = _lzSend(_sendParam.dstEid, message, options, _fee, _refundAddress);\n // @dev Formulate the OFT receipt.\n oftReceipt = OFTReceipt(amountSentLD, amountReceivedLD);\n\n emit OFTSent(msgReceipt.guid, _sendParam.dstEid, msg.sender, amountSentLD, amountReceivedLD);\n }\n\n /**\n * @dev Internal function to build the message and options.\n * @param _sendParam The parameters for the send() operation.\n * @param _amountLD The amount in local decimals.\n * @return message The encoded message.\n * @return options The encoded options.\n */\n function _buildMsgAndOptions(\n SendParam calldata _sendParam,\n uint256 _amountLD\n ) internal view virtual returns (bytes memory message, bytes memory options) {\n bool hasCompose;\n // @dev This generated message has the msg.sender encoded into the payload so the remote knows who the caller is.\n (message, hasCompose) = OFTMsgCodec.encode(\n _sendParam.to,\n _toSD(_amountLD),\n // @dev Must be include a non empty bytes if you want to compose, EVEN if you dont need it on the remote.\n // EVEN if you dont require an arbitrary payload to be sent... eg. '0x01'\n _sendParam.composeMsg\n );\n // @dev Change the msg type depending if its composed or not.\n uint16 msgType = hasCompose ? SEND_AND_CALL : SEND;\n // @dev Combine the callers _extraOptions with the enforced options via the OAppOptionsType3.\n options = combineOptions(_sendParam.dstEid, msgType, _sendParam.extraOptions);\n\n // @dev Optionally inspect the message and options depending if the OApp owner has set a msg inspector.\n // @dev If it fails inspection, needs to revert in the implementation. ie. does not rely on return boolean\n address inspector = msgInspector; // caches the msgInspector to avoid potential double storage read\n if (inspector != address(0)) IOAppMsgInspector(inspector).inspect(message, options);\n }\n\n /**\n * @dev Internal function to handle the receive on the LayerZero endpoint.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The encoded message.\n * @dev _executor The address of the executor.\n * @dev _extraData Additional data.\n */\n function _lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address /*_executor*/, // @dev unused in the default implementation.\n bytes calldata /*_extraData*/ // @dev unused in the default implementation.\n ) internal virtual override {\n // @dev The src sending chain doesnt know the address length on this chain (potentially non-evm)\n // Thus everything is bytes32() encoded in flight.\n address toAddress = _message.sendTo().bytes32ToAddress();\n // @dev Credit the amountLD to the recipient and return the ACTUAL amount the recipient received in local decimals\n uint256 amountReceivedLD = _credit(toAddress, _toLD(_message.amountSD()), _origin.srcEid);\n\n if (_message.isComposed()) {\n // @dev Proprietary composeMsg format for the OFT.\n bytes memory composeMsg = OFTComposeMsgCodec.encode(\n _origin.nonce,\n _origin.srcEid,\n amountReceivedLD,\n _message.composeMsg()\n );\n\n // @dev Stores the lzCompose payload that will be executed in a separate tx.\n // Standardizes functionality for executing arbitrary contract invocation on some non-evm chains.\n // @dev The off-chain executor will listen and process the msg based on the src-chain-callers compose options passed.\n // @dev The index is used when a OApp needs to compose multiple msgs on lzReceive.\n // For default OFT implementation there is only 1 compose msg per lzReceive, thus its always 0.\n endpoint.sendCompose(toAddress, _guid, 0 /* the index of the composed message*/, composeMsg);\n }\n\n emit OFTReceived(_guid, _origin.srcEid, toAddress, amountReceivedLD);\n }\n\n /**\n * @dev Internal function to handle the OAppPreCrimeSimulator simulated receive.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The LayerZero message.\n * @param _executor The address of the off-chain executor.\n * @param _extraData Arbitrary data passed by the msg executor.\n *\n * @dev Enables the preCrime simulator to mock sending lzReceive() messages,\n * routes the msg down from the OAppPreCrimeSimulator, and back up to the OAppReceiver.\n */\n function _lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual override {\n _lzReceive(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Check if the peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint ID to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n *\n * @dev Enables OAppPreCrimeSimulator to check whether a potential Inbound Packet is from a trusted source.\n */\n function isPeer(uint32 _eid, bytes32 _peer) public view virtual override returns (bool) {\n return peers[_eid] == _peer;\n }\n\n /**\n * @dev Internal function to remove dust from the given local decimal amount.\n * @param _amountLD The amount in local decimals.\n * @return amountLD The amount after removing dust.\n *\n * @dev Prevents the loss of dust when moving amounts between chains with different decimals.\n * @dev eg. uint(123) with a conversion rate of 100 becomes uint(100).\n */\n function _removeDust(uint256 _amountLD) internal view virtual returns (uint256 amountLD) {\n return (_amountLD / decimalConversionRate) * decimalConversionRate;\n }\n\n /**\n * @dev Internal function to convert an amount from shared decimals into local decimals.\n * @param _amountSD The amount in shared decimals.\n * @return amountLD The amount in local decimals.\n */\n function _toLD(uint64 _amountSD) internal view virtual returns (uint256 amountLD) {\n return _amountSD * decimalConversionRate;\n }\n\n /**\n * @dev Internal function to convert an amount from local decimals into shared decimals.\n * @param _amountLD The amount in local decimals.\n * @return amountSD The amount in shared decimals.\n */\n function _toSD(uint256 _amountLD) internal view virtual returns (uint64 amountSD) {\n return uint64(_amountLD / decimalConversionRate);\n }\n\n /**\n * @dev Internal function to mock the amount mutation from a OFT debit() operation.\n * @param _amountLD The amount to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @dev _dstEid The destination endpoint ID.\n * @return amountSentLD The amount sent, in local decimals.\n * @return amountReceivedLD The amount to be received on the remote chain, in local decimals.\n *\n * @dev This is where things like fees would be calculated and deducted from the amount to be received on the remote.\n */\n function _debitView(\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 /*_dstEid*/\n ) internal view virtual returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n // @dev Remove the dust so nothing is lost on the conversion between chains with different decimals for the token.\n amountSentLD = _removeDust(_amountLD);\n // @dev The amount to send is the same as amount received in the default implementation.\n amountReceivedLD = amountSentLD;\n\n // @dev Check for slippage.\n if (amountReceivedLD < _minAmountLD) {\n revert SlippageExceeded(amountReceivedLD, _minAmountLD);\n }\n }\n\n /**\n * @dev Internal function to perform a debit operation.\n * @param _from The address to debit.\n * @param _amountLD The amount to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination endpoint ID.\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev Defined here but are intended to be overriden depending on the OFT implementation.\n * @dev Depending on OFT implementation the _amountLD could differ from the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual returns (uint256 amountSentLD, uint256 amountReceivedLD);\n\n /**\n * @dev Internal function to perform a credit operation.\n * @param _to The address to credit.\n * @param _amountLD The amount to credit in local decimals.\n * @param _srcEid The source endpoint ID.\n * @return amountReceivedLD The amount ACTUALLY received in local decimals.\n *\n * @dev Defined here but are intended to be overriden depending on the OFT implementation.\n * @dev Depending on OFT implementation the _amountLD could differ from the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 _srcEid\n ) internal virtual returns (uint256 amountReceivedLD);\n}\n" + }, + "@openzeppelin/contracts/access/AccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/AccessControl.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\nimport \"../utils/Context.sol\";\nimport \"../utils/Strings.sol\";\nimport \"../utils/introspection/ERC165.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\n * members except through off-chain means by accessing the contract event logs. Some\n * applications may benefit from on-chain enumerability, for those cases see\n * {AccessControlEnumerable}.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```\n * function foo() public {\n * require(hasRole(MY_ROLE, msg.sender));\n * ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it.\n */\nabstract contract AccessControl is Context, IAccessControl, ERC165 {\n struct RoleData {\n mapping(address => bool) members;\n bytes32 adminRole;\n }\n\n mapping(bytes32 => RoleData) private _roles;\n\n bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n /**\n * @dev Modifier that checks that an account has a specific role. Reverts\n * with a standardized message including the required role.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n *\n * _Available since v4.1._\n */\n modifier onlyRole(bytes32 role) {\n _checkRole(role, _msgSender());\n _;\n }\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) public view override returns (bool) {\n return _roles[role].members[account];\n }\n\n /**\n * @dev Revert with a standard message if `account` is missing `role`.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n */\n function _checkRole(bytes32 role, address account) internal view {\n if (!hasRole(role, account)) {\n revert(\n string(\n abi.encodePacked(\n \"AccessControl: account \",\n Strings.toHexString(uint160(account), 20),\n \" is missing role \",\n Strings.toHexString(uint256(role), 32)\n )\n )\n );\n }\n }\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) public view override returns (bytes32) {\n return _roles[role].adminRole;\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _grantRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _revokeRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been revoked `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) public virtual override {\n require(account == _msgSender(), \"AccessControl: can only renounce roles for self\");\n\n _revokeRole(role, account);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event. Note that unlike {grantRole}, this function doesn't perform any\n * checks on the calling account.\n *\n * [WARNING]\n * ====\n * This function should only be called from the constructor when setting\n * up the initial roles for the system.\n *\n * Using this function in any other way is effectively circumventing the admin\n * system imposed by {AccessControl}.\n * ====\n *\n * NOTE: This function is deprecated in favor of {_grantRole}.\n */\n function _setupRole(bytes32 role, address account) internal virtual {\n _grantRole(role, account);\n }\n\n /**\n * @dev Sets `adminRole` as ``role``'s admin role.\n *\n * Emits a {RoleAdminChanged} event.\n */\n function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n bytes32 previousAdminRole = getRoleAdmin(role);\n _roles[role].adminRole = adminRole;\n emit RoleAdminChanged(role, previousAdminRole, adminRole);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * Internal function without access restriction.\n */\n function _grantRole(bytes32 role, address account) internal virtual {\n if (!hasRole(role, account)) {\n _roles[role].members[account] = true;\n emit RoleGranted(role, account, _msgSender());\n }\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * Internal function without access restriction.\n */\n function _revokeRole(bytes32 role, address account) internal virtual {\n if (hasRole(role, account)) {\n _roles[role].members[account] = false;\n emit RoleRevoked(role, account, _msgSender());\n }\n }\n}\n" + }, + "@openzeppelin/contracts/access/AccessControlEnumerable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/AccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControlEnumerable.sol\";\nimport \"./AccessControl.sol\";\nimport \"../utils/structs/EnumerableSet.sol\";\n\n/**\n * @dev Extension of {AccessControl} that allows enumerating the members of each role.\n */\nabstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {\n using EnumerableSet for EnumerableSet.AddressSet;\n\n mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) public view override returns (address) {\n return _roleMembers[role].at(index);\n }\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) public view override returns (uint256) {\n return _roleMembers[role].length();\n }\n\n /**\n * @dev Overload {_grantRole} to track enumerable memberships\n */\n function _grantRole(bytes32 role, address account) internal virtual override {\n super._grantRole(role, account);\n _roleMembers[role].add(account);\n }\n\n /**\n * @dev Overload {_revokeRole} to track enumerable memberships\n */\n function _revokeRole(bytes32 role, address account) internal virtual override {\n super._revokeRole(role, account);\n _roleMembers[role].remove(account);\n }\n}\n" + }, + "@openzeppelin/contracts/access/IAccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n /**\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n *\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n * {RoleAdminChanged} not being emitted signaling this.\n *\n * _Available since v3.1._\n */\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n /**\n * @dev Emitted when `account` is granted `role`.\n *\n * `sender` is the account that originated the contract call, an admin role\n * bearer except when using {AccessControl-_setupRole}.\n */\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Emitted when `account` is revoked `role`.\n *\n * `sender` is the account that originated the contract call:\n * - if using `revokeRole`, it is the admin role bearer\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\n */\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) external view returns (bool);\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been granted `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) external;\n}\n" + }, + "@openzeppelin/contracts/access/IAccessControlEnumerable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\n\n/**\n * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.\n */\ninterface IAccessControlEnumerable is IAccessControl {\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) external view returns (address);\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) external view returns (uint256);\n}\n" + }, + "@openzeppelin/contracts/access/Ownable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n" + }, + "@openzeppelin/contracts/security/Pausable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/ERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/ERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"./extensions/IERC20Metadata.sol\";\nimport \"../../utils/Context.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20, IERC20Metadata {\n mapping(address => uint256) private _balances;\n\n mapping(address => mapping(address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n\n /**\n * @dev Sets the values for {name} and {symbol}.\n *\n * The default value of {decimals} is 18. To select a different value for\n * {decimals} you should overload it.\n *\n * All two of these values are immutable: they can only be set once during\n * construction.\n */\n constructor(string memory name_, string memory symbol_) {\n _name = name_;\n _symbol = symbol_;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless this function is\n * overridden;\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `recipient` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n _approve(_msgSender(), spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * Requirements:\n *\n * - `sender` and `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n * - the caller must have allowance for ``sender``'s tokens of at least\n * `amount`.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public virtual override returns (bool) {\n _transfer(sender, recipient, amount);\n\n uint256 currentAllowance = _allowances[sender][_msgSender()];\n require(currentAllowance >= amount, \"ERC20: transfer amount exceeds allowance\");\n unchecked {\n _approve(sender, _msgSender(), currentAllowance - amount);\n }\n\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n uint256 currentAllowance = _allowances[_msgSender()][spender];\n require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n unchecked {\n _approve(_msgSender(), spender, currentAllowance - subtractedValue);\n }\n\n return true;\n }\n\n /**\n * @dev Moves `amount` of tokens from `sender` to `recipient`.\n *\n * This internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `sender` cannot be the zero address.\n * - `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n */\n function _transfer(\n address sender,\n address recipient,\n uint256 amount\n ) internal virtual {\n require(sender != address(0), \"ERC20: transfer from the zero address\");\n require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(sender, recipient, amount);\n\n uint256 senderBalance = _balances[sender];\n require(senderBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n unchecked {\n _balances[sender] = senderBalance - amount;\n }\n _balances[recipient] += amount;\n\n emit Transfer(sender, recipient, amount);\n\n _afterTokenTransfer(sender, recipient, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply += amount;\n _balances[account] += amount;\n emit Transfer(address(0), account, amount);\n\n _afterTokenTransfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n uint256 accountBalance = _balances[account];\n require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n unchecked {\n _balances[account] = accountBalance - amount;\n }\n _totalSupply -= amount;\n\n emit Transfer(account, address(0), amount);\n\n _afterTokenTransfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n\n /**\n * @dev Hook that is called after any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * has been transferred to `to`.\n * - when `from` is zero, `amount` tokens have been minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _afterTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC721 token receiver interface\n * @dev Interface for any contract that wants to support safeTransfers\n * from ERC721 asset contracts.\n */\ninterface IERC721Receiver {\n /**\n * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}\n * by `operator` from `from`, this function is called.\n *\n * It must return its Solidity selector to confirm the token transfer.\n * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.\n *\n * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.\n */\n function onERC721Received(\n address operator,\n address from,\n uint256 tokenId,\n bytes calldata data\n ) external returns (bytes4);\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/ERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC165.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n *\n * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.\n */\nabstract contract ERC165 is IERC165 {\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IERC165).interfaceId;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/IERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeMath.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)\n\npragma solidity ^0.8.0;\n\n// CAUTION\n// This version of SafeMath should only be used with Solidity 0.8 or later,\n// because it relies on the compiler's built in overflow checks.\n\n/**\n * @dev Wrappers over Solidity's arithmetic operations.\n *\n * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler\n * now has built in overflow checking.\n */\nlibrary SafeMath {\n /**\n * @dev Returns the addition of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n uint256 c = a + b;\n if (c < a) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the substraction of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b > a) return (false, 0);\n return (true, a - b);\n }\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n // benefit is lost if 'b' is also tested.\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n if (a == 0) return (true, 0);\n uint256 c = a * b;\n if (c / a != b) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the division of two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a / b);\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a % b);\n }\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `+` operator.\n *\n * Requirements:\n *\n * - Addition cannot overflow.\n */\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\n return a + b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting on\n * overflow (when the result is negative).\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\n return a - b;\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `*` operator.\n *\n * Requirements:\n *\n * - Multiplication cannot overflow.\n */\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\n return a * b;\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator.\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\n return a / b;\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting when dividing by zero.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\n return a % b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\n * overflow (when the result is negative).\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {trySub}.\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b <= a, errorMessage);\n return a - b;\n }\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting with custom message on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator. Note: this function uses a\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\n * uses an invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a / b;\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting with custom message when dividing by zero.\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {tryMod}.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a % b;\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Strings.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n bytes16 private constant _HEX_SYMBOLS = \"0123456789abcdef\";\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n // Inspired by OraclizeAPI's implementation - MIT licence\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\n\n if (value == 0) {\n return \"0\";\n }\n uint256 temp = value;\n uint256 digits;\n while (temp != 0) {\n digits++;\n temp /= 10;\n }\n bytes memory buffer = new bytes(digits);\n while (value != 0) {\n digits -= 1;\n buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\n value /= 10;\n }\n return string(buffer);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n if (value == 0) {\n return \"0x00\";\n }\n uint256 temp = value;\n uint256 length = 0;\n while (temp != 0) {\n length++;\n temp >>= 8;\n }\n return toHexString(value, length);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _HEX_SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/structs/EnumerableSet.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 => uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n" + }, + "contracts/automation/AbstractCCIPBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IRouterClient } from \"@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol\";\nimport { Client } from \"@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol\";\n\nabstract contract AbstractCCIPBridgeHelperModule is AbstractSafeModule {\n /**\n * @notice Bridges a token from the source chain to the destination chain using CCIP\n * @param ccipRouter The CCIP router contract\n * @param destinationChainSelector The selector for the destination chain\n * @param token The token to bridge\n * @param amount The amount of token to bridge\n */\n function _bridgeTokenWithCCIP(\n IRouterClient ccipRouter,\n uint64 destinationChainSelector,\n IERC20 token,\n uint256 amount\n ) internal {\n bool success;\n\n // Approve CCIP Router to move the token\n success = safeContract.execTransactionFromModule(\n address(token),\n 0, // Value\n abi.encodeWithSelector(token.approve.selector, ccipRouter, amount),\n 0 // Call\n );\n require(success, \"Failed to approve token\");\n\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(token),\n amount: amount\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory ccipMessage = Client.EVM2AnyMessage({\n receiver: abi.encode(address(safeContract)), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n // Get CCIP fee\n uint256 ccipFee = ccipRouter.getFee(\n destinationChainSelector,\n ccipMessage\n );\n\n // Send CCIP message\n success = safeContract.execTransactionFromModule(\n address(ccipRouter),\n ccipFee, // Value\n abi.encodeWithSelector(\n ccipRouter.ccipSend.selector,\n destinationChainSelector,\n ccipMessage\n ),\n 0 // Call\n );\n require(success, \"Failed to send CCIP message\");\n }\n}\n" + }, + "contracts/automation/AbstractLZBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\n\nimport { IOFT, SendParam } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\nimport { MessagingFee } from \"@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol\";\nimport { OptionsBuilder } from \"@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nabstract contract AbstractLZBridgeHelperModule is AbstractSafeModule {\n using OptionsBuilder for bytes;\n\n /**\n * @dev Bridges token using LayerZero.\n * @param lzEndpointId LayerZero endpoint id.\n * @param token Token to bridge.\n * @param lzAdapter LZ Adapter to use.\n * @param amount Amount of token to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n * @param isNativeToken Whether the token is native token.\n */\n function _bridgeTokenWithLz(\n uint32 lzEndpointId,\n IERC20 token,\n IOFT lzAdapter,\n uint256 amount,\n uint256 slippageBps,\n bool isNativeToken\n ) internal {\n bool success;\n\n if (!isNativeToken) {\n // Approve LZ Adapter to move the token\n success = safeContract.execTransactionFromModule(\n address(token),\n 0, // Value\n abi.encodeWithSelector(\n token.approve.selector,\n address(lzAdapter),\n amount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve token\");\n }\n\n // Calculate minimum amount to receive\n uint256 minAmount = (amount * (10000 - slippageBps)) / 10000;\n\n // Hardcoded gaslimit of 400k\n bytes memory options = OptionsBuilder\n .newOptions()\n .addExecutorLzReceiveOption(400000, 0);\n\n // Build send param\n SendParam memory sendParam = SendParam({\n dstEid: lzEndpointId,\n to: bytes32(uint256(uint160(address(safeContract)))),\n amountLD: amount,\n minAmountLD: minAmount,\n extraOptions: options,\n composeMsg: bytes(\"\"),\n oftCmd: bytes(\"\")\n });\n\n // Compute fees\n MessagingFee memory msgFee = lzAdapter.quoteSend(sendParam, false);\n\n uint256 value = isNativeToken\n ? amount + msgFee.nativeFee\n : msgFee.nativeFee;\n\n // Execute transaction\n success = safeContract.execTransactionFromModule(\n address(lzAdapter),\n value,\n abi.encodeWithSelector(\n lzAdapter.send.selector,\n sendParam,\n msgFee,\n address(safeContract)\n ),\n 0\n );\n require(success, \"Failed to bridge token\");\n }\n}\n" + }, + "contracts/automation/AbstractSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { ISafe } from \"../interfaces/ISafe.sol\";\n\nabstract contract AbstractSafeModule is AccessControlEnumerable {\n ISafe public immutable safeContract;\n\n bytes32 public constant OPERATOR_ROLE = keccak256(\"OPERATOR_ROLE\");\n\n modifier onlySafe() {\n require(\n msg.sender == address(safeContract),\n \"Caller is not the safe contract\"\n );\n _;\n }\n\n modifier onlyOperator() {\n require(\n hasRole(OPERATOR_ROLE, msg.sender),\n \"Caller is not an operator\"\n );\n _;\n }\n\n constructor(address _safeContract) {\n safeContract = ISafe(_safeContract);\n _grantRole(DEFAULT_ADMIN_ROLE, address(safeContract));\n _grantRole(OPERATOR_ROLE, address(safeContract));\n }\n\n /**\n * @dev Helps recovering any tokens accidentally sent to this module.\n * @param token Token to transfer. 0x0 to transfer Native token.\n * @param amount Amount to transfer. 0 to transfer all balance.\n */\n function transferTokens(address token, uint256 amount) external onlySafe {\n if (address(token) == address(0)) {\n // Move ETH\n amount = amount > 0 ? amount : address(this).balance;\n payable(address(safeContract)).transfer(amount);\n return;\n }\n\n // Move all balance if amount set to 0\n amount = amount > 0 ? amount : IERC20(token).balanceOf(address(this));\n\n // Transfer to Safe contract\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(token).transfer(address(safeContract), amount);\n }\n\n receive() external payable {\n // Accept ETH to pay for bridge fees\n }\n}\n" + }, + "contracts/automation/BaseBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// solhint-disable-next-line max-line-length\nimport { AbstractCCIPBridgeHelperModule, AbstractSafeModule, IRouterClient } from \"./AbstractCCIPBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { BridgedWOETHStrategy } from \"../strategies/BridgedWOETHStrategy.sol\";\n\ncontract BaseBridgeHelperModule is\n AccessControlEnumerable,\n AbstractCCIPBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0x98a0CbeF61bD2D21435f433bE4CD42B56B38CC93);\n IWETH9 public constant weth =\n IWETH9(0x4200000000000000000000000000000000000006);\n IERC20 public constant oethb =\n IERC20(0xDBFeFD2e8460a6Ee4955A68582F85708BAEA60A3);\n IERC4626 public constant bridgedWOETH =\n IERC4626(0xD8724322f44E5c58D7A815F542036fb17DbbF839);\n\n BridgedWOETHStrategy public constant bridgedWOETHStrategy =\n BridgedWOETHStrategy(0x80c864704DD06C3693ed5179190786EE38ACf835);\n\n IRouterClient public constant CCIP_ROUTER =\n IRouterClient(0x881e3A65B4d4a04dD529061dd0071cf975F58bCD);\n\n uint64 public constant CCIP_ETHEREUM_CHAIN_SELECTOR = 5009297550715157269;\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Ethereum.\n * @param woethAmount Amount of wOETH to bridge.\n */\n function bridgeWOETHToEthereum(uint256 woethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_ETHEREUM_CHAIN_SELECTOR,\n IERC20(address(bridgedWOETH)),\n woethAmount\n );\n }\n\n /**\n * @dev Bridges WETH to Ethereum.\n * @param wethAmount Amount of WETH to bridge.\n */\n function bridgeWETHToEthereum(uint256 wethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_ETHEREUM_CHAIN_SELECTOR,\n IERC20(address(weth)),\n wethAmount\n );\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem the wOETH for WETH using the Vault.\n * @return Amount of WETH received.\n */\n function depositWOETH(uint256 woethAmount, bool redeemWithVault)\n external\n onlyOperator\n returns (uint256)\n {\n return _depositWOETH(woethAmount, redeemWithVault);\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy and bridges it to Ethereum.\n * @param woethAmount Amount of wOETH to deposit.\n * @return Amount of WETH received.\n */\n function depositWOETHAndBridgeWETH(uint256 woethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n uint256 wethAmount = _depositWOETH(woethAmount, true);\n bridgeWETHToEthereum(wethAmount);\n return wethAmount;\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem the wOETH for WETH using the Vault.\n * @return Amount of WETH received.\n */\n function _depositWOETH(uint256 woethAmount, bool redeemWithVault)\n internal\n returns (uint256)\n {\n // Update oracle price\n bridgedWOETHStrategy.updateWOETHOraclePrice();\n\n // Rebase to account for any yields from price update\n vault.rebase();\n\n uint256 oethbAmount = oethb.balanceOf(address(safeContract));\n\n // Approve bridgedWOETH strategy to move wOETH\n bool success = safeContract.execTransactionFromModule(\n address(bridgedWOETH),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETH.approve.selector,\n address(bridgedWOETHStrategy),\n woethAmount\n ),\n 0 // Call\n );\n\n // Deposit to bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.depositBridgedWOETH.selector,\n woethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to deposit bridged WOETH\");\n\n oethbAmount = oethb.balanceOf(address(safeContract)) - oethbAmount;\n\n // Rebase to account for any yields from price update\n // and backing asset change from deposit\n vault.rebase();\n\n if (!redeemWithVault) {\n return oethbAmount;\n }\n\n // Redeem for WETH using Vault\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethbAmount,\n oethbAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETHb\");\n\n return oethbAmount;\n }\n\n /**\n * @dev Deposits WETH into the Vault and redeems wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to deposit.\n * @return Amount of wOETH received.\n */\n function depositWETHAndRedeemWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _withdrawWOETH(wethAmount);\n }\n\n function depositWETHAndBridgeWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n uint256 woethAmount = _withdrawWOETH(wethAmount);\n bridgeWOETHToEthereum(woethAmount);\n return woethAmount;\n }\n\n /**\n * @dev Withdraws wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to use to withdraw.\n * @return Amount of wOETH received.\n */\n function _withdrawWOETH(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETHb with WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETHb\");\n\n // Approve bridgedWOETH strategy to move OETHb\n success = safeContract.execTransactionFromModule(\n address(oethb),\n 0, // Value\n abi.encodeWithSelector(\n oethb.approve.selector,\n address(bridgedWOETHStrategy),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETHb\");\n\n uint256 woethAmount = bridgedWOETH.balanceOf(address(safeContract));\n\n // Withdraw from bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.withdrawBridgedWOETH.selector,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to withdraw bridged WOETH\");\n\n woethAmount =\n bridgedWOETH.balanceOf(address(safeContract)) -\n woethAmount;\n\n return woethAmount;\n }\n}\n" + }, + "contracts/automation/ClaimBribesSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { ICLGauge } from \"../interfaces/aerodrome/ICLGauge.sol\";\nimport { ICLPool } from \"../interfaces/aerodrome/ICLPool.sol\";\n\nstruct BribePoolInfo {\n address poolAddress;\n address rewardContractAddress;\n address[] rewardTokens;\n}\n\ninterface IAerodromeVoter {\n function claimBribes(\n address[] memory _bribes,\n address[][] memory _tokens,\n uint256 _tokenId\n ) external;\n}\n\ninterface IVeNFT {\n function ownerOf(uint256 tokenId) external view returns (address);\n\n function ownerToNFTokenIdList(address owner, uint256 index)\n external\n view\n returns (uint256);\n}\n\ninterface ICLRewardContract {\n function rewards(uint256 index) external view returns (address);\n\n function rewardsListLength() external view returns (uint256);\n}\n\ncontract ClaimBribesSafeModule is AbstractSafeModule {\n IAerodromeVoter public immutable voter;\n address public immutable veNFT;\n\n uint256[] nftIds;\n mapping(uint256 => uint256) nftIdIndex;\n\n BribePoolInfo[] bribePools;\n mapping(address => uint256) bribePoolIndex;\n\n event NFTIdAdded(uint256 nftId);\n event NFTIdRemoved(uint256 nftId);\n\n event BribePoolAdded(address bribePool);\n event BribePoolRemoved(address bribePool);\n\n constructor(\n address _safeContract,\n address _voter,\n address _veNFT\n ) AbstractSafeModule(_safeContract) {\n voter = IAerodromeVoter(_voter);\n veNFT = _veNFT;\n }\n\n /**\n * @dev Claim bribes for a range of NFTs\n * @param nftIndexStart The start index of the NFTs\n * @param nftIndexEnd The end index of the NFTs\n * @param silent Doesn't revert if the claim fails when true\n */\n function claimBribes(\n uint256 nftIndexStart,\n uint256 nftIndexEnd,\n bool silent\n ) external onlyOperator {\n if (nftIndexEnd < nftIndexStart) {\n (nftIndexStart, nftIndexEnd) = (nftIndexEnd, nftIndexStart);\n }\n uint256 nftCount = nftIds.length;\n nftIndexEnd = nftCount < nftIndexEnd ? nftCount : nftIndexEnd;\n\n (\n address[] memory rewardContractAddresses,\n address[][] memory rewardTokens\n ) = _getRewardsInfoArray();\n\n for (uint256 i = nftIndexStart; i < nftIndexEnd; i++) {\n uint256 nftId = nftIds[i];\n bool success = safeContract.execTransactionFromModule(\n address(voter),\n 0, // Value\n abi.encodeWithSelector(\n IAerodromeVoter.claimBribes.selector,\n rewardContractAddresses,\n rewardTokens,\n nftId\n ),\n 0 // Call\n );\n\n require(success || silent, \"ClaimBribes failed\");\n }\n }\n\n /**\n * @dev Get the reward contract address and reward tokens for all pools\n * @return rewardContractAddresses The reward contract addresses\n * @return rewardTokens The reward tokens\n */\n function _getRewardsInfoArray()\n internal\n view\n returns (\n address[] memory rewardContractAddresses,\n address[][] memory rewardTokens\n )\n {\n BribePoolInfo[] memory _bribePools = bribePools;\n uint256 bribePoolCount = _bribePools.length;\n rewardContractAddresses = new address[](bribePoolCount);\n rewardTokens = new address[][](bribePoolCount);\n\n for (uint256 i = 0; i < bribePoolCount; i++) {\n rewardContractAddresses[i] = _bribePools[i].rewardContractAddress;\n rewardTokens[i] = _bribePools[i].rewardTokens;\n }\n }\n\n /***************************************\n NFT Management\n ****************************************/\n /**\n * @dev Add NFT IDs to the list\n * @param _nftIds The NFT IDs to add\n */\n function addNFTIds(uint256[] memory _nftIds) external onlyOperator {\n for (uint256 i = 0; i < _nftIds.length; i++) {\n uint256 nftId = _nftIds[i];\n if (nftIdExists(nftId)) {\n // If it already exists, skip\n continue;\n }\n\n // Make sure the NFT is owned by the Safe\n require(\n IVeNFT(veNFT).ownerOf(nftId) == address(safeContract),\n \"NFT not owned by safe\"\n );\n\n nftIdIndex[nftId] = nftIds.length;\n nftIds.push(nftId);\n\n emit NFTIdAdded(nftId);\n }\n }\n\n /**\n * @dev Remove NFT IDs from the list\n * @param _nftIds The NFT IDs to remove\n */\n function removeNFTIds(uint256[] memory _nftIds) external onlyOperator {\n for (uint256 i = 0; i < _nftIds.length; i++) {\n uint256 nftId = _nftIds[i];\n if (!nftIdExists(nftId)) {\n // If it doesn't exist, skip\n continue;\n }\n\n uint256 index = nftIdIndex[nftId];\n uint256 lastNftId = nftIds[nftIds.length - 1];\n nftIds[index] = lastNftId;\n nftIdIndex[lastNftId] = index;\n nftIds.pop();\n\n emit NFTIdRemoved(nftId);\n }\n }\n\n /**\n * @dev Check if a NFT exists on the list\n * @param nftId The NFT ID to check\n * @return true if the NFT ID exists, false otherwise\n */\n function nftIdExists(uint256 nftId) public view returns (bool) {\n uint256 index = nftIdIndex[nftId];\n uint256[] memory _nftIds = nftIds;\n return (index < _nftIds.length) && _nftIds[index] == nftId;\n }\n\n /**\n * @dev Get the length of the nftIds list\n * @return The length of the nftIds list\n */\n function getNFTIdsLength() external view returns (uint256) {\n return nftIds.length;\n }\n\n /**\n * @dev Get all NFT IDs\n * @return The NFT IDs\n */\n function getAllNFTIds() external view returns (uint256[] memory) {\n return nftIds;\n }\n\n /**\n * @dev Fetch all NFT IDs from the veNFT contract\n * @notice This can revert if Safe owns too many NFTs since tx will be huge.\n * This function is public, anyone can call it, since it only fetches\n * the NFT IDs owned by the Safe. It shouldn't cause us any issues.\n */\n function fetchNFTIds() external {\n // Purge the array\n delete nftIds;\n\n uint256 i = 0;\n while (true) {\n uint256 nftId = IVeNFT(veNFT).ownerToNFTokenIdList(\n address(safeContract),\n i\n );\n if (nftId == 0) {\n break;\n }\n\n nftIdIndex[nftId] = nftIds.length;\n nftIds.push(nftId);\n i++;\n }\n }\n\n /**\n * @dev Remove all NFT IDs from the list\n */\n function removeAllNFTIds() external onlyOperator {\n uint256 length = nftIds.length;\n for (uint256 i = 0; i < length; i++) {\n uint256 nftId = nftIds[i];\n delete nftIdIndex[nftId];\n emit NFTIdRemoved(nftId);\n }\n\n delete nftIds;\n }\n\n /***************************************\n Bribe Pool Management\n ****************************************/\n // @dev Whitelist a pool to claim bribes from\n // @param _poolAddress The address of the pool to whitelist\n function addBribePool(address _poolAddress, bool _isVotingContract)\n external\n onlySafe\n {\n BribePoolInfo memory bribePool;\n\n if (_isVotingContract) {\n bribePool = BribePoolInfo({\n poolAddress: _poolAddress,\n rewardContractAddress: _poolAddress,\n rewardTokens: _getRewardTokenAddresses(_poolAddress)\n });\n } else {\n // Find the gauge address\n address _gaugeAddress = ICLPool(_poolAddress).gauge();\n // And the reward contract address\n address _rewardContractAddress = ICLGauge(_gaugeAddress)\n .feesVotingReward();\n\n bribePool = BribePoolInfo({\n poolAddress: _poolAddress,\n rewardContractAddress: _rewardContractAddress,\n rewardTokens: _getRewardTokenAddresses(_rewardContractAddress)\n });\n }\n\n if (bribePoolExists(_poolAddress)) {\n // Update if it already exists\n bribePools[bribePoolIndex[_poolAddress]] = bribePool;\n } else {\n // If not, Append to the list\n bribePoolIndex[_poolAddress] = bribePools.length;\n bribePools.push(bribePool);\n }\n\n emit BribePoolAdded(_poolAddress);\n }\n\n /**\n * @dev Update the reward token addresses for all pools\n */\n function updateRewardTokenAddresses() external onlyOperator {\n BribePoolInfo[] storage _bribePools = bribePools;\n for (uint256 i = 0; i < _bribePools.length; i++) {\n BribePoolInfo storage bribePool = _bribePools[i];\n bribePool.rewardTokens = _getRewardTokenAddresses(\n bribePool.rewardContractAddress == bribePool.poolAddress\n ? bribePool.poolAddress\n : bribePool.rewardContractAddress\n );\n }\n }\n\n /**\n * @dev Get the reward token addresses for a given reward contract address\n * @param _rewardContractAddress The address of the reward contract\n * @return _rewardTokens The reward token addresses\n */\n function _getRewardTokenAddresses(address _rewardContractAddress)\n internal\n view\n returns (address[] memory)\n {\n address[] memory _rewardTokens = new address[](\n ICLRewardContract(_rewardContractAddress).rewardsListLength()\n );\n for (uint256 i = 0; i < _rewardTokens.length; i++) {\n _rewardTokens[i] = ICLRewardContract(_rewardContractAddress)\n .rewards(i);\n }\n\n return _rewardTokens;\n }\n\n /**\n * @dev Remove a bribe pool from the list\n * @param _poolAddress The address of the pool to remove\n */\n function removeBribePool(address _poolAddress) external onlySafe {\n if (!bribePoolExists(_poolAddress)) {\n // If it doesn't exist, skip\n return;\n }\n\n uint256 index = bribePoolIndex[_poolAddress];\n BribePoolInfo memory lastBribePool = bribePools[bribePools.length - 1];\n bribePools[index] = lastBribePool;\n bribePoolIndex[lastBribePool.poolAddress] = index;\n bribePools.pop();\n\n emit BribePoolRemoved(_poolAddress);\n }\n\n /**\n * @dev Check if a bribe pool exists\n * @param bribePool The address of the pool to check\n * @return true if the pool exists, false otherwise\n */\n function bribePoolExists(address bribePool) public view returns (bool) {\n BribePoolInfo[] memory _bribePools = bribePools;\n uint256 poolIndex = bribePoolIndex[bribePool];\n return\n poolIndex < _bribePools.length &&\n _bribePools[poolIndex].poolAddress == bribePool;\n }\n\n /**\n * @dev Get the length of the bribe pools list\n * @return The length of the bribe pools list\n */\n function getBribePoolsLength() external view returns (uint256) {\n return bribePools.length;\n }\n}\n" + }, + "contracts/automation/ClaimStrategyRewardsSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\n\nimport { ISafe } from \"../interfaces/ISafe.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract ClaimStrategyRewardsSafeModule is AbstractSafeModule {\n using SafeERC20 for IERC20;\n\n mapping(address => bool) public isStrategyWhitelisted;\n address[] public strategies;\n\n event StrategyAdded(address strategy);\n event StrategyRemoved(address strategy);\n\n event ClaimRewardsFailed(address strategy);\n\n constructor(\n address _safeAddress,\n address operator,\n address[] memory _strategies\n ) AbstractSafeModule(_safeAddress) {\n _grantRole(OPERATOR_ROLE, operator);\n\n // Whitelist all strategies\n for (uint256 i = 0; i < _strategies.length; i++) {\n _addStrategy(_strategies[i]);\n }\n }\n\n /**\n * @dev Claim rewards from all whitelisted strategies\n * @param silent Doesn't revert on error if set to true\n */\n function claimRewards(bool silent) external onlyRole(OPERATOR_ROLE) {\n uint256 strategiesLength = strategies.length;\n for (uint256 i = 0; i < strategiesLength; i++) {\n address strategy = strategies[i];\n\n // Execute `collectRewardTokens` for all strategies\n bool success = safeContract.execTransactionFromModule(\n strategy, // To\n 0, // Value\n abi.encodeWithSelector(IStrategy.collectRewardTokens.selector),\n 0 // Call\n );\n\n if (!success) {\n emit ClaimRewardsFailed(strategy);\n }\n\n require(success || silent, \"Failed to claim rewards\");\n }\n }\n\n /**\n * @dev Add a strategy to the whitelist\n * @param _strategy The address of the strategy to add\n */\n function addStrategy(address _strategy)\n external\n onlyRole(DEFAULT_ADMIN_ROLE)\n {\n _addStrategy(_strategy);\n }\n\n function _addStrategy(address _strategy) internal {\n require(\n !isStrategyWhitelisted[_strategy],\n \"Strategy already whitelisted\"\n );\n isStrategyWhitelisted[_strategy] = true;\n strategies.push(_strategy);\n emit StrategyAdded(_strategy);\n }\n\n /**\n * @dev Remove a strategy from the whitelist\n * @param _strategy The address of the strategy to remove\n */\n function removeStrategy(address _strategy)\n external\n onlyRole(DEFAULT_ADMIN_ROLE)\n {\n require(isStrategyWhitelisted[_strategy], \"Strategy not whitelisted\");\n isStrategyWhitelisted[_strategy] = false;\n\n for (uint256 i = 0; i < strategies.length; i++) {\n if (strategies[i] == _strategy) {\n strategies[i] = strategies[strategies.length - 1];\n strategies.pop();\n break;\n }\n }\n\n emit StrategyRemoved(_strategy);\n }\n}\n" + }, + "contracts/automation/CollectXOGNRewardsModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IXOGN {\n function collectRewards() external;\n}\n\ncontract CollectXOGNRewardsModule is AbstractSafeModule {\n IXOGN public constant xogn =\n IXOGN(0x63898b3b6Ef3d39332082178656E9862bee45C57);\n address public constant rewardsSource =\n 0x7609c88E5880e934dd3A75bCFef44E31b1Badb8b;\n IERC20 public constant ogn =\n IERC20(0x8207c1FfC5B6804F6024322CcF34F29c3541Ae26);\n\n constructor(address _safeContract, address operator)\n AbstractSafeModule(_safeContract)\n {\n _grantRole(OPERATOR_ROLE, operator);\n }\n\n function collectRewards() external onlyOperator {\n uint256 balance = ogn.balanceOf(address(safeContract));\n\n bool success = safeContract.execTransactionFromModule(\n address(xogn),\n 0, // Value\n abi.encodeWithSelector(IXOGN.collectRewards.selector),\n 0 // Call\n );\n\n require(success, \"Failed to collect rewards\");\n\n balance = ogn.balanceOf(address(safeContract)) - balance;\n\n if (balance == 0) {\n return;\n }\n\n success = safeContract.execTransactionFromModule(\n address(ogn),\n 0, // Value\n abi.encodeWithSelector(\n IERC20.transfer.selector,\n rewardsSource,\n balance\n ),\n 0 // Call\n );\n\n require(success, \"Failed to collect rewards\");\n }\n}\n" + }, + "contracts/automation/CurvePoolBoosterBribesModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\n\ninterface ICurvePoolBooster {\n function manageTotalRewardAmount(\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external;\n\n function manageNumberOfPeriods(\n uint8 extraNumberOfPeriods,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external;\n}\n\ncontract CurvePoolBoosterBribesModule is AbstractSafeModule {\n address[] public POOLS;\n\n event PoolBoosterAddressAdded(address pool);\n event PoolBoosterAddressRemoved(address pool);\n\n constructor(\n address _safeContract,\n address _operator,\n address[] memory _pools\n ) AbstractSafeModule(_safeContract) {\n _grantRole(OPERATOR_ROLE, _operator);\n _addPoolBoosterAddress(_pools);\n }\n\n function addPoolBoosterAddress(address[] memory pools)\n external\n onlyOperator\n {\n _addPoolBoosterAddress(pools);\n }\n\n function _addPoolBoosterAddress(address[] memory pools) internal {\n for (uint256 i = 0; i < pools.length; i++) {\n POOLS.push(pools[i]);\n emit PoolBoosterAddressAdded(pools[i]);\n }\n }\n\n function removePoolBoosterAddress(address[] calldata pools)\n external\n onlyOperator\n {\n for (uint256 i = 0; i < pools.length; i++) {\n _removePoolBoosterAddress(pools[i]);\n }\n }\n\n function _removePoolBoosterAddress(address pool) internal {\n uint256 length = POOLS.length;\n for (uint256 i = 0; i < length; i++) {\n if (POOLS[i] == pool) {\n POOLS[i] = POOLS[length - 1];\n POOLS.pop();\n emit PoolBoosterAddressRemoved(pool);\n }\n }\n }\n\n function manageBribes() external onlyOperator {\n uint256 length = POOLS.length;\n for (uint256 i = 0; i < length; i++) {\n address poolBoosterAddress = POOLS[i];\n\n // PoolBooster need to have a balance of at least 0.002 ether to operate\n // 0.001 ether are used for the bridge fee\n require(\n poolBoosterAddress.balance > 0.002 ether,\n \"Insufficient balance for bribes\"\n );\n\n require(\n safeContract.execTransactionFromModule(\n poolBoosterAddress,\n 0, // Value\n abi.encodeWithSelector(\n ICurvePoolBooster.manageNumberOfPeriods.selector,\n 1, // extraNumberOfPeriods\n 0.001 ether, // bridgeFee\n 1000000 // additionalGasLimit\n ),\n 0\n ),\n \"Manage number of periods failed\"\n );\n\n require(\n safeContract.execTransactionFromModule(\n poolBoosterAddress,\n 0, // Value\n abi.encodeWithSelector(\n ICurvePoolBooster.manageTotalRewardAmount.selector,\n 0.001 ether, // bridgeFee\n 1000000 // additionalGasLimit\n ),\n 0\n ),\n \"Manage total reward failed\"\n );\n }\n }\n}\n" + }, + "contracts/automation/EthereumBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractLZBridgeHelperModule, AbstractSafeModule } from \"./AbstractLZBridgeHelperModule.sol\";\nimport { AbstractCCIPBridgeHelperModule, IRouterClient } from \"./AbstractCCIPBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IOFT } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract EthereumBridgeHelperModule is\n AccessControlEnumerable,\n AbstractLZBridgeHelperModule,\n AbstractCCIPBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0x39254033945AA2E4809Cc2977E7087BEE48bd7Ab);\n IWETH9 public constant weth =\n IWETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);\n IERC20 public constant oeth =\n IERC20(0x856c4Efb76C1D1AE02e20CEB03A2A6a08b0b8dC3);\n IERC4626 public constant woeth =\n IERC4626(0xDcEe70654261AF21C44c093C300eD3Bb97b78192);\n\n uint32 public constant LZ_PLUME_ENDPOINT_ID = 30370;\n IOFT public constant LZ_WOETH_OMNICHAIN_ADAPTER =\n IOFT(0x7d1bEa5807e6af125826d56ff477745BB89972b8);\n IOFT public constant LZ_ETH_OMNICHAIN_ADAPTER =\n IOFT(0x77b2043768d28E9C9aB44E1aBfC95944bcE57931);\n\n IRouterClient public constant CCIP_ROUTER =\n IRouterClient(0x80226fc0Ee2b096224EeAc085Bb9a8cba1146f7D);\n\n uint64 public constant CCIP_BASE_CHAIN_SELECTOR = 15971525489660198786;\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Plume.\n * @param woethAmount Amount of wOETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWOETHToPlume(uint256 woethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_PLUME_ENDPOINT_ID,\n woeth,\n LZ_WOETH_OMNICHAIN_ADAPTER,\n woethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Bridges wOETH to Base using CCIP.\n * @param woethAmount Amount of wOETH to bridge.\n */\n function bridgeWOETHToBase(uint256 woethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_BASE_CHAIN_SELECTOR,\n woeth,\n woethAmount\n );\n }\n\n /**\n * @dev Bridges wETH to Plume.\n * @param wethAmount Amount of wETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWETHToPlume(uint256 wethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n // Unwrap into ETH\n safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(weth.withdraw.selector, wethAmount),\n 0 // Call\n );\n\n _bridgeTokenWithLz(\n LZ_PLUME_ENDPOINT_ID,\n IERC20(address(weth)),\n LZ_ETH_OMNICHAIN_ADAPTER,\n wethAmount,\n slippageBps,\n true\n );\n }\n\n /**\n * @dev Bridges wETH to Base using CCIP.\n * @param wethAmount Amount of wETH to bridge.\n */\n function bridgeWETHToBase(uint256 wethAmount) public payable onlyOperator {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_BASE_CHAIN_SELECTOR,\n IERC20(address(weth)),\n wethAmount\n );\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH.\n * @param wethAmount Amount of WETH to mint.\n * @param useNativeToken Whether to use native token to mint.\n * @return Amount of wOETH minted.\n */\n function mintAndWrap(uint256 wethAmount, bool useNativeToken)\n external\n onlyOperator\n returns (uint256)\n {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n return _mintAndWrap(wethAmount);\n }\n\n function wrapETH(uint256 ethAmount) public payable onlyOperator {\n // Deposit ETH into WETH\n safeContract.execTransactionFromModule(\n address(weth),\n ethAmount, // Value\n abi.encodeWithSelector(weth.deposit.selector),\n 0 // Call\n );\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH.\n * @param wethAmount Amount of WETH to mint.\n * @return Amount of wOETH minted.\n */\n function _mintAndWrap(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETH\");\n\n // Approve wOETH to move OETH\n success = safeContract.execTransactionFromModule(\n address(oeth),\n 0, // Value\n abi.encodeWithSelector(\n oeth.approve.selector,\n address(woeth),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETH\");\n\n uint256 woethAmount = woeth.balanceOf(address(safeContract));\n\n // Wrap OETH into wOETH\n success = safeContract.execTransactionFromModule(\n address(woeth),\n 0, // Value\n abi.encodeWithSelector(\n woeth.deposit.selector,\n wethAmount,\n address(safeContract)\n ),\n 0 // Call\n );\n require(success, \"Failed to wrap OETH\");\n\n // Compute amount of wOETH minted\n return woeth.balanceOf(address(safeContract)) - woethAmount;\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH, then bridges it to Plume.\n * @param wethAmount Amount of WETH to mint.\n * @param slippageBps Bridge slippage in 10^4 basis points.\n * @param useNativeToken Whether to use native token to mint.\n */\n function mintWrapAndBridgeToPlume(\n uint256 wethAmount,\n uint256 slippageBps,\n bool useNativeToken\n ) external payable onlyOperator {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n uint256 woethAmount = _mintAndWrap(wethAmount);\n bridgeWOETHToPlume(woethAmount, slippageBps);\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH, then bridges it to Base using CCIP.\n * @param wethAmount Amount of WETH to mint.\n * @param useNativeToken Whether to use native token to mint.\n */\n function mintWrapAndBridgeToBase(uint256 wethAmount, bool useNativeToken)\n external\n payable\n onlyOperator\n {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n uint256 woethAmount = _mintAndWrap(wethAmount);\n bridgeWOETHToBase(woethAmount);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH.\n * @param woethAmount Amount of wOETH to unwrap.\n * @return Amount of WETH received.\n */\n function unwrapAndRedeem(uint256 woethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _unwrapAndRedeem(woethAmount);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH.\n * @param woethAmount Amount of wOETH to unwrap.\n * @return Amount of WETH received.\n */\n function _unwrapAndRedeem(uint256 woethAmount) internal returns (uint256) {\n uint256 oethAmount = oeth.balanceOf(address(safeContract));\n\n // Unwrap wOETH\n bool success = safeContract.execTransactionFromModule(\n address(woeth),\n 0, // Value\n abi.encodeWithSelector(\n woeth.redeem.selector,\n woethAmount,\n address(safeContract),\n address(safeContract)\n ),\n 0 // Call\n );\n require(success, \"Failed to unwrap wOETH\");\n\n oethAmount = oeth.balanceOf(address(safeContract)) - oethAmount;\n\n // Redeem OETH using Vault to get WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethAmount,\n oethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETH\");\n\n return oethAmount;\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH, then bridges it to Plume.\n * @param woethAmount Amount of wOETH to unwrap.\n * @param slippageBps Bridge slippage in 10^4 basis points.\n */\n function unwrapRedeemAndBridgeToPlume(\n uint256 woethAmount,\n uint256 slippageBps\n ) external payable onlyOperator {\n uint256 wethAmount = _unwrapAndRedeem(woethAmount);\n // Unwrap into ETH\n safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(weth.withdraw.selector, wethAmount),\n 0 // Call\n );\n\n bridgeWETHToPlume(wethAmount, slippageBps);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH, then bridges it to Base using CCIP.\n * @param woethAmount Amount of wOETH to unwrap.\n */\n function unwrapRedeemAndBridgeToBase(uint256 woethAmount)\n external\n payable\n onlyOperator\n {\n uint256 wethAmount = _unwrapAndRedeem(woethAmount);\n bridgeWETHToBase(wethAmount);\n }\n}\n" + }, + "contracts/automation/PlumeBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { AbstractLZBridgeHelperModule } from \"./AbstractLZBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IOFT } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { BridgedWOETHStrategy } from \"../strategies/BridgedWOETHStrategy.sol\";\n\ncontract PlumeBridgeHelperModule is\n AccessControlEnumerable,\n AbstractLZBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0xc8c8F8bEA5631A8AF26440AF32a55002138cB76a);\n IWETH9 public constant weth =\n IWETH9(0xca59cA09E5602fAe8B629DeE83FfA819741f14be);\n IERC20 public constant oethp =\n IERC20(0xFCbe50DbE43bF7E5C88C6F6Fb9ef432D4165406E);\n IERC4626 public constant bridgedWOETH =\n IERC4626(0xD8724322f44E5c58D7A815F542036fb17DbbF839);\n\n uint32 public constant LZ_ETHEREUM_ENDPOINT_ID = 30101;\n IOFT public constant LZ_WOETH_OMNICHAIN_ADAPTER =\n IOFT(0x592CB6A596E7919930bF49a27AdAeCA7C055e4DB);\n IOFT public constant LZ_ETH_OMNICHAIN_ADAPTER =\n IOFT(0x4683CE822272CD66CEa73F5F1f9f5cBcaEF4F066);\n\n BridgedWOETHStrategy public constant bridgedWOETHStrategy =\n BridgedWOETHStrategy(0x1E3EdD5e019207D6355Ea77F724b1F1BF639B569);\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Ethereum.\n * @param woethAmount Amount of wOETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWOETHToEthereum(uint256 woethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_ETHEREUM_ENDPOINT_ID,\n IERC20(address(bridgedWOETH)),\n LZ_WOETH_OMNICHAIN_ADAPTER,\n woethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Bridges wETH to Ethereum.\n * @param wethAmount Amount of wETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWETHToEthereum(uint256 wethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_ETHEREUM_ENDPOINT_ID,\n IERC20(address(weth)),\n LZ_ETH_OMNICHAIN_ADAPTER,\n wethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem with Vault.\n * @return Amount of OETHp received.\n */\n function depositWOETH(uint256 woethAmount, bool redeemWithVault)\n external\n onlyOperator\n returns (uint256)\n {\n return _depositWOETH(woethAmount, redeemWithVault);\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy and bridges it to Ethereum.\n * @param woethAmount Amount of wOETH to deposit.\n * @param slippageBps Slippage in 10^4 basis points.\n * @return Amount of WETH received.\n */\n function depositWOETHAndBridgeWETH(uint256 woethAmount, uint256 slippageBps)\n external\n payable\n onlyOperator\n returns (uint256)\n {\n uint256 wethAmount = _depositWOETH(woethAmount, true);\n bridgeWETHToEthereum(wethAmount, slippageBps);\n return wethAmount;\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem with Vault.\n * @return Amount of OETHp received.\n */\n function _depositWOETH(uint256 woethAmount, bool redeemWithVault)\n internal\n returns (uint256)\n {\n // Update oracle price\n bridgedWOETHStrategy.updateWOETHOraclePrice();\n\n // Rebase to account for any yields from price update\n vault.rebase();\n\n uint256 oethpAmount = oethp.balanceOf(address(safeContract));\n\n // Approve bridgedWOETH strategy to move wOETH\n bool success = safeContract.execTransactionFromModule(\n address(bridgedWOETH),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETH.approve.selector,\n address(bridgedWOETHStrategy),\n woethAmount\n ),\n 0 // Call\n );\n\n // Deposit to bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.depositBridgedWOETH.selector,\n woethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to deposit bridged WOETH\");\n\n oethpAmount = oethp.balanceOf(address(safeContract)) - oethpAmount;\n\n // Rebase to account for any yields from price update\n // and backing asset change from deposit\n vault.rebase();\n\n if (!redeemWithVault) {\n return oethpAmount;\n }\n\n // Redeem for WETH using Vault\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethpAmount,\n oethpAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETHp\");\n\n return oethpAmount;\n }\n\n /**\n * @dev Deposits wETH into the vault.\n * @param wethAmount Amount of wETH to deposit.\n * @return Amount of OETHp received.\n */\n function depositWETHAndRedeemWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _withdrawWOETH(wethAmount);\n }\n\n /**\n * @dev Deposits wETH into the vault and bridges it to Ethereum.\n * @param wethAmount Amount of wETH to deposit.\n * @param slippageBps Slippage in 10^4 basis points.\n * @return Amount of WOETH received.\n */\n function depositWETHAndBridgeWOETH(uint256 wethAmount, uint256 slippageBps)\n external\n payable\n onlyOperator\n returns (uint256)\n {\n uint256 woethAmount = _withdrawWOETH(wethAmount);\n bridgeWOETHToEthereum(woethAmount, slippageBps);\n return woethAmount;\n }\n\n /**\n * @dev Withdraws wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to use to withdraw.\n * @return Amount of wOETH received.\n */\n function _withdrawWOETH(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETHp with WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETHp\");\n\n // Approve bridgedWOETH strategy to move OETHp\n success = safeContract.execTransactionFromModule(\n address(oethp),\n 0, // Value\n abi.encodeWithSelector(\n oethp.approve.selector,\n address(bridgedWOETHStrategy),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETHp\");\n\n uint256 woethAmount = bridgedWOETH.balanceOf(address(safeContract));\n\n // Withdraw from bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.withdrawBridgedWOETH.selector,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to withdraw bridged WOETH\");\n\n woethAmount =\n bridgedWOETH.balanceOf(address(safeContract)) -\n woethAmount;\n\n return woethAmount;\n }\n}\n" + }, + "contracts/beacon/BeaconConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary BeaconConsolidation {\n /// @notice The address the validator consolidation requests are sent\n /// See https://eips.ethereum.org/EIPS/eip-7251\n address internal constant CONSOLIDATION_REQUEST_ADDRESS =\n 0x0000BBdDc7CE488642fb579F8B00f3a590007251;\n\n function request(bytes calldata source, bytes calldata target)\n internal\n returns (uint256 fee_)\n {\n require(source.length == 48, \"Invalid source byte length\");\n require(target.length == 48, \"Invalid target byte length\");\n\n fee_ = fee();\n\n // Call the Consolidation Request contract with the public keys of the source and target\n // validators packed together.\n // This does not have a function signature, so we use a call\n (bool success, ) = CONSOLIDATION_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(source, target)\n );\n\n require(success, \"Consolidation request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the consolidation request contract\n (bool success, bytes memory result) = CONSOLIDATION_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/beacon/BeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"./BeaconProofsLib.sol\";\n\ncontract BeaconProofs {\n /// @notice Verifies the validator public key against the beacon block root\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash The beacon chain hash of the validator public key\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view {\n BeaconProofsLib.verifyValidatorPubkey(\n beaconBlockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n withdrawalAddress\n );\n }\n\n /// @notice Verifies the balances container against the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerLeaf The leaf node containing the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view {\n BeaconProofsLib.verifyBalancesContainer(\n beaconBlockRoot,\n balancesContainerLeaf,\n balancesContainerProof\n );\n }\n\n /// @notice Verifies the validator balance against the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances\n /// @param balanceProof The merkle proof for the validator balance against the root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalanceGwei) {\n validatorBalanceGwei = BeaconProofsLib.verifyValidatorBalance(\n balancesContainerRoot,\n validatorBalanceLeaf,\n balanceProof,\n validatorIndex\n );\n }\n\n /// @notice Verifies the slot of the first pending deposit against the beacon block root\n /// BeaconBlock.state.PendingDeposits[0].slot\n /// @param beaconBlockRoot The root of the beacon block\n /// @param slot The beacon chain slot to verify\n /// @param firstPendingDepositSlotProof The merkle proof for the first pending deposit's slot\n /// against the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view {\n BeaconProofsLib.verifyFirstPendingDepositSlot(\n beaconBlockRoot,\n slot,\n firstPendingDepositSlotProof\n );\n }\n}\n" + }, + "contracts/beacon/BeaconProofsLib.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Merkle } from \"./Merkle.sol\";\nimport { Endian } from \"./Endian.sol\";\n\nlibrary BeaconProofsLib {\n // Known generalized indices in the beacon block\n // BeaconBlock.slot\n uint256 internal constant SLOT_GENERALIZED_INDEX = 8;\n // BeaconBlock.state.PendingDeposits[0]\n uint256 internal constant FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX =\n 198105366528;\n // BeaconBlock.state.PendingDeposits[0].slot\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX =\n 1584842932228;\n // BeaconBlock.body.executionPayload.blockNumber\n uint256 internal constant BLOCK_NUMBER_GENERALIZED_INDEX = 6438;\n // BeaconBlock.state.validators\n uint256 internal constant VALIDATORS_CONTAINER_GENERALIZED_INDEX = 715;\n // BeaconBlock.state.balances\n uint256 internal constant BALANCES_CONTAINER_GENERALIZED_INDEX = 716;\n\n // Beacon Container Tree Heights\n uint256 internal constant BALANCES_HEIGHT = 39;\n uint256 internal constant VALIDATORS_HEIGHT = 41;\n uint256 internal constant VALIDATOR_HEIGHT = 3;\n\n /// @notice Fields in the Validator container for phase 0\n /// See https://ethereum.github.io/consensus-specs/specs/phase0/beacon-chain/#validator\n uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;\n\n /// @notice Verifies the validator public key against the beacon block root\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash The beacon chain hash of the validator public key\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) internal view {\n // BeaconBlock.state.validators[validatorIndex].pubkey\n uint256 generalizedIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_HEIGHT,\n validatorIndex\n );\n generalizedIndex = concatGenIndices(\n generalizedIndex,\n VALIDATOR_HEIGHT,\n VALIDATOR_PUBKEY_INDEX\n );\n\n // Get the withdrawal address from the first witness in the pubkey merkle proof.\n address withdrawalAddressFromProof;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // The first 32 bytes of the proof is the withdrawal credential so load it into memory.\n calldatacopy(0, validatorPubKeyProof.offset, 32)\n // Cast the 32 bytes in memory to an address which is the last 20 bytes.\n withdrawalAddressFromProof := mload(0)\n }\n require(\n withdrawalAddressFromProof == withdrawalAddress,\n \"Invalid withdrawal address\"\n );\n\n require(\n Merkle.verifyInclusionSha256({\n proof: validatorPubKeyProof,\n root: beaconBlockRoot,\n leaf: pubKeyHash,\n index: generalizedIndex\n }),\n \"Invalid validator pubkey proof\"\n );\n }\n\n /// @notice Verifies the balances container against the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerRoot The merkle root of the the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata balancesContainerProof\n ) internal view {\n // BeaconBlock.state.balances\n require(\n Merkle.verifyInclusionSha256({\n proof: balancesContainerProof,\n root: beaconBlockRoot,\n leaf: balancesContainerRoot,\n index: BALANCES_CONTAINER_GENERALIZED_INDEX\n }),\n \"Invalid balance container proof\"\n );\n }\n\n /// @notice Verifies the validator balance against the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param balanceProof The merkle proof for the validator balance against the root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) internal view returns (uint256 validatorBalanceGwei) {\n // Four balances are stored in each leaf so the validator index is divided by 4\n uint64 balanceIndex = validatorIndex / 4;\n\n // Get the index within the balances container, not the Beacon Block\n // BeaconBlock.state.balances[balanceIndex]\n uint256 generalizedIndex = concatGenIndices(\n 1,\n BALANCES_HEIGHT,\n balanceIndex\n );\n\n validatorBalanceGwei = balanceAtIndex(\n validatorBalanceLeaf,\n validatorIndex\n );\n\n require(\n Merkle.verifyInclusionSha256({\n proof: balanceProof,\n root: balancesContainerRoot,\n leaf: validatorBalanceLeaf,\n index: generalizedIndex\n }),\n \"Invalid balance proof\"\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the slot of the first pending deposit against the beacon block root\n /// BeaconBlock.state.PendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block\n /// @param slot The beacon chain slot to verify\n /// @param firstPendingDepositSlotProof The merkle proof for the first pending deposit's slot\n /// against the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) internal view {\n // If the deposit queue is\n bytes32 leaf = slot == 0\n ? bytes32(0) // empty, use an empty leaf node\n : Endian.toLittleEndianUint64(slot); // not empty, convert uint64 slot number to a little endian bytes32\n\n // If the deposit queue is empty, use the root of the first pending deposit\n // BeaconBlock.state.PendingDeposits[0]\n /// If not empty, use the slot in the first pending deposit\n /// BeaconBlock.state.PendingDeposits[0].slot\n uint256 generalizedIndex = slot == 0\n ? FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX\n : FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX;\n\n require(\n Merkle.verifyInclusionSha256({\n proof: firstPendingDepositSlotProof,\n root: beaconBlockRoot,\n leaf: leaf,\n index: generalizedIndex\n }),\n \"Invalid pending deposit proof\"\n );\n }\n\n ////////////////////////////////////////////////////\n /// Internal Helper Functions\n ////////////////////////////////////////////////////\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n internal\n pure\n returns (uint256)\n {\n uint256 bitShiftAmount = (validatorIndex % 4) * 64;\n return\n Endian.fromLittleEndianUint64(\n bytes32((uint256(validatorBalanceLeaf) << bitShiftAmount))\n );\n }\n\n /// @notice Concatenates two beacon chain generalized indices into one.\n /// @param genIndex The first generalized index or 1 if calculating for a single container.\n /// @param height The merkle tree height of the second container. eg 39 for balances, 41 for validators.\n /// @param index The index within the second container. eg the validator index.\n /// @return genIndex The concatenated generalized index.\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) internal pure returns (uint256) {\n return (genIndex << height) | index;\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the Beacon Block Root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Block Root Oracle to get the parent block root\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/Endian.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nlibrary Endian {\n /**\n * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64\n * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type\n * @return n The big endian-formatted uint64\n * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits),\n * but it is immediately truncated to a uint64 (i.e. 64 bits)\n * through a right-shift/shr operation.\n */\n function fromLittleEndianUint64(bytes32 lenum)\n internal\n pure\n returns (uint64 n)\n {\n // the number needs to be stored in little-endian encoding (ie in bytes 0-8)\n n = uint64(uint256(lenum >> 192));\n // forgefmt: disable-next-item\n return\n (n >> 56) |\n ((0x00FF000000000000 & n) >> 40) |\n ((0x0000FF0000000000 & n) >> 24) |\n ((0x000000FF00000000 & n) >> 8) |\n ((0x00000000FF000000 & n) << 8) |\n ((0x0000000000FF0000 & n) << 24) |\n ((0x000000000000FF00 & n) << 40) |\n ((0x00000000000000FF & n) << 56);\n }\n\n function toLittleEndianUint64(uint64 benum)\n internal\n pure\n returns (bytes32 n)\n {\n // Convert to little-endian by reversing byte order\n uint64 reversed = (benum >> 56) |\n ((0x00FF000000000000 & benum) >> 40) |\n ((0x0000FF0000000000 & benum) >> 24) |\n ((0x000000FF00000000 & benum) >> 8) |\n ((0x00000000FF000000 & benum) << 8) |\n ((0x0000000000FF0000 & benum) << 24) |\n ((0x000000000000FF00 & benum) << 40) |\n ((0x00000000000000FF & benum) << 56);\n\n // Store the little-endian uint64 in the least significant 64 bits of bytes32\n n = bytes32(uint256(reversed));\n // Shift to most significant bits\n n = n << 192;\n }\n}\n" + }, + "contracts/beacon/Merkle.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev These functions deal with verification of Merkle Tree proofs.\n *\n * The tree and the proofs can be generated using our\n * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].\n * You will find a quickstart guide in the readme.\n *\n * WARNING: You should avoid using leaf values that are 64 bytes long prior to\n * hashing, or use a hash function other than keccak256 for hashing leaves.\n * This is because the concatenation of a sorted pair of internal nodes in\n * the merkle tree could be reinterpreted as a leaf value.\n * OpenZeppelin's JavaScript library generates merkle trees that are safe\n * against this attack out of the box.\n */\nlibrary Merkle {\n error InvalidProofLength();\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function verifyInclusionSha256(\n bytes memory proof,\n bytes32 root,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bool) {\n return processInclusionProofSha256(proof, leaf, index) == root;\n }\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * _Available since v4.4._\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function processInclusionProofSha256(\n bytes memory proof,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bytes32) {\n require(\n proof.length != 0 && proof.length % 32 == 0,\n InvalidProofLength()\n );\n bytes32[1] memory computedHash = [leaf];\n for (uint256 i = 32; i <= proof.length; i += 32) {\n if (index % 2 == 0) {\n // if ith bit of index is 0, then computedHash is a left sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(computedHash))\n mstore(0x20, mload(add(proof, i)))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n index := div(index, 2)\n }\n } else {\n // if ith bit of index is 1, then computedHash is a right sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(add(proof, i)))\n mstore(0x20, mload(computedHash))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n index := div(index, 2)\n }\n }\n }\n return computedHash[0];\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/bridges/OmnichainL2Adapter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.0;\n\nimport { MintBurnOFTAdapter } from \"@layerzerolabs/oft-evm/contracts/MintBurnOFTAdapter.sol\";\nimport { IMintableBurnable } from \"@layerzerolabs/oft-evm/contracts/interfaces/IMintableBurnable.sol\";\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\n\n/// NOTE: It's necessary to inherit from Ownable instead of Governable\n/// because OFTCore uses Ownable to manage the governor.\n/// Ownable uses slot 0 for storing the address, whereas Goveranble\n/// stores it in a computed slot.\n\n/// @notice Omnichain uses a deployed ERC-20 token and safeERC20\n/// to interact with the OFTCore contract.\n/// On L2, we follow the mint and burn mechanism.\n/// The adapter should have minter and burner roles.\n\n/// @title Omnichain L2 Adapter\ncontract OmnichainL2Adapter is MintBurnOFTAdapter {\n constructor(\n address _token,\n address _lzEndpoint,\n address _governor\n )\n MintBurnOFTAdapter(\n _token,\n IMintableBurnable(_token),\n _lzEndpoint,\n _governor\n )\n Ownable()\n {\n _transferOwnership(_governor);\n }\n\n /// @inheritdoc MintBurnOFTAdapter\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n )\n internal\n virtual\n override\n returns (uint256 amountSentLD, uint256 amountReceivedLD)\n {\n (amountSentLD, amountReceivedLD) = _debitView(\n _amountLD,\n _minAmountLD,\n _dstEid\n );\n // Burns tokens from the caller.\n IMintableERC20(address(minterBurner)).burn(_from, amountSentLD);\n }\n\n /// @inheritdoc MintBurnOFTAdapter\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /* _srcEid */\n ) internal virtual override returns (uint256 amountReceivedLD) {\n if (_to == address(0x0)) _to = address(0xdead); // _mint(...) does not support address(0x0)\n // Mints the tokens and transfers to the recipient.\n IMintableERC20(address(minterBurner)).mint(_to, _amountLD);\n // In the case of NON-default OFTAdapter, the amountLD MIGHT not be equal to amountReceivedLD.\n return _amountLD;\n }\n}\n\ninterface IMintableERC20 {\n function mint(address to, uint256 value) external;\n\n function burn(address to, uint256 value) external;\n}\n" + }, + "contracts/bridges/OmnichainMainnetAdapter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.0;\n\nimport { OFTAdapter } from \"@layerzerolabs/oft-evm/contracts/OFTAdapter.sol\";\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\n\n/// NOTE: It's necessary to inherit from Ownable instead of Governable\n/// because OFTCore uses Ownable to manage the governor.\n/// Ownable uses slot 0 for storing the address, whereas Goveranble\n/// stores it in a computed slot.\n\n/// @notice Omnichain uses a deployed ERC-20 token and safeERC20\n/// to interact with the OFTCore contract.\n/// On Ethereum, we follow the lock and unlock mechanism.\n\n/// @title Omnichain Mainnet Adapter\ncontract OmnichainMainnetAdapter is OFTAdapter {\n constructor(\n address _token,\n address _lzEndpoint,\n address _governor\n ) OFTAdapter(_token, _lzEndpoint, _governor) Ownable() {\n _transferOwnership(_governor);\n }\n}\n" + }, + "contracts/buyback/AbstractBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { ICVXLocker } from \"../interfaces/ICVXLocker.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\n\nabstract contract AbstractBuyback is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n event SwapRouterUpdated(address indexed _address);\n\n event RewardsSourceUpdated(address indexed _address);\n event TreasuryManagerUpdated(address indexed _address);\n event CVXShareBpsUpdated(uint256 bps);\n\n // Emitted whenever OUSD/OETH is swapped for OGN/CVX or any other token\n event OTokenBuyback(\n address indexed oToken,\n address indexed swappedFor,\n uint256 swapAmountIn,\n uint256 amountOut\n );\n\n // Address of 1-inch Swap Router\n address public swapRouter;\n\n // slither-disable-next-line constable-states\n address private __deprecated_ousd;\n // slither-disable-next-line constable-states\n address private __deprecated_ogv;\n // slither-disable-next-line constable-states\n address private __deprecated_usdt;\n // slither-disable-next-line constable-states\n address private __deprecated_weth9;\n\n // Address that receives OGN after swaps\n address public rewardsSource;\n\n // Address that receives all other tokens after swaps\n address public treasuryManager;\n\n // slither-disable-next-line constable-states\n uint256 private __deprecated_treasuryBps;\n\n address public immutable oToken;\n address public immutable ogn;\n address public immutable cvx;\n address public immutable cvxLocker;\n\n // Amount of `oToken` balance to use for OGN buyback\n uint256 public balanceForOGN;\n\n // Amount of `oToken` balance to use for CVX buyback\n uint256 public balanceForCVX;\n\n // Percentage of `oToken` balance to be used for CVX\n uint256 public cvxShareBps; // 10000 = 100%\n\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) {\n // Make sure nobody owns the implementation contract\n _setGovernor(address(0));\n\n oToken = _oToken;\n ogn = _ogn;\n cvx = _cvx;\n cvxLocker = _cvxLocker;\n }\n\n /**\n * @param _swapRouter Address of Uniswap V3 Router\n * @param _strategistAddr Address of Strategist multi-sig wallet\n * @param _treasuryManagerAddr Address that receives the treasury's share of OUSD\n * @param _rewardsSource Address of RewardsSource contract\n * @param _cvxShareBps Percentage of balance to use for CVX\n */\n function initialize(\n address _swapRouter,\n address _strategistAddr,\n address _treasuryManagerAddr,\n address _rewardsSource,\n uint256 _cvxShareBps\n ) external onlyGovernor initializer {\n _setStrategistAddr(_strategistAddr);\n\n _setSwapRouter(_swapRouter);\n _setRewardsSource(_rewardsSource);\n\n _setTreasuryManager(_treasuryManagerAddr);\n\n _setCVXShareBps(_cvxShareBps);\n }\n\n /**\n * @dev Set address of Uniswap Universal Router for performing liquidation\n * of platform fee tokens. Setting to 0x0 will pause swaps.\n *\n * @param _router Address of the Uniswap Universal router\n */\n function setSwapRouter(address _router) external onlyGovernor {\n _setSwapRouter(_router);\n }\n\n function _setSwapRouter(address _router) internal {\n address oldRouter = swapRouter;\n swapRouter = _router;\n\n if (oldRouter != address(0)) {\n // Remove allowance of old router, if any\n\n if (IERC20(ogn).allowance(address(this), oldRouter) != 0) {\n // slither-disable-next-line unused-return\n IERC20(ogn).safeApprove(oldRouter, 0);\n }\n\n if (IERC20(cvx).allowance(address(this), oldRouter) != 0) {\n // slither-disable-next-line unused-return\n IERC20(cvx).safeApprove(oldRouter, 0);\n }\n }\n\n emit SwapRouterUpdated(_router);\n }\n\n /**\n * @dev Sets the address that receives the OGN buyback rewards\n * @param _address Address\n */\n function setRewardsSource(address _address) external onlyGovernor {\n _setRewardsSource(_address);\n }\n\n function _setRewardsSource(address _address) internal {\n require(_address != address(0), \"Address not set\");\n rewardsSource = _address;\n emit RewardsSourceUpdated(_address);\n }\n\n /**\n * @dev Sets the address that can receive and manage the funds for Treasury\n * @param _address Address\n */\n function setTreasuryManager(address _address) external onlyGovernor {\n _setTreasuryManager(_address);\n }\n\n function _setTreasuryManager(address _address) internal {\n require(_address != address(0), \"Address not set\");\n treasuryManager = _address;\n emit TreasuryManagerUpdated(_address);\n }\n\n /**\n * @dev Sets the percentage of oToken to use for Flywheel tokens\n * @param _bps BPS, 10000 to 100%\n */\n function setCVXShareBps(uint256 _bps) external onlyGovernor {\n _setCVXShareBps(_bps);\n }\n\n function _setCVXShareBps(uint256 _bps) internal {\n require(_bps <= 10000, \"Invalid bps value\");\n cvxShareBps = _bps;\n emit CVXShareBpsUpdated(_bps);\n }\n\n /**\n * @dev Computes the split of oToken balance that can be\n * used for OGN and CVX buybacks.\n */\n function _updateBuybackSplits()\n internal\n returns (uint256 _balanceForOGN, uint256 _balanceForCVX)\n {\n _balanceForOGN = balanceForOGN;\n _balanceForCVX = balanceForCVX;\n\n uint256 totalBalance = IERC20(oToken).balanceOf(address(this));\n uint256 unsplitBalance = totalBalance - _balanceForOGN - _balanceForCVX;\n\n // Check if all balance is accounted for\n if (unsplitBalance != 0) {\n // If not, split unaccounted balance based on `cvxShareBps`\n uint256 addToCVX = (unsplitBalance * cvxShareBps) / 10000;\n _balanceForCVX = _balanceForCVX + addToCVX;\n _balanceForOGN = _balanceForOGN + unsplitBalance - addToCVX;\n\n // Update storage\n balanceForOGN = _balanceForOGN;\n balanceForCVX = _balanceForCVX;\n }\n }\n\n function updateBuybackSplits() external onlyGovernor {\n // slither-disable-next-line unused-return\n _updateBuybackSplits();\n }\n\n function _swapToken(\n address tokenOut,\n uint256 oTokenAmount,\n uint256 minAmountOut,\n bytes calldata swapData\n ) internal returns (uint256 amountOut) {\n require(oTokenAmount > 0, \"Invalid Swap Amount\");\n require(swapRouter != address(0), \"Swap Router not set\");\n require(minAmountOut > 0, \"Invalid minAmount\");\n\n // Transfer OToken to Swapper for swapping\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(oToken).transfer(swapRouter, oTokenAmount);\n\n // Swap\n amountOut = ISwapper(swapRouter).swap(\n oToken,\n tokenOut,\n oTokenAmount,\n minAmountOut,\n swapData\n );\n\n require(amountOut >= minAmountOut, \"Higher Slippage\");\n\n emit OTokenBuyback(oToken, tokenOut, oTokenAmount, amountOut);\n }\n\n /**\n * @dev Swaps `oTokenAmount` to OGN\n * @param oTokenAmount Amount of OUSD/OETH to swap\n * @param minOGN Minimum OGN to receive for oTokenAmount\n * @param swapData 1inch Swap Data\n */\n function swapForOGN(\n uint256 oTokenAmount,\n uint256 minOGN,\n bytes calldata swapData\n ) external onlyGovernorOrStrategist nonReentrant {\n (uint256 _amountForOGN, ) = _updateBuybackSplits();\n require(_amountForOGN >= oTokenAmount, \"Balance underflow\");\n require(rewardsSource != address(0), \"RewardsSource contract not set\");\n\n unchecked {\n // Subtract the amount to swap from net balance\n balanceForOGN = _amountForOGN - oTokenAmount;\n }\n\n uint256 ognReceived = _swapToken(ogn, oTokenAmount, minOGN, swapData);\n\n // Transfer OGN received to RewardsSource contract\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(ogn).transfer(rewardsSource, ognReceived);\n }\n\n /**\n * @dev Swaps `oTokenAmount` to CVX\n * @param oTokenAmount Amount of OUSD/OETH to swap\n * @param minCVX Minimum CVX to receive for oTokenAmount\n * @param swapData 1inch Swap Data\n */\n function swapForCVX(\n uint256 oTokenAmount,\n uint256 minCVX,\n bytes calldata swapData\n ) external onlyGovernorOrStrategist nonReentrant {\n (, uint256 _amountForCVX) = _updateBuybackSplits();\n require(_amountForCVX >= oTokenAmount, \"Balance underflow\");\n\n unchecked {\n // Subtract the amount to swap from net balance\n balanceForCVX = _amountForCVX - oTokenAmount;\n }\n\n uint256 cvxReceived = _swapToken(cvx, oTokenAmount, minCVX, swapData);\n\n // Lock all CVX\n _lockAllCVX(cvxReceived);\n }\n\n /**\n * @dev Locks all CVX held by the contract on behalf of the Treasury Manager\n */\n function lockAllCVX() external onlyGovernorOrStrategist {\n _lockAllCVX(IERC20(cvx).balanceOf(address(this)));\n }\n\n function _lockAllCVX(uint256 cvxAmount) internal {\n require(\n treasuryManager != address(0),\n \"Treasury manager address not set\"\n );\n\n // Lock all available CVX on behalf of `treasuryManager`\n ICVXLocker(cvxLocker).lock(treasuryManager, cvxAmount, 0);\n }\n\n /**\n * @dev Approve CVX Locker to move CVX held by this contract\n */\n function safeApproveAllTokens() external onlyGovernorOrStrategist {\n IERC20(cvx).safeApprove(cvxLocker, type(uint256).max);\n }\n\n /**\n * @notice Owner function to withdraw a specific amount of a token\n * @param token token to be transferered\n * @param amount amount of the token to be transferred\n */\n function transferToken(address token, uint256 amount)\n external\n onlyGovernor\n nonReentrant\n {\n IERC20(token).safeTransfer(_governor(), amount);\n }\n}\n" + }, + "contracts/buyback/ARMBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract ARMBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/buyback/OETHBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract OETHBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/buyback/OUSDBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract OUSDBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/echidna/Debugger.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary Debugger {\n event Debug(string debugString);\n event Debug(string description, string data);\n event Debug(string prefix, string description, string data);\n event Debug(string description, bytes32 data);\n event Debug(string prefix, string description, bytes32 data);\n event Debug(string description, uint256 data);\n event Debug(string prefix, string description, uint256 data);\n event Debug(string description, int256 data);\n event Debug(string prefix, string description, int256 data);\n event Debug(string description, address data);\n event Debug(string prefix, string description, address data);\n event Debug(string description, bool data);\n event Debug(string prefix, string description, bool data);\n\n function log(string memory debugString) internal {\n emit Debug(debugString);\n }\n\n function log(string memory description, string memory data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n string memory data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, bytes32 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n bytes32 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, uint256 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n uint256 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, int256 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n int256 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, address data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n address data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, bool data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n bool data\n ) internal {\n emit Debug(prefix, description, data);\n }\n}\n" + }, + "contracts/echidna/Echidna.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaTestApproval.sol\";\n\n/**\n * @title Echidna test contract for OUSD\n * @notice Target contract to be tested, containing all mixins\n * @author Rappie\n */\ncontract Echidna is EchidnaTestApproval {\n\n}\n" + }, + "contracts/echidna/EchidnaConfig.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Top-level mixin for configuring the desired fuzzing setup\n * @author Rappie\n */\ncontract EchidnaConfig {\n address internal constant ADDRESS_VAULT = address(0x10000);\n address internal constant ADDRESS_OUTSIDER_USER = address(0x20000);\n\n address internal constant ADDRESS_USER0 = address(0x30000);\n address internal constant ADDRESS_USER1 = address(0x40000);\n\n // Will be set in EchidnaSetup constructor\n address internal ADDRESS_OUTSIDER_CONTRACT;\n address internal ADDRESS_CONTRACT0;\n address internal ADDRESS_CONTRACT1;\n\n // Toggle known issues\n //\n // This can be used to skip tests that are known to fail. This is useful\n // when debugging a specific issue, but should be disabled when running\n // the full test suite.\n //\n // True => skip tests that are known to fail\n // False => run all tests\n //\n bool internal constant TOGGLE_KNOWN_ISSUES = false;\n\n // Toggle known issues within limits\n //\n // Same as TOGGLE_KNOWN_ISSUES, but also skip tests that are known to fail\n // within limits set by the variables below.\n //\n bool internal constant TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS = true;\n\n // Starting balance\n //\n // Gives OUSD a non-zero starting supply, which can be useful to ignore\n // certain edge cases.\n //\n // The starting balance is given to outsider accounts that are not used as\n // accounts while fuzzing.\n //\n bool internal constant TOGGLE_STARTING_BALANCE = true;\n uint256 internal constant STARTING_BALANCE = 1_000_000e18;\n\n // Change supply\n //\n // Set a limit to the amount of change per rebase, which can be useful to\n // ignore certain edge cases.\n //\n // True => limit the amount of change to a percentage of total supply\n // False => no limit\n //\n bool internal constant TOGGLE_CHANGESUPPLY_LIMIT = true;\n uint256 internal constant CHANGESUPPLY_DIVISOR = 10; // 10% of total supply\n\n // Mint limit\n //\n // Set a limit the amount minted per mint, which can be useful to\n // ignore certain edge cases.\n //\n // True => limit the amount of minted tokens\n // False => no limit\n //\n bool internal constant TOGGLE_MINT_LIMIT = true;\n uint256 internal constant MINT_MODULO = 1_000_000_000_000e18;\n\n // Known rounding errors\n uint256 internal constant TRANSFER_ROUNDING_ERROR = 1e18 - 1;\n uint256 internal constant OPT_IN_ROUNDING_ERROR = 1e18 - 1;\n uint256 internal constant MINT_ROUNDING_ERROR = 1e18 - 1;\n\n /**\n * @notice Modifier to skip tests that are known to fail\n * @dev see TOGGLE_KNOWN_ISSUES for more information\n */\n modifier hasKnownIssue() {\n if (TOGGLE_KNOWN_ISSUES) return;\n _;\n }\n\n /**\n * @notice Modifier to skip tests that are known to fail within limits\n * @dev see TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS for more information\n */\n modifier hasKnownIssueWithinLimits() {\n if (TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS) return;\n _;\n }\n\n /**\n * @notice Translate an account ID to an address\n * @param accountId The ID of the account\n * @return account The address of the account\n */\n function getAccount(uint8 accountId)\n internal\n view\n returns (address account)\n {\n accountId = accountId / 64;\n if (accountId == 0) return account = ADDRESS_USER0;\n if (accountId == 1) return account = ADDRESS_USER1;\n if (accountId == 2) return account = ADDRESS_CONTRACT0;\n if (accountId == 3) return account = ADDRESS_CONTRACT1;\n require(false, \"Unknown account ID\");\n }\n}\n" + }, + "contracts/echidna/EchidnaDebug.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport \"./EchidnaHelper.sol\";\nimport \"./Debugger.sol\";\n\nimport \"../token/OUSD.sol\";\n\n/**\n * @title Room for random debugging functions\n * @author Rappie\n */\ncontract EchidnaDebug is EchidnaHelper {\n function debugOUSD() public pure {\n // assert(ousd.balanceOf(ADDRESS_USER0) == 1000);\n // assert(ousd.rebaseState(ADDRESS_USER0) != OUSD.RebaseOptions.OptIn);\n // assert(Address.isContract(ADDRESS_CONTRACT0));\n // Debugger.log(\"nonRebasingSupply\", ousd.nonRebasingSupply());\n // assert(false);\n }\n}\n" + }, + "contracts/echidna/EchidnaHelper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaSetup.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin containing helper functions\n * @author Rappie\n */\ncontract EchidnaHelper is EchidnaSetup {\n /**\n * @notice Mint tokens to an account\n * @param toAcc Account to mint to\n * @param amount Amount to mint\n * @return Amount minted (in case of capped mint with modulo)\n */\n function mint(uint8 toAcc, uint256 amount) public returns (uint256) {\n address to = getAccount(toAcc);\n\n if (TOGGLE_MINT_LIMIT) {\n amount = amount % MINT_MODULO;\n }\n\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(to, amount);\n\n return amount;\n }\n\n /**\n * @notice Burn tokens from an account\n * @param fromAcc Account to burn from\n * @param amount Amount to burn\n */\n function burn(uint8 fromAcc, uint256 amount) public {\n address from = getAccount(fromAcc);\n hevm.prank(ADDRESS_VAULT);\n ousd.burn(from, amount);\n }\n\n /**\n * @notice Change the total supply of OUSD (rebase)\n * @param amount New total supply\n */\n function changeSupply(uint256 amount) public {\n if (TOGGLE_CHANGESUPPLY_LIMIT) {\n amount =\n ousd.totalSupply() +\n (amount % (ousd.totalSupply() / CHANGESUPPLY_DIVISOR));\n }\n\n hevm.prank(ADDRESS_VAULT);\n ousd.changeSupply(amount);\n }\n\n /**\n * @notice Transfer tokens between accounts\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function transfer(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n ousd.transfer(to, amount);\n }\n\n /**\n * @notice Transfer approved tokens between accounts\n * @param authorizedAcc Account that is authorized to transfer\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function transferFrom(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n ousd.transferFrom(from, to, amount);\n }\n\n /**\n * @notice Opt in to rebasing\n * @param targetAcc Account to opt in\n */\n function optIn(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n hevm.prank(target);\n ousd.rebaseOptIn();\n }\n\n /**\n * @notice Opt out of rebasing\n * @param targetAcc Account to opt out\n */\n function optOut(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n hevm.prank(target);\n ousd.rebaseOptOut();\n }\n\n /**\n * @notice Approve an account to spend OUSD\n * @param ownerAcc Account that owns the OUSD\n * @param spenderAcc Account that is approved to spend the OUSD\n * @param amount Amount to approve\n */\n function approve(\n uint8 ownerAcc,\n uint8 spenderAcc,\n uint256 amount\n ) public {\n address owner = getAccount(ownerAcc);\n address spender = getAccount(spenderAcc);\n hevm.prank(owner);\n // slither-disable-next-line unused-return\n ousd.approve(spender, amount);\n }\n\n /**\n * @notice Get the sum of all OUSD balances\n * @return total Total balance\n */\n function getTotalBalance() public view returns (uint256 total) {\n total += ousd.balanceOf(ADDRESS_VAULT);\n total += ousd.balanceOf(ADDRESS_OUTSIDER_USER);\n total += ousd.balanceOf(ADDRESS_OUTSIDER_CONTRACT);\n total += ousd.balanceOf(ADDRESS_USER0);\n total += ousd.balanceOf(ADDRESS_USER1);\n total += ousd.balanceOf(ADDRESS_CONTRACT0);\n total += ousd.balanceOf(ADDRESS_CONTRACT1);\n }\n\n /**\n * @notice Get the sum of all non-rebasing OUSD balances\n * @return total Total balance\n */\n function getTotalNonRebasingBalance() public returns (uint256 total) {\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_VAULT)\n ? ousd.balanceOf(ADDRESS_VAULT)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_OUTSIDER_USER)\n ? ousd.balanceOf(ADDRESS_OUTSIDER_USER)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_OUTSIDER_CONTRACT)\n ? ousd.balanceOf(ADDRESS_OUTSIDER_CONTRACT)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_USER0)\n ? ousd.balanceOf(ADDRESS_USER0)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_USER1)\n ? ousd.balanceOf(ADDRESS_USER1)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_CONTRACT0)\n ? ousd.balanceOf(ADDRESS_CONTRACT0)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_CONTRACT1)\n ? ousd.balanceOf(ADDRESS_CONTRACT1)\n : 0;\n }\n}\n" + }, + "contracts/echidna/EchidnaSetup.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./IHevm.sol\";\nimport \"./EchidnaConfig.sol\";\nimport \"./OUSDEchidna.sol\";\n\ncontract Dummy {}\n\n/**\n * @title Mixin for setup and deployment\n * @author Rappie\n */\ncontract EchidnaSetup is EchidnaConfig {\n IHevm hevm = IHevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);\n OUSDEchidna ousd = new OUSDEchidna();\n\n /**\n * @notice Deploy the OUSD contract and set up initial state\n */\n constructor() {\n ousd.initialize(ADDRESS_VAULT, 1e18);\n\n // Deploy dummny contracts as users\n Dummy outsider = new Dummy();\n ADDRESS_OUTSIDER_CONTRACT = address(outsider);\n Dummy dummy0 = new Dummy();\n ADDRESS_CONTRACT0 = address(dummy0);\n Dummy dummy1 = new Dummy();\n ADDRESS_CONTRACT1 = address(dummy1);\n\n // Start out with a reasonable amount of OUSD\n if (TOGGLE_STARTING_BALANCE) {\n // Rebasing tokens\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(ADDRESS_OUTSIDER_USER, STARTING_BALANCE / 2);\n\n // Non-rebasing tokens\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(ADDRESS_OUTSIDER_CONTRACT, STARTING_BALANCE / 2);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestAccounting.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestSupply.sol\";\n\n/**\n * @title Mixin for testing accounting functions\n * @author Rappie\n */\ncontract EchidnaTestAccounting is EchidnaTestSupply {\n /**\n * @notice After opting in, balance should not increase. (Ok to lose rounding funds doing this)\n * @param targetAcc Account to opt in\n */\n function testOptInBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optIn(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter <= balanceBefore);\n }\n\n /**\n * @notice After opting out, balance should remain the same\n * @param targetAcc Account to opt out\n */\n function testOptOutBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optOut(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Account balance should remain the same after opting in minus rounding error\n * @param targetAcc Account to opt in\n */\n function testOptInBalanceRounding(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optIn(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n int256 delta = int256(balanceAfter) - int256(balanceBefore);\n Debugger.log(\"delta\", delta);\n\n // slither-disable-next-line tautology\n assert(-1 * delta >= 0);\n assert(-1 * delta <= int256(OPT_IN_ROUNDING_ERROR));\n }\n\n /**\n * @notice After opting in, total supply should remain the same\n * @param targetAcc Account to opt in\n */\n function testOptInTotalSupply(uint8 targetAcc) public {\n uint256 totalSupplyBefore = ousd.totalSupply();\n optIn(targetAcc);\n uint256 totalSupplyAfter = ousd.totalSupply();\n\n assert(totalSupplyAfter == totalSupplyBefore);\n }\n\n /**\n * @notice After opting out, total supply should remain the same\n * @param targetAcc Account to opt out\n */\n function testOptOutTotalSupply(uint8 targetAcc) public {\n uint256 totalSupplyBefore = ousd.totalSupply();\n optOut(targetAcc);\n uint256 totalSupplyAfter = ousd.totalSupply();\n\n assert(totalSupplyAfter == totalSupplyBefore);\n }\n\n /**\n * @notice Account balance should remain the same when a smart contract auto converts\n * @param targetAcc Account to auto convert\n */\n function testAutoConvertBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n // slither-disable-next-line unused-return\n ousd._isNonRebasingAccountEchidna(target);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice The `balanceOf` function should never revert\n * @param targetAcc Account to check balance of\n */\n function testBalanceOfShouldNotRevert(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n // slither-disable-next-line unused-return\n try ousd.balanceOf(target) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestApproval.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaTestMintBurn.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin for testing approval related functions\n * @author Rappie\n */\ncontract EchidnaTestApproval is EchidnaTestMintBurn {\n /**\n * @notice Performing `transferFrom` with an amount inside the allowance should not revert\n * @param authorizedAcc The account that is authorized to transfer\n * @param fromAcc The account that is transferring\n * @param toAcc The account that is receiving\n * @param amount The amount to transfer\n */\n function testTransferFromShouldNotRevert(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount <= ousd.balanceOf(from));\n require(amount <= ousd.allowance(from, authorized));\n\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n try ousd.transferFrom(from, to, amount) {\n // pass\n } catch {\n assert(false);\n }\n }\n\n /**\n * @notice Performing `transferFrom` with an amount outside the allowance should revert\n * @param authorizedAcc The account that is authorized to transfer\n * @param fromAcc The account that is transferring\n * @param toAcc The account that is receiving\n * @param amount The amount to transfer\n */\n function testTransferFromShouldRevert(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount > 0);\n require(\n !(amount <= ousd.balanceOf(from) &&\n amount <= ousd.allowance(from, authorized))\n );\n\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n try ousd.transferFrom(from, to, amount) {\n assert(false);\n } catch {\n // pass\n }\n }\n\n /**\n * @notice Approving an amount should update the allowance and overwrite any previous allowance\n * @param ownerAcc The account that is approving\n * @param spenderAcc The account that is being approved\n * @param amount The amount to approve\n */\n function testApprove(\n uint8 ownerAcc,\n uint8 spenderAcc,\n uint256 amount\n ) public {\n address owner = getAccount(ownerAcc);\n address spender = getAccount(spenderAcc);\n\n approve(ownerAcc, spenderAcc, amount);\n uint256 allowanceAfter1 = ousd.allowance(owner, spender);\n\n assert(allowanceAfter1 == amount);\n\n approve(ownerAcc, spenderAcc, amount / 2);\n uint256 allowanceAfter2 = ousd.allowance(owner, spender);\n\n assert(allowanceAfter2 == amount / 2);\n }\n}\n" + }, + "contracts/echidna/EchidnaTestMintBurn.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestAccounting.sol\";\n\n/**\n * @title Mixin for testing Mint and Burn functions\n * @author Rappie\n */\ncontract EchidnaTestMintBurn is EchidnaTestAccounting {\n /**\n * @notice Minting 0 tokens should not affect account balance\n * @param targetAcc Account to mint to\n */\n function testMintZeroBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n mint(targetAcc, 0);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Burning 0 tokens should not affect account balance\n * @param targetAcc Account to burn from\n */\n function testBurnZeroBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n burn(targetAcc, 0);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Minting tokens must increase the account balance by at least amount\n * @param targetAcc Account to mint to\n * @param amount Amount to mint\n * @custom:error testMintBalance(uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * testMintBalance(0,1)\n * Event sequence:\n * Debug(«balanceBefore», 0)\n * Debug(«balanceAfter», 0)\n */\n function testMintBalance(uint8 targetAcc, uint256 amount)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n uint256 amountMinted = mint(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n Debugger.log(\"amountMinted\", amountMinted);\n Debugger.log(\"balanceBefore\", balanceBefore);\n Debugger.log(\"balanceAfter\", balanceAfter);\n\n assert(balanceAfter >= balanceBefore + amountMinted);\n }\n\n /**\n * @notice Burning tokens must decrease the account balance by at least amount\n * @param targetAcc Account to burn from\n * @param amount Amount to burn\n * @custom:error testBurnBalance(uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * mint(0,3)\n * testBurnBalance(0,1)\n * Event sequence:\n * Debug(«balanceBefore», 2)\n * Debug(«balanceAfter», 2)\n */\n function testBurnBalance(uint8 targetAcc, uint256 amount)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n burn(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n Debugger.log(\"balanceBefore\", balanceBefore);\n Debugger.log(\"balanceAfter\", balanceAfter);\n\n assert(balanceAfter <= balanceBefore - amount);\n }\n\n /**\n * @notice Minting tokens should not increase the account balance by less than rounding error above amount\n * @param targetAcc Account to mint to\n * @param amount Amount to mint\n */\n function testMintBalanceRounding(uint8 targetAcc, uint256 amount) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n uint256 amountMinted = mint(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n int256 delta = int256(balanceAfter) - int256(balanceBefore);\n\n // delta == amount, if no error\n // delta < amount, if too little is minted\n // delta > amount, if too much is minted\n int256 error = int256(amountMinted) - delta;\n\n assert(error >= 0);\n assert(error <= int256(MINT_ROUNDING_ERROR));\n }\n\n /**\n * @notice A burn of an account balance must result in a zero balance\n * @param targetAcc Account to burn from\n */\n function testBurnAllBalanceToZero(uint8 targetAcc) public hasKnownIssue {\n address target = getAccount(targetAcc);\n\n burn(targetAcc, ousd.balanceOf(target));\n assert(ousd.balanceOf(target) == 0);\n }\n\n /**\n * @notice You should always be able to burn an account's balance\n * @param targetAcc Account to burn from\n */\n function testBurnAllBalanceShouldNotRevert(uint8 targetAcc)\n public\n hasKnownIssue\n {\n address target = getAccount(targetAcc);\n uint256 balance = ousd.balanceOf(target);\n\n hevm.prank(ADDRESS_VAULT);\n try ousd.burn(target, balance) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestSupply.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestTransfer.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n/**\n * @title Mixin for testing supply related functions\n * @author Rappie\n */\ncontract EchidnaTestSupply is EchidnaTestTransfer {\n using StableMath for uint256;\n\n uint256 prevRebasingCreditsPerToken = type(uint256).max;\n\n /**\n * @notice After a `changeSupply`, the total supply should exactly\n * match the target total supply. (This is needed to ensure successive\n * rebases are correct).\n * @param supply New total supply\n * @custom:error testChangeSupply(uint256): failed!💥\n * Call sequence:\n * testChangeSupply(1044505275072865171609)\n * Event sequence:\n * TotalSupplyUpdatedHighres(1044505275072865171610, 1000000000000000000000000, 957391048054055578595)\n */\n function testChangeSupply(uint256 supply)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n hevm.prank(ADDRESS_VAULT);\n ousd.changeSupply(supply);\n\n assert(ousd.totalSupply() == supply);\n }\n\n /**\n * @notice The total supply must not be less than the sum of account balances.\n * (The difference will go into future rebases)\n * @custom:error testTotalSupplyLessThanTotalBalance(): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(1)\n * optOut(64)\n * transfer(0,64,1)\n * testTotalSupplyLessThanTotalBalance()\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000001000001)\n * Debug(«totalBalance», 1000000000000000001000002)\n */\n function testTotalSupplyLessThanTotalBalance()\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n uint256 totalSupply = ousd.totalSupply();\n uint256 totalBalance = getTotalBalance();\n\n Debugger.log(\"totalSupply\", totalSupply);\n Debugger.log(\"totalBalance\", totalBalance);\n\n assert(totalSupply >= totalBalance);\n }\n\n /**\n * @notice Non-rebasing supply should not be larger than total supply\n * @custom:error testNonRebasingSupplyVsTotalSupply(): failed!💥\n * Call sequence:\n * mint(0,2)\n * changeSupply(3)\n * burn(0,1)\n * optOut(0)\n * testNonRebasingSupplyVsTotalSupply()\n */\n function testNonRebasingSupplyVsTotalSupply() public hasKnownIssue {\n uint256 nonRebasingSupply = ousd.nonRebasingSupply();\n uint256 totalSupply = ousd.totalSupply();\n\n assert(nonRebasingSupply <= totalSupply);\n }\n\n /**\n * @notice Global `rebasingCreditsPerToken` should never increase\n * @custom:error testRebasingCreditsPerTokenNotIncreased(): failed!💥\n * Call sequence:\n * testRebasingCreditsPerTokenNotIncreased()\n * changeSupply(1)\n * testRebasingCreditsPerTokenNotIncreased()\n */\n function testRebasingCreditsPerTokenNotIncreased() public hasKnownIssue {\n uint256 curRebasingCreditsPerToken = ousd\n .rebasingCreditsPerTokenHighres();\n\n Debugger.log(\n \"prevRebasingCreditsPerToken\",\n prevRebasingCreditsPerToken\n );\n Debugger.log(\"curRebasingCreditsPerToken\", curRebasingCreditsPerToken);\n\n assert(curRebasingCreditsPerToken <= prevRebasingCreditsPerToken);\n\n prevRebasingCreditsPerToken = curRebasingCreditsPerToken;\n }\n\n /**\n * @notice The rebasing credits per token ratio must greater than zero\n */\n function testRebasingCreditsPerTokenAboveZero() public {\n assert(ousd.rebasingCreditsPerTokenHighres() > 0);\n }\n\n /**\n * @notice The sum of all non-rebasing balances should not be larger than\n * non-rebasing supply\n * @custom:error testTotalNonRebasingSupplyLessThanTotalBalance(): failed!💥\n * Call sequence\n * mint(0,2)\n * changeSupply(1)\n * optOut(0)\n * burn(0,1)\n * testTotalNonRebasingSupplyLessThanTotalBalance()\n * Event sequence:\n * Debug(«totalNonRebasingSupply», 500000000000000000000001)\n * Debug(«totalNonRebasingBalance», 500000000000000000000002)\n */\n function testTotalNonRebasingSupplyLessThanTotalBalance()\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n uint256 totalNonRebasingSupply = ousd.nonRebasingSupply();\n uint256 totalNonRebasingBalance = getTotalNonRebasingBalance();\n\n Debugger.log(\"totalNonRebasingSupply\", totalNonRebasingSupply);\n Debugger.log(\"totalNonRebasingBalance\", totalNonRebasingBalance);\n\n assert(totalNonRebasingSupply >= totalNonRebasingBalance);\n }\n\n /**\n * @notice An accounts credits / credits per token should not be larger it's balance\n * @param targetAcc The account to check\n */\n function testCreditsPerTokenVsBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n (uint256 credits, uint256 creditsPerToken, ) = ousd\n .creditsBalanceOfHighres(target);\n uint256 expectedBalance = credits.divPrecisely(creditsPerToken);\n\n uint256 balance = ousd.balanceOf(target);\n\n Debugger.log(\"credits\", credits);\n Debugger.log(\"creditsPerToken\", creditsPerToken);\n Debugger.log(\"expectedBalance\", expectedBalance);\n Debugger.log(\"balance\", balance);\n\n assert(expectedBalance == balance);\n }\n}\n" + }, + "contracts/echidna/EchidnaTestTransfer.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin for testing transfer related functions\n * @author Rappie\n */\ncontract EchidnaTestTransfer is EchidnaDebug {\n /**\n * @notice The receiving account's balance after a transfer must not increase by\n * less than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferBalanceReceivedLess(uint8,uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * mint(64,2)\n * testTransferBalanceReceivedLess(64,0,1)\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000000500002)\n * Debug(«toBalBefore», 0)\n * Debug(«toBalAfter», 0)\n */\n function testTransferBalanceReceivedLess(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue hasKnownIssueWithinLimits {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n\n assert(toBalAfter >= toBalBefore + amount);\n }\n\n /**\n * @notice The receiving account's balance after a transfer must not\n * increase by more than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceReceivedMore(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n\n assert(toBalAfter <= toBalBefore + amount);\n }\n\n /**\n * @notice The sending account's balance after a transfer must not\n * decrease by less than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferBalanceSentLess(uint8,uint8,uint256): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(1)\n * testTransferBalanceSentLess(0,64,1)\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000000500001)\n * Debug(«fromBalBefore», 1)\n * Debug(«fromBalAfter», 1)\n */\n function testTransferBalanceSentLess(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue hasKnownIssueWithinLimits {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n\n assert(fromBalAfter <= fromBalBefore - amount);\n }\n\n /**\n * @notice The sending account's balance after a transfer must not\n * decrease by more than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceSentMore(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n\n assert(fromBalAfter >= fromBalBefore - amount);\n }\n\n /**\n * @notice The receiving account's balance after a transfer must not\n * increase by less than the amount transferred (minus rounding error)\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceReceivedLessRounding(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n int256 toDelta = int256(toBalAfter) - int256(toBalBefore);\n\n // delta == amount, if no error\n // delta < amount, if too little is sent\n // delta > amount, if too much is sent\n int256 error = int256(amount) - toDelta;\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n Debugger.log(\"toDelta\", toDelta);\n Debugger.log(\"error\", error);\n\n assert(error >= 0);\n assert(error <= int256(TRANSFER_ROUNDING_ERROR));\n }\n\n /**\n * @notice The sending account's balance after a transfer must\n * not decrease by less than the amount transferred (minus rounding error)\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceSentLessRounding(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n int256 fromDelta = int256(fromBalAfter) - int256(fromBalBefore);\n\n // delta == -amount, if no error\n // delta < -amount, if too much is sent\n // delta > -amount, if too little is sent\n int256 error = int256(amount) + fromDelta;\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n Debugger.log(\"fromDelta\", fromDelta);\n Debugger.log(\"error\", error);\n\n assert(error >= 0);\n assert(error <= int256(TRANSFER_ROUNDING_ERROR));\n }\n\n /**\n * @notice An account should always be able to successfully transfer\n * an amount within its balance.\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferWithinBalanceDoesNotRevert(uint8,uint8,uint8): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(3)\n * optOut(0)\n * testTransferWithinBalanceDoesNotRevert(0,128,2)\n * optIn(0)\n * testTransferWithinBalanceDoesNotRevert(128,0,1)\n * Event sequence:\n * error Revert Panic(17): SafeMath over-/under-flows\n */\n function testTransferWithinBalanceDoesNotRevert(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount > 0);\n amount = amount % ousd.balanceOf(from);\n\n Debugger.log(\"Total supply\", ousd.totalSupply());\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(to, amount) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n\n /**\n * @notice An account should never be able to successfully transfer\n * an amount greater than their balance.\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferExceedingBalanceReverts(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n amount = ousd.balanceOf(from) + 1 + amount;\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(to, amount) {\n assert(false);\n } catch {\n assert(true);\n }\n }\n\n /**\n * @notice A transfer to the same account should not change that account's balance\n * @param targetAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferSelf(uint8 targetAcc, uint256 amount) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n transfer(targetAcc, targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceBefore == balanceAfter);\n }\n\n /**\n * @notice Transfers to the zero account revert\n * @param fromAcc Account to transfer from\n * @param amount Amount to transfer\n */\n function testTransferToZeroAddress(uint8 fromAcc, uint256 amount) public {\n address from = getAccount(fromAcc);\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(address(0), amount) {\n assert(false);\n } catch {\n assert(true);\n }\n }\n}\n" + }, + "contracts/echidna/IHevm.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// https://github.com/ethereum/hevm/blob/main/doc/src/controlling-the-unit-testing-environment.md#cheat-codes\n\ninterface IHevm {\n function warp(uint256 x) external;\n\n function roll(uint256 x) external;\n\n function store(\n address c,\n bytes32 loc,\n bytes32 val\n ) external;\n\n function load(address c, bytes32 loc) external returns (bytes32 val);\n\n function sign(uint256 sk, bytes32 digest)\n external\n returns (\n uint8 v,\n bytes32 r,\n bytes32 s\n );\n\n function addr(uint256 sk) external returns (address addr);\n\n function ffi(string[] calldata) external returns (bytes memory);\n\n function prank(address sender) external;\n}\n" + }, + "contracts/echidna/OUSDEchidna.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../token/OUSD.sol\";\n\ncontract OUSDEchidna is OUSD {\n constructor() OUSD() {}\n\n function _isNonRebasingAccountEchidna(address _account)\n public\n returns (bool)\n {\n _autoMigrate(_account);\n return alternativeCreditsPerToken[_account] > 0;\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/governance/InitializableGovernable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD InitializableGovernable Contract\n * @author Origin Protocol Inc\n */\nimport { Initializable } from \"../utils/Initializable.sol\";\n\nimport { Governable } from \"./Governable.sol\";\n\ncontract InitializableGovernable is Governable, Initializable {\n function _initialize(address _newGovernor) internal {\n _changeGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/governance/Strategizable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"./Governable.sol\";\n\ncontract Strategizable is Governable {\n event StrategistUpdated(address _address);\n\n // Address of strategist\n address public strategistAddr;\n\n // For future use\n uint256[50] private __gap;\n\n /**\n * @dev Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @dev Set address of Strategist\n * @param _address Address of Strategist\n */\n function setStrategistAddr(address _address) external onlyGovernor {\n _setStrategistAddr(_address);\n }\n\n /**\n * @dev Set address of Strategist\n * @param _address Address of Strategist\n */\n function _setStrategistAddr(address _address) internal {\n strategistAddr = _address;\n emit StrategistUpdated(_address);\n }\n}\n" + }, + "contracts/harvest/AbstractHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IUniswapV2Router } from \"../interfaces/uniswap/IUniswapV2Router02.sol\";\nimport { IUniswapV3Router } from \"../interfaces/uniswap/IUniswapV3Router.sol\";\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { ICurvePool } from \"../strategies/ICurvePool.sol\";\nimport \"../utils/Helpers.sol\";\n\nabstract contract AbstractHarvester is Governable {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using StableMath for uint256;\n\n enum SwapPlatform {\n UniswapV2Compatible,\n UniswapV3,\n Balancer,\n Curve\n }\n\n event SupportedStrategyUpdate(address strategyAddress, bool isSupported);\n event RewardTokenConfigUpdated(\n address tokenAddress,\n uint16 allowedSlippageBps,\n uint16 harvestRewardBps,\n SwapPlatform swapPlatform,\n address swapPlatformAddr,\n bytes swapData,\n uint256 liquidationLimit,\n bool doSwapRewardToken\n );\n event RewardTokenSwapped(\n address indexed rewardToken,\n address indexed swappedInto,\n SwapPlatform swapPlatform,\n uint256 amountIn,\n uint256 amountOut\n );\n event RewardProceedsTransferred(\n address indexed token,\n address farmer,\n uint256 protcolYield,\n uint256 farmerFee\n );\n event RewardProceedsAddressChanged(address newProceedsAddress);\n\n error EmptyAddress();\n error InvalidSlippageBps();\n error InvalidHarvestRewardBps();\n\n error InvalidSwapPlatform(SwapPlatform swapPlatform);\n\n error InvalidUniswapV2PathLength();\n error InvalidTokenInSwapPath(address token);\n error EmptyBalancerPoolId();\n error InvalidCurvePoolAssetIndex(address token);\n\n error UnsupportedStrategy(address strategyAddress);\n\n error SlippageError(uint256 actualBalance, uint256 minExpected);\n error BalanceMismatchAfterSwap(uint256 actualBalance, uint256 minExpected);\n\n // Configuration properties for harvesting logic of reward tokens\n struct RewardTokenConfig {\n // Max allowed slippage when swapping reward token for a stablecoin denominated in basis points.\n uint16 allowedSlippageBps;\n // Reward when calling a harvest function denominated in basis points.\n uint16 harvestRewardBps;\n // Address of compatible exchange protocol (Uniswap V2/V3, SushiSwap, Balancer and Curve).\n address swapPlatformAddr;\n /* When true the reward token is being swapped. In a need of (temporarily) disabling the swapping of\n * a reward token this needs to be set to false.\n */\n bool doSwapRewardToken;\n // Platform to use for Swapping\n SwapPlatform swapPlatform;\n /* How much token can be sold per one harvest call. If the balance of rewards tokens\n * exceeds that limit multiple harvest calls are required to harvest all of the tokens.\n * Set it to MAX_INT to effectively disable the limit.\n */\n uint256 liquidationLimit;\n }\n\n mapping(address => RewardTokenConfig) public rewardTokenConfigs;\n mapping(address => bool) public supportedStrategies;\n\n address public immutable vaultAddress;\n\n /**\n * Address receiving rewards proceeds. Initially the Vault contract later will possibly\n * be replaced by another contract that eases out rewards distribution.\n **/\n address public rewardProceedsAddress;\n\n /**\n * All tokens are swapped to this token before it gets transferred\n * to the `rewardProceedsAddress`. USDT for OUSD and WETH for OETH.\n **/\n address public immutable baseTokenAddress;\n // Cached decimals for `baseTokenAddress`\n uint256 public immutable baseTokenDecimals;\n\n // Uniswap V2 path for reward tokens using Uniswap V2 Router\n mapping(address => address[]) public uniswapV2Path;\n // Uniswap V3 path for reward tokens using Uniswap V3 Router\n mapping(address => bytes) public uniswapV3Path;\n // Pool ID to use for reward tokens on Balancer\n mapping(address => bytes32) public balancerPoolId;\n\n struct CurvePoolIndices {\n // Casted into uint128 and stored in a struct to save gas\n uint128 rewardTokenIndex;\n uint128 baseTokenIndex;\n }\n // Packed indices of assets on the Curve pool\n mapping(address => CurvePoolIndices) public curvePoolIndices;\n\n constructor(address _vaultAddress, address _baseTokenAddress) {\n require(_vaultAddress != address(0));\n require(_baseTokenAddress != address(0));\n\n vaultAddress = _vaultAddress;\n baseTokenAddress = _baseTokenAddress;\n\n // Cache decimals as well\n baseTokenDecimals = Helpers.getDecimals(_baseTokenAddress);\n }\n\n /***************************************\n Configuration\n ****************************************/\n\n /**\n * Set the Address receiving rewards proceeds.\n * @param _rewardProceedsAddress Address of the reward token\n */\n function setRewardProceedsAddress(address _rewardProceedsAddress)\n external\n onlyGovernor\n {\n if (_rewardProceedsAddress == address(0)) {\n revert EmptyAddress();\n }\n\n rewardProceedsAddress = _rewardProceedsAddress;\n emit RewardProceedsAddressChanged(_rewardProceedsAddress);\n }\n\n /**\n * @dev Add/update a reward token configuration that holds harvesting config variables\n * @param _tokenAddress Address of the reward token\n * @param tokenConfig.allowedSlippageBps uint16 maximum allowed slippage denominated in basis points.\n * Example: 300 == 3% slippage\n * @param tokenConfig.harvestRewardBps uint16 amount of reward tokens the caller of the function is rewarded.\n * Example: 100 == 1%\n * @param tokenConfig.swapPlatformAddr Address Address of a UniswapV2 compatible contract to perform\n * the exchange from reward tokens to stablecoin (currently hard-coded to USDT)\n * @param tokenConfig.liquidationLimit uint256 Maximum amount of token to be sold per one swap function call.\n * When value is 0 there is no limit.\n * @param tokenConfig.doSwapRewardToken bool Disables swapping of the token when set to true,\n * does not cause it to revert though.\n * @param tokenConfig.swapPlatform SwapPlatform to use for Swapping\n * @param swapData Additional data required for swapping\n */\n function setRewardTokenConfig(\n address _tokenAddress,\n RewardTokenConfig calldata tokenConfig,\n bytes calldata swapData\n ) external onlyGovernor {\n if (tokenConfig.allowedSlippageBps > 1000) {\n revert InvalidSlippageBps();\n }\n\n if (tokenConfig.harvestRewardBps > 1000) {\n revert InvalidHarvestRewardBps();\n }\n\n address newRouterAddress = tokenConfig.swapPlatformAddr;\n if (newRouterAddress == address(0)) {\n // Swap router address should be non zero address\n revert EmptyAddress();\n }\n\n address oldRouterAddress = rewardTokenConfigs[_tokenAddress]\n .swapPlatformAddr;\n rewardTokenConfigs[_tokenAddress] = tokenConfig;\n\n // Revert if feed does not exist\n // slither-disable-next-line unused-return\n IOracle(IVault(vaultAddress).priceProvider()).price(_tokenAddress);\n\n IERC20 token = IERC20(_tokenAddress);\n // if changing token swap provider cancel existing allowance\n if (\n /* oldRouterAddress == address(0) when there is no pre-existing\n * configuration for said rewards token\n */\n oldRouterAddress != address(0) &&\n oldRouterAddress != newRouterAddress\n ) {\n token.safeApprove(oldRouterAddress, 0);\n }\n\n // Give SwapRouter infinite approval when needed\n if (oldRouterAddress != newRouterAddress) {\n token.safeApprove(newRouterAddress, 0);\n token.safeApprove(newRouterAddress, type(uint256).max);\n }\n\n SwapPlatform _platform = tokenConfig.swapPlatform;\n if (_platform == SwapPlatform.UniswapV2Compatible) {\n uniswapV2Path[_tokenAddress] = _decodeUniswapV2Path(\n swapData,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.UniswapV3) {\n uniswapV3Path[_tokenAddress] = _decodeUniswapV3Path(\n swapData,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.Balancer) {\n balancerPoolId[_tokenAddress] = _decodeBalancerPoolId(\n swapData,\n newRouterAddress,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.Curve) {\n curvePoolIndices[_tokenAddress] = _decodeCurvePoolIndices(\n swapData,\n newRouterAddress,\n _tokenAddress\n );\n } else {\n // Note: This code is unreachable since Solidity reverts when\n // the value is outside the range of defined values of the enum\n // (even if it's under the max length of the base type)\n revert InvalidSwapPlatform(_platform);\n }\n\n emit RewardTokenConfigUpdated(\n _tokenAddress,\n tokenConfig.allowedSlippageBps,\n tokenConfig.harvestRewardBps,\n _platform,\n newRouterAddress,\n swapData,\n tokenConfig.liquidationLimit,\n tokenConfig.doSwapRewardToken\n );\n }\n\n /**\n * @dev Decodes the data passed into Uniswap V2 path and validates\n * it to make sure the path is for `token` to `baseToken`\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param token The address of the reward token\n * @return path The validated Uniswap V2 path\n */\n function _decodeUniswapV2Path(bytes calldata data, address token)\n internal\n view\n returns (address[] memory path)\n {\n (path) = abi.decode(data, (address[]));\n uint256 len = path.length;\n\n if (len < 2) {\n // Path should have at least two tokens\n revert InvalidUniswapV2PathLength();\n }\n\n // Do some validation\n if (path[0] != token) {\n revert InvalidTokenInSwapPath(path[0]);\n }\n\n if (path[len - 1] != baseTokenAddress) {\n revert InvalidTokenInSwapPath(path[len - 1]);\n }\n }\n\n /**\n * @dev Decodes the data passed into Uniswap V3 path and validates\n * it to make sure the path is for `token` to `baseToken`\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param token The address of the reward token\n * @return path The validated Uniswap V3 path\n */\n function _decodeUniswapV3Path(bytes calldata data, address token)\n internal\n view\n returns (bytes calldata path)\n {\n path = data;\n\n address decodedAddress = address(uint160(bytes20(data[0:20])));\n\n if (decodedAddress != token) {\n // Invalid Reward Token in swap path\n revert InvalidTokenInSwapPath(decodedAddress);\n }\n\n decodedAddress = address(uint160(bytes20(data[path.length - 20:])));\n if (decodedAddress != baseTokenAddress) {\n // Invalid Base Token in swap path\n revert InvalidTokenInSwapPath(decodedAddress);\n }\n }\n\n /**\n * @dev Decodes the data passed to Balancer Pool ID\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @return poolId The pool ID\n */\n function _decodeBalancerPoolId(\n bytes calldata data,\n address balancerVault,\n address token\n ) internal view returns (bytes32 poolId) {\n (poolId) = abi.decode(data, (bytes32));\n\n if (poolId == bytes32(0)) {\n revert EmptyBalancerPoolId();\n }\n\n IBalancerVault bVault = IBalancerVault(balancerVault);\n\n // Note: this reverts if token is not a pool asset\n // slither-disable-next-line unused-return\n bVault.getPoolTokenInfo(poolId, token);\n\n // slither-disable-next-line unused-return\n bVault.getPoolTokenInfo(poolId, baseTokenAddress);\n }\n\n /**\n * @dev Decodes the data passed to get the pool indices and\n * checks it against the Curve Pool to make sure it's\n * not misconfigured. The indices are packed into a single\n * uint256 for gas savings\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param poolAddress Curve pool address\n * @param token The address of the reward token\n * @return indices Packed pool asset indices\n */\n function _decodeCurvePoolIndices(\n bytes calldata data,\n address poolAddress,\n address token\n ) internal view returns (CurvePoolIndices memory indices) {\n indices = abi.decode(data, (CurvePoolIndices));\n\n ICurvePool pool = ICurvePool(poolAddress);\n if (token != pool.coins(indices.rewardTokenIndex)) {\n revert InvalidCurvePoolAssetIndex(token);\n }\n if (baseTokenAddress != pool.coins(indices.baseTokenIndex)) {\n revert InvalidCurvePoolAssetIndex(baseTokenAddress);\n }\n }\n\n /**\n * @dev Flags a strategy as supported or not supported one\n * @param _strategyAddress Address of the strategy\n * @param _isSupported Bool marking strategy as supported or not supported\n */\n function setSupportedStrategy(address _strategyAddress, bool _isSupported)\n external\n onlyGovernor\n {\n supportedStrategies[_strategyAddress] = _isSupported;\n emit SupportedStrategyUpdate(_strategyAddress, _isSupported);\n }\n\n /***************************************\n Rewards\n ****************************************/\n\n /**\n * @dev Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform. Can be called by anyone.\n * Rewards incentivizing the caller are sent to the caller of this function.\n * @param _strategyAddr Address of the strategy to collect rewards from\n */\n function harvestAndSwap(address _strategyAddr) external nonReentrant {\n // Remember _harvest function checks for the validity of _strategyAddr\n _harvestAndSwap(_strategyAddr, msg.sender);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform. Can be called by anyone\n * @param _strategyAddr Address of the strategy to collect rewards from\n * @param _rewardTo Address where to send a share of harvest rewards to as an incentive\n * for executing this function\n */\n function harvestAndSwap(address _strategyAddr, address _rewardTo)\n external\n nonReentrant\n {\n // Remember _harvest function checks for the validity of _strategyAddr\n _harvestAndSwap(_strategyAddr, _rewardTo);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform\n * @param _strategyAddr Address of the strategy to collect rewards from\n * @param _rewardTo Address where to send a share of harvest rewards to as an incentive\n * for executing this function\n */\n function _harvestAndSwap(address _strategyAddr, address _rewardTo)\n internal\n {\n _harvest(_strategyAddr);\n IStrategy strategy = IStrategy(_strategyAddr);\n address[] memory rewardTokens = strategy.getRewardTokenAddresses();\n IOracle priceProvider = IOracle(IVault(vaultAddress).priceProvider());\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; ++i) {\n _swap(rewardTokens[i], _rewardTo, priceProvider);\n }\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform\n * @param _strategyAddr Address of the strategy to collect rewards from.\n */\n function _harvest(address _strategyAddr) internal virtual {\n if (!supportedStrategies[_strategyAddr]) {\n revert UnsupportedStrategy(_strategyAddr);\n }\n\n IStrategy strategy = IStrategy(_strategyAddr);\n strategy.collectRewardTokens();\n }\n\n /**\n * @dev Swap a reward token for the base token on the configured\n * swap platform. The token must have a registered price feed\n * with the price provider\n * @param _swapToken Address of the token to swap\n * @param _rewardTo Address where to send the share of harvest rewards to\n * @param _priceProvider Oracle to get prices of the swap token\n */\n function _swap(\n address _swapToken,\n address _rewardTo,\n IOracle _priceProvider\n ) internal virtual {\n uint256 balance = IERC20(_swapToken).balanceOf(address(this));\n\n // No need to swap if the reward token is the base token. eg USDT or WETH.\n // There is also no limit on the transfer. Everything in the harvester will be transferred\n // to the Dripper regardless of the liquidationLimit config.\n if (_swapToken == baseTokenAddress) {\n IERC20(_swapToken).safeTransfer(rewardProceedsAddress, balance);\n // currently not paying the farmer any rewards as there is no swap\n emit RewardProceedsTransferred(\n baseTokenAddress,\n address(0),\n balance,\n 0\n );\n return;\n }\n\n RewardTokenConfig memory tokenConfig = rewardTokenConfigs[_swapToken];\n\n /* This will trigger a return when reward token configuration has not yet been set\n * or we have temporarily disabled swapping of specific reward token via setting\n * doSwapRewardToken to false.\n */\n if (!tokenConfig.doSwapRewardToken) {\n return;\n }\n\n if (balance == 0) {\n return;\n }\n\n if (tokenConfig.liquidationLimit > 0) {\n balance = Math.min(balance, tokenConfig.liquidationLimit);\n }\n\n // This'll revert if there is no price feed\n uint256 oraclePrice = _priceProvider.price(_swapToken);\n\n // Oracle price is 1e18\n uint256 minExpected = (balance *\n (1e4 - tokenConfig.allowedSlippageBps) * // max allowed slippage\n oraclePrice).scaleBy(\n baseTokenDecimals,\n Helpers.getDecimals(_swapToken)\n ) /\n 1e4 / // fix the max slippage decimal position\n 1e18; // and oracle price decimals position\n\n // Do the swap\n uint256 amountReceived = _doSwap(\n tokenConfig.swapPlatform,\n tokenConfig.swapPlatformAddr,\n _swapToken,\n balance,\n minExpected\n );\n\n if (amountReceived < minExpected) {\n revert SlippageError(amountReceived, minExpected);\n }\n\n emit RewardTokenSwapped(\n _swapToken,\n baseTokenAddress,\n tokenConfig.swapPlatform,\n balance,\n amountReceived\n );\n\n IERC20 baseToken = IERC20(baseTokenAddress);\n uint256 baseTokenBalance = baseToken.balanceOf(address(this));\n if (baseTokenBalance < amountReceived) {\n // Note: It's possible to bypass this check by transferring `baseToken`\n // directly to Harvester before calling the `harvestAndSwap`. However,\n // there's no incentive for an attacker to do that. Doing a balance diff\n // will increase the gas cost significantly\n revert BalanceMismatchAfterSwap(baseTokenBalance, amountReceived);\n }\n\n // Farmer only gets fee from the base amount they helped farm,\n // They do not get anything from anything that already was there\n // on the Harvester\n uint256 farmerFee = amountReceived.mulTruncateScale(\n tokenConfig.harvestRewardBps,\n 1e4\n );\n uint256 protocolYield = baseTokenBalance - farmerFee;\n\n baseToken.safeTransfer(rewardProceedsAddress, protocolYield);\n baseToken.safeTransfer(_rewardTo, farmerFee);\n emit RewardProceedsTransferred(\n baseTokenAddress,\n _rewardTo,\n protocolYield,\n farmerFee\n );\n }\n\n function _doSwap(\n SwapPlatform swapPlatform,\n address routerAddress,\n address rewardTokenAddress,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n if (swapPlatform == SwapPlatform.UniswapV2Compatible) {\n return\n _swapWithUniswapV2(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.UniswapV3) {\n return\n _swapWithUniswapV3(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.Balancer) {\n return\n _swapWithBalancer(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.Curve) {\n return\n _swapWithCurve(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else {\n // Should never be invoked since we catch invalid values\n // in the `setRewardTokenConfig` function before it's set\n revert InvalidSwapPlatform(swapPlatform);\n }\n }\n\n /**\n * @dev Swaps the token to `baseToken` with Uniswap V2\n *\n * @param routerAddress Uniswap V2 Router address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithUniswapV2(\n address routerAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n address[] memory path = uniswapV2Path[swapToken];\n\n uint256[] memory amounts = IUniswapV2Router(routerAddress)\n .swapExactTokensForTokens(\n amountIn,\n minAmountOut,\n path,\n address(this),\n block.timestamp\n );\n\n amountOut = amounts[amounts.length - 1];\n }\n\n /**\n * @dev Swaps the token to `baseToken` with Uniswap V3\n *\n * @param routerAddress Uniswap V3 Router address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithUniswapV3(\n address routerAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n bytes memory path = uniswapV3Path[swapToken];\n\n IUniswapV3Router.ExactInputParams memory params = IUniswapV3Router\n .ExactInputParams({\n path: path,\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: amountIn,\n amountOutMinimum: minAmountOut\n });\n amountOut = IUniswapV3Router(routerAddress).exactInput(params);\n }\n\n /**\n * @dev Swaps the token to `baseToken` on Balancer\n *\n * @param balancerVaultAddress BalancerVaultAddress\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithBalancer(\n address balancerVaultAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n bytes32 poolId = balancerPoolId[swapToken];\n\n IBalancerVault.SingleSwap memory singleSwap = IBalancerVault\n .SingleSwap({\n poolId: poolId,\n kind: IBalancerVault.SwapKind.GIVEN_IN,\n assetIn: swapToken,\n assetOut: baseTokenAddress,\n amount: amountIn,\n userData: hex\"\"\n });\n\n IBalancerVault.FundManagement memory fundMgmt = IBalancerVault\n .FundManagement({\n sender: address(this),\n fromInternalBalance: false,\n recipient: payable(address(this)),\n toInternalBalance: false\n });\n\n amountOut = IBalancerVault(balancerVaultAddress).swap(\n singleSwap,\n fundMgmt,\n minAmountOut,\n block.timestamp\n );\n }\n\n /**\n * @dev Swaps the token to `baseToken` on Curve\n *\n * @param poolAddress Curve Pool Address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithCurve(\n address poolAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n CurvePoolIndices memory indices = curvePoolIndices[swapToken];\n\n // Note: Not all CurvePools return the `amountOut`, make sure\n // to use only pool that do. Otherwise the swap would revert\n // always\n amountOut = ICurvePool(poolAddress).exchange(\n uint256(indices.rewardTokenIndex),\n uint256(indices.baseTokenIndex),\n amountIn,\n minAmountOut\n );\n }\n}\n" + }, + "contracts/harvest/Dripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\n/**\n * @title OUSD Dripper\n *\n * The dripper contract smooths out the yield from point-in-time yield events\n * and spreads the yield out over a configurable time period. This ensures a\n * continuous per block yield to makes users happy as their next rebase\n * amount is always moving up. Also, this makes historical day to day yields\n * smooth, rather than going from a near zero day, to a large APY day, then\n * back to a near zero day again.\n *\n *\n * Design notes\n * - USDT has a smaller resolution than the number of seconds\n * in a week, which can make per second payouts have a rounding error. However\n * the total effect is not large - cents per day, and this money is\n * not lost, just distributed in the future. While we could use a higher\n * decimal precision for the drip perSecond, we chose simpler code.\n * - By calculating the changing drip rates on collects only, harvests and yield\n * events don't have to call anything on this contract or pay any extra gas.\n * Collect() is already be paying for a single write, since it has to reset\n * the lastCollect time.\n * - By having a collectAndRebase method, and having our external systems call\n * that, the OUSD vault does not need any changes, not even to know the address\n * of the dripper.\n * - A rejected design was to retro-calculate the drip rate on each collect,\n * based on the balance at the time of the collect. While this would have\n * required less state, and would also have made the contract respond more quickly\n * to new income, it would break the predictability that is this contract's entire\n * purpose. If we did this, the amount of fundsAvailable() would make sharp increases\n * when funds were deposited.\n * - When the dripper recalculates the rate, it targets spending the balance over\n * the duration. This means that every time that collect is called, if no\n * new funds have been deposited the duration is being pushed back and the\n * rate decreases. This is expected, and ends up following a smoother but\n * longer curve the more collect() is called without incoming yield.\n *\n */\n\ncontract Dripper is Governable {\n using SafeERC20 for IERC20;\n\n struct Drip {\n uint64 lastCollect; // overflows 262 billion years after the sun dies\n uint192 perSecond; // drip rate per second\n }\n\n address immutable vault; // OUSD vault\n address immutable token; // token to drip out\n uint256 public dripDuration; // in seconds\n Drip public drip; // active drip parameters\n\n constructor(address _vault, address _token) {\n vault = _vault;\n token = _token;\n }\n\n /// @notice How much funds have dripped out already and are currently\n // available to be sent to the vault.\n /// @return The amount that would be sent if a collect was called\n function availableFunds() external view returns (uint256) {\n uint256 balance = IERC20(token).balanceOf(address(this));\n return _availableFunds(balance, drip);\n }\n\n /// @notice Collect all dripped funds and send to vault.\n /// Recalculate new drip rate.\n function collect() external {\n _collect();\n }\n\n /// @notice Collect all dripped funds, send to vault, recalculate new drip\n /// rate, and rebase OUSD.\n function collectAndRebase() external {\n _collect();\n IVault(vault).rebase();\n }\n\n /// @dev Change the drip duration. Governor only.\n /// @param _durationSeconds the number of seconds to drip out the entire\n /// balance over if no collects were called during that time.\n function setDripDuration(uint256 _durationSeconds)\n external\n virtual\n onlyGovernor\n {\n require(_durationSeconds > 0, \"duration must be non-zero\");\n dripDuration = _durationSeconds;\n _collect(); // duration change take immediate effect\n }\n\n /// @dev Transfer out ERC20 tokens held by the contract. Governor only.\n /// @param _asset ERC20 token address\n /// @param _amount amount to transfer\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /// @dev Calculate available funds by taking the lower of either the\n /// currently dripped out funds or the balance available.\n /// Uses passed in parameters to calculate with for gas savings.\n /// @param _balance current balance in contract\n /// @param _drip current drip parameters\n function _availableFunds(uint256 _balance, Drip memory _drip)\n internal\n view\n returns (uint256)\n {\n uint256 elapsed = block.timestamp - _drip.lastCollect;\n uint256 allowed = (elapsed * _drip.perSecond);\n return (allowed > _balance) ? _balance : allowed;\n }\n\n /// @dev Sends the currently dripped funds to be vault, and sets\n /// the new drip rate based on the new balance.\n function _collect() internal virtual {\n // Calculate send\n uint256 balance = IERC20(token).balanceOf(address(this));\n uint256 amountToSend = _availableFunds(balance, drip);\n uint256 remaining = balance - amountToSend;\n // Calculate new drip perSecond\n // Gas savings by setting entire struct at one time\n drip = Drip({\n perSecond: uint192(remaining / dripDuration),\n lastCollect: uint64(block.timestamp)\n });\n // Send funds\n IERC20(token).safeTransfer(vault, amountToSend);\n }\n\n /// @dev Transfer out all ERC20 held by the contract. Governor only.\n /// @param _asset ERC20 token address\n function transferAllToken(address _asset, address _receiver)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(\n _receiver,\n IERC20(_asset).balanceOf(address(this))\n );\n }\n}\n" + }, + "contracts/harvest/FixedRateDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Dripper } from \"./Dripper.sol\";\n\n/**\n * @title Fixed Rate Dripper\n *\n * Similar to the Dripper, Fixed Rate Dripper drips out yield per second.\n * However the Strategist decides the rate and it doesn't change after\n * a drip.\n *\n */\n\ncontract FixedRateDripper is Dripper {\n using SafeERC20 for IERC20;\n\n event DripRateUpdated(uint192 oldDripRate, uint192 newDripRate);\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vault).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n constructor(address _vault, address _token) Dripper(_vault, _token) {}\n\n /// @inheritdoc Dripper\n function setDripDuration(uint256) external virtual override {\n // Not used in FixedRateDripper\n revert(\"Drip duration disabled\");\n }\n\n /// @inheritdoc Dripper\n function _collect() internal virtual override {\n // Calculate amount to send\n uint256 balance = IERC20(token).balanceOf(address(this));\n uint256 amountToSend = _availableFunds(balance, drip);\n\n // Update timestamp\n drip.lastCollect = uint64(block.timestamp);\n\n // Send funds\n IERC20(token).safeTransfer(vault, amountToSend);\n }\n\n /**\n * @dev Sets the drip rate. Callable by Strategist or Governor.\n * Can be set to zero to stop dripper.\n * @param _perSecond Rate of WETH to drip per second\n */\n function setDripRate(uint192 _perSecond) external onlyGovernorOrStrategist {\n emit DripRateUpdated(_perSecond, drip.perSecond);\n\n /**\n * Note: It's important to call `_collect` before updating\n * the drip rate especially on a new proxy contract.\n * When `lastCollect` is not set/initialized, the elapsed\n * time would be calculated as `block.number` seconds,\n * resulting in a huge yield, if `collect` isn't called first.\n */\n // Collect at existing rate\n _collect();\n\n // Update rate\n drip.perSecond = _perSecond;\n }\n}\n" + }, + "contracts/harvest/Harvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractHarvester } from \"./AbstractHarvester.sol\";\n\ncontract Harvester is AbstractHarvester {\n constructor(address _vault, address _usdtAddress)\n AbstractHarvester(_vault, _usdtAddress)\n {}\n}\n" + }, + "contracts/harvest/OETHBaseHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { ISwapRouter } from \"../interfaces/aerodrome/ISwapRouter.sol\";\n\ncontract OETHBaseHarvester is Governable {\n using SafeERC20 for IERC20;\n\n IVault public immutable vault;\n IStrategy public immutable amoStrategy;\n IERC20 public immutable aero;\n IERC20 public immutable weth;\n ISwapRouter public immutable swapRouter;\n\n address public operatorAddr;\n\n // Similar sig to `AbstractHarvester.RewardTokenSwapped` for\n // future compatibility with monitoring\n event RewardTokenSwapped(\n address indexed rewardToken,\n address indexed swappedInto,\n uint8 swapPlatform,\n uint256 amountIn,\n uint256 amountOut\n );\n\n event OperatorChanged(address oldOperator, address newOperator);\n event YieldSent(address recipient, uint256 yield, uint256 fee);\n\n /**\n * @notice Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == vault.strategistAddr() || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @notice Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategistOrOperator() {\n require(\n msg.sender == operatorAddr ||\n msg.sender == vault.strategistAddr() ||\n isGovernor(),\n \"Caller is not the Operator or Strategist or Governor\"\n );\n _;\n }\n\n constructor(\n address _vault,\n address _amoStrategy,\n address _aero,\n address _weth,\n address _swapRouter\n ) {\n vault = IVault(_vault);\n amoStrategy = IStrategy(_amoStrategy);\n aero = IERC20(_aero);\n weth = IERC20(_weth);\n swapRouter = ISwapRouter(_swapRouter);\n }\n\n /**\n * @dev Changes the operator address which can call `harvest`\n * @param _operatorAddr New operator address\n */\n function setOperatorAddr(address _operatorAddr) external onlyGovernor {\n emit OperatorChanged(operatorAddr, _operatorAddr);\n operatorAddr = _operatorAddr;\n }\n\n /**\n * @notice Collects AERO from AMO strategy and\n * sends it to the Strategist multisig.\n * Anyone can call it.\n */\n function harvest() external onlyGovernorOrStrategistOrOperator {\n address strategistAddr = vault.strategistAddr();\n require(strategistAddr != address(0), \"Guardian address not set\");\n\n // Collect all AERO\n amoStrategy.collectRewardTokens();\n\n uint256 aeroBalance = aero.balanceOf(address(this));\n if (aeroBalance == 0) {\n // Do nothing if there's no AERO to transfer\n return;\n }\n\n // Transfer everything to Strategist\n aero.safeTransfer(strategistAddr, aeroBalance);\n }\n\n /**\n * @notice Harvests AERO from AMO strategy and then swaps some (or all)\n * of it into WETH to distribute yield and fee.\n * When `feeBps` is set to 10000 (100%), all WETH received is\n * sent to strategist.\n *\n * @param aeroToSwap Amount of AERO to swap\n * @param minWETHExpected Min. amount of WETH to expect\n * @param feeBps Performance fee bps (Sent to strategist)\n * @param sendYieldToDripper Sends yield to Dripper, if set to true.\n * Otherwise, to the Guardian\n */\n function harvestAndSwap(\n uint256 aeroToSwap,\n uint256 minWETHExpected,\n uint256 feeBps,\n bool sendYieldToDripper\n ) external onlyGovernorOrStrategist {\n address strategistAddr = vault.strategistAddr();\n require(strategistAddr != address(0), \"Guardian address not set\");\n\n // Yields can either be sent to the Dripper or Strategist\n address yieldRecipient = sendYieldToDripper\n ? vault.dripper()\n : strategistAddr;\n require(yieldRecipient != address(0), \"Yield recipient not set\");\n\n require(feeBps <= 10000, \"Invalid Fee Bps\");\n\n // Collect all AERO\n amoStrategy.collectRewardTokens();\n\n uint256 aeroBalance = aero.balanceOf(address(this));\n if (aeroBalance == 0) {\n // Do nothing if there's no AERO to transfer/swap\n return;\n }\n\n if (aeroToSwap > 0) {\n if (aeroBalance < aeroToSwap) {\n // Transfer in balance from the multisig as needed\n // slither-disable-next-line unchecked-transfer arbitrary-send-erc20\n aero.safeTransferFrom(\n strategistAddr,\n address(this),\n aeroToSwap - aeroBalance\n );\n }\n\n _doSwap(aeroToSwap, minWETHExpected);\n\n // Figure out AERO left in contract after swap\n aeroBalance = aero.balanceOf(address(this));\n }\n\n // Transfer out any leftover AERO after swap\n if (aeroBalance > 0) {\n aero.safeTransfer(strategistAddr, aeroBalance);\n }\n\n // Computes using all balance the contract holds,\n // not just the WETH received from swap. Use `transferToken`\n // if there's any WETH left that needs to be taken out\n uint256 availableWETHBalance = weth.balanceOf(address(this));\n // Computation rounds in favor of protocol\n uint256 fee = (availableWETHBalance * feeBps) / 10000;\n uint256 yield = availableWETHBalance - fee;\n\n // Transfer yield, if any\n if (yield > 0) {\n weth.safeTransfer(yieldRecipient, yield);\n }\n\n // Transfer fee to the Guardian, if any\n if (fee > 0) {\n weth.safeTransfer(strategistAddr, fee);\n }\n\n emit YieldSent(yieldRecipient, yield, fee);\n }\n\n /**\n * @notice Swaps AERO to WETH on Aerodrome\n * @param aeroToSwap Amount of AERO to swap\n * @param minWETHExpected Min. amount of WETH to expect\n */\n function _doSwap(uint256 aeroToSwap, uint256 minWETHExpected) internal {\n // Let the swap router move funds\n aero.approve(address(swapRouter), aeroToSwap);\n\n // Do the swap\n uint256 wethReceived = swapRouter.exactInputSingle(\n ISwapRouter.ExactInputSingleParams({\n tokenIn: address(aero),\n tokenOut: address(weth),\n tickSpacing: 200, // From AERO/WETH pool contract\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: aeroToSwap,\n amountOutMinimum: minWETHExpected,\n sqrtPriceLimitX96: 0\n })\n );\n\n emit RewardTokenSwapped(\n address(aero),\n address(weth),\n 0,\n aeroToSwap,\n wethReceived\n );\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * the contract, i.e. mistaken sends.\n * Also, allows to transfer any AERO left in the contract.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n virtual\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n}\n" + }, + "contracts/harvest/OETHDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Dripper } from \"./Dripper.sol\";\n\n/**\n * @title OETH Dripper Contract\n * @author Origin Protocol Inc\n */\ncontract OETHDripper is Dripper {\n constructor(address _vault, address _token) Dripper(_vault, _token) {}\n}\n" + }, + "contracts/harvest/OETHFixedRateDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { FixedRateDripper } from \"./FixedRateDripper.sol\";\n\n/**\n * @title OETH FixedRateDripper Contract\n * @author Origin Protocol Inc\n */\ncontract OETHFixedRateDripper is FixedRateDripper {\n constructor(address _vault, address _token)\n FixedRateDripper(_vault, _token)\n {}\n}\n" + }, + "contracts/harvest/OETHHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractHarvester } from \"./AbstractHarvester.sol\";\n\ncontract OETHHarvester is AbstractHarvester {\n constructor(address _vault, address _wethAddress)\n AbstractHarvester(_vault, _wethAddress)\n {}\n}\n" + }, + "contracts/harvest/OETHHarvesterSimple.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\n\n/// @title OETH Harvester Simple Contract\n/// @notice Contract to harvest rewards from strategies\n/// @author Origin Protocol Inc\ncontract OETHHarvesterSimple is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n ////////////////////////////////////////////////////\n /// --- CONSTANTS & IMMUTABLES\n ////////////////////////////////////////////////////\n /// @notice wrapped native token address (WETH or wS)\n address public immutable wrappedNativeToken;\n\n ////////////////////////////////////////////////////\n /// --- STORAGE\n ////////////////////////////////////////////////////\n /// @notice Dripper address\n address public dripper;\n\n /// @notice Mapping of supported strategies\n mapping(address => bool) public supportedStrategies;\n\n /// @notice Gap for upgrade safety\n uint256[48] private ___gap;\n\n ////////////////////////////////////////////////////\n /// --- EVENTS\n ////////////////////////////////////////////////////\n event Harvested(\n address indexed strategy,\n address token,\n uint256 amount,\n address indexed receiver\n );\n event SupportedStrategyUpdated(address strategy, bool status);\n event DripperUpdated(address dripper);\n\n ////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////\n constructor(address _wrappedNativeToken) {\n wrappedNativeToken = _wrappedNativeToken;\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /// @notice Initialize the contract\n function initialize() external onlyGovernor initializer {\n // Call it to set `initialized` to true and to prevent the implementation\n // from getting initialized in future through the proxy\n }\n\n ////////////////////////////////////////////////////\n /// --- MUTATIVE FUNCTIONS\n ////////////////////////////////////////////////////\n /// @notice Harvest rewards from a strategy and transfer to strategist or dripper\n /// @param _strategy Address of the strategy to harvest\n function harvestAndTransfer(address _strategy) external {\n _harvestAndTransfer(_strategy);\n }\n\n /// @notice Harvest rewards from multiple strategies and transfer to strategist or dripper\n /// @param _strategies Array of strategy addresses to harvest\n function harvestAndTransfer(address[] calldata _strategies) external {\n for (uint256 i = 0; i < _strategies.length; i++) {\n _harvestAndTransfer(_strategies[i]);\n }\n }\n\n /// @notice Internal logic to harvest rewards from a strategy\n function _harvestAndTransfer(address _strategy) internal virtual {\n // Ensure strategy is supported\n require(supportedStrategies[_strategy], \"Strategy not supported\");\n\n // Store locally for some gas savings\n address _strategist = strategistAddr;\n address _dripper = dripper;\n\n // Harvest rewards\n IStrategy(_strategy).collectRewardTokens();\n\n // Cache reward tokens\n address[] memory rewardTokens = IStrategy(_strategy)\n .getRewardTokenAddresses();\n\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; i++) {\n // Cache balance\n address token = rewardTokens[i];\n uint256 balance = IERC20(token).balanceOf(address(this));\n if (balance > 0) {\n // Determine receiver\n address receiver = token == wrappedNativeToken\n ? _dripper\n : _strategist;\n require(receiver != address(0), \"Invalid receiver\");\n\n // Transfer to the Strategist or the Dripper\n IERC20(token).safeTransfer(receiver, balance);\n emit Harvested(_strategy, token, balance, receiver);\n }\n }\n }\n\n ////////////////////////////////////////////////////\n /// --- GOVERNANCE\n ////////////////////////////////////////////////////\n /// @notice Set supported strategy\n /// @param _strategy Address of the strategy\n /// @param _isSupported Boolean indicating if strategy is supported\n function setSupportedStrategy(address _strategy, bool _isSupported)\n external\n onlyGovernorOrStrategist\n {\n require(_strategy != address(0), \"Invalid strategy\");\n supportedStrategies[_strategy] = _isSupported;\n emit SupportedStrategyUpdated(_strategy, _isSupported);\n }\n\n /// @notice Transfer tokens to strategist\n /// @param _asset Address of the token\n /// @param _amount Amount of tokens to transfer\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernorOrStrategist\n {\n IERC20(_asset).safeTransfer(strategistAddr, _amount);\n }\n\n /// @notice Set the dripper address\n /// @param _dripper Address of the dripper\n function setDripper(address _dripper) external onlyGovernor {\n _setDripper(_dripper);\n }\n\n /// @notice Internal logic to set the dripper address\n function _setDripper(address _dripper) internal {\n require(_dripper != address(0), \"Invalid dripper\");\n dripper = _dripper;\n emit DripperUpdated(_dripper);\n }\n}\n" + }, + "contracts/harvest/OSonicHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SuperOETHHarvester } from \"./SuperOETHHarvester.sol\";\n\ncontract OSonicHarvester is SuperOETHHarvester {\n /// @param _wrappedNativeToken Address of the native Wrapped S (wS) token\n constructor(address _wrappedNativeToken)\n SuperOETHHarvester(_wrappedNativeToken)\n {}\n}\n" + }, + "contracts/harvest/SuperOETHHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHHarvesterSimple, IERC20, IStrategy, SafeERC20 } from \"./OETHHarvesterSimple.sol\";\n\ncontract SuperOETHHarvester is OETHHarvesterSimple {\n using SafeERC20 for IERC20;\n\n constructor(address _wrappedNativeToken)\n OETHHarvesterSimple(_wrappedNativeToken)\n {}\n\n /// @inheritdoc OETHHarvesterSimple\n function _harvestAndTransfer(address _strategy) internal virtual override {\n // Ensure strategy is supported\n require(supportedStrategies[_strategy], \"Strategy not supported\");\n\n address receiver = strategistAddr;\n require(receiver != address(0), \"Invalid receiver\");\n\n // Harvest rewards\n IStrategy(_strategy).collectRewardTokens();\n\n // Cache reward tokens\n address[] memory rewardTokens = IStrategy(_strategy)\n .getRewardTokenAddresses();\n\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; i++) {\n // Cache balance\n address token = rewardTokens[i];\n uint256 balance = IERC20(token).balanceOf(address(this));\n if (balance > 0) {\n // Transfer everything to the strategist\n IERC20(token).safeTransfer(receiver, balance);\n emit Harvested(_strategy, token, balance, receiver);\n }\n }\n }\n}\n" + }, + "contracts/interfaces/aerodrome/IAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ICLPool } from \"./ICLPool.sol\";\n\ninterface IAMOStrategy {\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth);\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth);\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n error OutsideExpectedTickRange(int24 currentTick);\n\n function governor() external view returns (address);\n\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) external;\n\n function clPool() external view returns (ICLPool);\n\n function vaultAddress() external view returns (address);\n\n function poolWethShareVarianceAllowed() external view returns (uint256);\n\n function poolWethShare() external view returns (uint256);\n\n function tokenId() external view returns (uint256);\n\n function withdrawAll() external;\n\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external;\n\n function setWithdrawLiquidityShare(uint128 share) external;\n\n function lowerTick() external view returns (int24);\n\n function upperTick() external view returns (int24);\n\n function getPoolX96Price() external view returns (uint160 _sqrtRatioX96);\n\n function sqrtRatioX96TickLower() external view returns (uint160);\n\n function sqrtRatioX96TickHigher() external view returns (uint160);\n\n function tickSpacing() external view returns (int24);\n\n function allowedWethShareStart() external view returns (uint256);\n\n function allowedWethShareEnd() external view returns (uint256);\n\n function claimGovernance() external;\n\n function transferGovernance(address _governor) external;\n\n function getPositionPrincipal()\n external\n view\n returns (uint256 _amountWeth, uint256 _amountOethb);\n}\n" + }, + "contracts/interfaces/aerodrome/ICLGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\ninterface ICLGauge {\n /// @notice Returns the claimable rewards for a given account and tokenId\n /// @dev Throws if account is not the position owner\n /// @dev pool.updateRewardsGrowthGlobal() needs to be called first, to return the correct claimable rewards\n /// @param account The address of the user\n /// @param tokenId The tokenId of the position\n /// @return The amount of claimable reward\n function earned(address account, uint256 tokenId)\n external\n view\n returns (uint256);\n\n /// @notice Retrieve rewards for all tokens owned by an account\n /// @dev Throws if not called by the voter\n /// @param account The account of the user\n function getReward(address account) external;\n\n /// @notice Retrieve rewards for a tokenId\n /// @dev Throws if not called by the position owner\n /// @param tokenId The tokenId of the position\n function getReward(uint256 tokenId) external;\n\n /// @notice Notifies gauge of gauge rewards.\n /// @param amount Amount of gauge rewards (emissions) to notify. Must be greater than 604_800.\n function notifyRewardAmount(uint256 amount) external;\n\n /// @dev Notifies gauge of gauge rewards without distributing its fees.\n /// Assumes gauge reward tokens is 18 decimals.\n /// If not 18 decimals, rewardRate may have rounding issues.\n /// @param amount Amount of gauge rewards (emissions) to notify. Must be greater than 604_800.\n function notifyRewardWithoutClaim(uint256 amount) external;\n\n /// @notice Used to deposit a CL position into the gauge\n /// @notice Allows the user to receive emissions instead of fees\n /// @param tokenId The tokenId of the position\n function deposit(uint256 tokenId) external;\n\n /// @notice Used to withdraw a CL position from the gauge\n /// @notice Allows the user to receive fees instead of emissions\n /// @notice Outstanding emissions will be collected on withdrawal\n /// @param tokenId The tokenId of the position\n function withdraw(uint256 tokenId) external;\n\n // /// @notice Fetch all tokenIds staked by a given account\n // /// @param depositor The address of the user\n // /// @return The tokenIds of the staked positions\n // function stakedValues(address depositor) external view returns (uint256[] memory);\n\n // /// @notice Fetch a staked tokenId by index\n // /// @param depositor The address of the user\n // /// @param index The index of the staked tokenId\n // /// @return The tokenId of the staked position\n // function stakedByIndex(address depositor, uint256 index) external view returns (uint256);\n\n // /// @notice Check whether a position is staked in the gauge by a certain user\n // /// @param depositor The address of the user\n // /// @param tokenId The tokenId of the position\n // /// @return Whether the position is staked in the gauge\n // function stakedContains(address depositor, uint256 tokenId) external view returns (bool);\n\n // /// @notice The amount of positions staked in the gauge by a certain user\n // /// @param depositor The address of the user\n // /// @return The amount of positions staked in the gauge\n // function stakedLength(address depositor) external view returns (uint256);\n\n function feesVotingReward() external view returns (address);\n}\n" + }, + "contracts/interfaces/aerodrome/ICLPool.sol": { + "content": "pragma solidity >=0.5.0;\n\n/// @title The interface for a CL Pool\n/// @notice A CL pool facilitates swapping and automated market making between any two assets that strictly conform\n/// to the ERC20 specification\n/// @dev The pool interface is broken up into many smaller pieces\ninterface ICLPool {\n function slot0()\n external\n view\n returns (\n uint160 sqrtPriceX96,\n int24 tick,\n uint16 observationIndex,\n uint16 observationCardinality,\n uint16 observationCardinalityNext,\n bool unlocked\n );\n\n /// @notice The first of the two tokens of the pool, sorted by address\n /// @return The token contract address\n function token0() external view returns (address);\n\n /// @notice The second of the two tokens of the pool, sorted by address\n /// @return The token contract address\n function token1() external view returns (address);\n\n function tickSpacing() external view returns (int24);\n\n /// @notice The gauge corresponding to this pool\n /// @return The gauge contract address\n function gauge() external view returns (address);\n\n /// @notice The currently in range liquidity available to the pool\n /// @dev This value has no relationship to the total liquidity across all ticks\n /// @dev This value includes staked liquidity\n function liquidity() external view returns (uint128);\n\n /// @notice Look up information about a specific tick in the pool\n /// @param tick The tick to look up\n /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or\n /// tick upper,\n /// liquidityNet how much liquidity changes when the pool price crosses the tick,\n /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,\n /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,\n /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick\n /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from\n /// the current tick,\n /// secondsOutside the seconds spent on the other side of the tick from the current tick,\n /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise\n /// equal to false.\n /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.\n /// In addition, these values are only relative and must be used only in comparison to previous snapshots for\n /// a specific position.\n function ticks(int24 tick)\n external\n view\n returns (\n uint128 liquidityGross,\n int128 liquidityNet,\n uint256 feeGrowthOutside0X128,\n uint256 feeGrowthOutside1X128,\n int56 tickCumulativeOutside,\n uint160 secondsPerLiquidityOutsideX128,\n uint32 secondsOutside,\n bool initialized\n );\n}\n" + }, + "contracts/interfaces/aerodrome/INonfungiblePositionManager.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title Non-fungible token for positions\n/// @notice Wraps CL positions in a non-fungible token interface which allows for them to be transferred\n/// and authorized.\n// slither-disable-start erc20-interface\ninterface INonfungiblePositionManager {\n /**\n * @dev See {IERC721-approve}.\n */\n function approve(address to, uint256 tokenId) external;\n\n /**\n * @dev See {IERC721-getApproved}.\n */\n function getApproved(uint256 tokenId) external returns (address);\n\n /**\n * @dev See {IERC721-ownerOf}.\n */\n function ownerOf(uint256 tokenId) external view returns (address);\n\n /// @notice Returns the position information associated with a given token ID.\n /// @dev Throws if the token ID is not valid.\n /// @param tokenId The ID of the token that represents the position\n /// @return nonce The nonce for permits\n /// @return operator The address that is approved for spending\n /// @return token0 The address of the token0 for a specific pool\n /// @return token1 The address of the token1 for a specific pool\n /// @return tickSpacing The tick spacing associated with the pool\n /// @return tickLower The lower end of the tick range for the position\n /// @return tickUpper The higher end of the tick range for the position\n /// @return liquidity The liquidity of the position\n /// @return feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position\n /// @return feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position\n /// @return tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation\n /// @return tokensOwed1 The uncollected amount of token1 owed to the position as of the last computation\n function positions(uint256 tokenId)\n external\n view\n returns (\n uint96 nonce,\n address operator,\n address token0,\n address token1,\n int24 tickSpacing,\n int24 tickLower,\n int24 tickUpper,\n uint128 liquidity,\n uint256 feeGrowthInside0LastX128,\n uint256 feeGrowthInside1LastX128,\n uint128 tokensOwed0,\n uint128 tokensOwed1\n );\n\n struct MintParams {\n address token0;\n address token1;\n int24 tickSpacing;\n int24 tickLower;\n int24 tickUpper;\n uint256 amount0Desired;\n uint256 amount1Desired;\n uint256 amount0Min;\n uint256 amount1Min;\n address recipient;\n uint256 deadline;\n uint160 sqrtPriceX96;\n }\n\n /// @notice Creates a new position wrapped in a NFT\n /// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized\n /// a method does not exist, i.e. the pool is assumed to be initialized.\n /// @param params The params necessary to mint a position, encoded as `MintParams` in calldata\n /// @return tokenId The ID of the token that represents the minted position\n /// @return liquidity The amount of liquidity for this position\n /// @return amount0 The amount of token0\n /// @return amount1 The amount of token1\n function mint(MintParams calldata params)\n external\n payable\n returns (\n uint256 tokenId,\n uint128 liquidity,\n uint256 amount0,\n uint256 amount1\n );\n\n struct IncreaseLiquidityParams {\n uint256 tokenId;\n uint256 amount0Desired;\n uint256 amount1Desired;\n uint256 amount0Min;\n uint256 amount1Min;\n uint256 deadline;\n }\n\n /// @notice Increases the amount of liquidity in a position, with tokens paid by the `msg.sender`\n /// @param params tokenId The ID of the token for which liquidity is being increased,\n /// amount0Desired The desired amount of token0 to be spent,\n /// amount1Desired The desired amount of token1 to be spent,\n /// amount0Min The minimum amount of token0 to spend, which serves as a slippage check,\n /// amount1Min The minimum amount of token1 to spend, which serves as a slippage check,\n /// deadline The time by which the transaction must be included to effect the change\n /// @return liquidity The new liquidity amount as a result of the increase\n /// @return amount0 The amount of token0 to acheive resulting liquidity\n /// @return amount1 The amount of token1 to acheive resulting liquidity\n function increaseLiquidity(IncreaseLiquidityParams calldata params)\n external\n payable\n returns (\n uint128 liquidity,\n uint256 amount0,\n uint256 amount1\n );\n\n struct DecreaseLiquidityParams {\n uint256 tokenId;\n uint128 liquidity;\n uint256 amount0Min;\n uint256 amount1Min;\n uint256 deadline;\n }\n\n /// @notice Decreases the amount of liquidity in a position and accounts it to the position\n /// @param params tokenId The ID of the token for which liquidity is being decreased,\n /// amount The amount by which liquidity will be decreased,\n /// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity,\n /// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity,\n /// deadline The time by which the transaction must be included to effect the change\n /// @return amount0 The amount of token0 accounted to the position's tokens owed\n /// @return amount1 The amount of token1 accounted to the position's tokens owed\n /// @dev The use of this function can cause a loss to users of the NonfungiblePositionManager\n /// @dev for tokens that have very high decimals.\n /// @dev The amount of tokens necessary for the loss is: 3.4028237e+38.\n /// @dev This is equivalent to 1e20 value with 18 decimals.\n function decreaseLiquidity(DecreaseLiquidityParams calldata params)\n external\n payable\n returns (uint256 amount0, uint256 amount1);\n\n struct CollectParams {\n uint256 tokenId;\n address recipient;\n uint128 amount0Max;\n uint128 amount1Max;\n }\n\n /// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient\n /// @notice Used to update staked positions before deposit and withdraw\n /// @param params tokenId The ID of the NFT for which tokens are being collected,\n /// recipient The account that should receive the tokens,\n /// amount0Max The maximum amount of token0 to collect,\n /// amount1Max The maximum amount of token1 to collect\n /// @return amount0 The amount of fees collected in token0\n /// @return amount1 The amount of fees collected in token1\n function collect(CollectParams calldata params)\n external\n payable\n returns (uint256 amount0, uint256 amount1);\n\n /// @notice Burns a token ID, which deletes it from the NFT contract. The token must have 0 liquidity and all tokens\n /// must be collected first.\n /// @param tokenId The ID of the token that is being burned\n function burn(uint256 tokenId) external payable;\n\n /// @notice Sets a new Token Descriptor\n /// @param _tokenDescriptor Address of the new Token Descriptor to be chosen\n function setTokenDescriptor(address _tokenDescriptor) external;\n\n /// @notice Sets a new Owner address\n /// @param _owner Address of the new Owner to be chosen\n function setOwner(address _owner) external;\n}\n// slither-disable-end erc20-interface\n" + }, + "contracts/interfaces/aerodrome/IQuoterV2.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title QuoterV2 Interface\n/// @notice Supports quoting the calculated amounts from exact input or exact output swaps.\n/// @notice For each pool also tells you the number of initialized ticks crossed and the sqrt price of the\n/// pool after the swap.\n/// @dev These functions are not marked view because they rely on calling non-view functions and reverting\n/// to compute the result. They are also not gas efficient and should not be called on-chain.\ninterface IQuoterV2 {\n /// @notice Returns the amount out received for a given exact input swap without executing the swap\n /// @param path The path of the swap, i.e. each token pair and the pool tick spacing\n /// @param amountIn The amount of the first token to swap\n /// @return amountOut The amount of the last token that would be received\n /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path\n /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for\n /// each pool in the path\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactInput(bytes memory path, uint256 amountIn)\n external\n returns (\n uint256 amountOut,\n uint160[] memory sqrtPriceX96AfterList,\n uint32[] memory initializedTicksCrossedList,\n uint256 gasEstimate\n );\n\n struct QuoteExactInputSingleParams {\n address tokenIn;\n address tokenOut;\n uint256 amountIn;\n int24 tickSpacing;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Returns the amount out received for a given exact input but for a swap of a single pool\n /// @param params The params for the quote, encoded as `QuoteExactInputSingleParams`\n /// tokenIn The token being swapped in\n /// tokenOut The token being swapped out\n /// tickSpacing The tick spacing of the token pool to consider for the pair\n /// amountIn The desired input amount\n /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap\n /// @return amountOut The amount of `tokenOut` that would be received\n /// @return sqrtPriceX96After The sqrt price of the pool after the swap\n /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactInputSingle(QuoteExactInputSingleParams memory params)\n external\n returns (\n uint256 amountOut,\n uint160 sqrtPriceX96After,\n uint32 initializedTicksCrossed,\n uint256 gasEstimate\n );\n\n /// @notice Returns the amount in required for a given exact output swap without executing the swap\n /// @param path The path of the swap, i.e. each token pair and the pool tick spacing.\n /// Path must be provided in reverse order\n /// @param amountOut The amount of the last token to receive\n /// @return amountIn The amount of first token required to be paid\n /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path\n /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for\n /// each pool in the path\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactOutput(bytes memory path, uint256 amountOut)\n external\n returns (\n uint256 amountIn,\n uint160[] memory sqrtPriceX96AfterList,\n uint32[] memory initializedTicksCrossedList,\n uint256 gasEstimate\n );\n\n struct QuoteExactOutputSingleParams {\n address tokenIn;\n address tokenOut;\n uint256 amount;\n int24 tickSpacing;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Returns the amount in required to receive the given exact output amount but for a swap of a single pool\n /// @param params The params for the quote, encoded as `QuoteExactOutputSingleParams`\n /// tokenIn The token being swapped in\n /// tokenOut The token being swapped out\n /// tickSpacing The tick spacing of the token pool to consider for the pair\n /// amountOut The desired output amount\n /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap\n /// @return amountIn The amount required as the input for the swap in order to receive `amountOut`\n /// @return sqrtPriceX96After The sqrt price of the pool after the swap\n /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactOutputSingle(QuoteExactOutputSingleParams memory params)\n external\n returns (\n uint256 amountIn,\n uint160 sqrtPriceX96After,\n uint32 initializedTicksCrossed,\n uint256 gasEstimate\n );\n}\n" + }, + "contracts/interfaces/aerodrome/ISugarHelper.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\npragma abicoder v2;\n\nimport { INonfungiblePositionManager } from \"./INonfungiblePositionManager.sol\";\n\ninterface ISugarHelper {\n struct PopulatedTick {\n int24 tick;\n uint160 sqrtRatioX96;\n int128 liquidityNet;\n uint128 liquidityGross;\n }\n\n ///\n /// Wrappers for LiquidityAmounts\n ///\n\n function getAmountsForLiquidity(\n uint160 sqrtRatioX96,\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96,\n uint128 liquidity\n ) external pure returns (uint256 amount0, uint256 amount1);\n\n function getLiquidityForAmounts(\n uint256 amount0,\n uint256 amount1,\n uint160 sqrtRatioX96,\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96\n ) external pure returns (uint128 liquidity);\n\n /// @notice Computes the amount of token0 for a given amount of token1 and price range\n /// @param amount1 Amount of token1 to estimate liquidity\n /// @param pool Address of the pool to be used\n /// @param sqrtRatioX96 A sqrt price representing the current pool prices\n /// @param tickLow Lower tick boundary\n /// @param tickLow Upper tick boundary\n /// @dev If the given pool address is not the zero address, will fetch `sqrtRatioX96` from pool\n /// @return amount0 Estimated amount of token0\n function estimateAmount0(\n uint256 amount1,\n address pool,\n uint160 sqrtRatioX96,\n int24 tickLow,\n int24 tickHigh\n ) external view returns (uint256 amount0);\n\n /// @notice Computes the amount of token1 for a given amount of token0 and price range\n /// @param amount0 Amount of token0 to estimate liquidity\n /// @param pool Address of the pool to be used\n /// @param sqrtRatioX96 A sqrt price representing the current pool prices\n /// @param tickLow Lower tick boundary\n /// @param tickLow Upper tick boundary\n /// @dev If the given pool address is not the zero address, will fetch `sqrtRatioX96` from pool\n /// @return amount1 Estimated amount of token1\n function estimateAmount1(\n uint256 amount0,\n address pool,\n uint160 sqrtRatioX96,\n int24 tickLow,\n int24 tickHigh\n ) external view returns (uint256 amount1);\n\n ///\n /// Wrappers for PositionValue\n ///\n\n function principal(\n INonfungiblePositionManager positionManager,\n uint256 tokenId,\n uint160 sqrtRatioX96\n ) external view returns (uint256 amount0, uint256 amount1);\n\n function fees(INonfungiblePositionManager positionManager, uint256 tokenId)\n external\n view\n returns (uint256 amount0, uint256 amount1);\n\n ///\n /// Wrappers for TickMath\n ///\n\n function getSqrtRatioAtTick(int24 tick)\n external\n pure\n returns (uint160 sqrtRatioX96);\n\n function getTickAtSqrtRatio(uint160 sqrtRatioX96)\n external\n pure\n returns (int24 tick);\n\n /// @notice Fetches Tick Data for all populated Ticks in given bitmaps\n /// @param pool Address of the pool from which to fetch data\n /// @param startTick Tick from which the first bitmap will be fetched\n /// @dev The number of bitmaps fetched by this function should always be `MAX_BITMAPS`,\n /// unless there are less than `MAX_BITMAPS` left to iterate through\n /// @return populatedTicks Array of all Populated Ticks in the provided bitmaps\n function getPopulatedTicks(address pool, int24 startTick)\n external\n view\n returns (PopulatedTick[] memory populatedTicks);\n}\n" + }, + "contracts/interfaces/aerodrome/ISwapRouter.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title Router token swapping functionality\n/// @notice Functions for swapping tokens via CL\ninterface ISwapRouter {\n struct ExactInputSingleParams {\n address tokenIn;\n address tokenOut;\n int24 tickSpacing;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInputSingle(ExactInputSingleParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactOutputSingleParams {\n address tokenIn;\n address tokenOut;\n int24 tickSpacing;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutputSingle(ExactOutputSingleParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n\n struct ExactOutputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutput(ExactOutputParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n}\n" + }, + "contracts/interfaces/balancer/IBalancerVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"../../utils/InitializableAbstractStrategy.sol\";\n\ninterface IBalancerVault {\n enum WeightedPoolJoinKind {\n INIT,\n EXACT_TOKENS_IN_FOR_BPT_OUT,\n TOKEN_IN_FOR_EXACT_BPT_OUT,\n ALL_TOKENS_IN_FOR_EXACT_BPT_OUT,\n ADD_TOKEN\n }\n\n enum WeightedPoolExitKind {\n EXACT_BPT_IN_FOR_ONE_TOKEN_OUT,\n EXACT_BPT_IN_FOR_TOKENS_OUT,\n BPT_IN_FOR_EXACT_TOKENS_OUT,\n REMOVE_TOKEN\n }\n\n /**\n * @dev Called by users to join a Pool, which transfers tokens from `sender` into the Pool's balance. This will\n * trigger custom Pool behavior, which will typically grant something in return to `recipient` - often tokenized\n * Pool shares.\n *\n * If the caller is not `sender`, it must be an authorized relayer for them.\n *\n * The `assets` and `maxAmountsIn` arrays must have the same length, and each entry indicates the maximum amount\n * to send for each asset. The amounts to send are decided by the Pool and not the Vault: it just enforces\n * these maximums.\n *\n * If joining a Pool that holds WETH, it is possible to send ETH directly: the Vault will do the wrapping. To enable\n * this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead of the\n * WETH address. Note that it is not possible to combine ETH and WETH in the same join. Any excess ETH will be sent\n * back to the caller (not the sender, which is important for relayers).\n *\n * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when\n * interacting with Pools that register and deregister tokens frequently. If sending ETH however, the array must be\n * sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the final\n * `assets` array might not be sorted. Pools with no registered tokens cannot be joined.\n *\n * If `fromInternalBalance` is true, the caller's Internal Balance will be preferred: ERC20 transfers will only\n * be made for the difference between the requested amount and Internal Balance (if any). Note that ETH cannot be\n * withdrawn from Internal Balance: attempting to do so will trigger a revert.\n *\n * This causes the Vault to call the `IBasePool.onJoinPool` hook on the Pool's contract, where Pools implement\n * their own custom logic. This typically requires additional information from the user (such as the expected number\n * of Pool shares). This can be encoded in the `userData` argument, which is ignored by the Vault and passed\n * directly to the Pool's contract, as is `recipient`.\n *\n * Emits a `PoolBalanceChanged` event.\n */\n function joinPool(\n bytes32 poolId,\n address sender,\n address recipient,\n JoinPoolRequest memory request\n ) external payable;\n\n struct JoinPoolRequest {\n address[] assets;\n uint256[] maxAmountsIn;\n bytes userData;\n bool fromInternalBalance;\n }\n\n /**\n * @dev Called by users to exit a Pool, which transfers tokens from the Pool's balance to `recipient`. This will\n * trigger custom Pool behavior, which will typically ask for something in return from `sender` - often tokenized\n * Pool shares. The amount of tokens that can be withdrawn is limited by the Pool's `cash` balance (see\n * `getPoolTokenInfo`).\n *\n * If the caller is not `sender`, it must be an authorized relayer for them.\n *\n * The `tokens` and `minAmountsOut` arrays must have the same length, and each entry in these indicates the minimum\n * token amount to receive for each token contract. The amounts to send are decided by the Pool and not the Vault:\n * it just enforces these minimums.\n *\n * If exiting a Pool that holds WETH, it is possible to receive ETH directly: the Vault will do the unwrapping. To\n * enable this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead\n * of the WETH address. Note that it is not possible to combine ETH and WETH in the same exit.\n *\n * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when\n * interacting with Pools that register and deregister tokens frequently. If receiving ETH however, the array must\n * be sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the\n * final `assets` array might not be sorted. Pools with no registered tokens cannot be exited.\n *\n * If `toInternalBalance` is true, the tokens will be deposited to `recipient`'s Internal Balance. Otherwise,\n * an ERC20 transfer will be performed. Note that ETH cannot be deposited to Internal Balance: attempting to\n * do so will trigger a revert.\n *\n * `minAmountsOut` is the minimum amount of tokens the user expects to get out of the Pool, for each token in the\n * `tokens` array. This array must match the Pool's registered tokens.\n *\n * This causes the Vault to call the `IBasePool.onExitPool` hook on the Pool's contract, where Pools implement\n * their own custom logic. This typically requires additional information from the user (such as the expected number\n * of Pool shares to return). This can be encoded in the `userData` argument, which is ignored by the Vault and\n * passed directly to the Pool's contract.\n *\n * Emits a `PoolBalanceChanged` event.\n */\n function exitPool(\n bytes32 poolId,\n address sender,\n address payable recipient,\n ExitPoolRequest memory request\n ) external;\n\n struct ExitPoolRequest {\n address[] assets;\n uint256[] minAmountsOut;\n bytes userData;\n bool toInternalBalance;\n }\n\n /**\n * @dev Returns a Pool's registered tokens, the total balance for each, and the latest block when *any* of\n * the tokens' `balances` changed.\n *\n * The order of the `tokens` array is the same order that will be used in `joinPool`, `exitPool`, as well as in all\n * Pool hooks (where applicable). Calls to `registerTokens` and `deregisterTokens` may change this order.\n *\n * If a Pool only registers tokens once, and these are sorted in ascending order, they will be stored in the same\n * order as passed to `registerTokens`.\n *\n * Total balances include both tokens held by the Vault and those withdrawn by the Pool's Asset Managers. These are\n * the amounts used by joins, exits and swaps. For a detailed breakdown of token balances, use `getPoolTokenInfo`\n * instead.\n */\n function getPoolTokens(bytes32 poolId)\n external\n view\n returns (\n IERC20[] memory tokens,\n uint256[] memory balances,\n uint256 lastChangeBlock\n );\n\n /**\n * @dev Performs a set of user balance operations, which involve Internal Balance (deposit, withdraw or transfer)\n * and plain ERC20 transfers using the Vault's allowance. This last feature is particularly useful for relayers, as\n * it lets integrators reuse a user's Vault allowance.\n *\n * For each operation, if the caller is not `sender`, it must be an authorized relayer for them.\n */\n function manageUserBalance(UserBalanceOp[] memory ops) external payable;\n\n struct UserBalanceOp {\n UserBalanceOpKind kind;\n address asset;\n uint256 amount;\n address sender;\n address payable recipient;\n }\n\n enum UserBalanceOpKind {\n DEPOSIT_INTERNAL,\n WITHDRAW_INTERNAL,\n TRANSFER_INTERNAL,\n TRANSFER_EXTERNAL\n }\n\n enum SwapKind {\n GIVEN_IN,\n GIVEN_OUT\n }\n\n struct SingleSwap {\n bytes32 poolId;\n SwapKind kind;\n address assetIn;\n address assetOut;\n uint256 amount;\n bytes userData;\n }\n\n struct FundManagement {\n address sender;\n bool fromInternalBalance;\n address payable recipient;\n bool toInternalBalance;\n }\n\n function swap(\n SingleSwap calldata singleSwap,\n FundManagement calldata funds,\n uint256 limit,\n uint256 deadline\n ) external returns (uint256 amountCalculated);\n\n function getPoolTokenInfo(bytes32 poolId, address token)\n external\n view\n returns (\n uint256 cash,\n uint256 managed,\n uint256 lastChangeBlock,\n address assetManager\n );\n}\n" + }, + "contracts/interfaces/balancer/IMetaStablePool.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IRateProvider } from \"./IRateProvider.sol\";\n\ninterface IMetaStablePool {\n function getRateProviders()\n external\n view\n returns (IRateProvider[] memory providers);\n}\n" + }, + "contracts/interfaces/balancer/IOracleWeightedPool.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// The three values that can be queried:\n//\n// - PAIR_PRICE: the price of the tokens in the Pool, expressed as the price of the second token in units of the\n// first token. For example, if token A is worth $2, and token B is worth $4, the pair price will be 2.0.\n// Note that the price is computed *including* the tokens decimals. This means that the pair price of a Pool with\n// DAI and USDC will be close to 1.0, despite DAI having 18 decimals and USDC 6.\n//\n// - BPT_PRICE: the price of the Pool share token (BPT), in units of the first token.\n// Note that the price is computed *including* the tokens decimals. This means that the BPT price of a Pool with\n// USDC in which BPT is worth $5 will be 5.0, despite the BPT having 18 decimals and USDC 6.\n//\n// - INVARIANT: the value of the Pool's invariant, which serves as a measure of its liquidity.\nenum Variable {\n PAIR_PRICE,\n BPT_PRICE,\n INVARIANT\n}\n\n/**\n * @dev Information for a Time Weighted Average query.\n *\n * Each query computes the average over a window of duration `secs` seconds that ended `ago` seconds ago. For\n * example, the average over the past 30 minutes is computed by settings secs to 1800 and ago to 0. If secs is 1800\n * and ago is 1800 as well, the average between 60 and 30 minutes ago is computed instead.\n */\nstruct OracleAverageQuery {\n Variable variable;\n uint256 secs;\n uint256 ago;\n}\n\ninterface IOracleWeightedPool {\n /**\n * @dev Returns the time average weighted price corresponding to each of `queries`. Prices are represented as 18\n * decimal fixed point values.\n */\n function getTimeWeightedAverage(OracleAverageQuery[] memory queries)\n external\n view\n returns (uint256[] memory results);\n}\n" + }, + "contracts/interfaces/balancer/IRateProvider.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity ^0.8.0;\n\ninterface IRateProvider {\n function getRate() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/chainlink/AggregatorV3Interface.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorV3Interface {\n function decimals() external view returns (uint8);\n\n function description() external view returns (string memory);\n\n function version() external view returns (uint256);\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(uint80 _roundId)\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n\n function latestRoundData()\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n enum BalanceProofLevel {\n Container,\n BeaconBlock\n }\n\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view;\n\n function verifyBlockNumber(\n bytes32 beaconBlockRoot,\n uint256 blockNumber,\n bytes calldata blockNumberProof\n ) external view;\n\n function verifySlot(\n bytes32 beaconBlockRoot,\n uint256 slot,\n bytes calldata slotProof\n ) external view;\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n external\n pure\n returns (uint256);\n\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) external pure returns (uint256);\n}\n" + }, + "contracts/interfaces/IBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBuyback {\n function swap() external;\n}\n" + }, + "contracts/interfaces/ICampaignRemoteManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICampaignRemoteManager {\n function createCampaign(\n CampaignCreationParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n function manageCampaign(\n CampaignManagementParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n function closeCampaign(\n CampaignClosingParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n struct CampaignCreationParams {\n uint256 chainId;\n address gauge;\n address manager;\n address rewardToken;\n uint8 numberOfPeriods;\n uint256 maxRewardPerVote;\n uint256 totalRewardAmount;\n address[] addresses;\n address hook;\n bool isWhitelist;\n }\n\n struct CampaignManagementParams {\n uint256 campaignId;\n address rewardToken;\n uint8 numberOfPeriods;\n uint256 totalRewardAmount;\n uint256 maxRewardPerVote;\n }\n\n struct CampaignClosingParams {\n uint256 campaignId;\n }\n}\n" + }, + "contracts/interfaces/IChildLiquidityGaugeFactory.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IChildLiquidityGaugeFactory {\n event DeployedGauge(\n address indexed _implementation,\n address indexed _lp_token,\n address indexed _deployer,\n bytes32 _salt,\n address _gauge\n );\n event Minted(\n address indexed _user,\n address indexed _gauge,\n uint256 _new_total\n );\n event TransferOwnership(address _old_owner, address _new_owner);\n event UpdateCallProxy(address _old_call_proxy, address _new_call_proxy);\n event UpdateImplementation(\n address _old_implementation,\n address _new_implementation\n );\n event UpdateManager(address _manager);\n event UpdateMirrored(address indexed _gauge, bool _mirrored);\n event UpdateRoot(address _factory, address _implementation);\n event UpdateVotingEscrow(\n address _old_voting_escrow,\n address _new_voting_escrow\n );\n\n function accept_transfer_ownership() external;\n\n function call_proxy() external view returns (address);\n\n function commit_transfer_ownership(address _future_owner) external;\n\n function crv() external view returns (address);\n\n function deploy_gauge(address _lp_token, bytes32 _salt)\n external\n returns (address);\n\n function deploy_gauge(\n address _lp_token,\n bytes32 _salt,\n address _manager\n ) external returns (address);\n\n function future_owner() external view returns (address);\n\n function gauge_data(address arg0) external view returns (uint256);\n\n function get_gauge(uint256 arg0) external view returns (address);\n\n function get_gauge_count() external view returns (uint256);\n\n function get_gauge_from_lp_token(address arg0)\n external\n view\n returns (address);\n\n function get_implementation() external view returns (address);\n\n function is_mirrored(address _gauge) external view returns (bool);\n\n function is_valid_gauge(address _gauge) external view returns (bool);\n\n function last_request(address _gauge) external view returns (uint256);\n\n function manager() external view returns (address);\n\n function mint(address _gauge) external;\n\n function mint_many(address[32] memory _gauges) external;\n\n function minted(address arg0, address arg1) external view returns (uint256);\n\n function owner() external view returns (address);\n\n function root_factory() external view returns (address);\n\n function root_implementation() external view returns (address);\n\n function set_call_proxy(address _new_call_proxy) external;\n\n function set_crv(address _crv) external;\n\n function set_implementation(address _implementation) external;\n\n function set_manager(address _new_manager) external;\n\n function set_mirrored(address _gauge, bool _mirrored) external;\n\n function set_root(address _factory, address _implementation) external;\n\n function set_voting_escrow(address _voting_escrow) external;\n\n function version() external view returns (string memory);\n\n function voting_escrow() external view returns (address);\n}\n" + }, + "contracts/interfaces/IComptroller.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IComptroller {\n // Claim all the COMP accrued by specific holders in specific markets for their supplies and/or borrows\n function claimComp(\n address[] memory holders,\n address[] memory cTokens,\n bool borrowers,\n bool suppliers\n ) external;\n\n function oracle() external view returns (address);\n}\n" + }, + "contracts/interfaces/IConsolidation.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IConsolidationSource {\n function confirmConsolidation()\n external\n returns (uint256 consolidationCount);\n}\n\ninterface IConsolidationTarget {\n function requestConsolidation(\n bytes32 lastSourcePubKeyHash,\n bytes32 targetPubKeyHash\n ) external;\n}\n" + }, + "contracts/interfaces/ICurveLiquidityGaugeV6.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveLiquidityGaugeV6 {\n event ApplyOwnership(address admin);\n event Approval(\n address indexed _owner,\n address indexed _spender,\n uint256 _value\n );\n event CommitOwnership(address admin);\n event Deposit(address indexed provider, uint256 value);\n event SetGaugeManager(address _gauge_manager);\n event Transfer(address indexed _from, address indexed _to, uint256 _value);\n event UpdateLiquidityLimit(\n address indexed user,\n uint256 original_balance,\n uint256 original_supply,\n uint256 working_balance,\n uint256 working_supply\n );\n event Withdraw(address indexed provider, uint256 value);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_reward(address _reward_token, address _distributor) external;\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function claim_rewards() external;\n\n function claim_rewards(address _addr) external;\n\n function claim_rewards(address _addr, address _receiver) external;\n\n function claimable_reward(address _user, address _reward_token)\n external\n view\n returns (uint256);\n\n function claimable_tokens(address addr) external returns (uint256);\n\n function claimed_reward(address _addr, address _token)\n external\n view\n returns (uint256);\n\n function decimals() external view returns (uint256);\n\n function decreaseAllowance(address _spender, uint256 _subtracted_value)\n external\n returns (bool);\n\n function deposit(uint256 _value) external;\n\n function deposit(uint256 _value, address _addr) external;\n\n function deposit(\n uint256 _value,\n address _addr,\n bool _claim_rewards\n ) external;\n\n function deposit_reward_token(address _reward_token, uint256 _amount)\n external;\n\n function deposit_reward_token(\n address _reward_token,\n uint256 _amount,\n uint256 _epoch\n ) external;\n\n function factory() external view returns (address);\n\n function future_epoch_time() external view returns (uint256);\n\n function increaseAllowance(address _spender, uint256 _added_value)\n external\n returns (bool);\n\n function inflation_rate() external view returns (uint256);\n\n function integrate_checkpoint() external view returns (uint256);\n\n function integrate_checkpoint_of(address arg0)\n external\n view\n returns (uint256);\n\n function integrate_fraction(address arg0) external view returns (uint256);\n\n function integrate_inv_supply(uint256 arg0) external view returns (uint256);\n\n function integrate_inv_supply_of(address arg0)\n external\n view\n returns (uint256);\n\n function is_killed() external view returns (bool);\n\n function kick(address addr) external;\n\n function lp_token() external view returns (address);\n\n function manager() external view returns (address);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function period() external view returns (int128);\n\n function period_timestamp(uint256 arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function reward_count() external view returns (uint256);\n\n function reward_integral_for(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function reward_tokens(uint256 arg0) external view returns (address);\n\n function rewards_receiver(address arg0) external view returns (address);\n\n function salt() external view returns (bytes32);\n\n function set_gauge_manager(address _gauge_manager) external;\n\n function set_killed(bool _is_killed) external;\n\n function set_reward_distributor(address _reward_token, address _distributor)\n external;\n\n function set_rewards_receiver(address _receiver) external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function user_checkpoint(address addr) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw(uint256 _value) external;\n\n function withdraw(uint256 _value, bool _claim_rewards) external;\n\n function working_balances(address arg0) external view returns (uint256);\n\n function working_supply() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/ICurveMinter.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveMinter {\n event Minted(address indexed recipient, address gauge, uint256 minted);\n\n function allowed_to_mint_for(address arg0, address arg1)\n external\n view\n returns (bool);\n\n function controller() external view returns (address);\n\n function mint(address gauge_addr) external;\n\n function mint_for(address gauge_addr, address _for) external;\n\n function mint_many(address[8] memory gauge_addrs) external;\n\n function minted(address arg0, address arg1) external view returns (uint256);\n\n function toggle_approve_mint(address minting_user) external;\n\n function token() external view returns (address);\n}\n" + }, + "contracts/interfaces/ICurveStableSwapNG.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveStableSwapNG {\n event AddLiquidity(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event ApplyNewFee(uint256 fee, uint256 offpeg_fee_multiplier);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event RampA(\n uint256 old_A,\n uint256 new_A,\n uint256 initial_time,\n uint256 future_time\n );\n event RemoveLiquidity(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 token_supply\n );\n event RemoveLiquidityImbalance(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event RemoveLiquidityOne(\n address indexed provider,\n int128 token_id,\n uint256 token_amount,\n uint256 coin_amount,\n uint256 token_supply\n );\n event SetNewMATime(uint256 ma_exp_time, uint256 D_ma_time);\n event StopRampA(uint256 A, uint256 t);\n event TokenExchange(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event TokenExchangeUnderlying(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event Transfer(\n address indexed sender,\n address indexed receiver,\n uint256 value\n );\n\n function A() external view returns (uint256);\n\n function A_precise() external view returns (uint256);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function D_ma_time() external view returns (uint256);\n\n function D_oracle() external view returns (uint256);\n\n function N_COINS() external view returns (uint256);\n\n function add_liquidity(uint256[] memory _amounts, uint256 _min_mint_amount)\n external\n returns (uint256);\n\n function add_liquidity(\n uint256[] memory _amounts,\n uint256 _min_mint_amount,\n address _receiver\n ) external returns (uint256);\n\n function admin_balances(uint256 arg0) external view returns (uint256);\n\n function admin_fee() external view returns (uint256);\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function balances(uint256 i) external view returns (uint256);\n\n function calc_token_amount(uint256[] memory _amounts, bool _is_deposit)\n external\n view\n returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _burn_amount, int128 i)\n external\n view\n returns (uint256);\n\n function coins(uint256 arg0) external view returns (address);\n\n function decimals() external view returns (uint8);\n\n function dynamic_fee(int128 i, int128 j) external view returns (uint256);\n\n function ema_price(uint256 i) external view returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external returns (uint256);\n\n function exchange_received(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external returns (uint256);\n\n function exchange_received(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external returns (uint256);\n\n function fee() external view returns (uint256);\n\n function future_A() external view returns (uint256);\n\n function future_A_time() external view returns (uint256);\n\n function get_balances() external view returns (uint256[] memory);\n\n function get_dx(\n int128 i,\n int128 j,\n uint256 dy\n ) external view returns (uint256);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n\n function get_p(uint256 i) external view returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function initial_A() external view returns (uint256);\n\n function initial_A_time() external view returns (uint256);\n\n function last_price(uint256 i) external view returns (uint256);\n\n function ma_exp_time() external view returns (uint256);\n\n function ma_last_time() external view returns (uint256);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function offpeg_fee_multiplier() external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function price_oracle(uint256 i) external view returns (uint256);\n\n function ramp_A(uint256 _future_A, uint256 _future_time) external;\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts\n ) external returns (uint256[] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts,\n address _receiver\n ) external returns (uint256[] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts,\n address _receiver,\n bool _claim_admin_fees\n ) external returns (uint256[] memory);\n\n function remove_liquidity_imbalance(\n uint256[] memory _amounts,\n uint256 _max_burn_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[] memory _amounts,\n uint256 _max_burn_amount,\n address _receiver\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received,\n address _receiver\n ) external returns (uint256);\n\n function salt() external view returns (bytes32);\n\n function set_ma_exp_time(uint256 _ma_exp_time, uint256 _D_ma_time) external;\n\n function set_new_fee(uint256 _new_fee, uint256 _new_offpeg_fee_multiplier)\n external;\n\n function stop_ramp_A() external;\n\n function stored_rates() external view returns (uint256[] memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw_admin_fees() external;\n}\n" + }, + "contracts/interfaces/ICurveXChainLiquidityGauge.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveXChainLiquidityGauge {\n event Approval(\n address indexed _owner,\n address indexed _spender,\n uint256 _value\n );\n event Deposit(address indexed provider, uint256 value);\n event SetGaugeManager(address _gauge_manager);\n event Transfer(address indexed _from, address indexed _to, uint256 _value);\n event UpdateLiquidityLimit(\n address indexed user,\n uint256 original_balance,\n uint256 original_supply,\n uint256 working_balance,\n uint256 working_supply\n );\n event Withdraw(address indexed provider, uint256 value);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_reward(address _reward_token, address _distributor) external;\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function claim_rewards() external;\n\n function claim_rewards(address _addr) external;\n\n function claim_rewards(address _addr, address _receiver) external;\n\n function claimable_reward(address _user, address _reward_token)\n external\n view\n returns (uint256);\n\n function claimable_tokens(address addr) external returns (uint256);\n\n function claimed_reward(address _addr, address _token)\n external\n view\n returns (uint256);\n\n function decimals() external view returns (uint256);\n\n function decreaseAllowance(address _spender, uint256 _subtracted_value)\n external\n returns (bool);\n\n function deposit(uint256 _value) external;\n\n function deposit(uint256 _value, address _addr) external;\n\n function deposit(\n uint256 _value,\n address _addr,\n bool _claim_rewards\n ) external;\n\n function deposit_reward_token(address _reward_token, uint256 _amount)\n external;\n\n function deposit_reward_token(\n address _reward_token,\n uint256 _amount,\n uint256 _epoch\n ) external;\n\n function factory() external view returns (address);\n\n function increaseAllowance(address _spender, uint256 _added_value)\n external\n returns (bool);\n\n function inflation_rate(uint256 arg0) external view returns (uint256);\n\n function initialize(\n address _lp_token,\n address _root,\n address _manager\n ) external;\n\n function integrate_checkpoint() external view returns (uint256);\n\n function integrate_checkpoint_of(address arg0)\n external\n view\n returns (uint256);\n\n function integrate_fraction(address arg0) external view returns (uint256);\n\n function integrate_inv_supply(int128 arg0) external view returns (uint256);\n\n function integrate_inv_supply_of(address arg0)\n external\n view\n returns (uint256);\n\n function is_killed() external view returns (bool);\n\n function lp_token() external view returns (address);\n\n function manager() external view returns (address);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function period() external view returns (int128);\n\n function period_timestamp(int128 arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function recover_remaining(address _reward_token) external;\n\n function reward_count() external view returns (uint256);\n\n function reward_integral_for(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function reward_remaining(address arg0) external view returns (uint256);\n\n function reward_tokens(uint256 arg0) external view returns (address);\n\n function rewards_receiver(address arg0) external view returns (address);\n\n function root_gauge() external view returns (address);\n\n function set_gauge_manager(address _gauge_manager) external;\n\n function set_killed(bool _is_killed) external;\n\n function set_manager(address _gauge_manager) external;\n\n function set_reward_distributor(address _reward_token, address _distributor)\n external;\n\n function set_rewards_receiver(address _receiver) external;\n\n function set_root_gauge(address _root) external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function update_voting_escrow() external;\n\n function user_checkpoint(address addr) external returns (bool);\n\n function version() external view returns (string memory);\n\n function voting_escrow() external view returns (address);\n\n function withdraw(uint256 _value) external;\n\n function withdraw(uint256 _value, bool _claim_rewards) external;\n\n function withdraw(\n uint256 _value,\n bool _claim_rewards,\n address _receiver\n ) external;\n\n function working_balances(address arg0) external view returns (uint256);\n\n function working_supply() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/ICVXLocker.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface ICVXLocker {\n function lock(\n address _account,\n uint256 _amount,\n uint256 _spendRatio\n ) external;\n\n function lockedBalanceOf(address _account) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/IDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IDripper {\n /// @notice How much funds have dripped out already and are currently\n // available to be sent to the vault.\n /// @return The amount that would be sent if a collect was called\n function availableFunds() external view returns (uint256);\n\n /// @notice Collect all dripped funds and send to vault.\n /// Recalculate new drip rate.\n function collect() external;\n\n /// @notice Collect all dripped funds, send to vault, recalculate new drip\n /// rate, and rebase mToken.\n function collectAndRebase() external;\n\n /// @notice Change the drip duration. Governor only.\n /// @param _durationSeconds the number of seconds to drip out the entire\n /// balance over if no collects were called during that time.\n function setDripDuration(uint256 _durationSeconds) external;\n\n /// @dev Transfer out ERC20 tokens held by the contract. Governor only.\n /// @param _asset ERC20 token address\n /// @param _amount amount to transfer\n function transferToken(address _asset, uint256 _amount) external;\n}\n" + }, + "contracts/interfaces/IEthUsdOracle.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IEthUsdOracle {\n /**\n * @notice Returns ETH price in USD.\n * @return Price in USD with 6 decimal digits.\n */\n function ethUsdPrice() external view returns (uint256);\n\n /**\n * @notice Returns token price in USD.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in USD with 6 decimal digits.\n */\n function tokUsdPrice(string calldata symbol)\n external\n view\n returns (uint256);\n\n /**\n * @notice Returns the asset price in ETH.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in ETH with 8 decimal digits.\n */\n function tokEthPrice(string calldata symbol)\n external\n view\n returns (uint256);\n}\n\ninterface IViewEthUsdOracle {\n /**\n * @notice Returns ETH price in USD.\n * @return Price in USD with 6 decimal digits.\n */\n function ethUsdPrice() external view returns (uint256);\n\n /**\n * @notice Returns token price in USD.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in USD with 6 decimal digits.\n */\n function tokUsdPrice(string calldata symbol)\n external\n view\n returns (uint256);\n\n /**\n * @notice Returns the asset price in ETH.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in ETH with 8 decimal digits.\n */\n function tokEthPrice(string calldata symbol)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/IFraxETHMinter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IFraxETHMinter {\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares);\n}\n" + }, + "contracts/interfaces/IGetExchangeRateToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IGetExchangeRateToken {\n function getExchangeRate() external view returns (uint256 _exchangeRate);\n}\n" + }, + "contracts/interfaces/IMinMaxOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IMinMaxOracle {\n //Assuming 8 decimals\n function priceMin(string calldata symbol) external view returns (uint256);\n\n function priceMax(string calldata symbol) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IMockVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"./IVault.sol\";\n\ninterface IMockVault is IVault {\n function outstandingWithdrawalsAmount() external view returns (uint256);\n\n function wethAvailable() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IOETHZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOETHZapper {\n function deposit() external payable returns (uint256);\n}\n" + }, + "contracts/interfaces/IOneInch.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/// 1Inch swap data\nstruct SwapDescription {\n IERC20 srcToken; // contract address of a token to sell\n IERC20 dstToken; // contract address of a token to buy\n address payable srcReceiver;\n address payable dstReceiver; // Receiver of destination currency. default: fromAddress\n uint256 amount;\n uint256 minReturnAmount;\n uint256 flags;\n}\n\n/// @title Interface for making arbitrary calls during swap\ninterface IAggregationExecutor {\n /// @notice propagates information about original msg.sender and executes arbitrary data\n function execute(address msgSender) external payable; // 0x4b64e492\n}\n\ninterface IOneInchRouter {\n /// @notice Performs a swap, delegating all calls encoded in `data` to `executor`.\n function swap(\n IAggregationExecutor executor,\n SwapDescription calldata desc,\n bytes calldata permit,\n bytes calldata data\n ) external returns (uint256 returnAmount, uint256 spentAmount);\n\n /// @notice Performs swap using Uniswap exchange. Wraps and unwraps ETH if required.\n function unoswapTo(\n address payable recipient,\n IERC20 srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) external payable returns (uint256 returnAmount);\n\n /// @notice Performs swap using Uniswap V3 exchange. Wraps and unwraps ETH if required.\n function uniswapV3SwapTo(\n address payable recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) external payable returns (uint256 returnAmount);\n}\n" + }, + "contracts/interfaces/IOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOracle {\n /**\n * @dev returns the asset price in USD, in 8 decimal digits.\n *\n * The version of priceProvider deployed for OETH has 18 decimal digits\n */\n function price(address asset) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IOUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOUSD {\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n function _totalSupply() external view returns (uint256);\n\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address _account) external view returns (uint256);\n\n function burn(address account, uint256 amount) external;\n\n function changeSupply(uint256 _newTotalSupply) external;\n\n function claimGovernance() external;\n\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256);\n\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n );\n\n function decimals() external view returns (uint8);\n\n function decreaseAllowance(address _spender, uint256 _subtractedValue)\n external\n returns (bool);\n\n function governor() external view returns (address);\n\n function increaseAllowance(address _spender, uint256 _addedValue)\n external\n returns (bool);\n\n function initialize(\n string memory _nameArg,\n string memory _symbolArg,\n address _vaultAddress\n ) external;\n\n function isGovernor() external view returns (bool);\n\n function isUpgraded(address) external view returns (uint256);\n\n function mint(address _account, uint256 _amount) external;\n\n function name() external view returns (string memory);\n\n function nonRebasingCreditsPerToken(address)\n external\n view\n returns (uint256);\n\n function nonRebasingSupply() external view returns (uint256);\n\n function rebaseOptIn() external;\n\n function rebaseOptOut() external;\n\n function rebaseState(address) external view returns (uint8);\n\n function rebasingCredits() external view returns (uint256);\n\n function rebasingCreditsHighres() external view returns (uint256);\n\n function rebasingCreditsPerToken() external view returns (uint256);\n\n function rebasingCreditsPerTokenHighres() external view returns (uint256);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function transferGovernance(address _newGovernor) external;\n\n function vaultAddress() external view returns (address);\n}\n" + }, + "contracts/interfaces/IPriceOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPriceOracle {\n /**\n * @dev returns the asset price in USD, 6 decimal digits.\n * Compatible with the Open Price Feed.\n */\n function price(string calldata symbol) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IRETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IRETH {\n function getEthValue(uint256 _rethAmount) external view returns (uint256);\n\n function getRethValue(uint256 _ethAmount) external view returns (uint256);\n\n function getExchangeRate() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function balanceOf(address account) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 amount) external returns (bool);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/ISafe.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ISafe {\n function execTransactionFromModule(\n address,\n uint256,\n bytes memory,\n uint8\n ) external returns (bool);\n}\n" + }, + "contracts/interfaces/ISfrxETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface ISfrxETH {\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 amount\n );\n event Deposit(\n address indexed caller,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n event NewRewardsCycle(uint32 indexed cycleEnd, uint256 rewardAmount);\n event Transfer(address indexed from, address indexed to, uint256 amount);\n event Withdraw(\n address indexed caller,\n address indexed receiver,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address spender, uint256 amount) external returns (bool);\n\n function asset() external view returns (address);\n\n function balanceOf(address) external view returns (uint256);\n\n function convertToAssets(uint256 shares) external view returns (uint256);\n\n function convertToShares(uint256 assets) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit(uint256 assets, address receiver)\n external\n returns (uint256 shares);\n\n function depositWithSignature(\n uint256 assets,\n address receiver,\n uint256 deadline,\n bool approveMax,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external returns (uint256 shares);\n\n function lastRewardAmount() external view returns (uint192);\n\n function lastSync() external view returns (uint32);\n\n function maxDeposit(address) external view returns (uint256);\n\n function maxMint(address) external view returns (uint256);\n\n function maxRedeem(address owner) external view returns (uint256);\n\n function maxWithdraw(address owner) external view returns (uint256);\n\n function mint(uint256 shares, address receiver)\n external\n returns (uint256 assets);\n\n function name() external view returns (string memory);\n\n function nonces(address) external view returns (uint256);\n\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function previewDeposit(uint256 assets) external view returns (uint256);\n\n function previewMint(uint256 shares) external view returns (uint256);\n\n function previewRedeem(uint256 shares) external view returns (uint256);\n\n function previewWithdraw(uint256 assets) external view returns (uint256);\n\n function pricePerShare() external view returns (uint256);\n\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets);\n\n function rewardsCycleEnd() external view returns (uint32);\n\n function rewardsCycleLength() external view returns (uint32);\n\n function symbol() external view returns (string memory);\n\n function syncRewards() external;\n\n function totalAssets() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address to, uint256 amount) external returns (bool);\n\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/ISwapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ISwapper {\n /**\n * @param fromAsset The token address of the asset being sold.\n * @param toAsset The token address of the asset being purchased.\n * @param fromAssetAmount The amount of assets being sold.\n * @param minToAssetAmmount The minimum amount of assets to be purchased.\n * @param data tx.data returned from 1Inch's /v5.0/1/swap API\n */\n function swap(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n}\n" + }, + "contracts/interfaces/ITimelock.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ITimelock {\n function delay() external view returns (uint256);\n\n function GRACE_PERIOD() external view returns (uint256);\n\n function acceptAdmin() external;\n\n function queuedTransactions(bytes32 hash) external view returns (bool);\n\n function queueTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external returns (bytes32);\n\n function cancelTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external;\n\n function executeTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external payable returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ITimelockController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ITimelockController {\n function grantRole(bytes32 role, address account) external;\n\n function revokeRole(bytes32 role, address account) external;\n\n function renounceRole(bytes32 role, address account) external;\n\n function hasRole(bytes32 role, address account)\n external\n view\n returns (bool);\n\n function executeBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt\n ) external payable;\n\n function scheduleBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt,\n uint256 delay\n ) external;\n\n function hashOperationBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt\n ) external view returns (bytes32);\n\n function isOperationDone(bytes32 opHash) external view returns (bool);\n\n function isOperationReady(bytes32 opHash) external view returns (bool);\n\n function isOperation(bytes32 opHash) external view returns (bool);\n\n function getMinDelay() external view returns (uint256);\n\n function updateDelay(uint256 newDelay) external;\n\n function CANCELLER_ROLE() external view returns (bytes32);\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/interfaces/IWstETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWstETH {\n /**\n * @notice Get amount of wstETH for a given amount of stETH\n * @param _stETHAmount amount of stETH\n * @return Amount of wstETH for a given stETH amount\n */\n function getWstETHByStETH(uint256 _stETHAmount)\n external\n view\n returns (uint256);\n\n /**\n * @notice Get amount of stETH for a given amount of wstETH\n * @param _wstETHAmount amount of wstETH\n * @return Amount of stETH for a given wstETH amount\n */\n function getStETHByWstETH(uint256 _wstETHAmount)\n external\n view\n returns (uint256);\n\n /**\n * @notice Get amount of stETH for a one wstETH\n * @return Amount of stETH for 1 wstETH\n */\n function stEthPerToken() external view returns (uint256);\n\n /**\n * @notice Get amount of wstETH for a one stETH\n * @return Amount of wstETH for a 1 stETH\n */\n function tokensPerStEth() external view returns (uint256);\n\n /**\n * @notice Exchanges stETH to wstETH\n * @param _stETHAmount amount of stETH to wrap in exchange for wstETH\n * @dev Requirements:\n * - `_stETHAmount` must be non-zero\n * - msg.sender must approve at least `_stETHAmount` stETH to this\n * contract.\n * - msg.sender must have at least `_stETHAmount` of stETH.\n * User should first approve _stETHAmount to the WstETH contract\n * @return Amount of wstETH user receives after wrap\n */\n function wrap(uint256 _stETHAmount) external returns (uint256);\n\n /**\n * @notice Exchanges wstETH to stETH\n * @param _wstETHAmount amount of wstETH to uwrap in exchange for stETH\n * @dev Requirements:\n * - `_wstETHAmount` must be non-zero\n * - msg.sender must have at least `_wstETHAmount` wstETH.\n * @return Amount of stETH user receives after unwrap\n */\n function unwrap(uint256 _wstETHAmount) external returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/compound/ICompoundOracle.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\ninterface ICompoundOracle {\n function getUnderlyingPrice(address) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/ILens.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\nimport \"./compound/ICompoundOracle.sol\";\nimport \"./IMorpho.sol\";\n\ninterface ILens {\n /// STORAGE ///\n\n function MAX_BASIS_POINTS() external view returns (uint256);\n\n function WAD() external view returns (uint256);\n\n function morpho() external view returns (IMorpho);\n\n function comptroller() external view returns (IComptroller);\n\n /// GENERAL ///\n\n function getTotalSupply()\n external\n view\n returns (\n uint256 p2pSupplyAmount,\n uint256 poolSupplyAmount,\n uint256 totalSupplyAmount\n );\n\n function getTotalBorrow()\n external\n view\n returns (\n uint256 p2pBorrowAmount,\n uint256 poolBorrowAmount,\n uint256 totalBorrowAmount\n );\n\n /// MARKETS ///\n\n function isMarketCreated(address _poolToken) external view returns (bool);\n\n function isMarketCreatedAndNotPaused(address _poolToken)\n external\n view\n returns (bool);\n\n function isMarketCreatedAndNotPausedNorPartiallyPaused(address _poolToken)\n external\n view\n returns (bool);\n\n function getAllMarkets()\n external\n view\n returns (address[] memory marketsCreated_);\n\n function getMainMarketData(address _poolToken)\n external\n view\n returns (\n uint256 avgSupplyRatePerBlock,\n uint256 avgBorrowRatePerBlock,\n uint256 p2pSupplyAmount,\n uint256 p2pBorrowAmount,\n uint256 poolSupplyAmount,\n uint256 poolBorrowAmount\n );\n\n function getAdvancedMarketData(address _poolToken)\n external\n view\n returns (\n uint256 p2pSupplyIndex,\n uint256 p2pBorrowIndex,\n uint256 poolSupplyIndex,\n uint256 poolBorrowIndex,\n uint32 lastUpdateBlockNumber,\n uint256 p2pSupplyDelta,\n uint256 p2pBorrowDelta\n );\n\n function getMarketConfiguration(address _poolToken)\n external\n view\n returns (\n address underlying,\n bool isCreated,\n bool p2pDisabled,\n bool isPaused,\n bool isPartiallyPaused,\n uint16 reserveFactor,\n uint16 p2pIndexCursor,\n uint256 collateralFactor\n );\n\n function getTotalMarketSupply(address _poolToken)\n external\n view\n returns (uint256 p2pSupplyAmount, uint256 poolSupplyAmount);\n\n function getTotalMarketBorrow(address _poolToken)\n external\n view\n returns (uint256 p2pBorrowAmount, uint256 poolBorrowAmount);\n\n /// INDEXES ///\n\n function getCurrentP2PSupplyIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentP2PBorrowIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentPoolIndexes(address _poolToken)\n external\n view\n returns (\n uint256 currentPoolSupplyIndex,\n uint256 currentPoolBorrowIndex\n );\n\n function getIndexes(address _poolToken, bool _computeUpdatedIndexes)\n external\n view\n returns (\n uint256 p2pSupplyIndex,\n uint256 p2pBorrowIndex,\n uint256 poolSupplyIndex,\n uint256 poolBorrowIndex\n );\n\n /// USERS ///\n\n function getEnteredMarkets(address _user)\n external\n view\n returns (address[] memory enteredMarkets);\n\n function getUserHealthFactor(\n address _user,\n address[] calldata _updatedMarkets\n ) external view returns (uint256);\n\n function getUserBalanceStates(\n address _user,\n address[] calldata _updatedMarkets\n )\n external\n view\n returns (\n uint256 collateralValue,\n uint256 debtValue,\n uint256 maxDebtValue\n );\n\n function getCurrentSupplyBalanceInOf(address _poolToken, address _user)\n external\n view\n returns (\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getCurrentBorrowBalanceInOf(address _poolToken, address _user)\n external\n view\n returns (\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getUserMaxCapacitiesForAsset(address _user, address _poolToken)\n external\n view\n returns (uint256 withdrawable, uint256 borrowable);\n\n function getUserHypotheticalBalanceStates(\n address _user,\n address _poolToken,\n uint256 _withdrawnAmount,\n uint256 _borrowedAmount\n ) external view returns (uint256 debtValue, uint256 maxDebtValue);\n\n function getUserLiquidityDataForAsset(\n address _user,\n address _poolToken,\n bool _computeUpdatedIndexes,\n ICompoundOracle _oracle\n ) external view returns (Types.AssetLiquidityData memory assetData);\n\n function isLiquidatable(address _user, address[] memory _updatedMarkets)\n external\n view\n returns (bool);\n\n function computeLiquidationRepayAmount(\n address _user,\n address _poolTokenBorrowed,\n address _poolTokenCollateral,\n address[] calldata _updatedMarkets\n ) external view returns (uint256 toRepay);\n\n /// RATES ///\n\n function getAverageSupplyRatePerBlock(address _poolToken)\n external\n view\n returns (\n uint256 avgSupplyRatePerBlock,\n uint256 p2pSupplyAmount,\n uint256 poolSupplyAmount\n );\n\n function getAverageBorrowRatePerBlock(address _poolToken)\n external\n view\n returns (\n uint256 avgBorrowRatePerBlock,\n uint256 p2pBorrowAmount,\n uint256 poolBorrowAmount\n );\n\n function getNextUserSupplyRatePerBlock(\n address _poolToken,\n address _user,\n uint256 _amount\n )\n external\n view\n returns (\n uint256 nextSupplyRatePerBlock,\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getNextUserBorrowRatePerBlock(\n address _poolToken,\n address _user,\n uint256 _amount\n )\n external\n view\n returns (\n uint256 nextBorrowRatePerBlock,\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getCurrentUserSupplyRatePerBlock(address _poolToken, address _user)\n external\n view\n returns (uint256);\n\n function getCurrentUserBorrowRatePerBlock(address _poolToken, address _user)\n external\n view\n returns (uint256);\n\n function getRatesPerBlock(address _poolToken)\n external\n view\n returns (\n uint256 p2pSupplyRate,\n uint256 p2pBorrowRate,\n uint256 poolSupplyRate,\n uint256 poolBorrowRate\n );\n\n /// REWARDS ///\n\n function getUserUnclaimedRewards(\n address[] calldata _poolTokens,\n address _user\n ) external view returns (uint256 unclaimedRewards);\n\n function getAccruedSupplierComp(\n address _supplier,\n address _poolToken,\n uint256 _balance\n ) external view returns (uint256);\n\n function getAccruedBorrowerComp(\n address _borrower,\n address _poolToken,\n uint256 _balance\n ) external view returns (uint256);\n\n function getCurrentCompSupplyIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentCompBorrowIndex(address _poolToken)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/IMorpho.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\nimport \"./Types.sol\";\nimport \"../IComptroller.sol\";\nimport \"./compound/ICompoundOracle.sol\";\n\n// prettier-ignore\ninterface IMorpho {\n function comptroller() external view returns (IComptroller);\n function supply(address _poolTokenAddress, address _onBehalf, uint256 _amount) external;\n function supply(address _poolTokenAddress, address _onBehalf, uint256 _amount, uint256 _maxGasForMatching) external;\n function withdraw(address _poolTokenAddress, uint256 _amount) external;\n function claimRewards(\n address[] calldata _cTokenAddresses,\n bool _tradeForMorphoToken\n ) external returns (uint256 claimedAmount);\n}\n" + }, + "contracts/interfaces/morpho/Types.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\n/// @title Types.\n/// @author Morpho Labs.\n/// @custom:contact security@morpho.xyz\n/// @dev Common types and structs used in Moprho contracts.\nlibrary Types {\n /// ENUMS ///\n\n enum PositionType {\n SUPPLIERS_IN_P2P,\n SUPPLIERS_ON_POOL,\n BORROWERS_IN_P2P,\n BORROWERS_ON_POOL\n }\n\n /// STRUCTS ///\n\n struct SupplyBalance {\n uint256 inP2P; // In supplier's peer-to-peer unit, a unit that grows in underlying value, to keep track of the interests earned by suppliers in peer-to-peer. Multiply by the peer-to-peer supply index to get the underlying amount.\n uint256 onPool; // In cToken. Multiply by the pool supply index to get the underlying amount.\n }\n\n struct BorrowBalance {\n uint256 inP2P; // In borrower's peer-to-peer unit, a unit that grows in underlying value, to keep track of the interests paid by borrowers in peer-to-peer. Multiply by the peer-to-peer borrow index to get the underlying amount.\n uint256 onPool; // In cdUnit, a unit that grows in value, to keep track of the debt increase when borrowers are on Compound. Multiply by the pool borrow index to get the underlying amount.\n }\n\n // Max gas to consume during the matching process for supply, borrow, withdraw and repay functions.\n struct MaxGasForMatching {\n uint64 supply;\n uint64 borrow;\n uint64 withdraw;\n uint64 repay;\n }\n\n struct Delta {\n uint256 p2pSupplyDelta; // Difference between the stored peer-to-peer supply amount and the real peer-to-peer supply amount (in pool supply unit).\n uint256 p2pBorrowDelta; // Difference between the stored peer-to-peer borrow amount and the real peer-to-peer borrow amount (in pool borrow unit).\n uint256 p2pSupplyAmount; // Sum of all stored peer-to-peer supply (in peer-to-peer supply unit).\n uint256 p2pBorrowAmount; // Sum of all stored peer-to-peer borrow (in peer-to-peer borrow unit).\n }\n\n struct AssetLiquidityData {\n uint256 collateralValue; // The collateral value of the asset.\n uint256 maxDebtValue; // The maximum possible debt value of the asset.\n uint256 debtValue; // The debt value of the asset.\n uint256 underlyingPrice; // The price of the token.\n uint256 collateralFactor; // The liquidation threshold applied on this token.\n }\n\n struct LiquidityData {\n uint256 collateralValue; // The collateral value.\n uint256 maxDebtValue; // The maximum debt value possible.\n uint256 debtValue; // The debt value.\n }\n\n // Variables are packed together to save gas (will not exceed their limit during Morpho's lifetime).\n struct LastPoolIndexes {\n uint32 lastUpdateBlockNumber; // The last time the peer-to-peer indexes were updated.\n uint112 lastSupplyPoolIndex; // Last pool supply index.\n uint112 lastBorrowPoolIndex; // Last pool borrow index.\n }\n\n struct MarketParameters {\n uint16 reserveFactor; // Proportion of the interest earned by users sent to the DAO for each market, in basis point (100% = 10 000). The value is set at market creation.\n uint16 p2pIndexCursor; // Position of the peer-to-peer rate in the pool's spread. Determine the weights of the weighted arithmetic average in the indexes computations ((1 - p2pIndexCursor) * r^S + p2pIndexCursor * r^B) (in basis point).\n }\n\n struct MarketStatus {\n bool isCreated; // Whether or not this market is created.\n bool isPaused; // Whether the market is paused or not (all entry points on Morpho are frozen; supply, borrow, withdraw, repay and liquidate).\n bool isPartiallyPaused; // Whether the market is partially paused or not (only supply and borrow are frozen).\n }\n}\n" + }, + "contracts/interfaces/plume/IFeeRegistry.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\ninterface IFeeRegistry {\n function registerFee(\n bool isTokenA,\n uint32 binId,\n uint256 binFeeInQuote\n ) external;\n}\n" + }, + "contracts/interfaces/plume/ILiquidityRegistry.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface ILiquidityRegistry {\n function notifyBinLiquidity(\n IMaverickV2Pool pool,\n uint256 tokenId,\n uint32 binId,\n uint256 currentBinLpBalance\n ) external;\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Factory.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IFeeRegistry } from \"./IFeeRegistry.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2Factory {\n error FactorAlreadyInitialized();\n error FactorNotInitialized();\n error FactoryInvalidTokenOrder(IERC20 _tokenA, IERC20 _tokenB);\n error FactoryInvalidFee();\n error FactoryInvalidKinds(uint8 kinds);\n error FactoryInvalidTickSpacing(uint256 tickSpacing);\n error FactoryInvalidLookback(uint256 lookback);\n error FactoryInvalidTokenDecimals(uint8 decimalsA, uint8 decimalsB);\n error FactoryPoolAlreadyExists(\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds,\n address accessor\n );\n error FactoryAccessorMustBeNonZero();\n error NotImplemented();\n\n event PoolCreated(\n IMaverickV2Pool poolAddress,\n uint8 protocolFeeRatio,\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n int32 activeTick,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds,\n address accessor\n );\n event SetFactoryProtocolFeeReceiver(address receiver);\n event SetFactoryProtocolFeeRegistry(IFeeRegistry registry);\n\n struct DeployParameters {\n uint64 feeAIn;\n uint64 feeBIn;\n uint32 lookback;\n int32 activeTick;\n uint64 tokenAScale;\n uint64 tokenBScale;\n // slot\n IERC20 tokenA;\n // slot\n IERC20 tokenB;\n // slot\n uint16 tickSpacing;\n uint8 options;\n address accessor;\n }\n\n /**\n * @notice Called by deployer library to initialize a pool.\n */\n function deployParameters()\n external\n view\n returns (\n uint64 feeAIn,\n uint64 feeBIn,\n uint32 lookback,\n int32 activeTick,\n uint64 tokenAScale,\n uint64 tokenBScale,\n // slot\n IERC20 tokenA,\n // slot\n IERC20 tokenB,\n // slot\n uint16 tickSpacing,\n uint8 options,\n address accessor\n );\n\n /**\n * @notice Create a new MaverickV2Pool with symmetric swap fees.\n * @param fee Fraction of the pool swap amount that is retained as an LP in\n * D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * E.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function create(\n uint64 fee,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external returns (IMaverickV2Pool);\n\n /**\n * @notice Create a new MaverickV2Pool.\n * @param feeAIn Fraction of the pool swap amount for tokenA-input swaps\n * that is retained as an LP in D18 scale.\n * @param feeBIn Fraction of the pool swap amount for tokenB-input swaps\n * that is retained as an LP in D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * e.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function create(\n uint64 feeAIn,\n uint64 feeBIn,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external returns (IMaverickV2Pool);\n\n /**\n * @notice Bool indicating whether the pool was deployed from this factory.\n */\n function isFactoryPool(IMaverickV2Pool pool) external view returns (bool);\n\n /**\n * @notice Address that receives the protocol fee\n */\n function protocolFeeReceiver() external view returns (address);\n\n /**\n * @notice Address notified on swaps of the protocol fee\n */\n function protocolFeeRegistry() external view returns (IFeeRegistry);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds\n ) external view returns (IMaverickV2Pool);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(\n IERC20 _tokenA,\n IERC20 _tokenB,\n uint256 startIndex,\n uint256 endIndex\n ) external view returns (IMaverickV2Pool[] memory pools);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(uint256 startIndex, uint256 endIndex)\n external\n view\n returns (IMaverickV2Pool[] memory pools);\n\n /**\n * @notice Count of permissionless pools.\n */\n function poolCount() external view returns (uint256 _poolCount);\n\n /**\n * @notice Count of pools for a given accessor and token pair. For\n * permissionless pools, pass `accessor = address(0)`.\n */\n function poolByTokenCount(\n IERC20 _tokenA,\n IERC20 _tokenB,\n address accessor\n ) external view returns (uint256 _poolCount);\n\n /**\n * @notice Get the current factory owner.\n */\n function owner() external view returns (address);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2LiquidityManager.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\nimport { IMaverickV2Position } from \"./IMaverickV2Position.sol\";\nimport { IMaverickV2PoolLens } from \"./IMaverickV2PoolLens.sol\";\n\ninterface IMaverickV2LiquidityManager {\n error LiquidityManagerNotFactoryPool();\n error LiquidityManagerNotTokenIdOwner();\n\n /**\n * @notice Maverick V2 NFT position contract that tracks NFT-based\n * liquditiy positions.\n */\n function position() external view returns (IMaverickV2Position);\n\n /**\n * @notice Create Maverick V2 pool. Function is a pass through to the pool\n * factory and is provided here so that is can be assembled as part of a\n * multicall transaction.\n */\n function createPool(\n uint64 fee,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external payable returns (IMaverickV2Pool pool);\n\n /**\n * @notice Add Liquidity position NFT for msg.sender by specifying\n * msg.sender's token index.\n * @dev Token index is different from tokenId.\n * On the Position NFT contract a user can own multiple NFT tokenIds and\n * these are indexes by an enumeration index which is the `index` input\n * here.\n *\n * See addLiquidity for a description of the add params.\n */\n function addPositionLiquidityToSenderByTokenIndex(\n IMaverickV2Pool pool,\n uint256 index,\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs\n )\n external\n payable\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds\n );\n\n /**\n * @notice Mint new tokenId in the Position NFt contract to msg.sender.\n * Both mints an NFT and adds liquidity to the pool that is held by the\n * NFT.\n */\n function mintPositionNftToSender(\n IMaverickV2Pool pool,\n bytes calldata packedSqrtPriceBreaks,\n bytes[] calldata packedArgs\n )\n external\n payable\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds,\n uint256 tokenId\n );\n\n /**\n * @notice Donates liqudity to a pool that is held by the position contract\n * and will never be retrievable. Can be used to start a pool and ensure\n * there will always be a base level of liquditiy in the pool.\n */\n function donateLiquidity(\n IMaverickV2Pool pool,\n IMaverickV2Pool.AddLiquidityParams memory args\n ) external payable;\n\n /**\n * @notice Packs sqrtPrice breaks array with this format: [length,\n * array[0], array[1],..., array[length-1]] where length is 1 byte.\n */\n function packUint88Array(uint88[] memory fullArray)\n external\n pure\n returns (bytes memory packedArray);\n\n /**\n * @notice Packs addLiquidity paramters array element-wise.\n */\n function packAddLiquidityArgsArray(\n IMaverickV2Pool.AddLiquidityParams[] memory args\n ) external pure returns (bytes[] memory argsPacked);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Pool.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\n\ninterface IMaverickV2Pool {\n error PoolZeroLiquidityAdded();\n error PoolMinimumLiquidityNotMet();\n error PoolLocked();\n error PoolInvalidFee();\n error PoolTicksNotSorted(uint256 index, int256 previousTick, int256 tick);\n error PoolTicksAmountsLengthMismatch(\n uint256 ticksLength,\n uint256 amountsLength\n );\n error PoolBinIdsAmountsLengthMismatch(\n uint256 binIdsLength,\n uint256 amountsLength\n );\n error PoolKindNotSupported(uint256 kinds, uint256 kind);\n error PoolInsufficientBalance(\n uint256 deltaLpAmount,\n uint256 accountBalance\n );\n error PoolReservesExceedMaximum(uint256 amount);\n error PoolValueExceedsBits(uint256 amount, uint256 bits);\n error PoolTickMaxExceeded(uint256 tick);\n error PoolMigrateBinFirst();\n error PoolCurrentTickBeyondSwapLimit(int32 startingTick);\n error PoolSenderNotAccessor(address sender_, address accessor);\n error PoolSenderNotFactory(address sender_, address accessor);\n error PoolFunctionNotImplemented();\n error PoolTokenNotSolvent(\n uint256 internalReserve,\n uint256 tokenBalance,\n IERC20 token\n );\n error PoolNoProtocolFeeReceiverSet();\n\n event PoolSwap(\n address sender,\n address recipient,\n SwapParams params,\n uint256 amountIn,\n uint256 amountOut\n );\n event PoolFlashLoan(\n address sender,\n address recipient,\n uint256 amountA,\n uint256 amountB\n );\n event PoolProtocolFeeCollected(IERC20 token, uint256 protocolFee);\n\n event PoolAddLiquidity(\n address sender,\n address recipient,\n uint256 subaccount,\n AddLiquidityParams params,\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] binIds\n );\n\n event PoolMigrateBinsUpStack(\n address sender,\n uint32 binId,\n uint32 maxRecursion\n );\n\n event PoolRemoveLiquidity(\n address sender,\n address recipient,\n uint256 subaccount,\n RemoveLiquidityParams params,\n uint256 tokenAOut,\n uint256 tokenBOut\n );\n\n event PoolTickState(int32 tick, uint256 reserveA, uint256 reserveB);\n event PoolTickBinUpdate(int32 tick, uint8 kind, uint32 binId);\n event PoolSqrtPrice(uint256 sqrtPrice);\n\n /**\n * @notice Tick state parameters.\n */\n struct TickState {\n uint128 reserveA;\n uint128 reserveB;\n uint128 totalSupply;\n uint32[4] binIdsByTick;\n }\n\n /**\n * @notice Tick data parameters.\n * @param currentReserveA Current reserve of token A.\n * @param currentReserveB Current reserve of token B.\n * @param currentLiquidity Current liquidity amount.\n */\n struct TickData {\n uint256 currentReserveA;\n uint256 currentReserveB;\n uint256 currentLiquidity;\n }\n\n /**\n * @notice Bin state parameters.\n * @param mergeBinBalance LP token balance that this bin possesses of the merge bin.\n * @param mergeId Bin ID of the bin that this bin has merged into.\n * @param totalSupply Total amount of LP tokens in this bin.\n * @param kind One of the 4 kinds (0=static, 1=right, 2=left, 3=both).\n * @param tick The lower price tick of the bin in its current state.\n * @param tickBalance Balance of the tick.\n */\n struct BinState {\n uint128 mergeBinBalance;\n uint128 tickBalance;\n uint128 totalSupply;\n uint8 kind;\n int32 tick;\n uint32 mergeId;\n }\n\n /**\n * @notice Parameters for swap.\n * @param amount Amount of the token that is either the input if exactOutput is false\n * or the output if exactOutput is true.\n * @param tokenAIn Boolean indicating whether tokenA is the input.\n * @param exactOutput Boolean indicating whether the amount specified is\n * the exact output amount (true).\n * @param tickLimit The furthest tick a swap will execute in. If no limit\n * is desired, value should be set to type(int32).max for a tokenAIn swap\n * and type(int32).min for a swap where tokenB is the input.\n */\n struct SwapParams {\n uint256 amount;\n bool tokenAIn;\n bool exactOutput;\n int32 tickLimit;\n }\n\n /**\n * @notice Parameters associated with adding liquidity.\n * @param kind One of the 4 kinds (0=static, 1=right, 2=left, 3=both).\n * @param ticks Array of ticks to add liquidity to.\n * @param amounts Array of bin LP amounts to add.\n */\n struct AddLiquidityParams {\n uint8 kind;\n int32[] ticks;\n uint128[] amounts;\n }\n\n /**\n * @notice Parameters for each bin that will have liquidity removed.\n * @param binIds Index array of the bins losing liquidity.\n * @param amounts Array of bin LP amounts to remove.\n */\n struct RemoveLiquidityParams {\n uint32[] binIds;\n uint128[] amounts;\n }\n\n /**\n * @notice State of the pool.\n * @param reserveA Pool tokenA balanceOf at end of last operation\n * @param reserveB Pool tokenB balanceOf at end of last operation\n * @param lastTwaD8 Value of log time weighted average price at last block.\n * Value is 8-decimal scale and is in the fractional tick domain. E.g. a\n * value of 12.3e8 indicates the TWAP was 3/10ths of the way into the 12th\n * tick.\n * @param lastLogPriceD8 Value of log price at last block. Value is\n * 8-decimal scale and is in the fractional tick domain. E.g. a value of\n * 12.3e8 indicates the price was 3/10ths of the way into the 12th tick.\n * @param lastTimestamp Last block.timestamp value in seconds for latest\n * swap transaction.\n * @param activeTick Current tick position that contains the active bins.\n * @param isLocked Pool isLocked, E.g., locked or unlocked; isLocked values\n * defined in Pool.sol.\n * @param binCounter Index of the last bin created.\n * @param protocolFeeRatioD3 Ratio of the swap fee that is kept for the\n * protocol.\n */\n struct State {\n uint128 reserveA;\n uint128 reserveB;\n int64 lastTwaD8;\n int64 lastLogPriceD8;\n uint40 lastTimestamp;\n int32 activeTick;\n bool isLocked;\n uint32 binCounter;\n uint8 protocolFeeRatioD3;\n }\n\n /**\n * @notice Internal data used for data passing between Pool and Bin code.\n */\n struct BinDelta {\n uint128 deltaA;\n uint128 deltaB;\n }\n\n /**\n * @notice 1-15 number to represent the active kinds.\n * @notice 0b0001 = static;\n * @notice 0b0010 = right;\n * @notice 0b0100 = left;\n * @notice 0b1000 = both;\n *\n * E.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function kinds() external view returns (uint8 _kinds);\n\n /**\n * @notice Pool swap fee for the given direction (A-in or B-in swap) in\n * 18-decimal format. E.g. 0.01e18 is a 1% swap fee.\n */\n function fee(bool tokenAIn) external view returns (uint256);\n\n /**\n * @notice TickSpacing of pool where 1.0001^tickSpacing is the bin width.\n */\n function tickSpacing() external view returns (uint256);\n\n /**\n * @notice Lookback period of pool in seconds.\n */\n function lookback() external view returns (uint256);\n\n /**\n * @notice Address of Pool accessor. This is Zero address for\n * permissionless pools.\n */\n function accessor() external view returns (address);\n\n /**\n * @notice Pool tokenA. Address of tokenA is such that tokenA < tokenB.\n */\n function tokenA() external view returns (IERC20);\n\n /**\n * @notice Pool tokenB.\n */\n function tokenB() external view returns (IERC20);\n\n /**\n * @notice Deploying factory of the pool and also contract that has ability\n * to set and collect protocol fees for the pool.\n */\n function factory() external view returns (IMaverickV2Factory);\n\n /**\n * @notice Most significant bit of scale value is a flag to indicate whether\n * tokenA has more or less than 18 decimals. Scale is used in conjuction\n * with Math.toScale/Math.fromScale functions to convert from token amounts\n * to D18 scale internal pool accounting.\n */\n function tokenAScale() external view returns (uint256);\n\n /**\n * @notice Most significant bit of scale value is a flag to indicate whether\n * tokenA has more or less than 18 decimals. Scale is used in conjuction\n * with Math.toScale/Math.fromScale functions to convert from token amounts\n * to D18 scale internal pool accounting.\n */\n function tokenBScale() external view returns (uint256);\n\n /**\n * @notice ID of bin at input tick position and kind.\n */\n function binIdByTickKind(int32 tick, uint256 kind)\n external\n view\n returns (uint32);\n\n /**\n * @notice Accumulated tokenA protocol fee.\n */\n function protocolFeeA() external view returns (uint128);\n\n /**\n * @notice Accumulated tokenB protocol fee.\n */\n function protocolFeeB() external view returns (uint128);\n\n /**\n * @notice Lending fee rate on flash loans.\n */\n function lendingFeeRateD18() external view returns (uint256);\n\n /**\n * @notice External function to get the current time-weighted average price.\n */\n function getCurrentTwa() external view returns (int256);\n\n /**\n * @notice External function to get the state of the pool.\n */\n function getState() external view returns (State memory);\n\n /**\n * @notice Return state of Bin at input binId.\n */\n function getBin(uint32 binId) external view returns (BinState memory bin);\n\n /**\n * @notice Return state of Tick at input tick position.\n */\n function getTick(int32 tick)\n external\n view\n returns (TickState memory tickState);\n\n /**\n * @notice Retrieves the balance of a user within a bin.\n * @param user The user's address.\n * @param subaccount The subaccount for the user.\n * @param binId The ID of the bin.\n */\n function balanceOf(\n address user,\n uint256 subaccount,\n uint32 binId\n ) external view returns (uint128 lpToken);\n\n /**\n * @notice Add liquidity to a pool. This function allows users to deposit\n * tokens into a liquidity pool.\n * @dev This function will call `maverickV2AddLiquidityCallback` on the\n * calling contract to collect the tokenA/tokenB payment.\n * @param recipient The account that will receive credit for the added liquidity.\n * @param subaccount The account that will receive credit for the added liquidity.\n * @param params Parameters containing the details for adding liquidity,\n * such as token types and amounts.\n * @param data Bytes information that gets passed to the callback.\n * @return tokenAAmount The amount of token A added to the pool.\n * @return tokenBAmount The amount of token B added to the pool.\n * @return binIds An array of bin IDs where the liquidity is stored.\n */\n function addLiquidity(\n address recipient,\n uint256 subaccount,\n AddLiquidityParams calldata params,\n bytes calldata data\n )\n external\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds\n );\n\n /**\n * @notice Removes liquidity from the pool.\n * @dev Liquidy can only be removed from a bin that is either unmerged or\n * has a mergeId of an unmerged bin. If a bin is merged more than one\n * level deep, it must be migrated up the merge stack to the root bin\n * before liquidity removal.\n * @param recipient The address to receive the tokens.\n * @param subaccount The subaccount for the recipient.\n * @param params The parameters for removing liquidity.\n * @return tokenAOut The amount of token A received.\n * @return tokenBOut The amount of token B received.\n */\n function removeLiquidity(\n address recipient,\n uint256 subaccount,\n RemoveLiquidityParams calldata params\n ) external returns (uint256 tokenAOut, uint256 tokenBOut);\n\n /**\n * @notice Migrate bins up the linked list of merged bins so that its\n * mergeId is the currrent active bin.\n * @dev Liquidy can only be removed from a bin that is either unmerged or\n * has a mergeId of an unmerged bin. If a bin is merged more than one\n * level deep, it must be migrated up the merge stack to the root bin\n * before liquidity removal.\n * @param binId The ID of the bin to migrate.\n * @param maxRecursion The maximum recursion depth for the migration.\n */\n function migrateBinUpStack(uint32 binId, uint32 maxRecursion) external;\n\n /**\n * @notice Swap tokenA/tokenB assets in the pool. The swap user has two\n * options for funding their swap.\n * - The user can push the input token amount to the pool before calling\n * the swap function. In order to avoid having the pool call the callback,\n * the user should pass a zero-length `data` bytes object with the swap\n * call.\n * - The user can send the input token amount to the pool when the pool\n * calls the `maverickV2SwapCallback` function on the calling contract.\n * That callback has input parameters that specify the token address of the\n * input token, the input and output amounts, and the bytes data sent to\n * the swap function.\n * @dev If the users elects to do a callback-based swap, the output\n * assets will be sent before the callback is called, allowing the user to\n * execute flash swaps. However, the pool does have reentrancy protection,\n * so a swapper will not be able to interact with the same pool again\n * while they are in the callback function.\n * @param recipient The address to receive the output tokens.\n * @param params Parameters containing the details of the swap\n * @param data Bytes information that gets passed to the callback.\n */\n function swap(\n address recipient,\n SwapParams memory params,\n bytes calldata data\n ) external returns (uint256 amountIn, uint256 amountOut);\n\n /**\n * @notice Loan tokenA/tokenB assets from the pool to recipient. The fee\n * rate of a loan is determined by `lendingFeeRateD18`, which is set at the\n * protocol level by the factory. This function calls\n * `maverickV2FlashLoanCallback` on the calling contract. At the end of\n * the callback, the caller must pay back the loan with fee (if there is a\n * fee).\n * @param recipient The address to receive the loaned tokens.\n * @param amountB Loan amount of tokenA sent to recipient.\n * @param amountB Loan amount of tokenB sent to recipient.\n * @param data Bytes information that gets passed to the callback.\n */\n function flashLoan(\n address recipient,\n uint256 amountA,\n uint256 amountB,\n bytes calldata data\n ) external;\n\n /**\n * @notice Distributes accumulated protocol fee to factory protocolFeeReceiver\n */\n function distributeFees(bool isTokenA)\n external\n returns (uint256 protocolFee, IERC20 token);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2PoolLens.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2PoolLens {\n error LensTargetPriceOutOfBounds(\n uint256 targetSqrtPrice,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n );\n error LensTooLittleLiquidity(\n uint256 relativeLiquidityAmount,\n uint256 deltaA,\n uint256 deltaB\n );\n error LensTargetingTokenWithNoDelta(\n bool targetIsA,\n uint256 deltaA,\n uint256 deltaB\n );\n\n /**\n * @notice Add liquidity slippage parameters for a distribution of liquidity.\n * @param pool Pool where liquidity is being added.\n * @param kind Bin kind; all bins must have the same kind in a given call\n * to addLiquidity.\n * @param ticks Array of tick values to add liquidity to.\n * @param relativeLiquidityAmounts Relative liquidity amounts for the\n * specified ticks. Liquidity in this case is not bin LP balance, it is\n * the bin liquidity as defined by liquidity = deltaA / (sqrt(upper) -\n * sqrt(lower)) or deltaB = liquidity / sqrt(lower) - liquidity /\n * sqrt(upper).\n * @param addSpec Slippage specification.\n */\n struct AddParamsViewInputs {\n IMaverickV2Pool pool;\n uint8 kind;\n int32[] ticks;\n uint128[] relativeLiquidityAmounts;\n AddParamsSpecification addSpec;\n }\n\n /**\n * @notice Multi-price add param specification.\n * @param slippageFactorD18 Max slippage allowed as a percent in D18 scale. e.g. 1% slippage is 0.01e18\n * @param numberOfPriceBreaksPerSide Number of price break values on either\n * side of current price.\n * @param targetAmount Target token contribution amount in tokenA if\n * targetIsA is true, otherwise this is the target amount for tokenB.\n * @param targetIsA Indicates if the target amount is for tokenA or tokenB\n */\n struct AddParamsSpecification {\n uint256 slippageFactorD18;\n uint256 numberOfPriceBreaksPerSide;\n uint256 targetAmount;\n bool targetIsA;\n }\n\n /**\n * @notice Specification for deriving create pool parameters. Creating a\n * pool in the liquidity manager has several steps:\n *\n * - Deploy pool\n * - Donate a small amount of initial liquidity in the activeTick\n * - Execute a small swap to set the pool price to the desired value\n * - Add liquidity\n *\n * In order to execute these steps, the caller must specify the parameters\n * of each step. The PoolLens has helper function to derive the values\n * used by the LiquidityManager, but this struct is the input to that\n * helper function and represents the core intent of the pool creator.\n *\n * @param fee Fraction of the pool swap amount that is retained as an LP in\n * D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * e.g. a pool with all 4 modes will have kinds = b1111 = 15\n * @param initialTargetB Amount of B to be donated to the pool after pool\n * create. This amount needs to be big enough to meet the minimum bin\n * liquidity.\n * @param sqrtPrice Target sqrt price of the pool.\n * @param kind Bin kind; all bins must have the same kind in a given call\n * to addLiquidity.\n * @param ticks Array of tick values to add liquidity to.\n * @param relativeLiquidityAmounts Relative liquidity amounts for the\n * specified ticks. Liquidity in this case is not bin LP balance, it is\n * the bin liquidity as defined by liquidity = deltaA / (sqrt(upper) -\n * sqrt(lower)) or deltaB = liquidity / sqrt(lower) - liquidity /\n * sqrt(upper).\n * @param targetAmount Target token contribution amount in tokenA if\n * targetIsA is true, otherwise this is the target amount for tokenB.\n * @param targetIsA Indicates if the target amount is for tokenA or tokenB\n */\n struct CreateAndAddParamsViewInputs {\n uint64 feeAIn;\n uint64 feeBIn;\n uint16 tickSpacing;\n uint32 lookback;\n IERC20 tokenA;\n IERC20 tokenB;\n int32 activeTick;\n uint8 kinds;\n // donate params\n uint256 initialTargetB;\n uint256 sqrtPrice;\n // add target\n uint8 kind;\n int32[] ticks;\n uint128[] relativeLiquidityAmounts;\n uint256 targetAmount;\n bool targetIsA;\n }\n\n struct Output {\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256[] deltaAs;\n uint256[] deltaBs;\n uint128[] deltaLpBalances;\n }\n\n struct Reserves {\n uint256 amountA;\n uint256 amountB;\n }\n\n struct BinPositionKinds {\n uint128[4] values;\n }\n\n struct PoolState {\n IMaverickV2Pool.TickState[] tickStateMapping;\n IMaverickV2Pool.BinState[] binStateMapping;\n BinPositionKinds[] binIdByTickKindMapping;\n IMaverickV2Pool.State state;\n Reserves protocolFees;\n }\n\n struct BoostedPositionSpecification {\n IMaverickV2Pool pool;\n uint32[] binIds;\n uint128[] ratios;\n uint8 kind;\n }\n\n struct CreateAndAddParamsInputs {\n uint64 feeAIn;\n uint64 feeBIn;\n uint16 tickSpacing;\n uint32 lookback;\n IERC20 tokenA;\n IERC20 tokenB;\n int32 activeTick;\n uint8 kinds;\n // donate params\n IMaverickV2Pool.AddLiquidityParams donateParams;\n // swap params\n uint256 swapAmount;\n // add params\n IMaverickV2Pool.AddLiquidityParams addParams;\n bytes[] packedAddParams;\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256 preAddReserveA;\n uint256 preAddReserveB;\n }\n\n struct TickDeltas {\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256[] deltaAs;\n uint256[] deltaBs;\n }\n\n /**\n * @notice Converts add parameter slippage specification into add\n * parameters. The return values are given in both raw format and as packed\n * values that can be used in the LiquidityManager contract.\n */\n function getAddLiquidityParams(AddParamsViewInputs memory params)\n external\n view\n returns (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint88[] memory sqrtPriceBreaks,\n IMaverickV2Pool.AddLiquidityParams[] memory addParams,\n IMaverickV2PoolLens.TickDeltas[] memory tickDeltas\n );\n\n /**\n * @notice Converts add parameter slippage specification and new pool\n * specification into CreateAndAddParamsInputs parameters that can be used in the\n * LiquidityManager contract.\n */\n function getCreatePoolAtPriceAndAddLiquidityParams(\n CreateAndAddParamsViewInputs memory params\n ) external view returns (CreateAndAddParamsInputs memory output);\n\n /**\n * @notice View function that provides information about pool ticks within\n * a tick radius from the activeTick. Ticks with no reserves are not\n * included in part o f the return array.\n */\n function getTicksAroundActive(IMaverickV2Pool pool, int32 tickRadius)\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates\n );\n\n /**\n * @notice View function that provides information about pool ticks within\n * a range. Ticks with no reserves are not included in part o f the return\n * array.\n */\n function getTicks(\n IMaverickV2Pool pool,\n int32 tickStart,\n int32 tickEnd\n )\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates\n );\n\n /**\n * @notice View function that provides information about pool ticks within\n * a range. Information returned includes all pool state needed to emulate\n * a swap off chain. Ticks with no reserves are not included in part o f\n * the return array.\n */\n function getTicksAroundActiveWLiquidity(\n IMaverickV2Pool pool,\n int32 tickRadius\n )\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates,\n uint256[] memory liquidities,\n uint256[] memory sqrtLowerTickPrices,\n uint256[] memory sqrtUpperTickPrices,\n IMaverickV2Pool.State memory poolState,\n uint256 sqrtPrice,\n uint256 feeAIn,\n uint256 feeBIn\n );\n\n /**\n * @notice View function that provides pool state information.\n */\n function getFullPoolState(\n IMaverickV2Pool pool,\n uint32 binStart,\n uint32 binEnd\n ) external view returns (PoolState memory poolState);\n\n /**\n * @notice View function that provides price and liquidity of a given tick.\n */\n function getTickSqrtPriceAndL(IMaverickV2Pool pool, int32 tick)\n external\n view\n returns (uint256 sqrtPrice, uint256 liquidity);\n\n /**\n * @notice Pool sqrt price.\n */\n function getPoolSqrtPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 sqrtPrice);\n\n /**\n * @notice Pool price.\n */\n function getPoolPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 price);\n\n /**\n * @notice Token scale of two tokens in a pool.\n */\n function tokenScales(IMaverickV2Pool pool)\n external\n view\n returns (uint256 tokenAScale, uint256 tokenBScale);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Position.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\nimport { ILiquidityRegistry } from \"./ILiquidityRegistry.sol\";\n\ninterface IMaverickV2Position {\n event PositionClearData(uint256 indexed tokenId);\n event PositionSetData(\n uint256 indexed tokenId,\n uint256 index,\n PositionPoolBinIds newData\n );\n event SetLpReward(ILiquidityRegistry lpReward);\n\n error PositionDuplicatePool(uint256 index, IMaverickV2Pool pool);\n error PositionNotFactoryPool();\n error PositionPermissionedLiquidityPool();\n\n struct PositionPoolBinIds {\n IMaverickV2Pool pool;\n uint32[] binIds;\n }\n\n struct PositionFullInformation {\n PositionPoolBinIds poolBinIds;\n uint256 amountA;\n uint256 amountB;\n uint256[] binAAmounts;\n uint256[] binBAmounts;\n int32[] ticks;\n uint256[] liquidities;\n }\n\n /**\n * @notice Factory that tracks lp rewards.\n */\n function lpReward() external view returns (ILiquidityRegistry);\n\n /**\n * @notice Pool factory.\n */\n function factory() external view returns (IMaverickV2Factory);\n\n /**\n * @notice Mint NFT that holds liquidity in a Maverick V2 Pool. To mint\n * liquidity to an NFT, add liquidity to bins in a pool where the\n * add liquidity recipient is this contract and the subaccount is the\n * tokenId. LiquidityManager can be used to simplify minting Position NFTs.\n */\n function mint(\n address recipient,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external returns (uint256 tokenId);\n\n /**\n * @notice Overwrites tokenId pool/binId information for a given data index.\n */\n function setTokenIdData(\n uint256 tokenId,\n uint256 index,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n\n /**\n * @notice Overwrites entire pool/binId data set for a given tokenId.\n */\n function setTokenIdData(uint256 tokenId, PositionPoolBinIds[] memory data)\n external;\n\n /**\n * @notice Append new pool/binIds data array to tokenId.\n */\n function appendTokenIdData(\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n\n /**\n * @notice Get array pool/binIds data for a given tokenId.\n */\n function getTokenIdData(uint256 tokenId)\n external\n view\n returns (PositionPoolBinIds[] memory);\n\n /**\n * @notice Get value from array of pool/binIds data for a given tokenId.\n */\n function getTokenIdData(uint256 tokenId, uint256 index)\n external\n view\n returns (PositionPoolBinIds memory);\n\n /**\n * @notice Length of array of pool/binIds data for a given tokenId.\n */\n function tokenIdDataLength(uint256 tokenId)\n external\n view\n returns (uint256 length);\n\n /**\n * @notice Remove liquidity from tokenId for a given pool. User can\n * specify arbitrary bins to remove from for their subaccount in the pool\n * even if those bins are not in the tokenIdData set.\n */\n function removeLiquidity(\n uint256 tokenId,\n address recipient,\n IMaverickV2Pool pool,\n IMaverickV2Pool.RemoveLiquidityParams memory params\n ) external returns (uint256 tokenAAmount, uint256 tokenBAmount);\n\n /**\n * @notice Remove liquidity from tokenId for a given pool to sender. User\n * can specify arbitrary bins to remove from for their subaccount in the\n * pool even if those bins are not in the tokenIdData set.\n */\n function removeLiquidityToSender(\n uint256 tokenId,\n IMaverickV2Pool pool,\n IMaverickV2Pool.RemoveLiquidityParams memory params\n ) external returns (uint256 tokenAAmount, uint256 tokenBAmount);\n\n /**\n * @notice NFT asset information for a given range of pool/binIds indexes.\n * This function only returns the liquidity in the pools/binIds stored as\n * part of the tokenIdData, but it is possible that the NFT has additional\n * liquidity in pools/binIds that have not been recorded.\n */\n function tokenIdPositionInformation(\n uint256 tokenId,\n uint256 startIndex,\n uint256 stopIndex\n ) external view returns (PositionFullInformation[] memory output);\n\n /**\n * @notice NFT asset information for a given pool/binIds index. This\n * function only returns the liquidity in the pools/binIds stored as part\n * of the tokenIdData, but it is possible that the NFT has additional\n * liquidity in pools/binIds that have not been recorded.\n */\n function tokenIdPositionInformation(uint256 tokenId, uint256 index)\n external\n view\n returns (PositionFullInformation memory output);\n\n /**\n * @notice Get remove parameters for removing a fractional part of the\n * liquidity owned by a given tokenId. The fractional factor to remove is\n * given by proporationD18 in 18-decimal scale.\n */\n function getRemoveParams(\n uint256 tokenId,\n uint256 index,\n uint256 proportionD18\n )\n external\n view\n returns (IMaverickV2Pool.RemoveLiquidityParams memory params);\n\n /**\n * @notice Register the bin balances in the nft with the LpReward contract.\n */\n function checkpointBinLpBalance(\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Quoter.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2Quoter {\n error QuoterInvalidSwap();\n error QuoterInvalidAddLiquidity();\n\n /**\n * @notice Calculates a swap on a MaverickV2Pool and returns the resulting\n * amount and estimated gas. The gas estimate is only a rough estimate and\n * may not match a swap's gas.\n * @param pool The MaverickV2Pool to swap on.\n * @param amount The input amount.\n * @param tokenAIn Indicates if token A is the input token.\n * @param exactOutput Indicates if the amount is the output amount (true)\n * or input amount (false). If the tickLimit is reached, the full value of\n * the exactOutput may not be returned because the pool will stop swapping\n * before the whole order is filled.\n * @param tickLimit The tick limit for the swap. Once the swap lands in\n * this tick, it will stop and return the output amount swapped up to that\n * tick.\n */\n function calculateSwap(\n IMaverickV2Pool pool,\n uint128 amount,\n bool tokenAIn,\n bool exactOutput,\n int32 tickLimit\n )\n external\n returns (\n uint256 amountIn,\n uint256 amountOut,\n uint256 gasEstimate\n );\n\n /**\n * @notice Calculates a multihop swap and returns the resulting amount and\n * estimated gas. The gas estimate is only a rough estimate and\n * may not match a swap's gas.\n * @param path The path of pools to swap through. Path is given by an\n * packed array of (pool, tokenAIn) tuples. So each step in the path is 160\n * + 8 = 168 bits of data. e.g. path = abi.encodePacked(pool1, true, pool2, false);\n * @param amount The input amount.\n * @param exactOutput A boolean indicating if exact output is required.\n */\n function calculateMultiHopSwap(\n bytes memory path,\n uint256 amount,\n bool exactOutput\n ) external returns (uint256 returnAmount, uint256 gasEstimate);\n\n /**\n * @notice Computes the token amounts required for a given set of\n * addLiquidity parameters. The gas estimate is only a rough estimate and\n * may not match a add's gas.\n */\n function calculateAddLiquidity(\n IMaverickV2Pool pool,\n IMaverickV2Pool.AddLiquidityParams calldata params\n )\n external\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 gasEstimate\n );\n\n /**\n * @notice Pool's sqrt price.\n */\n function poolSqrtPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 sqrtPrice);\n}\n" + }, + "contracts/interfaces/plume/IPoolDistributor.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.0;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IPoolDistributor {\n function rewardToken() external view returns (address);\n\n function claimLp(\n address recipient,\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds,\n uint256 epoch\n ) external returns (uint256 amount);\n}\n" + }, + "contracts/interfaces/plume/IVotingDistributor.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.0;\n\ninterface IVotingDistributor {\n function lastEpoch() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/poolBooster/IMerklDistributor.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IMerklDistributor {\n struct CampaignParameters {\n // POPULATED ONCE CREATED\n\n // ID of the campaign. This can be left as a null bytes32 when creating campaigns\n // on Merkl.\n bytes32 campaignId;\n // CHOSEN BY CAMPAIGN CREATOR\n\n // Address of the campaign creator, if marked as address(0), it will be overriden with the\n // address of the `msg.sender` creating the campaign\n address creator;\n // Address of the token used as a reward\n address rewardToken;\n // Amount of `rewardToken` to distribute across all the epochs\n // Amount distributed per epoch is `amount/numEpoch`\n uint256 amount;\n // Type of campaign\n uint32 campaignType;\n // Timestamp at which the campaign should start\n uint32 startTimestamp;\n // Duration of the campaign in seconds. Has to be a multiple of EPOCH = 3600\n uint32 duration;\n // Extra data to pass to specify the campaign\n bytes campaignData;\n }\n\n function createCampaign(CampaignParameters memory newCampaign)\n external\n returns (bytes32);\n\n function signAndCreateCampaign(\n CampaignParameters memory newCampaign,\n bytes memory _signature\n ) external returns (bytes32);\n\n function sign(bytes memory _signature) external;\n\n function rewardTokenMinAmounts(address _rewardToken)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/poolBooster/IPoolBoostCentralRegistry.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPoolBoostCentralRegistry {\n /**\n * @dev all the supported pool booster types are listed here. It is possible\n * to have multiple versions of the factory that supports the same type of\n * pool booster. Factories are immutable and this can happen when a factory\n * or related pool booster required code update.\n * e.g. \"PoolBoosterSwapxDouble\" & \"PoolBoosterSwapxDouble_v2\"\n */\n enum PoolBoosterType {\n // Supports bribing 2 contracts per pool. Appropriate for Ichi vault concentrated\n // liquidity pools where (which is expected in most/all cases) both pool gauges\n // require bribing.\n SwapXDoubleBooster,\n // Supports bribing a single contract per pool. Appropriate for Classic Stable &\n // Classic Volatile pools and Ichi vaults where only 1 side (1 of the 2 gauges)\n // needs bribing\n SwapXSingleBooster,\n // Supports bribing a single contract per pool. Appropriate for Metropolis pools\n MetropolisBooster,\n // Supports creating a Merkl campaign.\n MerklBooster\n }\n\n struct PoolBoosterEntry {\n address boosterAddress;\n address ammPoolAddress;\n PoolBoosterType boosterType;\n }\n\n event PoolBoosterCreated(\n address poolBoosterAddress,\n address ammPoolAddress,\n PoolBoosterType poolBoosterType,\n address factoryAddress\n );\n event PoolBoosterRemoved(address poolBoosterAddress);\n\n function emitPoolBoosterCreated(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n PoolBoosterType _boosterType\n ) external;\n\n function emitPoolBoosterRemoved(address _poolBoosterAddress) external;\n}\n" + }, + "contracts/interfaces/poolBooster/IPoolBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPoolBooster {\n event BribeExecuted(uint256 amount);\n\n /// @notice Execute the bribe action\n function bribe() external;\n}\n" + }, + "contracts/interfaces/poolBooster/ISwapXAlgebraBribe.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBribe {\n /// @notice Notify a bribe amount\n /// @dev Rewards are saved into NEXT EPOCH mapping.\n function notifyRewardAmount(address _rewardsToken, uint256 reward) external;\n}\n" + }, + "contracts/interfaces/sonic/INodeDriver.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\n\ninterface INodeDriver {\n /// Seal epoch. Called BEFORE epoch sealing made by the client itself.\n function sealEpoch(\n uint256[] calldata offlineTimes,\n uint256[] calldata offlineBlocks,\n uint256[] calldata uptimes,\n uint256[] calldata originatedTxsFee\n ) external;\n}\n" + }, + "contracts/interfaces/sonic/ISFC.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\n\n/**\n * @title Special Fee Contract for Sonic network\n * @notice The SFC maintains a list of validators and delegators and distributes rewards to them.\n * @custom:security-contact security@fantom.foundation\n */\ninterface ISFC {\n error StakeIsFullySlashed();\n\n event CreatedValidator(\n uint256 indexed validatorID,\n address indexed auth,\n uint256 createdEpoch,\n uint256 createdTime\n );\n event Delegated(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 amount\n );\n event Undelegated(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 indexed wrID,\n uint256 amount\n );\n event Withdrawn(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 indexed wrID,\n uint256 amount,\n uint256 penalty\n );\n event ClaimedRewards(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 rewards\n );\n event RestakedRewards(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 rewards\n );\n event BurntFTM(uint256 amount);\n event UpdatedSlashingRefundRatio(\n uint256 indexed validatorID,\n uint256 refundRatio\n );\n event RefundedSlashedLegacyDelegation(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 amount\n );\n\n event DeactivatedValidator(\n uint256 indexed validatorID,\n uint256 deactivatedEpoch,\n uint256 deactivatedTime\n );\n event ChangedValidatorStatus(uint256 indexed validatorID, uint256 status);\n event AnnouncedRedirection(address indexed from, address indexed to);\n\n function currentSealedEpoch() external view returns (uint256);\n\n function getEpochSnapshot(uint256 epoch)\n external\n view\n returns (\n uint256 endTime,\n uint256 endBlock,\n uint256 epochFee,\n uint256 baseRewardPerSecond,\n uint256 totalStake,\n uint256 totalSupply\n );\n\n function getStake(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getValidator(uint256 validatorID)\n external\n view\n returns (\n uint256 status,\n uint256 receivedStake,\n address auth,\n uint256 createdEpoch,\n uint256 createdTime,\n uint256 deactivatedTime,\n uint256 deactivatedEpoch\n );\n\n function getValidatorID(address auth) external view returns (uint256);\n\n function getValidatorPubkey(uint256 validatorID)\n external\n view\n returns (bytes memory);\n\n function pubkeyAddressvalidatorID(address pubkeyAddress)\n external\n view\n returns (uint256);\n\n function getWithdrawalRequest(\n address delegator,\n uint256 validatorID,\n uint256 wrID\n )\n external\n view\n returns (\n uint256 epoch,\n uint256 time,\n uint256 amount\n );\n\n function isOwner() external view returns (bool);\n\n function lastValidatorID() external view returns (uint256);\n\n function minGasPrice() external view returns (uint256);\n\n function owner() external view returns (address);\n\n function renounceOwnership() external;\n\n function slashingRefundRatio(uint256 validatorID)\n external\n view\n returns (uint256);\n\n function stashedRewardsUntilEpoch(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function totalActiveStake() external view returns (uint256);\n\n function totalStake() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function transferOwnership(address newOwner) external;\n\n function treasuryAddress() external view returns (address);\n\n function version() external pure returns (bytes3);\n\n function currentEpoch() external view returns (uint256);\n\n function updateConstsAddress(address v) external;\n\n function constsAddress() external view returns (address);\n\n function getEpochValidatorIDs(uint256 epoch)\n external\n view\n returns (uint256[] memory);\n\n function getEpochReceivedStake(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochAccumulatedRewardPerToken(\n uint256 epoch,\n uint256 validatorID\n ) external view returns (uint256);\n\n function getEpochAccumulatedUptime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochAverageUptime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint32);\n\n function getEpochAccumulatedOriginatedTxsFee(\n uint256 epoch,\n uint256 validatorID\n ) external view returns (uint256);\n\n function getEpochOfflineTime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochOfflineBlocks(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochEndBlock(uint256 epoch) external view returns (uint256);\n\n function rewardsStash(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function createValidator(bytes calldata pubkey) external payable;\n\n function getSelfStake(uint256 validatorID) external view returns (uint256);\n\n function delegate(uint256 validatorID) external payable;\n\n function undelegate(\n uint256 validatorID,\n uint256 wrID,\n uint256 amount\n ) external;\n\n function isSlashed(uint256 validatorID) external view returns (bool);\n\n function withdraw(uint256 validatorID, uint256 wrID) external;\n\n function deactivateValidator(uint256 validatorID, uint256 status) external;\n\n function pendingRewards(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function stashRewards(address delegator, uint256 validatorID) external;\n\n function claimRewards(uint256 validatorID) external;\n\n function restakeRewards(uint256 validatorID) external;\n\n function updateSlashingRefundRatio(uint256 validatorID, uint256 refundRatio)\n external;\n\n function updateTreasuryAddress(address v) external;\n\n function burnFTM(uint256 amount) external;\n\n function sealEpoch(\n uint256[] calldata offlineTime,\n uint256[] calldata offlineBlocks,\n uint256[] calldata uptimes,\n uint256[] calldata originatedTxsFee\n ) external;\n\n function sealEpochValidators(uint256[] calldata nextValidatorIDs) external;\n\n function initialize(\n uint256 sealedEpoch,\n uint256 _totalSupply,\n address nodeDriver,\n address consts,\n address _owner\n ) external;\n\n function setGenesisValidator(\n address auth,\n uint256 validatorID,\n bytes calldata pubkey,\n uint256 createdTime\n ) external;\n\n function setGenesisDelegation(\n address delegator,\n uint256 validatorID,\n uint256 stake\n ) external;\n\n function updateStakeSubscriberAddress(address v) external;\n\n function stakeSubscriberAddress() external view returns (address);\n\n function setRedirectionAuthorizer(address v) external;\n\n function announceRedirection(address to) external;\n\n function initiateRedirection(address from, address to) external;\n\n function redirect(address to) external;\n}\n" + }, + "contracts/interfaces/sonic/ISwapXGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IGauge {\n function owner() external view returns (address);\n\n function TOKEN() external view returns (address);\n\n function DISTRIBUTION() external view returns (address);\n\n function balanceOf(address account) external view returns (uint256);\n\n function claimFees() external returns (uint256 claimed0, uint256 claimed1);\n\n function deposit(uint256 amount) external;\n\n function depositAll() external;\n\n function earned(address account) external view returns (uint256);\n\n function getReward() external;\n\n function getReward(address _user) external;\n\n function isForPair() external view returns (bool);\n\n function lastTimeRewardApplicable() external view returns (uint256);\n\n function lastUpdateTime() external view returns (uint256);\n\n function notifyRewardAmount(address token, uint256 reward) external;\n\n function periodFinish() external view returns (uint256);\n\n function rewardForDuration() external view returns (uint256);\n\n function rewardPerToken() external view returns (uint256);\n\n function rewardPerTokenStored() external view returns (uint256);\n\n function rewardRate() external view returns (uint256);\n\n function rewardToken() external view returns (address);\n\n function rewards(address) external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function userRewardPerTokenPaid(address) external view returns (uint256);\n\n function withdraw(uint256 amount) external;\n\n function withdrawAll() external;\n\n function withdrawAllAndHarvest() external;\n\n function withdrawExcess(address token, uint256 amount) external;\n\n function emergency() external returns (bool);\n\n function emergencyWithdraw() external;\n\n function activateEmergencyMode() external;\n\n function stopEmergencyMode() external;\n}\n" + }, + "contracts/interfaces/sonic/ISwapXPair.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IPair {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n\n event Mint(address indexed sender, uint256 amount0, uint256 amount1);\n event Burn(\n address indexed sender,\n uint256 amount0,\n uint256 amount1,\n address indexed to\n );\n event Swap(\n address indexed sender,\n uint256 amount0In,\n uint256 amount1In,\n uint256 amount0Out,\n uint256 amount1Out,\n address indexed to\n );\n event Claim(\n address indexed sender,\n address indexed recipient,\n uint256 amount0,\n uint256 amount1\n );\n\n function metadata()\n external\n view\n returns (\n uint256 dec0,\n uint256 dec1,\n uint256 r0,\n uint256 r1,\n bool st,\n address t0,\n address t1\n );\n\n function claimFees() external returns (uint256, uint256);\n\n function tokens() external view returns (address, address);\n\n function token0() external view returns (address);\n\n function token1() external view returns (address);\n\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function swap(\n uint256 amount0Out,\n uint256 amount1Out,\n address to,\n bytes calldata data\n ) external;\n\n function burn(address to)\n external\n returns (uint256 amount0, uint256 amount1);\n\n function mint(address to) external returns (uint256 liquidity);\n\n function getReserves()\n external\n view\n returns (\n uint256 _reserve0,\n uint256 _reserve1,\n uint256 _blockTimestampLast\n );\n\n function getAmountOut(uint256, address) external view returns (uint256);\n\n // ERC20 methods\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n\n function totalSupply() external view returns (uint256);\n\n function balanceOf(address) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 value) external returns (bool);\n\n function claimable0(address _user) external view returns (uint256);\n\n function claimable1(address _user) external view returns (uint256);\n\n function isStable() external view returns (bool);\n\n function skim(address to) external;\n\n function sync() external;\n}\n" + }, + "contracts/interfaces/sonic/IVoterV3.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IVoterV3 {\n /// @notice create a gauge\n function createGauge(address _pool, uint256 _gaugeType)\n external\n returns (\n address _gauge,\n address _internal_bribe,\n address _external_bribe\n );\n\n function gauges(address _pool) external view returns (address _gauge);\n}\n" + }, + "contracts/interfaces/sonic/IWrappedSonic.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWrappedSonic {\n event Deposit(address indexed account, uint256 value);\n event Withdrawal(address indexed account, uint256 value);\n event Transfer(address indexed from, address indexed to, uint256 value);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 value) external returns (bool);\n\n function balanceOf(address account) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function depositFor(address account) external payable returns (bool);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address to, uint256 value) external returns (bool);\n\n function transferFrom(\n address from,\n address to,\n uint256 value\n ) external returns (bool);\n\n function withdraw(uint256 value) external;\n\n function withdrawTo(address account, uint256 value) external returns (bool);\n}\n" + }, + "contracts/interfaces/Tether.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// slither-disable-start erc20-interface\ninterface Tether {\n function transfer(address to, uint256 value) external;\n\n function transferFrom(\n address from,\n address to,\n uint256 value\n ) external;\n\n function balanceOf(address) external view returns (uint256);\n\n function approve(address _spender, uint256 _value) external;\n}\n// slither-disable-end erc20-interface\n" + }, + "contracts/interfaces/uniswap/IUniswapUniversalRouter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapUniversalRouter {\n /// @notice Executes encoded commands along with provided inputs. Reverts if deadline has expired.\n /// @param commands A set of concatenated commands, each 1 byte in length\n /// @param inputs An array of byte strings containing abi encoded inputs for each command\n /// @param deadline The deadline by which the transaction must be executed\n function execute(\n bytes calldata commands,\n bytes[] calldata inputs,\n uint256 deadline\n ) external payable;\n\n function execute(bytes calldata commands, bytes[] calldata inputs)\n external\n payable;\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV2Pair.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapV2Pair {\n function token0() external view returns (address);\n\n function token1() external view returns (address);\n\n function getReserves()\n external\n view\n returns (\n uint112 reserve0,\n uint112 reserve1,\n uint32 blockTimestampLast\n );\n\n function price0CumulativeLast() external view returns (uint256);\n\n function price1CumulativeLast() external view returns (uint256);\n\n function sync() external;\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV2Router02.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapV2Router {\n function WETH() external pure returns (address);\n\n function swapExactTokensForTokens(\n uint256 amountIn,\n uint256 amountOutMin,\n address[] calldata path,\n address to,\n uint256 deadline\n ) external returns (uint256[] memory amounts);\n\n function addLiquidity(\n address tokenA,\n address tokenB,\n uint256 amountADesired,\n uint256 amountBDesired,\n uint256 amountAMin,\n uint256 amountBMin,\n address to,\n uint256 deadline\n )\n external\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 liquidity\n );\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV3Router.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// -- Solididy v0.5.x compatible interface\ninterface IUniswapV3Router {\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n}\n" + }, + "contracts/mocks/beacon/ExecutionLayerConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { GeneralPurposeToConsensusLayerRequest } from \"./GeneralPurposeToConsensusLayerRequest.sol\";\n\ncontract ExecutionLayerConsolidation is GeneralPurposeToConsensusLayerRequest {\n event ConsolidationRequestIssued(bytes sourceKey, bytes targetKey);\n bytes public lastSource;\n bytes public lastTarget;\n\n function handleRequest(bytes calldata data) internal override {\n // parameters should consist of twice the 48 bytes for 2 public keys\n require(data.length == 96, \"Invalid Consolidation data\");\n lastSource = data[:48];\n lastTarget = data[48:];\n\n emit ConsolidationRequestIssued(lastSource, lastTarget);\n }\n}\n" + }, + "contracts/mocks/beacon/ExecutionLayerWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { GeneralPurposeToConsensusLayerRequest } from \"./GeneralPurposeToConsensusLayerRequest.sol\";\n\ncontract ExecutionLayerWithdrawal is GeneralPurposeToConsensusLayerRequest {\n event WithdrawalRequestIssued(bytes publicKey, uint64 amount);\n\n bytes public lastPublicKey;\n uint64 public lastAmount;\n\n function handleRequest(bytes calldata data) internal override {\n // parameters should consist of 48 bytes for public key and 8 bytes for uint64 amount\n require(data.length == 56, \"Invalid Withdrawal data\");\n lastPublicKey = data[:48];\n lastAmount = uint64(bytes8(data[48:]));\n emit WithdrawalRequestIssued(lastPublicKey, lastAmount);\n }\n}\n" + }, + "contracts/mocks/beacon/GeneralPurposeToConsensusLayerRequest.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nabstract contract GeneralPurposeToConsensusLayerRequest {\n // solhint-disable no-complex-fallback\n fallback() external payable {\n // fee requested\n if (msg.data.length == 0) {\n uint256 fee = _fee();\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Return a uint256 value\n mstore(0, fee)\n return(0, 32) // Return 32 bytes from memory\n }\n }\n\n // else handle request\n handleRequest(msg.data);\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _fee() internal virtual returns (uint256) {\n return 1;\n }\n\n function handleRequest(bytes calldata data) internal virtual;\n}\n" + }, + "contracts/mocks/BurnableERC20.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ninterface IBurnableERC20 {\n function burn(uint256 value) external returns (bool);\n\n function burnFrom(address account, uint256 value) external returns (bool);\n}\n\n/**\n * @title BurnableERC20\n * @dev Exposes the burn function of ERC20 for tests\n */\nabstract contract BurnableERC20 is IBurnableERC20, ERC20 {\n /**\n * @dev Function to burn tokens\n * @param value The amount of tokens to burn.\n * @return A boolean that indicates if the operation was successful.\n */\n function burn(uint256 value) public virtual override returns (bool) {\n _burn(msg.sender, value);\n return true;\n }\n\n /**\n * @dev Function to burn tokens from a specific account\n * @param account The address with the tokens to burn.\n * @param value The amount of tokens to burn.\n * @return A boolean that indicates if the operation was successful.\n */\n function burnFrom(address account, uint256 value)\n public\n override\n returns (bool)\n {\n _burn(account, value);\n return true;\n }\n}\n" + }, + "contracts/mocks/curve/Mock3CRV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20, MintableERC20 } from \"../MintableERC20.sol\";\nimport { BurnableERC20 } from \"../BurnableERC20.sol\";\n\ncontract Mock3CRV is MintableERC20, BurnableERC20 {\n constructor() ERC20(\"Curve.fi DAI/USDC/USDT\", \"3Crv\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/curve/MockBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { MockRewardPool } from \"./MockRewardPool.sol\";\n\nimport { IRewardStaking } from \"../../strategies/IRewardStaking.sol\";\nimport { IMintableERC20, MintableERC20, ERC20 } from \"../MintableERC20.sol\";\nimport { IBurnableERC20, BurnableERC20 } from \"../BurnableERC20.sol\";\n\ncontract MockDepositToken is MintableERC20, BurnableERC20 {\n constructor() ERC20(\"DCVX\", \"CVX Deposit Token\") {}\n}\n\ncontract MockBooster {\n using SafeERC20 for IERC20;\n\n struct PoolInfo {\n address lptoken;\n address token;\n address crvRewards;\n }\n\n address public minter; // this is CVx for the booster on live\n address public crv; // Curve rewards token\n address public cvx; // Convex rewards token\n mapping(uint256 => PoolInfo) public poolInfo;\n\n constructor(\n address _rewardsMinter,\n address _crv,\n address _cvx\n ) public {\n minter = _rewardsMinter;\n crv = _crv;\n cvx = _cvx;\n }\n\n function setPool(uint256 pid, address _lpToken)\n external\n returns (address rewards)\n {\n address token = address(new MockDepositToken());\n // Deploy a new Convex Rewards Pool\n rewards = address(\n new MockRewardPool(pid, token, crv, cvx, address(this))\n );\n\n poolInfo[pid] = PoolInfo({\n lptoken: _lpToken,\n token: token,\n crvRewards: rewards\n });\n }\n\n function deposit(\n uint256 _pid,\n uint256 _amount,\n bool _stake\n ) public returns (bool) {\n PoolInfo storage pool = poolInfo[_pid];\n\n address lptoken = pool.lptoken;\n\n // hold on to the Curve LP tokens\n IERC20(lptoken).safeTransferFrom(msg.sender, address(this), _amount);\n\n address token = pool.token;\n if (_stake) {\n // mint Convex pool LP tokens and stake in rewards contract on user behalf\n IMintableERC20(token).mint(_amount);\n address rewardContract = pool.crvRewards;\n IERC20(token).safeApprove(rewardContract, 0);\n IERC20(token).safeApprove(rewardContract, _amount);\n IRewardStaking(rewardContract).stakeFor(msg.sender, _amount);\n } else {\n // mint Convex pool LP tokens and send to user\n IMintableERC20(token).mint(_amount);\n IERC20(token).transfer(msg.sender, _amount);\n }\n return true;\n }\n\n // Deposit all Curve LP tokens and stake\n function depositAll(uint256 _pid, bool _stake) external returns (bool) {\n address lptoken = poolInfo[_pid].lptoken;\n uint256 balance = IERC20(lptoken).balanceOf(msg.sender);\n deposit(_pid, balance, _stake);\n return true;\n }\n\n // withdraw Curve LP tokens\n function _withdraw(\n uint256 _pid,\n uint256 _amount,\n address _from,\n address _to\n ) internal {\n PoolInfo storage pool = poolInfo[_pid];\n\n // burn the Convex pool LP tokens\n IBurnableERC20(pool.token).burnFrom(_from, _amount);\n\n // return the Curve LP tokens\n IERC20(pool.lptoken).safeTransfer(_to, _amount);\n }\n\n // withdraw Curve LP tokens\n function withdraw(uint256 _pid, uint256 _amount) public returns (bool) {\n _withdraw(_pid, _amount, msg.sender, msg.sender);\n return true;\n }\n\n // withdraw all Curve LP tokens\n function withdrawAll(uint256 _pid) public returns (bool) {\n address token = poolInfo[_pid].token;\n uint256 userBal = IERC20(token).balanceOf(msg.sender);\n withdraw(_pid, userBal);\n return true;\n }\n\n // allow reward contracts to send here and withdraw to user\n function withdrawTo(\n uint256 _pid,\n uint256 _amount,\n address _to\n ) external returns (bool) {\n address rewardContract = poolInfo[_pid].crvRewards;\n require(msg.sender == rewardContract, \"!auth\");\n\n _withdraw(_pid, _amount, msg.sender, _to);\n return true;\n }\n\n // callback from reward contract when crv is received.\n function rewardClaimed(\n uint256 _pid,\n // solhint-disable-next-line no-unused-vars\n address _address,\n uint256 _amount\n ) external returns (bool) {\n address rewardContract = poolInfo[_pid].crvRewards;\n require(msg.sender == rewardContract, \"!auth\");\n\n //mint reward tokens\n // and transfer it\n IMintableERC20(minter).mint(_amount);\n IERC20(minter).transfer(msg.sender, _amount);\n return true;\n }\n}\n" + }, + "contracts/mocks/curve/MockCRV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../MintableERC20.sol\";\n\ncontract MockCRV is MintableERC20 {\n constructor() ERC20(\"Curve DAO Token\", \"CRV\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/curve/MockCRVMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\n\ncontract MockCRVMinter {\n address crv;\n\n constructor(address _crv) {\n crv = _crv;\n }\n\n function mint(address _address) external {\n uint256 amount = 2e18;\n IMintableERC20(crv).mint(amount);\n IERC20(crv).transfer(_address, amount);\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveAbstractMetapool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { MintableERC20, IMintableERC20 } from \"../MintableERC20.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport \"../../utils/Helpers.sol\";\n\nabstract contract MockCurveAbstractMetapool is MintableERC20 {\n using StableMath for uint256;\n\n address[] public coins;\n uint256[2] public balances;\n\n // Returns the same amount of LP tokens in 1e18 decimals\n function add_liquidity(uint256[2] calldata _amounts, uint256 _minAmount)\n external\n returns (uint256 lpAmount)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transferFrom(\n msg.sender,\n address(this),\n _amounts[i]\n );\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to sum\n lpAmount += _amounts[i].scaleBy(18, assetDecimals);\n balances[i] = balances[i] + _amounts[i];\n }\n }\n // Hacky way of simulating slippage to check _minAmount\n if (lpAmount == 29000e18) lpAmount = 14500e18;\n require(lpAmount >= _minAmount, \"Slippage ruined your day\");\n // Send LP token to sender, e.g. 3CRV\n _mint(msg.sender, lpAmount);\n }\n\n // Dumb implementation that returns the same amount\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n public\n view\n returns (uint256 lpAmount)\n {\n uint256 assetDecimals = Helpers.getDecimals(coins[uint128(_index)]);\n lpAmount = _amount.scaleBy(assetDecimals, 18);\n }\n\n function remove_liquidity_one_coin(\n uint256 _lpAmount,\n int128 _index,\n // solhint-disable-next-line no-unused-vars\n uint256 _minAmount\n ) external returns (uint256 amount) {\n _burn(msg.sender, _lpAmount);\n uint256[] memory amounts = new uint256[](coins.length);\n amounts[uint128(_index)] = _lpAmount;\n amount = calc_withdraw_one_coin(_lpAmount, _index);\n balances[uint128(_index)] -= amount;\n IERC20(coins[uint128(_index)]).transfer(msg.sender, amount);\n }\n\n function get_virtual_price() external pure returns (uint256) {\n return 1e18;\n }\n\n // solhint-disable-next-line no-unused-vars\n function remove_liquidity(uint256 _amount, uint256[2] memory _min_amounts)\n public\n returns (uint256[2] memory amounts)\n {\n _burn(msg.sender, _amount);\n uint256 totalSupply = totalSupply();\n for (uint256 i = 0; i < 2; i++) {\n amounts[i] = totalSupply > 0\n ? (_amount * IERC20(coins[i]).balanceOf(address(this))) /\n totalSupply\n : IERC20(coins[i]).balanceOf(address(this));\n balances[i] -= amounts[i];\n IERC20(coins[i]).transfer(msg.sender, amounts[i]);\n }\n }\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens\n ) public returns (uint256) {\n return\n _remove_liquidity_imbalance(\n _amounts,\n _max_burned_tokens,\n msg.sender\n );\n }\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens,\n address _reveiver\n ) public returns (uint256) {\n return\n _remove_liquidity_imbalance(\n _amounts,\n _max_burned_tokens,\n _reveiver\n );\n }\n\n function _remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens,\n address _reveiver\n ) internal returns (uint256 lpTokens) {\n lpTokens = _max_burned_tokens;\n _burn(msg.sender, lpTokens);\n for (uint256 i = 0; i < _amounts.length; i++) {\n balances[i] -= _amounts[i];\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transfer(_reveiver, _amounts[i]);\n }\n }\n }\n\n // Dumb implementation that sums the scaled amounts\n function calc_token_amount(uint256[2] memory _amounts, bool)\n public\n view\n returns (uint256 lpTokens)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to lpTokens\n lpTokens += _amounts[i].scaleBy(18, assetDecimals);\n }\n }\n\n /// @notice 0.02% fee\n function fee() external pure returns (uint256) {\n return 2000000;\n }\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n\n function burnFrom(address from, uint256 value) public {\n _burn(from, value);\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveGauge.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { ICurveGauge } from \"../../strategies/ICurveGauge.sol\";\n\ncontract MockCurveGauge is ICurveGauge {\n mapping(address => uint256) private _balances;\n address lpToken;\n\n constructor(address _lpToken) {\n lpToken = _lpToken;\n }\n\n function balanceOf(address account) public view override returns (uint256) {\n return _balances[account];\n }\n\n function deposit(uint256 _value, address _account) external override {\n IERC20(lpToken).transferFrom(msg.sender, address(this), _value);\n _balances[_account] += _value;\n }\n\n function withdraw(uint256 _value) external override {\n IERC20(lpToken).transfer(msg.sender, _value);\n // solhint-disable-next-line reentrancy\n _balances[msg.sender] -= _value;\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveMetapool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { MockCurveAbstractMetapool } from \"./MockCurveAbstractMetapool.sol\";\nimport \"../MintableERC20.sol\";\n\ncontract MockCurveMetapool is MockCurveAbstractMetapool {\n constructor(address[2] memory _coins)\n ERC20(\"Curve.fi 3pool/OUSD metapool\", \"3crv_OUSD\")\n {\n coins = _coins;\n }\n}\n" + }, + "contracts/mocks/curve/MockCurvePool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IBurnableERC20 } from \"../BurnableERC20.sol\";\n\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\nimport { ICurvePool } from \"../../strategies/ICurvePool.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport \"../../utils/Helpers.sol\";\n\ncontract MockCurvePool {\n using StableMath for uint256;\n\n address[] public coins;\n uint256[3] public balances;\n address lpToken;\n uint256 public slippage = 1 ether;\n\n constructor(address[3] memory _coins, address _lpToken) {\n coins = _coins;\n lpToken = _lpToken;\n }\n\n function setCoins(address[] memory _coins) external {\n coins = _coins;\n }\n\n // Returns the same amount of LP tokens in 1e18 decimals\n function add_liquidity(uint256[3] calldata _amounts, uint256 _minAmount)\n external\n {\n uint256 sum = 0;\n for (uint256 i = 0; i < _amounts.length; i++) {\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transferFrom(\n msg.sender,\n address(this),\n _amounts[i]\n );\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to sum\n sum += _amounts[i].scaleBy(18, assetDecimals);\n balances[i] = balances[i] + _amounts[i];\n }\n }\n // Hacky way of simulating slippage to check _minAmount\n if (sum == 29000e18) sum = 14500e18;\n require(sum >= _minAmount, \"Slippage ruined your day\");\n // Send LP token to sender, e.g. 3CRV\n IMintableERC20(lpToken).mint(sum);\n IERC20(lpToken).transfer(msg.sender, sum);\n }\n\n // Dumb implementation that returns the same amount\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n public\n view\n returns (uint256)\n {\n uint256 assetDecimals = Helpers.getDecimals(coins[uint128(_index)]);\n return _amount.scaleBy(assetDecimals, 18);\n }\n\n function remove_liquidity_one_coin(\n uint256 _amount,\n int128 _index,\n // solhint-disable-next-line no-unused-vars\n uint256 _minAmount\n ) external {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _amount);\n uint256[] memory amounts = new uint256[](coins.length);\n amounts[uint128(_index)] = _amount;\n uint256 coinAmount = calc_withdraw_one_coin(_amount, _index);\n balances[uint128(_index)] -= coinAmount;\n IERC20(coins[uint128(_index)]).transfer(msg.sender, coinAmount);\n }\n\n function get_virtual_price() external pure returns (uint256) {\n return 1e18;\n }\n\n // solhint-disable-next-line no-unused-vars\n function remove_liquidity(uint256 _lpAmount, uint256[3] memory _min_amounts)\n public\n {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _lpAmount);\n uint256 totalSupply = IERC20(lpToken).totalSupply();\n for (uint256 i = 0; i < 3; i++) {\n uint256 coinAmount = totalSupply > 0\n ? (_lpAmount * IERC20(coins[i]).balanceOf(address(this))) /\n totalSupply\n : IERC20(coins[i]).balanceOf(address(this));\n balances[i] -= coinAmount;\n IERC20(coins[i]).transfer(msg.sender, coinAmount);\n }\n }\n\n function remove_liquidity_imbalance(\n uint256[3] memory _amounts,\n uint256 _max_burned_tokens\n ) public {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _max_burned_tokens);\n // For each coin, transfer to the caller\n for (uint256 i = 0; i < _amounts.length; i++) {\n balances[i] -= _amounts[i];\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transfer(msg.sender, _amounts[i]);\n }\n }\n }\n\n // Dumb implementation that sums the scaled amounts\n function calc_token_amount(uint256[3] memory _amounts, bool)\n public\n view\n returns (uint256 lpTokens)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to lpTokens\n lpTokens += _amounts[i].scaleBy(18, assetDecimals);\n }\n }\n\n function fee() external pure returns (uint256) {\n return 1000000;\n }\n\n function exchange(\n uint256 coin0,\n uint256 coin1,\n uint256 amountIn,\n uint256 minAmountOut\n ) external returns (uint256 amountOut) {\n IERC20(coins[coin0]).transferFrom(msg.sender, address(this), amountIn);\n amountOut = (minAmountOut * slippage) / 1 ether;\n require(amountOut >= minAmountOut, \"Slippage error\");\n IMintableERC20(coins[coin1]).mintTo(msg.sender, amountOut);\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n}\n" + }, + "contracts/mocks/curve/MockCVX.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../MintableERC20.sol\";\n\ncontract MockCVX is MintableERC20 {\n constructor() ERC20(\"CVX\", \"CVX DAO Token\") {}\n}\n" + }, + "contracts/mocks/curve/MockRewardPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\ninterface IDeposit {\n function poolInfo(uint256)\n external\n view\n returns (\n address,\n address,\n address,\n address,\n address,\n bool\n );\n\n function rewardClaimed(\n uint256,\n address,\n uint256\n ) external;\n\n function withdrawTo(\n uint256,\n uint256,\n address\n ) external;\n}\n\ncontract MockRewardPool {\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 public pid;\n address public stakingToken;\n address public rewardTokenA;\n address public rewardTokenB;\n address public operator;\n\n uint256 private _totalSupply;\n\n mapping(address => uint256) private _balances;\n mapping(address => uint256) public rewards;\n\n constructor(\n uint256 _pid,\n address _stakingToken,\n address _rewardTokenA,\n address _rewardTokenB,\n address _operator\n ) public {\n pid = _pid;\n stakingToken = _stakingToken;\n rewardTokenA = _rewardTokenA;\n rewardTokenB = _rewardTokenB;\n operator = _operator;\n }\n\n function totalSupply() public view returns (uint256) {\n return _totalSupply;\n }\n\n function balanceOf(address account) public view returns (uint256) {\n return _balances[account];\n }\n\n function stakeFor(address _for, uint256 _amount) public returns (bool) {\n require(_amount > 0, \"RewardPool : Cannot stake 0\");\n\n //give to _for\n _totalSupply = _totalSupply.add(_amount);\n _balances[_for] = _balances[_for].add(_amount);\n\n //take away from sender\n IERC20(stakingToken).safeTransferFrom(\n msg.sender,\n address(this),\n _amount\n );\n\n return true;\n }\n\n function withdrawAndUnwrap(uint256 amount, bool claim)\n public\n returns (bool)\n {\n _totalSupply = _totalSupply.sub(amount);\n _balances[msg.sender] = _balances[msg.sender].sub(amount);\n\n //tell operator to withdraw from here directly to user\n IDeposit(operator).withdrawTo(pid, amount, msg.sender);\n\n //get rewards too\n if (claim) {\n getReward(msg.sender, true);\n }\n return true;\n }\n\n function withdrawAllAndUnwrap(bool claim) external {\n withdrawAndUnwrap(_balances[msg.sender], claim);\n }\n\n // solhint-disable-next-line no-unused-vars\n function getReward(address _account, bool _claimExtras)\n public\n returns (bool)\n {\n IMintableERC20(rewardTokenA).mint(2 * 1e18);\n IERC20(rewardTokenA).transfer(_account, 2 * 1e18);\n\n IMintableERC20(rewardTokenB).mint(3 * 1e18);\n IERC20(rewardTokenB).transfer(_account, 3 * 1e18);\n\n return true;\n }\n\n function getReward() public returns (bool) {\n getReward(msg.sender, true);\n }\n}\n" + }, + "contracts/mocks/ForceEtherSender.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract ForceEtherSender {\n // Constructor to optionally receive Ether upon deployment\n constructor() payable {}\n\n // Function to allow the contract to receive Ether\n receive() external payable {}\n\n // Function to self-destruct and force-send Ether to an address\n function forceSend(address payable recipient) external {\n // Requires that the contract has a balance greater than 0\n require(address(this).balance > 0, \"No Ether to send\");\n\n // selfdestruct sends all Ether held by the contract to the recipient\n selfdestruct(recipient);\n }\n}\n" + }, + "contracts/mocks/MintableERC20.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ninterface IMintableERC20 {\n function mint(uint256 value) external;\n\n function mintTo(address to, uint256 value) external;\n}\n\n/**\n * @title MintableERC20\n * @dev Exposes the mint function of ERC20 for tests\n */\nabstract contract MintableERC20 is IMintableERC20, ERC20 {\n /**\n * @dev Function to mint tokens\n * @param _value The amount of tokens to mint.\n */\n function mint(uint256 _value) public virtual override {\n _mint(msg.sender, _value);\n }\n\n /**\n * @dev Function to mint tokens\n * @param _to Address to mint to.\n * @param _value The amount of tokens to mint.\n */\n function mintTo(address _to, uint256 _value) public virtual override {\n _mint(_to, _value);\n }\n}\n" + }, + "contracts/mocks/Mock1InchSwapRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { SwapDescription } from \"../interfaces/IOneInch.sol\";\n\ncontract Mock1InchSwapRouter {\n using SafeERC20 for IERC20;\n\n event MockSwap(address executor, bytes permitData, bytes executorData);\n\n event MockSwapDesc(\n address srcToken,\n address dstToken,\n address srcReceiver,\n address dstReceiver,\n uint256 amount,\n uint256 minReturnAmount,\n uint256 flags\n );\n\n event MockUnoswapTo(\n address recipient,\n address srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] pools\n );\n\n event MockUniswapV3SwapTo(\n address recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] pools\n );\n\n /**\n * @dev transfers the shource asset and returns the minReturnAmount of the destination asset.\n */\n function swap(\n address executor,\n SwapDescription calldata desc,\n bytes calldata permitData,\n bytes calldata executorData\n ) public returns (uint256 returnAmount, uint256 spentAmount) {\n // Transfer the source tokens to the receiver contract\n IERC20(desc.srcToken).safeTransferFrom(\n msg.sender,\n desc.srcReceiver,\n desc.amount\n );\n\n // Transfer the destination tokens to the recipient\n IERC20(desc.dstToken).safeTransfer(\n desc.dstReceiver,\n desc.minReturnAmount\n );\n\n emit MockSwap(executor, permitData, executorData);\n _swapDesc(desc);\n returnAmount = 0;\n spentAmount = 0;\n }\n\n function _swapDesc(SwapDescription calldata desc) public {\n emit MockSwapDesc(\n address(desc.srcToken),\n address(desc.dstToken),\n desc.srcReceiver,\n desc.dstReceiver,\n desc.amount,\n desc.minReturnAmount,\n desc.flags\n );\n }\n\n /**\n * @dev only transfers the source asset to this contract.\n * Ideally it would return the destination asset but that's encoded in the pools array.\n */\n function unoswapTo(\n address payable recipient,\n address srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) public returns (uint256 returnAmount) {\n // transfer the from asset from the caller\n IERC20(srcToken).safeTransferFrom(msg.sender, address(this), amount);\n\n emit MockUnoswapTo(recipient, srcToken, amount, minReturn, pools);\n returnAmount = 0;\n }\n\n /**\n * @dev does not do any transfers. Just emits MockUniswapV3SwapTo.\n */\n function uniswapV3SwapTo(\n address payable recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) public returns (uint256 returnAmount) {\n emit MockUniswapV3SwapTo(recipient, amount, minReturn, pools);\n returnAmount = 0;\n }\n}\n" + }, + "contracts/mocks/MockAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { IAaveLendingPool, ILendingPoolAddressesProvider } from \"../strategies/IAave.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// 1. User calls 'getLendingPool'\n// 2. User calls 'deposit' (Aave)\n// - Deposit their underlying\n// - Mint aToken to them\n// 3. User calls redeem (aToken)\n// - Retrieve their aToken\n// - Return equal amount of underlying\n\ncontract MockAToken is MintableERC20 {\n address public lendingPool;\n IERC20 public underlyingToken;\n using SafeERC20 for IERC20;\n\n constructor(\n address _lendingPool,\n string memory _name,\n string memory _symbol,\n IERC20 _underlyingToken\n ) ERC20(_name, _symbol) {\n lendingPool = _lendingPool;\n underlyingToken = _underlyingToken;\n // addMinter(_lendingPool);\n }\n\n function decimals() public view override returns (uint8) {\n return ERC20(address(underlyingToken)).decimals();\n }\n\n function poolRedeem(uint256 _amount, address _to) external {\n require(msg.sender == lendingPool, \"pool only\");\n // Redeem these a Tokens\n _burn(_to, _amount);\n // For the underlying\n underlyingToken.safeTransferFrom(lendingPool, _to, _amount);\n }\n}\n\ncontract MockAave is IAaveLendingPool, ILendingPoolAddressesProvider {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n mapping(address => address) reserveToAToken;\n address pool = address(this);\n address payable core = payable(address(this));\n uint256 factor;\n\n function addAToken(address _aToken, address _underlying) public {\n IERC20(_underlying).safeApprove(_aToken, 0);\n IERC20(_underlying).safeApprove(_aToken, type(uint256).max);\n reserveToAToken[_underlying] = _aToken;\n }\n\n // set the reserve factor / basically the interest on deposit\n // in 18 precision\n // so 0.5% would be 5 * 10 ^ 15\n function setFactor(uint256 factor_) public {\n factor = factor_;\n }\n\n function deposit(\n address _reserve,\n uint256 _amount,\n address _to,\n uint16 /*_referralCode*/\n ) external override {\n uint256 previousBal = IERC20(reserveToAToken[_reserve]).balanceOf(\n msg.sender\n );\n uint256 interest = previousBal.mulTruncate(factor);\n MintableERC20(reserveToAToken[_reserve]).mintTo(msg.sender, interest);\n // Take their reserve\n IERC20(_reserve).safeTransferFrom(msg.sender, address(this), _amount);\n // Credit them with aToken\n MintableERC20(reserveToAToken[_reserve]).mintTo(_to, _amount);\n }\n\n function withdraw(\n address asset,\n uint256 amount,\n address to\n ) external override returns (uint256) {\n MockAToken atoken = MockAToken(reserveToAToken[asset]);\n atoken.poolRedeem(amount, to);\n return amount;\n }\n\n function getLendingPool() external view override returns (address) {\n return pool;\n }\n}\n" + }, + "contracts/mocks/MockAaveIncentivesController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { MockStkAave } from \"./MockStkAave.sol\";\n\ncontract MockAaveIncentivesController {\n mapping(address => uint256) private rewards;\n MockStkAave public REWARD_TOKEN;\n\n constructor(address _reward_token) {\n REWARD_TOKEN = MockStkAave(_reward_token);\n }\n\n function setRewardsBalance(address user, uint256 amount) external {\n rewards[user] = amount;\n }\n\n /**\n * @dev Returns the total of rewards of an user, already accrued + not yet accrued\n * @param user The address of the user\n * @return The rewards\n **/\n // solhint-disable-next-line no-unused-vars\n function getRewardsBalance(address[] calldata assets, address user)\n external\n view\n returns (uint256)\n {\n return rewards[user];\n }\n\n /**\n * @dev Claims reward for an user, on all the assets of the lending pool, accumulating the pending rewards\n * @param amount Amount of rewards to claim\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewards(\n // solhint-disable-next-line no-unused-vars\n address[] calldata assets,\n uint256 amount,\n address to\n ) external returns (uint256) {\n require(amount > 0);\n require(rewards[to] == amount);\n REWARD_TOKEN.mint(amount);\n require(REWARD_TOKEN.transfer(to, amount));\n // solhint-disable-next-line reentrancy\n rewards[to] = 0;\n return amount;\n }\n}\n" + }, + "contracts/mocks/MockAAVEToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAAVEToken is MintableERC20 {\n constructor() ERC20(\"AAVE\", \"AAVE\") {}\n}\n" + }, + "contracts/mocks/MockAero.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAero is MintableERC20 {\n constructor() ERC20(\"Aerodrome\", \"AERO\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/MockAura.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAura is MintableERC20 {\n constructor() ERC20(\"Aura\", \"AURA\") {}\n}\n" + }, + "contracts/mocks/MockBAL.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockBAL is MintableERC20 {\n constructor() ERC20(\"Balancer\", \"BAL\") {}\n}\n" + }, + "contracts/mocks/MockBalancerVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// import \"hardhat/console.sol\";\n\ncontract MockBalancerVault {\n using StableMath for uint256;\n uint256 public slippage = 1 ether;\n bool public transferDisabled = false;\n bool public slippageErrorDisabled = false;\n\n function swap(\n IBalancerVault.SingleSwap calldata singleSwap,\n IBalancerVault.FundManagement calldata funds,\n uint256 minAmountOut,\n uint256\n ) external returns (uint256 amountCalculated) {\n amountCalculated = (minAmountOut * slippage) / 1 ether;\n if (!slippageErrorDisabled) {\n require(amountCalculated >= minAmountOut, \"Slippage error\");\n }\n IERC20(singleSwap.assetIn).transferFrom(\n funds.sender,\n address(this),\n singleSwap.amount\n );\n if (!transferDisabled) {\n MintableERC20(singleSwap.assetOut).mintTo(\n funds.recipient,\n amountCalculated\n );\n }\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n\n function getPoolTokenInfo(bytes32 poolId, address token)\n external\n view\n returns (\n uint256,\n uint256,\n uint256,\n address\n )\n {}\n\n function disableTransfer() external {\n transferDisabled = true;\n }\n\n function disableSlippageError() external {\n slippageErrorDisabled = true;\n }\n}\n" + }, + "contracts/mocks/MockBeaconConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconConsolidation } from \"../beacon/BeaconConsolidation.sol\";\n\ncontract MockBeaconConsolidation {\n function fee() external view returns (uint256) {\n return BeaconConsolidation.fee();\n }\n\n function request(bytes calldata source, bytes calldata target)\n external\n returns (uint256 fee_)\n {\n return BeaconConsolidation.request(source, target);\n }\n}\n" + }, + "contracts/mocks/MockBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"../beacon/BeaconProofsLib.sol\";\nimport { BeaconProofs } from \"../beacon/BeaconProofs.sol\";\n\ncontract MockBeaconProofs is BeaconProofs {\n function concatGenIndices(\n uint256 index1,\n uint256 height2,\n uint256 index2\n ) external pure returns (uint256 genIndex) {\n return BeaconProofsLib.concatGenIndices(index1, height2, index2);\n }\n}\n" + }, + "contracts/mocks/MockBeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconRoots } from \"../beacon/BeaconRoots.sol\";\n\ncontract MockBeaconRoots {\n // Mapping to simulate the ring buffer: timestamp => beacon block root\n mapping(uint256 => bytes32) internal _beaconRoots;\n\n // Event to log when a new root is set (for testing)\n event RootSet(uint256 indexed timestamp, bytes32 root);\n\n // Fallback function to handle raw 32-byte timestamp input\n // solhint-disable no-complex-fallback\n fallback() external {\n // Ensure input is exactly 32 bytes (big-endian encoded timestamp)\n require(msg.data.length == 32, \"Input must be 32 bytes\");\n\n // Decode the 32-byte input as a uint256 timestamp (big-endian)\n uint256 timestamp = abi.decode(msg.data, (uint256));\n\n // Don't do any validation of timestamp so we can test any block\n\n // Retrieve the root. Will return bytes32(0) if not set.\n bytes32 root = _beaconRoots[timestamp];\n\n // Return the 32-byte root directly\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0, root)\n return(0, 32)\n }\n }\n\n // Mock function to set a beacon block root (for testing)\n function setBeaconRoot(uint256 timestamp, bytes32 root) external {\n require(timestamp > 0, \"Invalid timestamp\");\n require(root != bytes32(0), \"Invalid root\");\n\n // Store the root at the given timestamp\n _beaconRoots[timestamp] = root;\n\n emit RootSet(timestamp, root);\n }\n\n function setBeaconRoot(bytes32 root) external {\n require(root != bytes32(0), \"Invalid root\");\n\n // Store the root at the given timestamp\n _beaconRoots[block.timestamp] = root;\n\n emit RootSet(block.timestamp, root);\n }\n\n function parentBlockRoot(uint64 timestamp)\n external\n view\n returns (bytes32 parentRoot)\n {\n return BeaconRoots.parentBlockRoot(timestamp);\n }\n\n function latestBlockRoot()\n external\n view\n returns (bytes32 parentRoot, uint64 timestamp)\n {\n timestamp = uint64(block.timestamp);\n parentRoot = BeaconRoots.parentBlockRoot(timestamp);\n }\n}\n" + }, + "contracts/mocks/MockChainlinkOracleFeed.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\ncontract MockChainlinkOracleFeed is AggregatorV3Interface {\n int256 price;\n uint8 numDecimals;\n\n constructor(int256 _price, uint8 _decimals) {\n price = _price;\n numDecimals = _decimals;\n }\n\n function decimals() external view override returns (uint8) {\n return numDecimals;\n }\n\n function description() external pure override returns (string memory) {\n return \"MockOracleEthFeed\";\n }\n\n function version() external pure override returns (uint256) {\n return 1;\n }\n\n function setPrice(int256 _price) public {\n price = _price;\n }\n\n function setDecimals(uint8 _decimals) public {\n numDecimals = _decimals;\n }\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(uint80 _roundId)\n external\n view\n override\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n )\n {\n roundId = _roundId;\n answer = price;\n startedAt = 0;\n updatedAt = 0;\n answeredInRound = 0;\n }\n\n function latestRoundData()\n external\n view\n override\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n )\n {\n roundId = 0;\n answer = price;\n startedAt = 0;\n updatedAt = block.timestamp;\n answeredInRound = 0;\n }\n}\n" + }, + "contracts/mocks/MockCOMP.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockCOMP is MintableERC20 {\n constructor() ERC20(\"COMP\", \"COMP\") {}\n}\n" + }, + "contracts/mocks/MockComptroller.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract MockComptroller {\n // Claim all the COMP accrued by specific holders in specific markets for their supplies and/or borrows\n function claimComp(\n address[] memory holders,\n address[] memory cTokens,\n bool borrowers,\n bool suppliers\n ) external {}\n}\n" + }, + "contracts/mocks/MockCToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { ICERC20 } from \"../strategies/ICompound.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockCToken is ICERC20, ERC20 {\n using StableMath for uint256;\n\n IERC20 public underlyingToken;\n // underlying = cToken * exchangeRate\n // cToken = underlying / exchangeRate\n uint256 exchangeRate;\n address public override comptroller;\n\n constructor(ERC20 _underlyingToken, address _comptroller)\n ERC20(\"cMock\", \"cMK\")\n {\n uint8 underlyingDecimals = _underlyingToken.decimals();\n // if has 18 dp, exchange rate should be 1e26\n // if has 8 dp, exchange rate should be 1e18\n if (underlyingDecimals > 8) {\n exchangeRate = 10**uint256(18 + underlyingDecimals - 10);\n } else if (underlyingDecimals < 8) {\n // e.g. 18-8+6 = 16\n exchangeRate = 10**uint256(18 - 8 + underlyingDecimals);\n } else {\n exchangeRate = 1e18;\n }\n underlyingToken = _underlyingToken;\n comptroller = _comptroller;\n }\n\n function decimals() public pure override returns (uint8) {\n return 8;\n }\n\n function mint(uint256 mintAmount) public override returns (uint256) {\n // Credit them with cToken\n _mint(msg.sender, mintAmount.divPrecisely(exchangeRate));\n // Take their reserve\n underlyingToken.transferFrom(msg.sender, address(this), mintAmount);\n return 0;\n }\n\n function redeem(uint256 redeemAmount) external override returns (uint256) {\n uint256 tokenAmount = redeemAmount.mulTruncate(exchangeRate);\n // Burn the cToken\n _burn(msg.sender, redeemAmount);\n // Transfer underlying to caller\n underlyingToken.transfer(msg.sender, tokenAmount);\n return 0;\n }\n\n function redeemUnderlying(uint256 redeemAmount)\n external\n override\n returns (uint256)\n {\n uint256 cTokens = redeemAmount.divPrecisely(exchangeRate);\n // Burn the cToken\n _burn(msg.sender, cTokens);\n // Transfer underlying to caller\n underlyingToken.transfer(msg.sender, redeemAmount);\n return 0;\n }\n\n function balanceOfUnderlying(address owner)\n external\n view\n override\n returns (uint256)\n {\n uint256 cTokenBal = this.balanceOf(owner);\n return cTokenBal.mulTruncate(exchangeRate);\n }\n\n function balanceOf(address owner)\n public\n view\n override(ICERC20, ERC20)\n returns (uint256)\n {\n return ERC20.balanceOf(owner);\n }\n\n function updateExchangeRate()\n internal\n view\n returns (uint256 newExchangeRate)\n {\n uint256 factor = 100002 * (10**13); // 0.002%\n newExchangeRate = exchangeRate.mulTruncate(factor);\n }\n\n function exchangeRateStored() external view override returns (uint256) {\n return exchangeRate;\n }\n\n function supplyRatePerBlock() external pure override returns (uint256) {\n return 141 * (10**8);\n }\n}\n" + }, + "contracts/mocks/MockCVXLocker.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MockCVXLocker {\n address public immutable cvx;\n mapping(address => uint256) public lockedBalanceOf;\n\n constructor(address _cvx) {\n cvx = _cvx;\n }\n\n function lock(\n address _account,\n uint256 _amount,\n uint256\n ) external {\n lockedBalanceOf[_account] += _amount;\n ERC20(cvx).transferFrom(msg.sender, address(this), _amount);\n }\n\n function unlockAllTokens(address _account) external {\n lockedBalanceOf[_account] = 0;\n ERC20(cvx).transfer(_account, lockedBalanceOf[_account]);\n }\n}\n" + }, + "contracts/mocks/MockDAI.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockDAI is MintableERC20 {\n constructor() ERC20(\"DAI\", \"DAI\") {}\n}\n" + }, + "contracts/mocks/MockDepositContract.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IDepositContract } from \"./../interfaces/IDepositContract.sol\";\n\ncontract MockDepositContract is IDepositContract {\n uint256 deposit_count;\n\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable override {\n require(pubkey.length == 48, \"DepositContract: invalid pubkey length\");\n require(\n withdrawal_credentials.length == 32,\n \"DepositContract: invalid withdrawal_credentials length\"\n );\n require(\n signature.length == 96,\n \"DepositContract: invalid signature length\"\n );\n\n // Check deposit amount\n require(msg.value >= 1 ether, \"DepositContract: deposit value too low\");\n require(\n msg.value % 1 gwei == 0,\n \"DepositContract: deposit value not multiple of gwei\"\n );\n uint256 deposit_amount = msg.value / 1 gwei;\n require(\n deposit_amount <= type(uint64).max,\n \"DepositContract: deposit value too high\"\n );\n\n // Emit `DepositEvent` log\n bytes memory amount = to_little_endian_64(uint64(deposit_amount));\n emit DepositEvent(\n pubkey,\n withdrawal_credentials,\n amount,\n signature,\n to_little_endian_64(uint64(deposit_count))\n );\n require(\n deposit_data_root != 0,\n \"DepositContract: invalid deposit_data_root\"\n );\n }\n\n function get_deposit_root() external view override returns (bytes32) {\n // just return some bytes32\n return sha256(abi.encodePacked(deposit_count, bytes16(0)));\n }\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view override returns (bytes memory) {\n return to_little_endian_64(uint64(deposit_count));\n }\n\n function to_little_endian_64(uint64 value)\n internal\n pure\n returns (bytes memory ret)\n {\n ret = new bytes(8);\n bytes8 bytesValue = bytes8(value);\n // Byteswapping during copying to bytes.\n ret[0] = bytesValue[7];\n ret[1] = bytesValue[6];\n ret[2] = bytesValue[5];\n ret[3] = bytesValue[4];\n ret[4] = bytesValue[3];\n ret[5] = bytesValue[2];\n ret[6] = bytesValue[1];\n ret[7] = bytesValue[0];\n }\n}\n" + }, + "contracts/mocks/MockEvilDAI.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract MockEvilDAI is MintableERC20 {\n address host;\n address realCoin;\n\n constructor(address _host, address _realCoin) ERC20(\"DAI\", \"DAI\") {\n host = _host;\n realCoin = _realCoin;\n }\n\n function transferFrom(\n // solhint-disable-next-line no-unused-vars\n address _from,\n // solhint-disable-next-line no-unused-vars\n address _to,\n uint256 _amount\n ) public override returns (bool) {\n // call mint again!\n if (_amount != 69) {\n IVault(host).mint(address(this), 69, 0);\n }\n return true;\n }\n}\n" + }, + "contracts/mocks/MockEvilReentrantContract.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IRateProvider } from \"../interfaces/balancer/IRateProvider.sol\";\n\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { IERC20 } from \"../utils/InitializableAbstractStrategy.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockEvilReentrantContract {\n using StableMath for uint256;\n\n IBalancerVault public immutable balancerVault;\n IERC20 public immutable reth;\n IERC20 public immutable weth;\n IVault public immutable oethVault;\n address public immutable poolAddress;\n bytes32 public immutable balancerPoolId;\n\n constructor(\n address _balancerVault,\n address _oethVault,\n address _reth,\n address _weth,\n address _poolAddress,\n bytes32 _poolId\n ) {\n balancerVault = IBalancerVault(_balancerVault);\n oethVault = IVault(_oethVault);\n reth = IERC20(_reth);\n weth = IERC20(_weth);\n poolAddress = _poolAddress;\n balancerPoolId = _poolId;\n }\n\n function doEvilStuff() public {\n address priceProvider = oethVault.priceProvider();\n uint256 rethPrice = IOracle(priceProvider).price(address(reth));\n\n // 1. Join pool\n uint256[] memory amounts = new uint256[](2);\n amounts[0] = uint256(10 ether);\n amounts[1] = rethPrice * 10;\n\n address[] memory assets = new address[](2);\n assets[0] = address(reth);\n assets[1] = address(weth);\n\n uint256 minBPT = getBPTExpected(assets, amounts).mulTruncate(\n 0.99 ether\n );\n\n bytes memory joinUserData = abi.encode(\n IBalancerVault.WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,\n amounts,\n minBPT\n );\n\n IBalancerVault.JoinPoolRequest memory joinRequest = IBalancerVault\n .JoinPoolRequest(assets, amounts, joinUserData, false);\n\n balancerVault.joinPool(\n balancerPoolId,\n address(this),\n address(this),\n joinRequest\n );\n\n uint256 bptTokenBalance = IERC20(poolAddress).balanceOf(address(this));\n\n // 2. Redeem as ETH\n bytes memory exitUserData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT,\n bptTokenBalance,\n 1\n );\n\n assets[1] = address(0); // Receive ETH instead of WETH\n uint256[] memory exitAmounts = new uint256[](2);\n exitAmounts[1] = 15 ether;\n IBalancerVault.ExitPoolRequest memory exitRequest = IBalancerVault\n .ExitPoolRequest(assets, exitAmounts, exitUserData, false);\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n payable(address(this)),\n exitRequest\n );\n bptTokenBalance = IERC20(poolAddress).balanceOf(address(this));\n }\n\n function getBPTExpected(address[] memory _assets, uint256[] memory _amounts)\n internal\n view\n virtual\n returns (uint256 bptExpected)\n {\n // Get the oracle from the OETH Vault\n address priceProvider = oethVault.priceProvider();\n\n for (uint256 i = 0; i < _assets.length; ++i) {\n uint256 strategyAssetMarketPrice = IOracle(priceProvider).price(\n _assets[i]\n );\n // convert asset amount to ETH amount\n bptExpected =\n bptExpected +\n _amounts[i].mulTruncate(strategyAssetMarketPrice);\n }\n\n uint256 bptRate = IRateProvider(poolAddress).getRate();\n // Convert ETH amount to BPT amount\n bptExpected = bptExpected.divPrecisely(bptRate);\n }\n\n function approveAllTokens() public {\n // Approve all tokens\n weth.approve(address(oethVault), type(uint256).max);\n reth.approve(poolAddress, type(uint256).max);\n weth.approve(poolAddress, type(uint256).max);\n reth.approve(address(balancerVault), type(uint256).max);\n weth.approve(address(balancerVault), type(uint256).max);\n }\n\n receive() external payable {\n // 3. Try to mint OETH\n oethVault.mint(address(weth), 1 ether, 0.9 ether);\n }\n}\n" + }, + "contracts/mocks/MockfrxETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockfrxETH is MintableERC20 {\n constructor() ERC20(\"frxETH\", \"frxETH\") {}\n}\n" + }, + "contracts/mocks/MockFrxETHMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockFrxETHMinter {\n address public immutable frxETH;\n address public immutable sfrxETH;\n\n constructor(address _frxETH, address _sfrxETH) {\n frxETH = _frxETH;\n sfrxETH = _sfrxETH;\n }\n\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares)\n {\n IMintableERC20(frxETH).mintTo(sfrxETH, msg.value);\n IMintableERC20(sfrxETH).mintTo(recipient, msg.value);\n shares = msg.value;\n }\n}\n" + }, + "contracts/mocks/MockLimitedWrappedOusd.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WrappedOusd } from \"../token/WrappedOusd.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MockLimitedWrappedOusd is WrappedOusd {\n constructor(ERC20 underlying_) WrappedOusd(underlying_) {}\n\n function maxDeposit(address)\n public\n view\n virtual\n override\n returns (uint256)\n {\n return 1e18;\n }\n}\n" + }, + "contracts/mocks/MockMaverickDistributor.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Pool } from \"../interfaces/plume/IMaverickV2Pool.sol\";\n\ncontract MockMaverickDistributor {\n IERC20 public immutable rewardToken;\n\n uint256 public lastEpoch;\n uint256 public rewardAmount;\n\n constructor(address _rewardToken) {\n rewardToken = IERC20(_rewardToken);\n }\n\n function setLastEpoch(uint256 _epoch) external {\n lastEpoch = _epoch;\n }\n\n function setRewardTokenAmount(uint256 _amount) external {\n rewardAmount = _amount;\n }\n\n function claimLp(\n address recipient,\n uint256,\n IMaverickV2Pool,\n uint32[] memory,\n uint256\n ) external returns (uint256 amount) {\n rewardToken.transfer(recipient, rewardAmount);\n return rewardAmount;\n }\n}\n" + }, + "contracts/mocks/MockMetadataToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// IERC20Metadata is used in the resolveAsset function in contracts/utils/assets.js\n// We just need to import it here to make its ABI available to Hardhat\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n" + }, + "contracts/mocks/MockMintableUniswapPair.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport \"./MockUniswapPair.sol\";\n\ncontract MockMintableUniswapPair is MockUniswapPair, MintableERC20 {\n constructor(\n address _token0,\n address _token1,\n uint112 _reserve0,\n uint112 _reserve1\n )\n MockUniswapPair(_token0, _token1, _reserve0, _reserve1)\n ERC20(\"Uniswap V2\", \"UNI-v2\")\n {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/MockNonRebasing.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { OUSD } from \"../token/OUSD.sol\";\n\ncontract MockNonRebasing {\n OUSD oUSD;\n\n function setOUSD(address _oUSDAddress) public {\n oUSD = OUSD(_oUSDAddress);\n }\n\n function rebaseOptIn() public {\n oUSD.rebaseOptIn();\n }\n\n function rebaseOptOut() public {\n oUSD.rebaseOptOut();\n }\n\n function transfer(address _to, uint256 _value) public {\n oUSD.transfer(_to, _value);\n }\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) public {\n oUSD.transferFrom(_from, _to, _value);\n }\n\n function approve(address _spender, uint256 _addedValue) public {\n oUSD.approve(_spender, _addedValue);\n }\n\n function mintOusd(\n address _vaultContract,\n address _asset,\n uint256 _amount\n ) public {\n IVault(_vaultContract).mint(_asset, _amount, 0);\n }\n\n function redeemOusd(address _vaultContract, uint256 _amount) public {\n IVault(_vaultContract).redeem(_amount, 0);\n }\n\n function approveFor(\n address _contract,\n address _spender,\n uint256 _addedValue\n ) public {\n IERC20(_contract).approve(_spender, _addedValue);\n }\n}\n" + }, + "contracts/mocks/MockNonStandardToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\nimport \"./MintableERC20.sol\";\n\n/**\n * Mock token contract to simulate tokens that don't\n * throw/revert when a transfer/transferFrom call fails\n */\ncontract MockNonStandardToken is MintableERC20 {\n using SafeMath for uint256;\n\n constructor() ERC20(\"NonStandardToken\", \"NonStandardToken\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n\n function transfer(address recipient, uint256 amount)\n public\n override\n returns (bool)\n {\n if (balanceOf(msg.sender) < amount) {\n // Fail silently\n return false;\n }\n\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public override returns (bool) {\n if (balanceOf(sender) < amount) {\n // Fail silently\n return false;\n }\n\n _transfer(sender, recipient, amount);\n _approve(\n sender,\n _msgSender(),\n allowance(sender, _msgSender()).sub(\n amount,\n \"ERC20: transfer amount exceeds allowance\"\n )\n );\n return true;\n }\n}\n" + }, + "contracts/mocks/MockOETHVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultCore } from \"../vault/OETHVaultCore.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MockOETHVault is OETHVaultCore {\n using StableMath for uint256;\n\n constructor(address _weth) OETHVaultCore(_weth) {\n _setGovernor(msg.sender);\n }\n\n function supportAsset(address asset) external {\n assets[asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(0),\n decimals: 18,\n allowedOracleSlippageBps: 0\n });\n\n allAssets.push(asset);\n }\n}\n" + }, + "contracts/mocks/MockOETHVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"../vault/OETHVaultAdmin.sol\";\n\ncontract MockOETHVaultAdmin is OETHVaultAdmin {\n constructor(address _weth) OETHVaultAdmin(_weth) {}\n\n // fetches the WETH amount in outstanding withdrawals\n function outstandingWithdrawalsAmount()\n external\n view\n returns (uint256 wethAmount)\n {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n wethAmount = queue.queued - queue.claimed;\n }\n\n function wethAvailable() external view returns (uint256) {\n return _wethAvailable();\n }\n}\n" + }, + "contracts/mocks/MockOGN.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./BurnableERC20.sol\";\nimport \"./MintableERC20.sol\";\n\n/**\n * @title Origin token (OGN).\n *\n * @dev Token that allows minting and burning.\n * @dev Important note:\n * @dev There is a known race condition in the ERC20 standard on the approve() method.\n * @dev See details: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n * @dev The Origin token contract implements the increaseApproval() and decreaseApproval() methods.\n * @dev It is strongly recommended to use those methods rather than approve()\n * @dev when updating the token allowance.\n */\ncontract MockOGN is MintableERC20, BurnableERC20 {\n event SetWhitelistExpiration(uint256 expiration);\n event AllowedTransactorAdded(address sender);\n event AllowedTransactorRemoved(address sender);\n event AddCallSpenderWhitelist(address enabler, address spender);\n event RemoveCallSpenderWhitelist(address disabler, address spender);\n\n mapping(address => bool) public callSpenderWhitelist;\n address public owner = msg.sender;\n // UNIX timestamp (in seconds) after which this whitelist no longer applies\n uint256 public whitelistExpiration;\n // While the whitelist is active, either the sender or recipient must be\n // in allowedTransactors.\n mapping(address => bool) public allowedTransactors;\n\n // @dev Constructor that gives msg.sender all initial tokens.\n constructor(uint256 _initialSupply) ERC20(\"OriginToken\", \"OGN\") {\n owner = msg.sender;\n _mint(owner, _initialSupply);\n }\n\n //\n // approveAndCall methods\n //\n\n // @dev Add spender to whitelist of spenders for approveAndCall\n // @param _spender Address to add\n function addCallSpenderWhitelist(address _spender) public onlyOwner {\n callSpenderWhitelist[_spender] = true;\n emit AddCallSpenderWhitelist(msg.sender, _spender);\n }\n\n // @dev Remove spender from whitelist of spenders for approveAndCall\n // @param _spender Address to remove\n function removeCallSpenderWhitelist(address _spender) public onlyOwner {\n delete callSpenderWhitelist[_spender];\n emit RemoveCallSpenderWhitelist(msg.sender, _spender);\n }\n\n // @dev Approve transfer of tokens and make a contract call in a single\n // @dev transaction. This allows a DApp to avoid requiring two MetaMask\n // @dev approvals for a single logical action, such as creating a listing,\n // @dev which requires the seller to approve a token transfer and the\n // @dev marketplace contract to transfer tokens from the seller.\n //\n // @dev This is based on the ERC827 function approveAndCall and avoids\n // @dev security issues by only working with a whitelisted set of _spender\n // @dev addresses. The other difference is that the combination of this\n // @dev function ensures that the proxied function call receives the\n // @dev msg.sender for this function as its first parameter.\n //\n // @param _spender The address that will spend the funds.\n // @param _value The amount of tokens to be spent.\n // @param _selector Function selector for function to be called.\n // @param _callParams Packed, encoded parameters, omitting the first parameter which is always msg.sender\n function approveAndCallWithSender(\n address _spender,\n uint256 _value,\n bytes4 _selector,\n bytes memory _callParams\n ) public payable returns (bool) {\n require(_spender != address(this), \"token contract can't be approved\");\n require(callSpenderWhitelist[_spender], \"spender not in whitelist\");\n\n require(super.approve(_spender, _value), \"approve failed\");\n\n bytes memory callData = abi.encodePacked(\n _selector,\n uint256(uint160(msg.sender)),\n _callParams\n );\n // solium-disable-next-line security/no-call-value\n (bool success, ) = _spender.call{ value: msg.value }(callData);\n require(success, \"proxied call failed\");\n return true;\n }\n\n //\n // Functions for maintaining whitelist\n //\n\n modifier onlyOwner() {\n require(msg.sender == owner);\n _;\n }\n modifier allowedTransfer(address _from, address _to) {\n require(\n // solium-disable-next-line operator-whitespace\n !whitelistActive() ||\n allowedTransactors[_from] ||\n allowedTransactors[_to],\n \"neither sender nor recipient are allowed\"\n );\n _;\n }\n\n function whitelistActive() public view returns (bool) {\n return block.timestamp < whitelistExpiration;\n }\n\n function addAllowedTransactor(address _transactor) public onlyOwner {\n emit AllowedTransactorAdded(_transactor);\n allowedTransactors[_transactor] = true;\n }\n\n function removeAllowedTransactor(address _transactor) public onlyOwner {\n emit AllowedTransactorRemoved(_transactor);\n delete allowedTransactors[_transactor];\n }\n\n /**\n * @dev Set the whitelist expiration, after which the whitelist no longer\n * applies.\n */\n function setWhitelistExpiration(uint256 _expiration) public onlyOwner {\n // allow only if whitelist expiration hasn't yet been set, or if the\n // whitelist expiration hasn't passed yet\n require(\n whitelistExpiration == 0 || whitelistActive(),\n \"an expired whitelist cannot be extended\"\n );\n // prevent possible mistakes in calling this function\n require(\n _expiration >= block.timestamp + 1 days,\n \"whitelist expiration not far enough into the future\"\n );\n emit SetWhitelistExpiration(_expiration);\n whitelistExpiration = _expiration;\n }\n\n //\n // ERC20 transfer functions that have been overridden to enforce the\n // whitelist.\n //\n\n function transfer(address _to, uint256 _value)\n public\n override\n allowedTransfer(msg.sender, _to)\n returns (bool)\n {\n return super.transfer(_to, _value);\n }\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) public override allowedTransfer(_from, _to) returns (bool) {\n return super.transferFrom(_from, _to, _value);\n }\n}\n" + }, + "contracts/mocks/MockOGV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockOGV is MintableERC20 {\n constructor() ERC20(\"OGV\", \"OGV\") {}\n}\n" + }, + "contracts/mocks/MockOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/IPriceOracle.sol\";\nimport \"../interfaces/IMinMaxOracle.sol\";\n\n/**\n * Mock of both price Oracle and min max oracles\n */\ncontract MockOracle is IPriceOracle, IMinMaxOracle {\n mapping(bytes32 => uint256) prices;\n mapping(bytes32 => uint256[]) pricesMinMax;\n uint256 ethMin;\n uint256 ethMax;\n\n /**\n * @dev returns the asset price in USD, 6 decimal digits.\n * Compatible with the Open Price Feed.\n */\n function price(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n return prices[keccak256(abi.encodePacked(symbol))];\n }\n\n /**\n * @dev sets the price of the asset in USD, 6 decimal digits\n *\n */\n function setPrice(string calldata symbol, uint256 _price) external {\n prices[keccak256(abi.encodePacked(symbol))] = _price;\n }\n\n /**\n * @dev sets the min and max price of ETH in USD, 6 decimal digits\n *\n */\n function setEthPriceMinMax(uint256 _min, uint256 _max) external {\n ethMin = _min;\n ethMax = _max;\n }\n\n /**\n * @dev sets the prices Min Max for a specific symbol in ETH, 8 decimal digits\n *\n */\n function setTokPriceMinMax(\n string calldata symbol,\n uint256 _min,\n uint256 _max\n ) external {\n pricesMinMax[keccak256(abi.encodePacked(symbol))] = [_min, _max];\n }\n\n /**\n * @dev get the price of asset in ETH, 8 decimal digits.\n */\n function priceMin(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n uint256[] storage pMinMax = pricesMinMax[\n keccak256(abi.encodePacked(symbol))\n ];\n return (pMinMax[0] * ethMin) / 1e6;\n }\n\n /**\n * @dev get the price of asset in USD, 8 decimal digits.\n * Not needed for now\n */\n function priceMax(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n uint256[] storage pMinMax = pricesMinMax[\n keccak256(abi.encodePacked(symbol))\n ];\n return (pMinMax[1] * ethMax) / 1e6;\n }\n}\n" + }, + "contracts/mocks/MockOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"../oracle/AbstractOracleRouter.sol\";\n\n// @notice Oracle Router required for testing environment\ncontract MockOracleRouter is AbstractOracleRouter {\n struct FeedMetadata {\n address feedAddress;\n uint256 maxStaleness;\n }\n\n mapping(address => FeedMetadata) public assetToFeedMetadata;\n\n /* @dev Override feed and maxStaleness information for a particular asset\n * @param _asset the asset to override feed for\n * @param _feed new feed\n * @param _maxStaleness new maximum time allowed for feed data to be stale\n */\n function setFeed(\n address _asset,\n address _feed,\n uint256 _maxStaleness\n ) external {\n assetToFeedMetadata[_asset] = FeedMetadata(_feed, _maxStaleness);\n }\n\n /*\n * The dev version of the Oracle doesn't need to gas optimize and cache the decimals\n */\n function getDecimals(address _feed) internal view override returns (uint8) {\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n return AggregatorV3Interface(_feed).decimals();\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n FeedMetadata storage fm = assetToFeedMetadata[asset];\n feedAddress = fm.feedAddress;\n maxStaleness = fm.maxStaleness;\n }\n}\n" + }, + "contracts/mocks/MockOracleRouterNoStale.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { OracleRouter } from \"../oracle/OracleRouter.sol\";\nimport { OETHOracleRouter } from \"../oracle/OETHOracleRouter.sol\";\n\n// @notice Oracle Router used to bypass staleness\ncontract MockOracleRouterNoStale is OracleRouter {\n function feedMetadata(address asset)\n internal\n pure\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n (feedAddress, ) = super.feedMetadata(asset);\n maxStaleness = 365 days;\n }\n}\n\n// @notice Oracle Router used to bypass staleness\ncontract MockOETHOracleRouterNoStale is OETHOracleRouter {\n constructor() OETHOracleRouter() {}\n\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n (feedAddress, ) = super.feedMetadata(asset);\n maxStaleness = 365 days;\n }\n}\n" + }, + "contracts/mocks/MockOracleWeightedPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Variable, OracleAverageQuery, IOracleWeightedPool } from \"../interfaces/balancer/IOracleWeightedPool.sol\";\n\ncontract MockOracleWeightedPool is IOracleWeightedPool {\n uint256[] public nextResults;\n\n constructor() {\n nextResults = [1 ether, 1 ether];\n }\n\n function getTimeWeightedAverage(OracleAverageQuery[] memory)\n external\n view\n override\n returns (uint256[] memory results)\n {\n return nextResults;\n }\n\n function setNextResults(uint256[] calldata results) external {\n nextResults = results;\n }\n}\n" + }, + "contracts/mocks/MockPartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PartialWithdrawal } from \"../beacon/PartialWithdrawal.sol\";\n\ncontract MockPartialWithdrawal {\n function fee() external view returns (uint256) {\n return PartialWithdrawal.fee();\n }\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n external\n returns (uint256 fee_)\n {\n return PartialWithdrawal.request(validatorPubKey, amount);\n }\n}\n" + }, + "contracts/mocks/MockRebornMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n// solhint-disable-next-line no-console\nimport \"hardhat/console.sol\";\n\ncontract Sanctum {\n address public asset;\n address public vault;\n address public reborner;\n bool public shouldAttack = false;\n // should selfdestruct in the constructor\n bool public shouldDestruct = false;\n uint256 public targetMethod;\n address public ousdContract;\n\n constructor(address _asset, address _vault) {\n asset = _asset;\n vault = _vault;\n }\n\n function deploy(uint256 salt, bytes memory bytecode)\n public\n returns (address addr)\n {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt)\n }\n require(addr != address(0), \"Create2: Failed on deploy\");\n }\n\n function computeAddress(uint256 salt, bytes memory bytecode)\n public\n view\n returns (address)\n {\n bytes32 bytecodeHashHash = keccak256(bytecode);\n bytes32 _data = keccak256(\n abi.encodePacked(\n bytes1(0xff),\n address(this),\n salt,\n bytecodeHashHash\n )\n );\n return address(bytes20(_data << 96));\n }\n\n function setShouldAttack(bool _shouldAttack) public {\n shouldAttack = _shouldAttack;\n }\n\n // should call selfdestruct in the constructor\n function setShouldDesctruct(bool _shouldDestruct) public {\n shouldDestruct = _shouldDestruct;\n }\n\n function setTargetMethod(uint256 target) public {\n targetMethod = target;\n }\n\n function setOUSDAddress(address _ousdContract) public {\n ousdContract = _ousdContract;\n }\n}\n\ncontract Reborner {\n Sanctum sanctum;\n bool logging = false;\n\n constructor(address _sanctum) {\n log(\"We are created...\");\n sanctum = Sanctum(_sanctum);\n if (sanctum.shouldAttack()) {\n log(\"We are attacking now...\");\n\n uint256 target = sanctum.targetMethod();\n\n if (target == 1) {\n redeem();\n } else if (target == 2) {\n transfer();\n } else {\n mint();\n }\n }\n\n if (sanctum.shouldDestruct()) {\n bye();\n }\n }\n\n function mint() public {\n log(\"We are attempting to mint..\");\n address asset = sanctum.asset();\n address vault = sanctum.vault();\n IERC20(asset).approve(vault, 1e18);\n IVault(vault).mint(asset, 1e18, 0);\n log(\"We are now minting..\");\n }\n\n function redeem() public {\n log(\"We are attempting to redeem..\");\n address vault = sanctum.vault();\n IVault(vault).redeem(1e18, 1e18);\n log(\"We are now redeeming..\");\n }\n\n function transfer() public {\n log(\"We are attempting to transfer..\");\n address ousd = sanctum.ousdContract();\n require(IERC20(ousd).transfer(address(1), 1e18), \"transfer failed\");\n log(\"We are now transfering..\");\n }\n\n function bye() public {\n log(\"We are now destructing..\");\n selfdestruct(payable(msg.sender));\n }\n\n function log(string memory message) internal view {\n if (logging) {\n // solhint-disable-next-line no-console\n console.log(message);\n }\n }\n}\n" + }, + "contracts/mocks/MockRETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport \"../interfaces/IGetExchangeRateToken.sol\";\n\ncontract MockRETH is MintableERC20, IGetExchangeRateToken {\n uint256 private exchangeRate = 12e17;\n\n constructor() ERC20(\"Rocket Pool ETH\", \"rETH\") {}\n\n function getExchangeRate() external view override returns (uint256) {\n return exchangeRate;\n }\n\n function setExchangeRate(uint256 _rate) external {\n exchangeRate = _rate;\n }\n}\n" + }, + "contracts/mocks/MockRoosterAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Rooster AMO strategy exposing extra functionality\n * @author Origin Protocol Inc\n */\n\nimport { RoosterAMOStrategy } from \"../strategies/plume/RoosterAMOStrategy.sol\";\nimport { IMaverickV2Pool } from \"../interfaces/plume/IMaverickV2Pool.sol\";\n\ncontract MockRoosterAMOStrategy is RoosterAMOStrategy {\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethpAddress,\n address _liquidityManager,\n address _poolLens,\n address _maverickPosition,\n address _maverickQuoter,\n address _mPool,\n bool _upperTickAtParity,\n address _votingDistributor,\n address _poolDistributor\n )\n RoosterAMOStrategy(\n _stratConfig,\n _wethAddress,\n _oethpAddress,\n _liquidityManager,\n _poolLens,\n _maverickPosition,\n _maverickQuoter,\n _mPool,\n _upperTickAtParity,\n _votingDistributor,\n _poolDistributor\n )\n {}\n\n function getCurrentWethShare() external view returns (uint256) {\n uint256 _currentPrice = getPoolSqrtPrice();\n\n return _getWethShare(_currentPrice);\n }\n}\n" + }, + "contracts/mocks/MockSFC.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockSFC {\n error ZeroAmount();\n error TransferFailed();\n error StakeIsFullySlashed();\n\n // Mapping of delegator address to validator ID to amount delegated\n mapping(address => mapping(uint256 => uint256)) public delegations;\n // Mapping of delegator address to validator ID to withdrawal request ID to amount\n mapping(address => mapping(uint256 => mapping(uint256 => uint256)))\n public withdraws;\n // validator ID -> slashing refund ratio (allows to withdraw slashed stake)\n mapping(uint256 => uint256) public slashingRefundRatio;\n\n function getStake(address delegator, uint256 validatorID)\n external\n view\n returns (uint256)\n {\n return delegations[delegator][validatorID];\n }\n\n function delegate(uint256 validatorID) external payable {\n if (msg.value == 0) {\n revert ZeroAmount();\n }\n delegations[msg.sender][validatorID] += msg.value;\n }\n\n function undelegate(\n uint256 validatorID,\n uint256 wrID,\n uint256 amount\n ) external {\n require(\n delegations[msg.sender][validatorID] >= amount,\n \"insufficient stake\"\n );\n require(\n withdraws[msg.sender][validatorID][wrID] == 0,\n \"withdrawal request already exists\"\n );\n\n delegations[msg.sender][validatorID] -= amount;\n withdraws[msg.sender][validatorID][wrID] = amount;\n }\n\n function withdraw(uint256 validatorID, uint256 wrID) external {\n require(withdraws[msg.sender][validatorID][wrID] > 0, \"no withdrawal\");\n\n uint256 withdrawAmount = withdraws[msg.sender][validatorID][wrID];\n uint256 penalty = (withdrawAmount *\n (1e18 - slashingRefundRatio[validatorID])) / 1e18;\n\n if (penalty >= withdrawAmount) {\n revert StakeIsFullySlashed();\n }\n\n (bool sent, ) = msg.sender.call{ value: withdrawAmount - penalty }(\"\");\n if (!sent) {\n revert TransferFailed();\n }\n }\n\n function pendingRewards(address delegator, uint256 validatorID)\n external\n view\n returns (uint256)\n {}\n\n function claimRewards(uint256 validatorID) external {}\n\n function restakeRewards(uint256 validatorID) external {}\n\n /// @param refundRatio the percentage of the staked amount that can be refunded. 0.1e18 = 10%, 1e18 = 100%\n function slashValidator(uint256 validatorID, uint256 refundRatio) external {\n require(refundRatio <= 1e18, \"invalid refund ratio\");\n slashingRefundRatio[validatorID] = refundRatio;\n }\n}\n" + }, + "contracts/mocks/MocksfrxETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MocksfrxETH is MintableERC20 {\n address public frxETH;\n\n constructor(address _frxETH) ERC20(\"sfrxETH\", \"sfrxETH\") {\n frxETH = _frxETH;\n }\n\n function setMockfrxETHAddress(address _frxETH) external {\n frxETH = _frxETH;\n }\n\n function deposit(uint256 assets, address receiver)\n external\n returns (uint256 shares)\n {\n ERC20(frxETH).transferFrom(msg.sender, address(this), assets);\n\n _mint(receiver, assets);\n\n return assets;\n }\n\n function maxWithdraw(address owner) external view returns (uint256) {\n return balanceOf(owner);\n }\n\n function setMaxWithdrawableBalance(address owner, uint256 balance)\n external\n {\n uint256 currentBalance = balanceOf(owner);\n if (currentBalance > balance) {\n _burn(owner, currentBalance - balance);\n } else if (balance > currentBalance) {\n _mint(owner, balance - currentBalance);\n }\n }\n\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets) {\n _burn(owner, shares);\n\n ERC20(frxETH).transfer(receiver, shares);\n\n assets = shares;\n }\n\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares) {\n _burn(owner, assets);\n\n ERC20(frxETH).transfer(receiver, assets);\n\n shares = assets;\n }\n\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares)\n {\n _mint(recipient, msg.value);\n shares = msg.value;\n }\n}\n" + }, + "contracts/mocks/MockSSV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockSSV is MintableERC20 {\n constructor() ERC20(\"SSV Token\", \"SSV\") {}\n}\n" + }, + "contracts/mocks/MockSSVNetwork.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Cluster } from \"./../interfaces/ISSVNetwork.sol\";\n\ncontract MockSSVNetwork {\n function registerValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function exitValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds\n ) external {}\n\n function removeValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external {}\n\n function deposit(\n address clusterOwner,\n uint64[] calldata operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function setFeeRecipientAddress(address recipient) external {}\n}\n" + }, + "contracts/mocks/MockstETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockstETH is MintableERC20 {\n constructor() ERC20(\"stETH\", \"stETH\") {}\n}\n" + }, + "contracts/mocks/MockStkAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"./MintableERC20.sol\";\n\ncontract MockStkAave is MintableERC20 {\n uint256 public COOLDOWN_SECONDS = 864000;\n uint256 public UNSTAKE_WINDOW = 172800;\n address public STAKED_TOKEN;\n\n mapping(address => uint256) public stakerRewardsToClaim;\n mapping(address => uint256) public stakersCooldowns;\n\n using SafeERC20 for IERC20;\n\n constructor(address _stakedToken) ERC20(\"Staked Aave\", \"stkAAVE\") {\n STAKED_TOKEN = _stakedToken;\n }\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n\n function setStakedToken(address _stakedToken) external {\n STAKED_TOKEN = _stakedToken;\n }\n\n /**\n * @dev Redeems staked tokens, and stop earning rewards\n * @param to Address to redeem to\n * @param amount Amount to redeem\n **/\n function redeem(address to, uint256 amount) external {\n uint256 cooldownStartTimestamp = stakersCooldowns[msg.sender];\n uint256 windowStart = cooldownStartTimestamp + COOLDOWN_SECONDS;\n require(amount != 0, \"INVALID_ZERO_AMOUNT\");\n require(block.timestamp > windowStart, \"INSUFFICIENT_COOLDOWN\");\n require(\n block.timestamp - windowStart <= UNSTAKE_WINDOW,\n \"UNSTAKE_WINDOW_FINISHED\"\n );\n uint256 balanceOfMessageSender = balanceOf(msg.sender);\n uint256 amountToRedeem = (amount > balanceOfMessageSender)\n ? balanceOfMessageSender\n : amount;\n\n stakersCooldowns[msg.sender] = 0;\n _burn(msg.sender, amountToRedeem);\n IERC20(STAKED_TOKEN).safeTransfer(to, amountToRedeem);\n }\n\n /**\n * @dev Activates the cooldown period to unstake\n * - It can't be called if the user is not staking\n **/\n function cooldown() external {\n require(balanceOf(msg.sender) != 0, \"INVALID_BALANCE_ON_COOLDOWN\");\n stakersCooldowns[msg.sender] = block.timestamp;\n }\n\n /**\n * @dev Test helper function to allow changing the cooldown\n **/\n function setCooldown(address account, uint256 _cooldown) external {\n stakersCooldowns[account] = _cooldown;\n }\n}\n" + }, + "contracts/mocks/MockStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract MockStrategy {\n address[] public assets;\n\n address public withdrawAllAsset;\n address public withdrawAllRecipient;\n\n constructor() {}\n\n function deposit(address asset, uint256 amount) external {}\n\n function depositAll() external {}\n\n function withdraw(\n address recipient,\n address asset,\n uint256 amount\n ) external {\n IERC20(asset).transfer(recipient, amount);\n }\n\n function withdrawAll() external {\n IERC20(withdrawAllAsset).transfer(\n withdrawAllRecipient,\n IERC20(withdrawAllAsset).balanceOf(address(this))\n );\n }\n\n function checkBalance(address asset)\n external\n view\n returns (uint256 balance)\n {\n balance = IERC20(asset).balanceOf(address(this));\n }\n\n function supportsAsset(address) external view returns (bool) {\n return true;\n }\n\n function collectRewardTokens() external {}\n\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return new address[](0);\n }\n\n function setWithdrawAll(address asset, address recipient) external {\n withdrawAllAsset = asset;\n withdrawAllRecipient = recipient;\n }\n}\n" + }, + "contracts/mocks/MockSwapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IMintableERC20 } from \"./MintableERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract MockSwapper {\n uint256 public nextOutAmount;\n\n function swap(\n // solhint-disable-next-line no-unused-vars\n address _fromAsset,\n address _toAsset,\n // solhint-disable-next-line no-unused-vars\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n // solhint-disable-next-line no-unused-vars\n bytes calldata _data\n ) external returns (uint256 toAssetAmount) {\n toAssetAmount = (nextOutAmount > 0) ? nextOutAmount : _minToAssetAmount;\n nextOutAmount = 0;\n IMintableERC20(_toAsset).mint(toAssetAmount);\n IERC20(_toAsset).transfer(msg.sender, toAssetAmount);\n }\n\n function setNextOutAmount(uint256 _nextOutAmount) public {\n nextOutAmount = _nextOutAmount;\n }\n}\n" + }, + "contracts/mocks/MockTUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockTUSD is MintableERC20 {\n constructor() ERC20(\"TrueUSD\", \"TUSD\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/MockUniswapPair.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IUniswapV2Pair } from \"../interfaces/uniswap/IUniswapV2Pair.sol\";\n\ncontract MockUniswapPair is IUniswapV2Pair {\n address tok0;\n address tok1;\n uint112 reserve0;\n uint112 reserve1;\n uint256 blockTimestampLast;\n\n bool public hasSynced = false;\n\n constructor(\n address _token0,\n address _token1,\n uint112 _reserve0,\n uint112 _reserve1\n ) {\n tok0 = _token0;\n tok1 = _token1;\n reserve0 = _reserve0;\n reserve1 = _reserve1;\n blockTimestampLast = block.timestamp;\n }\n\n function token0() external view override returns (address) {\n return tok0;\n }\n\n function token1() external view override returns (address) {\n return tok1;\n }\n\n function getReserves()\n external\n view\n override\n returns (\n uint112,\n uint112,\n uint32\n )\n {\n return (reserve0, reserve1, uint32(blockTimestampLast));\n }\n\n function setReserves(uint112 _reserve0, uint112 _reserve1) public {\n reserve0 = _reserve0;\n reserve1 = _reserve1;\n blockTimestampLast = block.timestamp;\n }\n\n // CAUTION This will not work if you setReserves multiple times over\n // multiple different blocks because then it wouldn't be a continuous\n // reserve factor over that blockTimestamp, this assumes an even reserve\n // ratio all the way through\n function price0CumulativeLast() external view override returns (uint256) {\n return\n uint256(FixedPoint.fraction(reserve1, reserve0)._x) *\n blockTimestampLast;\n }\n\n function price1CumulativeLast() external view override returns (uint256) {\n return\n uint256(FixedPoint.fraction(reserve0, reserve1)._x) *\n blockTimestampLast;\n }\n\n function sync() external override {\n hasSynced = true;\n }\n\n function checkHasSynced() external view {\n require(hasSynced, \"Not synced\");\n }\n}\n\n// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))\nlibrary FixedPoint {\n // range: [0, 2**112 - 1]\n // resolution: 1 / 2**112\n struct uq112x112 {\n uint224 _x;\n }\n\n // returns a uq112x112 which represents the ratio of the numerator to the denominator\n // equivalent to encode(numerator).div(denominator)\n function fraction(uint112 numerator, uint112 denominator)\n internal\n pure\n returns (uq112x112 memory)\n {\n require(denominator > 0, \"FixedPoint: DIV_BY_ZERO\");\n return uq112x112((uint224(numerator) << 112) / denominator);\n }\n}\n" + }, + "contracts/mocks/MockUniswapRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { IUniswapV2Router } from \"../interfaces/uniswap/IUniswapV2Router02.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockUniswapRouter is IUniswapV2Router {\n using StableMath for uint256;\n\n mapping(address => address) public pairMaps;\n uint256 public slippage = 1 ether;\n\n function initialize(\n address[] calldata _0tokens,\n address[] calldata _1tokens\n ) public {\n require(\n _0tokens.length == _1tokens.length,\n \"Mock token pairs should be of the same length\"\n );\n for (uint256 i = 0; i < _0tokens.length; i++) {\n pairMaps[_0tokens[i]] = _1tokens[i];\n }\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n\n function swapExactTokensForTokens(\n uint256 amountIn,\n uint256 amountOutMin,\n address[] calldata path,\n address to,\n // solhint-disable-next-line no-unused-vars\n uint256\n ) external override returns (uint256[] memory amountsOut) {\n address tok0 = path[0];\n address tok1 = path[path.length - 1];\n\n uint256 amountOut = (amountOutMin * slippage) / 1 ether;\n require(amountOut >= amountOutMin, \"Slippage error\");\n\n IERC20(tok0).transferFrom(msg.sender, address(this), amountIn);\n MintableERC20(tok1).mintTo(to, amountOut);\n\n amountsOut = new uint256[](path.length);\n amountsOut[path.length - 1] = amountOut;\n }\n\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut)\n {\n (address tok0, address tok1) = _getFirstAndLastToken(params.path);\n\n amountOut = (params.amountOutMinimum * slippage) / 1 ether;\n\n IERC20(tok0).transferFrom(msg.sender, address(this), params.amountIn);\n MintableERC20(tok1).mintTo(params.recipient, amountOut);\n\n require(\n amountOut >= params.amountOutMinimum,\n \"UniswapMock: amountOut less than amountOutMinimum\"\n );\n return amountOut;\n }\n\n function addLiquidity(\n address tokenA,\n address tokenB,\n uint256 amountADesired,\n uint256 amountBDesired,\n uint256 amountAMin,\n uint256 amountBMin,\n address to,\n uint256 deadline\n )\n external\n override\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 liquidity\n )\n {\n // this is needed to make this contract whole else it'd be just virtual\n }\n\n function WETH() external pure override returns (address) {\n return address(0);\n }\n\n // Universal router mock\n function execute(\n bytes calldata,\n bytes[] calldata inputs,\n uint256\n ) external payable {\n uint256 inLen = inputs.length;\n for (uint256 i = 0; i < inLen; ++i) {\n (\n address recipient,\n ,\n uint256 amountOutMinimum,\n bytes memory path,\n\n ) = abi.decode(inputs[i], (address, uint256, uint256, bytes, bool));\n\n (address token0, address token1) = _getFirstAndLastToken(path);\n\n amountOutMinimum = amountOutMinimum.scaleBy(\n Helpers.getDecimals(token0),\n Helpers.getDecimals(token1)\n );\n\n MintableERC20(token1).mintTo(recipient, amountOutMinimum);\n }\n }\n\n function _getFirstAndLastToken(bytes memory path)\n internal\n view\n returns (address token0, address token1)\n {\n bytes memory tok0Bytes = new bytes(20);\n for (uint256 j = 0; j < 20; ++j) {\n tok0Bytes[j] = path[j];\n }\n token0 = address(bytes20(tok0Bytes));\n\n if (pairMaps[token0] != address(0)) {\n token0 = pairMaps[token0];\n }\n\n bytes memory tok1Bytes = new bytes(20);\n uint256 tok1Offset = path.length - 20;\n for (uint256 j = 0; j < 20; ++j) {\n tok1Bytes[j] = path[j + tok1Offset];\n }\n token1 = address(bytes20(tok1Bytes));\n\n if (pairMaps[token1] != address(0)) {\n token1 = pairMaps[token1];\n }\n }\n}\n" + }, + "contracts/mocks/MockUSDC.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDC is MintableERC20 {\n constructor() ERC20(\"USDC Coin\", \"USDC\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n}\n" + }, + "contracts/mocks/MockUSDS.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDS is MintableERC20 {\n constructor() ERC20(\"USDS\", \"USDS\") {}\n}\n" + }, + "contracts/mocks/MockUSDT.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDT is MintableERC20 {\n constructor() ERC20(\"USDT Coin\", \"USDT\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n}\n" + }, + "contracts/mocks/MockVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultCore } from \"../vault/VaultCore.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { VaultInitializer } from \"../vault/VaultInitializer.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MockVault is VaultCore {\n using StableMath for uint256;\n\n uint256 storedTotalValue;\n\n function setTotalValue(uint256 _value) public {\n storedTotalValue = _value;\n }\n\n function totalValue() external view override returns (uint256) {\n return storedTotalValue;\n }\n\n function _totalValue() internal view override returns (uint256) {\n return storedTotalValue;\n }\n\n function _checkBalance(address _asset)\n internal\n view\n override\n returns (uint256 balance)\n {\n // Avoids rounding errors by returning the total value\n // in a single currency\n if (allAssets[0] == _asset) {\n uint256 decimals = Helpers.getDecimals(_asset);\n return storedTotalValue.scaleBy(decimals, 18);\n } else {\n return 0;\n }\n }\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external onlyGovernor {\n maxSupplyDiff = _maxSupplyDiff;\n }\n}\n" + }, + "contracts/mocks/MockVaultCoreInstantRebase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultCore } from \"../vault/VaultCore.sol\";\n\ncontract MockVaultCoreInstantRebase is VaultCore {\n function _nextYield(uint256 supply, uint256 vaultValue)\n internal\n view\n override\n returns (uint256 yield, uint256 targetRate)\n {\n if (vaultValue <= supply) {\n return (0, 0);\n }\n yield = vaultValue - supply;\n return (yield, 0);\n }\n}\n" + }, + "contracts/mocks/MockWETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\n// Just importing to \"unbreak\" coverage tests\nimport { IERC721Receiver } from \"@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol\";\n\ncontract MockWETH is MintableERC20 {\n constructor() ERC20(\"WETH\", \"WETH\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/MockWS.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockWS is MintableERC20 {\n constructor() ERC20(\"Wrapped Sonic\", \"wS\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/TestUpgradedOUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../token/OUSD.sol\";\n\n// used to alter internal state of OUSD contract\ncontract TestUpgradedOUSD is OUSD {\n constructor() OUSD() {}\n\n function overwriteCreditBalances(address _account, uint256 _creditBalance)\n public\n {\n creditBalances[_account] = _creditBalance;\n }\n\n function overwriteAlternativeCPT(address _account, uint256 _acpt) public {\n alternativeCreditsPerToken[_account] = _acpt;\n }\n\n function overwriteRebaseState(address _account, RebaseOptions _rebaseOption)\n public\n {\n rebaseState[_account] = _rebaseOption;\n }\n}\n" + }, + "contracts/oracle/AbstractOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\n// @notice Abstract functionality that is shared between various Oracle Routers\nabstract contract AbstractOracleRouter is IOracle {\n using StableMath for uint256;\n using SafeCast for int256;\n\n uint256 internal constant MIN_DRIFT = 0.7e18;\n uint256 internal constant MAX_DRIFT = 1.3e18;\n address internal constant FIXED_PRICE =\n 0x0000000000000000000000000000000000000001;\n // Maximum allowed staleness buffer above normal Oracle maximum staleness\n uint256 internal constant STALENESS_BUFFER = 1 days;\n mapping(address => uint8) internal decimalsCache;\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n returns (address feedAddress, uint256 maxStaleness);\n\n /**\n * @notice Returns the total price in 18 digit unit for a given asset.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n if (shouldBePegged(asset)) {\n require(_price <= MAX_DRIFT, \"Oracle: Price exceeds max\");\n require(_price >= MIN_DRIFT, \"Oracle: Price under min\");\n }\n return _price;\n }\n\n function getDecimals(address _feed) internal view virtual returns (uint8) {\n uint8 decimals = decimalsCache[_feed];\n require(decimals > 0, \"Oracle: Decimals not cached\");\n return decimals;\n }\n\n /**\n * @notice Before an asset/feed price is fetches for the first time the\n * decimals need to be cached. This is a gas optimization\n * @param asset address of the asset\n * @return uint8 corresponding asset decimals\n */\n function cacheDecimals(address asset) external returns (uint8) {\n (address _feed, ) = feedMetadata(asset);\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n uint8 decimals = AggregatorV3Interface(_feed).decimals();\n decimalsCache[_feed] = decimals;\n return decimals;\n }\n\n function shouldBePegged(address _asset) internal view returns (bool) {\n string memory symbol = Helpers.getSymbol(_asset);\n bytes32 symbolHash = keccak256(abi.encodePacked(symbol));\n return\n symbolHash == keccak256(abi.encodePacked(\"DAI\")) ||\n symbolHash == keccak256(abi.encodePacked(\"USDC\")) ||\n symbolHash == keccak256(abi.encodePacked(\"USDT\"));\n }\n}\n" + }, + "contracts/oracle/OETHBaseOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\n// @notice Oracle Router (for OETH on Base) that denominates all prices in ETH\ncontract OETHBaseOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n using SafeCast for int256;\n\n address constant WETH = 0x4200000000000000000000000000000000000006;\n address constant WOETH = 0xD8724322f44E5c58D7A815F542036fb17DbbF839;\n address constant WOETH_CHAINLINK_FEED =\n 0xe96EB1EDa83d18cbac224233319FA5071464e1b9;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == WETH) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == WOETH) {\n // Chainlink: https://data.chain.link/feeds/base/base/woeth-oeth-exchange-rate\n // Bridged wOETH/OETH\n feedAddress = WOETH_CHAINLINK_FEED;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OETHFixedOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHOracleRouter } from \"./OETHOracleRouter.sol\";\n\n// @notice Oracle Router that returns 1e18 for all prices\n// used solely for deployment to testnets\ncontract OETHFixedOracle is OETHOracleRouter {\n constructor() OETHOracleRouter() {}\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n // solhint-disable-next-line no-unused-vars\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n // fixes price for all of the assets\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n }\n}\n" + }, + "contracts/oracle/OETHOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// @notice Oracle Router that denominates all prices in ETH\ncontract OETHOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = uint256(_iprice).scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == 0x5E8422345238F34275888049021821E8E08CAa1f) {\n // frxETH/ETH\n feedAddress = 0xC58F3385FBc1C8AD2c0C9a061D7c13b141D7A5Df;\n maxStaleness = 18 hours + STALENESS_BUFFER;\n } else if (asset == 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/steth-eth\n // Chainlink: stETH/ETH\n feedAddress = 0x86392dC19c0b719886221c78AB11eb8Cf5c52812;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xae78736Cd615f374D3085123A210448E74Fc6393) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/reth-eth\n // Chainlink: rETH/ETH\n feedAddress = 0x536218f9E9Eb48863970252233c8F271f554C2d0;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xD533a949740bb3306d119CC777fa900bA034cd52) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/crv-eth\n // Chainlink: CRV/ETH\n feedAddress = 0x8a12Be339B0cD1829b91Adc01977caa5E9ac121e;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/cvx-eth\n // Chainlink: CVX/ETH\n feedAddress = 0xC9CbF687f43176B302F03f5e58470b77D07c61c6;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xBe9895146f7AF43049ca1c1AE358B0541Ea49704) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/cbeth-eth\n // Chainlink: cbETH/ETH\n feedAddress = 0xF017fcB346A1885194689bA23Eff2fE6fA5C483b;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xba100000625a3754423978a60c9317c58a424e3D) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/bal-eth\n // Chainlink: BAL/ETH\n feedAddress = 0xC1438AA3823A6Ba0C159CfA8D98dF5A994bA120b;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OETHPlumeOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\n// @notice Oracle Router (for OETH on Plume) that denominates all prices in ETH\ncontract OETHPlumeOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n using SafeCast for int256;\n\n address constant WETH = 0xca59cA09E5602fAe8B629DeE83FfA819741f14be;\n address constant WOETH = 0xD8724322f44E5c58D7A815F542036fb17DbbF839;\n // Ref: https://docs.eo.app/docs/eprice/feed-addresses/plume\n address constant WOETH_ORACLE_FEED =\n 0x4915600Ed7d85De62011433eEf0BD5399f677e9b;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == WETH) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == WOETH) {\n // Chainlink: https://data.chain.link/feeds/base/base/woeth-oeth-exchange-rate\n // Bridged wOETH/OETH\n feedAddress = WOETH_ORACLE_FEED;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\n\n// @notice Oracle Router that denominates all prices in USD\ncontract OracleRouter is AbstractOracleRouter {\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n pure\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n /* + STALENESS_BUFFER is added in case Oracle for some reason doesn't\n * update on heartbeat and we add a generous buffer amount.\n */\n if (asset == 0x6B175474E89094C44Da98b954EedeAC495271d0F) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/dai-usd\n // Chainlink: DAI/USD\n feedAddress = 0xAed0c38402a5d19df6E4c03F4E2DceD6e29c1ee9;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xdC035D45d973E3EC169d2276DDab16f1e407384F) {\n // https://data.chain.link/feeds/ethereum/mainnet/usds-usd\n // Chainlink: USDS/USD\n feedAddress = 0xfF30586cD0F29eD462364C7e81375FC0C71219b1;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/usdc-usd\n // Chainlink: USDC/USD\n feedAddress = 0x8fFfFfd4AfB6115b954Bd326cbe7B4BA576818f6;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xdAC17F958D2ee523a2206206994597C13D831ec7) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/usdt-usd\n // Chainlink: USDT/USD\n feedAddress = 0x3E7d1eAB13ad0104d2750B8863b489D65364e32D;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xc00e94Cb662C3520282E6f5717214004A7f26888) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/comp-usd\n // Chainlink: COMP/USD\n feedAddress = 0xdbd020CAeF83eFd542f4De03e3cF0C28A4428bd5;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/aave-usd\n // Chainlink: AAVE/USD\n feedAddress = 0x547a514d5e3769680Ce22B2361c10Ea13619e8a9;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xD533a949740bb3306d119CC777fa900bA034cd52) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/crv-usd\n // Chainlink: CRV/USD\n feedAddress = 0xCd627aA160A6fA45Eb793D19Ef54f5062F20f33f;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B) {\n // Chainlink: CVX/USD\n feedAddress = 0xd962fC30A72A84cE50161031391756Bf2876Af5D;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OSonicOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHFixedOracle } from \"./OETHFixedOracle.sol\";\n\n// @notice Oracle Router that returns 1e18 for all prices\n// used solely for deployment to testnets\ncontract OSonicOracleRouter is OETHFixedOracle {\n constructor() OETHFixedOracle() {}\n}\n" + }, + "contracts/poolBooster/AbstractPoolBoosterFactory.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IPoolBoostCentralRegistry } from \"../interfaces/poolBooster/IPoolBoostCentralRegistry.sol\";\n\n/**\n * @title Abstract Pool booster factory\n * @author Origin Protocol Inc\n */\ncontract AbstractPoolBoosterFactory is Governable {\n struct PoolBoosterEntry {\n address boosterAddress;\n address ammPoolAddress;\n IPoolBoostCentralRegistry.PoolBoosterType boosterType;\n }\n\n // @notice address of Origin Sonic\n address public immutable oSonic;\n // @notice Central registry contract\n IPoolBoostCentralRegistry public immutable centralRegistry;\n\n // @notice list of all the pool boosters created by this factory\n PoolBoosterEntry[] public poolBoosters;\n // @notice mapping of AMM pool to pool booster\n mapping(address => PoolBoosterEntry) public poolBoosterFromPool;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) {\n require(_oSonic != address(0), \"Invalid oSonic address\");\n require(_governor != address(0), \"Invalid governor address\");\n require(\n _centralRegistry != address(0),\n \"Invalid central registry address\"\n );\n\n oSonic = _oSonic;\n centralRegistry = IPoolBoostCentralRegistry(_centralRegistry);\n _setGovernor(_governor);\n }\n\n /**\n * @notice Goes over all the pool boosters created by this factory and\n * calls bribe() on them.\n * @param _exclusionList A list of pool booster addresses to skip when\n * calling this function.\n */\n function bribeAll(address[] memory _exclusionList) external {\n uint256 lengthI = poolBoosters.length;\n for (uint256 i = 0; i < lengthI; i++) {\n address poolBoosterAddress = poolBoosters[i].boosterAddress;\n bool skipBribeCall = false;\n uint256 lengthJ = _exclusionList.length;\n for (uint256 j = 0; j < lengthJ; j++) {\n // pool booster in exclusion list\n if (_exclusionList[j] == poolBoosterAddress) {\n skipBribeCall = true;\n break;\n }\n }\n\n if (!skipBribeCall) {\n IPoolBooster(poolBoosterAddress).bribe();\n }\n }\n }\n\n /**\n * @notice Removes the pool booster from the internal list of pool boosters.\n * @dev This action does not destroy the pool booster contract nor does it\n * stop the yield delegation to it.\n * @param _poolBoosterAddress address of the pool booster\n */\n function removePoolBooster(address _poolBoosterAddress)\n external\n onlyGovernor\n {\n uint256 boostersLen = poolBoosters.length;\n for (uint256 i = 0; i < boostersLen; ++i) {\n if (poolBoosters[i].boosterAddress == _poolBoosterAddress) {\n // erase mapping\n delete poolBoosterFromPool[poolBoosters[i].ammPoolAddress];\n\n // overwrite current pool booster with the last entry in the list\n poolBoosters[i] = poolBoosters[boostersLen - 1];\n // drop the last entry\n poolBoosters.pop();\n\n centralRegistry.emitPoolBoosterRemoved(_poolBoosterAddress);\n break;\n }\n }\n }\n\n function _storePoolBoosterEntry(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType _boosterType\n ) internal {\n PoolBoosterEntry memory entry = PoolBoosterEntry(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType\n );\n\n poolBoosters.push(entry);\n poolBoosterFromPool[_ammPoolAddress] = entry;\n\n // emit the events of the pool booster created\n centralRegistry.emitPoolBoosterCreated(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType\n );\n }\n\n function _deployContract(bytes memory _bytecode, uint256 _salt)\n internal\n returns (address _address)\n {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _address := create2(\n 0,\n add(_bytecode, 0x20),\n mload(_bytecode),\n _salt\n )\n }\n\n require(\n _address.code.length > 0 && _address != address(0),\n \"Failed creating a pool booster\"\n );\n }\n\n // pre-compute the address of the deployed contract that will be\n // created when create2 is called\n function _computeAddress(bytes memory _bytecode, uint256 _salt)\n internal\n view\n returns (address)\n {\n bytes32 hash = keccak256(\n abi.encodePacked(\n bytes1(0xff),\n address(this),\n _salt,\n keccak256(_bytecode)\n )\n );\n\n // cast last 20 bytes of hash to address\n return address(uint160(uint256(hash)));\n }\n\n function poolBoosterLength() external view returns (uint256) {\n return poolBoosters.length;\n }\n}\n" + }, + "contracts/poolBooster/PoolBoostCentralRegistry.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IPoolBoostCentralRegistry } from \"../interfaces/poolBooster/IPoolBoostCentralRegistry.sol\";\n\n/**\n * @title Contract that holds all governance approved pool booster Factory\n * implementation deployments\n * @author Origin Protocol Inc\n */\ncontract PoolBoostCentralRegistry is Governable, IPoolBoostCentralRegistry {\n event FactoryApproved(address factoryAddress);\n event FactoryRemoved(address factoryAddress);\n\n // @notice List of approved factories\n address[] public factories;\n\n modifier onlyApprovedFactories() {\n require(isApprovedFactory(msg.sender), \"Not an approved factory\");\n _;\n }\n\n constructor() {\n // set the governor of the implementation contract to zero address\n _setGovernor(address(0));\n }\n\n /**\n * @notice Adds a factory address to the approved factory addresses\n * @param _factoryAddress address of the factory\n */\n function approveFactory(address _factoryAddress) external onlyGovernor {\n require(_factoryAddress != address(0), \"Invalid address\");\n require(\n !isApprovedFactory(_factoryAddress),\n \"Factory already approved\"\n );\n\n factories.push(_factoryAddress);\n emit FactoryApproved(_factoryAddress);\n }\n\n /**\n * @notice Removes the factory from approved factory addresses\n * @param _factoryAddress address of the factory\n */\n function removeFactory(address _factoryAddress) external onlyGovernor {\n require(_factoryAddress != address(0), \"Invalid address\");\n\n uint256 length = factories.length;\n bool factoryRemoved = false;\n for (uint256 i = 0; i < length; i++) {\n if (factories[i] != _factoryAddress) {\n continue;\n }\n\n factories[i] = factories[length - 1];\n factories.pop();\n emit FactoryRemoved(_factoryAddress);\n factoryRemoved = true;\n break;\n }\n require(factoryRemoved, \"Not an approved factory\");\n\n emit FactoryRemoved(_factoryAddress);\n }\n\n /**\n * @notice Emits a pool booster created event\n * @dev This has been created as a convenience method for the monitoring to have\n * an index of all of the created pool boosters by only listening to the\n * events of this contract.\n * @param _poolBoosterAddress address of the pool booster created\n * @param _ammPoolAddress address of the AMM pool forwarding yield to the pool booster\n * @param _boosterType PoolBoosterType the type of the pool booster\n */\n function emitPoolBoosterCreated(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n PoolBoosterType _boosterType\n ) external onlyApprovedFactories {\n emit PoolBoosterCreated(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType,\n msg.sender // address of the factory\n );\n }\n\n /**\n * @notice Emits a pool booster removed event\n * @dev This has been created as a convenience method for the monitoring to have\n * an index of all of the removed pool boosters by only listening to the\n * events of this contract.\n * @param _poolBoosterAddress address of the pool booster to be removed\n */\n function emitPoolBoosterRemoved(address _poolBoosterAddress)\n external\n onlyApprovedFactories\n {\n emit PoolBoosterRemoved(_poolBoosterAddress);\n }\n\n /**\n * @notice Returns true if the factory is approved\n * @param _factoryAddress address of the factory\n */\n function isApprovedFactory(address _factoryAddress)\n public\n view\n returns (bool)\n {\n uint256 length = factories.length;\n for (uint256 i = 0; i < length; i++) {\n if (factories[i] == _factoryAddress) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * @notice Returns all supported factories\n */\n function getAllFactories() external view returns (address[] memory) {\n return factories;\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactoryMerkl.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterMerkl } from \"./PoolBoosterMerkl.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Merkl pool boosters.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactoryMerkl is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n /// @notice address of the Merkl distributor\n address public merklDistributor;\n\n /// @notice event emitted when the Merkl distributor is updated\n event MerklDistributorUpdated(address newDistributor);\n\n /**\n * @param _oSonic address of the OSonic token\n * @param _governor address governor\n * @param _centralRegistry address of the central registry\n * @param _merklDistributor address of the Merkl distributor\n */\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry,\n address _merklDistributor\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {\n _setMerklDistributor(_merklDistributor);\n }\n\n /**\n * @dev Create a Pool Booster for Merkl.\n * @param _campaignType The type of campaign to create. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _campaignDuration The duration of the campaign in seconds\n * @param campaignData The data to be used for the campaign. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * This should be fetched from the Merkl UI.\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterMerkl(\n uint32 _campaignType,\n address _ammPoolAddress,\n uint32 _campaignDuration,\n bytes calldata campaignData,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n require(_campaignDuration > 1 hours, \"Invalid campaign duration\");\n require(campaignData.length > 0, \"Invalid campaign data\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterMerkl).creationCode,\n abi.encode(\n oSonic,\n merklDistributor,\n _campaignDuration,\n _campaignType,\n governor(),\n campaignData\n )\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.MerklBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for Merkl.\n * @param _campaignType The type of campaign to create. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterMerkl` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n uint32 _campaignType,\n address _ammPoolAddress,\n uint32 _campaignDuration,\n bytes calldata campaignData,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n require(_campaignDuration > 1 hours, \"Invalid campaign duration\");\n require(campaignData.length > 0, \"Invalid campaign data\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterMerkl).creationCode,\n abi.encode(\n oSonic,\n merklDistributor,\n _campaignDuration,\n _campaignType,\n governor(),\n campaignData\n )\n ),\n _salt\n );\n }\n\n /**\n * @dev Set the address of the Merkl distributor\n * @param _merklDistributor The address of the Merkl distributor\n */\n function setMerklDistributor(address _merklDistributor)\n external\n onlyGovernor\n {\n _setMerklDistributor(_merklDistributor);\n }\n\n function _setMerklDistributor(address _merklDistributor) internal {\n require(\n _merklDistributor != address(0),\n \"Invalid merklDistributor address\"\n );\n merklDistributor = _merklDistributor;\n emit MerklDistributorUpdated(_merklDistributor);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactoryMetropolis.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterMetropolis } from \"./PoolBoosterMetropolis.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Metropolis pool boosters.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactoryMetropolis is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n address public immutable rewardFactory;\n address public immutable voter;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n // @param address _rewardFactory address of the Metropolis reward factory\n // @param address _voter address of the Metropolis voter\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry,\n address _rewardFactory,\n address _voter\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {\n rewardFactory = _rewardFactory;\n voter = _voter;\n }\n\n /**\n * @dev Create a Pool Booster for Metropolis pool.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterMetropolis(address _ammPoolAddress, uint256 _salt)\n external\n onlyGovernor\n {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterMetropolis).creationCode,\n abi.encode(oSonic, rewardFactory, _ammPoolAddress, voter)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.MetropolisBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for Metropolis pool.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterMetropolis` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(address _ammPoolAddress, uint256 _salt)\n external\n view\n returns (address)\n {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterMetropolis).creationCode,\n abi.encode(oSonic, rewardFactory, _ammPoolAddress, voter)\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactorySwapxDouble.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterSwapxDouble } from \"./PoolBoosterSwapxDouble.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Swapx Ichi pool boosters where both of the\n * gauges need incentivizing.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactorySwapxDouble is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {}\n\n /**\n * @dev Create a Pool Booster for SwapX Ichi vault based pool where 2 Bribe contracts need to be\n * bribed\n * @param _bribeAddressOS address of the Bribes.sol(Bribe) contract for the OS token side\n * @param _bribeAddressOther address of the Bribes.sol(Bribe) contract for the other token in the pool\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _split 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n * bribe contract and 60% to other bribe contract\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterSwapxDouble(\n address _bribeAddressOS,\n address _bribeAddressOther,\n address _ammPoolAddress,\n uint256 _split,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterSwapxDouble).creationCode,\n abi.encode(_bribeAddressOS, _bribeAddressOther, oSonic, _split)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.SwapXDoubleBooster\n );\n }\n\n /**\n * @dev Compute the address of the pool booster to be deployed.\n * @param _bribeAddressOS address of the Bribes.sol(Bribe) contract for the OS token side\n * @param _bribeAddressOther address of the Bribes.sol(Bribe) contract for the other token in the pool\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _split 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n * bribe contract and 60% to other bribe contract\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterSwapxDouble` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n address _bribeAddressOS,\n address _bribeAddressOther,\n address _ammPoolAddress,\n uint256 _split,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterSwapxDouble).creationCode,\n abi.encode(\n _bribeAddressOS,\n _bribeAddressOther,\n oSonic,\n _split\n )\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactorySwapxSingle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterSwapxSingle } from \"./PoolBoosterSwapxSingle.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Swapx Single pool boosters - where a single\n * gauge is created for a pool. this is appropriate for Classic Stable & Classic\n * Volatile SwapX pools.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactorySwapxSingle is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {}\n\n /**\n * @dev Create a Pool Booster for SwapX classic volatile or classic stable pools where\n * a single Bribe contract is incentivized.\n * @param _bribeAddress address of the Bribes.sol contract\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterSwapxSingle(\n address _bribeAddress,\n address _ammPoolAddress,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterSwapxSingle).creationCode,\n abi.encode(_bribeAddress, oSonic)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.SwapXSingleBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for SwapX classic volatile or classic stable pools where\n * a single Bribe contract is incentivized.\n * @param _bribeAddress address of the Bribes.sol contract\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterSwapxSingle` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n address _bribeAddress,\n address _ammPoolAddress,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterSwapxSingle).creationCode,\n abi.encode(_bribeAddress, oSonic)\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterMerkl.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IMerklDistributor } from \"../interfaces/poolBooster/IMerklDistributor.sol\";\n\ninterface IERC1271 {\n /**\n * @dev Should return whether the signature provided is valid for the provided data\n * @param hash Hash of the data to be signed\n * @param signature Signature byte array associated with _data\n */\n function isValidSignature(bytes32 hash, bytes memory signature)\n external\n view\n returns (bytes4 magicValue);\n}\n\n/**\n * @title Pool booster for Merkl distributor\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterMerkl is IPoolBooster, IERC1271 {\n /// @notice address of merkl distributor\n IMerklDistributor public immutable merklDistributor;\n /// @notice address of the OS token\n IERC20 public immutable rewardToken;\n /// @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n /// @notice Campaign duration in seconds\n uint32 public immutable duration; // -> should be immutable\n /// @notice Campaign type\n uint32 public immutable campaignType;\n /// @notice Owner of the campaign\n address public immutable creator;\n /// @notice Campaign data\n bytes public campaignData;\n\n constructor(\n address _rewardToken,\n address _merklDistributor,\n uint32 _duration,\n uint32 _campaignType,\n address _creator,\n bytes memory _campaignData\n ) {\n require(_rewardToken != address(0), \"Invalid rewardToken address\");\n require(\n _merklDistributor != address(0),\n \"Invalid merklDistributor address\"\n );\n require(_campaignData.length > 0, \"Invalid campaignData\");\n require(_duration > 1 hours, \"Invalid duration\");\n\n campaignType = _campaignType;\n duration = _duration;\n creator = _creator;\n\n merklDistributor = IMerklDistributor(_merklDistributor);\n rewardToken = IERC20(_rewardToken);\n campaignData = _campaignData;\n }\n\n /// @notice Create a campaign on the Merkl distributor\n function bribe() external override {\n // Ensure token is approved for the Merkl distributor\n uint256 minAmount = merklDistributor.rewardTokenMinAmounts(\n address(rewardToken)\n );\n require(minAmount > 0, \"Min reward amount must be > 0\");\n\n // if balance too small or below threshold, do no bribes\n uint256 balance = rewardToken.balanceOf(address(this));\n if (\n balance < MIN_BRIBE_AMOUNT ||\n (balance * 1 hours < minAmount * duration)\n ) {\n return;\n }\n\n // Approve the bribe contract to spend the reward token\n rewardToken.approve(address(merklDistributor), balance);\n\n // Notify the bribe contract of the reward amount\n merklDistributor.signAndCreateCampaign(\n IMerklDistributor.CampaignParameters({\n campaignId: bytes32(0),\n creator: creator,\n rewardToken: address(rewardToken),\n amount: balance,\n campaignType: campaignType,\n startTimestamp: getNextPeriodStartTime(),\n duration: duration,\n campaignData: campaignData\n }),\n bytes(\"\")\n );\n emit BribeExecuted(balance);\n }\n\n /// @notice Used to sign a campaign on the Merkl distributor\n function isValidSignature(bytes32, bytes memory)\n external\n view\n override\n returns (bytes4 magicValue)\n {\n require(msg.sender == address(merklDistributor), \"Invalid sender\");\n // bytes4(keccak256(\"isValidSignature(bytes32,bytes)\")) == 0x1626ba7e\n return bytes4(0x1626ba7e);\n }\n\n /// @notice Returns the timestamp for the start of the next period based on the configured duration\n function getNextPeriodStartTime() public view returns (uint32) {\n // Calculate the timestamp for the next period boundary\n return uint32((block.timestamp / duration + 1) * duration);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterMetropolis.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @title Pool booster for Metropolis pools\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterMetropolis is IPoolBooster {\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice address of the pool\n address public immutable pool;\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n IRewarderFactory public immutable rewardFactory;\n\n IVoter public immutable voter;\n\n constructor(\n address _osToken,\n address _rewardFactory,\n address _pool,\n address _voter\n ) {\n require(_pool != address(0), \"Invalid pool address\");\n pool = _pool;\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n\n rewardFactory = IRewarderFactory(_rewardFactory);\n\n voter = IVoter(_voter);\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n (, uint256 minBribeAmount) = rewardFactory.getWhitelistedTokenInfo(\n address(osToken)\n );\n if (balance < MIN_BRIBE_AMOUNT || balance < minBribeAmount) {\n return;\n }\n\n uint256 id = voter.getCurrentVotingPeriod() + 1;\n\n // Deploy a rewarder\n IRewarder rewarder = IRewarder(\n rewardFactory.createBribeRewarder(address(osToken), pool)\n );\n\n // Approve the rewarder to spend the balance\n osToken.approve(address(rewarder), balance);\n\n // Fund and bribe the rewarder\n rewarder.fundAndBribe(id, id, balance);\n\n emit BribeExecuted(balance);\n }\n}\n\ninterface IRewarderFactory {\n function createBribeRewarder(address token, address pool)\n external\n returns (address rewarder);\n\n function getWhitelistedTokenInfo(address token)\n external\n view\n returns (bool isWhitelisted, uint256 minBribeAmount);\n}\n\ninterface IRewarder {\n function fundAndBribe(\n uint256 startId,\n uint256 lastId,\n uint256 amountPerPeriod\n ) external payable;\n}\n\ninterface IVoter {\n function getCurrentVotingPeriod() external view returns (uint256);\n}\n" + }, + "contracts/poolBooster/PoolBoosterSwapxDouble.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBribe } from \"../interfaces/poolBooster/ISwapXAlgebraBribe.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n/**\n * @title Pool booster for SwapX concentrated liquidity where 2 gauges are created for\n * every pool. Ichi vaults currently have such setup.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterSwapxDouble is IPoolBooster {\n using StableMath for uint256;\n\n // @notice address of the Bribes.sol(Bribe) contract for the OS token side\n IBribe public immutable bribeContractOS;\n // @notice address of the Bribes.sol(Bribe) contract for the other token in the pool\n IBribe public immutable bribeContractOther;\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n // bribe contract and 60% to other bribe contract\n uint256 public immutable split;\n\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n constructor(\n address _bribeContractOS,\n address _bribeContractOther,\n address _osToken,\n uint256 _split\n ) {\n require(\n _bribeContractOS != address(0),\n \"Invalid bribeContractOS address\"\n );\n require(\n _bribeContractOther != address(0),\n \"Invalid bribeContractOther address\"\n );\n // expect it to be between 1% & 99%\n require(_split > 1e16 && _split < 99e16, \"Unexpected split amount\");\n\n bribeContractOS = IBribe(_bribeContractOS);\n bribeContractOther = IBribe(_bribeContractOther);\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n split = _split;\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n if (balance < MIN_BRIBE_AMOUNT) {\n return;\n }\n\n uint256 osBribeAmount = balance.mulTruncate(split);\n uint256 otherBribeAmount = balance - osBribeAmount;\n\n osToken.approve(address(bribeContractOS), osBribeAmount);\n osToken.approve(address(bribeContractOther), otherBribeAmount);\n\n bribeContractOS.notifyRewardAmount(address(osToken), osBribeAmount);\n bribeContractOther.notifyRewardAmount(\n address(osToken),\n otherBribeAmount\n );\n\n emit BribeExecuted(balance);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterSwapxSingle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBribe } from \"../interfaces/poolBooster/ISwapXAlgebraBribe.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @title Pool booster for SwapX for Classic Stable Pools and Classic Volatile Pools\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterSwapxSingle is IPoolBooster {\n // @notice address of the Bribes.sol(Bribe) contract\n IBribe public immutable bribeContract;\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n constructor(address _bribeContract, address _osToken) {\n require(_bribeContract != address(0), \"Invalid bribeContract address\");\n bribeContract = IBribe(_bribeContract);\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n if (balance < MIN_BRIBE_AMOUNT) {\n return;\n }\n\n osToken.approve(address(bribeContract), balance);\n\n bribeContract.notifyRewardAmount(address(osToken), balance);\n emit BribeExecuted(balance);\n }\n}\n" + }, + "contracts/proxies/BaseProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice BridgedBaseWOETHProxy delegates calls to BridgedWOETH implementation\n */\ncontract BridgedBaseWOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseVaultProxy delegates calls to OETHBaseVault implementation\n */\ncontract OETHBaseVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseProxy delegates calls to OETH implementation\n */\ncontract OETHBaseProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHBaseProxy delegates calls to WOETH implementation\n */\ncontract WOETHBaseProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseDripperProxy delegates calls to a FixedRateDripper implementation\n */\ncontract OETHBaseDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice AerodromeAMOStrategyProxy delegates calls to AerodromeAMOStrategy implementation\n */\ncontract AerodromeAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BridgedWOETHStrategyProxy delegates calls to BridgedWOETHStrategy implementation\n */\ncontract BridgedWOETHStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseHarvesterProxy delegates calls to a SuperOETHHarvester implementation\n */\ncontract OETHBaseHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseCurveAMOProxy delegates calls to a OETHBaseCurveAMO implementation\n */\ncontract OETHBaseCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/proxies/InitializeGovernedUpgradeabilityProxy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { Governable } from \"../governance/Governable.sol\";\n\n/**\n * @title BaseGovernedUpgradeabilityProxy\n * @dev This contract combines an upgradeability proxy with our governor system.\n * It is based on an older version of OpenZeppelins BaseUpgradeabilityProxy\n * with Solidity ^0.8.0.\n * @author Origin Protocol Inc\n */\ncontract InitializeGovernedUpgradeabilityProxy is Governable {\n /**\n * @dev Emitted when the implementation is upgraded.\n * @param implementation Address of the new implementation.\n */\n event Upgraded(address indexed implementation);\n\n constructor() {\n _setGovernor(msg.sender);\n }\n\n /**\n * @dev Contract initializer with Governor enforcement\n * @param _logic Address of the initial implementation.\n * @param _initGovernor Address of the initial Governor.\n * @param _data Data to send as msg.data to the implementation to initialize\n * the proxied contract.\n * It should include the signature and the parameters of the function to be\n * called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n * This parameter is optional, if no data is given the initialization call\n * to proxied contract will be skipped.\n */\n function initialize(\n address _logic,\n address _initGovernor,\n bytes calldata _data\n ) public payable onlyGovernor {\n require(_implementation() == address(0));\n require(_logic != address(0), \"Implementation not set\");\n assert(\n IMPLEMENTATION_SLOT ==\n bytes32(uint256(keccak256(\"eip1967.proxy.implementation\")) - 1)\n );\n _setImplementation(_logic);\n if (_data.length > 0) {\n (bool success, ) = _logic.delegatecall(_data);\n require(success);\n }\n _changeGovernor(_initGovernor);\n }\n\n /**\n * @return The address of the proxy admin/it's also the governor.\n */\n function admin() external view returns (address) {\n return _governor();\n }\n\n /**\n * @return The address of the implementation.\n */\n function implementation() external view returns (address) {\n return _implementation();\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy.\n * Only the admin can call this function.\n * @param _newImplementation Address of the new implementation.\n */\n function upgradeTo(address _newImplementation) external onlyGovernor {\n _upgradeTo(_newImplementation);\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy and call a function\n * on the new implementation.\n * This is useful to initialize the proxied contract.\n * @param newImplementation Address of the new implementation.\n * @param data Data to send as msg.data in the low level call.\n * It should include the signature and the parameters of the function to be called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n */\n function upgradeToAndCall(address newImplementation, bytes calldata data)\n external\n payable\n onlyGovernor\n {\n _upgradeTo(newImplementation);\n (bool success, ) = newImplementation.delegatecall(data);\n require(success);\n }\n\n /**\n * @dev Fallback function.\n * Implemented entirely in `_fallback`.\n */\n fallback() external payable {\n _fallback();\n }\n\n /**\n * @dev Delegates execution to an implementation contract.\n * This is a low level function that doesn't return to its internal call site.\n * It will return to the external caller whatever the implementation returns.\n * @param _impl Address to delegate.\n */\n function _delegate(address _impl) internal {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(gas(), _impl, 0, calldatasize(), 0, 0)\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n /**\n * @dev Function that is run as the first thing in the fallback function.\n * Can be redefined in derived contracts to add functionality.\n * Redefinitions must call super._willFallback().\n */\n function _willFallback() internal {}\n\n /**\n * @dev fallback implementation.\n * Extracted to enable manual triggering.\n */\n function _fallback() internal {\n _willFallback();\n _delegate(_implementation());\n }\n\n /**\n * @dev Storage slot with the address of the current implementation.\n * This is the keccak-256 hash of \"eip1967.proxy.implementation\" subtracted by 1, and is\n * validated in the constructor.\n */\n bytes32 internal constant IMPLEMENTATION_SLOT =\n 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n /**\n * @dev Returns the current implementation.\n * @return impl Address of the current implementation\n */\n function _implementation() internal view returns (address impl) {\n bytes32 slot = IMPLEMENTATION_SLOT;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n impl := sload(slot)\n }\n }\n\n /**\n * @dev Upgrades the proxy to a new implementation.\n * @param newImplementation Address of the new implementation.\n */\n function _upgradeTo(address newImplementation) internal {\n _setImplementation(newImplementation);\n emit Upgraded(newImplementation);\n }\n\n /**\n * @dev Sets the implementation address of the proxy.\n * @param newImplementation Address of the new implementation.\n */\n function _setImplementation(address newImplementation) internal {\n require(\n Address.isContract(newImplementation),\n \"Cannot set a proxy implementation to a non-contract address\"\n );\n\n bytes32 slot = IMPLEMENTATION_SLOT;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(slot, newImplementation)\n }\n }\n}\n" + }, + "contracts/proxies/PlumeProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OETHPlumeVaultProxy delegates calls to OETHPlumeVault implementation\n */\ncontract OETHPlumeVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHPlumeProxy delegates calls to OETH implementation\n */\ncontract OETHPlumeProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHPlumeProxy delegates calls to WOETH implementation\n */\ncontract WOETHPlumeProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice RoosterAMOStrategyProxy delegates calls to a RoosterAMOStrategy implementation\n */\ncontract RoosterAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/proxies/Proxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OUSDProxy delegates calls to an OUSD implementation\n */\ncontract OUSDProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WrappedOUSDProxy delegates calls to a WrappedOUSD implementation\n */\ncontract WrappedOUSDProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice VaultProxy delegates calls to a Vault implementation\n */\ncontract VaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CompoundStrategyProxy delegates calls to a CompoundStrategy implementation\n */\ncontract CompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice AaveStrategyProxy delegates calls to a AaveStrategy implementation\n */\ncontract AaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ConvexStrategyProxy delegates calls to a ConvexStrategy implementation\n */\ncontract ConvexStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice HarvesterProxy delegates calls to a Harvester implementation\n */\ncontract HarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice DripperProxy delegates calls to a Dripper implementation\n */\ncontract DripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoCompoundStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\n */\ncontract MorphoCompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ConvexOUSDMetaStrategyProxy delegates calls to a ConvexOUSDMetaStrategy implementation\n */\ncontract ConvexOUSDMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoAaveStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\n */\ncontract MorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHProxy delegates calls to nowhere for now\n */\ncontract OETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHProxy delegates calls to nowhere for now\n */\ncontract WOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHVaultProxy delegates calls to a Vault implementation\n */\ncontract OETHVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHDripperProxy delegates calls to a OETHDripper implementation\n */\ncontract OETHDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHHarvesterProxy delegates calls to a Harvester implementation\n */\ncontract OETHHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CurveEthStrategyProxy delegates calls to a CurveEthStrategy implementation\n */\ncontract ConvexEthMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BuybackProxy delegates calls to Buyback implementation\n */\ncontract BuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHMorphoAaveStrategyProxy delegates calls to a MorphoAaveStrategy implementation\n */\ncontract OETHMorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBalancerMetaPoolrEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\n */\ncontract OETHBalancerMetaPoolrEthStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice OETHBalancerMetaPoolwstEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\n */\ncontract OETHBalancerMetaPoolwstEthStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MakerDsrStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MakerDsrStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBuybackProxy delegates calls to Buyback implementation\n */\ncontract OETHBuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BridgedWOETHProxy delegates calls to BridgedWOETH implementation\n */\ncontract BridgedWOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice NativeStakingSSVStrategyProxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulatorProxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulatorProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingSSVStrategy2Proxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategy2Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulator2Proxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulator2Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingSSVStrategy3Proxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategy3Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulator3Proxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulator3Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MetaMorphoStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MetaMorphoStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ARMBuybackProxy delegates calls to Buyback implementation\n */\ncontract ARMBuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoGauntletPrimeUSDCStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MorphoGauntletPrimeUSDCStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MorphoGauntletPrimeUSDTStrategyProxy delegates calls to a Generalized4626USDTStrategy implementation\n */\ncontract MorphoGauntletPrimeUSDTStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice CurvePoolBoosterProxy delegates calls to a CurvePoolBooster implementation\n */\ncontract CurvePoolBoosterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHFixedRateDripperProxy delegates calls to a OETHFixedRateDripper implementation\n */\ncontract OETHFixedRateDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHSimpleHarvesterProxy delegates calls to a OETHSimpleHarvester implementation\n */\ncontract OETHSimpleHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice PoolBoostCentralRegistryProxy delegates calls to the PoolBoostCentralRegistry implementation\n */\ncontract PoolBoostCentralRegistryProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MakerSSRStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MakerSSRStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OUSDCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\n */\ncontract OUSDCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\n */\ncontract OETHCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CompoundingStakingSSVStrategyProxy delegates calls to a CompoundingStakingSSVStrategy implementation\n */\ncontract CompoundingStakingSSVStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n" + }, + "contracts/proxies/SonicProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OSonicVaultProxy delegates calls to OSonicVault implementation\n */\ncontract OSonicVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicProxy delegates calls to OSonic implementation\n */\ncontract OSonicProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOSonicProxy delegates calls to WOSonic implementation\n */\ncontract WOSonicProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicDripperProxy delegates calls to a FixedRateDripper implementation\n */\ncontract OSonicDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice SonicStakingStrategyProxy delegates calls to SonicStakingStrategy implementation\n */\ncontract SonicStakingStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicHarvesterProxy delegates calls to a OSonicHarvester implementation\n */\ncontract OSonicHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice SonicSwapXAMOStrategyProxy delegates calls to a SonicSwapXAMOStrategy implementation\n */\ncontract SonicSwapXAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/strategies/AaveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Aave Strategy\n * @notice Investment strategy for investing stablecoins via Aave\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport \"./IAave.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\nimport { IAaveStakedToken } from \"./IAaveStakeToken.sol\";\nimport { IAaveIncentivesController } from \"./IAaveIncentivesController.sol\";\n\ncontract AaveStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n\n uint16 constant referralCode = 92;\n\n IAaveIncentivesController public incentivesController;\n IAaveStakedToken public stkAave;\n\n /**\n * @param _stratConfig The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as AAVE needs several extra\n * addresses for the rewards program.\n * @param _rewardTokenAddresses Address of the AAVE token\n * @param _assets Addresses of supported assets\n * @param _pTokens Platform Token corresponding addresses\n * @param _incentivesAddress Address of the AAVE incentives controller\n * @param _stkAaveAddress Address of the stkAave contract\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // AAVE\n address[] calldata _assets,\n address[] calldata _pTokens,\n address _incentivesAddress,\n address _stkAaveAddress\n ) external onlyGovernor initializer {\n incentivesController = IAaveIncentivesController(_incentivesAddress);\n stkAave = IAaveStakedToken(_stkAaveAddress);\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Deposit asset into Aave\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Aave\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n // Following line also doubles as a check that we are depositing\n // an asset that we support.\n emit Deposit(_asset, _getATokenFor(_asset), _amount);\n _getLendingPool().deposit(_asset, _amount, address(this), referralCode);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Aave\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Aave\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n emit Withdrawal(_asset, _getATokenFor(_asset), _amount);\n uint256 actual = _getLendingPool().withdraw(\n _asset,\n _amount,\n address(this)\n );\n require(actual == _amount, \"Did not withdraw enough\");\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n // Redeem entire balance of aToken\n IERC20 asset = IERC20(assetsMapped[i]);\n address aToken = _getATokenFor(assetsMapped[i]);\n uint256 balance = IERC20(aToken).balanceOf(address(this));\n if (balance > 0) {\n uint256 actual = _getLendingPool().withdraw(\n address(asset),\n balance,\n address(this)\n );\n require(actual == balance, \"Did not withdraw enough\");\n\n uint256 assetBalance = asset.balanceOf(address(this));\n // Transfer entire balance to Vault\n asset.safeTransfer(vaultAddress, assetBalance);\n\n emit Withdrawal(address(asset), aToken, assetBalance);\n }\n }\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n // Balance is always with token aToken decimals\n address aToken = _getATokenFor(_asset);\n balance = IERC20(aToken).balanceOf(address(this));\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Approve the spending of all assets by their corresponding aToken,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n address lendingPool = address(_getLendingPool());\n // approve the pool to spend the Asset\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n address asset = assetsMapped[i];\n // Safe approval\n IERC20(asset).safeApprove(lendingPool, 0);\n IERC20(asset).safeApprove(lendingPool, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / aTokens\n We need to give the AAVE lending pool approval to transfer the\n asset.\n * @param _asset Address of the asset to approve\n * @param _aToken Address of the aToken\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _aToken)\n internal\n override\n {\n address lendingPool = address(_getLendingPool());\n IERC20(_asset).safeApprove(lendingPool, 0);\n IERC20(_asset).safeApprove(lendingPool, type(uint256).max);\n }\n\n /**\n * @dev Get the aToken wrapped in the IERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return Corresponding aToken to this asset\n */\n function _getATokenFor(address _asset) internal view returns (address) {\n address aToken = assetToPToken[_asset];\n require(aToken != address(0), \"aToken does not exist\");\n return aToken;\n }\n\n /**\n * @dev Get the current address of the Aave lending pool, which is the gateway to\n * depositing.\n * @return Current lending pool implementation\n */\n function _getLendingPool() internal view returns (IAaveLendingPool) {\n address lendingPool = ILendingPoolAddressesProvider(platformAddress)\n .getLendingPool();\n require(lendingPool != address(0), \"Lending pool does not exist\");\n return IAaveLendingPool(lendingPool);\n }\n\n /**\n * @dev Collect stkAave, convert it to AAVE send to Vault.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n if (address(stkAave) == address(0)) {\n return;\n }\n\n // Check staked AAVE cooldown timer\n uint256 cooldown = stkAave.stakersCooldowns(address(this));\n uint256 windowStart = cooldown + stkAave.COOLDOWN_SECONDS();\n uint256 windowEnd = windowStart + stkAave.UNSTAKE_WINDOW();\n\n // If inside the unlock window, then we can redeem stkAave\n // for AAVE and send it to the vault.\n if (block.timestamp > windowStart && block.timestamp <= windowEnd) {\n // Redeem to AAVE\n uint256 stkAaveBalance = stkAave.balanceOf(address(this));\n stkAave.redeem(address(this), stkAaveBalance);\n\n // Transfer AAVE to harvesterAddress\n uint256 aaveBalance = IERC20(rewardTokenAddresses[0]).balanceOf(\n address(this)\n );\n if (aaveBalance > 0) {\n IERC20(rewardTokenAddresses[0]).safeTransfer(\n harvesterAddress,\n aaveBalance\n );\n }\n }\n\n // Collect available rewards and restart the cooldown timer, if either of\n // those should be run.\n if (block.timestamp > windowStart || cooldown == 0) {\n uint256 assetsLen = assetsMapped.length;\n // aToken addresses for incentives controller\n address[] memory aTokens = new address[](assetsLen);\n for (uint256 i = 0; i < assetsLen; ++i) {\n aTokens[i] = _getATokenFor(assetsMapped[i]);\n }\n\n // 1. If we have rewards availabile, collect them\n uint256 pendingRewards = incentivesController.getRewardsBalance(\n aTokens,\n address(this)\n );\n if (pendingRewards > 0) {\n // Because getting more stkAAVE from the incentives controller\n // with claimRewards() may push the stkAAVE cooldown time\n // forward, it is called after stakedAAVE has been turned into\n // AAVE.\n uint256 collected = incentivesController.claimRewards(\n aTokens,\n pendingRewards,\n address(this)\n );\n require(collected == pendingRewards, \"AAVE reward difference\");\n }\n\n // 2. Start cooldown counting down.\n if (stkAave.balanceOf(address(this)) > 0) {\n // Protected with if since cooldown call would revert\n // if no stkAave balance.\n stkAave.cooldown();\n }\n }\n }\n}\n" + }, + "contracts/strategies/AbstractCompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base Compound Abstract Strategy\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { ICERC20 } from \"./ICompound.sol\";\nimport { IComptroller } from \"../interfaces/IComptroller.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\nabstract contract AbstractCompoundStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n\n int256[50] private __reserved;\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Get the cToken wrapped in the ICERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return Corresponding cToken to this asset\n */\n function _getCTokenFor(address _asset) internal view returns (ICERC20) {\n address cToken = assetToPToken[_asset];\n require(cToken != address(0), \"cToken does not exist\");\n return ICERC20(cToken);\n }\n\n /**\n * @dev Converts an underlying amount into cToken amount\n * cTokenAmt = (underlying * 1e18) / exchangeRate\n * @param _cToken cToken for which to change\n * @param _underlying Amount of underlying to convert\n * @return amount Equivalent amount of cTokens\n */\n function _convertUnderlyingToCToken(ICERC20 _cToken, uint256 _underlying)\n internal\n view\n returns (uint256 amount)\n {\n // e.g. 1e18*1e18 / 205316390724364402565641705 = 50e8\n // e.g. 1e8*1e18 / 205316390724364402565641705 = 0.45 or 0\n amount = (_underlying * 1e18) / _cToken.exchangeRateStored();\n }\n}\n" + }, + "contracts/strategies/AbstractConvexMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { ICurveMetaPool } from \"./ICurveMetaPool.sol\";\nimport { IERC20, AbstractCurveStrategy, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\nabstract contract AbstractConvexMetaStrategy is AbstractCurveStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n event MaxWithdrawalSlippageUpdated(\n uint256 _prevMaxSlippagePercentage,\n uint256 _newMaxSlippagePercentage\n );\n\n // used to circumvent the stack too deep issue\n struct InitConfig {\n address cvxDepositorAddress; //Address of the Convex depositor(AKA booster) for this pool\n address metapoolAddress; //Address of the Curve MetaPool\n address metapoolMainToken; //Address of Main metapool token\n address cvxRewardStakerAddress; //Address of the CVX rewards staker\n address metapoolLPToken; //Address of metapool LP token\n uint256 cvxDepositorPTokenId; //Pid of the pool referred to by Depositor and staker\n }\n\n address internal cvxDepositorAddress;\n address internal cvxRewardStakerAddress;\n uint256 internal cvxDepositorPTokenId;\n ICurveMetaPool internal metapool;\n IERC20 internal metapoolMainToken;\n IERC20 internal metapoolLPToken;\n // Ordered list of metapool assets\n address[] internal metapoolAssets;\n // Max withdrawal slippage denominated in 1e18 (1e18 == 100%)\n uint256 public maxWithdrawalSlippage;\n uint128 internal crvCoinIndex;\n uint128 internal mainCoinIndex;\n\n int256[41] private ___reserved;\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * DAI, USDC, USDT\n * @param _pTokens Platform Token corresponding addresses\n * @param initConfig Various addresses and info for initialization state\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets,\n address[] calldata _pTokens,\n InitConfig calldata initConfig\n ) external onlyGovernor initializer {\n require(_assets.length == 3, \"Must have exactly three assets\");\n // Should be set prior to abstract initialize call otherwise\n // abstractSetPToken calls will fail\n cvxDepositorAddress = initConfig.cvxDepositorAddress;\n pTokenAddress = _pTokens[0];\n metapool = ICurveMetaPool(initConfig.metapoolAddress);\n metapoolMainToken = IERC20(initConfig.metapoolMainToken);\n cvxRewardStakerAddress = initConfig.cvxRewardStakerAddress;\n metapoolLPToken = IERC20(initConfig.metapoolLPToken);\n cvxDepositorPTokenId = initConfig.cvxDepositorPTokenId;\n maxWithdrawalSlippage = 1e16;\n\n metapoolAssets = [metapool.coins(0), metapool.coins(1)];\n crvCoinIndex = _getMetapoolCoinIndex(pTokenAddress);\n mainCoinIndex = _getMetapoolCoinIndex(initConfig.metapoolMainToken);\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n balance = 0;\n\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 contractPTokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (contractPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = contractPTokens.mulTruncate(virtual_price);\n balance += value;\n }\n\n /* We intentionally omit the metapoolLp tokens held by the metastrategyContract\n * since the contract should never (except in the middle of deposit/withdrawal\n * transaction) hold any amount of those tokens in normal operation. There\n * could be tokens sent to it by a 3rd party and we decide to actively ignore\n * those.\n */\n uint256 metapoolGaugePTokens = IRewardStaking(cvxRewardStakerAddress)\n .balanceOf(address(this));\n\n if (metapoolGaugePTokens > 0) {\n uint256 value = metapoolGaugePTokens.mulTruncate(\n metapool.get_virtual_price()\n );\n balance += value;\n }\n\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = balance.scaleBy(assetDecimals, 18) / THREEPOOL_ASSET_COUNT;\n }\n\n /**\n * @dev This function is completely analogous to _calcCurveTokenAmount[AbstractCurveStrategy]\n * and just utilizes different Curve (meta)pool API\n */\n function _calcCurveMetaTokenAmount(uint128 _coinIndex, uint256 _amount)\n internal\n returns (uint256 requiredMetapoolLP)\n {\n uint256[2] memory _amounts = [uint256(0), uint256(0)];\n _amounts[uint256(_coinIndex)] = _amount;\n\n // LP required when removing required asset ignoring fees\n uint256 lpRequiredNoFees = metapool.calc_token_amount(_amounts, false);\n /* LP required if fees would apply to entirety of removed amount\n *\n * fee is 1e10 denominated number: https://curve.readthedocs.io/exchange-pools.html#StableSwap.fee\n */\n uint256 lpRequiredFullFees = lpRequiredNoFees.mulTruncateScale(\n 1e10 + metapool.fee(),\n 1e10\n );\n\n /* asset received when withdrawing full fee applicable LP accounting for\n * slippage and fees\n */\n uint256 assetReceivedForFullLPFees = metapool.calc_withdraw_one_coin(\n lpRequiredFullFees,\n int128(_coinIndex)\n );\n\n // exact amount of LP required\n requiredMetapoolLP =\n (lpRequiredFullFees * _amount) /\n assetReceivedForFullLPFees;\n }\n\n function _approveBase() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // 3Pool for LP token (required for removing liquidity)\n pToken.safeApprove(platformAddress, 0);\n pToken.safeApprove(platformAddress, type(uint256).max);\n // Gauge for LP token\n metapoolLPToken.safeApprove(cvxDepositorAddress, 0);\n metapoolLPToken.safeApprove(cvxDepositorAddress, type(uint256).max);\n // Metapool for LP token\n pToken.safeApprove(address(metapool), 0);\n pToken.safeApprove(address(metapool), type(uint256).max);\n // Metapool for Metapool main token\n metapoolMainToken.safeApprove(address(metapool), 0);\n metapoolMainToken.safeApprove(address(metapool), type(uint256).max);\n }\n\n /**\n * @dev Get the index of the coin\n */\n function _getMetapoolCoinIndex(address _asset)\n internal\n view\n returns (uint128)\n {\n for (uint128 i = 0; i < 2; i++) {\n if (metapoolAssets[i] == _asset) return i;\n }\n revert(\"Invalid Metapool asset\");\n }\n\n /**\n * @dev Sets max withdrawal slippage that is considered when removing\n * liquidity from Metapools.\n * @param _maxWithdrawalSlippage Max withdrawal slippage denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxWithdrawalSlippage should actually be 0.1% (1e15)\n * for production usage. Contract allows as low value as 0% for confirming\n * correct behavior in test suite.\n */\n function setMaxWithdrawalSlippage(uint256 _maxWithdrawalSlippage)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(\n _maxWithdrawalSlippage <= 1e18,\n \"Max withdrawal slippage needs to be between 0% - 100%\"\n );\n emit MaxWithdrawalSlippageUpdated(\n maxWithdrawalSlippage,\n _maxWithdrawalSlippage\n );\n maxWithdrawalSlippage = _maxWithdrawalSlippage;\n }\n\n /**\n * @dev Collect accumulated CRV and CVX and send to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n IRewardStaking(cvxRewardStakerAddress).getReward();\n _collectRewardTokens();\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/AbstractCurveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve 3Pool Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\nabstract contract AbstractCurveStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 internal constant MAX_SLIPPAGE = 1e16; // 1%, same as the Curve UI\n // number of assets in Curve 3Pool (USDC, DAI, USDT)\n uint256 internal constant THREEPOOL_ASSET_COUNT = 3;\n address internal pTokenAddress;\n\n int256[49] private __reserved;\n\n /**\n * @dev Deposit asset into the Curve 3Pool\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_amount > 0, \"Must deposit something\");\n emit Deposit(_asset, pTokenAddress, _amount);\n\n // 3Pool requires passing deposit amounts for all 3 assets, set to 0 for\n // all\n uint256[3] memory _amounts;\n uint256 poolCoinIndex = _getCoinIndex(_asset);\n // Set the amount on the asset we want to deposit\n _amounts[poolCoinIndex] = _amount;\n ICurvePool curvePool = ICurvePool(platformAddress);\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n uint256 depositValue = _amount.scaleBy(18, assetDecimals).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = depositValue.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n // Do the deposit to 3pool\n curvePool.add_liquidity(_amounts, minMintAmount);\n _lpDepositAll();\n }\n\n function _lpDepositAll() internal virtual;\n\n /**\n * @dev Deposit the entire balance of any supported asset into the Curve 3pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n uint256 depositValue = 0;\n ICurvePool curvePool = ICurvePool(platformAddress);\n uint256 curveVirtualPrice = curvePool.get_virtual_price();\n\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address assetAddress = assetsMapped[i];\n uint256 balance = IERC20(assetAddress).balanceOf(address(this));\n if (balance > 0) {\n uint256 poolCoinIndex = _getCoinIndex(assetAddress);\n // Set the amount on the asset we want to deposit\n _amounts[poolCoinIndex] = balance;\n uint256 assetDecimals = Helpers.getDecimals(assetAddress);\n // Get value of deposit in Curve LP token to later determine\n // the minMintAmount argument for add_liquidity\n depositValue =\n depositValue +\n balance.scaleBy(18, assetDecimals).divPrecisely(\n curveVirtualPrice\n );\n emit Deposit(assetAddress, pTokenAddress, balance);\n }\n }\n\n uint256 minMintAmount = depositValue.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n // Do the deposit to 3pool\n curvePool.add_liquidity(_amounts, minMintAmount);\n\n /* In case of Curve Strategy all assets are mapped to the same pToken (3CrvLP). Let\n * descendants further handle the pToken. By either deploying it to the metapool and\n * resulting tokens in Gauge. Or deploying pTokens directly to the Gauge.\n */\n _lpDepositAll();\n }\n\n function _lpWithdraw(uint256 numCrvTokens) internal virtual;\n\n function _lpWithdrawAll() internal virtual;\n\n /**\n * @dev Withdraw asset from Curve 3Pool\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Invalid amount\");\n\n emit Withdrawal(_asset, pTokenAddress, _amount);\n\n uint256 contractCrv3Tokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n\n uint256 coinIndex = _getCoinIndex(_asset);\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 requiredCrv3Tokens = _calcCurveTokenAmount(coinIndex, _amount);\n\n // We have enough LP tokens, make sure they are all on this contract\n if (contractCrv3Tokens < requiredCrv3Tokens) {\n _lpWithdraw(requiredCrv3Tokens - contractCrv3Tokens);\n }\n\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n _amounts[coinIndex] = _amount;\n\n curvePool.remove_liquidity_imbalance(_amounts, requiredCrv3Tokens);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Calculate amount of LP required when withdrawing specific amount of one\n * of the underlying assets accounting for fees and slippage.\n *\n * Curve pools unfortunately do not contain a calculation function for\n * amount of LP required when withdrawing a specific amount of one of the\n * underlying tokens and also accounting for fees (Curve's calc_token_amount\n * does account for slippage but not fees).\n *\n * Steps taken to calculate the metric:\n * - get amount of LP required if fees wouldn't apply\n * - increase the LP amount as if fees would apply to the entirety of the underlying\n * asset withdrawal. (when withdrawing only one coin fees apply only to amounts\n * of other assets pool would return in case of balanced removal - since those need\n * to be swapped for the single underlying asset being withdrawn)\n * - get amount of underlying asset withdrawn (this Curve function does consider slippage\n * and fees) when using the increased LP amount. As LP amount is slightly over-increased\n * so is amount of underlying assets returned.\n * - since we know exactly how much asset we require take the rate of LP required for asset\n * withdrawn to get the exact amount of LP.\n */\n function _calcCurveTokenAmount(uint256 _coinIndex, uint256 _amount)\n internal\n returns (uint256 required3Crv)\n {\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n _amounts[_coinIndex] = _amount;\n\n // LP required when removing required asset ignoring fees\n uint256 lpRequiredNoFees = curvePool.calc_token_amount(_amounts, false);\n /* LP required if fees would apply to entirety of removed amount\n *\n * fee is 1e10 denominated number: https://curve.readthedocs.io/exchange-pools.html#StableSwap.fee\n */\n uint256 lpRequiredFullFees = lpRequiredNoFees.mulTruncateScale(\n 1e10 + curvePool.fee(),\n 1e10\n );\n\n /* asset received when withdrawing full fee applicable LP accounting for\n * slippage and fees\n */\n uint256 assetReceivedForFullLPFees = curvePool.calc_withdraw_one_coin(\n lpRequiredFullFees,\n int128(uint128(_coinIndex))\n );\n\n // exact amount of LP required\n required3Crv =\n (lpRequiredFullFees * _amount) /\n assetReceivedForFullLPFees;\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n _lpWithdrawAll();\n // Withdraws are proportional to assets held by 3Pool\n uint256[3] memory minWithdrawAmounts = [\n uint256(0),\n uint256(0),\n uint256(0)\n ];\n\n // Remove liquidity\n ICurvePool threePool = ICurvePool(platformAddress);\n threePool.remove_liquidity(\n IERC20(pTokenAddress).balanceOf(address(this)),\n minWithdrawAmounts\n );\n // Transfer assets out of Vault\n // Note that Curve will provide all 3 of the assets in 3pool even if\n // we have not set PToken addresses for all of them in this strategy\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n IERC20 asset = IERC20(threePool.coins(i));\n asset.safeTransfer(vaultAddress, asset.balanceOf(address(this)));\n }\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 totalPTokens = IERC20(pTokenAddress).balanceOf(address(this));\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (totalPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = (totalPTokens * virtual_price) / 1e18;\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = value.scaleBy(assetDecimals, 18) / THREEPOOL_ASSET_COUNT;\n }\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n // This strategy is a special case since it only supports one asset\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n _approveAsset(assetsMapped[i]);\n }\n }\n\n /**\n * @dev Call the necessary approvals for the Curve pool and gauge\n * @param _asset Address of the asset\n */\n function _abstractSetPToken(address _asset, address) internal override {\n _approveAsset(_asset);\n }\n\n function _approveAsset(address _asset) internal {\n IERC20 asset = IERC20(_asset);\n // 3Pool for asset (required for adding liquidity)\n asset.safeApprove(platformAddress, 0);\n asset.safeApprove(platformAddress, type(uint256).max);\n }\n\n function _approveBase() internal virtual;\n\n /**\n * @dev Get the index of the coin\n */\n function _getCoinIndex(address _asset) internal view returns (uint256) {\n for (uint256 i = 0; i < 3; i++) {\n if (assetsMapped[i] == _asset) return i;\n }\n revert(\"Invalid 3pool asset\");\n }\n}\n" + }, + "contracts/strategies/aerodrome/AerodromeAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Aerodrome AMO strategy\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nimport { ISugarHelper } from \"../../interfaces/aerodrome/ISugarHelper.sol\";\nimport { INonfungiblePositionManager } from \"../../interfaces/aerodrome/INonfungiblePositionManager.sol\";\nimport { ISwapRouter } from \"../../interfaces/aerodrome/ISwapRouter.sol\";\nimport { ICLPool } from \"../../interfaces/aerodrome/ICLPool.sol\";\nimport { ICLGauge } from \"../../interfaces/aerodrome/ICLGauge.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\n\ncontract AerodromeAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n using SafeCast for uint256;\n\n /************************************************\n Important (!) setup configuration\n *************************************************/\n\n /**\n * In order to be able to remove a reasonable amount of complexity from the contract one of the\n * preconditions for this contract to function correctly is to have an outside account mint a small\n * amount of liquidity in the tick space where the contract will deploy's its liquidity and then send\n * that NFT LP position to a dead address (transfer to zero address not allowed.) See example of such\n * NFT LP token:\n * https://basescan.org/token/0x827922686190790b37229fd06084350e74485b72?a=413296#inventory\n */\n\n /***************************************\n Storage slot members\n ****************************************/\n\n /// @notice tokenId of the liquidity position\n uint256 public tokenId;\n /// @dev Minimum amount of tokens the strategy would be able to withdraw from the pool.\n /// minimum amount of tokens are withdrawn at a 1:1 price\n uint256 public underlyingAssets;\n /// @notice Marks the start of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareStart;\n /// @notice Marks the end of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareEnd;\n /// @dev reserved for inheritance\n int256[46] private __reserved;\n\n /***************************************\n Constants, structs and events\n ****************************************/\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the OETHb token contract\n address public immutable OETHb;\n /// @notice lower tick set to -1 representing the price of 1.0001 of WETH for 1 OETHb.\n int24 public immutable lowerTick;\n /// @notice lower tick set to 0 representing the price of 1.0000 of WETH for 1 OETHb.\n int24 public immutable upperTick;\n /// @notice tick spacing of the pool (set to 1)\n int24 public immutable tickSpacing;\n /// @notice the swapRouter for performing swaps\n ISwapRouter public immutable swapRouter;\n /// @notice the underlying AMO Slipstream pool\n ICLPool public immutable clPool;\n /// @notice the gauge for the corresponding Slipstream pool (clPool)\n /// @dev can become an immutable once the gauge is created on the base main-net\n ICLGauge public immutable clGauge;\n /// @notice the Position manager contract that is used to manage the pool's position\n INonfungiblePositionManager public immutable positionManager;\n /// @notice helper contract for liquidity and ticker math\n ISugarHelper public immutable helper;\n /// @notice sqrtRatioX96TickLower\n /// @dev tick lower has value -1 and represents the lowest price of WETH priced in OETHb. Meaning the pool\n /// offers less than 1 OETHb for 1 WETH. In other terms to get 1 OETHB the swap needs to offer 1.0001 WETH\n /// this is where purchasing OETHb with WETH within the liquidity position is most expensive\n uint160 public immutable sqrtRatioX96TickLower;\n /// @notice sqrtRatioX96TickHigher\n /// @dev tick higher has value 0 and represents 1:1 price parity of WETH to OETHb\n uint160 public immutable sqrtRatioX96TickHigher;\n /// @dev tick closest to 1:1 price parity\n /// Correctly assessing which tick is closer to 1:1 price parity is important since it affects\n /// the way we calculate the underlying assets in check Balance. The underlying aerodrome pool\n /// orders the tokens depending on the values of their addresses. If OETH token is token0 in the pool\n /// then sqrtRatioX96TickClosestToParity=sqrtRatioX96TickLower. If it is token1 in the pool then\n /// sqrtRatioX96TickClosestToParity=sqrtRatioX96TickHigher\n uint160 public immutable sqrtRatioX96TickClosestToParity;\n\n /// @dev a threshold under which the contract no longer allows for the protocol to rebalance. Guarding\n /// against a strategist / guardian being taken over and with multiple transactions draining the\n /// protocol funds.\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth); // 0x989e5ca8\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth); // 0xa6737d87\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n ); // 0x3681e8e0\n error OutsideExpectedTickRange(int24 currentTick); // 0x5a2eba75\n\n event PoolRebalanced(uint256 currentPoolWethShare);\n\n event PoolWethShareIntervalUpdated(\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n\n event LiquidityRemoved(\n uint256 withdrawLiquidityShare,\n uint256 removedWETHAmount,\n uint256 removedOETHbAmount,\n uint256 wethAmountCollected,\n uint256 oethbAmountCollected,\n uint256 underlyingAssets\n );\n\n event LiquidityAdded(\n uint256 wethAmountDesired,\n uint256 oethbAmountDesired,\n uint256 wethAmountSupplied,\n uint256 oethbAmountSupplied,\n uint256 tokenId,\n uint256 underlyingAssets\n );\n\n event UnderlyingAssetsUpdated(uint256 underlyingAssets);\n\n /**\n * @dev Un-stakes the token from the gauge for the execution duration of\n * the function and after that re-stakes it back in.\n *\n * It is important that the token is unstaked and owned by the strategy contract\n * during any liquidity altering operations and that it is re-staked back into the\n * gauge after liquidity changes. If the token fails to re-stake back to the\n * gauge it is not earning incentives.\n */\n // all functions using this modifier are used by functions with reentrancy check\n // slither-disable-start reentrancy-no-eth\n modifier gaugeUnstakeAndRestake() {\n // because of solidity short-circuit _isLpTokenStakedInGauge doesn't get called\n // when tokenId == 0\n if (tokenId != 0 && _isLpTokenStakedInGauge()) {\n clGauge.withdraw(tokenId);\n }\n _;\n // because of solidity short-circuit _isLpTokenStakedInGauge doesn't get called\n // when tokenId == 0\n if (tokenId != 0 && !_isLpTokenStakedInGauge()) {\n /**\n * It can happen that a withdrawal (or a full withdrawal) transactions would\n * remove all of the liquidity from the token with a NFT token still existing.\n * In that case the token can not be staked into the gauge, as some liquidity\n * needs to be added to it first.\n */\n if (_getLiquidity() > 0) {\n // if token liquidity changes the positionManager requires re-approval.\n // to any contract pre-approved to handle the token.\n positionManager.approve(address(clGauge), tokenId);\n clGauge.deposit(tokenId);\n }\n }\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice the constructor\n /// @dev This contract is intended to be used as a proxy. To prevent the\n /// potential confusion of having a functional implementation contract\n /// the constructor has the `initializer` modifier. This way the\n /// `initialize` function can not be called on the implementation contract.\n /// For the same reason the implementation contract also has the governor\n /// set to a zero address.\n /// @param _stratConfig the basic strategy configuration\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _oethbAddress Address of the Erc20 OETHb Token contract\n /// @param _swapRouter Address of the Aerodrome Universal Swap Router\n /// @param _nonfungiblePositionManager Address of position manager to add/remove\n /// the liquidity\n /// @param _clPool Address of the Aerodrome concentrated liquidity pool\n /// @param _clGauge Address of the Aerodrome slipstream pool gauge\n /// @param _sugarHelper Address of the Aerodrome Sugar helper contract\n /// @param _lowerBoundingTick Smaller bounding tick of our liquidity position\n /// @param _upperBoundingTick Larger bounding tick of our liquidity position\n /// @param _tickClosestToParity Tick that is closer to 1:1 price parity\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethbAddress,\n address _swapRouter,\n address _nonfungiblePositionManager,\n address _clPool,\n address _clGauge,\n address _sugarHelper,\n int24 _lowerBoundingTick,\n int24 _upperBoundingTick,\n int24 _tickClosestToParity\n ) initializer InitializableAbstractStrategy(_stratConfig) {\n require(\n _lowerBoundingTick == _tickClosestToParity ||\n _upperBoundingTick == _tickClosestToParity,\n \"Misconfigured tickClosestToParity\"\n );\n require(\n ICLPool(_clPool).token0() == _wethAddress,\n \"Only WETH supported as token0\"\n );\n require(\n ICLPool(_clPool).token1() == _oethbAddress,\n \"Only OETHb supported as token1\"\n );\n int24 _tickSpacing = ICLPool(_clPool).tickSpacing();\n // when we generalize AMO we might support other tick spacings\n require(_tickSpacing == 1, \"Unsupported tickSpacing\");\n\n WETH = _wethAddress;\n OETHb = _oethbAddress;\n swapRouter = ISwapRouter(_swapRouter);\n positionManager = INonfungiblePositionManager(\n _nonfungiblePositionManager\n );\n clPool = ICLPool(_clPool);\n clGauge = ICLGauge(_clGauge);\n helper = ISugarHelper(_sugarHelper);\n sqrtRatioX96TickLower = ISugarHelper(_sugarHelper).getSqrtRatioAtTick(\n _lowerBoundingTick\n );\n sqrtRatioX96TickHigher = ISugarHelper(_sugarHelper).getSqrtRatioAtTick(\n _upperBoundingTick\n );\n sqrtRatioX96TickClosestToParity = ISugarHelper(_sugarHelper)\n .getSqrtRatioAtTick(_tickClosestToParity);\n\n lowerTick = _lowerBoundingTick;\n upperTick = _upperBoundingTick;\n tickSpacing = _tickSpacing;\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /**\n * @notice initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n */\n function initialize(address[] memory _rewardTokenAddresses)\n external\n onlyGovernor\n initializer\n {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n new address[](0),\n new address[](0)\n );\n }\n\n /***************************************\n Configuration \n ****************************************/\n\n /**\n * @notice Set allowed pool weth share interval. After the rebalance happens\n * the share of WETH token in the ticker needs to be withing the specifications\n * of the interval.\n *\n * @param _allowedWethShareStart Start of WETH share interval expressed as 18 decimal amount\n * @param _allowedWethShareEnd End of WETH share interval expressed as 18 decimal amount\n */\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external onlyGovernor {\n require(\n _allowedWethShareStart < _allowedWethShareEnd,\n \"Invalid interval\"\n );\n // can not go below 1% weth share\n require(_allowedWethShareStart > 0.01 ether, \"Invalid interval start\");\n // can not go above 95% weth share\n require(_allowedWethShareEnd < 0.95 ether, \"Invalid interval end\");\n\n allowedWethShareStart = _allowedWethShareStart;\n allowedWethShareEnd = _allowedWethShareEnd;\n emit PoolWethShareIntervalUpdated(\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n\n /***************************************\n Periphery utils\n ****************************************/\n\n function _isLpTokenStakedInGauge() internal view returns (bool) {\n require(tokenId != 0, \"Missing NFT LP token\");\n\n address owner = positionManager.ownerOf(tokenId);\n require(\n owner == address(clGauge) || owner == address(this),\n \"Unexpected token owner\"\n );\n return owner == address(clGauge);\n }\n\n /***************************************\n Strategy overrides \n ****************************************/\n\n /**\n * @notice Deposit an amount of assets into the strategy contract. Calling deposit doesn't\n * automatically deposit funds into the underlying Aerodrome pool\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposit WETH to the strategy contract. This function does not add liquidity to the\n * underlying Aerodrome pool.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance > 1e12) {\n _deposit(WETH, _wethBalance);\n }\n }\n\n /**\n * @dev Deposit WETH to the contract. This function doesn't deposit the liquidity to the\n * pool, that is done via the rebalance call.\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n emit Deposit(_asset, address(0), _amount);\n\n // if the pool price is not within the expected interval leave the WETH on the contract\n // as to not break the mints\n (bool _isExpectedRange, ) = _checkForExpectedPoolPrice(false);\n if (_isExpectedRange) {\n // deposit funds into the underlying pool\n _rebalance(0, false, 0);\n }\n }\n\n /**\n * @notice Rebalance the pool to the desired token split and Deposit any WETH on the contract to the\n * underlying aerodrome pool. Print the required amount of corresponding OETHb. After the rebalancing is\n * done burn any potentially remaining OETHb tokens still on the strategy contract.\n *\n * This function has a slightly different behaviour depending on the status of the underlying Aerodrome\n * slipstream pool. The function consists of the following 3 steps:\n * 1. withdrawPartialLiquidity -> so that moving the activeTrading price via a swap is cheaper\n * 2. swapToDesiredPosition -> move active trading price in the pool to be able to deposit WETH & OETHb\n * tokens with the desired pre-configured shares\n * 3. addLiquidity -> add liquidity into the pool respecting share split configuration\n *\n * Scenario 1: When there is no liquidity in the pool from the strategy but there is from other LPs then\n * only step 1 is skipped. (It is important to note that liquidity needs to exist in the configured\n * strategy tick ranges in order for the swap to be possible) Step 3 mints new liquidity position\n * instead of adding to an existing one.\n * Scenario 2: When there is strategy's liquidity in the pool all 3 steps are taken\n *\n *\n * Exact _amountToSwap, _swapWeth & _minTokenReceived parameters shall be determined by simulating the\n * transaction off-chain. The strategy checks that after the swap the share of the tokens is in the\n * expected ranges.\n *\n * @param _amountToSwap The amount of the token to swap\n * @param _swapWeth Swap using WETH when true, use OETHb when false\n * @param _minTokenReceived Slippage check -> minimum amount of token expected in return\n */\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) external nonReentrant onlyGovernorOrStrategist {\n _rebalance(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n\n function _rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n /**\n * Would be nice to check if there is any total liquidity in the pool before performing this swap\n * but there is no easy way to do that in UniswapV3:\n * - clPool.liquidity() -> only liquidity in the active tick\n * - asset[1&2].balanceOf(address(clPool)) -> will include uncollected tokens of LP providers\n * after their liquidity position has been decreased\n */\n\n /**\n * When rebalance is called for the first time there is no strategy\n * liquidity in the pool yet. The liquidity removal is thus skipped.\n * Also execute this function when WETH is required for the swap.\n */\n if (tokenId != 0 && _swapWeth && _amountToSwap > 0) {\n _ensureWETHBalance(_amountToSwap);\n }\n\n // in some cases we will just want to add liquidity and not issue a swap to move the\n // active trading position within the pool\n if (_amountToSwap > 0) {\n _swapToDesiredPosition(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n // calling check liquidity early so we don't get unexpected errors when adding liquidity\n // in the later stages of this function\n _checkForExpectedPoolPrice(true);\n\n _addLiquidity();\n\n // this call shouldn't be necessary, since adding liquidity shouldn't affect the active\n // trading price. It is a defensive programming measure.\n (, uint256 _wethSharePct) = _checkForExpectedPoolPrice(true);\n\n // revert if protocol insolvent\n _solvencyAssert();\n\n emit PoolRebalanced(_wethSharePct);\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethbSupply = IERC20(OETHb).totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethbSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /**\n * @dev Decrease partial or all liquidity from the pool.\n * @param _liquidityToDecrease The amount of liquidity to remove expressed in 18 decimal point\n */\n function _removeLiquidity(uint256 _liquidityToDecrease)\n internal\n gaugeUnstakeAndRestake\n {\n require(_liquidityToDecrease > 0, \"Must remove some liquidity\");\n\n uint128 _liquidity = _getLiquidity();\n // need to convert to uint256 since intermittent result is to big for uint128 to handle\n uint128 _liquidityToRemove = uint256(_liquidity)\n .mulTruncate(_liquidityToDecrease)\n .toUint128();\n\n /**\n * There is no liquidity to remove -> exit function early. This can happen after a\n * withdraw/withdrawAll removes all of the liquidity while retaining the NFT token.\n */\n if (_liquidity == 0 || _liquidityToRemove == 0) {\n return;\n }\n\n (uint256 _amountWeth, uint256 _amountOethb) = positionManager\n .decreaseLiquidity(\n // Both expected amounts can be 0 since we don't really care if any swaps\n // happen just before the liquidity removal.\n INonfungiblePositionManager.DecreaseLiquidityParams({\n tokenId: tokenId,\n liquidity: _liquidityToRemove,\n amount0Min: 0,\n amount1Min: 0,\n deadline: block.timestamp\n })\n );\n\n (\n uint256 _amountWethCollected,\n uint256 _amountOethbCollected\n ) = positionManager.collect(\n INonfungiblePositionManager.CollectParams({\n tokenId: tokenId,\n recipient: address(this),\n amount0Max: type(uint128).max, // defaults to all tokens owed\n amount1Max: type(uint128).max // defaults to all tokens owed\n })\n );\n\n _updateUnderlyingAssets();\n\n emit LiquidityRemoved(\n _liquidityToDecrease,\n _amountWeth, //removedWethAmount\n _amountOethb, //removedOethbAmount\n _amountWethCollected,\n _amountOethbCollected,\n underlyingAssets\n );\n\n _burnOethbOnTheContract();\n }\n\n /**\n * @dev Perform a swap so that after the swap the ticker has the desired WETH to OETHb token share.\n */\n function _swapToDesiredPosition(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n IERC20 _tokenToSwap = IERC20(_swapWeth ? WETH : OETHb);\n uint256 _balance = _tokenToSwap.balanceOf(address(this));\n\n if (_balance < _amountToSwap) {\n // This should never trigger since _ensureWETHBalance will already\n // throw an error if there is not enough WETH\n if (_swapWeth) {\n revert NotEnoughWethForSwap(_balance, _amountToSwap);\n }\n // if swapping OETHb\n uint256 mintForSwap = _amountToSwap - _balance;\n IVault(vaultAddress).mintForStrategy(mintForSwap);\n }\n\n // approve the specific amount of WETH required\n if (_swapWeth) {\n IERC20(WETH).approve(address(swapRouter), _amountToSwap);\n }\n\n // Swap it\n swapRouter.exactInputSingle(\n // sqrtPriceLimitX96 is just a rough sanity check that we are within 0 -> 1 tick\n // a more fine check is performed in _checkForExpectedPoolPrice\n // Note: this needs further work if we want to generalize this approach\n ISwapRouter.ExactInputSingleParams({\n tokenIn: address(_tokenToSwap),\n tokenOut: _swapWeth ? OETHb : WETH,\n tickSpacing: tickSpacing, // set to 1\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: _amountToSwap,\n amountOutMinimum: _minTokenReceived, // slippage check\n sqrtPriceLimitX96: _swapWeth\n ? sqrtRatioX96TickLower\n : sqrtRatioX96TickHigher\n })\n );\n\n /**\n * In the interest of each function in _rebalance to leave the contract state as\n * clean as possible the OETHb tokens here are burned. This decreases the\n * dependence where `_swapToDesiredPosition` function relies on later functions\n * (`addLiquidity`) to burn the OETHb. Reducing the risk of error introduction.\n */\n _burnOethbOnTheContract();\n }\n\n /**\n * @dev Add liquidity into the pool in the pre-configured WETH to OETHb share ratios\n * defined by the allowedPoolWethShareStart|End interval. This function will respect\n * liquidity ratios when there is no liquidity yet in the pool. If liquidity is already\n * present then it relies on the `_swapToDesiredPosition` function in a step before\n * to already move the trading price to desired position (with some tolerance).\n */\n // rebalance already has re-entrency checks\n // slither-disable-start reentrancy-no-eth\n function _addLiquidity() internal gaugeUnstakeAndRestake {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n // don't deposit small liquidity amounts\n if (_wethBalance <= 1e12) {\n return;\n }\n\n uint160 _currentPrice = getPoolX96Price();\n /**\n * Sanity check active trading price is positioned within our desired tick.\n *\n * We revert when price is equal to the lower tick even though that is still\n * a valid amount in regards to ticker position by Sugar.estimateAmount call.\n * Current price equaling tick bound at the 1:1 price parity results in\n * uint overfow when calculating the OETHb balance to deposit.\n */\n if (\n _currentPrice <= sqrtRatioX96TickLower ||\n _currentPrice >= sqrtRatioX96TickHigher\n ) {\n revert OutsideExpectedTickRange(getCurrentTradingTick());\n }\n\n /**\n * If estimateAmount1 call fails it could be due to _currentPrice being really\n * close to a tick and amount1 is a larger number than the sugar helper is able\n * to compute.\n *\n * If token addresses were reversed estimateAmount0 would be required here\n */\n uint256 _oethbRequired = helper.estimateAmount1(\n _wethBalance,\n address(0), // no need to pass pool address when current price is specified\n _currentPrice,\n lowerTick,\n upperTick\n );\n\n if (_oethbRequired > _oethbBalance) {\n IVault(vaultAddress).mintForStrategy(\n _oethbRequired - _oethbBalance\n );\n }\n\n // approve the specific amount of WETH required\n IERC20(WETH).approve(address(positionManager), _wethBalance);\n\n uint256 _wethAmountSupplied;\n uint256 _oethbAmountSupplied;\n if (tokenId == 0) {\n (\n tokenId,\n ,\n _wethAmountSupplied,\n _oethbAmountSupplied\n ) = positionManager.mint(\n /** amount0Min & amount1Min are left at 0 because slippage protection is ensured by the\n * _checkForExpectedPoolPrice\n *›\n * Also sqrtPriceX96 is 0 because the pool is already created\n * non zero amount attempts to create a new instance of the pool\n */\n INonfungiblePositionManager.MintParams({\n token0: WETH,\n token1: OETHb,\n tickSpacing: tickSpacing,\n tickLower: lowerTick,\n tickUpper: upperTick,\n amount0Desired: _wethBalance,\n amount1Desired: _oethbRequired,\n amount0Min: 0,\n amount1Min: 0,\n recipient: address(this),\n deadline: block.timestamp,\n sqrtPriceX96: 0\n })\n );\n } else {\n (, _wethAmountSupplied, _oethbAmountSupplied) = positionManager\n .increaseLiquidity(\n /** amount0Min & amount1Min are left at 0 because slippage protection is ensured by the\n * _checkForExpectedPoolPrice\n */\n INonfungiblePositionManager.IncreaseLiquidityParams({\n tokenId: tokenId,\n amount0Desired: _wethBalance,\n amount1Desired: _oethbRequired,\n amount0Min: 0,\n amount1Min: 0,\n deadline: block.timestamp\n })\n );\n }\n\n _updateUnderlyingAssets();\n emit LiquidityAdded(\n _wethBalance, // wethAmountDesired\n _oethbRequired, // oethbAmountDesired\n _wethAmountSupplied, // wethAmountSupplied\n _oethbAmountSupplied, // oethbAmountSupplied\n tokenId, // tokenId\n underlyingAssets\n );\n\n // burn remaining OETHb\n _burnOethbOnTheContract();\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @dev Check that the Aerodrome pool price is within the expected\n * parameters.\n * This function works whether the strategy contract has liquidity\n * position in the pool or not. The function returns _wethSharePct\n * as a gas optimization measure.\n * @param throwException when set to true the function throws an exception\n * when pool's price is not within expected range.\n * @return _isExpectedRange Bool expressing price is within expected range\n * @return _wethSharePct Share of WETH owned by this strategy contract in the\n * configured ticker.\n */\n function _checkForExpectedPoolPrice(bool throwException)\n internal\n view\n returns (bool _isExpectedRange, uint256 _wethSharePct)\n {\n require(\n allowedWethShareStart != 0 && allowedWethShareEnd != 0,\n \"Weth share interval not set\"\n );\n\n uint160 _currentPrice = getPoolX96Price();\n\n /**\n * First check we are in expected tick range\n *\n * We revert even though price being equal to the lower tick would still\n * count being within lower tick for the purpose of Sugar.estimateAmount calls\n */\n if (\n _currentPrice <= sqrtRatioX96TickLower ||\n _currentPrice >= sqrtRatioX96TickHigher\n ) {\n if (throwException) {\n revert OutsideExpectedTickRange(getCurrentTradingTick());\n }\n return (false, 0);\n }\n\n // 18 decimal number expressed WETH tick share\n _wethSharePct = _getWethShare(_currentPrice);\n\n if (\n _wethSharePct < allowedWethShareStart ||\n _wethSharePct > allowedWethShareEnd\n ) {\n if (throwException) {\n revert PoolRebalanceOutOfBounds(\n _wethSharePct,\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n return (false, _wethSharePct);\n }\n\n return (true, _wethSharePct);\n }\n\n /**\n * Burns any OETHb tokens remaining on the strategy contract\n */\n function _burnOethbOnTheContract() internal {\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n if (_oethbBalance > 1e12) {\n IVault(vaultAddress).burnForStrategy(_oethbBalance);\n }\n }\n\n /// @dev This function assumes there are no uncollected tokens in the clPool owned by the strategy contract.\n /// For that reason any liquidity withdrawals must also collect the tokens.\n function _updateUnderlyingAssets() internal {\n if (tokenId == 0) {\n underlyingAssets = 0;\n emit UnderlyingAssetsUpdated(underlyingAssets);\n return;\n }\n\n uint128 _liquidity = _getLiquidity();\n\n /**\n * Our net value represent the smallest amount of tokens we are able to extract from the position\n * given our liquidity.\n *\n * The least amount of tokens extraditable from the position is where the active trading price is\n * at the ticker 0 meaning the pool is offering 1:1 trades between WETH & OETHb. At that moment the pool\n * consists completely of OETHb and no WETH.\n *\n * The more swaps from WETH -> OETHb happen on the pool the more the price starts to move towards the -1\n * ticker making OETHb (priced in WETH) more expensive.\n *\n * An additional note: when liquidity is 0 then the helper returns 0 for both token amounts. And the\n * function set underlying assets to 0.\n */\n (uint256 _wethAmount, uint256 _oethbAmount) = helper\n .getAmountsForLiquidity(\n sqrtRatioX96TickClosestToParity, // sqrtRatioX96\n sqrtRatioX96TickLower, // sqrtRatioAX96\n sqrtRatioX96TickHigher, // sqrtRatioBX96\n _liquidity\n );\n\n require(_wethAmount == 0, \"Non zero wethAmount\");\n underlyingAssets = _oethbAmount;\n emit UnderlyingAssetsUpdated(underlyingAssets);\n }\n\n /**\n * @dev This function removes the appropriate amount of liquidity to assure that the required\n * amount of WETH is available on the contract\n *\n * @param _amount WETH balance required on the contract\n */\n function _ensureWETHBalance(uint256 _amount) internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance >= _amount) {\n return;\n }\n\n require(tokenId != 0, \"No liquidity available\");\n uint256 _additionalWethRequired = _amount - _wethBalance;\n (uint256 _wethInThePool, ) = getPositionPrincipal();\n\n if (_wethInThePool < _additionalWethRequired) {\n revert NotEnoughWethLiquidity(\n _wethInThePool,\n _additionalWethRequired\n );\n }\n\n uint256 shareOfWethToRemove = Math.min(\n _additionalWethRequired.divPrecisely(_wethInThePool) + 1,\n 1e18\n );\n _removeLiquidity(shareOfWethToRemove);\n }\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset WETH address\n * @param _amount Amount of WETH to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n _ensureWETHBalance(_amount);\n\n _withdraw(_recipient, _amount);\n }\n\n /**\n * @notice Withdraw WETH and sends it to the Vault.\n */\n function withdrawAll() external override onlyVault nonReentrant {\n if (tokenId != 0) {\n _removeLiquidity(1e18);\n }\n\n uint256 _balance = IERC20(WETH).balanceOf(address(this));\n if (_balance > 0) {\n _withdraw(vaultAddress, _balance);\n }\n }\n\n function _withdraw(address _recipient, uint256 _amount) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n IERC20(WETH).safeTransfer(_recipient, _amount);\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /**\n * @dev Collect the AERO token from the gauge\n */\n function _collectRewardTokens() internal override {\n if (tokenId != 0 && _isLpTokenStakedInGauge()) {\n clGauge.getReward(tokenId);\n }\n super._collectRewardTokens();\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /**\n * @dev Approve the spending of all assets\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n // to add liquidity to the clPool\n IERC20(OETHb).approve(address(positionManager), type(uint256).max);\n // to be able to rebalance using the swapRouter\n IERC20(OETHb).approve(address(swapRouter), type(uint256).max);\n\n /* the behaviour of this strategy has slightly changed and WETH could be\n * present on the contract between the transactions. For that reason we are\n * un-approving WETH to the swapRouter & positionManager and only approving\n * the required amount before a transaction\n */\n IERC20(WETH).approve(address(swapRouter), 0);\n IERC20(WETH).approve(address(positionManager), 0);\n }\n\n /***************************************\n Balances and Fees\n ****************************************/\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256)\n {\n require(_asset == WETH, \"Only WETH supported\");\n\n // we could in theory deposit to the strategy and forget to call rebalance in the same\n // governance transaction batch. In that case the WETH that is on the strategy contract\n // also needs to be accounted for.\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n // just paranoia check, in case there is OETHb in the strategy that for some reason hasn't\n // been burned yet.\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n return underlyingAssets + _wethBalance + _oethbBalance;\n }\n\n /**\n * @dev Returns the balance of both tokens in a given position (excluding fees)\n * @return _amountWeth Amount of WETH in position\n * @return _amountOethb Amount of OETHb in position\n */\n function getPositionPrincipal()\n public\n view\n returns (uint256 _amountWeth, uint256 _amountOethb)\n {\n if (tokenId == 0) {\n return (0, 0);\n }\n\n uint160 _sqrtRatioX96 = getPoolX96Price();\n (_amountWeth, _amountOethb) = helper.principal(\n positionManager,\n tokenId,\n _sqrtRatioX96\n );\n }\n\n /**\n * @notice Returns the current pool price in X96 format\n * @return _sqrtRatioX96 Pool price\n */\n function getPoolX96Price() public view returns (uint160 _sqrtRatioX96) {\n (_sqrtRatioX96, , , , , ) = clPool.slot0();\n }\n\n /**\n * @notice Returns the current active trading tick of the underlying pool\n * @return _currentTick Current pool trading tick\n */\n function getCurrentTradingTick() public view returns (int24 _currentTick) {\n (, _currentTick, , , , ) = clPool.slot0();\n }\n\n /**\n * @notice Returns the percentage of WETH liquidity in the configured ticker\n * owned by this strategy contract.\n * @return uint256 1e18 denominated percentage expressing the share\n */\n function getWETHShare() external view returns (uint256) {\n uint160 _currentPrice = getPoolX96Price();\n return _getWethShare(_currentPrice);\n }\n\n /**\n * @notice Returns the amount of liquidity in the contract's LP position\n * @return _liquidity Amount of liquidity in the position\n */\n function _getLiquidity() internal view returns (uint128 _liquidity) {\n if (tokenId == 0) {\n revert(\"No LP position\");\n }\n\n (, , , , , , , _liquidity, , , , ) = positionManager.positions(tokenId);\n }\n\n function _getWethShare(uint160 _currentPrice)\n internal\n view\n returns (uint256)\n {\n /**\n * If estimateAmount1 call fails it could be due to _currentPrice being really\n * close to a tick and amount1 too big to compute.\n *\n * If token addresses were reversed estimateAmount0 would be required here\n */\n uint256 _normalizedWethAmount = 1 ether;\n uint256 _correspondingOethAmount = helper.estimateAmount1(\n _normalizedWethAmount,\n address(0), // no need to pass pool address when current price is specified\n _currentPrice,\n lowerTick,\n upperTick\n );\n\n // 18 decimal number expressed weth tick share\n return\n _normalizedWethAmount.divPrecisely(\n _normalizedWethAmount + _correspondingOethAmount\n );\n }\n\n /***************************************\n Hidden functions\n ****************************************/\n /// @inheritdoc InitializableAbstractStrategy\n function setPTokenAddress(address, address) external override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /// @inheritdoc InitializableAbstractStrategy\n function removePToken(uint256) external override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Not supported\n */\n function _abstractSetPToken(address, address) internal override {\n // the deployer shall call safeApproveAllTokens() to set necessary approvals\n revert(\"Unsupported method\");\n }\n\n /***************************************\n ERC721 management\n ****************************************/\n\n /// @notice Callback function for whenever a NFT is transferred to this contract\n // solhint-disable-next-line max-line-length\n /// Ref: https://docs.openzeppelin.com/contracts/3.x/api/token/erc721#IERC721Receiver-onERC721Received-address-address-uint256-bytes-\n function onERC721Received(\n address,\n address,\n uint256,\n bytes calldata\n ) external returns (bytes4) {\n return this.onERC721Received.selector;\n }\n}\n" + }, + "contracts/strategies/balancer/AbstractAuraStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Base Balancer Abstract Strategy\n * @author Origin Protocol Inc\n */\n\nimport { AbstractBalancerStrategy } from \"./AbstractBalancerStrategy.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IERC4626 } from \"../../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport { IRewardStaking } from \"../IRewardStaking.sol\";\n\nabstract contract AbstractAuraStrategy is AbstractBalancerStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n /// @notice Address of the Aura rewards pool\n address public immutable auraRewardPoolAddress;\n\n // renamed from __reserved to not shadow AbstractBalancerStrategy.__reserved,\n int256[50] private __reserved_baseAuraStrategy;\n\n constructor(address _auraRewardPoolAddress) {\n auraRewardPoolAddress = _auraRewardPoolAddress;\n }\n\n /**\n * @dev Deposit all Balancer Pool Tokens (BPT) in this strategy contract\n * to the Aura rewards pool.\n */\n function _lpDepositAll() internal virtual override {\n uint256 bptBalance = IERC20(platformAddress).balanceOf(address(this));\n uint256 auraLp = IERC4626(auraRewardPoolAddress).deposit(\n bptBalance,\n address(this)\n );\n require(bptBalance == auraLp, \"Aura LP != BPT\");\n }\n\n /**\n * @dev Withdraw `numBPTTokens` Balancer Pool Tokens (BPT) from\n * the Aura rewards pool to this strategy contract.\n * @param numBPTTokens Number of Balancer Pool Tokens (BPT) to withdraw\n */\n function _lpWithdraw(uint256 numBPTTokens) internal virtual override {\n IRewardStaking(auraRewardPoolAddress).withdrawAndUnwrap(\n numBPTTokens,\n true // also claim reward tokens\n );\n }\n\n /**\n * @dev Withdraw all Balancer Pool Tokens (BPT) from\n * the Aura rewards pool to this strategy contract.\n */\n function _lpWithdrawAll() internal virtual override {\n // Get all the strategy's BPTs in Aura\n // maxRedeem is implemented as balanceOf(address) in Aura\n uint256 bptBalance = IERC4626(auraRewardPoolAddress).maxRedeem(\n address(this)\n );\n\n IRewardStaking(auraRewardPoolAddress).withdrawAndUnwrap(\n bptBalance,\n true // also claim reward tokens\n );\n }\n\n /**\n * @notice Collects BAL and AURA tokens from the rewards pool.\n */\n function collectRewardTokens()\n external\n virtual\n override\n onlyHarvester\n nonReentrant\n {\n /* Similar to Convex, calling this function collects both of the\n * accrued BAL and AURA tokens.\n */\n IRewardStaking(auraRewardPoolAddress).getReward();\n _collectRewardTokens();\n }\n\n /// @notice Balancer Pool Tokens (BPT) in the Balancer pool and the Aura rewards pool.\n function _getBalancerPoolTokens()\n internal\n view\n override\n returns (uint256 balancerPoolTokens)\n {\n balancerPoolTokens =\n IERC20(platformAddress).balanceOf(address(this)) +\n // maxRedeem is implemented as balanceOf(address) in Aura\n IERC4626(auraRewardPoolAddress).maxRedeem(address(this));\n }\n\n function _approveBase() internal virtual override {\n super._approveBase();\n\n IERC20 pToken = IERC20(platformAddress);\n pToken.safeApprove(auraRewardPoolAddress, type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/balancer/AbstractBalancerStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Balancer Abstract Strategy\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\nimport { IRateProvider } from \"../../interfaces/balancer/IRateProvider.sol\";\nimport { VaultReentrancyLib } from \"./VaultReentrancyLib.sol\";\nimport { IOracle } from \"../../interfaces/IOracle.sol\";\nimport { IWstETH } from \"../../interfaces/IWstETH.sol\";\nimport { IERC4626 } from \"../../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nabstract contract AbstractBalancerStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n address public immutable rETH;\n address public immutable stETH;\n address public immutable wstETH;\n address public immutable frxETH;\n address public immutable sfrxETH;\n\n /// @notice Address of the Balancer vault\n IBalancerVault public immutable balancerVault;\n /// @notice Balancer pool identifier\n bytes32 public immutable balancerPoolId;\n\n // Max withdrawal deviation denominated in 1e18 (1e18 == 100%)\n uint256 public maxWithdrawalDeviation;\n // Max deposit deviation denominated in 1e18 (1e18 == 100%)\n uint256 public maxDepositDeviation;\n\n int256[48] private __reserved;\n\n struct BaseBalancerConfig {\n address rEthAddress; // Address of the rETH token\n address stEthAddress; // Address of the stETH token\n address wstEthAddress; // Address of the wstETH token\n address frxEthAddress; // Address of the frxEth token\n address sfrxEthAddress; // Address of the sfrxEth token\n address balancerVaultAddress; // Address of the Balancer vault\n bytes32 balancerPoolId; // Balancer pool identifier\n }\n\n event MaxWithdrawalDeviationUpdated(\n uint256 _prevMaxDeviationPercentage,\n uint256 _newMaxDeviationPercentage\n );\n event MaxDepositDeviationUpdated(\n uint256 _prevMaxDeviationPercentage,\n uint256 _newMaxDeviationPercentage\n );\n\n /**\n * @dev Ensure we are not in a Vault context when this function is called, by attempting a no-op internal\n * balance operation. If we are already in a Vault transaction (e.g., a swap, join, or exit), the Vault's\n * reentrancy protection will cause this function to revert.\n *\n * Use this modifier with any function that can cause a state change in a pool and is either public itself,\n * or called by a public function *outside* a Vault operation (e.g., join, exit, or swap).\n *\n * This is to protect against Balancer's read-only re-entrancy vulnerability:\n * https://www.notion.so/originprotocol/Balancer-read-only-reentrancy-c686e72c82414ef18fa34312bb02e11b\n */\n modifier whenNotInBalancerVaultContext() {\n VaultReentrancyLib.ensureNotInVaultContext(balancerVault);\n _;\n }\n\n constructor(BaseBalancerConfig memory _balancerConfig) {\n rETH = _balancerConfig.rEthAddress;\n stETH = _balancerConfig.stEthAddress;\n wstETH = _balancerConfig.wstEthAddress;\n frxETH = _balancerConfig.frxEthAddress;\n sfrxETH = _balancerConfig.sfrxEthAddress;\n\n balancerVault = IBalancerVault(_balancerConfig.balancerVaultAddress);\n balancerPoolId = _balancerConfig.balancerPoolId;\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Balancer's strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of BAL & AURA\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * WETH, stETH\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // BAL & AURA\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n maxWithdrawalDeviation = 1e16;\n maxDepositDeviation = 1e16;\n\n emit MaxWithdrawalDeviationUpdated(0, maxWithdrawalDeviation);\n emit MaxDepositDeviationUpdated(0, maxDepositDeviation);\n\n IERC20[] memory poolAssets = _getPoolAssets();\n require(\n poolAssets.length == _assets.length,\n \"Pool assets length mismatch\"\n );\n for (uint256 i = 0; i < _assets.length; ++i) {\n address asset = _fromPoolAsset(address(poolAssets[i]));\n require(_assets[i] == asset, \"Pool assets mismatch\");\n }\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @notice Get strategy's share of an assets in the Balancer pool.\n * This is not denominated in OUSD/ETH value of the assets in the Balancer pool.\n * @param _asset Address of the Vault collateral asset\n * @return amount the amount of vault collateral assets\n *\n * IMPORTANT if this function is overridden it needs to have a whenNotInBalancerVaultContext\n * modifier on it or it is susceptible to read-only re-entrancy attack\n *\n * @dev it is important that this function is not affected by reporting inflated\n * values of assets in case of any pool manipulation. Such a manipulation could easily\n * exploit the protocol by:\n * - minting OETH\n * - tilting Balancer pool to report higher balances of assets\n * - rebasing() -> all that extra token balances get distributed to OETH holders\n * - tilting pool back\n * - redeeming OETH\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n whenNotInBalancerVaultContext\n returns (uint256 amount)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n\n uint256 bptBalance = _getBalancerPoolTokens();\n\n /* To calculate the worth of queried asset:\n * - assume that all tokens normalized to their ETH value have an equal split balance\n * in the pool when it is balanced\n * - multiply the BPT amount with the bpt rate to get the ETH denominated amount\n * of strategy's holdings\n * - divide that by the number of tokens we support in the pool to get ETH denominated\n * amount that is applicable to each supported token in the pool.\n *\n * It would be possible to support only 1 asset in the pool (and be exposed to all\n * the assets while holding BPT tokens) and deposit/withdraw/checkBalance using only\n * that asset. TBD: changes to other functions still required if we ever decide to\n * go with such configuration.\n */\n amount = (bptBalance.mulTruncate(\n IRateProvider(platformAddress).getRate()\n ) / assetsMapped.length);\n\n /* If the pool asset is equal to (strategy )_asset it means that a rate\n * provider for that asset exists and that asset is not necessarily\n * pegged to a unit (ETH).\n *\n * Because this function returns the balance of the asset and is not denominated in\n * ETH units we need to convert the ETH denominated amount to asset amount.\n */\n if (_toPoolAsset(_asset) == _asset) {\n amount = amount.divPrecisely(_getRateProviderRate(_asset));\n }\n }\n\n /**\n * @notice Returns the value of all assets managed by this strategy.\n * Uses the Balancer pool's rate (virtual price) to convert the strategy's\n * Balancer Pool Tokens (BPT) to ETH value.\n * @return value The ETH value\n *\n * IMPORTANT if this function is overridden it needs to have a whenNotInBalancerVaultContext\n * modifier on it or it is susceptible to read-only re-entrancy attack\n */\n function checkBalance()\n external\n view\n virtual\n whenNotInBalancerVaultContext\n returns (uint256 value)\n {\n uint256 bptBalance = _getBalancerPoolTokens();\n\n // Convert BPT to ETH value\n value = bptBalance.mulTruncate(\n IRateProvider(platformAddress).getRate()\n );\n }\n\n /// @notice Balancer Pool Tokens (BPT) in the Balancer pool.\n function _getBalancerPoolTokens()\n internal\n view\n virtual\n returns (uint256 balancerPoolTokens)\n {\n balancerPoolTokens = IERC20(platformAddress).balanceOf(address(this));\n }\n\n /* solhint-disable max-line-length */\n /**\n * @notice BPT price is calculated by taking the rate from the rateProvider of the asset in\n * question. If one does not exist it defaults to 1e18. To get the final BPT expected that\n * is multiplied by the underlying asset amount divided by BPT token rate. BPT token rate is\n * similar to Curve's virtual_price and expresses how much has the price of BPT appreciated\n * (e.g. due to swap fees) in relation to the underlying assets\n *\n * Using the above approach makes the strategy vulnerable to a possible MEV attack using\n * flash loan to manipulate the pool before a deposit/withdrawal since the function ignores\n * market values of the assets being priced in BPT.\n *\n * At the time of writing there is no safe on-chain approach to pricing BPT in a way that it\n * would make it invulnerable to MEV pool manipulation. See recent Balancer exploit:\n * https://www.notion.so/originprotocol/Balancer-OETH-strategy-9becdea132704e588782a919d7d471eb?pvs=4#1cf07de12fc64f1888072321e0644348\n *\n * To mitigate MEV possibilities during deposits and withdraws, the VaultValueChecker will use checkBalance before and after the move\n * to ensure the expected changes took place.\n *\n * @param _asset Address of the Balancer pool asset\n * @param _amount Amount of the Balancer pool asset\n * @return bptExpected of BPT expected in exchange for the asset\n *\n * @dev\n * bptAssetPrice = 1e18 (asset peg) * pool_asset_rate\n *\n * bptExpected = bptAssetPrice * asset_amount / BPT_token_rate\n *\n * bptExpected = 1e18 (asset peg) * pool_asset_rate * asset_amount / BPT_token_rate\n * bptExpected = asset_amount * pool_asset_rate / BPT_token_rate\n *\n * further information available here:\n * https://www.notion.so/originprotocol/Balancer-OETH-strategy-9becdea132704e588782a919d7d471eb?pvs=4#ce01495ae70346d8971f5dced809fb83\n */\n /* solhint-enable max-line-length */\n function _getBPTExpected(address _asset, uint256 _amount)\n internal\n view\n virtual\n returns (uint256 bptExpected)\n {\n uint256 bptRate = IRateProvider(platformAddress).getRate();\n uint256 poolAssetRate = _getRateProviderRate(_asset);\n bptExpected = _amount.mulTruncate(poolAssetRate).divPrecisely(bptRate);\n }\n\n function _getBPTExpected(\n address[] memory _assets,\n uint256[] memory _amounts\n ) internal view virtual returns (uint256 bptExpected) {\n require(_assets.length == _amounts.length, \"Assets & amounts mismatch\");\n\n for (uint256 i = 0; i < _assets.length; ++i) {\n uint256 poolAssetRate = _getRateProviderRate(_assets[i]);\n // convert asset amount to ETH amount\n bptExpected += _amounts[i].mulTruncate(poolAssetRate);\n }\n\n uint256 bptRate = IRateProvider(platformAddress).getRate();\n // Convert ETH amount to BPT amount\n bptExpected = bptExpected.divPrecisely(bptRate);\n }\n\n function _lpDepositAll() internal virtual;\n\n function _lpWithdraw(uint256 numBPTTokens) internal virtual;\n\n function _lpWithdrawAll() internal virtual;\n\n /**\n * @notice Balancer returns assets and rateProviders for corresponding assets ordered\n * by numerical order.\n */\n function _getPoolAssets() internal view returns (IERC20[] memory assets) {\n // slither-disable-next-line unused-return\n (assets, , ) = balancerVault.getPoolTokens(balancerPoolId);\n }\n\n /**\n * @dev If an asset is rebasing the Balancer pools have a wrapped versions of assets\n * that the strategy supports. This function converts the pool(wrapped) asset\n * and corresponding amount to strategy asset.\n */\n function _toPoolAsset(address asset, uint256 amount)\n internal\n view\n returns (address poolAsset, uint256 poolAmount)\n {\n if (asset == stETH) {\n poolAsset = wstETH;\n if (amount > 0) {\n poolAmount = IWstETH(wstETH).getWstETHByStETH(amount);\n }\n } else if (asset == frxETH) {\n poolAsset = sfrxETH;\n if (amount > 0) {\n poolAmount = IERC4626(sfrxETH).convertToShares(amount);\n }\n } else {\n poolAsset = asset;\n poolAmount = amount;\n }\n }\n\n /**\n * @dev Converts a Vault collateral asset to a Balancer pool asset.\n * stETH becomes wstETH, frxETH becomes sfrxETH and everything else stays the same.\n * @param asset Address of the Vault collateral asset.\n * @return Address of the Balancer pool asset.\n */\n function _toPoolAsset(address asset) internal view returns (address) {\n if (asset == stETH) {\n return wstETH;\n } else if (asset == frxETH) {\n return sfrxETH;\n }\n return asset;\n }\n\n /**\n * @dev Converts rebasing asset to its wrapped counterpart.\n */\n function _wrapPoolAsset(address asset, uint256 amount)\n internal\n returns (address wrappedAsset, uint256 wrappedAmount)\n {\n if (asset == stETH) {\n wrappedAsset = wstETH;\n if (amount > 0) {\n wrappedAmount = IWstETH(wstETH).wrap(amount);\n }\n } else if (asset == frxETH) {\n wrappedAsset = sfrxETH;\n if (amount > 0) {\n wrappedAmount = IERC4626(sfrxETH).deposit(\n amount,\n address(this)\n );\n }\n } else {\n wrappedAsset = asset;\n wrappedAmount = amount;\n }\n }\n\n /**\n * @dev Converts wrapped asset to its rebasing counterpart.\n */\n function _unwrapPoolAsset(address asset, uint256 amount)\n internal\n returns (uint256 unwrappedAmount)\n {\n if (asset == stETH) {\n unwrappedAmount = IWstETH(wstETH).unwrap(amount);\n } else if (asset == frxETH) {\n unwrappedAmount = IERC4626(sfrxETH).withdraw(\n amount,\n address(this),\n address(this)\n );\n } else {\n unwrappedAmount = amount;\n }\n }\n\n /**\n * @dev If an asset is rebasing the Balancer pools have a wrapped versions of assets\n * that the strategy supports. This function converts the rebasing strategy asset\n * and corresponding amount to wrapped(pool) asset.\n */\n function _fromPoolAsset(address poolAsset, uint256 poolAmount)\n internal\n view\n returns (address asset, uint256 amount)\n {\n if (poolAsset == wstETH) {\n asset = stETH;\n if (poolAmount > 0) {\n amount = IWstETH(wstETH).getStETHByWstETH(poolAmount);\n }\n } else if (poolAsset == sfrxETH) {\n asset = frxETH;\n if (poolAmount > 0) {\n amount = IERC4626(sfrxETH).convertToAssets(poolAmount);\n }\n } else {\n asset = poolAsset;\n amount = poolAmount;\n }\n }\n\n function _fromPoolAsset(address poolAsset)\n internal\n view\n returns (address asset)\n {\n if (poolAsset == wstETH) {\n asset = stETH;\n } else if (poolAsset == sfrxETH) {\n asset = frxETH;\n } else {\n asset = poolAsset;\n }\n }\n\n /**\n * @notice Sets max withdrawal deviation that is considered when removing\n * liquidity from Balancer pools.\n * @param _maxWithdrawalDeviation Max withdrawal deviation denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxWithdrawalDeviation will be 1% (1e16) for production\n * usage. Vault value checker in combination with checkBalance will\n * catch any unexpected manipulation.\n */\n function setMaxWithdrawalDeviation(uint256 _maxWithdrawalDeviation)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(\n _maxWithdrawalDeviation <= 1e18,\n \"Withdrawal dev. out of bounds\"\n );\n emit MaxWithdrawalDeviationUpdated(\n maxWithdrawalDeviation,\n _maxWithdrawalDeviation\n );\n maxWithdrawalDeviation = _maxWithdrawalDeviation;\n }\n\n /**\n * @notice Sets max deposit deviation that is considered when adding\n * liquidity to Balancer pools.\n * @param _maxDepositDeviation Max deposit deviation denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxDepositDeviation will default to 1% (1e16)\n * for production usage. Vault value checker in combination with\n * checkBalance will catch any unexpected manipulation.\n */\n function setMaxDepositDeviation(uint256 _maxDepositDeviation)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(_maxDepositDeviation <= 1e18, \"Deposit dev. out of bounds\");\n emit MaxDepositDeviationUpdated(\n maxDepositDeviation,\n _maxDepositDeviation\n );\n maxDepositDeviation = _maxDepositDeviation;\n }\n\n function _approveBase() internal virtual {\n IERC20 pToken = IERC20(platformAddress);\n // Balancer vault for BPT token (required for removing liquidity)\n pToken.safeApprove(address(balancerVault), type(uint256).max);\n }\n\n function _getRateProviderRate(address _asset)\n internal\n view\n virtual\n returns (uint256);\n}\n" + }, + "contracts/strategies/balancer/BalancerMetaPoolStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Balancer MetaStablePool Strategy\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { AbstractAuraStrategy, AbstractBalancerStrategy } from \"./AbstractAuraStrategy.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\nimport { IRateProvider } from \"../../interfaces/balancer/IRateProvider.sol\";\nimport { IMetaStablePool } from \"../../interfaces/balancer/IMetaStablePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\ncontract BalancerMetaPoolStrategy is AbstractAuraStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(\n BaseStrategyConfig memory _stratConfig,\n BaseBalancerConfig memory _balancerConfig,\n address _auraRewardPoolAddress\n )\n InitializableAbstractStrategy(_stratConfig)\n AbstractBalancerStrategy(_balancerConfig)\n AbstractAuraStrategy(_auraRewardPoolAddress)\n {}\n\n /**\n * @notice There are no plans to configure BalancerMetaPool as a default\n * asset strategy. For that reason there is no need to support this\n * functionality.\n */\n function deposit(address, uint256)\n external\n override\n onlyVault\n nonReentrant\n {\n revert(\"Not supported\");\n }\n\n /**\n * @notice There are no plans to configure BalancerMetaPool as a default\n * asset strategy. For that reason there is no need to support this\n * functionality.\n */\n function deposit(address[] calldata, uint256[] calldata)\n external\n onlyVault\n nonReentrant\n {\n revert(\"Not supported\");\n }\n\n /**\n * @notice Deposits all supported assets in this strategy contract to the Balancer pool.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 assetsLength = assetsMapped.length;\n address[] memory strategyAssets = new address[](assetsLength);\n uint256[] memory strategyAmounts = new uint256[](assetsLength);\n\n // For each vault collateral asset\n for (uint256 i = 0; i < assetsLength; ++i) {\n strategyAssets[i] = assetsMapped[i];\n // Get the asset balance in this strategy contract\n strategyAmounts[i] = IERC20(strategyAssets[i]).balanceOf(\n address(this)\n );\n }\n _deposit(strategyAssets, strategyAmounts);\n }\n\n /*\n * _deposit doesn't require a read-only re-entrancy protection since during the deposit\n * the function enters the Balancer Vault Context. If this function were called as part of\n * the attacking contract (while intercepting execution flow upon receiving ETH) the read-only\n * protection of the Balancer Vault would be triggered. Since the attacking contract would\n * already be in the Balancer Vault context and wouldn't be able to enter it again.\n */\n function _deposit(\n address[] memory _strategyAssets,\n uint256[] memory _strategyAmounts\n ) internal {\n require(\n _strategyAssets.length == _strategyAmounts.length,\n \"Array length missmatch\"\n );\n\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n\n uint256[] memory strategyAssetAmountsToPoolAssetAmounts = new uint256[](\n _strategyAssets.length\n );\n address[] memory strategyAssetsToPoolAssets = new address[](\n _strategyAssets.length\n );\n\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n address strategyAsset = _strategyAssets[i];\n uint256 strategyAmount = _strategyAmounts[i];\n\n require(\n assetToPToken[strategyAsset] != address(0),\n \"Unsupported asset\"\n );\n strategyAssetsToPoolAssets[i] = _toPoolAsset(strategyAsset);\n\n if (strategyAmount > 0) {\n emit Deposit(strategyAsset, platformAddress, strategyAmount);\n\n // wrap rebasing assets like stETH and frxETH to wstETH and sfrxETH\n (, strategyAssetAmountsToPoolAssetAmounts[i]) = _wrapPoolAsset(\n strategyAsset,\n strategyAmount\n );\n }\n }\n\n uint256[] memory amountsIn = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n for (uint256 i = 0; i < tokens.length; ++i) {\n // Convert IERC20 type to address\n poolAssets[i] = address(tokens[i]);\n\n // For each of the mapped assets\n for (uint256 j = 0; j < strategyAssetsToPoolAssets.length; ++j) {\n // If the pool asset is the same as the mapped asset\n if (poolAssets[i] == strategyAssetsToPoolAssets[j]) {\n amountsIn[i] = strategyAssetAmountsToPoolAssetAmounts[j];\n }\n }\n }\n\n uint256 minBPT = _getBPTExpected(\n strategyAssetsToPoolAssets,\n strategyAssetAmountsToPoolAssetAmounts\n );\n uint256 minBPTwDeviation = minBPT.mulTruncate(\n 1e18 - maxDepositDeviation\n );\n\n /* EXACT_TOKENS_IN_FOR_BPT_OUT:\n * User sends precise quantities of tokens, and receives an\n * estimated but unknown (computed at run time) quantity of BPT.\n *\n * ['uint256', 'uint256[]', 'uint256']\n * [EXACT_TOKENS_IN_FOR_BPT_OUT, amountsIn, minimumBPT]\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,\n amountsIn,\n minBPTwDeviation\n );\n\n IBalancerVault.JoinPoolRequest memory request = IBalancerVault\n .JoinPoolRequest(poolAssets, amountsIn, userData, false);\n\n // Add the pool assets in this strategy to the balancer pool\n balancerVault.joinPool(\n balancerPoolId,\n address(this),\n address(this),\n request\n );\n\n // Deposit the Balancer Pool Tokens (BPT) into Aura\n _lpDepositAll();\n }\n\n /**\n * @notice Withdraw a Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAsset Address of the Vault collateral asset\n * @param _strategyAmount The amount of Vault collateral assets to withdraw\n */\n function withdraw(\n address _recipient,\n address _strategyAsset,\n uint256 _strategyAmount\n ) external override onlyVault nonReentrant {\n address[] memory strategyAssets = new address[](1);\n uint256[] memory strategyAmounts = new uint256[](1);\n strategyAssets[0] = _strategyAsset;\n strategyAmounts[0] = _strategyAmount;\n\n _withdraw(_recipient, strategyAssets, strategyAmounts);\n }\n\n /**\n * @notice Withdraw multiple Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAssets Addresses of the Vault collateral assets\n * @param _strategyAmounts The amounts of Vault collateral assets to withdraw\n */\n function withdraw(\n address _recipient,\n address[] calldata _strategyAssets,\n uint256[] calldata _strategyAmounts\n ) external onlyVault nonReentrant {\n _withdraw(_recipient, _strategyAssets, _strategyAmounts);\n }\n\n /**\n * @dev Withdraw multiple Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAssets Addresses of the Vault collateral assets\n * @param _strategyAmounts The amounts of Vault collateral assets to withdraw\n *\n * _withdrawal doesn't require a read-only re-entrancy protection since during the withdrawal\n * the function enters the Balancer Vault Context. If this function were called as part of\n * the attacking contract (while intercepting execution flow upon receiving ETH) the read-only\n * protection of the Balancer Vault would be triggered. Since the attacking contract would\n * already be in the Balancer Vault context and wouldn't be able to enter it again.\n */\n function _withdraw(\n address _recipient,\n address[] memory _strategyAssets,\n uint256[] memory _strategyAmounts\n ) internal {\n require(\n _strategyAssets.length == _strategyAmounts.length,\n \"Invalid input arrays\"\n );\n\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n require(\n assetToPToken[_strategyAssets[i]] != address(0),\n \"Unsupported asset\"\n );\n }\n\n // STEP 1 - Calculate the Balancer pool assets and amounts from the vault collateral assets\n\n // Get all the supported balancer pool assets\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n // Calculate the balancer pool assets and amounts to withdraw\n uint256[] memory poolAssetsAmountsOut = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n // Is the wrapped asset amount indexed by the assets array, not the order of the Balancer pool tokens\n // eg wstETH and sfrxETH amounts, not the stETH and frxETH amounts\n uint256[] memory strategyAssetsToPoolAssetsAmounts = new uint256[](\n _strategyAssets.length\n );\n\n // For each of the Balancer pool assets\n for (uint256 i = 0; i < tokens.length; ++i) {\n poolAssets[i] = address(tokens[i]);\n\n // Convert the Balancer pool asset back to a vault collateral asset\n address strategyAsset = _fromPoolAsset(poolAssets[i]);\n\n // for each of the vault assets\n for (uint256 j = 0; j < _strategyAssets.length; ++j) {\n // If the vault asset equals the vault asset mapped from the Balancer pool asset\n if (_strategyAssets[j] == strategyAsset) {\n (, poolAssetsAmountsOut[i]) = _toPoolAsset(\n strategyAsset,\n _strategyAmounts[j]\n );\n strategyAssetsToPoolAssetsAmounts[j] = poolAssetsAmountsOut[\n i\n ];\n\n /* Because of the potential Balancer rounding error mentioned below\n * the contract might receive 1-2 WEI smaller amount than required\n * in the withdraw user data encoding. If slightly lesser token amount\n * is received the strategy can not unwrap the pool asset as it is\n * smaller than expected.\n *\n * For that reason we `overshoot` the required tokens expected to\n * circumvent the error\n */\n if (poolAssetsAmountsOut[i] > 0) {\n poolAssetsAmountsOut[i] += 2;\n }\n }\n }\n }\n\n // STEP 2 - Calculate the max about of Balancer Pool Tokens (BPT) to withdraw\n\n // Estimate the required amount of Balancer Pool Tokens (BPT) for the assets\n uint256 maxBPTtoWithdraw = _getBPTExpected(\n poolAssets,\n /* all non 0 values are overshot by 2 WEI and with the expected mainnet\n * ~1% withdrawal deviation, the 2 WEI aren't important\n */\n poolAssetsAmountsOut\n );\n // Increase BPTs by the max allowed deviation\n // Any excess BPTs will be left in this strategy contract\n maxBPTtoWithdraw = maxBPTtoWithdraw.mulTruncate(\n 1e18 + maxWithdrawalDeviation\n );\n\n // STEP 3 - Withdraw the Balancer Pool Tokens (BPT) from Aura to this strategy contract\n\n // Withdraw BPT from Aura allowing for BPTs left in this strategy contract from previous withdrawals\n _lpWithdraw(\n maxBPTtoWithdraw - IERC20(platformAddress).balanceOf(address(this))\n );\n\n // STEP 4 - Withdraw the balancer pool assets from the pool\n\n /* Custom asset exit: BPT_IN_FOR_EXACT_TOKENS_OUT:\n * User sends an estimated but unknown (computed at run time) quantity of BPT,\n * and receives precise quantities of specified tokens.\n *\n * ['uint256', 'uint256[]', 'uint256']\n * [BPT_IN_FOR_EXACT_TOKENS_OUT, amountsOut, maxBPTAmountIn]\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.BPT_IN_FOR_EXACT_TOKENS_OUT,\n poolAssetsAmountsOut,\n maxBPTtoWithdraw\n );\n\n IBalancerVault.ExitPoolRequest memory request = IBalancerVault\n .ExitPoolRequest(\n poolAssets,\n /* We specify the exact amount of a tokens we are expecting in the encoded\n * userData, for that reason we don't need to specify the amountsOut here.\n *\n * Also Balancer has a rounding issue that can make a transaction fail:\n * https://github.com/balancer/balancer-v2-monorepo/issues/2541\n * which is an extra reason why this field is empty.\n */\n new uint256[](tokens.length),\n userData,\n false\n );\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n /* Payable keyword is required because of the IBalancerVault interface even though\n * this strategy shall never be receiving native ETH\n */\n payable(address(this)),\n request\n );\n\n // STEP 5 - Re-deposit any left over BPT tokens back into Aura\n /* When concluding how much of BPT we need to withdraw from Aura we overshoot by\n * roughly around 1% (initial mainnet setting of maxWithdrawalDeviation). After exiting\n * the pool strategy could have left over BPT tokens that are not earning boosted yield.\n * We re-deploy those back in.\n */\n _lpDepositAll();\n\n // STEP 6 - Unswap balancer pool assets to vault collateral assets and send to the vault.\n\n // For each of the specified assets\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n // Unwrap assets like wstETH and sfrxETH to rebasing assets stETH and frxETH\n if (strategyAssetsToPoolAssetsAmounts[i] > 0) {\n _unwrapPoolAsset(\n _strategyAssets[i],\n strategyAssetsToPoolAssetsAmounts[i]\n );\n }\n\n // Transfer the vault collateral assets to the recipient, which is typically the vault\n if (_strategyAmounts[i] > 0) {\n IERC20(_strategyAssets[i]).safeTransfer(\n _recipient,\n _strategyAmounts[i]\n );\n\n emit Withdrawal(\n _strategyAssets[i],\n platformAddress,\n _strategyAmounts[i]\n );\n }\n }\n }\n\n /**\n * @notice Withdraws all supported Vault collateral assets from the Balancer pool\n * and send to the OToken's Vault.\n *\n * Is only executable by the OToken's Vault or the Governor.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n // STEP 1 - Withdraw all Balancer Pool Tokens (BPT) from Aura to this strategy contract\n\n _lpWithdrawAll();\n // Get the BPTs withdrawn from Aura plus any that were already in this strategy contract\n uint256 BPTtoWithdraw = IERC20(platformAddress).balanceOf(\n address(this)\n );\n // Get the balancer pool assets and their total balances\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n uint256[] memory minAmountsOut = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n for (uint256 i = 0; i < tokens.length; ++i) {\n poolAssets[i] = address(tokens[i]);\n }\n\n // STEP 2 - Withdraw the Balancer pool assets from the pool\n /* Proportional exit: EXACT_BPT_IN_FOR_TOKENS_OUT:\n * User sends a precise quantity of BPT, and receives an estimated but unknown\n * (computed at run time) quantity of a single token\n *\n * ['uint256', 'uint256']\n * [EXACT_BPT_IN_FOR_TOKENS_OUT, bptAmountIn]\n *\n * It is ok to pass an empty minAmountsOut since tilting the pool in any direction\n * when doing a proportional exit can only be beneficial to the strategy. Since\n * it will receive more of the underlying tokens for the BPT traded in.\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.EXACT_BPT_IN_FOR_TOKENS_OUT,\n BPTtoWithdraw\n );\n\n IBalancerVault.ExitPoolRequest memory request = IBalancerVault\n .ExitPoolRequest(poolAssets, minAmountsOut, userData, false);\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n /* Payable keyword is required because of the IBalancerVault interface even though\n * this strategy shall never be receiving native ETH\n */\n payable(address(this)),\n request\n );\n\n // STEP 3 - Convert the balancer pool assets to the vault collateral assets and send to the vault\n // For each of the Balancer pool assets\n for (uint256 i = 0; i < tokens.length; ++i) {\n address poolAsset = address(tokens[i]);\n // Convert the balancer pool asset to the strategy asset\n address strategyAsset = _fromPoolAsset(poolAsset);\n // Get the balancer pool assets withdraw from the pool plus any that were already in this strategy contract\n uint256 poolAssetAmount = IERC20(poolAsset).balanceOf(\n address(this)\n );\n\n // Unwrap assets like wstETH and sfrxETH to rebasing assets stETH and frxETH\n uint256 unwrappedAmount = 0;\n if (poolAssetAmount > 0) {\n unwrappedAmount = _unwrapPoolAsset(\n strategyAsset,\n poolAssetAmount\n );\n }\n\n // Transfer the vault collateral assets to the vault\n if (unwrappedAmount > 0) {\n IERC20(strategyAsset).safeTransfer(\n vaultAddress,\n unwrappedAmount\n );\n emit Withdrawal(\n strategyAsset,\n platformAddress,\n unwrappedAmount\n );\n }\n }\n }\n\n /**\n * @notice Approves the Balancer Vault to transfer poolAsset counterparts\n * of all of the supported assets from this strategy. E.g. stETH is a supported\n * strategy and Balancer Vault gets unlimited approval to transfer wstETH.\n *\n * If Balancer pool uses a wrapped version of a supported asset then also approve\n * unlimited usage of an asset to the contract responsible for wrapping.\n *\n * Approve unlimited spending by Balancer Vault and Aura reward pool of the\n * pool BPT tokens.\n *\n * Is only executable by the Governor.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n _abstractSetPToken(assetsMapped[i], platformAddress);\n }\n _approveBase();\n }\n\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address) internal override {\n address poolAsset = _toPoolAsset(_asset);\n if (_asset == stETH) {\n // slither-disable-next-line unused-return\n IERC20(stETH).approve(wstETH, type(uint256).max);\n } else if (_asset == frxETH) {\n // slither-disable-next-line unused-return\n IERC20(frxETH).approve(sfrxETH, type(uint256).max);\n }\n _approveAsset(poolAsset);\n }\n\n /**\n * @dev Approves the Balancer Vault to transfer an asset from\n * this strategy. The assets could be a Vault collateral asset\n * like WETH or rETH; or a Balancer pool asset that wraps the vault asset\n * like wstETH or sfrxETH.\n */\n function _approveAsset(address _asset) internal {\n IERC20 asset = IERC20(_asset);\n // slither-disable-next-line unused-return\n asset.approve(address(balancerVault), type(uint256).max);\n }\n\n /**\n * @notice Returns the rate supplied by the Balancer configured rate\n * provider. Rate is used to normalize the token to common underlying\n * pool denominator. (ETH for ETH Liquid staking derivatives)\n *\n * @param _asset Address of the Balancer pool asset\n * @return rate of the corresponding asset\n */\n function _getRateProviderRate(address _asset)\n internal\n view\n override\n returns (uint256)\n {\n IMetaStablePool pool = IMetaStablePool(platformAddress);\n IRateProvider[] memory providers = pool.getRateProviders();\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n\n uint256 providersLength = providers.length;\n for (uint256 i = 0; i < providersLength; ++i) {\n // _assets and corresponding rate providers are all in the same order\n if (address(tokens[i]) == _asset) {\n // rate provider doesn't exist, defaults to 1e18\n if (address(providers[i]) == address(0)) {\n return 1e18;\n }\n return providers[i].getRate();\n }\n }\n\n // should never happen\n assert(false);\n }\n}\n" + }, + "contracts/strategies/balancer/VaultReentrancyLib.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity >=0.7.0 <0.9.0;\n\nimport \"../../utils/BalancerErrors.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\n\nlibrary VaultReentrancyLib {\n /**\n * @dev Ensure we are not in a Vault context when this function is called, by attempting a no-op internal\n * balance operation. If we are already in a Vault transaction (e.g., a swap, join, or exit), the Vault's\n * reentrancy protection will cause this function to revert.\n *\n * The exact function call doesn't really matter: we're just trying to trigger the Vault reentrancy check\n * (and not hurt anything in case it works). An empty operation array with no specific operation at all works\n * for that purpose, and is also the least expensive in terms of gas and bytecode size.\n *\n * Call this at the top of any function that can cause a state change in a pool and is either public itself,\n * or called by a public function *outside* a Vault operation (e.g., join, exit, or swap).\n *\n * If this is *not* called in functions that are vulnerable to the read-only reentrancy issue described\n * here (https://forum.balancer.fi/t/reentrancy-vulnerability-scope-expanded/4345), those functions are unsafe,\n * and subject to manipulation that may result in loss of funds.\n */\n function ensureNotInVaultContext(IBalancerVault vault) internal view {\n // Perform the following operation to trigger the Vault's reentrancy guard:\n //\n // IBalancerVault.UserBalanceOp[] memory noop = new IBalancerVault.UserBalanceOp[](0);\n // _vault.manageUserBalance(noop);\n //\n // However, use a static call so that it can be a view function (even though the function is non-view).\n // This allows the library to be used more widely, as some functions that need to be protected might be\n // view.\n //\n // This staticcall always reverts, but we need to make sure it doesn't fail due to a re-entrancy attack.\n // Staticcalls consume all gas forwarded to them on a revert caused by storage modification.\n // By default, almost the entire available gas is forwarded to the staticcall,\n // causing the entire call to revert with an 'out of gas' error.\n //\n // We set the gas limit to 10k for the staticcall to\n // avoid wasting gas when it reverts due to storage modification.\n // `manageUserBalance` is a non-reentrant function in the Vault, so calling it invokes `_enterNonReentrant`\n // in the `ReentrancyGuard` contract, reproduced here:\n //\n // function _enterNonReentrant() private {\n // // If the Vault is actually being reentered, it will revert in the first line, at the `_require` that\n // // checks the reentrancy flag, with \"BAL#400\" (corresponding to Errors.REENTRANCY) in the revertData.\n // // The full revertData will be: `abi.encodeWithSignature(\"Error(string)\", \"BAL#400\")`.\n // _require(_status != _ENTERED, Errors.REENTRANCY);\n //\n // // If the Vault is not being reentered, the check above will pass: but it will *still* revert,\n // // because the next line attempts to modify storage during a staticcall. However, this type of\n // // failure results in empty revertData.\n // _status = _ENTERED;\n // }\n //\n // So based on this analysis, there are only two possible revertData values: empty, or abi.encoded BAL#400.\n //\n // It is of course much more bytecode and gas efficient to check for zero-length revertData than to compare it\n // to the encoded REENTRANCY revertData.\n //\n // While it should be impossible for the call to fail in any other way (especially since it reverts before\n // `manageUserBalance` even gets called), any other error would generate non-zero revertData, so checking for\n // empty data guards against this case too.\n\n (, bytes memory revertData) = address(vault).staticcall{ gas: 10_000 }(\n abi.encodeWithSelector(vault.manageUserBalance.selector, 0)\n );\n\n _require(revertData.length == 0, Errors.REENTRANCY);\n }\n}\n" + }, + "contracts/strategies/BaseCurveAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for the Curve OETH/WETH pool\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { ICurveStableSwapNG } from \"../interfaces/ICurveStableSwapNG.sol\";\nimport { ICurveXChainLiquidityGauge } from \"../interfaces/ICurveXChainLiquidityGauge.sol\";\nimport { IChildLiquidityGaugeFactory } from \"../interfaces/IChildLiquidityGaugeFactory.sol\";\n\ncontract BaseCurveAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @dev a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n // New immutable variables that must be set in the constructor\n /**\n * @notice Address of the Wrapped ETH (WETH) contract.\n */\n IWETH9 public immutable weth;\n\n /**\n * @notice Address of the OETH token contract.\n */\n IERC20 public immutable oeth;\n\n /**\n * @notice Address of the LP (Liquidity Provider) token contract.\n */\n IERC20 public immutable lpToken;\n\n /**\n * @notice Address of the Curve StableSwap NG pool contract.\n */\n ICurveStableSwapNG public immutable curvePool;\n\n /**\n * @notice Address of the Curve X-Chain Liquidity Gauge contract.\n */\n ICurveXChainLiquidityGauge public immutable gauge;\n\n /**\n * @notice Address of the Child Liquidity Gauge Factory contract.\n */\n IChildLiquidityGaugeFactory public immutable gaugeFactory;\n\n // Ordered list of pool assets\n uint128 public immutable oethCoinIndex;\n uint128 public immutable wethCoinIndex;\n\n /**\n * @notice Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n uint256 public maxSlippage;\n\n event MaxSlippageUpdated(uint256 newMaxSlippage);\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balancesBefore = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffBefore = balancesBefore[wethCoinIndex].toInt256() -\n balancesBefore[oethCoinIndex].toInt256();\n\n _;\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balancesAfter = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffAfter = balancesAfter[wethCoinIndex].toInt256() -\n balancesAfter[oethCoinIndex].toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of ETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _oeth,\n address _weth,\n address _gauge,\n address _gaugeFactory,\n uint128 _oethCoinIndex,\n uint128 _wethCoinIndex\n ) InitializableAbstractStrategy(_baseConfig) {\n oethCoinIndex = _oethCoinIndex;\n wethCoinIndex = _wethCoinIndex;\n\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveStableSwapNG(_baseConfig.platformAddress);\n\n oeth = IERC20(_oeth);\n weth = IWETH9(_weth);\n gauge = ICurveXChainLiquidityGauge(_gauge);\n gaugeFactory = IChildLiquidityGaugeFactory(_gaugeFactory);\n\n _setGovernor(address(0));\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV\n * @param _maxSlippage Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV\n uint256 _maxSlippage\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n address[] memory _assets = new address[](1);\n _assets[0] = address(weth);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n _setMaxSlippage(_maxSlippage);\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit WETH into the Curve pool\n * @param _weth Address of Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to deposit.\n */\n function deposit(address _weth, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_weth, _amount);\n }\n\n function _deposit(address _weth, uint256 _wethAmount) internal {\n require(_wethAmount > 0, \"Must deposit something\");\n require(_weth == address(weth), \"Can only deposit WETH\");\n\n emit Deposit(_weth, address(lpToken), _wethAmount);\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 oethToAdd = uint256(\n _max(\n 0,\n balances[wethCoinIndex].toInt256() +\n _wethAmount.toInt256() -\n balances[oethCoinIndex].toInt256()\n )\n );\n\n /* Add so much OETH so that the pool ends up being balanced. And at minimum\n * add as much OETH as WETH and at maximum twice as much OETH.\n */\n oethToAdd = Math.max(oethToAdd, _wethAmount);\n oethToAdd = Math.min(oethToAdd, _wethAmount * 2);\n\n /* Mint OETH with a strategy that attempts to contribute to stability of OETH/WETH pool. Try\n * to mint so much OETH that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OETH minted will always be at least equal or greater\n * to WETH amount deployed. And never larger than twice the WETH amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(oethToAdd);\n\n emit Deposit(address(oeth), address(lpToken), oethToAdd);\n\n uint256[] memory _amounts = new uint256[](2);\n _amounts[wethCoinIndex] = _wethAmount;\n _amounts[oethCoinIndex] = oethToAdd;\n\n uint256 valueInLpTokens = (_wethAmount + oethToAdd).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Do the deposit to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(_amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool's LP tokens into the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n /**\n * @notice Deposit the strategy's entire balance of WETH into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = weth.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(weth), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _weth Address of the Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to withdraw.\n */\n function withdraw(\n address _recipient,\n address _weth,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_weth == address(weth), \"Can only withdraw WETH\");\n\n emit Withdrawal(_weth, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(_amount);\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough WETH on balanced removal\n */\n uint256[] memory _minWithdrawalAmounts = new uint256[](2);\n _minWithdrawalAmounts[wethCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OETH and any that was left in the strategy\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n\n // Transfer WETH to the recipient\n require(\n weth.transfer(_recipient, _amount),\n \"Transfer of WETH not successful\"\n );\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n function calcTokenToBurn(uint256 _wethAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much WETH\n * we want we can determine how much of OETH we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolWETHBalance = curvePool.balances(wethCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolWETHBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_wethAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = gauge.balanceOf(address(this));\n // Can not withdraw zero LP tokens from the gauge\n if (gaugeTokens == 0) return;\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[] memory minWithdrawAmounts = new uint256[](2);\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(\n lpToken.balanceOf(address(this)),\n minWithdrawAmounts\n );\n\n // Burn all OETH\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Get the strategy contract's WETH balance.\n // This includes all that was removed from the Curve pool and\n // any ether that was sitting in the strategy contract before the removal.\n uint256 ethBalance = weth.balanceOf(address(this));\n require(\n weth.transfer(vaultAddress, ethBalance),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethBalance);\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[] memory amounts = new uint256[](2);\n amounts[oethCoinIndex] = _oTokens;\n\n // Convert OETH to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool LP tokens to the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Deposit(address(oeth), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough ETH.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove OTokens from the Curve pool\n uint256 oethToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n oethCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /**\n * @notice One-sided remove of ETH from the Curve pool, convert to WETH\n * and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for ETH.\n * @dev Curve pool LP tokens is used rather than WETH assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of ETH. Curve's `calc_token_amount` functioun does not include fees.\n * A 3rd party libary can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * caclulate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Curve gauge and remove ETH from the Curve pool\n uint256 ethAmount = _withdrawAndRemoveFromPool(\n _lpTokens,\n wethCoinIndex\n );\n\n // Transfer WETH to the vault\n require(\n weth.transfer(vaultAddress, ethAmount),\n \"Transfer of WETH not successful\"\n );\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(weth), address(lpToken), ethAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the Convex pool and\n * do a one-sided remove of ETH or OETH from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the Convex pool.\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = ETH, 1 = OETH.\n * @return coinsRemoved The amount of ETH or OETH removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Curve gauge\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to ETH value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n // Apply slippage to ETH value\n uint256 minAmount = valueInEth.mulTruncate(uint256(1e18) - maxSlippage);\n\n // Remove just the ETH from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethbSupply = oeth.totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethbSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // CRV rewards flow.\n //---\n // CRV inflation:\n // Gauge receive CRV rewards from inflation.\n // Each checkpoint on the gauge send this CRV inflation to gauge factory.\n // This strategy should call mint on the gauge factory to collect the CRV rewards.\n // ---\n // Extra rewards:\n // Calling claim_rewards on the gauge will only claim extra rewards (outside of CRV).\n // ---\n\n // Mint CRV on Child Liquidity gauge factory\n gaugeFactory.mint(address(gauge));\n // Collect extra gauge rewards (outside of CRV)\n gauge.claim_rewards();\n\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _lpAmount) internal {\n // withdraw lp tokens from the gauge without claiming rewards\n gauge.withdraw(_lpAmount);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // WETH balance needed here for the balance check that happens from vault during depositing.\n balance = weth.balanceOf(address(this));\n uint256 lpTokens = gauge.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += (lpTokens * curvePool.get_virtual_price()) / 1e18;\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(weth);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Sets the maximum slippage allowed for any swap/liquidity operation\n * @param _maxSlippage Maximum slippage allowed, 1e18 = 100%.\n */\n function setMaxSlippage(uint256 _maxSlippage) external onlyGovernor {\n _setMaxSlippage(_maxSlippage);\n }\n\n function _setMaxSlippage(uint256 _maxSlippage) internal {\n require(_maxSlippage <= 5e16, \"Slippage must be less than 100%\");\n maxSlippage = _maxSlippage;\n emit MaxSlippageUpdated(_maxSlippage);\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OETH (required for adding liquidity)\n // slither-disable-next-line unused-return\n oeth.approve(platformAddress, type(uint256).max);\n\n // Approve Curve pool for WETH (required for adding liquidity)\n // slither-disable-next-line unused-return\n weth.approve(platformAddress, type(uint256).max);\n\n // Approve Curve gauge contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Curve gauge.\n // slither-disable-next-line unused-return\n lpToken.approve(address(gauge), type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/BridgedWOETHStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, SafeERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { AggregatorV3Interface } from \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\n\ncontract BridgedWOETHStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using StableMath for uint128;\n using SafeCast for uint256;\n using SafeERC20 for IERC20;\n\n event MaxPriceDiffBpsUpdated(uint128 oldValue, uint128 newValue);\n event WOETHPriceUpdated(uint128 oldValue, uint128 newValue);\n\n IWETH9 public immutable weth;\n IERC20 public immutable bridgedWOETH;\n IERC20 public immutable oethb;\n\n uint256 public constant MAX_PRICE_STALENESS = 2 days;\n\n uint128 public lastOraclePrice;\n uint128 public maxPriceDiffBps;\n\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _weth,\n address _bridgedWOETH,\n address _oethb\n ) InitializableAbstractStrategy(_stratConfig) {\n weth = IWETH9(_weth);\n bridgedWOETH = IERC20(_bridgedWOETH);\n oethb = IERC20(_oethb);\n }\n\n function initialize(uint128 _maxPriceDiffBps)\n external\n onlyGovernor\n initializer\n {\n InitializableAbstractStrategy._initialize(\n new address[](0), // No reward tokens\n new address[](0), // No assets\n new address[](0) // No pTokens\n );\n\n _setMaxPriceDiffBps(_maxPriceDiffBps);\n }\n\n /**\n * @dev Sets the max price diff bps for the wOETH value appreciation\n * @param _maxPriceDiffBps Bps value, 10k == 100%\n */\n function setMaxPriceDiffBps(uint128 _maxPriceDiffBps)\n external\n onlyGovernor\n {\n _setMaxPriceDiffBps(_maxPriceDiffBps);\n }\n\n /**\n * @dev Sets the max price diff bps for the wOETH value appreciation\n * @param _maxPriceDiffBps Bps value, 10k == 100%\n */\n function _setMaxPriceDiffBps(uint128 _maxPriceDiffBps) internal {\n require(\n _maxPriceDiffBps > 0 && _maxPriceDiffBps <= 10000,\n \"Invalid bps value\"\n );\n\n emit MaxPriceDiffBpsUpdated(maxPriceDiffBps, _maxPriceDiffBps);\n\n maxPriceDiffBps = _maxPriceDiffBps;\n }\n\n /**\n * @dev Wrapper for _updateWOETHOraclePrice with nonReentrant flag\n * @return The latest price of wOETH from Oracle\n */\n function updateWOETHOraclePrice() external nonReentrant returns (uint256) {\n return _updateWOETHOraclePrice();\n }\n\n /**\n * @dev Finds the value of bridged wOETH from the Oracle.\n * Ensures that it's within the bounds and reasonable.\n * And stores it.\n *\n * NOTE: Intentionally not caching `Vault.priceProvider` here,\n * since doing so would mean that we also have to update this\n * strategy every time there's a change in oracle router.\n * Besides on L2, the gas is considerably cheaper than mainnet.\n *\n * @return Latest price from oracle\n */\n function _updateWOETHOraclePrice() internal returns (uint256) {\n // WETH price per unit of bridged wOETH\n uint256 oraclePrice = IOracle(IVault(vaultAddress).priceProvider())\n .price(address(bridgedWOETH));\n\n // 1 wOETH > 1 WETH, always\n require(oraclePrice > 1 ether, \"Invalid wOETH value\");\n\n uint128 oraclePrice128 = oraclePrice.toUint128();\n\n // Do some checks\n if (lastOraclePrice > 0) {\n // Make sure the value only goes up\n require(oraclePrice128 >= lastOraclePrice, \"Negative wOETH yield\");\n\n // lastOraclePrice * (1 + maxPriceDiffBps)\n uint256 maxPrice = (lastOraclePrice * (1e4 + maxPriceDiffBps)) /\n 1e4;\n\n // And that it's within the bounds.\n require(oraclePrice128 <= maxPrice, \"Price diff beyond threshold\");\n }\n\n emit WOETHPriceUpdated(lastOraclePrice, oraclePrice128);\n\n // Store the price\n lastOraclePrice = oraclePrice128;\n\n return oraclePrice;\n }\n\n /**\n * @dev Computes & returns the value of given wOETH in WETH\n * @param woethAmount Amount of wOETH\n * @return Value of wOETH in WETH (using the last stored oracle price)\n */\n function getBridgedWOETHValue(uint256 woethAmount)\n public\n view\n returns (uint256)\n {\n return (woethAmount * lastOraclePrice) / 1 ether;\n }\n\n /**\n * @dev Takes in bridged wOETH and mints & returns\n * equivalent amount of OETHb.\n * @param woethAmount Amount of bridged wOETH to transfer in\n */\n function depositBridgedWOETH(uint256 woethAmount)\n external\n onlyGovernorOrStrategist\n nonReentrant\n {\n // Update wOETH price\n uint256 oraclePrice = _updateWOETHOraclePrice();\n\n // Figure out how much they are worth\n uint256 oethToMint = (woethAmount * oraclePrice) / 1 ether;\n\n require(oethToMint > 0, \"Invalid deposit amount\");\n\n // There's no pToken, however, it just uses WOETH address in the event\n emit Deposit(address(weth), address(bridgedWOETH), oethToMint);\n\n // Mint OETHb tokens and transfer it to the caller\n IVault(vaultAddress).mintForStrategy(oethToMint);\n\n // Transfer out minted OETHb\n // slither-disable-next-line unchecked-transfer unused-return\n oethb.transfer(msg.sender, oethToMint);\n\n // Transfer in all bridged wOETH tokens\n // slither-disable-next-line unchecked-transfer unused-return\n bridgedWOETH.transferFrom(msg.sender, address(this), woethAmount);\n }\n\n /**\n * @dev Takes in OETHb and burns it and returns\n * equivalent amount of bridged wOETH.\n * @param oethToBurn Amount of OETHb to burn\n */\n function withdrawBridgedWOETH(uint256 oethToBurn)\n external\n onlyGovernorOrStrategist\n nonReentrant\n {\n // Update wOETH price\n uint256 oraclePrice = _updateWOETHOraclePrice();\n\n // Figure out how much they are worth\n uint256 woethAmount = (oethToBurn * 1 ether) / oraclePrice;\n\n require(woethAmount > 0, \"Invalid withdraw amount\");\n\n // There's no pToken, however, it just uses WOETH address in the event\n emit Withdrawal(address(weth), address(bridgedWOETH), oethToBurn);\n\n // Transfer WOETH back\n // slither-disable-next-line unchecked-transfer unused-return\n bridgedWOETH.transfer(msg.sender, woethAmount);\n\n // Transfer in OETHb\n // slither-disable-next-line unchecked-transfer unused-return\n oethb.transferFrom(msg.sender, address(this), oethToBurn);\n\n // Burn OETHb\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n }\n\n /**\n * @notice Returns the amount of backing WETH the strategy holds\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // Figure out how much wOETH is worth at the time.\n // Always uses the last stored oracle price.\n // Call updateWOETHOraclePrice manually to pull in latest yields.\n\n // NOTE: If the contract has been deployed but the call to\n // `updateWOETHOraclePrice()` has never been made, then this\n // will return zero. It should be fine because the strategy\n // should update the price whenever a deposit/withdraw happens.\n\n // If `updateWOETHOraclePrice()` hasn't been called in a while,\n // the strategy will underreport its holdings but never overreport it.\n\n balance =\n (bridgedWOETH.balanceOf(address(this)) * lastOraclePrice) /\n 1 ether;\n }\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n // Strategist deposits bridged wOETH but the contract only\n // reports the balance in WETH. As far as Vault is concerned,\n // it isn't aware of bridged wOETH token\n return _asset == address(weth);\n }\n\n /***************************************\n Overridden methods\n ****************************************/\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function transferToken(address _asset, uint256 _amount)\n public\n override\n onlyGovernor\n {\n require(\n _asset != address(bridgedWOETH) && _asset != address(weth),\n \"Cannot transfer supported asset\"\n );\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice deposit() function not used for this strategy\n */\n function deposit(address, uint256)\n external\n override\n onlyVault\n nonReentrant\n {\n // Use depositBridgedWOETH() instead\n require(false, \"Deposit disabled\");\n }\n\n /**\n * @notice depositAll() function not used for this strategy\n */\n function depositAll() external override onlyVault nonReentrant {\n // Use depositBridgedWOETH() instead\n require(false, \"Deposit disabled\");\n }\n\n /**\n * @notice withdraw() function not used for this strategy\n */\n function withdraw(\n // solhint-disable-next-line no-unused-vars\n address _recipient,\n // solhint-disable-next-line no-unused-vars\n address _asset,\n // solhint-disable-next-line no-unused-vars\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(false, \"Withdrawal disabled\");\n }\n\n /**\n * @notice withdrawAll() function not used for this strategy\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n // Withdrawal disabled\n }\n\n function _abstractSetPToken(address, address) internal override {\n revert(\"No pTokens are used\");\n }\n\n function safeApproveAllTokens() external override {}\n\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function removePToken(uint256) external override {\n revert(\"No pTokens are used\");\n }\n\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function collectRewardTokens() external override {}\n}\n" + }, + "contracts/strategies/CompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Compound Strategy\n * @notice Investment strategy for Compound like lending platforms. eg Compound and Flux\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICERC20 } from \"./ICompound.sol\";\nimport { AbstractCompoundStrategy, InitializableAbstractStrategy } from \"./AbstractCompoundStrategy.sol\";\nimport { IComptroller } from \"../interfaces/IComptroller.sol\";\nimport { IERC20 } from \"../utils/InitializableAbstractStrategy.sol\";\n\ncontract CompoundStrategy is AbstractCompoundStrategy {\n using SafeERC20 for IERC20;\n event SkippedWithdrawal(address asset, uint256 amount);\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @notice initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @notice Collect accumulated COMP and send to Harvester.\n */\n function collectRewardTokens()\n external\n virtual\n override\n onlyHarvester\n nonReentrant\n {\n // Claim COMP from Comptroller\n ICERC20 cToken = _getCTokenFor(assetsMapped[0]);\n IComptroller comptroller = IComptroller(cToken.comptroller());\n // Only collect from active cTokens, saves gas\n address[] memory ctokensToCollect = new address[](assetsMapped.length);\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n ctokensToCollect[i] = address(_getCTokenFor(assetsMapped[i]));\n }\n // Claim only for this strategy\n address[] memory claimers = new address[](1);\n claimers[0] = address(this);\n // Claim COMP from Comptroller. Only collect for supply, saves gas\n comptroller.claimComp(claimers, ctokensToCollect, false, true);\n // Transfer COMP to Harvester\n IERC20 rewardToken = IERC20(rewardTokenAddresses[0]);\n uint256 balance = rewardToken.balanceOf(address(this));\n emit RewardTokenCollected(\n harvesterAddress,\n rewardTokenAddresses[0],\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n\n /**\n * @notice Deposit asset into the underlying platform\n * @param _asset Address of asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit an asset into the underlying platform\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n ICERC20 cToken = _getCTokenFor(_asset);\n emit Deposit(_asset, address(cToken), _amount);\n require(cToken.mint(_amount) == 0, \"cToken mint failed\");\n }\n\n /**\n * @notice Deposit the entire balance of any supported asset in the strategy into the underlying platform\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n uint256 assetBalance = asset.balanceOf(address(this));\n if (assetBalance > 0) {\n _deposit(address(asset), assetBalance);\n }\n }\n }\n\n /**\n * @notice Withdraw an asset from the underlying platform\n * @param _recipient Address to receive withdrawn assets\n * @param _asset Address of the asset to withdraw\n * @param _amount Amount of assets to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n ICERC20 cToken = _getCTokenFor(_asset);\n // If redeeming 0 cTokens, just skip, else COMP will revert\n uint256 cTokensToRedeem = _convertUnderlyingToCToken(cToken, _amount);\n if (cTokensToRedeem == 0) {\n emit SkippedWithdrawal(_asset, _amount);\n return;\n }\n\n emit Withdrawal(_asset, address(cToken), _amount);\n require(cToken.redeemUnderlying(_amount) == 0, \"Redeem failed\");\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / cTokens\n * We need to approve the cToken and give it permission to spend the asset\n * @param _asset Address of the asset to approve. eg DAI\n * @param _pToken The pToken for the approval. eg cDAI or fDAI\n */\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n // Safe approval\n IERC20(_asset).safeApprove(_pToken, 0);\n IERC20(_asset).safeApprove(_pToken, type(uint256).max);\n }\n\n /**\n * @notice Remove all supported assets from the underlying platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n // Redeem entire balance of cToken\n ICERC20 cToken = _getCTokenFor(address(asset));\n uint256 cTokenBalance = cToken.balanceOf(address(this));\n if (cTokenBalance > 0) {\n require(cToken.redeem(cTokenBalance) == 0, \"Redeem failed\");\n uint256 assetBalance = asset.balanceOf(address(this));\n // Transfer entire balance to Vault\n asset.safeTransfer(vaultAddress, assetBalance);\n\n emit Withdrawal(address(asset), address(cToken), assetBalance);\n }\n }\n }\n\n /**\n * @notice Get the total asset value held in the underlying platform\n * This includes any interest that was generated since depositing.\n * The exchange rate between the cToken and asset gradually increases,\n * causing the cToken to be worth more corresponding asset.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n // Balance is always with token cToken decimals\n ICERC20 cToken = _getCTokenFor(_asset);\n balance = _checkBalance(cToken);\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * underlying = (cTokenAmt * exchangeRate) / 1e18\n * @param _cToken cToken for which to check balance\n * @return balance Total value of the asset in the platform\n */\n function _checkBalance(ICERC20 _cToken)\n internal\n view\n returns (uint256 balance)\n {\n // e.g. 50e8*205316390724364402565641705 / 1e18 = 1.0265..e18\n balance =\n (_cToken.balanceOf(address(this)) * _cToken.exchangeRateStored()) /\n 1e18;\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding cToken,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens() external override {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n address cToken = assetToPToken[address(asset)];\n // Safe approval\n asset.safeApprove(cToken, 0);\n asset.safeApprove(cToken, type(uint256).max);\n }\n }\n}\n" + }, + "contracts/strategies/ConvexEthMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Convex Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for the Curve OETH/ETH pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { ICurveETHPoolV1 } from \"./ICurveETHPoolV1.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\n\ncontract ConvexEthMetaStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 public constant MAX_SLIPPAGE = 1e16; // 1%, same as the Curve UI\n address public constant ETH_ADDRESS =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n // The following slots have been deprecated with immutable variables\n // slither-disable-next-line constable-states\n address private _deprecated_cvxDepositorAddress;\n // slither-disable-next-line constable-states\n address private _deprecated_cvxRewardStaker;\n // slither-disable-next-line constable-states\n uint256 private _deprecated_cvxDepositorPTokenId;\n // slither-disable-next-line constable-states\n address private _deprecated_curvePool;\n // slither-disable-next-line constable-states\n address private _deprecated_lpToken;\n // slither-disable-next-line constable-states\n address private _deprecated_oeth;\n // slither-disable-next-line constable-states\n address private _deprecated_weth;\n\n // Ordered list of pool assets\n // slither-disable-next-line constable-states\n uint128 private _deprecated_oethCoinIndex;\n // slither-disable-next-line constable-states\n uint128 private _deprecated_ethCoinIndex;\n\n // New immutable variables that must be set in the constructor\n address public immutable cvxDepositorAddress;\n IRewardStaking public immutable cvxRewardStaker;\n uint256 public immutable cvxDepositorPTokenId;\n ICurveETHPoolV1 public immutable curvePool;\n IERC20 public immutable lpToken;\n IERC20 public immutable oeth;\n IWETH9 public immutable weth;\n\n // Ordered list of pool assets\n uint128 public constant oethCoinIndex = 1;\n uint128 public constant ethCoinIndex = 0;\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier only works on functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balancesBefore = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffBefore = int256(balancesBefore[ethCoinIndex]) -\n int256(balancesBefore[oethCoinIndex]);\n\n _;\n\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balancesAfter = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffAfter = int256(balancesAfter[ethCoinIndex]) -\n int256(balancesAfter[oethCoinIndex]);\n\n if (diffBefore <= 0) {\n // If the pool was originally imbalanced in favor of OETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n }\n if (diffBefore >= 0) {\n // If the pool was originally imbalanced in favor of ETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n // Used to circumvent the stack too deep issue\n struct ConvexEthMetaConfig {\n address cvxDepositorAddress; //Address of the Convex depositor(AKA booster) for this pool\n address cvxRewardStakerAddress; //Address of the CVX rewards staker\n uint256 cvxDepositorPTokenId; //Pid of the pool referred to by Depositor and staker\n address oethAddress; //Address of OETH token\n address wethAddress; //Address of WETH\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n ConvexEthMetaConfig memory _convexConfig\n ) InitializableAbstractStrategy(_baseConfig) {\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveETHPoolV1(_baseConfig.platformAddress);\n\n cvxDepositorAddress = _convexConfig.cvxDepositorAddress;\n cvxRewardStaker = IRewardStaking(_convexConfig.cvxRewardStakerAddress);\n cvxDepositorPTokenId = _convexConfig.cvxDepositorPTokenId;\n oeth = IERC20(_convexConfig.oethAddress);\n weth = IWETH9(_convexConfig.wethAddress);\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. eg WETH\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets // WETH\n ) external onlyGovernor initializer {\n require(_assets.length == 1, \"Must have exactly one asset\");\n require(_assets[0] == address(weth), \"Asset not WETH\");\n\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit WETH into the Curve pool\n * @param _weth Address of Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to deposit.\n */\n function deposit(address _weth, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_weth, _amount);\n }\n\n function _deposit(address _weth, uint256 _wethAmount) internal {\n require(_wethAmount > 0, \"Must deposit something\");\n require(_weth == address(weth), \"Can only deposit WETH\");\n weth.withdraw(_wethAmount);\n\n emit Deposit(_weth, address(lpToken), _wethAmount);\n\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 oethToAdd = uint256(\n _max(\n 0,\n int256(balances[ethCoinIndex]) +\n int256(_wethAmount) -\n int256(balances[oethCoinIndex])\n )\n );\n\n /* Add so much OETH so that the pool ends up being balanced. And at minimum\n * add as much OETH as WETH and at maximum twice as much OETH.\n */\n oethToAdd = Math.max(oethToAdd, _wethAmount);\n oethToAdd = Math.min(oethToAdd, _wethAmount * 2);\n\n /* Mint OETH with a strategy that attempts to contribute to stability of OETH/WETH pool. Try\n * to mint so much OETH that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OETH minted will always be at least equal or greater\n * to WETH amount deployed. And never larger than twice the WETH amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(oethToAdd);\n\n emit Deposit(address(oeth), address(lpToken), oethToAdd);\n\n uint256[2] memory _amounts;\n _amounts[ethCoinIndex] = _wethAmount;\n _amounts[oethCoinIndex] = oethToAdd;\n\n uint256 valueInLpTokens = (_wethAmount + oethToAdd).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Do the deposit to the Curve pool\n // slither-disable-next-line arbitrary-send\n uint256 lpDeposited = curvePool.add_liquidity{ value: _wethAmount }(\n _amounts,\n minMintAmount\n );\n\n // Deposit the Curve pool's LP tokens into the Convex rewards pool\n require(\n IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n lpDeposited,\n true // Deposit with staking\n ),\n \"Depositing LP to Convex not successful\"\n );\n }\n\n /**\n * @notice Deposit the strategy's entire balance of WETH into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = weth.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(weth), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _weth Address of the Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to withdraw.\n */\n function withdraw(\n address _recipient,\n address _weth,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Invalid amount\");\n require(_weth == address(weth), \"Can only withdraw WETH\");\n\n emit Withdrawal(_weth, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(_amount);\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough WETH on balanced removal\n */\n uint256[2] memory _minWithdrawalAmounts = [uint256(0), uint256(0)];\n _minWithdrawalAmounts[ethCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OETH and any that was left in the strategy\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n\n // Transfer WETH to the recipient\n weth.deposit{ value: _amount }();\n require(\n weth.transfer(_recipient, _amount),\n \"Transfer of WETH not successful\"\n );\n }\n\n function calcTokenToBurn(uint256 _wethAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much WETH\n * we want we can determine how much of OETH we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolWETHBalance = curvePool.balances(ethCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolWETHBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_wethAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = cvxRewardStaker.balanceOf(address(this));\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[2] memory minWithdrawAmounts = [uint256(0), uint256(0)];\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(\n lpToken.balanceOf(address(this)),\n minWithdrawAmounts\n );\n\n // Burn all OETH\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Get the strategy contract's ether balance.\n // This includes all that was removed from the Curve pool and\n // any ether that was sitting in the strategy contract before the removal.\n uint256 ethBalance = address(this).balance;\n // Convert all the strategy contract's ether to WETH and transfer to the vault.\n weth.deposit{ value: ethBalance }();\n require(\n weth.transfer(vaultAddress, ethBalance),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethBalance);\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[2] memory amounts = [uint256(0), uint256(0)];\n amounts[oethCoinIndex] = _oTokens;\n\n // Convert OETH to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n\n // Deposit the Curve pool LP tokens to the Convex rewards pool\n require(\n IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n lpDeposited,\n true // Deposit with staking\n ),\n \"Failed to Deposit LP to Convex\"\n );\n\n emit Deposit(address(oeth), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough ETH.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove OTokens from the Curve pool\n uint256 oethToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n oethCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /**\n * @notice One-sided remove of ETH from the Curve pool, convert to WETH\n * and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for ETH.\n * @dev Curve pool LP tokens is used rather than WETH assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of ETH. Curve's `calc_token_amount` functioun does not include fees.\n * A 3rd party libary can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * caclulate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove ETH from the Curve pool\n uint256 ethAmount = _withdrawAndRemoveFromPool(_lpTokens, ethCoinIndex);\n\n // Convert ETH to WETH and transfer to the vault\n weth.deposit{ value: ethAmount }();\n require(\n weth.transfer(vaultAddress, ethAmount),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the Convex pool and\n * do a one-sided remove of ETH or OETH from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the Convex pool.\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = ETH, 1 = OETH.\n * @return coinsRemoved The amount of ETH or OETH removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Convex pool\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to ETH value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n // Apply slippage to ETH value\n uint256 minAmount = valueInEth.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Remove just the ETH from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV and CVX rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n cvxRewardStaker.getReward();\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _wethAmount) internal {\n // withdraw and unwrap with claim takes back the lpTokens\n // and also collects the rewards for deposit\n cvxRewardStaker.withdrawAndUnwrap(_wethAmount, true);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // Eth balance needed here for the balance check that happens from vault during depositing.\n balance = address(this).balance;\n uint256 lpTokens = cvxRewardStaker.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += (lpTokens * curvePool.get_virtual_price()) / 1e18;\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(weth);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @notice Accept unwrapped WETH\n */\n receive() external payable {}\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OETH (required for adding liquidity)\n // No approval is needed for ETH\n // slither-disable-next-line unused-return\n oeth.approve(platformAddress, type(uint256).max);\n\n // Approve Convex deposit contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Convex rewards pool\n // slither-disable-next-line unused-return\n lpToken.approve(cvxDepositorAddress, type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/ConvexGeneralizedMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { AbstractConvexMetaStrategy } from \"./AbstractConvexMetaStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract ConvexGeneralizedMetaStrategy is AbstractConvexMetaStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /* Take 3pool LP and deposit it to metapool. Take the LP from metapool\n * and deposit them to Convex.\n */\n function _lpDepositAll() internal override {\n IERC20 threePoolLp = IERC20(pTokenAddress);\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 threePoolLpBalance = threePoolLp.balanceOf(address(this));\n uint256 curve3PoolVirtualPrice = curvePool.get_virtual_price();\n uint256 threePoolLpDollarValue = threePoolLpBalance.mulTruncate(\n curve3PoolVirtualPrice\n );\n\n uint256[2] memory _amounts = [0, threePoolLpBalance];\n\n uint256 metapoolVirtualPrice = metapool.get_virtual_price();\n /**\n * First convert all the deposited tokens to dollar values,\n * then divide by virtual price to convert to metapool LP tokens\n * and apply the max slippage\n */\n uint256 minReceived = threePoolLpDollarValue\n .divPrecisely(metapoolVirtualPrice)\n .mulTruncate(uint256(1e18) - MAX_SLIPPAGE);\n\n uint256 metapoolLp = metapool.add_liquidity(_amounts, minReceived);\n\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n metapoolLp,\n true // Deposit with staking\n );\n\n require(success, \"Failed to deposit to Convex\");\n }\n\n /**\n * Withdraw the specified amount of tokens from the gauge. And use all the resulting tokens\n * to remove liquidity from metapool\n * @param num3CrvTokens Number of Convex 3pool LP tokens to withdraw from metapool\n */\n function _lpWithdraw(uint256 num3CrvTokens) internal override {\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n uint256 requiredMetapoolLpTokens = _calcCurveMetaTokenAmount(\n crvCoinIndex,\n num3CrvTokens\n );\n\n require(\n requiredMetapoolLpTokens <= gaugeTokens,\n string(\n bytes.concat(\n bytes(\"Attempting to withdraw \"),\n bytes(Strings.toString(requiredMetapoolLpTokens)),\n bytes(\", metapoolLP but only \"),\n bytes(Strings.toString(gaugeTokens)),\n bytes(\" available.\")\n )\n )\n );\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards for deposit\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n requiredMetapoolLpTokens,\n true\n );\n\n if (requiredMetapoolLpTokens > 0) {\n // slither-disable-next-line unused-return\n metapool.remove_liquidity_one_coin(\n requiredMetapoolLpTokens,\n int128(crvCoinIndex),\n num3CrvTokens\n );\n }\n }\n\n function _lpWithdrawAll() internal override {\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n gaugeTokens,\n true\n );\n\n if (gaugeTokens > 0) {\n uint256 burnDollarAmount = gaugeTokens.mulTruncate(\n metapool.get_virtual_price()\n );\n uint256 curve3PoolExpected = burnDollarAmount.divPrecisely(\n ICurvePool(platformAddress).get_virtual_price()\n );\n\n // Always withdraw all of the available metapool LP tokens (similar to how we always deposit all)\n // slither-disable-next-line unused-return\n metapool.remove_liquidity_one_coin(\n gaugeTokens,\n int128(crvCoinIndex),\n curve3PoolExpected -\n curve3PoolExpected.mulTruncate(maxWithdrawalSlippage)\n );\n }\n }\n}\n" + }, + "contracts/strategies/ConvexOUSDMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { AbstractConvexMetaStrategy } from \"./AbstractConvexMetaStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract ConvexOUSDMetaStrategy is AbstractConvexMetaStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /* Take 3pool LP and mint the corresponding amount of ousd. Deposit and stake that to\n * ousd Curve Metapool. Take the LP from metapool and deposit them to Convex.\n */\n function _lpDepositAll() internal override {\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 threePoolLpBalance = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n uint256 curve3PoolVirtualPrice = curvePool.get_virtual_price();\n uint256 threePoolLpDollarValue = threePoolLpBalance.mulTruncate(\n curve3PoolVirtualPrice\n );\n\n // safe to cast since min value is at least 0\n uint256 ousdToAdd = uint256(\n _max(\n 0,\n int256(\n metapool.balances(crvCoinIndex).mulTruncate(\n curve3PoolVirtualPrice\n )\n ) -\n int256(metapool.balances(mainCoinIndex)) +\n int256(threePoolLpDollarValue)\n )\n );\n\n /* Add so much OUSD so that the pool ends up being balanced. And at minimum\n * add twice as much OUSD as 3poolLP and at maximum at twice as\n * much OUSD.\n */\n ousdToAdd = Math.max(ousdToAdd, threePoolLpDollarValue);\n ousdToAdd = Math.min(ousdToAdd, threePoolLpDollarValue * 2);\n\n /* Mint OUSD with a strategy that attempts to contribute to stability of OUSD metapool. Try\n * to mint so much OUSD that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OUSD minted will always be at least equal or greater\n * to stablecoin(DAI, USDC, USDT) amount of 3CRVLP deployed. And never larger than twice the\n * stablecoin amount of 3CRVLP deployed even if it would have a further beneficial effect\n * on pool stability.\n */\n if (ousdToAdd > 0) {\n IVault(vaultAddress).mintForStrategy(ousdToAdd);\n }\n\n uint256[2] memory _amounts = [ousdToAdd, threePoolLpBalance];\n\n uint256 metapoolVirtualPrice = metapool.get_virtual_price();\n /**\n * First convert all the deposited tokens to dollar values,\n * then divide by virtual price to convert to metapool LP tokens\n * and apply the max slippage\n */\n uint256 minReceived = (ousdToAdd + threePoolLpDollarValue)\n .divPrecisely(metapoolVirtualPrice)\n .mulTruncate(uint256(1e18) - MAX_SLIPPAGE);\n\n uint256 metapoolLp = metapool.add_liquidity(_amounts, minReceived);\n\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n metapoolLp,\n true // Deposit with staking\n );\n\n require(success, \"Failed to deposit to Convex\");\n }\n\n /**\n * Withdraw the specified amount of tokens from the gauge. And use all the resulting tokens\n * to remove liquidity from metapool\n * @param num3CrvTokens Number of 3CRV tokens to withdraw from metapool\n */\n function _lpWithdraw(uint256 num3CrvTokens) internal override {\n ICurvePool curvePool = ICurvePool(platformAddress);\n /* The rate between coins in the metapool determines the rate at which metapool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much 3crvLp\n * we want we can determine how much of OUSD we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 crvPoolBalance = metapool.balances(crvCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * metapool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * metapoolLPToken.totalSupply()) / crvPoolBalance;\n // simplifying below to: `uint256 diff = (num3CrvTokens - 1) * k` causes loss of precision\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = crvPoolBalance * k -\n (crvPoolBalance - num3CrvTokens - 1) * k;\n uint256 lpToBurn = diff / 1e36;\n\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n require(\n lpToBurn <= gaugeTokens,\n string(\n bytes.concat(\n bytes(\"Attempting to withdraw \"),\n bytes(Strings.toString(lpToBurn)),\n bytes(\", metapoolLP but only \"),\n bytes(Strings.toString(gaugeTokens)),\n bytes(\" available.\")\n )\n )\n );\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards for deposit\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n lpToBurn,\n true\n );\n\n // calculate the min amount of OUSD expected for the specified amount of LP tokens\n uint256 minOUSDAmount = lpToBurn.mulTruncate(\n metapool.get_virtual_price()\n ) -\n num3CrvTokens.mulTruncate(curvePool.get_virtual_price()) -\n 1;\n\n // withdraw the liquidity from metapool\n uint256[2] memory _removedAmounts = metapool.remove_liquidity(\n lpToBurn,\n [minOUSDAmount, num3CrvTokens]\n );\n\n IVault(vaultAddress).burnForStrategy(_removedAmounts[mainCoinIndex]);\n }\n\n function _lpWithdrawAll() internal override {\n IERC20 metapoolErc20 = IERC20(address(metapool));\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n gaugeTokens,\n true\n );\n\n uint256[2] memory _minAmounts = [uint256(0), uint256(0)];\n uint256[2] memory _removedAmounts = metapool.remove_liquidity(\n metapoolErc20.balanceOf(address(this)),\n _minAmounts\n );\n\n IVault(vaultAddress).burnForStrategy(_removedAmounts[mainCoinIndex]);\n }\n}\n" + }, + "contracts/strategies/ConvexStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { IERC20, AbstractCurveStrategy, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\n/*\n * IMPORTANT(!) If ConvexStrategy needs to be re-deployed, it requires new\n * proxy contract with fresh storage slots. Changes in `AbstractCurveStrategy`\n * storage slots would break existing implementation.\n *\n * Remove this notice if ConvexStrategy is re-deployed\n */\ncontract ConvexStrategy is AbstractCurveStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n address internal cvxDepositorAddress;\n address internal cvxRewardStakerAddress;\n // slither-disable-next-line constable-states\n address private _deprecated_cvxRewardTokenAddress;\n uint256 internal cvxDepositorPTokenId;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * DAI, USDC, USDT\n * @param _pTokens Platform Token corresponding addresses\n * @param _cvxDepositorAddress Address of the Convex depositor(AKA booster) for this pool\n * @param _cvxRewardStakerAddress Address of the CVX rewards staker\n * @param _cvxDepositorPTokenId Pid of the pool referred to by Depositor and staker\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets,\n address[] calldata _pTokens,\n address _cvxDepositorAddress,\n address _cvxRewardStakerAddress,\n uint256 _cvxDepositorPTokenId\n ) external onlyGovernor initializer {\n require(_assets.length == 3, \"Must have exactly three assets\");\n // Should be set prior to abstract initialize call otherwise\n // abstractSetPToken calls will fail\n cvxDepositorAddress = _cvxDepositorAddress;\n cvxRewardStakerAddress = _cvxRewardStakerAddress;\n cvxDepositorPTokenId = _cvxDepositorPTokenId;\n pTokenAddress = _pTokens[0];\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n function _lpDepositAll() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // Deposit with staking\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n pToken.balanceOf(address(this)),\n true\n );\n require(success, \"Failed to deposit to Convex\");\n }\n\n function _lpWithdraw(uint256 numCrvTokens) internal override {\n uint256 gaugePTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n // Not enough in this contract or in the Gauge, can't proceed\n require(numCrvTokens > gaugePTokens, \"Insufficient 3CRV balance\");\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards to this\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n numCrvTokens,\n true\n );\n }\n\n function _lpWithdrawAll() internal override {\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards to this\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n IRewardStaking(cvxRewardStakerAddress).balanceOf(address(this)),\n true\n );\n }\n\n function _approveBase() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // 3Pool for LP token (required for removing liquidity)\n pToken.safeApprove(platformAddress, 0);\n pToken.safeApprove(platformAddress, type(uint256).max);\n // Gauge for LP token\n pToken.safeApprove(cvxDepositorAddress, 0);\n pToken.safeApprove(cvxDepositorAddress, type(uint256).max);\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 contractPTokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n uint256 gaugePTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n uint256 totalPTokens = contractPTokens + gaugePTokens;\n\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (totalPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = (totalPTokens * virtual_price) / 1e18;\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = value.scaleBy(assetDecimals, 18) / 3;\n }\n }\n\n /**\n * @dev Collect accumulated CRV and CVX and send to Vault.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n IRewardStaking(cvxRewardStakerAddress).getReward();\n _collectRewardTokens();\n }\n}\n" + }, + "contracts/strategies/CurveAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for a Curve pool using an OToken.\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { ICurveStableSwapNG } from \"../interfaces/ICurveStableSwapNG.sol\";\nimport { ICurveLiquidityGaugeV6 } from \"../interfaces/ICurveLiquidityGaugeV6.sol\";\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\nimport { ICurveMinter } from \"../interfaces/ICurveMinter.sol\";\n\ncontract CurveAMOStrategy is InitializableAbstractStrategy {\n using SafeCast for uint256;\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n /**\n * @dev a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n // New immutable variables that must be set in the constructor\n /**\n * @notice Address of the hard asset (weth, usdt, usdc).\n */\n IERC20 public immutable hardAsset;\n\n /**\n * @notice Address of the OTOKEN token contract.\n */\n IERC20 public immutable oToken;\n\n /**\n * @notice Address of the LP (Liquidity Provider) token contract.\n */\n IERC20 public immutable lpToken;\n\n /**\n * @notice Address of the Curve StableSwap NG pool contract.\n */\n ICurveStableSwapNG public immutable curvePool;\n\n /**\n * @notice Address of the Curve X-Chain Liquidity Gauge contract.\n */\n ICurveLiquidityGaugeV6 public immutable gauge;\n\n /**\n * @notice Address of the Curve Minter contract.\n */\n ICurveMinter public immutable minter;\n\n /**\n * @notice Index of the OTOKEN and hardAsset in the Curve pool.\n */\n uint128 public immutable otokenCoinIndex;\n uint128 public immutable hardAssetCoinIndex;\n\n /**\n * @notice Decimals of the OTOKEN and hardAsset.\n */\n uint8 public immutable decimalsOToken;\n uint8 public immutable decimalsHardAsset;\n\n /**\n * @notice Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n uint256 public maxSlippage;\n\n event MaxSlippageUpdated(uint256 newMaxSlippage);\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the hard asset and OToken balances in the Curve pool\n uint256[] memory balancesBefore = curvePool.get_balances();\n // diff = hardAsset balance - OTOKEN balance\n int256 diffBefore = (\n balancesBefore[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() - balancesBefore[otokenCoinIndex].toInt256();\n\n _;\n\n // Get the hard asset and OToken balances in the Curve pool\n uint256[] memory balancesAfter = curvePool.get_balances();\n // diff = hardAsset balance - OTOKEN balance\n int256 diffAfter = (\n balancesAfter[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() - balancesAfter[otokenCoinIndex].toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OTOKEN, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of hardAsset, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _otoken,\n address _hardAsset,\n address _gauge,\n address _minter\n ) InitializableAbstractStrategy(_baseConfig) {\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveStableSwapNG(_baseConfig.platformAddress);\n minter = ICurveMinter(_minter);\n\n oToken = IERC20(_otoken);\n hardAsset = IERC20(_hardAsset);\n gauge = ICurveLiquidityGaugeV6(_gauge);\n decimalsHardAsset = IBasicToken(_hardAsset).decimals();\n decimalsOToken = IBasicToken(_otoken).decimals();\n\n (hardAssetCoinIndex, otokenCoinIndex) = curvePool.coins(0) == _hardAsset\n ? (0, 1)\n : (1, 0);\n require(\n curvePool.coins(otokenCoinIndex) == _otoken &&\n curvePool.coins(hardAssetCoinIndex) == _hardAsset,\n \"Invalid coin indexes\"\n );\n require(gauge.lp_token() == address(curvePool), \"Invalid pool\");\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV\n * @param _maxSlippage Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV\n uint256 _maxSlippage\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n address[] memory _assets = new address[](1);\n _assets[0] = address(hardAsset);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n _setMaxSlippage(_maxSlippage);\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit hard asset into the Curve pool\n * @param _hardAsset Address of hard asset contract.\n * @param _amount Amount of hard asset to deposit.\n */\n function deposit(address _hardAsset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_hardAsset, _amount);\n }\n\n function _deposit(address _hardAsset, uint256 _hardAssetAmount) internal {\n require(_hardAssetAmount > 0, \"Must deposit something\");\n require(_hardAsset == address(hardAsset), \"Unsupported asset\");\n\n emit Deposit(_hardAsset, address(lpToken), _hardAssetAmount);\n uint256 scaledHardAssetAmount = _hardAssetAmount.scaleBy(\n decimalsOToken,\n decimalsHardAsset\n );\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 otokenToAdd = uint256(\n _max(\n 0,\n (\n balances[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() +\n scaledHardAssetAmount.toInt256() -\n balances[otokenCoinIndex].toInt256()\n )\n );\n\n /* Add so much OTOKEN so that the pool ends up being balanced. And at minimum\n * add as much OTOKEN as hard asset and at maximum twice as much OTOKEN.\n */\n otokenToAdd = Math.max(otokenToAdd, scaledHardAssetAmount);\n otokenToAdd = Math.min(otokenToAdd, scaledHardAssetAmount * 2);\n\n /* Mint OTOKEN with a strategy that attempts to contribute to stability of OTOKEN/hardAsset pool. Try\n * to mint so much OTOKEN that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OTOKEN minted will always be at least equal or greater\n * to hardAsset amount deployed. And never larger than twice the hardAsset amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(otokenToAdd);\n\n emit Deposit(address(oToken), address(lpToken), otokenToAdd);\n\n uint256[] memory _amounts = new uint256[](2);\n _amounts[hardAssetCoinIndex] = _hardAssetAmount;\n _amounts[otokenCoinIndex] = otokenToAdd;\n\n uint256 valueInLpTokens = (scaledHardAssetAmount + otokenToAdd)\n .divPrecisely(curvePool.get_virtual_price());\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Do the deposit to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(_amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool's LP tokens into the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n /**\n * @notice Deposit the strategy's entire balance of hardAsset into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = hardAsset.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(hardAsset), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw hardAsset and OTOKEN from the Curve pool, burn the OTOKEN,\n * transfer hardAsset to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _hardAsset Address of the hardAsset contract.\n * @param _amount Amount of hardAsset to withdraw.\n */\n function withdraw(\n address _recipient,\n address _hardAsset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(\n _hardAsset == address(hardAsset),\n \"Can only withdraw hard asset\"\n );\n\n emit Withdrawal(_hardAsset, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(\n _amount.scaleBy(decimalsOToken, decimalsHardAsset)\n );\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough hardAsset on balanced removal\n */\n uint256[] memory _minWithdrawalAmounts = new uint256[](2);\n _minWithdrawalAmounts[hardAssetCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OTOKEN and any that was left in the strategy\n uint256 otokenToBurn = oToken.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n\n // Transfer hardAsset to the recipient\n hardAsset.safeTransfer(_recipient, _amount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n function calcTokenToBurn(uint256 _hardAssetAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much hardAsset\n * we want we can determine how much of OTOKEN we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolHardAssetBalance = curvePool\n .balances(hardAssetCoinIndex)\n .scaleBy(decimalsOToken, decimalsHardAsset);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolHardAssetBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_hardAssetAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all hardAsset and OTOKEN from the Curve pool, burn the OTOKEN,\n * transfer hardAsset to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = gauge.balanceOf(address(this));\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[] memory minWithdrawAmounts = new uint256[](2);\n\n // Check balance of LP tokens in the strategy, if 0 return\n uint256 lpBalance = lpToken.balanceOf(address(this));\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n if (lpBalance > 0) {\n curvePool.remove_liquidity(lpBalance, minWithdrawAmounts);\n }\n\n // Burn all OTOKEN\n uint256 otokenToBurn = oToken.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n // Get the strategy contract's hardAsset balance.\n // This includes all that was removed from the Curve pool and\n // any hardAsset that was sitting in the strategy contract before the removal.\n uint256 hardAssetBalance = hardAsset.balanceOf(address(this));\n hardAsset.safeTransfer(vaultAddress, hardAssetBalance);\n\n if (hardAssetBalance > 0)\n emit Withdrawal(\n address(hardAsset),\n address(lpToken),\n hardAssetBalance\n );\n if (otokenToBurn > 0)\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many hardAsset.\n * The OToken/Asset, eg OTOKEN/hardAsset, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[] memory amounts = new uint256[](2);\n amounts[otokenCoinIndex] = _oTokens;\n\n // Convert OTOKEN to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool LP tokens to the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Deposit(address(oToken), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough hardAsset.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from gauge and remove OTokens from the Curve pool\n uint256 otokenToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n otokenCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n }\n\n /**\n * @notice One-sided remove of hardAsset from the Curve pool and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many hardAsset.\n * The OToken/Asset, eg OTOKEN/hardAsset, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for hardAsset.\n * @dev Curve pool LP tokens is used rather than hardAsset assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of hardAsset. Curve's `calc_token_amount` function does not include fees.\n * A 3rd party library can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * calculate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Curve gauge and remove hardAsset from the Curve pool\n uint256 hardAssetAmount = _withdrawAndRemoveFromPool(\n _lpTokens,\n hardAssetCoinIndex\n );\n\n // Transfer hardAsset to the vault\n hardAsset.safeTransfer(vaultAddress, hardAssetAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(hardAsset), address(lpToken), hardAssetAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the gauge and\n * do a one-sided remove of hardAsset or OTOKEN from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the gauge\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = hardAsset, 1 = OTOKEN.\n * @return coinsRemoved The amount of hardAsset or OTOKEN removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Curve gauge\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to hardAsset value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n\n if (coinIndex == hardAssetCoinIndex) {\n valueInEth = valueInEth.scaleBy(decimalsHardAsset, decimalsOToken);\n }\n\n // Apply slippage to hardAsset value\n uint256 minAmount = valueInEth.mulTruncate(uint256(1e18) - maxSlippage);\n\n // Remove just the hardAsset from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOtokenSupply = oToken.totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOtokenSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV rewards from inflation\n minter.mint(address(gauge));\n\n // Collect extra gauge rewards (outside of CRV)\n gauge.claim_rewards();\n\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _lpAmount) internal {\n require(\n gauge.balanceOf(address(this)) >= _lpAmount,\n \"Insufficient LP tokens\"\n );\n // withdraw lp tokens from the gauge without claiming rewards\n gauge.withdraw(_lpAmount);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(hardAsset), \"Unsupported asset\");\n\n // hardAsset balance needed here for the balance check that happens from vault during depositing.\n balance = hardAsset.balanceOf(address(this));\n uint256 lpTokens = gauge.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += ((lpTokens * curvePool.get_virtual_price()) / 1e18)\n .scaleBy(decimalsHardAsset, decimalsOToken);\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(hardAsset);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Sets the maximum slippage allowed for any swap/liquidity operation\n * @param _maxSlippage Maximum slippage allowed, 1e18 = 100%.\n */\n function setMaxSlippage(uint256 _maxSlippage) external onlyGovernor {\n _setMaxSlippage(_maxSlippage);\n }\n\n function _setMaxSlippage(uint256 _maxSlippage) internal {\n require(_maxSlippage <= 5e16, \"Slippage must be less than 100%\");\n maxSlippage = _maxSlippage;\n emit MaxSlippageUpdated(_maxSlippage);\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OTOKEN (required for adding liquidity)\n // slither-disable-next-line unused-return\n oToken.approve(platformAddress, type(uint256).max);\n\n // Approve Curve pool for hardAsset (required for adding liquidity)\n // slither-disable-next-line unused-return\n hardAsset.safeApprove(platformAddress, type(uint256).max);\n\n // Approve Curve gauge contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Curve gauge.\n // slither-disable-next-line unused-return\n lpToken.approve(address(gauge), type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/CurvePoolBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport { ICampaignRemoteManager } from \"../interfaces/ICampaignRemoteManager.sol\";\n\n/// @title CurvePoolBooster\n/// @author Origin Protocol\n/// @notice Contract to manage interactions with VotemarketV2 for a dedicated Curve pool/gauge.\ncontract CurvePoolBooster is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n ////////////////////////////////////////////////////\n /// --- CONSTANTS && IMMUTABLES\n ////////////////////////////////////////////////////\n /// @notice Base fee for the contract, 100%\n uint16 public constant FEE_BASE = 10_000;\n\n /// @notice Address of the gauge to manage\n address public immutable gauge;\n\n /// @notice Address of the reward token\n address public immutable rewardToken;\n\n /// @notice Chain id of the target chain\n uint256 public immutable targetChainId;\n\n ////////////////////////////////////////////////////\n /// --- STORAGE\n ////////////////////////////////////////////////////\n /// @notice Fee in FEE_BASE unit payed when managing campaign.\n uint16 public fee;\n\n /// @notice Address of the fee collector\n address public feeCollector;\n\n /// @notice Address of the campaignRemoteManager linked to VotemarketV2\n address public campaignRemoteManager;\n\n /// @notice Address of votemarket in L2\n address public votemarket;\n\n /// @notice Id of the campaign created\n uint256 public campaignId;\n\n ////////////////////////////////////////////////////\n /// --- EVENTS\n ////////////////////////////////////////////////////\n event FeeUpdated(uint16 newFee);\n event FeeCollected(address feeCollector, uint256 feeAmount);\n event FeeCollectorUpdated(address newFeeCollector);\n event VotemarketUpdated(address newVotemarket);\n event CampaignRemoteManagerUpdated(address newCampaignRemoteManager);\n event CampaignCreated(\n address gauge,\n address rewardToken,\n uint256 maxRewardPerVote,\n uint256 totalRewardAmount\n );\n event CampaignIdUpdated(uint256 newId);\n event CampaignClosed(uint256 campaignId);\n event TotalRewardAmountUpdated(uint256 extraTotalRewardAmount);\n event NumberOfPeriodsUpdated(uint8 extraNumberOfPeriods);\n event RewardPerVoteUpdated(uint256 newMaxRewardPerVote);\n event TokensRescued(address token, uint256 amount, address receiver);\n\n ////////////////////////////////////////////////////\n /// --- CONSTRUCTOR && INITIALIZATION\n ////////////////////////////////////////////////////\n constructor(\n uint256 _targetChainId,\n address _rewardToken,\n address _gauge\n ) {\n targetChainId = _targetChainId;\n rewardToken = _rewardToken;\n gauge = _gauge;\n\n // Prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /// @notice initialize function, to set up initial internal state\n /// @param _strategist Address of the strategist\n /// @param _fee Fee in FEE_BASE unit payed when managing campaign\n /// @param _feeCollector Address of the fee collector\n function initialize(\n address _strategist,\n uint16 _fee,\n address _feeCollector,\n address _campaignRemoteManager,\n address _votemarket\n ) external onlyGovernor initializer {\n _setStrategistAddr(_strategist);\n _setFee(_fee);\n _setFeeCollector(_feeCollector);\n _setCampaignRemoteManager(_campaignRemoteManager);\n _setVotemarket(_votemarket);\n }\n\n ////////////////////////////////////////////////////\n /// --- MUTATIVE FUNCTIONS\n ////////////////////////////////////////////////////\n /// @notice Create a new campaign on VotemarketV2\n /// @dev This will use all token available in this contract\n /// @param numberOfPeriods Duration of the campaign in weeks\n /// @param maxRewardPerVote Maximum reward per vote to distribute, to avoid overspending\n /// @param blacklist List of addresses to exclude from the campaign\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function createCampaign(\n uint8 numberOfPeriods,\n uint256 maxRewardPerVote,\n address[] calldata blacklist,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId == 0, \"Campaign already created\");\n require(numberOfPeriods > 1, \"Invalid number of periods\");\n require(maxRewardPerVote > 0, \"Invalid reward per vote\");\n\n // Handle fee (if any)\n uint256 balanceSubFee = _handleFee();\n\n // Approve the balanceSubFee to the campaign manager\n IERC20(rewardToken).safeApprove(campaignRemoteManager, 0);\n IERC20(rewardToken).safeApprove(campaignRemoteManager, balanceSubFee);\n\n // Create a new campaign\n ICampaignRemoteManager(campaignRemoteManager).createCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignCreationParams({\n chainId: targetChainId,\n gauge: gauge,\n manager: address(this),\n rewardToken: rewardToken,\n numberOfPeriods: numberOfPeriods,\n maxRewardPerVote: maxRewardPerVote,\n totalRewardAmount: balanceSubFee,\n addresses: blacklist,\n hook: address(0),\n isWhitelist: false\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit CampaignCreated(\n gauge,\n rewardToken,\n maxRewardPerVote,\n balanceSubFee\n );\n }\n\n /// @notice Manage the total reward amount of the campaign\n /// @dev This function should be called after the campaign is created\n /// @dev This will use all the token available in this contract\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageTotalRewardAmount(\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n\n // Handle fee (if any)\n uint256 balanceSubFee = _handleFee();\n\n // Approve the total reward amount to the campaign manager\n IERC20(rewardToken).safeApprove(campaignRemoteManager, 0);\n IERC20(rewardToken).safeApprove(campaignRemoteManager, balanceSubFee);\n\n // Manage the campaign\n // https://github.com/stake-dao/votemarket-v2/blob/main/packages/votemarket/src/Votemarket.sol#L668\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: 0,\n totalRewardAmount: balanceSubFee,\n maxRewardPerVote: 0\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit TotalRewardAmountUpdated(balanceSubFee);\n }\n\n /// @notice Manage the number of periods of the campaign\n /// @dev This function should be called after the campaign is created\n /// @param extraNumberOfPeriods Number of additional periods (cannot be 0)\n /// that will be added to already existing amount of periods.\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageNumberOfPeriods(\n uint8 extraNumberOfPeriods,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n require(extraNumberOfPeriods > 0, \"Invalid number of periods\");\n\n // Manage the campaign\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: extraNumberOfPeriods,\n totalRewardAmount: 0,\n maxRewardPerVote: 0\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit NumberOfPeriodsUpdated(extraNumberOfPeriods);\n }\n\n /// @notice Manage the reward per vote of the campaign\n /// @dev This function should be called after the campaign is created\n /// @param newMaxRewardPerVote New maximum reward per vote\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageRewardPerVote(\n uint256 newMaxRewardPerVote,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n require(newMaxRewardPerVote > 0, \"Invalid reward per vote\");\n\n // Manage the campaign\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: 0,\n totalRewardAmount: 0,\n maxRewardPerVote: newMaxRewardPerVote\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit RewardPerVoteUpdated(newMaxRewardPerVote);\n }\n\n /// @notice Close the campaign.\n /// @dev This function only work on the L2 chain. Not on mainnet.\n /// @dev The _campaignId parameter is not related to the campaignId of this contract, allowing greater flexibility.\n /// @param _campaignId Id of the campaign to close\n function closeCampaign(\n uint256 _campaignId,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n ICampaignRemoteManager(campaignRemoteManager).closeCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignClosingParams({\n campaignId: campaignId\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n emit CampaignClosed(_campaignId);\n }\n\n /// @notice calculate the fee amount and transfer it to the feeCollector\n /// @return Balance after fee\n function _handleFee() internal returns (uint256) {\n // Cache current rewardToken balance\n uint256 balance = IERC20(rewardToken).balanceOf(address(this));\n require(balance > 0, \"No reward to manage\");\n\n uint256 feeAmount = (balance * fee) / FEE_BASE;\n\n // If there is a fee, transfer it to the feeCollector\n if (feeAmount > 0) {\n // Transfer the fee to the feeCollector\n IERC20(rewardToken).safeTransfer(feeCollector, feeAmount);\n emit FeeCollected(feeCollector, feeAmount);\n\n return IERC20(rewardToken).balanceOf(address(this));\n }\n\n // Return remaining balance\n return balance;\n }\n\n ////////////////////////////////////////////////////\n /// --- GOVERNANCE && OPERATION\n ////////////////////////////////////////////////////\n /// @notice Set the campaign id\n /// @dev Only callable by the governor or strategist\n /// @param _campaignId New campaign id\n function setCampaignId(uint256 _campaignId)\n external\n onlyGovernorOrStrategist\n {\n campaignId = _campaignId;\n emit CampaignIdUpdated(_campaignId);\n }\n\n /// @notice Rescue ETH from the contract\n /// @dev Only callable by the governor or strategist\n /// @param receiver Address to receive the ETH\n function rescueETH(address receiver)\n external\n nonReentrant\n onlyGovernorOrStrategist\n {\n require(receiver != address(0), \"Invalid receiver\");\n uint256 balance = address(this).balance;\n (bool success, ) = receiver.call{ value: balance }(\"\");\n require(success, \"Transfer failed\");\n emit TokensRescued(address(0), balance, receiver);\n }\n\n /// @notice Rescue ERC20 tokens from the contract\n /// @dev Only callable by the governor or strategist\n /// @param token Address of the token to rescue\n function rescueToken(address token, address receiver)\n external\n nonReentrant\n onlyGovernor\n {\n require(receiver != address(0), \"Invalid receiver\");\n uint256 balance = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransfer(receiver, balance);\n emit TokensRescued(token, balance, receiver);\n }\n\n /// @notice Set the fee\n /// @dev Only callable by the governor\n /// @param _fee New fee\n function setFee(uint16 _fee) external onlyGovernor {\n _setFee(_fee);\n }\n\n /// @notice Internal logic to set the fee\n function _setFee(uint16 _fee) internal {\n require(_fee <= FEE_BASE / 2, \"Fee too high\");\n fee = _fee;\n emit FeeUpdated(_fee);\n }\n\n /// @notice Set the fee collector\n /// @dev Only callable by the governor\n /// @param _feeCollector New fee collector\n function setFeeCollector(address _feeCollector) external onlyGovernor {\n _setFeeCollector(_feeCollector);\n }\n\n /// @notice Internal logic to set the fee collector\n function _setFeeCollector(address _feeCollector) internal {\n require(_feeCollector != address(0), \"Invalid fee collector\");\n feeCollector = _feeCollector;\n emit FeeCollectorUpdated(_feeCollector);\n }\n\n /// @notice Set the campaignRemoteManager\n /// @param _campaignRemoteManager New campaignRemoteManager address\n function setCampaignRemoteManager(address _campaignRemoteManager)\n external\n onlyGovernor\n {\n _setCampaignRemoteManager(_campaignRemoteManager);\n }\n\n /// @notice Internal logic to set the campaignRemoteManager\n /// @param _campaignRemoteManager New campaignRemoteManager address\n function _setCampaignRemoteManager(address _campaignRemoteManager)\n internal\n {\n require(\n _campaignRemoteManager != address(0),\n \"Invalid campaignRemoteManager\"\n );\n campaignRemoteManager = _campaignRemoteManager;\n emit CampaignRemoteManagerUpdated(_campaignRemoteManager);\n }\n\n /// @notice Set the votemarket address\n /// @param _votemarket New votemarket address\n function setVotemarket(address _votemarket) external onlyGovernor {\n _setVotemarket(_votemarket);\n }\n\n /// @notice Internal logic to set the votemarket address\n function _setVotemarket(address _votemarket) internal onlyGovernor {\n require(_votemarket != address(0), \"Invalid votemarket\");\n votemarket = _votemarket;\n emit VotemarketUpdated(_votemarket);\n }\n\n receive() external payable {}\n}\n" + }, + "contracts/strategies/Generalized4626Strategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Generalized 4626 Strategy\n * @notice Investment strategy for ERC-4626 Tokenized Vaults\n * @author Origin Protocol Inc\n */\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\ncontract Generalized4626Strategy is InitializableAbstractStrategy {\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecate_shareToken;\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecate_assetToken;\n\n IERC20 public immutable shareToken;\n IERC20 public immutable assetToken;\n\n // For future use\n uint256[50] private __gap;\n\n /**\n * @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n * and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n * @param _assetToken Address of the ERC-4626 asset token. eg frxETH or DAI\n */\n constructor(BaseStrategyConfig memory _baseConfig, address _assetToken)\n InitializableAbstractStrategy(_baseConfig)\n {\n shareToken = IERC20(_baseConfig.platformAddress);\n assetToken = IERC20(_assetToken);\n }\n\n function initialize() external virtual onlyGovernor initializer {\n address[] memory rewardTokens = new address[](0);\n address[] memory assets = new address[](1);\n address[] memory pTokens = new address[](1);\n\n assets[0] = address(assetToken);\n pTokens[0] = address(platformAddress);\n\n InitializableAbstractStrategy._initialize(\n rewardTokens,\n assets,\n pTokens\n );\n }\n\n /**\n * @dev Deposit assets by converting them to shares\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit assets by converting them to shares\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal virtual {\n require(_amount > 0, \"Must deposit something\");\n require(_asset == address(assetToken), \"Unexpected asset address\");\n\n // slither-disable-next-line unused-return\n IERC4626(platformAddress).deposit(_amount, address(this));\n emit Deposit(_asset, address(shareToken), _amount);\n }\n\n /**\n * @dev Deposit the entire balance of assetToken to gain shareToken\n */\n function depositAll() external virtual override onlyVault nonReentrant {\n uint256 balance = assetToken.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(assetToken), balance);\n }\n }\n\n /**\n * @dev Withdraw asset by burning shares\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n require(_asset == address(assetToken), \"Unexpected asset address\");\n\n // slither-disable-next-line unused-return\n IERC4626(platformAddress).withdraw(_amount, _recipient, address(this));\n emit Withdrawal(_asset, address(shareToken), _amount);\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / share tokens\n */\n function _abstractSetPToken(address, address) internal virtual override {\n _approveBase();\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll()\n external\n virtual\n override\n onlyVaultOrGovernor\n nonReentrant\n {\n uint256 shareBalance = shareToken.balanceOf(address(this));\n uint256 assetAmount = 0;\n if (shareBalance > 0) {\n assetAmount = IERC4626(platformAddress).redeem(\n shareBalance,\n vaultAddress,\n address(this)\n );\n emit Withdrawal(\n address(assetToken),\n address(shareToken),\n assetAmount\n );\n }\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(_asset == address(assetToken), \"Unexpected asset address\");\n /* We are intentionally not counting the amount of assetToken parked on the\n * contract toward the checkBalance. The deposit and withdraw functions\n * should not result in assetToken being unused and owned by this strategy\n * contract.\n */\n IERC4626 platform = IERC4626(platformAddress);\n return platform.previewRedeem(platform.balanceOf(address(this)));\n }\n\n /**\n * @notice Governor approves the ERC-4626 Tokenized Vault to spend the asset.\n */\n function safeApproveAllTokens() external override onlyGovernor {\n _approveBase();\n }\n\n function _approveBase() internal virtual {\n // Approval the asset to be transferred to the ERC-4626 Tokenized Vault.\n // Used by the ERC-4626 deposit() and mint() functions\n // slither-disable-next-line unused-return\n assetToken.approve(platformAddress, type(uint256).max);\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset)\n public\n view\n virtual\n override\n returns (bool)\n {\n return _asset == address(assetToken);\n }\n\n /**\n * @notice is not supported for this strategy as the asset and\n * ERC-4626 Tokenized Vault are set at deploy time.\n * @dev If the ERC-4626 Tokenized Vault needed to be changed, a new\n * contract would need to be deployed and the proxy updated.\n */\n function setPTokenAddress(address, address) external override onlyGovernor {\n revert(\"unsupported function\");\n }\n\n /**\n * @notice is not supported for this strategy as the asset and\n * ERC-4626 Tokenized Vault are set at deploy time.\n * @dev If the ERC-4626 Tokenized Vault needed to be changed, a new\n * contract would need to be deployed and the proxy updated.\n */\n function removePToken(uint256) external override onlyGovernor {\n revert(\"unsupported function\");\n }\n}\n" + }, + "contracts/strategies/Generalized4626USDTStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IUSDT {\n // Tether's approve does not return a bool like standard IERC20 contracts\n // slither-disable-next-line erc20-interface\n function approve(address _spender, uint256 _value) external;\n}\n\n/**\n * @title Generalized 4626 Strategy when asset is Tether USD (USDT)\n * @notice Investment strategy for ERC-4626 Tokenized Vaults for the USDT asset.\n * @author Origin Protocol Inc\n */\nimport { Generalized4626Strategy } from \"./Generalized4626Strategy.sol\";\n\ncontract Generalized4626USDTStrategy is Generalized4626Strategy {\n /**\n * @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n * and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n * @param _assetToken Address of the ERC-4626 asset token. eg frxETH or DAI\n */\n constructor(BaseStrategyConfig memory _baseConfig, address _assetToken)\n Generalized4626Strategy(_baseConfig, _assetToken)\n {}\n\n /// @dev Override for Tether as USDT does not return a bool on approve.\n /// Using assetToken.approve will fail as it expects a bool return value\n function _approveBase() internal virtual override {\n // Approval the asset to be transferred to the ERC-4626 Tokenized Vault.\n // Used by the ERC-4626 deposit() and mint() functions\n // slither-disable-next-line unused-return\n IUSDT(address(assetToken)).approve(platformAddress, type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/IAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface for Aaves Lending Pool\n * Documentation: https://developers.aave.com/#lendingpool\n */\ninterface IAaveLendingPool {\n /**\n * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.\n * - E.g. User deposits 100 USDC and gets in return 100 aUSDC\n * @param asset The address of the underlying asset to deposit\n * @param amount The amount to be deposited\n * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user\n * wants to receive them on his own wallet, or a different address if the beneficiary of aTokens\n * is a different wallet\n * @param referralCode Code used to register the integrator originating the operation, for potential rewards.\n * 0 if the action is executed directly by the user, without any middle-man\n **/\n function deposit(\n address asset,\n uint256 amount,\n address onBehalfOf,\n uint16 referralCode\n ) external;\n\n /**\n * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned\n * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC\n * @param asset The address of the underlying asset to withdraw\n * @param amount The underlying amount to be withdrawn\n * - Send the value type(uint256).max in order to withdraw the whole aToken balance\n * @param to Address that will receive the underlying, same as msg.sender if the user\n * wants to receive it on his own wallet, or a different address if the beneficiary is a\n * different wallet\n * @return The final amount withdrawn\n **/\n function withdraw(\n address asset,\n uint256 amount,\n address to\n ) external returns (uint256);\n}\n\n/**\n * @dev Interface for Aaves Lending Pool\n * Documentation: https://developers.aave.com/#lendingpooladdressesprovider\n */\ninterface ILendingPoolAddressesProvider {\n /**\n * @notice Get the current address for Aave LendingPool\n * @dev Lending pool is the core contract on which to call deposit\n */\n function getLendingPool() external view returns (address);\n}\n" + }, + "contracts/strategies/IAaveIncentivesController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IAaveIncentivesController {\n event RewardsAccrued(address indexed user, uint256 amount);\n\n event RewardsClaimed(\n address indexed user,\n address indexed to,\n uint256 amount\n );\n\n event RewardsClaimed(\n address indexed user,\n address indexed to,\n address indexed claimer,\n uint256 amount\n );\n\n event ClaimerSet(address indexed user, address indexed claimer);\n\n /*\n * @dev Returns the configuration of the distribution for a certain asset\n * @param asset The address of the reference asset of the distribution\n * @return The asset index, the emission per second and the last updated timestamp\n **/\n function getAssetData(address asset)\n external\n view\n returns (\n uint256,\n uint256,\n uint256\n );\n\n /**\n * @dev Whitelists an address to claim the rewards on behalf of another address\n * @param user The address of the user\n * @param claimer The address of the claimer\n */\n function setClaimer(address user, address claimer) external;\n\n /**\n * @dev Returns the whitelisted claimer for a certain address (0x0 if not set)\n * @param user The address of the user\n * @return The claimer address\n */\n function getClaimer(address user) external view returns (address);\n\n /**\n * @dev Configure assets for a certain rewards emission\n * @param assets The assets to incentivize\n * @param emissionsPerSecond The emission for each asset\n */\n function configureAssets(\n address[] calldata assets,\n uint256[] calldata emissionsPerSecond\n ) external;\n\n /**\n * @dev Called by the corresponding asset on any update that affects the rewards distribution\n * @param asset The address of the user\n * @param userBalance The balance of the user of the asset in the lending pool\n * @param totalSupply The total supply of the asset in the lending pool\n **/\n function handleAction(\n address asset,\n uint256 userBalance,\n uint256 totalSupply\n ) external;\n\n /**\n * @dev Returns the total of rewards of an user, already accrued + not yet accrued\n * @param user The address of the user\n * @return The rewards\n **/\n function getRewardsBalance(address[] calldata assets, address user)\n external\n view\n returns (uint256);\n\n /**\n * @dev Claims reward for an user, on all the assets of the lending pool,\n * accumulating the pending rewards\n * @param amount Amount of rewards to claim\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewards(\n address[] calldata assets,\n uint256 amount,\n address to\n ) external returns (uint256);\n\n /**\n * @dev Claims reward for an user on behalf, on all the assets of the\n * lending pool, accumulating the pending rewards. The caller must\n * be whitelisted via \"allowClaimOnBehalf\" function by the RewardsAdmin role manager\n * @param amount Amount of rewards to claim\n * @param user Address to check and claim rewards\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewardsOnBehalf(\n address[] calldata assets,\n uint256 amount,\n address user,\n address to\n ) external returns (uint256);\n\n /**\n * @dev returns the unclaimed rewards of the user\n * @param user the address of the user\n * @return the unclaimed user rewards\n */\n function getUserUnclaimedRewards(address user)\n external\n view\n returns (uint256);\n\n /**\n * @dev returns the unclaimed rewards of the user\n * @param user the address of the user\n * @param asset The asset to incentivize\n * @return the user index for the asset\n */\n function getUserAssetData(address user, address asset)\n external\n view\n returns (uint256);\n\n /**\n * @dev for backward compatibility with previous implementation of the Incentives controller\n */\n function REWARD_TOKEN() external view returns (address);\n\n /**\n * @dev for backward compatibility with previous implementation of the Incentives controller\n */\n function PRECISION() external view returns (uint8);\n\n /**\n * @dev Gets the distribution end timestamp of the emissions\n */\n function DISTRIBUTION_END() external view returns (uint256);\n}\n" + }, + "contracts/strategies/IAaveStakeToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IAaveStakedToken {\n function COOLDOWN_SECONDS() external returns (uint256);\n\n function UNSTAKE_WINDOW() external returns (uint256);\n\n function balanceOf(address addr) external returns (uint256);\n\n function redeem(address to, uint256 amount) external;\n\n function stakersCooldowns(address addr) external returns (uint256);\n\n function cooldown() external;\n}\n" + }, + "contracts/strategies/ICompound.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @dev Compound C Token interface\n * Documentation: https://compound.finance/developers/ctokens\n */\ninterface ICERC20 {\n /**\n * @notice The mint function transfers an asset into the protocol, which begins accumulating\n * interest based on the current Supply Rate for the asset. The user receives a quantity of\n * cTokens equal to the underlying tokens supplied, divided by the current Exchange Rate.\n * @param mintAmount The amount of the asset to be supplied, in units of the underlying asset.\n * @return 0 on success, otherwise an Error codes\n */\n function mint(uint256 mintAmount) external returns (uint256);\n\n /**\n * @notice Sender redeems cTokens in exchange for the underlying asset\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param redeemTokens The number of cTokens to redeem into underlying\n * @return uint 0=success, otherwise an error code.\n */\n function redeem(uint256 redeemTokens) external returns (uint256);\n\n /**\n * @notice The redeem underlying function converts cTokens into a specified quantity of the underlying\n * asset, and returns them to the user. The amount of cTokens redeemed is equal to the quantity of\n * underlying tokens received, divided by the current Exchange Rate. The amount redeemed must be less\n * than the user's Account Liquidity and the market's available liquidity.\n * @param redeemAmount The amount of underlying to be redeemed.\n * @return 0 on success, otherwise an error code.\n */\n function redeemUnderlying(uint256 redeemAmount) external returns (uint256);\n\n /**\n * @notice The user's underlying balance, representing their assets in the protocol, is equal to\n * the user's cToken balance multiplied by the Exchange Rate.\n * @param owner The account to get the underlying balance of.\n * @return The amount of underlying currently owned by the account.\n */\n function balanceOfUnderlying(address owner) external returns (uint256);\n\n /**\n * @notice Calculates the exchange rate from the underlying to the CToken\n * @dev This function does not accrue interest before calculating the exchange rate\n * @return Calculated exchange rate scaled by 1e18\n */\n function exchangeRateStored() external view returns (uint256);\n\n /**\n * @notice Get the token balance of the `owner`\n * @param owner The address of the account to query\n * @return The number of tokens owned by `owner`\n */\n function balanceOf(address owner) external view returns (uint256);\n\n /**\n * @notice Get the supply rate per block for supplying the token to Compound.\n */\n function supplyRatePerBlock() external view returns (uint256);\n\n /**\n * @notice Address of the Compound Comptroller.\n */\n function comptroller() external view returns (address);\n}\n" + }, + "contracts/strategies/IConvexDeposits.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IConvexDeposits {\n function deposit(\n uint256 _pid,\n uint256 _amount,\n bool _stake\n ) external returns (bool);\n\n function deposit(\n uint256 _amount,\n bool _lock,\n address _stakeAddress\n ) external;\n}\n" + }, + "contracts/strategies/ICRVMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICRVMinter {\n function mint(address gaugeAddress) external;\n}\n" + }, + "contracts/strategies/ICurveETHPoolV1.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurveETHPoolV1 {\n event AddLiquidity(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event ApplyNewFee(uint256 fee);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event CommitNewFee(uint256 new_fee);\n event RampA(\n uint256 old_A,\n uint256 new_A,\n uint256 initial_time,\n uint256 future_time\n );\n event RemoveLiquidity(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 token_supply\n );\n event RemoveLiquidityImbalance(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event RemoveLiquidityOne(\n address indexed provider,\n uint256 token_amount,\n uint256 coin_amount,\n uint256 token_supply\n );\n event StopRampA(uint256 A, uint256 t);\n event TokenExchange(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event Transfer(\n address indexed sender,\n address indexed receiver,\n uint256 value\n );\n\n function A() external view returns (uint256);\n\n function A_precise() external view returns (uint256);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_liquidity(uint256[2] memory _amounts, uint256 _min_mint_amount)\n external\n payable\n returns (uint256);\n\n function add_liquidity(\n uint256[2] memory _amounts,\n uint256 _min_mint_amount,\n address _receiver\n ) external payable returns (uint256);\n\n function admin_action_deadline() external view returns (uint256);\n\n function admin_balances(uint256 i) external view returns (uint256);\n\n function admin_fee() external view returns (uint256);\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function apply_new_fee() external;\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function balances(uint256 arg0) external view returns (uint256);\n\n function calc_token_amount(uint256[2] memory _amounts, bool _is_deposit)\n external\n view\n returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _burn_amount, int128 i)\n external\n view\n returns (uint256);\n\n function coins(uint256 arg0) external view returns (address);\n\n function commit_new_fee(uint256 _new_fee) external;\n\n function decimals() external view returns (uint256);\n\n function ema_price() external view returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external payable returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external payable returns (uint256);\n\n function fee() external view returns (uint256);\n\n function future_A() external view returns (uint256);\n\n function future_A_time() external view returns (uint256);\n\n function future_fee() external view returns (uint256);\n\n function get_balances() external view returns (uint256[2] memory);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n\n function get_p() external view returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function initial_A() external view returns (uint256);\n\n function initial_A_time() external view returns (uint256);\n\n function initialize(\n string memory _name,\n string memory _symbol,\n address[4] memory _coins,\n uint256[4] memory _rate_multipliers,\n uint256 _A,\n uint256 _fee\n ) external;\n\n function last_price() external view returns (uint256);\n\n function ma_exp_time() external view returns (uint256);\n\n function ma_last_time() external view returns (uint256);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function price_oracle() external view returns (uint256);\n\n function ramp_A(uint256 _future_A, uint256 _future_time) external;\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[2] memory _min_amounts\n ) external returns (uint256[2] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[2] memory _min_amounts,\n address _receiver\n ) external returns (uint256[2] memory);\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burn_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burn_amount,\n address _receiver\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received,\n address _receiver\n ) external returns (uint256);\n\n function set_ma_exp_time(uint256 _ma_exp_time) external;\n\n function stop_ramp_A() external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw_admin_fees() external;\n}\n" + }, + "contracts/strategies/ICurveGauge.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurveGauge {\n function balanceOf(address account) external view returns (uint256);\n\n function deposit(uint256 value, address account) external;\n\n function withdraw(uint256 value) external;\n}\n" + }, + "contracts/strategies/ICurveMetaPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface ICurveMetaPool {\n function add_liquidity(uint256[2] calldata amounts, uint256 min_mint_amount)\n external\n returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function remove_liquidity(uint256 _amount, uint256[2] calldata min_amounts)\n external\n returns (uint256[2] calldata);\n\n function remove_liquidity_one_coin(\n uint256 _token_amount,\n int128 i,\n uint256 min_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[2] calldata amounts,\n uint256 max_burn_amount\n ) external returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _token_amount, int128 i)\n external\n view\n returns (uint256);\n\n function balances(uint256 i) external view returns (uint256);\n\n function calc_token_amount(uint256[2] calldata amounts, bool deposit)\n external\n view\n returns (uint256);\n\n function base_pool() external view returns (address);\n\n function fee() external view returns (uint256);\n\n function coins(uint256 i) external view returns (address);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 dx,\n uint256 min_dy\n ) external returns (uint256);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n}\n" + }, + "contracts/strategies/ICurvePool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurvePool {\n function get_virtual_price() external view returns (uint256);\n\n function add_liquidity(uint256[3] calldata _amounts, uint256 _min) external;\n\n function balances(uint256) external view returns (uint256);\n\n function calc_token_amount(uint256[3] calldata _amounts, bool _deposit)\n external\n returns (uint256);\n\n function fee() external view returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _amount,\n int128 _index,\n uint256 _minAmount\n ) external;\n\n function remove_liquidity(\n uint256 _amount,\n uint256[3] calldata _minWithdrawAmounts\n ) external;\n\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n external\n view\n returns (uint256);\n\n function exchange(\n uint256 i,\n uint256 j,\n uint256 dx,\n uint256 min_dy\n ) external returns (uint256);\n\n function coins(uint256 _index) external view returns (address);\n\n function remove_liquidity_imbalance(\n uint256[3] calldata _amounts,\n uint256 maxBurnAmount\n ) external;\n}\n" + }, + "contracts/strategies/IRewardStaking.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IRewardStaking {\n function stakeFor(address, uint256) external;\n\n function stake(uint256) external;\n\n function withdraw(uint256 amount, bool claim) external;\n\n function withdrawAndUnwrap(uint256 amount, bool claim) external;\n\n function earned(address account) external view returns (uint256);\n\n function getReward() external;\n\n function getReward(address _account, bool _claimExtras) external;\n\n function extraRewardsLength() external returns (uint256);\n\n function extraRewards(uint256 _pid) external returns (address);\n\n function rewardToken() external returns (address);\n\n function balanceOf(address account) external view returns (uint256);\n}\n" + }, + "contracts/strategies/MorphoAaveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Morpho Aave Strategy\n * @notice Investment strategy for investing stablecoins via Morpho (Aave)\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { IMorpho } from \"../interfaces/morpho/IMorpho.sol\";\nimport { ILens } from \"../interfaces/morpho/ILens.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MorphoAaveStrategy is InitializableAbstractStrategy {\n address public constant MORPHO = 0x777777c9898D384F785Ee44Acfe945efDFf5f3E0;\n address public constant LENS = 0x507fA343d0A90786d86C7cd885f5C49263A91FF4;\n\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @dev Initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Approve the spending of all assets by main Morpho contract,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address asset = assetsMapped[i];\n\n // Safe approval\n IERC20(asset).safeApprove(MORPHO, 0);\n IERC20(asset).safeApprove(MORPHO, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset\n * We need to approve and allow Morpho to move them\n * @param _asset Address of the asset to approve\n * @param _pToken The pToken for the approval\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n IERC20(_asset).safeApprove(MORPHO, 0);\n IERC20(_asset).safeApprove(MORPHO, type(uint256).max);\n }\n\n /**\n * @dev Collect accumulated rewards and send them to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Morpho Aave-v2 doesn't distribute reward tokens\n // solhint-disable-next-line max-line-length\n // Ref: https://developers.morpho.xyz/interact-with-morpho/get-started/interact-with-morpho/claim-rewards#morpho-aave-v2\n }\n\n /**\n * @dev Get the amount of rewards pending to be collected from the protocol\n */\n function getPendingRewards() external view returns (uint256 balance) {\n // Morpho Aave-v2 doesn't distribute reward tokens\n // solhint-disable-next-line max-line-length\n // Ref: https://developers.morpho.xyz/interact-with-morpho/get-started/interact-with-morpho/claim-rewards#morpho-aave-v2\n return 0;\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n\n address pToken = address(_getPTokenFor(_asset));\n\n IMorpho(MORPHO).supply(\n pToken,\n address(this), // the address of the user you want to supply on behalf of\n _amount\n );\n emit Deposit(_asset, pToken, _amount);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Morpho\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Morpho\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n address pToken = address(_getPTokenFor(_asset));\n\n IMorpho(MORPHO).withdraw(pToken, _amount);\n emit Withdrawal(_asset, pToken, _amount);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = _checkBalance(assetsMapped[i]);\n if (balance > 0) {\n _withdraw(vaultAddress, assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Return total value of an asset held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n return _checkBalance(_asset);\n }\n\n function _checkBalance(address _asset)\n internal\n view\n returns (uint256 balance)\n {\n address pToken = address(_getPTokenFor(_asset));\n\n // Total value represented by decimal position of underlying token\n (, , balance) = ILens(LENS).getCurrentSupplyBalanceInOf(\n pToken,\n address(this)\n );\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Get the pToken wrapped in the IERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return pToken Corresponding pToken to this asset\n */\n function _getPTokenFor(address _asset) internal view returns (IERC20) {\n address pToken = assetToPToken[_asset];\n require(pToken != address(0), \"pToken does not exist\");\n return IERC20(pToken);\n }\n}\n" + }, + "contracts/strategies/MorphoCompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Morpho Compound Strategy\n * @notice Investment strategy for investing stablecoins via Morpho (Compound)\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, AbstractCompoundStrategy, InitializableAbstractStrategy } from \"./AbstractCompoundStrategy.sol\";\nimport { IMorpho } from \"../interfaces/morpho/IMorpho.sol\";\nimport { ILens } from \"../interfaces/morpho/ILens.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MorphoCompoundStrategy is AbstractCompoundStrategy {\n address public constant MORPHO = 0x8888882f8f843896699869179fB6E4f7e3B58888;\n address public constant LENS = 0x930f1b46e1D081Ec1524efD95752bE3eCe51EF67;\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @dev Initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Approve the spending of all assets by main Morpho contract,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address asset = assetsMapped[i];\n\n // Safe approval\n IERC20(asset).safeApprove(MORPHO, 0);\n IERC20(asset).safeApprove(MORPHO, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset\n * We need to approve and allow Morpho to move them\n * @param _asset Address of the asset to approve\n * @param _pToken The pToken for the approval\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n IERC20(_asset).safeApprove(MORPHO, 0);\n IERC20(_asset).safeApprove(MORPHO, type(uint256).max);\n }\n\n /**\n * @dev Collect accumulated rewards and send them to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n /**\n * Gas considerations. We could query Morpho LENS's `getUserUnclaimedRewards` for each\n * cToken separately and only claimRewards where it is economically feasible. Each call\n * (out of 3) costs ~60k gas extra.\n *\n * Each extra cToken in the `poolTokens` of `claimRewards` function makes that call\n * 89-120k more expensive gas wise.\n *\n * With Lens query in case where:\n * - there is only 1 reward token to collect. Net gas usage in best case would be\n * 3*60 - 2*120 = -60k -> saving 60k gas\n * - there are 2 reward tokens to collect. Net gas usage in best case would be\n * 3*60 - 120 = 60k -> more expensive for 60k gas\n * - there are 3 reward tokens to collect. Net gas usage in best case would be\n * 3*60 = 180k -> more expensive for 180k gas\n *\n * For the above reasoning such \"optimization\" is not implemented\n */\n\n address[] memory poolTokens = new address[](assetsMapped.length);\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n poolTokens[i] = assetToPToken[assetsMapped[i]];\n }\n\n // slither-disable-next-line unused-return\n IMorpho(MORPHO).claimRewards(\n poolTokens, // The addresses of the underlying protocol's pools to claim rewards from\n false // Whether to trade the accrued rewards for MORPHO token, with a premium\n );\n\n // Transfer COMP to Harvester\n IERC20 rewardToken = IERC20(rewardTokenAddresses[0]);\n uint256 balance = rewardToken.balanceOf(address(this));\n emit RewardTokenCollected(\n harvesterAddress,\n rewardTokenAddresses[0],\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n\n /**\n * @dev Get the amount of rewards pending to be collected from the protocol\n */\n function getPendingRewards() external view returns (uint256 balance) {\n address[] memory poolTokens = new address[](assetsMapped.length);\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n poolTokens[i] = assetToPToken[assetsMapped[i]];\n }\n\n return ILens(LENS).getUserUnclaimedRewards(poolTokens, address(this));\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n\n IMorpho(MORPHO).supply(\n address(_getCTokenFor(_asset)),\n address(this), // the address of the user you want to supply on behalf of\n _amount\n );\n emit Deposit(_asset, address(_getCTokenFor(_asset)), _amount);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Morpho\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Morpho\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n address pToken = assetToPToken[_asset];\n\n IMorpho(MORPHO).withdraw(pToken, _amount);\n emit Withdrawal(_asset, address(_getCTokenFor(_asset)), _amount);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = _checkBalance(assetsMapped[i]);\n if (balance > 0) {\n _withdraw(vaultAddress, assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Return total value of an asset held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n return _checkBalance(_asset);\n }\n\n function _checkBalance(address _asset)\n internal\n view\n returns (uint256 balance)\n {\n address pToken = assetToPToken[_asset];\n\n // Total value represented by decimal position of underlying token\n (, , balance) = ILens(LENS).getCurrentSupplyBalanceInOf(\n pToken,\n address(this)\n );\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconProofs,\n _beaconGenesisTimestamp\n )\n {\n SSV_TOKEN = _ssvToken;\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// @param _rewardTokenAddresses Not used so empty array\n /// @param _assets Not used so empty array\n /// @param _pTokens Not used so empty array\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH that was transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _asset, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(_asset, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, WETH, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Accounts for all the assets managed by this strategy which includes:\n /// 1. The current WETH in this strategy contract\n /// 2. The last verified ETH balance, total deposits and total validator balances\n /// @param _asset Address of WETH asset.\n /// @return balance Total value in ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by the strategy.\n /// @param _asset The address of the WETH token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for validator registration.\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens when validators are registered\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n * The new ETH will be accounted for in `checkBalance` after the next snapBalances and verifyBalances txs.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n /// @dev This strategy does not use a platform token like the old Aave and Compound strategies.\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instead of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract.\n /// Withdrawals from validators also accumulate as ETH in this strategy contract.\n /// It's too complex to separate the rewards from withdrawals so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can now regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable {\n using SafeERC20 for IERC20;\n\n /// @notice The amount of ETH in wei that is required for a deposit to a new validator.\n /// Initially this is 32 ETH, but will be increased to 1 ETH after P2P's APIs have been updated\n /// to support deposits of 1 ETH.\n uint256 public constant DEPOSIT_AMOUNT_WEI = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n /// @notice The timestamp of the Beacon chain genesis.\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\n uint64 public immutable BEACON_GENESIS_TIMESTAMP;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// Deposit data for new compounding validators.\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountWei Amount of ETH in wei that has been deposited to the beacon chain deposit contract\n /// @param blockNumber Block number when the deposit was made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either PENDING or PROVEN\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint32 depositIndex;\n DepositStatus status;\n }\n /// @notice Mapping of the root of a deposit (depositDataRoot) to its data\n mapping(bytes32 => DepositData) public deposits;\n /// @notice List of deposit roots that are still to be verified as processed on the beacon chain\n bytes32[] public depositsRoots;\n\n // Validator data\n\n struct ValidatorData {\n bytes32 pubKeyHash; // Hash of the validator's public key using the Beacon Chain's format\n uint64 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes and indexes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n ValidatorData[] internal verifiedValidators;\n /// @notice State of the new compounding validators with a 0x02 withdrawal credential prefix.\n /// Uses the Beacon chain hashing for BLSPubkey which is\n /// sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => VALIDATOR_STATE) public validatorState;\n\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n uint64 timestamp;\n uint128 ethBalance;\n }\n // TODO is it more efficient to use the block root rather than hashing it?\n /// @notice Mapping of the block root to the balances at that slot\n mapping(bytes32 => Balances) public snappedBalances;\n uint64 public lastSnapTimestamp;\n uint128 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[50] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITED, // The validator has been verified to have a zero balance\n REMOVED // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified and is ready to be staked\n }\n\n event RegistratorChanged(address indexed newAddress);\n event SourceStrategyAdded(address indexed strategy);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n bytes32 indexed depositDataRoot,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint64 indexed validatorIndex\n );\n event DepositVerified(bytes32 indexed depositDataRoot, uint256 amountWei);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(\n uint256 indexed timestamp,\n bytes32 indexed blockRoot,\n uint256 ethBalance\n );\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 wethBalance,\n uint256 ethBalance\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_PROOFS = _beaconProofs;\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\n\n require(\n block.timestamp > _beaconGenesisTimestamp,\n \"Invalid genesis timestamp\"\n );\n }\n\n /***************************************\n Admin Functions\n ****************************************/\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /***************************************`\n Validator Management\n ****************************************/\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validatorState[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validator validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validator,\n uint64 depositAmountGwei\n ) external onlyRegistrator {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validator.pubkey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == VALIDATOR_STATE.REGISTERED ||\n currentState == VALIDATOR_STATE.VERIFIED),\n \"Not registered or verified\"\n );\n require(\n currentState == VALIDATOR_STATE.VERIFIED ||\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validator.pubkey,\n withdrawalCredentials,\n validator.signature,\n validator.depositDataRoot\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == VALIDATOR_STATE.REGISTERED) {\n validatorState[pubKeyHash] = VALIDATOR_STATE.STAKED;\n }\n\n /// After the Pectra upgrade the validators have a new restriction when proposing\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\n /// forward. Each slot is created at strict 12 second intervals and those slots can\n /// either have blocks attached to them or not. This way using the block.timestamp\n /// the slot number can easily be calculated.\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\n BEACON_GENESIS_TIMESTAMP) / 12;\n\n // Store the deposit data for verifyDeposit and verifyBalances\n deposits[validator.depositDataRoot] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n slot: depositSlot,\n depositIndex: SafeCast.toUint32(depositsRoots.length),\n status: DepositStatus.PENDING\n });\n depositsRoots.push(validator.depositDataRoot);\n\n emit ETHStaked(\n pubKeyHash,\n validator.depositDataRoot,\n validator.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then the request will fail.\n /// Only the Registrator can call this function.\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\n /// If no ETH balance, the tx will revert.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n payable\n onlyRegistrator\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove a sweeping validator from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == VALIDATOR_STATE.EXITED ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exited\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validatorState[pubKeyHash] = VALIDATOR_STATE.REMOVED;\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /***************************************\n SSV Management\n ****************************************/\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n Beacon Chain Proofs\n ****************************************/\n\n /// @notice Verifies a validator's index to its public key.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param validatorPubKeyProof The merkle proof that the validator's index matches its public key\n /// on the beacon chain.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.STAKED,\n \"Validator not staked\"\n );\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key.\n // Also verify the validator's withdrawal credential points to this strategy.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n address(this) // Withdrawal address is this strategy\n );\n\n // Store the validator state as verified\n validatorState[pubKeyHash] = VALIDATOR_STATE.VERIFIED;\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(\n ValidatorData({ pubKeyHash: pubKeyHash, index: validatorIndex })\n );\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n ///\n /// Important: this function has a limitation where the `verificationSlot` that is passed by the off-chain\n /// verifier requires a slot immediately after it to propose a block otherwise the `BeaconRoots.parentBlockRoot`\n /// will fail. This shouldn't be a problem, since by the current behaviour of beacon chain only 1%-3% slots\n /// don't propose a block.\n /// @param depositDataRoot The root of the deposit data that was stored when\n /// the deposit was made on the execution layer.\n /// @param verificationSlot Any slot on or after the deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\n /// set for the next block timestamp in 12 seconds time.\n /// @param firstPendingDepositSlot The slot of the first pending deposit in the beacon chain\n /// @param firstPendingDepositSlotProof The merkle proof that the slot of the first pending deposit\n /// matches the beacon chain.\n /// BeaconBlock.BeaconBlockBody.deposits[0].slot\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n bytes32 depositDataRoot,\n uint64 verificationSlot,\n uint64 firstPendingDepositSlot,\n bytes calldata firstPendingDepositSlotProof\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositDataRoot];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n validatorState[deposit.pubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the first pending deposit's slot is 0 then there are no pending deposits so\n // our deposit must have been processed on the beacon chain.\n require(\n deposit.slot < firstPendingDepositSlot ||\n firstPendingDepositSlot == 0,\n \"Deposit not processed\"\n );\n\n // The verification slot must be after the deposit's slot.\n // This is needed for when the deposit queue is empty. That is, firstPendingDepositSlot is 0.\n require(deposit.slot < verificationSlot, \"Slot not after deposit\");\n\n // Calculate the next block timestamp from the verification slot.\n uint64 nextBlockTimestamp = 12 *\n verificationSlot +\n BEACON_GENESIS_TIMESTAMP +\n // Add 12 seconds for the next block\n 12;\n // Get the parent beacon block root of the next block which is the block root of the verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the slot of the first pending deposit matches the beacon chain\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n blockRoot,\n firstPendingDepositSlot,\n firstPendingDepositSlotProof\n );\n\n // After verifying the proof, update the contract storage\n deposits[depositDataRoot].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n bytes32 lastDepositDataRoot = depositsRoots[depositsRoots.length - 1];\n depositsRoots[deposit.depositIndex] = lastDepositDataRoot;\n deposits[lastDepositDataRoot].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositsRoots.pop();\n\n emit DepositVerified(\n depositDataRoot,\n uint256(deposit.amountGwei) * 1 gwei\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block and beacon block root\n /// of the slot that is associated with the previous block.\n ///\n /// When snapping / verifying balance it is of a high importance that there is no\n /// miss-match in respect to ETH that is held by the contract and balances that are\n /// verified on the validators.\n ///\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\n /// constructing a block on the beacon chain consist of:\n /// - process_withdrawals: ETH is deducted from the validator's balance\n /// - process_execution_payload: immediately after the previous step executing all the\n /// transactions\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\n /// contained in the withdrawal credentials of the exited validators\n ///\n /// That means that balance increases which are part of the post-block execution state are\n /// done within the block, but the transaction that are contained within that block can not\n /// see / interact with the balance from the exited validators. Only transactions in the\n /// next block can do that.\n ///\n /// When snap balances is performed the state of the chain is snapped across 2 separate\n /// chain states:\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\n /// will be referred to as Y - 1 as it makes no difference in the argument\n ///\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\n /// miss-counting ETH or much more dangerous double counting of the ETH.\n ///\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\n /// - ETH balance in the contract on block X\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\n /// - ETH balance in validators that are active in slot Y - 1\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\n /// and have their balance visible to transactions in slot Y and corresponding block X\n /// (or sooner)\n ///\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\n ///\n /// *ETH balance in the contract on block X*\n ///\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\n ///\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\n /// would result in double counting the `stakedEth` since it would be present once in the\n /// snapped contract balance and the second time in deposit storage variables.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\n ///\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\n /// contract at block Z. The execution layer doesn't have direct access to the state of\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\n /// and could also be part of the validator balances. It does that by verifying that at\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\n /// the last snap till now all are still in queue. Which ensures they can not be part of\n /// the validator balances in later steps.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in validators that are active in slot Y - 1*\n ///\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\n /// correctness.\n ///\n /// This behaviour is correct.\n ///\n /// *The withdrawn validators*\n ///\n /// The withdrawn validators could have their balances deducted in any slot before slot\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\n /// look at the \"worst case scenario\" where the validator withdrawal is processed in the\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\n /// even if the validator exits at the latest possible time it is paramount that the ETH\n /// balance on the execution layer is recorded in the next block. Correctly accounting\n /// for the withdrawn ETH.\n ///\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\n /// validator balance verification as well as execution layer contract balance snap.\n ///\n /// This behaviour is correct.\n ///\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n /// Can only be called by the registrator.\n function snapBalances() external onlyRegistrator {\n _snapBalances();\n }\n\n function _snapBalances() internal {\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(\n SafeCast.toUint64(block.timestamp)\n );\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the balances in the mapping\n snappedBalances[blockRoot] = Balances({\n timestamp: SafeCast.toUint64(block.timestamp),\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n // Store the snapped timestamp\n lastSnapTimestamp = SafeCast.toUint64(block.timestamp);\n\n emit BalancesSnapped(block.timestamp, blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct VerifyBalancesParams {\n bytes32 blockRoot;\n uint64 firstPendingDepositSlot;\n // BeaconBlock.BeaconBlockBody.deposits[0].slot\n bytes firstPendingDepositSlotProof;\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(VerifyBalancesParams calldata params) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalances[params.blockRoot];\n // Check the balances are the latest\n require(lastSnapTimestamp > 0, \"No snapped balances\");\n require(balancesMem.timestamp == lastSnapTimestamp, \"Stale snap\");\n\n uint256 depositsCount = depositsRoots.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit to the beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n params.blockRoot,\n params.firstPendingDepositSlot,\n params.firstPendingDepositSlotProof\n );\n\n // For each native staking contract's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n bytes32 depositDataRoot = depositsRoots[i];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit slot is before the\n // slot of the staking strategy's deposit.\n // If the deposit has been processed, it will need to be verified with `verifyDeposit`\n require(\n params.firstPendingDepositSlot <\n deposits[depositDataRoot].slot,\n \"Deposit has been processed\"\n );\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei +=\n uint256(deposits[depositDataRoot].amountGwei) *\n 1 gwei;\n }\n }\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n params.validatorBalanceProofs.length == verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n params.validatorBalanceLeaves.length == verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n params.blockRoot,\n params.balancesContainerRoot,\n params.balancesContainerProof\n );\n\n // for each validator in reserve order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n params.balancesContainerRoot,\n params.validatorBalanceLeaves[i],\n params.validatorBalanceProofs[i],\n verifiedValidators[i].index\n );\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n validatorState[\n verifiedValidators[i].pubKeyHash\n ] = VALIDATOR_STATE.EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += uint256(validatorBalanceGwei) * 1 gwei;\n }\n }\n\n uint256 wethBalance = IWETH9(WETH).balanceOf(address(this));\n\n // Store the verified balance in storage\n lastVerifiedEthBalance = SafeCast.toUint128(\n totalDepositsWei + totalValidatorBalance + balancesMem.ethBalance\n );\n // Reset the last snap timestamp so a new snapBalances has to be made\n lastSnapTimestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n wethBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /***************************************\n WETH and ETH Accounting\n ****************************************/\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance\n if (lastVerifiedEthBalance > _ethAmount) {\n lastVerifiedEthBalance -= SafeCast.toUint128(_ethAmount);\n } else {\n // This can happen if all ETH in the validators was withdrawn\n // and there was more consensus rewards since the last balance verification.\n // Or it can happen if ETH is donated to this strategy.\n lastVerifiedEthBalance = 0;\n }\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += SafeCast.toUint128(_wethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /***************************************\n View Functions\n ****************************************/\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return An array of `ValidatorData` containing the public key hash and validator index.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorData[] memory)\n {\n return verifiedValidators;\n }\n\n struct DepositView {\n bytes32 root;\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n }\n\n /// @notice Returns the deposits that are still to be verified.\n /// These may or may not have been processed by the beacon chain.\n /// @return pendingDeposits An array of `DepositView` containing the deposit root, public key hash,\n /// amount in Gwei and the slot of the deposit.\n function getPendingDeposits()\n external\n view\n returns (DepositView[] memory pendingDeposits)\n {\n pendingDeposits = new DepositView[](depositsRoots.length);\n for (uint256 i = 0; i < depositsRoots.length; ++i) {\n DepositData memory deposit = deposits[depositsRoots[i]];\n pendingDeposits[i] = DepositView({\n root: depositsRoots[i],\n pubKeyHash: deposit.pubKeyHash,\n amountGwei: deposit.amountGwei,\n slot: deposit.slot\n });\n }\n }\n}\n" + }, + "contracts/strategies/NativeStaking/FeeAccumulator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\n/**\n * @title Fee Accumulator for Native Staking SSV Strategy\n * @notice Receives execution rewards which includes tx fees and\n * MEV rewards like tx priority and tx ordering.\n * It does NOT include swept ETH from beacon chain consensus rewards or full validator withdrawals.\n * @author Origin Protocol Inc\n */\ncontract FeeAccumulator {\n /// @notice The address of the Native Staking Strategy\n address public immutable STRATEGY;\n\n event ExecutionRewardsCollected(address indexed strategy, uint256 amount);\n\n /**\n * @param _strategy Address of the Native Staking Strategy\n */\n constructor(address _strategy) {\n STRATEGY = _strategy;\n }\n\n /**\n * @notice sends all ETH in this FeeAccumulator contract to the Native Staking Strategy.\n * @return eth The amount of execution rewards that were sent to the Native Staking Strategy\n */\n function collect() external returns (uint256 eth) {\n require(msg.sender == STRATEGY, \"Caller is not the Strategy\");\n\n eth = address(this).balance;\n if (eth > 0) {\n // Send the ETH to the Native Staking Strategy\n Address.sendValue(payable(STRATEGY), eth);\n\n emit ExecutionRewardsCollected(STRATEGY, eth);\n }\n }\n\n /**\n * @dev Accept ETH\n */\n receive() external payable {}\n}\n" + }, + "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { FeeAccumulator } from \"./FeeAccumulator.sol\";\nimport { ValidatorAccountant } from \"./ValidatorAccountant.sol\";\nimport { ISSVNetwork } from \"../../interfaces/ISSVNetwork.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/// @title Native Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract NativeStakingSSVStrategy is\n ValidatorAccountant,\n InitializableAbstractStrategy\n{\n using SafeERC20 for IERC20;\n\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n /// @notice Fee collector address\n /// @dev this address will receive maximal extractable value (MEV) rewards. These are\n /// rewards for arranging transactions in a way that benefits the validator.\n address payable public immutable FEE_ACCUMULATOR_ADDRESS;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[49] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n /// @param _feeAccumulator Address of the fee accumulator receiving execution layer validator rewards\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n uint256 _maxValidators,\n address _feeAccumulator,\n address _beaconChainDepositContract\n )\n InitializableAbstractStrategy(_baseConfig)\n ValidatorAccountant(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _maxValidators\n )\n {\n SSV_TOKEN = _ssvToken;\n FEE_ACCUMULATOR_ADDRESS = payable(_feeAccumulator);\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract.\n /// @param _rewardTokenAddresses Address of reward token for platform\n /// @param _assets Addresses of initial supported assets\n /// @param _pTokens Platform Token corresponding addresses\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n\n // Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\n FEE_ACCUMULATOR_ADDRESS\n );\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n /// @param _asset Address of asset to deposit. Has to be WETH.\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n depositedWethAccountedFor += _amount;\n _deposit(_asset, _amount);\n }\n\n /// @dev Deposit WETH to this strategy so it can later be staked into a validator.\n /// @param _asset Address of WETH\n /// @param _amount Amount of WETH to deposit\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n /*\n * We could do a check here that would revert when \"_amount % 32 ether != 0\". With the idea of\n * not allowing deposits that will result in WETH sitting on the strategy after all the possible batches\n * of 32ETH have been staked.\n * But someone could mess with our strategy by sending some WETH to it. And we might want to deposit just\n * enough WETH to add it up to 32 so it can be staked. For that reason the check is left out.\n *\n * WETH sitting on the strategy won't interfere with the accounting since accounting only operates on ETH.\n */\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n depositedWethAccountedFor = wethBalance;\n\n _deposit(WETH, newWeth);\n }\n }\n\n /// @notice Withdraw WETH from this contract. Used only if some WETH for is lingering on the contract.\n /// That can happen when:\n /// - after mints if the strategy is the default\n /// - time between depositToStrategy and stakeEth\n /// - the deposit was not a multiple of 32 WETH\n /// - someone sent WETH directly to this contract\n /// Will NOT revert if the strategy is paused from an accounting failure.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n _wethWithdrawn(_amount);\n\n IERC20(_asset).safeTransfer(_recipient, _amount);\n emit Withdrawal(_asset, address(0), _amount);\n }\n\n /// @notice transfer all WETH deposits back to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `exitSsvValidator` and `removeSsvValidator` operations.\n /// This does not withdraw any execution rewards from the FeeAccumulator or\n /// consensus rewards in this strategy.\n /// Any ETH in this strategy that was swept from a full validator withdrawal will not be withdrawn.\n /// ETH from full validator withdrawals is sent to the Vault using `doAccounting`.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n if (wethBalance > 0) {\n _withdraw(vaultAddress, WETH, wethBalance);\n }\n }\n\n /// @notice Returns the total value of (W)ETH that is staked to the validators\n /// and WETH deposits that are still to be staked.\n /// This does not include ETH from consensus rewards sitting in this strategy\n /// or ETH from MEV rewards in the FeeAccumulator. These rewards are harvested\n /// and sent to the Dripper so will eventually be sent to the Vault as WETH.\n /// @param _asset Address of weth asset\n /// @return balance Total value of (W)ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n balance =\n // add the ETH that has been staked in validators\n activeDepositedValidators *\n FULL_STAKE +\n // add the WETH in the strategy from deposits that are still to be staked\n IERC20(WETH).balanceOf(address(this));\n }\n\n function pause() external onlyStrategist {\n _pause();\n }\n\n /// @notice Returns bool indicating whether asset is supported by strategy.\n /// @param _asset The address of the asset token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for deposits\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\n function setFeeRecipient() external {\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\n FEE_ACCUMULATOR_ADDRESS\n );\n }\n\n /**\n * @notice Only accept ETH from the FeeAccumulator and the WETH contract - required when\n * unwrapping WETH just before staking it to the validator.\n * The strategy will also receive ETH from the priority fees of transactions when producing blocks\n * as defined in EIP-1559.\n * The tx fees come from the Beacon chain so do not need any EVM level permissions to receive ETH.\n * The tx fees are paid with each block produced. They are not included in the consensus rewards\n * which are periodically swept from the validators to this strategy.\n * For accounting purposes, the priority fees of transactions will be considered consensus rewards\n * and will be included in the AccountingConsensusRewards event.\n * @dev don't want to receive donations from anyone else as donations over the fuse limits will\n * mess with the accounting of the consensus rewards and validator full withdrawals.\n */\n receive() external payable {\n require(\n msg.sender == FEE_ACCUMULATOR_ADDRESS || msg.sender == WETH,\n \"Eth not from allowed contracts\"\n );\n }\n\n /***************************************\n Internal functions\n ****************************************/\n\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Convert accumulated ETH to WETH and send to the Harvester.\n /// Will revert if the strategy is paused for accounting.\n function _collectRewardTokens() internal override whenNotPaused {\n // collect ETH from execution rewards from the fee accumulator\n uint256 executionRewards = FeeAccumulator(FEE_ACCUMULATOR_ADDRESS)\n .collect();\n\n // total ETH rewards to be harvested = execution rewards + consensus rewards\n uint256 ethRewards = executionRewards + consensusRewards;\n\n require(\n address(this).balance >= ethRewards,\n \"Insufficient eth balance\"\n );\n\n if (ethRewards > 0) {\n // reset the counter keeping track of beacon chain consensus rewards\n consensusRewards = 0;\n\n // Convert ETH rewards to WETH\n IWETH9(WETH).deposit{ value: ethRewards }();\n\n IERC20(WETH).safeTransfer(harvesterAddress, ethRewards);\n emit RewardTokenCollected(harvesterAddress, WETH, ethRewards);\n }\n }\n\n /// @dev emits Withdrawal event from NativeStakingSSVStrategy\n function _wethWithdrawnToVault(uint256 _amount) internal override {\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _wethWithdrawn(uint256 _amount) internal override {\n /* In an ideal world we wouldn't need to reduce the deduction amount when the\n * depositedWethAccountedFor is smaller than the _amount.\n *\n * The reason this is required is that a malicious actor could sent WETH directly\n * to this contract and that would circumvent the increase of depositedWethAccountedFor\n * property. When the ETH would be staked the depositedWethAccountedFor amount could\n * be deducted so much that it would be negative.\n */\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n }\n}\n" + }, + "contracts/strategies/NativeStaking/ValidatorAccountant.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ValidatorRegistrator } from \"./ValidatorRegistrator.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\n\n/// @title Validator Accountant\n/// @notice Attributes the ETH swept from beacon chain validators to this strategy contract\n/// as either full or partial withdrawals. Partial withdrawals being consensus rewards.\n/// Full withdrawals are from exited validators.\n/// @author Origin Protocol Inc\nabstract contract ValidatorAccountant is ValidatorRegistrator {\n /// @notice The minimum amount of blocks that need to pass between two calls to manuallyFixAccounting\n uint256 public constant MIN_FIX_ACCOUNTING_CADENCE = 7200; // 1 day\n\n /// @notice Keeps track of the total consensus rewards swept from the beacon chain\n uint256 public consensusRewards;\n\n /// @notice start of fuse interval\n uint256 public fuseIntervalStart;\n /// @notice end of fuse interval\n uint256 public fuseIntervalEnd;\n /// @notice last block number manuallyFixAccounting has been called\n uint256 public lastFixAccountingBlockNumber;\n\n uint256[49] private __gap;\n\n event FuseIntervalUpdated(uint256 start, uint256 end);\n event AccountingFullyWithdrawnValidator(\n uint256 noOfValidators,\n uint256 remainingValidators,\n uint256 wethSentToVault\n );\n event AccountingValidatorSlashed(\n uint256 remainingValidators,\n uint256 wethSentToVault\n );\n event AccountingConsensusRewards(uint256 amount);\n\n event AccountingManuallyFixed(\n int256 validatorsDelta,\n int256 consensusRewardsDelta,\n uint256 wethToVault\n );\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n uint256 _maxValidators\n )\n ValidatorRegistrator(\n _wethAddress,\n _vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _maxValidators\n )\n {}\n\n /// @notice set fuse interval values\n function setFuseInterval(\n uint256 _fuseIntervalStart,\n uint256 _fuseIntervalEnd\n ) external onlyGovernor {\n require(\n _fuseIntervalStart < _fuseIntervalEnd &&\n _fuseIntervalEnd < 32 ether &&\n _fuseIntervalEnd - _fuseIntervalStart >= 4 ether,\n \"Incorrect fuse interval\"\n );\n\n fuseIntervalStart = _fuseIntervalStart;\n fuseIntervalEnd = _fuseIntervalEnd;\n\n emit FuseIntervalUpdated(_fuseIntervalStart, _fuseIntervalEnd);\n }\n\n /* solhint-disable max-line-length */\n /// This notion page offers a good explanation of how the accounting functions\n /// https://www.notion.so/originprotocol/Limited-simplified-native-staking-accounting-67a217c8420d40678eb943b9da0ee77d\n /// In short, after dividing by 32, if the ETH remaining on the contract falls between 0 and fuseIntervalStart,\n /// the accounting function will treat that ETH as Beacon chain consensus rewards.\n /// On the contrary, if after dividing by 32, the ETH remaining on the contract falls between fuseIntervalEnd and 32,\n /// the accounting function will treat that as a validator slashing.\n /// @notice Perform the accounting attributing beacon chain ETH to either full or partial withdrawals. Returns true when\n /// accounting is valid and fuse isn't \"blown\". Returns false when fuse is blown.\n /// @dev This function could in theory be permission-less but lets allow only the Registrator (Defender Action) to call it\n /// for now.\n /// @return accountingValid true if accounting was successful, false if fuse is blown\n /* solhint-enable max-line-length */\n function doAccounting()\n external\n onlyRegistrator\n whenNotPaused\n nonReentrant\n returns (bool accountingValid)\n {\n // pause the accounting on failure\n accountingValid = _doAccounting(true);\n }\n\n // slither-disable-start reentrancy-eth\n function _doAccounting(bool pauseOnFail)\n internal\n returns (bool accountingValid)\n {\n if (address(this).balance < consensusRewards) {\n return _failAccounting(pauseOnFail);\n }\n\n // Calculate all the new ETH that has been swept to the contract since the last accounting\n uint256 newSweptETH = address(this).balance - consensusRewards;\n accountingValid = true;\n\n // send the ETH that is from fully withdrawn validators to the Vault\n if (newSweptETH >= FULL_STAKE) {\n uint256 fullyWithdrawnValidators;\n // explicitly cast to uint256 as we want to round to a whole number of validators\n fullyWithdrawnValidators = uint256(newSweptETH / FULL_STAKE);\n activeDepositedValidators -= fullyWithdrawnValidators;\n\n uint256 wethToVault = FULL_STAKE * fullyWithdrawnValidators;\n IWETH9(WETH).deposit{ value: wethToVault }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, wethToVault);\n _wethWithdrawnToVault(wethToVault);\n\n emit AccountingFullyWithdrawnValidator(\n fullyWithdrawnValidators,\n activeDepositedValidators,\n wethToVault\n );\n }\n\n uint256 ethRemaining = address(this).balance - consensusRewards;\n // should be less than a whole validator stake\n require(ethRemaining < FULL_STAKE, \"Unexpected accounting\");\n\n // If no Beacon chain consensus rewards swept\n if (ethRemaining == 0) {\n // do nothing\n return accountingValid;\n } else if (ethRemaining < fuseIntervalStart) {\n // Beacon chain consensus rewards swept (partial validator withdrawals)\n // solhint-disable-next-line reentrancy\n consensusRewards += ethRemaining;\n emit AccountingConsensusRewards(ethRemaining);\n } else if (ethRemaining > fuseIntervalEnd) {\n // Beacon chain consensus rewards swept but also a slashed validator fully exited\n IWETH9(WETH).deposit{ value: ethRemaining }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, ethRemaining);\n activeDepositedValidators -= 1;\n\n _wethWithdrawnToVault(ethRemaining);\n\n emit AccountingValidatorSlashed(\n activeDepositedValidators,\n ethRemaining\n );\n }\n // Oh no... Fuse is blown. The Strategist needs to adjust the accounting values.\n else {\n return _failAccounting(pauseOnFail);\n }\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @dev pause any further accounting if required and return false\n function _failAccounting(bool pauseOnFail)\n internal\n returns (bool accountingValid)\n {\n // pause if not already\n if (pauseOnFail) {\n _pause();\n }\n // fail the accounting\n accountingValid = false;\n }\n\n /// @notice Allow the Strategist to fix the accounting of this strategy and unpause.\n /// @param _validatorsDelta adjust the active validators by up to plus three or minus three\n /// @param _consensusRewardsDelta adjust the accounted for consensus rewards up or down\n /// @param _ethToVaultAmount the amount of ETH that gets wrapped into WETH and sent to the Vault\n /// @dev There is a case when a validator(s) gets slashed so much that the eth swept from\n /// the beacon chain enters the fuse area and there are no consensus rewards on the contract\n /// to \"dip into\"/use. To increase the amount of unaccounted ETH over the fuse end interval\n /// we need to reduce the amount of active deposited validators and immediately send WETH\n /// to the vault, so it doesn't interfere with further accounting.\n function manuallyFixAccounting(\n int256 _validatorsDelta,\n int256 _consensusRewardsDelta,\n uint256 _ethToVaultAmount\n ) external onlyStrategist whenPaused nonReentrant {\n require(\n lastFixAccountingBlockNumber + MIN_FIX_ACCOUNTING_CADENCE <\n block.number,\n \"Fix accounting called too soon\"\n );\n require(\n _validatorsDelta >= -3 &&\n _validatorsDelta <= 3 &&\n // new value must be positive\n int256(activeDepositedValidators) + _validatorsDelta >= 0,\n \"Invalid validatorsDelta\"\n );\n require(\n _consensusRewardsDelta >= -332 ether &&\n _consensusRewardsDelta <= 332 ether &&\n // new value must be positive\n int256(consensusRewards) + _consensusRewardsDelta >= 0,\n \"Invalid consensusRewardsDelta\"\n );\n require(_ethToVaultAmount <= 32 ether * 3, \"Invalid wethToVaultAmount\");\n\n activeDepositedValidators = uint256(\n int256(activeDepositedValidators) + _validatorsDelta\n );\n consensusRewards = uint256(\n int256(consensusRewards) + _consensusRewardsDelta\n );\n lastFixAccountingBlockNumber = block.number;\n if (_ethToVaultAmount > 0) {\n IWETH9(WETH).deposit{ value: _ethToVaultAmount }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, _ethToVaultAmount);\n _wethWithdrawnToVault(_ethToVaultAmount);\n }\n\n emit AccountingManuallyFixed(\n _validatorsDelta,\n _consensusRewardsDelta,\n _ethToVaultAmount\n );\n\n // rerun the accounting to see if it has now been fixed.\n // Do not pause the accounting on failure as it is already paused\n require(_doAccounting(false), \"Fuse still blown\");\n\n // unpause since doAccounting was successful\n _unpause();\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n /// @dev allows for NativeStakingSSVStrategy contract to emit the Withdrawal event\n function _wethWithdrawnToVault(uint256 _amount) internal virtual;\n}\n" + }, + "contracts/strategies/NativeStaking/ValidatorRegistrator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Registrator of the validators\n * @notice This contract implements all the required functionality to register, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract ValidatorRegistrator is Governable, Pausable {\n /// @notice The maximum amount of ETH that can be staked by a validator\n /// @dev this can change in the future with EIP-7251, Increase the MAX_EFFECTIVE_BALANCE\n uint256 public constant FULL_STAKE = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Maximum number of validators that can be registered in this strategy\n uint256 public immutable MAX_VALIDATORS;\n\n /// @notice Address of the registrator - allowed to register, exit and remove validators\n address public validatorRegistrator;\n /// @notice The number of validators that have 32 (!) ETH actively deposited. When a new deposit\n /// to a validator happens this number increases, when a validator exit is detected this number\n /// decreases.\n uint256 public activeDepositedValidators;\n /// @notice State of the validators keccak256(pubKey) => state\n mapping(bytes32 => VALIDATOR_STATE) public validatorsStates;\n /// @notice The account that is allowed to modify stakeETHThreshold and reset stakeETHTally\n address public stakingMonitor;\n /// @notice Amount of ETH that can be staked before staking on the contract is suspended\n /// and the `stakingMonitor` needs to approve further staking by calling `resetStakeETHTally`\n uint256 public stakeETHThreshold;\n /// @notice Amount of ETH that has been staked since the `stakingMonitor` last called `resetStakeETHTally`.\n /// This can not go above `stakeETHThreshold`.\n uint256 public stakeETHTally;\n\n // For future use\n uint256[47] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n EXITING, // exit message has been posted and validator is in the process of exiting\n EXIT_COMPLETE // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event ETHStaked(bytes32 indexed pubKeyHash, bytes pubKey, uint256 amount);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event SSVValidatorExitInitiated(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event SSVValidatorExitCompleted(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event StakeETHThresholdChanged(uint256 amount);\n event StakeETHTallyReset();\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(\n msg.sender == validatorRegistrator,\n \"Caller is not the Registrator\"\n );\n _;\n }\n\n /// @dev Throws if called by any account other than the Staking monitor\n modifier onlyStakingMonitor() {\n require(msg.sender == stakingMonitor, \"Caller is not the Monitor\");\n _;\n }\n\n /// @dev Throws if called by any account other than the Strategist\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(VAULT_ADDRESS).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n uint256 _maxValidators\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n MAX_VALIDATORS = _maxValidators;\n }\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Set the address of the staking monitor that is allowed to reset stakeETHTally\n function setStakingMonitor(address _address) external onlyGovernor {\n stakingMonitor = _address;\n emit StakingMonitorChanged(_address);\n }\n\n /// @notice Set the amount of ETH that can be staked before staking monitor\n // needs to a approve further staking by resetting the stake ETH tally\n function setStakeETHThreshold(uint256 _amount) external onlyGovernor {\n stakeETHThreshold = _amount;\n emit StakeETHThresholdChanged(_amount);\n }\n\n /// @notice Reset the stakeETHTally\n function resetStakeETHTally() external onlyStakingMonitor {\n stakeETHTally = 0;\n emit StakeETHTallyReset();\n }\n\n /// @notice Stakes WETH to the node validators\n /// @param validators A list of validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n // slither-disable-start reentrancy-eth\n function stakeEth(ValidatorStakeData[] calldata validators)\n external\n onlyRegistrator\n whenNotPaused\n nonReentrant\n {\n uint256 requiredETH = validators.length * FULL_STAKE;\n\n // Check there is enough WETH from the deposits sitting in this strategy contract\n require(\n requiredETH <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(\n activeDepositedValidators + validators.length <= MAX_VALIDATORS,\n \"Max validators reached\"\n );\n\n require(\n stakeETHTally + requiredETH <= stakeETHThreshold,\n \"Staking ETH over threshold\"\n );\n stakeETHTally += requiredETH;\n\n // Convert required ETH from WETH\n IWETH9(WETH).withdraw(requiredETH);\n _wethWithdrawn(requiredETH);\n\n /* 0x01 to indicate that withdrawal credentials will contain an EOA address that the sweeping function\n * can sweep funds to.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x01),\n bytes11(0),\n address(this)\n );\n\n // For each validator\n for (uint256 i = 0; i < validators.length; ++i) {\n bytes32 pubKeyHash = keccak256(validators[i].pubkey);\n\n require(\n validatorsStates[pubKeyHash] == VALIDATOR_STATE.REGISTERED,\n \"Validator not registered\"\n );\n\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: FULL_STAKE\n }(\n validators[i].pubkey,\n withdrawalCredentials,\n validators[i].signature,\n validators[i].depositDataRoot\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.STAKED;\n\n emit ETHStaked(pubKeyHash, validators[i].pubkey, FULL_STAKE);\n }\n // save gas by changing this storage variable only once rather each time in the loop.\n activeDepositedValidators += validators.length;\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Registers a new validator in the SSV Cluster.\n /// Only the registrator can call this function.\n /// @param publicKeys The public keys of the validators\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for each validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n require(\n publicKeys.length == sharesData.length,\n \"Pubkey sharesData mismatch\"\n );\n // Check each public key has not already been used\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n emit SSVValidatorRegistered(pubKeyHash, publicKeys[i], operatorIds);\n }\n\n ISSVNetwork(SSV_NETWORK).bulkRegisterValidator(\n publicKeys,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n }\n\n /// @notice Exit validators from the Beacon chain.\n /// The staked ETH will eventually swept to this native staking strategy.\n /// Only the registrator can call this function.\n /// @param publicKeys List of SSV validator public keys\n /// @param operatorIds The operator IDs of the SSV Cluster\n // slither-disable-start reentrancy-no-eth\n function exitSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external onlyRegistrator whenNotPaused nonReentrant {\n ISSVNetwork(SSV_NETWORK).bulkExitValidator(publicKeys, operatorIds);\n\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n\n // Check each validator has not already been staked.\n // This would normally be done before the external call but is after\n // so only one for loop of the validators is needed.\n require(\n currentState == VALIDATOR_STATE.STAKED,\n \"Validator not staked\"\n );\n\n // Store the new validator state\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXITING;\n\n emit SSVValidatorExitInitiated(\n pubKeyHash,\n publicKeys[i],\n operatorIds\n );\n }\n }\n\n /// @notice Remove validators from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKeys List of SSV validator public keys\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function removeSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused nonReentrant {\n ISSVNetwork(SSV_NETWORK).bulkRemoveValidator(\n publicKeys,\n operatorIds,\n cluster\n );\n\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n\n // Check each validator is either registered or exited.\n // This would normally be done before the external call but is after\n // so only one for loop of the validators is needed.\n require(\n currentState == VALIDATOR_STATE.EXITING ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exiting\"\n );\n\n // Store the new validator state\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXIT_COMPLETE;\n\n emit SSVValidatorExitCompleted(\n pubKeyHash,\n publicKeys[i],\n operatorIds\n );\n }\n }\n\n /// @notice Deposits more SSV Tokens to the SSV Network contract which is used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// uses \"onlyStrategist\" modifier so continuous front-running can't DOS our maintenance service\n /// that tries to top up SSV tokens.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function depositSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyStrategist {\n ISSVNetwork(SSV_NETWORK).deposit(\n address(this),\n operatorIds,\n ssvAmount,\n cluster\n );\n }\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _wethWithdrawn(uint256 _amount) internal virtual;\n}\n" + }, + "contracts/strategies/plume/RoosterAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Rooster AMO strategy\n * @author Origin Protocol Inc\n * @custom:security-contact security@originprotocol.com\n */\nimport { Math as MathRooster } from \"../../../lib/rooster/v2-common/libraries/Math.sol\";\nimport { Math as Math_v5 } from \"../../../lib/rooster/openzeppelin-custom/contracts/utils/math/Math.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { IMaverickV2Pool } from \"../../interfaces/plume/IMaverickV2Pool.sol\";\nimport { IMaverickV2Quoter } from \"../../interfaces/plume/IMaverickV2Quoter.sol\";\nimport { IMaverickV2LiquidityManager } from \"../../interfaces/plume/IMaverickV2LiquidityManager.sol\";\nimport { IMaverickV2PoolLens } from \"../../interfaces/plume/IMaverickV2PoolLens.sol\";\nimport { IMaverickV2Position } from \"../../interfaces/plume/IMaverickV2Position.sol\";\nimport { IVotingDistributor } from \"../../interfaces/plume/IVotingDistributor.sol\";\nimport { IPoolDistributor } from \"../../interfaces/plume/IPoolDistributor.sol\";\n// importing custom version of rooster TickMath because of dependency collision. Maverick uses\n// a newer OpenZepplin Math library with functionality that is not present in 4.4.2 (the one we use)\nimport { TickMath } from \"../../../lib/rooster/v2-common/libraries/TickMath.sol\";\n\ncontract RoosterAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n using SafeCast for uint256;\n\n /***************************************\n Storage slot members\n ****************************************/\n\n /// @notice NFT tokenId of the liquidity position\n ///\n /// @dev starts with value of 1 and can not be 0\n // solhint-disable-next-line max-line-length\n /// https://github.com/rooster-protocol/rooster-contracts/blob/fbfecbc519e4495b12598024a42630b4a8ea4489/v2-common/contracts/base/Nft.sol#L14\n uint256 public tokenId;\n /// @dev Minimum amount of tokens the strategy would be able to withdraw from the pool.\n /// minimum amount of tokens are withdrawn at a 1:1 price\n /// Important: Underlying assets contains only assets that are deposited in the underlying Rooster pool.\n /// WETH or OETH held by this contract is not accounted for in underlying assets\n uint256 public underlyingAssets;\n /// @notice Marks the start of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareStart;\n /// @notice Marks the end of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareEnd;\n /// @dev reserved for inheritance\n int256[46] private __reserved;\n\n /***************************************\n Constants, structs and events\n ****************************************/\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the OETH token contract\n address public immutable OETH;\n /// @notice the underlying AMO Maverick (Rooster) pool\n IMaverickV2Pool public immutable mPool;\n /// @notice the Liquidity manager used to add liquidity to the pool\n IMaverickV2LiquidityManager public immutable liquidityManager;\n /// @notice the Maverick V2 poolLens\n ///\n /// @dev only used to provide the pool's current sqrtPrice\n IMaverickV2PoolLens public immutable poolLens;\n /// @notice the Maverick V2 position\n ///\n /// @dev provides details of the NFT LP position and offers functions to\n /// remove the liquidity.\n IMaverickV2Position public immutable maverickPosition;\n /// @notice the Maverick Quoter\n IMaverickV2Quoter public immutable quoter;\n /// @notice the Maverick Voting Distributor\n IVotingDistributor public immutable votingDistributor;\n /// @notice the Maverick Pool Distributor\n IPoolDistributor public immutable poolDistributor;\n\n /// @notice sqrtPriceTickLower\n /// @dev tick lower represents the lower price of OETH priced in WETH. Meaning the pool\n /// offers more than 1 OETH for 1 WETH. In other terms to get 1 OETH the swap needs to offer 0.9999 WETH\n /// this is where purchasing OETH with WETH within the liquidity position is the cheapest.\n ///\n /// _____________________\n /// | | |\n /// | WETH | OETH |\n /// | | |\n /// | | |\n /// --------- * ---- * ---------- * ---------\n /// currentPrice\n /// sqrtPriceHigher-(1:1 parity)\n /// sqrtPriceLower\n ///\n ///\n /// Price is defined as price of token1 in terms of token0. (token1 / token0)\n /// @notice sqrtPriceTickLower - OETH is priced 0.9999 WETH\n uint256 public immutable sqrtPriceTickLower;\n /// @notice sqrtPriceTickHigher\n /// @dev tick higher represents 1:1 price parity of WETH to OETH\n uint256 public immutable sqrtPriceTickHigher;\n /// @dev price at parity (in OETH this is equal to sqrtPriceTickHigher)\n uint256 public immutable sqrtPriceAtParity;\n /// @notice The tick where the strategy deploys the liquidity to\n int32 public constant TICK_NUMBER = -1;\n /// @notice Minimum liquidity that must be exceeded to continue with the action\n /// e.g. deposit, add liquidity\n uint256 public constant ACTION_THRESHOLD = 1e12;\n /// @notice Maverick pool static liquidity bin type\n uint8 public constant MAV_STATIC_BIN_KIND = 0;\n /// @dev a threshold under which the contract no longer allows for the protocol to rebalance. Guarding\n /// against a strategist / guardian being taken over and with multiple transactions draining the\n /// protocol funds.\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n /// @notice Emitted when the allowed interval within which the strategy contract is allowed to deposit\n /// liquidity to the underlying pool is updated.\n /// @param allowedWethShareStart The start of the interval\n /// @param allowedWethShareEnd The end of the interval\n event PoolWethShareIntervalUpdated(\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n /// @notice Emitted when liquidity is removed from the underlying pool\n /// @param withdrawLiquidityShare Share of strategy's liquidity that has been removed\n /// @param removedWETHAmount The amount of WETH removed\n /// @param removedOETHAmount The amount of OETH removed\n /// @param underlyingAssets Updated amount of strategy's underlying assets\n event LiquidityRemoved(\n uint256 withdrawLiquidityShare,\n uint256 removedWETHAmount,\n uint256 removedOETHAmount,\n uint256 underlyingAssets\n );\n\n /// @notice Emitted when the underlying pool is rebalanced\n /// @param currentPoolWethShare The resulting share of strategy's liquidity\n /// in the TICK_NUMBER\n event PoolRebalanced(uint256 currentPoolWethShare);\n\n /// @notice Emitted when the amount of underlying assets the strategy hold as\n /// liquidity in the pool is updated.\n /// @param underlyingAssets Updated amount of strategy's underlying assets\n event UnderlyingAssetsUpdated(uint256 underlyingAssets);\n\n /// @notice Emitted when liquidity is added to the underlying pool\n /// @param wethAmountDesired Amount of WETH desired to be deposited\n /// @param oethAmountDesired Amount of OETH desired to be deposited\n /// @param wethAmountSupplied Amount of WETH deposited\n /// @param oethAmountSupplied Amount of OETH deposited\n /// @param tokenId NFT liquidity token id\n /// @param underlyingAssets Updated amount of underlying assets\n event LiquidityAdded(\n uint256 wethAmountDesired,\n uint256 oethAmountDesired,\n uint256 wethAmountSupplied,\n uint256 oethAmountSupplied,\n uint256 tokenId,\n uint256 underlyingAssets\n ); // 0x1530ec74\n\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n ); // 0x3681e8e0\n\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth); // 0x989e5ca8\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth); // 0xa6737d87\n error OutsideExpectedTickRange(); // 0xa6e1bad2\n error SlippageCheck(uint256 tokenReceived); // 0x355cdb78\n\n /// @notice the constructor\n /// @dev This contract is intended to be used as a proxy. To prevent the\n /// potential confusion of having a functional implementation contract\n /// the constructor has the `initializer` modifier. This way the\n /// `initialize` function can not be called on the implementation contract.\n /// For the same reason the implementation contract also has the governor\n /// set to a zero address.\n /// @param _stratConfig the basic strategy configuration\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _oethAddress Address of the Erc20 OETH Token contract\n /// @param _liquidityManager Address of liquidity manager to add\n /// the liquidity\n /// @param _poolLens Address of the pool lens contract\n /// @param _maverickPosition Address of the Maverick's position contract\n /// @param _maverickQuoter Address of the Maverick's Quoter contract\n /// @param _mPool Address of the Rooster concentrated liquidity pool\n /// @param _upperTickAtParity Bool when true upperTick is the one where the\n /// price of OETH and WETH are at parity\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethAddress,\n address _liquidityManager,\n address _poolLens,\n address _maverickPosition,\n address _maverickQuoter,\n address _mPool,\n bool _upperTickAtParity,\n address _votingDistributor,\n address _poolDistributor\n ) initializer InitializableAbstractStrategy(_stratConfig) {\n require(\n address(IMaverickV2Pool(_mPool).tokenA()) == _wethAddress,\n \"WETH not TokenA\"\n );\n require(\n address(IMaverickV2Pool(_mPool).tokenB()) == _oethAddress,\n \"OETH not TokenB\"\n );\n require(\n _liquidityManager != address(0),\n \"LiquidityManager zero address not allowed\"\n );\n require(\n _maverickQuoter != address(0),\n \"Quoter zero address not allowed\"\n );\n require(_poolLens != address(0), \"PoolLens zero address not allowed\");\n require(\n _maverickPosition != address(0),\n \"Position zero address not allowed\"\n );\n require(\n _votingDistributor != address(0),\n \"Voting distributor zero address not allowed\"\n );\n require(\n _poolDistributor != address(0),\n \"Pool distributor zero address not allowed\"\n );\n\n uint256 _tickSpacing = IMaverickV2Pool(_mPool).tickSpacing();\n require(_tickSpacing == 1, \"Unsupported tickSpacing\");\n\n // tickSpacing == 1\n (sqrtPriceTickLower, sqrtPriceTickHigher) = TickMath.tickSqrtPrices(\n _tickSpacing,\n TICK_NUMBER\n );\n sqrtPriceAtParity = _upperTickAtParity\n ? sqrtPriceTickHigher\n : sqrtPriceTickLower;\n\n WETH = _wethAddress;\n OETH = _oethAddress;\n liquidityManager = IMaverickV2LiquidityManager(_liquidityManager);\n poolLens = IMaverickV2PoolLens(_poolLens);\n maverickPosition = IMaverickV2Position(_maverickPosition);\n quoter = IMaverickV2Quoter(_maverickQuoter);\n mPool = IMaverickV2Pool(_mPool);\n votingDistributor = IVotingDistributor(_votingDistributor);\n poolDistributor = IPoolDistributor(_poolDistributor);\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /**\n * @notice initialize function, to set up initial internal state\n */\n function initialize() external onlyGovernor initializer {\n // Read reward\n address[] memory _rewardTokens = new address[](1);\n _rewardTokens[0] = poolDistributor.rewardToken();\n\n require(_rewardTokens[0] != address(0), \"No reward token configured\");\n\n InitializableAbstractStrategy._initialize(\n _rewardTokens,\n new address[](0),\n new address[](0)\n );\n }\n\n /***************************************\n Configuration \n ****************************************/\n\n /**\n * @notice Set allowed pool weth share interval. After the rebalance happens\n * the share of WETH token in the ticker needs to be within the specifications\n * of the interval.\n *\n * @param _allowedWethShareStart Start of WETH share interval expressed as 18 decimal amount\n * @param _allowedWethShareEnd End of WETH share interval expressed as 18 decimal amount\n */\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external onlyGovernor {\n require(\n _allowedWethShareStart < _allowedWethShareEnd,\n \"Invalid interval\"\n );\n // can not go below 1% weth share\n require(_allowedWethShareStart > 0.01 ether, \"Invalid interval start\");\n // can not go above 95% weth share\n require(_allowedWethShareEnd < 0.95 ether, \"Invalid interval end\");\n\n allowedWethShareStart = _allowedWethShareStart;\n allowedWethShareEnd = _allowedWethShareEnd;\n emit PoolWethShareIntervalUpdated(\n _allowedWethShareStart,\n _allowedWethShareEnd\n );\n }\n\n /***************************************\n Strategy overrides \n ****************************************/\n\n /**\n * @notice Deposits funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposits all the funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n _deposit(WETH, _wethBalance);\n }\n\n /**\n * @dev Deposits funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n * Before this function can be called the initial pool position needs to already\n * be minted.\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n require(tokenId > 0, \"Initial position not minted\");\n emit Deposit(_asset, address(0), _amount);\n\n // if the pool price is not within the expected interval leave the WETH on the contract\n // as to not break the mints - in case it would be configured as a default asset strategy\n (bool _isExpectedRange, ) = _checkForExpectedPoolPrice(false);\n if (_isExpectedRange) {\n // deposit funds into the underlying pool. Because no swap is performed there is no\n // need to remove any of the liquidity beforehand.\n _rebalance(0, false, 0, 0);\n }\n }\n\n /**\n * @notice Withdraw an `amount` of WETH from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset WETH address\n * @param _amount Amount of WETH to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n _ensureWETHBalance(_amount);\n\n _withdraw(_recipient, _amount);\n }\n\n /**\n * @notice Withdraw WETH and sends it to the Vault.\n */\n function withdrawAll() external override onlyVault nonReentrant {\n if (tokenId != 0) {\n _removeLiquidity(1e18);\n }\n\n uint256 _balance = IERC20(WETH).balanceOf(address(this));\n if (_balance > 0) {\n _withdraw(vaultAddress, _balance);\n }\n }\n\n function _withdraw(address _recipient, uint256 _amount) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n * @return bool True when the _asset is WETH\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /**\n * @dev Approve the spending amounts for the assets\n */\n function _approveTokenAmounts(\n uint256 _wethAllowance,\n uint256 _oethAllowance\n ) internal {\n IERC20(WETH).approve(address(liquidityManager), _wethAllowance);\n IERC20(OETH).approve(address(liquidityManager), _oethAllowance);\n }\n\n /***************************************\n Liquidity management\n ****************************************/\n /**\n * @dev Add liquidity into the pool in the pre-configured WETH to OETH share ratios\n * defined by the allowedPoolWethShareStart|End interval.\n *\n * Normally a PoolLens contract is used to prepare the parameters to add liquidity to the\n * Rooster pools. It has some errors when doing those calculation and for that reason a\n * much more accurate Quoter contract is used. This is possible due to our requirement of\n * adding liquidity only to one tick - PoolLens supports adding liquidity into multiple ticks\n * using different distribution ratios.\n */\n function _addLiquidity() internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n // don't deposit small liquidity amounts\n if (_wethBalance <= ACTION_THRESHOLD) {\n return;\n }\n\n (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n ) = _getAddLiquidityParams(_wethBalance, 1e30);\n\n if (OETHRequired > _oethBalance) {\n IVault(vaultAddress).mintForStrategy(OETHRequired - _oethBalance);\n }\n\n _approveTokenAmounts(WETHRequired, OETHRequired);\n\n (\n uint256 _wethAmount,\n uint256 _oethAmount,\n uint32[] memory binIds\n ) = liquidityManager.addPositionLiquidityToSenderByTokenIndex(\n mPool,\n 0, // NFT token index\n packedSqrtPriceBreaks,\n packedArgs\n );\n\n require(binIds.length == 1, \"Unexpected binIds length\");\n\n // burn remaining OETH\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n\n // needs to be called after _updateUnderlyingAssets so the updated amount\n // is reflected in the event\n emit LiquidityAdded(\n _wethBalance, // wethAmountDesired\n OETHRequired, // oethAmountDesired\n _wethAmount, // wethAmountSupplied\n _oethAmount, // oethAmountSupplied\n tokenId, // tokenId\n underlyingAssets\n );\n }\n\n /**\n * @dev The function creates liquidity parameters required to be able to add liquidity to the pool.\n * The function needs to handle the 3 different cases of the way liquidity is added:\n * - only WETH present in the tick\n * - only OETH present in the tick\n * - both tokens present in the tick\n *\n */\n function _getAddLiquidityParams(uint256 _maxWETH, uint256 _maxOETH)\n internal\n returns (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n )\n {\n IMaverickV2Pool.AddLiquidityParams[]\n memory addParams = new IMaverickV2Pool.AddLiquidityParams[](1);\n int32[] memory ticks = new int32[](1);\n uint128[] memory amounts = new uint128[](1);\n ticks[0] = TICK_NUMBER;\n // arbitrary LP amount\n amounts[0] = 1e24;\n\n // construct value for Quoter with arbitrary LP amount\n IMaverickV2Pool.AddLiquidityParams memory addParam = IMaverickV2Pool\n .AddLiquidityParams({\n kind: MAV_STATIC_BIN_KIND,\n ticks: ticks,\n amounts: amounts\n });\n\n // get the WETH and OETH required to get the proportion of tokens required\n // given the arbitrary liquidity\n (WETHRequired, OETHRequired, ) = quoter.calculateAddLiquidity(\n mPool,\n addParam\n );\n\n /**\n * If either token required is 0 then the tick consists only of the other token. In that\n * case the liquidity calculations need to be done using the non 0 token. By setting the\n * tokenRequired from 0 to 1 the `min` in next step will ignore that (the bigger) value.\n */\n WETHRequired = WETHRequired == 0 ? 1 : WETHRequired;\n OETHRequired = OETHRequired == 0 ? 1 : OETHRequired;\n\n addParam.amounts[0] = Math_v5\n .min(\n ((_maxWETH - 1) * 1e24) / WETHRequired,\n ((_maxOETH - 1) * 1e24) / OETHRequired\n )\n .toUint128();\n\n // update the quotes with the actual amounts\n (WETHRequired, OETHRequired, ) = quoter.calculateAddLiquidity(\n mPool,\n addParam\n );\n\n require(_maxWETH >= WETHRequired, \"More WETH required than specified\");\n require(_maxOETH >= OETHRequired, \"More OETH required than specified\");\n\n // organize values to be used by manager\n addParams[0] = addParam;\n packedArgs = liquidityManager.packAddLiquidityArgsArray(addParams);\n // price can stay 0 if array only has one element\n packedSqrtPriceBreaks = liquidityManager.packUint88Array(\n new uint88[](1)\n );\n }\n\n /**\n * @dev Check that the Rooster pool price is within the expected\n * parameters.\n * This function works whether the strategy contract has liquidity\n * position in the pool or not. The function returns _wethSharePct\n * as a gas optimization measure.\n * @param _throwException when set to true the function throws an exception\n * when pool's price is not within expected range.\n * @return _isExpectedRange Bool expressing price is within expected range\n * @return _wethSharePct Share of WETH owned by this strategy contract in the\n * configured ticker.\n */\n function _checkForExpectedPoolPrice(bool _throwException)\n internal\n view\n returns (bool _isExpectedRange, uint256 _wethSharePct)\n {\n require(\n allowedWethShareStart != 0 && allowedWethShareEnd != 0,\n \"Weth share interval not set\"\n );\n\n uint256 _currentPrice = getPoolSqrtPrice();\n\n /**\n * First check pool price is in expected tick range\n *\n * A revert is issued even though price being equal to the lower bound as that can not\n * be within the approved tick range.\n */\n if (\n _currentPrice <= sqrtPriceTickLower ||\n _currentPrice >= sqrtPriceTickHigher\n ) {\n if (_throwException) {\n revert OutsideExpectedTickRange();\n }\n\n return (false, _currentPrice <= sqrtPriceTickLower ? 0 : 1e18);\n }\n\n // 18 decimal number expressed WETH tick share\n _wethSharePct = _getWethShare(_currentPrice);\n\n if (\n _wethSharePct < allowedWethShareStart ||\n _wethSharePct > allowedWethShareEnd\n ) {\n if (_throwException) {\n revert PoolRebalanceOutOfBounds(\n _wethSharePct,\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n return (false, _wethSharePct);\n }\n\n return (true, _wethSharePct);\n }\n\n /**\n * @notice Rebalance the pool to the desired token split and Deposit any WETH on the contract to the\n * underlying rooster pool. Print the required amount of corresponding OETH. After the rebalancing is\n * done burn any potentially remaining OETH tokens still on the strategy contract.\n *\n * This function has a slightly different behaviour depending on the status of the underlying Rooster\n * pool. The function consists of the following 3 steps:\n * 1. withdrawLiquidityOption -> this is a configurable option where either only part of the liquidity\n * necessary for the swap is removed, or all of it. This way the rebalance\n * is able to optimize for volume, for efficiency or anything in between\n * 2. swapToDesiredPosition -> move active trading price in the pool to be able to deposit WETH & OETH\n * tokens with the desired pre-configured ratios\n * 3. addLiquidity -> add liquidity into the pool respecting ratio split configuration\n *\n *\n * Exact _amountToSwap, _swapWeth & _minTokenReceived parameters shall be determined by simulating the\n * transaction off-chain. The strategy checks that after the swap the share of the tokens is in the\n * expected ranges.\n *\n * @param _amountToSwap The amount of the token to swap\n * @param _swapWeth Swap using WETH when true, use OETH when false\n * @param _minTokenReceived Slippage check -> minimum amount of token expected in return\n * @param _liquidityToRemovePct Percentage of liquidity to remove -> the percentage amount of liquidity to\n * remove before performing the swap. 1e18 denominated\n */\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived,\n uint256 _liquidityToRemovePct\n ) external nonReentrant onlyGovernorOrStrategist {\n _rebalance(\n _amountToSwap,\n _swapWeth,\n _minTokenReceived,\n _liquidityToRemovePct\n );\n }\n\n // slither-disable-start reentrancy-no-eth\n function _rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived,\n uint256 _liquidityToRemovePct\n ) internal {\n // Remove the required amount of liquidity\n if (_liquidityToRemovePct > 0) {\n _removeLiquidity(_liquidityToRemovePct);\n }\n\n // in some cases (e.g. deposits) we will just want to add liquidity and not\n // issue a swap to move the active trading position within the pool. Before or after a\n // deposit or as a standalone call the strategist might issue a rebalance to move the\n // active trading price to a more desired position.\n if (_amountToSwap > 0) {\n // In case liquidity has been removed and there is still not enough WETH owned by the\n // strategy contract remove additional required amount of WETH.\n if (_swapWeth) _ensureWETHBalance(_amountToSwap);\n\n _swapToDesiredPosition(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n\n // calling check liquidity early so we don't get unexpected errors when adding liquidity\n // in the later stages of this function\n _checkForExpectedPoolPrice(true);\n\n _addLiquidity();\n\n // this call shouldn't be necessary, since adding liquidity shouldn't affect the active\n // trading price. It is a defensive programming measure.\n (, uint256 _wethSharePct) = _checkForExpectedPoolPrice(true);\n\n // revert if protocol insolvent\n _solvencyAssert();\n\n emit PoolRebalanced(_wethSharePct);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @dev Perform a swap so that after the swap the tick has the desired WETH to OETH token share.\n */\n function _swapToDesiredPosition(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n IERC20 _tokenToSwap = IERC20(_swapWeth ? WETH : OETH);\n uint256 _balance = _tokenToSwap.balanceOf(address(this));\n\n if (_balance < _amountToSwap) {\n // This should never trigger since _ensureWETHBalance will already\n // throw an error if there is not enough WETH\n if (_swapWeth) {\n revert NotEnoughWethForSwap(_balance, _amountToSwap);\n }\n // if swapping OETH\n uint256 mintForSwap = _amountToSwap - _balance;\n IVault(vaultAddress).mintForStrategy(mintForSwap);\n }\n\n // SafeERC20 is used for IERC20 transfers. Not sure why slither complains\n // slither-disable-next-line unchecked-transfer\n _tokenToSwap.transfer(address(mPool), _amountToSwap);\n\n // tickLimit: the furthest tick a swap will execute in. If no limit is desired,\n // value should be set to type(int32).max for a tokenAIn (WETH) swap\n // and type(int32).min for a swap where tokenB (OETH) is the input\n\n IMaverickV2Pool.SwapParams memory swapParams = IMaverickV2Pool\n // exactOutput defines whether the amount specified is the output\n // or the input amount of the swap\n .SwapParams({\n amount: _amountToSwap,\n tokenAIn: _swapWeth,\n exactOutput: false,\n tickLimit: TICK_NUMBER\n });\n\n // swaps without a callback as the assets are already sent to the pool\n (, uint256 amountOut) = mPool.swap(\n address(this),\n swapParams,\n bytes(\"\")\n );\n\n /**\n * There could be additional checks here for validating minTokenReceived is within the\n * expected range (e.g. 99% - 101% of the token sent in). Though that doesn't provide\n * any additional security. After the swap the `_checkForExpectedPoolPrice` validates\n * that the swap has moved the price into the expected tick (# -1).\n *\n * If the guardian forgets to set a `_minTokenReceived` and a sandwich attack bends\n * the pool before the swap the `_checkForExpectedPoolPrice` will fail the transaction.\n *\n * A check would not prevent a compromised guardian from stealing funds as multiple\n * transactions each loosing smaller amount of funds are still possible.\n */\n if (amountOut < _minTokenReceived) {\n revert SlippageCheck(amountOut);\n }\n\n /**\n * In the interest of each function in `_rebalance` to leave the contract state as\n * clean as possible the OETH tokens here are burned. This decreases the\n * dependence where `_swapToDesiredPosition` function relies on later functions\n * (`addLiquidity`) to burn the OETH. Reducing the risk of error introduction.\n */\n _burnOethOnTheContract();\n }\n\n /**\n * @dev This function removes the appropriate amount of liquidity to ensure that the required\n * amount of WETH is available on the contract\n *\n * @param _amount WETH balance required on the contract\n */\n function _ensureWETHBalance(uint256 _amount) internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance >= _amount) {\n return;\n }\n\n require(tokenId != 0, \"No liquidity available\");\n uint256 _additionalWethRequired = _amount - _wethBalance;\n (uint256 _wethInThePool, ) = getPositionPrincipal();\n\n if (_wethInThePool < _additionalWethRequired) {\n revert NotEnoughWethLiquidity(\n _wethInThePool,\n _additionalWethRequired\n );\n }\n\n uint256 shareOfWethToRemove = _wethInThePool <= 1\n ? 1e18\n : Math_v5.min(\n /**\n * When dealing with shares of liquidity to remove there is always some\n * rounding involved. After extensive fuzz testing the below approach\n * yielded the best results where the strategy overdraws the least and\n * never removes insufficient amount of WETH.\n */\n (_additionalWethRequired + 2).divPrecisely(_wethInThePool - 1) +\n 2,\n 1e18\n );\n\n _removeLiquidity(shareOfWethToRemove);\n }\n\n /**\n * @dev Decrease partial or all liquidity from the pool.\n * @param _liquidityToDecrease The amount of liquidity to remove denominated in 1e18\n */\n function _removeLiquidity(uint256 _liquidityToDecrease) internal {\n require(_liquidityToDecrease > 0, \"Must remove some liquidity\");\n require(\n _liquidityToDecrease <= 1e18,\n \"Can not remove more than 100% of liquidity\"\n );\n\n // 0 indicates the first (and only) bin in the NFT LP position.\n IMaverickV2Pool.RemoveLiquidityParams memory params = maverickPosition\n .getRemoveParams(tokenId, 0, _liquidityToDecrease);\n (uint256 _amountWeth, uint256 _amountOeth) = maverickPosition\n .removeLiquidityToSender(tokenId, mPool, params);\n\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n\n // needs to be called after the _updateUnderlyingAssets so the updated amount is reflected\n // in the event\n emit LiquidityRemoved(\n _liquidityToDecrease,\n _amountWeth,\n _amountOeth,\n underlyingAssets\n );\n }\n\n /**\n * @dev Burns any OETH tokens remaining on the strategy contract if the balance is\n * above the action threshold.\n */\n function _burnOethOnTheContract() internal {\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(_oethBalance);\n }\n\n /**\n * @notice Returns the percentage of WETH liquidity in the configured ticker\n * owned by this strategy contract.\n * @return uint256 1e18 denominated percentage expressing the share\n */\n function getWETHShare() external view returns (uint256) {\n uint256 _currentPrice = getPoolSqrtPrice();\n return _getWethShare(_currentPrice);\n }\n\n /**\n * @dev Returns the share of WETH in tick denominated in 1e18\n */\n function _getWethShare(uint256 _currentPrice)\n internal\n view\n returns (uint256)\n {\n (\n uint256 wethAmount,\n uint256 oethAmount\n ) = _reservesInTickForGivenPriceAndLiquidity(\n sqrtPriceTickLower,\n sqrtPriceTickHigher,\n _currentPrice,\n 1e24\n );\n\n return wethAmount.divPrecisely(wethAmount + oethAmount);\n }\n\n /**\n * @notice Returns the current pool price in square root\n * @return Square root of the pool price\n */\n function getPoolSqrtPrice() public view returns (uint256) {\n return poolLens.getPoolSqrtPrice(mPool);\n }\n\n /**\n * @notice Returns the current active trading tick of the underlying pool\n * @return _currentTick Current pool trading tick\n */\n function getCurrentTradingTick() public view returns (int32 _currentTick) {\n _currentTick = mPool.getState().activeTick;\n }\n\n /**\n * @notice Mint the initial NFT position\n *\n * @dev This amount is \"gifted\" to the strategy contract and will count as a yield\n * surplus.\n */\n // slither-disable-start reentrancy-no-eth\n function mintInitialPosition() external onlyGovernor nonReentrant {\n require(tokenId == 0, \"Initial position already minted\");\n (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n ) = _getAddLiquidityParams(1e16, 1e16);\n\n // Mint rounded up OETH amount\n if (OETHRequired > 0) {\n IVault(vaultAddress).mintForStrategy(OETHRequired);\n }\n\n _approveTokenAmounts(WETHRequired, OETHRequired);\n\n // Store the tokenId before calling updateUnderlyingAssets as it relies on the tokenId\n // not being 0\n (, , , tokenId) = liquidityManager.mintPositionNftToSender(\n mPool,\n packedSqrtPriceBreaks,\n packedArgs\n );\n\n // burn remaining OETH\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @notice Returns the balance of tokens the strategy holds in the LP position\n * @return _amountWeth Amount of WETH in position\n * @return _amountOeth Amount of OETH in position\n */\n function getPositionPrincipal()\n public\n view\n returns (uint256 _amountWeth, uint256 _amountOeth)\n {\n if (tokenId == 0) {\n return (0, 0);\n }\n\n (_amountWeth, _amountOeth, ) = _getPositionInformation();\n }\n\n /**\n * @dev Returns the balance of tokens the strategy holds in the LP position\n * @return _amountWeth Amount of WETH in position\n * @return _amountOeth Amount of OETH in position\n * @return liquidity Amount of liquidity in the position\n */\n function _getPositionInformation()\n internal\n view\n returns (\n uint256 _amountWeth,\n uint256 _amountOeth,\n uint256 liquidity\n )\n {\n IMaverickV2Position.PositionFullInformation\n memory positionInfo = maverickPosition.tokenIdPositionInformation(\n tokenId,\n 0\n );\n\n require(\n positionInfo.liquidities.length == 1,\n \"Unexpected liquidities length\"\n );\n require(positionInfo.ticks.length == 1, \"Unexpected ticks length\");\n\n _amountWeth = positionInfo.amountA;\n _amountOeth = positionInfo.amountB;\n liquidity = positionInfo.liquidities[0];\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99.8%) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethSupply = IERC20(OETH).totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethSupply) < SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /**\n * @dev Collect Rooster reward token, and send it to the harvesterAddress\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Do nothing if there's no position minted\n if (tokenId > 0) {\n uint32[] memory binIds = new uint32[](1);\n IMaverickV2Pool.TickState memory tickState = mPool.getTick(\n TICK_NUMBER\n );\n // get the binId for the MAV_STATIC_BIN_KIND in tick TICK_NUMBER (-1)\n binIds[0] = tickState.binIdsByTick[0];\n\n uint256 lastEpoch = votingDistributor.lastEpoch();\n\n poolDistributor.claimLp(\n address(this),\n tokenId,\n mPool,\n binIds,\n lastEpoch\n );\n }\n\n // Run the internal inherited function\n _collectRewardTokens();\n }\n\n /***************************************\n Balances and Fees\n ****************************************/\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256)\n {\n require(_asset == WETH, \"Only WETH supported\");\n\n // because of PoolLens inaccuracy there is usually some dust WETH left on the contract\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n // just paranoia check, in case there is OETH in the strategy that for some reason hasn't\n // been burned yet. This should always be 0.\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n return underlyingAssets + _wethBalance + _oethBalance;\n }\n\n /// @dev This function updates the amount of underlying assets with the approach of the least possible\n /// total tokens extracted for the current liquidity in the pool.\n function _updateUnderlyingAssets() internal {\n /**\n * Our net value represent the smallest amount of tokens we are able to extract from the position\n * given our liquidity.\n *\n * The least amount of tokens ex-tractable from the position is where the active trading price is\n * at the edge between tick -1 & tick 0. There the pool is offering 1:1 trades between WETH & OETH.\n * At that moment the pool consists completely of WETH and no OETH.\n *\n * The more swaps from OETH -> WETH happen on the pool the more the price starts to move away from the tick 0\n * towards the middle of tick -1 making OETH (priced in WETH) cheaper.\n */\n\n uint256 _wethAmount = tokenId == 0 ? 0 : _balanceInPosition();\n\n underlyingAssets = _wethAmount;\n emit UnderlyingAssetsUpdated(_wethAmount);\n }\n\n /**\n * @dev Strategy reserves (which consist only of WETH in case of Rooster - Plume pool)\n * when the tick price is closest to parity - assuring the lowest amount of tokens\n * returned for the current position liquidity.\n */\n function _balanceInPosition() internal view returns (uint256 _wethBalance) {\n (, , uint256 liquidity) = _getPositionInformation();\n\n uint256 _oethBalance;\n\n (_wethBalance, _oethBalance) = _reservesInTickForGivenPriceAndLiquidity(\n sqrtPriceTickLower,\n sqrtPriceTickHigher,\n sqrtPriceAtParity,\n liquidity\n );\n\n require(_oethBalance == 0, \"Non zero oethBalance\");\n }\n\n /**\n * @notice Tick dominance denominated in 1e18\n * @return _tickDominance The share of liquidity in TICK_NUMBER tick owned\n * by the strategy contract denominated in 1e18\n */\n function tickDominance() public view returns (uint256 _tickDominance) {\n IMaverickV2Pool.TickState memory tickState = mPool.getTick(TICK_NUMBER);\n\n uint256 wethReserve = tickState.reserveA;\n uint256 oethReserve = tickState.reserveB;\n\n // prettier-ignore\n (uint256 _amountWeth, uint256 _amountOeth, ) = _getPositionInformation();\n\n if (wethReserve + oethReserve == 0) {\n return 0;\n }\n\n _tickDominance = (_amountWeth + _amountOeth).divPrecisely(\n wethReserve + oethReserve\n );\n }\n\n /***************************************\n Hidden functions\n ****************************************/\n /**\n * @dev Unsupported\n */\n function setPTokenAddress(address, address) external pure override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function removePToken(uint256) external pure override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function _abstractSetPToken(address, address) internal pure override {\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function safeApproveAllTokens() external pure override {\n // all the amounts are approved at the time required\n revert(\"Unsupported method\");\n }\n\n /***************************************\n Maverick liquidity utilities\n ****************************************/\n\n /// @notice Calculates deltaA = liquidity * (sqrt(upper) - sqrt(lower))\n /// Calculates deltaB = liquidity / sqrt(lower) - liquidity / sqrt(upper),\n /// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower))\n ///\n /// @dev refactored from here:\n // solhint-disable-next-line max-line-length\n /// https://github.com/rooster-protocol/rooster-contracts/blob/main/v2-supplemental/contracts/libraries/LiquidityUtilities.sol#L665-L695\n function _reservesInTickForGivenPriceAndLiquidity(\n uint256 _lowerSqrtPrice,\n uint256 _upperSqrtPrice,\n uint256 _newSqrtPrice,\n uint256 _liquidity\n ) internal pure returns (uint128 reserveA, uint128 reserveB) {\n if (_liquidity == 0) {\n (reserveA, reserveB) = (0, 0);\n } else {\n uint256 lowerEdge = MathRooster.max(_lowerSqrtPrice, _newSqrtPrice);\n\n reserveA = MathRooster\n .mulCeil(\n _liquidity,\n MathRooster.clip(\n MathRooster.min(_upperSqrtPrice, _newSqrtPrice),\n _lowerSqrtPrice\n )\n )\n .toUint128();\n reserveB = MathRooster\n .mulDivCeil(\n _liquidity,\n 1e18 * MathRooster.clip(_upperSqrtPrice, lowerEdge),\n _upperSqrtPrice * lowerEdge\n )\n .toUint128();\n }\n }\n}\n" + }, + "contracts/strategies/sonic/SonicStakingStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SonicValidatorDelegator } from \"./SonicValidatorDelegator.sol\";\nimport { IWrappedSonic } from \"../../interfaces/sonic/IWrappedSonic.sol\";\n\n/**\n * @title Staking Strategy for Sonic's native S currency\n * @author Origin Protocol Inc\n */\ncontract SonicStakingStrategy is SonicValidatorDelegator {\n // For future use\n uint256[50] private __gap;\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wrappedSonic,\n address _sfc\n ) SonicValidatorDelegator(_baseConfig, _wrappedSonic, _sfc) {}\n\n /// @notice Deposit wrapped S asset into the underlying platform.\n /// @param _asset Address of asset to deposit. Has to be Wrapped Sonic (wS).\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposit Wrapped Sonic (wS) to this strategy and delegate to a validator.\n * @param _asset Address of Wrapped Sonic (wS) token\n * @param _amount Amount of Wrapped Sonic (wS) to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal virtual {\n require(_amount > 0, \"Must deposit something\");\n\n _delegate(_amount);\n emit Deposit(_asset, address(0), _amount);\n }\n\n /**\n * @notice Deposit the entire balance of wrapped S in this strategy contract into\n * the underlying platform.\n */\n function depositAll() external virtual override onlyVault nonReentrant {\n uint256 wSBalance = IERC20(wrappedSonic).balanceOf(address(this));\n\n if (wSBalance > 0) {\n _deposit(wrappedSonic, wSBalance);\n }\n }\n\n /// @notice Withdraw Wrapped Sonic (wS) from this strategy contract.\n /// Used only if some wS is lingering on the contract.\n /// That can happen only when someone sends wS directly to this contract\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset Address of the Wrapped Sonic (wS) token\n /// @param _amount Amount of Wrapped Sonic (wS) to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal override {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(_asset).transfer(_recipient, _amount);\n\n emit Withdrawal(wrappedSonic, address(0), _amount);\n }\n\n /// @notice Transfer all Wrapped Sonic (wS) deposits back to the vault.\n /// This does not withdraw from delegated validators. That has to be done separately with `undelegate`.\n /// Any native S in this strategy will be withdrawn.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 balance = address(this).balance;\n if (balance > 0) {\n IWrappedSonic(wrappedSonic).deposit{ value: balance }();\n }\n uint256 wSBalance = IERC20(wrappedSonic).balanceOf(address(this));\n if (wSBalance > 0) {\n _withdraw(vaultAddress, wrappedSonic, wSBalance);\n }\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset token\n */\n function supportsAsset(address _asset)\n public\n view\n virtual\n override\n returns (bool)\n {\n return _asset == wrappedSonic;\n }\n\n /**\n * @notice is not supported for this strategy as the\n * Wrapped Sonic (wS) token is set at deploy time.\n */\n function setPTokenAddress(address, address)\n external\n view\n override\n onlyGovernor\n {\n revert(\"unsupported function\");\n }\n\n /// @notice is not used by this strategy as all staking rewards are restaked\n function collectRewardTokens() external override nonReentrant {\n revert(\"unsupported function\");\n }\n\n /**\n * @notice is not supported for this strategy as the\n * Wrapped Sonic (wS) token is set at deploy time.\n */\n function removePToken(uint256) external view override onlyGovernor {\n revert(\"unsupported function\");\n }\n\n /// @dev is not used by this strategy but must be implemented as it's abstract\n /// in the inherited `InitializableAbstractStrategy` contract.\n function _abstractSetPToken(address, address) internal virtual override {}\n\n /// @notice is not used by this strategy\n function safeApproveAllTokens() external override onlyGovernor {}\n}\n" + }, + "contracts/strategies/sonic/SonicSwapXAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title SwapX Algorithmic Market Maker (AMO) Strategy\n * @notice AMO strategy for the SwapX OS/wS stable pool\n * @author Origin Protocol Inc\n */\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport { sqrt } from \"../../utils/PRBMath.sol\";\nimport { IBasicToken } from \"../../interfaces/IBasicToken.sol\";\nimport { IPair } from \"../../interfaces/sonic/ISwapXPair.sol\";\nimport { IGauge } from \"../../interfaces/sonic/ISwapXGauge.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\n\ncontract SonicSwapXAMOStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @notice a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n /// @notice Precision for the SwapX Stable AMM (sAMM) invariant k.\n uint256 public constant PRECISION = 1e18;\n\n /// @notice Address of the Wrapped S (wS) token.\n address public immutable ws;\n\n /// @notice Address of the OS token contract.\n address public immutable os;\n\n /// @notice Address of the SwapX Stable pool contract.\n address public immutable pool;\n\n /// @notice Address of the SwapX Gauge contract.\n address public immutable gauge;\n\n /// @notice The max amount the OS/wS price can deviate from peg (1e18)\n /// before deposits are reverted scaled to 18 decimals.\n /// eg 0.01e18 or 1e16 is 1% which is 100 basis points.\n /// This is the amount below and above peg so a 50 basis point deviation (0.005e18)\n /// allows a price range from 0.995 to 1.005.\n uint256 public maxDepeg;\n\n event SwapOTokensToPool(\n uint256 osMinted,\n uint256 wsDepositAmount,\n uint256 osDepositAmount,\n uint256 lpTokens\n );\n event SwapAssetsToPool(\n uint256 wsSwapped,\n uint256 lpTokens,\n uint256 osBurnt\n );\n event MaxDepegUpdated(uint256 maxDepeg);\n\n /**\n * @dev Verifies that the caller is the Strategist of the Vault.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Skim the SwapX pool in case any extra wS or OS tokens were added\n */\n modifier skimPool() {\n IPair(pool).skim(address(this));\n _;\n }\n\n /**\n * @dev Checks the pool is balanced enough to allow deposits.\n */\n modifier nearBalancedPool() {\n // OS/wS price = wS / OS\n // Get the OS/wS price for selling 1 OS for wS\n // As OS is 1, the wS amount is the OS/wS price\n uint256 sellPrice = IPair(pool).getAmountOut(1e18, os);\n\n // Get the amount of OS received from selling 1 wS. This is buying OS.\n uint256 osAmount = IPair(pool).getAmountOut(1e18, ws);\n // Convert to a OS/wS price = wS / OS\n uint256 buyPrice = 1e36 / osAmount;\n\n uint256 pegPrice = 1e18;\n\n require(\n sellPrice >= pegPrice - maxDepeg && buyPrice <= pegPrice + maxDepeg,\n \"price out of range\"\n );\n _;\n }\n\n /**\n * @dev Checks the pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do swaps against the pool.\n * Deposits and withdrawals are proportional to the pool's balances hence don't need this check.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the pool\n (uint256 wsReservesBefore, uint256 osReservesBefore, ) = IPair(pool)\n .getReserves();\n // diff = wS balance - OS balance\n int256 diffBefore = wsReservesBefore.toInt256() -\n osReservesBefore.toInt256();\n\n _;\n\n // Get the asset and OToken balances in the pool\n (uint256 wsReservesAfter, uint256 osReservesAfter, ) = IPair(pool)\n .getReserves();\n // diff = wS balance - OS balance\n int256 diffAfter = wsReservesAfter.toInt256() -\n osReservesAfter.toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OS, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"Assets overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of wS, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"OTokens overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n /**\n * @param _baseConfig The `platformAddress` is the address of the SwapX pool.\n * The `vaultAddress` is the address of the Origin Sonic Vault.\n * @param _os Address of the OS token.\n * @param _ws Address of the Wrapped S (wS) token.\n * @param _gauge Address of the SwapX gauge for the pool.\n */\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _os,\n address _ws,\n address _gauge\n ) InitializableAbstractStrategy(_baseConfig) {\n // Check the pool tokens are correct\n require(\n IPair(_baseConfig.platformAddress).token0() == _ws &&\n IPair(_baseConfig.platformAddress).token1() == _os,\n \"Incorrect pool tokens\"\n );\n // Checked both tokens are to 18 decimals\n require(\n IBasicToken(_ws).decimals() == 18 &&\n IBasicToken(_os).decimals() == 18,\n \"Incorrect token decimals\"\n );\n // Check the SwapX pool is a Stable AMM (sAMM)\n require(\n IPair(_baseConfig.platformAddress).isStable() == true,\n \"Pool not stable\"\n );\n // Check the gauge is for the pool\n require(\n IGauge(_gauge).TOKEN() == _baseConfig.platformAddress,\n \"Incorrect gauge\"\n );\n\n // Set the immutable variables\n os = _os;\n ws = _ws;\n pool = _baseConfig.platformAddress;\n gauge = _gauge;\n\n // This is an implementation contract. The governor is set in the proxy contract.\n _setGovernor(address(0));\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as SwapX strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Array containing SWPx token address\n * @param _maxDepeg The max amount the OS/wS price can deviate from peg (1e18) before deposits are reverted.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n uint256 _maxDepeg\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = pool;\n\n address[] memory _assets = new address[](1);\n _assets[0] = ws;\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n maxDepeg = _maxDepeg;\n\n _approveBase();\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit an amount of Wrapped S (wS) into the SwapX pool.\n * Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @dev This tx must be wrapped by the VaultValueChecker.\n * To minimize loses, the pool should be rebalanced before depositing.\n * The pool's OS/wS price must be within the maxDepeg range.\n * @param _asset Address of Wrapped S (wS) token.\n * @param _wsAmount Amount of Wrapped S (wS) tokens to deposit.\n */\n function deposit(address _asset, uint256 _wsAmount)\n external\n override\n onlyVault\n nonReentrant\n skimPool\n nearBalancedPool\n {\n require(_asset == ws, \"Unsupported asset\");\n require(_wsAmount > 0, \"Must deposit something\");\n\n (uint256 osDepositAmount, ) = _deposit(_wsAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the deposited wS tokens\n emit Deposit(ws, pool, _wsAmount);\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osDepositAmount);\n }\n\n /**\n * @notice Deposit all the strategy's Wrapped S (wS) tokens into the SwapX pool.\n * Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @dev This tx must be wrapped by the VaultValueChecker.\n * To minimize loses, the pool should be rebalanced before depositing.\n * The pool's OS/wS price must be within the maxDepeg range.\n */\n function depositAll()\n external\n override\n onlyVault\n nonReentrant\n skimPool\n nearBalancedPool\n {\n uint256 wsBalance = IERC20(ws).balanceOf(address(this));\n if (wsBalance > 0) {\n (uint256 osDepositAmount, ) = _deposit(wsBalance);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the deposited wS tokens\n emit Deposit(ws, pool, wsBalance);\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osDepositAmount);\n }\n }\n\n /**\n * @dev Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @param _wsAmount Amount of Wrapped S (wS) tokens to deposit.\n * @return osDepositAmount Amount of OS tokens minted and deposited into the pool.\n * @return lpTokens Amount of SwapX pool LP tokens minted and deposited into the gauge.\n */\n function _deposit(uint256 _wsAmount)\n internal\n returns (uint256 osDepositAmount, uint256 lpTokens)\n {\n // Calculate the required amount of OS to mint based on the wS amount.\n osDepositAmount = _calcTokensToMint(_wsAmount);\n\n // Mint the required OS tokens to this strategy\n IVault(vaultAddress).mintForStrategy(osDepositAmount);\n\n // Add wS and OS liquidity to the pool and stake in gauge\n lpTokens = _depositToPoolAndGauge(_wsAmount, osDepositAmount);\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw wS and OS from the SwapX pool, burn the OS,\n * and transfer the wS to the recipient.\n * @param _recipient Address of the Vault.\n * @param _asset Address of the Wrapped S (wS) contract.\n * @param _wsAmount Amount of Wrapped S (wS) to withdraw.\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _wsAmount\n ) external override onlyVault nonReentrant skimPool {\n require(_wsAmount > 0, \"Must withdraw something\");\n require(_asset == ws, \"Unsupported asset\");\n // This strategy can't be set as a default strategy for wS in the Vault.\n // This means the recipient must always be the Vault.\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n // Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n uint256 lpTokens = _calcTokensToBurn(_wsAmount);\n\n // Withdraw pool LP tokens from the gauge and remove assets from from the pool\n _withdrawFromGaugeAndPool(lpTokens);\n\n // Burn all the removed OS and any that was left in the strategy\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Transfer wS to the recipient\n // Note there can be a dust amount of wS left in the strategy as\n // the burn of the pool's LP tokens is rounded up\n require(\n IERC20(ws).balanceOf(address(this)) >= _wsAmount,\n \"Not enough wS removed from pool\"\n );\n IERC20(ws).safeTransfer(_recipient, _wsAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the withdrawn wS tokens\n emit Withdrawal(ws, pool, _wsAmount);\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n }\n\n /**\n * @notice Withdraw all pool LP tokens from the gauge,\n * remove all wS and OS from the SwapX pool,\n * burn all the OS tokens,\n * and transfer all the wS to the Vault contract.\n * @dev There is no solvency check here as withdrawAll can be called to\n * quickly secure assets to the Vault in emergencies.\n */\n function withdrawAll()\n external\n override\n onlyVaultOrGovernor\n nonReentrant\n skimPool\n {\n // Get all the pool LP tokens the strategy has staked in the gauge\n uint256 lpTokens = IGauge(gauge).balanceOf(address(this));\n // Can not withdraw zero LP tokens from the gauge\n if (lpTokens == 0) return;\n\n if (IGauge(gauge).emergency()) {\n // The gauge is in emergency mode\n _emergencyWithdrawFromGaugeAndPool();\n } else {\n // Withdraw pool LP tokens from the gauge and remove assets from from the pool\n _withdrawFromGaugeAndPool(lpTokens);\n }\n\n // Burn all OS in this strategy contract\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Get the strategy contract's wS balance.\n // This includes all that was removed from the SwapX pool and\n // any that was sitting in the strategy contract before the removal.\n uint256 wsBalance = IERC20(ws).balanceOf(address(this));\n IERC20(ws).safeTransfer(vaultAddress, wsBalance);\n\n // Emit event for the withdrawn wS tokens\n emit Withdrawal(ws, pool, wsBalance);\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n }\n\n /***************************************\n Pool Rebalancing\n ****************************************/\n\n /** @notice Used when there is more OS than wS in the pool.\n * wS and OS is removed from the pool, the received wS is swapped for OS\n * and the left over OS in the strategy is burnt.\n * The OS/wS price is < 1.0 so OS is being bought at a discount.\n * @param _wsAmount Amount of Wrapped S (wS) to swap into the pool.\n */\n function swapAssetsToPool(uint256 _wsAmount)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n skimPool\n {\n require(_wsAmount > 0, \"Must swap something\");\n\n // 1. Partially remove liquidity so there’s enough wS for the swap\n\n // Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n uint256 lpTokens = _calcTokensToBurn(_wsAmount);\n require(lpTokens > 0, \"No LP tokens to burn\");\n\n _withdrawFromGaugeAndPool(lpTokens);\n\n // 2. Swap wS for OS against the pool\n // Swap exact amount of wS for OS against the pool\n // There can be a dust amount of wS left in the strategy as the burn of the pool's LP tokens is rounded up\n _swapExactTokensForTokens(_wsAmount, ws, os);\n\n // 3. Burn all the OS left in the strategy from the remove liquidity and swap\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n // Emit event for the swap\n emit SwapAssetsToPool(_wsAmount, lpTokens, osToBurn);\n }\n\n /**\n * @notice Used when there is more wS than OS in the pool.\n * OS is minted and swapped for wS against the pool,\n * more OS is minted and added back into the pool with the swapped out wS.\n * The OS/wS price is > 1.0 so OS is being sold at a premium.\n * @param _osAmount Amount of OS to swap into the pool.\n */\n function swapOTokensToPool(uint256 _osAmount)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n skimPool\n {\n require(_osAmount > 0, \"Must swap something\");\n\n // 1. Mint OS so it can be swapped into the pool\n\n // There can be OS in the strategy from skimming the pool\n uint256 osInStrategy = IERC20(os).balanceOf(address(this));\n require(_osAmount >= osInStrategy, \"Too much OS in strategy\");\n uint256 osToMint = _osAmount - osInStrategy;\n\n // Mint the required OS tokens to this strategy\n IVault(vaultAddress).mintForStrategy(osToMint);\n\n // 2. Swap OS for wS against the pool\n _swapExactTokensForTokens(_osAmount, os, ws);\n\n // The wS is from the swap and any wS that was sitting in the strategy\n uint256 wsDepositAmount = IERC20(ws).balanceOf(address(this));\n\n // 3. Add wS and OS back to the pool in proportion to the pool's reserves\n (uint256 osDepositAmount, uint256 lpTokens) = _deposit(wsDepositAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osToMint + osDepositAmount);\n // Emit event for the swap\n emit SwapOTokensToPool(\n osToMint,\n wsDepositAmount,\n osDepositAmount,\n lpTokens\n );\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Get the wS value of assets in the strategy and SwapX pool.\n * The value of the assets in the pool is calculated assuming the pool is balanced.\n * This way the value can not be manipulated by changing the pool's token balances.\n * @param _asset Address of the Wrapped S (wS) token\n * @return balance Total value in wS.\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == ws, \"Unsupported asset\");\n\n // wS balance needed here for the balance check that happens from vault during depositing.\n balance = IERC20(ws).balanceOf(address(this));\n\n // This assumes 1 gauge LP token = 1 pool LP token\n uint256 lpTokens = IGauge(gauge).balanceOf(address(this));\n if (lpTokens == 0) return balance;\n\n // Add the strategy’s share of the wS and OS tokens in the SwapX pool if the pool was balanced.\n balance += _lpValue(lpTokens);\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == ws;\n }\n\n /**\n * @notice Collect accumulated SWPx (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect SWPx rewards from the gauge\n IGauge(gauge).getReward();\n\n _collectRewardTokens();\n }\n\n /***************************************\n Internal SwapX Pool and Gauge Functions\n ****************************************/\n\n /**\n * @dev Calculate the required amount of OS to mint based on the wS amount.\n * This ensures the proportion of OS tokens being added to the pool matches the proportion of wS tokens.\n * For example, if the added wS tokens is 10% of existing wS tokens in the pool,\n * then the OS tokens being added should also be 10% of the OS tokens in the pool.\n * @param _wsAmount Amount of Wrapped S (wS) to be added to the pool.\n * @return osAmount Amount of OS to be minted and added to the pool.\n */\n function _calcTokensToMint(uint256 _wsAmount)\n internal\n view\n returns (uint256 osAmount)\n {\n (uint256 wsReserves, uint256 osReserves, ) = IPair(pool).getReserves();\n require(wsReserves > 0, \"Empty pool\");\n\n // OS to add = (wS being added * OS in pool) / wS in pool\n osAmount = (_wsAmount * osReserves) / wsReserves;\n }\n\n /**\n * @dev Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n * from the pool.\n * @param _wsAmount Amount of Wrapped S (wS) to be removed from the pool.\n * @return lpTokens Amount of SwapX pool LP tokens to burn.\n */\n function _calcTokensToBurn(uint256 _wsAmount)\n internal\n view\n returns (uint256 lpTokens)\n {\n /* The SwapX pool proportionally returns the reserve tokens when removing liquidity.\n * First, calculate the proportion of required wS tokens against the pools wS reserves.\n * That same proportion is used to calculate the required amount of pool LP tokens.\n * For example, if the required wS tokens is 10% of the pool's wS reserves,\n * then 10% of the pool's LP supply needs to be burned.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognizant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on, the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n (uint256 wsReserves, , ) = IPair(pool).getReserves();\n require(wsReserves > 0, \"Empty pool\");\n\n lpTokens = (_wsAmount * IPair(pool).totalSupply()) / wsReserves;\n lpTokens += 1; // Add 1 to ensure we get enough LP tokens with rounding\n }\n\n /**\n * @dev Deposit Wrapped S (wS) and OS liquidity to the SwapX pool\n * and stake the pool's LP token in the gauge.\n * @param _wsAmount Amount of Wrapped S (wS) to deposit.\n * @param _osAmount Amount of OS to deposit.\n * @return lpTokens Amount of SwapX pool LP tokens minted.\n */\n function _depositToPoolAndGauge(uint256 _wsAmount, uint256 _osAmount)\n internal\n returns (uint256 lpTokens)\n {\n // Transfer wS to the pool\n IERC20(ws).safeTransfer(pool, _wsAmount);\n // Transfer OS to the pool\n IERC20(os).safeTransfer(pool, _osAmount);\n\n // Mint LP tokens from the pool\n lpTokens = IPair(pool).mint(address(this));\n\n // Deposit the pool's LP tokens into the gauge\n IGauge(gauge).deposit(lpTokens);\n }\n\n /**\n * @dev Withdraw pool LP tokens from the gauge and remove wS and OS from the pool.\n * @param _lpTokens Amount of SwapX pool LP tokens to withdraw from the gauge\n */\n function _withdrawFromGaugeAndPool(uint256 _lpTokens) internal {\n require(\n IGauge(gauge).balanceOf(address(this)) >= _lpTokens,\n \"Not enough LP tokens in gauge\"\n );\n\n // Withdraw pool LP tokens from the gauge\n IGauge(gauge).withdraw(_lpTokens);\n\n // Transfer the pool LP tokens to the pool\n IERC20(pool).safeTransfer(pool, _lpTokens);\n\n // Burn the LP tokens and transfer the wS and OS back to the strategy\n IPair(pool).burn(address(this));\n }\n\n /**\n * @dev Withdraw all pool LP tokens from the gauge when it's in emergency mode\n * and remove wS and OS from the pool.\n */\n function _emergencyWithdrawFromGaugeAndPool() internal {\n // Withdraw all pool LP tokens from the gauge\n IGauge(gauge).emergencyWithdraw();\n\n // Get the pool LP tokens in strategy\n uint256 _lpTokens = IERC20(pool).balanceOf(address(this));\n\n // Transfer the pool LP tokens to the pool\n IERC20(pool).safeTransfer(pool, _lpTokens);\n\n // Burn the LP tokens and transfer the wS and OS back to the strategy\n IPair(pool).burn(address(this));\n }\n\n /**\n * @dev Swap exact amount of tokens for another token against the pool.\n * @param _amountIn Amount of tokens to swap into the pool.\n * @param _tokenIn Address of the token going into the pool.\n * @param _tokenOut Address of the token being swapped out of the pool.\n */\n function _swapExactTokensForTokens(\n uint256 _amountIn,\n address _tokenIn,\n address _tokenOut\n ) internal {\n // Transfer in tokens to the pool\n IERC20(_tokenIn).safeTransfer(pool, _amountIn);\n\n // Calculate how much out tokens we get from the swap\n uint256 amountOut = IPair(pool).getAmountOut(_amountIn, _tokenIn);\n\n // Safety check that we are dealing with the correct pool tokens\n require(\n (_tokenIn == ws && _tokenOut == os) ||\n (_tokenIn == os && _tokenOut == ws),\n \"Unsupported swap\"\n );\n\n // Work out the correct order of the amounts for the pool\n (uint256 amount0, uint256 amount1) = _tokenIn == ws\n ? (uint256(0), amountOut)\n : (amountOut, 0);\n\n // Perform the swap on the pool\n IPair(pool).swap(amount0, amount1, address(this), new bytes(0));\n\n // The slippage protection against the amount out is indirectly done\n // via the improvePoolBalance\n }\n\n /// @dev Calculate the value of a LP position in a SwapX stable pool\n /// if the pool was balanced.\n /// @param _lpTokens Amount of LP tokens in the SwapX pool\n /// @return value The wS value of the LP tokens when the pool is balanced\n function _lpValue(uint256 _lpTokens) internal view returns (uint256 value) {\n // Get total supply of LP tokens\n uint256 totalSupply = IPair(pool).totalSupply();\n if (totalSupply == 0) return 0;\n\n // Get the current reserves of the pool\n (uint256 wsReserves, uint256 osReserves, ) = IPair(pool).getReserves();\n\n // Calculate the invariant of the pool assuming both tokens have 18 decimals.\n // k is scaled to 18 decimals.\n uint256 k = _invariant(wsReserves, osReserves);\n\n // If x = y, let’s denote x = y = z (where z is the common reserve value)\n // Substitute z into the invariant:\n // k = z^3 * z + z * z^3\n // k = 2 * z^4\n // Going back the other way to calculate the common reserve value z\n // z = (k / 2) ^ (1/4)\n // the total value of the pool when x = y is 2 * z, which is 2 * (k / 2) ^ (1/4)\n uint256 zSquared = sqrt((k * 1e18) / 2); // 18 + 18 = 36 decimals becomes 18 decimals after sqrt\n uint256 z = sqrt(zSquared * 1e18); // 18 + 18 = 36 decimals becomes 18 decimals after sqrt\n uint256 totalValueOfPool = 2 * z;\n\n // lp value = lp tokens * value of pool / total supply\n value = (_lpTokens * totalValueOfPool) / totalSupply;\n }\n\n /**\n * @dev Compute the invariant for a SwapX stable pool.\n * This assumed both x and y tokens are to 18 decimals which is checked in the constructor.\n * invariant: k = x^3 * y + x * y^3\n * @dev This implementation is copied from SwapX's Pair contract.\n * @param _x The amount of Wrapped S (wS) tokens in the pool\n * @param _y The amount of the OS tokens in the pool\n * @return k The invariant of the SwapX stable pool\n */\n function _invariant(uint256 _x, uint256 _y)\n internal\n pure\n returns (uint256 k)\n {\n uint256 _a = (_x * _y) / PRECISION;\n uint256 _b = ((_x * _x) / PRECISION + (_y * _y) / PRECISION);\n // slither-disable-next-line divide-before-multiply\n k = (_a * _b) / PRECISION;\n }\n\n /**\n * @dev Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalSupply = IERC20(os).totalSupply();\n\n if (\n _totalSupply > 0 &&\n _totalVaultValue.divPrecisely(_totalSupply) < SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Setters\n ****************************************/\n\n /**\n * @notice Set the maximum deviation from the OS/wS peg (1e18) before deposits are reverted.\n * @param _maxDepeg the OS/wS price from peg (1e18) in 18 decimals.\n * eg 0.01e18 or 1e16 is 1% which is 100 basis points.\n */\n function setMaxDepeg(uint256 _maxDepeg) external onlyGovernor {\n maxDepeg = _maxDepeg;\n\n emit MaxDepegUpdated(_maxDepeg);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve SwapX gauge contract to transfer SwapX pool LP tokens\n // This is needed for deposits of SwapX pool LP tokens into the gauge.\n // slither-disable-next-line unused-return\n IPair(pool).approve(address(gauge), type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/sonic/SonicValidatorDelegator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { ISFC } from \"../../interfaces/sonic/ISFC.sol\";\nimport { IWrappedSonic } from \"../../interfaces/sonic/IWrappedSonic.sol\";\n\n/**\n * @title Manages delegation to Sonic validators\n * @notice This contract implements all the required functionality to delegate to,\n undelegate from and withdraw from validators.\n * @author Origin Protocol Inc\n */\nabstract contract SonicValidatorDelegator is InitializableAbstractStrategy {\n /// @notice Address of Sonic's wrapped S token\n address public immutable wrappedSonic;\n /// @notice Sonic's Special Fee Contract (SFC)\n ISFC public immutable sfc;\n\n /// @notice a unique ID for each withdrawal request\n uint256 public nextWithdrawId;\n /// @notice Sonic (S) that is pending withdrawal after undelegating\n uint256 public pendingWithdrawals;\n\n /// @notice List of supported validator IDs that can be delegated to\n uint256[] public supportedValidators;\n\n /// @notice Default validator id to deposit to\n uint256 public defaultValidatorId;\n\n struct WithdrawRequest {\n uint256 validatorId;\n uint256 undelegatedAmount;\n uint256 timestamp;\n }\n /// @notice Mapping of withdrawIds to validatorIds and undelegatedAmounts\n mapping(uint256 => WithdrawRequest) public withdrawals;\n\n /// @notice Address of the registrator - allowed to register, exit and remove validators\n address public validatorRegistrator;\n\n // For future use\n uint256[44] private __gap;\n\n event Delegated(uint256 indexed validatorId, uint256 delegatedAmount);\n event Undelegated(\n uint256 indexed withdrawId,\n uint256 indexed validatorId,\n uint256 undelegatedAmount\n );\n event Withdrawn(\n uint256 indexed withdrawId,\n uint256 indexed validatorId,\n uint256 undelegatedAmount,\n uint256 withdrawnAmount\n );\n event RegistratorChanged(address indexed newAddress);\n event SupportedValidator(uint256 indexed validatorId);\n event UnsupportedValidator(uint256 indexed validatorId);\n event DefaultValidatorIdChanged(uint256 indexed validatorId);\n\n /// @dev Throws if called by any account other than the Registrator or Strategist\n modifier onlyRegistratorOrStrategist() {\n require(\n msg.sender == validatorRegistrator ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Registrator or Strategist\"\n );\n _;\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wrappedSonic,\n address _sfc\n ) InitializableAbstractStrategy(_baseConfig) {\n wrappedSonic = _wrappedSonic;\n sfc = ISFC(_sfc);\n }\n\n function initialize() external virtual onlyGovernor initializer {\n address[] memory rewardTokens = new address[](0);\n address[] memory assets = new address[](1);\n address[] memory pTokens = new address[](1);\n\n assets[0] = address(wrappedSonic);\n pTokens[0] = address(platformAddress);\n\n InitializableAbstractStrategy._initialize(\n rewardTokens,\n assets,\n pTokens\n );\n }\n\n /// @notice Returns the total value of Sonic (S) that is delegated validators.\n /// Wrapped Sonic (wS) deposits that are still to be delegated and any undelegated amounts\n /// still pending a withdrawal.\n /// @param _asset Address of Wrapped Sonic (wS) token\n /// @return balance Total value managed by the strategy\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n\n // add the Wrapped Sonic (wS) in the strategy from deposits that are still to be delegated\n // and any undelegated amounts still pending a withdrawal\n balance =\n IERC20(wrappedSonic).balanceOf(address(this)) +\n pendingWithdrawals;\n\n // For each supported validator, get the staked amount and pending rewards\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; i++) {\n uint256 validator = supportedValidators[i];\n balance += sfc.getStake(address(this), validator);\n balance += sfc.pendingRewards(address(this), validator);\n }\n }\n\n /**\n * @dev Delegate from this strategy to a specific Sonic validator. Called\n * automatically on asset deposit\n * @param _amount the amount of Sonic (S) to delegate.\n */\n function _delegate(uint256 _amount) internal {\n require(\n isSupportedValidator(defaultValidatorId),\n \"Validator not supported\"\n );\n\n // unwrap Wrapped Sonic (wS) to native Sonic (S)\n IWrappedSonic(wrappedSonic).withdraw(_amount);\n\n //slither-disable-next-line arbitrary-send-eth\n sfc.delegate{ value: _amount }(defaultValidatorId);\n\n emit Delegated(defaultValidatorId, _amount);\n }\n\n /**\n * @notice Undelegate from a specific Sonic validator.\n * This needs to be followed by a `withdrawFromSFC` two weeks later.\n * @param _validatorId The Sonic validator ID to undelegate from.\n * @param _undelegateAmount the amount of Sonic (S) to undelegate.\n * @return withdrawId The unique ID of the withdrawal request.\n */\n function undelegate(uint256 _validatorId, uint256 _undelegateAmount)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n returns (uint256 withdrawId)\n {\n withdrawId = _undelegate(_validatorId, _undelegateAmount);\n }\n\n function _undelegate(uint256 _validatorId, uint256 _undelegateAmount)\n internal\n returns (uint256 withdrawId)\n {\n // Can still undelegate even if the validator is no longer supported\n require(_undelegateAmount > 0, \"Must undelegate something\");\n\n uint256 amountDelegated = sfc.getStake(address(this), _validatorId);\n require(\n _undelegateAmount <= amountDelegated,\n \"Insufficient delegation\"\n );\n\n withdrawId = nextWithdrawId++;\n\n withdrawals[withdrawId] = WithdrawRequest(\n _validatorId,\n _undelegateAmount,\n block.timestamp\n );\n pendingWithdrawals += _undelegateAmount;\n\n sfc.undelegate(_validatorId, withdrawId, _undelegateAmount);\n\n emit Undelegated(withdrawId, _validatorId, _undelegateAmount);\n }\n\n /**\n * @notice Withdraw native S from a previously undelegated validator.\n * The native S is wrapped wS and transferred to the Vault.\n * @param _withdrawId The unique withdraw ID used to `undelegate`\n * @return withdrawnAmount The amount of Sonic (S) withdrawn.\n * This can be less than the undelegated amount in the event of slashing.\n */\n function withdrawFromSFC(uint256 _withdrawId)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n returns (uint256 withdrawnAmount)\n {\n require(_withdrawId < nextWithdrawId, \"Invalid withdrawId\");\n\n // Can still withdraw even if the validator is no longer supported\n // Load the withdrawal from storage into memory\n WithdrawRequest memory withdrawal = withdrawals[_withdrawId];\n require(!isWithdrawnFromSFC(_withdrawId), \"Already withdrawn\");\n\n withdrawals[_withdrawId].undelegatedAmount = 0;\n pendingWithdrawals -= withdrawal.undelegatedAmount;\n\n uint256 sBalanceBefore = address(this).balance;\n\n // Try to withdraw from SFC\n try sfc.withdraw(withdrawal.validatorId, _withdrawId) {\n // continue below\n } catch (bytes memory err) {\n bytes4 errorSelector = bytes4(err);\n\n // If the validator has been fully slashed, SFC's withdraw function will\n // revert with a StakeIsFullySlashed custom error.\n if (errorSelector == ISFC.StakeIsFullySlashed.selector) {\n // The validator was fully slashed, so all the delegated amounts were lost.\n // Will swallow the error as we still want to update the\n // withdrawals and pendingWithdrawals storage variables.\n\n // The return param defaults to zero but lets set it explicitly so it's clear\n withdrawnAmount = 0;\n\n emit Withdrawn(\n _withdrawId,\n withdrawal.validatorId,\n withdrawal.undelegatedAmount,\n withdrawnAmount\n );\n\n // Exit here as there is nothing to transfer to the Vault\n return withdrawnAmount;\n } else {\n // Bubble up any other SFC custom errors.\n // Inline assembly is currently the only way to generically rethrow the exact same custom error\n // from the raw bytes err in a catch block while preserving its original selector and parameters.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n revert(add(32, err), mload(err))\n }\n }\n }\n\n // Set return parameter\n withdrawnAmount = address(this).balance - sBalanceBefore;\n\n // Wrap Sonic (S) to Wrapped Sonic (wS)\n IWrappedSonic(wrappedSonic).deposit{ value: withdrawnAmount }();\n\n // Transfer the Wrapped Sonic (wS) to the Vault\n _withdraw(vaultAddress, wrappedSonic, withdrawnAmount);\n\n // withdrawal.undelegatedAmount & withdrawnAmount can differ in case of slashing\n emit Withdrawn(\n _withdrawId,\n withdrawal.validatorId,\n withdrawal.undelegatedAmount,\n withdrawnAmount\n );\n }\n\n /// @notice returns a bool whether a withdrawalId has already been withdrawn or not\n /// @param _withdrawId The unique withdraw ID used to `undelegate`\n function isWithdrawnFromSFC(uint256 _withdrawId)\n public\n view\n returns (bool)\n {\n WithdrawRequest memory withdrawal = withdrawals[_withdrawId];\n require(withdrawal.validatorId > 0, \"Invalid withdrawId\");\n return withdrawal.undelegatedAmount == 0;\n }\n\n /**\n * @notice Restake any pending validator rewards for all supported validators\n * @param _validatorIds List of Sonic validator IDs to restake rewards\n */\n function restakeRewards(uint256[] calldata _validatorIds)\n external\n nonReentrant\n {\n for (uint256 i = 0; i < _validatorIds.length; ++i) {\n require(\n isSupportedValidator(_validatorIds[i]),\n \"Validator not supported\"\n );\n\n uint256 rewards = sfc.pendingRewards(\n address(this),\n _validatorIds[i]\n );\n\n if (rewards > 0) {\n sfc.restakeRewards(_validatorIds[i]);\n }\n }\n\n // The SFC contract will emit Delegated and RestakedRewards events.\n // The checkBalance function should not change as the pending rewards will moved to the staked amount.\n }\n\n /**\n * @notice Claim any pending rewards from validators\n * @param _validatorIds List of Sonic validator IDs to claim rewards\n */\n function collectRewards(uint256[] calldata _validatorIds)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n {\n uint256 sBalanceBefore = address(this).balance;\n\n for (uint256 i = 0; i < _validatorIds.length; ++i) {\n uint256 rewards = sfc.pendingRewards(\n address(this),\n _validatorIds[i]\n );\n\n if (rewards > 0) {\n // The SFC contract will emit ClaimedRewards(delegator (this), validatorId, rewards)\n sfc.claimRewards(_validatorIds[i]);\n }\n }\n\n uint256 rewardsAmount = address(this).balance - sBalanceBefore;\n\n // Wrap Sonic (S) to Wrapped Sonic (wS)\n IWrappedSonic(wrappedSonic).deposit{ value: rewardsAmount }();\n\n // Transfer the Wrapped Sonic (wS) to the Vault\n _withdraw(vaultAddress, wrappedSonic, rewardsAmount);\n }\n\n /**\n * @notice To receive native S from SFC and Wrapped Sonic (wS)\n *\n * @dev This does not prevent donating S tokens to the contract\n * as wrappedSonic has a `withdrawTo` function where a third party\n * owner of wrappedSonic can withdraw to this contract.\n */\n receive() external payable {\n require(\n msg.sender == address(sfc) || msg.sender == wrappedSonic,\n \"S not from allowed contracts\"\n );\n }\n\n /***************************************\n Admin functions\n ****************************************/\n\n /// @notice Set the address of the Registrator which can undelegate, withdraw and collect rewards\n /// @param _validatorRegistrator The address of the Registrator\n function setRegistrator(address _validatorRegistrator)\n external\n onlyGovernor\n {\n validatorRegistrator = _validatorRegistrator;\n emit RegistratorChanged(_validatorRegistrator);\n }\n\n /// @notice Set the default validatorId to delegate to on deposit\n /// @param _validatorId The validator identifier. eg 18\n function setDefaultValidatorId(uint256 _validatorId)\n external\n onlyRegistratorOrStrategist\n {\n require(isSupportedValidator(_validatorId), \"Validator not supported\");\n defaultValidatorId = _validatorId;\n emit DefaultValidatorIdChanged(_validatorId);\n }\n\n /// @notice Allows a validator to be delegated to by the Registrator\n /// @param _validatorId The validator identifier. eg 18\n function supportValidator(uint256 _validatorId) external onlyGovernor {\n require(\n !isSupportedValidator(_validatorId),\n \"Validator already supported\"\n );\n\n supportedValidators.push(_validatorId);\n\n emit SupportedValidator(_validatorId);\n }\n\n /// @notice Removes a validator from the supported list.\n /// Unsupported validators can still be undelegated from, withdrawn from and rewards collected.\n /// @param _validatorId The validator identifier. eg 18\n function unsupportValidator(uint256 _validatorId) external onlyGovernor {\n require(isSupportedValidator(_validatorId), \"Validator not supported\");\n\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; ++i) {\n if (supportedValidators[i] == _validatorId) {\n supportedValidators[i] = supportedValidators[validatorLen - 1];\n supportedValidators.pop();\n break;\n }\n }\n\n uint256 stake = sfc.getStake(address(this), _validatorId);\n\n // undelegate if validator still has funds staked\n if (stake > 0) {\n _undelegate(_validatorId, stake);\n }\n emit UnsupportedValidator(_validatorId);\n }\n\n /// @notice Returns the length of the supportedValidators array\n function supportedValidatorsLength() external view returns (uint256) {\n return supportedValidators.length;\n }\n\n /// @notice Returns whether a validator is supported by this strategy\n /// @param _validatorId The validator identifier\n function isSupportedValidator(uint256 _validatorId)\n public\n view\n returns (bool)\n {\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; ++i) {\n if (supportedValidators[i] == _validatorId) {\n return true;\n }\n }\n return false;\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal virtual;\n}\n" + }, + "contracts/strategies/VaultValueChecker.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IOUSD } from \"../interfaces/IOUSD.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract VaultValueChecker {\n IVault public immutable vault;\n IOUSD public immutable ousd;\n // Snapshot expiration time in seconds.\n // Used to prevent accidental use of an old snapshot, but\n // is not zero to allow easy testing of strategist actions in fork testing\n uint256 constant SNAPSHOT_EXPIRES = 5 * 60;\n\n struct Snapshot {\n uint256 vaultValue;\n uint256 totalSupply;\n uint256 time;\n }\n // By doing per user snapshots, we prevent a reentrancy attack\n // from a third party that updates the snapshot in the middle\n // of an allocation process\n\n mapping(address => Snapshot) public snapshots;\n\n constructor(address _vault, address _ousd) {\n vault = IVault(_vault);\n ousd = IOUSD(_ousd);\n }\n\n function takeSnapshot() external {\n snapshots[msg.sender] = Snapshot({\n vaultValue: vault.totalValue(),\n totalSupply: ousd.totalSupply(),\n time: block.timestamp\n });\n }\n\n function checkDelta(\n int256 expectedProfit,\n int256 profitVariance,\n int256 expectedVaultChange,\n int256 vaultChangeVariance\n ) external {\n // Intentionaly not view so that this method shows up in TX builders\n Snapshot memory snapshot = snapshots[msg.sender];\n int256 vaultChange = toInt256(vault.totalValue()) -\n toInt256(snapshot.vaultValue);\n int256 supplyChange = toInt256(ousd.totalSupply()) -\n toInt256(snapshot.totalSupply);\n int256 profit = vaultChange - supplyChange;\n\n require(\n snapshot.time >= block.timestamp - SNAPSHOT_EXPIRES,\n \"Snapshot too old\"\n );\n require(snapshot.time <= block.timestamp, \"Snapshot too new\");\n require(profit >= expectedProfit - profitVariance, \"Profit too low\");\n require(profit <= expectedProfit + profitVariance, \"Profit too high\");\n require(\n vaultChange >= expectedVaultChange - vaultChangeVariance,\n \"Vault value change too low\"\n );\n require(\n vaultChange <= expectedVaultChange + vaultChangeVariance,\n \"Vault value change too high\"\n );\n }\n\n function toInt256(uint256 value) internal pure returns (int256) {\n // From openzeppelin math/SafeCast.sol\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(\n value <= uint256(type(int256).max),\n \"SafeCast: value doesn't fit in an int256\"\n );\n return int256(value);\n }\n}\n\ncontract OETHVaultValueChecker is VaultValueChecker {\n constructor(address _vault, address _ousd)\n VaultValueChecker(_vault, _ousd)\n {}\n}\n" + }, + "contracts/swapper/Swapper1InchV5.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @notice 1Inch Pathfinder V5 implementation of the general ISwapper interface.\n * @author Origin Protocol Inc\n * @dev It is possible that dust token amounts are left in this contract after a swap.\n * This can happen with some tokens that don't send the full transfer amount.\n * These dust amounts can build up over time and be used by anyone who calls the `swap` function.\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IAggregationExecutor, IOneInchRouter, SwapDescription } from \"../interfaces/IOneInch.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\n\ncontract Swapper1InchV5 is ISwapper {\n using SafeERC20 for IERC20;\n\n /// @notice 1Inch router contract to give allowance to perform swaps\n address public constant SWAP_ROUTER =\n 0x1111111254EEB25477B68fb85Ed929f73A960582;\n\n // swap(address,(address,address,address,address,uint256,uint256,uint256),bytes,bytes)\n bytes4 internal constant SWAP_SELECTOR = 0x12aa3caf;\n // unoswapTo(address,address,uint256,uint256,uint256[])\n bytes4 internal constant UNISWAP_SELECTOR = 0xf78dc253;\n // uniswapV3SwapTo(address,uint256,uint256,uint256[])\n bytes4 internal constant UNISWAPV3_SELECTOR = 0xbc80f1a8;\n\n /**\n * @notice Strategist swaps assets sitting in the contract of the `assetHolder`.\n * @param _fromAsset The token address of the asset being sold by the vault.\n * @param _toAsset The token address of the asset being purchased by the vault.\n * @param _fromAssetAmount The amount of assets being sold by the vault.\n * @param _minToAssetAmount The minimum amount of assets to be purchased.\n * @param _data RLP encoded executer address and bytes data. This is re-encoded tx.data from 1Inch swap API\n */\n function swap(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n ) external override returns (uint256 toAssetAmount) {\n // Decode the function selector from the RLP encoded _data param\n bytes4 swapSelector = bytes4(_data[:4]);\n\n if (swapSelector == SWAP_SELECTOR) {\n // Decode the executer address and data from the RLP encoded _data param\n (, address executer, bytes memory executerData) = abi.decode(\n _data,\n (bytes4, address, bytes)\n );\n SwapDescription memory swapDesc = SwapDescription({\n srcToken: IERC20(_fromAsset),\n dstToken: IERC20(_toAsset),\n srcReceiver: payable(executer),\n dstReceiver: payable(msg.sender),\n amount: _fromAssetAmount,\n minReturnAmount: _minToAssetAmount,\n flags: 4 // 1st bit _PARTIAL_FILL, 2nd bit _REQUIRES_EXTRA_ETH, 3rd bit _SHOULD_CLAIM\n });\n (toAssetAmount, ) = IOneInchRouter(SWAP_ROUTER).swap(\n IAggregationExecutor(executer),\n swapDesc,\n hex\"\",\n executerData\n );\n } else if (swapSelector == UNISWAP_SELECTOR) {\n // Need to get the Uniswap pools data from the _data param\n (, uint256[] memory pools) = abi.decode(_data, (bytes4, uint256[]));\n toAssetAmount = IOneInchRouter(SWAP_ROUTER).unoswapTo(\n payable(msg.sender),\n IERC20(_fromAsset),\n _fromAssetAmount,\n _minToAssetAmount,\n pools\n );\n } else if (swapSelector == UNISWAPV3_SELECTOR) {\n // Need to get the Uniswap pools data from the _data param\n // slither-disable-next-line uninitialized-storage\n (, uint256[] memory pools) = abi.decode(_data, (bytes4, uint256[]));\n toAssetAmount = IOneInchRouter(SWAP_ROUTER).uniswapV3SwapTo(\n payable(msg.sender),\n _fromAssetAmount,\n _minToAssetAmount,\n pools\n );\n } else {\n revert(\"Unsupported swap function\");\n }\n }\n\n /**\n * @notice Approve assets for swapping.\n * @param _assets Array of token addresses to approve.\n * @dev unlimited approval is used as no tokens sit in this contract outside a transaction.\n */\n function approveAssets(address[] memory _assets) external {\n for (uint256 i = 0; i < _assets.length; ++i) {\n // Give the 1Inch router approval to transfer unlimited assets\n IERC20(_assets[i]).safeApprove(SWAP_ROUTER, type(uint256).max);\n }\n }\n}\n" + }, + "contracts/token/BridgedWOETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\n\ncontract BridgedWOETH is\n Governable,\n AccessControlEnumerable,\n Initializable,\n ERC20\n{\n bytes32 public constant MINTER_ROLE = keccak256(\"MINTER_ROLE\");\n bytes32 public constant BURNER_ROLE = keccak256(\"BURNER_ROLE\");\n\n constructor() ERC20(\"Wrapped OETH\", \"wOETH\") {\n // Nobody owns the implementation\n _setGovernor(address(0));\n }\n\n /**\n * @dev Initialize the proxy and set the Governor\n */\n function initialize() external initializer {\n // Governor can grant Minter/Burner roles\n _setupRole(DEFAULT_ADMIN_ROLE, _governor());\n }\n\n /**\n * @dev Mint tokens for `account`\n * @param account Address to mint tokens for\n * @param amount Amount of tokens to mint\n */\n function mint(address account, uint256 amount)\n external\n onlyRole(MINTER_ROLE)\n nonReentrant\n {\n _mint(account, amount);\n }\n\n /**\n * @dev Burns tokens from `account`\n * @param account Address to burn tokens from\n * @param amount Amount of tokens to burn\n */\n function burn(address account, uint256 amount)\n external\n onlyRole(BURNER_ROLE)\n nonReentrant\n {\n _burn(account, amount);\n }\n\n /**\n * @dev Burns tokens from `msg.sender`\n * @param amount Amount of tokens to burn\n */\n function burn(uint256 amount) external onlyRole(BURNER_ROLE) nonReentrant {\n _burn(msg.sender, amount);\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return \"Wrapped OETH\";\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return \"wOETH\";\n }\n\n /**\n * @dev Returns the decimals of the token\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETH is OUSD {\n function symbol() external pure override returns (string memory) {\n return \"OETH\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Origin Ether\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETHBase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBase is OUSD {\n constructor() {\n // Nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n function symbol() external pure override returns (string memory) {\n return \"superOETHb\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Super OETH\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETHPlume.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title Super OETH (Plume) Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETHPlume is OUSD {\n constructor() {\n // Nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n function symbol() external pure override returns (string memory) {\n return \"superOETHp\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Super OETH\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OSonic.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title Origin Sonic (OS) token on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonic is OUSD {\n function symbol() external pure override returns (string memory) {\n return \"OS\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Origin Sonic\";\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/token/OUSDResolutionUpgrade.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract OUSDResolutionUpgrade {\n enum RebaseOptions {\n NotSet,\n OptOut,\n OptIn\n }\n\n // From Initializable\n bool private initialized;\n bool private initializing;\n uint256[50] private ______igap;\n\n // From InitializableERC20Detailed\n uint256[100] private _____ugap;\n string private _name;\n string private _symbol;\n uint8 private _decimals;\n\n // From OUSD\n uint256 private constant MAX_SUPPLY = ~uint128(0); // (2^128) - 1\n uint256 public _totalSupply;\n mapping(address => mapping(address => uint256)) private _allowances;\n address public vaultAddress = address(0);\n mapping(address => uint256) private _creditBalances;\n uint256 private _rebasingCredits;\n uint256 private _rebasingCreditsPerToken;\n uint256 public nonRebasingSupply;\n mapping(address => uint256) public nonRebasingCreditsPerToken;\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) public isUpgraded;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() public view returns (uint256) {\n return _rebasingCreditsPerToken / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCredits() public view returns (uint256) {\n return _rebasingCredits / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() public view returns (uint256) {\n return _rebasingCreditsPerToken;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() public view returns (uint256) {\n return _rebasingCredits;\n }\n\n function upgradeGlobals() external {\n require(isUpgraded[address(0)] == 0, \"Globals already upgraded\");\n require(_rebasingCredits > 0, \"Sanity _rebasingCredits\");\n require(\n _rebasingCreditsPerToken > 0,\n \"Sanity _rebasingCreditsPerToken\"\n );\n isUpgraded[address(0)] = 1;\n _rebasingCredits = _rebasingCredits * RESOLUTION_INCREASE;\n _rebasingCreditsPerToken =\n _rebasingCreditsPerToken *\n RESOLUTION_INCREASE;\n }\n\n function upgradeAccounts(address[] calldata accounts) external {\n for (uint256 i = 0; i < accounts.length; i++) {\n address account = accounts[i];\n require(account != address(0), \"Reserved\");\n require(isUpgraded[account] == 0, \"Account already upgraded\");\n isUpgraded[account] = 1;\n\n // Handle special for non-rebasing accounts\n uint256 nrc = nonRebasingCreditsPerToken[account];\n if (nrc > 0) {\n require(nrc < 1e18, \"Account already highres\");\n nonRebasingCreditsPerToken[account] = nrc * RESOLUTION_INCREASE;\n }\n // Upgrade balance\n uint256 balance = _creditBalances[account];\n require(balance > 0, \"Will not upgrade zero balance\");\n _creditBalances[account] = balance * RESOLUTION_INCREASE;\n }\n }\n\n function creditsBalanceOfHighres(address _account)\n public\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n _creditBalances[_account],\n _creditsPerToken(_account),\n isUpgraded[_account] == 1\n );\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n if (nonRebasingCreditsPerToken[_account] != 0) {\n return nonRebasingCreditsPerToken[_account];\n } else {\n return _rebasingCreditsPerToken;\n }\n }\n}\n" + }, + "contracts/token/WOETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC4626 } from \"../../lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { OETH } from \"./OETH.sol\";\n\n/**\n * @title Wrapped OETH Token Contract\n * @author Origin Protocol Inc\n *\n * @dev An important capability of this contract is that it isn't susceptible to changes of the\n * exchange rate of WOETH/OETH if/when someone sends the underlying asset (OETH) to the contract.\n * If OETH weren't rebasing this could be achieved by solely tracking the ERC20 transfers of the OETH\n * token on mint, deposit, redeem, withdraw. The issue is that OETH is rebasing and OETH balances\n * will change when the token rebases.\n * For that reason the contract logic checks the actual underlying OETH token balance only once\n * (either on a fresh contract creation or upgrade) and considering the WOETH supply and\n * rebasingCreditsPerToken calculates the _adjuster. Once the adjuster is calculated any donations\n * to the contract are ignored. The totalSupply (instead of querying OETH balance) works off of\n * adjuster the current WOETH supply and rebasingCreditsPerToken. This makes WOETH value accrual\n * completely follow OETH's value accrual.\n * WOETH is safe to use in lending markets as the VualtCore's _rebase contains safeguards preventing\n * any sudden large rebases.\n */\n\ncontract WOETH is ERC4626, Governable, Initializable {\n using SafeERC20 for IERC20;\n /* This is a 1e27 adjustment constant that expresses the difference in exchange rate between\n * OETH's rebase since inception (expressed with rebasingCreditsPerToken) and WOETH to OETH\n * conversion.\n *\n * If WOETH and OETH are deployed at the same time, the value of adjuster is a neutral 1e27\n */\n uint256 public adjuster;\n uint256[49] private __gap;\n\n // no need to set ERC20 name and symbol since they are overridden in WOETH & WOETHBase\n constructor(ERC20 underlying_) ERC20(\"\", \"\") ERC4626(underlying_) {}\n\n /**\n * @notice Enable OETH rebasing for this contract\n */\n function initialize() external onlyGovernor initializer {\n OETH(address(asset())).rebaseOptIn();\n\n initialize2();\n }\n\n /**\n * @notice secondary initializer that newly deployed contracts will execute as part\n * of primary initialize function and the existing contracts will have it called\n * as a governance operation.\n */\n function initialize2() public onlyGovernor {\n require(adjuster == 0, \"Initialize2 already called\");\n\n if (totalSupply() == 0) {\n adjuster = 1e27;\n } else {\n adjuster =\n (rebasingCreditsPerTokenHighres() *\n ERC20(asset()).balanceOf(address(this))) /\n totalSupply();\n }\n }\n\n function name()\n public\n view\n virtual\n override(ERC20, IERC20Metadata)\n returns (string memory)\n {\n return \"Wrapped OETH\";\n }\n\n function symbol()\n public\n view\n virtual\n override(ERC20, IERC20Metadata)\n returns (string memory)\n {\n return \"wOETH\";\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends. Cannot transfer OETH\n * @param asset_ Address for the asset\n * @param amount_ Amount of the asset to transfer\n */\n function transferToken(address asset_, uint256 amount_)\n external\n onlyGovernor\n {\n require(asset_ != address(asset()), \"Cannot collect core asset\");\n IERC20(asset_).safeTransfer(governor(), amount_);\n }\n\n /// @inheritdoc ERC4626\n function convertToShares(uint256 assets)\n public\n view\n virtual\n override\n returns (uint256 shares)\n {\n return (assets * rebasingCreditsPerTokenHighres()) / adjuster;\n }\n\n /// @inheritdoc ERC4626\n function convertToAssets(uint256 shares)\n public\n view\n virtual\n override\n returns (uint256 assets)\n {\n return (shares * adjuster) / rebasingCreditsPerTokenHighres();\n }\n\n /// @inheritdoc ERC4626\n function totalAssets() public view override returns (uint256) {\n return (totalSupply() * adjuster) / rebasingCreditsPerTokenHighres();\n }\n\n function rebasingCreditsPerTokenHighres() internal view returns (uint256) {\n return OETH(asset()).rebasingCreditsPerTokenHighres();\n }\n}\n" + }, + "contracts/token/WOETHBase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WOETH } from \"./WOETH.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\n\ncontract WOETHBase is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name() public view virtual override returns (string memory) {\n return \"Wrapped Super OETH\";\n }\n\n function symbol() public view virtual override returns (string memory) {\n return \"wsuperOETHb\";\n }\n}\n" + }, + "contracts/token/WOETHPlume.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WOETH } from \"./WOETH.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @title wOETH (Plume) Token Contract\n * @author Origin Protocol Inc\n */\n\ncontract WOETHPlume is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name() public view virtual override returns (string memory) {\n return \"Wrapped Super OETH\";\n }\n\n function symbol() public view virtual override returns (string memory) {\n return \"wsuperOETHp\";\n }\n}\n" + }, + "contracts/token/WOSonic.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { WOETH } from \"./WOETH.sol\";\n\n/**\n * @title Wrapped Origin Sonic (wOS) token on Sonic\n * @author Origin Protocol Inc\n */\ncontract WOSonic is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"Wrapped OS\";\n }\n\n function symbol()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"wOS\";\n }\n}\n" + }, + "contracts/token/WrappedOusd.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { WOETH } from \"./WOETH.sol\";\n\n/**\n * @title Wrapped OUSD Token Contract\n * @author Origin Protocol Inc\n */\ncontract WrappedOusd is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"Wrapped OUSD\";\n }\n\n function symbol()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"WOUSD\";\n }\n}\n" + }, + "contracts/utils/AerodromeAMOQuoter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.7;\n\nimport { ICLPool } from \"../interfaces/aerodrome/ICLPool.sol\";\nimport { IQuoterV2 } from \"../interfaces/aerodrome/IQuoterV2.sol\";\nimport { IAMOStrategy } from \"../interfaces/aerodrome/IAMOStrategy.sol\";\n\n/// @title QuoterHelper\n/// @author Origin Protocol\n/// @notice Helper for Aerodrome AMO Quoter, as `_quoteAmountToSwapBeforeRebalance` use try/catch method and\n/// this can only be used when calling external contracts.\ncontract QuoterHelper {\n ////////////////////////////////////////////////////////////////\n /// --- STRUCTS & ENUMS\n ////////////////////////////////////////////////////////////////\n enum RevertReasons {\n DefaultStatus,\n RebalanceOutOfBounds,\n NotInExpectedTickRange,\n NotEnoughWethForSwap,\n NotEnoughWethLiquidity,\n UnexpectedError,\n Found,\n NotFound\n }\n\n struct RebalanceStatus {\n RevertReasons reason;\n uint256 currentPoolWETHShare; // Case 1\n uint256 allowedWETHShareStart; // Case 1\n uint256 allowedWETHShareEnd; // Case 1\n int24 currentTick; // Case 2\n uint256 balanceWETH; // Case 3\n uint256 amountWETH; // Case 3\n string revertMessage;\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTANT & IMMUTABLE\n ////////////////////////////////////////////////////////////////\n uint256 public constant BINARY_MIN_AMOUNT = 1 wei;\n\n uint256 public constant BINARY_MAX_ITERATIONS = 40;\n uint256 public constant PERCENTAGE_BASE = 1e18; // 100%\n uint256 public constant ALLOWED_VARIANCE_PERCENTAGE = 1e15; // 0.1%\n\n ////////////////////////////////////////////////////////////////\n /// --- VARIABLES STORAGE\n ////////////////////////////////////////////////////////////////\n ICLPool public immutable clPool;\n IQuoterV2 public immutable quoterV2;\n IAMOStrategy public immutable strategy;\n\n address public originalGovernor;\n\n ////////////////////////////////////////////////////////////////\n /// --- ERRORS & EVENTS\n ////////////////////////////////////////////////////////////////\n error UnexpectedError(string message);\n error OutOfIterations(uint256 iterations);\n error ValidAmount(\n uint256 amount,\n uint256 iterations,\n bool swapWETHForOETHB\n );\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////////////////\n constructor(IAMOStrategy _strategy, IQuoterV2 _quoterV2) {\n strategy = _strategy;\n quoterV2 = _quoterV2;\n clPool = _strategy.clPool();\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- FUNCTIONS\n ////////////////////////////////////////////////////////////////\n /// @notice This call can only end with a revert.\n function getAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) public {\n if (\n overrideBottomWethShare != type(uint256).max ||\n overrideTopWethShare != type(uint256).max\n ) {\n // Current values\n uint256 shareStart = strategy.allowedWethShareStart();\n uint256 shareEnd = strategy.allowedWethShareEnd();\n\n // Override values\n if (overrideBottomWethShare != type(uint256).max) {\n shareStart = overrideBottomWethShare;\n }\n if (overrideTopWethShare != type(uint256).max) {\n shareEnd = overrideTopWethShare;\n }\n\n strategy.setAllowedPoolWethShareInterval(shareStart, shareEnd);\n }\n\n uint256 iterations = 0;\n uint256 low = BINARY_MIN_AMOUNT;\n uint256 high;\n (high, ) = strategy.getPositionPrincipal();\n int24 lowerTick = strategy.lowerTick();\n int24 upperTick = strategy.upperTick();\n bool swapWETHForOETHB = getSwapDirectionForRebalance();\n\n while (low <= high && iterations < BINARY_MAX_ITERATIONS) {\n uint256 mid = (low + high) / 2;\n\n RebalanceStatus memory status = getRebalanceStatus(\n mid,\n swapWETHForOETHB\n );\n\n // Best case, we found the `amount` that will reach the target pool share!\n // We can revert with the amount and the number of iterations\n if (status.reason == RevertReasons.Found) {\n revert ValidAmount(mid, iterations, swapWETHForOETHB);\n }\n\n // If the rebalance failed then we should try to change the amount.\n // We will handle all possible revert reasons here.\n\n // Case 1: Rebalance out of bounds\n // If the pool is out of bounds, we need to adjust the amount to reach the target pool share\n if (status.reason == RevertReasons.RebalanceOutOfBounds) {\n // If the current pool share is less than the target pool share, we need to increase the amount\n if (\n swapWETHForOETHB\n ? status.currentPoolWETHShare <\n status.allowedWETHShareStart\n : status.currentPoolWETHShare >\n status.allowedWETHShareEnd\n ) {\n low = mid + 1;\n }\n // Else we need to decrease the amount\n else {\n high = mid;\n }\n }\n\n // Case 2: Not in expected tick range\n // If the pool is not in the expected tick range, we need to adjust the amount\n // to reach the target pool share\n if (status.reason == RevertReasons.NotInExpectedTickRange) {\n // If we are buying OETHb and the current tick is greater than the lower tick,\n //we need to increase the amount in order to continue to push price down.\n // If we are selling OETHb and the current tick is less than the upper tick,\n // we need to increase the amount in order to continue to push price up.\n if (\n swapWETHForOETHB\n ? status.currentTick > lowerTick\n : status.currentTick < upperTick\n ) {\n low = mid + 1;\n }\n // Else we need to decrease the amount\n else {\n high = mid;\n }\n }\n\n // Case 3: Not enough WETH for swap\n // If we don't have enough WETH to swap, we need to decrease the amount\n // This error can happen, when initial value of mid is too high, so we need to decrease it\n if (status.reason == RevertReasons.NotEnoughWethForSwap) {\n high = mid;\n }\n\n // Case 4: Not enough WETH liquidity\n // If we don't have enough WETH liquidity\n // Revert for the moment, we need to improve this\n if (status.reason == RevertReasons.NotEnoughWethLiquidity) {\n revert(\"Quoter: Not enough WETH liquidity\");\n }\n\n // Case 5: Unexpected error\n // Worst case, it reverted with an unexpected error.\n if (status.reason == RevertReasons.UnexpectedError) {\n revert UnexpectedError(status.revertMessage);\n }\n\n iterations++;\n }\n\n // Case 6: Out of iterations\n // If we didn't find the amount after the max iterations, we need to revert.\n revert OutOfIterations(iterations);\n }\n\n /// @notice Get the status of the rebalance\n /// @param amount The amount of token to swap\n /// @param swapWETH True if we need to swap WETH for OETHb, false otherwise\n /// @return status The status of the rebalance\n function getRebalanceStatus(uint256 amount, bool swapWETH)\n public\n returns (RebalanceStatus memory status)\n {\n try strategy.rebalance(amount, swapWETH, 0) {\n status.reason = RevertReasons.Found;\n return status;\n } catch Error(string memory reason) {\n status.reason = RevertReasons.UnexpectedError;\n status.revertMessage = reason;\n } catch (bytes memory reason) {\n bytes4 receivedSelector = bytes4(reason);\n\n // Case 1: Rebalance out of bounds\n if (\n receivedSelector ==\n IAMOStrategy.PoolRebalanceOutOfBounds.selector\n ) {\n uint256 currentPoolWETHShare;\n uint256 allowedWETHShareStart;\n uint256 allowedWETHShareEnd;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n currentPoolWETHShare := mload(add(reason, 0x24))\n allowedWETHShareStart := mload(add(reason, 0x44))\n allowedWETHShareEnd := mload(add(reason, 0x64))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.RebalanceOutOfBounds,\n currentPoolWETHShare: currentPoolWETHShare,\n allowedWETHShareStart: allowedWETHShareStart,\n allowedWETHShareEnd: allowedWETHShareEnd,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 2: Not in expected tick range\n if (\n receivedSelector ==\n IAMOStrategy.OutsideExpectedTickRange.selector\n ) {\n int24 currentTick;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n currentTick := mload(add(reason, 0x24))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.NotInExpectedTickRange,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: currentTick,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 3: Not enough WETH for swap\n if (\n receivedSelector == IAMOStrategy.NotEnoughWethForSwap.selector\n ) {\n uint256 balanceWETH;\n uint256 amountWETH;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n balanceWETH := mload(add(reason, 0x24))\n amountWETH := mload(add(reason, 0x44))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.NotEnoughWethForSwap,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: balanceWETH,\n amountWETH: amountWETH,\n revertMessage: \"\"\n });\n }\n\n // Case 4: Not enough WETH liquidity\n if (\n receivedSelector == IAMOStrategy.NotEnoughWethLiquidity.selector\n ) {\n return\n RebalanceStatus({\n reason: RevertReasons.NotEnoughWethLiquidity,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 5: Unexpected error\n return\n RebalanceStatus({\n reason: RevertReasons.UnexpectedError,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: abi.decode(reason, (string))\n });\n }\n }\n\n /// @notice Get the swap direction to reach the target price before rebalance.\n /// @return bool True if we need to swap WETH for OETHb, false otherwise.\n function getSwapDirectionForRebalance() public view returns (bool) {\n uint160 currentPrice = strategy.getPoolX96Price();\n uint160 ticker0Price = strategy.sqrtRatioX96TickLower();\n uint160 ticker1Price = strategy.sqrtRatioX96TickHigher();\n uint256 allowedWethShareStart = strategy.allowedWethShareStart();\n uint256 allowedWethShareEnd = strategy.allowedWethShareEnd();\n uint160 mid = uint160(allowedWethShareStart + allowedWethShareEnd) / 2;\n // slither-disable-start divide-before-multiply\n uint160 targetPrice = (ticker0Price *\n mid +\n ticker1Price *\n (1 ether - mid)) / 1 ether;\n // slither-disable-end divide-before-multiply\n\n return currentPrice > targetPrice;\n }\n\n // returns total amount in the position principal of the Aerodrome AMO strategy. Needed as a\n // separate function because of the limitation in local variable count in getAmountToSwapToReachPrice\n function getTotalStrategyPosition() internal returns (uint256) {\n (uint256 wethAmount, uint256 oethBalance) = strategy\n .getPositionPrincipal();\n return wethAmount + oethBalance;\n }\n\n /// @notice Get the amount of tokens to swap to reach the target price.\n /// @dev This act like a quoter, i.e. the transaction is not performed.\n /// @dev Because the amount to swap can be largely overestimated, because CLAMM alow partial orders,\n /// i.e. when we ask to swap a very large amount, with a close priceLimite, it will swap only a part of it,\n /// and not revert. So if overestimated amount is to high, use a custom maxAmount to avoid this issue.\n /// @param sqrtPriceTargetX96 The target price to reach.\n /// @return amount The amount of tokens to swap.\n /// @return iterations The number of iterations to find the amount.\n /// @return swapWETHForOETHB True if we need to swap WETH for OETHb, false otherwise.\n /// @return sqrtPriceX96After The price after the swap.\n function getAmountToSwapToReachPrice(uint160 sqrtPriceTargetX96)\n public\n returns (\n uint256,\n uint256,\n bool,\n uint160\n )\n {\n uint256 iterations = 0;\n uint256 low = BINARY_MIN_AMOUNT;\n // high search start is twice the position principle of Aerodrome AMO strategy.\n // should be more than enough\n uint256 high = getTotalStrategyPosition() * 2;\n bool swapWETHForOETHB = getSwapDirection(sqrtPriceTargetX96);\n\n while (low <= high && iterations < BINARY_MAX_ITERATIONS) {\n uint256 mid = (low + high) / 2;\n\n // Call QuoterV2 from SugarHelper\n (uint256 amountOut, uint160 sqrtPriceX96After, , ) = quoterV2\n .quoteExactInputSingle(\n IQuoterV2.QuoteExactInputSingleParams({\n tokenIn: swapWETHForOETHB\n ? clPool.token0()\n : clPool.token1(),\n tokenOut: swapWETHForOETHB\n ? clPool.token1()\n : clPool.token0(),\n amountIn: mid,\n tickSpacing: strategy.tickSpacing(),\n sqrtPriceLimitX96: sqrtPriceTargetX96\n })\n );\n\n if (\n isWithinAllowedVariance(sqrtPriceX96After, sqrtPriceTargetX96)\n ) {\n /** Very important to return `amountOut` instead of `mid` as the first return parameter.\n * The issues was that when quoting we impose a swap price limit (sqrtPriceLimitX96: sqrtPriceTargetX96)\n * and in that case the `amountIn` acts like a maximum amount to swap. And we don't know how much\n * of that amount was actually consumed. For that reason we \"estimate\" it by returning the\n * amountOut since that is only going to be a couple of basis point away from amountIn in the\n * worst cases.\n *\n * Note: we could be returning mid instead of amountOut in cases when those values are only basis\n * points apart (assuming that complete balance of amountIn has been consumed) but that might increase\n * complexity too much in an already complex contract.\n */\n return (\n amountOut,\n iterations,\n swapWETHForOETHB,\n sqrtPriceX96After\n );\n } else if (low == high) {\n // target swap amount not found.\n // might be that \"high\" amount is too low on start\n revert(\"SwapAmountNotFound\");\n } else if (\n swapWETHForOETHB\n ? sqrtPriceX96After > sqrtPriceTargetX96\n : sqrtPriceX96After < sqrtPriceTargetX96\n ) {\n low = mid + 1;\n } else {\n high = mid;\n }\n iterations++;\n }\n\n revert OutOfIterations(iterations);\n }\n\n /// @notice Check if the current price is within the allowed variance in comparison to the target price\n /// @return bool True if the current price is within the allowed variance, false otherwise\n function isWithinAllowedVariance(\n uint160 sqrtPriceCurrentX96,\n uint160 sqrtPriceTargetX96\n ) public view returns (bool) {\n uint160 range = strategy.sqrtRatioX96TickHigher() -\n strategy.sqrtRatioX96TickLower();\n if (sqrtPriceCurrentX96 > sqrtPriceTargetX96) {\n return\n (sqrtPriceCurrentX96 - sqrtPriceTargetX96) <=\n (ALLOWED_VARIANCE_PERCENTAGE * range) / PERCENTAGE_BASE;\n } else {\n return\n (sqrtPriceTargetX96 - sqrtPriceCurrentX96) <=\n (ALLOWED_VARIANCE_PERCENTAGE * range) / PERCENTAGE_BASE;\n }\n }\n\n /// @notice Get the swap direction to reach the target price.\n /// @param sqrtPriceTargetX96 The target price to reach.\n /// @return bool True if we need to swap WETH for OETHb, false otherwise.\n function getSwapDirection(uint160 sqrtPriceTargetX96)\n public\n view\n returns (bool)\n {\n uint160 currentPrice = strategy.getPoolX96Price();\n return currentPrice > sqrtPriceTargetX96;\n }\n\n function claimGovernanceOnAMO() public {\n originalGovernor = strategy.governor();\n strategy.claimGovernance();\n }\n\n function giveBackGovernanceOnAMO() public {\n require(\n originalGovernor != address(0),\n \"Quoter: Original governor not set\"\n );\n strategy.transferGovernance(originalGovernor);\n }\n}\n\n/// @title AerodromeAMOQuoter\n/// @author Origin Protocol\n/// @notice Quoter for Aerodrome AMO\ncontract AerodromeAMOQuoter {\n ////////////////////////////////////////////////////////////////\n /// --- STRUCTS & ENUMS\n ///////////////////////////////////////////////////////////////\n struct Data {\n uint256 amount;\n uint256 iterations;\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- VARIABLES STORAGE\n ////////////////////////////////////////////////////////////////\n QuoterHelper public immutable quoterHelper;\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////////////////\n constructor(address _strategy, address _quoterV2) {\n quoterHelper = new QuoterHelper(\n IAMOStrategy(_strategy),\n IQuoterV2(_quoterV2)\n );\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- ERRORS & EVENTS\n ////////////////////////////////////////////////////////////////\n event ValueFound(uint256 value, uint256 iterations, bool swapWETHForOETHB);\n event ValueFoundBis(\n uint256 value,\n uint256 iterations,\n bool swapWETHForOETHB,\n uint160 sqrtPriceAfterX96\n );\n event ValueNotFound(string message);\n\n ////////////////////////////////////////////////////////////////\n /// --- FUNCTIONS\n ////////////////////////////////////////////////////////////////\n /// @notice Use this to get the amount to swap before rebalance\n /// @dev This call will only revert, check the logs to get returned values.\n /// @dev Need to perform this call while impersonating the governor or strategist of AMO.\n /// @return data Data struct with the amount and the number of iterations\n function quoteAmountToSwapBeforeRebalance()\n public\n returns (Data memory data)\n {\n return\n _quoteAmountToSwapBeforeRebalance(\n type(uint256).max,\n type(uint256).max\n );\n }\n\n /// @notice Use this to get the amount to swap before rebalance and\n /// update allowedWethShareStart and allowedWethShareEnd on AMO.\n /// @dev This call will only revert, check the logs to get returned values.\n /// @dev Need to perform this call while impersonating the governor of AMO.\n /// @param overrideBottomWethShare New value for the allowedWethShareStart on AMO.\n /// Use type(uint256).max to keep same value.\n /// @param overrideTopWethShare New value for the allowedWethShareEnd on AMO.\n /// Use type(uint256).max to keep same value.\n /// @return data Data struct with the amount and the number of iterations\n function quoteAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) public returns (Data memory data) {\n return\n _quoteAmountToSwapBeforeRebalance(\n overrideBottomWethShare,\n overrideTopWethShare\n );\n }\n\n /// @notice Internal logic for quoteAmountToSwapBeforeRebalance.\n function _quoteAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) internal returns (Data memory data) {\n try\n quoterHelper.getAmountToSwapBeforeRebalance(\n overrideBottomWethShare,\n overrideTopWethShare\n )\n {\n revert(\"Previous call should only revert, it cannot succeed\");\n } catch (bytes memory reason) {\n bytes4 receivedSelector = bytes4(reason);\n if (receivedSelector == QuoterHelper.ValidAmount.selector) {\n uint256 value;\n uint256 iterations;\n bool swapWETHForOETHB;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n value := mload(add(reason, 0x24))\n iterations := mload(add(reason, 0x44))\n swapWETHForOETHB := mload(add(reason, 0x64))\n }\n emit ValueFound(value, iterations, swapWETHForOETHB);\n return Data({ amount: value, iterations: iterations });\n }\n\n if (receivedSelector == QuoterHelper.OutOfIterations.selector) {\n emit ValueNotFound(\"Out of iterations\");\n revert(\"Out of iterations\");\n }\n\n emit ValueNotFound(\"Unexpected error\");\n revert(abi.decode(reason, (string)));\n }\n }\n\n /// @notice Use this to get the amount to swap to reach the target price after swap.\n /// @dev This call will only revert, check the logs to get returned values.\n /// @param sqrtPriceTargetX96 The target price to reach.\n function quoteAmountToSwapToReachPrice(uint160 sqrtPriceTargetX96) public {\n (\n uint256 amount,\n uint256 iterations,\n bool swapWETHForOETHB,\n uint160 sqrtPriceAfterX96\n ) = quoterHelper.getAmountToSwapToReachPrice(sqrtPriceTargetX96);\n\n emit ValueFoundBis(\n amount,\n iterations,\n swapWETHForOETHB,\n sqrtPriceAfterX96\n );\n }\n\n function claimGovernance() public {\n quoterHelper.claimGovernanceOnAMO();\n }\n\n function giveBackGovernance() public {\n quoterHelper.giveBackGovernanceOnAMO();\n }\n}\n" + }, + "contracts/utils/BalancerErrors.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity >=0.7.4 <0.9.0;\n\n// solhint-disable\n\n/**\n * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are\n * supported.\n * Uses the default 'BAL' prefix for the error code\n */\nfunction _require(bool condition, uint256 errorCode) pure {\n if (!condition) _revert(errorCode);\n}\n\n/**\n * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are\n * supported.\n */\nfunction _require(\n bool condition,\n uint256 errorCode,\n bytes3 prefix\n) pure {\n if (!condition) _revert(errorCode, prefix);\n}\n\n/**\n * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported.\n * Uses the default 'BAL' prefix for the error code\n */\nfunction _revert(uint256 errorCode) pure {\n _revert(errorCode, 0x42414c); // This is the raw byte representation of \"BAL\"\n}\n\n/**\n * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported.\n */\nfunction _revert(uint256 errorCode, bytes3 prefix) pure {\n uint256 prefixUint = uint256(uint24(prefix));\n // We're going to dynamically create a revert string based on the error code, with the following format:\n // 'BAL#{errorCode}'\n // where the code is left-padded with zeroes to three digits (so they range from 000 to 999).\n //\n // We don't have revert strings embedded in the contract to save bytecode size: it takes much less space to store a\n // number (8 to 16 bits) than the individual string characters.\n //\n // The dynamic string creation algorithm that follows could be implemented in Solidity, but assembly allows for a\n // much denser implementation, again saving bytecode size. Given this function unconditionally reverts, this is a\n // safe place to rely on it without worrying about how its usage might affect e.g. memory contents.\n assembly {\n // First, we need to compute the ASCII representation of the error code. We assume that it is in the 0-999\n // range, so we only need to convert three digits. To convert the digits to ASCII, we add 0x30, the value for\n // the '0' character.\n\n let units := add(mod(errorCode, 10), 0x30)\n\n errorCode := div(errorCode, 10)\n let tenths := add(mod(errorCode, 10), 0x30)\n\n errorCode := div(errorCode, 10)\n let hundreds := add(mod(errorCode, 10), 0x30)\n\n // With the individual characters, we can now construct the full string.\n // We first append the '#' character (0x23) to the prefix. In the case of 'BAL', it results in 0x42414c23 ('BAL#')\n // Then, we shift this by 24 (to provide space for the 3 bytes of the error code), and add the\n // characters to it, each shifted by a multiple of 8.\n // The revert reason is then shifted left by 200 bits (256 minus the length of the string, 7 characters * 8 bits\n // per character = 56) to locate it in the most significant part of the 256 slot (the beginning of a byte\n // array).\n let formattedPrefix := shl(24, add(0x23, shl(8, prefixUint)))\n\n let revertReason := shl(\n 200,\n add(\n formattedPrefix,\n add(add(units, shl(8, tenths)), shl(16, hundreds))\n )\n )\n\n // We can now encode the reason in memory, which can be safely overwritten as we're about to revert. The encoded\n // message will have the following layout:\n // [ revert reason identifier ] [ string location offset ] [ string length ] [ string contents ]\n\n // The Solidity revert reason identifier is 0x08c739a0, the function selector of the Error(string) function. We\n // also write zeroes to the next 28 bytes of memory, but those are about to be overwritten.\n mstore(\n 0x0,\n 0x08c379a000000000000000000000000000000000000000000000000000000000\n )\n // Next is the offset to the location of the string, which will be placed immediately after (20 bytes away).\n mstore(\n 0x04,\n 0x0000000000000000000000000000000000000000000000000000000000000020\n )\n // The string length is fixed: 7 characters.\n mstore(0x24, 7)\n // Finally, the string itself is stored.\n mstore(0x44, revertReason)\n\n // Even if the string is only 7 bytes long, we need to return a full 32 byte slot containing it. The length of\n // the encoded message is therefore 4 + 32 + 32 + 32 = 100.\n revert(0, 100)\n }\n}\n\nlibrary Errors {\n // Math\n uint256 internal constant ADD_OVERFLOW = 0;\n uint256 internal constant SUB_OVERFLOW = 1;\n uint256 internal constant SUB_UNDERFLOW = 2;\n uint256 internal constant MUL_OVERFLOW = 3;\n uint256 internal constant ZERO_DIVISION = 4;\n uint256 internal constant DIV_INTERNAL = 5;\n uint256 internal constant X_OUT_OF_BOUNDS = 6;\n uint256 internal constant Y_OUT_OF_BOUNDS = 7;\n uint256 internal constant PRODUCT_OUT_OF_BOUNDS = 8;\n uint256 internal constant INVALID_EXPONENT = 9;\n\n // Input\n uint256 internal constant OUT_OF_BOUNDS = 100;\n uint256 internal constant UNSORTED_ARRAY = 101;\n uint256 internal constant UNSORTED_TOKENS = 102;\n uint256 internal constant INPUT_LENGTH_MISMATCH = 103;\n uint256 internal constant ZERO_TOKEN = 104;\n uint256 internal constant INSUFFICIENT_DATA = 105;\n\n // Shared pools\n uint256 internal constant MIN_TOKENS = 200;\n uint256 internal constant MAX_TOKENS = 201;\n uint256 internal constant MAX_SWAP_FEE_PERCENTAGE = 202;\n uint256 internal constant MIN_SWAP_FEE_PERCENTAGE = 203;\n uint256 internal constant MINIMUM_BPT = 204;\n uint256 internal constant CALLER_NOT_VAULT = 205;\n uint256 internal constant UNINITIALIZED = 206;\n uint256 internal constant BPT_IN_MAX_AMOUNT = 207;\n uint256 internal constant BPT_OUT_MIN_AMOUNT = 208;\n uint256 internal constant EXPIRED_PERMIT = 209;\n uint256 internal constant NOT_TWO_TOKENS = 210;\n uint256 internal constant DISABLED = 211;\n\n // Pools\n uint256 internal constant MIN_AMP = 300;\n uint256 internal constant MAX_AMP = 301;\n uint256 internal constant MIN_WEIGHT = 302;\n uint256 internal constant MAX_STABLE_TOKENS = 303;\n uint256 internal constant MAX_IN_RATIO = 304;\n uint256 internal constant MAX_OUT_RATIO = 305;\n uint256 internal constant MIN_BPT_IN_FOR_TOKEN_OUT = 306;\n uint256 internal constant MAX_OUT_BPT_FOR_TOKEN_IN = 307;\n uint256 internal constant NORMALIZED_WEIGHT_INVARIANT = 308;\n uint256 internal constant INVALID_TOKEN = 309;\n uint256 internal constant UNHANDLED_JOIN_KIND = 310;\n uint256 internal constant ZERO_INVARIANT = 311;\n uint256 internal constant ORACLE_INVALID_SECONDS_QUERY = 312;\n uint256 internal constant ORACLE_NOT_INITIALIZED = 313;\n uint256 internal constant ORACLE_QUERY_TOO_OLD = 314;\n uint256 internal constant ORACLE_INVALID_INDEX = 315;\n uint256 internal constant ORACLE_BAD_SECS = 316;\n uint256 internal constant AMP_END_TIME_TOO_CLOSE = 317;\n uint256 internal constant AMP_ONGOING_UPDATE = 318;\n uint256 internal constant AMP_RATE_TOO_HIGH = 319;\n uint256 internal constant AMP_NO_ONGOING_UPDATE = 320;\n uint256 internal constant STABLE_INVARIANT_DIDNT_CONVERGE = 321;\n uint256 internal constant STABLE_GET_BALANCE_DIDNT_CONVERGE = 322;\n uint256 internal constant RELAYER_NOT_CONTRACT = 323;\n uint256 internal constant BASE_POOL_RELAYER_NOT_CALLED = 324;\n uint256 internal constant REBALANCING_RELAYER_REENTERED = 325;\n uint256 internal constant GRADUAL_UPDATE_TIME_TRAVEL = 326;\n uint256 internal constant SWAPS_DISABLED = 327;\n uint256 internal constant CALLER_IS_NOT_LBP_OWNER = 328;\n uint256 internal constant PRICE_RATE_OVERFLOW = 329;\n uint256 internal constant INVALID_JOIN_EXIT_KIND_WHILE_SWAPS_DISABLED = 330;\n uint256 internal constant WEIGHT_CHANGE_TOO_FAST = 331;\n uint256 internal constant LOWER_GREATER_THAN_UPPER_TARGET = 332;\n uint256 internal constant UPPER_TARGET_TOO_HIGH = 333;\n uint256 internal constant UNHANDLED_BY_LINEAR_POOL = 334;\n uint256 internal constant OUT_OF_TARGET_RANGE = 335;\n uint256 internal constant UNHANDLED_EXIT_KIND = 336;\n uint256 internal constant UNAUTHORIZED_EXIT = 337;\n uint256 internal constant MAX_MANAGEMENT_SWAP_FEE_PERCENTAGE = 338;\n uint256 internal constant UNHANDLED_BY_MANAGED_POOL = 339;\n uint256 internal constant UNHANDLED_BY_PHANTOM_POOL = 340;\n uint256 internal constant TOKEN_DOES_NOT_HAVE_RATE_PROVIDER = 341;\n uint256 internal constant INVALID_INITIALIZATION = 342;\n uint256 internal constant OUT_OF_NEW_TARGET_RANGE = 343;\n uint256 internal constant FEATURE_DISABLED = 344;\n uint256 internal constant UNINITIALIZED_POOL_CONTROLLER = 345;\n uint256 internal constant SET_SWAP_FEE_DURING_FEE_CHANGE = 346;\n uint256 internal constant SET_SWAP_FEE_PENDING_FEE_CHANGE = 347;\n uint256 internal constant CHANGE_TOKENS_DURING_WEIGHT_CHANGE = 348;\n uint256 internal constant CHANGE_TOKENS_PENDING_WEIGHT_CHANGE = 349;\n uint256 internal constant MAX_WEIGHT = 350;\n uint256 internal constant UNAUTHORIZED_JOIN = 351;\n uint256 internal constant MAX_MANAGEMENT_AUM_FEE_PERCENTAGE = 352;\n uint256 internal constant FRACTIONAL_TARGET = 353;\n uint256 internal constant ADD_OR_REMOVE_BPT = 354;\n uint256 internal constant INVALID_CIRCUIT_BREAKER_BOUNDS = 355;\n uint256 internal constant CIRCUIT_BREAKER_TRIPPED = 356;\n uint256 internal constant MALICIOUS_QUERY_REVERT = 357;\n uint256 internal constant JOINS_EXITS_DISABLED = 358;\n\n // Lib\n uint256 internal constant REENTRANCY = 400;\n uint256 internal constant SENDER_NOT_ALLOWED = 401;\n uint256 internal constant PAUSED = 402;\n uint256 internal constant PAUSE_WINDOW_EXPIRED = 403;\n uint256 internal constant MAX_PAUSE_WINDOW_DURATION = 404;\n uint256 internal constant MAX_BUFFER_PERIOD_DURATION = 405;\n uint256 internal constant INSUFFICIENT_BALANCE = 406;\n uint256 internal constant INSUFFICIENT_ALLOWANCE = 407;\n uint256 internal constant ERC20_TRANSFER_FROM_ZERO_ADDRESS = 408;\n uint256 internal constant ERC20_TRANSFER_TO_ZERO_ADDRESS = 409;\n uint256 internal constant ERC20_MINT_TO_ZERO_ADDRESS = 410;\n uint256 internal constant ERC20_BURN_FROM_ZERO_ADDRESS = 411;\n uint256 internal constant ERC20_APPROVE_FROM_ZERO_ADDRESS = 412;\n uint256 internal constant ERC20_APPROVE_TO_ZERO_ADDRESS = 413;\n uint256 internal constant ERC20_TRANSFER_EXCEEDS_ALLOWANCE = 414;\n uint256 internal constant ERC20_DECREASED_ALLOWANCE_BELOW_ZERO = 415;\n uint256 internal constant ERC20_TRANSFER_EXCEEDS_BALANCE = 416;\n uint256 internal constant ERC20_BURN_EXCEEDS_ALLOWANCE = 417;\n uint256 internal constant SAFE_ERC20_CALL_FAILED = 418;\n uint256 internal constant ADDRESS_INSUFFICIENT_BALANCE = 419;\n uint256 internal constant ADDRESS_CANNOT_SEND_VALUE = 420;\n uint256 internal constant SAFE_CAST_VALUE_CANT_FIT_INT256 = 421;\n uint256 internal constant GRANT_SENDER_NOT_ADMIN = 422;\n uint256 internal constant REVOKE_SENDER_NOT_ADMIN = 423;\n uint256 internal constant RENOUNCE_SENDER_NOT_ALLOWED = 424;\n uint256 internal constant BUFFER_PERIOD_EXPIRED = 425;\n uint256 internal constant CALLER_IS_NOT_OWNER = 426;\n uint256 internal constant NEW_OWNER_IS_ZERO = 427;\n uint256 internal constant CODE_DEPLOYMENT_FAILED = 428;\n uint256 internal constant CALL_TO_NON_CONTRACT = 429;\n uint256 internal constant LOW_LEVEL_CALL_FAILED = 430;\n uint256 internal constant NOT_PAUSED = 431;\n uint256 internal constant ADDRESS_ALREADY_ALLOWLISTED = 432;\n uint256 internal constant ADDRESS_NOT_ALLOWLISTED = 433;\n uint256 internal constant ERC20_BURN_EXCEEDS_BALANCE = 434;\n uint256 internal constant INVALID_OPERATION = 435;\n uint256 internal constant CODEC_OVERFLOW = 436;\n uint256 internal constant IN_RECOVERY_MODE = 437;\n uint256 internal constant NOT_IN_RECOVERY_MODE = 438;\n uint256 internal constant INDUCED_FAILURE = 439;\n uint256 internal constant EXPIRED_SIGNATURE = 440;\n uint256 internal constant MALFORMED_SIGNATURE = 441;\n uint256 internal constant SAFE_CAST_VALUE_CANT_FIT_UINT64 = 442;\n uint256 internal constant UNHANDLED_FEE_TYPE = 443;\n uint256 internal constant BURN_FROM_ZERO = 444;\n\n // Vault\n uint256 internal constant INVALID_POOL_ID = 500;\n uint256 internal constant CALLER_NOT_POOL = 501;\n uint256 internal constant SENDER_NOT_ASSET_MANAGER = 502;\n uint256 internal constant USER_DOESNT_ALLOW_RELAYER = 503;\n uint256 internal constant INVALID_SIGNATURE = 504;\n uint256 internal constant EXIT_BELOW_MIN = 505;\n uint256 internal constant JOIN_ABOVE_MAX = 506;\n uint256 internal constant SWAP_LIMIT = 507;\n uint256 internal constant SWAP_DEADLINE = 508;\n uint256 internal constant CANNOT_SWAP_SAME_TOKEN = 509;\n uint256 internal constant UNKNOWN_AMOUNT_IN_FIRST_SWAP = 510;\n uint256 internal constant MALCONSTRUCTED_MULTIHOP_SWAP = 511;\n uint256 internal constant INTERNAL_BALANCE_OVERFLOW = 512;\n uint256 internal constant INSUFFICIENT_INTERNAL_BALANCE = 513;\n uint256 internal constant INVALID_ETH_INTERNAL_BALANCE = 514;\n uint256 internal constant INVALID_POST_LOAN_BALANCE = 515;\n uint256 internal constant INSUFFICIENT_ETH = 516;\n uint256 internal constant UNALLOCATED_ETH = 517;\n uint256 internal constant ETH_TRANSFER = 518;\n uint256 internal constant CANNOT_USE_ETH_SENTINEL = 519;\n uint256 internal constant TOKENS_MISMATCH = 520;\n uint256 internal constant TOKEN_NOT_REGISTERED = 521;\n uint256 internal constant TOKEN_ALREADY_REGISTERED = 522;\n uint256 internal constant TOKENS_ALREADY_SET = 523;\n uint256 internal constant TOKENS_LENGTH_MUST_BE_2 = 524;\n uint256 internal constant NONZERO_TOKEN_BALANCE = 525;\n uint256 internal constant BALANCE_TOTAL_OVERFLOW = 526;\n uint256 internal constant POOL_NO_TOKENS = 527;\n uint256 internal constant INSUFFICIENT_FLASH_LOAN_BALANCE = 528;\n\n // Fees\n uint256 internal constant SWAP_FEE_PERCENTAGE_TOO_HIGH = 600;\n uint256 internal constant FLASH_LOAN_FEE_PERCENTAGE_TOO_HIGH = 601;\n uint256 internal constant INSUFFICIENT_FLASH_LOAN_FEE_AMOUNT = 602;\n uint256 internal constant AUM_FEE_PERCENTAGE_TOO_HIGH = 603;\n\n // FeeSplitter\n uint256 internal constant SPLITTER_FEE_PERCENTAGE_TOO_HIGH = 700;\n\n // Misc\n uint256 internal constant UNIMPLEMENTED = 998;\n uint256 internal constant SHOULD_NOT_HAPPEN = 999;\n}\n" + }, + "contracts/utils/DepositContractUtils.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract DepositContractUtils {\n function calculateDepositDataRoot(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature\n ) public pure returns (bytes32 node) {\n uint256 deposit_amount = 32 ether / 1 gwei;\n bytes memory amount = to_little_endian_64(uint64(deposit_amount));\n\n // Compute deposit data root (`DepositData` hash tree root)\n bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, bytes16(0)));\n bytes32 signature_root = sha256(\n abi.encodePacked(\n sha256(abi.encodePacked(signature[:64])),\n sha256(abi.encodePacked(signature[64:], bytes32(0)))\n )\n );\n node = sha256(\n abi.encodePacked(\n sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)),\n sha256(abi.encodePacked(amount, bytes24(0), signature_root))\n )\n );\n }\n\n function to_little_endian_64(uint64 value)\n internal\n pure\n returns (bytes memory ret)\n {\n ret = new bytes(8);\n bytes8 bytesValue = bytes8(value);\n // Byteswapping during copying to bytes.\n ret[0] = bytesValue[7];\n ret[1] = bytesValue[6];\n ret[2] = bytesValue[5];\n ret[3] = bytesValue[4];\n ret[4] = bytesValue[3];\n ret[5] = bytesValue[2];\n ret[6] = bytesValue[1];\n ret[7] = bytesValue[0];\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/utils/InitializableERC20Detailed.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @dev Optional functions from the ERC20 standard.\n * Converted from openzeppelin/contracts/token/ERC20/ERC20Detailed.sol\n * @author Origin Protocol Inc\n */\nabstract contract InitializableERC20Detailed is IERC20 {\n // Storage gap to skip storage from prior to OUSD reset\n uint256[100] private _____gap;\n\n string private _name;\n string private _symbol;\n uint8 private _decimals;\n\n /**\n * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of\n * these values are immutable: they can only be set once during\n * construction.\n * @notice To avoid variable shadowing appended `Arg` after arguments name.\n */\n function _initialize(\n string memory nameArg,\n string memory symbolArg,\n uint8 decimalsArg\n ) internal {\n _name = nameArg;\n _symbol = symbolArg;\n _decimals = decimalsArg;\n }\n\n /**\n * @notice Returns the name of the token.\n */\n function name() public view returns (string memory) {\n return _name;\n }\n\n /**\n * @notice Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view returns (string memory) {\n return _symbol;\n }\n\n /**\n * @notice Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5,05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei.\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view returns (uint8) {\n return _decimals;\n }\n}\n" + }, + "contracts/utils/PRBMath.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// Copied from the PRBMath library\n// https://github.com/PaulRBerg/prb-math/blob/main/src/Common.sol\n\n/// @notice Calculates the square root of x using the Babylonian method.\n///\n/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.\n///\n/// Notes:\n/// - If x is not a perfect square, the result is rounded down.\n/// - Credits to OpenZeppelin for the explanations in comments below.\n///\n/// @param x The uint256 number for which to calculate the square root.\n/// @return result The result as a uint256.\n/// @custom:smtchecker abstract-function-nondet\nfunction sqrt(uint256 x) pure returns (uint256 result) {\n if (x == 0) {\n return 0;\n }\n\n // For our first guess, we calculate the biggest power of 2 which is smaller than the square root of x.\n //\n // We know that the \"msb\" (most significant bit) of x is a power of 2 such that we have:\n //\n // $$\n // msb(x) <= x <= 2*msb(x)$\n // $$\n //\n // We write $msb(x)$ as $2^k$, and we get:\n //\n // $$\n // k = log_2(x)\n // $$\n //\n // Thus, we can write the initial inequality as:\n //\n // $$\n // 2^{log_2(x)} <= x <= 2*2^{log_2(x)+1} \\\\\n // sqrt(2^k) <= sqrt(x) < sqrt(2^{k+1}) \\\\\n // 2^{k/2} <= sqrt(x) < 2^{(k+1)/2} <= 2^{(k/2)+1}\n // $$\n //\n // Consequently, $2^{log_2(x) /2} is a good first approximation of sqrt(x) with at least one correct bit.\n uint256 xAux = uint256(x);\n result = 1;\n if (xAux >= 2**128) {\n xAux >>= 128;\n result <<= 64;\n }\n if (xAux >= 2**64) {\n xAux >>= 64;\n result <<= 32;\n }\n if (xAux >= 2**32) {\n xAux >>= 32;\n result <<= 16;\n }\n if (xAux >= 2**16) {\n xAux >>= 16;\n result <<= 8;\n }\n if (xAux >= 2**8) {\n xAux >>= 8;\n result <<= 4;\n }\n if (xAux >= 2**4) {\n xAux >>= 4;\n result <<= 2;\n }\n if (xAux >= 2**2) {\n result <<= 1;\n }\n\n // At this point, `result` is an estimation with at least one bit of precision. We know the true value has at\n // most 128 bits, since it is the square root of a uint256. Newton's method converges quadratically (precision\n // doubles at every iteration). We thus need at most 7 iteration to turn our partial result with one bit of\n // precision into the expected uint128 result.\n unchecked {\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n\n // If x is not a perfect square, round the result toward zero.\n uint256 roundedResult = x / result;\n if (result >= roundedResult) {\n result = roundedResult;\n }\n }\n}\n" + }, + "contracts/utils/StableMath.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\n// Based on StableMath from Stability Labs Pty. Ltd.\n// https://github.com/mstable/mStable-contracts/blob/master/contracts/shared/StableMath.sol\n\nlibrary StableMath {\n using SafeMath for uint256;\n\n /**\n * @dev Scaling unit for use in specific calculations,\n * where 1 * 10**18, or 1e18 represents a unit '1'\n */\n uint256 private constant FULL_SCALE = 1e18;\n\n /***************************************\n Helpers\n ****************************************/\n\n /**\n * @dev Adjust the scale of an integer\n * @param to Decimals to scale to\n * @param from Decimals to scale from\n */\n function scaleBy(\n uint256 x,\n uint256 to,\n uint256 from\n ) internal pure returns (uint256) {\n if (to > from) {\n x = x.mul(10**(to - from));\n } else if (to < from) {\n // slither-disable-next-line divide-before-multiply\n x = x.div(10**(from - to));\n }\n return x;\n }\n\n /***************************************\n Precise Arithmetic\n ****************************************/\n\n /**\n * @dev Multiplies two precise units, and then truncates by the full scale\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit\n */\n function mulTruncate(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulTruncateScale(x, y, FULL_SCALE);\n }\n\n /**\n * @dev Multiplies two precise units, and then truncates by the given scale. For example,\n * when calculating 90% of 10e18, (10e18 * 9e17) / 1e18 = (9e36) / 1e18 = 9e18\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @param scale Scale unit\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit\n */\n function mulTruncateScale(\n uint256 x,\n uint256 y,\n uint256 scale\n ) internal pure returns (uint256) {\n // e.g. assume scale = fullScale\n // z = 10e18 * 9e17 = 9e36\n uint256 z = x.mul(y);\n // return 9e36 / 1e18 = 9e18\n return z.div(scale);\n }\n\n /**\n * @dev Multiplies two precise units, and then truncates by the full scale, rounding up the result\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit, rounded up to the closest base unit.\n */\n function mulTruncateCeil(uint256 x, uint256 y)\n internal\n pure\n returns (uint256)\n {\n // e.g. 8e17 * 17268172638 = 138145381104e17\n uint256 scaled = x.mul(y);\n // e.g. 138145381104e17 + 9.99...e17 = 138145381113.99...e17\n uint256 ceil = scaled.add(FULL_SCALE.sub(1));\n // e.g. 13814538111.399...e18 / 1e18 = 13814538111\n return ceil.div(FULL_SCALE);\n }\n\n /**\n * @dev Precisely divides two units, by first scaling the left hand operand. Useful\n * for finding percentage weightings, i.e. 8e18/10e18 = 80% (or 8e17)\n * @param x Left hand input to division\n * @param y Right hand input to division\n * @return Result after multiplying the left operand by the scale, and\n * executing the division on the right hand input.\n */\n function divPrecisely(uint256 x, uint256 y)\n internal\n pure\n returns (uint256)\n {\n // e.g. 8e18 * 1e18 = 8e36\n uint256 z = x.mul(FULL_SCALE);\n // e.g. 8e36 / 10e18 = 8e17\n return z.div(y);\n }\n}\n" + }, + "contracts/vault/OETHBaseVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"./OETHVaultAdmin.sol\";\n\n/**\n * @title OETH Base VaultAdmin Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBaseVaultAdmin is OETHVaultAdmin {\n constructor(address _weth) OETHVaultAdmin(_weth) {}\n}\n" + }, + "contracts/vault/OETHBaseVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { OETHVaultCore } from \"./OETHVaultCore.sol\";\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\n\n/**\n * @title OETH Base VaultCore Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBaseVaultCore is OETHVaultCore {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(address _weth) OETHVaultCore(_weth) {}\n\n // @inheritdoc OETHVaultCore\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n override\n {\n // Only Strategist or Governor can redeem using the Vault for now.\n // We don't have the onlyGovernorOrStrategist modifier on VaultCore.\n // Since we won't be using that modifier anywhere in the VaultCore as well,\n // the check has been added inline instead of moving it to VaultStorage.\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n\n super._redeem(_amount, _minimumUnitAmount);\n }\n}\n" + }, + "contracts/vault/OETHBaseZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\n\ncontract OETHBaseZapper {\n IERC20 public immutable oethb;\n IERC4626 public immutable woethb;\n IVault public immutable vault;\n\n IWETH9 public constant weth =\n IWETH9(0x4200000000000000000000000000000000000006);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(\n address _oethb,\n address _woethb,\n address _vault\n ) {\n oethb = IERC20(_oethb);\n woethb = IERC4626(_woethb);\n vault = IVault(_vault);\n\n weth.approve(address(_vault), type(uint256).max);\n IERC20(_oethb).approve(_woethb, type(uint256).max);\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return.\n * Will verify that the user is sent 1:1 for ETH.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit ETH and receive superOETHb in return\n * Will verify that the user is sent 1:1 for ETH.\n * @return Amount of OETH sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap ETH\n weth.deposit{ value: balance }();\n\n // Mint with WETH\n return _mint(balance, msg.sender);\n }\n\n /**\n * @dev Deposit ETH and receive superOETHb in return\n * @param minReceived min amount of wsuperOETHb to receive\n * @return Amount of wsuperOETHb sent to user\n */\n function depositETHForWrappedTokens(uint256 minReceived)\n external\n payable\n returns (uint256)\n {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap ETH\n weth.deposit{ value: balance }();\n\n // Mint with WETH\n uint256 mintedOethb = _mint(balance, address(this));\n\n // Wrap superOETHb into wsuperOETHb\n uint256 mintedWoethb = woethb.deposit(mintedOethb, msg.sender);\n\n require(mintedWoethb >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWoethb;\n }\n\n /**\n * @dev Deposit WETH and receive superOETHb in return\n * @param wethAmount Amount of WETH to deposit\n * @param minReceived min amount of wsuperOETHb to receive\n * @return Amount of wsuperOETHb sent to user\n */\n function depositWETHForWrappedTokens(\n uint256 wethAmount,\n uint256 minReceived\n ) external returns (uint256) {\n // slither-disable-next-line unchecked-transfer unused-return\n weth.transferFrom(msg.sender, address(this), wethAmount);\n\n emit Zap(msg.sender, address(weth), wethAmount);\n\n // Mint with WETH\n uint256 mintedOethb = _mint(wethAmount, address(this));\n\n // Wrap superOETHb into wsuperOETHb\n uint256 mintedWoethb = woethb.deposit(mintedOethb, msg.sender);\n\n require(mintedWoethb >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWoethb;\n }\n\n /**\n * @dev Internal function to mint superOETHb with WETH\n * @param minOETH Minimum amount of OETH to for user to receive\n * @param recipient Address that receives the tokens\n * @return Amount of OETH sent to user\n */\n function _mint(uint256 minOETH, address recipient)\n internal\n returns (uint256)\n {\n uint256 toMint = weth.balanceOf(address(this));\n vault.mint(address(weth), toMint, minOETH);\n uint256 mintedAmount = oethb.balanceOf(address(this));\n require(mintedAmount >= minOETH, \"Zapper: not enough minted\");\n\n if (recipient != address(this)) {\n require(oethb.transfer(recipient, mintedAmount));\n }\n\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/OETHVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Vault } from \"./Vault.sol\";\n\n/**\n * @title OETH Vault Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVault is Vault {\n\n}\n" + }, + "contracts/vault/OETHVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { VaultAdmin } from \"./VaultAdmin.sol\";\n\n/**\n * @title OETH VaultAdmin Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVaultAdmin is VaultAdmin {\n using SafeERC20 for IERC20;\n\n address public immutable weth;\n\n constructor(address _weth) {\n weth = _weth;\n }\n\n /**\n * @notice Adds a strategy to the mint whitelist.\n * Reverts if strategy isn't approved on Vault.\n * @param strategyAddr Strategy address\n */\n function addStrategyToMintWhitelist(address strategyAddr)\n external\n onlyGovernor\n {\n require(strategies[strategyAddr].isSupported, \"Strategy not approved\");\n\n require(\n !isMintWhitelistedStrategy[strategyAddr],\n \"Already whitelisted\"\n );\n\n isMintWhitelistedStrategy[strategyAddr] = true;\n\n emit StrategyAddedToMintWhitelist(strategyAddr);\n }\n\n /**\n * @notice Removes a strategy from the mint whitelist.\n * @param strategyAddr Strategy address\n */\n function removeStrategyFromMintWhitelist(address strategyAddr)\n external\n onlyGovernor\n {\n // Intentionally skipping `strategies.isSupported` check since\n // we may wanna remove an address even after removing the strategy\n\n require(isMintWhitelistedStrategy[strategyAddr], \"Not whitelisted\");\n\n isMintWhitelistedStrategy[strategyAddr] = false;\n\n emit StrategyRemovedFromMintWhitelist(strategyAddr);\n }\n\n /// @dev Simplified version of the deposit function as WETH is the only supported asset.\n function _depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal override {\n require(\n strategies[_strategyToAddress].isSupported,\n \"Invalid to Strategy\"\n );\n require(\n _assets.length == 1 && _amounts.length == 1 && _assets[0] == weth,\n \"Only WETH is supported\"\n );\n\n // Check the there is enough WETH to transfer once the WETH reserved for the withdrawal queue is accounted for\n require(_amounts[0] <= _wethAvailable(), \"Not enough WETH available\");\n\n // Send required amount of funds to the strategy\n IERC20(weth).safeTransfer(_strategyToAddress, _amounts[0]);\n\n // Deposit all the funds that have been sent to the strategy\n IStrategy(_strategyToAddress).depositAll();\n }\n\n function _withdrawFromStrategy(\n address _recipient,\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal override {\n super._withdrawFromStrategy(\n _recipient,\n _strategyFromAddress,\n _assets,\n _amounts\n );\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n function _withdrawAllFromStrategy(address _strategyAddr) internal override {\n super._withdrawAllFromStrategy(_strategyAddr);\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n function _withdrawAllFromStrategies() internal override {\n super._withdrawAllFromStrategies();\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n /// @dev Calculate how much WETH in the vault is not reserved for the withdrawal queue.\n // That is, it is available to be redeemed or deposited into a strategy.\n function _wethAvailable() internal view returns (uint256 wethAvailable) {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n uint256 outstandingWithdrawals = queue.queued - queue.claimed;\n\n // The amount of sitting in WETH in the vault\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // If there is not enough WETH in the vault to cover the outstanding withdrawals\n if (wethBalance <= outstandingWithdrawals) {\n return 0;\n }\n\n return wethBalance - outstandingWithdrawals;\n }\n\n function _swapCollateral(\n address,\n address,\n uint256,\n uint256,\n bytes calldata\n ) internal pure override returns (uint256) {\n revert(\"Collateral swap not supported\");\n }\n}\n" + }, + "contracts/vault/OETHVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { VaultCore } from \"./VaultCore.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\n\n/**\n * @title OETH VaultCore Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVaultCore is VaultCore {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n address public immutable weth;\n uint256 public wethAssetIndex;\n\n // For future use (because OETHBaseVaultCore inherits from this)\n uint256[50] private __gap;\n\n constructor(address _weth) {\n weth = _weth;\n }\n\n /**\n * @dev Caches WETH's index in `allAssets` variable.\n * Reduces gas usage by redeem by caching that.\n */\n function cacheWETHAssetIndex() external onlyGovernor {\n uint256 assetCount = allAssets.length;\n for (uint256 i; i < assetCount; ++i) {\n if (allAssets[i] == weth) {\n wethAssetIndex = i;\n break;\n }\n }\n\n require(allAssets[wethAssetIndex] == weth, \"Invalid WETH Asset Index\");\n }\n\n // @inheritdoc VaultCore\n function mintForStrategy(uint256 amount)\n external\n override\n whenNotCapitalPaused\n {\n require(\n strategies[msg.sender].isSupported == true,\n \"Unsupported strategy\"\n );\n require(\n isMintWhitelistedStrategy[msg.sender] == true,\n \"Not whitelisted strategy\"\n );\n\n emit Mint(msg.sender, amount);\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, amount);\n }\n\n // @inheritdoc VaultCore\n function burnForStrategy(uint256 amount)\n external\n override\n whenNotCapitalPaused\n {\n require(\n strategies[msg.sender].isSupported == true,\n \"Unsupported strategy\"\n );\n require(\n isMintWhitelistedStrategy[msg.sender] == true,\n \"Not whitelisted strategy\"\n );\n\n emit Redeem(msg.sender, amount);\n\n // Burn OTokens\n oUSD.burn(msg.sender, amount);\n }\n\n // @inheritdoc VaultCore\n // slither-disable-start reentrancy-no-eth\n function _mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) internal virtual override {\n require(_asset == weth, \"Unsupported asset for minting\");\n require(_amount > 0, \"Amount must be greater than 0\");\n require(\n _amount >= _minimumOusdAmount,\n \"Mint amount lower than minimum\"\n );\n\n emit Mint(msg.sender, _amount);\n\n // Rebase must happen before any transfers occur.\n if (!rebasePaused && _amount >= rebaseThreshold) {\n _rebase();\n }\n\n // Mint oTokens\n oUSD.mint(msg.sender, _amount);\n\n // Transfer the deposited coins to the vault\n IERC20(_asset).safeTransferFrom(msg.sender, address(this), _amount);\n\n // Give priority to the withdrawal queue for the new WETH liquidity\n _addWithdrawalQueueLiquidity();\n\n // Auto-allocate if necessary\n if (_amount >= autoAllocateThreshold) {\n _allocate();\n }\n }\n\n // slither-disable-end reentrancy-no-eth\n\n // @inheritdoc VaultCore\n function _calculateRedeemOutputs(uint256 _amount)\n internal\n view\n virtual\n override\n returns (uint256[] memory outputs)\n {\n // Overrides `VaultCore._calculateRedeemOutputs` to redeem with only\n // WETH instead of LST-mix. Doesn't change the function signature\n // for backward compatibility\n\n // Calculate redeem fee\n if (redeemFeeBps > 0) {\n uint256 redeemFee = _amount.mulTruncateScale(redeemFeeBps, 1e4);\n _amount = _amount - redeemFee;\n }\n\n // Ensure that the WETH index is cached\n uint256 _wethAssetIndex = wethAssetIndex;\n require(\n allAssets[_wethAssetIndex] == weth,\n \"WETH Asset index not cached\"\n );\n\n outputs = new uint256[](allAssets.length);\n outputs[_wethAssetIndex] = _amount;\n }\n\n // @inheritdoc VaultCore\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n override\n {\n // Override `VaultCore._redeem` to simplify it. Gets rid of oracle\n // usage and looping through all assets for LST-mix redeem. Instead\n // does a simple WETH-only redeem.\n emit Redeem(msg.sender, _amount);\n\n if (_amount == 0) {\n return;\n }\n\n // Amount excluding fees\n // No fee for the strategist or the governor, makes it easier to do operations\n uint256 amountMinusFee = (msg.sender == strategistAddr || isGovernor())\n ? _amount\n : _calculateRedeemOutputs(_amount)[wethAssetIndex];\n\n require(\n amountMinusFee >= _minimumUnitAmount,\n \"Redeem amount lower than minimum\"\n );\n\n // Is there enough WETH in the Vault available after accounting for the withdrawal queue\n require(_wethAvailable() >= amountMinusFee, \"Liquidity error\");\n\n // Transfer WETH minus the fee to the redeemer\n IERC20(weth).safeTransfer(msg.sender, amountMinusFee);\n\n // Burn OETH from user (including fees)\n oUSD.burn(msg.sender, _amount);\n\n // Prevent insolvency\n _postRedeem(_amount);\n }\n\n /**\n * @notice Request an asynchronous withdrawal of WETH in exchange for OETH.\n * The OETH is burned on request and the WETH is transferred to the withdrawer on claim.\n * This request can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal this request's `queued` amount.\n * There is a minimum of 10 minutes before a request can be claimed. After that, the request just needs\n * enough WETH liquidity in the Vault to satisfy all the outstanding requests to that point in the queue.\n * OETH is converted to WETH at 1:1.\n * @param _amount Amount of OETH to burn.\n * @return requestId Unique ID for the withdrawal request\n * @return queued Cumulative total of all WETH queued including already claimed requests.\n */\n function requestWithdrawal(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256 requestId, uint256 queued)\n {\n require(withdrawalClaimDelay > 0, \"Async withdrawals not enabled\");\n\n // The check that the requester has enough OETH is done in to later burn call\n\n requestId = withdrawalQueueMetadata.nextWithdrawalIndex;\n queued = withdrawalQueueMetadata.queued + _amount;\n\n // Store the next withdrawal request\n withdrawalQueueMetadata.nextWithdrawalIndex = SafeCast.toUint128(\n requestId + 1\n );\n // Store the updated queued amount which reserves WETH in the withdrawal queue\n // and reduces the vault's total assets\n withdrawalQueueMetadata.queued = SafeCast.toUint128(queued);\n // Store the user's withdrawal request\n withdrawalRequests[requestId] = WithdrawalRequest({\n withdrawer: msg.sender,\n claimed: false,\n timestamp: uint40(block.timestamp),\n amount: SafeCast.toUint128(_amount),\n queued: SafeCast.toUint128(queued)\n });\n\n // Burn the user's OETH\n oUSD.burn(msg.sender, _amount);\n\n // Prevent withdrawal if the vault is solvent by more than the allowed percentage\n _postRedeem(_amount);\n\n emit WithdrawalRequested(msg.sender, requestId, _amount, queued);\n }\n\n // slither-disable-start reentrancy-no-eth\n /**\n * @notice Claim a previously requested withdrawal once it is claimable.\n * This request can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal this request's `queued` amount and 10 minutes has passed.\n * If the requests is not claimable, the transaction will revert with `Queue pending liquidity`.\n * If the request is not older than 10 minutes, the transaction will revert with `Claim delay not met`.\n * OETH is converted to WETH at 1:1.\n * @param _requestId Unique ID for the withdrawal request\n * @return amount Amount of WETH transferred to the withdrawer\n */\n function claimWithdrawal(uint256 _requestId)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256 amount)\n {\n // Try and get more liquidity if there is not enough available\n if (\n withdrawalRequests[_requestId].queued >\n withdrawalQueueMetadata.claimable\n ) {\n // Add any WETH to the withdrawal queue\n // this needs to remain here as:\n // - Vault can be funded and `addWithdrawalQueueLiquidity` is not externally called\n // - funds can be withdrawn from a strategy\n //\n // Those funds need to be added to withdrawal queue liquidity\n _addWithdrawalQueueLiquidity();\n }\n\n amount = _claimWithdrawal(_requestId);\n\n // transfer WETH from the vault to the withdrawer\n IERC20(weth).safeTransfer(msg.sender, amount);\n\n // Prevent insolvency\n _postRedeem(amount);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @notice Claim a previously requested withdrawals once they are claimable.\n * This requests can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal each request's `queued` amount and 10 minutes has passed.\n * If one of the requests is not claimable, the whole transaction will revert with `Queue pending liquidity`.\n * If one of the requests is not older than 10 minutes,\n * the whole transaction will revert with `Claim delay not met`.\n * @param _requestIds Unique ID of each withdrawal request\n * @return amounts Amount of WETH received for each request\n * @return totalAmount Total amount of WETH transferred to the withdrawer\n */\n function claimWithdrawals(uint256[] calldata _requestIds)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256[] memory amounts, uint256 totalAmount)\n {\n // Add any WETH to the withdrawal queue\n // this needs to remain here as:\n // - Vault can be funded and `addWithdrawalQueueLiquidity` is not externally called\n // - funds can be withdrawn from a strategy\n //\n // Those funds need to be added to withdrawal queue liquidity\n _addWithdrawalQueueLiquidity();\n\n amounts = new uint256[](_requestIds.length);\n for (uint256 i; i < _requestIds.length; ++i) {\n amounts[i] = _claimWithdrawal(_requestIds[i]);\n totalAmount += amounts[i];\n }\n\n // transfer all the claimed WETH from the vault to the withdrawer\n IERC20(weth).safeTransfer(msg.sender, totalAmount);\n\n // Prevent insolvency\n _postRedeem(totalAmount);\n }\n\n function _claimWithdrawal(uint256 requestId)\n internal\n returns (uint256 amount)\n {\n require(withdrawalClaimDelay > 0, \"Async withdrawals not enabled\");\n\n // Load the structs from storage into memory\n WithdrawalRequest memory request = withdrawalRequests[requestId];\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n require(\n request.timestamp + withdrawalClaimDelay <= block.timestamp,\n \"Claim delay not met\"\n );\n // If there isn't enough reserved liquidity in the queue to claim\n require(request.queued <= queue.claimable, \"Queue pending liquidity\");\n require(request.withdrawer == msg.sender, \"Not requester\");\n require(request.claimed == false, \"Already claimed\");\n\n // Store the request as claimed\n withdrawalRequests[requestId].claimed = true;\n // Store the updated claimed amount\n withdrawalQueueMetadata.claimed = queue.claimed + request.amount;\n\n emit WithdrawalClaimed(msg.sender, requestId, request.amount);\n\n return request.amount;\n }\n\n /// @notice Adds WETH to the withdrawal queue if there is a funding shortfall.\n /// @dev is called from the Native Staking strategy when validator withdrawals are processed.\n /// It also called before any WETH is allocated to a strategy.\n function addWithdrawalQueueLiquidity() external {\n _addWithdrawalQueueLiquidity();\n }\n\n /// @dev Adds WETH to the withdrawal queue if there is a funding shortfall.\n /// This assumes 1 WETH equal 1 OETH.\n function _addWithdrawalQueueLiquidity()\n internal\n returns (uint256 addedClaimable)\n {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // Check if the claimable WETH is less than the queued amount\n uint256 queueShortfall = queue.queued - queue.claimable;\n\n // No need to do anything is the withdrawal queue is full funded\n if (queueShortfall == 0) {\n return 0;\n }\n\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // Of the claimable withdrawal requests, how much is unclaimed?\n // That is, the amount of WETH that is currently allocated for the withdrawal queue\n uint256 allocatedWeth = queue.claimable - queue.claimed;\n\n // If there is no unallocated WETH then there is nothing to add to the queue\n if (wethBalance <= allocatedWeth) {\n return 0;\n }\n\n uint256 unallocatedWeth = wethBalance - allocatedWeth;\n\n // the new claimable amount is the smaller of the queue shortfall or unallocated weth\n addedClaimable = queueShortfall < unallocatedWeth\n ? queueShortfall\n : unallocatedWeth;\n uint256 newClaimable = queue.claimable + addedClaimable;\n\n // Store the new claimable amount back to storage\n withdrawalQueueMetadata.claimable = SafeCast.toUint128(newClaimable);\n\n // emit a WithdrawalClaimable event\n emit WithdrawalClaimable(newClaimable, addedClaimable);\n }\n\n /***************************************\n View Functions\n ****************************************/\n\n /// @dev Calculate how much WETH in the vault is not reserved for the withdrawal queue.\n // That is, it is available to be redeemed or deposited into a strategy.\n function _wethAvailable() internal view returns (uint256 wethAvailable) {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n uint256 outstandingWithdrawals = queue.queued - queue.claimed;\n\n // The amount of sitting in WETH in the vault\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // If there is not enough WETH in the vault to cover the outstanding withdrawals\n if (wethBalance <= outstandingWithdrawals) {\n return 0;\n }\n\n return wethBalance - outstandingWithdrawals;\n }\n\n /// @dev Get the balance of an asset held in Vault and all strategies\n /// less any WETH that is reserved for the withdrawal queue.\n /// WETH is the only asset that can return a non-zero balance.\n /// All other assets will return 0 even if there is some dust amounts left in the Vault.\n /// For example, there is 1 wei left of stETH in the OETH Vault but will return 0 in this function.\n ///\n /// If there is not enough WETH in the vault and all strategies to cover all outstanding\n /// withdrawal requests then return a WETH balance of 0\n function _checkBalance(address _asset)\n internal\n view\n override\n returns (uint256 balance)\n {\n if (_asset != weth) {\n return 0;\n }\n\n // Get the WETH in the vault and the strategies\n balance = super._checkBalance(_asset);\n\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // If the vault becomes insolvent enough that the total value in the vault and all strategies\n // is less than the outstanding withdrawals.\n // For example, there was a mass slashing event and most users request a withdrawal.\n if (balance + queue.claimed < queue.queued) {\n return 0;\n }\n\n // Need to remove WETH that is reserved for the withdrawal queue\n return balance + queue.claimed - queue.queued;\n }\n\n /**\n * @notice Allocate unallocated funds on Vault to strategies.\n **/\n function allocate() external override whenNotCapitalPaused nonReentrant {\n // Add any unallocated WETH to the withdrawal queue first\n _addWithdrawalQueueLiquidity();\n\n _allocate();\n }\n\n /// @dev Allocate WETH to the default WETH strategy if there is excess to the Vault buffer.\n /// This is called from either `mint` or `allocate` and assumes `_addWithdrawalQueueLiquidity`\n /// has been called before this function.\n function _allocate() internal override {\n // No need to do anything if no default strategy for WETH\n address depositStrategyAddr = assetDefaultStrategies[weth];\n if (depositStrategyAddr == address(0)) return;\n\n uint256 wethAvailableInVault = _wethAvailable();\n // No need to do anything if there isn't any WETH in the vault to allocate\n if (wethAvailableInVault == 0) return;\n\n // Calculate the target buffer for the vault using the total supply\n uint256 totalSupply = oUSD.totalSupply();\n uint256 targetBuffer = totalSupply.mulTruncate(vaultBuffer);\n\n // If available WETH in the Vault is below or equal the target buffer then there's nothing to allocate\n if (wethAvailableInVault <= targetBuffer) return;\n\n // The amount of assets to allocate to the default strategy\n uint256 allocateAmount = wethAvailableInVault - targetBuffer;\n\n IStrategy strategy = IStrategy(depositStrategyAddr);\n // Transfer WETH to the strategy and call the strategy's deposit function\n IERC20(weth).safeTransfer(address(strategy), allocateAmount);\n strategy.deposit(weth, allocateAmount);\n\n emit AssetAllocated(weth, depositStrategyAddr, allocateAmount);\n }\n\n /// @dev The total value of all WETH held by the vault and all its strategies\n /// less any WETH that is reserved for the withdrawal queue.\n ///\n // If there is not enough WETH in the vault and all strategies to cover all outstanding\n // withdrawal requests then return a total value of 0.\n function _totalValue() internal view override returns (uint256 value) {\n // As WETH is the only asset, just return the WETH balance\n return _checkBalance(weth);\n }\n\n /// @dev Only WETH is supported in the OETH Vault so return the WETH balance only\n /// Any ETH balances in the Vault will be ignored.\n /// Amounts from previously supported vault assets will also be ignored.\n /// For example, there is 1 wei left of stETH in the OETH Vault but is will be ignored.\n function _totalValueInVault()\n internal\n view\n override\n returns (uint256 value)\n {\n value = IERC20(weth).balanceOf(address(this));\n }\n}\n" + }, + "contracts/vault/OETHZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { ISfrxETH } from \"../interfaces/ISfrxETH.sol\";\n\ncontract OETHZapper {\n IERC20 public immutable oeth;\n IVault public immutable vault;\n\n IWETH9 public constant weth =\n IWETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);\n IERC20 public constant frxeth =\n IERC20(0x5E8422345238F34275888049021821E8E08CAa1f);\n ISfrxETH public constant sfrxeth =\n ISfrxETH(0xac3E018457B222d93114458476f3E3416Abbe38F);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(address _oeth, address _vault) {\n oeth = IERC20(_oeth);\n vault = IVault(_vault);\n\n weth.approve(address(_vault), type(uint256).max);\n frxeth.approve(address(_vault), type(uint256).max);\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return.\n * Will verify that the user is sent 1:1 for ETH.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return\n * Will verify that the user is sent 1:1 for ETH.\n * @return Amount of OETH sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n weth.deposit{ value: balance }();\n emit Zap(msg.sender, ETH_MARKER, balance);\n return _mint(address(weth), balance);\n }\n\n /**\n * @dev Deposit SFRXETH to the vault and receive OETH in return\n * @param amount Amount of SFRXETH to deposit\n * @param minOETH Minimum amount of OETH to receive\n * @return Amount of OETH sent to user\n */\n function depositSFRXETH(uint256 amount, uint256 minOETH)\n external\n returns (uint256)\n {\n sfrxeth.redeem(amount, address(this), msg.sender);\n emit Zap(msg.sender, address(sfrxeth), amount);\n return _mint(address(frxeth), minOETH);\n }\n\n /**\n * @dev Internal function to mint OETH from an asset\n * @param asset Address of asset for the vault to mint from\n * @param minOETH Minimum amount of OETH to for user to receive\n * @return Amount of OETH sent to user\n */\n function _mint(address asset, uint256 minOETH) internal returns (uint256) {\n uint256 toMint = IERC20(asset).balanceOf(address(this));\n vault.mint(asset, toMint, minOETH);\n uint256 mintedAmount = oeth.balanceOf(address(this));\n require(mintedAmount >= minOETH, \"Zapper: not enough minted\");\n require(oeth.transfer(msg.sender, mintedAmount));\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/OSonicVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"./OETHVaultAdmin.sol\";\n\n/**\n * @title Origin Sonic VaultAdmin contract on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonicVaultAdmin is OETHVaultAdmin {\n /// @param _wS Sonic's Wrapped S token\n constructor(address _wS) OETHVaultAdmin(_wS) {}\n\n /***************************************\n Asset Config\n ****************************************/\n\n /**\n * @notice Add a supported asset to the contract, i.e. one that can be to mint OTokens.\n * @dev Overridden to remove price provider integration\n * @param _asset Address of asset\n * @param _unitConversion 0 decimals, 1 exchange rate\n */\n function supportAsset(address _asset, uint8 _unitConversion)\n external\n override\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Asset already supported\");\n\n assets[_asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(_unitConversion),\n decimals: 0, // will be overridden in _cacheDecimals\n allowedOracleSlippageBps: 0 // 0% by default\n });\n\n _cacheDecimals(_asset);\n allAssets.push(_asset);\n\n emit AssetSupported(_asset);\n }\n}\n" + }, + "contracts/vault/OSonicVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultCore } from \"./OETHVaultCore.sol\";\n\n/**\n * @title Origin Sonic VaultCore contract on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonicVaultCore is OETHVaultCore {\n /// @param _wS Sonic's Wrapped S token\n constructor(address _wS) OETHVaultCore(_wS) {}\n\n /**\n * @notice Instant redeem is not supported on Sonic.\n * Use the asynchronous `requestWithdrawal` a `claimWithdrawal` instead.\n */\n function _redeem(uint256, uint256) internal override {\n revert(\"unsupported function\");\n }\n}\n" + }, + "contracts/vault/OSonicZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWrappedSonic } from \"../interfaces/sonic/IWrappedSonic.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\n\n/**\n * @title Zapper for Origin Sonic (OS) tokens\n * @author Origin Protocol Inc\n */\ncontract OSonicZapper {\n IERC20 public immutable OS;\n IERC4626 public immutable wOS;\n IVault public immutable vault;\n\n IWrappedSonic public constant wS =\n IWrappedSonic(0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(\n address _OS,\n address _wOS,\n address _vault\n ) {\n OS = IERC20(_OS);\n wOS = IERC4626(_wOS);\n vault = IVault(_vault);\n\n wS.approve(address(_vault), type(uint256).max);\n IERC20(_OS).approve(_wOS, type(uint256).max);\n }\n\n /**\n * @dev Deposit native S currency and receive Origin Sonic (OS) tokens in return.\n * Will verify that the user is sent 1:1 for S.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit native S currency and receive Origin Sonic (OS) tokens in return.\n * Will verify that the user is sent 1:1 for S.\n * @return Amount of Origin Sonic (OS) tokens sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap native S\n wS.deposit{ value: balance }();\n\n // Mint Origin Sonic (OS) with Wrapped Sonic (wS)\n return _mint(balance, msg.sender);\n }\n\n /**\n * @dev Deposit S and receive Wrapped Origin Sonic (wOS) in return\n * @param minReceived min amount of Wrapped Origin Sonic (wOS) to receive\n * @return Amount of Wrapped Origin Sonic (wOS) tokens sent to user\n */\n function depositSForWrappedTokens(uint256 minReceived)\n external\n payable\n returns (uint256)\n {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap S\n wS.deposit{ value: balance }();\n\n // Mint with Wrapped Sonic\n uint256 mintOS = _mint(balance, address(this));\n\n // Wrap Origin Sonic (OS) into Wrapped Origin Sonic (wOS)\n uint256 mintedWOS = wOS.deposit(mintOS, msg.sender);\n\n require(mintedWOS >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWOS;\n }\n\n /**\n * @dev Deposit Wrapped Sonic (wS) tokens and receive Wrapped Origin Sonic (wOS) tokens in return\n * @param wSAmount Amount of Wrapped Sonic (wS) to deposit\n * @param minReceived min amount of Wrapped Origin Sonic (wOS) token to receive\n * @return Amount of Wrapped Origin Sonic (wOS) tokens sent to user\n */\n function depositWSForWrappedTokens(uint256 wSAmount, uint256 minReceived)\n external\n returns (uint256)\n {\n // slither-disable-next-line unchecked-transfer unused-return\n wS.transferFrom(msg.sender, address(this), wSAmount);\n\n emit Zap(msg.sender, address(wS), wSAmount);\n\n // Mint with Wrapped Sonic (wS)\n uint256 mintedOS = _mint(wSAmount, address(this));\n\n // Wrap Origin Sonic (OS) tokens into Wrapped Origin Sonic (wOS) tokens\n uint256 mintedWOS = wOS.deposit(mintedOS, msg.sender);\n\n require(mintedWOS >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWOS;\n }\n\n /**\n * @dev Internal function to mint Origin Sonic (OS) with Wrapped S (wS)\n * @param minOS Minimum amount of Origin Sonic (OS) tokens the user can receive\n * @param recipient Address that receives the tokens\n * @return Amount of Origin Sonic (OS) tokens sent to the recipient\n */\n function _mint(uint256 minOS, address recipient)\n internal\n returns (uint256)\n {\n uint256 toMint = wS.balanceOf(address(this));\n vault.mint(address(wS), toMint, minOS);\n uint256 mintedAmount = OS.balanceOf(address(this));\n require(mintedAmount >= minOS, \"Zapper: not enough minted\");\n\n if (recipient != address(this)) {\n require(OS.transfer(recipient, mintedAmount));\n }\n\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/Vault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD VaultInitializer Contract\n * @notice The VaultInitializer sets up the initial contract.\n * @author Origin Protocol Inc\n */\nimport { VaultInitializer } from \"./VaultInitializer.sol\";\nimport { VaultAdmin } from \"./VaultAdmin.sol\";\n\ncontract Vault is VaultInitializer, VaultAdmin {}\n" + }, + "contracts/vault/VaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultAdmin contract\n * @notice The VaultAdmin contract makes configuration and admin calls on the vault.\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport \"./VaultStorage.sol\";\n\ncontract VaultAdmin is VaultStorage {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /***************************************\n Configuration\n ****************************************/\n\n /**\n * @notice Set address of price provider.\n * @param _priceProvider Address of price provider\n */\n function setPriceProvider(address _priceProvider) external onlyGovernor {\n priceProvider = _priceProvider;\n emit PriceProviderUpdated(_priceProvider);\n }\n\n /**\n * @notice Set a fee in basis points to be charged for a redeem.\n * @param _redeemFeeBps Basis point fee to be charged\n */\n function setRedeemFeeBps(uint256 _redeemFeeBps) external onlyGovernor {\n require(_redeemFeeBps <= 1000, \"Redeem fee should not be over 10%\");\n redeemFeeBps = _redeemFeeBps;\n emit RedeemFeeUpdated(_redeemFeeBps);\n }\n\n /**\n * @notice Set a buffer of assets to keep in the Vault to handle most\n * redemptions without needing to spend gas unwinding assets from a Strategy.\n * @param _vaultBuffer Percentage using 18 decimals. 100% = 1e18.\n */\n function setVaultBuffer(uint256 _vaultBuffer)\n external\n onlyGovernorOrStrategist\n {\n require(_vaultBuffer <= 1e18, \"Invalid value\");\n vaultBuffer = _vaultBuffer;\n emit VaultBufferUpdated(_vaultBuffer);\n }\n\n /**\n * @notice Sets the minimum amount of OTokens in a mint to trigger an\n * automatic allocation of funds afterwords.\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setAutoAllocateThreshold(uint256 _threshold)\n external\n onlyGovernor\n {\n autoAllocateThreshold = _threshold;\n emit AllocateThresholdUpdated(_threshold);\n }\n\n /**\n * @notice Set a minimum amount of OTokens in a mint or redeem that triggers a\n * rebase\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setRebaseThreshold(uint256 _threshold) external onlyGovernor {\n rebaseThreshold = _threshold;\n emit RebaseThresholdUpdated(_threshold);\n }\n\n /**\n * @notice Set address of Strategist\n * @param _address Address of Strategist\n */\n function setStrategistAddr(address _address) external onlyGovernor {\n strategistAddr = _address;\n emit StrategistUpdated(_address);\n }\n\n /**\n * @notice Set the default Strategy for an asset, i.e. the one which the asset\n will be automatically allocated to and withdrawn from\n * @param _asset Address of the asset\n * @param _strategy Address of the Strategy\n */\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external\n onlyGovernorOrStrategist\n {\n emit AssetDefaultStrategyUpdated(_asset, _strategy);\n // If its a zero address being passed for the strategy we are removing\n // the default strategy\n if (_strategy != address(0)) {\n // Make sure the strategy meets some criteria\n require(strategies[_strategy].isSupported, \"Strategy not approved\");\n IStrategy strategy = IStrategy(_strategy);\n require(assets[_asset].isSupported, \"Asset is not supported\");\n require(\n strategy.supportsAsset(_asset),\n \"Asset not supported by Strategy\"\n );\n }\n assetDefaultStrategies[_asset] = _strategy;\n }\n\n /**\n * @notice Set maximum amount of OTokens that can at any point be minted and deployed\n * to strategy (used only by ConvexOUSDMetaStrategy for now).\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setNetOusdMintForStrategyThreshold(uint256 _threshold)\n external\n onlyGovernor\n {\n /**\n * Because `netOusdMintedForStrategy` check in vault core works both ways\n * (positive and negative) the actual impact of the amount of OToken minted\n * could be double the threshold. E.g.:\n * - contract has threshold set to 100\n * - state of netOusdMinted is -90\n * - in effect it can mint 190 OToken and still be within limits\n *\n * We are somewhat mitigating this behaviour by resetting the netOusdMinted\n * counter whenever new threshold is set. So it can only move one threshold\n * amount in each direction. This also enables us to reduce the threshold\n * amount and not have problems with current netOusdMinted being near\n * limits on either side.\n */\n netOusdMintedForStrategy = 0;\n netOusdMintForStrategyThreshold = _threshold;\n emit NetOusdMintForStrategyThresholdChanged(_threshold);\n }\n\n /**\n * @notice Changes the async withdrawal claim period for OETH & superOETHb\n * @param _delay Delay period (should be between 10 mins to 7 days).\n * Set to 0 to disable async withdrawals\n */\n function setWithdrawalClaimDelay(uint256 _delay) external onlyGovernor {\n require(\n _delay == 0 || (_delay >= 10 minutes && _delay <= 15 days),\n \"Invalid claim delay period\"\n );\n withdrawalClaimDelay = _delay;\n emit WithdrawalClaimDelayUpdated(_delay);\n }\n\n /**\n * @notice Set a yield streaming max rate. This spreads yield over\n * time if it is above the max rate.\n * @param yearlyApr in 1e18 notation. 3 * 1e18 = 3% APR\n */\n function setRebaseRateMax(uint256 yearlyApr)\n external\n onlyGovernorOrStrategist\n {\n // The old yield will be at the old rate\n IVault(address(this)).rebase();\n // Change the rate\n uint256 newPerSecond = yearlyApr / 100 / 365 days;\n require(newPerSecond <= MAX_REBASE_PER_SECOND, \"Rate too high\");\n rebasePerSecondMax = newPerSecond.toUint64();\n emit RebasePerSecondMaxChanged(newPerSecond);\n }\n\n /**\n * @notice Set the drip duration period\n * @param _dripDuration Time in seconds to target a constant yield rate\n */\n function setDripDuration(uint256 _dripDuration)\n external\n onlyGovernorOrStrategist\n {\n // The old yield will be at the old rate\n IVault(address(this)).rebase();\n dripDuration = _dripDuration.toUint64();\n emit DripDurationChanged(_dripDuration);\n }\n\n /***************************************\n Swaps\n ****************************************/\n\n /**\n * @notice Strategist swaps collateral assets sitting in the vault.\n * @param _fromAsset The token address of the asset being sold by the vault.\n * @param _toAsset The token address of the asset being purchased by the vault.\n * @param _fromAssetAmount The amount of assets being sold by the vault.\n * @param _minToAssetAmount The minimum amount of assets to be purchased.\n * @param _data implementation specific data. eg 1Inch swap data\n * @return toAssetAmount The amount of toAssets that was received from the swap\n */\n function swapCollateral(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n )\n external\n nonReentrant\n onlyGovernorOrStrategist\n returns (uint256 toAssetAmount)\n {\n toAssetAmount = _swapCollateral(\n _fromAsset,\n _toAsset,\n _fromAssetAmount,\n _minToAssetAmount,\n _data\n );\n }\n\n function _swapCollateral(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n ) internal virtual returns (uint256 toAssetAmount) {\n // Check fromAsset and toAsset are valid\n Asset memory fromAssetConfig = assets[_fromAsset];\n Asset memory toAssetConfig = assets[_toAsset];\n require(fromAssetConfig.isSupported, \"From asset is not supported\");\n require(toAssetConfig.isSupported, \"To asset is not supported\");\n\n // Load swap config into memory to avoid separate SLOADs\n SwapConfig memory config = swapConfig;\n\n // Scope a new block to remove toAssetBalBefore from the scope of swapCollateral.\n // This avoids a stack too deep error.\n {\n uint256 toAssetBalBefore = IERC20(_toAsset).balanceOf(\n address(this)\n );\n\n // Transfer from assets to the swapper contract\n IERC20(_fromAsset).safeTransfer(config.swapper, _fromAssetAmount);\n\n // Call to the Swapper contract to do the actual swap\n // The -1 is required for stETH which sometimes transfers 1 wei less than what was specified.\n // slither-disable-next-line unused-return\n ISwapper(config.swapper).swap(\n _fromAsset,\n _toAsset,\n _fromAssetAmount - 1,\n _minToAssetAmount,\n _data\n );\n\n // Compute the change in asset balance held by the Vault\n toAssetAmount =\n IERC20(_toAsset).balanceOf(address(this)) -\n toAssetBalBefore;\n }\n\n // Check the to assets returned is above slippage amount specified by the strategist\n require(\n toAssetAmount >= _minToAssetAmount,\n \"Strategist slippage limit\"\n );\n\n // Scope a new block to remove minOracleToAssetAmount from the scope of swapCollateral.\n // This avoids a stack too deep error.\n {\n // Check the slippage against the Oracle in case the strategist made a mistake or has become malicious.\n // to asset amount = from asset amount * from asset price / to asset price\n uint256 minOracleToAssetAmount = (_fromAssetAmount *\n (1e4 - fromAssetConfig.allowedOracleSlippageBps) *\n IOracle(priceProvider).price(_fromAsset)) /\n (IOracle(priceProvider).price(_toAsset) *\n (1e4 + toAssetConfig.allowedOracleSlippageBps));\n\n // Scale both sides up to 18 decimals to compare\n require(\n toAssetAmount.scaleBy(18, toAssetConfig.decimals) >=\n minOracleToAssetAmount.scaleBy(\n 18,\n fromAssetConfig.decimals\n ),\n \"Oracle slippage limit exceeded\"\n );\n }\n\n // Check the vault's total value hasn't gone below the OToken total supply\n // by more than the allowed percentage.\n require(\n IVault(address(this)).totalValue() >=\n (oUSD.totalSupply() * ((1e4 - config.allowedUndervalueBps))) /\n 1e4,\n \"Allowed value < supply\"\n );\n\n emit Swapped(_fromAsset, _toAsset, _fromAssetAmount, toAssetAmount);\n }\n\n /***************************************\n Swap Config\n ****************************************/\n\n /**\n * @notice Set the contract the performs swaps of collateral assets.\n * @param _swapperAddr Address of the Swapper contract that implements the ISwapper interface.\n */\n function setSwapper(address _swapperAddr) external onlyGovernor {\n swapConfig.swapper = _swapperAddr;\n emit SwapperChanged(_swapperAddr);\n }\n\n /// @notice Contract that swaps the vault's collateral assets\n function swapper() external view returns (address swapper_) {\n swapper_ = swapConfig.swapper;\n }\n\n /**\n * @notice Set max allowed percentage the vault total value can drop below the OToken total supply in basis points\n * when executing collateral swaps.\n * @param _basis Percentage in basis points. eg 100 == 1%\n */\n function setSwapAllowedUndervalue(uint16 _basis) external onlyGovernor {\n require(_basis < 10001, \"Invalid basis points\");\n swapConfig.allowedUndervalueBps = _basis;\n emit SwapAllowedUndervalueChanged(_basis);\n }\n\n /**\n * @notice Max allowed percentage the vault total value can drop below the OToken total supply in basis points\n * when executing a collateral swap.\n * For example 100 == 1%\n * @return value Percentage in basis points.\n */\n function allowedSwapUndervalue() external view returns (uint256 value) {\n value = swapConfig.allowedUndervalueBps;\n }\n\n /**\n * @notice Set the allowed slippage from the Oracle price for collateral asset swaps.\n * @param _asset Address of the asset token.\n * @param _allowedOracleSlippageBps allowed slippage from Oracle in basis points. eg 20 = 0.2%. Max 10%.\n */\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external\n onlyGovernor\n {\n require(assets[_asset].isSupported, \"Asset not supported\");\n require(_allowedOracleSlippageBps < 1000, \"Slippage too high\");\n\n assets[_asset].allowedOracleSlippageBps = _allowedOracleSlippageBps;\n\n emit SwapSlippageChanged(_asset, _allowedOracleSlippageBps);\n }\n\n /***************************************\n Asset Config\n ****************************************/\n\n /**\n * @notice Add a supported asset to the contract, i.e. one that can be\n * to mint OTokens.\n * @param _asset Address of asset\n */\n function supportAsset(address _asset, uint8 _unitConversion)\n external\n virtual\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Asset already supported\");\n\n assets[_asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(_unitConversion),\n decimals: 0, // will be overridden in _cacheDecimals\n allowedOracleSlippageBps: 0 // 0% by default\n });\n\n _cacheDecimals(_asset);\n allAssets.push(_asset);\n\n // Verify that our oracle supports the asset\n // slither-disable-next-line unused-return\n IOracle(priceProvider).price(_asset);\n\n emit AssetSupported(_asset);\n }\n\n /**\n * @notice Remove a supported asset from the Vault\n * @param _asset Address of asset\n */\n function removeAsset(address _asset) external onlyGovernor {\n require(assets[_asset].isSupported, \"Asset not supported\");\n\n // 1e13 for 18 decimals. And 10 for 6 decimals\n uint256 maxDustBalance = uint256(1e13).scaleBy(\n assets[_asset].decimals,\n 18\n );\n\n require(\n IVault(address(this)).checkBalance(_asset) <= maxDustBalance,\n \"Vault still holds asset\"\n );\n\n uint256 assetsCount = allAssets.length;\n uint256 assetIndex = assetsCount; // initialize at invalid index\n for (uint256 i = 0; i < assetsCount; ++i) {\n if (allAssets[i] == _asset) {\n assetIndex = i;\n break;\n }\n }\n\n // Note: If asset is not found in `allAssets`, the following line\n // will revert with an out-of-bound error. However, there's no\n // reason why an asset would have `Asset.isSupported = true` but\n // not exist in `allAssets`.\n\n // Update allAssets array\n allAssets[assetIndex] = allAssets[assetsCount - 1];\n allAssets.pop();\n\n // Reset default strategy\n assetDefaultStrategies[_asset] = address(0);\n emit AssetDefaultStrategyUpdated(_asset, address(0));\n\n // Remove asset from storage\n delete assets[_asset];\n\n emit AssetRemoved(_asset);\n }\n\n /**\n * @notice Cache decimals on OracleRouter for a particular asset. This action\n * is required before that asset's price can be accessed.\n * @param _asset Address of asset token\n */\n function cacheDecimals(address _asset) external onlyGovernor {\n _cacheDecimals(_asset);\n }\n\n /***************************************\n Strategy Config\n ****************************************/\n\n /**\n * @notice Add a strategy to the Vault.\n * @param _addr Address of the strategy to add\n */\n function approveStrategy(address _addr) external onlyGovernor {\n require(!strategies[_addr].isSupported, \"Strategy already approved\");\n strategies[_addr] = Strategy({ isSupported: true, _deprecated: 0 });\n allStrategies.push(_addr);\n emit StrategyApproved(_addr);\n }\n\n /**\n * @notice Remove a strategy from the Vault.\n * @param _addr Address of the strategy to remove\n */\n\n function removeStrategy(address _addr) external onlyGovernor {\n require(strategies[_addr].isSupported, \"Strategy not approved\");\n\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n require(\n assetDefaultStrategies[allAssets[i]] != _addr,\n \"Strategy is default for an asset\"\n );\n }\n\n // Initialize strategyIndex with out of bounds result so function will\n // revert if no valid index found\n uint256 stratCount = allStrategies.length;\n uint256 strategyIndex = stratCount;\n for (uint256 i = 0; i < stratCount; ++i) {\n if (allStrategies[i] == _addr) {\n strategyIndex = i;\n break;\n }\n }\n\n if (strategyIndex < stratCount) {\n allStrategies[strategyIndex] = allStrategies[stratCount - 1];\n allStrategies.pop();\n\n // Mark the strategy as not supported\n strategies[_addr].isSupported = false;\n\n // Withdraw all assets\n IStrategy strategy = IStrategy(_addr);\n strategy.withdrawAll();\n\n emit StrategyRemoved(_addr);\n }\n }\n\n /***************************************\n Strategies\n ****************************************/\n\n /**\n * @notice Deposit multiple assets from the vault into the strategy.\n * @param _strategyToAddress Address of the Strategy to deposit assets into.\n * @param _assets Array of asset address that will be deposited into the strategy.\n * @param _amounts Array of amounts of each corresponding asset to deposit.\n */\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external onlyGovernorOrStrategist nonReentrant {\n _depositToStrategy(_strategyToAddress, _assets, _amounts);\n }\n\n function _depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal virtual {\n require(\n strategies[_strategyToAddress].isSupported,\n \"Invalid to Strategy\"\n );\n require(_assets.length == _amounts.length, \"Parameter length mismatch\");\n\n uint256 assetCount = _assets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n address assetAddr = _assets[i];\n require(\n IStrategy(_strategyToAddress).supportsAsset(assetAddr),\n \"Asset unsupported\"\n );\n // Send required amount of funds to the strategy\n IERC20(assetAddr).safeTransfer(_strategyToAddress, _amounts[i]);\n }\n\n // Deposit all the funds that have been sent to the strategy\n IStrategy(_strategyToAddress).depositAll();\n }\n\n /**\n * @notice Withdraw multiple assets from the strategy to the vault.\n * @param _strategyFromAddress Address of the Strategy to withdraw assets from.\n * @param _assets Array of asset address that will be withdrawn from the strategy.\n * @param _amounts Array of amounts of each corresponding asset to withdraw.\n */\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external onlyGovernorOrStrategist nonReentrant {\n _withdrawFromStrategy(\n address(this),\n _strategyFromAddress,\n _assets,\n _amounts\n );\n }\n\n /**\n * @param _recipient can either be a strategy or the Vault\n */\n function _withdrawFromStrategy(\n address _recipient,\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal virtual {\n require(\n strategies[_strategyFromAddress].isSupported,\n \"Invalid from Strategy\"\n );\n require(_assets.length == _amounts.length, \"Parameter length mismatch\");\n\n uint256 assetCount = _assets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n // Withdraw from Strategy to the recipient\n IStrategy(_strategyFromAddress).withdraw(\n _recipient,\n _assets[i],\n _amounts[i]\n );\n }\n }\n\n /**\n * @notice Sets the maximum allowable difference between\n * total supply and backing assets' value.\n */\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external onlyGovernor {\n maxSupplyDiff = _maxSupplyDiff;\n emit MaxSupplyDiffChanged(_maxSupplyDiff);\n }\n\n /**\n * @notice Sets the trusteeAddress that can receive a portion of yield.\n * Setting to the zero address disables this feature.\n */\n function setTrusteeAddress(address _address) external onlyGovernor {\n trusteeAddress = _address;\n emit TrusteeAddressChanged(_address);\n }\n\n /**\n * @notice Sets the TrusteeFeeBps to the percentage of yield that should be\n * received in basis points.\n */\n function setTrusteeFeeBps(uint256 _basis) external onlyGovernor {\n require(_basis <= 5000, \"basis cannot exceed 50%\");\n trusteeFeeBps = _basis;\n emit TrusteeFeeBpsChanged(_basis);\n }\n\n /**\n * @notice Set OToken Metapool strategy\n * @param _ousdMetaStrategy Address of OToken metapool strategy\n */\n function setOusdMetaStrategy(address _ousdMetaStrategy)\n external\n onlyGovernor\n {\n ousdMetaStrategy = _ousdMetaStrategy;\n emit OusdMetaStrategyUpdated(_ousdMetaStrategy);\n }\n\n /***************************************\n Pause\n ****************************************/\n\n /**\n * @notice Set the deposit paused flag to true to prevent rebasing.\n */\n function pauseRebase() external onlyGovernorOrStrategist {\n rebasePaused = true;\n emit RebasePaused();\n }\n\n /**\n * @notice Set the deposit paused flag to true to allow rebasing.\n */\n function unpauseRebase() external onlyGovernorOrStrategist {\n rebasePaused = false;\n emit RebaseUnpaused();\n }\n\n /**\n * @notice Set the deposit paused flag to true to prevent capital movement.\n */\n function pauseCapital() external onlyGovernorOrStrategist {\n capitalPaused = true;\n emit CapitalPaused();\n }\n\n /**\n * @notice Set the deposit paused flag to false to enable capital movement.\n */\n function unpauseCapital() external onlyGovernorOrStrategist {\n capitalPaused = false;\n emit CapitalUnpaused();\n }\n\n /***************************************\n Utils\n ****************************************/\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Only unsupported assets\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /***************************************\n Strategies Admin\n ****************************************/\n\n /**\n * @notice Withdraws all assets from the strategy and sends assets to the Vault.\n * @param _strategyAddr Strategy address.\n */\n function withdrawAllFromStrategy(address _strategyAddr)\n external\n onlyGovernorOrStrategist\n {\n _withdrawAllFromStrategy(_strategyAddr);\n }\n\n function _withdrawAllFromStrategy(address _strategyAddr) internal virtual {\n require(\n strategies[_strategyAddr].isSupported,\n \"Strategy is not supported\"\n );\n IStrategy strategy = IStrategy(_strategyAddr);\n strategy.withdrawAll();\n }\n\n /**\n * @notice Withdraws all assets from all the strategies and sends assets to the Vault.\n */\n function withdrawAllFromStrategies() external onlyGovernorOrStrategist {\n _withdrawAllFromStrategies();\n }\n\n function _withdrawAllFromStrategies() internal virtual {\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n IStrategy(allStrategies[i]).withdrawAll();\n }\n }\n\n /***************************************\n Utils\n ****************************************/\n\n function _cacheDecimals(address token) internal {\n Asset storage tokenAsset = assets[token];\n if (tokenAsset.decimals != 0) {\n return;\n }\n uint8 decimals = IBasicToken(token).decimals();\n require(decimals >= 6 && decimals <= 18, \"Unexpected precision\");\n tokenAsset.decimals = decimals;\n }\n}\n" + }, + "contracts/vault/VaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultCore contract\n * @notice The Vault contract stores assets. On a deposit, OTokens will be minted\n and sent to the depositor. On a withdrawal, OTokens will be burned and\n assets will be sent to the withdrawer. The Vault accepts deposits of\n interest from yield bearing strategies which will modify the supply\n of OTokens.\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IGetExchangeRateToken } from \"../interfaces/IGetExchangeRateToken.sol\";\n\nimport \"./VaultInitializer.sol\";\n\ncontract VaultCore is VaultInitializer {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n /// @dev max signed int\n uint256 internal constant MAX_INT = uint256(type(int256).max);\n\n /**\n * @dev Verifies that the rebasing is not paused.\n */\n modifier whenNotRebasePaused() {\n require(!rebasePaused, \"Rebasing paused\");\n _;\n }\n\n /**\n * @dev Verifies that the deposits are not paused.\n */\n modifier whenNotCapitalPaused() {\n require(!capitalPaused, \"Capital paused\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the AMO strategy.\n */\n modifier onlyOusdMetaStrategy() {\n require(\n msg.sender == ousdMetaStrategy,\n \"Caller is not the OUSD meta strategy\"\n );\n _;\n }\n\n /**\n * @notice Deposit a supported asset and mint OTokens.\n * @param _asset Address of the asset being deposited\n * @param _amount Amount of the asset being deposited\n * @param _minimumOusdAmount Minimum OTokens to mint\n */\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external whenNotCapitalPaused nonReentrant {\n _mint(_asset, _amount, _minimumOusdAmount);\n }\n\n /**\n * @dev Deposit a supported asset and mint OTokens.\n * @param _asset Address of the asset being deposited\n * @param _amount Amount of the asset being deposited\n * @param _minimumOusdAmount Minimum OTokens to mint\n */\n function _mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) internal virtual {\n require(assets[_asset].isSupported, \"Asset is not supported\");\n require(_amount > 0, \"Amount must be greater than 0\");\n\n uint256 units = _toUnits(_amount, _asset);\n uint256 unitPrice = _toUnitPrice(_asset, true);\n uint256 priceAdjustedDeposit = (units * unitPrice) / 1e18;\n\n if (_minimumOusdAmount > 0) {\n require(\n priceAdjustedDeposit >= _minimumOusdAmount,\n \"Mint amount lower than minimum\"\n );\n }\n\n emit Mint(msg.sender, priceAdjustedDeposit);\n\n // Rebase must happen before any transfers occur.\n if (priceAdjustedDeposit >= rebaseThreshold && !rebasePaused) {\n _rebase();\n }\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, priceAdjustedDeposit);\n\n // Transfer the deposited coins to the vault\n IERC20 asset = IERC20(_asset);\n asset.safeTransferFrom(msg.sender, address(this), _amount);\n\n if (priceAdjustedDeposit >= autoAllocateThreshold) {\n _allocate();\n }\n }\n\n /**\n * @notice Mint OTokens for a Metapool Strategy\n * @param _amount Amount of the asset being deposited\n *\n * Notice: can't use `nonReentrant` modifier since the `mint` function can\n * call `allocate`, and that can trigger `ConvexOUSDMetaStrategy` to call this function\n * while the execution of the `mint` has not yet completed -> causing a `nonReentrant` collision.\n *\n * Also important to understand is that this is a limitation imposed by the test suite.\n * Production / mainnet contracts should never be configured in a way where mint/redeem functions\n * that are moving funds between the Vault and end user wallets can influence strategies\n * utilizing this function.\n */\n function mintForStrategy(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n onlyOusdMetaStrategy\n {\n require(_amount < MAX_INT, \"Amount too high\");\n\n emit Mint(msg.sender, _amount);\n\n // safe to cast because of the require check at the beginning of the function\n netOusdMintedForStrategy += int256(_amount);\n\n require(\n abs(netOusdMintedForStrategy) < netOusdMintForStrategyThreshold,\n \"Minted ousd surpassed netOusdMintForStrategyThreshold.\"\n );\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, _amount);\n }\n\n // In memoriam\n\n /**\n * @notice Withdraw a supported asset and burn OTokens.\n * @param _amount Amount of OTokens to burn\n * @param _minimumUnitAmount Minimum stablecoin units to receive in return\n */\n function redeem(uint256 _amount, uint256 _minimumUnitAmount)\n external\n whenNotCapitalPaused\n nonReentrant\n {\n _redeem(_amount, _minimumUnitAmount);\n }\n\n /**\n * @notice Withdraw a supported asset and burn OTokens.\n * @param _amount Amount of OTokens to burn\n * @param _minimumUnitAmount Minimum stablecoin units to receive in return\n */\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n {\n // Calculate redemption outputs\n uint256[] memory outputs = _calculateRedeemOutputs(_amount);\n\n emit Redeem(msg.sender, _amount);\n\n // Send outputs\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n if (outputs[i] == 0) continue;\n\n address assetAddr = allAssets[i];\n\n if (IERC20(assetAddr).balanceOf(address(this)) >= outputs[i]) {\n // Use Vault funds first if sufficient\n IERC20(assetAddr).safeTransfer(msg.sender, outputs[i]);\n } else {\n address strategyAddr = assetDefaultStrategies[assetAddr];\n if (strategyAddr != address(0)) {\n // Nothing in Vault, but something in Strategy, send from there\n IStrategy strategy = IStrategy(strategyAddr);\n strategy.withdraw(msg.sender, assetAddr, outputs[i]);\n } else {\n // Cant find funds anywhere\n revert(\"Liquidity error\");\n }\n }\n }\n\n if (_minimumUnitAmount > 0) {\n uint256 unitTotal = 0;\n for (uint256 i = 0; i < outputs.length; ++i) {\n unitTotal += _toUnits(outputs[i], allAssets[i]);\n }\n require(\n unitTotal >= _minimumUnitAmount,\n \"Redeem amount lower than minimum\"\n );\n }\n\n oUSD.burn(msg.sender, _amount);\n\n _postRedeem(_amount);\n }\n\n function _postRedeem(uint256 _amount) internal {\n // Until we can prove that we won't affect the prices of our assets\n // by withdrawing them, this should be here.\n // It's possible that a strategy was off on its asset total, perhaps\n // a reward token sold for more or for less than anticipated.\n uint256 totalUnits = 0;\n if (_amount >= rebaseThreshold && !rebasePaused) {\n totalUnits = _rebase();\n } else {\n totalUnits = _totalValue();\n }\n\n // Check that the OTokens are backed by enough assets\n if (maxSupplyDiff > 0) {\n // If there are more outstanding withdrawal requests than assets in the vault and strategies\n // then the available assets will be negative and totalUnits will be rounded up to zero.\n // As we don't know the exact shortfall amount, we will reject all redeem and withdrawals\n require(totalUnits > 0, \"Too many outstanding requests\");\n\n // Allow a max difference of maxSupplyDiff% between\n // backing assets value and OUSD total supply\n uint256 diff = oUSD.totalSupply().divPrecisely(totalUnits);\n require(\n (diff > 1e18 ? diff - 1e18 : 1e18 - diff) <= maxSupplyDiff,\n \"Backing supply liquidity error\"\n );\n }\n }\n\n /**\n * @notice Burn OTokens for Metapool Strategy\n * @param _amount Amount of OUSD to burn\n *\n * @dev Notice: can't use `nonReentrant` modifier since the `redeem` function could\n * require withdrawal on `ConvexOUSDMetaStrategy` and that one can call `burnForStrategy`\n * while the execution of the `redeem` has not yet completed -> causing a `nonReentrant` collision.\n *\n * Also important to understand is that this is a limitation imposed by the test suite.\n * Production / mainnet contracts should never be configured in a way where mint/redeem functions\n * that are moving funds between the Vault and end user wallets can influence strategies\n * utilizing this function.\n */\n function burnForStrategy(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n onlyOusdMetaStrategy\n {\n require(_amount < MAX_INT, \"Amount too high\");\n\n emit Redeem(msg.sender, _amount);\n\n // safe to cast because of the require check at the beginning of the function\n netOusdMintedForStrategy -= int256(_amount);\n\n require(\n abs(netOusdMintedForStrategy) < netOusdMintForStrategyThreshold,\n \"Attempting to burn too much OUSD.\"\n );\n\n // Burn OTokens\n oUSD.burn(msg.sender, _amount);\n }\n\n /**\n * @notice Allocate unallocated funds on Vault to strategies.\n **/\n function allocate() external virtual whenNotCapitalPaused nonReentrant {\n _allocate();\n }\n\n /**\n * @dev Allocate unallocated funds on Vault to strategies.\n **/\n function _allocate() internal virtual {\n uint256 vaultValue = _totalValueInVault();\n // Nothing in vault to allocate\n if (vaultValue == 0) return;\n uint256 strategiesValue = _totalValueInStrategies();\n // We have a method that does the same as this, gas optimisation\n uint256 calculatedTotalValue = vaultValue + strategiesValue;\n\n // We want to maintain a buffer on the Vault so calculate a percentage\n // modifier to multiply each amount being allocated by to enforce the\n // vault buffer\n uint256 vaultBufferModifier;\n if (strategiesValue == 0) {\n // Nothing in Strategies, allocate 100% minus the vault buffer to\n // strategies\n vaultBufferModifier = uint256(1e18) - vaultBuffer;\n } else {\n vaultBufferModifier =\n (vaultBuffer * calculatedTotalValue) /\n vaultValue;\n if (1e18 > vaultBufferModifier) {\n // E.g. 1e18 - (1e17 * 10e18)/5e18 = 8e17\n // (5e18 * 8e17) / 1e18 = 4e18 allocated from Vault\n vaultBufferModifier = uint256(1e18) - vaultBufferModifier;\n } else {\n // We need to let the buffer fill\n return;\n }\n }\n if (vaultBufferModifier == 0) return;\n\n // Iterate over all assets in the Vault and allocate to the appropriate\n // strategy\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(allAssets[i]);\n uint256 assetBalance = asset.balanceOf(address(this));\n // No balance, nothing to do here\n if (assetBalance == 0) continue;\n\n // Multiply the balance by the vault buffer modifier and truncate\n // to the scale of the asset decimals\n uint256 allocateAmount = assetBalance.mulTruncate(\n vaultBufferModifier\n );\n\n address depositStrategyAddr = assetDefaultStrategies[\n address(asset)\n ];\n\n if (depositStrategyAddr != address(0) && allocateAmount > 0) {\n IStrategy strategy = IStrategy(depositStrategyAddr);\n // Transfer asset to Strategy and call deposit method to\n // mint or take required action\n asset.safeTransfer(address(strategy), allocateAmount);\n strategy.deposit(address(asset), allocateAmount);\n emit AssetAllocated(\n address(asset),\n depositStrategyAddr,\n allocateAmount\n );\n }\n }\n }\n\n /**\n * @notice Calculate the total value of assets held by the Vault and all\n * strategies and update the supply of OTokens.\n */\n function rebase() external virtual nonReentrant {\n _rebase();\n }\n\n /**\n * @dev Calculate the total value of assets held by the Vault and all\n * strategies and update the supply of OTokens, optionally sending a\n * portion of the yield to the trustee.\n * @return totalUnits Total balance of Vault in units\n */\n function _rebase() internal whenNotRebasePaused returns (uint256) {\n uint256 supply = oUSD.totalSupply();\n uint256 vaultValue = _totalValue();\n // If no supply yet, do not rebase\n if (supply == 0) {\n return vaultValue;\n }\n\n // Calculate yield and new supply\n (uint256 yield, uint256 targetRate) = _nextYield(supply, vaultValue);\n uint256 newSupply = supply + yield;\n // Only rebase upwards and if we have enough backing funds\n if (newSupply <= supply || newSupply > vaultValue) {\n return vaultValue;\n }\n\n rebasePerSecondTarget = uint64(_min(targetRate, type(uint64).max));\n lastRebase = uint64(block.timestamp); // Intentional cast\n\n // Fee collection on yield\n address _trusteeAddress = trusteeAddress; // gas savings\n uint256 fee = 0;\n if (_trusteeAddress != address(0)) {\n fee = (yield * trusteeFeeBps) / 1e4;\n if (fee > 0) {\n require(fee < yield, \"Fee must not be greater than yield\");\n oUSD.mint(_trusteeAddress, fee);\n }\n }\n emit YieldDistribution(_trusteeAddress, yield, fee);\n\n // Only ratchet OToken supply upwards\n // Final check uses latest totalSupply\n if (newSupply > oUSD.totalSupply()) {\n oUSD.changeSupply(newSupply);\n }\n return vaultValue;\n }\n\n /**\n * @notice Calculates the amount that would rebase at next rebase.\n * This is before any fees.\n * @return yield amount of expected yield\n */\n function previewYield() external view returns (uint256 yield) {\n (yield, ) = _nextYield(oUSD.totalSupply(), _totalValue());\n return yield;\n }\n\n function _nextYield(uint256 supply, uint256 vaultValue)\n internal\n view\n virtual\n returns (uint256 yield, uint256 targetRate)\n {\n uint256 nonRebasing = oUSD.nonRebasingSupply();\n uint256 rebasing = supply - nonRebasing;\n uint256 elapsed = block.timestamp - lastRebase;\n targetRate = rebasePerSecondTarget;\n\n if (\n elapsed == 0 || // Yield only once per block.\n rebasing == 0 || // No yield if there are no rebasing tokens to give it to.\n supply > vaultValue || // No yield if we do not have yield to give.\n block.timestamp >= type(uint64).max // No yield if we are too far in the future to calculate it correctly.\n ) {\n return (0, targetRate);\n }\n\n // Start with the full difference available\n yield = vaultValue - supply;\n\n // Cap via optional automatic duration smoothing\n uint256 _dripDuration = dripDuration;\n if (_dripDuration > 1) {\n // If we are able to sustain an increased drip rate for\n // double the duration, then increase the target drip rate\n targetRate = _max(targetRate, yield / (_dripDuration * 2));\n // If we cannot sustain the target rate any more,\n // then rebase what we can, and reduce the target\n targetRate = _min(targetRate, yield / _dripDuration);\n // drip at the new target rate\n yield = _min(yield, targetRate * elapsed);\n }\n\n // Cap per second. elapsed is not 1e18 denominated\n yield = _min(yield, (rebasing * elapsed * rebasePerSecondMax) / 1e18);\n\n // Cap at a hard max per rebase, to avoid long durations resulting in huge rebases\n yield = _min(yield, (rebasing * MAX_REBASE) / 1e18);\n\n return (yield, targetRate);\n }\n\n /**\n * @notice Determine the total value of assets held by the vault and its\n * strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function totalValue() external view virtual returns (uint256 value) {\n value = _totalValue();\n }\n\n /**\n * @dev Internal Calculate the total value of the assets held by the\n * vault and its strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValue() internal view virtual returns (uint256 value) {\n return _totalValueInVault() + _totalValueInStrategies();\n }\n\n /**\n * @dev Internal to calculate total value of all assets held in Vault.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInVault()\n internal\n view\n virtual\n returns (uint256 value)\n {\n uint256 assetCount = allAssets.length;\n for (uint256 y; y < assetCount; ++y) {\n address assetAddr = allAssets[y];\n uint256 balance = IERC20(assetAddr).balanceOf(address(this));\n if (balance > 0) {\n value += _toUnits(balance, assetAddr);\n }\n }\n }\n\n /**\n * @dev Internal to calculate total value of all assets held in Strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInStrategies() internal view returns (uint256 value) {\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n value = value + _totalValueInStrategy(allStrategies[i]);\n }\n }\n\n /**\n * @dev Internal to calculate total value of all assets held by strategy.\n * @param _strategyAddr Address of the strategy\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInStrategy(address _strategyAddr)\n internal\n view\n returns (uint256 value)\n {\n IStrategy strategy = IStrategy(_strategyAddr);\n uint256 assetCount = allAssets.length;\n for (uint256 y; y < assetCount; ++y) {\n address assetAddr = allAssets[y];\n if (strategy.supportsAsset(assetAddr)) {\n uint256 balance = strategy.checkBalance(assetAddr);\n if (balance > 0) {\n value += _toUnits(balance, assetAddr);\n }\n }\n }\n }\n\n /**\n * @notice Get the balance of an asset held in Vault and all strategies.\n * @param _asset Address of asset\n * @return uint256 Balance of asset in decimals of asset\n */\n function checkBalance(address _asset) external view returns (uint256) {\n return _checkBalance(_asset);\n }\n\n /**\n * @notice Get the balance of an asset held in Vault and all strategies.\n * @param _asset Address of asset\n * @return balance Balance of asset in decimals of asset\n */\n function _checkBalance(address _asset)\n internal\n view\n virtual\n returns (uint256 balance)\n {\n IERC20 asset = IERC20(_asset);\n balance = asset.balanceOf(address(this));\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n IStrategy strategy = IStrategy(allStrategies[i]);\n if (strategy.supportsAsset(_asset)) {\n balance = balance + strategy.checkBalance(_asset);\n }\n }\n }\n\n /**\n * @notice Calculate the outputs for a redeem function, i.e. the mix of\n * coins that will be returned\n */\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory)\n {\n return _calculateRedeemOutputs(_amount);\n }\n\n /**\n * @dev Calculate the outputs for a redeem function, i.e. the mix of\n * coins that will be returned.\n * @return outputs Array of amounts respective to the supported assets\n */\n function _calculateRedeemOutputs(uint256 _amount)\n internal\n view\n virtual\n returns (uint256[] memory outputs)\n {\n // We always give out coins in proportion to how many we have,\n // Now if all coins were the same value, this math would easy,\n // just take the percentage of each coin, and multiply by the\n // value to be given out. But if coins are worth more than $1,\n // then we would end up handing out too many coins. We need to\n // adjust by the total value of coins.\n //\n // To do this, we total up the value of our coins, by their\n // percentages. Then divide what we would otherwise give out by\n // this number.\n //\n // Let say we have 100 DAI at $1.06 and 200 USDT at $1.00.\n // So for every 1 DAI we give out, we'll be handing out 2 USDT\n // Our total output ratio is: 33% * 1.06 + 66% * 1.00 = 1.02\n //\n // So when calculating the output, we take the percentage of\n // each coin, times the desired output value, divided by the\n // totalOutputRatio.\n //\n // For example, withdrawing: 30 OUSD:\n // DAI 33% * 30 / 1.02 = 9.80 DAI\n // USDT = 66 % * 30 / 1.02 = 19.60 USDT\n //\n // Checking these numbers:\n // 9.80 DAI * 1.06 = $10.40\n // 19.60 USDT * 1.00 = $19.60\n //\n // And so the user gets $10.40 + $19.60 = $30 worth of value.\n\n uint256 assetCount = allAssets.length;\n uint256[] memory assetUnits = new uint256[](assetCount);\n uint256[] memory assetBalances = new uint256[](assetCount);\n outputs = new uint256[](assetCount);\n\n // Calculate redeem fee\n if (redeemFeeBps > 0) {\n uint256 redeemFee = _amount.mulTruncateScale(redeemFeeBps, 1e4);\n _amount = _amount - redeemFee;\n }\n\n // Calculate assets balances and decimals once,\n // for a large gas savings.\n uint256 totalUnits = 0;\n for (uint256 i = 0; i < assetCount; ++i) {\n address assetAddr = allAssets[i];\n uint256 balance = _checkBalance(assetAddr);\n assetBalances[i] = balance;\n assetUnits[i] = _toUnits(balance, assetAddr);\n totalUnits = totalUnits + assetUnits[i];\n }\n // Calculate totalOutputRatio\n uint256 totalOutputRatio = 0;\n for (uint256 i = 0; i < assetCount; ++i) {\n uint256 unitPrice = _toUnitPrice(allAssets[i], false);\n uint256 ratio = (assetUnits[i] * unitPrice) / totalUnits;\n totalOutputRatio = totalOutputRatio + ratio;\n }\n // Calculate final outputs\n uint256 factor = _amount.divPrecisely(totalOutputRatio);\n for (uint256 i = 0; i < assetCount; ++i) {\n outputs[i] = (assetBalances[i] * factor) / totalUnits;\n }\n }\n\n /***************************************\n Pricing\n ****************************************/\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * Never goes above 1, since that is how we price mints.\n * @param asset address of the asset\n * @return price uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed\n */\n function priceUnitMint(address asset)\n external\n view\n returns (uint256 price)\n {\n /* need to supply 1 asset unit in asset's decimals and can not just hard-code\n * to 1e18 and ignore calling `_toUnits` since we need to consider assets\n * with the exchange rate\n */\n uint256 units = _toUnits(\n uint256(1e18).scaleBy(_getDecimals(asset), 18),\n asset\n );\n price = (_toUnitPrice(asset, true) * units) / 1e18;\n }\n\n /**\n * @notice Returns the total price in 18 digit unit for a given asset.\n * Never goes below 1, since that is how we price redeems\n * @param asset Address of the asset\n * @return price uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed\n */\n function priceUnitRedeem(address asset)\n external\n view\n returns (uint256 price)\n {\n /* need to supply 1 asset unit in asset's decimals and can not just hard-code\n * to 1e18 and ignore calling `_toUnits` since we need to consider assets\n * with the exchange rate\n */\n uint256 units = _toUnits(\n uint256(1e18).scaleBy(_getDecimals(asset), 18),\n asset\n );\n price = (_toUnitPrice(asset, false) * units) / 1e18;\n }\n\n /***************************************\n Utils\n ****************************************/\n\n /**\n * @dev Convert a quantity of a token into 1e18 fixed decimal \"units\"\n * in the underlying base (USD/ETH) used by the vault.\n * Price is not taken into account, only quantity.\n *\n * Examples of this conversion:\n *\n * - 1e18 DAI becomes 1e18 units (same decimals)\n * - 1e6 USDC becomes 1e18 units (decimal conversion)\n * - 1e18 rETH becomes 1.2e18 units (exchange rate conversion)\n *\n * @param _raw Quantity of asset\n * @param _asset Core Asset address\n * @return value 1e18 normalized quantity of units\n */\n function _toUnits(uint256 _raw, address _asset)\n internal\n view\n returns (uint256)\n {\n UnitConversion conversion = assets[_asset].unitConversion;\n if (conversion == UnitConversion.DECIMALS) {\n return _raw.scaleBy(18, _getDecimals(_asset));\n } else if (conversion == UnitConversion.GETEXCHANGERATE) {\n uint256 exchangeRate = IGetExchangeRateToken(_asset)\n .getExchangeRate();\n return (_raw * exchangeRate) / 1e18;\n } else {\n revert(\"Unsupported conversion type\");\n }\n }\n\n /**\n * @dev Returns asset's unit price accounting for different asset types\n * and takes into account the context in which that price exists -\n * - mint or redeem.\n *\n * Note: since we are returning the price of the unit and not the one of the\n * asset (see comment above how 1 rETH exchanges for 1.2 units) we need\n * to make the Oracle price adjustment as well since we are pricing the\n * units and not the assets.\n *\n * The price also snaps to a \"full unit price\" in case a mint or redeem\n * action would be unfavourable to the protocol.\n *\n */\n function _toUnitPrice(address _asset, bool isMint)\n internal\n view\n returns (uint256 price)\n {\n UnitConversion conversion = assets[_asset].unitConversion;\n price = IOracle(priceProvider).price(_asset);\n\n if (conversion == UnitConversion.GETEXCHANGERATE) {\n uint256 exchangeRate = IGetExchangeRateToken(_asset)\n .getExchangeRate();\n price = (price * 1e18) / exchangeRate;\n } else if (conversion != UnitConversion.DECIMALS) {\n revert(\"Unsupported conversion type\");\n }\n\n /* At this stage the price is already adjusted to the unit\n * so the price checks are agnostic to underlying asset being\n * pegged to a USD or to an ETH or having a custom exchange rate.\n */\n require(price <= MAX_UNIT_PRICE_DRIFT, \"Vault: Price exceeds max\");\n require(price >= MIN_UNIT_PRICE_DRIFT, \"Vault: Price under min\");\n\n if (isMint) {\n /* Never price a normalized unit price for more than one\n * unit of OETH/OUSD when minting.\n */\n if (price > 1e18) {\n price = 1e18;\n }\n require(price >= MINT_MINIMUM_UNIT_PRICE, \"Asset price below peg\");\n } else {\n /* Never give out more than 1 normalized unit amount of assets\n * for one unit of OETH/OUSD when redeeming.\n */\n if (price < 1e18) {\n price = 1e18;\n }\n }\n }\n\n /**\n * @dev Get the number of decimals of a token asset\n * @param _asset Address of the asset\n * @return decimals number of decimals\n */\n function _getDecimals(address _asset)\n internal\n view\n returns (uint256 decimals)\n {\n decimals = assets[_asset].decimals;\n require(decimals > 0, \"Decimals not cached\");\n }\n\n /**\n * @notice Return the number of assets supported by the Vault.\n */\n function getAssetCount() public view returns (uint256) {\n return allAssets.length;\n }\n\n /**\n * @notice Gets the vault configuration of a supported asset.\n * @param _asset Address of the token asset\n */\n function getAssetConfig(address _asset)\n public\n view\n returns (Asset memory config)\n {\n config = assets[_asset];\n }\n\n /**\n * @notice Return all vault asset addresses in order\n */\n function getAllAssets() external view returns (address[] memory) {\n return allAssets;\n }\n\n /**\n * @notice Return the number of strategies active on the Vault.\n */\n function getStrategyCount() external view returns (uint256) {\n return allStrategies.length;\n }\n\n /**\n * @notice Return the array of all strategies\n */\n function getAllStrategies() external view returns (address[] memory) {\n return allStrategies;\n }\n\n /**\n * @notice Returns whether the vault supports the asset\n * @param _asset address of the asset\n * @return true if supported\n */\n function isSupportedAsset(address _asset) external view returns (bool) {\n return assets[_asset].isSupported;\n }\n\n function ADMIN_IMPLEMENTATION() external view returns (address adminImpl) {\n bytes32 slot = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n adminImpl := sload(slot)\n }\n }\n\n /**\n * @dev Falldown to the admin implementation\n * @notice This is a catch all for all functions not declared in core\n */\n // solhint-disable-next-line no-complex-fallback\n fallback() external {\n bytes32 slot = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(\n gas(),\n sload(slot),\n 0,\n calldatasize(),\n 0,\n 0\n )\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n function abs(int256 x) private pure returns (uint256) {\n require(x < int256(MAX_INT), \"Amount too high\");\n return x >= 0 ? uint256(x) : uint256(-x);\n }\n\n function _min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n function _max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a > b ? a : b;\n }\n}\n" + }, + "contracts/vault/VaultInitializer.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultInitializer contract\n * @notice The Vault contract initializes the vault.\n * @author Origin Protocol Inc\n */\n\nimport \"./VaultStorage.sol\";\n\ncontract VaultInitializer is VaultStorage {\n function initialize(address _priceProvider, address _oToken)\n external\n onlyGovernor\n initializer\n {\n require(_priceProvider != address(0), \"PriceProvider address is zero\");\n require(_oToken != address(0), \"oToken address is zero\");\n\n oUSD = OUSD(_oToken);\n\n priceProvider = _priceProvider;\n\n rebasePaused = false;\n capitalPaused = true;\n\n // Initial redeem fee of 0 basis points\n redeemFeeBps = 0;\n // Initial Vault buffer of 0%\n vaultBuffer = 0;\n // Initial allocate threshold of 25,000 OUSD\n autoAllocateThreshold = 25000e18;\n // Threshold for rebasing\n rebaseThreshold = 1000e18;\n // Initialize all strategies\n allStrategies = new address[](0);\n // Start with drip duration disabled\n dripDuration = 1;\n }\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + }, + "contracts/zapper/WOETHCCIPZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IRouterClient } from \"@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol\";\nimport { Client } from \"@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol\";\n// solhint-disable-next-line max-line-length\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"./../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IOETHZapper } from \"./../interfaces/IOETHZapper.sol\";\n\n/**\n * @title WOETH CCIP Zapper Contract\n * @notice Helps to directly convert ETH on mainnet into WOETH on L2s.\n * @author Origin Protocol Inc\n */\n\ncontract WOETHCCIPZapper {\n /**\n * @dev Event emitted when a zap occurs\n * @param messageId Unique message identifier for each zap\n * @param sender Address initiating the zap\n * @param recipient Recipient address at destination chain\n * @param amount Amount of ETH zapped\n */\n event Zap(\n bytes32 indexed messageId,\n address sender,\n address recipient,\n uint256 amount\n );\n\n // @dev Thrown when Zap amount is less than fee.\n error AmountLessThanFee();\n\n /**\n * @dev The destination chain selector\n */\n uint64 public immutable destinationChainSelector;\n\n /**\n * @dev The WOETH source chain (Mainnet)\n */\n IERC4626 public immutable woethOnSourceChain;\n\n /**\n * @dev The WOETH destination chain (Arbitrum)\n */\n IERC20 public immutable woethOnDestinationChain;\n\n /**\n * @dev The OETH zapper contract address\n */\n IOETHZapper public immutable oethZapper;\n\n /**\n * @dev The CCIP router contract address\n */\n IRouterClient public immutable ccipRouter;\n\n /**\n * @dev The OETH token contract address\n */\n IERC20 public immutable oeth;\n\n constructor(\n address _ccipRouter,\n uint64 _destinationChainSelector,\n IERC4626 _woethOnSourceChain,\n IERC20 _woethOnDestinationChain,\n IOETHZapper _oethZapper,\n IERC20 _oeth\n ) {\n ccipRouter = IRouterClient(_ccipRouter);\n destinationChainSelector = _destinationChainSelector;\n woethOnSourceChain = _woethOnSourceChain;\n woethOnDestinationChain = _woethOnDestinationChain;\n oethZapper = _oethZapper;\n oeth = _oeth;\n\n // Max allowance for Router and WOETH contracts\n _oeth.approve(address(_woethOnSourceChain), type(uint256).max); // for wrapping\n _woethOnSourceChain.approve(address(_ccipRouter), type(uint256).max); // for zapping\n }\n\n /**\n * @notice Accepts ETH, zaps for OETH, wraps it for WOETH and sends it to the destination chain (arbitrum)\n * @param receiver The address of the EOA on the destination chain\n * @return messageId The ID of the message that was sent\n */\n function zap(address receiver)\n external\n payable\n returns (bytes32 messageId)\n {\n return _zap(receiver, msg.value);\n }\n\n /**\n * @notice Used to estimate fee for CCIP transaction\n * @param amount The amount of ETH to be zapped\n * @param receiver The address of the EOA on the destination chain\n * @return feeAmount The CCIP tx fee in ETH.\n */\n\n function getFee(uint256 amount, address receiver)\n public\n view\n returns (uint256 feeAmount)\n {\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(woethOnSourceChain),\n amount: amount\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({\n receiver: abi.encode(receiver), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n feeAmount = ccipRouter.getFee(destinationChainSelector, message);\n }\n\n /**\n * @dev Deposit ETH and receive WOETH in L2.\n * @dev Note that the WOETH will be sent to the msg.sender at destination chain as well.\n */\n receive() external payable {\n _zap(msg.sender, msg.value);\n }\n\n function _zap(address receiver, uint256 amount)\n internal\n returns (bytes32 messageId)\n {\n // Estimate fee for zapping.\n uint256 feeAmount = getFee(amount, receiver);\n if (amount < feeAmount) {\n revert AmountLessThanFee();\n }\n\n // Convert only the msg.value - fees amount to WOETH.\n amount -= feeAmount;\n\n // 1.) Zap for OETH\n uint256 oethReceived = oethZapper.deposit{ value: amount }();\n\n // 2.) Wrap the received woeth\n uint256 woethReceived = woethOnSourceChain.deposit(\n oethReceived,\n address(this)\n );\n\n // 3.) Setup params for CCIP transfer\n\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(woethOnSourceChain),\n amount: woethReceived\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({\n receiver: abi.encode(receiver), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n // See: https://docs.chain.link/ccip/best-practices#setting-gaslimit\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n // ZAP ϟ\n //slither-disable-next-line arbitrary-send-eth\n messageId = ccipRouter.ccipSend{ value: feeAmount }(\n destinationChainSelector,\n message\n );\n\n // Emit Zap event with message details\n emit Zap(messageId, msg.sender, receiver, amount);\n\n // Return the message ID\n return messageId;\n }\n}\n" + }, + "hardhat/console.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.4.22 <0.9.0;\n\nlibrary console {\n address constant CONSOLE_ADDRESS =\n 0x000000000000000000636F6e736F6c652e6c6f67;\n\n function _sendLogPayloadImplementation(bytes memory payload) internal view {\n address consoleAddress = CONSOLE_ADDRESS;\n /// @solidity memory-safe-assembly\n assembly {\n pop(\n staticcall(\n gas(),\n consoleAddress,\n add(payload, 32),\n mload(payload),\n 0,\n 0\n )\n )\n }\n }\n\n function _castToPure(\n function(bytes memory) internal view fnIn\n ) internal pure returns (function(bytes memory) pure fnOut) {\n assembly {\n fnOut := fnIn\n }\n }\n\n function _sendLogPayload(bytes memory payload) internal pure {\n _castToPure(_sendLogPayloadImplementation)(payload);\n }\n\n function log() internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log()\"));\n }\n\n function logInt(int256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(int256)\", p0));\n }\n\n function logUint(uint256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256)\", p0));\n }\n\n function logString(string memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n }\n\n function logBool(bool p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n }\n\n function logAddress(address p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n }\n\n function logBytes(bytes memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes)\", p0));\n }\n\n function logBytes1(bytes1 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes1)\", p0));\n }\n\n function logBytes2(bytes2 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes2)\", p0));\n }\n\n function logBytes3(bytes3 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes3)\", p0));\n }\n\n function logBytes4(bytes4 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes4)\", p0));\n }\n\n function logBytes5(bytes5 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes5)\", p0));\n }\n\n function logBytes6(bytes6 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes6)\", p0));\n }\n\n function logBytes7(bytes7 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes7)\", p0));\n }\n\n function logBytes8(bytes8 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes8)\", p0));\n }\n\n function logBytes9(bytes9 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes9)\", p0));\n }\n\n function logBytes10(bytes10 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes10)\", p0));\n }\n\n function logBytes11(bytes11 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes11)\", p0));\n }\n\n function logBytes12(bytes12 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes12)\", p0));\n }\n\n function logBytes13(bytes13 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes13)\", p0));\n }\n\n function logBytes14(bytes14 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes14)\", p0));\n }\n\n function logBytes15(bytes15 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes15)\", p0));\n }\n\n function logBytes16(bytes16 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes16)\", p0));\n }\n\n function logBytes17(bytes17 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes17)\", p0));\n }\n\n function logBytes18(bytes18 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes18)\", p0));\n }\n\n function logBytes19(bytes19 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes19)\", p0));\n }\n\n function logBytes20(bytes20 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes20)\", p0));\n }\n\n function logBytes21(bytes21 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes21)\", p0));\n }\n\n function logBytes22(bytes22 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes22)\", p0));\n }\n\n function logBytes23(bytes23 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes23)\", p0));\n }\n\n function logBytes24(bytes24 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes24)\", p0));\n }\n\n function logBytes25(bytes25 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes25)\", p0));\n }\n\n function logBytes26(bytes26 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes26)\", p0));\n }\n\n function logBytes27(bytes27 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes27)\", p0));\n }\n\n function logBytes28(bytes28 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes28)\", p0));\n }\n\n function logBytes29(bytes29 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes29)\", p0));\n }\n\n function logBytes30(bytes30 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes30)\", p0));\n }\n\n function logBytes31(bytes31 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes31)\", p0));\n }\n\n function logBytes32(bytes32 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes32)\", p0));\n }\n\n function log(uint256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256)\", p0));\n }\n\n function log(string memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n }\n\n function log(bool p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n }\n\n function log(address p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n }\n\n function log(uint256 p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256)\", p0, p1));\n }\n\n function log(uint256 p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string)\", p0, p1));\n }\n\n function log(uint256 p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool)\", p0, p1));\n }\n\n function log(uint256 p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address)\", p0, p1));\n }\n\n function log(string memory p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256)\", p0, p1));\n }\n\n function log(string memory p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string)\", p0, p1));\n }\n\n function log(string memory p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool)\", p0, p1));\n }\n\n function log(string memory p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address)\", p0, p1));\n }\n\n function log(bool p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256)\", p0, p1));\n }\n\n function log(bool p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string)\", p0, p1));\n }\n\n function log(bool p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool)\", p0, p1));\n }\n\n function log(bool p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address)\", p0, p1));\n }\n\n function log(address p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256)\", p0, p1));\n }\n\n function log(address p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string)\", p0, p1));\n }\n\n function log(address p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool)\", p0, p1));\n }\n\n function log(address p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address)\", p0, p1));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,address)\", p0, p1, p2, p3));\n }\n}\n" + }, + "lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC4626 } from \"../../../../interfaces/IERC4626.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\n// From Open Zeppelin draft PR commit:\n// fac43034dca85ff539db3fc8aa2a7084b843d454\n// https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3171\n\nabstract contract ERC4626 is ERC20, IERC4626 {\n IERC20Metadata private immutable _asset;\n\n constructor(IERC20Metadata __asset) {\n _asset = __asset;\n }\n\n /** @dev See {IERC4262-asset} */\n function asset() public view virtual override returns (address) {\n return address(_asset);\n }\n\n /** @dev See {IERC4262-totalAssets} */\n function totalAssets() public view virtual override returns (uint256) {\n return _asset.balanceOf(address(this));\n }\n\n /**\n * @dev See {IERC4262-convertToShares}\n *\n * Will revert if asserts > 0, totalSupply > 0 and totalAssets = 0. That corresponds to a case where any asset\n * would represent an infinite amout of shares.\n */\n function convertToShares(uint256 assets) public view virtual override returns (uint256 shares) {\n uint256 supply = totalSupply();\n\n return\n (assets == 0 || supply == 0)\n ? (assets * 10**decimals()) / 10**_asset.decimals()\n : (assets * supply) / totalAssets();\n }\n\n /** @dev See {IERC4262-convertToAssets} */\n function convertToAssets(uint256 shares) public view virtual override returns (uint256 assets) {\n uint256 supply = totalSupply();\n\n return (supply == 0) ? (shares * 10**_asset.decimals()) / 10**decimals() : (shares * totalAssets()) / supply;\n }\n\n /** @dev See {IERC4262-maxDeposit} */\n function maxDeposit(address) public view virtual override returns (uint256) {\n return type(uint256).max;\n }\n\n /** @dev See {IERC4262-maxMint} */\n function maxMint(address) public view virtual override returns (uint256) {\n return type(uint256).max;\n }\n\n /** @dev See {IERC4262-maxWithdraw} */\n function maxWithdraw(address owner) public view virtual override returns (uint256) {\n return convertToAssets(balanceOf(owner));\n }\n\n /** @dev See {IERC4262-maxRedeem} */\n function maxRedeem(address owner) public view virtual override returns (uint256) {\n return balanceOf(owner);\n }\n\n /** @dev See {IERC4262-previewDeposit} */\n function previewDeposit(uint256 assets) public view virtual override returns (uint256) {\n return convertToShares(assets);\n }\n\n /** @dev See {IERC4262-previewMint} */\n function previewMint(uint256 shares) public view virtual override returns (uint256) {\n uint256 assets = convertToAssets(shares);\n return assets + (convertToShares(assets) < shares ? 1 : 0);\n }\n\n /** @dev See {IERC4262-previewWithdraw} */\n function previewWithdraw(uint256 assets) public view virtual override returns (uint256) {\n uint256 shares = convertToShares(assets);\n return shares + (convertToAssets(shares) < assets ? 1 : 0);\n }\n\n /** @dev See {IERC4262-previewRedeem} */\n function previewRedeem(uint256 shares) public view virtual override returns (uint256) {\n return convertToAssets(shares);\n }\n\n /** @dev See {IERC4262-deposit} */\n function deposit(uint256 assets, address receiver) public virtual override returns (uint256) {\n require(assets <= maxDeposit(receiver), \"ERC4626: deposit more then max\");\n\n address caller = _msgSender();\n uint256 shares = previewDeposit(assets);\n\n // if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through\n // the tokensToSend hook, so we need to transfer before we mint to keep the invariants.\n SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);\n _mint(receiver, shares);\n\n emit Deposit(caller, receiver, assets, shares);\n\n return shares;\n }\n\n /** @dev See {IERC4262-mint} */\n function mint(uint256 shares, address receiver) public virtual override returns (uint256) {\n require(shares <= maxMint(receiver), \"ERC4626: mint more then max\");\n\n address caller = _msgSender();\n uint256 assets = previewMint(shares);\n\n // if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through\n // the tokensToSend hook, so we need to transfer before we mint to keep the invariants.\n SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);\n _mint(receiver, shares);\n\n emit Deposit(caller, receiver, assets, shares);\n\n return assets;\n }\n\n /** @dev See {IERC4262-withdraw} */\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) public virtual override returns (uint256) {\n require(assets <= maxWithdraw(owner), \"ERC4626: withdraw more then max\");\n\n address caller = _msgSender();\n uint256 shares = previewWithdraw(assets);\n\n if (caller != owner) {\n _spendAllowance(owner, caller, shares);\n }\n\n // if _asset is ERC777, transfer can call reenter AFTER the transfer happens through\n // the tokensReceived hook, so we need to transfer after we burn to keep the invariants.\n _burn(owner, shares);\n SafeERC20.safeTransfer(_asset, receiver, assets);\n\n emit Withdraw(caller, receiver, owner, assets, shares);\n\n return shares;\n }\n\n /** @dev See {IERC4262-redeem} */\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) public virtual override returns (uint256) {\n require(shares <= maxRedeem(owner), \"ERC4626: redeem more then max\");\n\n address caller = _msgSender();\n uint256 assets = previewRedeem(shares);\n\n if (caller != owner) {\n _spendAllowance(owner, caller, shares);\n }\n\n // if _asset is ERC777, transfer can call reenter AFTER the transfer happens through\n // the tokensReceived hook, so we need to transfer after we burn to keep the invariants.\n _burn(owner, shares);\n SafeERC20.safeTransfer(_asset, receiver, assets);\n\n emit Withdraw(caller, receiver, owner, assets, shares);\n\n return assets;\n }\n\n // Included here, since this method was not yet present in\n // the version of Open Zeppelin ERC20 code we use.\n function _spendAllowance(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n uint256 currentAllowance = allowance(owner, spender);\n if (currentAllowance != type(uint256).max) {\n require(currentAllowance >= amount, \"ERC20: insufficient allowance\");\n unchecked {\n _approve(owner, spender, currentAllowance - amount);\n }\n }\n }\n}" + }, + "lib/openzeppelin/interfaces/IERC4626.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IERC4626 is IERC20, IERC20Metadata {\n event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);\n\n event Withdraw(\n address indexed caller,\n address indexed receiver,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n\n /**\n * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.\n *\n * - MUST be an ERC-20 token contract.\n * - MUST NOT revert.\n */\n function asset() external view returns (address assetTokenAddress);\n\n /**\n * @dev Returns the total amount of the underlying asset that is “managed” by Vault.\n *\n * - SHOULD include any compounding that occurs from yield.\n * - MUST be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT revert.\n */\n function totalAssets() external view returns (uint256 totalManagedAssets);\n\n /**\n * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal\n * scenario where all the conditions are met.\n *\n * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT show any variations depending on the caller.\n * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n * - MUST NOT revert.\n *\n * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n * from.\n */\n function convertToShares(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal\n * scenario where all the conditions are met.\n *\n * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT show any variations depending on the caller.\n * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n * - MUST NOT revert.\n *\n * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n * from.\n */\n function convertToAssets(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,\n * through a deposit call.\n *\n * - MUST return a limited value if receiver is subject to some deposit limit.\n * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.\n * - MUST NOT revert.\n */\n function maxDeposit(address receiver) external view returns (uint256 maxAssets);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given\n * current on-chain conditions.\n *\n * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit\n * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called\n * in the same transaction.\n * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the\n * deposit would be accepted, regardless if the user has enough tokens approved, etc.\n * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by depositing.\n */\n function previewDeposit(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.\n *\n * - MUST emit the Deposit event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * deposit execution, and are accounted for during deposit.\n * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not\n * approving enough underlying tokens to the Vault contract, etc).\n *\n * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n */\n function deposit(uint256 assets, address receiver) external returns (uint256 shares);\n\n /**\n * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.\n * - MUST return a limited value if receiver is subject to some mint limit.\n * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.\n * - MUST NOT revert.\n */\n function maxMint(address receiver) external view returns (uint256 maxShares);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given\n * current on-chain conditions.\n *\n * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call\n * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the\n * same transaction.\n * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint\n * would be accepted, regardless if the user has enough tokens approved, etc.\n * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by minting.\n */\n function previewMint(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.\n *\n * - MUST emit the Deposit event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint\n * execution, and are accounted for during mint.\n * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not\n * approving enough underlying tokens to the Vault contract, etc).\n *\n * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n */\n function mint(uint256 shares, address receiver) external returns (uint256 assets);\n\n /**\n * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the\n * Vault, through a withdraw call.\n *\n * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n * - MUST NOT revert.\n */\n function maxWithdraw(address owner) external view returns (uint256 maxAssets);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,\n * given current on-chain conditions.\n *\n * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw\n * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if\n * called\n * in the same transaction.\n * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though\n * the withdrawal would be accepted, regardless if the user has enough shares, etc.\n * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by depositing.\n */\n function previewWithdraw(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.\n *\n * - MUST emit the Withdraw event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * withdraw execution, and are accounted for during withdraw.\n * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner\n * not having enough shares, etc).\n *\n * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n * Those methods should be performed separately.\n */\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares);\n\n /**\n * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,\n * through a redeem call.\n *\n * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.\n * - MUST NOT revert.\n */\n function maxRedeem(address owner) external view returns (uint256 maxShares);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,\n * given current on-chain conditions.\n *\n * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call\n * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the\n * same transaction.\n * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the\n * redemption would be accepted, regardless if the user has enough shares, etc.\n * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by redeeming.\n */\n function previewRedeem(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.\n *\n * - MUST emit the Withdraw event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * redeem execution, and are accounted for during redeem.\n * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner\n * not having enough shares, etc).\n *\n * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n * Those methods should be performed separately.\n */\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets);\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.20;\n\nimport {Panic} from \"../Panic.sol\";\nimport {SafeCast} from \"./SafeCast.sol\";\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n enum Rounding {\n Floor, // Toward negative infinity\n Ceil, // Toward positive infinity\n Trunc, // Toward zero\n Expand // Away from zero\n }\n\n /**\n * @dev Return the 512-bit addition of two uint256.\n *\n * The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low.\n */\n function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {\n assembly (\"memory-safe\") {\n low := add(a, b)\n high := lt(low, a)\n }\n }\n\n /**\n * @dev Return the 512-bit multiplication of two uint256.\n *\n * The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low.\n */\n function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {\n // 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use\n // the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n // variables such that product = high * 2²⁵⁶ + low.\n assembly (\"memory-safe\") {\n let mm := mulmod(a, b, not(0))\n low := mul(a, b)\n high := sub(sub(mm, low), lt(mm, low))\n }\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, with a success flag (no overflow).\n */\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a + b;\n success = c >= a;\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).\n */\n function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a - b;\n success = c <= a;\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).\n */\n function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a * b;\n assembly (\"memory-safe\") {\n // Only true when the multiplication doesn't overflow\n // (c / a == b) || (a == 0)\n success := or(eq(div(c, a), b), iszero(a))\n }\n // equivalent to: success ? c : 0\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the division of two unsigned integers, with a success flag (no division by zero).\n */\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n success = b > 0;\n assembly (\"memory-safe\") {\n // The `DIV` opcode returns zero when the denominator is 0.\n result := div(a, b)\n }\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).\n */\n function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n success = b > 0;\n assembly (\"memory-safe\") {\n // The `MOD` opcode returns zero when the denominator is 0.\n result := mod(a, b)\n }\n }\n }\n\n /**\n * @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing.\n */\n function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {\n (bool success, uint256 result) = tryAdd(a, b);\n return ternary(success, result, type(uint256).max);\n }\n\n /**\n * @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.\n */\n function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {\n (, uint256 result) = trySub(a, b);\n return result;\n }\n\n /**\n * @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing.\n */\n function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {\n (bool success, uint256 result) = tryMul(a, b);\n return ternary(success, result, type(uint256).max);\n }\n\n /**\n * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.\n *\n * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.\n * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute\n * one branch when needed, making this function more expensive.\n */\n function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {\n unchecked {\n // branchless ternary works because:\n // b ^ (a ^ b) == a\n // b ^ 0 == b\n return b ^ ((a ^ b) * SafeCast.toUint(condition));\n }\n }\n\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return ternary(a > b, a, b);\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return ternary(a < b, a, b);\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds towards infinity instead\n * of rounding towards zero.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n if (b == 0) {\n // Guarantee the same behavior as in a regular Solidity division.\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n\n // The following calculation ensures accurate ceiling division without overflow.\n // Since a is non-zero, (a - 1) / b will not overflow.\n // The largest possible result occurs when (a - 1) / b is type(uint256).max,\n // but the largest value we can obtain is type(uint256).max - 1, which happens\n // when a = type(uint256).max and b = 1.\n unchecked {\n return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);\n }\n }\n\n /**\n * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or\n * denominator == 0.\n *\n * Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by\n * Uniswap Labs also under MIT license.\n */\n function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {\n unchecked {\n (uint256 high, uint256 low) = mul512(x, y);\n\n // Handle non-overflow cases, 256 by 256 division.\n if (high == 0) {\n // Solidity will revert if denominator == 0, unlike the div opcode on its own.\n // The surrounding unchecked block does not change this fact.\n // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.\n return low / denominator;\n }\n\n // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.\n if (denominator <= high) {\n Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));\n }\n\n ///////////////////////////////////////////////\n // 512 by 256 division.\n ///////////////////////////////////////////////\n\n // Make division exact by subtracting the remainder from [high low].\n uint256 remainder;\n assembly (\"memory-safe\") {\n // Compute remainder using mulmod.\n remainder := mulmod(x, y, denominator)\n\n // Subtract 256 bit number from 512 bit number.\n high := sub(high, gt(remainder, low))\n low := sub(low, remainder)\n }\n\n // Factor powers of two out of denominator and compute largest power of two divisor of denominator.\n // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.\n\n uint256 twos = denominator & (0 - denominator);\n assembly (\"memory-safe\") {\n // Divide denominator by twos.\n denominator := div(denominator, twos)\n\n // Divide [high low] by twos.\n low := div(low, twos)\n\n // Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.\n twos := add(div(sub(0, twos), twos), 1)\n }\n\n // Shift in bits from high into low.\n low |= high * twos;\n\n // Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such\n // that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for\n // four bits. That is, denominator * inv ≡ 1 mod 2⁴.\n uint256 inverse = (3 * denominator) ^ 2;\n\n // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also\n // works in modular arithmetic, doubling the correct bits in each step.\n inverse *= 2 - denominator * inverse; // inverse mod 2⁸\n inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶\n inverse *= 2 - denominator * inverse; // inverse mod 2³²\n inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴\n inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸\n inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶\n\n // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n // This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is\n // less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and high\n // is no longer required.\n result = low * inverse;\n return result;\n }\n }\n\n /**\n * @dev Calculates x * y / denominator with full precision, following the selected rounding direction.\n */\n function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {\n return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);\n }\n\n /**\n * @dev Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256.\n */\n function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) {\n unchecked {\n (uint256 high, uint256 low) = mul512(x, y);\n if (high >= 1 << n) {\n Panic.panic(Panic.UNDER_OVERFLOW);\n }\n return (high << (256 - n)) | (low >> n);\n }\n }\n\n /**\n * @dev Calculates x * y >> n with full precision, following the selected rounding direction.\n */\n function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) {\n return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 0);\n }\n\n /**\n * @dev Calculate the modular multiplicative inverse of a number in Z/nZ.\n *\n * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.\n * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.\n *\n * If the input value is not inversible, 0 is returned.\n *\n * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the\n * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.\n */\n function invMod(uint256 a, uint256 n) internal pure returns (uint256) {\n unchecked {\n if (n == 0) return 0;\n\n // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)\n // Used to compute integers x and y such that: ax + ny = gcd(a, n).\n // When the gcd is 1, then the inverse of a modulo n exists and it's x.\n // ax + ny = 1\n // ax = 1 + (-y)n\n // ax ≡ 1 (mod n) # x is the inverse of a modulo n\n\n // If the remainder is 0 the gcd is n right away.\n uint256 remainder = a % n;\n uint256 gcd = n;\n\n // Therefore the initial coefficients are:\n // ax + ny = gcd(a, n) = n\n // 0a + 1n = n\n int256 x = 0;\n int256 y = 1;\n\n while (remainder != 0) {\n uint256 quotient = gcd / remainder;\n\n (gcd, remainder) = (\n // The old remainder is the next gcd to try.\n remainder,\n // Compute the next remainder.\n // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd\n // where gcd is at most n (capped to type(uint256).max)\n gcd - remainder * quotient\n );\n\n (x, y) = (\n // Increment the coefficient of a.\n y,\n // Decrement the coefficient of n.\n // Can overflow, but the result is casted to uint256 so that the\n // next value of y is \"wrapped around\" to a value between 0 and n - 1.\n x - y * int256(quotient)\n );\n }\n\n if (gcd != 1) return 0; // No inverse exists.\n return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.\n }\n }\n\n /**\n * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.\n *\n * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is\n * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that\n * `a**(p-2)` is the modular multiplicative inverse of a in Fp.\n *\n * NOTE: this function does NOT check that `p` is a prime greater than `2`.\n */\n function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {\n unchecked {\n return Math.modExp(a, p - 2, p);\n }\n }\n\n /**\n * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)\n *\n * Requirements:\n * - modulus can't be zero\n * - underlying staticcall to precompile must succeed\n *\n * IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make\n * sure the chain you're using it on supports the precompiled contract for modular exponentiation\n * at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,\n * the underlying function will succeed given the lack of a revert, but the result may be incorrectly\n * interpreted as 0.\n */\n function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {\n (bool success, uint256 result) = tryModExp(b, e, m);\n if (!success) {\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n return result;\n }\n\n /**\n * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).\n * It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying\n * to operate modulo 0 or if the underlying precompile reverted.\n *\n * IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain\n * you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in\n * https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack\n * of a revert, but the result may be incorrectly interpreted as 0.\n */\n function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {\n if (m == 0) return (false, 0);\n assembly (\"memory-safe\") {\n let ptr := mload(0x40)\n // | Offset | Content | Content (Hex) |\n // |-----------|------------|--------------------------------------------------------------------|\n // | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x60:0x7f | value of b | 0x<.............................................................b> |\n // | 0x80:0x9f | value of e | 0x<.............................................................e> |\n // | 0xa0:0xbf | value of m | 0x<.............................................................m> |\n mstore(ptr, 0x20)\n mstore(add(ptr, 0x20), 0x20)\n mstore(add(ptr, 0x40), 0x20)\n mstore(add(ptr, 0x60), b)\n mstore(add(ptr, 0x80), e)\n mstore(add(ptr, 0xa0), m)\n\n // Given the result < m, it's guaranteed to fit in 32 bytes,\n // so we can use the memory scratch space located at offset 0.\n success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)\n result := mload(0x00)\n }\n }\n\n /**\n * @dev Variant of {modExp} that supports inputs of arbitrary length.\n */\n function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {\n (bool success, bytes memory result) = tryModExp(b, e, m);\n if (!success) {\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n return result;\n }\n\n /**\n * @dev Variant of {tryModExp} that supports inputs of arbitrary length.\n */\n function tryModExp(\n bytes memory b,\n bytes memory e,\n bytes memory m\n ) internal view returns (bool success, bytes memory result) {\n if (_zeroBytes(m)) return (false, new bytes(0));\n\n uint256 mLen = m.length;\n\n // Encode call args in result and move the free memory pointer\n result = abi.encodePacked(b.length, e.length, mLen, b, e, m);\n\n assembly (\"memory-safe\") {\n let dataPtr := add(result, 0x20)\n // Write result on top of args to avoid allocating extra memory.\n success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)\n // Overwrite the length.\n // result.length > returndatasize() is guaranteed because returndatasize() == m.length\n mstore(result, mLen)\n // Set the memory pointer after the returned data.\n mstore(0x40, add(dataPtr, mLen))\n }\n }\n\n /**\n * @dev Returns whether the provided byte array is zero.\n */\n function _zeroBytes(bytes memory byteArray) private pure returns (bool) {\n for (uint256 i = 0; i < byteArray.length; ++i) {\n if (byteArray[i] != 0) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded\n * towards zero.\n *\n * This method is based on Newton's method for computing square roots; the algorithm is restricted to only\n * using integer operations.\n */\n function sqrt(uint256 a) internal pure returns (uint256) {\n unchecked {\n // Take care of easy edge cases when a == 0 or a == 1\n if (a <= 1) {\n return a;\n }\n\n // In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a\n // sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between\n // the current value as `ε_n = | x_n - sqrt(a) |`.\n //\n // For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root\n // of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is\n // bigger than any uint256.\n //\n // By noticing that\n // `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`\n // we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar\n // to the msb function.\n uint256 aa = a;\n uint256 xn = 1;\n\n if (aa >= (1 << 128)) {\n aa >>= 128;\n xn <<= 64;\n }\n if (aa >= (1 << 64)) {\n aa >>= 64;\n xn <<= 32;\n }\n if (aa >= (1 << 32)) {\n aa >>= 32;\n xn <<= 16;\n }\n if (aa >= (1 << 16)) {\n aa >>= 16;\n xn <<= 8;\n }\n if (aa >= (1 << 8)) {\n aa >>= 8;\n xn <<= 4;\n }\n if (aa >= (1 << 4)) {\n aa >>= 4;\n xn <<= 2;\n }\n if (aa >= (1 << 2)) {\n xn <<= 1;\n }\n\n // We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).\n //\n // We can refine our estimation by noticing that the middle of that interval minimizes the error.\n // If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).\n // This is going to be our x_0 (and ε_0)\n xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)\n\n // From here, Newton's method give us:\n // x_{n+1} = (x_n + a / x_n) / 2\n //\n // One should note that:\n // x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a\n // = ((x_n² + a) / (2 * x_n))² - a\n // = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a\n // = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)\n // = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)\n // = (x_n² - a)² / (2 * x_n)²\n // = ((x_n² - a) / (2 * x_n))²\n // ≥ 0\n // Which proves that for all n ≥ 1, sqrt(a) ≤ x_n\n //\n // This gives us the proof of quadratic convergence of the sequence:\n // ε_{n+1} = | x_{n+1} - sqrt(a) |\n // = | (x_n + a / x_n) / 2 - sqrt(a) |\n // = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |\n // = | (x_n - sqrt(a))² / (2 * x_n) |\n // = | ε_n² / (2 * x_n) |\n // = ε_n² / | (2 * x_n) |\n //\n // For the first iteration, we have a special case where x_0 is known:\n // ε_1 = ε_0² / | (2 * x_0) |\n // ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))\n // ≤ 2**(2*e-4) / (3 * 2**(e-1))\n // ≤ 2**(e-3) / 3\n // ≤ 2**(e-3-log2(3))\n // ≤ 2**(e-4.5)\n //\n // For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:\n // ε_{n+1} = ε_n² / | (2 * x_n) |\n // ≤ (2**(e-k))² / (2 * 2**(e-1))\n // ≤ 2**(2*e-2*k) / 2**e\n // ≤ 2**(e-2*k)\n xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above\n xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5\n xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9\n xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18\n xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36\n xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72\n\n // Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision\n // ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either\n // sqrt(a) or sqrt(a) + 1.\n return xn - SafeCast.toUint(xn > a / xn);\n }\n }\n\n /**\n * @dev Calculates sqrt(a), following the selected rounding direction.\n */\n function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = sqrt(a);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);\n }\n }\n\n /**\n * @dev Return the log in base 2 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n */\n function log2(uint256 x) internal pure returns (uint256 r) {\n // If value has upper 128 bits set, log2 result is at least 128\n r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;\n // If upper 64 bits of 128-bit half set, add 64 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;\n // If upper 32 bits of 64-bit half set, add 32 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;\n // If upper 16 bits of 32-bit half set, add 16 to result\n r |= SafeCast.toUint((x >> r) > 0xffff) << 4;\n // If upper 8 bits of 16-bit half set, add 8 to result\n r |= SafeCast.toUint((x >> r) > 0xff) << 3;\n // If upper 4 bits of 8-bit half set, add 4 to result\n r |= SafeCast.toUint((x >> r) > 0xf) << 2;\n\n // Shifts value right by the current result and use it as an index into this lookup table:\n //\n // | x (4 bits) | index | table[index] = MSB position |\n // |------------|---------|-----------------------------|\n // | 0000 | 0 | table[0] = 0 |\n // | 0001 | 1 | table[1] = 0 |\n // | 0010 | 2 | table[2] = 1 |\n // | 0011 | 3 | table[3] = 1 |\n // | 0100 | 4 | table[4] = 2 |\n // | 0101 | 5 | table[5] = 2 |\n // | 0110 | 6 | table[6] = 2 |\n // | 0111 | 7 | table[7] = 2 |\n // | 1000 | 8 | table[8] = 3 |\n // | 1001 | 9 | table[9] = 3 |\n // | 1010 | 10 | table[10] = 3 |\n // | 1011 | 11 | table[11] = 3 |\n // | 1100 | 12 | table[12] = 3 |\n // | 1101 | 13 | table[13] = 3 |\n // | 1110 | 14 | table[14] = 3 |\n // | 1111 | 15 | table[15] = 3 |\n //\n // The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.\n assembly (\"memory-safe\") {\n r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))\n }\n }\n\n /**\n * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log2(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);\n }\n }\n\n /**\n * @dev Return the log in base 10 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n */\n function log10(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >= 10 ** 64) {\n value /= 10 ** 64;\n result += 64;\n }\n if (value >= 10 ** 32) {\n value /= 10 ** 32;\n result += 32;\n }\n if (value >= 10 ** 16) {\n value /= 10 ** 16;\n result += 16;\n }\n if (value >= 10 ** 8) {\n value /= 10 ** 8;\n result += 8;\n }\n if (value >= 10 ** 4) {\n value /= 10 ** 4;\n result += 4;\n }\n if (value >= 10 ** 2) {\n value /= 10 ** 2;\n result += 2;\n }\n if (value >= 10 ** 1) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log10(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);\n }\n }\n\n /**\n * @dev Return the log in base 256 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n *\n * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n */\n function log256(uint256 x) internal pure returns (uint256 r) {\n // If value has upper 128 bits set, log2 result is at least 128\n r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;\n // If upper 64 bits of 128-bit half set, add 64 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;\n // If upper 32 bits of 64-bit half set, add 32 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;\n // If upper 16 bits of 32-bit half set, add 16 to result\n r |= SafeCast.toUint((x >> r) > 0xffff) << 4;\n // Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8\n return (r >> 3) | SafeCast.toUint((x >> r) > 0xff);\n }\n\n /**\n * @dev Return the log in base 256, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log256(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);\n }\n }\n\n /**\n * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.\n */\n function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {\n return uint8(rounding) % 2 == 1;\n }\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)\n// This file was procedurally generated from scripts/generate/templates/SafeCast.js.\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n */\nlibrary SafeCast {\n /**\n * @dev Value doesn't fit in an uint of `bits` size.\n */\n error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);\n\n /**\n * @dev An int value doesn't fit in an uint of `bits` size.\n */\n error SafeCastOverflowedIntToUint(int256 value);\n\n /**\n * @dev Value doesn't fit in an int of `bits` size.\n */\n error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);\n\n /**\n * @dev An uint value doesn't fit in an int of `bits` size.\n */\n error SafeCastOverflowedUintToInt(uint256 value);\n\n /**\n * @dev Returns the downcasted uint248 from uint256, reverting on\n * overflow (when the input is greater than largest uint248).\n *\n * Counterpart to Solidity's `uint248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n */\n function toUint248(uint256 value) internal pure returns (uint248) {\n if (value > type(uint248).max) {\n revert SafeCastOverflowedUintDowncast(248, value);\n }\n return uint248(value);\n }\n\n /**\n * @dev Returns the downcasted uint240 from uint256, reverting on\n * overflow (when the input is greater than largest uint240).\n *\n * Counterpart to Solidity's `uint240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n */\n function toUint240(uint256 value) internal pure returns (uint240) {\n if (value > type(uint240).max) {\n revert SafeCastOverflowedUintDowncast(240, value);\n }\n return uint240(value);\n }\n\n /**\n * @dev Returns the downcasted uint232 from uint256, reverting on\n * overflow (when the input is greater than largest uint232).\n *\n * Counterpart to Solidity's `uint232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n */\n function toUint232(uint256 value) internal pure returns (uint232) {\n if (value > type(uint232).max) {\n revert SafeCastOverflowedUintDowncast(232, value);\n }\n return uint232(value);\n }\n\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n if (value > type(uint224).max) {\n revert SafeCastOverflowedUintDowncast(224, value);\n }\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint216 from uint256, reverting on\n * overflow (when the input is greater than largest uint216).\n *\n * Counterpart to Solidity's `uint216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n */\n function toUint216(uint256 value) internal pure returns (uint216) {\n if (value > type(uint216).max) {\n revert SafeCastOverflowedUintDowncast(216, value);\n }\n return uint216(value);\n }\n\n /**\n * @dev Returns the downcasted uint208 from uint256, reverting on\n * overflow (when the input is greater than largest uint208).\n *\n * Counterpart to Solidity's `uint208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n */\n function toUint208(uint256 value) internal pure returns (uint208) {\n if (value > type(uint208).max) {\n revert SafeCastOverflowedUintDowncast(208, value);\n }\n return uint208(value);\n }\n\n /**\n * @dev Returns the downcasted uint200 from uint256, reverting on\n * overflow (when the input is greater than largest uint200).\n *\n * Counterpart to Solidity's `uint200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n */\n function toUint200(uint256 value) internal pure returns (uint200) {\n if (value > type(uint200).max) {\n revert SafeCastOverflowedUintDowncast(200, value);\n }\n return uint200(value);\n }\n\n /**\n * @dev Returns the downcasted uint192 from uint256, reverting on\n * overflow (when the input is greater than largest uint192).\n *\n * Counterpart to Solidity's `uint192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n */\n function toUint192(uint256 value) internal pure returns (uint192) {\n if (value > type(uint192).max) {\n revert SafeCastOverflowedUintDowncast(192, value);\n }\n return uint192(value);\n }\n\n /**\n * @dev Returns the downcasted uint184 from uint256, reverting on\n * overflow (when the input is greater than largest uint184).\n *\n * Counterpart to Solidity's `uint184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n */\n function toUint184(uint256 value) internal pure returns (uint184) {\n if (value > type(uint184).max) {\n revert SafeCastOverflowedUintDowncast(184, value);\n }\n return uint184(value);\n }\n\n /**\n * @dev Returns the downcasted uint176 from uint256, reverting on\n * overflow (when the input is greater than largest uint176).\n *\n * Counterpart to Solidity's `uint176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n */\n function toUint176(uint256 value) internal pure returns (uint176) {\n if (value > type(uint176).max) {\n revert SafeCastOverflowedUintDowncast(176, value);\n }\n return uint176(value);\n }\n\n /**\n * @dev Returns the downcasted uint168 from uint256, reverting on\n * overflow (when the input is greater than largest uint168).\n *\n * Counterpart to Solidity's `uint168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n */\n function toUint168(uint256 value) internal pure returns (uint168) {\n if (value > type(uint168).max) {\n revert SafeCastOverflowedUintDowncast(168, value);\n }\n return uint168(value);\n }\n\n /**\n * @dev Returns the downcasted uint160 from uint256, reverting on\n * overflow (when the input is greater than largest uint160).\n *\n * Counterpart to Solidity's `uint160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n */\n function toUint160(uint256 value) internal pure returns (uint160) {\n if (value > type(uint160).max) {\n revert SafeCastOverflowedUintDowncast(160, value);\n }\n return uint160(value);\n }\n\n /**\n * @dev Returns the downcasted uint152 from uint256, reverting on\n * overflow (when the input is greater than largest uint152).\n *\n * Counterpart to Solidity's `uint152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n */\n function toUint152(uint256 value) internal pure returns (uint152) {\n if (value > type(uint152).max) {\n revert SafeCastOverflowedUintDowncast(152, value);\n }\n return uint152(value);\n }\n\n /**\n * @dev Returns the downcasted uint144 from uint256, reverting on\n * overflow (when the input is greater than largest uint144).\n *\n * Counterpart to Solidity's `uint144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n */\n function toUint144(uint256 value) internal pure returns (uint144) {\n if (value > type(uint144).max) {\n revert SafeCastOverflowedUintDowncast(144, value);\n }\n return uint144(value);\n }\n\n /**\n * @dev Returns the downcasted uint136 from uint256, reverting on\n * overflow (when the input is greater than largest uint136).\n *\n * Counterpart to Solidity's `uint136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n */\n function toUint136(uint256 value) internal pure returns (uint136) {\n if (value > type(uint136).max) {\n revert SafeCastOverflowedUintDowncast(136, value);\n }\n return uint136(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n if (value > type(uint128).max) {\n revert SafeCastOverflowedUintDowncast(128, value);\n }\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint120 from uint256, reverting on\n * overflow (when the input is greater than largest uint120).\n *\n * Counterpart to Solidity's `uint120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n */\n function toUint120(uint256 value) internal pure returns (uint120) {\n if (value > type(uint120).max) {\n revert SafeCastOverflowedUintDowncast(120, value);\n }\n return uint120(value);\n }\n\n /**\n * @dev Returns the downcasted uint112 from uint256, reverting on\n * overflow (when the input is greater than largest uint112).\n *\n * Counterpart to Solidity's `uint112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n */\n function toUint112(uint256 value) internal pure returns (uint112) {\n if (value > type(uint112).max) {\n revert SafeCastOverflowedUintDowncast(112, value);\n }\n return uint112(value);\n }\n\n /**\n * @dev Returns the downcasted uint104 from uint256, reverting on\n * overflow (when the input is greater than largest uint104).\n *\n * Counterpart to Solidity's `uint104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n */\n function toUint104(uint256 value) internal pure returns (uint104) {\n if (value > type(uint104).max) {\n revert SafeCastOverflowedUintDowncast(104, value);\n }\n return uint104(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n if (value > type(uint96).max) {\n revert SafeCastOverflowedUintDowncast(96, value);\n }\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint88 from uint256, reverting on\n * overflow (when the input is greater than largest uint88).\n *\n * Counterpart to Solidity's `uint88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n */\n function toUint88(uint256 value) internal pure returns (uint88) {\n if (value > type(uint88).max) {\n revert SafeCastOverflowedUintDowncast(88, value);\n }\n return uint88(value);\n }\n\n /**\n * @dev Returns the downcasted uint80 from uint256, reverting on\n * overflow (when the input is greater than largest uint80).\n *\n * Counterpart to Solidity's `uint80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n */\n function toUint80(uint256 value) internal pure returns (uint80) {\n if (value > type(uint80).max) {\n revert SafeCastOverflowedUintDowncast(80, value);\n }\n return uint80(value);\n }\n\n /**\n * @dev Returns the downcasted uint72 from uint256, reverting on\n * overflow (when the input is greater than largest uint72).\n *\n * Counterpart to Solidity's `uint72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n */\n function toUint72(uint256 value) internal pure returns (uint72) {\n if (value > type(uint72).max) {\n revert SafeCastOverflowedUintDowncast(72, value);\n }\n return uint72(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n if (value > type(uint64).max) {\n revert SafeCastOverflowedUintDowncast(64, value);\n }\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint56 from uint256, reverting on\n * overflow (when the input is greater than largest uint56).\n *\n * Counterpart to Solidity's `uint56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n */\n function toUint56(uint256 value) internal pure returns (uint56) {\n if (value > type(uint56).max) {\n revert SafeCastOverflowedUintDowncast(56, value);\n }\n return uint56(value);\n }\n\n /**\n * @dev Returns the downcasted uint48 from uint256, reverting on\n * overflow (when the input is greater than largest uint48).\n *\n * Counterpart to Solidity's `uint48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n */\n function toUint48(uint256 value) internal pure returns (uint48) {\n if (value > type(uint48).max) {\n revert SafeCastOverflowedUintDowncast(48, value);\n }\n return uint48(value);\n }\n\n /**\n * @dev Returns the downcasted uint40 from uint256, reverting on\n * overflow (when the input is greater than largest uint40).\n *\n * Counterpart to Solidity's `uint40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n */\n function toUint40(uint256 value) internal pure returns (uint40) {\n if (value > type(uint40).max) {\n revert SafeCastOverflowedUintDowncast(40, value);\n }\n return uint40(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n if (value > type(uint32).max) {\n revert SafeCastOverflowedUintDowncast(32, value);\n }\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint24 from uint256, reverting on\n * overflow (when the input is greater than largest uint24).\n *\n * Counterpart to Solidity's `uint24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n */\n function toUint24(uint256 value) internal pure returns (uint24) {\n if (value > type(uint24).max) {\n revert SafeCastOverflowedUintDowncast(24, value);\n }\n return uint24(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n if (value > type(uint16).max) {\n revert SafeCastOverflowedUintDowncast(16, value);\n }\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n if (value > type(uint8).max) {\n revert SafeCastOverflowedUintDowncast(8, value);\n }\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n if (value < 0) {\n revert SafeCastOverflowedIntToUint(value);\n }\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int248 from int256, reverting on\n * overflow (when the input is less than smallest int248 or\n * greater than largest int248).\n *\n * Counterpart to Solidity's `int248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n */\n function toInt248(int256 value) internal pure returns (int248 downcasted) {\n downcasted = int248(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(248, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int240 from int256, reverting on\n * overflow (when the input is less than smallest int240 or\n * greater than largest int240).\n *\n * Counterpart to Solidity's `int240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n */\n function toInt240(int256 value) internal pure returns (int240 downcasted) {\n downcasted = int240(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(240, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int232 from int256, reverting on\n * overflow (when the input is less than smallest int232 or\n * greater than largest int232).\n *\n * Counterpart to Solidity's `int232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n */\n function toInt232(int256 value) internal pure returns (int232 downcasted) {\n downcasted = int232(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(232, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int224 from int256, reverting on\n * overflow (when the input is less than smallest int224 or\n * greater than largest int224).\n *\n * Counterpart to Solidity's `int224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toInt224(int256 value) internal pure returns (int224 downcasted) {\n downcasted = int224(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(224, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int216 from int256, reverting on\n * overflow (when the input is less than smallest int216 or\n * greater than largest int216).\n *\n * Counterpart to Solidity's `int216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n */\n function toInt216(int256 value) internal pure returns (int216 downcasted) {\n downcasted = int216(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(216, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int208 from int256, reverting on\n * overflow (when the input is less than smallest int208 or\n * greater than largest int208).\n *\n * Counterpart to Solidity's `int208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n */\n function toInt208(int256 value) internal pure returns (int208 downcasted) {\n downcasted = int208(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(208, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int200 from int256, reverting on\n * overflow (when the input is less than smallest int200 or\n * greater than largest int200).\n *\n * Counterpart to Solidity's `int200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n */\n function toInt200(int256 value) internal pure returns (int200 downcasted) {\n downcasted = int200(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(200, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int192 from int256, reverting on\n * overflow (when the input is less than smallest int192 or\n * greater than largest int192).\n *\n * Counterpart to Solidity's `int192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n */\n function toInt192(int256 value) internal pure returns (int192 downcasted) {\n downcasted = int192(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(192, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int184 from int256, reverting on\n * overflow (when the input is less than smallest int184 or\n * greater than largest int184).\n *\n * Counterpart to Solidity's `int184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n */\n function toInt184(int256 value) internal pure returns (int184 downcasted) {\n downcasted = int184(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(184, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int176 from int256, reverting on\n * overflow (when the input is less than smallest int176 or\n * greater than largest int176).\n *\n * Counterpart to Solidity's `int176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n */\n function toInt176(int256 value) internal pure returns (int176 downcasted) {\n downcasted = int176(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(176, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int168 from int256, reverting on\n * overflow (when the input is less than smallest int168 or\n * greater than largest int168).\n *\n * Counterpart to Solidity's `int168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n */\n function toInt168(int256 value) internal pure returns (int168 downcasted) {\n downcasted = int168(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(168, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int160 from int256, reverting on\n * overflow (when the input is less than smallest int160 or\n * greater than largest int160).\n *\n * Counterpart to Solidity's `int160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n */\n function toInt160(int256 value) internal pure returns (int160 downcasted) {\n downcasted = int160(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(160, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int152 from int256, reverting on\n * overflow (when the input is less than smallest int152 or\n * greater than largest int152).\n *\n * Counterpart to Solidity's `int152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n */\n function toInt152(int256 value) internal pure returns (int152 downcasted) {\n downcasted = int152(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(152, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int144 from int256, reverting on\n * overflow (when the input is less than smallest int144 or\n * greater than largest int144).\n *\n * Counterpart to Solidity's `int144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n */\n function toInt144(int256 value) internal pure returns (int144 downcasted) {\n downcasted = int144(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(144, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int136 from int256, reverting on\n * overflow (when the input is less than smallest int136 or\n * greater than largest int136).\n *\n * Counterpart to Solidity's `int136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n */\n function toInt136(int256 value) internal pure returns (int136 downcasted) {\n downcasted = int136(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(136, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toInt128(int256 value) internal pure returns (int128 downcasted) {\n downcasted = int128(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(128, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int120 from int256, reverting on\n * overflow (when the input is less than smallest int120 or\n * greater than largest int120).\n *\n * Counterpart to Solidity's `int120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n */\n function toInt120(int256 value) internal pure returns (int120 downcasted) {\n downcasted = int120(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(120, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int112 from int256, reverting on\n * overflow (when the input is less than smallest int112 or\n * greater than largest int112).\n *\n * Counterpart to Solidity's `int112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n */\n function toInt112(int256 value) internal pure returns (int112 downcasted) {\n downcasted = int112(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(112, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int104 from int256, reverting on\n * overflow (when the input is less than smallest int104 or\n * greater than largest int104).\n *\n * Counterpart to Solidity's `int104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n */\n function toInt104(int256 value) internal pure returns (int104 downcasted) {\n downcasted = int104(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(104, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int96 from int256, reverting on\n * overflow (when the input is less than smallest int96 or\n * greater than largest int96).\n *\n * Counterpart to Solidity's `int96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toInt96(int256 value) internal pure returns (int96 downcasted) {\n downcasted = int96(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(96, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int88 from int256, reverting on\n * overflow (when the input is less than smallest int88 or\n * greater than largest int88).\n *\n * Counterpart to Solidity's `int88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n */\n function toInt88(int256 value) internal pure returns (int88 downcasted) {\n downcasted = int88(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(88, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int80 from int256, reverting on\n * overflow (when the input is less than smallest int80 or\n * greater than largest int80).\n *\n * Counterpart to Solidity's `int80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n */\n function toInt80(int256 value) internal pure returns (int80 downcasted) {\n downcasted = int80(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(80, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int72 from int256, reverting on\n * overflow (when the input is less than smallest int72 or\n * greater than largest int72).\n *\n * Counterpart to Solidity's `int72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n */\n function toInt72(int256 value) internal pure returns (int72 downcasted) {\n downcasted = int72(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(72, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toInt64(int256 value) internal pure returns (int64 downcasted) {\n downcasted = int64(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(64, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int56 from int256, reverting on\n * overflow (when the input is less than smallest int56 or\n * greater than largest int56).\n *\n * Counterpart to Solidity's `int56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n */\n function toInt56(int256 value) internal pure returns (int56 downcasted) {\n downcasted = int56(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(56, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int48 from int256, reverting on\n * overflow (when the input is less than smallest int48 or\n * greater than largest int48).\n *\n * Counterpart to Solidity's `int48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n */\n function toInt48(int256 value) internal pure returns (int48 downcasted) {\n downcasted = int48(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(48, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int40 from int256, reverting on\n * overflow (when the input is less than smallest int40 or\n * greater than largest int40).\n *\n * Counterpart to Solidity's `int40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n */\n function toInt40(int256 value) internal pure returns (int40 downcasted) {\n downcasted = int40(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(40, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toInt32(int256 value) internal pure returns (int32 downcasted) {\n downcasted = int32(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(32, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int24 from int256, reverting on\n * overflow (when the input is less than smallest int24 or\n * greater than largest int24).\n *\n * Counterpart to Solidity's `int24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n */\n function toInt24(int256 value) internal pure returns (int24 downcasted) {\n downcasted = int24(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(24, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toInt16(int256 value) internal pure returns (int16 downcasted) {\n downcasted = int16(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(16, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n */\n function toInt8(int256 value) internal pure returns (int8 downcasted) {\n downcasted = int8(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(8, value);\n }\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n if (value > uint256(type(int256).max)) {\n revert SafeCastOverflowedUintToInt(value);\n }\n return int256(value);\n }\n\n /**\n * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.\n */\n function toUint(bool b) internal pure returns (uint256 u) {\n assembly (\"memory-safe\") {\n u := iszero(iszero(b))\n }\n }\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/Panic.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Helper library for emitting standardized panic codes.\n *\n * ```solidity\n * contract Example {\n * using Panic for uint256;\n *\n * // Use any of the declared internal constants\n * function foo() { Panic.GENERIC.panic(); }\n *\n * // Alternatively\n * function foo() { Panic.panic(Panic.GENERIC); }\n * }\n * ```\n *\n * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].\n *\n * _Available since v5.1._\n */\n// slither-disable-next-line unused-state\nlibrary Panic {\n /// @dev generic / unspecified error\n uint256 internal constant GENERIC = 0x00;\n /// @dev used by the assert() builtin\n uint256 internal constant ASSERT = 0x01;\n /// @dev arithmetic underflow or overflow\n uint256 internal constant UNDER_OVERFLOW = 0x11;\n /// @dev division or modulo by zero\n uint256 internal constant DIVISION_BY_ZERO = 0x12;\n /// @dev enum conversion error\n uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;\n /// @dev invalid encoding in storage\n uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;\n /// @dev empty array pop\n uint256 internal constant EMPTY_ARRAY_POP = 0x31;\n /// @dev array out of bounds access\n uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;\n /// @dev resource error (too large allocation or too large array)\n uint256 internal constant RESOURCE_ERROR = 0x41;\n /// @dev calling invalid internal function\n uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;\n\n /// @dev Reverts with a panic code. Recommended to use with\n /// the internal constants with predefined codes.\n function panic(uint256 code) internal pure {\n assembly (\"memory-safe\") {\n mstore(0x00, 0x4e487b71)\n mstore(0x20, code)\n revert(0x1c, 0x24)\n }\n }\n}" + }, + "lib/rooster/v2-common/libraries/Constants.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\n// factory contraints on pools\nuint8 constant MAX_PROTOCOL_FEE_RATIO_D3 = 0.25e3; // 25%\nuint256 constant MAX_PROTOCOL_LENDING_FEE_RATE_D18 = 0.02e18; // 2%\nuint64 constant MAX_POOL_FEE_D18 = 0.9e18; // 90%\nuint64 constant MIN_LOOKBACK = 1 seconds;\n\n// pool constraints\nuint8 constant NUMBER_OF_KINDS = 4;\nint32 constant NUMBER_OF_KINDS_32 = int32(int8(NUMBER_OF_KINDS));\nuint256 constant MAX_TICK = 322_378; // max price 1e14 in D18 scale\nint32 constant MAX_TICK_32 = int32(int256(MAX_TICK));\nint32 constant MIN_TICK_32 = int32(-int256(MAX_TICK));\nuint256 constant MAX_BINS_TO_MERGE = 3;\nuint128 constant MINIMUM_LIQUIDITY = 1e8;\n\n// accessor named constants\nuint8 constant ALL_KINDS_MASK = 0xF; // 0b1111\nuint8 constant PERMISSIONED_LIQUIDITY_MASK = 0x10; // 0b010000\nuint8 constant PERMISSIONED_SWAP_MASK = 0x20; // 0b100000\nuint8 constant OPTIONS_MASK = ALL_KINDS_MASK | PERMISSIONED_LIQUIDITY_MASK | PERMISSIONED_SWAP_MASK; // 0b111111\n\n// named values\naddress constant MERGED_LP_BALANCE_ADDRESS = address(0);\nuint256 constant MERGED_LP_BALANCE_SUBACCOUNT = 0;\nuint128 constant ONE = 1e18;\nuint128 constant ONE_SQUARED = 1e36;\nint256 constant INT256_ONE = 1e18;\nuint256 constant ONE_D8 = 1e8;\nuint256 constant ONE_D3 = 1e3;\nint40 constant INT_ONE_D8 = 1e8;\nint40 constant HALF_TICK_D8 = 0.5e8;\nuint8 constant DEFAULT_DECIMALS = 18;\nuint256 constant DEFAULT_SCALE = 1;\nbytes constant EMPTY_PRICE_BREAKS = hex\"010000000000000000000000\";" + }, + "lib/rooster/v2-common/libraries/Math.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport {Math as OzMath} from \"../../openzeppelin-custom/contracts/utils/math/Math.sol\";\n\nimport {ONE, DEFAULT_SCALE, DEFAULT_DECIMALS, INT_ONE_D8, ONE_SQUARED} from \"./Constants.sol\";\n\n/**\n * @notice Math functions.\n */\nlibrary Math {\n /**\n * @notice Returns the lesser of two values.\n * @param x First uint256 value.\n * @param y Second uint256 value.\n */\n function min(uint256 x, uint256 y) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), lt(y, x)))\n }\n }\n\n /**\n * @notice Returns the lesser of two uint128 values.\n * @param x First uint128 value.\n * @param y Second uint128 value.\n */\n function min128(uint128 x, uint128 y) internal pure returns (uint128 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), lt(y, x)))\n }\n }\n\n /**\n * @notice Returns the lesser of two int256 values.\n * @param x First int256 value.\n * @param y Second int256 value.\n */\n function min(int256 x, int256 y) internal pure returns (int256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), slt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two uint256 values.\n * @param x First uint256 value.\n * @param y Second uint256 value.\n */\n function max(uint256 x, uint256 y) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), gt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two int256 values.\n * @param x First int256 value.\n * @param y Second int256 value.\n */\n function max(int256 x, int256 y) internal pure returns (int256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), sgt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two uint128 values.\n * @param x First uint128 value.\n * @param y Second uint128 value.\n */\n function max128(uint128 x, uint128 y) internal pure returns (uint128 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), gt(y, x)))\n }\n }\n\n /**\n * @notice Thresholds a value to be within the specified bounds.\n * @param value The value to bound.\n * @param lowerLimit The minimum allowable value.\n * @param upperLimit The maximum allowable value.\n */\n function boundValue(\n uint256 value,\n uint256 lowerLimit,\n uint256 upperLimit\n ) internal pure returns (uint256 outputValue) {\n outputValue = min(max(value, lowerLimit), upperLimit);\n }\n\n /**\n * @notice Returns the difference between two uint128 values or zero if the result would be negative.\n * @param x The minuend.\n * @param y The subtrahend.\n */\n function clip128(uint128 x, uint128 y) internal pure returns (uint128) {\n unchecked {\n return x < y ? 0 : x - y;\n }\n }\n\n /**\n * @notice Returns the difference between two uint256 values or zero if the result would be negative.\n * @param x The minuend.\n * @param y The subtrahend.\n */\n function clip(uint256 x, uint256 y) internal pure returns (uint256) {\n unchecked {\n return x < y ? 0 : x - y;\n }\n }\n\n /**\n * @notice Divides one uint256 by another, rounding down to the nearest\n * integer.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divFloor(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivFloor(x, ONE, y);\n }\n\n /**\n * @notice Divides one uint256 by another, rounding up to the nearest integer.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divCeil(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivCeil(x, ONE, y);\n }\n\n /**\n * @notice Multiplies two uint256 values and then divides by ONE, rounding down.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulFloor(uint256 x, uint256 y) internal pure returns (uint256) {\n return OzMath.mulDiv(x, y, ONE);\n }\n\n /**\n * @notice Multiplies two uint256 values and then divides by ONE, rounding up.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulCeil(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivCeil(x, y, ONE);\n }\n\n /**\n * @notice Calculates the multiplicative inverse of a uint256, rounding down.\n * @param x The value to invert.\n */\n function invFloor(uint256 x) internal pure returns (uint256) {\n unchecked {\n return ONE_SQUARED / x;\n }\n }\n\n /**\n * @notice Calculates the multiplicative inverse of a uint256, rounding up.\n * @param denominator The value to invert.\n */\n function invCeil(uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // divide z - 1 by the denominator and add 1.\n z := add(div(sub(ONE_SQUARED, 1), denominator), 1)\n }\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding down.\n * @param x The multiplicand.\n * @param y The multiplier.\n * @param k The divisor.\n */\n function mulDivFloor(uint256 x, uint256 y, uint256 k) internal pure returns (uint256 result) {\n result = OzMath.mulDiv(x, y, max(1, k));\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding up if there's a remainder.\n * @param x The multiplicand.\n * @param y The multiplier.\n * @param k The divisor.\n */\n function mulDivCeil(uint256 x, uint256 y, uint256 k) internal pure returns (uint256 result) {\n result = mulDivFloor(x, y, k);\n if (mulmod(x, y, max(1, k)) != 0) result = result + 1;\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding\n * down. Will revert if `x * y` is larger than `type(uint256).max`.\n * @param x The first operand for multiplication.\n * @param y The second operand for multiplication.\n * @param denominator The divisor after multiplication.\n */\n function mulDivDown(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // Store x * y in z for now.\n z := mul(x, y)\n if iszero(denominator) {\n denominator := 1\n }\n\n if iszero(or(iszero(x), eq(div(z, x), y))) {\n revert(0, 0)\n }\n\n // Divide z by the denominator.\n z := div(z, denominator)\n }\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding\n * up. Will revert if `x * y` is larger than `type(uint256).max`.\n * @param x The first operand for multiplication.\n * @param y The second operand for multiplication.\n * @param denominator The divisor after multiplication.\n */\n function mulDivUp(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // Store x * y in z for now.\n z := mul(x, y)\n if iszero(denominator) {\n denominator := 1\n }\n\n if iszero(or(iszero(x), eq(div(z, x), y))) {\n revert(0, 0)\n }\n\n // First, divide z - 1 by the denominator and add 1.\n // We allow z - 1 to underflow if z is 0, because we multiply the\n // end result by 0 if z is zero, ensuring we return 0 if z is zero.\n z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))\n }\n }\n\n /**\n * @notice Multiplies a uint256 by another and divides by a constant,\n * rounding down. Will revert if `x * y` is larger than\n * `type(uint256).max`.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulDown(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivDown(x, y, ONE);\n }\n\n /**\n * @notice Divides a uint256 by another, rounding down the result. Will\n * revert if `x * 1e18` is larger than `type(uint256).max`.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divDown(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivDown(x, ONE, y);\n }\n\n /**\n * @notice Divides a uint256 by another, rounding up the result. Will\n * revert if `x * 1e18` is larger than `type(uint256).max`.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divUp(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivUp(x, ONE, y);\n }\n\n /**\n * @notice Scales a number based on a difference in decimals from a default.\n * @param decimals The new decimal precision.\n */\n function scale(uint8 decimals) internal pure returns (uint256) {\n unchecked {\n if (decimals == DEFAULT_DECIMALS) {\n return DEFAULT_SCALE;\n } else {\n return 10 ** (DEFAULT_DECIMALS - decimals);\n }\n }\n }\n\n /**\n * @notice Adjusts a scaled amount to the token decimal scale.\n * @param amount The scaled amount.\n * @param scaleFactor The scaling factor to adjust by.\n * @param ceil Whether to round up (true) or down (false).\n */\n function ammScaleToTokenScale(uint256 amount, uint256 scaleFactor, bool ceil) internal pure returns (uint256 z) {\n unchecked {\n if (scaleFactor == DEFAULT_SCALE || amount == 0) {\n return amount;\n } else {\n if (!ceil) return amount / scaleFactor;\n assembly (\"memory-safe\") {\n z := add(div(sub(amount, 1), scaleFactor), 1)\n }\n }\n }\n }\n\n /**\n * @notice Adjusts a token amount to the D18 AMM scale.\n * @param amount The amount in token scale.\n * @param scaleFactor The scale factor for adjustment.\n */\n function tokenScaleToAmmScale(uint256 amount, uint256 scaleFactor) internal pure returns (uint256) {\n if (scaleFactor == DEFAULT_SCALE) {\n return amount;\n } else {\n return amount * scaleFactor;\n }\n }\n\n /**\n * @notice Returns the absolute value of a signed 32-bit integer.\n * @param x The integer to take the absolute value of.\n */\n function abs32(int32 x) internal pure returns (uint32) {\n unchecked {\n return uint32(x < 0 ? -x : x);\n }\n }\n\n /**\n * @notice Returns the absolute value of a signed 256-bit integer.\n * @param x The integer to take the absolute value of.\n */\n function abs(int256 x) internal pure returns (uint256) {\n unchecked {\n return uint256(x < 0 ? -x : x);\n }\n }\n\n /**\n * @notice Calculates the integer square root of a uint256 rounded down.\n * @param x The number to take the square root of.\n */\n function sqrt(uint256 x) internal pure returns (uint256 z) {\n // from https://github.com/transmissions11/solmate/blob/e8f96f25d48fe702117ce76c79228ca4f20206cb/src/utils/FixedPointMathLib.sol\n assembly (\"memory-safe\") {\n let y := x\n z := 181\n\n if iszero(lt(y, 0x10000000000000000000000000000000000)) {\n y := shr(128, y)\n z := shl(64, z)\n }\n if iszero(lt(y, 0x1000000000000000000)) {\n y := shr(64, y)\n z := shl(32, z)\n }\n if iszero(lt(y, 0x10000000000)) {\n y := shr(32, y)\n z := shl(16, z)\n }\n if iszero(lt(y, 0x1000000)) {\n y := shr(16, y)\n z := shl(8, z)\n }\n\n z := shr(18, mul(z, add(y, 65536)))\n\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n\n z := sub(z, lt(div(x, z), z))\n }\n }\n\n /**\n * @notice Computes the floor of a D8-scaled number as an int32, ignoring\n * potential overflow in the cast.\n * @param val The D8-scaled number.\n */\n function floorD8Unchecked(int256 val) internal pure returns (int32) {\n int32 val32;\n bool check;\n unchecked {\n val32 = int32(val / INT_ONE_D8);\n check = (val < 0 && val % INT_ONE_D8 != 0);\n }\n return check ? val32 - 1 : val32;\n }\n}" + }, + "lib/rooster/v2-common/libraries/TickMath.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport {Math as OzMath} from \"../../openzeppelin-custom/contracts/utils/math/Math.sol\";\nimport {Math} from \"./Math.sol\";\nimport {MAX_TICK, ONE} from \"./Constants.sol\";\n\n/**\n * @notice Math functions related to tick operations.\n */\n// slither-disable-start divide-before-multiply\nlibrary TickMath {\n using Math for uint256;\n\n error TickMaxExceeded(int256 tick);\n\n /**\n * @notice Compute the lower and upper sqrtPrice of a tick.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n */\n function tickSqrtPrices(\n uint256 tickSpacing,\n int32 _tick\n ) internal pure returns (uint256 sqrtLowerPrice, uint256 sqrtUpperPrice) {\n unchecked {\n sqrtLowerPrice = tickSqrtPrice(tickSpacing, _tick);\n sqrtUpperPrice = tickSqrtPrice(tickSpacing, _tick + 1);\n }\n }\n\n /**\n * @notice Compute the base tick value from the pool tick and the\n * tickSpacing. Revert if base tick is beyond the max tick boundary.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n */\n function subTickIndex(uint256 tickSpacing, int32 _tick) internal pure returns (uint32 subTick) {\n subTick = Math.abs32(_tick);\n subTick *= uint32(tickSpacing);\n if (subTick > MAX_TICK) {\n revert TickMaxExceeded(_tick);\n }\n }\n\n /**\n * @notice Calculate the square root price for a given tick and tick spacing.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n * @return _result The square root price.\n */\n function tickSqrtPrice(uint256 tickSpacing, int32 _tick) internal pure returns (uint256 _result) {\n unchecked {\n uint256 tick = subTickIndex(tickSpacing, _tick);\n\n uint256 ratio = tick & 0x1 != 0 ? 0xfffcb933bd6fad9d3af5f0b9f25db4d6 : 0x100000000000000000000000000000000;\n if (tick & 0x2 != 0) ratio = (ratio * 0xfff97272373d41fd789c8cb37ffcaa1c) >> 128;\n if (tick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656ac9229c67059486f389) >> 128;\n if (tick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e81259b3cddc7a064941) >> 128;\n if (tick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f67b19e8887e0bd251eb7) >> 128;\n if (tick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98cd2e57b660be99eb2c4a) >> 128;\n if (tick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c9838804e327cb417cafcb) >> 128;\n if (tick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99d51e2cc356c2f617dbe0) >> 128;\n if (tick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900aecf64236ab31f1f9dcb5) >> 128;\n if (tick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac4d9194200696907cf2e37) >> 128;\n if (tick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b88206f8abe8a3b44dd9be) >> 128;\n if (tick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c578ef4f1d17b2b235d480) >> 128;\n if (tick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd254ee83bdd3f248e7e785e) >> 128;\n if (tick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d8f7dd10e744d913d033333) >> 128;\n if (tick & 0x4000 != 0) ratio = (ratio * 0x70d869a156ddd32a39e257bc3f50aa9b) >> 128;\n if (tick & 0x8000 != 0) ratio = (ratio * 0x31be135f97da6e09a19dc367e3b6da40) >> 128;\n if (tick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7e5a9780b0cc4e25d61a56) >> 128;\n if (tick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedbcb3a6ccb7ce618d14225) >> 128;\n if (tick & 0x40000 != 0) ratio = (ratio * 0x2216e584f630389b2052b8db590e) >> 128;\n if (_tick > 0) ratio = type(uint256).max / ratio;\n _result = (ratio * ONE) >> 128;\n }\n }\n\n /**\n * @notice Calculate liquidity of a tick.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n */\n function getTickL(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n ) internal pure returns (uint256 liquidity) {\n // known:\n // - sqrt price values are different\n // - reserveA and reserveB fit in 128 bit\n // - sqrt price is in (1e-7, 1e7)\n // - D18 max for uint256 is 1.15e59\n // - D18 min is 1e-18\n\n unchecked {\n // diff is in (5e-12, 4e6); max tick spacing is 10_000\n uint256 diff = sqrtUpperTickPrice - sqrtLowerTickPrice;\n\n // Need to maximize precision by shifting small values A and B up so\n // that they use more of the available bit range. Two constraints to\n // consider: we need A * B * diff / sqrtPrice to be bigger than 1e-18\n // when the bump is not in play. This constrains the threshold for\n // bumping to be at least 77 bit; ie, either a or b needs 2^77 which\n // means that term A * B * diff / sqrtPrice > 1e-18.\n //\n // At the other end, the second constraint is that b^2 needs to fit in\n // a 256-bit number, so, post bump, the max reserve value needs to be\n // less than 6e22. With a 78-bit threshold and a 57-bit bump, we have A\n // and B are in (1.4e-1, 4.4e22 (2^(78+57))) with bump, and one of A or\n // B is at least 2^78 without the bump, but the other reserve value may\n // be as small as 1 wei.\n uint256 precisionBump = 0;\n if ((reserveA >> 78) == 0 && (reserveB >> 78) == 0) {\n precisionBump = 57;\n reserveA <<= precisionBump;\n reserveB <<= precisionBump;\n }\n\n if (reserveB == 0) return Math.divDown(reserveA, diff) >> precisionBump;\n if (reserveA == 0)\n return Math.mulDivDown(reserveB.mulDown(sqrtLowerTickPrice), sqrtUpperTickPrice, diff) >> precisionBump;\n\n // b is in (7.2e-9 (2^57 / 1e7 / 2), 2.8e29 (2^(78+57) * 1e7 / 2)) with bump\n // b is in a subset of the same range without bump\n uint256 b = (reserveA.divDown(sqrtUpperTickPrice) + reserveB.mulDown(sqrtLowerTickPrice)) >> 1;\n\n // b^2 is in (5.1e-17, 4.8e58); and will not overflow on either end;\n // A*B is in (3e-13 (2^78 / 1e18 * 1e-18), 1.9e45) without bump and is in a subset range with bump\n // A*B*diff/sqrtUpper is in (1.5e-17 (3e-13 * 5e-12 * 1e7), 7.6e58);\n\n // Since b^2 is at the upper edge of the precision range, we are not\n // able to multiply the argument of the sqrt by 1e18, instead, we move\n // this factor outside of the sqrt. The resulting loss of precision\n // means that this liquidity value is a lower bound on the tick\n // liquidity\n return\n OzMath.mulDiv(\n b +\n Math.sqrt(\n (OzMath.mulDiv(b, b, ONE) +\n OzMath.mulDiv(reserveB.mulFloor(reserveA), diff, sqrtUpperTickPrice))\n ) *\n 1e9,\n sqrtUpperTickPrice,\n diff\n ) >> precisionBump;\n }\n }\n\n /**\n * @notice Calculate square root price of a tick. Returns left edge of the\n * tick if the tick has no reserves.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n * @return sqrtPrice The calculated square root price.\n */\n function getSqrtPrice(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice,\n uint256 liquidity\n ) internal pure returns (uint256 sqrtPrice) {\n unchecked {\n if (reserveA == 0) {\n return sqrtLowerTickPrice;\n }\n if (reserveB == 0) {\n return sqrtUpperTickPrice;\n }\n sqrtPrice = Math.sqrt(\n ONE *\n (reserveA + liquidity.mulDown(sqrtLowerTickPrice)).divDown(\n reserveB + liquidity.divDown(sqrtUpperTickPrice)\n )\n );\n sqrtPrice = Math.boundValue(sqrtPrice, sqrtLowerTickPrice, sqrtUpperTickPrice);\n }\n }\n\n /**\n * @notice Calculate square root price of a tick. Returns left edge of the\n * tick if the tick has no reserves.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n * @return sqrtPrice The calculated square root price.\n * @return liquidity The calculated liquidity.\n */\n function getTickSqrtPriceAndL(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n ) internal pure returns (uint256 sqrtPrice, uint256 liquidity) {\n liquidity = getTickL(reserveA, reserveB, sqrtLowerTickPrice, sqrtUpperTickPrice);\n sqrtPrice = getSqrtPrice(reserveA, reserveB, sqrtLowerTickPrice, sqrtUpperTickPrice, liquidity);\n }\n}\n// slither-disable-end divide-before-multiply" + }, + "solidity-bytes-utils/contracts/BytesLib.sol": { + "content": "// SPDX-License-Identifier: Unlicense\n/*\n * @title Solidity Bytes Arrays Utils\n * @author Gonçalo Sá \n *\n * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.\n * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.\n */\npragma solidity >=0.8.0 <0.9.0;\n\n\nlibrary BytesLib {\n function concat(\n bytes memory _preBytes,\n bytes memory _postBytes\n )\n internal\n pure\n returns (bytes memory)\n {\n bytes memory tempBytes;\n\n assembly {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // Store the length of the first bytes array at the beginning of\n // the memory for tempBytes.\n let length := mload(_preBytes)\n mstore(tempBytes, length)\n\n // Maintain a memory counter for the current write location in the\n // temp bytes array by adding the 32 bytes for the array length to\n // the starting location.\n let mc := add(tempBytes, 0x20)\n // Stop copying when the memory counter reaches the length of the\n // first bytes array.\n let end := add(mc, length)\n\n for {\n // Initialize a copy counter to the start of the _preBytes data,\n // 32 bytes into its memory.\n let cc := add(_preBytes, 0x20)\n } lt(mc, end) {\n // Increase both counters by 32 bytes each iteration.\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // Write the _preBytes data into the tempBytes memory 32 bytes\n // at a time.\n mstore(mc, mload(cc))\n }\n\n // Add the length of _postBytes to the current length of tempBytes\n // and store it as the new length in the first 32 bytes of the\n // tempBytes memory.\n length := mload(_postBytes)\n mstore(tempBytes, add(length, mload(tempBytes)))\n\n // Move the memory counter back from a multiple of 0x20 to the\n // actual end of the _preBytes data.\n mc := end\n // Stop copying when the memory counter reaches the new combined\n // length of the arrays.\n end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n // Update the free-memory pointer by padding our last write location\n // to 32 bytes: add 31 bytes to the end of tempBytes to move to the\n // next 32 byte block, then round down to the nearest multiple of\n // 32. If the sum of the length of the two arrays is zero then add\n // one before rounding down to leave a blank 32 bytes (the length block with 0).\n mstore(0x40, and(\n add(add(end, iszero(add(length, mload(_preBytes)))), 31),\n not(31) // Round down to the nearest 32 bytes.\n ))\n }\n\n return tempBytes;\n }\n\n function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {\n assembly {\n // Read the first 32 bytes of _preBytes storage, which is the length\n // of the array. (We don't need to use the offset into the slot\n // because arrays use the entire slot.)\n let fslot := sload(_preBytes.slot)\n // Arrays of 31 bytes or less have an even value in their slot,\n // while longer arrays have an odd value. The actual length is\n // the slot divided by two for odd values, and the lowest order\n // byte divided by two for even values.\n // If the slot is even, bitwise and the slot with 255 and divide by\n // two to get the length. If the slot is odd, bitwise and the slot\n // with -1 and divide by two.\n let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)\n let mlength := mload(_postBytes)\n let newlength := add(slength, mlength)\n // slength can contain both the length and contents of the array\n // if length < 32 bytes so let's prepare for that\n // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage\n switch add(lt(slength, 32), lt(newlength, 32))\n case 2 {\n // Since the new array still fits in the slot, we just need to\n // update the contents of the slot.\n // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length\n sstore(\n _preBytes.slot,\n // all the modifications to the slot are inside this\n // next block\n add(\n // we can just add to the slot contents because the\n // bytes we want to change are the LSBs\n fslot,\n add(\n mul(\n div(\n // load the bytes from memory\n mload(add(_postBytes, 0x20)),\n // zero all bytes to the right\n exp(0x100, sub(32, mlength))\n ),\n // and now shift left the number of bytes to\n // leave space for the length in the slot\n exp(0x100, sub(32, newlength))\n ),\n // increase length by the double of the memory\n // bytes length\n mul(mlength, 2)\n )\n )\n )\n }\n case 1 {\n // The stored value fits in the slot, but the combined value\n // will exceed it.\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n let sc := add(keccak256(0x0, 0x20), div(slength, 32))\n\n // save new length\n sstore(_preBytes.slot, add(mul(newlength, 2), 1))\n\n // The contents of the _postBytes array start 32 bytes into\n // the structure. Our first read should obtain the `submod`\n // bytes that can fit into the unused space in the last word\n // of the stored array. To get this, we read 32 bytes starting\n // from `submod`, so the data we read overlaps with the array\n // contents by `submod` bytes. Masking the lowest-order\n // `submod` bytes allows us to add that value directly to the\n // stored value.\n\n let submod := sub(32, slength)\n let mc := add(_postBytes, submod)\n let end := add(_postBytes, mlength)\n let mask := sub(exp(0x100, submod), 1)\n\n sstore(\n sc,\n add(\n and(\n fslot,\n 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00\n ),\n and(mload(mc), mask)\n )\n )\n\n for {\n mc := add(mc, 0x20)\n sc := add(sc, 1)\n } lt(mc, end) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n sstore(sc, mload(mc))\n }\n\n mask := exp(0x100, sub(mc, end))\n\n sstore(sc, mul(div(mload(mc), mask), mask))\n }\n default {\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n // Start copying to the last used word of the stored array.\n let sc := add(keccak256(0x0, 0x20), div(slength, 32))\n\n // save new length\n sstore(_preBytes.slot, add(mul(newlength, 2), 1))\n\n // Copy over the first `submod` bytes of the new data as in\n // case 1 above.\n let slengthmod := mod(slength, 32)\n let mlengthmod := mod(mlength, 32)\n let submod := sub(32, slengthmod)\n let mc := add(_postBytes, submod)\n let end := add(_postBytes, mlength)\n let mask := sub(exp(0x100, submod), 1)\n\n sstore(sc, add(sload(sc), and(mload(mc), mask)))\n\n for {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } lt(mc, end) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n sstore(sc, mload(mc))\n }\n\n mask := exp(0x100, sub(mc, end))\n\n sstore(sc, mul(div(mload(mc), mask), mask))\n }\n }\n }\n\n function slice(\n bytes memory _bytes,\n uint256 _start,\n uint256 _length\n )\n internal\n pure\n returns (bytes memory)\n {\n // We're using the unchecked block below because otherwise execution ends \n // with the native overflow error code.\n unchecked {\n require(_length + 31 >= _length, \"slice_overflow\");\n }\n require(_bytes.length >= _start + _length, \"slice_outOfBounds\");\n\n bytes memory tempBytes;\n\n assembly {\n switch iszero(_length)\n case 0 {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // The first word of the slice result is potentially a partial\n // word read from the original array. To read it, we calculate\n // the length of that partial word and start copying that many\n // bytes into the array. The first word we copy will start with\n // data we don't care about, but the last `lengthmod` bytes will\n // land at the beginning of the contents of the new array. When\n // we're done copying, we overwrite the full first word with\n // the actual length of the slice.\n let lengthmod := and(_length, 31)\n\n // The multiplication in the next line is necessary\n // because when slicing multiples of 32 bytes (lengthmod == 0)\n // the following copy loop was copying the origin's length\n // and then ending prematurely not copying everything it should.\n let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))\n let end := add(mc, _length)\n\n for {\n // The multiplication in the next line has the same exact purpose\n // as the one above.\n let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n mstore(tempBytes, _length)\n\n //update free-memory pointer\n //allocating the array padded to 32 bytes like the compiler does now\n mstore(0x40, and(add(mc, 31), not(31)))\n }\n //if we want a zero-length slice let's just return a zero-length array\n default {\n tempBytes := mload(0x40)\n //zero out the 32 bytes slice we are about to return\n //we need to do it because Solidity does not garbage collect\n mstore(tempBytes, 0)\n\n mstore(0x40, add(tempBytes, 0x20))\n }\n }\n\n return tempBytes;\n }\n\n function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {\n require(_bytes.length >= _start + 20, \"toAddress_outOfBounds\");\n address tempAddress;\n\n assembly {\n tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)\n }\n\n return tempAddress;\n }\n\n function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {\n require(_bytes.length >= _start + 1 , \"toUint8_outOfBounds\");\n uint8 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x1), _start))\n }\n\n return tempUint;\n }\n\n function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {\n require(_bytes.length >= _start + 2, \"toUint16_outOfBounds\");\n uint16 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x2), _start))\n }\n\n return tempUint;\n }\n\n function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {\n require(_bytes.length >= _start + 4, \"toUint32_outOfBounds\");\n uint32 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x4), _start))\n }\n\n return tempUint;\n }\n\n function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {\n require(_bytes.length >= _start + 8, \"toUint64_outOfBounds\");\n uint64 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x8), _start))\n }\n\n return tempUint;\n }\n\n function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {\n require(_bytes.length >= _start + 12, \"toUint96_outOfBounds\");\n uint96 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0xc), _start))\n }\n\n return tempUint;\n }\n\n function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {\n require(_bytes.length >= _start + 16, \"toUint128_outOfBounds\");\n uint128 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x10), _start))\n }\n\n return tempUint;\n }\n\n function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {\n require(_bytes.length >= _start + 32, \"toUint256_outOfBounds\");\n uint256 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x20), _start))\n }\n\n return tempUint;\n }\n\n function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {\n require(_bytes.length >= _start + 32, \"toBytes32_outOfBounds\");\n bytes32 tempBytes32;\n\n assembly {\n tempBytes32 := mload(add(add(_bytes, 0x20), _start))\n }\n\n return tempBytes32;\n }\n\n function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {\n bool success = true;\n\n assembly {\n let length := mload(_preBytes)\n\n // if lengths don't match the arrays are not equal\n switch eq(length, mload(_postBytes))\n case 1 {\n // cb is a circuit breaker in the for loop since there's\n // no said feature for inline assembly loops\n // cb = 1 - don't breaker\n // cb = 0 - break\n let cb := 1\n\n let mc := add(_preBytes, 0x20)\n let end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n // the next line is the loop condition:\n // while(uint256(mc < end) + cb == 2)\n } eq(add(lt(mc, end), cb), 2) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // if any of these checks fails then arrays are not equal\n if iszero(eq(mload(mc), mload(cc))) {\n // unsuccess:\n success := 0\n cb := 0\n }\n }\n }\n default {\n // unsuccess:\n success := 0\n }\n }\n\n return success;\n }\n\n function equalStorage(\n bytes storage _preBytes,\n bytes memory _postBytes\n )\n internal\n view\n returns (bool)\n {\n bool success = true;\n\n assembly {\n // we know _preBytes_offset is 0\n let fslot := sload(_preBytes.slot)\n // Decode the length of the stored array like in concatStorage().\n let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)\n let mlength := mload(_postBytes)\n\n // if lengths don't match the arrays are not equal\n switch eq(slength, mlength)\n case 1 {\n // slength can contain both the length and contents of the array\n // if length < 32 bytes so let's prepare for that\n // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage\n if iszero(iszero(slength)) {\n switch lt(slength, 32)\n case 1 {\n // blank the last byte which is the length\n fslot := mul(div(fslot, 0x100), 0x100)\n\n if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {\n // unsuccess:\n success := 0\n }\n }\n default {\n // cb is a circuit breaker in the for loop since there's\n // no said feature for inline assembly loops\n // cb = 1 - don't breaker\n // cb = 0 - break\n let cb := 1\n\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n let sc := keccak256(0x0, 0x20)\n\n let mc := add(_postBytes, 0x20)\n let end := add(mc, mlength)\n\n // the next line is the loop condition:\n // while(uint256(mc < end) + cb == 2)\n for {} eq(add(lt(mc, end), cb), 2) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n if iszero(eq(sload(sc), mload(mc))) {\n // unsuccess:\n success := 0\n cb := 0\n }\n }\n }\n }\n }\n default {\n // unsuccess:\n success := 0\n }\n }\n\n return success;\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/06875a59a2af82155e02dd8c22ec498c.json b/contracts/deployments/hoodi/solcInputs/06875a59a2af82155e02dd8c22ec498c.json new file mode 100644 index 0000000000..ada0216f82 --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/06875a59a2af82155e02dd8c22ec498c.json @@ -0,0 +1,48 @@ +{ + "language": "Solidity", + "sources": { + "contracts/beacon/BeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"./BeaconProofsLib.sol\";\n\n/**\n * @title Verifies merkle proofs of beacon chain data.\n * @author Origin Protocol Inc\n */\ncontract BeaconProofs {\n /// @notice Verifies the validator public key to the beacon block root\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view {\n BeaconProofsLib.verifyValidatorPubkey(\n beaconBlockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n withdrawalAddress\n );\n }\n\n /// @notice Verifies the balances container to the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerRoot The merkle root of the the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata balancesContainerProof\n ) external view {\n BeaconProofsLib.verifyBalancesContainer(\n beaconBlockRoot,\n balancesContainerRoot,\n balancesContainerProof\n );\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param balanceProof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalanceGwei) {\n validatorBalanceGwei = BeaconProofsLib.verifyValidatorBalance(\n balancesContainerRoot,\n validatorBalanceLeaf,\n balanceProof,\n validatorIndex\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the slot of the first pending deposit to the beacon block root\n /// BeaconBlock.state.PendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero would be a good choice.\n /// @param firstPendingDepositSlotProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view returns (bool isEmptyDepositQueue) {\n isEmptyDepositQueue = BeaconProofsLib.verifyFirstPendingDepositSlot(\n beaconBlockRoot,\n slot,\n firstPendingDepositSlotProof\n );\n }\n}\n" + }, + "contracts/beacon/BeaconProofsLib.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Merkle } from \"./Merkle.sol\";\nimport { Endian } from \"./Endian.sol\";\n\n/**\n * @title Library to verify merkle proofs of beacon chain data.\n * @author Origin Protocol Inc\n */\nlibrary BeaconProofsLib {\n // Known generalized indices in the beacon block\n /// @dev BeaconBlock.slot\n uint256 internal constant SLOT_GENERALIZED_INDEX = 8;\n /// @dev BeaconBlock.state.PendingDeposits[0]\n uint256 internal constant FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX =\n 198105366528;\n /// @dev BeaconBlock.state.PendingDeposits[0].slot\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX =\n 1584842932228;\n /// @dev BeaconBlock.body.executionPayload.blockNumber\n uint256 internal constant BLOCK_NUMBER_GENERALIZED_INDEX = 6438;\n /// @dev BeaconBlock.state.validators\n uint256 internal constant VALIDATORS_CONTAINER_GENERALIZED_INDEX = 715;\n /// @dev BeaconBlock.state.balances\n uint256 internal constant BALANCES_CONTAINER_GENERALIZED_INDEX = 716;\n\n /// @dev Number of bytes in the proof to the first pending deposit.\n /// 37 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0]\n uint256 internal constant FIRST_PENDING_DEPOSIT_PROOF_LENGTH = 37 * 32;\n /// @dev Number of bytes in the proof to the slot of the first pending deposit.\n /// 40 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0].slot\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_PROOF_LENGTH = 40 * 32;\n\n /// @dev Merkle height of the Balances container\n /// BeaconBlock.state.balances\n uint256 internal constant BALANCES_HEIGHT = 39;\n /// @dev Merkle height of the Validators container\n /// BeaconBlock.state.validators\n uint256 internal constant VALIDATORS_HEIGHT = 41;\n /// @dev Merkle height of the Validator container\n /// BeaconBlock.state.validators[validatorIndex]\n uint256 internal constant VALIDATOR_HEIGHT = 3;\n\n /// @dev Position of the pubkey field in the Validator container.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;\n\n /// @notice Verifies the validator public key to the beacon block root\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n require(\n // 53 * 32 bytes = 1696 bytes\n validatorPubKeyProof.length == 1696,\n \"Invalid proof length\"\n );\n\n // BeaconBlock.state.validators[validatorIndex]\n uint256 generalizedIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_HEIGHT,\n validatorIndex\n );\n // BeaconBlock.state.validators[validatorIndex].pubkey\n generalizedIndex = concatGenIndices(\n generalizedIndex,\n VALIDATOR_HEIGHT,\n VALIDATOR_PUBKEY_INDEX\n );\n\n // Get the withdrawal address from the first witness in the pubkey merkle proof.\n address withdrawalAddressFromProof;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // The first 32 bytes of the proof is the withdrawal credential so load it into memory.\n calldatacopy(0, validatorPubKeyProof.offset, 32)\n // Cast the 32 bytes in memory to an address which is the last 20 bytes.\n withdrawalAddressFromProof := mload(0)\n }\n require(\n withdrawalAddressFromProof == withdrawalAddress,\n \"Invalid withdrawal address\"\n );\n\n require(\n Merkle.verifyInclusionSha256({\n proof: validatorPubKeyProof,\n root: beaconBlockRoot,\n leaf: pubKeyHash,\n index: generalizedIndex\n }),\n \"Invalid validator pubkey proof\"\n );\n }\n\n /// @notice Verifies the balances container to the beacon block root.\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param balancesContainerRoot The merkle root of the the balances container.\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata balancesContainerProof\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n require(\n // 9 * 32 bytes = 288 bytes\n balancesContainerProof.length == 288,\n \"Invalid proof length\"\n );\n\n // BeaconBlock.state.balances\n require(\n Merkle.verifyInclusionSha256({\n proof: balancesContainerProof,\n root: beaconBlockRoot,\n leaf: balancesContainerRoot,\n index: BALANCES_CONTAINER_GENERALIZED_INDEX\n }),\n \"Invalid balance container proof\"\n );\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param balanceProof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for.\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index.\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) internal view returns (uint256 validatorBalanceGwei) {\n require(balancesContainerRoot != bytes32(0), \"Invalid container root\");\n require(\n // 39 * 32 bytes = 1248 bytes\n balanceProof.length == 1248,\n \"Invalid proof length\"\n );\n\n // Four balances are stored in each leaf so the validator index is divided by 4\n uint64 balanceIndex = validatorIndex / 4;\n\n // Get the index within the balances container, not the Beacon Block\n // BeaconBlock.state.balances[balanceIndex]\n uint256 generalizedIndex = concatGenIndices(\n 1,\n BALANCES_HEIGHT,\n balanceIndex\n );\n\n validatorBalanceGwei = balanceAtIndex(\n validatorBalanceLeaf,\n validatorIndex\n );\n\n require(\n Merkle.verifyInclusionSha256({\n proof: balanceProof,\n root: balancesContainerRoot,\n leaf: validatorBalanceLeaf,\n index: generalizedIndex\n }),\n \"Invalid balance proof\"\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the slot of the first pending deposit to the beacon block root\n /// BeaconBlock.state.PendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero would be a good choice.\n /// @param firstPendingDepositSlotProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) internal view returns (bool isEmptyDepositQueue) {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n require(\n // 40 * 32 bytes = 1280 bytes\n firstPendingDepositSlotProof.length == 1280 ||\n // 37 * 32 bytes = 1184 bytes\n firstPendingDepositSlotProof.length == 1184,\n \"Invalid proof length\"\n );\n\n uint256 generalizedIndex;\n bytes32 leaf;\n // If the deposit queue is empty\n if (\n firstPendingDepositSlotProof.length ==\n FIRST_PENDING_DEPOSIT_PROOF_LENGTH\n ) {\n isEmptyDepositQueue = true;\n // use an empty leaf node as the root of the first pending deposit\n // when the deposit queue is empty\n leaf = bytes32(0);\n // BeaconBlock.state.PendingDeposits[0]\n generalizedIndex = FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX;\n } else if (\n firstPendingDepositSlotProof.length ==\n FIRST_PENDING_DEPOSIT_SLOT_PROOF_LENGTH\n ) {\n // Convert uint64 slot number to a little endian bytes32\n leaf = Endian.toLittleEndianUint64(slot);\n // BeaconBlock.state.PendingDeposits[0].slot\n generalizedIndex = FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX;\n } else {\n revert(\"Invalid proof length\");\n }\n\n require(\n Merkle.verifyInclusionSha256({\n proof: firstPendingDepositSlotProof,\n root: beaconBlockRoot,\n leaf: leaf,\n index: generalizedIndex\n }),\n \"Invalid pending deposit proof\"\n );\n }\n\n ////////////////////////////////////////////////////\n /// Internal Helper Functions\n ////////////////////////////////////////////////////\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n internal\n pure\n returns (uint256)\n {\n uint256 bitShiftAmount = (validatorIndex % 4) * 64;\n return\n Endian.fromLittleEndianUint64(\n bytes32((uint256(validatorBalanceLeaf) << bitShiftAmount))\n );\n }\n\n /// @notice Concatenates two beacon chain generalized indices into one.\n /// @param genIndex The first generalized index or 1 if calculating for a single container.\n /// @param height The merkle tree height of the second container. eg 39 for balances, 41 for validators.\n /// @param index The index within the second container. eg the validator index.\n /// @return genIndex The concatenated generalized index.\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) internal pure returns (uint256) {\n return (genIndex << height) | index;\n }\n}\n" + }, + "contracts/beacon/Endian.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n/**\n * @title Library to handle conversion between little-endian and big-endian formats.\n * @author Origin Protocol Inc\n */\nlibrary Endian {\n /**\n * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64\n * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type\n * @return n The big endian-formatted uint64\n * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits),\n * but it is immediately truncated to a uint64 (i.e. 64 bits)\n * through a right-shift/shr operation.\n */\n function fromLittleEndianUint64(bytes32 lenum)\n internal\n pure\n returns (uint64 n)\n {\n // the number needs to be stored in little-endian encoding (ie in bytes 0-8)\n n = uint64(uint256(lenum >> 192));\n // forgefmt: disable-next-item\n return\n (n >> 56) |\n ((0x00FF000000000000 & n) >> 40) |\n ((0x0000FF0000000000 & n) >> 24) |\n ((0x000000FF00000000 & n) >> 8) |\n ((0x00000000FF000000 & n) << 8) |\n ((0x0000000000FF0000 & n) << 24) |\n ((0x000000000000FF00 & n) << 40) |\n ((0x00000000000000FF & n) << 56);\n }\n\n function toLittleEndianUint64(uint64 benum)\n internal\n pure\n returns (bytes32 n)\n {\n // Convert to little-endian by reversing byte order\n uint64 reversed = (benum >> 56) |\n ((0x00FF000000000000 & benum) >> 40) |\n ((0x0000FF0000000000 & benum) >> 24) |\n ((0x000000FF00000000 & benum) >> 8) |\n ((0x00000000FF000000 & benum) << 8) |\n ((0x0000000000FF0000 & benum) << 24) |\n ((0x000000000000FF00 & benum) << 40) |\n ((0x00000000000000FF & benum) << 56);\n\n // Store the little-endian uint64 in the least significant 64 bits of bytes32\n n = bytes32(uint256(reversed));\n // Shift to most significant bits\n n = n << 192;\n }\n}\n" + }, + "contracts/beacon/Merkle.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev These functions deal with verification of Merkle Tree proofs.\n *\n * The tree and the proofs can be generated using our\n * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].\n * You will find a quickstart guide in the readme.\n *\n * WARNING: You should avoid using leaf values that are 64 bytes long prior to\n * hashing, or use a hash function other than keccak256 for hashing leaves.\n * This is because the concatenation of a sorted pair of internal nodes in\n * the merkle tree could be reinterpreted as a leaf value.\n * OpenZeppelin's JavaScript library generates merkle trees that are safe\n * against this attack out of the box.\n */\nlibrary Merkle {\n error InvalidProofLength();\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function verifyInclusionSha256(\n bytes memory proof,\n bytes32 root,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bool) {\n return processInclusionProofSha256(proof, leaf, index) == root;\n }\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * _Available since v4.4._\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function processInclusionProofSha256(\n bytes memory proof,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bytes32) {\n require(\n proof.length != 0 && proof.length % 32 == 0,\n InvalidProofLength()\n );\n bytes32[1] memory computedHash = [leaf];\n for (uint256 i = 32; i <= proof.length; i += 32) {\n if (index % 2 == 0) {\n // if ith bit of index is 0, then computedHash is a left sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(computedHash))\n mstore(0x20, mload(add(proof, i)))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n index := div(index, 2)\n }\n } else {\n // if ith bit of index is 1, then computedHash is a right sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(add(proof, i)))\n mstore(0x20, mload(computedHash))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n index := div(index, 2)\n }\n }\n }\n return computedHash[0];\n }\n}\n" + }, + "contracts/mocks/MockBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"../beacon/BeaconProofsLib.sol\";\nimport { BeaconProofs } from \"../beacon/BeaconProofs.sol\";\n\ncontract MockBeaconProofs is BeaconProofs {\n function concatGenIndices(\n uint256 index1,\n uint256 height2,\n uint256 index2\n ) external pure returns (uint256 genIndex) {\n return BeaconProofsLib.concatGenIndices(index1, height2, index2);\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/0aba374c50dae0e419bbff62c059ae0f.json b/contracts/deployments/hoodi/solcInputs/0aba374c50dae0e419bbff62c059ae0f.json new file mode 100644 index 0000000000..a449b696d5 --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/0aba374c50dae0e419bbff62c059ae0f.json @@ -0,0 +1,135 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/security/Pausable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/beacon/BeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"./BeaconProofsLib.sol\";\n\ncontract BeaconProofs {\n /// @notice Verifies the validator public key against the beacon block root\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash The beacon chain hash of the validator public key\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view {\n BeaconProofsLib.verifyValidatorPubkey(\n beaconBlockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n withdrawalAddress\n );\n }\n\n /// @notice Verifies the balances container against the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerLeaf The leaf node containing the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view {\n BeaconProofsLib.verifyBalancesContainer(\n beaconBlockRoot,\n balancesContainerLeaf,\n balancesContainerProof\n );\n }\n\n /// @notice Verifies the validator balance against the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances\n /// @param balanceProof The merkle proof for the validator balance against the root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalanceGwei) {\n validatorBalanceGwei = BeaconProofsLib.verifyValidatorBalance(\n balancesContainerRoot,\n validatorBalanceLeaf,\n balanceProof,\n validatorIndex\n );\n }\n\n /// @notice Verifies the slot of the first pending deposit against the beacon block root\n /// BeaconBlock.state.PendingDeposits[0].slot\n /// @param beaconBlockRoot The root of the beacon block\n /// @param slot The beacon chain slot to verify\n /// @param firstPendingDepositSlotProof The merkle proof for the first pending deposit's slot\n /// against the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view {\n BeaconProofsLib.verifyFirstPendingDepositSlot(\n beaconBlockRoot,\n slot,\n firstPendingDepositSlotProof\n );\n }\n}\n" + }, + "contracts/beacon/BeaconProofsLib.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Merkle } from \"./Merkle.sol\";\nimport { Endian } from \"./Endian.sol\";\n\nlibrary BeaconProofsLib {\n // Known generalized indices in the beacon block\n // BeaconBlock.slot\n uint256 internal constant SLOT_GENERALIZED_INDEX = 8;\n // BeaconBlock.state.PendingDeposits[0].slot\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX =\n 1584842932228;\n // BeaconBlock.body.executionPayload.blockNumber\n uint256 internal constant BLOCK_NUMBER_GENERALIZED_INDEX = 6438;\n // BeaconBlock.state.validators\n uint256 internal constant VALIDATORS_CONTAINER_GENERALIZED_INDEX = 715;\n // BeaconBlock.state.balances\n uint256 internal constant BALANCES_CONTAINER_GENERALIZED_INDEX = 716;\n\n // Beacon Container Tree Heights\n uint256 internal constant BALANCES_HEIGHT = 39;\n uint256 internal constant VALIDATORS_HEIGHT = 41;\n uint256 internal constant VALIDATOR_HEIGHT = 3;\n\n /// @notice Fields in the Validator container for phase 0\n /// See https://ethereum.github.io/consensus-specs/specs/phase0/beacon-chain/#validator\n uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;\n\n /// @notice Verifies the validator public key against the beacon block root\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash The beacon chain hash of the validator public key\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) internal view {\n // BeaconBlock.state.validators[validatorIndex].pubkey\n uint256 generalizedIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_HEIGHT,\n validatorIndex\n );\n generalizedIndex = concatGenIndices(\n generalizedIndex,\n VALIDATOR_HEIGHT,\n VALIDATOR_PUBKEY_INDEX\n );\n\n // Get the withdrawal address from the first witness in the pubkey merkle proof.\n address withdrawalAddressFromProof;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // The first 32 bytes of the proof is the withdrawal credential so load it into memory.\n calldatacopy(0, validatorPubKeyProof.offset, 32)\n // Cast the 32 bytes in memory to an address which is the last 20 bytes.\n withdrawalAddressFromProof := mload(0)\n }\n require(\n withdrawalAddressFromProof == withdrawalAddress,\n \"Invalid withdrawal address\"\n );\n\n require(\n Merkle.verifyInclusionSha256({\n proof: validatorPubKeyProof,\n root: beaconBlockRoot,\n leaf: pubKeyHash,\n index: generalizedIndex\n }),\n \"Invalid validator pubkey proof\"\n );\n }\n\n /// @notice Verifies the balances container against the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerRoot The merkle root of the the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata balancesContainerProof\n ) internal view {\n // BeaconBlock.state.balances\n require(\n Merkle.verifyInclusionSha256({\n proof: balancesContainerProof,\n root: beaconBlockRoot,\n leaf: balancesContainerRoot,\n index: BALANCES_CONTAINER_GENERALIZED_INDEX\n }),\n \"Invalid balance container proof\"\n );\n }\n\n /// @notice Verifies the validator balance against the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param balanceProof The merkle proof for the validator balance against the root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) internal view returns (uint256 validatorBalanceGwei) {\n // Four balances are stored in each leaf so the validator index is divided by 4\n uint64 balanceIndex = validatorIndex / 4;\n\n // Get the index within the balances container, not the Beacon Block\n // BeaconBlock.state.balances[balanceIndex]\n uint256 generalizedIndex = concatGenIndices(\n 1,\n BALANCES_HEIGHT,\n balanceIndex\n );\n\n validatorBalanceGwei = balanceAtIndex(\n validatorBalanceLeaf,\n validatorIndex\n );\n\n require(\n Merkle.verifyInclusionSha256({\n proof: balanceProof,\n root: balancesContainerRoot,\n leaf: validatorBalanceLeaf,\n index: generalizedIndex\n }),\n \"Invalid balance proof\"\n );\n }\n\n /// @notice Verifies the slot of the first pending deposit against the beacon block root\n /// BeaconBlock.state.PendingDeposits[0].slot\n /// @param beaconBlockRoot The root of the beacon block\n /// @param slot The beacon chain slot to verify\n /// @param firstPendingDepositSlotProof The merkle proof for the first pending deposit's slot\n /// against the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) internal view {\n // Convert uint64 slot number to a little endian bytes32\n bytes32 slotLeaf = Endian.toLittleEndianUint64(slot);\n\n require(\n Merkle.verifyInclusionSha256({\n proof: firstPendingDepositSlotProof,\n root: beaconBlockRoot,\n leaf: slotLeaf,\n index: FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX\n }),\n \"Invalid pending deposit proof\"\n );\n }\n\n ////////////////////////////////////////////////////\n /// Internal Helper Functions\n ////////////////////////////////////////////////////\n\n function balanceAtIndex(\n bytes32 validatorBalanceLeaf,\n uint64 validatorIndex\n ) internal pure returns (uint256) {\n uint256 bitShiftAmount = (validatorIndex % 4) * 64;\n return\n Endian.fromLittleEndianUint64(\n bytes32((uint256(validatorBalanceLeaf) << bitShiftAmount))\n );\n }\n\n /// @notice Concatenates two beacon chain generalized indices into one.\n /// @param genIndex The first generalized index or 1 if calculating for a single container.\n /// @param height The merkle tree height of the second container. eg 39 for balances, 41 for validators.\n /// @param index The index within the second container. eg the validator index.\n /// @return genIndex The concatenated generalized index.\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) internal pure returns (uint256) {\n return (genIndex << height) | index;\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the Beacon Block Root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Block Root Oracle to get the parent block root\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/Endian.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nlibrary Endian {\n /**\n * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64\n * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type\n * @return n The big endian-formatted uint64\n * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits),\n * but it is immediately truncated to a uint64 (i.e. 64 bits)\n * through a right-shift/shr operation.\n */\n function fromLittleEndianUint64(bytes32 lenum)\n internal\n pure\n returns (uint64 n)\n {\n // the number needs to be stored in little-endian encoding (ie in bytes 0-8)\n n = uint64(uint256(lenum >> 192));\n // forgefmt: disable-next-item\n return\n (n >> 56) |\n ((0x00FF000000000000 & n) >> 40) |\n ((0x0000FF0000000000 & n) >> 24) |\n ((0x000000FF00000000 & n) >> 8) |\n ((0x00000000FF000000 & n) << 8) |\n ((0x0000000000FF0000 & n) << 24) |\n ((0x000000000000FF00 & n) << 40) |\n ((0x00000000000000FF & n) << 56);\n }\n\n function toLittleEndianUint64(uint64 benum)\n internal\n pure\n returns (bytes32 n)\n {\n // Convert to little-endian by reversing byte order\n uint64 reversed = (benum >> 56) |\n ((0x00FF000000000000 & benum) >> 40) |\n ((0x0000FF0000000000 & benum) >> 24) |\n ((0x000000FF00000000 & benum) >> 8) |\n ((0x00000000FF000000 & benum) << 8) |\n ((0x0000000000FF0000 & benum) << 24) |\n ((0x000000000000FF00 & benum) << 40) |\n ((0x00000000000000FF & benum) << 56);\n\n // Store the little-endian uint64 in the least significant 64 bits of bytes32\n n = bytes32(uint256(reversed));\n // Shift to most significant bits\n n = n << 192;\n }\n}\n" + }, + "contracts/beacon/Merkle.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev These functions deal with verification of Merkle Tree proofs.\n *\n * The tree and the proofs can be generated using our\n * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].\n * You will find a quickstart guide in the readme.\n *\n * WARNING: You should avoid using leaf values that are 64 bytes long prior to\n * hashing, or use a hash function other than keccak256 for hashing leaves.\n * This is because the concatenation of a sorted pair of internal nodes in\n * the merkle tree could be reinterpreted as a leaf value.\n * OpenZeppelin's JavaScript library generates merkle trees that are safe\n * against this attack out of the box.\n */\nlibrary Merkle {\n error InvalidProofLength();\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function verifyInclusionSha256(\n bytes memory proof,\n bytes32 root,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bool) {\n return processInclusionProofSha256(proof, leaf, index) == root;\n }\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * _Available since v4.4._\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function processInclusionProofSha256(\n bytes memory proof,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bytes32) {\n require(\n proof.length != 0 && proof.length % 32 == 0,\n InvalidProofLength()\n );\n bytes32[1] memory computedHash = [leaf];\n for (uint256 i = 32; i <= proof.length; i += 32) {\n if (index % 2 == 0) {\n // if ith bit of index is 0, then computedHash is a left sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(computedHash))\n mstore(0x20, mload(add(proof, i)))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n index := div(index, 2)\n }\n } else {\n // if ith bit of index is 1, then computedHash is a right sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(add(proof, i)))\n mstore(0x20, mload(computedHash))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n index := div(index, 2)\n }\n }\n }\n return computedHash[0];\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconOracle {\n function blockToSlot(uint64 blockNumber) external returns (uint64 slot);\n\n function slotToBlock(uint64 slot) external returns (uint64 blockNumber);\n\n function slotToRoot(uint64 slot) external returns (bytes32 blockRoot);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n enum BalanceProofLevel {\n Container,\n BeaconBlock\n }\n\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view;\n\n function verifyBlockNumber(\n bytes32 beaconBlockRoot,\n uint256 blockNumber,\n bytes calldata blockNumberProof\n ) external view;\n\n function verifySlot(\n bytes32 beaconBlockRoot,\n uint256 slot,\n bytes calldata slotProof\n ) external view;\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n external\n pure\n returns (uint256);\n\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) external pure returns (uint256);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/mocks/MockBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"../beacon/BeaconProofsLib.sol\";\nimport { BeaconProofs } from \"../beacon/BeaconProofs.sol\";\n\ncontract MockBeaconProofs is BeaconProofs {\n function concatGenIndices(\n uint256 index1,\n uint256 height2,\n uint256 index2\n ) external pure returns (uint256 genIndex) {\n return BeaconProofsLib.concatGenIndices(index1, height2, index2);\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconProofs,\n uint64 _pectraForkTimestamp\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconProofs,\n _pectraForkTimestamp\n )\n {\n SSV_TOKEN = _ssvToken;\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract.\n /// @param _rewardTokenAddresses Address of reward token for platform\n /// @param _assets Addresses of initial supported assets\n /// @param _pTokens Platform Token corresponding addresses\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused for validator consolidation.\n /// @param _asset Address of asset to deposit. Has to be WETH.\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused for validator consolidation.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// Will revert if the strategy is paused for validator consolidation.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _asset, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(_asset, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n /// Will revert if the strategy is paused for validator consolidation.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, WETH, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Returns the last verified balance of validator deposits, validator balance,\n /// WETH and ETH in the strategy contract.\n /// @param _asset Address of weth asset\n /// @return balance Total value of (W)ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by strategy.\n /// @param _asset The address of the asset token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for deposits\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instread of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract,\n /// but so does withdrawals from validators. It's too complex to separate the two\n /// so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\nimport { IBeaconOracle } from \"../../interfaces/IBeaconOracle.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable {\n using SafeERC20 for IERC20;\n\n /// @notice The amount of ETH in wei that is required for a deposit to a new validator.\n /// Initially this is 32 ETH, but will be increased to 1 ETH after P2P's APIs have been updated\n /// to support deposits of 1 ETH.\n uint256 public constant DEPOSIT_AMOUNT_WEI = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n /// @notice The timestamp of the Beacon chain genesis.\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\n uint64 public immutable BEACON_GENESIS_TIMESTAMP;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// Deposit data for new compounding validators.\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountWei Amount of ETH in wei that has been deposited to the beacon chain deposit contract\n /// @param blockNumber Block number when the deposit was made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either PENDING or PROVEN\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint32 depositIndex;\n DepositStatus status;\n }\n /// @notice Mapping of the root of a deposit (depositDataRoot) to its data\n mapping(bytes32 => DepositData) public deposits;\n /// @notice List of deposit roots that are still to be verified as processed on the beacon chain\n bytes32[] public depositsRoots;\n\n // Validator data\n\n struct ValidatorData {\n bytes32 pubKeyHash; // Hash of the validator's public key using the Beacon Chain's format\n uint64 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes and indexes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n ValidatorData[] internal verifiedValidators;\n /// @notice State of the new compounding validators with a 0x02 withdrawal credential prefix.\n /// Uses the Beacon chain hashing for BLSPubkey which is\n /// sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => VALIDATOR_STATE) public validatorState;\n\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n uint64 timestamp;\n uint128 ethBalance;\n }\n // TODO is it more efficient to use the block root rather than hashing it?\n /// @notice Mapping of the block root to the balances at that slot\n mapping(bytes32 => Balances) public snappedBalances;\n uint64 public lastSnapTimestamp;\n uint128 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[50] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITED, // The validator has been verified to have a zero balance\n REMOVED // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified and is ready to be staked\n }\n\n event RegistratorChanged(address indexed newAddress);\n event SourceStrategyAdded(address indexed strategy);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n bytes32 indexed depositDataRoot,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint64 indexed validatorIndex\n );\n event DepositVerified(bytes32 indexed depositDataRoot, uint256 amountWei);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(\n uint256 indexed timestamp,\n bytes32 indexed blockRoot,\n uint256 ethBalance\n );\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 wethBalance,\n uint256 ethBalance\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_PROOFS = _beaconProofs;\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\n\n require(\n block.timestamp > _beaconGenesisTimestamp,\n \"Invalid genesis timestamp\"\n );\n }\n\n /***************************************\n Admin Functions\n ****************************************/\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /***************************************`\n Validator Management\n ****************************************/\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validatorState[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validator validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validator,\n uint64 depositAmountGwei\n ) external onlyRegistrator {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validator.pubkey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == VALIDATOR_STATE.REGISTERED ||\n currentState == VALIDATOR_STATE.VERIFIED),\n \"Not registered or verified\"\n );\n require(\n currentState == VALIDATOR_STATE.VERIFIED ||\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validator.pubkey,\n withdrawalCredentials,\n validator.signature,\n validator.depositDataRoot\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == VALIDATOR_STATE.REGISTERED) {\n validatorState[pubKeyHash] = VALIDATOR_STATE.STAKED;\n }\n\n /// After the Pectra upgrade the validators have a new restriction when proposing\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\n /// forward. Each slot is created at strict 12 second intervals and those slots can\n /// either have blocks attached to them or not. This way using the block.timestamp\n /// the slot number can easily be calculated.\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\n BEACON_GENESIS_TIMESTAMP) / 12;\n\n // Store the deposit data for verifyDeposit and verifyBalances\n deposits[validator.depositDataRoot] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n slot: depositSlot,\n depositIndex: SafeCast.toUint32(depositsRoots.length),\n status: DepositStatus.PENDING\n });\n depositsRoots.push(validator.depositDataRoot);\n\n emit ETHStaked(\n pubKeyHash,\n validator.depositDataRoot,\n validator.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then the request will fail.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(\n bytes calldata publicKey,\n uint64 amountGwei\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove a sweeping validator from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == VALIDATOR_STATE.EXITED ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exited\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validatorState[pubKeyHash] = VALIDATOR_STATE.REMOVED;\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /***************************************\n SSV Management\n ****************************************/\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n Beacon Chain Proofs\n ****************************************/\n\n /// @notice Verifies a validator's index to its public key.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param validatorPubKeyProof The merkle proof that the validator's index matches its public key\n /// on the beacon chain.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.STAKED,\n \"Validator not staked\"\n );\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key.\n // Also verify the validator's withdrawal credential points to this strategy.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n address(this) // Withdrawal address is this strategy\n );\n\n // Store the validator state as verified\n validatorState[pubKeyHash] = VALIDATOR_STATE.VERIFIED;\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(\n ValidatorData({ pubKeyHash: pubKeyHash, index: validatorIndex })\n );\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n ///\n /// Important: this function has a limitation where the `verificationSlot` that is passed by the off-chain\n /// verifier requires a slot immediately after it to propose a block otherwise the `BeaconRoots.parentBlockRoot`\n /// will fail. This shouldn't be a problem, since by the current behaviour of beacon chain only 1%-3% slots\n /// don't propose a block.\n /// @param depositDataRoot The root of the deposit data that was stored when\n /// the deposit was made on the execution layer.\n /// @param verificationSlot Any slot on or after the deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\n /// set for the next block timestamp in 12 seconds time.\n /// @param firstPendingDepositSlot The slot of the first pending deposit in the beacon chain\n /// @param firstPendingDepositSlotProof The merkle proof that the slot of the first pending deposit\n /// matches the beacon chain.\n /// BeaconBlock.BeaconBlockBody.deposits[0].slot\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n bytes32 depositDataRoot,\n uint64 verificationSlot,\n uint64 firstPendingDepositSlot,\n bytes calldata firstPendingDepositSlotProof\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositDataRoot];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n validatorState[deposit.pubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the first pending deposit's slot is 0 then there are no pending deposits so\n // our deposit must have been processed on the beacon chain.\n require(\n deposit.slot < firstPendingDepositSlot ||\n firstPendingDepositSlot == 0,\n \"Deposit not processed\"\n );\n\n // The verification slot must be after the deposit's slot.\n // This is needed for when the deposit queue is empty. That is, firstPendingDepositSlot is 0.\n require(deposit.slot < verificationSlot, \"Slot not after deposit\");\n\n // Calculate the next block timestamp from the verification slot.\n uint64 nextBlockTimestamp = 12 *\n verificationSlot +\n BEACON_GENESIS_TIMESTAMP +\n // Add 12 seconds for the next block\n 12;\n // Get the parent beacon block root of the next block which is the block root of the verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the slot of the first pending deposit matches the beacon chain\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n blockRoot,\n firstPendingDepositSlot,\n firstPendingDepositSlotProof\n );\n\n // After verifying the proof, update the contract storage\n deposits[depositDataRoot].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n bytes32 lastDepositDataRoot = depositsRoots[depositsRoots.length - 1];\n depositsRoots[deposit.depositIndex] = lastDepositDataRoot;\n deposits[lastDepositDataRoot].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositsRoots.pop();\n\n emit DepositVerified(\n depositDataRoot,\n uint256(deposit.amountGwei) * 1 gwei\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block.\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n /// Can only be called by the registrator.\n function snapBalances() external onlyRegistrator {\n _snapBalances();\n }\n\n function _snapBalances() internal {\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(\n SafeCast.toUint64(block.timestamp)\n );\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the balances in the mapping\n snappedBalances[blockRoot] = Balances({\n timestamp: SafeCast.toUint64(block.timestamp),\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n // Store the snapped timestamp\n lastSnapTimestamp = SafeCast.toUint64(block.timestamp);\n\n emit BalancesSnapped(block.timestamp, blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct VerifyBalancesParams {\n bytes32 blockRoot;\n uint64 firstPendingDepositSlot;\n // BeaconBlock.BeaconBlockBody.deposits[0].slot\n bytes firstPendingDepositSlotProof;\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(VerifyBalancesParams calldata params) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalances[params.blockRoot];\n // Check the balances are the latest\n require(lastSnapTimestamp > 0, \"No snapped balances\");\n require(balancesMem.timestamp == lastSnapTimestamp, \"Stale snap\");\n\n uint256 depositsCount = depositsRoots.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit to the beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n params.blockRoot,\n params.firstPendingDepositSlot,\n params.firstPendingDepositSlotProof\n );\n\n // For each native staking contract's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n bytes32 depositDataRoot = depositsRoots[i];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit slot is before the\n // slot of the staking strategy's deposit.\n // If the deposit has been processed, it will need to be verified with `verifyDeposit`\n require(\n params.firstPendingDepositSlot <\n deposits[depositDataRoot].slot,\n \"Deposit has been processed\"\n );\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei +=\n uint256(deposits[depositDataRoot].amountGwei) *\n 1 gwei;\n }\n }\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n params.validatorBalanceProofs.length == verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n params.validatorBalanceLeaves.length == verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n params.blockRoot,\n params.balancesContainerRoot,\n params.balancesContainerProof\n );\n\n // for each validator in reserve order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n params.balancesContainerRoot,\n params.validatorBalanceLeaves[i],\n params.validatorBalanceProofs[i],\n verifiedValidators[i].index\n );\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n validatorState[\n verifiedValidators[i].pubKeyHash\n ] = VALIDATOR_STATE.EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += uint256(validatorBalanceGwei) * 1 gwei;\n }\n }\n\n uint256 wethBalance = IWETH9(WETH).balanceOf(address(this));\n\n // Store the verified balance in storage\n lastVerifiedEthBalance = SafeCast.toUint128(\n totalDepositsWei + totalValidatorBalance + balancesMem.ethBalance\n );\n // Reset the last snap timestamp so a new snapBalances has to be made\n lastSnapTimestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n wethBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /***************************************\n WETH and ETH Accounting\n ****************************************/\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance\n if (lastVerifiedEthBalance > _ethAmount) {\n lastVerifiedEthBalance -= SafeCast.toUint128(_ethAmount);\n } else {\n // This can happen if all ETH in the validators was withdrawn\n // and there was more consensus rewards since the last balance verification.\n // Or it can happen if ETH is donated to this strategy.\n lastVerifiedEthBalance = 0;\n }\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += SafeCast.toUint128(_wethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /***************************************\n View Functions\n ****************************************/\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return An array of `ValidatorData` containing the public key hash and validator index.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorData[] memory)\n {\n return verifiedValidators;\n }\n\n /// @notice Returns the deposits that are still to be verified.\n /// These may or may not have been processed by the beacon chain.\n function getPendingDeposits()\n external\n view\n returns (DepositData[] memory pendingDeposits)\n {\n pendingDeposits = new DepositData[](depositsRoots.length);\n for (uint256 i = 0; i < depositsRoots.length; ++i) {\n pendingDeposits[i] = deposits[depositsRoots[i]];\n }\n }\n}\n" + }, + "contracts/strategies/NativeStaking/FeeAccumulator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\n/**\n * @title Fee Accumulator for Native Staking SSV Strategy\n * @notice Receives execution rewards which includes tx fees and\n * MEV rewards like tx priority and tx ordering.\n * It does NOT include swept ETH from beacon chain consensus rewards or full validator withdrawals.\n * @author Origin Protocol Inc\n */\ncontract FeeAccumulator {\n /// @notice The address of the Native Staking Strategy\n address public immutable STRATEGY;\n\n event ExecutionRewardsCollected(address indexed strategy, uint256 amount);\n\n /**\n * @param _strategy Address of the Native Staking Strategy\n */\n constructor(address _strategy) {\n STRATEGY = _strategy;\n }\n\n /**\n * @notice sends all ETH in this FeeAccumulator contract to the Native Staking Strategy.\n * @return eth The amount of execution rewards that were sent to the Native Staking Strategy\n */\n function collect() external returns (uint256 eth) {\n require(msg.sender == STRATEGY, \"Caller is not the Strategy\");\n\n eth = address(this).balance;\n if (eth > 0) {\n // Send the ETH to the Native Staking Strategy\n Address.sendValue(payable(STRATEGY), eth);\n\n emit ExecutionRewardsCollected(STRATEGY, eth);\n }\n }\n\n /**\n * @dev Accept ETH\n */\n receive() external payable {}\n}\n" + }, + "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { FeeAccumulator } from \"./FeeAccumulator.sol\";\nimport { ValidatorAccountant } from \"./ValidatorAccountant.sol\";\nimport { ISSVNetwork } from \"../../interfaces/ISSVNetwork.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/// @title Native Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract NativeStakingSSVStrategy is\n ValidatorAccountant,\n InitializableAbstractStrategy\n{\n using SafeERC20 for IERC20;\n\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n /// @notice Fee collector address\n /// @dev this address will receive maximal extractable value (MEV) rewards. These are\n /// rewards for arranging transactions in a way that benefits the validator.\n address payable public immutable FEE_ACCUMULATOR_ADDRESS;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[49] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n /// @param _feeAccumulator Address of the fee accumulator receiving execution layer validator rewards\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n uint256 _maxValidators,\n address _feeAccumulator,\n address _beaconChainDepositContract\n )\n InitializableAbstractStrategy(_baseConfig)\n ValidatorAccountant(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _maxValidators\n )\n {\n SSV_TOKEN = _ssvToken;\n FEE_ACCUMULATOR_ADDRESS = payable(_feeAccumulator);\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract.\n /// @param _rewardTokenAddresses Address of reward token for platform\n /// @param _assets Addresses of initial supported assets\n /// @param _pTokens Platform Token corresponding addresses\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n\n // Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\n FEE_ACCUMULATOR_ADDRESS\n );\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n /// @param _asset Address of asset to deposit. Has to be WETH.\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n depositedWethAccountedFor += _amount;\n _deposit(_asset, _amount);\n }\n\n /// @dev Deposit WETH to this strategy so it can later be staked into a validator.\n /// @param _asset Address of WETH\n /// @param _amount Amount of WETH to deposit\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n /*\n * We could do a check here that would revert when \"_amount % 32 ether != 0\". With the idea of\n * not allowing deposits that will result in WETH sitting on the strategy after all the possible batches\n * of 32ETH have been staked.\n * But someone could mess with our strategy by sending some WETH to it. And we might want to deposit just\n * enough WETH to add it up to 32 so it can be staked. For that reason the check is left out.\n *\n * WETH sitting on the strategy won't interfere with the accounting since accounting only operates on ETH.\n */\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n depositedWethAccountedFor = wethBalance;\n\n _deposit(WETH, newWeth);\n }\n }\n\n /// @notice Withdraw WETH from this contract. Used only if some WETH for is lingering on the contract.\n /// That can happen when:\n /// - after mints if the strategy is the default\n /// - time between depositToStrategy and stakeEth\n /// - the deposit was not a multiple of 32 WETH\n /// - someone sent WETH directly to this contract\n /// Will NOT revert if the strategy is paused from an accounting failure.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n _wethWithdrawn(_amount);\n\n IERC20(_asset).safeTransfer(_recipient, _amount);\n emit Withdrawal(_asset, address(0), _amount);\n }\n\n /// @notice transfer all WETH deposits back to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `exitSsvValidator` and `removeSsvValidator` operations.\n /// This does not withdraw any execution rewards from the FeeAccumulator or\n /// consensus rewards in this strategy.\n /// Any ETH in this strategy that was swept from a full validator withdrawal will not be withdrawn.\n /// ETH from full validator withdrawals is sent to the Vault using `doAccounting`.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n if (wethBalance > 0) {\n _withdraw(vaultAddress, WETH, wethBalance);\n }\n }\n\n /// @notice Returns the total value of (W)ETH that is staked to the validators\n /// and WETH deposits that are still to be staked.\n /// This does not include ETH from consensus rewards sitting in this strategy\n /// or ETH from MEV rewards in the FeeAccumulator. These rewards are harvested\n /// and sent to the Dripper so will eventually be sent to the Vault as WETH.\n /// @param _asset Address of weth asset\n /// @return balance Total value of (W)ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n balance =\n // add the ETH that has been staked in validators\n activeDepositedValidators *\n FULL_STAKE +\n // add the WETH in the strategy from deposits that are still to be staked\n IERC20(WETH).balanceOf(address(this));\n }\n\n function pause() external onlyStrategist {\n _pause();\n }\n\n /// @notice Returns bool indicating whether asset is supported by strategy.\n /// @param _asset The address of the asset token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for deposits\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\n function setFeeRecipient() external {\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\n FEE_ACCUMULATOR_ADDRESS\n );\n }\n\n /**\n * @notice Only accept ETH from the FeeAccumulator and the WETH contract - required when\n * unwrapping WETH just before staking it to the validator.\n * The strategy will also receive ETH from the priority fees of transactions when producing blocks\n * as defined in EIP-1559.\n * The tx fees come from the Beacon chain so do not need any EVM level permissions to receive ETH.\n * The tx fees are paid with each block produced. They are not included in the consensus rewards\n * which are periodically swept from the validators to this strategy.\n * For accounting purposes, the priority fees of transactions will be considered consensus rewards\n * and will be included in the AccountingConsensusRewards event.\n * @dev don't want to receive donations from anyone else as donations over the fuse limits will\n * mess with the accounting of the consensus rewards and validator full withdrawals.\n */\n receive() external payable {\n require(\n msg.sender == FEE_ACCUMULATOR_ADDRESS || msg.sender == WETH,\n \"Eth not from allowed contracts\"\n );\n }\n\n /***************************************\n Internal functions\n ****************************************/\n\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Convert accumulated ETH to WETH and send to the Harvester.\n /// Will revert if the strategy is paused for accounting.\n function _collectRewardTokens() internal override whenNotPaused {\n // collect ETH from execution rewards from the fee accumulator\n uint256 executionRewards = FeeAccumulator(FEE_ACCUMULATOR_ADDRESS)\n .collect();\n\n // total ETH rewards to be harvested = execution rewards + consensus rewards\n uint256 ethRewards = executionRewards + consensusRewards;\n\n require(\n address(this).balance >= ethRewards,\n \"Insufficient eth balance\"\n );\n\n if (ethRewards > 0) {\n // reset the counter keeping track of beacon chain consensus rewards\n consensusRewards = 0;\n\n // Convert ETH rewards to WETH\n IWETH9(WETH).deposit{ value: ethRewards }();\n\n IERC20(WETH).safeTransfer(harvesterAddress, ethRewards);\n emit RewardTokenCollected(harvesterAddress, WETH, ethRewards);\n }\n }\n\n /// @dev emits Withdrawal event from NativeStakingSSVStrategy\n function _wethWithdrawnToVault(uint256 _amount) internal override {\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _wethWithdrawn(uint256 _amount) internal override {\n /* In an ideal world we wouldn't need to reduce the deduction amount when the\n * depositedWethAccountedFor is smaller than the _amount.\n *\n * The reason this is required is that a malicious actor could sent WETH directly\n * to this contract and that would circumvent the increase of depositedWethAccountedFor\n * property. When the ETH would be staked the depositedWethAccountedFor amount could\n * be deducted so much that it would be negative.\n */\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n }\n}\n" + }, + "contracts/strategies/NativeStaking/ValidatorAccountant.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ValidatorRegistrator } from \"./ValidatorRegistrator.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\n\n/// @title Validator Accountant\n/// @notice Attributes the ETH swept from beacon chain validators to this strategy contract\n/// as either full or partial withdrawals. Partial withdrawals being consensus rewards.\n/// Full withdrawals are from exited validators.\n/// @author Origin Protocol Inc\nabstract contract ValidatorAccountant is ValidatorRegistrator {\n /// @notice The minimum amount of blocks that need to pass between two calls to manuallyFixAccounting\n uint256 public constant MIN_FIX_ACCOUNTING_CADENCE = 7200; // 1 day\n\n /// @notice Keeps track of the total consensus rewards swept from the beacon chain\n uint256 public consensusRewards;\n\n /// @notice start of fuse interval\n uint256 public fuseIntervalStart;\n /// @notice end of fuse interval\n uint256 public fuseIntervalEnd;\n /// @notice last block number manuallyFixAccounting has been called\n uint256 public lastFixAccountingBlockNumber;\n\n uint256[49] private __gap;\n\n event FuseIntervalUpdated(uint256 start, uint256 end);\n event AccountingFullyWithdrawnValidator(\n uint256 noOfValidators,\n uint256 remainingValidators,\n uint256 wethSentToVault\n );\n event AccountingValidatorSlashed(\n uint256 remainingValidators,\n uint256 wethSentToVault\n );\n event AccountingConsensusRewards(uint256 amount);\n\n event AccountingManuallyFixed(\n int256 validatorsDelta,\n int256 consensusRewardsDelta,\n uint256 wethToVault\n );\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n uint256 _maxValidators\n )\n ValidatorRegistrator(\n _wethAddress,\n _vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _maxValidators\n )\n {}\n\n /// @notice set fuse interval values\n function setFuseInterval(\n uint256 _fuseIntervalStart,\n uint256 _fuseIntervalEnd\n ) external onlyGovernor {\n require(\n _fuseIntervalStart < _fuseIntervalEnd &&\n _fuseIntervalEnd < 32 ether &&\n _fuseIntervalEnd - _fuseIntervalStart >= 4 ether,\n \"Incorrect fuse interval\"\n );\n\n fuseIntervalStart = _fuseIntervalStart;\n fuseIntervalEnd = _fuseIntervalEnd;\n\n emit FuseIntervalUpdated(_fuseIntervalStart, _fuseIntervalEnd);\n }\n\n /* solhint-disable max-line-length */\n /// This notion page offers a good explanation of how the accounting functions\n /// https://www.notion.so/originprotocol/Limited-simplified-native-staking-accounting-67a217c8420d40678eb943b9da0ee77d\n /// In short, after dividing by 32, if the ETH remaining on the contract falls between 0 and fuseIntervalStart,\n /// the accounting function will treat that ETH as Beacon chain consensus rewards.\n /// On the contrary, if after dividing by 32, the ETH remaining on the contract falls between fuseIntervalEnd and 32,\n /// the accounting function will treat that as a validator slashing.\n /// @notice Perform the accounting attributing beacon chain ETH to either full or partial withdrawals. Returns true when\n /// accounting is valid and fuse isn't \"blown\". Returns false when fuse is blown.\n /// @dev This function could in theory be permission-less but lets allow only the Registrator (Defender Action) to call it\n /// for now.\n /// @return accountingValid true if accounting was successful, false if fuse is blown\n /* solhint-enable max-line-length */\n function doAccounting()\n external\n onlyRegistrator\n whenNotPaused\n nonReentrant\n returns (bool accountingValid)\n {\n // pause the accounting on failure\n accountingValid = _doAccounting(true);\n }\n\n // slither-disable-start reentrancy-eth\n function _doAccounting(bool pauseOnFail)\n internal\n returns (bool accountingValid)\n {\n if (address(this).balance < consensusRewards) {\n return _failAccounting(pauseOnFail);\n }\n\n // Calculate all the new ETH that has been swept to the contract since the last accounting\n uint256 newSweptETH = address(this).balance - consensusRewards;\n accountingValid = true;\n\n // send the ETH that is from fully withdrawn validators to the Vault\n if (newSweptETH >= FULL_STAKE) {\n uint256 fullyWithdrawnValidators;\n // explicitly cast to uint256 as we want to round to a whole number of validators\n fullyWithdrawnValidators = uint256(newSweptETH / FULL_STAKE);\n activeDepositedValidators -= fullyWithdrawnValidators;\n\n uint256 wethToVault = FULL_STAKE * fullyWithdrawnValidators;\n IWETH9(WETH).deposit{ value: wethToVault }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, wethToVault);\n _wethWithdrawnToVault(wethToVault);\n\n emit AccountingFullyWithdrawnValidator(\n fullyWithdrawnValidators,\n activeDepositedValidators,\n wethToVault\n );\n }\n\n uint256 ethRemaining = address(this).balance - consensusRewards;\n // should be less than a whole validator stake\n require(ethRemaining < FULL_STAKE, \"Unexpected accounting\");\n\n // If no Beacon chain consensus rewards swept\n if (ethRemaining == 0) {\n // do nothing\n return accountingValid;\n } else if (ethRemaining < fuseIntervalStart) {\n // Beacon chain consensus rewards swept (partial validator withdrawals)\n // solhint-disable-next-line reentrancy\n consensusRewards += ethRemaining;\n emit AccountingConsensusRewards(ethRemaining);\n } else if (ethRemaining > fuseIntervalEnd) {\n // Beacon chain consensus rewards swept but also a slashed validator fully exited\n IWETH9(WETH).deposit{ value: ethRemaining }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, ethRemaining);\n activeDepositedValidators -= 1;\n\n _wethWithdrawnToVault(ethRemaining);\n\n emit AccountingValidatorSlashed(\n activeDepositedValidators,\n ethRemaining\n );\n }\n // Oh no... Fuse is blown. The Strategist needs to adjust the accounting values.\n else {\n return _failAccounting(pauseOnFail);\n }\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @dev pause any further accounting if required and return false\n function _failAccounting(bool pauseOnFail)\n internal\n returns (bool accountingValid)\n {\n // pause if not already\n if (pauseOnFail) {\n _pause();\n }\n // fail the accounting\n accountingValid = false;\n }\n\n /// @notice Allow the Strategist to fix the accounting of this strategy and unpause.\n /// @param _validatorsDelta adjust the active validators by up to plus three or minus three\n /// @param _consensusRewardsDelta adjust the accounted for consensus rewards up or down\n /// @param _ethToVaultAmount the amount of ETH that gets wrapped into WETH and sent to the Vault\n /// @dev There is a case when a validator(s) gets slashed so much that the eth swept from\n /// the beacon chain enters the fuse area and there are no consensus rewards on the contract\n /// to \"dip into\"/use. To increase the amount of unaccounted ETH over the fuse end interval\n /// we need to reduce the amount of active deposited validators and immediately send WETH\n /// to the vault, so it doesn't interfere with further accounting.\n function manuallyFixAccounting(\n int256 _validatorsDelta,\n int256 _consensusRewardsDelta,\n uint256 _ethToVaultAmount\n ) external onlyStrategist whenPaused nonReentrant {\n require(\n lastFixAccountingBlockNumber + MIN_FIX_ACCOUNTING_CADENCE <\n block.number,\n \"Fix accounting called too soon\"\n );\n require(\n _validatorsDelta >= -3 &&\n _validatorsDelta <= 3 &&\n // new value must be positive\n int256(activeDepositedValidators) + _validatorsDelta >= 0,\n \"Invalid validatorsDelta\"\n );\n require(\n _consensusRewardsDelta >= -332 ether &&\n _consensusRewardsDelta <= 332 ether &&\n // new value must be positive\n int256(consensusRewards) + _consensusRewardsDelta >= 0,\n \"Invalid consensusRewardsDelta\"\n );\n require(_ethToVaultAmount <= 32 ether * 3, \"Invalid wethToVaultAmount\");\n\n activeDepositedValidators = uint256(\n int256(activeDepositedValidators) + _validatorsDelta\n );\n consensusRewards = uint256(\n int256(consensusRewards) + _consensusRewardsDelta\n );\n lastFixAccountingBlockNumber = block.number;\n if (_ethToVaultAmount > 0) {\n IWETH9(WETH).deposit{ value: _ethToVaultAmount }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, _ethToVaultAmount);\n _wethWithdrawnToVault(_ethToVaultAmount);\n }\n\n emit AccountingManuallyFixed(\n _validatorsDelta,\n _consensusRewardsDelta,\n _ethToVaultAmount\n );\n\n // rerun the accounting to see if it has now been fixed.\n // Do not pause the accounting on failure as it is already paused\n require(_doAccounting(false), \"Fuse still blown\");\n\n // unpause since doAccounting was successful\n _unpause();\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n /// @dev allows for NativeStakingSSVStrategy contract to emit the Withdrawal event\n function _wethWithdrawnToVault(uint256 _amount) internal virtual;\n}\n" + }, + "contracts/strategies/NativeStaking/ValidatorRegistrator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Registrator of the validators\n * @notice This contract implements all the required functionality to register, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract ValidatorRegistrator is Governable, Pausable {\n /// @notice The maximum amount of ETH that can be staked by a validator\n /// @dev this can change in the future with EIP-7251, Increase the MAX_EFFECTIVE_BALANCE\n uint256 public constant FULL_STAKE = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Maximum number of validators that can be registered in this strategy\n uint256 public immutable MAX_VALIDATORS;\n\n /// @notice Address of the registrator - allowed to register, exit and remove validators\n address public validatorRegistrator;\n /// @notice The number of validators that have 32 (!) ETH actively deposited. When a new deposit\n /// to a validator happens this number increases, when a validator exit is detected this number\n /// decreases.\n uint256 public activeDepositedValidators;\n /// @notice State of the validators keccak256(pubKey) => state\n mapping(bytes32 => VALIDATOR_STATE) public validatorsStates;\n /// @notice The account that is allowed to modify stakeETHThreshold and reset stakeETHTally\n address public stakingMonitor;\n /// @notice Amount of ETH that can be staked before staking on the contract is suspended\n /// and the `stakingMonitor` needs to approve further staking by calling `resetStakeETHTally`\n uint256 public stakeETHThreshold;\n /// @notice Amount of ETH that has been staked since the `stakingMonitor` last called `resetStakeETHTally`.\n /// This can not go above `stakeETHThreshold`.\n uint256 public stakeETHTally;\n\n // For future use\n uint256[47] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n EXITING, // exit message has been posted and validator is in the process of exiting\n EXIT_COMPLETE // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event ETHStaked(bytes32 indexed pubKeyHash, bytes pubKey, uint256 amount);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event SSVValidatorExitInitiated(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event SSVValidatorExitCompleted(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event StakeETHThresholdChanged(uint256 amount);\n event StakeETHTallyReset();\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(\n msg.sender == validatorRegistrator,\n \"Caller is not the Registrator\"\n );\n _;\n }\n\n /// @dev Throws if called by any account other than the Staking monitor\n modifier onlyStakingMonitor() {\n require(msg.sender == stakingMonitor, \"Caller is not the Monitor\");\n _;\n }\n\n /// @dev Throws if called by any account other than the Strategist\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(VAULT_ADDRESS).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n uint256 _maxValidators\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n MAX_VALIDATORS = _maxValidators;\n }\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Set the address of the staking monitor that is allowed to reset stakeETHTally\n function setStakingMonitor(address _address) external onlyGovernor {\n stakingMonitor = _address;\n emit StakingMonitorChanged(_address);\n }\n\n /// @notice Set the amount of ETH that can be staked before staking monitor\n // needs to a approve further staking by resetting the stake ETH tally\n function setStakeETHThreshold(uint256 _amount) external onlyGovernor {\n stakeETHThreshold = _amount;\n emit StakeETHThresholdChanged(_amount);\n }\n\n /// @notice Reset the stakeETHTally\n function resetStakeETHTally() external onlyStakingMonitor {\n stakeETHTally = 0;\n emit StakeETHTallyReset();\n }\n\n /// @notice Stakes WETH to the node validators\n /// @param validators A list of validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n // slither-disable-start reentrancy-eth\n function stakeEth(ValidatorStakeData[] calldata validators)\n external\n onlyRegistrator\n whenNotPaused\n nonReentrant\n {\n uint256 requiredETH = validators.length * FULL_STAKE;\n\n // Check there is enough WETH from the deposits sitting in this strategy contract\n require(\n requiredETH <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(\n activeDepositedValidators + validators.length <= MAX_VALIDATORS,\n \"Max validators reached\"\n );\n\n require(\n stakeETHTally + requiredETH <= stakeETHThreshold,\n \"Staking ETH over threshold\"\n );\n stakeETHTally += requiredETH;\n\n // Convert required ETH from WETH\n IWETH9(WETH).withdraw(requiredETH);\n _wethWithdrawn(requiredETH);\n\n /* 0x01 to indicate that withdrawal credentials will contain an EOA address that the sweeping function\n * can sweep funds to.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x01),\n bytes11(0),\n address(this)\n );\n\n // For each validator\n for (uint256 i = 0; i < validators.length; ++i) {\n bytes32 pubKeyHash = keccak256(validators[i].pubkey);\n\n require(\n validatorsStates[pubKeyHash] == VALIDATOR_STATE.REGISTERED,\n \"Validator not registered\"\n );\n\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: FULL_STAKE\n }(\n validators[i].pubkey,\n withdrawalCredentials,\n validators[i].signature,\n validators[i].depositDataRoot\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.STAKED;\n\n emit ETHStaked(pubKeyHash, validators[i].pubkey, FULL_STAKE);\n }\n // save gas by changing this storage variable only once rather each time in the loop.\n activeDepositedValidators += validators.length;\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Registers a new validator in the SSV Cluster.\n /// Only the registrator can call this function.\n /// @param publicKeys The public keys of the validators\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for each validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n require(\n publicKeys.length == sharesData.length,\n \"Pubkey sharesData mismatch\"\n );\n // Check each public key has not already been used\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n emit SSVValidatorRegistered(pubKeyHash, publicKeys[i], operatorIds);\n }\n\n ISSVNetwork(SSV_NETWORK).bulkRegisterValidator(\n publicKeys,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n }\n\n /// @notice Exit validators from the Beacon chain.\n /// The staked ETH will eventually swept to this native staking strategy.\n /// Only the registrator can call this function.\n /// @param publicKeys List of SSV validator public keys\n /// @param operatorIds The operator IDs of the SSV Cluster\n // slither-disable-start reentrancy-no-eth\n function exitSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external onlyRegistrator whenNotPaused nonReentrant {\n ISSVNetwork(SSV_NETWORK).bulkExitValidator(publicKeys, operatorIds);\n\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n\n // Check each validator has not already been staked.\n // This would normally be done before the external call but is after\n // so only one for loop of the validators is needed.\n require(\n currentState == VALIDATOR_STATE.STAKED,\n \"Validator not staked\"\n );\n\n // Store the new validator state\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXITING;\n\n emit SSVValidatorExitInitiated(\n pubKeyHash,\n publicKeys[i],\n operatorIds\n );\n }\n }\n\n /// @notice Remove validators from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKeys List of SSV validator public keys\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function removeSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused nonReentrant {\n ISSVNetwork(SSV_NETWORK).bulkRemoveValidator(\n publicKeys,\n operatorIds,\n cluster\n );\n\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n\n // Check each validator is either registered or exited.\n // This would normally be done before the external call but is after\n // so only one for loop of the validators is needed.\n require(\n currentState == VALIDATOR_STATE.EXITING ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exiting\"\n );\n\n // Store the new validator state\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXIT_COMPLETE;\n\n emit SSVValidatorExitCompleted(\n pubKeyHash,\n publicKeys[i],\n operatorIds\n );\n }\n }\n\n /// @notice Deposits more SSV Tokens to the SSV Network contract which is used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// uses \"onlyStrategist\" modifier so continuous front-running can't DOS our maintenance service\n /// that tries to top up SSV tokens.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function depositSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyStrategist {\n ISSVNetwork(SSV_NETWORK).deposit(\n address(this),\n operatorIds,\n ssvAmount,\n cluster\n );\n }\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _wethWithdrawn(uint256 _amount) internal virtual;\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/0e4db469ee96659b1c00ca00b9b8648c.json b/contracts/deployments/hoodi/solcInputs/0e4db469ee96659b1c00ca00b9b8648c.json new file mode 100644 index 0000000000..8ca05a6d5d --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/0e4db469ee96659b1c00ca00b9b8648c.json @@ -0,0 +1,108 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/security/Pausable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/beacon/BeaconConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary BeaconConsolidation {\n /// @notice The address the validator consolidation requests are sent\n /// See https://eips.ethereum.org/EIPS/eip-7251\n address internal constant CONSOLIDATION_REQUEST_ADDRESS =\n 0x0000BBdDc7CE488642fb579F8B00f3a590007251;\n\n function request(bytes calldata source, bytes calldata target)\n internal\n returns (uint256 fee_)\n {\n require(source.length == 48, \"Invalid source byte length\");\n require(target.length == 48, \"Invalid target byte length\");\n\n fee_ = fee();\n\n // Call the Consolidation Request contract with the public keys of the source and target\n // validators packed together.\n // This does not have a function signature, so we use a call\n (bool success, ) = CONSOLIDATION_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(source, target)\n );\n\n require(success, \"Consolidation request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the consolidation request contract\n (bool success, bytes memory result) = CONSOLIDATION_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IConsolidation.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IConsolidationSource {\n function confirmConsolidation()\n external\n returns (uint256 consolidationCount);\n}\n\ninterface IConsolidationTarget {\n function requestConsolidation(\n bytes32 lastSourcePubKeyHash,\n bytes32 targetPubKeyHash\n ) external;\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/strategies/NativeStaking/FeeAccumulator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\n/**\n * @title Fee Accumulator for Native Staking SSV Strategy\n * @notice Receives execution rewards which includes tx fees and\n * MEV rewards like tx priority and tx ordering.\n * It does NOT include swept ETH from beacon chain consensus rewards or full validator withdrawals.\n * @author Origin Protocol Inc\n */\ncontract FeeAccumulator {\n /// @notice The address of the Native Staking Strategy\n address public immutable STRATEGY;\n\n event ExecutionRewardsCollected(address indexed strategy, uint256 amount);\n\n /**\n * @param _strategy Address of the Native Staking Strategy\n */\n constructor(address _strategy) {\n STRATEGY = _strategy;\n }\n\n /**\n * @notice sends all ETH in this FeeAccumulator contract to the Native Staking Strategy.\n * @return eth The amount of execution rewards that were sent to the Native Staking Strategy\n */\n function collect() external returns (uint256 eth) {\n require(msg.sender == STRATEGY, \"Caller is not the Strategy\");\n\n eth = address(this).balance;\n if (eth > 0) {\n // Send the ETH to the Native Staking Strategy\n Address.sendValue(payable(STRATEGY), eth);\n\n emit ExecutionRewardsCollected(STRATEGY, eth);\n }\n }\n\n /**\n * @dev Accept ETH\n */\n receive() external payable {}\n}\n" + }, + "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { FeeAccumulator } from \"./FeeAccumulator.sol\";\nimport { ValidatorAccountant } from \"./ValidatorAccountant.sol\";\nimport { ISSVNetwork } from \"../../interfaces/ISSVNetwork.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/// @title Native Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract NativeStakingSSVStrategy is\n ValidatorAccountant,\n InitializableAbstractStrategy\n{\n using SafeERC20 for IERC20;\n\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n /// @notice Fee collector address\n /// @dev this address will receive maximal extractable value (MEV) rewards. These are\n /// rewards for arranging transactions in a way that benefits the validator.\n address payable public immutable FEE_ACCUMULATOR_ADDRESS;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[49] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n /// @param _feeAccumulator Address of the fee accumulator receiving execution layer validator rewards\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n uint256 _maxValidators,\n address _feeAccumulator,\n address _beaconChainDepositContract\n )\n InitializableAbstractStrategy(_baseConfig)\n ValidatorAccountant(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _maxValidators\n )\n {\n SSV_TOKEN = _ssvToken;\n FEE_ACCUMULATOR_ADDRESS = payable(_feeAccumulator);\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract.\n /// @param _rewardTokenAddresses Address of reward token for platform\n /// @param _assets Addresses of initial supported assets\n /// @param _pTokens Platform Token corresponding addresses\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n\n // Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\n FEE_ACCUMULATOR_ADDRESS\n );\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n /// @param _asset Address of asset to deposit. Has to be WETH.\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n depositedWethAccountedFor += _amount;\n _deposit(_asset, _amount);\n }\n\n /// @dev Deposit WETH to this strategy so it can later be staked into a validator.\n /// @param _asset Address of WETH\n /// @param _amount Amount of WETH to deposit\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n /*\n * We could do a check here that would revert when \"_amount % 32 ether != 0\". With the idea of\n * not allowing deposits that will result in WETH sitting on the strategy after all the possible batches\n * of 32ETH have been staked.\n * But someone could mess with our strategy by sending some WETH to it. And we might want to deposit just\n * enough WETH to add it up to 32 so it can be staked. For that reason the check is left out.\n *\n * WETH sitting on the strategy won't interfere with the accounting since accounting only operates on ETH.\n */\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n depositedWethAccountedFor = wethBalance;\n\n _deposit(WETH, newWeth);\n }\n }\n\n /// @notice Withdraw WETH from this contract. Used only if some WETH for is lingering on the contract.\n /// That can happen when:\n /// - after mints if the strategy is the default\n /// - time between depositToStrategy and stakeEth\n /// - the deposit was not a multiple of 32 WETH\n /// - someone sent WETH directly to this contract\n /// Will NOT revert if the strategy is paused from an accounting failure.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n _wethWithdrawn(_amount);\n\n IERC20(_asset).safeTransfer(_recipient, _amount);\n emit Withdrawal(_asset, address(0), _amount);\n }\n\n /// @notice transfer all WETH deposits back to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `exitSsvValidator` and `removeSsvValidator` operations.\n /// This does not withdraw any execution rewards from the FeeAccumulator or\n /// consensus rewards in this strategy.\n /// Any ETH in this strategy that was swept from a full validator withdrawal will not be withdrawn.\n /// ETH from full validator withdrawals is sent to the Vault using `doAccounting`.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n if (wethBalance > 0) {\n _withdraw(vaultAddress, WETH, wethBalance);\n }\n }\n\n /// @notice Returns the total value of (W)ETH that is staked to the validators\n /// and WETH deposits that are still to be staked.\n /// This does not include ETH from consensus rewards sitting in this strategy\n /// or ETH from MEV rewards in the FeeAccumulator. These rewards are harvested\n /// and sent to the Dripper so will eventually be sent to the Vault as WETH.\n /// @param _asset Address of weth asset\n /// @return balance Total value of (W)ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n balance =\n // add the ETH that has been staked in validators\n activeDepositedValidators *\n FULL_STAKE +\n // add the WETH in the strategy from deposits that are still to be staked\n IERC20(WETH).balanceOf(address(this));\n }\n\n function pause() external onlyStrategist {\n _pause();\n }\n\n /// @notice Returns bool indicating whether asset is supported by strategy.\n /// @param _asset The address of the asset token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for deposits\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\n function setFeeRecipient() external {\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\n FEE_ACCUMULATOR_ADDRESS\n );\n }\n\n /**\n * @notice Only accept ETH from the FeeAccumulator and the WETH contract - required when\n * unwrapping WETH just before staking it to the validator.\n * The strategy will also receive ETH from the priority fees of transactions when producing blocks\n * as defined in EIP-1559.\n * The tx fees come from the Beacon chain so do not need any EVM level permissions to receive ETH.\n * The tx fees are paid with each block produced. They are not included in the consensus rewards\n * which are periodically swept from the validators to this strategy.\n * For accounting purposes, the priority fees of transactions will be considered consensus rewards\n * and will be included in the AccountingConsensusRewards event.\n * @dev don't want to receive donations from anyone else as donations over the fuse limits will\n * mess with the accounting of the consensus rewards and validator full withdrawals.\n */\n receive() external payable {\n require(\n msg.sender == FEE_ACCUMULATOR_ADDRESS || msg.sender == WETH,\n \"Eth not from allowed contracts\"\n );\n }\n\n /***************************************\n Internal functions\n ****************************************/\n\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Convert accumulated ETH to WETH and send to the Harvester.\n /// Will revert if the strategy is paused for accounting.\n function _collectRewardTokens() internal override whenNotPaused {\n // collect ETH from execution rewards from the fee accumulator\n uint256 executionRewards = FeeAccumulator(FEE_ACCUMULATOR_ADDRESS)\n .collect();\n\n // total ETH rewards to be harvested = execution rewards + consensus rewards\n uint256 ethRewards = executionRewards + consensusRewards;\n\n require(\n address(this).balance >= ethRewards,\n \"Insufficient eth balance\"\n );\n\n if (ethRewards > 0) {\n // reset the counter keeping track of beacon chain consensus rewards\n consensusRewards = 0;\n\n // Convert ETH rewards to WETH\n IWETH9(WETH).deposit{ value: ethRewards }();\n\n IERC20(WETH).safeTransfer(harvesterAddress, ethRewards);\n emit RewardTokenCollected(harvesterAddress, WETH, ethRewards);\n }\n }\n\n /// @dev emits Withdrawal event from NativeStakingSSVStrategy\n function _wethWithdrawnToVault(uint256 _amount) internal override {\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _wethWithdrawn(uint256 _amount) internal override {\n /* In an ideal world we wouldn't need to reduce the deduction amount when the\n * depositedWethAccountedFor is smaller than the _amount.\n *\n * The reason this is required is that a malicious actor could sent WETH directly\n * to this contract and that would circumvent the increase of depositedWethAccountedFor\n * property. When the ETH would be staked the depositedWethAccountedFor amount could\n * be deducted so much that it would be negative.\n */\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n }\n}\n" + }, + "contracts/strategies/NativeStaking/ValidatorAccountant.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ValidatorRegistrator } from \"./ValidatorRegistrator.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\n\n/// @title Validator Accountant\n/// @notice Attributes the ETH swept from beacon chain validators to this strategy contract\n/// as either full or partial withdrawals. Partial withdrawals being consensus rewards.\n/// Full withdrawals are from exited validators.\n/// @author Origin Protocol Inc\nabstract contract ValidatorAccountant is ValidatorRegistrator {\n /// @notice The minimum amount of blocks that need to pass between two calls to manuallyFixAccounting\n uint256 public constant MIN_FIX_ACCOUNTING_CADENCE = 7200; // 1 day\n\n /// @notice Keeps track of the total consensus rewards swept from the beacon chain\n uint256 public consensusRewards;\n\n /// @notice start of fuse interval\n uint256 public fuseIntervalStart;\n /// @notice end of fuse interval\n uint256 public fuseIntervalEnd;\n /// @notice last block number manuallyFixAccounting has been called\n uint256 public lastFixAccountingBlockNumber;\n\n uint256[49] private __gap;\n\n event FuseIntervalUpdated(uint256 start, uint256 end);\n event AccountingFullyWithdrawnValidator(\n uint256 noOfValidators,\n uint256 remainingValidators,\n uint256 wethSentToVault\n );\n event AccountingValidatorSlashed(\n uint256 remainingValidators,\n uint256 wethSentToVault\n );\n event AccountingConsensusRewards(uint256 amount);\n\n event AccountingManuallyFixed(\n int256 validatorsDelta,\n int256 consensusRewardsDelta,\n uint256 wethToVault\n );\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n uint256 _maxValidators\n )\n ValidatorRegistrator(\n _wethAddress,\n _vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _maxValidators\n )\n {}\n\n /// @notice set fuse interval values\n function setFuseInterval(\n uint256 _fuseIntervalStart,\n uint256 _fuseIntervalEnd\n ) external onlyGovernor {\n require(\n _fuseIntervalStart < _fuseIntervalEnd &&\n _fuseIntervalEnd < 32 ether &&\n _fuseIntervalEnd - _fuseIntervalStart >= 4 ether,\n \"Incorrect fuse interval\"\n );\n\n fuseIntervalStart = _fuseIntervalStart;\n fuseIntervalEnd = _fuseIntervalEnd;\n\n emit FuseIntervalUpdated(_fuseIntervalStart, _fuseIntervalEnd);\n }\n\n /* solhint-disable max-line-length */\n /// This notion page offers a good explanation of how the accounting functions\n /// https://www.notion.so/originprotocol/Limited-simplified-native-staking-accounting-67a217c8420d40678eb943b9da0ee77d\n /// In short, after dividing by 32, if the ETH remaining on the contract falls between 0 and fuseIntervalStart,\n /// the accounting function will treat that ETH as Beacon chain consensus rewards.\n /// On the contrary, if after dividing by 32, the ETH remaining on the contract falls between fuseIntervalEnd and 32,\n /// the accounting function will treat that as a validator slashing.\n /// @notice Perform the accounting attributing beacon chain ETH to either full or partial withdrawals. Returns true when\n /// accounting is valid and fuse isn't \"blown\". Returns false when fuse is blown.\n /// @dev This function could in theory be permission-less but lets allow only the Registrator (Defender Action) to call it\n /// for now.\n /// @return accountingValid true if accounting was successful, false if fuse is blown\n /* solhint-enable max-line-length */\n function doAccounting()\n external\n onlyRegistrator\n whenNotPaused\n nonReentrant\n returns (bool accountingValid)\n {\n // pause the accounting on failure\n accountingValid = _doAccounting(true);\n }\n\n // slither-disable-start reentrancy-eth\n function _doAccounting(bool pauseOnFail)\n internal\n returns (bool accountingValid)\n {\n if (address(this).balance < consensusRewards) {\n return _failAccounting(pauseOnFail);\n }\n\n // Calculate all the new ETH that has been swept to the contract since the last accounting\n uint256 newSweptETH = address(this).balance - consensusRewards;\n accountingValid = true;\n\n // send the ETH that is from fully withdrawn validators to the Vault\n if (newSweptETH >= FULL_STAKE) {\n uint256 fullyWithdrawnValidators;\n // explicitly cast to uint256 as we want to round to a whole number of validators\n fullyWithdrawnValidators = uint256(newSweptETH / FULL_STAKE);\n activeDepositedValidators -= fullyWithdrawnValidators;\n\n uint256 wethToVault = FULL_STAKE * fullyWithdrawnValidators;\n IWETH9(WETH).deposit{ value: wethToVault }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, wethToVault);\n _wethWithdrawnToVault(wethToVault);\n\n emit AccountingFullyWithdrawnValidator(\n fullyWithdrawnValidators,\n activeDepositedValidators,\n wethToVault\n );\n }\n\n uint256 ethRemaining = address(this).balance - consensusRewards;\n // should be less than a whole validator stake\n require(ethRemaining < FULL_STAKE, \"Unexpected accounting\");\n\n // If no Beacon chain consensus rewards swept\n if (ethRemaining == 0) {\n // do nothing\n return accountingValid;\n } else if (ethRemaining < fuseIntervalStart) {\n // Beacon chain consensus rewards swept (partial validator withdrawals)\n // solhint-disable-next-line reentrancy\n consensusRewards += ethRemaining;\n emit AccountingConsensusRewards(ethRemaining);\n } else if (ethRemaining > fuseIntervalEnd) {\n // Beacon chain consensus rewards swept but also a slashed validator fully exited\n IWETH9(WETH).deposit{ value: ethRemaining }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, ethRemaining);\n activeDepositedValidators -= 1;\n\n _wethWithdrawnToVault(ethRemaining);\n\n emit AccountingValidatorSlashed(\n activeDepositedValidators,\n ethRemaining\n );\n }\n // Oh no... Fuse is blown. The Strategist needs to adjust the accounting values.\n else {\n return _failAccounting(pauseOnFail);\n }\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @dev pause any further accounting if required and return false\n function _failAccounting(bool pauseOnFail)\n internal\n returns (bool accountingValid)\n {\n // pause if not already\n if (pauseOnFail) {\n _pause();\n }\n // fail the accounting\n accountingValid = false;\n }\n\n /// @notice Allow the Strategist to fix the accounting of this strategy and unpause.\n /// @param _validatorsDelta adjust the active validators by up to plus three or minus three\n /// @param _consensusRewardsDelta adjust the accounted for consensus rewards up or down\n /// @param _ethToVaultAmount the amount of ETH that gets wrapped into WETH and sent to the Vault\n /// @dev There is a case when a validator(s) gets slashed so much that the eth swept from\n /// the beacon chain enters the fuse area and there are no consensus rewards on the contract\n /// to \"dip into\"/use. To increase the amount of unaccounted ETH over the fuse end interval\n /// we need to reduce the amount of active deposited validators and immediately send WETH\n /// to the vault, so it doesn't interfere with further accounting.\n function manuallyFixAccounting(\n int256 _validatorsDelta,\n int256 _consensusRewardsDelta,\n uint256 _ethToVaultAmount\n ) external onlyStrategist whenPaused nonReentrant {\n require(\n lastFixAccountingBlockNumber + MIN_FIX_ACCOUNTING_CADENCE <\n block.number,\n \"Fix accounting called too soon\"\n );\n require(\n _validatorsDelta >= -3 &&\n _validatorsDelta <= 3 &&\n // new value must be positive\n int256(activeDepositedValidators) + _validatorsDelta >= 0,\n \"Invalid validatorsDelta\"\n );\n require(\n _consensusRewardsDelta >= -332 ether &&\n _consensusRewardsDelta <= 332 ether &&\n // new value must be positive\n int256(consensusRewards) + _consensusRewardsDelta >= 0,\n \"Invalid consensusRewardsDelta\"\n );\n require(_ethToVaultAmount <= 32 ether * 3, \"Invalid wethToVaultAmount\");\n\n activeDepositedValidators = uint256(\n int256(activeDepositedValidators) + _validatorsDelta\n );\n consensusRewards = uint256(\n int256(consensusRewards) + _consensusRewardsDelta\n );\n lastFixAccountingBlockNumber = block.number;\n if (_ethToVaultAmount > 0) {\n IWETH9(WETH).deposit{ value: _ethToVaultAmount }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, _ethToVaultAmount);\n _wethWithdrawnToVault(_ethToVaultAmount);\n }\n\n emit AccountingManuallyFixed(\n _validatorsDelta,\n _consensusRewardsDelta,\n _ethToVaultAmount\n );\n\n // rerun the accounting to see if it has now been fixed.\n // Do not pause the accounting on failure as it is already paused\n require(_doAccounting(false), \"Fuse still blown\");\n\n // unpause since doAccounting was successful\n _unpause();\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n /// @dev allows for NativeStakingSSVStrategy contract to emit the Withdrawal event\n function _wethWithdrawnToVault(uint256 _amount) internal virtual;\n}\n" + }, + "contracts/strategies/NativeStaking/ValidatorRegistrator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IConsolidationTarget } from \"../../interfaces/IConsolidation.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconConsolidation } from \"../../beacon/BeaconConsolidation.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Registrator of the validators\n * @notice This contract implements all the required functionality to register, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract ValidatorRegistrator is Governable, Pausable {\n /// @notice The maximum amount of ETH that can be staked by a validator\n /// @dev this can change in the future with EIP-7251, Increase the MAX_EFFECTIVE_BALANCE\n uint256 public constant FULL_STAKE = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Maximum number of validators that can be registered in this strategy\n uint256 public immutable MAX_VALIDATORS;\n\n /// @notice Address of the registrator - allowed to register, exit and remove validators\n address public validatorRegistrator;\n /// @notice The number of validators that have 32 (!) ETH actively deposited. When a new deposit\n /// to a validator happens this number increases, when a validator exit is detected this number\n /// decreases.\n uint256 public activeDepositedValidators;\n /// @notice State of the validators keccak256(pubKey) => state\n mapping(bytes32 => VALIDATOR_STATE) public validatorsStates;\n /// @notice The account that is allowed to modify stakeETHThreshold and reset stakeETHTally\n address public stakingMonitor;\n /// @notice Amount of ETH that can be staked before staking on the contract is suspended\n /// and the `stakingMonitor` needs to approve further staking by calling `resetStakeETHTally`\n uint256 public stakeETHThreshold;\n /// @notice Amount of ETH that has been staked since the `stakingMonitor` last called `resetStakeETHTally`.\n /// This can not go above `stakeETHThreshold`.\n uint256 public stakeETHTally;\n\n /// @notice Number of validators currently being consolidated\n uint256 public consolidationCount;\n address public consolidationTargetStrategy;\n /// @notice Mapping of support target staking strategies that can be used for consolidation\n mapping(address => bool) public consolidationTargetStrategies;\n\n // For future use\n uint256[44] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n EXITING, // exit message has been posted and validator is in the process of exiting\n EXIT_COMPLETE // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event ETHStaked(bytes32 indexed pubKeyHash, bytes pubKey, uint256 amount);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event SSVValidatorExitInitiated(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event SSVValidatorExitCompleted(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event ConsolidationRequested(\n bytes[] sourcePubKeys,\n bytes targetPubKey,\n address targetStakingStrategy,\n uint256 consolidationCount\n );\n event ConsolidationConfirmed(\n uint256 consolidationCount,\n uint256 activeDepositedValidators\n );\n event StakeETHThresholdChanged(uint256 amount);\n event StakeETHTallyReset();\n event TargetStrategyAdded(address indexed strategy);\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(\n msg.sender == validatorRegistrator,\n \"Caller is not the Registrator\"\n );\n _;\n }\n\n /// @dev Throws if called by any account other than the Staking monitor\n modifier onlyStakingMonitor() {\n require(msg.sender == stakingMonitor, \"Caller is not the Monitor\");\n _;\n }\n\n /// @dev Throws if called by any account other than the Strategist\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(VAULT_ADDRESS).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n uint256 _maxValidators\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n MAX_VALIDATORS = _maxValidators;\n }\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Set the address of the staking monitor that is allowed to reset stakeETHTally\n function setStakingMonitor(address _address) external onlyGovernor {\n stakingMonitor = _address;\n emit StakingMonitorChanged(_address);\n }\n\n /// @notice Set the amount of ETH that can be staked before staking monitor\n // needs to a approve further staking by resetting the stake ETH tally\n function setStakeETHThreshold(uint256 _amount) external onlyGovernor {\n stakeETHThreshold = _amount;\n emit StakeETHThresholdChanged(_amount);\n }\n\n /// @notice Reset the stakeETHTally\n function resetStakeETHTally() external onlyStakingMonitor {\n stakeETHTally = 0;\n emit StakeETHTallyReset();\n }\n\n /// @notice Adds support for a new staking strategy that can be used for consolidation.\n function addTargetStrategy(address _strategy) external onlyGovernor {\n consolidationTargetStrategies[_strategy] = true;\n\n emit TargetStrategyAdded(_strategy);\n }\n\n /// @notice Stakes WETH to the node validators\n /// @param validators A list of validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n // slither-disable-start reentrancy-eth\n function stakeEth(ValidatorStakeData[] calldata validators)\n external\n onlyRegistrator\n whenNotPaused\n nonReentrant\n {\n uint256 requiredETH = validators.length * FULL_STAKE;\n\n // Check there is enough WETH from the deposits sitting in this strategy contract\n require(\n requiredETH <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(\n activeDepositedValidators + validators.length <= MAX_VALIDATORS,\n \"Max validators reached\"\n );\n\n require(\n stakeETHTally + requiredETH <= stakeETHThreshold,\n \"Staking ETH over threshold\"\n );\n stakeETHTally += requiredETH;\n\n // Convert required ETH from WETH\n IWETH9(WETH).withdraw(requiredETH);\n _wethWithdrawn(requiredETH);\n\n /* 0x01 to indicate that withdrawal credentials will contain an EOA address that the sweeping function\n * can sweep funds to.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x01),\n bytes11(0),\n address(this)\n );\n\n // For each validator\n for (uint256 i = 0; i < validators.length; ++i) {\n bytes32 pubKeyHash = keccak256(validators[i].pubkey);\n\n require(\n validatorsStates[pubKeyHash] == VALIDATOR_STATE.REGISTERED,\n \"Validator not registered\"\n );\n\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: FULL_STAKE\n }(\n validators[i].pubkey,\n withdrawalCredentials,\n validators[i].signature,\n validators[i].depositDataRoot\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.STAKED;\n\n emit ETHStaked(pubKeyHash, validators[i].pubkey, FULL_STAKE);\n }\n // save gas by changing this storage variable only once rather each time in the loop.\n activeDepositedValidators += validators.length;\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Registers a new validator in the SSV Cluster.\n /// Only the registrator can call this function.\n /// @param publicKeys The public keys of the validators\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for each validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n require(\n publicKeys.length == sharesData.length,\n \"Pubkey sharesData mismatch\"\n );\n // Check each public key has not already been used\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n emit SSVValidatorRegistered(pubKeyHash, publicKeys[i], operatorIds);\n }\n\n ISSVNetwork(SSV_NETWORK).bulkRegisterValidator(\n publicKeys,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n }\n\n /// @notice Exit validators from the Beacon chain.\n /// The staked ETH will eventually swept to this native staking strategy.\n /// Only the registrator can call this function.\n /// @param publicKeys List of SSV validator public keys\n /// @param operatorIds The operator IDs of the SSV Cluster\n // slither-disable-start reentrancy-no-eth\n function exitSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external onlyRegistrator whenNotPaused nonReentrant {\n ISSVNetwork(SSV_NETWORK).bulkExitValidator(publicKeys, operatorIds);\n\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n\n // Check each validator has not already been staked.\n // This would normally be done before the external call but is after\n // so only one for loop of the validators is needed.\n require(\n currentState == VALIDATOR_STATE.STAKED,\n \"Validator not staked\"\n );\n\n // Store the new validator state\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXITING;\n\n emit SSVValidatorExitInitiated(\n pubKeyHash,\n publicKeys[i],\n operatorIds\n );\n }\n }\n\n /// @notice Remove validators from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKeys List of SSV validator public keys\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function removeSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused nonReentrant {\n ISSVNetwork(SSV_NETWORK).bulkRemoveValidator(\n publicKeys,\n operatorIds,\n cluster\n );\n\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n\n // Check each validator is either registered or exited.\n // This would normally be done before the external call but is after\n // so only one for loop of the validators is needed.\n require(\n currentState == VALIDATOR_STATE.EXITING ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exiting\"\n );\n\n // Store the new validator state\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXIT_COMPLETE;\n\n emit SSVValidatorExitCompleted(\n pubKeyHash,\n publicKeys[i],\n operatorIds\n );\n }\n }\n\n /// @notice Deposits more SSV Tokens to the SSV Network contract which is used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// uses \"onlyStrategist\" modifier so continuous front-running can't DOS our maintenance service\n /// that tries to top up SSV tokens.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function depositSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyStrategist {\n ISSVNetwork(SSV_NETWORK).deposit(\n address(this),\n operatorIds,\n ssvAmount,\n cluster\n );\n }\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n New Consolidation\n ****************************************/\n\n function requestConsolidation(\n bytes[] calldata sourcePubKeys,\n bytes calldata targetPubKey,\n address targetStakingStrategy\n ) external nonReentrant whenNotPaused onlyGovernor {\n require(\n consolidationTargetStrategies[targetStakingStrategy],\n \"Invalid target strategy\"\n );\n\n bytes32 targetPubKeyHash = keccak256(targetPubKey);\n bytes32 sourcePubKeyHash;\n for (uint256 i = 0; i < sourcePubKeys.length; ++i) {\n // hash the source validator's public key using the Beacon Chain's format\n sourcePubKeyHash = keccak256(sourcePubKeys[i]);\n require(sourcePubKeyHash != targetPubKeyHash, \"Self consolidation\");\n require(\n validatorsStates[sourcePubKeyHash] == VALIDATOR_STATE.STAKED,\n \"Source validator not staked\"\n );\n\n // Request consolidation from source to target validator\n BeaconConsolidation.request(sourcePubKeys[i], targetPubKey);\n\n // Store the state of the source validator as exiting so it can be removed\n // after the consolidation is confirmed\n validatorsStates[sourcePubKeyHash] == VALIDATOR_STATE.EXITING;\n }\n\n // Hash using the Beacon Chain's format\n bytes32 lastSourcePubKeyHash = _hashPubKey(\n sourcePubKeys[sourcePubKeys.length - 1]\n );\n // Call the new compounding staking strategy to validate the target validator\n IConsolidationTarget(targetStakingStrategy).requestConsolidation(\n lastSourcePubKeyHash,\n targetPubKeyHash\n );\n\n // Store the consolidation state\n consolidationCount = sourcePubKeys.length;\n consolidationTargetStrategy = targetStakingStrategy;\n\n // Pause the strategy to prevent further consolidations or validator exits\n _pause();\n\n emit ConsolidationRequested(\n sourcePubKeys,\n targetPubKey,\n targetStakingStrategy,\n sourcePubKeys.length\n );\n }\n\n function confirmConsolidation()\n external\n nonReentrant\n whenPaused\n returns (uint256 consolidationCount_)\n {\n // Check the caller is the target staking strategy\n require(\n msg.sender == consolidationTargetStrategy,\n \"Not target strategy\"\n );\n\n // Load the number of validators being consolidated into memory\n consolidationCount_ = consolidationCount;\n\n // Need to check this is from the new staking strategy\n require(consolidationCount_ > 0, \"No consolidation in progress\");\n\n // Store the reduced number of active deposited validators\n // managed by this strategy\n activeDepositedValidators -= consolidationCount_;\n\n // Reset the consolidation count\n consolidationCount = 0;\n consolidationTargetStrategy = address(0);\n\n // Unpause the strategy to allow further operations\n _unpause();\n\n emit ConsolidationConfirmed(\n consolidationCount_,\n activeDepositedValidators\n );\n }\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _wethWithdrawn(uint256 _amount) internal virtual;\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/1f427944062510ef43043025720d6f6d.json b/contracts/deployments/hoodi/solcInputs/1f427944062510ef43043025720d6f6d.json new file mode 100644 index 0000000000..c1e4a4c7e8 --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/1f427944062510ef43043025720d6f6d.json @@ -0,0 +1,111 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/security/Pausable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the Beacon Block Root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Block Root Oracle to get the parent block root\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconOracle {\n function blockToSlot(uint64 blockNumber) external returns (uint64 slot);\n\n function slotToBlock(uint64 slot) external returns (uint64 blockNumber);\n\n function slotToRoot(uint64 slot) external returns (bytes32 blockRoot);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n enum BalanceProofLevel {\n Container,\n BeaconBlock\n }\n\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 root,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex,\n BalanceProofLevel level\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view;\n\n function verifyBlockNumber(\n bytes32 beaconBlockRoot,\n uint256 blockNumber,\n bytes calldata blockNumberProof\n ) external view;\n\n function verifySlot(\n bytes32 beaconBlockRoot,\n uint256 slot,\n bytes calldata slotProof\n ) external view;\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n external\n pure\n returns (uint256);\n\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) external pure returns (uint256);\n}\n" + }, + "contracts/interfaces/IConsolidation.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IConsolidationSource {\n function confirmConsolidation()\n external\n returns (uint256 consolidationCount);\n}\n\ninterface IConsolidationTarget {\n function requestConsolidation(\n bytes32 lastSourcePubKeyHash,\n bytes32 targetPubKeyHash\n ) external;\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconOracle Address of the Beacon Oracle contract that maps block numbers to slots\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconOracle,\n address _beaconProofs\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconOracle,\n _beaconProofs\n )\n {\n SSV_TOKEN = _ssvToken;\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract.\n /// @param _rewardTokenAddresses Address of reward token for platform\n /// @param _assets Addresses of initial supported assets\n /// @param _pTokens Platform Token corresponding addresses\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused for validator consolidation.\n /// @param _asset Address of asset to deposit. Has to be WETH.\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused for validator consolidation.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// Will revert if the strategy is paused for validator consolidation.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant whenNotPaused {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _asset, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertWethToEth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(_asset, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n /// Will revert if the strategy is paused for validator consolidation.\n function withdrawAll()\n external\n override\n onlyVaultOrGovernor\n nonReentrant\n whenNotPaused\n {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, WETH, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Returns the last verified balance of validator deposits, validator balance,\n /// WETH and ETH in the strategy contract.\n /// @param _asset Address of weth asset\n /// @return balance Total value of (W)ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by strategy.\n /// @param _asset The address of the asset token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for deposits\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instread of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract,\n /// but so does withdrawals from validators. It's too complex to separate the two\n /// so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IConsolidationSource } from \"../../interfaces/IConsolidation.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\nimport { IBeaconOracle } from \"../../interfaces/IBeaconOracle.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit, remove and consolidate validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable, Pausable {\n using SafeERC20 for IERC20;\n\n /// @notice The amount of ETH in wei that is required for a deposit to a new validator.\n /// Initially this is 32 ETH, but will be increased to 1 ETH after P2P's APIs have been updated\n /// to support deposits of 1 ETH.\n uint256 public constant DEPOSIT_AMOUNT_WEI = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Oracle contract that maps block numbers to slots\n address public immutable BEACON_ORACLE;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// Deposit data for new compounding validators.\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountWei Amount of ETH in wei that has been deposited to the beacon chain deposit contract\n /// @param blockNumber Block number when the deposit was made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either PENDING or PROVEN\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 blockNumber;\n uint32 depositIndex;\n DepositStatus status;\n }\n /// @notice Mapping of the root of a deposit (depositDataRoot) to its data\n mapping(bytes32 => DepositData) public deposits;\n /// @notice List of deposit roots that are still to be verified as processed on the beacon chain\n bytes32[] public depositsRoots;\n\n // Validator data\n\n struct ValidatorData {\n bytes32 pubKeyHash; // Hash of the validator's public key using the Beacon Chain's format\n uint64 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes and indexes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n ValidatorData[] internal verifiedValidators;\n /// @notice State of the new compounding validators with a 0x02 withdrawal credential prefix.\n /// Uses the Beacon chain hashing for BLSPubkey which is\n /// sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => VALIDATOR_STATE) public validatorState;\n\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n uint64 timestamp;\n uint128 ethBalance;\n }\n // TODO is it more efficient to use the block root rather than hashing it?\n /// @notice Mapping of the block root to the balances at that slot\n mapping(bytes32 => Balances) public snappedBalances;\n uint64 public lastSnapTimestamp;\n uint128 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n bytes32 public consolidationLastPubKeyHash;\n address public consolidationSourceStrategy;\n mapping(address => bool) public consolidationSourceStrategies;\n\n // For future use\n uint256[50] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITED, // The validator has been verified to have a zero balance\n REMOVED // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified and is ready to be staked\n }\n\n event RegistratorChanged(address indexed newAddress);\n event SourceStrategyAdded(address indexed strategy);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n bytes32 indexed depositDataRoot,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint64 indexed validatorIndex\n );\n event DepositVerified(bytes32 indexed depositDataRoot, uint256 amountWei);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(\n uint256 indexed timestamp,\n bytes32 indexed blockRoot,\n uint256 ethBalance\n );\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 wethBalance,\n uint256 ethBalance\n );\n event ConsolidationRequested(\n bytes32 indexed lastSourcePubKeyHash,\n bytes32 indexed targetPubKeyHash,\n address indexed sourceStrategy\n );\n event ConsolidationVerified(\n bytes32 indexed lastSourcePubKeyHash,\n uint64 indexed lastValidatorIndex,\n uint256 consolidationCount\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconOracle Address of the Beacon Oracle contract that maps block numbers to slots\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconOracle,\n address _beaconProofs\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_ORACLE = _beaconOracle;\n BEACON_PROOFS = _beaconProofs;\n }\n\n /***************************************\n Admin Functions\n ****************************************/\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Adds support for a legacy staking strategy that can be used for consolidation.\n function addSourceStrategy(address _strategy) external onlyGovernor {\n consolidationSourceStrategies[_strategy] = true;\n\n emit SourceStrategyAdded(_strategy);\n }\n\n /***************************************`\n Validator Management\n ****************************************/\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validatorState[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validator validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validator,\n uint64 depositAmountGwei\n ) external onlyRegistrator whenNotPaused {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertEthToWeth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validator.pubkey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == VALIDATOR_STATE.REGISTERED ||\n currentState == VALIDATOR_STATE.VERIFIED),\n \"Not registered or verified\"\n );\n require(\n currentState == VALIDATOR_STATE.VERIFIED ||\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validator.pubkey,\n withdrawalCredentials,\n validator.signature,\n validator.depositDataRoot\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == VALIDATOR_STATE.REGISTERED) {\n validatorState[pubKeyHash] = VALIDATOR_STATE.STAKED;\n }\n // Store the deposit data for verifyDeposit and verifyBalances\n deposits[validator.depositDataRoot] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n blockNumber: SafeCast.toUint64(block.number),\n depositIndex: SafeCast.toUint32(depositsRoots.length),\n status: DepositStatus.PENDING\n });\n depositsRoots.push(validator.depositDataRoot);\n\n emit ETHStaked(\n pubKeyHash,\n validator.depositDataRoot,\n validator.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then the request will fail.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n onlyRegistrator\n whenNotPaused\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove a sweeping validator from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == VALIDATOR_STATE.EXITED ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exited\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validatorState[pubKeyHash] = VALIDATOR_STATE.REMOVED;\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /// @notice Receives requests from supported legacy strategies to consolidate sweeping validators to\n /// a new compounding validator on this new strategy.\n /// @param lastSourcePubKeyHash The last source validator to be consolidated hashed using the Beacon Chain's format.\n /// @param targetPubKeyHash The target validator's public key hash using the Beacon Chain's format.\n function requestConsolidation(\n bytes32 lastSourcePubKeyHash,\n bytes32 targetPubKeyHash\n ) external whenNotPaused {\n require(\n consolidationSourceStrategies[msg.sender],\n \"Not a source strategy\"\n );\n\n // The target validator must be a compounding validator that has been verified\n require(\n validatorState[targetPubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Target validator not verified\"\n );\n\n // Ensure balances can not be verified until after the consolidation has been verified.\n lastSnapTimestamp = 0;\n\n // Store consolidation state\n consolidationLastPubKeyHash = lastSourcePubKeyHash;\n consolidationSourceStrategy = msg.sender;\n\n // Pause the strategy while the consolidation is in progress\n _pause();\n\n emit ConsolidationRequested(\n lastSourcePubKeyHash,\n targetPubKeyHash,\n msg.sender\n );\n }\n\n /***************************************\n SSV Management\n ****************************************/\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n Beacon Chain Proofs\n ****************************************/\n\n /// @notice Verifies a validator's index to its public key.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param validatorPubKeyProof The merkle proof that the validator's index matches its public key\n /// on the beacon chain.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.STAKED,\n \"Validator not staked\"\n );\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key\n IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n address(this) // Withdrawal address is this strategy\n );\n\n // TODO verify the validator's withdrawal credential points to this strategy\n\n // Store the validator state as verified\n validatorState[pubKeyHash] = VALIDATOR_STATE.VERIFIED;\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(\n ValidatorData({ pubKeyHash: pubKeyHash, index: validatorIndex })\n );\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n /// @param depositDataRoot The root of the deposit data that was stored when\n /// the deposit was made on the execution layer.\n /// @param depositBlockNumber A block number that is on or after the block the deposit\n /// was made on the execution layer.\n /// @param processedSlot Any slot on or after the deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// @param firstPendingDepositSlot The slot of the first pending deposit in the beacon chain\n /// @param firstPendingDepositSlotProof The merkle proof that the slot of the first pending deposit\n /// matches the beacon chain.\n /// BeaconBlock.BeaconBlockBody.deposits[0].slot\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n bytes32 depositDataRoot,\n uint64 depositBlockNumber,\n uint64 processedSlot,\n uint64 firstPendingDepositSlot,\n bytes calldata firstPendingDepositSlotProof\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositDataRoot];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n validatorState[deposit.pubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n // The deposit block number mapped to a slot needs to be the same block or after\n // the deposit in `stakeETH` was created on the execution layer.\n require(\n deposit.blockNumber <= depositBlockNumber,\n \"Deposit block before deposit\"\n );\n\n // Get the slot that is on or after the deposit was made on the execution layer.\n uint64 depositSlot = IBeaconOracle(BEACON_ORACLE).blockToSlot(\n depositBlockNumber\n );\n\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the first pending deposit's slot is 0 then there are no pending deposits so\n // our deposit must have been processed on the beacon chain.\n require(\n depositSlot < firstPendingDepositSlot ||\n firstPendingDepositSlot == 0,\n \"Deposit not processed\"\n );\n\n // The slot of the execution layer deposit must be before\n // the slot that the deposit was processed on the beacon chain.\n require(depositSlot < processedSlot, \"Slot not after deposit\");\n\n // Get the beacon block root for the slot that is on or after the deposit was processed on the beacon chain.\n bytes32 blockRoot = IBeaconOracle(BEACON_ORACLE).slotToRoot(\n processedSlot\n );\n\n // Verify the slot of the first pending deposit matches the beacon chain\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n blockRoot,\n firstPendingDepositSlot,\n firstPendingDepositSlotProof\n );\n\n // After verifying the proof, update the contract storage\n deposits[depositDataRoot].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n bytes32 lastDepositDataRoot = depositsRoots[depositsRoots.length - 1];\n depositsRoots[deposit.depositIndex] = lastDepositDataRoot;\n deposits[lastDepositDataRoot].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositsRoots.pop();\n\n emit DepositVerified(\n depositDataRoot,\n uint256(deposit.amountGwei) * 1 gwei\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n // TODO what if the last validator was exited rather than consolidated?\n // slither-disable-start reentrancy-no-eth\n // function verifyConsolidation(\n // uint64 parentBlockTimestamp,\n // uint64 lastValidatorIndex,\n // bytes calldata validatorPubKeyProof,\n // bytes32 balancesLeaf,\n // bytes calldata validatorBalanceProof\n // ) external onlyRegistrator {\n // bytes32 consolidationLastPubKeyHashMem = consolidationLastPubKeyHash;\n // require(\n // consolidationLastPubKeyHashMem != bytes32(0),\n // \"No consolidations\"\n // );\n\n // bytes32 blockRoot = BeaconRoots.parentBlockRoot(parentBlockTimestamp);\n // // Verify the validator index has the same public key as the last source validator\n // IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n // blockRoot,\n // consolidationLastPubKeyHashMem,\n // validatorPubKeyProof,\n // lastValidatorIndex,\n // address(this) // Withdrawal address is this strategy\n // );\n\n // // Verify the balance of the last validator in the consolidation batch\n // // is zero. If its not then the consolidation has not been completed.\n // // This proof is to the beacon block root, not the balances container root.\n // uint256 validatorBalance = IBeaconProofs(BEACON_PROOFS)\n // .verifyValidatorBalance(\n // blockRoot,\n // balancesLeaf,\n // validatorBalanceProof,\n // lastValidatorIndex,\n // IBeaconProofs.BalanceProofLevel.BeaconBlock\n // );\n // require(validatorBalance == 0, \"Last validator balance not zero\");\n\n // // Call the old sweeping strategy to confirm the consolidation has been completed.\n // // This will decrease the balance of the source strategy by 32 ETH for each validator being consolidated.\n // uint256 consolidationCount = IConsolidationSource(\n // consolidationSourceStrategy\n // ).confirmConsolidation();\n\n // // Increase the balance of this strategy by 32 ETH for each validator being consolidated.\n // // This nets out the decrease in the source strategy's balance.\n // lastVerifiedEthBalance += SafeCast.toUint128(\n // consolidationCount * 32 ether\n // );\n\n // // Reset the stored consolidation state\n // consolidationLastPubKeyHash = bytes32(0);\n // consolidationSourceStrategy = address(0);\n\n // emit ConsolidationVerified(\n // consolidationLastPubKeyHashMem,\n // lastValidatorIndex,\n // consolidationCount\n // );\n\n // // Unpause now the balance of the target validator has been verified\n // _unpause();\n\n // // Take a snap of the balances so the actual balances of the new validator balances can be verified\n // _snapBalances();\n // }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block.\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n /// Can only be called by the registrator.\n /// Can not be called while a consolidation is in progress.\n function snapBalances() external whenNotPaused onlyRegistrator {\n _snapBalances();\n }\n\n function _snapBalances() internal {\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(\n SafeCast.toUint64(block.timestamp)\n );\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the balances in the mapping\n snappedBalances[blockRoot] = Balances({\n timestamp: SafeCast.toUint64(block.timestamp),\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n // Store the snapped timestamp\n lastSnapTimestamp = SafeCast.toUint64(block.timestamp);\n\n emit BalancesSnapped(block.timestamp, blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct VerifyBalancesParams {\n bytes32 blockRoot;\n uint64 firstPendingDepositSlot;\n // BeaconBlock.BeaconBlockBody.deposits[0].slot\n bytes firstPendingDepositSlotProof;\n bytes32 balancesContainerRoot;\n // BeaconBlock.state.validators\n bytes validatorContainerProof;\n bytes32[] validatorBalanceLeaves;\n // BeaconBlock.state.validators[validatorIndex].balance\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(VerifyBalancesParams calldata params) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalances[params.blockRoot];\n // Check the balances are the latest\n require(lastSnapTimestamp > 0, \"No snapped balances\");\n require(balancesMem.timestamp == lastSnapTimestamp, \"Stale snap\");\n\n uint256 depositsCount = depositsRoots.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit to the beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n params.blockRoot,\n params.firstPendingDepositSlot,\n params.firstPendingDepositSlotProof\n );\n\n uint64 firstPendingDepositBlockNumber = IBeaconOracle(BEACON_ORACLE)\n .slotToBlock(params.firstPendingDepositSlot);\n\n // For each native staking contract's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n bytes32 depositDataRoot = depositsRoots[i];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit block number is before the\n // block number of the staking strategy's deposit.\n // If it has it will need to be verified with `verifyDeposit`\n require(\n firstPendingDepositBlockNumber <\n deposits[depositDataRoot].blockNumber,\n \"Deposit has been processed\"\n );\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei +=\n uint256(deposits[depositDataRoot].amountGwei) *\n 1 gwei;\n }\n }\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n params.validatorBalanceProofs.length == verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n params.validatorBalanceLeaves.length == verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n params.blockRoot,\n params.balancesContainerRoot,\n params.validatorContainerProof\n );\n\n // for each validator in reserve order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n params.balancesContainerRoot,\n params.validatorBalanceLeaves[i],\n params.validatorBalanceProofs[i],\n verifiedValidators[i].index,\n IBeaconProofs.BalanceProofLevel.Container\n );\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n validatorState[\n verifiedValidators[i].pubKeyHash\n ] = VALIDATOR_STATE.EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += uint256(validatorBalanceGwei) * 1 gwei;\n }\n }\n\n uint256 wethBalance = IWETH9(WETH).balanceOf(address(this));\n\n // Store the verified balance in storage\n lastVerifiedEthBalance = SafeCast.toUint128(\n totalDepositsWei + totalValidatorBalance + balancesMem.ethBalance\n );\n // Reset the last snap timestamp so a new snapBalances has to be made\n lastSnapTimestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n wethBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /***************************************\n WETH and ETH Accounting\n ****************************************/\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n function _convertWethToEth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance\n if (lastVerifiedEthBalance > _ethAmount) {\n lastVerifiedEthBalance -= SafeCast.toUint128(_ethAmount);\n } else {\n // This can happen if all ETH in the validators was withdrawn\n // and there was more consensus rewards since the last balance verification.\n // Or it can happen if ETH is donated to this strategy.\n lastVerifiedEthBalance = 0;\n }\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n function _convertEthToWeth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += SafeCast.toUint128(_wethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /***************************************\n View Functions\n ****************************************/\n\n function getVerifiedValidators()\n external\n view\n returns (ValidatorData[] memory)\n {\n return verifiedValidators;\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/294c99ebae323732f7829a948fbd35c6.json b/contracts/deployments/hoodi/solcInputs/294c99ebae323732f7829a948fbd35c6.json new file mode 100644 index 0000000000..8e627954d9 --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/294c99ebae323732f7829a948fbd35c6.json @@ -0,0 +1,1092 @@ +{ + "language": "Solidity", + "sources": { + "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {Client} from \"../libraries/Client.sol\";\n\ninterface IRouterClient {\n error UnsupportedDestinationChain(uint64 destChainSelector);\n error InsufficientFeeTokenAmount();\n error InvalidMsgValue();\n\n /// @notice Checks if the given chain ID is supported for sending/receiving.\n /// @param chainSelector The chain to check.\n /// @return supported is true if it is supported, false if not.\n function isChainSupported(uint64 chainSelector) external view returns (bool supported);\n\n /// @notice Gets a list of all supported tokens which can be sent or received\n /// to/from a given chain id.\n /// @param chainSelector The chainSelector.\n /// @return tokens The addresses of all tokens that are supported.\n function getSupportedTokens(uint64 chainSelector) external view returns (address[] memory tokens);\n\n /// @param destinationChainSelector The destination chainSelector\n /// @param message The cross-chain CCIP message including data and/or tokens\n /// @return fee returns execution fee for the message\n /// delivery to destination chain, denominated in the feeToken specified in the message.\n /// @dev Reverts with appropriate reason upon invalid message.\n function getFee(\n uint64 destinationChainSelector,\n Client.EVM2AnyMessage memory message\n ) external view returns (uint256 fee);\n\n /// @notice Request a message to be sent to the destination chain\n /// @param destinationChainSelector The destination chain ID\n /// @param message The cross-chain CCIP message including data and/or tokens\n /// @return messageId The message ID\n /// @dev Note if msg.value is larger than the required fee (from getFee) we accept\n /// the overpayment with no refund.\n /// @dev Reverts with appropriate reason upon invalid message.\n function ccipSend(\n uint64 destinationChainSelector,\n Client.EVM2AnyMessage calldata message\n ) external payable returns (bytes32);\n}\n" + }, + "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// End consumer library.\nlibrary Client {\n /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.\n struct EVMTokenAmount {\n address token; // token address on the local chain.\n uint256 amount; // Amount of tokens.\n }\n\n struct Any2EVMMessage {\n bytes32 messageId; // MessageId corresponding to ccipSend on source.\n uint64 sourceChainSelector; // Source chain selector.\n bytes sender; // abi.decode(sender) if coming from an EVM chain.\n bytes data; // payload sent in original message.\n EVMTokenAmount[] destTokenAmounts; // Tokens and their amounts in their destination chain representation.\n }\n\n // If extraArgs is empty bytes, the default is 200k gas limit.\n struct EVM2AnyMessage {\n bytes receiver; // abi.encode(receiver address) for dest EVM chains\n bytes data; // Data payload\n EVMTokenAmount[] tokenAmounts; // Token transfers\n address feeToken; // Address of feeToken. address(0) means you will send msg.value.\n bytes extraArgs; // Populate this with _argsToBytes(EVMExtraArgsV1)\n }\n\n // bytes4(keccak256(\"CCIP EVMExtraArgsV1\"));\n bytes4 public constant EVM_EXTRA_ARGS_V1_TAG = 0x97a657c9;\n struct EVMExtraArgsV1 {\n uint256 gasLimit;\n }\n\n function _argsToBytes(EVMExtraArgsV1 memory extraArgs) internal pure returns (bytes memory bts) {\n return abi.encodeWithSelector(EVM_EXTRA_ARGS_V1_TAG, extraArgs);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport \"@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol\";\n\nlibrary ExecutorOptions {\n using CalldataBytesLib for bytes;\n\n uint8 internal constant WORKER_ID = 1;\n\n uint8 internal constant OPTION_TYPE_LZRECEIVE = 1;\n uint8 internal constant OPTION_TYPE_NATIVE_DROP = 2;\n uint8 internal constant OPTION_TYPE_LZCOMPOSE = 3;\n uint8 internal constant OPTION_TYPE_ORDERED_EXECUTION = 4;\n uint8 internal constant OPTION_TYPE_LZREAD = 5;\n\n error Executor_InvalidLzReceiveOption();\n error Executor_InvalidNativeDropOption();\n error Executor_InvalidLzComposeOption();\n error Executor_InvalidLzReadOption();\n\n /// @dev decode the next executor option from the options starting from the specified cursor\n /// @param _options [executor_id][executor_option][executor_id][executor_option]...\n /// executor_option = [option_size][option_type][option]\n /// option_size = len(option_type) + len(option)\n /// executor_id: uint8, option_size: uint16, option_type: uint8, option: bytes\n /// @param _cursor the cursor to start decoding from\n /// @return optionType the type of the option\n /// @return option the option of the executor\n /// @return cursor the cursor to start decoding the next executor option\n function nextExecutorOption(\n bytes calldata _options,\n uint256 _cursor\n ) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) {\n unchecked {\n // skip worker id\n cursor = _cursor + 1;\n\n // read option size\n uint16 size = _options.toU16(cursor);\n cursor += 2;\n\n // read option type\n optionType = _options.toU8(cursor);\n\n // startCursor and endCursor are used to slice the option from _options\n uint256 startCursor = cursor + 1; // skip option type\n uint256 endCursor = cursor + size;\n option = _options[startCursor:endCursor];\n cursor += size;\n }\n }\n\n function decodeLzReceiveOption(bytes calldata _option) internal pure returns (uint128 gas, uint128 value) {\n if (_option.length != 16 && _option.length != 32) revert Executor_InvalidLzReceiveOption();\n gas = _option.toU128(0);\n value = _option.length == 32 ? _option.toU128(16) : 0;\n }\n\n function decodeNativeDropOption(bytes calldata _option) internal pure returns (uint128 amount, bytes32 receiver) {\n if (_option.length != 48) revert Executor_InvalidNativeDropOption();\n amount = _option.toU128(0);\n receiver = _option.toB32(16);\n }\n\n function decodeLzComposeOption(\n bytes calldata _option\n ) internal pure returns (uint16 index, uint128 gas, uint128 value) {\n if (_option.length != 18 && _option.length != 34) revert Executor_InvalidLzComposeOption();\n index = _option.toU16(0);\n gas = _option.toU128(2);\n value = _option.length == 34 ? _option.toU128(18) : 0;\n }\n\n function decodeLzReadOption(\n bytes calldata _option\n ) internal pure returns (uint128 gas, uint32 calldataSize, uint128 value) {\n if (_option.length != 20 && _option.length != 36) revert Executor_InvalidLzReadOption();\n gas = _option.toU128(0);\n calldataSize = _option.toU32(16);\n value = _option.length == 36 ? _option.toU128(20) : 0;\n }\n\n function encodeLzReceiveOption(uint128 _gas, uint128 _value) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_gas) : abi.encodePacked(_gas, _value);\n }\n\n function encodeNativeDropOption(uint128 _amount, bytes32 _receiver) internal pure returns (bytes memory) {\n return abi.encodePacked(_amount, _receiver);\n }\n\n function encodeLzComposeOption(uint16 _index, uint128 _gas, uint128 _value) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_index, _gas) : abi.encodePacked(_index, _gas, _value);\n }\n\n function encodeLzReadOption(\n uint128 _gas,\n uint32 _calldataSize,\n uint128 _value\n ) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_gas, _calldataSize) : abi.encodePacked(_gas, _calldataSize, _value);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/DVNOptions.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport { BytesLib } from \"solidity-bytes-utils/contracts/BytesLib.sol\";\n\nimport { BitMap256 } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/BitMaps.sol\";\nimport { CalldataBytesLib } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol\";\n\nlibrary DVNOptions {\n using CalldataBytesLib for bytes;\n using BytesLib for bytes;\n\n uint8 internal constant WORKER_ID = 2;\n uint8 internal constant OPTION_TYPE_PRECRIME = 1;\n\n error DVN_InvalidDVNIdx();\n error DVN_InvalidDVNOptions(uint256 cursor);\n\n /// @dev group dvn options by its idx\n /// @param _options [dvn_id][dvn_option][dvn_id][dvn_option]...\n /// dvn_option = [option_size][dvn_idx][option_type][option]\n /// option_size = len(dvn_idx) + len(option_type) + len(option)\n /// dvn_id: uint8, dvn_idx: uint8, option_size: uint16, option_type: uint8, option: bytes\n /// @return dvnOptions the grouped options, still share the same format of _options\n /// @return dvnIndices the dvn indices\n function groupDVNOptionsByIdx(\n bytes memory _options\n ) internal pure returns (bytes[] memory dvnOptions, uint8[] memory dvnIndices) {\n if (_options.length == 0) return (dvnOptions, dvnIndices);\n\n uint8 numDVNs = getNumDVNs(_options);\n\n // if there is only 1 dvn, we can just return the whole options\n if (numDVNs == 1) {\n dvnOptions = new bytes[](1);\n dvnOptions[0] = _options;\n\n dvnIndices = new uint8[](1);\n dvnIndices[0] = _options.toUint8(3); // dvn idx\n return (dvnOptions, dvnIndices);\n }\n\n // otherwise, we need to group the options by dvn_idx\n dvnIndices = new uint8[](numDVNs);\n dvnOptions = new bytes[](numDVNs);\n unchecked {\n uint256 cursor = 0;\n uint256 start = 0;\n uint8 lastDVNIdx = 255; // 255 is an invalid dvn_idx\n\n while (cursor < _options.length) {\n ++cursor; // skip worker_id\n\n // optionLength asserted in getNumDVNs (skip check)\n uint16 optionLength = _options.toUint16(cursor);\n cursor += 2;\n\n // dvnIdx asserted in getNumDVNs (skip check)\n uint8 dvnIdx = _options.toUint8(cursor);\n\n // dvnIdx must equal to the lastDVNIdx for the first option\n // so it is always skipped in the first option\n // this operation slices out options whenever the scan finds a different lastDVNIdx\n if (lastDVNIdx == 255) {\n lastDVNIdx = dvnIdx;\n } else if (dvnIdx != lastDVNIdx) {\n uint256 len = cursor - start - 3; // 3 is for worker_id and option_length\n bytes memory opt = _options.slice(start, len);\n _insertDVNOptions(dvnOptions, dvnIndices, lastDVNIdx, opt);\n\n // reset the start and lastDVNIdx\n start += len;\n lastDVNIdx = dvnIdx;\n }\n\n cursor += optionLength;\n }\n\n // skip check the cursor here because the cursor is asserted in getNumDVNs\n // if we have reached the end of the options, we need to process the last dvn\n uint256 size = cursor - start;\n bytes memory op = _options.slice(start, size);\n _insertDVNOptions(dvnOptions, dvnIndices, lastDVNIdx, op);\n\n // revert dvnIndices to start from 0\n for (uint8 i = 0; i < numDVNs; ++i) {\n --dvnIndices[i];\n }\n }\n }\n\n function _insertDVNOptions(\n bytes[] memory _dvnOptions,\n uint8[] memory _dvnIndices,\n uint8 _dvnIdx,\n bytes memory _newOptions\n ) internal pure {\n // dvnIdx starts from 0 but default value of dvnIndices is 0,\n // so we tell if the slot is empty by adding 1 to dvnIdx\n if (_dvnIdx == 255) revert DVN_InvalidDVNIdx();\n uint8 dvnIdxAdj = _dvnIdx + 1;\n\n for (uint256 j = 0; j < _dvnIndices.length; ++j) {\n uint8 index = _dvnIndices[j];\n if (dvnIdxAdj == index) {\n _dvnOptions[j] = abi.encodePacked(_dvnOptions[j], _newOptions);\n break;\n } else if (index == 0) {\n // empty slot, that means it is the first time we see this dvn\n _dvnIndices[j] = dvnIdxAdj;\n _dvnOptions[j] = _newOptions;\n break;\n }\n }\n }\n\n /// @dev get the number of unique dvns\n /// @param _options the format is the same as groupDVNOptionsByIdx\n function getNumDVNs(bytes memory _options) internal pure returns (uint8 numDVNs) {\n uint256 cursor = 0;\n BitMap256 bitmap;\n\n // find number of unique dvn_idx\n unchecked {\n while (cursor < _options.length) {\n ++cursor; // skip worker_id\n\n uint16 optionLength = _options.toUint16(cursor);\n cursor += 2;\n if (optionLength < 2) revert DVN_InvalidDVNOptions(cursor); // at least 1 byte for dvn_idx and 1 byte for option_type\n\n uint8 dvnIdx = _options.toUint8(cursor);\n\n // if dvnIdx is not set, increment numDVNs\n // max num of dvns is 255, 255 is an invalid dvn_idx\n // The order of the dvnIdx is not required to be sequential, as enforcing the order may weaken\n // the composability of the options. e.g. if we refrain from enforcing the order, an OApp that has\n // already enforced certain options can append additional options to the end of the enforced\n // ones without restrictions.\n if (dvnIdx == 255) revert DVN_InvalidDVNIdx();\n if (!bitmap.get(dvnIdx)) {\n ++numDVNs;\n bitmap = bitmap.set(dvnIdx);\n }\n\n cursor += optionLength;\n }\n }\n if (cursor != _options.length) revert DVN_InvalidDVNOptions(cursor);\n }\n\n /// @dev decode the next dvn option from _options starting from the specified cursor\n /// @param _options the format is the same as groupDVNOptionsByIdx\n /// @param _cursor the cursor to start decoding\n /// @return optionType the type of the option\n /// @return option the option\n /// @return cursor the cursor to start decoding the next option\n function nextDVNOption(\n bytes calldata _options,\n uint256 _cursor\n ) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) {\n unchecked {\n // skip worker id\n cursor = _cursor + 1;\n\n // read option size\n uint16 size = _options.toU16(cursor);\n cursor += 2;\n\n // read option type\n optionType = _options.toU8(cursor + 1); // skip dvn_idx\n\n // startCursor and endCursor are used to slice the option from _options\n uint256 startCursor = cursor + 2; // skip option type and dvn_idx\n uint256 endCursor = cursor + size;\n option = _options[startCursor:endCursor];\n cursor += size;\n }\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { IMessageLibManager } from \"./IMessageLibManager.sol\";\nimport { IMessagingComposer } from \"./IMessagingComposer.sol\";\nimport { IMessagingChannel } from \"./IMessagingChannel.sol\";\nimport { IMessagingContext } from \"./IMessagingContext.sol\";\n\nstruct MessagingParams {\n uint32 dstEid;\n bytes32 receiver;\n bytes message;\n bytes options;\n bool payInLzToken;\n}\n\nstruct MessagingReceipt {\n bytes32 guid;\n uint64 nonce;\n MessagingFee fee;\n}\n\nstruct MessagingFee {\n uint256 nativeFee;\n uint256 lzTokenFee;\n}\n\nstruct Origin {\n uint32 srcEid;\n bytes32 sender;\n uint64 nonce;\n}\n\ninterface ILayerZeroEndpointV2 is IMessageLibManager, IMessagingComposer, IMessagingChannel, IMessagingContext {\n event PacketSent(bytes encodedPayload, bytes options, address sendLibrary);\n\n event PacketVerified(Origin origin, address receiver, bytes32 payloadHash);\n\n event PacketDelivered(Origin origin, address receiver);\n\n event LzReceiveAlert(\n address indexed receiver,\n address indexed executor,\n Origin origin,\n bytes32 guid,\n uint256 gas,\n uint256 value,\n bytes message,\n bytes extraData,\n bytes reason\n );\n\n event LzTokenSet(address token);\n\n event DelegateSet(address sender, address delegate);\n\n function quote(MessagingParams calldata _params, address _sender) external view returns (MessagingFee memory);\n\n function send(\n MessagingParams calldata _params,\n address _refundAddress\n ) external payable returns (MessagingReceipt memory);\n\n function verify(Origin calldata _origin, address _receiver, bytes32 _payloadHash) external;\n\n function verifiable(Origin calldata _origin, address _receiver) external view returns (bool);\n\n function initializable(Origin calldata _origin, address _receiver) external view returns (bool);\n\n function lzReceive(\n Origin calldata _origin,\n address _receiver,\n bytes32 _guid,\n bytes calldata _message,\n bytes calldata _extraData\n ) external payable;\n\n // oapp can burn messages partially by calling this function with its own business logic if messages are verified in order\n function clear(address _oapp, Origin calldata _origin, bytes32 _guid, bytes calldata _message) external;\n\n function setLzToken(address _lzToken) external;\n\n function lzToken() external view returns (address);\n\n function nativeToken() external view returns (address);\n\n function setDelegate(address _delegate) external;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { Origin } from \"./ILayerZeroEndpointV2.sol\";\n\ninterface ILayerZeroReceiver {\n function allowInitializePath(Origin calldata _origin) external view returns (bool);\n\n function nextNonce(uint32 _eid, bytes32 _sender) external view returns (uint64);\n\n function lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) external payable;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessageLib.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { IERC165 } from \"@openzeppelin/contracts/utils/introspection/IERC165.sol\";\n\nimport { SetConfigParam } from \"./IMessageLibManager.sol\";\n\nenum MessageLibType {\n Send,\n Receive,\n SendAndReceive\n}\n\ninterface IMessageLib is IERC165 {\n function setConfig(address _oapp, SetConfigParam[] calldata _config) external;\n\n function getConfig(uint32 _eid, address _oapp, uint32 _configType) external view returns (bytes memory config);\n\n function isSupportedEid(uint32 _eid) external view returns (bool);\n\n // message libs of same major version are compatible\n function version() external view returns (uint64 major, uint8 minor, uint8 endpointVersion);\n\n function messageLibType() external view returns (MessageLibType);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessageLibManager.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nstruct SetConfigParam {\n uint32 eid;\n uint32 configType;\n bytes config;\n}\n\ninterface IMessageLibManager {\n struct Timeout {\n address lib;\n uint256 expiry;\n }\n\n event LibraryRegistered(address newLib);\n event DefaultSendLibrarySet(uint32 eid, address newLib);\n event DefaultReceiveLibrarySet(uint32 eid, address newLib);\n event DefaultReceiveLibraryTimeoutSet(uint32 eid, address oldLib, uint256 expiry);\n event SendLibrarySet(address sender, uint32 eid, address newLib);\n event ReceiveLibrarySet(address receiver, uint32 eid, address newLib);\n event ReceiveLibraryTimeoutSet(address receiver, uint32 eid, address oldLib, uint256 timeout);\n\n function registerLibrary(address _lib) external;\n\n function isRegisteredLibrary(address _lib) external view returns (bool);\n\n function getRegisteredLibraries() external view returns (address[] memory);\n\n function setDefaultSendLibrary(uint32 _eid, address _newLib) external;\n\n function defaultSendLibrary(uint32 _eid) external view returns (address);\n\n function setDefaultReceiveLibrary(uint32 _eid, address _newLib, uint256 _gracePeriod) external;\n\n function defaultReceiveLibrary(uint32 _eid) external view returns (address);\n\n function setDefaultReceiveLibraryTimeout(uint32 _eid, address _lib, uint256 _expiry) external;\n\n function defaultReceiveLibraryTimeout(uint32 _eid) external view returns (address lib, uint256 expiry);\n\n function isSupportedEid(uint32 _eid) external view returns (bool);\n\n function isValidReceiveLibrary(address _receiver, uint32 _eid, address _lib) external view returns (bool);\n\n /// ------------------- OApp interfaces -------------------\n function setSendLibrary(address _oapp, uint32 _eid, address _newLib) external;\n\n function getSendLibrary(address _sender, uint32 _eid) external view returns (address lib);\n\n function isDefaultSendLibrary(address _sender, uint32 _eid) external view returns (bool);\n\n function setReceiveLibrary(address _oapp, uint32 _eid, address _newLib, uint256 _gracePeriod) external;\n\n function getReceiveLibrary(address _receiver, uint32 _eid) external view returns (address lib, bool isDefault);\n\n function setReceiveLibraryTimeout(address _oapp, uint32 _eid, address _lib, uint256 _expiry) external;\n\n function receiveLibraryTimeout(address _receiver, uint32 _eid) external view returns (address lib, uint256 expiry);\n\n function setConfig(address _oapp, address _lib, SetConfigParam[] calldata _params) external;\n\n function getConfig(\n address _oapp,\n address _lib,\n uint32 _eid,\n uint32 _configType\n ) external view returns (bytes memory config);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingChannel.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingChannel {\n event InboundNonceSkipped(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce);\n event PacketNilified(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);\n event PacketBurnt(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);\n\n function eid() external view returns (uint32);\n\n // this is an emergency function if a message cannot be verified for some reasons\n // required to provide _nextNonce to avoid race condition\n function skip(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce) external;\n\n function nilify(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;\n\n function burn(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;\n\n function nextGuid(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (bytes32);\n\n function inboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);\n\n function outboundNonce(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (uint64);\n\n function inboundPayloadHash(\n address _receiver,\n uint32 _srcEid,\n bytes32 _sender,\n uint64 _nonce\n ) external view returns (bytes32);\n\n function lazyInboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingComposer.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingComposer {\n event ComposeSent(address from, address to, bytes32 guid, uint16 index, bytes message);\n event ComposeDelivered(address from, address to, bytes32 guid, uint16 index);\n event LzComposeAlert(\n address indexed from,\n address indexed to,\n address indexed executor,\n bytes32 guid,\n uint16 index,\n uint256 gas,\n uint256 value,\n bytes message,\n bytes extraData,\n bytes reason\n );\n\n function composeQueue(\n address _from,\n address _to,\n bytes32 _guid,\n uint16 _index\n ) external view returns (bytes32 messageHash);\n\n function sendCompose(address _to, bytes32 _guid, uint16 _index, bytes calldata _message) external;\n\n function lzCompose(\n address _from,\n address _to,\n bytes32 _guid,\n uint16 _index,\n bytes calldata _message,\n bytes calldata _extraData\n ) external payable;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingContext.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingContext {\n function isSendingMessage() external view returns (bool);\n\n function getSendContext() external view returns (uint32 dstEid, address sender);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ISendLib.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { MessagingFee } from \"./ILayerZeroEndpointV2.sol\";\nimport { IMessageLib } from \"./IMessageLib.sol\";\n\nstruct Packet {\n uint64 nonce;\n uint32 srcEid;\n address sender;\n uint32 dstEid;\n bytes32 receiver;\n bytes32 guid;\n bytes message;\n}\n\ninterface ISendLib is IMessageLib {\n function send(\n Packet calldata _packet,\n bytes calldata _options,\n bool _payInLzToken\n ) external returns (MessagingFee memory, bytes memory encodedPacket);\n\n function quote(\n Packet calldata _packet,\n bytes calldata _options,\n bool _payInLzToken\n ) external view returns (MessagingFee memory);\n\n function setTreasury(address _treasury) external;\n\n function withdrawFee(address _to, uint256 _amount) external;\n\n function withdrawLzTokenFee(address _lzToken, address _to, uint256 _amount) external;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/AddressCast.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nlibrary AddressCast {\n error AddressCast_InvalidSizeForAddress();\n error AddressCast_InvalidAddress();\n\n function toBytes32(bytes calldata _addressBytes) internal pure returns (bytes32 result) {\n if (_addressBytes.length > 32) revert AddressCast_InvalidAddress();\n result = bytes32(_addressBytes);\n unchecked {\n uint256 offset = 32 - _addressBytes.length;\n result = result >> (offset * 8);\n }\n }\n\n function toBytes32(address _address) internal pure returns (bytes32 result) {\n result = bytes32(uint256(uint160(_address)));\n }\n\n function toBytes(bytes32 _addressBytes32, uint256 _size) internal pure returns (bytes memory result) {\n if (_size == 0 || _size > 32) revert AddressCast_InvalidSizeForAddress();\n result = new bytes(_size);\n unchecked {\n uint256 offset = 256 - _size * 8;\n assembly {\n mstore(add(result, 32), shl(offset, _addressBytes32))\n }\n }\n }\n\n function toAddress(bytes32 _addressBytes32) internal pure returns (address result) {\n result = address(uint160(uint256(_addressBytes32)));\n }\n\n function toAddress(bytes calldata _addressBytes) internal pure returns (address result) {\n if (_addressBytes.length != 20) revert AddressCast_InvalidAddress();\n result = address(bytes20(_addressBytes));\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nlibrary CalldataBytesLib {\n function toU8(bytes calldata _bytes, uint256 _start) internal pure returns (uint8) {\n return uint8(_bytes[_start]);\n }\n\n function toU16(bytes calldata _bytes, uint256 _start) internal pure returns (uint16) {\n unchecked {\n uint256 end = _start + 2;\n return uint16(bytes2(_bytes[_start:end]));\n }\n }\n\n function toU32(bytes calldata _bytes, uint256 _start) internal pure returns (uint32) {\n unchecked {\n uint256 end = _start + 4;\n return uint32(bytes4(_bytes[_start:end]));\n }\n }\n\n function toU64(bytes calldata _bytes, uint256 _start) internal pure returns (uint64) {\n unchecked {\n uint256 end = _start + 8;\n return uint64(bytes8(_bytes[_start:end]));\n }\n }\n\n function toU128(bytes calldata _bytes, uint256 _start) internal pure returns (uint128) {\n unchecked {\n uint256 end = _start + 16;\n return uint128(bytes16(_bytes[_start:end]));\n }\n }\n\n function toU256(bytes calldata _bytes, uint256 _start) internal pure returns (uint256) {\n unchecked {\n uint256 end = _start + 32;\n return uint256(bytes32(_bytes[_start:end]));\n }\n }\n\n function toAddr(bytes calldata _bytes, uint256 _start) internal pure returns (address) {\n unchecked {\n uint256 end = _start + 20;\n return address(bytes20(_bytes[_start:end]));\n }\n }\n\n function toB32(bytes calldata _bytes, uint256 _start) internal pure returns (bytes32) {\n unchecked {\n uint256 end = _start + 32;\n return bytes32(_bytes[_start:end]);\n }\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/BitMaps.sol": { + "content": "// SPDX-License-Identifier: MIT\n\n// modified from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/structs/BitMaps.sol\npragma solidity ^0.8.20;\n\ntype BitMap256 is uint256;\n\nusing BitMaps for BitMap256 global;\n\nlibrary BitMaps {\n /**\n * @dev Returns whether the bit at `index` is set.\n */\n function get(BitMap256 bitmap, uint8 index) internal pure returns (bool) {\n uint256 mask = 1 << index;\n return BitMap256.unwrap(bitmap) & mask != 0;\n }\n\n /**\n * @dev Sets the bit at `index`.\n */\n function set(BitMap256 bitmap, uint8 index) internal pure returns (BitMap256) {\n uint256 mask = 1 << index;\n return BitMap256.wrap(BitMap256.unwrap(bitmap) | mask);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport { Packet } from \"../../interfaces/ISendLib.sol\";\nimport { AddressCast } from \"../../libs/AddressCast.sol\";\n\nlibrary PacketV1Codec {\n using AddressCast for address;\n using AddressCast for bytes32;\n\n uint8 internal constant PACKET_VERSION = 1;\n\n // header (version + nonce + path)\n // version\n uint256 private constant PACKET_VERSION_OFFSET = 0;\n // nonce\n uint256 private constant NONCE_OFFSET = 1;\n // path\n uint256 private constant SRC_EID_OFFSET = 9;\n uint256 private constant SENDER_OFFSET = 13;\n uint256 private constant DST_EID_OFFSET = 45;\n uint256 private constant RECEIVER_OFFSET = 49;\n // payload (guid + message)\n uint256 private constant GUID_OFFSET = 81; // keccak256(nonce + path)\n uint256 private constant MESSAGE_OFFSET = 113;\n\n function encode(Packet memory _packet) internal pure returns (bytes memory encodedPacket) {\n encodedPacket = abi.encodePacked(\n PACKET_VERSION,\n _packet.nonce,\n _packet.srcEid,\n _packet.sender.toBytes32(),\n _packet.dstEid,\n _packet.receiver,\n _packet.guid,\n _packet.message\n );\n }\n\n function encodePacketHeader(Packet memory _packet) internal pure returns (bytes memory) {\n return\n abi.encodePacked(\n PACKET_VERSION,\n _packet.nonce,\n _packet.srcEid,\n _packet.sender.toBytes32(),\n _packet.dstEid,\n _packet.receiver\n );\n }\n\n function encodePayload(Packet memory _packet) internal pure returns (bytes memory) {\n return abi.encodePacked(_packet.guid, _packet.message);\n }\n\n function header(bytes calldata _packet) internal pure returns (bytes calldata) {\n return _packet[0:GUID_OFFSET];\n }\n\n function version(bytes calldata _packet) internal pure returns (uint8) {\n return uint8(bytes1(_packet[PACKET_VERSION_OFFSET:NONCE_OFFSET]));\n }\n\n function nonce(bytes calldata _packet) internal pure returns (uint64) {\n return uint64(bytes8(_packet[NONCE_OFFSET:SRC_EID_OFFSET]));\n }\n\n function srcEid(bytes calldata _packet) internal pure returns (uint32) {\n return uint32(bytes4(_packet[SRC_EID_OFFSET:SENDER_OFFSET]));\n }\n\n function sender(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[SENDER_OFFSET:DST_EID_OFFSET]);\n }\n\n function senderAddressB20(bytes calldata _packet) internal pure returns (address) {\n return sender(_packet).toAddress();\n }\n\n function dstEid(bytes calldata _packet) internal pure returns (uint32) {\n return uint32(bytes4(_packet[DST_EID_OFFSET:RECEIVER_OFFSET]));\n }\n\n function receiver(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[RECEIVER_OFFSET:GUID_OFFSET]);\n }\n\n function receiverB20(bytes calldata _packet) internal pure returns (address) {\n return receiver(_packet).toAddress();\n }\n\n function guid(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[GUID_OFFSET:MESSAGE_OFFSET]);\n }\n\n function message(bytes calldata _packet) internal pure returns (bytes calldata) {\n return bytes(_packet[MESSAGE_OFFSET:]);\n }\n\n function payload(bytes calldata _packet) internal pure returns (bytes calldata) {\n return bytes(_packet[GUID_OFFSET:]);\n }\n\n function payloadHash(bytes calldata _packet) internal pure returns (bytes32) {\n return keccak256(payload(_packet));\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { ILayerZeroEndpointV2 } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\n\n/**\n * @title IOAppCore\n */\ninterface IOAppCore {\n // Custom error messages\n error OnlyPeer(uint32 eid, bytes32 sender);\n error NoPeer(uint32 eid);\n error InvalidEndpointCall();\n error InvalidDelegate();\n\n // Event emitted when a peer (OApp) is set for a corresponding endpoint\n event PeerSet(uint32 eid, bytes32 peer);\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n */\n function oAppVersion() external view returns (uint64 senderVersion, uint64 receiverVersion);\n\n /**\n * @notice Retrieves the LayerZero endpoint associated with the OApp.\n * @return iEndpoint The LayerZero endpoint as an interface.\n */\n function endpoint() external view returns (ILayerZeroEndpointV2 iEndpoint);\n\n /**\n * @notice Retrieves the peer (OApp) associated with a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @return peer The peer address (OApp instance) associated with the corresponding endpoint.\n */\n function peers(uint32 _eid) external view returns (bytes32 peer);\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n */\n function setPeer(uint32 _eid, bytes32 _peer) external;\n\n /**\n * @notice Sets the delegate address for the OApp Core.\n * @param _delegate The address of the delegate to be set.\n */\n function setDelegate(address _delegate) external;\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppMsgInspector.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n/**\n * @title IOAppMsgInspector\n * @dev Interface for the OApp Message Inspector, allowing examination of message and options contents.\n */\ninterface IOAppMsgInspector {\n // Custom error message for inspection failure\n error InspectionFailed(bytes message, bytes options);\n\n /**\n * @notice Allows the inspector to examine LayerZero message contents and optionally throw a revert if invalid.\n * @param _message The message payload to be inspected.\n * @param _options Additional options or parameters for inspection.\n * @return valid A boolean indicating whether the inspection passed (true) or failed (false).\n *\n * @dev Optionally done as a revert, OR use the boolean provided to handle the failure.\n */\n function inspect(bytes calldata _message, bytes calldata _options) external view returns (bool valid);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppOptionsType3.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Struct representing enforced option parameters.\n */\nstruct EnforcedOptionParam {\n uint32 eid; // Endpoint ID\n uint16 msgType; // Message Type\n bytes options; // Additional options\n}\n\n/**\n * @title IOAppOptionsType3\n * @dev Interface for the OApp with Type 3 Options, allowing the setting and combining of enforced options.\n */\ninterface IOAppOptionsType3 {\n // Custom error message for invalid options\n error InvalidOptions(bytes options);\n\n // Event emitted when enforced options are set\n event EnforcedOptionSet(EnforcedOptionParam[] _enforcedOptions);\n\n /**\n * @notice Sets enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n */\n function setEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) external;\n\n /**\n * @notice Combines options for a given endpoint and message type.\n * @param _eid The endpoint ID.\n * @param _msgType The OApp message type.\n * @param _extraOptions Additional options passed by the caller.\n * @return options The combination of caller specified options AND enforced options.\n */\n function combineOptions(\n uint32 _eid,\n uint16 _msgType,\n bytes calldata _extraOptions\n ) external view returns (bytes memory options);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\nimport { ILayerZeroReceiver, Origin } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroReceiver.sol\";\n\ninterface IOAppReceiver is ILayerZeroReceiver {\n /**\n * @notice Indicates whether an address is an approved composeMsg sender to the Endpoint.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _message The lzReceive payload.\n * @param _sender The sender address.\n * @return isSender Is a valid sender.\n *\n * @dev Applications can optionally choose to implement a separate composeMsg sender that is NOT the bridging layer.\n * @dev The default sender IS the OAppReceiver implementer.\n */\n function isComposeMsgSender(\n Origin calldata _origin,\n bytes calldata _message,\n address _sender\n ) external view returns (bool isSender);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IOAppOptionsType3, EnforcedOptionParam } from \"../interfaces/IOAppOptionsType3.sol\";\n\n/**\n * @title OAppOptionsType3\n * @dev Abstract contract implementing the IOAppOptionsType3 interface with type 3 options.\n */\nabstract contract OAppOptionsType3 is IOAppOptionsType3, Ownable {\n uint16 internal constant OPTION_TYPE_3 = 3;\n\n // @dev The \"msgType\" should be defined in the child contract.\n mapping(uint32 eid => mapping(uint16 msgType => bytes enforcedOption)) public enforcedOptions;\n\n /**\n * @dev Sets the enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Provides a way for the OApp to enforce things like paying for PreCrime, AND/OR minimum dst lzReceive gas amounts etc.\n * @dev These enforced options can vary as the potential options/execution on the remote may differ as per the msgType.\n * eg. Amount of lzReceive() gas necessary to deliver a lzCompose() message adds overhead you dont want to pay\n * if you are only making a standard LayerZero message ie. lzReceive() WITHOUT sendCompose().\n */\n function setEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) public virtual onlyOwner {\n _setEnforcedOptions(_enforcedOptions);\n }\n\n /**\n * @dev Sets the enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n *\n * @dev Provides a way for the OApp to enforce things like paying for PreCrime, AND/OR minimum dst lzReceive gas amounts etc.\n * @dev These enforced options can vary as the potential options/execution on the remote may differ as per the msgType.\n * eg. Amount of lzReceive() gas necessary to deliver a lzCompose() message adds overhead you dont want to pay\n * if you are only making a standard LayerZero message ie. lzReceive() WITHOUT sendCompose().\n */\n function _setEnforcedOptions(EnforcedOptionParam[] memory _enforcedOptions) internal virtual {\n for (uint256 i = 0; i < _enforcedOptions.length; i++) {\n // @dev Enforced options are only available for optionType 3, as type 1 and 2 dont support combining.\n _assertOptionsType3(_enforcedOptions[i].options);\n enforcedOptions[_enforcedOptions[i].eid][_enforcedOptions[i].msgType] = _enforcedOptions[i].options;\n }\n\n emit EnforcedOptionSet(_enforcedOptions);\n }\n\n /**\n * @notice Combines options for a given endpoint and message type.\n * @param _eid The endpoint ID.\n * @param _msgType The OAPP message type.\n * @param _extraOptions Additional options passed by the caller.\n * @return options The combination of caller specified options AND enforced options.\n *\n * @dev If there is an enforced lzReceive option:\n * - {gasLimit: 200k, msg.value: 1 ether} AND a caller supplies a lzReceive option: {gasLimit: 100k, msg.value: 0.5 ether}\n * - The resulting options will be {gasLimit: 300k, msg.value: 1.5 ether} when the message is executed on the remote lzReceive() function.\n * @dev This presence of duplicated options is handled off-chain in the verifier/executor.\n */\n function combineOptions(\n uint32 _eid,\n uint16 _msgType,\n bytes calldata _extraOptions\n ) public view virtual returns (bytes memory) {\n bytes memory enforced = enforcedOptions[_eid][_msgType];\n\n // No enforced options, pass whatever the caller supplied, even if it's empty or legacy type 1/2 options.\n if (enforced.length == 0) return _extraOptions;\n\n // No caller options, return enforced\n if (_extraOptions.length == 0) return enforced;\n\n // @dev If caller provided _extraOptions, must be type 3 as its the ONLY type that can be combined.\n if (_extraOptions.length >= 2) {\n _assertOptionsType3(_extraOptions);\n // @dev Remove the first 2 bytes containing the type from the _extraOptions and combine with enforced.\n return bytes.concat(enforced, _extraOptions[2:]);\n }\n\n // No valid set of options was found.\n revert InvalidOptions(_extraOptions);\n }\n\n /**\n * @dev Internal function to assert that options are of type 3.\n * @param _options The options to be checked.\n */\n function _assertOptionsType3(bytes memory _options) internal pure virtual {\n uint16 optionsType;\n assembly {\n optionsType := mload(add(_options, 2))\n }\n if (optionsType != OPTION_TYPE_3) revert InvalidOptions(_options);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { BytesLib } from \"solidity-bytes-utils/contracts/BytesLib.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { ExecutorOptions } from \"@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol\";\nimport { DVNOptions } from \"@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/DVNOptions.sol\";\n\n/**\n * @title OptionsBuilder\n * @dev Library for building and encoding various message options.\n */\nlibrary OptionsBuilder {\n using SafeCast for uint256;\n using BytesLib for bytes;\n\n // Constants for options types\n uint16 internal constant TYPE_1 = 1; // legacy options type 1\n uint16 internal constant TYPE_2 = 2; // legacy options type 2\n uint16 internal constant TYPE_3 = 3;\n\n // Custom error message\n error InvalidSize(uint256 max, uint256 actual);\n error InvalidOptionType(uint16 optionType);\n\n // Modifier to ensure only options of type 3 are used\n modifier onlyType3(bytes memory _options) {\n if (_options.toUint16(0) != TYPE_3) revert InvalidOptionType(_options.toUint16(0));\n _;\n }\n\n /**\n * @dev Creates a new options container with type 3.\n * @return options The newly created options container.\n */\n function newOptions() internal pure returns (bytes memory) {\n return abi.encodePacked(TYPE_3);\n }\n\n /**\n * @dev Adds an executor LZ receive option to the existing options.\n * @param _options The existing options container.\n * @param _gas The gasLimit used on the lzReceive() function in the OApp.\n * @param _value The msg.value passed to the lzReceive() function in the OApp.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed by the executor\n * eg. if (_gas: 200k, and _value: 1 ether) AND (_gas: 100k, _value: 0.5 ether) are sent in an option to the LayerZeroEndpoint,\n * that becomes (300k, 1.5 ether) when the message is executed on the remote lzReceive() function.\n */\n function addExecutorLzReceiveOption(\n bytes memory _options,\n uint128 _gas,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzReceiveOption(_gas, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZRECEIVE, option);\n }\n\n /**\n * @dev Adds an executor native drop option to the existing options.\n * @param _options The existing options container.\n * @param _amount The amount for the native value that is airdropped to the 'receiver'.\n * @param _receiver The receiver address for the native drop option.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed by the executor on the remote chain.\n */\n function addExecutorNativeDropOption(\n bytes memory _options,\n uint128 _amount,\n bytes32 _receiver\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeNativeDropOption(_amount, _receiver);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_NATIVE_DROP, option);\n }\n\n // /**\n // * @dev Adds an executor native drop option to the existing options.\n // * @param _options The existing options container.\n // * @param _amount The amount for the native value that is airdropped to the 'receiver'.\n // * @param _receiver The receiver address for the native drop option.\n // * @return options The updated options container.\n // *\n // * @dev When multiples of this option are added, they are summed by the executor on the remote chain.\n // */\n function addExecutorLzReadOption(\n bytes memory _options,\n uint128 _gas,\n uint32 _size,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzReadOption(_gas, _size, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZREAD, option);\n }\n\n /**\n * @dev Adds an executor LZ compose option to the existing options.\n * @param _options The existing options container.\n * @param _index The index for the lzCompose() function call.\n * @param _gas The gasLimit for the lzCompose() function call.\n * @param _value The msg.value for the lzCompose() function call.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed PER index by the executor on the remote chain.\n * @dev If the OApp sends N lzCompose calls on the remote, you must provide N incremented indexes starting with 0.\n * ie. When your remote OApp composes (N = 3) messages, you must set this option for index 0,1,2\n */\n function addExecutorLzComposeOption(\n bytes memory _options,\n uint16 _index,\n uint128 _gas,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzComposeOption(_index, _gas, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZCOMPOSE, option);\n }\n\n /**\n * @dev Adds an executor ordered execution option to the existing options.\n * @param _options The existing options container.\n * @return options The updated options container.\n */\n function addExecutorOrderedExecutionOption(\n bytes memory _options\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_ORDERED_EXECUTION, bytes(\"\"));\n }\n\n /**\n * @dev Adds a DVN pre-crime option to the existing options.\n * @param _options The existing options container.\n * @param _dvnIdx The DVN index for the pre-crime option.\n * @return options The updated options container.\n */\n function addDVNPreCrimeOption(\n bytes memory _options,\n uint8 _dvnIdx\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return addDVNOption(_options, _dvnIdx, DVNOptions.OPTION_TYPE_PRECRIME, bytes(\"\"));\n }\n\n /**\n * @dev Adds an executor option to the existing options.\n * @param _options The existing options container.\n * @param _optionType The type of the executor option.\n * @param _option The encoded data for the executor option.\n * @return options The updated options container.\n */\n function addExecutorOption(\n bytes memory _options,\n uint8 _optionType,\n bytes memory _option\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return\n abi.encodePacked(\n _options,\n ExecutorOptions.WORKER_ID,\n _option.length.toUint16() + 1, // +1 for optionType\n _optionType,\n _option\n );\n }\n\n /**\n * @dev Adds a DVN option to the existing options.\n * @param _options The existing options container.\n * @param _dvnIdx The DVN index for the DVN option.\n * @param _optionType The type of the DVN option.\n * @param _option The encoded data for the DVN option.\n * @return options The updated options container.\n */\n function addDVNOption(\n bytes memory _options,\n uint8 _dvnIdx,\n uint8 _optionType,\n bytes memory _option\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return\n abi.encodePacked(\n _options,\n DVNOptions.WORKER_ID,\n _option.length.toUint16() + 2, // +2 for optionType and dvnIdx\n _dvnIdx,\n _optionType,\n _option\n );\n }\n\n /**\n * @dev Encodes legacy options of type 1.\n * @param _executionGas The gasLimit value passed to lzReceive().\n * @return legacyOptions The encoded legacy options.\n */\n function encodeLegacyOptionsType1(uint256 _executionGas) internal pure returns (bytes memory) {\n if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);\n return abi.encodePacked(TYPE_1, _executionGas);\n }\n\n /**\n * @dev Encodes legacy options of type 2.\n * @param _executionGas The gasLimit value passed to lzReceive().\n * @param _nativeForDst The amount of native air dropped to the receiver.\n * @param _receiver The _nativeForDst receiver address.\n * @return legacyOptions The encoded legacy options of type 2.\n */\n function encodeLegacyOptionsType2(\n uint256 _executionGas,\n uint256 _nativeForDst,\n bytes memory _receiver // @dev Use bytes instead of bytes32 in legacy type 2 for _receiver.\n ) internal pure returns (bytes memory) {\n if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);\n if (_nativeForDst > type(uint128).max) revert InvalidSize(type(uint128).max, _nativeForDst);\n if (_receiver.length > 32) revert InvalidSize(32, _receiver.length);\n return abi.encodePacked(TYPE_2, _executionGas, _nativeForDst, _receiver);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n// @dev Import the 'MessagingFee' and 'MessagingReceipt' so it's exposed to OApp implementers\n// solhint-disable-next-line no-unused-import\nimport { OAppSender, MessagingFee, MessagingReceipt } from \"./OAppSender.sol\";\n// @dev Import the 'Origin' so it's exposed to OApp implementers\n// solhint-disable-next-line no-unused-import\nimport { OAppReceiver, Origin } from \"./OAppReceiver.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OApp\n * @dev Abstract contract serving as the base for OApp implementation, combining OAppSender and OAppReceiver functionality.\n */\nabstract contract OApp is OAppSender, OAppReceiver {\n /**\n * @dev Constructor to initialize the OApp with the provided endpoint and owner.\n * @param _endpoint The address of the LOCAL LayerZero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(address _endpoint, address _delegate) OAppCore(_endpoint, _delegate) {}\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol implementation.\n * @return receiverVersion The version of the OAppReceiver.sol implementation.\n */\n function oAppVersion()\n public\n pure\n virtual\n override(OAppSender, OAppReceiver)\n returns (uint64 senderVersion, uint64 receiverVersion)\n {\n return (SENDER_VERSION, RECEIVER_VERSION);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IOAppCore, ILayerZeroEndpointV2 } from \"./interfaces/IOAppCore.sol\";\n\n/**\n * @title OAppCore\n * @dev Abstract contract implementing the IOAppCore interface with basic OApp configurations.\n */\nabstract contract OAppCore is IOAppCore, Ownable {\n // The LayerZero endpoint associated with the given OApp\n ILayerZeroEndpointV2 public immutable endpoint;\n\n // Mapping to store peers associated with corresponding endpoints\n mapping(uint32 eid => bytes32 peer) public peers;\n\n /**\n * @dev Constructor to initialize the OAppCore with the provided endpoint and delegate.\n * @param _endpoint The address of the LOCAL Layer Zero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n *\n * @dev The delegate typically should be set as the owner of the contract.\n */\n constructor(address _endpoint, address _delegate) {\n endpoint = ILayerZeroEndpointV2(_endpoint);\n\n if (_delegate == address(0)) revert InvalidDelegate();\n endpoint.setDelegate(_delegate);\n }\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.\n * @dev Set this to bytes32(0) to remove the peer address.\n * @dev Peer is a bytes32 to accommodate non-evm chains.\n */\n function setPeer(uint32 _eid, bytes32 _peer) public virtual onlyOwner {\n _setPeer(_eid, _peer);\n }\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n *\n * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.\n * @dev Set this to bytes32(0) to remove the peer address.\n * @dev Peer is a bytes32 to accommodate non-evm chains.\n */\n function _setPeer(uint32 _eid, bytes32 _peer) internal virtual {\n peers[_eid] = _peer;\n emit PeerSet(_eid, _peer);\n }\n\n /**\n * @notice Internal function to get the peer address associated with a specific endpoint; reverts if NOT set.\n * ie. the peer is set to bytes32(0).\n * @param _eid The endpoint ID.\n * @return peer The address of the peer associated with the specified endpoint.\n */\n function _getPeerOrRevert(uint32 _eid) internal view virtual returns (bytes32) {\n bytes32 peer = peers[_eid];\n if (peer == bytes32(0)) revert NoPeer(_eid);\n return peer;\n }\n\n /**\n * @notice Sets the delegate address for the OApp.\n * @param _delegate The address of the delegate to be set.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Provides the ability for a delegate to set configs, on behalf of the OApp, directly on the Endpoint contract.\n */\n function setDelegate(address _delegate) public onlyOwner {\n endpoint.setDelegate(_delegate);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IOAppReceiver, Origin } from \"./interfaces/IOAppReceiver.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OAppReceiver\n * @dev Abstract contract implementing the ILayerZeroReceiver interface and extending OAppCore for OApp receivers.\n */\nabstract contract OAppReceiver is IOAppReceiver, OAppCore {\n // Custom error message for when the caller is not the registered endpoint/\n error OnlyEndpoint(address addr);\n\n // @dev The version of the OAppReceiver implementation.\n // @dev Version is bumped when changes are made to this contract.\n uint64 internal constant RECEIVER_VERSION = 2;\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n *\n * @dev Providing 0 as the default for OAppSender version. Indicates that the OAppSender is not implemented.\n * ie. this is a RECEIVE only OApp.\n * @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions.\n */\n function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {\n return (0, RECEIVER_VERSION);\n }\n\n /**\n * @notice Indicates whether an address is an approved composeMsg sender to the Endpoint.\n * @dev _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @dev _message The lzReceive payload.\n * @param _sender The sender address.\n * @return isSender Is a valid sender.\n *\n * @dev Applications can optionally choose to implement separate composeMsg senders that are NOT the bridging layer.\n * @dev The default sender IS the OAppReceiver implementer.\n */\n function isComposeMsgSender(\n Origin calldata /*_origin*/,\n bytes calldata /*_message*/,\n address _sender\n ) public view virtual returns (bool) {\n return _sender == address(this);\n }\n\n /**\n * @notice Checks if the path initialization is allowed based on the provided origin.\n * @param origin The origin information containing the source endpoint and sender address.\n * @return Whether the path has been initialized.\n *\n * @dev This indicates to the endpoint that the OApp has enabled msgs for this particular path to be received.\n * @dev This defaults to assuming if a peer has been set, its initialized.\n * Can be overridden by the OApp if there is other logic to determine this.\n */\n function allowInitializePath(Origin calldata origin) public view virtual returns (bool) {\n return peers[origin.srcEid] == origin.sender;\n }\n\n /**\n * @notice Retrieves the next nonce for a given source endpoint and sender address.\n * @dev _srcEid The source endpoint ID.\n * @dev _sender The sender address.\n * @return nonce The next nonce.\n *\n * @dev The path nonce starts from 1. If 0 is returned it means that there is NO nonce ordered enforcement.\n * @dev Is required by the off-chain executor to determine the OApp expects msg execution is ordered.\n * @dev This is also enforced by the OApp.\n * @dev By default this is NOT enabled. ie. nextNonce is hardcoded to return 0.\n */\n function nextNonce(uint32 /*_srcEid*/, bytes32 /*_sender*/) public view virtual returns (uint64 nonce) {\n return 0;\n }\n\n /**\n * @dev Entry point for receiving messages or packets from the endpoint.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The payload of the received message.\n * @param _executor The address of the executor for the received message.\n * @param _extraData Additional arbitrary data provided by the corresponding executor.\n *\n * @dev Entry point for receiving msg/packet from the LayerZero endpoint.\n */\n function lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) public payable virtual {\n // Ensures that only the endpoint can attempt to lzReceive() messages to this OApp.\n if (address(endpoint) != msg.sender) revert OnlyEndpoint(msg.sender);\n\n // Ensure that the sender matches the expected peer for the source endpoint.\n if (_getPeerOrRevert(_origin.srcEid) != _origin.sender) revert OnlyPeer(_origin.srcEid, _origin.sender);\n\n // Call the internal OApp implementation of lzReceive.\n _lzReceive(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Internal function to implement lzReceive logic without needing to copy the basic parameter validation.\n */\n function _lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual;\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { SafeERC20, IERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { MessagingParams, MessagingFee, MessagingReceipt } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OAppSender\n * @dev Abstract contract implementing the OAppSender functionality for sending messages to a LayerZero endpoint.\n */\nabstract contract OAppSender is OAppCore {\n using SafeERC20 for IERC20;\n\n // Custom error messages\n error NotEnoughNative(uint256 msgValue);\n error LzTokenUnavailable();\n\n // @dev The version of the OAppSender implementation.\n // @dev Version is bumped when changes are made to this contract.\n uint64 internal constant SENDER_VERSION = 1;\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n *\n * @dev Providing 0 as the default for OAppReceiver version. Indicates that the OAppReceiver is not implemented.\n * ie. this is a SEND only OApp.\n * @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions\n */\n function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {\n return (SENDER_VERSION, 0);\n }\n\n /**\n * @dev Internal function to interact with the LayerZero EndpointV2.quote() for fee calculation.\n * @param _dstEid The destination endpoint ID.\n * @param _message The message payload.\n * @param _options Additional options for the message.\n * @param _payInLzToken Flag indicating whether to pay the fee in LZ tokens.\n * @return fee The calculated MessagingFee for the message.\n * - nativeFee: The native fee for the message.\n * - lzTokenFee: The LZ token fee for the message.\n */\n function _quote(\n uint32 _dstEid,\n bytes memory _message,\n bytes memory _options,\n bool _payInLzToken\n ) internal view virtual returns (MessagingFee memory fee) {\n return\n endpoint.quote(\n MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _payInLzToken),\n address(this)\n );\n }\n\n /**\n * @dev Internal function to interact with the LayerZero EndpointV2.send() for sending a message.\n * @param _dstEid The destination endpoint ID.\n * @param _message The message payload.\n * @param _options Additional options for the message.\n * @param _fee The calculated LayerZero fee for the message.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess fee values sent to the endpoint.\n * @return receipt The receipt for the sent message.\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function _lzSend(\n uint32 _dstEid,\n bytes memory _message,\n bytes memory _options,\n MessagingFee memory _fee,\n address _refundAddress\n ) internal virtual returns (MessagingReceipt memory receipt) {\n // @dev Push corresponding fees to the endpoint, any excess is sent back to the _refundAddress from the endpoint.\n uint256 messageValue = _payNative(_fee.nativeFee);\n if (_fee.lzTokenFee > 0) _payLzToken(_fee.lzTokenFee);\n\n return\n // solhint-disable-next-line check-send-result\n endpoint.send{ value: messageValue }(\n MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _fee.lzTokenFee > 0),\n _refundAddress\n );\n }\n\n /**\n * @dev Internal function to pay the native fee associated with the message.\n * @param _nativeFee The native fee to be paid.\n * @return nativeFee The amount of native currency paid.\n *\n * @dev If the OApp needs to initiate MULTIPLE LayerZero messages in a single transaction,\n * this will need to be overridden because msg.value would contain multiple lzFees.\n * @dev Should be overridden in the event the LayerZero endpoint requires a different native currency.\n * @dev Some EVMs use an ERC20 as a method for paying transactions/gasFees.\n * @dev The endpoint is EITHER/OR, ie. it will NOT support both types of native payment at a time.\n */\n function _payNative(uint256 _nativeFee) internal virtual returns (uint256 nativeFee) {\n if (msg.value != _nativeFee) revert NotEnoughNative(msg.value);\n return _nativeFee;\n }\n\n /**\n * @dev Internal function to pay the LZ token fee associated with the message.\n * @param _lzTokenFee The LZ token fee to be paid.\n *\n * @dev If the caller is trying to pay in the specified lzToken, then the lzTokenFee is passed to the endpoint.\n * @dev Any excess sent, is passed back to the specified _refundAddress in the _lzSend().\n */\n function _payLzToken(uint256 _lzTokenFee) internal virtual {\n // @dev Cannot cache the token because it is not immutable in the endpoint.\n address lzToken = endpoint.lzToken();\n if (lzToken == address(0)) revert LzTokenUnavailable();\n\n // Pay LZ token fee by sending tokens to the endpoint.\n IERC20(lzToken).safeTransferFrom(msg.sender, address(endpoint), _lzTokenFee);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/interfaces/IOAppPreCrimeSimulator.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n// @dev Import the Origin so it's exposed to OAppPreCrimeSimulator implementers.\n// solhint-disable-next-line no-unused-import\nimport { InboundPacket, Origin } from \"../libs/Packet.sol\";\n\n/**\n * @title IOAppPreCrimeSimulator Interface\n * @dev Interface for the preCrime simulation functionality in an OApp.\n */\ninterface IOAppPreCrimeSimulator {\n // @dev simulation result used in PreCrime implementation\n error SimulationResult(bytes result);\n error OnlySelf();\n\n /**\n * @dev Emitted when the preCrime contract address is set.\n * @param preCrimeAddress The address of the preCrime contract.\n */\n event PreCrimeSet(address preCrimeAddress);\n\n /**\n * @dev Retrieves the address of the preCrime contract implementation.\n * @return The address of the preCrime contract.\n */\n function preCrime() external view returns (address);\n\n /**\n * @dev Retrieves the address of the OApp contract.\n * @return The address of the OApp contract.\n */\n function oApp() external view returns (address);\n\n /**\n * @dev Sets the preCrime contract address.\n * @param _preCrime The address of the preCrime contract.\n */\n function setPreCrime(address _preCrime) external;\n\n /**\n * @dev Mocks receiving a packet, then reverts with a series of data to infer the state/result.\n * @param _packets An array of LayerZero InboundPacket objects representing received packets.\n */\n function lzReceiveAndRevert(InboundPacket[] calldata _packets) external payable;\n\n /**\n * @dev checks if the specified peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint Id to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n */\n function isPeer(uint32 _eid, bytes32 _peer) external view returns (bool);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/interfaces/IPreCrime.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\nstruct PreCrimePeer {\n uint32 eid;\n bytes32 preCrime;\n bytes32 oApp;\n}\n\n// TODO not done yet\ninterface IPreCrime {\n error OnlyOffChain();\n\n // for simulate()\n error PacketOversize(uint256 max, uint256 actual);\n error PacketUnsorted();\n error SimulationFailed(bytes reason);\n\n // for preCrime()\n error SimulationResultNotFound(uint32 eid);\n error InvalidSimulationResult(uint32 eid, bytes reason);\n error CrimeFound(bytes crime);\n\n function getConfig(bytes[] calldata _packets, uint256[] calldata _packetMsgValues) external returns (bytes memory);\n\n function simulate(\n bytes[] calldata _packets,\n uint256[] calldata _packetMsgValues\n ) external payable returns (bytes memory);\n\n function buildSimulationResult() external view returns (bytes memory);\n\n function preCrime(\n bytes[] calldata _packets,\n uint256[] calldata _packetMsgValues,\n bytes[] calldata _simulations\n ) external;\n\n function version() external view returns (uint64 major, uint8 minor);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/libs/Packet.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Origin } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\nimport { PacketV1Codec } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol\";\n\n/**\n * @title InboundPacket\n * @dev Structure representing an inbound packet received by the contract.\n */\nstruct InboundPacket {\n Origin origin; // Origin information of the packet.\n uint32 dstEid; // Destination endpointId of the packet.\n address receiver; // Receiver address for the packet.\n bytes32 guid; // Unique identifier of the packet.\n uint256 value; // msg.value of the packet.\n address executor; // Executor address for the packet.\n bytes message; // Message payload of the packet.\n bytes extraData; // Additional arbitrary data for the packet.\n}\n\n/**\n * @title PacketDecoder\n * @dev Library for decoding LayerZero packets.\n */\nlibrary PacketDecoder {\n using PacketV1Codec for bytes;\n\n /**\n * @dev Decode an inbound packet from the given packet data.\n * @param _packet The packet data to decode.\n * @return packet An InboundPacket struct representing the decoded packet.\n */\n function decode(bytes calldata _packet) internal pure returns (InboundPacket memory packet) {\n packet.origin = Origin(_packet.srcEid(), _packet.sender(), _packet.nonce());\n packet.dstEid = _packet.dstEid();\n packet.receiver = _packet.receiverB20();\n packet.guid = _packet.guid();\n packet.message = _packet.message();\n }\n\n /**\n * @dev Decode multiple inbound packets from the given packet data and associated message values.\n * @param _packets An array of packet data to decode.\n * @param _packetMsgValues An array of associated message values for each packet.\n * @return packets An array of InboundPacket structs representing the decoded packets.\n */\n function decode(\n bytes[] calldata _packets,\n uint256[] memory _packetMsgValues\n ) internal pure returns (InboundPacket[] memory packets) {\n packets = new InboundPacket[](_packets.length);\n for (uint256 i = 0; i < _packets.length; i++) {\n bytes calldata packet = _packets[i];\n packets[i] = PacketDecoder.decode(packet);\n // @dev Allows the verifier to specify the msg.value that gets passed in lzReceive.\n packets[i].value = _packetMsgValues[i];\n }\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/OAppPreCrimeSimulator.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IPreCrime } from \"./interfaces/IPreCrime.sol\";\nimport { IOAppPreCrimeSimulator, InboundPacket, Origin } from \"./interfaces/IOAppPreCrimeSimulator.sol\";\n\n/**\n * @title OAppPreCrimeSimulator\n * @dev Abstract contract serving as the base for preCrime simulation functionality in an OApp.\n */\nabstract contract OAppPreCrimeSimulator is IOAppPreCrimeSimulator, Ownable {\n // The address of the preCrime implementation.\n address public preCrime;\n\n /**\n * @dev Retrieves the address of the OApp contract.\n * @return The address of the OApp contract.\n *\n * @dev The simulator contract is the base contract for the OApp by default.\n * @dev If the simulator is a separate contract, override this function.\n */\n function oApp() external view virtual returns (address) {\n return address(this);\n }\n\n /**\n * @dev Sets the preCrime contract address.\n * @param _preCrime The address of the preCrime contract.\n */\n function setPreCrime(address _preCrime) public virtual onlyOwner {\n preCrime = _preCrime;\n emit PreCrimeSet(_preCrime);\n }\n\n /**\n * @dev Interface for pre-crime simulations. Always reverts at the end with the simulation results.\n * @param _packets An array of InboundPacket objects representing received packets to be delivered.\n *\n * @dev WARNING: MUST revert at the end with the simulation results.\n * @dev Gives the preCrime implementation the ability to mock sending packets to the lzReceive function,\n * WITHOUT actually executing them.\n */\n function lzReceiveAndRevert(InboundPacket[] calldata _packets) public payable virtual {\n for (uint256 i = 0; i < _packets.length; i++) {\n InboundPacket calldata packet = _packets[i];\n\n // Ignore packets that are not from trusted peers.\n if (!isPeer(packet.origin.srcEid, packet.origin.sender)) continue;\n\n // @dev Because a verifier is calling this function, it doesnt have access to executor params:\n // - address _executor\n // - bytes calldata _extraData\n // preCrime will NOT work for OApps that rely on these two parameters inside of their _lzReceive().\n // They are instead stubbed to default values, address(0) and bytes(\"\")\n // @dev Calling this.lzReceiveSimulate removes ability for assembly return 0 callstack exit,\n // which would cause the revert to be ignored.\n this.lzReceiveSimulate{ value: packet.value }(\n packet.origin,\n packet.guid,\n packet.message,\n packet.executor,\n packet.extraData\n );\n }\n\n // @dev Revert with the simulation results. msg.sender must implement IPreCrime.buildSimulationResult().\n revert SimulationResult(IPreCrime(msg.sender).buildSimulationResult());\n }\n\n /**\n * @dev Is effectively an internal function because msg.sender must be address(this).\n * Allows resetting the call stack for 'internal' calls.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _guid The unique identifier of the packet.\n * @param _message The message payload of the packet.\n * @param _executor The executor address for the packet.\n * @param _extraData Additional data for the packet.\n */\n function lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) external payable virtual {\n // @dev Ensure ONLY can be called 'internally'.\n if (msg.sender != address(this)) revert OnlySelf();\n _lzReceiveSimulate(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Internal function to handle the OAppPreCrimeSimulator simulated receive.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The GUID of the LayerZero message.\n * @param _message The LayerZero message.\n * @param _executor The address of the off-chain executor.\n * @param _extraData Arbitrary data passed by the msg executor.\n *\n * @dev Enables the preCrime simulator to mock sending lzReceive() messages,\n * routes the msg down from the OAppPreCrimeSimulator, and back up to the OAppReceiver.\n */\n function _lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual;\n\n /**\n * @dev checks if the specified peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint Id to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n */\n function isPeer(uint32 _eid, bytes32 _peer) public view virtual returns (bool);\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/interfaces/IMintableBurnable.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\n/// @title Interface for mintable and burnable tokens\ninterface IMintableBurnable {\n \n /**\n * @notice Burns tokens from a specified account\n * @param _from Address from which tokens will be burned\n * @param _amount Amount of tokens to be burned\n * @return success Indicates whether the operation was successful\n */\n function burn(address _from, uint256 _amount) external returns (bool success);\n\n /**\n * @notice Mints tokens to a specified account\n * @param _to Address to which tokens will be minted\n * @param _amount Amount of tokens to be minted\n * @return success Indicates whether the operation was successful\n */\n function mint(address _to, uint256 _amount) external returns (bool success);\n}" + }, + "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { MessagingReceipt, MessagingFee } from \"@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol\";\n\n/**\n * @dev Struct representing token parameters for the OFT send() operation.\n */\nstruct SendParam {\n uint32 dstEid; // Destination endpoint ID.\n bytes32 to; // Recipient address.\n uint256 amountLD; // Amount to send in local decimals.\n uint256 minAmountLD; // Minimum amount to send in local decimals.\n bytes extraOptions; // Additional options supplied by the caller to be used in the LayerZero message.\n bytes composeMsg; // The composed message for the send() operation.\n bytes oftCmd; // The OFT command to be executed, unused in default OFT implementations.\n}\n\n/**\n * @dev Struct representing OFT limit information.\n * @dev These amounts can change dynamically and are up the specific oft implementation.\n */\nstruct OFTLimit {\n uint256 minAmountLD; // Minimum amount in local decimals that can be sent to the recipient.\n uint256 maxAmountLD; // Maximum amount in local decimals that can be sent to the recipient.\n}\n\n/**\n * @dev Struct representing OFT receipt information.\n */\nstruct OFTReceipt {\n uint256 amountSentLD; // Amount of tokens ACTUALLY debited from the sender in local decimals.\n // @dev In non-default implementations, the amountReceivedLD COULD differ from this value.\n uint256 amountReceivedLD; // Amount of tokens to be received on the remote side.\n}\n\n/**\n * @dev Struct representing OFT fee details.\n * @dev Future proof mechanism to provide a standardized way to communicate fees to things like a UI.\n */\nstruct OFTFeeDetail {\n int256 feeAmountLD; // Amount of the fee in local decimals.\n string description; // Description of the fee.\n}\n\n/**\n * @title IOFT\n * @dev Interface for the OftChain (OFT) token.\n * @dev Does not inherit ERC20 to accommodate usage by OFTAdapter as well.\n * @dev This specific interface ID is '0x02e49c2c'.\n */\ninterface IOFT {\n // Custom error messages\n error InvalidLocalDecimals();\n error SlippageExceeded(uint256 amountLD, uint256 minAmountLD);\n\n // Events\n event OFTSent(\n bytes32 indexed guid, // GUID of the OFT message.\n uint32 dstEid, // Destination Endpoint ID.\n address indexed fromAddress, // Address of the sender on the src chain.\n uint256 amountSentLD, // Amount of tokens sent in local decimals.\n uint256 amountReceivedLD // Amount of tokens received in local decimals.\n );\n event OFTReceived(\n bytes32 indexed guid, // GUID of the OFT message.\n uint32 srcEid, // Source Endpoint ID.\n address indexed toAddress, // Address of the recipient on the dst chain.\n uint256 amountReceivedLD // Amount of tokens received in local decimals.\n );\n\n /**\n * @notice Retrieves interfaceID and the version of the OFT.\n * @return interfaceId The interface ID.\n * @return version The version.\n *\n * @dev interfaceId: This specific interface ID is '0x02e49c2c'.\n * @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.\n * @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.\n * ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)\n */\n function oftVersion() external view returns (bytes4 interfaceId, uint64 version);\n\n /**\n * @notice Retrieves the address of the token associated with the OFT.\n * @return token The address of the ERC20 token implementation.\n */\n function token() external view returns (address);\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the 'token()' to send.\n * @return requiresApproval Needs approval of the underlying token implementation.\n *\n * @dev Allows things like wallet implementers to determine integration requirements,\n * without understanding the underlying token implementation.\n */\n function approvalRequired() external view returns (bool);\n\n /**\n * @notice Retrieves the shared decimals of the OFT.\n * @return sharedDecimals The shared decimals of the OFT.\n */\n function sharedDecimals() external view returns (uint8);\n\n /**\n * @notice Provides the fee breakdown and settings data for an OFT. Unused in the default implementation.\n * @param _sendParam The parameters for the send operation.\n * @return limit The OFT limit information.\n * @return oftFeeDetails The details of OFT fees.\n * @return receipt The OFT receipt information.\n */\n function quoteOFT(\n SendParam calldata _sendParam\n ) external view returns (OFTLimit memory, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory);\n\n /**\n * @notice Provides a quote for the send() operation.\n * @param _sendParam The parameters for the send() operation.\n * @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.\n * @return fee The calculated LayerZero messaging fee from the send() operation.\n *\n * @dev MessagingFee: LayerZero msg fee\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n */\n function quoteSend(SendParam calldata _sendParam, bool _payInLzToken) external view returns (MessagingFee memory);\n\n /**\n * @notice Executes the send() operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The fee information supplied by the caller.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds from fees etc. on the src.\n * @return receipt The LayerZero messaging receipt from the send() operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) external payable returns (MessagingReceipt memory, OFTReceipt memory);\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/libs/OFTComposeMsgCodec.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nlibrary OFTComposeMsgCodec {\n // Offset constants for decoding composed messages\n uint8 private constant NONCE_OFFSET = 8;\n uint8 private constant SRC_EID_OFFSET = 12;\n uint8 private constant AMOUNT_LD_OFFSET = 44;\n uint8 private constant COMPOSE_FROM_OFFSET = 76;\n\n /**\n * @dev Encodes a OFT composed message.\n * @param _nonce The nonce value.\n * @param _srcEid The source endpoint ID.\n * @param _amountLD The amount in local decimals.\n * @param _composeMsg The composed message.\n * @return _msg The encoded Composed message.\n */\n function encode(\n uint64 _nonce,\n uint32 _srcEid,\n uint256 _amountLD,\n bytes memory _composeMsg // 0x[composeFrom][composeMsg]\n ) internal pure returns (bytes memory _msg) {\n _msg = abi.encodePacked(_nonce, _srcEid, _amountLD, _composeMsg);\n }\n\n /**\n * @dev Retrieves the nonce for the composed message.\n * @param _msg The message.\n * @return The nonce value.\n */\n function nonce(bytes calldata _msg) internal pure returns (uint64) {\n return uint64(bytes8(_msg[:NONCE_OFFSET]));\n }\n\n /**\n * @dev Retrieves the source endpoint ID for the composed message.\n * @param _msg The message.\n * @return The source endpoint ID.\n */\n function srcEid(bytes calldata _msg) internal pure returns (uint32) {\n return uint32(bytes4(_msg[NONCE_OFFSET:SRC_EID_OFFSET]));\n }\n\n /**\n * @dev Retrieves the amount in local decimals from the composed message.\n * @param _msg The message.\n * @return The amount in local decimals.\n */\n function amountLD(bytes calldata _msg) internal pure returns (uint256) {\n return uint256(bytes32(_msg[SRC_EID_OFFSET:AMOUNT_LD_OFFSET]));\n }\n\n /**\n * @dev Retrieves the composeFrom value from the composed message.\n * @param _msg The message.\n * @return The composeFrom value.\n */\n function composeFrom(bytes calldata _msg) internal pure returns (bytes32) {\n return bytes32(_msg[AMOUNT_LD_OFFSET:COMPOSE_FROM_OFFSET]);\n }\n\n /**\n * @dev Retrieves the composed message.\n * @param _msg The message.\n * @return The composed message.\n */\n function composeMsg(bytes calldata _msg) internal pure returns (bytes memory) {\n return _msg[COMPOSE_FROM_OFFSET:];\n }\n\n /**\n * @dev Converts an address to bytes32.\n * @param _addr The address to convert.\n * @return The bytes32 representation of the address.\n */\n function addressToBytes32(address _addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(_addr)));\n }\n\n /**\n * @dev Converts bytes32 to an address.\n * @param _b The bytes32 value to convert.\n * @return The address representation of bytes32.\n */\n function bytes32ToAddress(bytes32 _b) internal pure returns (address) {\n return address(uint160(uint256(_b)));\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/libs/OFTMsgCodec.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nlibrary OFTMsgCodec {\n // Offset constants for encoding and decoding OFT messages\n uint8 private constant SEND_TO_OFFSET = 32;\n uint8 private constant SEND_AMOUNT_SD_OFFSET = 40;\n\n /**\n * @dev Encodes an OFT LayerZero message.\n * @param _sendTo The recipient address.\n * @param _amountShared The amount in shared decimals.\n * @param _composeMsg The composed message.\n * @return _msg The encoded message.\n * @return hasCompose A boolean indicating whether the message has a composed payload.\n */\n function encode(\n bytes32 _sendTo,\n uint64 _amountShared,\n bytes memory _composeMsg\n ) internal view returns (bytes memory _msg, bool hasCompose) {\n hasCompose = _composeMsg.length > 0;\n // @dev Remote chains will want to know the composed function caller ie. msg.sender on the src.\n _msg = hasCompose\n ? abi.encodePacked(_sendTo, _amountShared, addressToBytes32(msg.sender), _composeMsg)\n : abi.encodePacked(_sendTo, _amountShared);\n }\n\n /**\n * @dev Checks if the OFT message is composed.\n * @param _msg The OFT message.\n * @return A boolean indicating whether the message is composed.\n */\n function isComposed(bytes calldata _msg) internal pure returns (bool) {\n return _msg.length > SEND_AMOUNT_SD_OFFSET;\n }\n\n /**\n * @dev Retrieves the recipient address from the OFT message.\n * @param _msg The OFT message.\n * @return The recipient address.\n */\n function sendTo(bytes calldata _msg) internal pure returns (bytes32) {\n return bytes32(_msg[:SEND_TO_OFFSET]);\n }\n\n /**\n * @dev Retrieves the amount in shared decimals from the OFT message.\n * @param _msg The OFT message.\n * @return The amount in shared decimals.\n */\n function amountSD(bytes calldata _msg) internal pure returns (uint64) {\n return uint64(bytes8(_msg[SEND_TO_OFFSET:SEND_AMOUNT_SD_OFFSET]));\n }\n\n /**\n * @dev Retrieves the composed message from the OFT message.\n * @param _msg The OFT message.\n * @return The composed message.\n */\n function composeMsg(bytes calldata _msg) internal pure returns (bytes memory) {\n return _msg[SEND_AMOUNT_SD_OFFSET:];\n }\n\n /**\n * @dev Converts an address to bytes32.\n * @param _addr The address to convert.\n * @return The bytes32 representation of the address.\n */\n function addressToBytes32(address _addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(_addr)));\n }\n\n /**\n * @dev Converts bytes32 to an address.\n * @param _b The bytes32 value to convert.\n * @return The address representation of bytes32.\n */\n function bytes32ToAddress(bytes32 _b) internal pure returns (address) {\n return address(uint160(uint256(_b)));\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/MintBurnOFTAdapter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\n// External imports\nimport { IERC20, IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\n// Local imports\nimport { IMintableBurnable } from \"./interfaces/IMintableBurnable.sol\";\nimport { OFTCore } from \"./OFTCore.sol\";\n\n/**\n * @title MintBurnOFTAdapter\n * @notice A variant of the standard OFT Adapter that uses an existing ERC20's mint and burn mechanisms for cross-chain transfers.\n *\n * @dev Inherits from OFTCore and provides implementations for _debit and _credit functions using a mintable and burnable token.\n */\nabstract contract MintBurnOFTAdapter is OFTCore {\n /// @dev The underlying ERC20 token.\n IERC20 internal immutable innerToken;\n\n /// @notice The contract responsible for minting and burning tokens.\n IMintableBurnable public immutable minterBurner;\n\n /**\n * @notice Initializes the MintBurnOFTAdapter contract.\n *\n * @param _token The address of the underlying ERC20 token.\n * @param _minterBurner The contract responsible for minting and burning tokens.\n * @param _lzEndpoint The LayerZero endpoint address.\n * @param _delegate The address of the delegate.\n *\n * @dev Calls the OFTCore constructor with the token's decimals, the endpoint, and the delegate.\n */\n constructor(\n address _token,\n IMintableBurnable _minterBurner,\n address _lzEndpoint,\n address _delegate\n ) OFTCore(IERC20Metadata(_token).decimals(), _lzEndpoint, _delegate) {\n innerToken = IERC20(_token);\n minterBurner = _minterBurner;\n }\n\n /**\n * @notice Retrieves the address of the underlying ERC20 token.\n *\n * @return The address of the adapted ERC20 token.\n *\n * @dev In the case of MintBurnOFTAdapter, address(this) and erc20 are NOT the same contract.\n */\n function token() public view returns (address) {\n return address(innerToken);\n }\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the underlying token to send.\n *\n * @return requiresApproval True if approval is required, false otherwise.\n *\n * @dev In this MintBurnOFTAdapter, approval is NOT required because it uses mint and burn privileges.\n */\n function approvalRequired() external pure virtual returns (bool) {\n return false;\n }\n\n /**\n * @notice Burns tokens from the sender's balance to prepare for sending.\n *\n * @param _from The address to debit the tokens from.\n * @param _amountLD The amount of tokens to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination chain ID.\n *\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, i.e., 1 token in, 1 token out.\n * If the 'innerToken' applies something like a transfer fee, the default will NOT work.\n * A pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual override returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n (amountSentLD, amountReceivedLD) = _debitView(_amountLD, _minAmountLD, _dstEid);\n // Burns tokens from the caller.\n minterBurner.burn(_from, amountSentLD);\n }\n\n /**\n * @notice Mints tokens to the specified address upon receiving them.\n *\n * @param _to The address to credit the tokens to.\n * @param _amountLD The amount of tokens to credit in local decimals.\n *\n * @return amountReceivedLD The amount of tokens actually received in local decimals.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, i.e., 1 token in, 1 token out.\n * If the 'innerToken' applies something like a transfer fee, the default will NOT work.\n * A pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /* _srcEid */\n ) internal virtual override returns (uint256 amountReceivedLD) {\n if (_to == address(0x0)) _to = address(0xdead); // _mint(...) does not support address(0x0)\n // Mints the tokens and transfers to the recipient.\n minterBurner.mint(_to, _amountLD);\n // In the case of NON-default OFTAdapter, the amountLD MIGHT not be equal to amountReceivedLD.\n return _amountLD;\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/OFTAdapter.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IERC20Metadata, IERC20 } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IOFT, OFTCore } from \"./OFTCore.sol\";\n\n/**\n * @title OFTAdapter Contract\n * @dev OFTAdapter is a contract that adapts an ERC-20 token to the OFT functionality.\n *\n * @dev For existing ERC20 tokens, this can be used to convert the token to crosschain compatibility.\n * @dev WARNING: ONLY 1 of these should exist for a given global mesh,\n * unless you make a NON-default implementation of OFT and needs to be done very carefully.\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountSentLD/amountReceivedLD.\n */\nabstract contract OFTAdapter is OFTCore {\n using SafeERC20 for IERC20;\n\n IERC20 internal immutable innerToken;\n\n /**\n * @dev Constructor for the OFTAdapter contract.\n * @param _token The address of the ERC-20 token to be adapted.\n * @param _lzEndpoint The LayerZero endpoint address.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(\n address _token,\n address _lzEndpoint,\n address _delegate\n ) OFTCore(IERC20Metadata(_token).decimals(), _lzEndpoint, _delegate) {\n innerToken = IERC20(_token);\n }\n\n /**\n * @dev Retrieves the address of the underlying ERC20 implementation.\n * @return The address of the adapted ERC-20 token.\n *\n * @dev In the case of OFTAdapter, address(this) and erc20 are NOT the same contract.\n */\n function token() public view returns (address) {\n return address(innerToken);\n }\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the 'token()' to send.\n * @return requiresApproval Needs approval of the underlying token implementation.\n *\n * @dev In the case of default OFTAdapter, approval is required.\n * @dev In non-default OFTAdapter contracts with something like mint and burn privileges, it would NOT need approval.\n */\n function approvalRequired() external pure virtual returns (bool) {\n return true;\n }\n\n /**\n * @dev Locks tokens from the sender's specified balance in this contract.\n * @param _from The address to debit from.\n * @param _amountLD The amount of tokens to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination chain ID.\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev msg.sender will need to approve this _amountLD of tokens to be locked inside of the contract.\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual override returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n (amountSentLD, amountReceivedLD) = _debitView(_amountLD, _minAmountLD, _dstEid);\n // @dev Lock tokens by moving them into this contract from the caller.\n innerToken.safeTransferFrom(_from, address(this), amountSentLD);\n }\n\n /**\n * @dev Credits tokens to the specified address.\n * @param _to The address to credit the tokens to.\n * @param _amountLD The amount of tokens to credit in local decimals.\n * @dev _srcEid The source chain ID.\n * @return amountReceivedLD The amount of tokens ACTUALLY received in local decimals.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /*_srcEid*/\n ) internal virtual override returns (uint256 amountReceivedLD) {\n // @dev Unlock the tokens and transfer to the recipient.\n innerToken.safeTransfer(_to, _amountLD);\n // @dev In the case of NON-default OFTAdapter, the amountLD MIGHT not be == amountReceivedLD.\n return _amountLD;\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/OFTCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { OApp, Origin } from \"@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol\";\nimport { OAppOptionsType3 } from \"@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol\";\nimport { IOAppMsgInspector } from \"@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppMsgInspector.sol\";\n\nimport { OAppPreCrimeSimulator } from \"@layerzerolabs/oapp-evm/contracts/precrime/OAppPreCrimeSimulator.sol\";\n\nimport { IOFT, SendParam, OFTLimit, OFTReceipt, OFTFeeDetail, MessagingReceipt, MessagingFee } from \"./interfaces/IOFT.sol\";\nimport { OFTMsgCodec } from \"./libs/OFTMsgCodec.sol\";\nimport { OFTComposeMsgCodec } from \"./libs/OFTComposeMsgCodec.sol\";\n\n/**\n * @title OFTCore\n * @dev Abstract contract for the OftChain (OFT) token.\n */\nabstract contract OFTCore is IOFT, OApp, OAppPreCrimeSimulator, OAppOptionsType3 {\n using OFTMsgCodec for bytes;\n using OFTMsgCodec for bytes32;\n\n // @notice Provides a conversion rate when swapping between denominations of SD and LD\n // - shareDecimals == SD == shared Decimals\n // - localDecimals == LD == local decimals\n // @dev Considers that tokens have different decimal amounts on various chains.\n // @dev eg.\n // For a token\n // - locally with 4 decimals --> 1.2345 => uint(12345)\n // - remotely with 2 decimals --> 1.23 => uint(123)\n // - The conversion rate would be 10 ** (4 - 2) = 100\n // @dev If you want to send 1.2345 -> (uint 12345), you CANNOT represent that value on the remote,\n // you can only display 1.23 -> uint(123).\n // @dev To preserve the dust that would otherwise be lost on that conversion,\n // we need to unify a denomination that can be represented on ALL chains inside of the OFT mesh\n uint256 public immutable decimalConversionRate;\n\n // @notice Msg types that are used to identify the various OFT operations.\n // @dev This can be extended in child contracts for non-default oft operations\n // @dev These values are used in things like combineOptions() in OAppOptionsType3.sol.\n uint16 public constant SEND = 1;\n uint16 public constant SEND_AND_CALL = 2;\n\n // Address of an optional contract to inspect both 'message' and 'options'\n address public msgInspector;\n event MsgInspectorSet(address inspector);\n\n /**\n * @dev Constructor.\n * @param _localDecimals The decimals of the token on the local chain (this chain).\n * @param _endpoint The address of the LayerZero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(uint8 _localDecimals, address _endpoint, address _delegate) OApp(_endpoint, _delegate) {\n if (_localDecimals < sharedDecimals()) revert InvalidLocalDecimals();\n decimalConversionRate = 10 ** (_localDecimals - sharedDecimals());\n }\n\n /**\n * @notice Retrieves interfaceID and the version of the OFT.\n * @return interfaceId The interface ID.\n * @return version The version.\n *\n * @dev interfaceId: This specific interface ID is '0x02e49c2c'.\n * @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.\n * @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.\n * ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)\n */\n function oftVersion() external pure virtual returns (bytes4 interfaceId, uint64 version) {\n return (type(IOFT).interfaceId, 1);\n }\n\n /**\n * @dev Retrieves the shared decimals of the OFT.\n * @return The shared decimals of the OFT.\n *\n * @dev Sets an implicit cap on the amount of tokens, over uint64.max() will need some sort of outbound cap / totalSupply cap\n * Lowest common decimal denominator between chains.\n * Defaults to 6 decimal places to provide up to 18,446,744,073,709.551615 units (max uint64).\n * For tokens exceeding this totalSupply(), they will need to override the sharedDecimals function with something smaller.\n * ie. 4 sharedDecimals would be 1,844,674,407,370,955.1615\n */\n function sharedDecimals() public view virtual returns (uint8) {\n return 6;\n }\n\n /**\n * @dev Sets the message inspector address for the OFT.\n * @param _msgInspector The address of the message inspector.\n *\n * @dev This is an optional contract that can be used to inspect both 'message' and 'options'.\n * @dev Set it to address(0) to disable it, or set it to a contract address to enable it.\n */\n function setMsgInspector(address _msgInspector) public virtual onlyOwner {\n msgInspector = _msgInspector;\n emit MsgInspectorSet(_msgInspector);\n }\n\n /**\n * @notice Provides the fee breakdown and settings data for an OFT. Unused in the default implementation.\n * @param _sendParam The parameters for the send operation.\n * @return oftLimit The OFT limit information.\n * @return oftFeeDetails The details of OFT fees.\n * @return oftReceipt The OFT receipt information.\n */\n function quoteOFT(\n SendParam calldata _sendParam\n )\n external\n view\n virtual\n returns (OFTLimit memory oftLimit, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory oftReceipt)\n {\n uint256 minAmountLD = 0; // Unused in the default implementation.\n uint256 maxAmountLD = IERC20(this.token()).totalSupply(); // Unused in the default implementation.\n oftLimit = OFTLimit(minAmountLD, maxAmountLD);\n\n // Unused in the default implementation; reserved for future complex fee details.\n oftFeeDetails = new OFTFeeDetail[](0);\n\n // @dev This is the same as the send() operation, but without the actual send.\n // - amountSentLD is the amount in local decimals that would be sent from the sender.\n // - amountReceivedLD is the amount in local decimals that will be credited to the recipient on the remote OFT instance.\n // @dev The amountSentLD MIGHT not equal the amount the user actually receives. HOWEVER, the default does.\n (uint256 amountSentLD, uint256 amountReceivedLD) = _debitView(\n _sendParam.amountLD,\n _sendParam.minAmountLD,\n _sendParam.dstEid\n );\n oftReceipt = OFTReceipt(amountSentLD, amountReceivedLD);\n }\n\n /**\n * @notice Provides a quote for the send() operation.\n * @param _sendParam The parameters for the send() operation.\n * @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.\n * @return msgFee The calculated LayerZero messaging fee from the send() operation.\n *\n * @dev MessagingFee: LayerZero msg fee\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n */\n function quoteSend(\n SendParam calldata _sendParam,\n bool _payInLzToken\n ) external view virtual returns (MessagingFee memory msgFee) {\n // @dev mock the amount to receive, this is the same operation used in the send().\n // The quote is as similar as possible to the actual send() operation.\n (, uint256 amountReceivedLD) = _debitView(_sendParam.amountLD, _sendParam.minAmountLD, _sendParam.dstEid);\n\n // @dev Builds the options and OFT message to quote in the endpoint.\n (bytes memory message, bytes memory options) = _buildMsgAndOptions(_sendParam, amountReceivedLD);\n\n // @dev Calculates the LayerZero fee for the send() operation.\n return _quote(_sendParam.dstEid, message, options, _payInLzToken);\n }\n\n /**\n * @dev Executes the send operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The calculated fee for the send() operation.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds.\n * @return msgReceipt The receipt for the send operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) external payable virtual returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) {\n return _send(_sendParam, _fee, _refundAddress);\n }\n\n /**\n * @dev Internal function to execute the send operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The calculated fee for the send() operation.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds.\n * @return msgReceipt The receipt for the send operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function _send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) internal virtual returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) {\n // @dev Applies the token transfers regarding this send() operation.\n // - amountSentLD is the amount in local decimals that was ACTUALLY sent/debited from the sender.\n // - amountReceivedLD is the amount in local decimals that will be received/credited to the recipient on the remote OFT instance.\n (uint256 amountSentLD, uint256 amountReceivedLD) = _debit(\n msg.sender,\n _sendParam.amountLD,\n _sendParam.minAmountLD,\n _sendParam.dstEid\n );\n\n // @dev Builds the options and OFT message to quote in the endpoint.\n (bytes memory message, bytes memory options) = _buildMsgAndOptions(_sendParam, amountReceivedLD);\n\n // @dev Sends the message to the LayerZero endpoint and returns the LayerZero msg receipt.\n msgReceipt = _lzSend(_sendParam.dstEid, message, options, _fee, _refundAddress);\n // @dev Formulate the OFT receipt.\n oftReceipt = OFTReceipt(amountSentLD, amountReceivedLD);\n\n emit OFTSent(msgReceipt.guid, _sendParam.dstEid, msg.sender, amountSentLD, amountReceivedLD);\n }\n\n /**\n * @dev Internal function to build the message and options.\n * @param _sendParam The parameters for the send() operation.\n * @param _amountLD The amount in local decimals.\n * @return message The encoded message.\n * @return options The encoded options.\n */\n function _buildMsgAndOptions(\n SendParam calldata _sendParam,\n uint256 _amountLD\n ) internal view virtual returns (bytes memory message, bytes memory options) {\n bool hasCompose;\n // @dev This generated message has the msg.sender encoded into the payload so the remote knows who the caller is.\n (message, hasCompose) = OFTMsgCodec.encode(\n _sendParam.to,\n _toSD(_amountLD),\n // @dev Must be include a non empty bytes if you want to compose, EVEN if you dont need it on the remote.\n // EVEN if you dont require an arbitrary payload to be sent... eg. '0x01'\n _sendParam.composeMsg\n );\n // @dev Change the msg type depending if its composed or not.\n uint16 msgType = hasCompose ? SEND_AND_CALL : SEND;\n // @dev Combine the callers _extraOptions with the enforced options via the OAppOptionsType3.\n options = combineOptions(_sendParam.dstEid, msgType, _sendParam.extraOptions);\n\n // @dev Optionally inspect the message and options depending if the OApp owner has set a msg inspector.\n // @dev If it fails inspection, needs to revert in the implementation. ie. does not rely on return boolean\n address inspector = msgInspector; // caches the msgInspector to avoid potential double storage read\n if (inspector != address(0)) IOAppMsgInspector(inspector).inspect(message, options);\n }\n\n /**\n * @dev Internal function to handle the receive on the LayerZero endpoint.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The encoded message.\n * @dev _executor The address of the executor.\n * @dev _extraData Additional data.\n */\n function _lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address /*_executor*/, // @dev unused in the default implementation.\n bytes calldata /*_extraData*/ // @dev unused in the default implementation.\n ) internal virtual override {\n // @dev The src sending chain doesnt know the address length on this chain (potentially non-evm)\n // Thus everything is bytes32() encoded in flight.\n address toAddress = _message.sendTo().bytes32ToAddress();\n // @dev Credit the amountLD to the recipient and return the ACTUAL amount the recipient received in local decimals\n uint256 amountReceivedLD = _credit(toAddress, _toLD(_message.amountSD()), _origin.srcEid);\n\n if (_message.isComposed()) {\n // @dev Proprietary composeMsg format for the OFT.\n bytes memory composeMsg = OFTComposeMsgCodec.encode(\n _origin.nonce,\n _origin.srcEid,\n amountReceivedLD,\n _message.composeMsg()\n );\n\n // @dev Stores the lzCompose payload that will be executed in a separate tx.\n // Standardizes functionality for executing arbitrary contract invocation on some non-evm chains.\n // @dev The off-chain executor will listen and process the msg based on the src-chain-callers compose options passed.\n // @dev The index is used when a OApp needs to compose multiple msgs on lzReceive.\n // For default OFT implementation there is only 1 compose msg per lzReceive, thus its always 0.\n endpoint.sendCompose(toAddress, _guid, 0 /* the index of the composed message*/, composeMsg);\n }\n\n emit OFTReceived(_guid, _origin.srcEid, toAddress, amountReceivedLD);\n }\n\n /**\n * @dev Internal function to handle the OAppPreCrimeSimulator simulated receive.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The LayerZero message.\n * @param _executor The address of the off-chain executor.\n * @param _extraData Arbitrary data passed by the msg executor.\n *\n * @dev Enables the preCrime simulator to mock sending lzReceive() messages,\n * routes the msg down from the OAppPreCrimeSimulator, and back up to the OAppReceiver.\n */\n function _lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual override {\n _lzReceive(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Check if the peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint ID to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n *\n * @dev Enables OAppPreCrimeSimulator to check whether a potential Inbound Packet is from a trusted source.\n */\n function isPeer(uint32 _eid, bytes32 _peer) public view virtual override returns (bool) {\n return peers[_eid] == _peer;\n }\n\n /**\n * @dev Internal function to remove dust from the given local decimal amount.\n * @param _amountLD The amount in local decimals.\n * @return amountLD The amount after removing dust.\n *\n * @dev Prevents the loss of dust when moving amounts between chains with different decimals.\n * @dev eg. uint(123) with a conversion rate of 100 becomes uint(100).\n */\n function _removeDust(uint256 _amountLD) internal view virtual returns (uint256 amountLD) {\n return (_amountLD / decimalConversionRate) * decimalConversionRate;\n }\n\n /**\n * @dev Internal function to convert an amount from shared decimals into local decimals.\n * @param _amountSD The amount in shared decimals.\n * @return amountLD The amount in local decimals.\n */\n function _toLD(uint64 _amountSD) internal view virtual returns (uint256 amountLD) {\n return _amountSD * decimalConversionRate;\n }\n\n /**\n * @dev Internal function to convert an amount from local decimals into shared decimals.\n * @param _amountLD The amount in local decimals.\n * @return amountSD The amount in shared decimals.\n */\n function _toSD(uint256 _amountLD) internal view virtual returns (uint64 amountSD) {\n return uint64(_amountLD / decimalConversionRate);\n }\n\n /**\n * @dev Internal function to mock the amount mutation from a OFT debit() operation.\n * @param _amountLD The amount to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @dev _dstEid The destination endpoint ID.\n * @return amountSentLD The amount sent, in local decimals.\n * @return amountReceivedLD The amount to be received on the remote chain, in local decimals.\n *\n * @dev This is where things like fees would be calculated and deducted from the amount to be received on the remote.\n */\n function _debitView(\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 /*_dstEid*/\n ) internal view virtual returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n // @dev Remove the dust so nothing is lost on the conversion between chains with different decimals for the token.\n amountSentLD = _removeDust(_amountLD);\n // @dev The amount to send is the same as amount received in the default implementation.\n amountReceivedLD = amountSentLD;\n\n // @dev Check for slippage.\n if (amountReceivedLD < _minAmountLD) {\n revert SlippageExceeded(amountReceivedLD, _minAmountLD);\n }\n }\n\n /**\n * @dev Internal function to perform a debit operation.\n * @param _from The address to debit.\n * @param _amountLD The amount to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination endpoint ID.\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev Defined here but are intended to be overriden depending on the OFT implementation.\n * @dev Depending on OFT implementation the _amountLD could differ from the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual returns (uint256 amountSentLD, uint256 amountReceivedLD);\n\n /**\n * @dev Internal function to perform a credit operation.\n * @param _to The address to credit.\n * @param _amountLD The amount to credit in local decimals.\n * @param _srcEid The source endpoint ID.\n * @return amountReceivedLD The amount ACTUALLY received in local decimals.\n *\n * @dev Defined here but are intended to be overriden depending on the OFT implementation.\n * @dev Depending on OFT implementation the _amountLD could differ from the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 _srcEid\n ) internal virtual returns (uint256 amountReceivedLD);\n}\n" + }, + "@openzeppelin/contracts/access/AccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/AccessControl.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\nimport \"../utils/Context.sol\";\nimport \"../utils/Strings.sol\";\nimport \"../utils/introspection/ERC165.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\n * members except through off-chain means by accessing the contract event logs. Some\n * applications may benefit from on-chain enumerability, for those cases see\n * {AccessControlEnumerable}.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```\n * function foo() public {\n * require(hasRole(MY_ROLE, msg.sender));\n * ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it.\n */\nabstract contract AccessControl is Context, IAccessControl, ERC165 {\n struct RoleData {\n mapping(address => bool) members;\n bytes32 adminRole;\n }\n\n mapping(bytes32 => RoleData) private _roles;\n\n bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n /**\n * @dev Modifier that checks that an account has a specific role. Reverts\n * with a standardized message including the required role.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n *\n * _Available since v4.1._\n */\n modifier onlyRole(bytes32 role) {\n _checkRole(role, _msgSender());\n _;\n }\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) public view override returns (bool) {\n return _roles[role].members[account];\n }\n\n /**\n * @dev Revert with a standard message if `account` is missing `role`.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n */\n function _checkRole(bytes32 role, address account) internal view {\n if (!hasRole(role, account)) {\n revert(\n string(\n abi.encodePacked(\n \"AccessControl: account \",\n Strings.toHexString(uint160(account), 20),\n \" is missing role \",\n Strings.toHexString(uint256(role), 32)\n )\n )\n );\n }\n }\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) public view override returns (bytes32) {\n return _roles[role].adminRole;\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _grantRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _revokeRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been revoked `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) public virtual override {\n require(account == _msgSender(), \"AccessControl: can only renounce roles for self\");\n\n _revokeRole(role, account);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event. Note that unlike {grantRole}, this function doesn't perform any\n * checks on the calling account.\n *\n * [WARNING]\n * ====\n * This function should only be called from the constructor when setting\n * up the initial roles for the system.\n *\n * Using this function in any other way is effectively circumventing the admin\n * system imposed by {AccessControl}.\n * ====\n *\n * NOTE: This function is deprecated in favor of {_grantRole}.\n */\n function _setupRole(bytes32 role, address account) internal virtual {\n _grantRole(role, account);\n }\n\n /**\n * @dev Sets `adminRole` as ``role``'s admin role.\n *\n * Emits a {RoleAdminChanged} event.\n */\n function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n bytes32 previousAdminRole = getRoleAdmin(role);\n _roles[role].adminRole = adminRole;\n emit RoleAdminChanged(role, previousAdminRole, adminRole);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * Internal function without access restriction.\n */\n function _grantRole(bytes32 role, address account) internal virtual {\n if (!hasRole(role, account)) {\n _roles[role].members[account] = true;\n emit RoleGranted(role, account, _msgSender());\n }\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * Internal function without access restriction.\n */\n function _revokeRole(bytes32 role, address account) internal virtual {\n if (hasRole(role, account)) {\n _roles[role].members[account] = false;\n emit RoleRevoked(role, account, _msgSender());\n }\n }\n}\n" + }, + "@openzeppelin/contracts/access/AccessControlEnumerable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/AccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControlEnumerable.sol\";\nimport \"./AccessControl.sol\";\nimport \"../utils/structs/EnumerableSet.sol\";\n\n/**\n * @dev Extension of {AccessControl} that allows enumerating the members of each role.\n */\nabstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {\n using EnumerableSet for EnumerableSet.AddressSet;\n\n mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) public view override returns (address) {\n return _roleMembers[role].at(index);\n }\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) public view override returns (uint256) {\n return _roleMembers[role].length();\n }\n\n /**\n * @dev Overload {_grantRole} to track enumerable memberships\n */\n function _grantRole(bytes32 role, address account) internal virtual override {\n super._grantRole(role, account);\n _roleMembers[role].add(account);\n }\n\n /**\n * @dev Overload {_revokeRole} to track enumerable memberships\n */\n function _revokeRole(bytes32 role, address account) internal virtual override {\n super._revokeRole(role, account);\n _roleMembers[role].remove(account);\n }\n}\n" + }, + "@openzeppelin/contracts/access/IAccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n /**\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n *\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n * {RoleAdminChanged} not being emitted signaling this.\n *\n * _Available since v3.1._\n */\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n /**\n * @dev Emitted when `account` is granted `role`.\n *\n * `sender` is the account that originated the contract call, an admin role\n * bearer except when using {AccessControl-_setupRole}.\n */\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Emitted when `account` is revoked `role`.\n *\n * `sender` is the account that originated the contract call:\n * - if using `revokeRole`, it is the admin role bearer\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\n */\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) external view returns (bool);\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been granted `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) external;\n}\n" + }, + "@openzeppelin/contracts/access/IAccessControlEnumerable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\n\n/**\n * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.\n */\ninterface IAccessControlEnumerable is IAccessControl {\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) external view returns (address);\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) external view returns (uint256);\n}\n" + }, + "@openzeppelin/contracts/access/Ownable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n" + }, + "@openzeppelin/contracts/security/Pausable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/ERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/ERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"./extensions/IERC20Metadata.sol\";\nimport \"../../utils/Context.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20, IERC20Metadata {\n mapping(address => uint256) private _balances;\n\n mapping(address => mapping(address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n\n /**\n * @dev Sets the values for {name} and {symbol}.\n *\n * The default value of {decimals} is 18. To select a different value for\n * {decimals} you should overload it.\n *\n * All two of these values are immutable: they can only be set once during\n * construction.\n */\n constructor(string memory name_, string memory symbol_) {\n _name = name_;\n _symbol = symbol_;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless this function is\n * overridden;\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `recipient` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n _approve(_msgSender(), spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * Requirements:\n *\n * - `sender` and `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n * - the caller must have allowance for ``sender``'s tokens of at least\n * `amount`.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public virtual override returns (bool) {\n _transfer(sender, recipient, amount);\n\n uint256 currentAllowance = _allowances[sender][_msgSender()];\n require(currentAllowance >= amount, \"ERC20: transfer amount exceeds allowance\");\n unchecked {\n _approve(sender, _msgSender(), currentAllowance - amount);\n }\n\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n uint256 currentAllowance = _allowances[_msgSender()][spender];\n require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n unchecked {\n _approve(_msgSender(), spender, currentAllowance - subtractedValue);\n }\n\n return true;\n }\n\n /**\n * @dev Moves `amount` of tokens from `sender` to `recipient`.\n *\n * This internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `sender` cannot be the zero address.\n * - `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n */\n function _transfer(\n address sender,\n address recipient,\n uint256 amount\n ) internal virtual {\n require(sender != address(0), \"ERC20: transfer from the zero address\");\n require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(sender, recipient, amount);\n\n uint256 senderBalance = _balances[sender];\n require(senderBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n unchecked {\n _balances[sender] = senderBalance - amount;\n }\n _balances[recipient] += amount;\n\n emit Transfer(sender, recipient, amount);\n\n _afterTokenTransfer(sender, recipient, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply += amount;\n _balances[account] += amount;\n emit Transfer(address(0), account, amount);\n\n _afterTokenTransfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n uint256 accountBalance = _balances[account];\n require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n unchecked {\n _balances[account] = accountBalance - amount;\n }\n _totalSupply -= amount;\n\n emit Transfer(account, address(0), amount);\n\n _afterTokenTransfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n\n /**\n * @dev Hook that is called after any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * has been transferred to `to`.\n * - when `from` is zero, `amount` tokens have been minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _afterTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC721 token receiver interface\n * @dev Interface for any contract that wants to support safeTransfers\n * from ERC721 asset contracts.\n */\ninterface IERC721Receiver {\n /**\n * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}\n * by `operator` from `from`, this function is called.\n *\n * It must return its Solidity selector to confirm the token transfer.\n * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.\n *\n * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.\n */\n function onERC721Received(\n address operator,\n address from,\n uint256 tokenId,\n bytes calldata data\n ) external returns (bytes4);\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/ERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC165.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n *\n * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.\n */\nabstract contract ERC165 is IERC165 {\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IERC165).interfaceId;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/IERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeMath.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)\n\npragma solidity ^0.8.0;\n\n// CAUTION\n// This version of SafeMath should only be used with Solidity 0.8 or later,\n// because it relies on the compiler's built in overflow checks.\n\n/**\n * @dev Wrappers over Solidity's arithmetic operations.\n *\n * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler\n * now has built in overflow checking.\n */\nlibrary SafeMath {\n /**\n * @dev Returns the addition of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n uint256 c = a + b;\n if (c < a) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the substraction of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b > a) return (false, 0);\n return (true, a - b);\n }\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n // benefit is lost if 'b' is also tested.\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n if (a == 0) return (true, 0);\n uint256 c = a * b;\n if (c / a != b) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the division of two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a / b);\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a % b);\n }\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `+` operator.\n *\n * Requirements:\n *\n * - Addition cannot overflow.\n */\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\n return a + b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting on\n * overflow (when the result is negative).\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\n return a - b;\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `*` operator.\n *\n * Requirements:\n *\n * - Multiplication cannot overflow.\n */\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\n return a * b;\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator.\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\n return a / b;\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting when dividing by zero.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\n return a % b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\n * overflow (when the result is negative).\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {trySub}.\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b <= a, errorMessage);\n return a - b;\n }\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting with custom message on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator. Note: this function uses a\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\n * uses an invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a / b;\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting with custom message when dividing by zero.\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {tryMod}.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a % b;\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Strings.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n bytes16 private constant _HEX_SYMBOLS = \"0123456789abcdef\";\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n // Inspired by OraclizeAPI's implementation - MIT licence\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\n\n if (value == 0) {\n return \"0\";\n }\n uint256 temp = value;\n uint256 digits;\n while (temp != 0) {\n digits++;\n temp /= 10;\n }\n bytes memory buffer = new bytes(digits);\n while (value != 0) {\n digits -= 1;\n buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\n value /= 10;\n }\n return string(buffer);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n if (value == 0) {\n return \"0x00\";\n }\n uint256 temp = value;\n uint256 length = 0;\n while (temp != 0) {\n length++;\n temp >>= 8;\n }\n return toHexString(value, length);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _HEX_SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/structs/EnumerableSet.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 => uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n" + }, + "contracts/automation/AbstractCCIPBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IRouterClient } from \"@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol\";\nimport { Client } from \"@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol\";\n\nabstract contract AbstractCCIPBridgeHelperModule is AbstractSafeModule {\n /**\n * @notice Bridges a token from the source chain to the destination chain using CCIP\n * @param ccipRouter The CCIP router contract\n * @param destinationChainSelector The selector for the destination chain\n * @param token The token to bridge\n * @param amount The amount of token to bridge\n */\n function _bridgeTokenWithCCIP(\n IRouterClient ccipRouter,\n uint64 destinationChainSelector,\n IERC20 token,\n uint256 amount\n ) internal {\n bool success;\n\n // Approve CCIP Router to move the token\n success = safeContract.execTransactionFromModule(\n address(token),\n 0, // Value\n abi.encodeWithSelector(token.approve.selector, ccipRouter, amount),\n 0 // Call\n );\n require(success, \"Failed to approve token\");\n\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(token),\n amount: amount\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory ccipMessage = Client.EVM2AnyMessage({\n receiver: abi.encode(address(safeContract)), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n // Get CCIP fee\n uint256 ccipFee = ccipRouter.getFee(\n destinationChainSelector,\n ccipMessage\n );\n\n // Send CCIP message\n success = safeContract.execTransactionFromModule(\n address(ccipRouter),\n ccipFee, // Value\n abi.encodeWithSelector(\n ccipRouter.ccipSend.selector,\n destinationChainSelector,\n ccipMessage\n ),\n 0 // Call\n );\n require(success, \"Failed to send CCIP message\");\n }\n}\n" + }, + "contracts/automation/AbstractLZBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\n\nimport { IOFT, SendParam } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\nimport { MessagingFee } from \"@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol\";\nimport { OptionsBuilder } from \"@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nabstract contract AbstractLZBridgeHelperModule is AbstractSafeModule {\n using OptionsBuilder for bytes;\n\n /**\n * @dev Bridges token using LayerZero.\n * @param lzEndpointId LayerZero endpoint id.\n * @param token Token to bridge.\n * @param lzAdapter LZ Adapter to use.\n * @param amount Amount of token to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n * @param isNativeToken Whether the token is native token.\n */\n function _bridgeTokenWithLz(\n uint32 lzEndpointId,\n IERC20 token,\n IOFT lzAdapter,\n uint256 amount,\n uint256 slippageBps,\n bool isNativeToken\n ) internal {\n bool success;\n\n if (!isNativeToken) {\n // Approve LZ Adapter to move the token\n success = safeContract.execTransactionFromModule(\n address(token),\n 0, // Value\n abi.encodeWithSelector(\n token.approve.selector,\n address(lzAdapter),\n amount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve token\");\n }\n\n // Calculate minimum amount to receive\n uint256 minAmount = (amount * (10000 - slippageBps)) / 10000;\n\n // Hardcoded gaslimit of 400k\n bytes memory options = OptionsBuilder\n .newOptions()\n .addExecutorLzReceiveOption(400000, 0);\n\n // Build send param\n SendParam memory sendParam = SendParam({\n dstEid: lzEndpointId,\n to: bytes32(uint256(uint160(address(safeContract)))),\n amountLD: amount,\n minAmountLD: minAmount,\n extraOptions: options,\n composeMsg: bytes(\"\"),\n oftCmd: bytes(\"\")\n });\n\n // Compute fees\n MessagingFee memory msgFee = lzAdapter.quoteSend(sendParam, false);\n\n uint256 value = isNativeToken\n ? amount + msgFee.nativeFee\n : msgFee.nativeFee;\n\n // Execute transaction\n success = safeContract.execTransactionFromModule(\n address(lzAdapter),\n value,\n abi.encodeWithSelector(\n lzAdapter.send.selector,\n sendParam,\n msgFee,\n address(safeContract)\n ),\n 0\n );\n require(success, \"Failed to bridge token\");\n }\n}\n" + }, + "contracts/automation/AbstractSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { ISafe } from \"../interfaces/ISafe.sol\";\n\nabstract contract AbstractSafeModule is AccessControlEnumerable {\n ISafe public immutable safeContract;\n\n bytes32 public constant OPERATOR_ROLE = keccak256(\"OPERATOR_ROLE\");\n\n modifier onlySafe() {\n require(\n msg.sender == address(safeContract),\n \"Caller is not the safe contract\"\n );\n _;\n }\n\n modifier onlyOperator() {\n require(\n hasRole(OPERATOR_ROLE, msg.sender),\n \"Caller is not an operator\"\n );\n _;\n }\n\n constructor(address _safeContract) {\n safeContract = ISafe(_safeContract);\n _grantRole(DEFAULT_ADMIN_ROLE, address(safeContract));\n _grantRole(OPERATOR_ROLE, address(safeContract));\n }\n\n /**\n * @dev Helps recovering any tokens accidentally sent to this module.\n * @param token Token to transfer. 0x0 to transfer Native token.\n * @param amount Amount to transfer. 0 to transfer all balance.\n */\n function transferTokens(address token, uint256 amount) external onlySafe {\n if (address(token) == address(0)) {\n // Move ETH\n amount = amount > 0 ? amount : address(this).balance;\n payable(address(safeContract)).transfer(amount);\n return;\n }\n\n // Move all balance if amount set to 0\n amount = amount > 0 ? amount : IERC20(token).balanceOf(address(this));\n\n // Transfer to Safe contract\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(token).transfer(address(safeContract), amount);\n }\n\n receive() external payable {\n // Accept ETH to pay for bridge fees\n }\n}\n" + }, + "contracts/automation/BaseBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// solhint-disable-next-line max-line-length\nimport { AbstractCCIPBridgeHelperModule, AbstractSafeModule, IRouterClient } from \"./AbstractCCIPBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { BridgedWOETHStrategy } from \"../strategies/BridgedWOETHStrategy.sol\";\n\ncontract BaseBridgeHelperModule is\n AccessControlEnumerable,\n AbstractCCIPBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0x98a0CbeF61bD2D21435f433bE4CD42B56B38CC93);\n IWETH9 public constant weth =\n IWETH9(0x4200000000000000000000000000000000000006);\n IERC20 public constant oethb =\n IERC20(0xDBFeFD2e8460a6Ee4955A68582F85708BAEA60A3);\n IERC4626 public constant bridgedWOETH =\n IERC4626(0xD8724322f44E5c58D7A815F542036fb17DbbF839);\n\n BridgedWOETHStrategy public constant bridgedWOETHStrategy =\n BridgedWOETHStrategy(0x80c864704DD06C3693ed5179190786EE38ACf835);\n\n IRouterClient public constant CCIP_ROUTER =\n IRouterClient(0x881e3A65B4d4a04dD529061dd0071cf975F58bCD);\n\n uint64 public constant CCIP_ETHEREUM_CHAIN_SELECTOR = 5009297550715157269;\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Ethereum.\n * @param woethAmount Amount of wOETH to bridge.\n */\n function bridgeWOETHToEthereum(uint256 woethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_ETHEREUM_CHAIN_SELECTOR,\n IERC20(address(bridgedWOETH)),\n woethAmount\n );\n }\n\n /**\n * @dev Bridges WETH to Ethereum.\n * @param wethAmount Amount of WETH to bridge.\n */\n function bridgeWETHToEthereum(uint256 wethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_ETHEREUM_CHAIN_SELECTOR,\n IERC20(address(weth)),\n wethAmount\n );\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem the wOETH for WETH using the Vault.\n * @return Amount of WETH received.\n */\n function depositWOETH(uint256 woethAmount, bool redeemWithVault)\n external\n onlyOperator\n returns (uint256)\n {\n return _depositWOETH(woethAmount, redeemWithVault);\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy and bridges it to Ethereum.\n * @param woethAmount Amount of wOETH to deposit.\n * @return Amount of WETH received.\n */\n function depositWOETHAndBridgeWETH(uint256 woethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n uint256 wethAmount = _depositWOETH(woethAmount, true);\n bridgeWETHToEthereum(wethAmount);\n return wethAmount;\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem the wOETH for WETH using the Vault.\n * @return Amount of WETH received.\n */\n function _depositWOETH(uint256 woethAmount, bool redeemWithVault)\n internal\n returns (uint256)\n {\n // Update oracle price\n bridgedWOETHStrategy.updateWOETHOraclePrice();\n\n // Rebase to account for any yields from price update\n vault.rebase();\n\n uint256 oethbAmount = oethb.balanceOf(address(safeContract));\n\n // Approve bridgedWOETH strategy to move wOETH\n bool success = safeContract.execTransactionFromModule(\n address(bridgedWOETH),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETH.approve.selector,\n address(bridgedWOETHStrategy),\n woethAmount\n ),\n 0 // Call\n );\n\n // Deposit to bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.depositBridgedWOETH.selector,\n woethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to deposit bridged WOETH\");\n\n oethbAmount = oethb.balanceOf(address(safeContract)) - oethbAmount;\n\n // Rebase to account for any yields from price update\n // and backing asset change from deposit\n vault.rebase();\n\n if (!redeemWithVault) {\n return oethbAmount;\n }\n\n // Redeem for WETH using Vault\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethbAmount,\n oethbAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETHb\");\n\n return oethbAmount;\n }\n\n /**\n * @dev Deposits WETH into the Vault and redeems wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to deposit.\n * @return Amount of wOETH received.\n */\n function depositWETHAndRedeemWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _withdrawWOETH(wethAmount);\n }\n\n function depositWETHAndBridgeWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n uint256 woethAmount = _withdrawWOETH(wethAmount);\n bridgeWOETHToEthereum(woethAmount);\n return woethAmount;\n }\n\n /**\n * @dev Withdraws wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to use to withdraw.\n * @return Amount of wOETH received.\n */\n function _withdrawWOETH(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETHb with WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETHb\");\n\n // Approve bridgedWOETH strategy to move OETHb\n success = safeContract.execTransactionFromModule(\n address(oethb),\n 0, // Value\n abi.encodeWithSelector(\n oethb.approve.selector,\n address(bridgedWOETHStrategy),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETHb\");\n\n uint256 woethAmount = bridgedWOETH.balanceOf(address(safeContract));\n\n // Withdraw from bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.withdrawBridgedWOETH.selector,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to withdraw bridged WOETH\");\n\n woethAmount =\n bridgedWOETH.balanceOf(address(safeContract)) -\n woethAmount;\n\n return woethAmount;\n }\n}\n" + }, + "contracts/automation/ClaimBribesSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { ICLGauge } from \"../interfaces/aerodrome/ICLGauge.sol\";\nimport { ICLPool } from \"../interfaces/aerodrome/ICLPool.sol\";\n\nstruct BribePoolInfo {\n address poolAddress;\n address rewardContractAddress;\n address[] rewardTokens;\n}\n\ninterface IAerodromeVoter {\n function claimBribes(\n address[] memory _bribes,\n address[][] memory _tokens,\n uint256 _tokenId\n ) external;\n}\n\ninterface IVeNFT {\n function ownerOf(uint256 tokenId) external view returns (address);\n\n function ownerToNFTokenIdList(address owner, uint256 index)\n external\n view\n returns (uint256);\n}\n\ninterface ICLRewardContract {\n function rewards(uint256 index) external view returns (address);\n\n function rewardsListLength() external view returns (uint256);\n}\n\ncontract ClaimBribesSafeModule is AbstractSafeModule {\n IAerodromeVoter public immutable voter;\n address public immutable veNFT;\n\n uint256[] nftIds;\n mapping(uint256 => uint256) nftIdIndex;\n\n BribePoolInfo[] bribePools;\n mapping(address => uint256) bribePoolIndex;\n\n event NFTIdAdded(uint256 nftId);\n event NFTIdRemoved(uint256 nftId);\n\n event BribePoolAdded(address bribePool);\n event BribePoolRemoved(address bribePool);\n\n constructor(\n address _safeContract,\n address _voter,\n address _veNFT\n ) AbstractSafeModule(_safeContract) {\n voter = IAerodromeVoter(_voter);\n veNFT = _veNFT;\n }\n\n /**\n * @dev Claim bribes for a range of NFTs\n * @param nftIndexStart The start index of the NFTs\n * @param nftIndexEnd The end index of the NFTs\n * @param silent Doesn't revert if the claim fails when true\n */\n function claimBribes(\n uint256 nftIndexStart,\n uint256 nftIndexEnd,\n bool silent\n ) external onlyOperator {\n if (nftIndexEnd < nftIndexStart) {\n (nftIndexStart, nftIndexEnd) = (nftIndexEnd, nftIndexStart);\n }\n uint256 nftCount = nftIds.length;\n nftIndexEnd = nftCount < nftIndexEnd ? nftCount : nftIndexEnd;\n\n (\n address[] memory rewardContractAddresses,\n address[][] memory rewardTokens\n ) = _getRewardsInfoArray();\n\n for (uint256 i = nftIndexStart; i < nftIndexEnd; i++) {\n uint256 nftId = nftIds[i];\n bool success = safeContract.execTransactionFromModule(\n address(voter),\n 0, // Value\n abi.encodeWithSelector(\n IAerodromeVoter.claimBribes.selector,\n rewardContractAddresses,\n rewardTokens,\n nftId\n ),\n 0 // Call\n );\n\n require(success || silent, \"ClaimBribes failed\");\n }\n }\n\n /**\n * @dev Get the reward contract address and reward tokens for all pools\n * @return rewardContractAddresses The reward contract addresses\n * @return rewardTokens The reward tokens\n */\n function _getRewardsInfoArray()\n internal\n view\n returns (\n address[] memory rewardContractAddresses,\n address[][] memory rewardTokens\n )\n {\n BribePoolInfo[] memory _bribePools = bribePools;\n uint256 bribePoolCount = _bribePools.length;\n rewardContractAddresses = new address[](bribePoolCount);\n rewardTokens = new address[][](bribePoolCount);\n\n for (uint256 i = 0; i < bribePoolCount; i++) {\n rewardContractAddresses[i] = _bribePools[i].rewardContractAddress;\n rewardTokens[i] = _bribePools[i].rewardTokens;\n }\n }\n\n /***************************************\n NFT Management\n ****************************************/\n /**\n * @dev Add NFT IDs to the list\n * @param _nftIds The NFT IDs to add\n */\n function addNFTIds(uint256[] memory _nftIds) external onlyOperator {\n for (uint256 i = 0; i < _nftIds.length; i++) {\n uint256 nftId = _nftIds[i];\n if (nftIdExists(nftId)) {\n // If it already exists, skip\n continue;\n }\n\n // Make sure the NFT is owned by the Safe\n require(\n IVeNFT(veNFT).ownerOf(nftId) == address(safeContract),\n \"NFT not owned by safe\"\n );\n\n nftIdIndex[nftId] = nftIds.length;\n nftIds.push(nftId);\n\n emit NFTIdAdded(nftId);\n }\n }\n\n /**\n * @dev Remove NFT IDs from the list\n * @param _nftIds The NFT IDs to remove\n */\n function removeNFTIds(uint256[] memory _nftIds) external onlyOperator {\n for (uint256 i = 0; i < _nftIds.length; i++) {\n uint256 nftId = _nftIds[i];\n if (!nftIdExists(nftId)) {\n // If it doesn't exist, skip\n continue;\n }\n\n uint256 index = nftIdIndex[nftId];\n uint256 lastNftId = nftIds[nftIds.length - 1];\n nftIds[index] = lastNftId;\n nftIdIndex[lastNftId] = index;\n nftIds.pop();\n\n emit NFTIdRemoved(nftId);\n }\n }\n\n /**\n * @dev Check if a NFT exists on the list\n * @param nftId The NFT ID to check\n * @return true if the NFT ID exists, false otherwise\n */\n function nftIdExists(uint256 nftId) public view returns (bool) {\n uint256 index = nftIdIndex[nftId];\n uint256[] memory _nftIds = nftIds;\n return (index < _nftIds.length) && _nftIds[index] == nftId;\n }\n\n /**\n * @dev Get the length of the nftIds list\n * @return The length of the nftIds list\n */\n function getNFTIdsLength() external view returns (uint256) {\n return nftIds.length;\n }\n\n /**\n * @dev Get all NFT IDs\n * @return The NFT IDs\n */\n function getAllNFTIds() external view returns (uint256[] memory) {\n return nftIds;\n }\n\n /**\n * @dev Fetch all NFT IDs from the veNFT contract\n * @notice This can revert if Safe owns too many NFTs since tx will be huge.\n * This function is public, anyone can call it, since it only fetches\n * the NFT IDs owned by the Safe. It shouldn't cause us any issues.\n */\n function fetchNFTIds() external {\n // Purge the array\n delete nftIds;\n\n uint256 i = 0;\n while (true) {\n uint256 nftId = IVeNFT(veNFT).ownerToNFTokenIdList(\n address(safeContract),\n i\n );\n if (nftId == 0) {\n break;\n }\n\n nftIdIndex[nftId] = nftIds.length;\n nftIds.push(nftId);\n i++;\n }\n }\n\n /**\n * @dev Remove all NFT IDs from the list\n */\n function removeAllNFTIds() external onlyOperator {\n uint256 length = nftIds.length;\n for (uint256 i = 0; i < length; i++) {\n uint256 nftId = nftIds[i];\n delete nftIdIndex[nftId];\n emit NFTIdRemoved(nftId);\n }\n\n delete nftIds;\n }\n\n /***************************************\n Bribe Pool Management\n ****************************************/\n // @dev Whitelist a pool to claim bribes from\n // @param _poolAddress The address of the pool to whitelist\n function addBribePool(address _poolAddress, bool _isVotingContract)\n external\n onlySafe\n {\n BribePoolInfo memory bribePool;\n\n if (_isVotingContract) {\n bribePool = BribePoolInfo({\n poolAddress: _poolAddress,\n rewardContractAddress: _poolAddress,\n rewardTokens: _getRewardTokenAddresses(_poolAddress)\n });\n } else {\n // Find the gauge address\n address _gaugeAddress = ICLPool(_poolAddress).gauge();\n // And the reward contract address\n address _rewardContractAddress = ICLGauge(_gaugeAddress)\n .feesVotingReward();\n\n bribePool = BribePoolInfo({\n poolAddress: _poolAddress,\n rewardContractAddress: _rewardContractAddress,\n rewardTokens: _getRewardTokenAddresses(_rewardContractAddress)\n });\n }\n\n if (bribePoolExists(_poolAddress)) {\n // Update if it already exists\n bribePools[bribePoolIndex[_poolAddress]] = bribePool;\n } else {\n // If not, Append to the list\n bribePoolIndex[_poolAddress] = bribePools.length;\n bribePools.push(bribePool);\n }\n\n emit BribePoolAdded(_poolAddress);\n }\n\n /**\n * @dev Update the reward token addresses for all pools\n */\n function updateRewardTokenAddresses() external onlyOperator {\n BribePoolInfo[] storage _bribePools = bribePools;\n for (uint256 i = 0; i < _bribePools.length; i++) {\n BribePoolInfo storage bribePool = _bribePools[i];\n bribePool.rewardTokens = _getRewardTokenAddresses(\n bribePool.rewardContractAddress == bribePool.poolAddress\n ? bribePool.poolAddress\n : bribePool.rewardContractAddress\n );\n }\n }\n\n /**\n * @dev Get the reward token addresses for a given reward contract address\n * @param _rewardContractAddress The address of the reward contract\n * @return _rewardTokens The reward token addresses\n */\n function _getRewardTokenAddresses(address _rewardContractAddress)\n internal\n view\n returns (address[] memory)\n {\n address[] memory _rewardTokens = new address[](\n ICLRewardContract(_rewardContractAddress).rewardsListLength()\n );\n for (uint256 i = 0; i < _rewardTokens.length; i++) {\n _rewardTokens[i] = ICLRewardContract(_rewardContractAddress)\n .rewards(i);\n }\n\n return _rewardTokens;\n }\n\n /**\n * @dev Remove a bribe pool from the list\n * @param _poolAddress The address of the pool to remove\n */\n function removeBribePool(address _poolAddress) external onlySafe {\n if (!bribePoolExists(_poolAddress)) {\n // If it doesn't exist, skip\n return;\n }\n\n uint256 index = bribePoolIndex[_poolAddress];\n BribePoolInfo memory lastBribePool = bribePools[bribePools.length - 1];\n bribePools[index] = lastBribePool;\n bribePoolIndex[lastBribePool.poolAddress] = index;\n bribePools.pop();\n\n emit BribePoolRemoved(_poolAddress);\n }\n\n /**\n * @dev Check if a bribe pool exists\n * @param bribePool The address of the pool to check\n * @return true if the pool exists, false otherwise\n */\n function bribePoolExists(address bribePool) public view returns (bool) {\n BribePoolInfo[] memory _bribePools = bribePools;\n uint256 poolIndex = bribePoolIndex[bribePool];\n return\n poolIndex < _bribePools.length &&\n _bribePools[poolIndex].poolAddress == bribePool;\n }\n\n /**\n * @dev Get the length of the bribe pools list\n * @return The length of the bribe pools list\n */\n function getBribePoolsLength() external view returns (uint256) {\n return bribePools.length;\n }\n}\n" + }, + "contracts/automation/ClaimStrategyRewardsSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\n\nimport { ISafe } from \"../interfaces/ISafe.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract ClaimStrategyRewardsSafeModule is AbstractSafeModule {\n using SafeERC20 for IERC20;\n\n mapping(address => bool) public isStrategyWhitelisted;\n address[] public strategies;\n\n event StrategyAdded(address strategy);\n event StrategyRemoved(address strategy);\n\n event ClaimRewardsFailed(address strategy);\n\n constructor(\n address _safeAddress,\n address operator,\n address[] memory _strategies\n ) AbstractSafeModule(_safeAddress) {\n _grantRole(OPERATOR_ROLE, operator);\n\n // Whitelist all strategies\n for (uint256 i = 0; i < _strategies.length; i++) {\n _addStrategy(_strategies[i]);\n }\n }\n\n /**\n * @dev Claim rewards from all whitelisted strategies\n * @param silent Doesn't revert on error if set to true\n */\n function claimRewards(bool silent) external onlyRole(OPERATOR_ROLE) {\n uint256 strategiesLength = strategies.length;\n for (uint256 i = 0; i < strategiesLength; i++) {\n address strategy = strategies[i];\n\n // Execute `collectRewardTokens` for all strategies\n bool success = safeContract.execTransactionFromModule(\n strategy, // To\n 0, // Value\n abi.encodeWithSelector(IStrategy.collectRewardTokens.selector),\n 0 // Call\n );\n\n if (!success) {\n emit ClaimRewardsFailed(strategy);\n }\n\n require(success || silent, \"Failed to claim rewards\");\n }\n }\n\n /**\n * @dev Add a strategy to the whitelist\n * @param _strategy The address of the strategy to add\n */\n function addStrategy(address _strategy)\n external\n onlyRole(DEFAULT_ADMIN_ROLE)\n {\n _addStrategy(_strategy);\n }\n\n function _addStrategy(address _strategy) internal {\n require(\n !isStrategyWhitelisted[_strategy],\n \"Strategy already whitelisted\"\n );\n isStrategyWhitelisted[_strategy] = true;\n strategies.push(_strategy);\n emit StrategyAdded(_strategy);\n }\n\n /**\n * @dev Remove a strategy from the whitelist\n * @param _strategy The address of the strategy to remove\n */\n function removeStrategy(address _strategy)\n external\n onlyRole(DEFAULT_ADMIN_ROLE)\n {\n require(isStrategyWhitelisted[_strategy], \"Strategy not whitelisted\");\n isStrategyWhitelisted[_strategy] = false;\n\n for (uint256 i = 0; i < strategies.length; i++) {\n if (strategies[i] == _strategy) {\n strategies[i] = strategies[strategies.length - 1];\n strategies.pop();\n break;\n }\n }\n\n emit StrategyRemoved(_strategy);\n }\n}\n" + }, + "contracts/automation/CollectXOGNRewardsModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IXOGN {\n function collectRewards() external;\n}\n\ncontract CollectXOGNRewardsModule is AbstractSafeModule {\n IXOGN public constant xogn =\n IXOGN(0x63898b3b6Ef3d39332082178656E9862bee45C57);\n address public constant rewardsSource =\n 0x7609c88E5880e934dd3A75bCFef44E31b1Badb8b;\n IERC20 public constant ogn =\n IERC20(0x8207c1FfC5B6804F6024322CcF34F29c3541Ae26);\n\n constructor(address _safeContract, address operator)\n AbstractSafeModule(_safeContract)\n {\n _grantRole(OPERATOR_ROLE, operator);\n }\n\n function collectRewards() external onlyOperator {\n uint256 balance = ogn.balanceOf(address(safeContract));\n\n bool success = safeContract.execTransactionFromModule(\n address(xogn),\n 0, // Value\n abi.encodeWithSelector(IXOGN.collectRewards.selector),\n 0 // Call\n );\n\n require(success, \"Failed to collect rewards\");\n\n balance = ogn.balanceOf(address(safeContract)) - balance;\n\n if (balance == 0) {\n return;\n }\n\n success = safeContract.execTransactionFromModule(\n address(ogn),\n 0, // Value\n abi.encodeWithSelector(\n IERC20.transfer.selector,\n rewardsSource,\n balance\n ),\n 0 // Call\n );\n\n require(success, \"Failed to collect rewards\");\n }\n}\n" + }, + "contracts/automation/EthereumBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractLZBridgeHelperModule, AbstractSafeModule } from \"./AbstractLZBridgeHelperModule.sol\";\nimport { AbstractCCIPBridgeHelperModule, IRouterClient } from \"./AbstractCCIPBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IOFT } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract EthereumBridgeHelperModule is\n AccessControlEnumerable,\n AbstractLZBridgeHelperModule,\n AbstractCCIPBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0x39254033945AA2E4809Cc2977E7087BEE48bd7Ab);\n IWETH9 public constant weth =\n IWETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);\n IERC20 public constant oeth =\n IERC20(0x856c4Efb76C1D1AE02e20CEB03A2A6a08b0b8dC3);\n IERC4626 public constant woeth =\n IERC4626(0xDcEe70654261AF21C44c093C300eD3Bb97b78192);\n\n uint32 public constant LZ_PLUME_ENDPOINT_ID = 30370;\n IOFT public constant LZ_WOETH_OMNICHAIN_ADAPTER =\n IOFT(0x7d1bEa5807e6af125826d56ff477745BB89972b8);\n IOFT public constant LZ_ETH_OMNICHAIN_ADAPTER =\n IOFT(0x77b2043768d28E9C9aB44E1aBfC95944bcE57931);\n\n IRouterClient public constant CCIP_ROUTER =\n IRouterClient(0x80226fc0Ee2b096224EeAc085Bb9a8cba1146f7D);\n\n uint64 public constant CCIP_BASE_CHAIN_SELECTOR = 15971525489660198786;\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Plume.\n * @param woethAmount Amount of wOETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWOETHToPlume(uint256 woethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_PLUME_ENDPOINT_ID,\n woeth,\n LZ_WOETH_OMNICHAIN_ADAPTER,\n woethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Bridges wOETH to Base using CCIP.\n * @param woethAmount Amount of wOETH to bridge.\n */\n function bridgeWOETHToBase(uint256 woethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_BASE_CHAIN_SELECTOR,\n woeth,\n woethAmount\n );\n }\n\n /**\n * @dev Bridges wETH to Plume.\n * @param wethAmount Amount of wETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWETHToPlume(uint256 wethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n // Unwrap into ETH\n safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(weth.withdraw.selector, wethAmount),\n 0 // Call\n );\n\n _bridgeTokenWithLz(\n LZ_PLUME_ENDPOINT_ID,\n IERC20(address(weth)),\n LZ_ETH_OMNICHAIN_ADAPTER,\n wethAmount,\n slippageBps,\n true\n );\n }\n\n /**\n * @dev Bridges wETH to Base using CCIP.\n * @param wethAmount Amount of wETH to bridge.\n */\n function bridgeWETHToBase(uint256 wethAmount) public payable onlyOperator {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_BASE_CHAIN_SELECTOR,\n IERC20(address(weth)),\n wethAmount\n );\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH.\n * @param wethAmount Amount of WETH to mint.\n * @param useNativeToken Whether to use native token to mint.\n * @return Amount of wOETH minted.\n */\n function mintAndWrap(uint256 wethAmount, bool useNativeToken)\n external\n onlyOperator\n returns (uint256)\n {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n return _mintAndWrap(wethAmount);\n }\n\n function wrapETH(uint256 ethAmount) public payable onlyOperator {\n // Deposit ETH into WETH\n safeContract.execTransactionFromModule(\n address(weth),\n ethAmount, // Value\n abi.encodeWithSelector(weth.deposit.selector),\n 0 // Call\n );\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH.\n * @param wethAmount Amount of WETH to mint.\n * @return Amount of wOETH minted.\n */\n function _mintAndWrap(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETH\");\n\n // Approve wOETH to move OETH\n success = safeContract.execTransactionFromModule(\n address(oeth),\n 0, // Value\n abi.encodeWithSelector(\n oeth.approve.selector,\n address(woeth),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETH\");\n\n uint256 woethAmount = woeth.balanceOf(address(safeContract));\n\n // Wrap OETH into wOETH\n success = safeContract.execTransactionFromModule(\n address(woeth),\n 0, // Value\n abi.encodeWithSelector(\n woeth.deposit.selector,\n wethAmount,\n address(safeContract)\n ),\n 0 // Call\n );\n require(success, \"Failed to wrap OETH\");\n\n // Compute amount of wOETH minted\n return woeth.balanceOf(address(safeContract)) - woethAmount;\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH, then bridges it to Plume.\n * @param wethAmount Amount of WETH to mint.\n * @param slippageBps Bridge slippage in 10^4 basis points.\n * @param useNativeToken Whether to use native token to mint.\n */\n function mintWrapAndBridgeToPlume(\n uint256 wethAmount,\n uint256 slippageBps,\n bool useNativeToken\n ) external payable onlyOperator {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n uint256 woethAmount = _mintAndWrap(wethAmount);\n bridgeWOETHToPlume(woethAmount, slippageBps);\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH, then bridges it to Base using CCIP.\n * @param wethAmount Amount of WETH to mint.\n * @param useNativeToken Whether to use native token to mint.\n */\n function mintWrapAndBridgeToBase(uint256 wethAmount, bool useNativeToken)\n external\n payable\n onlyOperator\n {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n uint256 woethAmount = _mintAndWrap(wethAmount);\n bridgeWOETHToBase(woethAmount);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH.\n * @param woethAmount Amount of wOETH to unwrap.\n * @return Amount of WETH received.\n */\n function unwrapAndRedeem(uint256 woethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _unwrapAndRedeem(woethAmount);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH.\n * @param woethAmount Amount of wOETH to unwrap.\n * @return Amount of WETH received.\n */\n function _unwrapAndRedeem(uint256 woethAmount) internal returns (uint256) {\n uint256 oethAmount = oeth.balanceOf(address(safeContract));\n\n // Unwrap wOETH\n bool success = safeContract.execTransactionFromModule(\n address(woeth),\n 0, // Value\n abi.encodeWithSelector(\n woeth.redeem.selector,\n woethAmount,\n address(safeContract),\n address(safeContract)\n ),\n 0 // Call\n );\n require(success, \"Failed to unwrap wOETH\");\n\n oethAmount = oeth.balanceOf(address(safeContract)) - oethAmount;\n\n // Redeem OETH using Vault to get WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethAmount,\n oethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETH\");\n\n return oethAmount;\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH, then bridges it to Plume.\n * @param woethAmount Amount of wOETH to unwrap.\n * @param slippageBps Bridge slippage in 10^4 basis points.\n */\n function unwrapRedeemAndBridgeToPlume(\n uint256 woethAmount,\n uint256 slippageBps\n ) external payable onlyOperator {\n uint256 wethAmount = _unwrapAndRedeem(woethAmount);\n // Unwrap into ETH\n safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(weth.withdraw.selector, wethAmount),\n 0 // Call\n );\n\n bridgeWETHToPlume(wethAmount, slippageBps);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH, then bridges it to Base using CCIP.\n * @param woethAmount Amount of wOETH to unwrap.\n */\n function unwrapRedeemAndBridgeToBase(uint256 woethAmount)\n external\n payable\n onlyOperator\n {\n uint256 wethAmount = _unwrapAndRedeem(woethAmount);\n bridgeWETHToBase(wethAmount);\n }\n}\n" + }, + "contracts/automation/PlumeBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { AbstractLZBridgeHelperModule } from \"./AbstractLZBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IOFT } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { BridgedWOETHStrategy } from \"../strategies/BridgedWOETHStrategy.sol\";\n\ncontract PlumeBridgeHelperModule is\n AccessControlEnumerable,\n AbstractLZBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0xc8c8F8bEA5631A8AF26440AF32a55002138cB76a);\n IWETH9 public constant weth =\n IWETH9(0xca59cA09E5602fAe8B629DeE83FfA819741f14be);\n IERC20 public constant oethp =\n IERC20(0xFCbe50DbE43bF7E5C88C6F6Fb9ef432D4165406E);\n IERC4626 public constant bridgedWOETH =\n IERC4626(0xD8724322f44E5c58D7A815F542036fb17DbbF839);\n\n uint32 public constant LZ_ETHEREUM_ENDPOINT_ID = 30101;\n IOFT public constant LZ_WOETH_OMNICHAIN_ADAPTER =\n IOFT(0x592CB6A596E7919930bF49a27AdAeCA7C055e4DB);\n IOFT public constant LZ_ETH_OMNICHAIN_ADAPTER =\n IOFT(0x4683CE822272CD66CEa73F5F1f9f5cBcaEF4F066);\n\n BridgedWOETHStrategy public constant bridgedWOETHStrategy =\n BridgedWOETHStrategy(0x1E3EdD5e019207D6355Ea77F724b1F1BF639B569);\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Ethereum.\n * @param woethAmount Amount of wOETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWOETHToEthereum(uint256 woethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_ETHEREUM_ENDPOINT_ID,\n IERC20(address(bridgedWOETH)),\n LZ_WOETH_OMNICHAIN_ADAPTER,\n woethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Bridges wETH to Ethereum.\n * @param wethAmount Amount of wETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWETHToEthereum(uint256 wethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_ETHEREUM_ENDPOINT_ID,\n IERC20(address(weth)),\n LZ_ETH_OMNICHAIN_ADAPTER,\n wethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem with Vault.\n * @return Amount of OETHp received.\n */\n function depositWOETH(uint256 woethAmount, bool redeemWithVault)\n external\n onlyOperator\n returns (uint256)\n {\n return _depositWOETH(woethAmount, redeemWithVault);\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy and bridges it to Ethereum.\n * @param woethAmount Amount of wOETH to deposit.\n * @param slippageBps Slippage in 10^4 basis points.\n * @return Amount of WETH received.\n */\n function depositWOETHAndBridgeWETH(uint256 woethAmount, uint256 slippageBps)\n external\n payable\n onlyOperator\n returns (uint256)\n {\n uint256 wethAmount = _depositWOETH(woethAmount, true);\n bridgeWETHToEthereum(wethAmount, slippageBps);\n return wethAmount;\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem with Vault.\n * @return Amount of OETHp received.\n */\n function _depositWOETH(uint256 woethAmount, bool redeemWithVault)\n internal\n returns (uint256)\n {\n // Update oracle price\n bridgedWOETHStrategy.updateWOETHOraclePrice();\n\n // Rebase to account for any yields from price update\n vault.rebase();\n\n uint256 oethpAmount = oethp.balanceOf(address(safeContract));\n\n // Approve bridgedWOETH strategy to move wOETH\n bool success = safeContract.execTransactionFromModule(\n address(bridgedWOETH),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETH.approve.selector,\n address(bridgedWOETHStrategy),\n woethAmount\n ),\n 0 // Call\n );\n\n // Deposit to bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.depositBridgedWOETH.selector,\n woethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to deposit bridged WOETH\");\n\n oethpAmount = oethp.balanceOf(address(safeContract)) - oethpAmount;\n\n // Rebase to account for any yields from price update\n // and backing asset change from deposit\n vault.rebase();\n\n if (!redeemWithVault) {\n return oethpAmount;\n }\n\n // Redeem for WETH using Vault\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethpAmount,\n oethpAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETHp\");\n\n return oethpAmount;\n }\n\n /**\n * @dev Deposits wETH into the vault.\n * @param wethAmount Amount of wETH to deposit.\n * @return Amount of OETHp received.\n */\n function depositWETHAndRedeemWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _withdrawWOETH(wethAmount);\n }\n\n /**\n * @dev Deposits wETH into the vault and bridges it to Ethereum.\n * @param wethAmount Amount of wETH to deposit.\n * @param slippageBps Slippage in 10^4 basis points.\n * @return Amount of WOETH received.\n */\n function depositWETHAndBridgeWOETH(uint256 wethAmount, uint256 slippageBps)\n external\n payable\n onlyOperator\n returns (uint256)\n {\n uint256 woethAmount = _withdrawWOETH(wethAmount);\n bridgeWOETHToEthereum(woethAmount, slippageBps);\n return woethAmount;\n }\n\n /**\n * @dev Withdraws wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to use to withdraw.\n * @return Amount of wOETH received.\n */\n function _withdrawWOETH(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETHp with WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETHp\");\n\n // Approve bridgedWOETH strategy to move OETHp\n success = safeContract.execTransactionFromModule(\n address(oethp),\n 0, // Value\n abi.encodeWithSelector(\n oethp.approve.selector,\n address(bridgedWOETHStrategy),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETHp\");\n\n uint256 woethAmount = bridgedWOETH.balanceOf(address(safeContract));\n\n // Withdraw from bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.withdrawBridgedWOETH.selector,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to withdraw bridged WOETH\");\n\n woethAmount =\n bridgedWOETH.balanceOf(address(safeContract)) -\n woethAmount;\n\n return woethAmount;\n }\n}\n" + }, + "contracts/bridges/OmnichainL2Adapter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.0;\n\nimport { MintBurnOFTAdapter } from \"@layerzerolabs/oft-evm/contracts/MintBurnOFTAdapter.sol\";\nimport { IMintableBurnable } from \"@layerzerolabs/oft-evm/contracts/interfaces/IMintableBurnable.sol\";\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\n\n/// NOTE: It's necessary to inherit from Ownable instead of Governable\n/// because OFTCore uses Ownable to manage the governor.\n/// Ownable uses slot 0 for storing the address, whereas Goveranble\n/// stores it in a computed slot.\n\n/// @notice Omnichain uses a deployed ERC-20 token and safeERC20\n/// to interact with the OFTCore contract.\n/// On L2, we follow the mint and burn mechanism.\n/// The adapter should have minter and burner roles.\n\n/// @title Omnichain L2 Adapter\ncontract OmnichainL2Adapter is MintBurnOFTAdapter {\n constructor(\n address _token,\n address _lzEndpoint,\n address _governor\n )\n MintBurnOFTAdapter(\n _token,\n IMintableBurnable(_token),\n _lzEndpoint,\n _governor\n )\n Ownable()\n {\n _transferOwnership(_governor);\n }\n\n /// @inheritdoc MintBurnOFTAdapter\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n )\n internal\n virtual\n override\n returns (uint256 amountSentLD, uint256 amountReceivedLD)\n {\n (amountSentLD, amountReceivedLD) = _debitView(\n _amountLD,\n _minAmountLD,\n _dstEid\n );\n // Burns tokens from the caller.\n IMintableERC20(address(minterBurner)).burn(_from, amountSentLD);\n }\n\n /// @inheritdoc MintBurnOFTAdapter\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /* _srcEid */\n ) internal virtual override returns (uint256 amountReceivedLD) {\n if (_to == address(0x0)) _to = address(0xdead); // _mint(...) does not support address(0x0)\n // Mints the tokens and transfers to the recipient.\n IMintableERC20(address(minterBurner)).mint(_to, _amountLD);\n // In the case of NON-default OFTAdapter, the amountLD MIGHT not be equal to amountReceivedLD.\n return _amountLD;\n }\n}\n\ninterface IMintableERC20 {\n function mint(address to, uint256 value) external;\n\n function burn(address to, uint256 value) external;\n}\n" + }, + "contracts/bridges/OmnichainMainnetAdapter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.0;\n\nimport { OFTAdapter } from \"@layerzerolabs/oft-evm/contracts/OFTAdapter.sol\";\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\n\n/// NOTE: It's necessary to inherit from Ownable instead of Governable\n/// because OFTCore uses Ownable to manage the governor.\n/// Ownable uses slot 0 for storing the address, whereas Goveranble\n/// stores it in a computed slot.\n\n/// @notice Omnichain uses a deployed ERC-20 token and safeERC20\n/// to interact with the OFTCore contract.\n/// On Ethereum, we follow the lock and unlock mechanism.\n\n/// @title Omnichain Mainnet Adapter\ncontract OmnichainMainnetAdapter is OFTAdapter {\n constructor(\n address _token,\n address _lzEndpoint,\n address _governor\n ) OFTAdapter(_token, _lzEndpoint, _governor) Ownable() {\n _transferOwnership(_governor);\n }\n}\n" + }, + "contracts/buyback/AbstractBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { ICVXLocker } from \"../interfaces/ICVXLocker.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\n\nabstract contract AbstractBuyback is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n event SwapRouterUpdated(address indexed _address);\n\n event RewardsSourceUpdated(address indexed _address);\n event TreasuryManagerUpdated(address indexed _address);\n event CVXShareBpsUpdated(uint256 bps);\n\n // Emitted whenever OUSD/OETH is swapped for OGN/CVX or any other token\n event OTokenBuyback(\n address indexed oToken,\n address indexed swappedFor,\n uint256 swapAmountIn,\n uint256 amountOut\n );\n\n // Address of 1-inch Swap Router\n address public swapRouter;\n\n // slither-disable-next-line constable-states\n address private __deprecated_ousd;\n // slither-disable-next-line constable-states\n address private __deprecated_ogv;\n // slither-disable-next-line constable-states\n address private __deprecated_usdt;\n // slither-disable-next-line constable-states\n address private __deprecated_weth9;\n\n // Address that receives OGN after swaps\n address public rewardsSource;\n\n // Address that receives all other tokens after swaps\n address public treasuryManager;\n\n // slither-disable-next-line constable-states\n uint256 private __deprecated_treasuryBps;\n\n address public immutable oToken;\n address public immutable ogn;\n address public immutable cvx;\n address public immutable cvxLocker;\n\n // Amount of `oToken` balance to use for OGN buyback\n uint256 public balanceForOGN;\n\n // Amount of `oToken` balance to use for CVX buyback\n uint256 public balanceForCVX;\n\n // Percentage of `oToken` balance to be used for CVX\n uint256 public cvxShareBps; // 10000 = 100%\n\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) {\n // Make sure nobody owns the implementation contract\n _setGovernor(address(0));\n\n oToken = _oToken;\n ogn = _ogn;\n cvx = _cvx;\n cvxLocker = _cvxLocker;\n }\n\n /**\n * @param _swapRouter Address of Uniswap V3 Router\n * @param _strategistAddr Address of Strategist multi-sig wallet\n * @param _treasuryManagerAddr Address that receives the treasury's share of OUSD\n * @param _rewardsSource Address of RewardsSource contract\n * @param _cvxShareBps Percentage of balance to use for CVX\n */\n function initialize(\n address _swapRouter,\n address _strategistAddr,\n address _treasuryManagerAddr,\n address _rewardsSource,\n uint256 _cvxShareBps\n ) external onlyGovernor initializer {\n _setStrategistAddr(_strategistAddr);\n\n _setSwapRouter(_swapRouter);\n _setRewardsSource(_rewardsSource);\n\n _setTreasuryManager(_treasuryManagerAddr);\n\n _setCVXShareBps(_cvxShareBps);\n }\n\n /**\n * @dev Set address of Uniswap Universal Router for performing liquidation\n * of platform fee tokens. Setting to 0x0 will pause swaps.\n *\n * @param _router Address of the Uniswap Universal router\n */\n function setSwapRouter(address _router) external onlyGovernor {\n _setSwapRouter(_router);\n }\n\n function _setSwapRouter(address _router) internal {\n address oldRouter = swapRouter;\n swapRouter = _router;\n\n if (oldRouter != address(0)) {\n // Remove allowance of old router, if any\n\n if (IERC20(ogn).allowance(address(this), oldRouter) != 0) {\n // slither-disable-next-line unused-return\n IERC20(ogn).safeApprove(oldRouter, 0);\n }\n\n if (IERC20(cvx).allowance(address(this), oldRouter) != 0) {\n // slither-disable-next-line unused-return\n IERC20(cvx).safeApprove(oldRouter, 0);\n }\n }\n\n emit SwapRouterUpdated(_router);\n }\n\n /**\n * @dev Sets the address that receives the OGN buyback rewards\n * @param _address Address\n */\n function setRewardsSource(address _address) external onlyGovernor {\n _setRewardsSource(_address);\n }\n\n function _setRewardsSource(address _address) internal {\n require(_address != address(0), \"Address not set\");\n rewardsSource = _address;\n emit RewardsSourceUpdated(_address);\n }\n\n /**\n * @dev Sets the address that can receive and manage the funds for Treasury\n * @param _address Address\n */\n function setTreasuryManager(address _address) external onlyGovernor {\n _setTreasuryManager(_address);\n }\n\n function _setTreasuryManager(address _address) internal {\n require(_address != address(0), \"Address not set\");\n treasuryManager = _address;\n emit TreasuryManagerUpdated(_address);\n }\n\n /**\n * @dev Sets the percentage of oToken to use for Flywheel tokens\n * @param _bps BPS, 10000 to 100%\n */\n function setCVXShareBps(uint256 _bps) external onlyGovernor {\n _setCVXShareBps(_bps);\n }\n\n function _setCVXShareBps(uint256 _bps) internal {\n require(_bps <= 10000, \"Invalid bps value\");\n cvxShareBps = _bps;\n emit CVXShareBpsUpdated(_bps);\n }\n\n /**\n * @dev Computes the split of oToken balance that can be\n * used for OGN and CVX buybacks.\n */\n function _updateBuybackSplits()\n internal\n returns (uint256 _balanceForOGN, uint256 _balanceForCVX)\n {\n _balanceForOGN = balanceForOGN;\n _balanceForCVX = balanceForCVX;\n\n uint256 totalBalance = IERC20(oToken).balanceOf(address(this));\n uint256 unsplitBalance = totalBalance - _balanceForOGN - _balanceForCVX;\n\n // Check if all balance is accounted for\n if (unsplitBalance != 0) {\n // If not, split unaccounted balance based on `cvxShareBps`\n uint256 addToCVX = (unsplitBalance * cvxShareBps) / 10000;\n _balanceForCVX = _balanceForCVX + addToCVX;\n _balanceForOGN = _balanceForOGN + unsplitBalance - addToCVX;\n\n // Update storage\n balanceForOGN = _balanceForOGN;\n balanceForCVX = _balanceForCVX;\n }\n }\n\n function updateBuybackSplits() external onlyGovernor {\n // slither-disable-next-line unused-return\n _updateBuybackSplits();\n }\n\n function _swapToken(\n address tokenOut,\n uint256 oTokenAmount,\n uint256 minAmountOut,\n bytes calldata swapData\n ) internal returns (uint256 amountOut) {\n require(oTokenAmount > 0, \"Invalid Swap Amount\");\n require(swapRouter != address(0), \"Swap Router not set\");\n require(minAmountOut > 0, \"Invalid minAmount\");\n\n // Transfer OToken to Swapper for swapping\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(oToken).transfer(swapRouter, oTokenAmount);\n\n // Swap\n amountOut = ISwapper(swapRouter).swap(\n oToken,\n tokenOut,\n oTokenAmount,\n minAmountOut,\n swapData\n );\n\n require(amountOut >= minAmountOut, \"Higher Slippage\");\n\n emit OTokenBuyback(oToken, tokenOut, oTokenAmount, amountOut);\n }\n\n /**\n * @dev Swaps `oTokenAmount` to OGN\n * @param oTokenAmount Amount of OUSD/OETH to swap\n * @param minOGN Minimum OGN to receive for oTokenAmount\n * @param swapData 1inch Swap Data\n */\n function swapForOGN(\n uint256 oTokenAmount,\n uint256 minOGN,\n bytes calldata swapData\n ) external onlyGovernorOrStrategist nonReentrant {\n (uint256 _amountForOGN, ) = _updateBuybackSplits();\n require(_amountForOGN >= oTokenAmount, \"Balance underflow\");\n require(rewardsSource != address(0), \"RewardsSource contract not set\");\n\n unchecked {\n // Subtract the amount to swap from net balance\n balanceForOGN = _amountForOGN - oTokenAmount;\n }\n\n uint256 ognReceived = _swapToken(ogn, oTokenAmount, minOGN, swapData);\n\n // Transfer OGN received to RewardsSource contract\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(ogn).transfer(rewardsSource, ognReceived);\n }\n\n /**\n * @dev Swaps `oTokenAmount` to CVX\n * @param oTokenAmount Amount of OUSD/OETH to swap\n * @param minCVX Minimum CVX to receive for oTokenAmount\n * @param swapData 1inch Swap Data\n */\n function swapForCVX(\n uint256 oTokenAmount,\n uint256 minCVX,\n bytes calldata swapData\n ) external onlyGovernorOrStrategist nonReentrant {\n (, uint256 _amountForCVX) = _updateBuybackSplits();\n require(_amountForCVX >= oTokenAmount, \"Balance underflow\");\n\n unchecked {\n // Subtract the amount to swap from net balance\n balanceForCVX = _amountForCVX - oTokenAmount;\n }\n\n uint256 cvxReceived = _swapToken(cvx, oTokenAmount, minCVX, swapData);\n\n // Lock all CVX\n _lockAllCVX(cvxReceived);\n }\n\n /**\n * @dev Locks all CVX held by the contract on behalf of the Treasury Manager\n */\n function lockAllCVX() external onlyGovernorOrStrategist {\n _lockAllCVX(IERC20(cvx).balanceOf(address(this)));\n }\n\n function _lockAllCVX(uint256 cvxAmount) internal {\n require(\n treasuryManager != address(0),\n \"Treasury manager address not set\"\n );\n\n // Lock all available CVX on behalf of `treasuryManager`\n ICVXLocker(cvxLocker).lock(treasuryManager, cvxAmount, 0);\n }\n\n /**\n * @dev Approve CVX Locker to move CVX held by this contract\n */\n function safeApproveAllTokens() external onlyGovernorOrStrategist {\n IERC20(cvx).safeApprove(cvxLocker, type(uint256).max);\n }\n\n /**\n * @notice Owner function to withdraw a specific amount of a token\n * @param token token to be transferered\n * @param amount amount of the token to be transferred\n */\n function transferToken(address token, uint256 amount)\n external\n onlyGovernor\n nonReentrant\n {\n IERC20(token).safeTransfer(_governor(), amount);\n }\n}\n" + }, + "contracts/buyback/ARMBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract ARMBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/buyback/OETHBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract OETHBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/buyback/OUSDBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract OUSDBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/echidna/Debugger.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary Debugger {\n event Debug(string debugString);\n event Debug(string description, string data);\n event Debug(string prefix, string description, string data);\n event Debug(string description, bytes32 data);\n event Debug(string prefix, string description, bytes32 data);\n event Debug(string description, uint256 data);\n event Debug(string prefix, string description, uint256 data);\n event Debug(string description, int256 data);\n event Debug(string prefix, string description, int256 data);\n event Debug(string description, address data);\n event Debug(string prefix, string description, address data);\n event Debug(string description, bool data);\n event Debug(string prefix, string description, bool data);\n\n function log(string memory debugString) internal {\n emit Debug(debugString);\n }\n\n function log(string memory description, string memory data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n string memory data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, bytes32 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n bytes32 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, uint256 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n uint256 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, int256 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n int256 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, address data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n address data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, bool data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n bool data\n ) internal {\n emit Debug(prefix, description, data);\n }\n}\n" + }, + "contracts/echidna/Echidna.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaTestApproval.sol\";\n\n/**\n * @title Echidna test contract for OUSD\n * @notice Target contract to be tested, containing all mixins\n * @author Rappie\n */\ncontract Echidna is EchidnaTestApproval {\n\n}\n" + }, + "contracts/echidna/EchidnaConfig.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Top-level mixin for configuring the desired fuzzing setup\n * @author Rappie\n */\ncontract EchidnaConfig {\n address internal constant ADDRESS_VAULT = address(0x10000);\n address internal constant ADDRESS_OUTSIDER_USER = address(0x20000);\n\n address internal constant ADDRESS_USER0 = address(0x30000);\n address internal constant ADDRESS_USER1 = address(0x40000);\n\n // Will be set in EchidnaSetup constructor\n address internal ADDRESS_OUTSIDER_CONTRACT;\n address internal ADDRESS_CONTRACT0;\n address internal ADDRESS_CONTRACT1;\n\n // Toggle known issues\n //\n // This can be used to skip tests that are known to fail. This is useful\n // when debugging a specific issue, but should be disabled when running\n // the full test suite.\n //\n // True => skip tests that are known to fail\n // False => run all tests\n //\n bool internal constant TOGGLE_KNOWN_ISSUES = false;\n\n // Toggle known issues within limits\n //\n // Same as TOGGLE_KNOWN_ISSUES, but also skip tests that are known to fail\n // within limits set by the variables below.\n //\n bool internal constant TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS = true;\n\n // Starting balance\n //\n // Gives OUSD a non-zero starting supply, which can be useful to ignore\n // certain edge cases.\n //\n // The starting balance is given to outsider accounts that are not used as\n // accounts while fuzzing.\n //\n bool internal constant TOGGLE_STARTING_BALANCE = true;\n uint256 internal constant STARTING_BALANCE = 1_000_000e18;\n\n // Change supply\n //\n // Set a limit to the amount of change per rebase, which can be useful to\n // ignore certain edge cases.\n //\n // True => limit the amount of change to a percentage of total supply\n // False => no limit\n //\n bool internal constant TOGGLE_CHANGESUPPLY_LIMIT = true;\n uint256 internal constant CHANGESUPPLY_DIVISOR = 10; // 10% of total supply\n\n // Mint limit\n //\n // Set a limit the the amount minted per mint, which can be useful to\n // ignore certain edge cases.\n //\n // True => limit the amount of minted tokens\n // False => no limit\n //\n bool internal constant TOGGLE_MINT_LIMIT = true;\n uint256 internal constant MINT_MODULO = 1_000_000_000_000e18;\n\n // Known rounding errors\n uint256 internal constant TRANSFER_ROUNDING_ERROR = 1e18 - 1;\n uint256 internal constant OPT_IN_ROUNDING_ERROR = 1e18 - 1;\n uint256 internal constant MINT_ROUNDING_ERROR = 1e18 - 1;\n\n /**\n * @notice Modifier to skip tests that are known to fail\n * @dev see TOGGLE_KNOWN_ISSUES for more information\n */\n modifier hasKnownIssue() {\n if (TOGGLE_KNOWN_ISSUES) return;\n _;\n }\n\n /**\n * @notice Modifier to skip tests that are known to fail within limits\n * @dev see TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS for more information\n */\n modifier hasKnownIssueWithinLimits() {\n if (TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS) return;\n _;\n }\n\n /**\n * @notice Translate an account ID to an address\n * @param accountId The ID of the account\n * @return account The address of the account\n */\n function getAccount(uint8 accountId)\n internal\n view\n returns (address account)\n {\n accountId = accountId / 64;\n if (accountId == 0) return account = ADDRESS_USER0;\n if (accountId == 1) return account = ADDRESS_USER1;\n if (accountId == 2) return account = ADDRESS_CONTRACT0;\n if (accountId == 3) return account = ADDRESS_CONTRACT1;\n require(false, \"Unknown account ID\");\n }\n}\n" + }, + "contracts/echidna/EchidnaDebug.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport \"./EchidnaHelper.sol\";\nimport \"./Debugger.sol\";\n\nimport \"../token/OUSD.sol\";\n\n/**\n * @title Room for random debugging functions\n * @author Rappie\n */\ncontract EchidnaDebug is EchidnaHelper {\n function debugOUSD() public pure {\n // assert(ousd.balanceOf(ADDRESS_USER0) == 1000);\n // assert(ousd.rebaseState(ADDRESS_USER0) != OUSD.RebaseOptions.OptIn);\n // assert(Address.isContract(ADDRESS_CONTRACT0));\n // Debugger.log(\"nonRebasingSupply\", ousd.nonRebasingSupply());\n // assert(false);\n }\n}\n" + }, + "contracts/echidna/EchidnaHelper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaSetup.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin containing helper functions\n * @author Rappie\n */\ncontract EchidnaHelper is EchidnaSetup {\n /**\n * @notice Mint tokens to an account\n * @param toAcc Account to mint to\n * @param amount Amount to mint\n * @return Amount minted (in case of capped mint with modulo)\n */\n function mint(uint8 toAcc, uint256 amount) public returns (uint256) {\n address to = getAccount(toAcc);\n\n if (TOGGLE_MINT_LIMIT) {\n amount = amount % MINT_MODULO;\n }\n\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(to, amount);\n\n return amount;\n }\n\n /**\n * @notice Burn tokens from an account\n * @param fromAcc Account to burn from\n * @param amount Amount to burn\n */\n function burn(uint8 fromAcc, uint256 amount) public {\n address from = getAccount(fromAcc);\n hevm.prank(ADDRESS_VAULT);\n ousd.burn(from, amount);\n }\n\n /**\n * @notice Change the total supply of OUSD (rebase)\n * @param amount New total supply\n */\n function changeSupply(uint256 amount) public {\n if (TOGGLE_CHANGESUPPLY_LIMIT) {\n amount =\n ousd.totalSupply() +\n (amount % (ousd.totalSupply() / CHANGESUPPLY_DIVISOR));\n }\n\n hevm.prank(ADDRESS_VAULT);\n ousd.changeSupply(amount);\n }\n\n /**\n * @notice Transfer tokens between accounts\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function transfer(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n ousd.transfer(to, amount);\n }\n\n /**\n * @notice Transfer approved tokens between accounts\n * @param authorizedAcc Account that is authorized to transfer\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function transferFrom(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n ousd.transferFrom(from, to, amount);\n }\n\n /**\n * @notice Opt in to rebasing\n * @param targetAcc Account to opt in\n */\n function optIn(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n hevm.prank(target);\n ousd.rebaseOptIn();\n }\n\n /**\n * @notice Opt out of rebasing\n * @param targetAcc Account to opt out\n */\n function optOut(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n hevm.prank(target);\n ousd.rebaseOptOut();\n }\n\n /**\n * @notice Approve an account to spend OUSD\n * @param ownerAcc Account that owns the OUSD\n * @param spenderAcc Account that is approved to spend the OUSD\n * @param amount Amount to approve\n */\n function approve(\n uint8 ownerAcc,\n uint8 spenderAcc,\n uint256 amount\n ) public {\n address owner = getAccount(ownerAcc);\n address spender = getAccount(spenderAcc);\n hevm.prank(owner);\n // slither-disable-next-line unused-return\n ousd.approve(spender, amount);\n }\n\n /**\n * @notice Get the sum of all OUSD balances\n * @return total Total balance\n */\n function getTotalBalance() public view returns (uint256 total) {\n total += ousd.balanceOf(ADDRESS_VAULT);\n total += ousd.balanceOf(ADDRESS_OUTSIDER_USER);\n total += ousd.balanceOf(ADDRESS_OUTSIDER_CONTRACT);\n total += ousd.balanceOf(ADDRESS_USER0);\n total += ousd.balanceOf(ADDRESS_USER1);\n total += ousd.balanceOf(ADDRESS_CONTRACT0);\n total += ousd.balanceOf(ADDRESS_CONTRACT1);\n }\n\n /**\n * @notice Get the sum of all non-rebasing OUSD balances\n * @return total Total balance\n */\n function getTotalNonRebasingBalance() public returns (uint256 total) {\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_VAULT)\n ? ousd.balanceOf(ADDRESS_VAULT)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_OUTSIDER_USER)\n ? ousd.balanceOf(ADDRESS_OUTSIDER_USER)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_OUTSIDER_CONTRACT)\n ? ousd.balanceOf(ADDRESS_OUTSIDER_CONTRACT)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_USER0)\n ? ousd.balanceOf(ADDRESS_USER0)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_USER1)\n ? ousd.balanceOf(ADDRESS_USER1)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_CONTRACT0)\n ? ousd.balanceOf(ADDRESS_CONTRACT0)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_CONTRACT1)\n ? ousd.balanceOf(ADDRESS_CONTRACT1)\n : 0;\n }\n}\n" + }, + "contracts/echidna/EchidnaSetup.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./IHevm.sol\";\nimport \"./EchidnaConfig.sol\";\nimport \"./OUSDEchidna.sol\";\n\ncontract Dummy {}\n\n/**\n * @title Mixin for setup and deployment\n * @author Rappie\n */\ncontract EchidnaSetup is EchidnaConfig {\n IHevm hevm = IHevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);\n OUSDEchidna ousd = new OUSDEchidna();\n\n /**\n * @notice Deploy the OUSD contract and set up initial state\n */\n constructor() {\n ousd.initialize(ADDRESS_VAULT, 1e18);\n\n // Deploy dummny contracts as users\n Dummy outsider = new Dummy();\n ADDRESS_OUTSIDER_CONTRACT = address(outsider);\n Dummy dummy0 = new Dummy();\n ADDRESS_CONTRACT0 = address(dummy0);\n Dummy dummy1 = new Dummy();\n ADDRESS_CONTRACT1 = address(dummy1);\n\n // Start out with a reasonable amount of OUSD\n if (TOGGLE_STARTING_BALANCE) {\n // Rebasing tokens\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(ADDRESS_OUTSIDER_USER, STARTING_BALANCE / 2);\n\n // Non-rebasing tokens\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(ADDRESS_OUTSIDER_CONTRACT, STARTING_BALANCE / 2);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestAccounting.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestSupply.sol\";\n\n/**\n * @title Mixin for testing accounting functions\n * @author Rappie\n */\ncontract EchidnaTestAccounting is EchidnaTestSupply {\n /**\n * @notice After opting in, balance should not increase. (Ok to lose rounding funds doing this)\n * @param targetAcc Account to opt in\n */\n function testOptInBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optIn(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter <= balanceBefore);\n }\n\n /**\n * @notice After opting out, balance should remain the same\n * @param targetAcc Account to opt out\n */\n function testOptOutBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optOut(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Account balance should remain the same after opting in minus rounding error\n * @param targetAcc Account to opt in\n */\n function testOptInBalanceRounding(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optIn(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n int256 delta = int256(balanceAfter) - int256(balanceBefore);\n Debugger.log(\"delta\", delta);\n\n // slither-disable-next-line tautology\n assert(-1 * delta >= 0);\n assert(-1 * delta <= int256(OPT_IN_ROUNDING_ERROR));\n }\n\n /**\n * @notice After opting in, total supply should remain the same\n * @param targetAcc Account to opt in\n */\n function testOptInTotalSupply(uint8 targetAcc) public {\n uint256 totalSupplyBefore = ousd.totalSupply();\n optIn(targetAcc);\n uint256 totalSupplyAfter = ousd.totalSupply();\n\n assert(totalSupplyAfter == totalSupplyBefore);\n }\n\n /**\n * @notice After opting out, total supply should remain the same\n * @param targetAcc Account to opt out\n */\n function testOptOutTotalSupply(uint8 targetAcc) public {\n uint256 totalSupplyBefore = ousd.totalSupply();\n optOut(targetAcc);\n uint256 totalSupplyAfter = ousd.totalSupply();\n\n assert(totalSupplyAfter == totalSupplyBefore);\n }\n\n /**\n * @notice Account balance should remain the same when a smart contract auto converts\n * @param targetAcc Account to auto convert\n */\n function testAutoConvertBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n // slither-disable-next-line unused-return\n ousd._isNonRebasingAccountEchidna(target);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice The `balanceOf` function should never revert\n * @param targetAcc Account to check balance of\n */\n function testBalanceOfShouldNotRevert(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n // slither-disable-next-line unused-return\n try ousd.balanceOf(target) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestApproval.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaTestMintBurn.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin for testing approval related functions\n * @author Rappie\n */\ncontract EchidnaTestApproval is EchidnaTestMintBurn {\n /**\n * @notice Performing `transferFrom` with an amount inside the allowance should not revert\n * @param authorizedAcc The account that is authorized to transfer\n * @param fromAcc The account that is transferring\n * @param toAcc The account that is receiving\n * @param amount The amount to transfer\n */\n function testTransferFromShouldNotRevert(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount <= ousd.balanceOf(from));\n require(amount <= ousd.allowance(from, authorized));\n\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n try ousd.transferFrom(from, to, amount) {\n // pass\n } catch {\n assert(false);\n }\n }\n\n /**\n * @notice Performing `transferFrom` with an amount outside the allowance should revert\n * @param authorizedAcc The account that is authorized to transfer\n * @param fromAcc The account that is transferring\n * @param toAcc The account that is receiving\n * @param amount The amount to transfer\n */\n function testTransferFromShouldRevert(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount > 0);\n require(\n !(amount <= ousd.balanceOf(from) &&\n amount <= ousd.allowance(from, authorized))\n );\n\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n try ousd.transferFrom(from, to, amount) {\n assert(false);\n } catch {\n // pass\n }\n }\n\n /**\n * @notice Approving an amount should update the allowance and overwrite any previous allowance\n * @param ownerAcc The account that is approving\n * @param spenderAcc The account that is being approved\n * @param amount The amount to approve\n */\n function testApprove(\n uint8 ownerAcc,\n uint8 spenderAcc,\n uint256 amount\n ) public {\n address owner = getAccount(ownerAcc);\n address spender = getAccount(spenderAcc);\n\n approve(ownerAcc, spenderAcc, amount);\n uint256 allowanceAfter1 = ousd.allowance(owner, spender);\n\n assert(allowanceAfter1 == amount);\n\n approve(ownerAcc, spenderAcc, amount / 2);\n uint256 allowanceAfter2 = ousd.allowance(owner, spender);\n\n assert(allowanceAfter2 == amount / 2);\n }\n}\n" + }, + "contracts/echidna/EchidnaTestMintBurn.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestAccounting.sol\";\n\n/**\n * @title Mixin for testing Mint and Burn functions\n * @author Rappie\n */\ncontract EchidnaTestMintBurn is EchidnaTestAccounting {\n /**\n * @notice Minting 0 tokens should not affect account balance\n * @param targetAcc Account to mint to\n */\n function testMintZeroBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n mint(targetAcc, 0);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Burning 0 tokens should not affect account balance\n * @param targetAcc Account to burn from\n */\n function testBurnZeroBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n burn(targetAcc, 0);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Minting tokens must increase the account balance by at least amount\n * @param targetAcc Account to mint to\n * @param amount Amount to mint\n * @custom:error testMintBalance(uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * testMintBalance(0,1)\n * Event sequence:\n * Debug(«balanceBefore», 0)\n * Debug(«balanceAfter», 0)\n */\n function testMintBalance(uint8 targetAcc, uint256 amount)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n uint256 amountMinted = mint(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n Debugger.log(\"amountMinted\", amountMinted);\n Debugger.log(\"balanceBefore\", balanceBefore);\n Debugger.log(\"balanceAfter\", balanceAfter);\n\n assert(balanceAfter >= balanceBefore + amountMinted);\n }\n\n /**\n * @notice Burning tokens must decrease the account balance by at least amount\n * @param targetAcc Account to burn from\n * @param amount Amount to burn\n * @custom:error testBurnBalance(uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * mint(0,3)\n * testBurnBalance(0,1)\n * Event sequence:\n * Debug(«balanceBefore», 2)\n * Debug(«balanceAfter», 2)\n */\n function testBurnBalance(uint8 targetAcc, uint256 amount)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n burn(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n Debugger.log(\"balanceBefore\", balanceBefore);\n Debugger.log(\"balanceAfter\", balanceAfter);\n\n assert(balanceAfter <= balanceBefore - amount);\n }\n\n /**\n * @notice Minting tokens should not increase the account balance by less than rounding error above amount\n * @param targetAcc Account to mint to\n * @param amount Amount to mint\n */\n function testMintBalanceRounding(uint8 targetAcc, uint256 amount) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n uint256 amountMinted = mint(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n int256 delta = int256(balanceAfter) - int256(balanceBefore);\n\n // delta == amount, if no error\n // delta < amount, if too little is minted\n // delta > amount, if too much is minted\n int256 error = int256(amountMinted) - delta;\n\n assert(error >= 0);\n assert(error <= int256(MINT_ROUNDING_ERROR));\n }\n\n /**\n * @notice A burn of an account balance must result in a zero balance\n * @param targetAcc Account to burn from\n */\n function testBurnAllBalanceToZero(uint8 targetAcc) public hasKnownIssue {\n address target = getAccount(targetAcc);\n\n burn(targetAcc, ousd.balanceOf(target));\n assert(ousd.balanceOf(target) == 0);\n }\n\n /**\n * @notice You should always be able to burn an account's balance\n * @param targetAcc Account to burn from\n */\n function testBurnAllBalanceShouldNotRevert(uint8 targetAcc)\n public\n hasKnownIssue\n {\n address target = getAccount(targetAcc);\n uint256 balance = ousd.balanceOf(target);\n\n hevm.prank(ADDRESS_VAULT);\n try ousd.burn(target, balance) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestSupply.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestTransfer.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n/**\n * @title Mixin for testing supply related functions\n * @author Rappie\n */\ncontract EchidnaTestSupply is EchidnaTestTransfer {\n using StableMath for uint256;\n\n uint256 prevRebasingCreditsPerToken = type(uint256).max;\n\n /**\n * @notice After a `changeSupply`, the total supply should exactly\n * match the target total supply. (This is needed to ensure successive\n * rebases are correct).\n * @param supply New total supply\n * @custom:error testChangeSupply(uint256): failed!💥\n * Call sequence:\n * testChangeSupply(1044505275072865171609)\n * Event sequence:\n * TotalSupplyUpdatedHighres(1044505275072865171610, 1000000000000000000000000, 957391048054055578595)\n */\n function testChangeSupply(uint256 supply)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n hevm.prank(ADDRESS_VAULT);\n ousd.changeSupply(supply);\n\n assert(ousd.totalSupply() == supply);\n }\n\n /**\n * @notice The total supply must not be less than the sum of account balances.\n * (The difference will go into future rebases)\n * @custom:error testTotalSupplyLessThanTotalBalance(): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(1)\n * optOut(64)\n * transfer(0,64,1)\n * testTotalSupplyLessThanTotalBalance()\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000001000001)\n * Debug(«totalBalance», 1000000000000000001000002)\n */\n function testTotalSupplyLessThanTotalBalance()\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n uint256 totalSupply = ousd.totalSupply();\n uint256 totalBalance = getTotalBalance();\n\n Debugger.log(\"totalSupply\", totalSupply);\n Debugger.log(\"totalBalance\", totalBalance);\n\n assert(totalSupply >= totalBalance);\n }\n\n /**\n * @notice Non-rebasing supply should not be larger than total supply\n * @custom:error testNonRebasingSupplyVsTotalSupply(): failed!💥\n * Call sequence:\n * mint(0,2)\n * changeSupply(3)\n * burn(0,1)\n * optOut(0)\n * testNonRebasingSupplyVsTotalSupply()\n */\n function testNonRebasingSupplyVsTotalSupply() public hasKnownIssue {\n uint256 nonRebasingSupply = ousd.nonRebasingSupply();\n uint256 totalSupply = ousd.totalSupply();\n\n assert(nonRebasingSupply <= totalSupply);\n }\n\n /**\n * @notice Global `rebasingCreditsPerToken` should never increase\n * @custom:error testRebasingCreditsPerTokenNotIncreased(): failed!💥\n * Call sequence:\n * testRebasingCreditsPerTokenNotIncreased()\n * changeSupply(1)\n * testRebasingCreditsPerTokenNotIncreased()\n */\n function testRebasingCreditsPerTokenNotIncreased() public hasKnownIssue {\n uint256 curRebasingCreditsPerToken = ousd\n .rebasingCreditsPerTokenHighres();\n\n Debugger.log(\n \"prevRebasingCreditsPerToken\",\n prevRebasingCreditsPerToken\n );\n Debugger.log(\"curRebasingCreditsPerToken\", curRebasingCreditsPerToken);\n\n assert(curRebasingCreditsPerToken <= prevRebasingCreditsPerToken);\n\n prevRebasingCreditsPerToken = curRebasingCreditsPerToken;\n }\n\n /**\n * @notice The rebasing credits per token ratio must greater than zero\n */\n function testRebasingCreditsPerTokenAboveZero() public {\n assert(ousd.rebasingCreditsPerTokenHighres() > 0);\n }\n\n /**\n * @notice The sum of all non-rebasing balances should not be larger than\n * non-rebasing supply\n * @custom:error testTotalNonRebasingSupplyLessThanTotalBalance(): failed!💥\n * Call sequence\n * mint(0,2)\n * changeSupply(1)\n * optOut(0)\n * burn(0,1)\n * testTotalNonRebasingSupplyLessThanTotalBalance()\n * Event sequence:\n * Debug(«totalNonRebasingSupply», 500000000000000000000001)\n * Debug(«totalNonRebasingBalance», 500000000000000000000002)\n */\n function testTotalNonRebasingSupplyLessThanTotalBalance()\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n uint256 totalNonRebasingSupply = ousd.nonRebasingSupply();\n uint256 totalNonRebasingBalance = getTotalNonRebasingBalance();\n\n Debugger.log(\"totalNonRebasingSupply\", totalNonRebasingSupply);\n Debugger.log(\"totalNonRebasingBalance\", totalNonRebasingBalance);\n\n assert(totalNonRebasingSupply >= totalNonRebasingBalance);\n }\n\n /**\n * @notice An accounts credits / credits per token should not be larger it's balance\n * @param targetAcc The account to check\n */\n function testCreditsPerTokenVsBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n (uint256 credits, uint256 creditsPerToken, ) = ousd\n .creditsBalanceOfHighres(target);\n uint256 expectedBalance = credits.divPrecisely(creditsPerToken);\n\n uint256 balance = ousd.balanceOf(target);\n\n Debugger.log(\"credits\", credits);\n Debugger.log(\"creditsPerToken\", creditsPerToken);\n Debugger.log(\"expectedBalance\", expectedBalance);\n Debugger.log(\"balance\", balance);\n\n assert(expectedBalance == balance);\n }\n}\n" + }, + "contracts/echidna/EchidnaTestTransfer.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin for testing transfer related functions\n * @author Rappie\n */\ncontract EchidnaTestTransfer is EchidnaDebug {\n /**\n * @notice The receiving account's balance after a transfer must not increase by\n * less than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferBalanceReceivedLess(uint8,uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * mint(64,2)\n * testTransferBalanceReceivedLess(64,0,1)\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000000500002)\n * Debug(«toBalBefore», 0)\n * Debug(«toBalAfter», 0)\n */\n function testTransferBalanceReceivedLess(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue hasKnownIssueWithinLimits {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n\n assert(toBalAfter >= toBalBefore + amount);\n }\n\n /**\n * @notice The receiving account's balance after a transfer must not\n * increase by more than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceReceivedMore(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n\n assert(toBalAfter <= toBalBefore + amount);\n }\n\n /**\n * @notice The sending account's balance after a transfer must not\n * decrease by less than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferBalanceSentLess(uint8,uint8,uint256): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(1)\n * testTransferBalanceSentLess(0,64,1)\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000000500001)\n * Debug(«fromBalBefore», 1)\n * Debug(«fromBalAfter», 1)\n */\n function testTransferBalanceSentLess(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue hasKnownIssueWithinLimits {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n\n assert(fromBalAfter <= fromBalBefore - amount);\n }\n\n /**\n * @notice The sending account's balance after a transfer must not\n * decrease by more than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceSentMore(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n\n assert(fromBalAfter >= fromBalBefore - amount);\n }\n\n /**\n * @notice The receiving account's balance after a transfer must not\n * increase by less than the amount transferred (minus rounding error)\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceReceivedLessRounding(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n int256 toDelta = int256(toBalAfter) - int256(toBalBefore);\n\n // delta == amount, if no error\n // delta < amount, if too little is sent\n // delta > amount, if too much is sent\n int256 error = int256(amount) - toDelta;\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n Debugger.log(\"toDelta\", toDelta);\n Debugger.log(\"error\", error);\n\n assert(error >= 0);\n assert(error <= int256(TRANSFER_ROUNDING_ERROR));\n }\n\n /**\n * @notice The sending account's balance after a transfer must\n * not decrease by less than the amount transferred (minus rounding error)\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceSentLessRounding(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n int256 fromDelta = int256(fromBalAfter) - int256(fromBalBefore);\n\n // delta == -amount, if no error\n // delta < -amount, if too much is sent\n // delta > -amount, if too little is sent\n int256 error = int256(amount) + fromDelta;\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n Debugger.log(\"fromDelta\", fromDelta);\n Debugger.log(\"error\", error);\n\n assert(error >= 0);\n assert(error <= int256(TRANSFER_ROUNDING_ERROR));\n }\n\n /**\n * @notice An account should always be able to successfully transfer\n * an amount within its balance.\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferWithinBalanceDoesNotRevert(uint8,uint8,uint8): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(3)\n * optOut(0)\n * testTransferWithinBalanceDoesNotRevert(0,128,2)\n * optIn(0)\n * testTransferWithinBalanceDoesNotRevert(128,0,1)\n * Event sequence:\n * error Revert Panic(17): SafeMath over-/under-flows\n */\n function testTransferWithinBalanceDoesNotRevert(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount > 0);\n amount = amount % ousd.balanceOf(from);\n\n Debugger.log(\"Total supply\", ousd.totalSupply());\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(to, amount) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n\n /**\n * @notice An account should never be able to successfully transfer\n * an amount greater than their balance.\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferExceedingBalanceReverts(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n amount = ousd.balanceOf(from) + 1 + amount;\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(to, amount) {\n assert(false);\n } catch {\n assert(true);\n }\n }\n\n /**\n * @notice A transfer to the same account should not change that account's balance\n * @param targetAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferSelf(uint8 targetAcc, uint256 amount) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n transfer(targetAcc, targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceBefore == balanceAfter);\n }\n\n /**\n * @notice Transfers to the zero account revert\n * @param fromAcc Account to transfer from\n * @param amount Amount to transfer\n */\n function testTransferToZeroAddress(uint8 fromAcc, uint256 amount) public {\n address from = getAccount(fromAcc);\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(address(0), amount) {\n assert(false);\n } catch {\n assert(true);\n }\n }\n}\n" + }, + "contracts/echidna/IHevm.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// https://github.com/ethereum/hevm/blob/main/doc/src/controlling-the-unit-testing-environment.md#cheat-codes\n\ninterface IHevm {\n function warp(uint256 x) external;\n\n function roll(uint256 x) external;\n\n function store(\n address c,\n bytes32 loc,\n bytes32 val\n ) external;\n\n function load(address c, bytes32 loc) external returns (bytes32 val);\n\n function sign(uint256 sk, bytes32 digest)\n external\n returns (\n uint8 v,\n bytes32 r,\n bytes32 s\n );\n\n function addr(uint256 sk) external returns (address addr);\n\n function ffi(string[] calldata) external returns (bytes memory);\n\n function prank(address sender) external;\n}\n" + }, + "contracts/echidna/OUSDEchidna.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../token/OUSD.sol\";\n\ncontract OUSDEchidna is OUSD {\n constructor() OUSD() {}\n\n function _isNonRebasingAccountEchidna(address _account)\n public\n returns (bool)\n {\n _autoMigrate(_account);\n return alternativeCreditsPerToken[_account] > 0;\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/governance/InitializableGovernable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD InitializableGovernable Contract\n * @author Origin Protocol Inc\n */\nimport { Initializable } from \"../utils/Initializable.sol\";\n\nimport { Governable } from \"./Governable.sol\";\n\ncontract InitializableGovernable is Governable, Initializable {\n function _initialize(address _newGovernor) internal {\n _changeGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/governance/Strategizable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"./Governable.sol\";\n\ncontract Strategizable is Governable {\n event StrategistUpdated(address _address);\n\n // Address of strategist\n address public strategistAddr;\n\n // For future use\n uint256[50] private __gap;\n\n /**\n * @dev Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @dev Set address of Strategist\n * @param _address Address of Strategist\n */\n function setStrategistAddr(address _address) external onlyGovernor {\n _setStrategistAddr(_address);\n }\n\n /**\n * @dev Set address of Strategist\n * @param _address Address of Strategist\n */\n function _setStrategistAddr(address _address) internal {\n strategistAddr = _address;\n emit StrategistUpdated(_address);\n }\n}\n" + }, + "contracts/harvest/AbstractHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IUniswapV2Router } from \"../interfaces/uniswap/IUniswapV2Router02.sol\";\nimport { IUniswapV3Router } from \"../interfaces/uniswap/IUniswapV3Router.sol\";\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { ICurvePool } from \"../strategies/ICurvePool.sol\";\nimport \"../utils/Helpers.sol\";\n\nabstract contract AbstractHarvester is Governable {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using StableMath for uint256;\n\n enum SwapPlatform {\n UniswapV2Compatible,\n UniswapV3,\n Balancer,\n Curve\n }\n\n event SupportedStrategyUpdate(address strategyAddress, bool isSupported);\n event RewardTokenConfigUpdated(\n address tokenAddress,\n uint16 allowedSlippageBps,\n uint16 harvestRewardBps,\n SwapPlatform swapPlatform,\n address swapPlatformAddr,\n bytes swapData,\n uint256 liquidationLimit,\n bool doSwapRewardToken\n );\n event RewardTokenSwapped(\n address indexed rewardToken,\n address indexed swappedInto,\n SwapPlatform swapPlatform,\n uint256 amountIn,\n uint256 amountOut\n );\n event RewardProceedsTransferred(\n address indexed token,\n address farmer,\n uint256 protcolYield,\n uint256 farmerFee\n );\n event RewardProceedsAddressChanged(address newProceedsAddress);\n\n error EmptyAddress();\n error InvalidSlippageBps();\n error InvalidHarvestRewardBps();\n\n error InvalidSwapPlatform(SwapPlatform swapPlatform);\n\n error InvalidUniswapV2PathLength();\n error InvalidTokenInSwapPath(address token);\n error EmptyBalancerPoolId();\n error InvalidCurvePoolAssetIndex(address token);\n\n error UnsupportedStrategy(address strategyAddress);\n\n error SlippageError(uint256 actualBalance, uint256 minExpected);\n error BalanceMismatchAfterSwap(uint256 actualBalance, uint256 minExpected);\n\n // Configuration properties for harvesting logic of reward tokens\n struct RewardTokenConfig {\n // Max allowed slippage when swapping reward token for a stablecoin denominated in basis points.\n uint16 allowedSlippageBps;\n // Reward when calling a harvest function denominated in basis points.\n uint16 harvestRewardBps;\n // Address of compatible exchange protocol (Uniswap V2/V3, SushiSwap, Balancer and Curve).\n address swapPlatformAddr;\n /* When true the reward token is being swapped. In a need of (temporarily) disabling the swapping of\n * a reward token this needs to be set to false.\n */\n bool doSwapRewardToken;\n // Platform to use for Swapping\n SwapPlatform swapPlatform;\n /* How much token can be sold per one harvest call. If the balance of rewards tokens\n * exceeds that limit multiple harvest calls are required to harvest all of the tokens.\n * Set it to MAX_INT to effectively disable the limit.\n */\n uint256 liquidationLimit;\n }\n\n mapping(address => RewardTokenConfig) public rewardTokenConfigs;\n mapping(address => bool) public supportedStrategies;\n\n address public immutable vaultAddress;\n\n /**\n * Address receiving rewards proceeds. Initially the Vault contract later will possibly\n * be replaced by another contract that eases out rewards distribution.\n **/\n address public rewardProceedsAddress;\n\n /**\n * All tokens are swapped to this token before it gets transferred\n * to the `rewardProceedsAddress`. USDT for OUSD and WETH for OETH.\n **/\n address public immutable baseTokenAddress;\n // Cached decimals for `baseTokenAddress`\n uint256 public immutable baseTokenDecimals;\n\n // Uniswap V2 path for reward tokens using Uniswap V2 Router\n mapping(address => address[]) public uniswapV2Path;\n // Uniswap V3 path for reward tokens using Uniswap V3 Router\n mapping(address => bytes) public uniswapV3Path;\n // Pool ID to use for reward tokens on Balancer\n mapping(address => bytes32) public balancerPoolId;\n\n struct CurvePoolIndices {\n // Casted into uint128 and stored in a struct to save gas\n uint128 rewardTokenIndex;\n uint128 baseTokenIndex;\n }\n // Packed indices of assets on the Curve pool\n mapping(address => CurvePoolIndices) public curvePoolIndices;\n\n constructor(address _vaultAddress, address _baseTokenAddress) {\n require(_vaultAddress != address(0));\n require(_baseTokenAddress != address(0));\n\n vaultAddress = _vaultAddress;\n baseTokenAddress = _baseTokenAddress;\n\n // Cache decimals as well\n baseTokenDecimals = Helpers.getDecimals(_baseTokenAddress);\n }\n\n /***************************************\n Configuration\n ****************************************/\n\n /**\n * Set the Address receiving rewards proceeds.\n * @param _rewardProceedsAddress Address of the reward token\n */\n function setRewardProceedsAddress(address _rewardProceedsAddress)\n external\n onlyGovernor\n {\n if (_rewardProceedsAddress == address(0)) {\n revert EmptyAddress();\n }\n\n rewardProceedsAddress = _rewardProceedsAddress;\n emit RewardProceedsAddressChanged(_rewardProceedsAddress);\n }\n\n /**\n * @dev Add/update a reward token configuration that holds harvesting config variables\n * @param _tokenAddress Address of the reward token\n * @param tokenConfig.allowedSlippageBps uint16 maximum allowed slippage denominated in basis points.\n * Example: 300 == 3% slippage\n * @param tokenConfig.harvestRewardBps uint16 amount of reward tokens the caller of the function is rewarded.\n * Example: 100 == 1%\n * @param tokenConfig.swapPlatformAddr Address Address of a UniswapV2 compatible contract to perform\n * the exchange from reward tokens to stablecoin (currently hard-coded to USDT)\n * @param tokenConfig.liquidationLimit uint256 Maximum amount of token to be sold per one swap function call.\n * When value is 0 there is no limit.\n * @param tokenConfig.doSwapRewardToken bool Disables swapping of the token when set to true,\n * does not cause it to revert though.\n * @param tokenConfig.swapPlatform SwapPlatform to use for Swapping\n * @param swapData Additional data required for swapping\n */\n function setRewardTokenConfig(\n address _tokenAddress,\n RewardTokenConfig calldata tokenConfig,\n bytes calldata swapData\n ) external onlyGovernor {\n if (tokenConfig.allowedSlippageBps > 1000) {\n revert InvalidSlippageBps();\n }\n\n if (tokenConfig.harvestRewardBps > 1000) {\n revert InvalidHarvestRewardBps();\n }\n\n address newRouterAddress = tokenConfig.swapPlatformAddr;\n if (newRouterAddress == address(0)) {\n // Swap router address should be non zero address\n revert EmptyAddress();\n }\n\n address oldRouterAddress = rewardTokenConfigs[_tokenAddress]\n .swapPlatformAddr;\n rewardTokenConfigs[_tokenAddress] = tokenConfig;\n\n // Revert if feed does not exist\n // slither-disable-next-line unused-return\n IOracle(IVault(vaultAddress).priceProvider()).price(_tokenAddress);\n\n IERC20 token = IERC20(_tokenAddress);\n // if changing token swap provider cancel existing allowance\n if (\n /* oldRouterAddress == address(0) when there is no pre-existing\n * configuration for said rewards token\n */\n oldRouterAddress != address(0) &&\n oldRouterAddress != newRouterAddress\n ) {\n token.safeApprove(oldRouterAddress, 0);\n }\n\n // Give SwapRouter infinite approval when needed\n if (oldRouterAddress != newRouterAddress) {\n token.safeApprove(newRouterAddress, 0);\n token.safeApprove(newRouterAddress, type(uint256).max);\n }\n\n SwapPlatform _platform = tokenConfig.swapPlatform;\n if (_platform == SwapPlatform.UniswapV2Compatible) {\n uniswapV2Path[_tokenAddress] = _decodeUniswapV2Path(\n swapData,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.UniswapV3) {\n uniswapV3Path[_tokenAddress] = _decodeUniswapV3Path(\n swapData,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.Balancer) {\n balancerPoolId[_tokenAddress] = _decodeBalancerPoolId(\n swapData,\n newRouterAddress,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.Curve) {\n curvePoolIndices[_tokenAddress] = _decodeCurvePoolIndices(\n swapData,\n newRouterAddress,\n _tokenAddress\n );\n } else {\n // Note: This code is unreachable since Solidity reverts when\n // the value is outside the range of defined values of the enum\n // (even if it's under the max length of the base type)\n revert InvalidSwapPlatform(_platform);\n }\n\n emit RewardTokenConfigUpdated(\n _tokenAddress,\n tokenConfig.allowedSlippageBps,\n tokenConfig.harvestRewardBps,\n _platform,\n newRouterAddress,\n swapData,\n tokenConfig.liquidationLimit,\n tokenConfig.doSwapRewardToken\n );\n }\n\n /**\n * @dev Decodes the data passed into Uniswap V2 path and validates\n * it to make sure the path is for `token` to `baseToken`\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param token The address of the reward token\n * @return path The validated Uniswap V2 path\n */\n function _decodeUniswapV2Path(bytes calldata data, address token)\n internal\n view\n returns (address[] memory path)\n {\n (path) = abi.decode(data, (address[]));\n uint256 len = path.length;\n\n if (len < 2) {\n // Path should have at least two tokens\n revert InvalidUniswapV2PathLength();\n }\n\n // Do some validation\n if (path[0] != token) {\n revert InvalidTokenInSwapPath(path[0]);\n }\n\n if (path[len - 1] != baseTokenAddress) {\n revert InvalidTokenInSwapPath(path[len - 1]);\n }\n }\n\n /**\n * @dev Decodes the data passed into Uniswap V3 path and validates\n * it to make sure the path is for `token` to `baseToken`\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param token The address of the reward token\n * @return path The validated Uniswap V3 path\n */\n function _decodeUniswapV3Path(bytes calldata data, address token)\n internal\n view\n returns (bytes calldata path)\n {\n path = data;\n\n address decodedAddress = address(uint160(bytes20(data[0:20])));\n\n if (decodedAddress != token) {\n // Invalid Reward Token in swap path\n revert InvalidTokenInSwapPath(decodedAddress);\n }\n\n decodedAddress = address(uint160(bytes20(data[path.length - 20:])));\n if (decodedAddress != baseTokenAddress) {\n // Invalid Base Token in swap path\n revert InvalidTokenInSwapPath(decodedAddress);\n }\n }\n\n /**\n * @dev Decodes the data passed to Balancer Pool ID\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @return poolId The pool ID\n */\n function _decodeBalancerPoolId(\n bytes calldata data,\n address balancerVault,\n address token\n ) internal view returns (bytes32 poolId) {\n (poolId) = abi.decode(data, (bytes32));\n\n if (poolId == bytes32(0)) {\n revert EmptyBalancerPoolId();\n }\n\n IBalancerVault bVault = IBalancerVault(balancerVault);\n\n // Note: this reverts if token is not a pool asset\n // slither-disable-next-line unused-return\n bVault.getPoolTokenInfo(poolId, token);\n\n // slither-disable-next-line unused-return\n bVault.getPoolTokenInfo(poolId, baseTokenAddress);\n }\n\n /**\n * @dev Decodes the data passed to get the pool indices and\n * checks it against the Curve Pool to make sure it's\n * not misconfigured. The indices are packed into a single\n * uint256 for gas savings\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param poolAddress Curve pool address\n * @param token The address of the reward token\n * @return indices Packed pool asset indices\n */\n function _decodeCurvePoolIndices(\n bytes calldata data,\n address poolAddress,\n address token\n ) internal view returns (CurvePoolIndices memory indices) {\n indices = abi.decode(data, (CurvePoolIndices));\n\n ICurvePool pool = ICurvePool(poolAddress);\n if (token != pool.coins(indices.rewardTokenIndex)) {\n revert InvalidCurvePoolAssetIndex(token);\n }\n if (baseTokenAddress != pool.coins(indices.baseTokenIndex)) {\n revert InvalidCurvePoolAssetIndex(baseTokenAddress);\n }\n }\n\n /**\n * @dev Flags a strategy as supported or not supported one\n * @param _strategyAddress Address of the strategy\n * @param _isSupported Bool marking strategy as supported or not supported\n */\n function setSupportedStrategy(address _strategyAddress, bool _isSupported)\n external\n onlyGovernor\n {\n supportedStrategies[_strategyAddress] = _isSupported;\n emit SupportedStrategyUpdate(_strategyAddress, _isSupported);\n }\n\n /***************************************\n Rewards\n ****************************************/\n\n /**\n * @dev Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform. Can be called by anyone.\n * Rewards incentivizing the caller are sent to the caller of this function.\n * @param _strategyAddr Address of the strategy to collect rewards from\n */\n function harvestAndSwap(address _strategyAddr) external nonReentrant {\n // Remember _harvest function checks for the validity of _strategyAddr\n _harvestAndSwap(_strategyAddr, msg.sender);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform. Can be called by anyone\n * @param _strategyAddr Address of the strategy to collect rewards from\n * @param _rewardTo Address where to send a share of harvest rewards to as an incentive\n * for executing this function\n */\n function harvestAndSwap(address _strategyAddr, address _rewardTo)\n external\n nonReentrant\n {\n // Remember _harvest function checks for the validity of _strategyAddr\n _harvestAndSwap(_strategyAddr, _rewardTo);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform\n * @param _strategyAddr Address of the strategy to collect rewards from\n * @param _rewardTo Address where to send a share of harvest rewards to as an incentive\n * for executing this function\n */\n function _harvestAndSwap(address _strategyAddr, address _rewardTo)\n internal\n {\n _harvest(_strategyAddr);\n IStrategy strategy = IStrategy(_strategyAddr);\n address[] memory rewardTokens = strategy.getRewardTokenAddresses();\n IOracle priceProvider = IOracle(IVault(vaultAddress).priceProvider());\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; ++i) {\n _swap(rewardTokens[i], _rewardTo, priceProvider);\n }\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform\n * @param _strategyAddr Address of the strategy to collect rewards from.\n */\n function _harvest(address _strategyAddr) internal virtual {\n if (!supportedStrategies[_strategyAddr]) {\n revert UnsupportedStrategy(_strategyAddr);\n }\n\n IStrategy strategy = IStrategy(_strategyAddr);\n strategy.collectRewardTokens();\n }\n\n /**\n * @dev Swap a reward token for the base token on the configured\n * swap platform. The token must have a registered price feed\n * with the price provider\n * @param _swapToken Address of the token to swap\n * @param _rewardTo Address where to send the share of harvest rewards to\n * @param _priceProvider Oracle to get prices of the swap token\n */\n function _swap(\n address _swapToken,\n address _rewardTo,\n IOracle _priceProvider\n ) internal virtual {\n uint256 balance = IERC20(_swapToken).balanceOf(address(this));\n\n // No need to swap if the reward token is the base token. eg USDT or WETH.\n // There is also no limit on the transfer. Everything in the harvester will be transferred\n // to the Dripper regardless of the liquidationLimit config.\n if (_swapToken == baseTokenAddress) {\n IERC20(_swapToken).safeTransfer(rewardProceedsAddress, balance);\n // currently not paying the farmer any rewards as there is no swap\n emit RewardProceedsTransferred(\n baseTokenAddress,\n address(0),\n balance,\n 0\n );\n return;\n }\n\n RewardTokenConfig memory tokenConfig = rewardTokenConfigs[_swapToken];\n\n /* This will trigger a return when reward token configuration has not yet been set\n * or we have temporarily disabled swapping of specific reward token via setting\n * doSwapRewardToken to false.\n */\n if (!tokenConfig.doSwapRewardToken) {\n return;\n }\n\n if (balance == 0) {\n return;\n }\n\n if (tokenConfig.liquidationLimit > 0) {\n balance = Math.min(balance, tokenConfig.liquidationLimit);\n }\n\n // This'll revert if there is no price feed\n uint256 oraclePrice = _priceProvider.price(_swapToken);\n\n // Oracle price is 1e18\n uint256 minExpected = (balance *\n (1e4 - tokenConfig.allowedSlippageBps) * // max allowed slippage\n oraclePrice).scaleBy(\n baseTokenDecimals,\n Helpers.getDecimals(_swapToken)\n ) /\n 1e4 / // fix the max slippage decimal position\n 1e18; // and oracle price decimals position\n\n // Do the swap\n uint256 amountReceived = _doSwap(\n tokenConfig.swapPlatform,\n tokenConfig.swapPlatformAddr,\n _swapToken,\n balance,\n minExpected\n );\n\n if (amountReceived < minExpected) {\n revert SlippageError(amountReceived, minExpected);\n }\n\n emit RewardTokenSwapped(\n _swapToken,\n baseTokenAddress,\n tokenConfig.swapPlatform,\n balance,\n amountReceived\n );\n\n IERC20 baseToken = IERC20(baseTokenAddress);\n uint256 baseTokenBalance = baseToken.balanceOf(address(this));\n if (baseTokenBalance < amountReceived) {\n // Note: It's possible to bypass this check by transferring `baseToken`\n // directly to Harvester before calling the `harvestAndSwap`. However,\n // there's no incentive for an attacker to do that. Doing a balance diff\n // will increase the gas cost significantly\n revert BalanceMismatchAfterSwap(baseTokenBalance, amountReceived);\n }\n\n // Farmer only gets fee from the base amount they helped farm,\n // They do not get anything from anything that already was there\n // on the Harvester\n uint256 farmerFee = amountReceived.mulTruncateScale(\n tokenConfig.harvestRewardBps,\n 1e4\n );\n uint256 protocolYield = baseTokenBalance - farmerFee;\n\n baseToken.safeTransfer(rewardProceedsAddress, protocolYield);\n baseToken.safeTransfer(_rewardTo, farmerFee);\n emit RewardProceedsTransferred(\n baseTokenAddress,\n _rewardTo,\n protocolYield,\n farmerFee\n );\n }\n\n function _doSwap(\n SwapPlatform swapPlatform,\n address routerAddress,\n address rewardTokenAddress,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n if (swapPlatform == SwapPlatform.UniswapV2Compatible) {\n return\n _swapWithUniswapV2(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.UniswapV3) {\n return\n _swapWithUniswapV3(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.Balancer) {\n return\n _swapWithBalancer(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.Curve) {\n return\n _swapWithCurve(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else {\n // Should never be invoked since we catch invalid values\n // in the `setRewardTokenConfig` function before it's set\n revert InvalidSwapPlatform(swapPlatform);\n }\n }\n\n /**\n * @dev Swaps the token to `baseToken` with Uniswap V2\n *\n * @param routerAddress Uniswap V2 Router address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithUniswapV2(\n address routerAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n address[] memory path = uniswapV2Path[swapToken];\n\n uint256[] memory amounts = IUniswapV2Router(routerAddress)\n .swapExactTokensForTokens(\n amountIn,\n minAmountOut,\n path,\n address(this),\n block.timestamp\n );\n\n amountOut = amounts[amounts.length - 1];\n }\n\n /**\n * @dev Swaps the token to `baseToken` with Uniswap V3\n *\n * @param routerAddress Uniswap V3 Router address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithUniswapV3(\n address routerAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n bytes memory path = uniswapV3Path[swapToken];\n\n IUniswapV3Router.ExactInputParams memory params = IUniswapV3Router\n .ExactInputParams({\n path: path,\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: amountIn,\n amountOutMinimum: minAmountOut\n });\n amountOut = IUniswapV3Router(routerAddress).exactInput(params);\n }\n\n /**\n * @dev Swaps the token to `baseToken` on Balancer\n *\n * @param balancerVaultAddress BalancerVaultAddress\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithBalancer(\n address balancerVaultAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n bytes32 poolId = balancerPoolId[swapToken];\n\n IBalancerVault.SingleSwap memory singleSwap = IBalancerVault\n .SingleSwap({\n poolId: poolId,\n kind: IBalancerVault.SwapKind.GIVEN_IN,\n assetIn: swapToken,\n assetOut: baseTokenAddress,\n amount: amountIn,\n userData: hex\"\"\n });\n\n IBalancerVault.FundManagement memory fundMgmt = IBalancerVault\n .FundManagement({\n sender: address(this),\n fromInternalBalance: false,\n recipient: payable(address(this)),\n toInternalBalance: false\n });\n\n amountOut = IBalancerVault(balancerVaultAddress).swap(\n singleSwap,\n fundMgmt,\n minAmountOut,\n block.timestamp\n );\n }\n\n /**\n * @dev Swaps the token to `baseToken` on Curve\n *\n * @param poolAddress Curve Pool Address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithCurve(\n address poolAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n CurvePoolIndices memory indices = curvePoolIndices[swapToken];\n\n // Note: Not all CurvePools return the `amountOut`, make sure\n // to use only pool that do. Otherwise the swap would revert\n // always\n amountOut = ICurvePool(poolAddress).exchange(\n uint256(indices.rewardTokenIndex),\n uint256(indices.baseTokenIndex),\n amountIn,\n minAmountOut\n );\n }\n}\n" + }, + "contracts/harvest/Dripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\n/**\n * @title OUSD Dripper\n *\n * The dripper contract smooths out the yield from point-in-time yield events\n * and spreads the yield out over a configurable time period. This ensures a\n * continuous per block yield to makes users happy as their next rebase\n * amount is always moving up. Also, this makes historical day to day yields\n * smooth, rather than going from a near zero day, to a large APY day, then\n * back to a near zero day again.\n *\n *\n * Design notes\n * - USDT has a smaller resolution than the number of seconds\n * in a week, which can make per second payouts have a rounding error. However\n * the total effect is not large - cents per day, and this money is\n * not lost, just distributed in the future. While we could use a higher\n * decimal precision for the drip perSecond, we chose simpler code.\n * - By calculating the changing drip rates on collects only, harvests and yield\n * events don't have to call anything on this contract or pay any extra gas.\n * Collect() is already be paying for a single write, since it has to reset\n * the lastCollect time.\n * - By having a collectAndRebase method, and having our external systems call\n * that, the OUSD vault does not need any changes, not even to know the address\n * of the dripper.\n * - A rejected design was to retro-calculate the drip rate on each collect,\n * based on the balance at the time of the collect. While this would have\n * required less state, and would also have made the contract respond more quickly\n * to new income, it would break the predictability that is this contract's entire\n * purpose. If we did this, the amount of fundsAvailable() would make sharp increases\n * when funds were deposited.\n * - When the dripper recalculates the rate, it targets spending the balance over\n * the duration. This means that every time that collect is called, if no\n * new funds have been deposited the duration is being pushed back and the\n * rate decreases. This is expected, and ends up following a smoother but\n * longer curve the more collect() is called without incoming yield.\n *\n */\n\ncontract Dripper is Governable {\n using SafeERC20 for IERC20;\n\n struct Drip {\n uint64 lastCollect; // overflows 262 billion years after the sun dies\n uint192 perSecond; // drip rate per second\n }\n\n address immutable vault; // OUSD vault\n address immutable token; // token to drip out\n uint256 public dripDuration; // in seconds\n Drip public drip; // active drip parameters\n\n constructor(address _vault, address _token) {\n vault = _vault;\n token = _token;\n }\n\n /// @notice How much funds have dripped out already and are currently\n // available to be sent to the vault.\n /// @return The amount that would be sent if a collect was called\n function availableFunds() external view returns (uint256) {\n uint256 balance = IERC20(token).balanceOf(address(this));\n return _availableFunds(balance, drip);\n }\n\n /// @notice Collect all dripped funds and send to vault.\n /// Recalculate new drip rate.\n function collect() external {\n _collect();\n }\n\n /// @notice Collect all dripped funds, send to vault, recalculate new drip\n /// rate, and rebase OUSD.\n function collectAndRebase() external {\n _collect();\n IVault(vault).rebase();\n }\n\n /// @dev Change the drip duration. Governor only.\n /// @param _durationSeconds the number of seconds to drip out the entire\n /// balance over if no collects were called during that time.\n function setDripDuration(uint256 _durationSeconds)\n external\n virtual\n onlyGovernor\n {\n require(_durationSeconds > 0, \"duration must be non-zero\");\n dripDuration = _durationSeconds;\n _collect(); // duration change take immediate effect\n }\n\n /// @dev Transfer out ERC20 tokens held by the contract. Governor only.\n /// @param _asset ERC20 token address\n /// @param _amount amount to transfer\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /// @dev Calculate available funds by taking the lower of either the\n /// currently dripped out funds or the balance available.\n /// Uses passed in parameters to calculate with for gas savings.\n /// @param _balance current balance in contract\n /// @param _drip current drip parameters\n function _availableFunds(uint256 _balance, Drip memory _drip)\n internal\n view\n returns (uint256)\n {\n uint256 elapsed = block.timestamp - _drip.lastCollect;\n uint256 allowed = (elapsed * _drip.perSecond);\n return (allowed > _balance) ? _balance : allowed;\n }\n\n /// @dev Sends the currently dripped funds to be vault, and sets\n /// the new drip rate based on the new balance.\n function _collect() internal virtual {\n // Calculate send\n uint256 balance = IERC20(token).balanceOf(address(this));\n uint256 amountToSend = _availableFunds(balance, drip);\n uint256 remaining = balance - amountToSend;\n // Calculate new drip perSecond\n // Gas savings by setting entire struct at one time\n drip = Drip({\n perSecond: uint192(remaining / dripDuration),\n lastCollect: uint64(block.timestamp)\n });\n // Send funds\n IERC20(token).safeTransfer(vault, amountToSend);\n }\n\n /// @dev Transfer out all ERC20 held by the contract. Governor only.\n /// @param _asset ERC20 token address\n function transferAllToken(address _asset, address _receiver)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(\n _receiver,\n IERC20(_asset).balanceOf(address(this))\n );\n }\n}\n" + }, + "contracts/harvest/FixedRateDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Dripper } from \"./Dripper.sol\";\n\n/**\n * @title Fixed Rate Dripper\n *\n * Similar to the Dripper, Fixed Rate Dripper drips out yield per second.\n * However the Strategist decides the rate and it doesn't change after\n * a drip.\n *\n */\n\ncontract FixedRateDripper is Dripper {\n using SafeERC20 for IERC20;\n\n event DripRateUpdated(uint192 oldDripRate, uint192 newDripRate);\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vault).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n constructor(address _vault, address _token) Dripper(_vault, _token) {}\n\n /// @inheritdoc Dripper\n function setDripDuration(uint256) external virtual override {\n // Not used in FixedRateDripper\n revert(\"Drip duration disabled\");\n }\n\n /// @inheritdoc Dripper\n function _collect() internal virtual override {\n // Calculate amount to send\n uint256 balance = IERC20(token).balanceOf(address(this));\n uint256 amountToSend = _availableFunds(balance, drip);\n\n // Update timestamp\n drip.lastCollect = uint64(block.timestamp);\n\n // Send funds\n IERC20(token).safeTransfer(vault, amountToSend);\n }\n\n /**\n * @dev Sets the drip rate. Callable by Strategist or Governor.\n * Can be set to zero to stop dripper.\n * @param _perSecond Rate of WETH to drip per second\n */\n function setDripRate(uint192 _perSecond) external onlyGovernorOrStrategist {\n emit DripRateUpdated(_perSecond, drip.perSecond);\n\n /**\n * Note: It's important to call `_collect` before updating\n * the drip rate especially on a new proxy contract.\n * When `lastCollect` is not set/initialized, the elapsed\n * time would be calculated as `block.number` seconds,\n * resulting in a huge yield, if `collect` isn't called first.\n */\n // Collect at existing rate\n _collect();\n\n // Update rate\n drip.perSecond = _perSecond;\n }\n}\n" + }, + "contracts/harvest/Harvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractHarvester } from \"./AbstractHarvester.sol\";\n\ncontract Harvester is AbstractHarvester {\n constructor(address _vault, address _usdtAddress)\n AbstractHarvester(_vault, _usdtAddress)\n {}\n}\n" + }, + "contracts/harvest/OETHBaseHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { ISwapRouter } from \"../interfaces/aerodrome/ISwapRouter.sol\";\n\ncontract OETHBaseHarvester is Governable {\n using SafeERC20 for IERC20;\n\n IVault public immutable vault;\n IStrategy public immutable amoStrategy;\n IERC20 public immutable aero;\n IERC20 public immutable weth;\n ISwapRouter public immutable swapRouter;\n\n address public operatorAddr;\n\n // Similar sig to `AbstractHarvester.RewardTokenSwapped` for\n // future compatibility with monitoring\n event RewardTokenSwapped(\n address indexed rewardToken,\n address indexed swappedInto,\n uint8 swapPlatform,\n uint256 amountIn,\n uint256 amountOut\n );\n\n event OperatorChanged(address oldOperator, address newOperator);\n event YieldSent(address recipient, uint256 yield, uint256 fee);\n\n /**\n * @notice Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == vault.strategistAddr() || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @notice Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategistOrOperator() {\n require(\n msg.sender == operatorAddr ||\n msg.sender == vault.strategistAddr() ||\n isGovernor(),\n \"Caller is not the Operator or Strategist or Governor\"\n );\n _;\n }\n\n constructor(\n address _vault,\n address _amoStrategy,\n address _aero,\n address _weth,\n address _swapRouter\n ) {\n vault = IVault(_vault);\n amoStrategy = IStrategy(_amoStrategy);\n aero = IERC20(_aero);\n weth = IERC20(_weth);\n swapRouter = ISwapRouter(_swapRouter);\n }\n\n /**\n * @dev Changes the operator address which can call `harvest`\n * @param _operatorAddr New operator address\n */\n function setOperatorAddr(address _operatorAddr) external onlyGovernor {\n emit OperatorChanged(operatorAddr, _operatorAddr);\n operatorAddr = _operatorAddr;\n }\n\n /**\n * @notice Collects AERO from AMO strategy and\n * sends it to the Strategist multisig.\n * Anyone can call it.\n */\n function harvest() external onlyGovernorOrStrategistOrOperator {\n address strategistAddr = vault.strategistAddr();\n require(strategistAddr != address(0), \"Guardian address not set\");\n\n // Collect all AERO\n amoStrategy.collectRewardTokens();\n\n uint256 aeroBalance = aero.balanceOf(address(this));\n if (aeroBalance == 0) {\n // Do nothing if there's no AERO to transfer\n return;\n }\n\n // Transfer everything to Strategist\n aero.safeTransfer(strategistAddr, aeroBalance);\n }\n\n /**\n * @notice Harvests AERO from AMO strategy and then swaps some (or all)\n * of it into WETH to distribute yield and fee.\n * When `feeBps` is set to 10000 (100%), all WETH received is\n * sent to strategist.\n *\n * @param aeroToSwap Amount of AERO to swap\n * @param minWETHExpected Min. amount of WETH to expect\n * @param feeBps Performance fee bps (Sent to strategist)\n * @param sendYieldToDripper Sends yield to Dripper, if set to true.\n * Otherwise, to the Guardian\n */\n function harvestAndSwap(\n uint256 aeroToSwap,\n uint256 minWETHExpected,\n uint256 feeBps,\n bool sendYieldToDripper\n ) external onlyGovernorOrStrategist {\n address strategistAddr = vault.strategistAddr();\n require(strategistAddr != address(0), \"Guardian address not set\");\n\n // Yields can either be sent to the Dripper or Strategist\n address yieldRecipient = sendYieldToDripper\n ? vault.dripper()\n : strategistAddr;\n require(yieldRecipient != address(0), \"Yield recipient not set\");\n\n require(feeBps <= 10000, \"Invalid Fee Bps\");\n\n // Collect all AERO\n amoStrategy.collectRewardTokens();\n\n uint256 aeroBalance = aero.balanceOf(address(this));\n if (aeroBalance == 0) {\n // Do nothing if there's no AERO to transfer/swap\n return;\n }\n\n if (aeroToSwap > 0) {\n if (aeroBalance < aeroToSwap) {\n // Transfer in balance from the multisig as needed\n // slither-disable-next-line unchecked-transfer arbitrary-send-erc20\n aero.safeTransferFrom(\n strategistAddr,\n address(this),\n aeroToSwap - aeroBalance\n );\n }\n\n _doSwap(aeroToSwap, minWETHExpected);\n\n // Figure out AERO left in contract after swap\n aeroBalance = aero.balanceOf(address(this));\n }\n\n // Transfer out any leftover AERO after swap\n if (aeroBalance > 0) {\n aero.safeTransfer(strategistAddr, aeroBalance);\n }\n\n // Computes using all balance the contract holds,\n // not just the WETH received from swap. Use `transferToken`\n // if there's any WETH left that needs to be taken out\n uint256 availableWETHBalance = weth.balanceOf(address(this));\n // Computation rounds in favor of protocol\n uint256 fee = (availableWETHBalance * feeBps) / 10000;\n uint256 yield = availableWETHBalance - fee;\n\n // Transfer yield, if any\n if (yield > 0) {\n weth.safeTransfer(yieldRecipient, yield);\n }\n\n // Transfer fee to the Guardian, if any\n if (fee > 0) {\n weth.safeTransfer(strategistAddr, fee);\n }\n\n emit YieldSent(yieldRecipient, yield, fee);\n }\n\n /**\n * @notice Swaps AERO to WETH on Aerodrome\n * @param aeroToSwap Amount of AERO to swap\n * @param minWETHExpected Min. amount of WETH to expect\n */\n function _doSwap(uint256 aeroToSwap, uint256 minWETHExpected) internal {\n // Let the swap router move funds\n aero.approve(address(swapRouter), aeroToSwap);\n\n // Do the swap\n uint256 wethReceived = swapRouter.exactInputSingle(\n ISwapRouter.ExactInputSingleParams({\n tokenIn: address(aero),\n tokenOut: address(weth),\n tickSpacing: 200, // From AERO/WETH pool contract\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: aeroToSwap,\n amountOutMinimum: minWETHExpected,\n sqrtPriceLimitX96: 0\n })\n );\n\n emit RewardTokenSwapped(\n address(aero),\n address(weth),\n 0,\n aeroToSwap,\n wethReceived\n );\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * the contract, i.e. mistaken sends.\n * Also, allows to transfer any AERO left in the contract.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n virtual\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n}\n" + }, + "contracts/harvest/OETHDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Dripper } from \"./Dripper.sol\";\n\n/**\n * @title OETH Dripper Contract\n * @author Origin Protocol Inc\n */\ncontract OETHDripper is Dripper {\n constructor(address _vault, address _token) Dripper(_vault, _token) {}\n}\n" + }, + "contracts/harvest/OETHFixedRateDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { FixedRateDripper } from \"./FixedRateDripper.sol\";\n\n/**\n * @title OETH FixedRateDripper Contract\n * @author Origin Protocol Inc\n */\ncontract OETHFixedRateDripper is FixedRateDripper {\n constructor(address _vault, address _token)\n FixedRateDripper(_vault, _token)\n {}\n}\n" + }, + "contracts/harvest/OETHHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractHarvester } from \"./AbstractHarvester.sol\";\n\ncontract OETHHarvester is AbstractHarvester {\n constructor(address _vault, address _wethAddress)\n AbstractHarvester(_vault, _wethAddress)\n {}\n}\n" + }, + "contracts/harvest/OETHHarvesterSimple.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\n\n/// @title OETH Harvester Simple Contract\n/// @notice Contract to harvest rewards from strategies\n/// @author Origin Protocol Inc\ncontract OETHHarvesterSimple is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n ////////////////////////////////////////////////////\n /// --- CONSTANTS & IMMUTABLES\n ////////////////////////////////////////////////////\n /// @notice wrapped native token address (WETH or wS)\n address public immutable wrappedNativeToken;\n\n ////////////////////////////////////////////////////\n /// --- STORAGE\n ////////////////////////////////////////////////////\n /// @notice Dripper address\n address public dripper;\n\n /// @notice Mapping of supported strategies\n mapping(address => bool) public supportedStrategies;\n\n /// @notice Gap for upgrade safety\n uint256[48] private ___gap;\n\n ////////////////////////////////////////////////////\n /// --- EVENTS\n ////////////////////////////////////////////////////\n event Harvested(\n address indexed strategy,\n address token,\n uint256 amount,\n address indexed receiver\n );\n event SupportedStrategyUpdated(address strategy, bool status);\n event DripperUpdated(address dripper);\n\n ////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////\n constructor(address _wrappedNativeToken) {\n wrappedNativeToken = _wrappedNativeToken;\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /// @notice Initialize the contract\n function initialize() external onlyGovernor initializer {\n // Call it to set `initialized` to true and to prevent the implementation\n // from getting initialized in future through the proxy\n }\n\n ////////////////////////////////////////////////////\n /// --- MUTATIVE FUNCTIONS\n ////////////////////////////////////////////////////\n /// @notice Harvest rewards from a strategy and transfer to strategist or dripper\n /// @param _strategy Address of the strategy to harvest\n function harvestAndTransfer(address _strategy) external {\n _harvestAndTransfer(_strategy);\n }\n\n /// @notice Harvest rewards from multiple strategies and transfer to strategist or dripper\n /// @param _strategies Array of strategy addresses to harvest\n function harvestAndTransfer(address[] calldata _strategies) external {\n for (uint256 i = 0; i < _strategies.length; i++) {\n _harvestAndTransfer(_strategies[i]);\n }\n }\n\n /// @notice Internal logic to harvest rewards from a strategy\n function _harvestAndTransfer(address _strategy) internal virtual {\n // Ensure strategy is supported\n require(supportedStrategies[_strategy], \"Strategy not supported\");\n\n // Store locally for some gas savings\n address _strategist = strategistAddr;\n address _dripper = dripper;\n\n // Harvest rewards\n IStrategy(_strategy).collectRewardTokens();\n\n // Cache reward tokens\n address[] memory rewardTokens = IStrategy(_strategy)\n .getRewardTokenAddresses();\n\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; i++) {\n // Cache balance\n address token = rewardTokens[i];\n uint256 balance = IERC20(token).balanceOf(address(this));\n if (balance > 0) {\n // Determine receiver\n address receiver = token == wrappedNativeToken\n ? _dripper\n : _strategist;\n require(receiver != address(0), \"Invalid receiver\");\n\n // Transfer to the Strategist or the Dripper\n IERC20(token).safeTransfer(receiver, balance);\n emit Harvested(_strategy, token, balance, receiver);\n }\n }\n }\n\n ////////////////////////////////////////////////////\n /// --- GOVERNANCE\n ////////////////////////////////////////////////////\n /// @notice Set supported strategy\n /// @param _strategy Address of the strategy\n /// @param _isSupported Boolean indicating if strategy is supported\n function setSupportedStrategy(address _strategy, bool _isSupported)\n external\n onlyGovernorOrStrategist\n {\n require(_strategy != address(0), \"Invalid strategy\");\n supportedStrategies[_strategy] = _isSupported;\n emit SupportedStrategyUpdated(_strategy, _isSupported);\n }\n\n /// @notice Transfer tokens to strategist\n /// @param _asset Address of the token\n /// @param _amount Amount of tokens to transfer\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernorOrStrategist\n {\n IERC20(_asset).safeTransfer(strategistAddr, _amount);\n }\n\n /// @notice Set the dripper address\n /// @param _dripper Address of the dripper\n function setDripper(address _dripper) external onlyGovernor {\n _setDripper(_dripper);\n }\n\n /// @notice Internal logic to set the dripper address\n function _setDripper(address _dripper) internal {\n require(_dripper != address(0), \"Invalid dripper\");\n dripper = _dripper;\n emit DripperUpdated(_dripper);\n }\n}\n" + }, + "contracts/harvest/OSonicHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SuperOETHHarvester } from \"./SuperOETHHarvester.sol\";\n\ncontract OSonicHarvester is SuperOETHHarvester {\n /// @param _wrappedNativeToken Address of the native Wrapped S (wS) token\n constructor(address _wrappedNativeToken)\n SuperOETHHarvester(_wrappedNativeToken)\n {}\n}\n" + }, + "contracts/harvest/SuperOETHHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHHarvesterSimple, IERC20, IStrategy, SafeERC20 } from \"./OETHHarvesterSimple.sol\";\n\ncontract SuperOETHHarvester is OETHHarvesterSimple {\n using SafeERC20 for IERC20;\n\n constructor(address _wrappedNativeToken)\n OETHHarvesterSimple(_wrappedNativeToken)\n {}\n\n /// @inheritdoc OETHHarvesterSimple\n function _harvestAndTransfer(address _strategy) internal virtual override {\n // Ensure strategy is supported\n require(supportedStrategies[_strategy], \"Strategy not supported\");\n\n address receiver = strategistAddr;\n require(receiver != address(0), \"Invalid receiver\");\n\n // Harvest rewards\n IStrategy(_strategy).collectRewardTokens();\n\n // Cache reward tokens\n address[] memory rewardTokens = IStrategy(_strategy)\n .getRewardTokenAddresses();\n\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; i++) {\n // Cache balance\n address token = rewardTokens[i];\n uint256 balance = IERC20(token).balanceOf(address(this));\n if (balance > 0) {\n // Transfer everything to the strategist\n IERC20(token).safeTransfer(receiver, balance);\n emit Harvested(_strategy, token, balance, receiver);\n }\n }\n }\n}\n" + }, + "contracts/interfaces/aerodrome/IAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ICLPool } from \"./ICLPool.sol\";\n\ninterface IAMOStrategy {\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth);\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth);\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n error OutsideExpectedTickRange(int24 currentTick);\n\n function governor() external view returns (address);\n\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) external;\n\n function clPool() external view returns (ICLPool);\n\n function vaultAddress() external view returns (address);\n\n function poolWethShareVarianceAllowed() external view returns (uint256);\n\n function poolWethShare() external view returns (uint256);\n\n function tokenId() external view returns (uint256);\n\n function withdrawAll() external;\n\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external;\n\n function setWithdrawLiquidityShare(uint128 share) external;\n\n function lowerTick() external view returns (int24);\n\n function upperTick() external view returns (int24);\n\n function getPoolX96Price() external view returns (uint160 _sqrtRatioX96);\n\n function sqrtRatioX96TickLower() external view returns (uint160);\n\n function sqrtRatioX96TickHigher() external view returns (uint160);\n\n function tickSpacing() external view returns (int24);\n\n function allowedWethShareStart() external view returns (uint256);\n\n function allowedWethShareEnd() external view returns (uint256);\n\n function claimGovernance() external;\n\n function transferGovernance(address _governor) external;\n\n function getPositionPrincipal()\n external\n view\n returns (uint256 _amountWeth, uint256 _amountOethb);\n}\n" + }, + "contracts/interfaces/aerodrome/ICLGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\ninterface ICLGauge {\n /// @notice Returns the claimable rewards for a given account and tokenId\n /// @dev Throws if account is not the position owner\n /// @dev pool.updateRewardsGrowthGlobal() needs to be called first, to return the correct claimable rewards\n /// @param account The address of the user\n /// @param tokenId The tokenId of the position\n /// @return The amount of claimable reward\n function earned(address account, uint256 tokenId)\n external\n view\n returns (uint256);\n\n /// @notice Retrieve rewards for all tokens owned by an account\n /// @dev Throws if not called by the voter\n /// @param account The account of the user\n function getReward(address account) external;\n\n /// @notice Retrieve rewards for a tokenId\n /// @dev Throws if not called by the position owner\n /// @param tokenId The tokenId of the position\n function getReward(uint256 tokenId) external;\n\n /// @notice Notifies gauge of gauge rewards.\n /// @param amount Amount of gauge rewards (emissions) to notify. Must be greater than 604_800.\n function notifyRewardAmount(uint256 amount) external;\n\n /// @dev Notifies gauge of gauge rewards without distributing its fees.\n /// Assumes gauge reward tokens is 18 decimals.\n /// If not 18 decimals, rewardRate may have rounding issues.\n /// @param amount Amount of gauge rewards (emissions) to notify. Must be greater than 604_800.\n function notifyRewardWithoutClaim(uint256 amount) external;\n\n /// @notice Used to deposit a CL position into the gauge\n /// @notice Allows the user to receive emissions instead of fees\n /// @param tokenId The tokenId of the position\n function deposit(uint256 tokenId) external;\n\n /// @notice Used to withdraw a CL position from the gauge\n /// @notice Allows the user to receive fees instead of emissions\n /// @notice Outstanding emissions will be collected on withdrawal\n /// @param tokenId The tokenId of the position\n function withdraw(uint256 tokenId) external;\n\n // /// @notice Fetch all tokenIds staked by a given account\n // /// @param depositor The address of the user\n // /// @return The tokenIds of the staked positions\n // function stakedValues(address depositor) external view returns (uint256[] memory);\n\n // /// @notice Fetch a staked tokenId by index\n // /// @param depositor The address of the user\n // /// @param index The index of the staked tokenId\n // /// @return The tokenId of the staked position\n // function stakedByIndex(address depositor, uint256 index) external view returns (uint256);\n\n // /// @notice Check whether a position is staked in the gauge by a certain user\n // /// @param depositor The address of the user\n // /// @param tokenId The tokenId of the position\n // /// @return Whether the position is staked in the gauge\n // function stakedContains(address depositor, uint256 tokenId) external view returns (bool);\n\n // /// @notice The amount of positions staked in the gauge by a certain user\n // /// @param depositor The address of the user\n // /// @return The amount of positions staked in the gauge\n // function stakedLength(address depositor) external view returns (uint256);\n\n function feesVotingReward() external view returns (address);\n}\n" + }, + "contracts/interfaces/aerodrome/ICLPool.sol": { + "content": "pragma solidity >=0.5.0;\n\n/// @title The interface for a CL Pool\n/// @notice A CL pool facilitates swapping and automated market making between any two assets that strictly conform\n/// to the ERC20 specification\n/// @dev The pool interface is broken up into many smaller pieces\ninterface ICLPool {\n function slot0()\n external\n view\n returns (\n uint160 sqrtPriceX96,\n int24 tick,\n uint16 observationIndex,\n uint16 observationCardinality,\n uint16 observationCardinalityNext,\n bool unlocked\n );\n\n /// @notice The first of the two tokens of the pool, sorted by address\n /// @return The token contract address\n function token0() external view returns (address);\n\n /// @notice The second of the two tokens of the pool, sorted by address\n /// @return The token contract address\n function token1() external view returns (address);\n\n function tickSpacing() external view returns (int24);\n\n /// @notice The gauge corresponding to this pool\n /// @return The gauge contract address\n function gauge() external view returns (address);\n\n /// @notice The currently in range liquidity available to the pool\n /// @dev This value has no relationship to the total liquidity across all ticks\n /// @dev This value includes staked liquidity\n function liquidity() external view returns (uint128);\n\n /// @notice Look up information about a specific tick in the pool\n /// @param tick The tick to look up\n /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or\n /// tick upper,\n /// liquidityNet how much liquidity changes when the pool price crosses the tick,\n /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,\n /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,\n /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick\n /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from\n /// the current tick,\n /// secondsOutside the seconds spent on the other side of the tick from the current tick,\n /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise\n /// equal to false.\n /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.\n /// In addition, these values are only relative and must be used only in comparison to previous snapshots for\n /// a specific position.\n function ticks(int24 tick)\n external\n view\n returns (\n uint128 liquidityGross,\n int128 liquidityNet,\n uint256 feeGrowthOutside0X128,\n uint256 feeGrowthOutside1X128,\n int56 tickCumulativeOutside,\n uint160 secondsPerLiquidityOutsideX128,\n uint32 secondsOutside,\n bool initialized\n );\n}\n" + }, + "contracts/interfaces/aerodrome/INonfungiblePositionManager.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title Non-fungible token for positions\n/// @notice Wraps CL positions in a non-fungible token interface which allows for them to be transferred\n/// and authorized.\n// slither-disable-start erc20-interface\ninterface INonfungiblePositionManager {\n /**\n * @dev See {IERC721-approve}.\n */\n function approve(address to, uint256 tokenId) external;\n\n /**\n * @dev See {IERC721-getApproved}.\n */\n function getApproved(uint256 tokenId) external returns (address);\n\n /**\n * @dev See {IERC721-ownerOf}.\n */\n function ownerOf(uint256 tokenId) external view returns (address);\n\n /// @notice Returns the position information associated with a given token ID.\n /// @dev Throws if the token ID is not valid.\n /// @param tokenId The ID of the token that represents the position\n /// @return nonce The nonce for permits\n /// @return operator The address that is approved for spending\n /// @return token0 The address of the token0 for a specific pool\n /// @return token1 The address of the token1 for a specific pool\n /// @return tickSpacing The tick spacing associated with the pool\n /// @return tickLower The lower end of the tick range for the position\n /// @return tickUpper The higher end of the tick range for the position\n /// @return liquidity The liquidity of the position\n /// @return feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position\n /// @return feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position\n /// @return tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation\n /// @return tokensOwed1 The uncollected amount of token1 owed to the position as of the last computation\n function positions(uint256 tokenId)\n external\n view\n returns (\n uint96 nonce,\n address operator,\n address token0,\n address token1,\n int24 tickSpacing,\n int24 tickLower,\n int24 tickUpper,\n uint128 liquidity,\n uint256 feeGrowthInside0LastX128,\n uint256 feeGrowthInside1LastX128,\n uint128 tokensOwed0,\n uint128 tokensOwed1\n );\n\n struct MintParams {\n address token0;\n address token1;\n int24 tickSpacing;\n int24 tickLower;\n int24 tickUpper;\n uint256 amount0Desired;\n uint256 amount1Desired;\n uint256 amount0Min;\n uint256 amount1Min;\n address recipient;\n uint256 deadline;\n uint160 sqrtPriceX96;\n }\n\n /// @notice Creates a new position wrapped in a NFT\n /// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized\n /// a method does not exist, i.e. the pool is assumed to be initialized.\n /// @param params The params necessary to mint a position, encoded as `MintParams` in calldata\n /// @return tokenId The ID of the token that represents the minted position\n /// @return liquidity The amount of liquidity for this position\n /// @return amount0 The amount of token0\n /// @return amount1 The amount of token1\n function mint(MintParams calldata params)\n external\n payable\n returns (\n uint256 tokenId,\n uint128 liquidity,\n uint256 amount0,\n uint256 amount1\n );\n\n struct IncreaseLiquidityParams {\n uint256 tokenId;\n uint256 amount0Desired;\n uint256 amount1Desired;\n uint256 amount0Min;\n uint256 amount1Min;\n uint256 deadline;\n }\n\n /// @notice Increases the amount of liquidity in a position, with tokens paid by the `msg.sender`\n /// @param params tokenId The ID of the token for which liquidity is being increased,\n /// amount0Desired The desired amount of token0 to be spent,\n /// amount1Desired The desired amount of token1 to be spent,\n /// amount0Min The minimum amount of token0 to spend, which serves as a slippage check,\n /// amount1Min The minimum amount of token1 to spend, which serves as a slippage check,\n /// deadline The time by which the transaction must be included to effect the change\n /// @return liquidity The new liquidity amount as a result of the increase\n /// @return amount0 The amount of token0 to acheive resulting liquidity\n /// @return amount1 The amount of token1 to acheive resulting liquidity\n function increaseLiquidity(IncreaseLiquidityParams calldata params)\n external\n payable\n returns (\n uint128 liquidity,\n uint256 amount0,\n uint256 amount1\n );\n\n struct DecreaseLiquidityParams {\n uint256 tokenId;\n uint128 liquidity;\n uint256 amount0Min;\n uint256 amount1Min;\n uint256 deadline;\n }\n\n /// @notice Decreases the amount of liquidity in a position and accounts it to the position\n /// @param params tokenId The ID of the token for which liquidity is being decreased,\n /// amount The amount by which liquidity will be decreased,\n /// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity,\n /// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity,\n /// deadline The time by which the transaction must be included to effect the change\n /// @return amount0 The amount of token0 accounted to the position's tokens owed\n /// @return amount1 The amount of token1 accounted to the position's tokens owed\n /// @dev The use of this function can cause a loss to users of the NonfungiblePositionManager\n /// @dev for tokens that have very high decimals.\n /// @dev The amount of tokens necessary for the loss is: 3.4028237e+38.\n /// @dev This is equivalent to 1e20 value with 18 decimals.\n function decreaseLiquidity(DecreaseLiquidityParams calldata params)\n external\n payable\n returns (uint256 amount0, uint256 amount1);\n\n struct CollectParams {\n uint256 tokenId;\n address recipient;\n uint128 amount0Max;\n uint128 amount1Max;\n }\n\n /// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient\n /// @notice Used to update staked positions before deposit and withdraw\n /// @param params tokenId The ID of the NFT for which tokens are being collected,\n /// recipient The account that should receive the tokens,\n /// amount0Max The maximum amount of token0 to collect,\n /// amount1Max The maximum amount of token1 to collect\n /// @return amount0 The amount of fees collected in token0\n /// @return amount1 The amount of fees collected in token1\n function collect(CollectParams calldata params)\n external\n payable\n returns (uint256 amount0, uint256 amount1);\n\n /// @notice Burns a token ID, which deletes it from the NFT contract. The token must have 0 liquidity and all tokens\n /// must be collected first.\n /// @param tokenId The ID of the token that is being burned\n function burn(uint256 tokenId) external payable;\n\n /// @notice Sets a new Token Descriptor\n /// @param _tokenDescriptor Address of the new Token Descriptor to be chosen\n function setTokenDescriptor(address _tokenDescriptor) external;\n\n /// @notice Sets a new Owner address\n /// @param _owner Address of the new Owner to be chosen\n function setOwner(address _owner) external;\n}\n// slither-disable-end erc20-interface\n" + }, + "contracts/interfaces/aerodrome/IQuoterV2.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title QuoterV2 Interface\n/// @notice Supports quoting the calculated amounts from exact input or exact output swaps.\n/// @notice For each pool also tells you the number of initialized ticks crossed and the sqrt price of the\n/// pool after the swap.\n/// @dev These functions are not marked view because they rely on calling non-view functions and reverting\n/// to compute the result. They are also not gas efficient and should not be called on-chain.\ninterface IQuoterV2 {\n /// @notice Returns the amount out received for a given exact input swap without executing the swap\n /// @param path The path of the swap, i.e. each token pair and the pool tick spacing\n /// @param amountIn The amount of the first token to swap\n /// @return amountOut The amount of the last token that would be received\n /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path\n /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for\n /// each pool in the path\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactInput(bytes memory path, uint256 amountIn)\n external\n returns (\n uint256 amountOut,\n uint160[] memory sqrtPriceX96AfterList,\n uint32[] memory initializedTicksCrossedList,\n uint256 gasEstimate\n );\n\n struct QuoteExactInputSingleParams {\n address tokenIn;\n address tokenOut;\n uint256 amountIn;\n int24 tickSpacing;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Returns the amount out received for a given exact input but for a swap of a single pool\n /// @param params The params for the quote, encoded as `QuoteExactInputSingleParams`\n /// tokenIn The token being swapped in\n /// tokenOut The token being swapped out\n /// tickSpacing The tick spacing of the token pool to consider for the pair\n /// amountIn The desired input amount\n /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap\n /// @return amountOut The amount of `tokenOut` that would be received\n /// @return sqrtPriceX96After The sqrt price of the pool after the swap\n /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactInputSingle(QuoteExactInputSingleParams memory params)\n external\n returns (\n uint256 amountOut,\n uint160 sqrtPriceX96After,\n uint32 initializedTicksCrossed,\n uint256 gasEstimate\n );\n\n /// @notice Returns the amount in required for a given exact output swap without executing the swap\n /// @param path The path of the swap, i.e. each token pair and the pool tick spacing.\n /// Path must be provided in reverse order\n /// @param amountOut The amount of the last token to receive\n /// @return amountIn The amount of first token required to be paid\n /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path\n /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for\n /// each pool in the path\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactOutput(bytes memory path, uint256 amountOut)\n external\n returns (\n uint256 amountIn,\n uint160[] memory sqrtPriceX96AfterList,\n uint32[] memory initializedTicksCrossedList,\n uint256 gasEstimate\n );\n\n struct QuoteExactOutputSingleParams {\n address tokenIn;\n address tokenOut;\n uint256 amount;\n int24 tickSpacing;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Returns the amount in required to receive the given exact output amount but for a swap of a single pool\n /// @param params The params for the quote, encoded as `QuoteExactOutputSingleParams`\n /// tokenIn The token being swapped in\n /// tokenOut The token being swapped out\n /// tickSpacing The tick spacing of the token pool to consider for the pair\n /// amountOut The desired output amount\n /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap\n /// @return amountIn The amount required as the input for the swap in order to receive `amountOut`\n /// @return sqrtPriceX96After The sqrt price of the pool after the swap\n /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactOutputSingle(QuoteExactOutputSingleParams memory params)\n external\n returns (\n uint256 amountIn,\n uint160 sqrtPriceX96After,\n uint32 initializedTicksCrossed,\n uint256 gasEstimate\n );\n}\n" + }, + "contracts/interfaces/aerodrome/ISugarHelper.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\npragma abicoder v2;\n\nimport { INonfungiblePositionManager } from \"./INonfungiblePositionManager.sol\";\n\ninterface ISugarHelper {\n struct PopulatedTick {\n int24 tick;\n uint160 sqrtRatioX96;\n int128 liquidityNet;\n uint128 liquidityGross;\n }\n\n ///\n /// Wrappers for LiquidityAmounts\n ///\n\n function getAmountsForLiquidity(\n uint160 sqrtRatioX96,\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96,\n uint128 liquidity\n ) external pure returns (uint256 amount0, uint256 amount1);\n\n function getLiquidityForAmounts(\n uint256 amount0,\n uint256 amount1,\n uint160 sqrtRatioX96,\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96\n ) external pure returns (uint128 liquidity);\n\n /// @notice Computes the amount of token0 for a given amount of token1 and price range\n /// @param amount1 Amount of token1 to estimate liquidity\n /// @param pool Address of the pool to be used\n /// @param sqrtRatioX96 A sqrt price representing the current pool prices\n /// @param tickLow Lower tick boundary\n /// @param tickLow Upper tick boundary\n /// @dev If the given pool address is not the zero address, will fetch `sqrtRatioX96` from pool\n /// @return amount0 Estimated amount of token0\n function estimateAmount0(\n uint256 amount1,\n address pool,\n uint160 sqrtRatioX96,\n int24 tickLow,\n int24 tickHigh\n ) external view returns (uint256 amount0);\n\n /// @notice Computes the amount of token1 for a given amount of token0 and price range\n /// @param amount0 Amount of token0 to estimate liquidity\n /// @param pool Address of the pool to be used\n /// @param sqrtRatioX96 A sqrt price representing the current pool prices\n /// @param tickLow Lower tick boundary\n /// @param tickLow Upper tick boundary\n /// @dev If the given pool address is not the zero address, will fetch `sqrtRatioX96` from pool\n /// @return amount1 Estimated amount of token1\n function estimateAmount1(\n uint256 amount0,\n address pool,\n uint160 sqrtRatioX96,\n int24 tickLow,\n int24 tickHigh\n ) external view returns (uint256 amount1);\n\n ///\n /// Wrappers for PositionValue\n ///\n\n function principal(\n INonfungiblePositionManager positionManager,\n uint256 tokenId,\n uint160 sqrtRatioX96\n ) external view returns (uint256 amount0, uint256 amount1);\n\n function fees(INonfungiblePositionManager positionManager, uint256 tokenId)\n external\n view\n returns (uint256 amount0, uint256 amount1);\n\n ///\n /// Wrappers for TickMath\n ///\n\n function getSqrtRatioAtTick(int24 tick)\n external\n pure\n returns (uint160 sqrtRatioX96);\n\n function getTickAtSqrtRatio(uint160 sqrtRatioX96)\n external\n pure\n returns (int24 tick);\n\n /// @notice Fetches Tick Data for all populated Ticks in given bitmaps\n /// @param pool Address of the pool from which to fetch data\n /// @param startTick Tick from which the first bitmap will be fetched\n /// @dev The number of bitmaps fetched by this function should always be `MAX_BITMAPS`,\n /// unless there are less than `MAX_BITMAPS` left to iterate through\n /// @return populatedTicks Array of all Populated Ticks in the provided bitmaps\n function getPopulatedTicks(address pool, int24 startTick)\n external\n view\n returns (PopulatedTick[] memory populatedTicks);\n}\n" + }, + "contracts/interfaces/aerodrome/ISwapRouter.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title Router token swapping functionality\n/// @notice Functions for swapping tokens via CL\ninterface ISwapRouter {\n struct ExactInputSingleParams {\n address tokenIn;\n address tokenOut;\n int24 tickSpacing;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInputSingle(ExactInputSingleParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactOutputSingleParams {\n address tokenIn;\n address tokenOut;\n int24 tickSpacing;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutputSingle(ExactOutputSingleParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n\n struct ExactOutputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutput(ExactOutputParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n}\n" + }, + "contracts/interfaces/balancer/IBalancerVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"../../utils/InitializableAbstractStrategy.sol\";\n\ninterface IBalancerVault {\n enum WeightedPoolJoinKind {\n INIT,\n EXACT_TOKENS_IN_FOR_BPT_OUT,\n TOKEN_IN_FOR_EXACT_BPT_OUT,\n ALL_TOKENS_IN_FOR_EXACT_BPT_OUT,\n ADD_TOKEN\n }\n\n enum WeightedPoolExitKind {\n EXACT_BPT_IN_FOR_ONE_TOKEN_OUT,\n EXACT_BPT_IN_FOR_TOKENS_OUT,\n BPT_IN_FOR_EXACT_TOKENS_OUT,\n REMOVE_TOKEN\n }\n\n /**\n * @dev Called by users to join a Pool, which transfers tokens from `sender` into the Pool's balance. This will\n * trigger custom Pool behavior, which will typically grant something in return to `recipient` - often tokenized\n * Pool shares.\n *\n * If the caller is not `sender`, it must be an authorized relayer for them.\n *\n * The `assets` and `maxAmountsIn` arrays must have the same length, and each entry indicates the maximum amount\n * to send for each asset. The amounts to send are decided by the Pool and not the Vault: it just enforces\n * these maximums.\n *\n * If joining a Pool that holds WETH, it is possible to send ETH directly: the Vault will do the wrapping. To enable\n * this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead of the\n * WETH address. Note that it is not possible to combine ETH and WETH in the same join. Any excess ETH will be sent\n * back to the caller (not the sender, which is important for relayers).\n *\n * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when\n * interacting with Pools that register and deregister tokens frequently. If sending ETH however, the array must be\n * sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the final\n * `assets` array might not be sorted. Pools with no registered tokens cannot be joined.\n *\n * If `fromInternalBalance` is true, the caller's Internal Balance will be preferred: ERC20 transfers will only\n * be made for the difference between the requested amount and Internal Balance (if any). Note that ETH cannot be\n * withdrawn from Internal Balance: attempting to do so will trigger a revert.\n *\n * This causes the Vault to call the `IBasePool.onJoinPool` hook on the Pool's contract, where Pools implement\n * their own custom logic. This typically requires additional information from the user (such as the expected number\n * of Pool shares). This can be encoded in the `userData` argument, which is ignored by the Vault and passed\n * directly to the Pool's contract, as is `recipient`.\n *\n * Emits a `PoolBalanceChanged` event.\n */\n function joinPool(\n bytes32 poolId,\n address sender,\n address recipient,\n JoinPoolRequest memory request\n ) external payable;\n\n struct JoinPoolRequest {\n address[] assets;\n uint256[] maxAmountsIn;\n bytes userData;\n bool fromInternalBalance;\n }\n\n /**\n * @dev Called by users to exit a Pool, which transfers tokens from the Pool's balance to `recipient`. This will\n * trigger custom Pool behavior, which will typically ask for something in return from `sender` - often tokenized\n * Pool shares. The amount of tokens that can be withdrawn is limited by the Pool's `cash` balance (see\n * `getPoolTokenInfo`).\n *\n * If the caller is not `sender`, it must be an authorized relayer for them.\n *\n * The `tokens` and `minAmountsOut` arrays must have the same length, and each entry in these indicates the minimum\n * token amount to receive for each token contract. The amounts to send are decided by the Pool and not the Vault:\n * it just enforces these minimums.\n *\n * If exiting a Pool that holds WETH, it is possible to receive ETH directly: the Vault will do the unwrapping. To\n * enable this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead\n * of the WETH address. Note that it is not possible to combine ETH and WETH in the same exit.\n *\n * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when\n * interacting with Pools that register and deregister tokens frequently. If receiving ETH however, the array must\n * be sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the\n * final `assets` array might not be sorted. Pools with no registered tokens cannot be exited.\n *\n * If `toInternalBalance` is true, the tokens will be deposited to `recipient`'s Internal Balance. Otherwise,\n * an ERC20 transfer will be performed. Note that ETH cannot be deposited to Internal Balance: attempting to\n * do so will trigger a revert.\n *\n * `minAmountsOut` is the minimum amount of tokens the user expects to get out of the Pool, for each token in the\n * `tokens` array. This array must match the Pool's registered tokens.\n *\n * This causes the Vault to call the `IBasePool.onExitPool` hook on the Pool's contract, where Pools implement\n * their own custom logic. This typically requires additional information from the user (such as the expected number\n * of Pool shares to return). This can be encoded in the `userData` argument, which is ignored by the Vault and\n * passed directly to the Pool's contract.\n *\n * Emits a `PoolBalanceChanged` event.\n */\n function exitPool(\n bytes32 poolId,\n address sender,\n address payable recipient,\n ExitPoolRequest memory request\n ) external;\n\n struct ExitPoolRequest {\n address[] assets;\n uint256[] minAmountsOut;\n bytes userData;\n bool toInternalBalance;\n }\n\n /**\n * @dev Returns a Pool's registered tokens, the total balance for each, and the latest block when *any* of\n * the tokens' `balances` changed.\n *\n * The order of the `tokens` array is the same order that will be used in `joinPool`, `exitPool`, as well as in all\n * Pool hooks (where applicable). Calls to `registerTokens` and `deregisterTokens` may change this order.\n *\n * If a Pool only registers tokens once, and these are sorted in ascending order, they will be stored in the same\n * order as passed to `registerTokens`.\n *\n * Total balances include both tokens held by the Vault and those withdrawn by the Pool's Asset Managers. These are\n * the amounts used by joins, exits and swaps. For a detailed breakdown of token balances, use `getPoolTokenInfo`\n * instead.\n */\n function getPoolTokens(bytes32 poolId)\n external\n view\n returns (\n IERC20[] memory tokens,\n uint256[] memory balances,\n uint256 lastChangeBlock\n );\n\n /**\n * @dev Performs a set of user balance operations, which involve Internal Balance (deposit, withdraw or transfer)\n * and plain ERC20 transfers using the Vault's allowance. This last feature is particularly useful for relayers, as\n * it lets integrators reuse a user's Vault allowance.\n *\n * For each operation, if the caller is not `sender`, it must be an authorized relayer for them.\n */\n function manageUserBalance(UserBalanceOp[] memory ops) external payable;\n\n struct UserBalanceOp {\n UserBalanceOpKind kind;\n address asset;\n uint256 amount;\n address sender;\n address payable recipient;\n }\n\n enum UserBalanceOpKind {\n DEPOSIT_INTERNAL,\n WITHDRAW_INTERNAL,\n TRANSFER_INTERNAL,\n TRANSFER_EXTERNAL\n }\n\n enum SwapKind {\n GIVEN_IN,\n GIVEN_OUT\n }\n\n struct SingleSwap {\n bytes32 poolId;\n SwapKind kind;\n address assetIn;\n address assetOut;\n uint256 amount;\n bytes userData;\n }\n\n struct FundManagement {\n address sender;\n bool fromInternalBalance;\n address payable recipient;\n bool toInternalBalance;\n }\n\n function swap(\n SingleSwap calldata singleSwap,\n FundManagement calldata funds,\n uint256 limit,\n uint256 deadline\n ) external returns (uint256 amountCalculated);\n\n function getPoolTokenInfo(bytes32 poolId, address token)\n external\n view\n returns (\n uint256 cash,\n uint256 managed,\n uint256 lastChangeBlock,\n address assetManager\n );\n}\n" + }, + "contracts/interfaces/balancer/IMetaStablePool.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IRateProvider } from \"./IRateProvider.sol\";\n\ninterface IMetaStablePool {\n function getRateProviders()\n external\n view\n returns (IRateProvider[] memory providers);\n}\n" + }, + "contracts/interfaces/balancer/IOracleWeightedPool.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// The three values that can be queried:\n//\n// - PAIR_PRICE: the price of the tokens in the Pool, expressed as the price of the second token in units of the\n// first token. For example, if token A is worth $2, and token B is worth $4, the pair price will be 2.0.\n// Note that the price is computed *including* the tokens decimals. This means that the pair price of a Pool with\n// DAI and USDC will be close to 1.0, despite DAI having 18 decimals and USDC 6.\n//\n// - BPT_PRICE: the price of the Pool share token (BPT), in units of the first token.\n// Note that the price is computed *including* the tokens decimals. This means that the BPT price of a Pool with\n// USDC in which BPT is worth $5 will be 5.0, despite the BPT having 18 decimals and USDC 6.\n//\n// - INVARIANT: the value of the Pool's invariant, which serves as a measure of its liquidity.\nenum Variable {\n PAIR_PRICE,\n BPT_PRICE,\n INVARIANT\n}\n\n/**\n * @dev Information for a Time Weighted Average query.\n *\n * Each query computes the average over a window of duration `secs` seconds that ended `ago` seconds ago. For\n * example, the average over the past 30 minutes is computed by settings secs to 1800 and ago to 0. If secs is 1800\n * and ago is 1800 as well, the average between 60 and 30 minutes ago is computed instead.\n */\nstruct OracleAverageQuery {\n Variable variable;\n uint256 secs;\n uint256 ago;\n}\n\ninterface IOracleWeightedPool {\n /**\n * @dev Returns the time average weighted price corresponding to each of `queries`. Prices are represented as 18\n * decimal fixed point values.\n */\n function getTimeWeightedAverage(OracleAverageQuery[] memory queries)\n external\n view\n returns (uint256[] memory results);\n}\n" + }, + "contracts/interfaces/balancer/IRateProvider.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity ^0.8.0;\n\ninterface IRateProvider {\n function getRate() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/chainlink/AggregatorV3Interface.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorV3Interface {\n function decimals() external view returns (uint8);\n\n function description() external view returns (string memory);\n\n function version() external view returns (uint256);\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(uint80 _roundId)\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n\n function latestRoundData()\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBuyback {\n function swap() external;\n}\n" + }, + "contracts/interfaces/ICampaignRemoteManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICampaignRemoteManager {\n function createCampaign(\n CampaignCreationParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n function manageCampaign(\n CampaignManagementParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n function closeCampaign(\n CampaignClosingParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n struct CampaignCreationParams {\n uint256 chainId;\n address gauge;\n address manager;\n address rewardToken;\n uint8 numberOfPeriods;\n uint256 maxRewardPerVote;\n uint256 totalRewardAmount;\n address[] addresses;\n address hook;\n bool isWhitelist;\n }\n\n struct CampaignManagementParams {\n uint256 campaignId;\n address rewardToken;\n uint8 numberOfPeriods;\n uint256 totalRewardAmount;\n uint256 maxRewardPerVote;\n }\n\n struct CampaignClosingParams {\n uint256 campaignId;\n }\n}\n" + }, + "contracts/interfaces/IChildLiquidityGaugeFactory.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IChildLiquidityGaugeFactory {\n event DeployedGauge(\n address indexed _implementation,\n address indexed _lp_token,\n address indexed _deployer,\n bytes32 _salt,\n address _gauge\n );\n event Minted(\n address indexed _user,\n address indexed _gauge,\n uint256 _new_total\n );\n event TransferOwnership(address _old_owner, address _new_owner);\n event UpdateCallProxy(address _old_call_proxy, address _new_call_proxy);\n event UpdateImplementation(\n address _old_implementation,\n address _new_implementation\n );\n event UpdateManager(address _manager);\n event UpdateMirrored(address indexed _gauge, bool _mirrored);\n event UpdateRoot(address _factory, address _implementation);\n event UpdateVotingEscrow(\n address _old_voting_escrow,\n address _new_voting_escrow\n );\n\n function accept_transfer_ownership() external;\n\n function call_proxy() external view returns (address);\n\n function commit_transfer_ownership(address _future_owner) external;\n\n function crv() external view returns (address);\n\n function deploy_gauge(address _lp_token, bytes32 _salt)\n external\n returns (address);\n\n function deploy_gauge(\n address _lp_token,\n bytes32 _salt,\n address _manager\n ) external returns (address);\n\n function future_owner() external view returns (address);\n\n function gauge_data(address arg0) external view returns (uint256);\n\n function get_gauge(uint256 arg0) external view returns (address);\n\n function get_gauge_count() external view returns (uint256);\n\n function get_gauge_from_lp_token(address arg0)\n external\n view\n returns (address);\n\n function get_implementation() external view returns (address);\n\n function is_mirrored(address _gauge) external view returns (bool);\n\n function is_valid_gauge(address _gauge) external view returns (bool);\n\n function last_request(address _gauge) external view returns (uint256);\n\n function manager() external view returns (address);\n\n function mint(address _gauge) external;\n\n function mint_many(address[32] memory _gauges) external;\n\n function minted(address arg0, address arg1) external view returns (uint256);\n\n function owner() external view returns (address);\n\n function root_factory() external view returns (address);\n\n function root_implementation() external view returns (address);\n\n function set_call_proxy(address _new_call_proxy) external;\n\n function set_crv(address _crv) external;\n\n function set_implementation(address _implementation) external;\n\n function set_manager(address _new_manager) external;\n\n function set_mirrored(address _gauge, bool _mirrored) external;\n\n function set_root(address _factory, address _implementation) external;\n\n function set_voting_escrow(address _voting_escrow) external;\n\n function version() external view returns (string memory);\n\n function voting_escrow() external view returns (address);\n}\n" + }, + "contracts/interfaces/IComptroller.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IComptroller {\n // Claim all the COMP accrued by specific holders in specific markets for their supplies and/or borrows\n function claimComp(\n address[] memory holders,\n address[] memory cTokens,\n bool borrowers,\n bool suppliers\n ) external;\n\n function oracle() external view returns (address);\n}\n" + }, + "contracts/interfaces/ICurveLiquidityGaugeV6.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveLiquidityGaugeV6 {\n event ApplyOwnership(address admin);\n event Approval(\n address indexed _owner,\n address indexed _spender,\n uint256 _value\n );\n event CommitOwnership(address admin);\n event Deposit(address indexed provider, uint256 value);\n event SetGaugeManager(address _gauge_manager);\n event Transfer(address indexed _from, address indexed _to, uint256 _value);\n event UpdateLiquidityLimit(\n address indexed user,\n uint256 original_balance,\n uint256 original_supply,\n uint256 working_balance,\n uint256 working_supply\n );\n event Withdraw(address indexed provider, uint256 value);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_reward(address _reward_token, address _distributor) external;\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function claim_rewards() external;\n\n function claim_rewards(address _addr) external;\n\n function claim_rewards(address _addr, address _receiver) external;\n\n function claimable_reward(address _user, address _reward_token)\n external\n view\n returns (uint256);\n\n function claimable_tokens(address addr) external returns (uint256);\n\n function claimed_reward(address _addr, address _token)\n external\n view\n returns (uint256);\n\n function decimals() external view returns (uint256);\n\n function decreaseAllowance(address _spender, uint256 _subtracted_value)\n external\n returns (bool);\n\n function deposit(uint256 _value) external;\n\n function deposit(uint256 _value, address _addr) external;\n\n function deposit(\n uint256 _value,\n address _addr,\n bool _claim_rewards\n ) external;\n\n function deposit_reward_token(address _reward_token, uint256 _amount)\n external;\n\n function deposit_reward_token(\n address _reward_token,\n uint256 _amount,\n uint256 _epoch\n ) external;\n\n function factory() external view returns (address);\n\n function future_epoch_time() external view returns (uint256);\n\n function increaseAllowance(address _spender, uint256 _added_value)\n external\n returns (bool);\n\n function inflation_rate() external view returns (uint256);\n\n function integrate_checkpoint() external view returns (uint256);\n\n function integrate_checkpoint_of(address arg0)\n external\n view\n returns (uint256);\n\n function integrate_fraction(address arg0) external view returns (uint256);\n\n function integrate_inv_supply(uint256 arg0) external view returns (uint256);\n\n function integrate_inv_supply_of(address arg0)\n external\n view\n returns (uint256);\n\n function is_killed() external view returns (bool);\n\n function kick(address addr) external;\n\n function lp_token() external view returns (address);\n\n function manager() external view returns (address);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function period() external view returns (int128);\n\n function period_timestamp(uint256 arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function reward_count() external view returns (uint256);\n\n function reward_integral_for(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function reward_tokens(uint256 arg0) external view returns (address);\n\n function rewards_receiver(address arg0) external view returns (address);\n\n function salt() external view returns (bytes32);\n\n function set_gauge_manager(address _gauge_manager) external;\n\n function set_killed(bool _is_killed) external;\n\n function set_reward_distributor(address _reward_token, address _distributor)\n external;\n\n function set_rewards_receiver(address _receiver) external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function user_checkpoint(address addr) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw(uint256 _value) external;\n\n function withdraw(uint256 _value, bool _claim_rewards) external;\n\n function working_balances(address arg0) external view returns (uint256);\n\n function working_supply() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/ICurveMinter.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveMinter {\n event Minted(address indexed recipient, address gauge, uint256 minted);\n\n function allowed_to_mint_for(address arg0, address arg1)\n external\n view\n returns (bool);\n\n function controller() external view returns (address);\n\n function mint(address gauge_addr) external;\n\n function mint_for(address gauge_addr, address _for) external;\n\n function mint_many(address[8] memory gauge_addrs) external;\n\n function minted(address arg0, address arg1) external view returns (uint256);\n\n function toggle_approve_mint(address minting_user) external;\n\n function token() external view returns (address);\n}\n" + }, + "contracts/interfaces/ICurveStableSwapNG.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveStableSwapNG {\n event AddLiquidity(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event ApplyNewFee(uint256 fee, uint256 offpeg_fee_multiplier);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event RampA(\n uint256 old_A,\n uint256 new_A,\n uint256 initial_time,\n uint256 future_time\n );\n event RemoveLiquidity(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 token_supply\n );\n event RemoveLiquidityImbalance(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event RemoveLiquidityOne(\n address indexed provider,\n int128 token_id,\n uint256 token_amount,\n uint256 coin_amount,\n uint256 token_supply\n );\n event SetNewMATime(uint256 ma_exp_time, uint256 D_ma_time);\n event StopRampA(uint256 A, uint256 t);\n event TokenExchange(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event TokenExchangeUnderlying(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event Transfer(\n address indexed sender,\n address indexed receiver,\n uint256 value\n );\n\n function A() external view returns (uint256);\n\n function A_precise() external view returns (uint256);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function D_ma_time() external view returns (uint256);\n\n function D_oracle() external view returns (uint256);\n\n function N_COINS() external view returns (uint256);\n\n function add_liquidity(uint256[] memory _amounts, uint256 _min_mint_amount)\n external\n returns (uint256);\n\n function add_liquidity(\n uint256[] memory _amounts,\n uint256 _min_mint_amount,\n address _receiver\n ) external returns (uint256);\n\n function admin_balances(uint256 arg0) external view returns (uint256);\n\n function admin_fee() external view returns (uint256);\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function balances(uint256 i) external view returns (uint256);\n\n function calc_token_amount(uint256[] memory _amounts, bool _is_deposit)\n external\n view\n returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _burn_amount, int128 i)\n external\n view\n returns (uint256);\n\n function coins(uint256 arg0) external view returns (address);\n\n function decimals() external view returns (uint8);\n\n function dynamic_fee(int128 i, int128 j) external view returns (uint256);\n\n function ema_price(uint256 i) external view returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external returns (uint256);\n\n function exchange_received(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external returns (uint256);\n\n function exchange_received(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external returns (uint256);\n\n function fee() external view returns (uint256);\n\n function future_A() external view returns (uint256);\n\n function future_A_time() external view returns (uint256);\n\n function get_balances() external view returns (uint256[] memory);\n\n function get_dx(\n int128 i,\n int128 j,\n uint256 dy\n ) external view returns (uint256);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n\n function get_p(uint256 i) external view returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function initial_A() external view returns (uint256);\n\n function initial_A_time() external view returns (uint256);\n\n function last_price(uint256 i) external view returns (uint256);\n\n function ma_exp_time() external view returns (uint256);\n\n function ma_last_time() external view returns (uint256);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function offpeg_fee_multiplier() external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function price_oracle(uint256 i) external view returns (uint256);\n\n function ramp_A(uint256 _future_A, uint256 _future_time) external;\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts\n ) external returns (uint256[] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts,\n address _receiver\n ) external returns (uint256[] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts,\n address _receiver,\n bool _claim_admin_fees\n ) external returns (uint256[] memory);\n\n function remove_liquidity_imbalance(\n uint256[] memory _amounts,\n uint256 _max_burn_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[] memory _amounts,\n uint256 _max_burn_amount,\n address _receiver\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received,\n address _receiver\n ) external returns (uint256);\n\n function salt() external view returns (bytes32);\n\n function set_ma_exp_time(uint256 _ma_exp_time, uint256 _D_ma_time) external;\n\n function set_new_fee(uint256 _new_fee, uint256 _new_offpeg_fee_multiplier)\n external;\n\n function stop_ramp_A() external;\n\n function stored_rates() external view returns (uint256[] memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw_admin_fees() external;\n}\n" + }, + "contracts/interfaces/ICurveXChainLiquidityGauge.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveXChainLiquidityGauge {\n event Approval(\n address indexed _owner,\n address indexed _spender,\n uint256 _value\n );\n event Deposit(address indexed provider, uint256 value);\n event SetGaugeManager(address _gauge_manager);\n event Transfer(address indexed _from, address indexed _to, uint256 _value);\n event UpdateLiquidityLimit(\n address indexed user,\n uint256 original_balance,\n uint256 original_supply,\n uint256 working_balance,\n uint256 working_supply\n );\n event Withdraw(address indexed provider, uint256 value);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_reward(address _reward_token, address _distributor) external;\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function claim_rewards() external;\n\n function claim_rewards(address _addr) external;\n\n function claim_rewards(address _addr, address _receiver) external;\n\n function claimable_reward(address _user, address _reward_token)\n external\n view\n returns (uint256);\n\n function claimable_tokens(address addr) external returns (uint256);\n\n function claimed_reward(address _addr, address _token)\n external\n view\n returns (uint256);\n\n function decimals() external view returns (uint256);\n\n function decreaseAllowance(address _spender, uint256 _subtracted_value)\n external\n returns (bool);\n\n function deposit(uint256 _value) external;\n\n function deposit(uint256 _value, address _addr) external;\n\n function deposit(\n uint256 _value,\n address _addr,\n bool _claim_rewards\n ) external;\n\n function deposit_reward_token(address _reward_token, uint256 _amount)\n external;\n\n function deposit_reward_token(\n address _reward_token,\n uint256 _amount,\n uint256 _epoch\n ) external;\n\n function factory() external view returns (address);\n\n function increaseAllowance(address _spender, uint256 _added_value)\n external\n returns (bool);\n\n function inflation_rate(uint256 arg0) external view returns (uint256);\n\n function initialize(\n address _lp_token,\n address _root,\n address _manager\n ) external;\n\n function integrate_checkpoint() external view returns (uint256);\n\n function integrate_checkpoint_of(address arg0)\n external\n view\n returns (uint256);\n\n function integrate_fraction(address arg0) external view returns (uint256);\n\n function integrate_inv_supply(int128 arg0) external view returns (uint256);\n\n function integrate_inv_supply_of(address arg0)\n external\n view\n returns (uint256);\n\n function is_killed() external view returns (bool);\n\n function lp_token() external view returns (address);\n\n function manager() external view returns (address);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function period() external view returns (int128);\n\n function period_timestamp(int128 arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function recover_remaining(address _reward_token) external;\n\n function reward_count() external view returns (uint256);\n\n function reward_integral_for(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function reward_remaining(address arg0) external view returns (uint256);\n\n function reward_tokens(uint256 arg0) external view returns (address);\n\n function rewards_receiver(address arg0) external view returns (address);\n\n function root_gauge() external view returns (address);\n\n function set_gauge_manager(address _gauge_manager) external;\n\n function set_killed(bool _is_killed) external;\n\n function set_manager(address _gauge_manager) external;\n\n function set_reward_distributor(address _reward_token, address _distributor)\n external;\n\n function set_rewards_receiver(address _receiver) external;\n\n function set_root_gauge(address _root) external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function update_voting_escrow() external;\n\n function user_checkpoint(address addr) external returns (bool);\n\n function version() external view returns (string memory);\n\n function voting_escrow() external view returns (address);\n\n function withdraw(uint256 _value) external;\n\n function withdraw(uint256 _value, bool _claim_rewards) external;\n\n function withdraw(\n uint256 _value,\n bool _claim_rewards,\n address _receiver\n ) external;\n\n function working_balances(address arg0) external view returns (uint256);\n\n function working_supply() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/ICVXLocker.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface ICVXLocker {\n function lock(\n address _account,\n uint256 _amount,\n uint256 _spendRatio\n ) external;\n\n function lockedBalanceOf(address _account) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/IDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IDripper {\n /// @notice How much funds have dripped out already and are currently\n // available to be sent to the vault.\n /// @return The amount that would be sent if a collect was called\n function availableFunds() external view returns (uint256);\n\n /// @notice Collect all dripped funds and send to vault.\n /// Recalculate new drip rate.\n function collect() external;\n\n /// @notice Collect all dripped funds, send to vault, recalculate new drip\n /// rate, and rebase mToken.\n function collectAndRebase() external;\n\n /// @notice Change the drip duration. Governor only.\n /// @param _durationSeconds the number of seconds to drip out the entire\n /// balance over if no collects were called during that time.\n function setDripDuration(uint256 _durationSeconds) external;\n\n /// @dev Transfer out ERC20 tokens held by the contract. Governor only.\n /// @param _asset ERC20 token address\n /// @param _amount amount to transfer\n function transferToken(address _asset, uint256 _amount) external;\n}\n" + }, + "contracts/interfaces/IEthUsdOracle.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IEthUsdOracle {\n /**\n * @notice Returns ETH price in USD.\n * @return Price in USD with 6 decimal digits.\n */\n function ethUsdPrice() external view returns (uint256);\n\n /**\n * @notice Returns token price in USD.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in USD with 6 decimal digits.\n */\n function tokUsdPrice(string calldata symbol)\n external\n view\n returns (uint256);\n\n /**\n * @notice Returns the asset price in ETH.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in ETH with 8 decimal digits.\n */\n function tokEthPrice(string calldata symbol)\n external\n view\n returns (uint256);\n}\n\ninterface IViewEthUsdOracle {\n /**\n * @notice Returns ETH price in USD.\n * @return Price in USD with 6 decimal digits.\n */\n function ethUsdPrice() external view returns (uint256);\n\n /**\n * @notice Returns token price in USD.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in USD with 6 decimal digits.\n */\n function tokUsdPrice(string calldata symbol)\n external\n view\n returns (uint256);\n\n /**\n * @notice Returns the asset price in ETH.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in ETH with 8 decimal digits.\n */\n function tokEthPrice(string calldata symbol)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/IFraxETHMinter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IFraxETHMinter {\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares);\n}\n" + }, + "contracts/interfaces/IGetExchangeRateToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IGetExchangeRateToken {\n function getExchangeRate() external view returns (uint256 _exchangeRate);\n}\n" + }, + "contracts/interfaces/IMinMaxOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IMinMaxOracle {\n //Assuming 8 decimals\n function priceMin(string calldata symbol) external view returns (uint256);\n\n function priceMax(string calldata symbol) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IMockVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"./IVault.sol\";\n\ninterface IMockVault is IVault {\n function outstandingWithdrawalsAmount() external view returns (uint256);\n\n function wethAvailable() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IOETHZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOETHZapper {\n function deposit() external payable returns (uint256);\n}\n" + }, + "contracts/interfaces/IOneInch.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/// 1Inch swap data\nstruct SwapDescription {\n IERC20 srcToken; // contract address of a token to sell\n IERC20 dstToken; // contract address of a token to buy\n address payable srcReceiver;\n address payable dstReceiver; // Receiver of destination currency. default: fromAddress\n uint256 amount;\n uint256 minReturnAmount;\n uint256 flags;\n}\n\n/// @title Interface for making arbitrary calls during swap\ninterface IAggregationExecutor {\n /// @notice propagates information about original msg.sender and executes arbitrary data\n function execute(address msgSender) external payable; // 0x4b64e492\n}\n\ninterface IOneInchRouter {\n /// @notice Performs a swap, delegating all calls encoded in `data` to `executor`.\n function swap(\n IAggregationExecutor executor,\n SwapDescription calldata desc,\n bytes calldata permit,\n bytes calldata data\n ) external returns (uint256 returnAmount, uint256 spentAmount);\n\n /// @notice Performs swap using Uniswap exchange. Wraps and unwraps ETH if required.\n function unoswapTo(\n address payable recipient,\n IERC20 srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) external payable returns (uint256 returnAmount);\n\n /// @notice Performs swap using Uniswap V3 exchange. Wraps and unwraps ETH if required.\n function uniswapV3SwapTo(\n address payable recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) external payable returns (uint256 returnAmount);\n}\n" + }, + "contracts/interfaces/IOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOracle {\n /**\n * @dev returns the asset price in USD, in 8 decimal digits.\n *\n * The version of priceProvider deployed for OETH has 18 decimal digits\n */\n function price(address asset) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IOUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOUSD {\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n function _totalSupply() external view returns (uint256);\n\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address _account) external view returns (uint256);\n\n function burn(address account, uint256 amount) external;\n\n function changeSupply(uint256 _newTotalSupply) external;\n\n function claimGovernance() external;\n\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256);\n\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n );\n\n function decimals() external view returns (uint8);\n\n function decreaseAllowance(address _spender, uint256 _subtractedValue)\n external\n returns (bool);\n\n function governor() external view returns (address);\n\n function increaseAllowance(address _spender, uint256 _addedValue)\n external\n returns (bool);\n\n function initialize(\n string memory _nameArg,\n string memory _symbolArg,\n address _vaultAddress\n ) external;\n\n function isGovernor() external view returns (bool);\n\n function isUpgraded(address) external view returns (uint256);\n\n function mint(address _account, uint256 _amount) external;\n\n function name() external view returns (string memory);\n\n function nonRebasingCreditsPerToken(address)\n external\n view\n returns (uint256);\n\n function nonRebasingSupply() external view returns (uint256);\n\n function rebaseOptIn() external;\n\n function rebaseOptOut() external;\n\n function rebaseState(address) external view returns (uint8);\n\n function rebasingCredits() external view returns (uint256);\n\n function rebasingCreditsHighres() external view returns (uint256);\n\n function rebasingCreditsPerToken() external view returns (uint256);\n\n function rebasingCreditsPerTokenHighres() external view returns (uint256);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function transferGovernance(address _newGovernor) external;\n\n function vaultAddress() external view returns (address);\n}\n" + }, + "contracts/interfaces/IPriceOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPriceOracle {\n /**\n * @dev returns the asset price in USD, 6 decimal digits.\n * Compatible with the Open Price Feed.\n */\n function price(string calldata symbol) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IRETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IRETH {\n function getEthValue(uint256 _rethAmount) external view returns (uint256);\n\n function getRethValue(uint256 _ethAmount) external view returns (uint256);\n\n function getExchangeRate() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function balanceOf(address account) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 amount) external returns (bool);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/ISafe.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ISafe {\n function execTransactionFromModule(\n address,\n uint256,\n bytes memory,\n uint8\n ) external returns (bool);\n}\n" + }, + "contracts/interfaces/ISfrxETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface ISfrxETH {\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 amount\n );\n event Deposit(\n address indexed caller,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n event NewRewardsCycle(uint32 indexed cycleEnd, uint256 rewardAmount);\n event Transfer(address indexed from, address indexed to, uint256 amount);\n event Withdraw(\n address indexed caller,\n address indexed receiver,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address spender, uint256 amount) external returns (bool);\n\n function asset() external view returns (address);\n\n function balanceOf(address) external view returns (uint256);\n\n function convertToAssets(uint256 shares) external view returns (uint256);\n\n function convertToShares(uint256 assets) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit(uint256 assets, address receiver)\n external\n returns (uint256 shares);\n\n function depositWithSignature(\n uint256 assets,\n address receiver,\n uint256 deadline,\n bool approveMax,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external returns (uint256 shares);\n\n function lastRewardAmount() external view returns (uint192);\n\n function lastSync() external view returns (uint32);\n\n function maxDeposit(address) external view returns (uint256);\n\n function maxMint(address) external view returns (uint256);\n\n function maxRedeem(address owner) external view returns (uint256);\n\n function maxWithdraw(address owner) external view returns (uint256);\n\n function mint(uint256 shares, address receiver)\n external\n returns (uint256 assets);\n\n function name() external view returns (string memory);\n\n function nonces(address) external view returns (uint256);\n\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function previewDeposit(uint256 assets) external view returns (uint256);\n\n function previewMint(uint256 shares) external view returns (uint256);\n\n function previewRedeem(uint256 shares) external view returns (uint256);\n\n function previewWithdraw(uint256 assets) external view returns (uint256);\n\n function pricePerShare() external view returns (uint256);\n\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets);\n\n function rewardsCycleEnd() external view returns (uint32);\n\n function rewardsCycleLength() external view returns (uint32);\n\n function symbol() external view returns (string memory);\n\n function syncRewards() external;\n\n function totalAssets() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address to, uint256 amount) external returns (bool);\n\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/ISwapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ISwapper {\n /**\n * @param fromAsset The token address of the asset being sold.\n * @param toAsset The token address of the asset being purchased.\n * @param fromAssetAmount The amount of assets being sold.\n * @param minToAssetAmmount The minimum amount of assets to be purchased.\n * @param data tx.data returned from 1Inch's /v5.0/1/swap API\n */\n function swap(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n}\n" + }, + "contracts/interfaces/ITimelock.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ITimelock {\n function delay() external view returns (uint256);\n\n function GRACE_PERIOD() external view returns (uint256);\n\n function acceptAdmin() external;\n\n function queuedTransactions(bytes32 hash) external view returns (bool);\n\n function queueTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external returns (bytes32);\n\n function cancelTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external;\n\n function executeTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external payable returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ITimelockController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ITimelockController {\n function grantRole(bytes32 role, address account) external;\n\n function revokeRole(bytes32 role, address account) external;\n\n function renounceRole(bytes32 role, address account) external;\n\n function hasRole(bytes32 role, address account)\n external\n view\n returns (bool);\n\n function executeBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt\n ) external payable;\n\n function scheduleBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt,\n uint256 delay\n ) external;\n\n function hashOperationBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt\n ) external view returns (bytes32);\n\n function isOperationDone(bytes32 opHash) external view returns (bool);\n\n function isOperationReady(bytes32 opHash) external view returns (bool);\n\n function isOperation(bytes32 opHash) external view returns (bool);\n\n function getMinDelay() external view returns (uint256);\n\n function updateDelay(uint256 newDelay) external;\n\n function CANCELLER_ROLE() external view returns (bytes32);\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/interfaces/IWstETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWstETH {\n /**\n * @notice Get amount of wstETH for a given amount of stETH\n * @param _stETHAmount amount of stETH\n * @return Amount of wstETH for a given stETH amount\n */\n function getWstETHByStETH(uint256 _stETHAmount)\n external\n view\n returns (uint256);\n\n /**\n * @notice Get amount of stETH for a given amount of wstETH\n * @param _wstETHAmount amount of wstETH\n * @return Amount of stETH for a given wstETH amount\n */\n function getStETHByWstETH(uint256 _wstETHAmount)\n external\n view\n returns (uint256);\n\n /**\n * @notice Get amount of stETH for a one wstETH\n * @return Amount of stETH for 1 wstETH\n */\n function stEthPerToken() external view returns (uint256);\n\n /**\n * @notice Get amount of wstETH for a one stETH\n * @return Amount of wstETH for a 1 stETH\n */\n function tokensPerStEth() external view returns (uint256);\n\n /**\n * @notice Exchanges stETH to wstETH\n * @param _stETHAmount amount of stETH to wrap in exchange for wstETH\n * @dev Requirements:\n * - `_stETHAmount` must be non-zero\n * - msg.sender must approve at least `_stETHAmount` stETH to this\n * contract.\n * - msg.sender must have at least `_stETHAmount` of stETH.\n * User should first approve _stETHAmount to the WstETH contract\n * @return Amount of wstETH user receives after wrap\n */\n function wrap(uint256 _stETHAmount) external returns (uint256);\n\n /**\n * @notice Exchanges wstETH to stETH\n * @param _wstETHAmount amount of wstETH to uwrap in exchange for stETH\n * @dev Requirements:\n * - `_wstETHAmount` must be non-zero\n * - msg.sender must have at least `_wstETHAmount` wstETH.\n * @return Amount of stETH user receives after unwrap\n */\n function unwrap(uint256 _wstETHAmount) external returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/compound/ICompoundOracle.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\ninterface ICompoundOracle {\n function getUnderlyingPrice(address) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/ILens.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\nimport \"./compound/ICompoundOracle.sol\";\nimport \"./IMorpho.sol\";\n\ninterface ILens {\n /// STORAGE ///\n\n function MAX_BASIS_POINTS() external view returns (uint256);\n\n function WAD() external view returns (uint256);\n\n function morpho() external view returns (IMorpho);\n\n function comptroller() external view returns (IComptroller);\n\n /// GENERAL ///\n\n function getTotalSupply()\n external\n view\n returns (\n uint256 p2pSupplyAmount,\n uint256 poolSupplyAmount,\n uint256 totalSupplyAmount\n );\n\n function getTotalBorrow()\n external\n view\n returns (\n uint256 p2pBorrowAmount,\n uint256 poolBorrowAmount,\n uint256 totalBorrowAmount\n );\n\n /// MARKETS ///\n\n function isMarketCreated(address _poolToken) external view returns (bool);\n\n function isMarketCreatedAndNotPaused(address _poolToken)\n external\n view\n returns (bool);\n\n function isMarketCreatedAndNotPausedNorPartiallyPaused(address _poolToken)\n external\n view\n returns (bool);\n\n function getAllMarkets()\n external\n view\n returns (address[] memory marketsCreated_);\n\n function getMainMarketData(address _poolToken)\n external\n view\n returns (\n uint256 avgSupplyRatePerBlock,\n uint256 avgBorrowRatePerBlock,\n uint256 p2pSupplyAmount,\n uint256 p2pBorrowAmount,\n uint256 poolSupplyAmount,\n uint256 poolBorrowAmount\n );\n\n function getAdvancedMarketData(address _poolToken)\n external\n view\n returns (\n uint256 p2pSupplyIndex,\n uint256 p2pBorrowIndex,\n uint256 poolSupplyIndex,\n uint256 poolBorrowIndex,\n uint32 lastUpdateBlockNumber,\n uint256 p2pSupplyDelta,\n uint256 p2pBorrowDelta\n );\n\n function getMarketConfiguration(address _poolToken)\n external\n view\n returns (\n address underlying,\n bool isCreated,\n bool p2pDisabled,\n bool isPaused,\n bool isPartiallyPaused,\n uint16 reserveFactor,\n uint16 p2pIndexCursor,\n uint256 collateralFactor\n );\n\n function getTotalMarketSupply(address _poolToken)\n external\n view\n returns (uint256 p2pSupplyAmount, uint256 poolSupplyAmount);\n\n function getTotalMarketBorrow(address _poolToken)\n external\n view\n returns (uint256 p2pBorrowAmount, uint256 poolBorrowAmount);\n\n /// INDEXES ///\n\n function getCurrentP2PSupplyIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentP2PBorrowIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentPoolIndexes(address _poolToken)\n external\n view\n returns (\n uint256 currentPoolSupplyIndex,\n uint256 currentPoolBorrowIndex\n );\n\n function getIndexes(address _poolToken, bool _computeUpdatedIndexes)\n external\n view\n returns (\n uint256 p2pSupplyIndex,\n uint256 p2pBorrowIndex,\n uint256 poolSupplyIndex,\n uint256 poolBorrowIndex\n );\n\n /// USERS ///\n\n function getEnteredMarkets(address _user)\n external\n view\n returns (address[] memory enteredMarkets);\n\n function getUserHealthFactor(\n address _user,\n address[] calldata _updatedMarkets\n ) external view returns (uint256);\n\n function getUserBalanceStates(\n address _user,\n address[] calldata _updatedMarkets\n )\n external\n view\n returns (\n uint256 collateralValue,\n uint256 debtValue,\n uint256 maxDebtValue\n );\n\n function getCurrentSupplyBalanceInOf(address _poolToken, address _user)\n external\n view\n returns (\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getCurrentBorrowBalanceInOf(address _poolToken, address _user)\n external\n view\n returns (\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getUserMaxCapacitiesForAsset(address _user, address _poolToken)\n external\n view\n returns (uint256 withdrawable, uint256 borrowable);\n\n function getUserHypotheticalBalanceStates(\n address _user,\n address _poolToken,\n uint256 _withdrawnAmount,\n uint256 _borrowedAmount\n ) external view returns (uint256 debtValue, uint256 maxDebtValue);\n\n function getUserLiquidityDataForAsset(\n address _user,\n address _poolToken,\n bool _computeUpdatedIndexes,\n ICompoundOracle _oracle\n ) external view returns (Types.AssetLiquidityData memory assetData);\n\n function isLiquidatable(address _user, address[] memory _updatedMarkets)\n external\n view\n returns (bool);\n\n function computeLiquidationRepayAmount(\n address _user,\n address _poolTokenBorrowed,\n address _poolTokenCollateral,\n address[] calldata _updatedMarkets\n ) external view returns (uint256 toRepay);\n\n /// RATES ///\n\n function getAverageSupplyRatePerBlock(address _poolToken)\n external\n view\n returns (\n uint256 avgSupplyRatePerBlock,\n uint256 p2pSupplyAmount,\n uint256 poolSupplyAmount\n );\n\n function getAverageBorrowRatePerBlock(address _poolToken)\n external\n view\n returns (\n uint256 avgBorrowRatePerBlock,\n uint256 p2pBorrowAmount,\n uint256 poolBorrowAmount\n );\n\n function getNextUserSupplyRatePerBlock(\n address _poolToken,\n address _user,\n uint256 _amount\n )\n external\n view\n returns (\n uint256 nextSupplyRatePerBlock,\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getNextUserBorrowRatePerBlock(\n address _poolToken,\n address _user,\n uint256 _amount\n )\n external\n view\n returns (\n uint256 nextBorrowRatePerBlock,\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getCurrentUserSupplyRatePerBlock(address _poolToken, address _user)\n external\n view\n returns (uint256);\n\n function getCurrentUserBorrowRatePerBlock(address _poolToken, address _user)\n external\n view\n returns (uint256);\n\n function getRatesPerBlock(address _poolToken)\n external\n view\n returns (\n uint256 p2pSupplyRate,\n uint256 p2pBorrowRate,\n uint256 poolSupplyRate,\n uint256 poolBorrowRate\n );\n\n /// REWARDS ///\n\n function getUserUnclaimedRewards(\n address[] calldata _poolTokens,\n address _user\n ) external view returns (uint256 unclaimedRewards);\n\n function getAccruedSupplierComp(\n address _supplier,\n address _poolToken,\n uint256 _balance\n ) external view returns (uint256);\n\n function getAccruedBorrowerComp(\n address _borrower,\n address _poolToken,\n uint256 _balance\n ) external view returns (uint256);\n\n function getCurrentCompSupplyIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentCompBorrowIndex(address _poolToken)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/IMorpho.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\nimport \"./Types.sol\";\nimport \"../IComptroller.sol\";\nimport \"./compound/ICompoundOracle.sol\";\n\n// prettier-ignore\ninterface IMorpho {\n function comptroller() external view returns (IComptroller);\n function supply(address _poolTokenAddress, address _onBehalf, uint256 _amount) external;\n function supply(address _poolTokenAddress, address _onBehalf, uint256 _amount, uint256 _maxGasForMatching) external;\n function withdraw(address _poolTokenAddress, uint256 _amount) external;\n function claimRewards(\n address[] calldata _cTokenAddresses,\n bool _tradeForMorphoToken\n ) external returns (uint256 claimedAmount);\n}\n" + }, + "contracts/interfaces/morpho/Types.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\n/// @title Types.\n/// @author Morpho Labs.\n/// @custom:contact security@morpho.xyz\n/// @dev Common types and structs used in Moprho contracts.\nlibrary Types {\n /// ENUMS ///\n\n enum PositionType {\n SUPPLIERS_IN_P2P,\n SUPPLIERS_ON_POOL,\n BORROWERS_IN_P2P,\n BORROWERS_ON_POOL\n }\n\n /// STRUCTS ///\n\n struct SupplyBalance {\n uint256 inP2P; // In supplier's peer-to-peer unit, a unit that grows in underlying value, to keep track of the interests earned by suppliers in peer-to-peer. Multiply by the peer-to-peer supply index to get the underlying amount.\n uint256 onPool; // In cToken. Multiply by the pool supply index to get the underlying amount.\n }\n\n struct BorrowBalance {\n uint256 inP2P; // In borrower's peer-to-peer unit, a unit that grows in underlying value, to keep track of the interests paid by borrowers in peer-to-peer. Multiply by the peer-to-peer borrow index to get the underlying amount.\n uint256 onPool; // In cdUnit, a unit that grows in value, to keep track of the debt increase when borrowers are on Compound. Multiply by the pool borrow index to get the underlying amount.\n }\n\n // Max gas to consume during the matching process for supply, borrow, withdraw and repay functions.\n struct MaxGasForMatching {\n uint64 supply;\n uint64 borrow;\n uint64 withdraw;\n uint64 repay;\n }\n\n struct Delta {\n uint256 p2pSupplyDelta; // Difference between the stored peer-to-peer supply amount and the real peer-to-peer supply amount (in pool supply unit).\n uint256 p2pBorrowDelta; // Difference between the stored peer-to-peer borrow amount and the real peer-to-peer borrow amount (in pool borrow unit).\n uint256 p2pSupplyAmount; // Sum of all stored peer-to-peer supply (in peer-to-peer supply unit).\n uint256 p2pBorrowAmount; // Sum of all stored peer-to-peer borrow (in peer-to-peer borrow unit).\n }\n\n struct AssetLiquidityData {\n uint256 collateralValue; // The collateral value of the asset.\n uint256 maxDebtValue; // The maximum possible debt value of the asset.\n uint256 debtValue; // The debt value of the asset.\n uint256 underlyingPrice; // The price of the token.\n uint256 collateralFactor; // The liquidation threshold applied on this token.\n }\n\n struct LiquidityData {\n uint256 collateralValue; // The collateral value.\n uint256 maxDebtValue; // The maximum debt value possible.\n uint256 debtValue; // The debt value.\n }\n\n // Variables are packed together to save gas (will not exceed their limit during Morpho's lifetime).\n struct LastPoolIndexes {\n uint32 lastUpdateBlockNumber; // The last time the peer-to-peer indexes were updated.\n uint112 lastSupplyPoolIndex; // Last pool supply index.\n uint112 lastBorrowPoolIndex; // Last pool borrow index.\n }\n\n struct MarketParameters {\n uint16 reserveFactor; // Proportion of the interest earned by users sent to the DAO for each market, in basis point (100% = 10 000). The value is set at market creation.\n uint16 p2pIndexCursor; // Position of the peer-to-peer rate in the pool's spread. Determine the weights of the weighted arithmetic average in the indexes computations ((1 - p2pIndexCursor) * r^S + p2pIndexCursor * r^B) (in basis point).\n }\n\n struct MarketStatus {\n bool isCreated; // Whether or not this market is created.\n bool isPaused; // Whether the market is paused or not (all entry points on Morpho are frozen; supply, borrow, withdraw, repay and liquidate).\n bool isPartiallyPaused; // Whether the market is partially paused or not (only supply and borrow are frozen).\n }\n}\n" + }, + "contracts/interfaces/plume/IFeeRegistry.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\ninterface IFeeRegistry {\n function registerFee(\n bool isTokenA,\n uint32 binId,\n uint256 binFeeInQuote\n ) external;\n}\n" + }, + "contracts/interfaces/plume/ILiquidityRegistry.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface ILiquidityRegistry {\n function notifyBinLiquidity(\n IMaverickV2Pool pool,\n uint256 tokenId,\n uint32 binId,\n uint256 currentBinLpBalance\n ) external;\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Factory.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IFeeRegistry } from \"./IFeeRegistry.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2Factory {\n error FactorAlreadyInitialized();\n error FactorNotInitialized();\n error FactoryInvalidTokenOrder(IERC20 _tokenA, IERC20 _tokenB);\n error FactoryInvalidFee();\n error FactoryInvalidKinds(uint8 kinds);\n error FactoryInvalidTickSpacing(uint256 tickSpacing);\n error FactoryInvalidLookback(uint256 lookback);\n error FactoryInvalidTokenDecimals(uint8 decimalsA, uint8 decimalsB);\n error FactoryPoolAlreadyExists(\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds,\n address accessor\n );\n error FactoryAccessorMustBeNonZero();\n error NotImplemented();\n\n event PoolCreated(\n IMaverickV2Pool poolAddress,\n uint8 protocolFeeRatio,\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n int32 activeTick,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds,\n address accessor\n );\n event SetFactoryProtocolFeeReceiver(address receiver);\n event SetFactoryProtocolFeeRegistry(IFeeRegistry registry);\n\n struct DeployParameters {\n uint64 feeAIn;\n uint64 feeBIn;\n uint32 lookback;\n int32 activeTick;\n uint64 tokenAScale;\n uint64 tokenBScale;\n // slot\n IERC20 tokenA;\n // slot\n IERC20 tokenB;\n // slot\n uint16 tickSpacing;\n uint8 options;\n address accessor;\n }\n\n /**\n * @notice Called by deployer library to initialize a pool.\n */\n function deployParameters()\n external\n view\n returns (\n uint64 feeAIn,\n uint64 feeBIn,\n uint32 lookback,\n int32 activeTick,\n uint64 tokenAScale,\n uint64 tokenBScale,\n // slot\n IERC20 tokenA,\n // slot\n IERC20 tokenB,\n // slot\n uint16 tickSpacing,\n uint8 options,\n address accessor\n );\n\n /**\n * @notice Create a new MaverickV2Pool with symmetric swap fees.\n * @param fee Fraction of the pool swap amount that is retained as an LP in\n * D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * E.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function create(\n uint64 fee,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external returns (IMaverickV2Pool);\n\n /**\n * @notice Create a new MaverickV2Pool.\n * @param feeAIn Fraction of the pool swap amount for tokenA-input swaps\n * that is retained as an LP in D18 scale.\n * @param feeBIn Fraction of the pool swap amount for tokenB-input swaps\n * that is retained as an LP in D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * e.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function create(\n uint64 feeAIn,\n uint64 feeBIn,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external returns (IMaverickV2Pool);\n\n /**\n * @notice Bool indicating whether the pool was deployed from this factory.\n */\n function isFactoryPool(IMaverickV2Pool pool) external view returns (bool);\n\n /**\n * @notice Address that receives the protocol fee\n */\n function protocolFeeReceiver() external view returns (address);\n\n /**\n * @notice Address notified on swaps of the protocol fee\n */\n function protocolFeeRegistry() external view returns (IFeeRegistry);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds\n ) external view returns (IMaverickV2Pool);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(\n IERC20 _tokenA,\n IERC20 _tokenB,\n uint256 startIndex,\n uint256 endIndex\n ) external view returns (IMaverickV2Pool[] memory pools);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(uint256 startIndex, uint256 endIndex)\n external\n view\n returns (IMaverickV2Pool[] memory pools);\n\n /**\n * @notice Count of permissionless pools.\n */\n function poolCount() external view returns (uint256 _poolCount);\n\n /**\n * @notice Count of pools for a given accessor and token pair. For\n * permissionless pools, pass `accessor = address(0)`.\n */\n function poolByTokenCount(\n IERC20 _tokenA,\n IERC20 _tokenB,\n address accessor\n ) external view returns (uint256 _poolCount);\n\n /**\n * @notice Get the current factory owner.\n */\n function owner() external view returns (address);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2LiquidityManager.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\nimport { IMaverickV2Position } from \"./IMaverickV2Position.sol\";\nimport { IMaverickV2PoolLens } from \"./IMaverickV2PoolLens.sol\";\n\ninterface IMaverickV2LiquidityManager {\n error LiquidityManagerNotFactoryPool();\n error LiquidityManagerNotTokenIdOwner();\n\n /**\n * @notice Maverick V2 NFT position contract that tracks NFT-based\n * liquditiy positions.\n */\n function position() external view returns (IMaverickV2Position);\n\n /**\n * @notice Create Maverick V2 pool. Function is a pass through to the pool\n * factory and is provided here so that is can be assembled as part of a\n * multicall transaction.\n */\n function createPool(\n uint64 fee,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external payable returns (IMaverickV2Pool pool);\n\n /**\n * @notice Add Liquidity position NFT for msg.sender by specifying\n * msg.sender's token index.\n * @dev Token index is different from tokenId.\n * On the Position NFT contract a user can own multiple NFT tokenIds and\n * these are indexes by an enumeration index which is the `index` input\n * here.\n *\n * See addLiquidity for a description of the add params.\n */\n function addPositionLiquidityToSenderByTokenIndex(\n IMaverickV2Pool pool,\n uint256 index,\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs\n )\n external\n payable\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds\n );\n\n /**\n * @notice Mint new tokenId in the Position NFt contract to msg.sender.\n * Both mints an NFT and adds liquidity to the pool that is held by the\n * NFT.\n */\n function mintPositionNftToSender(\n IMaverickV2Pool pool,\n bytes calldata packedSqrtPriceBreaks,\n bytes[] calldata packedArgs\n )\n external\n payable\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds,\n uint256 tokenId\n );\n\n /**\n * @notice Donates liqudity to a pool that is held by the position contract\n * and will never be retrievable. Can be used to start a pool and ensure\n * there will always be a base level of liquditiy in the pool.\n */\n function donateLiquidity(\n IMaverickV2Pool pool,\n IMaverickV2Pool.AddLiquidityParams memory args\n ) external payable;\n\n /**\n * @notice Packs sqrtPrice breaks array with this format: [length,\n * array[0], array[1],..., array[length-1]] where length is 1 byte.\n */\n function packUint88Array(uint88[] memory fullArray)\n external\n pure\n returns (bytes memory packedArray);\n\n /**\n * @notice Packs addLiquidity paramters array element-wise.\n */\n function packAddLiquidityArgsArray(\n IMaverickV2Pool.AddLiquidityParams[] memory args\n ) external pure returns (bytes[] memory argsPacked);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Pool.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\n\ninterface IMaverickV2Pool {\n error PoolZeroLiquidityAdded();\n error PoolMinimumLiquidityNotMet();\n error PoolLocked();\n error PoolInvalidFee();\n error PoolTicksNotSorted(uint256 index, int256 previousTick, int256 tick);\n error PoolTicksAmountsLengthMismatch(\n uint256 ticksLength,\n uint256 amountsLength\n );\n error PoolBinIdsAmountsLengthMismatch(\n uint256 binIdsLength,\n uint256 amountsLength\n );\n error PoolKindNotSupported(uint256 kinds, uint256 kind);\n error PoolInsufficientBalance(\n uint256 deltaLpAmount,\n uint256 accountBalance\n );\n error PoolReservesExceedMaximum(uint256 amount);\n error PoolValueExceedsBits(uint256 amount, uint256 bits);\n error PoolTickMaxExceeded(uint256 tick);\n error PoolMigrateBinFirst();\n error PoolCurrentTickBeyondSwapLimit(int32 startingTick);\n error PoolSenderNotAccessor(address sender_, address accessor);\n error PoolSenderNotFactory(address sender_, address accessor);\n error PoolFunctionNotImplemented();\n error PoolTokenNotSolvent(\n uint256 internalReserve,\n uint256 tokenBalance,\n IERC20 token\n );\n error PoolNoProtocolFeeReceiverSet();\n\n event PoolSwap(\n address sender,\n address recipient,\n SwapParams params,\n uint256 amountIn,\n uint256 amountOut\n );\n event PoolFlashLoan(\n address sender,\n address recipient,\n uint256 amountA,\n uint256 amountB\n );\n event PoolProtocolFeeCollected(IERC20 token, uint256 protocolFee);\n\n event PoolAddLiquidity(\n address sender,\n address recipient,\n uint256 subaccount,\n AddLiquidityParams params,\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] binIds\n );\n\n event PoolMigrateBinsUpStack(\n address sender,\n uint32 binId,\n uint32 maxRecursion\n );\n\n event PoolRemoveLiquidity(\n address sender,\n address recipient,\n uint256 subaccount,\n RemoveLiquidityParams params,\n uint256 tokenAOut,\n uint256 tokenBOut\n );\n\n event PoolTickState(int32 tick, uint256 reserveA, uint256 reserveB);\n event PoolTickBinUpdate(int32 tick, uint8 kind, uint32 binId);\n event PoolSqrtPrice(uint256 sqrtPrice);\n\n /**\n * @notice Tick state parameters.\n */\n struct TickState {\n uint128 reserveA;\n uint128 reserveB;\n uint128 totalSupply;\n uint32[4] binIdsByTick;\n }\n\n /**\n * @notice Tick data parameters.\n * @param currentReserveA Current reserve of token A.\n * @param currentReserveB Current reserve of token B.\n * @param currentLiquidity Current liquidity amount.\n */\n struct TickData {\n uint256 currentReserveA;\n uint256 currentReserveB;\n uint256 currentLiquidity;\n }\n\n /**\n * @notice Bin state parameters.\n * @param mergeBinBalance LP token balance that this bin possesses of the merge bin.\n * @param mergeId Bin ID of the bin that this bin has merged into.\n * @param totalSupply Total amount of LP tokens in this bin.\n * @param kind One of the 4 kinds (0=static, 1=right, 2=left, 3=both).\n * @param tick The lower price tick of the bin in its current state.\n * @param tickBalance Balance of the tick.\n */\n struct BinState {\n uint128 mergeBinBalance;\n uint128 tickBalance;\n uint128 totalSupply;\n uint8 kind;\n int32 tick;\n uint32 mergeId;\n }\n\n /**\n * @notice Parameters for swap.\n * @param amount Amount of the token that is either the input if exactOutput is false\n * or the output if exactOutput is true.\n * @param tokenAIn Boolean indicating whether tokenA is the input.\n * @param exactOutput Boolean indicating whether the amount specified is\n * the exact output amount (true).\n * @param tickLimit The furthest tick a swap will execute in. If no limit\n * is desired, value should be set to type(int32).max for a tokenAIn swap\n * and type(int32).min for a swap where tokenB is the input.\n */\n struct SwapParams {\n uint256 amount;\n bool tokenAIn;\n bool exactOutput;\n int32 tickLimit;\n }\n\n /**\n * @notice Parameters associated with adding liquidity.\n * @param kind One of the 4 kinds (0=static, 1=right, 2=left, 3=both).\n * @param ticks Array of ticks to add liquidity to.\n * @param amounts Array of bin LP amounts to add.\n */\n struct AddLiquidityParams {\n uint8 kind;\n int32[] ticks;\n uint128[] amounts;\n }\n\n /**\n * @notice Parameters for each bin that will have liquidity removed.\n * @param binIds Index array of the bins losing liquidity.\n * @param amounts Array of bin LP amounts to remove.\n */\n struct RemoveLiquidityParams {\n uint32[] binIds;\n uint128[] amounts;\n }\n\n /**\n * @notice State of the pool.\n * @param reserveA Pool tokenA balanceOf at end of last operation\n * @param reserveB Pool tokenB balanceOf at end of last operation\n * @param lastTwaD8 Value of log time weighted average price at last block.\n * Value is 8-decimal scale and is in the fractional tick domain. E.g. a\n * value of 12.3e8 indicates the TWAP was 3/10ths of the way into the 12th\n * tick.\n * @param lastLogPriceD8 Value of log price at last block. Value is\n * 8-decimal scale and is in the fractional tick domain. E.g. a value of\n * 12.3e8 indicates the price was 3/10ths of the way into the 12th tick.\n * @param lastTimestamp Last block.timestamp value in seconds for latest\n * swap transaction.\n * @param activeTick Current tick position that contains the active bins.\n * @param isLocked Pool isLocked, E.g., locked or unlocked; isLocked values\n * defined in Pool.sol.\n * @param binCounter Index of the last bin created.\n * @param protocolFeeRatioD3 Ratio of the swap fee that is kept for the\n * protocol.\n */\n struct State {\n uint128 reserveA;\n uint128 reserveB;\n int64 lastTwaD8;\n int64 lastLogPriceD8;\n uint40 lastTimestamp;\n int32 activeTick;\n bool isLocked;\n uint32 binCounter;\n uint8 protocolFeeRatioD3;\n }\n\n /**\n * @notice Internal data used for data passing between Pool and Bin code.\n */\n struct BinDelta {\n uint128 deltaA;\n uint128 deltaB;\n }\n\n /**\n * @notice 1-15 number to represent the active kinds.\n * @notice 0b0001 = static;\n * @notice 0b0010 = right;\n * @notice 0b0100 = left;\n * @notice 0b1000 = both;\n *\n * E.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function kinds() external view returns (uint8 _kinds);\n\n /**\n * @notice Pool swap fee for the given direction (A-in or B-in swap) in\n * 18-decimal format. E.g. 0.01e18 is a 1% swap fee.\n */\n function fee(bool tokenAIn) external view returns (uint256);\n\n /**\n * @notice TickSpacing of pool where 1.0001^tickSpacing is the bin width.\n */\n function tickSpacing() external view returns (uint256);\n\n /**\n * @notice Lookback period of pool in seconds.\n */\n function lookback() external view returns (uint256);\n\n /**\n * @notice Address of Pool accessor. This is Zero address for\n * permissionless pools.\n */\n function accessor() external view returns (address);\n\n /**\n * @notice Pool tokenA. Address of tokenA is such that tokenA < tokenB.\n */\n function tokenA() external view returns (IERC20);\n\n /**\n * @notice Pool tokenB.\n */\n function tokenB() external view returns (IERC20);\n\n /**\n * @notice Deploying factory of the pool and also contract that has ability\n * to set and collect protocol fees for the pool.\n */\n function factory() external view returns (IMaverickV2Factory);\n\n /**\n * @notice Most significant bit of scale value is a flag to indicate whether\n * tokenA has more or less than 18 decimals. Scale is used in conjuction\n * with Math.toScale/Math.fromScale functions to convert from token amounts\n * to D18 scale internal pool accounting.\n */\n function tokenAScale() external view returns (uint256);\n\n /**\n * @notice Most significant bit of scale value is a flag to indicate whether\n * tokenA has more or less than 18 decimals. Scale is used in conjuction\n * with Math.toScale/Math.fromScale functions to convert from token amounts\n * to D18 scale internal pool accounting.\n */\n function tokenBScale() external view returns (uint256);\n\n /**\n * @notice ID of bin at input tick position and kind.\n */\n function binIdByTickKind(int32 tick, uint256 kind)\n external\n view\n returns (uint32);\n\n /**\n * @notice Accumulated tokenA protocol fee.\n */\n function protocolFeeA() external view returns (uint128);\n\n /**\n * @notice Accumulated tokenB protocol fee.\n */\n function protocolFeeB() external view returns (uint128);\n\n /**\n * @notice Lending fee rate on flash loans.\n */\n function lendingFeeRateD18() external view returns (uint256);\n\n /**\n * @notice External function to get the current time-weighted average price.\n */\n function getCurrentTwa() external view returns (int256);\n\n /**\n * @notice External function to get the state of the pool.\n */\n function getState() external view returns (State memory);\n\n /**\n * @notice Return state of Bin at input binId.\n */\n function getBin(uint32 binId) external view returns (BinState memory bin);\n\n /**\n * @notice Return state of Tick at input tick position.\n */\n function getTick(int32 tick)\n external\n view\n returns (TickState memory tickState);\n\n /**\n * @notice Retrieves the balance of a user within a bin.\n * @param user The user's address.\n * @param subaccount The subaccount for the user.\n * @param binId The ID of the bin.\n */\n function balanceOf(\n address user,\n uint256 subaccount,\n uint32 binId\n ) external view returns (uint128 lpToken);\n\n /**\n * @notice Add liquidity to a pool. This function allows users to deposit\n * tokens into a liquidity pool.\n * @dev This function will call `maverickV2AddLiquidityCallback` on the\n * calling contract to collect the tokenA/tokenB payment.\n * @param recipient The account that will receive credit for the added liquidity.\n * @param subaccount The account that will receive credit for the added liquidity.\n * @param params Parameters containing the details for adding liquidity,\n * such as token types and amounts.\n * @param data Bytes information that gets passed to the callback.\n * @return tokenAAmount The amount of token A added to the pool.\n * @return tokenBAmount The amount of token B added to the pool.\n * @return binIds An array of bin IDs where the liquidity is stored.\n */\n function addLiquidity(\n address recipient,\n uint256 subaccount,\n AddLiquidityParams calldata params,\n bytes calldata data\n )\n external\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds\n );\n\n /**\n * @notice Removes liquidity from the pool.\n * @dev Liquidy can only be removed from a bin that is either unmerged or\n * has a mergeId of an unmerged bin. If a bin is merged more than one\n * level deep, it must be migrated up the merge stack to the root bin\n * before liquidity removal.\n * @param recipient The address to receive the tokens.\n * @param subaccount The subaccount for the recipient.\n * @param params The parameters for removing liquidity.\n * @return tokenAOut The amount of token A received.\n * @return tokenBOut The amount of token B received.\n */\n function removeLiquidity(\n address recipient,\n uint256 subaccount,\n RemoveLiquidityParams calldata params\n ) external returns (uint256 tokenAOut, uint256 tokenBOut);\n\n /**\n * @notice Migrate bins up the linked list of merged bins so that its\n * mergeId is the currrent active bin.\n * @dev Liquidy can only be removed from a bin that is either unmerged or\n * has a mergeId of an unmerged bin. If a bin is merged more than one\n * level deep, it must be migrated up the merge stack to the root bin\n * before liquidity removal.\n * @param binId The ID of the bin to migrate.\n * @param maxRecursion The maximum recursion depth for the migration.\n */\n function migrateBinUpStack(uint32 binId, uint32 maxRecursion) external;\n\n /**\n * @notice Swap tokenA/tokenB assets in the pool. The swap user has two\n * options for funding their swap.\n * - The user can push the input token amount to the pool before calling\n * the swap function. In order to avoid having the pool call the callback,\n * the user should pass a zero-length `data` bytes object with the swap\n * call.\n * - The user can send the input token amount to the pool when the pool\n * calls the `maverickV2SwapCallback` function on the calling contract.\n * That callback has input parameters that specify the token address of the\n * input token, the input and output amounts, and the bytes data sent to\n * the swap function.\n * @dev If the users elects to do a callback-based swap, the output\n * assets will be sent before the callback is called, allowing the user to\n * execute flash swaps. However, the pool does have reentrancy protection,\n * so a swapper will not be able to interact with the same pool again\n * while they are in the callback function.\n * @param recipient The address to receive the output tokens.\n * @param params Parameters containing the details of the swap\n * @param data Bytes information that gets passed to the callback.\n */\n function swap(\n address recipient,\n SwapParams memory params,\n bytes calldata data\n ) external returns (uint256 amountIn, uint256 amountOut);\n\n /**\n * @notice Loan tokenA/tokenB assets from the pool to recipient. The fee\n * rate of a loan is determined by `lendingFeeRateD18`, which is set at the\n * protocol level by the factory. This function calls\n * `maverickV2FlashLoanCallback` on the calling contract. At the end of\n * the callback, the caller must pay back the loan with fee (if there is a\n * fee).\n * @param recipient The address to receive the loaned tokens.\n * @param amountB Loan amount of tokenA sent to recipient.\n * @param amountB Loan amount of tokenB sent to recipient.\n * @param data Bytes information that gets passed to the callback.\n */\n function flashLoan(\n address recipient,\n uint256 amountA,\n uint256 amountB,\n bytes calldata data\n ) external;\n\n /**\n * @notice Distributes accumulated protocol fee to factory protocolFeeReceiver\n */\n function distributeFees(bool isTokenA)\n external\n returns (uint256 protocolFee, IERC20 token);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2PoolLens.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2PoolLens {\n error LensTargetPriceOutOfBounds(\n uint256 targetSqrtPrice,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n );\n error LensTooLittleLiquidity(\n uint256 relativeLiquidityAmount,\n uint256 deltaA,\n uint256 deltaB\n );\n error LensTargetingTokenWithNoDelta(\n bool targetIsA,\n uint256 deltaA,\n uint256 deltaB\n );\n\n /**\n * @notice Add liquidity slippage parameters for a distribution of liquidity.\n * @param pool Pool where liquidity is being added.\n * @param kind Bin kind; all bins must have the same kind in a given call\n * to addLiquidity.\n * @param ticks Array of tick values to add liquidity to.\n * @param relativeLiquidityAmounts Relative liquidity amounts for the\n * specified ticks. Liquidity in this case is not bin LP balance, it is\n * the bin liquidity as defined by liquidity = deltaA / (sqrt(upper) -\n * sqrt(lower)) or deltaB = liquidity / sqrt(lower) - liquidity /\n * sqrt(upper).\n * @param addSpec Slippage specification.\n */\n struct AddParamsViewInputs {\n IMaverickV2Pool pool;\n uint8 kind;\n int32[] ticks;\n uint128[] relativeLiquidityAmounts;\n AddParamsSpecification addSpec;\n }\n\n /**\n * @notice Multi-price add param specification.\n * @param slippageFactorD18 Max slippage allowed as a percent in D18 scale. e.g. 1% slippage is 0.01e18\n * @param numberOfPriceBreaksPerSide Number of price break values on either\n * side of current price.\n * @param targetAmount Target token contribution amount in tokenA if\n * targetIsA is true, otherwise this is the target amount for tokenB.\n * @param targetIsA Indicates if the target amount is for tokenA or tokenB\n */\n struct AddParamsSpecification {\n uint256 slippageFactorD18;\n uint256 numberOfPriceBreaksPerSide;\n uint256 targetAmount;\n bool targetIsA;\n }\n\n /**\n * @notice Specification for deriving create pool parameters. Creating a\n * pool in the liquidity manager has several steps:\n *\n * - Deploy pool\n * - Donate a small amount of initial liquidity in the activeTick\n * - Execute a small swap to set the pool price to the desired value\n * - Add liquidity\n *\n * In order to execute these steps, the caller must specify the parameters\n * of each step. The PoolLens has helper function to derive the values\n * used by the LiquidityManager, but this struct is the input to that\n * helper function and represents the core intent of the pool creator.\n *\n * @param fee Fraction of the pool swap amount that is retained as an LP in\n * D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * e.g. a pool with all 4 modes will have kinds = b1111 = 15\n * @param initialTargetB Amount of B to be donated to the pool after pool\n * create. This amount needs to be big enough to meet the minimum bin\n * liquidity.\n * @param sqrtPrice Target sqrt price of the pool.\n * @param kind Bin kind; all bins must have the same kind in a given call\n * to addLiquidity.\n * @param ticks Array of tick values to add liquidity to.\n * @param relativeLiquidityAmounts Relative liquidity amounts for the\n * specified ticks. Liquidity in this case is not bin LP balance, it is\n * the bin liquidity as defined by liquidity = deltaA / (sqrt(upper) -\n * sqrt(lower)) or deltaB = liquidity / sqrt(lower) - liquidity /\n * sqrt(upper).\n * @param targetAmount Target token contribution amount in tokenA if\n * targetIsA is true, otherwise this is the target amount for tokenB.\n * @param targetIsA Indicates if the target amount is for tokenA or tokenB\n */\n struct CreateAndAddParamsViewInputs {\n uint64 feeAIn;\n uint64 feeBIn;\n uint16 tickSpacing;\n uint32 lookback;\n IERC20 tokenA;\n IERC20 tokenB;\n int32 activeTick;\n uint8 kinds;\n // donate params\n uint256 initialTargetB;\n uint256 sqrtPrice;\n // add target\n uint8 kind;\n int32[] ticks;\n uint128[] relativeLiquidityAmounts;\n uint256 targetAmount;\n bool targetIsA;\n }\n\n struct Output {\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256[] deltaAs;\n uint256[] deltaBs;\n uint128[] deltaLpBalances;\n }\n\n struct Reserves {\n uint256 amountA;\n uint256 amountB;\n }\n\n struct BinPositionKinds {\n uint128[4] values;\n }\n\n struct PoolState {\n IMaverickV2Pool.TickState[] tickStateMapping;\n IMaverickV2Pool.BinState[] binStateMapping;\n BinPositionKinds[] binIdByTickKindMapping;\n IMaverickV2Pool.State state;\n Reserves protocolFees;\n }\n\n struct BoostedPositionSpecification {\n IMaverickV2Pool pool;\n uint32[] binIds;\n uint128[] ratios;\n uint8 kind;\n }\n\n struct CreateAndAddParamsInputs {\n uint64 feeAIn;\n uint64 feeBIn;\n uint16 tickSpacing;\n uint32 lookback;\n IERC20 tokenA;\n IERC20 tokenB;\n int32 activeTick;\n uint8 kinds;\n // donate params\n IMaverickV2Pool.AddLiquidityParams donateParams;\n // swap params\n uint256 swapAmount;\n // add params\n IMaverickV2Pool.AddLiquidityParams addParams;\n bytes[] packedAddParams;\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256 preAddReserveA;\n uint256 preAddReserveB;\n }\n\n struct TickDeltas {\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256[] deltaAs;\n uint256[] deltaBs;\n }\n\n /**\n * @notice Converts add parameter slippage specification into add\n * parameters. The return values are given in both raw format and as packed\n * values that can be used in the LiquidityManager contract.\n */\n function getAddLiquidityParams(AddParamsViewInputs memory params)\n external\n view\n returns (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint88[] memory sqrtPriceBreaks,\n IMaverickV2Pool.AddLiquidityParams[] memory addParams,\n IMaverickV2PoolLens.TickDeltas[] memory tickDeltas\n );\n\n /**\n * @notice Converts add parameter slippage specification and new pool\n * specification into CreateAndAddParamsInputs parameters that can be used in the\n * LiquidityManager contract.\n */\n function getCreatePoolAtPriceAndAddLiquidityParams(\n CreateAndAddParamsViewInputs memory params\n ) external view returns (CreateAndAddParamsInputs memory output);\n\n /**\n * @notice View function that provides information about pool ticks within\n * a tick radius from the activeTick. Ticks with no reserves are not\n * included in part o f the return array.\n */\n function getTicksAroundActive(IMaverickV2Pool pool, int32 tickRadius)\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates\n );\n\n /**\n * @notice View function that provides information about pool ticks within\n * a range. Ticks with no reserves are not included in part o f the return\n * array.\n */\n function getTicks(\n IMaverickV2Pool pool,\n int32 tickStart,\n int32 tickEnd\n )\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates\n );\n\n /**\n * @notice View function that provides information about pool ticks within\n * a range. Information returned includes all pool state needed to emulate\n * a swap off chain. Ticks with no reserves are not included in part o f\n * the return array.\n */\n function getTicksAroundActiveWLiquidity(\n IMaverickV2Pool pool,\n int32 tickRadius\n )\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates,\n uint256[] memory liquidities,\n uint256[] memory sqrtLowerTickPrices,\n uint256[] memory sqrtUpperTickPrices,\n IMaverickV2Pool.State memory poolState,\n uint256 sqrtPrice,\n uint256 feeAIn,\n uint256 feeBIn\n );\n\n /**\n * @notice View function that provides pool state information.\n */\n function getFullPoolState(\n IMaverickV2Pool pool,\n uint32 binStart,\n uint32 binEnd\n ) external view returns (PoolState memory poolState);\n\n /**\n * @notice View function that provides price and liquidity of a given tick.\n */\n function getTickSqrtPriceAndL(IMaverickV2Pool pool, int32 tick)\n external\n view\n returns (uint256 sqrtPrice, uint256 liquidity);\n\n /**\n * @notice Pool sqrt price.\n */\n function getPoolSqrtPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 sqrtPrice);\n\n /**\n * @notice Pool price.\n */\n function getPoolPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 price);\n\n /**\n * @notice Token scale of two tokens in a pool.\n */\n function tokenScales(IMaverickV2Pool pool)\n external\n view\n returns (uint256 tokenAScale, uint256 tokenBScale);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Position.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\nimport { ILiquidityRegistry } from \"./ILiquidityRegistry.sol\";\n\ninterface IMaverickV2Position {\n event PositionClearData(uint256 indexed tokenId);\n event PositionSetData(\n uint256 indexed tokenId,\n uint256 index,\n PositionPoolBinIds newData\n );\n event SetLpReward(ILiquidityRegistry lpReward);\n\n error PositionDuplicatePool(uint256 index, IMaverickV2Pool pool);\n error PositionNotFactoryPool();\n error PositionPermissionedLiquidityPool();\n\n struct PositionPoolBinIds {\n IMaverickV2Pool pool;\n uint32[] binIds;\n }\n\n struct PositionFullInformation {\n PositionPoolBinIds poolBinIds;\n uint256 amountA;\n uint256 amountB;\n uint256[] binAAmounts;\n uint256[] binBAmounts;\n int32[] ticks;\n uint256[] liquidities;\n }\n\n /**\n * @notice Factory that tracks lp rewards.\n */\n function lpReward() external view returns (ILiquidityRegistry);\n\n /**\n * @notice Pool factory.\n */\n function factory() external view returns (IMaverickV2Factory);\n\n /**\n * @notice Mint NFT that holds liquidity in a Maverick V2 Pool. To mint\n * liquidity to an NFT, add liquidity to bins in a pool where the\n * add liquidity recipient is this contract and the subaccount is the\n * tokenId. LiquidityManager can be used to simplify minting Position NFTs.\n */\n function mint(\n address recipient,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external returns (uint256 tokenId);\n\n /**\n * @notice Overwrites tokenId pool/binId information for a given data index.\n */\n function setTokenIdData(\n uint256 tokenId,\n uint256 index,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n\n /**\n * @notice Overwrites entire pool/binId data set for a given tokenId.\n */\n function setTokenIdData(uint256 tokenId, PositionPoolBinIds[] memory data)\n external;\n\n /**\n * @notice Append new pool/binIds data array to tokenId.\n */\n function appendTokenIdData(\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n\n /**\n * @notice Get array pool/binIds data for a given tokenId.\n */\n function getTokenIdData(uint256 tokenId)\n external\n view\n returns (PositionPoolBinIds[] memory);\n\n /**\n * @notice Get value from array of pool/binIds data for a given tokenId.\n */\n function getTokenIdData(uint256 tokenId, uint256 index)\n external\n view\n returns (PositionPoolBinIds memory);\n\n /**\n * @notice Length of array of pool/binIds data for a given tokenId.\n */\n function tokenIdDataLength(uint256 tokenId)\n external\n view\n returns (uint256 length);\n\n /**\n * @notice Remove liquidity from tokenId for a given pool. User can\n * specify arbitrary bins to remove from for their subaccount in the pool\n * even if those bins are not in the tokenIdData set.\n */\n function removeLiquidity(\n uint256 tokenId,\n address recipient,\n IMaverickV2Pool pool,\n IMaverickV2Pool.RemoveLiquidityParams memory params\n ) external returns (uint256 tokenAAmount, uint256 tokenBAmount);\n\n /**\n * @notice Remove liquidity from tokenId for a given pool to sender. User\n * can specify arbitrary bins to remove from for their subaccount in the\n * pool even if those bins are not in the tokenIdData set.\n */\n function removeLiquidityToSender(\n uint256 tokenId,\n IMaverickV2Pool pool,\n IMaverickV2Pool.RemoveLiquidityParams memory params\n ) external returns (uint256 tokenAAmount, uint256 tokenBAmount);\n\n /**\n * @notice NFT asset information for a given range of pool/binIds indexes.\n * This function only returns the liquidity in the pools/binIds stored as\n * part of the tokenIdData, but it is possible that the NFT has additional\n * liquidity in pools/binIds that have not been recorded.\n */\n function tokenIdPositionInformation(\n uint256 tokenId,\n uint256 startIndex,\n uint256 stopIndex\n ) external view returns (PositionFullInformation[] memory output);\n\n /**\n * @notice NFT asset information for a given pool/binIds index. This\n * function only returns the liquidity in the pools/binIds stored as part\n * of the tokenIdData, but it is possible that the NFT has additional\n * liquidity in pools/binIds that have not been recorded.\n */\n function tokenIdPositionInformation(uint256 tokenId, uint256 index)\n external\n view\n returns (PositionFullInformation memory output);\n\n /**\n * @notice Get remove parameters for removing a fractional part of the\n * liquidity owned by a given tokenId. The fractional factor to remove is\n * given by proporationD18 in 18-decimal scale.\n */\n function getRemoveParams(\n uint256 tokenId,\n uint256 index,\n uint256 proportionD18\n )\n external\n view\n returns (IMaverickV2Pool.RemoveLiquidityParams memory params);\n\n /**\n * @notice Register the bin balances in the nft with the LpReward contract.\n */\n function checkpointBinLpBalance(\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Quoter.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2Quoter {\n error QuoterInvalidSwap();\n error QuoterInvalidAddLiquidity();\n\n /**\n * @notice Calculates a swap on a MaverickV2Pool and returns the resulting\n * amount and estimated gas. The gas estimate is only a rough estimate and\n * may not match a swap's gas.\n * @param pool The MaverickV2Pool to swap on.\n * @param amount The input amount.\n * @param tokenAIn Indicates if token A is the input token.\n * @param exactOutput Indicates if the amount is the output amount (true)\n * or input amount (false). If the tickLimit is reached, the full value of\n * the exactOutput may not be returned because the pool will stop swapping\n * before the whole order is filled.\n * @param tickLimit The tick limit for the swap. Once the swap lands in\n * this tick, it will stop and return the output amount swapped up to that\n * tick.\n */\n function calculateSwap(\n IMaverickV2Pool pool,\n uint128 amount,\n bool tokenAIn,\n bool exactOutput,\n int32 tickLimit\n )\n external\n returns (\n uint256 amountIn,\n uint256 amountOut,\n uint256 gasEstimate\n );\n\n /**\n * @notice Calculates a multihop swap and returns the resulting amount and\n * estimated gas. The gas estimate is only a rough estimate and\n * may not match a swap's gas.\n * @param path The path of pools to swap through. Path is given by an\n * packed array of (pool, tokenAIn) tuples. So each step in the path is 160\n * + 8 = 168 bits of data. e.g. path = abi.encodePacked(pool1, true, pool2, false);\n * @param amount The input amount.\n * @param exactOutput A boolean indicating if exact output is required.\n */\n function calculateMultiHopSwap(\n bytes memory path,\n uint256 amount,\n bool exactOutput\n ) external returns (uint256 returnAmount, uint256 gasEstimate);\n\n /**\n * @notice Computes the token amounts required for a given set of\n * addLiquidity parameters. The gas estimate is only a rough estimate and\n * may not match a add's gas.\n */\n function calculateAddLiquidity(\n IMaverickV2Pool pool,\n IMaverickV2Pool.AddLiquidityParams calldata params\n )\n external\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 gasEstimate\n );\n\n /**\n * @notice Pool's sqrt price.\n */\n function poolSqrtPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 sqrtPrice);\n}\n" + }, + "contracts/interfaces/plume/IPoolDistributor.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.0;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IPoolDistributor {\n function rewardToken() external view returns (address);\n\n function claimLp(\n address recipient,\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds,\n uint256 epoch\n ) external returns (uint256 amount);\n}\n" + }, + "contracts/interfaces/plume/IVotingDistributor.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.0;\n\ninterface IVotingDistributor {\n function lastEpoch() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/poolBooster/IMerklDistributor.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IMerklDistributor {\n struct CampaignParameters {\n // POPULATED ONCE CREATED\n\n // ID of the campaign. This can be left as a null bytes32 when creating campaigns\n // on Merkl.\n bytes32 campaignId;\n // CHOSEN BY CAMPAIGN CREATOR\n\n // Address of the campaign creator, if marked as address(0), it will be overriden with the\n // address of the `msg.sender` creating the campaign\n address creator;\n // Address of the token used as a reward\n address rewardToken;\n // Amount of `rewardToken` to distribute across all the epochs\n // Amount distributed per epoch is `amount/numEpoch`\n uint256 amount;\n // Type of campaign\n uint32 campaignType;\n // Timestamp at which the campaign should start\n uint32 startTimestamp;\n // Duration of the campaign in seconds. Has to be a multiple of EPOCH = 3600\n uint32 duration;\n // Extra data to pass to specify the campaign\n bytes campaignData;\n }\n\n function createCampaign(CampaignParameters memory newCampaign)\n external\n returns (bytes32);\n\n function signAndCreateCampaign(\n CampaignParameters memory newCampaign,\n bytes memory _signature\n ) external returns (bytes32);\n\n function sign(bytes memory _signature) external;\n\n function rewardTokenMinAmounts(address _rewardToken)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/poolBooster/IPoolBoostCentralRegistry.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPoolBoostCentralRegistry {\n /**\n * @dev all the supported pool booster types are listed here. It is possible\n * to have multiple versions of the factory that supports the same type of\n * pool booster. Factories are immutable and this can happen when a factory\n * or related pool booster required code update.\n * e.g. \"PoolBoosterSwapxDouble\" & \"PoolBoosterSwapxDouble_v2\"\n */\n enum PoolBoosterType {\n // Supports bribing 2 contracts per pool. Appropriate for Ichi vault concentrated\n // liquidity pools where (which is expected in most/all cases) both pool gauges\n // require bribing.\n SwapXDoubleBooster,\n // Supports bribing a single contract per pool. Appropriate for Classic Stable &\n // Classic Volatile pools and Ichi vaults where only 1 side (1 of the 2 gauges)\n // needs bribing\n SwapXSingleBooster,\n // Supports bribing a single contract per pool. Appropriate for Metropolis pools\n MetropolisBooster,\n // Supports creating a Merkl campaign.\n MerklBooster\n }\n\n struct PoolBoosterEntry {\n address boosterAddress;\n address ammPoolAddress;\n PoolBoosterType boosterType;\n }\n\n event PoolBoosterCreated(\n address poolBoosterAddress,\n address ammPoolAddress,\n PoolBoosterType poolBoosterType,\n address factoryAddress\n );\n event PoolBoosterRemoved(address poolBoosterAddress);\n\n function emitPoolBoosterCreated(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n PoolBoosterType _boosterType\n ) external;\n\n function emitPoolBoosterRemoved(address _poolBoosterAddress) external;\n}\n" + }, + "contracts/interfaces/poolBooster/IPoolBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPoolBooster {\n event BribeExecuted(uint256 amount);\n\n /// @notice Execute the bribe action\n function bribe() external;\n}\n" + }, + "contracts/interfaces/poolBooster/ISwapXAlgebraBribe.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBribe {\n /// @notice Notify a bribe amount\n /// @dev Rewards are saved into NEXT EPOCH mapping.\n function notifyRewardAmount(address _rewardsToken, uint256 reward) external;\n}\n" + }, + "contracts/interfaces/sonic/INodeDriver.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\n\ninterface INodeDriver {\n /// Seal epoch. Called BEFORE epoch sealing made by the client itself.\n function sealEpoch(\n uint256[] calldata offlineTimes,\n uint256[] calldata offlineBlocks,\n uint256[] calldata uptimes,\n uint256[] calldata originatedTxsFee\n ) external;\n}\n" + }, + "contracts/interfaces/sonic/ISFC.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\n\n/**\n * @title Special Fee Contract for Sonic network\n * @notice The SFC maintains a list of validators and delegators and distributes rewards to them.\n * @custom:security-contact security@fantom.foundation\n */\ninterface ISFC {\n error StakeIsFullySlashed();\n\n event CreatedValidator(\n uint256 indexed validatorID,\n address indexed auth,\n uint256 createdEpoch,\n uint256 createdTime\n );\n event Delegated(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 amount\n );\n event Undelegated(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 indexed wrID,\n uint256 amount\n );\n event Withdrawn(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 indexed wrID,\n uint256 amount,\n uint256 penalty\n );\n event ClaimedRewards(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 rewards\n );\n event RestakedRewards(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 rewards\n );\n event BurntFTM(uint256 amount);\n event UpdatedSlashingRefundRatio(\n uint256 indexed validatorID,\n uint256 refundRatio\n );\n event RefundedSlashedLegacyDelegation(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 amount\n );\n\n event DeactivatedValidator(\n uint256 indexed validatorID,\n uint256 deactivatedEpoch,\n uint256 deactivatedTime\n );\n event ChangedValidatorStatus(uint256 indexed validatorID, uint256 status);\n event AnnouncedRedirection(address indexed from, address indexed to);\n\n function currentSealedEpoch() external view returns (uint256);\n\n function getEpochSnapshot(uint256 epoch)\n external\n view\n returns (\n uint256 endTime,\n uint256 endBlock,\n uint256 epochFee,\n uint256 baseRewardPerSecond,\n uint256 totalStake,\n uint256 totalSupply\n );\n\n function getStake(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getValidator(uint256 validatorID)\n external\n view\n returns (\n uint256 status,\n uint256 receivedStake,\n address auth,\n uint256 createdEpoch,\n uint256 createdTime,\n uint256 deactivatedTime,\n uint256 deactivatedEpoch\n );\n\n function getValidatorID(address auth) external view returns (uint256);\n\n function getValidatorPubkey(uint256 validatorID)\n external\n view\n returns (bytes memory);\n\n function pubkeyAddressvalidatorID(address pubkeyAddress)\n external\n view\n returns (uint256);\n\n function getWithdrawalRequest(\n address delegator,\n uint256 validatorID,\n uint256 wrID\n )\n external\n view\n returns (\n uint256 epoch,\n uint256 time,\n uint256 amount\n );\n\n function isOwner() external view returns (bool);\n\n function lastValidatorID() external view returns (uint256);\n\n function minGasPrice() external view returns (uint256);\n\n function owner() external view returns (address);\n\n function renounceOwnership() external;\n\n function slashingRefundRatio(uint256 validatorID)\n external\n view\n returns (uint256);\n\n function stashedRewardsUntilEpoch(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function totalActiveStake() external view returns (uint256);\n\n function totalStake() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function transferOwnership(address newOwner) external;\n\n function treasuryAddress() external view returns (address);\n\n function version() external pure returns (bytes3);\n\n function currentEpoch() external view returns (uint256);\n\n function updateConstsAddress(address v) external;\n\n function constsAddress() external view returns (address);\n\n function getEpochValidatorIDs(uint256 epoch)\n external\n view\n returns (uint256[] memory);\n\n function getEpochReceivedStake(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochAccumulatedRewardPerToken(\n uint256 epoch,\n uint256 validatorID\n ) external view returns (uint256);\n\n function getEpochAccumulatedUptime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochAverageUptime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint32);\n\n function getEpochAccumulatedOriginatedTxsFee(\n uint256 epoch,\n uint256 validatorID\n ) external view returns (uint256);\n\n function getEpochOfflineTime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochOfflineBlocks(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochEndBlock(uint256 epoch) external view returns (uint256);\n\n function rewardsStash(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function createValidator(bytes calldata pubkey) external payable;\n\n function getSelfStake(uint256 validatorID) external view returns (uint256);\n\n function delegate(uint256 validatorID) external payable;\n\n function undelegate(\n uint256 validatorID,\n uint256 wrID,\n uint256 amount\n ) external;\n\n function isSlashed(uint256 validatorID) external view returns (bool);\n\n function withdraw(uint256 validatorID, uint256 wrID) external;\n\n function deactivateValidator(uint256 validatorID, uint256 status) external;\n\n function pendingRewards(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function stashRewards(address delegator, uint256 validatorID) external;\n\n function claimRewards(uint256 validatorID) external;\n\n function restakeRewards(uint256 validatorID) external;\n\n function updateSlashingRefundRatio(uint256 validatorID, uint256 refundRatio)\n external;\n\n function updateTreasuryAddress(address v) external;\n\n function burnFTM(uint256 amount) external;\n\n function sealEpoch(\n uint256[] calldata offlineTime,\n uint256[] calldata offlineBlocks,\n uint256[] calldata uptimes,\n uint256[] calldata originatedTxsFee\n ) external;\n\n function sealEpochValidators(uint256[] calldata nextValidatorIDs) external;\n\n function initialize(\n uint256 sealedEpoch,\n uint256 _totalSupply,\n address nodeDriver,\n address consts,\n address _owner\n ) external;\n\n function setGenesisValidator(\n address auth,\n uint256 validatorID,\n bytes calldata pubkey,\n uint256 createdTime\n ) external;\n\n function setGenesisDelegation(\n address delegator,\n uint256 validatorID,\n uint256 stake\n ) external;\n\n function updateStakeSubscriberAddress(address v) external;\n\n function stakeSubscriberAddress() external view returns (address);\n\n function setRedirectionAuthorizer(address v) external;\n\n function announceRedirection(address to) external;\n\n function initiateRedirection(address from, address to) external;\n\n function redirect(address to) external;\n}\n" + }, + "contracts/interfaces/sonic/ISwapXGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IGauge {\n function owner() external view returns (address);\n\n function TOKEN() external view returns (address);\n\n function DISTRIBUTION() external view returns (address);\n\n function balanceOf(address account) external view returns (uint256);\n\n function claimFees() external returns (uint256 claimed0, uint256 claimed1);\n\n function deposit(uint256 amount) external;\n\n function depositAll() external;\n\n function earned(address account) external view returns (uint256);\n\n function getReward() external;\n\n function getReward(address _user) external;\n\n function isForPair() external view returns (bool);\n\n function lastTimeRewardApplicable() external view returns (uint256);\n\n function lastUpdateTime() external view returns (uint256);\n\n function notifyRewardAmount(address token, uint256 reward) external;\n\n function periodFinish() external view returns (uint256);\n\n function rewardForDuration() external view returns (uint256);\n\n function rewardPerToken() external view returns (uint256);\n\n function rewardPerTokenStored() external view returns (uint256);\n\n function rewardRate() external view returns (uint256);\n\n function rewardToken() external view returns (address);\n\n function rewards(address) external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function userRewardPerTokenPaid(address) external view returns (uint256);\n\n function withdraw(uint256 amount) external;\n\n function withdrawAll() external;\n\n function withdrawAllAndHarvest() external;\n\n function withdrawExcess(address token, uint256 amount) external;\n\n function emergency() external returns (bool);\n\n function emergencyWithdraw() external;\n\n function activateEmergencyMode() external;\n\n function stopEmergencyMode() external;\n}\n" + }, + "contracts/interfaces/sonic/ISwapXPair.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IPair {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n\n event Mint(address indexed sender, uint256 amount0, uint256 amount1);\n event Burn(\n address indexed sender,\n uint256 amount0,\n uint256 amount1,\n address indexed to\n );\n event Swap(\n address indexed sender,\n uint256 amount0In,\n uint256 amount1In,\n uint256 amount0Out,\n uint256 amount1Out,\n address indexed to\n );\n event Claim(\n address indexed sender,\n address indexed recipient,\n uint256 amount0,\n uint256 amount1\n );\n\n function metadata()\n external\n view\n returns (\n uint256 dec0,\n uint256 dec1,\n uint256 r0,\n uint256 r1,\n bool st,\n address t0,\n address t1\n );\n\n function claimFees() external returns (uint256, uint256);\n\n function tokens() external view returns (address, address);\n\n function token0() external view returns (address);\n\n function token1() external view returns (address);\n\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function swap(\n uint256 amount0Out,\n uint256 amount1Out,\n address to,\n bytes calldata data\n ) external;\n\n function burn(address to)\n external\n returns (uint256 amount0, uint256 amount1);\n\n function mint(address to) external returns (uint256 liquidity);\n\n function getReserves()\n external\n view\n returns (\n uint256 _reserve0,\n uint256 _reserve1,\n uint256 _blockTimestampLast\n );\n\n function getAmountOut(uint256, address) external view returns (uint256);\n\n // ERC20 methods\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n\n function totalSupply() external view returns (uint256);\n\n function balanceOf(address) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 value) external returns (bool);\n\n function claimable0(address _user) external view returns (uint256);\n\n function claimable1(address _user) external view returns (uint256);\n\n function isStable() external view returns (bool);\n\n function skim(address to) external;\n\n function sync() external;\n}\n" + }, + "contracts/interfaces/sonic/IVoterV3.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IVoterV3 {\n /// @notice create a gauge\n function createGauge(address _pool, uint256 _gaugeType)\n external\n returns (\n address _gauge,\n address _internal_bribe,\n address _external_bribe\n );\n\n function gauges(address _pool) external view returns (address _gauge);\n}\n" + }, + "contracts/interfaces/sonic/IWrappedSonic.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWrappedSonic {\n event Deposit(address indexed account, uint256 value);\n event Withdrawal(address indexed account, uint256 value);\n event Transfer(address indexed from, address indexed to, uint256 value);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 value) external returns (bool);\n\n function balanceOf(address account) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function depositFor(address account) external payable returns (bool);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address to, uint256 value) external returns (bool);\n\n function transferFrom(\n address from,\n address to,\n uint256 value\n ) external returns (bool);\n\n function withdraw(uint256 value) external;\n\n function withdrawTo(address account, uint256 value) external returns (bool);\n}\n" + }, + "contracts/interfaces/Tether.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// slither-disable-start erc20-interface\ninterface Tether {\n function transfer(address to, uint256 value) external;\n\n function transferFrom(\n address from,\n address to,\n uint256 value\n ) external;\n\n function balanceOf(address) external view returns (uint256);\n\n function approve(address _spender, uint256 _value) external;\n}\n// slither-disable-end erc20-interface\n" + }, + "contracts/interfaces/uniswap/IUniswapUniversalRouter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapUniversalRouter {\n /// @notice Executes encoded commands along with provided inputs. Reverts if deadline has expired.\n /// @param commands A set of concatenated commands, each 1 byte in length\n /// @param inputs An array of byte strings containing abi encoded inputs for each command\n /// @param deadline The deadline by which the transaction must be executed\n function execute(\n bytes calldata commands,\n bytes[] calldata inputs,\n uint256 deadline\n ) external payable;\n\n function execute(bytes calldata commands, bytes[] calldata inputs)\n external\n payable;\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV2Pair.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapV2Pair {\n function token0() external view returns (address);\n\n function token1() external view returns (address);\n\n function getReserves()\n external\n view\n returns (\n uint112 reserve0,\n uint112 reserve1,\n uint32 blockTimestampLast\n );\n\n function price0CumulativeLast() external view returns (uint256);\n\n function price1CumulativeLast() external view returns (uint256);\n\n function sync() external;\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV2Router02.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapV2Router {\n function WETH() external pure returns (address);\n\n function swapExactTokensForTokens(\n uint256 amountIn,\n uint256 amountOutMin,\n address[] calldata path,\n address to,\n uint256 deadline\n ) external returns (uint256[] memory amounts);\n\n function addLiquidity(\n address tokenA,\n address tokenB,\n uint256 amountADesired,\n uint256 amountBDesired,\n uint256 amountAMin,\n uint256 amountBMin,\n address to,\n uint256 deadline\n )\n external\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 liquidity\n );\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV3Router.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// -- Solididy v0.5.x compatible interface\ninterface IUniswapV3Router {\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n}\n" + }, + "contracts/mocks/BurnableERC20.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ninterface IBurnableERC20 {\n function burn(uint256 value) external returns (bool);\n\n function burnFrom(address account, uint256 value) external returns (bool);\n}\n\n/**\n * @title BurnableERC20\n * @dev Exposes the burn function of ERC20 for tests\n */\nabstract contract BurnableERC20 is IBurnableERC20, ERC20 {\n /**\n * @dev Function to burn tokens\n * @param value The amount of tokens to burn.\n * @return A boolean that indicates if the operation was successful.\n */\n function burn(uint256 value) public virtual override returns (bool) {\n _burn(msg.sender, value);\n return true;\n }\n\n /**\n * @dev Function to burn tokens from a specific account\n * @param account The address with the tokens to burn.\n * @param value The amount of tokens to burn.\n * @return A boolean that indicates if the operation was successful.\n */\n function burnFrom(address account, uint256 value)\n public\n override\n returns (bool)\n {\n _burn(account, value);\n return true;\n }\n}\n" + }, + "contracts/mocks/curve/Mock3CRV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20, MintableERC20 } from \"../MintableERC20.sol\";\nimport { BurnableERC20 } from \"../BurnableERC20.sol\";\n\ncontract Mock3CRV is MintableERC20, BurnableERC20 {\n constructor() ERC20(\"Curve.fi DAI/USDC/USDT\", \"3Crv\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/curve/MockBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { MockRewardPool } from \"./MockRewardPool.sol\";\n\nimport { IRewardStaking } from \"../../strategies/IRewardStaking.sol\";\nimport { IMintableERC20, MintableERC20, ERC20 } from \"../MintableERC20.sol\";\nimport { IBurnableERC20, BurnableERC20 } from \"../BurnableERC20.sol\";\n\ncontract MockDepositToken is MintableERC20, BurnableERC20 {\n constructor() ERC20(\"DCVX\", \"CVX Deposit Token\") {}\n}\n\ncontract MockBooster {\n using SafeERC20 for IERC20;\n\n struct PoolInfo {\n address lptoken;\n address token;\n address crvRewards;\n }\n\n address public minter; // this is CVx for the booster on live\n address public crv; // Curve rewards token\n address public cvx; // Convex rewards token\n mapping(uint256 => PoolInfo) public poolInfo;\n\n constructor(\n address _rewardsMinter,\n address _crv,\n address _cvx\n ) public {\n minter = _rewardsMinter;\n crv = _crv;\n cvx = _cvx;\n }\n\n function setPool(uint256 pid, address _lpToken)\n external\n returns (address rewards)\n {\n address token = address(new MockDepositToken());\n // Deploy a new Convex Rewards Pool\n rewards = address(\n new MockRewardPool(pid, token, crv, cvx, address(this))\n );\n\n poolInfo[pid] = PoolInfo({\n lptoken: _lpToken,\n token: token,\n crvRewards: rewards\n });\n }\n\n function deposit(\n uint256 _pid,\n uint256 _amount,\n bool _stake\n ) public returns (bool) {\n PoolInfo storage pool = poolInfo[_pid];\n\n address lptoken = pool.lptoken;\n\n // hold on to the Curve LP tokens\n IERC20(lptoken).safeTransferFrom(msg.sender, address(this), _amount);\n\n address token = pool.token;\n if (_stake) {\n // mint Convex pool LP tokens and stake in rewards contract on user behalf\n IMintableERC20(token).mint(_amount);\n address rewardContract = pool.crvRewards;\n IERC20(token).safeApprove(rewardContract, 0);\n IERC20(token).safeApprove(rewardContract, _amount);\n IRewardStaking(rewardContract).stakeFor(msg.sender, _amount);\n } else {\n // mint Convex pool LP tokens and send to user\n IMintableERC20(token).mint(_amount);\n IERC20(token).transfer(msg.sender, _amount);\n }\n return true;\n }\n\n // Deposit all Curve LP tokens and stake\n function depositAll(uint256 _pid, bool _stake) external returns (bool) {\n address lptoken = poolInfo[_pid].lptoken;\n uint256 balance = IERC20(lptoken).balanceOf(msg.sender);\n deposit(_pid, balance, _stake);\n return true;\n }\n\n // withdraw Curve LP tokens\n function _withdraw(\n uint256 _pid,\n uint256 _amount,\n address _from,\n address _to\n ) internal {\n PoolInfo storage pool = poolInfo[_pid];\n\n // burn the Convex pool LP tokens\n IBurnableERC20(pool.token).burnFrom(_from, _amount);\n\n // return the Curve LP tokens\n IERC20(pool.lptoken).safeTransfer(_to, _amount);\n }\n\n // withdraw Curve LP tokens\n function withdraw(uint256 _pid, uint256 _amount) public returns (bool) {\n _withdraw(_pid, _amount, msg.sender, msg.sender);\n return true;\n }\n\n // withdraw all Curve LP tokens\n function withdrawAll(uint256 _pid) public returns (bool) {\n address token = poolInfo[_pid].token;\n uint256 userBal = IERC20(token).balanceOf(msg.sender);\n withdraw(_pid, userBal);\n return true;\n }\n\n // allow reward contracts to send here and withdraw to user\n function withdrawTo(\n uint256 _pid,\n uint256 _amount,\n address _to\n ) external returns (bool) {\n address rewardContract = poolInfo[_pid].crvRewards;\n require(msg.sender == rewardContract, \"!auth\");\n\n _withdraw(_pid, _amount, msg.sender, _to);\n return true;\n }\n\n // callback from reward contract when crv is received.\n function rewardClaimed(\n uint256 _pid,\n // solhint-disable-next-line no-unused-vars\n address _address,\n uint256 _amount\n ) external returns (bool) {\n address rewardContract = poolInfo[_pid].crvRewards;\n require(msg.sender == rewardContract, \"!auth\");\n\n //mint reward tokens\n // and transfer it\n IMintableERC20(minter).mint(_amount);\n IERC20(minter).transfer(msg.sender, _amount);\n return true;\n }\n}\n" + }, + "contracts/mocks/curve/MockCRV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../MintableERC20.sol\";\n\ncontract MockCRV is MintableERC20 {\n constructor() ERC20(\"Curve DAO Token\", \"CRV\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/curve/MockCRVMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\n\ncontract MockCRVMinter {\n address crv;\n\n constructor(address _crv) {\n crv = _crv;\n }\n\n function mint(address _address) external {\n uint256 amount = 2e18;\n IMintableERC20(crv).mint(amount);\n IERC20(crv).transfer(_address, amount);\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveAbstractMetapool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { MintableERC20, IMintableERC20 } from \"../MintableERC20.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport \"../../utils/Helpers.sol\";\n\nabstract contract MockCurveAbstractMetapool is MintableERC20 {\n using StableMath for uint256;\n\n address[] public coins;\n uint256[2] public balances;\n\n // Returns the same amount of LP tokens in 1e18 decimals\n function add_liquidity(uint256[2] calldata _amounts, uint256 _minAmount)\n external\n returns (uint256 lpAmount)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transferFrom(\n msg.sender,\n address(this),\n _amounts[i]\n );\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to sum\n lpAmount += _amounts[i].scaleBy(18, assetDecimals);\n balances[i] = balances[i] + _amounts[i];\n }\n }\n // Hacky way of simulating slippage to check _minAmount\n if (lpAmount == 29000e18) lpAmount = 14500e18;\n require(lpAmount >= _minAmount, \"Slippage ruined your day\");\n // Send LP token to sender, e.g. 3CRV\n _mint(msg.sender, lpAmount);\n }\n\n // Dumb implementation that returns the same amount\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n public\n view\n returns (uint256 lpAmount)\n {\n uint256 assetDecimals = Helpers.getDecimals(coins[uint128(_index)]);\n lpAmount = _amount.scaleBy(assetDecimals, 18);\n }\n\n function remove_liquidity_one_coin(\n uint256 _lpAmount,\n int128 _index,\n // solhint-disable-next-line no-unused-vars\n uint256 _minAmount\n ) external returns (uint256 amount) {\n _burn(msg.sender, _lpAmount);\n uint256[] memory amounts = new uint256[](coins.length);\n amounts[uint128(_index)] = _lpAmount;\n amount = calc_withdraw_one_coin(_lpAmount, _index);\n balances[uint128(_index)] -= amount;\n IERC20(coins[uint128(_index)]).transfer(msg.sender, amount);\n }\n\n function get_virtual_price() external pure returns (uint256) {\n return 1e18;\n }\n\n // solhint-disable-next-line no-unused-vars\n function remove_liquidity(uint256 _amount, uint256[2] memory _min_amounts)\n public\n returns (uint256[2] memory amounts)\n {\n _burn(msg.sender, _amount);\n uint256 totalSupply = totalSupply();\n for (uint256 i = 0; i < 2; i++) {\n amounts[i] = totalSupply > 0\n ? (_amount * IERC20(coins[i]).balanceOf(address(this))) /\n totalSupply\n : IERC20(coins[i]).balanceOf(address(this));\n balances[i] -= amounts[i];\n IERC20(coins[i]).transfer(msg.sender, amounts[i]);\n }\n }\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens\n ) public returns (uint256) {\n return\n _remove_liquidity_imbalance(\n _amounts,\n _max_burned_tokens,\n msg.sender\n );\n }\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens,\n address _reveiver\n ) public returns (uint256) {\n return\n _remove_liquidity_imbalance(\n _amounts,\n _max_burned_tokens,\n _reveiver\n );\n }\n\n function _remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens,\n address _reveiver\n ) internal returns (uint256 lpTokens) {\n lpTokens = _max_burned_tokens;\n _burn(msg.sender, lpTokens);\n for (uint256 i = 0; i < _amounts.length; i++) {\n balances[i] -= _amounts[i];\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transfer(_reveiver, _amounts[i]);\n }\n }\n }\n\n // Dumb implementation that sums the scaled amounts\n function calc_token_amount(uint256[2] memory _amounts, bool)\n public\n view\n returns (uint256 lpTokens)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to lpTokens\n lpTokens += _amounts[i].scaleBy(18, assetDecimals);\n }\n }\n\n /// @notice 0.02% fee\n function fee() external pure returns (uint256) {\n return 2000000;\n }\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n\n function burnFrom(address from, uint256 value) public {\n _burn(from, value);\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveGauge.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { ICurveGauge } from \"../../strategies/ICurveGauge.sol\";\n\ncontract MockCurveGauge is ICurveGauge {\n mapping(address => uint256) private _balances;\n address lpToken;\n\n constructor(address _lpToken) {\n lpToken = _lpToken;\n }\n\n function balanceOf(address account) public view override returns (uint256) {\n return _balances[account];\n }\n\n function deposit(uint256 _value, address _account) external override {\n IERC20(lpToken).transferFrom(msg.sender, address(this), _value);\n _balances[_account] += _value;\n }\n\n function withdraw(uint256 _value) external override {\n IERC20(lpToken).transfer(msg.sender, _value);\n // solhint-disable-next-line reentrancy\n _balances[msg.sender] -= _value;\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveMetapool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { MockCurveAbstractMetapool } from \"./MockCurveAbstractMetapool.sol\";\nimport \"../MintableERC20.sol\";\n\ncontract MockCurveMetapool is MockCurveAbstractMetapool {\n constructor(address[2] memory _coins)\n ERC20(\"Curve.fi 3pool/OUSD metapool\", \"3crv_OUSD\")\n {\n coins = _coins;\n }\n}\n" + }, + "contracts/mocks/curve/MockCurvePool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IBurnableERC20 } from \"../BurnableERC20.sol\";\n\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\nimport { ICurvePool } from \"../../strategies/ICurvePool.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport \"../../utils/Helpers.sol\";\n\ncontract MockCurvePool {\n using StableMath for uint256;\n\n address[] public coins;\n uint256[3] public balances;\n address lpToken;\n uint256 public slippage = 1 ether;\n\n constructor(address[3] memory _coins, address _lpToken) {\n coins = _coins;\n lpToken = _lpToken;\n }\n\n function setCoins(address[] memory _coins) external {\n coins = _coins;\n }\n\n // Returns the same amount of LP tokens in 1e18 decimals\n function add_liquidity(uint256[3] calldata _amounts, uint256 _minAmount)\n external\n {\n uint256 sum = 0;\n for (uint256 i = 0; i < _amounts.length; i++) {\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transferFrom(\n msg.sender,\n address(this),\n _amounts[i]\n );\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to sum\n sum += _amounts[i].scaleBy(18, assetDecimals);\n balances[i] = balances[i] + _amounts[i];\n }\n }\n // Hacky way of simulating slippage to check _minAmount\n if (sum == 29000e18) sum = 14500e18;\n require(sum >= _minAmount, \"Slippage ruined your day\");\n // Send LP token to sender, e.g. 3CRV\n IMintableERC20(lpToken).mint(sum);\n IERC20(lpToken).transfer(msg.sender, sum);\n }\n\n // Dumb implementation that returns the same amount\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n public\n view\n returns (uint256)\n {\n uint256 assetDecimals = Helpers.getDecimals(coins[uint128(_index)]);\n return _amount.scaleBy(assetDecimals, 18);\n }\n\n function remove_liquidity_one_coin(\n uint256 _amount,\n int128 _index,\n // solhint-disable-next-line no-unused-vars\n uint256 _minAmount\n ) external {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _amount);\n uint256[] memory amounts = new uint256[](coins.length);\n amounts[uint128(_index)] = _amount;\n uint256 coinAmount = calc_withdraw_one_coin(_amount, _index);\n balances[uint128(_index)] -= coinAmount;\n IERC20(coins[uint128(_index)]).transfer(msg.sender, coinAmount);\n }\n\n function get_virtual_price() external pure returns (uint256) {\n return 1e18;\n }\n\n // solhint-disable-next-line no-unused-vars\n function remove_liquidity(uint256 _lpAmount, uint256[3] memory _min_amounts)\n public\n {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _lpAmount);\n uint256 totalSupply = IERC20(lpToken).totalSupply();\n for (uint256 i = 0; i < 3; i++) {\n uint256 coinAmount = totalSupply > 0\n ? (_lpAmount * IERC20(coins[i]).balanceOf(address(this))) /\n totalSupply\n : IERC20(coins[i]).balanceOf(address(this));\n balances[i] -= coinAmount;\n IERC20(coins[i]).transfer(msg.sender, coinAmount);\n }\n }\n\n function remove_liquidity_imbalance(\n uint256[3] memory _amounts,\n uint256 _max_burned_tokens\n ) public {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _max_burned_tokens);\n // For each coin, transfer to the caller\n for (uint256 i = 0; i < _amounts.length; i++) {\n balances[i] -= _amounts[i];\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transfer(msg.sender, _amounts[i]);\n }\n }\n }\n\n // Dumb implementation that sums the scaled amounts\n function calc_token_amount(uint256[3] memory _amounts, bool)\n public\n view\n returns (uint256 lpTokens)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to lpTokens\n lpTokens += _amounts[i].scaleBy(18, assetDecimals);\n }\n }\n\n function fee() external pure returns (uint256) {\n return 1000000;\n }\n\n function exchange(\n uint256 coin0,\n uint256 coin1,\n uint256 amountIn,\n uint256 minAmountOut\n ) external returns (uint256 amountOut) {\n IERC20(coins[coin0]).transferFrom(msg.sender, address(this), amountIn);\n amountOut = (minAmountOut * slippage) / 1 ether;\n require(amountOut >= minAmountOut, \"Slippage error\");\n IMintableERC20(coins[coin1]).mintTo(msg.sender, amountOut);\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n}\n" + }, + "contracts/mocks/curve/MockCVX.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../MintableERC20.sol\";\n\ncontract MockCVX is MintableERC20 {\n constructor() ERC20(\"CVX\", \"CVX DAO Token\") {}\n}\n" + }, + "contracts/mocks/curve/MockRewardPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\ninterface IDeposit {\n function poolInfo(uint256)\n external\n view\n returns (\n address,\n address,\n address,\n address,\n address,\n bool\n );\n\n function rewardClaimed(\n uint256,\n address,\n uint256\n ) external;\n\n function withdrawTo(\n uint256,\n uint256,\n address\n ) external;\n}\n\ncontract MockRewardPool {\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 public pid;\n address public stakingToken;\n address public rewardTokenA;\n address public rewardTokenB;\n address public operator;\n\n uint256 private _totalSupply;\n\n mapping(address => uint256) private _balances;\n mapping(address => uint256) public rewards;\n\n constructor(\n uint256 _pid,\n address _stakingToken,\n address _rewardTokenA,\n address _rewardTokenB,\n address _operator\n ) public {\n pid = _pid;\n stakingToken = _stakingToken;\n rewardTokenA = _rewardTokenA;\n rewardTokenB = _rewardTokenB;\n operator = _operator;\n }\n\n function totalSupply() public view returns (uint256) {\n return _totalSupply;\n }\n\n function balanceOf(address account) public view returns (uint256) {\n return _balances[account];\n }\n\n function stakeFor(address _for, uint256 _amount) public returns (bool) {\n require(_amount > 0, \"RewardPool : Cannot stake 0\");\n\n //give to _for\n _totalSupply = _totalSupply.add(_amount);\n _balances[_for] = _balances[_for].add(_amount);\n\n //take away from sender\n IERC20(stakingToken).safeTransferFrom(\n msg.sender,\n address(this),\n _amount\n );\n\n return true;\n }\n\n function withdrawAndUnwrap(uint256 amount, bool claim)\n public\n returns (bool)\n {\n _totalSupply = _totalSupply.sub(amount);\n _balances[msg.sender] = _balances[msg.sender].sub(amount);\n\n //tell operator to withdraw from here directly to user\n IDeposit(operator).withdrawTo(pid, amount, msg.sender);\n\n //get rewards too\n if (claim) {\n getReward(msg.sender, true);\n }\n return true;\n }\n\n function withdrawAllAndUnwrap(bool claim) external {\n withdrawAndUnwrap(_balances[msg.sender], claim);\n }\n\n // solhint-disable-next-line no-unused-vars\n function getReward(address _account, bool _claimExtras)\n public\n returns (bool)\n {\n IMintableERC20(rewardTokenA).mint(2 * 1e18);\n IERC20(rewardTokenA).transfer(_account, 2 * 1e18);\n\n IMintableERC20(rewardTokenB).mint(3 * 1e18);\n IERC20(rewardTokenB).transfer(_account, 3 * 1e18);\n\n return true;\n }\n\n function getReward() public returns (bool) {\n getReward(msg.sender, true);\n }\n}\n" + }, + "contracts/mocks/ForceEtherSender.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract ForceEtherSender {\n // Constructor to optionally receive Ether upon deployment\n constructor() payable {}\n\n // Function to allow the contract to receive Ether\n receive() external payable {}\n\n // Function to self-destruct and force-send Ether to an address\n function forceSend(address payable recipient) external {\n // Requires that the contract has a balance greater than 0\n require(address(this).balance > 0, \"No Ether to send\");\n\n // selfdestruct sends all Ether held by the contract to the recipient\n selfdestruct(recipient);\n }\n}\n" + }, + "contracts/mocks/MintableERC20.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ninterface IMintableERC20 {\n function mint(uint256 value) external;\n\n function mintTo(address to, uint256 value) external;\n}\n\n/**\n * @title MintableERC20\n * @dev Exposes the mint function of ERC20 for tests\n */\nabstract contract MintableERC20 is IMintableERC20, ERC20 {\n /**\n * @dev Function to mint tokens\n * @param _value The amount of tokens to mint.\n */\n function mint(uint256 _value) public virtual override {\n _mint(msg.sender, _value);\n }\n\n /**\n * @dev Function to mint tokens\n * @param _to Address to mint to.\n * @param _value The amount of tokens to mint.\n */\n function mintTo(address _to, uint256 _value) public virtual override {\n _mint(_to, _value);\n }\n}\n" + }, + "contracts/mocks/Mock1InchSwapRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { SwapDescription } from \"../interfaces/IOneInch.sol\";\n\ncontract Mock1InchSwapRouter {\n using SafeERC20 for IERC20;\n\n event MockSwap(address executor, bytes permitData, bytes executorData);\n\n event MockSwapDesc(\n address srcToken,\n address dstToken,\n address srcReceiver,\n address dstReceiver,\n uint256 amount,\n uint256 minReturnAmount,\n uint256 flags\n );\n\n event MockUnoswapTo(\n address recipient,\n address srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] pools\n );\n\n event MockUniswapV3SwapTo(\n address recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] pools\n );\n\n /**\n * @dev transfers the shource asset and returns the minReturnAmount of the destination asset.\n */\n function swap(\n address executor,\n SwapDescription calldata desc,\n bytes calldata permitData,\n bytes calldata executorData\n ) public returns (uint256 returnAmount, uint256 spentAmount) {\n // Transfer the source tokens to the receiver contract\n IERC20(desc.srcToken).safeTransferFrom(\n msg.sender,\n desc.srcReceiver,\n desc.amount\n );\n\n // Transfer the destination tokens to the recipient\n IERC20(desc.dstToken).safeTransfer(\n desc.dstReceiver,\n desc.minReturnAmount\n );\n\n emit MockSwap(executor, permitData, executorData);\n _swapDesc(desc);\n returnAmount = 0;\n spentAmount = 0;\n }\n\n function _swapDesc(SwapDescription calldata desc) public {\n emit MockSwapDesc(\n address(desc.srcToken),\n address(desc.dstToken),\n desc.srcReceiver,\n desc.dstReceiver,\n desc.amount,\n desc.minReturnAmount,\n desc.flags\n );\n }\n\n /**\n * @dev only transfers the source asset to this contract.\n * Ideally it would return the destination asset but that's encoded in the pools array.\n */\n function unoswapTo(\n address payable recipient,\n address srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) public returns (uint256 returnAmount) {\n // transfer the from asset from the caller\n IERC20(srcToken).safeTransferFrom(msg.sender, address(this), amount);\n\n emit MockUnoswapTo(recipient, srcToken, amount, minReturn, pools);\n returnAmount = 0;\n }\n\n /**\n * @dev does not do any transfers. Just emits MockUniswapV3SwapTo.\n */\n function uniswapV3SwapTo(\n address payable recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) public returns (uint256 returnAmount) {\n emit MockUniswapV3SwapTo(recipient, amount, minReturn, pools);\n returnAmount = 0;\n }\n}\n" + }, + "contracts/mocks/MockAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { IAaveLendingPool, ILendingPoolAddressesProvider } from \"../strategies/IAave.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// 1. User calls 'getLendingPool'\n// 2. User calls 'deposit' (Aave)\n// - Deposit their underlying\n// - Mint aToken to them\n// 3. User calls redeem (aToken)\n// - Retrieve their aToken\n// - Return equal amount of underlying\n\ncontract MockAToken is MintableERC20 {\n address public lendingPool;\n IERC20 public underlyingToken;\n using SafeERC20 for IERC20;\n\n constructor(\n address _lendingPool,\n string memory _name,\n string memory _symbol,\n IERC20 _underlyingToken\n ) ERC20(_name, _symbol) {\n lendingPool = _lendingPool;\n underlyingToken = _underlyingToken;\n // addMinter(_lendingPool);\n }\n\n function decimals() public view override returns (uint8) {\n return ERC20(address(underlyingToken)).decimals();\n }\n\n function poolRedeem(uint256 _amount, address _to) external {\n require(msg.sender == lendingPool, \"pool only\");\n // Redeem these a Tokens\n _burn(_to, _amount);\n // For the underlying\n underlyingToken.safeTransferFrom(lendingPool, _to, _amount);\n }\n}\n\ncontract MockAave is IAaveLendingPool, ILendingPoolAddressesProvider {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n mapping(address => address) reserveToAToken;\n address pool = address(this);\n address payable core = payable(address(this));\n uint256 factor;\n\n function addAToken(address _aToken, address _underlying) public {\n IERC20(_underlying).safeApprove(_aToken, 0);\n IERC20(_underlying).safeApprove(_aToken, type(uint256).max);\n reserveToAToken[_underlying] = _aToken;\n }\n\n // set the reserve factor / basically the interest on deposit\n // in 18 precision\n // so 0.5% would be 5 * 10 ^ 15\n function setFactor(uint256 factor_) public {\n factor = factor_;\n }\n\n function deposit(\n address _reserve,\n uint256 _amount,\n address _to,\n uint16 /*_referralCode*/\n ) external override {\n uint256 previousBal = IERC20(reserveToAToken[_reserve]).balanceOf(\n msg.sender\n );\n uint256 interest = previousBal.mulTruncate(factor);\n MintableERC20(reserveToAToken[_reserve]).mintTo(msg.sender, interest);\n // Take their reserve\n IERC20(_reserve).safeTransferFrom(msg.sender, address(this), _amount);\n // Credit them with aToken\n MintableERC20(reserveToAToken[_reserve]).mintTo(_to, _amount);\n }\n\n function withdraw(\n address asset,\n uint256 amount,\n address to\n ) external override returns (uint256) {\n MockAToken atoken = MockAToken(reserveToAToken[asset]);\n atoken.poolRedeem(amount, to);\n return amount;\n }\n\n function getLendingPool() external view override returns (address) {\n return pool;\n }\n}\n" + }, + "contracts/mocks/MockAaveIncentivesController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { MockStkAave } from \"./MockStkAave.sol\";\n\ncontract MockAaveIncentivesController {\n mapping(address => uint256) private rewards;\n MockStkAave public REWARD_TOKEN;\n\n constructor(address _reward_token) {\n REWARD_TOKEN = MockStkAave(_reward_token);\n }\n\n function setRewardsBalance(address user, uint256 amount) external {\n rewards[user] = amount;\n }\n\n /**\n * @dev Returns the total of rewards of an user, already accrued + not yet accrued\n * @param user The address of the user\n * @return The rewards\n **/\n // solhint-disable-next-line no-unused-vars\n function getRewardsBalance(address[] calldata assets, address user)\n external\n view\n returns (uint256)\n {\n return rewards[user];\n }\n\n /**\n * @dev Claims reward for an user, on all the assets of the lending pool, accumulating the pending rewards\n * @param amount Amount of rewards to claim\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewards(\n // solhint-disable-next-line no-unused-vars\n address[] calldata assets,\n uint256 amount,\n address to\n ) external returns (uint256) {\n require(amount > 0);\n require(rewards[to] == amount);\n REWARD_TOKEN.mint(amount);\n require(REWARD_TOKEN.transfer(to, amount));\n // solhint-disable-next-line reentrancy\n rewards[to] = 0;\n return amount;\n }\n}\n" + }, + "contracts/mocks/MockAAVEToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAAVEToken is MintableERC20 {\n constructor() ERC20(\"AAVE\", \"AAVE\") {}\n}\n" + }, + "contracts/mocks/MockAero.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAero is MintableERC20 {\n constructor() ERC20(\"Aerodrome\", \"AERO\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/MockAura.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAura is MintableERC20 {\n constructor() ERC20(\"Aura\", \"AURA\") {}\n}\n" + }, + "contracts/mocks/MockBAL.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockBAL is MintableERC20 {\n constructor() ERC20(\"Balancer\", \"BAL\") {}\n}\n" + }, + "contracts/mocks/MockBalancerVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// import \"hardhat/console.sol\";\n\ncontract MockBalancerVault {\n using StableMath for uint256;\n uint256 public slippage = 1 ether;\n bool public transferDisabled = false;\n bool public slippageErrorDisabled = false;\n\n function swap(\n IBalancerVault.SingleSwap calldata singleSwap,\n IBalancerVault.FundManagement calldata funds,\n uint256 minAmountOut,\n uint256\n ) external returns (uint256 amountCalculated) {\n amountCalculated = (minAmountOut * slippage) / 1 ether;\n if (!slippageErrorDisabled) {\n require(amountCalculated >= minAmountOut, \"Slippage error\");\n }\n IERC20(singleSwap.assetIn).transferFrom(\n funds.sender,\n address(this),\n singleSwap.amount\n );\n if (!transferDisabled) {\n MintableERC20(singleSwap.assetOut).mintTo(\n funds.recipient,\n amountCalculated\n );\n }\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n\n function getPoolTokenInfo(bytes32 poolId, address token)\n external\n view\n returns (\n uint256,\n uint256,\n uint256,\n address\n )\n {}\n\n function disableTransfer() external {\n transferDisabled = true;\n }\n\n function disableSlippageError() external {\n slippageErrorDisabled = true;\n }\n}\n" + }, + "contracts/mocks/MockChainlinkOracleFeed.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\ncontract MockChainlinkOracleFeed is AggregatorV3Interface {\n int256 price;\n uint8 numDecimals;\n\n constructor(int256 _price, uint8 _decimals) {\n price = _price;\n numDecimals = _decimals;\n }\n\n function decimals() external view override returns (uint8) {\n return numDecimals;\n }\n\n function description() external pure override returns (string memory) {\n return \"MockOracleEthFeed\";\n }\n\n function version() external pure override returns (uint256) {\n return 1;\n }\n\n function setPrice(int256 _price) public {\n price = _price;\n }\n\n function setDecimals(uint8 _decimals) public {\n numDecimals = _decimals;\n }\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(uint80 _roundId)\n external\n view\n override\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n )\n {\n roundId = _roundId;\n answer = price;\n startedAt = 0;\n updatedAt = 0;\n answeredInRound = 0;\n }\n\n function latestRoundData()\n external\n view\n override\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n )\n {\n roundId = 0;\n answer = price;\n startedAt = 0;\n updatedAt = block.timestamp;\n answeredInRound = 0;\n }\n}\n" + }, + "contracts/mocks/MockCOMP.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockCOMP is MintableERC20 {\n constructor() ERC20(\"COMP\", \"COMP\") {}\n}\n" + }, + "contracts/mocks/MockComptroller.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract MockComptroller {\n // Claim all the COMP accrued by specific holders in specific markets for their supplies and/or borrows\n function claimComp(\n address[] memory holders,\n address[] memory cTokens,\n bool borrowers,\n bool suppliers\n ) external {}\n}\n" + }, + "contracts/mocks/MockCToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { ICERC20 } from \"../strategies/ICompound.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockCToken is ICERC20, ERC20 {\n using StableMath for uint256;\n\n IERC20 public underlyingToken;\n // underlying = cToken * exchangeRate\n // cToken = underlying / exchangeRate\n uint256 exchangeRate;\n address public override comptroller;\n\n constructor(ERC20 _underlyingToken, address _comptroller)\n ERC20(\"cMock\", \"cMK\")\n {\n uint8 underlyingDecimals = _underlyingToken.decimals();\n // if has 18 dp, exchange rate should be 1e26\n // if has 8 dp, exchange rate should be 1e18\n if (underlyingDecimals > 8) {\n exchangeRate = 10**uint256(18 + underlyingDecimals - 10);\n } else if (underlyingDecimals < 8) {\n // e.g. 18-8+6 = 16\n exchangeRate = 10**uint256(18 - 8 + underlyingDecimals);\n } else {\n exchangeRate = 1e18;\n }\n underlyingToken = _underlyingToken;\n comptroller = _comptroller;\n }\n\n function decimals() public pure override returns (uint8) {\n return 8;\n }\n\n function mint(uint256 mintAmount) public override returns (uint256) {\n // Credit them with cToken\n _mint(msg.sender, mintAmount.divPrecisely(exchangeRate));\n // Take their reserve\n underlyingToken.transferFrom(msg.sender, address(this), mintAmount);\n return 0;\n }\n\n function redeem(uint256 redeemAmount) external override returns (uint256) {\n uint256 tokenAmount = redeemAmount.mulTruncate(exchangeRate);\n // Burn the cToken\n _burn(msg.sender, redeemAmount);\n // Transfer underlying to caller\n underlyingToken.transfer(msg.sender, tokenAmount);\n return 0;\n }\n\n function redeemUnderlying(uint256 redeemAmount)\n external\n override\n returns (uint256)\n {\n uint256 cTokens = redeemAmount.divPrecisely(exchangeRate);\n // Burn the cToken\n _burn(msg.sender, cTokens);\n // Transfer underlying to caller\n underlyingToken.transfer(msg.sender, redeemAmount);\n return 0;\n }\n\n function balanceOfUnderlying(address owner)\n external\n view\n override\n returns (uint256)\n {\n uint256 cTokenBal = this.balanceOf(owner);\n return cTokenBal.mulTruncate(exchangeRate);\n }\n\n function balanceOf(address owner)\n public\n view\n override(ICERC20, ERC20)\n returns (uint256)\n {\n return ERC20.balanceOf(owner);\n }\n\n function updateExchangeRate()\n internal\n view\n returns (uint256 newExchangeRate)\n {\n uint256 factor = 100002 * (10**13); // 0.002%\n newExchangeRate = exchangeRate.mulTruncate(factor);\n }\n\n function exchangeRateStored() external view override returns (uint256) {\n return exchangeRate;\n }\n\n function supplyRatePerBlock() external pure override returns (uint256) {\n return 141 * (10**8);\n }\n}\n" + }, + "contracts/mocks/MockCVXLocker.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MockCVXLocker {\n address public immutable cvx;\n mapping(address => uint256) public lockedBalanceOf;\n\n constructor(address _cvx) {\n cvx = _cvx;\n }\n\n function lock(\n address _account,\n uint256 _amount,\n uint256\n ) external {\n lockedBalanceOf[_account] += _amount;\n ERC20(cvx).transferFrom(msg.sender, address(this), _amount);\n }\n\n function unlockAllTokens(address _account) external {\n lockedBalanceOf[_account] = 0;\n ERC20(cvx).transfer(_account, lockedBalanceOf[_account]);\n }\n}\n" + }, + "contracts/mocks/MockDAI.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockDAI is MintableERC20 {\n constructor() ERC20(\"DAI\", \"DAI\") {}\n}\n" + }, + "contracts/mocks/MockDepositContract.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IDepositContract } from \"./../interfaces/IDepositContract.sol\";\n\ncontract MockDepositContract is IDepositContract {\n uint256 deposit_count;\n\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable override {\n require(pubkey.length == 48, \"DepositContract: invalid pubkey length\");\n require(\n withdrawal_credentials.length == 32,\n \"DepositContract: invalid withdrawal_credentials length\"\n );\n require(\n signature.length == 96,\n \"DepositContract: invalid signature length\"\n );\n\n // Check deposit amount\n require(msg.value >= 1 ether, \"DepositContract: deposit value too low\");\n require(\n msg.value % 1 gwei == 0,\n \"DepositContract: deposit value not multiple of gwei\"\n );\n uint256 deposit_amount = msg.value / 1 gwei;\n require(\n deposit_amount <= type(uint64).max,\n \"DepositContract: deposit value too high\"\n );\n\n // Emit `DepositEvent` log\n bytes memory amount = to_little_endian_64(uint64(deposit_amount));\n emit DepositEvent(\n pubkey,\n withdrawal_credentials,\n amount,\n signature,\n to_little_endian_64(uint64(deposit_count))\n );\n require(\n deposit_data_root != 0,\n \"DepositContract: invalid deposit_data_root\"\n );\n }\n\n function get_deposit_root() external view override returns (bytes32) {\n // just return some bytes32\n return sha256(abi.encodePacked(deposit_count, bytes16(0)));\n }\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view override returns (bytes memory) {\n return to_little_endian_64(uint64(deposit_count));\n }\n\n function to_little_endian_64(uint64 value)\n internal\n pure\n returns (bytes memory ret)\n {\n ret = new bytes(8);\n bytes8 bytesValue = bytes8(value);\n // Byteswapping during copying to bytes.\n ret[0] = bytesValue[7];\n ret[1] = bytesValue[6];\n ret[2] = bytesValue[5];\n ret[3] = bytesValue[4];\n ret[4] = bytesValue[3];\n ret[5] = bytesValue[2];\n ret[6] = bytesValue[1];\n ret[7] = bytesValue[0];\n }\n}\n" + }, + "contracts/mocks/MockEvilDAI.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract MockEvilDAI is MintableERC20 {\n address host;\n address realCoin;\n\n constructor(address _host, address _realCoin) ERC20(\"DAI\", \"DAI\") {\n host = _host;\n realCoin = _realCoin;\n }\n\n function transferFrom(\n // solhint-disable-next-line no-unused-vars\n address _from,\n // solhint-disable-next-line no-unused-vars\n address _to,\n uint256 _amount\n ) public override returns (bool) {\n // call mint again!\n if (_amount != 69) {\n IVault(host).mint(address(this), 69, 0);\n }\n return true;\n }\n}\n" + }, + "contracts/mocks/MockEvilReentrantContract.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IRateProvider } from \"../interfaces/balancer/IRateProvider.sol\";\n\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { IERC20 } from \"../utils/InitializableAbstractStrategy.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockEvilReentrantContract {\n using StableMath for uint256;\n\n IBalancerVault public immutable balancerVault;\n IERC20 public immutable reth;\n IERC20 public immutable weth;\n IVault public immutable oethVault;\n address public immutable poolAddress;\n bytes32 public immutable balancerPoolId;\n\n constructor(\n address _balancerVault,\n address _oethVault,\n address _reth,\n address _weth,\n address _poolAddress,\n bytes32 _poolId\n ) {\n balancerVault = IBalancerVault(_balancerVault);\n oethVault = IVault(_oethVault);\n reth = IERC20(_reth);\n weth = IERC20(_weth);\n poolAddress = _poolAddress;\n balancerPoolId = _poolId;\n }\n\n function doEvilStuff() public {\n address priceProvider = oethVault.priceProvider();\n uint256 rethPrice = IOracle(priceProvider).price(address(reth));\n\n // 1. Join pool\n uint256[] memory amounts = new uint256[](2);\n amounts[0] = uint256(10 ether);\n amounts[1] = rethPrice * 10;\n\n address[] memory assets = new address[](2);\n assets[0] = address(reth);\n assets[1] = address(weth);\n\n uint256 minBPT = getBPTExpected(assets, amounts).mulTruncate(\n 0.99 ether\n );\n\n bytes memory joinUserData = abi.encode(\n IBalancerVault.WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,\n amounts,\n minBPT\n );\n\n IBalancerVault.JoinPoolRequest memory joinRequest = IBalancerVault\n .JoinPoolRequest(assets, amounts, joinUserData, false);\n\n balancerVault.joinPool(\n balancerPoolId,\n address(this),\n address(this),\n joinRequest\n );\n\n uint256 bptTokenBalance = IERC20(poolAddress).balanceOf(address(this));\n\n // 2. Redeem as ETH\n bytes memory exitUserData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT,\n bptTokenBalance,\n 1\n );\n\n assets[1] = address(0); // Receive ETH instead of WETH\n uint256[] memory exitAmounts = new uint256[](2);\n exitAmounts[1] = 15 ether;\n IBalancerVault.ExitPoolRequest memory exitRequest = IBalancerVault\n .ExitPoolRequest(assets, exitAmounts, exitUserData, false);\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n payable(address(this)),\n exitRequest\n );\n bptTokenBalance = IERC20(poolAddress).balanceOf(address(this));\n }\n\n function getBPTExpected(address[] memory _assets, uint256[] memory _amounts)\n internal\n view\n virtual\n returns (uint256 bptExpected)\n {\n // Get the oracle from the OETH Vault\n address priceProvider = oethVault.priceProvider();\n\n for (uint256 i = 0; i < _assets.length; ++i) {\n uint256 strategyAssetMarketPrice = IOracle(priceProvider).price(\n _assets[i]\n );\n // convert asset amount to ETH amount\n bptExpected =\n bptExpected +\n _amounts[i].mulTruncate(strategyAssetMarketPrice);\n }\n\n uint256 bptRate = IRateProvider(poolAddress).getRate();\n // Convert ETH amount to BPT amount\n bptExpected = bptExpected.divPrecisely(bptRate);\n }\n\n function approveAllTokens() public {\n // Approve all tokens\n weth.approve(address(oethVault), type(uint256).max);\n reth.approve(poolAddress, type(uint256).max);\n weth.approve(poolAddress, type(uint256).max);\n reth.approve(address(balancerVault), type(uint256).max);\n weth.approve(address(balancerVault), type(uint256).max);\n }\n\n receive() external payable {\n // 3. Try to mint OETH\n oethVault.mint(address(weth), 1 ether, 0.9 ether);\n }\n}\n" + }, + "contracts/mocks/MockfrxETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockfrxETH is MintableERC20 {\n constructor() ERC20(\"frxETH\", \"frxETH\") {}\n}\n" + }, + "contracts/mocks/MockFrxETHMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockFrxETHMinter {\n address public immutable frxETH;\n address public immutable sfrxETH;\n\n constructor(address _frxETH, address _sfrxETH) {\n frxETH = _frxETH;\n sfrxETH = _sfrxETH;\n }\n\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares)\n {\n IMintableERC20(frxETH).mintTo(sfrxETH, msg.value);\n IMintableERC20(sfrxETH).mintTo(recipient, msg.value);\n shares = msg.value;\n }\n}\n" + }, + "contracts/mocks/MockLimitedWrappedOusd.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WrappedOusd } from \"../token/WrappedOusd.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MockLimitedWrappedOusd is WrappedOusd {\n constructor(ERC20 underlying_) WrappedOusd(underlying_) {}\n\n function maxDeposit(address)\n public\n view\n virtual\n override\n returns (uint256)\n {\n return 1e18;\n }\n}\n" + }, + "contracts/mocks/MockMaverickDistributor.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Pool } from \"../interfaces/plume/IMaverickV2Pool.sol\";\n\ncontract MockMaverickDistributor {\n IERC20 public immutable rewardToken;\n\n uint256 public lastEpoch;\n uint256 public rewardAmount;\n\n constructor(address _rewardToken) {\n rewardToken = IERC20(_rewardToken);\n }\n\n function setLastEpoch(uint256 _epoch) external {\n lastEpoch = _epoch;\n }\n\n function setRewardTokenAmount(uint256 _amount) external {\n rewardAmount = _amount;\n }\n\n function claimLp(\n address recipient,\n uint256,\n IMaverickV2Pool,\n uint32[] memory,\n uint256\n ) external returns (uint256 amount) {\n rewardToken.transfer(recipient, rewardAmount);\n return rewardAmount;\n }\n}\n" + }, + "contracts/mocks/MockMetadataToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// IERC20Metadata is used in the resolveAsset function in contracts/utils/assets.js\n// We just need to import it here to make its ABI available to Hardhat\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n" + }, + "contracts/mocks/MockMintableUniswapPair.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport \"./MockUniswapPair.sol\";\n\ncontract MockMintableUniswapPair is MockUniswapPair, MintableERC20 {\n constructor(\n address _token0,\n address _token1,\n uint112 _reserve0,\n uint112 _reserve1\n )\n MockUniswapPair(_token0, _token1, _reserve0, _reserve1)\n ERC20(\"Uniswap V2\", \"UNI-v2\")\n {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/MockNonRebasing.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { OUSD } from \"../token/OUSD.sol\";\n\ncontract MockNonRebasing {\n OUSD oUSD;\n\n function setOUSD(address _oUSDAddress) public {\n oUSD = OUSD(_oUSDAddress);\n }\n\n function rebaseOptIn() public {\n oUSD.rebaseOptIn();\n }\n\n function rebaseOptOut() public {\n oUSD.rebaseOptOut();\n }\n\n function transfer(address _to, uint256 _value) public {\n oUSD.transfer(_to, _value);\n }\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) public {\n oUSD.transferFrom(_from, _to, _value);\n }\n\n function approve(address _spender, uint256 _addedValue) public {\n oUSD.approve(_spender, _addedValue);\n }\n\n function mintOusd(\n address _vaultContract,\n address _asset,\n uint256 _amount\n ) public {\n IVault(_vaultContract).mint(_asset, _amount, 0);\n }\n\n function redeemOusd(address _vaultContract, uint256 _amount) public {\n IVault(_vaultContract).redeem(_amount, 0);\n }\n\n function approveFor(\n address _contract,\n address _spender,\n uint256 _addedValue\n ) public {\n IERC20(_contract).approve(_spender, _addedValue);\n }\n}\n" + }, + "contracts/mocks/MockNonStandardToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\nimport \"./MintableERC20.sol\";\n\n/**\n * Mock token contract to simulate tokens that don't\n * throw/revert when a transfer/transferFrom call fails\n */\ncontract MockNonStandardToken is MintableERC20 {\n using SafeMath for uint256;\n\n constructor() ERC20(\"NonStandardToken\", \"NonStandardToken\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n\n function transfer(address recipient, uint256 amount)\n public\n override\n returns (bool)\n {\n if (balanceOf(msg.sender) < amount) {\n // Fail silently\n return false;\n }\n\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public override returns (bool) {\n if (balanceOf(sender) < amount) {\n // Fail silently\n return false;\n }\n\n _transfer(sender, recipient, amount);\n _approve(\n sender,\n _msgSender(),\n allowance(sender, _msgSender()).sub(\n amount,\n \"ERC20: transfer amount exceeds allowance\"\n )\n );\n return true;\n }\n}\n" + }, + "contracts/mocks/MockOETHVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultCore } from \"../vault/OETHVaultCore.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MockOETHVault is OETHVaultCore {\n using StableMath for uint256;\n\n constructor(address _weth) OETHVaultCore(_weth) {\n _setGovernor(msg.sender);\n }\n\n function supportAsset(address asset) external {\n assets[asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(0),\n decimals: 18,\n allowedOracleSlippageBps: 0\n });\n\n allAssets.push(asset);\n }\n}\n" + }, + "contracts/mocks/MockOETHVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"../vault/OETHVaultAdmin.sol\";\n\ncontract MockOETHVaultAdmin is OETHVaultAdmin {\n constructor(address _weth) OETHVaultAdmin(_weth) {}\n\n // fetches the WETH amount in outstanding withdrawals\n function outstandingWithdrawalsAmount()\n external\n view\n returns (uint256 wethAmount)\n {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n wethAmount = queue.queued - queue.claimed;\n }\n\n function wethAvailable() external view returns (uint256) {\n return _wethAvailable();\n }\n}\n" + }, + "contracts/mocks/MockOGN.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./BurnableERC20.sol\";\nimport \"./MintableERC20.sol\";\n\n/**\n * @title Origin token (OGN).\n *\n * @dev Token that allows minting and burning.\n * @dev Important note:\n * @dev There is a known race condition in the ERC20 standard on the approve() method.\n * @dev See details: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n * @dev The Origin token contract implements the increaseApproval() and decreaseApproval() methods.\n * @dev It is strongly recommended to use those methods rather than approve()\n * @dev when updating the token allowance.\n */\ncontract MockOGN is MintableERC20, BurnableERC20 {\n event SetWhitelistExpiration(uint256 expiration);\n event AllowedTransactorAdded(address sender);\n event AllowedTransactorRemoved(address sender);\n event AddCallSpenderWhitelist(address enabler, address spender);\n event RemoveCallSpenderWhitelist(address disabler, address spender);\n\n mapping(address => bool) public callSpenderWhitelist;\n address public owner = msg.sender;\n // UNIX timestamp (in seconds) after which this whitelist no longer applies\n uint256 public whitelistExpiration;\n // While the whitelist is active, either the sender or recipient must be\n // in allowedTransactors.\n mapping(address => bool) public allowedTransactors;\n\n // @dev Constructor that gives msg.sender all initial tokens.\n constructor(uint256 _initialSupply) ERC20(\"OriginToken\", \"OGN\") {\n owner = msg.sender;\n _mint(owner, _initialSupply);\n }\n\n //\n // approveAndCall methods\n //\n\n // @dev Add spender to whitelist of spenders for approveAndCall\n // @param _spender Address to add\n function addCallSpenderWhitelist(address _spender) public onlyOwner {\n callSpenderWhitelist[_spender] = true;\n emit AddCallSpenderWhitelist(msg.sender, _spender);\n }\n\n // @dev Remove spender from whitelist of spenders for approveAndCall\n // @param _spender Address to remove\n function removeCallSpenderWhitelist(address _spender) public onlyOwner {\n delete callSpenderWhitelist[_spender];\n emit RemoveCallSpenderWhitelist(msg.sender, _spender);\n }\n\n // @dev Approve transfer of tokens and make a contract call in a single\n // @dev transaction. This allows a DApp to avoid requiring two MetaMask\n // @dev approvals for a single logical action, such as creating a listing,\n // @dev which requires the seller to approve a token transfer and the\n // @dev marketplace contract to transfer tokens from the seller.\n //\n // @dev This is based on the ERC827 function approveAndCall and avoids\n // @dev security issues by only working with a whitelisted set of _spender\n // @dev addresses. The other difference is that the combination of this\n // @dev function ensures that the proxied function call receives the\n // @dev msg.sender for this function as its first parameter.\n //\n // @param _spender The address that will spend the funds.\n // @param _value The amount of tokens to be spent.\n // @param _selector Function selector for function to be called.\n // @param _callParams Packed, encoded parameters, omitting the first parameter which is always msg.sender\n function approveAndCallWithSender(\n address _spender,\n uint256 _value,\n bytes4 _selector,\n bytes memory _callParams\n ) public payable returns (bool) {\n require(_spender != address(this), \"token contract can't be approved\");\n require(callSpenderWhitelist[_spender], \"spender not in whitelist\");\n\n require(super.approve(_spender, _value), \"approve failed\");\n\n bytes memory callData = abi.encodePacked(\n _selector,\n uint256(uint160(msg.sender)),\n _callParams\n );\n // solium-disable-next-line security/no-call-value\n (bool success, ) = _spender.call{ value: msg.value }(callData);\n require(success, \"proxied call failed\");\n return true;\n }\n\n //\n // Functions for maintaining whitelist\n //\n\n modifier onlyOwner() {\n require(msg.sender == owner);\n _;\n }\n modifier allowedTransfer(address _from, address _to) {\n require(\n // solium-disable-next-line operator-whitespace\n !whitelistActive() ||\n allowedTransactors[_from] ||\n allowedTransactors[_to],\n \"neither sender nor recipient are allowed\"\n );\n _;\n }\n\n function whitelistActive() public view returns (bool) {\n return block.timestamp < whitelistExpiration;\n }\n\n function addAllowedTransactor(address _transactor) public onlyOwner {\n emit AllowedTransactorAdded(_transactor);\n allowedTransactors[_transactor] = true;\n }\n\n function removeAllowedTransactor(address _transactor) public onlyOwner {\n emit AllowedTransactorRemoved(_transactor);\n delete allowedTransactors[_transactor];\n }\n\n /**\n * @dev Set the whitelist expiration, after which the whitelist no longer\n * applies.\n */\n function setWhitelistExpiration(uint256 _expiration) public onlyOwner {\n // allow only if whitelist expiration hasn't yet been set, or if the\n // whitelist expiration hasn't passed yet\n require(\n whitelistExpiration == 0 || whitelistActive(),\n \"an expired whitelist cannot be extended\"\n );\n // prevent possible mistakes in calling this function\n require(\n _expiration >= block.timestamp + 1 days,\n \"whitelist expiration not far enough into the future\"\n );\n emit SetWhitelistExpiration(_expiration);\n whitelistExpiration = _expiration;\n }\n\n //\n // ERC20 transfer functions that have been overridden to enforce the\n // whitelist.\n //\n\n function transfer(address _to, uint256 _value)\n public\n override\n allowedTransfer(msg.sender, _to)\n returns (bool)\n {\n return super.transfer(_to, _value);\n }\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) public override allowedTransfer(_from, _to) returns (bool) {\n return super.transferFrom(_from, _to, _value);\n }\n}\n" + }, + "contracts/mocks/MockOGV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockOGV is MintableERC20 {\n constructor() ERC20(\"OGV\", \"OGV\") {}\n}\n" + }, + "contracts/mocks/MockOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/IPriceOracle.sol\";\nimport \"../interfaces/IMinMaxOracle.sol\";\n\n/**\n * Mock of both price Oracle and min max oracles\n */\ncontract MockOracle is IPriceOracle, IMinMaxOracle {\n mapping(bytes32 => uint256) prices;\n mapping(bytes32 => uint256[]) pricesMinMax;\n uint256 ethMin;\n uint256 ethMax;\n\n /**\n * @dev returns the asset price in USD, 6 decimal digits.\n * Compatible with the Open Price Feed.\n */\n function price(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n return prices[keccak256(abi.encodePacked(symbol))];\n }\n\n /**\n * @dev sets the price of the asset in USD, 6 decimal digits\n *\n */\n function setPrice(string calldata symbol, uint256 _price) external {\n prices[keccak256(abi.encodePacked(symbol))] = _price;\n }\n\n /**\n * @dev sets the min and max price of ETH in USD, 6 decimal digits\n *\n */\n function setEthPriceMinMax(uint256 _min, uint256 _max) external {\n ethMin = _min;\n ethMax = _max;\n }\n\n /**\n * @dev sets the prices Min Max for a specific symbol in ETH, 8 decimal digits\n *\n */\n function setTokPriceMinMax(\n string calldata symbol,\n uint256 _min,\n uint256 _max\n ) external {\n pricesMinMax[keccak256(abi.encodePacked(symbol))] = [_min, _max];\n }\n\n /**\n * @dev get the price of asset in ETH, 8 decimal digits.\n */\n function priceMin(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n uint256[] storage pMinMax = pricesMinMax[\n keccak256(abi.encodePacked(symbol))\n ];\n return (pMinMax[0] * ethMin) / 1e6;\n }\n\n /**\n * @dev get the price of asset in USD, 8 decimal digits.\n * Not needed for now\n */\n function priceMax(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n uint256[] storage pMinMax = pricesMinMax[\n keccak256(abi.encodePacked(symbol))\n ];\n return (pMinMax[1] * ethMax) / 1e6;\n }\n}\n" + }, + "contracts/mocks/MockOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"../oracle/AbstractOracleRouter.sol\";\n\n// @notice Oracle Router required for testing environment\ncontract MockOracleRouter is AbstractOracleRouter {\n struct FeedMetadata {\n address feedAddress;\n uint256 maxStaleness;\n }\n\n mapping(address => FeedMetadata) public assetToFeedMetadata;\n\n /* @dev Override feed and maxStaleness information for a particular asset\n * @param _asset the asset to override feed for\n * @param _feed new feed\n * @param _maxStaleness new maximum time allowed for feed data to be stale\n */\n function setFeed(\n address _asset,\n address _feed,\n uint256 _maxStaleness\n ) external {\n assetToFeedMetadata[_asset] = FeedMetadata(_feed, _maxStaleness);\n }\n\n /*\n * The dev version of the Oracle doesn't need to gas optimize and cache the decimals\n */\n function getDecimals(address _feed) internal view override returns (uint8) {\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n return AggregatorV3Interface(_feed).decimals();\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n FeedMetadata storage fm = assetToFeedMetadata[asset];\n feedAddress = fm.feedAddress;\n maxStaleness = fm.maxStaleness;\n }\n}\n" + }, + "contracts/mocks/MockOracleRouterNoStale.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { OracleRouter } from \"../oracle/OracleRouter.sol\";\nimport { OETHOracleRouter } from \"../oracle/OETHOracleRouter.sol\";\n\n// @notice Oracle Router used to bypass staleness\ncontract MockOracleRouterNoStale is OracleRouter {\n function feedMetadata(address asset)\n internal\n pure\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n (feedAddress, ) = super.feedMetadata(asset);\n maxStaleness = 365 days;\n }\n}\n\n// @notice Oracle Router used to bypass staleness\ncontract MockOETHOracleRouterNoStale is OETHOracleRouter {\n constructor() OETHOracleRouter() {}\n\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n (feedAddress, ) = super.feedMetadata(asset);\n maxStaleness = 365 days;\n }\n}\n" + }, + "contracts/mocks/MockOracleWeightedPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Variable, OracleAverageQuery, IOracleWeightedPool } from \"../interfaces/balancer/IOracleWeightedPool.sol\";\n\ncontract MockOracleWeightedPool is IOracleWeightedPool {\n uint256[] public nextResults;\n\n constructor() {\n nextResults = [1 ether, 1 ether];\n }\n\n function getTimeWeightedAverage(OracleAverageQuery[] memory)\n external\n view\n override\n returns (uint256[] memory results)\n {\n return nextResults;\n }\n\n function setNextResults(uint256[] calldata results) external {\n nextResults = results;\n }\n}\n" + }, + "contracts/mocks/MockRebornMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n// solhint-disable-next-line no-console\nimport \"hardhat/console.sol\";\n\ncontract Sanctum {\n address public asset;\n address public vault;\n address public reborner;\n bool public shouldAttack = false;\n // should selfdestruct in the constructor\n bool public shouldDestruct = false;\n uint256 public targetMethod;\n address public ousdContract;\n\n constructor(address _asset, address _vault) {\n asset = _asset;\n vault = _vault;\n }\n\n function deploy(uint256 salt, bytes memory bytecode)\n public\n returns (address addr)\n {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt)\n }\n require(addr != address(0), \"Create2: Failed on deploy\");\n }\n\n function computeAddress(uint256 salt, bytes memory bytecode)\n public\n view\n returns (address)\n {\n bytes32 bytecodeHashHash = keccak256(bytecode);\n bytes32 _data = keccak256(\n abi.encodePacked(\n bytes1(0xff),\n address(this),\n salt,\n bytecodeHashHash\n )\n );\n return address(bytes20(_data << 96));\n }\n\n function setShouldAttack(bool _shouldAttack) public {\n shouldAttack = _shouldAttack;\n }\n\n // should call selfdestruct in the constructor\n function setShouldDesctruct(bool _shouldDestruct) public {\n shouldDestruct = _shouldDestruct;\n }\n\n function setTargetMethod(uint256 target) public {\n targetMethod = target;\n }\n\n function setOUSDAddress(address _ousdContract) public {\n ousdContract = _ousdContract;\n }\n}\n\ncontract Reborner {\n Sanctum sanctum;\n bool logging = false;\n\n constructor(address _sanctum) {\n log(\"We are created...\");\n sanctum = Sanctum(_sanctum);\n if (sanctum.shouldAttack()) {\n log(\"We are attacking now...\");\n\n uint256 target = sanctum.targetMethod();\n\n if (target == 1) {\n redeem();\n } else if (target == 2) {\n transfer();\n } else {\n mint();\n }\n }\n\n if (sanctum.shouldDestruct()) {\n bye();\n }\n }\n\n function mint() public {\n log(\"We are attempting to mint..\");\n address asset = sanctum.asset();\n address vault = sanctum.vault();\n IERC20(asset).approve(vault, 1e18);\n IVault(vault).mint(asset, 1e18, 0);\n log(\"We are now minting..\");\n }\n\n function redeem() public {\n log(\"We are attempting to redeem..\");\n address vault = sanctum.vault();\n IVault(vault).redeem(1e18, 1e18);\n log(\"We are now redeeming..\");\n }\n\n function transfer() public {\n log(\"We are attempting to transfer..\");\n address ousd = sanctum.ousdContract();\n require(IERC20(ousd).transfer(address(1), 1e18), \"transfer failed\");\n log(\"We are now transfering..\");\n }\n\n function bye() public {\n log(\"We are now destructing..\");\n selfdestruct(payable(msg.sender));\n }\n\n function log(string memory message) internal view {\n if (logging) {\n // solhint-disable-next-line no-console\n console.log(message);\n }\n }\n}\n" + }, + "contracts/mocks/MockRETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport \"../interfaces/IGetExchangeRateToken.sol\";\n\ncontract MockRETH is MintableERC20, IGetExchangeRateToken {\n uint256 private exchangeRate = 12e17;\n\n constructor() ERC20(\"Rocket Pool ETH\", \"rETH\") {}\n\n function getExchangeRate() external view override returns (uint256) {\n return exchangeRate;\n }\n\n function setExchangeRate(uint256 _rate) external {\n exchangeRate = _rate;\n }\n}\n" + }, + "contracts/mocks/MockRoosterAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Rooster AMO strategy exposing extra functionality\n * @author Origin Protocol Inc\n */\n\nimport { RoosterAMOStrategy } from \"../strategies/plume/RoosterAMOStrategy.sol\";\nimport { IMaverickV2Pool } from \"../interfaces/plume/IMaverickV2Pool.sol\";\n\ncontract MockRoosterAMOStrategy is RoosterAMOStrategy {\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethpAddress,\n address _liquidityManager,\n address _poolLens,\n address _maverickPosition,\n address _maverickQuoter,\n address _mPool,\n bool _upperTickAtParity,\n address _votingDistributor,\n address _poolDistributor\n )\n RoosterAMOStrategy(\n _stratConfig,\n _wethAddress,\n _oethpAddress,\n _liquidityManager,\n _poolLens,\n _maverickPosition,\n _maverickQuoter,\n _mPool,\n _upperTickAtParity,\n _votingDistributor,\n _poolDistributor\n )\n {}\n\n function getCurrentWethShare() external view returns (uint256) {\n uint256 _currentPrice = getPoolSqrtPrice();\n\n return _getWethShare(_currentPrice);\n }\n}\n" + }, + "contracts/mocks/MockSFC.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockSFC {\n error ZeroAmount();\n error TransferFailed();\n error StakeIsFullySlashed();\n\n // Mapping of delegator address to validator ID to amount delegated\n mapping(address => mapping(uint256 => uint256)) public delegations;\n // Mapping of delegator address to validator ID to withdrawal request ID to amount\n mapping(address => mapping(uint256 => mapping(uint256 => uint256)))\n public withdraws;\n // validator ID -> slashing refund ratio (allows to withdraw slashed stake)\n mapping(uint256 => uint256) public slashingRefundRatio;\n\n function getStake(address delegator, uint256 validatorID)\n external\n view\n returns (uint256)\n {\n return delegations[delegator][validatorID];\n }\n\n function delegate(uint256 validatorID) external payable {\n if (msg.value == 0) {\n revert ZeroAmount();\n }\n delegations[msg.sender][validatorID] += msg.value;\n }\n\n function undelegate(\n uint256 validatorID,\n uint256 wrID,\n uint256 amount\n ) external {\n require(\n delegations[msg.sender][validatorID] >= amount,\n \"insufficient stake\"\n );\n require(\n withdraws[msg.sender][validatorID][wrID] == 0,\n \"withdrawal request already exists\"\n );\n\n delegations[msg.sender][validatorID] -= amount;\n withdraws[msg.sender][validatorID][wrID] = amount;\n }\n\n function withdraw(uint256 validatorID, uint256 wrID) external {\n require(withdraws[msg.sender][validatorID][wrID] > 0, \"no withdrawal\");\n\n uint256 withdrawAmount = withdraws[msg.sender][validatorID][wrID];\n uint256 penalty = (withdrawAmount *\n (1e18 - slashingRefundRatio[validatorID])) / 1e18;\n\n if (penalty >= withdrawAmount) {\n revert StakeIsFullySlashed();\n }\n\n (bool sent, ) = msg.sender.call{ value: withdrawAmount - penalty }(\"\");\n if (!sent) {\n revert TransferFailed();\n }\n }\n\n function pendingRewards(address delegator, uint256 validatorID)\n external\n view\n returns (uint256)\n {}\n\n function claimRewards(uint256 validatorID) external {}\n\n function restakeRewards(uint256 validatorID) external {}\n\n /// @param refundRatio the percentage of the staked amount that can be refunded. 0.1e18 = 10%, 1e18 = 100%\n function slashValidator(uint256 validatorID, uint256 refundRatio) external {\n require(refundRatio <= 1e18, \"invalid refund ratio\");\n slashingRefundRatio[validatorID] = refundRatio;\n }\n}\n" + }, + "contracts/mocks/MocksfrxETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MocksfrxETH is MintableERC20 {\n address public frxETH;\n\n constructor(address _frxETH) ERC20(\"sfrxETH\", \"sfrxETH\") {\n frxETH = _frxETH;\n }\n\n function setMockfrxETHAddress(address _frxETH) external {\n frxETH = _frxETH;\n }\n\n function deposit(uint256 assets, address receiver)\n external\n returns (uint256 shares)\n {\n ERC20(frxETH).transferFrom(msg.sender, address(this), assets);\n\n _mint(receiver, assets);\n\n return assets;\n }\n\n function maxWithdraw(address owner) external view returns (uint256) {\n return balanceOf(owner);\n }\n\n function setMaxWithdrawableBalance(address owner, uint256 balance)\n external\n {\n uint256 currentBalance = balanceOf(owner);\n if (currentBalance > balance) {\n _burn(owner, currentBalance - balance);\n } else if (balance > currentBalance) {\n _mint(owner, balance - currentBalance);\n }\n }\n\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets) {\n _burn(owner, shares);\n\n ERC20(frxETH).transfer(receiver, shares);\n\n assets = shares;\n }\n\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares) {\n _burn(owner, assets);\n\n ERC20(frxETH).transfer(receiver, assets);\n\n shares = assets;\n }\n\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares)\n {\n _mint(recipient, msg.value);\n shares = msg.value;\n }\n}\n" + }, + "contracts/mocks/MockSSV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockSSV is MintableERC20 {\n constructor() ERC20(\"SSV Token\", \"SSV\") {}\n}\n" + }, + "contracts/mocks/MockSSVNetwork.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Cluster } from \"./../interfaces/ISSVNetwork.sol\";\n\ncontract MockSSVNetwork {\n function registerValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function exitValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds\n ) external {}\n\n function removeValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external {}\n\n function deposit(\n address clusterOwner,\n uint64[] calldata operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function setFeeRecipientAddress(address recipient) external {}\n}\n" + }, + "contracts/mocks/MockstETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockstETH is MintableERC20 {\n constructor() ERC20(\"stETH\", \"stETH\") {}\n}\n" + }, + "contracts/mocks/MockStkAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"./MintableERC20.sol\";\n\ncontract MockStkAave is MintableERC20 {\n uint256 public COOLDOWN_SECONDS = 864000;\n uint256 public UNSTAKE_WINDOW = 172800;\n address public STAKED_TOKEN;\n\n mapping(address => uint256) public stakerRewardsToClaim;\n mapping(address => uint256) public stakersCooldowns;\n\n using SafeERC20 for IERC20;\n\n constructor(address _stakedToken) ERC20(\"Staked Aave\", \"stkAAVE\") {\n STAKED_TOKEN = _stakedToken;\n }\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n\n function setStakedToken(address _stakedToken) external {\n STAKED_TOKEN = _stakedToken;\n }\n\n /**\n * @dev Redeems staked tokens, and stop earning rewards\n * @param to Address to redeem to\n * @param amount Amount to redeem\n **/\n function redeem(address to, uint256 amount) external {\n uint256 cooldownStartTimestamp = stakersCooldowns[msg.sender];\n uint256 windowStart = cooldownStartTimestamp + COOLDOWN_SECONDS;\n require(amount != 0, \"INVALID_ZERO_AMOUNT\");\n require(block.timestamp > windowStart, \"INSUFFICIENT_COOLDOWN\");\n require(\n block.timestamp - windowStart <= UNSTAKE_WINDOW,\n \"UNSTAKE_WINDOW_FINISHED\"\n );\n uint256 balanceOfMessageSender = balanceOf(msg.sender);\n uint256 amountToRedeem = (amount > balanceOfMessageSender)\n ? balanceOfMessageSender\n : amount;\n\n stakersCooldowns[msg.sender] = 0;\n _burn(msg.sender, amountToRedeem);\n IERC20(STAKED_TOKEN).safeTransfer(to, amountToRedeem);\n }\n\n /**\n * @dev Activates the cooldown period to unstake\n * - It can't be called if the user is not staking\n **/\n function cooldown() external {\n require(balanceOf(msg.sender) != 0, \"INVALID_BALANCE_ON_COOLDOWN\");\n stakersCooldowns[msg.sender] = block.timestamp;\n }\n\n /**\n * @dev Test helper function to allow changing the cooldown\n **/\n function setCooldown(address account, uint256 _cooldown) external {\n stakersCooldowns[account] = _cooldown;\n }\n}\n" + }, + "contracts/mocks/MockStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract MockStrategy {\n address[] public assets;\n\n address public withdrawAllAsset;\n address public withdrawAllRecipient;\n\n constructor() {}\n\n function deposit(address asset, uint256 amount) external {}\n\n function depositAll() external {}\n\n function withdraw(\n address recipient,\n address asset,\n uint256 amount\n ) external {\n IERC20(asset).transfer(recipient, amount);\n }\n\n function withdrawAll() external {\n IERC20(withdrawAllAsset).transfer(\n withdrawAllRecipient,\n IERC20(withdrawAllAsset).balanceOf(address(this))\n );\n }\n\n function checkBalance(address asset)\n external\n view\n returns (uint256 balance)\n {\n balance = IERC20(asset).balanceOf(address(this));\n }\n\n function supportsAsset(address) external view returns (bool) {\n return true;\n }\n\n function collectRewardTokens() external {}\n\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return new address[](0);\n }\n\n function setWithdrawAll(address asset, address recipient) external {\n withdrawAllAsset = asset;\n withdrawAllRecipient = recipient;\n }\n}\n" + }, + "contracts/mocks/MockSwapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IMintableERC20 } from \"./MintableERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract MockSwapper {\n uint256 public nextOutAmount;\n\n function swap(\n // solhint-disable-next-line no-unused-vars\n address _fromAsset,\n address _toAsset,\n // solhint-disable-next-line no-unused-vars\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n // solhint-disable-next-line no-unused-vars\n bytes calldata _data\n ) external returns (uint256 toAssetAmount) {\n toAssetAmount = (nextOutAmount > 0) ? nextOutAmount : _minToAssetAmount;\n nextOutAmount = 0;\n IMintableERC20(_toAsset).mint(toAssetAmount);\n IERC20(_toAsset).transfer(msg.sender, toAssetAmount);\n }\n\n function setNextOutAmount(uint256 _nextOutAmount) public {\n nextOutAmount = _nextOutAmount;\n }\n}\n" + }, + "contracts/mocks/MockTUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockTUSD is MintableERC20 {\n constructor() ERC20(\"TrueUSD\", \"TUSD\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/MockUniswapPair.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IUniswapV2Pair } from \"../interfaces/uniswap/IUniswapV2Pair.sol\";\n\ncontract MockUniswapPair is IUniswapV2Pair {\n address tok0;\n address tok1;\n uint112 reserve0;\n uint112 reserve1;\n uint256 blockTimestampLast;\n\n bool public hasSynced = false;\n\n constructor(\n address _token0,\n address _token1,\n uint112 _reserve0,\n uint112 _reserve1\n ) {\n tok0 = _token0;\n tok1 = _token1;\n reserve0 = _reserve0;\n reserve1 = _reserve1;\n blockTimestampLast = block.timestamp;\n }\n\n function token0() external view override returns (address) {\n return tok0;\n }\n\n function token1() external view override returns (address) {\n return tok1;\n }\n\n function getReserves()\n external\n view\n override\n returns (\n uint112,\n uint112,\n uint32\n )\n {\n return (reserve0, reserve1, uint32(blockTimestampLast));\n }\n\n function setReserves(uint112 _reserve0, uint112 _reserve1) public {\n reserve0 = _reserve0;\n reserve1 = _reserve1;\n blockTimestampLast = block.timestamp;\n }\n\n // CAUTION This will not work if you setReserves multiple times over\n // multiple different blocks because then it wouldn't be a continuous\n // reserve factor over that blockTimestamp, this assumes an even reserve\n // ratio all the way through\n function price0CumulativeLast() external view override returns (uint256) {\n return\n uint256(FixedPoint.fraction(reserve1, reserve0)._x) *\n blockTimestampLast;\n }\n\n function price1CumulativeLast() external view override returns (uint256) {\n return\n uint256(FixedPoint.fraction(reserve0, reserve1)._x) *\n blockTimestampLast;\n }\n\n function sync() external override {\n hasSynced = true;\n }\n\n function checkHasSynced() external view {\n require(hasSynced, \"Not synced\");\n }\n}\n\n// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))\nlibrary FixedPoint {\n // range: [0, 2**112 - 1]\n // resolution: 1 / 2**112\n struct uq112x112 {\n uint224 _x;\n }\n\n // returns a uq112x112 which represents the ratio of the numerator to the denominator\n // equivalent to encode(numerator).div(denominator)\n function fraction(uint112 numerator, uint112 denominator)\n internal\n pure\n returns (uq112x112 memory)\n {\n require(denominator > 0, \"FixedPoint: DIV_BY_ZERO\");\n return uq112x112((uint224(numerator) << 112) / denominator);\n }\n}\n" + }, + "contracts/mocks/MockUniswapRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { IUniswapV2Router } from \"../interfaces/uniswap/IUniswapV2Router02.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockUniswapRouter is IUniswapV2Router {\n using StableMath for uint256;\n\n mapping(address => address) public pairMaps;\n uint256 public slippage = 1 ether;\n\n function initialize(\n address[] calldata _0tokens,\n address[] calldata _1tokens\n ) public {\n require(\n _0tokens.length == _1tokens.length,\n \"Mock token pairs should be of the same length\"\n );\n for (uint256 i = 0; i < _0tokens.length; i++) {\n pairMaps[_0tokens[i]] = _1tokens[i];\n }\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n\n function swapExactTokensForTokens(\n uint256 amountIn,\n uint256 amountOutMin,\n address[] calldata path,\n address to,\n // solhint-disable-next-line no-unused-vars\n uint256\n ) external override returns (uint256[] memory amountsOut) {\n address tok0 = path[0];\n address tok1 = path[path.length - 1];\n\n uint256 amountOut = (amountOutMin * slippage) / 1 ether;\n require(amountOut >= amountOutMin, \"Slippage error\");\n\n IERC20(tok0).transferFrom(msg.sender, address(this), amountIn);\n MintableERC20(tok1).mintTo(to, amountOut);\n\n amountsOut = new uint256[](path.length);\n amountsOut[path.length - 1] = amountOut;\n }\n\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut)\n {\n (address tok0, address tok1) = _getFirstAndLastToken(params.path);\n\n amountOut = (params.amountOutMinimum * slippage) / 1 ether;\n\n IERC20(tok0).transferFrom(msg.sender, address(this), params.amountIn);\n MintableERC20(tok1).mintTo(params.recipient, amountOut);\n\n require(\n amountOut >= params.amountOutMinimum,\n \"UniswapMock: amountOut less than amountOutMinimum\"\n );\n return amountOut;\n }\n\n function addLiquidity(\n address tokenA,\n address tokenB,\n uint256 amountADesired,\n uint256 amountBDesired,\n uint256 amountAMin,\n uint256 amountBMin,\n address to,\n uint256 deadline\n )\n external\n override\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 liquidity\n )\n {\n // this is needed to make this contract whole else it'd be just virtual\n }\n\n function WETH() external pure override returns (address) {\n return address(0);\n }\n\n // Universal router mock\n function execute(\n bytes calldata,\n bytes[] calldata inputs,\n uint256\n ) external payable {\n uint256 inLen = inputs.length;\n for (uint256 i = 0; i < inLen; ++i) {\n (\n address recipient,\n ,\n uint256 amountOutMinimum,\n bytes memory path,\n\n ) = abi.decode(inputs[i], (address, uint256, uint256, bytes, bool));\n\n (address token0, address token1) = _getFirstAndLastToken(path);\n\n amountOutMinimum = amountOutMinimum.scaleBy(\n Helpers.getDecimals(token0),\n Helpers.getDecimals(token1)\n );\n\n MintableERC20(token1).mintTo(recipient, amountOutMinimum);\n }\n }\n\n function _getFirstAndLastToken(bytes memory path)\n internal\n view\n returns (address token0, address token1)\n {\n bytes memory tok0Bytes = new bytes(20);\n for (uint256 j = 0; j < 20; ++j) {\n tok0Bytes[j] = path[j];\n }\n token0 = address(bytes20(tok0Bytes));\n\n if (pairMaps[token0] != address(0)) {\n token0 = pairMaps[token0];\n }\n\n bytes memory tok1Bytes = new bytes(20);\n uint256 tok1Offset = path.length - 20;\n for (uint256 j = 0; j < 20; ++j) {\n tok1Bytes[j] = path[j + tok1Offset];\n }\n token1 = address(bytes20(tok1Bytes));\n\n if (pairMaps[token1] != address(0)) {\n token1 = pairMaps[token1];\n }\n }\n}\n" + }, + "contracts/mocks/MockUSDC.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDC is MintableERC20 {\n constructor() ERC20(\"USDC Coin\", \"USDC\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n}\n" + }, + "contracts/mocks/MockUSDS.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDS is MintableERC20 {\n constructor() ERC20(\"USDS\", \"USDS\") {}\n}\n" + }, + "contracts/mocks/MockUSDT.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDT is MintableERC20 {\n constructor() ERC20(\"USDT Coin\", \"USDT\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n}\n" + }, + "contracts/mocks/MockVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultCore } from \"../vault/VaultCore.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { VaultInitializer } from \"../vault/VaultInitializer.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MockVault is VaultCore {\n using StableMath for uint256;\n\n uint256 storedTotalValue;\n\n function setTotalValue(uint256 _value) public {\n storedTotalValue = _value;\n }\n\n function totalValue() external view override returns (uint256) {\n return storedTotalValue;\n }\n\n function _totalValue() internal view override returns (uint256) {\n return storedTotalValue;\n }\n\n function _checkBalance(address _asset)\n internal\n view\n override\n returns (uint256 balance)\n {\n // Avoids rounding errors by returning the total value\n // in a single currency\n if (allAssets[0] == _asset) {\n uint256 decimals = Helpers.getDecimals(_asset);\n return storedTotalValue.scaleBy(decimals, 18);\n } else {\n return 0;\n }\n }\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external onlyGovernor {\n maxSupplyDiff = _maxSupplyDiff;\n }\n}\n" + }, + "contracts/mocks/MockVaultCoreInstantRebase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultCore } from \"../vault/VaultCore.sol\";\n\ncontract MockVaultCoreInstantRebase is VaultCore {\n function _nextYield(uint256 supply, uint256 vaultValue)\n internal\n view\n override\n returns (uint256 yield, uint256 targetRate)\n {\n if (vaultValue <= supply) {\n return (0, 0);\n }\n yield = vaultValue - supply;\n return (yield, 0);\n }\n}\n" + }, + "contracts/mocks/MockWETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\n// Just importing to \"unbreak\" coverage tests\nimport { IERC721Receiver } from \"@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol\";\n\ncontract MockWETH is MintableERC20 {\n constructor() ERC20(\"WETH\", \"WETH\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/MockWS.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockWS is MintableERC20 {\n constructor() ERC20(\"Wrapped Sonic\", \"wS\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/TestUpgradedOUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../token/OUSD.sol\";\n\n// used to alter internal state of OUSD contract\ncontract TestUpgradedOUSD is OUSD {\n constructor() OUSD() {}\n\n function overwriteCreditBalances(address _account, uint256 _creditBalance)\n public\n {\n creditBalances[_account] = _creditBalance;\n }\n\n function overwriteAlternativeCPT(address _account, uint256 _acpt) public {\n alternativeCreditsPerToken[_account] = _acpt;\n }\n\n function overwriteRebaseState(address _account, RebaseOptions _rebaseOption)\n public\n {\n rebaseState[_account] = _rebaseOption;\n }\n}\n" + }, + "contracts/oracle/AbstractOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\n// @notice Abstract functionality that is shared between various Oracle Routers\nabstract contract AbstractOracleRouter is IOracle {\n using StableMath for uint256;\n using SafeCast for int256;\n\n uint256 internal constant MIN_DRIFT = 0.7e18;\n uint256 internal constant MAX_DRIFT = 1.3e18;\n address internal constant FIXED_PRICE =\n 0x0000000000000000000000000000000000000001;\n // Maximum allowed staleness buffer above normal Oracle maximum staleness\n uint256 internal constant STALENESS_BUFFER = 1 days;\n mapping(address => uint8) internal decimalsCache;\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n returns (address feedAddress, uint256 maxStaleness);\n\n /**\n * @notice Returns the total price in 18 digit unit for a given asset.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n if (shouldBePegged(asset)) {\n require(_price <= MAX_DRIFT, \"Oracle: Price exceeds max\");\n require(_price >= MIN_DRIFT, \"Oracle: Price under min\");\n }\n return _price;\n }\n\n function getDecimals(address _feed) internal view virtual returns (uint8) {\n uint8 decimals = decimalsCache[_feed];\n require(decimals > 0, \"Oracle: Decimals not cached\");\n return decimals;\n }\n\n /**\n * @notice Before an asset/feed price is fetches for the first time the\n * decimals need to be cached. This is a gas optimization\n * @param asset address of the asset\n * @return uint8 corresponding asset decimals\n */\n function cacheDecimals(address asset) external returns (uint8) {\n (address _feed, ) = feedMetadata(asset);\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n uint8 decimals = AggregatorV3Interface(_feed).decimals();\n decimalsCache[_feed] = decimals;\n return decimals;\n }\n\n function shouldBePegged(address _asset) internal view returns (bool) {\n string memory symbol = Helpers.getSymbol(_asset);\n bytes32 symbolHash = keccak256(abi.encodePacked(symbol));\n return\n symbolHash == keccak256(abi.encodePacked(\"DAI\")) ||\n symbolHash == keccak256(abi.encodePacked(\"USDC\")) ||\n symbolHash == keccak256(abi.encodePacked(\"USDT\"));\n }\n}\n" + }, + "contracts/oracle/OETHBaseOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\n// @notice Oracle Router (for OETH on Base) that denominates all prices in ETH\ncontract OETHBaseOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n using SafeCast for int256;\n\n address constant WETH = 0x4200000000000000000000000000000000000006;\n address constant WOETH = 0xD8724322f44E5c58D7A815F542036fb17DbbF839;\n address constant WOETH_CHAINLINK_FEED =\n 0xe96EB1EDa83d18cbac224233319FA5071464e1b9;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == WETH) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == WOETH) {\n // Chainlink: https://data.chain.link/feeds/base/base/woeth-oeth-exchange-rate\n // Bridged wOETH/OETH\n feedAddress = WOETH_CHAINLINK_FEED;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OETHFixedOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHOracleRouter } from \"./OETHOracleRouter.sol\";\n\n// @notice Oracle Router that returns 1e18 for all prices\n// used solely for deployment to testnets\ncontract OETHFixedOracle is OETHOracleRouter {\n constructor() OETHOracleRouter() {}\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n // solhint-disable-next-line no-unused-vars\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n // fixes price for all of the assets\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n }\n}\n" + }, + "contracts/oracle/OETHOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// @notice Oracle Router that denominates all prices in ETH\ncontract OETHOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = uint256(_iprice).scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == 0x5E8422345238F34275888049021821E8E08CAa1f) {\n // frxETH/ETH\n feedAddress = 0xC58F3385FBc1C8AD2c0C9a061D7c13b141D7A5Df;\n maxStaleness = 18 hours + STALENESS_BUFFER;\n } else if (asset == 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/steth-eth\n // Chainlink: stETH/ETH\n feedAddress = 0x86392dC19c0b719886221c78AB11eb8Cf5c52812;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xae78736Cd615f374D3085123A210448E74Fc6393) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/reth-eth\n // Chainlink: rETH/ETH\n feedAddress = 0x536218f9E9Eb48863970252233c8F271f554C2d0;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xD533a949740bb3306d119CC777fa900bA034cd52) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/crv-eth\n // Chainlink: CRV/ETH\n feedAddress = 0x8a12Be339B0cD1829b91Adc01977caa5E9ac121e;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/cvx-eth\n // Chainlink: CVX/ETH\n feedAddress = 0xC9CbF687f43176B302F03f5e58470b77D07c61c6;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xBe9895146f7AF43049ca1c1AE358B0541Ea49704) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/cbeth-eth\n // Chainlink: cbETH/ETH\n feedAddress = 0xF017fcB346A1885194689bA23Eff2fE6fA5C483b;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xba100000625a3754423978a60c9317c58a424e3D) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/bal-eth\n // Chainlink: BAL/ETH\n feedAddress = 0xC1438AA3823A6Ba0C159CfA8D98dF5A994bA120b;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OETHPlumeOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\n// @notice Oracle Router (for OETH on Plume) that denominates all prices in ETH\ncontract OETHPlumeOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n using SafeCast for int256;\n\n address constant WETH = 0xca59cA09E5602fAe8B629DeE83FfA819741f14be;\n address constant WOETH = 0xD8724322f44E5c58D7A815F542036fb17DbbF839;\n // Ref: https://docs.eo.app/docs/eprice/feed-addresses/plume\n address constant WOETH_ORACLE_FEED =\n 0x4915600Ed7d85De62011433eEf0BD5399f677e9b;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == WETH) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == WOETH) {\n // Chainlink: https://data.chain.link/feeds/base/base/woeth-oeth-exchange-rate\n // Bridged wOETH/OETH\n feedAddress = WOETH_ORACLE_FEED;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\n\n// @notice Oracle Router that denominates all prices in USD\ncontract OracleRouter is AbstractOracleRouter {\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n pure\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n /* + STALENESS_BUFFER is added in case Oracle for some reason doesn't\n * update on heartbeat and we add a generous buffer amount.\n */\n if (asset == 0x6B175474E89094C44Da98b954EedeAC495271d0F) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/dai-usd\n // Chainlink: DAI/USD\n feedAddress = 0xAed0c38402a5d19df6E4c03F4E2DceD6e29c1ee9;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xdC035D45d973E3EC169d2276DDab16f1e407384F) {\n // https://data.chain.link/feeds/ethereum/mainnet/usds-usd\n // Chainlink: USDS/USD\n feedAddress = 0xfF30586cD0F29eD462364C7e81375FC0C71219b1;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/usdc-usd\n // Chainlink: USDC/USD\n feedAddress = 0x8fFfFfd4AfB6115b954Bd326cbe7B4BA576818f6;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xdAC17F958D2ee523a2206206994597C13D831ec7) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/usdt-usd\n // Chainlink: USDT/USD\n feedAddress = 0x3E7d1eAB13ad0104d2750B8863b489D65364e32D;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xc00e94Cb662C3520282E6f5717214004A7f26888) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/comp-usd\n // Chainlink: COMP/USD\n feedAddress = 0xdbd020CAeF83eFd542f4De03e3cF0C28A4428bd5;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/aave-usd\n // Chainlink: AAVE/USD\n feedAddress = 0x547a514d5e3769680Ce22B2361c10Ea13619e8a9;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xD533a949740bb3306d119CC777fa900bA034cd52) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/crv-usd\n // Chainlink: CRV/USD\n feedAddress = 0xCd627aA160A6fA45Eb793D19Ef54f5062F20f33f;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B) {\n // Chainlink: CVX/USD\n feedAddress = 0xd962fC30A72A84cE50161031391756Bf2876Af5D;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OSonicOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHFixedOracle } from \"./OETHFixedOracle.sol\";\n\n// @notice Oracle Router that returns 1e18 for all prices\n// used solely for deployment to testnets\ncontract OSonicOracleRouter is OETHFixedOracle {\n constructor() OETHFixedOracle() {}\n}\n" + }, + "contracts/poolBooster/AbstractPoolBoosterFactory.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IPoolBoostCentralRegistry } from \"../interfaces/poolBooster/IPoolBoostCentralRegistry.sol\";\n\n/**\n * @title Abstract Pool booster factory\n * @author Origin Protocol Inc\n */\ncontract AbstractPoolBoosterFactory is Governable {\n struct PoolBoosterEntry {\n address boosterAddress;\n address ammPoolAddress;\n IPoolBoostCentralRegistry.PoolBoosterType boosterType;\n }\n\n // @notice address of Origin Sonic\n address public immutable oSonic;\n // @notice Central registry contract\n IPoolBoostCentralRegistry public immutable centralRegistry;\n\n // @notice list of all the pool boosters created by this factory\n PoolBoosterEntry[] public poolBoosters;\n // @notice mapping of AMM pool to pool booster\n mapping(address => PoolBoosterEntry) public poolBoosterFromPool;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) {\n require(_oSonic != address(0), \"Invalid oSonic address\");\n require(_governor != address(0), \"Invalid governor address\");\n require(\n _centralRegistry != address(0),\n \"Invalid central registry address\"\n );\n\n oSonic = _oSonic;\n centralRegistry = IPoolBoostCentralRegistry(_centralRegistry);\n _setGovernor(_governor);\n }\n\n /**\n * @notice Goes over all the pool boosters created by this factory and\n * calls bribe() on them.\n * @param _exclusionList A list of pool booster addresses to skip when\n * calling this function.\n */\n function bribeAll(address[] memory _exclusionList) external {\n uint256 lengthI = poolBoosters.length;\n for (uint256 i = 0; i < lengthI; i++) {\n address poolBoosterAddress = poolBoosters[i].boosterAddress;\n bool skipBribeCall = false;\n uint256 lengthJ = _exclusionList.length;\n for (uint256 j = 0; j < lengthJ; j++) {\n // pool booster in exclusion list\n if (_exclusionList[j] == poolBoosterAddress) {\n skipBribeCall = true;\n break;\n }\n }\n\n if (!skipBribeCall) {\n IPoolBooster(poolBoosterAddress).bribe();\n }\n }\n }\n\n /**\n * @notice Removes the pool booster from the internal list of pool boosters.\n * @dev This action does not destroy the pool booster contract nor does it\n * stop the yield delegation to it.\n * @param _poolBoosterAddress address of the pool booster\n */\n function removePoolBooster(address _poolBoosterAddress)\n external\n onlyGovernor\n {\n uint256 boostersLen = poolBoosters.length;\n for (uint256 i = 0; i < boostersLen; ++i) {\n if (poolBoosters[i].boosterAddress == _poolBoosterAddress) {\n // erase mapping\n delete poolBoosterFromPool[poolBoosters[i].ammPoolAddress];\n\n // overwrite current pool booster with the last entry in the list\n poolBoosters[i] = poolBoosters[boostersLen - 1];\n // drop the last entry\n poolBoosters.pop();\n\n centralRegistry.emitPoolBoosterRemoved(_poolBoosterAddress);\n break;\n }\n }\n }\n\n function _storePoolBoosterEntry(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType _boosterType\n ) internal {\n PoolBoosterEntry memory entry = PoolBoosterEntry(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType\n );\n\n poolBoosters.push(entry);\n poolBoosterFromPool[_ammPoolAddress] = entry;\n\n // emit the events of the pool booster created\n centralRegistry.emitPoolBoosterCreated(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType\n );\n }\n\n function _deployContract(bytes memory _bytecode, uint256 _salt)\n internal\n returns (address _address)\n {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _address := create2(\n 0,\n add(_bytecode, 0x20),\n mload(_bytecode),\n _salt\n )\n }\n\n require(\n _address.code.length > 0 && _address != address(0),\n \"Failed creating a pool booster\"\n );\n }\n\n // pre-compute the address of the deployed contract that will be\n // created when create2 is called\n function _computeAddress(bytes memory _bytecode, uint256 _salt)\n internal\n view\n returns (address)\n {\n bytes32 hash = keccak256(\n abi.encodePacked(\n bytes1(0xff),\n address(this),\n _salt,\n keccak256(_bytecode)\n )\n );\n\n // cast last 20 bytes of hash to address\n return address(uint160(uint256(hash)));\n }\n\n function poolBoosterLength() external view returns (uint256) {\n return poolBoosters.length;\n }\n}\n" + }, + "contracts/poolBooster/PoolBoostCentralRegistry.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IPoolBoostCentralRegistry } from \"../interfaces/poolBooster/IPoolBoostCentralRegistry.sol\";\n\n/**\n * @title Contract that holds all governance approved pool booster Factory\n * implementation deployments\n * @author Origin Protocol Inc\n */\ncontract PoolBoostCentralRegistry is Governable, IPoolBoostCentralRegistry {\n event FactoryApproved(address factoryAddress);\n event FactoryRemoved(address factoryAddress);\n\n // @notice List of approved factories\n address[] public factories;\n\n modifier onlyApprovedFactories() {\n require(isApprovedFactory(msg.sender), \"Not an approved factory\");\n _;\n }\n\n constructor() {\n // set the governor of the implementation contract to zero address\n _setGovernor(address(0));\n }\n\n /**\n * @notice Adds a factory address to the approved factory addresses\n * @param _factoryAddress address of the factory\n */\n function approveFactory(address _factoryAddress) external onlyGovernor {\n require(_factoryAddress != address(0), \"Invalid address\");\n require(\n !isApprovedFactory(_factoryAddress),\n \"Factory already approved\"\n );\n\n factories.push(_factoryAddress);\n emit FactoryApproved(_factoryAddress);\n }\n\n /**\n * @notice Removes the factory from approved factory addresses\n * @param _factoryAddress address of the factory\n */\n function removeFactory(address _factoryAddress) external onlyGovernor {\n require(_factoryAddress != address(0), \"Invalid address\");\n\n uint256 length = factories.length;\n bool factoryRemoved = false;\n for (uint256 i = 0; i < length; i++) {\n if (factories[i] != _factoryAddress) {\n continue;\n }\n\n factories[i] = factories[length - 1];\n factories.pop();\n emit FactoryRemoved(_factoryAddress);\n factoryRemoved = true;\n break;\n }\n require(factoryRemoved, \"Not an approved factory\");\n\n emit FactoryRemoved(_factoryAddress);\n }\n\n /**\n * @notice Emits a pool booster created event\n * @dev This has been created as a convenience method for the monitoring to have\n * an index of all of the created pool boosters by only listening to the\n * events of this contract.\n * @param _poolBoosterAddress address of the pool booster created\n * @param _ammPoolAddress address of the AMM pool forwarding yield to the pool booster\n * @param _boosterType PoolBoosterType the type of the pool booster\n */\n function emitPoolBoosterCreated(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n PoolBoosterType _boosterType\n ) external onlyApprovedFactories {\n emit PoolBoosterCreated(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType,\n msg.sender // address of the factory\n );\n }\n\n /**\n * @notice Emits a pool booster removed event\n * @dev This has been created as a convenience method for the monitoring to have\n * an index of all of the removed pool boosters by only listening to the\n * events of this contract.\n * @param _poolBoosterAddress address of the pool booster to be removed\n */\n function emitPoolBoosterRemoved(address _poolBoosterAddress)\n external\n onlyApprovedFactories\n {\n emit PoolBoosterRemoved(_poolBoosterAddress);\n }\n\n /**\n * @notice Returns true if the factory is approved\n * @param _factoryAddress address of the factory\n */\n function isApprovedFactory(address _factoryAddress)\n public\n view\n returns (bool)\n {\n uint256 length = factories.length;\n for (uint256 i = 0; i < length; i++) {\n if (factories[i] == _factoryAddress) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * @notice Returns all supported factories\n */\n function getAllFactories() external view returns (address[] memory) {\n return factories;\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactoryMerkl.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterMerkl } from \"./PoolBoosterMerkl.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Merkl pool boosters.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactoryMerkl is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n /// @notice address of the Merkl distributor\n address public merklDistributor;\n\n /// @notice event emitted when the Merkl distributor is updated\n event MerklDistributorUpdated(address newDistributor);\n\n /**\n * @param _oSonic address of the OSonic token\n * @param _governor address governor\n * @param _centralRegistry address of the central registry\n * @param _merklDistributor address of the Merkl distributor\n */\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry,\n address _merklDistributor\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {\n _setMerklDistributor(_merklDistributor);\n }\n\n /**\n * @dev Create a Pool Booster for Merkl.\n * @param _campaignType The type of campaign to create. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _campaignDuration The duration of the campaign in seconds\n * @param campaignData The data to be used for the campaign. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * This should be fetched from the Merkl UI.\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterMerkl(\n uint32 _campaignType,\n address _ammPoolAddress,\n uint32 _campaignDuration,\n bytes calldata campaignData,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n require(_campaignDuration > 1 hours, \"Invalid campaign duration\");\n require(campaignData.length > 0, \"Invalid campaign data\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterMerkl).creationCode,\n abi.encode(\n oSonic,\n merklDistributor,\n _campaignDuration,\n _campaignType,\n governor(),\n campaignData\n )\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.MerklBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for Merkl.\n * @param _campaignType The type of campaign to create. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterMerkl` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n uint32 _campaignType,\n address _ammPoolAddress,\n uint32 _campaignDuration,\n bytes calldata campaignData,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n require(_campaignDuration > 1 hours, \"Invalid campaign duration\");\n require(campaignData.length > 0, \"Invalid campaign data\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterMerkl).creationCode,\n abi.encode(\n oSonic,\n merklDistributor,\n _campaignDuration,\n _campaignType,\n governor(),\n campaignData\n )\n ),\n _salt\n );\n }\n\n /**\n * @dev Set the address of the Merkl distributor\n * @param _merklDistributor The address of the Merkl distributor\n */\n function setMerklDistributor(address _merklDistributor)\n external\n onlyGovernor\n {\n _setMerklDistributor(_merklDistributor);\n }\n\n function _setMerklDistributor(address _merklDistributor) internal {\n require(\n _merklDistributor != address(0),\n \"Invalid merklDistributor address\"\n );\n merklDistributor = _merklDistributor;\n emit MerklDistributorUpdated(_merklDistributor);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactoryMetropolis.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterMetropolis } from \"./PoolBoosterMetropolis.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Metropolis pool boosters.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactoryMetropolis is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n address public immutable rewardFactory;\n address public immutable voter;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n // @param address _rewardFactory address of the Metropolis reward factory\n // @param address _voter address of the Metropolis voter\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry,\n address _rewardFactory,\n address _voter\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {\n rewardFactory = _rewardFactory;\n voter = _voter;\n }\n\n /**\n * @dev Create a Pool Booster for Metropolis pool.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterMetropolis(address _ammPoolAddress, uint256 _salt)\n external\n onlyGovernor\n {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterMetropolis).creationCode,\n abi.encode(oSonic, rewardFactory, _ammPoolAddress, voter)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.MetropolisBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for Metropolis pool.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterMetropolis` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(address _ammPoolAddress, uint256 _salt)\n external\n view\n returns (address)\n {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterMetropolis).creationCode,\n abi.encode(oSonic, rewardFactory, _ammPoolAddress, voter)\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactorySwapxDouble.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterSwapxDouble } from \"./PoolBoosterSwapxDouble.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Swapx Ichi pool boosters where both of the\n * gauges need incentivizing.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactorySwapxDouble is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {}\n\n /**\n * @dev Create a Pool Booster for SwapX Ichi vault based pool where 2 Bribe contracts need to be\n * bribed\n * @param _bribeAddressOS address of the Bribes.sol(Bribe) contract for the OS token side\n * @param _bribeAddressOther address of the Bribes.sol(Bribe) contract for the other token in the pool\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _split 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n * bribe contract and 60% to other bribe contract\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterSwapxDouble(\n address _bribeAddressOS,\n address _bribeAddressOther,\n address _ammPoolAddress,\n uint256 _split,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterSwapxDouble).creationCode,\n abi.encode(_bribeAddressOS, _bribeAddressOther, oSonic, _split)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.SwapXDoubleBooster\n );\n }\n\n /**\n * @dev Compute the address of the pool booster to be deployed.\n * @param _bribeAddressOS address of the Bribes.sol(Bribe) contract for the OS token side\n * @param _bribeAddressOther address of the Bribes.sol(Bribe) contract for the other token in the pool\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _split 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n * bribe contract and 60% to other bribe contract\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterSwapxDouble` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n address _bribeAddressOS,\n address _bribeAddressOther,\n address _ammPoolAddress,\n uint256 _split,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterSwapxDouble).creationCode,\n abi.encode(\n _bribeAddressOS,\n _bribeAddressOther,\n oSonic,\n _split\n )\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactorySwapxSingle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterSwapxSingle } from \"./PoolBoosterSwapxSingle.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Swapx Single pool boosters - where a single\n * gauge is created for a pool. this is appropriate for Classic Stable & Classic\n * Volatile SwapX pools.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactorySwapxSingle is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {}\n\n /**\n * @dev Create a Pool Booster for SwapX classic volatile or classic stable pools where\n * a single Bribe contract is incentivized.\n * @param _bribeAddress address of the Bribes.sol contract\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterSwapxSingle(\n address _bribeAddress,\n address _ammPoolAddress,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterSwapxSingle).creationCode,\n abi.encode(_bribeAddress, oSonic)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.SwapXSingleBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for SwapX classic volatile or classic stable pools where\n * a single Bribe contract is incentivized.\n * @param _bribeAddress address of the Bribes.sol contract\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterSwapxSingle` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n address _bribeAddress,\n address _ammPoolAddress,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterSwapxSingle).creationCode,\n abi.encode(_bribeAddress, oSonic)\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterMerkl.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IMerklDistributor } from \"../interfaces/poolBooster/IMerklDistributor.sol\";\n\ninterface IERC1271 {\n /**\n * @dev Should return whether the signature provided is valid for the provided data\n * @param hash Hash of the data to be signed\n * @param signature Signature byte array associated with _data\n */\n function isValidSignature(bytes32 hash, bytes memory signature)\n external\n view\n returns (bytes4 magicValue);\n}\n\n/**\n * @title Pool booster for Merkl distributor\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterMerkl is IPoolBooster, IERC1271 {\n /// @notice address of merkl distributor\n IMerklDistributor public immutable merklDistributor;\n /// @notice address of the OS token\n IERC20 public immutable rewardToken;\n /// @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n /// @notice Campaign duration in seconds\n uint32 public immutable duration; // -> should be immutable\n /// @notice Campaign type\n uint32 public immutable campaignType;\n /// @notice Owner of the campaign\n address public immutable creator;\n /// @notice Campaign data\n bytes public campaignData;\n\n constructor(\n address _rewardToken,\n address _merklDistributor,\n uint32 _duration,\n uint32 _campaignType,\n address _creator,\n bytes memory _campaignData\n ) {\n require(_rewardToken != address(0), \"Invalid rewardToken address\");\n require(\n _merklDistributor != address(0),\n \"Invalid merklDistributor address\"\n );\n require(_campaignData.length > 0, \"Invalid campaignData\");\n require(_duration > 1 hours, \"Invalid duration\");\n\n campaignType = _campaignType;\n duration = _duration;\n creator = _creator;\n\n merklDistributor = IMerklDistributor(_merklDistributor);\n rewardToken = IERC20(_rewardToken);\n campaignData = _campaignData;\n }\n\n /// @notice Create a campaign on the Merkl distributor\n function bribe() external override {\n // Ensure token is approved for the Merkl distributor\n uint256 minAmount = merklDistributor.rewardTokenMinAmounts(\n address(rewardToken)\n );\n require(minAmount > 0, \"Min reward amount must be > 0\");\n\n // if balance too small or below threshold, do no bribes\n uint256 balance = rewardToken.balanceOf(address(this));\n if (\n balance < MIN_BRIBE_AMOUNT ||\n (balance * 1 hours < minAmount * duration)\n ) {\n return;\n }\n\n // Approve the bribe contract to spend the reward token\n rewardToken.approve(address(merklDistributor), balance);\n\n // Notify the bribe contract of the reward amount\n merklDistributor.signAndCreateCampaign(\n IMerklDistributor.CampaignParameters({\n campaignId: bytes32(0),\n creator: creator,\n rewardToken: address(rewardToken),\n amount: balance,\n campaignType: campaignType,\n startTimestamp: getNextPeriodStartTime(),\n duration: duration,\n campaignData: campaignData\n }),\n bytes(\"\")\n );\n emit BribeExecuted(balance);\n }\n\n /// @notice Used to sign a campaign on the Merkl distributor\n function isValidSignature(bytes32, bytes memory)\n external\n view\n override\n returns (bytes4 magicValue)\n {\n require(msg.sender == address(merklDistributor), \"Invalid sender\");\n // bytes4(keccak256(\"isValidSignature(bytes32,bytes)\")) == 0x1626ba7e\n return bytes4(0x1626ba7e);\n }\n\n /// @notice Returns the timestamp for the start of the next period based on the configured duration\n function getNextPeriodStartTime() public view returns (uint32) {\n // Calculate the timestamp for the next period boundary\n return uint32((block.timestamp / duration + 1) * duration);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterMetropolis.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @title Pool booster for Metropolis pools\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterMetropolis is IPoolBooster {\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice address of the pool\n address public immutable pool;\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n IRewarderFactory public immutable rewardFactory;\n\n IVoter public immutable voter;\n\n constructor(\n address _osToken,\n address _rewardFactory,\n address _pool,\n address _voter\n ) {\n require(_pool != address(0), \"Invalid pool address\");\n pool = _pool;\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n\n rewardFactory = IRewarderFactory(_rewardFactory);\n\n voter = IVoter(_voter);\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n (, uint256 minBribeAmount) = rewardFactory.getWhitelistedTokenInfo(\n address(osToken)\n );\n if (balance < MIN_BRIBE_AMOUNT || balance < minBribeAmount) {\n return;\n }\n\n uint256 id = voter.getCurrentVotingPeriod() + 1;\n\n // Deploy a rewarder\n IRewarder rewarder = IRewarder(\n rewardFactory.createBribeRewarder(address(osToken), pool)\n );\n\n // Approve the rewarder to spend the balance\n osToken.approve(address(rewarder), balance);\n\n // Fund and bribe the rewarder\n rewarder.fundAndBribe(id, id, balance);\n\n emit BribeExecuted(balance);\n }\n}\n\ninterface IRewarderFactory {\n function createBribeRewarder(address token, address pool)\n external\n returns (address rewarder);\n\n function getWhitelistedTokenInfo(address token)\n external\n view\n returns (bool isWhitelisted, uint256 minBribeAmount);\n}\n\ninterface IRewarder {\n function fundAndBribe(\n uint256 startId,\n uint256 lastId,\n uint256 amountPerPeriod\n ) external payable;\n}\n\ninterface IVoter {\n function getCurrentVotingPeriod() external view returns (uint256);\n}\n" + }, + "contracts/poolBooster/PoolBoosterSwapxDouble.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBribe } from \"../interfaces/poolBooster/ISwapXAlgebraBribe.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n/**\n * @title Pool booster for SwapX concentrated liquidity where 2 gauges are created for\n * every pool. Ichi vaults currently have such setup.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterSwapxDouble is IPoolBooster {\n using StableMath for uint256;\n\n // @notice address of the Bribes.sol(Bribe) contract for the OS token side\n IBribe public immutable bribeContractOS;\n // @notice address of the Bribes.sol(Bribe) contract for the other token in the pool\n IBribe public immutable bribeContractOther;\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n // bribe contract and 60% to other bribe contract\n uint256 public immutable split;\n\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n constructor(\n address _bribeContractOS,\n address _bribeContractOther,\n address _osToken,\n uint256 _split\n ) {\n require(\n _bribeContractOS != address(0),\n \"Invalid bribeContractOS address\"\n );\n require(\n _bribeContractOther != address(0),\n \"Invalid bribeContractOther address\"\n );\n // expect it to be between 1% & 99%\n require(_split > 1e16 && _split < 99e16, \"Unexpected split amount\");\n\n bribeContractOS = IBribe(_bribeContractOS);\n bribeContractOther = IBribe(_bribeContractOther);\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n split = _split;\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n if (balance < MIN_BRIBE_AMOUNT) {\n return;\n }\n\n uint256 osBribeAmount = balance.mulTruncate(split);\n uint256 otherBribeAmount = balance - osBribeAmount;\n\n osToken.approve(address(bribeContractOS), osBribeAmount);\n osToken.approve(address(bribeContractOther), otherBribeAmount);\n\n bribeContractOS.notifyRewardAmount(address(osToken), osBribeAmount);\n bribeContractOther.notifyRewardAmount(\n address(osToken),\n otherBribeAmount\n );\n\n emit BribeExecuted(balance);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterSwapxSingle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBribe } from \"../interfaces/poolBooster/ISwapXAlgebraBribe.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @title Pool booster for SwapX for Classic Stable Pools and Classic Volatile Pools\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterSwapxSingle is IPoolBooster {\n // @notice address of the Bribes.sol(Bribe) contract\n IBribe public immutable bribeContract;\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n constructor(address _bribeContract, address _osToken) {\n require(_bribeContract != address(0), \"Invalid bribeContract address\");\n bribeContract = IBribe(_bribeContract);\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n if (balance < MIN_BRIBE_AMOUNT) {\n return;\n }\n\n osToken.approve(address(bribeContract), balance);\n\n bribeContract.notifyRewardAmount(address(osToken), balance);\n emit BribeExecuted(balance);\n }\n}\n" + }, + "contracts/proxies/BaseProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice BridgedBaseWOETHProxy delegates calls to BridgedWOETH implementation\n */\ncontract BridgedBaseWOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseVaultProxy delegates calls to OETHBaseVault implementation\n */\ncontract OETHBaseVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseProxy delegates calls to OETH implementation\n */\ncontract OETHBaseProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHBaseProxy delegates calls to WOETH implementation\n */\ncontract WOETHBaseProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseDripperProxy delegates calls to a FixedRateDripper implementation\n */\ncontract OETHBaseDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice AerodromeAMOStrategyProxy delegates calls to AerodromeAMOStrategy implementation\n */\ncontract AerodromeAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BridgedWOETHStrategyProxy delegates calls to BridgedWOETHStrategy implementation\n */\ncontract BridgedWOETHStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseHarvesterProxy delegates calls to a SuperOETHHarvester implementation\n */\ncontract OETHBaseHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseCurveAMOProxy delegates calls to a OETHBaseCurveAMO implementation\n */\ncontract OETHBaseCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/proxies/InitializeGovernedUpgradeabilityProxy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { Governable } from \"../governance/Governable.sol\";\n\n/**\n * @title BaseGovernedUpgradeabilityProxy\n * @dev This contract combines an upgradeability proxy with our governor system.\n * It is based on an older version of OpenZeppelins BaseUpgradeabilityProxy\n * with Solidity ^0.8.0.\n * @author Origin Protocol Inc\n */\ncontract InitializeGovernedUpgradeabilityProxy is Governable {\n /**\n * @dev Emitted when the implementation is upgraded.\n * @param implementation Address of the new implementation.\n */\n event Upgraded(address indexed implementation);\n\n constructor() {\n _setGovernor(msg.sender);\n }\n\n /**\n * @dev Contract initializer with Governor enforcement\n * @param _logic Address of the initial implementation.\n * @param _initGovernor Address of the initial Governor.\n * @param _data Data to send as msg.data to the implementation to initialize\n * the proxied contract.\n * It should include the signature and the parameters of the function to be\n * called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n * This parameter is optional, if no data is given the initialization call\n * to proxied contract will be skipped.\n */\n function initialize(\n address _logic,\n address _initGovernor,\n bytes calldata _data\n ) public payable onlyGovernor {\n require(_implementation() == address(0));\n require(_logic != address(0), \"Implementation not set\");\n assert(\n IMPLEMENTATION_SLOT ==\n bytes32(uint256(keccak256(\"eip1967.proxy.implementation\")) - 1)\n );\n _setImplementation(_logic);\n if (_data.length > 0) {\n (bool success, ) = _logic.delegatecall(_data);\n require(success);\n }\n _changeGovernor(_initGovernor);\n }\n\n /**\n * @return The address of the proxy admin/it's also the governor.\n */\n function admin() external view returns (address) {\n return _governor();\n }\n\n /**\n * @return The address of the implementation.\n */\n function implementation() external view returns (address) {\n return _implementation();\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy.\n * Only the admin can call this function.\n * @param _newImplementation Address of the new implementation.\n */\n function upgradeTo(address _newImplementation) external onlyGovernor {\n _upgradeTo(_newImplementation);\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy and call a function\n * on the new implementation.\n * This is useful to initialize the proxied contract.\n * @param newImplementation Address of the new implementation.\n * @param data Data to send as msg.data in the low level call.\n * It should include the signature and the parameters of the function to be called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n */\n function upgradeToAndCall(address newImplementation, bytes calldata data)\n external\n payable\n onlyGovernor\n {\n _upgradeTo(newImplementation);\n (bool success, ) = newImplementation.delegatecall(data);\n require(success);\n }\n\n /**\n * @dev Fallback function.\n * Implemented entirely in `_fallback`.\n */\n fallback() external payable {\n _fallback();\n }\n\n /**\n * @dev Delegates execution to an implementation contract.\n * This is a low level function that doesn't return to its internal call site.\n * It will return to the external caller whatever the implementation returns.\n * @param _impl Address to delegate.\n */\n function _delegate(address _impl) internal {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(gas(), _impl, 0, calldatasize(), 0, 0)\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n /**\n * @dev Function that is run as the first thing in the fallback function.\n * Can be redefined in derived contracts to add functionality.\n * Redefinitions must call super._willFallback().\n */\n function _willFallback() internal {}\n\n /**\n * @dev fallback implementation.\n * Extracted to enable manual triggering.\n */\n function _fallback() internal {\n _willFallback();\n _delegate(_implementation());\n }\n\n /**\n * @dev Storage slot with the address of the current implementation.\n * This is the keccak-256 hash of \"eip1967.proxy.implementation\" subtracted by 1, and is\n * validated in the constructor.\n */\n bytes32 internal constant IMPLEMENTATION_SLOT =\n 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n /**\n * @dev Returns the current implementation.\n * @return impl Address of the current implementation\n */\n function _implementation() internal view returns (address impl) {\n bytes32 slot = IMPLEMENTATION_SLOT;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n impl := sload(slot)\n }\n }\n\n /**\n * @dev Upgrades the proxy to a new implementation.\n * @param newImplementation Address of the new implementation.\n */\n function _upgradeTo(address newImplementation) internal {\n _setImplementation(newImplementation);\n emit Upgraded(newImplementation);\n }\n\n /**\n * @dev Sets the implementation address of the proxy.\n * @param newImplementation Address of the new implementation.\n */\n function _setImplementation(address newImplementation) internal {\n require(\n Address.isContract(newImplementation),\n \"Cannot set a proxy implementation to a non-contract address\"\n );\n\n bytes32 slot = IMPLEMENTATION_SLOT;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(slot, newImplementation)\n }\n }\n}\n" + }, + "contracts/proxies/PlumeProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OETHPlumeVaultProxy delegates calls to OETHPlumeVault implementation\n */\ncontract OETHPlumeVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHPlumeProxy delegates calls to OETH implementation\n */\ncontract OETHPlumeProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHPlumeProxy delegates calls to WOETH implementation\n */\ncontract WOETHPlumeProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice RoosterAMOStrategyProxy delegates calls to a RoosterAMOStrategy implementation\n */\ncontract RoosterAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/proxies/Proxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OUSDProxy delegates calls to an OUSD implementation\n */\ncontract OUSDProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WrappedOUSDProxy delegates calls to a WrappedOUSD implementation\n */\ncontract WrappedOUSDProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice VaultProxy delegates calls to a Vault implementation\n */\ncontract VaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CompoundStrategyProxy delegates calls to a CompoundStrategy implementation\n */\ncontract CompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice AaveStrategyProxy delegates calls to a AaveStrategy implementation\n */\ncontract AaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ConvexStrategyProxy delegates calls to a ConvexStrategy implementation\n */\ncontract ConvexStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice HarvesterProxy delegates calls to a Harvester implementation\n */\ncontract HarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice DripperProxy delegates calls to a Dripper implementation\n */\ncontract DripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoCompoundStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\n */\ncontract MorphoCompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ConvexOUSDMetaStrategyProxy delegates calls to a ConvexOUSDMetaStrategy implementation\n */\ncontract ConvexOUSDMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoAaveStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\n */\ncontract MorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHProxy delegates calls to nowhere for now\n */\ncontract OETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHProxy delegates calls to nowhere for now\n */\ncontract WOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHVaultProxy delegates calls to a Vault implementation\n */\ncontract OETHVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHDripperProxy delegates calls to a OETHDripper implementation\n */\ncontract OETHDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHHarvesterProxy delegates calls to a Harvester implementation\n */\ncontract OETHHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CurveEthStrategyProxy delegates calls to a CurveEthStrategy implementation\n */\ncontract ConvexEthMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BuybackProxy delegates calls to Buyback implementation\n */\ncontract BuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHMorphoAaveStrategyProxy delegates calls to a MorphoAaveStrategy implementation\n */\ncontract OETHMorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBalancerMetaPoolrEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\n */\ncontract OETHBalancerMetaPoolrEthStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice OETHBalancerMetaPoolwstEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\n */\ncontract OETHBalancerMetaPoolwstEthStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MakerDsrStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MakerDsrStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBuybackProxy delegates calls to Buyback implementation\n */\ncontract OETHBuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BridgedWOETHProxy delegates calls to BridgedWOETH implementation\n */\ncontract BridgedWOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice NativeStakingSSVStrategyProxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulatorProxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulatorProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingSSVStrategy2Proxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategy2Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulator2Proxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulator2Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingSSVStrategy3Proxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategy3Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulator3Proxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulator3Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MetaMorphoStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MetaMorphoStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ARMBuybackProxy delegates calls to Buyback implementation\n */\ncontract ARMBuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoGauntletPrimeUSDCStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MorphoGauntletPrimeUSDCStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MorphoGauntletPrimeUSDTStrategyProxy delegates calls to a Generalized4626USDTStrategy implementation\n */\ncontract MorphoGauntletPrimeUSDTStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice CurvePoolBoosterProxy delegates calls to a CurvePoolBooster implementation\n */\ncontract CurvePoolBoosterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHFixedRateDripperProxy delegates calls to a OETHFixedRateDripper implementation\n */\ncontract OETHFixedRateDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHSimpleHarvesterProxy delegates calls to a OETHSimpleHarvester implementation\n */\ncontract OETHSimpleHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice PoolBoostCentralRegistryProxy delegates calls to the PoolBoostCentralRegistry implementation\n */\ncontract PoolBoostCentralRegistryProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MakerSSRStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MakerSSRStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OUSDCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\n */\ncontract OUSDCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\n */\ncontract OETHCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/proxies/SonicProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OSonicVaultProxy delegates calls to OSonicVault implementation\n */\ncontract OSonicVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicProxy delegates calls to OSonic implementation\n */\ncontract OSonicProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOSonicProxy delegates calls to WOSonic implementation\n */\ncontract WOSonicProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicDripperProxy delegates calls to a FixedRateDripper implementation\n */\ncontract OSonicDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice SonicStakingStrategyProxy delegates calls to SonicStakingStrategy implementation\n */\ncontract SonicStakingStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicHarvesterProxy delegates calls to a OSonicHarvester implementation\n */\ncontract OSonicHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice SonicSwapXAMOStrategyProxy delegates calls to a SonicSwapXAMOStrategy implementation\n */\ncontract SonicSwapXAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/strategies/AaveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Aave Strategy\n * @notice Investment strategy for investing stablecoins via Aave\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport \"./IAave.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\nimport { IAaveStakedToken } from \"./IAaveStakeToken.sol\";\nimport { IAaveIncentivesController } from \"./IAaveIncentivesController.sol\";\n\ncontract AaveStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n\n uint16 constant referralCode = 92;\n\n IAaveIncentivesController public incentivesController;\n IAaveStakedToken public stkAave;\n\n /**\n * @param _stratConfig The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as AAVE needs several extra\n * addresses for the rewards program.\n * @param _rewardTokenAddresses Address of the AAVE token\n * @param _assets Addresses of supported assets\n * @param _pTokens Platform Token corresponding addresses\n * @param _incentivesAddress Address of the AAVE incentives controller\n * @param _stkAaveAddress Address of the stkAave contract\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // AAVE\n address[] calldata _assets,\n address[] calldata _pTokens,\n address _incentivesAddress,\n address _stkAaveAddress\n ) external onlyGovernor initializer {\n incentivesController = IAaveIncentivesController(_incentivesAddress);\n stkAave = IAaveStakedToken(_stkAaveAddress);\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Deposit asset into Aave\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Aave\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n // Following line also doubles as a check that we are depositing\n // an asset that we support.\n emit Deposit(_asset, _getATokenFor(_asset), _amount);\n _getLendingPool().deposit(_asset, _amount, address(this), referralCode);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Aave\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Aave\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n emit Withdrawal(_asset, _getATokenFor(_asset), _amount);\n uint256 actual = _getLendingPool().withdraw(\n _asset,\n _amount,\n address(this)\n );\n require(actual == _amount, \"Did not withdraw enough\");\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n // Redeem entire balance of aToken\n IERC20 asset = IERC20(assetsMapped[i]);\n address aToken = _getATokenFor(assetsMapped[i]);\n uint256 balance = IERC20(aToken).balanceOf(address(this));\n if (balance > 0) {\n uint256 actual = _getLendingPool().withdraw(\n address(asset),\n balance,\n address(this)\n );\n require(actual == balance, \"Did not withdraw enough\");\n\n uint256 assetBalance = asset.balanceOf(address(this));\n // Transfer entire balance to Vault\n asset.safeTransfer(vaultAddress, assetBalance);\n\n emit Withdrawal(address(asset), aToken, assetBalance);\n }\n }\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n // Balance is always with token aToken decimals\n address aToken = _getATokenFor(_asset);\n balance = IERC20(aToken).balanceOf(address(this));\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Approve the spending of all assets by their corresponding aToken,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n address lendingPool = address(_getLendingPool());\n // approve the pool to spend the Asset\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n address asset = assetsMapped[i];\n // Safe approval\n IERC20(asset).safeApprove(lendingPool, 0);\n IERC20(asset).safeApprove(lendingPool, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / aTokens\n We need to give the AAVE lending pool approval to transfer the\n asset.\n * @param _asset Address of the asset to approve\n * @param _aToken Address of the aToken\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _aToken)\n internal\n override\n {\n address lendingPool = address(_getLendingPool());\n IERC20(_asset).safeApprove(lendingPool, 0);\n IERC20(_asset).safeApprove(lendingPool, type(uint256).max);\n }\n\n /**\n * @dev Get the aToken wrapped in the IERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return Corresponding aToken to this asset\n */\n function _getATokenFor(address _asset) internal view returns (address) {\n address aToken = assetToPToken[_asset];\n require(aToken != address(0), \"aToken does not exist\");\n return aToken;\n }\n\n /**\n * @dev Get the current address of the Aave lending pool, which is the gateway to\n * depositing.\n * @return Current lending pool implementation\n */\n function _getLendingPool() internal view returns (IAaveLendingPool) {\n address lendingPool = ILendingPoolAddressesProvider(platformAddress)\n .getLendingPool();\n require(lendingPool != address(0), \"Lending pool does not exist\");\n return IAaveLendingPool(lendingPool);\n }\n\n /**\n * @dev Collect stkAave, convert it to AAVE send to Vault.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n if (address(stkAave) == address(0)) {\n return;\n }\n\n // Check staked AAVE cooldown timer\n uint256 cooldown = stkAave.stakersCooldowns(address(this));\n uint256 windowStart = cooldown + stkAave.COOLDOWN_SECONDS();\n uint256 windowEnd = windowStart + stkAave.UNSTAKE_WINDOW();\n\n // If inside the unlock window, then we can redeem stkAave\n // for AAVE and send it to the vault.\n if (block.timestamp > windowStart && block.timestamp <= windowEnd) {\n // Redeem to AAVE\n uint256 stkAaveBalance = stkAave.balanceOf(address(this));\n stkAave.redeem(address(this), stkAaveBalance);\n\n // Transfer AAVE to harvesterAddress\n uint256 aaveBalance = IERC20(rewardTokenAddresses[0]).balanceOf(\n address(this)\n );\n if (aaveBalance > 0) {\n IERC20(rewardTokenAddresses[0]).safeTransfer(\n harvesterAddress,\n aaveBalance\n );\n }\n }\n\n // Collect available rewards and restart the cooldown timer, if either of\n // those should be run.\n if (block.timestamp > windowStart || cooldown == 0) {\n uint256 assetsLen = assetsMapped.length;\n // aToken addresses for incentives controller\n address[] memory aTokens = new address[](assetsLen);\n for (uint256 i = 0; i < assetsLen; ++i) {\n aTokens[i] = _getATokenFor(assetsMapped[i]);\n }\n\n // 1. If we have rewards availabile, collect them\n uint256 pendingRewards = incentivesController.getRewardsBalance(\n aTokens,\n address(this)\n );\n if (pendingRewards > 0) {\n // Because getting more stkAAVE from the incentives controller\n // with claimRewards() may push the stkAAVE cooldown time\n // forward, it is called after stakedAAVE has been turned into\n // AAVE.\n uint256 collected = incentivesController.claimRewards(\n aTokens,\n pendingRewards,\n address(this)\n );\n require(collected == pendingRewards, \"AAVE reward difference\");\n }\n\n // 2. Start cooldown counting down.\n if (stkAave.balanceOf(address(this)) > 0) {\n // Protected with if since cooldown call would revert\n // if no stkAave balance.\n stkAave.cooldown();\n }\n }\n }\n}\n" + }, + "contracts/strategies/AbstractCompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base Compound Abstract Strategy\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { ICERC20 } from \"./ICompound.sol\";\nimport { IComptroller } from \"../interfaces/IComptroller.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\nabstract contract AbstractCompoundStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n\n int256[50] private __reserved;\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Get the cToken wrapped in the ICERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return Corresponding cToken to this asset\n */\n function _getCTokenFor(address _asset) internal view returns (ICERC20) {\n address cToken = assetToPToken[_asset];\n require(cToken != address(0), \"cToken does not exist\");\n return ICERC20(cToken);\n }\n\n /**\n * @dev Converts an underlying amount into cToken amount\n * cTokenAmt = (underlying * 1e18) / exchangeRate\n * @param _cToken cToken for which to change\n * @param _underlying Amount of underlying to convert\n * @return amount Equivalent amount of cTokens\n */\n function _convertUnderlyingToCToken(ICERC20 _cToken, uint256 _underlying)\n internal\n view\n returns (uint256 amount)\n {\n // e.g. 1e18*1e18 / 205316390724364402565641705 = 50e8\n // e.g. 1e8*1e18 / 205316390724364402565641705 = 0.45 or 0\n amount = (_underlying * 1e18) / _cToken.exchangeRateStored();\n }\n}\n" + }, + "contracts/strategies/AbstractConvexMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { ICurveMetaPool } from \"./ICurveMetaPool.sol\";\nimport { IERC20, AbstractCurveStrategy, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\nabstract contract AbstractConvexMetaStrategy is AbstractCurveStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n event MaxWithdrawalSlippageUpdated(\n uint256 _prevMaxSlippagePercentage,\n uint256 _newMaxSlippagePercentage\n );\n\n // used to circumvent the stack too deep issue\n struct InitConfig {\n address cvxDepositorAddress; //Address of the Convex depositor(AKA booster) for this pool\n address metapoolAddress; //Address of the Curve MetaPool\n address metapoolMainToken; //Address of Main metapool token\n address cvxRewardStakerAddress; //Address of the CVX rewards staker\n address metapoolLPToken; //Address of metapool LP token\n uint256 cvxDepositorPTokenId; //Pid of the pool referred to by Depositor and staker\n }\n\n address internal cvxDepositorAddress;\n address internal cvxRewardStakerAddress;\n uint256 internal cvxDepositorPTokenId;\n ICurveMetaPool internal metapool;\n IERC20 internal metapoolMainToken;\n IERC20 internal metapoolLPToken;\n // Ordered list of metapool assets\n address[] internal metapoolAssets;\n // Max withdrawal slippage denominated in 1e18 (1e18 == 100%)\n uint256 public maxWithdrawalSlippage;\n uint128 internal crvCoinIndex;\n uint128 internal mainCoinIndex;\n\n int256[41] private ___reserved;\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * DAI, USDC, USDT\n * @param _pTokens Platform Token corresponding addresses\n * @param initConfig Various addresses and info for initialization state\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets,\n address[] calldata _pTokens,\n InitConfig calldata initConfig\n ) external onlyGovernor initializer {\n require(_assets.length == 3, \"Must have exactly three assets\");\n // Should be set prior to abstract initialize call otherwise\n // abstractSetPToken calls will fail\n cvxDepositorAddress = initConfig.cvxDepositorAddress;\n pTokenAddress = _pTokens[0];\n metapool = ICurveMetaPool(initConfig.metapoolAddress);\n metapoolMainToken = IERC20(initConfig.metapoolMainToken);\n cvxRewardStakerAddress = initConfig.cvxRewardStakerAddress;\n metapoolLPToken = IERC20(initConfig.metapoolLPToken);\n cvxDepositorPTokenId = initConfig.cvxDepositorPTokenId;\n maxWithdrawalSlippage = 1e16;\n\n metapoolAssets = [metapool.coins(0), metapool.coins(1)];\n crvCoinIndex = _getMetapoolCoinIndex(pTokenAddress);\n mainCoinIndex = _getMetapoolCoinIndex(initConfig.metapoolMainToken);\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n balance = 0;\n\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 contractPTokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (contractPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = contractPTokens.mulTruncate(virtual_price);\n balance += value;\n }\n\n /* We intentionally omit the metapoolLp tokens held by the metastrategyContract\n * since the contract should never (except in the middle of deposit/withdrawal\n * transaction) hold any amount of those tokens in normal operation. There\n * could be tokens sent to it by a 3rd party and we decide to actively ignore\n * those.\n */\n uint256 metapoolGaugePTokens = IRewardStaking(cvxRewardStakerAddress)\n .balanceOf(address(this));\n\n if (metapoolGaugePTokens > 0) {\n uint256 value = metapoolGaugePTokens.mulTruncate(\n metapool.get_virtual_price()\n );\n balance += value;\n }\n\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = balance.scaleBy(assetDecimals, 18) / THREEPOOL_ASSET_COUNT;\n }\n\n /**\n * @dev This function is completely analogous to _calcCurveTokenAmount[AbstractCurveStrategy]\n * and just utilizes different Curve (meta)pool API\n */\n function _calcCurveMetaTokenAmount(uint128 _coinIndex, uint256 _amount)\n internal\n returns (uint256 requiredMetapoolLP)\n {\n uint256[2] memory _amounts = [uint256(0), uint256(0)];\n _amounts[uint256(_coinIndex)] = _amount;\n\n // LP required when removing required asset ignoring fees\n uint256 lpRequiredNoFees = metapool.calc_token_amount(_amounts, false);\n /* LP required if fees would apply to entirety of removed amount\n *\n * fee is 1e10 denominated number: https://curve.readthedocs.io/exchange-pools.html#StableSwap.fee\n */\n uint256 lpRequiredFullFees = lpRequiredNoFees.mulTruncateScale(\n 1e10 + metapool.fee(),\n 1e10\n );\n\n /* asset received when withdrawing full fee applicable LP accounting for\n * slippage and fees\n */\n uint256 assetReceivedForFullLPFees = metapool.calc_withdraw_one_coin(\n lpRequiredFullFees,\n int128(_coinIndex)\n );\n\n // exact amount of LP required\n requiredMetapoolLP =\n (lpRequiredFullFees * _amount) /\n assetReceivedForFullLPFees;\n }\n\n function _approveBase() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // 3Pool for LP token (required for removing liquidity)\n pToken.safeApprove(platformAddress, 0);\n pToken.safeApprove(platformAddress, type(uint256).max);\n // Gauge for LP token\n metapoolLPToken.safeApprove(cvxDepositorAddress, 0);\n metapoolLPToken.safeApprove(cvxDepositorAddress, type(uint256).max);\n // Metapool for LP token\n pToken.safeApprove(address(metapool), 0);\n pToken.safeApprove(address(metapool), type(uint256).max);\n // Metapool for Metapool main token\n metapoolMainToken.safeApprove(address(metapool), 0);\n metapoolMainToken.safeApprove(address(metapool), type(uint256).max);\n }\n\n /**\n * @dev Get the index of the coin\n */\n function _getMetapoolCoinIndex(address _asset)\n internal\n view\n returns (uint128)\n {\n for (uint128 i = 0; i < 2; i++) {\n if (metapoolAssets[i] == _asset) return i;\n }\n revert(\"Invalid Metapool asset\");\n }\n\n /**\n * @dev Sets max withdrawal slippage that is considered when removing\n * liquidity from Metapools.\n * @param _maxWithdrawalSlippage Max withdrawal slippage denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxWithdrawalSlippage should actually be 0.1% (1e15)\n * for production usage. Contract allows as low value as 0% for confirming\n * correct behavior in test suite.\n */\n function setMaxWithdrawalSlippage(uint256 _maxWithdrawalSlippage)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(\n _maxWithdrawalSlippage <= 1e18,\n \"Max withdrawal slippage needs to be between 0% - 100%\"\n );\n emit MaxWithdrawalSlippageUpdated(\n maxWithdrawalSlippage,\n _maxWithdrawalSlippage\n );\n maxWithdrawalSlippage = _maxWithdrawalSlippage;\n }\n\n /**\n * @dev Collect accumulated CRV and CVX and send to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n IRewardStaking(cvxRewardStakerAddress).getReward();\n _collectRewardTokens();\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/AbstractCurveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve 3Pool Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\nabstract contract AbstractCurveStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 internal constant MAX_SLIPPAGE = 1e16; // 1%, same as the Curve UI\n // number of assets in Curve 3Pool (USDC, DAI, USDT)\n uint256 internal constant THREEPOOL_ASSET_COUNT = 3;\n address internal pTokenAddress;\n\n int256[49] private __reserved;\n\n /**\n * @dev Deposit asset into the Curve 3Pool\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_amount > 0, \"Must deposit something\");\n emit Deposit(_asset, pTokenAddress, _amount);\n\n // 3Pool requires passing deposit amounts for all 3 assets, set to 0 for\n // all\n uint256[3] memory _amounts;\n uint256 poolCoinIndex = _getCoinIndex(_asset);\n // Set the amount on the asset we want to deposit\n _amounts[poolCoinIndex] = _amount;\n ICurvePool curvePool = ICurvePool(platformAddress);\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n uint256 depositValue = _amount.scaleBy(18, assetDecimals).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = depositValue.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n // Do the deposit to 3pool\n curvePool.add_liquidity(_amounts, minMintAmount);\n _lpDepositAll();\n }\n\n function _lpDepositAll() internal virtual;\n\n /**\n * @dev Deposit the entire balance of any supported asset into the Curve 3pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n uint256 depositValue = 0;\n ICurvePool curvePool = ICurvePool(platformAddress);\n uint256 curveVirtualPrice = curvePool.get_virtual_price();\n\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address assetAddress = assetsMapped[i];\n uint256 balance = IERC20(assetAddress).balanceOf(address(this));\n if (balance > 0) {\n uint256 poolCoinIndex = _getCoinIndex(assetAddress);\n // Set the amount on the asset we want to deposit\n _amounts[poolCoinIndex] = balance;\n uint256 assetDecimals = Helpers.getDecimals(assetAddress);\n // Get value of deposit in Curve LP token to later determine\n // the minMintAmount argument for add_liquidity\n depositValue =\n depositValue +\n balance.scaleBy(18, assetDecimals).divPrecisely(\n curveVirtualPrice\n );\n emit Deposit(assetAddress, pTokenAddress, balance);\n }\n }\n\n uint256 minMintAmount = depositValue.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n // Do the deposit to 3pool\n curvePool.add_liquidity(_amounts, minMintAmount);\n\n /* In case of Curve Strategy all assets are mapped to the same pToken (3CrvLP). Let\n * descendants further handle the pToken. By either deploying it to the metapool and\n * resulting tokens in Gauge. Or deploying pTokens directly to the Gauge.\n */\n _lpDepositAll();\n }\n\n function _lpWithdraw(uint256 numCrvTokens) internal virtual;\n\n function _lpWithdrawAll() internal virtual;\n\n /**\n * @dev Withdraw asset from Curve 3Pool\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Invalid amount\");\n\n emit Withdrawal(_asset, pTokenAddress, _amount);\n\n uint256 contractCrv3Tokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n\n uint256 coinIndex = _getCoinIndex(_asset);\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 requiredCrv3Tokens = _calcCurveTokenAmount(coinIndex, _amount);\n\n // We have enough LP tokens, make sure they are all on this contract\n if (contractCrv3Tokens < requiredCrv3Tokens) {\n _lpWithdraw(requiredCrv3Tokens - contractCrv3Tokens);\n }\n\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n _amounts[coinIndex] = _amount;\n\n curvePool.remove_liquidity_imbalance(_amounts, requiredCrv3Tokens);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Calculate amount of LP required when withdrawing specific amount of one\n * of the underlying assets accounting for fees and slippage.\n *\n * Curve pools unfortunately do not contain a calculation function for\n * amount of LP required when withdrawing a specific amount of one of the\n * underlying tokens and also accounting for fees (Curve's calc_token_amount\n * does account for slippage but not fees).\n *\n * Steps taken to calculate the metric:\n * - get amount of LP required if fees wouldn't apply\n * - increase the LP amount as if fees would apply to the entirety of the underlying\n * asset withdrawal. (when withdrawing only one coin fees apply only to amounts\n * of other assets pool would return in case of balanced removal - since those need\n * to be swapped for the single underlying asset being withdrawn)\n * - get amount of underlying asset withdrawn (this Curve function does consider slippage\n * and fees) when using the increased LP amount. As LP amount is slightly over-increased\n * so is amount of underlying assets returned.\n * - since we know exactly how much asset we require take the rate of LP required for asset\n * withdrawn to get the exact amount of LP.\n */\n function _calcCurveTokenAmount(uint256 _coinIndex, uint256 _amount)\n internal\n returns (uint256 required3Crv)\n {\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n _amounts[_coinIndex] = _amount;\n\n // LP required when removing required asset ignoring fees\n uint256 lpRequiredNoFees = curvePool.calc_token_amount(_amounts, false);\n /* LP required if fees would apply to entirety of removed amount\n *\n * fee is 1e10 denominated number: https://curve.readthedocs.io/exchange-pools.html#StableSwap.fee\n */\n uint256 lpRequiredFullFees = lpRequiredNoFees.mulTruncateScale(\n 1e10 + curvePool.fee(),\n 1e10\n );\n\n /* asset received when withdrawing full fee applicable LP accounting for\n * slippage and fees\n */\n uint256 assetReceivedForFullLPFees = curvePool.calc_withdraw_one_coin(\n lpRequiredFullFees,\n int128(uint128(_coinIndex))\n );\n\n // exact amount of LP required\n required3Crv =\n (lpRequiredFullFees * _amount) /\n assetReceivedForFullLPFees;\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n _lpWithdrawAll();\n // Withdraws are proportional to assets held by 3Pool\n uint256[3] memory minWithdrawAmounts = [\n uint256(0),\n uint256(0),\n uint256(0)\n ];\n\n // Remove liquidity\n ICurvePool threePool = ICurvePool(platformAddress);\n threePool.remove_liquidity(\n IERC20(pTokenAddress).balanceOf(address(this)),\n minWithdrawAmounts\n );\n // Transfer assets out of Vault\n // Note that Curve will provide all 3 of the assets in 3pool even if\n // we have not set PToken addresses for all of them in this strategy\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n IERC20 asset = IERC20(threePool.coins(i));\n asset.safeTransfer(vaultAddress, asset.balanceOf(address(this)));\n }\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 totalPTokens = IERC20(pTokenAddress).balanceOf(address(this));\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (totalPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = (totalPTokens * virtual_price) / 1e18;\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = value.scaleBy(assetDecimals, 18) / THREEPOOL_ASSET_COUNT;\n }\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n // This strategy is a special case since it only supports one asset\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n _approveAsset(assetsMapped[i]);\n }\n }\n\n /**\n * @dev Call the necessary approvals for the Curve pool and gauge\n * @param _asset Address of the asset\n */\n function _abstractSetPToken(address _asset, address) internal override {\n _approveAsset(_asset);\n }\n\n function _approveAsset(address _asset) internal {\n IERC20 asset = IERC20(_asset);\n // 3Pool for asset (required for adding liquidity)\n asset.safeApprove(platformAddress, 0);\n asset.safeApprove(platformAddress, type(uint256).max);\n }\n\n function _approveBase() internal virtual;\n\n /**\n * @dev Get the index of the coin\n */\n function _getCoinIndex(address _asset) internal view returns (uint256) {\n for (uint256 i = 0; i < 3; i++) {\n if (assetsMapped[i] == _asset) return i;\n }\n revert(\"Invalid 3pool asset\");\n }\n}\n" + }, + "contracts/strategies/aerodrome/AerodromeAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Aerodrome AMO strategy\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nimport { ISugarHelper } from \"../../interfaces/aerodrome/ISugarHelper.sol\";\nimport { INonfungiblePositionManager } from \"../../interfaces/aerodrome/INonfungiblePositionManager.sol\";\nimport { ISwapRouter } from \"../../interfaces/aerodrome/ISwapRouter.sol\";\nimport { ICLPool } from \"../../interfaces/aerodrome/ICLPool.sol\";\nimport { ICLGauge } from \"../../interfaces/aerodrome/ICLGauge.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\n\ncontract AerodromeAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n using SafeCast for uint256;\n\n /************************************************\n Important (!) setup configuration\n *************************************************/\n\n /**\n * In order to be able to remove a reasonable amount of complexity from the contract one of the\n * preconditions for this contract to function correctly is to have an outside account mint a small\n * amount of liquidity in the tick space where the contract will deploy's its liquidity and then send\n * that NFT LP position to a dead address (transfer to zero address not allowed.) See example of such\n * NFT LP token:\n * https://basescan.org/token/0x827922686190790b37229fd06084350e74485b72?a=413296#inventory\n */\n\n /***************************************\n Storage slot members\n ****************************************/\n\n /// @notice tokenId of the liquidity position\n uint256 public tokenId;\n /// @dev Minimum amount of tokens the strategy would be able to withdraw from the pool.\n /// minimum amount of tokens are withdrawn at a 1:1 price\n uint256 public underlyingAssets;\n /// @notice Marks the start of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareStart;\n /// @notice Marks the end of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareEnd;\n /// @dev reserved for inheritance\n int256[46] private __reserved;\n\n /***************************************\n Constants, structs and events\n ****************************************/\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the OETHb token contract\n address public immutable OETHb;\n /// @notice lower tick set to -1 representing the price of 1.0001 of WETH for 1 OETHb.\n int24 public immutable lowerTick;\n /// @notice lower tick set to 0 representing the price of 1.0000 of WETH for 1 OETHb.\n int24 public immutable upperTick;\n /// @notice tick spacing of the pool (set to 1)\n int24 public immutable tickSpacing;\n /// @notice the swapRouter for performing swaps\n ISwapRouter public immutable swapRouter;\n /// @notice the underlying AMO Slipstream pool\n ICLPool public immutable clPool;\n /// @notice the gauge for the corresponding Slipstream pool (clPool)\n /// @dev can become an immutable once the gauge is created on the base main-net\n ICLGauge public immutable clGauge;\n /// @notice the Position manager contract that is used to manage the pool's position\n INonfungiblePositionManager public immutable positionManager;\n /// @notice helper contract for liquidity and ticker math\n ISugarHelper public immutable helper;\n /// @notice sqrtRatioX96TickLower\n /// @dev tick lower has value -1 and represents the lowest price of WETH priced in OETHb. Meaning the pool\n /// offers less than 1 OETHb for 1 WETH. In other terms to get 1 OETHB the swap needs to offer 1.0001 WETH\n /// this is where purchasing OETHb with WETH within the liquidity position is most expensive\n uint160 public immutable sqrtRatioX96TickLower;\n /// @notice sqrtRatioX96TickHigher\n /// @dev tick higher has value 0 and represents 1:1 price parity of WETH to OETHb\n uint160 public immutable sqrtRatioX96TickHigher;\n /// @dev tick closest to 1:1 price parity\n /// Correctly assessing which tick is closer to 1:1 price parity is important since it affects\n /// the way we calculate the underlying assets in check Balance. The underlying aerodrome pool\n /// orders the tokens depending on the values of their addresses. If OETH token is token0 in the pool\n /// then sqrtRatioX96TickClosestToParity=sqrtRatioX96TickLower. If it is token1 in the pool then\n /// sqrtRatioX96TickClosestToParity=sqrtRatioX96TickHigher\n uint160 public immutable sqrtRatioX96TickClosestToParity;\n\n /// @dev a threshold under which the contract no longer allows for the protocol to rebalance. Guarding\n /// against a strategist / guardian being taken over and with multiple transactions draining the\n /// protocol funds.\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth); // 0x989e5ca8\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth); // 0xa6737d87\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n ); // 0x3681e8e0\n error OutsideExpectedTickRange(int24 currentTick); // 0x5a2eba75\n\n event PoolRebalanced(uint256 currentPoolWethShare);\n\n event PoolWethShareIntervalUpdated(\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n\n event LiquidityRemoved(\n uint256 withdrawLiquidityShare,\n uint256 removedWETHAmount,\n uint256 removedOETHbAmount,\n uint256 wethAmountCollected,\n uint256 oethbAmountCollected,\n uint256 underlyingAssets\n );\n\n event LiquidityAdded(\n uint256 wethAmountDesired,\n uint256 oethbAmountDesired,\n uint256 wethAmountSupplied,\n uint256 oethbAmountSupplied,\n uint256 tokenId,\n uint256 underlyingAssets\n );\n\n event UnderlyingAssetsUpdated(uint256 underlyingAssets);\n\n /**\n * @dev Un-stakes the token from the gauge for the execution duration of\n * the function and after that re-stakes it back in.\n *\n * It is important that the token is unstaked and owned by the strategy contract\n * during any liquidity altering operations and that it is re-staked back into the\n * gauge after liquidity changes. If the token fails to re-stake back to the\n * gauge it is not earning incentives.\n */\n // all functions using this modifier are used by functions with reentrancy check\n // slither-disable-start reentrancy-no-eth\n modifier gaugeUnstakeAndRestake() {\n // because of solidity short-circuit _isLpTokenStakedInGauge doesn't get called\n // when tokenId == 0\n if (tokenId != 0 && _isLpTokenStakedInGauge()) {\n clGauge.withdraw(tokenId);\n }\n _;\n // because of solidity short-circuit _isLpTokenStakedInGauge doesn't get called\n // when tokenId == 0\n if (tokenId != 0 && !_isLpTokenStakedInGauge()) {\n /**\n * It can happen that a withdrawal (or a full withdrawal) transactions would\n * remove all of the liquidity from the token with a NFT token still existing.\n * In that case the token can not be staked into the gauge, as some liquidity\n * needs to be added to it first.\n */\n if (_getLiquidity() > 0) {\n // if token liquidity changes the positionManager requires re-approval.\n // to any contract pre-approved to handle the token.\n positionManager.approve(address(clGauge), tokenId);\n clGauge.deposit(tokenId);\n }\n }\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice the constructor\n /// @dev This contract is intended to be used as a proxy. To prevent the\n /// potential confusion of having a functional implementation contract\n /// the constructor has the `initializer` modifier. This way the\n /// `initialize` function can not be called on the implementation contract.\n /// For the same reason the implementation contract also has the governor\n /// set to a zero address.\n /// @param _stratConfig the basic strategy configuration\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _oethbAddress Address of the Erc20 OETHb Token contract\n /// @param _swapRouter Address of the Aerodrome Universal Swap Router\n /// @param _nonfungiblePositionManager Address of position manager to add/remove\n /// the liquidity\n /// @param _clPool Address of the Aerodrome concentrated liquidity pool\n /// @param _clGauge Address of the Aerodrome slipstream pool gauge\n /// @param _sugarHelper Address of the Aerodrome Sugar helper contract\n /// @param _lowerBoundingTick Smaller bounding tick of our liquidity position\n /// @param _upperBoundingTick Larger bounding tick of our liquidity position\n /// @param _tickClosestToParity Tick that is closer to 1:1 price parity\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethbAddress,\n address _swapRouter,\n address _nonfungiblePositionManager,\n address _clPool,\n address _clGauge,\n address _sugarHelper,\n int24 _lowerBoundingTick,\n int24 _upperBoundingTick,\n int24 _tickClosestToParity\n ) initializer InitializableAbstractStrategy(_stratConfig) {\n require(\n _lowerBoundingTick == _tickClosestToParity ||\n _upperBoundingTick == _tickClosestToParity,\n \"Misconfigured tickClosestToParity\"\n );\n require(\n ICLPool(_clPool).token0() == _wethAddress,\n \"Only WETH supported as token0\"\n );\n require(\n ICLPool(_clPool).token1() == _oethbAddress,\n \"Only OETHb supported as token1\"\n );\n int24 _tickSpacing = ICLPool(_clPool).tickSpacing();\n // when we generalize AMO we might support other tick spacings\n require(_tickSpacing == 1, \"Unsupported tickSpacing\");\n\n WETH = _wethAddress;\n OETHb = _oethbAddress;\n swapRouter = ISwapRouter(_swapRouter);\n positionManager = INonfungiblePositionManager(\n _nonfungiblePositionManager\n );\n clPool = ICLPool(_clPool);\n clGauge = ICLGauge(_clGauge);\n helper = ISugarHelper(_sugarHelper);\n sqrtRatioX96TickLower = ISugarHelper(_sugarHelper).getSqrtRatioAtTick(\n _lowerBoundingTick\n );\n sqrtRatioX96TickHigher = ISugarHelper(_sugarHelper).getSqrtRatioAtTick(\n _upperBoundingTick\n );\n sqrtRatioX96TickClosestToParity = ISugarHelper(_sugarHelper)\n .getSqrtRatioAtTick(_tickClosestToParity);\n\n lowerTick = _lowerBoundingTick;\n upperTick = _upperBoundingTick;\n tickSpacing = _tickSpacing;\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /**\n * @notice initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n */\n function initialize(address[] memory _rewardTokenAddresses)\n external\n onlyGovernor\n initializer\n {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n new address[](0),\n new address[](0)\n );\n }\n\n /***************************************\n Configuration \n ****************************************/\n\n /**\n * @notice Set allowed pool weth share interval. After the rebalance happens\n * the share of WETH token in the ticker needs to be withing the specifications\n * of the interval.\n *\n * @param _allowedWethShareStart Start of WETH share interval expressed as 18 decimal amount\n * @param _allowedWethShareEnd End of WETH share interval expressed as 18 decimal amount\n */\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external onlyGovernor {\n require(\n _allowedWethShareStart < _allowedWethShareEnd,\n \"Invalid interval\"\n );\n // can not go below 1% weth share\n require(_allowedWethShareStart > 0.01 ether, \"Invalid interval start\");\n // can not go above 95% weth share\n require(_allowedWethShareEnd < 0.95 ether, \"Invalid interval end\");\n\n allowedWethShareStart = _allowedWethShareStart;\n allowedWethShareEnd = _allowedWethShareEnd;\n emit PoolWethShareIntervalUpdated(\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n\n /***************************************\n Periphery utils\n ****************************************/\n\n function _isLpTokenStakedInGauge() internal view returns (bool) {\n require(tokenId != 0, \"Missing NFT LP token\");\n\n address owner = positionManager.ownerOf(tokenId);\n require(\n owner == address(clGauge) || owner == address(this),\n \"Unexpected token owner\"\n );\n return owner == address(clGauge);\n }\n\n /***************************************\n Strategy overrides \n ****************************************/\n\n /**\n * @notice Deposit an amount of assets into the strategy contract. Calling deposit doesn't\n * automatically deposit funds into the underlying Aerodrome pool\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposit WETH to the strategy contract. This function does not add liquidity to the\n * underlying Aerodrome pool.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance > 1e12) {\n _deposit(WETH, _wethBalance);\n }\n }\n\n /**\n * @dev Deposit WETH to the contract. This function doesn't deposit the liquidity to the\n * pool, that is done via the rebalance call.\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n emit Deposit(_asset, address(0), _amount);\n\n // if the pool price is not within the expected interval leave the WETH on the contract\n // as to not break the mints\n (bool _isExpectedRange, ) = _checkForExpectedPoolPrice(false);\n if (_isExpectedRange) {\n // deposit funds into the underlying pool\n _rebalance(0, false, 0);\n }\n }\n\n /**\n * @notice Rebalance the pool to the desired token split and Deposit any WETH on the contract to the\n * underlying aerodrome pool. Print the required amount of corresponding OETHb. After the rebalancing is\n * done burn any potentially remaining OETHb tokens still on the strategy contract.\n *\n * This function has a slightly different behaviour depending on the status of the underlying Aerodrome\n * slipstream pool. The function consists of the following 3 steps:\n * 1. withdrawPartialLiquidity -> so that moving the activeTrading price via a swap is cheaper\n * 2. swapToDesiredPosition -> move active trading price in the pool to be able to deposit WETH & OETHb\n * tokens with the desired pre-configured shares\n * 3. addLiquidity -> add liquidity into the pool respecting share split configuration\n *\n * Scenario 1: When there is no liquidity in the pool from the strategy but there is from other LPs then\n * only step 1 is skipped. (It is important to note that liquidity needs to exist in the configured\n * strategy tick ranges in order for the swap to be possible) Step 3 mints new liquidity position\n * instead of adding to an existing one.\n * Scenario 2: When there is strategy's liquidity in the pool all 3 steps are taken\n *\n *\n * Exact _amountToSwap, _swapWeth & _minTokenReceived parameters shall be determined by simulating the\n * transaction off-chain. The strategy checks that after the swap the share of the tokens is in the\n * expected ranges.\n *\n * @param _amountToSwap The amount of the token to swap\n * @param _swapWeth Swap using WETH when true, use OETHb when false\n * @param _minTokenReceived Slippage check -> minimum amount of token expected in return\n */\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) external nonReentrant onlyGovernorOrStrategist {\n _rebalance(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n\n function _rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n /**\n * Would be nice to check if there is any total liquidity in the pool before performing this swap\n * but there is no easy way to do that in UniswapV3:\n * - clPool.liquidity() -> only liquidity in the active tick\n * - asset[1&2].balanceOf(address(clPool)) -> will include uncollected tokens of LP providers\n * after their liquidity position has been decreased\n */\n\n /**\n * When rebalance is called for the first time there is no strategy\n * liquidity in the pool yet. The liquidity removal is thus skipped.\n * Also execute this function when WETH is required for the swap.\n */\n if (tokenId != 0 && _swapWeth && _amountToSwap > 0) {\n _ensureWETHBalance(_amountToSwap);\n }\n\n // in some cases we will just want to add liquidity and not issue a swap to move the\n // active trading position within the pool\n if (_amountToSwap > 0) {\n _swapToDesiredPosition(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n // calling check liquidity early so we don't get unexpected errors when adding liquidity\n // in the later stages of this function\n _checkForExpectedPoolPrice(true);\n\n _addLiquidity();\n\n // this call shouldn't be necessary, since adding liquidity shouldn't affect the active\n // trading price. It is a defensive programming measure.\n (, uint256 _wethSharePct) = _checkForExpectedPoolPrice(true);\n\n // revert if protocol insolvent\n _solvencyAssert();\n\n emit PoolRebalanced(_wethSharePct);\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethbSupply = IERC20(OETHb).totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethbSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /**\n * @dev Decrease partial or all liquidity from the pool.\n * @param _liquidityToDecrease The amount of liquidity to remove expressed in 18 decimal point\n */\n function _removeLiquidity(uint256 _liquidityToDecrease)\n internal\n gaugeUnstakeAndRestake\n {\n require(_liquidityToDecrease > 0, \"Must remove some liquidity\");\n\n uint128 _liquidity = _getLiquidity();\n // need to convert to uint256 since intermittent result is to big for uint128 to handle\n uint128 _liquidityToRemove = uint256(_liquidity)\n .mulTruncate(_liquidityToDecrease)\n .toUint128();\n\n /**\n * There is no liquidity to remove -> exit function early. This can happen after a\n * withdraw/withdrawAll removes all of the liquidity while retaining the NFT token.\n */\n if (_liquidity == 0 || _liquidityToRemove == 0) {\n return;\n }\n\n (uint256 _amountWeth, uint256 _amountOethb) = positionManager\n .decreaseLiquidity(\n // Both expected amounts can be 0 since we don't really care if any swaps\n // happen just before the liquidity removal.\n INonfungiblePositionManager.DecreaseLiquidityParams({\n tokenId: tokenId,\n liquidity: _liquidityToRemove,\n amount0Min: 0,\n amount1Min: 0,\n deadline: block.timestamp\n })\n );\n\n (\n uint256 _amountWethCollected,\n uint256 _amountOethbCollected\n ) = positionManager.collect(\n INonfungiblePositionManager.CollectParams({\n tokenId: tokenId,\n recipient: address(this),\n amount0Max: type(uint128).max, // defaults to all tokens owed\n amount1Max: type(uint128).max // defaults to all tokens owed\n })\n );\n\n _updateUnderlyingAssets();\n\n emit LiquidityRemoved(\n _liquidityToDecrease,\n _amountWeth, //removedWethAmount\n _amountOethb, //removedOethbAmount\n _amountWethCollected,\n _amountOethbCollected,\n underlyingAssets\n );\n\n _burnOethbOnTheContract();\n }\n\n /**\n * @dev Perform a swap so that after the swap the ticker has the desired WETH to OETHb token share.\n */\n function _swapToDesiredPosition(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n IERC20 _tokenToSwap = IERC20(_swapWeth ? WETH : OETHb);\n uint256 _balance = _tokenToSwap.balanceOf(address(this));\n\n if (_balance < _amountToSwap) {\n // This should never trigger since _ensureWETHBalance will already\n // throw an error if there is not enough WETH\n if (_swapWeth) {\n revert NotEnoughWethForSwap(_balance, _amountToSwap);\n }\n // if swapping OETHb\n uint256 mintForSwap = _amountToSwap - _balance;\n IVault(vaultAddress).mintForStrategy(mintForSwap);\n }\n\n // approve the specific amount of WETH required\n if (_swapWeth) {\n IERC20(WETH).approve(address(swapRouter), _amountToSwap);\n }\n\n // Swap it\n swapRouter.exactInputSingle(\n // sqrtPriceLimitX96 is just a rough sanity check that we are within 0 -> 1 tick\n // a more fine check is performed in _checkForExpectedPoolPrice\n // Note: this needs further work if we want to generalize this approach\n ISwapRouter.ExactInputSingleParams({\n tokenIn: address(_tokenToSwap),\n tokenOut: _swapWeth ? OETHb : WETH,\n tickSpacing: tickSpacing, // set to 1\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: _amountToSwap,\n amountOutMinimum: _minTokenReceived, // slippage check\n sqrtPriceLimitX96: _swapWeth\n ? sqrtRatioX96TickLower\n : sqrtRatioX96TickHigher\n })\n );\n\n /**\n * In the interest of each function in _rebalance to leave the contract state as\n * clean as possible the OETHb tokens here are burned. This decreases the\n * dependence where `_swapToDesiredPosition` function relies on later functions\n * (`addLiquidity`) to burn the OETHb. Reducing the risk of error introduction.\n */\n _burnOethbOnTheContract();\n }\n\n /**\n * @dev Add liquidity into the pool in the pre-configured WETH to OETHb share ratios\n * defined by the allowedPoolWethShareStart|End interval. This function will respect\n * liquidity ratios when there is no liquidity yet in the pool. If liquidity is already\n * present then it relies on the `_swapToDesiredPosition` function in a step before\n * to already move the trading price to desired position (with some tolerance).\n */\n // rebalance already has re-entrency checks\n // slither-disable-start reentrancy-no-eth\n function _addLiquidity() internal gaugeUnstakeAndRestake {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n // don't deposit small liquidity amounts\n if (_wethBalance <= 1e12) {\n return;\n }\n\n uint160 _currentPrice = getPoolX96Price();\n /**\n * Sanity check active trading price is positioned within our desired tick.\n *\n * We revert when price is equal to the lower tick even though that is still\n * a valid amount in regards to ticker position by Sugar.estimateAmount call.\n * Current price equaling tick bound at the 1:1 price parity results in\n * uint overfow when calculating the OETHb balance to deposit.\n */\n if (\n _currentPrice <= sqrtRatioX96TickLower ||\n _currentPrice >= sqrtRatioX96TickHigher\n ) {\n revert OutsideExpectedTickRange(getCurrentTradingTick());\n }\n\n /**\n * If estimateAmount1 call fails it could be due to _currentPrice being really\n * close to a tick and amount1 is a larger number than the sugar helper is able\n * to compute.\n *\n * If token addresses were reversed estimateAmount0 would be required here\n */\n uint256 _oethbRequired = helper.estimateAmount1(\n _wethBalance,\n address(0), // no need to pass pool address when current price is specified\n _currentPrice,\n lowerTick,\n upperTick\n );\n\n if (_oethbRequired > _oethbBalance) {\n IVault(vaultAddress).mintForStrategy(\n _oethbRequired - _oethbBalance\n );\n }\n\n // approve the specific amount of WETH required\n IERC20(WETH).approve(address(positionManager), _wethBalance);\n\n uint256 _wethAmountSupplied;\n uint256 _oethbAmountSupplied;\n if (tokenId == 0) {\n (\n tokenId,\n ,\n _wethAmountSupplied,\n _oethbAmountSupplied\n ) = positionManager.mint(\n /** amount0Min & amount1Min are left at 0 because slippage protection is ensured by the\n * _checkForExpectedPoolPrice\n *›\n * Also sqrtPriceX96 is 0 because the pool is already created\n * non zero amount attempts to create a new instance of the pool\n */\n INonfungiblePositionManager.MintParams({\n token0: WETH,\n token1: OETHb,\n tickSpacing: tickSpacing,\n tickLower: lowerTick,\n tickUpper: upperTick,\n amount0Desired: _wethBalance,\n amount1Desired: _oethbRequired,\n amount0Min: 0,\n amount1Min: 0,\n recipient: address(this),\n deadline: block.timestamp,\n sqrtPriceX96: 0\n })\n );\n } else {\n (, _wethAmountSupplied, _oethbAmountSupplied) = positionManager\n .increaseLiquidity(\n /** amount0Min & amount1Min are left at 0 because slippage protection is ensured by the\n * _checkForExpectedPoolPrice\n */\n INonfungiblePositionManager.IncreaseLiquidityParams({\n tokenId: tokenId,\n amount0Desired: _wethBalance,\n amount1Desired: _oethbRequired,\n amount0Min: 0,\n amount1Min: 0,\n deadline: block.timestamp\n })\n );\n }\n\n _updateUnderlyingAssets();\n emit LiquidityAdded(\n _wethBalance, // wethAmountDesired\n _oethbRequired, // oethbAmountDesired\n _wethAmountSupplied, // wethAmountSupplied\n _oethbAmountSupplied, // oethbAmountSupplied\n tokenId, // tokenId\n underlyingAssets\n );\n\n // burn remaining OETHb\n _burnOethbOnTheContract();\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @dev Check that the Aerodrome pool price is within the expected\n * parameters.\n * This function works whether the strategy contract has liquidity\n * position in the pool or not. The function returns _wethSharePct\n * as a gas optimization measure.\n * @param throwException when set to true the function throws an exception\n * when pool's price is not within expected range.\n * @return _isExpectedRange Bool expressing price is within expected range\n * @return _wethSharePct Share of WETH owned by this strategy contract in the\n * configured ticker.\n */\n function _checkForExpectedPoolPrice(bool throwException)\n internal\n view\n returns (bool _isExpectedRange, uint256 _wethSharePct)\n {\n require(\n allowedWethShareStart != 0 && allowedWethShareEnd != 0,\n \"Weth share interval not set\"\n );\n\n uint160 _currentPrice = getPoolX96Price();\n\n /**\n * First check we are in expected tick range\n *\n * We revert even though price being equal to the lower tick would still\n * count being within lower tick for the purpose of Sugar.estimateAmount calls\n */\n if (\n _currentPrice <= sqrtRatioX96TickLower ||\n _currentPrice >= sqrtRatioX96TickHigher\n ) {\n if (throwException) {\n revert OutsideExpectedTickRange(getCurrentTradingTick());\n }\n return (false, 0);\n }\n\n // 18 decimal number expressed WETH tick share\n _wethSharePct = _getWethShare(_currentPrice);\n\n if (\n _wethSharePct < allowedWethShareStart ||\n _wethSharePct > allowedWethShareEnd\n ) {\n if (throwException) {\n revert PoolRebalanceOutOfBounds(\n _wethSharePct,\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n return (false, _wethSharePct);\n }\n\n return (true, _wethSharePct);\n }\n\n /**\n * Burns any OETHb tokens remaining on the strategy contract\n */\n function _burnOethbOnTheContract() internal {\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n if (_oethbBalance > 1e12) {\n IVault(vaultAddress).burnForStrategy(_oethbBalance);\n }\n }\n\n /// @dev This function assumes there are no uncollected tokens in the clPool owned by the strategy contract.\n /// For that reason any liquidity withdrawals must also collect the tokens.\n function _updateUnderlyingAssets() internal {\n if (tokenId == 0) {\n underlyingAssets = 0;\n emit UnderlyingAssetsUpdated(underlyingAssets);\n return;\n }\n\n uint128 _liquidity = _getLiquidity();\n\n /**\n * Our net value represent the smallest amount of tokens we are able to extract from the position\n * given our liquidity.\n *\n * The least amount of tokens extraditable from the position is where the active trading price is\n * at the ticker 0 meaning the pool is offering 1:1 trades between WETH & OETHb. At that moment the pool\n * consists completely of OETHb and no WETH.\n *\n * The more swaps from WETH -> OETHb happen on the pool the more the price starts to move towards the -1\n * ticker making OETHb (priced in WETH) more expensive.\n *\n * An additional note: when liquidity is 0 then the helper returns 0 for both token amounts. And the\n * function set underlying assets to 0.\n */\n (uint256 _wethAmount, uint256 _oethbAmount) = helper\n .getAmountsForLiquidity(\n sqrtRatioX96TickClosestToParity, // sqrtRatioX96\n sqrtRatioX96TickLower, // sqrtRatioAX96\n sqrtRatioX96TickHigher, // sqrtRatioBX96\n _liquidity\n );\n\n require(_wethAmount == 0, \"Non zero wethAmount\");\n underlyingAssets = _oethbAmount;\n emit UnderlyingAssetsUpdated(underlyingAssets);\n }\n\n /**\n * @dev This function removes the appropriate amount of liquidity to assure that the required\n * amount of WETH is available on the contract\n *\n * @param _amount WETH balance required on the contract\n */\n function _ensureWETHBalance(uint256 _amount) internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance >= _amount) {\n return;\n }\n\n require(tokenId != 0, \"No liquidity available\");\n uint256 _additionalWethRequired = _amount - _wethBalance;\n (uint256 _wethInThePool, ) = getPositionPrincipal();\n\n if (_wethInThePool < _additionalWethRequired) {\n revert NotEnoughWethLiquidity(\n _wethInThePool,\n _additionalWethRequired\n );\n }\n\n uint256 shareOfWethToRemove = Math.min(\n _additionalWethRequired.divPrecisely(_wethInThePool) + 1,\n 1e18\n );\n _removeLiquidity(shareOfWethToRemove);\n }\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset WETH address\n * @param _amount Amount of WETH to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n _ensureWETHBalance(_amount);\n\n _withdraw(_recipient, _amount);\n }\n\n /**\n * @notice Withdraw WETH and sends it to the Vault.\n */\n function withdrawAll() external override onlyVault nonReentrant {\n if (tokenId != 0) {\n _removeLiquidity(1e18);\n }\n\n uint256 _balance = IERC20(WETH).balanceOf(address(this));\n if (_balance > 0) {\n _withdraw(vaultAddress, _balance);\n }\n }\n\n function _withdraw(address _recipient, uint256 _amount) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n IERC20(WETH).safeTransfer(_recipient, _amount);\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /**\n * @dev Collect the AERO token from the gauge\n */\n function _collectRewardTokens() internal override {\n if (tokenId != 0 && _isLpTokenStakedInGauge()) {\n clGauge.getReward(tokenId);\n }\n super._collectRewardTokens();\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /**\n * @dev Approve the spending of all assets\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n // to add liquidity to the clPool\n IERC20(OETHb).approve(address(positionManager), type(uint256).max);\n // to be able to rebalance using the swapRouter\n IERC20(OETHb).approve(address(swapRouter), type(uint256).max);\n\n /* the behaviour of this strategy has slightly changed and WETH could be\n * present on the contract between the transactions. For that reason we are\n * un-approving WETH to the swapRouter & positionManager and only approving\n * the required amount before a transaction\n */\n IERC20(WETH).approve(address(swapRouter), 0);\n IERC20(WETH).approve(address(positionManager), 0);\n }\n\n /***************************************\n Balances and Fees\n ****************************************/\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256)\n {\n require(_asset == WETH, \"Only WETH supported\");\n\n // we could in theory deposit to the strategy and forget to call rebalance in the same\n // governance transaction batch. In that case the WETH that is on the strategy contract\n // also needs to be accounted for.\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n // just paranoia check, in case there is OETHb in the strategy that for some reason hasn't\n // been burned yet.\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n return underlyingAssets + _wethBalance + _oethbBalance;\n }\n\n /**\n * @dev Returns the balance of both tokens in a given position (excluding fees)\n * @return _amountWeth Amount of WETH in position\n * @return _amountOethb Amount of OETHb in position\n */\n function getPositionPrincipal()\n public\n view\n returns (uint256 _amountWeth, uint256 _amountOethb)\n {\n if (tokenId == 0) {\n return (0, 0);\n }\n\n uint160 _sqrtRatioX96 = getPoolX96Price();\n (_amountWeth, _amountOethb) = helper.principal(\n positionManager,\n tokenId,\n _sqrtRatioX96\n );\n }\n\n /**\n * @notice Returns the current pool price in X96 format\n * @return _sqrtRatioX96 Pool price\n */\n function getPoolX96Price() public view returns (uint160 _sqrtRatioX96) {\n (_sqrtRatioX96, , , , , ) = clPool.slot0();\n }\n\n /**\n * @notice Returns the current active trading tick of the underlying pool\n * @return _currentTick Current pool trading tick\n */\n function getCurrentTradingTick() public view returns (int24 _currentTick) {\n (, _currentTick, , , , ) = clPool.slot0();\n }\n\n /**\n * @notice Returns the percentage of WETH liquidity in the configured ticker\n * owned by this strategy contract.\n * @return uint256 1e18 denominated percentage expressing the share\n */\n function getWETHShare() external view returns (uint256) {\n uint160 _currentPrice = getPoolX96Price();\n return _getWethShare(_currentPrice);\n }\n\n /**\n * @notice Returns the amount of liquidity in the contract's LP position\n * @return _liquidity Amount of liquidity in the position\n */\n function _getLiquidity() internal view returns (uint128 _liquidity) {\n if (tokenId == 0) {\n revert(\"No LP position\");\n }\n\n (, , , , , , , _liquidity, , , , ) = positionManager.positions(tokenId);\n }\n\n function _getWethShare(uint160 _currentPrice)\n internal\n view\n returns (uint256)\n {\n /**\n * If estimateAmount1 call fails it could be due to _currentPrice being really\n * close to a tick and amount1 too big to compute.\n *\n * If token addresses were reversed estimateAmount0 would be required here\n */\n uint256 _normalizedWethAmount = 1 ether;\n uint256 _correspondingOethAmount = helper.estimateAmount1(\n _normalizedWethAmount,\n address(0), // no need to pass pool address when current price is specified\n _currentPrice,\n lowerTick,\n upperTick\n );\n\n // 18 decimal number expressed weth tick share\n return\n _normalizedWethAmount.divPrecisely(\n _normalizedWethAmount + _correspondingOethAmount\n );\n }\n\n /***************************************\n Hidden functions\n ****************************************/\n /// @inheritdoc InitializableAbstractStrategy\n function setPTokenAddress(address, address) external override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /// @inheritdoc InitializableAbstractStrategy\n function removePToken(uint256) external override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Not supported\n */\n function _abstractSetPToken(address, address) internal override {\n // the deployer shall call safeApproveAllTokens() to set necessary approvals\n revert(\"Unsupported method\");\n }\n\n /***************************************\n ERC721 management\n ****************************************/\n\n /// @notice Callback function for whenever a NFT is transferred to this contract\n // solhint-disable-next-line max-line-length\n /// Ref: https://docs.openzeppelin.com/contracts/3.x/api/token/erc721#IERC721Receiver-onERC721Received-address-address-uint256-bytes-\n function onERC721Received(\n address,\n address,\n uint256,\n bytes calldata\n ) external returns (bytes4) {\n return this.onERC721Received.selector;\n }\n}\n" + }, + "contracts/strategies/balancer/AbstractAuraStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Base Balancer Abstract Strategy\n * @author Origin Protocol Inc\n */\n\nimport { AbstractBalancerStrategy } from \"./AbstractBalancerStrategy.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IERC4626 } from \"../../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport { IRewardStaking } from \"../IRewardStaking.sol\";\n\nabstract contract AbstractAuraStrategy is AbstractBalancerStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n /// @notice Address of the Aura rewards pool\n address public immutable auraRewardPoolAddress;\n\n // renamed from __reserved to not shadow AbstractBalancerStrategy.__reserved,\n int256[50] private __reserved_baseAuraStrategy;\n\n constructor(address _auraRewardPoolAddress) {\n auraRewardPoolAddress = _auraRewardPoolAddress;\n }\n\n /**\n * @dev Deposit all Balancer Pool Tokens (BPT) in this strategy contract\n * to the Aura rewards pool.\n */\n function _lpDepositAll() internal virtual override {\n uint256 bptBalance = IERC20(platformAddress).balanceOf(address(this));\n uint256 auraLp = IERC4626(auraRewardPoolAddress).deposit(\n bptBalance,\n address(this)\n );\n require(bptBalance == auraLp, \"Aura LP != BPT\");\n }\n\n /**\n * @dev Withdraw `numBPTTokens` Balancer Pool Tokens (BPT) from\n * the Aura rewards pool to this strategy contract.\n * @param numBPTTokens Number of Balancer Pool Tokens (BPT) to withdraw\n */\n function _lpWithdraw(uint256 numBPTTokens) internal virtual override {\n IRewardStaking(auraRewardPoolAddress).withdrawAndUnwrap(\n numBPTTokens,\n true // also claim reward tokens\n );\n }\n\n /**\n * @dev Withdraw all Balancer Pool Tokens (BPT) from\n * the Aura rewards pool to this strategy contract.\n */\n function _lpWithdrawAll() internal virtual override {\n // Get all the strategy's BPTs in Aura\n // maxRedeem is implemented as balanceOf(address) in Aura\n uint256 bptBalance = IERC4626(auraRewardPoolAddress).maxRedeem(\n address(this)\n );\n\n IRewardStaking(auraRewardPoolAddress).withdrawAndUnwrap(\n bptBalance,\n true // also claim reward tokens\n );\n }\n\n /**\n * @notice Collects BAL and AURA tokens from the rewards pool.\n */\n function collectRewardTokens()\n external\n virtual\n override\n onlyHarvester\n nonReentrant\n {\n /* Similar to Convex, calling this function collects both of the\n * accrued BAL and AURA tokens.\n */\n IRewardStaking(auraRewardPoolAddress).getReward();\n _collectRewardTokens();\n }\n\n /// @notice Balancer Pool Tokens (BPT) in the Balancer pool and the Aura rewards pool.\n function _getBalancerPoolTokens()\n internal\n view\n override\n returns (uint256 balancerPoolTokens)\n {\n balancerPoolTokens =\n IERC20(platformAddress).balanceOf(address(this)) +\n // maxRedeem is implemented as balanceOf(address) in Aura\n IERC4626(auraRewardPoolAddress).maxRedeem(address(this));\n }\n\n function _approveBase() internal virtual override {\n super._approveBase();\n\n IERC20 pToken = IERC20(platformAddress);\n pToken.safeApprove(auraRewardPoolAddress, type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/balancer/AbstractBalancerStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Balancer Abstract Strategy\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\nimport { IRateProvider } from \"../../interfaces/balancer/IRateProvider.sol\";\nimport { VaultReentrancyLib } from \"./VaultReentrancyLib.sol\";\nimport { IOracle } from \"../../interfaces/IOracle.sol\";\nimport { IWstETH } from \"../../interfaces/IWstETH.sol\";\nimport { IERC4626 } from \"../../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nabstract contract AbstractBalancerStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n address public immutable rETH;\n address public immutable stETH;\n address public immutable wstETH;\n address public immutable frxETH;\n address public immutable sfrxETH;\n\n /// @notice Address of the Balancer vault\n IBalancerVault public immutable balancerVault;\n /// @notice Balancer pool identifier\n bytes32 public immutable balancerPoolId;\n\n // Max withdrawal deviation denominated in 1e18 (1e18 == 100%)\n uint256 public maxWithdrawalDeviation;\n // Max deposit deviation denominated in 1e18 (1e18 == 100%)\n uint256 public maxDepositDeviation;\n\n int256[48] private __reserved;\n\n struct BaseBalancerConfig {\n address rEthAddress; // Address of the rETH token\n address stEthAddress; // Address of the stETH token\n address wstEthAddress; // Address of the wstETH token\n address frxEthAddress; // Address of the frxEth token\n address sfrxEthAddress; // Address of the sfrxEth token\n address balancerVaultAddress; // Address of the Balancer vault\n bytes32 balancerPoolId; // Balancer pool identifier\n }\n\n event MaxWithdrawalDeviationUpdated(\n uint256 _prevMaxDeviationPercentage,\n uint256 _newMaxDeviationPercentage\n );\n event MaxDepositDeviationUpdated(\n uint256 _prevMaxDeviationPercentage,\n uint256 _newMaxDeviationPercentage\n );\n\n /**\n * @dev Ensure we are not in a Vault context when this function is called, by attempting a no-op internal\n * balance operation. If we are already in a Vault transaction (e.g., a swap, join, or exit), the Vault's\n * reentrancy protection will cause this function to revert.\n *\n * Use this modifier with any function that can cause a state change in a pool and is either public itself,\n * or called by a public function *outside* a Vault operation (e.g., join, exit, or swap).\n *\n * This is to protect against Balancer's read-only re-entrancy vulnerability:\n * https://www.notion.so/originprotocol/Balancer-read-only-reentrancy-c686e72c82414ef18fa34312bb02e11b\n */\n modifier whenNotInBalancerVaultContext() {\n VaultReentrancyLib.ensureNotInVaultContext(balancerVault);\n _;\n }\n\n constructor(BaseBalancerConfig memory _balancerConfig) {\n rETH = _balancerConfig.rEthAddress;\n stETH = _balancerConfig.stEthAddress;\n wstETH = _balancerConfig.wstEthAddress;\n frxETH = _balancerConfig.frxEthAddress;\n sfrxETH = _balancerConfig.sfrxEthAddress;\n\n balancerVault = IBalancerVault(_balancerConfig.balancerVaultAddress);\n balancerPoolId = _balancerConfig.balancerPoolId;\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Balancer's strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of BAL & AURA\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * WETH, stETH\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // BAL & AURA\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n maxWithdrawalDeviation = 1e16;\n maxDepositDeviation = 1e16;\n\n emit MaxWithdrawalDeviationUpdated(0, maxWithdrawalDeviation);\n emit MaxDepositDeviationUpdated(0, maxDepositDeviation);\n\n IERC20[] memory poolAssets = _getPoolAssets();\n require(\n poolAssets.length == _assets.length,\n \"Pool assets length mismatch\"\n );\n for (uint256 i = 0; i < _assets.length; ++i) {\n address asset = _fromPoolAsset(address(poolAssets[i]));\n require(_assets[i] == asset, \"Pool assets mismatch\");\n }\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @notice Get strategy's share of an assets in the Balancer pool.\n * This is not denominated in OUSD/ETH value of the assets in the Balancer pool.\n * @param _asset Address of the Vault collateral asset\n * @return amount the amount of vault collateral assets\n *\n * IMPORTANT if this function is overridden it needs to have a whenNotInBalancerVaultContext\n * modifier on it or it is susceptible to read-only re-entrancy attack\n *\n * @dev it is important that this function is not affected by reporting inflated\n * values of assets in case of any pool manipulation. Such a manipulation could easily\n * exploit the protocol by:\n * - minting OETH\n * - tilting Balancer pool to report higher balances of assets\n * - rebasing() -> all that extra token balances get distributed to OETH holders\n * - tilting pool back\n * - redeeming OETH\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n whenNotInBalancerVaultContext\n returns (uint256 amount)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n\n uint256 bptBalance = _getBalancerPoolTokens();\n\n /* To calculate the worth of queried asset:\n * - assume that all tokens normalized to their ETH value have an equal split balance\n * in the pool when it is balanced\n * - multiply the BPT amount with the bpt rate to get the ETH denominated amount\n * of strategy's holdings\n * - divide that by the number of tokens we support in the pool to get ETH denominated\n * amount that is applicable to each supported token in the pool.\n *\n * It would be possible to support only 1 asset in the pool (and be exposed to all\n * the assets while holding BPT tokens) and deposit/withdraw/checkBalance using only\n * that asset. TBD: changes to other functions still required if we ever decide to\n * go with such configuration.\n */\n amount = (bptBalance.mulTruncate(\n IRateProvider(platformAddress).getRate()\n ) / assetsMapped.length);\n\n /* If the pool asset is equal to (strategy )_asset it means that a rate\n * provider for that asset exists and that asset is not necessarily\n * pegged to a unit (ETH).\n *\n * Because this function returns the balance of the asset and is not denominated in\n * ETH units we need to convert the ETH denominated amount to asset amount.\n */\n if (_toPoolAsset(_asset) == _asset) {\n amount = amount.divPrecisely(_getRateProviderRate(_asset));\n }\n }\n\n /**\n * @notice Returns the value of all assets managed by this strategy.\n * Uses the Balancer pool's rate (virtual price) to convert the strategy's\n * Balancer Pool Tokens (BPT) to ETH value.\n * @return value The ETH value\n *\n * IMPORTANT if this function is overridden it needs to have a whenNotInBalancerVaultContext\n * modifier on it or it is susceptible to read-only re-entrancy attack\n */\n function checkBalance()\n external\n view\n virtual\n whenNotInBalancerVaultContext\n returns (uint256 value)\n {\n uint256 bptBalance = _getBalancerPoolTokens();\n\n // Convert BPT to ETH value\n value = bptBalance.mulTruncate(\n IRateProvider(platformAddress).getRate()\n );\n }\n\n /// @notice Balancer Pool Tokens (BPT) in the Balancer pool.\n function _getBalancerPoolTokens()\n internal\n view\n virtual\n returns (uint256 balancerPoolTokens)\n {\n balancerPoolTokens = IERC20(platformAddress).balanceOf(address(this));\n }\n\n /* solhint-disable max-line-length */\n /**\n * @notice BPT price is calculated by taking the rate from the rateProvider of the asset in\n * question. If one does not exist it defaults to 1e18. To get the final BPT expected that\n * is multiplied by the underlying asset amount divided by BPT token rate. BPT token rate is\n * similar to Curve's virtual_price and expresses how much has the price of BPT appreciated\n * (e.g. due to swap fees) in relation to the underlying assets\n *\n * Using the above approach makes the strategy vulnerable to a possible MEV attack using\n * flash loan to manipulate the pool before a deposit/withdrawal since the function ignores\n * market values of the assets being priced in BPT.\n *\n * At the time of writing there is no safe on-chain approach to pricing BPT in a way that it\n * would make it invulnerable to MEV pool manipulation. See recent Balancer exploit:\n * https://www.notion.so/originprotocol/Balancer-OETH-strategy-9becdea132704e588782a919d7d471eb?pvs=4#1cf07de12fc64f1888072321e0644348\n *\n * To mitigate MEV possibilities during deposits and withdraws, the VaultValueChecker will use checkBalance before and after the move\n * to ensure the expected changes took place.\n *\n * @param _asset Address of the Balancer pool asset\n * @param _amount Amount of the Balancer pool asset\n * @return bptExpected of BPT expected in exchange for the asset\n *\n * @dev\n * bptAssetPrice = 1e18 (asset peg) * pool_asset_rate\n *\n * bptExpected = bptAssetPrice * asset_amount / BPT_token_rate\n *\n * bptExpected = 1e18 (asset peg) * pool_asset_rate * asset_amount / BPT_token_rate\n * bptExpected = asset_amount * pool_asset_rate / BPT_token_rate\n *\n * further information available here:\n * https://www.notion.so/originprotocol/Balancer-OETH-strategy-9becdea132704e588782a919d7d471eb?pvs=4#ce01495ae70346d8971f5dced809fb83\n */\n /* solhint-enable max-line-length */\n function _getBPTExpected(address _asset, uint256 _amount)\n internal\n view\n virtual\n returns (uint256 bptExpected)\n {\n uint256 bptRate = IRateProvider(platformAddress).getRate();\n uint256 poolAssetRate = _getRateProviderRate(_asset);\n bptExpected = _amount.mulTruncate(poolAssetRate).divPrecisely(bptRate);\n }\n\n function _getBPTExpected(\n address[] memory _assets,\n uint256[] memory _amounts\n ) internal view virtual returns (uint256 bptExpected) {\n require(_assets.length == _amounts.length, \"Assets & amounts mismatch\");\n\n for (uint256 i = 0; i < _assets.length; ++i) {\n uint256 poolAssetRate = _getRateProviderRate(_assets[i]);\n // convert asset amount to ETH amount\n bptExpected += _amounts[i].mulTruncate(poolAssetRate);\n }\n\n uint256 bptRate = IRateProvider(platformAddress).getRate();\n // Convert ETH amount to BPT amount\n bptExpected = bptExpected.divPrecisely(bptRate);\n }\n\n function _lpDepositAll() internal virtual;\n\n function _lpWithdraw(uint256 numBPTTokens) internal virtual;\n\n function _lpWithdrawAll() internal virtual;\n\n /**\n * @notice Balancer returns assets and rateProviders for corresponding assets ordered\n * by numerical order.\n */\n function _getPoolAssets() internal view returns (IERC20[] memory assets) {\n // slither-disable-next-line unused-return\n (assets, , ) = balancerVault.getPoolTokens(balancerPoolId);\n }\n\n /**\n * @dev If an asset is rebasing the Balancer pools have a wrapped versions of assets\n * that the strategy supports. This function converts the pool(wrapped) asset\n * and corresponding amount to strategy asset.\n */\n function _toPoolAsset(address asset, uint256 amount)\n internal\n view\n returns (address poolAsset, uint256 poolAmount)\n {\n if (asset == stETH) {\n poolAsset = wstETH;\n if (amount > 0) {\n poolAmount = IWstETH(wstETH).getWstETHByStETH(amount);\n }\n } else if (asset == frxETH) {\n poolAsset = sfrxETH;\n if (amount > 0) {\n poolAmount = IERC4626(sfrxETH).convertToShares(amount);\n }\n } else {\n poolAsset = asset;\n poolAmount = amount;\n }\n }\n\n /**\n * @dev Converts a Vault collateral asset to a Balancer pool asset.\n * stETH becomes wstETH, frxETH becomes sfrxETH and everything else stays the same.\n * @param asset Address of the Vault collateral asset.\n * @return Address of the Balancer pool asset.\n */\n function _toPoolAsset(address asset) internal view returns (address) {\n if (asset == stETH) {\n return wstETH;\n } else if (asset == frxETH) {\n return sfrxETH;\n }\n return asset;\n }\n\n /**\n * @dev Converts rebasing asset to its wrapped counterpart.\n */\n function _wrapPoolAsset(address asset, uint256 amount)\n internal\n returns (address wrappedAsset, uint256 wrappedAmount)\n {\n if (asset == stETH) {\n wrappedAsset = wstETH;\n if (amount > 0) {\n wrappedAmount = IWstETH(wstETH).wrap(amount);\n }\n } else if (asset == frxETH) {\n wrappedAsset = sfrxETH;\n if (amount > 0) {\n wrappedAmount = IERC4626(sfrxETH).deposit(\n amount,\n address(this)\n );\n }\n } else {\n wrappedAsset = asset;\n wrappedAmount = amount;\n }\n }\n\n /**\n * @dev Converts wrapped asset to its rebasing counterpart.\n */\n function _unwrapPoolAsset(address asset, uint256 amount)\n internal\n returns (uint256 unwrappedAmount)\n {\n if (asset == stETH) {\n unwrappedAmount = IWstETH(wstETH).unwrap(amount);\n } else if (asset == frxETH) {\n unwrappedAmount = IERC4626(sfrxETH).withdraw(\n amount,\n address(this),\n address(this)\n );\n } else {\n unwrappedAmount = amount;\n }\n }\n\n /**\n * @dev If an asset is rebasing the Balancer pools have a wrapped versions of assets\n * that the strategy supports. This function converts the rebasing strategy asset\n * and corresponding amount to wrapped(pool) asset.\n */\n function _fromPoolAsset(address poolAsset, uint256 poolAmount)\n internal\n view\n returns (address asset, uint256 amount)\n {\n if (poolAsset == wstETH) {\n asset = stETH;\n if (poolAmount > 0) {\n amount = IWstETH(wstETH).getStETHByWstETH(poolAmount);\n }\n } else if (poolAsset == sfrxETH) {\n asset = frxETH;\n if (poolAmount > 0) {\n amount = IERC4626(sfrxETH).convertToAssets(poolAmount);\n }\n } else {\n asset = poolAsset;\n amount = poolAmount;\n }\n }\n\n function _fromPoolAsset(address poolAsset)\n internal\n view\n returns (address asset)\n {\n if (poolAsset == wstETH) {\n asset = stETH;\n } else if (poolAsset == sfrxETH) {\n asset = frxETH;\n } else {\n asset = poolAsset;\n }\n }\n\n /**\n * @notice Sets max withdrawal deviation that is considered when removing\n * liquidity from Balancer pools.\n * @param _maxWithdrawalDeviation Max withdrawal deviation denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxWithdrawalDeviation will be 1% (1e16) for production\n * usage. Vault value checker in combination with checkBalance will\n * catch any unexpected manipulation.\n */\n function setMaxWithdrawalDeviation(uint256 _maxWithdrawalDeviation)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(\n _maxWithdrawalDeviation <= 1e18,\n \"Withdrawal dev. out of bounds\"\n );\n emit MaxWithdrawalDeviationUpdated(\n maxWithdrawalDeviation,\n _maxWithdrawalDeviation\n );\n maxWithdrawalDeviation = _maxWithdrawalDeviation;\n }\n\n /**\n * @notice Sets max deposit deviation that is considered when adding\n * liquidity to Balancer pools.\n * @param _maxDepositDeviation Max deposit deviation denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxDepositDeviation will default to 1% (1e16)\n * for production usage. Vault value checker in combination with\n * checkBalance will catch any unexpected manipulation.\n */\n function setMaxDepositDeviation(uint256 _maxDepositDeviation)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(_maxDepositDeviation <= 1e18, \"Deposit dev. out of bounds\");\n emit MaxDepositDeviationUpdated(\n maxDepositDeviation,\n _maxDepositDeviation\n );\n maxDepositDeviation = _maxDepositDeviation;\n }\n\n function _approveBase() internal virtual {\n IERC20 pToken = IERC20(platformAddress);\n // Balancer vault for BPT token (required for removing liquidity)\n pToken.safeApprove(address(balancerVault), type(uint256).max);\n }\n\n function _getRateProviderRate(address _asset)\n internal\n view\n virtual\n returns (uint256);\n}\n" + }, + "contracts/strategies/balancer/BalancerMetaPoolStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Balancer MetaStablePool Strategy\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { AbstractAuraStrategy, AbstractBalancerStrategy } from \"./AbstractAuraStrategy.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\nimport { IRateProvider } from \"../../interfaces/balancer/IRateProvider.sol\";\nimport { IMetaStablePool } from \"../../interfaces/balancer/IMetaStablePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\ncontract BalancerMetaPoolStrategy is AbstractAuraStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(\n BaseStrategyConfig memory _stratConfig,\n BaseBalancerConfig memory _balancerConfig,\n address _auraRewardPoolAddress\n )\n InitializableAbstractStrategy(_stratConfig)\n AbstractBalancerStrategy(_balancerConfig)\n AbstractAuraStrategy(_auraRewardPoolAddress)\n {}\n\n /**\n * @notice There are no plans to configure BalancerMetaPool as a default\n * asset strategy. For that reason there is no need to support this\n * functionality.\n */\n function deposit(address, uint256)\n external\n override\n onlyVault\n nonReentrant\n {\n revert(\"Not supported\");\n }\n\n /**\n * @notice There are no plans to configure BalancerMetaPool as a default\n * asset strategy. For that reason there is no need to support this\n * functionality.\n */\n function deposit(address[] calldata, uint256[] calldata)\n external\n onlyVault\n nonReentrant\n {\n revert(\"Not supported\");\n }\n\n /**\n * @notice Deposits all supported assets in this strategy contract to the Balancer pool.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 assetsLength = assetsMapped.length;\n address[] memory strategyAssets = new address[](assetsLength);\n uint256[] memory strategyAmounts = new uint256[](assetsLength);\n\n // For each vault collateral asset\n for (uint256 i = 0; i < assetsLength; ++i) {\n strategyAssets[i] = assetsMapped[i];\n // Get the asset balance in this strategy contract\n strategyAmounts[i] = IERC20(strategyAssets[i]).balanceOf(\n address(this)\n );\n }\n _deposit(strategyAssets, strategyAmounts);\n }\n\n /*\n * _deposit doesn't require a read-only re-entrancy protection since during the deposit\n * the function enters the Balancer Vault Context. If this function were called as part of\n * the attacking contract (while intercepting execution flow upon receiving ETH) the read-only\n * protection of the Balancer Vault would be triggered. Since the attacking contract would\n * already be in the Balancer Vault context and wouldn't be able to enter it again.\n */\n function _deposit(\n address[] memory _strategyAssets,\n uint256[] memory _strategyAmounts\n ) internal {\n require(\n _strategyAssets.length == _strategyAmounts.length,\n \"Array length missmatch\"\n );\n\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n\n uint256[] memory strategyAssetAmountsToPoolAssetAmounts = new uint256[](\n _strategyAssets.length\n );\n address[] memory strategyAssetsToPoolAssets = new address[](\n _strategyAssets.length\n );\n\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n address strategyAsset = _strategyAssets[i];\n uint256 strategyAmount = _strategyAmounts[i];\n\n require(\n assetToPToken[strategyAsset] != address(0),\n \"Unsupported asset\"\n );\n strategyAssetsToPoolAssets[i] = _toPoolAsset(strategyAsset);\n\n if (strategyAmount > 0) {\n emit Deposit(strategyAsset, platformAddress, strategyAmount);\n\n // wrap rebasing assets like stETH and frxETH to wstETH and sfrxETH\n (, strategyAssetAmountsToPoolAssetAmounts[i]) = _wrapPoolAsset(\n strategyAsset,\n strategyAmount\n );\n }\n }\n\n uint256[] memory amountsIn = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n for (uint256 i = 0; i < tokens.length; ++i) {\n // Convert IERC20 type to address\n poolAssets[i] = address(tokens[i]);\n\n // For each of the mapped assets\n for (uint256 j = 0; j < strategyAssetsToPoolAssets.length; ++j) {\n // If the pool asset is the same as the mapped asset\n if (poolAssets[i] == strategyAssetsToPoolAssets[j]) {\n amountsIn[i] = strategyAssetAmountsToPoolAssetAmounts[j];\n }\n }\n }\n\n uint256 minBPT = _getBPTExpected(\n strategyAssetsToPoolAssets,\n strategyAssetAmountsToPoolAssetAmounts\n );\n uint256 minBPTwDeviation = minBPT.mulTruncate(\n 1e18 - maxDepositDeviation\n );\n\n /* EXACT_TOKENS_IN_FOR_BPT_OUT:\n * User sends precise quantities of tokens, and receives an\n * estimated but unknown (computed at run time) quantity of BPT.\n *\n * ['uint256', 'uint256[]', 'uint256']\n * [EXACT_TOKENS_IN_FOR_BPT_OUT, amountsIn, minimumBPT]\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,\n amountsIn,\n minBPTwDeviation\n );\n\n IBalancerVault.JoinPoolRequest memory request = IBalancerVault\n .JoinPoolRequest(poolAssets, amountsIn, userData, false);\n\n // Add the pool assets in this strategy to the balancer pool\n balancerVault.joinPool(\n balancerPoolId,\n address(this),\n address(this),\n request\n );\n\n // Deposit the Balancer Pool Tokens (BPT) into Aura\n _lpDepositAll();\n }\n\n /**\n * @notice Withdraw a Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAsset Address of the Vault collateral asset\n * @param _strategyAmount The amount of Vault collateral assets to withdraw\n */\n function withdraw(\n address _recipient,\n address _strategyAsset,\n uint256 _strategyAmount\n ) external override onlyVault nonReentrant {\n address[] memory strategyAssets = new address[](1);\n uint256[] memory strategyAmounts = new uint256[](1);\n strategyAssets[0] = _strategyAsset;\n strategyAmounts[0] = _strategyAmount;\n\n _withdraw(_recipient, strategyAssets, strategyAmounts);\n }\n\n /**\n * @notice Withdraw multiple Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAssets Addresses of the Vault collateral assets\n * @param _strategyAmounts The amounts of Vault collateral assets to withdraw\n */\n function withdraw(\n address _recipient,\n address[] calldata _strategyAssets,\n uint256[] calldata _strategyAmounts\n ) external onlyVault nonReentrant {\n _withdraw(_recipient, _strategyAssets, _strategyAmounts);\n }\n\n /**\n * @dev Withdraw multiple Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAssets Addresses of the Vault collateral assets\n * @param _strategyAmounts The amounts of Vault collateral assets to withdraw\n *\n * _withdrawal doesn't require a read-only re-entrancy protection since during the withdrawal\n * the function enters the Balancer Vault Context. If this function were called as part of\n * the attacking contract (while intercepting execution flow upon receiving ETH) the read-only\n * protection of the Balancer Vault would be triggered. Since the attacking contract would\n * already be in the Balancer Vault context and wouldn't be able to enter it again.\n */\n function _withdraw(\n address _recipient,\n address[] memory _strategyAssets,\n uint256[] memory _strategyAmounts\n ) internal {\n require(\n _strategyAssets.length == _strategyAmounts.length,\n \"Invalid input arrays\"\n );\n\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n require(\n assetToPToken[_strategyAssets[i]] != address(0),\n \"Unsupported asset\"\n );\n }\n\n // STEP 1 - Calculate the Balancer pool assets and amounts from the vault collateral assets\n\n // Get all the supported balancer pool assets\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n // Calculate the balancer pool assets and amounts to withdraw\n uint256[] memory poolAssetsAmountsOut = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n // Is the wrapped asset amount indexed by the assets array, not the order of the Balancer pool tokens\n // eg wstETH and sfrxETH amounts, not the stETH and frxETH amounts\n uint256[] memory strategyAssetsToPoolAssetsAmounts = new uint256[](\n _strategyAssets.length\n );\n\n // For each of the Balancer pool assets\n for (uint256 i = 0; i < tokens.length; ++i) {\n poolAssets[i] = address(tokens[i]);\n\n // Convert the Balancer pool asset back to a vault collateral asset\n address strategyAsset = _fromPoolAsset(poolAssets[i]);\n\n // for each of the vault assets\n for (uint256 j = 0; j < _strategyAssets.length; ++j) {\n // If the vault asset equals the vault asset mapped from the Balancer pool asset\n if (_strategyAssets[j] == strategyAsset) {\n (, poolAssetsAmountsOut[i]) = _toPoolAsset(\n strategyAsset,\n _strategyAmounts[j]\n );\n strategyAssetsToPoolAssetsAmounts[j] = poolAssetsAmountsOut[\n i\n ];\n\n /* Because of the potential Balancer rounding error mentioned below\n * the contract might receive 1-2 WEI smaller amount than required\n * in the withdraw user data encoding. If slightly lesser token amount\n * is received the strategy can not unwrap the pool asset as it is\n * smaller than expected.\n *\n * For that reason we `overshoot` the required tokens expected to\n * circumvent the error\n */\n if (poolAssetsAmountsOut[i] > 0) {\n poolAssetsAmountsOut[i] += 2;\n }\n }\n }\n }\n\n // STEP 2 - Calculate the max about of Balancer Pool Tokens (BPT) to withdraw\n\n // Estimate the required amount of Balancer Pool Tokens (BPT) for the assets\n uint256 maxBPTtoWithdraw = _getBPTExpected(\n poolAssets,\n /* all non 0 values are overshot by 2 WEI and with the expected mainnet\n * ~1% withdrawal deviation, the 2 WEI aren't important\n */\n poolAssetsAmountsOut\n );\n // Increase BPTs by the max allowed deviation\n // Any excess BPTs will be left in this strategy contract\n maxBPTtoWithdraw = maxBPTtoWithdraw.mulTruncate(\n 1e18 + maxWithdrawalDeviation\n );\n\n // STEP 3 - Withdraw the Balancer Pool Tokens (BPT) from Aura to this strategy contract\n\n // Withdraw BPT from Aura allowing for BPTs left in this strategy contract from previous withdrawals\n _lpWithdraw(\n maxBPTtoWithdraw - IERC20(platformAddress).balanceOf(address(this))\n );\n\n // STEP 4 - Withdraw the balancer pool assets from the pool\n\n /* Custom asset exit: BPT_IN_FOR_EXACT_TOKENS_OUT:\n * User sends an estimated but unknown (computed at run time) quantity of BPT,\n * and receives precise quantities of specified tokens.\n *\n * ['uint256', 'uint256[]', 'uint256']\n * [BPT_IN_FOR_EXACT_TOKENS_OUT, amountsOut, maxBPTAmountIn]\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.BPT_IN_FOR_EXACT_TOKENS_OUT,\n poolAssetsAmountsOut,\n maxBPTtoWithdraw\n );\n\n IBalancerVault.ExitPoolRequest memory request = IBalancerVault\n .ExitPoolRequest(\n poolAssets,\n /* We specify the exact amount of a tokens we are expecting in the encoded\n * userData, for that reason we don't need to specify the amountsOut here.\n *\n * Also Balancer has a rounding issue that can make a transaction fail:\n * https://github.com/balancer/balancer-v2-monorepo/issues/2541\n * which is an extra reason why this field is empty.\n */\n new uint256[](tokens.length),\n userData,\n false\n );\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n /* Payable keyword is required because of the IBalancerVault interface even though\n * this strategy shall never be receiving native ETH\n */\n payable(address(this)),\n request\n );\n\n // STEP 5 - Re-deposit any left over BPT tokens back into Aura\n /* When concluding how much of BPT we need to withdraw from Aura we overshoot by\n * roughly around 1% (initial mainnet setting of maxWithdrawalDeviation). After exiting\n * the pool strategy could have left over BPT tokens that are not earning boosted yield.\n * We re-deploy those back in.\n */\n _lpDepositAll();\n\n // STEP 6 - Unswap balancer pool assets to vault collateral assets and send to the vault.\n\n // For each of the specified assets\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n // Unwrap assets like wstETH and sfrxETH to rebasing assets stETH and frxETH\n if (strategyAssetsToPoolAssetsAmounts[i] > 0) {\n _unwrapPoolAsset(\n _strategyAssets[i],\n strategyAssetsToPoolAssetsAmounts[i]\n );\n }\n\n // Transfer the vault collateral assets to the recipient, which is typically the vault\n if (_strategyAmounts[i] > 0) {\n IERC20(_strategyAssets[i]).safeTransfer(\n _recipient,\n _strategyAmounts[i]\n );\n\n emit Withdrawal(\n _strategyAssets[i],\n platformAddress,\n _strategyAmounts[i]\n );\n }\n }\n }\n\n /**\n * @notice Withdraws all supported Vault collateral assets from the Balancer pool\n * and send to the OToken's Vault.\n *\n * Is only executable by the OToken's Vault or the Governor.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n // STEP 1 - Withdraw all Balancer Pool Tokens (BPT) from Aura to this strategy contract\n\n _lpWithdrawAll();\n // Get the BPTs withdrawn from Aura plus any that were already in this strategy contract\n uint256 BPTtoWithdraw = IERC20(platformAddress).balanceOf(\n address(this)\n );\n // Get the balancer pool assets and their total balances\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n uint256[] memory minAmountsOut = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n for (uint256 i = 0; i < tokens.length; ++i) {\n poolAssets[i] = address(tokens[i]);\n }\n\n // STEP 2 - Withdraw the Balancer pool assets from the pool\n /* Proportional exit: EXACT_BPT_IN_FOR_TOKENS_OUT:\n * User sends a precise quantity of BPT, and receives an estimated but unknown\n * (computed at run time) quantity of a single token\n *\n * ['uint256', 'uint256']\n * [EXACT_BPT_IN_FOR_TOKENS_OUT, bptAmountIn]\n *\n * It is ok to pass an empty minAmountsOut since tilting the pool in any direction\n * when doing a proportional exit can only be beneficial to the strategy. Since\n * it will receive more of the underlying tokens for the BPT traded in.\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.EXACT_BPT_IN_FOR_TOKENS_OUT,\n BPTtoWithdraw\n );\n\n IBalancerVault.ExitPoolRequest memory request = IBalancerVault\n .ExitPoolRequest(poolAssets, minAmountsOut, userData, false);\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n /* Payable keyword is required because of the IBalancerVault interface even though\n * this strategy shall never be receiving native ETH\n */\n payable(address(this)),\n request\n );\n\n // STEP 3 - Convert the balancer pool assets to the vault collateral assets and send to the vault\n // For each of the Balancer pool assets\n for (uint256 i = 0; i < tokens.length; ++i) {\n address poolAsset = address(tokens[i]);\n // Convert the balancer pool asset to the strategy asset\n address strategyAsset = _fromPoolAsset(poolAsset);\n // Get the balancer pool assets withdraw from the pool plus any that were already in this strategy contract\n uint256 poolAssetAmount = IERC20(poolAsset).balanceOf(\n address(this)\n );\n\n // Unwrap assets like wstETH and sfrxETH to rebasing assets stETH and frxETH\n uint256 unwrappedAmount = 0;\n if (poolAssetAmount > 0) {\n unwrappedAmount = _unwrapPoolAsset(\n strategyAsset,\n poolAssetAmount\n );\n }\n\n // Transfer the vault collateral assets to the vault\n if (unwrappedAmount > 0) {\n IERC20(strategyAsset).safeTransfer(\n vaultAddress,\n unwrappedAmount\n );\n emit Withdrawal(\n strategyAsset,\n platformAddress,\n unwrappedAmount\n );\n }\n }\n }\n\n /**\n * @notice Approves the Balancer Vault to transfer poolAsset counterparts\n * of all of the supported assets from this strategy. E.g. stETH is a supported\n * strategy and Balancer Vault gets unlimited approval to transfer wstETH.\n *\n * If Balancer pool uses a wrapped version of a supported asset then also approve\n * unlimited usage of an asset to the contract responsible for wrapping.\n *\n * Approve unlimited spending by Balancer Vault and Aura reward pool of the\n * pool BPT tokens.\n *\n * Is only executable by the Governor.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n _abstractSetPToken(assetsMapped[i], platformAddress);\n }\n _approveBase();\n }\n\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address) internal override {\n address poolAsset = _toPoolAsset(_asset);\n if (_asset == stETH) {\n // slither-disable-next-line unused-return\n IERC20(stETH).approve(wstETH, type(uint256).max);\n } else if (_asset == frxETH) {\n // slither-disable-next-line unused-return\n IERC20(frxETH).approve(sfrxETH, type(uint256).max);\n }\n _approveAsset(poolAsset);\n }\n\n /**\n * @dev Approves the Balancer Vault to transfer an asset from\n * this strategy. The assets could be a Vault collateral asset\n * like WETH or rETH; or a Balancer pool asset that wraps the vault asset\n * like wstETH or sfrxETH.\n */\n function _approveAsset(address _asset) internal {\n IERC20 asset = IERC20(_asset);\n // slither-disable-next-line unused-return\n asset.approve(address(balancerVault), type(uint256).max);\n }\n\n /**\n * @notice Returns the rate supplied by the Balancer configured rate\n * provider. Rate is used to normalize the token to common underlying\n * pool denominator. (ETH for ETH Liquid staking derivatives)\n *\n * @param _asset Address of the Balancer pool asset\n * @return rate of the corresponding asset\n */\n function _getRateProviderRate(address _asset)\n internal\n view\n override\n returns (uint256)\n {\n IMetaStablePool pool = IMetaStablePool(platformAddress);\n IRateProvider[] memory providers = pool.getRateProviders();\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n\n uint256 providersLength = providers.length;\n for (uint256 i = 0; i < providersLength; ++i) {\n // _assets and corresponding rate providers are all in the same order\n if (address(tokens[i]) == _asset) {\n // rate provider doesn't exist, defaults to 1e18\n if (address(providers[i]) == address(0)) {\n return 1e18;\n }\n return providers[i].getRate();\n }\n }\n\n // should never happen\n assert(false);\n }\n}\n" + }, + "contracts/strategies/balancer/VaultReentrancyLib.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity >=0.7.0 <0.9.0;\n\nimport \"../../utils/BalancerErrors.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\n\nlibrary VaultReentrancyLib {\n /**\n * @dev Ensure we are not in a Vault context when this function is called, by attempting a no-op internal\n * balance operation. If we are already in a Vault transaction (e.g., a swap, join, or exit), the Vault's\n * reentrancy protection will cause this function to revert.\n *\n * The exact function call doesn't really matter: we're just trying to trigger the Vault reentrancy check\n * (and not hurt anything in case it works). An empty operation array with no specific operation at all works\n * for that purpose, and is also the least expensive in terms of gas and bytecode size.\n *\n * Call this at the top of any function that can cause a state change in a pool and is either public itself,\n * or called by a public function *outside* a Vault operation (e.g., join, exit, or swap).\n *\n * If this is *not* called in functions that are vulnerable to the read-only reentrancy issue described\n * here (https://forum.balancer.fi/t/reentrancy-vulnerability-scope-expanded/4345), those functions are unsafe,\n * and subject to manipulation that may result in loss of funds.\n */\n function ensureNotInVaultContext(IBalancerVault vault) internal view {\n // Perform the following operation to trigger the Vault's reentrancy guard:\n //\n // IBalancerVault.UserBalanceOp[] memory noop = new IBalancerVault.UserBalanceOp[](0);\n // _vault.manageUserBalance(noop);\n //\n // However, use a static call so that it can be a view function (even though the function is non-view).\n // This allows the library to be used more widely, as some functions that need to be protected might be\n // view.\n //\n // This staticcall always reverts, but we need to make sure it doesn't fail due to a re-entrancy attack.\n // Staticcalls consume all gas forwarded to them on a revert caused by storage modification.\n // By default, almost the entire available gas is forwarded to the staticcall,\n // causing the entire call to revert with an 'out of gas' error.\n //\n // We set the gas limit to 10k for the staticcall to\n // avoid wasting gas when it reverts due to storage modification.\n // `manageUserBalance` is a non-reentrant function in the Vault, so calling it invokes `_enterNonReentrant`\n // in the `ReentrancyGuard` contract, reproduced here:\n //\n // function _enterNonReentrant() private {\n // // If the Vault is actually being reentered, it will revert in the first line, at the `_require` that\n // // checks the reentrancy flag, with \"BAL#400\" (corresponding to Errors.REENTRANCY) in the revertData.\n // // The full revertData will be: `abi.encodeWithSignature(\"Error(string)\", \"BAL#400\")`.\n // _require(_status != _ENTERED, Errors.REENTRANCY);\n //\n // // If the Vault is not being reentered, the check above will pass: but it will *still* revert,\n // // because the next line attempts to modify storage during a staticcall. However, this type of\n // // failure results in empty revertData.\n // _status = _ENTERED;\n // }\n //\n // So based on this analysis, there are only two possible revertData values: empty, or abi.encoded BAL#400.\n //\n // It is of course much more bytecode and gas efficient to check for zero-length revertData than to compare it\n // to the encoded REENTRANCY revertData.\n //\n // While it should be impossible for the call to fail in any other way (especially since it reverts before\n // `manageUserBalance` even gets called), any other error would generate non-zero revertData, so checking for\n // empty data guards against this case too.\n\n (, bytes memory revertData) = address(vault).staticcall{ gas: 10_000 }(\n abi.encodeWithSelector(vault.manageUserBalance.selector, 0)\n );\n\n _require(revertData.length == 0, Errors.REENTRANCY);\n }\n}\n" + }, + "contracts/strategies/BaseCurveAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for the Curve OETH/WETH pool\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { ICurveStableSwapNG } from \"../interfaces/ICurveStableSwapNG.sol\";\nimport { ICurveXChainLiquidityGauge } from \"../interfaces/ICurveXChainLiquidityGauge.sol\";\nimport { IChildLiquidityGaugeFactory } from \"../interfaces/IChildLiquidityGaugeFactory.sol\";\n\ncontract BaseCurveAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @dev a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n // New immutable variables that must be set in the constructor\n /**\n * @notice Address of the Wrapped ETH (WETH) contract.\n */\n IWETH9 public immutable weth;\n\n /**\n * @notice Address of the OETH token contract.\n */\n IERC20 public immutable oeth;\n\n /**\n * @notice Address of the LP (Liquidity Provider) token contract.\n */\n IERC20 public immutable lpToken;\n\n /**\n * @notice Address of the Curve StableSwap NG pool contract.\n */\n ICurveStableSwapNG public immutable curvePool;\n\n /**\n * @notice Address of the Curve X-Chain Liquidity Gauge contract.\n */\n ICurveXChainLiquidityGauge public immutable gauge;\n\n /**\n * @notice Address of the Child Liquidity Gauge Factory contract.\n */\n IChildLiquidityGaugeFactory public immutable gaugeFactory;\n\n // Ordered list of pool assets\n uint128 public immutable oethCoinIndex;\n uint128 public immutable wethCoinIndex;\n\n /**\n * @notice Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n uint256 public maxSlippage;\n\n event MaxSlippageUpdated(uint256 newMaxSlippage);\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balancesBefore = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffBefore = balancesBefore[wethCoinIndex].toInt256() -\n balancesBefore[oethCoinIndex].toInt256();\n\n _;\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balancesAfter = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffAfter = balancesAfter[wethCoinIndex].toInt256() -\n balancesAfter[oethCoinIndex].toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of ETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _oeth,\n address _weth,\n address _gauge,\n address _gaugeFactory,\n uint128 _oethCoinIndex,\n uint128 _wethCoinIndex\n ) InitializableAbstractStrategy(_baseConfig) {\n oethCoinIndex = _oethCoinIndex;\n wethCoinIndex = _wethCoinIndex;\n\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveStableSwapNG(_baseConfig.platformAddress);\n\n oeth = IERC20(_oeth);\n weth = IWETH9(_weth);\n gauge = ICurveXChainLiquidityGauge(_gauge);\n gaugeFactory = IChildLiquidityGaugeFactory(_gaugeFactory);\n\n _setGovernor(address(0));\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV\n * @param _maxSlippage Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV\n uint256 _maxSlippage\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n address[] memory _assets = new address[](1);\n _assets[0] = address(weth);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n _setMaxSlippage(_maxSlippage);\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit WETH into the Curve pool\n * @param _weth Address of Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to deposit.\n */\n function deposit(address _weth, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_weth, _amount);\n }\n\n function _deposit(address _weth, uint256 _wethAmount) internal {\n require(_wethAmount > 0, \"Must deposit something\");\n require(_weth == address(weth), \"Can only deposit WETH\");\n\n emit Deposit(_weth, address(lpToken), _wethAmount);\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 oethToAdd = uint256(\n _max(\n 0,\n balances[wethCoinIndex].toInt256() +\n _wethAmount.toInt256() -\n balances[oethCoinIndex].toInt256()\n )\n );\n\n /* Add so much OETH so that the pool ends up being balanced. And at minimum\n * add as much OETH as WETH and at maximum twice as much OETH.\n */\n oethToAdd = Math.max(oethToAdd, _wethAmount);\n oethToAdd = Math.min(oethToAdd, _wethAmount * 2);\n\n /* Mint OETH with a strategy that attempts to contribute to stability of OETH/WETH pool. Try\n * to mint so much OETH that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OETH minted will always be at least equal or greater\n * to WETH amount deployed. And never larger than twice the WETH amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(oethToAdd);\n\n emit Deposit(address(oeth), address(lpToken), oethToAdd);\n\n uint256[] memory _amounts = new uint256[](2);\n _amounts[wethCoinIndex] = _wethAmount;\n _amounts[oethCoinIndex] = oethToAdd;\n\n uint256 valueInLpTokens = (_wethAmount + oethToAdd).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Do the deposit to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(_amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool's LP tokens into the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n /**\n * @notice Deposit the strategy's entire balance of WETH into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = weth.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(weth), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _weth Address of the Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to withdraw.\n */\n function withdraw(\n address _recipient,\n address _weth,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_weth == address(weth), \"Can only withdraw WETH\");\n\n emit Withdrawal(_weth, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(_amount);\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough WETH on balanced removal\n */\n uint256[] memory _minWithdrawalAmounts = new uint256[](2);\n _minWithdrawalAmounts[wethCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OETH and any that was left in the strategy\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n\n // Transfer WETH to the recipient\n require(\n weth.transfer(_recipient, _amount),\n \"Transfer of WETH not successful\"\n );\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n function calcTokenToBurn(uint256 _wethAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much WETH\n * we want we can determine how much of OETH we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolWETHBalance = curvePool.balances(wethCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolWETHBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_wethAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = gauge.balanceOf(address(this));\n // Can not withdraw zero LP tokens from the gauge\n if (gaugeTokens == 0) return;\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[] memory minWithdrawAmounts = new uint256[](2);\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(\n lpToken.balanceOf(address(this)),\n minWithdrawAmounts\n );\n\n // Burn all OETH\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Get the strategy contract's WETH balance.\n // This includes all that was removed from the Curve pool and\n // any ether that was sitting in the strategy contract before the removal.\n uint256 ethBalance = weth.balanceOf(address(this));\n require(\n weth.transfer(vaultAddress, ethBalance),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethBalance);\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[] memory amounts = new uint256[](2);\n amounts[oethCoinIndex] = _oTokens;\n\n // Convert OETH to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool LP tokens to the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Deposit(address(oeth), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough ETH.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove OTokens from the Curve pool\n uint256 oethToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n oethCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /**\n * @notice One-sided remove of ETH from the Curve pool, convert to WETH\n * and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for ETH.\n * @dev Curve pool LP tokens is used rather than WETH assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of ETH. Curve's `calc_token_amount` functioun does not include fees.\n * A 3rd party libary can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * caclulate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Curve gauge and remove ETH from the Curve pool\n uint256 ethAmount = _withdrawAndRemoveFromPool(\n _lpTokens,\n wethCoinIndex\n );\n\n // Transfer WETH to the vault\n require(\n weth.transfer(vaultAddress, ethAmount),\n \"Transfer of WETH not successful\"\n );\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(weth), address(lpToken), ethAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the Convex pool and\n * do a one-sided remove of ETH or OETH from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the Convex pool.\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = ETH, 1 = OETH.\n * @return coinsRemoved The amount of ETH or OETH removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Curve gauge\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to ETH value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n // Apply slippage to ETH value\n uint256 minAmount = valueInEth.mulTruncate(uint256(1e18) - maxSlippage);\n\n // Remove just the ETH from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethbSupply = oeth.totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethbSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // CRV rewards flow.\n //---\n // CRV inflation:\n // Gauge receive CRV rewards from inflation.\n // Each checkpoint on the gauge send this CRV inflation to gauge factory.\n // This strategy should call mint on the gauge factory to collect the CRV rewards.\n // ---\n // Extra rewards:\n // Calling claim_rewards on the gauge will only claim extra rewards (outside of CRV).\n // ---\n\n // Mint CRV on Child Liquidity gauge factory\n gaugeFactory.mint(address(gauge));\n // Collect extra gauge rewards (outside of CRV)\n gauge.claim_rewards();\n\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _lpAmount) internal {\n // withdraw lp tokens from the gauge without claiming rewards\n gauge.withdraw(_lpAmount);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // WETH balance needed here for the balance check that happens from vault during depositing.\n balance = weth.balanceOf(address(this));\n uint256 lpTokens = gauge.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += (lpTokens * curvePool.get_virtual_price()) / 1e18;\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(weth);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Sets the maximum slippage allowed for any swap/liquidity operation\n * @param _maxSlippage Maximum slippage allowed, 1e18 = 100%.\n */\n function setMaxSlippage(uint256 _maxSlippage) external onlyGovernor {\n _setMaxSlippage(_maxSlippage);\n }\n\n function _setMaxSlippage(uint256 _maxSlippage) internal {\n require(_maxSlippage <= 5e16, \"Slippage must be less than 100%\");\n maxSlippage = _maxSlippage;\n emit MaxSlippageUpdated(_maxSlippage);\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OETH (required for adding liquidity)\n // slither-disable-next-line unused-return\n oeth.approve(platformAddress, type(uint256).max);\n\n // Approve Curve pool for WETH (required for adding liquidity)\n // slither-disable-next-line unused-return\n weth.approve(platformAddress, type(uint256).max);\n\n // Approve Curve gauge contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Curve gauge.\n // slither-disable-next-line unused-return\n lpToken.approve(address(gauge), type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/BridgedWOETHStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, SafeERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { AggregatorV3Interface } from \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\n\ncontract BridgedWOETHStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using StableMath for uint128;\n using SafeCast for uint256;\n using SafeERC20 for IERC20;\n\n event MaxPriceDiffBpsUpdated(uint128 oldValue, uint128 newValue);\n event WOETHPriceUpdated(uint128 oldValue, uint128 newValue);\n\n IWETH9 public immutable weth;\n IERC20 public immutable bridgedWOETH;\n IERC20 public immutable oethb;\n\n uint256 public constant MAX_PRICE_STALENESS = 2 days;\n\n uint128 public lastOraclePrice;\n uint128 public maxPriceDiffBps;\n\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _weth,\n address _bridgedWOETH,\n address _oethb\n ) InitializableAbstractStrategy(_stratConfig) {\n weth = IWETH9(_weth);\n bridgedWOETH = IERC20(_bridgedWOETH);\n oethb = IERC20(_oethb);\n }\n\n function initialize(uint128 _maxPriceDiffBps)\n external\n onlyGovernor\n initializer\n {\n InitializableAbstractStrategy._initialize(\n new address[](0), // No reward tokens\n new address[](0), // No assets\n new address[](0) // No pTokens\n );\n\n _setMaxPriceDiffBps(_maxPriceDiffBps);\n }\n\n /**\n * @dev Sets the max price diff bps for the wOETH value appreciation\n * @param _maxPriceDiffBps Bps value, 10k == 100%\n */\n function setMaxPriceDiffBps(uint128 _maxPriceDiffBps)\n external\n onlyGovernor\n {\n _setMaxPriceDiffBps(_maxPriceDiffBps);\n }\n\n /**\n * @dev Sets the max price diff bps for the wOETH value appreciation\n * @param _maxPriceDiffBps Bps value, 10k == 100%\n */\n function _setMaxPriceDiffBps(uint128 _maxPriceDiffBps) internal {\n require(\n _maxPriceDiffBps > 0 && _maxPriceDiffBps <= 10000,\n \"Invalid bps value\"\n );\n\n emit MaxPriceDiffBpsUpdated(maxPriceDiffBps, _maxPriceDiffBps);\n\n maxPriceDiffBps = _maxPriceDiffBps;\n }\n\n /**\n * @dev Wrapper for _updateWOETHOraclePrice with nonReentrant flag\n * @return The latest price of wOETH from Oracle\n */\n function updateWOETHOraclePrice() external nonReentrant returns (uint256) {\n return _updateWOETHOraclePrice();\n }\n\n /**\n * @dev Finds the value of bridged wOETH from the Oracle.\n * Ensures that it's within the bounds and reasonable.\n * And stores it.\n *\n * NOTE: Intentionally not caching `Vault.priceProvider` here,\n * since doing so would mean that we also have to update this\n * strategy every time there's a change in oracle router.\n * Besides on L2, the gas is considerably cheaper than mainnet.\n *\n * @return Latest price from oracle\n */\n function _updateWOETHOraclePrice() internal returns (uint256) {\n // WETH price per unit of bridged wOETH\n uint256 oraclePrice = IOracle(IVault(vaultAddress).priceProvider())\n .price(address(bridgedWOETH));\n\n // 1 wOETH > 1 WETH, always\n require(oraclePrice > 1 ether, \"Invalid wOETH value\");\n\n uint128 oraclePrice128 = oraclePrice.toUint128();\n\n // Do some checks\n if (lastOraclePrice > 0) {\n // Make sure the value only goes up\n require(oraclePrice128 >= lastOraclePrice, \"Negative wOETH yield\");\n\n // lastOraclePrice * (1 + maxPriceDiffBps)\n uint256 maxPrice = (lastOraclePrice * (1e4 + maxPriceDiffBps)) /\n 1e4;\n\n // And that it's within the bounds.\n require(oraclePrice128 <= maxPrice, \"Price diff beyond threshold\");\n }\n\n emit WOETHPriceUpdated(lastOraclePrice, oraclePrice128);\n\n // Store the price\n lastOraclePrice = oraclePrice128;\n\n return oraclePrice;\n }\n\n /**\n * @dev Computes & returns the value of given wOETH in WETH\n * @param woethAmount Amount of wOETH\n * @return Value of wOETH in WETH (using the last stored oracle price)\n */\n function getBridgedWOETHValue(uint256 woethAmount)\n public\n view\n returns (uint256)\n {\n return (woethAmount * lastOraclePrice) / 1 ether;\n }\n\n /**\n * @dev Takes in bridged wOETH and mints & returns\n * equivalent amount of OETHb.\n * @param woethAmount Amount of bridged wOETH to transfer in\n */\n function depositBridgedWOETH(uint256 woethAmount)\n external\n onlyGovernorOrStrategist\n nonReentrant\n {\n // Update wOETH price\n uint256 oraclePrice = _updateWOETHOraclePrice();\n\n // Figure out how much they are worth\n uint256 oethToMint = (woethAmount * oraclePrice) / 1 ether;\n\n require(oethToMint > 0, \"Invalid deposit amount\");\n\n // There's no pToken, however, it just uses WOETH address in the event\n emit Deposit(address(weth), address(bridgedWOETH), oethToMint);\n\n // Mint OETHb tokens and transfer it to the caller\n IVault(vaultAddress).mintForStrategy(oethToMint);\n\n // Transfer out minted OETHb\n // slither-disable-next-line unchecked-transfer unused-return\n oethb.transfer(msg.sender, oethToMint);\n\n // Transfer in all bridged wOETH tokens\n // slither-disable-next-line unchecked-transfer unused-return\n bridgedWOETH.transferFrom(msg.sender, address(this), woethAmount);\n }\n\n /**\n * @dev Takes in OETHb and burns it and returns\n * equivalent amount of bridged wOETH.\n * @param oethToBurn Amount of OETHb to burn\n */\n function withdrawBridgedWOETH(uint256 oethToBurn)\n external\n onlyGovernorOrStrategist\n nonReentrant\n {\n // Update wOETH price\n uint256 oraclePrice = _updateWOETHOraclePrice();\n\n // Figure out how much they are worth\n uint256 woethAmount = (oethToBurn * 1 ether) / oraclePrice;\n\n require(woethAmount > 0, \"Invalid withdraw amount\");\n\n // There's no pToken, however, it just uses WOETH address in the event\n emit Withdrawal(address(weth), address(bridgedWOETH), oethToBurn);\n\n // Transfer WOETH back\n // slither-disable-next-line unchecked-transfer unused-return\n bridgedWOETH.transfer(msg.sender, woethAmount);\n\n // Transfer in OETHb\n // slither-disable-next-line unchecked-transfer unused-return\n oethb.transferFrom(msg.sender, address(this), oethToBurn);\n\n // Burn OETHb\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n }\n\n /**\n * @notice Returns the amount of backing WETH the strategy holds\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // Figure out how much wOETH is worth at the time.\n // Always uses the last stored oracle price.\n // Call updateWOETHOraclePrice manually to pull in latest yields.\n\n // NOTE: If the contract has been deployed but the call to\n // `updateWOETHOraclePrice()` has never been made, then this\n // will return zero. It should be fine because the strategy\n // should update the price whenever a deposit/withdraw happens.\n\n // If `updateWOETHOraclePrice()` hasn't been called in a while,\n // the strategy will underreport its holdings but never overreport it.\n\n balance =\n (bridgedWOETH.balanceOf(address(this)) * lastOraclePrice) /\n 1 ether;\n }\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n // Strategist deposits bridged wOETH but the contract only\n // reports the balance in WETH. As far as Vault is concerned,\n // it isn't aware of bridged wOETH token\n return _asset == address(weth);\n }\n\n /***************************************\n Overridden methods\n ****************************************/\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function transferToken(address _asset, uint256 _amount)\n public\n override\n onlyGovernor\n {\n require(\n _asset != address(bridgedWOETH) && _asset != address(weth),\n \"Cannot transfer supported asset\"\n );\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice deposit() function not used for this strategy\n */\n function deposit(address, uint256)\n external\n override\n onlyVault\n nonReentrant\n {\n // Use depositBridgedWOETH() instead\n require(false, \"Deposit disabled\");\n }\n\n /**\n * @notice depositAll() function not used for this strategy\n */\n function depositAll() external override onlyVault nonReentrant {\n // Use depositBridgedWOETH() instead\n require(false, \"Deposit disabled\");\n }\n\n /**\n * @notice withdraw() function not used for this strategy\n */\n function withdraw(\n // solhint-disable-next-line no-unused-vars\n address _recipient,\n // solhint-disable-next-line no-unused-vars\n address _asset,\n // solhint-disable-next-line no-unused-vars\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(false, \"Withdrawal disabled\");\n }\n\n /**\n * @notice withdrawAll() function not used for this strategy\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n // Withdrawal disabled\n }\n\n function _abstractSetPToken(address, address) internal override {\n revert(\"No pTokens are used\");\n }\n\n function safeApproveAllTokens() external override {}\n\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function removePToken(uint256) external override {\n revert(\"No pTokens are used\");\n }\n\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function collectRewardTokens() external override {}\n}\n" + }, + "contracts/strategies/CompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Compound Strategy\n * @notice Investment strategy for Compound like lending platforms. eg Compound and Flux\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICERC20 } from \"./ICompound.sol\";\nimport { AbstractCompoundStrategy, InitializableAbstractStrategy } from \"./AbstractCompoundStrategy.sol\";\nimport { IComptroller } from \"../interfaces/IComptroller.sol\";\nimport { IERC20 } from \"../utils/InitializableAbstractStrategy.sol\";\n\ncontract CompoundStrategy is AbstractCompoundStrategy {\n using SafeERC20 for IERC20;\n event SkippedWithdrawal(address asset, uint256 amount);\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @notice initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @notice Collect accumulated COMP and send to Harvester.\n */\n function collectRewardTokens()\n external\n virtual\n override\n onlyHarvester\n nonReentrant\n {\n // Claim COMP from Comptroller\n ICERC20 cToken = _getCTokenFor(assetsMapped[0]);\n IComptroller comptroller = IComptroller(cToken.comptroller());\n // Only collect from active cTokens, saves gas\n address[] memory ctokensToCollect = new address[](assetsMapped.length);\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n ctokensToCollect[i] = address(_getCTokenFor(assetsMapped[i]));\n }\n // Claim only for this strategy\n address[] memory claimers = new address[](1);\n claimers[0] = address(this);\n // Claim COMP from Comptroller. Only collect for supply, saves gas\n comptroller.claimComp(claimers, ctokensToCollect, false, true);\n // Transfer COMP to Harvester\n IERC20 rewardToken = IERC20(rewardTokenAddresses[0]);\n uint256 balance = rewardToken.balanceOf(address(this));\n emit RewardTokenCollected(\n harvesterAddress,\n rewardTokenAddresses[0],\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n\n /**\n * @notice Deposit asset into the underlying platform\n * @param _asset Address of asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit an asset into the underlying platform\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n ICERC20 cToken = _getCTokenFor(_asset);\n emit Deposit(_asset, address(cToken), _amount);\n require(cToken.mint(_amount) == 0, \"cToken mint failed\");\n }\n\n /**\n * @notice Deposit the entire balance of any supported asset in the strategy into the underlying platform\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n uint256 assetBalance = asset.balanceOf(address(this));\n if (assetBalance > 0) {\n _deposit(address(asset), assetBalance);\n }\n }\n }\n\n /**\n * @notice Withdraw an asset from the underlying platform\n * @param _recipient Address to receive withdrawn assets\n * @param _asset Address of the asset to withdraw\n * @param _amount Amount of assets to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n ICERC20 cToken = _getCTokenFor(_asset);\n // If redeeming 0 cTokens, just skip, else COMP will revert\n uint256 cTokensToRedeem = _convertUnderlyingToCToken(cToken, _amount);\n if (cTokensToRedeem == 0) {\n emit SkippedWithdrawal(_asset, _amount);\n return;\n }\n\n emit Withdrawal(_asset, address(cToken), _amount);\n require(cToken.redeemUnderlying(_amount) == 0, \"Redeem failed\");\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / cTokens\n * We need to approve the cToken and give it permission to spend the asset\n * @param _asset Address of the asset to approve. eg DAI\n * @param _pToken The pToken for the approval. eg cDAI or fDAI\n */\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n // Safe approval\n IERC20(_asset).safeApprove(_pToken, 0);\n IERC20(_asset).safeApprove(_pToken, type(uint256).max);\n }\n\n /**\n * @notice Remove all supported assets from the underlying platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n // Redeem entire balance of cToken\n ICERC20 cToken = _getCTokenFor(address(asset));\n uint256 cTokenBalance = cToken.balanceOf(address(this));\n if (cTokenBalance > 0) {\n require(cToken.redeem(cTokenBalance) == 0, \"Redeem failed\");\n uint256 assetBalance = asset.balanceOf(address(this));\n // Transfer entire balance to Vault\n asset.safeTransfer(vaultAddress, assetBalance);\n\n emit Withdrawal(address(asset), address(cToken), assetBalance);\n }\n }\n }\n\n /**\n * @notice Get the total asset value held in the underlying platform\n * This includes any interest that was generated since depositing.\n * The exchange rate between the cToken and asset gradually increases,\n * causing the cToken to be worth more corresponding asset.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n // Balance is always with token cToken decimals\n ICERC20 cToken = _getCTokenFor(_asset);\n balance = _checkBalance(cToken);\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * underlying = (cTokenAmt * exchangeRate) / 1e18\n * @param _cToken cToken for which to check balance\n * @return balance Total value of the asset in the platform\n */\n function _checkBalance(ICERC20 _cToken)\n internal\n view\n returns (uint256 balance)\n {\n // e.g. 50e8*205316390724364402565641705 / 1e18 = 1.0265..e18\n balance =\n (_cToken.balanceOf(address(this)) * _cToken.exchangeRateStored()) /\n 1e18;\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding cToken,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens() external override {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n address cToken = assetToPToken[address(asset)];\n // Safe approval\n asset.safeApprove(cToken, 0);\n asset.safeApprove(cToken, type(uint256).max);\n }\n }\n}\n" + }, + "contracts/strategies/ConvexEthMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Convex Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for the Curve OETH/ETH pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { ICurveETHPoolV1 } from \"./ICurveETHPoolV1.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\n\ncontract ConvexEthMetaStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 public constant MAX_SLIPPAGE = 1e16; // 1%, same as the Curve UI\n address public constant ETH_ADDRESS =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n // The following slots have been deprecated with immutable variables\n // slither-disable-next-line constable-states\n address private _deprecated_cvxDepositorAddress;\n // slither-disable-next-line constable-states\n address private _deprecated_cvxRewardStaker;\n // slither-disable-next-line constable-states\n uint256 private _deprecated_cvxDepositorPTokenId;\n // slither-disable-next-line constable-states\n address private _deprecated_curvePool;\n // slither-disable-next-line constable-states\n address private _deprecated_lpToken;\n // slither-disable-next-line constable-states\n address private _deprecated_oeth;\n // slither-disable-next-line constable-states\n address private _deprecated_weth;\n\n // Ordered list of pool assets\n // slither-disable-next-line constable-states\n uint128 private _deprecated_oethCoinIndex;\n // slither-disable-next-line constable-states\n uint128 private _deprecated_ethCoinIndex;\n\n // New immutable variables that must be set in the constructor\n address public immutable cvxDepositorAddress;\n IRewardStaking public immutable cvxRewardStaker;\n uint256 public immutable cvxDepositorPTokenId;\n ICurveETHPoolV1 public immutable curvePool;\n IERC20 public immutable lpToken;\n IERC20 public immutable oeth;\n IWETH9 public immutable weth;\n\n // Ordered list of pool assets\n uint128 public constant oethCoinIndex = 1;\n uint128 public constant ethCoinIndex = 0;\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier only works on functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balancesBefore = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffBefore = int256(balancesBefore[ethCoinIndex]) -\n int256(balancesBefore[oethCoinIndex]);\n\n _;\n\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balancesAfter = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffAfter = int256(balancesAfter[ethCoinIndex]) -\n int256(balancesAfter[oethCoinIndex]);\n\n if (diffBefore <= 0) {\n // If the pool was originally imbalanced in favor of OETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n }\n if (diffBefore >= 0) {\n // If the pool was originally imbalanced in favor of ETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n // Used to circumvent the stack too deep issue\n struct ConvexEthMetaConfig {\n address cvxDepositorAddress; //Address of the Convex depositor(AKA booster) for this pool\n address cvxRewardStakerAddress; //Address of the CVX rewards staker\n uint256 cvxDepositorPTokenId; //Pid of the pool referred to by Depositor and staker\n address oethAddress; //Address of OETH token\n address wethAddress; //Address of WETH\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n ConvexEthMetaConfig memory _convexConfig\n ) InitializableAbstractStrategy(_baseConfig) {\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveETHPoolV1(_baseConfig.platformAddress);\n\n cvxDepositorAddress = _convexConfig.cvxDepositorAddress;\n cvxRewardStaker = IRewardStaking(_convexConfig.cvxRewardStakerAddress);\n cvxDepositorPTokenId = _convexConfig.cvxDepositorPTokenId;\n oeth = IERC20(_convexConfig.oethAddress);\n weth = IWETH9(_convexConfig.wethAddress);\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. eg WETH\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets // WETH\n ) external onlyGovernor initializer {\n require(_assets.length == 1, \"Must have exactly one asset\");\n require(_assets[0] == address(weth), \"Asset not WETH\");\n\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit WETH into the Curve pool\n * @param _weth Address of Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to deposit.\n */\n function deposit(address _weth, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_weth, _amount);\n }\n\n function _deposit(address _weth, uint256 _wethAmount) internal {\n require(_wethAmount > 0, \"Must deposit something\");\n require(_weth == address(weth), \"Can only deposit WETH\");\n weth.withdraw(_wethAmount);\n\n emit Deposit(_weth, address(lpToken), _wethAmount);\n\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 oethToAdd = uint256(\n _max(\n 0,\n int256(balances[ethCoinIndex]) +\n int256(_wethAmount) -\n int256(balances[oethCoinIndex])\n )\n );\n\n /* Add so much OETH so that the pool ends up being balanced. And at minimum\n * add as much OETH as WETH and at maximum twice as much OETH.\n */\n oethToAdd = Math.max(oethToAdd, _wethAmount);\n oethToAdd = Math.min(oethToAdd, _wethAmount * 2);\n\n /* Mint OETH with a strategy that attempts to contribute to stability of OETH/WETH pool. Try\n * to mint so much OETH that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OETH minted will always be at least equal or greater\n * to WETH amount deployed. And never larger than twice the WETH amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(oethToAdd);\n\n emit Deposit(address(oeth), address(lpToken), oethToAdd);\n\n uint256[2] memory _amounts;\n _amounts[ethCoinIndex] = _wethAmount;\n _amounts[oethCoinIndex] = oethToAdd;\n\n uint256 valueInLpTokens = (_wethAmount + oethToAdd).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Do the deposit to the Curve pool\n // slither-disable-next-line arbitrary-send\n uint256 lpDeposited = curvePool.add_liquidity{ value: _wethAmount }(\n _amounts,\n minMintAmount\n );\n\n // Deposit the Curve pool's LP tokens into the Convex rewards pool\n require(\n IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n lpDeposited,\n true // Deposit with staking\n ),\n \"Depositing LP to Convex not successful\"\n );\n }\n\n /**\n * @notice Deposit the strategy's entire balance of WETH into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = weth.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(weth), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _weth Address of the Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to withdraw.\n */\n function withdraw(\n address _recipient,\n address _weth,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Invalid amount\");\n require(_weth == address(weth), \"Can only withdraw WETH\");\n\n emit Withdrawal(_weth, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(_amount);\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough WETH on balanced removal\n */\n uint256[2] memory _minWithdrawalAmounts = [uint256(0), uint256(0)];\n _minWithdrawalAmounts[ethCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OETH and any that was left in the strategy\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n\n // Transfer WETH to the recipient\n weth.deposit{ value: _amount }();\n require(\n weth.transfer(_recipient, _amount),\n \"Transfer of WETH not successful\"\n );\n }\n\n function calcTokenToBurn(uint256 _wethAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much WETH\n * we want we can determine how much of OETH we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolWETHBalance = curvePool.balances(ethCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolWETHBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_wethAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = cvxRewardStaker.balanceOf(address(this));\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[2] memory minWithdrawAmounts = [uint256(0), uint256(0)];\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(\n lpToken.balanceOf(address(this)),\n minWithdrawAmounts\n );\n\n // Burn all OETH\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Get the strategy contract's ether balance.\n // This includes all that was removed from the Curve pool and\n // any ether that was sitting in the strategy contract before the removal.\n uint256 ethBalance = address(this).balance;\n // Convert all the strategy contract's ether to WETH and transfer to the vault.\n weth.deposit{ value: ethBalance }();\n require(\n weth.transfer(vaultAddress, ethBalance),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethBalance);\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[2] memory amounts = [uint256(0), uint256(0)];\n amounts[oethCoinIndex] = _oTokens;\n\n // Convert OETH to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n\n // Deposit the Curve pool LP tokens to the Convex rewards pool\n require(\n IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n lpDeposited,\n true // Deposit with staking\n ),\n \"Failed to Deposit LP to Convex\"\n );\n\n emit Deposit(address(oeth), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough ETH.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove OTokens from the Curve pool\n uint256 oethToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n oethCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /**\n * @notice One-sided remove of ETH from the Curve pool, convert to WETH\n * and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for ETH.\n * @dev Curve pool LP tokens is used rather than WETH assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of ETH. Curve's `calc_token_amount` functioun does not include fees.\n * A 3rd party libary can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * caclulate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove ETH from the Curve pool\n uint256 ethAmount = _withdrawAndRemoveFromPool(_lpTokens, ethCoinIndex);\n\n // Convert ETH to WETH and transfer to the vault\n weth.deposit{ value: ethAmount }();\n require(\n weth.transfer(vaultAddress, ethAmount),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the Convex pool and\n * do a one-sided remove of ETH or OETH from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the Convex pool.\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = ETH, 1 = OETH.\n * @return coinsRemoved The amount of ETH or OETH removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Convex pool\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to ETH value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n // Apply slippage to ETH value\n uint256 minAmount = valueInEth.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Remove just the ETH from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV and CVX rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n cvxRewardStaker.getReward();\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _wethAmount) internal {\n // withdraw and unwrap with claim takes back the lpTokens\n // and also collects the rewards for deposit\n cvxRewardStaker.withdrawAndUnwrap(_wethAmount, true);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // Eth balance needed here for the balance check that happens from vault during depositing.\n balance = address(this).balance;\n uint256 lpTokens = cvxRewardStaker.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += (lpTokens * curvePool.get_virtual_price()) / 1e18;\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(weth);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @notice Accept unwrapped WETH\n */\n receive() external payable {}\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OETH (required for adding liquidity)\n // No approval is needed for ETH\n // slither-disable-next-line unused-return\n oeth.approve(platformAddress, type(uint256).max);\n\n // Approve Convex deposit contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Convex rewards pool\n // slither-disable-next-line unused-return\n lpToken.approve(cvxDepositorAddress, type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/ConvexGeneralizedMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { AbstractConvexMetaStrategy } from \"./AbstractConvexMetaStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract ConvexGeneralizedMetaStrategy is AbstractConvexMetaStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /* Take 3pool LP and deposit it to metapool. Take the LP from metapool\n * and deposit them to Convex.\n */\n function _lpDepositAll() internal override {\n IERC20 threePoolLp = IERC20(pTokenAddress);\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 threePoolLpBalance = threePoolLp.balanceOf(address(this));\n uint256 curve3PoolVirtualPrice = curvePool.get_virtual_price();\n uint256 threePoolLpDollarValue = threePoolLpBalance.mulTruncate(\n curve3PoolVirtualPrice\n );\n\n uint256[2] memory _amounts = [0, threePoolLpBalance];\n\n uint256 metapoolVirtualPrice = metapool.get_virtual_price();\n /**\n * First convert all the deposited tokens to dollar values,\n * then divide by virtual price to convert to metapool LP tokens\n * and apply the max slippage\n */\n uint256 minReceived = threePoolLpDollarValue\n .divPrecisely(metapoolVirtualPrice)\n .mulTruncate(uint256(1e18) - MAX_SLIPPAGE);\n\n uint256 metapoolLp = metapool.add_liquidity(_amounts, minReceived);\n\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n metapoolLp,\n true // Deposit with staking\n );\n\n require(success, \"Failed to deposit to Convex\");\n }\n\n /**\n * Withdraw the specified amount of tokens from the gauge. And use all the resulting tokens\n * to remove liquidity from metapool\n * @param num3CrvTokens Number of Convex 3pool LP tokens to withdraw from metapool\n */\n function _lpWithdraw(uint256 num3CrvTokens) internal override {\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n uint256 requiredMetapoolLpTokens = _calcCurveMetaTokenAmount(\n crvCoinIndex,\n num3CrvTokens\n );\n\n require(\n requiredMetapoolLpTokens <= gaugeTokens,\n string(\n bytes.concat(\n bytes(\"Attempting to withdraw \"),\n bytes(Strings.toString(requiredMetapoolLpTokens)),\n bytes(\", metapoolLP but only \"),\n bytes(Strings.toString(gaugeTokens)),\n bytes(\" available.\")\n )\n )\n );\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards for deposit\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n requiredMetapoolLpTokens,\n true\n );\n\n if (requiredMetapoolLpTokens > 0) {\n // slither-disable-next-line unused-return\n metapool.remove_liquidity_one_coin(\n requiredMetapoolLpTokens,\n int128(crvCoinIndex),\n num3CrvTokens\n );\n }\n }\n\n function _lpWithdrawAll() internal override {\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n gaugeTokens,\n true\n );\n\n if (gaugeTokens > 0) {\n uint256 burnDollarAmount = gaugeTokens.mulTruncate(\n metapool.get_virtual_price()\n );\n uint256 curve3PoolExpected = burnDollarAmount.divPrecisely(\n ICurvePool(platformAddress).get_virtual_price()\n );\n\n // Always withdraw all of the available metapool LP tokens (similar to how we always deposit all)\n // slither-disable-next-line unused-return\n metapool.remove_liquidity_one_coin(\n gaugeTokens,\n int128(crvCoinIndex),\n curve3PoolExpected -\n curve3PoolExpected.mulTruncate(maxWithdrawalSlippage)\n );\n }\n }\n}\n" + }, + "contracts/strategies/ConvexOUSDMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { AbstractConvexMetaStrategy } from \"./AbstractConvexMetaStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract ConvexOUSDMetaStrategy is AbstractConvexMetaStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /* Take 3pool LP and mint the corresponding amount of ousd. Deposit and stake that to\n * ousd Curve Metapool. Take the LP from metapool and deposit them to Convex.\n */\n function _lpDepositAll() internal override {\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 threePoolLpBalance = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n uint256 curve3PoolVirtualPrice = curvePool.get_virtual_price();\n uint256 threePoolLpDollarValue = threePoolLpBalance.mulTruncate(\n curve3PoolVirtualPrice\n );\n\n // safe to cast since min value is at least 0\n uint256 ousdToAdd = uint256(\n _max(\n 0,\n int256(\n metapool.balances(crvCoinIndex).mulTruncate(\n curve3PoolVirtualPrice\n )\n ) -\n int256(metapool.balances(mainCoinIndex)) +\n int256(threePoolLpDollarValue)\n )\n );\n\n /* Add so much OUSD so that the pool ends up being balanced. And at minimum\n * add twice as much OUSD as 3poolLP and at maximum at twice as\n * much OUSD.\n */\n ousdToAdd = Math.max(ousdToAdd, threePoolLpDollarValue);\n ousdToAdd = Math.min(ousdToAdd, threePoolLpDollarValue * 2);\n\n /* Mint OUSD with a strategy that attempts to contribute to stability of OUSD metapool. Try\n * to mint so much OUSD that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OUSD minted will always be at least equal or greater\n * to stablecoin(DAI, USDC, USDT) amount of 3CRVLP deployed. And never larger than twice the\n * stablecoin amount of 3CRVLP deployed even if it would have a further beneficial effect\n * on pool stability.\n */\n if (ousdToAdd > 0) {\n IVault(vaultAddress).mintForStrategy(ousdToAdd);\n }\n\n uint256[2] memory _amounts = [ousdToAdd, threePoolLpBalance];\n\n uint256 metapoolVirtualPrice = metapool.get_virtual_price();\n /**\n * First convert all the deposited tokens to dollar values,\n * then divide by virtual price to convert to metapool LP tokens\n * and apply the max slippage\n */\n uint256 minReceived = (ousdToAdd + threePoolLpDollarValue)\n .divPrecisely(metapoolVirtualPrice)\n .mulTruncate(uint256(1e18) - MAX_SLIPPAGE);\n\n uint256 metapoolLp = metapool.add_liquidity(_amounts, minReceived);\n\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n metapoolLp,\n true // Deposit with staking\n );\n\n require(success, \"Failed to deposit to Convex\");\n }\n\n /**\n * Withdraw the specified amount of tokens from the gauge. And use all the resulting tokens\n * to remove liquidity from metapool\n * @param num3CrvTokens Number of 3CRV tokens to withdraw from metapool\n */\n function _lpWithdraw(uint256 num3CrvTokens) internal override {\n ICurvePool curvePool = ICurvePool(platformAddress);\n /* The rate between coins in the metapool determines the rate at which metapool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much 3crvLp\n * we want we can determine how much of OUSD we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 crvPoolBalance = metapool.balances(crvCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * metapool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * metapoolLPToken.totalSupply()) / crvPoolBalance;\n // simplifying below to: `uint256 diff = (num3CrvTokens - 1) * k` causes loss of precision\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = crvPoolBalance * k -\n (crvPoolBalance - num3CrvTokens - 1) * k;\n uint256 lpToBurn = diff / 1e36;\n\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n require(\n lpToBurn <= gaugeTokens,\n string(\n bytes.concat(\n bytes(\"Attempting to withdraw \"),\n bytes(Strings.toString(lpToBurn)),\n bytes(\", metapoolLP but only \"),\n bytes(Strings.toString(gaugeTokens)),\n bytes(\" available.\")\n )\n )\n );\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards for deposit\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n lpToBurn,\n true\n );\n\n // calculate the min amount of OUSD expected for the specified amount of LP tokens\n uint256 minOUSDAmount = lpToBurn.mulTruncate(\n metapool.get_virtual_price()\n ) -\n num3CrvTokens.mulTruncate(curvePool.get_virtual_price()) -\n 1;\n\n // withdraw the liquidity from metapool\n uint256[2] memory _removedAmounts = metapool.remove_liquidity(\n lpToBurn,\n [minOUSDAmount, num3CrvTokens]\n );\n\n IVault(vaultAddress).burnForStrategy(_removedAmounts[mainCoinIndex]);\n }\n\n function _lpWithdrawAll() internal override {\n IERC20 metapoolErc20 = IERC20(address(metapool));\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n gaugeTokens,\n true\n );\n\n uint256[2] memory _minAmounts = [uint256(0), uint256(0)];\n uint256[2] memory _removedAmounts = metapool.remove_liquidity(\n metapoolErc20.balanceOf(address(this)),\n _minAmounts\n );\n\n IVault(vaultAddress).burnForStrategy(_removedAmounts[mainCoinIndex]);\n }\n}\n" + }, + "contracts/strategies/ConvexStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { IERC20, AbstractCurveStrategy, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\n/*\n * IMPORTANT(!) If ConvexStrategy needs to be re-deployed, it requires new\n * proxy contract with fresh storage slots. Changes in `AbstractCurveStrategy`\n * storage slots would break existing implementation.\n *\n * Remove this notice if ConvexStrategy is re-deployed\n */\ncontract ConvexStrategy is AbstractCurveStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n address internal cvxDepositorAddress;\n address internal cvxRewardStakerAddress;\n // slither-disable-next-line constable-states\n address private _deprecated_cvxRewardTokenAddress;\n uint256 internal cvxDepositorPTokenId;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * DAI, USDC, USDT\n * @param _pTokens Platform Token corresponding addresses\n * @param _cvxDepositorAddress Address of the Convex depositor(AKA booster) for this pool\n * @param _cvxRewardStakerAddress Address of the CVX rewards staker\n * @param _cvxDepositorPTokenId Pid of the pool referred to by Depositor and staker\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets,\n address[] calldata _pTokens,\n address _cvxDepositorAddress,\n address _cvxRewardStakerAddress,\n uint256 _cvxDepositorPTokenId\n ) external onlyGovernor initializer {\n require(_assets.length == 3, \"Must have exactly three assets\");\n // Should be set prior to abstract initialize call otherwise\n // abstractSetPToken calls will fail\n cvxDepositorAddress = _cvxDepositorAddress;\n cvxRewardStakerAddress = _cvxRewardStakerAddress;\n cvxDepositorPTokenId = _cvxDepositorPTokenId;\n pTokenAddress = _pTokens[0];\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n function _lpDepositAll() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // Deposit with staking\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n pToken.balanceOf(address(this)),\n true\n );\n require(success, \"Failed to deposit to Convex\");\n }\n\n function _lpWithdraw(uint256 numCrvTokens) internal override {\n uint256 gaugePTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n // Not enough in this contract or in the Gauge, can't proceed\n require(numCrvTokens > gaugePTokens, \"Insufficient 3CRV balance\");\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards to this\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n numCrvTokens,\n true\n );\n }\n\n function _lpWithdrawAll() internal override {\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards to this\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n IRewardStaking(cvxRewardStakerAddress).balanceOf(address(this)),\n true\n );\n }\n\n function _approveBase() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // 3Pool for LP token (required for removing liquidity)\n pToken.safeApprove(platformAddress, 0);\n pToken.safeApprove(platformAddress, type(uint256).max);\n // Gauge for LP token\n pToken.safeApprove(cvxDepositorAddress, 0);\n pToken.safeApprove(cvxDepositorAddress, type(uint256).max);\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 contractPTokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n uint256 gaugePTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n uint256 totalPTokens = contractPTokens + gaugePTokens;\n\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (totalPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = (totalPTokens * virtual_price) / 1e18;\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = value.scaleBy(assetDecimals, 18) / 3;\n }\n }\n\n /**\n * @dev Collect accumulated CRV and CVX and send to Vault.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n IRewardStaking(cvxRewardStakerAddress).getReward();\n _collectRewardTokens();\n }\n}\n" + }, + "contracts/strategies/CurveAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for a Curve pool using an OToken.\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { ICurveStableSwapNG } from \"../interfaces/ICurveStableSwapNG.sol\";\nimport { ICurveLiquidityGaugeV6 } from \"../interfaces/ICurveLiquidityGaugeV6.sol\";\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\nimport { ICurveMinter } from \"../interfaces/ICurveMinter.sol\";\n\ncontract CurveAMOStrategy is InitializableAbstractStrategy {\n using SafeCast for uint256;\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n /**\n * @dev a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n // New immutable variables that must be set in the constructor\n /**\n * @notice Address of the hard asset (weth, usdt, usdc).\n */\n IERC20 public immutable hardAsset;\n\n /**\n * @notice Address of the OTOKEN token contract.\n */\n IERC20 public immutable oToken;\n\n /**\n * @notice Address of the LP (Liquidity Provider) token contract.\n */\n IERC20 public immutable lpToken;\n\n /**\n * @notice Address of the Curve StableSwap NG pool contract.\n */\n ICurveStableSwapNG public immutable curvePool;\n\n /**\n * @notice Address of the Curve X-Chain Liquidity Gauge contract.\n */\n ICurveLiquidityGaugeV6 public immutable gauge;\n\n /**\n * @notice Address of the Curve Minter contract.\n */\n ICurveMinter public immutable minter;\n\n /**\n * @notice Index of the OTOKEN and hardAsset in the Curve pool.\n */\n uint128 public immutable otokenCoinIndex;\n uint128 public immutable hardAssetCoinIndex;\n\n /**\n * @notice Decimals of the OTOKEN and hardAsset.\n */\n uint8 public immutable decimalsOToken;\n uint8 public immutable decimalsHardAsset;\n\n /**\n * @notice Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n uint256 public maxSlippage;\n\n event MaxSlippageUpdated(uint256 newMaxSlippage);\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the hard asset and OToken balances in the Curve pool\n uint256[] memory balancesBefore = curvePool.get_balances();\n // diff = hardAsset balance - OTOKEN balance\n int256 diffBefore = (\n balancesBefore[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() - balancesBefore[otokenCoinIndex].toInt256();\n\n _;\n\n // Get the hard asset and OToken balances in the Curve pool\n uint256[] memory balancesAfter = curvePool.get_balances();\n // diff = hardAsset balance - OTOKEN balance\n int256 diffAfter = (\n balancesAfter[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() - balancesAfter[otokenCoinIndex].toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OTOKEN, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of hardAsset, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _otoken,\n address _hardAsset,\n address _gauge,\n address _minter\n ) InitializableAbstractStrategy(_baseConfig) {\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveStableSwapNG(_baseConfig.platformAddress);\n minter = ICurveMinter(_minter);\n\n oToken = IERC20(_otoken);\n hardAsset = IERC20(_hardAsset);\n gauge = ICurveLiquidityGaugeV6(_gauge);\n decimalsHardAsset = IBasicToken(_hardAsset).decimals();\n decimalsOToken = IBasicToken(_otoken).decimals();\n\n (hardAssetCoinIndex, otokenCoinIndex) = curvePool.coins(0) == _hardAsset\n ? (0, 1)\n : (1, 0);\n require(\n curvePool.coins(otokenCoinIndex) == _otoken &&\n curvePool.coins(hardAssetCoinIndex) == _hardAsset,\n \"Invalid coin indexes\"\n );\n require(gauge.lp_token() == address(curvePool), \"Invalid pool\");\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV\n * @param _maxSlippage Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV\n uint256 _maxSlippage\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n address[] memory _assets = new address[](1);\n _assets[0] = address(hardAsset);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n _setMaxSlippage(_maxSlippage);\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit hard asset into the Curve pool\n * @param _hardAsset Address of hard asset contract.\n * @param _amount Amount of hard asset to deposit.\n */\n function deposit(address _hardAsset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_hardAsset, _amount);\n }\n\n function _deposit(address _hardAsset, uint256 _hardAssetAmount) internal {\n require(_hardAssetAmount > 0, \"Must deposit something\");\n require(_hardAsset == address(hardAsset), \"Unsupported asset\");\n\n emit Deposit(_hardAsset, address(lpToken), _hardAssetAmount);\n uint256 scaledHardAssetAmount = _hardAssetAmount.scaleBy(\n decimalsOToken,\n decimalsHardAsset\n );\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 otokenToAdd = uint256(\n _max(\n 0,\n (\n balances[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() +\n scaledHardAssetAmount.toInt256() -\n balances[otokenCoinIndex].toInt256()\n )\n );\n\n /* Add so much OTOKEN so that the pool ends up being balanced. And at minimum\n * add as much OTOKEN as hard asset and at maximum twice as much OTOKEN.\n */\n otokenToAdd = Math.max(otokenToAdd, scaledHardAssetAmount);\n otokenToAdd = Math.min(otokenToAdd, scaledHardAssetAmount * 2);\n\n /* Mint OTOKEN with a strategy that attempts to contribute to stability of OTOKEN/hardAsset pool. Try\n * to mint so much OTOKEN that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OTOKEN minted will always be at least equal or greater\n * to hardAsset amount deployed. And never larger than twice the hardAsset amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(otokenToAdd);\n\n emit Deposit(address(oToken), address(lpToken), otokenToAdd);\n\n uint256[] memory _amounts = new uint256[](2);\n _amounts[hardAssetCoinIndex] = _hardAssetAmount;\n _amounts[otokenCoinIndex] = otokenToAdd;\n\n uint256 valueInLpTokens = (scaledHardAssetAmount + otokenToAdd)\n .divPrecisely(curvePool.get_virtual_price());\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Do the deposit to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(_amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool's LP tokens into the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n /**\n * @notice Deposit the strategy's entire balance of hardAsset into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = hardAsset.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(hardAsset), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw hardAsset and OTOKEN from the Curve pool, burn the OTOKEN,\n * transfer hardAsset to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _hardAsset Address of the hardAsset contract.\n * @param _amount Amount of hardAsset to withdraw.\n */\n function withdraw(\n address _recipient,\n address _hardAsset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(\n _hardAsset == address(hardAsset),\n \"Can only withdraw hard asset\"\n );\n\n emit Withdrawal(_hardAsset, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(\n _amount.scaleBy(decimalsOToken, decimalsHardAsset)\n );\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough hardAsset on balanced removal\n */\n uint256[] memory _minWithdrawalAmounts = new uint256[](2);\n _minWithdrawalAmounts[hardAssetCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OTOKEN and any that was left in the strategy\n uint256 otokenToBurn = oToken.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n\n // Transfer hardAsset to the recipient\n hardAsset.safeTransfer(_recipient, _amount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n function calcTokenToBurn(uint256 _hardAssetAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much hardAsset\n * we want we can determine how much of OTOKEN we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolHardAssetBalance = curvePool\n .balances(hardAssetCoinIndex)\n .scaleBy(decimalsOToken, decimalsHardAsset);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolHardAssetBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_hardAssetAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all hardAsset and OTOKEN from the Curve pool, burn the OTOKEN,\n * transfer hardAsset to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = gauge.balanceOf(address(this));\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[] memory minWithdrawAmounts = new uint256[](2);\n\n // Check balance of LP tokens in the strategy, if 0 return\n uint256 lpBalance = lpToken.balanceOf(address(this));\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n if (lpBalance > 0) {\n curvePool.remove_liquidity(lpBalance, minWithdrawAmounts);\n }\n\n // Burn all OTOKEN\n uint256 otokenToBurn = oToken.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n // Get the strategy contract's hardAsset balance.\n // This includes all that was removed from the Curve pool and\n // any hardAsset that was sitting in the strategy contract before the removal.\n uint256 hardAssetBalance = hardAsset.balanceOf(address(this));\n hardAsset.safeTransfer(vaultAddress, hardAssetBalance);\n\n if (hardAssetBalance > 0)\n emit Withdrawal(\n address(hardAsset),\n address(lpToken),\n hardAssetBalance\n );\n if (otokenToBurn > 0)\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many hardAsset.\n * The OToken/Asset, eg OTOKEN/hardAsset, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[] memory amounts = new uint256[](2);\n amounts[otokenCoinIndex] = _oTokens;\n\n // Convert OTOKEN to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool LP tokens to the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Deposit(address(oToken), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough hardAsset.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from gauge and remove OTokens from the Curve pool\n uint256 otokenToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n otokenCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n }\n\n /**\n * @notice One-sided remove of hardAsset from the Curve pool and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many hardAsset.\n * The OToken/Asset, eg OTOKEN/hardAsset, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for hardAsset.\n * @dev Curve pool LP tokens is used rather than hardAsset assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of hardAsset. Curve's `calc_token_amount` function does not include fees.\n * A 3rd party library can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * calculate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Curve gauge and remove hardAsset from the Curve pool\n uint256 hardAssetAmount = _withdrawAndRemoveFromPool(\n _lpTokens,\n hardAssetCoinIndex\n );\n\n // Transfer hardAsset to the vault\n hardAsset.safeTransfer(vaultAddress, hardAssetAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(hardAsset), address(lpToken), hardAssetAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the gauge and\n * do a one-sided remove of hardAsset or OTOKEN from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the gauge\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = hardAsset, 1 = OTOKEN.\n * @return coinsRemoved The amount of hardAsset or OTOKEN removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Curve gauge\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to hardAsset value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n\n if (coinIndex == hardAssetCoinIndex) {\n valueInEth = valueInEth.scaleBy(decimalsHardAsset, decimalsOToken);\n }\n\n // Apply slippage to hardAsset value\n uint256 minAmount = valueInEth.mulTruncate(uint256(1e18) - maxSlippage);\n\n // Remove just the hardAsset from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOtokenSupply = oToken.totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOtokenSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV rewards from inflation\n minter.mint(address(gauge));\n\n // Collect extra gauge rewards (outside of CRV)\n gauge.claim_rewards();\n\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _lpAmount) internal {\n require(\n gauge.balanceOf(address(this)) >= _lpAmount,\n \"Insufficient LP tokens\"\n );\n // withdraw lp tokens from the gauge without claiming rewards\n gauge.withdraw(_lpAmount);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(hardAsset), \"Unsupported asset\");\n\n // hardAsset balance needed here for the balance check that happens from vault during depositing.\n balance = hardAsset.balanceOf(address(this));\n uint256 lpTokens = gauge.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += ((lpTokens * curvePool.get_virtual_price()) / 1e18)\n .scaleBy(decimalsHardAsset, decimalsOToken);\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(hardAsset);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Sets the maximum slippage allowed for any swap/liquidity operation\n * @param _maxSlippage Maximum slippage allowed, 1e18 = 100%.\n */\n function setMaxSlippage(uint256 _maxSlippage) external onlyGovernor {\n _setMaxSlippage(_maxSlippage);\n }\n\n function _setMaxSlippage(uint256 _maxSlippage) internal {\n require(_maxSlippage <= 5e16, \"Slippage must be less than 100%\");\n maxSlippage = _maxSlippage;\n emit MaxSlippageUpdated(_maxSlippage);\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OTOKEN (required for adding liquidity)\n // slither-disable-next-line unused-return\n oToken.approve(platformAddress, type(uint256).max);\n\n // Approve Curve pool for hardAsset (required for adding liquidity)\n // slither-disable-next-line unused-return\n hardAsset.safeApprove(platformAddress, type(uint256).max);\n\n // Approve Curve gauge contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Curve gauge.\n // slither-disable-next-line unused-return\n lpToken.approve(address(gauge), type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/CurvePoolBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport { ICampaignRemoteManager } from \"../interfaces/ICampaignRemoteManager.sol\";\n\n/// @title CurvePoolBooster\n/// @author Origin Protocol\n/// @notice Contract to manage interactions with VotemarketV2 for a dedicated Curve pool/gauge.\ncontract CurvePoolBooster is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n ////////////////////////////////////////////////////\n /// --- CONSTANTS && IMMUTABLES\n ////////////////////////////////////////////////////\n /// @notice Base fee for the contract, 100%\n uint16 public constant FEE_BASE = 10_000;\n\n /// @notice Address of the gauge to manage\n address public immutable gauge;\n\n /// @notice Address of the reward token\n address public immutable rewardToken;\n\n /// @notice Chain id of the target chain\n uint256 public immutable targetChainId;\n\n ////////////////////////////////////////////////////\n /// --- STORAGE\n ////////////////////////////////////////////////////\n /// @notice Fee in FEE_BASE unit payed when managing campaign.\n uint16 public fee;\n\n /// @notice Address of the fee collector\n address public feeCollector;\n\n /// @notice Address of the campaignRemoteManager linked to VotemarketV2\n address public campaignRemoteManager;\n\n /// @notice Address of votemarket in L2\n address public votemarket;\n\n /// @notice Id of the campaign created\n uint256 public campaignId;\n\n ////////////////////////////////////////////////////\n /// --- EVENTS\n ////////////////////////////////////////////////////\n event FeeUpdated(uint16 newFee);\n event FeeCollected(address feeCollector, uint256 feeAmount);\n event FeeCollectorUpdated(address newFeeCollector);\n event VotemarketUpdated(address newVotemarket);\n event CampaignRemoteManagerUpdated(address newCampaignRemoteManager);\n event CampaignCreated(\n address gauge,\n address rewardToken,\n uint256 maxRewardPerVote,\n uint256 totalRewardAmount\n );\n event CampaignIdUpdated(uint256 newId);\n event CampaignClosed(uint256 campaignId);\n event TotalRewardAmountUpdated(uint256 extraTotalRewardAmount);\n event NumberOfPeriodsUpdated(uint8 extraNumberOfPeriods);\n event RewardPerVoteUpdated(uint256 newMaxRewardPerVote);\n event TokensRescued(address token, uint256 amount, address receiver);\n\n ////////////////////////////////////////////////////\n /// --- CONSTRUCTOR && INITIALIZATION\n ////////////////////////////////////////////////////\n constructor(\n uint256 _targetChainId,\n address _rewardToken,\n address _gauge\n ) {\n targetChainId = _targetChainId;\n rewardToken = _rewardToken;\n gauge = _gauge;\n\n // Prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /// @notice initialize function, to set up initial internal state\n /// @param _strategist Address of the strategist\n /// @param _fee Fee in FEE_BASE unit payed when managing campaign\n /// @param _feeCollector Address of the fee collector\n function initialize(\n address _strategist,\n uint16 _fee,\n address _feeCollector,\n address _campaignRemoteManager,\n address _votemarket\n ) external onlyGovernor initializer {\n _setStrategistAddr(_strategist);\n _setFee(_fee);\n _setFeeCollector(_feeCollector);\n _setCampaignRemoteManager(_campaignRemoteManager);\n _setVotemarket(_votemarket);\n }\n\n ////////////////////////////////////////////////////\n /// --- MUTATIVE FUNCTIONS\n ////////////////////////////////////////////////////\n /// @notice Create a new campaign on VotemarketV2\n /// @dev This will use all token available in this contract\n /// @param numberOfPeriods Duration of the campaign in weeks\n /// @param maxRewardPerVote Maximum reward per vote to distribute, to avoid overspending\n /// @param blacklist List of addresses to exclude from the campaign\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function createCampaign(\n uint8 numberOfPeriods,\n uint256 maxRewardPerVote,\n address[] calldata blacklist,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId == 0, \"Campaign already created\");\n require(numberOfPeriods > 1, \"Invalid number of periods\");\n require(maxRewardPerVote > 0, \"Invalid reward per vote\");\n\n // Handle fee (if any)\n uint256 balanceSubFee = _handleFee();\n\n // Approve the balanceSubFee to the campaign manager\n IERC20(rewardToken).safeApprove(campaignRemoteManager, 0);\n IERC20(rewardToken).safeApprove(campaignRemoteManager, balanceSubFee);\n\n // Create a new campaign\n ICampaignRemoteManager(campaignRemoteManager).createCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignCreationParams({\n chainId: targetChainId,\n gauge: gauge,\n manager: address(this),\n rewardToken: rewardToken,\n numberOfPeriods: numberOfPeriods,\n maxRewardPerVote: maxRewardPerVote,\n totalRewardAmount: balanceSubFee,\n addresses: blacklist,\n hook: address(0),\n isWhitelist: false\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit CampaignCreated(\n gauge,\n rewardToken,\n maxRewardPerVote,\n balanceSubFee\n );\n }\n\n /// @notice Manage the total reward amount of the campaign\n /// @dev This function should be called after the campaign is created\n /// @dev This will use all the token available in this contract\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageTotalRewardAmount(\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n\n // Handle fee (if any)\n uint256 balanceSubFee = _handleFee();\n\n // Approve the total reward amount to the campaign manager\n IERC20(rewardToken).safeApprove(campaignRemoteManager, 0);\n IERC20(rewardToken).safeApprove(campaignRemoteManager, balanceSubFee);\n\n // Manage the campaign\n // https://github.com/stake-dao/votemarket-v2/blob/main/packages/votemarket/src/Votemarket.sol#L668\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: 0,\n totalRewardAmount: balanceSubFee,\n maxRewardPerVote: 0\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit TotalRewardAmountUpdated(balanceSubFee);\n }\n\n /// @notice Manage the number of periods of the campaign\n /// @dev This function should be called after the campaign is created\n /// @param extraNumberOfPeriods Number of additional periods (cannot be 0)\n /// that will be added to already existing amount of periods.\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageNumberOfPeriods(\n uint8 extraNumberOfPeriods,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n require(extraNumberOfPeriods > 0, \"Invalid number of periods\");\n\n // Manage the campaign\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: extraNumberOfPeriods,\n totalRewardAmount: 0,\n maxRewardPerVote: 0\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit NumberOfPeriodsUpdated(extraNumberOfPeriods);\n }\n\n /// @notice Manage the reward per vote of the campaign\n /// @dev This function should be called after the campaign is created\n /// @param newMaxRewardPerVote New maximum reward per vote\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageRewardPerVote(\n uint256 newMaxRewardPerVote,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n require(newMaxRewardPerVote > 0, \"Invalid reward per vote\");\n\n // Manage the campaign\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: 0,\n totalRewardAmount: 0,\n maxRewardPerVote: newMaxRewardPerVote\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit RewardPerVoteUpdated(newMaxRewardPerVote);\n }\n\n /// @notice Close the campaign.\n /// @dev This function only work on the L2 chain. Not on mainnet.\n /// @dev The _campaignId parameter is not related to the campaignId of this contract, allowing greater flexibility.\n /// @param _campaignId Id of the campaign to close\n function closeCampaign(\n uint256 _campaignId,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n ICampaignRemoteManager(campaignRemoteManager).closeCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignClosingParams({\n campaignId: campaignId\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n emit CampaignClosed(_campaignId);\n }\n\n /// @notice calculate the fee amount and transfer it to the feeCollector\n /// @return Balance after fee\n function _handleFee() internal returns (uint256) {\n // Cache current rewardToken balance\n uint256 balance = IERC20(rewardToken).balanceOf(address(this));\n require(balance > 0, \"No reward to manage\");\n\n uint256 feeAmount = (balance * fee) / FEE_BASE;\n\n // If there is a fee, transfer it to the feeCollector\n if (feeAmount > 0) {\n // Transfer the fee to the feeCollector\n IERC20(rewardToken).safeTransfer(feeCollector, feeAmount);\n emit FeeCollected(feeCollector, feeAmount);\n\n return IERC20(rewardToken).balanceOf(address(this));\n }\n\n // Return remaining balance\n return balance;\n }\n\n ////////////////////////////////////////////////////\n /// --- GOVERNANCE && OPERATION\n ////////////////////////////////////////////////////\n /// @notice Set the campaign id\n /// @dev Only callable by the governor or strategist\n /// @param _campaignId New campaign id\n function setCampaignId(uint256 _campaignId)\n external\n onlyGovernorOrStrategist\n {\n campaignId = _campaignId;\n emit CampaignIdUpdated(_campaignId);\n }\n\n /// @notice Rescue ETH from the contract\n /// @dev Only callable by the governor or strategist\n /// @param receiver Address to receive the ETH\n function rescueETH(address receiver)\n external\n nonReentrant\n onlyGovernorOrStrategist\n {\n require(receiver != address(0), \"Invalid receiver\");\n uint256 balance = address(this).balance;\n (bool success, ) = receiver.call{ value: balance }(\"\");\n require(success, \"Transfer failed\");\n emit TokensRescued(address(0), balance, receiver);\n }\n\n /// @notice Rescue ERC20 tokens from the contract\n /// @dev Only callable by the governor or strategist\n /// @param token Address of the token to rescue\n function rescueToken(address token, address receiver)\n external\n nonReentrant\n onlyGovernor\n {\n require(receiver != address(0), \"Invalid receiver\");\n uint256 balance = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransfer(receiver, balance);\n emit TokensRescued(token, balance, receiver);\n }\n\n /// @notice Set the fee\n /// @dev Only callable by the governor\n /// @param _fee New fee\n function setFee(uint16 _fee) external onlyGovernor {\n _setFee(_fee);\n }\n\n /// @notice Internal logic to set the fee\n function _setFee(uint16 _fee) internal {\n require(_fee <= FEE_BASE / 2, \"Fee too high\");\n fee = _fee;\n emit FeeUpdated(_fee);\n }\n\n /// @notice Set the fee collector\n /// @dev Only callable by the governor\n /// @param _feeCollector New fee collector\n function setFeeCollector(address _feeCollector) external onlyGovernor {\n _setFeeCollector(_feeCollector);\n }\n\n /// @notice Internal logic to set the fee collector\n function _setFeeCollector(address _feeCollector) internal {\n require(_feeCollector != address(0), \"Invalid fee collector\");\n feeCollector = _feeCollector;\n emit FeeCollectorUpdated(_feeCollector);\n }\n\n /// @notice Set the campaignRemoteManager\n /// @param _campaignRemoteManager New campaignRemoteManager address\n function setCampaignRemoteManager(address _campaignRemoteManager)\n external\n onlyGovernor\n {\n _setCampaignRemoteManager(_campaignRemoteManager);\n }\n\n /// @notice Internal logic to set the campaignRemoteManager\n /// @param _campaignRemoteManager New campaignRemoteManager address\n function _setCampaignRemoteManager(address _campaignRemoteManager)\n internal\n {\n require(\n _campaignRemoteManager != address(0),\n \"Invalid campaignRemoteManager\"\n );\n campaignRemoteManager = _campaignRemoteManager;\n emit CampaignRemoteManagerUpdated(_campaignRemoteManager);\n }\n\n /// @notice Set the votemarket address\n /// @param _votemarket New votemarket address\n function setVotemarket(address _votemarket) external onlyGovernor {\n _setVotemarket(_votemarket);\n }\n\n /// @notice Internal logic to set the votemarket address\n function _setVotemarket(address _votemarket) internal onlyGovernor {\n require(_votemarket != address(0), \"Invalid votemarket\");\n votemarket = _votemarket;\n emit VotemarketUpdated(_votemarket);\n }\n\n receive() external payable {}\n}\n" + }, + "contracts/strategies/Generalized4626Strategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Generalized 4626 Strategy\n * @notice Investment strategy for ERC-4626 Tokenized Vaults\n * @author Origin Protocol Inc\n */\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\ncontract Generalized4626Strategy is InitializableAbstractStrategy {\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecate_shareToken;\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecate_assetToken;\n\n IERC20 public immutable shareToken;\n IERC20 public immutable assetToken;\n\n // For future use\n uint256[50] private __gap;\n\n /**\n * @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n * and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n * @param _assetToken Address of the ERC-4626 asset token. eg frxETH or DAI\n */\n constructor(BaseStrategyConfig memory _baseConfig, address _assetToken)\n InitializableAbstractStrategy(_baseConfig)\n {\n shareToken = IERC20(_baseConfig.platformAddress);\n assetToken = IERC20(_assetToken);\n }\n\n function initialize() external virtual onlyGovernor initializer {\n address[] memory rewardTokens = new address[](0);\n address[] memory assets = new address[](1);\n address[] memory pTokens = new address[](1);\n\n assets[0] = address(assetToken);\n pTokens[0] = address(platformAddress);\n\n InitializableAbstractStrategy._initialize(\n rewardTokens,\n assets,\n pTokens\n );\n }\n\n /**\n * @dev Deposit assets by converting them to shares\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit assets by converting them to shares\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal virtual {\n require(_amount > 0, \"Must deposit something\");\n require(_asset == address(assetToken), \"Unexpected asset address\");\n\n // slither-disable-next-line unused-return\n IERC4626(platformAddress).deposit(_amount, address(this));\n emit Deposit(_asset, address(shareToken), _amount);\n }\n\n /**\n * @dev Deposit the entire balance of assetToken to gain shareToken\n */\n function depositAll() external virtual override onlyVault nonReentrant {\n uint256 balance = assetToken.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(assetToken), balance);\n }\n }\n\n /**\n * @dev Withdraw asset by burning shares\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n require(_asset == address(assetToken), \"Unexpected asset address\");\n\n // slither-disable-next-line unused-return\n IERC4626(platformAddress).withdraw(_amount, _recipient, address(this));\n emit Withdrawal(_asset, address(shareToken), _amount);\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / share tokens\n */\n function _abstractSetPToken(address, address) internal virtual override {\n _approveBase();\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll()\n external\n virtual\n override\n onlyVaultOrGovernor\n nonReentrant\n {\n uint256 shareBalance = shareToken.balanceOf(address(this));\n uint256 assetAmount = 0;\n if (shareBalance > 0) {\n assetAmount = IERC4626(platformAddress).redeem(\n shareBalance,\n vaultAddress,\n address(this)\n );\n emit Withdrawal(\n address(assetToken),\n address(shareToken),\n assetAmount\n );\n }\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(_asset == address(assetToken), \"Unexpected asset address\");\n /* We are intentionally not counting the amount of assetToken parked on the\n * contract toward the checkBalance. The deposit and withdraw functions\n * should not result in assetToken being unused and owned by this strategy\n * contract.\n */\n IERC4626 platform = IERC4626(platformAddress);\n return platform.previewRedeem(platform.balanceOf(address(this)));\n }\n\n /**\n * @notice Governor approves the the ERC-4626 Tokenized Vault to spend the asset.\n */\n function safeApproveAllTokens() external override onlyGovernor {\n _approveBase();\n }\n\n function _approveBase() internal virtual {\n // Approval the asset to be transferred to the ERC-4626 Tokenized Vault.\n // Used by the ERC-4626 deposit() and mint() functions\n // slither-disable-next-line unused-return\n assetToken.approve(platformAddress, type(uint256).max);\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset)\n public\n view\n virtual\n override\n returns (bool)\n {\n return _asset == address(assetToken);\n }\n\n /**\n * @notice is not supported for this strategy as the asset and\n * ERC-4626 Tokenized Vault are set at deploy time.\n * @dev If the ERC-4626 Tokenized Vault needed to be changed, a new\n * contract would need to be deployed and the proxy updated.\n */\n function setPTokenAddress(address, address) external override onlyGovernor {\n revert(\"unsupported function\");\n }\n\n /**\n * @notice is not supported for this strategy as the asset and\n * ERC-4626 Tokenized Vault are set at deploy time.\n * @dev If the ERC-4626 Tokenized Vault needed to be changed, a new\n * contract would need to be deployed and the proxy updated.\n */\n function removePToken(uint256) external override onlyGovernor {\n revert(\"unsupported function\");\n }\n}\n" + }, + "contracts/strategies/Generalized4626USDTStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IUSDT {\n // Tether's approve does not return a bool like standard IERC20 contracts\n // slither-disable-next-line erc20-interface\n function approve(address _spender, uint256 _value) external;\n}\n\n/**\n * @title Generalized 4626 Strategy when asset is Tether USD (USDT)\n * @notice Investment strategy for ERC-4626 Tokenized Vaults for the USDT asset.\n * @author Origin Protocol Inc\n */\nimport { Generalized4626Strategy } from \"./Generalized4626Strategy.sol\";\n\ncontract Generalized4626USDTStrategy is Generalized4626Strategy {\n /**\n * @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n * and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n * @param _assetToken Address of the ERC-4626 asset token. eg frxETH or DAI\n */\n constructor(BaseStrategyConfig memory _baseConfig, address _assetToken)\n Generalized4626Strategy(_baseConfig, _assetToken)\n {}\n\n /// @dev Override for Tether as USDT does not return a bool on approve.\n /// Using assetToken.approve will fail as it expects a bool return value\n function _approveBase() internal virtual override {\n // Approval the asset to be transferred to the ERC-4626 Tokenized Vault.\n // Used by the ERC-4626 deposit() and mint() functions\n // slither-disable-next-line unused-return\n IUSDT(address(assetToken)).approve(platformAddress, type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/IAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface for Aaves Lending Pool\n * Documentation: https://developers.aave.com/#lendingpool\n */\ninterface IAaveLendingPool {\n /**\n * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.\n * - E.g. User deposits 100 USDC and gets in return 100 aUSDC\n * @param asset The address of the underlying asset to deposit\n * @param amount The amount to be deposited\n * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user\n * wants to receive them on his own wallet, or a different address if the beneficiary of aTokens\n * is a different wallet\n * @param referralCode Code used to register the integrator originating the operation, for potential rewards.\n * 0 if the action is executed directly by the user, without any middle-man\n **/\n function deposit(\n address asset,\n uint256 amount,\n address onBehalfOf,\n uint16 referralCode\n ) external;\n\n /**\n * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned\n * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC\n * @param asset The address of the underlying asset to withdraw\n * @param amount The underlying amount to be withdrawn\n * - Send the value type(uint256).max in order to withdraw the whole aToken balance\n * @param to Address that will receive the underlying, same as msg.sender if the user\n * wants to receive it on his own wallet, or a different address if the beneficiary is a\n * different wallet\n * @return The final amount withdrawn\n **/\n function withdraw(\n address asset,\n uint256 amount,\n address to\n ) external returns (uint256);\n}\n\n/**\n * @dev Interface for Aaves Lending Pool\n * Documentation: https://developers.aave.com/#lendingpooladdressesprovider\n */\ninterface ILendingPoolAddressesProvider {\n /**\n * @notice Get the current address for Aave LendingPool\n * @dev Lending pool is the core contract on which to call deposit\n */\n function getLendingPool() external view returns (address);\n}\n" + }, + "contracts/strategies/IAaveIncentivesController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IAaveIncentivesController {\n event RewardsAccrued(address indexed user, uint256 amount);\n\n event RewardsClaimed(\n address indexed user,\n address indexed to,\n uint256 amount\n );\n\n event RewardsClaimed(\n address indexed user,\n address indexed to,\n address indexed claimer,\n uint256 amount\n );\n\n event ClaimerSet(address indexed user, address indexed claimer);\n\n /*\n * @dev Returns the configuration of the distribution for a certain asset\n * @param asset The address of the reference asset of the distribution\n * @return The asset index, the emission per second and the last updated timestamp\n **/\n function getAssetData(address asset)\n external\n view\n returns (\n uint256,\n uint256,\n uint256\n );\n\n /**\n * @dev Whitelists an address to claim the rewards on behalf of another address\n * @param user The address of the user\n * @param claimer The address of the claimer\n */\n function setClaimer(address user, address claimer) external;\n\n /**\n * @dev Returns the whitelisted claimer for a certain address (0x0 if not set)\n * @param user The address of the user\n * @return The claimer address\n */\n function getClaimer(address user) external view returns (address);\n\n /**\n * @dev Configure assets for a certain rewards emission\n * @param assets The assets to incentivize\n * @param emissionsPerSecond The emission for each asset\n */\n function configureAssets(\n address[] calldata assets,\n uint256[] calldata emissionsPerSecond\n ) external;\n\n /**\n * @dev Called by the corresponding asset on any update that affects the rewards distribution\n * @param asset The address of the user\n * @param userBalance The balance of the user of the asset in the lending pool\n * @param totalSupply The total supply of the asset in the lending pool\n **/\n function handleAction(\n address asset,\n uint256 userBalance,\n uint256 totalSupply\n ) external;\n\n /**\n * @dev Returns the total of rewards of an user, already accrued + not yet accrued\n * @param user The address of the user\n * @return The rewards\n **/\n function getRewardsBalance(address[] calldata assets, address user)\n external\n view\n returns (uint256);\n\n /**\n * @dev Claims reward for an user, on all the assets of the lending pool,\n * accumulating the pending rewards\n * @param amount Amount of rewards to claim\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewards(\n address[] calldata assets,\n uint256 amount,\n address to\n ) external returns (uint256);\n\n /**\n * @dev Claims reward for an user on behalf, on all the assets of the\n * lending pool, accumulating the pending rewards. The caller must\n * be whitelisted via \"allowClaimOnBehalf\" function by the RewardsAdmin role manager\n * @param amount Amount of rewards to claim\n * @param user Address to check and claim rewards\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewardsOnBehalf(\n address[] calldata assets,\n uint256 amount,\n address user,\n address to\n ) external returns (uint256);\n\n /**\n * @dev returns the unclaimed rewards of the user\n * @param user the address of the user\n * @return the unclaimed user rewards\n */\n function getUserUnclaimedRewards(address user)\n external\n view\n returns (uint256);\n\n /**\n * @dev returns the unclaimed rewards of the user\n * @param user the address of the user\n * @param asset The asset to incentivize\n * @return the user index for the asset\n */\n function getUserAssetData(address user, address asset)\n external\n view\n returns (uint256);\n\n /**\n * @dev for backward compatibility with previous implementation of the Incentives controller\n */\n function REWARD_TOKEN() external view returns (address);\n\n /**\n * @dev for backward compatibility with previous implementation of the Incentives controller\n */\n function PRECISION() external view returns (uint8);\n\n /**\n * @dev Gets the distribution end timestamp of the emissions\n */\n function DISTRIBUTION_END() external view returns (uint256);\n}\n" + }, + "contracts/strategies/IAaveStakeToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IAaveStakedToken {\n function COOLDOWN_SECONDS() external returns (uint256);\n\n function UNSTAKE_WINDOW() external returns (uint256);\n\n function balanceOf(address addr) external returns (uint256);\n\n function redeem(address to, uint256 amount) external;\n\n function stakersCooldowns(address addr) external returns (uint256);\n\n function cooldown() external;\n}\n" + }, + "contracts/strategies/ICompound.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @dev Compound C Token interface\n * Documentation: https://compound.finance/developers/ctokens\n */\ninterface ICERC20 {\n /**\n * @notice The mint function transfers an asset into the protocol, which begins accumulating\n * interest based on the current Supply Rate for the asset. The user receives a quantity of\n * cTokens equal to the underlying tokens supplied, divided by the current Exchange Rate.\n * @param mintAmount The amount of the asset to be supplied, in units of the underlying asset.\n * @return 0 on success, otherwise an Error codes\n */\n function mint(uint256 mintAmount) external returns (uint256);\n\n /**\n * @notice Sender redeems cTokens in exchange for the underlying asset\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param redeemTokens The number of cTokens to redeem into underlying\n * @return uint 0=success, otherwise an error code.\n */\n function redeem(uint256 redeemTokens) external returns (uint256);\n\n /**\n * @notice The redeem underlying function converts cTokens into a specified quantity of the underlying\n * asset, and returns them to the user. The amount of cTokens redeemed is equal to the quantity of\n * underlying tokens received, divided by the current Exchange Rate. The amount redeemed must be less\n * than the user's Account Liquidity and the market's available liquidity.\n * @param redeemAmount The amount of underlying to be redeemed.\n * @return 0 on success, otherwise an error code.\n */\n function redeemUnderlying(uint256 redeemAmount) external returns (uint256);\n\n /**\n * @notice The user's underlying balance, representing their assets in the protocol, is equal to\n * the user's cToken balance multiplied by the Exchange Rate.\n * @param owner The account to get the underlying balance of.\n * @return The amount of underlying currently owned by the account.\n */\n function balanceOfUnderlying(address owner) external returns (uint256);\n\n /**\n * @notice Calculates the exchange rate from the underlying to the CToken\n * @dev This function does not accrue interest before calculating the exchange rate\n * @return Calculated exchange rate scaled by 1e18\n */\n function exchangeRateStored() external view returns (uint256);\n\n /**\n * @notice Get the token balance of the `owner`\n * @param owner The address of the account to query\n * @return The number of tokens owned by `owner`\n */\n function balanceOf(address owner) external view returns (uint256);\n\n /**\n * @notice Get the supply rate per block for supplying the token to Compound.\n */\n function supplyRatePerBlock() external view returns (uint256);\n\n /**\n * @notice Address of the Compound Comptroller.\n */\n function comptroller() external view returns (address);\n}\n" + }, + "contracts/strategies/IConvexDeposits.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IConvexDeposits {\n function deposit(\n uint256 _pid,\n uint256 _amount,\n bool _stake\n ) external returns (bool);\n\n function deposit(\n uint256 _amount,\n bool _lock,\n address _stakeAddress\n ) external;\n}\n" + }, + "contracts/strategies/ICRVMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICRVMinter {\n function mint(address gaugeAddress) external;\n}\n" + }, + "contracts/strategies/ICurveETHPoolV1.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurveETHPoolV1 {\n event AddLiquidity(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event ApplyNewFee(uint256 fee);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event CommitNewFee(uint256 new_fee);\n event RampA(\n uint256 old_A,\n uint256 new_A,\n uint256 initial_time,\n uint256 future_time\n );\n event RemoveLiquidity(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 token_supply\n );\n event RemoveLiquidityImbalance(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event RemoveLiquidityOne(\n address indexed provider,\n uint256 token_amount,\n uint256 coin_amount,\n uint256 token_supply\n );\n event StopRampA(uint256 A, uint256 t);\n event TokenExchange(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event Transfer(\n address indexed sender,\n address indexed receiver,\n uint256 value\n );\n\n function A() external view returns (uint256);\n\n function A_precise() external view returns (uint256);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_liquidity(uint256[2] memory _amounts, uint256 _min_mint_amount)\n external\n payable\n returns (uint256);\n\n function add_liquidity(\n uint256[2] memory _amounts,\n uint256 _min_mint_amount,\n address _receiver\n ) external payable returns (uint256);\n\n function admin_action_deadline() external view returns (uint256);\n\n function admin_balances(uint256 i) external view returns (uint256);\n\n function admin_fee() external view returns (uint256);\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function apply_new_fee() external;\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function balances(uint256 arg0) external view returns (uint256);\n\n function calc_token_amount(uint256[2] memory _amounts, bool _is_deposit)\n external\n view\n returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _burn_amount, int128 i)\n external\n view\n returns (uint256);\n\n function coins(uint256 arg0) external view returns (address);\n\n function commit_new_fee(uint256 _new_fee) external;\n\n function decimals() external view returns (uint256);\n\n function ema_price() external view returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external payable returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external payable returns (uint256);\n\n function fee() external view returns (uint256);\n\n function future_A() external view returns (uint256);\n\n function future_A_time() external view returns (uint256);\n\n function future_fee() external view returns (uint256);\n\n function get_balances() external view returns (uint256[2] memory);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n\n function get_p() external view returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function initial_A() external view returns (uint256);\n\n function initial_A_time() external view returns (uint256);\n\n function initialize(\n string memory _name,\n string memory _symbol,\n address[4] memory _coins,\n uint256[4] memory _rate_multipliers,\n uint256 _A,\n uint256 _fee\n ) external;\n\n function last_price() external view returns (uint256);\n\n function ma_exp_time() external view returns (uint256);\n\n function ma_last_time() external view returns (uint256);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function price_oracle() external view returns (uint256);\n\n function ramp_A(uint256 _future_A, uint256 _future_time) external;\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[2] memory _min_amounts\n ) external returns (uint256[2] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[2] memory _min_amounts,\n address _receiver\n ) external returns (uint256[2] memory);\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burn_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burn_amount,\n address _receiver\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received,\n address _receiver\n ) external returns (uint256);\n\n function set_ma_exp_time(uint256 _ma_exp_time) external;\n\n function stop_ramp_A() external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw_admin_fees() external;\n}\n" + }, + "contracts/strategies/ICurveGauge.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurveGauge {\n function balanceOf(address account) external view returns (uint256);\n\n function deposit(uint256 value, address account) external;\n\n function withdraw(uint256 value) external;\n}\n" + }, + "contracts/strategies/ICurveMetaPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface ICurveMetaPool {\n function add_liquidity(uint256[2] calldata amounts, uint256 min_mint_amount)\n external\n returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function remove_liquidity(uint256 _amount, uint256[2] calldata min_amounts)\n external\n returns (uint256[2] calldata);\n\n function remove_liquidity_one_coin(\n uint256 _token_amount,\n int128 i,\n uint256 min_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[2] calldata amounts,\n uint256 max_burn_amount\n ) external returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _token_amount, int128 i)\n external\n view\n returns (uint256);\n\n function balances(uint256 i) external view returns (uint256);\n\n function calc_token_amount(uint256[2] calldata amounts, bool deposit)\n external\n view\n returns (uint256);\n\n function base_pool() external view returns (address);\n\n function fee() external view returns (uint256);\n\n function coins(uint256 i) external view returns (address);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 dx,\n uint256 min_dy\n ) external returns (uint256);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n}\n" + }, + "contracts/strategies/ICurvePool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurvePool {\n function get_virtual_price() external view returns (uint256);\n\n function add_liquidity(uint256[3] calldata _amounts, uint256 _min) external;\n\n function balances(uint256) external view returns (uint256);\n\n function calc_token_amount(uint256[3] calldata _amounts, bool _deposit)\n external\n returns (uint256);\n\n function fee() external view returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _amount,\n int128 _index,\n uint256 _minAmount\n ) external;\n\n function remove_liquidity(\n uint256 _amount,\n uint256[3] calldata _minWithdrawAmounts\n ) external;\n\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n external\n view\n returns (uint256);\n\n function exchange(\n uint256 i,\n uint256 j,\n uint256 dx,\n uint256 min_dy\n ) external returns (uint256);\n\n function coins(uint256 _index) external view returns (address);\n\n function remove_liquidity_imbalance(\n uint256[3] calldata _amounts,\n uint256 maxBurnAmount\n ) external;\n}\n" + }, + "contracts/strategies/IRewardStaking.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IRewardStaking {\n function stakeFor(address, uint256) external;\n\n function stake(uint256) external;\n\n function withdraw(uint256 amount, bool claim) external;\n\n function withdrawAndUnwrap(uint256 amount, bool claim) external;\n\n function earned(address account) external view returns (uint256);\n\n function getReward() external;\n\n function getReward(address _account, bool _claimExtras) external;\n\n function extraRewardsLength() external returns (uint256);\n\n function extraRewards(uint256 _pid) external returns (address);\n\n function rewardToken() external returns (address);\n\n function balanceOf(address account) external view returns (uint256);\n}\n" + }, + "contracts/strategies/MorphoAaveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Morpho Aave Strategy\n * @notice Investment strategy for investing stablecoins via Morpho (Aave)\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { IMorpho } from \"../interfaces/morpho/IMorpho.sol\";\nimport { ILens } from \"../interfaces/morpho/ILens.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MorphoAaveStrategy is InitializableAbstractStrategy {\n address public constant MORPHO = 0x777777c9898D384F785Ee44Acfe945efDFf5f3E0;\n address public constant LENS = 0x507fA343d0A90786d86C7cd885f5C49263A91FF4;\n\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @dev Initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Approve the spending of all assets by main Morpho contract,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address asset = assetsMapped[i];\n\n // Safe approval\n IERC20(asset).safeApprove(MORPHO, 0);\n IERC20(asset).safeApprove(MORPHO, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset\n * We need to approve and allow Morpho to move them\n * @param _asset Address of the asset to approve\n * @param _pToken The pToken for the approval\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n IERC20(_asset).safeApprove(MORPHO, 0);\n IERC20(_asset).safeApprove(MORPHO, type(uint256).max);\n }\n\n /**\n * @dev Collect accumulated rewards and send them to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Morpho Aave-v2 doesn't distribute reward tokens\n // solhint-disable-next-line max-line-length\n // Ref: https://developers.morpho.xyz/interact-with-morpho/get-started/interact-with-morpho/claim-rewards#morpho-aave-v2\n }\n\n /**\n * @dev Get the amount of rewards pending to be collected from the protocol\n */\n function getPendingRewards() external view returns (uint256 balance) {\n // Morpho Aave-v2 doesn't distribute reward tokens\n // solhint-disable-next-line max-line-length\n // Ref: https://developers.morpho.xyz/interact-with-morpho/get-started/interact-with-morpho/claim-rewards#morpho-aave-v2\n return 0;\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n\n address pToken = address(_getPTokenFor(_asset));\n\n IMorpho(MORPHO).supply(\n pToken,\n address(this), // the address of the user you want to supply on behalf of\n _amount\n );\n emit Deposit(_asset, pToken, _amount);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Morpho\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Morpho\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n address pToken = address(_getPTokenFor(_asset));\n\n IMorpho(MORPHO).withdraw(pToken, _amount);\n emit Withdrawal(_asset, pToken, _amount);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = _checkBalance(assetsMapped[i]);\n if (balance > 0) {\n _withdraw(vaultAddress, assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Return total value of an asset held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n return _checkBalance(_asset);\n }\n\n function _checkBalance(address _asset)\n internal\n view\n returns (uint256 balance)\n {\n address pToken = address(_getPTokenFor(_asset));\n\n // Total value represented by decimal position of underlying token\n (, , balance) = ILens(LENS).getCurrentSupplyBalanceInOf(\n pToken,\n address(this)\n );\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Get the pToken wrapped in the IERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return pToken Corresponding pToken to this asset\n */\n function _getPTokenFor(address _asset) internal view returns (IERC20) {\n address pToken = assetToPToken[_asset];\n require(pToken != address(0), \"pToken does not exist\");\n return IERC20(pToken);\n }\n}\n" + }, + "contracts/strategies/MorphoCompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Morpho Compound Strategy\n * @notice Investment strategy for investing stablecoins via Morpho (Compound)\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, AbstractCompoundStrategy, InitializableAbstractStrategy } from \"./AbstractCompoundStrategy.sol\";\nimport { IMorpho } from \"../interfaces/morpho/IMorpho.sol\";\nimport { ILens } from \"../interfaces/morpho/ILens.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MorphoCompoundStrategy is AbstractCompoundStrategy {\n address public constant MORPHO = 0x8888882f8f843896699869179fB6E4f7e3B58888;\n address public constant LENS = 0x930f1b46e1D081Ec1524efD95752bE3eCe51EF67;\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @dev Initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Approve the spending of all assets by main Morpho contract,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address asset = assetsMapped[i];\n\n // Safe approval\n IERC20(asset).safeApprove(MORPHO, 0);\n IERC20(asset).safeApprove(MORPHO, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset\n * We need to approve and allow Morpho to move them\n * @param _asset Address of the asset to approve\n * @param _pToken The pToken for the approval\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n IERC20(_asset).safeApprove(MORPHO, 0);\n IERC20(_asset).safeApprove(MORPHO, type(uint256).max);\n }\n\n /**\n * @dev Collect accumulated rewards and send them to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n /**\n * Gas considerations. We could query Morpho LENS's `getUserUnclaimedRewards` for each\n * cToken separately and only claimRewards where it is economically feasible. Each call\n * (out of 3) costs ~60k gas extra.\n *\n * Each extra cToken in the `poolTokens` of `claimRewards` function makes that call\n * 89-120k more expensive gas wise.\n *\n * With Lens query in case where:\n * - there is only 1 reward token to collect. Net gas usage in best case would be\n * 3*60 - 2*120 = -60k -> saving 60k gas\n * - there are 2 reward tokens to collect. Net gas usage in best case would be\n * 3*60 - 120 = 60k -> more expensive for 60k gas\n * - there are 3 reward tokens to collect. Net gas usage in best case would be\n * 3*60 = 180k -> more expensive for 180k gas\n *\n * For the above reasoning such \"optimization\" is not implemented\n */\n\n address[] memory poolTokens = new address[](assetsMapped.length);\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n poolTokens[i] = assetToPToken[assetsMapped[i]];\n }\n\n // slither-disable-next-line unused-return\n IMorpho(MORPHO).claimRewards(\n poolTokens, // The addresses of the underlying protocol's pools to claim rewards from\n false // Whether to trade the accrued rewards for MORPHO token, with a premium\n );\n\n // Transfer COMP to Harvester\n IERC20 rewardToken = IERC20(rewardTokenAddresses[0]);\n uint256 balance = rewardToken.balanceOf(address(this));\n emit RewardTokenCollected(\n harvesterAddress,\n rewardTokenAddresses[0],\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n\n /**\n * @dev Get the amount of rewards pending to be collected from the protocol\n */\n function getPendingRewards() external view returns (uint256 balance) {\n address[] memory poolTokens = new address[](assetsMapped.length);\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n poolTokens[i] = assetToPToken[assetsMapped[i]];\n }\n\n return ILens(LENS).getUserUnclaimedRewards(poolTokens, address(this));\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n\n IMorpho(MORPHO).supply(\n address(_getCTokenFor(_asset)),\n address(this), // the address of the user you want to supply on behalf of\n _amount\n );\n emit Deposit(_asset, address(_getCTokenFor(_asset)), _amount);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Morpho\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Morpho\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n address pToken = assetToPToken[_asset];\n\n IMorpho(MORPHO).withdraw(pToken, _amount);\n emit Withdrawal(_asset, address(_getCTokenFor(_asset)), _amount);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = _checkBalance(assetsMapped[i]);\n if (balance > 0) {\n _withdraw(vaultAddress, assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Return total value of an asset held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n return _checkBalance(_asset);\n }\n\n function _checkBalance(address _asset)\n internal\n view\n returns (uint256 balance)\n {\n address pToken = assetToPToken[_asset];\n\n // Total value represented by decimal position of underlying token\n (, , balance) = ILens(LENS).getCurrentSupplyBalanceInOf(\n pToken,\n address(this)\n );\n }\n}\n" + }, + "contracts/strategies/NativeStaking/FeeAccumulator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\n/**\n * @title Fee Accumulator for Native Staking SSV Strategy\n * @notice Receives execution rewards which includes tx fees and\n * MEV rewards like tx priority and tx ordering.\n * It does NOT include swept ETH from beacon chain consensus rewards or full validator withdrawals.\n * @author Origin Protocol Inc\n */\ncontract FeeAccumulator {\n /// @notice The address of the Native Staking Strategy\n address public immutable STRATEGY;\n\n event ExecutionRewardsCollected(address indexed strategy, uint256 amount);\n\n /**\n * @param _strategy Address of the Native Staking Strategy\n */\n constructor(address _strategy) {\n STRATEGY = _strategy;\n }\n\n /**\n * @notice sends all ETH in this FeeAccumulator contract to the Native Staking Strategy.\n * @return eth The amount of execution rewards that were sent to the Native Staking Strategy\n */\n function collect() external returns (uint256 eth) {\n require(msg.sender == STRATEGY, \"Caller is not the Strategy\");\n\n eth = address(this).balance;\n if (eth > 0) {\n // Send the ETH to the Native Staking Strategy\n Address.sendValue(payable(STRATEGY), eth);\n\n emit ExecutionRewardsCollected(STRATEGY, eth);\n }\n }\n\n /**\n * @dev Accept ETH\n */\n receive() external payable {}\n}\n" + }, + "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { FeeAccumulator } from \"./FeeAccumulator.sol\";\nimport { ValidatorAccountant } from \"./ValidatorAccountant.sol\";\nimport { ISSVNetwork } from \"../../interfaces/ISSVNetwork.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/// @title Native Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract NativeStakingSSVStrategy is\n ValidatorAccountant,\n InitializableAbstractStrategy\n{\n using SafeERC20 for IERC20;\n\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n /// @notice Fee collector address\n /// @dev this address will receive maximal extractable value (MEV) rewards. These are\n /// rewards for arranging transactions in a way that benefits the validator.\n address payable public immutable FEE_ACCUMULATOR_ADDRESS;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[49] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n /// @param _feeAccumulator Address of the fee accumulator receiving execution layer validator rewards\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n uint256 _maxValidators,\n address _feeAccumulator,\n address _beaconChainDepositContract\n )\n InitializableAbstractStrategy(_baseConfig)\n ValidatorAccountant(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _maxValidators\n )\n {\n SSV_TOKEN = _ssvToken;\n FEE_ACCUMULATOR_ADDRESS = payable(_feeAccumulator);\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract.\n /// @param _rewardTokenAddresses Address of reward token for platform\n /// @param _assets Addresses of initial supported assets\n /// @param _pTokens Platform Token corresponding addresses\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n\n // Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\n FEE_ACCUMULATOR_ADDRESS\n );\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n /// @param _asset Address of asset to deposit. Has to be WETH.\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n depositedWethAccountedFor += _amount;\n _deposit(_asset, _amount);\n }\n\n /// @dev Deposit WETH to this strategy so it can later be staked into a validator.\n /// @param _asset Address of WETH\n /// @param _amount Amount of WETH to deposit\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n /*\n * We could do a check here that would revert when \"_amount % 32 ether != 0\". With the idea of\n * not allowing deposits that will result in WETH sitting on the strategy after all the possible batches\n * of 32ETH have been staked.\n * But someone could mess with our strategy by sending some WETH to it. And we might want to deposit just\n * enough WETH to add it up to 32 so it can be staked. For that reason the check is left out.\n *\n * WETH sitting on the strategy won't interfere with the accounting since accounting only operates on ETH.\n */\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n depositedWethAccountedFor = wethBalance;\n\n _deposit(WETH, newWeth);\n }\n }\n\n /// @notice Withdraw WETH from this contract. Used only if some WETH for is lingering on the contract.\n /// That can happen when:\n /// - after mints if the strategy is the default\n /// - time between depositToStrategy and stakeEth\n /// - the deposit was not a multiple of 32 WETH\n /// - someone sent WETH directly to this contract\n /// Will NOT revert if the strategy is paused from an accounting failure.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n _wethWithdrawn(_amount);\n\n IERC20(_asset).safeTransfer(_recipient, _amount);\n emit Withdrawal(_asset, address(0), _amount);\n }\n\n /// @notice transfer all WETH deposits back to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `exitSsvValidator` and `removeSsvValidator` operations.\n /// This does not withdraw any execution rewards from the FeeAccumulator or\n /// consensus rewards in this strategy.\n /// Any ETH in this strategy that was swept from a full validator withdrawal will not be withdrawn.\n /// ETH from full validator withdrawals is sent to the Vault using `doAccounting`.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n if (wethBalance > 0) {\n _withdraw(vaultAddress, WETH, wethBalance);\n }\n }\n\n /// @notice Returns the total value of (W)ETH that is staked to the validators\n /// and WETH deposits that are still to be staked.\n /// This does not include ETH from consensus rewards sitting in this strategy\n /// or ETH from MEV rewards in the FeeAccumulator. These rewards are harvested\n /// and sent to the Dripper so will eventually be sent to the Vault as WETH.\n /// @param _asset Address of weth asset\n /// @return balance Total value of (W)ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n balance =\n // add the ETH that has been staked in validators\n activeDepositedValidators *\n FULL_STAKE +\n // add the WETH in the strategy from deposits that are still to be staked\n IERC20(WETH).balanceOf(address(this));\n }\n\n function pause() external onlyStrategist {\n _pause();\n }\n\n /// @notice Returns bool indicating whether asset is supported by strategy.\n /// @param _asset The address of the asset token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for deposits\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\n function setFeeRecipient() external {\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\n FEE_ACCUMULATOR_ADDRESS\n );\n }\n\n /**\n * @notice Only accept ETH from the FeeAccumulator and the WETH contract - required when\n * unwrapping WETH just before staking it to the validator.\n * The strategy will also receive ETH from the priority fees of transactions when producing blocks\n * as defined in EIP-1559.\n * The tx fees come from the Beacon chain so do not need any EVM level permissions to receive ETH.\n * The tx fees are paid with each block produced. They are not included in the consensus rewards\n * which are periodically swept from the validators to this strategy.\n * For accounting purposes, the priority fees of transactions will be considered consensus rewards\n * and will be included in the AccountingConsensusRewards event.\n * @dev don't want to receive donations from anyone else as donations over the fuse limits will\n * mess with the accounting of the consensus rewards and validator full withdrawals.\n */\n receive() external payable {\n require(\n msg.sender == FEE_ACCUMULATOR_ADDRESS || msg.sender == WETH,\n \"Eth not from allowed contracts\"\n );\n }\n\n /***************************************\n Internal functions\n ****************************************/\n\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Convert accumulated ETH to WETH and send to the Harvester.\n /// Will revert if the strategy is paused for accounting.\n function _collectRewardTokens() internal override whenNotPaused {\n // collect ETH from execution rewards from the fee accumulator\n uint256 executionRewards = FeeAccumulator(FEE_ACCUMULATOR_ADDRESS)\n .collect();\n\n // total ETH rewards to be harvested = execution rewards + consensus rewards\n uint256 ethRewards = executionRewards + consensusRewards;\n\n require(\n address(this).balance >= ethRewards,\n \"Insufficient eth balance\"\n );\n\n if (ethRewards > 0) {\n // reset the counter keeping track of beacon chain consensus rewards\n consensusRewards = 0;\n\n // Convert ETH rewards to WETH\n IWETH9(WETH).deposit{ value: ethRewards }();\n\n IERC20(WETH).safeTransfer(harvesterAddress, ethRewards);\n emit RewardTokenCollected(harvesterAddress, WETH, ethRewards);\n }\n }\n\n /// @dev emits Withdrawal event from NativeStakingSSVStrategy\n function _wethWithdrawnToVault(uint256 _amount) internal override {\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _wethWithdrawn(uint256 _amount) internal override {\n /* In an ideal world we wouldn't need to reduce the deduction amount when the\n * depositedWethAccountedFor is smaller than the _amount.\n *\n * The reason this is required is that a malicious actor could sent WETH directly\n * to this contract and that would circumvent the increase of depositedWethAccountedFor\n * property. When the ETH would be staked the depositedWethAccountedFor amount could\n * be deducted so much that it would be negative.\n */\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n }\n}\n" + }, + "contracts/strategies/NativeStaking/ValidatorAccountant.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ValidatorRegistrator } from \"./ValidatorRegistrator.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\n\n/// @title Validator Accountant\n/// @notice Attributes the ETH swept from beacon chain validators to this strategy contract\n/// as either full or partial withdrawals. Partial withdrawals being consensus rewards.\n/// Full withdrawals are from exited validators.\n/// @author Origin Protocol Inc\nabstract contract ValidatorAccountant is ValidatorRegistrator {\n /// @notice The minimum amount of blocks that need to pass between two calls to manuallyFixAccounting\n uint256 public constant MIN_FIX_ACCOUNTING_CADENCE = 7200; // 1 day\n\n /// @notice Keeps track of the total consensus rewards swept from the beacon chain\n uint256 public consensusRewards;\n\n /// @notice start of fuse interval\n uint256 public fuseIntervalStart;\n /// @notice end of fuse interval\n uint256 public fuseIntervalEnd;\n /// @notice last block number manuallyFixAccounting has been called\n uint256 public lastFixAccountingBlockNumber;\n\n uint256[49] private __gap;\n\n event FuseIntervalUpdated(uint256 start, uint256 end);\n event AccountingFullyWithdrawnValidator(\n uint256 noOfValidators,\n uint256 remainingValidators,\n uint256 wethSentToVault\n );\n event AccountingValidatorSlashed(\n uint256 remainingValidators,\n uint256 wethSentToVault\n );\n event AccountingConsensusRewards(uint256 amount);\n\n event AccountingManuallyFixed(\n int256 validatorsDelta,\n int256 consensusRewardsDelta,\n uint256 wethToVault\n );\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n uint256 _maxValidators\n )\n ValidatorRegistrator(\n _wethAddress,\n _vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _maxValidators\n )\n {}\n\n /// @notice set fuse interval values\n function setFuseInterval(\n uint256 _fuseIntervalStart,\n uint256 _fuseIntervalEnd\n ) external onlyGovernor {\n require(\n _fuseIntervalStart < _fuseIntervalEnd &&\n _fuseIntervalEnd < 32 ether &&\n _fuseIntervalEnd - _fuseIntervalStart >= 4 ether,\n \"Incorrect fuse interval\"\n );\n\n fuseIntervalStart = _fuseIntervalStart;\n fuseIntervalEnd = _fuseIntervalEnd;\n\n emit FuseIntervalUpdated(_fuseIntervalStart, _fuseIntervalEnd);\n }\n\n /* solhint-disable max-line-length */\n /// This notion page offers a good explanation of how the accounting functions\n /// https://www.notion.so/originprotocol/Limited-simplified-native-staking-accounting-67a217c8420d40678eb943b9da0ee77d\n /// In short, after dividing by 32, if the ETH remaining on the contract falls between 0 and fuseIntervalStart,\n /// the accounting function will treat that ETH as Beacon chain consensus rewards.\n /// On the contrary, if after dividing by 32, the ETH remaining on the contract falls between fuseIntervalEnd and 32,\n /// the accounting function will treat that as a validator slashing.\n /// @notice Perform the accounting attributing beacon chain ETH to either full or partial withdrawals. Returns true when\n /// accounting is valid and fuse isn't \"blown\". Returns false when fuse is blown.\n /// @dev This function could in theory be permission-less but lets allow only the Registrator (Defender Action) to call it\n /// for now.\n /// @return accountingValid true if accounting was successful, false if fuse is blown\n /* solhint-enable max-line-length */\n function doAccounting()\n external\n onlyRegistrator\n whenNotPaused\n nonReentrant\n returns (bool accountingValid)\n {\n // pause the accounting on failure\n accountingValid = _doAccounting(true);\n }\n\n // slither-disable-start reentrancy-eth\n function _doAccounting(bool pauseOnFail)\n internal\n returns (bool accountingValid)\n {\n if (address(this).balance < consensusRewards) {\n return _failAccounting(pauseOnFail);\n }\n\n // Calculate all the new ETH that has been swept to the contract since the last accounting\n uint256 newSweptETH = address(this).balance - consensusRewards;\n accountingValid = true;\n\n // send the ETH that is from fully withdrawn validators to the Vault\n if (newSweptETH >= FULL_STAKE) {\n uint256 fullyWithdrawnValidators;\n // explicitly cast to uint256 as we want to round to a whole number of validators\n fullyWithdrawnValidators = uint256(newSweptETH / FULL_STAKE);\n activeDepositedValidators -= fullyWithdrawnValidators;\n\n uint256 wethToVault = FULL_STAKE * fullyWithdrawnValidators;\n IWETH9(WETH).deposit{ value: wethToVault }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, wethToVault);\n _wethWithdrawnToVault(wethToVault);\n\n emit AccountingFullyWithdrawnValidator(\n fullyWithdrawnValidators,\n activeDepositedValidators,\n wethToVault\n );\n }\n\n uint256 ethRemaining = address(this).balance - consensusRewards;\n // should be less than a whole validator stake\n require(ethRemaining < FULL_STAKE, \"Unexpected accounting\");\n\n // If no Beacon chain consensus rewards swept\n if (ethRemaining == 0) {\n // do nothing\n return accountingValid;\n } else if (ethRemaining < fuseIntervalStart) {\n // Beacon chain consensus rewards swept (partial validator withdrawals)\n // solhint-disable-next-line reentrancy\n consensusRewards += ethRemaining;\n emit AccountingConsensusRewards(ethRemaining);\n } else if (ethRemaining > fuseIntervalEnd) {\n // Beacon chain consensus rewards swept but also a slashed validator fully exited\n IWETH9(WETH).deposit{ value: ethRemaining }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, ethRemaining);\n activeDepositedValidators -= 1;\n\n _wethWithdrawnToVault(ethRemaining);\n\n emit AccountingValidatorSlashed(\n activeDepositedValidators,\n ethRemaining\n );\n }\n // Oh no... Fuse is blown. The Strategist needs to adjust the accounting values.\n else {\n return _failAccounting(pauseOnFail);\n }\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @dev pause any further accounting if required and return false\n function _failAccounting(bool pauseOnFail)\n internal\n returns (bool accountingValid)\n {\n // pause if not already\n if (pauseOnFail) {\n _pause();\n }\n // fail the accounting\n accountingValid = false;\n }\n\n /// @notice Allow the Strategist to fix the accounting of this strategy and unpause.\n /// @param _validatorsDelta adjust the active validators by up to plus three or minus three\n /// @param _consensusRewardsDelta adjust the accounted for consensus rewards up or down\n /// @param _ethToVaultAmount the amount of ETH that gets wrapped into WETH and sent to the Vault\n /// @dev There is a case when a validator(s) gets slashed so much that the eth swept from\n /// the beacon chain enters the fuse area and there are no consensus rewards on the contract\n /// to \"dip into\"/use. To increase the amount of unaccounted ETH over the fuse end interval\n /// we need to reduce the amount of active deposited validators and immediately send WETH\n /// to the vault, so it doesn't interfere with further accounting.\n function manuallyFixAccounting(\n int256 _validatorsDelta,\n int256 _consensusRewardsDelta,\n uint256 _ethToVaultAmount\n ) external onlyStrategist whenPaused nonReentrant {\n require(\n lastFixAccountingBlockNumber + MIN_FIX_ACCOUNTING_CADENCE <\n block.number,\n \"Fix accounting called too soon\"\n );\n require(\n _validatorsDelta >= -3 &&\n _validatorsDelta <= 3 &&\n // new value must be positive\n int256(activeDepositedValidators) + _validatorsDelta >= 0,\n \"Invalid validatorsDelta\"\n );\n require(\n _consensusRewardsDelta >= -332 ether &&\n _consensusRewardsDelta <= 332 ether &&\n // new value must be positive\n int256(consensusRewards) + _consensusRewardsDelta >= 0,\n \"Invalid consensusRewardsDelta\"\n );\n require(_ethToVaultAmount <= 32 ether * 3, \"Invalid wethToVaultAmount\");\n\n activeDepositedValidators = uint256(\n int256(activeDepositedValidators) + _validatorsDelta\n );\n consensusRewards = uint256(\n int256(consensusRewards) + _consensusRewardsDelta\n );\n lastFixAccountingBlockNumber = block.number;\n if (_ethToVaultAmount > 0) {\n IWETH9(WETH).deposit{ value: _ethToVaultAmount }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, _ethToVaultAmount);\n _wethWithdrawnToVault(_ethToVaultAmount);\n }\n\n emit AccountingManuallyFixed(\n _validatorsDelta,\n _consensusRewardsDelta,\n _ethToVaultAmount\n );\n\n // rerun the accounting to see if it has now been fixed.\n // Do not pause the accounting on failure as it is already paused\n require(_doAccounting(false), \"Fuse still blown\");\n\n // unpause since doAccounting was successful\n _unpause();\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n /// @dev allows for NativeStakingSSVStrategy contract to emit the Withdrawal event\n function _wethWithdrawnToVault(uint256 _amount) internal virtual;\n}\n" + }, + "contracts/strategies/NativeStaking/ValidatorRegistrator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Registrator of the validators\n * @notice This contract implements all the required functionality to register, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract ValidatorRegistrator is Governable, Pausable {\n /// @notice The maximum amount of ETH that can be staked by a validator\n /// @dev this can change in the future with EIP-7251, Increase the MAX_EFFECTIVE_BALANCE\n uint256 public constant FULL_STAKE = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Maximum number of validators that can be registered in this strategy\n uint256 public immutable MAX_VALIDATORS;\n\n /// @notice Address of the registrator - allowed to register, exit and remove validators\n address public validatorRegistrator;\n /// @notice The number of validators that have 32 (!) ETH actively deposited. When a new deposit\n /// to a validator happens this number increases, when a validator exit is detected this number\n /// decreases.\n uint256 public activeDepositedValidators;\n /// @notice State of the validators keccak256(pubKey) => state\n mapping(bytes32 => VALIDATOR_STATE) public validatorsStates;\n /// @notice The account that is allowed to modify stakeETHThreshold and reset stakeETHTally\n address public stakingMonitor;\n /// @notice Amount of ETH that can be staked before staking on the contract is suspended\n /// and the `stakingMonitor` needs to approve further staking by calling `resetStakeETHTally`\n uint256 public stakeETHThreshold;\n /// @notice Amount of ETH that has been staked since the `stakingMonitor` last called `resetStakeETHTally`.\n /// This can not go above `stakeETHThreshold`.\n uint256 public stakeETHTally;\n // For future use\n uint256[47] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n EXITING, // exit message has been posted and validator is in the process of exiting\n EXIT_COMPLETE // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event ETHStaked(bytes32 indexed pubKeyHash, bytes pubKey, uint256 amount);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event SSVValidatorExitInitiated(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event SSVValidatorExitCompleted(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event StakeETHThresholdChanged(uint256 amount);\n event StakeETHTallyReset();\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(\n msg.sender == validatorRegistrator,\n \"Caller is not the Registrator\"\n );\n _;\n }\n\n /// @dev Throws if called by any account other than the Staking monitor\n modifier onlyStakingMonitor() {\n require(msg.sender == stakingMonitor, \"Caller is not the Monitor\");\n _;\n }\n\n /// @dev Throws if called by any account other than the Strategist\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(VAULT_ADDRESS).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n uint256 _maxValidators\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n MAX_VALIDATORS = _maxValidators;\n }\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Set the address of the staking monitor that is allowed to reset stakeETHTally\n function setStakingMonitor(address _address) external onlyGovernor {\n stakingMonitor = _address;\n emit StakingMonitorChanged(_address);\n }\n\n /// @notice Set the amount of ETH that can be staked before staking monitor\n // needs to a approve further staking by resetting the stake ETH tally\n function setStakeETHThreshold(uint256 _amount) external onlyGovernor {\n stakeETHThreshold = _amount;\n emit StakeETHThresholdChanged(_amount);\n }\n\n /// @notice Reset the stakeETHTally\n function resetStakeETHTally() external onlyStakingMonitor {\n stakeETHTally = 0;\n emit StakeETHTallyReset();\n }\n\n /// @notice Stakes WETH to the node validators\n /// @param validators A list of validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n // slither-disable-start reentrancy-eth\n function stakeEth(ValidatorStakeData[] calldata validators)\n external\n onlyRegistrator\n whenNotPaused\n nonReentrant\n {\n uint256 requiredETH = validators.length * FULL_STAKE;\n\n // Check there is enough WETH from the deposits sitting in this strategy contract\n require(\n requiredETH <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(\n activeDepositedValidators + validators.length <= MAX_VALIDATORS,\n \"Max validators reached\"\n );\n\n require(\n stakeETHTally + requiredETH <= stakeETHThreshold,\n \"Staking ETH over threshold\"\n );\n stakeETHTally += requiredETH;\n\n // Convert required ETH from WETH\n IWETH9(WETH).withdraw(requiredETH);\n _wethWithdrawn(requiredETH);\n\n /* 0x01 to indicate that withdrawal credentials will contain an EOA address that the sweeping function\n * can sweep funds to.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x01),\n bytes11(0),\n address(this)\n );\n\n // For each validator\n for (uint256 i = 0; i < validators.length; ++i) {\n bytes32 pubKeyHash = keccak256(validators[i].pubkey);\n\n require(\n validatorsStates[pubKeyHash] == VALIDATOR_STATE.REGISTERED,\n \"Validator not registered\"\n );\n\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: FULL_STAKE\n }(\n validators[i].pubkey,\n withdrawalCredentials,\n validators[i].signature,\n validators[i].depositDataRoot\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.STAKED;\n\n emit ETHStaked(pubKeyHash, validators[i].pubkey, FULL_STAKE);\n }\n // save gas by changing this storage variable only once rather each time in the loop.\n activeDepositedValidators += validators.length;\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Registers a new validator in the SSV Cluster.\n /// Only the registrator can call this function.\n /// @param publicKeys The public keys of the validators\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for each validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n require(\n publicKeys.length == sharesData.length,\n \"Pubkey sharesData mismatch\"\n );\n // Check each public key has not already been used\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n emit SSVValidatorRegistered(pubKeyHash, publicKeys[i], operatorIds);\n }\n\n ISSVNetwork(SSV_NETWORK).bulkRegisterValidator(\n publicKeys,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Exit a validator from the Beacon chain.\n /// The staked ETH will eventually swept to this native staking strategy.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n // slither-disable-start reentrancy-no-eth\n function exitSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds\n ) external onlyRegistrator whenNotPaused {\n bytes32 pubKeyHash = keccak256(publicKey);\n VALIDATOR_STATE currentState = validatorsStates[pubKeyHash];\n require(currentState == VALIDATOR_STATE.STAKED, \"Validator not staked\");\n\n ISSVNetwork(SSV_NETWORK).exitValidator(publicKey, operatorIds);\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXITING;\n\n emit SSVValidatorExitInitiated(pubKeyHash, publicKey, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove a validator from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n bytes32 pubKeyHash = keccak256(publicKey);\n VALIDATOR_STATE currentState = validatorsStates[pubKeyHash];\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == VALIDATOR_STATE.EXITING ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exiting\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXIT_COMPLETE;\n\n emit SSVValidatorExitCompleted(pubKeyHash, publicKey, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Deposits more SSV Tokens to the SSV Network contract which is used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// uses \"onlyStrategist\" modifier so continuous front-running can't DOS our maintenance service\n /// that tries to top up SSV tokens.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function depositSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyStrategist {\n ISSVNetwork(SSV_NETWORK).deposit(\n address(this),\n operatorIds,\n ssvAmount,\n cluster\n );\n }\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _wethWithdrawn(uint256 _amount) internal virtual;\n}\n" + }, + "contracts/strategies/plume/RoosterAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Rooster AMO strategy\n * @author Origin Protocol Inc\n * @custom:security-contact security@originprotocol.com\n */\nimport { Math as MathRooster } from \"../../../lib/rooster/v2-common/libraries/Math.sol\";\nimport { Math as Math_v5 } from \"../../../lib/rooster/openzeppelin-custom/contracts/utils/math/Math.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { IMaverickV2Pool } from \"../../interfaces/plume/IMaverickV2Pool.sol\";\nimport { IMaverickV2Quoter } from \"../../interfaces/plume/IMaverickV2Quoter.sol\";\nimport { IMaverickV2LiquidityManager } from \"../../interfaces/plume/IMaverickV2LiquidityManager.sol\";\nimport { IMaverickV2PoolLens } from \"../../interfaces/plume/IMaverickV2PoolLens.sol\";\nimport { IMaverickV2Position } from \"../../interfaces/plume/IMaverickV2Position.sol\";\nimport { IVotingDistributor } from \"../../interfaces/plume/IVotingDistributor.sol\";\nimport { IPoolDistributor } from \"../../interfaces/plume/IPoolDistributor.sol\";\n// importing custom version of rooster TickMath because of dependency collision. Maverick uses\n// a newer OpenZepplin Math library with functionality that is not present in 4.4.2 (the one we use)\nimport { TickMath } from \"../../../lib/rooster/v2-common/libraries/TickMath.sol\";\n\ncontract RoosterAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n using SafeCast for uint256;\n\n /***************************************\n Storage slot members\n ****************************************/\n\n /// @notice NFT tokenId of the liquidity position\n ///\n /// @dev starts with value of 1 and can not be 0\n // solhint-disable-next-line max-line-length\n /// https://github.com/rooster-protocol/rooster-contracts/blob/fbfecbc519e4495b12598024a42630b4a8ea4489/v2-common/contracts/base/Nft.sol#L14\n uint256 public tokenId;\n /// @dev Minimum amount of tokens the strategy would be able to withdraw from the pool.\n /// minimum amount of tokens are withdrawn at a 1:1 price\n /// Important: Underlying assets contains only assets that are deposited in the underlying Rooster pool.\n /// WETH or OETH held by this contract is not accounted for in underlying assets\n uint256 public underlyingAssets;\n /// @notice Marks the start of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareStart;\n /// @notice Marks the end of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareEnd;\n /// @dev reserved for inheritance\n int256[46] private __reserved;\n\n /***************************************\n Constants, structs and events\n ****************************************/\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the OETH token contract\n address public immutable OETH;\n /// @notice the underlying AMO Maverick (Rooster) pool\n IMaverickV2Pool public immutable mPool;\n /// @notice the Liquidity manager used to add liquidity to the pool\n IMaverickV2LiquidityManager public immutable liquidityManager;\n /// @notice the Maverick V2 poolLens\n ///\n /// @dev only used to provide the pool's current sqrtPrice\n IMaverickV2PoolLens public immutable poolLens;\n /// @notice the Maverick V2 position\n ///\n /// @dev provides details of the NFT LP position and offers functions to\n /// remove the liquidity.\n IMaverickV2Position public immutable maverickPosition;\n /// @notice the Maverick Quoter\n IMaverickV2Quoter public immutable quoter;\n /// @notice the Maverick Voting Distributor\n IVotingDistributor public immutable votingDistributor;\n /// @notice the Maverick Pool Distributor\n IPoolDistributor public immutable poolDistributor;\n\n /// @notice sqrtPriceTickLower\n /// @dev tick lower represents the lower price of OETH priced in WETH. Meaning the pool\n /// offers more than 1 OETH for 1 WETH. In other terms to get 1 OETH the swap needs to offer 0.9999 WETH\n /// this is where purchasing OETH with WETH within the liquidity position is the cheapest.\n ///\n /// _____________________\n /// | | |\n /// | WETH | OETH |\n /// | | |\n /// | | |\n /// --------- * ---- * ---------- * ---------\n /// currentPrice\n /// sqrtPriceHigher-(1:1 parity)\n /// sqrtPriceLower\n ///\n ///\n /// Price is defined as price of token1 in terms of token0. (token1 / token0)\n /// @notice sqrtPriceTickLower - OETH is priced 0.9999 WETH\n uint256 public immutable sqrtPriceTickLower;\n /// @notice sqrtPriceTickHigher\n /// @dev tick higher represents 1:1 price parity of WETH to OETH\n uint256 public immutable sqrtPriceTickHigher;\n /// @dev price at parity (in OETH this is equal to sqrtPriceTickHigher)\n uint256 public immutable sqrtPriceAtParity;\n /// @notice The tick where the strategy deploys the liquidity to\n int32 public constant TICK_NUMBER = -1;\n /// @notice Minimum liquidity that must be exceeded to continue with the action\n /// e.g. deposit, add liquidity\n uint256 public constant ACTION_THRESHOLD = 1e12;\n /// @notice Maverick pool static liquidity bin type\n uint8 public constant MAV_STATIC_BIN_KIND = 0;\n /// @dev a threshold under which the contract no longer allows for the protocol to rebalance. Guarding\n /// against a strategist / guardian being taken over and with multiple transactions draining the\n /// protocol funds.\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n /// @notice Emitted when the allowed interval within which the strategy contract is allowed to deposit\n /// liquidity to the underlying pool is updated.\n /// @param allowedWethShareStart The start of the interval\n /// @param allowedWethShareEnd The end of the interval\n event PoolWethShareIntervalUpdated(\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n /// @notice Emitted when liquidity is removed from the underlying pool\n /// @param withdrawLiquidityShare Share of strategy's liquidity that has been removed\n /// @param removedWETHAmount The amount of WETH removed\n /// @param removedOETHAmount The amount of OETH removed\n /// @param underlyingAssets Updated amount of strategy's underlying assets\n event LiquidityRemoved(\n uint256 withdrawLiquidityShare,\n uint256 removedWETHAmount,\n uint256 removedOETHAmount,\n uint256 underlyingAssets\n );\n\n /// @notice Emitted when the underlying pool is rebalanced\n /// @param currentPoolWethShare The resulting share of strategy's liquidity\n /// in the TICK_NUMBER\n event PoolRebalanced(uint256 currentPoolWethShare);\n\n /// @notice Emitted when the amount of underlying assets the strategy hold as\n /// liquidity in the pool is updated.\n /// @param underlyingAssets Updated amount of strategy's underlying assets\n event UnderlyingAssetsUpdated(uint256 underlyingAssets);\n\n /// @notice Emitted when liquidity is added to the underlying pool\n /// @param wethAmountDesired Amount of WETH desired to be deposited\n /// @param oethAmountDesired Amount of OETH desired to be deposited\n /// @param wethAmountSupplied Amount of WETH deposited\n /// @param oethAmountSupplied Amount of OETH deposited\n /// @param tokenId NFT liquidity token id\n /// @param underlyingAssets Updated amount of underlying assets\n event LiquidityAdded(\n uint256 wethAmountDesired,\n uint256 oethAmountDesired,\n uint256 wethAmountSupplied,\n uint256 oethAmountSupplied,\n uint256 tokenId,\n uint256 underlyingAssets\n ); // 0x1530ec74\n\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n ); // 0x3681e8e0\n\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth); // 0x989e5ca8\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth); // 0xa6737d87\n error OutsideExpectedTickRange(); // 0xa6e1bad2\n error SlippageCheck(uint256 tokenReceived); // 0x355cdb78\n\n /// @notice the constructor\n /// @dev This contract is intended to be used as a proxy. To prevent the\n /// potential confusion of having a functional implementation contract\n /// the constructor has the `initializer` modifier. This way the\n /// `initialize` function can not be called on the implementation contract.\n /// For the same reason the implementation contract also has the governor\n /// set to a zero address.\n /// @param _stratConfig the basic strategy configuration\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _oethAddress Address of the Erc20 OETH Token contract\n /// @param _liquidityManager Address of liquidity manager to add\n /// the liquidity\n /// @param _poolLens Address of the pool lens contract\n /// @param _maverickPosition Address of the Maverick's position contract\n /// @param _maverickQuoter Address of the Maverick's Quoter contract\n /// @param _mPool Address of the Rooster concentrated liquidity pool\n /// @param _upperTickAtParity Bool when true upperTick is the one where the\n /// price of OETH and WETH are at parity\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethAddress,\n address _liquidityManager,\n address _poolLens,\n address _maverickPosition,\n address _maverickQuoter,\n address _mPool,\n bool _upperTickAtParity,\n address _votingDistributor,\n address _poolDistributor\n ) initializer InitializableAbstractStrategy(_stratConfig) {\n require(\n address(IMaverickV2Pool(_mPool).tokenA()) == _wethAddress,\n \"WETH not TokenA\"\n );\n require(\n address(IMaverickV2Pool(_mPool).tokenB()) == _oethAddress,\n \"OETH not TokenB\"\n );\n require(\n _liquidityManager != address(0),\n \"LiquidityManager zero address not allowed\"\n );\n require(\n _maverickQuoter != address(0),\n \"Quoter zero address not allowed\"\n );\n require(_poolLens != address(0), \"PoolLens zero address not allowed\");\n require(\n _maverickPosition != address(0),\n \"Position zero address not allowed\"\n );\n require(\n _votingDistributor != address(0),\n \"Voting distributor zero address not allowed\"\n );\n require(\n _poolDistributor != address(0),\n \"Pool distributor zero address not allowed\"\n );\n\n uint256 _tickSpacing = IMaverickV2Pool(_mPool).tickSpacing();\n require(_tickSpacing == 1, \"Unsupported tickSpacing\");\n\n // tickSpacing == 1\n (sqrtPriceTickLower, sqrtPriceTickHigher) = TickMath.tickSqrtPrices(\n _tickSpacing,\n TICK_NUMBER\n );\n sqrtPriceAtParity = _upperTickAtParity\n ? sqrtPriceTickHigher\n : sqrtPriceTickLower;\n\n WETH = _wethAddress;\n OETH = _oethAddress;\n liquidityManager = IMaverickV2LiquidityManager(_liquidityManager);\n poolLens = IMaverickV2PoolLens(_poolLens);\n maverickPosition = IMaverickV2Position(_maverickPosition);\n quoter = IMaverickV2Quoter(_maverickQuoter);\n mPool = IMaverickV2Pool(_mPool);\n votingDistributor = IVotingDistributor(_votingDistributor);\n poolDistributor = IPoolDistributor(_poolDistributor);\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /**\n * @notice initialize function, to set up initial internal state\n */\n function initialize() external onlyGovernor initializer {\n // Read reward\n address[] memory _rewardTokens = new address[](1);\n _rewardTokens[0] = poolDistributor.rewardToken();\n\n require(_rewardTokens[0] != address(0), \"No reward token configured\");\n\n InitializableAbstractStrategy._initialize(\n _rewardTokens,\n new address[](0),\n new address[](0)\n );\n }\n\n /***************************************\n Configuration \n ****************************************/\n\n /**\n * @notice Set allowed pool weth share interval. After the rebalance happens\n * the share of WETH token in the ticker needs to be within the specifications\n * of the interval.\n *\n * @param _allowedWethShareStart Start of WETH share interval expressed as 18 decimal amount\n * @param _allowedWethShareEnd End of WETH share interval expressed as 18 decimal amount\n */\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external onlyGovernor {\n require(\n _allowedWethShareStart < _allowedWethShareEnd,\n \"Invalid interval\"\n );\n // can not go below 1% weth share\n require(_allowedWethShareStart > 0.01 ether, \"Invalid interval start\");\n // can not go above 95% weth share\n require(_allowedWethShareEnd < 0.95 ether, \"Invalid interval end\");\n\n allowedWethShareStart = _allowedWethShareStart;\n allowedWethShareEnd = _allowedWethShareEnd;\n emit PoolWethShareIntervalUpdated(\n _allowedWethShareStart,\n _allowedWethShareEnd\n );\n }\n\n /***************************************\n Strategy overrides \n ****************************************/\n\n /**\n * @notice Deposits funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposits all the funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n _deposit(WETH, _wethBalance);\n }\n\n /**\n * @dev Deposits funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n * Before this function can be called the initial pool position needs to already\n * be minted.\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n require(tokenId > 0, \"Initial position not minted\");\n emit Deposit(_asset, address(0), _amount);\n\n // if the pool price is not within the expected interval leave the WETH on the contract\n // as to not break the mints - in case it would be configured as a default asset strategy\n (bool _isExpectedRange, ) = _checkForExpectedPoolPrice(false);\n if (_isExpectedRange) {\n // deposit funds into the underlying pool. Because no swap is performed there is no\n // need to remove any of the liquidity beforehand.\n _rebalance(0, false, 0, 0);\n }\n }\n\n /**\n * @notice Withdraw an `amount` of WETH from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset WETH address\n * @param _amount Amount of WETH to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n _ensureWETHBalance(_amount);\n\n _withdraw(_recipient, _amount);\n }\n\n /**\n * @notice Withdraw WETH and sends it to the Vault.\n */\n function withdrawAll() external override onlyVault nonReentrant {\n if (tokenId != 0) {\n _removeLiquidity(1e18);\n }\n\n uint256 _balance = IERC20(WETH).balanceOf(address(this));\n if (_balance > 0) {\n _withdraw(vaultAddress, _balance);\n }\n }\n\n function _withdraw(address _recipient, uint256 _amount) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n * @return bool True when the _asset is WETH\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /**\n * @dev Approve the spending amounts for the assets\n */\n function _approveTokenAmounts(\n uint256 _wethAllowance,\n uint256 _oethAllowance\n ) internal {\n IERC20(WETH).approve(address(liquidityManager), _wethAllowance);\n IERC20(OETH).approve(address(liquidityManager), _oethAllowance);\n }\n\n /***************************************\n Liquidity management\n ****************************************/\n /**\n * @dev Add liquidity into the pool in the pre-configured WETH to OETH share ratios\n * defined by the allowedPoolWethShareStart|End interval.\n *\n * Normally a PoolLens contract is used to prepare the parameters to add liquidity to the\n * Rooster pools. It has some errors when doing those calculation and for that reason a\n * much more accurate Quoter contract is used. This is possible due to our requirement of\n * adding liquidity only to one tick - PoolLens supports adding liquidity into multiple ticks\n * using different distribution ratios.\n */\n function _addLiquidity() internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n // don't deposit small liquidity amounts\n if (_wethBalance <= ACTION_THRESHOLD) {\n return;\n }\n\n (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n ) = _getAddLiquidityParams(_wethBalance, 1e30);\n\n if (OETHRequired > _oethBalance) {\n IVault(vaultAddress).mintForStrategy(OETHRequired - _oethBalance);\n }\n\n _approveTokenAmounts(WETHRequired, OETHRequired);\n\n (\n uint256 _wethAmount,\n uint256 _oethAmount,\n uint32[] memory binIds\n ) = liquidityManager.addPositionLiquidityToSenderByTokenIndex(\n mPool,\n 0, // NFT token index\n packedSqrtPriceBreaks,\n packedArgs\n );\n\n require(binIds.length == 1, \"Unexpected binIds length\");\n\n // burn remaining OETH\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n\n // needs to be called after _updateUnderlyingAssets so the updated amount\n // is reflected in the event\n emit LiquidityAdded(\n _wethBalance, // wethAmountDesired\n OETHRequired, // oethAmountDesired\n _wethAmount, // wethAmountSupplied\n _oethAmount, // oethAmountSupplied\n tokenId, // tokenId\n underlyingAssets\n );\n }\n\n /**\n * @dev The function creates liquidity parameters required to be able to add liquidity to the pool.\n * The function needs to handle the 3 different cases of the way liquidity is added:\n * - only WETH present in the tick\n * - only OETH present in the tick\n * - both tokens present in the tick\n *\n */\n function _getAddLiquidityParams(uint256 _maxWETH, uint256 _maxOETH)\n internal\n returns (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n )\n {\n IMaverickV2Pool.AddLiquidityParams[]\n memory addParams = new IMaverickV2Pool.AddLiquidityParams[](1);\n int32[] memory ticks = new int32[](1);\n uint128[] memory amounts = new uint128[](1);\n ticks[0] = TICK_NUMBER;\n // arbitrary LP amount\n amounts[0] = 1e24;\n\n // construct value for Quoter with arbitrary LP amount\n IMaverickV2Pool.AddLiquidityParams memory addParam = IMaverickV2Pool\n .AddLiquidityParams({\n kind: MAV_STATIC_BIN_KIND,\n ticks: ticks,\n amounts: amounts\n });\n\n // get the WETH and OETH required to get the proportion of tokens required\n // given the arbitrary liquidity\n (WETHRequired, OETHRequired, ) = quoter.calculateAddLiquidity(\n mPool,\n addParam\n );\n\n /**\n * If either token required is 0 then the tick consists only of the other token. In that\n * case the liquidity calculations need to be done using the non 0 token. By setting the\n * tokenRequired from 0 to 1 the `min` in next step will ignore that (the bigger) value.\n */\n WETHRequired = WETHRequired == 0 ? 1 : WETHRequired;\n OETHRequired = OETHRequired == 0 ? 1 : OETHRequired;\n\n addParam.amounts[0] = Math_v5\n .min(\n ((_maxWETH - 1) * 1e24) / WETHRequired,\n ((_maxOETH - 1) * 1e24) / OETHRequired\n )\n .toUint128();\n\n // update the quotes with the actual amounts\n (WETHRequired, OETHRequired, ) = quoter.calculateAddLiquidity(\n mPool,\n addParam\n );\n\n require(_maxWETH >= WETHRequired, \"More WETH required than specified\");\n require(_maxOETH >= OETHRequired, \"More OETH required than specified\");\n\n // organize values to be used by manager\n addParams[0] = addParam;\n packedArgs = liquidityManager.packAddLiquidityArgsArray(addParams);\n // price can stay 0 if array only has one element\n packedSqrtPriceBreaks = liquidityManager.packUint88Array(\n new uint88[](1)\n );\n }\n\n /**\n * @dev Check that the Rooster pool price is within the expected\n * parameters.\n * This function works whether the strategy contract has liquidity\n * position in the pool or not. The function returns _wethSharePct\n * as a gas optimization measure.\n * @param _throwException when set to true the function throws an exception\n * when pool's price is not within expected range.\n * @return _isExpectedRange Bool expressing price is within expected range\n * @return _wethSharePct Share of WETH owned by this strategy contract in the\n * configured ticker.\n */\n function _checkForExpectedPoolPrice(bool _throwException)\n internal\n view\n returns (bool _isExpectedRange, uint256 _wethSharePct)\n {\n require(\n allowedWethShareStart != 0 && allowedWethShareEnd != 0,\n \"Weth share interval not set\"\n );\n\n uint256 _currentPrice = getPoolSqrtPrice();\n\n /**\n * First check pool price is in expected tick range\n *\n * A revert is issued even though price being equal to the lower bound as that can not\n * be within the approved tick range.\n */\n if (\n _currentPrice <= sqrtPriceTickLower ||\n _currentPrice >= sqrtPriceTickHigher\n ) {\n if (_throwException) {\n revert OutsideExpectedTickRange();\n }\n\n return (false, _currentPrice <= sqrtPriceTickLower ? 0 : 1e18);\n }\n\n // 18 decimal number expressed WETH tick share\n _wethSharePct = _getWethShare(_currentPrice);\n\n if (\n _wethSharePct < allowedWethShareStart ||\n _wethSharePct > allowedWethShareEnd\n ) {\n if (_throwException) {\n revert PoolRebalanceOutOfBounds(\n _wethSharePct,\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n return (false, _wethSharePct);\n }\n\n return (true, _wethSharePct);\n }\n\n /**\n * @notice Rebalance the pool to the desired token split and Deposit any WETH on the contract to the\n * underlying rooster pool. Print the required amount of corresponding OETH. After the rebalancing is\n * done burn any potentially remaining OETH tokens still on the strategy contract.\n *\n * This function has a slightly different behaviour depending on the status of the underlying Rooster\n * pool. The function consists of the following 3 steps:\n * 1. withdrawLiquidityOption -> this is a configurable option where either only part of the liquidity\n * necessary for the swap is removed, or all of it. This way the rebalance\n * is able to optimize for volume, for efficiency or anything in between\n * 2. swapToDesiredPosition -> move active trading price in the pool to be able to deposit WETH & OETH\n * tokens with the desired pre-configured ratios\n * 3. addLiquidity -> add liquidity into the pool respecting ratio split configuration\n *\n *\n * Exact _amountToSwap, _swapWeth & _minTokenReceived parameters shall be determined by simulating the\n * transaction off-chain. The strategy checks that after the swap the share of the tokens is in the\n * expected ranges.\n *\n * @param _amountToSwap The amount of the token to swap\n * @param _swapWeth Swap using WETH when true, use OETH when false\n * @param _minTokenReceived Slippage check -> minimum amount of token expected in return\n * @param _liquidityToRemovePct Percentage of liquidity to remove -> the percentage amount of liquidity to\n * remove before performing the swap. 1e18 denominated\n */\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived,\n uint256 _liquidityToRemovePct\n ) external nonReentrant onlyGovernorOrStrategist {\n _rebalance(\n _amountToSwap,\n _swapWeth,\n _minTokenReceived,\n _liquidityToRemovePct\n );\n }\n\n // slither-disable-start reentrancy-no-eth\n function _rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived,\n uint256 _liquidityToRemovePct\n ) internal {\n // Remove the required amount of liquidity\n if (_liquidityToRemovePct > 0) {\n _removeLiquidity(_liquidityToRemovePct);\n }\n\n // in some cases (e.g. deposits) we will just want to add liquidity and not\n // issue a swap to move the active trading position within the pool. Before or after a\n // deposit or as a standalone call the strategist might issue a rebalance to move the\n // active trading price to a more desired position.\n if (_amountToSwap > 0) {\n // In case liquidity has been removed and there is still not enough WETH owned by the\n // strategy contract remove additional required amount of WETH.\n if (_swapWeth) _ensureWETHBalance(_amountToSwap);\n\n _swapToDesiredPosition(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n\n // calling check liquidity early so we don't get unexpected errors when adding liquidity\n // in the later stages of this function\n _checkForExpectedPoolPrice(true);\n\n _addLiquidity();\n\n // this call shouldn't be necessary, since adding liquidity shouldn't affect the active\n // trading price. It is a defensive programming measure.\n (, uint256 _wethSharePct) = _checkForExpectedPoolPrice(true);\n\n // revert if protocol insolvent\n _solvencyAssert();\n\n emit PoolRebalanced(_wethSharePct);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @dev Perform a swap so that after the swap the tick has the desired WETH to OETH token share.\n */\n function _swapToDesiredPosition(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n IERC20 _tokenToSwap = IERC20(_swapWeth ? WETH : OETH);\n uint256 _balance = _tokenToSwap.balanceOf(address(this));\n\n if (_balance < _amountToSwap) {\n // This should never trigger since _ensureWETHBalance will already\n // throw an error if there is not enough WETH\n if (_swapWeth) {\n revert NotEnoughWethForSwap(_balance, _amountToSwap);\n }\n // if swapping OETH\n uint256 mintForSwap = _amountToSwap - _balance;\n IVault(vaultAddress).mintForStrategy(mintForSwap);\n }\n\n // SafeERC20 is used for IERC20 transfers. Not sure why slither complains\n // slither-disable-next-line unchecked-transfer\n _tokenToSwap.transfer(address(mPool), _amountToSwap);\n\n // tickLimit: the furthest tick a swap will execute in. If no limit is desired,\n // value should be set to type(int32).max for a tokenAIn (WETH) swap\n // and type(int32).min for a swap where tokenB (OETH) is the input\n\n IMaverickV2Pool.SwapParams memory swapParams = IMaverickV2Pool\n // exactOutput defines whether the amount specified is the output\n // or the input amount of the swap\n .SwapParams({\n amount: _amountToSwap,\n tokenAIn: _swapWeth,\n exactOutput: false,\n tickLimit: TICK_NUMBER\n });\n\n // swaps without a callback as the assets are already sent to the pool\n (, uint256 amountOut) = mPool.swap(\n address(this),\n swapParams,\n bytes(\"\")\n );\n\n /**\n * There could be additional checks here for validating minTokenReceived is within the\n * expected range (e.g. 99% - 101% of the token sent in). Though that doesn't provide\n * any additional security. After the swap the `_checkForExpectedPoolPrice` validates\n * that the swap has moved the price into the expected tick (# -1).\n *\n * If the guardian forgets to set a `_minTokenReceived` and a sandwich attack bends\n * the pool before the swap the `_checkForExpectedPoolPrice` will fail the transaction.\n *\n * A check would not prevent a compromised guardian from stealing funds as multiple\n * transactions each loosing smaller amount of funds are still possible.\n */\n if (amountOut < _minTokenReceived) {\n revert SlippageCheck(amountOut);\n }\n\n /**\n * In the interest of each function in `_rebalance` to leave the contract state as\n * clean as possible the OETH tokens here are burned. This decreases the\n * dependence where `_swapToDesiredPosition` function relies on later functions\n * (`addLiquidity`) to burn the OETH. Reducing the risk of error introduction.\n */\n _burnOethOnTheContract();\n }\n\n /**\n * @dev This function removes the appropriate amount of liquidity to ensure that the required\n * amount of WETH is available on the contract\n *\n * @param _amount WETH balance required on the contract\n */\n function _ensureWETHBalance(uint256 _amount) internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance >= _amount) {\n return;\n }\n\n require(tokenId != 0, \"No liquidity available\");\n uint256 _additionalWethRequired = _amount - _wethBalance;\n (uint256 _wethInThePool, ) = getPositionPrincipal();\n\n if (_wethInThePool < _additionalWethRequired) {\n revert NotEnoughWethLiquidity(\n _wethInThePool,\n _additionalWethRequired\n );\n }\n\n uint256 shareOfWethToRemove = _wethInThePool <= 1\n ? 1e18\n : Math_v5.min(\n /**\n * When dealing with shares of liquidity to remove there is always some\n * rounding involved. After extensive fuzz testing the below approach\n * yielded the best results where the strategy overdraws the least and\n * never removes insufficient amount of WETH.\n */\n (_additionalWethRequired + 2).divPrecisely(_wethInThePool - 1) +\n 2,\n 1e18\n );\n\n _removeLiquidity(shareOfWethToRemove);\n }\n\n /**\n * @dev Decrease partial or all liquidity from the pool.\n * @param _liquidityToDecrease The amount of liquidity to remove denominated in 1e18\n */\n function _removeLiquidity(uint256 _liquidityToDecrease) internal {\n require(_liquidityToDecrease > 0, \"Must remove some liquidity\");\n require(\n _liquidityToDecrease <= 1e18,\n \"Can not remove more than 100% of liquidity\"\n );\n\n // 0 indicates the first (and only) bin in the NFT LP position.\n IMaverickV2Pool.RemoveLiquidityParams memory params = maverickPosition\n .getRemoveParams(tokenId, 0, _liquidityToDecrease);\n (uint256 _amountWeth, uint256 _amountOeth) = maverickPosition\n .removeLiquidityToSender(tokenId, mPool, params);\n\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n\n // needs to be called after the _updateUnderlyingAssets so the updated amount is reflected\n // in the event\n emit LiquidityRemoved(\n _liquidityToDecrease,\n _amountWeth,\n _amountOeth,\n underlyingAssets\n );\n }\n\n /**\n * @dev Burns any OETH tokens remaining on the strategy contract if the balance is\n * above the action threshold.\n */\n function _burnOethOnTheContract() internal {\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(_oethBalance);\n }\n\n /**\n * @notice Returns the percentage of WETH liquidity in the configured ticker\n * owned by this strategy contract.\n * @return uint256 1e18 denominated percentage expressing the share\n */\n function getWETHShare() external view returns (uint256) {\n uint256 _currentPrice = getPoolSqrtPrice();\n return _getWethShare(_currentPrice);\n }\n\n /**\n * @dev Returns the share of WETH in tick denominated in 1e18\n */\n function _getWethShare(uint256 _currentPrice)\n internal\n view\n returns (uint256)\n {\n (\n uint256 wethAmount,\n uint256 oethAmount\n ) = _reservesInTickForGivenPriceAndLiquidity(\n sqrtPriceTickLower,\n sqrtPriceTickHigher,\n _currentPrice,\n 1e24\n );\n\n return wethAmount.divPrecisely(wethAmount + oethAmount);\n }\n\n /**\n * @notice Returns the current pool price in square root\n * @return Square root of the pool price\n */\n function getPoolSqrtPrice() public view returns (uint256) {\n return poolLens.getPoolSqrtPrice(mPool);\n }\n\n /**\n * @notice Returns the current active trading tick of the underlying pool\n * @return _currentTick Current pool trading tick\n */\n function getCurrentTradingTick() public view returns (int32 _currentTick) {\n _currentTick = mPool.getState().activeTick;\n }\n\n /**\n * @notice Mint the initial NFT position\n *\n * @dev This amount is \"gifted\" to the strategy contract and will count as a yield\n * surplus.\n */\n // slither-disable-start reentrancy-no-eth\n function mintInitialPosition() external onlyGovernor nonReentrant {\n require(tokenId == 0, \"Initial position already minted\");\n (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n ) = _getAddLiquidityParams(1e16, 1e16);\n\n // Mint rounded up OETH amount\n if (OETHRequired > 0) {\n IVault(vaultAddress).mintForStrategy(OETHRequired);\n }\n\n _approveTokenAmounts(WETHRequired, OETHRequired);\n\n // Store the tokenId before calling updateUnderlyingAssets as it relies on the tokenId\n // not being 0\n (, , , tokenId) = liquidityManager.mintPositionNftToSender(\n mPool,\n packedSqrtPriceBreaks,\n packedArgs\n );\n\n // burn remaining OETH\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @notice Returns the balance of tokens the strategy holds in the LP position\n * @return _amountWeth Amount of WETH in position\n * @return _amountOeth Amount of OETH in position\n */\n function getPositionPrincipal()\n public\n view\n returns (uint256 _amountWeth, uint256 _amountOeth)\n {\n if (tokenId == 0) {\n return (0, 0);\n }\n\n (_amountWeth, _amountOeth, ) = _getPositionInformation();\n }\n\n /**\n * @dev Returns the balance of tokens the strategy holds in the LP position\n * @return _amountWeth Amount of WETH in position\n * @return _amountOeth Amount of OETH in position\n * @return liquidity Amount of liquidity in the position\n */\n function _getPositionInformation()\n internal\n view\n returns (\n uint256 _amountWeth,\n uint256 _amountOeth,\n uint256 liquidity\n )\n {\n IMaverickV2Position.PositionFullInformation\n memory positionInfo = maverickPosition.tokenIdPositionInformation(\n tokenId,\n 0\n );\n\n require(\n positionInfo.liquidities.length == 1,\n \"Unexpected liquidities length\"\n );\n require(positionInfo.ticks.length == 1, \"Unexpected ticks length\");\n\n _amountWeth = positionInfo.amountA;\n _amountOeth = positionInfo.amountB;\n liquidity = positionInfo.liquidities[0];\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99.8%) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethSupply = IERC20(OETH).totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethSupply) < SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /**\n * @dev Collect Rooster reward token, and send it to the harvesterAddress\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Do nothing if there's no position minted\n if (tokenId > 0) {\n uint32[] memory binIds = new uint32[](1);\n IMaverickV2Pool.TickState memory tickState = mPool.getTick(\n TICK_NUMBER\n );\n // get the binId for the MAV_STATIC_BIN_KIND in tick TICK_NUMBER (-1)\n binIds[0] = tickState.binIdsByTick[0];\n\n uint256 lastEpoch = votingDistributor.lastEpoch();\n\n poolDistributor.claimLp(\n address(this),\n tokenId,\n mPool,\n binIds,\n lastEpoch\n );\n }\n\n // Run the internal inherited function\n _collectRewardTokens();\n }\n\n /***************************************\n Balances and Fees\n ****************************************/\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256)\n {\n require(_asset == WETH, \"Only WETH supported\");\n\n // because of PoolLens inaccuracy there is usually some dust WETH left on the contract\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n // just paranoia check, in case there is OETH in the strategy that for some reason hasn't\n // been burned yet. This should always be 0.\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n return underlyingAssets + _wethBalance + _oethBalance;\n }\n\n /// @dev This function updates the amount of underlying assets with the approach of the least possible\n /// total tokens extracted for the current liquidity in the pool.\n function _updateUnderlyingAssets() internal {\n /**\n * Our net value represent the smallest amount of tokens we are able to extract from the position\n * given our liquidity.\n *\n * The least amount of tokens ex-tractable from the position is where the active trading price is\n * at the edge between tick -1 & tick 0. There the pool is offering 1:1 trades between WETH & OETH.\n * At that moment the pool consists completely of WETH and no OETH.\n *\n * The more swaps from OETH -> WETH happen on the pool the more the price starts to move away from the tick 0\n * towards the middle of tick -1 making OETH (priced in WETH) cheaper.\n */\n\n uint256 _wethAmount = tokenId == 0 ? 0 : _balanceInPosition();\n\n underlyingAssets = _wethAmount;\n emit UnderlyingAssetsUpdated(_wethAmount);\n }\n\n /**\n * @dev Strategy reserves (which consist only of WETH in case of Rooster - Plume pool)\n * when the tick price is closest to parity - assuring the lowest amount of tokens\n * returned for the current position liquidity.\n */\n function _balanceInPosition() internal view returns (uint256 _wethBalance) {\n (, , uint256 liquidity) = _getPositionInformation();\n\n uint256 _oethBalance;\n\n (_wethBalance, _oethBalance) = _reservesInTickForGivenPriceAndLiquidity(\n sqrtPriceTickLower,\n sqrtPriceTickHigher,\n sqrtPriceAtParity,\n liquidity\n );\n\n require(_oethBalance == 0, \"Non zero oethBalance\");\n }\n\n /**\n * @notice Tick dominance denominated in 1e18\n * @return _tickDominance The share of liquidity in TICK_NUMBER tick owned\n * by the strategy contract denominated in 1e18\n */\n function tickDominance() public view returns (uint256 _tickDominance) {\n IMaverickV2Pool.TickState memory tickState = mPool.getTick(TICK_NUMBER);\n\n uint256 wethReserve = tickState.reserveA;\n uint256 oethReserve = tickState.reserveB;\n\n // prettier-ignore\n (uint256 _amountWeth, uint256 _amountOeth, ) = _getPositionInformation();\n\n if (wethReserve + oethReserve == 0) {\n return 0;\n }\n\n _tickDominance = (_amountWeth + _amountOeth).divPrecisely(\n wethReserve + oethReserve\n );\n }\n\n /***************************************\n Hidden functions\n ****************************************/\n /**\n * @dev Unsupported\n */\n function setPTokenAddress(address, address) external pure override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function removePToken(uint256) external pure override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function _abstractSetPToken(address, address) internal pure override {\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function safeApproveAllTokens() external pure override {\n // all the amounts are approved at the time required\n revert(\"Unsupported method\");\n }\n\n /***************************************\n Maverick liquidity utilities\n ****************************************/\n\n /// @notice Calculates deltaA = liquidity * (sqrt(upper) - sqrt(lower))\n /// Calculates deltaB = liquidity / sqrt(lower) - liquidity / sqrt(upper),\n /// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower))\n ///\n /// @dev refactored from here:\n // solhint-disable-next-line max-line-length\n /// https://github.com/rooster-protocol/rooster-contracts/blob/main/v2-supplemental/contracts/libraries/LiquidityUtilities.sol#L665-L695\n function _reservesInTickForGivenPriceAndLiquidity(\n uint256 _lowerSqrtPrice,\n uint256 _upperSqrtPrice,\n uint256 _newSqrtPrice,\n uint256 _liquidity\n ) internal pure returns (uint128 reserveA, uint128 reserveB) {\n if (_liquidity == 0) {\n (reserveA, reserveB) = (0, 0);\n } else {\n uint256 lowerEdge = MathRooster.max(_lowerSqrtPrice, _newSqrtPrice);\n\n reserveA = MathRooster\n .mulCeil(\n _liquidity,\n MathRooster.clip(\n MathRooster.min(_upperSqrtPrice, _newSqrtPrice),\n _lowerSqrtPrice\n )\n )\n .toUint128();\n reserveB = MathRooster\n .mulDivCeil(\n _liquidity,\n 1e18 * MathRooster.clip(_upperSqrtPrice, lowerEdge),\n _upperSqrtPrice * lowerEdge\n )\n .toUint128();\n }\n }\n}\n" + }, + "contracts/strategies/sonic/SonicStakingStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SonicValidatorDelegator } from \"./SonicValidatorDelegator.sol\";\nimport { IWrappedSonic } from \"../../interfaces/sonic/IWrappedSonic.sol\";\n\n/**\n * @title Staking Strategy for Sonic's native S currency\n * @author Origin Protocol Inc\n */\ncontract SonicStakingStrategy is SonicValidatorDelegator {\n // For future use\n uint256[50] private __gap;\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wrappedSonic,\n address _sfc\n ) SonicValidatorDelegator(_baseConfig, _wrappedSonic, _sfc) {}\n\n /// @notice Deposit wrapped S asset into the underlying platform.\n /// @param _asset Address of asset to deposit. Has to be Wrapped Sonic (wS).\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposit Wrapped Sonic (wS) to this strategy and delegate to a validator.\n * @param _asset Address of Wrapped Sonic (wS) token\n * @param _amount Amount of Wrapped Sonic (wS) to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal virtual {\n require(_amount > 0, \"Must deposit something\");\n\n _delegate(_amount);\n emit Deposit(_asset, address(0), _amount);\n }\n\n /**\n * @notice Deposit the entire balance of wrapped S in this strategy contract into\n * the underlying platform.\n */\n function depositAll() external virtual override onlyVault nonReentrant {\n uint256 wSBalance = IERC20(wrappedSonic).balanceOf(address(this));\n\n if (wSBalance > 0) {\n _deposit(wrappedSonic, wSBalance);\n }\n }\n\n /// @notice Withdraw Wrapped Sonic (wS) from this strategy contract.\n /// Used only if some wS is lingering on the contract.\n /// That can happen only when someone sends wS directly to this contract\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset Address of the Wrapped Sonic (wS) token\n /// @param _amount Amount of Wrapped Sonic (wS) to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal override {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(_asset).transfer(_recipient, _amount);\n\n emit Withdrawal(wrappedSonic, address(0), _amount);\n }\n\n /// @notice Transfer all Wrapped Sonic (wS) deposits back to the vault.\n /// This does not withdraw from delegated validators. That has to be done separately with `undelegate`.\n /// Any native S in this strategy will be withdrawn.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 balance = address(this).balance;\n if (balance > 0) {\n IWrappedSonic(wrappedSonic).deposit{ value: balance }();\n }\n uint256 wSBalance = IERC20(wrappedSonic).balanceOf(address(this));\n if (wSBalance > 0) {\n _withdraw(vaultAddress, wrappedSonic, wSBalance);\n }\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset token\n */\n function supportsAsset(address _asset)\n public\n view\n virtual\n override\n returns (bool)\n {\n return _asset == wrappedSonic;\n }\n\n /**\n * @notice is not supported for this strategy as the\n * Wrapped Sonic (wS) token is set at deploy time.\n */\n function setPTokenAddress(address, address)\n external\n view\n override\n onlyGovernor\n {\n revert(\"unsupported function\");\n }\n\n /// @notice is not used by this strategy as all staking rewards are restaked\n function collectRewardTokens() external override nonReentrant {\n revert(\"unsupported function\");\n }\n\n /**\n * @notice is not supported for this strategy as the\n * Wrapped Sonic (wS) token is set at deploy time.\n */\n function removePToken(uint256) external view override onlyGovernor {\n revert(\"unsupported function\");\n }\n\n /// @dev is not used by this strategy but must be implemented as it's abstract\n /// in the inherited `InitializableAbstractStrategy` contract.\n function _abstractSetPToken(address, address) internal virtual override {}\n\n /// @notice is not used by this strategy\n function safeApproveAllTokens() external override onlyGovernor {}\n}\n" + }, + "contracts/strategies/sonic/SonicSwapXAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title SwapX Algorithmic Market Maker (AMO) Strategy\n * @notice AMO strategy for the SwapX OS/wS stable pool\n * @author Origin Protocol Inc\n */\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport { sqrt } from \"../../utils/PRBMath.sol\";\nimport { IBasicToken } from \"../../interfaces/IBasicToken.sol\";\nimport { IPair } from \"../../interfaces/sonic/ISwapXPair.sol\";\nimport { IGauge } from \"../../interfaces/sonic/ISwapXGauge.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\n\ncontract SonicSwapXAMOStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @notice a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n /// @notice Precision for the SwapX Stable AMM (sAMM) invariant k.\n uint256 public constant PRECISION = 1e18;\n\n /// @notice Address of the Wrapped S (wS) token.\n address public immutable ws;\n\n /// @notice Address of the OS token contract.\n address public immutable os;\n\n /// @notice Address of the SwapX Stable pool contract.\n address public immutable pool;\n\n /// @notice Address of the SwapX Gauge contract.\n address public immutable gauge;\n\n /// @notice The max amount the OS/wS price can deviate from peg (1e18)\n /// before deposits are reverted scaled to 18 decimals.\n /// eg 0.01e18 or 1e16 is 1% which is 100 basis points.\n /// This is the amount below and above peg so a 50 basis point deviation (0.005e18)\n /// allows a price range from 0.995 to 1.005.\n uint256 public maxDepeg;\n\n event SwapOTokensToPool(\n uint256 osMinted,\n uint256 wsDepositAmount,\n uint256 osDepositAmount,\n uint256 lpTokens\n );\n event SwapAssetsToPool(\n uint256 wsSwapped,\n uint256 lpTokens,\n uint256 osBurnt\n );\n event MaxDepegUpdated(uint256 maxDepeg);\n\n /**\n * @dev Verifies that the caller is the Strategist of the Vault.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Skim the SwapX pool in case any extra wS or OS tokens were added\n */\n modifier skimPool() {\n IPair(pool).skim(address(this));\n _;\n }\n\n /**\n * @dev Checks the pool is balanced enough to allow deposits.\n */\n modifier nearBalancedPool() {\n // OS/wS price = wS / OS\n // Get the OS/wS price for selling 1 OS for wS\n // As OS is 1, the wS amount is the OS/wS price\n uint256 sellPrice = IPair(pool).getAmountOut(1e18, os);\n\n // Get the amount of OS received from selling 1 wS. This is buying OS.\n uint256 osAmount = IPair(pool).getAmountOut(1e18, ws);\n // Convert to a OS/wS price = wS / OS\n uint256 buyPrice = 1e36 / osAmount;\n\n uint256 pegPrice = 1e18;\n\n require(\n sellPrice >= pegPrice - maxDepeg && buyPrice <= pegPrice + maxDepeg,\n \"price out of range\"\n );\n _;\n }\n\n /**\n * @dev Checks the pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do swaps against the pool.\n * Deposits and withdrawals are proportional to the pool's balances hence don't need this check.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the pool\n (uint256 wsReservesBefore, uint256 osReservesBefore, ) = IPair(pool)\n .getReserves();\n // diff = wS balance - OS balance\n int256 diffBefore = wsReservesBefore.toInt256() -\n osReservesBefore.toInt256();\n\n _;\n\n // Get the asset and OToken balances in the pool\n (uint256 wsReservesAfter, uint256 osReservesAfter, ) = IPair(pool)\n .getReserves();\n // diff = wS balance - OS balance\n int256 diffAfter = wsReservesAfter.toInt256() -\n osReservesAfter.toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OS, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"Assets overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of wS, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"OTokens overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n /**\n * @param _baseConfig The `platformAddress` is the address of the SwapX pool.\n * The `vaultAddress` is the address of the Origin Sonic Vault.\n * @param _os Address of the OS token.\n * @param _ws Address of the Wrapped S (wS) token.\n * @param _gauge Address of the SwapX gauge for the pool.\n */\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _os,\n address _ws,\n address _gauge\n ) InitializableAbstractStrategy(_baseConfig) {\n // Check the pool tokens are correct\n require(\n IPair(_baseConfig.platformAddress).token0() == _ws &&\n IPair(_baseConfig.platformAddress).token1() == _os,\n \"Incorrect pool tokens\"\n );\n // Checked both tokens are to 18 decimals\n require(\n IBasicToken(_ws).decimals() == 18 &&\n IBasicToken(_os).decimals() == 18,\n \"Incorrect token decimals\"\n );\n // Check the SwapX pool is a Stable AMM (sAMM)\n require(\n IPair(_baseConfig.platformAddress).isStable() == true,\n \"Pool not stable\"\n );\n // Check the gauge is for the pool\n require(\n IGauge(_gauge).TOKEN() == _baseConfig.platformAddress,\n \"Incorrect gauge\"\n );\n\n // Set the immutable variables\n os = _os;\n ws = _ws;\n pool = _baseConfig.platformAddress;\n gauge = _gauge;\n\n // This is an implementation contract. The governor is set in the proxy contract.\n _setGovernor(address(0));\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as SwapX strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Array containing SWPx token address\n * @param _maxDepeg The max amount the OS/wS price can deviate from peg (1e18) before deposits are reverted.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n uint256 _maxDepeg\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = pool;\n\n address[] memory _assets = new address[](1);\n _assets[0] = ws;\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n maxDepeg = _maxDepeg;\n\n _approveBase();\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit an amount of Wrapped S (wS) into the SwapX pool.\n * Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @dev This tx must be wrapped by the VaultValueChecker.\n * To minimize loses, the pool should be rebalanced before depositing.\n * The pool's OS/wS price must be within the maxDepeg range.\n * @param _asset Address of Wrapped S (wS) token.\n * @param _wsAmount Amount of Wrapped S (wS) tokens to deposit.\n */\n function deposit(address _asset, uint256 _wsAmount)\n external\n override\n onlyVault\n nonReentrant\n skimPool\n nearBalancedPool\n {\n require(_asset == ws, \"Unsupported asset\");\n require(_wsAmount > 0, \"Must deposit something\");\n\n (uint256 osDepositAmount, ) = _deposit(_wsAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the deposited wS tokens\n emit Deposit(ws, pool, _wsAmount);\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osDepositAmount);\n }\n\n /**\n * @notice Deposit all the strategy's Wrapped S (wS) tokens into the SwapX pool.\n * Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @dev This tx must be wrapped by the VaultValueChecker.\n * To minimize loses, the pool should be rebalanced before depositing.\n * The pool's OS/wS price must be within the maxDepeg range.\n */\n function depositAll()\n external\n override\n onlyVault\n nonReentrant\n skimPool\n nearBalancedPool\n {\n uint256 wsBalance = IERC20(ws).balanceOf(address(this));\n if (wsBalance > 0) {\n (uint256 osDepositAmount, ) = _deposit(wsBalance);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the deposited wS tokens\n emit Deposit(ws, pool, wsBalance);\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osDepositAmount);\n }\n }\n\n /**\n * @dev Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @param _wsAmount Amount of Wrapped S (wS) tokens to deposit.\n * @return osDepositAmount Amount of OS tokens minted and deposited into the pool.\n * @return lpTokens Amount of SwapX pool LP tokens minted and deposited into the gauge.\n */\n function _deposit(uint256 _wsAmount)\n internal\n returns (uint256 osDepositAmount, uint256 lpTokens)\n {\n // Calculate the required amount of OS to mint based on the wS amount.\n osDepositAmount = _calcTokensToMint(_wsAmount);\n\n // Mint the required OS tokens to this strategy\n IVault(vaultAddress).mintForStrategy(osDepositAmount);\n\n // Add wS and OS liquidity to the pool and stake in gauge\n lpTokens = _depositToPoolAndGauge(_wsAmount, osDepositAmount);\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw wS and OS from the SwapX pool, burn the OS,\n * and transfer the wS to the recipient.\n * @param _recipient Address of the Vault.\n * @param _asset Address of the Wrapped S (wS) contract.\n * @param _wsAmount Amount of Wrapped S (wS) to withdraw.\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _wsAmount\n ) external override onlyVault nonReentrant skimPool {\n require(_wsAmount > 0, \"Must withdraw something\");\n require(_asset == ws, \"Unsupported asset\");\n // This strategy can't be set as a default strategy for wS in the Vault.\n // This means the recipient must always be the Vault.\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n // Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n uint256 lpTokens = _calcTokensToBurn(_wsAmount);\n\n // Withdraw pool LP tokens from the gauge and remove assets from from the pool\n _withdrawFromGaugeAndPool(lpTokens);\n\n // Burn all the removed OS and any that was left in the strategy\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Transfer wS to the recipient\n // Note there can be a dust amount of wS left in the strategy as\n // the burn of the pool's LP tokens is rounded up\n require(\n IERC20(ws).balanceOf(address(this)) >= _wsAmount,\n \"Not enough wS removed from pool\"\n );\n IERC20(ws).safeTransfer(_recipient, _wsAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the withdrawn wS tokens\n emit Withdrawal(ws, pool, _wsAmount);\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n }\n\n /**\n * @notice Withdraw all pool LP tokens from the gauge,\n * remove all wS and OS from the SwapX pool,\n * burn all the OS tokens,\n * and transfer all the wS to the Vault contract.\n * @dev There is no solvency check here as withdrawAll can be called to\n * quickly secure assets to the Vault in emergencies.\n */\n function withdrawAll()\n external\n override\n onlyVaultOrGovernor\n nonReentrant\n skimPool\n {\n // Get all the pool LP tokens the strategy has staked in the gauge\n uint256 lpTokens = IGauge(gauge).balanceOf(address(this));\n // Can not withdraw zero LP tokens from the gauge\n if (lpTokens == 0) return;\n\n if (IGauge(gauge).emergency()) {\n // The gauge is in emergency mode\n _emergencyWithdrawFromGaugeAndPool();\n } else {\n // Withdraw pool LP tokens from the gauge and remove assets from from the pool\n _withdrawFromGaugeAndPool(lpTokens);\n }\n\n // Burn all OS in this strategy contract\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Get the strategy contract's wS balance.\n // This includes all that was removed from the SwapX pool and\n // any that was sitting in the strategy contract before the removal.\n uint256 wsBalance = IERC20(ws).balanceOf(address(this));\n IERC20(ws).safeTransfer(vaultAddress, wsBalance);\n\n // Emit event for the withdrawn wS tokens\n emit Withdrawal(ws, pool, wsBalance);\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n }\n\n /***************************************\n Pool Rebalancing\n ****************************************/\n\n /** @notice Used when there is more OS than wS in the pool.\n * wS and OS is removed from the pool, the received wS is swapped for OS\n * and the left over OS in the strategy is burnt.\n * The OS/wS price is < 1.0 so OS is being bought at a discount.\n * @param _wsAmount Amount of Wrapped S (wS) to swap into the pool.\n */\n function swapAssetsToPool(uint256 _wsAmount)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n skimPool\n {\n require(_wsAmount > 0, \"Must swap something\");\n\n // 1. Partially remove liquidity so there’s enough wS for the swap\n\n // Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n uint256 lpTokens = _calcTokensToBurn(_wsAmount);\n require(lpTokens > 0, \"No LP tokens to burn\");\n\n _withdrawFromGaugeAndPool(lpTokens);\n\n // 2. Swap wS for OS against the pool\n // Swap exact amount of wS for OS against the pool\n // There can be a dust amount of wS left in the strategy as the burn of the pool's LP tokens is rounded up\n _swapExactTokensForTokens(_wsAmount, ws, os);\n\n // 3. Burn all the OS left in the strategy from the remove liquidity and swap\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n // Emit event for the swap\n emit SwapAssetsToPool(_wsAmount, lpTokens, osToBurn);\n }\n\n /**\n * @notice Used when there is more wS than OS in the pool.\n * OS is minted and swapped for wS against the pool,\n * more OS is minted and added back into the pool with the swapped out wS.\n * The OS/wS price is > 1.0 so OS is being sold at a premium.\n * @param _osAmount Amount of OS to swap into the pool.\n */\n function swapOTokensToPool(uint256 _osAmount)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n skimPool\n {\n require(_osAmount > 0, \"Must swap something\");\n\n // 1. Mint OS so it can be swapped into the pool\n\n // There can be OS in the strategy from skimming the pool\n uint256 osInStrategy = IERC20(os).balanceOf(address(this));\n require(_osAmount >= osInStrategy, \"Too much OS in strategy\");\n uint256 osToMint = _osAmount - osInStrategy;\n\n // Mint the required OS tokens to this strategy\n IVault(vaultAddress).mintForStrategy(osToMint);\n\n // 2. Swap OS for wS against the pool\n _swapExactTokensForTokens(_osAmount, os, ws);\n\n // The wS is from the swap and any wS that was sitting in the strategy\n uint256 wsDepositAmount = IERC20(ws).balanceOf(address(this));\n\n // 3. Add wS and OS back to the pool in proportion to the pool's reserves\n (uint256 osDepositAmount, uint256 lpTokens) = _deposit(wsDepositAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osToMint + osDepositAmount);\n // Emit event for the swap\n emit SwapOTokensToPool(\n osToMint,\n wsDepositAmount,\n osDepositAmount,\n lpTokens\n );\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Get the wS value of assets in the strategy and SwapX pool.\n * The value of the assets in the pool is calculated assuming the pool is balanced.\n * This way the value can not be manipulated by changing the pool's token balances.\n * @param _asset Address of the Wrapped S (wS) token\n * @return balance Total value in wS.\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == ws, \"Unsupported asset\");\n\n // wS balance needed here for the balance check that happens from vault during depositing.\n balance = IERC20(ws).balanceOf(address(this));\n\n // This assumes 1 gauge LP token = 1 pool LP token\n uint256 lpTokens = IGauge(gauge).balanceOf(address(this));\n if (lpTokens == 0) return balance;\n\n // Add the strategy’s share of the wS and OS tokens in the SwapX pool if the pool was balanced.\n balance += _lpValue(lpTokens);\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == ws;\n }\n\n /**\n * @notice Collect accumulated SWPx (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect SWPx rewards from the gauge\n IGauge(gauge).getReward();\n\n _collectRewardTokens();\n }\n\n /***************************************\n Internal SwapX Pool and Gauge Functions\n ****************************************/\n\n /**\n * @dev Calculate the required amount of OS to mint based on the wS amount.\n * This ensures the proportion of OS tokens being added to the pool matches the proportion of wS tokens.\n * For example, if the added wS tokens is 10% of existing wS tokens in the pool,\n * then the OS tokens being added should also be 10% of the OS tokens in the pool.\n * @param _wsAmount Amount of Wrapped S (wS) to be added to the pool.\n * @return osAmount Amount of OS to be minted and added to the pool.\n */\n function _calcTokensToMint(uint256 _wsAmount)\n internal\n view\n returns (uint256 osAmount)\n {\n (uint256 wsReserves, uint256 osReserves, ) = IPair(pool).getReserves();\n require(wsReserves > 0, \"Empty pool\");\n\n // OS to add = (wS being added * OS in pool) / wS in pool\n osAmount = (_wsAmount * osReserves) / wsReserves;\n }\n\n /**\n * @dev Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n * from the pool.\n * @param _wsAmount Amount of Wrapped S (wS) to be removed from the pool.\n * @return lpTokens Amount of SwapX pool LP tokens to burn.\n */\n function _calcTokensToBurn(uint256 _wsAmount)\n internal\n view\n returns (uint256 lpTokens)\n {\n /* The SwapX pool proportionally returns the reserve tokens when removing liquidity.\n * First, calculate the proportion of required wS tokens against the pools wS reserves.\n * That same proportion is used to calculate the required amount of pool LP tokens.\n * For example, if the required wS tokens is 10% of the pool's wS reserves,\n * then 10% of the pool's LP supply needs to be burned.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognizant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on, the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n (uint256 wsReserves, , ) = IPair(pool).getReserves();\n require(wsReserves > 0, \"Empty pool\");\n\n lpTokens = (_wsAmount * IPair(pool).totalSupply()) / wsReserves;\n lpTokens += 1; // Add 1 to ensure we get enough LP tokens with rounding\n }\n\n /**\n * @dev Deposit Wrapped S (wS) and OS liquidity to the SwapX pool\n * and stake the pool's LP token in the gauge.\n * @param _wsAmount Amount of Wrapped S (wS) to deposit.\n * @param _osAmount Amount of OS to deposit.\n * @return lpTokens Amount of SwapX pool LP tokens minted.\n */\n function _depositToPoolAndGauge(uint256 _wsAmount, uint256 _osAmount)\n internal\n returns (uint256 lpTokens)\n {\n // Transfer wS to the pool\n IERC20(ws).safeTransfer(pool, _wsAmount);\n // Transfer OS to the pool\n IERC20(os).safeTransfer(pool, _osAmount);\n\n // Mint LP tokens from the pool\n lpTokens = IPair(pool).mint(address(this));\n\n // Deposit the pool's LP tokens into the gauge\n IGauge(gauge).deposit(lpTokens);\n }\n\n /**\n * @dev Withdraw pool LP tokens from the gauge and remove wS and OS from the pool.\n * @param _lpTokens Amount of SwapX pool LP tokens to withdraw from the gauge\n */\n function _withdrawFromGaugeAndPool(uint256 _lpTokens) internal {\n require(\n IGauge(gauge).balanceOf(address(this)) >= _lpTokens,\n \"Not enough LP tokens in gauge\"\n );\n\n // Withdraw pool LP tokens from the gauge\n IGauge(gauge).withdraw(_lpTokens);\n\n // Transfer the pool LP tokens to the pool\n IERC20(pool).safeTransfer(pool, _lpTokens);\n\n // Burn the LP tokens and transfer the wS and OS back to the strategy\n IPair(pool).burn(address(this));\n }\n\n /**\n * @dev Withdraw all pool LP tokens from the gauge when it's in emergency mode\n * and remove wS and OS from the pool.\n */\n function _emergencyWithdrawFromGaugeAndPool() internal {\n // Withdraw all pool LP tokens from the gauge\n IGauge(gauge).emergencyWithdraw();\n\n // Get the pool LP tokens in strategy\n uint256 _lpTokens = IERC20(pool).balanceOf(address(this));\n\n // Transfer the pool LP tokens to the pool\n IERC20(pool).safeTransfer(pool, _lpTokens);\n\n // Burn the LP tokens and transfer the wS and OS back to the strategy\n IPair(pool).burn(address(this));\n }\n\n /**\n * @dev Swap exact amount of tokens for another token against the pool.\n * @param _amountIn Amount of tokens to swap into the pool.\n * @param _tokenIn Address of the token going into the pool.\n * @param _tokenOut Address of the token being swapped out of the pool.\n */\n function _swapExactTokensForTokens(\n uint256 _amountIn,\n address _tokenIn,\n address _tokenOut\n ) internal {\n // Transfer in tokens to the pool\n IERC20(_tokenIn).safeTransfer(pool, _amountIn);\n\n // Calculate how much out tokens we get from the swap\n uint256 amountOut = IPair(pool).getAmountOut(_amountIn, _tokenIn);\n\n // Safety check that we are dealing with the correct pool tokens\n require(\n (_tokenIn == ws && _tokenOut == os) ||\n (_tokenIn == os && _tokenOut == ws),\n \"Unsupported swap\"\n );\n\n // Work out the correct order of the amounts for the pool\n (uint256 amount0, uint256 amount1) = _tokenIn == ws\n ? (uint256(0), amountOut)\n : (amountOut, 0);\n\n // Perform the swap on the pool\n IPair(pool).swap(amount0, amount1, address(this), new bytes(0));\n\n // The slippage protection against the amount out is indirectly done\n // via the improvePoolBalance\n }\n\n /// @dev Calculate the value of a LP position in a SwapX stable pool\n /// if the pool was balanced.\n /// @param _lpTokens Amount of LP tokens in the SwapX pool\n /// @return value The wS value of the LP tokens when the pool is balanced\n function _lpValue(uint256 _lpTokens) internal view returns (uint256 value) {\n // Get total supply of LP tokens\n uint256 totalSupply = IPair(pool).totalSupply();\n if (totalSupply == 0) return 0;\n\n // Get the current reserves of the pool\n (uint256 wsReserves, uint256 osReserves, ) = IPair(pool).getReserves();\n\n // Calculate the invariant of the pool assuming both tokens have 18 decimals.\n // k is scaled to 18 decimals.\n uint256 k = _invariant(wsReserves, osReserves);\n\n // If x = y, let’s denote x = y = z (where z is the common reserve value)\n // Substitute z into the invariant:\n // k = z^3 * z + z * z^3\n // k = 2 * z^4\n // Going back the other way to calculate the common reserve value z\n // z = (k / 2) ^ (1/4)\n // the total value of the pool when x = y is 2 * z, which is 2 * (k / 2) ^ (1/4)\n uint256 zSquared = sqrt((k * 1e18) / 2); // 18 + 18 = 36 decimals becomes 18 decimals after sqrt\n uint256 z = sqrt(zSquared * 1e18); // 18 + 18 = 36 decimals becomes 18 decimals after sqrt\n uint256 totalValueOfPool = 2 * z;\n\n // lp value = lp tokens * value of pool / total supply\n value = (_lpTokens * totalValueOfPool) / totalSupply;\n }\n\n /**\n * @dev Compute the invariant for a SwapX stable pool.\n * This assumed both x and y tokens are to 18 decimals which is checked in the constructor.\n * invariant: k = x^3 * y + x * y^3\n * @dev This implementation is copied from SwapX's Pair contract.\n * @param _x The amount of Wrapped S (wS) tokens in the pool\n * @param _y The amount of the OS tokens in the pool\n * @return k The invariant of the SwapX stable pool\n */\n function _invariant(uint256 _x, uint256 _y)\n internal\n pure\n returns (uint256 k)\n {\n uint256 _a = (_x * _y) / PRECISION;\n uint256 _b = ((_x * _x) / PRECISION + (_y * _y) / PRECISION);\n // slither-disable-next-line divide-before-multiply\n k = (_a * _b) / PRECISION;\n }\n\n /**\n * @dev Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalSupply = IERC20(os).totalSupply();\n\n if (\n _totalSupply > 0 &&\n _totalVaultValue.divPrecisely(_totalSupply) < SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Setters\n ****************************************/\n\n /**\n * @notice Set the maximum deviation from the OS/wS peg (1e18) before deposits are reverted.\n * @param _maxDepeg the OS/wS price from peg (1e18) in 18 decimals.\n * eg 0.01e18 or 1e16 is 1% which is 100 basis points.\n */\n function setMaxDepeg(uint256 _maxDepeg) external onlyGovernor {\n maxDepeg = _maxDepeg;\n\n emit MaxDepegUpdated(_maxDepeg);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve SwapX gauge contract to transfer SwapX pool LP tokens\n // This is needed for deposits of SwapX pool LP tokens into the gauge.\n // slither-disable-next-line unused-return\n IPair(pool).approve(address(gauge), type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/sonic/SonicValidatorDelegator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { ISFC } from \"../../interfaces/sonic/ISFC.sol\";\nimport { IWrappedSonic } from \"../../interfaces/sonic/IWrappedSonic.sol\";\n\n/**\n * @title Manages delegation to Sonic validators\n * @notice This contract implements all the required functionality to delegate to,\n undelegate from and withdraw from validators.\n * @author Origin Protocol Inc\n */\nabstract contract SonicValidatorDelegator is InitializableAbstractStrategy {\n /// @notice Address of Sonic's wrapped S token\n address public immutable wrappedSonic;\n /// @notice Sonic's Special Fee Contract (SFC)\n ISFC public immutable sfc;\n\n /// @notice a unique ID for each withdrawal request\n uint256 public nextWithdrawId;\n /// @notice Sonic (S) that is pending withdrawal after undelegating\n uint256 public pendingWithdrawals;\n\n /// @notice List of supported validator IDs that can be delegated to\n uint256[] public supportedValidators;\n\n /// @notice Default validator id to deposit to\n uint256 public defaultValidatorId;\n\n struct WithdrawRequest {\n uint256 validatorId;\n uint256 undelegatedAmount;\n uint256 timestamp;\n }\n /// @notice Mapping of withdrawIds to validatorIds and undelegatedAmounts\n mapping(uint256 => WithdrawRequest) public withdrawals;\n\n /// @notice Address of the registrator - allowed to register, exit and remove validators\n address public validatorRegistrator;\n\n // For future use\n uint256[44] private __gap;\n\n event Delegated(uint256 indexed validatorId, uint256 delegatedAmount);\n event Undelegated(\n uint256 indexed withdrawId,\n uint256 indexed validatorId,\n uint256 undelegatedAmount\n );\n event Withdrawn(\n uint256 indexed withdrawId,\n uint256 indexed validatorId,\n uint256 undelegatedAmount,\n uint256 withdrawnAmount\n );\n event RegistratorChanged(address indexed newAddress);\n event SupportedValidator(uint256 indexed validatorId);\n event UnsupportedValidator(uint256 indexed validatorId);\n event DefaultValidatorIdChanged(uint256 indexed validatorId);\n\n /// @dev Throws if called by any account other than the Registrator or Strategist\n modifier onlyRegistratorOrStrategist() {\n require(\n msg.sender == validatorRegistrator ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Registrator or Strategist\"\n );\n _;\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wrappedSonic,\n address _sfc\n ) InitializableAbstractStrategy(_baseConfig) {\n wrappedSonic = _wrappedSonic;\n sfc = ISFC(_sfc);\n }\n\n function initialize() external virtual onlyGovernor initializer {\n address[] memory rewardTokens = new address[](0);\n address[] memory assets = new address[](1);\n address[] memory pTokens = new address[](1);\n\n assets[0] = address(wrappedSonic);\n pTokens[0] = address(platformAddress);\n\n InitializableAbstractStrategy._initialize(\n rewardTokens,\n assets,\n pTokens\n );\n }\n\n /// @notice Returns the total value of Sonic (S) that is delegated validators.\n /// Wrapped Sonic (wS) deposits that are still to be delegated and any undelegated amounts\n /// still pending a withdrawal.\n /// @param _asset Address of Wrapped Sonic (wS) token\n /// @return balance Total value managed by the strategy\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n\n // add the Wrapped Sonic (wS) in the strategy from deposits that are still to be delegated\n // and any undelegated amounts still pending a withdrawal\n balance =\n IERC20(wrappedSonic).balanceOf(address(this)) +\n pendingWithdrawals;\n\n // For each supported validator, get the staked amount and pending rewards\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; i++) {\n uint256 validator = supportedValidators[i];\n balance += sfc.getStake(address(this), validator);\n balance += sfc.pendingRewards(address(this), validator);\n }\n }\n\n /**\n * @dev Delegate from this strategy to a specific Sonic validator. Called\n * automatically on asset deposit\n * @param _amount the amount of Sonic (S) to delegate.\n */\n function _delegate(uint256 _amount) internal {\n require(\n isSupportedValidator(defaultValidatorId),\n \"Validator not supported\"\n );\n\n // unwrap Wrapped Sonic (wS) to native Sonic (S)\n IWrappedSonic(wrappedSonic).withdraw(_amount);\n\n //slither-disable-next-line arbitrary-send-eth\n sfc.delegate{ value: _amount }(defaultValidatorId);\n\n emit Delegated(defaultValidatorId, _amount);\n }\n\n /**\n * @notice Undelegate from a specific Sonic validator.\n * This needs to be followed by a `withdrawFromSFC` two weeks later.\n * @param _validatorId The Sonic validator ID to undelegate from.\n * @param _undelegateAmount the amount of Sonic (S) to undelegate.\n * @return withdrawId The unique ID of the withdrawal request.\n */\n function undelegate(uint256 _validatorId, uint256 _undelegateAmount)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n returns (uint256 withdrawId)\n {\n withdrawId = _undelegate(_validatorId, _undelegateAmount);\n }\n\n function _undelegate(uint256 _validatorId, uint256 _undelegateAmount)\n internal\n returns (uint256 withdrawId)\n {\n // Can still undelegate even if the validator is no longer supported\n require(_undelegateAmount > 0, \"Must undelegate something\");\n\n uint256 amountDelegated = sfc.getStake(address(this), _validatorId);\n require(\n _undelegateAmount <= amountDelegated,\n \"Insufficient delegation\"\n );\n\n withdrawId = nextWithdrawId++;\n\n withdrawals[withdrawId] = WithdrawRequest(\n _validatorId,\n _undelegateAmount,\n block.timestamp\n );\n pendingWithdrawals += _undelegateAmount;\n\n sfc.undelegate(_validatorId, withdrawId, _undelegateAmount);\n\n emit Undelegated(withdrawId, _validatorId, _undelegateAmount);\n }\n\n /**\n * @notice Withdraw native S from a previously undelegated validator.\n * The native S is wrapped wS and transferred to the Vault.\n * @param _withdrawId The unique withdraw ID used to `undelegate`\n * @return withdrawnAmount The amount of Sonic (S) withdrawn.\n * This can be less than the undelegated amount in the event of slashing.\n */\n function withdrawFromSFC(uint256 _withdrawId)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n returns (uint256 withdrawnAmount)\n {\n require(_withdrawId < nextWithdrawId, \"Invalid withdrawId\");\n\n // Can still withdraw even if the validator is no longer supported\n // Load the withdrawal from storage into memory\n WithdrawRequest memory withdrawal = withdrawals[_withdrawId];\n require(!isWithdrawnFromSFC(_withdrawId), \"Already withdrawn\");\n\n withdrawals[_withdrawId].undelegatedAmount = 0;\n pendingWithdrawals -= withdrawal.undelegatedAmount;\n\n uint256 sBalanceBefore = address(this).balance;\n\n // Try to withdraw from SFC\n try sfc.withdraw(withdrawal.validatorId, _withdrawId) {\n // continue below\n } catch (bytes memory err) {\n bytes4 errorSelector = bytes4(err);\n\n // If the validator has been fully slashed, SFC's withdraw function will\n // revert with a StakeIsFullySlashed custom error.\n if (errorSelector == ISFC.StakeIsFullySlashed.selector) {\n // The validator was fully slashed, so all the delegated amounts were lost.\n // Will swallow the error as we still want to update the\n // withdrawals and pendingWithdrawals storage variables.\n\n // The return param defaults to zero but lets set it explicitly so it's clear\n withdrawnAmount = 0;\n\n emit Withdrawn(\n _withdrawId,\n withdrawal.validatorId,\n withdrawal.undelegatedAmount,\n withdrawnAmount\n );\n\n // Exit here as there is nothing to transfer to the Vault\n return withdrawnAmount;\n } else {\n // Bubble up any other SFC custom errors.\n // Inline assembly is currently the only way to generically rethrow the exact same custom error\n // from the raw bytes err in a catch block while preserving its original selector and parameters.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n revert(add(32, err), mload(err))\n }\n }\n }\n\n // Set return parameter\n withdrawnAmount = address(this).balance - sBalanceBefore;\n\n // Wrap Sonic (S) to Wrapped Sonic (wS)\n IWrappedSonic(wrappedSonic).deposit{ value: withdrawnAmount }();\n\n // Transfer the Wrapped Sonic (wS) to the Vault\n _withdraw(vaultAddress, wrappedSonic, withdrawnAmount);\n\n // withdrawal.undelegatedAmount & withdrawnAmount can differ in case of slashing\n emit Withdrawn(\n _withdrawId,\n withdrawal.validatorId,\n withdrawal.undelegatedAmount,\n withdrawnAmount\n );\n }\n\n /// @notice returns a bool whether a withdrawalId has already been withdrawn or not\n /// @param _withdrawId The unique withdraw ID used to `undelegate`\n function isWithdrawnFromSFC(uint256 _withdrawId)\n public\n view\n returns (bool)\n {\n WithdrawRequest memory withdrawal = withdrawals[_withdrawId];\n require(withdrawal.validatorId > 0, \"Invalid withdrawId\");\n return withdrawal.undelegatedAmount == 0;\n }\n\n /**\n * @notice Restake any pending validator rewards for all supported validators\n * @param _validatorIds List of Sonic validator IDs to restake rewards\n */\n function restakeRewards(uint256[] calldata _validatorIds)\n external\n nonReentrant\n {\n for (uint256 i = 0; i < _validatorIds.length; ++i) {\n require(\n isSupportedValidator(_validatorIds[i]),\n \"Validator not supported\"\n );\n\n uint256 rewards = sfc.pendingRewards(\n address(this),\n _validatorIds[i]\n );\n\n if (rewards > 0) {\n sfc.restakeRewards(_validatorIds[i]);\n }\n }\n\n // The SFC contract will emit Delegated and RestakedRewards events.\n // The checkBalance function should not change as the pending rewards will moved to the staked amount.\n }\n\n /**\n * @notice Claim any pending rewards from validators\n * @param _validatorIds List of Sonic validator IDs to claim rewards\n */\n function collectRewards(uint256[] calldata _validatorIds)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n {\n uint256 sBalanceBefore = address(this).balance;\n\n for (uint256 i = 0; i < _validatorIds.length; ++i) {\n uint256 rewards = sfc.pendingRewards(\n address(this),\n _validatorIds[i]\n );\n\n if (rewards > 0) {\n // The SFC contract will emit ClaimedRewards(delegator (this), validatorId, rewards)\n sfc.claimRewards(_validatorIds[i]);\n }\n }\n\n uint256 rewardsAmount = address(this).balance - sBalanceBefore;\n\n // Wrap Sonic (S) to Wrapped Sonic (wS)\n IWrappedSonic(wrappedSonic).deposit{ value: rewardsAmount }();\n\n // Transfer the Wrapped Sonic (wS) to the Vault\n _withdraw(vaultAddress, wrappedSonic, rewardsAmount);\n }\n\n /**\n * @notice To receive native S from SFC and Wrapped Sonic (wS)\n *\n * @dev This does not prevent donating S tokens to the contract\n * as wrappedSonic has a `withdrawTo` function where a third party\n * owner of wrappedSonic can withdraw to this contract.\n */\n receive() external payable {\n require(\n msg.sender == address(sfc) || msg.sender == wrappedSonic,\n \"S not from allowed contracts\"\n );\n }\n\n /***************************************\n Admin functions\n ****************************************/\n\n /// @notice Set the address of the Registrator which can undelegate, withdraw and collect rewards\n /// @param _validatorRegistrator The address of the Registrator\n function setRegistrator(address _validatorRegistrator)\n external\n onlyGovernor\n {\n validatorRegistrator = _validatorRegistrator;\n emit RegistratorChanged(_validatorRegistrator);\n }\n\n /// @notice Set the default validatorId to delegate to on deposit\n /// @param _validatorId The validator identifier. eg 18\n function setDefaultValidatorId(uint256 _validatorId)\n external\n onlyRegistratorOrStrategist\n {\n require(isSupportedValidator(_validatorId), \"Validator not supported\");\n defaultValidatorId = _validatorId;\n emit DefaultValidatorIdChanged(_validatorId);\n }\n\n /// @notice Allows a validator to be delegated to by the Registrator\n /// @param _validatorId The validator identifier. eg 18\n function supportValidator(uint256 _validatorId) external onlyGovernor {\n require(\n !isSupportedValidator(_validatorId),\n \"Validator already supported\"\n );\n\n supportedValidators.push(_validatorId);\n\n emit SupportedValidator(_validatorId);\n }\n\n /// @notice Removes a validator from the supported list.\n /// Unsupported validators can still be undelegated from, withdrawn from and rewards collected.\n /// @param _validatorId The validator identifier. eg 18\n function unsupportValidator(uint256 _validatorId) external onlyGovernor {\n require(isSupportedValidator(_validatorId), \"Validator not supported\");\n\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; ++i) {\n if (supportedValidators[i] == _validatorId) {\n supportedValidators[i] = supportedValidators[validatorLen - 1];\n supportedValidators.pop();\n break;\n }\n }\n\n uint256 stake = sfc.getStake(address(this), _validatorId);\n\n // undelegate if validator still has funds staked\n if (stake > 0) {\n _undelegate(_validatorId, stake);\n }\n emit UnsupportedValidator(_validatorId);\n }\n\n /// @notice Returns the length of the supportedValidators array\n function supportedValidatorsLength() external view returns (uint256) {\n return supportedValidators.length;\n }\n\n /// @notice Returns whether a validator is supported by this strategy\n /// @param _validatorId The validator identifier\n function isSupportedValidator(uint256 _validatorId)\n public\n view\n returns (bool)\n {\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; ++i) {\n if (supportedValidators[i] == _validatorId) {\n return true;\n }\n }\n return false;\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal virtual;\n}\n" + }, + "contracts/strategies/VaultValueChecker.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IOUSD } from \"../interfaces/IOUSD.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract VaultValueChecker {\n IVault public immutable vault;\n IOUSD public immutable ousd;\n // Snapshot expiration time in seconds.\n // Used to prevent accidental use of an old snapshot, but\n // is not zero to allow easy testing of strategist actions in fork testing\n uint256 constant SNAPSHOT_EXPIRES = 5 * 60;\n\n struct Snapshot {\n uint256 vaultValue;\n uint256 totalSupply;\n uint256 time;\n }\n // By doing per user snapshots, we prevent a reentrancy attack\n // from a third party that updates the snapshot in the middle\n // of an allocation process\n\n mapping(address => Snapshot) public snapshots;\n\n constructor(address _vault, address _ousd) {\n vault = IVault(_vault);\n ousd = IOUSD(_ousd);\n }\n\n function takeSnapshot() external {\n snapshots[msg.sender] = Snapshot({\n vaultValue: vault.totalValue(),\n totalSupply: ousd.totalSupply(),\n time: block.timestamp\n });\n }\n\n function checkDelta(\n int256 expectedProfit,\n int256 profitVariance,\n int256 expectedVaultChange,\n int256 vaultChangeVariance\n ) external {\n // Intentionaly not view so that this method shows up in TX builders\n Snapshot memory snapshot = snapshots[msg.sender];\n int256 vaultChange = toInt256(vault.totalValue()) -\n toInt256(snapshot.vaultValue);\n int256 supplyChange = toInt256(ousd.totalSupply()) -\n toInt256(snapshot.totalSupply);\n int256 profit = vaultChange - supplyChange;\n\n require(\n snapshot.time >= block.timestamp - SNAPSHOT_EXPIRES,\n \"Snapshot too old\"\n );\n require(snapshot.time <= block.timestamp, \"Snapshot too new\");\n require(profit >= expectedProfit - profitVariance, \"Profit too low\");\n require(profit <= expectedProfit + profitVariance, \"Profit too high\");\n require(\n vaultChange >= expectedVaultChange - vaultChangeVariance,\n \"Vault value change too low\"\n );\n require(\n vaultChange <= expectedVaultChange + vaultChangeVariance,\n \"Vault value change too high\"\n );\n }\n\n function toInt256(uint256 value) internal pure returns (int256) {\n // From openzeppelin math/SafeCast.sol\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(\n value <= uint256(type(int256).max),\n \"SafeCast: value doesn't fit in an int256\"\n );\n return int256(value);\n }\n}\n\ncontract OETHVaultValueChecker is VaultValueChecker {\n constructor(address _vault, address _ousd)\n VaultValueChecker(_vault, _ousd)\n {}\n}\n" + }, + "contracts/swapper/Swapper1InchV5.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @notice 1Inch Pathfinder V5 implementation of the general ISwapper interface.\n * @author Origin Protocol Inc\n * @dev It is possible that dust token amounts are left in this contract after a swap.\n * This can happen with some tokens that don't send the full transfer amount.\n * These dust amounts can build up over time and be used by anyone who calls the `swap` function.\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IAggregationExecutor, IOneInchRouter, SwapDescription } from \"../interfaces/IOneInch.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\n\ncontract Swapper1InchV5 is ISwapper {\n using SafeERC20 for IERC20;\n\n /// @notice 1Inch router contract to give allowance to perform swaps\n address public constant SWAP_ROUTER =\n 0x1111111254EEB25477B68fb85Ed929f73A960582;\n\n // swap(address,(address,address,address,address,uint256,uint256,uint256),bytes,bytes)\n bytes4 internal constant SWAP_SELECTOR = 0x12aa3caf;\n // unoswapTo(address,address,uint256,uint256,uint256[])\n bytes4 internal constant UNISWAP_SELECTOR = 0xf78dc253;\n // uniswapV3SwapTo(address,uint256,uint256,uint256[])\n bytes4 internal constant UNISWAPV3_SELECTOR = 0xbc80f1a8;\n\n /**\n * @notice Strategist swaps assets sitting in the contract of the `assetHolder`.\n * @param _fromAsset The token address of the asset being sold by the vault.\n * @param _toAsset The token address of the asset being purchased by the vault.\n * @param _fromAssetAmount The amount of assets being sold by the vault.\n * @param _minToAssetAmount The minimum amount of assets to be purchased.\n * @param _data RLP encoded executer address and bytes data. This is re-encoded tx.data from 1Inch swap API\n */\n function swap(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n ) external override returns (uint256 toAssetAmount) {\n // Decode the function selector from the RLP encoded _data param\n bytes4 swapSelector = bytes4(_data[:4]);\n\n if (swapSelector == SWAP_SELECTOR) {\n // Decode the executer address and data from the RLP encoded _data param\n (, address executer, bytes memory executerData) = abi.decode(\n _data,\n (bytes4, address, bytes)\n );\n SwapDescription memory swapDesc = SwapDescription({\n srcToken: IERC20(_fromAsset),\n dstToken: IERC20(_toAsset),\n srcReceiver: payable(executer),\n dstReceiver: payable(msg.sender),\n amount: _fromAssetAmount,\n minReturnAmount: _minToAssetAmount,\n flags: 4 // 1st bit _PARTIAL_FILL, 2nd bit _REQUIRES_EXTRA_ETH, 3rd bit _SHOULD_CLAIM\n });\n (toAssetAmount, ) = IOneInchRouter(SWAP_ROUTER).swap(\n IAggregationExecutor(executer),\n swapDesc,\n hex\"\",\n executerData\n );\n } else if (swapSelector == UNISWAP_SELECTOR) {\n // Need to get the Uniswap pools data from the _data param\n (, uint256[] memory pools) = abi.decode(_data, (bytes4, uint256[]));\n toAssetAmount = IOneInchRouter(SWAP_ROUTER).unoswapTo(\n payable(msg.sender),\n IERC20(_fromAsset),\n _fromAssetAmount,\n _minToAssetAmount,\n pools\n );\n } else if (swapSelector == UNISWAPV3_SELECTOR) {\n // Need to get the Uniswap pools data from the _data param\n // slither-disable-next-line uninitialized-storage\n (, uint256[] memory pools) = abi.decode(_data, (bytes4, uint256[]));\n toAssetAmount = IOneInchRouter(SWAP_ROUTER).uniswapV3SwapTo(\n payable(msg.sender),\n _fromAssetAmount,\n _minToAssetAmount,\n pools\n );\n } else {\n revert(\"Unsupported swap function\");\n }\n }\n\n /**\n * @notice Approve assets for swapping.\n * @param _assets Array of token addresses to approve.\n * @dev unlimited approval is used as no tokens sit in this contract outside a transaction.\n */\n function approveAssets(address[] memory _assets) external {\n for (uint256 i = 0; i < _assets.length; ++i) {\n // Give the 1Inch router approval to transfer unlimited assets\n IERC20(_assets[i]).safeApprove(SWAP_ROUTER, type(uint256).max);\n }\n }\n}\n" + }, + "contracts/token/BridgedWOETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\n\ncontract BridgedWOETH is\n Governable,\n AccessControlEnumerable,\n Initializable,\n ERC20\n{\n bytes32 public constant MINTER_ROLE = keccak256(\"MINTER_ROLE\");\n bytes32 public constant BURNER_ROLE = keccak256(\"BURNER_ROLE\");\n\n constructor() ERC20(\"Wrapped OETH\", \"wOETH\") {\n // Nobody owns the implementation\n _setGovernor(address(0));\n }\n\n /**\n * @dev Initialize the proxy and set the Governor\n */\n function initialize() external initializer {\n // Governor can grant Minter/Burner roles\n _setupRole(DEFAULT_ADMIN_ROLE, _governor());\n }\n\n /**\n * @dev Mint tokens for `account`\n * @param account Address to mint tokens for\n * @param amount Amount of tokens to mint\n */\n function mint(address account, uint256 amount)\n external\n onlyRole(MINTER_ROLE)\n nonReentrant\n {\n _mint(account, amount);\n }\n\n /**\n * @dev Burns tokens from `account`\n * @param account Address to burn tokens from\n * @param amount Amount of tokens to burn\n */\n function burn(address account, uint256 amount)\n external\n onlyRole(BURNER_ROLE)\n nonReentrant\n {\n _burn(account, amount);\n }\n\n /**\n * @dev Burns tokens from `msg.sender`\n * @param amount Amount of tokens to burn\n */\n function burn(uint256 amount) external onlyRole(BURNER_ROLE) nonReentrant {\n _burn(msg.sender, amount);\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return \"Wrapped OETH\";\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return \"wOETH\";\n }\n\n /**\n * @dev Returns the decimals of the token\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETH is OUSD {\n function symbol() external pure override returns (string memory) {\n return \"OETH\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Origin Ether\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETHBase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBase is OUSD {\n constructor() {\n // Nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n function symbol() external pure override returns (string memory) {\n return \"superOETHb\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Super OETH\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETHPlume.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title Super OETH (Plume) Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETHPlume is OUSD {\n constructor() {\n // Nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n function symbol() external pure override returns (string memory) {\n return \"superOETHp\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Super OETH\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OSonic.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title Origin Sonic (OS) token on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonic is OUSD {\n function symbol() external pure override returns (string memory) {\n return \"OS\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Origin Sonic\";\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/token/OUSDResolutionUpgrade.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract OUSDResolutionUpgrade {\n enum RebaseOptions {\n NotSet,\n OptOut,\n OptIn\n }\n\n // From Initializable\n bool private initialized;\n bool private initializing;\n uint256[50] private ______igap;\n\n // From InitializableERC20Detailed\n uint256[100] private _____ugap;\n string private _name;\n string private _symbol;\n uint8 private _decimals;\n\n // From OUSD\n uint256 private constant MAX_SUPPLY = ~uint128(0); // (2^128) - 1\n uint256 public _totalSupply;\n mapping(address => mapping(address => uint256)) private _allowances;\n address public vaultAddress = address(0);\n mapping(address => uint256) private _creditBalances;\n uint256 private _rebasingCredits;\n uint256 private _rebasingCreditsPerToken;\n uint256 public nonRebasingSupply;\n mapping(address => uint256) public nonRebasingCreditsPerToken;\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) public isUpgraded;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() public view returns (uint256) {\n return _rebasingCreditsPerToken / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCredits() public view returns (uint256) {\n return _rebasingCredits / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() public view returns (uint256) {\n return _rebasingCreditsPerToken;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() public view returns (uint256) {\n return _rebasingCredits;\n }\n\n function upgradeGlobals() external {\n require(isUpgraded[address(0)] == 0, \"Globals already upgraded\");\n require(_rebasingCredits > 0, \"Sanity _rebasingCredits\");\n require(\n _rebasingCreditsPerToken > 0,\n \"Sanity _rebasingCreditsPerToken\"\n );\n isUpgraded[address(0)] = 1;\n _rebasingCredits = _rebasingCredits * RESOLUTION_INCREASE;\n _rebasingCreditsPerToken =\n _rebasingCreditsPerToken *\n RESOLUTION_INCREASE;\n }\n\n function upgradeAccounts(address[] calldata accounts) external {\n for (uint256 i = 0; i < accounts.length; i++) {\n address account = accounts[i];\n require(account != address(0), \"Reserved\");\n require(isUpgraded[account] == 0, \"Account already upgraded\");\n isUpgraded[account] = 1;\n\n // Handle special for non-rebasing accounts\n uint256 nrc = nonRebasingCreditsPerToken[account];\n if (nrc > 0) {\n require(nrc < 1e18, \"Account already highres\");\n nonRebasingCreditsPerToken[account] = nrc * RESOLUTION_INCREASE;\n }\n // Upgrade balance\n uint256 balance = _creditBalances[account];\n require(balance > 0, \"Will not upgrade zero balance\");\n _creditBalances[account] = balance * RESOLUTION_INCREASE;\n }\n }\n\n function creditsBalanceOfHighres(address _account)\n public\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n _creditBalances[_account],\n _creditsPerToken(_account),\n isUpgraded[_account] == 1\n );\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n if (nonRebasingCreditsPerToken[_account] != 0) {\n return nonRebasingCreditsPerToken[_account];\n } else {\n return _rebasingCreditsPerToken;\n }\n }\n}\n" + }, + "contracts/token/WOETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC4626 } from \"../../lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { OETH } from \"./OETH.sol\";\n\n/**\n * @title Wrapped OETH Token Contract\n * @author Origin Protocol Inc\n *\n * @dev An important capability of this contract is that it isn't susceptible to changes of the\n * exchange rate of WOETH/OETH if/when someone sends the underlying asset (OETH) to the contract.\n * If OETH weren't rebasing this could be achieved by solely tracking the ERC20 transfers of the OETH\n * token on mint, deposit, redeem, withdraw. The issue is that OETH is rebasing and OETH balances\n * will change when the token rebases.\n * For that reason the contract logic checks the actual underlying OETH token balance only once\n * (either on a fresh contract creation or upgrade) and considering the WOETH supply and\n * rebasingCreditsPerToken calculates the _adjuster. Once the adjuster is calculated any donations\n * to the contract are ignored. The totalSupply (instead of querying OETH balance) works off of\n * adjuster the current WOETH supply and rebasingCreditsPerToken. This makes WOETH value accrual\n * completely follow OETH's value accrual.\n * WOETH is safe to use in lending markets as the VualtCore's _rebase contains safeguards preventing\n * any sudden large rebases.\n */\n\ncontract WOETH is ERC4626, Governable, Initializable {\n using SafeERC20 for IERC20;\n /* This is a 1e27 adjustment constant that expresses the difference in exchange rate between\n * OETH's rebase since inception (expressed with rebasingCreditsPerToken) and WOETH to OETH\n * conversion.\n *\n * If WOETH and OETH are deployed at the same time, the value of adjuster is a neutral 1e27\n */\n uint256 public adjuster;\n uint256[49] private __gap;\n\n // no need to set ERC20 name and symbol since they are overridden in WOETH & WOETHBase\n constructor(ERC20 underlying_) ERC20(\"\", \"\") ERC4626(underlying_) {}\n\n /**\n * @notice Enable OETH rebasing for this contract\n */\n function initialize() external onlyGovernor initializer {\n OETH(address(asset())).rebaseOptIn();\n\n initialize2();\n }\n\n /**\n * @notice secondary initializer that newly deployed contracts will execute as part\n * of primary initialize function and the existing contracts will have it called\n * as a governance operation.\n */\n function initialize2() public onlyGovernor {\n require(adjuster == 0, \"Initialize2 already called\");\n\n if (totalSupply() == 0) {\n adjuster = 1e27;\n } else {\n adjuster =\n (rebasingCreditsPerTokenHighres() *\n ERC20(asset()).balanceOf(address(this))) /\n totalSupply();\n }\n }\n\n function name()\n public\n view\n virtual\n override(ERC20, IERC20Metadata)\n returns (string memory)\n {\n return \"Wrapped OETH\";\n }\n\n function symbol()\n public\n view\n virtual\n override(ERC20, IERC20Metadata)\n returns (string memory)\n {\n return \"wOETH\";\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends. Cannot transfer OETH\n * @param asset_ Address for the asset\n * @param amount_ Amount of the asset to transfer\n */\n function transferToken(address asset_, uint256 amount_)\n external\n onlyGovernor\n {\n require(asset_ != address(asset()), \"Cannot collect core asset\");\n IERC20(asset_).safeTransfer(governor(), amount_);\n }\n\n /// @inheritdoc ERC4626\n function convertToShares(uint256 assets)\n public\n view\n virtual\n override\n returns (uint256 shares)\n {\n return (assets * rebasingCreditsPerTokenHighres()) / adjuster;\n }\n\n /// @inheritdoc ERC4626\n function convertToAssets(uint256 shares)\n public\n view\n virtual\n override\n returns (uint256 assets)\n {\n return (shares * adjuster) / rebasingCreditsPerTokenHighres();\n }\n\n /// @inheritdoc ERC4626\n function totalAssets() public view override returns (uint256) {\n return (totalSupply() * adjuster) / rebasingCreditsPerTokenHighres();\n }\n\n function rebasingCreditsPerTokenHighres() internal view returns (uint256) {\n return OETH(asset()).rebasingCreditsPerTokenHighres();\n }\n}\n" + }, + "contracts/token/WOETHBase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WOETH } from \"./WOETH.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\n\ncontract WOETHBase is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name() public view virtual override returns (string memory) {\n return \"Wrapped Super OETH\";\n }\n\n function symbol() public view virtual override returns (string memory) {\n return \"wsuperOETHb\";\n }\n}\n" + }, + "contracts/token/WOETHPlume.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WOETH } from \"./WOETH.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @title wOETH (Plume) Token Contract\n * @author Origin Protocol Inc\n */\n\ncontract WOETHPlume is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name() public view virtual override returns (string memory) {\n return \"Wrapped Super OETH\";\n }\n\n function symbol() public view virtual override returns (string memory) {\n return \"wsuperOETHp\";\n }\n}\n" + }, + "contracts/token/WOSonic.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { WOETH } from \"./WOETH.sol\";\n\n/**\n * @title Wrapped Origin Sonic (wOS) token on Sonic\n * @author Origin Protocol Inc\n */\ncontract WOSonic is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"Wrapped OS\";\n }\n\n function symbol()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"wOS\";\n }\n}\n" + }, + "contracts/token/WrappedOusd.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { WOETH } from \"./WOETH.sol\";\n\n/**\n * @title Wrapped OUSD Token Contract\n * @author Origin Protocol Inc\n */\ncontract WrappedOusd is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"Wrapped OUSD\";\n }\n\n function symbol()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"WOUSD\";\n }\n}\n" + }, + "contracts/utils/AerodromeAMOQuoter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.7;\n\nimport { ICLPool } from \"../interfaces/aerodrome/ICLPool.sol\";\nimport { IQuoterV2 } from \"../interfaces/aerodrome/IQuoterV2.sol\";\nimport { IAMOStrategy } from \"../interfaces/aerodrome/IAMOStrategy.sol\";\n\n/// @title QuoterHelper\n/// @author Origin Protocol\n/// @notice Helper for Aerodrome AMO Quoter, as `_quoteAmountToSwapBeforeRebalance` use try/catch method and\n/// this can only be used when calling external contracts.\ncontract QuoterHelper {\n ////////////////////////////////////////////////////////////////\n /// --- STRUCTS & ENUMS\n ////////////////////////////////////////////////////////////////\n enum RevertReasons {\n DefaultStatus,\n RebalanceOutOfBounds,\n NotInExpectedTickRange,\n NotEnoughWethForSwap,\n NotEnoughWethLiquidity,\n UnexpectedError,\n Found,\n NotFound\n }\n\n struct RebalanceStatus {\n RevertReasons reason;\n uint256 currentPoolWETHShare; // Case 1\n uint256 allowedWETHShareStart; // Case 1\n uint256 allowedWETHShareEnd; // Case 1\n int24 currentTick; // Case 2\n uint256 balanceWETH; // Case 3\n uint256 amountWETH; // Case 3\n string revertMessage;\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTANT & IMMUTABLE\n ////////////////////////////////////////////////////////////////\n uint256 public constant BINARY_MIN_AMOUNT = 1 wei;\n\n uint256 public constant BINARY_MAX_ITERATIONS = 40;\n uint256 public constant PERCENTAGE_BASE = 1e18; // 100%\n uint256 public constant ALLOWED_VARIANCE_PERCENTAGE = 1e15; // 0.1%\n\n ////////////////////////////////////////////////////////////////\n /// --- VARIABLES STORAGE\n ////////////////////////////////////////////////////////////////\n ICLPool public immutable clPool;\n IQuoterV2 public immutable quoterV2;\n IAMOStrategy public immutable strategy;\n\n address public originalGovernor;\n\n ////////////////////////////////////////////////////////////////\n /// --- ERRORS & EVENTS\n ////////////////////////////////////////////////////////////////\n error UnexpectedError(string message);\n error OutOfIterations(uint256 iterations);\n error ValidAmount(\n uint256 amount,\n uint256 iterations,\n bool swapWETHForOETHB\n );\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////////////////\n constructor(IAMOStrategy _strategy, IQuoterV2 _quoterV2) {\n strategy = _strategy;\n quoterV2 = _quoterV2;\n clPool = _strategy.clPool();\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- FUNCTIONS\n ////////////////////////////////////////////////////////////////\n /// @notice This call can only end with a revert.\n function getAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) public {\n if (\n overrideBottomWethShare != type(uint256).max ||\n overrideTopWethShare != type(uint256).max\n ) {\n // Current values\n uint256 shareStart = strategy.allowedWethShareStart();\n uint256 shareEnd = strategy.allowedWethShareEnd();\n\n // Override values\n if (overrideBottomWethShare != type(uint256).max) {\n shareStart = overrideBottomWethShare;\n }\n if (overrideTopWethShare != type(uint256).max) {\n shareEnd = overrideTopWethShare;\n }\n\n strategy.setAllowedPoolWethShareInterval(shareStart, shareEnd);\n }\n\n uint256 iterations = 0;\n uint256 low = BINARY_MIN_AMOUNT;\n uint256 high;\n (high, ) = strategy.getPositionPrincipal();\n int24 lowerTick = strategy.lowerTick();\n int24 upperTick = strategy.upperTick();\n bool swapWETHForOETHB = getSwapDirectionForRebalance();\n\n while (low <= high && iterations < BINARY_MAX_ITERATIONS) {\n uint256 mid = (low + high) / 2;\n\n RebalanceStatus memory status = getRebalanceStatus(\n mid,\n swapWETHForOETHB\n );\n\n // Best case, we found the `amount` that will reach the target pool share!\n // We can revert with the amount and the number of iterations\n if (status.reason == RevertReasons.Found) {\n revert ValidAmount(mid, iterations, swapWETHForOETHB);\n }\n\n // If the rebalance failed then we should try to change the amount.\n // We will handle all possible revert reasons here.\n\n // Case 1: Rebalance out of bounds\n // If the pool is out of bounds, we need to adjust the amount to reach the target pool share\n if (status.reason == RevertReasons.RebalanceOutOfBounds) {\n // If the current pool share is less than the target pool share, we need to increase the amount\n if (\n swapWETHForOETHB\n ? status.currentPoolWETHShare <\n status.allowedWETHShareStart\n : status.currentPoolWETHShare >\n status.allowedWETHShareEnd\n ) {\n low = mid + 1;\n }\n // Else we need to decrease the amount\n else {\n high = mid;\n }\n }\n\n // Case 2: Not in expected tick range\n // If the pool is not in the expected tick range, we need to adjust the amount\n // to reach the target pool share\n if (status.reason == RevertReasons.NotInExpectedTickRange) {\n // If we are buying OETHb and the current tick is greater than the lower tick,\n //we need to increase the amount in order to continue to push price down.\n // If we are selling OETHb and the current tick is less than the upper tick,\n // we need to increase the amount in order to continue to push price up.\n if (\n swapWETHForOETHB\n ? status.currentTick > lowerTick\n : status.currentTick < upperTick\n ) {\n low = mid + 1;\n }\n // Else we need to decrease the amount\n else {\n high = mid;\n }\n }\n\n // Case 3: Not enough WETH for swap\n // If we don't have enough WETH to swap, we need to decrease the amount\n // This error can happen, when initial value of mid is too high, so we need to decrease it\n if (status.reason == RevertReasons.NotEnoughWethForSwap) {\n high = mid;\n }\n\n // Case 4: Not enough WETH liquidity\n // If we don't have enough WETH liquidity\n // Revert for the moment, we need to improve this\n if (status.reason == RevertReasons.NotEnoughWethLiquidity) {\n revert(\"Quoter: Not enough WETH liquidity\");\n }\n\n // Case 5: Unexpected error\n // Worst case, it reverted with an unexpected error.\n if (status.reason == RevertReasons.UnexpectedError) {\n revert UnexpectedError(status.revertMessage);\n }\n\n iterations++;\n }\n\n // Case 6: Out of iterations\n // If we didn't find the amount after the max iterations, we need to revert.\n revert OutOfIterations(iterations);\n }\n\n /// @notice Get the status of the rebalance\n /// @param amount The amount of token to swap\n /// @param swapWETH True if we need to swap WETH for OETHb, false otherwise\n /// @return status The status of the rebalance\n function getRebalanceStatus(uint256 amount, bool swapWETH)\n public\n returns (RebalanceStatus memory status)\n {\n try strategy.rebalance(amount, swapWETH, 0) {\n status.reason = RevertReasons.Found;\n return status;\n } catch Error(string memory reason) {\n status.reason = RevertReasons.UnexpectedError;\n status.revertMessage = reason;\n } catch (bytes memory reason) {\n bytes4 receivedSelector = bytes4(reason);\n\n // Case 1: Rebalance out of bounds\n if (\n receivedSelector ==\n IAMOStrategy.PoolRebalanceOutOfBounds.selector\n ) {\n uint256 currentPoolWETHShare;\n uint256 allowedWETHShareStart;\n uint256 allowedWETHShareEnd;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n currentPoolWETHShare := mload(add(reason, 0x24))\n allowedWETHShareStart := mload(add(reason, 0x44))\n allowedWETHShareEnd := mload(add(reason, 0x64))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.RebalanceOutOfBounds,\n currentPoolWETHShare: currentPoolWETHShare,\n allowedWETHShareStart: allowedWETHShareStart,\n allowedWETHShareEnd: allowedWETHShareEnd,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 2: Not in expected tick range\n if (\n receivedSelector ==\n IAMOStrategy.OutsideExpectedTickRange.selector\n ) {\n int24 currentTick;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n currentTick := mload(add(reason, 0x24))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.NotInExpectedTickRange,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: currentTick,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 3: Not enough WETH for swap\n if (\n receivedSelector == IAMOStrategy.NotEnoughWethForSwap.selector\n ) {\n uint256 balanceWETH;\n uint256 amountWETH;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n balanceWETH := mload(add(reason, 0x24))\n amountWETH := mload(add(reason, 0x44))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.NotEnoughWethForSwap,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: balanceWETH,\n amountWETH: amountWETH,\n revertMessage: \"\"\n });\n }\n\n // Case 4: Not enough WETH liquidity\n if (\n receivedSelector == IAMOStrategy.NotEnoughWethLiquidity.selector\n ) {\n return\n RebalanceStatus({\n reason: RevertReasons.NotEnoughWethLiquidity,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 5: Unexpected error\n return\n RebalanceStatus({\n reason: RevertReasons.UnexpectedError,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: abi.decode(reason, (string))\n });\n }\n }\n\n /// @notice Get the swap direction to reach the target price before rebalance.\n /// @return bool True if we need to swap WETH for OETHb, false otherwise.\n function getSwapDirectionForRebalance() public view returns (bool) {\n uint160 currentPrice = strategy.getPoolX96Price();\n uint160 ticker0Price = strategy.sqrtRatioX96TickLower();\n uint160 ticker1Price = strategy.sqrtRatioX96TickHigher();\n uint256 allowedWethShareStart = strategy.allowedWethShareStart();\n uint256 allowedWethShareEnd = strategy.allowedWethShareEnd();\n uint160 mid = uint160(allowedWethShareStart + allowedWethShareEnd) / 2;\n // slither-disable-start divide-before-multiply\n uint160 targetPrice = (ticker0Price *\n mid +\n ticker1Price *\n (1 ether - mid)) / 1 ether;\n // slither-disable-end divide-before-multiply\n\n return currentPrice > targetPrice;\n }\n\n // returns total amount in the position principal of the Aerodrome AMO strategy. Needed as a\n // separate function because of the limitation in local variable count in getAmountToSwapToReachPrice\n function getTotalStrategyPosition() internal returns (uint256) {\n (uint256 wethAmount, uint256 oethBalance) = strategy\n .getPositionPrincipal();\n return wethAmount + oethBalance;\n }\n\n /// @notice Get the amount of tokens to swap to reach the target price.\n /// @dev This act like a quoter, i.e. the transaction is not performed.\n /// @dev Because the amount to swap can be largely overestimated, because CLAMM alow partial orders,\n /// i.e. when we ask to swap a very large amount, with a close priceLimite, it will swap only a part of it,\n /// and not revert. So if overestimated amount is to high, use a custom maxAmount to avoid this issue.\n /// @param sqrtPriceTargetX96 The target price to reach.\n /// @return amount The amount of tokens to swap.\n /// @return iterations The number of iterations to find the amount.\n /// @return swapWETHForOETHB True if we need to swap WETH for OETHb, false otherwise.\n /// @return sqrtPriceX96After The price after the swap.\n function getAmountToSwapToReachPrice(uint160 sqrtPriceTargetX96)\n public\n returns (\n uint256,\n uint256,\n bool,\n uint160\n )\n {\n uint256 iterations = 0;\n uint256 low = BINARY_MIN_AMOUNT;\n // high search start is twice the position principle of Aerodrome AMO strategy.\n // should be more than enough\n uint256 high = getTotalStrategyPosition() * 2;\n bool swapWETHForOETHB = getSwapDirection(sqrtPriceTargetX96);\n\n while (low <= high && iterations < BINARY_MAX_ITERATIONS) {\n uint256 mid = (low + high) / 2;\n\n // Call QuoterV2 from SugarHelper\n (uint256 amountOut, uint160 sqrtPriceX96After, , ) = quoterV2\n .quoteExactInputSingle(\n IQuoterV2.QuoteExactInputSingleParams({\n tokenIn: swapWETHForOETHB\n ? clPool.token0()\n : clPool.token1(),\n tokenOut: swapWETHForOETHB\n ? clPool.token1()\n : clPool.token0(),\n amountIn: mid,\n tickSpacing: strategy.tickSpacing(),\n sqrtPriceLimitX96: sqrtPriceTargetX96\n })\n );\n\n if (\n isWithinAllowedVariance(sqrtPriceX96After, sqrtPriceTargetX96)\n ) {\n /** Very important to return `amountOut` instead of `mid` as the first return parameter.\n * The issues was that when quoting we impose a swap price limit (sqrtPriceLimitX96: sqrtPriceTargetX96)\n * and in that case the `amountIn` acts like a maximum amount to swap. And we don't know how much\n * of that amount was actually consumed. For that reason we \"estimate\" it by returning the\n * amountOut since that is only going to be a couple of basis point away from amountIn in the\n * worst cases.\n *\n * Note: we could be returning mid instead of amountOut in cases when those values are only basis\n * points apart (assuming that complete balance of amountIn has been consumed) but that might increase\n * complexity too much in an already complex contract.\n */\n return (\n amountOut,\n iterations,\n swapWETHForOETHB,\n sqrtPriceX96After\n );\n } else if (low == high) {\n // target swap amount not found.\n // might be that \"high\" amount is too low on start\n revert(\"SwapAmountNotFound\");\n } else if (\n swapWETHForOETHB\n ? sqrtPriceX96After > sqrtPriceTargetX96\n : sqrtPriceX96After < sqrtPriceTargetX96\n ) {\n low = mid + 1;\n } else {\n high = mid;\n }\n iterations++;\n }\n\n revert OutOfIterations(iterations);\n }\n\n /// @notice Check if the current price is within the allowed variance in comparison to the target price\n /// @return bool True if the current price is within the allowed variance, false otherwise\n function isWithinAllowedVariance(\n uint160 sqrtPriceCurrentX96,\n uint160 sqrtPriceTargetX96\n ) public view returns (bool) {\n uint160 range = strategy.sqrtRatioX96TickHigher() -\n strategy.sqrtRatioX96TickLower();\n if (sqrtPriceCurrentX96 > sqrtPriceTargetX96) {\n return\n (sqrtPriceCurrentX96 - sqrtPriceTargetX96) <=\n (ALLOWED_VARIANCE_PERCENTAGE * range) / PERCENTAGE_BASE;\n } else {\n return\n (sqrtPriceTargetX96 - sqrtPriceCurrentX96) <=\n (ALLOWED_VARIANCE_PERCENTAGE * range) / PERCENTAGE_BASE;\n }\n }\n\n /// @notice Get the swap direction to reach the target price.\n /// @param sqrtPriceTargetX96 The target price to reach.\n /// @return bool True if we need to swap WETH for OETHb, false otherwise.\n function getSwapDirection(uint160 sqrtPriceTargetX96)\n public\n view\n returns (bool)\n {\n uint160 currentPrice = strategy.getPoolX96Price();\n return currentPrice > sqrtPriceTargetX96;\n }\n\n function claimGovernanceOnAMO() public {\n originalGovernor = strategy.governor();\n strategy.claimGovernance();\n }\n\n function giveBackGovernanceOnAMO() public {\n require(\n originalGovernor != address(0),\n \"Quoter: Original governor not set\"\n );\n strategy.transferGovernance(originalGovernor);\n }\n}\n\n/// @title AerodromeAMOQuoter\n/// @author Origin Protocol\n/// @notice Quoter for Aerodrome AMO\ncontract AerodromeAMOQuoter {\n ////////////////////////////////////////////////////////////////\n /// --- STRUCTS & ENUMS\n ///////////////////////////////////////////////////////////////\n struct Data {\n uint256 amount;\n uint256 iterations;\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- VARIABLES STORAGE\n ////////////////////////////////////////////////////////////////\n QuoterHelper public immutable quoterHelper;\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////////////////\n constructor(address _strategy, address _quoterV2) {\n quoterHelper = new QuoterHelper(\n IAMOStrategy(_strategy),\n IQuoterV2(_quoterV2)\n );\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- ERRORS & EVENTS\n ////////////////////////////////////////////////////////////////\n event ValueFound(uint256 value, uint256 iterations, bool swapWETHForOETHB);\n event ValueFoundBis(\n uint256 value,\n uint256 iterations,\n bool swapWETHForOETHB,\n uint160 sqrtPriceAfterX96\n );\n event ValueNotFound(string message);\n\n ////////////////////////////////////////////////////////////////\n /// --- FUNCTIONS\n ////////////////////////////////////////////////////////////////\n /// @notice Use this to get the amount to swap before rebalance\n /// @dev This call will only revert, check the logs to get returned values.\n /// @dev Need to perform this call while impersonating the governor or strategist of AMO.\n /// @return data Data struct with the amount and the number of iterations\n function quoteAmountToSwapBeforeRebalance()\n public\n returns (Data memory data)\n {\n return\n _quoteAmountToSwapBeforeRebalance(\n type(uint256).max,\n type(uint256).max\n );\n }\n\n /// @notice Use this to get the amount to swap before rebalance and\n /// update allowedWethShareStart and allowedWethShareEnd on AMO.\n /// @dev This call will only revert, check the logs to get returned values.\n /// @dev Need to perform this call while impersonating the governor of AMO.\n /// @param overrideBottomWethShare New value for the allowedWethShareStart on AMO.\n /// Use type(uint256).max to keep same value.\n /// @param overrideTopWethShare New value for the allowedWethShareEnd on AMO.\n /// Use type(uint256).max to keep same value.\n /// @return data Data struct with the amount and the number of iterations\n function quoteAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) public returns (Data memory data) {\n return\n _quoteAmountToSwapBeforeRebalance(\n overrideBottomWethShare,\n overrideTopWethShare\n );\n }\n\n /// @notice Internal logic for quoteAmountToSwapBeforeRebalance.\n function _quoteAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) internal returns (Data memory data) {\n try\n quoterHelper.getAmountToSwapBeforeRebalance(\n overrideBottomWethShare,\n overrideTopWethShare\n )\n {\n revert(\"Previous call should only revert, it cannot succeed\");\n } catch (bytes memory reason) {\n bytes4 receivedSelector = bytes4(reason);\n if (receivedSelector == QuoterHelper.ValidAmount.selector) {\n uint256 value;\n uint256 iterations;\n bool swapWETHForOETHB;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n value := mload(add(reason, 0x24))\n iterations := mload(add(reason, 0x44))\n swapWETHForOETHB := mload(add(reason, 0x64))\n }\n emit ValueFound(value, iterations, swapWETHForOETHB);\n return Data({ amount: value, iterations: iterations });\n }\n\n if (receivedSelector == QuoterHelper.OutOfIterations.selector) {\n emit ValueNotFound(\"Out of iterations\");\n revert(\"Out of iterations\");\n }\n\n emit ValueNotFound(\"Unexpected error\");\n revert(abi.decode(reason, (string)));\n }\n }\n\n /// @notice Use this to get the amount to swap to reach the target price after swap.\n /// @dev This call will only revert, check the logs to get returned values.\n /// @param sqrtPriceTargetX96 The target price to reach.\n function quoteAmountToSwapToReachPrice(uint160 sqrtPriceTargetX96) public {\n (\n uint256 amount,\n uint256 iterations,\n bool swapWETHForOETHB,\n uint160 sqrtPriceAfterX96\n ) = quoterHelper.getAmountToSwapToReachPrice(sqrtPriceTargetX96);\n\n emit ValueFoundBis(\n amount,\n iterations,\n swapWETHForOETHB,\n sqrtPriceAfterX96\n );\n }\n\n function claimGovernance() public {\n quoterHelper.claimGovernanceOnAMO();\n }\n\n function giveBackGovernance() public {\n quoterHelper.giveBackGovernanceOnAMO();\n }\n}\n" + }, + "contracts/utils/BalancerErrors.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity >=0.7.4 <0.9.0;\n\n// solhint-disable\n\n/**\n * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are\n * supported.\n * Uses the default 'BAL' prefix for the error code\n */\nfunction _require(bool condition, uint256 errorCode) pure {\n if (!condition) _revert(errorCode);\n}\n\n/**\n * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are\n * supported.\n */\nfunction _require(\n bool condition,\n uint256 errorCode,\n bytes3 prefix\n) pure {\n if (!condition) _revert(errorCode, prefix);\n}\n\n/**\n * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported.\n * Uses the default 'BAL' prefix for the error code\n */\nfunction _revert(uint256 errorCode) pure {\n _revert(errorCode, 0x42414c); // This is the raw byte representation of \"BAL\"\n}\n\n/**\n * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported.\n */\nfunction _revert(uint256 errorCode, bytes3 prefix) pure {\n uint256 prefixUint = uint256(uint24(prefix));\n // We're going to dynamically create a revert string based on the error code, with the following format:\n // 'BAL#{errorCode}'\n // where the code is left-padded with zeroes to three digits (so they range from 000 to 999).\n //\n // We don't have revert strings embedded in the contract to save bytecode size: it takes much less space to store a\n // number (8 to 16 bits) than the individual string characters.\n //\n // The dynamic string creation algorithm that follows could be implemented in Solidity, but assembly allows for a\n // much denser implementation, again saving bytecode size. Given this function unconditionally reverts, this is a\n // safe place to rely on it without worrying about how its usage might affect e.g. memory contents.\n assembly {\n // First, we need to compute the ASCII representation of the error code. We assume that it is in the 0-999\n // range, so we only need to convert three digits. To convert the digits to ASCII, we add 0x30, the value for\n // the '0' character.\n\n let units := add(mod(errorCode, 10), 0x30)\n\n errorCode := div(errorCode, 10)\n let tenths := add(mod(errorCode, 10), 0x30)\n\n errorCode := div(errorCode, 10)\n let hundreds := add(mod(errorCode, 10), 0x30)\n\n // With the individual characters, we can now construct the full string.\n // We first append the '#' character (0x23) to the prefix. In the case of 'BAL', it results in 0x42414c23 ('BAL#')\n // Then, we shift this by 24 (to provide space for the 3 bytes of the error code), and add the\n // characters to it, each shifted by a multiple of 8.\n // The revert reason is then shifted left by 200 bits (256 minus the length of the string, 7 characters * 8 bits\n // per character = 56) to locate it in the most significant part of the 256 slot (the beginning of a byte\n // array).\n let formattedPrefix := shl(24, add(0x23, shl(8, prefixUint)))\n\n let revertReason := shl(\n 200,\n add(\n formattedPrefix,\n add(add(units, shl(8, tenths)), shl(16, hundreds))\n )\n )\n\n // We can now encode the reason in memory, which can be safely overwritten as we're about to revert. The encoded\n // message will have the following layout:\n // [ revert reason identifier ] [ string location offset ] [ string length ] [ string contents ]\n\n // The Solidity revert reason identifier is 0x08c739a0, the function selector of the Error(string) function. We\n // also write zeroes to the next 28 bytes of memory, but those are about to be overwritten.\n mstore(\n 0x0,\n 0x08c379a000000000000000000000000000000000000000000000000000000000\n )\n // Next is the offset to the location of the string, which will be placed immediately after (20 bytes away).\n mstore(\n 0x04,\n 0x0000000000000000000000000000000000000000000000000000000000000020\n )\n // The string length is fixed: 7 characters.\n mstore(0x24, 7)\n // Finally, the string itself is stored.\n mstore(0x44, revertReason)\n\n // Even if the string is only 7 bytes long, we need to return a full 32 byte slot containing it. The length of\n // the encoded message is therefore 4 + 32 + 32 + 32 = 100.\n revert(0, 100)\n }\n}\n\nlibrary Errors {\n // Math\n uint256 internal constant ADD_OVERFLOW = 0;\n uint256 internal constant SUB_OVERFLOW = 1;\n uint256 internal constant SUB_UNDERFLOW = 2;\n uint256 internal constant MUL_OVERFLOW = 3;\n uint256 internal constant ZERO_DIVISION = 4;\n uint256 internal constant DIV_INTERNAL = 5;\n uint256 internal constant X_OUT_OF_BOUNDS = 6;\n uint256 internal constant Y_OUT_OF_BOUNDS = 7;\n uint256 internal constant PRODUCT_OUT_OF_BOUNDS = 8;\n uint256 internal constant INVALID_EXPONENT = 9;\n\n // Input\n uint256 internal constant OUT_OF_BOUNDS = 100;\n uint256 internal constant UNSORTED_ARRAY = 101;\n uint256 internal constant UNSORTED_TOKENS = 102;\n uint256 internal constant INPUT_LENGTH_MISMATCH = 103;\n uint256 internal constant ZERO_TOKEN = 104;\n uint256 internal constant INSUFFICIENT_DATA = 105;\n\n // Shared pools\n uint256 internal constant MIN_TOKENS = 200;\n uint256 internal constant MAX_TOKENS = 201;\n uint256 internal constant MAX_SWAP_FEE_PERCENTAGE = 202;\n uint256 internal constant MIN_SWAP_FEE_PERCENTAGE = 203;\n uint256 internal constant MINIMUM_BPT = 204;\n uint256 internal constant CALLER_NOT_VAULT = 205;\n uint256 internal constant UNINITIALIZED = 206;\n uint256 internal constant BPT_IN_MAX_AMOUNT = 207;\n uint256 internal constant BPT_OUT_MIN_AMOUNT = 208;\n uint256 internal constant EXPIRED_PERMIT = 209;\n uint256 internal constant NOT_TWO_TOKENS = 210;\n uint256 internal constant DISABLED = 211;\n\n // Pools\n uint256 internal constant MIN_AMP = 300;\n uint256 internal constant MAX_AMP = 301;\n uint256 internal constant MIN_WEIGHT = 302;\n uint256 internal constant MAX_STABLE_TOKENS = 303;\n uint256 internal constant MAX_IN_RATIO = 304;\n uint256 internal constant MAX_OUT_RATIO = 305;\n uint256 internal constant MIN_BPT_IN_FOR_TOKEN_OUT = 306;\n uint256 internal constant MAX_OUT_BPT_FOR_TOKEN_IN = 307;\n uint256 internal constant NORMALIZED_WEIGHT_INVARIANT = 308;\n uint256 internal constant INVALID_TOKEN = 309;\n uint256 internal constant UNHANDLED_JOIN_KIND = 310;\n uint256 internal constant ZERO_INVARIANT = 311;\n uint256 internal constant ORACLE_INVALID_SECONDS_QUERY = 312;\n uint256 internal constant ORACLE_NOT_INITIALIZED = 313;\n uint256 internal constant ORACLE_QUERY_TOO_OLD = 314;\n uint256 internal constant ORACLE_INVALID_INDEX = 315;\n uint256 internal constant ORACLE_BAD_SECS = 316;\n uint256 internal constant AMP_END_TIME_TOO_CLOSE = 317;\n uint256 internal constant AMP_ONGOING_UPDATE = 318;\n uint256 internal constant AMP_RATE_TOO_HIGH = 319;\n uint256 internal constant AMP_NO_ONGOING_UPDATE = 320;\n uint256 internal constant STABLE_INVARIANT_DIDNT_CONVERGE = 321;\n uint256 internal constant STABLE_GET_BALANCE_DIDNT_CONVERGE = 322;\n uint256 internal constant RELAYER_NOT_CONTRACT = 323;\n uint256 internal constant BASE_POOL_RELAYER_NOT_CALLED = 324;\n uint256 internal constant REBALANCING_RELAYER_REENTERED = 325;\n uint256 internal constant GRADUAL_UPDATE_TIME_TRAVEL = 326;\n uint256 internal constant SWAPS_DISABLED = 327;\n uint256 internal constant CALLER_IS_NOT_LBP_OWNER = 328;\n uint256 internal constant PRICE_RATE_OVERFLOW = 329;\n uint256 internal constant INVALID_JOIN_EXIT_KIND_WHILE_SWAPS_DISABLED = 330;\n uint256 internal constant WEIGHT_CHANGE_TOO_FAST = 331;\n uint256 internal constant LOWER_GREATER_THAN_UPPER_TARGET = 332;\n uint256 internal constant UPPER_TARGET_TOO_HIGH = 333;\n uint256 internal constant UNHANDLED_BY_LINEAR_POOL = 334;\n uint256 internal constant OUT_OF_TARGET_RANGE = 335;\n uint256 internal constant UNHANDLED_EXIT_KIND = 336;\n uint256 internal constant UNAUTHORIZED_EXIT = 337;\n uint256 internal constant MAX_MANAGEMENT_SWAP_FEE_PERCENTAGE = 338;\n uint256 internal constant UNHANDLED_BY_MANAGED_POOL = 339;\n uint256 internal constant UNHANDLED_BY_PHANTOM_POOL = 340;\n uint256 internal constant TOKEN_DOES_NOT_HAVE_RATE_PROVIDER = 341;\n uint256 internal constant INVALID_INITIALIZATION = 342;\n uint256 internal constant OUT_OF_NEW_TARGET_RANGE = 343;\n uint256 internal constant FEATURE_DISABLED = 344;\n uint256 internal constant UNINITIALIZED_POOL_CONTROLLER = 345;\n uint256 internal constant SET_SWAP_FEE_DURING_FEE_CHANGE = 346;\n uint256 internal constant SET_SWAP_FEE_PENDING_FEE_CHANGE = 347;\n uint256 internal constant CHANGE_TOKENS_DURING_WEIGHT_CHANGE = 348;\n uint256 internal constant CHANGE_TOKENS_PENDING_WEIGHT_CHANGE = 349;\n uint256 internal constant MAX_WEIGHT = 350;\n uint256 internal constant UNAUTHORIZED_JOIN = 351;\n uint256 internal constant MAX_MANAGEMENT_AUM_FEE_PERCENTAGE = 352;\n uint256 internal constant FRACTIONAL_TARGET = 353;\n uint256 internal constant ADD_OR_REMOVE_BPT = 354;\n uint256 internal constant INVALID_CIRCUIT_BREAKER_BOUNDS = 355;\n uint256 internal constant CIRCUIT_BREAKER_TRIPPED = 356;\n uint256 internal constant MALICIOUS_QUERY_REVERT = 357;\n uint256 internal constant JOINS_EXITS_DISABLED = 358;\n\n // Lib\n uint256 internal constant REENTRANCY = 400;\n uint256 internal constant SENDER_NOT_ALLOWED = 401;\n uint256 internal constant PAUSED = 402;\n uint256 internal constant PAUSE_WINDOW_EXPIRED = 403;\n uint256 internal constant MAX_PAUSE_WINDOW_DURATION = 404;\n uint256 internal constant MAX_BUFFER_PERIOD_DURATION = 405;\n uint256 internal constant INSUFFICIENT_BALANCE = 406;\n uint256 internal constant INSUFFICIENT_ALLOWANCE = 407;\n uint256 internal constant ERC20_TRANSFER_FROM_ZERO_ADDRESS = 408;\n uint256 internal constant ERC20_TRANSFER_TO_ZERO_ADDRESS = 409;\n uint256 internal constant ERC20_MINT_TO_ZERO_ADDRESS = 410;\n uint256 internal constant ERC20_BURN_FROM_ZERO_ADDRESS = 411;\n uint256 internal constant ERC20_APPROVE_FROM_ZERO_ADDRESS = 412;\n uint256 internal constant ERC20_APPROVE_TO_ZERO_ADDRESS = 413;\n uint256 internal constant ERC20_TRANSFER_EXCEEDS_ALLOWANCE = 414;\n uint256 internal constant ERC20_DECREASED_ALLOWANCE_BELOW_ZERO = 415;\n uint256 internal constant ERC20_TRANSFER_EXCEEDS_BALANCE = 416;\n uint256 internal constant ERC20_BURN_EXCEEDS_ALLOWANCE = 417;\n uint256 internal constant SAFE_ERC20_CALL_FAILED = 418;\n uint256 internal constant ADDRESS_INSUFFICIENT_BALANCE = 419;\n uint256 internal constant ADDRESS_CANNOT_SEND_VALUE = 420;\n uint256 internal constant SAFE_CAST_VALUE_CANT_FIT_INT256 = 421;\n uint256 internal constant GRANT_SENDER_NOT_ADMIN = 422;\n uint256 internal constant REVOKE_SENDER_NOT_ADMIN = 423;\n uint256 internal constant RENOUNCE_SENDER_NOT_ALLOWED = 424;\n uint256 internal constant BUFFER_PERIOD_EXPIRED = 425;\n uint256 internal constant CALLER_IS_NOT_OWNER = 426;\n uint256 internal constant NEW_OWNER_IS_ZERO = 427;\n uint256 internal constant CODE_DEPLOYMENT_FAILED = 428;\n uint256 internal constant CALL_TO_NON_CONTRACT = 429;\n uint256 internal constant LOW_LEVEL_CALL_FAILED = 430;\n uint256 internal constant NOT_PAUSED = 431;\n uint256 internal constant ADDRESS_ALREADY_ALLOWLISTED = 432;\n uint256 internal constant ADDRESS_NOT_ALLOWLISTED = 433;\n uint256 internal constant ERC20_BURN_EXCEEDS_BALANCE = 434;\n uint256 internal constant INVALID_OPERATION = 435;\n uint256 internal constant CODEC_OVERFLOW = 436;\n uint256 internal constant IN_RECOVERY_MODE = 437;\n uint256 internal constant NOT_IN_RECOVERY_MODE = 438;\n uint256 internal constant INDUCED_FAILURE = 439;\n uint256 internal constant EXPIRED_SIGNATURE = 440;\n uint256 internal constant MALFORMED_SIGNATURE = 441;\n uint256 internal constant SAFE_CAST_VALUE_CANT_FIT_UINT64 = 442;\n uint256 internal constant UNHANDLED_FEE_TYPE = 443;\n uint256 internal constant BURN_FROM_ZERO = 444;\n\n // Vault\n uint256 internal constant INVALID_POOL_ID = 500;\n uint256 internal constant CALLER_NOT_POOL = 501;\n uint256 internal constant SENDER_NOT_ASSET_MANAGER = 502;\n uint256 internal constant USER_DOESNT_ALLOW_RELAYER = 503;\n uint256 internal constant INVALID_SIGNATURE = 504;\n uint256 internal constant EXIT_BELOW_MIN = 505;\n uint256 internal constant JOIN_ABOVE_MAX = 506;\n uint256 internal constant SWAP_LIMIT = 507;\n uint256 internal constant SWAP_DEADLINE = 508;\n uint256 internal constant CANNOT_SWAP_SAME_TOKEN = 509;\n uint256 internal constant UNKNOWN_AMOUNT_IN_FIRST_SWAP = 510;\n uint256 internal constant MALCONSTRUCTED_MULTIHOP_SWAP = 511;\n uint256 internal constant INTERNAL_BALANCE_OVERFLOW = 512;\n uint256 internal constant INSUFFICIENT_INTERNAL_BALANCE = 513;\n uint256 internal constant INVALID_ETH_INTERNAL_BALANCE = 514;\n uint256 internal constant INVALID_POST_LOAN_BALANCE = 515;\n uint256 internal constant INSUFFICIENT_ETH = 516;\n uint256 internal constant UNALLOCATED_ETH = 517;\n uint256 internal constant ETH_TRANSFER = 518;\n uint256 internal constant CANNOT_USE_ETH_SENTINEL = 519;\n uint256 internal constant TOKENS_MISMATCH = 520;\n uint256 internal constant TOKEN_NOT_REGISTERED = 521;\n uint256 internal constant TOKEN_ALREADY_REGISTERED = 522;\n uint256 internal constant TOKENS_ALREADY_SET = 523;\n uint256 internal constant TOKENS_LENGTH_MUST_BE_2 = 524;\n uint256 internal constant NONZERO_TOKEN_BALANCE = 525;\n uint256 internal constant BALANCE_TOTAL_OVERFLOW = 526;\n uint256 internal constant POOL_NO_TOKENS = 527;\n uint256 internal constant INSUFFICIENT_FLASH_LOAN_BALANCE = 528;\n\n // Fees\n uint256 internal constant SWAP_FEE_PERCENTAGE_TOO_HIGH = 600;\n uint256 internal constant FLASH_LOAN_FEE_PERCENTAGE_TOO_HIGH = 601;\n uint256 internal constant INSUFFICIENT_FLASH_LOAN_FEE_AMOUNT = 602;\n uint256 internal constant AUM_FEE_PERCENTAGE_TOO_HIGH = 603;\n\n // FeeSplitter\n uint256 internal constant SPLITTER_FEE_PERCENTAGE_TOO_HIGH = 700;\n\n // Misc\n uint256 internal constant UNIMPLEMENTED = 998;\n uint256 internal constant SHOULD_NOT_HAPPEN = 999;\n}\n" + }, + "contracts/utils/DepositContractUtils.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract DepositContractUtils {\n function calculateDepositDataRoot(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature\n ) public pure returns (bytes32 node) {\n uint256 deposit_amount = 32 ether / 1 gwei;\n bytes memory amount = to_little_endian_64(uint64(deposit_amount));\n\n // Compute deposit data root (`DepositData` hash tree root)\n bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, bytes16(0)));\n bytes32 signature_root = sha256(\n abi.encodePacked(\n sha256(abi.encodePacked(signature[:64])),\n sha256(abi.encodePacked(signature[64:], bytes32(0)))\n )\n );\n node = sha256(\n abi.encodePacked(\n sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)),\n sha256(abi.encodePacked(amount, bytes24(0), signature_root))\n )\n );\n }\n\n function to_little_endian_64(uint64 value)\n internal\n pure\n returns (bytes memory ret)\n {\n ret = new bytes(8);\n bytes8 bytesValue = bytes8(value);\n // Byteswapping during copying to bytes.\n ret[0] = bytesValue[7];\n ret[1] = bytesValue[6];\n ret[2] = bytesValue[5];\n ret[3] = bytesValue[4];\n ret[4] = bytesValue[3];\n ret[5] = bytesValue[2];\n ret[6] = bytesValue[1];\n ret[7] = bytesValue[0];\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/utils/InitializableERC20Detailed.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @dev Optional functions from the ERC20 standard.\n * Converted from openzeppelin/contracts/token/ERC20/ERC20Detailed.sol\n * @author Origin Protocol Inc\n */\nabstract contract InitializableERC20Detailed is IERC20 {\n // Storage gap to skip storage from prior to OUSD reset\n uint256[100] private _____gap;\n\n string private _name;\n string private _symbol;\n uint8 private _decimals;\n\n /**\n * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of\n * these values are immutable: they can only be set once during\n * construction.\n * @notice To avoid variable shadowing appended `Arg` after arguments name.\n */\n function _initialize(\n string memory nameArg,\n string memory symbolArg,\n uint8 decimalsArg\n ) internal {\n _name = nameArg;\n _symbol = symbolArg;\n _decimals = decimalsArg;\n }\n\n /**\n * @notice Returns the name of the token.\n */\n function name() public view returns (string memory) {\n return _name;\n }\n\n /**\n * @notice Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view returns (string memory) {\n return _symbol;\n }\n\n /**\n * @notice Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5,05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei.\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view returns (uint8) {\n return _decimals;\n }\n}\n" + }, + "contracts/utils/PRBMath.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// Copied from the PRBMath library\n// https://github.com/PaulRBerg/prb-math/blob/main/src/Common.sol\n\n/// @notice Calculates the square root of x using the Babylonian method.\n///\n/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.\n///\n/// Notes:\n/// - If x is not a perfect square, the result is rounded down.\n/// - Credits to OpenZeppelin for the explanations in comments below.\n///\n/// @param x The uint256 number for which to calculate the square root.\n/// @return result The result as a uint256.\n/// @custom:smtchecker abstract-function-nondet\nfunction sqrt(uint256 x) pure returns (uint256 result) {\n if (x == 0) {\n return 0;\n }\n\n // For our first guess, we calculate the biggest power of 2 which is smaller than the square root of x.\n //\n // We know that the \"msb\" (most significant bit) of x is a power of 2 such that we have:\n //\n // $$\n // msb(x) <= x <= 2*msb(x)$\n // $$\n //\n // We write $msb(x)$ as $2^k$, and we get:\n //\n // $$\n // k = log_2(x)\n // $$\n //\n // Thus, we can write the initial inequality as:\n //\n // $$\n // 2^{log_2(x)} <= x <= 2*2^{log_2(x)+1} \\\\\n // sqrt(2^k) <= sqrt(x) < sqrt(2^{k+1}) \\\\\n // 2^{k/2} <= sqrt(x) < 2^{(k+1)/2} <= 2^{(k/2)+1}\n // $$\n //\n // Consequently, $2^{log_2(x) /2} is a good first approximation of sqrt(x) with at least one correct bit.\n uint256 xAux = uint256(x);\n result = 1;\n if (xAux >= 2**128) {\n xAux >>= 128;\n result <<= 64;\n }\n if (xAux >= 2**64) {\n xAux >>= 64;\n result <<= 32;\n }\n if (xAux >= 2**32) {\n xAux >>= 32;\n result <<= 16;\n }\n if (xAux >= 2**16) {\n xAux >>= 16;\n result <<= 8;\n }\n if (xAux >= 2**8) {\n xAux >>= 8;\n result <<= 4;\n }\n if (xAux >= 2**4) {\n xAux >>= 4;\n result <<= 2;\n }\n if (xAux >= 2**2) {\n result <<= 1;\n }\n\n // At this point, `result` is an estimation with at least one bit of precision. We know the true value has at\n // most 128 bits, since it is the square root of a uint256. Newton's method converges quadratically (precision\n // doubles at every iteration). We thus need at most 7 iteration to turn our partial result with one bit of\n // precision into the expected uint128 result.\n unchecked {\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n\n // If x is not a perfect square, round the result toward zero.\n uint256 roundedResult = x / result;\n if (result >= roundedResult) {\n result = roundedResult;\n }\n }\n}\n" + }, + "contracts/utils/StableMath.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\n// Based on StableMath from Stability Labs Pty. Ltd.\n// https://github.com/mstable/mStable-contracts/blob/master/contracts/shared/StableMath.sol\n\nlibrary StableMath {\n using SafeMath for uint256;\n\n /**\n * @dev Scaling unit for use in specific calculations,\n * where 1 * 10**18, or 1e18 represents a unit '1'\n */\n uint256 private constant FULL_SCALE = 1e18;\n\n /***************************************\n Helpers\n ****************************************/\n\n /**\n * @dev Adjust the scale of an integer\n * @param to Decimals to scale to\n * @param from Decimals to scale from\n */\n function scaleBy(\n uint256 x,\n uint256 to,\n uint256 from\n ) internal pure returns (uint256) {\n if (to > from) {\n x = x.mul(10**(to - from));\n } else if (to < from) {\n // slither-disable-next-line divide-before-multiply\n x = x.div(10**(from - to));\n }\n return x;\n }\n\n /***************************************\n Precise Arithmetic\n ****************************************/\n\n /**\n * @dev Multiplies two precise units, and then truncates by the full scale\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit\n */\n function mulTruncate(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulTruncateScale(x, y, FULL_SCALE);\n }\n\n /**\n * @dev Multiplies two precise units, and then truncates by the given scale. For example,\n * when calculating 90% of 10e18, (10e18 * 9e17) / 1e18 = (9e36) / 1e18 = 9e18\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @param scale Scale unit\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit\n */\n function mulTruncateScale(\n uint256 x,\n uint256 y,\n uint256 scale\n ) internal pure returns (uint256) {\n // e.g. assume scale = fullScale\n // z = 10e18 * 9e17 = 9e36\n uint256 z = x.mul(y);\n // return 9e36 / 1e18 = 9e18\n return z.div(scale);\n }\n\n /**\n * @dev Multiplies two precise units, and then truncates by the full scale, rounding up the result\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit, rounded up to the closest base unit.\n */\n function mulTruncateCeil(uint256 x, uint256 y)\n internal\n pure\n returns (uint256)\n {\n // e.g. 8e17 * 17268172638 = 138145381104e17\n uint256 scaled = x.mul(y);\n // e.g. 138145381104e17 + 9.99...e17 = 138145381113.99...e17\n uint256 ceil = scaled.add(FULL_SCALE.sub(1));\n // e.g. 13814538111.399...e18 / 1e18 = 13814538111\n return ceil.div(FULL_SCALE);\n }\n\n /**\n * @dev Precisely divides two units, by first scaling the left hand operand. Useful\n * for finding percentage weightings, i.e. 8e18/10e18 = 80% (or 8e17)\n * @param x Left hand input to division\n * @param y Right hand input to division\n * @return Result after multiplying the left operand by the scale, and\n * executing the division on the right hand input.\n */\n function divPrecisely(uint256 x, uint256 y)\n internal\n pure\n returns (uint256)\n {\n // e.g. 8e18 * 1e18 = 8e36\n uint256 z = x.mul(FULL_SCALE);\n // e.g. 8e36 / 10e18 = 8e17\n return z.div(y);\n }\n}\n" + }, + "contracts/vault/OETHBaseVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"./OETHVaultAdmin.sol\";\n\n/**\n * @title OETH Base VaultAdmin Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBaseVaultAdmin is OETHVaultAdmin {\n constructor(address _weth) OETHVaultAdmin(_weth) {}\n}\n" + }, + "contracts/vault/OETHBaseVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { OETHVaultCore } from \"./OETHVaultCore.sol\";\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\n\n/**\n * @title OETH Base VaultCore Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBaseVaultCore is OETHVaultCore {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(address _weth) OETHVaultCore(_weth) {}\n\n // @inheritdoc OETHVaultCore\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n override\n {\n // Only Strategist or Governor can redeem using the Vault for now.\n // We don't have the onlyGovernorOrStrategist modifier on VaultCore.\n // Since we won't be using that modifier anywhere in the VaultCore as well,\n // the check has been added inline instead of moving it to VaultStorage.\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n\n super._redeem(_amount, _minimumUnitAmount);\n }\n}\n" + }, + "contracts/vault/OETHBaseZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\n\ncontract OETHBaseZapper {\n IERC20 public immutable oethb;\n IERC4626 public immutable woethb;\n IVault public immutable vault;\n\n IWETH9 public constant weth =\n IWETH9(0x4200000000000000000000000000000000000006);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(\n address _oethb,\n address _woethb,\n address _vault\n ) {\n oethb = IERC20(_oethb);\n woethb = IERC4626(_woethb);\n vault = IVault(_vault);\n\n weth.approve(address(_vault), type(uint256).max);\n IERC20(_oethb).approve(_woethb, type(uint256).max);\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return.\n * Will verify that the user is sent 1:1 for ETH.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit ETH and receive superOETHb in return\n * Will verify that the user is sent 1:1 for ETH.\n * @return Amount of OETH sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap ETH\n weth.deposit{ value: balance }();\n\n // Mint with WETH\n return _mint(balance, msg.sender);\n }\n\n /**\n * @dev Deposit ETH and receive superOETHb in return\n * @param minReceived min amount of wsuperOETHb to receive\n * @return Amount of wsuperOETHb sent to user\n */\n function depositETHForWrappedTokens(uint256 minReceived)\n external\n payable\n returns (uint256)\n {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap ETH\n weth.deposit{ value: balance }();\n\n // Mint with WETH\n uint256 mintedOethb = _mint(balance, address(this));\n\n // Wrap superOETHb into wsuperOETHb\n uint256 mintedWoethb = woethb.deposit(mintedOethb, msg.sender);\n\n require(mintedWoethb >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWoethb;\n }\n\n /**\n * @dev Deposit WETH and receive superOETHb in return\n * @param wethAmount Amount of WETH to deposit\n * @param minReceived min amount of wsuperOETHb to receive\n * @return Amount of wsuperOETHb sent to user\n */\n function depositWETHForWrappedTokens(\n uint256 wethAmount,\n uint256 minReceived\n ) external returns (uint256) {\n // slither-disable-next-line unchecked-transfer unused-return\n weth.transferFrom(msg.sender, address(this), wethAmount);\n\n emit Zap(msg.sender, address(weth), wethAmount);\n\n // Mint with WETH\n uint256 mintedOethb = _mint(wethAmount, address(this));\n\n // Wrap superOETHb into wsuperOETHb\n uint256 mintedWoethb = woethb.deposit(mintedOethb, msg.sender);\n\n require(mintedWoethb >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWoethb;\n }\n\n /**\n * @dev Internal function to mint superOETHb with WETH\n * @param minOETH Minimum amount of OETH to for user to receive\n * @param recipient Address that receives the tokens\n * @return Amount of OETH sent to user\n */\n function _mint(uint256 minOETH, address recipient)\n internal\n returns (uint256)\n {\n uint256 toMint = weth.balanceOf(address(this));\n vault.mint(address(weth), toMint, minOETH);\n uint256 mintedAmount = oethb.balanceOf(address(this));\n require(mintedAmount >= minOETH, \"Zapper: not enough minted\");\n\n if (recipient != address(this)) {\n require(oethb.transfer(recipient, mintedAmount));\n }\n\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/OETHVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Vault } from \"./Vault.sol\";\n\n/**\n * @title OETH Vault Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVault is Vault {\n\n}\n" + }, + "contracts/vault/OETHVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { VaultAdmin } from \"./VaultAdmin.sol\";\n\n/**\n * @title OETH VaultAdmin Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVaultAdmin is VaultAdmin {\n using SafeERC20 for IERC20;\n\n address public immutable weth;\n\n constructor(address _weth) {\n weth = _weth;\n }\n\n /**\n * @notice Adds a strategy to the mint whitelist.\n * Reverts if strategy isn't approved on Vault.\n * @param strategyAddr Strategy address\n */\n function addStrategyToMintWhitelist(address strategyAddr)\n external\n onlyGovernor\n {\n require(strategies[strategyAddr].isSupported, \"Strategy not approved\");\n\n require(\n !isMintWhitelistedStrategy[strategyAddr],\n \"Already whitelisted\"\n );\n\n isMintWhitelistedStrategy[strategyAddr] = true;\n\n emit StrategyAddedToMintWhitelist(strategyAddr);\n }\n\n /**\n * @notice Removes a strategy from the mint whitelist.\n * @param strategyAddr Strategy address\n */\n function removeStrategyFromMintWhitelist(address strategyAddr)\n external\n onlyGovernor\n {\n // Intentionally skipping `strategies.isSupported` check since\n // we may wanna remove an address even after removing the strategy\n\n require(isMintWhitelistedStrategy[strategyAddr], \"Not whitelisted\");\n\n isMintWhitelistedStrategy[strategyAddr] = false;\n\n emit StrategyRemovedFromMintWhitelist(strategyAddr);\n }\n\n /// @dev Simplified version of the deposit function as WETH is the only supported asset.\n function _depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal override {\n require(\n strategies[_strategyToAddress].isSupported,\n \"Invalid to Strategy\"\n );\n require(\n _assets.length == 1 && _amounts.length == 1 && _assets[0] == weth,\n \"Only WETH is supported\"\n );\n\n // Check the there is enough WETH to transfer once the WETH reserved for the withdrawal queue is accounted for\n require(_amounts[0] <= _wethAvailable(), \"Not enough WETH available\");\n\n // Send required amount of funds to the strategy\n IERC20(weth).safeTransfer(_strategyToAddress, _amounts[0]);\n\n // Deposit all the funds that have been sent to the strategy\n IStrategy(_strategyToAddress).depositAll();\n }\n\n function _withdrawFromStrategy(\n address _recipient,\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal override {\n super._withdrawFromStrategy(\n _recipient,\n _strategyFromAddress,\n _assets,\n _amounts\n );\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n function _withdrawAllFromStrategy(address _strategyAddr) internal override {\n super._withdrawAllFromStrategy(_strategyAddr);\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n function _withdrawAllFromStrategies() internal override {\n super._withdrawAllFromStrategies();\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n /// @dev Calculate how much WETH in the vault is not reserved for the withdrawal queue.\n // That is, it is available to be redeemed or deposited into a strategy.\n function _wethAvailable() internal view returns (uint256 wethAvailable) {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n uint256 outstandingWithdrawals = queue.queued - queue.claimed;\n\n // The amount of sitting in WETH in the vault\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // If there is not enough WETH in the vault to cover the outstanding withdrawals\n if (wethBalance <= outstandingWithdrawals) {\n return 0;\n }\n\n return wethBalance - outstandingWithdrawals;\n }\n\n function _swapCollateral(\n address,\n address,\n uint256,\n uint256,\n bytes calldata\n ) internal pure override returns (uint256) {\n revert(\"Collateral swap not supported\");\n }\n}\n" + }, + "contracts/vault/OETHVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { VaultCore } from \"./VaultCore.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\n\n/**\n * @title OETH VaultCore Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVaultCore is VaultCore {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n address public immutable weth;\n uint256 public wethAssetIndex;\n\n // For future use (because OETHBaseVaultCore inherits from this)\n uint256[50] private __gap;\n\n constructor(address _weth) {\n weth = _weth;\n }\n\n /**\n * @dev Caches WETH's index in `allAssets` variable.\n * Reduces gas usage by redeem by caching that.\n */\n function cacheWETHAssetIndex() external onlyGovernor {\n uint256 assetCount = allAssets.length;\n for (uint256 i; i < assetCount; ++i) {\n if (allAssets[i] == weth) {\n wethAssetIndex = i;\n break;\n }\n }\n\n require(allAssets[wethAssetIndex] == weth, \"Invalid WETH Asset Index\");\n }\n\n // @inheritdoc VaultCore\n function mintForStrategy(uint256 amount)\n external\n override\n whenNotCapitalPaused\n {\n require(\n strategies[msg.sender].isSupported == true,\n \"Unsupported strategy\"\n );\n require(\n isMintWhitelistedStrategy[msg.sender] == true,\n \"Not whitelisted strategy\"\n );\n\n emit Mint(msg.sender, amount);\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, amount);\n }\n\n // @inheritdoc VaultCore\n function burnForStrategy(uint256 amount)\n external\n override\n whenNotCapitalPaused\n {\n require(\n strategies[msg.sender].isSupported == true,\n \"Unsupported strategy\"\n );\n require(\n isMintWhitelistedStrategy[msg.sender] == true,\n \"Not whitelisted strategy\"\n );\n\n emit Redeem(msg.sender, amount);\n\n // Burn OTokens\n oUSD.burn(msg.sender, amount);\n }\n\n // @inheritdoc VaultCore\n // slither-disable-start reentrancy-no-eth\n function _mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) internal virtual override {\n require(_asset == weth, \"Unsupported asset for minting\");\n require(_amount > 0, \"Amount must be greater than 0\");\n require(\n _amount >= _minimumOusdAmount,\n \"Mint amount lower than minimum\"\n );\n\n emit Mint(msg.sender, _amount);\n\n // Rebase must happen before any transfers occur.\n if (!rebasePaused && _amount >= rebaseThreshold) {\n _rebase();\n }\n\n // Mint oTokens\n oUSD.mint(msg.sender, _amount);\n\n // Transfer the deposited coins to the vault\n IERC20(_asset).safeTransferFrom(msg.sender, address(this), _amount);\n\n // Give priority to the withdrawal queue for the new WETH liquidity\n _addWithdrawalQueueLiquidity();\n\n // Auto-allocate if necessary\n if (_amount >= autoAllocateThreshold) {\n _allocate();\n }\n }\n\n // slither-disable-end reentrancy-no-eth\n\n // @inheritdoc VaultCore\n function _calculateRedeemOutputs(uint256 _amount)\n internal\n view\n virtual\n override\n returns (uint256[] memory outputs)\n {\n // Overrides `VaultCore._calculateRedeemOutputs` to redeem with only\n // WETH instead of LST-mix. Doesn't change the function signature\n // for backward compatibility\n\n // Calculate redeem fee\n if (redeemFeeBps > 0) {\n uint256 redeemFee = _amount.mulTruncateScale(redeemFeeBps, 1e4);\n _amount = _amount - redeemFee;\n }\n\n // Ensure that the WETH index is cached\n uint256 _wethAssetIndex = wethAssetIndex;\n require(\n allAssets[_wethAssetIndex] == weth,\n \"WETH Asset index not cached\"\n );\n\n outputs = new uint256[](allAssets.length);\n outputs[_wethAssetIndex] = _amount;\n }\n\n // @inheritdoc VaultCore\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n override\n {\n // Override `VaultCore._redeem` to simplify it. Gets rid of oracle\n // usage and looping through all assets for LST-mix redeem. Instead\n // does a simple WETH-only redeem.\n emit Redeem(msg.sender, _amount);\n\n if (_amount == 0) {\n return;\n }\n\n // Amount excluding fees\n // No fee for the strategist or the governor, makes it easier to do operations\n uint256 amountMinusFee = (msg.sender == strategistAddr || isGovernor())\n ? _amount\n : _calculateRedeemOutputs(_amount)[wethAssetIndex];\n\n require(\n amountMinusFee >= _minimumUnitAmount,\n \"Redeem amount lower than minimum\"\n );\n\n // Is there enough WETH in the Vault available after accounting for the withdrawal queue\n require(_wethAvailable() >= amountMinusFee, \"Liquidity error\");\n\n // Transfer WETH minus the fee to the redeemer\n IERC20(weth).safeTransfer(msg.sender, amountMinusFee);\n\n // Burn OETH from user (including fees)\n oUSD.burn(msg.sender, _amount);\n\n // Prevent insolvency\n _postRedeem(_amount);\n }\n\n /**\n * @notice Request an asynchronous withdrawal of WETH in exchange for OETH.\n * The OETH is burned on request and the WETH is transferred to the withdrawer on claim.\n * This request can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal this request's `queued` amount.\n * There is a minimum of 10 minutes before a request can be claimed. After that, the request just needs\n * enough WETH liquidity in the Vault to satisfy all the outstanding requests to that point in the queue.\n * OETH is converted to WETH at 1:1.\n * @param _amount Amount of OETH to burn.\n * @return requestId Unique ID for the withdrawal request\n * @return queued Cumulative total of all WETH queued including already claimed requests.\n */\n function requestWithdrawal(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256 requestId, uint256 queued)\n {\n require(withdrawalClaimDelay > 0, \"Async withdrawals not enabled\");\n\n // The check that the requester has enough OETH is done in to later burn call\n\n requestId = withdrawalQueueMetadata.nextWithdrawalIndex;\n queued = withdrawalQueueMetadata.queued + _amount;\n\n // Store the next withdrawal request\n withdrawalQueueMetadata.nextWithdrawalIndex = SafeCast.toUint128(\n requestId + 1\n );\n // Store the updated queued amount which reserves WETH in the withdrawal queue\n // and reduces the vault's total assets\n withdrawalQueueMetadata.queued = SafeCast.toUint128(queued);\n // Store the user's withdrawal request\n withdrawalRequests[requestId] = WithdrawalRequest({\n withdrawer: msg.sender,\n claimed: false,\n timestamp: uint40(block.timestamp),\n amount: SafeCast.toUint128(_amount),\n queued: SafeCast.toUint128(queued)\n });\n\n // Burn the user's OETH\n oUSD.burn(msg.sender, _amount);\n\n // Prevent withdrawal if the vault is solvent by more than the the allowed percentage\n _postRedeem(_amount);\n\n emit WithdrawalRequested(msg.sender, requestId, _amount, queued);\n }\n\n // slither-disable-start reentrancy-no-eth\n /**\n * @notice Claim a previously requested withdrawal once it is claimable.\n * This request can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal this request's `queued` amount and 10 minutes has passed.\n * If the requests is not claimable, the transaction will revert with `Queue pending liquidity`.\n * If the request is not older than 10 minutes, the transaction will revert with `Claim delay not met`.\n * OETH is converted to WETH at 1:1.\n * @param _requestId Unique ID for the withdrawal request\n * @return amount Amount of WETH transferred to the withdrawer\n */\n function claimWithdrawal(uint256 _requestId)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256 amount)\n {\n // Try and get more liquidity if there is not enough available\n if (\n withdrawalRequests[_requestId].queued >\n withdrawalQueueMetadata.claimable\n ) {\n // Add any WETH to the withdrawal queue\n // this needs to remain here as:\n // - Vault can be funded and `addWithdrawalQueueLiquidity` is not externally called\n // - funds can be withdrawn from a strategy\n //\n // Those funds need to be added to withdrawal queue liquidity\n _addWithdrawalQueueLiquidity();\n }\n\n amount = _claimWithdrawal(_requestId);\n\n // transfer WETH from the vault to the withdrawer\n IERC20(weth).safeTransfer(msg.sender, amount);\n\n // Prevent insolvency\n _postRedeem(amount);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @notice Claim a previously requested withdrawals once they are claimable.\n * This requests can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal each request's `queued` amount and 10 minutes has passed.\n * If one of the requests is not claimable, the whole transaction will revert with `Queue pending liquidity`.\n * If one of the requests is not older than 10 minutes,\n * the whole transaction will revert with `Claim delay not met`.\n * @param _requestIds Unique ID of each withdrawal request\n * @return amounts Amount of WETH received for each request\n * @return totalAmount Total amount of WETH transferred to the withdrawer\n */\n function claimWithdrawals(uint256[] calldata _requestIds)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256[] memory amounts, uint256 totalAmount)\n {\n // Add any WETH to the withdrawal queue\n // this needs to remain here as:\n // - Vault can be funded and `addWithdrawalQueueLiquidity` is not externally called\n // - funds can be withdrawn from a strategy\n //\n // Those funds need to be added to withdrawal queue liquidity\n _addWithdrawalQueueLiquidity();\n\n amounts = new uint256[](_requestIds.length);\n for (uint256 i; i < _requestIds.length; ++i) {\n amounts[i] = _claimWithdrawal(_requestIds[i]);\n totalAmount += amounts[i];\n }\n\n // transfer all the claimed WETH from the vault to the withdrawer\n IERC20(weth).safeTransfer(msg.sender, totalAmount);\n\n // Prevent insolvency\n _postRedeem(totalAmount);\n }\n\n function _claimWithdrawal(uint256 requestId)\n internal\n returns (uint256 amount)\n {\n require(withdrawalClaimDelay > 0, \"Async withdrawals not enabled\");\n\n // Load the structs from storage into memory\n WithdrawalRequest memory request = withdrawalRequests[requestId];\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n require(\n request.timestamp + withdrawalClaimDelay <= block.timestamp,\n \"Claim delay not met\"\n );\n // If there isn't enough reserved liquidity in the queue to claim\n require(request.queued <= queue.claimable, \"Queue pending liquidity\");\n require(request.withdrawer == msg.sender, \"Not requester\");\n require(request.claimed == false, \"Already claimed\");\n\n // Store the request as claimed\n withdrawalRequests[requestId].claimed = true;\n // Store the updated claimed amount\n withdrawalQueueMetadata.claimed = queue.claimed + request.amount;\n\n emit WithdrawalClaimed(msg.sender, requestId, request.amount);\n\n return request.amount;\n }\n\n /// @notice Adds WETH to the withdrawal queue if there is a funding shortfall.\n /// @dev is called from the Native Staking strategy when validator withdrawals are processed.\n /// It also called before any WETH is allocated to a strategy.\n function addWithdrawalQueueLiquidity() external {\n _addWithdrawalQueueLiquidity();\n }\n\n /// @dev Adds WETH to the withdrawal queue if there is a funding shortfall.\n /// This assumes 1 WETH equal 1 OETH.\n function _addWithdrawalQueueLiquidity()\n internal\n returns (uint256 addedClaimable)\n {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // Check if the claimable WETH is less than the queued amount\n uint256 queueShortfall = queue.queued - queue.claimable;\n\n // No need to do anything is the withdrawal queue is full funded\n if (queueShortfall == 0) {\n return 0;\n }\n\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // Of the claimable withdrawal requests, how much is unclaimed?\n // That is, the amount of WETH that is currently allocated for the withdrawal queue\n uint256 allocatedWeth = queue.claimable - queue.claimed;\n\n // If there is no unallocated WETH then there is nothing to add to the queue\n if (wethBalance <= allocatedWeth) {\n return 0;\n }\n\n uint256 unallocatedWeth = wethBalance - allocatedWeth;\n\n // the new claimable amount is the smaller of the queue shortfall or unallocated weth\n addedClaimable = queueShortfall < unallocatedWeth\n ? queueShortfall\n : unallocatedWeth;\n uint256 newClaimable = queue.claimable + addedClaimable;\n\n // Store the new claimable amount back to storage\n withdrawalQueueMetadata.claimable = SafeCast.toUint128(newClaimable);\n\n // emit a WithdrawalClaimable event\n emit WithdrawalClaimable(newClaimable, addedClaimable);\n }\n\n /***************************************\n View Functions\n ****************************************/\n\n /// @dev Calculate how much WETH in the vault is not reserved for the withdrawal queue.\n // That is, it is available to be redeemed or deposited into a strategy.\n function _wethAvailable() internal view returns (uint256 wethAvailable) {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n uint256 outstandingWithdrawals = queue.queued - queue.claimed;\n\n // The amount of sitting in WETH in the vault\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // If there is not enough WETH in the vault to cover the outstanding withdrawals\n if (wethBalance <= outstandingWithdrawals) {\n return 0;\n }\n\n return wethBalance - outstandingWithdrawals;\n }\n\n /// @dev Get the balance of an asset held in Vault and all strategies\n /// less any WETH that is reserved for the withdrawal queue.\n /// WETH is the only asset that can return a non-zero balance.\n /// All other assets will return 0 even if there is some dust amounts left in the Vault.\n /// For example, there is 1 wei left of stETH in the OETH Vault but will return 0 in this function.\n ///\n /// If there is not enough WETH in the vault and all strategies to cover all outstanding\n /// withdrawal requests then return a WETH balance of 0\n function _checkBalance(address _asset)\n internal\n view\n override\n returns (uint256 balance)\n {\n if (_asset != weth) {\n return 0;\n }\n\n // Get the WETH in the vault and the strategies\n balance = super._checkBalance(_asset);\n\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // If the vault becomes insolvent enough that the total value in the vault and all strategies\n // is less than the outstanding withdrawals.\n // For example, there was a mass slashing event and most users request a withdrawal.\n if (balance + queue.claimed < queue.queued) {\n return 0;\n }\n\n // Need to remove WETH that is reserved for the withdrawal queue\n return balance + queue.claimed - queue.queued;\n }\n\n /**\n * @notice Allocate unallocated funds on Vault to strategies.\n **/\n function allocate() external override whenNotCapitalPaused nonReentrant {\n // Add any unallocated WETH to the withdrawal queue first\n _addWithdrawalQueueLiquidity();\n\n _allocate();\n }\n\n /// @dev Allocate WETH to the default WETH strategy if there is excess to the Vault buffer.\n /// This is called from either `mint` or `allocate` and assumes `_addWithdrawalQueueLiquidity`\n /// has been called before this function.\n function _allocate() internal override {\n // No need to do anything if no default strategy for WETH\n address depositStrategyAddr = assetDefaultStrategies[weth];\n if (depositStrategyAddr == address(0)) return;\n\n uint256 wethAvailableInVault = _wethAvailable();\n // No need to do anything if there isn't any WETH in the vault to allocate\n if (wethAvailableInVault == 0) return;\n\n // Calculate the target buffer for the vault using the total supply\n uint256 totalSupply = oUSD.totalSupply();\n uint256 targetBuffer = totalSupply.mulTruncate(vaultBuffer);\n\n // If available WETH in the Vault is below or equal the target buffer then there's nothing to allocate\n if (wethAvailableInVault <= targetBuffer) return;\n\n // The amount of assets to allocate to the default strategy\n uint256 allocateAmount = wethAvailableInVault - targetBuffer;\n\n IStrategy strategy = IStrategy(depositStrategyAddr);\n // Transfer WETH to the strategy and call the strategy's deposit function\n IERC20(weth).safeTransfer(address(strategy), allocateAmount);\n strategy.deposit(weth, allocateAmount);\n\n emit AssetAllocated(weth, depositStrategyAddr, allocateAmount);\n }\n\n /// @dev The total value of all WETH held by the vault and all its strategies\n /// less any WETH that is reserved for the withdrawal queue.\n ///\n // If there is not enough WETH in the vault and all strategies to cover all outstanding\n // withdrawal requests then return a total value of 0.\n function _totalValue() internal view override returns (uint256 value) {\n // As WETH is the only asset, just return the WETH balance\n return _checkBalance(weth);\n }\n\n /// @dev Only WETH is supported in the OETH Vault so return the WETH balance only\n /// Any ETH balances in the Vault will be ignored.\n /// Amounts from previously supported vault assets will also be ignored.\n /// For example, there is 1 wei left of stETH in the OETH Vault but is will be ignored.\n function _totalValueInVault()\n internal\n view\n override\n returns (uint256 value)\n {\n value = IERC20(weth).balanceOf(address(this));\n }\n}\n" + }, + "contracts/vault/OETHZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { ISfrxETH } from \"../interfaces/ISfrxETH.sol\";\n\ncontract OETHZapper {\n IERC20 public immutable oeth;\n IVault public immutable vault;\n\n IWETH9 public constant weth =\n IWETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);\n IERC20 public constant frxeth =\n IERC20(0x5E8422345238F34275888049021821E8E08CAa1f);\n ISfrxETH public constant sfrxeth =\n ISfrxETH(0xac3E018457B222d93114458476f3E3416Abbe38F);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(address _oeth, address _vault) {\n oeth = IERC20(_oeth);\n vault = IVault(_vault);\n\n weth.approve(address(_vault), type(uint256).max);\n frxeth.approve(address(_vault), type(uint256).max);\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return.\n * Will verify that the user is sent 1:1 for ETH.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return\n * Will verify that the user is sent 1:1 for ETH.\n * @return Amount of OETH sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n weth.deposit{ value: balance }();\n emit Zap(msg.sender, ETH_MARKER, balance);\n return _mint(address(weth), balance);\n }\n\n /**\n * @dev Deposit SFRXETH to the vault and receive OETH in return\n * @param amount Amount of SFRXETH to deposit\n * @param minOETH Minimum amount of OETH to receive\n * @return Amount of OETH sent to user\n */\n function depositSFRXETH(uint256 amount, uint256 minOETH)\n external\n returns (uint256)\n {\n sfrxeth.redeem(amount, address(this), msg.sender);\n emit Zap(msg.sender, address(sfrxeth), amount);\n return _mint(address(frxeth), minOETH);\n }\n\n /**\n * @dev Internal function to mint OETH from an asset\n * @param asset Address of asset for the vault to mint from\n * @param minOETH Minimum amount of OETH to for user to receive\n * @return Amount of OETH sent to user\n */\n function _mint(address asset, uint256 minOETH) internal returns (uint256) {\n uint256 toMint = IERC20(asset).balanceOf(address(this));\n vault.mint(asset, toMint, minOETH);\n uint256 mintedAmount = oeth.balanceOf(address(this));\n require(mintedAmount >= minOETH, \"Zapper: not enough minted\");\n require(oeth.transfer(msg.sender, mintedAmount));\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/OSonicVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"./OETHVaultAdmin.sol\";\n\n/**\n * @title Origin Sonic VaultAdmin contract on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonicVaultAdmin is OETHVaultAdmin {\n /// @param _wS Sonic's Wrapped S token\n constructor(address _wS) OETHVaultAdmin(_wS) {}\n\n /***************************************\n Asset Config\n ****************************************/\n\n /**\n * @notice Add a supported asset to the contract, i.e. one that can be to mint OTokens.\n * @dev Overridden to remove price provider integration\n * @param _asset Address of asset\n * @param _unitConversion 0 decimals, 1 exchange rate\n */\n function supportAsset(address _asset, uint8 _unitConversion)\n external\n override\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Asset already supported\");\n\n assets[_asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(_unitConversion),\n decimals: 0, // will be overridden in _cacheDecimals\n allowedOracleSlippageBps: 0 // 0% by default\n });\n\n _cacheDecimals(_asset);\n allAssets.push(_asset);\n\n emit AssetSupported(_asset);\n }\n}\n" + }, + "contracts/vault/OSonicVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultCore } from \"./OETHVaultCore.sol\";\n\n/**\n * @title Origin Sonic VaultCore contract on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonicVaultCore is OETHVaultCore {\n /// @param _wS Sonic's Wrapped S token\n constructor(address _wS) OETHVaultCore(_wS) {}\n\n /**\n * @notice Instant redeem is not supported on Sonic.\n * Use the asynchronous `requestWithdrawal` a `claimWithdrawal` instead.\n */\n function _redeem(uint256, uint256) internal override {\n revert(\"unsupported function\");\n }\n}\n" + }, + "contracts/vault/OSonicZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWrappedSonic } from \"../interfaces/sonic/IWrappedSonic.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\n\n/**\n * @title Zapper for Origin Sonic (OS) tokens\n * @author Origin Protocol Inc\n */\ncontract OSonicZapper {\n IERC20 public immutable OS;\n IERC4626 public immutable wOS;\n IVault public immutable vault;\n\n IWrappedSonic public constant wS =\n IWrappedSonic(0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(\n address _OS,\n address _wOS,\n address _vault\n ) {\n OS = IERC20(_OS);\n wOS = IERC4626(_wOS);\n vault = IVault(_vault);\n\n wS.approve(address(_vault), type(uint256).max);\n IERC20(_OS).approve(_wOS, type(uint256).max);\n }\n\n /**\n * @dev Deposit native S currency and receive Origin Sonic (OS) tokens in return.\n * Will verify that the user is sent 1:1 for S.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit native S currency and receive Origin Sonic (OS) tokens in return.\n * Will verify that the user is sent 1:1 for S.\n * @return Amount of Origin Sonic (OS) tokens sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap native S\n wS.deposit{ value: balance }();\n\n // Mint Origin Sonic (OS) with Wrapped Sonic (wS)\n return _mint(balance, msg.sender);\n }\n\n /**\n * @dev Deposit S and receive Wrapped Origin Sonic (wOS) in return\n * @param minReceived min amount of Wrapped Origin Sonic (wOS) to receive\n * @return Amount of Wrapped Origin Sonic (wOS) tokens sent to user\n */\n function depositSForWrappedTokens(uint256 minReceived)\n external\n payable\n returns (uint256)\n {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap S\n wS.deposit{ value: balance }();\n\n // Mint with Wrapped Sonic\n uint256 mintOS = _mint(balance, address(this));\n\n // Wrap Origin Sonic (OS) into Wrapped Origin Sonic (wOS)\n uint256 mintedWOS = wOS.deposit(mintOS, msg.sender);\n\n require(mintedWOS >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWOS;\n }\n\n /**\n * @dev Deposit Wrapped Sonic (wS) tokens and receive Wrapped Origin Sonic (wOS) tokens in return\n * @param wSAmount Amount of Wrapped Sonic (wS) to deposit\n * @param minReceived min amount of Wrapped Origin Sonic (wOS) token to receive\n * @return Amount of Wrapped Origin Sonic (wOS) tokens sent to user\n */\n function depositWSForWrappedTokens(uint256 wSAmount, uint256 minReceived)\n external\n returns (uint256)\n {\n // slither-disable-next-line unchecked-transfer unused-return\n wS.transferFrom(msg.sender, address(this), wSAmount);\n\n emit Zap(msg.sender, address(wS), wSAmount);\n\n // Mint with Wrapped Sonic (wS)\n uint256 mintedOS = _mint(wSAmount, address(this));\n\n // Wrap Origin Sonic (OS) tokens into Wrapped Origin Sonic (wOS) tokens\n uint256 mintedWOS = wOS.deposit(mintedOS, msg.sender);\n\n require(mintedWOS >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWOS;\n }\n\n /**\n * @dev Internal function to mint Origin Sonic (OS) with Wrapped S (wS)\n * @param minOS Minimum amount of Origin Sonic (OS) tokens the user can receive\n * @param recipient Address that receives the tokens\n * @return Amount of Origin Sonic (OS) tokens sent to the recipient\n */\n function _mint(uint256 minOS, address recipient)\n internal\n returns (uint256)\n {\n uint256 toMint = wS.balanceOf(address(this));\n vault.mint(address(wS), toMint, minOS);\n uint256 mintedAmount = OS.balanceOf(address(this));\n require(mintedAmount >= minOS, \"Zapper: not enough minted\");\n\n if (recipient != address(this)) {\n require(OS.transfer(recipient, mintedAmount));\n }\n\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/Vault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD VaultInitializer Contract\n * @notice The VaultInitializer sets up the initial contract.\n * @author Origin Protocol Inc\n */\nimport { VaultInitializer } from \"./VaultInitializer.sol\";\nimport { VaultAdmin } from \"./VaultAdmin.sol\";\n\ncontract Vault is VaultInitializer, VaultAdmin {}\n" + }, + "contracts/vault/VaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultAdmin contract\n * @notice The VaultAdmin contract makes configuration and admin calls on the vault.\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport \"./VaultStorage.sol\";\n\ncontract VaultAdmin is VaultStorage {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /***************************************\n Configuration\n ****************************************/\n\n /**\n * @notice Set address of price provider.\n * @param _priceProvider Address of price provider\n */\n function setPriceProvider(address _priceProvider) external onlyGovernor {\n priceProvider = _priceProvider;\n emit PriceProviderUpdated(_priceProvider);\n }\n\n /**\n * @notice Set a fee in basis points to be charged for a redeem.\n * @param _redeemFeeBps Basis point fee to be charged\n */\n function setRedeemFeeBps(uint256 _redeemFeeBps) external onlyGovernor {\n require(_redeemFeeBps <= 1000, \"Redeem fee should not be over 10%\");\n redeemFeeBps = _redeemFeeBps;\n emit RedeemFeeUpdated(_redeemFeeBps);\n }\n\n /**\n * @notice Set a buffer of assets to keep in the Vault to handle most\n * redemptions without needing to spend gas unwinding assets from a Strategy.\n * @param _vaultBuffer Percentage using 18 decimals. 100% = 1e18.\n */\n function setVaultBuffer(uint256 _vaultBuffer)\n external\n onlyGovernorOrStrategist\n {\n require(_vaultBuffer <= 1e18, \"Invalid value\");\n vaultBuffer = _vaultBuffer;\n emit VaultBufferUpdated(_vaultBuffer);\n }\n\n /**\n * @notice Sets the minimum amount of OTokens in a mint to trigger an\n * automatic allocation of funds afterwords.\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setAutoAllocateThreshold(uint256 _threshold)\n external\n onlyGovernor\n {\n autoAllocateThreshold = _threshold;\n emit AllocateThresholdUpdated(_threshold);\n }\n\n /**\n * @notice Set a minimum amount of OTokens in a mint or redeem that triggers a\n * rebase\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setRebaseThreshold(uint256 _threshold) external onlyGovernor {\n rebaseThreshold = _threshold;\n emit RebaseThresholdUpdated(_threshold);\n }\n\n /**\n * @notice Set address of Strategist\n * @param _address Address of Strategist\n */\n function setStrategistAddr(address _address) external onlyGovernor {\n strategistAddr = _address;\n emit StrategistUpdated(_address);\n }\n\n /**\n * @notice Set the default Strategy for an asset, i.e. the one which the asset\n will be automatically allocated to and withdrawn from\n * @param _asset Address of the asset\n * @param _strategy Address of the Strategy\n */\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external\n onlyGovernorOrStrategist\n {\n emit AssetDefaultStrategyUpdated(_asset, _strategy);\n // If its a zero address being passed for the strategy we are removing\n // the default strategy\n if (_strategy != address(0)) {\n // Make sure the strategy meets some criteria\n require(strategies[_strategy].isSupported, \"Strategy not approved\");\n IStrategy strategy = IStrategy(_strategy);\n require(assets[_asset].isSupported, \"Asset is not supported\");\n require(\n strategy.supportsAsset(_asset),\n \"Asset not supported by Strategy\"\n );\n }\n assetDefaultStrategies[_asset] = _strategy;\n }\n\n /**\n * @notice Set maximum amount of OTokens that can at any point be minted and deployed\n * to strategy (used only by ConvexOUSDMetaStrategy for now).\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setNetOusdMintForStrategyThreshold(uint256 _threshold)\n external\n onlyGovernor\n {\n /**\n * Because `netOusdMintedForStrategy` check in vault core works both ways\n * (positive and negative) the actual impact of the amount of OToken minted\n * could be double the threshold. E.g.:\n * - contract has threshold set to 100\n * - state of netOusdMinted is -90\n * - in effect it can mint 190 OToken and still be within limits\n *\n * We are somewhat mitigating this behaviour by resetting the netOusdMinted\n * counter whenever new threshold is set. So it can only move one threshold\n * amount in each direction. This also enables us to reduce the threshold\n * amount and not have problems with current netOusdMinted being near\n * limits on either side.\n */\n netOusdMintedForStrategy = 0;\n netOusdMintForStrategyThreshold = _threshold;\n emit NetOusdMintForStrategyThresholdChanged(_threshold);\n }\n\n /**\n * @notice Changes the async withdrawal claim period for OETH & superOETHb\n * @param _delay Delay period (should be between 10 mins to 7 days).\n * Set to 0 to disable async withdrawals\n */\n function setWithdrawalClaimDelay(uint256 _delay) external onlyGovernor {\n require(\n _delay == 0 || (_delay >= 10 minutes && _delay <= 15 days),\n \"Invalid claim delay period\"\n );\n withdrawalClaimDelay = _delay;\n emit WithdrawalClaimDelayUpdated(_delay);\n }\n\n /**\n * @notice Set a yield streaming max rate. This spreads yield over\n * time if it is above the max rate.\n * @param yearlyApr in 1e18 notation. 3 * 1e18 = 3% APR\n */\n function setRebaseRateMax(uint256 yearlyApr)\n external\n onlyGovernorOrStrategist\n {\n // The old yield will be at the old rate\n IVault(address(this)).rebase();\n // Change the rate\n uint256 newPerSecond = yearlyApr / 100 / 365 days;\n require(newPerSecond <= MAX_REBASE_PER_SECOND, \"Rate too high\");\n rebasePerSecondMax = newPerSecond.toUint64();\n emit RebasePerSecondMaxChanged(newPerSecond);\n }\n\n /**\n * @notice Set the drip duration period\n * @param _dripDuration Time in seconds to target a constant yield rate\n */\n function setDripDuration(uint256 _dripDuration)\n external\n onlyGovernorOrStrategist\n {\n // The old yield will be at the old rate\n IVault(address(this)).rebase();\n dripDuration = _dripDuration.toUint64();\n emit DripDurationChanged(_dripDuration);\n }\n\n /***************************************\n Swaps\n ****************************************/\n\n /**\n * @notice Strategist swaps collateral assets sitting in the vault.\n * @param _fromAsset The token address of the asset being sold by the vault.\n * @param _toAsset The token address of the asset being purchased by the vault.\n * @param _fromAssetAmount The amount of assets being sold by the vault.\n * @param _minToAssetAmount The minimum amount of assets to be purchased.\n * @param _data implementation specific data. eg 1Inch swap data\n * @return toAssetAmount The amount of toAssets that was received from the swap\n */\n function swapCollateral(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n )\n external\n nonReentrant\n onlyGovernorOrStrategist\n returns (uint256 toAssetAmount)\n {\n toAssetAmount = _swapCollateral(\n _fromAsset,\n _toAsset,\n _fromAssetAmount,\n _minToAssetAmount,\n _data\n );\n }\n\n function _swapCollateral(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n ) internal virtual returns (uint256 toAssetAmount) {\n // Check fromAsset and toAsset are valid\n Asset memory fromAssetConfig = assets[_fromAsset];\n Asset memory toAssetConfig = assets[_toAsset];\n require(fromAssetConfig.isSupported, \"From asset is not supported\");\n require(toAssetConfig.isSupported, \"To asset is not supported\");\n\n // Load swap config into memory to avoid separate SLOADs\n SwapConfig memory config = swapConfig;\n\n // Scope a new block to remove toAssetBalBefore from the scope of swapCollateral.\n // This avoids a stack too deep error.\n {\n uint256 toAssetBalBefore = IERC20(_toAsset).balanceOf(\n address(this)\n );\n\n // Transfer from assets to the swapper contract\n IERC20(_fromAsset).safeTransfer(config.swapper, _fromAssetAmount);\n\n // Call to the Swapper contract to do the actual swap\n // The -1 is required for stETH which sometimes transfers 1 wei less than what was specified.\n // slither-disable-next-line unused-return\n ISwapper(config.swapper).swap(\n _fromAsset,\n _toAsset,\n _fromAssetAmount - 1,\n _minToAssetAmount,\n _data\n );\n\n // Compute the change in asset balance held by the Vault\n toAssetAmount =\n IERC20(_toAsset).balanceOf(address(this)) -\n toAssetBalBefore;\n }\n\n // Check the to assets returned is above slippage amount specified by the strategist\n require(\n toAssetAmount >= _minToAssetAmount,\n \"Strategist slippage limit\"\n );\n\n // Scope a new block to remove minOracleToAssetAmount from the scope of swapCollateral.\n // This avoids a stack too deep error.\n {\n // Check the slippage against the Oracle in case the strategist made a mistake or has become malicious.\n // to asset amount = from asset amount * from asset price / to asset price\n uint256 minOracleToAssetAmount = (_fromAssetAmount *\n (1e4 - fromAssetConfig.allowedOracleSlippageBps) *\n IOracle(priceProvider).price(_fromAsset)) /\n (IOracle(priceProvider).price(_toAsset) *\n (1e4 + toAssetConfig.allowedOracleSlippageBps));\n\n // Scale both sides up to 18 decimals to compare\n require(\n toAssetAmount.scaleBy(18, toAssetConfig.decimals) >=\n minOracleToAssetAmount.scaleBy(\n 18,\n fromAssetConfig.decimals\n ),\n \"Oracle slippage limit exceeded\"\n );\n }\n\n // Check the vault's total value hasn't gone below the OToken total supply\n // by more than the allowed percentage.\n require(\n IVault(address(this)).totalValue() >=\n (oUSD.totalSupply() * ((1e4 - config.allowedUndervalueBps))) /\n 1e4,\n \"Allowed value < supply\"\n );\n\n emit Swapped(_fromAsset, _toAsset, _fromAssetAmount, toAssetAmount);\n }\n\n /***************************************\n Swap Config\n ****************************************/\n\n /**\n * @notice Set the contract the performs swaps of collateral assets.\n * @param _swapperAddr Address of the Swapper contract that implements the ISwapper interface.\n */\n function setSwapper(address _swapperAddr) external onlyGovernor {\n swapConfig.swapper = _swapperAddr;\n emit SwapperChanged(_swapperAddr);\n }\n\n /// @notice Contract that swaps the vault's collateral assets\n function swapper() external view returns (address swapper_) {\n swapper_ = swapConfig.swapper;\n }\n\n /**\n * @notice Set max allowed percentage the vault total value can drop below the OToken total supply in basis points\n * when executing collateral swaps.\n * @param _basis Percentage in basis points. eg 100 == 1%\n */\n function setSwapAllowedUndervalue(uint16 _basis) external onlyGovernor {\n require(_basis < 10001, \"Invalid basis points\");\n swapConfig.allowedUndervalueBps = _basis;\n emit SwapAllowedUndervalueChanged(_basis);\n }\n\n /**\n * @notice Max allowed percentage the vault total value can drop below the OToken total supply in basis points\n * when executing a collateral swap.\n * For example 100 == 1%\n * @return value Percentage in basis points.\n */\n function allowedSwapUndervalue() external view returns (uint256 value) {\n value = swapConfig.allowedUndervalueBps;\n }\n\n /**\n * @notice Set the allowed slippage from the Oracle price for collateral asset swaps.\n * @param _asset Address of the asset token.\n * @param _allowedOracleSlippageBps allowed slippage from Oracle in basis points. eg 20 = 0.2%. Max 10%.\n */\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external\n onlyGovernor\n {\n require(assets[_asset].isSupported, \"Asset not supported\");\n require(_allowedOracleSlippageBps < 1000, \"Slippage too high\");\n\n assets[_asset].allowedOracleSlippageBps = _allowedOracleSlippageBps;\n\n emit SwapSlippageChanged(_asset, _allowedOracleSlippageBps);\n }\n\n /***************************************\n Asset Config\n ****************************************/\n\n /**\n * @notice Add a supported asset to the contract, i.e. one that can be\n * to mint OTokens.\n * @param _asset Address of asset\n */\n function supportAsset(address _asset, uint8 _unitConversion)\n external\n virtual\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Asset already supported\");\n\n assets[_asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(_unitConversion),\n decimals: 0, // will be overridden in _cacheDecimals\n allowedOracleSlippageBps: 0 // 0% by default\n });\n\n _cacheDecimals(_asset);\n allAssets.push(_asset);\n\n // Verify that our oracle supports the asset\n // slither-disable-next-line unused-return\n IOracle(priceProvider).price(_asset);\n\n emit AssetSupported(_asset);\n }\n\n /**\n * @notice Remove a supported asset from the Vault\n * @param _asset Address of asset\n */\n function removeAsset(address _asset) external onlyGovernor {\n require(assets[_asset].isSupported, \"Asset not supported\");\n\n // 1e13 for 18 decimals. And 10 for 6 decimals\n uint256 maxDustBalance = uint256(1e13).scaleBy(\n assets[_asset].decimals,\n 18\n );\n\n require(\n IVault(address(this)).checkBalance(_asset) <= maxDustBalance,\n \"Vault still holds asset\"\n );\n\n uint256 assetsCount = allAssets.length;\n uint256 assetIndex = assetsCount; // initialize at invalid index\n for (uint256 i = 0; i < assetsCount; ++i) {\n if (allAssets[i] == _asset) {\n assetIndex = i;\n break;\n }\n }\n\n // Note: If asset is not found in `allAssets`, the following line\n // will revert with an out-of-bound error. However, there's no\n // reason why an asset would have `Asset.isSupported = true` but\n // not exist in `allAssets`.\n\n // Update allAssets array\n allAssets[assetIndex] = allAssets[assetsCount - 1];\n allAssets.pop();\n\n // Reset default strategy\n assetDefaultStrategies[_asset] = address(0);\n emit AssetDefaultStrategyUpdated(_asset, address(0));\n\n // Remove asset from storage\n delete assets[_asset];\n\n emit AssetRemoved(_asset);\n }\n\n /**\n * @notice Cache decimals on OracleRouter for a particular asset. This action\n * is required before that asset's price can be accessed.\n * @param _asset Address of asset token\n */\n function cacheDecimals(address _asset) external onlyGovernor {\n _cacheDecimals(_asset);\n }\n\n /***************************************\n Strategy Config\n ****************************************/\n\n /**\n * @notice Add a strategy to the Vault.\n * @param _addr Address of the strategy to add\n */\n function approveStrategy(address _addr) external onlyGovernor {\n require(!strategies[_addr].isSupported, \"Strategy already approved\");\n strategies[_addr] = Strategy({ isSupported: true, _deprecated: 0 });\n allStrategies.push(_addr);\n emit StrategyApproved(_addr);\n }\n\n /**\n * @notice Remove a strategy from the Vault.\n * @param _addr Address of the strategy to remove\n */\n\n function removeStrategy(address _addr) external onlyGovernor {\n require(strategies[_addr].isSupported, \"Strategy not approved\");\n\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n require(\n assetDefaultStrategies[allAssets[i]] != _addr,\n \"Strategy is default for an asset\"\n );\n }\n\n // Initialize strategyIndex with out of bounds result so function will\n // revert if no valid index found\n uint256 stratCount = allStrategies.length;\n uint256 strategyIndex = stratCount;\n for (uint256 i = 0; i < stratCount; ++i) {\n if (allStrategies[i] == _addr) {\n strategyIndex = i;\n break;\n }\n }\n\n if (strategyIndex < stratCount) {\n allStrategies[strategyIndex] = allStrategies[stratCount - 1];\n allStrategies.pop();\n\n // Mark the strategy as not supported\n strategies[_addr].isSupported = false;\n\n // Withdraw all assets\n IStrategy strategy = IStrategy(_addr);\n strategy.withdrawAll();\n\n emit StrategyRemoved(_addr);\n }\n }\n\n /***************************************\n Strategies\n ****************************************/\n\n /**\n * @notice Deposit multiple assets from the vault into the strategy.\n * @param _strategyToAddress Address of the Strategy to deposit assets into.\n * @param _assets Array of asset address that will be deposited into the strategy.\n * @param _amounts Array of amounts of each corresponding asset to deposit.\n */\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external onlyGovernorOrStrategist nonReentrant {\n _depositToStrategy(_strategyToAddress, _assets, _amounts);\n }\n\n function _depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal virtual {\n require(\n strategies[_strategyToAddress].isSupported,\n \"Invalid to Strategy\"\n );\n require(_assets.length == _amounts.length, \"Parameter length mismatch\");\n\n uint256 assetCount = _assets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n address assetAddr = _assets[i];\n require(\n IStrategy(_strategyToAddress).supportsAsset(assetAddr),\n \"Asset unsupported\"\n );\n // Send required amount of funds to the strategy\n IERC20(assetAddr).safeTransfer(_strategyToAddress, _amounts[i]);\n }\n\n // Deposit all the funds that have been sent to the strategy\n IStrategy(_strategyToAddress).depositAll();\n }\n\n /**\n * @notice Withdraw multiple assets from the strategy to the vault.\n * @param _strategyFromAddress Address of the Strategy to withdraw assets from.\n * @param _assets Array of asset address that will be withdrawn from the strategy.\n * @param _amounts Array of amounts of each corresponding asset to withdraw.\n */\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external onlyGovernorOrStrategist nonReentrant {\n _withdrawFromStrategy(\n address(this),\n _strategyFromAddress,\n _assets,\n _amounts\n );\n }\n\n /**\n * @param _recipient can either be a strategy or the Vault\n */\n function _withdrawFromStrategy(\n address _recipient,\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal virtual {\n require(\n strategies[_strategyFromAddress].isSupported,\n \"Invalid from Strategy\"\n );\n require(_assets.length == _amounts.length, \"Parameter length mismatch\");\n\n uint256 assetCount = _assets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n // Withdraw from Strategy to the recipient\n IStrategy(_strategyFromAddress).withdraw(\n _recipient,\n _assets[i],\n _amounts[i]\n );\n }\n }\n\n /**\n * @notice Sets the maximum allowable difference between\n * total supply and backing assets' value.\n */\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external onlyGovernor {\n maxSupplyDiff = _maxSupplyDiff;\n emit MaxSupplyDiffChanged(_maxSupplyDiff);\n }\n\n /**\n * @notice Sets the trusteeAddress that can receive a portion of yield.\n * Setting to the zero address disables this feature.\n */\n function setTrusteeAddress(address _address) external onlyGovernor {\n trusteeAddress = _address;\n emit TrusteeAddressChanged(_address);\n }\n\n /**\n * @notice Sets the TrusteeFeeBps to the percentage of yield that should be\n * received in basis points.\n */\n function setTrusteeFeeBps(uint256 _basis) external onlyGovernor {\n require(_basis <= 5000, \"basis cannot exceed 50%\");\n trusteeFeeBps = _basis;\n emit TrusteeFeeBpsChanged(_basis);\n }\n\n /**\n * @notice Set OToken Metapool strategy\n * @param _ousdMetaStrategy Address of OToken metapool strategy\n */\n function setOusdMetaStrategy(address _ousdMetaStrategy)\n external\n onlyGovernor\n {\n ousdMetaStrategy = _ousdMetaStrategy;\n emit OusdMetaStrategyUpdated(_ousdMetaStrategy);\n }\n\n /***************************************\n Pause\n ****************************************/\n\n /**\n * @notice Set the deposit paused flag to true to prevent rebasing.\n */\n function pauseRebase() external onlyGovernorOrStrategist {\n rebasePaused = true;\n emit RebasePaused();\n }\n\n /**\n * @notice Set the deposit paused flag to true to allow rebasing.\n */\n function unpauseRebase() external onlyGovernorOrStrategist {\n rebasePaused = false;\n emit RebaseUnpaused();\n }\n\n /**\n * @notice Set the deposit paused flag to true to prevent capital movement.\n */\n function pauseCapital() external onlyGovernorOrStrategist {\n capitalPaused = true;\n emit CapitalPaused();\n }\n\n /**\n * @notice Set the deposit paused flag to false to enable capital movement.\n */\n function unpauseCapital() external onlyGovernorOrStrategist {\n capitalPaused = false;\n emit CapitalUnpaused();\n }\n\n /***************************************\n Utils\n ****************************************/\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Only unsupported assets\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /***************************************\n Strategies Admin\n ****************************************/\n\n /**\n * @notice Withdraws all assets from the strategy and sends assets to the Vault.\n * @param _strategyAddr Strategy address.\n */\n function withdrawAllFromStrategy(address _strategyAddr)\n external\n onlyGovernorOrStrategist\n {\n _withdrawAllFromStrategy(_strategyAddr);\n }\n\n function _withdrawAllFromStrategy(address _strategyAddr) internal virtual {\n require(\n strategies[_strategyAddr].isSupported,\n \"Strategy is not supported\"\n );\n IStrategy strategy = IStrategy(_strategyAddr);\n strategy.withdrawAll();\n }\n\n /**\n * @notice Withdraws all assets from all the strategies and sends assets to the Vault.\n */\n function withdrawAllFromStrategies() external onlyGovernorOrStrategist {\n _withdrawAllFromStrategies();\n }\n\n function _withdrawAllFromStrategies() internal virtual {\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n IStrategy(allStrategies[i]).withdrawAll();\n }\n }\n\n /***************************************\n Utils\n ****************************************/\n\n function _cacheDecimals(address token) internal {\n Asset storage tokenAsset = assets[token];\n if (tokenAsset.decimals != 0) {\n return;\n }\n uint8 decimals = IBasicToken(token).decimals();\n require(decimals >= 6 && decimals <= 18, \"Unexpected precision\");\n tokenAsset.decimals = decimals;\n }\n}\n" + }, + "contracts/vault/VaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultCore contract\n * @notice The Vault contract stores assets. On a deposit, OTokens will be minted\n and sent to the depositor. On a withdrawal, OTokens will be burned and\n assets will be sent to the withdrawer. The Vault accepts deposits of\n interest from yield bearing strategies which will modify the supply\n of OTokens.\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IGetExchangeRateToken } from \"../interfaces/IGetExchangeRateToken.sol\";\n\nimport \"./VaultInitializer.sol\";\n\ncontract VaultCore is VaultInitializer {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n /// @dev max signed int\n uint256 internal constant MAX_INT = uint256(type(int256).max);\n\n /**\n * @dev Verifies that the rebasing is not paused.\n */\n modifier whenNotRebasePaused() {\n require(!rebasePaused, \"Rebasing paused\");\n _;\n }\n\n /**\n * @dev Verifies that the deposits are not paused.\n */\n modifier whenNotCapitalPaused() {\n require(!capitalPaused, \"Capital paused\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the AMO strategy.\n */\n modifier onlyOusdMetaStrategy() {\n require(\n msg.sender == ousdMetaStrategy,\n \"Caller is not the OUSD meta strategy\"\n );\n _;\n }\n\n /**\n * @notice Deposit a supported asset and mint OTokens.\n * @param _asset Address of the asset being deposited\n * @param _amount Amount of the asset being deposited\n * @param _minimumOusdAmount Minimum OTokens to mint\n */\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external whenNotCapitalPaused nonReentrant {\n _mint(_asset, _amount, _minimumOusdAmount);\n }\n\n /**\n * @dev Deposit a supported asset and mint OTokens.\n * @param _asset Address of the asset being deposited\n * @param _amount Amount of the asset being deposited\n * @param _minimumOusdAmount Minimum OTokens to mint\n */\n function _mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) internal virtual {\n require(assets[_asset].isSupported, \"Asset is not supported\");\n require(_amount > 0, \"Amount must be greater than 0\");\n\n uint256 units = _toUnits(_amount, _asset);\n uint256 unitPrice = _toUnitPrice(_asset, true);\n uint256 priceAdjustedDeposit = (units * unitPrice) / 1e18;\n\n if (_minimumOusdAmount > 0) {\n require(\n priceAdjustedDeposit >= _minimumOusdAmount,\n \"Mint amount lower than minimum\"\n );\n }\n\n emit Mint(msg.sender, priceAdjustedDeposit);\n\n // Rebase must happen before any transfers occur.\n if (priceAdjustedDeposit >= rebaseThreshold && !rebasePaused) {\n _rebase();\n }\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, priceAdjustedDeposit);\n\n // Transfer the deposited coins to the vault\n IERC20 asset = IERC20(_asset);\n asset.safeTransferFrom(msg.sender, address(this), _amount);\n\n if (priceAdjustedDeposit >= autoAllocateThreshold) {\n _allocate();\n }\n }\n\n /**\n * @notice Mint OTokens for a Metapool Strategy\n * @param _amount Amount of the asset being deposited\n *\n * Notice: can't use `nonReentrant` modifier since the `mint` function can\n * call `allocate`, and that can trigger `ConvexOUSDMetaStrategy` to call this function\n * while the execution of the `mint` has not yet completed -> causing a `nonReentrant` collision.\n *\n * Also important to understand is that this is a limitation imposed by the test suite.\n * Production / mainnet contracts should never be configured in a way where mint/redeem functions\n * that are moving funds between the Vault and end user wallets can influence strategies\n * utilizing this function.\n */\n function mintForStrategy(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n onlyOusdMetaStrategy\n {\n require(_amount < MAX_INT, \"Amount too high\");\n\n emit Mint(msg.sender, _amount);\n\n // safe to cast because of the require check at the beginning of the function\n netOusdMintedForStrategy += int256(_amount);\n\n require(\n abs(netOusdMintedForStrategy) < netOusdMintForStrategyThreshold,\n \"Minted ousd surpassed netOusdMintForStrategyThreshold.\"\n );\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, _amount);\n }\n\n // In memoriam\n\n /**\n * @notice Withdraw a supported asset and burn OTokens.\n * @param _amount Amount of OTokens to burn\n * @param _minimumUnitAmount Minimum stablecoin units to receive in return\n */\n function redeem(uint256 _amount, uint256 _minimumUnitAmount)\n external\n whenNotCapitalPaused\n nonReentrant\n {\n _redeem(_amount, _minimumUnitAmount);\n }\n\n /**\n * @notice Withdraw a supported asset and burn OTokens.\n * @param _amount Amount of OTokens to burn\n * @param _minimumUnitAmount Minimum stablecoin units to receive in return\n */\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n {\n // Calculate redemption outputs\n uint256[] memory outputs = _calculateRedeemOutputs(_amount);\n\n emit Redeem(msg.sender, _amount);\n\n // Send outputs\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n if (outputs[i] == 0) continue;\n\n address assetAddr = allAssets[i];\n\n if (IERC20(assetAddr).balanceOf(address(this)) >= outputs[i]) {\n // Use Vault funds first if sufficient\n IERC20(assetAddr).safeTransfer(msg.sender, outputs[i]);\n } else {\n address strategyAddr = assetDefaultStrategies[assetAddr];\n if (strategyAddr != address(0)) {\n // Nothing in Vault, but something in Strategy, send from there\n IStrategy strategy = IStrategy(strategyAddr);\n strategy.withdraw(msg.sender, assetAddr, outputs[i]);\n } else {\n // Cant find funds anywhere\n revert(\"Liquidity error\");\n }\n }\n }\n\n if (_minimumUnitAmount > 0) {\n uint256 unitTotal = 0;\n for (uint256 i = 0; i < outputs.length; ++i) {\n unitTotal += _toUnits(outputs[i], allAssets[i]);\n }\n require(\n unitTotal >= _minimumUnitAmount,\n \"Redeem amount lower than minimum\"\n );\n }\n\n oUSD.burn(msg.sender, _amount);\n\n _postRedeem(_amount);\n }\n\n function _postRedeem(uint256 _amount) internal {\n // Until we can prove that we won't affect the prices of our assets\n // by withdrawing them, this should be here.\n // It's possible that a strategy was off on its asset total, perhaps\n // a reward token sold for more or for less than anticipated.\n uint256 totalUnits = 0;\n if (_amount >= rebaseThreshold && !rebasePaused) {\n totalUnits = _rebase();\n } else {\n totalUnits = _totalValue();\n }\n\n // Check that the OTokens are backed by enough assets\n if (maxSupplyDiff > 0) {\n // If there are more outstanding withdrawal requests than assets in the vault and strategies\n // then the available assets will be negative and totalUnits will be rounded up to zero.\n // As we don't know the exact shortfall amount, we will reject all redeem and withdrawals\n require(totalUnits > 0, \"Too many outstanding requests\");\n\n // Allow a max difference of maxSupplyDiff% between\n // backing assets value and OUSD total supply\n uint256 diff = oUSD.totalSupply().divPrecisely(totalUnits);\n require(\n (diff > 1e18 ? diff - 1e18 : 1e18 - diff) <= maxSupplyDiff,\n \"Backing supply liquidity error\"\n );\n }\n }\n\n /**\n * @notice Burn OTokens for Metapool Strategy\n * @param _amount Amount of OUSD to burn\n *\n * @dev Notice: can't use `nonReentrant` modifier since the `redeem` function could\n * require withdrawal on `ConvexOUSDMetaStrategy` and that one can call `burnForStrategy`\n * while the execution of the `redeem` has not yet completed -> causing a `nonReentrant` collision.\n *\n * Also important to understand is that this is a limitation imposed by the test suite.\n * Production / mainnet contracts should never be configured in a way where mint/redeem functions\n * that are moving funds between the Vault and end user wallets can influence strategies\n * utilizing this function.\n */\n function burnForStrategy(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n onlyOusdMetaStrategy\n {\n require(_amount < MAX_INT, \"Amount too high\");\n\n emit Redeem(msg.sender, _amount);\n\n // safe to cast because of the require check at the beginning of the function\n netOusdMintedForStrategy -= int256(_amount);\n\n require(\n abs(netOusdMintedForStrategy) < netOusdMintForStrategyThreshold,\n \"Attempting to burn too much OUSD.\"\n );\n\n // Burn OTokens\n oUSD.burn(msg.sender, _amount);\n }\n\n /**\n * @notice Allocate unallocated funds on Vault to strategies.\n **/\n function allocate() external virtual whenNotCapitalPaused nonReentrant {\n _allocate();\n }\n\n /**\n * @dev Allocate unallocated funds on Vault to strategies.\n **/\n function _allocate() internal virtual {\n uint256 vaultValue = _totalValueInVault();\n // Nothing in vault to allocate\n if (vaultValue == 0) return;\n uint256 strategiesValue = _totalValueInStrategies();\n // We have a method that does the same as this, gas optimisation\n uint256 calculatedTotalValue = vaultValue + strategiesValue;\n\n // We want to maintain a buffer on the Vault so calculate a percentage\n // modifier to multiply each amount being allocated by to enforce the\n // vault buffer\n uint256 vaultBufferModifier;\n if (strategiesValue == 0) {\n // Nothing in Strategies, allocate 100% minus the vault buffer to\n // strategies\n vaultBufferModifier = uint256(1e18) - vaultBuffer;\n } else {\n vaultBufferModifier =\n (vaultBuffer * calculatedTotalValue) /\n vaultValue;\n if (1e18 > vaultBufferModifier) {\n // E.g. 1e18 - (1e17 * 10e18)/5e18 = 8e17\n // (5e18 * 8e17) / 1e18 = 4e18 allocated from Vault\n vaultBufferModifier = uint256(1e18) - vaultBufferModifier;\n } else {\n // We need to let the buffer fill\n return;\n }\n }\n if (vaultBufferModifier == 0) return;\n\n // Iterate over all assets in the Vault and allocate to the appropriate\n // strategy\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(allAssets[i]);\n uint256 assetBalance = asset.balanceOf(address(this));\n // No balance, nothing to do here\n if (assetBalance == 0) continue;\n\n // Multiply the balance by the vault buffer modifier and truncate\n // to the scale of the asset decimals\n uint256 allocateAmount = assetBalance.mulTruncate(\n vaultBufferModifier\n );\n\n address depositStrategyAddr = assetDefaultStrategies[\n address(asset)\n ];\n\n if (depositStrategyAddr != address(0) && allocateAmount > 0) {\n IStrategy strategy = IStrategy(depositStrategyAddr);\n // Transfer asset to Strategy and call deposit method to\n // mint or take required action\n asset.safeTransfer(address(strategy), allocateAmount);\n strategy.deposit(address(asset), allocateAmount);\n emit AssetAllocated(\n address(asset),\n depositStrategyAddr,\n allocateAmount\n );\n }\n }\n }\n\n /**\n * @notice Calculate the total value of assets held by the Vault and all\n * strategies and update the supply of OTokens.\n */\n function rebase() external virtual nonReentrant {\n _rebase();\n }\n\n /**\n * @dev Calculate the total value of assets held by the Vault and all\n * strategies and update the supply of OTokens, optionally sending a\n * portion of the yield to the trustee.\n * @return totalUnits Total balance of Vault in units\n */\n function _rebase() internal whenNotRebasePaused returns (uint256) {\n uint256 supply = oUSD.totalSupply();\n uint256 vaultValue = _totalValue();\n // If no supply yet, do not rebase\n if (supply == 0) {\n return vaultValue;\n }\n\n // Calculate yield and new supply\n (uint256 yield, uint256 targetRate) = _nextYield(supply, vaultValue);\n uint256 newSupply = supply + yield;\n // Only rebase upwards and if we have enough backing funds\n if (newSupply <= supply || newSupply > vaultValue) {\n return vaultValue;\n }\n\n rebasePerSecondTarget = uint64(_min(targetRate, type(uint64).max));\n lastRebase = uint64(block.timestamp); // Intentional cast\n\n // Fee collection on yield\n address _trusteeAddress = trusteeAddress; // gas savings\n uint256 fee = 0;\n if (_trusteeAddress != address(0)) {\n fee = (yield * trusteeFeeBps) / 1e4;\n if (fee > 0) {\n require(fee < yield, \"Fee must not be greater than yield\");\n oUSD.mint(_trusteeAddress, fee);\n }\n }\n emit YieldDistribution(_trusteeAddress, yield, fee);\n\n // Only ratchet OToken supply upwards\n // Final check uses latest totalSupply\n if (newSupply > oUSD.totalSupply()) {\n oUSD.changeSupply(newSupply);\n }\n return vaultValue;\n }\n\n /**\n * @notice Calculates the amount that would rebase at at next rebase.\n * This is before any fees.\n * @return yield amount of expected yield\n */\n function previewYield() external view returns (uint256 yield) {\n (yield, ) = _nextYield(oUSD.totalSupply(), _totalValue());\n return yield;\n }\n\n function _nextYield(uint256 supply, uint256 vaultValue)\n internal\n view\n virtual\n returns (uint256 yield, uint256 targetRate)\n {\n uint256 nonRebasing = oUSD.nonRebasingSupply();\n uint256 rebasing = supply - nonRebasing;\n uint256 elapsed = block.timestamp - lastRebase;\n targetRate = rebasePerSecondTarget;\n\n if (\n elapsed == 0 || // Yield only once per block.\n rebasing == 0 || // No yield if there are no rebasing tokens to give it to.\n supply > vaultValue || // No yield if we do not have yield to give.\n block.timestamp >= type(uint64).max // No yield if we are too far in the future to calculate it correctly.\n ) {\n return (0, targetRate);\n }\n\n // Start with the full difference available\n yield = vaultValue - supply;\n\n // Cap via optional automatic duration smoothing\n uint256 _dripDuration = dripDuration;\n if (_dripDuration > 1) {\n // If we are able to sustain an increased drip rate for\n // double the duration, then increase the target drip rate\n targetRate = _max(targetRate, yield / (_dripDuration * 2));\n // If we cannot sustain the target rate any more,\n // then rebase what we can, and reduce the target\n targetRate = _min(targetRate, yield / _dripDuration);\n // drip at the new target rate\n yield = _min(yield, targetRate * elapsed);\n }\n\n // Cap per second. elapsed is not 1e18 denominated\n yield = _min(yield, (rebasing * elapsed * rebasePerSecondMax) / 1e18);\n\n // Cap at a hard max per rebase, to avoid long durations resulting in huge rebases\n yield = _min(yield, (rebasing * MAX_REBASE) / 1e18);\n\n return (yield, targetRate);\n }\n\n /**\n * @notice Determine the total value of assets held by the vault and its\n * strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function totalValue() external view virtual returns (uint256 value) {\n value = _totalValue();\n }\n\n /**\n * @dev Internal Calculate the total value of the assets held by the\n * vault and its strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValue() internal view virtual returns (uint256 value) {\n return _totalValueInVault() + _totalValueInStrategies();\n }\n\n /**\n * @dev Internal to calculate total value of all assets held in Vault.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInVault()\n internal\n view\n virtual\n returns (uint256 value)\n {\n uint256 assetCount = allAssets.length;\n for (uint256 y; y < assetCount; ++y) {\n address assetAddr = allAssets[y];\n uint256 balance = IERC20(assetAddr).balanceOf(address(this));\n if (balance > 0) {\n value += _toUnits(balance, assetAddr);\n }\n }\n }\n\n /**\n * @dev Internal to calculate total value of all assets held in Strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInStrategies() internal view returns (uint256 value) {\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n value = value + _totalValueInStrategy(allStrategies[i]);\n }\n }\n\n /**\n * @dev Internal to calculate total value of all assets held by strategy.\n * @param _strategyAddr Address of the strategy\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInStrategy(address _strategyAddr)\n internal\n view\n returns (uint256 value)\n {\n IStrategy strategy = IStrategy(_strategyAddr);\n uint256 assetCount = allAssets.length;\n for (uint256 y; y < assetCount; ++y) {\n address assetAddr = allAssets[y];\n if (strategy.supportsAsset(assetAddr)) {\n uint256 balance = strategy.checkBalance(assetAddr);\n if (balance > 0) {\n value += _toUnits(balance, assetAddr);\n }\n }\n }\n }\n\n /**\n * @notice Get the balance of an asset held in Vault and all strategies.\n * @param _asset Address of asset\n * @return uint256 Balance of asset in decimals of asset\n */\n function checkBalance(address _asset) external view returns (uint256) {\n return _checkBalance(_asset);\n }\n\n /**\n * @notice Get the balance of an asset held in Vault and all strategies.\n * @param _asset Address of asset\n * @return balance Balance of asset in decimals of asset\n */\n function _checkBalance(address _asset)\n internal\n view\n virtual\n returns (uint256 balance)\n {\n IERC20 asset = IERC20(_asset);\n balance = asset.balanceOf(address(this));\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n IStrategy strategy = IStrategy(allStrategies[i]);\n if (strategy.supportsAsset(_asset)) {\n balance = balance + strategy.checkBalance(_asset);\n }\n }\n }\n\n /**\n * @notice Calculate the outputs for a redeem function, i.e. the mix of\n * coins that will be returned\n */\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory)\n {\n return _calculateRedeemOutputs(_amount);\n }\n\n /**\n * @dev Calculate the outputs for a redeem function, i.e. the mix of\n * coins that will be returned.\n * @return outputs Array of amounts respective to the supported assets\n */\n function _calculateRedeemOutputs(uint256 _amount)\n internal\n view\n virtual\n returns (uint256[] memory outputs)\n {\n // We always give out coins in proportion to how many we have,\n // Now if all coins were the same value, this math would easy,\n // just take the percentage of each coin, and multiply by the\n // value to be given out. But if coins are worth more than $1,\n // then we would end up handing out too many coins. We need to\n // adjust by the total value of coins.\n //\n // To do this, we total up the value of our coins, by their\n // percentages. Then divide what we would otherwise give out by\n // this number.\n //\n // Let say we have 100 DAI at $1.06 and 200 USDT at $1.00.\n // So for every 1 DAI we give out, we'll be handing out 2 USDT\n // Our total output ratio is: 33% * 1.06 + 66% * 1.00 = 1.02\n //\n // So when calculating the output, we take the percentage of\n // each coin, times the desired output value, divided by the\n // totalOutputRatio.\n //\n // For example, withdrawing: 30 OUSD:\n // DAI 33% * 30 / 1.02 = 9.80 DAI\n // USDT = 66 % * 30 / 1.02 = 19.60 USDT\n //\n // Checking these numbers:\n // 9.80 DAI * 1.06 = $10.40\n // 19.60 USDT * 1.00 = $19.60\n //\n // And so the user gets $10.40 + $19.60 = $30 worth of value.\n\n uint256 assetCount = allAssets.length;\n uint256[] memory assetUnits = new uint256[](assetCount);\n uint256[] memory assetBalances = new uint256[](assetCount);\n outputs = new uint256[](assetCount);\n\n // Calculate redeem fee\n if (redeemFeeBps > 0) {\n uint256 redeemFee = _amount.mulTruncateScale(redeemFeeBps, 1e4);\n _amount = _amount - redeemFee;\n }\n\n // Calculate assets balances and decimals once,\n // for a large gas savings.\n uint256 totalUnits = 0;\n for (uint256 i = 0; i < assetCount; ++i) {\n address assetAddr = allAssets[i];\n uint256 balance = _checkBalance(assetAddr);\n assetBalances[i] = balance;\n assetUnits[i] = _toUnits(balance, assetAddr);\n totalUnits = totalUnits + assetUnits[i];\n }\n // Calculate totalOutputRatio\n uint256 totalOutputRatio = 0;\n for (uint256 i = 0; i < assetCount; ++i) {\n uint256 unitPrice = _toUnitPrice(allAssets[i], false);\n uint256 ratio = (assetUnits[i] * unitPrice) / totalUnits;\n totalOutputRatio = totalOutputRatio + ratio;\n }\n // Calculate final outputs\n uint256 factor = _amount.divPrecisely(totalOutputRatio);\n for (uint256 i = 0; i < assetCount; ++i) {\n outputs[i] = (assetBalances[i] * factor) / totalUnits;\n }\n }\n\n /***************************************\n Pricing\n ****************************************/\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * Never goes above 1, since that is how we price mints.\n * @param asset address of the asset\n * @return price uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed\n */\n function priceUnitMint(address asset)\n external\n view\n returns (uint256 price)\n {\n /* need to supply 1 asset unit in asset's decimals and can not just hard-code\n * to 1e18 and ignore calling `_toUnits` since we need to consider assets\n * with the exchange rate\n */\n uint256 units = _toUnits(\n uint256(1e18).scaleBy(_getDecimals(asset), 18),\n asset\n );\n price = (_toUnitPrice(asset, true) * units) / 1e18;\n }\n\n /**\n * @notice Returns the total price in 18 digit unit for a given asset.\n * Never goes below 1, since that is how we price redeems\n * @param asset Address of the asset\n * @return price uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed\n */\n function priceUnitRedeem(address asset)\n external\n view\n returns (uint256 price)\n {\n /* need to supply 1 asset unit in asset's decimals and can not just hard-code\n * to 1e18 and ignore calling `_toUnits` since we need to consider assets\n * with the exchange rate\n */\n uint256 units = _toUnits(\n uint256(1e18).scaleBy(_getDecimals(asset), 18),\n asset\n );\n price = (_toUnitPrice(asset, false) * units) / 1e18;\n }\n\n /***************************************\n Utils\n ****************************************/\n\n /**\n * @dev Convert a quantity of a token into 1e18 fixed decimal \"units\"\n * in the underlying base (USD/ETH) used by the vault.\n * Price is not taken into account, only quantity.\n *\n * Examples of this conversion:\n *\n * - 1e18 DAI becomes 1e18 units (same decimals)\n * - 1e6 USDC becomes 1e18 units (decimal conversion)\n * - 1e18 rETH becomes 1.2e18 units (exchange rate conversion)\n *\n * @param _raw Quantity of asset\n * @param _asset Core Asset address\n * @return value 1e18 normalized quantity of units\n */\n function _toUnits(uint256 _raw, address _asset)\n internal\n view\n returns (uint256)\n {\n UnitConversion conversion = assets[_asset].unitConversion;\n if (conversion == UnitConversion.DECIMALS) {\n return _raw.scaleBy(18, _getDecimals(_asset));\n } else if (conversion == UnitConversion.GETEXCHANGERATE) {\n uint256 exchangeRate = IGetExchangeRateToken(_asset)\n .getExchangeRate();\n return (_raw * exchangeRate) / 1e18;\n } else {\n revert(\"Unsupported conversion type\");\n }\n }\n\n /**\n * @dev Returns asset's unit price accounting for different asset types\n * and takes into account the context in which that price exists -\n * - mint or redeem.\n *\n * Note: since we are returning the price of the unit and not the one of the\n * asset (see comment above how 1 rETH exchanges for 1.2 units) we need\n * to make the Oracle price adjustment as well since we are pricing the\n * units and not the assets.\n *\n * The price also snaps to a \"full unit price\" in case a mint or redeem\n * action would be unfavourable to the protocol.\n *\n */\n function _toUnitPrice(address _asset, bool isMint)\n internal\n view\n returns (uint256 price)\n {\n UnitConversion conversion = assets[_asset].unitConversion;\n price = IOracle(priceProvider).price(_asset);\n\n if (conversion == UnitConversion.GETEXCHANGERATE) {\n uint256 exchangeRate = IGetExchangeRateToken(_asset)\n .getExchangeRate();\n price = (price * 1e18) / exchangeRate;\n } else if (conversion != UnitConversion.DECIMALS) {\n revert(\"Unsupported conversion type\");\n }\n\n /* At this stage the price is already adjusted to the unit\n * so the price checks are agnostic to underlying asset being\n * pegged to a USD or to an ETH or having a custom exchange rate.\n */\n require(price <= MAX_UNIT_PRICE_DRIFT, \"Vault: Price exceeds max\");\n require(price >= MIN_UNIT_PRICE_DRIFT, \"Vault: Price under min\");\n\n if (isMint) {\n /* Never price a normalized unit price for more than one\n * unit of OETH/OUSD when minting.\n */\n if (price > 1e18) {\n price = 1e18;\n }\n require(price >= MINT_MINIMUM_UNIT_PRICE, \"Asset price below peg\");\n } else {\n /* Never give out more than 1 normalized unit amount of assets\n * for one unit of OETH/OUSD when redeeming.\n */\n if (price < 1e18) {\n price = 1e18;\n }\n }\n }\n\n /**\n * @dev Get the number of decimals of a token asset\n * @param _asset Address of the asset\n * @return decimals number of decimals\n */\n function _getDecimals(address _asset)\n internal\n view\n returns (uint256 decimals)\n {\n decimals = assets[_asset].decimals;\n require(decimals > 0, \"Decimals not cached\");\n }\n\n /**\n * @notice Return the number of assets supported by the Vault.\n */\n function getAssetCount() public view returns (uint256) {\n return allAssets.length;\n }\n\n /**\n * @notice Gets the vault configuration of a supported asset.\n * @param _asset Address of the token asset\n */\n function getAssetConfig(address _asset)\n public\n view\n returns (Asset memory config)\n {\n config = assets[_asset];\n }\n\n /**\n * @notice Return all vault asset addresses in order\n */\n function getAllAssets() external view returns (address[] memory) {\n return allAssets;\n }\n\n /**\n * @notice Return the number of strategies active on the Vault.\n */\n function getStrategyCount() external view returns (uint256) {\n return allStrategies.length;\n }\n\n /**\n * @notice Return the array of all strategies\n */\n function getAllStrategies() external view returns (address[] memory) {\n return allStrategies;\n }\n\n /**\n * @notice Returns whether the vault supports the asset\n * @param _asset address of the asset\n * @return true if supported\n */\n function isSupportedAsset(address _asset) external view returns (bool) {\n return assets[_asset].isSupported;\n }\n\n function ADMIN_IMPLEMENTATION() external view returns (address adminImpl) {\n bytes32 slot = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n adminImpl := sload(slot)\n }\n }\n\n /**\n * @dev Falldown to the admin implementation\n * @notice This is a catch all for all functions not declared in core\n */\n // solhint-disable-next-line no-complex-fallback\n fallback() external {\n bytes32 slot = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(\n gas(),\n sload(slot),\n 0,\n calldatasize(),\n 0,\n 0\n )\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n function abs(int256 x) private pure returns (uint256) {\n require(x < int256(MAX_INT), \"Amount too high\");\n return x >= 0 ? uint256(x) : uint256(-x);\n }\n\n function _min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n function _max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a > b ? a : b;\n }\n}\n" + }, + "contracts/vault/VaultInitializer.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultInitializer contract\n * @notice The Vault contract initializes the vault.\n * @author Origin Protocol Inc\n */\n\nimport \"./VaultStorage.sol\";\n\ncontract VaultInitializer is VaultStorage {\n function initialize(address _priceProvider, address _oToken)\n external\n onlyGovernor\n initializer\n {\n require(_priceProvider != address(0), \"PriceProvider address is zero\");\n require(_oToken != address(0), \"oToken address is zero\");\n\n oUSD = OUSD(_oToken);\n\n priceProvider = _priceProvider;\n\n rebasePaused = false;\n capitalPaused = true;\n\n // Initial redeem fee of 0 basis points\n redeemFeeBps = 0;\n // Initial Vault buffer of 0%\n vaultBuffer = 0;\n // Initial allocate threshold of 25,000 OUSD\n autoAllocateThreshold = 25000e18;\n // Threshold for rebasing\n rebaseThreshold = 1000e18;\n // Initialize all strategies\n allStrategies = new address[](0);\n // Start with drip duration disabled\n dripDuration = 1;\n }\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + }, + "contracts/zapper/WOETHCCIPZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IRouterClient } from \"@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol\";\nimport { Client } from \"@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol\";\n// solhint-disable-next-line max-line-length\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"./../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IOETHZapper } from \"./../interfaces/IOETHZapper.sol\";\n\n/**\n * @title WOETH CCIP Zapper Contract\n * @notice Helps to directly convert ETH on mainnet into WOETH on L2s.\n * @author Origin Protocol Inc\n */\n\ncontract WOETHCCIPZapper {\n /**\n * @dev Event emitted when a zap occurs\n * @param messageId Unique message identifier for each zap\n * @param sender Address initiating the zap\n * @param recipient Recipient address at destination chain\n * @param amount Amount of ETH zapped\n */\n event Zap(\n bytes32 indexed messageId,\n address sender,\n address recipient,\n uint256 amount\n );\n\n // @dev Thrown when Zap amount is less than fee.\n error AmountLessThanFee();\n\n /**\n * @dev The destination chain selector\n */\n uint64 public immutable destinationChainSelector;\n\n /**\n * @dev The WOETH source chain (Mainnet)\n */\n IERC4626 public immutable woethOnSourceChain;\n\n /**\n * @dev The WOETH destination chain (Arbitrum)\n */\n IERC20 public immutable woethOnDestinationChain;\n\n /**\n * @dev The OETH zapper contract address\n */\n IOETHZapper public immutable oethZapper;\n\n /**\n * @dev The CCIP router contract address\n */\n IRouterClient public immutable ccipRouter;\n\n /**\n * @dev The OETH token contract address\n */\n IERC20 public immutable oeth;\n\n constructor(\n address _ccipRouter,\n uint64 _destinationChainSelector,\n IERC4626 _woethOnSourceChain,\n IERC20 _woethOnDestinationChain,\n IOETHZapper _oethZapper,\n IERC20 _oeth\n ) {\n ccipRouter = IRouterClient(_ccipRouter);\n destinationChainSelector = _destinationChainSelector;\n woethOnSourceChain = _woethOnSourceChain;\n woethOnDestinationChain = _woethOnDestinationChain;\n oethZapper = _oethZapper;\n oeth = _oeth;\n\n // Max allowance for Router and WOETH contracts\n _oeth.approve(address(_woethOnSourceChain), type(uint256).max); // for wrapping\n _woethOnSourceChain.approve(address(_ccipRouter), type(uint256).max); // for zapping\n }\n\n /**\n * @notice Accepts ETH, zaps for OETH, wraps it for WOETH and sends it to the destination chain (arbitrum)\n * @param receiver The address of the EOA on the destination chain\n * @return messageId The ID of the message that was sent\n */\n function zap(address receiver)\n external\n payable\n returns (bytes32 messageId)\n {\n return _zap(receiver, msg.value);\n }\n\n /**\n * @notice Used to estimate fee for CCIP transaction\n * @param amount The amount of ETH to be zapped\n * @param receiver The address of the EOA on the destination chain\n * @return feeAmount The CCIP tx fee in ETH.\n */\n\n function getFee(uint256 amount, address receiver)\n public\n view\n returns (uint256 feeAmount)\n {\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(woethOnSourceChain),\n amount: amount\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({\n receiver: abi.encode(receiver), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n feeAmount = ccipRouter.getFee(destinationChainSelector, message);\n }\n\n /**\n * @dev Deposit ETH and receive WOETH in L2.\n * @dev Note that the WOETH will be sent to the msg.sender at destination chain as well.\n */\n receive() external payable {\n _zap(msg.sender, msg.value);\n }\n\n function _zap(address receiver, uint256 amount)\n internal\n returns (bytes32 messageId)\n {\n // Estimate fee for zapping.\n uint256 feeAmount = getFee(amount, receiver);\n if (amount < feeAmount) {\n revert AmountLessThanFee();\n }\n\n // Convert only the msg.value - fees amount to WOETH.\n amount -= feeAmount;\n\n // 1.) Zap for OETH\n uint256 oethReceived = oethZapper.deposit{ value: amount }();\n\n // 2.) Wrap the received woeth\n uint256 woethReceived = woethOnSourceChain.deposit(\n oethReceived,\n address(this)\n );\n\n // 3.) Setup params for CCIP transfer\n\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(woethOnSourceChain),\n amount: woethReceived\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({\n receiver: abi.encode(receiver), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n // See: https://docs.chain.link/ccip/best-practices#setting-gaslimit\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n // ZAP ϟ\n //slither-disable-next-line arbitrary-send-eth\n messageId = ccipRouter.ccipSend{ value: feeAmount }(\n destinationChainSelector,\n message\n );\n\n // Emit Zap event with message details\n emit Zap(messageId, msg.sender, receiver, amount);\n\n // Return the message ID\n return messageId;\n }\n}\n" + }, + "hardhat/console.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.4.22 <0.9.0;\n\nlibrary console {\n address constant CONSOLE_ADDRESS =\n 0x000000000000000000636F6e736F6c652e6c6f67;\n\n function _sendLogPayloadImplementation(bytes memory payload) internal view {\n address consoleAddress = CONSOLE_ADDRESS;\n /// @solidity memory-safe-assembly\n assembly {\n pop(\n staticcall(\n gas(),\n consoleAddress,\n add(payload, 32),\n mload(payload),\n 0,\n 0\n )\n )\n }\n }\n\n function _castToPure(\n function(bytes memory) internal view fnIn\n ) internal pure returns (function(bytes memory) pure fnOut) {\n assembly {\n fnOut := fnIn\n }\n }\n\n function _sendLogPayload(bytes memory payload) internal pure {\n _castToPure(_sendLogPayloadImplementation)(payload);\n }\n\n function log() internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log()\"));\n }\n\n function logInt(int256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(int256)\", p0));\n }\n\n function logUint(uint256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256)\", p0));\n }\n\n function logString(string memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n }\n\n function logBool(bool p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n }\n\n function logAddress(address p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n }\n\n function logBytes(bytes memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes)\", p0));\n }\n\n function logBytes1(bytes1 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes1)\", p0));\n }\n\n function logBytes2(bytes2 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes2)\", p0));\n }\n\n function logBytes3(bytes3 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes3)\", p0));\n }\n\n function logBytes4(bytes4 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes4)\", p0));\n }\n\n function logBytes5(bytes5 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes5)\", p0));\n }\n\n function logBytes6(bytes6 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes6)\", p0));\n }\n\n function logBytes7(bytes7 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes7)\", p0));\n }\n\n function logBytes8(bytes8 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes8)\", p0));\n }\n\n function logBytes9(bytes9 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes9)\", p0));\n }\n\n function logBytes10(bytes10 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes10)\", p0));\n }\n\n function logBytes11(bytes11 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes11)\", p0));\n }\n\n function logBytes12(bytes12 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes12)\", p0));\n }\n\n function logBytes13(bytes13 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes13)\", p0));\n }\n\n function logBytes14(bytes14 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes14)\", p0));\n }\n\n function logBytes15(bytes15 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes15)\", p0));\n }\n\n function logBytes16(bytes16 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes16)\", p0));\n }\n\n function logBytes17(bytes17 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes17)\", p0));\n }\n\n function logBytes18(bytes18 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes18)\", p0));\n }\n\n function logBytes19(bytes19 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes19)\", p0));\n }\n\n function logBytes20(bytes20 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes20)\", p0));\n }\n\n function logBytes21(bytes21 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes21)\", p0));\n }\n\n function logBytes22(bytes22 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes22)\", p0));\n }\n\n function logBytes23(bytes23 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes23)\", p0));\n }\n\n function logBytes24(bytes24 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes24)\", p0));\n }\n\n function logBytes25(bytes25 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes25)\", p0));\n }\n\n function logBytes26(bytes26 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes26)\", p0));\n }\n\n function logBytes27(bytes27 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes27)\", p0));\n }\n\n function logBytes28(bytes28 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes28)\", p0));\n }\n\n function logBytes29(bytes29 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes29)\", p0));\n }\n\n function logBytes30(bytes30 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes30)\", p0));\n }\n\n function logBytes31(bytes31 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes31)\", p0));\n }\n\n function logBytes32(bytes32 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes32)\", p0));\n }\n\n function log(uint256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256)\", p0));\n }\n\n function log(string memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n }\n\n function log(bool p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n }\n\n function log(address p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n }\n\n function log(uint256 p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256)\", p0, p1));\n }\n\n function log(uint256 p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string)\", p0, p1));\n }\n\n function log(uint256 p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool)\", p0, p1));\n }\n\n function log(uint256 p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address)\", p0, p1));\n }\n\n function log(string memory p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256)\", p0, p1));\n }\n\n function log(string memory p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string)\", p0, p1));\n }\n\n function log(string memory p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool)\", p0, p1));\n }\n\n function log(string memory p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address)\", p0, p1));\n }\n\n function log(bool p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256)\", p0, p1));\n }\n\n function log(bool p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string)\", p0, p1));\n }\n\n function log(bool p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool)\", p0, p1));\n }\n\n function log(bool p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address)\", p0, p1));\n }\n\n function log(address p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256)\", p0, p1));\n }\n\n function log(address p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string)\", p0, p1));\n }\n\n function log(address p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool)\", p0, p1));\n }\n\n function log(address p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address)\", p0, p1));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,address)\", p0, p1, p2, p3));\n }\n}\n" + }, + "lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC4626 } from \"../../../../interfaces/IERC4626.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\n// From Open Zeppelin draft PR commit:\n// fac43034dca85ff539db3fc8aa2a7084b843d454\n// https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3171\n\nabstract contract ERC4626 is ERC20, IERC4626 {\n IERC20Metadata private immutable _asset;\n\n constructor(IERC20Metadata __asset) {\n _asset = __asset;\n }\n\n /** @dev See {IERC4262-asset} */\n function asset() public view virtual override returns (address) {\n return address(_asset);\n }\n\n /** @dev See {IERC4262-totalAssets} */\n function totalAssets() public view virtual override returns (uint256) {\n return _asset.balanceOf(address(this));\n }\n\n /**\n * @dev See {IERC4262-convertToShares}\n *\n * Will revert if asserts > 0, totalSupply > 0 and totalAssets = 0. That corresponds to a case where any asset\n * would represent an infinite amout of shares.\n */\n function convertToShares(uint256 assets) public view virtual override returns (uint256 shares) {\n uint256 supply = totalSupply();\n\n return\n (assets == 0 || supply == 0)\n ? (assets * 10**decimals()) / 10**_asset.decimals()\n : (assets * supply) / totalAssets();\n }\n\n /** @dev See {IERC4262-convertToAssets} */\n function convertToAssets(uint256 shares) public view virtual override returns (uint256 assets) {\n uint256 supply = totalSupply();\n\n return (supply == 0) ? (shares * 10**_asset.decimals()) / 10**decimals() : (shares * totalAssets()) / supply;\n }\n\n /** @dev See {IERC4262-maxDeposit} */\n function maxDeposit(address) public view virtual override returns (uint256) {\n return type(uint256).max;\n }\n\n /** @dev See {IERC4262-maxMint} */\n function maxMint(address) public view virtual override returns (uint256) {\n return type(uint256).max;\n }\n\n /** @dev See {IERC4262-maxWithdraw} */\n function maxWithdraw(address owner) public view virtual override returns (uint256) {\n return convertToAssets(balanceOf(owner));\n }\n\n /** @dev See {IERC4262-maxRedeem} */\n function maxRedeem(address owner) public view virtual override returns (uint256) {\n return balanceOf(owner);\n }\n\n /** @dev See {IERC4262-previewDeposit} */\n function previewDeposit(uint256 assets) public view virtual override returns (uint256) {\n return convertToShares(assets);\n }\n\n /** @dev See {IERC4262-previewMint} */\n function previewMint(uint256 shares) public view virtual override returns (uint256) {\n uint256 assets = convertToAssets(shares);\n return assets + (convertToShares(assets) < shares ? 1 : 0);\n }\n\n /** @dev See {IERC4262-previewWithdraw} */\n function previewWithdraw(uint256 assets) public view virtual override returns (uint256) {\n uint256 shares = convertToShares(assets);\n return shares + (convertToAssets(shares) < assets ? 1 : 0);\n }\n\n /** @dev See {IERC4262-previewRedeem} */\n function previewRedeem(uint256 shares) public view virtual override returns (uint256) {\n return convertToAssets(shares);\n }\n\n /** @dev See {IERC4262-deposit} */\n function deposit(uint256 assets, address receiver) public virtual override returns (uint256) {\n require(assets <= maxDeposit(receiver), \"ERC4626: deposit more then max\");\n\n address caller = _msgSender();\n uint256 shares = previewDeposit(assets);\n\n // if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through\n // the tokensToSend hook, so we need to transfer before we mint to keep the invariants.\n SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);\n _mint(receiver, shares);\n\n emit Deposit(caller, receiver, assets, shares);\n\n return shares;\n }\n\n /** @dev See {IERC4262-mint} */\n function mint(uint256 shares, address receiver) public virtual override returns (uint256) {\n require(shares <= maxMint(receiver), \"ERC4626: mint more then max\");\n\n address caller = _msgSender();\n uint256 assets = previewMint(shares);\n\n // if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through\n // the tokensToSend hook, so we need to transfer before we mint to keep the invariants.\n SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);\n _mint(receiver, shares);\n\n emit Deposit(caller, receiver, assets, shares);\n\n return assets;\n }\n\n /** @dev See {IERC4262-withdraw} */\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) public virtual override returns (uint256) {\n require(assets <= maxWithdraw(owner), \"ERC4626: withdraw more then max\");\n\n address caller = _msgSender();\n uint256 shares = previewWithdraw(assets);\n\n if (caller != owner) {\n _spendAllowance(owner, caller, shares);\n }\n\n // if _asset is ERC777, transfer can call reenter AFTER the transfer happens through\n // the tokensReceived hook, so we need to transfer after we burn to keep the invariants.\n _burn(owner, shares);\n SafeERC20.safeTransfer(_asset, receiver, assets);\n\n emit Withdraw(caller, receiver, owner, assets, shares);\n\n return shares;\n }\n\n /** @dev See {IERC4262-redeem} */\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) public virtual override returns (uint256) {\n require(shares <= maxRedeem(owner), \"ERC4626: redeem more then max\");\n\n address caller = _msgSender();\n uint256 assets = previewRedeem(shares);\n\n if (caller != owner) {\n _spendAllowance(owner, caller, shares);\n }\n\n // if _asset is ERC777, transfer can call reenter AFTER the transfer happens through\n // the tokensReceived hook, so we need to transfer after we burn to keep the invariants.\n _burn(owner, shares);\n SafeERC20.safeTransfer(_asset, receiver, assets);\n\n emit Withdraw(caller, receiver, owner, assets, shares);\n\n return assets;\n }\n\n // Included here, since this method was not yet present in\n // the version of Open Zeppelin ERC20 code we use.\n function _spendAllowance(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n uint256 currentAllowance = allowance(owner, spender);\n if (currentAllowance != type(uint256).max) {\n require(currentAllowance >= amount, \"ERC20: insufficient allowance\");\n unchecked {\n _approve(owner, spender, currentAllowance - amount);\n }\n }\n }\n}" + }, + "lib/openzeppelin/interfaces/IERC4626.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IERC4626 is IERC20, IERC20Metadata {\n event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);\n\n event Withdraw(\n address indexed caller,\n address indexed receiver,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n\n /**\n * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.\n *\n * - MUST be an ERC-20 token contract.\n * - MUST NOT revert.\n */\n function asset() external view returns (address assetTokenAddress);\n\n /**\n * @dev Returns the total amount of the underlying asset that is “managed” by Vault.\n *\n * - SHOULD include any compounding that occurs from yield.\n * - MUST be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT revert.\n */\n function totalAssets() external view returns (uint256 totalManagedAssets);\n\n /**\n * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal\n * scenario where all the conditions are met.\n *\n * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT show any variations depending on the caller.\n * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n * - MUST NOT revert.\n *\n * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n * from.\n */\n function convertToShares(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal\n * scenario where all the conditions are met.\n *\n * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT show any variations depending on the caller.\n * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n * - MUST NOT revert.\n *\n * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n * from.\n */\n function convertToAssets(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,\n * through a deposit call.\n *\n * - MUST return a limited value if receiver is subject to some deposit limit.\n * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.\n * - MUST NOT revert.\n */\n function maxDeposit(address receiver) external view returns (uint256 maxAssets);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given\n * current on-chain conditions.\n *\n * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit\n * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called\n * in the same transaction.\n * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the\n * deposit would be accepted, regardless if the user has enough tokens approved, etc.\n * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by depositing.\n */\n function previewDeposit(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.\n *\n * - MUST emit the Deposit event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * deposit execution, and are accounted for during deposit.\n * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not\n * approving enough underlying tokens to the Vault contract, etc).\n *\n * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n */\n function deposit(uint256 assets, address receiver) external returns (uint256 shares);\n\n /**\n * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.\n * - MUST return a limited value if receiver is subject to some mint limit.\n * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.\n * - MUST NOT revert.\n */\n function maxMint(address receiver) external view returns (uint256 maxShares);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given\n * current on-chain conditions.\n *\n * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call\n * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the\n * same transaction.\n * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint\n * would be accepted, regardless if the user has enough tokens approved, etc.\n * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by minting.\n */\n function previewMint(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.\n *\n * - MUST emit the Deposit event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint\n * execution, and are accounted for during mint.\n * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not\n * approving enough underlying tokens to the Vault contract, etc).\n *\n * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n */\n function mint(uint256 shares, address receiver) external returns (uint256 assets);\n\n /**\n * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the\n * Vault, through a withdraw call.\n *\n * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n * - MUST NOT revert.\n */\n function maxWithdraw(address owner) external view returns (uint256 maxAssets);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,\n * given current on-chain conditions.\n *\n * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw\n * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if\n * called\n * in the same transaction.\n * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though\n * the withdrawal would be accepted, regardless if the user has enough shares, etc.\n * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by depositing.\n */\n function previewWithdraw(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.\n *\n * - MUST emit the Withdraw event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * withdraw execution, and are accounted for during withdraw.\n * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner\n * not having enough shares, etc).\n *\n * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n * Those methods should be performed separately.\n */\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares);\n\n /**\n * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,\n * through a redeem call.\n *\n * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.\n * - MUST NOT revert.\n */\n function maxRedeem(address owner) external view returns (uint256 maxShares);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,\n * given current on-chain conditions.\n *\n * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call\n * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the\n * same transaction.\n * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the\n * redemption would be accepted, regardless if the user has enough shares, etc.\n * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by redeeming.\n */\n function previewRedeem(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.\n *\n * - MUST emit the Withdraw event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * redeem execution, and are accounted for during redeem.\n * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner\n * not having enough shares, etc).\n *\n * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n * Those methods should be performed separately.\n */\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets);\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.20;\n\nimport {Panic} from \"../Panic.sol\";\nimport {SafeCast} from \"./SafeCast.sol\";\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n enum Rounding {\n Floor, // Toward negative infinity\n Ceil, // Toward positive infinity\n Trunc, // Toward zero\n Expand // Away from zero\n }\n\n /**\n * @dev Return the 512-bit addition of two uint256.\n *\n * The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low.\n */\n function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {\n assembly (\"memory-safe\") {\n low := add(a, b)\n high := lt(low, a)\n }\n }\n\n /**\n * @dev Return the 512-bit multiplication of two uint256.\n *\n * The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low.\n */\n function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {\n // 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use\n // the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n // variables such that product = high * 2²⁵⁶ + low.\n assembly (\"memory-safe\") {\n let mm := mulmod(a, b, not(0))\n low := mul(a, b)\n high := sub(sub(mm, low), lt(mm, low))\n }\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, with a success flag (no overflow).\n */\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a + b;\n success = c >= a;\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).\n */\n function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a - b;\n success = c <= a;\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).\n */\n function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a * b;\n assembly (\"memory-safe\") {\n // Only true when the multiplication doesn't overflow\n // (c / a == b) || (a == 0)\n success := or(eq(div(c, a), b), iszero(a))\n }\n // equivalent to: success ? c : 0\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the division of two unsigned integers, with a success flag (no division by zero).\n */\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n success = b > 0;\n assembly (\"memory-safe\") {\n // The `DIV` opcode returns zero when the denominator is 0.\n result := div(a, b)\n }\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).\n */\n function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n success = b > 0;\n assembly (\"memory-safe\") {\n // The `MOD` opcode returns zero when the denominator is 0.\n result := mod(a, b)\n }\n }\n }\n\n /**\n * @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing.\n */\n function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {\n (bool success, uint256 result) = tryAdd(a, b);\n return ternary(success, result, type(uint256).max);\n }\n\n /**\n * @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.\n */\n function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {\n (, uint256 result) = trySub(a, b);\n return result;\n }\n\n /**\n * @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing.\n */\n function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {\n (bool success, uint256 result) = tryMul(a, b);\n return ternary(success, result, type(uint256).max);\n }\n\n /**\n * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.\n *\n * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.\n * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute\n * one branch when needed, making this function more expensive.\n */\n function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {\n unchecked {\n // branchless ternary works because:\n // b ^ (a ^ b) == a\n // b ^ 0 == b\n return b ^ ((a ^ b) * SafeCast.toUint(condition));\n }\n }\n\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return ternary(a > b, a, b);\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return ternary(a < b, a, b);\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds towards infinity instead\n * of rounding towards zero.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n if (b == 0) {\n // Guarantee the same behavior as in a regular Solidity division.\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n\n // The following calculation ensures accurate ceiling division without overflow.\n // Since a is non-zero, (a - 1) / b will not overflow.\n // The largest possible result occurs when (a - 1) / b is type(uint256).max,\n // but the largest value we can obtain is type(uint256).max - 1, which happens\n // when a = type(uint256).max and b = 1.\n unchecked {\n return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);\n }\n }\n\n /**\n * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or\n * denominator == 0.\n *\n * Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by\n * Uniswap Labs also under MIT license.\n */\n function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {\n unchecked {\n (uint256 high, uint256 low) = mul512(x, y);\n\n // Handle non-overflow cases, 256 by 256 division.\n if (high == 0) {\n // Solidity will revert if denominator == 0, unlike the div opcode on its own.\n // The surrounding unchecked block does not change this fact.\n // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.\n return low / denominator;\n }\n\n // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.\n if (denominator <= high) {\n Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));\n }\n\n ///////////////////////////////////////////////\n // 512 by 256 division.\n ///////////////////////////////////////////////\n\n // Make division exact by subtracting the remainder from [high low].\n uint256 remainder;\n assembly (\"memory-safe\") {\n // Compute remainder using mulmod.\n remainder := mulmod(x, y, denominator)\n\n // Subtract 256 bit number from 512 bit number.\n high := sub(high, gt(remainder, low))\n low := sub(low, remainder)\n }\n\n // Factor powers of two out of denominator and compute largest power of two divisor of denominator.\n // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.\n\n uint256 twos = denominator & (0 - denominator);\n assembly (\"memory-safe\") {\n // Divide denominator by twos.\n denominator := div(denominator, twos)\n\n // Divide [high low] by twos.\n low := div(low, twos)\n\n // Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.\n twos := add(div(sub(0, twos), twos), 1)\n }\n\n // Shift in bits from high into low.\n low |= high * twos;\n\n // Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such\n // that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for\n // four bits. That is, denominator * inv ≡ 1 mod 2⁴.\n uint256 inverse = (3 * denominator) ^ 2;\n\n // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also\n // works in modular arithmetic, doubling the correct bits in each step.\n inverse *= 2 - denominator * inverse; // inverse mod 2⁸\n inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶\n inverse *= 2 - denominator * inverse; // inverse mod 2³²\n inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴\n inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸\n inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶\n\n // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n // This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is\n // less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and high\n // is no longer required.\n result = low * inverse;\n return result;\n }\n }\n\n /**\n * @dev Calculates x * y / denominator with full precision, following the selected rounding direction.\n */\n function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {\n return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);\n }\n\n /**\n * @dev Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256.\n */\n function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) {\n unchecked {\n (uint256 high, uint256 low) = mul512(x, y);\n if (high >= 1 << n) {\n Panic.panic(Panic.UNDER_OVERFLOW);\n }\n return (high << (256 - n)) | (low >> n);\n }\n }\n\n /**\n * @dev Calculates x * y >> n with full precision, following the selected rounding direction.\n */\n function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) {\n return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 0);\n }\n\n /**\n * @dev Calculate the modular multiplicative inverse of a number in Z/nZ.\n *\n * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.\n * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.\n *\n * If the input value is not inversible, 0 is returned.\n *\n * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the\n * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.\n */\n function invMod(uint256 a, uint256 n) internal pure returns (uint256) {\n unchecked {\n if (n == 0) return 0;\n\n // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)\n // Used to compute integers x and y such that: ax + ny = gcd(a, n).\n // When the gcd is 1, then the inverse of a modulo n exists and it's x.\n // ax + ny = 1\n // ax = 1 + (-y)n\n // ax ≡ 1 (mod n) # x is the inverse of a modulo n\n\n // If the remainder is 0 the gcd is n right away.\n uint256 remainder = a % n;\n uint256 gcd = n;\n\n // Therefore the initial coefficients are:\n // ax + ny = gcd(a, n) = n\n // 0a + 1n = n\n int256 x = 0;\n int256 y = 1;\n\n while (remainder != 0) {\n uint256 quotient = gcd / remainder;\n\n (gcd, remainder) = (\n // The old remainder is the next gcd to try.\n remainder,\n // Compute the next remainder.\n // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd\n // where gcd is at most n (capped to type(uint256).max)\n gcd - remainder * quotient\n );\n\n (x, y) = (\n // Increment the coefficient of a.\n y,\n // Decrement the coefficient of n.\n // Can overflow, but the result is casted to uint256 so that the\n // next value of y is \"wrapped around\" to a value between 0 and n - 1.\n x - y * int256(quotient)\n );\n }\n\n if (gcd != 1) return 0; // No inverse exists.\n return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.\n }\n }\n\n /**\n * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.\n *\n * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is\n * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that\n * `a**(p-2)` is the modular multiplicative inverse of a in Fp.\n *\n * NOTE: this function does NOT check that `p` is a prime greater than `2`.\n */\n function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {\n unchecked {\n return Math.modExp(a, p - 2, p);\n }\n }\n\n /**\n * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)\n *\n * Requirements:\n * - modulus can't be zero\n * - underlying staticcall to precompile must succeed\n *\n * IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make\n * sure the chain you're using it on supports the precompiled contract for modular exponentiation\n * at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,\n * the underlying function will succeed given the lack of a revert, but the result may be incorrectly\n * interpreted as 0.\n */\n function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {\n (bool success, uint256 result) = tryModExp(b, e, m);\n if (!success) {\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n return result;\n }\n\n /**\n * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).\n * It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying\n * to operate modulo 0 or if the underlying precompile reverted.\n *\n * IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain\n * you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in\n * https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack\n * of a revert, but the result may be incorrectly interpreted as 0.\n */\n function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {\n if (m == 0) return (false, 0);\n assembly (\"memory-safe\") {\n let ptr := mload(0x40)\n // | Offset | Content | Content (Hex) |\n // |-----------|------------|--------------------------------------------------------------------|\n // | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x60:0x7f | value of b | 0x<.............................................................b> |\n // | 0x80:0x9f | value of e | 0x<.............................................................e> |\n // | 0xa0:0xbf | value of m | 0x<.............................................................m> |\n mstore(ptr, 0x20)\n mstore(add(ptr, 0x20), 0x20)\n mstore(add(ptr, 0x40), 0x20)\n mstore(add(ptr, 0x60), b)\n mstore(add(ptr, 0x80), e)\n mstore(add(ptr, 0xa0), m)\n\n // Given the result < m, it's guaranteed to fit in 32 bytes,\n // so we can use the memory scratch space located at offset 0.\n success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)\n result := mload(0x00)\n }\n }\n\n /**\n * @dev Variant of {modExp} that supports inputs of arbitrary length.\n */\n function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {\n (bool success, bytes memory result) = tryModExp(b, e, m);\n if (!success) {\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n return result;\n }\n\n /**\n * @dev Variant of {tryModExp} that supports inputs of arbitrary length.\n */\n function tryModExp(\n bytes memory b,\n bytes memory e,\n bytes memory m\n ) internal view returns (bool success, bytes memory result) {\n if (_zeroBytes(m)) return (false, new bytes(0));\n\n uint256 mLen = m.length;\n\n // Encode call args in result and move the free memory pointer\n result = abi.encodePacked(b.length, e.length, mLen, b, e, m);\n\n assembly (\"memory-safe\") {\n let dataPtr := add(result, 0x20)\n // Write result on top of args to avoid allocating extra memory.\n success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)\n // Overwrite the length.\n // result.length > returndatasize() is guaranteed because returndatasize() == m.length\n mstore(result, mLen)\n // Set the memory pointer after the returned data.\n mstore(0x40, add(dataPtr, mLen))\n }\n }\n\n /**\n * @dev Returns whether the provided byte array is zero.\n */\n function _zeroBytes(bytes memory byteArray) private pure returns (bool) {\n for (uint256 i = 0; i < byteArray.length; ++i) {\n if (byteArray[i] != 0) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded\n * towards zero.\n *\n * This method is based on Newton's method for computing square roots; the algorithm is restricted to only\n * using integer operations.\n */\n function sqrt(uint256 a) internal pure returns (uint256) {\n unchecked {\n // Take care of easy edge cases when a == 0 or a == 1\n if (a <= 1) {\n return a;\n }\n\n // In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a\n // sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between\n // the current value as `ε_n = | x_n - sqrt(a) |`.\n //\n // For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root\n // of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is\n // bigger than any uint256.\n //\n // By noticing that\n // `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`\n // we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar\n // to the msb function.\n uint256 aa = a;\n uint256 xn = 1;\n\n if (aa >= (1 << 128)) {\n aa >>= 128;\n xn <<= 64;\n }\n if (aa >= (1 << 64)) {\n aa >>= 64;\n xn <<= 32;\n }\n if (aa >= (1 << 32)) {\n aa >>= 32;\n xn <<= 16;\n }\n if (aa >= (1 << 16)) {\n aa >>= 16;\n xn <<= 8;\n }\n if (aa >= (1 << 8)) {\n aa >>= 8;\n xn <<= 4;\n }\n if (aa >= (1 << 4)) {\n aa >>= 4;\n xn <<= 2;\n }\n if (aa >= (1 << 2)) {\n xn <<= 1;\n }\n\n // We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).\n //\n // We can refine our estimation by noticing that the middle of that interval minimizes the error.\n // If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).\n // This is going to be our x_0 (and ε_0)\n xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)\n\n // From here, Newton's method give us:\n // x_{n+1} = (x_n + a / x_n) / 2\n //\n // One should note that:\n // x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a\n // = ((x_n² + a) / (2 * x_n))² - a\n // = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a\n // = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)\n // = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)\n // = (x_n² - a)² / (2 * x_n)²\n // = ((x_n² - a) / (2 * x_n))²\n // ≥ 0\n // Which proves that for all n ≥ 1, sqrt(a) ≤ x_n\n //\n // This gives us the proof of quadratic convergence of the sequence:\n // ε_{n+1} = | x_{n+1} - sqrt(a) |\n // = | (x_n + a / x_n) / 2 - sqrt(a) |\n // = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |\n // = | (x_n - sqrt(a))² / (2 * x_n) |\n // = | ε_n² / (2 * x_n) |\n // = ε_n² / | (2 * x_n) |\n //\n // For the first iteration, we have a special case where x_0 is known:\n // ε_1 = ε_0² / | (2 * x_0) |\n // ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))\n // ≤ 2**(2*e-4) / (3 * 2**(e-1))\n // ≤ 2**(e-3) / 3\n // ≤ 2**(e-3-log2(3))\n // ≤ 2**(e-4.5)\n //\n // For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:\n // ε_{n+1} = ε_n² / | (2 * x_n) |\n // ≤ (2**(e-k))² / (2 * 2**(e-1))\n // ≤ 2**(2*e-2*k) / 2**e\n // ≤ 2**(e-2*k)\n xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above\n xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5\n xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9\n xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18\n xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36\n xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72\n\n // Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision\n // ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either\n // sqrt(a) or sqrt(a) + 1.\n return xn - SafeCast.toUint(xn > a / xn);\n }\n }\n\n /**\n * @dev Calculates sqrt(a), following the selected rounding direction.\n */\n function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = sqrt(a);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);\n }\n }\n\n /**\n * @dev Return the log in base 2 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n */\n function log2(uint256 x) internal pure returns (uint256 r) {\n // If value has upper 128 bits set, log2 result is at least 128\n r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;\n // If upper 64 bits of 128-bit half set, add 64 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;\n // If upper 32 bits of 64-bit half set, add 32 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;\n // If upper 16 bits of 32-bit half set, add 16 to result\n r |= SafeCast.toUint((x >> r) > 0xffff) << 4;\n // If upper 8 bits of 16-bit half set, add 8 to result\n r |= SafeCast.toUint((x >> r) > 0xff) << 3;\n // If upper 4 bits of 8-bit half set, add 4 to result\n r |= SafeCast.toUint((x >> r) > 0xf) << 2;\n\n // Shifts value right by the current result and use it as an index into this lookup table:\n //\n // | x (4 bits) | index | table[index] = MSB position |\n // |------------|---------|-----------------------------|\n // | 0000 | 0 | table[0] = 0 |\n // | 0001 | 1 | table[1] = 0 |\n // | 0010 | 2 | table[2] = 1 |\n // | 0011 | 3 | table[3] = 1 |\n // | 0100 | 4 | table[4] = 2 |\n // | 0101 | 5 | table[5] = 2 |\n // | 0110 | 6 | table[6] = 2 |\n // | 0111 | 7 | table[7] = 2 |\n // | 1000 | 8 | table[8] = 3 |\n // | 1001 | 9 | table[9] = 3 |\n // | 1010 | 10 | table[10] = 3 |\n // | 1011 | 11 | table[11] = 3 |\n // | 1100 | 12 | table[12] = 3 |\n // | 1101 | 13 | table[13] = 3 |\n // | 1110 | 14 | table[14] = 3 |\n // | 1111 | 15 | table[15] = 3 |\n //\n // The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.\n assembly (\"memory-safe\") {\n r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))\n }\n }\n\n /**\n * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log2(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);\n }\n }\n\n /**\n * @dev Return the log in base 10 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n */\n function log10(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >= 10 ** 64) {\n value /= 10 ** 64;\n result += 64;\n }\n if (value >= 10 ** 32) {\n value /= 10 ** 32;\n result += 32;\n }\n if (value >= 10 ** 16) {\n value /= 10 ** 16;\n result += 16;\n }\n if (value >= 10 ** 8) {\n value /= 10 ** 8;\n result += 8;\n }\n if (value >= 10 ** 4) {\n value /= 10 ** 4;\n result += 4;\n }\n if (value >= 10 ** 2) {\n value /= 10 ** 2;\n result += 2;\n }\n if (value >= 10 ** 1) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log10(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);\n }\n }\n\n /**\n * @dev Return the log in base 256 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n *\n * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n */\n function log256(uint256 x) internal pure returns (uint256 r) {\n // If value has upper 128 bits set, log2 result is at least 128\n r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;\n // If upper 64 bits of 128-bit half set, add 64 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;\n // If upper 32 bits of 64-bit half set, add 32 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;\n // If upper 16 bits of 32-bit half set, add 16 to result\n r |= SafeCast.toUint((x >> r) > 0xffff) << 4;\n // Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8\n return (r >> 3) | SafeCast.toUint((x >> r) > 0xff);\n }\n\n /**\n * @dev Return the log in base 256, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log256(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);\n }\n }\n\n /**\n * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.\n */\n function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {\n return uint8(rounding) % 2 == 1;\n }\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)\n// This file was procedurally generated from scripts/generate/templates/SafeCast.js.\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n */\nlibrary SafeCast {\n /**\n * @dev Value doesn't fit in an uint of `bits` size.\n */\n error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);\n\n /**\n * @dev An int value doesn't fit in an uint of `bits` size.\n */\n error SafeCastOverflowedIntToUint(int256 value);\n\n /**\n * @dev Value doesn't fit in an int of `bits` size.\n */\n error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);\n\n /**\n * @dev An uint value doesn't fit in an int of `bits` size.\n */\n error SafeCastOverflowedUintToInt(uint256 value);\n\n /**\n * @dev Returns the downcasted uint248 from uint256, reverting on\n * overflow (when the input is greater than largest uint248).\n *\n * Counterpart to Solidity's `uint248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n */\n function toUint248(uint256 value) internal pure returns (uint248) {\n if (value > type(uint248).max) {\n revert SafeCastOverflowedUintDowncast(248, value);\n }\n return uint248(value);\n }\n\n /**\n * @dev Returns the downcasted uint240 from uint256, reverting on\n * overflow (when the input is greater than largest uint240).\n *\n * Counterpart to Solidity's `uint240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n */\n function toUint240(uint256 value) internal pure returns (uint240) {\n if (value > type(uint240).max) {\n revert SafeCastOverflowedUintDowncast(240, value);\n }\n return uint240(value);\n }\n\n /**\n * @dev Returns the downcasted uint232 from uint256, reverting on\n * overflow (when the input is greater than largest uint232).\n *\n * Counterpart to Solidity's `uint232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n */\n function toUint232(uint256 value) internal pure returns (uint232) {\n if (value > type(uint232).max) {\n revert SafeCastOverflowedUintDowncast(232, value);\n }\n return uint232(value);\n }\n\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n if (value > type(uint224).max) {\n revert SafeCastOverflowedUintDowncast(224, value);\n }\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint216 from uint256, reverting on\n * overflow (when the input is greater than largest uint216).\n *\n * Counterpart to Solidity's `uint216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n */\n function toUint216(uint256 value) internal pure returns (uint216) {\n if (value > type(uint216).max) {\n revert SafeCastOverflowedUintDowncast(216, value);\n }\n return uint216(value);\n }\n\n /**\n * @dev Returns the downcasted uint208 from uint256, reverting on\n * overflow (when the input is greater than largest uint208).\n *\n * Counterpart to Solidity's `uint208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n */\n function toUint208(uint256 value) internal pure returns (uint208) {\n if (value > type(uint208).max) {\n revert SafeCastOverflowedUintDowncast(208, value);\n }\n return uint208(value);\n }\n\n /**\n * @dev Returns the downcasted uint200 from uint256, reverting on\n * overflow (when the input is greater than largest uint200).\n *\n * Counterpart to Solidity's `uint200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n */\n function toUint200(uint256 value) internal pure returns (uint200) {\n if (value > type(uint200).max) {\n revert SafeCastOverflowedUintDowncast(200, value);\n }\n return uint200(value);\n }\n\n /**\n * @dev Returns the downcasted uint192 from uint256, reverting on\n * overflow (when the input is greater than largest uint192).\n *\n * Counterpart to Solidity's `uint192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n */\n function toUint192(uint256 value) internal pure returns (uint192) {\n if (value > type(uint192).max) {\n revert SafeCastOverflowedUintDowncast(192, value);\n }\n return uint192(value);\n }\n\n /**\n * @dev Returns the downcasted uint184 from uint256, reverting on\n * overflow (when the input is greater than largest uint184).\n *\n * Counterpart to Solidity's `uint184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n */\n function toUint184(uint256 value) internal pure returns (uint184) {\n if (value > type(uint184).max) {\n revert SafeCastOverflowedUintDowncast(184, value);\n }\n return uint184(value);\n }\n\n /**\n * @dev Returns the downcasted uint176 from uint256, reverting on\n * overflow (when the input is greater than largest uint176).\n *\n * Counterpart to Solidity's `uint176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n */\n function toUint176(uint256 value) internal pure returns (uint176) {\n if (value > type(uint176).max) {\n revert SafeCastOverflowedUintDowncast(176, value);\n }\n return uint176(value);\n }\n\n /**\n * @dev Returns the downcasted uint168 from uint256, reverting on\n * overflow (when the input is greater than largest uint168).\n *\n * Counterpart to Solidity's `uint168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n */\n function toUint168(uint256 value) internal pure returns (uint168) {\n if (value > type(uint168).max) {\n revert SafeCastOverflowedUintDowncast(168, value);\n }\n return uint168(value);\n }\n\n /**\n * @dev Returns the downcasted uint160 from uint256, reverting on\n * overflow (when the input is greater than largest uint160).\n *\n * Counterpart to Solidity's `uint160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n */\n function toUint160(uint256 value) internal pure returns (uint160) {\n if (value > type(uint160).max) {\n revert SafeCastOverflowedUintDowncast(160, value);\n }\n return uint160(value);\n }\n\n /**\n * @dev Returns the downcasted uint152 from uint256, reverting on\n * overflow (when the input is greater than largest uint152).\n *\n * Counterpart to Solidity's `uint152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n */\n function toUint152(uint256 value) internal pure returns (uint152) {\n if (value > type(uint152).max) {\n revert SafeCastOverflowedUintDowncast(152, value);\n }\n return uint152(value);\n }\n\n /**\n * @dev Returns the downcasted uint144 from uint256, reverting on\n * overflow (when the input is greater than largest uint144).\n *\n * Counterpart to Solidity's `uint144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n */\n function toUint144(uint256 value) internal pure returns (uint144) {\n if (value > type(uint144).max) {\n revert SafeCastOverflowedUintDowncast(144, value);\n }\n return uint144(value);\n }\n\n /**\n * @dev Returns the downcasted uint136 from uint256, reverting on\n * overflow (when the input is greater than largest uint136).\n *\n * Counterpart to Solidity's `uint136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n */\n function toUint136(uint256 value) internal pure returns (uint136) {\n if (value > type(uint136).max) {\n revert SafeCastOverflowedUintDowncast(136, value);\n }\n return uint136(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n if (value > type(uint128).max) {\n revert SafeCastOverflowedUintDowncast(128, value);\n }\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint120 from uint256, reverting on\n * overflow (when the input is greater than largest uint120).\n *\n * Counterpart to Solidity's `uint120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n */\n function toUint120(uint256 value) internal pure returns (uint120) {\n if (value > type(uint120).max) {\n revert SafeCastOverflowedUintDowncast(120, value);\n }\n return uint120(value);\n }\n\n /**\n * @dev Returns the downcasted uint112 from uint256, reverting on\n * overflow (when the input is greater than largest uint112).\n *\n * Counterpart to Solidity's `uint112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n */\n function toUint112(uint256 value) internal pure returns (uint112) {\n if (value > type(uint112).max) {\n revert SafeCastOverflowedUintDowncast(112, value);\n }\n return uint112(value);\n }\n\n /**\n * @dev Returns the downcasted uint104 from uint256, reverting on\n * overflow (when the input is greater than largest uint104).\n *\n * Counterpart to Solidity's `uint104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n */\n function toUint104(uint256 value) internal pure returns (uint104) {\n if (value > type(uint104).max) {\n revert SafeCastOverflowedUintDowncast(104, value);\n }\n return uint104(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n if (value > type(uint96).max) {\n revert SafeCastOverflowedUintDowncast(96, value);\n }\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint88 from uint256, reverting on\n * overflow (when the input is greater than largest uint88).\n *\n * Counterpart to Solidity's `uint88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n */\n function toUint88(uint256 value) internal pure returns (uint88) {\n if (value > type(uint88).max) {\n revert SafeCastOverflowedUintDowncast(88, value);\n }\n return uint88(value);\n }\n\n /**\n * @dev Returns the downcasted uint80 from uint256, reverting on\n * overflow (when the input is greater than largest uint80).\n *\n * Counterpart to Solidity's `uint80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n */\n function toUint80(uint256 value) internal pure returns (uint80) {\n if (value > type(uint80).max) {\n revert SafeCastOverflowedUintDowncast(80, value);\n }\n return uint80(value);\n }\n\n /**\n * @dev Returns the downcasted uint72 from uint256, reverting on\n * overflow (when the input is greater than largest uint72).\n *\n * Counterpart to Solidity's `uint72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n */\n function toUint72(uint256 value) internal pure returns (uint72) {\n if (value > type(uint72).max) {\n revert SafeCastOverflowedUintDowncast(72, value);\n }\n return uint72(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n if (value > type(uint64).max) {\n revert SafeCastOverflowedUintDowncast(64, value);\n }\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint56 from uint256, reverting on\n * overflow (when the input is greater than largest uint56).\n *\n * Counterpart to Solidity's `uint56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n */\n function toUint56(uint256 value) internal pure returns (uint56) {\n if (value > type(uint56).max) {\n revert SafeCastOverflowedUintDowncast(56, value);\n }\n return uint56(value);\n }\n\n /**\n * @dev Returns the downcasted uint48 from uint256, reverting on\n * overflow (when the input is greater than largest uint48).\n *\n * Counterpart to Solidity's `uint48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n */\n function toUint48(uint256 value) internal pure returns (uint48) {\n if (value > type(uint48).max) {\n revert SafeCastOverflowedUintDowncast(48, value);\n }\n return uint48(value);\n }\n\n /**\n * @dev Returns the downcasted uint40 from uint256, reverting on\n * overflow (when the input is greater than largest uint40).\n *\n * Counterpart to Solidity's `uint40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n */\n function toUint40(uint256 value) internal pure returns (uint40) {\n if (value > type(uint40).max) {\n revert SafeCastOverflowedUintDowncast(40, value);\n }\n return uint40(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n if (value > type(uint32).max) {\n revert SafeCastOverflowedUintDowncast(32, value);\n }\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint24 from uint256, reverting on\n * overflow (when the input is greater than largest uint24).\n *\n * Counterpart to Solidity's `uint24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n */\n function toUint24(uint256 value) internal pure returns (uint24) {\n if (value > type(uint24).max) {\n revert SafeCastOverflowedUintDowncast(24, value);\n }\n return uint24(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n if (value > type(uint16).max) {\n revert SafeCastOverflowedUintDowncast(16, value);\n }\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n if (value > type(uint8).max) {\n revert SafeCastOverflowedUintDowncast(8, value);\n }\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n if (value < 0) {\n revert SafeCastOverflowedIntToUint(value);\n }\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int248 from int256, reverting on\n * overflow (when the input is less than smallest int248 or\n * greater than largest int248).\n *\n * Counterpart to Solidity's `int248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n */\n function toInt248(int256 value) internal pure returns (int248 downcasted) {\n downcasted = int248(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(248, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int240 from int256, reverting on\n * overflow (when the input is less than smallest int240 or\n * greater than largest int240).\n *\n * Counterpart to Solidity's `int240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n */\n function toInt240(int256 value) internal pure returns (int240 downcasted) {\n downcasted = int240(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(240, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int232 from int256, reverting on\n * overflow (when the input is less than smallest int232 or\n * greater than largest int232).\n *\n * Counterpart to Solidity's `int232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n */\n function toInt232(int256 value) internal pure returns (int232 downcasted) {\n downcasted = int232(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(232, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int224 from int256, reverting on\n * overflow (when the input is less than smallest int224 or\n * greater than largest int224).\n *\n * Counterpart to Solidity's `int224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toInt224(int256 value) internal pure returns (int224 downcasted) {\n downcasted = int224(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(224, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int216 from int256, reverting on\n * overflow (when the input is less than smallest int216 or\n * greater than largest int216).\n *\n * Counterpart to Solidity's `int216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n */\n function toInt216(int256 value) internal pure returns (int216 downcasted) {\n downcasted = int216(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(216, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int208 from int256, reverting on\n * overflow (when the input is less than smallest int208 or\n * greater than largest int208).\n *\n * Counterpart to Solidity's `int208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n */\n function toInt208(int256 value) internal pure returns (int208 downcasted) {\n downcasted = int208(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(208, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int200 from int256, reverting on\n * overflow (when the input is less than smallest int200 or\n * greater than largest int200).\n *\n * Counterpart to Solidity's `int200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n */\n function toInt200(int256 value) internal pure returns (int200 downcasted) {\n downcasted = int200(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(200, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int192 from int256, reverting on\n * overflow (when the input is less than smallest int192 or\n * greater than largest int192).\n *\n * Counterpart to Solidity's `int192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n */\n function toInt192(int256 value) internal pure returns (int192 downcasted) {\n downcasted = int192(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(192, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int184 from int256, reverting on\n * overflow (when the input is less than smallest int184 or\n * greater than largest int184).\n *\n * Counterpart to Solidity's `int184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n */\n function toInt184(int256 value) internal pure returns (int184 downcasted) {\n downcasted = int184(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(184, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int176 from int256, reverting on\n * overflow (when the input is less than smallest int176 or\n * greater than largest int176).\n *\n * Counterpart to Solidity's `int176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n */\n function toInt176(int256 value) internal pure returns (int176 downcasted) {\n downcasted = int176(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(176, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int168 from int256, reverting on\n * overflow (when the input is less than smallest int168 or\n * greater than largest int168).\n *\n * Counterpart to Solidity's `int168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n */\n function toInt168(int256 value) internal pure returns (int168 downcasted) {\n downcasted = int168(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(168, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int160 from int256, reverting on\n * overflow (when the input is less than smallest int160 or\n * greater than largest int160).\n *\n * Counterpart to Solidity's `int160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n */\n function toInt160(int256 value) internal pure returns (int160 downcasted) {\n downcasted = int160(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(160, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int152 from int256, reverting on\n * overflow (when the input is less than smallest int152 or\n * greater than largest int152).\n *\n * Counterpart to Solidity's `int152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n */\n function toInt152(int256 value) internal pure returns (int152 downcasted) {\n downcasted = int152(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(152, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int144 from int256, reverting on\n * overflow (when the input is less than smallest int144 or\n * greater than largest int144).\n *\n * Counterpart to Solidity's `int144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n */\n function toInt144(int256 value) internal pure returns (int144 downcasted) {\n downcasted = int144(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(144, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int136 from int256, reverting on\n * overflow (when the input is less than smallest int136 or\n * greater than largest int136).\n *\n * Counterpart to Solidity's `int136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n */\n function toInt136(int256 value) internal pure returns (int136 downcasted) {\n downcasted = int136(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(136, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toInt128(int256 value) internal pure returns (int128 downcasted) {\n downcasted = int128(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(128, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int120 from int256, reverting on\n * overflow (when the input is less than smallest int120 or\n * greater than largest int120).\n *\n * Counterpart to Solidity's `int120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n */\n function toInt120(int256 value) internal pure returns (int120 downcasted) {\n downcasted = int120(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(120, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int112 from int256, reverting on\n * overflow (when the input is less than smallest int112 or\n * greater than largest int112).\n *\n * Counterpart to Solidity's `int112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n */\n function toInt112(int256 value) internal pure returns (int112 downcasted) {\n downcasted = int112(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(112, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int104 from int256, reverting on\n * overflow (when the input is less than smallest int104 or\n * greater than largest int104).\n *\n * Counterpart to Solidity's `int104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n */\n function toInt104(int256 value) internal pure returns (int104 downcasted) {\n downcasted = int104(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(104, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int96 from int256, reverting on\n * overflow (when the input is less than smallest int96 or\n * greater than largest int96).\n *\n * Counterpart to Solidity's `int96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toInt96(int256 value) internal pure returns (int96 downcasted) {\n downcasted = int96(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(96, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int88 from int256, reverting on\n * overflow (when the input is less than smallest int88 or\n * greater than largest int88).\n *\n * Counterpart to Solidity's `int88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n */\n function toInt88(int256 value) internal pure returns (int88 downcasted) {\n downcasted = int88(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(88, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int80 from int256, reverting on\n * overflow (when the input is less than smallest int80 or\n * greater than largest int80).\n *\n * Counterpart to Solidity's `int80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n */\n function toInt80(int256 value) internal pure returns (int80 downcasted) {\n downcasted = int80(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(80, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int72 from int256, reverting on\n * overflow (when the input is less than smallest int72 or\n * greater than largest int72).\n *\n * Counterpart to Solidity's `int72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n */\n function toInt72(int256 value) internal pure returns (int72 downcasted) {\n downcasted = int72(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(72, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toInt64(int256 value) internal pure returns (int64 downcasted) {\n downcasted = int64(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(64, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int56 from int256, reverting on\n * overflow (when the input is less than smallest int56 or\n * greater than largest int56).\n *\n * Counterpart to Solidity's `int56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n */\n function toInt56(int256 value) internal pure returns (int56 downcasted) {\n downcasted = int56(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(56, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int48 from int256, reverting on\n * overflow (when the input is less than smallest int48 or\n * greater than largest int48).\n *\n * Counterpart to Solidity's `int48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n */\n function toInt48(int256 value) internal pure returns (int48 downcasted) {\n downcasted = int48(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(48, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int40 from int256, reverting on\n * overflow (when the input is less than smallest int40 or\n * greater than largest int40).\n *\n * Counterpart to Solidity's `int40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n */\n function toInt40(int256 value) internal pure returns (int40 downcasted) {\n downcasted = int40(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(40, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toInt32(int256 value) internal pure returns (int32 downcasted) {\n downcasted = int32(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(32, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int24 from int256, reverting on\n * overflow (when the input is less than smallest int24 or\n * greater than largest int24).\n *\n * Counterpart to Solidity's `int24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n */\n function toInt24(int256 value) internal pure returns (int24 downcasted) {\n downcasted = int24(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(24, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toInt16(int256 value) internal pure returns (int16 downcasted) {\n downcasted = int16(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(16, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n */\n function toInt8(int256 value) internal pure returns (int8 downcasted) {\n downcasted = int8(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(8, value);\n }\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n if (value > uint256(type(int256).max)) {\n revert SafeCastOverflowedUintToInt(value);\n }\n return int256(value);\n }\n\n /**\n * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.\n */\n function toUint(bool b) internal pure returns (uint256 u) {\n assembly (\"memory-safe\") {\n u := iszero(iszero(b))\n }\n }\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/Panic.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Helper library for emitting standardized panic codes.\n *\n * ```solidity\n * contract Example {\n * using Panic for uint256;\n *\n * // Use any of the declared internal constants\n * function foo() { Panic.GENERIC.panic(); }\n *\n * // Alternatively\n * function foo() { Panic.panic(Panic.GENERIC); }\n * }\n * ```\n *\n * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].\n *\n * _Available since v5.1._\n */\n// slither-disable-next-line unused-state\nlibrary Panic {\n /// @dev generic / unspecified error\n uint256 internal constant GENERIC = 0x00;\n /// @dev used by the assert() builtin\n uint256 internal constant ASSERT = 0x01;\n /// @dev arithmetic underflow or overflow\n uint256 internal constant UNDER_OVERFLOW = 0x11;\n /// @dev division or modulo by zero\n uint256 internal constant DIVISION_BY_ZERO = 0x12;\n /// @dev enum conversion error\n uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;\n /// @dev invalid encoding in storage\n uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;\n /// @dev empty array pop\n uint256 internal constant EMPTY_ARRAY_POP = 0x31;\n /// @dev array out of bounds access\n uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;\n /// @dev resource error (too large allocation or too large array)\n uint256 internal constant RESOURCE_ERROR = 0x41;\n /// @dev calling invalid internal function\n uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;\n\n /// @dev Reverts with a panic code. Recommended to use with\n /// the internal constants with predefined codes.\n function panic(uint256 code) internal pure {\n assembly (\"memory-safe\") {\n mstore(0x00, 0x4e487b71)\n mstore(0x20, code)\n revert(0x1c, 0x24)\n }\n }\n}" + }, + "lib/rooster/v2-common/libraries/Constants.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\n// factory contraints on pools\nuint8 constant MAX_PROTOCOL_FEE_RATIO_D3 = 0.25e3; // 25%\nuint256 constant MAX_PROTOCOL_LENDING_FEE_RATE_D18 = 0.02e18; // 2%\nuint64 constant MAX_POOL_FEE_D18 = 0.9e18; // 90%\nuint64 constant MIN_LOOKBACK = 1 seconds;\n\n// pool constraints\nuint8 constant NUMBER_OF_KINDS = 4;\nint32 constant NUMBER_OF_KINDS_32 = int32(int8(NUMBER_OF_KINDS));\nuint256 constant MAX_TICK = 322_378; // max price 1e14 in D18 scale\nint32 constant MAX_TICK_32 = int32(int256(MAX_TICK));\nint32 constant MIN_TICK_32 = int32(-int256(MAX_TICK));\nuint256 constant MAX_BINS_TO_MERGE = 3;\nuint128 constant MINIMUM_LIQUIDITY = 1e8;\n\n// accessor named constants\nuint8 constant ALL_KINDS_MASK = 0xF; // 0b1111\nuint8 constant PERMISSIONED_LIQUIDITY_MASK = 0x10; // 0b010000\nuint8 constant PERMISSIONED_SWAP_MASK = 0x20; // 0b100000\nuint8 constant OPTIONS_MASK = ALL_KINDS_MASK | PERMISSIONED_LIQUIDITY_MASK | PERMISSIONED_SWAP_MASK; // 0b111111\n\n// named values\naddress constant MERGED_LP_BALANCE_ADDRESS = address(0);\nuint256 constant MERGED_LP_BALANCE_SUBACCOUNT = 0;\nuint128 constant ONE = 1e18;\nuint128 constant ONE_SQUARED = 1e36;\nint256 constant INT256_ONE = 1e18;\nuint256 constant ONE_D8 = 1e8;\nuint256 constant ONE_D3 = 1e3;\nint40 constant INT_ONE_D8 = 1e8;\nint40 constant HALF_TICK_D8 = 0.5e8;\nuint8 constant DEFAULT_DECIMALS = 18;\nuint256 constant DEFAULT_SCALE = 1;\nbytes constant EMPTY_PRICE_BREAKS = hex\"010000000000000000000000\";" + }, + "lib/rooster/v2-common/libraries/Math.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport {Math as OzMath} from \"../../openzeppelin-custom/contracts/utils/math/Math.sol\";\n\nimport {ONE, DEFAULT_SCALE, DEFAULT_DECIMALS, INT_ONE_D8, ONE_SQUARED} from \"./Constants.sol\";\n\n/**\n * @notice Math functions.\n */\nlibrary Math {\n /**\n * @notice Returns the lesser of two values.\n * @param x First uint256 value.\n * @param y Second uint256 value.\n */\n function min(uint256 x, uint256 y) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), lt(y, x)))\n }\n }\n\n /**\n * @notice Returns the lesser of two uint128 values.\n * @param x First uint128 value.\n * @param y Second uint128 value.\n */\n function min128(uint128 x, uint128 y) internal pure returns (uint128 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), lt(y, x)))\n }\n }\n\n /**\n * @notice Returns the lesser of two int256 values.\n * @param x First int256 value.\n * @param y Second int256 value.\n */\n function min(int256 x, int256 y) internal pure returns (int256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), slt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two uint256 values.\n * @param x First uint256 value.\n * @param y Second uint256 value.\n */\n function max(uint256 x, uint256 y) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), gt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two int256 values.\n * @param x First int256 value.\n * @param y Second int256 value.\n */\n function max(int256 x, int256 y) internal pure returns (int256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), sgt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two uint128 values.\n * @param x First uint128 value.\n * @param y Second uint128 value.\n */\n function max128(uint128 x, uint128 y) internal pure returns (uint128 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), gt(y, x)))\n }\n }\n\n /**\n * @notice Thresholds a value to be within the specified bounds.\n * @param value The value to bound.\n * @param lowerLimit The minimum allowable value.\n * @param upperLimit The maximum allowable value.\n */\n function boundValue(\n uint256 value,\n uint256 lowerLimit,\n uint256 upperLimit\n ) internal pure returns (uint256 outputValue) {\n outputValue = min(max(value, lowerLimit), upperLimit);\n }\n\n /**\n * @notice Returns the difference between two uint128 values or zero if the result would be negative.\n * @param x The minuend.\n * @param y The subtrahend.\n */\n function clip128(uint128 x, uint128 y) internal pure returns (uint128) {\n unchecked {\n return x < y ? 0 : x - y;\n }\n }\n\n /**\n * @notice Returns the difference between two uint256 values or zero if the result would be negative.\n * @param x The minuend.\n * @param y The subtrahend.\n */\n function clip(uint256 x, uint256 y) internal pure returns (uint256) {\n unchecked {\n return x < y ? 0 : x - y;\n }\n }\n\n /**\n * @notice Divides one uint256 by another, rounding down to the nearest\n * integer.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divFloor(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivFloor(x, ONE, y);\n }\n\n /**\n * @notice Divides one uint256 by another, rounding up to the nearest integer.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divCeil(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivCeil(x, ONE, y);\n }\n\n /**\n * @notice Multiplies two uint256 values and then divides by ONE, rounding down.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulFloor(uint256 x, uint256 y) internal pure returns (uint256) {\n return OzMath.mulDiv(x, y, ONE);\n }\n\n /**\n * @notice Multiplies two uint256 values and then divides by ONE, rounding up.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulCeil(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivCeil(x, y, ONE);\n }\n\n /**\n * @notice Calculates the multiplicative inverse of a uint256, rounding down.\n * @param x The value to invert.\n */\n function invFloor(uint256 x) internal pure returns (uint256) {\n unchecked {\n return ONE_SQUARED / x;\n }\n }\n\n /**\n * @notice Calculates the multiplicative inverse of a uint256, rounding up.\n * @param denominator The value to invert.\n */\n function invCeil(uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // divide z - 1 by the denominator and add 1.\n z := add(div(sub(ONE_SQUARED, 1), denominator), 1)\n }\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding down.\n * @param x The multiplicand.\n * @param y The multiplier.\n * @param k The divisor.\n */\n function mulDivFloor(uint256 x, uint256 y, uint256 k) internal pure returns (uint256 result) {\n result = OzMath.mulDiv(x, y, max(1, k));\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding up if there's a remainder.\n * @param x The multiplicand.\n * @param y The multiplier.\n * @param k The divisor.\n */\n function mulDivCeil(uint256 x, uint256 y, uint256 k) internal pure returns (uint256 result) {\n result = mulDivFloor(x, y, k);\n if (mulmod(x, y, max(1, k)) != 0) result = result + 1;\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding\n * down. Will revert if `x * y` is larger than `type(uint256).max`.\n * @param x The first operand for multiplication.\n * @param y The second operand for multiplication.\n * @param denominator The divisor after multiplication.\n */\n function mulDivDown(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // Store x * y in z for now.\n z := mul(x, y)\n if iszero(denominator) {\n denominator := 1\n }\n\n if iszero(or(iszero(x), eq(div(z, x), y))) {\n revert(0, 0)\n }\n\n // Divide z by the denominator.\n z := div(z, denominator)\n }\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding\n * up. Will revert if `x * y` is larger than `type(uint256).max`.\n * @param x The first operand for multiplication.\n * @param y The second operand for multiplication.\n * @param denominator The divisor after multiplication.\n */\n function mulDivUp(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // Store x * y in z for now.\n z := mul(x, y)\n if iszero(denominator) {\n denominator := 1\n }\n\n if iszero(or(iszero(x), eq(div(z, x), y))) {\n revert(0, 0)\n }\n\n // First, divide z - 1 by the denominator and add 1.\n // We allow z - 1 to underflow if z is 0, because we multiply the\n // end result by 0 if z is zero, ensuring we return 0 if z is zero.\n z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))\n }\n }\n\n /**\n * @notice Multiplies a uint256 by another and divides by a constant,\n * rounding down. Will revert if `x * y` is larger than\n * `type(uint256).max`.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulDown(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivDown(x, y, ONE);\n }\n\n /**\n * @notice Divides a uint256 by another, rounding down the result. Will\n * revert if `x * 1e18` is larger than `type(uint256).max`.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divDown(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivDown(x, ONE, y);\n }\n\n /**\n * @notice Divides a uint256 by another, rounding up the result. Will\n * revert if `x * 1e18` is larger than `type(uint256).max`.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divUp(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivUp(x, ONE, y);\n }\n\n /**\n * @notice Scales a number based on a difference in decimals from a default.\n * @param decimals The new decimal precision.\n */\n function scale(uint8 decimals) internal pure returns (uint256) {\n unchecked {\n if (decimals == DEFAULT_DECIMALS) {\n return DEFAULT_SCALE;\n } else {\n return 10 ** (DEFAULT_DECIMALS - decimals);\n }\n }\n }\n\n /**\n * @notice Adjusts a scaled amount to the token decimal scale.\n * @param amount The scaled amount.\n * @param scaleFactor The scaling factor to adjust by.\n * @param ceil Whether to round up (true) or down (false).\n */\n function ammScaleToTokenScale(uint256 amount, uint256 scaleFactor, bool ceil) internal pure returns (uint256 z) {\n unchecked {\n if (scaleFactor == DEFAULT_SCALE || amount == 0) {\n return amount;\n } else {\n if (!ceil) return amount / scaleFactor;\n assembly (\"memory-safe\") {\n z := add(div(sub(amount, 1), scaleFactor), 1)\n }\n }\n }\n }\n\n /**\n * @notice Adjusts a token amount to the D18 AMM scale.\n * @param amount The amount in token scale.\n * @param scaleFactor The scale factor for adjustment.\n */\n function tokenScaleToAmmScale(uint256 amount, uint256 scaleFactor) internal pure returns (uint256) {\n if (scaleFactor == DEFAULT_SCALE) {\n return amount;\n } else {\n return amount * scaleFactor;\n }\n }\n\n /**\n * @notice Returns the absolute value of a signed 32-bit integer.\n * @param x The integer to take the absolute value of.\n */\n function abs32(int32 x) internal pure returns (uint32) {\n unchecked {\n return uint32(x < 0 ? -x : x);\n }\n }\n\n /**\n * @notice Returns the absolute value of a signed 256-bit integer.\n * @param x The integer to take the absolute value of.\n */\n function abs(int256 x) internal pure returns (uint256) {\n unchecked {\n return uint256(x < 0 ? -x : x);\n }\n }\n\n /**\n * @notice Calculates the integer square root of a uint256 rounded down.\n * @param x The number to take the square root of.\n */\n function sqrt(uint256 x) internal pure returns (uint256 z) {\n // from https://github.com/transmissions11/solmate/blob/e8f96f25d48fe702117ce76c79228ca4f20206cb/src/utils/FixedPointMathLib.sol\n assembly (\"memory-safe\") {\n let y := x\n z := 181\n\n if iszero(lt(y, 0x10000000000000000000000000000000000)) {\n y := shr(128, y)\n z := shl(64, z)\n }\n if iszero(lt(y, 0x1000000000000000000)) {\n y := shr(64, y)\n z := shl(32, z)\n }\n if iszero(lt(y, 0x10000000000)) {\n y := shr(32, y)\n z := shl(16, z)\n }\n if iszero(lt(y, 0x1000000)) {\n y := shr(16, y)\n z := shl(8, z)\n }\n\n z := shr(18, mul(z, add(y, 65536)))\n\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n\n z := sub(z, lt(div(x, z), z))\n }\n }\n\n /**\n * @notice Computes the floor of a D8-scaled number as an int32, ignoring\n * potential overflow in the cast.\n * @param val The D8-scaled number.\n */\n function floorD8Unchecked(int256 val) internal pure returns (int32) {\n int32 val32;\n bool check;\n unchecked {\n val32 = int32(val / INT_ONE_D8);\n check = (val < 0 && val % INT_ONE_D8 != 0);\n }\n return check ? val32 - 1 : val32;\n }\n}" + }, + "lib/rooster/v2-common/libraries/TickMath.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport {Math as OzMath} from \"../../openzeppelin-custom/contracts/utils/math/Math.sol\";\nimport {Math} from \"./Math.sol\";\nimport {MAX_TICK, ONE} from \"./Constants.sol\";\n\n/**\n * @notice Math functions related to tick operations.\n */\n// slither-disable-start divide-before-multiply\nlibrary TickMath {\n using Math for uint256;\n\n error TickMaxExceeded(int256 tick);\n\n /**\n * @notice Compute the lower and upper sqrtPrice of a tick.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n */\n function tickSqrtPrices(\n uint256 tickSpacing,\n int32 _tick\n ) internal pure returns (uint256 sqrtLowerPrice, uint256 sqrtUpperPrice) {\n unchecked {\n sqrtLowerPrice = tickSqrtPrice(tickSpacing, _tick);\n sqrtUpperPrice = tickSqrtPrice(tickSpacing, _tick + 1);\n }\n }\n\n /**\n * @notice Compute the base tick value from the pool tick and the\n * tickSpacing. Revert if base tick is beyond the max tick boundary.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n */\n function subTickIndex(uint256 tickSpacing, int32 _tick) internal pure returns (uint32 subTick) {\n subTick = Math.abs32(_tick);\n subTick *= uint32(tickSpacing);\n if (subTick > MAX_TICK) {\n revert TickMaxExceeded(_tick);\n }\n }\n\n /**\n * @notice Calculate the square root price for a given tick and tick spacing.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n * @return _result The square root price.\n */\n function tickSqrtPrice(uint256 tickSpacing, int32 _tick) internal pure returns (uint256 _result) {\n unchecked {\n uint256 tick = subTickIndex(tickSpacing, _tick);\n\n uint256 ratio = tick & 0x1 != 0 ? 0xfffcb933bd6fad9d3af5f0b9f25db4d6 : 0x100000000000000000000000000000000;\n if (tick & 0x2 != 0) ratio = (ratio * 0xfff97272373d41fd789c8cb37ffcaa1c) >> 128;\n if (tick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656ac9229c67059486f389) >> 128;\n if (tick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e81259b3cddc7a064941) >> 128;\n if (tick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f67b19e8887e0bd251eb7) >> 128;\n if (tick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98cd2e57b660be99eb2c4a) >> 128;\n if (tick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c9838804e327cb417cafcb) >> 128;\n if (tick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99d51e2cc356c2f617dbe0) >> 128;\n if (tick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900aecf64236ab31f1f9dcb5) >> 128;\n if (tick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac4d9194200696907cf2e37) >> 128;\n if (tick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b88206f8abe8a3b44dd9be) >> 128;\n if (tick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c578ef4f1d17b2b235d480) >> 128;\n if (tick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd254ee83bdd3f248e7e785e) >> 128;\n if (tick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d8f7dd10e744d913d033333) >> 128;\n if (tick & 0x4000 != 0) ratio = (ratio * 0x70d869a156ddd32a39e257bc3f50aa9b) >> 128;\n if (tick & 0x8000 != 0) ratio = (ratio * 0x31be135f97da6e09a19dc367e3b6da40) >> 128;\n if (tick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7e5a9780b0cc4e25d61a56) >> 128;\n if (tick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedbcb3a6ccb7ce618d14225) >> 128;\n if (tick & 0x40000 != 0) ratio = (ratio * 0x2216e584f630389b2052b8db590e) >> 128;\n if (_tick > 0) ratio = type(uint256).max / ratio;\n _result = (ratio * ONE) >> 128;\n }\n }\n\n /**\n * @notice Calculate liquidity of a tick.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n */\n function getTickL(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n ) internal pure returns (uint256 liquidity) {\n // known:\n // - sqrt price values are different\n // - reserveA and reserveB fit in 128 bit\n // - sqrt price is in (1e-7, 1e7)\n // - D18 max for uint256 is 1.15e59\n // - D18 min is 1e-18\n\n unchecked {\n // diff is in (5e-12, 4e6); max tick spacing is 10_000\n uint256 diff = sqrtUpperTickPrice - sqrtLowerTickPrice;\n\n // Need to maximize precision by shifting small values A and B up so\n // that they use more of the available bit range. Two constraints to\n // consider: we need A * B * diff / sqrtPrice to be bigger than 1e-18\n // when the bump is not in play. This constrains the threshold for\n // bumping to be at least 77 bit; ie, either a or b needs 2^77 which\n // means that term A * B * diff / sqrtPrice > 1e-18.\n //\n // At the other end, the second constraint is that b^2 needs to fit in\n // a 256-bit number, so, post bump, the max reserve value needs to be\n // less than 6e22. With a 78-bit threshold and a 57-bit bump, we have A\n // and B are in (1.4e-1, 4.4e22 (2^(78+57))) with bump, and one of A or\n // B is at least 2^78 without the bump, but the other reserve value may\n // be as small as 1 wei.\n uint256 precisionBump = 0;\n if ((reserveA >> 78) == 0 && (reserveB >> 78) == 0) {\n precisionBump = 57;\n reserveA <<= precisionBump;\n reserveB <<= precisionBump;\n }\n\n if (reserveB == 0) return Math.divDown(reserveA, diff) >> precisionBump;\n if (reserveA == 0)\n return Math.mulDivDown(reserveB.mulDown(sqrtLowerTickPrice), sqrtUpperTickPrice, diff) >> precisionBump;\n\n // b is in (7.2e-9 (2^57 / 1e7 / 2), 2.8e29 (2^(78+57) * 1e7 / 2)) with bump\n // b is in a subset of the same range without bump\n uint256 b = (reserveA.divDown(sqrtUpperTickPrice) + reserveB.mulDown(sqrtLowerTickPrice)) >> 1;\n\n // b^2 is in (5.1e-17, 4.8e58); and will not overflow on either end;\n // A*B is in (3e-13 (2^78 / 1e18 * 1e-18), 1.9e45) without bump and is in a subset range with bump\n // A*B*diff/sqrtUpper is in (1.5e-17 (3e-13 * 5e-12 * 1e7), 7.6e58);\n\n // Since b^2 is at the upper edge of the precision range, we are not\n // able to multiply the argument of the sqrt by 1e18, instead, we move\n // this factor outside of the sqrt. The resulting loss of precision\n // means that this liquidity value is a lower bound on the tick\n // liquidity\n return\n OzMath.mulDiv(\n b +\n Math.sqrt(\n (OzMath.mulDiv(b, b, ONE) +\n OzMath.mulDiv(reserveB.mulFloor(reserveA), diff, sqrtUpperTickPrice))\n ) *\n 1e9,\n sqrtUpperTickPrice,\n diff\n ) >> precisionBump;\n }\n }\n\n /**\n * @notice Calculate square root price of a tick. Returns left edge of the\n * tick if the tick has no reserves.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n * @return sqrtPrice The calculated square root price.\n */\n function getSqrtPrice(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice,\n uint256 liquidity\n ) internal pure returns (uint256 sqrtPrice) {\n unchecked {\n if (reserveA == 0) {\n return sqrtLowerTickPrice;\n }\n if (reserveB == 0) {\n return sqrtUpperTickPrice;\n }\n sqrtPrice = Math.sqrt(\n ONE *\n (reserveA + liquidity.mulDown(sqrtLowerTickPrice)).divDown(\n reserveB + liquidity.divDown(sqrtUpperTickPrice)\n )\n );\n sqrtPrice = Math.boundValue(sqrtPrice, sqrtLowerTickPrice, sqrtUpperTickPrice);\n }\n }\n\n /**\n * @notice Calculate square root price of a tick. Returns left edge of the\n * tick if the tick has no reserves.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n * @return sqrtPrice The calculated square root price.\n * @return liquidity The calculated liquidity.\n */\n function getTickSqrtPriceAndL(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n ) internal pure returns (uint256 sqrtPrice, uint256 liquidity) {\n liquidity = getTickL(reserveA, reserveB, sqrtLowerTickPrice, sqrtUpperTickPrice);\n sqrtPrice = getSqrtPrice(reserveA, reserveB, sqrtLowerTickPrice, sqrtUpperTickPrice, liquidity);\n }\n}\n// slither-disable-end divide-before-multiply" + }, + "solidity-bytes-utils/contracts/BytesLib.sol": { + "content": "// SPDX-License-Identifier: Unlicense\n/*\n * @title Solidity Bytes Arrays Utils\n * @author Gonçalo Sá \n *\n * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.\n * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.\n */\npragma solidity >=0.8.0 <0.9.0;\n\n\nlibrary BytesLib {\n function concat(\n bytes memory _preBytes,\n bytes memory _postBytes\n )\n internal\n pure\n returns (bytes memory)\n {\n bytes memory tempBytes;\n\n assembly {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // Store the length of the first bytes array at the beginning of\n // the memory for tempBytes.\n let length := mload(_preBytes)\n mstore(tempBytes, length)\n\n // Maintain a memory counter for the current write location in the\n // temp bytes array by adding the 32 bytes for the array length to\n // the starting location.\n let mc := add(tempBytes, 0x20)\n // Stop copying when the memory counter reaches the length of the\n // first bytes array.\n let end := add(mc, length)\n\n for {\n // Initialize a copy counter to the start of the _preBytes data,\n // 32 bytes into its memory.\n let cc := add(_preBytes, 0x20)\n } lt(mc, end) {\n // Increase both counters by 32 bytes each iteration.\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // Write the _preBytes data into the tempBytes memory 32 bytes\n // at a time.\n mstore(mc, mload(cc))\n }\n\n // Add the length of _postBytes to the current length of tempBytes\n // and store it as the new length in the first 32 bytes of the\n // tempBytes memory.\n length := mload(_postBytes)\n mstore(tempBytes, add(length, mload(tempBytes)))\n\n // Move the memory counter back from a multiple of 0x20 to the\n // actual end of the _preBytes data.\n mc := end\n // Stop copying when the memory counter reaches the new combined\n // length of the arrays.\n end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n // Update the free-memory pointer by padding our last write location\n // to 32 bytes: add 31 bytes to the end of tempBytes to move to the\n // next 32 byte block, then round down to the nearest multiple of\n // 32. If the sum of the length of the two arrays is zero then add\n // one before rounding down to leave a blank 32 bytes (the length block with 0).\n mstore(0x40, and(\n add(add(end, iszero(add(length, mload(_preBytes)))), 31),\n not(31) // Round down to the nearest 32 bytes.\n ))\n }\n\n return tempBytes;\n }\n\n function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {\n assembly {\n // Read the first 32 bytes of _preBytes storage, which is the length\n // of the array. (We don't need to use the offset into the slot\n // because arrays use the entire slot.)\n let fslot := sload(_preBytes.slot)\n // Arrays of 31 bytes or less have an even value in their slot,\n // while longer arrays have an odd value. The actual length is\n // the slot divided by two for odd values, and the lowest order\n // byte divided by two for even values.\n // If the slot is even, bitwise and the slot with 255 and divide by\n // two to get the length. If the slot is odd, bitwise and the slot\n // with -1 and divide by two.\n let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)\n let mlength := mload(_postBytes)\n let newlength := add(slength, mlength)\n // slength can contain both the length and contents of the array\n // if length < 32 bytes so let's prepare for that\n // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage\n switch add(lt(slength, 32), lt(newlength, 32))\n case 2 {\n // Since the new array still fits in the slot, we just need to\n // update the contents of the slot.\n // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length\n sstore(\n _preBytes.slot,\n // all the modifications to the slot are inside this\n // next block\n add(\n // we can just add to the slot contents because the\n // bytes we want to change are the LSBs\n fslot,\n add(\n mul(\n div(\n // load the bytes from memory\n mload(add(_postBytes, 0x20)),\n // zero all bytes to the right\n exp(0x100, sub(32, mlength))\n ),\n // and now shift left the number of bytes to\n // leave space for the length in the slot\n exp(0x100, sub(32, newlength))\n ),\n // increase length by the double of the memory\n // bytes length\n mul(mlength, 2)\n )\n )\n )\n }\n case 1 {\n // The stored value fits in the slot, but the combined value\n // will exceed it.\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n let sc := add(keccak256(0x0, 0x20), div(slength, 32))\n\n // save new length\n sstore(_preBytes.slot, add(mul(newlength, 2), 1))\n\n // The contents of the _postBytes array start 32 bytes into\n // the structure. Our first read should obtain the `submod`\n // bytes that can fit into the unused space in the last word\n // of the stored array. To get this, we read 32 bytes starting\n // from `submod`, so the data we read overlaps with the array\n // contents by `submod` bytes. Masking the lowest-order\n // `submod` bytes allows us to add that value directly to the\n // stored value.\n\n let submod := sub(32, slength)\n let mc := add(_postBytes, submod)\n let end := add(_postBytes, mlength)\n let mask := sub(exp(0x100, submod), 1)\n\n sstore(\n sc,\n add(\n and(\n fslot,\n 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00\n ),\n and(mload(mc), mask)\n )\n )\n\n for {\n mc := add(mc, 0x20)\n sc := add(sc, 1)\n } lt(mc, end) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n sstore(sc, mload(mc))\n }\n\n mask := exp(0x100, sub(mc, end))\n\n sstore(sc, mul(div(mload(mc), mask), mask))\n }\n default {\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n // Start copying to the last used word of the stored array.\n let sc := add(keccak256(0x0, 0x20), div(slength, 32))\n\n // save new length\n sstore(_preBytes.slot, add(mul(newlength, 2), 1))\n\n // Copy over the first `submod` bytes of the new data as in\n // case 1 above.\n let slengthmod := mod(slength, 32)\n let mlengthmod := mod(mlength, 32)\n let submod := sub(32, slengthmod)\n let mc := add(_postBytes, submod)\n let end := add(_postBytes, mlength)\n let mask := sub(exp(0x100, submod), 1)\n\n sstore(sc, add(sload(sc), and(mload(mc), mask)))\n\n for {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } lt(mc, end) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n sstore(sc, mload(mc))\n }\n\n mask := exp(0x100, sub(mc, end))\n\n sstore(sc, mul(div(mload(mc), mask), mask))\n }\n }\n }\n\n function slice(\n bytes memory _bytes,\n uint256 _start,\n uint256 _length\n )\n internal\n pure\n returns (bytes memory)\n {\n // We're using the unchecked block below because otherwise execution ends \n // with the native overflow error code.\n unchecked {\n require(_length + 31 >= _length, \"slice_overflow\");\n }\n require(_bytes.length >= _start + _length, \"slice_outOfBounds\");\n\n bytes memory tempBytes;\n\n assembly {\n switch iszero(_length)\n case 0 {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // The first word of the slice result is potentially a partial\n // word read from the original array. To read it, we calculate\n // the length of that partial word and start copying that many\n // bytes into the array. The first word we copy will start with\n // data we don't care about, but the last `lengthmod` bytes will\n // land at the beginning of the contents of the new array. When\n // we're done copying, we overwrite the full first word with\n // the actual length of the slice.\n let lengthmod := and(_length, 31)\n\n // The multiplication in the next line is necessary\n // because when slicing multiples of 32 bytes (lengthmod == 0)\n // the following copy loop was copying the origin's length\n // and then ending prematurely not copying everything it should.\n let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))\n let end := add(mc, _length)\n\n for {\n // The multiplication in the next line has the same exact purpose\n // as the one above.\n let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n mstore(tempBytes, _length)\n\n //update free-memory pointer\n //allocating the array padded to 32 bytes like the compiler does now\n mstore(0x40, and(add(mc, 31), not(31)))\n }\n //if we want a zero-length slice let's just return a zero-length array\n default {\n tempBytes := mload(0x40)\n //zero out the 32 bytes slice we are about to return\n //we need to do it because Solidity does not garbage collect\n mstore(tempBytes, 0)\n\n mstore(0x40, add(tempBytes, 0x20))\n }\n }\n\n return tempBytes;\n }\n\n function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {\n require(_bytes.length >= _start + 20, \"toAddress_outOfBounds\");\n address tempAddress;\n\n assembly {\n tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)\n }\n\n return tempAddress;\n }\n\n function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {\n require(_bytes.length >= _start + 1 , \"toUint8_outOfBounds\");\n uint8 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x1), _start))\n }\n\n return tempUint;\n }\n\n function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {\n require(_bytes.length >= _start + 2, \"toUint16_outOfBounds\");\n uint16 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x2), _start))\n }\n\n return tempUint;\n }\n\n function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {\n require(_bytes.length >= _start + 4, \"toUint32_outOfBounds\");\n uint32 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x4), _start))\n }\n\n return tempUint;\n }\n\n function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {\n require(_bytes.length >= _start + 8, \"toUint64_outOfBounds\");\n uint64 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x8), _start))\n }\n\n return tempUint;\n }\n\n function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {\n require(_bytes.length >= _start + 12, \"toUint96_outOfBounds\");\n uint96 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0xc), _start))\n }\n\n return tempUint;\n }\n\n function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {\n require(_bytes.length >= _start + 16, \"toUint128_outOfBounds\");\n uint128 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x10), _start))\n }\n\n return tempUint;\n }\n\n function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {\n require(_bytes.length >= _start + 32, \"toUint256_outOfBounds\");\n uint256 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x20), _start))\n }\n\n return tempUint;\n }\n\n function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {\n require(_bytes.length >= _start + 32, \"toBytes32_outOfBounds\");\n bytes32 tempBytes32;\n\n assembly {\n tempBytes32 := mload(add(add(_bytes, 0x20), _start))\n }\n\n return tempBytes32;\n }\n\n function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {\n bool success = true;\n\n assembly {\n let length := mload(_preBytes)\n\n // if lengths don't match the arrays are not equal\n switch eq(length, mload(_postBytes))\n case 1 {\n // cb is a circuit breaker in the for loop since there's\n // no said feature for inline assembly loops\n // cb = 1 - don't breaker\n // cb = 0 - break\n let cb := 1\n\n let mc := add(_preBytes, 0x20)\n let end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n // the next line is the loop condition:\n // while(uint256(mc < end) + cb == 2)\n } eq(add(lt(mc, end), cb), 2) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // if any of these checks fails then arrays are not equal\n if iszero(eq(mload(mc), mload(cc))) {\n // unsuccess:\n success := 0\n cb := 0\n }\n }\n }\n default {\n // unsuccess:\n success := 0\n }\n }\n\n return success;\n }\n\n function equalStorage(\n bytes storage _preBytes,\n bytes memory _postBytes\n )\n internal\n view\n returns (bool)\n {\n bool success = true;\n\n assembly {\n // we know _preBytes_offset is 0\n let fslot := sload(_preBytes.slot)\n // Decode the length of the stored array like in concatStorage().\n let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)\n let mlength := mload(_postBytes)\n\n // if lengths don't match the arrays are not equal\n switch eq(slength, mlength)\n case 1 {\n // slength can contain both the length and contents of the array\n // if length < 32 bytes so let's prepare for that\n // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage\n if iszero(iszero(slength)) {\n switch lt(slength, 32)\n case 1 {\n // blank the last byte which is the length\n fslot := mul(div(fslot, 0x100), 0x100)\n\n if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {\n // unsuccess:\n success := 0\n }\n }\n default {\n // cb is a circuit breaker in the for loop since there's\n // no said feature for inline assembly loops\n // cb = 1 - don't breaker\n // cb = 0 - break\n let cb := 1\n\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n let sc := keccak256(0x0, 0x20)\n\n let mc := add(_postBytes, 0x20)\n let end := add(mc, mlength)\n\n // the next line is the loop condition:\n // while(uint256(mc < end) + cb == 2)\n for {} eq(add(lt(mc, end), cb), 2) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n if iszero(eq(sload(sc), mload(mc))) {\n // unsuccess:\n success := 0\n cb := 0\n }\n }\n }\n }\n }\n default {\n // unsuccess:\n success := 0\n }\n }\n\n return success;\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/3061977d794410fdfe54a096f7dcc314.json b/contracts/deployments/hoodi/solcInputs/3061977d794410fdfe54a096f7dcc314.json new file mode 100644 index 0000000000..3da012a5fc --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/3061977d794410fdfe54a096f7dcc314.json @@ -0,0 +1,102 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to retrieve beacon block roots.\n * @author Origin Protocol Inc\n */\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the beacon block root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Roots contract to get the parent block root.\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to request full or partial withdrawals from validators on the beacon chain.\n * @author Origin Protocol Inc\n */\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n /// @notice Requests a partial withdrawal for a given validator public key and amount.\n /// @param validatorPubKey The public key of the validator to withdraw from\n /// @param amount The amount of ETH to withdraw\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n /// @notice Gets fee for withdrawal requests contract on Beacon chain\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof,\n bytes calldata validatorPubKeyProof\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint64 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes32 pubKeyHash,\n bytes calldata firstPendingDepositProof\n ) external view returns (bool isEmptyDepositQueue);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with\n /// `platformAddress` not used so empty address\n /// `vaultAddress` the address of the OETH Vault contract\n /// @param _wethAddress Address of the WETH Token contract\n /// @param _ssvToken Address of the SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconProofs,\n _beaconGenesisTimestamp\n )\n {\n SSV_TOKEN = _ssvToken;\n\n // Make sure nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// @param _rewardTokenAddresses Not used so empty array\n /// @param _assets Not used so empty array\n /// @param _pTokens Not used so empty array\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n safeApproveAllTokens();\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH that was transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// @param _recipient Address to receive withdrawn assets.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH to withdraw.\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(WETH, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Accounts for all the assets managed by this strategy which includes:\n /// 1. The current WETH in this strategy contract\n /// 2. The last verified ETH balance, total deposits and total validator balances\n /// @param _asset Address of WETH asset.\n /// @return balance Total value in ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by the strategy.\n /// @param _asset The address of the WETH token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for validator registration.\n function safeApproveAllTokens() public override {\n // Approves the SSV Network contract to transfer SSV tokens when validators are registered\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n * The new ETH will be accounted for in `checkBalance` after the next snapBalances and verifyBalances txs.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n /// @notice is not supported for this strategy as there is no platform token.\n function setPTokenAddress(address, address) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @notice is not supported for this strategy as there is no platform token.\n function removePToken(uint256) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @dev This strategy does not use a platform token like the old Aave and Compound strategies.\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instead of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract.\n /// Withdrawals from validators also accumulate as ETH in this strategy contract.\n /// It's too complex to separate the rewards from withdrawals so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can now regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingView.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/**\n * @title Viewing contract for the Compounding Staking Strategy.\n * @author Origin Protocol Inc\n */\ncontract CompoundingStakingStrategyView {\n /// @notice The address of the Compounding Staking Strategy contract\n CompoundingValidatorManager public immutable stakingStrategy;\n\n constructor(address _stakingStrategy) {\n stakingStrategy = CompoundingValidatorManager(_stakingStrategy);\n }\n\n struct ValidatorView {\n bytes32 pubKeyHash;\n uint64 index;\n CompoundingValidatorManager.ValidatorState state;\n }\n\n struct DepositView {\n uint256 depositID;\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint256 withdrawableEpoch;\n }\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return validators An array of `ValidatorView` containing the public key hash, validator index and state.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorView[] memory validators)\n {\n uint256 validatorCount = stakingStrategy.verifiedValidatorsLength();\n validators = new ValidatorView[](validatorCount);\n for (uint256 i = 0; i < validatorCount; ++i) {\n bytes32 pubKeyHash = stakingStrategy.verifiedValidators(i);\n (\n CompoundingValidatorManager.ValidatorState state,\n uint64 index\n ) = stakingStrategy.validator(pubKeyHash);\n validators[i] = ValidatorView({\n pubKeyHash: pubKeyHash,\n index: index,\n state: state\n });\n }\n }\n\n /// @notice Returns the deposits that are still to be verified.\n /// These may or may not have been processed by the beacon chain.\n /// @return pendingDeposits An array of `DepositView` containing the deposit ID, public key hash,\n /// amount in Gwei and the slot of the deposit.\n function getPendingDeposits()\n external\n view\n returns (DepositView[] memory pendingDeposits)\n {\n uint256 depositsCount = stakingStrategy.depositListLength();\n pendingDeposits = new DepositView[](depositsCount);\n for (uint256 i = 0; i < depositsCount; ++i) {\n (\n bytes32 pubKeyHash,\n uint64 amountGwei,\n uint64 slot,\n ,\n ,\n uint256 withdrawableEpoch\n ) = stakingStrategy.deposits(stakingStrategy.depositList(i));\n pendingDeposits[i] = DepositView({\n depositID: stakingStrategy.depositList(i),\n pubKeyHash: pubKeyHash,\n amountGwei: amountGwei,\n slot: slot,\n withdrawableEpoch: withdrawableEpoch\n });\n }\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable {\n using SafeERC20 for IERC20;\n\n /// @dev The amount of ETH in wei that is required for a deposit to a new validator.\n /// Initially this is 32 ETH, but will be reduced to 1 ETH after P2P's APIs have been updated\n /// to support deposits of 1 ETH.\n uint256 internal constant DEPOSIT_AMOUNT_WEI = 32 ether;\n /// @dev The maximum number of deposits that are waiting to be verified as processed on the beacon chain.\n uint256 internal constant MAX_DEPOSITS = 12;\n /// @dev The maximum number of validators that can be verified.\n uint256 internal constant MAX_VERIFIED_VALIDATORS = 48;\n /// @dev The default withdrawable epoch value on the Beacon chain.\n /// A value in the far future means the validator is not exiting.\n uint64 internal constant FAR_FUTURE_EPOCH = type(uint64).max;\n /// @dev The number of seconds between each beacon chain slot.\n uint64 internal constant SLOT_DURATION = 12;\n /// @dev The number of slots in each beacon chain epoch.\n uint64 internal constant SLOTS_PER_EPOCH = 32;\n /// @dev Minimum time in seconds to allow snapped balances to be verified.\n /// Set to 1 epoch as the pending deposits only changes every epoch.\n /// That's also enough time to generate the proofs and call `verifyBalances`.\n uint64 internal constant SNAP_BALANCES_DELAY =\n SLOTS_PER_EPOCH * SLOT_DURATION;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n /// @notice The timestamp of the Beacon chain genesis.\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\n uint64 public immutable BEACON_GENESIS_TIMESTAMP;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// Deposit data for new compounding validators.\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified and is ready to be staked\n }\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountWei Amount of ETH in wei that has been deposited to the beacon chain deposit contract\n /// @param blockNumber Block number when the deposit was made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either PENDING or VERIFIED\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint32 depositIndex;\n DepositStatus status;\n uint64 withdrawableEpoch;\n }\n /// @notice Restricts to only one deposit to an unverified validator at a time.\n /// This is to limit front-running attacks of deposits to the beacon chain contract.\n bool public firstDeposit;\n /// @notice Unique identifier of the next validator deposit.\n uint128 public nextDepositID;\n /// @notice Mapping of the deposit ID to the deposit data\n mapping(uint256 => DepositData) public deposits;\n /// @notice List of strategy deposit IDs to a validator.\n /// The list can be for deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n /// The list may not be ordered by time of deposit.\n /// Removed deposits will move the last deposit to the removed index.\n uint256[] public depositList;\n\n // Validator data\n enum ValidatorState {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITING, // The validator has been requested to exit or has been verified as forced exit\n EXITED, // The validator has been verified to have a zero balance\n REMOVED, // validator has funds withdrawn to the EigenPod and is removed from the SSV\n INVALID // The validator has been front-run and the withdrawal address is not this strategy\n }\n\n struct ValidatorData {\n ValidatorState state;\n uint64 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n bytes32[] public verifiedValidators;\n /// @notice Mapping of the hash of the validator's public key to the validator state and index.\n /// Uses the Beacon chain hashing for BLSPubkey which is sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => ValidatorData) public validator;\n\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n uint64 timestamp;\n uint128 ethBalance;\n }\n /// @notice Mapping of the block root to the balances at that slot\n mapping(bytes32 => Balances) public snappedBalances;\n /// @notice The timestamp of the last snapshot taken\n uint64 public lastSnapTimestamp;\n /// @notice The last verified ETH balance of the strategy\n uint128 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[50] private __gap;\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event FirstDepositReset();\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n uint256 indexed depositID,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint64 indexed validatorIndex\n );\n event ValidatorInvalid(bytes32 indexed pubKeyHash);\n event DepositVerified(uint256 indexed depositID, uint256 amountWei);\n event DepositToValidatorExiting(\n uint256 indexed depositID,\n uint256 amountWei,\n uint64 withdrawableEpoch\n );\n event DepositValidatorExited(uint256 indexed depositID, uint256 amountWei);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(bytes32 indexed blockRoot, uint256 ethBalance);\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 ethBalance\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_PROOFS = _beaconProofs;\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\n\n require(\n block.timestamp > _beaconGenesisTimestamp,\n \"Invalid genesis timestamp\"\n );\n }\n\n /**\n *\n * Admin Functions\n *\n */\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Reset the `firstDeposit` flag to false so deposits to unverified validators can be made again.\n function resetFirstDeposit() external onlyGovernor {\n require(firstDeposit, \"No first deposit\");\n\n firstDeposit = false;\n\n emit FirstDepositReset();\n }\n\n /**\n *\n * Validator Management\n *\n */\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validator[pubKeyHash].state == ValidatorState.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validator[pubKeyHash].state = ValidatorState.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n struct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n }\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validatorStakeData validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validatorStakeData,\n uint64 depositAmountGwei\n ) external onlyRegistrator {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(depositList.length < MAX_DEPOSITS, \"Max deposits\");\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validatorStakeData.pubkey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.VERIFIED),\n \"Not registered or verified\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n if (currentState == ValidatorState.REGISTERED) {\n // Can only have one pending deposit to an unverified validator at a time.\n // This is to limit front-running deposit attacks to a single deposit.\n // The exiting deposit needs to be verified before another deposit can be made.\n // If there was a front-running attack, the validator needs to be verified as invalid\n // and the Governor calls `resetFirstDeposit` to set `firstDeposit` to false.\n require(!firstDeposit, \"Existing first deposit\");\n // Limits the amount of ETH that can be at risk from a front-running deposit attack.\n require(\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n // Limits the number of validator balance proofs to verifyBalances\n require(\n verifiedValidators.length + 1 < MAX_VERIFIED_VALIDATORS,\n \"Max validators\"\n );\n\n // Flag a deposit to an unverified validator so only no other deposits can be made\n // to an unverified validator.\n firstDeposit = true;\n }\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validatorStakeData.pubkey,\n withdrawalCredentials,\n validatorStakeData.signature,\n validatorStakeData.depositDataRoot\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == ValidatorState.REGISTERED) {\n validator[pubKeyHash].state = ValidatorState.STAKED;\n }\n\n /// After the Pectra upgrade the validators have a new restriction when proposing\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\n /// forward. Each slot is created at strict 12 second intervals and those slots can\n /// either have blocks attached to them or not. This way using the block.timestamp\n /// the slot number can easily be calculated.\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\n BEACON_GENESIS_TIMESTAMP) / SLOT_DURATION;\n\n // Store the deposit data for verifyDeposit and verifyBalances\n uint256 depositID = nextDepositID++;\n deposits[depositID] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n slot: depositSlot,\n depositIndex: SafeCast.toUint32(depositList.length),\n status: DepositStatus.PENDING,\n withdrawableEpoch: FAR_FUTURE_EPOCH\n });\n depositList.push(depositID);\n\n emit ETHStaked(\n pubKeyHash,\n depositID,\n validatorStakeData.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then only the amount in excess of 32 ETH will be withdrawn.\n /// Only the Registrator can call this function.\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\n /// If no ETH balance, the tx will revert.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n payable\n onlyRegistrator\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n require(\n currentState == ValidatorState.VERIFIED,\n \"Validator not verified\"\n );\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n // If a full withdrawal (validator exit)\n if (amountGwei == 0) {\n // Store the validator state as exiting so no more deposits can be made to it.\n validator[pubKeyHash].state = ValidatorState.EXITING;\n }\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove the validator from the SSV Cluster after:\n /// - the validator has been exited from `validatorWithdrawal` or slashed\n /// - the validator has incorrectly registered and can not be staked to\n /// - the initial deposit was front-run and the withdrawal address is not this strategy's address.\n /// Make sure `validatorWithdrawal` is called with a zero amount and the validator has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.EXITED ||\n currentState == ValidatorState.INVALID,\n \"Validator not regd or exited\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validator[pubKeyHash].state = ValidatorState.REMOVED;\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /**\n *\n * SSV Management\n *\n */\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be withdrawn from the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /**\n *\n * Beacon Chain Proofs\n *\n */\n\n /// @notice Verifies a validator's index to its public key.\n /// Adds to the list of verified validators if the validator's withdrawal address is this strategy's address.\n /// Marks the validator as invalid and removes the deposit if the withdrawal address is not this strategy's address.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param withdrawalAddress The withdrawal address of the validator which should be this strategy's address.\n /// If the withdrawal address is not this strategy's address, the initial deposit was front-run\n /// and the validator is marked as invalid.\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n address withdrawalAddress,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validator[pubKeyHash].state == ValidatorState.STAKED,\n \"Validator not staked\"\n );\n\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key.\n // Also verify the validator's withdrawal credential points to the `withdrawalAddress`.\n IBeaconProofs(BEACON_PROOFS).verifyValidator(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n withdrawalAddress\n );\n\n // If the initial deposit was front-run and the withdrawal address is not this strategy\n if (withdrawalAddress != address(this)) {\n validator[pubKeyHash] = ValidatorData({\n state: ValidatorState.INVALID,\n index: validatorIndex\n });\n\n // Find and remove the deposit as the funds can not be recovered\n uint256 depositCount = depositList.length;\n for (uint256 i = 0; i < depositCount; i++) {\n DepositData memory deposit = deposits[depositList[i]];\n if (deposit.pubKeyHash == pubKeyHash) {\n _removeDeposit(depositList[i], deposit);\n break;\n }\n }\n\n // Leave the `firstDeposit` flag as true so no more deposits to unverified validators can be made.\n // The Governor has to reset the `firstDeposit` to false before another deposit to\n // an unverified validator can be made.\n // The Governor can set a new `validatorRegistrator` if they suspect it has been compromised.\n\n emit ValidatorInvalid(pubKeyHash);\n return;\n }\n\n // Store the validator state as verified\n validator[pubKeyHash] = ValidatorData({\n state: ValidatorState.VERIFIED,\n index: validatorIndex\n });\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(pubKeyHash);\n\n // Reset the firstDeposit flag as the first deposit to an unverified validator has been verified.\n firstDeposit = false;\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n struct FirstPendingDepositProofData {\n uint64 slot;\n uint64 validatorIndex;\n bytes32 pubKeyHash;\n bytes pendingDepositPubKeyProof;\n bytes withdrawableEpochProof;\n bytes validatorPubKeyProof;\n }\n\n struct DepositValidatorProofData {\n uint64 withdrawableEpoch;\n bytes withdrawableEpochProof;\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n ///\n /// Important: this function has a limitation where the `verificationSlot` that is passed by the off-chain\n /// verifier requires a slot immediately after it to propose a block otherwise the `BeaconRoots.parentBlockRoot`\n /// will fail. This shouldn't be a problem, since by the current behaviour of beacon chain only 1%-3% slots\n /// don't propose a block.\n /// @param depositID The deposit ID emitted in `ETHStaked` from the `stakeEth` function.\n /// @param depositProcessedSlot Any slot on or after the strategy's deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\n /// set for the next block timestamp in 12 seconds time.\n /// @param firstDepositValidatorCreatedSlot The slot on or after when the validator of the first pending deposit\n /// was created on the beacon chain. This is used to verify the validator has not exited.\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n uint256 depositID,\n uint64 depositProcessedSlot,\n uint64 firstDepositValidatorCreatedSlot,\n FirstPendingDepositProofData calldata firstPendingDeposit,\n DepositValidatorProofData calldata strategyValidatorData\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositID];\n ValidatorData memory strategyValidator = validator[deposit.pubKeyHash];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n strategyValidator.state == ValidatorState.VERIFIED,\n \"Validator not verified\"\n );\n // The verification slot must be after the deposit's slot.\n // This is needed for when the deposit queue is empty.\n require(deposit.slot < depositProcessedSlot, \"Slot not after deposit\");\n require(\n depositProcessedSlot <= firstDepositValidatorCreatedSlot,\n \"Invalid verification slots\"\n );\n\n // Get the parent beacon block root of the next block which is the block root of the deposit verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 depositBlockRoot = BeaconRoots.parentBlockRoot(\n _calcNextBlockTimestamp(depositProcessedSlot)\n );\n\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n depositBlockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.pubKeyHash,\n firstPendingDeposit.pendingDepositPubKeyProof\n );\n\n // If the deposit queue is not empty\n if (!isDepositQueueEmpty) {\n // Get the parent beacon block root of the next block which is\n // the block root of the validator verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 validatorBlockRoot = BeaconRoots.parentBlockRoot(\n _calcNextBlockTimestamp(firstDepositValidatorCreatedSlot)\n );\n\n // Verify the validator of the first pending deposit is not exiting.\n // If it is exiting we can't be sure this deposit has not been postponed in the deposit queue.\n // Hence we can not verify if the strategy's deposit has been processed or not.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n validatorBlockRoot,\n firstPendingDeposit.validatorIndex,\n firstPendingDeposit.pubKeyHash,\n FAR_FUTURE_EPOCH,\n firstPendingDeposit.withdrawableEpochProof,\n firstPendingDeposit.validatorPubKeyProof\n );\n }\n\n // Verify the withdrawableEpoch on the validator of the strategy's deposit\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n depositBlockRoot,\n strategyValidator.index,\n strategyValidatorData.withdrawableEpoch,\n strategyValidatorData.withdrawableEpochProof\n );\n\n // If the validator is exiting because it has been slashed\n if (strategyValidatorData.withdrawableEpoch != FAR_FUTURE_EPOCH) {\n // Store the exit epoch in the deposit data\n deposit.withdrawableEpoch = strategyValidatorData.withdrawableEpoch;\n\n emit DepositToValidatorExiting(\n depositID,\n uint256(deposit.amountGwei) * 1 gwei,\n strategyValidatorData.withdrawableEpoch\n );\n\n validator[deposit.pubKeyHash].state = ValidatorState.EXITING;\n\n // Leave the deposit status as PENDING\n return;\n }\n\n // solhint-disable max-line-length\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the deposit queue is empty then our deposit must have been processed on the beacon chain.\n // The deposit slot can be zero for validators consolidating to a compounding validator or 0x01 validator\n // being promoted to a compounding one. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // We can not guarantee that the deposit has been processed in that case.\n // solhint-enable max-line-length\n require(\n deposit.slot < firstPendingDeposit.slot || isDepositQueueEmpty,\n \"Deposit likely not processed\"\n );\n\n // Remove the deposit now it has been verified as processed on the beacon chain.\n _removeDeposit(depositID, deposit);\n\n emit DepositVerified(depositID, uint256(deposit.amountGwei) * 1 gwei);\n }\n\n function _removeDeposit(uint256 depositID, DepositData memory deposit)\n internal\n {\n // After verifying the proof, update the contract storage\n deposits[depositID].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n uint256 lastDeposit = depositList[depositList.length - 1];\n depositList[deposit.depositIndex] = lastDeposit;\n deposits[lastDeposit].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositList.pop();\n }\n\n /// @dev Calculates the timestamp of the next execution block from the given slot.\n /// @param slot The beacon chain slot number used for merkle proof verification.\n function _calcNextBlockTimestamp(uint64 slot)\n internal\n view\n returns (uint64)\n {\n // Calculate the next block timestamp from the slot.\n return SLOT_DURATION * slot + BEACON_GENESIS_TIMESTAMP + SLOT_DURATION;\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block and beacon block root\n /// of the slot that is associated with the previous block.\n ///\n /// When snapping / verifying balance it is of a high importance that there is no\n /// miss-match in respect to ETH that is held by the contract and balances that are\n /// verified on the validators.\n ///\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\n /// constructing a block on the beacon chain consist of:\n /// - process_withdrawals: ETH is deducted from the validator's balance\n /// - process_execution_payload: immediately after the previous step executing all the\n /// transactions\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\n /// contained in the withdrawal credentials of the exited validators\n ///\n /// That means that balance increases which are part of the post-block execution state are\n /// done within the block, but the transaction that are contained within that block can not\n /// see / interact with the balance from the exited validators. Only transactions in the\n /// next block can do that.\n ///\n /// When snap balances is performed the state of the chain is snapped across 2 separate\n /// chain states:\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\n /// will be referred to as Y - 1 as it makes no difference in the argument\n ///\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\n /// miss-counting ETH or much more dangerous double counting of the ETH.\n ///\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\n /// - ETH balance in the contract on block X\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\n /// - ETH balance in validators that are active in slot Y - 1\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\n /// and have their balance visible to transactions in slot Y and corresponding block X\n /// (or sooner)\n ///\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\n ///\n /// *ETH balance in the contract on block X*\n ///\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\n ///\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\n /// would result in double counting the `stakedEth` since it would be present once in the\n /// snapped contract balance and the second time in deposit storage variables.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\n ///\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\n /// contract at block Z. The execution layer doesn't have direct access to the state of\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\n /// and could also be part of the validator balances. It does that by verifying that at\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\n /// the last snap till now all are still in queue. Which ensures they can not be part of\n /// the validator balances in later steps.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in validators that are active in slot Y - 1*\n ///\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\n /// correctness.\n ///\n /// This behaviour is correct.\n ///\n /// *The withdrawn validators*\n ///\n /// The withdrawn validators could have their balances deducted in any slot before slot\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\n /// look at the \"worst case scenario\" where the validator withdrawal is processed in the\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\n /// even if the validator exits at the latest possible time it is paramount that the ETH\n /// balance on the execution layer is recorded in the next block. Correctly accounting\n /// for the withdrawn ETH.\n ///\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\n /// validator balance verification as well as execution layer contract balance snap.\n ///\n /// This behaviour is correct.\n ///\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n function snapBalances() external {\n uint64 currentTimestamp = SafeCast.toUint64(block.timestamp);\n require(\n lastSnapTimestamp + SNAP_BALANCES_DELAY < currentTimestamp,\n \"Snap too soon\"\n );\n\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(currentTimestamp);\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the balances in the mapping\n snappedBalances[blockRoot] = Balances({\n timestamp: currentTimestamp,\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n // Store the snapped timestamp\n lastSnapTimestamp = currentTimestamp;\n\n emit BalancesSnapped(blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct BalanceProofs {\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n /// @param snapBlockRoot The beacon block root emitted from `snapBalance` in `BalancesSnapped`.\n /// @param balanceProofs a `BalanceProofs` struct containing the following:\n /// balancesContainerRoot - the merkle root of the balances container\n /// balancesContainerProof - The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// validatorBalanceLeaves - Array of leaf nodes containing the validator balance with three other balances.\n /// validatorBalanceProofs - Array of merkle proofs for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(\n bytes32 snapBlockRoot,\n uint64 validatorVerificationBlockTimestamp,\n FirstPendingDepositProofData calldata firstPendingDeposit,\n BalanceProofs calldata balanceProofs\n ) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalances[snapBlockRoot];\n // Check the balances are the latest\n require(lastSnapTimestamp > 0, \"No snapped balances\");\n require(balancesMem.timestamp == lastSnapTimestamp, \"Stale snap\");\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n balanceProofs.validatorBalanceProofs.length ==\n verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n balanceProofs.validatorBalanceLeaves.length ==\n verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n snapBlockRoot,\n balanceProofs.balancesContainerRoot,\n balanceProofs.balancesContainerProof\n );\n\n // for each validator in reserve order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n balanceProofs.balancesContainerRoot,\n balanceProofs.validatorBalanceLeaves[i],\n balanceProofs.validatorBalanceProofs[i],\n validator[verifiedValidators[i]].index\n );\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n // This could have been in VERIFIED or EXITING state\n validator[verifiedValidators[i]].state = ValidatorState\n .EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += validatorBalanceGwei * 1 gwei;\n }\n }\n\n uint256 depositsCount = depositList.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification.\n // This section is after the validator balance verifications so an exited validator will be marked\n // as EXITED before the deposits are verified. If there was a deposit to an exited validator\n // then the deposit can only be removed once the validator is fully exited.\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n snapBlockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.pubKeyHash,\n firstPendingDeposit.pendingDepositPubKeyProof\n );\n\n // If there are no deposits in the beacon chain queue then our deposits must have been processed.\n // If the deposits have been processed, each deposit will need to be verified with `verifyDeposit`\n require(!isDepositQueueEmpty, \"Deposits have been processed\");\n\n // The verification of the validator the first pending deposit is for must be on or after when\n // `snapBalances` was called.\n require(\n balancesMem.timestamp <= validatorVerificationBlockTimestamp,\n \"Invalid validator timestamp\"\n );\n\n // Verify the validator of the first pending deposit is not exiting by checking\n // the withdrawable epoch is far into the future.\n // If it is exiting we can't be sure this deposit has not been postponed in the deposit queue.\n // Hence we can not verify if the strategy's deposit has been processed or not.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n // Get the parent beacon block root of the next block which is\n // the block root of the validator verification slot.\n // This will revert if the slot after the verification slot was missed.\n BeaconRoots.parentBlockRoot(\n validatorVerificationBlockTimestamp\n ),\n firstPendingDeposit.validatorIndex,\n firstPendingDeposit.pubKeyHash,\n // Validator is not exiting\n FAR_FUTURE_EPOCH,\n firstPendingDeposit.withdrawableEpochProof,\n firstPendingDeposit.validatorPubKeyProof\n );\n\n // solhint-disable max-line-length\n // If a validator is converted from a sweeping validator to a compounding validator, any balance in excess\n // of the min 32 ETH is put in the pending deposit queue. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // This will have a slot value of zero unfortunately.\n // We can not prove the strategy's deposits are still pending with a zero slot value so revert the tx.\n // Another snapBalances will need to be taken that does not have consolidation deposits at the front of the\n // beacon chain deposit queue.\n // solhint-enable max-line-length\n require(\n firstPendingDeposit.slot > 0,\n \"Invalid first pending deposit\"\n );\n\n // Calculate the epoch at the time of the snapBalances\n uint64 verificationEpoch = (SafeCast.toUint64(\n balancesMem.timestamp\n ) - BEACON_GENESIS_TIMESTAMP) / (SLOT_DURATION * SLOTS_PER_EPOCH);\n\n // For each staking strategy's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n uint256 depositID = depositList[i];\n DepositData memory depositData = deposits[depositID];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit slot is before the slot of the staking strategy's deposit.\n // If the deposit has been processed, it will need to be verified with `verifyDeposit`.\n // OR the deposit is to an exiting validator so check it is still not withdrawable.\n // If the validator is not withdrawable, then the deposit can not have been processed yet.\n // If the validator is now withdrawable, then the deposit may have been processed. The strategy\n // now has to wait until the validator's balance is verified to be zero.\n // OR the validator has exited and the deposit is now verified as processed.\n require(\n firstPendingDeposit.slot < depositData.slot ||\n (verificationEpoch < depositData.withdrawableEpoch &&\n depositData.withdrawableEpoch !=\n FAR_FUTURE_EPOCH) ||\n validator[depositData.pubKeyHash].state ==\n ValidatorState.EXITED,\n \"Deposit likely processed\"\n );\n\n // Remove the deposit if the validator has exited.\n if (\n validator[depositData.pubKeyHash].state ==\n ValidatorState.EXITED\n ) {\n _removeDeposit(depositID, depositData);\n\n emit DepositValidatorExited(\n depositID,\n uint256(depositData.amountGwei) * 1 gwei\n );\n\n // Skip to the next deposit as the deposit amount is now in the strategy's ETH balance\n continue;\n }\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei += uint256(depositData.amountGwei) * 1 gwei;\n }\n }\n\n // Store the verified balance in storage\n lastVerifiedEthBalance = SafeCast.toUint128(\n totalDepositsWei + totalValidatorBalance + balancesMem.ethBalance\n );\n // Reset the last snap timestamp so a new snapBalances has to be made\n lastSnapTimestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n require(pubKey.length == 48, \"Invalid public key length\");\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /**\n *\n * WETH and ETH Accounting\n *\n */\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n // The min is required as more WETH can be withdrawn than deposited\n // as the strategy earns consensus and execution rewards.\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance.\n // The ETH balance in this strategy contract can be more than the last verified ETH balance\n // due to partial withdrawals or full exits being processed by the beacon chain since the last snapBalances.\n // It can also happen from execution rewards (MEV) or ETH donations.\n lastVerifiedEthBalance -= SafeCast.toUint128(\n Math.min(uint256(lastVerifiedEthBalance), _ethAmount)\n );\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += SafeCast.toUint128(_wethAmount);\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /**\n *\n * View Functions\n *\n */\n\n /// @notice Returns the number of deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n function depositListLength() external view returns (uint256) {\n return depositList.length;\n }\n\n /// @notice Returns the number of verified validators.\n function verifiedValidatorsLength() external view returns (uint256) {\n return verifiedValidators.length;\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/3152748504b6a5234f27bb6386288929.json b/contracts/deployments/hoodi/solcInputs/3152748504b6a5234f27bb6386288929.json new file mode 100644 index 0000000000..85285115a7 --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/3152748504b6a5234f27bb6386288929.json @@ -0,0 +1,54 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/proxies/BaseProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice BridgedBaseWOETHProxy delegates calls to BridgedWOETH implementation\n */\ncontract BridgedBaseWOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseVaultProxy delegates calls to OETHBaseVault implementation\n */\ncontract OETHBaseVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseProxy delegates calls to OETH implementation\n */\ncontract OETHBaseProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHBaseProxy delegates calls to WOETH implementation\n */\ncontract WOETHBaseProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseDripperProxy delegates calls to a FixedRateDripper implementation\n */\ncontract OETHBaseDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice AerodromeAMOStrategyProxy delegates calls to AerodromeAMOStrategy implementation\n */\ncontract AerodromeAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BridgedWOETHStrategyProxy delegates calls to BridgedWOETHStrategy implementation\n */\ncontract BridgedWOETHStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseHarvesterProxy delegates calls to a SuperOETHHarvester implementation\n */\ncontract OETHBaseHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseCurveAMOProxy delegates calls to a OETHBaseCurveAMO implementation\n */\ncontract OETHBaseCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/proxies/InitializeGovernedUpgradeabilityProxy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { Governable } from \"../governance/Governable.sol\";\n\n/**\n * @title BaseGovernedUpgradeabilityProxy\n * @dev This contract combines an upgradeability proxy with our governor system.\n * It is based on an older version of OpenZeppelins BaseUpgradeabilityProxy\n * with Solidity ^0.8.0.\n * @author Origin Protocol Inc\n */\ncontract InitializeGovernedUpgradeabilityProxy is Governable {\n /**\n * @dev Emitted when the implementation is upgraded.\n * @param implementation Address of the new implementation.\n */\n event Upgraded(address indexed implementation);\n\n constructor() {\n _setGovernor(msg.sender);\n }\n\n /**\n * @dev Contract initializer with Governor enforcement\n * @param _logic Address of the initial implementation.\n * @param _initGovernor Address of the initial Governor.\n * @param _data Data to send as msg.data to the implementation to initialize\n * the proxied contract.\n * It should include the signature and the parameters of the function to be\n * called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n * This parameter is optional, if no data is given the initialization call\n * to proxied contract will be skipped.\n */\n function initialize(\n address _logic,\n address _initGovernor,\n bytes calldata _data\n ) public payable onlyGovernor {\n require(_implementation() == address(0));\n require(_logic != address(0), \"Implementation not set\");\n assert(\n IMPLEMENTATION_SLOT ==\n bytes32(uint256(keccak256(\"eip1967.proxy.implementation\")) - 1)\n );\n _setImplementation(_logic);\n if (_data.length > 0) {\n (bool success, ) = _logic.delegatecall(_data);\n require(success);\n }\n _changeGovernor(_initGovernor);\n }\n\n /**\n * @return The address of the proxy admin/it's also the governor.\n */\n function admin() external view returns (address) {\n return _governor();\n }\n\n /**\n * @return The address of the implementation.\n */\n function implementation() external view returns (address) {\n return _implementation();\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy.\n * Only the admin can call this function.\n * @param _newImplementation Address of the new implementation.\n */\n function upgradeTo(address _newImplementation) external onlyGovernor {\n _upgradeTo(_newImplementation);\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy and call a function\n * on the new implementation.\n * This is useful to initialize the proxied contract.\n * @param newImplementation Address of the new implementation.\n * @param data Data to send as msg.data in the low level call.\n * It should include the signature and the parameters of the function to be called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n */\n function upgradeToAndCall(address newImplementation, bytes calldata data)\n external\n payable\n onlyGovernor\n {\n _upgradeTo(newImplementation);\n (bool success, ) = newImplementation.delegatecall(data);\n require(success);\n }\n\n /**\n * @dev Fallback function.\n * Implemented entirely in `_fallback`.\n */\n fallback() external payable {\n _fallback();\n }\n\n /**\n * @dev Delegates execution to an implementation contract.\n * This is a low level function that doesn't return to its internal call site.\n * It will return to the external caller whatever the implementation returns.\n * @param _impl Address to delegate.\n */\n function _delegate(address _impl) internal {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(gas(), _impl, 0, calldatasize(), 0, 0)\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n /**\n * @dev Function that is run as the first thing in the fallback function.\n * Can be redefined in derived contracts to add functionality.\n * Redefinitions must call super._willFallback().\n */\n function _willFallback() internal {}\n\n /**\n * @dev fallback implementation.\n * Extracted to enable manual triggering.\n */\n function _fallback() internal {\n _willFallback();\n _delegate(_implementation());\n }\n\n /**\n * @dev Storage slot with the address of the current implementation.\n * This is the keccak-256 hash of \"eip1967.proxy.implementation\" subtracted by 1, and is\n * validated in the constructor.\n */\n bytes32 internal constant IMPLEMENTATION_SLOT =\n 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n /**\n * @dev Returns the current implementation.\n * @return impl Address of the current implementation\n */\n function _implementation() internal view returns (address impl) {\n bytes32 slot = IMPLEMENTATION_SLOT;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n impl := sload(slot)\n }\n }\n\n /**\n * @dev Upgrades the proxy to a new implementation.\n * @param newImplementation Address of the new implementation.\n */\n function _upgradeTo(address newImplementation) internal {\n _setImplementation(newImplementation);\n emit Upgraded(newImplementation);\n }\n\n /**\n * @dev Sets the implementation address of the proxy.\n * @param newImplementation Address of the new implementation.\n */\n function _setImplementation(address newImplementation) internal {\n require(\n Address.isContract(newImplementation),\n \"Cannot set a proxy implementation to a non-contract address\"\n );\n\n bytes32 slot = IMPLEMENTATION_SLOT;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(slot, newImplementation)\n }\n }\n}\n" + }, + "contracts/proxies/PlumeProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OETHPlumeVaultProxy delegates calls to OETHPlumeVault implementation\n */\ncontract OETHPlumeVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHPlumeProxy delegates calls to OETH implementation\n */\ncontract OETHPlumeProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHPlumeProxy delegates calls to WOETH implementation\n */\ncontract WOETHPlumeProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice RoosterAMOStrategyProxy delegates calls to a RoosterAMOStrategy implementation\n */\ncontract RoosterAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/proxies/Proxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OUSDProxy delegates calls to an OUSD implementation\n */\ncontract OUSDProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WrappedOUSDProxy delegates calls to a WrappedOUSD implementation\n */\ncontract WrappedOUSDProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice VaultProxy delegates calls to a Vault implementation\n */\ncontract VaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CompoundStrategyProxy delegates calls to a CompoundStrategy implementation\n */\ncontract CompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice AaveStrategyProxy delegates calls to a AaveStrategy implementation\n */\ncontract AaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ConvexStrategyProxy delegates calls to a ConvexStrategy implementation\n */\ncontract ConvexStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice HarvesterProxy delegates calls to a Harvester implementation\n */\ncontract HarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice DripperProxy delegates calls to a Dripper implementation\n */\ncontract DripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoCompoundStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\n */\ncontract MorphoCompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ConvexOUSDMetaStrategyProxy delegates calls to a ConvexOUSDMetaStrategy implementation\n */\ncontract ConvexOUSDMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoAaveStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\n */\ncontract MorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHProxy delegates calls to nowhere for now\n */\ncontract OETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHProxy delegates calls to nowhere for now\n */\ncontract WOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHVaultProxy delegates calls to a Vault implementation\n */\ncontract OETHVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHDripperProxy delegates calls to a OETHDripper implementation\n */\ncontract OETHDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHHarvesterProxy delegates calls to a Harvester implementation\n */\ncontract OETHHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CurveEthStrategyProxy delegates calls to a CurveEthStrategy implementation\n */\ncontract ConvexEthMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BuybackProxy delegates calls to Buyback implementation\n */\ncontract BuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHMorphoAaveStrategyProxy delegates calls to a MorphoAaveStrategy implementation\n */\ncontract OETHMorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBalancerMetaPoolrEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\n */\ncontract OETHBalancerMetaPoolrEthStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice OETHBalancerMetaPoolwstEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\n */\ncontract OETHBalancerMetaPoolwstEthStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MakerDsrStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MakerDsrStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBuybackProxy delegates calls to Buyback implementation\n */\ncontract OETHBuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BridgedWOETHProxy delegates calls to BridgedWOETH implementation\n */\ncontract BridgedWOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice NativeStakingSSVStrategyProxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulatorProxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulatorProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingSSVStrategy2Proxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategy2Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulator2Proxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulator2Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingSSVStrategy3Proxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategy3Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulator3Proxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulator3Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MetaMorphoStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MetaMorphoStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ARMBuybackProxy delegates calls to Buyback implementation\n */\ncontract ARMBuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoGauntletPrimeUSDCStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MorphoGauntletPrimeUSDCStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MorphoGauntletPrimeUSDTStrategyProxy delegates calls to a Generalized4626USDTStrategy implementation\n */\ncontract MorphoGauntletPrimeUSDTStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice CurvePoolBoosterProxy delegates calls to a CurvePoolBooster implementation\n */\ncontract CurvePoolBoosterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHFixedRateDripperProxy delegates calls to a OETHFixedRateDripper implementation\n */\ncontract OETHFixedRateDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHSimpleHarvesterProxy delegates calls to a OETHSimpleHarvester implementation\n */\ncontract OETHSimpleHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice PoolBoostCentralRegistryProxy delegates calls to the PoolBoostCentralRegistry implementation\n */\ncontract PoolBoostCentralRegistryProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MakerSSRStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MakerSSRStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OUSDCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\n */\ncontract OUSDCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\n */\ncontract OETHCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CompoundingStakingSSVStrategyProxy delegates calls to a CompoundingStakingSSVStrategy implementation\n */\ncontract CompoundingStakingSSVStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n" + }, + "contracts/proxies/SonicProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OSonicVaultProxy delegates calls to OSonicVault implementation\n */\ncontract OSonicVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicProxy delegates calls to OSonic implementation\n */\ncontract OSonicProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOSonicProxy delegates calls to WOSonic implementation\n */\ncontract WOSonicProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicDripperProxy delegates calls to a FixedRateDripper implementation\n */\ncontract OSonicDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice SonicStakingStrategyProxy delegates calls to SonicStakingStrategy implementation\n */\ncontract SonicStakingStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicHarvesterProxy delegates calls to a OSonicHarvester implementation\n */\ncontract OSonicHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice SonicSwapXAMOStrategyProxy delegates calls to a SonicSwapXAMOStrategy implementation\n */\ncontract SonicSwapXAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/3520797b51e53045544e09b50bba6d79.json b/contracts/deployments/hoodi/solcInputs/3520797b51e53045544e09b50bba6d79.json new file mode 100644 index 0000000000..aaa4ac610c --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/3520797b51e53045544e09b50bba6d79.json @@ -0,0 +1,123 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/security/Pausable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice The length of the beacon block root ring buffer\n uint256 internal constant BEACON_ROOTS_HISTORY_BUFFER_LENGTH = 8191;\n\n /// @notice Returns the Beacon Block Root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Commented out the following checks as it makes unit and fork testing very difficult.\n // require(block.timestamp >= timestamp, \"Timestamp in future\");\n // require(\n // block.timestamp - timestamp <\n // BEACON_ROOTS_HISTORY_BUFFER_LENGTH * 12,\n // \"Timestamp too old\"\n // );\n\n // Call the Beacon Block Root Oracle to get the parent block root\n // This does not have a function signature, so we use a staticcall\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconOracle {\n function blockToSlot(uint64 blockNumber) external returns (uint64 slot);\n\n function slotToBlock(uint64 slot) external returns (uint64 blockNumber);\n\n function slotToRoot(uint64 slot) external returns (bytes32 blockRoot);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n enum BalanceProofLevel {\n Container,\n BeaconBlock\n }\n\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 root,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex,\n BalanceProofLevel level\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view;\n\n function verifyBlockNumber(\n bytes32 beaconBlockRoot,\n uint256 blockNumber,\n bytes calldata blockNumberProof\n ) external view;\n\n function verifySlot(\n bytes32 beaconBlockRoot,\n uint256 slot,\n bytes calldata slotProof\n ) external view;\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n external\n pure\n returns (uint256);\n\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) external pure returns (uint256);\n}\n" + }, + "contracts/interfaces/IConsolidation.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IConsolidationSource {\n function confirmConsolidation()\n external\n returns (uint256 consolidationCount);\n}\n\ninterface IConsolidationTarget {\n function requestConsolidation(\n bytes32 lastSourcePubKeyHash,\n bytes32 targetPubKeyHash\n ) external;\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/mocks/beacon/ExecutionLayerConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { GeneralPurposeToConsensusLayerRequest } from \"./GeneralPurposeToConsensusLayerRequest.sol\";\n\ncontract ExecutionLayerConsolidation is GeneralPurposeToConsensusLayerRequest {\n event ConsolidationRequestIssued(bytes sourceKey, bytes targetKey);\n bytes public lastSource;\n bytes public lastTarget;\n\n function handleRequest(bytes calldata data) internal override {\n // parameters should consist of twice the 48 bytes for 2 public keys\n require(data.length == 96, \"Invalid Consolidation data\");\n lastSource = data[:48];\n lastTarget = data[48:];\n\n emit ConsolidationRequestIssued(lastSource, lastTarget);\n }\n}\n" + }, + "contracts/mocks/beacon/ExecutionLayerWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { GeneralPurposeToConsensusLayerRequest } from \"./GeneralPurposeToConsensusLayerRequest.sol\";\n\ncontract ExecutionLayerWithdrawal is GeneralPurposeToConsensusLayerRequest {\n event WithdrawalRequestIssued(bytes publicKey, uint64 amount);\n\n bytes public lastPublicKey;\n uint64 public lastAmount;\n\n function handleRequest(bytes calldata data) internal override {\n // parameters should consist of 48 bytes for public key and 8 bytes for uint64 amount\n require(data.length == 56, \"Invalid Withdrawal data\");\n lastPublicKey = data[:48];\n lastAmount = uint64(bytes8(data[48:]));\n emit WithdrawalRequestIssued(lastPublicKey, lastAmount);\n }\n}\n" + }, + "contracts/mocks/beacon/GeneralPurposeToConsensusLayerRequest.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nabstract contract GeneralPurposeToConsensusLayerRequest {\n // solhint-disable no-complex-fallback\n fallback() external payable {\n // fee requested\n if (msg.data.length == 0) {\n uint256 fee = _fee();\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Return a uint256 value\n mstore(0, fee)\n return(0, 32) // Return 32 bytes from memory\n }\n }\n\n // else handle request\n handleRequest(msg.data);\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _fee() internal virtual returns (uint256) {\n return 1;\n }\n\n function handleRequest(bytes calldata data) internal virtual;\n}\n" + }, + "contracts/mocks/MockPartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PartialWithdrawal } from \"../beacon/PartialWithdrawal.sol\";\n\ncontract MockPartialWithdrawal {\n function fee() external view returns (uint256) {\n return PartialWithdrawal.fee();\n }\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n external\n returns (uint256 fee_)\n {\n return PartialWithdrawal.request(validatorPubKey, amount);\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconOracle Address of the Beacon Oracle contract that maps block numbers to slots\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconOracle,\n address _beaconProofs\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconOracle,\n _beaconProofs\n )\n {\n SSV_TOKEN = _ssvToken;\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract.\n /// @param _rewardTokenAddresses Address of reward token for platform\n /// @param _assets Addresses of initial supported assets\n /// @param _pTokens Platform Token corresponding addresses\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused for validator consolidation.\n /// @param _asset Address of asset to deposit. Has to be WETH.\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused for validator consolidation.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// Will revert if the strategy is paused for validator consolidation.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant whenNotPaused {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _asset, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertWethToEth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(_asset, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n /// Will revert if the strategy is paused for validator consolidation.\n function withdrawAll()\n external\n override\n onlyVaultOrGovernor\n nonReentrant\n whenNotPaused\n {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, WETH, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Returns the last verified balance of validator deposits, validator balance,\n /// WETH and ETH in the strategy contract.\n /// @param _asset Address of weth asset\n /// @return balance Total value of (W)ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by strategy.\n /// @param _asset The address of the asset token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for deposits\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instread of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract,\n /// but so does withdrawals from validators. It's too complex to separate the two\n /// so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IConsolidationSource } from \"../../interfaces/IConsolidation.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\nimport { IBeaconOracle } from \"../../interfaces/IBeaconOracle.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit, remove and consolidate validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable, Pausable {\n using SafeERC20 for IERC20;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Oracle contract that maps block numbers to slots\n address public immutable BEACON_ORACLE;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// Deposit data for new compounding validators.\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountWei Amount of ETH in wei that has been deposited to the beacon chain deposit contract\n /// @param blockNumber Block number when the deposit was made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either PENDING or PROVEN\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 blockNumber;\n uint32 depositIndex;\n DepositStatus status;\n }\n /// @notice Mapping of the root of a deposit (depositDataRoot) to its data\n mapping(bytes32 => DepositData) public deposits;\n /// @notice List of deposit roots that are still to be verified as processed on the beacon chain\n bytes32[] public depositsRoots;\n\n // Validator data\n\n struct ValidatorData {\n bytes32 pubKeyHash; // Hash of the validator's public key using the Beacon Chain's format\n uint64 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes and indexes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n ValidatorData[] public verifiedValidators;\n /// @notice State of the new compounding validators with a 0x02 withdrawal credential prefix.\n /// Uses the Beacon chain hashing for BLSPubkey which is\n /// sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => VALIDATOR_STATE) public validatorState;\n\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n uint64 timestamp;\n uint128 ethBalance;\n }\n // TODO is it more efficient to use the block root rather than hashing it?\n /// @notice Mapping of the block root to the balances at that slot\n mapping(bytes32 => Balances) public snappedBalances;\n uint64 public lastSnapTimestamp;\n uint128 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n bytes32 public consolidationLastPubKeyHash;\n address public consolidationSourceStrategy;\n mapping(address => bool) public consolidationSourceStrategies;\n\n // For future use\n uint256[50] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITED, // The validator has been verified to have a zero balance\n REMOVED // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified and is ready to be staked\n }\n\n event RegistratorChanged(address indexed newAddress);\n event SourceStrategyAdded(address indexed strategy);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n bytes32 indexed depositDataRoot,\n bytes pubKey,\n uint256 amountWei\n );\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint64 indexed validatorIndex\n );\n event DepositVerified(bytes32 indexed depositDataRoot, uint256 amountWei);\n event BalancesSnapped(\n uint256 indexed timestamp,\n bytes32 indexed blockRoot,\n uint256 ethBalance\n );\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 wethBalance,\n uint256 ethBalance\n );\n event ConsolidationRequested(\n bytes32 indexed lastSourcePubKeyHash,\n bytes32 indexed targetPubKeyHash,\n address indexed sourceStrategy\n );\n event ConsolidationVerified(\n bytes32 indexed lastSourcePubKeyHash,\n uint64 indexed lastValidatorIndex,\n uint256 consolidationCount\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconOracle Address of the Beacon Oracle contract that maps block numbers to slots\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconOracle,\n address _beaconProofs\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_ORACLE = _beaconOracle;\n BEACON_PROOFS = _beaconProofs;\n }\n\n /***************************************\n Admin Functions\n ****************************************/\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Adds support for a legacy staking strategy that can be used for consolidation.\n function addSourceStrategy(address _strategy) external onlyGovernor {\n consolidationSourceStrategies[_strategy] = true;\n\n emit SourceStrategyAdded(_strategy);\n }\n\n /***************************************`\n Validator Management\n ****************************************/\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validatorState[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validator validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validator,\n uint64 depositAmountGwei\n ) external onlyRegistrator whenNotPaused {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertEthToWeth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validator.pubkey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == VALIDATOR_STATE.REGISTERED ||\n currentState == VALIDATOR_STATE.VERIFIED),\n \"Not registered or verified\"\n );\n require(\n currentState == VALIDATOR_STATE.VERIFIED ||\n depositAmountWei == 1 ether,\n \"First deposit not 1 ETH\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validator.pubkey,\n withdrawalCredentials,\n validator.signature,\n validator.depositDataRoot\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == VALIDATOR_STATE.REGISTERED) {\n validatorState[pubKeyHash] = VALIDATOR_STATE.STAKED;\n }\n // Store the deposit data for verifyDeposit and verifyBalances\n deposits[validator.depositDataRoot] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n blockNumber: SafeCast.toUint64(block.number),\n depositIndex: SafeCast.toUint32(depositsRoots.length),\n status: DepositStatus.PENDING\n });\n depositsRoots.push(validator.depositDataRoot);\n\n emit ETHStaked(\n pubKeyHash,\n validator.depositDataRoot,\n validator.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// If the remaining balance is < 32 ETH then the validator will be exited.\n /// That can result in the ETH sent to the strategy being more than the requested amount.\n /// The staked ETH will eventually be withdrawn to this staking strategy.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n onlyRegistrator\n whenNotPaused\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove a sweeping validator from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == VALIDATOR_STATE.EXITED ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exited\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validatorState[pubKeyHash] = VALIDATOR_STATE.REMOVED;\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /// @notice Receives requests from supported legacy strategies to consolidate sweeping validators to\n /// a new compounding validator on this new strategy.\n /// @param lastSourcePubKeyHash The last source validator to be consolidated hashed using the Beacon Chain's format.\n /// @param targetPubKeyHash The target validator's public key hash using the Beacon Chain's format.\n function requestConsolidation(\n bytes32 lastSourcePubKeyHash,\n bytes32 targetPubKeyHash\n ) external whenNotPaused {\n require(\n consolidationSourceStrategies[msg.sender],\n \"Not a source strategy\"\n );\n\n // The target validator must be a compounding validator that has been verified\n require(\n validatorState[targetPubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Target validator not verified\"\n );\n\n // Ensure balances can not be verified until after the consolidation has been verified.\n lastSnapTimestamp = 0;\n\n // Store consolidation state\n consolidationLastPubKeyHash = lastSourcePubKeyHash;\n consolidationSourceStrategy = msg.sender;\n\n // Pause the strategy while the consolidation is in progress\n _pause();\n\n emit ConsolidationRequested(\n lastSourcePubKeyHash,\n targetPubKeyHash,\n msg.sender\n );\n }\n\n /***************************************\n SSV Management\n ****************************************/\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n Beacon Chain Proofs\n ****************************************/\n\n /// @notice Verifies a validator's index to its public key.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param validatorPubKeyProof The merkle proof that the validator's index matches its public key\n /// on the beacon chain.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.STAKED,\n \"Validator not staked\"\n );\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key\n IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex\n );\n\n // TODO verify the validator's withdrawal credential points to this strategy\n\n // Store the validator state as verified\n validatorState[pubKeyHash] = VALIDATOR_STATE.VERIFIED;\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(\n ValidatorData({ pubKeyHash: pubKeyHash, index: validatorIndex })\n );\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n /// @param depositDataRoot The root of the deposit data that was stored when\n /// the deposit was made on the execution layer.\n /// @param depositBlockNumber A block number that is on or after the block the deposit\n /// was made on the execution layer.\n /// @param processedSlot Any slot on or after the deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// @param firstPendingDepositSlot The slot of the first pending deposit in the beacon chain\n /// @param firstPendingDepositSlotProof The merkle proof that the slot of the first pending deposit\n /// matches the beacon chain.\n /// BeaconBlock.BeaconBlockBody.deposits[0].slot\n function verifyDeposit(\n bytes32 depositDataRoot,\n uint64 depositBlockNumber,\n uint64 processedSlot,\n uint64 firstPendingDepositSlot,\n bytes calldata firstPendingDepositSlotProof\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositDataRoot];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n validatorState[deposit.pubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n // The block number mapped to a slot needs to be the same block or after the deposit\n // was created on the execution layer.\n require(\n deposit.blockNumber <= depositBlockNumber,\n \"Deposit block before deposit\"\n );\n\n // Get the slot that is on or after the deposit was made on the execution layer.\n uint64 depositSlot = IBeaconOracle(BEACON_ORACLE).blockToSlot(\n depositBlockNumber\n );\n\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the first pending deposit's slot is 0 then there are no pending deposits so\n // our deposit must have been processed on the beacon chain.\n require(\n depositSlot < firstPendingDepositSlot ||\n firstPendingDepositSlot == 0,\n \"Deposit not processed\"\n );\n\n // The slot of the execution layer deposit must be before\n // the slot that the deposit was processed on the beacon chain.\n require(depositSlot < processedSlot, \"Slot not after deposit\");\n\n // Get the beacon block root for the slot that is on or after the deposit was processed on the beacon chain.\n bytes32 blockRoot = IBeaconOracle(BEACON_ORACLE).slotToRoot(\n processedSlot\n );\n\n // Verify the slot of the first pending deposit matches the beacon chain\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n blockRoot,\n firstPendingDepositSlot,\n firstPendingDepositSlotProof\n );\n\n // After verifying the proof, update the contract storage\n deposits[depositDataRoot].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n depositsRoots[deposit.depositIndex] = depositsRoots[\n depositsRoots.length - 1\n ];\n // Delete the last deposit from the list\n depositsRoots.pop();\n\n emit DepositVerified(\n depositDataRoot,\n uint256(deposit.amountGwei) * 1 gwei\n );\n }\n\n // TODO what if the last validator was exited rather than consolidated?\n function verifyConsolidation(\n uint64 parentBlockTimestamp,\n uint64 lastValidatorIndex,\n bytes calldata validatorPubKeyProof,\n bytes32 balancesLeaf,\n bytes calldata validatorBalanceProof\n ) external onlyRegistrator {\n bytes32 consolidationLastPubKeyHashMem = consolidationLastPubKeyHash;\n require(\n consolidationLastPubKeyHashMem != bytes32(0),\n \"No consolidations\"\n );\n\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(parentBlockTimestamp);\n // Verify the validator index has the same public key as the last source validator\n IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n blockRoot,\n consolidationLastPubKeyHashMem,\n validatorPubKeyProof,\n lastValidatorIndex\n );\n\n // Verify the balance of the last validator in the consolidation batch\n // is zero. If its not then the consolidation has not been completed.\n // This proof is to the beacon block root, not the balances container root.\n uint256 validatorBalance = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n blockRoot,\n balancesLeaf,\n validatorBalanceProof,\n lastValidatorIndex,\n IBeaconProofs.BalanceProofLevel.BeaconBlock\n );\n require(validatorBalance == 0, \"Last validator balance not zero\");\n\n // Call the old sweeping strategy to confirm the consolidation has been completed.\n // This will decrease the balance of the source strategy by 32 ETH for each validator being consolidated.\n uint256 consolidationCount = IConsolidationSource(\n consolidationSourceStrategy\n ).confirmConsolidation();\n\n // Increase the balance of this strategy by 32 ETH for each validator being consolidated.\n // This nets out the decrease in the source strategy's balance.\n lastVerifiedEthBalance += SafeCast.toUint128(\n consolidationCount * 32 ether\n );\n\n // Reset the stored consolidation state\n consolidationLastPubKeyHash = bytes32(0);\n consolidationSourceStrategy = address(0);\n\n emit ConsolidationVerified(\n consolidationLastPubKeyHashMem,\n lastValidatorIndex,\n consolidationCount\n );\n\n // Unpause now the balance of the target validator has been verified\n _unpause();\n\n // Take a snap of the balances so the actual balances of the new validator balances can be verified\n _snapBalances();\n }\n\n /// @notice Stores the current ETH balance at the current block.\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n /// Can not be called while a consolidation is in progress.\n function snapBalances() public whenNotPaused {\n _snapBalances();\n }\n\n function _snapBalances() internal {\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(\n SafeCast.toUint64(block.timestamp)\n );\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the balances in the mapping\n snappedBalances[blockRoot] = Balances({\n timestamp: SafeCast.toUint64(block.timestamp),\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n // Store the snapped timestamp\n lastSnapTimestamp = SafeCast.toUint64(block.timestamp);\n\n emit BalancesSnapped(block.timestamp, blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct VerifyBalancesParams {\n bytes32 blockRoot;\n uint64 firstPendingDepositSlot;\n // BeaconBlock.BeaconBlockBody.deposits[0].slot\n bytes firstPendingDepositSlotProof;\n bytes32 balancesContainerRoot;\n // BeaconBlock.state.validators\n bytes validatorContainerProof;\n bytes32[] validatorBalanceLeaves;\n // BeaconBlock.state.validators[validatorIndex].balance\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n function verifyBalances(VerifyBalancesParams calldata params) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalances[params.blockRoot];\n // Check the balances are the latest\n require(lastSnapTimestamp > 0, \"No snapped balances\");\n require(balancesMem.timestamp == lastSnapTimestamp, \"Stale snap\");\n\n uint256 depositsCount = depositsRoots.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit to the beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n params.blockRoot,\n params.firstPendingDepositSlot,\n params.firstPendingDepositSlotProof\n );\n\n uint64 firstPendingDepositBlockNumber = IBeaconOracle(BEACON_ORACLE)\n .slotToBlock(params.firstPendingDepositSlot);\n\n // For each native staking contract's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n bytes32 depositDataRoot = depositsRoots[i];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit block number is before the\n // block number of the staking strategy's deposit.\n // If it has it will need to be verified with `verifyDeposit`\n require(\n firstPendingDepositBlockNumber <\n deposits[depositDataRoot].blockNumber,\n \"Deposit has been processed\"\n );\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei +=\n uint256(deposits[depositDataRoot].amountGwei) *\n 1 gwei;\n }\n }\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n params.blockRoot,\n params.balancesContainerRoot,\n params.validatorContainerProof\n );\n\n // for each validator\n for (uint256 i = 0; i < verifiedValidatorsCount; ++i) {\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n params.balancesContainerRoot,\n params.validatorBalanceLeaves[i],\n params.validatorBalanceProofs[i],\n verifiedValidators[i].index,\n IBeaconProofs.BalanceProofLevel.Container\n );\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += uint256(validatorBalanceGwei) * 1 gwei;\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n validatorState[\n verifiedValidators[i].pubKeyHash\n ] = VALIDATOR_STATE.EXITED;\n\n // Remove the validator from the list of verified validators.\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n // Remove the validator with a zero balance from the list of verified validators\n // Move the last validator to the current index\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n }\n }\n }\n\n uint256 wethBalance = IWETH9(WETH).balanceOf(address(this));\n\n // Store the verified balance in storage\n lastVerifiedEthBalance = SafeCast.toUint128(\n totalDepositsWei + totalValidatorBalance + balancesMem.ethBalance\n );\n // Reset the last snap timestamp so a new snapBalances has to be made\n lastSnapTimestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n wethBalance,\n balancesMem.ethBalance\n );\n }\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /***************************************\n WETH and ETH Accounting\n ****************************************/\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n function _convertWethToEth(uint256 _ethAmount) internal {\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance\n if (lastVerifiedEthBalance > _ethAmount) {\n lastVerifiedEthBalance -= SafeCast.toUint128(_ethAmount);\n } else {\n // This can happen if all ETH in the validators was withdrawn\n // and there was more consensus rewards since the last balance verification.\n // Or it can happen if ETH is donated to this strategy.\n lastVerifiedEthBalance = 0;\n }\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n function _convertEthToWeth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += SafeCast.toUint128(_wethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address public dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/3de98f56666780054699342674f08b14.json b/contracts/deployments/hoodi/solcInputs/3de98f56666780054699342674f08b14.json new file mode 100644 index 0000000000..a0762a62f6 --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/3de98f56666780054699342674f08b14.json @@ -0,0 +1,1155 @@ +{ + "language": "Solidity", + "sources": { + "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {Client} from \"../libraries/Client.sol\";\n\ninterface IRouterClient {\n error UnsupportedDestinationChain(uint64 destChainSelector);\n error InsufficientFeeTokenAmount();\n error InvalidMsgValue();\n\n /// @notice Checks if the given chain ID is supported for sending/receiving.\n /// @param chainSelector The chain to check.\n /// @return supported is true if it is supported, false if not.\n function isChainSupported(uint64 chainSelector) external view returns (bool supported);\n\n /// @notice Gets a list of all supported tokens which can be sent or received\n /// to/from a given chain id.\n /// @param chainSelector The chainSelector.\n /// @return tokens The addresses of all tokens that are supported.\n function getSupportedTokens(uint64 chainSelector) external view returns (address[] memory tokens);\n\n /// @param destinationChainSelector The destination chainSelector\n /// @param message The cross-chain CCIP message including data and/or tokens\n /// @return fee returns execution fee for the message\n /// delivery to destination chain, denominated in the feeToken specified in the message.\n /// @dev Reverts with appropriate reason upon invalid message.\n function getFee(\n uint64 destinationChainSelector,\n Client.EVM2AnyMessage memory message\n ) external view returns (uint256 fee);\n\n /// @notice Request a message to be sent to the destination chain\n /// @param destinationChainSelector The destination chain ID\n /// @param message The cross-chain CCIP message including data and/or tokens\n /// @return messageId The message ID\n /// @dev Note if msg.value is larger than the required fee (from getFee) we accept\n /// the overpayment with no refund.\n /// @dev Reverts with appropriate reason upon invalid message.\n function ccipSend(\n uint64 destinationChainSelector,\n Client.EVM2AnyMessage calldata message\n ) external payable returns (bytes32);\n}\n" + }, + "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// End consumer library.\nlibrary Client {\n /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.\n struct EVMTokenAmount {\n address token; // token address on the local chain.\n uint256 amount; // Amount of tokens.\n }\n\n struct Any2EVMMessage {\n bytes32 messageId; // MessageId corresponding to ccipSend on source.\n uint64 sourceChainSelector; // Source chain selector.\n bytes sender; // abi.decode(sender) if coming from an EVM chain.\n bytes data; // payload sent in original message.\n EVMTokenAmount[] destTokenAmounts; // Tokens and their amounts in their destination chain representation.\n }\n\n // If extraArgs is empty bytes, the default is 200k gas limit.\n struct EVM2AnyMessage {\n bytes receiver; // abi.encode(receiver address) for dest EVM chains\n bytes data; // Data payload\n EVMTokenAmount[] tokenAmounts; // Token transfers\n address feeToken; // Address of feeToken. address(0) means you will send msg.value.\n bytes extraArgs; // Populate this with _argsToBytes(EVMExtraArgsV1)\n }\n\n // bytes4(keccak256(\"CCIP EVMExtraArgsV1\"));\n bytes4 public constant EVM_EXTRA_ARGS_V1_TAG = 0x97a657c9;\n struct EVMExtraArgsV1 {\n uint256 gasLimit;\n }\n\n function _argsToBytes(EVMExtraArgsV1 memory extraArgs) internal pure returns (bytes memory bts) {\n return abi.encodeWithSelector(EVM_EXTRA_ARGS_V1_TAG, extraArgs);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport \"@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol\";\n\nlibrary ExecutorOptions {\n using CalldataBytesLib for bytes;\n\n uint8 internal constant WORKER_ID = 1;\n\n uint8 internal constant OPTION_TYPE_LZRECEIVE = 1;\n uint8 internal constant OPTION_TYPE_NATIVE_DROP = 2;\n uint8 internal constant OPTION_TYPE_LZCOMPOSE = 3;\n uint8 internal constant OPTION_TYPE_ORDERED_EXECUTION = 4;\n uint8 internal constant OPTION_TYPE_LZREAD = 5;\n\n error Executor_InvalidLzReceiveOption();\n error Executor_InvalidNativeDropOption();\n error Executor_InvalidLzComposeOption();\n error Executor_InvalidLzReadOption();\n\n /// @dev decode the next executor option from the options starting from the specified cursor\n /// @param _options [executor_id][executor_option][executor_id][executor_option]...\n /// executor_option = [option_size][option_type][option]\n /// option_size = len(option_type) + len(option)\n /// executor_id: uint8, option_size: uint16, option_type: uint8, option: bytes\n /// @param _cursor the cursor to start decoding from\n /// @return optionType the type of the option\n /// @return option the option of the executor\n /// @return cursor the cursor to start decoding the next executor option\n function nextExecutorOption(\n bytes calldata _options,\n uint256 _cursor\n ) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) {\n unchecked {\n // skip worker id\n cursor = _cursor + 1;\n\n // read option size\n uint16 size = _options.toU16(cursor);\n cursor += 2;\n\n // read option type\n optionType = _options.toU8(cursor);\n\n // startCursor and endCursor are used to slice the option from _options\n uint256 startCursor = cursor + 1; // skip option type\n uint256 endCursor = cursor + size;\n option = _options[startCursor:endCursor];\n cursor += size;\n }\n }\n\n function decodeLzReceiveOption(bytes calldata _option) internal pure returns (uint128 gas, uint128 value) {\n if (_option.length != 16 && _option.length != 32) revert Executor_InvalidLzReceiveOption();\n gas = _option.toU128(0);\n value = _option.length == 32 ? _option.toU128(16) : 0;\n }\n\n function decodeNativeDropOption(bytes calldata _option) internal pure returns (uint128 amount, bytes32 receiver) {\n if (_option.length != 48) revert Executor_InvalidNativeDropOption();\n amount = _option.toU128(0);\n receiver = _option.toB32(16);\n }\n\n function decodeLzComposeOption(\n bytes calldata _option\n ) internal pure returns (uint16 index, uint128 gas, uint128 value) {\n if (_option.length != 18 && _option.length != 34) revert Executor_InvalidLzComposeOption();\n index = _option.toU16(0);\n gas = _option.toU128(2);\n value = _option.length == 34 ? _option.toU128(18) : 0;\n }\n\n function decodeLzReadOption(\n bytes calldata _option\n ) internal pure returns (uint128 gas, uint32 calldataSize, uint128 value) {\n if (_option.length != 20 && _option.length != 36) revert Executor_InvalidLzReadOption();\n gas = _option.toU128(0);\n calldataSize = _option.toU32(16);\n value = _option.length == 36 ? _option.toU128(20) : 0;\n }\n\n function encodeLzReceiveOption(uint128 _gas, uint128 _value) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_gas) : abi.encodePacked(_gas, _value);\n }\n\n function encodeNativeDropOption(uint128 _amount, bytes32 _receiver) internal pure returns (bytes memory) {\n return abi.encodePacked(_amount, _receiver);\n }\n\n function encodeLzComposeOption(uint16 _index, uint128 _gas, uint128 _value) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_index, _gas) : abi.encodePacked(_index, _gas, _value);\n }\n\n function encodeLzReadOption(\n uint128 _gas,\n uint32 _calldataSize,\n uint128 _value\n ) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_gas, _calldataSize) : abi.encodePacked(_gas, _calldataSize, _value);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/DVNOptions.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport { BytesLib } from \"solidity-bytes-utils/contracts/BytesLib.sol\";\n\nimport { BitMap256 } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/BitMaps.sol\";\nimport { CalldataBytesLib } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol\";\n\nlibrary DVNOptions {\n using CalldataBytesLib for bytes;\n using BytesLib for bytes;\n\n uint8 internal constant WORKER_ID = 2;\n uint8 internal constant OPTION_TYPE_PRECRIME = 1;\n\n error DVN_InvalidDVNIdx();\n error DVN_InvalidDVNOptions(uint256 cursor);\n\n /// @dev group dvn options by its idx\n /// @param _options [dvn_id][dvn_option][dvn_id][dvn_option]...\n /// dvn_option = [option_size][dvn_idx][option_type][option]\n /// option_size = len(dvn_idx) + len(option_type) + len(option)\n /// dvn_id: uint8, dvn_idx: uint8, option_size: uint16, option_type: uint8, option: bytes\n /// @return dvnOptions the grouped options, still share the same format of _options\n /// @return dvnIndices the dvn indices\n function groupDVNOptionsByIdx(\n bytes memory _options\n ) internal pure returns (bytes[] memory dvnOptions, uint8[] memory dvnIndices) {\n if (_options.length == 0) return (dvnOptions, dvnIndices);\n\n uint8 numDVNs = getNumDVNs(_options);\n\n // if there is only 1 dvn, we can just return the whole options\n if (numDVNs == 1) {\n dvnOptions = new bytes[](1);\n dvnOptions[0] = _options;\n\n dvnIndices = new uint8[](1);\n dvnIndices[0] = _options.toUint8(3); // dvn idx\n return (dvnOptions, dvnIndices);\n }\n\n // otherwise, we need to group the options by dvn_idx\n dvnIndices = new uint8[](numDVNs);\n dvnOptions = new bytes[](numDVNs);\n unchecked {\n uint256 cursor = 0;\n uint256 start = 0;\n uint8 lastDVNIdx = 255; // 255 is an invalid dvn_idx\n\n while (cursor < _options.length) {\n ++cursor; // skip worker_id\n\n // optionLength asserted in getNumDVNs (skip check)\n uint16 optionLength = _options.toUint16(cursor);\n cursor += 2;\n\n // dvnIdx asserted in getNumDVNs (skip check)\n uint8 dvnIdx = _options.toUint8(cursor);\n\n // dvnIdx must equal to the lastDVNIdx for the first option\n // so it is always skipped in the first option\n // this operation slices out options whenever the scan finds a different lastDVNIdx\n if (lastDVNIdx == 255) {\n lastDVNIdx = dvnIdx;\n } else if (dvnIdx != lastDVNIdx) {\n uint256 len = cursor - start - 3; // 3 is for worker_id and option_length\n bytes memory opt = _options.slice(start, len);\n _insertDVNOptions(dvnOptions, dvnIndices, lastDVNIdx, opt);\n\n // reset the start and lastDVNIdx\n start += len;\n lastDVNIdx = dvnIdx;\n }\n\n cursor += optionLength;\n }\n\n // skip check the cursor here because the cursor is asserted in getNumDVNs\n // if we have reached the end of the options, we need to process the last dvn\n uint256 size = cursor - start;\n bytes memory op = _options.slice(start, size);\n _insertDVNOptions(dvnOptions, dvnIndices, lastDVNIdx, op);\n\n // revert dvnIndices to start from 0\n for (uint8 i = 0; i < numDVNs; ++i) {\n --dvnIndices[i];\n }\n }\n }\n\n function _insertDVNOptions(\n bytes[] memory _dvnOptions,\n uint8[] memory _dvnIndices,\n uint8 _dvnIdx,\n bytes memory _newOptions\n ) internal pure {\n // dvnIdx starts from 0 but default value of dvnIndices is 0,\n // so we tell if the slot is empty by adding 1 to dvnIdx\n if (_dvnIdx == 255) revert DVN_InvalidDVNIdx();\n uint8 dvnIdxAdj = _dvnIdx + 1;\n\n for (uint256 j = 0; j < _dvnIndices.length; ++j) {\n uint8 index = _dvnIndices[j];\n if (dvnIdxAdj == index) {\n _dvnOptions[j] = abi.encodePacked(_dvnOptions[j], _newOptions);\n break;\n } else if (index == 0) {\n // empty slot, that means it is the first time we see this dvn\n _dvnIndices[j] = dvnIdxAdj;\n _dvnOptions[j] = _newOptions;\n break;\n }\n }\n }\n\n /// @dev get the number of unique dvns\n /// @param _options the format is the same as groupDVNOptionsByIdx\n function getNumDVNs(bytes memory _options) internal pure returns (uint8 numDVNs) {\n uint256 cursor = 0;\n BitMap256 bitmap;\n\n // find number of unique dvn_idx\n unchecked {\n while (cursor < _options.length) {\n ++cursor; // skip worker_id\n\n uint16 optionLength = _options.toUint16(cursor);\n cursor += 2;\n if (optionLength < 2) revert DVN_InvalidDVNOptions(cursor); // at least 1 byte for dvn_idx and 1 byte for option_type\n\n uint8 dvnIdx = _options.toUint8(cursor);\n\n // if dvnIdx is not set, increment numDVNs\n // max num of dvns is 255, 255 is an invalid dvn_idx\n // The order of the dvnIdx is not required to be sequential, as enforcing the order may weaken\n // the composability of the options. e.g. if we refrain from enforcing the order, an OApp that has\n // already enforced certain options can append additional options to the end of the enforced\n // ones without restrictions.\n if (dvnIdx == 255) revert DVN_InvalidDVNIdx();\n if (!bitmap.get(dvnIdx)) {\n ++numDVNs;\n bitmap = bitmap.set(dvnIdx);\n }\n\n cursor += optionLength;\n }\n }\n if (cursor != _options.length) revert DVN_InvalidDVNOptions(cursor);\n }\n\n /// @dev decode the next dvn option from _options starting from the specified cursor\n /// @param _options the format is the same as groupDVNOptionsByIdx\n /// @param _cursor the cursor to start decoding\n /// @return optionType the type of the option\n /// @return option the option\n /// @return cursor the cursor to start decoding the next option\n function nextDVNOption(\n bytes calldata _options,\n uint256 _cursor\n ) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) {\n unchecked {\n // skip worker id\n cursor = _cursor + 1;\n\n // read option size\n uint16 size = _options.toU16(cursor);\n cursor += 2;\n\n // read option type\n optionType = _options.toU8(cursor + 1); // skip dvn_idx\n\n // startCursor and endCursor are used to slice the option from _options\n uint256 startCursor = cursor + 2; // skip option type and dvn_idx\n uint256 endCursor = cursor + size;\n option = _options[startCursor:endCursor];\n cursor += size;\n }\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { IMessageLibManager } from \"./IMessageLibManager.sol\";\nimport { IMessagingComposer } from \"./IMessagingComposer.sol\";\nimport { IMessagingChannel } from \"./IMessagingChannel.sol\";\nimport { IMessagingContext } from \"./IMessagingContext.sol\";\n\nstruct MessagingParams {\n uint32 dstEid;\n bytes32 receiver;\n bytes message;\n bytes options;\n bool payInLzToken;\n}\n\nstruct MessagingReceipt {\n bytes32 guid;\n uint64 nonce;\n MessagingFee fee;\n}\n\nstruct MessagingFee {\n uint256 nativeFee;\n uint256 lzTokenFee;\n}\n\nstruct Origin {\n uint32 srcEid;\n bytes32 sender;\n uint64 nonce;\n}\n\ninterface ILayerZeroEndpointV2 is IMessageLibManager, IMessagingComposer, IMessagingChannel, IMessagingContext {\n event PacketSent(bytes encodedPayload, bytes options, address sendLibrary);\n\n event PacketVerified(Origin origin, address receiver, bytes32 payloadHash);\n\n event PacketDelivered(Origin origin, address receiver);\n\n event LzReceiveAlert(\n address indexed receiver,\n address indexed executor,\n Origin origin,\n bytes32 guid,\n uint256 gas,\n uint256 value,\n bytes message,\n bytes extraData,\n bytes reason\n );\n\n event LzTokenSet(address token);\n\n event DelegateSet(address sender, address delegate);\n\n function quote(MessagingParams calldata _params, address _sender) external view returns (MessagingFee memory);\n\n function send(\n MessagingParams calldata _params,\n address _refundAddress\n ) external payable returns (MessagingReceipt memory);\n\n function verify(Origin calldata _origin, address _receiver, bytes32 _payloadHash) external;\n\n function verifiable(Origin calldata _origin, address _receiver) external view returns (bool);\n\n function initializable(Origin calldata _origin, address _receiver) external view returns (bool);\n\n function lzReceive(\n Origin calldata _origin,\n address _receiver,\n bytes32 _guid,\n bytes calldata _message,\n bytes calldata _extraData\n ) external payable;\n\n // oapp can burn messages partially by calling this function with its own business logic if messages are verified in order\n function clear(address _oapp, Origin calldata _origin, bytes32 _guid, bytes calldata _message) external;\n\n function setLzToken(address _lzToken) external;\n\n function lzToken() external view returns (address);\n\n function nativeToken() external view returns (address);\n\n function setDelegate(address _delegate) external;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { Origin } from \"./ILayerZeroEndpointV2.sol\";\n\ninterface ILayerZeroReceiver {\n function allowInitializePath(Origin calldata _origin) external view returns (bool);\n\n function nextNonce(uint32 _eid, bytes32 _sender) external view returns (uint64);\n\n function lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) external payable;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessageLib.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { IERC165 } from \"@openzeppelin/contracts/utils/introspection/IERC165.sol\";\n\nimport { SetConfigParam } from \"./IMessageLibManager.sol\";\n\nenum MessageLibType {\n Send,\n Receive,\n SendAndReceive\n}\n\ninterface IMessageLib is IERC165 {\n function setConfig(address _oapp, SetConfigParam[] calldata _config) external;\n\n function getConfig(uint32 _eid, address _oapp, uint32 _configType) external view returns (bytes memory config);\n\n function isSupportedEid(uint32 _eid) external view returns (bool);\n\n // message libs of same major version are compatible\n function version() external view returns (uint64 major, uint8 minor, uint8 endpointVersion);\n\n function messageLibType() external view returns (MessageLibType);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessageLibManager.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nstruct SetConfigParam {\n uint32 eid;\n uint32 configType;\n bytes config;\n}\n\ninterface IMessageLibManager {\n struct Timeout {\n address lib;\n uint256 expiry;\n }\n\n event LibraryRegistered(address newLib);\n event DefaultSendLibrarySet(uint32 eid, address newLib);\n event DefaultReceiveLibrarySet(uint32 eid, address newLib);\n event DefaultReceiveLibraryTimeoutSet(uint32 eid, address oldLib, uint256 expiry);\n event SendLibrarySet(address sender, uint32 eid, address newLib);\n event ReceiveLibrarySet(address receiver, uint32 eid, address newLib);\n event ReceiveLibraryTimeoutSet(address receiver, uint32 eid, address oldLib, uint256 timeout);\n\n function registerLibrary(address _lib) external;\n\n function isRegisteredLibrary(address _lib) external view returns (bool);\n\n function getRegisteredLibraries() external view returns (address[] memory);\n\n function setDefaultSendLibrary(uint32 _eid, address _newLib) external;\n\n function defaultSendLibrary(uint32 _eid) external view returns (address);\n\n function setDefaultReceiveLibrary(uint32 _eid, address _newLib, uint256 _gracePeriod) external;\n\n function defaultReceiveLibrary(uint32 _eid) external view returns (address);\n\n function setDefaultReceiveLibraryTimeout(uint32 _eid, address _lib, uint256 _expiry) external;\n\n function defaultReceiveLibraryTimeout(uint32 _eid) external view returns (address lib, uint256 expiry);\n\n function isSupportedEid(uint32 _eid) external view returns (bool);\n\n function isValidReceiveLibrary(address _receiver, uint32 _eid, address _lib) external view returns (bool);\n\n /// ------------------- OApp interfaces -------------------\n function setSendLibrary(address _oapp, uint32 _eid, address _newLib) external;\n\n function getSendLibrary(address _sender, uint32 _eid) external view returns (address lib);\n\n function isDefaultSendLibrary(address _sender, uint32 _eid) external view returns (bool);\n\n function setReceiveLibrary(address _oapp, uint32 _eid, address _newLib, uint256 _gracePeriod) external;\n\n function getReceiveLibrary(address _receiver, uint32 _eid) external view returns (address lib, bool isDefault);\n\n function setReceiveLibraryTimeout(address _oapp, uint32 _eid, address _lib, uint256 _expiry) external;\n\n function receiveLibraryTimeout(address _receiver, uint32 _eid) external view returns (address lib, uint256 expiry);\n\n function setConfig(address _oapp, address _lib, SetConfigParam[] calldata _params) external;\n\n function getConfig(\n address _oapp,\n address _lib,\n uint32 _eid,\n uint32 _configType\n ) external view returns (bytes memory config);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingChannel.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingChannel {\n event InboundNonceSkipped(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce);\n event PacketNilified(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);\n event PacketBurnt(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);\n\n function eid() external view returns (uint32);\n\n // this is an emergency function if a message cannot be verified for some reasons\n // required to provide _nextNonce to avoid race condition\n function skip(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce) external;\n\n function nilify(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;\n\n function burn(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;\n\n function nextGuid(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (bytes32);\n\n function inboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);\n\n function outboundNonce(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (uint64);\n\n function inboundPayloadHash(\n address _receiver,\n uint32 _srcEid,\n bytes32 _sender,\n uint64 _nonce\n ) external view returns (bytes32);\n\n function lazyInboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingComposer.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingComposer {\n event ComposeSent(address from, address to, bytes32 guid, uint16 index, bytes message);\n event ComposeDelivered(address from, address to, bytes32 guid, uint16 index);\n event LzComposeAlert(\n address indexed from,\n address indexed to,\n address indexed executor,\n bytes32 guid,\n uint16 index,\n uint256 gas,\n uint256 value,\n bytes message,\n bytes extraData,\n bytes reason\n );\n\n function composeQueue(\n address _from,\n address _to,\n bytes32 _guid,\n uint16 _index\n ) external view returns (bytes32 messageHash);\n\n function sendCompose(address _to, bytes32 _guid, uint16 _index, bytes calldata _message) external;\n\n function lzCompose(\n address _from,\n address _to,\n bytes32 _guid,\n uint16 _index,\n bytes calldata _message,\n bytes calldata _extraData\n ) external payable;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingContext.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingContext {\n function isSendingMessage() external view returns (bool);\n\n function getSendContext() external view returns (uint32 dstEid, address sender);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ISendLib.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { MessagingFee } from \"./ILayerZeroEndpointV2.sol\";\nimport { IMessageLib } from \"./IMessageLib.sol\";\n\nstruct Packet {\n uint64 nonce;\n uint32 srcEid;\n address sender;\n uint32 dstEid;\n bytes32 receiver;\n bytes32 guid;\n bytes message;\n}\n\ninterface ISendLib is IMessageLib {\n function send(\n Packet calldata _packet,\n bytes calldata _options,\n bool _payInLzToken\n ) external returns (MessagingFee memory, bytes memory encodedPacket);\n\n function quote(\n Packet calldata _packet,\n bytes calldata _options,\n bool _payInLzToken\n ) external view returns (MessagingFee memory);\n\n function setTreasury(address _treasury) external;\n\n function withdrawFee(address _to, uint256 _amount) external;\n\n function withdrawLzTokenFee(address _lzToken, address _to, uint256 _amount) external;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/AddressCast.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nlibrary AddressCast {\n error AddressCast_InvalidSizeForAddress();\n error AddressCast_InvalidAddress();\n\n function toBytes32(bytes calldata _addressBytes) internal pure returns (bytes32 result) {\n if (_addressBytes.length > 32) revert AddressCast_InvalidAddress();\n result = bytes32(_addressBytes);\n unchecked {\n uint256 offset = 32 - _addressBytes.length;\n result = result >> (offset * 8);\n }\n }\n\n function toBytes32(address _address) internal pure returns (bytes32 result) {\n result = bytes32(uint256(uint160(_address)));\n }\n\n function toBytes(bytes32 _addressBytes32, uint256 _size) internal pure returns (bytes memory result) {\n if (_size == 0 || _size > 32) revert AddressCast_InvalidSizeForAddress();\n result = new bytes(_size);\n unchecked {\n uint256 offset = 256 - _size * 8;\n assembly {\n mstore(add(result, 32), shl(offset, _addressBytes32))\n }\n }\n }\n\n function toAddress(bytes32 _addressBytes32) internal pure returns (address result) {\n result = address(uint160(uint256(_addressBytes32)));\n }\n\n function toAddress(bytes calldata _addressBytes) internal pure returns (address result) {\n if (_addressBytes.length != 20) revert AddressCast_InvalidAddress();\n result = address(bytes20(_addressBytes));\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nlibrary CalldataBytesLib {\n function toU8(bytes calldata _bytes, uint256 _start) internal pure returns (uint8) {\n return uint8(_bytes[_start]);\n }\n\n function toU16(bytes calldata _bytes, uint256 _start) internal pure returns (uint16) {\n unchecked {\n uint256 end = _start + 2;\n return uint16(bytes2(_bytes[_start:end]));\n }\n }\n\n function toU32(bytes calldata _bytes, uint256 _start) internal pure returns (uint32) {\n unchecked {\n uint256 end = _start + 4;\n return uint32(bytes4(_bytes[_start:end]));\n }\n }\n\n function toU64(bytes calldata _bytes, uint256 _start) internal pure returns (uint64) {\n unchecked {\n uint256 end = _start + 8;\n return uint64(bytes8(_bytes[_start:end]));\n }\n }\n\n function toU128(bytes calldata _bytes, uint256 _start) internal pure returns (uint128) {\n unchecked {\n uint256 end = _start + 16;\n return uint128(bytes16(_bytes[_start:end]));\n }\n }\n\n function toU256(bytes calldata _bytes, uint256 _start) internal pure returns (uint256) {\n unchecked {\n uint256 end = _start + 32;\n return uint256(bytes32(_bytes[_start:end]));\n }\n }\n\n function toAddr(bytes calldata _bytes, uint256 _start) internal pure returns (address) {\n unchecked {\n uint256 end = _start + 20;\n return address(bytes20(_bytes[_start:end]));\n }\n }\n\n function toB32(bytes calldata _bytes, uint256 _start) internal pure returns (bytes32) {\n unchecked {\n uint256 end = _start + 32;\n return bytes32(_bytes[_start:end]);\n }\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/BitMaps.sol": { + "content": "// SPDX-License-Identifier: MIT\n\n// modified from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/structs/BitMaps.sol\npragma solidity ^0.8.20;\n\ntype BitMap256 is uint256;\n\nusing BitMaps for BitMap256 global;\n\nlibrary BitMaps {\n /**\n * @dev Returns whether the bit at `index` is set.\n */\n function get(BitMap256 bitmap, uint8 index) internal pure returns (bool) {\n uint256 mask = 1 << index;\n return BitMap256.unwrap(bitmap) & mask != 0;\n }\n\n /**\n * @dev Sets the bit at `index`.\n */\n function set(BitMap256 bitmap, uint8 index) internal pure returns (BitMap256) {\n uint256 mask = 1 << index;\n return BitMap256.wrap(BitMap256.unwrap(bitmap) | mask);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport { Packet } from \"../../interfaces/ISendLib.sol\";\nimport { AddressCast } from \"../../libs/AddressCast.sol\";\n\nlibrary PacketV1Codec {\n using AddressCast for address;\n using AddressCast for bytes32;\n\n uint8 internal constant PACKET_VERSION = 1;\n\n // header (version + nonce + path)\n // version\n uint256 private constant PACKET_VERSION_OFFSET = 0;\n // nonce\n uint256 private constant NONCE_OFFSET = 1;\n // path\n uint256 private constant SRC_EID_OFFSET = 9;\n uint256 private constant SENDER_OFFSET = 13;\n uint256 private constant DST_EID_OFFSET = 45;\n uint256 private constant RECEIVER_OFFSET = 49;\n // payload (guid + message)\n uint256 private constant GUID_OFFSET = 81; // keccak256(nonce + path)\n uint256 private constant MESSAGE_OFFSET = 113;\n\n function encode(Packet memory _packet) internal pure returns (bytes memory encodedPacket) {\n encodedPacket = abi.encodePacked(\n PACKET_VERSION,\n _packet.nonce,\n _packet.srcEid,\n _packet.sender.toBytes32(),\n _packet.dstEid,\n _packet.receiver,\n _packet.guid,\n _packet.message\n );\n }\n\n function encodePacketHeader(Packet memory _packet) internal pure returns (bytes memory) {\n return\n abi.encodePacked(\n PACKET_VERSION,\n _packet.nonce,\n _packet.srcEid,\n _packet.sender.toBytes32(),\n _packet.dstEid,\n _packet.receiver\n );\n }\n\n function encodePayload(Packet memory _packet) internal pure returns (bytes memory) {\n return abi.encodePacked(_packet.guid, _packet.message);\n }\n\n function header(bytes calldata _packet) internal pure returns (bytes calldata) {\n return _packet[0:GUID_OFFSET];\n }\n\n function version(bytes calldata _packet) internal pure returns (uint8) {\n return uint8(bytes1(_packet[PACKET_VERSION_OFFSET:NONCE_OFFSET]));\n }\n\n function nonce(bytes calldata _packet) internal pure returns (uint64) {\n return uint64(bytes8(_packet[NONCE_OFFSET:SRC_EID_OFFSET]));\n }\n\n function srcEid(bytes calldata _packet) internal pure returns (uint32) {\n return uint32(bytes4(_packet[SRC_EID_OFFSET:SENDER_OFFSET]));\n }\n\n function sender(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[SENDER_OFFSET:DST_EID_OFFSET]);\n }\n\n function senderAddressB20(bytes calldata _packet) internal pure returns (address) {\n return sender(_packet).toAddress();\n }\n\n function dstEid(bytes calldata _packet) internal pure returns (uint32) {\n return uint32(bytes4(_packet[DST_EID_OFFSET:RECEIVER_OFFSET]));\n }\n\n function receiver(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[RECEIVER_OFFSET:GUID_OFFSET]);\n }\n\n function receiverB20(bytes calldata _packet) internal pure returns (address) {\n return receiver(_packet).toAddress();\n }\n\n function guid(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[GUID_OFFSET:MESSAGE_OFFSET]);\n }\n\n function message(bytes calldata _packet) internal pure returns (bytes calldata) {\n return bytes(_packet[MESSAGE_OFFSET:]);\n }\n\n function payload(bytes calldata _packet) internal pure returns (bytes calldata) {\n return bytes(_packet[GUID_OFFSET:]);\n }\n\n function payloadHash(bytes calldata _packet) internal pure returns (bytes32) {\n return keccak256(payload(_packet));\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { ILayerZeroEndpointV2 } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\n\n/**\n * @title IOAppCore\n */\ninterface IOAppCore {\n // Custom error messages\n error OnlyPeer(uint32 eid, bytes32 sender);\n error NoPeer(uint32 eid);\n error InvalidEndpointCall();\n error InvalidDelegate();\n\n // Event emitted when a peer (OApp) is set for a corresponding endpoint\n event PeerSet(uint32 eid, bytes32 peer);\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n */\n function oAppVersion() external view returns (uint64 senderVersion, uint64 receiverVersion);\n\n /**\n * @notice Retrieves the LayerZero endpoint associated with the OApp.\n * @return iEndpoint The LayerZero endpoint as an interface.\n */\n function endpoint() external view returns (ILayerZeroEndpointV2 iEndpoint);\n\n /**\n * @notice Retrieves the peer (OApp) associated with a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @return peer The peer address (OApp instance) associated with the corresponding endpoint.\n */\n function peers(uint32 _eid) external view returns (bytes32 peer);\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n */\n function setPeer(uint32 _eid, bytes32 _peer) external;\n\n /**\n * @notice Sets the delegate address for the OApp Core.\n * @param _delegate The address of the delegate to be set.\n */\n function setDelegate(address _delegate) external;\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppMsgInspector.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n/**\n * @title IOAppMsgInspector\n * @dev Interface for the OApp Message Inspector, allowing examination of message and options contents.\n */\ninterface IOAppMsgInspector {\n // Custom error message for inspection failure\n error InspectionFailed(bytes message, bytes options);\n\n /**\n * @notice Allows the inspector to examine LayerZero message contents and optionally throw a revert if invalid.\n * @param _message The message payload to be inspected.\n * @param _options Additional options or parameters for inspection.\n * @return valid A boolean indicating whether the inspection passed (true) or failed (false).\n *\n * @dev Optionally done as a revert, OR use the boolean provided to handle the failure.\n */\n function inspect(bytes calldata _message, bytes calldata _options) external view returns (bool valid);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppOptionsType3.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Struct representing enforced option parameters.\n */\nstruct EnforcedOptionParam {\n uint32 eid; // Endpoint ID\n uint16 msgType; // Message Type\n bytes options; // Additional options\n}\n\n/**\n * @title IOAppOptionsType3\n * @dev Interface for the OApp with Type 3 Options, allowing the setting and combining of enforced options.\n */\ninterface IOAppOptionsType3 {\n // Custom error message for invalid options\n error InvalidOptions(bytes options);\n\n // Event emitted when enforced options are set\n event EnforcedOptionSet(EnforcedOptionParam[] _enforcedOptions);\n\n /**\n * @notice Sets enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n */\n function setEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) external;\n\n /**\n * @notice Combines options for a given endpoint and message type.\n * @param _eid The endpoint ID.\n * @param _msgType The OApp message type.\n * @param _extraOptions Additional options passed by the caller.\n * @return options The combination of caller specified options AND enforced options.\n */\n function combineOptions(\n uint32 _eid,\n uint16 _msgType,\n bytes calldata _extraOptions\n ) external view returns (bytes memory options);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\nimport { ILayerZeroReceiver, Origin } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroReceiver.sol\";\n\ninterface IOAppReceiver is ILayerZeroReceiver {\n /**\n * @notice Indicates whether an address is an approved composeMsg sender to the Endpoint.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _message The lzReceive payload.\n * @param _sender The sender address.\n * @return isSender Is a valid sender.\n *\n * @dev Applications can optionally choose to implement a separate composeMsg sender that is NOT the bridging layer.\n * @dev The default sender IS the OAppReceiver implementer.\n */\n function isComposeMsgSender(\n Origin calldata _origin,\n bytes calldata _message,\n address _sender\n ) external view returns (bool isSender);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IOAppOptionsType3, EnforcedOptionParam } from \"../interfaces/IOAppOptionsType3.sol\";\n\n/**\n * @title OAppOptionsType3\n * @dev Abstract contract implementing the IOAppOptionsType3 interface with type 3 options.\n */\nabstract contract OAppOptionsType3 is IOAppOptionsType3, Ownable {\n uint16 internal constant OPTION_TYPE_3 = 3;\n\n // @dev The \"msgType\" should be defined in the child contract.\n mapping(uint32 eid => mapping(uint16 msgType => bytes enforcedOption)) public enforcedOptions;\n\n /**\n * @dev Sets the enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Provides a way for the OApp to enforce things like paying for PreCrime, AND/OR minimum dst lzReceive gas amounts etc.\n * @dev These enforced options can vary as the potential options/execution on the remote may differ as per the msgType.\n * eg. Amount of lzReceive() gas necessary to deliver a lzCompose() message adds overhead you dont want to pay\n * if you are only making a standard LayerZero message ie. lzReceive() WITHOUT sendCompose().\n */\n function setEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) public virtual onlyOwner {\n _setEnforcedOptions(_enforcedOptions);\n }\n\n /**\n * @dev Sets the enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n *\n * @dev Provides a way for the OApp to enforce things like paying for PreCrime, AND/OR minimum dst lzReceive gas amounts etc.\n * @dev These enforced options can vary as the potential options/execution on the remote may differ as per the msgType.\n * eg. Amount of lzReceive() gas necessary to deliver a lzCompose() message adds overhead you dont want to pay\n * if you are only making a standard LayerZero message ie. lzReceive() WITHOUT sendCompose().\n */\n function _setEnforcedOptions(EnforcedOptionParam[] memory _enforcedOptions) internal virtual {\n for (uint256 i = 0; i < _enforcedOptions.length; i++) {\n // @dev Enforced options are only available for optionType 3, as type 1 and 2 dont support combining.\n _assertOptionsType3(_enforcedOptions[i].options);\n enforcedOptions[_enforcedOptions[i].eid][_enforcedOptions[i].msgType] = _enforcedOptions[i].options;\n }\n\n emit EnforcedOptionSet(_enforcedOptions);\n }\n\n /**\n * @notice Combines options for a given endpoint and message type.\n * @param _eid The endpoint ID.\n * @param _msgType The OAPP message type.\n * @param _extraOptions Additional options passed by the caller.\n * @return options The combination of caller specified options AND enforced options.\n *\n * @dev If there is an enforced lzReceive option:\n * - {gasLimit: 200k, msg.value: 1 ether} AND a caller supplies a lzReceive option: {gasLimit: 100k, msg.value: 0.5 ether}\n * - The resulting options will be {gasLimit: 300k, msg.value: 1.5 ether} when the message is executed on the remote lzReceive() function.\n * @dev This presence of duplicated options is handled off-chain in the verifier/executor.\n */\n function combineOptions(\n uint32 _eid,\n uint16 _msgType,\n bytes calldata _extraOptions\n ) public view virtual returns (bytes memory) {\n bytes memory enforced = enforcedOptions[_eid][_msgType];\n\n // No enforced options, pass whatever the caller supplied, even if it's empty or legacy type 1/2 options.\n if (enforced.length == 0) return _extraOptions;\n\n // No caller options, return enforced\n if (_extraOptions.length == 0) return enforced;\n\n // @dev If caller provided _extraOptions, must be type 3 as its the ONLY type that can be combined.\n if (_extraOptions.length >= 2) {\n _assertOptionsType3(_extraOptions);\n // @dev Remove the first 2 bytes containing the type from the _extraOptions and combine with enforced.\n return bytes.concat(enforced, _extraOptions[2:]);\n }\n\n // No valid set of options was found.\n revert InvalidOptions(_extraOptions);\n }\n\n /**\n * @dev Internal function to assert that options are of type 3.\n * @param _options The options to be checked.\n */\n function _assertOptionsType3(bytes memory _options) internal pure virtual {\n uint16 optionsType;\n assembly {\n optionsType := mload(add(_options, 2))\n }\n if (optionsType != OPTION_TYPE_3) revert InvalidOptions(_options);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { BytesLib } from \"solidity-bytes-utils/contracts/BytesLib.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { ExecutorOptions } from \"@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol\";\nimport { DVNOptions } from \"@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/DVNOptions.sol\";\n\n/**\n * @title OptionsBuilder\n * @dev Library for building and encoding various message options.\n */\nlibrary OptionsBuilder {\n using SafeCast for uint256;\n using BytesLib for bytes;\n\n // Constants for options types\n uint16 internal constant TYPE_1 = 1; // legacy options type 1\n uint16 internal constant TYPE_2 = 2; // legacy options type 2\n uint16 internal constant TYPE_3 = 3;\n\n // Custom error message\n error InvalidSize(uint256 max, uint256 actual);\n error InvalidOptionType(uint16 optionType);\n\n // Modifier to ensure only options of type 3 are used\n modifier onlyType3(bytes memory _options) {\n if (_options.toUint16(0) != TYPE_3) revert InvalidOptionType(_options.toUint16(0));\n _;\n }\n\n /**\n * @dev Creates a new options container with type 3.\n * @return options The newly created options container.\n */\n function newOptions() internal pure returns (bytes memory) {\n return abi.encodePacked(TYPE_3);\n }\n\n /**\n * @dev Adds an executor LZ receive option to the existing options.\n * @param _options The existing options container.\n * @param _gas The gasLimit used on the lzReceive() function in the OApp.\n * @param _value The msg.value passed to the lzReceive() function in the OApp.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed by the executor\n * eg. if (_gas: 200k, and _value: 1 ether) AND (_gas: 100k, _value: 0.5 ether) are sent in an option to the LayerZeroEndpoint,\n * that becomes (300k, 1.5 ether) when the message is executed on the remote lzReceive() function.\n */\n function addExecutorLzReceiveOption(\n bytes memory _options,\n uint128 _gas,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzReceiveOption(_gas, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZRECEIVE, option);\n }\n\n /**\n * @dev Adds an executor native drop option to the existing options.\n * @param _options The existing options container.\n * @param _amount The amount for the native value that is airdropped to the 'receiver'.\n * @param _receiver The receiver address for the native drop option.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed by the executor on the remote chain.\n */\n function addExecutorNativeDropOption(\n bytes memory _options,\n uint128 _amount,\n bytes32 _receiver\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeNativeDropOption(_amount, _receiver);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_NATIVE_DROP, option);\n }\n\n // /**\n // * @dev Adds an executor native drop option to the existing options.\n // * @param _options The existing options container.\n // * @param _amount The amount for the native value that is airdropped to the 'receiver'.\n // * @param _receiver The receiver address for the native drop option.\n // * @return options The updated options container.\n // *\n // * @dev When multiples of this option are added, they are summed by the executor on the remote chain.\n // */\n function addExecutorLzReadOption(\n bytes memory _options,\n uint128 _gas,\n uint32 _size,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzReadOption(_gas, _size, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZREAD, option);\n }\n\n /**\n * @dev Adds an executor LZ compose option to the existing options.\n * @param _options The existing options container.\n * @param _index The index for the lzCompose() function call.\n * @param _gas The gasLimit for the lzCompose() function call.\n * @param _value The msg.value for the lzCompose() function call.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed PER index by the executor on the remote chain.\n * @dev If the OApp sends N lzCompose calls on the remote, you must provide N incremented indexes starting with 0.\n * ie. When your remote OApp composes (N = 3) messages, you must set this option for index 0,1,2\n */\n function addExecutorLzComposeOption(\n bytes memory _options,\n uint16 _index,\n uint128 _gas,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzComposeOption(_index, _gas, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZCOMPOSE, option);\n }\n\n /**\n * @dev Adds an executor ordered execution option to the existing options.\n * @param _options The existing options container.\n * @return options The updated options container.\n */\n function addExecutorOrderedExecutionOption(\n bytes memory _options\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_ORDERED_EXECUTION, bytes(\"\"));\n }\n\n /**\n * @dev Adds a DVN pre-crime option to the existing options.\n * @param _options The existing options container.\n * @param _dvnIdx The DVN index for the pre-crime option.\n * @return options The updated options container.\n */\n function addDVNPreCrimeOption(\n bytes memory _options,\n uint8 _dvnIdx\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return addDVNOption(_options, _dvnIdx, DVNOptions.OPTION_TYPE_PRECRIME, bytes(\"\"));\n }\n\n /**\n * @dev Adds an executor option to the existing options.\n * @param _options The existing options container.\n * @param _optionType The type of the executor option.\n * @param _option The encoded data for the executor option.\n * @return options The updated options container.\n */\n function addExecutorOption(\n bytes memory _options,\n uint8 _optionType,\n bytes memory _option\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return\n abi.encodePacked(\n _options,\n ExecutorOptions.WORKER_ID,\n _option.length.toUint16() + 1, // +1 for optionType\n _optionType,\n _option\n );\n }\n\n /**\n * @dev Adds a DVN option to the existing options.\n * @param _options The existing options container.\n * @param _dvnIdx The DVN index for the DVN option.\n * @param _optionType The type of the DVN option.\n * @param _option The encoded data for the DVN option.\n * @return options The updated options container.\n */\n function addDVNOption(\n bytes memory _options,\n uint8 _dvnIdx,\n uint8 _optionType,\n bytes memory _option\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return\n abi.encodePacked(\n _options,\n DVNOptions.WORKER_ID,\n _option.length.toUint16() + 2, // +2 for optionType and dvnIdx\n _dvnIdx,\n _optionType,\n _option\n );\n }\n\n /**\n * @dev Encodes legacy options of type 1.\n * @param _executionGas The gasLimit value passed to lzReceive().\n * @return legacyOptions The encoded legacy options.\n */\n function encodeLegacyOptionsType1(uint256 _executionGas) internal pure returns (bytes memory) {\n if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);\n return abi.encodePacked(TYPE_1, _executionGas);\n }\n\n /**\n * @dev Encodes legacy options of type 2.\n * @param _executionGas The gasLimit value passed to lzReceive().\n * @param _nativeForDst The amount of native air dropped to the receiver.\n * @param _receiver The _nativeForDst receiver address.\n * @return legacyOptions The encoded legacy options of type 2.\n */\n function encodeLegacyOptionsType2(\n uint256 _executionGas,\n uint256 _nativeForDst,\n bytes memory _receiver // @dev Use bytes instead of bytes32 in legacy type 2 for _receiver.\n ) internal pure returns (bytes memory) {\n if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);\n if (_nativeForDst > type(uint128).max) revert InvalidSize(type(uint128).max, _nativeForDst);\n if (_receiver.length > 32) revert InvalidSize(32, _receiver.length);\n return abi.encodePacked(TYPE_2, _executionGas, _nativeForDst, _receiver);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n// @dev Import the 'MessagingFee' and 'MessagingReceipt' so it's exposed to OApp implementers\n// solhint-disable-next-line no-unused-import\nimport { OAppSender, MessagingFee, MessagingReceipt } from \"./OAppSender.sol\";\n// @dev Import the 'Origin' so it's exposed to OApp implementers\n// solhint-disable-next-line no-unused-import\nimport { OAppReceiver, Origin } from \"./OAppReceiver.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OApp\n * @dev Abstract contract serving as the base for OApp implementation, combining OAppSender and OAppReceiver functionality.\n */\nabstract contract OApp is OAppSender, OAppReceiver {\n /**\n * @dev Constructor to initialize the OApp with the provided endpoint and owner.\n * @param _endpoint The address of the LOCAL LayerZero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(address _endpoint, address _delegate) OAppCore(_endpoint, _delegate) {}\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol implementation.\n * @return receiverVersion The version of the OAppReceiver.sol implementation.\n */\n function oAppVersion()\n public\n pure\n virtual\n override(OAppSender, OAppReceiver)\n returns (uint64 senderVersion, uint64 receiverVersion)\n {\n return (SENDER_VERSION, RECEIVER_VERSION);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IOAppCore, ILayerZeroEndpointV2 } from \"./interfaces/IOAppCore.sol\";\n\n/**\n * @title OAppCore\n * @dev Abstract contract implementing the IOAppCore interface with basic OApp configurations.\n */\nabstract contract OAppCore is IOAppCore, Ownable {\n // The LayerZero endpoint associated with the given OApp\n ILayerZeroEndpointV2 public immutable endpoint;\n\n // Mapping to store peers associated with corresponding endpoints\n mapping(uint32 eid => bytes32 peer) public peers;\n\n /**\n * @dev Constructor to initialize the OAppCore with the provided endpoint and delegate.\n * @param _endpoint The address of the LOCAL Layer Zero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n *\n * @dev The delegate typically should be set as the owner of the contract.\n */\n constructor(address _endpoint, address _delegate) {\n endpoint = ILayerZeroEndpointV2(_endpoint);\n\n if (_delegate == address(0)) revert InvalidDelegate();\n endpoint.setDelegate(_delegate);\n }\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.\n * @dev Set this to bytes32(0) to remove the peer address.\n * @dev Peer is a bytes32 to accommodate non-evm chains.\n */\n function setPeer(uint32 _eid, bytes32 _peer) public virtual onlyOwner {\n _setPeer(_eid, _peer);\n }\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n *\n * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.\n * @dev Set this to bytes32(0) to remove the peer address.\n * @dev Peer is a bytes32 to accommodate non-evm chains.\n */\n function _setPeer(uint32 _eid, bytes32 _peer) internal virtual {\n peers[_eid] = _peer;\n emit PeerSet(_eid, _peer);\n }\n\n /**\n * @notice Internal function to get the peer address associated with a specific endpoint; reverts if NOT set.\n * ie. the peer is set to bytes32(0).\n * @param _eid The endpoint ID.\n * @return peer The address of the peer associated with the specified endpoint.\n */\n function _getPeerOrRevert(uint32 _eid) internal view virtual returns (bytes32) {\n bytes32 peer = peers[_eid];\n if (peer == bytes32(0)) revert NoPeer(_eid);\n return peer;\n }\n\n /**\n * @notice Sets the delegate address for the OApp.\n * @param _delegate The address of the delegate to be set.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Provides the ability for a delegate to set configs, on behalf of the OApp, directly on the Endpoint contract.\n */\n function setDelegate(address _delegate) public onlyOwner {\n endpoint.setDelegate(_delegate);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IOAppReceiver, Origin } from \"./interfaces/IOAppReceiver.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OAppReceiver\n * @dev Abstract contract implementing the ILayerZeroReceiver interface and extending OAppCore for OApp receivers.\n */\nabstract contract OAppReceiver is IOAppReceiver, OAppCore {\n // Custom error message for when the caller is not the registered endpoint/\n error OnlyEndpoint(address addr);\n\n // @dev The version of the OAppReceiver implementation.\n // @dev Version is bumped when changes are made to this contract.\n uint64 internal constant RECEIVER_VERSION = 2;\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n *\n * @dev Providing 0 as the default for OAppSender version. Indicates that the OAppSender is not implemented.\n * ie. this is a RECEIVE only OApp.\n * @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions.\n */\n function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {\n return (0, RECEIVER_VERSION);\n }\n\n /**\n * @notice Indicates whether an address is an approved composeMsg sender to the Endpoint.\n * @dev _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @dev _message The lzReceive payload.\n * @param _sender The sender address.\n * @return isSender Is a valid sender.\n *\n * @dev Applications can optionally choose to implement separate composeMsg senders that are NOT the bridging layer.\n * @dev The default sender IS the OAppReceiver implementer.\n */\n function isComposeMsgSender(\n Origin calldata /*_origin*/,\n bytes calldata /*_message*/,\n address _sender\n ) public view virtual returns (bool) {\n return _sender == address(this);\n }\n\n /**\n * @notice Checks if the path initialization is allowed based on the provided origin.\n * @param origin The origin information containing the source endpoint and sender address.\n * @return Whether the path has been initialized.\n *\n * @dev This indicates to the endpoint that the OApp has enabled msgs for this particular path to be received.\n * @dev This defaults to assuming if a peer has been set, its initialized.\n * Can be overridden by the OApp if there is other logic to determine this.\n */\n function allowInitializePath(Origin calldata origin) public view virtual returns (bool) {\n return peers[origin.srcEid] == origin.sender;\n }\n\n /**\n * @notice Retrieves the next nonce for a given source endpoint and sender address.\n * @dev _srcEid The source endpoint ID.\n * @dev _sender The sender address.\n * @return nonce The next nonce.\n *\n * @dev The path nonce starts from 1. If 0 is returned it means that there is NO nonce ordered enforcement.\n * @dev Is required by the off-chain executor to determine the OApp expects msg execution is ordered.\n * @dev This is also enforced by the OApp.\n * @dev By default this is NOT enabled. ie. nextNonce is hardcoded to return 0.\n */\n function nextNonce(uint32 /*_srcEid*/, bytes32 /*_sender*/) public view virtual returns (uint64 nonce) {\n return 0;\n }\n\n /**\n * @dev Entry point for receiving messages or packets from the endpoint.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The payload of the received message.\n * @param _executor The address of the executor for the received message.\n * @param _extraData Additional arbitrary data provided by the corresponding executor.\n *\n * @dev Entry point for receiving msg/packet from the LayerZero endpoint.\n */\n function lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) public payable virtual {\n // Ensures that only the endpoint can attempt to lzReceive() messages to this OApp.\n if (address(endpoint) != msg.sender) revert OnlyEndpoint(msg.sender);\n\n // Ensure that the sender matches the expected peer for the source endpoint.\n if (_getPeerOrRevert(_origin.srcEid) != _origin.sender) revert OnlyPeer(_origin.srcEid, _origin.sender);\n\n // Call the internal OApp implementation of lzReceive.\n _lzReceive(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Internal function to implement lzReceive logic without needing to copy the basic parameter validation.\n */\n function _lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual;\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { SafeERC20, IERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { MessagingParams, MessagingFee, MessagingReceipt } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OAppSender\n * @dev Abstract contract implementing the OAppSender functionality for sending messages to a LayerZero endpoint.\n */\nabstract contract OAppSender is OAppCore {\n using SafeERC20 for IERC20;\n\n // Custom error messages\n error NotEnoughNative(uint256 msgValue);\n error LzTokenUnavailable();\n\n // @dev The version of the OAppSender implementation.\n // @dev Version is bumped when changes are made to this contract.\n uint64 internal constant SENDER_VERSION = 1;\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n *\n * @dev Providing 0 as the default for OAppReceiver version. Indicates that the OAppReceiver is not implemented.\n * ie. this is a SEND only OApp.\n * @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions\n */\n function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {\n return (SENDER_VERSION, 0);\n }\n\n /**\n * @dev Internal function to interact with the LayerZero EndpointV2.quote() for fee calculation.\n * @param _dstEid The destination endpoint ID.\n * @param _message The message payload.\n * @param _options Additional options for the message.\n * @param _payInLzToken Flag indicating whether to pay the fee in LZ tokens.\n * @return fee The calculated MessagingFee for the message.\n * - nativeFee: The native fee for the message.\n * - lzTokenFee: The LZ token fee for the message.\n */\n function _quote(\n uint32 _dstEid,\n bytes memory _message,\n bytes memory _options,\n bool _payInLzToken\n ) internal view virtual returns (MessagingFee memory fee) {\n return\n endpoint.quote(\n MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _payInLzToken),\n address(this)\n );\n }\n\n /**\n * @dev Internal function to interact with the LayerZero EndpointV2.send() for sending a message.\n * @param _dstEid The destination endpoint ID.\n * @param _message The message payload.\n * @param _options Additional options for the message.\n * @param _fee The calculated LayerZero fee for the message.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess fee values sent to the endpoint.\n * @return receipt The receipt for the sent message.\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function _lzSend(\n uint32 _dstEid,\n bytes memory _message,\n bytes memory _options,\n MessagingFee memory _fee,\n address _refundAddress\n ) internal virtual returns (MessagingReceipt memory receipt) {\n // @dev Push corresponding fees to the endpoint, any excess is sent back to the _refundAddress from the endpoint.\n uint256 messageValue = _payNative(_fee.nativeFee);\n if (_fee.lzTokenFee > 0) _payLzToken(_fee.lzTokenFee);\n\n return\n // solhint-disable-next-line check-send-result\n endpoint.send{ value: messageValue }(\n MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _fee.lzTokenFee > 0),\n _refundAddress\n );\n }\n\n /**\n * @dev Internal function to pay the native fee associated with the message.\n * @param _nativeFee The native fee to be paid.\n * @return nativeFee The amount of native currency paid.\n *\n * @dev If the OApp needs to initiate MULTIPLE LayerZero messages in a single transaction,\n * this will need to be overridden because msg.value would contain multiple lzFees.\n * @dev Should be overridden in the event the LayerZero endpoint requires a different native currency.\n * @dev Some EVMs use an ERC20 as a method for paying transactions/gasFees.\n * @dev The endpoint is EITHER/OR, ie. it will NOT support both types of native payment at a time.\n */\n function _payNative(uint256 _nativeFee) internal virtual returns (uint256 nativeFee) {\n if (msg.value != _nativeFee) revert NotEnoughNative(msg.value);\n return _nativeFee;\n }\n\n /**\n * @dev Internal function to pay the LZ token fee associated with the message.\n * @param _lzTokenFee The LZ token fee to be paid.\n *\n * @dev If the caller is trying to pay in the specified lzToken, then the lzTokenFee is passed to the endpoint.\n * @dev Any excess sent, is passed back to the specified _refundAddress in the _lzSend().\n */\n function _payLzToken(uint256 _lzTokenFee) internal virtual {\n // @dev Cannot cache the token because it is not immutable in the endpoint.\n address lzToken = endpoint.lzToken();\n if (lzToken == address(0)) revert LzTokenUnavailable();\n\n // Pay LZ token fee by sending tokens to the endpoint.\n IERC20(lzToken).safeTransferFrom(msg.sender, address(endpoint), _lzTokenFee);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/interfaces/IOAppPreCrimeSimulator.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n// @dev Import the Origin so it's exposed to OAppPreCrimeSimulator implementers.\n// solhint-disable-next-line no-unused-import\nimport { InboundPacket, Origin } from \"../libs/Packet.sol\";\n\n/**\n * @title IOAppPreCrimeSimulator Interface\n * @dev Interface for the preCrime simulation functionality in an OApp.\n */\ninterface IOAppPreCrimeSimulator {\n // @dev simulation result used in PreCrime implementation\n error SimulationResult(bytes result);\n error OnlySelf();\n\n /**\n * @dev Emitted when the preCrime contract address is set.\n * @param preCrimeAddress The address of the preCrime contract.\n */\n event PreCrimeSet(address preCrimeAddress);\n\n /**\n * @dev Retrieves the address of the preCrime contract implementation.\n * @return The address of the preCrime contract.\n */\n function preCrime() external view returns (address);\n\n /**\n * @dev Retrieves the address of the OApp contract.\n * @return The address of the OApp contract.\n */\n function oApp() external view returns (address);\n\n /**\n * @dev Sets the preCrime contract address.\n * @param _preCrime The address of the preCrime contract.\n */\n function setPreCrime(address _preCrime) external;\n\n /**\n * @dev Mocks receiving a packet, then reverts with a series of data to infer the state/result.\n * @param _packets An array of LayerZero InboundPacket objects representing received packets.\n */\n function lzReceiveAndRevert(InboundPacket[] calldata _packets) external payable;\n\n /**\n * @dev checks if the specified peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint Id to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n */\n function isPeer(uint32 _eid, bytes32 _peer) external view returns (bool);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/interfaces/IPreCrime.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\nstruct PreCrimePeer {\n uint32 eid;\n bytes32 preCrime;\n bytes32 oApp;\n}\n\n// TODO not done yet\ninterface IPreCrime {\n error OnlyOffChain();\n\n // for simulate()\n error PacketOversize(uint256 max, uint256 actual);\n error PacketUnsorted();\n error SimulationFailed(bytes reason);\n\n // for preCrime()\n error SimulationResultNotFound(uint32 eid);\n error InvalidSimulationResult(uint32 eid, bytes reason);\n error CrimeFound(bytes crime);\n\n function getConfig(bytes[] calldata _packets, uint256[] calldata _packetMsgValues) external returns (bytes memory);\n\n function simulate(\n bytes[] calldata _packets,\n uint256[] calldata _packetMsgValues\n ) external payable returns (bytes memory);\n\n function buildSimulationResult() external view returns (bytes memory);\n\n function preCrime(\n bytes[] calldata _packets,\n uint256[] calldata _packetMsgValues,\n bytes[] calldata _simulations\n ) external;\n\n function version() external view returns (uint64 major, uint8 minor);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/libs/Packet.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Origin } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\nimport { PacketV1Codec } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol\";\n\n/**\n * @title InboundPacket\n * @dev Structure representing an inbound packet received by the contract.\n */\nstruct InboundPacket {\n Origin origin; // Origin information of the packet.\n uint32 dstEid; // Destination endpointId of the packet.\n address receiver; // Receiver address for the packet.\n bytes32 guid; // Unique identifier of the packet.\n uint256 value; // msg.value of the packet.\n address executor; // Executor address for the packet.\n bytes message; // Message payload of the packet.\n bytes extraData; // Additional arbitrary data for the packet.\n}\n\n/**\n * @title PacketDecoder\n * @dev Library for decoding LayerZero packets.\n */\nlibrary PacketDecoder {\n using PacketV1Codec for bytes;\n\n /**\n * @dev Decode an inbound packet from the given packet data.\n * @param _packet The packet data to decode.\n * @return packet An InboundPacket struct representing the decoded packet.\n */\n function decode(bytes calldata _packet) internal pure returns (InboundPacket memory packet) {\n packet.origin = Origin(_packet.srcEid(), _packet.sender(), _packet.nonce());\n packet.dstEid = _packet.dstEid();\n packet.receiver = _packet.receiverB20();\n packet.guid = _packet.guid();\n packet.message = _packet.message();\n }\n\n /**\n * @dev Decode multiple inbound packets from the given packet data and associated message values.\n * @param _packets An array of packet data to decode.\n * @param _packetMsgValues An array of associated message values for each packet.\n * @return packets An array of InboundPacket structs representing the decoded packets.\n */\n function decode(\n bytes[] calldata _packets,\n uint256[] memory _packetMsgValues\n ) internal pure returns (InboundPacket[] memory packets) {\n packets = new InboundPacket[](_packets.length);\n for (uint256 i = 0; i < _packets.length; i++) {\n bytes calldata packet = _packets[i];\n packets[i] = PacketDecoder.decode(packet);\n // @dev Allows the verifier to specify the msg.value that gets passed in lzReceive.\n packets[i].value = _packetMsgValues[i];\n }\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/OAppPreCrimeSimulator.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IPreCrime } from \"./interfaces/IPreCrime.sol\";\nimport { IOAppPreCrimeSimulator, InboundPacket, Origin } from \"./interfaces/IOAppPreCrimeSimulator.sol\";\n\n/**\n * @title OAppPreCrimeSimulator\n * @dev Abstract contract serving as the base for preCrime simulation functionality in an OApp.\n */\nabstract contract OAppPreCrimeSimulator is IOAppPreCrimeSimulator, Ownable {\n // The address of the preCrime implementation.\n address public preCrime;\n\n /**\n * @dev Retrieves the address of the OApp contract.\n * @return The address of the OApp contract.\n *\n * @dev The simulator contract is the base contract for the OApp by default.\n * @dev If the simulator is a separate contract, override this function.\n */\n function oApp() external view virtual returns (address) {\n return address(this);\n }\n\n /**\n * @dev Sets the preCrime contract address.\n * @param _preCrime The address of the preCrime contract.\n */\n function setPreCrime(address _preCrime) public virtual onlyOwner {\n preCrime = _preCrime;\n emit PreCrimeSet(_preCrime);\n }\n\n /**\n * @dev Interface for pre-crime simulations. Always reverts at the end with the simulation results.\n * @param _packets An array of InboundPacket objects representing received packets to be delivered.\n *\n * @dev WARNING: MUST revert at the end with the simulation results.\n * @dev Gives the preCrime implementation the ability to mock sending packets to the lzReceive function,\n * WITHOUT actually executing them.\n */\n function lzReceiveAndRevert(InboundPacket[] calldata _packets) public payable virtual {\n for (uint256 i = 0; i < _packets.length; i++) {\n InboundPacket calldata packet = _packets[i];\n\n // Ignore packets that are not from trusted peers.\n if (!isPeer(packet.origin.srcEid, packet.origin.sender)) continue;\n\n // @dev Because a verifier is calling this function, it doesnt have access to executor params:\n // - address _executor\n // - bytes calldata _extraData\n // preCrime will NOT work for OApps that rely on these two parameters inside of their _lzReceive().\n // They are instead stubbed to default values, address(0) and bytes(\"\")\n // @dev Calling this.lzReceiveSimulate removes ability for assembly return 0 callstack exit,\n // which would cause the revert to be ignored.\n this.lzReceiveSimulate{ value: packet.value }(\n packet.origin,\n packet.guid,\n packet.message,\n packet.executor,\n packet.extraData\n );\n }\n\n // @dev Revert with the simulation results. msg.sender must implement IPreCrime.buildSimulationResult().\n revert SimulationResult(IPreCrime(msg.sender).buildSimulationResult());\n }\n\n /**\n * @dev Is effectively an internal function because msg.sender must be address(this).\n * Allows resetting the call stack for 'internal' calls.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _guid The unique identifier of the packet.\n * @param _message The message payload of the packet.\n * @param _executor The executor address for the packet.\n * @param _extraData Additional data for the packet.\n */\n function lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) external payable virtual {\n // @dev Ensure ONLY can be called 'internally'.\n if (msg.sender != address(this)) revert OnlySelf();\n _lzReceiveSimulate(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Internal function to handle the OAppPreCrimeSimulator simulated receive.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The GUID of the LayerZero message.\n * @param _message The LayerZero message.\n * @param _executor The address of the off-chain executor.\n * @param _extraData Arbitrary data passed by the msg executor.\n *\n * @dev Enables the preCrime simulator to mock sending lzReceive() messages,\n * routes the msg down from the OAppPreCrimeSimulator, and back up to the OAppReceiver.\n */\n function _lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual;\n\n /**\n * @dev checks if the specified peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint Id to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n */\n function isPeer(uint32 _eid, bytes32 _peer) public view virtual returns (bool);\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/interfaces/IMintableBurnable.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\n/// @title Interface for mintable and burnable tokens\ninterface IMintableBurnable {\n \n /**\n * @notice Burns tokens from a specified account\n * @param _from Address from which tokens will be burned\n * @param _amount Amount of tokens to be burned\n * @return success Indicates whether the operation was successful\n */\n function burn(address _from, uint256 _amount) external returns (bool success);\n\n /**\n * @notice Mints tokens to a specified account\n * @param _to Address to which tokens will be minted\n * @param _amount Amount of tokens to be minted\n * @return success Indicates whether the operation was successful\n */\n function mint(address _to, uint256 _amount) external returns (bool success);\n}" + }, + "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { MessagingReceipt, MessagingFee } from \"@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol\";\n\n/**\n * @dev Struct representing token parameters for the OFT send() operation.\n */\nstruct SendParam {\n uint32 dstEid; // Destination endpoint ID.\n bytes32 to; // Recipient address.\n uint256 amountLD; // Amount to send in local decimals.\n uint256 minAmountLD; // Minimum amount to send in local decimals.\n bytes extraOptions; // Additional options supplied by the caller to be used in the LayerZero message.\n bytes composeMsg; // The composed message for the send() operation.\n bytes oftCmd; // The OFT command to be executed, unused in default OFT implementations.\n}\n\n/**\n * @dev Struct representing OFT limit information.\n * @dev These amounts can change dynamically and are up the specific oft implementation.\n */\nstruct OFTLimit {\n uint256 minAmountLD; // Minimum amount in local decimals that can be sent to the recipient.\n uint256 maxAmountLD; // Maximum amount in local decimals that can be sent to the recipient.\n}\n\n/**\n * @dev Struct representing OFT receipt information.\n */\nstruct OFTReceipt {\n uint256 amountSentLD; // Amount of tokens ACTUALLY debited from the sender in local decimals.\n // @dev In non-default implementations, the amountReceivedLD COULD differ from this value.\n uint256 amountReceivedLD; // Amount of tokens to be received on the remote side.\n}\n\n/**\n * @dev Struct representing OFT fee details.\n * @dev Future proof mechanism to provide a standardized way to communicate fees to things like a UI.\n */\nstruct OFTFeeDetail {\n int256 feeAmountLD; // Amount of the fee in local decimals.\n string description; // Description of the fee.\n}\n\n/**\n * @title IOFT\n * @dev Interface for the OftChain (OFT) token.\n * @dev Does not inherit ERC20 to accommodate usage by OFTAdapter as well.\n * @dev This specific interface ID is '0x02e49c2c'.\n */\ninterface IOFT {\n // Custom error messages\n error InvalidLocalDecimals();\n error SlippageExceeded(uint256 amountLD, uint256 minAmountLD);\n\n // Events\n event OFTSent(\n bytes32 indexed guid, // GUID of the OFT message.\n uint32 dstEid, // Destination Endpoint ID.\n address indexed fromAddress, // Address of the sender on the src chain.\n uint256 amountSentLD, // Amount of tokens sent in local decimals.\n uint256 amountReceivedLD // Amount of tokens received in local decimals.\n );\n event OFTReceived(\n bytes32 indexed guid, // GUID of the OFT message.\n uint32 srcEid, // Source Endpoint ID.\n address indexed toAddress, // Address of the recipient on the dst chain.\n uint256 amountReceivedLD // Amount of tokens received in local decimals.\n );\n\n /**\n * @notice Retrieves interfaceID and the version of the OFT.\n * @return interfaceId The interface ID.\n * @return version The version.\n *\n * @dev interfaceId: This specific interface ID is '0x02e49c2c'.\n * @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.\n * @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.\n * ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)\n */\n function oftVersion() external view returns (bytes4 interfaceId, uint64 version);\n\n /**\n * @notice Retrieves the address of the token associated with the OFT.\n * @return token The address of the ERC20 token implementation.\n */\n function token() external view returns (address);\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the 'token()' to send.\n * @return requiresApproval Needs approval of the underlying token implementation.\n *\n * @dev Allows things like wallet implementers to determine integration requirements,\n * without understanding the underlying token implementation.\n */\n function approvalRequired() external view returns (bool);\n\n /**\n * @notice Retrieves the shared decimals of the OFT.\n * @return sharedDecimals The shared decimals of the OFT.\n */\n function sharedDecimals() external view returns (uint8);\n\n /**\n * @notice Provides the fee breakdown and settings data for an OFT. Unused in the default implementation.\n * @param _sendParam The parameters for the send operation.\n * @return limit The OFT limit information.\n * @return oftFeeDetails The details of OFT fees.\n * @return receipt The OFT receipt information.\n */\n function quoteOFT(\n SendParam calldata _sendParam\n ) external view returns (OFTLimit memory, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory);\n\n /**\n * @notice Provides a quote for the send() operation.\n * @param _sendParam The parameters for the send() operation.\n * @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.\n * @return fee The calculated LayerZero messaging fee from the send() operation.\n *\n * @dev MessagingFee: LayerZero msg fee\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n */\n function quoteSend(SendParam calldata _sendParam, bool _payInLzToken) external view returns (MessagingFee memory);\n\n /**\n * @notice Executes the send() operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The fee information supplied by the caller.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds from fees etc. on the src.\n * @return receipt The LayerZero messaging receipt from the send() operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) external payable returns (MessagingReceipt memory, OFTReceipt memory);\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/libs/OFTComposeMsgCodec.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nlibrary OFTComposeMsgCodec {\n // Offset constants for decoding composed messages\n uint8 private constant NONCE_OFFSET = 8;\n uint8 private constant SRC_EID_OFFSET = 12;\n uint8 private constant AMOUNT_LD_OFFSET = 44;\n uint8 private constant COMPOSE_FROM_OFFSET = 76;\n\n /**\n * @dev Encodes a OFT composed message.\n * @param _nonce The nonce value.\n * @param _srcEid The source endpoint ID.\n * @param _amountLD The amount in local decimals.\n * @param _composeMsg The composed message.\n * @return _msg The encoded Composed message.\n */\n function encode(\n uint64 _nonce,\n uint32 _srcEid,\n uint256 _amountLD,\n bytes memory _composeMsg // 0x[composeFrom][composeMsg]\n ) internal pure returns (bytes memory _msg) {\n _msg = abi.encodePacked(_nonce, _srcEid, _amountLD, _composeMsg);\n }\n\n /**\n * @dev Retrieves the nonce for the composed message.\n * @param _msg The message.\n * @return The nonce value.\n */\n function nonce(bytes calldata _msg) internal pure returns (uint64) {\n return uint64(bytes8(_msg[:NONCE_OFFSET]));\n }\n\n /**\n * @dev Retrieves the source endpoint ID for the composed message.\n * @param _msg The message.\n * @return The source endpoint ID.\n */\n function srcEid(bytes calldata _msg) internal pure returns (uint32) {\n return uint32(bytes4(_msg[NONCE_OFFSET:SRC_EID_OFFSET]));\n }\n\n /**\n * @dev Retrieves the amount in local decimals from the composed message.\n * @param _msg The message.\n * @return The amount in local decimals.\n */\n function amountLD(bytes calldata _msg) internal pure returns (uint256) {\n return uint256(bytes32(_msg[SRC_EID_OFFSET:AMOUNT_LD_OFFSET]));\n }\n\n /**\n * @dev Retrieves the composeFrom value from the composed message.\n * @param _msg The message.\n * @return The composeFrom value.\n */\n function composeFrom(bytes calldata _msg) internal pure returns (bytes32) {\n return bytes32(_msg[AMOUNT_LD_OFFSET:COMPOSE_FROM_OFFSET]);\n }\n\n /**\n * @dev Retrieves the composed message.\n * @param _msg The message.\n * @return The composed message.\n */\n function composeMsg(bytes calldata _msg) internal pure returns (bytes memory) {\n return _msg[COMPOSE_FROM_OFFSET:];\n }\n\n /**\n * @dev Converts an address to bytes32.\n * @param _addr The address to convert.\n * @return The bytes32 representation of the address.\n */\n function addressToBytes32(address _addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(_addr)));\n }\n\n /**\n * @dev Converts bytes32 to an address.\n * @param _b The bytes32 value to convert.\n * @return The address representation of bytes32.\n */\n function bytes32ToAddress(bytes32 _b) internal pure returns (address) {\n return address(uint160(uint256(_b)));\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/libs/OFTMsgCodec.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nlibrary OFTMsgCodec {\n // Offset constants for encoding and decoding OFT messages\n uint8 private constant SEND_TO_OFFSET = 32;\n uint8 private constant SEND_AMOUNT_SD_OFFSET = 40;\n\n /**\n * @dev Encodes an OFT LayerZero message.\n * @param _sendTo The recipient address.\n * @param _amountShared The amount in shared decimals.\n * @param _composeMsg The composed message.\n * @return _msg The encoded message.\n * @return hasCompose A boolean indicating whether the message has a composed payload.\n */\n function encode(\n bytes32 _sendTo,\n uint64 _amountShared,\n bytes memory _composeMsg\n ) internal view returns (bytes memory _msg, bool hasCompose) {\n hasCompose = _composeMsg.length > 0;\n // @dev Remote chains will want to know the composed function caller ie. msg.sender on the src.\n _msg = hasCompose\n ? abi.encodePacked(_sendTo, _amountShared, addressToBytes32(msg.sender), _composeMsg)\n : abi.encodePacked(_sendTo, _amountShared);\n }\n\n /**\n * @dev Checks if the OFT message is composed.\n * @param _msg The OFT message.\n * @return A boolean indicating whether the message is composed.\n */\n function isComposed(bytes calldata _msg) internal pure returns (bool) {\n return _msg.length > SEND_AMOUNT_SD_OFFSET;\n }\n\n /**\n * @dev Retrieves the recipient address from the OFT message.\n * @param _msg The OFT message.\n * @return The recipient address.\n */\n function sendTo(bytes calldata _msg) internal pure returns (bytes32) {\n return bytes32(_msg[:SEND_TO_OFFSET]);\n }\n\n /**\n * @dev Retrieves the amount in shared decimals from the OFT message.\n * @param _msg The OFT message.\n * @return The amount in shared decimals.\n */\n function amountSD(bytes calldata _msg) internal pure returns (uint64) {\n return uint64(bytes8(_msg[SEND_TO_OFFSET:SEND_AMOUNT_SD_OFFSET]));\n }\n\n /**\n * @dev Retrieves the composed message from the OFT message.\n * @param _msg The OFT message.\n * @return The composed message.\n */\n function composeMsg(bytes calldata _msg) internal pure returns (bytes memory) {\n return _msg[SEND_AMOUNT_SD_OFFSET:];\n }\n\n /**\n * @dev Converts an address to bytes32.\n * @param _addr The address to convert.\n * @return The bytes32 representation of the address.\n */\n function addressToBytes32(address _addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(_addr)));\n }\n\n /**\n * @dev Converts bytes32 to an address.\n * @param _b The bytes32 value to convert.\n * @return The address representation of bytes32.\n */\n function bytes32ToAddress(bytes32 _b) internal pure returns (address) {\n return address(uint160(uint256(_b)));\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/MintBurnOFTAdapter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\n// External imports\nimport { IERC20, IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\n// Local imports\nimport { IMintableBurnable } from \"./interfaces/IMintableBurnable.sol\";\nimport { OFTCore } from \"./OFTCore.sol\";\n\n/**\n * @title MintBurnOFTAdapter\n * @notice A variant of the standard OFT Adapter that uses an existing ERC20's mint and burn mechanisms for cross-chain transfers.\n *\n * @dev Inherits from OFTCore and provides implementations for _debit and _credit functions using a mintable and burnable token.\n */\nabstract contract MintBurnOFTAdapter is OFTCore {\n /// @dev The underlying ERC20 token.\n IERC20 internal immutable innerToken;\n\n /// @notice The contract responsible for minting and burning tokens.\n IMintableBurnable public immutable minterBurner;\n\n /**\n * @notice Initializes the MintBurnOFTAdapter contract.\n *\n * @param _token The address of the underlying ERC20 token.\n * @param _minterBurner The contract responsible for minting and burning tokens.\n * @param _lzEndpoint The LayerZero endpoint address.\n * @param _delegate The address of the delegate.\n *\n * @dev Calls the OFTCore constructor with the token's decimals, the endpoint, and the delegate.\n */\n constructor(\n address _token,\n IMintableBurnable _minterBurner,\n address _lzEndpoint,\n address _delegate\n ) OFTCore(IERC20Metadata(_token).decimals(), _lzEndpoint, _delegate) {\n innerToken = IERC20(_token);\n minterBurner = _minterBurner;\n }\n\n /**\n * @notice Retrieves the address of the underlying ERC20 token.\n *\n * @return The address of the adapted ERC20 token.\n *\n * @dev In the case of MintBurnOFTAdapter, address(this) and erc20 are NOT the same contract.\n */\n function token() public view returns (address) {\n return address(innerToken);\n }\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the underlying token to send.\n *\n * @return requiresApproval True if approval is required, false otherwise.\n *\n * @dev In this MintBurnOFTAdapter, approval is NOT required because it uses mint and burn privileges.\n */\n function approvalRequired() external pure virtual returns (bool) {\n return false;\n }\n\n /**\n * @notice Burns tokens from the sender's balance to prepare for sending.\n *\n * @param _from The address to debit the tokens from.\n * @param _amountLD The amount of tokens to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination chain ID.\n *\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, i.e., 1 token in, 1 token out.\n * If the 'innerToken' applies something like a transfer fee, the default will NOT work.\n * A pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual override returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n (amountSentLD, amountReceivedLD) = _debitView(_amountLD, _minAmountLD, _dstEid);\n // Burns tokens from the caller.\n minterBurner.burn(_from, amountSentLD);\n }\n\n /**\n * @notice Mints tokens to the specified address upon receiving them.\n *\n * @param _to The address to credit the tokens to.\n * @param _amountLD The amount of tokens to credit in local decimals.\n *\n * @return amountReceivedLD The amount of tokens actually received in local decimals.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, i.e., 1 token in, 1 token out.\n * If the 'innerToken' applies something like a transfer fee, the default will NOT work.\n * A pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /* _srcEid */\n ) internal virtual override returns (uint256 amountReceivedLD) {\n if (_to == address(0x0)) _to = address(0xdead); // _mint(...) does not support address(0x0)\n // Mints the tokens and transfers to the recipient.\n minterBurner.mint(_to, _amountLD);\n // In the case of NON-default OFTAdapter, the amountLD MIGHT not be equal to amountReceivedLD.\n return _amountLD;\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/OFTAdapter.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IERC20Metadata, IERC20 } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IOFT, OFTCore } from \"./OFTCore.sol\";\n\n/**\n * @title OFTAdapter Contract\n * @dev OFTAdapter is a contract that adapts an ERC-20 token to the OFT functionality.\n *\n * @dev For existing ERC20 tokens, this can be used to convert the token to crosschain compatibility.\n * @dev WARNING: ONLY 1 of these should exist for a given global mesh,\n * unless you make a NON-default implementation of OFT and needs to be done very carefully.\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountSentLD/amountReceivedLD.\n */\nabstract contract OFTAdapter is OFTCore {\n using SafeERC20 for IERC20;\n\n IERC20 internal immutable innerToken;\n\n /**\n * @dev Constructor for the OFTAdapter contract.\n * @param _token The address of the ERC-20 token to be adapted.\n * @param _lzEndpoint The LayerZero endpoint address.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(\n address _token,\n address _lzEndpoint,\n address _delegate\n ) OFTCore(IERC20Metadata(_token).decimals(), _lzEndpoint, _delegate) {\n innerToken = IERC20(_token);\n }\n\n /**\n * @dev Retrieves the address of the underlying ERC20 implementation.\n * @return The address of the adapted ERC-20 token.\n *\n * @dev In the case of OFTAdapter, address(this) and erc20 are NOT the same contract.\n */\n function token() public view returns (address) {\n return address(innerToken);\n }\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the 'token()' to send.\n * @return requiresApproval Needs approval of the underlying token implementation.\n *\n * @dev In the case of default OFTAdapter, approval is required.\n * @dev In non-default OFTAdapter contracts with something like mint and burn privileges, it would NOT need approval.\n */\n function approvalRequired() external pure virtual returns (bool) {\n return true;\n }\n\n /**\n * @dev Locks tokens from the sender's specified balance in this contract.\n * @param _from The address to debit from.\n * @param _amountLD The amount of tokens to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination chain ID.\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev msg.sender will need to approve this _amountLD of tokens to be locked inside of the contract.\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual override returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n (amountSentLD, amountReceivedLD) = _debitView(_amountLD, _minAmountLD, _dstEid);\n // @dev Lock tokens by moving them into this contract from the caller.\n innerToken.safeTransferFrom(_from, address(this), amountSentLD);\n }\n\n /**\n * @dev Credits tokens to the specified address.\n * @param _to The address to credit the tokens to.\n * @param _amountLD The amount of tokens to credit in local decimals.\n * @dev _srcEid The source chain ID.\n * @return amountReceivedLD The amount of tokens ACTUALLY received in local decimals.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /*_srcEid*/\n ) internal virtual override returns (uint256 amountReceivedLD) {\n // @dev Unlock the tokens and transfer to the recipient.\n innerToken.safeTransfer(_to, _amountLD);\n // @dev In the case of NON-default OFTAdapter, the amountLD MIGHT not be == amountReceivedLD.\n return _amountLD;\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/OFTCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { OApp, Origin } from \"@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol\";\nimport { OAppOptionsType3 } from \"@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol\";\nimport { IOAppMsgInspector } from \"@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppMsgInspector.sol\";\n\nimport { OAppPreCrimeSimulator } from \"@layerzerolabs/oapp-evm/contracts/precrime/OAppPreCrimeSimulator.sol\";\n\nimport { IOFT, SendParam, OFTLimit, OFTReceipt, OFTFeeDetail, MessagingReceipt, MessagingFee } from \"./interfaces/IOFT.sol\";\nimport { OFTMsgCodec } from \"./libs/OFTMsgCodec.sol\";\nimport { OFTComposeMsgCodec } from \"./libs/OFTComposeMsgCodec.sol\";\n\n/**\n * @title OFTCore\n * @dev Abstract contract for the OftChain (OFT) token.\n */\nabstract contract OFTCore is IOFT, OApp, OAppPreCrimeSimulator, OAppOptionsType3 {\n using OFTMsgCodec for bytes;\n using OFTMsgCodec for bytes32;\n\n // @notice Provides a conversion rate when swapping between denominations of SD and LD\n // - shareDecimals == SD == shared Decimals\n // - localDecimals == LD == local decimals\n // @dev Considers that tokens have different decimal amounts on various chains.\n // @dev eg.\n // For a token\n // - locally with 4 decimals --> 1.2345 => uint(12345)\n // - remotely with 2 decimals --> 1.23 => uint(123)\n // - The conversion rate would be 10 ** (4 - 2) = 100\n // @dev If you want to send 1.2345 -> (uint 12345), you CANNOT represent that value on the remote,\n // you can only display 1.23 -> uint(123).\n // @dev To preserve the dust that would otherwise be lost on that conversion,\n // we need to unify a denomination that can be represented on ALL chains inside of the OFT mesh\n uint256 public immutable decimalConversionRate;\n\n // @notice Msg types that are used to identify the various OFT operations.\n // @dev This can be extended in child contracts for non-default oft operations\n // @dev These values are used in things like combineOptions() in OAppOptionsType3.sol.\n uint16 public constant SEND = 1;\n uint16 public constant SEND_AND_CALL = 2;\n\n // Address of an optional contract to inspect both 'message' and 'options'\n address public msgInspector;\n event MsgInspectorSet(address inspector);\n\n /**\n * @dev Constructor.\n * @param _localDecimals The decimals of the token on the local chain (this chain).\n * @param _endpoint The address of the LayerZero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(uint8 _localDecimals, address _endpoint, address _delegate) OApp(_endpoint, _delegate) {\n if (_localDecimals < sharedDecimals()) revert InvalidLocalDecimals();\n decimalConversionRate = 10 ** (_localDecimals - sharedDecimals());\n }\n\n /**\n * @notice Retrieves interfaceID and the version of the OFT.\n * @return interfaceId The interface ID.\n * @return version The version.\n *\n * @dev interfaceId: This specific interface ID is '0x02e49c2c'.\n * @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.\n * @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.\n * ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)\n */\n function oftVersion() external pure virtual returns (bytes4 interfaceId, uint64 version) {\n return (type(IOFT).interfaceId, 1);\n }\n\n /**\n * @dev Retrieves the shared decimals of the OFT.\n * @return The shared decimals of the OFT.\n *\n * @dev Sets an implicit cap on the amount of tokens, over uint64.max() will need some sort of outbound cap / totalSupply cap\n * Lowest common decimal denominator between chains.\n * Defaults to 6 decimal places to provide up to 18,446,744,073,709.551615 units (max uint64).\n * For tokens exceeding this totalSupply(), they will need to override the sharedDecimals function with something smaller.\n * ie. 4 sharedDecimals would be 1,844,674,407,370,955.1615\n */\n function sharedDecimals() public view virtual returns (uint8) {\n return 6;\n }\n\n /**\n * @dev Sets the message inspector address for the OFT.\n * @param _msgInspector The address of the message inspector.\n *\n * @dev This is an optional contract that can be used to inspect both 'message' and 'options'.\n * @dev Set it to address(0) to disable it, or set it to a contract address to enable it.\n */\n function setMsgInspector(address _msgInspector) public virtual onlyOwner {\n msgInspector = _msgInspector;\n emit MsgInspectorSet(_msgInspector);\n }\n\n /**\n * @notice Provides the fee breakdown and settings data for an OFT. Unused in the default implementation.\n * @param _sendParam The parameters for the send operation.\n * @return oftLimit The OFT limit information.\n * @return oftFeeDetails The details of OFT fees.\n * @return oftReceipt The OFT receipt information.\n */\n function quoteOFT(\n SendParam calldata _sendParam\n )\n external\n view\n virtual\n returns (OFTLimit memory oftLimit, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory oftReceipt)\n {\n uint256 minAmountLD = 0; // Unused in the default implementation.\n uint256 maxAmountLD = IERC20(this.token()).totalSupply(); // Unused in the default implementation.\n oftLimit = OFTLimit(minAmountLD, maxAmountLD);\n\n // Unused in the default implementation; reserved for future complex fee details.\n oftFeeDetails = new OFTFeeDetail[](0);\n\n // @dev This is the same as the send() operation, but without the actual send.\n // - amountSentLD is the amount in local decimals that would be sent from the sender.\n // - amountReceivedLD is the amount in local decimals that will be credited to the recipient on the remote OFT instance.\n // @dev The amountSentLD MIGHT not equal the amount the user actually receives. HOWEVER, the default does.\n (uint256 amountSentLD, uint256 amountReceivedLD) = _debitView(\n _sendParam.amountLD,\n _sendParam.minAmountLD,\n _sendParam.dstEid\n );\n oftReceipt = OFTReceipt(amountSentLD, amountReceivedLD);\n }\n\n /**\n * @notice Provides a quote for the send() operation.\n * @param _sendParam The parameters for the send() operation.\n * @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.\n * @return msgFee The calculated LayerZero messaging fee from the send() operation.\n *\n * @dev MessagingFee: LayerZero msg fee\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n */\n function quoteSend(\n SendParam calldata _sendParam,\n bool _payInLzToken\n ) external view virtual returns (MessagingFee memory msgFee) {\n // @dev mock the amount to receive, this is the same operation used in the send().\n // The quote is as similar as possible to the actual send() operation.\n (, uint256 amountReceivedLD) = _debitView(_sendParam.amountLD, _sendParam.minAmountLD, _sendParam.dstEid);\n\n // @dev Builds the options and OFT message to quote in the endpoint.\n (bytes memory message, bytes memory options) = _buildMsgAndOptions(_sendParam, amountReceivedLD);\n\n // @dev Calculates the LayerZero fee for the send() operation.\n return _quote(_sendParam.dstEid, message, options, _payInLzToken);\n }\n\n /**\n * @dev Executes the send operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The calculated fee for the send() operation.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds.\n * @return msgReceipt The receipt for the send operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) external payable virtual returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) {\n return _send(_sendParam, _fee, _refundAddress);\n }\n\n /**\n * @dev Internal function to execute the send operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The calculated fee for the send() operation.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds.\n * @return msgReceipt The receipt for the send operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function _send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) internal virtual returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) {\n // @dev Applies the token transfers regarding this send() operation.\n // - amountSentLD is the amount in local decimals that was ACTUALLY sent/debited from the sender.\n // - amountReceivedLD is the amount in local decimals that will be received/credited to the recipient on the remote OFT instance.\n (uint256 amountSentLD, uint256 amountReceivedLD) = _debit(\n msg.sender,\n _sendParam.amountLD,\n _sendParam.minAmountLD,\n _sendParam.dstEid\n );\n\n // @dev Builds the options and OFT message to quote in the endpoint.\n (bytes memory message, bytes memory options) = _buildMsgAndOptions(_sendParam, amountReceivedLD);\n\n // @dev Sends the message to the LayerZero endpoint and returns the LayerZero msg receipt.\n msgReceipt = _lzSend(_sendParam.dstEid, message, options, _fee, _refundAddress);\n // @dev Formulate the OFT receipt.\n oftReceipt = OFTReceipt(amountSentLD, amountReceivedLD);\n\n emit OFTSent(msgReceipt.guid, _sendParam.dstEid, msg.sender, amountSentLD, amountReceivedLD);\n }\n\n /**\n * @dev Internal function to build the message and options.\n * @param _sendParam The parameters for the send() operation.\n * @param _amountLD The amount in local decimals.\n * @return message The encoded message.\n * @return options The encoded options.\n */\n function _buildMsgAndOptions(\n SendParam calldata _sendParam,\n uint256 _amountLD\n ) internal view virtual returns (bytes memory message, bytes memory options) {\n bool hasCompose;\n // @dev This generated message has the msg.sender encoded into the payload so the remote knows who the caller is.\n (message, hasCompose) = OFTMsgCodec.encode(\n _sendParam.to,\n _toSD(_amountLD),\n // @dev Must be include a non empty bytes if you want to compose, EVEN if you dont need it on the remote.\n // EVEN if you dont require an arbitrary payload to be sent... eg. '0x01'\n _sendParam.composeMsg\n );\n // @dev Change the msg type depending if its composed or not.\n uint16 msgType = hasCompose ? SEND_AND_CALL : SEND;\n // @dev Combine the callers _extraOptions with the enforced options via the OAppOptionsType3.\n options = combineOptions(_sendParam.dstEid, msgType, _sendParam.extraOptions);\n\n // @dev Optionally inspect the message and options depending if the OApp owner has set a msg inspector.\n // @dev If it fails inspection, needs to revert in the implementation. ie. does not rely on return boolean\n address inspector = msgInspector; // caches the msgInspector to avoid potential double storage read\n if (inspector != address(0)) IOAppMsgInspector(inspector).inspect(message, options);\n }\n\n /**\n * @dev Internal function to handle the receive on the LayerZero endpoint.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The encoded message.\n * @dev _executor The address of the executor.\n * @dev _extraData Additional data.\n */\n function _lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address /*_executor*/, // @dev unused in the default implementation.\n bytes calldata /*_extraData*/ // @dev unused in the default implementation.\n ) internal virtual override {\n // @dev The src sending chain doesnt know the address length on this chain (potentially non-evm)\n // Thus everything is bytes32() encoded in flight.\n address toAddress = _message.sendTo().bytes32ToAddress();\n // @dev Credit the amountLD to the recipient and return the ACTUAL amount the recipient received in local decimals\n uint256 amountReceivedLD = _credit(toAddress, _toLD(_message.amountSD()), _origin.srcEid);\n\n if (_message.isComposed()) {\n // @dev Proprietary composeMsg format for the OFT.\n bytes memory composeMsg = OFTComposeMsgCodec.encode(\n _origin.nonce,\n _origin.srcEid,\n amountReceivedLD,\n _message.composeMsg()\n );\n\n // @dev Stores the lzCompose payload that will be executed in a separate tx.\n // Standardizes functionality for executing arbitrary contract invocation on some non-evm chains.\n // @dev The off-chain executor will listen and process the msg based on the src-chain-callers compose options passed.\n // @dev The index is used when a OApp needs to compose multiple msgs on lzReceive.\n // For default OFT implementation there is only 1 compose msg per lzReceive, thus its always 0.\n endpoint.sendCompose(toAddress, _guid, 0 /* the index of the composed message*/, composeMsg);\n }\n\n emit OFTReceived(_guid, _origin.srcEid, toAddress, amountReceivedLD);\n }\n\n /**\n * @dev Internal function to handle the OAppPreCrimeSimulator simulated receive.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The LayerZero message.\n * @param _executor The address of the off-chain executor.\n * @param _extraData Arbitrary data passed by the msg executor.\n *\n * @dev Enables the preCrime simulator to mock sending lzReceive() messages,\n * routes the msg down from the OAppPreCrimeSimulator, and back up to the OAppReceiver.\n */\n function _lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual override {\n _lzReceive(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Check if the peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint ID to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n *\n * @dev Enables OAppPreCrimeSimulator to check whether a potential Inbound Packet is from a trusted source.\n */\n function isPeer(uint32 _eid, bytes32 _peer) public view virtual override returns (bool) {\n return peers[_eid] == _peer;\n }\n\n /**\n * @dev Internal function to remove dust from the given local decimal amount.\n * @param _amountLD The amount in local decimals.\n * @return amountLD The amount after removing dust.\n *\n * @dev Prevents the loss of dust when moving amounts between chains with different decimals.\n * @dev eg. uint(123) with a conversion rate of 100 becomes uint(100).\n */\n function _removeDust(uint256 _amountLD) internal view virtual returns (uint256 amountLD) {\n return (_amountLD / decimalConversionRate) * decimalConversionRate;\n }\n\n /**\n * @dev Internal function to convert an amount from shared decimals into local decimals.\n * @param _amountSD The amount in shared decimals.\n * @return amountLD The amount in local decimals.\n */\n function _toLD(uint64 _amountSD) internal view virtual returns (uint256 amountLD) {\n return _amountSD * decimalConversionRate;\n }\n\n /**\n * @dev Internal function to convert an amount from local decimals into shared decimals.\n * @param _amountLD The amount in local decimals.\n * @return amountSD The amount in shared decimals.\n */\n function _toSD(uint256 _amountLD) internal view virtual returns (uint64 amountSD) {\n return uint64(_amountLD / decimalConversionRate);\n }\n\n /**\n * @dev Internal function to mock the amount mutation from a OFT debit() operation.\n * @param _amountLD The amount to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @dev _dstEid The destination endpoint ID.\n * @return amountSentLD The amount sent, in local decimals.\n * @return amountReceivedLD The amount to be received on the remote chain, in local decimals.\n *\n * @dev This is where things like fees would be calculated and deducted from the amount to be received on the remote.\n */\n function _debitView(\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 /*_dstEid*/\n ) internal view virtual returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n // @dev Remove the dust so nothing is lost on the conversion between chains with different decimals for the token.\n amountSentLD = _removeDust(_amountLD);\n // @dev The amount to send is the same as amount received in the default implementation.\n amountReceivedLD = amountSentLD;\n\n // @dev Check for slippage.\n if (amountReceivedLD < _minAmountLD) {\n revert SlippageExceeded(amountReceivedLD, _minAmountLD);\n }\n }\n\n /**\n * @dev Internal function to perform a debit operation.\n * @param _from The address to debit.\n * @param _amountLD The amount to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination endpoint ID.\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev Defined here but are intended to be overriden depending on the OFT implementation.\n * @dev Depending on OFT implementation the _amountLD could differ from the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual returns (uint256 amountSentLD, uint256 amountReceivedLD);\n\n /**\n * @dev Internal function to perform a credit operation.\n * @param _to The address to credit.\n * @param _amountLD The amount to credit in local decimals.\n * @param _srcEid The source endpoint ID.\n * @return amountReceivedLD The amount ACTUALLY received in local decimals.\n *\n * @dev Defined here but are intended to be overriden depending on the OFT implementation.\n * @dev Depending on OFT implementation the _amountLD could differ from the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 _srcEid\n ) internal virtual returns (uint256 amountReceivedLD);\n}\n" + }, + "@openzeppelin/contracts/access/AccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/AccessControl.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\nimport \"../utils/Context.sol\";\nimport \"../utils/Strings.sol\";\nimport \"../utils/introspection/ERC165.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\n * members except through off-chain means by accessing the contract event logs. Some\n * applications may benefit from on-chain enumerability, for those cases see\n * {AccessControlEnumerable}.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```\n * function foo() public {\n * require(hasRole(MY_ROLE, msg.sender));\n * ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it.\n */\nabstract contract AccessControl is Context, IAccessControl, ERC165 {\n struct RoleData {\n mapping(address => bool) members;\n bytes32 adminRole;\n }\n\n mapping(bytes32 => RoleData) private _roles;\n\n bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n /**\n * @dev Modifier that checks that an account has a specific role. Reverts\n * with a standardized message including the required role.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n *\n * _Available since v4.1._\n */\n modifier onlyRole(bytes32 role) {\n _checkRole(role, _msgSender());\n _;\n }\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) public view override returns (bool) {\n return _roles[role].members[account];\n }\n\n /**\n * @dev Revert with a standard message if `account` is missing `role`.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n */\n function _checkRole(bytes32 role, address account) internal view {\n if (!hasRole(role, account)) {\n revert(\n string(\n abi.encodePacked(\n \"AccessControl: account \",\n Strings.toHexString(uint160(account), 20),\n \" is missing role \",\n Strings.toHexString(uint256(role), 32)\n )\n )\n );\n }\n }\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) public view override returns (bytes32) {\n return _roles[role].adminRole;\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _grantRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _revokeRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been revoked `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) public virtual override {\n require(account == _msgSender(), \"AccessControl: can only renounce roles for self\");\n\n _revokeRole(role, account);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event. Note that unlike {grantRole}, this function doesn't perform any\n * checks on the calling account.\n *\n * [WARNING]\n * ====\n * This function should only be called from the constructor when setting\n * up the initial roles for the system.\n *\n * Using this function in any other way is effectively circumventing the admin\n * system imposed by {AccessControl}.\n * ====\n *\n * NOTE: This function is deprecated in favor of {_grantRole}.\n */\n function _setupRole(bytes32 role, address account) internal virtual {\n _grantRole(role, account);\n }\n\n /**\n * @dev Sets `adminRole` as ``role``'s admin role.\n *\n * Emits a {RoleAdminChanged} event.\n */\n function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n bytes32 previousAdminRole = getRoleAdmin(role);\n _roles[role].adminRole = adminRole;\n emit RoleAdminChanged(role, previousAdminRole, adminRole);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * Internal function without access restriction.\n */\n function _grantRole(bytes32 role, address account) internal virtual {\n if (!hasRole(role, account)) {\n _roles[role].members[account] = true;\n emit RoleGranted(role, account, _msgSender());\n }\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * Internal function without access restriction.\n */\n function _revokeRole(bytes32 role, address account) internal virtual {\n if (hasRole(role, account)) {\n _roles[role].members[account] = false;\n emit RoleRevoked(role, account, _msgSender());\n }\n }\n}\n" + }, + "@openzeppelin/contracts/access/AccessControlEnumerable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/AccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControlEnumerable.sol\";\nimport \"./AccessControl.sol\";\nimport \"../utils/structs/EnumerableSet.sol\";\n\n/**\n * @dev Extension of {AccessControl} that allows enumerating the members of each role.\n */\nabstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {\n using EnumerableSet for EnumerableSet.AddressSet;\n\n mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) public view override returns (address) {\n return _roleMembers[role].at(index);\n }\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) public view override returns (uint256) {\n return _roleMembers[role].length();\n }\n\n /**\n * @dev Overload {_grantRole} to track enumerable memberships\n */\n function _grantRole(bytes32 role, address account) internal virtual override {\n super._grantRole(role, account);\n _roleMembers[role].add(account);\n }\n\n /**\n * @dev Overload {_revokeRole} to track enumerable memberships\n */\n function _revokeRole(bytes32 role, address account) internal virtual override {\n super._revokeRole(role, account);\n _roleMembers[role].remove(account);\n }\n}\n" + }, + "@openzeppelin/contracts/access/IAccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n /**\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n *\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n * {RoleAdminChanged} not being emitted signaling this.\n *\n * _Available since v3.1._\n */\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n /**\n * @dev Emitted when `account` is granted `role`.\n *\n * `sender` is the account that originated the contract call, an admin role\n * bearer except when using {AccessControl-_setupRole}.\n */\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Emitted when `account` is revoked `role`.\n *\n * `sender` is the account that originated the contract call:\n * - if using `revokeRole`, it is the admin role bearer\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\n */\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) external view returns (bool);\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been granted `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) external;\n}\n" + }, + "@openzeppelin/contracts/access/IAccessControlEnumerable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\n\n/**\n * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.\n */\ninterface IAccessControlEnumerable is IAccessControl {\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) external view returns (address);\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) external view returns (uint256);\n}\n" + }, + "@openzeppelin/contracts/access/Ownable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n" + }, + "@openzeppelin/contracts/security/Pausable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/ERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/ERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"./extensions/IERC20Metadata.sol\";\nimport \"../../utils/Context.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20, IERC20Metadata {\n mapping(address => uint256) private _balances;\n\n mapping(address => mapping(address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n\n /**\n * @dev Sets the values for {name} and {symbol}.\n *\n * The default value of {decimals} is 18. To select a different value for\n * {decimals} you should overload it.\n *\n * All two of these values are immutable: they can only be set once during\n * construction.\n */\n constructor(string memory name_, string memory symbol_) {\n _name = name_;\n _symbol = symbol_;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless this function is\n * overridden;\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `recipient` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n _approve(_msgSender(), spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * Requirements:\n *\n * - `sender` and `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n * - the caller must have allowance for ``sender``'s tokens of at least\n * `amount`.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public virtual override returns (bool) {\n _transfer(sender, recipient, amount);\n\n uint256 currentAllowance = _allowances[sender][_msgSender()];\n require(currentAllowance >= amount, \"ERC20: transfer amount exceeds allowance\");\n unchecked {\n _approve(sender, _msgSender(), currentAllowance - amount);\n }\n\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n uint256 currentAllowance = _allowances[_msgSender()][spender];\n require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n unchecked {\n _approve(_msgSender(), spender, currentAllowance - subtractedValue);\n }\n\n return true;\n }\n\n /**\n * @dev Moves `amount` of tokens from `sender` to `recipient`.\n *\n * This internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `sender` cannot be the zero address.\n * - `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n */\n function _transfer(\n address sender,\n address recipient,\n uint256 amount\n ) internal virtual {\n require(sender != address(0), \"ERC20: transfer from the zero address\");\n require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(sender, recipient, amount);\n\n uint256 senderBalance = _balances[sender];\n require(senderBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n unchecked {\n _balances[sender] = senderBalance - amount;\n }\n _balances[recipient] += amount;\n\n emit Transfer(sender, recipient, amount);\n\n _afterTokenTransfer(sender, recipient, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply += amount;\n _balances[account] += amount;\n emit Transfer(address(0), account, amount);\n\n _afterTokenTransfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n uint256 accountBalance = _balances[account];\n require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n unchecked {\n _balances[account] = accountBalance - amount;\n }\n _totalSupply -= amount;\n\n emit Transfer(account, address(0), amount);\n\n _afterTokenTransfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n\n /**\n * @dev Hook that is called after any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * has been transferred to `to`.\n * - when `from` is zero, `amount` tokens have been minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _afterTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC721 token receiver interface\n * @dev Interface for any contract that wants to support safeTransfers\n * from ERC721 asset contracts.\n */\ninterface IERC721Receiver {\n /**\n * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}\n * by `operator` from `from`, this function is called.\n *\n * It must return its Solidity selector to confirm the token transfer.\n * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.\n *\n * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.\n */\n function onERC721Received(\n address operator,\n address from,\n uint256 tokenId,\n bytes calldata data\n ) external returns (bytes4);\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/ERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC165.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n *\n * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.\n */\nabstract contract ERC165 is IERC165 {\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IERC165).interfaceId;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/IERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeMath.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)\n\npragma solidity ^0.8.0;\n\n// CAUTION\n// This version of SafeMath should only be used with Solidity 0.8 or later,\n// because it relies on the compiler's built in overflow checks.\n\n/**\n * @dev Wrappers over Solidity's arithmetic operations.\n *\n * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler\n * now has built in overflow checking.\n */\nlibrary SafeMath {\n /**\n * @dev Returns the addition of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n uint256 c = a + b;\n if (c < a) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the substraction of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b > a) return (false, 0);\n return (true, a - b);\n }\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n // benefit is lost if 'b' is also tested.\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n if (a == 0) return (true, 0);\n uint256 c = a * b;\n if (c / a != b) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the division of two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a / b);\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a % b);\n }\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `+` operator.\n *\n * Requirements:\n *\n * - Addition cannot overflow.\n */\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\n return a + b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting on\n * overflow (when the result is negative).\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\n return a - b;\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `*` operator.\n *\n * Requirements:\n *\n * - Multiplication cannot overflow.\n */\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\n return a * b;\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator.\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\n return a / b;\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting when dividing by zero.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\n return a % b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\n * overflow (when the result is negative).\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {trySub}.\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b <= a, errorMessage);\n return a - b;\n }\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting with custom message on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator. Note: this function uses a\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\n * uses an invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a / b;\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting with custom message when dividing by zero.\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {tryMod}.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a % b;\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Strings.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n bytes16 private constant _HEX_SYMBOLS = \"0123456789abcdef\";\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n // Inspired by OraclizeAPI's implementation - MIT licence\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\n\n if (value == 0) {\n return \"0\";\n }\n uint256 temp = value;\n uint256 digits;\n while (temp != 0) {\n digits++;\n temp /= 10;\n }\n bytes memory buffer = new bytes(digits);\n while (value != 0) {\n digits -= 1;\n buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\n value /= 10;\n }\n return string(buffer);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n if (value == 0) {\n return \"0x00\";\n }\n uint256 temp = value;\n uint256 length = 0;\n while (temp != 0) {\n length++;\n temp >>= 8;\n }\n return toHexString(value, length);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _HEX_SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/structs/EnumerableSet.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 => uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n" + }, + "contracts/automation/AbstractCCIPBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IRouterClient } from \"@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol\";\nimport { Client } from \"@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol\";\n\nabstract contract AbstractCCIPBridgeHelperModule is AbstractSafeModule {\n /**\n * @notice Bridges a token from the source chain to the destination chain using CCIP\n * @param ccipRouter The CCIP router contract\n * @param destinationChainSelector The selector for the destination chain\n * @param token The token to bridge\n * @param amount The amount of token to bridge\n */\n function _bridgeTokenWithCCIP(\n IRouterClient ccipRouter,\n uint64 destinationChainSelector,\n IERC20 token,\n uint256 amount\n ) internal {\n bool success;\n\n // Approve CCIP Router to move the token\n success = safeContract.execTransactionFromModule(\n address(token),\n 0, // Value\n abi.encodeWithSelector(token.approve.selector, ccipRouter, amount),\n 0 // Call\n );\n require(success, \"Failed to approve token\");\n\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(token),\n amount: amount\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory ccipMessage = Client.EVM2AnyMessage({\n receiver: abi.encode(address(safeContract)), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n // Get CCIP fee\n uint256 ccipFee = ccipRouter.getFee(\n destinationChainSelector,\n ccipMessage\n );\n\n // Send CCIP message\n success = safeContract.execTransactionFromModule(\n address(ccipRouter),\n ccipFee, // Value\n abi.encodeWithSelector(\n ccipRouter.ccipSend.selector,\n destinationChainSelector,\n ccipMessage\n ),\n 0 // Call\n );\n require(success, \"Failed to send CCIP message\");\n }\n}\n" + }, + "contracts/automation/AbstractLZBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\n\nimport { IOFT, SendParam } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\nimport { MessagingFee } from \"@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol\";\nimport { OptionsBuilder } from \"@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nabstract contract AbstractLZBridgeHelperModule is AbstractSafeModule {\n using OptionsBuilder for bytes;\n\n /**\n * @dev Bridges token using LayerZero.\n * @param lzEndpointId LayerZero endpoint id.\n * @param token Token to bridge.\n * @param lzAdapter LZ Adapter to use.\n * @param amount Amount of token to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n * @param isNativeToken Whether the token is native token.\n */\n function _bridgeTokenWithLz(\n uint32 lzEndpointId,\n IERC20 token,\n IOFT lzAdapter,\n uint256 amount,\n uint256 slippageBps,\n bool isNativeToken\n ) internal {\n bool success;\n\n if (!isNativeToken) {\n // Approve LZ Adapter to move the token\n success = safeContract.execTransactionFromModule(\n address(token),\n 0, // Value\n abi.encodeWithSelector(\n token.approve.selector,\n address(lzAdapter),\n amount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve token\");\n }\n\n // Calculate minimum amount to receive\n uint256 minAmount = (amount * (10000 - slippageBps)) / 10000;\n\n // Hardcoded gaslimit of 400k\n bytes memory options = OptionsBuilder\n .newOptions()\n .addExecutorLzReceiveOption(400000, 0);\n\n // Build send param\n SendParam memory sendParam = SendParam({\n dstEid: lzEndpointId,\n to: bytes32(uint256(uint160(address(safeContract)))),\n amountLD: amount,\n minAmountLD: minAmount,\n extraOptions: options,\n composeMsg: bytes(\"\"),\n oftCmd: bytes(\"\")\n });\n\n // Compute fees\n MessagingFee memory msgFee = lzAdapter.quoteSend(sendParam, false);\n\n uint256 value = isNativeToken\n ? amount + msgFee.nativeFee\n : msgFee.nativeFee;\n\n // Execute transaction\n success = safeContract.execTransactionFromModule(\n address(lzAdapter),\n value,\n abi.encodeWithSelector(\n lzAdapter.send.selector,\n sendParam,\n msgFee,\n address(safeContract)\n ),\n 0\n );\n require(success, \"Failed to bridge token\");\n }\n}\n" + }, + "contracts/automation/AbstractSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { ISafe } from \"../interfaces/ISafe.sol\";\n\nabstract contract AbstractSafeModule is AccessControlEnumerable {\n ISafe public immutable safeContract;\n\n bytes32 public constant OPERATOR_ROLE = keccak256(\"OPERATOR_ROLE\");\n\n modifier onlySafe() {\n require(\n msg.sender == address(safeContract),\n \"Caller is not the safe contract\"\n );\n _;\n }\n\n modifier onlyOperator() {\n require(\n hasRole(OPERATOR_ROLE, msg.sender),\n \"Caller is not an operator\"\n );\n _;\n }\n\n constructor(address _safeContract) {\n safeContract = ISafe(_safeContract);\n _grantRole(DEFAULT_ADMIN_ROLE, address(safeContract));\n _grantRole(OPERATOR_ROLE, address(safeContract));\n }\n\n /**\n * @dev Helps recovering any tokens accidentally sent to this module.\n * @param token Token to transfer. 0x0 to transfer Native token.\n * @param amount Amount to transfer. 0 to transfer all balance.\n */\n function transferTokens(address token, uint256 amount) external onlySafe {\n if (address(token) == address(0)) {\n // Move ETH\n amount = amount > 0 ? amount : address(this).balance;\n payable(address(safeContract)).transfer(amount);\n return;\n }\n\n // Move all balance if amount set to 0\n amount = amount > 0 ? amount : IERC20(token).balanceOf(address(this));\n\n // Transfer to Safe contract\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(token).transfer(address(safeContract), amount);\n }\n\n receive() external payable {\n // Accept ETH to pay for bridge fees\n }\n}\n" + }, + "contracts/automation/BaseBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// solhint-disable-next-line max-line-length\nimport { AbstractCCIPBridgeHelperModule, AbstractSafeModule, IRouterClient } from \"./AbstractCCIPBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { BridgedWOETHStrategy } from \"../strategies/BridgedWOETHStrategy.sol\";\n\ncontract BaseBridgeHelperModule is\n AccessControlEnumerable,\n AbstractCCIPBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0x98a0CbeF61bD2D21435f433bE4CD42B56B38CC93);\n IWETH9 public constant weth =\n IWETH9(0x4200000000000000000000000000000000000006);\n IERC20 public constant oethb =\n IERC20(0xDBFeFD2e8460a6Ee4955A68582F85708BAEA60A3);\n IERC4626 public constant bridgedWOETH =\n IERC4626(0xD8724322f44E5c58D7A815F542036fb17DbbF839);\n\n BridgedWOETHStrategy public constant bridgedWOETHStrategy =\n BridgedWOETHStrategy(0x80c864704DD06C3693ed5179190786EE38ACf835);\n\n IRouterClient public constant CCIP_ROUTER =\n IRouterClient(0x881e3A65B4d4a04dD529061dd0071cf975F58bCD);\n\n uint64 public constant CCIP_ETHEREUM_CHAIN_SELECTOR = 5009297550715157269;\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Ethereum.\n * @param woethAmount Amount of wOETH to bridge.\n */\n function bridgeWOETHToEthereum(uint256 woethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_ETHEREUM_CHAIN_SELECTOR,\n IERC20(address(bridgedWOETH)),\n woethAmount\n );\n }\n\n /**\n * @dev Bridges WETH to Ethereum.\n * @param wethAmount Amount of WETH to bridge.\n */\n function bridgeWETHToEthereum(uint256 wethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_ETHEREUM_CHAIN_SELECTOR,\n IERC20(address(weth)),\n wethAmount\n );\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem the wOETH for WETH using the Vault.\n * @return Amount of WETH received.\n */\n function depositWOETH(uint256 woethAmount, bool redeemWithVault)\n external\n onlyOperator\n returns (uint256)\n {\n return _depositWOETH(woethAmount, redeemWithVault);\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy and bridges it to Ethereum.\n * @param woethAmount Amount of wOETH to deposit.\n * @return Amount of WETH received.\n */\n function depositWOETHAndBridgeWETH(uint256 woethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n uint256 wethAmount = _depositWOETH(woethAmount, true);\n bridgeWETHToEthereum(wethAmount);\n return wethAmount;\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem the wOETH for WETH using the Vault.\n * @return Amount of WETH received.\n */\n function _depositWOETH(uint256 woethAmount, bool redeemWithVault)\n internal\n returns (uint256)\n {\n // Update oracle price\n bridgedWOETHStrategy.updateWOETHOraclePrice();\n\n // Rebase to account for any yields from price update\n vault.rebase();\n\n uint256 oethbAmount = oethb.balanceOf(address(safeContract));\n\n // Approve bridgedWOETH strategy to move wOETH\n bool success = safeContract.execTransactionFromModule(\n address(bridgedWOETH),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETH.approve.selector,\n address(bridgedWOETHStrategy),\n woethAmount\n ),\n 0 // Call\n );\n\n // Deposit to bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.depositBridgedWOETH.selector,\n woethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to deposit bridged WOETH\");\n\n oethbAmount = oethb.balanceOf(address(safeContract)) - oethbAmount;\n\n // Rebase to account for any yields from price update\n // and backing asset change from deposit\n vault.rebase();\n\n if (!redeemWithVault) {\n return oethbAmount;\n }\n\n // Redeem for WETH using Vault\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethbAmount,\n oethbAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETHb\");\n\n return oethbAmount;\n }\n\n /**\n * @dev Deposits WETH into the Vault and redeems wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to deposit.\n * @return Amount of wOETH received.\n */\n function depositWETHAndRedeemWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _withdrawWOETH(wethAmount);\n }\n\n function depositWETHAndBridgeWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n uint256 woethAmount = _withdrawWOETH(wethAmount);\n bridgeWOETHToEthereum(woethAmount);\n return woethAmount;\n }\n\n /**\n * @dev Withdraws wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to use to withdraw.\n * @return Amount of wOETH received.\n */\n function _withdrawWOETH(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETHb with WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETHb\");\n\n // Approve bridgedWOETH strategy to move OETHb\n success = safeContract.execTransactionFromModule(\n address(oethb),\n 0, // Value\n abi.encodeWithSelector(\n oethb.approve.selector,\n address(bridgedWOETHStrategy),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETHb\");\n\n uint256 woethAmount = bridgedWOETH.balanceOf(address(safeContract));\n\n // Withdraw from bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.withdrawBridgedWOETH.selector,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to withdraw bridged WOETH\");\n\n woethAmount =\n bridgedWOETH.balanceOf(address(safeContract)) -\n woethAmount;\n\n return woethAmount;\n }\n}\n" + }, + "contracts/automation/ClaimBribesSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { ICLGauge } from \"../interfaces/aerodrome/ICLGauge.sol\";\nimport { ICLPool } from \"../interfaces/aerodrome/ICLPool.sol\";\n\nstruct BribePoolInfo {\n address poolAddress;\n address rewardContractAddress;\n address[] rewardTokens;\n}\n\ninterface IAerodromeVoter {\n function claimBribes(\n address[] memory _bribes,\n address[][] memory _tokens,\n uint256 _tokenId\n ) external;\n}\n\ninterface IVeNFT {\n function ownerOf(uint256 tokenId) external view returns (address);\n\n function ownerToNFTokenIdList(address owner, uint256 index)\n external\n view\n returns (uint256);\n}\n\ninterface ICLRewardContract {\n function rewards(uint256 index) external view returns (address);\n\n function rewardsListLength() external view returns (uint256);\n}\n\ncontract ClaimBribesSafeModule is AbstractSafeModule {\n IAerodromeVoter public immutable voter;\n address public immutable veNFT;\n\n uint256[] nftIds;\n mapping(uint256 => uint256) nftIdIndex;\n\n BribePoolInfo[] bribePools;\n mapping(address => uint256) bribePoolIndex;\n\n event NFTIdAdded(uint256 nftId);\n event NFTIdRemoved(uint256 nftId);\n\n event BribePoolAdded(address bribePool);\n event BribePoolRemoved(address bribePool);\n\n constructor(\n address _safeContract,\n address _voter,\n address _veNFT\n ) AbstractSafeModule(_safeContract) {\n voter = IAerodromeVoter(_voter);\n veNFT = _veNFT;\n }\n\n /**\n * @dev Claim bribes for a range of NFTs\n * @param nftIndexStart The start index of the NFTs\n * @param nftIndexEnd The end index of the NFTs\n * @param silent Doesn't revert if the claim fails when true\n */\n function claimBribes(\n uint256 nftIndexStart,\n uint256 nftIndexEnd,\n bool silent\n ) external onlyOperator {\n if (nftIndexEnd < nftIndexStart) {\n (nftIndexStart, nftIndexEnd) = (nftIndexEnd, nftIndexStart);\n }\n uint256 nftCount = nftIds.length;\n nftIndexEnd = nftCount < nftIndexEnd ? nftCount : nftIndexEnd;\n\n (\n address[] memory rewardContractAddresses,\n address[][] memory rewardTokens\n ) = _getRewardsInfoArray();\n\n for (uint256 i = nftIndexStart; i < nftIndexEnd; i++) {\n uint256 nftId = nftIds[i];\n bool success = safeContract.execTransactionFromModule(\n address(voter),\n 0, // Value\n abi.encodeWithSelector(\n IAerodromeVoter.claimBribes.selector,\n rewardContractAddresses,\n rewardTokens,\n nftId\n ),\n 0 // Call\n );\n\n require(success || silent, \"ClaimBribes failed\");\n }\n }\n\n /**\n * @dev Get the reward contract address and reward tokens for all pools\n * @return rewardContractAddresses The reward contract addresses\n * @return rewardTokens The reward tokens\n */\n function _getRewardsInfoArray()\n internal\n view\n returns (\n address[] memory rewardContractAddresses,\n address[][] memory rewardTokens\n )\n {\n BribePoolInfo[] memory _bribePools = bribePools;\n uint256 bribePoolCount = _bribePools.length;\n rewardContractAddresses = new address[](bribePoolCount);\n rewardTokens = new address[][](bribePoolCount);\n\n for (uint256 i = 0; i < bribePoolCount; i++) {\n rewardContractAddresses[i] = _bribePools[i].rewardContractAddress;\n rewardTokens[i] = _bribePools[i].rewardTokens;\n }\n }\n\n /***************************************\n NFT Management\n ****************************************/\n /**\n * @dev Add NFT IDs to the list\n * @param _nftIds The NFT IDs to add\n */\n function addNFTIds(uint256[] memory _nftIds) external onlyOperator {\n for (uint256 i = 0; i < _nftIds.length; i++) {\n uint256 nftId = _nftIds[i];\n if (nftIdExists(nftId)) {\n // If it already exists, skip\n continue;\n }\n\n // Make sure the NFT is owned by the Safe\n require(\n IVeNFT(veNFT).ownerOf(nftId) == address(safeContract),\n \"NFT not owned by safe\"\n );\n\n nftIdIndex[nftId] = nftIds.length;\n nftIds.push(nftId);\n\n emit NFTIdAdded(nftId);\n }\n }\n\n /**\n * @dev Remove NFT IDs from the list\n * @param _nftIds The NFT IDs to remove\n */\n function removeNFTIds(uint256[] memory _nftIds) external onlyOperator {\n for (uint256 i = 0; i < _nftIds.length; i++) {\n uint256 nftId = _nftIds[i];\n if (!nftIdExists(nftId)) {\n // If it doesn't exist, skip\n continue;\n }\n\n uint256 index = nftIdIndex[nftId];\n uint256 lastNftId = nftIds[nftIds.length - 1];\n nftIds[index] = lastNftId;\n nftIdIndex[lastNftId] = index;\n nftIds.pop();\n\n emit NFTIdRemoved(nftId);\n }\n }\n\n /**\n * @dev Check if a NFT exists on the list\n * @param nftId The NFT ID to check\n * @return true if the NFT ID exists, false otherwise\n */\n function nftIdExists(uint256 nftId) public view returns (bool) {\n uint256 index = nftIdIndex[nftId];\n uint256[] memory _nftIds = nftIds;\n return (index < _nftIds.length) && _nftIds[index] == nftId;\n }\n\n /**\n * @dev Get the length of the nftIds list\n * @return The length of the nftIds list\n */\n function getNFTIdsLength() external view returns (uint256) {\n return nftIds.length;\n }\n\n /**\n * @dev Get all NFT IDs\n * @return The NFT IDs\n */\n function getAllNFTIds() external view returns (uint256[] memory) {\n return nftIds;\n }\n\n /**\n * @dev Fetch all NFT IDs from the veNFT contract\n * @notice This can revert if Safe owns too many NFTs since tx will be huge.\n * This function is public, anyone can call it, since it only fetches\n * the NFT IDs owned by the Safe. It shouldn't cause us any issues.\n */\n function fetchNFTIds() external {\n // Purge the array\n delete nftIds;\n\n uint256 i = 0;\n while (true) {\n uint256 nftId = IVeNFT(veNFT).ownerToNFTokenIdList(\n address(safeContract),\n i\n );\n if (nftId == 0) {\n break;\n }\n\n nftIdIndex[nftId] = nftIds.length;\n nftIds.push(nftId);\n i++;\n }\n }\n\n /**\n * @dev Remove all NFT IDs from the list\n */\n function removeAllNFTIds() external onlyOperator {\n uint256 length = nftIds.length;\n for (uint256 i = 0; i < length; i++) {\n uint256 nftId = nftIds[i];\n delete nftIdIndex[nftId];\n emit NFTIdRemoved(nftId);\n }\n\n delete nftIds;\n }\n\n /***************************************\n Bribe Pool Management\n ****************************************/\n // @dev Whitelist a pool to claim bribes from\n // @param _poolAddress The address of the pool to whitelist\n function addBribePool(address _poolAddress, bool _isVotingContract)\n external\n onlySafe\n {\n BribePoolInfo memory bribePool;\n\n if (_isVotingContract) {\n bribePool = BribePoolInfo({\n poolAddress: _poolAddress,\n rewardContractAddress: _poolAddress,\n rewardTokens: _getRewardTokenAddresses(_poolAddress)\n });\n } else {\n // Find the gauge address\n address _gaugeAddress = ICLPool(_poolAddress).gauge();\n // And the reward contract address\n address _rewardContractAddress = ICLGauge(_gaugeAddress)\n .feesVotingReward();\n\n bribePool = BribePoolInfo({\n poolAddress: _poolAddress,\n rewardContractAddress: _rewardContractAddress,\n rewardTokens: _getRewardTokenAddresses(_rewardContractAddress)\n });\n }\n\n if (bribePoolExists(_poolAddress)) {\n // Update if it already exists\n bribePools[bribePoolIndex[_poolAddress]] = bribePool;\n } else {\n // If not, Append to the list\n bribePoolIndex[_poolAddress] = bribePools.length;\n bribePools.push(bribePool);\n }\n\n emit BribePoolAdded(_poolAddress);\n }\n\n /**\n * @dev Update the reward token addresses for all pools\n */\n function updateRewardTokenAddresses() external onlyOperator {\n BribePoolInfo[] storage _bribePools = bribePools;\n for (uint256 i = 0; i < _bribePools.length; i++) {\n BribePoolInfo storage bribePool = _bribePools[i];\n bribePool.rewardTokens = _getRewardTokenAddresses(\n bribePool.rewardContractAddress == bribePool.poolAddress\n ? bribePool.poolAddress\n : bribePool.rewardContractAddress\n );\n }\n }\n\n /**\n * @dev Get the reward token addresses for a given reward contract address\n * @param _rewardContractAddress The address of the reward contract\n * @return _rewardTokens The reward token addresses\n */\n function _getRewardTokenAddresses(address _rewardContractAddress)\n internal\n view\n returns (address[] memory)\n {\n address[] memory _rewardTokens = new address[](\n ICLRewardContract(_rewardContractAddress).rewardsListLength()\n );\n for (uint256 i = 0; i < _rewardTokens.length; i++) {\n _rewardTokens[i] = ICLRewardContract(_rewardContractAddress)\n .rewards(i);\n }\n\n return _rewardTokens;\n }\n\n /**\n * @dev Remove a bribe pool from the list\n * @param _poolAddress The address of the pool to remove\n */\n function removeBribePool(address _poolAddress) external onlySafe {\n if (!bribePoolExists(_poolAddress)) {\n // If it doesn't exist, skip\n return;\n }\n\n uint256 index = bribePoolIndex[_poolAddress];\n BribePoolInfo memory lastBribePool = bribePools[bribePools.length - 1];\n bribePools[index] = lastBribePool;\n bribePoolIndex[lastBribePool.poolAddress] = index;\n bribePools.pop();\n\n emit BribePoolRemoved(_poolAddress);\n }\n\n /**\n * @dev Check if a bribe pool exists\n * @param bribePool The address of the pool to check\n * @return true if the pool exists, false otherwise\n */\n function bribePoolExists(address bribePool) public view returns (bool) {\n BribePoolInfo[] memory _bribePools = bribePools;\n uint256 poolIndex = bribePoolIndex[bribePool];\n return\n poolIndex < _bribePools.length &&\n _bribePools[poolIndex].poolAddress == bribePool;\n }\n\n /**\n * @dev Get the length of the bribe pools list\n * @return The length of the bribe pools list\n */\n function getBribePoolsLength() external view returns (uint256) {\n return bribePools.length;\n }\n}\n" + }, + "contracts/automation/ClaimStrategyRewardsSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\n\nimport { ISafe } from \"../interfaces/ISafe.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract ClaimStrategyRewardsSafeModule is AbstractSafeModule {\n using SafeERC20 for IERC20;\n\n mapping(address => bool) public isStrategyWhitelisted;\n address[] public strategies;\n\n event StrategyAdded(address strategy);\n event StrategyRemoved(address strategy);\n\n event ClaimRewardsFailed(address strategy);\n\n constructor(\n address _safeAddress,\n address operator,\n address[] memory _strategies\n ) AbstractSafeModule(_safeAddress) {\n _grantRole(OPERATOR_ROLE, operator);\n\n // Whitelist all strategies\n for (uint256 i = 0; i < _strategies.length; i++) {\n _addStrategy(_strategies[i]);\n }\n }\n\n /**\n * @dev Claim rewards from all whitelisted strategies\n * @param silent Doesn't revert on error if set to true\n */\n function claimRewards(bool silent) external onlyRole(OPERATOR_ROLE) {\n uint256 strategiesLength = strategies.length;\n for (uint256 i = 0; i < strategiesLength; i++) {\n address strategy = strategies[i];\n\n // Execute `collectRewardTokens` for all strategies\n bool success = safeContract.execTransactionFromModule(\n strategy, // To\n 0, // Value\n abi.encodeWithSelector(IStrategy.collectRewardTokens.selector),\n 0 // Call\n );\n\n if (!success) {\n emit ClaimRewardsFailed(strategy);\n }\n\n require(success || silent, \"Failed to claim rewards\");\n }\n }\n\n /**\n * @dev Add a strategy to the whitelist\n * @param _strategy The address of the strategy to add\n */\n function addStrategy(address _strategy)\n external\n onlyRole(DEFAULT_ADMIN_ROLE)\n {\n _addStrategy(_strategy);\n }\n\n function _addStrategy(address _strategy) internal {\n require(\n !isStrategyWhitelisted[_strategy],\n \"Strategy already whitelisted\"\n );\n isStrategyWhitelisted[_strategy] = true;\n strategies.push(_strategy);\n emit StrategyAdded(_strategy);\n }\n\n /**\n * @dev Remove a strategy from the whitelist\n * @param _strategy The address of the strategy to remove\n */\n function removeStrategy(address _strategy)\n external\n onlyRole(DEFAULT_ADMIN_ROLE)\n {\n require(isStrategyWhitelisted[_strategy], \"Strategy not whitelisted\");\n isStrategyWhitelisted[_strategy] = false;\n\n for (uint256 i = 0; i < strategies.length; i++) {\n if (strategies[i] == _strategy) {\n strategies[i] = strategies[strategies.length - 1];\n strategies.pop();\n break;\n }\n }\n\n emit StrategyRemoved(_strategy);\n }\n}\n" + }, + "contracts/automation/CollectXOGNRewardsModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IXOGN {\n function collectRewards() external;\n}\n\ncontract CollectXOGNRewardsModule is AbstractSafeModule {\n IXOGN public constant xogn =\n IXOGN(0x63898b3b6Ef3d39332082178656E9862bee45C57);\n address public constant rewardsSource =\n 0x7609c88E5880e934dd3A75bCFef44E31b1Badb8b;\n IERC20 public constant ogn =\n IERC20(0x8207c1FfC5B6804F6024322CcF34F29c3541Ae26);\n\n constructor(address _safeContract, address operator)\n AbstractSafeModule(_safeContract)\n {\n _grantRole(OPERATOR_ROLE, operator);\n }\n\n function collectRewards() external onlyOperator {\n uint256 balance = ogn.balanceOf(address(safeContract));\n\n bool success = safeContract.execTransactionFromModule(\n address(xogn),\n 0, // Value\n abi.encodeWithSelector(IXOGN.collectRewards.selector),\n 0 // Call\n );\n\n require(success, \"Failed to collect rewards\");\n\n balance = ogn.balanceOf(address(safeContract)) - balance;\n\n if (balance == 0) {\n return;\n }\n\n success = safeContract.execTransactionFromModule(\n address(ogn),\n 0, // Value\n abi.encodeWithSelector(\n IERC20.transfer.selector,\n rewardsSource,\n balance\n ),\n 0 // Call\n );\n\n require(success, \"Failed to collect rewards\");\n }\n}\n" + }, + "contracts/automation/EthereumBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractLZBridgeHelperModule, AbstractSafeModule } from \"./AbstractLZBridgeHelperModule.sol\";\nimport { AbstractCCIPBridgeHelperModule, IRouterClient } from \"./AbstractCCIPBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IOFT } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract EthereumBridgeHelperModule is\n AccessControlEnumerable,\n AbstractLZBridgeHelperModule,\n AbstractCCIPBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0x39254033945AA2E4809Cc2977E7087BEE48bd7Ab);\n IWETH9 public constant weth =\n IWETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);\n IERC20 public constant oeth =\n IERC20(0x856c4Efb76C1D1AE02e20CEB03A2A6a08b0b8dC3);\n IERC4626 public constant woeth =\n IERC4626(0xDcEe70654261AF21C44c093C300eD3Bb97b78192);\n\n uint32 public constant LZ_PLUME_ENDPOINT_ID = 30370;\n IOFT public constant LZ_WOETH_OMNICHAIN_ADAPTER =\n IOFT(0x7d1bEa5807e6af125826d56ff477745BB89972b8);\n IOFT public constant LZ_ETH_OMNICHAIN_ADAPTER =\n IOFT(0x77b2043768d28E9C9aB44E1aBfC95944bcE57931);\n\n IRouterClient public constant CCIP_ROUTER =\n IRouterClient(0x80226fc0Ee2b096224EeAc085Bb9a8cba1146f7D);\n\n uint64 public constant CCIP_BASE_CHAIN_SELECTOR = 15971525489660198786;\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Plume.\n * @param woethAmount Amount of wOETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWOETHToPlume(uint256 woethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_PLUME_ENDPOINT_ID,\n woeth,\n LZ_WOETH_OMNICHAIN_ADAPTER,\n woethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Bridges wOETH to Base using CCIP.\n * @param woethAmount Amount of wOETH to bridge.\n */\n function bridgeWOETHToBase(uint256 woethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_BASE_CHAIN_SELECTOR,\n woeth,\n woethAmount\n );\n }\n\n /**\n * @dev Bridges wETH to Plume.\n * @param wethAmount Amount of wETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWETHToPlume(uint256 wethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n // Unwrap into ETH\n safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(weth.withdraw.selector, wethAmount),\n 0 // Call\n );\n\n _bridgeTokenWithLz(\n LZ_PLUME_ENDPOINT_ID,\n IERC20(address(weth)),\n LZ_ETH_OMNICHAIN_ADAPTER,\n wethAmount,\n slippageBps,\n true\n );\n }\n\n /**\n * @dev Bridges wETH to Base using CCIP.\n * @param wethAmount Amount of wETH to bridge.\n */\n function bridgeWETHToBase(uint256 wethAmount) public payable onlyOperator {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_BASE_CHAIN_SELECTOR,\n IERC20(address(weth)),\n wethAmount\n );\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH.\n * @param wethAmount Amount of WETH to mint.\n * @param useNativeToken Whether to use native token to mint.\n * @return Amount of wOETH minted.\n */\n function mintAndWrap(uint256 wethAmount, bool useNativeToken)\n external\n onlyOperator\n returns (uint256)\n {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n return _mintAndWrap(wethAmount);\n }\n\n function wrapETH(uint256 ethAmount) public payable onlyOperator {\n // Deposit ETH into WETH\n safeContract.execTransactionFromModule(\n address(weth),\n ethAmount, // Value\n abi.encodeWithSelector(weth.deposit.selector),\n 0 // Call\n );\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH.\n * @param wethAmount Amount of WETH to mint.\n * @return Amount of wOETH minted.\n */\n function _mintAndWrap(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETH\");\n\n // Approve wOETH to move OETH\n success = safeContract.execTransactionFromModule(\n address(oeth),\n 0, // Value\n abi.encodeWithSelector(\n oeth.approve.selector,\n address(woeth),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETH\");\n\n uint256 woethAmount = woeth.balanceOf(address(safeContract));\n\n // Wrap OETH into wOETH\n success = safeContract.execTransactionFromModule(\n address(woeth),\n 0, // Value\n abi.encodeWithSelector(\n woeth.deposit.selector,\n wethAmount,\n address(safeContract)\n ),\n 0 // Call\n );\n require(success, \"Failed to wrap OETH\");\n\n // Compute amount of wOETH minted\n return woeth.balanceOf(address(safeContract)) - woethAmount;\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH, then bridges it to Plume.\n * @param wethAmount Amount of WETH to mint.\n * @param slippageBps Bridge slippage in 10^4 basis points.\n * @param useNativeToken Whether to use native token to mint.\n */\n function mintWrapAndBridgeToPlume(\n uint256 wethAmount,\n uint256 slippageBps,\n bool useNativeToken\n ) external payable onlyOperator {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n uint256 woethAmount = _mintAndWrap(wethAmount);\n bridgeWOETHToPlume(woethAmount, slippageBps);\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH, then bridges it to Base using CCIP.\n * @param wethAmount Amount of WETH to mint.\n * @param useNativeToken Whether to use native token to mint.\n */\n function mintWrapAndBridgeToBase(uint256 wethAmount, bool useNativeToken)\n external\n payable\n onlyOperator\n {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n uint256 woethAmount = _mintAndWrap(wethAmount);\n bridgeWOETHToBase(woethAmount);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH.\n * @param woethAmount Amount of wOETH to unwrap.\n * @return Amount of WETH received.\n */\n function unwrapAndRedeem(uint256 woethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _unwrapAndRedeem(woethAmount);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH.\n * @param woethAmount Amount of wOETH to unwrap.\n * @return Amount of WETH received.\n */\n function _unwrapAndRedeem(uint256 woethAmount) internal returns (uint256) {\n uint256 oethAmount = oeth.balanceOf(address(safeContract));\n\n // Unwrap wOETH\n bool success = safeContract.execTransactionFromModule(\n address(woeth),\n 0, // Value\n abi.encodeWithSelector(\n woeth.redeem.selector,\n woethAmount,\n address(safeContract),\n address(safeContract)\n ),\n 0 // Call\n );\n require(success, \"Failed to unwrap wOETH\");\n\n oethAmount = oeth.balanceOf(address(safeContract)) - oethAmount;\n\n // Redeem OETH using Vault to get WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethAmount,\n oethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETH\");\n\n return oethAmount;\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH, then bridges it to Plume.\n * @param woethAmount Amount of wOETH to unwrap.\n * @param slippageBps Bridge slippage in 10^4 basis points.\n */\n function unwrapRedeemAndBridgeToPlume(\n uint256 woethAmount,\n uint256 slippageBps\n ) external payable onlyOperator {\n uint256 wethAmount = _unwrapAndRedeem(woethAmount);\n // Unwrap into ETH\n safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(weth.withdraw.selector, wethAmount),\n 0 // Call\n );\n\n bridgeWETHToPlume(wethAmount, slippageBps);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH, then bridges it to Base using CCIP.\n * @param woethAmount Amount of wOETH to unwrap.\n */\n function unwrapRedeemAndBridgeToBase(uint256 woethAmount)\n external\n payable\n onlyOperator\n {\n uint256 wethAmount = _unwrapAndRedeem(woethAmount);\n bridgeWETHToBase(wethAmount);\n }\n}\n" + }, + "contracts/automation/PlumeBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { AbstractLZBridgeHelperModule } from \"./AbstractLZBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IOFT } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { BridgedWOETHStrategy } from \"../strategies/BridgedWOETHStrategy.sol\";\n\ncontract PlumeBridgeHelperModule is\n AccessControlEnumerable,\n AbstractLZBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0xc8c8F8bEA5631A8AF26440AF32a55002138cB76a);\n IWETH9 public constant weth =\n IWETH9(0xca59cA09E5602fAe8B629DeE83FfA819741f14be);\n IERC20 public constant oethp =\n IERC20(0xFCbe50DbE43bF7E5C88C6F6Fb9ef432D4165406E);\n IERC4626 public constant bridgedWOETH =\n IERC4626(0xD8724322f44E5c58D7A815F542036fb17DbbF839);\n\n uint32 public constant LZ_ETHEREUM_ENDPOINT_ID = 30101;\n IOFT public constant LZ_WOETH_OMNICHAIN_ADAPTER =\n IOFT(0x592CB6A596E7919930bF49a27AdAeCA7C055e4DB);\n IOFT public constant LZ_ETH_OMNICHAIN_ADAPTER =\n IOFT(0x4683CE822272CD66CEa73F5F1f9f5cBcaEF4F066);\n\n BridgedWOETHStrategy public constant bridgedWOETHStrategy =\n BridgedWOETHStrategy(0x1E3EdD5e019207D6355Ea77F724b1F1BF639B569);\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Ethereum.\n * @param woethAmount Amount of wOETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWOETHToEthereum(uint256 woethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_ETHEREUM_ENDPOINT_ID,\n IERC20(address(bridgedWOETH)),\n LZ_WOETH_OMNICHAIN_ADAPTER,\n woethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Bridges wETH to Ethereum.\n * @param wethAmount Amount of wETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWETHToEthereum(uint256 wethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_ETHEREUM_ENDPOINT_ID,\n IERC20(address(weth)),\n LZ_ETH_OMNICHAIN_ADAPTER,\n wethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem with Vault.\n * @return Amount of OETHp received.\n */\n function depositWOETH(uint256 woethAmount, bool redeemWithVault)\n external\n onlyOperator\n returns (uint256)\n {\n return _depositWOETH(woethAmount, redeemWithVault);\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy and bridges it to Ethereum.\n * @param woethAmount Amount of wOETH to deposit.\n * @param slippageBps Slippage in 10^4 basis points.\n * @return Amount of WETH received.\n */\n function depositWOETHAndBridgeWETH(uint256 woethAmount, uint256 slippageBps)\n external\n payable\n onlyOperator\n returns (uint256)\n {\n uint256 wethAmount = _depositWOETH(woethAmount, true);\n bridgeWETHToEthereum(wethAmount, slippageBps);\n return wethAmount;\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem with Vault.\n * @return Amount of OETHp received.\n */\n function _depositWOETH(uint256 woethAmount, bool redeemWithVault)\n internal\n returns (uint256)\n {\n // Update oracle price\n bridgedWOETHStrategy.updateWOETHOraclePrice();\n\n // Rebase to account for any yields from price update\n vault.rebase();\n\n uint256 oethpAmount = oethp.balanceOf(address(safeContract));\n\n // Approve bridgedWOETH strategy to move wOETH\n bool success = safeContract.execTransactionFromModule(\n address(bridgedWOETH),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETH.approve.selector,\n address(bridgedWOETHStrategy),\n woethAmount\n ),\n 0 // Call\n );\n\n // Deposit to bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.depositBridgedWOETH.selector,\n woethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to deposit bridged WOETH\");\n\n oethpAmount = oethp.balanceOf(address(safeContract)) - oethpAmount;\n\n // Rebase to account for any yields from price update\n // and backing asset change from deposit\n vault.rebase();\n\n if (!redeemWithVault) {\n return oethpAmount;\n }\n\n // Redeem for WETH using Vault\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethpAmount,\n oethpAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETHp\");\n\n return oethpAmount;\n }\n\n /**\n * @dev Deposits wETH into the vault.\n * @param wethAmount Amount of wETH to deposit.\n * @return Amount of OETHp received.\n */\n function depositWETHAndRedeemWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _withdrawWOETH(wethAmount);\n }\n\n /**\n * @dev Deposits wETH into the vault and bridges it to Ethereum.\n * @param wethAmount Amount of wETH to deposit.\n * @param slippageBps Slippage in 10^4 basis points.\n * @return Amount of WOETH received.\n */\n function depositWETHAndBridgeWOETH(uint256 wethAmount, uint256 slippageBps)\n external\n payable\n onlyOperator\n returns (uint256)\n {\n uint256 woethAmount = _withdrawWOETH(wethAmount);\n bridgeWOETHToEthereum(woethAmount, slippageBps);\n return woethAmount;\n }\n\n /**\n * @dev Withdraws wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to use to withdraw.\n * @return Amount of wOETH received.\n */\n function _withdrawWOETH(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETHp with WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETHp\");\n\n // Approve bridgedWOETH strategy to move OETHp\n success = safeContract.execTransactionFromModule(\n address(oethp),\n 0, // Value\n abi.encodeWithSelector(\n oethp.approve.selector,\n address(bridgedWOETHStrategy),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETHp\");\n\n uint256 woethAmount = bridgedWOETH.balanceOf(address(safeContract));\n\n // Withdraw from bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.withdrawBridgedWOETH.selector,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to withdraw bridged WOETH\");\n\n woethAmount =\n bridgedWOETH.balanceOf(address(safeContract)) -\n woethAmount;\n\n return woethAmount;\n }\n}\n" + }, + "contracts/beacon/BeaconConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary BeaconConsolidation {\n /// @notice The address the validator consolidation requests are sent\n /// See https://eips.ethereum.org/EIPS/eip-7251\n address internal constant CONSOLIDATION_REQUEST_ADDRESS =\n 0x0000BBdDc7CE488642fb579F8B00f3a590007251;\n\n function request(bytes calldata source, bytes calldata target)\n internal\n returns (uint256 fee_)\n {\n require(source.length == 48, \"Invalid source byte length\");\n require(target.length == 48, \"Invalid target byte length\");\n\n fee_ = fee();\n\n // Call the Consolidation Request contract with the public keys of the source and target\n // validators packed together.\n // This does not have a function signature, so we use a call\n (bool success, ) = CONSOLIDATION_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(source, target)\n );\n\n require(success, \"Consolidation request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the consolidation request contract\n (bool success, bytes memory result) = CONSOLIDATION_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/beacon/BeaconOracle.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"./BeaconProofsLib.sol\";\nimport { BeaconRoots } from \"./BeaconRoots.sol\";\n\n/// @title Beacon Chain Oracle\n/// @notice An Oracle for mapping execution layer block numbers to beacon chain slots.\n/// @author Origin Protocol Inc\ncontract BeaconOracle {\n /// @notice Maps a block number to slot\n mapping(uint64 => uint64) internal _blockToSlot;\n /// @notice Maps a slot to a number\n mapping(uint64 => uint64) internal _slotToBlock;\n /// @notice Maps a slot to a beacon block root\n mapping(uint64 => bytes32) internal _slotToRoot;\n\n event BlockToSlot(\n bytes32 indexed blockRoot,\n uint64 indexed blockNumber,\n uint64 indexed slot\n );\n\n /// @notice Uses merkle a proof against the beacon block root to link\n /// an execution layer block number to a beacon chain slot.\n /// @param nextBlockTimestamp The timestamp of the block after the one being proven.\n /// @param blockNumber The execution layer block number.\n /// @param slot The beacon chain slot.\n /// @param slotProof The merkle proof witnesses for the slot against the beacon block root.\n /// @param blockProof The merkle proof witnesses for the block number against the beacon block root.\n function verifySlot(\n uint64 nextBlockTimestamp,\n uint64 blockNumber,\n uint64 slot,\n bytes calldata slotProof,\n bytes calldata blockProof\n ) external returns (bytes32 blockRoot) {\n require(_blockToSlot[blockNumber] == 0, \"Block already mapped\");\n\n // Get the parent beacon block root for the given timestamp.\n // This is the beacon block root of the previous slot.\n blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the slot to the beacon block root\n BeaconProofsLib.verifySlot(blockRoot, slot, slotProof);\n\n // Verify the block number to the beacon block root\n BeaconProofsLib.verifyBlockNumber(blockRoot, blockNumber, blockProof);\n\n // Store mappings\n _blockToSlot[blockNumber] = slot;\n _slotToBlock[slot] = blockNumber;\n _slotToRoot[slot] = blockRoot;\n\n emit BlockToSlot(blockRoot, blockNumber, slot);\n }\n\n /// @notice Returns the beacon chain slot for a given execution layer block number.\n function blockToSlot(uint64 blockNumber)\n external\n view\n returns (uint64 slot)\n {\n slot = _blockToSlot[blockNumber];\n\n require(slot != 0, \"Block not mapped\");\n }\n\n /// @notice Returns the execution layer block number for a given beacon chain slot.\n function slotToBlock(uint64 slot)\n external\n view\n returns (uint64 blockNumber)\n {\n blockNumber = _slotToBlock[slot];\n\n require(blockNumber != 0, \"Slot not mapped\");\n }\n\n /// @notice Returns the beacon block root for a given beacon chain slot.\n function slotToRoot(uint64 slot) external view returns (bytes32 blockRoot) {\n blockRoot = _slotToRoot[slot];\n\n require(blockRoot != 0, \"Slot not mapped\");\n }\n\n /// @notice Returns true if an execution layer block number has been mapped to a beacon chain slot.\n function isBlockMapped(uint64 blockNumber) external view returns (bool) {\n return _blockToSlot[blockNumber] != 0;\n }\n\n /// @notice Returns true if a beacon chain slot has been mapped to an execution layer block number.\n function isSlotMapped(uint64 slot) external view returns (bool) {\n return _slotToBlock[slot] != 0;\n }\n}\n" + }, + "contracts/beacon/BeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"./BeaconProofsLib.sol\";\n\ncontract BeaconProofs {\n /// @notice Verifies the validator public key against the beacon block root\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash The beacon chain hash of the validator public key\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view {\n BeaconProofsLib.verifyValidatorPubkey(\n beaconBlockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n withdrawalAddress\n );\n }\n\n /// @notice Verifies the balances container against the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerLeaf The leaf node containing the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view {\n BeaconProofsLib.verifyBalancesContainer(\n beaconBlockRoot,\n balancesContainerLeaf,\n balancesContainerProof\n );\n }\n\n /// @notice Verifies the validator balance against the root of the Balances container\n /// or the beacon block root\n /// @param root The root of the Balances container or the beacon block root\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances\n /// @param balanceProof The merkle proof for the validator balance against the root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @param level The level of the balance proof, either Container or BeaconBlock\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 root,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex,\n BeaconProofsLib.BalanceProofLevel level\n ) external view returns (uint256 validatorBalanceGwei) {\n validatorBalanceGwei = BeaconProofsLib.verifyValidatorBalance(\n root,\n validatorBalanceLeaf,\n balanceProof,\n validatorIndex,\n level\n );\n }\n\n /// @notice Verifies the slot of the first pending deposit against the beacon block root\n /// BeaconBlock.state.PendingDeposits[0].slot\n /// @param beaconBlockRoot The root of the beacon block\n /// @param slot The beacon chain slot to verify\n /// @param firstPendingDepositSlotProof The merkle proof for the first pending deposit's slot\n /// against the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view {\n BeaconProofsLib.verifyFirstPendingDepositSlot(\n beaconBlockRoot,\n slot,\n firstPendingDepositSlotProof\n );\n }\n\n /// @notice Verifies the block number to the the beacon block root\n /// BeaconBlock.body.executionPayload.blockNumber\n /// @param beaconBlockRoot The root of the beacon block\n /// @param blockNumber The execution layer block number to verify\n /// @param blockNumberProof The merkle proof for the block number against the beacon block\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyBlockNumber(\n bytes32 beaconBlockRoot,\n uint256 blockNumber,\n bytes calldata blockNumberProof\n ) external view {\n BeaconProofsLib.verifyBlockNumber(\n beaconBlockRoot,\n blockNumber,\n blockNumberProof\n );\n }\n\n /// @notice Verifies the slot number against the beacon block root.\n /// BeaconBlock.slot\n /// @param beaconBlockRoot The root of the beacon block\n /// @param slot The beacon chain slot to verify\n /// @param slotProof The merkle proof for the slot against the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifySlot(\n bytes32 beaconBlockRoot,\n uint256 slot,\n bytes calldata slotProof\n ) external view {\n BeaconProofsLib.verifySlot(beaconBlockRoot, slot, slotProof);\n }\n}\n" + }, + "contracts/beacon/BeaconProofsLib.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Merkle } from \"./Merkle.sol\";\nimport { Endian } from \"./Endian.sol\";\n\nlibrary BeaconProofsLib {\n // Known generalized indices in the beacon block\n // BeaconBlock.slot\n uint256 internal constant SLOT_GENERALIZED_INDEX = 8;\n // BeaconBlock.state.PendingDeposits[0].slot\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX =\n 1584842932228;\n // BeaconBlock.body.executionPayload.blockNumber\n uint256 internal constant BLOCK_NUMBER_GENERALIZED_INDEX = 6438;\n // BeaconBlock.state.validators\n uint256 internal constant VALIDATORS_CONTAINER_GENERALIZED_INDEX = 715;\n // BeaconBlock.state.balances\n uint256 internal constant BALANCES_CONTAINER_GENERALIZED_INDEX = 716;\n\n // Beacon Container Tree Heights\n uint256 internal constant BALANCES_HEIGHT = 39;\n uint256 internal constant VALIDATORS_HEIGHT = 41;\n uint256 internal constant VALIDATOR_HEIGHT = 3;\n\n /// @notice Fields in the Validator container for phase 0\n /// See https://ethereum.github.io/consensus-specs/specs/phase0/beacon-chain/#validator\n uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;\n\n enum BalanceProofLevel {\n Container,\n BeaconBlock\n }\n\n /// @notice Verifies the validator public key against the beacon block root\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash The beacon chain hash of the validator public key\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) internal view {\n // BeaconBlock.state.validators[validatorIndex].pubkey\n uint256 generalizedIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_HEIGHT,\n validatorIndex\n );\n generalizedIndex = concatGenIndices(\n generalizedIndex,\n VALIDATOR_HEIGHT,\n VALIDATOR_PUBKEY_INDEX\n );\n\n // Get the withdrawal address from the first witness in the pubkey merkle proof.\n address withdrawalAddressFromProof;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // The first 32 bytes of the proof is the withdrawal credential so load it into memory.\n calldatacopy(0, validatorPubKeyProof.offset, 32)\n // Cast the 32 bytes in memory to an address which is the last 20 bytes.\n withdrawalAddressFromProof := mload(0)\n }\n require(\n withdrawalAddressFromProof == withdrawalAddress,\n \"Invalid withdrawal address\"\n );\n\n require(\n Merkle.verifyInclusionSha256({\n proof: validatorPubKeyProof,\n root: beaconBlockRoot,\n leaf: pubKeyHash,\n index: generalizedIndex\n }),\n \"Invalid validator pubkey proof\"\n );\n }\n\n /// @notice Verifies the balances container against the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerLeaf The leaf node containing the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) internal view {\n // BeaconBlock.state.balances\n require(\n Merkle.verifyInclusionSha256({\n proof: balancesContainerProof,\n root: beaconBlockRoot,\n leaf: balancesContainerLeaf,\n index: BALANCES_CONTAINER_GENERALIZED_INDEX\n }),\n \"Invalid balance container proof\"\n );\n }\n\n /// @notice Verifies the validator balance against the root of the Balances container\n /// or the beacon block root\n /// @param root The root of the Balances container or the beacon block root\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances\n /// @param balanceProof The merkle proof for the validator balance against the root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @param level The level of the balance proof, either Container or BeaconBlock\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 root,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex,\n BalanceProofLevel level\n ) internal view returns (uint256 validatorBalanceGwei) {\n // Four balances are stored in each leaf so the validator index is divided by 4\n uint64 balanceIndex = validatorIndex / 4;\n\n // slither-disable-next-line uninitialized-local\n uint256 generalizedIndex;\n if (level == BalanceProofLevel.Container) {\n // Get the index within the balances container, not the Beacon Block\n // BeaconBlock.state.balances[balanceIndex]\n generalizedIndex = concatGenIndices(\n 1,\n BALANCES_HEIGHT,\n balanceIndex\n );\n }\n\n if (level == BalanceProofLevel.BeaconBlock) {\n generalizedIndex = concatGenIndices(\n BALANCES_CONTAINER_GENERALIZED_INDEX,\n BALANCES_HEIGHT,\n balanceIndex\n );\n }\n\n validatorBalanceGwei = balanceAtIndex(\n validatorBalanceLeaf,\n validatorIndex\n );\n\n require(\n Merkle.verifyInclusionSha256({\n proof: balanceProof,\n root: root,\n leaf: validatorBalanceLeaf,\n index: generalizedIndex\n }),\n \"Invalid balance proof\"\n );\n }\n\n /// @notice Verifies the slot of the first pending deposit against the beacon block root\n /// BeaconBlock.state.PendingDeposits[0].slot\n /// @param beaconBlockRoot The root of the beacon block\n /// @param slot The beacon chain slot to verify\n /// @param firstPendingDepositSlotProof The merkle proof for the first pending deposit's slot\n /// against the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) internal view {\n // Convert uint64 slot number to a little endian bytes32\n bytes32 slotLeaf = Endian.toLittleEndianUint64(slot);\n\n require(\n Merkle.verifyInclusionSha256({\n proof: firstPendingDepositSlotProof,\n root: beaconBlockRoot,\n leaf: slotLeaf,\n index: FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX\n }),\n \"Invalid pending deposit proof\"\n );\n }\n\n /// @notice Verifies the block number to the the beacon block root\n /// BeaconBlock.body.executionPayload.blockNumber\n /// @param beaconBlockRoot The root of the beacon block\n /// @param blockNumber The execution layer block number to verify\n /// @param blockNumberProof The merkle proof for the block number against the beacon block\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyBlockNumber(\n bytes32 beaconBlockRoot,\n uint256 blockNumber,\n bytes calldata blockNumberProof\n ) internal view {\n // Convert uint64 block number to a little endian bytes32\n bytes32 blockNumberLeaf = Endian.toLittleEndianUint64(\n uint64(blockNumber)\n );\n require(\n Merkle.verifyInclusionSha256({\n proof: blockNumberProof,\n root: beaconBlockRoot,\n leaf: blockNumberLeaf,\n index: BLOCK_NUMBER_GENERALIZED_INDEX\n }),\n \"Invalid block number proof\"\n );\n }\n\n /// @notice Verifies the slot number against the beacon block root.\n /// BeaconBlock.slot\n /// @param beaconBlockRoot The root of the beacon block\n /// @param slot The beacon chain slot to verify\n /// @param slotProof The merkle proof for the slot against the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifySlot(\n bytes32 beaconBlockRoot,\n uint256 slot,\n bytes calldata slotProof\n ) internal view {\n require(\n Merkle.verifyInclusionSha256({\n proof: slotProof,\n root: beaconBlockRoot,\n leaf: Endian.toLittleEndianUint64(uint64(slot)),\n index: SLOT_GENERALIZED_INDEX\n }),\n \"Invalid slot number proof\"\n );\n }\n\n ////////////////////////////////////////////////////\n /// Internal Helper Functions\n ////////////////////////////////////////////////////\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n internal\n pure\n returns (uint256)\n {\n uint256 bitShiftAmount = (validatorIndex % 4) * 64;\n return\n Endian.fromLittleEndianUint64(\n bytes32((uint256(validatorBalanceLeaf) << bitShiftAmount))\n );\n }\n\n /// @notice Concatenates two beacon chain generalized indices into one.\n /// @param genIndex The first generalized index or 1 if calculating for a single container.\n /// @param height The merkle tree height of the second container. eg 39 for balances, 41 for validators.\n /// @param index The index within the second container. eg the validator index.\n /// @return genIndex The concatenated generalized index.\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) internal pure returns (uint256) {\n return (genIndex << height) | index;\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the Beacon Block Root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Block Root Oracle to get the parent block root\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/Endian.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nlibrary Endian {\n /**\n * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64\n * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type\n * @return n The big endian-formatted uint64\n * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits),\n * but it is immediately truncated to a uint64 (i.e. 64 bits)\n * through a right-shift/shr operation.\n */\n function fromLittleEndianUint64(bytes32 lenum)\n internal\n pure\n returns (uint64 n)\n {\n // the number needs to be stored in little-endian encoding (ie in bytes 0-8)\n n = uint64(uint256(lenum >> 192));\n // forgefmt: disable-next-item\n return\n (n >> 56) |\n ((0x00FF000000000000 & n) >> 40) |\n ((0x0000FF0000000000 & n) >> 24) |\n ((0x000000FF00000000 & n) >> 8) |\n ((0x00000000FF000000 & n) << 8) |\n ((0x0000000000FF0000 & n) << 24) |\n ((0x000000000000FF00 & n) << 40) |\n ((0x00000000000000FF & n) << 56);\n }\n\n function toLittleEndianUint64(uint64 benum)\n internal\n pure\n returns (bytes32 n)\n {\n // Convert to little-endian by reversing byte order\n uint64 reversed = (benum >> 56) |\n ((0x00FF000000000000 & benum) >> 40) |\n ((0x0000FF0000000000 & benum) >> 24) |\n ((0x000000FF00000000 & benum) >> 8) |\n ((0x00000000FF000000 & benum) << 8) |\n ((0x0000000000FF0000 & benum) << 24) |\n ((0x000000000000FF00 & benum) << 40) |\n ((0x00000000000000FF & benum) << 56);\n\n // Store the little-endian uint64 in the least significant 64 bits of bytes32\n n = bytes32(uint256(reversed));\n // Shift to most significant bits\n n = n << 192;\n }\n}\n" + }, + "contracts/beacon/Merkle.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev These functions deal with verification of Merkle Tree proofs.\n *\n * The tree and the proofs can be generated using our\n * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].\n * You will find a quickstart guide in the readme.\n *\n * WARNING: You should avoid using leaf values that are 64 bytes long prior to\n * hashing, or use a hash function other than keccak256 for hashing leaves.\n * This is because the concatenation of a sorted pair of internal nodes in\n * the merkle tree could be reinterpreted as a leaf value.\n * OpenZeppelin's JavaScript library generates merkle trees that are safe\n * against this attack out of the box.\n */\nlibrary Merkle {\n error InvalidProofLength();\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function verifyInclusionSha256(\n bytes memory proof,\n bytes32 root,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bool) {\n return processInclusionProofSha256(proof, leaf, index) == root;\n }\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * _Available since v4.4._\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function processInclusionProofSha256(\n bytes memory proof,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bytes32) {\n require(\n proof.length != 0 && proof.length % 32 == 0,\n InvalidProofLength()\n );\n bytes32[1] memory computedHash = [leaf];\n for (uint256 i = 32; i <= proof.length; i += 32) {\n if (index % 2 == 0) {\n // if ith bit of index is 0, then computedHash is a left sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(computedHash))\n mstore(0x20, mload(add(proof, i)))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n index := div(index, 2)\n }\n } else {\n // if ith bit of index is 1, then computedHash is a right sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(add(proof, i)))\n mstore(0x20, mload(computedHash))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n index := div(index, 2)\n }\n }\n }\n return computedHash[0];\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/bridges/OmnichainL2Adapter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.0;\n\nimport { MintBurnOFTAdapter } from \"@layerzerolabs/oft-evm/contracts/MintBurnOFTAdapter.sol\";\nimport { IMintableBurnable } from \"@layerzerolabs/oft-evm/contracts/interfaces/IMintableBurnable.sol\";\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\n\n/// NOTE: It's necessary to inherit from Ownable instead of Governable\n/// because OFTCore uses Ownable to manage the governor.\n/// Ownable uses slot 0 for storing the address, whereas Goveranble\n/// stores it in a computed slot.\n\n/// @notice Omnichain uses a deployed ERC-20 token and safeERC20\n/// to interact with the OFTCore contract.\n/// On L2, we follow the mint and burn mechanism.\n/// The adapter should have minter and burner roles.\n\n/// @title Omnichain L2 Adapter\ncontract OmnichainL2Adapter is MintBurnOFTAdapter {\n constructor(\n address _token,\n address _lzEndpoint,\n address _governor\n )\n MintBurnOFTAdapter(\n _token,\n IMintableBurnable(_token),\n _lzEndpoint,\n _governor\n )\n Ownable()\n {\n _transferOwnership(_governor);\n }\n\n /// @inheritdoc MintBurnOFTAdapter\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n )\n internal\n virtual\n override\n returns (uint256 amountSentLD, uint256 amountReceivedLD)\n {\n (amountSentLD, amountReceivedLD) = _debitView(\n _amountLD,\n _minAmountLD,\n _dstEid\n );\n // Burns tokens from the caller.\n IMintableERC20(address(minterBurner)).burn(_from, amountSentLD);\n }\n\n /// @inheritdoc MintBurnOFTAdapter\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /* _srcEid */\n ) internal virtual override returns (uint256 amountReceivedLD) {\n if (_to == address(0x0)) _to = address(0xdead); // _mint(...) does not support address(0x0)\n // Mints the tokens and transfers to the recipient.\n IMintableERC20(address(minterBurner)).mint(_to, _amountLD);\n // In the case of NON-default OFTAdapter, the amountLD MIGHT not be equal to amountReceivedLD.\n return _amountLD;\n }\n}\n\ninterface IMintableERC20 {\n function mint(address to, uint256 value) external;\n\n function burn(address to, uint256 value) external;\n}\n" + }, + "contracts/bridges/OmnichainMainnetAdapter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.0;\n\nimport { OFTAdapter } from \"@layerzerolabs/oft-evm/contracts/OFTAdapter.sol\";\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\n\n/// NOTE: It's necessary to inherit from Ownable instead of Governable\n/// because OFTCore uses Ownable to manage the governor.\n/// Ownable uses slot 0 for storing the address, whereas Goveranble\n/// stores it in a computed slot.\n\n/// @notice Omnichain uses a deployed ERC-20 token and safeERC20\n/// to interact with the OFTCore contract.\n/// On Ethereum, we follow the lock and unlock mechanism.\n\n/// @title Omnichain Mainnet Adapter\ncontract OmnichainMainnetAdapter is OFTAdapter {\n constructor(\n address _token,\n address _lzEndpoint,\n address _governor\n ) OFTAdapter(_token, _lzEndpoint, _governor) Ownable() {\n _transferOwnership(_governor);\n }\n}\n" + }, + "contracts/buyback/AbstractBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { ICVXLocker } from \"../interfaces/ICVXLocker.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\n\nabstract contract AbstractBuyback is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n event SwapRouterUpdated(address indexed _address);\n\n event RewardsSourceUpdated(address indexed _address);\n event TreasuryManagerUpdated(address indexed _address);\n event CVXShareBpsUpdated(uint256 bps);\n\n // Emitted whenever OUSD/OETH is swapped for OGN/CVX or any other token\n event OTokenBuyback(\n address indexed oToken,\n address indexed swappedFor,\n uint256 swapAmountIn,\n uint256 amountOut\n );\n\n // Address of 1-inch Swap Router\n address public swapRouter;\n\n // slither-disable-next-line constable-states\n address private __deprecated_ousd;\n // slither-disable-next-line constable-states\n address private __deprecated_ogv;\n // slither-disable-next-line constable-states\n address private __deprecated_usdt;\n // slither-disable-next-line constable-states\n address private __deprecated_weth9;\n\n // Address that receives OGN after swaps\n address public rewardsSource;\n\n // Address that receives all other tokens after swaps\n address public treasuryManager;\n\n // slither-disable-next-line constable-states\n uint256 private __deprecated_treasuryBps;\n\n address public immutable oToken;\n address public immutable ogn;\n address public immutable cvx;\n address public immutable cvxLocker;\n\n // Amount of `oToken` balance to use for OGN buyback\n uint256 public balanceForOGN;\n\n // Amount of `oToken` balance to use for CVX buyback\n uint256 public balanceForCVX;\n\n // Percentage of `oToken` balance to be used for CVX\n uint256 public cvxShareBps; // 10000 = 100%\n\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) {\n // Make sure nobody owns the implementation contract\n _setGovernor(address(0));\n\n oToken = _oToken;\n ogn = _ogn;\n cvx = _cvx;\n cvxLocker = _cvxLocker;\n }\n\n /**\n * @param _swapRouter Address of Uniswap V3 Router\n * @param _strategistAddr Address of Strategist multi-sig wallet\n * @param _treasuryManagerAddr Address that receives the treasury's share of OUSD\n * @param _rewardsSource Address of RewardsSource contract\n * @param _cvxShareBps Percentage of balance to use for CVX\n */\n function initialize(\n address _swapRouter,\n address _strategistAddr,\n address _treasuryManagerAddr,\n address _rewardsSource,\n uint256 _cvxShareBps\n ) external onlyGovernor initializer {\n _setStrategistAddr(_strategistAddr);\n\n _setSwapRouter(_swapRouter);\n _setRewardsSource(_rewardsSource);\n\n _setTreasuryManager(_treasuryManagerAddr);\n\n _setCVXShareBps(_cvxShareBps);\n }\n\n /**\n * @dev Set address of Uniswap Universal Router for performing liquidation\n * of platform fee tokens. Setting to 0x0 will pause swaps.\n *\n * @param _router Address of the Uniswap Universal router\n */\n function setSwapRouter(address _router) external onlyGovernor {\n _setSwapRouter(_router);\n }\n\n function _setSwapRouter(address _router) internal {\n address oldRouter = swapRouter;\n swapRouter = _router;\n\n if (oldRouter != address(0)) {\n // Remove allowance of old router, if any\n\n if (IERC20(ogn).allowance(address(this), oldRouter) != 0) {\n // slither-disable-next-line unused-return\n IERC20(ogn).safeApprove(oldRouter, 0);\n }\n\n if (IERC20(cvx).allowance(address(this), oldRouter) != 0) {\n // slither-disable-next-line unused-return\n IERC20(cvx).safeApprove(oldRouter, 0);\n }\n }\n\n emit SwapRouterUpdated(_router);\n }\n\n /**\n * @dev Sets the address that receives the OGN buyback rewards\n * @param _address Address\n */\n function setRewardsSource(address _address) external onlyGovernor {\n _setRewardsSource(_address);\n }\n\n function _setRewardsSource(address _address) internal {\n require(_address != address(0), \"Address not set\");\n rewardsSource = _address;\n emit RewardsSourceUpdated(_address);\n }\n\n /**\n * @dev Sets the address that can receive and manage the funds for Treasury\n * @param _address Address\n */\n function setTreasuryManager(address _address) external onlyGovernor {\n _setTreasuryManager(_address);\n }\n\n function _setTreasuryManager(address _address) internal {\n require(_address != address(0), \"Address not set\");\n treasuryManager = _address;\n emit TreasuryManagerUpdated(_address);\n }\n\n /**\n * @dev Sets the percentage of oToken to use for Flywheel tokens\n * @param _bps BPS, 10000 to 100%\n */\n function setCVXShareBps(uint256 _bps) external onlyGovernor {\n _setCVXShareBps(_bps);\n }\n\n function _setCVXShareBps(uint256 _bps) internal {\n require(_bps <= 10000, \"Invalid bps value\");\n cvxShareBps = _bps;\n emit CVXShareBpsUpdated(_bps);\n }\n\n /**\n * @dev Computes the split of oToken balance that can be\n * used for OGN and CVX buybacks.\n */\n function _updateBuybackSplits()\n internal\n returns (uint256 _balanceForOGN, uint256 _balanceForCVX)\n {\n _balanceForOGN = balanceForOGN;\n _balanceForCVX = balanceForCVX;\n\n uint256 totalBalance = IERC20(oToken).balanceOf(address(this));\n uint256 unsplitBalance = totalBalance - _balanceForOGN - _balanceForCVX;\n\n // Check if all balance is accounted for\n if (unsplitBalance != 0) {\n // If not, split unaccounted balance based on `cvxShareBps`\n uint256 addToCVX = (unsplitBalance * cvxShareBps) / 10000;\n _balanceForCVX = _balanceForCVX + addToCVX;\n _balanceForOGN = _balanceForOGN + unsplitBalance - addToCVX;\n\n // Update storage\n balanceForOGN = _balanceForOGN;\n balanceForCVX = _balanceForCVX;\n }\n }\n\n function updateBuybackSplits() external onlyGovernor {\n // slither-disable-next-line unused-return\n _updateBuybackSplits();\n }\n\n function _swapToken(\n address tokenOut,\n uint256 oTokenAmount,\n uint256 minAmountOut,\n bytes calldata swapData\n ) internal returns (uint256 amountOut) {\n require(oTokenAmount > 0, \"Invalid Swap Amount\");\n require(swapRouter != address(0), \"Swap Router not set\");\n require(minAmountOut > 0, \"Invalid minAmount\");\n\n // Transfer OToken to Swapper for swapping\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(oToken).transfer(swapRouter, oTokenAmount);\n\n // Swap\n amountOut = ISwapper(swapRouter).swap(\n oToken,\n tokenOut,\n oTokenAmount,\n minAmountOut,\n swapData\n );\n\n require(amountOut >= minAmountOut, \"Higher Slippage\");\n\n emit OTokenBuyback(oToken, tokenOut, oTokenAmount, amountOut);\n }\n\n /**\n * @dev Swaps `oTokenAmount` to OGN\n * @param oTokenAmount Amount of OUSD/OETH to swap\n * @param minOGN Minimum OGN to receive for oTokenAmount\n * @param swapData 1inch Swap Data\n */\n function swapForOGN(\n uint256 oTokenAmount,\n uint256 minOGN,\n bytes calldata swapData\n ) external onlyGovernorOrStrategist nonReentrant {\n (uint256 _amountForOGN, ) = _updateBuybackSplits();\n require(_amountForOGN >= oTokenAmount, \"Balance underflow\");\n require(rewardsSource != address(0), \"RewardsSource contract not set\");\n\n unchecked {\n // Subtract the amount to swap from net balance\n balanceForOGN = _amountForOGN - oTokenAmount;\n }\n\n uint256 ognReceived = _swapToken(ogn, oTokenAmount, minOGN, swapData);\n\n // Transfer OGN received to RewardsSource contract\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(ogn).transfer(rewardsSource, ognReceived);\n }\n\n /**\n * @dev Swaps `oTokenAmount` to CVX\n * @param oTokenAmount Amount of OUSD/OETH to swap\n * @param minCVX Minimum CVX to receive for oTokenAmount\n * @param swapData 1inch Swap Data\n */\n function swapForCVX(\n uint256 oTokenAmount,\n uint256 minCVX,\n bytes calldata swapData\n ) external onlyGovernorOrStrategist nonReentrant {\n (, uint256 _amountForCVX) = _updateBuybackSplits();\n require(_amountForCVX >= oTokenAmount, \"Balance underflow\");\n\n unchecked {\n // Subtract the amount to swap from net balance\n balanceForCVX = _amountForCVX - oTokenAmount;\n }\n\n uint256 cvxReceived = _swapToken(cvx, oTokenAmount, minCVX, swapData);\n\n // Lock all CVX\n _lockAllCVX(cvxReceived);\n }\n\n /**\n * @dev Locks all CVX held by the contract on behalf of the Treasury Manager\n */\n function lockAllCVX() external onlyGovernorOrStrategist {\n _lockAllCVX(IERC20(cvx).balanceOf(address(this)));\n }\n\n function _lockAllCVX(uint256 cvxAmount) internal {\n require(\n treasuryManager != address(0),\n \"Treasury manager address not set\"\n );\n\n // Lock all available CVX on behalf of `treasuryManager`\n ICVXLocker(cvxLocker).lock(treasuryManager, cvxAmount, 0);\n }\n\n /**\n * @dev Approve CVX Locker to move CVX held by this contract\n */\n function safeApproveAllTokens() external onlyGovernorOrStrategist {\n IERC20(cvx).safeApprove(cvxLocker, type(uint256).max);\n }\n\n /**\n * @notice Owner function to withdraw a specific amount of a token\n * @param token token to be transferered\n * @param amount amount of the token to be transferred\n */\n function transferToken(address token, uint256 amount)\n external\n onlyGovernor\n nonReentrant\n {\n IERC20(token).safeTransfer(_governor(), amount);\n }\n}\n" + }, + "contracts/buyback/ARMBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract ARMBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/buyback/OETHBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract OETHBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/buyback/OUSDBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract OUSDBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/echidna/Debugger.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary Debugger {\n event Debug(string debugString);\n event Debug(string description, string data);\n event Debug(string prefix, string description, string data);\n event Debug(string description, bytes32 data);\n event Debug(string prefix, string description, bytes32 data);\n event Debug(string description, uint256 data);\n event Debug(string prefix, string description, uint256 data);\n event Debug(string description, int256 data);\n event Debug(string prefix, string description, int256 data);\n event Debug(string description, address data);\n event Debug(string prefix, string description, address data);\n event Debug(string description, bool data);\n event Debug(string prefix, string description, bool data);\n\n function log(string memory debugString) internal {\n emit Debug(debugString);\n }\n\n function log(string memory description, string memory data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n string memory data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, bytes32 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n bytes32 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, uint256 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n uint256 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, int256 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n int256 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, address data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n address data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, bool data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n bool data\n ) internal {\n emit Debug(prefix, description, data);\n }\n}\n" + }, + "contracts/echidna/Echidna.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaTestApproval.sol\";\n\n/**\n * @title Echidna test contract for OUSD\n * @notice Target contract to be tested, containing all mixins\n * @author Rappie\n */\ncontract Echidna is EchidnaTestApproval {\n\n}\n" + }, + "contracts/echidna/EchidnaConfig.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Top-level mixin for configuring the desired fuzzing setup\n * @author Rappie\n */\ncontract EchidnaConfig {\n address internal constant ADDRESS_VAULT = address(0x10000);\n address internal constant ADDRESS_OUTSIDER_USER = address(0x20000);\n\n address internal constant ADDRESS_USER0 = address(0x30000);\n address internal constant ADDRESS_USER1 = address(0x40000);\n\n // Will be set in EchidnaSetup constructor\n address internal ADDRESS_OUTSIDER_CONTRACT;\n address internal ADDRESS_CONTRACT0;\n address internal ADDRESS_CONTRACT1;\n\n // Toggle known issues\n //\n // This can be used to skip tests that are known to fail. This is useful\n // when debugging a specific issue, but should be disabled when running\n // the full test suite.\n //\n // True => skip tests that are known to fail\n // False => run all tests\n //\n bool internal constant TOGGLE_KNOWN_ISSUES = false;\n\n // Toggle known issues within limits\n //\n // Same as TOGGLE_KNOWN_ISSUES, but also skip tests that are known to fail\n // within limits set by the variables below.\n //\n bool internal constant TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS = true;\n\n // Starting balance\n //\n // Gives OUSD a non-zero starting supply, which can be useful to ignore\n // certain edge cases.\n //\n // The starting balance is given to outsider accounts that are not used as\n // accounts while fuzzing.\n //\n bool internal constant TOGGLE_STARTING_BALANCE = true;\n uint256 internal constant STARTING_BALANCE = 1_000_000e18;\n\n // Change supply\n //\n // Set a limit to the amount of change per rebase, which can be useful to\n // ignore certain edge cases.\n //\n // True => limit the amount of change to a percentage of total supply\n // False => no limit\n //\n bool internal constant TOGGLE_CHANGESUPPLY_LIMIT = true;\n uint256 internal constant CHANGESUPPLY_DIVISOR = 10; // 10% of total supply\n\n // Mint limit\n //\n // Set a limit the amount minted per mint, which can be useful to\n // ignore certain edge cases.\n //\n // True => limit the amount of minted tokens\n // False => no limit\n //\n bool internal constant TOGGLE_MINT_LIMIT = true;\n uint256 internal constant MINT_MODULO = 1_000_000_000_000e18;\n\n // Known rounding errors\n uint256 internal constant TRANSFER_ROUNDING_ERROR = 1e18 - 1;\n uint256 internal constant OPT_IN_ROUNDING_ERROR = 1e18 - 1;\n uint256 internal constant MINT_ROUNDING_ERROR = 1e18 - 1;\n\n /**\n * @notice Modifier to skip tests that are known to fail\n * @dev see TOGGLE_KNOWN_ISSUES for more information\n */\n modifier hasKnownIssue() {\n if (TOGGLE_KNOWN_ISSUES) return;\n _;\n }\n\n /**\n * @notice Modifier to skip tests that are known to fail within limits\n * @dev see TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS for more information\n */\n modifier hasKnownIssueWithinLimits() {\n if (TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS) return;\n _;\n }\n\n /**\n * @notice Translate an account ID to an address\n * @param accountId The ID of the account\n * @return account The address of the account\n */\n function getAccount(uint8 accountId)\n internal\n view\n returns (address account)\n {\n accountId = accountId / 64;\n if (accountId == 0) return account = ADDRESS_USER0;\n if (accountId == 1) return account = ADDRESS_USER1;\n if (accountId == 2) return account = ADDRESS_CONTRACT0;\n if (accountId == 3) return account = ADDRESS_CONTRACT1;\n require(false, \"Unknown account ID\");\n }\n}\n" + }, + "contracts/echidna/EchidnaDebug.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport \"./EchidnaHelper.sol\";\nimport \"./Debugger.sol\";\n\nimport \"../token/OUSD.sol\";\n\n/**\n * @title Room for random debugging functions\n * @author Rappie\n */\ncontract EchidnaDebug is EchidnaHelper {\n function debugOUSD() public pure {\n // assert(ousd.balanceOf(ADDRESS_USER0) == 1000);\n // assert(ousd.rebaseState(ADDRESS_USER0) != OUSD.RebaseOptions.OptIn);\n // assert(Address.isContract(ADDRESS_CONTRACT0));\n // Debugger.log(\"nonRebasingSupply\", ousd.nonRebasingSupply());\n // assert(false);\n }\n}\n" + }, + "contracts/echidna/EchidnaHelper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaSetup.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin containing helper functions\n * @author Rappie\n */\ncontract EchidnaHelper is EchidnaSetup {\n /**\n * @notice Mint tokens to an account\n * @param toAcc Account to mint to\n * @param amount Amount to mint\n * @return Amount minted (in case of capped mint with modulo)\n */\n function mint(uint8 toAcc, uint256 amount) public returns (uint256) {\n address to = getAccount(toAcc);\n\n if (TOGGLE_MINT_LIMIT) {\n amount = amount % MINT_MODULO;\n }\n\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(to, amount);\n\n return amount;\n }\n\n /**\n * @notice Burn tokens from an account\n * @param fromAcc Account to burn from\n * @param amount Amount to burn\n */\n function burn(uint8 fromAcc, uint256 amount) public {\n address from = getAccount(fromAcc);\n hevm.prank(ADDRESS_VAULT);\n ousd.burn(from, amount);\n }\n\n /**\n * @notice Change the total supply of OUSD (rebase)\n * @param amount New total supply\n */\n function changeSupply(uint256 amount) public {\n if (TOGGLE_CHANGESUPPLY_LIMIT) {\n amount =\n ousd.totalSupply() +\n (amount % (ousd.totalSupply() / CHANGESUPPLY_DIVISOR));\n }\n\n hevm.prank(ADDRESS_VAULT);\n ousd.changeSupply(amount);\n }\n\n /**\n * @notice Transfer tokens between accounts\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function transfer(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n ousd.transfer(to, amount);\n }\n\n /**\n * @notice Transfer approved tokens between accounts\n * @param authorizedAcc Account that is authorized to transfer\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function transferFrom(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n ousd.transferFrom(from, to, amount);\n }\n\n /**\n * @notice Opt in to rebasing\n * @param targetAcc Account to opt in\n */\n function optIn(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n hevm.prank(target);\n ousd.rebaseOptIn();\n }\n\n /**\n * @notice Opt out of rebasing\n * @param targetAcc Account to opt out\n */\n function optOut(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n hevm.prank(target);\n ousd.rebaseOptOut();\n }\n\n /**\n * @notice Approve an account to spend OUSD\n * @param ownerAcc Account that owns the OUSD\n * @param spenderAcc Account that is approved to spend the OUSD\n * @param amount Amount to approve\n */\n function approve(\n uint8 ownerAcc,\n uint8 spenderAcc,\n uint256 amount\n ) public {\n address owner = getAccount(ownerAcc);\n address spender = getAccount(spenderAcc);\n hevm.prank(owner);\n // slither-disable-next-line unused-return\n ousd.approve(spender, amount);\n }\n\n /**\n * @notice Get the sum of all OUSD balances\n * @return total Total balance\n */\n function getTotalBalance() public view returns (uint256 total) {\n total += ousd.balanceOf(ADDRESS_VAULT);\n total += ousd.balanceOf(ADDRESS_OUTSIDER_USER);\n total += ousd.balanceOf(ADDRESS_OUTSIDER_CONTRACT);\n total += ousd.balanceOf(ADDRESS_USER0);\n total += ousd.balanceOf(ADDRESS_USER1);\n total += ousd.balanceOf(ADDRESS_CONTRACT0);\n total += ousd.balanceOf(ADDRESS_CONTRACT1);\n }\n\n /**\n * @notice Get the sum of all non-rebasing OUSD balances\n * @return total Total balance\n */\n function getTotalNonRebasingBalance() public returns (uint256 total) {\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_VAULT)\n ? ousd.balanceOf(ADDRESS_VAULT)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_OUTSIDER_USER)\n ? ousd.balanceOf(ADDRESS_OUTSIDER_USER)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_OUTSIDER_CONTRACT)\n ? ousd.balanceOf(ADDRESS_OUTSIDER_CONTRACT)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_USER0)\n ? ousd.balanceOf(ADDRESS_USER0)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_USER1)\n ? ousd.balanceOf(ADDRESS_USER1)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_CONTRACT0)\n ? ousd.balanceOf(ADDRESS_CONTRACT0)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_CONTRACT1)\n ? ousd.balanceOf(ADDRESS_CONTRACT1)\n : 0;\n }\n}\n" + }, + "contracts/echidna/EchidnaSetup.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./IHevm.sol\";\nimport \"./EchidnaConfig.sol\";\nimport \"./OUSDEchidna.sol\";\n\ncontract Dummy {}\n\n/**\n * @title Mixin for setup and deployment\n * @author Rappie\n */\ncontract EchidnaSetup is EchidnaConfig {\n IHevm hevm = IHevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);\n OUSDEchidna ousd = new OUSDEchidna();\n\n /**\n * @notice Deploy the OUSD contract and set up initial state\n */\n constructor() {\n ousd.initialize(ADDRESS_VAULT, 1e18);\n\n // Deploy dummny contracts as users\n Dummy outsider = new Dummy();\n ADDRESS_OUTSIDER_CONTRACT = address(outsider);\n Dummy dummy0 = new Dummy();\n ADDRESS_CONTRACT0 = address(dummy0);\n Dummy dummy1 = new Dummy();\n ADDRESS_CONTRACT1 = address(dummy1);\n\n // Start out with a reasonable amount of OUSD\n if (TOGGLE_STARTING_BALANCE) {\n // Rebasing tokens\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(ADDRESS_OUTSIDER_USER, STARTING_BALANCE / 2);\n\n // Non-rebasing tokens\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(ADDRESS_OUTSIDER_CONTRACT, STARTING_BALANCE / 2);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestAccounting.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestSupply.sol\";\n\n/**\n * @title Mixin for testing accounting functions\n * @author Rappie\n */\ncontract EchidnaTestAccounting is EchidnaTestSupply {\n /**\n * @notice After opting in, balance should not increase. (Ok to lose rounding funds doing this)\n * @param targetAcc Account to opt in\n */\n function testOptInBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optIn(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter <= balanceBefore);\n }\n\n /**\n * @notice After opting out, balance should remain the same\n * @param targetAcc Account to opt out\n */\n function testOptOutBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optOut(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Account balance should remain the same after opting in minus rounding error\n * @param targetAcc Account to opt in\n */\n function testOptInBalanceRounding(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optIn(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n int256 delta = int256(balanceAfter) - int256(balanceBefore);\n Debugger.log(\"delta\", delta);\n\n // slither-disable-next-line tautology\n assert(-1 * delta >= 0);\n assert(-1 * delta <= int256(OPT_IN_ROUNDING_ERROR));\n }\n\n /**\n * @notice After opting in, total supply should remain the same\n * @param targetAcc Account to opt in\n */\n function testOptInTotalSupply(uint8 targetAcc) public {\n uint256 totalSupplyBefore = ousd.totalSupply();\n optIn(targetAcc);\n uint256 totalSupplyAfter = ousd.totalSupply();\n\n assert(totalSupplyAfter == totalSupplyBefore);\n }\n\n /**\n * @notice After opting out, total supply should remain the same\n * @param targetAcc Account to opt out\n */\n function testOptOutTotalSupply(uint8 targetAcc) public {\n uint256 totalSupplyBefore = ousd.totalSupply();\n optOut(targetAcc);\n uint256 totalSupplyAfter = ousd.totalSupply();\n\n assert(totalSupplyAfter == totalSupplyBefore);\n }\n\n /**\n * @notice Account balance should remain the same when a smart contract auto converts\n * @param targetAcc Account to auto convert\n */\n function testAutoConvertBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n // slither-disable-next-line unused-return\n ousd._isNonRebasingAccountEchidna(target);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice The `balanceOf` function should never revert\n * @param targetAcc Account to check balance of\n */\n function testBalanceOfShouldNotRevert(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n // slither-disable-next-line unused-return\n try ousd.balanceOf(target) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestApproval.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaTestMintBurn.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin for testing approval related functions\n * @author Rappie\n */\ncontract EchidnaTestApproval is EchidnaTestMintBurn {\n /**\n * @notice Performing `transferFrom` with an amount inside the allowance should not revert\n * @param authorizedAcc The account that is authorized to transfer\n * @param fromAcc The account that is transferring\n * @param toAcc The account that is receiving\n * @param amount The amount to transfer\n */\n function testTransferFromShouldNotRevert(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount <= ousd.balanceOf(from));\n require(amount <= ousd.allowance(from, authorized));\n\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n try ousd.transferFrom(from, to, amount) {\n // pass\n } catch {\n assert(false);\n }\n }\n\n /**\n * @notice Performing `transferFrom` with an amount outside the allowance should revert\n * @param authorizedAcc The account that is authorized to transfer\n * @param fromAcc The account that is transferring\n * @param toAcc The account that is receiving\n * @param amount The amount to transfer\n */\n function testTransferFromShouldRevert(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount > 0);\n require(\n !(amount <= ousd.balanceOf(from) &&\n amount <= ousd.allowance(from, authorized))\n );\n\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n try ousd.transferFrom(from, to, amount) {\n assert(false);\n } catch {\n // pass\n }\n }\n\n /**\n * @notice Approving an amount should update the allowance and overwrite any previous allowance\n * @param ownerAcc The account that is approving\n * @param spenderAcc The account that is being approved\n * @param amount The amount to approve\n */\n function testApprove(\n uint8 ownerAcc,\n uint8 spenderAcc,\n uint256 amount\n ) public {\n address owner = getAccount(ownerAcc);\n address spender = getAccount(spenderAcc);\n\n approve(ownerAcc, spenderAcc, amount);\n uint256 allowanceAfter1 = ousd.allowance(owner, spender);\n\n assert(allowanceAfter1 == amount);\n\n approve(ownerAcc, spenderAcc, amount / 2);\n uint256 allowanceAfter2 = ousd.allowance(owner, spender);\n\n assert(allowanceAfter2 == amount / 2);\n }\n}\n" + }, + "contracts/echidna/EchidnaTestMintBurn.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestAccounting.sol\";\n\n/**\n * @title Mixin for testing Mint and Burn functions\n * @author Rappie\n */\ncontract EchidnaTestMintBurn is EchidnaTestAccounting {\n /**\n * @notice Minting 0 tokens should not affect account balance\n * @param targetAcc Account to mint to\n */\n function testMintZeroBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n mint(targetAcc, 0);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Burning 0 tokens should not affect account balance\n * @param targetAcc Account to burn from\n */\n function testBurnZeroBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n burn(targetAcc, 0);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Minting tokens must increase the account balance by at least amount\n * @param targetAcc Account to mint to\n * @param amount Amount to mint\n * @custom:error testMintBalance(uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * testMintBalance(0,1)\n * Event sequence:\n * Debug(«balanceBefore», 0)\n * Debug(«balanceAfter», 0)\n */\n function testMintBalance(uint8 targetAcc, uint256 amount)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n uint256 amountMinted = mint(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n Debugger.log(\"amountMinted\", amountMinted);\n Debugger.log(\"balanceBefore\", balanceBefore);\n Debugger.log(\"balanceAfter\", balanceAfter);\n\n assert(balanceAfter >= balanceBefore + amountMinted);\n }\n\n /**\n * @notice Burning tokens must decrease the account balance by at least amount\n * @param targetAcc Account to burn from\n * @param amount Amount to burn\n * @custom:error testBurnBalance(uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * mint(0,3)\n * testBurnBalance(0,1)\n * Event sequence:\n * Debug(«balanceBefore», 2)\n * Debug(«balanceAfter», 2)\n */\n function testBurnBalance(uint8 targetAcc, uint256 amount)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n burn(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n Debugger.log(\"balanceBefore\", balanceBefore);\n Debugger.log(\"balanceAfter\", balanceAfter);\n\n assert(balanceAfter <= balanceBefore - amount);\n }\n\n /**\n * @notice Minting tokens should not increase the account balance by less than rounding error above amount\n * @param targetAcc Account to mint to\n * @param amount Amount to mint\n */\n function testMintBalanceRounding(uint8 targetAcc, uint256 amount) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n uint256 amountMinted = mint(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n int256 delta = int256(balanceAfter) - int256(balanceBefore);\n\n // delta == amount, if no error\n // delta < amount, if too little is minted\n // delta > amount, if too much is minted\n int256 error = int256(amountMinted) - delta;\n\n assert(error >= 0);\n assert(error <= int256(MINT_ROUNDING_ERROR));\n }\n\n /**\n * @notice A burn of an account balance must result in a zero balance\n * @param targetAcc Account to burn from\n */\n function testBurnAllBalanceToZero(uint8 targetAcc) public hasKnownIssue {\n address target = getAccount(targetAcc);\n\n burn(targetAcc, ousd.balanceOf(target));\n assert(ousd.balanceOf(target) == 0);\n }\n\n /**\n * @notice You should always be able to burn an account's balance\n * @param targetAcc Account to burn from\n */\n function testBurnAllBalanceShouldNotRevert(uint8 targetAcc)\n public\n hasKnownIssue\n {\n address target = getAccount(targetAcc);\n uint256 balance = ousd.balanceOf(target);\n\n hevm.prank(ADDRESS_VAULT);\n try ousd.burn(target, balance) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestSupply.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestTransfer.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n/**\n * @title Mixin for testing supply related functions\n * @author Rappie\n */\ncontract EchidnaTestSupply is EchidnaTestTransfer {\n using StableMath for uint256;\n\n uint256 prevRebasingCreditsPerToken = type(uint256).max;\n\n /**\n * @notice After a `changeSupply`, the total supply should exactly\n * match the target total supply. (This is needed to ensure successive\n * rebases are correct).\n * @param supply New total supply\n * @custom:error testChangeSupply(uint256): failed!💥\n * Call sequence:\n * testChangeSupply(1044505275072865171609)\n * Event sequence:\n * TotalSupplyUpdatedHighres(1044505275072865171610, 1000000000000000000000000, 957391048054055578595)\n */\n function testChangeSupply(uint256 supply)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n hevm.prank(ADDRESS_VAULT);\n ousd.changeSupply(supply);\n\n assert(ousd.totalSupply() == supply);\n }\n\n /**\n * @notice The total supply must not be less than the sum of account balances.\n * (The difference will go into future rebases)\n * @custom:error testTotalSupplyLessThanTotalBalance(): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(1)\n * optOut(64)\n * transfer(0,64,1)\n * testTotalSupplyLessThanTotalBalance()\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000001000001)\n * Debug(«totalBalance», 1000000000000000001000002)\n */\n function testTotalSupplyLessThanTotalBalance()\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n uint256 totalSupply = ousd.totalSupply();\n uint256 totalBalance = getTotalBalance();\n\n Debugger.log(\"totalSupply\", totalSupply);\n Debugger.log(\"totalBalance\", totalBalance);\n\n assert(totalSupply >= totalBalance);\n }\n\n /**\n * @notice Non-rebasing supply should not be larger than total supply\n * @custom:error testNonRebasingSupplyVsTotalSupply(): failed!💥\n * Call sequence:\n * mint(0,2)\n * changeSupply(3)\n * burn(0,1)\n * optOut(0)\n * testNonRebasingSupplyVsTotalSupply()\n */\n function testNonRebasingSupplyVsTotalSupply() public hasKnownIssue {\n uint256 nonRebasingSupply = ousd.nonRebasingSupply();\n uint256 totalSupply = ousd.totalSupply();\n\n assert(nonRebasingSupply <= totalSupply);\n }\n\n /**\n * @notice Global `rebasingCreditsPerToken` should never increase\n * @custom:error testRebasingCreditsPerTokenNotIncreased(): failed!💥\n * Call sequence:\n * testRebasingCreditsPerTokenNotIncreased()\n * changeSupply(1)\n * testRebasingCreditsPerTokenNotIncreased()\n */\n function testRebasingCreditsPerTokenNotIncreased() public hasKnownIssue {\n uint256 curRebasingCreditsPerToken = ousd\n .rebasingCreditsPerTokenHighres();\n\n Debugger.log(\n \"prevRebasingCreditsPerToken\",\n prevRebasingCreditsPerToken\n );\n Debugger.log(\"curRebasingCreditsPerToken\", curRebasingCreditsPerToken);\n\n assert(curRebasingCreditsPerToken <= prevRebasingCreditsPerToken);\n\n prevRebasingCreditsPerToken = curRebasingCreditsPerToken;\n }\n\n /**\n * @notice The rebasing credits per token ratio must greater than zero\n */\n function testRebasingCreditsPerTokenAboveZero() public {\n assert(ousd.rebasingCreditsPerTokenHighres() > 0);\n }\n\n /**\n * @notice The sum of all non-rebasing balances should not be larger than\n * non-rebasing supply\n * @custom:error testTotalNonRebasingSupplyLessThanTotalBalance(): failed!💥\n * Call sequence\n * mint(0,2)\n * changeSupply(1)\n * optOut(0)\n * burn(0,1)\n * testTotalNonRebasingSupplyLessThanTotalBalance()\n * Event sequence:\n * Debug(«totalNonRebasingSupply», 500000000000000000000001)\n * Debug(«totalNonRebasingBalance», 500000000000000000000002)\n */\n function testTotalNonRebasingSupplyLessThanTotalBalance()\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n uint256 totalNonRebasingSupply = ousd.nonRebasingSupply();\n uint256 totalNonRebasingBalance = getTotalNonRebasingBalance();\n\n Debugger.log(\"totalNonRebasingSupply\", totalNonRebasingSupply);\n Debugger.log(\"totalNonRebasingBalance\", totalNonRebasingBalance);\n\n assert(totalNonRebasingSupply >= totalNonRebasingBalance);\n }\n\n /**\n * @notice An accounts credits / credits per token should not be larger it's balance\n * @param targetAcc The account to check\n */\n function testCreditsPerTokenVsBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n (uint256 credits, uint256 creditsPerToken, ) = ousd\n .creditsBalanceOfHighres(target);\n uint256 expectedBalance = credits.divPrecisely(creditsPerToken);\n\n uint256 balance = ousd.balanceOf(target);\n\n Debugger.log(\"credits\", credits);\n Debugger.log(\"creditsPerToken\", creditsPerToken);\n Debugger.log(\"expectedBalance\", expectedBalance);\n Debugger.log(\"balance\", balance);\n\n assert(expectedBalance == balance);\n }\n}\n" + }, + "contracts/echidna/EchidnaTestTransfer.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin for testing transfer related functions\n * @author Rappie\n */\ncontract EchidnaTestTransfer is EchidnaDebug {\n /**\n * @notice The receiving account's balance after a transfer must not increase by\n * less than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferBalanceReceivedLess(uint8,uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * mint(64,2)\n * testTransferBalanceReceivedLess(64,0,1)\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000000500002)\n * Debug(«toBalBefore», 0)\n * Debug(«toBalAfter», 0)\n */\n function testTransferBalanceReceivedLess(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue hasKnownIssueWithinLimits {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n\n assert(toBalAfter >= toBalBefore + amount);\n }\n\n /**\n * @notice The receiving account's balance after a transfer must not\n * increase by more than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceReceivedMore(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n\n assert(toBalAfter <= toBalBefore + amount);\n }\n\n /**\n * @notice The sending account's balance after a transfer must not\n * decrease by less than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferBalanceSentLess(uint8,uint8,uint256): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(1)\n * testTransferBalanceSentLess(0,64,1)\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000000500001)\n * Debug(«fromBalBefore», 1)\n * Debug(«fromBalAfter», 1)\n */\n function testTransferBalanceSentLess(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue hasKnownIssueWithinLimits {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n\n assert(fromBalAfter <= fromBalBefore - amount);\n }\n\n /**\n * @notice The sending account's balance after a transfer must not\n * decrease by more than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceSentMore(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n\n assert(fromBalAfter >= fromBalBefore - amount);\n }\n\n /**\n * @notice The receiving account's balance after a transfer must not\n * increase by less than the amount transferred (minus rounding error)\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceReceivedLessRounding(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n int256 toDelta = int256(toBalAfter) - int256(toBalBefore);\n\n // delta == amount, if no error\n // delta < amount, if too little is sent\n // delta > amount, if too much is sent\n int256 error = int256(amount) - toDelta;\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n Debugger.log(\"toDelta\", toDelta);\n Debugger.log(\"error\", error);\n\n assert(error >= 0);\n assert(error <= int256(TRANSFER_ROUNDING_ERROR));\n }\n\n /**\n * @notice The sending account's balance after a transfer must\n * not decrease by less than the amount transferred (minus rounding error)\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceSentLessRounding(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n int256 fromDelta = int256(fromBalAfter) - int256(fromBalBefore);\n\n // delta == -amount, if no error\n // delta < -amount, if too much is sent\n // delta > -amount, if too little is sent\n int256 error = int256(amount) + fromDelta;\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n Debugger.log(\"fromDelta\", fromDelta);\n Debugger.log(\"error\", error);\n\n assert(error >= 0);\n assert(error <= int256(TRANSFER_ROUNDING_ERROR));\n }\n\n /**\n * @notice An account should always be able to successfully transfer\n * an amount within its balance.\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferWithinBalanceDoesNotRevert(uint8,uint8,uint8): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(3)\n * optOut(0)\n * testTransferWithinBalanceDoesNotRevert(0,128,2)\n * optIn(0)\n * testTransferWithinBalanceDoesNotRevert(128,0,1)\n * Event sequence:\n * error Revert Panic(17): SafeMath over-/under-flows\n */\n function testTransferWithinBalanceDoesNotRevert(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount > 0);\n amount = amount % ousd.balanceOf(from);\n\n Debugger.log(\"Total supply\", ousd.totalSupply());\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(to, amount) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n\n /**\n * @notice An account should never be able to successfully transfer\n * an amount greater than their balance.\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferExceedingBalanceReverts(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n amount = ousd.balanceOf(from) + 1 + amount;\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(to, amount) {\n assert(false);\n } catch {\n assert(true);\n }\n }\n\n /**\n * @notice A transfer to the same account should not change that account's balance\n * @param targetAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferSelf(uint8 targetAcc, uint256 amount) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n transfer(targetAcc, targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceBefore == balanceAfter);\n }\n\n /**\n * @notice Transfers to the zero account revert\n * @param fromAcc Account to transfer from\n * @param amount Amount to transfer\n */\n function testTransferToZeroAddress(uint8 fromAcc, uint256 amount) public {\n address from = getAccount(fromAcc);\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(address(0), amount) {\n assert(false);\n } catch {\n assert(true);\n }\n }\n}\n" + }, + "contracts/echidna/IHevm.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// https://github.com/ethereum/hevm/blob/main/doc/src/controlling-the-unit-testing-environment.md#cheat-codes\n\ninterface IHevm {\n function warp(uint256 x) external;\n\n function roll(uint256 x) external;\n\n function store(\n address c,\n bytes32 loc,\n bytes32 val\n ) external;\n\n function load(address c, bytes32 loc) external returns (bytes32 val);\n\n function sign(uint256 sk, bytes32 digest)\n external\n returns (\n uint8 v,\n bytes32 r,\n bytes32 s\n );\n\n function addr(uint256 sk) external returns (address addr);\n\n function ffi(string[] calldata) external returns (bytes memory);\n\n function prank(address sender) external;\n}\n" + }, + "contracts/echidna/OUSDEchidna.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../token/OUSD.sol\";\n\ncontract OUSDEchidna is OUSD {\n constructor() OUSD() {}\n\n function _isNonRebasingAccountEchidna(address _account)\n public\n returns (bool)\n {\n _autoMigrate(_account);\n return alternativeCreditsPerToken[_account] > 0;\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/governance/InitializableGovernable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD InitializableGovernable Contract\n * @author Origin Protocol Inc\n */\nimport { Initializable } from \"../utils/Initializable.sol\";\n\nimport { Governable } from \"./Governable.sol\";\n\ncontract InitializableGovernable is Governable, Initializable {\n function _initialize(address _newGovernor) internal {\n _changeGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/governance/Strategizable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"./Governable.sol\";\n\ncontract Strategizable is Governable {\n event StrategistUpdated(address _address);\n\n // Address of strategist\n address public strategistAddr;\n\n // For future use\n uint256[50] private __gap;\n\n /**\n * @dev Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @dev Set address of Strategist\n * @param _address Address of Strategist\n */\n function setStrategistAddr(address _address) external onlyGovernor {\n _setStrategistAddr(_address);\n }\n\n /**\n * @dev Set address of Strategist\n * @param _address Address of Strategist\n */\n function _setStrategistAddr(address _address) internal {\n strategistAddr = _address;\n emit StrategistUpdated(_address);\n }\n}\n" + }, + "contracts/harvest/AbstractHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IUniswapV2Router } from \"../interfaces/uniswap/IUniswapV2Router02.sol\";\nimport { IUniswapV3Router } from \"../interfaces/uniswap/IUniswapV3Router.sol\";\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { ICurvePool } from \"../strategies/ICurvePool.sol\";\nimport \"../utils/Helpers.sol\";\n\nabstract contract AbstractHarvester is Governable {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using StableMath for uint256;\n\n enum SwapPlatform {\n UniswapV2Compatible,\n UniswapV3,\n Balancer,\n Curve\n }\n\n event SupportedStrategyUpdate(address strategyAddress, bool isSupported);\n event RewardTokenConfigUpdated(\n address tokenAddress,\n uint16 allowedSlippageBps,\n uint16 harvestRewardBps,\n SwapPlatform swapPlatform,\n address swapPlatformAddr,\n bytes swapData,\n uint256 liquidationLimit,\n bool doSwapRewardToken\n );\n event RewardTokenSwapped(\n address indexed rewardToken,\n address indexed swappedInto,\n SwapPlatform swapPlatform,\n uint256 amountIn,\n uint256 amountOut\n );\n event RewardProceedsTransferred(\n address indexed token,\n address farmer,\n uint256 protcolYield,\n uint256 farmerFee\n );\n event RewardProceedsAddressChanged(address newProceedsAddress);\n\n error EmptyAddress();\n error InvalidSlippageBps();\n error InvalidHarvestRewardBps();\n\n error InvalidSwapPlatform(SwapPlatform swapPlatform);\n\n error InvalidUniswapV2PathLength();\n error InvalidTokenInSwapPath(address token);\n error EmptyBalancerPoolId();\n error InvalidCurvePoolAssetIndex(address token);\n\n error UnsupportedStrategy(address strategyAddress);\n\n error SlippageError(uint256 actualBalance, uint256 minExpected);\n error BalanceMismatchAfterSwap(uint256 actualBalance, uint256 minExpected);\n\n // Configuration properties for harvesting logic of reward tokens\n struct RewardTokenConfig {\n // Max allowed slippage when swapping reward token for a stablecoin denominated in basis points.\n uint16 allowedSlippageBps;\n // Reward when calling a harvest function denominated in basis points.\n uint16 harvestRewardBps;\n // Address of compatible exchange protocol (Uniswap V2/V3, SushiSwap, Balancer and Curve).\n address swapPlatformAddr;\n /* When true the reward token is being swapped. In a need of (temporarily) disabling the swapping of\n * a reward token this needs to be set to false.\n */\n bool doSwapRewardToken;\n // Platform to use for Swapping\n SwapPlatform swapPlatform;\n /* How much token can be sold per one harvest call. If the balance of rewards tokens\n * exceeds that limit multiple harvest calls are required to harvest all of the tokens.\n * Set it to MAX_INT to effectively disable the limit.\n */\n uint256 liquidationLimit;\n }\n\n mapping(address => RewardTokenConfig) public rewardTokenConfigs;\n mapping(address => bool) public supportedStrategies;\n\n address public immutable vaultAddress;\n\n /**\n * Address receiving rewards proceeds. Initially the Vault contract later will possibly\n * be replaced by another contract that eases out rewards distribution.\n **/\n address public rewardProceedsAddress;\n\n /**\n * All tokens are swapped to this token before it gets transferred\n * to the `rewardProceedsAddress`. USDT for OUSD and WETH for OETH.\n **/\n address public immutable baseTokenAddress;\n // Cached decimals for `baseTokenAddress`\n uint256 public immutable baseTokenDecimals;\n\n // Uniswap V2 path for reward tokens using Uniswap V2 Router\n mapping(address => address[]) public uniswapV2Path;\n // Uniswap V3 path for reward tokens using Uniswap V3 Router\n mapping(address => bytes) public uniswapV3Path;\n // Pool ID to use for reward tokens on Balancer\n mapping(address => bytes32) public balancerPoolId;\n\n struct CurvePoolIndices {\n // Casted into uint128 and stored in a struct to save gas\n uint128 rewardTokenIndex;\n uint128 baseTokenIndex;\n }\n // Packed indices of assets on the Curve pool\n mapping(address => CurvePoolIndices) public curvePoolIndices;\n\n constructor(address _vaultAddress, address _baseTokenAddress) {\n require(_vaultAddress != address(0));\n require(_baseTokenAddress != address(0));\n\n vaultAddress = _vaultAddress;\n baseTokenAddress = _baseTokenAddress;\n\n // Cache decimals as well\n baseTokenDecimals = Helpers.getDecimals(_baseTokenAddress);\n }\n\n /***************************************\n Configuration\n ****************************************/\n\n /**\n * Set the Address receiving rewards proceeds.\n * @param _rewardProceedsAddress Address of the reward token\n */\n function setRewardProceedsAddress(address _rewardProceedsAddress)\n external\n onlyGovernor\n {\n if (_rewardProceedsAddress == address(0)) {\n revert EmptyAddress();\n }\n\n rewardProceedsAddress = _rewardProceedsAddress;\n emit RewardProceedsAddressChanged(_rewardProceedsAddress);\n }\n\n /**\n * @dev Add/update a reward token configuration that holds harvesting config variables\n * @param _tokenAddress Address of the reward token\n * @param tokenConfig.allowedSlippageBps uint16 maximum allowed slippage denominated in basis points.\n * Example: 300 == 3% slippage\n * @param tokenConfig.harvestRewardBps uint16 amount of reward tokens the caller of the function is rewarded.\n * Example: 100 == 1%\n * @param tokenConfig.swapPlatformAddr Address Address of a UniswapV2 compatible contract to perform\n * the exchange from reward tokens to stablecoin (currently hard-coded to USDT)\n * @param tokenConfig.liquidationLimit uint256 Maximum amount of token to be sold per one swap function call.\n * When value is 0 there is no limit.\n * @param tokenConfig.doSwapRewardToken bool Disables swapping of the token when set to true,\n * does not cause it to revert though.\n * @param tokenConfig.swapPlatform SwapPlatform to use for Swapping\n * @param swapData Additional data required for swapping\n */\n function setRewardTokenConfig(\n address _tokenAddress,\n RewardTokenConfig calldata tokenConfig,\n bytes calldata swapData\n ) external onlyGovernor {\n if (tokenConfig.allowedSlippageBps > 1000) {\n revert InvalidSlippageBps();\n }\n\n if (tokenConfig.harvestRewardBps > 1000) {\n revert InvalidHarvestRewardBps();\n }\n\n address newRouterAddress = tokenConfig.swapPlatformAddr;\n if (newRouterAddress == address(0)) {\n // Swap router address should be non zero address\n revert EmptyAddress();\n }\n\n address oldRouterAddress = rewardTokenConfigs[_tokenAddress]\n .swapPlatformAddr;\n rewardTokenConfigs[_tokenAddress] = tokenConfig;\n\n // Revert if feed does not exist\n // slither-disable-next-line unused-return\n IOracle(IVault(vaultAddress).priceProvider()).price(_tokenAddress);\n\n IERC20 token = IERC20(_tokenAddress);\n // if changing token swap provider cancel existing allowance\n if (\n /* oldRouterAddress == address(0) when there is no pre-existing\n * configuration for said rewards token\n */\n oldRouterAddress != address(0) &&\n oldRouterAddress != newRouterAddress\n ) {\n token.safeApprove(oldRouterAddress, 0);\n }\n\n // Give SwapRouter infinite approval when needed\n if (oldRouterAddress != newRouterAddress) {\n token.safeApprove(newRouterAddress, 0);\n token.safeApprove(newRouterAddress, type(uint256).max);\n }\n\n SwapPlatform _platform = tokenConfig.swapPlatform;\n if (_platform == SwapPlatform.UniswapV2Compatible) {\n uniswapV2Path[_tokenAddress] = _decodeUniswapV2Path(\n swapData,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.UniswapV3) {\n uniswapV3Path[_tokenAddress] = _decodeUniswapV3Path(\n swapData,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.Balancer) {\n balancerPoolId[_tokenAddress] = _decodeBalancerPoolId(\n swapData,\n newRouterAddress,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.Curve) {\n curvePoolIndices[_tokenAddress] = _decodeCurvePoolIndices(\n swapData,\n newRouterAddress,\n _tokenAddress\n );\n } else {\n // Note: This code is unreachable since Solidity reverts when\n // the value is outside the range of defined values of the enum\n // (even if it's under the max length of the base type)\n revert InvalidSwapPlatform(_platform);\n }\n\n emit RewardTokenConfigUpdated(\n _tokenAddress,\n tokenConfig.allowedSlippageBps,\n tokenConfig.harvestRewardBps,\n _platform,\n newRouterAddress,\n swapData,\n tokenConfig.liquidationLimit,\n tokenConfig.doSwapRewardToken\n );\n }\n\n /**\n * @dev Decodes the data passed into Uniswap V2 path and validates\n * it to make sure the path is for `token` to `baseToken`\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param token The address of the reward token\n * @return path The validated Uniswap V2 path\n */\n function _decodeUniswapV2Path(bytes calldata data, address token)\n internal\n view\n returns (address[] memory path)\n {\n (path) = abi.decode(data, (address[]));\n uint256 len = path.length;\n\n if (len < 2) {\n // Path should have at least two tokens\n revert InvalidUniswapV2PathLength();\n }\n\n // Do some validation\n if (path[0] != token) {\n revert InvalidTokenInSwapPath(path[0]);\n }\n\n if (path[len - 1] != baseTokenAddress) {\n revert InvalidTokenInSwapPath(path[len - 1]);\n }\n }\n\n /**\n * @dev Decodes the data passed into Uniswap V3 path and validates\n * it to make sure the path is for `token` to `baseToken`\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param token The address of the reward token\n * @return path The validated Uniswap V3 path\n */\n function _decodeUniswapV3Path(bytes calldata data, address token)\n internal\n view\n returns (bytes calldata path)\n {\n path = data;\n\n address decodedAddress = address(uint160(bytes20(data[0:20])));\n\n if (decodedAddress != token) {\n // Invalid Reward Token in swap path\n revert InvalidTokenInSwapPath(decodedAddress);\n }\n\n decodedAddress = address(uint160(bytes20(data[path.length - 20:])));\n if (decodedAddress != baseTokenAddress) {\n // Invalid Base Token in swap path\n revert InvalidTokenInSwapPath(decodedAddress);\n }\n }\n\n /**\n * @dev Decodes the data passed to Balancer Pool ID\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @return poolId The pool ID\n */\n function _decodeBalancerPoolId(\n bytes calldata data,\n address balancerVault,\n address token\n ) internal view returns (bytes32 poolId) {\n (poolId) = abi.decode(data, (bytes32));\n\n if (poolId == bytes32(0)) {\n revert EmptyBalancerPoolId();\n }\n\n IBalancerVault bVault = IBalancerVault(balancerVault);\n\n // Note: this reverts if token is not a pool asset\n // slither-disable-next-line unused-return\n bVault.getPoolTokenInfo(poolId, token);\n\n // slither-disable-next-line unused-return\n bVault.getPoolTokenInfo(poolId, baseTokenAddress);\n }\n\n /**\n * @dev Decodes the data passed to get the pool indices and\n * checks it against the Curve Pool to make sure it's\n * not misconfigured. The indices are packed into a single\n * uint256 for gas savings\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param poolAddress Curve pool address\n * @param token The address of the reward token\n * @return indices Packed pool asset indices\n */\n function _decodeCurvePoolIndices(\n bytes calldata data,\n address poolAddress,\n address token\n ) internal view returns (CurvePoolIndices memory indices) {\n indices = abi.decode(data, (CurvePoolIndices));\n\n ICurvePool pool = ICurvePool(poolAddress);\n if (token != pool.coins(indices.rewardTokenIndex)) {\n revert InvalidCurvePoolAssetIndex(token);\n }\n if (baseTokenAddress != pool.coins(indices.baseTokenIndex)) {\n revert InvalidCurvePoolAssetIndex(baseTokenAddress);\n }\n }\n\n /**\n * @dev Flags a strategy as supported or not supported one\n * @param _strategyAddress Address of the strategy\n * @param _isSupported Bool marking strategy as supported or not supported\n */\n function setSupportedStrategy(address _strategyAddress, bool _isSupported)\n external\n onlyGovernor\n {\n supportedStrategies[_strategyAddress] = _isSupported;\n emit SupportedStrategyUpdate(_strategyAddress, _isSupported);\n }\n\n /***************************************\n Rewards\n ****************************************/\n\n /**\n * @dev Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform. Can be called by anyone.\n * Rewards incentivizing the caller are sent to the caller of this function.\n * @param _strategyAddr Address of the strategy to collect rewards from\n */\n function harvestAndSwap(address _strategyAddr) external nonReentrant {\n // Remember _harvest function checks for the validity of _strategyAddr\n _harvestAndSwap(_strategyAddr, msg.sender);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform. Can be called by anyone\n * @param _strategyAddr Address of the strategy to collect rewards from\n * @param _rewardTo Address where to send a share of harvest rewards to as an incentive\n * for executing this function\n */\n function harvestAndSwap(address _strategyAddr, address _rewardTo)\n external\n nonReentrant\n {\n // Remember _harvest function checks for the validity of _strategyAddr\n _harvestAndSwap(_strategyAddr, _rewardTo);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform\n * @param _strategyAddr Address of the strategy to collect rewards from\n * @param _rewardTo Address where to send a share of harvest rewards to as an incentive\n * for executing this function\n */\n function _harvestAndSwap(address _strategyAddr, address _rewardTo)\n internal\n {\n _harvest(_strategyAddr);\n IStrategy strategy = IStrategy(_strategyAddr);\n address[] memory rewardTokens = strategy.getRewardTokenAddresses();\n IOracle priceProvider = IOracle(IVault(vaultAddress).priceProvider());\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; ++i) {\n _swap(rewardTokens[i], _rewardTo, priceProvider);\n }\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform\n * @param _strategyAddr Address of the strategy to collect rewards from.\n */\n function _harvest(address _strategyAddr) internal virtual {\n if (!supportedStrategies[_strategyAddr]) {\n revert UnsupportedStrategy(_strategyAddr);\n }\n\n IStrategy strategy = IStrategy(_strategyAddr);\n strategy.collectRewardTokens();\n }\n\n /**\n * @dev Swap a reward token for the base token on the configured\n * swap platform. The token must have a registered price feed\n * with the price provider\n * @param _swapToken Address of the token to swap\n * @param _rewardTo Address where to send the share of harvest rewards to\n * @param _priceProvider Oracle to get prices of the swap token\n */\n function _swap(\n address _swapToken,\n address _rewardTo,\n IOracle _priceProvider\n ) internal virtual {\n uint256 balance = IERC20(_swapToken).balanceOf(address(this));\n\n // No need to swap if the reward token is the base token. eg USDT or WETH.\n // There is also no limit on the transfer. Everything in the harvester will be transferred\n // to the Dripper regardless of the liquidationLimit config.\n if (_swapToken == baseTokenAddress) {\n IERC20(_swapToken).safeTransfer(rewardProceedsAddress, balance);\n // currently not paying the farmer any rewards as there is no swap\n emit RewardProceedsTransferred(\n baseTokenAddress,\n address(0),\n balance,\n 0\n );\n return;\n }\n\n RewardTokenConfig memory tokenConfig = rewardTokenConfigs[_swapToken];\n\n /* This will trigger a return when reward token configuration has not yet been set\n * or we have temporarily disabled swapping of specific reward token via setting\n * doSwapRewardToken to false.\n */\n if (!tokenConfig.doSwapRewardToken) {\n return;\n }\n\n if (balance == 0) {\n return;\n }\n\n if (tokenConfig.liquidationLimit > 0) {\n balance = Math.min(balance, tokenConfig.liquidationLimit);\n }\n\n // This'll revert if there is no price feed\n uint256 oraclePrice = _priceProvider.price(_swapToken);\n\n // Oracle price is 1e18\n uint256 minExpected = (balance *\n (1e4 - tokenConfig.allowedSlippageBps) * // max allowed slippage\n oraclePrice).scaleBy(\n baseTokenDecimals,\n Helpers.getDecimals(_swapToken)\n ) /\n 1e4 / // fix the max slippage decimal position\n 1e18; // and oracle price decimals position\n\n // Do the swap\n uint256 amountReceived = _doSwap(\n tokenConfig.swapPlatform,\n tokenConfig.swapPlatformAddr,\n _swapToken,\n balance,\n minExpected\n );\n\n if (amountReceived < minExpected) {\n revert SlippageError(amountReceived, minExpected);\n }\n\n emit RewardTokenSwapped(\n _swapToken,\n baseTokenAddress,\n tokenConfig.swapPlatform,\n balance,\n amountReceived\n );\n\n IERC20 baseToken = IERC20(baseTokenAddress);\n uint256 baseTokenBalance = baseToken.balanceOf(address(this));\n if (baseTokenBalance < amountReceived) {\n // Note: It's possible to bypass this check by transferring `baseToken`\n // directly to Harvester before calling the `harvestAndSwap`. However,\n // there's no incentive for an attacker to do that. Doing a balance diff\n // will increase the gas cost significantly\n revert BalanceMismatchAfterSwap(baseTokenBalance, amountReceived);\n }\n\n // Farmer only gets fee from the base amount they helped farm,\n // They do not get anything from anything that already was there\n // on the Harvester\n uint256 farmerFee = amountReceived.mulTruncateScale(\n tokenConfig.harvestRewardBps,\n 1e4\n );\n uint256 protocolYield = baseTokenBalance - farmerFee;\n\n baseToken.safeTransfer(rewardProceedsAddress, protocolYield);\n baseToken.safeTransfer(_rewardTo, farmerFee);\n emit RewardProceedsTransferred(\n baseTokenAddress,\n _rewardTo,\n protocolYield,\n farmerFee\n );\n }\n\n function _doSwap(\n SwapPlatform swapPlatform,\n address routerAddress,\n address rewardTokenAddress,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n if (swapPlatform == SwapPlatform.UniswapV2Compatible) {\n return\n _swapWithUniswapV2(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.UniswapV3) {\n return\n _swapWithUniswapV3(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.Balancer) {\n return\n _swapWithBalancer(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.Curve) {\n return\n _swapWithCurve(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else {\n // Should never be invoked since we catch invalid values\n // in the `setRewardTokenConfig` function before it's set\n revert InvalidSwapPlatform(swapPlatform);\n }\n }\n\n /**\n * @dev Swaps the token to `baseToken` with Uniswap V2\n *\n * @param routerAddress Uniswap V2 Router address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithUniswapV2(\n address routerAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n address[] memory path = uniswapV2Path[swapToken];\n\n uint256[] memory amounts = IUniswapV2Router(routerAddress)\n .swapExactTokensForTokens(\n amountIn,\n minAmountOut,\n path,\n address(this),\n block.timestamp\n );\n\n amountOut = amounts[amounts.length - 1];\n }\n\n /**\n * @dev Swaps the token to `baseToken` with Uniswap V3\n *\n * @param routerAddress Uniswap V3 Router address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithUniswapV3(\n address routerAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n bytes memory path = uniswapV3Path[swapToken];\n\n IUniswapV3Router.ExactInputParams memory params = IUniswapV3Router\n .ExactInputParams({\n path: path,\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: amountIn,\n amountOutMinimum: minAmountOut\n });\n amountOut = IUniswapV3Router(routerAddress).exactInput(params);\n }\n\n /**\n * @dev Swaps the token to `baseToken` on Balancer\n *\n * @param balancerVaultAddress BalancerVaultAddress\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithBalancer(\n address balancerVaultAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n bytes32 poolId = balancerPoolId[swapToken];\n\n IBalancerVault.SingleSwap memory singleSwap = IBalancerVault\n .SingleSwap({\n poolId: poolId,\n kind: IBalancerVault.SwapKind.GIVEN_IN,\n assetIn: swapToken,\n assetOut: baseTokenAddress,\n amount: amountIn,\n userData: hex\"\"\n });\n\n IBalancerVault.FundManagement memory fundMgmt = IBalancerVault\n .FundManagement({\n sender: address(this),\n fromInternalBalance: false,\n recipient: payable(address(this)),\n toInternalBalance: false\n });\n\n amountOut = IBalancerVault(balancerVaultAddress).swap(\n singleSwap,\n fundMgmt,\n minAmountOut,\n block.timestamp\n );\n }\n\n /**\n * @dev Swaps the token to `baseToken` on Curve\n *\n * @param poolAddress Curve Pool Address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithCurve(\n address poolAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n CurvePoolIndices memory indices = curvePoolIndices[swapToken];\n\n // Note: Not all CurvePools return the `amountOut`, make sure\n // to use only pool that do. Otherwise the swap would revert\n // always\n amountOut = ICurvePool(poolAddress).exchange(\n uint256(indices.rewardTokenIndex),\n uint256(indices.baseTokenIndex),\n amountIn,\n minAmountOut\n );\n }\n}\n" + }, + "contracts/harvest/Dripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\n/**\n * @title OUSD Dripper\n *\n * The dripper contract smooths out the yield from point-in-time yield events\n * and spreads the yield out over a configurable time period. This ensures a\n * continuous per block yield to makes users happy as their next rebase\n * amount is always moving up. Also, this makes historical day to day yields\n * smooth, rather than going from a near zero day, to a large APY day, then\n * back to a near zero day again.\n *\n *\n * Design notes\n * - USDT has a smaller resolution than the number of seconds\n * in a week, which can make per second payouts have a rounding error. However\n * the total effect is not large - cents per day, and this money is\n * not lost, just distributed in the future. While we could use a higher\n * decimal precision for the drip perSecond, we chose simpler code.\n * - By calculating the changing drip rates on collects only, harvests and yield\n * events don't have to call anything on this contract or pay any extra gas.\n * Collect() is already be paying for a single write, since it has to reset\n * the lastCollect time.\n * - By having a collectAndRebase method, and having our external systems call\n * that, the OUSD vault does not need any changes, not even to know the address\n * of the dripper.\n * - A rejected design was to retro-calculate the drip rate on each collect,\n * based on the balance at the time of the collect. While this would have\n * required less state, and would also have made the contract respond more quickly\n * to new income, it would break the predictability that is this contract's entire\n * purpose. If we did this, the amount of fundsAvailable() would make sharp increases\n * when funds were deposited.\n * - When the dripper recalculates the rate, it targets spending the balance over\n * the duration. This means that every time that collect is called, if no\n * new funds have been deposited the duration is being pushed back and the\n * rate decreases. This is expected, and ends up following a smoother but\n * longer curve the more collect() is called without incoming yield.\n *\n */\n\ncontract Dripper is Governable {\n using SafeERC20 for IERC20;\n\n struct Drip {\n uint64 lastCollect; // overflows 262 billion years after the sun dies\n uint192 perSecond; // drip rate per second\n }\n\n address immutable vault; // OUSD vault\n address immutable token; // token to drip out\n uint256 public dripDuration; // in seconds\n Drip public drip; // active drip parameters\n\n constructor(address _vault, address _token) {\n vault = _vault;\n token = _token;\n }\n\n /// @notice How much funds have dripped out already and are currently\n // available to be sent to the vault.\n /// @return The amount that would be sent if a collect was called\n function availableFunds() external view returns (uint256) {\n uint256 balance = IERC20(token).balanceOf(address(this));\n return _availableFunds(balance, drip);\n }\n\n /// @notice Collect all dripped funds and send to vault.\n /// Recalculate new drip rate.\n function collect() external {\n _collect();\n }\n\n /// @notice Collect all dripped funds, send to vault, recalculate new drip\n /// rate, and rebase OUSD.\n function collectAndRebase() external {\n _collect();\n IVault(vault).rebase();\n }\n\n /// @dev Change the drip duration. Governor only.\n /// @param _durationSeconds the number of seconds to drip out the entire\n /// balance over if no collects were called during that time.\n function setDripDuration(uint256 _durationSeconds)\n external\n virtual\n onlyGovernor\n {\n require(_durationSeconds > 0, \"duration must be non-zero\");\n dripDuration = _durationSeconds;\n _collect(); // duration change take immediate effect\n }\n\n /// @dev Transfer out ERC20 tokens held by the contract. Governor only.\n /// @param _asset ERC20 token address\n /// @param _amount amount to transfer\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /// @dev Calculate available funds by taking the lower of either the\n /// currently dripped out funds or the balance available.\n /// Uses passed in parameters to calculate with for gas savings.\n /// @param _balance current balance in contract\n /// @param _drip current drip parameters\n function _availableFunds(uint256 _balance, Drip memory _drip)\n internal\n view\n returns (uint256)\n {\n uint256 elapsed = block.timestamp - _drip.lastCollect;\n uint256 allowed = (elapsed * _drip.perSecond);\n return (allowed > _balance) ? _balance : allowed;\n }\n\n /// @dev Sends the currently dripped funds to be vault, and sets\n /// the new drip rate based on the new balance.\n function _collect() internal virtual {\n // Calculate send\n uint256 balance = IERC20(token).balanceOf(address(this));\n uint256 amountToSend = _availableFunds(balance, drip);\n uint256 remaining = balance - amountToSend;\n // Calculate new drip perSecond\n // Gas savings by setting entire struct at one time\n drip = Drip({\n perSecond: uint192(remaining / dripDuration),\n lastCollect: uint64(block.timestamp)\n });\n // Send funds\n IERC20(token).safeTransfer(vault, amountToSend);\n }\n\n /// @dev Transfer out all ERC20 held by the contract. Governor only.\n /// @param _asset ERC20 token address\n function transferAllToken(address _asset, address _receiver)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(\n _receiver,\n IERC20(_asset).balanceOf(address(this))\n );\n }\n}\n" + }, + "contracts/harvest/FixedRateDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Dripper } from \"./Dripper.sol\";\n\n/**\n * @title Fixed Rate Dripper\n *\n * Similar to the Dripper, Fixed Rate Dripper drips out yield per second.\n * However the Strategist decides the rate and it doesn't change after\n * a drip.\n *\n */\n\ncontract FixedRateDripper is Dripper {\n using SafeERC20 for IERC20;\n\n event DripRateUpdated(uint192 oldDripRate, uint192 newDripRate);\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vault).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n constructor(address _vault, address _token) Dripper(_vault, _token) {}\n\n /// @inheritdoc Dripper\n function setDripDuration(uint256) external virtual override {\n // Not used in FixedRateDripper\n revert(\"Drip duration disabled\");\n }\n\n /// @inheritdoc Dripper\n function _collect() internal virtual override {\n // Calculate amount to send\n uint256 balance = IERC20(token).balanceOf(address(this));\n uint256 amountToSend = _availableFunds(balance, drip);\n\n // Update timestamp\n drip.lastCollect = uint64(block.timestamp);\n\n // Send funds\n IERC20(token).safeTransfer(vault, amountToSend);\n }\n\n /**\n * @dev Sets the drip rate. Callable by Strategist or Governor.\n * Can be set to zero to stop dripper.\n * @param _perSecond Rate of WETH to drip per second\n */\n function setDripRate(uint192 _perSecond) external onlyGovernorOrStrategist {\n emit DripRateUpdated(_perSecond, drip.perSecond);\n\n /**\n * Note: It's important to call `_collect` before updating\n * the drip rate especially on a new proxy contract.\n * When `lastCollect` is not set/initialized, the elapsed\n * time would be calculated as `block.number` seconds,\n * resulting in a huge yield, if `collect` isn't called first.\n */\n // Collect at existing rate\n _collect();\n\n // Update rate\n drip.perSecond = _perSecond;\n }\n}\n" + }, + "contracts/harvest/Harvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractHarvester } from \"./AbstractHarvester.sol\";\n\ncontract Harvester is AbstractHarvester {\n constructor(address _vault, address _usdtAddress)\n AbstractHarvester(_vault, _usdtAddress)\n {}\n}\n" + }, + "contracts/harvest/OETHBaseHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { ISwapRouter } from \"../interfaces/aerodrome/ISwapRouter.sol\";\n\ncontract OETHBaseHarvester is Governable {\n using SafeERC20 for IERC20;\n\n IVault public immutable vault;\n IStrategy public immutable amoStrategy;\n IERC20 public immutable aero;\n IERC20 public immutable weth;\n ISwapRouter public immutable swapRouter;\n\n address public operatorAddr;\n\n // Similar sig to `AbstractHarvester.RewardTokenSwapped` for\n // future compatibility with monitoring\n event RewardTokenSwapped(\n address indexed rewardToken,\n address indexed swappedInto,\n uint8 swapPlatform,\n uint256 amountIn,\n uint256 amountOut\n );\n\n event OperatorChanged(address oldOperator, address newOperator);\n event YieldSent(address recipient, uint256 yield, uint256 fee);\n\n /**\n * @notice Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == vault.strategistAddr() || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @notice Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategistOrOperator() {\n require(\n msg.sender == operatorAddr ||\n msg.sender == vault.strategistAddr() ||\n isGovernor(),\n \"Caller is not the Operator or Strategist or Governor\"\n );\n _;\n }\n\n constructor(\n address _vault,\n address _amoStrategy,\n address _aero,\n address _weth,\n address _swapRouter\n ) {\n vault = IVault(_vault);\n amoStrategy = IStrategy(_amoStrategy);\n aero = IERC20(_aero);\n weth = IERC20(_weth);\n swapRouter = ISwapRouter(_swapRouter);\n }\n\n /**\n * @dev Changes the operator address which can call `harvest`\n * @param _operatorAddr New operator address\n */\n function setOperatorAddr(address _operatorAddr) external onlyGovernor {\n emit OperatorChanged(operatorAddr, _operatorAddr);\n operatorAddr = _operatorAddr;\n }\n\n /**\n * @notice Collects AERO from AMO strategy and\n * sends it to the Strategist multisig.\n * Anyone can call it.\n */\n function harvest() external onlyGovernorOrStrategistOrOperator {\n address strategistAddr = vault.strategistAddr();\n require(strategistAddr != address(0), \"Guardian address not set\");\n\n // Collect all AERO\n amoStrategy.collectRewardTokens();\n\n uint256 aeroBalance = aero.balanceOf(address(this));\n if (aeroBalance == 0) {\n // Do nothing if there's no AERO to transfer\n return;\n }\n\n // Transfer everything to Strategist\n aero.safeTransfer(strategistAddr, aeroBalance);\n }\n\n /**\n * @notice Harvests AERO from AMO strategy and then swaps some (or all)\n * of it into WETH to distribute yield and fee.\n * When `feeBps` is set to 10000 (100%), all WETH received is\n * sent to strategist.\n *\n * @param aeroToSwap Amount of AERO to swap\n * @param minWETHExpected Min. amount of WETH to expect\n * @param feeBps Performance fee bps (Sent to strategist)\n * @param sendYieldToDripper Sends yield to Dripper, if set to true.\n * Otherwise, to the Guardian\n */\n function harvestAndSwap(\n uint256 aeroToSwap,\n uint256 minWETHExpected,\n uint256 feeBps,\n bool sendYieldToDripper\n ) external onlyGovernorOrStrategist {\n address strategistAddr = vault.strategistAddr();\n require(strategistAddr != address(0), \"Guardian address not set\");\n\n // Yields can either be sent to the Dripper or Strategist\n address yieldRecipient = sendYieldToDripper\n ? vault.dripper()\n : strategistAddr;\n require(yieldRecipient != address(0), \"Yield recipient not set\");\n\n require(feeBps <= 10000, \"Invalid Fee Bps\");\n\n // Collect all AERO\n amoStrategy.collectRewardTokens();\n\n uint256 aeroBalance = aero.balanceOf(address(this));\n if (aeroBalance == 0) {\n // Do nothing if there's no AERO to transfer/swap\n return;\n }\n\n if (aeroToSwap > 0) {\n if (aeroBalance < aeroToSwap) {\n // Transfer in balance from the multisig as needed\n // slither-disable-next-line unchecked-transfer arbitrary-send-erc20\n aero.safeTransferFrom(\n strategistAddr,\n address(this),\n aeroToSwap - aeroBalance\n );\n }\n\n _doSwap(aeroToSwap, minWETHExpected);\n\n // Figure out AERO left in contract after swap\n aeroBalance = aero.balanceOf(address(this));\n }\n\n // Transfer out any leftover AERO after swap\n if (aeroBalance > 0) {\n aero.safeTransfer(strategistAddr, aeroBalance);\n }\n\n // Computes using all balance the contract holds,\n // not just the WETH received from swap. Use `transferToken`\n // if there's any WETH left that needs to be taken out\n uint256 availableWETHBalance = weth.balanceOf(address(this));\n // Computation rounds in favor of protocol\n uint256 fee = (availableWETHBalance * feeBps) / 10000;\n uint256 yield = availableWETHBalance - fee;\n\n // Transfer yield, if any\n if (yield > 0) {\n weth.safeTransfer(yieldRecipient, yield);\n }\n\n // Transfer fee to the Guardian, if any\n if (fee > 0) {\n weth.safeTransfer(strategistAddr, fee);\n }\n\n emit YieldSent(yieldRecipient, yield, fee);\n }\n\n /**\n * @notice Swaps AERO to WETH on Aerodrome\n * @param aeroToSwap Amount of AERO to swap\n * @param minWETHExpected Min. amount of WETH to expect\n */\n function _doSwap(uint256 aeroToSwap, uint256 minWETHExpected) internal {\n // Let the swap router move funds\n aero.approve(address(swapRouter), aeroToSwap);\n\n // Do the swap\n uint256 wethReceived = swapRouter.exactInputSingle(\n ISwapRouter.ExactInputSingleParams({\n tokenIn: address(aero),\n tokenOut: address(weth),\n tickSpacing: 200, // From AERO/WETH pool contract\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: aeroToSwap,\n amountOutMinimum: minWETHExpected,\n sqrtPriceLimitX96: 0\n })\n );\n\n emit RewardTokenSwapped(\n address(aero),\n address(weth),\n 0,\n aeroToSwap,\n wethReceived\n );\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * the contract, i.e. mistaken sends.\n * Also, allows to transfer any AERO left in the contract.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n virtual\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n}\n" + }, + "contracts/harvest/OETHDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Dripper } from \"./Dripper.sol\";\n\n/**\n * @title OETH Dripper Contract\n * @author Origin Protocol Inc\n */\ncontract OETHDripper is Dripper {\n constructor(address _vault, address _token) Dripper(_vault, _token) {}\n}\n" + }, + "contracts/harvest/OETHFixedRateDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { FixedRateDripper } from \"./FixedRateDripper.sol\";\n\n/**\n * @title OETH FixedRateDripper Contract\n * @author Origin Protocol Inc\n */\ncontract OETHFixedRateDripper is FixedRateDripper {\n constructor(address _vault, address _token)\n FixedRateDripper(_vault, _token)\n {}\n}\n" + }, + "contracts/harvest/OETHHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractHarvester } from \"./AbstractHarvester.sol\";\n\ncontract OETHHarvester is AbstractHarvester {\n constructor(address _vault, address _wethAddress)\n AbstractHarvester(_vault, _wethAddress)\n {}\n}\n" + }, + "contracts/harvest/OETHHarvesterSimple.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\n\n/// @title OETH Harvester Simple Contract\n/// @notice Contract to harvest rewards from strategies\n/// @author Origin Protocol Inc\ncontract OETHHarvesterSimple is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n ////////////////////////////////////////////////////\n /// --- CONSTANTS & IMMUTABLES\n ////////////////////////////////////////////////////\n /// @notice wrapped native token address (WETH or wS)\n address public immutable wrappedNativeToken;\n\n ////////////////////////////////////////////////////\n /// --- STORAGE\n ////////////////////////////////////////////////////\n /// @notice Dripper address\n address public dripper;\n\n /// @notice Mapping of supported strategies\n mapping(address => bool) public supportedStrategies;\n\n /// @notice Gap for upgrade safety\n uint256[48] private ___gap;\n\n ////////////////////////////////////////////////////\n /// --- EVENTS\n ////////////////////////////////////////////////////\n event Harvested(\n address indexed strategy,\n address token,\n uint256 amount,\n address indexed receiver\n );\n event SupportedStrategyUpdated(address strategy, bool status);\n event DripperUpdated(address dripper);\n\n ////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////\n constructor(address _wrappedNativeToken) {\n wrappedNativeToken = _wrappedNativeToken;\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /// @notice Initialize the contract\n function initialize() external onlyGovernor initializer {\n // Call it to set `initialized` to true and to prevent the implementation\n // from getting initialized in future through the proxy\n }\n\n ////////////////////////////////////////////////////\n /// --- MUTATIVE FUNCTIONS\n ////////////////////////////////////////////////////\n /// @notice Harvest rewards from a strategy and transfer to strategist or dripper\n /// @param _strategy Address of the strategy to harvest\n function harvestAndTransfer(address _strategy) external {\n _harvestAndTransfer(_strategy);\n }\n\n /// @notice Harvest rewards from multiple strategies and transfer to strategist or dripper\n /// @param _strategies Array of strategy addresses to harvest\n function harvestAndTransfer(address[] calldata _strategies) external {\n for (uint256 i = 0; i < _strategies.length; i++) {\n _harvestAndTransfer(_strategies[i]);\n }\n }\n\n /// @notice Internal logic to harvest rewards from a strategy\n function _harvestAndTransfer(address _strategy) internal virtual {\n // Ensure strategy is supported\n require(supportedStrategies[_strategy], \"Strategy not supported\");\n\n // Store locally for some gas savings\n address _strategist = strategistAddr;\n address _dripper = dripper;\n\n // Harvest rewards\n IStrategy(_strategy).collectRewardTokens();\n\n // Cache reward tokens\n address[] memory rewardTokens = IStrategy(_strategy)\n .getRewardTokenAddresses();\n\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; i++) {\n // Cache balance\n address token = rewardTokens[i];\n uint256 balance = IERC20(token).balanceOf(address(this));\n if (balance > 0) {\n // Determine receiver\n address receiver = token == wrappedNativeToken\n ? _dripper\n : _strategist;\n require(receiver != address(0), \"Invalid receiver\");\n\n // Transfer to the Strategist or the Dripper\n IERC20(token).safeTransfer(receiver, balance);\n emit Harvested(_strategy, token, balance, receiver);\n }\n }\n }\n\n ////////////////////////////////////////////////////\n /// --- GOVERNANCE\n ////////////////////////////////////////////////////\n /// @notice Set supported strategy\n /// @param _strategy Address of the strategy\n /// @param _isSupported Boolean indicating if strategy is supported\n function setSupportedStrategy(address _strategy, bool _isSupported)\n external\n onlyGovernorOrStrategist\n {\n require(_strategy != address(0), \"Invalid strategy\");\n supportedStrategies[_strategy] = _isSupported;\n emit SupportedStrategyUpdated(_strategy, _isSupported);\n }\n\n /// @notice Transfer tokens to strategist\n /// @param _asset Address of the token\n /// @param _amount Amount of tokens to transfer\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernorOrStrategist\n {\n IERC20(_asset).safeTransfer(strategistAddr, _amount);\n }\n\n /// @notice Set the dripper address\n /// @param _dripper Address of the dripper\n function setDripper(address _dripper) external onlyGovernor {\n _setDripper(_dripper);\n }\n\n /// @notice Internal logic to set the dripper address\n function _setDripper(address _dripper) internal {\n require(_dripper != address(0), \"Invalid dripper\");\n dripper = _dripper;\n emit DripperUpdated(_dripper);\n }\n}\n" + }, + "contracts/harvest/OSonicHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SuperOETHHarvester } from \"./SuperOETHHarvester.sol\";\n\ncontract OSonicHarvester is SuperOETHHarvester {\n /// @param _wrappedNativeToken Address of the native Wrapped S (wS) token\n constructor(address _wrappedNativeToken)\n SuperOETHHarvester(_wrappedNativeToken)\n {}\n}\n" + }, + "contracts/harvest/SuperOETHHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHHarvesterSimple, IERC20, IStrategy, SafeERC20 } from \"./OETHHarvesterSimple.sol\";\n\ncontract SuperOETHHarvester is OETHHarvesterSimple {\n using SafeERC20 for IERC20;\n\n constructor(address _wrappedNativeToken)\n OETHHarvesterSimple(_wrappedNativeToken)\n {}\n\n /// @inheritdoc OETHHarvesterSimple\n function _harvestAndTransfer(address _strategy) internal virtual override {\n // Ensure strategy is supported\n require(supportedStrategies[_strategy], \"Strategy not supported\");\n\n address receiver = strategistAddr;\n require(receiver != address(0), \"Invalid receiver\");\n\n // Harvest rewards\n IStrategy(_strategy).collectRewardTokens();\n\n // Cache reward tokens\n address[] memory rewardTokens = IStrategy(_strategy)\n .getRewardTokenAddresses();\n\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; i++) {\n // Cache balance\n address token = rewardTokens[i];\n uint256 balance = IERC20(token).balanceOf(address(this));\n if (balance > 0) {\n // Transfer everything to the strategist\n IERC20(token).safeTransfer(receiver, balance);\n emit Harvested(_strategy, token, balance, receiver);\n }\n }\n }\n}\n" + }, + "contracts/interfaces/aerodrome/IAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ICLPool } from \"./ICLPool.sol\";\n\ninterface IAMOStrategy {\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth);\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth);\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n error OutsideExpectedTickRange(int24 currentTick);\n\n function governor() external view returns (address);\n\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) external;\n\n function clPool() external view returns (ICLPool);\n\n function vaultAddress() external view returns (address);\n\n function poolWethShareVarianceAllowed() external view returns (uint256);\n\n function poolWethShare() external view returns (uint256);\n\n function tokenId() external view returns (uint256);\n\n function withdrawAll() external;\n\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external;\n\n function setWithdrawLiquidityShare(uint128 share) external;\n\n function lowerTick() external view returns (int24);\n\n function upperTick() external view returns (int24);\n\n function getPoolX96Price() external view returns (uint160 _sqrtRatioX96);\n\n function sqrtRatioX96TickLower() external view returns (uint160);\n\n function sqrtRatioX96TickHigher() external view returns (uint160);\n\n function tickSpacing() external view returns (int24);\n\n function allowedWethShareStart() external view returns (uint256);\n\n function allowedWethShareEnd() external view returns (uint256);\n\n function claimGovernance() external;\n\n function transferGovernance(address _governor) external;\n\n function getPositionPrincipal()\n external\n view\n returns (uint256 _amountWeth, uint256 _amountOethb);\n}\n" + }, + "contracts/interfaces/aerodrome/ICLGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\ninterface ICLGauge {\n /// @notice Returns the claimable rewards for a given account and tokenId\n /// @dev Throws if account is not the position owner\n /// @dev pool.updateRewardsGrowthGlobal() needs to be called first, to return the correct claimable rewards\n /// @param account The address of the user\n /// @param tokenId The tokenId of the position\n /// @return The amount of claimable reward\n function earned(address account, uint256 tokenId)\n external\n view\n returns (uint256);\n\n /// @notice Retrieve rewards for all tokens owned by an account\n /// @dev Throws if not called by the voter\n /// @param account The account of the user\n function getReward(address account) external;\n\n /// @notice Retrieve rewards for a tokenId\n /// @dev Throws if not called by the position owner\n /// @param tokenId The tokenId of the position\n function getReward(uint256 tokenId) external;\n\n /// @notice Notifies gauge of gauge rewards.\n /// @param amount Amount of gauge rewards (emissions) to notify. Must be greater than 604_800.\n function notifyRewardAmount(uint256 amount) external;\n\n /// @dev Notifies gauge of gauge rewards without distributing its fees.\n /// Assumes gauge reward tokens is 18 decimals.\n /// If not 18 decimals, rewardRate may have rounding issues.\n /// @param amount Amount of gauge rewards (emissions) to notify. Must be greater than 604_800.\n function notifyRewardWithoutClaim(uint256 amount) external;\n\n /// @notice Used to deposit a CL position into the gauge\n /// @notice Allows the user to receive emissions instead of fees\n /// @param tokenId The tokenId of the position\n function deposit(uint256 tokenId) external;\n\n /// @notice Used to withdraw a CL position from the gauge\n /// @notice Allows the user to receive fees instead of emissions\n /// @notice Outstanding emissions will be collected on withdrawal\n /// @param tokenId The tokenId of the position\n function withdraw(uint256 tokenId) external;\n\n // /// @notice Fetch all tokenIds staked by a given account\n // /// @param depositor The address of the user\n // /// @return The tokenIds of the staked positions\n // function stakedValues(address depositor) external view returns (uint256[] memory);\n\n // /// @notice Fetch a staked tokenId by index\n // /// @param depositor The address of the user\n // /// @param index The index of the staked tokenId\n // /// @return The tokenId of the staked position\n // function stakedByIndex(address depositor, uint256 index) external view returns (uint256);\n\n // /// @notice Check whether a position is staked in the gauge by a certain user\n // /// @param depositor The address of the user\n // /// @param tokenId The tokenId of the position\n // /// @return Whether the position is staked in the gauge\n // function stakedContains(address depositor, uint256 tokenId) external view returns (bool);\n\n // /// @notice The amount of positions staked in the gauge by a certain user\n // /// @param depositor The address of the user\n // /// @return The amount of positions staked in the gauge\n // function stakedLength(address depositor) external view returns (uint256);\n\n function feesVotingReward() external view returns (address);\n}\n" + }, + "contracts/interfaces/aerodrome/ICLPool.sol": { + "content": "pragma solidity >=0.5.0;\n\n/// @title The interface for a CL Pool\n/// @notice A CL pool facilitates swapping and automated market making between any two assets that strictly conform\n/// to the ERC20 specification\n/// @dev The pool interface is broken up into many smaller pieces\ninterface ICLPool {\n function slot0()\n external\n view\n returns (\n uint160 sqrtPriceX96,\n int24 tick,\n uint16 observationIndex,\n uint16 observationCardinality,\n uint16 observationCardinalityNext,\n bool unlocked\n );\n\n /// @notice The first of the two tokens of the pool, sorted by address\n /// @return The token contract address\n function token0() external view returns (address);\n\n /// @notice The second of the two tokens of the pool, sorted by address\n /// @return The token contract address\n function token1() external view returns (address);\n\n function tickSpacing() external view returns (int24);\n\n /// @notice The gauge corresponding to this pool\n /// @return The gauge contract address\n function gauge() external view returns (address);\n\n /// @notice The currently in range liquidity available to the pool\n /// @dev This value has no relationship to the total liquidity across all ticks\n /// @dev This value includes staked liquidity\n function liquidity() external view returns (uint128);\n\n /// @notice Look up information about a specific tick in the pool\n /// @param tick The tick to look up\n /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or\n /// tick upper,\n /// liquidityNet how much liquidity changes when the pool price crosses the tick,\n /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,\n /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,\n /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick\n /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from\n /// the current tick,\n /// secondsOutside the seconds spent on the other side of the tick from the current tick,\n /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise\n /// equal to false.\n /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.\n /// In addition, these values are only relative and must be used only in comparison to previous snapshots for\n /// a specific position.\n function ticks(int24 tick)\n external\n view\n returns (\n uint128 liquidityGross,\n int128 liquidityNet,\n uint256 feeGrowthOutside0X128,\n uint256 feeGrowthOutside1X128,\n int56 tickCumulativeOutside,\n uint160 secondsPerLiquidityOutsideX128,\n uint32 secondsOutside,\n bool initialized\n );\n}\n" + }, + "contracts/interfaces/aerodrome/INonfungiblePositionManager.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title Non-fungible token for positions\n/// @notice Wraps CL positions in a non-fungible token interface which allows for them to be transferred\n/// and authorized.\n// slither-disable-start erc20-interface\ninterface INonfungiblePositionManager {\n /**\n * @dev See {IERC721-approve}.\n */\n function approve(address to, uint256 tokenId) external;\n\n /**\n * @dev See {IERC721-getApproved}.\n */\n function getApproved(uint256 tokenId) external returns (address);\n\n /**\n * @dev See {IERC721-ownerOf}.\n */\n function ownerOf(uint256 tokenId) external view returns (address);\n\n /// @notice Returns the position information associated with a given token ID.\n /// @dev Throws if the token ID is not valid.\n /// @param tokenId The ID of the token that represents the position\n /// @return nonce The nonce for permits\n /// @return operator The address that is approved for spending\n /// @return token0 The address of the token0 for a specific pool\n /// @return token1 The address of the token1 for a specific pool\n /// @return tickSpacing The tick spacing associated with the pool\n /// @return tickLower The lower end of the tick range for the position\n /// @return tickUpper The higher end of the tick range for the position\n /// @return liquidity The liquidity of the position\n /// @return feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position\n /// @return feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position\n /// @return tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation\n /// @return tokensOwed1 The uncollected amount of token1 owed to the position as of the last computation\n function positions(uint256 tokenId)\n external\n view\n returns (\n uint96 nonce,\n address operator,\n address token0,\n address token1,\n int24 tickSpacing,\n int24 tickLower,\n int24 tickUpper,\n uint128 liquidity,\n uint256 feeGrowthInside0LastX128,\n uint256 feeGrowthInside1LastX128,\n uint128 tokensOwed0,\n uint128 tokensOwed1\n );\n\n struct MintParams {\n address token0;\n address token1;\n int24 tickSpacing;\n int24 tickLower;\n int24 tickUpper;\n uint256 amount0Desired;\n uint256 amount1Desired;\n uint256 amount0Min;\n uint256 amount1Min;\n address recipient;\n uint256 deadline;\n uint160 sqrtPriceX96;\n }\n\n /// @notice Creates a new position wrapped in a NFT\n /// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized\n /// a method does not exist, i.e. the pool is assumed to be initialized.\n /// @param params The params necessary to mint a position, encoded as `MintParams` in calldata\n /// @return tokenId The ID of the token that represents the minted position\n /// @return liquidity The amount of liquidity for this position\n /// @return amount0 The amount of token0\n /// @return amount1 The amount of token1\n function mint(MintParams calldata params)\n external\n payable\n returns (\n uint256 tokenId,\n uint128 liquidity,\n uint256 amount0,\n uint256 amount1\n );\n\n struct IncreaseLiquidityParams {\n uint256 tokenId;\n uint256 amount0Desired;\n uint256 amount1Desired;\n uint256 amount0Min;\n uint256 amount1Min;\n uint256 deadline;\n }\n\n /// @notice Increases the amount of liquidity in a position, with tokens paid by the `msg.sender`\n /// @param params tokenId The ID of the token for which liquidity is being increased,\n /// amount0Desired The desired amount of token0 to be spent,\n /// amount1Desired The desired amount of token1 to be spent,\n /// amount0Min The minimum amount of token0 to spend, which serves as a slippage check,\n /// amount1Min The minimum amount of token1 to spend, which serves as a slippage check,\n /// deadline The time by which the transaction must be included to effect the change\n /// @return liquidity The new liquidity amount as a result of the increase\n /// @return amount0 The amount of token0 to acheive resulting liquidity\n /// @return amount1 The amount of token1 to acheive resulting liquidity\n function increaseLiquidity(IncreaseLiquidityParams calldata params)\n external\n payable\n returns (\n uint128 liquidity,\n uint256 amount0,\n uint256 amount1\n );\n\n struct DecreaseLiquidityParams {\n uint256 tokenId;\n uint128 liquidity;\n uint256 amount0Min;\n uint256 amount1Min;\n uint256 deadline;\n }\n\n /// @notice Decreases the amount of liquidity in a position and accounts it to the position\n /// @param params tokenId The ID of the token for which liquidity is being decreased,\n /// amount The amount by which liquidity will be decreased,\n /// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity,\n /// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity,\n /// deadline The time by which the transaction must be included to effect the change\n /// @return amount0 The amount of token0 accounted to the position's tokens owed\n /// @return amount1 The amount of token1 accounted to the position's tokens owed\n /// @dev The use of this function can cause a loss to users of the NonfungiblePositionManager\n /// @dev for tokens that have very high decimals.\n /// @dev The amount of tokens necessary for the loss is: 3.4028237e+38.\n /// @dev This is equivalent to 1e20 value with 18 decimals.\n function decreaseLiquidity(DecreaseLiquidityParams calldata params)\n external\n payable\n returns (uint256 amount0, uint256 amount1);\n\n struct CollectParams {\n uint256 tokenId;\n address recipient;\n uint128 amount0Max;\n uint128 amount1Max;\n }\n\n /// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient\n /// @notice Used to update staked positions before deposit and withdraw\n /// @param params tokenId The ID of the NFT for which tokens are being collected,\n /// recipient The account that should receive the tokens,\n /// amount0Max The maximum amount of token0 to collect,\n /// amount1Max The maximum amount of token1 to collect\n /// @return amount0 The amount of fees collected in token0\n /// @return amount1 The amount of fees collected in token1\n function collect(CollectParams calldata params)\n external\n payable\n returns (uint256 amount0, uint256 amount1);\n\n /// @notice Burns a token ID, which deletes it from the NFT contract. The token must have 0 liquidity and all tokens\n /// must be collected first.\n /// @param tokenId The ID of the token that is being burned\n function burn(uint256 tokenId) external payable;\n\n /// @notice Sets a new Token Descriptor\n /// @param _tokenDescriptor Address of the new Token Descriptor to be chosen\n function setTokenDescriptor(address _tokenDescriptor) external;\n\n /// @notice Sets a new Owner address\n /// @param _owner Address of the new Owner to be chosen\n function setOwner(address _owner) external;\n}\n// slither-disable-end erc20-interface\n" + }, + "contracts/interfaces/aerodrome/IQuoterV2.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title QuoterV2 Interface\n/// @notice Supports quoting the calculated amounts from exact input or exact output swaps.\n/// @notice For each pool also tells you the number of initialized ticks crossed and the sqrt price of the\n/// pool after the swap.\n/// @dev These functions are not marked view because they rely on calling non-view functions and reverting\n/// to compute the result. They are also not gas efficient and should not be called on-chain.\ninterface IQuoterV2 {\n /// @notice Returns the amount out received for a given exact input swap without executing the swap\n /// @param path The path of the swap, i.e. each token pair and the pool tick spacing\n /// @param amountIn The amount of the first token to swap\n /// @return amountOut The amount of the last token that would be received\n /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path\n /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for\n /// each pool in the path\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactInput(bytes memory path, uint256 amountIn)\n external\n returns (\n uint256 amountOut,\n uint160[] memory sqrtPriceX96AfterList,\n uint32[] memory initializedTicksCrossedList,\n uint256 gasEstimate\n );\n\n struct QuoteExactInputSingleParams {\n address tokenIn;\n address tokenOut;\n uint256 amountIn;\n int24 tickSpacing;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Returns the amount out received for a given exact input but for a swap of a single pool\n /// @param params The params for the quote, encoded as `QuoteExactInputSingleParams`\n /// tokenIn The token being swapped in\n /// tokenOut The token being swapped out\n /// tickSpacing The tick spacing of the token pool to consider for the pair\n /// amountIn The desired input amount\n /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap\n /// @return amountOut The amount of `tokenOut` that would be received\n /// @return sqrtPriceX96After The sqrt price of the pool after the swap\n /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactInputSingle(QuoteExactInputSingleParams memory params)\n external\n returns (\n uint256 amountOut,\n uint160 sqrtPriceX96After,\n uint32 initializedTicksCrossed,\n uint256 gasEstimate\n );\n\n /// @notice Returns the amount in required for a given exact output swap without executing the swap\n /// @param path The path of the swap, i.e. each token pair and the pool tick spacing.\n /// Path must be provided in reverse order\n /// @param amountOut The amount of the last token to receive\n /// @return amountIn The amount of first token required to be paid\n /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path\n /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for\n /// each pool in the path\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactOutput(bytes memory path, uint256 amountOut)\n external\n returns (\n uint256 amountIn,\n uint160[] memory sqrtPriceX96AfterList,\n uint32[] memory initializedTicksCrossedList,\n uint256 gasEstimate\n );\n\n struct QuoteExactOutputSingleParams {\n address tokenIn;\n address tokenOut;\n uint256 amount;\n int24 tickSpacing;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Returns the amount in required to receive the given exact output amount but for a swap of a single pool\n /// @param params The params for the quote, encoded as `QuoteExactOutputSingleParams`\n /// tokenIn The token being swapped in\n /// tokenOut The token being swapped out\n /// tickSpacing The tick spacing of the token pool to consider for the pair\n /// amountOut The desired output amount\n /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap\n /// @return amountIn The amount required as the input for the swap in order to receive `amountOut`\n /// @return sqrtPriceX96After The sqrt price of the pool after the swap\n /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactOutputSingle(QuoteExactOutputSingleParams memory params)\n external\n returns (\n uint256 amountIn,\n uint160 sqrtPriceX96After,\n uint32 initializedTicksCrossed,\n uint256 gasEstimate\n );\n}\n" + }, + "contracts/interfaces/aerodrome/ISugarHelper.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\npragma abicoder v2;\n\nimport { INonfungiblePositionManager } from \"./INonfungiblePositionManager.sol\";\n\ninterface ISugarHelper {\n struct PopulatedTick {\n int24 tick;\n uint160 sqrtRatioX96;\n int128 liquidityNet;\n uint128 liquidityGross;\n }\n\n ///\n /// Wrappers for LiquidityAmounts\n ///\n\n function getAmountsForLiquidity(\n uint160 sqrtRatioX96,\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96,\n uint128 liquidity\n ) external pure returns (uint256 amount0, uint256 amount1);\n\n function getLiquidityForAmounts(\n uint256 amount0,\n uint256 amount1,\n uint160 sqrtRatioX96,\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96\n ) external pure returns (uint128 liquidity);\n\n /// @notice Computes the amount of token0 for a given amount of token1 and price range\n /// @param amount1 Amount of token1 to estimate liquidity\n /// @param pool Address of the pool to be used\n /// @param sqrtRatioX96 A sqrt price representing the current pool prices\n /// @param tickLow Lower tick boundary\n /// @param tickLow Upper tick boundary\n /// @dev If the given pool address is not the zero address, will fetch `sqrtRatioX96` from pool\n /// @return amount0 Estimated amount of token0\n function estimateAmount0(\n uint256 amount1,\n address pool,\n uint160 sqrtRatioX96,\n int24 tickLow,\n int24 tickHigh\n ) external view returns (uint256 amount0);\n\n /// @notice Computes the amount of token1 for a given amount of token0 and price range\n /// @param amount0 Amount of token0 to estimate liquidity\n /// @param pool Address of the pool to be used\n /// @param sqrtRatioX96 A sqrt price representing the current pool prices\n /// @param tickLow Lower tick boundary\n /// @param tickLow Upper tick boundary\n /// @dev If the given pool address is not the zero address, will fetch `sqrtRatioX96` from pool\n /// @return amount1 Estimated amount of token1\n function estimateAmount1(\n uint256 amount0,\n address pool,\n uint160 sqrtRatioX96,\n int24 tickLow,\n int24 tickHigh\n ) external view returns (uint256 amount1);\n\n ///\n /// Wrappers for PositionValue\n ///\n\n function principal(\n INonfungiblePositionManager positionManager,\n uint256 tokenId,\n uint160 sqrtRatioX96\n ) external view returns (uint256 amount0, uint256 amount1);\n\n function fees(INonfungiblePositionManager positionManager, uint256 tokenId)\n external\n view\n returns (uint256 amount0, uint256 amount1);\n\n ///\n /// Wrappers for TickMath\n ///\n\n function getSqrtRatioAtTick(int24 tick)\n external\n pure\n returns (uint160 sqrtRatioX96);\n\n function getTickAtSqrtRatio(uint160 sqrtRatioX96)\n external\n pure\n returns (int24 tick);\n\n /// @notice Fetches Tick Data for all populated Ticks in given bitmaps\n /// @param pool Address of the pool from which to fetch data\n /// @param startTick Tick from which the first bitmap will be fetched\n /// @dev The number of bitmaps fetched by this function should always be `MAX_BITMAPS`,\n /// unless there are less than `MAX_BITMAPS` left to iterate through\n /// @return populatedTicks Array of all Populated Ticks in the provided bitmaps\n function getPopulatedTicks(address pool, int24 startTick)\n external\n view\n returns (PopulatedTick[] memory populatedTicks);\n}\n" + }, + "contracts/interfaces/aerodrome/ISwapRouter.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title Router token swapping functionality\n/// @notice Functions for swapping tokens via CL\ninterface ISwapRouter {\n struct ExactInputSingleParams {\n address tokenIn;\n address tokenOut;\n int24 tickSpacing;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInputSingle(ExactInputSingleParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactOutputSingleParams {\n address tokenIn;\n address tokenOut;\n int24 tickSpacing;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutputSingle(ExactOutputSingleParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n\n struct ExactOutputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutput(ExactOutputParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n}\n" + }, + "contracts/interfaces/balancer/IBalancerVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"../../utils/InitializableAbstractStrategy.sol\";\n\ninterface IBalancerVault {\n enum WeightedPoolJoinKind {\n INIT,\n EXACT_TOKENS_IN_FOR_BPT_OUT,\n TOKEN_IN_FOR_EXACT_BPT_OUT,\n ALL_TOKENS_IN_FOR_EXACT_BPT_OUT,\n ADD_TOKEN\n }\n\n enum WeightedPoolExitKind {\n EXACT_BPT_IN_FOR_ONE_TOKEN_OUT,\n EXACT_BPT_IN_FOR_TOKENS_OUT,\n BPT_IN_FOR_EXACT_TOKENS_OUT,\n REMOVE_TOKEN\n }\n\n /**\n * @dev Called by users to join a Pool, which transfers tokens from `sender` into the Pool's balance. This will\n * trigger custom Pool behavior, which will typically grant something in return to `recipient` - often tokenized\n * Pool shares.\n *\n * If the caller is not `sender`, it must be an authorized relayer for them.\n *\n * The `assets` and `maxAmountsIn` arrays must have the same length, and each entry indicates the maximum amount\n * to send for each asset. The amounts to send are decided by the Pool and not the Vault: it just enforces\n * these maximums.\n *\n * If joining a Pool that holds WETH, it is possible to send ETH directly: the Vault will do the wrapping. To enable\n * this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead of the\n * WETH address. Note that it is not possible to combine ETH and WETH in the same join. Any excess ETH will be sent\n * back to the caller (not the sender, which is important for relayers).\n *\n * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when\n * interacting with Pools that register and deregister tokens frequently. If sending ETH however, the array must be\n * sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the final\n * `assets` array might not be sorted. Pools with no registered tokens cannot be joined.\n *\n * If `fromInternalBalance` is true, the caller's Internal Balance will be preferred: ERC20 transfers will only\n * be made for the difference between the requested amount and Internal Balance (if any). Note that ETH cannot be\n * withdrawn from Internal Balance: attempting to do so will trigger a revert.\n *\n * This causes the Vault to call the `IBasePool.onJoinPool` hook on the Pool's contract, where Pools implement\n * their own custom logic. This typically requires additional information from the user (such as the expected number\n * of Pool shares). This can be encoded in the `userData` argument, which is ignored by the Vault and passed\n * directly to the Pool's contract, as is `recipient`.\n *\n * Emits a `PoolBalanceChanged` event.\n */\n function joinPool(\n bytes32 poolId,\n address sender,\n address recipient,\n JoinPoolRequest memory request\n ) external payable;\n\n struct JoinPoolRequest {\n address[] assets;\n uint256[] maxAmountsIn;\n bytes userData;\n bool fromInternalBalance;\n }\n\n /**\n * @dev Called by users to exit a Pool, which transfers tokens from the Pool's balance to `recipient`. This will\n * trigger custom Pool behavior, which will typically ask for something in return from `sender` - often tokenized\n * Pool shares. The amount of tokens that can be withdrawn is limited by the Pool's `cash` balance (see\n * `getPoolTokenInfo`).\n *\n * If the caller is not `sender`, it must be an authorized relayer for them.\n *\n * The `tokens` and `minAmountsOut` arrays must have the same length, and each entry in these indicates the minimum\n * token amount to receive for each token contract. The amounts to send are decided by the Pool and not the Vault:\n * it just enforces these minimums.\n *\n * If exiting a Pool that holds WETH, it is possible to receive ETH directly: the Vault will do the unwrapping. To\n * enable this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead\n * of the WETH address. Note that it is not possible to combine ETH and WETH in the same exit.\n *\n * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when\n * interacting with Pools that register and deregister tokens frequently. If receiving ETH however, the array must\n * be sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the\n * final `assets` array might not be sorted. Pools with no registered tokens cannot be exited.\n *\n * If `toInternalBalance` is true, the tokens will be deposited to `recipient`'s Internal Balance. Otherwise,\n * an ERC20 transfer will be performed. Note that ETH cannot be deposited to Internal Balance: attempting to\n * do so will trigger a revert.\n *\n * `minAmountsOut` is the minimum amount of tokens the user expects to get out of the Pool, for each token in the\n * `tokens` array. This array must match the Pool's registered tokens.\n *\n * This causes the Vault to call the `IBasePool.onExitPool` hook on the Pool's contract, where Pools implement\n * their own custom logic. This typically requires additional information from the user (such as the expected number\n * of Pool shares to return). This can be encoded in the `userData` argument, which is ignored by the Vault and\n * passed directly to the Pool's contract.\n *\n * Emits a `PoolBalanceChanged` event.\n */\n function exitPool(\n bytes32 poolId,\n address sender,\n address payable recipient,\n ExitPoolRequest memory request\n ) external;\n\n struct ExitPoolRequest {\n address[] assets;\n uint256[] minAmountsOut;\n bytes userData;\n bool toInternalBalance;\n }\n\n /**\n * @dev Returns a Pool's registered tokens, the total balance for each, and the latest block when *any* of\n * the tokens' `balances` changed.\n *\n * The order of the `tokens` array is the same order that will be used in `joinPool`, `exitPool`, as well as in all\n * Pool hooks (where applicable). Calls to `registerTokens` and `deregisterTokens` may change this order.\n *\n * If a Pool only registers tokens once, and these are sorted in ascending order, they will be stored in the same\n * order as passed to `registerTokens`.\n *\n * Total balances include both tokens held by the Vault and those withdrawn by the Pool's Asset Managers. These are\n * the amounts used by joins, exits and swaps. For a detailed breakdown of token balances, use `getPoolTokenInfo`\n * instead.\n */\n function getPoolTokens(bytes32 poolId)\n external\n view\n returns (\n IERC20[] memory tokens,\n uint256[] memory balances,\n uint256 lastChangeBlock\n );\n\n /**\n * @dev Performs a set of user balance operations, which involve Internal Balance (deposit, withdraw or transfer)\n * and plain ERC20 transfers using the Vault's allowance. This last feature is particularly useful for relayers, as\n * it lets integrators reuse a user's Vault allowance.\n *\n * For each operation, if the caller is not `sender`, it must be an authorized relayer for them.\n */\n function manageUserBalance(UserBalanceOp[] memory ops) external payable;\n\n struct UserBalanceOp {\n UserBalanceOpKind kind;\n address asset;\n uint256 amount;\n address sender;\n address payable recipient;\n }\n\n enum UserBalanceOpKind {\n DEPOSIT_INTERNAL,\n WITHDRAW_INTERNAL,\n TRANSFER_INTERNAL,\n TRANSFER_EXTERNAL\n }\n\n enum SwapKind {\n GIVEN_IN,\n GIVEN_OUT\n }\n\n struct SingleSwap {\n bytes32 poolId;\n SwapKind kind;\n address assetIn;\n address assetOut;\n uint256 amount;\n bytes userData;\n }\n\n struct FundManagement {\n address sender;\n bool fromInternalBalance;\n address payable recipient;\n bool toInternalBalance;\n }\n\n function swap(\n SingleSwap calldata singleSwap,\n FundManagement calldata funds,\n uint256 limit,\n uint256 deadline\n ) external returns (uint256 amountCalculated);\n\n function getPoolTokenInfo(bytes32 poolId, address token)\n external\n view\n returns (\n uint256 cash,\n uint256 managed,\n uint256 lastChangeBlock,\n address assetManager\n );\n}\n" + }, + "contracts/interfaces/balancer/IMetaStablePool.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IRateProvider } from \"./IRateProvider.sol\";\n\ninterface IMetaStablePool {\n function getRateProviders()\n external\n view\n returns (IRateProvider[] memory providers);\n}\n" + }, + "contracts/interfaces/balancer/IOracleWeightedPool.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// The three values that can be queried:\n//\n// - PAIR_PRICE: the price of the tokens in the Pool, expressed as the price of the second token in units of the\n// first token. For example, if token A is worth $2, and token B is worth $4, the pair price will be 2.0.\n// Note that the price is computed *including* the tokens decimals. This means that the pair price of a Pool with\n// DAI and USDC will be close to 1.0, despite DAI having 18 decimals and USDC 6.\n//\n// - BPT_PRICE: the price of the Pool share token (BPT), in units of the first token.\n// Note that the price is computed *including* the tokens decimals. This means that the BPT price of a Pool with\n// USDC in which BPT is worth $5 will be 5.0, despite the BPT having 18 decimals and USDC 6.\n//\n// - INVARIANT: the value of the Pool's invariant, which serves as a measure of its liquidity.\nenum Variable {\n PAIR_PRICE,\n BPT_PRICE,\n INVARIANT\n}\n\n/**\n * @dev Information for a Time Weighted Average query.\n *\n * Each query computes the average over a window of duration `secs` seconds that ended `ago` seconds ago. For\n * example, the average over the past 30 minutes is computed by settings secs to 1800 and ago to 0. If secs is 1800\n * and ago is 1800 as well, the average between 60 and 30 minutes ago is computed instead.\n */\nstruct OracleAverageQuery {\n Variable variable;\n uint256 secs;\n uint256 ago;\n}\n\ninterface IOracleWeightedPool {\n /**\n * @dev Returns the time average weighted price corresponding to each of `queries`. Prices are represented as 18\n * decimal fixed point values.\n */\n function getTimeWeightedAverage(OracleAverageQuery[] memory queries)\n external\n view\n returns (uint256[] memory results);\n}\n" + }, + "contracts/interfaces/balancer/IRateProvider.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity ^0.8.0;\n\ninterface IRateProvider {\n function getRate() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/chainlink/AggregatorV3Interface.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorV3Interface {\n function decimals() external view returns (uint8);\n\n function description() external view returns (string memory);\n\n function version() external view returns (uint256);\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(uint80 _roundId)\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n\n function latestRoundData()\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconOracle {\n function blockToSlot(uint64 blockNumber) external returns (uint64 slot);\n\n function slotToBlock(uint64 slot) external returns (uint64 blockNumber);\n\n function slotToRoot(uint64 slot) external returns (bytes32 blockRoot);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n enum BalanceProofLevel {\n Container,\n BeaconBlock\n }\n\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 root,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex,\n BalanceProofLevel level\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view;\n\n function verifyBlockNumber(\n bytes32 beaconBlockRoot,\n uint256 blockNumber,\n bytes calldata blockNumberProof\n ) external view;\n\n function verifySlot(\n bytes32 beaconBlockRoot,\n uint256 slot,\n bytes calldata slotProof\n ) external view;\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n external\n pure\n returns (uint256);\n\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) external pure returns (uint256);\n}\n" + }, + "contracts/interfaces/IBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBuyback {\n function swap() external;\n}\n" + }, + "contracts/interfaces/ICampaignRemoteManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICampaignRemoteManager {\n function createCampaign(\n CampaignCreationParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n function manageCampaign(\n CampaignManagementParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n function closeCampaign(\n CampaignClosingParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n struct CampaignCreationParams {\n uint256 chainId;\n address gauge;\n address manager;\n address rewardToken;\n uint8 numberOfPeriods;\n uint256 maxRewardPerVote;\n uint256 totalRewardAmount;\n address[] addresses;\n address hook;\n bool isWhitelist;\n }\n\n struct CampaignManagementParams {\n uint256 campaignId;\n address rewardToken;\n uint8 numberOfPeriods;\n uint256 totalRewardAmount;\n uint256 maxRewardPerVote;\n }\n\n struct CampaignClosingParams {\n uint256 campaignId;\n }\n}\n" + }, + "contracts/interfaces/IChildLiquidityGaugeFactory.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IChildLiquidityGaugeFactory {\n event DeployedGauge(\n address indexed _implementation,\n address indexed _lp_token,\n address indexed _deployer,\n bytes32 _salt,\n address _gauge\n );\n event Minted(\n address indexed _user,\n address indexed _gauge,\n uint256 _new_total\n );\n event TransferOwnership(address _old_owner, address _new_owner);\n event UpdateCallProxy(address _old_call_proxy, address _new_call_proxy);\n event UpdateImplementation(\n address _old_implementation,\n address _new_implementation\n );\n event UpdateManager(address _manager);\n event UpdateMirrored(address indexed _gauge, bool _mirrored);\n event UpdateRoot(address _factory, address _implementation);\n event UpdateVotingEscrow(\n address _old_voting_escrow,\n address _new_voting_escrow\n );\n\n function accept_transfer_ownership() external;\n\n function call_proxy() external view returns (address);\n\n function commit_transfer_ownership(address _future_owner) external;\n\n function crv() external view returns (address);\n\n function deploy_gauge(address _lp_token, bytes32 _salt)\n external\n returns (address);\n\n function deploy_gauge(\n address _lp_token,\n bytes32 _salt,\n address _manager\n ) external returns (address);\n\n function future_owner() external view returns (address);\n\n function gauge_data(address arg0) external view returns (uint256);\n\n function get_gauge(uint256 arg0) external view returns (address);\n\n function get_gauge_count() external view returns (uint256);\n\n function get_gauge_from_lp_token(address arg0)\n external\n view\n returns (address);\n\n function get_implementation() external view returns (address);\n\n function is_mirrored(address _gauge) external view returns (bool);\n\n function is_valid_gauge(address _gauge) external view returns (bool);\n\n function last_request(address _gauge) external view returns (uint256);\n\n function manager() external view returns (address);\n\n function mint(address _gauge) external;\n\n function mint_many(address[32] memory _gauges) external;\n\n function minted(address arg0, address arg1) external view returns (uint256);\n\n function owner() external view returns (address);\n\n function root_factory() external view returns (address);\n\n function root_implementation() external view returns (address);\n\n function set_call_proxy(address _new_call_proxy) external;\n\n function set_crv(address _crv) external;\n\n function set_implementation(address _implementation) external;\n\n function set_manager(address _new_manager) external;\n\n function set_mirrored(address _gauge, bool _mirrored) external;\n\n function set_root(address _factory, address _implementation) external;\n\n function set_voting_escrow(address _voting_escrow) external;\n\n function version() external view returns (string memory);\n\n function voting_escrow() external view returns (address);\n}\n" + }, + "contracts/interfaces/IComptroller.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IComptroller {\n // Claim all the COMP accrued by specific holders in specific markets for their supplies and/or borrows\n function claimComp(\n address[] memory holders,\n address[] memory cTokens,\n bool borrowers,\n bool suppliers\n ) external;\n\n function oracle() external view returns (address);\n}\n" + }, + "contracts/interfaces/IConsolidation.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IConsolidationSource {\n function confirmConsolidation()\n external\n returns (uint256 consolidationCount);\n}\n\ninterface IConsolidationTarget {\n function requestConsolidation(\n bytes32 lastSourcePubKeyHash,\n bytes32 targetPubKeyHash\n ) external;\n}\n" + }, + "contracts/interfaces/ICurveLiquidityGaugeV6.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveLiquidityGaugeV6 {\n event ApplyOwnership(address admin);\n event Approval(\n address indexed _owner,\n address indexed _spender,\n uint256 _value\n );\n event CommitOwnership(address admin);\n event Deposit(address indexed provider, uint256 value);\n event SetGaugeManager(address _gauge_manager);\n event Transfer(address indexed _from, address indexed _to, uint256 _value);\n event UpdateLiquidityLimit(\n address indexed user,\n uint256 original_balance,\n uint256 original_supply,\n uint256 working_balance,\n uint256 working_supply\n );\n event Withdraw(address indexed provider, uint256 value);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_reward(address _reward_token, address _distributor) external;\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function claim_rewards() external;\n\n function claim_rewards(address _addr) external;\n\n function claim_rewards(address _addr, address _receiver) external;\n\n function claimable_reward(address _user, address _reward_token)\n external\n view\n returns (uint256);\n\n function claimable_tokens(address addr) external returns (uint256);\n\n function claimed_reward(address _addr, address _token)\n external\n view\n returns (uint256);\n\n function decimals() external view returns (uint256);\n\n function decreaseAllowance(address _spender, uint256 _subtracted_value)\n external\n returns (bool);\n\n function deposit(uint256 _value) external;\n\n function deposit(uint256 _value, address _addr) external;\n\n function deposit(\n uint256 _value,\n address _addr,\n bool _claim_rewards\n ) external;\n\n function deposit_reward_token(address _reward_token, uint256 _amount)\n external;\n\n function deposit_reward_token(\n address _reward_token,\n uint256 _amount,\n uint256 _epoch\n ) external;\n\n function factory() external view returns (address);\n\n function future_epoch_time() external view returns (uint256);\n\n function increaseAllowance(address _spender, uint256 _added_value)\n external\n returns (bool);\n\n function inflation_rate() external view returns (uint256);\n\n function integrate_checkpoint() external view returns (uint256);\n\n function integrate_checkpoint_of(address arg0)\n external\n view\n returns (uint256);\n\n function integrate_fraction(address arg0) external view returns (uint256);\n\n function integrate_inv_supply(uint256 arg0) external view returns (uint256);\n\n function integrate_inv_supply_of(address arg0)\n external\n view\n returns (uint256);\n\n function is_killed() external view returns (bool);\n\n function kick(address addr) external;\n\n function lp_token() external view returns (address);\n\n function manager() external view returns (address);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function period() external view returns (int128);\n\n function period_timestamp(uint256 arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function reward_count() external view returns (uint256);\n\n function reward_integral_for(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function reward_tokens(uint256 arg0) external view returns (address);\n\n function rewards_receiver(address arg0) external view returns (address);\n\n function salt() external view returns (bytes32);\n\n function set_gauge_manager(address _gauge_manager) external;\n\n function set_killed(bool _is_killed) external;\n\n function set_reward_distributor(address _reward_token, address _distributor)\n external;\n\n function set_rewards_receiver(address _receiver) external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function user_checkpoint(address addr) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw(uint256 _value) external;\n\n function withdraw(uint256 _value, bool _claim_rewards) external;\n\n function working_balances(address arg0) external view returns (uint256);\n\n function working_supply() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/ICurveMinter.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveMinter {\n event Minted(address indexed recipient, address gauge, uint256 minted);\n\n function allowed_to_mint_for(address arg0, address arg1)\n external\n view\n returns (bool);\n\n function controller() external view returns (address);\n\n function mint(address gauge_addr) external;\n\n function mint_for(address gauge_addr, address _for) external;\n\n function mint_many(address[8] memory gauge_addrs) external;\n\n function minted(address arg0, address arg1) external view returns (uint256);\n\n function toggle_approve_mint(address minting_user) external;\n\n function token() external view returns (address);\n}\n" + }, + "contracts/interfaces/ICurveStableSwapNG.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveStableSwapNG {\n event AddLiquidity(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event ApplyNewFee(uint256 fee, uint256 offpeg_fee_multiplier);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event RampA(\n uint256 old_A,\n uint256 new_A,\n uint256 initial_time,\n uint256 future_time\n );\n event RemoveLiquidity(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 token_supply\n );\n event RemoveLiquidityImbalance(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event RemoveLiquidityOne(\n address indexed provider,\n int128 token_id,\n uint256 token_amount,\n uint256 coin_amount,\n uint256 token_supply\n );\n event SetNewMATime(uint256 ma_exp_time, uint256 D_ma_time);\n event StopRampA(uint256 A, uint256 t);\n event TokenExchange(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event TokenExchangeUnderlying(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event Transfer(\n address indexed sender,\n address indexed receiver,\n uint256 value\n );\n\n function A() external view returns (uint256);\n\n function A_precise() external view returns (uint256);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function D_ma_time() external view returns (uint256);\n\n function D_oracle() external view returns (uint256);\n\n function N_COINS() external view returns (uint256);\n\n function add_liquidity(uint256[] memory _amounts, uint256 _min_mint_amount)\n external\n returns (uint256);\n\n function add_liquidity(\n uint256[] memory _amounts,\n uint256 _min_mint_amount,\n address _receiver\n ) external returns (uint256);\n\n function admin_balances(uint256 arg0) external view returns (uint256);\n\n function admin_fee() external view returns (uint256);\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function balances(uint256 i) external view returns (uint256);\n\n function calc_token_amount(uint256[] memory _amounts, bool _is_deposit)\n external\n view\n returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _burn_amount, int128 i)\n external\n view\n returns (uint256);\n\n function coins(uint256 arg0) external view returns (address);\n\n function decimals() external view returns (uint8);\n\n function dynamic_fee(int128 i, int128 j) external view returns (uint256);\n\n function ema_price(uint256 i) external view returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external returns (uint256);\n\n function exchange_received(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external returns (uint256);\n\n function exchange_received(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external returns (uint256);\n\n function fee() external view returns (uint256);\n\n function future_A() external view returns (uint256);\n\n function future_A_time() external view returns (uint256);\n\n function get_balances() external view returns (uint256[] memory);\n\n function get_dx(\n int128 i,\n int128 j,\n uint256 dy\n ) external view returns (uint256);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n\n function get_p(uint256 i) external view returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function initial_A() external view returns (uint256);\n\n function initial_A_time() external view returns (uint256);\n\n function last_price(uint256 i) external view returns (uint256);\n\n function ma_exp_time() external view returns (uint256);\n\n function ma_last_time() external view returns (uint256);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function offpeg_fee_multiplier() external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function price_oracle(uint256 i) external view returns (uint256);\n\n function ramp_A(uint256 _future_A, uint256 _future_time) external;\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts\n ) external returns (uint256[] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts,\n address _receiver\n ) external returns (uint256[] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts,\n address _receiver,\n bool _claim_admin_fees\n ) external returns (uint256[] memory);\n\n function remove_liquidity_imbalance(\n uint256[] memory _amounts,\n uint256 _max_burn_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[] memory _amounts,\n uint256 _max_burn_amount,\n address _receiver\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received,\n address _receiver\n ) external returns (uint256);\n\n function salt() external view returns (bytes32);\n\n function set_ma_exp_time(uint256 _ma_exp_time, uint256 _D_ma_time) external;\n\n function set_new_fee(uint256 _new_fee, uint256 _new_offpeg_fee_multiplier)\n external;\n\n function stop_ramp_A() external;\n\n function stored_rates() external view returns (uint256[] memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw_admin_fees() external;\n}\n" + }, + "contracts/interfaces/ICurveXChainLiquidityGauge.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveXChainLiquidityGauge {\n event Approval(\n address indexed _owner,\n address indexed _spender,\n uint256 _value\n );\n event Deposit(address indexed provider, uint256 value);\n event SetGaugeManager(address _gauge_manager);\n event Transfer(address indexed _from, address indexed _to, uint256 _value);\n event UpdateLiquidityLimit(\n address indexed user,\n uint256 original_balance,\n uint256 original_supply,\n uint256 working_balance,\n uint256 working_supply\n );\n event Withdraw(address indexed provider, uint256 value);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_reward(address _reward_token, address _distributor) external;\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function claim_rewards() external;\n\n function claim_rewards(address _addr) external;\n\n function claim_rewards(address _addr, address _receiver) external;\n\n function claimable_reward(address _user, address _reward_token)\n external\n view\n returns (uint256);\n\n function claimable_tokens(address addr) external returns (uint256);\n\n function claimed_reward(address _addr, address _token)\n external\n view\n returns (uint256);\n\n function decimals() external view returns (uint256);\n\n function decreaseAllowance(address _spender, uint256 _subtracted_value)\n external\n returns (bool);\n\n function deposit(uint256 _value) external;\n\n function deposit(uint256 _value, address _addr) external;\n\n function deposit(\n uint256 _value,\n address _addr,\n bool _claim_rewards\n ) external;\n\n function deposit_reward_token(address _reward_token, uint256 _amount)\n external;\n\n function deposit_reward_token(\n address _reward_token,\n uint256 _amount,\n uint256 _epoch\n ) external;\n\n function factory() external view returns (address);\n\n function increaseAllowance(address _spender, uint256 _added_value)\n external\n returns (bool);\n\n function inflation_rate(uint256 arg0) external view returns (uint256);\n\n function initialize(\n address _lp_token,\n address _root,\n address _manager\n ) external;\n\n function integrate_checkpoint() external view returns (uint256);\n\n function integrate_checkpoint_of(address arg0)\n external\n view\n returns (uint256);\n\n function integrate_fraction(address arg0) external view returns (uint256);\n\n function integrate_inv_supply(int128 arg0) external view returns (uint256);\n\n function integrate_inv_supply_of(address arg0)\n external\n view\n returns (uint256);\n\n function is_killed() external view returns (bool);\n\n function lp_token() external view returns (address);\n\n function manager() external view returns (address);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function period() external view returns (int128);\n\n function period_timestamp(int128 arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function recover_remaining(address _reward_token) external;\n\n function reward_count() external view returns (uint256);\n\n function reward_integral_for(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function reward_remaining(address arg0) external view returns (uint256);\n\n function reward_tokens(uint256 arg0) external view returns (address);\n\n function rewards_receiver(address arg0) external view returns (address);\n\n function root_gauge() external view returns (address);\n\n function set_gauge_manager(address _gauge_manager) external;\n\n function set_killed(bool _is_killed) external;\n\n function set_manager(address _gauge_manager) external;\n\n function set_reward_distributor(address _reward_token, address _distributor)\n external;\n\n function set_rewards_receiver(address _receiver) external;\n\n function set_root_gauge(address _root) external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function update_voting_escrow() external;\n\n function user_checkpoint(address addr) external returns (bool);\n\n function version() external view returns (string memory);\n\n function voting_escrow() external view returns (address);\n\n function withdraw(uint256 _value) external;\n\n function withdraw(uint256 _value, bool _claim_rewards) external;\n\n function withdraw(\n uint256 _value,\n bool _claim_rewards,\n address _receiver\n ) external;\n\n function working_balances(address arg0) external view returns (uint256);\n\n function working_supply() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/ICVXLocker.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface ICVXLocker {\n function lock(\n address _account,\n uint256 _amount,\n uint256 _spendRatio\n ) external;\n\n function lockedBalanceOf(address _account) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/IDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IDripper {\n /// @notice How much funds have dripped out already and are currently\n // available to be sent to the vault.\n /// @return The amount that would be sent if a collect was called\n function availableFunds() external view returns (uint256);\n\n /// @notice Collect all dripped funds and send to vault.\n /// Recalculate new drip rate.\n function collect() external;\n\n /// @notice Collect all dripped funds, send to vault, recalculate new drip\n /// rate, and rebase mToken.\n function collectAndRebase() external;\n\n /// @notice Change the drip duration. Governor only.\n /// @param _durationSeconds the number of seconds to drip out the entire\n /// balance over if no collects were called during that time.\n function setDripDuration(uint256 _durationSeconds) external;\n\n /// @dev Transfer out ERC20 tokens held by the contract. Governor only.\n /// @param _asset ERC20 token address\n /// @param _amount amount to transfer\n function transferToken(address _asset, uint256 _amount) external;\n}\n" + }, + "contracts/interfaces/IEthUsdOracle.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IEthUsdOracle {\n /**\n * @notice Returns ETH price in USD.\n * @return Price in USD with 6 decimal digits.\n */\n function ethUsdPrice() external view returns (uint256);\n\n /**\n * @notice Returns token price in USD.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in USD with 6 decimal digits.\n */\n function tokUsdPrice(string calldata symbol)\n external\n view\n returns (uint256);\n\n /**\n * @notice Returns the asset price in ETH.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in ETH with 8 decimal digits.\n */\n function tokEthPrice(string calldata symbol)\n external\n view\n returns (uint256);\n}\n\ninterface IViewEthUsdOracle {\n /**\n * @notice Returns ETH price in USD.\n * @return Price in USD with 6 decimal digits.\n */\n function ethUsdPrice() external view returns (uint256);\n\n /**\n * @notice Returns token price in USD.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in USD with 6 decimal digits.\n */\n function tokUsdPrice(string calldata symbol)\n external\n view\n returns (uint256);\n\n /**\n * @notice Returns the asset price in ETH.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in ETH with 8 decimal digits.\n */\n function tokEthPrice(string calldata symbol)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/IFraxETHMinter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IFraxETHMinter {\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares);\n}\n" + }, + "contracts/interfaces/IGetExchangeRateToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IGetExchangeRateToken {\n function getExchangeRate() external view returns (uint256 _exchangeRate);\n}\n" + }, + "contracts/interfaces/IMinMaxOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IMinMaxOracle {\n //Assuming 8 decimals\n function priceMin(string calldata symbol) external view returns (uint256);\n\n function priceMax(string calldata symbol) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IMockVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"./IVault.sol\";\n\ninterface IMockVault is IVault {\n function outstandingWithdrawalsAmount() external view returns (uint256);\n\n function wethAvailable() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IOETHZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOETHZapper {\n function deposit() external payable returns (uint256);\n}\n" + }, + "contracts/interfaces/IOneInch.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/// 1Inch swap data\nstruct SwapDescription {\n IERC20 srcToken; // contract address of a token to sell\n IERC20 dstToken; // contract address of a token to buy\n address payable srcReceiver;\n address payable dstReceiver; // Receiver of destination currency. default: fromAddress\n uint256 amount;\n uint256 minReturnAmount;\n uint256 flags;\n}\n\n/// @title Interface for making arbitrary calls during swap\ninterface IAggregationExecutor {\n /// @notice propagates information about original msg.sender and executes arbitrary data\n function execute(address msgSender) external payable; // 0x4b64e492\n}\n\ninterface IOneInchRouter {\n /// @notice Performs a swap, delegating all calls encoded in `data` to `executor`.\n function swap(\n IAggregationExecutor executor,\n SwapDescription calldata desc,\n bytes calldata permit,\n bytes calldata data\n ) external returns (uint256 returnAmount, uint256 spentAmount);\n\n /// @notice Performs swap using Uniswap exchange. Wraps and unwraps ETH if required.\n function unoswapTo(\n address payable recipient,\n IERC20 srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) external payable returns (uint256 returnAmount);\n\n /// @notice Performs swap using Uniswap V3 exchange. Wraps and unwraps ETH if required.\n function uniswapV3SwapTo(\n address payable recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) external payable returns (uint256 returnAmount);\n}\n" + }, + "contracts/interfaces/IOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOracle {\n /**\n * @dev returns the asset price in USD, in 8 decimal digits.\n *\n * The version of priceProvider deployed for OETH has 18 decimal digits\n */\n function price(address asset) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IOUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOUSD {\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n function _totalSupply() external view returns (uint256);\n\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address _account) external view returns (uint256);\n\n function burn(address account, uint256 amount) external;\n\n function changeSupply(uint256 _newTotalSupply) external;\n\n function claimGovernance() external;\n\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256);\n\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n );\n\n function decimals() external view returns (uint8);\n\n function decreaseAllowance(address _spender, uint256 _subtractedValue)\n external\n returns (bool);\n\n function governor() external view returns (address);\n\n function increaseAllowance(address _spender, uint256 _addedValue)\n external\n returns (bool);\n\n function initialize(\n string memory _nameArg,\n string memory _symbolArg,\n address _vaultAddress\n ) external;\n\n function isGovernor() external view returns (bool);\n\n function isUpgraded(address) external view returns (uint256);\n\n function mint(address _account, uint256 _amount) external;\n\n function name() external view returns (string memory);\n\n function nonRebasingCreditsPerToken(address)\n external\n view\n returns (uint256);\n\n function nonRebasingSupply() external view returns (uint256);\n\n function rebaseOptIn() external;\n\n function rebaseOptOut() external;\n\n function rebaseState(address) external view returns (uint8);\n\n function rebasingCredits() external view returns (uint256);\n\n function rebasingCreditsHighres() external view returns (uint256);\n\n function rebasingCreditsPerToken() external view returns (uint256);\n\n function rebasingCreditsPerTokenHighres() external view returns (uint256);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function transferGovernance(address _newGovernor) external;\n\n function vaultAddress() external view returns (address);\n}\n" + }, + "contracts/interfaces/IPriceOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPriceOracle {\n /**\n * @dev returns the asset price in USD, 6 decimal digits.\n * Compatible with the Open Price Feed.\n */\n function price(string calldata symbol) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IRETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IRETH {\n function getEthValue(uint256 _rethAmount) external view returns (uint256);\n\n function getRethValue(uint256 _ethAmount) external view returns (uint256);\n\n function getExchangeRate() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function balanceOf(address account) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 amount) external returns (bool);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/ISafe.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ISafe {\n function execTransactionFromModule(\n address,\n uint256,\n bytes memory,\n uint8\n ) external returns (bool);\n}\n" + }, + "contracts/interfaces/ISfrxETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface ISfrxETH {\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 amount\n );\n event Deposit(\n address indexed caller,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n event NewRewardsCycle(uint32 indexed cycleEnd, uint256 rewardAmount);\n event Transfer(address indexed from, address indexed to, uint256 amount);\n event Withdraw(\n address indexed caller,\n address indexed receiver,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address spender, uint256 amount) external returns (bool);\n\n function asset() external view returns (address);\n\n function balanceOf(address) external view returns (uint256);\n\n function convertToAssets(uint256 shares) external view returns (uint256);\n\n function convertToShares(uint256 assets) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit(uint256 assets, address receiver)\n external\n returns (uint256 shares);\n\n function depositWithSignature(\n uint256 assets,\n address receiver,\n uint256 deadline,\n bool approveMax,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external returns (uint256 shares);\n\n function lastRewardAmount() external view returns (uint192);\n\n function lastSync() external view returns (uint32);\n\n function maxDeposit(address) external view returns (uint256);\n\n function maxMint(address) external view returns (uint256);\n\n function maxRedeem(address owner) external view returns (uint256);\n\n function maxWithdraw(address owner) external view returns (uint256);\n\n function mint(uint256 shares, address receiver)\n external\n returns (uint256 assets);\n\n function name() external view returns (string memory);\n\n function nonces(address) external view returns (uint256);\n\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function previewDeposit(uint256 assets) external view returns (uint256);\n\n function previewMint(uint256 shares) external view returns (uint256);\n\n function previewRedeem(uint256 shares) external view returns (uint256);\n\n function previewWithdraw(uint256 assets) external view returns (uint256);\n\n function pricePerShare() external view returns (uint256);\n\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets);\n\n function rewardsCycleEnd() external view returns (uint32);\n\n function rewardsCycleLength() external view returns (uint32);\n\n function symbol() external view returns (string memory);\n\n function syncRewards() external;\n\n function totalAssets() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address to, uint256 amount) external returns (bool);\n\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/ISwapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ISwapper {\n /**\n * @param fromAsset The token address of the asset being sold.\n * @param toAsset The token address of the asset being purchased.\n * @param fromAssetAmount The amount of assets being sold.\n * @param minToAssetAmmount The minimum amount of assets to be purchased.\n * @param data tx.data returned from 1Inch's /v5.0/1/swap API\n */\n function swap(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n}\n" + }, + "contracts/interfaces/ITimelock.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ITimelock {\n function delay() external view returns (uint256);\n\n function GRACE_PERIOD() external view returns (uint256);\n\n function acceptAdmin() external;\n\n function queuedTransactions(bytes32 hash) external view returns (bool);\n\n function queueTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external returns (bytes32);\n\n function cancelTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external;\n\n function executeTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external payable returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ITimelockController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ITimelockController {\n function grantRole(bytes32 role, address account) external;\n\n function revokeRole(bytes32 role, address account) external;\n\n function renounceRole(bytes32 role, address account) external;\n\n function hasRole(bytes32 role, address account)\n external\n view\n returns (bool);\n\n function executeBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt\n ) external payable;\n\n function scheduleBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt,\n uint256 delay\n ) external;\n\n function hashOperationBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt\n ) external view returns (bytes32);\n\n function isOperationDone(bytes32 opHash) external view returns (bool);\n\n function isOperationReady(bytes32 opHash) external view returns (bool);\n\n function isOperation(bytes32 opHash) external view returns (bool);\n\n function getMinDelay() external view returns (uint256);\n\n function updateDelay(uint256 newDelay) external;\n\n function CANCELLER_ROLE() external view returns (bytes32);\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/interfaces/IWstETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWstETH {\n /**\n * @notice Get amount of wstETH for a given amount of stETH\n * @param _stETHAmount amount of stETH\n * @return Amount of wstETH for a given stETH amount\n */\n function getWstETHByStETH(uint256 _stETHAmount)\n external\n view\n returns (uint256);\n\n /**\n * @notice Get amount of stETH for a given amount of wstETH\n * @param _wstETHAmount amount of wstETH\n * @return Amount of stETH for a given wstETH amount\n */\n function getStETHByWstETH(uint256 _wstETHAmount)\n external\n view\n returns (uint256);\n\n /**\n * @notice Get amount of stETH for a one wstETH\n * @return Amount of stETH for 1 wstETH\n */\n function stEthPerToken() external view returns (uint256);\n\n /**\n * @notice Get amount of wstETH for a one stETH\n * @return Amount of wstETH for a 1 stETH\n */\n function tokensPerStEth() external view returns (uint256);\n\n /**\n * @notice Exchanges stETH to wstETH\n * @param _stETHAmount amount of stETH to wrap in exchange for wstETH\n * @dev Requirements:\n * - `_stETHAmount` must be non-zero\n * - msg.sender must approve at least `_stETHAmount` stETH to this\n * contract.\n * - msg.sender must have at least `_stETHAmount` of stETH.\n * User should first approve _stETHAmount to the WstETH contract\n * @return Amount of wstETH user receives after wrap\n */\n function wrap(uint256 _stETHAmount) external returns (uint256);\n\n /**\n * @notice Exchanges wstETH to stETH\n * @param _wstETHAmount amount of wstETH to uwrap in exchange for stETH\n * @dev Requirements:\n * - `_wstETHAmount` must be non-zero\n * - msg.sender must have at least `_wstETHAmount` wstETH.\n * @return Amount of stETH user receives after unwrap\n */\n function unwrap(uint256 _wstETHAmount) external returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/compound/ICompoundOracle.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\ninterface ICompoundOracle {\n function getUnderlyingPrice(address) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/ILens.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\nimport \"./compound/ICompoundOracle.sol\";\nimport \"./IMorpho.sol\";\n\ninterface ILens {\n /// STORAGE ///\n\n function MAX_BASIS_POINTS() external view returns (uint256);\n\n function WAD() external view returns (uint256);\n\n function morpho() external view returns (IMorpho);\n\n function comptroller() external view returns (IComptroller);\n\n /// GENERAL ///\n\n function getTotalSupply()\n external\n view\n returns (\n uint256 p2pSupplyAmount,\n uint256 poolSupplyAmount,\n uint256 totalSupplyAmount\n );\n\n function getTotalBorrow()\n external\n view\n returns (\n uint256 p2pBorrowAmount,\n uint256 poolBorrowAmount,\n uint256 totalBorrowAmount\n );\n\n /// MARKETS ///\n\n function isMarketCreated(address _poolToken) external view returns (bool);\n\n function isMarketCreatedAndNotPaused(address _poolToken)\n external\n view\n returns (bool);\n\n function isMarketCreatedAndNotPausedNorPartiallyPaused(address _poolToken)\n external\n view\n returns (bool);\n\n function getAllMarkets()\n external\n view\n returns (address[] memory marketsCreated_);\n\n function getMainMarketData(address _poolToken)\n external\n view\n returns (\n uint256 avgSupplyRatePerBlock,\n uint256 avgBorrowRatePerBlock,\n uint256 p2pSupplyAmount,\n uint256 p2pBorrowAmount,\n uint256 poolSupplyAmount,\n uint256 poolBorrowAmount\n );\n\n function getAdvancedMarketData(address _poolToken)\n external\n view\n returns (\n uint256 p2pSupplyIndex,\n uint256 p2pBorrowIndex,\n uint256 poolSupplyIndex,\n uint256 poolBorrowIndex,\n uint32 lastUpdateBlockNumber,\n uint256 p2pSupplyDelta,\n uint256 p2pBorrowDelta\n );\n\n function getMarketConfiguration(address _poolToken)\n external\n view\n returns (\n address underlying,\n bool isCreated,\n bool p2pDisabled,\n bool isPaused,\n bool isPartiallyPaused,\n uint16 reserveFactor,\n uint16 p2pIndexCursor,\n uint256 collateralFactor\n );\n\n function getTotalMarketSupply(address _poolToken)\n external\n view\n returns (uint256 p2pSupplyAmount, uint256 poolSupplyAmount);\n\n function getTotalMarketBorrow(address _poolToken)\n external\n view\n returns (uint256 p2pBorrowAmount, uint256 poolBorrowAmount);\n\n /// INDEXES ///\n\n function getCurrentP2PSupplyIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentP2PBorrowIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentPoolIndexes(address _poolToken)\n external\n view\n returns (\n uint256 currentPoolSupplyIndex,\n uint256 currentPoolBorrowIndex\n );\n\n function getIndexes(address _poolToken, bool _computeUpdatedIndexes)\n external\n view\n returns (\n uint256 p2pSupplyIndex,\n uint256 p2pBorrowIndex,\n uint256 poolSupplyIndex,\n uint256 poolBorrowIndex\n );\n\n /// USERS ///\n\n function getEnteredMarkets(address _user)\n external\n view\n returns (address[] memory enteredMarkets);\n\n function getUserHealthFactor(\n address _user,\n address[] calldata _updatedMarkets\n ) external view returns (uint256);\n\n function getUserBalanceStates(\n address _user,\n address[] calldata _updatedMarkets\n )\n external\n view\n returns (\n uint256 collateralValue,\n uint256 debtValue,\n uint256 maxDebtValue\n );\n\n function getCurrentSupplyBalanceInOf(address _poolToken, address _user)\n external\n view\n returns (\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getCurrentBorrowBalanceInOf(address _poolToken, address _user)\n external\n view\n returns (\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getUserMaxCapacitiesForAsset(address _user, address _poolToken)\n external\n view\n returns (uint256 withdrawable, uint256 borrowable);\n\n function getUserHypotheticalBalanceStates(\n address _user,\n address _poolToken,\n uint256 _withdrawnAmount,\n uint256 _borrowedAmount\n ) external view returns (uint256 debtValue, uint256 maxDebtValue);\n\n function getUserLiquidityDataForAsset(\n address _user,\n address _poolToken,\n bool _computeUpdatedIndexes,\n ICompoundOracle _oracle\n ) external view returns (Types.AssetLiquidityData memory assetData);\n\n function isLiquidatable(address _user, address[] memory _updatedMarkets)\n external\n view\n returns (bool);\n\n function computeLiquidationRepayAmount(\n address _user,\n address _poolTokenBorrowed,\n address _poolTokenCollateral,\n address[] calldata _updatedMarkets\n ) external view returns (uint256 toRepay);\n\n /// RATES ///\n\n function getAverageSupplyRatePerBlock(address _poolToken)\n external\n view\n returns (\n uint256 avgSupplyRatePerBlock,\n uint256 p2pSupplyAmount,\n uint256 poolSupplyAmount\n );\n\n function getAverageBorrowRatePerBlock(address _poolToken)\n external\n view\n returns (\n uint256 avgBorrowRatePerBlock,\n uint256 p2pBorrowAmount,\n uint256 poolBorrowAmount\n );\n\n function getNextUserSupplyRatePerBlock(\n address _poolToken,\n address _user,\n uint256 _amount\n )\n external\n view\n returns (\n uint256 nextSupplyRatePerBlock,\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getNextUserBorrowRatePerBlock(\n address _poolToken,\n address _user,\n uint256 _amount\n )\n external\n view\n returns (\n uint256 nextBorrowRatePerBlock,\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getCurrentUserSupplyRatePerBlock(address _poolToken, address _user)\n external\n view\n returns (uint256);\n\n function getCurrentUserBorrowRatePerBlock(address _poolToken, address _user)\n external\n view\n returns (uint256);\n\n function getRatesPerBlock(address _poolToken)\n external\n view\n returns (\n uint256 p2pSupplyRate,\n uint256 p2pBorrowRate,\n uint256 poolSupplyRate,\n uint256 poolBorrowRate\n );\n\n /// REWARDS ///\n\n function getUserUnclaimedRewards(\n address[] calldata _poolTokens,\n address _user\n ) external view returns (uint256 unclaimedRewards);\n\n function getAccruedSupplierComp(\n address _supplier,\n address _poolToken,\n uint256 _balance\n ) external view returns (uint256);\n\n function getAccruedBorrowerComp(\n address _borrower,\n address _poolToken,\n uint256 _balance\n ) external view returns (uint256);\n\n function getCurrentCompSupplyIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentCompBorrowIndex(address _poolToken)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/IMorpho.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\nimport \"./Types.sol\";\nimport \"../IComptroller.sol\";\nimport \"./compound/ICompoundOracle.sol\";\n\n// prettier-ignore\ninterface IMorpho {\n function comptroller() external view returns (IComptroller);\n function supply(address _poolTokenAddress, address _onBehalf, uint256 _amount) external;\n function supply(address _poolTokenAddress, address _onBehalf, uint256 _amount, uint256 _maxGasForMatching) external;\n function withdraw(address _poolTokenAddress, uint256 _amount) external;\n function claimRewards(\n address[] calldata _cTokenAddresses,\n bool _tradeForMorphoToken\n ) external returns (uint256 claimedAmount);\n}\n" + }, + "contracts/interfaces/morpho/Types.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\n/// @title Types.\n/// @author Morpho Labs.\n/// @custom:contact security@morpho.xyz\n/// @dev Common types and structs used in Moprho contracts.\nlibrary Types {\n /// ENUMS ///\n\n enum PositionType {\n SUPPLIERS_IN_P2P,\n SUPPLIERS_ON_POOL,\n BORROWERS_IN_P2P,\n BORROWERS_ON_POOL\n }\n\n /// STRUCTS ///\n\n struct SupplyBalance {\n uint256 inP2P; // In supplier's peer-to-peer unit, a unit that grows in underlying value, to keep track of the interests earned by suppliers in peer-to-peer. Multiply by the peer-to-peer supply index to get the underlying amount.\n uint256 onPool; // In cToken. Multiply by the pool supply index to get the underlying amount.\n }\n\n struct BorrowBalance {\n uint256 inP2P; // In borrower's peer-to-peer unit, a unit that grows in underlying value, to keep track of the interests paid by borrowers in peer-to-peer. Multiply by the peer-to-peer borrow index to get the underlying amount.\n uint256 onPool; // In cdUnit, a unit that grows in value, to keep track of the debt increase when borrowers are on Compound. Multiply by the pool borrow index to get the underlying amount.\n }\n\n // Max gas to consume during the matching process for supply, borrow, withdraw and repay functions.\n struct MaxGasForMatching {\n uint64 supply;\n uint64 borrow;\n uint64 withdraw;\n uint64 repay;\n }\n\n struct Delta {\n uint256 p2pSupplyDelta; // Difference between the stored peer-to-peer supply amount and the real peer-to-peer supply amount (in pool supply unit).\n uint256 p2pBorrowDelta; // Difference between the stored peer-to-peer borrow amount and the real peer-to-peer borrow amount (in pool borrow unit).\n uint256 p2pSupplyAmount; // Sum of all stored peer-to-peer supply (in peer-to-peer supply unit).\n uint256 p2pBorrowAmount; // Sum of all stored peer-to-peer borrow (in peer-to-peer borrow unit).\n }\n\n struct AssetLiquidityData {\n uint256 collateralValue; // The collateral value of the asset.\n uint256 maxDebtValue; // The maximum possible debt value of the asset.\n uint256 debtValue; // The debt value of the asset.\n uint256 underlyingPrice; // The price of the token.\n uint256 collateralFactor; // The liquidation threshold applied on this token.\n }\n\n struct LiquidityData {\n uint256 collateralValue; // The collateral value.\n uint256 maxDebtValue; // The maximum debt value possible.\n uint256 debtValue; // The debt value.\n }\n\n // Variables are packed together to save gas (will not exceed their limit during Morpho's lifetime).\n struct LastPoolIndexes {\n uint32 lastUpdateBlockNumber; // The last time the peer-to-peer indexes were updated.\n uint112 lastSupplyPoolIndex; // Last pool supply index.\n uint112 lastBorrowPoolIndex; // Last pool borrow index.\n }\n\n struct MarketParameters {\n uint16 reserveFactor; // Proportion of the interest earned by users sent to the DAO for each market, in basis point (100% = 10 000). The value is set at market creation.\n uint16 p2pIndexCursor; // Position of the peer-to-peer rate in the pool's spread. Determine the weights of the weighted arithmetic average in the indexes computations ((1 - p2pIndexCursor) * r^S + p2pIndexCursor * r^B) (in basis point).\n }\n\n struct MarketStatus {\n bool isCreated; // Whether or not this market is created.\n bool isPaused; // Whether the market is paused or not (all entry points on Morpho are frozen; supply, borrow, withdraw, repay and liquidate).\n bool isPartiallyPaused; // Whether the market is partially paused or not (only supply and borrow are frozen).\n }\n}\n" + }, + "contracts/interfaces/plume/IFeeRegistry.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\ninterface IFeeRegistry {\n function registerFee(\n bool isTokenA,\n uint32 binId,\n uint256 binFeeInQuote\n ) external;\n}\n" + }, + "contracts/interfaces/plume/ILiquidityRegistry.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface ILiquidityRegistry {\n function notifyBinLiquidity(\n IMaverickV2Pool pool,\n uint256 tokenId,\n uint32 binId,\n uint256 currentBinLpBalance\n ) external;\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Factory.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IFeeRegistry } from \"./IFeeRegistry.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2Factory {\n error FactorAlreadyInitialized();\n error FactorNotInitialized();\n error FactoryInvalidTokenOrder(IERC20 _tokenA, IERC20 _tokenB);\n error FactoryInvalidFee();\n error FactoryInvalidKinds(uint8 kinds);\n error FactoryInvalidTickSpacing(uint256 tickSpacing);\n error FactoryInvalidLookback(uint256 lookback);\n error FactoryInvalidTokenDecimals(uint8 decimalsA, uint8 decimalsB);\n error FactoryPoolAlreadyExists(\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds,\n address accessor\n );\n error FactoryAccessorMustBeNonZero();\n error NotImplemented();\n\n event PoolCreated(\n IMaverickV2Pool poolAddress,\n uint8 protocolFeeRatio,\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n int32 activeTick,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds,\n address accessor\n );\n event SetFactoryProtocolFeeReceiver(address receiver);\n event SetFactoryProtocolFeeRegistry(IFeeRegistry registry);\n\n struct DeployParameters {\n uint64 feeAIn;\n uint64 feeBIn;\n uint32 lookback;\n int32 activeTick;\n uint64 tokenAScale;\n uint64 tokenBScale;\n // slot\n IERC20 tokenA;\n // slot\n IERC20 tokenB;\n // slot\n uint16 tickSpacing;\n uint8 options;\n address accessor;\n }\n\n /**\n * @notice Called by deployer library to initialize a pool.\n */\n function deployParameters()\n external\n view\n returns (\n uint64 feeAIn,\n uint64 feeBIn,\n uint32 lookback,\n int32 activeTick,\n uint64 tokenAScale,\n uint64 tokenBScale,\n // slot\n IERC20 tokenA,\n // slot\n IERC20 tokenB,\n // slot\n uint16 tickSpacing,\n uint8 options,\n address accessor\n );\n\n /**\n * @notice Create a new MaverickV2Pool with symmetric swap fees.\n * @param fee Fraction of the pool swap amount that is retained as an LP in\n * D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * E.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function create(\n uint64 fee,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external returns (IMaverickV2Pool);\n\n /**\n * @notice Create a new MaverickV2Pool.\n * @param feeAIn Fraction of the pool swap amount for tokenA-input swaps\n * that is retained as an LP in D18 scale.\n * @param feeBIn Fraction of the pool swap amount for tokenB-input swaps\n * that is retained as an LP in D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * e.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function create(\n uint64 feeAIn,\n uint64 feeBIn,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external returns (IMaverickV2Pool);\n\n /**\n * @notice Bool indicating whether the pool was deployed from this factory.\n */\n function isFactoryPool(IMaverickV2Pool pool) external view returns (bool);\n\n /**\n * @notice Address that receives the protocol fee\n */\n function protocolFeeReceiver() external view returns (address);\n\n /**\n * @notice Address notified on swaps of the protocol fee\n */\n function protocolFeeRegistry() external view returns (IFeeRegistry);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds\n ) external view returns (IMaverickV2Pool);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(\n IERC20 _tokenA,\n IERC20 _tokenB,\n uint256 startIndex,\n uint256 endIndex\n ) external view returns (IMaverickV2Pool[] memory pools);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(uint256 startIndex, uint256 endIndex)\n external\n view\n returns (IMaverickV2Pool[] memory pools);\n\n /**\n * @notice Count of permissionless pools.\n */\n function poolCount() external view returns (uint256 _poolCount);\n\n /**\n * @notice Count of pools for a given accessor and token pair. For\n * permissionless pools, pass `accessor = address(0)`.\n */\n function poolByTokenCount(\n IERC20 _tokenA,\n IERC20 _tokenB,\n address accessor\n ) external view returns (uint256 _poolCount);\n\n /**\n * @notice Get the current factory owner.\n */\n function owner() external view returns (address);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2LiquidityManager.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\nimport { IMaverickV2Position } from \"./IMaverickV2Position.sol\";\nimport { IMaverickV2PoolLens } from \"./IMaverickV2PoolLens.sol\";\n\ninterface IMaverickV2LiquidityManager {\n error LiquidityManagerNotFactoryPool();\n error LiquidityManagerNotTokenIdOwner();\n\n /**\n * @notice Maverick V2 NFT position contract that tracks NFT-based\n * liquditiy positions.\n */\n function position() external view returns (IMaverickV2Position);\n\n /**\n * @notice Create Maverick V2 pool. Function is a pass through to the pool\n * factory and is provided here so that is can be assembled as part of a\n * multicall transaction.\n */\n function createPool(\n uint64 fee,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external payable returns (IMaverickV2Pool pool);\n\n /**\n * @notice Add Liquidity position NFT for msg.sender by specifying\n * msg.sender's token index.\n * @dev Token index is different from tokenId.\n * On the Position NFT contract a user can own multiple NFT tokenIds and\n * these are indexes by an enumeration index which is the `index` input\n * here.\n *\n * See addLiquidity for a description of the add params.\n */\n function addPositionLiquidityToSenderByTokenIndex(\n IMaverickV2Pool pool,\n uint256 index,\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs\n )\n external\n payable\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds\n );\n\n /**\n * @notice Mint new tokenId in the Position NFt contract to msg.sender.\n * Both mints an NFT and adds liquidity to the pool that is held by the\n * NFT.\n */\n function mintPositionNftToSender(\n IMaverickV2Pool pool,\n bytes calldata packedSqrtPriceBreaks,\n bytes[] calldata packedArgs\n )\n external\n payable\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds,\n uint256 tokenId\n );\n\n /**\n * @notice Donates liqudity to a pool that is held by the position contract\n * and will never be retrievable. Can be used to start a pool and ensure\n * there will always be a base level of liquditiy in the pool.\n */\n function donateLiquidity(\n IMaverickV2Pool pool,\n IMaverickV2Pool.AddLiquidityParams memory args\n ) external payable;\n\n /**\n * @notice Packs sqrtPrice breaks array with this format: [length,\n * array[0], array[1],..., array[length-1]] where length is 1 byte.\n */\n function packUint88Array(uint88[] memory fullArray)\n external\n pure\n returns (bytes memory packedArray);\n\n /**\n * @notice Packs addLiquidity paramters array element-wise.\n */\n function packAddLiquidityArgsArray(\n IMaverickV2Pool.AddLiquidityParams[] memory args\n ) external pure returns (bytes[] memory argsPacked);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Pool.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\n\ninterface IMaverickV2Pool {\n error PoolZeroLiquidityAdded();\n error PoolMinimumLiquidityNotMet();\n error PoolLocked();\n error PoolInvalidFee();\n error PoolTicksNotSorted(uint256 index, int256 previousTick, int256 tick);\n error PoolTicksAmountsLengthMismatch(\n uint256 ticksLength,\n uint256 amountsLength\n );\n error PoolBinIdsAmountsLengthMismatch(\n uint256 binIdsLength,\n uint256 amountsLength\n );\n error PoolKindNotSupported(uint256 kinds, uint256 kind);\n error PoolInsufficientBalance(\n uint256 deltaLpAmount,\n uint256 accountBalance\n );\n error PoolReservesExceedMaximum(uint256 amount);\n error PoolValueExceedsBits(uint256 amount, uint256 bits);\n error PoolTickMaxExceeded(uint256 tick);\n error PoolMigrateBinFirst();\n error PoolCurrentTickBeyondSwapLimit(int32 startingTick);\n error PoolSenderNotAccessor(address sender_, address accessor);\n error PoolSenderNotFactory(address sender_, address accessor);\n error PoolFunctionNotImplemented();\n error PoolTokenNotSolvent(\n uint256 internalReserve,\n uint256 tokenBalance,\n IERC20 token\n );\n error PoolNoProtocolFeeReceiverSet();\n\n event PoolSwap(\n address sender,\n address recipient,\n SwapParams params,\n uint256 amountIn,\n uint256 amountOut\n );\n event PoolFlashLoan(\n address sender,\n address recipient,\n uint256 amountA,\n uint256 amountB\n );\n event PoolProtocolFeeCollected(IERC20 token, uint256 protocolFee);\n\n event PoolAddLiquidity(\n address sender,\n address recipient,\n uint256 subaccount,\n AddLiquidityParams params,\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] binIds\n );\n\n event PoolMigrateBinsUpStack(\n address sender,\n uint32 binId,\n uint32 maxRecursion\n );\n\n event PoolRemoveLiquidity(\n address sender,\n address recipient,\n uint256 subaccount,\n RemoveLiquidityParams params,\n uint256 tokenAOut,\n uint256 tokenBOut\n );\n\n event PoolTickState(int32 tick, uint256 reserveA, uint256 reserveB);\n event PoolTickBinUpdate(int32 tick, uint8 kind, uint32 binId);\n event PoolSqrtPrice(uint256 sqrtPrice);\n\n /**\n * @notice Tick state parameters.\n */\n struct TickState {\n uint128 reserveA;\n uint128 reserveB;\n uint128 totalSupply;\n uint32[4] binIdsByTick;\n }\n\n /**\n * @notice Tick data parameters.\n * @param currentReserveA Current reserve of token A.\n * @param currentReserveB Current reserve of token B.\n * @param currentLiquidity Current liquidity amount.\n */\n struct TickData {\n uint256 currentReserveA;\n uint256 currentReserveB;\n uint256 currentLiquidity;\n }\n\n /**\n * @notice Bin state parameters.\n * @param mergeBinBalance LP token balance that this bin possesses of the merge bin.\n * @param mergeId Bin ID of the bin that this bin has merged into.\n * @param totalSupply Total amount of LP tokens in this bin.\n * @param kind One of the 4 kinds (0=static, 1=right, 2=left, 3=both).\n * @param tick The lower price tick of the bin in its current state.\n * @param tickBalance Balance of the tick.\n */\n struct BinState {\n uint128 mergeBinBalance;\n uint128 tickBalance;\n uint128 totalSupply;\n uint8 kind;\n int32 tick;\n uint32 mergeId;\n }\n\n /**\n * @notice Parameters for swap.\n * @param amount Amount of the token that is either the input if exactOutput is false\n * or the output if exactOutput is true.\n * @param tokenAIn Boolean indicating whether tokenA is the input.\n * @param exactOutput Boolean indicating whether the amount specified is\n * the exact output amount (true).\n * @param tickLimit The furthest tick a swap will execute in. If no limit\n * is desired, value should be set to type(int32).max for a tokenAIn swap\n * and type(int32).min for a swap where tokenB is the input.\n */\n struct SwapParams {\n uint256 amount;\n bool tokenAIn;\n bool exactOutput;\n int32 tickLimit;\n }\n\n /**\n * @notice Parameters associated with adding liquidity.\n * @param kind One of the 4 kinds (0=static, 1=right, 2=left, 3=both).\n * @param ticks Array of ticks to add liquidity to.\n * @param amounts Array of bin LP amounts to add.\n */\n struct AddLiquidityParams {\n uint8 kind;\n int32[] ticks;\n uint128[] amounts;\n }\n\n /**\n * @notice Parameters for each bin that will have liquidity removed.\n * @param binIds Index array of the bins losing liquidity.\n * @param amounts Array of bin LP amounts to remove.\n */\n struct RemoveLiquidityParams {\n uint32[] binIds;\n uint128[] amounts;\n }\n\n /**\n * @notice State of the pool.\n * @param reserveA Pool tokenA balanceOf at end of last operation\n * @param reserveB Pool tokenB balanceOf at end of last operation\n * @param lastTwaD8 Value of log time weighted average price at last block.\n * Value is 8-decimal scale and is in the fractional tick domain. E.g. a\n * value of 12.3e8 indicates the TWAP was 3/10ths of the way into the 12th\n * tick.\n * @param lastLogPriceD8 Value of log price at last block. Value is\n * 8-decimal scale and is in the fractional tick domain. E.g. a value of\n * 12.3e8 indicates the price was 3/10ths of the way into the 12th tick.\n * @param lastTimestamp Last block.timestamp value in seconds for latest\n * swap transaction.\n * @param activeTick Current tick position that contains the active bins.\n * @param isLocked Pool isLocked, E.g., locked or unlocked; isLocked values\n * defined in Pool.sol.\n * @param binCounter Index of the last bin created.\n * @param protocolFeeRatioD3 Ratio of the swap fee that is kept for the\n * protocol.\n */\n struct State {\n uint128 reserveA;\n uint128 reserveB;\n int64 lastTwaD8;\n int64 lastLogPriceD8;\n uint40 lastTimestamp;\n int32 activeTick;\n bool isLocked;\n uint32 binCounter;\n uint8 protocolFeeRatioD3;\n }\n\n /**\n * @notice Internal data used for data passing between Pool and Bin code.\n */\n struct BinDelta {\n uint128 deltaA;\n uint128 deltaB;\n }\n\n /**\n * @notice 1-15 number to represent the active kinds.\n * @notice 0b0001 = static;\n * @notice 0b0010 = right;\n * @notice 0b0100 = left;\n * @notice 0b1000 = both;\n *\n * E.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function kinds() external view returns (uint8 _kinds);\n\n /**\n * @notice Pool swap fee for the given direction (A-in or B-in swap) in\n * 18-decimal format. E.g. 0.01e18 is a 1% swap fee.\n */\n function fee(bool tokenAIn) external view returns (uint256);\n\n /**\n * @notice TickSpacing of pool where 1.0001^tickSpacing is the bin width.\n */\n function tickSpacing() external view returns (uint256);\n\n /**\n * @notice Lookback period of pool in seconds.\n */\n function lookback() external view returns (uint256);\n\n /**\n * @notice Address of Pool accessor. This is Zero address for\n * permissionless pools.\n */\n function accessor() external view returns (address);\n\n /**\n * @notice Pool tokenA. Address of tokenA is such that tokenA < tokenB.\n */\n function tokenA() external view returns (IERC20);\n\n /**\n * @notice Pool tokenB.\n */\n function tokenB() external view returns (IERC20);\n\n /**\n * @notice Deploying factory of the pool and also contract that has ability\n * to set and collect protocol fees for the pool.\n */\n function factory() external view returns (IMaverickV2Factory);\n\n /**\n * @notice Most significant bit of scale value is a flag to indicate whether\n * tokenA has more or less than 18 decimals. Scale is used in conjuction\n * with Math.toScale/Math.fromScale functions to convert from token amounts\n * to D18 scale internal pool accounting.\n */\n function tokenAScale() external view returns (uint256);\n\n /**\n * @notice Most significant bit of scale value is a flag to indicate whether\n * tokenA has more or less than 18 decimals. Scale is used in conjuction\n * with Math.toScale/Math.fromScale functions to convert from token amounts\n * to D18 scale internal pool accounting.\n */\n function tokenBScale() external view returns (uint256);\n\n /**\n * @notice ID of bin at input tick position and kind.\n */\n function binIdByTickKind(int32 tick, uint256 kind)\n external\n view\n returns (uint32);\n\n /**\n * @notice Accumulated tokenA protocol fee.\n */\n function protocolFeeA() external view returns (uint128);\n\n /**\n * @notice Accumulated tokenB protocol fee.\n */\n function protocolFeeB() external view returns (uint128);\n\n /**\n * @notice Lending fee rate on flash loans.\n */\n function lendingFeeRateD18() external view returns (uint256);\n\n /**\n * @notice External function to get the current time-weighted average price.\n */\n function getCurrentTwa() external view returns (int256);\n\n /**\n * @notice External function to get the state of the pool.\n */\n function getState() external view returns (State memory);\n\n /**\n * @notice Return state of Bin at input binId.\n */\n function getBin(uint32 binId) external view returns (BinState memory bin);\n\n /**\n * @notice Return state of Tick at input tick position.\n */\n function getTick(int32 tick)\n external\n view\n returns (TickState memory tickState);\n\n /**\n * @notice Retrieves the balance of a user within a bin.\n * @param user The user's address.\n * @param subaccount The subaccount for the user.\n * @param binId The ID of the bin.\n */\n function balanceOf(\n address user,\n uint256 subaccount,\n uint32 binId\n ) external view returns (uint128 lpToken);\n\n /**\n * @notice Add liquidity to a pool. This function allows users to deposit\n * tokens into a liquidity pool.\n * @dev This function will call `maverickV2AddLiquidityCallback` on the\n * calling contract to collect the tokenA/tokenB payment.\n * @param recipient The account that will receive credit for the added liquidity.\n * @param subaccount The account that will receive credit for the added liquidity.\n * @param params Parameters containing the details for adding liquidity,\n * such as token types and amounts.\n * @param data Bytes information that gets passed to the callback.\n * @return tokenAAmount The amount of token A added to the pool.\n * @return tokenBAmount The amount of token B added to the pool.\n * @return binIds An array of bin IDs where the liquidity is stored.\n */\n function addLiquidity(\n address recipient,\n uint256 subaccount,\n AddLiquidityParams calldata params,\n bytes calldata data\n )\n external\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds\n );\n\n /**\n * @notice Removes liquidity from the pool.\n * @dev Liquidy can only be removed from a bin that is either unmerged or\n * has a mergeId of an unmerged bin. If a bin is merged more than one\n * level deep, it must be migrated up the merge stack to the root bin\n * before liquidity removal.\n * @param recipient The address to receive the tokens.\n * @param subaccount The subaccount for the recipient.\n * @param params The parameters for removing liquidity.\n * @return tokenAOut The amount of token A received.\n * @return tokenBOut The amount of token B received.\n */\n function removeLiquidity(\n address recipient,\n uint256 subaccount,\n RemoveLiquidityParams calldata params\n ) external returns (uint256 tokenAOut, uint256 tokenBOut);\n\n /**\n * @notice Migrate bins up the linked list of merged bins so that its\n * mergeId is the currrent active bin.\n * @dev Liquidy can only be removed from a bin that is either unmerged or\n * has a mergeId of an unmerged bin. If a bin is merged more than one\n * level deep, it must be migrated up the merge stack to the root bin\n * before liquidity removal.\n * @param binId The ID of the bin to migrate.\n * @param maxRecursion The maximum recursion depth for the migration.\n */\n function migrateBinUpStack(uint32 binId, uint32 maxRecursion) external;\n\n /**\n * @notice Swap tokenA/tokenB assets in the pool. The swap user has two\n * options for funding their swap.\n * - The user can push the input token amount to the pool before calling\n * the swap function. In order to avoid having the pool call the callback,\n * the user should pass a zero-length `data` bytes object with the swap\n * call.\n * - The user can send the input token amount to the pool when the pool\n * calls the `maverickV2SwapCallback` function on the calling contract.\n * That callback has input parameters that specify the token address of the\n * input token, the input and output amounts, and the bytes data sent to\n * the swap function.\n * @dev If the users elects to do a callback-based swap, the output\n * assets will be sent before the callback is called, allowing the user to\n * execute flash swaps. However, the pool does have reentrancy protection,\n * so a swapper will not be able to interact with the same pool again\n * while they are in the callback function.\n * @param recipient The address to receive the output tokens.\n * @param params Parameters containing the details of the swap\n * @param data Bytes information that gets passed to the callback.\n */\n function swap(\n address recipient,\n SwapParams memory params,\n bytes calldata data\n ) external returns (uint256 amountIn, uint256 amountOut);\n\n /**\n * @notice Loan tokenA/tokenB assets from the pool to recipient. The fee\n * rate of a loan is determined by `lendingFeeRateD18`, which is set at the\n * protocol level by the factory. This function calls\n * `maverickV2FlashLoanCallback` on the calling contract. At the end of\n * the callback, the caller must pay back the loan with fee (if there is a\n * fee).\n * @param recipient The address to receive the loaned tokens.\n * @param amountB Loan amount of tokenA sent to recipient.\n * @param amountB Loan amount of tokenB sent to recipient.\n * @param data Bytes information that gets passed to the callback.\n */\n function flashLoan(\n address recipient,\n uint256 amountA,\n uint256 amountB,\n bytes calldata data\n ) external;\n\n /**\n * @notice Distributes accumulated protocol fee to factory protocolFeeReceiver\n */\n function distributeFees(bool isTokenA)\n external\n returns (uint256 protocolFee, IERC20 token);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2PoolLens.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2PoolLens {\n error LensTargetPriceOutOfBounds(\n uint256 targetSqrtPrice,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n );\n error LensTooLittleLiquidity(\n uint256 relativeLiquidityAmount,\n uint256 deltaA,\n uint256 deltaB\n );\n error LensTargetingTokenWithNoDelta(\n bool targetIsA,\n uint256 deltaA,\n uint256 deltaB\n );\n\n /**\n * @notice Add liquidity slippage parameters for a distribution of liquidity.\n * @param pool Pool where liquidity is being added.\n * @param kind Bin kind; all bins must have the same kind in a given call\n * to addLiquidity.\n * @param ticks Array of tick values to add liquidity to.\n * @param relativeLiquidityAmounts Relative liquidity amounts for the\n * specified ticks. Liquidity in this case is not bin LP balance, it is\n * the bin liquidity as defined by liquidity = deltaA / (sqrt(upper) -\n * sqrt(lower)) or deltaB = liquidity / sqrt(lower) - liquidity /\n * sqrt(upper).\n * @param addSpec Slippage specification.\n */\n struct AddParamsViewInputs {\n IMaverickV2Pool pool;\n uint8 kind;\n int32[] ticks;\n uint128[] relativeLiquidityAmounts;\n AddParamsSpecification addSpec;\n }\n\n /**\n * @notice Multi-price add param specification.\n * @param slippageFactorD18 Max slippage allowed as a percent in D18 scale. e.g. 1% slippage is 0.01e18\n * @param numberOfPriceBreaksPerSide Number of price break values on either\n * side of current price.\n * @param targetAmount Target token contribution amount in tokenA if\n * targetIsA is true, otherwise this is the target amount for tokenB.\n * @param targetIsA Indicates if the target amount is for tokenA or tokenB\n */\n struct AddParamsSpecification {\n uint256 slippageFactorD18;\n uint256 numberOfPriceBreaksPerSide;\n uint256 targetAmount;\n bool targetIsA;\n }\n\n /**\n * @notice Specification for deriving create pool parameters. Creating a\n * pool in the liquidity manager has several steps:\n *\n * - Deploy pool\n * - Donate a small amount of initial liquidity in the activeTick\n * - Execute a small swap to set the pool price to the desired value\n * - Add liquidity\n *\n * In order to execute these steps, the caller must specify the parameters\n * of each step. The PoolLens has helper function to derive the values\n * used by the LiquidityManager, but this struct is the input to that\n * helper function and represents the core intent of the pool creator.\n *\n * @param fee Fraction of the pool swap amount that is retained as an LP in\n * D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * e.g. a pool with all 4 modes will have kinds = b1111 = 15\n * @param initialTargetB Amount of B to be donated to the pool after pool\n * create. This amount needs to be big enough to meet the minimum bin\n * liquidity.\n * @param sqrtPrice Target sqrt price of the pool.\n * @param kind Bin kind; all bins must have the same kind in a given call\n * to addLiquidity.\n * @param ticks Array of tick values to add liquidity to.\n * @param relativeLiquidityAmounts Relative liquidity amounts for the\n * specified ticks. Liquidity in this case is not bin LP balance, it is\n * the bin liquidity as defined by liquidity = deltaA / (sqrt(upper) -\n * sqrt(lower)) or deltaB = liquidity / sqrt(lower) - liquidity /\n * sqrt(upper).\n * @param targetAmount Target token contribution amount in tokenA if\n * targetIsA is true, otherwise this is the target amount for tokenB.\n * @param targetIsA Indicates if the target amount is for tokenA or tokenB\n */\n struct CreateAndAddParamsViewInputs {\n uint64 feeAIn;\n uint64 feeBIn;\n uint16 tickSpacing;\n uint32 lookback;\n IERC20 tokenA;\n IERC20 tokenB;\n int32 activeTick;\n uint8 kinds;\n // donate params\n uint256 initialTargetB;\n uint256 sqrtPrice;\n // add target\n uint8 kind;\n int32[] ticks;\n uint128[] relativeLiquidityAmounts;\n uint256 targetAmount;\n bool targetIsA;\n }\n\n struct Output {\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256[] deltaAs;\n uint256[] deltaBs;\n uint128[] deltaLpBalances;\n }\n\n struct Reserves {\n uint256 amountA;\n uint256 amountB;\n }\n\n struct BinPositionKinds {\n uint128[4] values;\n }\n\n struct PoolState {\n IMaverickV2Pool.TickState[] tickStateMapping;\n IMaverickV2Pool.BinState[] binStateMapping;\n BinPositionKinds[] binIdByTickKindMapping;\n IMaverickV2Pool.State state;\n Reserves protocolFees;\n }\n\n struct BoostedPositionSpecification {\n IMaverickV2Pool pool;\n uint32[] binIds;\n uint128[] ratios;\n uint8 kind;\n }\n\n struct CreateAndAddParamsInputs {\n uint64 feeAIn;\n uint64 feeBIn;\n uint16 tickSpacing;\n uint32 lookback;\n IERC20 tokenA;\n IERC20 tokenB;\n int32 activeTick;\n uint8 kinds;\n // donate params\n IMaverickV2Pool.AddLiquidityParams donateParams;\n // swap params\n uint256 swapAmount;\n // add params\n IMaverickV2Pool.AddLiquidityParams addParams;\n bytes[] packedAddParams;\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256 preAddReserveA;\n uint256 preAddReserveB;\n }\n\n struct TickDeltas {\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256[] deltaAs;\n uint256[] deltaBs;\n }\n\n /**\n * @notice Converts add parameter slippage specification into add\n * parameters. The return values are given in both raw format and as packed\n * values that can be used in the LiquidityManager contract.\n */\n function getAddLiquidityParams(AddParamsViewInputs memory params)\n external\n view\n returns (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint88[] memory sqrtPriceBreaks,\n IMaverickV2Pool.AddLiquidityParams[] memory addParams,\n IMaverickV2PoolLens.TickDeltas[] memory tickDeltas\n );\n\n /**\n * @notice Converts add parameter slippage specification and new pool\n * specification into CreateAndAddParamsInputs parameters that can be used in the\n * LiquidityManager contract.\n */\n function getCreatePoolAtPriceAndAddLiquidityParams(\n CreateAndAddParamsViewInputs memory params\n ) external view returns (CreateAndAddParamsInputs memory output);\n\n /**\n * @notice View function that provides information about pool ticks within\n * a tick radius from the activeTick. Ticks with no reserves are not\n * included in part o f the return array.\n */\n function getTicksAroundActive(IMaverickV2Pool pool, int32 tickRadius)\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates\n );\n\n /**\n * @notice View function that provides information about pool ticks within\n * a range. Ticks with no reserves are not included in part o f the return\n * array.\n */\n function getTicks(\n IMaverickV2Pool pool,\n int32 tickStart,\n int32 tickEnd\n )\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates\n );\n\n /**\n * @notice View function that provides information about pool ticks within\n * a range. Information returned includes all pool state needed to emulate\n * a swap off chain. Ticks with no reserves are not included in part o f\n * the return array.\n */\n function getTicksAroundActiveWLiquidity(\n IMaverickV2Pool pool,\n int32 tickRadius\n )\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates,\n uint256[] memory liquidities,\n uint256[] memory sqrtLowerTickPrices,\n uint256[] memory sqrtUpperTickPrices,\n IMaverickV2Pool.State memory poolState,\n uint256 sqrtPrice,\n uint256 feeAIn,\n uint256 feeBIn\n );\n\n /**\n * @notice View function that provides pool state information.\n */\n function getFullPoolState(\n IMaverickV2Pool pool,\n uint32 binStart,\n uint32 binEnd\n ) external view returns (PoolState memory poolState);\n\n /**\n * @notice View function that provides price and liquidity of a given tick.\n */\n function getTickSqrtPriceAndL(IMaverickV2Pool pool, int32 tick)\n external\n view\n returns (uint256 sqrtPrice, uint256 liquidity);\n\n /**\n * @notice Pool sqrt price.\n */\n function getPoolSqrtPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 sqrtPrice);\n\n /**\n * @notice Pool price.\n */\n function getPoolPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 price);\n\n /**\n * @notice Token scale of two tokens in a pool.\n */\n function tokenScales(IMaverickV2Pool pool)\n external\n view\n returns (uint256 tokenAScale, uint256 tokenBScale);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Position.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\nimport { ILiquidityRegistry } from \"./ILiquidityRegistry.sol\";\n\ninterface IMaverickV2Position {\n event PositionClearData(uint256 indexed tokenId);\n event PositionSetData(\n uint256 indexed tokenId,\n uint256 index,\n PositionPoolBinIds newData\n );\n event SetLpReward(ILiquidityRegistry lpReward);\n\n error PositionDuplicatePool(uint256 index, IMaverickV2Pool pool);\n error PositionNotFactoryPool();\n error PositionPermissionedLiquidityPool();\n\n struct PositionPoolBinIds {\n IMaverickV2Pool pool;\n uint32[] binIds;\n }\n\n struct PositionFullInformation {\n PositionPoolBinIds poolBinIds;\n uint256 amountA;\n uint256 amountB;\n uint256[] binAAmounts;\n uint256[] binBAmounts;\n int32[] ticks;\n uint256[] liquidities;\n }\n\n /**\n * @notice Factory that tracks lp rewards.\n */\n function lpReward() external view returns (ILiquidityRegistry);\n\n /**\n * @notice Pool factory.\n */\n function factory() external view returns (IMaverickV2Factory);\n\n /**\n * @notice Mint NFT that holds liquidity in a Maverick V2 Pool. To mint\n * liquidity to an NFT, add liquidity to bins in a pool where the\n * add liquidity recipient is this contract and the subaccount is the\n * tokenId. LiquidityManager can be used to simplify minting Position NFTs.\n */\n function mint(\n address recipient,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external returns (uint256 tokenId);\n\n /**\n * @notice Overwrites tokenId pool/binId information for a given data index.\n */\n function setTokenIdData(\n uint256 tokenId,\n uint256 index,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n\n /**\n * @notice Overwrites entire pool/binId data set for a given tokenId.\n */\n function setTokenIdData(uint256 tokenId, PositionPoolBinIds[] memory data)\n external;\n\n /**\n * @notice Append new pool/binIds data array to tokenId.\n */\n function appendTokenIdData(\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n\n /**\n * @notice Get array pool/binIds data for a given tokenId.\n */\n function getTokenIdData(uint256 tokenId)\n external\n view\n returns (PositionPoolBinIds[] memory);\n\n /**\n * @notice Get value from array of pool/binIds data for a given tokenId.\n */\n function getTokenIdData(uint256 tokenId, uint256 index)\n external\n view\n returns (PositionPoolBinIds memory);\n\n /**\n * @notice Length of array of pool/binIds data for a given tokenId.\n */\n function tokenIdDataLength(uint256 tokenId)\n external\n view\n returns (uint256 length);\n\n /**\n * @notice Remove liquidity from tokenId for a given pool. User can\n * specify arbitrary bins to remove from for their subaccount in the pool\n * even if those bins are not in the tokenIdData set.\n */\n function removeLiquidity(\n uint256 tokenId,\n address recipient,\n IMaverickV2Pool pool,\n IMaverickV2Pool.RemoveLiquidityParams memory params\n ) external returns (uint256 tokenAAmount, uint256 tokenBAmount);\n\n /**\n * @notice Remove liquidity from tokenId for a given pool to sender. User\n * can specify arbitrary bins to remove from for their subaccount in the\n * pool even if those bins are not in the tokenIdData set.\n */\n function removeLiquidityToSender(\n uint256 tokenId,\n IMaverickV2Pool pool,\n IMaverickV2Pool.RemoveLiquidityParams memory params\n ) external returns (uint256 tokenAAmount, uint256 tokenBAmount);\n\n /**\n * @notice NFT asset information for a given range of pool/binIds indexes.\n * This function only returns the liquidity in the pools/binIds stored as\n * part of the tokenIdData, but it is possible that the NFT has additional\n * liquidity in pools/binIds that have not been recorded.\n */\n function tokenIdPositionInformation(\n uint256 tokenId,\n uint256 startIndex,\n uint256 stopIndex\n ) external view returns (PositionFullInformation[] memory output);\n\n /**\n * @notice NFT asset information for a given pool/binIds index. This\n * function only returns the liquidity in the pools/binIds stored as part\n * of the tokenIdData, but it is possible that the NFT has additional\n * liquidity in pools/binIds that have not been recorded.\n */\n function tokenIdPositionInformation(uint256 tokenId, uint256 index)\n external\n view\n returns (PositionFullInformation memory output);\n\n /**\n * @notice Get remove parameters for removing a fractional part of the\n * liquidity owned by a given tokenId. The fractional factor to remove is\n * given by proporationD18 in 18-decimal scale.\n */\n function getRemoveParams(\n uint256 tokenId,\n uint256 index,\n uint256 proportionD18\n )\n external\n view\n returns (IMaverickV2Pool.RemoveLiquidityParams memory params);\n\n /**\n * @notice Register the bin balances in the nft with the LpReward contract.\n */\n function checkpointBinLpBalance(\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Quoter.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2Quoter {\n error QuoterInvalidSwap();\n error QuoterInvalidAddLiquidity();\n\n /**\n * @notice Calculates a swap on a MaverickV2Pool and returns the resulting\n * amount and estimated gas. The gas estimate is only a rough estimate and\n * may not match a swap's gas.\n * @param pool The MaverickV2Pool to swap on.\n * @param amount The input amount.\n * @param tokenAIn Indicates if token A is the input token.\n * @param exactOutput Indicates if the amount is the output amount (true)\n * or input amount (false). If the tickLimit is reached, the full value of\n * the exactOutput may not be returned because the pool will stop swapping\n * before the whole order is filled.\n * @param tickLimit The tick limit for the swap. Once the swap lands in\n * this tick, it will stop and return the output amount swapped up to that\n * tick.\n */\n function calculateSwap(\n IMaverickV2Pool pool,\n uint128 amount,\n bool tokenAIn,\n bool exactOutput,\n int32 tickLimit\n )\n external\n returns (\n uint256 amountIn,\n uint256 amountOut,\n uint256 gasEstimate\n );\n\n /**\n * @notice Calculates a multihop swap and returns the resulting amount and\n * estimated gas. The gas estimate is only a rough estimate and\n * may not match a swap's gas.\n * @param path The path of pools to swap through. Path is given by an\n * packed array of (pool, tokenAIn) tuples. So each step in the path is 160\n * + 8 = 168 bits of data. e.g. path = abi.encodePacked(pool1, true, pool2, false);\n * @param amount The input amount.\n * @param exactOutput A boolean indicating if exact output is required.\n */\n function calculateMultiHopSwap(\n bytes memory path,\n uint256 amount,\n bool exactOutput\n ) external returns (uint256 returnAmount, uint256 gasEstimate);\n\n /**\n * @notice Computes the token amounts required for a given set of\n * addLiquidity parameters. The gas estimate is only a rough estimate and\n * may not match a add's gas.\n */\n function calculateAddLiquidity(\n IMaverickV2Pool pool,\n IMaverickV2Pool.AddLiquidityParams calldata params\n )\n external\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 gasEstimate\n );\n\n /**\n * @notice Pool's sqrt price.\n */\n function poolSqrtPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 sqrtPrice);\n}\n" + }, + "contracts/interfaces/plume/IPoolDistributor.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.0;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IPoolDistributor {\n function rewardToken() external view returns (address);\n\n function claimLp(\n address recipient,\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds,\n uint256 epoch\n ) external returns (uint256 amount);\n}\n" + }, + "contracts/interfaces/plume/IVotingDistributor.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.0;\n\ninterface IVotingDistributor {\n function lastEpoch() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/poolBooster/IMerklDistributor.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IMerklDistributor {\n struct CampaignParameters {\n // POPULATED ONCE CREATED\n\n // ID of the campaign. This can be left as a null bytes32 when creating campaigns\n // on Merkl.\n bytes32 campaignId;\n // CHOSEN BY CAMPAIGN CREATOR\n\n // Address of the campaign creator, if marked as address(0), it will be overriden with the\n // address of the `msg.sender` creating the campaign\n address creator;\n // Address of the token used as a reward\n address rewardToken;\n // Amount of `rewardToken` to distribute across all the epochs\n // Amount distributed per epoch is `amount/numEpoch`\n uint256 amount;\n // Type of campaign\n uint32 campaignType;\n // Timestamp at which the campaign should start\n uint32 startTimestamp;\n // Duration of the campaign in seconds. Has to be a multiple of EPOCH = 3600\n uint32 duration;\n // Extra data to pass to specify the campaign\n bytes campaignData;\n }\n\n function createCampaign(CampaignParameters memory newCampaign)\n external\n returns (bytes32);\n\n function signAndCreateCampaign(\n CampaignParameters memory newCampaign,\n bytes memory _signature\n ) external returns (bytes32);\n\n function sign(bytes memory _signature) external;\n\n function rewardTokenMinAmounts(address _rewardToken)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/poolBooster/IPoolBoostCentralRegistry.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPoolBoostCentralRegistry {\n /**\n * @dev all the supported pool booster types are listed here. It is possible\n * to have multiple versions of the factory that supports the same type of\n * pool booster. Factories are immutable and this can happen when a factory\n * or related pool booster required code update.\n * e.g. \"PoolBoosterSwapxDouble\" & \"PoolBoosterSwapxDouble_v2\"\n */\n enum PoolBoosterType {\n // Supports bribing 2 contracts per pool. Appropriate for Ichi vault concentrated\n // liquidity pools where (which is expected in most/all cases) both pool gauges\n // require bribing.\n SwapXDoubleBooster,\n // Supports bribing a single contract per pool. Appropriate for Classic Stable &\n // Classic Volatile pools and Ichi vaults where only 1 side (1 of the 2 gauges)\n // needs bribing\n SwapXSingleBooster,\n // Supports bribing a single contract per pool. Appropriate for Metropolis pools\n MetropolisBooster,\n // Supports creating a Merkl campaign.\n MerklBooster\n }\n\n struct PoolBoosterEntry {\n address boosterAddress;\n address ammPoolAddress;\n PoolBoosterType boosterType;\n }\n\n event PoolBoosterCreated(\n address poolBoosterAddress,\n address ammPoolAddress,\n PoolBoosterType poolBoosterType,\n address factoryAddress\n );\n event PoolBoosterRemoved(address poolBoosterAddress);\n\n function emitPoolBoosterCreated(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n PoolBoosterType _boosterType\n ) external;\n\n function emitPoolBoosterRemoved(address _poolBoosterAddress) external;\n}\n" + }, + "contracts/interfaces/poolBooster/IPoolBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPoolBooster {\n event BribeExecuted(uint256 amount);\n\n /// @notice Execute the bribe action\n function bribe() external;\n}\n" + }, + "contracts/interfaces/poolBooster/ISwapXAlgebraBribe.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBribe {\n /// @notice Notify a bribe amount\n /// @dev Rewards are saved into NEXT EPOCH mapping.\n function notifyRewardAmount(address _rewardsToken, uint256 reward) external;\n}\n" + }, + "contracts/interfaces/sonic/INodeDriver.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\n\ninterface INodeDriver {\n /// Seal epoch. Called BEFORE epoch sealing made by the client itself.\n function sealEpoch(\n uint256[] calldata offlineTimes,\n uint256[] calldata offlineBlocks,\n uint256[] calldata uptimes,\n uint256[] calldata originatedTxsFee\n ) external;\n}\n" + }, + "contracts/interfaces/sonic/ISFC.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\n\n/**\n * @title Special Fee Contract for Sonic network\n * @notice The SFC maintains a list of validators and delegators and distributes rewards to them.\n * @custom:security-contact security@fantom.foundation\n */\ninterface ISFC {\n error StakeIsFullySlashed();\n\n event CreatedValidator(\n uint256 indexed validatorID,\n address indexed auth,\n uint256 createdEpoch,\n uint256 createdTime\n );\n event Delegated(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 amount\n );\n event Undelegated(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 indexed wrID,\n uint256 amount\n );\n event Withdrawn(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 indexed wrID,\n uint256 amount,\n uint256 penalty\n );\n event ClaimedRewards(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 rewards\n );\n event RestakedRewards(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 rewards\n );\n event BurntFTM(uint256 amount);\n event UpdatedSlashingRefundRatio(\n uint256 indexed validatorID,\n uint256 refundRatio\n );\n event RefundedSlashedLegacyDelegation(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 amount\n );\n\n event DeactivatedValidator(\n uint256 indexed validatorID,\n uint256 deactivatedEpoch,\n uint256 deactivatedTime\n );\n event ChangedValidatorStatus(uint256 indexed validatorID, uint256 status);\n event AnnouncedRedirection(address indexed from, address indexed to);\n\n function currentSealedEpoch() external view returns (uint256);\n\n function getEpochSnapshot(uint256 epoch)\n external\n view\n returns (\n uint256 endTime,\n uint256 endBlock,\n uint256 epochFee,\n uint256 baseRewardPerSecond,\n uint256 totalStake,\n uint256 totalSupply\n );\n\n function getStake(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getValidator(uint256 validatorID)\n external\n view\n returns (\n uint256 status,\n uint256 receivedStake,\n address auth,\n uint256 createdEpoch,\n uint256 createdTime,\n uint256 deactivatedTime,\n uint256 deactivatedEpoch\n );\n\n function getValidatorID(address auth) external view returns (uint256);\n\n function getValidatorPubkey(uint256 validatorID)\n external\n view\n returns (bytes memory);\n\n function pubkeyAddressvalidatorID(address pubkeyAddress)\n external\n view\n returns (uint256);\n\n function getWithdrawalRequest(\n address delegator,\n uint256 validatorID,\n uint256 wrID\n )\n external\n view\n returns (\n uint256 epoch,\n uint256 time,\n uint256 amount\n );\n\n function isOwner() external view returns (bool);\n\n function lastValidatorID() external view returns (uint256);\n\n function minGasPrice() external view returns (uint256);\n\n function owner() external view returns (address);\n\n function renounceOwnership() external;\n\n function slashingRefundRatio(uint256 validatorID)\n external\n view\n returns (uint256);\n\n function stashedRewardsUntilEpoch(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function totalActiveStake() external view returns (uint256);\n\n function totalStake() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function transferOwnership(address newOwner) external;\n\n function treasuryAddress() external view returns (address);\n\n function version() external pure returns (bytes3);\n\n function currentEpoch() external view returns (uint256);\n\n function updateConstsAddress(address v) external;\n\n function constsAddress() external view returns (address);\n\n function getEpochValidatorIDs(uint256 epoch)\n external\n view\n returns (uint256[] memory);\n\n function getEpochReceivedStake(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochAccumulatedRewardPerToken(\n uint256 epoch,\n uint256 validatorID\n ) external view returns (uint256);\n\n function getEpochAccumulatedUptime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochAverageUptime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint32);\n\n function getEpochAccumulatedOriginatedTxsFee(\n uint256 epoch,\n uint256 validatorID\n ) external view returns (uint256);\n\n function getEpochOfflineTime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochOfflineBlocks(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochEndBlock(uint256 epoch) external view returns (uint256);\n\n function rewardsStash(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function createValidator(bytes calldata pubkey) external payable;\n\n function getSelfStake(uint256 validatorID) external view returns (uint256);\n\n function delegate(uint256 validatorID) external payable;\n\n function undelegate(\n uint256 validatorID,\n uint256 wrID,\n uint256 amount\n ) external;\n\n function isSlashed(uint256 validatorID) external view returns (bool);\n\n function withdraw(uint256 validatorID, uint256 wrID) external;\n\n function deactivateValidator(uint256 validatorID, uint256 status) external;\n\n function pendingRewards(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function stashRewards(address delegator, uint256 validatorID) external;\n\n function claimRewards(uint256 validatorID) external;\n\n function restakeRewards(uint256 validatorID) external;\n\n function updateSlashingRefundRatio(uint256 validatorID, uint256 refundRatio)\n external;\n\n function updateTreasuryAddress(address v) external;\n\n function burnFTM(uint256 amount) external;\n\n function sealEpoch(\n uint256[] calldata offlineTime,\n uint256[] calldata offlineBlocks,\n uint256[] calldata uptimes,\n uint256[] calldata originatedTxsFee\n ) external;\n\n function sealEpochValidators(uint256[] calldata nextValidatorIDs) external;\n\n function initialize(\n uint256 sealedEpoch,\n uint256 _totalSupply,\n address nodeDriver,\n address consts,\n address _owner\n ) external;\n\n function setGenesisValidator(\n address auth,\n uint256 validatorID,\n bytes calldata pubkey,\n uint256 createdTime\n ) external;\n\n function setGenesisDelegation(\n address delegator,\n uint256 validatorID,\n uint256 stake\n ) external;\n\n function updateStakeSubscriberAddress(address v) external;\n\n function stakeSubscriberAddress() external view returns (address);\n\n function setRedirectionAuthorizer(address v) external;\n\n function announceRedirection(address to) external;\n\n function initiateRedirection(address from, address to) external;\n\n function redirect(address to) external;\n}\n" + }, + "contracts/interfaces/sonic/ISwapXGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IGauge {\n function owner() external view returns (address);\n\n function TOKEN() external view returns (address);\n\n function DISTRIBUTION() external view returns (address);\n\n function balanceOf(address account) external view returns (uint256);\n\n function claimFees() external returns (uint256 claimed0, uint256 claimed1);\n\n function deposit(uint256 amount) external;\n\n function depositAll() external;\n\n function earned(address account) external view returns (uint256);\n\n function getReward() external;\n\n function getReward(address _user) external;\n\n function isForPair() external view returns (bool);\n\n function lastTimeRewardApplicable() external view returns (uint256);\n\n function lastUpdateTime() external view returns (uint256);\n\n function notifyRewardAmount(address token, uint256 reward) external;\n\n function periodFinish() external view returns (uint256);\n\n function rewardForDuration() external view returns (uint256);\n\n function rewardPerToken() external view returns (uint256);\n\n function rewardPerTokenStored() external view returns (uint256);\n\n function rewardRate() external view returns (uint256);\n\n function rewardToken() external view returns (address);\n\n function rewards(address) external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function userRewardPerTokenPaid(address) external view returns (uint256);\n\n function withdraw(uint256 amount) external;\n\n function withdrawAll() external;\n\n function withdrawAllAndHarvest() external;\n\n function withdrawExcess(address token, uint256 amount) external;\n\n function emergency() external returns (bool);\n\n function emergencyWithdraw() external;\n\n function activateEmergencyMode() external;\n\n function stopEmergencyMode() external;\n}\n" + }, + "contracts/interfaces/sonic/ISwapXPair.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IPair {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n\n event Mint(address indexed sender, uint256 amount0, uint256 amount1);\n event Burn(\n address indexed sender,\n uint256 amount0,\n uint256 amount1,\n address indexed to\n );\n event Swap(\n address indexed sender,\n uint256 amount0In,\n uint256 amount1In,\n uint256 amount0Out,\n uint256 amount1Out,\n address indexed to\n );\n event Claim(\n address indexed sender,\n address indexed recipient,\n uint256 amount0,\n uint256 amount1\n );\n\n function metadata()\n external\n view\n returns (\n uint256 dec0,\n uint256 dec1,\n uint256 r0,\n uint256 r1,\n bool st,\n address t0,\n address t1\n );\n\n function claimFees() external returns (uint256, uint256);\n\n function tokens() external view returns (address, address);\n\n function token0() external view returns (address);\n\n function token1() external view returns (address);\n\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function swap(\n uint256 amount0Out,\n uint256 amount1Out,\n address to,\n bytes calldata data\n ) external;\n\n function burn(address to)\n external\n returns (uint256 amount0, uint256 amount1);\n\n function mint(address to) external returns (uint256 liquidity);\n\n function getReserves()\n external\n view\n returns (\n uint256 _reserve0,\n uint256 _reserve1,\n uint256 _blockTimestampLast\n );\n\n function getAmountOut(uint256, address) external view returns (uint256);\n\n // ERC20 methods\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n\n function totalSupply() external view returns (uint256);\n\n function balanceOf(address) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 value) external returns (bool);\n\n function claimable0(address _user) external view returns (uint256);\n\n function claimable1(address _user) external view returns (uint256);\n\n function isStable() external view returns (bool);\n\n function skim(address to) external;\n\n function sync() external;\n}\n" + }, + "contracts/interfaces/sonic/IVoterV3.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IVoterV3 {\n /// @notice create a gauge\n function createGauge(address _pool, uint256 _gaugeType)\n external\n returns (\n address _gauge,\n address _internal_bribe,\n address _external_bribe\n );\n\n function gauges(address _pool) external view returns (address _gauge);\n}\n" + }, + "contracts/interfaces/sonic/IWrappedSonic.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWrappedSonic {\n event Deposit(address indexed account, uint256 value);\n event Withdrawal(address indexed account, uint256 value);\n event Transfer(address indexed from, address indexed to, uint256 value);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 value) external returns (bool);\n\n function balanceOf(address account) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function depositFor(address account) external payable returns (bool);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address to, uint256 value) external returns (bool);\n\n function transferFrom(\n address from,\n address to,\n uint256 value\n ) external returns (bool);\n\n function withdraw(uint256 value) external;\n\n function withdrawTo(address account, uint256 value) external returns (bool);\n}\n" + }, + "contracts/interfaces/Tether.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// slither-disable-start erc20-interface\ninterface Tether {\n function transfer(address to, uint256 value) external;\n\n function transferFrom(\n address from,\n address to,\n uint256 value\n ) external;\n\n function balanceOf(address) external view returns (uint256);\n\n function approve(address _spender, uint256 _value) external;\n}\n// slither-disable-end erc20-interface\n" + }, + "contracts/interfaces/uniswap/IUniswapUniversalRouter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapUniversalRouter {\n /// @notice Executes encoded commands along with provided inputs. Reverts if deadline has expired.\n /// @param commands A set of concatenated commands, each 1 byte in length\n /// @param inputs An array of byte strings containing abi encoded inputs for each command\n /// @param deadline The deadline by which the transaction must be executed\n function execute(\n bytes calldata commands,\n bytes[] calldata inputs,\n uint256 deadline\n ) external payable;\n\n function execute(bytes calldata commands, bytes[] calldata inputs)\n external\n payable;\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV2Pair.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapV2Pair {\n function token0() external view returns (address);\n\n function token1() external view returns (address);\n\n function getReserves()\n external\n view\n returns (\n uint112 reserve0,\n uint112 reserve1,\n uint32 blockTimestampLast\n );\n\n function price0CumulativeLast() external view returns (uint256);\n\n function price1CumulativeLast() external view returns (uint256);\n\n function sync() external;\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV2Router02.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapV2Router {\n function WETH() external pure returns (address);\n\n function swapExactTokensForTokens(\n uint256 amountIn,\n uint256 amountOutMin,\n address[] calldata path,\n address to,\n uint256 deadline\n ) external returns (uint256[] memory amounts);\n\n function addLiquidity(\n address tokenA,\n address tokenB,\n uint256 amountADesired,\n uint256 amountBDesired,\n uint256 amountAMin,\n uint256 amountBMin,\n address to,\n uint256 deadline\n )\n external\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 liquidity\n );\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV3Router.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// -- Solididy v0.5.x compatible interface\ninterface IUniswapV3Router {\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n}\n" + }, + "contracts/mocks/beacon/ExecutionLayerConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { GeneralPurposeToConsensusLayerRequest } from \"./GeneralPurposeToConsensusLayerRequest.sol\";\n\ncontract ExecutionLayerConsolidation is GeneralPurposeToConsensusLayerRequest {\n event ConsolidationRequestIssued(bytes sourceKey, bytes targetKey);\n bytes public lastSource;\n bytes public lastTarget;\n\n function handleRequest(bytes calldata data) internal override {\n // parameters should consist of twice the 48 bytes for 2 public keys\n require(data.length == 96, \"Invalid Consolidation data\");\n lastSource = data[:48];\n lastTarget = data[48:];\n\n emit ConsolidationRequestIssued(lastSource, lastTarget);\n }\n}\n" + }, + "contracts/mocks/beacon/ExecutionLayerWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { GeneralPurposeToConsensusLayerRequest } from \"./GeneralPurposeToConsensusLayerRequest.sol\";\n\ncontract ExecutionLayerWithdrawal is GeneralPurposeToConsensusLayerRequest {\n event WithdrawalRequestIssued(bytes publicKey, uint64 amount);\n\n bytes public lastPublicKey;\n uint64 public lastAmount;\n\n function handleRequest(bytes calldata data) internal override {\n // parameters should consist of 48 bytes for public key and 8 bytes for uint64 amount\n require(data.length == 56, \"Invalid Withdrawal data\");\n lastPublicKey = data[:48];\n lastAmount = uint64(bytes8(data[48:]));\n emit WithdrawalRequestIssued(lastPublicKey, lastAmount);\n }\n}\n" + }, + "contracts/mocks/beacon/GeneralPurposeToConsensusLayerRequest.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nabstract contract GeneralPurposeToConsensusLayerRequest {\n // solhint-disable no-complex-fallback\n fallback() external payable {\n // fee requested\n if (msg.data.length == 0) {\n uint256 fee = _fee();\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Return a uint256 value\n mstore(0, fee)\n return(0, 32) // Return 32 bytes from memory\n }\n }\n\n // else handle request\n handleRequest(msg.data);\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _fee() internal virtual returns (uint256) {\n return 1;\n }\n\n function handleRequest(bytes calldata data) internal virtual;\n}\n" + }, + "contracts/mocks/BurnableERC20.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ninterface IBurnableERC20 {\n function burn(uint256 value) external returns (bool);\n\n function burnFrom(address account, uint256 value) external returns (bool);\n}\n\n/**\n * @title BurnableERC20\n * @dev Exposes the burn function of ERC20 for tests\n */\nabstract contract BurnableERC20 is IBurnableERC20, ERC20 {\n /**\n * @dev Function to burn tokens\n * @param value The amount of tokens to burn.\n * @return A boolean that indicates if the operation was successful.\n */\n function burn(uint256 value) public virtual override returns (bool) {\n _burn(msg.sender, value);\n return true;\n }\n\n /**\n * @dev Function to burn tokens from a specific account\n * @param account The address with the tokens to burn.\n * @param value The amount of tokens to burn.\n * @return A boolean that indicates if the operation was successful.\n */\n function burnFrom(address account, uint256 value)\n public\n override\n returns (bool)\n {\n _burn(account, value);\n return true;\n }\n}\n" + }, + "contracts/mocks/curve/Mock3CRV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20, MintableERC20 } from \"../MintableERC20.sol\";\nimport { BurnableERC20 } from \"../BurnableERC20.sol\";\n\ncontract Mock3CRV is MintableERC20, BurnableERC20 {\n constructor() ERC20(\"Curve.fi DAI/USDC/USDT\", \"3Crv\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/curve/MockBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { MockRewardPool } from \"./MockRewardPool.sol\";\n\nimport { IRewardStaking } from \"../../strategies/IRewardStaking.sol\";\nimport { IMintableERC20, MintableERC20, ERC20 } from \"../MintableERC20.sol\";\nimport { IBurnableERC20, BurnableERC20 } from \"../BurnableERC20.sol\";\n\ncontract MockDepositToken is MintableERC20, BurnableERC20 {\n constructor() ERC20(\"DCVX\", \"CVX Deposit Token\") {}\n}\n\ncontract MockBooster {\n using SafeERC20 for IERC20;\n\n struct PoolInfo {\n address lptoken;\n address token;\n address crvRewards;\n }\n\n address public minter; // this is CVx for the booster on live\n address public crv; // Curve rewards token\n address public cvx; // Convex rewards token\n mapping(uint256 => PoolInfo) public poolInfo;\n\n constructor(\n address _rewardsMinter,\n address _crv,\n address _cvx\n ) public {\n minter = _rewardsMinter;\n crv = _crv;\n cvx = _cvx;\n }\n\n function setPool(uint256 pid, address _lpToken)\n external\n returns (address rewards)\n {\n address token = address(new MockDepositToken());\n // Deploy a new Convex Rewards Pool\n rewards = address(\n new MockRewardPool(pid, token, crv, cvx, address(this))\n );\n\n poolInfo[pid] = PoolInfo({\n lptoken: _lpToken,\n token: token,\n crvRewards: rewards\n });\n }\n\n function deposit(\n uint256 _pid,\n uint256 _amount,\n bool _stake\n ) public returns (bool) {\n PoolInfo storage pool = poolInfo[_pid];\n\n address lptoken = pool.lptoken;\n\n // hold on to the Curve LP tokens\n IERC20(lptoken).safeTransferFrom(msg.sender, address(this), _amount);\n\n address token = pool.token;\n if (_stake) {\n // mint Convex pool LP tokens and stake in rewards contract on user behalf\n IMintableERC20(token).mint(_amount);\n address rewardContract = pool.crvRewards;\n IERC20(token).safeApprove(rewardContract, 0);\n IERC20(token).safeApprove(rewardContract, _amount);\n IRewardStaking(rewardContract).stakeFor(msg.sender, _amount);\n } else {\n // mint Convex pool LP tokens and send to user\n IMintableERC20(token).mint(_amount);\n IERC20(token).transfer(msg.sender, _amount);\n }\n return true;\n }\n\n // Deposit all Curve LP tokens and stake\n function depositAll(uint256 _pid, bool _stake) external returns (bool) {\n address lptoken = poolInfo[_pid].lptoken;\n uint256 balance = IERC20(lptoken).balanceOf(msg.sender);\n deposit(_pid, balance, _stake);\n return true;\n }\n\n // withdraw Curve LP tokens\n function _withdraw(\n uint256 _pid,\n uint256 _amount,\n address _from,\n address _to\n ) internal {\n PoolInfo storage pool = poolInfo[_pid];\n\n // burn the Convex pool LP tokens\n IBurnableERC20(pool.token).burnFrom(_from, _amount);\n\n // return the Curve LP tokens\n IERC20(pool.lptoken).safeTransfer(_to, _amount);\n }\n\n // withdraw Curve LP tokens\n function withdraw(uint256 _pid, uint256 _amount) public returns (bool) {\n _withdraw(_pid, _amount, msg.sender, msg.sender);\n return true;\n }\n\n // withdraw all Curve LP tokens\n function withdrawAll(uint256 _pid) public returns (bool) {\n address token = poolInfo[_pid].token;\n uint256 userBal = IERC20(token).balanceOf(msg.sender);\n withdraw(_pid, userBal);\n return true;\n }\n\n // allow reward contracts to send here and withdraw to user\n function withdrawTo(\n uint256 _pid,\n uint256 _amount,\n address _to\n ) external returns (bool) {\n address rewardContract = poolInfo[_pid].crvRewards;\n require(msg.sender == rewardContract, \"!auth\");\n\n _withdraw(_pid, _amount, msg.sender, _to);\n return true;\n }\n\n // callback from reward contract when crv is received.\n function rewardClaimed(\n uint256 _pid,\n // solhint-disable-next-line no-unused-vars\n address _address,\n uint256 _amount\n ) external returns (bool) {\n address rewardContract = poolInfo[_pid].crvRewards;\n require(msg.sender == rewardContract, \"!auth\");\n\n //mint reward tokens\n // and transfer it\n IMintableERC20(minter).mint(_amount);\n IERC20(minter).transfer(msg.sender, _amount);\n return true;\n }\n}\n" + }, + "contracts/mocks/curve/MockCRV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../MintableERC20.sol\";\n\ncontract MockCRV is MintableERC20 {\n constructor() ERC20(\"Curve DAO Token\", \"CRV\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/curve/MockCRVMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\n\ncontract MockCRVMinter {\n address crv;\n\n constructor(address _crv) {\n crv = _crv;\n }\n\n function mint(address _address) external {\n uint256 amount = 2e18;\n IMintableERC20(crv).mint(amount);\n IERC20(crv).transfer(_address, amount);\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveAbstractMetapool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { MintableERC20, IMintableERC20 } from \"../MintableERC20.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport \"../../utils/Helpers.sol\";\n\nabstract contract MockCurveAbstractMetapool is MintableERC20 {\n using StableMath for uint256;\n\n address[] public coins;\n uint256[2] public balances;\n\n // Returns the same amount of LP tokens in 1e18 decimals\n function add_liquidity(uint256[2] calldata _amounts, uint256 _minAmount)\n external\n returns (uint256 lpAmount)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transferFrom(\n msg.sender,\n address(this),\n _amounts[i]\n );\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to sum\n lpAmount += _amounts[i].scaleBy(18, assetDecimals);\n balances[i] = balances[i] + _amounts[i];\n }\n }\n // Hacky way of simulating slippage to check _minAmount\n if (lpAmount == 29000e18) lpAmount = 14500e18;\n require(lpAmount >= _minAmount, \"Slippage ruined your day\");\n // Send LP token to sender, e.g. 3CRV\n _mint(msg.sender, lpAmount);\n }\n\n // Dumb implementation that returns the same amount\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n public\n view\n returns (uint256 lpAmount)\n {\n uint256 assetDecimals = Helpers.getDecimals(coins[uint128(_index)]);\n lpAmount = _amount.scaleBy(assetDecimals, 18);\n }\n\n function remove_liquidity_one_coin(\n uint256 _lpAmount,\n int128 _index,\n // solhint-disable-next-line no-unused-vars\n uint256 _minAmount\n ) external returns (uint256 amount) {\n _burn(msg.sender, _lpAmount);\n uint256[] memory amounts = new uint256[](coins.length);\n amounts[uint128(_index)] = _lpAmount;\n amount = calc_withdraw_one_coin(_lpAmount, _index);\n balances[uint128(_index)] -= amount;\n IERC20(coins[uint128(_index)]).transfer(msg.sender, amount);\n }\n\n function get_virtual_price() external pure returns (uint256) {\n return 1e18;\n }\n\n // solhint-disable-next-line no-unused-vars\n function remove_liquidity(uint256 _amount, uint256[2] memory _min_amounts)\n public\n returns (uint256[2] memory amounts)\n {\n _burn(msg.sender, _amount);\n uint256 totalSupply = totalSupply();\n for (uint256 i = 0; i < 2; i++) {\n amounts[i] = totalSupply > 0\n ? (_amount * IERC20(coins[i]).balanceOf(address(this))) /\n totalSupply\n : IERC20(coins[i]).balanceOf(address(this));\n balances[i] -= amounts[i];\n IERC20(coins[i]).transfer(msg.sender, amounts[i]);\n }\n }\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens\n ) public returns (uint256) {\n return\n _remove_liquidity_imbalance(\n _amounts,\n _max_burned_tokens,\n msg.sender\n );\n }\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens,\n address _reveiver\n ) public returns (uint256) {\n return\n _remove_liquidity_imbalance(\n _amounts,\n _max_burned_tokens,\n _reveiver\n );\n }\n\n function _remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens,\n address _reveiver\n ) internal returns (uint256 lpTokens) {\n lpTokens = _max_burned_tokens;\n _burn(msg.sender, lpTokens);\n for (uint256 i = 0; i < _amounts.length; i++) {\n balances[i] -= _amounts[i];\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transfer(_reveiver, _amounts[i]);\n }\n }\n }\n\n // Dumb implementation that sums the scaled amounts\n function calc_token_amount(uint256[2] memory _amounts, bool)\n public\n view\n returns (uint256 lpTokens)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to lpTokens\n lpTokens += _amounts[i].scaleBy(18, assetDecimals);\n }\n }\n\n /// @notice 0.02% fee\n function fee() external pure returns (uint256) {\n return 2000000;\n }\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n\n function burnFrom(address from, uint256 value) public {\n _burn(from, value);\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveGauge.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { ICurveGauge } from \"../../strategies/ICurveGauge.sol\";\n\ncontract MockCurveGauge is ICurveGauge {\n mapping(address => uint256) private _balances;\n address lpToken;\n\n constructor(address _lpToken) {\n lpToken = _lpToken;\n }\n\n function balanceOf(address account) public view override returns (uint256) {\n return _balances[account];\n }\n\n function deposit(uint256 _value, address _account) external override {\n IERC20(lpToken).transferFrom(msg.sender, address(this), _value);\n _balances[_account] += _value;\n }\n\n function withdraw(uint256 _value) external override {\n IERC20(lpToken).transfer(msg.sender, _value);\n // solhint-disable-next-line reentrancy\n _balances[msg.sender] -= _value;\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveMetapool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { MockCurveAbstractMetapool } from \"./MockCurveAbstractMetapool.sol\";\nimport \"../MintableERC20.sol\";\n\ncontract MockCurveMetapool is MockCurveAbstractMetapool {\n constructor(address[2] memory _coins)\n ERC20(\"Curve.fi 3pool/OUSD metapool\", \"3crv_OUSD\")\n {\n coins = _coins;\n }\n}\n" + }, + "contracts/mocks/curve/MockCurvePool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IBurnableERC20 } from \"../BurnableERC20.sol\";\n\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\nimport { ICurvePool } from \"../../strategies/ICurvePool.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport \"../../utils/Helpers.sol\";\n\ncontract MockCurvePool {\n using StableMath for uint256;\n\n address[] public coins;\n uint256[3] public balances;\n address lpToken;\n uint256 public slippage = 1 ether;\n\n constructor(address[3] memory _coins, address _lpToken) {\n coins = _coins;\n lpToken = _lpToken;\n }\n\n function setCoins(address[] memory _coins) external {\n coins = _coins;\n }\n\n // Returns the same amount of LP tokens in 1e18 decimals\n function add_liquidity(uint256[3] calldata _amounts, uint256 _minAmount)\n external\n {\n uint256 sum = 0;\n for (uint256 i = 0; i < _amounts.length; i++) {\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transferFrom(\n msg.sender,\n address(this),\n _amounts[i]\n );\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to sum\n sum += _amounts[i].scaleBy(18, assetDecimals);\n balances[i] = balances[i] + _amounts[i];\n }\n }\n // Hacky way of simulating slippage to check _minAmount\n if (sum == 29000e18) sum = 14500e18;\n require(sum >= _minAmount, \"Slippage ruined your day\");\n // Send LP token to sender, e.g. 3CRV\n IMintableERC20(lpToken).mint(sum);\n IERC20(lpToken).transfer(msg.sender, sum);\n }\n\n // Dumb implementation that returns the same amount\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n public\n view\n returns (uint256)\n {\n uint256 assetDecimals = Helpers.getDecimals(coins[uint128(_index)]);\n return _amount.scaleBy(assetDecimals, 18);\n }\n\n function remove_liquidity_one_coin(\n uint256 _amount,\n int128 _index,\n // solhint-disable-next-line no-unused-vars\n uint256 _minAmount\n ) external {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _amount);\n uint256[] memory amounts = new uint256[](coins.length);\n amounts[uint128(_index)] = _amount;\n uint256 coinAmount = calc_withdraw_one_coin(_amount, _index);\n balances[uint128(_index)] -= coinAmount;\n IERC20(coins[uint128(_index)]).transfer(msg.sender, coinAmount);\n }\n\n function get_virtual_price() external pure returns (uint256) {\n return 1e18;\n }\n\n // solhint-disable-next-line no-unused-vars\n function remove_liquidity(uint256 _lpAmount, uint256[3] memory _min_amounts)\n public\n {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _lpAmount);\n uint256 totalSupply = IERC20(lpToken).totalSupply();\n for (uint256 i = 0; i < 3; i++) {\n uint256 coinAmount = totalSupply > 0\n ? (_lpAmount * IERC20(coins[i]).balanceOf(address(this))) /\n totalSupply\n : IERC20(coins[i]).balanceOf(address(this));\n balances[i] -= coinAmount;\n IERC20(coins[i]).transfer(msg.sender, coinAmount);\n }\n }\n\n function remove_liquidity_imbalance(\n uint256[3] memory _amounts,\n uint256 _max_burned_tokens\n ) public {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _max_burned_tokens);\n // For each coin, transfer to the caller\n for (uint256 i = 0; i < _amounts.length; i++) {\n balances[i] -= _amounts[i];\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transfer(msg.sender, _amounts[i]);\n }\n }\n }\n\n // Dumb implementation that sums the scaled amounts\n function calc_token_amount(uint256[3] memory _amounts, bool)\n public\n view\n returns (uint256 lpTokens)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to lpTokens\n lpTokens += _amounts[i].scaleBy(18, assetDecimals);\n }\n }\n\n function fee() external pure returns (uint256) {\n return 1000000;\n }\n\n function exchange(\n uint256 coin0,\n uint256 coin1,\n uint256 amountIn,\n uint256 minAmountOut\n ) external returns (uint256 amountOut) {\n IERC20(coins[coin0]).transferFrom(msg.sender, address(this), amountIn);\n amountOut = (minAmountOut * slippage) / 1 ether;\n require(amountOut >= minAmountOut, \"Slippage error\");\n IMintableERC20(coins[coin1]).mintTo(msg.sender, amountOut);\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n}\n" + }, + "contracts/mocks/curve/MockCVX.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../MintableERC20.sol\";\n\ncontract MockCVX is MintableERC20 {\n constructor() ERC20(\"CVX\", \"CVX DAO Token\") {}\n}\n" + }, + "contracts/mocks/curve/MockRewardPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\ninterface IDeposit {\n function poolInfo(uint256)\n external\n view\n returns (\n address,\n address,\n address,\n address,\n address,\n bool\n );\n\n function rewardClaimed(\n uint256,\n address,\n uint256\n ) external;\n\n function withdrawTo(\n uint256,\n uint256,\n address\n ) external;\n}\n\ncontract MockRewardPool {\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 public pid;\n address public stakingToken;\n address public rewardTokenA;\n address public rewardTokenB;\n address public operator;\n\n uint256 private _totalSupply;\n\n mapping(address => uint256) private _balances;\n mapping(address => uint256) public rewards;\n\n constructor(\n uint256 _pid,\n address _stakingToken,\n address _rewardTokenA,\n address _rewardTokenB,\n address _operator\n ) public {\n pid = _pid;\n stakingToken = _stakingToken;\n rewardTokenA = _rewardTokenA;\n rewardTokenB = _rewardTokenB;\n operator = _operator;\n }\n\n function totalSupply() public view returns (uint256) {\n return _totalSupply;\n }\n\n function balanceOf(address account) public view returns (uint256) {\n return _balances[account];\n }\n\n function stakeFor(address _for, uint256 _amount) public returns (bool) {\n require(_amount > 0, \"RewardPool : Cannot stake 0\");\n\n //give to _for\n _totalSupply = _totalSupply.add(_amount);\n _balances[_for] = _balances[_for].add(_amount);\n\n //take away from sender\n IERC20(stakingToken).safeTransferFrom(\n msg.sender,\n address(this),\n _amount\n );\n\n return true;\n }\n\n function withdrawAndUnwrap(uint256 amount, bool claim)\n public\n returns (bool)\n {\n _totalSupply = _totalSupply.sub(amount);\n _balances[msg.sender] = _balances[msg.sender].sub(amount);\n\n //tell operator to withdraw from here directly to user\n IDeposit(operator).withdrawTo(pid, amount, msg.sender);\n\n //get rewards too\n if (claim) {\n getReward(msg.sender, true);\n }\n return true;\n }\n\n function withdrawAllAndUnwrap(bool claim) external {\n withdrawAndUnwrap(_balances[msg.sender], claim);\n }\n\n // solhint-disable-next-line no-unused-vars\n function getReward(address _account, bool _claimExtras)\n public\n returns (bool)\n {\n IMintableERC20(rewardTokenA).mint(2 * 1e18);\n IERC20(rewardTokenA).transfer(_account, 2 * 1e18);\n\n IMintableERC20(rewardTokenB).mint(3 * 1e18);\n IERC20(rewardTokenB).transfer(_account, 3 * 1e18);\n\n return true;\n }\n\n function getReward() public returns (bool) {\n getReward(msg.sender, true);\n }\n}\n" + }, + "contracts/mocks/ForceEtherSender.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract ForceEtherSender {\n // Constructor to optionally receive Ether upon deployment\n constructor() payable {}\n\n // Function to allow the contract to receive Ether\n receive() external payable {}\n\n // Function to self-destruct and force-send Ether to an address\n function forceSend(address payable recipient) external {\n // Requires that the contract has a balance greater than 0\n require(address(this).balance > 0, \"No Ether to send\");\n\n // selfdestruct sends all Ether held by the contract to the recipient\n selfdestruct(recipient);\n }\n}\n" + }, + "contracts/mocks/MintableERC20.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ninterface IMintableERC20 {\n function mint(uint256 value) external;\n\n function mintTo(address to, uint256 value) external;\n}\n\n/**\n * @title MintableERC20\n * @dev Exposes the mint function of ERC20 for tests\n */\nabstract contract MintableERC20 is IMintableERC20, ERC20 {\n /**\n * @dev Function to mint tokens\n * @param _value The amount of tokens to mint.\n */\n function mint(uint256 _value) public virtual override {\n _mint(msg.sender, _value);\n }\n\n /**\n * @dev Function to mint tokens\n * @param _to Address to mint to.\n * @param _value The amount of tokens to mint.\n */\n function mintTo(address _to, uint256 _value) public virtual override {\n _mint(_to, _value);\n }\n}\n" + }, + "contracts/mocks/Mock1InchSwapRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { SwapDescription } from \"../interfaces/IOneInch.sol\";\n\ncontract Mock1InchSwapRouter {\n using SafeERC20 for IERC20;\n\n event MockSwap(address executor, bytes permitData, bytes executorData);\n\n event MockSwapDesc(\n address srcToken,\n address dstToken,\n address srcReceiver,\n address dstReceiver,\n uint256 amount,\n uint256 minReturnAmount,\n uint256 flags\n );\n\n event MockUnoswapTo(\n address recipient,\n address srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] pools\n );\n\n event MockUniswapV3SwapTo(\n address recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] pools\n );\n\n /**\n * @dev transfers the shource asset and returns the minReturnAmount of the destination asset.\n */\n function swap(\n address executor,\n SwapDescription calldata desc,\n bytes calldata permitData,\n bytes calldata executorData\n ) public returns (uint256 returnAmount, uint256 spentAmount) {\n // Transfer the source tokens to the receiver contract\n IERC20(desc.srcToken).safeTransferFrom(\n msg.sender,\n desc.srcReceiver,\n desc.amount\n );\n\n // Transfer the destination tokens to the recipient\n IERC20(desc.dstToken).safeTransfer(\n desc.dstReceiver,\n desc.minReturnAmount\n );\n\n emit MockSwap(executor, permitData, executorData);\n _swapDesc(desc);\n returnAmount = 0;\n spentAmount = 0;\n }\n\n function _swapDesc(SwapDescription calldata desc) public {\n emit MockSwapDesc(\n address(desc.srcToken),\n address(desc.dstToken),\n desc.srcReceiver,\n desc.dstReceiver,\n desc.amount,\n desc.minReturnAmount,\n desc.flags\n );\n }\n\n /**\n * @dev only transfers the source asset to this contract.\n * Ideally it would return the destination asset but that's encoded in the pools array.\n */\n function unoswapTo(\n address payable recipient,\n address srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) public returns (uint256 returnAmount) {\n // transfer the from asset from the caller\n IERC20(srcToken).safeTransferFrom(msg.sender, address(this), amount);\n\n emit MockUnoswapTo(recipient, srcToken, amount, minReturn, pools);\n returnAmount = 0;\n }\n\n /**\n * @dev does not do any transfers. Just emits MockUniswapV3SwapTo.\n */\n function uniswapV3SwapTo(\n address payable recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) public returns (uint256 returnAmount) {\n emit MockUniswapV3SwapTo(recipient, amount, minReturn, pools);\n returnAmount = 0;\n }\n}\n" + }, + "contracts/mocks/MockAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { IAaveLendingPool, ILendingPoolAddressesProvider } from \"../strategies/IAave.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// 1. User calls 'getLendingPool'\n// 2. User calls 'deposit' (Aave)\n// - Deposit their underlying\n// - Mint aToken to them\n// 3. User calls redeem (aToken)\n// - Retrieve their aToken\n// - Return equal amount of underlying\n\ncontract MockAToken is MintableERC20 {\n address public lendingPool;\n IERC20 public underlyingToken;\n using SafeERC20 for IERC20;\n\n constructor(\n address _lendingPool,\n string memory _name,\n string memory _symbol,\n IERC20 _underlyingToken\n ) ERC20(_name, _symbol) {\n lendingPool = _lendingPool;\n underlyingToken = _underlyingToken;\n // addMinter(_lendingPool);\n }\n\n function decimals() public view override returns (uint8) {\n return ERC20(address(underlyingToken)).decimals();\n }\n\n function poolRedeem(uint256 _amount, address _to) external {\n require(msg.sender == lendingPool, \"pool only\");\n // Redeem these a Tokens\n _burn(_to, _amount);\n // For the underlying\n underlyingToken.safeTransferFrom(lendingPool, _to, _amount);\n }\n}\n\ncontract MockAave is IAaveLendingPool, ILendingPoolAddressesProvider {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n mapping(address => address) reserveToAToken;\n address pool = address(this);\n address payable core = payable(address(this));\n uint256 factor;\n\n function addAToken(address _aToken, address _underlying) public {\n IERC20(_underlying).safeApprove(_aToken, 0);\n IERC20(_underlying).safeApprove(_aToken, type(uint256).max);\n reserveToAToken[_underlying] = _aToken;\n }\n\n // set the reserve factor / basically the interest on deposit\n // in 18 precision\n // so 0.5% would be 5 * 10 ^ 15\n function setFactor(uint256 factor_) public {\n factor = factor_;\n }\n\n function deposit(\n address _reserve,\n uint256 _amount,\n address _to,\n uint16 /*_referralCode*/\n ) external override {\n uint256 previousBal = IERC20(reserveToAToken[_reserve]).balanceOf(\n msg.sender\n );\n uint256 interest = previousBal.mulTruncate(factor);\n MintableERC20(reserveToAToken[_reserve]).mintTo(msg.sender, interest);\n // Take their reserve\n IERC20(_reserve).safeTransferFrom(msg.sender, address(this), _amount);\n // Credit them with aToken\n MintableERC20(reserveToAToken[_reserve]).mintTo(_to, _amount);\n }\n\n function withdraw(\n address asset,\n uint256 amount,\n address to\n ) external override returns (uint256) {\n MockAToken atoken = MockAToken(reserveToAToken[asset]);\n atoken.poolRedeem(amount, to);\n return amount;\n }\n\n function getLendingPool() external view override returns (address) {\n return pool;\n }\n}\n" + }, + "contracts/mocks/MockAaveIncentivesController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { MockStkAave } from \"./MockStkAave.sol\";\n\ncontract MockAaveIncentivesController {\n mapping(address => uint256) private rewards;\n MockStkAave public REWARD_TOKEN;\n\n constructor(address _reward_token) {\n REWARD_TOKEN = MockStkAave(_reward_token);\n }\n\n function setRewardsBalance(address user, uint256 amount) external {\n rewards[user] = amount;\n }\n\n /**\n * @dev Returns the total of rewards of an user, already accrued + not yet accrued\n * @param user The address of the user\n * @return The rewards\n **/\n // solhint-disable-next-line no-unused-vars\n function getRewardsBalance(address[] calldata assets, address user)\n external\n view\n returns (uint256)\n {\n return rewards[user];\n }\n\n /**\n * @dev Claims reward for an user, on all the assets of the lending pool, accumulating the pending rewards\n * @param amount Amount of rewards to claim\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewards(\n // solhint-disable-next-line no-unused-vars\n address[] calldata assets,\n uint256 amount,\n address to\n ) external returns (uint256) {\n require(amount > 0);\n require(rewards[to] == amount);\n REWARD_TOKEN.mint(amount);\n require(REWARD_TOKEN.transfer(to, amount));\n // solhint-disable-next-line reentrancy\n rewards[to] = 0;\n return amount;\n }\n}\n" + }, + "contracts/mocks/MockAAVEToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAAVEToken is MintableERC20 {\n constructor() ERC20(\"AAVE\", \"AAVE\") {}\n}\n" + }, + "contracts/mocks/MockAero.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAero is MintableERC20 {\n constructor() ERC20(\"Aerodrome\", \"AERO\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/MockAura.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAura is MintableERC20 {\n constructor() ERC20(\"Aura\", \"AURA\") {}\n}\n" + }, + "contracts/mocks/MockBAL.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockBAL is MintableERC20 {\n constructor() ERC20(\"Balancer\", \"BAL\") {}\n}\n" + }, + "contracts/mocks/MockBalancerVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// import \"hardhat/console.sol\";\n\ncontract MockBalancerVault {\n using StableMath for uint256;\n uint256 public slippage = 1 ether;\n bool public transferDisabled = false;\n bool public slippageErrorDisabled = false;\n\n function swap(\n IBalancerVault.SingleSwap calldata singleSwap,\n IBalancerVault.FundManagement calldata funds,\n uint256 minAmountOut,\n uint256\n ) external returns (uint256 amountCalculated) {\n amountCalculated = (minAmountOut * slippage) / 1 ether;\n if (!slippageErrorDisabled) {\n require(amountCalculated >= minAmountOut, \"Slippage error\");\n }\n IERC20(singleSwap.assetIn).transferFrom(\n funds.sender,\n address(this),\n singleSwap.amount\n );\n if (!transferDisabled) {\n MintableERC20(singleSwap.assetOut).mintTo(\n funds.recipient,\n amountCalculated\n );\n }\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n\n function getPoolTokenInfo(bytes32 poolId, address token)\n external\n view\n returns (\n uint256,\n uint256,\n uint256,\n address\n )\n {}\n\n function disableTransfer() external {\n transferDisabled = true;\n }\n\n function disableSlippageError() external {\n slippageErrorDisabled = true;\n }\n}\n" + }, + "contracts/mocks/MockBeaconConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconConsolidation } from \"../beacon/BeaconConsolidation.sol\";\n\ncontract MockBeaconConsolidation {\n function fee() external view returns (uint256) {\n return BeaconConsolidation.fee();\n }\n\n function request(bytes calldata source, bytes calldata target)\n external\n returns (uint256 fee_)\n {\n return BeaconConsolidation.request(source, target);\n }\n}\n" + }, + "contracts/mocks/MockBeaconOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconOracle } from \"../beacon/BeaconOracle.sol\";\n\ncontract MockBeaconOracle is BeaconOracle {\n function mapSlot(\n uint64 blockNumber,\n uint64 slot,\n bytes32 _blockRoot\n ) external {\n _blockToSlot[blockNumber] = slot;\n _slotToBlock[slot] = blockNumber;\n _slotToRoot[slot] = _blockRoot;\n }\n}\n" + }, + "contracts/mocks/MockBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"../beacon/BeaconProofsLib.sol\";\nimport { BeaconProofs } from \"../beacon/BeaconProofs.sol\";\n\ncontract MockBeaconProofs is BeaconProofs {\n function concatGenIndices(\n uint256 index1,\n uint256 height2,\n uint256 index2\n ) external pure returns (uint256 genIndex) {\n return BeaconProofsLib.concatGenIndices(index1, height2, index2);\n }\n}\n" + }, + "contracts/mocks/MockBeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconRoots } from \"../beacon/BeaconRoots.sol\";\n\ncontract MockBeaconRoots {\n // Mapping to simulate the ring buffer: timestamp => beacon block root\n mapping(uint256 => bytes32) internal _beaconRoots;\n\n // Event to log when a new root is set (for testing)\n event RootSet(uint256 indexed timestamp, bytes32 root);\n\n // Fallback function to handle raw 32-byte timestamp input\n // solhint-disable no-complex-fallback\n fallback() external {\n // Ensure input is exactly 32 bytes (big-endian encoded timestamp)\n require(msg.data.length == 32, \"Input must be 32 bytes\");\n\n // Decode the 32-byte input as a uint256 timestamp (big-endian)\n uint256 timestamp = abi.decode(msg.data, (uint256));\n\n // Don't do any validation of timestamp so we can test any block\n\n // Retrieve the root. Will return bytes32(0) if not set.\n bytes32 root = _beaconRoots[timestamp];\n\n // Return the 32-byte root directly\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0, root)\n return(0, 32)\n }\n }\n\n // Mock function to set a beacon block root (for testing)\n function setBeaconRoot(uint256 timestamp, bytes32 root) external {\n require(timestamp > 0, \"Invalid timestamp\");\n require(root != bytes32(0), \"Invalid root\");\n\n // Store the root at the given timestamp\n _beaconRoots[timestamp] = root;\n\n emit RootSet(timestamp, root);\n }\n\n function setBeaconRoot(bytes32 root) external {\n require(root != bytes32(0), \"Invalid root\");\n\n // Store the root at the given timestamp\n _beaconRoots[block.timestamp] = root;\n\n emit RootSet(block.timestamp, root);\n }\n\n function parentBlockRoot(uint64 timestamp)\n external\n view\n returns (bytes32 parentRoot)\n {\n return BeaconRoots.parentBlockRoot(timestamp);\n }\n\n function latestBlockRoot()\n external\n view\n returns (bytes32 parentRoot, uint64 timestamp)\n {\n timestamp = uint64(block.timestamp);\n parentRoot = BeaconRoots.parentBlockRoot(timestamp);\n }\n}\n" + }, + "contracts/mocks/MockChainlinkOracleFeed.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\ncontract MockChainlinkOracleFeed is AggregatorV3Interface {\n int256 price;\n uint8 numDecimals;\n\n constructor(int256 _price, uint8 _decimals) {\n price = _price;\n numDecimals = _decimals;\n }\n\n function decimals() external view override returns (uint8) {\n return numDecimals;\n }\n\n function description() external pure override returns (string memory) {\n return \"MockOracleEthFeed\";\n }\n\n function version() external pure override returns (uint256) {\n return 1;\n }\n\n function setPrice(int256 _price) public {\n price = _price;\n }\n\n function setDecimals(uint8 _decimals) public {\n numDecimals = _decimals;\n }\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(uint80 _roundId)\n external\n view\n override\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n )\n {\n roundId = _roundId;\n answer = price;\n startedAt = 0;\n updatedAt = 0;\n answeredInRound = 0;\n }\n\n function latestRoundData()\n external\n view\n override\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n )\n {\n roundId = 0;\n answer = price;\n startedAt = 0;\n updatedAt = block.timestamp;\n answeredInRound = 0;\n }\n}\n" + }, + "contracts/mocks/MockCOMP.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockCOMP is MintableERC20 {\n constructor() ERC20(\"COMP\", \"COMP\") {}\n}\n" + }, + "contracts/mocks/MockComptroller.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract MockComptroller {\n // Claim all the COMP accrued by specific holders in specific markets for their supplies and/or borrows\n function claimComp(\n address[] memory holders,\n address[] memory cTokens,\n bool borrowers,\n bool suppliers\n ) external {}\n}\n" + }, + "contracts/mocks/MockCToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { ICERC20 } from \"../strategies/ICompound.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockCToken is ICERC20, ERC20 {\n using StableMath for uint256;\n\n IERC20 public underlyingToken;\n // underlying = cToken * exchangeRate\n // cToken = underlying / exchangeRate\n uint256 exchangeRate;\n address public override comptroller;\n\n constructor(ERC20 _underlyingToken, address _comptroller)\n ERC20(\"cMock\", \"cMK\")\n {\n uint8 underlyingDecimals = _underlyingToken.decimals();\n // if has 18 dp, exchange rate should be 1e26\n // if has 8 dp, exchange rate should be 1e18\n if (underlyingDecimals > 8) {\n exchangeRate = 10**uint256(18 + underlyingDecimals - 10);\n } else if (underlyingDecimals < 8) {\n // e.g. 18-8+6 = 16\n exchangeRate = 10**uint256(18 - 8 + underlyingDecimals);\n } else {\n exchangeRate = 1e18;\n }\n underlyingToken = _underlyingToken;\n comptroller = _comptroller;\n }\n\n function decimals() public pure override returns (uint8) {\n return 8;\n }\n\n function mint(uint256 mintAmount) public override returns (uint256) {\n // Credit them with cToken\n _mint(msg.sender, mintAmount.divPrecisely(exchangeRate));\n // Take their reserve\n underlyingToken.transferFrom(msg.sender, address(this), mintAmount);\n return 0;\n }\n\n function redeem(uint256 redeemAmount) external override returns (uint256) {\n uint256 tokenAmount = redeemAmount.mulTruncate(exchangeRate);\n // Burn the cToken\n _burn(msg.sender, redeemAmount);\n // Transfer underlying to caller\n underlyingToken.transfer(msg.sender, tokenAmount);\n return 0;\n }\n\n function redeemUnderlying(uint256 redeemAmount)\n external\n override\n returns (uint256)\n {\n uint256 cTokens = redeemAmount.divPrecisely(exchangeRate);\n // Burn the cToken\n _burn(msg.sender, cTokens);\n // Transfer underlying to caller\n underlyingToken.transfer(msg.sender, redeemAmount);\n return 0;\n }\n\n function balanceOfUnderlying(address owner)\n external\n view\n override\n returns (uint256)\n {\n uint256 cTokenBal = this.balanceOf(owner);\n return cTokenBal.mulTruncate(exchangeRate);\n }\n\n function balanceOf(address owner)\n public\n view\n override(ICERC20, ERC20)\n returns (uint256)\n {\n return ERC20.balanceOf(owner);\n }\n\n function updateExchangeRate()\n internal\n view\n returns (uint256 newExchangeRate)\n {\n uint256 factor = 100002 * (10**13); // 0.002%\n newExchangeRate = exchangeRate.mulTruncate(factor);\n }\n\n function exchangeRateStored() external view override returns (uint256) {\n return exchangeRate;\n }\n\n function supplyRatePerBlock() external pure override returns (uint256) {\n return 141 * (10**8);\n }\n}\n" + }, + "contracts/mocks/MockCVXLocker.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MockCVXLocker {\n address public immutable cvx;\n mapping(address => uint256) public lockedBalanceOf;\n\n constructor(address _cvx) {\n cvx = _cvx;\n }\n\n function lock(\n address _account,\n uint256 _amount,\n uint256\n ) external {\n lockedBalanceOf[_account] += _amount;\n ERC20(cvx).transferFrom(msg.sender, address(this), _amount);\n }\n\n function unlockAllTokens(address _account) external {\n lockedBalanceOf[_account] = 0;\n ERC20(cvx).transfer(_account, lockedBalanceOf[_account]);\n }\n}\n" + }, + "contracts/mocks/MockDAI.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockDAI is MintableERC20 {\n constructor() ERC20(\"DAI\", \"DAI\") {}\n}\n" + }, + "contracts/mocks/MockDepositContract.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IDepositContract } from \"./../interfaces/IDepositContract.sol\";\n\ncontract MockDepositContract is IDepositContract {\n uint256 deposit_count;\n\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable override {\n require(pubkey.length == 48, \"DepositContract: invalid pubkey length\");\n require(\n withdrawal_credentials.length == 32,\n \"DepositContract: invalid withdrawal_credentials length\"\n );\n require(\n signature.length == 96,\n \"DepositContract: invalid signature length\"\n );\n\n // Check deposit amount\n require(msg.value >= 1 ether, \"DepositContract: deposit value too low\");\n require(\n msg.value % 1 gwei == 0,\n \"DepositContract: deposit value not multiple of gwei\"\n );\n uint256 deposit_amount = msg.value / 1 gwei;\n require(\n deposit_amount <= type(uint64).max,\n \"DepositContract: deposit value too high\"\n );\n\n // Emit `DepositEvent` log\n bytes memory amount = to_little_endian_64(uint64(deposit_amount));\n emit DepositEvent(\n pubkey,\n withdrawal_credentials,\n amount,\n signature,\n to_little_endian_64(uint64(deposit_count))\n );\n require(\n deposit_data_root != 0,\n \"DepositContract: invalid deposit_data_root\"\n );\n }\n\n function get_deposit_root() external view override returns (bytes32) {\n // just return some bytes32\n return sha256(abi.encodePacked(deposit_count, bytes16(0)));\n }\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view override returns (bytes memory) {\n return to_little_endian_64(uint64(deposit_count));\n }\n\n function to_little_endian_64(uint64 value)\n internal\n pure\n returns (bytes memory ret)\n {\n ret = new bytes(8);\n bytes8 bytesValue = bytes8(value);\n // Byteswapping during copying to bytes.\n ret[0] = bytesValue[7];\n ret[1] = bytesValue[6];\n ret[2] = bytesValue[5];\n ret[3] = bytesValue[4];\n ret[4] = bytesValue[3];\n ret[5] = bytesValue[2];\n ret[6] = bytesValue[1];\n ret[7] = bytesValue[0];\n }\n}\n" + }, + "contracts/mocks/MockEvilDAI.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract MockEvilDAI is MintableERC20 {\n address host;\n address realCoin;\n\n constructor(address _host, address _realCoin) ERC20(\"DAI\", \"DAI\") {\n host = _host;\n realCoin = _realCoin;\n }\n\n function transferFrom(\n // solhint-disable-next-line no-unused-vars\n address _from,\n // solhint-disable-next-line no-unused-vars\n address _to,\n uint256 _amount\n ) public override returns (bool) {\n // call mint again!\n if (_amount != 69) {\n IVault(host).mint(address(this), 69, 0);\n }\n return true;\n }\n}\n" + }, + "contracts/mocks/MockEvilReentrantContract.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IRateProvider } from \"../interfaces/balancer/IRateProvider.sol\";\n\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { IERC20 } from \"../utils/InitializableAbstractStrategy.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockEvilReentrantContract {\n using StableMath for uint256;\n\n IBalancerVault public immutable balancerVault;\n IERC20 public immutable reth;\n IERC20 public immutable weth;\n IVault public immutable oethVault;\n address public immutable poolAddress;\n bytes32 public immutable balancerPoolId;\n\n constructor(\n address _balancerVault,\n address _oethVault,\n address _reth,\n address _weth,\n address _poolAddress,\n bytes32 _poolId\n ) {\n balancerVault = IBalancerVault(_balancerVault);\n oethVault = IVault(_oethVault);\n reth = IERC20(_reth);\n weth = IERC20(_weth);\n poolAddress = _poolAddress;\n balancerPoolId = _poolId;\n }\n\n function doEvilStuff() public {\n address priceProvider = oethVault.priceProvider();\n uint256 rethPrice = IOracle(priceProvider).price(address(reth));\n\n // 1. Join pool\n uint256[] memory amounts = new uint256[](2);\n amounts[0] = uint256(10 ether);\n amounts[1] = rethPrice * 10;\n\n address[] memory assets = new address[](2);\n assets[0] = address(reth);\n assets[1] = address(weth);\n\n uint256 minBPT = getBPTExpected(assets, amounts).mulTruncate(\n 0.99 ether\n );\n\n bytes memory joinUserData = abi.encode(\n IBalancerVault.WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,\n amounts,\n minBPT\n );\n\n IBalancerVault.JoinPoolRequest memory joinRequest = IBalancerVault\n .JoinPoolRequest(assets, amounts, joinUserData, false);\n\n balancerVault.joinPool(\n balancerPoolId,\n address(this),\n address(this),\n joinRequest\n );\n\n uint256 bptTokenBalance = IERC20(poolAddress).balanceOf(address(this));\n\n // 2. Redeem as ETH\n bytes memory exitUserData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT,\n bptTokenBalance,\n 1\n );\n\n assets[1] = address(0); // Receive ETH instead of WETH\n uint256[] memory exitAmounts = new uint256[](2);\n exitAmounts[1] = 15 ether;\n IBalancerVault.ExitPoolRequest memory exitRequest = IBalancerVault\n .ExitPoolRequest(assets, exitAmounts, exitUserData, false);\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n payable(address(this)),\n exitRequest\n );\n bptTokenBalance = IERC20(poolAddress).balanceOf(address(this));\n }\n\n function getBPTExpected(address[] memory _assets, uint256[] memory _amounts)\n internal\n view\n virtual\n returns (uint256 bptExpected)\n {\n // Get the oracle from the OETH Vault\n address priceProvider = oethVault.priceProvider();\n\n for (uint256 i = 0; i < _assets.length; ++i) {\n uint256 strategyAssetMarketPrice = IOracle(priceProvider).price(\n _assets[i]\n );\n // convert asset amount to ETH amount\n bptExpected =\n bptExpected +\n _amounts[i].mulTruncate(strategyAssetMarketPrice);\n }\n\n uint256 bptRate = IRateProvider(poolAddress).getRate();\n // Convert ETH amount to BPT amount\n bptExpected = bptExpected.divPrecisely(bptRate);\n }\n\n function approveAllTokens() public {\n // Approve all tokens\n weth.approve(address(oethVault), type(uint256).max);\n reth.approve(poolAddress, type(uint256).max);\n weth.approve(poolAddress, type(uint256).max);\n reth.approve(address(balancerVault), type(uint256).max);\n weth.approve(address(balancerVault), type(uint256).max);\n }\n\n receive() external payable {\n // 3. Try to mint OETH\n oethVault.mint(address(weth), 1 ether, 0.9 ether);\n }\n}\n" + }, + "contracts/mocks/MockfrxETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockfrxETH is MintableERC20 {\n constructor() ERC20(\"frxETH\", \"frxETH\") {}\n}\n" + }, + "contracts/mocks/MockFrxETHMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockFrxETHMinter {\n address public immutable frxETH;\n address public immutable sfrxETH;\n\n constructor(address _frxETH, address _sfrxETH) {\n frxETH = _frxETH;\n sfrxETH = _sfrxETH;\n }\n\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares)\n {\n IMintableERC20(frxETH).mintTo(sfrxETH, msg.value);\n IMintableERC20(sfrxETH).mintTo(recipient, msg.value);\n shares = msg.value;\n }\n}\n" + }, + "contracts/mocks/MockLimitedWrappedOusd.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WrappedOusd } from \"../token/WrappedOusd.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MockLimitedWrappedOusd is WrappedOusd {\n constructor(ERC20 underlying_) WrappedOusd(underlying_) {}\n\n function maxDeposit(address)\n public\n view\n virtual\n override\n returns (uint256)\n {\n return 1e18;\n }\n}\n" + }, + "contracts/mocks/MockMaverickDistributor.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Pool } from \"../interfaces/plume/IMaverickV2Pool.sol\";\n\ncontract MockMaverickDistributor {\n IERC20 public immutable rewardToken;\n\n uint256 public lastEpoch;\n uint256 public rewardAmount;\n\n constructor(address _rewardToken) {\n rewardToken = IERC20(_rewardToken);\n }\n\n function setLastEpoch(uint256 _epoch) external {\n lastEpoch = _epoch;\n }\n\n function setRewardTokenAmount(uint256 _amount) external {\n rewardAmount = _amount;\n }\n\n function claimLp(\n address recipient,\n uint256,\n IMaverickV2Pool,\n uint32[] memory,\n uint256\n ) external returns (uint256 amount) {\n rewardToken.transfer(recipient, rewardAmount);\n return rewardAmount;\n }\n}\n" + }, + "contracts/mocks/MockMetadataToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// IERC20Metadata is used in the resolveAsset function in contracts/utils/assets.js\n// We just need to import it here to make its ABI available to Hardhat\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n" + }, + "contracts/mocks/MockMintableUniswapPair.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport \"./MockUniswapPair.sol\";\n\ncontract MockMintableUniswapPair is MockUniswapPair, MintableERC20 {\n constructor(\n address _token0,\n address _token1,\n uint112 _reserve0,\n uint112 _reserve1\n )\n MockUniswapPair(_token0, _token1, _reserve0, _reserve1)\n ERC20(\"Uniswap V2\", \"UNI-v2\")\n {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/MockNonRebasing.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { OUSD } from \"../token/OUSD.sol\";\n\ncontract MockNonRebasing {\n OUSD oUSD;\n\n function setOUSD(address _oUSDAddress) public {\n oUSD = OUSD(_oUSDAddress);\n }\n\n function rebaseOptIn() public {\n oUSD.rebaseOptIn();\n }\n\n function rebaseOptOut() public {\n oUSD.rebaseOptOut();\n }\n\n function transfer(address _to, uint256 _value) public {\n oUSD.transfer(_to, _value);\n }\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) public {\n oUSD.transferFrom(_from, _to, _value);\n }\n\n function approve(address _spender, uint256 _addedValue) public {\n oUSD.approve(_spender, _addedValue);\n }\n\n function mintOusd(\n address _vaultContract,\n address _asset,\n uint256 _amount\n ) public {\n IVault(_vaultContract).mint(_asset, _amount, 0);\n }\n\n function redeemOusd(address _vaultContract, uint256 _amount) public {\n IVault(_vaultContract).redeem(_amount, 0);\n }\n\n function approveFor(\n address _contract,\n address _spender,\n uint256 _addedValue\n ) public {\n IERC20(_contract).approve(_spender, _addedValue);\n }\n}\n" + }, + "contracts/mocks/MockNonStandardToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\nimport \"./MintableERC20.sol\";\n\n/**\n * Mock token contract to simulate tokens that don't\n * throw/revert when a transfer/transferFrom call fails\n */\ncontract MockNonStandardToken is MintableERC20 {\n using SafeMath for uint256;\n\n constructor() ERC20(\"NonStandardToken\", \"NonStandardToken\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n\n function transfer(address recipient, uint256 amount)\n public\n override\n returns (bool)\n {\n if (balanceOf(msg.sender) < amount) {\n // Fail silently\n return false;\n }\n\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public override returns (bool) {\n if (balanceOf(sender) < amount) {\n // Fail silently\n return false;\n }\n\n _transfer(sender, recipient, amount);\n _approve(\n sender,\n _msgSender(),\n allowance(sender, _msgSender()).sub(\n amount,\n \"ERC20: transfer amount exceeds allowance\"\n )\n );\n return true;\n }\n}\n" + }, + "contracts/mocks/MockOETHVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultCore } from \"../vault/OETHVaultCore.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MockOETHVault is OETHVaultCore {\n using StableMath for uint256;\n\n constructor(address _weth) OETHVaultCore(_weth) {\n _setGovernor(msg.sender);\n }\n\n function supportAsset(address asset) external {\n assets[asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(0),\n decimals: 18,\n allowedOracleSlippageBps: 0\n });\n\n allAssets.push(asset);\n }\n}\n" + }, + "contracts/mocks/MockOETHVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"../vault/OETHVaultAdmin.sol\";\n\ncontract MockOETHVaultAdmin is OETHVaultAdmin {\n constructor(address _weth) OETHVaultAdmin(_weth) {}\n\n // fetches the WETH amount in outstanding withdrawals\n function outstandingWithdrawalsAmount()\n external\n view\n returns (uint256 wethAmount)\n {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n wethAmount = queue.queued - queue.claimed;\n }\n\n function wethAvailable() external view returns (uint256) {\n return _wethAvailable();\n }\n}\n" + }, + "contracts/mocks/MockOGN.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./BurnableERC20.sol\";\nimport \"./MintableERC20.sol\";\n\n/**\n * @title Origin token (OGN).\n *\n * @dev Token that allows minting and burning.\n * @dev Important note:\n * @dev There is a known race condition in the ERC20 standard on the approve() method.\n * @dev See details: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n * @dev The Origin token contract implements the increaseApproval() and decreaseApproval() methods.\n * @dev It is strongly recommended to use those methods rather than approve()\n * @dev when updating the token allowance.\n */\ncontract MockOGN is MintableERC20, BurnableERC20 {\n event SetWhitelistExpiration(uint256 expiration);\n event AllowedTransactorAdded(address sender);\n event AllowedTransactorRemoved(address sender);\n event AddCallSpenderWhitelist(address enabler, address spender);\n event RemoveCallSpenderWhitelist(address disabler, address spender);\n\n mapping(address => bool) public callSpenderWhitelist;\n address public owner = msg.sender;\n // UNIX timestamp (in seconds) after which this whitelist no longer applies\n uint256 public whitelistExpiration;\n // While the whitelist is active, either the sender or recipient must be\n // in allowedTransactors.\n mapping(address => bool) public allowedTransactors;\n\n // @dev Constructor that gives msg.sender all initial tokens.\n constructor(uint256 _initialSupply) ERC20(\"OriginToken\", \"OGN\") {\n owner = msg.sender;\n _mint(owner, _initialSupply);\n }\n\n //\n // approveAndCall methods\n //\n\n // @dev Add spender to whitelist of spenders for approveAndCall\n // @param _spender Address to add\n function addCallSpenderWhitelist(address _spender) public onlyOwner {\n callSpenderWhitelist[_spender] = true;\n emit AddCallSpenderWhitelist(msg.sender, _spender);\n }\n\n // @dev Remove spender from whitelist of spenders for approveAndCall\n // @param _spender Address to remove\n function removeCallSpenderWhitelist(address _spender) public onlyOwner {\n delete callSpenderWhitelist[_spender];\n emit RemoveCallSpenderWhitelist(msg.sender, _spender);\n }\n\n // @dev Approve transfer of tokens and make a contract call in a single\n // @dev transaction. This allows a DApp to avoid requiring two MetaMask\n // @dev approvals for a single logical action, such as creating a listing,\n // @dev which requires the seller to approve a token transfer and the\n // @dev marketplace contract to transfer tokens from the seller.\n //\n // @dev This is based on the ERC827 function approveAndCall and avoids\n // @dev security issues by only working with a whitelisted set of _spender\n // @dev addresses. The other difference is that the combination of this\n // @dev function ensures that the proxied function call receives the\n // @dev msg.sender for this function as its first parameter.\n //\n // @param _spender The address that will spend the funds.\n // @param _value The amount of tokens to be spent.\n // @param _selector Function selector for function to be called.\n // @param _callParams Packed, encoded parameters, omitting the first parameter which is always msg.sender\n function approveAndCallWithSender(\n address _spender,\n uint256 _value,\n bytes4 _selector,\n bytes memory _callParams\n ) public payable returns (bool) {\n require(_spender != address(this), \"token contract can't be approved\");\n require(callSpenderWhitelist[_spender], \"spender not in whitelist\");\n\n require(super.approve(_spender, _value), \"approve failed\");\n\n bytes memory callData = abi.encodePacked(\n _selector,\n uint256(uint160(msg.sender)),\n _callParams\n );\n // solium-disable-next-line security/no-call-value\n (bool success, ) = _spender.call{ value: msg.value }(callData);\n require(success, \"proxied call failed\");\n return true;\n }\n\n //\n // Functions for maintaining whitelist\n //\n\n modifier onlyOwner() {\n require(msg.sender == owner);\n _;\n }\n modifier allowedTransfer(address _from, address _to) {\n require(\n // solium-disable-next-line operator-whitespace\n !whitelistActive() ||\n allowedTransactors[_from] ||\n allowedTransactors[_to],\n \"neither sender nor recipient are allowed\"\n );\n _;\n }\n\n function whitelistActive() public view returns (bool) {\n return block.timestamp < whitelistExpiration;\n }\n\n function addAllowedTransactor(address _transactor) public onlyOwner {\n emit AllowedTransactorAdded(_transactor);\n allowedTransactors[_transactor] = true;\n }\n\n function removeAllowedTransactor(address _transactor) public onlyOwner {\n emit AllowedTransactorRemoved(_transactor);\n delete allowedTransactors[_transactor];\n }\n\n /**\n * @dev Set the whitelist expiration, after which the whitelist no longer\n * applies.\n */\n function setWhitelistExpiration(uint256 _expiration) public onlyOwner {\n // allow only if whitelist expiration hasn't yet been set, or if the\n // whitelist expiration hasn't passed yet\n require(\n whitelistExpiration == 0 || whitelistActive(),\n \"an expired whitelist cannot be extended\"\n );\n // prevent possible mistakes in calling this function\n require(\n _expiration >= block.timestamp + 1 days,\n \"whitelist expiration not far enough into the future\"\n );\n emit SetWhitelistExpiration(_expiration);\n whitelistExpiration = _expiration;\n }\n\n //\n // ERC20 transfer functions that have been overridden to enforce the\n // whitelist.\n //\n\n function transfer(address _to, uint256 _value)\n public\n override\n allowedTransfer(msg.sender, _to)\n returns (bool)\n {\n return super.transfer(_to, _value);\n }\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) public override allowedTransfer(_from, _to) returns (bool) {\n return super.transferFrom(_from, _to, _value);\n }\n}\n" + }, + "contracts/mocks/MockOGV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockOGV is MintableERC20 {\n constructor() ERC20(\"OGV\", \"OGV\") {}\n}\n" + }, + "contracts/mocks/MockOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/IPriceOracle.sol\";\nimport \"../interfaces/IMinMaxOracle.sol\";\n\n/**\n * Mock of both price Oracle and min max oracles\n */\ncontract MockOracle is IPriceOracle, IMinMaxOracle {\n mapping(bytes32 => uint256) prices;\n mapping(bytes32 => uint256[]) pricesMinMax;\n uint256 ethMin;\n uint256 ethMax;\n\n /**\n * @dev returns the asset price in USD, 6 decimal digits.\n * Compatible with the Open Price Feed.\n */\n function price(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n return prices[keccak256(abi.encodePacked(symbol))];\n }\n\n /**\n * @dev sets the price of the asset in USD, 6 decimal digits\n *\n */\n function setPrice(string calldata symbol, uint256 _price) external {\n prices[keccak256(abi.encodePacked(symbol))] = _price;\n }\n\n /**\n * @dev sets the min and max price of ETH in USD, 6 decimal digits\n *\n */\n function setEthPriceMinMax(uint256 _min, uint256 _max) external {\n ethMin = _min;\n ethMax = _max;\n }\n\n /**\n * @dev sets the prices Min Max for a specific symbol in ETH, 8 decimal digits\n *\n */\n function setTokPriceMinMax(\n string calldata symbol,\n uint256 _min,\n uint256 _max\n ) external {\n pricesMinMax[keccak256(abi.encodePacked(symbol))] = [_min, _max];\n }\n\n /**\n * @dev get the price of asset in ETH, 8 decimal digits.\n */\n function priceMin(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n uint256[] storage pMinMax = pricesMinMax[\n keccak256(abi.encodePacked(symbol))\n ];\n return (pMinMax[0] * ethMin) / 1e6;\n }\n\n /**\n * @dev get the price of asset in USD, 8 decimal digits.\n * Not needed for now\n */\n function priceMax(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n uint256[] storage pMinMax = pricesMinMax[\n keccak256(abi.encodePacked(symbol))\n ];\n return (pMinMax[1] * ethMax) / 1e6;\n }\n}\n" + }, + "contracts/mocks/MockOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"../oracle/AbstractOracleRouter.sol\";\n\n// @notice Oracle Router required for testing environment\ncontract MockOracleRouter is AbstractOracleRouter {\n struct FeedMetadata {\n address feedAddress;\n uint256 maxStaleness;\n }\n\n mapping(address => FeedMetadata) public assetToFeedMetadata;\n\n /* @dev Override feed and maxStaleness information for a particular asset\n * @param _asset the asset to override feed for\n * @param _feed new feed\n * @param _maxStaleness new maximum time allowed for feed data to be stale\n */\n function setFeed(\n address _asset,\n address _feed,\n uint256 _maxStaleness\n ) external {\n assetToFeedMetadata[_asset] = FeedMetadata(_feed, _maxStaleness);\n }\n\n /*\n * The dev version of the Oracle doesn't need to gas optimize and cache the decimals\n */\n function getDecimals(address _feed) internal view override returns (uint8) {\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n return AggregatorV3Interface(_feed).decimals();\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n FeedMetadata storage fm = assetToFeedMetadata[asset];\n feedAddress = fm.feedAddress;\n maxStaleness = fm.maxStaleness;\n }\n}\n" + }, + "contracts/mocks/MockOracleRouterNoStale.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { OracleRouter } from \"../oracle/OracleRouter.sol\";\nimport { OETHOracleRouter } from \"../oracle/OETHOracleRouter.sol\";\n\n// @notice Oracle Router used to bypass staleness\ncontract MockOracleRouterNoStale is OracleRouter {\n function feedMetadata(address asset)\n internal\n pure\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n (feedAddress, ) = super.feedMetadata(asset);\n maxStaleness = 365 days;\n }\n}\n\n// @notice Oracle Router used to bypass staleness\ncontract MockOETHOracleRouterNoStale is OETHOracleRouter {\n constructor() OETHOracleRouter() {}\n\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n (feedAddress, ) = super.feedMetadata(asset);\n maxStaleness = 365 days;\n }\n}\n" + }, + "contracts/mocks/MockOracleWeightedPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Variable, OracleAverageQuery, IOracleWeightedPool } from \"../interfaces/balancer/IOracleWeightedPool.sol\";\n\ncontract MockOracleWeightedPool is IOracleWeightedPool {\n uint256[] public nextResults;\n\n constructor() {\n nextResults = [1 ether, 1 ether];\n }\n\n function getTimeWeightedAverage(OracleAverageQuery[] memory)\n external\n view\n override\n returns (uint256[] memory results)\n {\n return nextResults;\n }\n\n function setNextResults(uint256[] calldata results) external {\n nextResults = results;\n }\n}\n" + }, + "contracts/mocks/MockPartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PartialWithdrawal } from \"../beacon/PartialWithdrawal.sol\";\n\ncontract MockPartialWithdrawal {\n function fee() external view returns (uint256) {\n return PartialWithdrawal.fee();\n }\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n external\n returns (uint256 fee_)\n {\n return PartialWithdrawal.request(validatorPubKey, amount);\n }\n}\n" + }, + "contracts/mocks/MockRebornMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n// solhint-disable-next-line no-console\nimport \"hardhat/console.sol\";\n\ncontract Sanctum {\n address public asset;\n address public vault;\n address public reborner;\n bool public shouldAttack = false;\n // should selfdestruct in the constructor\n bool public shouldDestruct = false;\n uint256 public targetMethod;\n address public ousdContract;\n\n constructor(address _asset, address _vault) {\n asset = _asset;\n vault = _vault;\n }\n\n function deploy(uint256 salt, bytes memory bytecode)\n public\n returns (address addr)\n {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt)\n }\n require(addr != address(0), \"Create2: Failed on deploy\");\n }\n\n function computeAddress(uint256 salt, bytes memory bytecode)\n public\n view\n returns (address)\n {\n bytes32 bytecodeHashHash = keccak256(bytecode);\n bytes32 _data = keccak256(\n abi.encodePacked(\n bytes1(0xff),\n address(this),\n salt,\n bytecodeHashHash\n )\n );\n return address(bytes20(_data << 96));\n }\n\n function setShouldAttack(bool _shouldAttack) public {\n shouldAttack = _shouldAttack;\n }\n\n // should call selfdestruct in the constructor\n function setShouldDesctruct(bool _shouldDestruct) public {\n shouldDestruct = _shouldDestruct;\n }\n\n function setTargetMethod(uint256 target) public {\n targetMethod = target;\n }\n\n function setOUSDAddress(address _ousdContract) public {\n ousdContract = _ousdContract;\n }\n}\n\ncontract Reborner {\n Sanctum sanctum;\n bool logging = false;\n\n constructor(address _sanctum) {\n log(\"We are created...\");\n sanctum = Sanctum(_sanctum);\n if (sanctum.shouldAttack()) {\n log(\"We are attacking now...\");\n\n uint256 target = sanctum.targetMethod();\n\n if (target == 1) {\n redeem();\n } else if (target == 2) {\n transfer();\n } else {\n mint();\n }\n }\n\n if (sanctum.shouldDestruct()) {\n bye();\n }\n }\n\n function mint() public {\n log(\"We are attempting to mint..\");\n address asset = sanctum.asset();\n address vault = sanctum.vault();\n IERC20(asset).approve(vault, 1e18);\n IVault(vault).mint(asset, 1e18, 0);\n log(\"We are now minting..\");\n }\n\n function redeem() public {\n log(\"We are attempting to redeem..\");\n address vault = sanctum.vault();\n IVault(vault).redeem(1e18, 1e18);\n log(\"We are now redeeming..\");\n }\n\n function transfer() public {\n log(\"We are attempting to transfer..\");\n address ousd = sanctum.ousdContract();\n require(IERC20(ousd).transfer(address(1), 1e18), \"transfer failed\");\n log(\"We are now transfering..\");\n }\n\n function bye() public {\n log(\"We are now destructing..\");\n selfdestruct(payable(msg.sender));\n }\n\n function log(string memory message) internal view {\n if (logging) {\n // solhint-disable-next-line no-console\n console.log(message);\n }\n }\n}\n" + }, + "contracts/mocks/MockRETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport \"../interfaces/IGetExchangeRateToken.sol\";\n\ncontract MockRETH is MintableERC20, IGetExchangeRateToken {\n uint256 private exchangeRate = 12e17;\n\n constructor() ERC20(\"Rocket Pool ETH\", \"rETH\") {}\n\n function getExchangeRate() external view override returns (uint256) {\n return exchangeRate;\n }\n\n function setExchangeRate(uint256 _rate) external {\n exchangeRate = _rate;\n }\n}\n" + }, + "contracts/mocks/MockRoosterAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Rooster AMO strategy exposing extra functionality\n * @author Origin Protocol Inc\n */\n\nimport { RoosterAMOStrategy } from \"../strategies/plume/RoosterAMOStrategy.sol\";\nimport { IMaverickV2Pool } from \"../interfaces/plume/IMaverickV2Pool.sol\";\n\ncontract MockRoosterAMOStrategy is RoosterAMOStrategy {\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethpAddress,\n address _liquidityManager,\n address _poolLens,\n address _maverickPosition,\n address _maverickQuoter,\n address _mPool,\n bool _upperTickAtParity,\n address _votingDistributor,\n address _poolDistributor\n )\n RoosterAMOStrategy(\n _stratConfig,\n _wethAddress,\n _oethpAddress,\n _liquidityManager,\n _poolLens,\n _maverickPosition,\n _maverickQuoter,\n _mPool,\n _upperTickAtParity,\n _votingDistributor,\n _poolDistributor\n )\n {}\n\n function getCurrentWethShare() external view returns (uint256) {\n uint256 _currentPrice = getPoolSqrtPrice();\n\n return _getWethShare(_currentPrice);\n }\n}\n" + }, + "contracts/mocks/MockSFC.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockSFC {\n error ZeroAmount();\n error TransferFailed();\n error StakeIsFullySlashed();\n\n // Mapping of delegator address to validator ID to amount delegated\n mapping(address => mapping(uint256 => uint256)) public delegations;\n // Mapping of delegator address to validator ID to withdrawal request ID to amount\n mapping(address => mapping(uint256 => mapping(uint256 => uint256)))\n public withdraws;\n // validator ID -> slashing refund ratio (allows to withdraw slashed stake)\n mapping(uint256 => uint256) public slashingRefundRatio;\n\n function getStake(address delegator, uint256 validatorID)\n external\n view\n returns (uint256)\n {\n return delegations[delegator][validatorID];\n }\n\n function delegate(uint256 validatorID) external payable {\n if (msg.value == 0) {\n revert ZeroAmount();\n }\n delegations[msg.sender][validatorID] += msg.value;\n }\n\n function undelegate(\n uint256 validatorID,\n uint256 wrID,\n uint256 amount\n ) external {\n require(\n delegations[msg.sender][validatorID] >= amount,\n \"insufficient stake\"\n );\n require(\n withdraws[msg.sender][validatorID][wrID] == 0,\n \"withdrawal request already exists\"\n );\n\n delegations[msg.sender][validatorID] -= amount;\n withdraws[msg.sender][validatorID][wrID] = amount;\n }\n\n function withdraw(uint256 validatorID, uint256 wrID) external {\n require(withdraws[msg.sender][validatorID][wrID] > 0, \"no withdrawal\");\n\n uint256 withdrawAmount = withdraws[msg.sender][validatorID][wrID];\n uint256 penalty = (withdrawAmount *\n (1e18 - slashingRefundRatio[validatorID])) / 1e18;\n\n if (penalty >= withdrawAmount) {\n revert StakeIsFullySlashed();\n }\n\n (bool sent, ) = msg.sender.call{ value: withdrawAmount - penalty }(\"\");\n if (!sent) {\n revert TransferFailed();\n }\n }\n\n function pendingRewards(address delegator, uint256 validatorID)\n external\n view\n returns (uint256)\n {}\n\n function claimRewards(uint256 validatorID) external {}\n\n function restakeRewards(uint256 validatorID) external {}\n\n /// @param refundRatio the percentage of the staked amount that can be refunded. 0.1e18 = 10%, 1e18 = 100%\n function slashValidator(uint256 validatorID, uint256 refundRatio) external {\n require(refundRatio <= 1e18, \"invalid refund ratio\");\n slashingRefundRatio[validatorID] = refundRatio;\n }\n}\n" + }, + "contracts/mocks/MocksfrxETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MocksfrxETH is MintableERC20 {\n address public frxETH;\n\n constructor(address _frxETH) ERC20(\"sfrxETH\", \"sfrxETH\") {\n frxETH = _frxETH;\n }\n\n function setMockfrxETHAddress(address _frxETH) external {\n frxETH = _frxETH;\n }\n\n function deposit(uint256 assets, address receiver)\n external\n returns (uint256 shares)\n {\n ERC20(frxETH).transferFrom(msg.sender, address(this), assets);\n\n _mint(receiver, assets);\n\n return assets;\n }\n\n function maxWithdraw(address owner) external view returns (uint256) {\n return balanceOf(owner);\n }\n\n function setMaxWithdrawableBalance(address owner, uint256 balance)\n external\n {\n uint256 currentBalance = balanceOf(owner);\n if (currentBalance > balance) {\n _burn(owner, currentBalance - balance);\n } else if (balance > currentBalance) {\n _mint(owner, balance - currentBalance);\n }\n }\n\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets) {\n _burn(owner, shares);\n\n ERC20(frxETH).transfer(receiver, shares);\n\n assets = shares;\n }\n\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares) {\n _burn(owner, assets);\n\n ERC20(frxETH).transfer(receiver, assets);\n\n shares = assets;\n }\n\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares)\n {\n _mint(recipient, msg.value);\n shares = msg.value;\n }\n}\n" + }, + "contracts/mocks/MockSSV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockSSV is MintableERC20 {\n constructor() ERC20(\"SSV Token\", \"SSV\") {}\n}\n" + }, + "contracts/mocks/MockSSVNetwork.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Cluster } from \"./../interfaces/ISSVNetwork.sol\";\n\ncontract MockSSVNetwork {\n function registerValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function exitValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds\n ) external {}\n\n function removeValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external {}\n\n function deposit(\n address clusterOwner,\n uint64[] calldata operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function setFeeRecipientAddress(address recipient) external {}\n}\n" + }, + "contracts/mocks/MockstETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockstETH is MintableERC20 {\n constructor() ERC20(\"stETH\", \"stETH\") {}\n}\n" + }, + "contracts/mocks/MockStkAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"./MintableERC20.sol\";\n\ncontract MockStkAave is MintableERC20 {\n uint256 public COOLDOWN_SECONDS = 864000;\n uint256 public UNSTAKE_WINDOW = 172800;\n address public STAKED_TOKEN;\n\n mapping(address => uint256) public stakerRewardsToClaim;\n mapping(address => uint256) public stakersCooldowns;\n\n using SafeERC20 for IERC20;\n\n constructor(address _stakedToken) ERC20(\"Staked Aave\", \"stkAAVE\") {\n STAKED_TOKEN = _stakedToken;\n }\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n\n function setStakedToken(address _stakedToken) external {\n STAKED_TOKEN = _stakedToken;\n }\n\n /**\n * @dev Redeems staked tokens, and stop earning rewards\n * @param to Address to redeem to\n * @param amount Amount to redeem\n **/\n function redeem(address to, uint256 amount) external {\n uint256 cooldownStartTimestamp = stakersCooldowns[msg.sender];\n uint256 windowStart = cooldownStartTimestamp + COOLDOWN_SECONDS;\n require(amount != 0, \"INVALID_ZERO_AMOUNT\");\n require(block.timestamp > windowStart, \"INSUFFICIENT_COOLDOWN\");\n require(\n block.timestamp - windowStart <= UNSTAKE_WINDOW,\n \"UNSTAKE_WINDOW_FINISHED\"\n );\n uint256 balanceOfMessageSender = balanceOf(msg.sender);\n uint256 amountToRedeem = (amount > balanceOfMessageSender)\n ? balanceOfMessageSender\n : amount;\n\n stakersCooldowns[msg.sender] = 0;\n _burn(msg.sender, amountToRedeem);\n IERC20(STAKED_TOKEN).safeTransfer(to, amountToRedeem);\n }\n\n /**\n * @dev Activates the cooldown period to unstake\n * - It can't be called if the user is not staking\n **/\n function cooldown() external {\n require(balanceOf(msg.sender) != 0, \"INVALID_BALANCE_ON_COOLDOWN\");\n stakersCooldowns[msg.sender] = block.timestamp;\n }\n\n /**\n * @dev Test helper function to allow changing the cooldown\n **/\n function setCooldown(address account, uint256 _cooldown) external {\n stakersCooldowns[account] = _cooldown;\n }\n}\n" + }, + "contracts/mocks/MockStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract MockStrategy {\n address[] public assets;\n\n address public withdrawAllAsset;\n address public withdrawAllRecipient;\n\n constructor() {}\n\n function deposit(address asset, uint256 amount) external {}\n\n function depositAll() external {}\n\n function withdraw(\n address recipient,\n address asset,\n uint256 amount\n ) external {\n IERC20(asset).transfer(recipient, amount);\n }\n\n function withdrawAll() external {\n IERC20(withdrawAllAsset).transfer(\n withdrawAllRecipient,\n IERC20(withdrawAllAsset).balanceOf(address(this))\n );\n }\n\n function checkBalance(address asset)\n external\n view\n returns (uint256 balance)\n {\n balance = IERC20(asset).balanceOf(address(this));\n }\n\n function supportsAsset(address) external view returns (bool) {\n return true;\n }\n\n function collectRewardTokens() external {}\n\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return new address[](0);\n }\n\n function setWithdrawAll(address asset, address recipient) external {\n withdrawAllAsset = asset;\n withdrawAllRecipient = recipient;\n }\n}\n" + }, + "contracts/mocks/MockSwapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IMintableERC20 } from \"./MintableERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract MockSwapper {\n uint256 public nextOutAmount;\n\n function swap(\n // solhint-disable-next-line no-unused-vars\n address _fromAsset,\n address _toAsset,\n // solhint-disable-next-line no-unused-vars\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n // solhint-disable-next-line no-unused-vars\n bytes calldata _data\n ) external returns (uint256 toAssetAmount) {\n toAssetAmount = (nextOutAmount > 0) ? nextOutAmount : _minToAssetAmount;\n nextOutAmount = 0;\n IMintableERC20(_toAsset).mint(toAssetAmount);\n IERC20(_toAsset).transfer(msg.sender, toAssetAmount);\n }\n\n function setNextOutAmount(uint256 _nextOutAmount) public {\n nextOutAmount = _nextOutAmount;\n }\n}\n" + }, + "contracts/mocks/MockTUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockTUSD is MintableERC20 {\n constructor() ERC20(\"TrueUSD\", \"TUSD\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/MockUniswapPair.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IUniswapV2Pair } from \"../interfaces/uniswap/IUniswapV2Pair.sol\";\n\ncontract MockUniswapPair is IUniswapV2Pair {\n address tok0;\n address tok1;\n uint112 reserve0;\n uint112 reserve1;\n uint256 blockTimestampLast;\n\n bool public hasSynced = false;\n\n constructor(\n address _token0,\n address _token1,\n uint112 _reserve0,\n uint112 _reserve1\n ) {\n tok0 = _token0;\n tok1 = _token1;\n reserve0 = _reserve0;\n reserve1 = _reserve1;\n blockTimestampLast = block.timestamp;\n }\n\n function token0() external view override returns (address) {\n return tok0;\n }\n\n function token1() external view override returns (address) {\n return tok1;\n }\n\n function getReserves()\n external\n view\n override\n returns (\n uint112,\n uint112,\n uint32\n )\n {\n return (reserve0, reserve1, uint32(blockTimestampLast));\n }\n\n function setReserves(uint112 _reserve0, uint112 _reserve1) public {\n reserve0 = _reserve0;\n reserve1 = _reserve1;\n blockTimestampLast = block.timestamp;\n }\n\n // CAUTION This will not work if you setReserves multiple times over\n // multiple different blocks because then it wouldn't be a continuous\n // reserve factor over that blockTimestamp, this assumes an even reserve\n // ratio all the way through\n function price0CumulativeLast() external view override returns (uint256) {\n return\n uint256(FixedPoint.fraction(reserve1, reserve0)._x) *\n blockTimestampLast;\n }\n\n function price1CumulativeLast() external view override returns (uint256) {\n return\n uint256(FixedPoint.fraction(reserve0, reserve1)._x) *\n blockTimestampLast;\n }\n\n function sync() external override {\n hasSynced = true;\n }\n\n function checkHasSynced() external view {\n require(hasSynced, \"Not synced\");\n }\n}\n\n// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))\nlibrary FixedPoint {\n // range: [0, 2**112 - 1]\n // resolution: 1 / 2**112\n struct uq112x112 {\n uint224 _x;\n }\n\n // returns a uq112x112 which represents the ratio of the numerator to the denominator\n // equivalent to encode(numerator).div(denominator)\n function fraction(uint112 numerator, uint112 denominator)\n internal\n pure\n returns (uq112x112 memory)\n {\n require(denominator > 0, \"FixedPoint: DIV_BY_ZERO\");\n return uq112x112((uint224(numerator) << 112) / denominator);\n }\n}\n" + }, + "contracts/mocks/MockUniswapRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { IUniswapV2Router } from \"../interfaces/uniswap/IUniswapV2Router02.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockUniswapRouter is IUniswapV2Router {\n using StableMath for uint256;\n\n mapping(address => address) public pairMaps;\n uint256 public slippage = 1 ether;\n\n function initialize(\n address[] calldata _0tokens,\n address[] calldata _1tokens\n ) public {\n require(\n _0tokens.length == _1tokens.length,\n \"Mock token pairs should be of the same length\"\n );\n for (uint256 i = 0; i < _0tokens.length; i++) {\n pairMaps[_0tokens[i]] = _1tokens[i];\n }\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n\n function swapExactTokensForTokens(\n uint256 amountIn,\n uint256 amountOutMin,\n address[] calldata path,\n address to,\n // solhint-disable-next-line no-unused-vars\n uint256\n ) external override returns (uint256[] memory amountsOut) {\n address tok0 = path[0];\n address tok1 = path[path.length - 1];\n\n uint256 amountOut = (amountOutMin * slippage) / 1 ether;\n require(amountOut >= amountOutMin, \"Slippage error\");\n\n IERC20(tok0).transferFrom(msg.sender, address(this), amountIn);\n MintableERC20(tok1).mintTo(to, amountOut);\n\n amountsOut = new uint256[](path.length);\n amountsOut[path.length - 1] = amountOut;\n }\n\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut)\n {\n (address tok0, address tok1) = _getFirstAndLastToken(params.path);\n\n amountOut = (params.amountOutMinimum * slippage) / 1 ether;\n\n IERC20(tok0).transferFrom(msg.sender, address(this), params.amountIn);\n MintableERC20(tok1).mintTo(params.recipient, amountOut);\n\n require(\n amountOut >= params.amountOutMinimum,\n \"UniswapMock: amountOut less than amountOutMinimum\"\n );\n return amountOut;\n }\n\n function addLiquidity(\n address tokenA,\n address tokenB,\n uint256 amountADesired,\n uint256 amountBDesired,\n uint256 amountAMin,\n uint256 amountBMin,\n address to,\n uint256 deadline\n )\n external\n override\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 liquidity\n )\n {\n // this is needed to make this contract whole else it'd be just virtual\n }\n\n function WETH() external pure override returns (address) {\n return address(0);\n }\n\n // Universal router mock\n function execute(\n bytes calldata,\n bytes[] calldata inputs,\n uint256\n ) external payable {\n uint256 inLen = inputs.length;\n for (uint256 i = 0; i < inLen; ++i) {\n (\n address recipient,\n ,\n uint256 amountOutMinimum,\n bytes memory path,\n\n ) = abi.decode(inputs[i], (address, uint256, uint256, bytes, bool));\n\n (address token0, address token1) = _getFirstAndLastToken(path);\n\n amountOutMinimum = amountOutMinimum.scaleBy(\n Helpers.getDecimals(token0),\n Helpers.getDecimals(token1)\n );\n\n MintableERC20(token1).mintTo(recipient, amountOutMinimum);\n }\n }\n\n function _getFirstAndLastToken(bytes memory path)\n internal\n view\n returns (address token0, address token1)\n {\n bytes memory tok0Bytes = new bytes(20);\n for (uint256 j = 0; j < 20; ++j) {\n tok0Bytes[j] = path[j];\n }\n token0 = address(bytes20(tok0Bytes));\n\n if (pairMaps[token0] != address(0)) {\n token0 = pairMaps[token0];\n }\n\n bytes memory tok1Bytes = new bytes(20);\n uint256 tok1Offset = path.length - 20;\n for (uint256 j = 0; j < 20; ++j) {\n tok1Bytes[j] = path[j + tok1Offset];\n }\n token1 = address(bytes20(tok1Bytes));\n\n if (pairMaps[token1] != address(0)) {\n token1 = pairMaps[token1];\n }\n }\n}\n" + }, + "contracts/mocks/MockUSDC.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDC is MintableERC20 {\n constructor() ERC20(\"USDC Coin\", \"USDC\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n}\n" + }, + "contracts/mocks/MockUSDS.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDS is MintableERC20 {\n constructor() ERC20(\"USDS\", \"USDS\") {}\n}\n" + }, + "contracts/mocks/MockUSDT.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDT is MintableERC20 {\n constructor() ERC20(\"USDT Coin\", \"USDT\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n}\n" + }, + "contracts/mocks/MockVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultCore } from \"../vault/VaultCore.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { VaultInitializer } from \"../vault/VaultInitializer.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MockVault is VaultCore {\n using StableMath for uint256;\n\n uint256 storedTotalValue;\n\n function setTotalValue(uint256 _value) public {\n storedTotalValue = _value;\n }\n\n function totalValue() external view override returns (uint256) {\n return storedTotalValue;\n }\n\n function _totalValue() internal view override returns (uint256) {\n return storedTotalValue;\n }\n\n function _checkBalance(address _asset)\n internal\n view\n override\n returns (uint256 balance)\n {\n // Avoids rounding errors by returning the total value\n // in a single currency\n if (allAssets[0] == _asset) {\n uint256 decimals = Helpers.getDecimals(_asset);\n return storedTotalValue.scaleBy(decimals, 18);\n } else {\n return 0;\n }\n }\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external onlyGovernor {\n maxSupplyDiff = _maxSupplyDiff;\n }\n}\n" + }, + "contracts/mocks/MockVaultCoreInstantRebase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultCore } from \"../vault/VaultCore.sol\";\n\ncontract MockVaultCoreInstantRebase is VaultCore {\n function _nextYield(uint256 supply, uint256 vaultValue)\n internal\n view\n override\n returns (uint256 yield, uint256 targetRate)\n {\n if (vaultValue <= supply) {\n return (0, 0);\n }\n yield = vaultValue - supply;\n return (yield, 0);\n }\n}\n" + }, + "contracts/mocks/MockWETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\n// Just importing to \"unbreak\" coverage tests\nimport { IERC721Receiver } from \"@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol\";\n\ncontract MockWETH is MintableERC20 {\n constructor() ERC20(\"WETH\", \"WETH\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/MockWS.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockWS is MintableERC20 {\n constructor() ERC20(\"Wrapped Sonic\", \"wS\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/TestUpgradedOUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../token/OUSD.sol\";\n\n// used to alter internal state of OUSD contract\ncontract TestUpgradedOUSD is OUSD {\n constructor() OUSD() {}\n\n function overwriteCreditBalances(address _account, uint256 _creditBalance)\n public\n {\n creditBalances[_account] = _creditBalance;\n }\n\n function overwriteAlternativeCPT(address _account, uint256 _acpt) public {\n alternativeCreditsPerToken[_account] = _acpt;\n }\n\n function overwriteRebaseState(address _account, RebaseOptions _rebaseOption)\n public\n {\n rebaseState[_account] = _rebaseOption;\n }\n}\n" + }, + "contracts/oracle/AbstractOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\n// @notice Abstract functionality that is shared between various Oracle Routers\nabstract contract AbstractOracleRouter is IOracle {\n using StableMath for uint256;\n using SafeCast for int256;\n\n uint256 internal constant MIN_DRIFT = 0.7e18;\n uint256 internal constant MAX_DRIFT = 1.3e18;\n address internal constant FIXED_PRICE =\n 0x0000000000000000000000000000000000000001;\n // Maximum allowed staleness buffer above normal Oracle maximum staleness\n uint256 internal constant STALENESS_BUFFER = 1 days;\n mapping(address => uint8) internal decimalsCache;\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n returns (address feedAddress, uint256 maxStaleness);\n\n /**\n * @notice Returns the total price in 18 digit unit for a given asset.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n if (shouldBePegged(asset)) {\n require(_price <= MAX_DRIFT, \"Oracle: Price exceeds max\");\n require(_price >= MIN_DRIFT, \"Oracle: Price under min\");\n }\n return _price;\n }\n\n function getDecimals(address _feed) internal view virtual returns (uint8) {\n uint8 decimals = decimalsCache[_feed];\n require(decimals > 0, \"Oracle: Decimals not cached\");\n return decimals;\n }\n\n /**\n * @notice Before an asset/feed price is fetches for the first time the\n * decimals need to be cached. This is a gas optimization\n * @param asset address of the asset\n * @return uint8 corresponding asset decimals\n */\n function cacheDecimals(address asset) external returns (uint8) {\n (address _feed, ) = feedMetadata(asset);\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n uint8 decimals = AggregatorV3Interface(_feed).decimals();\n decimalsCache[_feed] = decimals;\n return decimals;\n }\n\n function shouldBePegged(address _asset) internal view returns (bool) {\n string memory symbol = Helpers.getSymbol(_asset);\n bytes32 symbolHash = keccak256(abi.encodePacked(symbol));\n return\n symbolHash == keccak256(abi.encodePacked(\"DAI\")) ||\n symbolHash == keccak256(abi.encodePacked(\"USDC\")) ||\n symbolHash == keccak256(abi.encodePacked(\"USDT\"));\n }\n}\n" + }, + "contracts/oracle/OETHBaseOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\n// @notice Oracle Router (for OETH on Base) that denominates all prices in ETH\ncontract OETHBaseOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n using SafeCast for int256;\n\n address constant WETH = 0x4200000000000000000000000000000000000006;\n address constant WOETH = 0xD8724322f44E5c58D7A815F542036fb17DbbF839;\n address constant WOETH_CHAINLINK_FEED =\n 0xe96EB1EDa83d18cbac224233319FA5071464e1b9;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == WETH) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == WOETH) {\n // Chainlink: https://data.chain.link/feeds/base/base/woeth-oeth-exchange-rate\n // Bridged wOETH/OETH\n feedAddress = WOETH_CHAINLINK_FEED;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OETHFixedOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHOracleRouter } from \"./OETHOracleRouter.sol\";\n\n// @notice Oracle Router that returns 1e18 for all prices\n// used solely for deployment to testnets\ncontract OETHFixedOracle is OETHOracleRouter {\n constructor() OETHOracleRouter() {}\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n // solhint-disable-next-line no-unused-vars\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n // fixes price for all of the assets\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n }\n}\n" + }, + "contracts/oracle/OETHOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// @notice Oracle Router that denominates all prices in ETH\ncontract OETHOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = uint256(_iprice).scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == 0x5E8422345238F34275888049021821E8E08CAa1f) {\n // frxETH/ETH\n feedAddress = 0xC58F3385FBc1C8AD2c0C9a061D7c13b141D7A5Df;\n maxStaleness = 18 hours + STALENESS_BUFFER;\n } else if (asset == 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/steth-eth\n // Chainlink: stETH/ETH\n feedAddress = 0x86392dC19c0b719886221c78AB11eb8Cf5c52812;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xae78736Cd615f374D3085123A210448E74Fc6393) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/reth-eth\n // Chainlink: rETH/ETH\n feedAddress = 0x536218f9E9Eb48863970252233c8F271f554C2d0;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xD533a949740bb3306d119CC777fa900bA034cd52) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/crv-eth\n // Chainlink: CRV/ETH\n feedAddress = 0x8a12Be339B0cD1829b91Adc01977caa5E9ac121e;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/cvx-eth\n // Chainlink: CVX/ETH\n feedAddress = 0xC9CbF687f43176B302F03f5e58470b77D07c61c6;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xBe9895146f7AF43049ca1c1AE358B0541Ea49704) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/cbeth-eth\n // Chainlink: cbETH/ETH\n feedAddress = 0xF017fcB346A1885194689bA23Eff2fE6fA5C483b;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xba100000625a3754423978a60c9317c58a424e3D) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/bal-eth\n // Chainlink: BAL/ETH\n feedAddress = 0xC1438AA3823A6Ba0C159CfA8D98dF5A994bA120b;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OETHPlumeOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\n// @notice Oracle Router (for OETH on Plume) that denominates all prices in ETH\ncontract OETHPlumeOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n using SafeCast for int256;\n\n address constant WETH = 0xca59cA09E5602fAe8B629DeE83FfA819741f14be;\n address constant WOETH = 0xD8724322f44E5c58D7A815F542036fb17DbbF839;\n // Ref: https://docs.eo.app/docs/eprice/feed-addresses/plume\n address constant WOETH_ORACLE_FEED =\n 0x4915600Ed7d85De62011433eEf0BD5399f677e9b;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == WETH) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == WOETH) {\n // Chainlink: https://data.chain.link/feeds/base/base/woeth-oeth-exchange-rate\n // Bridged wOETH/OETH\n feedAddress = WOETH_ORACLE_FEED;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\n\n// @notice Oracle Router that denominates all prices in USD\ncontract OracleRouter is AbstractOracleRouter {\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n pure\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n /* + STALENESS_BUFFER is added in case Oracle for some reason doesn't\n * update on heartbeat and we add a generous buffer amount.\n */\n if (asset == 0x6B175474E89094C44Da98b954EedeAC495271d0F) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/dai-usd\n // Chainlink: DAI/USD\n feedAddress = 0xAed0c38402a5d19df6E4c03F4E2DceD6e29c1ee9;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xdC035D45d973E3EC169d2276DDab16f1e407384F) {\n // https://data.chain.link/feeds/ethereum/mainnet/usds-usd\n // Chainlink: USDS/USD\n feedAddress = 0xfF30586cD0F29eD462364C7e81375FC0C71219b1;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/usdc-usd\n // Chainlink: USDC/USD\n feedAddress = 0x8fFfFfd4AfB6115b954Bd326cbe7B4BA576818f6;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xdAC17F958D2ee523a2206206994597C13D831ec7) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/usdt-usd\n // Chainlink: USDT/USD\n feedAddress = 0x3E7d1eAB13ad0104d2750B8863b489D65364e32D;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xc00e94Cb662C3520282E6f5717214004A7f26888) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/comp-usd\n // Chainlink: COMP/USD\n feedAddress = 0xdbd020CAeF83eFd542f4De03e3cF0C28A4428bd5;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/aave-usd\n // Chainlink: AAVE/USD\n feedAddress = 0x547a514d5e3769680Ce22B2361c10Ea13619e8a9;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xD533a949740bb3306d119CC777fa900bA034cd52) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/crv-usd\n // Chainlink: CRV/USD\n feedAddress = 0xCd627aA160A6fA45Eb793D19Ef54f5062F20f33f;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B) {\n // Chainlink: CVX/USD\n feedAddress = 0xd962fC30A72A84cE50161031391756Bf2876Af5D;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OSonicOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHFixedOracle } from \"./OETHFixedOracle.sol\";\n\n// @notice Oracle Router that returns 1e18 for all prices\n// used solely for deployment to testnets\ncontract OSonicOracleRouter is OETHFixedOracle {\n constructor() OETHFixedOracle() {}\n}\n" + }, + "contracts/poolBooster/AbstractPoolBoosterFactory.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IPoolBoostCentralRegistry } from \"../interfaces/poolBooster/IPoolBoostCentralRegistry.sol\";\n\n/**\n * @title Abstract Pool booster factory\n * @author Origin Protocol Inc\n */\ncontract AbstractPoolBoosterFactory is Governable {\n struct PoolBoosterEntry {\n address boosterAddress;\n address ammPoolAddress;\n IPoolBoostCentralRegistry.PoolBoosterType boosterType;\n }\n\n // @notice address of Origin Sonic\n address public immutable oSonic;\n // @notice Central registry contract\n IPoolBoostCentralRegistry public immutable centralRegistry;\n\n // @notice list of all the pool boosters created by this factory\n PoolBoosterEntry[] public poolBoosters;\n // @notice mapping of AMM pool to pool booster\n mapping(address => PoolBoosterEntry) public poolBoosterFromPool;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) {\n require(_oSonic != address(0), \"Invalid oSonic address\");\n require(_governor != address(0), \"Invalid governor address\");\n require(\n _centralRegistry != address(0),\n \"Invalid central registry address\"\n );\n\n oSonic = _oSonic;\n centralRegistry = IPoolBoostCentralRegistry(_centralRegistry);\n _setGovernor(_governor);\n }\n\n /**\n * @notice Goes over all the pool boosters created by this factory and\n * calls bribe() on them.\n * @param _exclusionList A list of pool booster addresses to skip when\n * calling this function.\n */\n function bribeAll(address[] memory _exclusionList) external {\n uint256 lengthI = poolBoosters.length;\n for (uint256 i = 0; i < lengthI; i++) {\n address poolBoosterAddress = poolBoosters[i].boosterAddress;\n bool skipBribeCall = false;\n uint256 lengthJ = _exclusionList.length;\n for (uint256 j = 0; j < lengthJ; j++) {\n // pool booster in exclusion list\n if (_exclusionList[j] == poolBoosterAddress) {\n skipBribeCall = true;\n break;\n }\n }\n\n if (!skipBribeCall) {\n IPoolBooster(poolBoosterAddress).bribe();\n }\n }\n }\n\n /**\n * @notice Removes the pool booster from the internal list of pool boosters.\n * @dev This action does not destroy the pool booster contract nor does it\n * stop the yield delegation to it.\n * @param _poolBoosterAddress address of the pool booster\n */\n function removePoolBooster(address _poolBoosterAddress)\n external\n onlyGovernor\n {\n uint256 boostersLen = poolBoosters.length;\n for (uint256 i = 0; i < boostersLen; ++i) {\n if (poolBoosters[i].boosterAddress == _poolBoosterAddress) {\n // erase mapping\n delete poolBoosterFromPool[poolBoosters[i].ammPoolAddress];\n\n // overwrite current pool booster with the last entry in the list\n poolBoosters[i] = poolBoosters[boostersLen - 1];\n // drop the last entry\n poolBoosters.pop();\n\n centralRegistry.emitPoolBoosterRemoved(_poolBoosterAddress);\n break;\n }\n }\n }\n\n function _storePoolBoosterEntry(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType _boosterType\n ) internal {\n PoolBoosterEntry memory entry = PoolBoosterEntry(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType\n );\n\n poolBoosters.push(entry);\n poolBoosterFromPool[_ammPoolAddress] = entry;\n\n // emit the events of the pool booster created\n centralRegistry.emitPoolBoosterCreated(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType\n );\n }\n\n function _deployContract(bytes memory _bytecode, uint256 _salt)\n internal\n returns (address _address)\n {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _address := create2(\n 0,\n add(_bytecode, 0x20),\n mload(_bytecode),\n _salt\n )\n }\n\n require(\n _address.code.length > 0 && _address != address(0),\n \"Failed creating a pool booster\"\n );\n }\n\n // pre-compute the address of the deployed contract that will be\n // created when create2 is called\n function _computeAddress(bytes memory _bytecode, uint256 _salt)\n internal\n view\n returns (address)\n {\n bytes32 hash = keccak256(\n abi.encodePacked(\n bytes1(0xff),\n address(this),\n _salt,\n keccak256(_bytecode)\n )\n );\n\n // cast last 20 bytes of hash to address\n return address(uint160(uint256(hash)));\n }\n\n function poolBoosterLength() external view returns (uint256) {\n return poolBoosters.length;\n }\n}\n" + }, + "contracts/poolBooster/PoolBoostCentralRegistry.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IPoolBoostCentralRegistry } from \"../interfaces/poolBooster/IPoolBoostCentralRegistry.sol\";\n\n/**\n * @title Contract that holds all governance approved pool booster Factory\n * implementation deployments\n * @author Origin Protocol Inc\n */\ncontract PoolBoostCentralRegistry is Governable, IPoolBoostCentralRegistry {\n event FactoryApproved(address factoryAddress);\n event FactoryRemoved(address factoryAddress);\n\n // @notice List of approved factories\n address[] public factories;\n\n modifier onlyApprovedFactories() {\n require(isApprovedFactory(msg.sender), \"Not an approved factory\");\n _;\n }\n\n constructor() {\n // set the governor of the implementation contract to zero address\n _setGovernor(address(0));\n }\n\n /**\n * @notice Adds a factory address to the approved factory addresses\n * @param _factoryAddress address of the factory\n */\n function approveFactory(address _factoryAddress) external onlyGovernor {\n require(_factoryAddress != address(0), \"Invalid address\");\n require(\n !isApprovedFactory(_factoryAddress),\n \"Factory already approved\"\n );\n\n factories.push(_factoryAddress);\n emit FactoryApproved(_factoryAddress);\n }\n\n /**\n * @notice Removes the factory from approved factory addresses\n * @param _factoryAddress address of the factory\n */\n function removeFactory(address _factoryAddress) external onlyGovernor {\n require(_factoryAddress != address(0), \"Invalid address\");\n\n uint256 length = factories.length;\n bool factoryRemoved = false;\n for (uint256 i = 0; i < length; i++) {\n if (factories[i] != _factoryAddress) {\n continue;\n }\n\n factories[i] = factories[length - 1];\n factories.pop();\n emit FactoryRemoved(_factoryAddress);\n factoryRemoved = true;\n break;\n }\n require(factoryRemoved, \"Not an approved factory\");\n\n emit FactoryRemoved(_factoryAddress);\n }\n\n /**\n * @notice Emits a pool booster created event\n * @dev This has been created as a convenience method for the monitoring to have\n * an index of all of the created pool boosters by only listening to the\n * events of this contract.\n * @param _poolBoosterAddress address of the pool booster created\n * @param _ammPoolAddress address of the AMM pool forwarding yield to the pool booster\n * @param _boosterType PoolBoosterType the type of the pool booster\n */\n function emitPoolBoosterCreated(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n PoolBoosterType _boosterType\n ) external onlyApprovedFactories {\n emit PoolBoosterCreated(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType,\n msg.sender // address of the factory\n );\n }\n\n /**\n * @notice Emits a pool booster removed event\n * @dev This has been created as a convenience method for the monitoring to have\n * an index of all of the removed pool boosters by only listening to the\n * events of this contract.\n * @param _poolBoosterAddress address of the pool booster to be removed\n */\n function emitPoolBoosterRemoved(address _poolBoosterAddress)\n external\n onlyApprovedFactories\n {\n emit PoolBoosterRemoved(_poolBoosterAddress);\n }\n\n /**\n * @notice Returns true if the factory is approved\n * @param _factoryAddress address of the factory\n */\n function isApprovedFactory(address _factoryAddress)\n public\n view\n returns (bool)\n {\n uint256 length = factories.length;\n for (uint256 i = 0; i < length; i++) {\n if (factories[i] == _factoryAddress) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * @notice Returns all supported factories\n */\n function getAllFactories() external view returns (address[] memory) {\n return factories;\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactoryMerkl.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterMerkl } from \"./PoolBoosterMerkl.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Merkl pool boosters.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactoryMerkl is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n /// @notice address of the Merkl distributor\n address public merklDistributor;\n\n /// @notice event emitted when the Merkl distributor is updated\n event MerklDistributorUpdated(address newDistributor);\n\n /**\n * @param _oSonic address of the OSonic token\n * @param _governor address governor\n * @param _centralRegistry address of the central registry\n * @param _merklDistributor address of the Merkl distributor\n */\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry,\n address _merklDistributor\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {\n _setMerklDistributor(_merklDistributor);\n }\n\n /**\n * @dev Create a Pool Booster for Merkl.\n * @param _campaignType The type of campaign to create. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _campaignDuration The duration of the campaign in seconds\n * @param campaignData The data to be used for the campaign. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * This should be fetched from the Merkl UI.\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterMerkl(\n uint32 _campaignType,\n address _ammPoolAddress,\n uint32 _campaignDuration,\n bytes calldata campaignData,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n require(_campaignDuration > 1 hours, \"Invalid campaign duration\");\n require(campaignData.length > 0, \"Invalid campaign data\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterMerkl).creationCode,\n abi.encode(\n oSonic,\n merklDistributor,\n _campaignDuration,\n _campaignType,\n governor(),\n campaignData\n )\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.MerklBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for Merkl.\n * @param _campaignType The type of campaign to create. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterMerkl` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n uint32 _campaignType,\n address _ammPoolAddress,\n uint32 _campaignDuration,\n bytes calldata campaignData,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n require(_campaignDuration > 1 hours, \"Invalid campaign duration\");\n require(campaignData.length > 0, \"Invalid campaign data\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterMerkl).creationCode,\n abi.encode(\n oSonic,\n merklDistributor,\n _campaignDuration,\n _campaignType,\n governor(),\n campaignData\n )\n ),\n _salt\n );\n }\n\n /**\n * @dev Set the address of the Merkl distributor\n * @param _merklDistributor The address of the Merkl distributor\n */\n function setMerklDistributor(address _merklDistributor)\n external\n onlyGovernor\n {\n _setMerklDistributor(_merklDistributor);\n }\n\n function _setMerklDistributor(address _merklDistributor) internal {\n require(\n _merklDistributor != address(0),\n \"Invalid merklDistributor address\"\n );\n merklDistributor = _merklDistributor;\n emit MerklDistributorUpdated(_merklDistributor);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactoryMetropolis.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterMetropolis } from \"./PoolBoosterMetropolis.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Metropolis pool boosters.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactoryMetropolis is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n address public immutable rewardFactory;\n address public immutable voter;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n // @param address _rewardFactory address of the Metropolis reward factory\n // @param address _voter address of the Metropolis voter\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry,\n address _rewardFactory,\n address _voter\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {\n rewardFactory = _rewardFactory;\n voter = _voter;\n }\n\n /**\n * @dev Create a Pool Booster for Metropolis pool.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterMetropolis(address _ammPoolAddress, uint256 _salt)\n external\n onlyGovernor\n {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterMetropolis).creationCode,\n abi.encode(oSonic, rewardFactory, _ammPoolAddress, voter)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.MetropolisBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for Metropolis pool.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterMetropolis` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(address _ammPoolAddress, uint256 _salt)\n external\n view\n returns (address)\n {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterMetropolis).creationCode,\n abi.encode(oSonic, rewardFactory, _ammPoolAddress, voter)\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactorySwapxDouble.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterSwapxDouble } from \"./PoolBoosterSwapxDouble.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Swapx Ichi pool boosters where both of the\n * gauges need incentivizing.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactorySwapxDouble is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {}\n\n /**\n * @dev Create a Pool Booster for SwapX Ichi vault based pool where 2 Bribe contracts need to be\n * bribed\n * @param _bribeAddressOS address of the Bribes.sol(Bribe) contract for the OS token side\n * @param _bribeAddressOther address of the Bribes.sol(Bribe) contract for the other token in the pool\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _split 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n * bribe contract and 60% to other bribe contract\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterSwapxDouble(\n address _bribeAddressOS,\n address _bribeAddressOther,\n address _ammPoolAddress,\n uint256 _split,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterSwapxDouble).creationCode,\n abi.encode(_bribeAddressOS, _bribeAddressOther, oSonic, _split)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.SwapXDoubleBooster\n );\n }\n\n /**\n * @dev Compute the address of the pool booster to be deployed.\n * @param _bribeAddressOS address of the Bribes.sol(Bribe) contract for the OS token side\n * @param _bribeAddressOther address of the Bribes.sol(Bribe) contract for the other token in the pool\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _split 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n * bribe contract and 60% to other bribe contract\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterSwapxDouble` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n address _bribeAddressOS,\n address _bribeAddressOther,\n address _ammPoolAddress,\n uint256 _split,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterSwapxDouble).creationCode,\n abi.encode(\n _bribeAddressOS,\n _bribeAddressOther,\n oSonic,\n _split\n )\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactorySwapxSingle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterSwapxSingle } from \"./PoolBoosterSwapxSingle.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Swapx Single pool boosters - where a single\n * gauge is created for a pool. this is appropriate for Classic Stable & Classic\n * Volatile SwapX pools.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactorySwapxSingle is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {}\n\n /**\n * @dev Create a Pool Booster for SwapX classic volatile or classic stable pools where\n * a single Bribe contract is incentivized.\n * @param _bribeAddress address of the Bribes.sol contract\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterSwapxSingle(\n address _bribeAddress,\n address _ammPoolAddress,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterSwapxSingle).creationCode,\n abi.encode(_bribeAddress, oSonic)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.SwapXSingleBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for SwapX classic volatile or classic stable pools where\n * a single Bribe contract is incentivized.\n * @param _bribeAddress address of the Bribes.sol contract\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterSwapxSingle` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n address _bribeAddress,\n address _ammPoolAddress,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterSwapxSingle).creationCode,\n abi.encode(_bribeAddress, oSonic)\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterMerkl.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IMerklDistributor } from \"../interfaces/poolBooster/IMerklDistributor.sol\";\n\ninterface IERC1271 {\n /**\n * @dev Should return whether the signature provided is valid for the provided data\n * @param hash Hash of the data to be signed\n * @param signature Signature byte array associated with _data\n */\n function isValidSignature(bytes32 hash, bytes memory signature)\n external\n view\n returns (bytes4 magicValue);\n}\n\n/**\n * @title Pool booster for Merkl distributor\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterMerkl is IPoolBooster, IERC1271 {\n /// @notice address of merkl distributor\n IMerklDistributor public immutable merklDistributor;\n /// @notice address of the OS token\n IERC20 public immutable rewardToken;\n /// @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n /// @notice Campaign duration in seconds\n uint32 public immutable duration; // -> should be immutable\n /// @notice Campaign type\n uint32 public immutable campaignType;\n /// @notice Owner of the campaign\n address public immutable creator;\n /// @notice Campaign data\n bytes public campaignData;\n\n constructor(\n address _rewardToken,\n address _merklDistributor,\n uint32 _duration,\n uint32 _campaignType,\n address _creator,\n bytes memory _campaignData\n ) {\n require(_rewardToken != address(0), \"Invalid rewardToken address\");\n require(\n _merklDistributor != address(0),\n \"Invalid merklDistributor address\"\n );\n require(_campaignData.length > 0, \"Invalid campaignData\");\n require(_duration > 1 hours, \"Invalid duration\");\n\n campaignType = _campaignType;\n duration = _duration;\n creator = _creator;\n\n merklDistributor = IMerklDistributor(_merklDistributor);\n rewardToken = IERC20(_rewardToken);\n campaignData = _campaignData;\n }\n\n /// @notice Create a campaign on the Merkl distributor\n function bribe() external override {\n // Ensure token is approved for the Merkl distributor\n uint256 minAmount = merklDistributor.rewardTokenMinAmounts(\n address(rewardToken)\n );\n require(minAmount > 0, \"Min reward amount must be > 0\");\n\n // if balance too small or below threshold, do no bribes\n uint256 balance = rewardToken.balanceOf(address(this));\n if (\n balance < MIN_BRIBE_AMOUNT ||\n (balance * 1 hours < minAmount * duration)\n ) {\n return;\n }\n\n // Approve the bribe contract to spend the reward token\n rewardToken.approve(address(merklDistributor), balance);\n\n // Notify the bribe contract of the reward amount\n merklDistributor.signAndCreateCampaign(\n IMerklDistributor.CampaignParameters({\n campaignId: bytes32(0),\n creator: creator,\n rewardToken: address(rewardToken),\n amount: balance,\n campaignType: campaignType,\n startTimestamp: getNextPeriodStartTime(),\n duration: duration,\n campaignData: campaignData\n }),\n bytes(\"\")\n );\n emit BribeExecuted(balance);\n }\n\n /// @notice Used to sign a campaign on the Merkl distributor\n function isValidSignature(bytes32, bytes memory)\n external\n view\n override\n returns (bytes4 magicValue)\n {\n require(msg.sender == address(merklDistributor), \"Invalid sender\");\n // bytes4(keccak256(\"isValidSignature(bytes32,bytes)\")) == 0x1626ba7e\n return bytes4(0x1626ba7e);\n }\n\n /// @notice Returns the timestamp for the start of the next period based on the configured duration\n function getNextPeriodStartTime() public view returns (uint32) {\n // Calculate the timestamp for the next period boundary\n return uint32((block.timestamp / duration + 1) * duration);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterMetropolis.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @title Pool booster for Metropolis pools\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterMetropolis is IPoolBooster {\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice address of the pool\n address public immutable pool;\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n IRewarderFactory public immutable rewardFactory;\n\n IVoter public immutable voter;\n\n constructor(\n address _osToken,\n address _rewardFactory,\n address _pool,\n address _voter\n ) {\n require(_pool != address(0), \"Invalid pool address\");\n pool = _pool;\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n\n rewardFactory = IRewarderFactory(_rewardFactory);\n\n voter = IVoter(_voter);\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n (, uint256 minBribeAmount) = rewardFactory.getWhitelistedTokenInfo(\n address(osToken)\n );\n if (balance < MIN_BRIBE_AMOUNT || balance < minBribeAmount) {\n return;\n }\n\n uint256 id = voter.getCurrentVotingPeriod() + 1;\n\n // Deploy a rewarder\n IRewarder rewarder = IRewarder(\n rewardFactory.createBribeRewarder(address(osToken), pool)\n );\n\n // Approve the rewarder to spend the balance\n osToken.approve(address(rewarder), balance);\n\n // Fund and bribe the rewarder\n rewarder.fundAndBribe(id, id, balance);\n\n emit BribeExecuted(balance);\n }\n}\n\ninterface IRewarderFactory {\n function createBribeRewarder(address token, address pool)\n external\n returns (address rewarder);\n\n function getWhitelistedTokenInfo(address token)\n external\n view\n returns (bool isWhitelisted, uint256 minBribeAmount);\n}\n\ninterface IRewarder {\n function fundAndBribe(\n uint256 startId,\n uint256 lastId,\n uint256 amountPerPeriod\n ) external payable;\n}\n\ninterface IVoter {\n function getCurrentVotingPeriod() external view returns (uint256);\n}\n" + }, + "contracts/poolBooster/PoolBoosterSwapxDouble.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBribe } from \"../interfaces/poolBooster/ISwapXAlgebraBribe.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n/**\n * @title Pool booster for SwapX concentrated liquidity where 2 gauges are created for\n * every pool. Ichi vaults currently have such setup.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterSwapxDouble is IPoolBooster {\n using StableMath for uint256;\n\n // @notice address of the Bribes.sol(Bribe) contract for the OS token side\n IBribe public immutable bribeContractOS;\n // @notice address of the Bribes.sol(Bribe) contract for the other token in the pool\n IBribe public immutable bribeContractOther;\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n // bribe contract and 60% to other bribe contract\n uint256 public immutable split;\n\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n constructor(\n address _bribeContractOS,\n address _bribeContractOther,\n address _osToken,\n uint256 _split\n ) {\n require(\n _bribeContractOS != address(0),\n \"Invalid bribeContractOS address\"\n );\n require(\n _bribeContractOther != address(0),\n \"Invalid bribeContractOther address\"\n );\n // expect it to be between 1% & 99%\n require(_split > 1e16 && _split < 99e16, \"Unexpected split amount\");\n\n bribeContractOS = IBribe(_bribeContractOS);\n bribeContractOther = IBribe(_bribeContractOther);\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n split = _split;\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n if (balance < MIN_BRIBE_AMOUNT) {\n return;\n }\n\n uint256 osBribeAmount = balance.mulTruncate(split);\n uint256 otherBribeAmount = balance - osBribeAmount;\n\n osToken.approve(address(bribeContractOS), osBribeAmount);\n osToken.approve(address(bribeContractOther), otherBribeAmount);\n\n bribeContractOS.notifyRewardAmount(address(osToken), osBribeAmount);\n bribeContractOther.notifyRewardAmount(\n address(osToken),\n otherBribeAmount\n );\n\n emit BribeExecuted(balance);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterSwapxSingle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBribe } from \"../interfaces/poolBooster/ISwapXAlgebraBribe.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @title Pool booster for SwapX for Classic Stable Pools and Classic Volatile Pools\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterSwapxSingle is IPoolBooster {\n // @notice address of the Bribes.sol(Bribe) contract\n IBribe public immutable bribeContract;\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n constructor(address _bribeContract, address _osToken) {\n require(_bribeContract != address(0), \"Invalid bribeContract address\");\n bribeContract = IBribe(_bribeContract);\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n if (balance < MIN_BRIBE_AMOUNT) {\n return;\n }\n\n osToken.approve(address(bribeContract), balance);\n\n bribeContract.notifyRewardAmount(address(osToken), balance);\n emit BribeExecuted(balance);\n }\n}\n" + }, + "contracts/proxies/BaseProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice BridgedBaseWOETHProxy delegates calls to BridgedWOETH implementation\n */\ncontract BridgedBaseWOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseVaultProxy delegates calls to OETHBaseVault implementation\n */\ncontract OETHBaseVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseProxy delegates calls to OETH implementation\n */\ncontract OETHBaseProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHBaseProxy delegates calls to WOETH implementation\n */\ncontract WOETHBaseProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseDripperProxy delegates calls to a FixedRateDripper implementation\n */\ncontract OETHBaseDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice AerodromeAMOStrategyProxy delegates calls to AerodromeAMOStrategy implementation\n */\ncontract AerodromeAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BridgedWOETHStrategyProxy delegates calls to BridgedWOETHStrategy implementation\n */\ncontract BridgedWOETHStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseHarvesterProxy delegates calls to a SuperOETHHarvester implementation\n */\ncontract OETHBaseHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseCurveAMOProxy delegates calls to a OETHBaseCurveAMO implementation\n */\ncontract OETHBaseCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/proxies/InitializeGovernedUpgradeabilityProxy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { Governable } from \"../governance/Governable.sol\";\n\n/**\n * @title BaseGovernedUpgradeabilityProxy\n * @dev This contract combines an upgradeability proxy with our governor system.\n * It is based on an older version of OpenZeppelins BaseUpgradeabilityProxy\n * with Solidity ^0.8.0.\n * @author Origin Protocol Inc\n */\ncontract InitializeGovernedUpgradeabilityProxy is Governable {\n /**\n * @dev Emitted when the implementation is upgraded.\n * @param implementation Address of the new implementation.\n */\n event Upgraded(address indexed implementation);\n\n constructor() {\n _setGovernor(msg.sender);\n }\n\n /**\n * @dev Contract initializer with Governor enforcement\n * @param _logic Address of the initial implementation.\n * @param _initGovernor Address of the initial Governor.\n * @param _data Data to send as msg.data to the implementation to initialize\n * the proxied contract.\n * It should include the signature and the parameters of the function to be\n * called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n * This parameter is optional, if no data is given the initialization call\n * to proxied contract will be skipped.\n */\n function initialize(\n address _logic,\n address _initGovernor,\n bytes calldata _data\n ) public payable onlyGovernor {\n require(_implementation() == address(0));\n require(_logic != address(0), \"Implementation not set\");\n assert(\n IMPLEMENTATION_SLOT ==\n bytes32(uint256(keccak256(\"eip1967.proxy.implementation\")) - 1)\n );\n _setImplementation(_logic);\n if (_data.length > 0) {\n (bool success, ) = _logic.delegatecall(_data);\n require(success);\n }\n _changeGovernor(_initGovernor);\n }\n\n /**\n * @return The address of the proxy admin/it's also the governor.\n */\n function admin() external view returns (address) {\n return _governor();\n }\n\n /**\n * @return The address of the implementation.\n */\n function implementation() external view returns (address) {\n return _implementation();\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy.\n * Only the admin can call this function.\n * @param _newImplementation Address of the new implementation.\n */\n function upgradeTo(address _newImplementation) external onlyGovernor {\n _upgradeTo(_newImplementation);\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy and call a function\n * on the new implementation.\n * This is useful to initialize the proxied contract.\n * @param newImplementation Address of the new implementation.\n * @param data Data to send as msg.data in the low level call.\n * It should include the signature and the parameters of the function to be called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n */\n function upgradeToAndCall(address newImplementation, bytes calldata data)\n external\n payable\n onlyGovernor\n {\n _upgradeTo(newImplementation);\n (bool success, ) = newImplementation.delegatecall(data);\n require(success);\n }\n\n /**\n * @dev Fallback function.\n * Implemented entirely in `_fallback`.\n */\n fallback() external payable {\n _fallback();\n }\n\n /**\n * @dev Delegates execution to an implementation contract.\n * This is a low level function that doesn't return to its internal call site.\n * It will return to the external caller whatever the implementation returns.\n * @param _impl Address to delegate.\n */\n function _delegate(address _impl) internal {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(gas(), _impl, 0, calldatasize(), 0, 0)\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n /**\n * @dev Function that is run as the first thing in the fallback function.\n * Can be redefined in derived contracts to add functionality.\n * Redefinitions must call super._willFallback().\n */\n function _willFallback() internal {}\n\n /**\n * @dev fallback implementation.\n * Extracted to enable manual triggering.\n */\n function _fallback() internal {\n _willFallback();\n _delegate(_implementation());\n }\n\n /**\n * @dev Storage slot with the address of the current implementation.\n * This is the keccak-256 hash of \"eip1967.proxy.implementation\" subtracted by 1, and is\n * validated in the constructor.\n */\n bytes32 internal constant IMPLEMENTATION_SLOT =\n 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n /**\n * @dev Returns the current implementation.\n * @return impl Address of the current implementation\n */\n function _implementation() internal view returns (address impl) {\n bytes32 slot = IMPLEMENTATION_SLOT;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n impl := sload(slot)\n }\n }\n\n /**\n * @dev Upgrades the proxy to a new implementation.\n * @param newImplementation Address of the new implementation.\n */\n function _upgradeTo(address newImplementation) internal {\n _setImplementation(newImplementation);\n emit Upgraded(newImplementation);\n }\n\n /**\n * @dev Sets the implementation address of the proxy.\n * @param newImplementation Address of the new implementation.\n */\n function _setImplementation(address newImplementation) internal {\n require(\n Address.isContract(newImplementation),\n \"Cannot set a proxy implementation to a non-contract address\"\n );\n\n bytes32 slot = IMPLEMENTATION_SLOT;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(slot, newImplementation)\n }\n }\n}\n" + }, + "contracts/proxies/PlumeProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OETHPlumeVaultProxy delegates calls to OETHPlumeVault implementation\n */\ncontract OETHPlumeVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHPlumeProxy delegates calls to OETH implementation\n */\ncontract OETHPlumeProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHPlumeProxy delegates calls to WOETH implementation\n */\ncontract WOETHPlumeProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice RoosterAMOStrategyProxy delegates calls to a RoosterAMOStrategy implementation\n */\ncontract RoosterAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/proxies/Proxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OUSDProxy delegates calls to an OUSD implementation\n */\ncontract OUSDProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WrappedOUSDProxy delegates calls to a WrappedOUSD implementation\n */\ncontract WrappedOUSDProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice VaultProxy delegates calls to a Vault implementation\n */\ncontract VaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CompoundStrategyProxy delegates calls to a CompoundStrategy implementation\n */\ncontract CompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice AaveStrategyProxy delegates calls to a AaveStrategy implementation\n */\ncontract AaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ConvexStrategyProxy delegates calls to a ConvexStrategy implementation\n */\ncontract ConvexStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice HarvesterProxy delegates calls to a Harvester implementation\n */\ncontract HarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice DripperProxy delegates calls to a Dripper implementation\n */\ncontract DripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoCompoundStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\n */\ncontract MorphoCompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ConvexOUSDMetaStrategyProxy delegates calls to a ConvexOUSDMetaStrategy implementation\n */\ncontract ConvexOUSDMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoAaveStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\n */\ncontract MorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHProxy delegates calls to nowhere for now\n */\ncontract OETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHProxy delegates calls to nowhere for now\n */\ncontract WOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHVaultProxy delegates calls to a Vault implementation\n */\ncontract OETHVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHDripperProxy delegates calls to a OETHDripper implementation\n */\ncontract OETHDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHHarvesterProxy delegates calls to a Harvester implementation\n */\ncontract OETHHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CurveEthStrategyProxy delegates calls to a CurveEthStrategy implementation\n */\ncontract ConvexEthMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BuybackProxy delegates calls to Buyback implementation\n */\ncontract BuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHMorphoAaveStrategyProxy delegates calls to a MorphoAaveStrategy implementation\n */\ncontract OETHMorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBalancerMetaPoolrEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\n */\ncontract OETHBalancerMetaPoolrEthStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice OETHBalancerMetaPoolwstEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\n */\ncontract OETHBalancerMetaPoolwstEthStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MakerDsrStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MakerDsrStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBuybackProxy delegates calls to Buyback implementation\n */\ncontract OETHBuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BridgedWOETHProxy delegates calls to BridgedWOETH implementation\n */\ncontract BridgedWOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice NativeStakingSSVStrategyProxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulatorProxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulatorProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingSSVStrategy2Proxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategy2Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulator2Proxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulator2Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingSSVStrategy3Proxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategy3Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulator3Proxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulator3Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MetaMorphoStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MetaMorphoStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ARMBuybackProxy delegates calls to Buyback implementation\n */\ncontract ARMBuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoGauntletPrimeUSDCStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MorphoGauntletPrimeUSDCStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MorphoGauntletPrimeUSDTStrategyProxy delegates calls to a Generalized4626USDTStrategy implementation\n */\ncontract MorphoGauntletPrimeUSDTStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice CurvePoolBoosterProxy delegates calls to a CurvePoolBooster implementation\n */\ncontract CurvePoolBoosterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHFixedRateDripperProxy delegates calls to a OETHFixedRateDripper implementation\n */\ncontract OETHFixedRateDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHSimpleHarvesterProxy delegates calls to a OETHSimpleHarvester implementation\n */\ncontract OETHSimpleHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice PoolBoostCentralRegistryProxy delegates calls to the PoolBoostCentralRegistry implementation\n */\ncontract PoolBoostCentralRegistryProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MakerSSRStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MakerSSRStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OUSDCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\n */\ncontract OUSDCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\n */\ncontract OETHCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CompoundingStakingSSVStrategyProxy delegates calls to a CompoundingStakingSSVStrategy implementation\n */\ncontract CompoundingStakingSSVStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n" + }, + "contracts/proxies/SonicProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OSonicVaultProxy delegates calls to OSonicVault implementation\n */\ncontract OSonicVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicProxy delegates calls to OSonic implementation\n */\ncontract OSonicProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOSonicProxy delegates calls to WOSonic implementation\n */\ncontract WOSonicProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicDripperProxy delegates calls to a FixedRateDripper implementation\n */\ncontract OSonicDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice SonicStakingStrategyProxy delegates calls to SonicStakingStrategy implementation\n */\ncontract SonicStakingStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicHarvesterProxy delegates calls to a OSonicHarvester implementation\n */\ncontract OSonicHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice SonicSwapXAMOStrategyProxy delegates calls to a SonicSwapXAMOStrategy implementation\n */\ncontract SonicSwapXAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/strategies/AaveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Aave Strategy\n * @notice Investment strategy for investing stablecoins via Aave\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport \"./IAave.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\nimport { IAaveStakedToken } from \"./IAaveStakeToken.sol\";\nimport { IAaveIncentivesController } from \"./IAaveIncentivesController.sol\";\n\ncontract AaveStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n\n uint16 constant referralCode = 92;\n\n IAaveIncentivesController public incentivesController;\n IAaveStakedToken public stkAave;\n\n /**\n * @param _stratConfig The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as AAVE needs several extra\n * addresses for the rewards program.\n * @param _rewardTokenAddresses Address of the AAVE token\n * @param _assets Addresses of supported assets\n * @param _pTokens Platform Token corresponding addresses\n * @param _incentivesAddress Address of the AAVE incentives controller\n * @param _stkAaveAddress Address of the stkAave contract\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // AAVE\n address[] calldata _assets,\n address[] calldata _pTokens,\n address _incentivesAddress,\n address _stkAaveAddress\n ) external onlyGovernor initializer {\n incentivesController = IAaveIncentivesController(_incentivesAddress);\n stkAave = IAaveStakedToken(_stkAaveAddress);\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Deposit asset into Aave\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Aave\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n // Following line also doubles as a check that we are depositing\n // an asset that we support.\n emit Deposit(_asset, _getATokenFor(_asset), _amount);\n _getLendingPool().deposit(_asset, _amount, address(this), referralCode);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Aave\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Aave\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n emit Withdrawal(_asset, _getATokenFor(_asset), _amount);\n uint256 actual = _getLendingPool().withdraw(\n _asset,\n _amount,\n address(this)\n );\n require(actual == _amount, \"Did not withdraw enough\");\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n // Redeem entire balance of aToken\n IERC20 asset = IERC20(assetsMapped[i]);\n address aToken = _getATokenFor(assetsMapped[i]);\n uint256 balance = IERC20(aToken).balanceOf(address(this));\n if (balance > 0) {\n uint256 actual = _getLendingPool().withdraw(\n address(asset),\n balance,\n address(this)\n );\n require(actual == balance, \"Did not withdraw enough\");\n\n uint256 assetBalance = asset.balanceOf(address(this));\n // Transfer entire balance to Vault\n asset.safeTransfer(vaultAddress, assetBalance);\n\n emit Withdrawal(address(asset), aToken, assetBalance);\n }\n }\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n // Balance is always with token aToken decimals\n address aToken = _getATokenFor(_asset);\n balance = IERC20(aToken).balanceOf(address(this));\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Approve the spending of all assets by their corresponding aToken,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n address lendingPool = address(_getLendingPool());\n // approve the pool to spend the Asset\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n address asset = assetsMapped[i];\n // Safe approval\n IERC20(asset).safeApprove(lendingPool, 0);\n IERC20(asset).safeApprove(lendingPool, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / aTokens\n We need to give the AAVE lending pool approval to transfer the\n asset.\n * @param _asset Address of the asset to approve\n * @param _aToken Address of the aToken\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _aToken)\n internal\n override\n {\n address lendingPool = address(_getLendingPool());\n IERC20(_asset).safeApprove(lendingPool, 0);\n IERC20(_asset).safeApprove(lendingPool, type(uint256).max);\n }\n\n /**\n * @dev Get the aToken wrapped in the IERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return Corresponding aToken to this asset\n */\n function _getATokenFor(address _asset) internal view returns (address) {\n address aToken = assetToPToken[_asset];\n require(aToken != address(0), \"aToken does not exist\");\n return aToken;\n }\n\n /**\n * @dev Get the current address of the Aave lending pool, which is the gateway to\n * depositing.\n * @return Current lending pool implementation\n */\n function _getLendingPool() internal view returns (IAaveLendingPool) {\n address lendingPool = ILendingPoolAddressesProvider(platformAddress)\n .getLendingPool();\n require(lendingPool != address(0), \"Lending pool does not exist\");\n return IAaveLendingPool(lendingPool);\n }\n\n /**\n * @dev Collect stkAave, convert it to AAVE send to Vault.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n if (address(stkAave) == address(0)) {\n return;\n }\n\n // Check staked AAVE cooldown timer\n uint256 cooldown = stkAave.stakersCooldowns(address(this));\n uint256 windowStart = cooldown + stkAave.COOLDOWN_SECONDS();\n uint256 windowEnd = windowStart + stkAave.UNSTAKE_WINDOW();\n\n // If inside the unlock window, then we can redeem stkAave\n // for AAVE and send it to the vault.\n if (block.timestamp > windowStart && block.timestamp <= windowEnd) {\n // Redeem to AAVE\n uint256 stkAaveBalance = stkAave.balanceOf(address(this));\n stkAave.redeem(address(this), stkAaveBalance);\n\n // Transfer AAVE to harvesterAddress\n uint256 aaveBalance = IERC20(rewardTokenAddresses[0]).balanceOf(\n address(this)\n );\n if (aaveBalance > 0) {\n IERC20(rewardTokenAddresses[0]).safeTransfer(\n harvesterAddress,\n aaveBalance\n );\n }\n }\n\n // Collect available rewards and restart the cooldown timer, if either of\n // those should be run.\n if (block.timestamp > windowStart || cooldown == 0) {\n uint256 assetsLen = assetsMapped.length;\n // aToken addresses for incentives controller\n address[] memory aTokens = new address[](assetsLen);\n for (uint256 i = 0; i < assetsLen; ++i) {\n aTokens[i] = _getATokenFor(assetsMapped[i]);\n }\n\n // 1. If we have rewards availabile, collect them\n uint256 pendingRewards = incentivesController.getRewardsBalance(\n aTokens,\n address(this)\n );\n if (pendingRewards > 0) {\n // Because getting more stkAAVE from the incentives controller\n // with claimRewards() may push the stkAAVE cooldown time\n // forward, it is called after stakedAAVE has been turned into\n // AAVE.\n uint256 collected = incentivesController.claimRewards(\n aTokens,\n pendingRewards,\n address(this)\n );\n require(collected == pendingRewards, \"AAVE reward difference\");\n }\n\n // 2. Start cooldown counting down.\n if (stkAave.balanceOf(address(this)) > 0) {\n // Protected with if since cooldown call would revert\n // if no stkAave balance.\n stkAave.cooldown();\n }\n }\n }\n}\n" + }, + "contracts/strategies/AbstractCompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base Compound Abstract Strategy\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { ICERC20 } from \"./ICompound.sol\";\nimport { IComptroller } from \"../interfaces/IComptroller.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\nabstract contract AbstractCompoundStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n\n int256[50] private __reserved;\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Get the cToken wrapped in the ICERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return Corresponding cToken to this asset\n */\n function _getCTokenFor(address _asset) internal view returns (ICERC20) {\n address cToken = assetToPToken[_asset];\n require(cToken != address(0), \"cToken does not exist\");\n return ICERC20(cToken);\n }\n\n /**\n * @dev Converts an underlying amount into cToken amount\n * cTokenAmt = (underlying * 1e18) / exchangeRate\n * @param _cToken cToken for which to change\n * @param _underlying Amount of underlying to convert\n * @return amount Equivalent amount of cTokens\n */\n function _convertUnderlyingToCToken(ICERC20 _cToken, uint256 _underlying)\n internal\n view\n returns (uint256 amount)\n {\n // e.g. 1e18*1e18 / 205316390724364402565641705 = 50e8\n // e.g. 1e8*1e18 / 205316390724364402565641705 = 0.45 or 0\n amount = (_underlying * 1e18) / _cToken.exchangeRateStored();\n }\n}\n" + }, + "contracts/strategies/AbstractConvexMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { ICurveMetaPool } from \"./ICurveMetaPool.sol\";\nimport { IERC20, AbstractCurveStrategy, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\nabstract contract AbstractConvexMetaStrategy is AbstractCurveStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n event MaxWithdrawalSlippageUpdated(\n uint256 _prevMaxSlippagePercentage,\n uint256 _newMaxSlippagePercentage\n );\n\n // used to circumvent the stack too deep issue\n struct InitConfig {\n address cvxDepositorAddress; //Address of the Convex depositor(AKA booster) for this pool\n address metapoolAddress; //Address of the Curve MetaPool\n address metapoolMainToken; //Address of Main metapool token\n address cvxRewardStakerAddress; //Address of the CVX rewards staker\n address metapoolLPToken; //Address of metapool LP token\n uint256 cvxDepositorPTokenId; //Pid of the pool referred to by Depositor and staker\n }\n\n address internal cvxDepositorAddress;\n address internal cvxRewardStakerAddress;\n uint256 internal cvxDepositorPTokenId;\n ICurveMetaPool internal metapool;\n IERC20 internal metapoolMainToken;\n IERC20 internal metapoolLPToken;\n // Ordered list of metapool assets\n address[] internal metapoolAssets;\n // Max withdrawal slippage denominated in 1e18 (1e18 == 100%)\n uint256 public maxWithdrawalSlippage;\n uint128 internal crvCoinIndex;\n uint128 internal mainCoinIndex;\n\n int256[41] private ___reserved;\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * DAI, USDC, USDT\n * @param _pTokens Platform Token corresponding addresses\n * @param initConfig Various addresses and info for initialization state\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets,\n address[] calldata _pTokens,\n InitConfig calldata initConfig\n ) external onlyGovernor initializer {\n require(_assets.length == 3, \"Must have exactly three assets\");\n // Should be set prior to abstract initialize call otherwise\n // abstractSetPToken calls will fail\n cvxDepositorAddress = initConfig.cvxDepositorAddress;\n pTokenAddress = _pTokens[0];\n metapool = ICurveMetaPool(initConfig.metapoolAddress);\n metapoolMainToken = IERC20(initConfig.metapoolMainToken);\n cvxRewardStakerAddress = initConfig.cvxRewardStakerAddress;\n metapoolLPToken = IERC20(initConfig.metapoolLPToken);\n cvxDepositorPTokenId = initConfig.cvxDepositorPTokenId;\n maxWithdrawalSlippage = 1e16;\n\n metapoolAssets = [metapool.coins(0), metapool.coins(1)];\n crvCoinIndex = _getMetapoolCoinIndex(pTokenAddress);\n mainCoinIndex = _getMetapoolCoinIndex(initConfig.metapoolMainToken);\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n balance = 0;\n\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 contractPTokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (contractPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = contractPTokens.mulTruncate(virtual_price);\n balance += value;\n }\n\n /* We intentionally omit the metapoolLp tokens held by the metastrategyContract\n * since the contract should never (except in the middle of deposit/withdrawal\n * transaction) hold any amount of those tokens in normal operation. There\n * could be tokens sent to it by a 3rd party and we decide to actively ignore\n * those.\n */\n uint256 metapoolGaugePTokens = IRewardStaking(cvxRewardStakerAddress)\n .balanceOf(address(this));\n\n if (metapoolGaugePTokens > 0) {\n uint256 value = metapoolGaugePTokens.mulTruncate(\n metapool.get_virtual_price()\n );\n balance += value;\n }\n\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = balance.scaleBy(assetDecimals, 18) / THREEPOOL_ASSET_COUNT;\n }\n\n /**\n * @dev This function is completely analogous to _calcCurveTokenAmount[AbstractCurveStrategy]\n * and just utilizes different Curve (meta)pool API\n */\n function _calcCurveMetaTokenAmount(uint128 _coinIndex, uint256 _amount)\n internal\n returns (uint256 requiredMetapoolLP)\n {\n uint256[2] memory _amounts = [uint256(0), uint256(0)];\n _amounts[uint256(_coinIndex)] = _amount;\n\n // LP required when removing required asset ignoring fees\n uint256 lpRequiredNoFees = metapool.calc_token_amount(_amounts, false);\n /* LP required if fees would apply to entirety of removed amount\n *\n * fee is 1e10 denominated number: https://curve.readthedocs.io/exchange-pools.html#StableSwap.fee\n */\n uint256 lpRequiredFullFees = lpRequiredNoFees.mulTruncateScale(\n 1e10 + metapool.fee(),\n 1e10\n );\n\n /* asset received when withdrawing full fee applicable LP accounting for\n * slippage and fees\n */\n uint256 assetReceivedForFullLPFees = metapool.calc_withdraw_one_coin(\n lpRequiredFullFees,\n int128(_coinIndex)\n );\n\n // exact amount of LP required\n requiredMetapoolLP =\n (lpRequiredFullFees * _amount) /\n assetReceivedForFullLPFees;\n }\n\n function _approveBase() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // 3Pool for LP token (required for removing liquidity)\n pToken.safeApprove(platformAddress, 0);\n pToken.safeApprove(platformAddress, type(uint256).max);\n // Gauge for LP token\n metapoolLPToken.safeApprove(cvxDepositorAddress, 0);\n metapoolLPToken.safeApprove(cvxDepositorAddress, type(uint256).max);\n // Metapool for LP token\n pToken.safeApprove(address(metapool), 0);\n pToken.safeApprove(address(metapool), type(uint256).max);\n // Metapool for Metapool main token\n metapoolMainToken.safeApprove(address(metapool), 0);\n metapoolMainToken.safeApprove(address(metapool), type(uint256).max);\n }\n\n /**\n * @dev Get the index of the coin\n */\n function _getMetapoolCoinIndex(address _asset)\n internal\n view\n returns (uint128)\n {\n for (uint128 i = 0; i < 2; i++) {\n if (metapoolAssets[i] == _asset) return i;\n }\n revert(\"Invalid Metapool asset\");\n }\n\n /**\n * @dev Sets max withdrawal slippage that is considered when removing\n * liquidity from Metapools.\n * @param _maxWithdrawalSlippage Max withdrawal slippage denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxWithdrawalSlippage should actually be 0.1% (1e15)\n * for production usage. Contract allows as low value as 0% for confirming\n * correct behavior in test suite.\n */\n function setMaxWithdrawalSlippage(uint256 _maxWithdrawalSlippage)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(\n _maxWithdrawalSlippage <= 1e18,\n \"Max withdrawal slippage needs to be between 0% - 100%\"\n );\n emit MaxWithdrawalSlippageUpdated(\n maxWithdrawalSlippage,\n _maxWithdrawalSlippage\n );\n maxWithdrawalSlippage = _maxWithdrawalSlippage;\n }\n\n /**\n * @dev Collect accumulated CRV and CVX and send to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n IRewardStaking(cvxRewardStakerAddress).getReward();\n _collectRewardTokens();\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/AbstractCurveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve 3Pool Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\nabstract contract AbstractCurveStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 internal constant MAX_SLIPPAGE = 1e16; // 1%, same as the Curve UI\n // number of assets in Curve 3Pool (USDC, DAI, USDT)\n uint256 internal constant THREEPOOL_ASSET_COUNT = 3;\n address internal pTokenAddress;\n\n int256[49] private __reserved;\n\n /**\n * @dev Deposit asset into the Curve 3Pool\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_amount > 0, \"Must deposit something\");\n emit Deposit(_asset, pTokenAddress, _amount);\n\n // 3Pool requires passing deposit amounts for all 3 assets, set to 0 for\n // all\n uint256[3] memory _amounts;\n uint256 poolCoinIndex = _getCoinIndex(_asset);\n // Set the amount on the asset we want to deposit\n _amounts[poolCoinIndex] = _amount;\n ICurvePool curvePool = ICurvePool(platformAddress);\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n uint256 depositValue = _amount.scaleBy(18, assetDecimals).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = depositValue.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n // Do the deposit to 3pool\n curvePool.add_liquidity(_amounts, minMintAmount);\n _lpDepositAll();\n }\n\n function _lpDepositAll() internal virtual;\n\n /**\n * @dev Deposit the entire balance of any supported asset into the Curve 3pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n uint256 depositValue = 0;\n ICurvePool curvePool = ICurvePool(platformAddress);\n uint256 curveVirtualPrice = curvePool.get_virtual_price();\n\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address assetAddress = assetsMapped[i];\n uint256 balance = IERC20(assetAddress).balanceOf(address(this));\n if (balance > 0) {\n uint256 poolCoinIndex = _getCoinIndex(assetAddress);\n // Set the amount on the asset we want to deposit\n _amounts[poolCoinIndex] = balance;\n uint256 assetDecimals = Helpers.getDecimals(assetAddress);\n // Get value of deposit in Curve LP token to later determine\n // the minMintAmount argument for add_liquidity\n depositValue =\n depositValue +\n balance.scaleBy(18, assetDecimals).divPrecisely(\n curveVirtualPrice\n );\n emit Deposit(assetAddress, pTokenAddress, balance);\n }\n }\n\n uint256 minMintAmount = depositValue.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n // Do the deposit to 3pool\n curvePool.add_liquidity(_amounts, minMintAmount);\n\n /* In case of Curve Strategy all assets are mapped to the same pToken (3CrvLP). Let\n * descendants further handle the pToken. By either deploying it to the metapool and\n * resulting tokens in Gauge. Or deploying pTokens directly to the Gauge.\n */\n _lpDepositAll();\n }\n\n function _lpWithdraw(uint256 numCrvTokens) internal virtual;\n\n function _lpWithdrawAll() internal virtual;\n\n /**\n * @dev Withdraw asset from Curve 3Pool\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Invalid amount\");\n\n emit Withdrawal(_asset, pTokenAddress, _amount);\n\n uint256 contractCrv3Tokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n\n uint256 coinIndex = _getCoinIndex(_asset);\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 requiredCrv3Tokens = _calcCurveTokenAmount(coinIndex, _amount);\n\n // We have enough LP tokens, make sure they are all on this contract\n if (contractCrv3Tokens < requiredCrv3Tokens) {\n _lpWithdraw(requiredCrv3Tokens - contractCrv3Tokens);\n }\n\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n _amounts[coinIndex] = _amount;\n\n curvePool.remove_liquidity_imbalance(_amounts, requiredCrv3Tokens);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Calculate amount of LP required when withdrawing specific amount of one\n * of the underlying assets accounting for fees and slippage.\n *\n * Curve pools unfortunately do not contain a calculation function for\n * amount of LP required when withdrawing a specific amount of one of the\n * underlying tokens and also accounting for fees (Curve's calc_token_amount\n * does account for slippage but not fees).\n *\n * Steps taken to calculate the metric:\n * - get amount of LP required if fees wouldn't apply\n * - increase the LP amount as if fees would apply to the entirety of the underlying\n * asset withdrawal. (when withdrawing only one coin fees apply only to amounts\n * of other assets pool would return in case of balanced removal - since those need\n * to be swapped for the single underlying asset being withdrawn)\n * - get amount of underlying asset withdrawn (this Curve function does consider slippage\n * and fees) when using the increased LP amount. As LP amount is slightly over-increased\n * so is amount of underlying assets returned.\n * - since we know exactly how much asset we require take the rate of LP required for asset\n * withdrawn to get the exact amount of LP.\n */\n function _calcCurveTokenAmount(uint256 _coinIndex, uint256 _amount)\n internal\n returns (uint256 required3Crv)\n {\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n _amounts[_coinIndex] = _amount;\n\n // LP required when removing required asset ignoring fees\n uint256 lpRequiredNoFees = curvePool.calc_token_amount(_amounts, false);\n /* LP required if fees would apply to entirety of removed amount\n *\n * fee is 1e10 denominated number: https://curve.readthedocs.io/exchange-pools.html#StableSwap.fee\n */\n uint256 lpRequiredFullFees = lpRequiredNoFees.mulTruncateScale(\n 1e10 + curvePool.fee(),\n 1e10\n );\n\n /* asset received when withdrawing full fee applicable LP accounting for\n * slippage and fees\n */\n uint256 assetReceivedForFullLPFees = curvePool.calc_withdraw_one_coin(\n lpRequiredFullFees,\n int128(uint128(_coinIndex))\n );\n\n // exact amount of LP required\n required3Crv =\n (lpRequiredFullFees * _amount) /\n assetReceivedForFullLPFees;\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n _lpWithdrawAll();\n // Withdraws are proportional to assets held by 3Pool\n uint256[3] memory minWithdrawAmounts = [\n uint256(0),\n uint256(0),\n uint256(0)\n ];\n\n // Remove liquidity\n ICurvePool threePool = ICurvePool(platformAddress);\n threePool.remove_liquidity(\n IERC20(pTokenAddress).balanceOf(address(this)),\n minWithdrawAmounts\n );\n // Transfer assets out of Vault\n // Note that Curve will provide all 3 of the assets in 3pool even if\n // we have not set PToken addresses for all of them in this strategy\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n IERC20 asset = IERC20(threePool.coins(i));\n asset.safeTransfer(vaultAddress, asset.balanceOf(address(this)));\n }\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 totalPTokens = IERC20(pTokenAddress).balanceOf(address(this));\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (totalPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = (totalPTokens * virtual_price) / 1e18;\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = value.scaleBy(assetDecimals, 18) / THREEPOOL_ASSET_COUNT;\n }\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n // This strategy is a special case since it only supports one asset\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n _approveAsset(assetsMapped[i]);\n }\n }\n\n /**\n * @dev Call the necessary approvals for the Curve pool and gauge\n * @param _asset Address of the asset\n */\n function _abstractSetPToken(address _asset, address) internal override {\n _approveAsset(_asset);\n }\n\n function _approveAsset(address _asset) internal {\n IERC20 asset = IERC20(_asset);\n // 3Pool for asset (required for adding liquidity)\n asset.safeApprove(platformAddress, 0);\n asset.safeApprove(platformAddress, type(uint256).max);\n }\n\n function _approveBase() internal virtual;\n\n /**\n * @dev Get the index of the coin\n */\n function _getCoinIndex(address _asset) internal view returns (uint256) {\n for (uint256 i = 0; i < 3; i++) {\n if (assetsMapped[i] == _asset) return i;\n }\n revert(\"Invalid 3pool asset\");\n }\n}\n" + }, + "contracts/strategies/aerodrome/AerodromeAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Aerodrome AMO strategy\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nimport { ISugarHelper } from \"../../interfaces/aerodrome/ISugarHelper.sol\";\nimport { INonfungiblePositionManager } from \"../../interfaces/aerodrome/INonfungiblePositionManager.sol\";\nimport { ISwapRouter } from \"../../interfaces/aerodrome/ISwapRouter.sol\";\nimport { ICLPool } from \"../../interfaces/aerodrome/ICLPool.sol\";\nimport { ICLGauge } from \"../../interfaces/aerodrome/ICLGauge.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\n\ncontract AerodromeAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n using SafeCast for uint256;\n\n /************************************************\n Important (!) setup configuration\n *************************************************/\n\n /**\n * In order to be able to remove a reasonable amount of complexity from the contract one of the\n * preconditions for this contract to function correctly is to have an outside account mint a small\n * amount of liquidity in the tick space where the contract will deploy's its liquidity and then send\n * that NFT LP position to a dead address (transfer to zero address not allowed.) See example of such\n * NFT LP token:\n * https://basescan.org/token/0x827922686190790b37229fd06084350e74485b72?a=413296#inventory\n */\n\n /***************************************\n Storage slot members\n ****************************************/\n\n /// @notice tokenId of the liquidity position\n uint256 public tokenId;\n /// @dev Minimum amount of tokens the strategy would be able to withdraw from the pool.\n /// minimum amount of tokens are withdrawn at a 1:1 price\n uint256 public underlyingAssets;\n /// @notice Marks the start of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareStart;\n /// @notice Marks the end of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareEnd;\n /// @dev reserved for inheritance\n int256[46] private __reserved;\n\n /***************************************\n Constants, structs and events\n ****************************************/\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the OETHb token contract\n address public immutable OETHb;\n /// @notice lower tick set to -1 representing the price of 1.0001 of WETH for 1 OETHb.\n int24 public immutable lowerTick;\n /// @notice lower tick set to 0 representing the price of 1.0000 of WETH for 1 OETHb.\n int24 public immutable upperTick;\n /// @notice tick spacing of the pool (set to 1)\n int24 public immutable tickSpacing;\n /// @notice the swapRouter for performing swaps\n ISwapRouter public immutable swapRouter;\n /// @notice the underlying AMO Slipstream pool\n ICLPool public immutable clPool;\n /// @notice the gauge for the corresponding Slipstream pool (clPool)\n /// @dev can become an immutable once the gauge is created on the base main-net\n ICLGauge public immutable clGauge;\n /// @notice the Position manager contract that is used to manage the pool's position\n INonfungiblePositionManager public immutable positionManager;\n /// @notice helper contract for liquidity and ticker math\n ISugarHelper public immutable helper;\n /// @notice sqrtRatioX96TickLower\n /// @dev tick lower has value -1 and represents the lowest price of WETH priced in OETHb. Meaning the pool\n /// offers less than 1 OETHb for 1 WETH. In other terms to get 1 OETHB the swap needs to offer 1.0001 WETH\n /// this is where purchasing OETHb with WETH within the liquidity position is most expensive\n uint160 public immutable sqrtRatioX96TickLower;\n /// @notice sqrtRatioX96TickHigher\n /// @dev tick higher has value 0 and represents 1:1 price parity of WETH to OETHb\n uint160 public immutable sqrtRatioX96TickHigher;\n /// @dev tick closest to 1:1 price parity\n /// Correctly assessing which tick is closer to 1:1 price parity is important since it affects\n /// the way we calculate the underlying assets in check Balance. The underlying aerodrome pool\n /// orders the tokens depending on the values of their addresses. If OETH token is token0 in the pool\n /// then sqrtRatioX96TickClosestToParity=sqrtRatioX96TickLower. If it is token1 in the pool then\n /// sqrtRatioX96TickClosestToParity=sqrtRatioX96TickHigher\n uint160 public immutable sqrtRatioX96TickClosestToParity;\n\n /// @dev a threshold under which the contract no longer allows for the protocol to rebalance. Guarding\n /// against a strategist / guardian being taken over and with multiple transactions draining the\n /// protocol funds.\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth); // 0x989e5ca8\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth); // 0xa6737d87\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n ); // 0x3681e8e0\n error OutsideExpectedTickRange(int24 currentTick); // 0x5a2eba75\n\n event PoolRebalanced(uint256 currentPoolWethShare);\n\n event PoolWethShareIntervalUpdated(\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n\n event LiquidityRemoved(\n uint256 withdrawLiquidityShare,\n uint256 removedWETHAmount,\n uint256 removedOETHbAmount,\n uint256 wethAmountCollected,\n uint256 oethbAmountCollected,\n uint256 underlyingAssets\n );\n\n event LiquidityAdded(\n uint256 wethAmountDesired,\n uint256 oethbAmountDesired,\n uint256 wethAmountSupplied,\n uint256 oethbAmountSupplied,\n uint256 tokenId,\n uint256 underlyingAssets\n );\n\n event UnderlyingAssetsUpdated(uint256 underlyingAssets);\n\n /**\n * @dev Un-stakes the token from the gauge for the execution duration of\n * the function and after that re-stakes it back in.\n *\n * It is important that the token is unstaked and owned by the strategy contract\n * during any liquidity altering operations and that it is re-staked back into the\n * gauge after liquidity changes. If the token fails to re-stake back to the\n * gauge it is not earning incentives.\n */\n // all functions using this modifier are used by functions with reentrancy check\n // slither-disable-start reentrancy-no-eth\n modifier gaugeUnstakeAndRestake() {\n // because of solidity short-circuit _isLpTokenStakedInGauge doesn't get called\n // when tokenId == 0\n if (tokenId != 0 && _isLpTokenStakedInGauge()) {\n clGauge.withdraw(tokenId);\n }\n _;\n // because of solidity short-circuit _isLpTokenStakedInGauge doesn't get called\n // when tokenId == 0\n if (tokenId != 0 && !_isLpTokenStakedInGauge()) {\n /**\n * It can happen that a withdrawal (or a full withdrawal) transactions would\n * remove all of the liquidity from the token with a NFT token still existing.\n * In that case the token can not be staked into the gauge, as some liquidity\n * needs to be added to it first.\n */\n if (_getLiquidity() > 0) {\n // if token liquidity changes the positionManager requires re-approval.\n // to any contract pre-approved to handle the token.\n positionManager.approve(address(clGauge), tokenId);\n clGauge.deposit(tokenId);\n }\n }\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice the constructor\n /// @dev This contract is intended to be used as a proxy. To prevent the\n /// potential confusion of having a functional implementation contract\n /// the constructor has the `initializer` modifier. This way the\n /// `initialize` function can not be called on the implementation contract.\n /// For the same reason the implementation contract also has the governor\n /// set to a zero address.\n /// @param _stratConfig the basic strategy configuration\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _oethbAddress Address of the Erc20 OETHb Token contract\n /// @param _swapRouter Address of the Aerodrome Universal Swap Router\n /// @param _nonfungiblePositionManager Address of position manager to add/remove\n /// the liquidity\n /// @param _clPool Address of the Aerodrome concentrated liquidity pool\n /// @param _clGauge Address of the Aerodrome slipstream pool gauge\n /// @param _sugarHelper Address of the Aerodrome Sugar helper contract\n /// @param _lowerBoundingTick Smaller bounding tick of our liquidity position\n /// @param _upperBoundingTick Larger bounding tick of our liquidity position\n /// @param _tickClosestToParity Tick that is closer to 1:1 price parity\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethbAddress,\n address _swapRouter,\n address _nonfungiblePositionManager,\n address _clPool,\n address _clGauge,\n address _sugarHelper,\n int24 _lowerBoundingTick,\n int24 _upperBoundingTick,\n int24 _tickClosestToParity\n ) initializer InitializableAbstractStrategy(_stratConfig) {\n require(\n _lowerBoundingTick == _tickClosestToParity ||\n _upperBoundingTick == _tickClosestToParity,\n \"Misconfigured tickClosestToParity\"\n );\n require(\n ICLPool(_clPool).token0() == _wethAddress,\n \"Only WETH supported as token0\"\n );\n require(\n ICLPool(_clPool).token1() == _oethbAddress,\n \"Only OETHb supported as token1\"\n );\n int24 _tickSpacing = ICLPool(_clPool).tickSpacing();\n // when we generalize AMO we might support other tick spacings\n require(_tickSpacing == 1, \"Unsupported tickSpacing\");\n\n WETH = _wethAddress;\n OETHb = _oethbAddress;\n swapRouter = ISwapRouter(_swapRouter);\n positionManager = INonfungiblePositionManager(\n _nonfungiblePositionManager\n );\n clPool = ICLPool(_clPool);\n clGauge = ICLGauge(_clGauge);\n helper = ISugarHelper(_sugarHelper);\n sqrtRatioX96TickLower = ISugarHelper(_sugarHelper).getSqrtRatioAtTick(\n _lowerBoundingTick\n );\n sqrtRatioX96TickHigher = ISugarHelper(_sugarHelper).getSqrtRatioAtTick(\n _upperBoundingTick\n );\n sqrtRatioX96TickClosestToParity = ISugarHelper(_sugarHelper)\n .getSqrtRatioAtTick(_tickClosestToParity);\n\n lowerTick = _lowerBoundingTick;\n upperTick = _upperBoundingTick;\n tickSpacing = _tickSpacing;\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /**\n * @notice initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n */\n function initialize(address[] memory _rewardTokenAddresses)\n external\n onlyGovernor\n initializer\n {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n new address[](0),\n new address[](0)\n );\n }\n\n /***************************************\n Configuration \n ****************************************/\n\n /**\n * @notice Set allowed pool weth share interval. After the rebalance happens\n * the share of WETH token in the ticker needs to be withing the specifications\n * of the interval.\n *\n * @param _allowedWethShareStart Start of WETH share interval expressed as 18 decimal amount\n * @param _allowedWethShareEnd End of WETH share interval expressed as 18 decimal amount\n */\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external onlyGovernor {\n require(\n _allowedWethShareStart < _allowedWethShareEnd,\n \"Invalid interval\"\n );\n // can not go below 1% weth share\n require(_allowedWethShareStart > 0.01 ether, \"Invalid interval start\");\n // can not go above 95% weth share\n require(_allowedWethShareEnd < 0.95 ether, \"Invalid interval end\");\n\n allowedWethShareStart = _allowedWethShareStart;\n allowedWethShareEnd = _allowedWethShareEnd;\n emit PoolWethShareIntervalUpdated(\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n\n /***************************************\n Periphery utils\n ****************************************/\n\n function _isLpTokenStakedInGauge() internal view returns (bool) {\n require(tokenId != 0, \"Missing NFT LP token\");\n\n address owner = positionManager.ownerOf(tokenId);\n require(\n owner == address(clGauge) || owner == address(this),\n \"Unexpected token owner\"\n );\n return owner == address(clGauge);\n }\n\n /***************************************\n Strategy overrides \n ****************************************/\n\n /**\n * @notice Deposit an amount of assets into the strategy contract. Calling deposit doesn't\n * automatically deposit funds into the underlying Aerodrome pool\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposit WETH to the strategy contract. This function does not add liquidity to the\n * underlying Aerodrome pool.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance > 1e12) {\n _deposit(WETH, _wethBalance);\n }\n }\n\n /**\n * @dev Deposit WETH to the contract. This function doesn't deposit the liquidity to the\n * pool, that is done via the rebalance call.\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n emit Deposit(_asset, address(0), _amount);\n\n // if the pool price is not within the expected interval leave the WETH on the contract\n // as to not break the mints\n (bool _isExpectedRange, ) = _checkForExpectedPoolPrice(false);\n if (_isExpectedRange) {\n // deposit funds into the underlying pool\n _rebalance(0, false, 0);\n }\n }\n\n /**\n * @notice Rebalance the pool to the desired token split and Deposit any WETH on the contract to the\n * underlying aerodrome pool. Print the required amount of corresponding OETHb. After the rebalancing is\n * done burn any potentially remaining OETHb tokens still on the strategy contract.\n *\n * This function has a slightly different behaviour depending on the status of the underlying Aerodrome\n * slipstream pool. The function consists of the following 3 steps:\n * 1. withdrawPartialLiquidity -> so that moving the activeTrading price via a swap is cheaper\n * 2. swapToDesiredPosition -> move active trading price in the pool to be able to deposit WETH & OETHb\n * tokens with the desired pre-configured shares\n * 3. addLiquidity -> add liquidity into the pool respecting share split configuration\n *\n * Scenario 1: When there is no liquidity in the pool from the strategy but there is from other LPs then\n * only step 1 is skipped. (It is important to note that liquidity needs to exist in the configured\n * strategy tick ranges in order for the swap to be possible) Step 3 mints new liquidity position\n * instead of adding to an existing one.\n * Scenario 2: When there is strategy's liquidity in the pool all 3 steps are taken\n *\n *\n * Exact _amountToSwap, _swapWeth & _minTokenReceived parameters shall be determined by simulating the\n * transaction off-chain. The strategy checks that after the swap the share of the tokens is in the\n * expected ranges.\n *\n * @param _amountToSwap The amount of the token to swap\n * @param _swapWeth Swap using WETH when true, use OETHb when false\n * @param _minTokenReceived Slippage check -> minimum amount of token expected in return\n */\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) external nonReentrant onlyGovernorOrStrategist {\n _rebalance(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n\n function _rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n /**\n * Would be nice to check if there is any total liquidity in the pool before performing this swap\n * but there is no easy way to do that in UniswapV3:\n * - clPool.liquidity() -> only liquidity in the active tick\n * - asset[1&2].balanceOf(address(clPool)) -> will include uncollected tokens of LP providers\n * after their liquidity position has been decreased\n */\n\n /**\n * When rebalance is called for the first time there is no strategy\n * liquidity in the pool yet. The liquidity removal is thus skipped.\n * Also execute this function when WETH is required for the swap.\n */\n if (tokenId != 0 && _swapWeth && _amountToSwap > 0) {\n _ensureWETHBalance(_amountToSwap);\n }\n\n // in some cases we will just want to add liquidity and not issue a swap to move the\n // active trading position within the pool\n if (_amountToSwap > 0) {\n _swapToDesiredPosition(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n // calling check liquidity early so we don't get unexpected errors when adding liquidity\n // in the later stages of this function\n _checkForExpectedPoolPrice(true);\n\n _addLiquidity();\n\n // this call shouldn't be necessary, since adding liquidity shouldn't affect the active\n // trading price. It is a defensive programming measure.\n (, uint256 _wethSharePct) = _checkForExpectedPoolPrice(true);\n\n // revert if protocol insolvent\n _solvencyAssert();\n\n emit PoolRebalanced(_wethSharePct);\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethbSupply = IERC20(OETHb).totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethbSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /**\n * @dev Decrease partial or all liquidity from the pool.\n * @param _liquidityToDecrease The amount of liquidity to remove expressed in 18 decimal point\n */\n function _removeLiquidity(uint256 _liquidityToDecrease)\n internal\n gaugeUnstakeAndRestake\n {\n require(_liquidityToDecrease > 0, \"Must remove some liquidity\");\n\n uint128 _liquidity = _getLiquidity();\n // need to convert to uint256 since intermittent result is to big for uint128 to handle\n uint128 _liquidityToRemove = uint256(_liquidity)\n .mulTruncate(_liquidityToDecrease)\n .toUint128();\n\n /**\n * There is no liquidity to remove -> exit function early. This can happen after a\n * withdraw/withdrawAll removes all of the liquidity while retaining the NFT token.\n */\n if (_liquidity == 0 || _liquidityToRemove == 0) {\n return;\n }\n\n (uint256 _amountWeth, uint256 _amountOethb) = positionManager\n .decreaseLiquidity(\n // Both expected amounts can be 0 since we don't really care if any swaps\n // happen just before the liquidity removal.\n INonfungiblePositionManager.DecreaseLiquidityParams({\n tokenId: tokenId,\n liquidity: _liquidityToRemove,\n amount0Min: 0,\n amount1Min: 0,\n deadline: block.timestamp\n })\n );\n\n (\n uint256 _amountWethCollected,\n uint256 _amountOethbCollected\n ) = positionManager.collect(\n INonfungiblePositionManager.CollectParams({\n tokenId: tokenId,\n recipient: address(this),\n amount0Max: type(uint128).max, // defaults to all tokens owed\n amount1Max: type(uint128).max // defaults to all tokens owed\n })\n );\n\n _updateUnderlyingAssets();\n\n emit LiquidityRemoved(\n _liquidityToDecrease,\n _amountWeth, //removedWethAmount\n _amountOethb, //removedOethbAmount\n _amountWethCollected,\n _amountOethbCollected,\n underlyingAssets\n );\n\n _burnOethbOnTheContract();\n }\n\n /**\n * @dev Perform a swap so that after the swap the ticker has the desired WETH to OETHb token share.\n */\n function _swapToDesiredPosition(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n IERC20 _tokenToSwap = IERC20(_swapWeth ? WETH : OETHb);\n uint256 _balance = _tokenToSwap.balanceOf(address(this));\n\n if (_balance < _amountToSwap) {\n // This should never trigger since _ensureWETHBalance will already\n // throw an error if there is not enough WETH\n if (_swapWeth) {\n revert NotEnoughWethForSwap(_balance, _amountToSwap);\n }\n // if swapping OETHb\n uint256 mintForSwap = _amountToSwap - _balance;\n IVault(vaultAddress).mintForStrategy(mintForSwap);\n }\n\n // approve the specific amount of WETH required\n if (_swapWeth) {\n IERC20(WETH).approve(address(swapRouter), _amountToSwap);\n }\n\n // Swap it\n swapRouter.exactInputSingle(\n // sqrtPriceLimitX96 is just a rough sanity check that we are within 0 -> 1 tick\n // a more fine check is performed in _checkForExpectedPoolPrice\n // Note: this needs further work if we want to generalize this approach\n ISwapRouter.ExactInputSingleParams({\n tokenIn: address(_tokenToSwap),\n tokenOut: _swapWeth ? OETHb : WETH,\n tickSpacing: tickSpacing, // set to 1\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: _amountToSwap,\n amountOutMinimum: _minTokenReceived, // slippage check\n sqrtPriceLimitX96: _swapWeth\n ? sqrtRatioX96TickLower\n : sqrtRatioX96TickHigher\n })\n );\n\n /**\n * In the interest of each function in _rebalance to leave the contract state as\n * clean as possible the OETHb tokens here are burned. This decreases the\n * dependence where `_swapToDesiredPosition` function relies on later functions\n * (`addLiquidity`) to burn the OETHb. Reducing the risk of error introduction.\n */\n _burnOethbOnTheContract();\n }\n\n /**\n * @dev Add liquidity into the pool in the pre-configured WETH to OETHb share ratios\n * defined by the allowedPoolWethShareStart|End interval. This function will respect\n * liquidity ratios when there is no liquidity yet in the pool. If liquidity is already\n * present then it relies on the `_swapToDesiredPosition` function in a step before\n * to already move the trading price to desired position (with some tolerance).\n */\n // rebalance already has re-entrency checks\n // slither-disable-start reentrancy-no-eth\n function _addLiquidity() internal gaugeUnstakeAndRestake {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n // don't deposit small liquidity amounts\n if (_wethBalance <= 1e12) {\n return;\n }\n\n uint160 _currentPrice = getPoolX96Price();\n /**\n * Sanity check active trading price is positioned within our desired tick.\n *\n * We revert when price is equal to the lower tick even though that is still\n * a valid amount in regards to ticker position by Sugar.estimateAmount call.\n * Current price equaling tick bound at the 1:1 price parity results in\n * uint overfow when calculating the OETHb balance to deposit.\n */\n if (\n _currentPrice <= sqrtRatioX96TickLower ||\n _currentPrice >= sqrtRatioX96TickHigher\n ) {\n revert OutsideExpectedTickRange(getCurrentTradingTick());\n }\n\n /**\n * If estimateAmount1 call fails it could be due to _currentPrice being really\n * close to a tick and amount1 is a larger number than the sugar helper is able\n * to compute.\n *\n * If token addresses were reversed estimateAmount0 would be required here\n */\n uint256 _oethbRequired = helper.estimateAmount1(\n _wethBalance,\n address(0), // no need to pass pool address when current price is specified\n _currentPrice,\n lowerTick,\n upperTick\n );\n\n if (_oethbRequired > _oethbBalance) {\n IVault(vaultAddress).mintForStrategy(\n _oethbRequired - _oethbBalance\n );\n }\n\n // approve the specific amount of WETH required\n IERC20(WETH).approve(address(positionManager), _wethBalance);\n\n uint256 _wethAmountSupplied;\n uint256 _oethbAmountSupplied;\n if (tokenId == 0) {\n (\n tokenId,\n ,\n _wethAmountSupplied,\n _oethbAmountSupplied\n ) = positionManager.mint(\n /** amount0Min & amount1Min are left at 0 because slippage protection is ensured by the\n * _checkForExpectedPoolPrice\n *›\n * Also sqrtPriceX96 is 0 because the pool is already created\n * non zero amount attempts to create a new instance of the pool\n */\n INonfungiblePositionManager.MintParams({\n token0: WETH,\n token1: OETHb,\n tickSpacing: tickSpacing,\n tickLower: lowerTick,\n tickUpper: upperTick,\n amount0Desired: _wethBalance,\n amount1Desired: _oethbRequired,\n amount0Min: 0,\n amount1Min: 0,\n recipient: address(this),\n deadline: block.timestamp,\n sqrtPriceX96: 0\n })\n );\n } else {\n (, _wethAmountSupplied, _oethbAmountSupplied) = positionManager\n .increaseLiquidity(\n /** amount0Min & amount1Min are left at 0 because slippage protection is ensured by the\n * _checkForExpectedPoolPrice\n */\n INonfungiblePositionManager.IncreaseLiquidityParams({\n tokenId: tokenId,\n amount0Desired: _wethBalance,\n amount1Desired: _oethbRequired,\n amount0Min: 0,\n amount1Min: 0,\n deadline: block.timestamp\n })\n );\n }\n\n _updateUnderlyingAssets();\n emit LiquidityAdded(\n _wethBalance, // wethAmountDesired\n _oethbRequired, // oethbAmountDesired\n _wethAmountSupplied, // wethAmountSupplied\n _oethbAmountSupplied, // oethbAmountSupplied\n tokenId, // tokenId\n underlyingAssets\n );\n\n // burn remaining OETHb\n _burnOethbOnTheContract();\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @dev Check that the Aerodrome pool price is within the expected\n * parameters.\n * This function works whether the strategy contract has liquidity\n * position in the pool or not. The function returns _wethSharePct\n * as a gas optimization measure.\n * @param throwException when set to true the function throws an exception\n * when pool's price is not within expected range.\n * @return _isExpectedRange Bool expressing price is within expected range\n * @return _wethSharePct Share of WETH owned by this strategy contract in the\n * configured ticker.\n */\n function _checkForExpectedPoolPrice(bool throwException)\n internal\n view\n returns (bool _isExpectedRange, uint256 _wethSharePct)\n {\n require(\n allowedWethShareStart != 0 && allowedWethShareEnd != 0,\n \"Weth share interval not set\"\n );\n\n uint160 _currentPrice = getPoolX96Price();\n\n /**\n * First check we are in expected tick range\n *\n * We revert even though price being equal to the lower tick would still\n * count being within lower tick for the purpose of Sugar.estimateAmount calls\n */\n if (\n _currentPrice <= sqrtRatioX96TickLower ||\n _currentPrice >= sqrtRatioX96TickHigher\n ) {\n if (throwException) {\n revert OutsideExpectedTickRange(getCurrentTradingTick());\n }\n return (false, 0);\n }\n\n // 18 decimal number expressed WETH tick share\n _wethSharePct = _getWethShare(_currentPrice);\n\n if (\n _wethSharePct < allowedWethShareStart ||\n _wethSharePct > allowedWethShareEnd\n ) {\n if (throwException) {\n revert PoolRebalanceOutOfBounds(\n _wethSharePct,\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n return (false, _wethSharePct);\n }\n\n return (true, _wethSharePct);\n }\n\n /**\n * Burns any OETHb tokens remaining on the strategy contract\n */\n function _burnOethbOnTheContract() internal {\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n if (_oethbBalance > 1e12) {\n IVault(vaultAddress).burnForStrategy(_oethbBalance);\n }\n }\n\n /// @dev This function assumes there are no uncollected tokens in the clPool owned by the strategy contract.\n /// For that reason any liquidity withdrawals must also collect the tokens.\n function _updateUnderlyingAssets() internal {\n if (tokenId == 0) {\n underlyingAssets = 0;\n emit UnderlyingAssetsUpdated(underlyingAssets);\n return;\n }\n\n uint128 _liquidity = _getLiquidity();\n\n /**\n * Our net value represent the smallest amount of tokens we are able to extract from the position\n * given our liquidity.\n *\n * The least amount of tokens extraditable from the position is where the active trading price is\n * at the ticker 0 meaning the pool is offering 1:1 trades between WETH & OETHb. At that moment the pool\n * consists completely of OETHb and no WETH.\n *\n * The more swaps from WETH -> OETHb happen on the pool the more the price starts to move towards the -1\n * ticker making OETHb (priced in WETH) more expensive.\n *\n * An additional note: when liquidity is 0 then the helper returns 0 for both token amounts. And the\n * function set underlying assets to 0.\n */\n (uint256 _wethAmount, uint256 _oethbAmount) = helper\n .getAmountsForLiquidity(\n sqrtRatioX96TickClosestToParity, // sqrtRatioX96\n sqrtRatioX96TickLower, // sqrtRatioAX96\n sqrtRatioX96TickHigher, // sqrtRatioBX96\n _liquidity\n );\n\n require(_wethAmount == 0, \"Non zero wethAmount\");\n underlyingAssets = _oethbAmount;\n emit UnderlyingAssetsUpdated(underlyingAssets);\n }\n\n /**\n * @dev This function removes the appropriate amount of liquidity to assure that the required\n * amount of WETH is available on the contract\n *\n * @param _amount WETH balance required on the contract\n */\n function _ensureWETHBalance(uint256 _amount) internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance >= _amount) {\n return;\n }\n\n require(tokenId != 0, \"No liquidity available\");\n uint256 _additionalWethRequired = _amount - _wethBalance;\n (uint256 _wethInThePool, ) = getPositionPrincipal();\n\n if (_wethInThePool < _additionalWethRequired) {\n revert NotEnoughWethLiquidity(\n _wethInThePool,\n _additionalWethRequired\n );\n }\n\n uint256 shareOfWethToRemove = Math.min(\n _additionalWethRequired.divPrecisely(_wethInThePool) + 1,\n 1e18\n );\n _removeLiquidity(shareOfWethToRemove);\n }\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset WETH address\n * @param _amount Amount of WETH to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n _ensureWETHBalance(_amount);\n\n _withdraw(_recipient, _amount);\n }\n\n /**\n * @notice Withdraw WETH and sends it to the Vault.\n */\n function withdrawAll() external override onlyVault nonReentrant {\n if (tokenId != 0) {\n _removeLiquidity(1e18);\n }\n\n uint256 _balance = IERC20(WETH).balanceOf(address(this));\n if (_balance > 0) {\n _withdraw(vaultAddress, _balance);\n }\n }\n\n function _withdraw(address _recipient, uint256 _amount) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n IERC20(WETH).safeTransfer(_recipient, _amount);\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /**\n * @dev Collect the AERO token from the gauge\n */\n function _collectRewardTokens() internal override {\n if (tokenId != 0 && _isLpTokenStakedInGauge()) {\n clGauge.getReward(tokenId);\n }\n super._collectRewardTokens();\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /**\n * @dev Approve the spending of all assets\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n // to add liquidity to the clPool\n IERC20(OETHb).approve(address(positionManager), type(uint256).max);\n // to be able to rebalance using the swapRouter\n IERC20(OETHb).approve(address(swapRouter), type(uint256).max);\n\n /* the behaviour of this strategy has slightly changed and WETH could be\n * present on the contract between the transactions. For that reason we are\n * un-approving WETH to the swapRouter & positionManager and only approving\n * the required amount before a transaction\n */\n IERC20(WETH).approve(address(swapRouter), 0);\n IERC20(WETH).approve(address(positionManager), 0);\n }\n\n /***************************************\n Balances and Fees\n ****************************************/\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256)\n {\n require(_asset == WETH, \"Only WETH supported\");\n\n // we could in theory deposit to the strategy and forget to call rebalance in the same\n // governance transaction batch. In that case the WETH that is on the strategy contract\n // also needs to be accounted for.\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n // just paranoia check, in case there is OETHb in the strategy that for some reason hasn't\n // been burned yet.\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n return underlyingAssets + _wethBalance + _oethbBalance;\n }\n\n /**\n * @dev Returns the balance of both tokens in a given position (excluding fees)\n * @return _amountWeth Amount of WETH in position\n * @return _amountOethb Amount of OETHb in position\n */\n function getPositionPrincipal()\n public\n view\n returns (uint256 _amountWeth, uint256 _amountOethb)\n {\n if (tokenId == 0) {\n return (0, 0);\n }\n\n uint160 _sqrtRatioX96 = getPoolX96Price();\n (_amountWeth, _amountOethb) = helper.principal(\n positionManager,\n tokenId,\n _sqrtRatioX96\n );\n }\n\n /**\n * @notice Returns the current pool price in X96 format\n * @return _sqrtRatioX96 Pool price\n */\n function getPoolX96Price() public view returns (uint160 _sqrtRatioX96) {\n (_sqrtRatioX96, , , , , ) = clPool.slot0();\n }\n\n /**\n * @notice Returns the current active trading tick of the underlying pool\n * @return _currentTick Current pool trading tick\n */\n function getCurrentTradingTick() public view returns (int24 _currentTick) {\n (, _currentTick, , , , ) = clPool.slot0();\n }\n\n /**\n * @notice Returns the percentage of WETH liquidity in the configured ticker\n * owned by this strategy contract.\n * @return uint256 1e18 denominated percentage expressing the share\n */\n function getWETHShare() external view returns (uint256) {\n uint160 _currentPrice = getPoolX96Price();\n return _getWethShare(_currentPrice);\n }\n\n /**\n * @notice Returns the amount of liquidity in the contract's LP position\n * @return _liquidity Amount of liquidity in the position\n */\n function _getLiquidity() internal view returns (uint128 _liquidity) {\n if (tokenId == 0) {\n revert(\"No LP position\");\n }\n\n (, , , , , , , _liquidity, , , , ) = positionManager.positions(tokenId);\n }\n\n function _getWethShare(uint160 _currentPrice)\n internal\n view\n returns (uint256)\n {\n /**\n * If estimateAmount1 call fails it could be due to _currentPrice being really\n * close to a tick and amount1 too big to compute.\n *\n * If token addresses were reversed estimateAmount0 would be required here\n */\n uint256 _normalizedWethAmount = 1 ether;\n uint256 _correspondingOethAmount = helper.estimateAmount1(\n _normalizedWethAmount,\n address(0), // no need to pass pool address when current price is specified\n _currentPrice,\n lowerTick,\n upperTick\n );\n\n // 18 decimal number expressed weth tick share\n return\n _normalizedWethAmount.divPrecisely(\n _normalizedWethAmount + _correspondingOethAmount\n );\n }\n\n /***************************************\n Hidden functions\n ****************************************/\n /// @inheritdoc InitializableAbstractStrategy\n function setPTokenAddress(address, address) external override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /// @inheritdoc InitializableAbstractStrategy\n function removePToken(uint256) external override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Not supported\n */\n function _abstractSetPToken(address, address) internal override {\n // the deployer shall call safeApproveAllTokens() to set necessary approvals\n revert(\"Unsupported method\");\n }\n\n /***************************************\n ERC721 management\n ****************************************/\n\n /// @notice Callback function for whenever a NFT is transferred to this contract\n // solhint-disable-next-line max-line-length\n /// Ref: https://docs.openzeppelin.com/contracts/3.x/api/token/erc721#IERC721Receiver-onERC721Received-address-address-uint256-bytes-\n function onERC721Received(\n address,\n address,\n uint256,\n bytes calldata\n ) external returns (bytes4) {\n return this.onERC721Received.selector;\n }\n}\n" + }, + "contracts/strategies/balancer/AbstractAuraStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Base Balancer Abstract Strategy\n * @author Origin Protocol Inc\n */\n\nimport { AbstractBalancerStrategy } from \"./AbstractBalancerStrategy.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IERC4626 } from \"../../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport { IRewardStaking } from \"../IRewardStaking.sol\";\n\nabstract contract AbstractAuraStrategy is AbstractBalancerStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n /// @notice Address of the Aura rewards pool\n address public immutable auraRewardPoolAddress;\n\n // renamed from __reserved to not shadow AbstractBalancerStrategy.__reserved,\n int256[50] private __reserved_baseAuraStrategy;\n\n constructor(address _auraRewardPoolAddress) {\n auraRewardPoolAddress = _auraRewardPoolAddress;\n }\n\n /**\n * @dev Deposit all Balancer Pool Tokens (BPT) in this strategy contract\n * to the Aura rewards pool.\n */\n function _lpDepositAll() internal virtual override {\n uint256 bptBalance = IERC20(platformAddress).balanceOf(address(this));\n uint256 auraLp = IERC4626(auraRewardPoolAddress).deposit(\n bptBalance,\n address(this)\n );\n require(bptBalance == auraLp, \"Aura LP != BPT\");\n }\n\n /**\n * @dev Withdraw `numBPTTokens` Balancer Pool Tokens (BPT) from\n * the Aura rewards pool to this strategy contract.\n * @param numBPTTokens Number of Balancer Pool Tokens (BPT) to withdraw\n */\n function _lpWithdraw(uint256 numBPTTokens) internal virtual override {\n IRewardStaking(auraRewardPoolAddress).withdrawAndUnwrap(\n numBPTTokens,\n true // also claim reward tokens\n );\n }\n\n /**\n * @dev Withdraw all Balancer Pool Tokens (BPT) from\n * the Aura rewards pool to this strategy contract.\n */\n function _lpWithdrawAll() internal virtual override {\n // Get all the strategy's BPTs in Aura\n // maxRedeem is implemented as balanceOf(address) in Aura\n uint256 bptBalance = IERC4626(auraRewardPoolAddress).maxRedeem(\n address(this)\n );\n\n IRewardStaking(auraRewardPoolAddress).withdrawAndUnwrap(\n bptBalance,\n true // also claim reward tokens\n );\n }\n\n /**\n * @notice Collects BAL and AURA tokens from the rewards pool.\n */\n function collectRewardTokens()\n external\n virtual\n override\n onlyHarvester\n nonReentrant\n {\n /* Similar to Convex, calling this function collects both of the\n * accrued BAL and AURA tokens.\n */\n IRewardStaking(auraRewardPoolAddress).getReward();\n _collectRewardTokens();\n }\n\n /// @notice Balancer Pool Tokens (BPT) in the Balancer pool and the Aura rewards pool.\n function _getBalancerPoolTokens()\n internal\n view\n override\n returns (uint256 balancerPoolTokens)\n {\n balancerPoolTokens =\n IERC20(platformAddress).balanceOf(address(this)) +\n // maxRedeem is implemented as balanceOf(address) in Aura\n IERC4626(auraRewardPoolAddress).maxRedeem(address(this));\n }\n\n function _approveBase() internal virtual override {\n super._approveBase();\n\n IERC20 pToken = IERC20(platformAddress);\n pToken.safeApprove(auraRewardPoolAddress, type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/balancer/AbstractBalancerStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Balancer Abstract Strategy\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\nimport { IRateProvider } from \"../../interfaces/balancer/IRateProvider.sol\";\nimport { VaultReentrancyLib } from \"./VaultReentrancyLib.sol\";\nimport { IOracle } from \"../../interfaces/IOracle.sol\";\nimport { IWstETH } from \"../../interfaces/IWstETH.sol\";\nimport { IERC4626 } from \"../../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nabstract contract AbstractBalancerStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n address public immutable rETH;\n address public immutable stETH;\n address public immutable wstETH;\n address public immutable frxETH;\n address public immutable sfrxETH;\n\n /// @notice Address of the Balancer vault\n IBalancerVault public immutable balancerVault;\n /// @notice Balancer pool identifier\n bytes32 public immutable balancerPoolId;\n\n // Max withdrawal deviation denominated in 1e18 (1e18 == 100%)\n uint256 public maxWithdrawalDeviation;\n // Max deposit deviation denominated in 1e18 (1e18 == 100%)\n uint256 public maxDepositDeviation;\n\n int256[48] private __reserved;\n\n struct BaseBalancerConfig {\n address rEthAddress; // Address of the rETH token\n address stEthAddress; // Address of the stETH token\n address wstEthAddress; // Address of the wstETH token\n address frxEthAddress; // Address of the frxEth token\n address sfrxEthAddress; // Address of the sfrxEth token\n address balancerVaultAddress; // Address of the Balancer vault\n bytes32 balancerPoolId; // Balancer pool identifier\n }\n\n event MaxWithdrawalDeviationUpdated(\n uint256 _prevMaxDeviationPercentage,\n uint256 _newMaxDeviationPercentage\n );\n event MaxDepositDeviationUpdated(\n uint256 _prevMaxDeviationPercentage,\n uint256 _newMaxDeviationPercentage\n );\n\n /**\n * @dev Ensure we are not in a Vault context when this function is called, by attempting a no-op internal\n * balance operation. If we are already in a Vault transaction (e.g., a swap, join, or exit), the Vault's\n * reentrancy protection will cause this function to revert.\n *\n * Use this modifier with any function that can cause a state change in a pool and is either public itself,\n * or called by a public function *outside* a Vault operation (e.g., join, exit, or swap).\n *\n * This is to protect against Balancer's read-only re-entrancy vulnerability:\n * https://www.notion.so/originprotocol/Balancer-read-only-reentrancy-c686e72c82414ef18fa34312bb02e11b\n */\n modifier whenNotInBalancerVaultContext() {\n VaultReentrancyLib.ensureNotInVaultContext(balancerVault);\n _;\n }\n\n constructor(BaseBalancerConfig memory _balancerConfig) {\n rETH = _balancerConfig.rEthAddress;\n stETH = _balancerConfig.stEthAddress;\n wstETH = _balancerConfig.wstEthAddress;\n frxETH = _balancerConfig.frxEthAddress;\n sfrxETH = _balancerConfig.sfrxEthAddress;\n\n balancerVault = IBalancerVault(_balancerConfig.balancerVaultAddress);\n balancerPoolId = _balancerConfig.balancerPoolId;\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Balancer's strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of BAL & AURA\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * WETH, stETH\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // BAL & AURA\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n maxWithdrawalDeviation = 1e16;\n maxDepositDeviation = 1e16;\n\n emit MaxWithdrawalDeviationUpdated(0, maxWithdrawalDeviation);\n emit MaxDepositDeviationUpdated(0, maxDepositDeviation);\n\n IERC20[] memory poolAssets = _getPoolAssets();\n require(\n poolAssets.length == _assets.length,\n \"Pool assets length mismatch\"\n );\n for (uint256 i = 0; i < _assets.length; ++i) {\n address asset = _fromPoolAsset(address(poolAssets[i]));\n require(_assets[i] == asset, \"Pool assets mismatch\");\n }\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @notice Get strategy's share of an assets in the Balancer pool.\n * This is not denominated in OUSD/ETH value of the assets in the Balancer pool.\n * @param _asset Address of the Vault collateral asset\n * @return amount the amount of vault collateral assets\n *\n * IMPORTANT if this function is overridden it needs to have a whenNotInBalancerVaultContext\n * modifier on it or it is susceptible to read-only re-entrancy attack\n *\n * @dev it is important that this function is not affected by reporting inflated\n * values of assets in case of any pool manipulation. Such a manipulation could easily\n * exploit the protocol by:\n * - minting OETH\n * - tilting Balancer pool to report higher balances of assets\n * - rebasing() -> all that extra token balances get distributed to OETH holders\n * - tilting pool back\n * - redeeming OETH\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n whenNotInBalancerVaultContext\n returns (uint256 amount)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n\n uint256 bptBalance = _getBalancerPoolTokens();\n\n /* To calculate the worth of queried asset:\n * - assume that all tokens normalized to their ETH value have an equal split balance\n * in the pool when it is balanced\n * - multiply the BPT amount with the bpt rate to get the ETH denominated amount\n * of strategy's holdings\n * - divide that by the number of tokens we support in the pool to get ETH denominated\n * amount that is applicable to each supported token in the pool.\n *\n * It would be possible to support only 1 asset in the pool (and be exposed to all\n * the assets while holding BPT tokens) and deposit/withdraw/checkBalance using only\n * that asset. TBD: changes to other functions still required if we ever decide to\n * go with such configuration.\n */\n amount = (bptBalance.mulTruncate(\n IRateProvider(platformAddress).getRate()\n ) / assetsMapped.length);\n\n /* If the pool asset is equal to (strategy )_asset it means that a rate\n * provider for that asset exists and that asset is not necessarily\n * pegged to a unit (ETH).\n *\n * Because this function returns the balance of the asset and is not denominated in\n * ETH units we need to convert the ETH denominated amount to asset amount.\n */\n if (_toPoolAsset(_asset) == _asset) {\n amount = amount.divPrecisely(_getRateProviderRate(_asset));\n }\n }\n\n /**\n * @notice Returns the value of all assets managed by this strategy.\n * Uses the Balancer pool's rate (virtual price) to convert the strategy's\n * Balancer Pool Tokens (BPT) to ETH value.\n * @return value The ETH value\n *\n * IMPORTANT if this function is overridden it needs to have a whenNotInBalancerVaultContext\n * modifier on it or it is susceptible to read-only re-entrancy attack\n */\n function checkBalance()\n external\n view\n virtual\n whenNotInBalancerVaultContext\n returns (uint256 value)\n {\n uint256 bptBalance = _getBalancerPoolTokens();\n\n // Convert BPT to ETH value\n value = bptBalance.mulTruncate(\n IRateProvider(platformAddress).getRate()\n );\n }\n\n /// @notice Balancer Pool Tokens (BPT) in the Balancer pool.\n function _getBalancerPoolTokens()\n internal\n view\n virtual\n returns (uint256 balancerPoolTokens)\n {\n balancerPoolTokens = IERC20(platformAddress).balanceOf(address(this));\n }\n\n /* solhint-disable max-line-length */\n /**\n * @notice BPT price is calculated by taking the rate from the rateProvider of the asset in\n * question. If one does not exist it defaults to 1e18. To get the final BPT expected that\n * is multiplied by the underlying asset amount divided by BPT token rate. BPT token rate is\n * similar to Curve's virtual_price and expresses how much has the price of BPT appreciated\n * (e.g. due to swap fees) in relation to the underlying assets\n *\n * Using the above approach makes the strategy vulnerable to a possible MEV attack using\n * flash loan to manipulate the pool before a deposit/withdrawal since the function ignores\n * market values of the assets being priced in BPT.\n *\n * At the time of writing there is no safe on-chain approach to pricing BPT in a way that it\n * would make it invulnerable to MEV pool manipulation. See recent Balancer exploit:\n * https://www.notion.so/originprotocol/Balancer-OETH-strategy-9becdea132704e588782a919d7d471eb?pvs=4#1cf07de12fc64f1888072321e0644348\n *\n * To mitigate MEV possibilities during deposits and withdraws, the VaultValueChecker will use checkBalance before and after the move\n * to ensure the expected changes took place.\n *\n * @param _asset Address of the Balancer pool asset\n * @param _amount Amount of the Balancer pool asset\n * @return bptExpected of BPT expected in exchange for the asset\n *\n * @dev\n * bptAssetPrice = 1e18 (asset peg) * pool_asset_rate\n *\n * bptExpected = bptAssetPrice * asset_amount / BPT_token_rate\n *\n * bptExpected = 1e18 (asset peg) * pool_asset_rate * asset_amount / BPT_token_rate\n * bptExpected = asset_amount * pool_asset_rate / BPT_token_rate\n *\n * further information available here:\n * https://www.notion.so/originprotocol/Balancer-OETH-strategy-9becdea132704e588782a919d7d471eb?pvs=4#ce01495ae70346d8971f5dced809fb83\n */\n /* solhint-enable max-line-length */\n function _getBPTExpected(address _asset, uint256 _amount)\n internal\n view\n virtual\n returns (uint256 bptExpected)\n {\n uint256 bptRate = IRateProvider(platformAddress).getRate();\n uint256 poolAssetRate = _getRateProviderRate(_asset);\n bptExpected = _amount.mulTruncate(poolAssetRate).divPrecisely(bptRate);\n }\n\n function _getBPTExpected(\n address[] memory _assets,\n uint256[] memory _amounts\n ) internal view virtual returns (uint256 bptExpected) {\n require(_assets.length == _amounts.length, \"Assets & amounts mismatch\");\n\n for (uint256 i = 0; i < _assets.length; ++i) {\n uint256 poolAssetRate = _getRateProviderRate(_assets[i]);\n // convert asset amount to ETH amount\n bptExpected += _amounts[i].mulTruncate(poolAssetRate);\n }\n\n uint256 bptRate = IRateProvider(platformAddress).getRate();\n // Convert ETH amount to BPT amount\n bptExpected = bptExpected.divPrecisely(bptRate);\n }\n\n function _lpDepositAll() internal virtual;\n\n function _lpWithdraw(uint256 numBPTTokens) internal virtual;\n\n function _lpWithdrawAll() internal virtual;\n\n /**\n * @notice Balancer returns assets and rateProviders for corresponding assets ordered\n * by numerical order.\n */\n function _getPoolAssets() internal view returns (IERC20[] memory assets) {\n // slither-disable-next-line unused-return\n (assets, , ) = balancerVault.getPoolTokens(balancerPoolId);\n }\n\n /**\n * @dev If an asset is rebasing the Balancer pools have a wrapped versions of assets\n * that the strategy supports. This function converts the pool(wrapped) asset\n * and corresponding amount to strategy asset.\n */\n function _toPoolAsset(address asset, uint256 amount)\n internal\n view\n returns (address poolAsset, uint256 poolAmount)\n {\n if (asset == stETH) {\n poolAsset = wstETH;\n if (amount > 0) {\n poolAmount = IWstETH(wstETH).getWstETHByStETH(amount);\n }\n } else if (asset == frxETH) {\n poolAsset = sfrxETH;\n if (amount > 0) {\n poolAmount = IERC4626(sfrxETH).convertToShares(amount);\n }\n } else {\n poolAsset = asset;\n poolAmount = amount;\n }\n }\n\n /**\n * @dev Converts a Vault collateral asset to a Balancer pool asset.\n * stETH becomes wstETH, frxETH becomes sfrxETH and everything else stays the same.\n * @param asset Address of the Vault collateral asset.\n * @return Address of the Balancer pool asset.\n */\n function _toPoolAsset(address asset) internal view returns (address) {\n if (asset == stETH) {\n return wstETH;\n } else if (asset == frxETH) {\n return sfrxETH;\n }\n return asset;\n }\n\n /**\n * @dev Converts rebasing asset to its wrapped counterpart.\n */\n function _wrapPoolAsset(address asset, uint256 amount)\n internal\n returns (address wrappedAsset, uint256 wrappedAmount)\n {\n if (asset == stETH) {\n wrappedAsset = wstETH;\n if (amount > 0) {\n wrappedAmount = IWstETH(wstETH).wrap(amount);\n }\n } else if (asset == frxETH) {\n wrappedAsset = sfrxETH;\n if (amount > 0) {\n wrappedAmount = IERC4626(sfrxETH).deposit(\n amount,\n address(this)\n );\n }\n } else {\n wrappedAsset = asset;\n wrappedAmount = amount;\n }\n }\n\n /**\n * @dev Converts wrapped asset to its rebasing counterpart.\n */\n function _unwrapPoolAsset(address asset, uint256 amount)\n internal\n returns (uint256 unwrappedAmount)\n {\n if (asset == stETH) {\n unwrappedAmount = IWstETH(wstETH).unwrap(amount);\n } else if (asset == frxETH) {\n unwrappedAmount = IERC4626(sfrxETH).withdraw(\n amount,\n address(this),\n address(this)\n );\n } else {\n unwrappedAmount = amount;\n }\n }\n\n /**\n * @dev If an asset is rebasing the Balancer pools have a wrapped versions of assets\n * that the strategy supports. This function converts the rebasing strategy asset\n * and corresponding amount to wrapped(pool) asset.\n */\n function _fromPoolAsset(address poolAsset, uint256 poolAmount)\n internal\n view\n returns (address asset, uint256 amount)\n {\n if (poolAsset == wstETH) {\n asset = stETH;\n if (poolAmount > 0) {\n amount = IWstETH(wstETH).getStETHByWstETH(poolAmount);\n }\n } else if (poolAsset == sfrxETH) {\n asset = frxETH;\n if (poolAmount > 0) {\n amount = IERC4626(sfrxETH).convertToAssets(poolAmount);\n }\n } else {\n asset = poolAsset;\n amount = poolAmount;\n }\n }\n\n function _fromPoolAsset(address poolAsset)\n internal\n view\n returns (address asset)\n {\n if (poolAsset == wstETH) {\n asset = stETH;\n } else if (poolAsset == sfrxETH) {\n asset = frxETH;\n } else {\n asset = poolAsset;\n }\n }\n\n /**\n * @notice Sets max withdrawal deviation that is considered when removing\n * liquidity from Balancer pools.\n * @param _maxWithdrawalDeviation Max withdrawal deviation denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxWithdrawalDeviation will be 1% (1e16) for production\n * usage. Vault value checker in combination with checkBalance will\n * catch any unexpected manipulation.\n */\n function setMaxWithdrawalDeviation(uint256 _maxWithdrawalDeviation)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(\n _maxWithdrawalDeviation <= 1e18,\n \"Withdrawal dev. out of bounds\"\n );\n emit MaxWithdrawalDeviationUpdated(\n maxWithdrawalDeviation,\n _maxWithdrawalDeviation\n );\n maxWithdrawalDeviation = _maxWithdrawalDeviation;\n }\n\n /**\n * @notice Sets max deposit deviation that is considered when adding\n * liquidity to Balancer pools.\n * @param _maxDepositDeviation Max deposit deviation denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxDepositDeviation will default to 1% (1e16)\n * for production usage. Vault value checker in combination with\n * checkBalance will catch any unexpected manipulation.\n */\n function setMaxDepositDeviation(uint256 _maxDepositDeviation)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(_maxDepositDeviation <= 1e18, \"Deposit dev. out of bounds\");\n emit MaxDepositDeviationUpdated(\n maxDepositDeviation,\n _maxDepositDeviation\n );\n maxDepositDeviation = _maxDepositDeviation;\n }\n\n function _approveBase() internal virtual {\n IERC20 pToken = IERC20(platformAddress);\n // Balancer vault for BPT token (required for removing liquidity)\n pToken.safeApprove(address(balancerVault), type(uint256).max);\n }\n\n function _getRateProviderRate(address _asset)\n internal\n view\n virtual\n returns (uint256);\n}\n" + }, + "contracts/strategies/balancer/BalancerMetaPoolStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Balancer MetaStablePool Strategy\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { AbstractAuraStrategy, AbstractBalancerStrategy } from \"./AbstractAuraStrategy.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\nimport { IRateProvider } from \"../../interfaces/balancer/IRateProvider.sol\";\nimport { IMetaStablePool } from \"../../interfaces/balancer/IMetaStablePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\ncontract BalancerMetaPoolStrategy is AbstractAuraStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(\n BaseStrategyConfig memory _stratConfig,\n BaseBalancerConfig memory _balancerConfig,\n address _auraRewardPoolAddress\n )\n InitializableAbstractStrategy(_stratConfig)\n AbstractBalancerStrategy(_balancerConfig)\n AbstractAuraStrategy(_auraRewardPoolAddress)\n {}\n\n /**\n * @notice There are no plans to configure BalancerMetaPool as a default\n * asset strategy. For that reason there is no need to support this\n * functionality.\n */\n function deposit(address, uint256)\n external\n override\n onlyVault\n nonReentrant\n {\n revert(\"Not supported\");\n }\n\n /**\n * @notice There are no plans to configure BalancerMetaPool as a default\n * asset strategy. For that reason there is no need to support this\n * functionality.\n */\n function deposit(address[] calldata, uint256[] calldata)\n external\n onlyVault\n nonReentrant\n {\n revert(\"Not supported\");\n }\n\n /**\n * @notice Deposits all supported assets in this strategy contract to the Balancer pool.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 assetsLength = assetsMapped.length;\n address[] memory strategyAssets = new address[](assetsLength);\n uint256[] memory strategyAmounts = new uint256[](assetsLength);\n\n // For each vault collateral asset\n for (uint256 i = 0; i < assetsLength; ++i) {\n strategyAssets[i] = assetsMapped[i];\n // Get the asset balance in this strategy contract\n strategyAmounts[i] = IERC20(strategyAssets[i]).balanceOf(\n address(this)\n );\n }\n _deposit(strategyAssets, strategyAmounts);\n }\n\n /*\n * _deposit doesn't require a read-only re-entrancy protection since during the deposit\n * the function enters the Balancer Vault Context. If this function were called as part of\n * the attacking contract (while intercepting execution flow upon receiving ETH) the read-only\n * protection of the Balancer Vault would be triggered. Since the attacking contract would\n * already be in the Balancer Vault context and wouldn't be able to enter it again.\n */\n function _deposit(\n address[] memory _strategyAssets,\n uint256[] memory _strategyAmounts\n ) internal {\n require(\n _strategyAssets.length == _strategyAmounts.length,\n \"Array length missmatch\"\n );\n\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n\n uint256[] memory strategyAssetAmountsToPoolAssetAmounts = new uint256[](\n _strategyAssets.length\n );\n address[] memory strategyAssetsToPoolAssets = new address[](\n _strategyAssets.length\n );\n\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n address strategyAsset = _strategyAssets[i];\n uint256 strategyAmount = _strategyAmounts[i];\n\n require(\n assetToPToken[strategyAsset] != address(0),\n \"Unsupported asset\"\n );\n strategyAssetsToPoolAssets[i] = _toPoolAsset(strategyAsset);\n\n if (strategyAmount > 0) {\n emit Deposit(strategyAsset, platformAddress, strategyAmount);\n\n // wrap rebasing assets like stETH and frxETH to wstETH and sfrxETH\n (, strategyAssetAmountsToPoolAssetAmounts[i]) = _wrapPoolAsset(\n strategyAsset,\n strategyAmount\n );\n }\n }\n\n uint256[] memory amountsIn = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n for (uint256 i = 0; i < tokens.length; ++i) {\n // Convert IERC20 type to address\n poolAssets[i] = address(tokens[i]);\n\n // For each of the mapped assets\n for (uint256 j = 0; j < strategyAssetsToPoolAssets.length; ++j) {\n // If the pool asset is the same as the mapped asset\n if (poolAssets[i] == strategyAssetsToPoolAssets[j]) {\n amountsIn[i] = strategyAssetAmountsToPoolAssetAmounts[j];\n }\n }\n }\n\n uint256 minBPT = _getBPTExpected(\n strategyAssetsToPoolAssets,\n strategyAssetAmountsToPoolAssetAmounts\n );\n uint256 minBPTwDeviation = minBPT.mulTruncate(\n 1e18 - maxDepositDeviation\n );\n\n /* EXACT_TOKENS_IN_FOR_BPT_OUT:\n * User sends precise quantities of tokens, and receives an\n * estimated but unknown (computed at run time) quantity of BPT.\n *\n * ['uint256', 'uint256[]', 'uint256']\n * [EXACT_TOKENS_IN_FOR_BPT_OUT, amountsIn, minimumBPT]\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,\n amountsIn,\n minBPTwDeviation\n );\n\n IBalancerVault.JoinPoolRequest memory request = IBalancerVault\n .JoinPoolRequest(poolAssets, amountsIn, userData, false);\n\n // Add the pool assets in this strategy to the balancer pool\n balancerVault.joinPool(\n balancerPoolId,\n address(this),\n address(this),\n request\n );\n\n // Deposit the Balancer Pool Tokens (BPT) into Aura\n _lpDepositAll();\n }\n\n /**\n * @notice Withdraw a Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAsset Address of the Vault collateral asset\n * @param _strategyAmount The amount of Vault collateral assets to withdraw\n */\n function withdraw(\n address _recipient,\n address _strategyAsset,\n uint256 _strategyAmount\n ) external override onlyVault nonReentrant {\n address[] memory strategyAssets = new address[](1);\n uint256[] memory strategyAmounts = new uint256[](1);\n strategyAssets[0] = _strategyAsset;\n strategyAmounts[0] = _strategyAmount;\n\n _withdraw(_recipient, strategyAssets, strategyAmounts);\n }\n\n /**\n * @notice Withdraw multiple Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAssets Addresses of the Vault collateral assets\n * @param _strategyAmounts The amounts of Vault collateral assets to withdraw\n */\n function withdraw(\n address _recipient,\n address[] calldata _strategyAssets,\n uint256[] calldata _strategyAmounts\n ) external onlyVault nonReentrant {\n _withdraw(_recipient, _strategyAssets, _strategyAmounts);\n }\n\n /**\n * @dev Withdraw multiple Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAssets Addresses of the Vault collateral assets\n * @param _strategyAmounts The amounts of Vault collateral assets to withdraw\n *\n * _withdrawal doesn't require a read-only re-entrancy protection since during the withdrawal\n * the function enters the Balancer Vault Context. If this function were called as part of\n * the attacking contract (while intercepting execution flow upon receiving ETH) the read-only\n * protection of the Balancer Vault would be triggered. Since the attacking contract would\n * already be in the Balancer Vault context and wouldn't be able to enter it again.\n */\n function _withdraw(\n address _recipient,\n address[] memory _strategyAssets,\n uint256[] memory _strategyAmounts\n ) internal {\n require(\n _strategyAssets.length == _strategyAmounts.length,\n \"Invalid input arrays\"\n );\n\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n require(\n assetToPToken[_strategyAssets[i]] != address(0),\n \"Unsupported asset\"\n );\n }\n\n // STEP 1 - Calculate the Balancer pool assets and amounts from the vault collateral assets\n\n // Get all the supported balancer pool assets\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n // Calculate the balancer pool assets and amounts to withdraw\n uint256[] memory poolAssetsAmountsOut = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n // Is the wrapped asset amount indexed by the assets array, not the order of the Balancer pool tokens\n // eg wstETH and sfrxETH amounts, not the stETH and frxETH amounts\n uint256[] memory strategyAssetsToPoolAssetsAmounts = new uint256[](\n _strategyAssets.length\n );\n\n // For each of the Balancer pool assets\n for (uint256 i = 0; i < tokens.length; ++i) {\n poolAssets[i] = address(tokens[i]);\n\n // Convert the Balancer pool asset back to a vault collateral asset\n address strategyAsset = _fromPoolAsset(poolAssets[i]);\n\n // for each of the vault assets\n for (uint256 j = 0; j < _strategyAssets.length; ++j) {\n // If the vault asset equals the vault asset mapped from the Balancer pool asset\n if (_strategyAssets[j] == strategyAsset) {\n (, poolAssetsAmountsOut[i]) = _toPoolAsset(\n strategyAsset,\n _strategyAmounts[j]\n );\n strategyAssetsToPoolAssetsAmounts[j] = poolAssetsAmountsOut[\n i\n ];\n\n /* Because of the potential Balancer rounding error mentioned below\n * the contract might receive 1-2 WEI smaller amount than required\n * in the withdraw user data encoding. If slightly lesser token amount\n * is received the strategy can not unwrap the pool asset as it is\n * smaller than expected.\n *\n * For that reason we `overshoot` the required tokens expected to\n * circumvent the error\n */\n if (poolAssetsAmountsOut[i] > 0) {\n poolAssetsAmountsOut[i] += 2;\n }\n }\n }\n }\n\n // STEP 2 - Calculate the max about of Balancer Pool Tokens (BPT) to withdraw\n\n // Estimate the required amount of Balancer Pool Tokens (BPT) for the assets\n uint256 maxBPTtoWithdraw = _getBPTExpected(\n poolAssets,\n /* all non 0 values are overshot by 2 WEI and with the expected mainnet\n * ~1% withdrawal deviation, the 2 WEI aren't important\n */\n poolAssetsAmountsOut\n );\n // Increase BPTs by the max allowed deviation\n // Any excess BPTs will be left in this strategy contract\n maxBPTtoWithdraw = maxBPTtoWithdraw.mulTruncate(\n 1e18 + maxWithdrawalDeviation\n );\n\n // STEP 3 - Withdraw the Balancer Pool Tokens (BPT) from Aura to this strategy contract\n\n // Withdraw BPT from Aura allowing for BPTs left in this strategy contract from previous withdrawals\n _lpWithdraw(\n maxBPTtoWithdraw - IERC20(platformAddress).balanceOf(address(this))\n );\n\n // STEP 4 - Withdraw the balancer pool assets from the pool\n\n /* Custom asset exit: BPT_IN_FOR_EXACT_TOKENS_OUT:\n * User sends an estimated but unknown (computed at run time) quantity of BPT,\n * and receives precise quantities of specified tokens.\n *\n * ['uint256', 'uint256[]', 'uint256']\n * [BPT_IN_FOR_EXACT_TOKENS_OUT, amountsOut, maxBPTAmountIn]\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.BPT_IN_FOR_EXACT_TOKENS_OUT,\n poolAssetsAmountsOut,\n maxBPTtoWithdraw\n );\n\n IBalancerVault.ExitPoolRequest memory request = IBalancerVault\n .ExitPoolRequest(\n poolAssets,\n /* We specify the exact amount of a tokens we are expecting in the encoded\n * userData, for that reason we don't need to specify the amountsOut here.\n *\n * Also Balancer has a rounding issue that can make a transaction fail:\n * https://github.com/balancer/balancer-v2-monorepo/issues/2541\n * which is an extra reason why this field is empty.\n */\n new uint256[](tokens.length),\n userData,\n false\n );\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n /* Payable keyword is required because of the IBalancerVault interface even though\n * this strategy shall never be receiving native ETH\n */\n payable(address(this)),\n request\n );\n\n // STEP 5 - Re-deposit any left over BPT tokens back into Aura\n /* When concluding how much of BPT we need to withdraw from Aura we overshoot by\n * roughly around 1% (initial mainnet setting of maxWithdrawalDeviation). After exiting\n * the pool strategy could have left over BPT tokens that are not earning boosted yield.\n * We re-deploy those back in.\n */\n _lpDepositAll();\n\n // STEP 6 - Unswap balancer pool assets to vault collateral assets and send to the vault.\n\n // For each of the specified assets\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n // Unwrap assets like wstETH and sfrxETH to rebasing assets stETH and frxETH\n if (strategyAssetsToPoolAssetsAmounts[i] > 0) {\n _unwrapPoolAsset(\n _strategyAssets[i],\n strategyAssetsToPoolAssetsAmounts[i]\n );\n }\n\n // Transfer the vault collateral assets to the recipient, which is typically the vault\n if (_strategyAmounts[i] > 0) {\n IERC20(_strategyAssets[i]).safeTransfer(\n _recipient,\n _strategyAmounts[i]\n );\n\n emit Withdrawal(\n _strategyAssets[i],\n platformAddress,\n _strategyAmounts[i]\n );\n }\n }\n }\n\n /**\n * @notice Withdraws all supported Vault collateral assets from the Balancer pool\n * and send to the OToken's Vault.\n *\n * Is only executable by the OToken's Vault or the Governor.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n // STEP 1 - Withdraw all Balancer Pool Tokens (BPT) from Aura to this strategy contract\n\n _lpWithdrawAll();\n // Get the BPTs withdrawn from Aura plus any that were already in this strategy contract\n uint256 BPTtoWithdraw = IERC20(platformAddress).balanceOf(\n address(this)\n );\n // Get the balancer pool assets and their total balances\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n uint256[] memory minAmountsOut = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n for (uint256 i = 0; i < tokens.length; ++i) {\n poolAssets[i] = address(tokens[i]);\n }\n\n // STEP 2 - Withdraw the Balancer pool assets from the pool\n /* Proportional exit: EXACT_BPT_IN_FOR_TOKENS_OUT:\n * User sends a precise quantity of BPT, and receives an estimated but unknown\n * (computed at run time) quantity of a single token\n *\n * ['uint256', 'uint256']\n * [EXACT_BPT_IN_FOR_TOKENS_OUT, bptAmountIn]\n *\n * It is ok to pass an empty minAmountsOut since tilting the pool in any direction\n * when doing a proportional exit can only be beneficial to the strategy. Since\n * it will receive more of the underlying tokens for the BPT traded in.\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.EXACT_BPT_IN_FOR_TOKENS_OUT,\n BPTtoWithdraw\n );\n\n IBalancerVault.ExitPoolRequest memory request = IBalancerVault\n .ExitPoolRequest(poolAssets, minAmountsOut, userData, false);\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n /* Payable keyword is required because of the IBalancerVault interface even though\n * this strategy shall never be receiving native ETH\n */\n payable(address(this)),\n request\n );\n\n // STEP 3 - Convert the balancer pool assets to the vault collateral assets and send to the vault\n // For each of the Balancer pool assets\n for (uint256 i = 0; i < tokens.length; ++i) {\n address poolAsset = address(tokens[i]);\n // Convert the balancer pool asset to the strategy asset\n address strategyAsset = _fromPoolAsset(poolAsset);\n // Get the balancer pool assets withdraw from the pool plus any that were already in this strategy contract\n uint256 poolAssetAmount = IERC20(poolAsset).balanceOf(\n address(this)\n );\n\n // Unwrap assets like wstETH and sfrxETH to rebasing assets stETH and frxETH\n uint256 unwrappedAmount = 0;\n if (poolAssetAmount > 0) {\n unwrappedAmount = _unwrapPoolAsset(\n strategyAsset,\n poolAssetAmount\n );\n }\n\n // Transfer the vault collateral assets to the vault\n if (unwrappedAmount > 0) {\n IERC20(strategyAsset).safeTransfer(\n vaultAddress,\n unwrappedAmount\n );\n emit Withdrawal(\n strategyAsset,\n platformAddress,\n unwrappedAmount\n );\n }\n }\n }\n\n /**\n * @notice Approves the Balancer Vault to transfer poolAsset counterparts\n * of all of the supported assets from this strategy. E.g. stETH is a supported\n * strategy and Balancer Vault gets unlimited approval to transfer wstETH.\n *\n * If Balancer pool uses a wrapped version of a supported asset then also approve\n * unlimited usage of an asset to the contract responsible for wrapping.\n *\n * Approve unlimited spending by Balancer Vault and Aura reward pool of the\n * pool BPT tokens.\n *\n * Is only executable by the Governor.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n _abstractSetPToken(assetsMapped[i], platformAddress);\n }\n _approveBase();\n }\n\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address) internal override {\n address poolAsset = _toPoolAsset(_asset);\n if (_asset == stETH) {\n // slither-disable-next-line unused-return\n IERC20(stETH).approve(wstETH, type(uint256).max);\n } else if (_asset == frxETH) {\n // slither-disable-next-line unused-return\n IERC20(frxETH).approve(sfrxETH, type(uint256).max);\n }\n _approveAsset(poolAsset);\n }\n\n /**\n * @dev Approves the Balancer Vault to transfer an asset from\n * this strategy. The assets could be a Vault collateral asset\n * like WETH or rETH; or a Balancer pool asset that wraps the vault asset\n * like wstETH or sfrxETH.\n */\n function _approveAsset(address _asset) internal {\n IERC20 asset = IERC20(_asset);\n // slither-disable-next-line unused-return\n asset.approve(address(balancerVault), type(uint256).max);\n }\n\n /**\n * @notice Returns the rate supplied by the Balancer configured rate\n * provider. Rate is used to normalize the token to common underlying\n * pool denominator. (ETH for ETH Liquid staking derivatives)\n *\n * @param _asset Address of the Balancer pool asset\n * @return rate of the corresponding asset\n */\n function _getRateProviderRate(address _asset)\n internal\n view\n override\n returns (uint256)\n {\n IMetaStablePool pool = IMetaStablePool(platformAddress);\n IRateProvider[] memory providers = pool.getRateProviders();\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n\n uint256 providersLength = providers.length;\n for (uint256 i = 0; i < providersLength; ++i) {\n // _assets and corresponding rate providers are all in the same order\n if (address(tokens[i]) == _asset) {\n // rate provider doesn't exist, defaults to 1e18\n if (address(providers[i]) == address(0)) {\n return 1e18;\n }\n return providers[i].getRate();\n }\n }\n\n // should never happen\n assert(false);\n }\n}\n" + }, + "contracts/strategies/balancer/VaultReentrancyLib.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity >=0.7.0 <0.9.0;\n\nimport \"../../utils/BalancerErrors.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\n\nlibrary VaultReentrancyLib {\n /**\n * @dev Ensure we are not in a Vault context when this function is called, by attempting a no-op internal\n * balance operation. If we are already in a Vault transaction (e.g., a swap, join, or exit), the Vault's\n * reentrancy protection will cause this function to revert.\n *\n * The exact function call doesn't really matter: we're just trying to trigger the Vault reentrancy check\n * (and not hurt anything in case it works). An empty operation array with no specific operation at all works\n * for that purpose, and is also the least expensive in terms of gas and bytecode size.\n *\n * Call this at the top of any function that can cause a state change in a pool and is either public itself,\n * or called by a public function *outside* a Vault operation (e.g., join, exit, or swap).\n *\n * If this is *not* called in functions that are vulnerable to the read-only reentrancy issue described\n * here (https://forum.balancer.fi/t/reentrancy-vulnerability-scope-expanded/4345), those functions are unsafe,\n * and subject to manipulation that may result in loss of funds.\n */\n function ensureNotInVaultContext(IBalancerVault vault) internal view {\n // Perform the following operation to trigger the Vault's reentrancy guard:\n //\n // IBalancerVault.UserBalanceOp[] memory noop = new IBalancerVault.UserBalanceOp[](0);\n // _vault.manageUserBalance(noop);\n //\n // However, use a static call so that it can be a view function (even though the function is non-view).\n // This allows the library to be used more widely, as some functions that need to be protected might be\n // view.\n //\n // This staticcall always reverts, but we need to make sure it doesn't fail due to a re-entrancy attack.\n // Staticcalls consume all gas forwarded to them on a revert caused by storage modification.\n // By default, almost the entire available gas is forwarded to the staticcall,\n // causing the entire call to revert with an 'out of gas' error.\n //\n // We set the gas limit to 10k for the staticcall to\n // avoid wasting gas when it reverts due to storage modification.\n // `manageUserBalance` is a non-reentrant function in the Vault, so calling it invokes `_enterNonReentrant`\n // in the `ReentrancyGuard` contract, reproduced here:\n //\n // function _enterNonReentrant() private {\n // // If the Vault is actually being reentered, it will revert in the first line, at the `_require` that\n // // checks the reentrancy flag, with \"BAL#400\" (corresponding to Errors.REENTRANCY) in the revertData.\n // // The full revertData will be: `abi.encodeWithSignature(\"Error(string)\", \"BAL#400\")`.\n // _require(_status != _ENTERED, Errors.REENTRANCY);\n //\n // // If the Vault is not being reentered, the check above will pass: but it will *still* revert,\n // // because the next line attempts to modify storage during a staticcall. However, this type of\n // // failure results in empty revertData.\n // _status = _ENTERED;\n // }\n //\n // So based on this analysis, there are only two possible revertData values: empty, or abi.encoded BAL#400.\n //\n // It is of course much more bytecode and gas efficient to check for zero-length revertData than to compare it\n // to the encoded REENTRANCY revertData.\n //\n // While it should be impossible for the call to fail in any other way (especially since it reverts before\n // `manageUserBalance` even gets called), any other error would generate non-zero revertData, so checking for\n // empty data guards against this case too.\n\n (, bytes memory revertData) = address(vault).staticcall{ gas: 10_000 }(\n abi.encodeWithSelector(vault.manageUserBalance.selector, 0)\n );\n\n _require(revertData.length == 0, Errors.REENTRANCY);\n }\n}\n" + }, + "contracts/strategies/BaseCurveAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for the Curve OETH/WETH pool\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { ICurveStableSwapNG } from \"../interfaces/ICurveStableSwapNG.sol\";\nimport { ICurveXChainLiquidityGauge } from \"../interfaces/ICurveXChainLiquidityGauge.sol\";\nimport { IChildLiquidityGaugeFactory } from \"../interfaces/IChildLiquidityGaugeFactory.sol\";\n\ncontract BaseCurveAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @dev a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n // New immutable variables that must be set in the constructor\n /**\n * @notice Address of the Wrapped ETH (WETH) contract.\n */\n IWETH9 public immutable weth;\n\n /**\n * @notice Address of the OETH token contract.\n */\n IERC20 public immutable oeth;\n\n /**\n * @notice Address of the LP (Liquidity Provider) token contract.\n */\n IERC20 public immutable lpToken;\n\n /**\n * @notice Address of the Curve StableSwap NG pool contract.\n */\n ICurveStableSwapNG public immutable curvePool;\n\n /**\n * @notice Address of the Curve X-Chain Liquidity Gauge contract.\n */\n ICurveXChainLiquidityGauge public immutable gauge;\n\n /**\n * @notice Address of the Child Liquidity Gauge Factory contract.\n */\n IChildLiquidityGaugeFactory public immutable gaugeFactory;\n\n // Ordered list of pool assets\n uint128 public immutable oethCoinIndex;\n uint128 public immutable wethCoinIndex;\n\n /**\n * @notice Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n uint256 public maxSlippage;\n\n event MaxSlippageUpdated(uint256 newMaxSlippage);\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balancesBefore = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffBefore = balancesBefore[wethCoinIndex].toInt256() -\n balancesBefore[oethCoinIndex].toInt256();\n\n _;\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balancesAfter = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffAfter = balancesAfter[wethCoinIndex].toInt256() -\n balancesAfter[oethCoinIndex].toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of ETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _oeth,\n address _weth,\n address _gauge,\n address _gaugeFactory,\n uint128 _oethCoinIndex,\n uint128 _wethCoinIndex\n ) InitializableAbstractStrategy(_baseConfig) {\n oethCoinIndex = _oethCoinIndex;\n wethCoinIndex = _wethCoinIndex;\n\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveStableSwapNG(_baseConfig.platformAddress);\n\n oeth = IERC20(_oeth);\n weth = IWETH9(_weth);\n gauge = ICurveXChainLiquidityGauge(_gauge);\n gaugeFactory = IChildLiquidityGaugeFactory(_gaugeFactory);\n\n _setGovernor(address(0));\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV\n * @param _maxSlippage Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV\n uint256 _maxSlippage\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n address[] memory _assets = new address[](1);\n _assets[0] = address(weth);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n _setMaxSlippage(_maxSlippage);\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit WETH into the Curve pool\n * @param _weth Address of Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to deposit.\n */\n function deposit(address _weth, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_weth, _amount);\n }\n\n function _deposit(address _weth, uint256 _wethAmount) internal {\n require(_wethAmount > 0, \"Must deposit something\");\n require(_weth == address(weth), \"Can only deposit WETH\");\n\n emit Deposit(_weth, address(lpToken), _wethAmount);\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 oethToAdd = uint256(\n _max(\n 0,\n balances[wethCoinIndex].toInt256() +\n _wethAmount.toInt256() -\n balances[oethCoinIndex].toInt256()\n )\n );\n\n /* Add so much OETH so that the pool ends up being balanced. And at minimum\n * add as much OETH as WETH and at maximum twice as much OETH.\n */\n oethToAdd = Math.max(oethToAdd, _wethAmount);\n oethToAdd = Math.min(oethToAdd, _wethAmount * 2);\n\n /* Mint OETH with a strategy that attempts to contribute to stability of OETH/WETH pool. Try\n * to mint so much OETH that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OETH minted will always be at least equal or greater\n * to WETH amount deployed. And never larger than twice the WETH amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(oethToAdd);\n\n emit Deposit(address(oeth), address(lpToken), oethToAdd);\n\n uint256[] memory _amounts = new uint256[](2);\n _amounts[wethCoinIndex] = _wethAmount;\n _amounts[oethCoinIndex] = oethToAdd;\n\n uint256 valueInLpTokens = (_wethAmount + oethToAdd).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Do the deposit to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(_amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool's LP tokens into the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n /**\n * @notice Deposit the strategy's entire balance of WETH into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = weth.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(weth), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _weth Address of the Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to withdraw.\n */\n function withdraw(\n address _recipient,\n address _weth,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_weth == address(weth), \"Can only withdraw WETH\");\n\n emit Withdrawal(_weth, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(_amount);\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough WETH on balanced removal\n */\n uint256[] memory _minWithdrawalAmounts = new uint256[](2);\n _minWithdrawalAmounts[wethCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OETH and any that was left in the strategy\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n\n // Transfer WETH to the recipient\n require(\n weth.transfer(_recipient, _amount),\n \"Transfer of WETH not successful\"\n );\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n function calcTokenToBurn(uint256 _wethAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much WETH\n * we want we can determine how much of OETH we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolWETHBalance = curvePool.balances(wethCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolWETHBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_wethAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = gauge.balanceOf(address(this));\n // Can not withdraw zero LP tokens from the gauge\n if (gaugeTokens == 0) return;\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[] memory minWithdrawAmounts = new uint256[](2);\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(\n lpToken.balanceOf(address(this)),\n minWithdrawAmounts\n );\n\n // Burn all OETH\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Get the strategy contract's WETH balance.\n // This includes all that was removed from the Curve pool and\n // any ether that was sitting in the strategy contract before the removal.\n uint256 ethBalance = weth.balanceOf(address(this));\n require(\n weth.transfer(vaultAddress, ethBalance),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethBalance);\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[] memory amounts = new uint256[](2);\n amounts[oethCoinIndex] = _oTokens;\n\n // Convert OETH to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool LP tokens to the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Deposit(address(oeth), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough ETH.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove OTokens from the Curve pool\n uint256 oethToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n oethCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /**\n * @notice One-sided remove of ETH from the Curve pool, convert to WETH\n * and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for ETH.\n * @dev Curve pool LP tokens is used rather than WETH assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of ETH. Curve's `calc_token_amount` functioun does not include fees.\n * A 3rd party libary can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * caclulate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Curve gauge and remove ETH from the Curve pool\n uint256 ethAmount = _withdrawAndRemoveFromPool(\n _lpTokens,\n wethCoinIndex\n );\n\n // Transfer WETH to the vault\n require(\n weth.transfer(vaultAddress, ethAmount),\n \"Transfer of WETH not successful\"\n );\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(weth), address(lpToken), ethAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the Convex pool and\n * do a one-sided remove of ETH or OETH from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the Convex pool.\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = ETH, 1 = OETH.\n * @return coinsRemoved The amount of ETH or OETH removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Curve gauge\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to ETH value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n // Apply slippage to ETH value\n uint256 minAmount = valueInEth.mulTruncate(uint256(1e18) - maxSlippage);\n\n // Remove just the ETH from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethbSupply = oeth.totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethbSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // CRV rewards flow.\n //---\n // CRV inflation:\n // Gauge receive CRV rewards from inflation.\n // Each checkpoint on the gauge send this CRV inflation to gauge factory.\n // This strategy should call mint on the gauge factory to collect the CRV rewards.\n // ---\n // Extra rewards:\n // Calling claim_rewards on the gauge will only claim extra rewards (outside of CRV).\n // ---\n\n // Mint CRV on Child Liquidity gauge factory\n gaugeFactory.mint(address(gauge));\n // Collect extra gauge rewards (outside of CRV)\n gauge.claim_rewards();\n\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _lpAmount) internal {\n // withdraw lp tokens from the gauge without claiming rewards\n gauge.withdraw(_lpAmount);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // WETH balance needed here for the balance check that happens from vault during depositing.\n balance = weth.balanceOf(address(this));\n uint256 lpTokens = gauge.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += (lpTokens * curvePool.get_virtual_price()) / 1e18;\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(weth);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Sets the maximum slippage allowed for any swap/liquidity operation\n * @param _maxSlippage Maximum slippage allowed, 1e18 = 100%.\n */\n function setMaxSlippage(uint256 _maxSlippage) external onlyGovernor {\n _setMaxSlippage(_maxSlippage);\n }\n\n function _setMaxSlippage(uint256 _maxSlippage) internal {\n require(_maxSlippage <= 5e16, \"Slippage must be less than 100%\");\n maxSlippage = _maxSlippage;\n emit MaxSlippageUpdated(_maxSlippage);\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OETH (required for adding liquidity)\n // slither-disable-next-line unused-return\n oeth.approve(platformAddress, type(uint256).max);\n\n // Approve Curve pool for WETH (required for adding liquidity)\n // slither-disable-next-line unused-return\n weth.approve(platformAddress, type(uint256).max);\n\n // Approve Curve gauge contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Curve gauge.\n // slither-disable-next-line unused-return\n lpToken.approve(address(gauge), type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/BridgedWOETHStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, SafeERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { AggregatorV3Interface } from \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\n\ncontract BridgedWOETHStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using StableMath for uint128;\n using SafeCast for uint256;\n using SafeERC20 for IERC20;\n\n event MaxPriceDiffBpsUpdated(uint128 oldValue, uint128 newValue);\n event WOETHPriceUpdated(uint128 oldValue, uint128 newValue);\n\n IWETH9 public immutable weth;\n IERC20 public immutable bridgedWOETH;\n IERC20 public immutable oethb;\n\n uint256 public constant MAX_PRICE_STALENESS = 2 days;\n\n uint128 public lastOraclePrice;\n uint128 public maxPriceDiffBps;\n\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _weth,\n address _bridgedWOETH,\n address _oethb\n ) InitializableAbstractStrategy(_stratConfig) {\n weth = IWETH9(_weth);\n bridgedWOETH = IERC20(_bridgedWOETH);\n oethb = IERC20(_oethb);\n }\n\n function initialize(uint128 _maxPriceDiffBps)\n external\n onlyGovernor\n initializer\n {\n InitializableAbstractStrategy._initialize(\n new address[](0), // No reward tokens\n new address[](0), // No assets\n new address[](0) // No pTokens\n );\n\n _setMaxPriceDiffBps(_maxPriceDiffBps);\n }\n\n /**\n * @dev Sets the max price diff bps for the wOETH value appreciation\n * @param _maxPriceDiffBps Bps value, 10k == 100%\n */\n function setMaxPriceDiffBps(uint128 _maxPriceDiffBps)\n external\n onlyGovernor\n {\n _setMaxPriceDiffBps(_maxPriceDiffBps);\n }\n\n /**\n * @dev Sets the max price diff bps for the wOETH value appreciation\n * @param _maxPriceDiffBps Bps value, 10k == 100%\n */\n function _setMaxPriceDiffBps(uint128 _maxPriceDiffBps) internal {\n require(\n _maxPriceDiffBps > 0 && _maxPriceDiffBps <= 10000,\n \"Invalid bps value\"\n );\n\n emit MaxPriceDiffBpsUpdated(maxPriceDiffBps, _maxPriceDiffBps);\n\n maxPriceDiffBps = _maxPriceDiffBps;\n }\n\n /**\n * @dev Wrapper for _updateWOETHOraclePrice with nonReentrant flag\n * @return The latest price of wOETH from Oracle\n */\n function updateWOETHOraclePrice() external nonReentrant returns (uint256) {\n return _updateWOETHOraclePrice();\n }\n\n /**\n * @dev Finds the value of bridged wOETH from the Oracle.\n * Ensures that it's within the bounds and reasonable.\n * And stores it.\n *\n * NOTE: Intentionally not caching `Vault.priceProvider` here,\n * since doing so would mean that we also have to update this\n * strategy every time there's a change in oracle router.\n * Besides on L2, the gas is considerably cheaper than mainnet.\n *\n * @return Latest price from oracle\n */\n function _updateWOETHOraclePrice() internal returns (uint256) {\n // WETH price per unit of bridged wOETH\n uint256 oraclePrice = IOracle(IVault(vaultAddress).priceProvider())\n .price(address(bridgedWOETH));\n\n // 1 wOETH > 1 WETH, always\n require(oraclePrice > 1 ether, \"Invalid wOETH value\");\n\n uint128 oraclePrice128 = oraclePrice.toUint128();\n\n // Do some checks\n if (lastOraclePrice > 0) {\n // Make sure the value only goes up\n require(oraclePrice128 >= lastOraclePrice, \"Negative wOETH yield\");\n\n // lastOraclePrice * (1 + maxPriceDiffBps)\n uint256 maxPrice = (lastOraclePrice * (1e4 + maxPriceDiffBps)) /\n 1e4;\n\n // And that it's within the bounds.\n require(oraclePrice128 <= maxPrice, \"Price diff beyond threshold\");\n }\n\n emit WOETHPriceUpdated(lastOraclePrice, oraclePrice128);\n\n // Store the price\n lastOraclePrice = oraclePrice128;\n\n return oraclePrice;\n }\n\n /**\n * @dev Computes & returns the value of given wOETH in WETH\n * @param woethAmount Amount of wOETH\n * @return Value of wOETH in WETH (using the last stored oracle price)\n */\n function getBridgedWOETHValue(uint256 woethAmount)\n public\n view\n returns (uint256)\n {\n return (woethAmount * lastOraclePrice) / 1 ether;\n }\n\n /**\n * @dev Takes in bridged wOETH and mints & returns\n * equivalent amount of OETHb.\n * @param woethAmount Amount of bridged wOETH to transfer in\n */\n function depositBridgedWOETH(uint256 woethAmount)\n external\n onlyGovernorOrStrategist\n nonReentrant\n {\n // Update wOETH price\n uint256 oraclePrice = _updateWOETHOraclePrice();\n\n // Figure out how much they are worth\n uint256 oethToMint = (woethAmount * oraclePrice) / 1 ether;\n\n require(oethToMint > 0, \"Invalid deposit amount\");\n\n // There's no pToken, however, it just uses WOETH address in the event\n emit Deposit(address(weth), address(bridgedWOETH), oethToMint);\n\n // Mint OETHb tokens and transfer it to the caller\n IVault(vaultAddress).mintForStrategy(oethToMint);\n\n // Transfer out minted OETHb\n // slither-disable-next-line unchecked-transfer unused-return\n oethb.transfer(msg.sender, oethToMint);\n\n // Transfer in all bridged wOETH tokens\n // slither-disable-next-line unchecked-transfer unused-return\n bridgedWOETH.transferFrom(msg.sender, address(this), woethAmount);\n }\n\n /**\n * @dev Takes in OETHb and burns it and returns\n * equivalent amount of bridged wOETH.\n * @param oethToBurn Amount of OETHb to burn\n */\n function withdrawBridgedWOETH(uint256 oethToBurn)\n external\n onlyGovernorOrStrategist\n nonReentrant\n {\n // Update wOETH price\n uint256 oraclePrice = _updateWOETHOraclePrice();\n\n // Figure out how much they are worth\n uint256 woethAmount = (oethToBurn * 1 ether) / oraclePrice;\n\n require(woethAmount > 0, \"Invalid withdraw amount\");\n\n // There's no pToken, however, it just uses WOETH address in the event\n emit Withdrawal(address(weth), address(bridgedWOETH), oethToBurn);\n\n // Transfer WOETH back\n // slither-disable-next-line unchecked-transfer unused-return\n bridgedWOETH.transfer(msg.sender, woethAmount);\n\n // Transfer in OETHb\n // slither-disable-next-line unchecked-transfer unused-return\n oethb.transferFrom(msg.sender, address(this), oethToBurn);\n\n // Burn OETHb\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n }\n\n /**\n * @notice Returns the amount of backing WETH the strategy holds\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // Figure out how much wOETH is worth at the time.\n // Always uses the last stored oracle price.\n // Call updateWOETHOraclePrice manually to pull in latest yields.\n\n // NOTE: If the contract has been deployed but the call to\n // `updateWOETHOraclePrice()` has never been made, then this\n // will return zero. It should be fine because the strategy\n // should update the price whenever a deposit/withdraw happens.\n\n // If `updateWOETHOraclePrice()` hasn't been called in a while,\n // the strategy will underreport its holdings but never overreport it.\n\n balance =\n (bridgedWOETH.balanceOf(address(this)) * lastOraclePrice) /\n 1 ether;\n }\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n // Strategist deposits bridged wOETH but the contract only\n // reports the balance in WETH. As far as Vault is concerned,\n // it isn't aware of bridged wOETH token\n return _asset == address(weth);\n }\n\n /***************************************\n Overridden methods\n ****************************************/\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function transferToken(address _asset, uint256 _amount)\n public\n override\n onlyGovernor\n {\n require(\n _asset != address(bridgedWOETH) && _asset != address(weth),\n \"Cannot transfer supported asset\"\n );\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice deposit() function not used for this strategy\n */\n function deposit(address, uint256)\n external\n override\n onlyVault\n nonReentrant\n {\n // Use depositBridgedWOETH() instead\n require(false, \"Deposit disabled\");\n }\n\n /**\n * @notice depositAll() function not used for this strategy\n */\n function depositAll() external override onlyVault nonReentrant {\n // Use depositBridgedWOETH() instead\n require(false, \"Deposit disabled\");\n }\n\n /**\n * @notice withdraw() function not used for this strategy\n */\n function withdraw(\n // solhint-disable-next-line no-unused-vars\n address _recipient,\n // solhint-disable-next-line no-unused-vars\n address _asset,\n // solhint-disable-next-line no-unused-vars\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(false, \"Withdrawal disabled\");\n }\n\n /**\n * @notice withdrawAll() function not used for this strategy\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n // Withdrawal disabled\n }\n\n function _abstractSetPToken(address, address) internal override {\n revert(\"No pTokens are used\");\n }\n\n function safeApproveAllTokens() external override {}\n\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function removePToken(uint256) external override {\n revert(\"No pTokens are used\");\n }\n\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function collectRewardTokens() external override {}\n}\n" + }, + "contracts/strategies/CompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Compound Strategy\n * @notice Investment strategy for Compound like lending platforms. eg Compound and Flux\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICERC20 } from \"./ICompound.sol\";\nimport { AbstractCompoundStrategy, InitializableAbstractStrategy } from \"./AbstractCompoundStrategy.sol\";\nimport { IComptroller } from \"../interfaces/IComptroller.sol\";\nimport { IERC20 } from \"../utils/InitializableAbstractStrategy.sol\";\n\ncontract CompoundStrategy is AbstractCompoundStrategy {\n using SafeERC20 for IERC20;\n event SkippedWithdrawal(address asset, uint256 amount);\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @notice initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @notice Collect accumulated COMP and send to Harvester.\n */\n function collectRewardTokens()\n external\n virtual\n override\n onlyHarvester\n nonReentrant\n {\n // Claim COMP from Comptroller\n ICERC20 cToken = _getCTokenFor(assetsMapped[0]);\n IComptroller comptroller = IComptroller(cToken.comptroller());\n // Only collect from active cTokens, saves gas\n address[] memory ctokensToCollect = new address[](assetsMapped.length);\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n ctokensToCollect[i] = address(_getCTokenFor(assetsMapped[i]));\n }\n // Claim only for this strategy\n address[] memory claimers = new address[](1);\n claimers[0] = address(this);\n // Claim COMP from Comptroller. Only collect for supply, saves gas\n comptroller.claimComp(claimers, ctokensToCollect, false, true);\n // Transfer COMP to Harvester\n IERC20 rewardToken = IERC20(rewardTokenAddresses[0]);\n uint256 balance = rewardToken.balanceOf(address(this));\n emit RewardTokenCollected(\n harvesterAddress,\n rewardTokenAddresses[0],\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n\n /**\n * @notice Deposit asset into the underlying platform\n * @param _asset Address of asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit an asset into the underlying platform\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n ICERC20 cToken = _getCTokenFor(_asset);\n emit Deposit(_asset, address(cToken), _amount);\n require(cToken.mint(_amount) == 0, \"cToken mint failed\");\n }\n\n /**\n * @notice Deposit the entire balance of any supported asset in the strategy into the underlying platform\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n uint256 assetBalance = asset.balanceOf(address(this));\n if (assetBalance > 0) {\n _deposit(address(asset), assetBalance);\n }\n }\n }\n\n /**\n * @notice Withdraw an asset from the underlying platform\n * @param _recipient Address to receive withdrawn assets\n * @param _asset Address of the asset to withdraw\n * @param _amount Amount of assets to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n ICERC20 cToken = _getCTokenFor(_asset);\n // If redeeming 0 cTokens, just skip, else COMP will revert\n uint256 cTokensToRedeem = _convertUnderlyingToCToken(cToken, _amount);\n if (cTokensToRedeem == 0) {\n emit SkippedWithdrawal(_asset, _amount);\n return;\n }\n\n emit Withdrawal(_asset, address(cToken), _amount);\n require(cToken.redeemUnderlying(_amount) == 0, \"Redeem failed\");\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / cTokens\n * We need to approve the cToken and give it permission to spend the asset\n * @param _asset Address of the asset to approve. eg DAI\n * @param _pToken The pToken for the approval. eg cDAI or fDAI\n */\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n // Safe approval\n IERC20(_asset).safeApprove(_pToken, 0);\n IERC20(_asset).safeApprove(_pToken, type(uint256).max);\n }\n\n /**\n * @notice Remove all supported assets from the underlying platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n // Redeem entire balance of cToken\n ICERC20 cToken = _getCTokenFor(address(asset));\n uint256 cTokenBalance = cToken.balanceOf(address(this));\n if (cTokenBalance > 0) {\n require(cToken.redeem(cTokenBalance) == 0, \"Redeem failed\");\n uint256 assetBalance = asset.balanceOf(address(this));\n // Transfer entire balance to Vault\n asset.safeTransfer(vaultAddress, assetBalance);\n\n emit Withdrawal(address(asset), address(cToken), assetBalance);\n }\n }\n }\n\n /**\n * @notice Get the total asset value held in the underlying platform\n * This includes any interest that was generated since depositing.\n * The exchange rate between the cToken and asset gradually increases,\n * causing the cToken to be worth more corresponding asset.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n // Balance is always with token cToken decimals\n ICERC20 cToken = _getCTokenFor(_asset);\n balance = _checkBalance(cToken);\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * underlying = (cTokenAmt * exchangeRate) / 1e18\n * @param _cToken cToken for which to check balance\n * @return balance Total value of the asset in the platform\n */\n function _checkBalance(ICERC20 _cToken)\n internal\n view\n returns (uint256 balance)\n {\n // e.g. 50e8*205316390724364402565641705 / 1e18 = 1.0265..e18\n balance =\n (_cToken.balanceOf(address(this)) * _cToken.exchangeRateStored()) /\n 1e18;\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding cToken,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens() external override {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n address cToken = assetToPToken[address(asset)];\n // Safe approval\n asset.safeApprove(cToken, 0);\n asset.safeApprove(cToken, type(uint256).max);\n }\n }\n}\n" + }, + "contracts/strategies/ConvexEthMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Convex Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for the Curve OETH/ETH pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { ICurveETHPoolV1 } from \"./ICurveETHPoolV1.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\n\ncontract ConvexEthMetaStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 public constant MAX_SLIPPAGE = 1e16; // 1%, same as the Curve UI\n address public constant ETH_ADDRESS =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n // The following slots have been deprecated with immutable variables\n // slither-disable-next-line constable-states\n address private _deprecated_cvxDepositorAddress;\n // slither-disable-next-line constable-states\n address private _deprecated_cvxRewardStaker;\n // slither-disable-next-line constable-states\n uint256 private _deprecated_cvxDepositorPTokenId;\n // slither-disable-next-line constable-states\n address private _deprecated_curvePool;\n // slither-disable-next-line constable-states\n address private _deprecated_lpToken;\n // slither-disable-next-line constable-states\n address private _deprecated_oeth;\n // slither-disable-next-line constable-states\n address private _deprecated_weth;\n\n // Ordered list of pool assets\n // slither-disable-next-line constable-states\n uint128 private _deprecated_oethCoinIndex;\n // slither-disable-next-line constable-states\n uint128 private _deprecated_ethCoinIndex;\n\n // New immutable variables that must be set in the constructor\n address public immutable cvxDepositorAddress;\n IRewardStaking public immutable cvxRewardStaker;\n uint256 public immutable cvxDepositorPTokenId;\n ICurveETHPoolV1 public immutable curvePool;\n IERC20 public immutable lpToken;\n IERC20 public immutable oeth;\n IWETH9 public immutable weth;\n\n // Ordered list of pool assets\n uint128 public constant oethCoinIndex = 1;\n uint128 public constant ethCoinIndex = 0;\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier only works on functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balancesBefore = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffBefore = int256(balancesBefore[ethCoinIndex]) -\n int256(balancesBefore[oethCoinIndex]);\n\n _;\n\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balancesAfter = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffAfter = int256(balancesAfter[ethCoinIndex]) -\n int256(balancesAfter[oethCoinIndex]);\n\n if (diffBefore <= 0) {\n // If the pool was originally imbalanced in favor of OETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n }\n if (diffBefore >= 0) {\n // If the pool was originally imbalanced in favor of ETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n // Used to circumvent the stack too deep issue\n struct ConvexEthMetaConfig {\n address cvxDepositorAddress; //Address of the Convex depositor(AKA booster) for this pool\n address cvxRewardStakerAddress; //Address of the CVX rewards staker\n uint256 cvxDepositorPTokenId; //Pid of the pool referred to by Depositor and staker\n address oethAddress; //Address of OETH token\n address wethAddress; //Address of WETH\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n ConvexEthMetaConfig memory _convexConfig\n ) InitializableAbstractStrategy(_baseConfig) {\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveETHPoolV1(_baseConfig.platformAddress);\n\n cvxDepositorAddress = _convexConfig.cvxDepositorAddress;\n cvxRewardStaker = IRewardStaking(_convexConfig.cvxRewardStakerAddress);\n cvxDepositorPTokenId = _convexConfig.cvxDepositorPTokenId;\n oeth = IERC20(_convexConfig.oethAddress);\n weth = IWETH9(_convexConfig.wethAddress);\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. eg WETH\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets // WETH\n ) external onlyGovernor initializer {\n require(_assets.length == 1, \"Must have exactly one asset\");\n require(_assets[0] == address(weth), \"Asset not WETH\");\n\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit WETH into the Curve pool\n * @param _weth Address of Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to deposit.\n */\n function deposit(address _weth, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_weth, _amount);\n }\n\n function _deposit(address _weth, uint256 _wethAmount) internal {\n require(_wethAmount > 0, \"Must deposit something\");\n require(_weth == address(weth), \"Can only deposit WETH\");\n weth.withdraw(_wethAmount);\n\n emit Deposit(_weth, address(lpToken), _wethAmount);\n\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 oethToAdd = uint256(\n _max(\n 0,\n int256(balances[ethCoinIndex]) +\n int256(_wethAmount) -\n int256(balances[oethCoinIndex])\n )\n );\n\n /* Add so much OETH so that the pool ends up being balanced. And at minimum\n * add as much OETH as WETH and at maximum twice as much OETH.\n */\n oethToAdd = Math.max(oethToAdd, _wethAmount);\n oethToAdd = Math.min(oethToAdd, _wethAmount * 2);\n\n /* Mint OETH with a strategy that attempts to contribute to stability of OETH/WETH pool. Try\n * to mint so much OETH that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OETH minted will always be at least equal or greater\n * to WETH amount deployed. And never larger than twice the WETH amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(oethToAdd);\n\n emit Deposit(address(oeth), address(lpToken), oethToAdd);\n\n uint256[2] memory _amounts;\n _amounts[ethCoinIndex] = _wethAmount;\n _amounts[oethCoinIndex] = oethToAdd;\n\n uint256 valueInLpTokens = (_wethAmount + oethToAdd).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Do the deposit to the Curve pool\n // slither-disable-next-line arbitrary-send\n uint256 lpDeposited = curvePool.add_liquidity{ value: _wethAmount }(\n _amounts,\n minMintAmount\n );\n\n // Deposit the Curve pool's LP tokens into the Convex rewards pool\n require(\n IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n lpDeposited,\n true // Deposit with staking\n ),\n \"Depositing LP to Convex not successful\"\n );\n }\n\n /**\n * @notice Deposit the strategy's entire balance of WETH into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = weth.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(weth), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _weth Address of the Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to withdraw.\n */\n function withdraw(\n address _recipient,\n address _weth,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Invalid amount\");\n require(_weth == address(weth), \"Can only withdraw WETH\");\n\n emit Withdrawal(_weth, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(_amount);\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough WETH on balanced removal\n */\n uint256[2] memory _minWithdrawalAmounts = [uint256(0), uint256(0)];\n _minWithdrawalAmounts[ethCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OETH and any that was left in the strategy\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n\n // Transfer WETH to the recipient\n weth.deposit{ value: _amount }();\n require(\n weth.transfer(_recipient, _amount),\n \"Transfer of WETH not successful\"\n );\n }\n\n function calcTokenToBurn(uint256 _wethAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much WETH\n * we want we can determine how much of OETH we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolWETHBalance = curvePool.balances(ethCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolWETHBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_wethAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = cvxRewardStaker.balanceOf(address(this));\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[2] memory minWithdrawAmounts = [uint256(0), uint256(0)];\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(\n lpToken.balanceOf(address(this)),\n minWithdrawAmounts\n );\n\n // Burn all OETH\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Get the strategy contract's ether balance.\n // This includes all that was removed from the Curve pool and\n // any ether that was sitting in the strategy contract before the removal.\n uint256 ethBalance = address(this).balance;\n // Convert all the strategy contract's ether to WETH and transfer to the vault.\n weth.deposit{ value: ethBalance }();\n require(\n weth.transfer(vaultAddress, ethBalance),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethBalance);\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[2] memory amounts = [uint256(0), uint256(0)];\n amounts[oethCoinIndex] = _oTokens;\n\n // Convert OETH to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n\n // Deposit the Curve pool LP tokens to the Convex rewards pool\n require(\n IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n lpDeposited,\n true // Deposit with staking\n ),\n \"Failed to Deposit LP to Convex\"\n );\n\n emit Deposit(address(oeth), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough ETH.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove OTokens from the Curve pool\n uint256 oethToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n oethCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /**\n * @notice One-sided remove of ETH from the Curve pool, convert to WETH\n * and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for ETH.\n * @dev Curve pool LP tokens is used rather than WETH assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of ETH. Curve's `calc_token_amount` functioun does not include fees.\n * A 3rd party libary can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * caclulate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove ETH from the Curve pool\n uint256 ethAmount = _withdrawAndRemoveFromPool(_lpTokens, ethCoinIndex);\n\n // Convert ETH to WETH and transfer to the vault\n weth.deposit{ value: ethAmount }();\n require(\n weth.transfer(vaultAddress, ethAmount),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the Convex pool and\n * do a one-sided remove of ETH or OETH from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the Convex pool.\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = ETH, 1 = OETH.\n * @return coinsRemoved The amount of ETH or OETH removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Convex pool\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to ETH value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n // Apply slippage to ETH value\n uint256 minAmount = valueInEth.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Remove just the ETH from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV and CVX rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n cvxRewardStaker.getReward();\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _wethAmount) internal {\n // withdraw and unwrap with claim takes back the lpTokens\n // and also collects the rewards for deposit\n cvxRewardStaker.withdrawAndUnwrap(_wethAmount, true);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // Eth balance needed here for the balance check that happens from vault during depositing.\n balance = address(this).balance;\n uint256 lpTokens = cvxRewardStaker.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += (lpTokens * curvePool.get_virtual_price()) / 1e18;\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(weth);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @notice Accept unwrapped WETH\n */\n receive() external payable {}\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OETH (required for adding liquidity)\n // No approval is needed for ETH\n // slither-disable-next-line unused-return\n oeth.approve(platformAddress, type(uint256).max);\n\n // Approve Convex deposit contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Convex rewards pool\n // slither-disable-next-line unused-return\n lpToken.approve(cvxDepositorAddress, type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/ConvexGeneralizedMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { AbstractConvexMetaStrategy } from \"./AbstractConvexMetaStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract ConvexGeneralizedMetaStrategy is AbstractConvexMetaStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /* Take 3pool LP and deposit it to metapool. Take the LP from metapool\n * and deposit them to Convex.\n */\n function _lpDepositAll() internal override {\n IERC20 threePoolLp = IERC20(pTokenAddress);\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 threePoolLpBalance = threePoolLp.balanceOf(address(this));\n uint256 curve3PoolVirtualPrice = curvePool.get_virtual_price();\n uint256 threePoolLpDollarValue = threePoolLpBalance.mulTruncate(\n curve3PoolVirtualPrice\n );\n\n uint256[2] memory _amounts = [0, threePoolLpBalance];\n\n uint256 metapoolVirtualPrice = metapool.get_virtual_price();\n /**\n * First convert all the deposited tokens to dollar values,\n * then divide by virtual price to convert to metapool LP tokens\n * and apply the max slippage\n */\n uint256 minReceived = threePoolLpDollarValue\n .divPrecisely(metapoolVirtualPrice)\n .mulTruncate(uint256(1e18) - MAX_SLIPPAGE);\n\n uint256 metapoolLp = metapool.add_liquidity(_amounts, minReceived);\n\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n metapoolLp,\n true // Deposit with staking\n );\n\n require(success, \"Failed to deposit to Convex\");\n }\n\n /**\n * Withdraw the specified amount of tokens from the gauge. And use all the resulting tokens\n * to remove liquidity from metapool\n * @param num3CrvTokens Number of Convex 3pool LP tokens to withdraw from metapool\n */\n function _lpWithdraw(uint256 num3CrvTokens) internal override {\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n uint256 requiredMetapoolLpTokens = _calcCurveMetaTokenAmount(\n crvCoinIndex,\n num3CrvTokens\n );\n\n require(\n requiredMetapoolLpTokens <= gaugeTokens,\n string(\n bytes.concat(\n bytes(\"Attempting to withdraw \"),\n bytes(Strings.toString(requiredMetapoolLpTokens)),\n bytes(\", metapoolLP but only \"),\n bytes(Strings.toString(gaugeTokens)),\n bytes(\" available.\")\n )\n )\n );\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards for deposit\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n requiredMetapoolLpTokens,\n true\n );\n\n if (requiredMetapoolLpTokens > 0) {\n // slither-disable-next-line unused-return\n metapool.remove_liquidity_one_coin(\n requiredMetapoolLpTokens,\n int128(crvCoinIndex),\n num3CrvTokens\n );\n }\n }\n\n function _lpWithdrawAll() internal override {\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n gaugeTokens,\n true\n );\n\n if (gaugeTokens > 0) {\n uint256 burnDollarAmount = gaugeTokens.mulTruncate(\n metapool.get_virtual_price()\n );\n uint256 curve3PoolExpected = burnDollarAmount.divPrecisely(\n ICurvePool(platformAddress).get_virtual_price()\n );\n\n // Always withdraw all of the available metapool LP tokens (similar to how we always deposit all)\n // slither-disable-next-line unused-return\n metapool.remove_liquidity_one_coin(\n gaugeTokens,\n int128(crvCoinIndex),\n curve3PoolExpected -\n curve3PoolExpected.mulTruncate(maxWithdrawalSlippage)\n );\n }\n }\n}\n" + }, + "contracts/strategies/ConvexOUSDMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { AbstractConvexMetaStrategy } from \"./AbstractConvexMetaStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract ConvexOUSDMetaStrategy is AbstractConvexMetaStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /* Take 3pool LP and mint the corresponding amount of ousd. Deposit and stake that to\n * ousd Curve Metapool. Take the LP from metapool and deposit them to Convex.\n */\n function _lpDepositAll() internal override {\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 threePoolLpBalance = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n uint256 curve3PoolVirtualPrice = curvePool.get_virtual_price();\n uint256 threePoolLpDollarValue = threePoolLpBalance.mulTruncate(\n curve3PoolVirtualPrice\n );\n\n // safe to cast since min value is at least 0\n uint256 ousdToAdd = uint256(\n _max(\n 0,\n int256(\n metapool.balances(crvCoinIndex).mulTruncate(\n curve3PoolVirtualPrice\n )\n ) -\n int256(metapool.balances(mainCoinIndex)) +\n int256(threePoolLpDollarValue)\n )\n );\n\n /* Add so much OUSD so that the pool ends up being balanced. And at minimum\n * add twice as much OUSD as 3poolLP and at maximum at twice as\n * much OUSD.\n */\n ousdToAdd = Math.max(ousdToAdd, threePoolLpDollarValue);\n ousdToAdd = Math.min(ousdToAdd, threePoolLpDollarValue * 2);\n\n /* Mint OUSD with a strategy that attempts to contribute to stability of OUSD metapool. Try\n * to mint so much OUSD that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OUSD minted will always be at least equal or greater\n * to stablecoin(DAI, USDC, USDT) amount of 3CRVLP deployed. And never larger than twice the\n * stablecoin amount of 3CRVLP deployed even if it would have a further beneficial effect\n * on pool stability.\n */\n if (ousdToAdd > 0) {\n IVault(vaultAddress).mintForStrategy(ousdToAdd);\n }\n\n uint256[2] memory _amounts = [ousdToAdd, threePoolLpBalance];\n\n uint256 metapoolVirtualPrice = metapool.get_virtual_price();\n /**\n * First convert all the deposited tokens to dollar values,\n * then divide by virtual price to convert to metapool LP tokens\n * and apply the max slippage\n */\n uint256 minReceived = (ousdToAdd + threePoolLpDollarValue)\n .divPrecisely(metapoolVirtualPrice)\n .mulTruncate(uint256(1e18) - MAX_SLIPPAGE);\n\n uint256 metapoolLp = metapool.add_liquidity(_amounts, minReceived);\n\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n metapoolLp,\n true // Deposit with staking\n );\n\n require(success, \"Failed to deposit to Convex\");\n }\n\n /**\n * Withdraw the specified amount of tokens from the gauge. And use all the resulting tokens\n * to remove liquidity from metapool\n * @param num3CrvTokens Number of 3CRV tokens to withdraw from metapool\n */\n function _lpWithdraw(uint256 num3CrvTokens) internal override {\n ICurvePool curvePool = ICurvePool(platformAddress);\n /* The rate between coins in the metapool determines the rate at which metapool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much 3crvLp\n * we want we can determine how much of OUSD we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 crvPoolBalance = metapool.balances(crvCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * metapool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * metapoolLPToken.totalSupply()) / crvPoolBalance;\n // simplifying below to: `uint256 diff = (num3CrvTokens - 1) * k` causes loss of precision\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = crvPoolBalance * k -\n (crvPoolBalance - num3CrvTokens - 1) * k;\n uint256 lpToBurn = diff / 1e36;\n\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n require(\n lpToBurn <= gaugeTokens,\n string(\n bytes.concat(\n bytes(\"Attempting to withdraw \"),\n bytes(Strings.toString(lpToBurn)),\n bytes(\", metapoolLP but only \"),\n bytes(Strings.toString(gaugeTokens)),\n bytes(\" available.\")\n )\n )\n );\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards for deposit\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n lpToBurn,\n true\n );\n\n // calculate the min amount of OUSD expected for the specified amount of LP tokens\n uint256 minOUSDAmount = lpToBurn.mulTruncate(\n metapool.get_virtual_price()\n ) -\n num3CrvTokens.mulTruncate(curvePool.get_virtual_price()) -\n 1;\n\n // withdraw the liquidity from metapool\n uint256[2] memory _removedAmounts = metapool.remove_liquidity(\n lpToBurn,\n [minOUSDAmount, num3CrvTokens]\n );\n\n IVault(vaultAddress).burnForStrategy(_removedAmounts[mainCoinIndex]);\n }\n\n function _lpWithdrawAll() internal override {\n IERC20 metapoolErc20 = IERC20(address(metapool));\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n gaugeTokens,\n true\n );\n\n uint256[2] memory _minAmounts = [uint256(0), uint256(0)];\n uint256[2] memory _removedAmounts = metapool.remove_liquidity(\n metapoolErc20.balanceOf(address(this)),\n _minAmounts\n );\n\n IVault(vaultAddress).burnForStrategy(_removedAmounts[mainCoinIndex]);\n }\n}\n" + }, + "contracts/strategies/ConvexStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { IERC20, AbstractCurveStrategy, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\n/*\n * IMPORTANT(!) If ConvexStrategy needs to be re-deployed, it requires new\n * proxy contract with fresh storage slots. Changes in `AbstractCurveStrategy`\n * storage slots would break existing implementation.\n *\n * Remove this notice if ConvexStrategy is re-deployed\n */\ncontract ConvexStrategy is AbstractCurveStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n address internal cvxDepositorAddress;\n address internal cvxRewardStakerAddress;\n // slither-disable-next-line constable-states\n address private _deprecated_cvxRewardTokenAddress;\n uint256 internal cvxDepositorPTokenId;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * DAI, USDC, USDT\n * @param _pTokens Platform Token corresponding addresses\n * @param _cvxDepositorAddress Address of the Convex depositor(AKA booster) for this pool\n * @param _cvxRewardStakerAddress Address of the CVX rewards staker\n * @param _cvxDepositorPTokenId Pid of the pool referred to by Depositor and staker\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets,\n address[] calldata _pTokens,\n address _cvxDepositorAddress,\n address _cvxRewardStakerAddress,\n uint256 _cvxDepositorPTokenId\n ) external onlyGovernor initializer {\n require(_assets.length == 3, \"Must have exactly three assets\");\n // Should be set prior to abstract initialize call otherwise\n // abstractSetPToken calls will fail\n cvxDepositorAddress = _cvxDepositorAddress;\n cvxRewardStakerAddress = _cvxRewardStakerAddress;\n cvxDepositorPTokenId = _cvxDepositorPTokenId;\n pTokenAddress = _pTokens[0];\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n function _lpDepositAll() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // Deposit with staking\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n pToken.balanceOf(address(this)),\n true\n );\n require(success, \"Failed to deposit to Convex\");\n }\n\n function _lpWithdraw(uint256 numCrvTokens) internal override {\n uint256 gaugePTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n // Not enough in this contract or in the Gauge, can't proceed\n require(numCrvTokens > gaugePTokens, \"Insufficient 3CRV balance\");\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards to this\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n numCrvTokens,\n true\n );\n }\n\n function _lpWithdrawAll() internal override {\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards to this\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n IRewardStaking(cvxRewardStakerAddress).balanceOf(address(this)),\n true\n );\n }\n\n function _approveBase() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // 3Pool for LP token (required for removing liquidity)\n pToken.safeApprove(platformAddress, 0);\n pToken.safeApprove(platformAddress, type(uint256).max);\n // Gauge for LP token\n pToken.safeApprove(cvxDepositorAddress, 0);\n pToken.safeApprove(cvxDepositorAddress, type(uint256).max);\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 contractPTokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n uint256 gaugePTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n uint256 totalPTokens = contractPTokens + gaugePTokens;\n\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (totalPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = (totalPTokens * virtual_price) / 1e18;\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = value.scaleBy(assetDecimals, 18) / 3;\n }\n }\n\n /**\n * @dev Collect accumulated CRV and CVX and send to Vault.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n IRewardStaking(cvxRewardStakerAddress).getReward();\n _collectRewardTokens();\n }\n}\n" + }, + "contracts/strategies/CurveAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for a Curve pool using an OToken.\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { ICurveStableSwapNG } from \"../interfaces/ICurveStableSwapNG.sol\";\nimport { ICurveLiquidityGaugeV6 } from \"../interfaces/ICurveLiquidityGaugeV6.sol\";\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\nimport { ICurveMinter } from \"../interfaces/ICurveMinter.sol\";\n\ncontract CurveAMOStrategy is InitializableAbstractStrategy {\n using SafeCast for uint256;\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n /**\n * @dev a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n // New immutable variables that must be set in the constructor\n /**\n * @notice Address of the hard asset (weth, usdt, usdc).\n */\n IERC20 public immutable hardAsset;\n\n /**\n * @notice Address of the OTOKEN token contract.\n */\n IERC20 public immutable oToken;\n\n /**\n * @notice Address of the LP (Liquidity Provider) token contract.\n */\n IERC20 public immutable lpToken;\n\n /**\n * @notice Address of the Curve StableSwap NG pool contract.\n */\n ICurveStableSwapNG public immutable curvePool;\n\n /**\n * @notice Address of the Curve X-Chain Liquidity Gauge contract.\n */\n ICurveLiquidityGaugeV6 public immutable gauge;\n\n /**\n * @notice Address of the Curve Minter contract.\n */\n ICurveMinter public immutable minter;\n\n /**\n * @notice Index of the OTOKEN and hardAsset in the Curve pool.\n */\n uint128 public immutable otokenCoinIndex;\n uint128 public immutable hardAssetCoinIndex;\n\n /**\n * @notice Decimals of the OTOKEN and hardAsset.\n */\n uint8 public immutable decimalsOToken;\n uint8 public immutable decimalsHardAsset;\n\n /**\n * @notice Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n uint256 public maxSlippage;\n\n event MaxSlippageUpdated(uint256 newMaxSlippage);\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the hard asset and OToken balances in the Curve pool\n uint256[] memory balancesBefore = curvePool.get_balances();\n // diff = hardAsset balance - OTOKEN balance\n int256 diffBefore = (\n balancesBefore[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() - balancesBefore[otokenCoinIndex].toInt256();\n\n _;\n\n // Get the hard asset and OToken balances in the Curve pool\n uint256[] memory balancesAfter = curvePool.get_balances();\n // diff = hardAsset balance - OTOKEN balance\n int256 diffAfter = (\n balancesAfter[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() - balancesAfter[otokenCoinIndex].toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OTOKEN, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of hardAsset, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _otoken,\n address _hardAsset,\n address _gauge,\n address _minter\n ) InitializableAbstractStrategy(_baseConfig) {\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveStableSwapNG(_baseConfig.platformAddress);\n minter = ICurveMinter(_minter);\n\n oToken = IERC20(_otoken);\n hardAsset = IERC20(_hardAsset);\n gauge = ICurveLiquidityGaugeV6(_gauge);\n decimalsHardAsset = IBasicToken(_hardAsset).decimals();\n decimalsOToken = IBasicToken(_otoken).decimals();\n\n (hardAssetCoinIndex, otokenCoinIndex) = curvePool.coins(0) == _hardAsset\n ? (0, 1)\n : (1, 0);\n require(\n curvePool.coins(otokenCoinIndex) == _otoken &&\n curvePool.coins(hardAssetCoinIndex) == _hardAsset,\n \"Invalid coin indexes\"\n );\n require(gauge.lp_token() == address(curvePool), \"Invalid pool\");\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV\n * @param _maxSlippage Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV\n uint256 _maxSlippage\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n address[] memory _assets = new address[](1);\n _assets[0] = address(hardAsset);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n _setMaxSlippage(_maxSlippage);\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit hard asset into the Curve pool\n * @param _hardAsset Address of hard asset contract.\n * @param _amount Amount of hard asset to deposit.\n */\n function deposit(address _hardAsset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_hardAsset, _amount);\n }\n\n function _deposit(address _hardAsset, uint256 _hardAssetAmount) internal {\n require(_hardAssetAmount > 0, \"Must deposit something\");\n require(_hardAsset == address(hardAsset), \"Unsupported asset\");\n\n emit Deposit(_hardAsset, address(lpToken), _hardAssetAmount);\n uint256 scaledHardAssetAmount = _hardAssetAmount.scaleBy(\n decimalsOToken,\n decimalsHardAsset\n );\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 otokenToAdd = uint256(\n _max(\n 0,\n (\n balances[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() +\n scaledHardAssetAmount.toInt256() -\n balances[otokenCoinIndex].toInt256()\n )\n );\n\n /* Add so much OTOKEN so that the pool ends up being balanced. And at minimum\n * add as much OTOKEN as hard asset and at maximum twice as much OTOKEN.\n */\n otokenToAdd = Math.max(otokenToAdd, scaledHardAssetAmount);\n otokenToAdd = Math.min(otokenToAdd, scaledHardAssetAmount * 2);\n\n /* Mint OTOKEN with a strategy that attempts to contribute to stability of OTOKEN/hardAsset pool. Try\n * to mint so much OTOKEN that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OTOKEN minted will always be at least equal or greater\n * to hardAsset amount deployed. And never larger than twice the hardAsset amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(otokenToAdd);\n\n emit Deposit(address(oToken), address(lpToken), otokenToAdd);\n\n uint256[] memory _amounts = new uint256[](2);\n _amounts[hardAssetCoinIndex] = _hardAssetAmount;\n _amounts[otokenCoinIndex] = otokenToAdd;\n\n uint256 valueInLpTokens = (scaledHardAssetAmount + otokenToAdd)\n .divPrecisely(curvePool.get_virtual_price());\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Do the deposit to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(_amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool's LP tokens into the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n /**\n * @notice Deposit the strategy's entire balance of hardAsset into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = hardAsset.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(hardAsset), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw hardAsset and OTOKEN from the Curve pool, burn the OTOKEN,\n * transfer hardAsset to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _hardAsset Address of the hardAsset contract.\n * @param _amount Amount of hardAsset to withdraw.\n */\n function withdraw(\n address _recipient,\n address _hardAsset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(\n _hardAsset == address(hardAsset),\n \"Can only withdraw hard asset\"\n );\n\n emit Withdrawal(_hardAsset, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(\n _amount.scaleBy(decimalsOToken, decimalsHardAsset)\n );\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough hardAsset on balanced removal\n */\n uint256[] memory _minWithdrawalAmounts = new uint256[](2);\n _minWithdrawalAmounts[hardAssetCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OTOKEN and any that was left in the strategy\n uint256 otokenToBurn = oToken.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n\n // Transfer hardAsset to the recipient\n hardAsset.safeTransfer(_recipient, _amount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n function calcTokenToBurn(uint256 _hardAssetAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much hardAsset\n * we want we can determine how much of OTOKEN we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolHardAssetBalance = curvePool\n .balances(hardAssetCoinIndex)\n .scaleBy(decimalsOToken, decimalsHardAsset);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolHardAssetBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_hardAssetAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all hardAsset and OTOKEN from the Curve pool, burn the OTOKEN,\n * transfer hardAsset to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = gauge.balanceOf(address(this));\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[] memory minWithdrawAmounts = new uint256[](2);\n\n // Check balance of LP tokens in the strategy, if 0 return\n uint256 lpBalance = lpToken.balanceOf(address(this));\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n if (lpBalance > 0) {\n curvePool.remove_liquidity(lpBalance, minWithdrawAmounts);\n }\n\n // Burn all OTOKEN\n uint256 otokenToBurn = oToken.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n // Get the strategy contract's hardAsset balance.\n // This includes all that was removed from the Curve pool and\n // any hardAsset that was sitting in the strategy contract before the removal.\n uint256 hardAssetBalance = hardAsset.balanceOf(address(this));\n hardAsset.safeTransfer(vaultAddress, hardAssetBalance);\n\n if (hardAssetBalance > 0)\n emit Withdrawal(\n address(hardAsset),\n address(lpToken),\n hardAssetBalance\n );\n if (otokenToBurn > 0)\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many hardAsset.\n * The OToken/Asset, eg OTOKEN/hardAsset, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[] memory amounts = new uint256[](2);\n amounts[otokenCoinIndex] = _oTokens;\n\n // Convert OTOKEN to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool LP tokens to the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Deposit(address(oToken), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough hardAsset.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from gauge and remove OTokens from the Curve pool\n uint256 otokenToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n otokenCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n }\n\n /**\n * @notice One-sided remove of hardAsset from the Curve pool and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many hardAsset.\n * The OToken/Asset, eg OTOKEN/hardAsset, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for hardAsset.\n * @dev Curve pool LP tokens is used rather than hardAsset assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of hardAsset. Curve's `calc_token_amount` function does not include fees.\n * A 3rd party library can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * calculate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Curve gauge and remove hardAsset from the Curve pool\n uint256 hardAssetAmount = _withdrawAndRemoveFromPool(\n _lpTokens,\n hardAssetCoinIndex\n );\n\n // Transfer hardAsset to the vault\n hardAsset.safeTransfer(vaultAddress, hardAssetAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(hardAsset), address(lpToken), hardAssetAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the gauge and\n * do a one-sided remove of hardAsset or OTOKEN from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the gauge\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = hardAsset, 1 = OTOKEN.\n * @return coinsRemoved The amount of hardAsset or OTOKEN removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Curve gauge\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to hardAsset value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n\n if (coinIndex == hardAssetCoinIndex) {\n valueInEth = valueInEth.scaleBy(decimalsHardAsset, decimalsOToken);\n }\n\n // Apply slippage to hardAsset value\n uint256 minAmount = valueInEth.mulTruncate(uint256(1e18) - maxSlippage);\n\n // Remove just the hardAsset from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOtokenSupply = oToken.totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOtokenSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV rewards from inflation\n minter.mint(address(gauge));\n\n // Collect extra gauge rewards (outside of CRV)\n gauge.claim_rewards();\n\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _lpAmount) internal {\n require(\n gauge.balanceOf(address(this)) >= _lpAmount,\n \"Insufficient LP tokens\"\n );\n // withdraw lp tokens from the gauge without claiming rewards\n gauge.withdraw(_lpAmount);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(hardAsset), \"Unsupported asset\");\n\n // hardAsset balance needed here for the balance check that happens from vault during depositing.\n balance = hardAsset.balanceOf(address(this));\n uint256 lpTokens = gauge.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += ((lpTokens * curvePool.get_virtual_price()) / 1e18)\n .scaleBy(decimalsHardAsset, decimalsOToken);\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(hardAsset);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Sets the maximum slippage allowed for any swap/liquidity operation\n * @param _maxSlippage Maximum slippage allowed, 1e18 = 100%.\n */\n function setMaxSlippage(uint256 _maxSlippage) external onlyGovernor {\n _setMaxSlippage(_maxSlippage);\n }\n\n function _setMaxSlippage(uint256 _maxSlippage) internal {\n require(_maxSlippage <= 5e16, \"Slippage must be less than 100%\");\n maxSlippage = _maxSlippage;\n emit MaxSlippageUpdated(_maxSlippage);\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OTOKEN (required for adding liquidity)\n // slither-disable-next-line unused-return\n oToken.approve(platformAddress, type(uint256).max);\n\n // Approve Curve pool for hardAsset (required for adding liquidity)\n // slither-disable-next-line unused-return\n hardAsset.safeApprove(platformAddress, type(uint256).max);\n\n // Approve Curve gauge contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Curve gauge.\n // slither-disable-next-line unused-return\n lpToken.approve(address(gauge), type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/CurvePoolBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport { ICampaignRemoteManager } from \"../interfaces/ICampaignRemoteManager.sol\";\n\n/// @title CurvePoolBooster\n/// @author Origin Protocol\n/// @notice Contract to manage interactions with VotemarketV2 for a dedicated Curve pool/gauge.\ncontract CurvePoolBooster is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n ////////////////////////////////////////////////////\n /// --- CONSTANTS && IMMUTABLES\n ////////////////////////////////////////////////////\n /// @notice Base fee for the contract, 100%\n uint16 public constant FEE_BASE = 10_000;\n\n /// @notice Address of the gauge to manage\n address public immutable gauge;\n\n /// @notice Address of the reward token\n address public immutable rewardToken;\n\n /// @notice Chain id of the target chain\n uint256 public immutable targetChainId;\n\n ////////////////////////////////////////////////////\n /// --- STORAGE\n ////////////////////////////////////////////////////\n /// @notice Fee in FEE_BASE unit payed when managing campaign.\n uint16 public fee;\n\n /// @notice Address of the fee collector\n address public feeCollector;\n\n /// @notice Address of the campaignRemoteManager linked to VotemarketV2\n address public campaignRemoteManager;\n\n /// @notice Address of votemarket in L2\n address public votemarket;\n\n /// @notice Id of the campaign created\n uint256 public campaignId;\n\n ////////////////////////////////////////////////////\n /// --- EVENTS\n ////////////////////////////////////////////////////\n event FeeUpdated(uint16 newFee);\n event FeeCollected(address feeCollector, uint256 feeAmount);\n event FeeCollectorUpdated(address newFeeCollector);\n event VotemarketUpdated(address newVotemarket);\n event CampaignRemoteManagerUpdated(address newCampaignRemoteManager);\n event CampaignCreated(\n address gauge,\n address rewardToken,\n uint256 maxRewardPerVote,\n uint256 totalRewardAmount\n );\n event CampaignIdUpdated(uint256 newId);\n event CampaignClosed(uint256 campaignId);\n event TotalRewardAmountUpdated(uint256 extraTotalRewardAmount);\n event NumberOfPeriodsUpdated(uint8 extraNumberOfPeriods);\n event RewardPerVoteUpdated(uint256 newMaxRewardPerVote);\n event TokensRescued(address token, uint256 amount, address receiver);\n\n ////////////////////////////////////////////////////\n /// --- CONSTRUCTOR && INITIALIZATION\n ////////////////////////////////////////////////////\n constructor(\n uint256 _targetChainId,\n address _rewardToken,\n address _gauge\n ) {\n targetChainId = _targetChainId;\n rewardToken = _rewardToken;\n gauge = _gauge;\n\n // Prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /// @notice initialize function, to set up initial internal state\n /// @param _strategist Address of the strategist\n /// @param _fee Fee in FEE_BASE unit payed when managing campaign\n /// @param _feeCollector Address of the fee collector\n function initialize(\n address _strategist,\n uint16 _fee,\n address _feeCollector,\n address _campaignRemoteManager,\n address _votemarket\n ) external onlyGovernor initializer {\n _setStrategistAddr(_strategist);\n _setFee(_fee);\n _setFeeCollector(_feeCollector);\n _setCampaignRemoteManager(_campaignRemoteManager);\n _setVotemarket(_votemarket);\n }\n\n ////////////////////////////////////////////////////\n /// --- MUTATIVE FUNCTIONS\n ////////////////////////////////////////////////////\n /// @notice Create a new campaign on VotemarketV2\n /// @dev This will use all token available in this contract\n /// @param numberOfPeriods Duration of the campaign in weeks\n /// @param maxRewardPerVote Maximum reward per vote to distribute, to avoid overspending\n /// @param blacklist List of addresses to exclude from the campaign\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function createCampaign(\n uint8 numberOfPeriods,\n uint256 maxRewardPerVote,\n address[] calldata blacklist,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId == 0, \"Campaign already created\");\n require(numberOfPeriods > 1, \"Invalid number of periods\");\n require(maxRewardPerVote > 0, \"Invalid reward per vote\");\n\n // Handle fee (if any)\n uint256 balanceSubFee = _handleFee();\n\n // Approve the balanceSubFee to the campaign manager\n IERC20(rewardToken).safeApprove(campaignRemoteManager, 0);\n IERC20(rewardToken).safeApprove(campaignRemoteManager, balanceSubFee);\n\n // Create a new campaign\n ICampaignRemoteManager(campaignRemoteManager).createCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignCreationParams({\n chainId: targetChainId,\n gauge: gauge,\n manager: address(this),\n rewardToken: rewardToken,\n numberOfPeriods: numberOfPeriods,\n maxRewardPerVote: maxRewardPerVote,\n totalRewardAmount: balanceSubFee,\n addresses: blacklist,\n hook: address(0),\n isWhitelist: false\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit CampaignCreated(\n gauge,\n rewardToken,\n maxRewardPerVote,\n balanceSubFee\n );\n }\n\n /// @notice Manage the total reward amount of the campaign\n /// @dev This function should be called after the campaign is created\n /// @dev This will use all the token available in this contract\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageTotalRewardAmount(\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n\n // Handle fee (if any)\n uint256 balanceSubFee = _handleFee();\n\n // Approve the total reward amount to the campaign manager\n IERC20(rewardToken).safeApprove(campaignRemoteManager, 0);\n IERC20(rewardToken).safeApprove(campaignRemoteManager, balanceSubFee);\n\n // Manage the campaign\n // https://github.com/stake-dao/votemarket-v2/blob/main/packages/votemarket/src/Votemarket.sol#L668\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: 0,\n totalRewardAmount: balanceSubFee,\n maxRewardPerVote: 0\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit TotalRewardAmountUpdated(balanceSubFee);\n }\n\n /// @notice Manage the number of periods of the campaign\n /// @dev This function should be called after the campaign is created\n /// @param extraNumberOfPeriods Number of additional periods (cannot be 0)\n /// that will be added to already existing amount of periods.\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageNumberOfPeriods(\n uint8 extraNumberOfPeriods,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n require(extraNumberOfPeriods > 0, \"Invalid number of periods\");\n\n // Manage the campaign\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: extraNumberOfPeriods,\n totalRewardAmount: 0,\n maxRewardPerVote: 0\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit NumberOfPeriodsUpdated(extraNumberOfPeriods);\n }\n\n /// @notice Manage the reward per vote of the campaign\n /// @dev This function should be called after the campaign is created\n /// @param newMaxRewardPerVote New maximum reward per vote\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageRewardPerVote(\n uint256 newMaxRewardPerVote,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n require(newMaxRewardPerVote > 0, \"Invalid reward per vote\");\n\n // Manage the campaign\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: 0,\n totalRewardAmount: 0,\n maxRewardPerVote: newMaxRewardPerVote\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit RewardPerVoteUpdated(newMaxRewardPerVote);\n }\n\n /// @notice Close the campaign.\n /// @dev This function only work on the L2 chain. Not on mainnet.\n /// @dev The _campaignId parameter is not related to the campaignId of this contract, allowing greater flexibility.\n /// @param _campaignId Id of the campaign to close\n function closeCampaign(\n uint256 _campaignId,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n ICampaignRemoteManager(campaignRemoteManager).closeCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignClosingParams({\n campaignId: campaignId\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n emit CampaignClosed(_campaignId);\n }\n\n /// @notice calculate the fee amount and transfer it to the feeCollector\n /// @return Balance after fee\n function _handleFee() internal returns (uint256) {\n // Cache current rewardToken balance\n uint256 balance = IERC20(rewardToken).balanceOf(address(this));\n require(balance > 0, \"No reward to manage\");\n\n uint256 feeAmount = (balance * fee) / FEE_BASE;\n\n // If there is a fee, transfer it to the feeCollector\n if (feeAmount > 0) {\n // Transfer the fee to the feeCollector\n IERC20(rewardToken).safeTransfer(feeCollector, feeAmount);\n emit FeeCollected(feeCollector, feeAmount);\n\n return IERC20(rewardToken).balanceOf(address(this));\n }\n\n // Return remaining balance\n return balance;\n }\n\n ////////////////////////////////////////////////////\n /// --- GOVERNANCE && OPERATION\n ////////////////////////////////////////////////////\n /// @notice Set the campaign id\n /// @dev Only callable by the governor or strategist\n /// @param _campaignId New campaign id\n function setCampaignId(uint256 _campaignId)\n external\n onlyGovernorOrStrategist\n {\n campaignId = _campaignId;\n emit CampaignIdUpdated(_campaignId);\n }\n\n /// @notice Rescue ETH from the contract\n /// @dev Only callable by the governor or strategist\n /// @param receiver Address to receive the ETH\n function rescueETH(address receiver)\n external\n nonReentrant\n onlyGovernorOrStrategist\n {\n require(receiver != address(0), \"Invalid receiver\");\n uint256 balance = address(this).balance;\n (bool success, ) = receiver.call{ value: balance }(\"\");\n require(success, \"Transfer failed\");\n emit TokensRescued(address(0), balance, receiver);\n }\n\n /// @notice Rescue ERC20 tokens from the contract\n /// @dev Only callable by the governor or strategist\n /// @param token Address of the token to rescue\n function rescueToken(address token, address receiver)\n external\n nonReentrant\n onlyGovernor\n {\n require(receiver != address(0), \"Invalid receiver\");\n uint256 balance = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransfer(receiver, balance);\n emit TokensRescued(token, balance, receiver);\n }\n\n /// @notice Set the fee\n /// @dev Only callable by the governor\n /// @param _fee New fee\n function setFee(uint16 _fee) external onlyGovernor {\n _setFee(_fee);\n }\n\n /// @notice Internal logic to set the fee\n function _setFee(uint16 _fee) internal {\n require(_fee <= FEE_BASE / 2, \"Fee too high\");\n fee = _fee;\n emit FeeUpdated(_fee);\n }\n\n /// @notice Set the fee collector\n /// @dev Only callable by the governor\n /// @param _feeCollector New fee collector\n function setFeeCollector(address _feeCollector) external onlyGovernor {\n _setFeeCollector(_feeCollector);\n }\n\n /// @notice Internal logic to set the fee collector\n function _setFeeCollector(address _feeCollector) internal {\n require(_feeCollector != address(0), \"Invalid fee collector\");\n feeCollector = _feeCollector;\n emit FeeCollectorUpdated(_feeCollector);\n }\n\n /// @notice Set the campaignRemoteManager\n /// @param _campaignRemoteManager New campaignRemoteManager address\n function setCampaignRemoteManager(address _campaignRemoteManager)\n external\n onlyGovernor\n {\n _setCampaignRemoteManager(_campaignRemoteManager);\n }\n\n /// @notice Internal logic to set the campaignRemoteManager\n /// @param _campaignRemoteManager New campaignRemoteManager address\n function _setCampaignRemoteManager(address _campaignRemoteManager)\n internal\n {\n require(\n _campaignRemoteManager != address(0),\n \"Invalid campaignRemoteManager\"\n );\n campaignRemoteManager = _campaignRemoteManager;\n emit CampaignRemoteManagerUpdated(_campaignRemoteManager);\n }\n\n /// @notice Set the votemarket address\n /// @param _votemarket New votemarket address\n function setVotemarket(address _votemarket) external onlyGovernor {\n _setVotemarket(_votemarket);\n }\n\n /// @notice Internal logic to set the votemarket address\n function _setVotemarket(address _votemarket) internal onlyGovernor {\n require(_votemarket != address(0), \"Invalid votemarket\");\n votemarket = _votemarket;\n emit VotemarketUpdated(_votemarket);\n }\n\n receive() external payable {}\n}\n" + }, + "contracts/strategies/Generalized4626Strategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Generalized 4626 Strategy\n * @notice Investment strategy for ERC-4626 Tokenized Vaults\n * @author Origin Protocol Inc\n */\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\ncontract Generalized4626Strategy is InitializableAbstractStrategy {\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecate_shareToken;\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecate_assetToken;\n\n IERC20 public immutable shareToken;\n IERC20 public immutable assetToken;\n\n // For future use\n uint256[50] private __gap;\n\n /**\n * @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n * and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n * @param _assetToken Address of the ERC-4626 asset token. eg frxETH or DAI\n */\n constructor(BaseStrategyConfig memory _baseConfig, address _assetToken)\n InitializableAbstractStrategy(_baseConfig)\n {\n shareToken = IERC20(_baseConfig.platformAddress);\n assetToken = IERC20(_assetToken);\n }\n\n function initialize() external virtual onlyGovernor initializer {\n address[] memory rewardTokens = new address[](0);\n address[] memory assets = new address[](1);\n address[] memory pTokens = new address[](1);\n\n assets[0] = address(assetToken);\n pTokens[0] = address(platformAddress);\n\n InitializableAbstractStrategy._initialize(\n rewardTokens,\n assets,\n pTokens\n );\n }\n\n /**\n * @dev Deposit assets by converting them to shares\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit assets by converting them to shares\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal virtual {\n require(_amount > 0, \"Must deposit something\");\n require(_asset == address(assetToken), \"Unexpected asset address\");\n\n // slither-disable-next-line unused-return\n IERC4626(platformAddress).deposit(_amount, address(this));\n emit Deposit(_asset, address(shareToken), _amount);\n }\n\n /**\n * @dev Deposit the entire balance of assetToken to gain shareToken\n */\n function depositAll() external virtual override onlyVault nonReentrant {\n uint256 balance = assetToken.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(assetToken), balance);\n }\n }\n\n /**\n * @dev Withdraw asset by burning shares\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n require(_asset == address(assetToken), \"Unexpected asset address\");\n\n // slither-disable-next-line unused-return\n IERC4626(platformAddress).withdraw(_amount, _recipient, address(this));\n emit Withdrawal(_asset, address(shareToken), _amount);\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / share tokens\n */\n function _abstractSetPToken(address, address) internal virtual override {\n _approveBase();\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll()\n external\n virtual\n override\n onlyVaultOrGovernor\n nonReentrant\n {\n uint256 shareBalance = shareToken.balanceOf(address(this));\n uint256 assetAmount = 0;\n if (shareBalance > 0) {\n assetAmount = IERC4626(platformAddress).redeem(\n shareBalance,\n vaultAddress,\n address(this)\n );\n emit Withdrawal(\n address(assetToken),\n address(shareToken),\n assetAmount\n );\n }\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(_asset == address(assetToken), \"Unexpected asset address\");\n /* We are intentionally not counting the amount of assetToken parked on the\n * contract toward the checkBalance. The deposit and withdraw functions\n * should not result in assetToken being unused and owned by this strategy\n * contract.\n */\n IERC4626 platform = IERC4626(platformAddress);\n return platform.previewRedeem(platform.balanceOf(address(this)));\n }\n\n /**\n * @notice Governor approves the ERC-4626 Tokenized Vault to spend the asset.\n */\n function safeApproveAllTokens() external override onlyGovernor {\n _approveBase();\n }\n\n function _approveBase() internal virtual {\n // Approval the asset to be transferred to the ERC-4626 Tokenized Vault.\n // Used by the ERC-4626 deposit() and mint() functions\n // slither-disable-next-line unused-return\n assetToken.approve(platformAddress, type(uint256).max);\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset)\n public\n view\n virtual\n override\n returns (bool)\n {\n return _asset == address(assetToken);\n }\n\n /**\n * @notice is not supported for this strategy as the asset and\n * ERC-4626 Tokenized Vault are set at deploy time.\n * @dev If the ERC-4626 Tokenized Vault needed to be changed, a new\n * contract would need to be deployed and the proxy updated.\n */\n function setPTokenAddress(address, address) external override onlyGovernor {\n revert(\"unsupported function\");\n }\n\n /**\n * @notice is not supported for this strategy as the asset and\n * ERC-4626 Tokenized Vault are set at deploy time.\n * @dev If the ERC-4626 Tokenized Vault needed to be changed, a new\n * contract would need to be deployed and the proxy updated.\n */\n function removePToken(uint256) external override onlyGovernor {\n revert(\"unsupported function\");\n }\n}\n" + }, + "contracts/strategies/Generalized4626USDTStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IUSDT {\n // Tether's approve does not return a bool like standard IERC20 contracts\n // slither-disable-next-line erc20-interface\n function approve(address _spender, uint256 _value) external;\n}\n\n/**\n * @title Generalized 4626 Strategy when asset is Tether USD (USDT)\n * @notice Investment strategy for ERC-4626 Tokenized Vaults for the USDT asset.\n * @author Origin Protocol Inc\n */\nimport { Generalized4626Strategy } from \"./Generalized4626Strategy.sol\";\n\ncontract Generalized4626USDTStrategy is Generalized4626Strategy {\n /**\n * @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n * and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n * @param _assetToken Address of the ERC-4626 asset token. eg frxETH or DAI\n */\n constructor(BaseStrategyConfig memory _baseConfig, address _assetToken)\n Generalized4626Strategy(_baseConfig, _assetToken)\n {}\n\n /// @dev Override for Tether as USDT does not return a bool on approve.\n /// Using assetToken.approve will fail as it expects a bool return value\n function _approveBase() internal virtual override {\n // Approval the asset to be transferred to the ERC-4626 Tokenized Vault.\n // Used by the ERC-4626 deposit() and mint() functions\n // slither-disable-next-line unused-return\n IUSDT(address(assetToken)).approve(platformAddress, type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/IAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface for Aaves Lending Pool\n * Documentation: https://developers.aave.com/#lendingpool\n */\ninterface IAaveLendingPool {\n /**\n * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.\n * - E.g. User deposits 100 USDC and gets in return 100 aUSDC\n * @param asset The address of the underlying asset to deposit\n * @param amount The amount to be deposited\n * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user\n * wants to receive them on his own wallet, or a different address if the beneficiary of aTokens\n * is a different wallet\n * @param referralCode Code used to register the integrator originating the operation, for potential rewards.\n * 0 if the action is executed directly by the user, without any middle-man\n **/\n function deposit(\n address asset,\n uint256 amount,\n address onBehalfOf,\n uint16 referralCode\n ) external;\n\n /**\n * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned\n * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC\n * @param asset The address of the underlying asset to withdraw\n * @param amount The underlying amount to be withdrawn\n * - Send the value type(uint256).max in order to withdraw the whole aToken balance\n * @param to Address that will receive the underlying, same as msg.sender if the user\n * wants to receive it on his own wallet, or a different address if the beneficiary is a\n * different wallet\n * @return The final amount withdrawn\n **/\n function withdraw(\n address asset,\n uint256 amount,\n address to\n ) external returns (uint256);\n}\n\n/**\n * @dev Interface for Aaves Lending Pool\n * Documentation: https://developers.aave.com/#lendingpooladdressesprovider\n */\ninterface ILendingPoolAddressesProvider {\n /**\n * @notice Get the current address for Aave LendingPool\n * @dev Lending pool is the core contract on which to call deposit\n */\n function getLendingPool() external view returns (address);\n}\n" + }, + "contracts/strategies/IAaveIncentivesController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IAaveIncentivesController {\n event RewardsAccrued(address indexed user, uint256 amount);\n\n event RewardsClaimed(\n address indexed user,\n address indexed to,\n uint256 amount\n );\n\n event RewardsClaimed(\n address indexed user,\n address indexed to,\n address indexed claimer,\n uint256 amount\n );\n\n event ClaimerSet(address indexed user, address indexed claimer);\n\n /*\n * @dev Returns the configuration of the distribution for a certain asset\n * @param asset The address of the reference asset of the distribution\n * @return The asset index, the emission per second and the last updated timestamp\n **/\n function getAssetData(address asset)\n external\n view\n returns (\n uint256,\n uint256,\n uint256\n );\n\n /**\n * @dev Whitelists an address to claim the rewards on behalf of another address\n * @param user The address of the user\n * @param claimer The address of the claimer\n */\n function setClaimer(address user, address claimer) external;\n\n /**\n * @dev Returns the whitelisted claimer for a certain address (0x0 if not set)\n * @param user The address of the user\n * @return The claimer address\n */\n function getClaimer(address user) external view returns (address);\n\n /**\n * @dev Configure assets for a certain rewards emission\n * @param assets The assets to incentivize\n * @param emissionsPerSecond The emission for each asset\n */\n function configureAssets(\n address[] calldata assets,\n uint256[] calldata emissionsPerSecond\n ) external;\n\n /**\n * @dev Called by the corresponding asset on any update that affects the rewards distribution\n * @param asset The address of the user\n * @param userBalance The balance of the user of the asset in the lending pool\n * @param totalSupply The total supply of the asset in the lending pool\n **/\n function handleAction(\n address asset,\n uint256 userBalance,\n uint256 totalSupply\n ) external;\n\n /**\n * @dev Returns the total of rewards of an user, already accrued + not yet accrued\n * @param user The address of the user\n * @return The rewards\n **/\n function getRewardsBalance(address[] calldata assets, address user)\n external\n view\n returns (uint256);\n\n /**\n * @dev Claims reward for an user, on all the assets of the lending pool,\n * accumulating the pending rewards\n * @param amount Amount of rewards to claim\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewards(\n address[] calldata assets,\n uint256 amount,\n address to\n ) external returns (uint256);\n\n /**\n * @dev Claims reward for an user on behalf, on all the assets of the\n * lending pool, accumulating the pending rewards. The caller must\n * be whitelisted via \"allowClaimOnBehalf\" function by the RewardsAdmin role manager\n * @param amount Amount of rewards to claim\n * @param user Address to check and claim rewards\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewardsOnBehalf(\n address[] calldata assets,\n uint256 amount,\n address user,\n address to\n ) external returns (uint256);\n\n /**\n * @dev returns the unclaimed rewards of the user\n * @param user the address of the user\n * @return the unclaimed user rewards\n */\n function getUserUnclaimedRewards(address user)\n external\n view\n returns (uint256);\n\n /**\n * @dev returns the unclaimed rewards of the user\n * @param user the address of the user\n * @param asset The asset to incentivize\n * @return the user index for the asset\n */\n function getUserAssetData(address user, address asset)\n external\n view\n returns (uint256);\n\n /**\n * @dev for backward compatibility with previous implementation of the Incentives controller\n */\n function REWARD_TOKEN() external view returns (address);\n\n /**\n * @dev for backward compatibility with previous implementation of the Incentives controller\n */\n function PRECISION() external view returns (uint8);\n\n /**\n * @dev Gets the distribution end timestamp of the emissions\n */\n function DISTRIBUTION_END() external view returns (uint256);\n}\n" + }, + "contracts/strategies/IAaveStakeToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IAaveStakedToken {\n function COOLDOWN_SECONDS() external returns (uint256);\n\n function UNSTAKE_WINDOW() external returns (uint256);\n\n function balanceOf(address addr) external returns (uint256);\n\n function redeem(address to, uint256 amount) external;\n\n function stakersCooldowns(address addr) external returns (uint256);\n\n function cooldown() external;\n}\n" + }, + "contracts/strategies/ICompound.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @dev Compound C Token interface\n * Documentation: https://compound.finance/developers/ctokens\n */\ninterface ICERC20 {\n /**\n * @notice The mint function transfers an asset into the protocol, which begins accumulating\n * interest based on the current Supply Rate for the asset. The user receives a quantity of\n * cTokens equal to the underlying tokens supplied, divided by the current Exchange Rate.\n * @param mintAmount The amount of the asset to be supplied, in units of the underlying asset.\n * @return 0 on success, otherwise an Error codes\n */\n function mint(uint256 mintAmount) external returns (uint256);\n\n /**\n * @notice Sender redeems cTokens in exchange for the underlying asset\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param redeemTokens The number of cTokens to redeem into underlying\n * @return uint 0=success, otherwise an error code.\n */\n function redeem(uint256 redeemTokens) external returns (uint256);\n\n /**\n * @notice The redeem underlying function converts cTokens into a specified quantity of the underlying\n * asset, and returns them to the user. The amount of cTokens redeemed is equal to the quantity of\n * underlying tokens received, divided by the current Exchange Rate. The amount redeemed must be less\n * than the user's Account Liquidity and the market's available liquidity.\n * @param redeemAmount The amount of underlying to be redeemed.\n * @return 0 on success, otherwise an error code.\n */\n function redeemUnderlying(uint256 redeemAmount) external returns (uint256);\n\n /**\n * @notice The user's underlying balance, representing their assets in the protocol, is equal to\n * the user's cToken balance multiplied by the Exchange Rate.\n * @param owner The account to get the underlying balance of.\n * @return The amount of underlying currently owned by the account.\n */\n function balanceOfUnderlying(address owner) external returns (uint256);\n\n /**\n * @notice Calculates the exchange rate from the underlying to the CToken\n * @dev This function does not accrue interest before calculating the exchange rate\n * @return Calculated exchange rate scaled by 1e18\n */\n function exchangeRateStored() external view returns (uint256);\n\n /**\n * @notice Get the token balance of the `owner`\n * @param owner The address of the account to query\n * @return The number of tokens owned by `owner`\n */\n function balanceOf(address owner) external view returns (uint256);\n\n /**\n * @notice Get the supply rate per block for supplying the token to Compound.\n */\n function supplyRatePerBlock() external view returns (uint256);\n\n /**\n * @notice Address of the Compound Comptroller.\n */\n function comptroller() external view returns (address);\n}\n" + }, + "contracts/strategies/IConvexDeposits.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IConvexDeposits {\n function deposit(\n uint256 _pid,\n uint256 _amount,\n bool _stake\n ) external returns (bool);\n\n function deposit(\n uint256 _amount,\n bool _lock,\n address _stakeAddress\n ) external;\n}\n" + }, + "contracts/strategies/ICRVMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICRVMinter {\n function mint(address gaugeAddress) external;\n}\n" + }, + "contracts/strategies/ICurveETHPoolV1.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurveETHPoolV1 {\n event AddLiquidity(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event ApplyNewFee(uint256 fee);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event CommitNewFee(uint256 new_fee);\n event RampA(\n uint256 old_A,\n uint256 new_A,\n uint256 initial_time,\n uint256 future_time\n );\n event RemoveLiquidity(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 token_supply\n );\n event RemoveLiquidityImbalance(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event RemoveLiquidityOne(\n address indexed provider,\n uint256 token_amount,\n uint256 coin_amount,\n uint256 token_supply\n );\n event StopRampA(uint256 A, uint256 t);\n event TokenExchange(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event Transfer(\n address indexed sender,\n address indexed receiver,\n uint256 value\n );\n\n function A() external view returns (uint256);\n\n function A_precise() external view returns (uint256);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_liquidity(uint256[2] memory _amounts, uint256 _min_mint_amount)\n external\n payable\n returns (uint256);\n\n function add_liquidity(\n uint256[2] memory _amounts,\n uint256 _min_mint_amount,\n address _receiver\n ) external payable returns (uint256);\n\n function admin_action_deadline() external view returns (uint256);\n\n function admin_balances(uint256 i) external view returns (uint256);\n\n function admin_fee() external view returns (uint256);\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function apply_new_fee() external;\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function balances(uint256 arg0) external view returns (uint256);\n\n function calc_token_amount(uint256[2] memory _amounts, bool _is_deposit)\n external\n view\n returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _burn_amount, int128 i)\n external\n view\n returns (uint256);\n\n function coins(uint256 arg0) external view returns (address);\n\n function commit_new_fee(uint256 _new_fee) external;\n\n function decimals() external view returns (uint256);\n\n function ema_price() external view returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external payable returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external payable returns (uint256);\n\n function fee() external view returns (uint256);\n\n function future_A() external view returns (uint256);\n\n function future_A_time() external view returns (uint256);\n\n function future_fee() external view returns (uint256);\n\n function get_balances() external view returns (uint256[2] memory);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n\n function get_p() external view returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function initial_A() external view returns (uint256);\n\n function initial_A_time() external view returns (uint256);\n\n function initialize(\n string memory _name,\n string memory _symbol,\n address[4] memory _coins,\n uint256[4] memory _rate_multipliers,\n uint256 _A,\n uint256 _fee\n ) external;\n\n function last_price() external view returns (uint256);\n\n function ma_exp_time() external view returns (uint256);\n\n function ma_last_time() external view returns (uint256);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function price_oracle() external view returns (uint256);\n\n function ramp_A(uint256 _future_A, uint256 _future_time) external;\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[2] memory _min_amounts\n ) external returns (uint256[2] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[2] memory _min_amounts,\n address _receiver\n ) external returns (uint256[2] memory);\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burn_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burn_amount,\n address _receiver\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received,\n address _receiver\n ) external returns (uint256);\n\n function set_ma_exp_time(uint256 _ma_exp_time) external;\n\n function stop_ramp_A() external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw_admin_fees() external;\n}\n" + }, + "contracts/strategies/ICurveGauge.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurveGauge {\n function balanceOf(address account) external view returns (uint256);\n\n function deposit(uint256 value, address account) external;\n\n function withdraw(uint256 value) external;\n}\n" + }, + "contracts/strategies/ICurveMetaPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface ICurveMetaPool {\n function add_liquidity(uint256[2] calldata amounts, uint256 min_mint_amount)\n external\n returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function remove_liquidity(uint256 _amount, uint256[2] calldata min_amounts)\n external\n returns (uint256[2] calldata);\n\n function remove_liquidity_one_coin(\n uint256 _token_amount,\n int128 i,\n uint256 min_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[2] calldata amounts,\n uint256 max_burn_amount\n ) external returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _token_amount, int128 i)\n external\n view\n returns (uint256);\n\n function balances(uint256 i) external view returns (uint256);\n\n function calc_token_amount(uint256[2] calldata amounts, bool deposit)\n external\n view\n returns (uint256);\n\n function base_pool() external view returns (address);\n\n function fee() external view returns (uint256);\n\n function coins(uint256 i) external view returns (address);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 dx,\n uint256 min_dy\n ) external returns (uint256);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n}\n" + }, + "contracts/strategies/ICurvePool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurvePool {\n function get_virtual_price() external view returns (uint256);\n\n function add_liquidity(uint256[3] calldata _amounts, uint256 _min) external;\n\n function balances(uint256) external view returns (uint256);\n\n function calc_token_amount(uint256[3] calldata _amounts, bool _deposit)\n external\n returns (uint256);\n\n function fee() external view returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _amount,\n int128 _index,\n uint256 _minAmount\n ) external;\n\n function remove_liquidity(\n uint256 _amount,\n uint256[3] calldata _minWithdrawAmounts\n ) external;\n\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n external\n view\n returns (uint256);\n\n function exchange(\n uint256 i,\n uint256 j,\n uint256 dx,\n uint256 min_dy\n ) external returns (uint256);\n\n function coins(uint256 _index) external view returns (address);\n\n function remove_liquidity_imbalance(\n uint256[3] calldata _amounts,\n uint256 maxBurnAmount\n ) external;\n}\n" + }, + "contracts/strategies/IRewardStaking.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IRewardStaking {\n function stakeFor(address, uint256) external;\n\n function stake(uint256) external;\n\n function withdraw(uint256 amount, bool claim) external;\n\n function withdrawAndUnwrap(uint256 amount, bool claim) external;\n\n function earned(address account) external view returns (uint256);\n\n function getReward() external;\n\n function getReward(address _account, bool _claimExtras) external;\n\n function extraRewardsLength() external returns (uint256);\n\n function extraRewards(uint256 _pid) external returns (address);\n\n function rewardToken() external returns (address);\n\n function balanceOf(address account) external view returns (uint256);\n}\n" + }, + "contracts/strategies/MorphoAaveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Morpho Aave Strategy\n * @notice Investment strategy for investing stablecoins via Morpho (Aave)\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { IMorpho } from \"../interfaces/morpho/IMorpho.sol\";\nimport { ILens } from \"../interfaces/morpho/ILens.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MorphoAaveStrategy is InitializableAbstractStrategy {\n address public constant MORPHO = 0x777777c9898D384F785Ee44Acfe945efDFf5f3E0;\n address public constant LENS = 0x507fA343d0A90786d86C7cd885f5C49263A91FF4;\n\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @dev Initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Approve the spending of all assets by main Morpho contract,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address asset = assetsMapped[i];\n\n // Safe approval\n IERC20(asset).safeApprove(MORPHO, 0);\n IERC20(asset).safeApprove(MORPHO, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset\n * We need to approve and allow Morpho to move them\n * @param _asset Address of the asset to approve\n * @param _pToken The pToken for the approval\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n IERC20(_asset).safeApprove(MORPHO, 0);\n IERC20(_asset).safeApprove(MORPHO, type(uint256).max);\n }\n\n /**\n * @dev Collect accumulated rewards and send them to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Morpho Aave-v2 doesn't distribute reward tokens\n // solhint-disable-next-line max-line-length\n // Ref: https://developers.morpho.xyz/interact-with-morpho/get-started/interact-with-morpho/claim-rewards#morpho-aave-v2\n }\n\n /**\n * @dev Get the amount of rewards pending to be collected from the protocol\n */\n function getPendingRewards() external view returns (uint256 balance) {\n // Morpho Aave-v2 doesn't distribute reward tokens\n // solhint-disable-next-line max-line-length\n // Ref: https://developers.morpho.xyz/interact-with-morpho/get-started/interact-with-morpho/claim-rewards#morpho-aave-v2\n return 0;\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n\n address pToken = address(_getPTokenFor(_asset));\n\n IMorpho(MORPHO).supply(\n pToken,\n address(this), // the address of the user you want to supply on behalf of\n _amount\n );\n emit Deposit(_asset, pToken, _amount);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Morpho\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Morpho\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n address pToken = address(_getPTokenFor(_asset));\n\n IMorpho(MORPHO).withdraw(pToken, _amount);\n emit Withdrawal(_asset, pToken, _amount);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = _checkBalance(assetsMapped[i]);\n if (balance > 0) {\n _withdraw(vaultAddress, assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Return total value of an asset held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n return _checkBalance(_asset);\n }\n\n function _checkBalance(address _asset)\n internal\n view\n returns (uint256 balance)\n {\n address pToken = address(_getPTokenFor(_asset));\n\n // Total value represented by decimal position of underlying token\n (, , balance) = ILens(LENS).getCurrentSupplyBalanceInOf(\n pToken,\n address(this)\n );\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Get the pToken wrapped in the IERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return pToken Corresponding pToken to this asset\n */\n function _getPTokenFor(address _asset) internal view returns (IERC20) {\n address pToken = assetToPToken[_asset];\n require(pToken != address(0), \"pToken does not exist\");\n return IERC20(pToken);\n }\n}\n" + }, + "contracts/strategies/MorphoCompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Morpho Compound Strategy\n * @notice Investment strategy for investing stablecoins via Morpho (Compound)\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, AbstractCompoundStrategy, InitializableAbstractStrategy } from \"./AbstractCompoundStrategy.sol\";\nimport { IMorpho } from \"../interfaces/morpho/IMorpho.sol\";\nimport { ILens } from \"../interfaces/morpho/ILens.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MorphoCompoundStrategy is AbstractCompoundStrategy {\n address public constant MORPHO = 0x8888882f8f843896699869179fB6E4f7e3B58888;\n address public constant LENS = 0x930f1b46e1D081Ec1524efD95752bE3eCe51EF67;\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @dev Initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Approve the spending of all assets by main Morpho contract,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address asset = assetsMapped[i];\n\n // Safe approval\n IERC20(asset).safeApprove(MORPHO, 0);\n IERC20(asset).safeApprove(MORPHO, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset\n * We need to approve and allow Morpho to move them\n * @param _asset Address of the asset to approve\n * @param _pToken The pToken for the approval\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n IERC20(_asset).safeApprove(MORPHO, 0);\n IERC20(_asset).safeApprove(MORPHO, type(uint256).max);\n }\n\n /**\n * @dev Collect accumulated rewards and send them to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n /**\n * Gas considerations. We could query Morpho LENS's `getUserUnclaimedRewards` for each\n * cToken separately and only claimRewards where it is economically feasible. Each call\n * (out of 3) costs ~60k gas extra.\n *\n * Each extra cToken in the `poolTokens` of `claimRewards` function makes that call\n * 89-120k more expensive gas wise.\n *\n * With Lens query in case where:\n * - there is only 1 reward token to collect. Net gas usage in best case would be\n * 3*60 - 2*120 = -60k -> saving 60k gas\n * - there are 2 reward tokens to collect. Net gas usage in best case would be\n * 3*60 - 120 = 60k -> more expensive for 60k gas\n * - there are 3 reward tokens to collect. Net gas usage in best case would be\n * 3*60 = 180k -> more expensive for 180k gas\n *\n * For the above reasoning such \"optimization\" is not implemented\n */\n\n address[] memory poolTokens = new address[](assetsMapped.length);\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n poolTokens[i] = assetToPToken[assetsMapped[i]];\n }\n\n // slither-disable-next-line unused-return\n IMorpho(MORPHO).claimRewards(\n poolTokens, // The addresses of the underlying protocol's pools to claim rewards from\n false // Whether to trade the accrued rewards for MORPHO token, with a premium\n );\n\n // Transfer COMP to Harvester\n IERC20 rewardToken = IERC20(rewardTokenAddresses[0]);\n uint256 balance = rewardToken.balanceOf(address(this));\n emit RewardTokenCollected(\n harvesterAddress,\n rewardTokenAddresses[0],\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n\n /**\n * @dev Get the amount of rewards pending to be collected from the protocol\n */\n function getPendingRewards() external view returns (uint256 balance) {\n address[] memory poolTokens = new address[](assetsMapped.length);\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n poolTokens[i] = assetToPToken[assetsMapped[i]];\n }\n\n return ILens(LENS).getUserUnclaimedRewards(poolTokens, address(this));\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n\n IMorpho(MORPHO).supply(\n address(_getCTokenFor(_asset)),\n address(this), // the address of the user you want to supply on behalf of\n _amount\n );\n emit Deposit(_asset, address(_getCTokenFor(_asset)), _amount);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Morpho\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Morpho\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n address pToken = assetToPToken[_asset];\n\n IMorpho(MORPHO).withdraw(pToken, _amount);\n emit Withdrawal(_asset, address(_getCTokenFor(_asset)), _amount);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = _checkBalance(assetsMapped[i]);\n if (balance > 0) {\n _withdraw(vaultAddress, assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Return total value of an asset held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n return _checkBalance(_asset);\n }\n\n function _checkBalance(address _asset)\n internal\n view\n returns (uint256 balance)\n {\n address pToken = assetToPToken[_asset];\n\n // Total value represented by decimal position of underlying token\n (, , balance) = ILens(LENS).getCurrentSupplyBalanceInOf(\n pToken,\n address(this)\n );\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconOracle Address of the Beacon Oracle contract that maps block numbers to slots\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconOracle,\n address _beaconProofs\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconOracle,\n _beaconProofs\n )\n {\n SSV_TOKEN = _ssvToken;\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract.\n /// @param _rewardTokenAddresses Address of reward token for platform\n /// @param _assets Addresses of initial supported assets\n /// @param _pTokens Platform Token corresponding addresses\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused for validator consolidation.\n /// @param _asset Address of asset to deposit. Has to be WETH.\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused for validator consolidation.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// Will revert if the strategy is paused for validator consolidation.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant whenNotPaused {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _asset, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertWethToEth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(_asset, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n /// Will revert if the strategy is paused for validator consolidation.\n function withdrawAll()\n external\n override\n onlyVaultOrGovernor\n nonReentrant\n whenNotPaused\n {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, WETH, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Returns the last verified balance of validator deposits, validator balance,\n /// WETH and ETH in the strategy contract.\n /// @param _asset Address of weth asset\n /// @return balance Total value of (W)ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by strategy.\n /// @param _asset The address of the asset token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for deposits\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instread of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract,\n /// but so does withdrawals from validators. It's too complex to separate the two\n /// so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IConsolidationSource } from \"../../interfaces/IConsolidation.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\nimport { IBeaconOracle } from \"../../interfaces/IBeaconOracle.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit, remove and consolidate validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable, Pausable {\n using SafeERC20 for IERC20;\n\n /// @notice The amount of ETH in wei that is required for a deposit to a new validator.\n /// Initially this is 32 ETH, but will be increased to 1 ETH after P2P's APIs have been updated\n /// to support deposits of 1 ETH.\n uint256 public constant DEPOSIT_AMOUNT_WEI = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Oracle contract that maps block numbers to slots\n address public immutable BEACON_ORACLE;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// Deposit data for new compounding validators.\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountWei Amount of ETH in wei that has been deposited to the beacon chain deposit contract\n /// @param blockNumber Block number when the deposit was made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either PENDING or PROVEN\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 blockNumber;\n uint32 depositIndex;\n DepositStatus status;\n }\n /// @notice Mapping of the root of a deposit (depositDataRoot) to its data\n mapping(bytes32 => DepositData) public deposits;\n /// @notice List of deposit roots that are still to be verified as processed on the beacon chain\n bytes32[] public depositsRoots;\n\n // Validator data\n\n struct ValidatorData {\n bytes32 pubKeyHash; // Hash of the validator's public key using the Beacon Chain's format\n uint64 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes and indexes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n ValidatorData[] internal verifiedValidators;\n /// @notice State of the new compounding validators with a 0x02 withdrawal credential prefix.\n /// Uses the Beacon chain hashing for BLSPubkey which is\n /// sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => VALIDATOR_STATE) public validatorState;\n\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n uint64 timestamp;\n uint128 ethBalance;\n }\n // TODO is it more efficient to use the block root rather than hashing it?\n /// @notice Mapping of the block root to the balances at that slot\n mapping(bytes32 => Balances) public snappedBalances;\n uint64 public lastSnapTimestamp;\n uint128 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n bytes32 public consolidationLastPubKeyHash;\n address public consolidationSourceStrategy;\n mapping(address => bool) public consolidationSourceStrategies;\n\n // For future use\n uint256[50] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITED, // The validator has been verified to have a zero balance\n REMOVED // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified and is ready to be staked\n }\n\n event RegistratorChanged(address indexed newAddress);\n event SourceStrategyAdded(address indexed strategy);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n bytes32 indexed depositDataRoot,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint64 indexed validatorIndex\n );\n event DepositVerified(bytes32 indexed depositDataRoot, uint256 amountWei);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(\n uint256 indexed timestamp,\n bytes32 indexed blockRoot,\n uint256 ethBalance\n );\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 wethBalance,\n uint256 ethBalance\n );\n event ConsolidationRequested(\n bytes32 indexed lastSourcePubKeyHash,\n bytes32 indexed targetPubKeyHash,\n address indexed sourceStrategy\n );\n event ConsolidationVerified(\n bytes32 indexed lastSourcePubKeyHash,\n uint64 indexed lastValidatorIndex,\n uint256 consolidationCount\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconOracle Address of the Beacon Oracle contract that maps block numbers to slots\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconOracle,\n address _beaconProofs\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_ORACLE = _beaconOracle;\n BEACON_PROOFS = _beaconProofs;\n }\n\n /***************************************\n Admin Functions\n ****************************************/\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Adds support for a legacy staking strategy that can be used for consolidation.\n function addSourceStrategy(address _strategy) external onlyGovernor {\n consolidationSourceStrategies[_strategy] = true;\n\n emit SourceStrategyAdded(_strategy);\n }\n\n /***************************************`\n Validator Management\n ****************************************/\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validatorState[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validator validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validator,\n uint64 depositAmountGwei\n ) external onlyRegistrator whenNotPaused {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertEthToWeth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validator.pubkey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == VALIDATOR_STATE.REGISTERED ||\n currentState == VALIDATOR_STATE.VERIFIED),\n \"Not registered or verified\"\n );\n require(\n currentState == VALIDATOR_STATE.VERIFIED ||\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validator.pubkey,\n withdrawalCredentials,\n validator.signature,\n validator.depositDataRoot\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == VALIDATOR_STATE.REGISTERED) {\n validatorState[pubKeyHash] = VALIDATOR_STATE.STAKED;\n }\n // Store the deposit data for verifyDeposit and verifyBalances\n deposits[validator.depositDataRoot] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n blockNumber: SafeCast.toUint64(block.number),\n depositIndex: SafeCast.toUint32(depositsRoots.length),\n status: DepositStatus.PENDING\n });\n depositsRoots.push(validator.depositDataRoot);\n\n emit ETHStaked(\n pubKeyHash,\n validator.depositDataRoot,\n validator.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then the request will fail.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n onlyRegistrator\n whenNotPaused\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove a sweeping validator from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == VALIDATOR_STATE.EXITED ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exited\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validatorState[pubKeyHash] = VALIDATOR_STATE.REMOVED;\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /// @notice Receives requests from supported legacy strategies to consolidate sweeping validators to\n /// a new compounding validator on this new strategy.\n /// @param lastSourcePubKeyHash The last source validator to be consolidated hashed using the Beacon Chain's format.\n /// @param targetPubKeyHash The target validator's public key hash using the Beacon Chain's format.\n function requestConsolidation(\n bytes32 lastSourcePubKeyHash,\n bytes32 targetPubKeyHash\n ) external whenNotPaused {\n require(\n consolidationSourceStrategies[msg.sender],\n \"Not a source strategy\"\n );\n\n // The target validator must be a compounding validator that has been verified\n require(\n validatorState[targetPubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Target validator not verified\"\n );\n\n // Ensure balances can not be verified until after the consolidation has been verified.\n lastSnapTimestamp = 0;\n\n // Store consolidation state\n consolidationLastPubKeyHash = lastSourcePubKeyHash;\n consolidationSourceStrategy = msg.sender;\n\n // Pause the strategy while the consolidation is in progress\n _pause();\n\n emit ConsolidationRequested(\n lastSourcePubKeyHash,\n targetPubKeyHash,\n msg.sender\n );\n }\n\n /***************************************\n SSV Management\n ****************************************/\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n Beacon Chain Proofs\n ****************************************/\n\n /// @notice Verifies a validator's index to its public key.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param validatorPubKeyProof The merkle proof that the validator's index matches its public key\n /// on the beacon chain.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.STAKED,\n \"Validator not staked\"\n );\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key\n IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n address(this) // Withdrawal address is this strategy\n );\n\n // TODO verify the validator's withdrawal credential points to this strategy\n\n // Store the validator state as verified\n validatorState[pubKeyHash] = VALIDATOR_STATE.VERIFIED;\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(\n ValidatorData({ pubKeyHash: pubKeyHash, index: validatorIndex })\n );\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n /// @param depositDataRoot The root of the deposit data that was stored when\n /// the deposit was made on the execution layer.\n /// @param depositBlockNumber A block number that is on or after the block the deposit\n /// was made on the execution layer.\n /// @param processedSlot Any slot on or after the deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// @param firstPendingDepositSlot The slot of the first pending deposit in the beacon chain\n /// @param firstPendingDepositSlotProof The merkle proof that the slot of the first pending deposit\n /// matches the beacon chain.\n /// BeaconBlock.BeaconBlockBody.deposits[0].slot\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n bytes32 depositDataRoot,\n uint64 depositBlockNumber,\n uint64 processedSlot,\n uint64 firstPendingDepositSlot,\n bytes calldata firstPendingDepositSlotProof\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositDataRoot];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n validatorState[deposit.pubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n // The deposit block number mapped to a slot needs to be the same block or after\n // the deposit in `stakeETH` was created on the execution layer.\n require(\n deposit.blockNumber <= depositBlockNumber,\n \"Deposit block before deposit\"\n );\n\n // Get the slot that is on or after the deposit was made on the execution layer.\n uint64 depositSlot = IBeaconOracle(BEACON_ORACLE).blockToSlot(\n depositBlockNumber\n );\n\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the first pending deposit's slot is 0 then there are no pending deposits so\n // our deposit must have been processed on the beacon chain.\n require(\n depositSlot < firstPendingDepositSlot ||\n firstPendingDepositSlot == 0,\n \"Deposit not processed\"\n );\n\n // The slot of the execution layer deposit must be before\n // the slot that the deposit was processed on the beacon chain.\n require(depositSlot < processedSlot, \"Slot not after deposit\");\n\n // Get the beacon block root for the slot that is on or after the deposit was processed on the beacon chain.\n bytes32 blockRoot = IBeaconOracle(BEACON_ORACLE).slotToRoot(\n processedSlot\n );\n\n // Verify the slot of the first pending deposit matches the beacon chain\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n blockRoot,\n firstPendingDepositSlot,\n firstPendingDepositSlotProof\n );\n\n // After verifying the proof, update the contract storage\n deposits[depositDataRoot].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n bytes32 lastDepositDataRoot = depositsRoots[depositsRoots.length - 1];\n depositsRoots[deposit.depositIndex] = lastDepositDataRoot;\n deposits[lastDepositDataRoot].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositsRoots.pop();\n\n emit DepositVerified(\n depositDataRoot,\n uint256(deposit.amountGwei) * 1 gwei\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n // TODO what if the last validator was exited rather than consolidated?\n // slither-disable-start reentrancy-no-eth\n function verifyConsolidation(\n uint64 parentBlockTimestamp,\n uint64 lastValidatorIndex,\n bytes calldata validatorPubKeyProof,\n bytes32 balancesLeaf,\n bytes calldata validatorBalanceProof\n ) external onlyRegistrator {\n bytes32 consolidationLastPubKeyHashMem = consolidationLastPubKeyHash;\n require(\n consolidationLastPubKeyHashMem != bytes32(0),\n \"No consolidations\"\n );\n\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(parentBlockTimestamp);\n // Verify the validator index has the same public key as the last source validator\n IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n blockRoot,\n consolidationLastPubKeyHashMem,\n validatorPubKeyProof,\n lastValidatorIndex,\n address(this) // Withdrawal address is this strategy\n );\n\n // Verify the balance of the last validator in the consolidation batch\n // is zero. If its not then the consolidation has not been completed.\n // This proof is to the beacon block root, not the balances container root.\n uint256 validatorBalance = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n blockRoot,\n balancesLeaf,\n validatorBalanceProof,\n lastValidatorIndex,\n IBeaconProofs.BalanceProofLevel.BeaconBlock\n );\n require(validatorBalance == 0, \"Last validator balance not zero\");\n\n // Call the old sweeping strategy to confirm the consolidation has been completed.\n // This will decrease the balance of the source strategy by 32 ETH for each validator being consolidated.\n uint256 consolidationCount = IConsolidationSource(\n consolidationSourceStrategy\n ).confirmConsolidation();\n\n // Increase the balance of this strategy by 32 ETH for each validator being consolidated.\n // This nets out the decrease in the source strategy's balance.\n lastVerifiedEthBalance += SafeCast.toUint128(\n consolidationCount * 32 ether\n );\n\n // Reset the stored consolidation state\n consolidationLastPubKeyHash = bytes32(0);\n consolidationSourceStrategy = address(0);\n\n emit ConsolidationVerified(\n consolidationLastPubKeyHashMem,\n lastValidatorIndex,\n consolidationCount\n );\n\n // Unpause now the balance of the target validator has been verified\n _unpause();\n\n // Take a snap of the balances so the actual balances of the new validator balances can be verified\n _snapBalances();\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block.\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n /// Can only be called by the registrator.\n /// Can not be called while a consolidation is in progress.\n function snapBalances() external whenNotPaused onlyRegistrator {\n _snapBalances();\n }\n\n function _snapBalances() internal {\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(\n SafeCast.toUint64(block.timestamp)\n );\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the balances in the mapping\n snappedBalances[blockRoot] = Balances({\n timestamp: SafeCast.toUint64(block.timestamp),\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n // Store the snapped timestamp\n lastSnapTimestamp = SafeCast.toUint64(block.timestamp);\n\n emit BalancesSnapped(block.timestamp, blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct VerifyBalancesParams {\n bytes32 blockRoot;\n uint64 firstPendingDepositSlot;\n // BeaconBlock.BeaconBlockBody.deposits[0].slot\n bytes firstPendingDepositSlotProof;\n bytes32 balancesContainerRoot;\n // BeaconBlock.state.validators\n bytes validatorContainerProof;\n bytes32[] validatorBalanceLeaves;\n // BeaconBlock.state.validators[validatorIndex].balance\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(VerifyBalancesParams calldata params) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalances[params.blockRoot];\n // Check the balances are the latest\n require(lastSnapTimestamp > 0, \"No snapped balances\");\n require(balancesMem.timestamp == lastSnapTimestamp, \"Stale snap\");\n\n uint256 depositsCount = depositsRoots.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit to the beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n params.blockRoot,\n params.firstPendingDepositSlot,\n params.firstPendingDepositSlotProof\n );\n\n uint64 firstPendingDepositBlockNumber = IBeaconOracle(BEACON_ORACLE)\n .slotToBlock(params.firstPendingDepositSlot);\n\n // For each native staking contract's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n bytes32 depositDataRoot = depositsRoots[i];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit block number is before the\n // block number of the staking strategy's deposit.\n // If it has it will need to be verified with `verifyDeposit`\n require(\n firstPendingDepositBlockNumber <\n deposits[depositDataRoot].blockNumber,\n \"Deposit has been processed\"\n );\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei +=\n uint256(deposits[depositDataRoot].amountGwei) *\n 1 gwei;\n }\n }\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n params.validatorBalanceProofs.length == verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n params.validatorBalanceLeaves.length == verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n params.blockRoot,\n params.balancesContainerRoot,\n params.validatorContainerProof\n );\n\n // for each validator in reserve order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n params.balancesContainerRoot,\n params.validatorBalanceLeaves[i],\n params.validatorBalanceProofs[i],\n verifiedValidators[i].index,\n IBeaconProofs.BalanceProofLevel.Container\n );\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n validatorState[\n verifiedValidators[i].pubKeyHash\n ] = VALIDATOR_STATE.EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += uint256(validatorBalanceGwei) * 1 gwei;\n }\n }\n\n uint256 wethBalance = IWETH9(WETH).balanceOf(address(this));\n\n // Store the verified balance in storage\n lastVerifiedEthBalance = SafeCast.toUint128(\n totalDepositsWei + totalValidatorBalance + balancesMem.ethBalance\n );\n // Reset the last snap timestamp so a new snapBalances has to be made\n lastSnapTimestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n wethBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /***************************************\n WETH and ETH Accounting\n ****************************************/\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n function _convertWethToEth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance\n if (lastVerifiedEthBalance > _ethAmount) {\n lastVerifiedEthBalance -= SafeCast.toUint128(_ethAmount);\n } else {\n // This can happen if all ETH in the validators was withdrawn\n // and there was more consensus rewards since the last balance verification.\n // Or it can happen if ETH is donated to this strategy.\n lastVerifiedEthBalance = 0;\n }\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n function _convertEthToWeth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += SafeCast.toUint128(_wethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /***************************************\n View Functions\n ****************************************/\n\n function getVerifiedValidators()\n external\n view\n returns (ValidatorData[] memory)\n {\n return verifiedValidators;\n }\n}\n" + }, + "contracts/strategies/NativeStaking/FeeAccumulator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\n/**\n * @title Fee Accumulator for Native Staking SSV Strategy\n * @notice Receives execution rewards which includes tx fees and\n * MEV rewards like tx priority and tx ordering.\n * It does NOT include swept ETH from beacon chain consensus rewards or full validator withdrawals.\n * @author Origin Protocol Inc\n */\ncontract FeeAccumulator {\n /// @notice The address of the Native Staking Strategy\n address public immutable STRATEGY;\n\n event ExecutionRewardsCollected(address indexed strategy, uint256 amount);\n\n /**\n * @param _strategy Address of the Native Staking Strategy\n */\n constructor(address _strategy) {\n STRATEGY = _strategy;\n }\n\n /**\n * @notice sends all ETH in this FeeAccumulator contract to the Native Staking Strategy.\n * @return eth The amount of execution rewards that were sent to the Native Staking Strategy\n */\n function collect() external returns (uint256 eth) {\n require(msg.sender == STRATEGY, \"Caller is not the Strategy\");\n\n eth = address(this).balance;\n if (eth > 0) {\n // Send the ETH to the Native Staking Strategy\n Address.sendValue(payable(STRATEGY), eth);\n\n emit ExecutionRewardsCollected(STRATEGY, eth);\n }\n }\n\n /**\n * @dev Accept ETH\n */\n receive() external payable {}\n}\n" + }, + "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { FeeAccumulator } from \"./FeeAccumulator.sol\";\nimport { ValidatorAccountant } from \"./ValidatorAccountant.sol\";\nimport { ISSVNetwork } from \"../../interfaces/ISSVNetwork.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/// @title Native Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract NativeStakingSSVStrategy is\n ValidatorAccountant,\n InitializableAbstractStrategy\n{\n using SafeERC20 for IERC20;\n\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n /// @notice Fee collector address\n /// @dev this address will receive maximal extractable value (MEV) rewards. These are\n /// rewards for arranging transactions in a way that benefits the validator.\n address payable public immutable FEE_ACCUMULATOR_ADDRESS;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[49] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n /// @param _feeAccumulator Address of the fee accumulator receiving execution layer validator rewards\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n uint256 _maxValidators,\n address _feeAccumulator,\n address _beaconChainDepositContract\n )\n InitializableAbstractStrategy(_baseConfig)\n ValidatorAccountant(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _maxValidators\n )\n {\n SSV_TOKEN = _ssvToken;\n FEE_ACCUMULATOR_ADDRESS = payable(_feeAccumulator);\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract.\n /// @param _rewardTokenAddresses Address of reward token for platform\n /// @param _assets Addresses of initial supported assets\n /// @param _pTokens Platform Token corresponding addresses\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n\n // Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\n FEE_ACCUMULATOR_ADDRESS\n );\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n /// @param _asset Address of asset to deposit. Has to be WETH.\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n depositedWethAccountedFor += _amount;\n _deposit(_asset, _amount);\n }\n\n /// @dev Deposit WETH to this strategy so it can later be staked into a validator.\n /// @param _asset Address of WETH\n /// @param _amount Amount of WETH to deposit\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n /*\n * We could do a check here that would revert when \"_amount % 32 ether != 0\". With the idea of\n * not allowing deposits that will result in WETH sitting on the strategy after all the possible batches\n * of 32ETH have been staked.\n * But someone could mess with our strategy by sending some WETH to it. And we might want to deposit just\n * enough WETH to add it up to 32 so it can be staked. For that reason the check is left out.\n *\n * WETH sitting on the strategy won't interfere with the accounting since accounting only operates on ETH.\n */\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n depositedWethAccountedFor = wethBalance;\n\n _deposit(WETH, newWeth);\n }\n }\n\n /// @notice Withdraw WETH from this contract. Used only if some WETH for is lingering on the contract.\n /// That can happen when:\n /// - after mints if the strategy is the default\n /// - time between depositToStrategy and stakeEth\n /// - the deposit was not a multiple of 32 WETH\n /// - someone sent WETH directly to this contract\n /// Will NOT revert if the strategy is paused from an accounting failure.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n _wethWithdrawn(_amount);\n\n IERC20(_asset).safeTransfer(_recipient, _amount);\n emit Withdrawal(_asset, address(0), _amount);\n }\n\n /// @notice transfer all WETH deposits back to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `exitSsvValidator` and `removeSsvValidator` operations.\n /// This does not withdraw any execution rewards from the FeeAccumulator or\n /// consensus rewards in this strategy.\n /// Any ETH in this strategy that was swept from a full validator withdrawal will not be withdrawn.\n /// ETH from full validator withdrawals is sent to the Vault using `doAccounting`.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n if (wethBalance > 0) {\n _withdraw(vaultAddress, WETH, wethBalance);\n }\n }\n\n /// @notice Returns the total value of (W)ETH that is staked to the validators\n /// and WETH deposits that are still to be staked.\n /// This does not include ETH from consensus rewards sitting in this strategy\n /// or ETH from MEV rewards in the FeeAccumulator. These rewards are harvested\n /// and sent to the Dripper so will eventually be sent to the Vault as WETH.\n /// @param _asset Address of weth asset\n /// @return balance Total value of (W)ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n balance =\n // add the ETH that has been staked in validators\n activeDepositedValidators *\n FULL_STAKE +\n // add the WETH in the strategy from deposits that are still to be staked\n IERC20(WETH).balanceOf(address(this));\n }\n\n function pause() external onlyStrategist {\n _pause();\n }\n\n /// @notice Returns bool indicating whether asset is supported by strategy.\n /// @param _asset The address of the asset token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for deposits\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\n function setFeeRecipient() external {\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\n FEE_ACCUMULATOR_ADDRESS\n );\n }\n\n /**\n * @notice Only accept ETH from the FeeAccumulator and the WETH contract - required when\n * unwrapping WETH just before staking it to the validator.\n * The strategy will also receive ETH from the priority fees of transactions when producing blocks\n * as defined in EIP-1559.\n * The tx fees come from the Beacon chain so do not need any EVM level permissions to receive ETH.\n * The tx fees are paid with each block produced. They are not included in the consensus rewards\n * which are periodically swept from the validators to this strategy.\n * For accounting purposes, the priority fees of transactions will be considered consensus rewards\n * and will be included in the AccountingConsensusRewards event.\n * @dev don't want to receive donations from anyone else as donations over the fuse limits will\n * mess with the accounting of the consensus rewards and validator full withdrawals.\n */\n receive() external payable {\n require(\n msg.sender == FEE_ACCUMULATOR_ADDRESS || msg.sender == WETH,\n \"Eth not from allowed contracts\"\n );\n }\n\n /***************************************\n Internal functions\n ****************************************/\n\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Convert accumulated ETH to WETH and send to the Harvester.\n /// Will revert if the strategy is paused for accounting.\n function _collectRewardTokens() internal override whenNotPaused {\n // collect ETH from execution rewards from the fee accumulator\n uint256 executionRewards = FeeAccumulator(FEE_ACCUMULATOR_ADDRESS)\n .collect();\n\n // total ETH rewards to be harvested = execution rewards + consensus rewards\n uint256 ethRewards = executionRewards + consensusRewards;\n\n require(\n address(this).balance >= ethRewards,\n \"Insufficient eth balance\"\n );\n\n if (ethRewards > 0) {\n // reset the counter keeping track of beacon chain consensus rewards\n consensusRewards = 0;\n\n // Convert ETH rewards to WETH\n IWETH9(WETH).deposit{ value: ethRewards }();\n\n IERC20(WETH).safeTransfer(harvesterAddress, ethRewards);\n emit RewardTokenCollected(harvesterAddress, WETH, ethRewards);\n }\n }\n\n /// @dev emits Withdrawal event from NativeStakingSSVStrategy\n function _wethWithdrawnToVault(uint256 _amount) internal override {\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _wethWithdrawn(uint256 _amount) internal override {\n /* In an ideal world we wouldn't need to reduce the deduction amount when the\n * depositedWethAccountedFor is smaller than the _amount.\n *\n * The reason this is required is that a malicious actor could sent WETH directly\n * to this contract and that would circumvent the increase of depositedWethAccountedFor\n * property. When the ETH would be staked the depositedWethAccountedFor amount could\n * be deducted so much that it would be negative.\n */\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n }\n}\n" + }, + "contracts/strategies/NativeStaking/ValidatorAccountant.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ValidatorRegistrator } from \"./ValidatorRegistrator.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\n\n/// @title Validator Accountant\n/// @notice Attributes the ETH swept from beacon chain validators to this strategy contract\n/// as either full or partial withdrawals. Partial withdrawals being consensus rewards.\n/// Full withdrawals are from exited validators.\n/// @author Origin Protocol Inc\nabstract contract ValidatorAccountant is ValidatorRegistrator {\n /// @notice The minimum amount of blocks that need to pass between two calls to manuallyFixAccounting\n uint256 public constant MIN_FIX_ACCOUNTING_CADENCE = 7200; // 1 day\n\n /// @notice Keeps track of the total consensus rewards swept from the beacon chain\n uint256 public consensusRewards;\n\n /// @notice start of fuse interval\n uint256 public fuseIntervalStart;\n /// @notice end of fuse interval\n uint256 public fuseIntervalEnd;\n /// @notice last block number manuallyFixAccounting has been called\n uint256 public lastFixAccountingBlockNumber;\n\n uint256[49] private __gap;\n\n event FuseIntervalUpdated(uint256 start, uint256 end);\n event AccountingFullyWithdrawnValidator(\n uint256 noOfValidators,\n uint256 remainingValidators,\n uint256 wethSentToVault\n );\n event AccountingValidatorSlashed(\n uint256 remainingValidators,\n uint256 wethSentToVault\n );\n event AccountingConsensusRewards(uint256 amount);\n\n event AccountingManuallyFixed(\n int256 validatorsDelta,\n int256 consensusRewardsDelta,\n uint256 wethToVault\n );\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n uint256 _maxValidators\n )\n ValidatorRegistrator(\n _wethAddress,\n _vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _maxValidators\n )\n {}\n\n /// @notice set fuse interval values\n function setFuseInterval(\n uint256 _fuseIntervalStart,\n uint256 _fuseIntervalEnd\n ) external onlyGovernor {\n require(\n _fuseIntervalStart < _fuseIntervalEnd &&\n _fuseIntervalEnd < 32 ether &&\n _fuseIntervalEnd - _fuseIntervalStart >= 4 ether,\n \"Incorrect fuse interval\"\n );\n\n fuseIntervalStart = _fuseIntervalStart;\n fuseIntervalEnd = _fuseIntervalEnd;\n\n emit FuseIntervalUpdated(_fuseIntervalStart, _fuseIntervalEnd);\n }\n\n /* solhint-disable max-line-length */\n /// This notion page offers a good explanation of how the accounting functions\n /// https://www.notion.so/originprotocol/Limited-simplified-native-staking-accounting-67a217c8420d40678eb943b9da0ee77d\n /// In short, after dividing by 32, if the ETH remaining on the contract falls between 0 and fuseIntervalStart,\n /// the accounting function will treat that ETH as Beacon chain consensus rewards.\n /// On the contrary, if after dividing by 32, the ETH remaining on the contract falls between fuseIntervalEnd and 32,\n /// the accounting function will treat that as a validator slashing.\n /// @notice Perform the accounting attributing beacon chain ETH to either full or partial withdrawals. Returns true when\n /// accounting is valid and fuse isn't \"blown\". Returns false when fuse is blown.\n /// @dev This function could in theory be permission-less but lets allow only the Registrator (Defender Action) to call it\n /// for now.\n /// @return accountingValid true if accounting was successful, false if fuse is blown\n /* solhint-enable max-line-length */\n function doAccounting()\n external\n onlyRegistrator\n whenNotPaused\n nonReentrant\n returns (bool accountingValid)\n {\n // pause the accounting on failure\n accountingValid = _doAccounting(true);\n }\n\n // slither-disable-start reentrancy-eth\n function _doAccounting(bool pauseOnFail)\n internal\n returns (bool accountingValid)\n {\n if (address(this).balance < consensusRewards) {\n return _failAccounting(pauseOnFail);\n }\n\n // Calculate all the new ETH that has been swept to the contract since the last accounting\n uint256 newSweptETH = address(this).balance - consensusRewards;\n accountingValid = true;\n\n // send the ETH that is from fully withdrawn validators to the Vault\n if (newSweptETH >= FULL_STAKE) {\n uint256 fullyWithdrawnValidators;\n // explicitly cast to uint256 as we want to round to a whole number of validators\n fullyWithdrawnValidators = uint256(newSweptETH / FULL_STAKE);\n activeDepositedValidators -= fullyWithdrawnValidators;\n\n uint256 wethToVault = FULL_STAKE * fullyWithdrawnValidators;\n IWETH9(WETH).deposit{ value: wethToVault }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, wethToVault);\n _wethWithdrawnToVault(wethToVault);\n\n emit AccountingFullyWithdrawnValidator(\n fullyWithdrawnValidators,\n activeDepositedValidators,\n wethToVault\n );\n }\n\n uint256 ethRemaining = address(this).balance - consensusRewards;\n // should be less than a whole validator stake\n require(ethRemaining < FULL_STAKE, \"Unexpected accounting\");\n\n // If no Beacon chain consensus rewards swept\n if (ethRemaining == 0) {\n // do nothing\n return accountingValid;\n } else if (ethRemaining < fuseIntervalStart) {\n // Beacon chain consensus rewards swept (partial validator withdrawals)\n // solhint-disable-next-line reentrancy\n consensusRewards += ethRemaining;\n emit AccountingConsensusRewards(ethRemaining);\n } else if (ethRemaining > fuseIntervalEnd) {\n // Beacon chain consensus rewards swept but also a slashed validator fully exited\n IWETH9(WETH).deposit{ value: ethRemaining }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, ethRemaining);\n activeDepositedValidators -= 1;\n\n _wethWithdrawnToVault(ethRemaining);\n\n emit AccountingValidatorSlashed(\n activeDepositedValidators,\n ethRemaining\n );\n }\n // Oh no... Fuse is blown. The Strategist needs to adjust the accounting values.\n else {\n return _failAccounting(pauseOnFail);\n }\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @dev pause any further accounting if required and return false\n function _failAccounting(bool pauseOnFail)\n internal\n returns (bool accountingValid)\n {\n // pause if not already\n if (pauseOnFail) {\n _pause();\n }\n // fail the accounting\n accountingValid = false;\n }\n\n /// @notice Allow the Strategist to fix the accounting of this strategy and unpause.\n /// @param _validatorsDelta adjust the active validators by up to plus three or minus three\n /// @param _consensusRewardsDelta adjust the accounted for consensus rewards up or down\n /// @param _ethToVaultAmount the amount of ETH that gets wrapped into WETH and sent to the Vault\n /// @dev There is a case when a validator(s) gets slashed so much that the eth swept from\n /// the beacon chain enters the fuse area and there are no consensus rewards on the contract\n /// to \"dip into\"/use. To increase the amount of unaccounted ETH over the fuse end interval\n /// we need to reduce the amount of active deposited validators and immediately send WETH\n /// to the vault, so it doesn't interfere with further accounting.\n function manuallyFixAccounting(\n int256 _validatorsDelta,\n int256 _consensusRewardsDelta,\n uint256 _ethToVaultAmount\n ) external onlyStrategist whenPaused nonReentrant {\n require(\n lastFixAccountingBlockNumber + MIN_FIX_ACCOUNTING_CADENCE <\n block.number,\n \"Fix accounting called too soon\"\n );\n require(\n _validatorsDelta >= -3 &&\n _validatorsDelta <= 3 &&\n // new value must be positive\n int256(activeDepositedValidators) + _validatorsDelta >= 0,\n \"Invalid validatorsDelta\"\n );\n require(\n _consensusRewardsDelta >= -332 ether &&\n _consensusRewardsDelta <= 332 ether &&\n // new value must be positive\n int256(consensusRewards) + _consensusRewardsDelta >= 0,\n \"Invalid consensusRewardsDelta\"\n );\n require(_ethToVaultAmount <= 32 ether * 3, \"Invalid wethToVaultAmount\");\n\n activeDepositedValidators = uint256(\n int256(activeDepositedValidators) + _validatorsDelta\n );\n consensusRewards = uint256(\n int256(consensusRewards) + _consensusRewardsDelta\n );\n lastFixAccountingBlockNumber = block.number;\n if (_ethToVaultAmount > 0) {\n IWETH9(WETH).deposit{ value: _ethToVaultAmount }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, _ethToVaultAmount);\n _wethWithdrawnToVault(_ethToVaultAmount);\n }\n\n emit AccountingManuallyFixed(\n _validatorsDelta,\n _consensusRewardsDelta,\n _ethToVaultAmount\n );\n\n // rerun the accounting to see if it has now been fixed.\n // Do not pause the accounting on failure as it is already paused\n require(_doAccounting(false), \"Fuse still blown\");\n\n // unpause since doAccounting was successful\n _unpause();\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n /// @dev allows for NativeStakingSSVStrategy contract to emit the Withdrawal event\n function _wethWithdrawnToVault(uint256 _amount) internal virtual;\n}\n" + }, + "contracts/strategies/NativeStaking/ValidatorRegistrator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IConsolidationTarget } from \"../../interfaces/IConsolidation.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconConsolidation } from \"../../beacon/BeaconConsolidation.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Registrator of the validators\n * @notice This contract implements all the required functionality to register, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract ValidatorRegistrator is Governable, Pausable {\n /// @notice The maximum amount of ETH that can be staked by a validator\n /// @dev this can change in the future with EIP-7251, Increase the MAX_EFFECTIVE_BALANCE\n uint256 public constant FULL_STAKE = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Maximum number of validators that can be registered in this strategy\n uint256 public immutable MAX_VALIDATORS;\n\n /// @notice Address of the registrator - allowed to register, exit and remove validators\n address public validatorRegistrator;\n /// @notice The number of validators that have 32 (!) ETH actively deposited. When a new deposit\n /// to a validator happens this number increases, when a validator exit is detected this number\n /// decreases.\n uint256 public activeDepositedValidators;\n /// @notice State of the validators keccak256(pubKey) => state\n mapping(bytes32 => VALIDATOR_STATE) public validatorsStates;\n /// @notice The account that is allowed to modify stakeETHThreshold and reset stakeETHTally\n address public stakingMonitor;\n /// @notice Amount of ETH that can be staked before staking on the contract is suspended\n /// and the `stakingMonitor` needs to approve further staking by calling `resetStakeETHTally`\n uint256 public stakeETHThreshold;\n /// @notice Amount of ETH that has been staked since the `stakingMonitor` last called `resetStakeETHTally`.\n /// This can not go above `stakeETHThreshold`.\n uint256 public stakeETHTally;\n\n /// @notice Number of validators currently being consolidated\n uint256 public consolidationCount;\n address public consolidationTargetStrategy;\n /// @notice Mapping of support target staking strategies that can be used for consolidation\n mapping(address => bool) public consolidationTargetStrategies;\n\n // For future use\n uint256[44] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n EXITING, // exit message has been posted and validator is in the process of exiting\n EXIT_COMPLETE // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event ETHStaked(bytes32 indexed pubKeyHash, bytes pubKey, uint256 amount);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event SSVValidatorExitInitiated(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event SSVValidatorExitCompleted(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event ConsolidationRequested(\n bytes[] sourcePubKeys,\n bytes targetPubKey,\n address targetStakingStrategy,\n uint256 consolidationCount\n );\n event ConsolidationConfirmed(\n uint256 consolidationCount,\n uint256 activeDepositedValidators\n );\n event StakeETHThresholdChanged(uint256 amount);\n event StakeETHTallyReset();\n event TargetStrategyAdded(address indexed strategy);\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(\n msg.sender == validatorRegistrator,\n \"Caller is not the Registrator\"\n );\n _;\n }\n\n /// @dev Throws if called by any account other than the Staking monitor\n modifier onlyStakingMonitor() {\n require(msg.sender == stakingMonitor, \"Caller is not the Monitor\");\n _;\n }\n\n /// @dev Throws if called by any account other than the Strategist\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(VAULT_ADDRESS).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n uint256 _maxValidators\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n MAX_VALIDATORS = _maxValidators;\n }\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Set the address of the staking monitor that is allowed to reset stakeETHTally\n function setStakingMonitor(address _address) external onlyGovernor {\n stakingMonitor = _address;\n emit StakingMonitorChanged(_address);\n }\n\n /// @notice Set the amount of ETH that can be staked before staking monitor\n // needs to a approve further staking by resetting the stake ETH tally\n function setStakeETHThreshold(uint256 _amount) external onlyGovernor {\n stakeETHThreshold = _amount;\n emit StakeETHThresholdChanged(_amount);\n }\n\n /// @notice Reset the stakeETHTally\n function resetStakeETHTally() external onlyStakingMonitor {\n stakeETHTally = 0;\n emit StakeETHTallyReset();\n }\n\n /// @notice Adds support for a new staking strategy that can be used for consolidation.\n function addTargetStrategy(address _strategy) external onlyGovernor {\n consolidationTargetStrategies[_strategy] = true;\n\n emit TargetStrategyAdded(_strategy);\n }\n\n /// @notice Stakes WETH to the node validators\n /// @param validators A list of validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n // slither-disable-start reentrancy-eth\n function stakeEth(ValidatorStakeData[] calldata validators)\n external\n onlyRegistrator\n whenNotPaused\n nonReentrant\n {\n uint256 requiredETH = validators.length * FULL_STAKE;\n\n // Check there is enough WETH from the deposits sitting in this strategy contract\n require(\n requiredETH <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(\n activeDepositedValidators + validators.length <= MAX_VALIDATORS,\n \"Max validators reached\"\n );\n\n require(\n stakeETHTally + requiredETH <= stakeETHThreshold,\n \"Staking ETH over threshold\"\n );\n stakeETHTally += requiredETH;\n\n // Convert required ETH from WETH\n IWETH9(WETH).withdraw(requiredETH);\n _wethWithdrawn(requiredETH);\n\n /* 0x01 to indicate that withdrawal credentials will contain an EOA address that the sweeping function\n * can sweep funds to.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x01),\n bytes11(0),\n address(this)\n );\n\n // For each validator\n for (uint256 i = 0; i < validators.length; ++i) {\n bytes32 pubKeyHash = keccak256(validators[i].pubkey);\n\n require(\n validatorsStates[pubKeyHash] == VALIDATOR_STATE.REGISTERED,\n \"Validator not registered\"\n );\n\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: FULL_STAKE\n }(\n validators[i].pubkey,\n withdrawalCredentials,\n validators[i].signature,\n validators[i].depositDataRoot\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.STAKED;\n\n emit ETHStaked(pubKeyHash, validators[i].pubkey, FULL_STAKE);\n }\n // save gas by changing this storage variable only once rather each time in the loop.\n activeDepositedValidators += validators.length;\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Registers a new validator in the SSV Cluster.\n /// Only the registrator can call this function.\n /// @param publicKeys The public keys of the validators\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for each validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n require(\n publicKeys.length == sharesData.length,\n \"Pubkey sharesData mismatch\"\n );\n // Check each public key has not already been used\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n emit SSVValidatorRegistered(pubKeyHash, publicKeys[i], operatorIds);\n }\n\n ISSVNetwork(SSV_NETWORK).bulkRegisterValidator(\n publicKeys,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Exit a validator from the Beacon chain.\n /// The staked ETH will eventually swept to this native staking strategy.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n // slither-disable-start reentrancy-no-eth\n function exitSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds\n ) external onlyRegistrator whenNotPaused {\n bytes32 pubKeyHash = keccak256(publicKey);\n VALIDATOR_STATE currentState = validatorsStates[pubKeyHash];\n require(currentState == VALIDATOR_STATE.STAKED, \"Validator not staked\");\n\n ISSVNetwork(SSV_NETWORK).exitValidator(publicKey, operatorIds);\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXITING;\n\n emit SSVValidatorExitInitiated(pubKeyHash, publicKey, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove a validator from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n bytes32 pubKeyHash = keccak256(publicKey);\n VALIDATOR_STATE currentState = validatorsStates[pubKeyHash];\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == VALIDATOR_STATE.EXITING ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exiting\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXIT_COMPLETE;\n\n emit SSVValidatorExitCompleted(pubKeyHash, publicKey, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Deposits more SSV Tokens to the SSV Network contract which is used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// uses \"onlyStrategist\" modifier so continuous front-running can't DOS our maintenance service\n /// that tries to top up SSV tokens.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function depositSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyStrategist {\n ISSVNetwork(SSV_NETWORK).deposit(\n address(this),\n operatorIds,\n ssvAmount,\n cluster\n );\n }\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n New Consolidation\n ****************************************/\n\n function requestConsolidation(\n bytes[] calldata sourcePubKeys,\n bytes calldata targetPubKey,\n address targetStakingStrategy\n ) external nonReentrant whenNotPaused onlyGovernor {\n require(\n consolidationTargetStrategies[targetStakingStrategy],\n \"Invalid target strategy\"\n );\n\n bytes32 targetPubKeyHash = keccak256(targetPubKey);\n bytes32 sourcePubKeyHash;\n for (uint256 i = 0; i < sourcePubKeys.length; ++i) {\n // hash the source validator's public key using the Beacon Chain's format\n sourcePubKeyHash = keccak256(sourcePubKeys[i]);\n require(sourcePubKeyHash != targetPubKeyHash, \"Self consolidation\");\n require(\n validatorsStates[sourcePubKeyHash] == VALIDATOR_STATE.STAKED,\n \"Source validator not staked\"\n );\n\n // Request consolidation from source to target validator\n BeaconConsolidation.request(sourcePubKeys[i], targetPubKey);\n\n // Store the state of the source validator as exiting so it can be removed\n // after the consolidation is confirmed\n validatorsStates[sourcePubKeyHash] == VALIDATOR_STATE.EXITING;\n }\n\n // Hash using the Beacon Chain's format\n bytes32 lastSourcePubKeyHash = _hashPubKey(\n sourcePubKeys[sourcePubKeys.length - 1]\n );\n // Call the new compounding staking strategy to validate the target validator\n IConsolidationTarget(targetStakingStrategy).requestConsolidation(\n lastSourcePubKeyHash,\n targetPubKeyHash\n );\n\n // Store the consolidation state\n consolidationCount = sourcePubKeys.length;\n consolidationTargetStrategy = targetStakingStrategy;\n\n // Pause the strategy to prevent further consolidations or validator exits\n _pause();\n\n emit ConsolidationRequested(\n sourcePubKeys,\n targetPubKey,\n targetStakingStrategy,\n sourcePubKeys.length\n );\n }\n\n function confirmConsolidation()\n external\n nonReentrant\n whenPaused\n returns (uint256 consolidationCount_)\n {\n // Check the caller is the target staking strategy\n require(\n msg.sender == consolidationTargetStrategy,\n \"Not target strategy\"\n );\n\n // Load the number of validators being consolidated into memory\n consolidationCount_ = consolidationCount;\n\n // Need to check this is from the new staking strategy\n require(consolidationCount_ > 0, \"No consolidation in progress\");\n\n // Store the reduced number of active deposited validators\n // managed by this strategy\n activeDepositedValidators -= consolidationCount_;\n\n // Reset the consolidation count\n consolidationCount = 0;\n consolidationTargetStrategy = address(0);\n\n // Unpause the strategy to allow further operations\n _unpause();\n\n emit ConsolidationConfirmed(\n consolidationCount_,\n activeDepositedValidators\n );\n }\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _wethWithdrawn(uint256 _amount) internal virtual;\n}\n" + }, + "contracts/strategies/plume/RoosterAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Rooster AMO strategy\n * @author Origin Protocol Inc\n * @custom:security-contact security@originprotocol.com\n */\nimport { Math as MathRooster } from \"../../../lib/rooster/v2-common/libraries/Math.sol\";\nimport { Math as Math_v5 } from \"../../../lib/rooster/openzeppelin-custom/contracts/utils/math/Math.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { IMaverickV2Pool } from \"../../interfaces/plume/IMaverickV2Pool.sol\";\nimport { IMaverickV2Quoter } from \"../../interfaces/plume/IMaverickV2Quoter.sol\";\nimport { IMaverickV2LiquidityManager } from \"../../interfaces/plume/IMaverickV2LiquidityManager.sol\";\nimport { IMaverickV2PoolLens } from \"../../interfaces/plume/IMaverickV2PoolLens.sol\";\nimport { IMaverickV2Position } from \"../../interfaces/plume/IMaverickV2Position.sol\";\nimport { IVotingDistributor } from \"../../interfaces/plume/IVotingDistributor.sol\";\nimport { IPoolDistributor } from \"../../interfaces/plume/IPoolDistributor.sol\";\n// importing custom version of rooster TickMath because of dependency collision. Maverick uses\n// a newer OpenZepplin Math library with functionality that is not present in 4.4.2 (the one we use)\nimport { TickMath } from \"../../../lib/rooster/v2-common/libraries/TickMath.sol\";\n\ncontract RoosterAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n using SafeCast for uint256;\n\n /***************************************\n Storage slot members\n ****************************************/\n\n /// @notice NFT tokenId of the liquidity position\n ///\n /// @dev starts with value of 1 and can not be 0\n // solhint-disable-next-line max-line-length\n /// https://github.com/rooster-protocol/rooster-contracts/blob/fbfecbc519e4495b12598024a42630b4a8ea4489/v2-common/contracts/base/Nft.sol#L14\n uint256 public tokenId;\n /// @dev Minimum amount of tokens the strategy would be able to withdraw from the pool.\n /// minimum amount of tokens are withdrawn at a 1:1 price\n /// Important: Underlying assets contains only assets that are deposited in the underlying Rooster pool.\n /// WETH or OETH held by this contract is not accounted for in underlying assets\n uint256 public underlyingAssets;\n /// @notice Marks the start of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareStart;\n /// @notice Marks the end of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareEnd;\n /// @dev reserved for inheritance\n int256[46] private __reserved;\n\n /***************************************\n Constants, structs and events\n ****************************************/\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the OETH token contract\n address public immutable OETH;\n /// @notice the underlying AMO Maverick (Rooster) pool\n IMaverickV2Pool public immutable mPool;\n /// @notice the Liquidity manager used to add liquidity to the pool\n IMaverickV2LiquidityManager public immutable liquidityManager;\n /// @notice the Maverick V2 poolLens\n ///\n /// @dev only used to provide the pool's current sqrtPrice\n IMaverickV2PoolLens public immutable poolLens;\n /// @notice the Maverick V2 position\n ///\n /// @dev provides details of the NFT LP position and offers functions to\n /// remove the liquidity.\n IMaverickV2Position public immutable maverickPosition;\n /// @notice the Maverick Quoter\n IMaverickV2Quoter public immutable quoter;\n /// @notice the Maverick Voting Distributor\n IVotingDistributor public immutable votingDistributor;\n /// @notice the Maverick Pool Distributor\n IPoolDistributor public immutable poolDistributor;\n\n /// @notice sqrtPriceTickLower\n /// @dev tick lower represents the lower price of OETH priced in WETH. Meaning the pool\n /// offers more than 1 OETH for 1 WETH. In other terms to get 1 OETH the swap needs to offer 0.9999 WETH\n /// this is where purchasing OETH with WETH within the liquidity position is the cheapest.\n ///\n /// _____________________\n /// | | |\n /// | WETH | OETH |\n /// | | |\n /// | | |\n /// --------- * ---- * ---------- * ---------\n /// currentPrice\n /// sqrtPriceHigher-(1:1 parity)\n /// sqrtPriceLower\n ///\n ///\n /// Price is defined as price of token1 in terms of token0. (token1 / token0)\n /// @notice sqrtPriceTickLower - OETH is priced 0.9999 WETH\n uint256 public immutable sqrtPriceTickLower;\n /// @notice sqrtPriceTickHigher\n /// @dev tick higher represents 1:1 price parity of WETH to OETH\n uint256 public immutable sqrtPriceTickHigher;\n /// @dev price at parity (in OETH this is equal to sqrtPriceTickHigher)\n uint256 public immutable sqrtPriceAtParity;\n /// @notice The tick where the strategy deploys the liquidity to\n int32 public constant TICK_NUMBER = -1;\n /// @notice Minimum liquidity that must be exceeded to continue with the action\n /// e.g. deposit, add liquidity\n uint256 public constant ACTION_THRESHOLD = 1e12;\n /// @notice Maverick pool static liquidity bin type\n uint8 public constant MAV_STATIC_BIN_KIND = 0;\n /// @dev a threshold under which the contract no longer allows for the protocol to rebalance. Guarding\n /// against a strategist / guardian being taken over and with multiple transactions draining the\n /// protocol funds.\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n /// @notice Emitted when the allowed interval within which the strategy contract is allowed to deposit\n /// liquidity to the underlying pool is updated.\n /// @param allowedWethShareStart The start of the interval\n /// @param allowedWethShareEnd The end of the interval\n event PoolWethShareIntervalUpdated(\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n /// @notice Emitted when liquidity is removed from the underlying pool\n /// @param withdrawLiquidityShare Share of strategy's liquidity that has been removed\n /// @param removedWETHAmount The amount of WETH removed\n /// @param removedOETHAmount The amount of OETH removed\n /// @param underlyingAssets Updated amount of strategy's underlying assets\n event LiquidityRemoved(\n uint256 withdrawLiquidityShare,\n uint256 removedWETHAmount,\n uint256 removedOETHAmount,\n uint256 underlyingAssets\n );\n\n /// @notice Emitted when the underlying pool is rebalanced\n /// @param currentPoolWethShare The resulting share of strategy's liquidity\n /// in the TICK_NUMBER\n event PoolRebalanced(uint256 currentPoolWethShare);\n\n /// @notice Emitted when the amount of underlying assets the strategy hold as\n /// liquidity in the pool is updated.\n /// @param underlyingAssets Updated amount of strategy's underlying assets\n event UnderlyingAssetsUpdated(uint256 underlyingAssets);\n\n /// @notice Emitted when liquidity is added to the underlying pool\n /// @param wethAmountDesired Amount of WETH desired to be deposited\n /// @param oethAmountDesired Amount of OETH desired to be deposited\n /// @param wethAmountSupplied Amount of WETH deposited\n /// @param oethAmountSupplied Amount of OETH deposited\n /// @param tokenId NFT liquidity token id\n /// @param underlyingAssets Updated amount of underlying assets\n event LiquidityAdded(\n uint256 wethAmountDesired,\n uint256 oethAmountDesired,\n uint256 wethAmountSupplied,\n uint256 oethAmountSupplied,\n uint256 tokenId,\n uint256 underlyingAssets\n ); // 0x1530ec74\n\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n ); // 0x3681e8e0\n\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth); // 0x989e5ca8\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth); // 0xa6737d87\n error OutsideExpectedTickRange(); // 0xa6e1bad2\n error SlippageCheck(uint256 tokenReceived); // 0x355cdb78\n\n /// @notice the constructor\n /// @dev This contract is intended to be used as a proxy. To prevent the\n /// potential confusion of having a functional implementation contract\n /// the constructor has the `initializer` modifier. This way the\n /// `initialize` function can not be called on the implementation contract.\n /// For the same reason the implementation contract also has the governor\n /// set to a zero address.\n /// @param _stratConfig the basic strategy configuration\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _oethAddress Address of the Erc20 OETH Token contract\n /// @param _liquidityManager Address of liquidity manager to add\n /// the liquidity\n /// @param _poolLens Address of the pool lens contract\n /// @param _maverickPosition Address of the Maverick's position contract\n /// @param _maverickQuoter Address of the Maverick's Quoter contract\n /// @param _mPool Address of the Rooster concentrated liquidity pool\n /// @param _upperTickAtParity Bool when true upperTick is the one where the\n /// price of OETH and WETH are at parity\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethAddress,\n address _liquidityManager,\n address _poolLens,\n address _maverickPosition,\n address _maverickQuoter,\n address _mPool,\n bool _upperTickAtParity,\n address _votingDistributor,\n address _poolDistributor\n ) initializer InitializableAbstractStrategy(_stratConfig) {\n require(\n address(IMaverickV2Pool(_mPool).tokenA()) == _wethAddress,\n \"WETH not TokenA\"\n );\n require(\n address(IMaverickV2Pool(_mPool).tokenB()) == _oethAddress,\n \"OETH not TokenB\"\n );\n require(\n _liquidityManager != address(0),\n \"LiquidityManager zero address not allowed\"\n );\n require(\n _maverickQuoter != address(0),\n \"Quoter zero address not allowed\"\n );\n require(_poolLens != address(0), \"PoolLens zero address not allowed\");\n require(\n _maverickPosition != address(0),\n \"Position zero address not allowed\"\n );\n require(\n _votingDistributor != address(0),\n \"Voting distributor zero address not allowed\"\n );\n require(\n _poolDistributor != address(0),\n \"Pool distributor zero address not allowed\"\n );\n\n uint256 _tickSpacing = IMaverickV2Pool(_mPool).tickSpacing();\n require(_tickSpacing == 1, \"Unsupported tickSpacing\");\n\n // tickSpacing == 1\n (sqrtPriceTickLower, sqrtPriceTickHigher) = TickMath.tickSqrtPrices(\n _tickSpacing,\n TICK_NUMBER\n );\n sqrtPriceAtParity = _upperTickAtParity\n ? sqrtPriceTickHigher\n : sqrtPriceTickLower;\n\n WETH = _wethAddress;\n OETH = _oethAddress;\n liquidityManager = IMaverickV2LiquidityManager(_liquidityManager);\n poolLens = IMaverickV2PoolLens(_poolLens);\n maverickPosition = IMaverickV2Position(_maverickPosition);\n quoter = IMaverickV2Quoter(_maverickQuoter);\n mPool = IMaverickV2Pool(_mPool);\n votingDistributor = IVotingDistributor(_votingDistributor);\n poolDistributor = IPoolDistributor(_poolDistributor);\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /**\n * @notice initialize function, to set up initial internal state\n */\n function initialize() external onlyGovernor initializer {\n // Read reward\n address[] memory _rewardTokens = new address[](1);\n _rewardTokens[0] = poolDistributor.rewardToken();\n\n require(_rewardTokens[0] != address(0), \"No reward token configured\");\n\n InitializableAbstractStrategy._initialize(\n _rewardTokens,\n new address[](0),\n new address[](0)\n );\n }\n\n /***************************************\n Configuration \n ****************************************/\n\n /**\n * @notice Set allowed pool weth share interval. After the rebalance happens\n * the share of WETH token in the ticker needs to be within the specifications\n * of the interval.\n *\n * @param _allowedWethShareStart Start of WETH share interval expressed as 18 decimal amount\n * @param _allowedWethShareEnd End of WETH share interval expressed as 18 decimal amount\n */\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external onlyGovernor {\n require(\n _allowedWethShareStart < _allowedWethShareEnd,\n \"Invalid interval\"\n );\n // can not go below 1% weth share\n require(_allowedWethShareStart > 0.01 ether, \"Invalid interval start\");\n // can not go above 95% weth share\n require(_allowedWethShareEnd < 0.95 ether, \"Invalid interval end\");\n\n allowedWethShareStart = _allowedWethShareStart;\n allowedWethShareEnd = _allowedWethShareEnd;\n emit PoolWethShareIntervalUpdated(\n _allowedWethShareStart,\n _allowedWethShareEnd\n );\n }\n\n /***************************************\n Strategy overrides \n ****************************************/\n\n /**\n * @notice Deposits funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposits all the funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n _deposit(WETH, _wethBalance);\n }\n\n /**\n * @dev Deposits funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n * Before this function can be called the initial pool position needs to already\n * be minted.\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n require(tokenId > 0, \"Initial position not minted\");\n emit Deposit(_asset, address(0), _amount);\n\n // if the pool price is not within the expected interval leave the WETH on the contract\n // as to not break the mints - in case it would be configured as a default asset strategy\n (bool _isExpectedRange, ) = _checkForExpectedPoolPrice(false);\n if (_isExpectedRange) {\n // deposit funds into the underlying pool. Because no swap is performed there is no\n // need to remove any of the liquidity beforehand.\n _rebalance(0, false, 0, 0);\n }\n }\n\n /**\n * @notice Withdraw an `amount` of WETH from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset WETH address\n * @param _amount Amount of WETH to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n _ensureWETHBalance(_amount);\n\n _withdraw(_recipient, _amount);\n }\n\n /**\n * @notice Withdraw WETH and sends it to the Vault.\n */\n function withdrawAll() external override onlyVault nonReentrant {\n if (tokenId != 0) {\n _removeLiquidity(1e18);\n }\n\n uint256 _balance = IERC20(WETH).balanceOf(address(this));\n if (_balance > 0) {\n _withdraw(vaultAddress, _balance);\n }\n }\n\n function _withdraw(address _recipient, uint256 _amount) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n * @return bool True when the _asset is WETH\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /**\n * @dev Approve the spending amounts for the assets\n */\n function _approveTokenAmounts(\n uint256 _wethAllowance,\n uint256 _oethAllowance\n ) internal {\n IERC20(WETH).approve(address(liquidityManager), _wethAllowance);\n IERC20(OETH).approve(address(liquidityManager), _oethAllowance);\n }\n\n /***************************************\n Liquidity management\n ****************************************/\n /**\n * @dev Add liquidity into the pool in the pre-configured WETH to OETH share ratios\n * defined by the allowedPoolWethShareStart|End interval.\n *\n * Normally a PoolLens contract is used to prepare the parameters to add liquidity to the\n * Rooster pools. It has some errors when doing those calculation and for that reason a\n * much more accurate Quoter contract is used. This is possible due to our requirement of\n * adding liquidity only to one tick - PoolLens supports adding liquidity into multiple ticks\n * using different distribution ratios.\n */\n function _addLiquidity() internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n // don't deposit small liquidity amounts\n if (_wethBalance <= ACTION_THRESHOLD) {\n return;\n }\n\n (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n ) = _getAddLiquidityParams(_wethBalance, 1e30);\n\n if (OETHRequired > _oethBalance) {\n IVault(vaultAddress).mintForStrategy(OETHRequired - _oethBalance);\n }\n\n _approveTokenAmounts(WETHRequired, OETHRequired);\n\n (\n uint256 _wethAmount,\n uint256 _oethAmount,\n uint32[] memory binIds\n ) = liquidityManager.addPositionLiquidityToSenderByTokenIndex(\n mPool,\n 0, // NFT token index\n packedSqrtPriceBreaks,\n packedArgs\n );\n\n require(binIds.length == 1, \"Unexpected binIds length\");\n\n // burn remaining OETH\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n\n // needs to be called after _updateUnderlyingAssets so the updated amount\n // is reflected in the event\n emit LiquidityAdded(\n _wethBalance, // wethAmountDesired\n OETHRequired, // oethAmountDesired\n _wethAmount, // wethAmountSupplied\n _oethAmount, // oethAmountSupplied\n tokenId, // tokenId\n underlyingAssets\n );\n }\n\n /**\n * @dev The function creates liquidity parameters required to be able to add liquidity to the pool.\n * The function needs to handle the 3 different cases of the way liquidity is added:\n * - only WETH present in the tick\n * - only OETH present in the tick\n * - both tokens present in the tick\n *\n */\n function _getAddLiquidityParams(uint256 _maxWETH, uint256 _maxOETH)\n internal\n returns (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n )\n {\n IMaverickV2Pool.AddLiquidityParams[]\n memory addParams = new IMaverickV2Pool.AddLiquidityParams[](1);\n int32[] memory ticks = new int32[](1);\n uint128[] memory amounts = new uint128[](1);\n ticks[0] = TICK_NUMBER;\n // arbitrary LP amount\n amounts[0] = 1e24;\n\n // construct value for Quoter with arbitrary LP amount\n IMaverickV2Pool.AddLiquidityParams memory addParam = IMaverickV2Pool\n .AddLiquidityParams({\n kind: MAV_STATIC_BIN_KIND,\n ticks: ticks,\n amounts: amounts\n });\n\n // get the WETH and OETH required to get the proportion of tokens required\n // given the arbitrary liquidity\n (WETHRequired, OETHRequired, ) = quoter.calculateAddLiquidity(\n mPool,\n addParam\n );\n\n /**\n * If either token required is 0 then the tick consists only of the other token. In that\n * case the liquidity calculations need to be done using the non 0 token. By setting the\n * tokenRequired from 0 to 1 the `min` in next step will ignore that (the bigger) value.\n */\n WETHRequired = WETHRequired == 0 ? 1 : WETHRequired;\n OETHRequired = OETHRequired == 0 ? 1 : OETHRequired;\n\n addParam.amounts[0] = Math_v5\n .min(\n ((_maxWETH - 1) * 1e24) / WETHRequired,\n ((_maxOETH - 1) * 1e24) / OETHRequired\n )\n .toUint128();\n\n // update the quotes with the actual amounts\n (WETHRequired, OETHRequired, ) = quoter.calculateAddLiquidity(\n mPool,\n addParam\n );\n\n require(_maxWETH >= WETHRequired, \"More WETH required than specified\");\n require(_maxOETH >= OETHRequired, \"More OETH required than specified\");\n\n // organize values to be used by manager\n addParams[0] = addParam;\n packedArgs = liquidityManager.packAddLiquidityArgsArray(addParams);\n // price can stay 0 if array only has one element\n packedSqrtPriceBreaks = liquidityManager.packUint88Array(\n new uint88[](1)\n );\n }\n\n /**\n * @dev Check that the Rooster pool price is within the expected\n * parameters.\n * This function works whether the strategy contract has liquidity\n * position in the pool or not. The function returns _wethSharePct\n * as a gas optimization measure.\n * @param _throwException when set to true the function throws an exception\n * when pool's price is not within expected range.\n * @return _isExpectedRange Bool expressing price is within expected range\n * @return _wethSharePct Share of WETH owned by this strategy contract in the\n * configured ticker.\n */\n function _checkForExpectedPoolPrice(bool _throwException)\n internal\n view\n returns (bool _isExpectedRange, uint256 _wethSharePct)\n {\n require(\n allowedWethShareStart != 0 && allowedWethShareEnd != 0,\n \"Weth share interval not set\"\n );\n\n uint256 _currentPrice = getPoolSqrtPrice();\n\n /**\n * First check pool price is in expected tick range\n *\n * A revert is issued even though price being equal to the lower bound as that can not\n * be within the approved tick range.\n */\n if (\n _currentPrice <= sqrtPriceTickLower ||\n _currentPrice >= sqrtPriceTickHigher\n ) {\n if (_throwException) {\n revert OutsideExpectedTickRange();\n }\n\n return (false, _currentPrice <= sqrtPriceTickLower ? 0 : 1e18);\n }\n\n // 18 decimal number expressed WETH tick share\n _wethSharePct = _getWethShare(_currentPrice);\n\n if (\n _wethSharePct < allowedWethShareStart ||\n _wethSharePct > allowedWethShareEnd\n ) {\n if (_throwException) {\n revert PoolRebalanceOutOfBounds(\n _wethSharePct,\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n return (false, _wethSharePct);\n }\n\n return (true, _wethSharePct);\n }\n\n /**\n * @notice Rebalance the pool to the desired token split and Deposit any WETH on the contract to the\n * underlying rooster pool. Print the required amount of corresponding OETH. After the rebalancing is\n * done burn any potentially remaining OETH tokens still on the strategy contract.\n *\n * This function has a slightly different behaviour depending on the status of the underlying Rooster\n * pool. The function consists of the following 3 steps:\n * 1. withdrawLiquidityOption -> this is a configurable option where either only part of the liquidity\n * necessary for the swap is removed, or all of it. This way the rebalance\n * is able to optimize for volume, for efficiency or anything in between\n * 2. swapToDesiredPosition -> move active trading price in the pool to be able to deposit WETH & OETH\n * tokens with the desired pre-configured ratios\n * 3. addLiquidity -> add liquidity into the pool respecting ratio split configuration\n *\n *\n * Exact _amountToSwap, _swapWeth & _minTokenReceived parameters shall be determined by simulating the\n * transaction off-chain. The strategy checks that after the swap the share of the tokens is in the\n * expected ranges.\n *\n * @param _amountToSwap The amount of the token to swap\n * @param _swapWeth Swap using WETH when true, use OETH when false\n * @param _minTokenReceived Slippage check -> minimum amount of token expected in return\n * @param _liquidityToRemovePct Percentage of liquidity to remove -> the percentage amount of liquidity to\n * remove before performing the swap. 1e18 denominated\n */\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived,\n uint256 _liquidityToRemovePct\n ) external nonReentrant onlyGovernorOrStrategist {\n _rebalance(\n _amountToSwap,\n _swapWeth,\n _minTokenReceived,\n _liquidityToRemovePct\n );\n }\n\n // slither-disable-start reentrancy-no-eth\n function _rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived,\n uint256 _liquidityToRemovePct\n ) internal {\n // Remove the required amount of liquidity\n if (_liquidityToRemovePct > 0) {\n _removeLiquidity(_liquidityToRemovePct);\n }\n\n // in some cases (e.g. deposits) we will just want to add liquidity and not\n // issue a swap to move the active trading position within the pool. Before or after a\n // deposit or as a standalone call the strategist might issue a rebalance to move the\n // active trading price to a more desired position.\n if (_amountToSwap > 0) {\n // In case liquidity has been removed and there is still not enough WETH owned by the\n // strategy contract remove additional required amount of WETH.\n if (_swapWeth) _ensureWETHBalance(_amountToSwap);\n\n _swapToDesiredPosition(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n\n // calling check liquidity early so we don't get unexpected errors when adding liquidity\n // in the later stages of this function\n _checkForExpectedPoolPrice(true);\n\n _addLiquidity();\n\n // this call shouldn't be necessary, since adding liquidity shouldn't affect the active\n // trading price. It is a defensive programming measure.\n (, uint256 _wethSharePct) = _checkForExpectedPoolPrice(true);\n\n // revert if protocol insolvent\n _solvencyAssert();\n\n emit PoolRebalanced(_wethSharePct);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @dev Perform a swap so that after the swap the tick has the desired WETH to OETH token share.\n */\n function _swapToDesiredPosition(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n IERC20 _tokenToSwap = IERC20(_swapWeth ? WETH : OETH);\n uint256 _balance = _tokenToSwap.balanceOf(address(this));\n\n if (_balance < _amountToSwap) {\n // This should never trigger since _ensureWETHBalance will already\n // throw an error if there is not enough WETH\n if (_swapWeth) {\n revert NotEnoughWethForSwap(_balance, _amountToSwap);\n }\n // if swapping OETH\n uint256 mintForSwap = _amountToSwap - _balance;\n IVault(vaultAddress).mintForStrategy(mintForSwap);\n }\n\n // SafeERC20 is used for IERC20 transfers. Not sure why slither complains\n // slither-disable-next-line unchecked-transfer\n _tokenToSwap.transfer(address(mPool), _amountToSwap);\n\n // tickLimit: the furthest tick a swap will execute in. If no limit is desired,\n // value should be set to type(int32).max for a tokenAIn (WETH) swap\n // and type(int32).min for a swap where tokenB (OETH) is the input\n\n IMaverickV2Pool.SwapParams memory swapParams = IMaverickV2Pool\n // exactOutput defines whether the amount specified is the output\n // or the input amount of the swap\n .SwapParams({\n amount: _amountToSwap,\n tokenAIn: _swapWeth,\n exactOutput: false,\n tickLimit: TICK_NUMBER\n });\n\n // swaps without a callback as the assets are already sent to the pool\n (, uint256 amountOut) = mPool.swap(\n address(this),\n swapParams,\n bytes(\"\")\n );\n\n /**\n * There could be additional checks here for validating minTokenReceived is within the\n * expected range (e.g. 99% - 101% of the token sent in). Though that doesn't provide\n * any additional security. After the swap the `_checkForExpectedPoolPrice` validates\n * that the swap has moved the price into the expected tick (# -1).\n *\n * If the guardian forgets to set a `_minTokenReceived` and a sandwich attack bends\n * the pool before the swap the `_checkForExpectedPoolPrice` will fail the transaction.\n *\n * A check would not prevent a compromised guardian from stealing funds as multiple\n * transactions each loosing smaller amount of funds are still possible.\n */\n if (amountOut < _minTokenReceived) {\n revert SlippageCheck(amountOut);\n }\n\n /**\n * In the interest of each function in `_rebalance` to leave the contract state as\n * clean as possible the OETH tokens here are burned. This decreases the\n * dependence where `_swapToDesiredPosition` function relies on later functions\n * (`addLiquidity`) to burn the OETH. Reducing the risk of error introduction.\n */\n _burnOethOnTheContract();\n }\n\n /**\n * @dev This function removes the appropriate amount of liquidity to ensure that the required\n * amount of WETH is available on the contract\n *\n * @param _amount WETH balance required on the contract\n */\n function _ensureWETHBalance(uint256 _amount) internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance >= _amount) {\n return;\n }\n\n require(tokenId != 0, \"No liquidity available\");\n uint256 _additionalWethRequired = _amount - _wethBalance;\n (uint256 _wethInThePool, ) = getPositionPrincipal();\n\n if (_wethInThePool < _additionalWethRequired) {\n revert NotEnoughWethLiquidity(\n _wethInThePool,\n _additionalWethRequired\n );\n }\n\n uint256 shareOfWethToRemove = _wethInThePool <= 1\n ? 1e18\n : Math_v5.min(\n /**\n * When dealing with shares of liquidity to remove there is always some\n * rounding involved. After extensive fuzz testing the below approach\n * yielded the best results where the strategy overdraws the least and\n * never removes insufficient amount of WETH.\n */\n (_additionalWethRequired + 2).divPrecisely(_wethInThePool - 1) +\n 2,\n 1e18\n );\n\n _removeLiquidity(shareOfWethToRemove);\n }\n\n /**\n * @dev Decrease partial or all liquidity from the pool.\n * @param _liquidityToDecrease The amount of liquidity to remove denominated in 1e18\n */\n function _removeLiquidity(uint256 _liquidityToDecrease) internal {\n require(_liquidityToDecrease > 0, \"Must remove some liquidity\");\n require(\n _liquidityToDecrease <= 1e18,\n \"Can not remove more than 100% of liquidity\"\n );\n\n // 0 indicates the first (and only) bin in the NFT LP position.\n IMaverickV2Pool.RemoveLiquidityParams memory params = maverickPosition\n .getRemoveParams(tokenId, 0, _liquidityToDecrease);\n (uint256 _amountWeth, uint256 _amountOeth) = maverickPosition\n .removeLiquidityToSender(tokenId, mPool, params);\n\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n\n // needs to be called after the _updateUnderlyingAssets so the updated amount is reflected\n // in the event\n emit LiquidityRemoved(\n _liquidityToDecrease,\n _amountWeth,\n _amountOeth,\n underlyingAssets\n );\n }\n\n /**\n * @dev Burns any OETH tokens remaining on the strategy contract if the balance is\n * above the action threshold.\n */\n function _burnOethOnTheContract() internal {\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(_oethBalance);\n }\n\n /**\n * @notice Returns the percentage of WETH liquidity in the configured ticker\n * owned by this strategy contract.\n * @return uint256 1e18 denominated percentage expressing the share\n */\n function getWETHShare() external view returns (uint256) {\n uint256 _currentPrice = getPoolSqrtPrice();\n return _getWethShare(_currentPrice);\n }\n\n /**\n * @dev Returns the share of WETH in tick denominated in 1e18\n */\n function _getWethShare(uint256 _currentPrice)\n internal\n view\n returns (uint256)\n {\n (\n uint256 wethAmount,\n uint256 oethAmount\n ) = _reservesInTickForGivenPriceAndLiquidity(\n sqrtPriceTickLower,\n sqrtPriceTickHigher,\n _currentPrice,\n 1e24\n );\n\n return wethAmount.divPrecisely(wethAmount + oethAmount);\n }\n\n /**\n * @notice Returns the current pool price in square root\n * @return Square root of the pool price\n */\n function getPoolSqrtPrice() public view returns (uint256) {\n return poolLens.getPoolSqrtPrice(mPool);\n }\n\n /**\n * @notice Returns the current active trading tick of the underlying pool\n * @return _currentTick Current pool trading tick\n */\n function getCurrentTradingTick() public view returns (int32 _currentTick) {\n _currentTick = mPool.getState().activeTick;\n }\n\n /**\n * @notice Mint the initial NFT position\n *\n * @dev This amount is \"gifted\" to the strategy contract and will count as a yield\n * surplus.\n */\n // slither-disable-start reentrancy-no-eth\n function mintInitialPosition() external onlyGovernor nonReentrant {\n require(tokenId == 0, \"Initial position already minted\");\n (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n ) = _getAddLiquidityParams(1e16, 1e16);\n\n // Mint rounded up OETH amount\n if (OETHRequired > 0) {\n IVault(vaultAddress).mintForStrategy(OETHRequired);\n }\n\n _approveTokenAmounts(WETHRequired, OETHRequired);\n\n // Store the tokenId before calling updateUnderlyingAssets as it relies on the tokenId\n // not being 0\n (, , , tokenId) = liquidityManager.mintPositionNftToSender(\n mPool,\n packedSqrtPriceBreaks,\n packedArgs\n );\n\n // burn remaining OETH\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @notice Returns the balance of tokens the strategy holds in the LP position\n * @return _amountWeth Amount of WETH in position\n * @return _amountOeth Amount of OETH in position\n */\n function getPositionPrincipal()\n public\n view\n returns (uint256 _amountWeth, uint256 _amountOeth)\n {\n if (tokenId == 0) {\n return (0, 0);\n }\n\n (_amountWeth, _amountOeth, ) = _getPositionInformation();\n }\n\n /**\n * @dev Returns the balance of tokens the strategy holds in the LP position\n * @return _amountWeth Amount of WETH in position\n * @return _amountOeth Amount of OETH in position\n * @return liquidity Amount of liquidity in the position\n */\n function _getPositionInformation()\n internal\n view\n returns (\n uint256 _amountWeth,\n uint256 _amountOeth,\n uint256 liquidity\n )\n {\n IMaverickV2Position.PositionFullInformation\n memory positionInfo = maverickPosition.tokenIdPositionInformation(\n tokenId,\n 0\n );\n\n require(\n positionInfo.liquidities.length == 1,\n \"Unexpected liquidities length\"\n );\n require(positionInfo.ticks.length == 1, \"Unexpected ticks length\");\n\n _amountWeth = positionInfo.amountA;\n _amountOeth = positionInfo.amountB;\n liquidity = positionInfo.liquidities[0];\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99.8%) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethSupply = IERC20(OETH).totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethSupply) < SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /**\n * @dev Collect Rooster reward token, and send it to the harvesterAddress\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Do nothing if there's no position minted\n if (tokenId > 0) {\n uint32[] memory binIds = new uint32[](1);\n IMaverickV2Pool.TickState memory tickState = mPool.getTick(\n TICK_NUMBER\n );\n // get the binId for the MAV_STATIC_BIN_KIND in tick TICK_NUMBER (-1)\n binIds[0] = tickState.binIdsByTick[0];\n\n uint256 lastEpoch = votingDistributor.lastEpoch();\n\n poolDistributor.claimLp(\n address(this),\n tokenId,\n mPool,\n binIds,\n lastEpoch\n );\n }\n\n // Run the internal inherited function\n _collectRewardTokens();\n }\n\n /***************************************\n Balances and Fees\n ****************************************/\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256)\n {\n require(_asset == WETH, \"Only WETH supported\");\n\n // because of PoolLens inaccuracy there is usually some dust WETH left on the contract\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n // just paranoia check, in case there is OETH in the strategy that for some reason hasn't\n // been burned yet. This should always be 0.\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n return underlyingAssets + _wethBalance + _oethBalance;\n }\n\n /// @dev This function updates the amount of underlying assets with the approach of the least possible\n /// total tokens extracted for the current liquidity in the pool.\n function _updateUnderlyingAssets() internal {\n /**\n * Our net value represent the smallest amount of tokens we are able to extract from the position\n * given our liquidity.\n *\n * The least amount of tokens ex-tractable from the position is where the active trading price is\n * at the edge between tick -1 & tick 0. There the pool is offering 1:1 trades between WETH & OETH.\n * At that moment the pool consists completely of WETH and no OETH.\n *\n * The more swaps from OETH -> WETH happen on the pool the more the price starts to move away from the tick 0\n * towards the middle of tick -1 making OETH (priced in WETH) cheaper.\n */\n\n uint256 _wethAmount = tokenId == 0 ? 0 : _balanceInPosition();\n\n underlyingAssets = _wethAmount;\n emit UnderlyingAssetsUpdated(_wethAmount);\n }\n\n /**\n * @dev Strategy reserves (which consist only of WETH in case of Rooster - Plume pool)\n * when the tick price is closest to parity - assuring the lowest amount of tokens\n * returned for the current position liquidity.\n */\n function _balanceInPosition() internal view returns (uint256 _wethBalance) {\n (, , uint256 liquidity) = _getPositionInformation();\n\n uint256 _oethBalance;\n\n (_wethBalance, _oethBalance) = _reservesInTickForGivenPriceAndLiquidity(\n sqrtPriceTickLower,\n sqrtPriceTickHigher,\n sqrtPriceAtParity,\n liquidity\n );\n\n require(_oethBalance == 0, \"Non zero oethBalance\");\n }\n\n /**\n * @notice Tick dominance denominated in 1e18\n * @return _tickDominance The share of liquidity in TICK_NUMBER tick owned\n * by the strategy contract denominated in 1e18\n */\n function tickDominance() public view returns (uint256 _tickDominance) {\n IMaverickV2Pool.TickState memory tickState = mPool.getTick(TICK_NUMBER);\n\n uint256 wethReserve = tickState.reserveA;\n uint256 oethReserve = tickState.reserveB;\n\n // prettier-ignore\n (uint256 _amountWeth, uint256 _amountOeth, ) = _getPositionInformation();\n\n if (wethReserve + oethReserve == 0) {\n return 0;\n }\n\n _tickDominance = (_amountWeth + _amountOeth).divPrecisely(\n wethReserve + oethReserve\n );\n }\n\n /***************************************\n Hidden functions\n ****************************************/\n /**\n * @dev Unsupported\n */\n function setPTokenAddress(address, address) external pure override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function removePToken(uint256) external pure override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function _abstractSetPToken(address, address) internal pure override {\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function safeApproveAllTokens() external pure override {\n // all the amounts are approved at the time required\n revert(\"Unsupported method\");\n }\n\n /***************************************\n Maverick liquidity utilities\n ****************************************/\n\n /// @notice Calculates deltaA = liquidity * (sqrt(upper) - sqrt(lower))\n /// Calculates deltaB = liquidity / sqrt(lower) - liquidity / sqrt(upper),\n /// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower))\n ///\n /// @dev refactored from here:\n // solhint-disable-next-line max-line-length\n /// https://github.com/rooster-protocol/rooster-contracts/blob/main/v2-supplemental/contracts/libraries/LiquidityUtilities.sol#L665-L695\n function _reservesInTickForGivenPriceAndLiquidity(\n uint256 _lowerSqrtPrice,\n uint256 _upperSqrtPrice,\n uint256 _newSqrtPrice,\n uint256 _liquidity\n ) internal pure returns (uint128 reserveA, uint128 reserveB) {\n if (_liquidity == 0) {\n (reserveA, reserveB) = (0, 0);\n } else {\n uint256 lowerEdge = MathRooster.max(_lowerSqrtPrice, _newSqrtPrice);\n\n reserveA = MathRooster\n .mulCeil(\n _liquidity,\n MathRooster.clip(\n MathRooster.min(_upperSqrtPrice, _newSqrtPrice),\n _lowerSqrtPrice\n )\n )\n .toUint128();\n reserveB = MathRooster\n .mulDivCeil(\n _liquidity,\n 1e18 * MathRooster.clip(_upperSqrtPrice, lowerEdge),\n _upperSqrtPrice * lowerEdge\n )\n .toUint128();\n }\n }\n}\n" + }, + "contracts/strategies/sonic/SonicStakingStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SonicValidatorDelegator } from \"./SonicValidatorDelegator.sol\";\nimport { IWrappedSonic } from \"../../interfaces/sonic/IWrappedSonic.sol\";\n\n/**\n * @title Staking Strategy for Sonic's native S currency\n * @author Origin Protocol Inc\n */\ncontract SonicStakingStrategy is SonicValidatorDelegator {\n // For future use\n uint256[50] private __gap;\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wrappedSonic,\n address _sfc\n ) SonicValidatorDelegator(_baseConfig, _wrappedSonic, _sfc) {}\n\n /// @notice Deposit wrapped S asset into the underlying platform.\n /// @param _asset Address of asset to deposit. Has to be Wrapped Sonic (wS).\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposit Wrapped Sonic (wS) to this strategy and delegate to a validator.\n * @param _asset Address of Wrapped Sonic (wS) token\n * @param _amount Amount of Wrapped Sonic (wS) to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal virtual {\n require(_amount > 0, \"Must deposit something\");\n\n _delegate(_amount);\n emit Deposit(_asset, address(0), _amount);\n }\n\n /**\n * @notice Deposit the entire balance of wrapped S in this strategy contract into\n * the underlying platform.\n */\n function depositAll() external virtual override onlyVault nonReentrant {\n uint256 wSBalance = IERC20(wrappedSonic).balanceOf(address(this));\n\n if (wSBalance > 0) {\n _deposit(wrappedSonic, wSBalance);\n }\n }\n\n /// @notice Withdraw Wrapped Sonic (wS) from this strategy contract.\n /// Used only if some wS is lingering on the contract.\n /// That can happen only when someone sends wS directly to this contract\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset Address of the Wrapped Sonic (wS) token\n /// @param _amount Amount of Wrapped Sonic (wS) to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal override {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(_asset).transfer(_recipient, _amount);\n\n emit Withdrawal(wrappedSonic, address(0), _amount);\n }\n\n /// @notice Transfer all Wrapped Sonic (wS) deposits back to the vault.\n /// This does not withdraw from delegated validators. That has to be done separately with `undelegate`.\n /// Any native S in this strategy will be withdrawn.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 balance = address(this).balance;\n if (balance > 0) {\n IWrappedSonic(wrappedSonic).deposit{ value: balance }();\n }\n uint256 wSBalance = IERC20(wrappedSonic).balanceOf(address(this));\n if (wSBalance > 0) {\n _withdraw(vaultAddress, wrappedSonic, wSBalance);\n }\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset token\n */\n function supportsAsset(address _asset)\n public\n view\n virtual\n override\n returns (bool)\n {\n return _asset == wrappedSonic;\n }\n\n /**\n * @notice is not supported for this strategy as the\n * Wrapped Sonic (wS) token is set at deploy time.\n */\n function setPTokenAddress(address, address)\n external\n view\n override\n onlyGovernor\n {\n revert(\"unsupported function\");\n }\n\n /// @notice is not used by this strategy as all staking rewards are restaked\n function collectRewardTokens() external override nonReentrant {\n revert(\"unsupported function\");\n }\n\n /**\n * @notice is not supported for this strategy as the\n * Wrapped Sonic (wS) token is set at deploy time.\n */\n function removePToken(uint256) external view override onlyGovernor {\n revert(\"unsupported function\");\n }\n\n /// @dev is not used by this strategy but must be implemented as it's abstract\n /// in the inherited `InitializableAbstractStrategy` contract.\n function _abstractSetPToken(address, address) internal virtual override {}\n\n /// @notice is not used by this strategy\n function safeApproveAllTokens() external override onlyGovernor {}\n}\n" + }, + "contracts/strategies/sonic/SonicSwapXAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title SwapX Algorithmic Market Maker (AMO) Strategy\n * @notice AMO strategy for the SwapX OS/wS stable pool\n * @author Origin Protocol Inc\n */\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport { sqrt } from \"../../utils/PRBMath.sol\";\nimport { IBasicToken } from \"../../interfaces/IBasicToken.sol\";\nimport { IPair } from \"../../interfaces/sonic/ISwapXPair.sol\";\nimport { IGauge } from \"../../interfaces/sonic/ISwapXGauge.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\n\ncontract SonicSwapXAMOStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @notice a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n /// @notice Precision for the SwapX Stable AMM (sAMM) invariant k.\n uint256 public constant PRECISION = 1e18;\n\n /// @notice Address of the Wrapped S (wS) token.\n address public immutable ws;\n\n /// @notice Address of the OS token contract.\n address public immutable os;\n\n /// @notice Address of the SwapX Stable pool contract.\n address public immutable pool;\n\n /// @notice Address of the SwapX Gauge contract.\n address public immutable gauge;\n\n /// @notice The max amount the OS/wS price can deviate from peg (1e18)\n /// before deposits are reverted scaled to 18 decimals.\n /// eg 0.01e18 or 1e16 is 1% which is 100 basis points.\n /// This is the amount below and above peg so a 50 basis point deviation (0.005e18)\n /// allows a price range from 0.995 to 1.005.\n uint256 public maxDepeg;\n\n event SwapOTokensToPool(\n uint256 osMinted,\n uint256 wsDepositAmount,\n uint256 osDepositAmount,\n uint256 lpTokens\n );\n event SwapAssetsToPool(\n uint256 wsSwapped,\n uint256 lpTokens,\n uint256 osBurnt\n );\n event MaxDepegUpdated(uint256 maxDepeg);\n\n /**\n * @dev Verifies that the caller is the Strategist of the Vault.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Skim the SwapX pool in case any extra wS or OS tokens were added\n */\n modifier skimPool() {\n IPair(pool).skim(address(this));\n _;\n }\n\n /**\n * @dev Checks the pool is balanced enough to allow deposits.\n */\n modifier nearBalancedPool() {\n // OS/wS price = wS / OS\n // Get the OS/wS price for selling 1 OS for wS\n // As OS is 1, the wS amount is the OS/wS price\n uint256 sellPrice = IPair(pool).getAmountOut(1e18, os);\n\n // Get the amount of OS received from selling 1 wS. This is buying OS.\n uint256 osAmount = IPair(pool).getAmountOut(1e18, ws);\n // Convert to a OS/wS price = wS / OS\n uint256 buyPrice = 1e36 / osAmount;\n\n uint256 pegPrice = 1e18;\n\n require(\n sellPrice >= pegPrice - maxDepeg && buyPrice <= pegPrice + maxDepeg,\n \"price out of range\"\n );\n _;\n }\n\n /**\n * @dev Checks the pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do swaps against the pool.\n * Deposits and withdrawals are proportional to the pool's balances hence don't need this check.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the pool\n (uint256 wsReservesBefore, uint256 osReservesBefore, ) = IPair(pool)\n .getReserves();\n // diff = wS balance - OS balance\n int256 diffBefore = wsReservesBefore.toInt256() -\n osReservesBefore.toInt256();\n\n _;\n\n // Get the asset and OToken balances in the pool\n (uint256 wsReservesAfter, uint256 osReservesAfter, ) = IPair(pool)\n .getReserves();\n // diff = wS balance - OS balance\n int256 diffAfter = wsReservesAfter.toInt256() -\n osReservesAfter.toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OS, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"Assets overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of wS, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"OTokens overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n /**\n * @param _baseConfig The `platformAddress` is the address of the SwapX pool.\n * The `vaultAddress` is the address of the Origin Sonic Vault.\n * @param _os Address of the OS token.\n * @param _ws Address of the Wrapped S (wS) token.\n * @param _gauge Address of the SwapX gauge for the pool.\n */\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _os,\n address _ws,\n address _gauge\n ) InitializableAbstractStrategy(_baseConfig) {\n // Check the pool tokens are correct\n require(\n IPair(_baseConfig.platformAddress).token0() == _ws &&\n IPair(_baseConfig.platformAddress).token1() == _os,\n \"Incorrect pool tokens\"\n );\n // Checked both tokens are to 18 decimals\n require(\n IBasicToken(_ws).decimals() == 18 &&\n IBasicToken(_os).decimals() == 18,\n \"Incorrect token decimals\"\n );\n // Check the SwapX pool is a Stable AMM (sAMM)\n require(\n IPair(_baseConfig.platformAddress).isStable() == true,\n \"Pool not stable\"\n );\n // Check the gauge is for the pool\n require(\n IGauge(_gauge).TOKEN() == _baseConfig.platformAddress,\n \"Incorrect gauge\"\n );\n\n // Set the immutable variables\n os = _os;\n ws = _ws;\n pool = _baseConfig.platformAddress;\n gauge = _gauge;\n\n // This is an implementation contract. The governor is set in the proxy contract.\n _setGovernor(address(0));\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as SwapX strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Array containing SWPx token address\n * @param _maxDepeg The max amount the OS/wS price can deviate from peg (1e18) before deposits are reverted.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n uint256 _maxDepeg\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = pool;\n\n address[] memory _assets = new address[](1);\n _assets[0] = ws;\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n maxDepeg = _maxDepeg;\n\n _approveBase();\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit an amount of Wrapped S (wS) into the SwapX pool.\n * Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @dev This tx must be wrapped by the VaultValueChecker.\n * To minimize loses, the pool should be rebalanced before depositing.\n * The pool's OS/wS price must be within the maxDepeg range.\n * @param _asset Address of Wrapped S (wS) token.\n * @param _wsAmount Amount of Wrapped S (wS) tokens to deposit.\n */\n function deposit(address _asset, uint256 _wsAmount)\n external\n override\n onlyVault\n nonReentrant\n skimPool\n nearBalancedPool\n {\n require(_asset == ws, \"Unsupported asset\");\n require(_wsAmount > 0, \"Must deposit something\");\n\n (uint256 osDepositAmount, ) = _deposit(_wsAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the deposited wS tokens\n emit Deposit(ws, pool, _wsAmount);\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osDepositAmount);\n }\n\n /**\n * @notice Deposit all the strategy's Wrapped S (wS) tokens into the SwapX pool.\n * Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @dev This tx must be wrapped by the VaultValueChecker.\n * To minimize loses, the pool should be rebalanced before depositing.\n * The pool's OS/wS price must be within the maxDepeg range.\n */\n function depositAll()\n external\n override\n onlyVault\n nonReentrant\n skimPool\n nearBalancedPool\n {\n uint256 wsBalance = IERC20(ws).balanceOf(address(this));\n if (wsBalance > 0) {\n (uint256 osDepositAmount, ) = _deposit(wsBalance);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the deposited wS tokens\n emit Deposit(ws, pool, wsBalance);\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osDepositAmount);\n }\n }\n\n /**\n * @dev Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @param _wsAmount Amount of Wrapped S (wS) tokens to deposit.\n * @return osDepositAmount Amount of OS tokens minted and deposited into the pool.\n * @return lpTokens Amount of SwapX pool LP tokens minted and deposited into the gauge.\n */\n function _deposit(uint256 _wsAmount)\n internal\n returns (uint256 osDepositAmount, uint256 lpTokens)\n {\n // Calculate the required amount of OS to mint based on the wS amount.\n osDepositAmount = _calcTokensToMint(_wsAmount);\n\n // Mint the required OS tokens to this strategy\n IVault(vaultAddress).mintForStrategy(osDepositAmount);\n\n // Add wS and OS liquidity to the pool and stake in gauge\n lpTokens = _depositToPoolAndGauge(_wsAmount, osDepositAmount);\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw wS and OS from the SwapX pool, burn the OS,\n * and transfer the wS to the recipient.\n * @param _recipient Address of the Vault.\n * @param _asset Address of the Wrapped S (wS) contract.\n * @param _wsAmount Amount of Wrapped S (wS) to withdraw.\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _wsAmount\n ) external override onlyVault nonReentrant skimPool {\n require(_wsAmount > 0, \"Must withdraw something\");\n require(_asset == ws, \"Unsupported asset\");\n // This strategy can't be set as a default strategy for wS in the Vault.\n // This means the recipient must always be the Vault.\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n // Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n uint256 lpTokens = _calcTokensToBurn(_wsAmount);\n\n // Withdraw pool LP tokens from the gauge and remove assets from from the pool\n _withdrawFromGaugeAndPool(lpTokens);\n\n // Burn all the removed OS and any that was left in the strategy\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Transfer wS to the recipient\n // Note there can be a dust amount of wS left in the strategy as\n // the burn of the pool's LP tokens is rounded up\n require(\n IERC20(ws).balanceOf(address(this)) >= _wsAmount,\n \"Not enough wS removed from pool\"\n );\n IERC20(ws).safeTransfer(_recipient, _wsAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the withdrawn wS tokens\n emit Withdrawal(ws, pool, _wsAmount);\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n }\n\n /**\n * @notice Withdraw all pool LP tokens from the gauge,\n * remove all wS and OS from the SwapX pool,\n * burn all the OS tokens,\n * and transfer all the wS to the Vault contract.\n * @dev There is no solvency check here as withdrawAll can be called to\n * quickly secure assets to the Vault in emergencies.\n */\n function withdrawAll()\n external\n override\n onlyVaultOrGovernor\n nonReentrant\n skimPool\n {\n // Get all the pool LP tokens the strategy has staked in the gauge\n uint256 lpTokens = IGauge(gauge).balanceOf(address(this));\n // Can not withdraw zero LP tokens from the gauge\n if (lpTokens == 0) return;\n\n if (IGauge(gauge).emergency()) {\n // The gauge is in emergency mode\n _emergencyWithdrawFromGaugeAndPool();\n } else {\n // Withdraw pool LP tokens from the gauge and remove assets from from the pool\n _withdrawFromGaugeAndPool(lpTokens);\n }\n\n // Burn all OS in this strategy contract\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Get the strategy contract's wS balance.\n // This includes all that was removed from the SwapX pool and\n // any that was sitting in the strategy contract before the removal.\n uint256 wsBalance = IERC20(ws).balanceOf(address(this));\n IERC20(ws).safeTransfer(vaultAddress, wsBalance);\n\n // Emit event for the withdrawn wS tokens\n emit Withdrawal(ws, pool, wsBalance);\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n }\n\n /***************************************\n Pool Rebalancing\n ****************************************/\n\n /** @notice Used when there is more OS than wS in the pool.\n * wS and OS is removed from the pool, the received wS is swapped for OS\n * and the left over OS in the strategy is burnt.\n * The OS/wS price is < 1.0 so OS is being bought at a discount.\n * @param _wsAmount Amount of Wrapped S (wS) to swap into the pool.\n */\n function swapAssetsToPool(uint256 _wsAmount)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n skimPool\n {\n require(_wsAmount > 0, \"Must swap something\");\n\n // 1. Partially remove liquidity so there’s enough wS for the swap\n\n // Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n uint256 lpTokens = _calcTokensToBurn(_wsAmount);\n require(lpTokens > 0, \"No LP tokens to burn\");\n\n _withdrawFromGaugeAndPool(lpTokens);\n\n // 2. Swap wS for OS against the pool\n // Swap exact amount of wS for OS against the pool\n // There can be a dust amount of wS left in the strategy as the burn of the pool's LP tokens is rounded up\n _swapExactTokensForTokens(_wsAmount, ws, os);\n\n // 3. Burn all the OS left in the strategy from the remove liquidity and swap\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n // Emit event for the swap\n emit SwapAssetsToPool(_wsAmount, lpTokens, osToBurn);\n }\n\n /**\n * @notice Used when there is more wS than OS in the pool.\n * OS is minted and swapped for wS against the pool,\n * more OS is minted and added back into the pool with the swapped out wS.\n * The OS/wS price is > 1.0 so OS is being sold at a premium.\n * @param _osAmount Amount of OS to swap into the pool.\n */\n function swapOTokensToPool(uint256 _osAmount)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n skimPool\n {\n require(_osAmount > 0, \"Must swap something\");\n\n // 1. Mint OS so it can be swapped into the pool\n\n // There can be OS in the strategy from skimming the pool\n uint256 osInStrategy = IERC20(os).balanceOf(address(this));\n require(_osAmount >= osInStrategy, \"Too much OS in strategy\");\n uint256 osToMint = _osAmount - osInStrategy;\n\n // Mint the required OS tokens to this strategy\n IVault(vaultAddress).mintForStrategy(osToMint);\n\n // 2. Swap OS for wS against the pool\n _swapExactTokensForTokens(_osAmount, os, ws);\n\n // The wS is from the swap and any wS that was sitting in the strategy\n uint256 wsDepositAmount = IERC20(ws).balanceOf(address(this));\n\n // 3. Add wS and OS back to the pool in proportion to the pool's reserves\n (uint256 osDepositAmount, uint256 lpTokens) = _deposit(wsDepositAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osToMint + osDepositAmount);\n // Emit event for the swap\n emit SwapOTokensToPool(\n osToMint,\n wsDepositAmount,\n osDepositAmount,\n lpTokens\n );\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Get the wS value of assets in the strategy and SwapX pool.\n * The value of the assets in the pool is calculated assuming the pool is balanced.\n * This way the value can not be manipulated by changing the pool's token balances.\n * @param _asset Address of the Wrapped S (wS) token\n * @return balance Total value in wS.\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == ws, \"Unsupported asset\");\n\n // wS balance needed here for the balance check that happens from vault during depositing.\n balance = IERC20(ws).balanceOf(address(this));\n\n // This assumes 1 gauge LP token = 1 pool LP token\n uint256 lpTokens = IGauge(gauge).balanceOf(address(this));\n if (lpTokens == 0) return balance;\n\n // Add the strategy’s share of the wS and OS tokens in the SwapX pool if the pool was balanced.\n balance += _lpValue(lpTokens);\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == ws;\n }\n\n /**\n * @notice Collect accumulated SWPx (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect SWPx rewards from the gauge\n IGauge(gauge).getReward();\n\n _collectRewardTokens();\n }\n\n /***************************************\n Internal SwapX Pool and Gauge Functions\n ****************************************/\n\n /**\n * @dev Calculate the required amount of OS to mint based on the wS amount.\n * This ensures the proportion of OS tokens being added to the pool matches the proportion of wS tokens.\n * For example, if the added wS tokens is 10% of existing wS tokens in the pool,\n * then the OS tokens being added should also be 10% of the OS tokens in the pool.\n * @param _wsAmount Amount of Wrapped S (wS) to be added to the pool.\n * @return osAmount Amount of OS to be minted and added to the pool.\n */\n function _calcTokensToMint(uint256 _wsAmount)\n internal\n view\n returns (uint256 osAmount)\n {\n (uint256 wsReserves, uint256 osReserves, ) = IPair(pool).getReserves();\n require(wsReserves > 0, \"Empty pool\");\n\n // OS to add = (wS being added * OS in pool) / wS in pool\n osAmount = (_wsAmount * osReserves) / wsReserves;\n }\n\n /**\n * @dev Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n * from the pool.\n * @param _wsAmount Amount of Wrapped S (wS) to be removed from the pool.\n * @return lpTokens Amount of SwapX pool LP tokens to burn.\n */\n function _calcTokensToBurn(uint256 _wsAmount)\n internal\n view\n returns (uint256 lpTokens)\n {\n /* The SwapX pool proportionally returns the reserve tokens when removing liquidity.\n * First, calculate the proportion of required wS tokens against the pools wS reserves.\n * That same proportion is used to calculate the required amount of pool LP tokens.\n * For example, if the required wS tokens is 10% of the pool's wS reserves,\n * then 10% of the pool's LP supply needs to be burned.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognizant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on, the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n (uint256 wsReserves, , ) = IPair(pool).getReserves();\n require(wsReserves > 0, \"Empty pool\");\n\n lpTokens = (_wsAmount * IPair(pool).totalSupply()) / wsReserves;\n lpTokens += 1; // Add 1 to ensure we get enough LP tokens with rounding\n }\n\n /**\n * @dev Deposit Wrapped S (wS) and OS liquidity to the SwapX pool\n * and stake the pool's LP token in the gauge.\n * @param _wsAmount Amount of Wrapped S (wS) to deposit.\n * @param _osAmount Amount of OS to deposit.\n * @return lpTokens Amount of SwapX pool LP tokens minted.\n */\n function _depositToPoolAndGauge(uint256 _wsAmount, uint256 _osAmount)\n internal\n returns (uint256 lpTokens)\n {\n // Transfer wS to the pool\n IERC20(ws).safeTransfer(pool, _wsAmount);\n // Transfer OS to the pool\n IERC20(os).safeTransfer(pool, _osAmount);\n\n // Mint LP tokens from the pool\n lpTokens = IPair(pool).mint(address(this));\n\n // Deposit the pool's LP tokens into the gauge\n IGauge(gauge).deposit(lpTokens);\n }\n\n /**\n * @dev Withdraw pool LP tokens from the gauge and remove wS and OS from the pool.\n * @param _lpTokens Amount of SwapX pool LP tokens to withdraw from the gauge\n */\n function _withdrawFromGaugeAndPool(uint256 _lpTokens) internal {\n require(\n IGauge(gauge).balanceOf(address(this)) >= _lpTokens,\n \"Not enough LP tokens in gauge\"\n );\n\n // Withdraw pool LP tokens from the gauge\n IGauge(gauge).withdraw(_lpTokens);\n\n // Transfer the pool LP tokens to the pool\n IERC20(pool).safeTransfer(pool, _lpTokens);\n\n // Burn the LP tokens and transfer the wS and OS back to the strategy\n IPair(pool).burn(address(this));\n }\n\n /**\n * @dev Withdraw all pool LP tokens from the gauge when it's in emergency mode\n * and remove wS and OS from the pool.\n */\n function _emergencyWithdrawFromGaugeAndPool() internal {\n // Withdraw all pool LP tokens from the gauge\n IGauge(gauge).emergencyWithdraw();\n\n // Get the pool LP tokens in strategy\n uint256 _lpTokens = IERC20(pool).balanceOf(address(this));\n\n // Transfer the pool LP tokens to the pool\n IERC20(pool).safeTransfer(pool, _lpTokens);\n\n // Burn the LP tokens and transfer the wS and OS back to the strategy\n IPair(pool).burn(address(this));\n }\n\n /**\n * @dev Swap exact amount of tokens for another token against the pool.\n * @param _amountIn Amount of tokens to swap into the pool.\n * @param _tokenIn Address of the token going into the pool.\n * @param _tokenOut Address of the token being swapped out of the pool.\n */\n function _swapExactTokensForTokens(\n uint256 _amountIn,\n address _tokenIn,\n address _tokenOut\n ) internal {\n // Transfer in tokens to the pool\n IERC20(_tokenIn).safeTransfer(pool, _amountIn);\n\n // Calculate how much out tokens we get from the swap\n uint256 amountOut = IPair(pool).getAmountOut(_amountIn, _tokenIn);\n\n // Safety check that we are dealing with the correct pool tokens\n require(\n (_tokenIn == ws && _tokenOut == os) ||\n (_tokenIn == os && _tokenOut == ws),\n \"Unsupported swap\"\n );\n\n // Work out the correct order of the amounts for the pool\n (uint256 amount0, uint256 amount1) = _tokenIn == ws\n ? (uint256(0), amountOut)\n : (amountOut, 0);\n\n // Perform the swap on the pool\n IPair(pool).swap(amount0, amount1, address(this), new bytes(0));\n\n // The slippage protection against the amount out is indirectly done\n // via the improvePoolBalance\n }\n\n /// @dev Calculate the value of a LP position in a SwapX stable pool\n /// if the pool was balanced.\n /// @param _lpTokens Amount of LP tokens in the SwapX pool\n /// @return value The wS value of the LP tokens when the pool is balanced\n function _lpValue(uint256 _lpTokens) internal view returns (uint256 value) {\n // Get total supply of LP tokens\n uint256 totalSupply = IPair(pool).totalSupply();\n if (totalSupply == 0) return 0;\n\n // Get the current reserves of the pool\n (uint256 wsReserves, uint256 osReserves, ) = IPair(pool).getReserves();\n\n // Calculate the invariant of the pool assuming both tokens have 18 decimals.\n // k is scaled to 18 decimals.\n uint256 k = _invariant(wsReserves, osReserves);\n\n // If x = y, let’s denote x = y = z (where z is the common reserve value)\n // Substitute z into the invariant:\n // k = z^3 * z + z * z^3\n // k = 2 * z^4\n // Going back the other way to calculate the common reserve value z\n // z = (k / 2) ^ (1/4)\n // the total value of the pool when x = y is 2 * z, which is 2 * (k / 2) ^ (1/4)\n uint256 zSquared = sqrt((k * 1e18) / 2); // 18 + 18 = 36 decimals becomes 18 decimals after sqrt\n uint256 z = sqrt(zSquared * 1e18); // 18 + 18 = 36 decimals becomes 18 decimals after sqrt\n uint256 totalValueOfPool = 2 * z;\n\n // lp value = lp tokens * value of pool / total supply\n value = (_lpTokens * totalValueOfPool) / totalSupply;\n }\n\n /**\n * @dev Compute the invariant for a SwapX stable pool.\n * This assumed both x and y tokens are to 18 decimals which is checked in the constructor.\n * invariant: k = x^3 * y + x * y^3\n * @dev This implementation is copied from SwapX's Pair contract.\n * @param _x The amount of Wrapped S (wS) tokens in the pool\n * @param _y The amount of the OS tokens in the pool\n * @return k The invariant of the SwapX stable pool\n */\n function _invariant(uint256 _x, uint256 _y)\n internal\n pure\n returns (uint256 k)\n {\n uint256 _a = (_x * _y) / PRECISION;\n uint256 _b = ((_x * _x) / PRECISION + (_y * _y) / PRECISION);\n // slither-disable-next-line divide-before-multiply\n k = (_a * _b) / PRECISION;\n }\n\n /**\n * @dev Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalSupply = IERC20(os).totalSupply();\n\n if (\n _totalSupply > 0 &&\n _totalVaultValue.divPrecisely(_totalSupply) < SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Setters\n ****************************************/\n\n /**\n * @notice Set the maximum deviation from the OS/wS peg (1e18) before deposits are reverted.\n * @param _maxDepeg the OS/wS price from peg (1e18) in 18 decimals.\n * eg 0.01e18 or 1e16 is 1% which is 100 basis points.\n */\n function setMaxDepeg(uint256 _maxDepeg) external onlyGovernor {\n maxDepeg = _maxDepeg;\n\n emit MaxDepegUpdated(_maxDepeg);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve SwapX gauge contract to transfer SwapX pool LP tokens\n // This is needed for deposits of SwapX pool LP tokens into the gauge.\n // slither-disable-next-line unused-return\n IPair(pool).approve(address(gauge), type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/sonic/SonicValidatorDelegator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { ISFC } from \"../../interfaces/sonic/ISFC.sol\";\nimport { IWrappedSonic } from \"../../interfaces/sonic/IWrappedSonic.sol\";\n\n/**\n * @title Manages delegation to Sonic validators\n * @notice This contract implements all the required functionality to delegate to,\n undelegate from and withdraw from validators.\n * @author Origin Protocol Inc\n */\nabstract contract SonicValidatorDelegator is InitializableAbstractStrategy {\n /// @notice Address of Sonic's wrapped S token\n address public immutable wrappedSonic;\n /// @notice Sonic's Special Fee Contract (SFC)\n ISFC public immutable sfc;\n\n /// @notice a unique ID for each withdrawal request\n uint256 public nextWithdrawId;\n /// @notice Sonic (S) that is pending withdrawal after undelegating\n uint256 public pendingWithdrawals;\n\n /// @notice List of supported validator IDs that can be delegated to\n uint256[] public supportedValidators;\n\n /// @notice Default validator id to deposit to\n uint256 public defaultValidatorId;\n\n struct WithdrawRequest {\n uint256 validatorId;\n uint256 undelegatedAmount;\n uint256 timestamp;\n }\n /// @notice Mapping of withdrawIds to validatorIds and undelegatedAmounts\n mapping(uint256 => WithdrawRequest) public withdrawals;\n\n /// @notice Address of the registrator - allowed to register, exit and remove validators\n address public validatorRegistrator;\n\n // For future use\n uint256[44] private __gap;\n\n event Delegated(uint256 indexed validatorId, uint256 delegatedAmount);\n event Undelegated(\n uint256 indexed withdrawId,\n uint256 indexed validatorId,\n uint256 undelegatedAmount\n );\n event Withdrawn(\n uint256 indexed withdrawId,\n uint256 indexed validatorId,\n uint256 undelegatedAmount,\n uint256 withdrawnAmount\n );\n event RegistratorChanged(address indexed newAddress);\n event SupportedValidator(uint256 indexed validatorId);\n event UnsupportedValidator(uint256 indexed validatorId);\n event DefaultValidatorIdChanged(uint256 indexed validatorId);\n\n /// @dev Throws if called by any account other than the Registrator or Strategist\n modifier onlyRegistratorOrStrategist() {\n require(\n msg.sender == validatorRegistrator ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Registrator or Strategist\"\n );\n _;\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wrappedSonic,\n address _sfc\n ) InitializableAbstractStrategy(_baseConfig) {\n wrappedSonic = _wrappedSonic;\n sfc = ISFC(_sfc);\n }\n\n function initialize() external virtual onlyGovernor initializer {\n address[] memory rewardTokens = new address[](0);\n address[] memory assets = new address[](1);\n address[] memory pTokens = new address[](1);\n\n assets[0] = address(wrappedSonic);\n pTokens[0] = address(platformAddress);\n\n InitializableAbstractStrategy._initialize(\n rewardTokens,\n assets,\n pTokens\n );\n }\n\n /// @notice Returns the total value of Sonic (S) that is delegated validators.\n /// Wrapped Sonic (wS) deposits that are still to be delegated and any undelegated amounts\n /// still pending a withdrawal.\n /// @param _asset Address of Wrapped Sonic (wS) token\n /// @return balance Total value managed by the strategy\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n\n // add the Wrapped Sonic (wS) in the strategy from deposits that are still to be delegated\n // and any undelegated amounts still pending a withdrawal\n balance =\n IERC20(wrappedSonic).balanceOf(address(this)) +\n pendingWithdrawals;\n\n // For each supported validator, get the staked amount and pending rewards\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; i++) {\n uint256 validator = supportedValidators[i];\n balance += sfc.getStake(address(this), validator);\n balance += sfc.pendingRewards(address(this), validator);\n }\n }\n\n /**\n * @dev Delegate from this strategy to a specific Sonic validator. Called\n * automatically on asset deposit\n * @param _amount the amount of Sonic (S) to delegate.\n */\n function _delegate(uint256 _amount) internal {\n require(\n isSupportedValidator(defaultValidatorId),\n \"Validator not supported\"\n );\n\n // unwrap Wrapped Sonic (wS) to native Sonic (S)\n IWrappedSonic(wrappedSonic).withdraw(_amount);\n\n //slither-disable-next-line arbitrary-send-eth\n sfc.delegate{ value: _amount }(defaultValidatorId);\n\n emit Delegated(defaultValidatorId, _amount);\n }\n\n /**\n * @notice Undelegate from a specific Sonic validator.\n * This needs to be followed by a `withdrawFromSFC` two weeks later.\n * @param _validatorId The Sonic validator ID to undelegate from.\n * @param _undelegateAmount the amount of Sonic (S) to undelegate.\n * @return withdrawId The unique ID of the withdrawal request.\n */\n function undelegate(uint256 _validatorId, uint256 _undelegateAmount)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n returns (uint256 withdrawId)\n {\n withdrawId = _undelegate(_validatorId, _undelegateAmount);\n }\n\n function _undelegate(uint256 _validatorId, uint256 _undelegateAmount)\n internal\n returns (uint256 withdrawId)\n {\n // Can still undelegate even if the validator is no longer supported\n require(_undelegateAmount > 0, \"Must undelegate something\");\n\n uint256 amountDelegated = sfc.getStake(address(this), _validatorId);\n require(\n _undelegateAmount <= amountDelegated,\n \"Insufficient delegation\"\n );\n\n withdrawId = nextWithdrawId++;\n\n withdrawals[withdrawId] = WithdrawRequest(\n _validatorId,\n _undelegateAmount,\n block.timestamp\n );\n pendingWithdrawals += _undelegateAmount;\n\n sfc.undelegate(_validatorId, withdrawId, _undelegateAmount);\n\n emit Undelegated(withdrawId, _validatorId, _undelegateAmount);\n }\n\n /**\n * @notice Withdraw native S from a previously undelegated validator.\n * The native S is wrapped wS and transferred to the Vault.\n * @param _withdrawId The unique withdraw ID used to `undelegate`\n * @return withdrawnAmount The amount of Sonic (S) withdrawn.\n * This can be less than the undelegated amount in the event of slashing.\n */\n function withdrawFromSFC(uint256 _withdrawId)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n returns (uint256 withdrawnAmount)\n {\n require(_withdrawId < nextWithdrawId, \"Invalid withdrawId\");\n\n // Can still withdraw even if the validator is no longer supported\n // Load the withdrawal from storage into memory\n WithdrawRequest memory withdrawal = withdrawals[_withdrawId];\n require(!isWithdrawnFromSFC(_withdrawId), \"Already withdrawn\");\n\n withdrawals[_withdrawId].undelegatedAmount = 0;\n pendingWithdrawals -= withdrawal.undelegatedAmount;\n\n uint256 sBalanceBefore = address(this).balance;\n\n // Try to withdraw from SFC\n try sfc.withdraw(withdrawal.validatorId, _withdrawId) {\n // continue below\n } catch (bytes memory err) {\n bytes4 errorSelector = bytes4(err);\n\n // If the validator has been fully slashed, SFC's withdraw function will\n // revert with a StakeIsFullySlashed custom error.\n if (errorSelector == ISFC.StakeIsFullySlashed.selector) {\n // The validator was fully slashed, so all the delegated amounts were lost.\n // Will swallow the error as we still want to update the\n // withdrawals and pendingWithdrawals storage variables.\n\n // The return param defaults to zero but lets set it explicitly so it's clear\n withdrawnAmount = 0;\n\n emit Withdrawn(\n _withdrawId,\n withdrawal.validatorId,\n withdrawal.undelegatedAmount,\n withdrawnAmount\n );\n\n // Exit here as there is nothing to transfer to the Vault\n return withdrawnAmount;\n } else {\n // Bubble up any other SFC custom errors.\n // Inline assembly is currently the only way to generically rethrow the exact same custom error\n // from the raw bytes err in a catch block while preserving its original selector and parameters.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n revert(add(32, err), mload(err))\n }\n }\n }\n\n // Set return parameter\n withdrawnAmount = address(this).balance - sBalanceBefore;\n\n // Wrap Sonic (S) to Wrapped Sonic (wS)\n IWrappedSonic(wrappedSonic).deposit{ value: withdrawnAmount }();\n\n // Transfer the Wrapped Sonic (wS) to the Vault\n _withdraw(vaultAddress, wrappedSonic, withdrawnAmount);\n\n // withdrawal.undelegatedAmount & withdrawnAmount can differ in case of slashing\n emit Withdrawn(\n _withdrawId,\n withdrawal.validatorId,\n withdrawal.undelegatedAmount,\n withdrawnAmount\n );\n }\n\n /// @notice returns a bool whether a withdrawalId has already been withdrawn or not\n /// @param _withdrawId The unique withdraw ID used to `undelegate`\n function isWithdrawnFromSFC(uint256 _withdrawId)\n public\n view\n returns (bool)\n {\n WithdrawRequest memory withdrawal = withdrawals[_withdrawId];\n require(withdrawal.validatorId > 0, \"Invalid withdrawId\");\n return withdrawal.undelegatedAmount == 0;\n }\n\n /**\n * @notice Restake any pending validator rewards for all supported validators\n * @param _validatorIds List of Sonic validator IDs to restake rewards\n */\n function restakeRewards(uint256[] calldata _validatorIds)\n external\n nonReentrant\n {\n for (uint256 i = 0; i < _validatorIds.length; ++i) {\n require(\n isSupportedValidator(_validatorIds[i]),\n \"Validator not supported\"\n );\n\n uint256 rewards = sfc.pendingRewards(\n address(this),\n _validatorIds[i]\n );\n\n if (rewards > 0) {\n sfc.restakeRewards(_validatorIds[i]);\n }\n }\n\n // The SFC contract will emit Delegated and RestakedRewards events.\n // The checkBalance function should not change as the pending rewards will moved to the staked amount.\n }\n\n /**\n * @notice Claim any pending rewards from validators\n * @param _validatorIds List of Sonic validator IDs to claim rewards\n */\n function collectRewards(uint256[] calldata _validatorIds)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n {\n uint256 sBalanceBefore = address(this).balance;\n\n for (uint256 i = 0; i < _validatorIds.length; ++i) {\n uint256 rewards = sfc.pendingRewards(\n address(this),\n _validatorIds[i]\n );\n\n if (rewards > 0) {\n // The SFC contract will emit ClaimedRewards(delegator (this), validatorId, rewards)\n sfc.claimRewards(_validatorIds[i]);\n }\n }\n\n uint256 rewardsAmount = address(this).balance - sBalanceBefore;\n\n // Wrap Sonic (S) to Wrapped Sonic (wS)\n IWrappedSonic(wrappedSonic).deposit{ value: rewardsAmount }();\n\n // Transfer the Wrapped Sonic (wS) to the Vault\n _withdraw(vaultAddress, wrappedSonic, rewardsAmount);\n }\n\n /**\n * @notice To receive native S from SFC and Wrapped Sonic (wS)\n *\n * @dev This does not prevent donating S tokens to the contract\n * as wrappedSonic has a `withdrawTo` function where a third party\n * owner of wrappedSonic can withdraw to this contract.\n */\n receive() external payable {\n require(\n msg.sender == address(sfc) || msg.sender == wrappedSonic,\n \"S not from allowed contracts\"\n );\n }\n\n /***************************************\n Admin functions\n ****************************************/\n\n /// @notice Set the address of the Registrator which can undelegate, withdraw and collect rewards\n /// @param _validatorRegistrator The address of the Registrator\n function setRegistrator(address _validatorRegistrator)\n external\n onlyGovernor\n {\n validatorRegistrator = _validatorRegistrator;\n emit RegistratorChanged(_validatorRegistrator);\n }\n\n /// @notice Set the default validatorId to delegate to on deposit\n /// @param _validatorId The validator identifier. eg 18\n function setDefaultValidatorId(uint256 _validatorId)\n external\n onlyRegistratorOrStrategist\n {\n require(isSupportedValidator(_validatorId), \"Validator not supported\");\n defaultValidatorId = _validatorId;\n emit DefaultValidatorIdChanged(_validatorId);\n }\n\n /// @notice Allows a validator to be delegated to by the Registrator\n /// @param _validatorId The validator identifier. eg 18\n function supportValidator(uint256 _validatorId) external onlyGovernor {\n require(\n !isSupportedValidator(_validatorId),\n \"Validator already supported\"\n );\n\n supportedValidators.push(_validatorId);\n\n emit SupportedValidator(_validatorId);\n }\n\n /// @notice Removes a validator from the supported list.\n /// Unsupported validators can still be undelegated from, withdrawn from and rewards collected.\n /// @param _validatorId The validator identifier. eg 18\n function unsupportValidator(uint256 _validatorId) external onlyGovernor {\n require(isSupportedValidator(_validatorId), \"Validator not supported\");\n\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; ++i) {\n if (supportedValidators[i] == _validatorId) {\n supportedValidators[i] = supportedValidators[validatorLen - 1];\n supportedValidators.pop();\n break;\n }\n }\n\n uint256 stake = sfc.getStake(address(this), _validatorId);\n\n // undelegate if validator still has funds staked\n if (stake > 0) {\n _undelegate(_validatorId, stake);\n }\n emit UnsupportedValidator(_validatorId);\n }\n\n /// @notice Returns the length of the supportedValidators array\n function supportedValidatorsLength() external view returns (uint256) {\n return supportedValidators.length;\n }\n\n /// @notice Returns whether a validator is supported by this strategy\n /// @param _validatorId The validator identifier\n function isSupportedValidator(uint256 _validatorId)\n public\n view\n returns (bool)\n {\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; ++i) {\n if (supportedValidators[i] == _validatorId) {\n return true;\n }\n }\n return false;\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal virtual;\n}\n" + }, + "contracts/strategies/VaultValueChecker.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IOUSD } from \"../interfaces/IOUSD.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract VaultValueChecker {\n IVault public immutable vault;\n IOUSD public immutable ousd;\n // Snapshot expiration time in seconds.\n // Used to prevent accidental use of an old snapshot, but\n // is not zero to allow easy testing of strategist actions in fork testing\n uint256 constant SNAPSHOT_EXPIRES = 5 * 60;\n\n struct Snapshot {\n uint256 vaultValue;\n uint256 totalSupply;\n uint256 time;\n }\n // By doing per user snapshots, we prevent a reentrancy attack\n // from a third party that updates the snapshot in the middle\n // of an allocation process\n\n mapping(address => Snapshot) public snapshots;\n\n constructor(address _vault, address _ousd) {\n vault = IVault(_vault);\n ousd = IOUSD(_ousd);\n }\n\n function takeSnapshot() external {\n snapshots[msg.sender] = Snapshot({\n vaultValue: vault.totalValue(),\n totalSupply: ousd.totalSupply(),\n time: block.timestamp\n });\n }\n\n function checkDelta(\n int256 expectedProfit,\n int256 profitVariance,\n int256 expectedVaultChange,\n int256 vaultChangeVariance\n ) external {\n // Intentionaly not view so that this method shows up in TX builders\n Snapshot memory snapshot = snapshots[msg.sender];\n int256 vaultChange = toInt256(vault.totalValue()) -\n toInt256(snapshot.vaultValue);\n int256 supplyChange = toInt256(ousd.totalSupply()) -\n toInt256(snapshot.totalSupply);\n int256 profit = vaultChange - supplyChange;\n\n require(\n snapshot.time >= block.timestamp - SNAPSHOT_EXPIRES,\n \"Snapshot too old\"\n );\n require(snapshot.time <= block.timestamp, \"Snapshot too new\");\n require(profit >= expectedProfit - profitVariance, \"Profit too low\");\n require(profit <= expectedProfit + profitVariance, \"Profit too high\");\n require(\n vaultChange >= expectedVaultChange - vaultChangeVariance,\n \"Vault value change too low\"\n );\n require(\n vaultChange <= expectedVaultChange + vaultChangeVariance,\n \"Vault value change too high\"\n );\n }\n\n function toInt256(uint256 value) internal pure returns (int256) {\n // From openzeppelin math/SafeCast.sol\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(\n value <= uint256(type(int256).max),\n \"SafeCast: value doesn't fit in an int256\"\n );\n return int256(value);\n }\n}\n\ncontract OETHVaultValueChecker is VaultValueChecker {\n constructor(address _vault, address _ousd)\n VaultValueChecker(_vault, _ousd)\n {}\n}\n" + }, + "contracts/swapper/Swapper1InchV5.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @notice 1Inch Pathfinder V5 implementation of the general ISwapper interface.\n * @author Origin Protocol Inc\n * @dev It is possible that dust token amounts are left in this contract after a swap.\n * This can happen with some tokens that don't send the full transfer amount.\n * These dust amounts can build up over time and be used by anyone who calls the `swap` function.\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IAggregationExecutor, IOneInchRouter, SwapDescription } from \"../interfaces/IOneInch.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\n\ncontract Swapper1InchV5 is ISwapper {\n using SafeERC20 for IERC20;\n\n /// @notice 1Inch router contract to give allowance to perform swaps\n address public constant SWAP_ROUTER =\n 0x1111111254EEB25477B68fb85Ed929f73A960582;\n\n // swap(address,(address,address,address,address,uint256,uint256,uint256),bytes,bytes)\n bytes4 internal constant SWAP_SELECTOR = 0x12aa3caf;\n // unoswapTo(address,address,uint256,uint256,uint256[])\n bytes4 internal constant UNISWAP_SELECTOR = 0xf78dc253;\n // uniswapV3SwapTo(address,uint256,uint256,uint256[])\n bytes4 internal constant UNISWAPV3_SELECTOR = 0xbc80f1a8;\n\n /**\n * @notice Strategist swaps assets sitting in the contract of the `assetHolder`.\n * @param _fromAsset The token address of the asset being sold by the vault.\n * @param _toAsset The token address of the asset being purchased by the vault.\n * @param _fromAssetAmount The amount of assets being sold by the vault.\n * @param _minToAssetAmount The minimum amount of assets to be purchased.\n * @param _data RLP encoded executer address and bytes data. This is re-encoded tx.data from 1Inch swap API\n */\n function swap(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n ) external override returns (uint256 toAssetAmount) {\n // Decode the function selector from the RLP encoded _data param\n bytes4 swapSelector = bytes4(_data[:4]);\n\n if (swapSelector == SWAP_SELECTOR) {\n // Decode the executer address and data from the RLP encoded _data param\n (, address executer, bytes memory executerData) = abi.decode(\n _data,\n (bytes4, address, bytes)\n );\n SwapDescription memory swapDesc = SwapDescription({\n srcToken: IERC20(_fromAsset),\n dstToken: IERC20(_toAsset),\n srcReceiver: payable(executer),\n dstReceiver: payable(msg.sender),\n amount: _fromAssetAmount,\n minReturnAmount: _minToAssetAmount,\n flags: 4 // 1st bit _PARTIAL_FILL, 2nd bit _REQUIRES_EXTRA_ETH, 3rd bit _SHOULD_CLAIM\n });\n (toAssetAmount, ) = IOneInchRouter(SWAP_ROUTER).swap(\n IAggregationExecutor(executer),\n swapDesc,\n hex\"\",\n executerData\n );\n } else if (swapSelector == UNISWAP_SELECTOR) {\n // Need to get the Uniswap pools data from the _data param\n (, uint256[] memory pools) = abi.decode(_data, (bytes4, uint256[]));\n toAssetAmount = IOneInchRouter(SWAP_ROUTER).unoswapTo(\n payable(msg.sender),\n IERC20(_fromAsset),\n _fromAssetAmount,\n _minToAssetAmount,\n pools\n );\n } else if (swapSelector == UNISWAPV3_SELECTOR) {\n // Need to get the Uniswap pools data from the _data param\n // slither-disable-next-line uninitialized-storage\n (, uint256[] memory pools) = abi.decode(_data, (bytes4, uint256[]));\n toAssetAmount = IOneInchRouter(SWAP_ROUTER).uniswapV3SwapTo(\n payable(msg.sender),\n _fromAssetAmount,\n _minToAssetAmount,\n pools\n );\n } else {\n revert(\"Unsupported swap function\");\n }\n }\n\n /**\n * @notice Approve assets for swapping.\n * @param _assets Array of token addresses to approve.\n * @dev unlimited approval is used as no tokens sit in this contract outside a transaction.\n */\n function approveAssets(address[] memory _assets) external {\n for (uint256 i = 0; i < _assets.length; ++i) {\n // Give the 1Inch router approval to transfer unlimited assets\n IERC20(_assets[i]).safeApprove(SWAP_ROUTER, type(uint256).max);\n }\n }\n}\n" + }, + "contracts/token/BridgedWOETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\n\ncontract BridgedWOETH is\n Governable,\n AccessControlEnumerable,\n Initializable,\n ERC20\n{\n bytes32 public constant MINTER_ROLE = keccak256(\"MINTER_ROLE\");\n bytes32 public constant BURNER_ROLE = keccak256(\"BURNER_ROLE\");\n\n constructor() ERC20(\"Wrapped OETH\", \"wOETH\") {\n // Nobody owns the implementation\n _setGovernor(address(0));\n }\n\n /**\n * @dev Initialize the proxy and set the Governor\n */\n function initialize() external initializer {\n // Governor can grant Minter/Burner roles\n _setupRole(DEFAULT_ADMIN_ROLE, _governor());\n }\n\n /**\n * @dev Mint tokens for `account`\n * @param account Address to mint tokens for\n * @param amount Amount of tokens to mint\n */\n function mint(address account, uint256 amount)\n external\n onlyRole(MINTER_ROLE)\n nonReentrant\n {\n _mint(account, amount);\n }\n\n /**\n * @dev Burns tokens from `account`\n * @param account Address to burn tokens from\n * @param amount Amount of tokens to burn\n */\n function burn(address account, uint256 amount)\n external\n onlyRole(BURNER_ROLE)\n nonReentrant\n {\n _burn(account, amount);\n }\n\n /**\n * @dev Burns tokens from `msg.sender`\n * @param amount Amount of tokens to burn\n */\n function burn(uint256 amount) external onlyRole(BURNER_ROLE) nonReentrant {\n _burn(msg.sender, amount);\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return \"Wrapped OETH\";\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return \"wOETH\";\n }\n\n /**\n * @dev Returns the decimals of the token\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETH is OUSD {\n function symbol() external pure override returns (string memory) {\n return \"OETH\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Origin Ether\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETHBase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBase is OUSD {\n constructor() {\n // Nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n function symbol() external pure override returns (string memory) {\n return \"superOETHb\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Super OETH\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETHPlume.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title Super OETH (Plume) Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETHPlume is OUSD {\n constructor() {\n // Nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n function symbol() external pure override returns (string memory) {\n return \"superOETHp\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Super OETH\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OSonic.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title Origin Sonic (OS) token on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonic is OUSD {\n function symbol() external pure override returns (string memory) {\n return \"OS\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Origin Sonic\";\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/token/OUSDResolutionUpgrade.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract OUSDResolutionUpgrade {\n enum RebaseOptions {\n NotSet,\n OptOut,\n OptIn\n }\n\n // From Initializable\n bool private initialized;\n bool private initializing;\n uint256[50] private ______igap;\n\n // From InitializableERC20Detailed\n uint256[100] private _____ugap;\n string private _name;\n string private _symbol;\n uint8 private _decimals;\n\n // From OUSD\n uint256 private constant MAX_SUPPLY = ~uint128(0); // (2^128) - 1\n uint256 public _totalSupply;\n mapping(address => mapping(address => uint256)) private _allowances;\n address public vaultAddress = address(0);\n mapping(address => uint256) private _creditBalances;\n uint256 private _rebasingCredits;\n uint256 private _rebasingCreditsPerToken;\n uint256 public nonRebasingSupply;\n mapping(address => uint256) public nonRebasingCreditsPerToken;\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) public isUpgraded;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() public view returns (uint256) {\n return _rebasingCreditsPerToken / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCredits() public view returns (uint256) {\n return _rebasingCredits / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() public view returns (uint256) {\n return _rebasingCreditsPerToken;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() public view returns (uint256) {\n return _rebasingCredits;\n }\n\n function upgradeGlobals() external {\n require(isUpgraded[address(0)] == 0, \"Globals already upgraded\");\n require(_rebasingCredits > 0, \"Sanity _rebasingCredits\");\n require(\n _rebasingCreditsPerToken > 0,\n \"Sanity _rebasingCreditsPerToken\"\n );\n isUpgraded[address(0)] = 1;\n _rebasingCredits = _rebasingCredits * RESOLUTION_INCREASE;\n _rebasingCreditsPerToken =\n _rebasingCreditsPerToken *\n RESOLUTION_INCREASE;\n }\n\n function upgradeAccounts(address[] calldata accounts) external {\n for (uint256 i = 0; i < accounts.length; i++) {\n address account = accounts[i];\n require(account != address(0), \"Reserved\");\n require(isUpgraded[account] == 0, \"Account already upgraded\");\n isUpgraded[account] = 1;\n\n // Handle special for non-rebasing accounts\n uint256 nrc = nonRebasingCreditsPerToken[account];\n if (nrc > 0) {\n require(nrc < 1e18, \"Account already highres\");\n nonRebasingCreditsPerToken[account] = nrc * RESOLUTION_INCREASE;\n }\n // Upgrade balance\n uint256 balance = _creditBalances[account];\n require(balance > 0, \"Will not upgrade zero balance\");\n _creditBalances[account] = balance * RESOLUTION_INCREASE;\n }\n }\n\n function creditsBalanceOfHighres(address _account)\n public\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n _creditBalances[_account],\n _creditsPerToken(_account),\n isUpgraded[_account] == 1\n );\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n if (nonRebasingCreditsPerToken[_account] != 0) {\n return nonRebasingCreditsPerToken[_account];\n } else {\n return _rebasingCreditsPerToken;\n }\n }\n}\n" + }, + "contracts/token/WOETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC4626 } from \"../../lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { OETH } from \"./OETH.sol\";\n\n/**\n * @title Wrapped OETH Token Contract\n * @author Origin Protocol Inc\n *\n * @dev An important capability of this contract is that it isn't susceptible to changes of the\n * exchange rate of WOETH/OETH if/when someone sends the underlying asset (OETH) to the contract.\n * If OETH weren't rebasing this could be achieved by solely tracking the ERC20 transfers of the OETH\n * token on mint, deposit, redeem, withdraw. The issue is that OETH is rebasing and OETH balances\n * will change when the token rebases.\n * For that reason the contract logic checks the actual underlying OETH token balance only once\n * (either on a fresh contract creation or upgrade) and considering the WOETH supply and\n * rebasingCreditsPerToken calculates the _adjuster. Once the adjuster is calculated any donations\n * to the contract are ignored. The totalSupply (instead of querying OETH balance) works off of\n * adjuster the current WOETH supply and rebasingCreditsPerToken. This makes WOETH value accrual\n * completely follow OETH's value accrual.\n * WOETH is safe to use in lending markets as the VualtCore's _rebase contains safeguards preventing\n * any sudden large rebases.\n */\n\ncontract WOETH is ERC4626, Governable, Initializable {\n using SafeERC20 for IERC20;\n /* This is a 1e27 adjustment constant that expresses the difference in exchange rate between\n * OETH's rebase since inception (expressed with rebasingCreditsPerToken) and WOETH to OETH\n * conversion.\n *\n * If WOETH and OETH are deployed at the same time, the value of adjuster is a neutral 1e27\n */\n uint256 public adjuster;\n uint256[49] private __gap;\n\n // no need to set ERC20 name and symbol since they are overridden in WOETH & WOETHBase\n constructor(ERC20 underlying_) ERC20(\"\", \"\") ERC4626(underlying_) {}\n\n /**\n * @notice Enable OETH rebasing for this contract\n */\n function initialize() external onlyGovernor initializer {\n OETH(address(asset())).rebaseOptIn();\n\n initialize2();\n }\n\n /**\n * @notice secondary initializer that newly deployed contracts will execute as part\n * of primary initialize function and the existing contracts will have it called\n * as a governance operation.\n */\n function initialize2() public onlyGovernor {\n require(adjuster == 0, \"Initialize2 already called\");\n\n if (totalSupply() == 0) {\n adjuster = 1e27;\n } else {\n adjuster =\n (rebasingCreditsPerTokenHighres() *\n ERC20(asset()).balanceOf(address(this))) /\n totalSupply();\n }\n }\n\n function name()\n public\n view\n virtual\n override(ERC20, IERC20Metadata)\n returns (string memory)\n {\n return \"Wrapped OETH\";\n }\n\n function symbol()\n public\n view\n virtual\n override(ERC20, IERC20Metadata)\n returns (string memory)\n {\n return \"wOETH\";\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends. Cannot transfer OETH\n * @param asset_ Address for the asset\n * @param amount_ Amount of the asset to transfer\n */\n function transferToken(address asset_, uint256 amount_)\n external\n onlyGovernor\n {\n require(asset_ != address(asset()), \"Cannot collect core asset\");\n IERC20(asset_).safeTransfer(governor(), amount_);\n }\n\n /// @inheritdoc ERC4626\n function convertToShares(uint256 assets)\n public\n view\n virtual\n override\n returns (uint256 shares)\n {\n return (assets * rebasingCreditsPerTokenHighres()) / adjuster;\n }\n\n /// @inheritdoc ERC4626\n function convertToAssets(uint256 shares)\n public\n view\n virtual\n override\n returns (uint256 assets)\n {\n return (shares * adjuster) / rebasingCreditsPerTokenHighres();\n }\n\n /// @inheritdoc ERC4626\n function totalAssets() public view override returns (uint256) {\n return (totalSupply() * adjuster) / rebasingCreditsPerTokenHighres();\n }\n\n function rebasingCreditsPerTokenHighres() internal view returns (uint256) {\n return OETH(asset()).rebasingCreditsPerTokenHighres();\n }\n}\n" + }, + "contracts/token/WOETHBase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WOETH } from \"./WOETH.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\n\ncontract WOETHBase is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name() public view virtual override returns (string memory) {\n return \"Wrapped Super OETH\";\n }\n\n function symbol() public view virtual override returns (string memory) {\n return \"wsuperOETHb\";\n }\n}\n" + }, + "contracts/token/WOETHPlume.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WOETH } from \"./WOETH.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @title wOETH (Plume) Token Contract\n * @author Origin Protocol Inc\n */\n\ncontract WOETHPlume is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name() public view virtual override returns (string memory) {\n return \"Wrapped Super OETH\";\n }\n\n function symbol() public view virtual override returns (string memory) {\n return \"wsuperOETHp\";\n }\n}\n" + }, + "contracts/token/WOSonic.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { WOETH } from \"./WOETH.sol\";\n\n/**\n * @title Wrapped Origin Sonic (wOS) token on Sonic\n * @author Origin Protocol Inc\n */\ncontract WOSonic is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"Wrapped OS\";\n }\n\n function symbol()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"wOS\";\n }\n}\n" + }, + "contracts/token/WrappedOusd.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { WOETH } from \"./WOETH.sol\";\n\n/**\n * @title Wrapped OUSD Token Contract\n * @author Origin Protocol Inc\n */\ncontract WrappedOusd is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"Wrapped OUSD\";\n }\n\n function symbol()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"WOUSD\";\n }\n}\n" + }, + "contracts/utils/AerodromeAMOQuoter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.7;\n\nimport { ICLPool } from \"../interfaces/aerodrome/ICLPool.sol\";\nimport { IQuoterV2 } from \"../interfaces/aerodrome/IQuoterV2.sol\";\nimport { IAMOStrategy } from \"../interfaces/aerodrome/IAMOStrategy.sol\";\n\n/// @title QuoterHelper\n/// @author Origin Protocol\n/// @notice Helper for Aerodrome AMO Quoter, as `_quoteAmountToSwapBeforeRebalance` use try/catch method and\n/// this can only be used when calling external contracts.\ncontract QuoterHelper {\n ////////////////////////////////////////////////////////////////\n /// --- STRUCTS & ENUMS\n ////////////////////////////////////////////////////////////////\n enum RevertReasons {\n DefaultStatus,\n RebalanceOutOfBounds,\n NotInExpectedTickRange,\n NotEnoughWethForSwap,\n NotEnoughWethLiquidity,\n UnexpectedError,\n Found,\n NotFound\n }\n\n struct RebalanceStatus {\n RevertReasons reason;\n uint256 currentPoolWETHShare; // Case 1\n uint256 allowedWETHShareStart; // Case 1\n uint256 allowedWETHShareEnd; // Case 1\n int24 currentTick; // Case 2\n uint256 balanceWETH; // Case 3\n uint256 amountWETH; // Case 3\n string revertMessage;\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTANT & IMMUTABLE\n ////////////////////////////////////////////////////////////////\n uint256 public constant BINARY_MIN_AMOUNT = 1 wei;\n\n uint256 public constant BINARY_MAX_ITERATIONS = 40;\n uint256 public constant PERCENTAGE_BASE = 1e18; // 100%\n uint256 public constant ALLOWED_VARIANCE_PERCENTAGE = 1e15; // 0.1%\n\n ////////////////////////////////////////////////////////////////\n /// --- VARIABLES STORAGE\n ////////////////////////////////////////////////////////////////\n ICLPool public immutable clPool;\n IQuoterV2 public immutable quoterV2;\n IAMOStrategy public immutable strategy;\n\n address public originalGovernor;\n\n ////////////////////////////////////////////////////////////////\n /// --- ERRORS & EVENTS\n ////////////////////////////////////////////////////////////////\n error UnexpectedError(string message);\n error OutOfIterations(uint256 iterations);\n error ValidAmount(\n uint256 amount,\n uint256 iterations,\n bool swapWETHForOETHB\n );\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////////////////\n constructor(IAMOStrategy _strategy, IQuoterV2 _quoterV2) {\n strategy = _strategy;\n quoterV2 = _quoterV2;\n clPool = _strategy.clPool();\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- FUNCTIONS\n ////////////////////////////////////////////////////////////////\n /// @notice This call can only end with a revert.\n function getAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) public {\n if (\n overrideBottomWethShare != type(uint256).max ||\n overrideTopWethShare != type(uint256).max\n ) {\n // Current values\n uint256 shareStart = strategy.allowedWethShareStart();\n uint256 shareEnd = strategy.allowedWethShareEnd();\n\n // Override values\n if (overrideBottomWethShare != type(uint256).max) {\n shareStart = overrideBottomWethShare;\n }\n if (overrideTopWethShare != type(uint256).max) {\n shareEnd = overrideTopWethShare;\n }\n\n strategy.setAllowedPoolWethShareInterval(shareStart, shareEnd);\n }\n\n uint256 iterations = 0;\n uint256 low = BINARY_MIN_AMOUNT;\n uint256 high;\n (high, ) = strategy.getPositionPrincipal();\n int24 lowerTick = strategy.lowerTick();\n int24 upperTick = strategy.upperTick();\n bool swapWETHForOETHB = getSwapDirectionForRebalance();\n\n while (low <= high && iterations < BINARY_MAX_ITERATIONS) {\n uint256 mid = (low + high) / 2;\n\n RebalanceStatus memory status = getRebalanceStatus(\n mid,\n swapWETHForOETHB\n );\n\n // Best case, we found the `amount` that will reach the target pool share!\n // We can revert with the amount and the number of iterations\n if (status.reason == RevertReasons.Found) {\n revert ValidAmount(mid, iterations, swapWETHForOETHB);\n }\n\n // If the rebalance failed then we should try to change the amount.\n // We will handle all possible revert reasons here.\n\n // Case 1: Rebalance out of bounds\n // If the pool is out of bounds, we need to adjust the amount to reach the target pool share\n if (status.reason == RevertReasons.RebalanceOutOfBounds) {\n // If the current pool share is less than the target pool share, we need to increase the amount\n if (\n swapWETHForOETHB\n ? status.currentPoolWETHShare <\n status.allowedWETHShareStart\n : status.currentPoolWETHShare >\n status.allowedWETHShareEnd\n ) {\n low = mid + 1;\n }\n // Else we need to decrease the amount\n else {\n high = mid;\n }\n }\n\n // Case 2: Not in expected tick range\n // If the pool is not in the expected tick range, we need to adjust the amount\n // to reach the target pool share\n if (status.reason == RevertReasons.NotInExpectedTickRange) {\n // If we are buying OETHb and the current tick is greater than the lower tick,\n //we need to increase the amount in order to continue to push price down.\n // If we are selling OETHb and the current tick is less than the upper tick,\n // we need to increase the amount in order to continue to push price up.\n if (\n swapWETHForOETHB\n ? status.currentTick > lowerTick\n : status.currentTick < upperTick\n ) {\n low = mid + 1;\n }\n // Else we need to decrease the amount\n else {\n high = mid;\n }\n }\n\n // Case 3: Not enough WETH for swap\n // If we don't have enough WETH to swap, we need to decrease the amount\n // This error can happen, when initial value of mid is too high, so we need to decrease it\n if (status.reason == RevertReasons.NotEnoughWethForSwap) {\n high = mid;\n }\n\n // Case 4: Not enough WETH liquidity\n // If we don't have enough WETH liquidity\n // Revert for the moment, we need to improve this\n if (status.reason == RevertReasons.NotEnoughWethLiquidity) {\n revert(\"Quoter: Not enough WETH liquidity\");\n }\n\n // Case 5: Unexpected error\n // Worst case, it reverted with an unexpected error.\n if (status.reason == RevertReasons.UnexpectedError) {\n revert UnexpectedError(status.revertMessage);\n }\n\n iterations++;\n }\n\n // Case 6: Out of iterations\n // If we didn't find the amount after the max iterations, we need to revert.\n revert OutOfIterations(iterations);\n }\n\n /// @notice Get the status of the rebalance\n /// @param amount The amount of token to swap\n /// @param swapWETH True if we need to swap WETH for OETHb, false otherwise\n /// @return status The status of the rebalance\n function getRebalanceStatus(uint256 amount, bool swapWETH)\n public\n returns (RebalanceStatus memory status)\n {\n try strategy.rebalance(amount, swapWETH, 0) {\n status.reason = RevertReasons.Found;\n return status;\n } catch Error(string memory reason) {\n status.reason = RevertReasons.UnexpectedError;\n status.revertMessage = reason;\n } catch (bytes memory reason) {\n bytes4 receivedSelector = bytes4(reason);\n\n // Case 1: Rebalance out of bounds\n if (\n receivedSelector ==\n IAMOStrategy.PoolRebalanceOutOfBounds.selector\n ) {\n uint256 currentPoolWETHShare;\n uint256 allowedWETHShareStart;\n uint256 allowedWETHShareEnd;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n currentPoolWETHShare := mload(add(reason, 0x24))\n allowedWETHShareStart := mload(add(reason, 0x44))\n allowedWETHShareEnd := mload(add(reason, 0x64))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.RebalanceOutOfBounds,\n currentPoolWETHShare: currentPoolWETHShare,\n allowedWETHShareStart: allowedWETHShareStart,\n allowedWETHShareEnd: allowedWETHShareEnd,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 2: Not in expected tick range\n if (\n receivedSelector ==\n IAMOStrategy.OutsideExpectedTickRange.selector\n ) {\n int24 currentTick;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n currentTick := mload(add(reason, 0x24))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.NotInExpectedTickRange,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: currentTick,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 3: Not enough WETH for swap\n if (\n receivedSelector == IAMOStrategy.NotEnoughWethForSwap.selector\n ) {\n uint256 balanceWETH;\n uint256 amountWETH;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n balanceWETH := mload(add(reason, 0x24))\n amountWETH := mload(add(reason, 0x44))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.NotEnoughWethForSwap,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: balanceWETH,\n amountWETH: amountWETH,\n revertMessage: \"\"\n });\n }\n\n // Case 4: Not enough WETH liquidity\n if (\n receivedSelector == IAMOStrategy.NotEnoughWethLiquidity.selector\n ) {\n return\n RebalanceStatus({\n reason: RevertReasons.NotEnoughWethLiquidity,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 5: Unexpected error\n return\n RebalanceStatus({\n reason: RevertReasons.UnexpectedError,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: abi.decode(reason, (string))\n });\n }\n }\n\n /// @notice Get the swap direction to reach the target price before rebalance.\n /// @return bool True if we need to swap WETH for OETHb, false otherwise.\n function getSwapDirectionForRebalance() public view returns (bool) {\n uint160 currentPrice = strategy.getPoolX96Price();\n uint160 ticker0Price = strategy.sqrtRatioX96TickLower();\n uint160 ticker1Price = strategy.sqrtRatioX96TickHigher();\n uint256 allowedWethShareStart = strategy.allowedWethShareStart();\n uint256 allowedWethShareEnd = strategy.allowedWethShareEnd();\n uint160 mid = uint160(allowedWethShareStart + allowedWethShareEnd) / 2;\n // slither-disable-start divide-before-multiply\n uint160 targetPrice = (ticker0Price *\n mid +\n ticker1Price *\n (1 ether - mid)) / 1 ether;\n // slither-disable-end divide-before-multiply\n\n return currentPrice > targetPrice;\n }\n\n // returns total amount in the position principal of the Aerodrome AMO strategy. Needed as a\n // separate function because of the limitation in local variable count in getAmountToSwapToReachPrice\n function getTotalStrategyPosition() internal returns (uint256) {\n (uint256 wethAmount, uint256 oethBalance) = strategy\n .getPositionPrincipal();\n return wethAmount + oethBalance;\n }\n\n /// @notice Get the amount of tokens to swap to reach the target price.\n /// @dev This act like a quoter, i.e. the transaction is not performed.\n /// @dev Because the amount to swap can be largely overestimated, because CLAMM alow partial orders,\n /// i.e. when we ask to swap a very large amount, with a close priceLimite, it will swap only a part of it,\n /// and not revert. So if overestimated amount is to high, use a custom maxAmount to avoid this issue.\n /// @param sqrtPriceTargetX96 The target price to reach.\n /// @return amount The amount of tokens to swap.\n /// @return iterations The number of iterations to find the amount.\n /// @return swapWETHForOETHB True if we need to swap WETH for OETHb, false otherwise.\n /// @return sqrtPriceX96After The price after the swap.\n function getAmountToSwapToReachPrice(uint160 sqrtPriceTargetX96)\n public\n returns (\n uint256,\n uint256,\n bool,\n uint160\n )\n {\n uint256 iterations = 0;\n uint256 low = BINARY_MIN_AMOUNT;\n // high search start is twice the position principle of Aerodrome AMO strategy.\n // should be more than enough\n uint256 high = getTotalStrategyPosition() * 2;\n bool swapWETHForOETHB = getSwapDirection(sqrtPriceTargetX96);\n\n while (low <= high && iterations < BINARY_MAX_ITERATIONS) {\n uint256 mid = (low + high) / 2;\n\n // Call QuoterV2 from SugarHelper\n (uint256 amountOut, uint160 sqrtPriceX96After, , ) = quoterV2\n .quoteExactInputSingle(\n IQuoterV2.QuoteExactInputSingleParams({\n tokenIn: swapWETHForOETHB\n ? clPool.token0()\n : clPool.token1(),\n tokenOut: swapWETHForOETHB\n ? clPool.token1()\n : clPool.token0(),\n amountIn: mid,\n tickSpacing: strategy.tickSpacing(),\n sqrtPriceLimitX96: sqrtPriceTargetX96\n })\n );\n\n if (\n isWithinAllowedVariance(sqrtPriceX96After, sqrtPriceTargetX96)\n ) {\n /** Very important to return `amountOut` instead of `mid` as the first return parameter.\n * The issues was that when quoting we impose a swap price limit (sqrtPriceLimitX96: sqrtPriceTargetX96)\n * and in that case the `amountIn` acts like a maximum amount to swap. And we don't know how much\n * of that amount was actually consumed. For that reason we \"estimate\" it by returning the\n * amountOut since that is only going to be a couple of basis point away from amountIn in the\n * worst cases.\n *\n * Note: we could be returning mid instead of amountOut in cases when those values are only basis\n * points apart (assuming that complete balance of amountIn has been consumed) but that might increase\n * complexity too much in an already complex contract.\n */\n return (\n amountOut,\n iterations,\n swapWETHForOETHB,\n sqrtPriceX96After\n );\n } else if (low == high) {\n // target swap amount not found.\n // might be that \"high\" amount is too low on start\n revert(\"SwapAmountNotFound\");\n } else if (\n swapWETHForOETHB\n ? sqrtPriceX96After > sqrtPriceTargetX96\n : sqrtPriceX96After < sqrtPriceTargetX96\n ) {\n low = mid + 1;\n } else {\n high = mid;\n }\n iterations++;\n }\n\n revert OutOfIterations(iterations);\n }\n\n /// @notice Check if the current price is within the allowed variance in comparison to the target price\n /// @return bool True if the current price is within the allowed variance, false otherwise\n function isWithinAllowedVariance(\n uint160 sqrtPriceCurrentX96,\n uint160 sqrtPriceTargetX96\n ) public view returns (bool) {\n uint160 range = strategy.sqrtRatioX96TickHigher() -\n strategy.sqrtRatioX96TickLower();\n if (sqrtPriceCurrentX96 > sqrtPriceTargetX96) {\n return\n (sqrtPriceCurrentX96 - sqrtPriceTargetX96) <=\n (ALLOWED_VARIANCE_PERCENTAGE * range) / PERCENTAGE_BASE;\n } else {\n return\n (sqrtPriceTargetX96 - sqrtPriceCurrentX96) <=\n (ALLOWED_VARIANCE_PERCENTAGE * range) / PERCENTAGE_BASE;\n }\n }\n\n /// @notice Get the swap direction to reach the target price.\n /// @param sqrtPriceTargetX96 The target price to reach.\n /// @return bool True if we need to swap WETH for OETHb, false otherwise.\n function getSwapDirection(uint160 sqrtPriceTargetX96)\n public\n view\n returns (bool)\n {\n uint160 currentPrice = strategy.getPoolX96Price();\n return currentPrice > sqrtPriceTargetX96;\n }\n\n function claimGovernanceOnAMO() public {\n originalGovernor = strategy.governor();\n strategy.claimGovernance();\n }\n\n function giveBackGovernanceOnAMO() public {\n require(\n originalGovernor != address(0),\n \"Quoter: Original governor not set\"\n );\n strategy.transferGovernance(originalGovernor);\n }\n}\n\n/// @title AerodromeAMOQuoter\n/// @author Origin Protocol\n/// @notice Quoter for Aerodrome AMO\ncontract AerodromeAMOQuoter {\n ////////////////////////////////////////////////////////////////\n /// --- STRUCTS & ENUMS\n ///////////////////////////////////////////////////////////////\n struct Data {\n uint256 amount;\n uint256 iterations;\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- VARIABLES STORAGE\n ////////////////////////////////////////////////////////////////\n QuoterHelper public immutable quoterHelper;\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////////////////\n constructor(address _strategy, address _quoterV2) {\n quoterHelper = new QuoterHelper(\n IAMOStrategy(_strategy),\n IQuoterV2(_quoterV2)\n );\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- ERRORS & EVENTS\n ////////////////////////////////////////////////////////////////\n event ValueFound(uint256 value, uint256 iterations, bool swapWETHForOETHB);\n event ValueFoundBis(\n uint256 value,\n uint256 iterations,\n bool swapWETHForOETHB,\n uint160 sqrtPriceAfterX96\n );\n event ValueNotFound(string message);\n\n ////////////////////////////////////////////////////////////////\n /// --- FUNCTIONS\n ////////////////////////////////////////////////////////////////\n /// @notice Use this to get the amount to swap before rebalance\n /// @dev This call will only revert, check the logs to get returned values.\n /// @dev Need to perform this call while impersonating the governor or strategist of AMO.\n /// @return data Data struct with the amount and the number of iterations\n function quoteAmountToSwapBeforeRebalance()\n public\n returns (Data memory data)\n {\n return\n _quoteAmountToSwapBeforeRebalance(\n type(uint256).max,\n type(uint256).max\n );\n }\n\n /// @notice Use this to get the amount to swap before rebalance and\n /// update allowedWethShareStart and allowedWethShareEnd on AMO.\n /// @dev This call will only revert, check the logs to get returned values.\n /// @dev Need to perform this call while impersonating the governor of AMO.\n /// @param overrideBottomWethShare New value for the allowedWethShareStart on AMO.\n /// Use type(uint256).max to keep same value.\n /// @param overrideTopWethShare New value for the allowedWethShareEnd on AMO.\n /// Use type(uint256).max to keep same value.\n /// @return data Data struct with the amount and the number of iterations\n function quoteAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) public returns (Data memory data) {\n return\n _quoteAmountToSwapBeforeRebalance(\n overrideBottomWethShare,\n overrideTopWethShare\n );\n }\n\n /// @notice Internal logic for quoteAmountToSwapBeforeRebalance.\n function _quoteAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) internal returns (Data memory data) {\n try\n quoterHelper.getAmountToSwapBeforeRebalance(\n overrideBottomWethShare,\n overrideTopWethShare\n )\n {\n revert(\"Previous call should only revert, it cannot succeed\");\n } catch (bytes memory reason) {\n bytes4 receivedSelector = bytes4(reason);\n if (receivedSelector == QuoterHelper.ValidAmount.selector) {\n uint256 value;\n uint256 iterations;\n bool swapWETHForOETHB;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n value := mload(add(reason, 0x24))\n iterations := mload(add(reason, 0x44))\n swapWETHForOETHB := mload(add(reason, 0x64))\n }\n emit ValueFound(value, iterations, swapWETHForOETHB);\n return Data({ amount: value, iterations: iterations });\n }\n\n if (receivedSelector == QuoterHelper.OutOfIterations.selector) {\n emit ValueNotFound(\"Out of iterations\");\n revert(\"Out of iterations\");\n }\n\n emit ValueNotFound(\"Unexpected error\");\n revert(abi.decode(reason, (string)));\n }\n }\n\n /// @notice Use this to get the amount to swap to reach the target price after swap.\n /// @dev This call will only revert, check the logs to get returned values.\n /// @param sqrtPriceTargetX96 The target price to reach.\n function quoteAmountToSwapToReachPrice(uint160 sqrtPriceTargetX96) public {\n (\n uint256 amount,\n uint256 iterations,\n bool swapWETHForOETHB,\n uint160 sqrtPriceAfterX96\n ) = quoterHelper.getAmountToSwapToReachPrice(sqrtPriceTargetX96);\n\n emit ValueFoundBis(\n amount,\n iterations,\n swapWETHForOETHB,\n sqrtPriceAfterX96\n );\n }\n\n function claimGovernance() public {\n quoterHelper.claimGovernanceOnAMO();\n }\n\n function giveBackGovernance() public {\n quoterHelper.giveBackGovernanceOnAMO();\n }\n}\n" + }, + "contracts/utils/BalancerErrors.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity >=0.7.4 <0.9.0;\n\n// solhint-disable\n\n/**\n * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are\n * supported.\n * Uses the default 'BAL' prefix for the error code\n */\nfunction _require(bool condition, uint256 errorCode) pure {\n if (!condition) _revert(errorCode);\n}\n\n/**\n * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are\n * supported.\n */\nfunction _require(\n bool condition,\n uint256 errorCode,\n bytes3 prefix\n) pure {\n if (!condition) _revert(errorCode, prefix);\n}\n\n/**\n * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported.\n * Uses the default 'BAL' prefix for the error code\n */\nfunction _revert(uint256 errorCode) pure {\n _revert(errorCode, 0x42414c); // This is the raw byte representation of \"BAL\"\n}\n\n/**\n * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported.\n */\nfunction _revert(uint256 errorCode, bytes3 prefix) pure {\n uint256 prefixUint = uint256(uint24(prefix));\n // We're going to dynamically create a revert string based on the error code, with the following format:\n // 'BAL#{errorCode}'\n // where the code is left-padded with zeroes to three digits (so they range from 000 to 999).\n //\n // We don't have revert strings embedded in the contract to save bytecode size: it takes much less space to store a\n // number (8 to 16 bits) than the individual string characters.\n //\n // The dynamic string creation algorithm that follows could be implemented in Solidity, but assembly allows for a\n // much denser implementation, again saving bytecode size. Given this function unconditionally reverts, this is a\n // safe place to rely on it without worrying about how its usage might affect e.g. memory contents.\n assembly {\n // First, we need to compute the ASCII representation of the error code. We assume that it is in the 0-999\n // range, so we only need to convert three digits. To convert the digits to ASCII, we add 0x30, the value for\n // the '0' character.\n\n let units := add(mod(errorCode, 10), 0x30)\n\n errorCode := div(errorCode, 10)\n let tenths := add(mod(errorCode, 10), 0x30)\n\n errorCode := div(errorCode, 10)\n let hundreds := add(mod(errorCode, 10), 0x30)\n\n // With the individual characters, we can now construct the full string.\n // We first append the '#' character (0x23) to the prefix. In the case of 'BAL', it results in 0x42414c23 ('BAL#')\n // Then, we shift this by 24 (to provide space for the 3 bytes of the error code), and add the\n // characters to it, each shifted by a multiple of 8.\n // The revert reason is then shifted left by 200 bits (256 minus the length of the string, 7 characters * 8 bits\n // per character = 56) to locate it in the most significant part of the 256 slot (the beginning of a byte\n // array).\n let formattedPrefix := shl(24, add(0x23, shl(8, prefixUint)))\n\n let revertReason := shl(\n 200,\n add(\n formattedPrefix,\n add(add(units, shl(8, tenths)), shl(16, hundreds))\n )\n )\n\n // We can now encode the reason in memory, which can be safely overwritten as we're about to revert. The encoded\n // message will have the following layout:\n // [ revert reason identifier ] [ string location offset ] [ string length ] [ string contents ]\n\n // The Solidity revert reason identifier is 0x08c739a0, the function selector of the Error(string) function. We\n // also write zeroes to the next 28 bytes of memory, but those are about to be overwritten.\n mstore(\n 0x0,\n 0x08c379a000000000000000000000000000000000000000000000000000000000\n )\n // Next is the offset to the location of the string, which will be placed immediately after (20 bytes away).\n mstore(\n 0x04,\n 0x0000000000000000000000000000000000000000000000000000000000000020\n )\n // The string length is fixed: 7 characters.\n mstore(0x24, 7)\n // Finally, the string itself is stored.\n mstore(0x44, revertReason)\n\n // Even if the string is only 7 bytes long, we need to return a full 32 byte slot containing it. The length of\n // the encoded message is therefore 4 + 32 + 32 + 32 = 100.\n revert(0, 100)\n }\n}\n\nlibrary Errors {\n // Math\n uint256 internal constant ADD_OVERFLOW = 0;\n uint256 internal constant SUB_OVERFLOW = 1;\n uint256 internal constant SUB_UNDERFLOW = 2;\n uint256 internal constant MUL_OVERFLOW = 3;\n uint256 internal constant ZERO_DIVISION = 4;\n uint256 internal constant DIV_INTERNAL = 5;\n uint256 internal constant X_OUT_OF_BOUNDS = 6;\n uint256 internal constant Y_OUT_OF_BOUNDS = 7;\n uint256 internal constant PRODUCT_OUT_OF_BOUNDS = 8;\n uint256 internal constant INVALID_EXPONENT = 9;\n\n // Input\n uint256 internal constant OUT_OF_BOUNDS = 100;\n uint256 internal constant UNSORTED_ARRAY = 101;\n uint256 internal constant UNSORTED_TOKENS = 102;\n uint256 internal constant INPUT_LENGTH_MISMATCH = 103;\n uint256 internal constant ZERO_TOKEN = 104;\n uint256 internal constant INSUFFICIENT_DATA = 105;\n\n // Shared pools\n uint256 internal constant MIN_TOKENS = 200;\n uint256 internal constant MAX_TOKENS = 201;\n uint256 internal constant MAX_SWAP_FEE_PERCENTAGE = 202;\n uint256 internal constant MIN_SWAP_FEE_PERCENTAGE = 203;\n uint256 internal constant MINIMUM_BPT = 204;\n uint256 internal constant CALLER_NOT_VAULT = 205;\n uint256 internal constant UNINITIALIZED = 206;\n uint256 internal constant BPT_IN_MAX_AMOUNT = 207;\n uint256 internal constant BPT_OUT_MIN_AMOUNT = 208;\n uint256 internal constant EXPIRED_PERMIT = 209;\n uint256 internal constant NOT_TWO_TOKENS = 210;\n uint256 internal constant DISABLED = 211;\n\n // Pools\n uint256 internal constant MIN_AMP = 300;\n uint256 internal constant MAX_AMP = 301;\n uint256 internal constant MIN_WEIGHT = 302;\n uint256 internal constant MAX_STABLE_TOKENS = 303;\n uint256 internal constant MAX_IN_RATIO = 304;\n uint256 internal constant MAX_OUT_RATIO = 305;\n uint256 internal constant MIN_BPT_IN_FOR_TOKEN_OUT = 306;\n uint256 internal constant MAX_OUT_BPT_FOR_TOKEN_IN = 307;\n uint256 internal constant NORMALIZED_WEIGHT_INVARIANT = 308;\n uint256 internal constant INVALID_TOKEN = 309;\n uint256 internal constant UNHANDLED_JOIN_KIND = 310;\n uint256 internal constant ZERO_INVARIANT = 311;\n uint256 internal constant ORACLE_INVALID_SECONDS_QUERY = 312;\n uint256 internal constant ORACLE_NOT_INITIALIZED = 313;\n uint256 internal constant ORACLE_QUERY_TOO_OLD = 314;\n uint256 internal constant ORACLE_INVALID_INDEX = 315;\n uint256 internal constant ORACLE_BAD_SECS = 316;\n uint256 internal constant AMP_END_TIME_TOO_CLOSE = 317;\n uint256 internal constant AMP_ONGOING_UPDATE = 318;\n uint256 internal constant AMP_RATE_TOO_HIGH = 319;\n uint256 internal constant AMP_NO_ONGOING_UPDATE = 320;\n uint256 internal constant STABLE_INVARIANT_DIDNT_CONVERGE = 321;\n uint256 internal constant STABLE_GET_BALANCE_DIDNT_CONVERGE = 322;\n uint256 internal constant RELAYER_NOT_CONTRACT = 323;\n uint256 internal constant BASE_POOL_RELAYER_NOT_CALLED = 324;\n uint256 internal constant REBALANCING_RELAYER_REENTERED = 325;\n uint256 internal constant GRADUAL_UPDATE_TIME_TRAVEL = 326;\n uint256 internal constant SWAPS_DISABLED = 327;\n uint256 internal constant CALLER_IS_NOT_LBP_OWNER = 328;\n uint256 internal constant PRICE_RATE_OVERFLOW = 329;\n uint256 internal constant INVALID_JOIN_EXIT_KIND_WHILE_SWAPS_DISABLED = 330;\n uint256 internal constant WEIGHT_CHANGE_TOO_FAST = 331;\n uint256 internal constant LOWER_GREATER_THAN_UPPER_TARGET = 332;\n uint256 internal constant UPPER_TARGET_TOO_HIGH = 333;\n uint256 internal constant UNHANDLED_BY_LINEAR_POOL = 334;\n uint256 internal constant OUT_OF_TARGET_RANGE = 335;\n uint256 internal constant UNHANDLED_EXIT_KIND = 336;\n uint256 internal constant UNAUTHORIZED_EXIT = 337;\n uint256 internal constant MAX_MANAGEMENT_SWAP_FEE_PERCENTAGE = 338;\n uint256 internal constant UNHANDLED_BY_MANAGED_POOL = 339;\n uint256 internal constant UNHANDLED_BY_PHANTOM_POOL = 340;\n uint256 internal constant TOKEN_DOES_NOT_HAVE_RATE_PROVIDER = 341;\n uint256 internal constant INVALID_INITIALIZATION = 342;\n uint256 internal constant OUT_OF_NEW_TARGET_RANGE = 343;\n uint256 internal constant FEATURE_DISABLED = 344;\n uint256 internal constant UNINITIALIZED_POOL_CONTROLLER = 345;\n uint256 internal constant SET_SWAP_FEE_DURING_FEE_CHANGE = 346;\n uint256 internal constant SET_SWAP_FEE_PENDING_FEE_CHANGE = 347;\n uint256 internal constant CHANGE_TOKENS_DURING_WEIGHT_CHANGE = 348;\n uint256 internal constant CHANGE_TOKENS_PENDING_WEIGHT_CHANGE = 349;\n uint256 internal constant MAX_WEIGHT = 350;\n uint256 internal constant UNAUTHORIZED_JOIN = 351;\n uint256 internal constant MAX_MANAGEMENT_AUM_FEE_PERCENTAGE = 352;\n uint256 internal constant FRACTIONAL_TARGET = 353;\n uint256 internal constant ADD_OR_REMOVE_BPT = 354;\n uint256 internal constant INVALID_CIRCUIT_BREAKER_BOUNDS = 355;\n uint256 internal constant CIRCUIT_BREAKER_TRIPPED = 356;\n uint256 internal constant MALICIOUS_QUERY_REVERT = 357;\n uint256 internal constant JOINS_EXITS_DISABLED = 358;\n\n // Lib\n uint256 internal constant REENTRANCY = 400;\n uint256 internal constant SENDER_NOT_ALLOWED = 401;\n uint256 internal constant PAUSED = 402;\n uint256 internal constant PAUSE_WINDOW_EXPIRED = 403;\n uint256 internal constant MAX_PAUSE_WINDOW_DURATION = 404;\n uint256 internal constant MAX_BUFFER_PERIOD_DURATION = 405;\n uint256 internal constant INSUFFICIENT_BALANCE = 406;\n uint256 internal constant INSUFFICIENT_ALLOWANCE = 407;\n uint256 internal constant ERC20_TRANSFER_FROM_ZERO_ADDRESS = 408;\n uint256 internal constant ERC20_TRANSFER_TO_ZERO_ADDRESS = 409;\n uint256 internal constant ERC20_MINT_TO_ZERO_ADDRESS = 410;\n uint256 internal constant ERC20_BURN_FROM_ZERO_ADDRESS = 411;\n uint256 internal constant ERC20_APPROVE_FROM_ZERO_ADDRESS = 412;\n uint256 internal constant ERC20_APPROVE_TO_ZERO_ADDRESS = 413;\n uint256 internal constant ERC20_TRANSFER_EXCEEDS_ALLOWANCE = 414;\n uint256 internal constant ERC20_DECREASED_ALLOWANCE_BELOW_ZERO = 415;\n uint256 internal constant ERC20_TRANSFER_EXCEEDS_BALANCE = 416;\n uint256 internal constant ERC20_BURN_EXCEEDS_ALLOWANCE = 417;\n uint256 internal constant SAFE_ERC20_CALL_FAILED = 418;\n uint256 internal constant ADDRESS_INSUFFICIENT_BALANCE = 419;\n uint256 internal constant ADDRESS_CANNOT_SEND_VALUE = 420;\n uint256 internal constant SAFE_CAST_VALUE_CANT_FIT_INT256 = 421;\n uint256 internal constant GRANT_SENDER_NOT_ADMIN = 422;\n uint256 internal constant REVOKE_SENDER_NOT_ADMIN = 423;\n uint256 internal constant RENOUNCE_SENDER_NOT_ALLOWED = 424;\n uint256 internal constant BUFFER_PERIOD_EXPIRED = 425;\n uint256 internal constant CALLER_IS_NOT_OWNER = 426;\n uint256 internal constant NEW_OWNER_IS_ZERO = 427;\n uint256 internal constant CODE_DEPLOYMENT_FAILED = 428;\n uint256 internal constant CALL_TO_NON_CONTRACT = 429;\n uint256 internal constant LOW_LEVEL_CALL_FAILED = 430;\n uint256 internal constant NOT_PAUSED = 431;\n uint256 internal constant ADDRESS_ALREADY_ALLOWLISTED = 432;\n uint256 internal constant ADDRESS_NOT_ALLOWLISTED = 433;\n uint256 internal constant ERC20_BURN_EXCEEDS_BALANCE = 434;\n uint256 internal constant INVALID_OPERATION = 435;\n uint256 internal constant CODEC_OVERFLOW = 436;\n uint256 internal constant IN_RECOVERY_MODE = 437;\n uint256 internal constant NOT_IN_RECOVERY_MODE = 438;\n uint256 internal constant INDUCED_FAILURE = 439;\n uint256 internal constant EXPIRED_SIGNATURE = 440;\n uint256 internal constant MALFORMED_SIGNATURE = 441;\n uint256 internal constant SAFE_CAST_VALUE_CANT_FIT_UINT64 = 442;\n uint256 internal constant UNHANDLED_FEE_TYPE = 443;\n uint256 internal constant BURN_FROM_ZERO = 444;\n\n // Vault\n uint256 internal constant INVALID_POOL_ID = 500;\n uint256 internal constant CALLER_NOT_POOL = 501;\n uint256 internal constant SENDER_NOT_ASSET_MANAGER = 502;\n uint256 internal constant USER_DOESNT_ALLOW_RELAYER = 503;\n uint256 internal constant INVALID_SIGNATURE = 504;\n uint256 internal constant EXIT_BELOW_MIN = 505;\n uint256 internal constant JOIN_ABOVE_MAX = 506;\n uint256 internal constant SWAP_LIMIT = 507;\n uint256 internal constant SWAP_DEADLINE = 508;\n uint256 internal constant CANNOT_SWAP_SAME_TOKEN = 509;\n uint256 internal constant UNKNOWN_AMOUNT_IN_FIRST_SWAP = 510;\n uint256 internal constant MALCONSTRUCTED_MULTIHOP_SWAP = 511;\n uint256 internal constant INTERNAL_BALANCE_OVERFLOW = 512;\n uint256 internal constant INSUFFICIENT_INTERNAL_BALANCE = 513;\n uint256 internal constant INVALID_ETH_INTERNAL_BALANCE = 514;\n uint256 internal constant INVALID_POST_LOAN_BALANCE = 515;\n uint256 internal constant INSUFFICIENT_ETH = 516;\n uint256 internal constant UNALLOCATED_ETH = 517;\n uint256 internal constant ETH_TRANSFER = 518;\n uint256 internal constant CANNOT_USE_ETH_SENTINEL = 519;\n uint256 internal constant TOKENS_MISMATCH = 520;\n uint256 internal constant TOKEN_NOT_REGISTERED = 521;\n uint256 internal constant TOKEN_ALREADY_REGISTERED = 522;\n uint256 internal constant TOKENS_ALREADY_SET = 523;\n uint256 internal constant TOKENS_LENGTH_MUST_BE_2 = 524;\n uint256 internal constant NONZERO_TOKEN_BALANCE = 525;\n uint256 internal constant BALANCE_TOTAL_OVERFLOW = 526;\n uint256 internal constant POOL_NO_TOKENS = 527;\n uint256 internal constant INSUFFICIENT_FLASH_LOAN_BALANCE = 528;\n\n // Fees\n uint256 internal constant SWAP_FEE_PERCENTAGE_TOO_HIGH = 600;\n uint256 internal constant FLASH_LOAN_FEE_PERCENTAGE_TOO_HIGH = 601;\n uint256 internal constant INSUFFICIENT_FLASH_LOAN_FEE_AMOUNT = 602;\n uint256 internal constant AUM_FEE_PERCENTAGE_TOO_HIGH = 603;\n\n // FeeSplitter\n uint256 internal constant SPLITTER_FEE_PERCENTAGE_TOO_HIGH = 700;\n\n // Misc\n uint256 internal constant UNIMPLEMENTED = 998;\n uint256 internal constant SHOULD_NOT_HAPPEN = 999;\n}\n" + }, + "contracts/utils/DepositContractUtils.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract DepositContractUtils {\n function calculateDepositDataRoot(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature\n ) public pure returns (bytes32 node) {\n uint256 deposit_amount = 32 ether / 1 gwei;\n bytes memory amount = to_little_endian_64(uint64(deposit_amount));\n\n // Compute deposit data root (`DepositData` hash tree root)\n bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, bytes16(0)));\n bytes32 signature_root = sha256(\n abi.encodePacked(\n sha256(abi.encodePacked(signature[:64])),\n sha256(abi.encodePacked(signature[64:], bytes32(0)))\n )\n );\n node = sha256(\n abi.encodePacked(\n sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)),\n sha256(abi.encodePacked(amount, bytes24(0), signature_root))\n )\n );\n }\n\n function to_little_endian_64(uint64 value)\n internal\n pure\n returns (bytes memory ret)\n {\n ret = new bytes(8);\n bytes8 bytesValue = bytes8(value);\n // Byteswapping during copying to bytes.\n ret[0] = bytesValue[7];\n ret[1] = bytesValue[6];\n ret[2] = bytesValue[5];\n ret[3] = bytesValue[4];\n ret[4] = bytesValue[3];\n ret[5] = bytesValue[2];\n ret[6] = bytesValue[1];\n ret[7] = bytesValue[0];\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/utils/InitializableERC20Detailed.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @dev Optional functions from the ERC20 standard.\n * Converted from openzeppelin/contracts/token/ERC20/ERC20Detailed.sol\n * @author Origin Protocol Inc\n */\nabstract contract InitializableERC20Detailed is IERC20 {\n // Storage gap to skip storage from prior to OUSD reset\n uint256[100] private _____gap;\n\n string private _name;\n string private _symbol;\n uint8 private _decimals;\n\n /**\n * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of\n * these values are immutable: they can only be set once during\n * construction.\n * @notice To avoid variable shadowing appended `Arg` after arguments name.\n */\n function _initialize(\n string memory nameArg,\n string memory symbolArg,\n uint8 decimalsArg\n ) internal {\n _name = nameArg;\n _symbol = symbolArg;\n _decimals = decimalsArg;\n }\n\n /**\n * @notice Returns the name of the token.\n */\n function name() public view returns (string memory) {\n return _name;\n }\n\n /**\n * @notice Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view returns (string memory) {\n return _symbol;\n }\n\n /**\n * @notice Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5,05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei.\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view returns (uint8) {\n return _decimals;\n }\n}\n" + }, + "contracts/utils/PRBMath.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// Copied from the PRBMath library\n// https://github.com/PaulRBerg/prb-math/blob/main/src/Common.sol\n\n/// @notice Calculates the square root of x using the Babylonian method.\n///\n/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.\n///\n/// Notes:\n/// - If x is not a perfect square, the result is rounded down.\n/// - Credits to OpenZeppelin for the explanations in comments below.\n///\n/// @param x The uint256 number for which to calculate the square root.\n/// @return result The result as a uint256.\n/// @custom:smtchecker abstract-function-nondet\nfunction sqrt(uint256 x) pure returns (uint256 result) {\n if (x == 0) {\n return 0;\n }\n\n // For our first guess, we calculate the biggest power of 2 which is smaller than the square root of x.\n //\n // We know that the \"msb\" (most significant bit) of x is a power of 2 such that we have:\n //\n // $$\n // msb(x) <= x <= 2*msb(x)$\n // $$\n //\n // We write $msb(x)$ as $2^k$, and we get:\n //\n // $$\n // k = log_2(x)\n // $$\n //\n // Thus, we can write the initial inequality as:\n //\n // $$\n // 2^{log_2(x)} <= x <= 2*2^{log_2(x)+1} \\\\\n // sqrt(2^k) <= sqrt(x) < sqrt(2^{k+1}) \\\\\n // 2^{k/2} <= sqrt(x) < 2^{(k+1)/2} <= 2^{(k/2)+1}\n // $$\n //\n // Consequently, $2^{log_2(x) /2} is a good first approximation of sqrt(x) with at least one correct bit.\n uint256 xAux = uint256(x);\n result = 1;\n if (xAux >= 2**128) {\n xAux >>= 128;\n result <<= 64;\n }\n if (xAux >= 2**64) {\n xAux >>= 64;\n result <<= 32;\n }\n if (xAux >= 2**32) {\n xAux >>= 32;\n result <<= 16;\n }\n if (xAux >= 2**16) {\n xAux >>= 16;\n result <<= 8;\n }\n if (xAux >= 2**8) {\n xAux >>= 8;\n result <<= 4;\n }\n if (xAux >= 2**4) {\n xAux >>= 4;\n result <<= 2;\n }\n if (xAux >= 2**2) {\n result <<= 1;\n }\n\n // At this point, `result` is an estimation with at least one bit of precision. We know the true value has at\n // most 128 bits, since it is the square root of a uint256. Newton's method converges quadratically (precision\n // doubles at every iteration). We thus need at most 7 iteration to turn our partial result with one bit of\n // precision into the expected uint128 result.\n unchecked {\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n\n // If x is not a perfect square, round the result toward zero.\n uint256 roundedResult = x / result;\n if (result >= roundedResult) {\n result = roundedResult;\n }\n }\n}\n" + }, + "contracts/utils/StableMath.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\n// Based on StableMath from Stability Labs Pty. Ltd.\n// https://github.com/mstable/mStable-contracts/blob/master/contracts/shared/StableMath.sol\n\nlibrary StableMath {\n using SafeMath for uint256;\n\n /**\n * @dev Scaling unit for use in specific calculations,\n * where 1 * 10**18, or 1e18 represents a unit '1'\n */\n uint256 private constant FULL_SCALE = 1e18;\n\n /***************************************\n Helpers\n ****************************************/\n\n /**\n * @dev Adjust the scale of an integer\n * @param to Decimals to scale to\n * @param from Decimals to scale from\n */\n function scaleBy(\n uint256 x,\n uint256 to,\n uint256 from\n ) internal pure returns (uint256) {\n if (to > from) {\n x = x.mul(10**(to - from));\n } else if (to < from) {\n // slither-disable-next-line divide-before-multiply\n x = x.div(10**(from - to));\n }\n return x;\n }\n\n /***************************************\n Precise Arithmetic\n ****************************************/\n\n /**\n * @dev Multiplies two precise units, and then truncates by the full scale\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit\n */\n function mulTruncate(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulTruncateScale(x, y, FULL_SCALE);\n }\n\n /**\n * @dev Multiplies two precise units, and then truncates by the given scale. For example,\n * when calculating 90% of 10e18, (10e18 * 9e17) / 1e18 = (9e36) / 1e18 = 9e18\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @param scale Scale unit\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit\n */\n function mulTruncateScale(\n uint256 x,\n uint256 y,\n uint256 scale\n ) internal pure returns (uint256) {\n // e.g. assume scale = fullScale\n // z = 10e18 * 9e17 = 9e36\n uint256 z = x.mul(y);\n // return 9e36 / 1e18 = 9e18\n return z.div(scale);\n }\n\n /**\n * @dev Multiplies two precise units, and then truncates by the full scale, rounding up the result\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit, rounded up to the closest base unit.\n */\n function mulTruncateCeil(uint256 x, uint256 y)\n internal\n pure\n returns (uint256)\n {\n // e.g. 8e17 * 17268172638 = 138145381104e17\n uint256 scaled = x.mul(y);\n // e.g. 138145381104e17 + 9.99...e17 = 138145381113.99...e17\n uint256 ceil = scaled.add(FULL_SCALE.sub(1));\n // e.g. 13814538111.399...e18 / 1e18 = 13814538111\n return ceil.div(FULL_SCALE);\n }\n\n /**\n * @dev Precisely divides two units, by first scaling the left hand operand. Useful\n * for finding percentage weightings, i.e. 8e18/10e18 = 80% (or 8e17)\n * @param x Left hand input to division\n * @param y Right hand input to division\n * @return Result after multiplying the left operand by the scale, and\n * executing the division on the right hand input.\n */\n function divPrecisely(uint256 x, uint256 y)\n internal\n pure\n returns (uint256)\n {\n // e.g. 8e18 * 1e18 = 8e36\n uint256 z = x.mul(FULL_SCALE);\n // e.g. 8e36 / 10e18 = 8e17\n return z.div(y);\n }\n}\n" + }, + "contracts/vault/OETHBaseVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"./OETHVaultAdmin.sol\";\n\n/**\n * @title OETH Base VaultAdmin Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBaseVaultAdmin is OETHVaultAdmin {\n constructor(address _weth) OETHVaultAdmin(_weth) {}\n}\n" + }, + "contracts/vault/OETHBaseVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { OETHVaultCore } from \"./OETHVaultCore.sol\";\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\n\n/**\n * @title OETH Base VaultCore Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBaseVaultCore is OETHVaultCore {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(address _weth) OETHVaultCore(_weth) {}\n\n // @inheritdoc OETHVaultCore\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n override\n {\n // Only Strategist or Governor can redeem using the Vault for now.\n // We don't have the onlyGovernorOrStrategist modifier on VaultCore.\n // Since we won't be using that modifier anywhere in the VaultCore as well,\n // the check has been added inline instead of moving it to VaultStorage.\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n\n super._redeem(_amount, _minimumUnitAmount);\n }\n}\n" + }, + "contracts/vault/OETHBaseZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\n\ncontract OETHBaseZapper {\n IERC20 public immutable oethb;\n IERC4626 public immutable woethb;\n IVault public immutable vault;\n\n IWETH9 public constant weth =\n IWETH9(0x4200000000000000000000000000000000000006);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(\n address _oethb,\n address _woethb,\n address _vault\n ) {\n oethb = IERC20(_oethb);\n woethb = IERC4626(_woethb);\n vault = IVault(_vault);\n\n weth.approve(address(_vault), type(uint256).max);\n IERC20(_oethb).approve(_woethb, type(uint256).max);\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return.\n * Will verify that the user is sent 1:1 for ETH.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit ETH and receive superOETHb in return\n * Will verify that the user is sent 1:1 for ETH.\n * @return Amount of OETH sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap ETH\n weth.deposit{ value: balance }();\n\n // Mint with WETH\n return _mint(balance, msg.sender);\n }\n\n /**\n * @dev Deposit ETH and receive superOETHb in return\n * @param minReceived min amount of wsuperOETHb to receive\n * @return Amount of wsuperOETHb sent to user\n */\n function depositETHForWrappedTokens(uint256 minReceived)\n external\n payable\n returns (uint256)\n {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap ETH\n weth.deposit{ value: balance }();\n\n // Mint with WETH\n uint256 mintedOethb = _mint(balance, address(this));\n\n // Wrap superOETHb into wsuperOETHb\n uint256 mintedWoethb = woethb.deposit(mintedOethb, msg.sender);\n\n require(mintedWoethb >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWoethb;\n }\n\n /**\n * @dev Deposit WETH and receive superOETHb in return\n * @param wethAmount Amount of WETH to deposit\n * @param minReceived min amount of wsuperOETHb to receive\n * @return Amount of wsuperOETHb sent to user\n */\n function depositWETHForWrappedTokens(\n uint256 wethAmount,\n uint256 minReceived\n ) external returns (uint256) {\n // slither-disable-next-line unchecked-transfer unused-return\n weth.transferFrom(msg.sender, address(this), wethAmount);\n\n emit Zap(msg.sender, address(weth), wethAmount);\n\n // Mint with WETH\n uint256 mintedOethb = _mint(wethAmount, address(this));\n\n // Wrap superOETHb into wsuperOETHb\n uint256 mintedWoethb = woethb.deposit(mintedOethb, msg.sender);\n\n require(mintedWoethb >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWoethb;\n }\n\n /**\n * @dev Internal function to mint superOETHb with WETH\n * @param minOETH Minimum amount of OETH to for user to receive\n * @param recipient Address that receives the tokens\n * @return Amount of OETH sent to user\n */\n function _mint(uint256 minOETH, address recipient)\n internal\n returns (uint256)\n {\n uint256 toMint = weth.balanceOf(address(this));\n vault.mint(address(weth), toMint, minOETH);\n uint256 mintedAmount = oethb.balanceOf(address(this));\n require(mintedAmount >= minOETH, \"Zapper: not enough minted\");\n\n if (recipient != address(this)) {\n require(oethb.transfer(recipient, mintedAmount));\n }\n\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/OETHVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Vault } from \"./Vault.sol\";\n\n/**\n * @title OETH Vault Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVault is Vault {\n\n}\n" + }, + "contracts/vault/OETHVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { VaultAdmin } from \"./VaultAdmin.sol\";\n\n/**\n * @title OETH VaultAdmin Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVaultAdmin is VaultAdmin {\n using SafeERC20 for IERC20;\n\n address public immutable weth;\n\n constructor(address _weth) {\n weth = _weth;\n }\n\n /**\n * @notice Adds a strategy to the mint whitelist.\n * Reverts if strategy isn't approved on Vault.\n * @param strategyAddr Strategy address\n */\n function addStrategyToMintWhitelist(address strategyAddr)\n external\n onlyGovernor\n {\n require(strategies[strategyAddr].isSupported, \"Strategy not approved\");\n\n require(\n !isMintWhitelistedStrategy[strategyAddr],\n \"Already whitelisted\"\n );\n\n isMintWhitelistedStrategy[strategyAddr] = true;\n\n emit StrategyAddedToMintWhitelist(strategyAddr);\n }\n\n /**\n * @notice Removes a strategy from the mint whitelist.\n * @param strategyAddr Strategy address\n */\n function removeStrategyFromMintWhitelist(address strategyAddr)\n external\n onlyGovernor\n {\n // Intentionally skipping `strategies.isSupported` check since\n // we may wanna remove an address even after removing the strategy\n\n require(isMintWhitelistedStrategy[strategyAddr], \"Not whitelisted\");\n\n isMintWhitelistedStrategy[strategyAddr] = false;\n\n emit StrategyRemovedFromMintWhitelist(strategyAddr);\n }\n\n /// @dev Simplified version of the deposit function as WETH is the only supported asset.\n function _depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal override {\n require(\n strategies[_strategyToAddress].isSupported,\n \"Invalid to Strategy\"\n );\n require(\n _assets.length == 1 && _amounts.length == 1 && _assets[0] == weth,\n \"Only WETH is supported\"\n );\n\n // Check the there is enough WETH to transfer once the WETH reserved for the withdrawal queue is accounted for\n require(_amounts[0] <= _wethAvailable(), \"Not enough WETH available\");\n\n // Send required amount of funds to the strategy\n IERC20(weth).safeTransfer(_strategyToAddress, _amounts[0]);\n\n // Deposit all the funds that have been sent to the strategy\n IStrategy(_strategyToAddress).depositAll();\n }\n\n function _withdrawFromStrategy(\n address _recipient,\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal override {\n super._withdrawFromStrategy(\n _recipient,\n _strategyFromAddress,\n _assets,\n _amounts\n );\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n function _withdrawAllFromStrategy(address _strategyAddr) internal override {\n super._withdrawAllFromStrategy(_strategyAddr);\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n function _withdrawAllFromStrategies() internal override {\n super._withdrawAllFromStrategies();\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n /// @dev Calculate how much WETH in the vault is not reserved for the withdrawal queue.\n // That is, it is available to be redeemed or deposited into a strategy.\n function _wethAvailable() internal view returns (uint256 wethAvailable) {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n uint256 outstandingWithdrawals = queue.queued - queue.claimed;\n\n // The amount of sitting in WETH in the vault\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // If there is not enough WETH in the vault to cover the outstanding withdrawals\n if (wethBalance <= outstandingWithdrawals) {\n return 0;\n }\n\n return wethBalance - outstandingWithdrawals;\n }\n\n function _swapCollateral(\n address,\n address,\n uint256,\n uint256,\n bytes calldata\n ) internal pure override returns (uint256) {\n revert(\"Collateral swap not supported\");\n }\n}\n" + }, + "contracts/vault/OETHVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { VaultCore } from \"./VaultCore.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\n\n/**\n * @title OETH VaultCore Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVaultCore is VaultCore {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n address public immutable weth;\n uint256 public wethAssetIndex;\n\n // For future use (because OETHBaseVaultCore inherits from this)\n uint256[50] private __gap;\n\n constructor(address _weth) {\n weth = _weth;\n }\n\n /**\n * @dev Caches WETH's index in `allAssets` variable.\n * Reduces gas usage by redeem by caching that.\n */\n function cacheWETHAssetIndex() external onlyGovernor {\n uint256 assetCount = allAssets.length;\n for (uint256 i; i < assetCount; ++i) {\n if (allAssets[i] == weth) {\n wethAssetIndex = i;\n break;\n }\n }\n\n require(allAssets[wethAssetIndex] == weth, \"Invalid WETH Asset Index\");\n }\n\n // @inheritdoc VaultCore\n function mintForStrategy(uint256 amount)\n external\n override\n whenNotCapitalPaused\n {\n require(\n strategies[msg.sender].isSupported == true,\n \"Unsupported strategy\"\n );\n require(\n isMintWhitelistedStrategy[msg.sender] == true,\n \"Not whitelisted strategy\"\n );\n\n emit Mint(msg.sender, amount);\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, amount);\n }\n\n // @inheritdoc VaultCore\n function burnForStrategy(uint256 amount)\n external\n override\n whenNotCapitalPaused\n {\n require(\n strategies[msg.sender].isSupported == true,\n \"Unsupported strategy\"\n );\n require(\n isMintWhitelistedStrategy[msg.sender] == true,\n \"Not whitelisted strategy\"\n );\n\n emit Redeem(msg.sender, amount);\n\n // Burn OTokens\n oUSD.burn(msg.sender, amount);\n }\n\n // @inheritdoc VaultCore\n // slither-disable-start reentrancy-no-eth\n function _mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) internal virtual override {\n require(_asset == weth, \"Unsupported asset for minting\");\n require(_amount > 0, \"Amount must be greater than 0\");\n require(\n _amount >= _minimumOusdAmount,\n \"Mint amount lower than minimum\"\n );\n\n emit Mint(msg.sender, _amount);\n\n // Rebase must happen before any transfers occur.\n if (!rebasePaused && _amount >= rebaseThreshold) {\n _rebase();\n }\n\n // Mint oTokens\n oUSD.mint(msg.sender, _amount);\n\n // Transfer the deposited coins to the vault\n IERC20(_asset).safeTransferFrom(msg.sender, address(this), _amount);\n\n // Give priority to the withdrawal queue for the new WETH liquidity\n _addWithdrawalQueueLiquidity();\n\n // Auto-allocate if necessary\n if (_amount >= autoAllocateThreshold) {\n _allocate();\n }\n }\n\n // slither-disable-end reentrancy-no-eth\n\n // @inheritdoc VaultCore\n function _calculateRedeemOutputs(uint256 _amount)\n internal\n view\n virtual\n override\n returns (uint256[] memory outputs)\n {\n // Overrides `VaultCore._calculateRedeemOutputs` to redeem with only\n // WETH instead of LST-mix. Doesn't change the function signature\n // for backward compatibility\n\n // Calculate redeem fee\n if (redeemFeeBps > 0) {\n uint256 redeemFee = _amount.mulTruncateScale(redeemFeeBps, 1e4);\n _amount = _amount - redeemFee;\n }\n\n // Ensure that the WETH index is cached\n uint256 _wethAssetIndex = wethAssetIndex;\n require(\n allAssets[_wethAssetIndex] == weth,\n \"WETH Asset index not cached\"\n );\n\n outputs = new uint256[](allAssets.length);\n outputs[_wethAssetIndex] = _amount;\n }\n\n // @inheritdoc VaultCore\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n override\n {\n // Override `VaultCore._redeem` to simplify it. Gets rid of oracle\n // usage and looping through all assets for LST-mix redeem. Instead\n // does a simple WETH-only redeem.\n emit Redeem(msg.sender, _amount);\n\n if (_amount == 0) {\n return;\n }\n\n // Amount excluding fees\n // No fee for the strategist or the governor, makes it easier to do operations\n uint256 amountMinusFee = (msg.sender == strategistAddr || isGovernor())\n ? _amount\n : _calculateRedeemOutputs(_amount)[wethAssetIndex];\n\n require(\n amountMinusFee >= _minimumUnitAmount,\n \"Redeem amount lower than minimum\"\n );\n\n // Is there enough WETH in the Vault available after accounting for the withdrawal queue\n require(_wethAvailable() >= amountMinusFee, \"Liquidity error\");\n\n // Transfer WETH minus the fee to the redeemer\n IERC20(weth).safeTransfer(msg.sender, amountMinusFee);\n\n // Burn OETH from user (including fees)\n oUSD.burn(msg.sender, _amount);\n\n // Prevent insolvency\n _postRedeem(_amount);\n }\n\n /**\n * @notice Request an asynchronous withdrawal of WETH in exchange for OETH.\n * The OETH is burned on request and the WETH is transferred to the withdrawer on claim.\n * This request can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal this request's `queued` amount.\n * There is a minimum of 10 minutes before a request can be claimed. After that, the request just needs\n * enough WETH liquidity in the Vault to satisfy all the outstanding requests to that point in the queue.\n * OETH is converted to WETH at 1:1.\n * @param _amount Amount of OETH to burn.\n * @return requestId Unique ID for the withdrawal request\n * @return queued Cumulative total of all WETH queued including already claimed requests.\n */\n function requestWithdrawal(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256 requestId, uint256 queued)\n {\n require(withdrawalClaimDelay > 0, \"Async withdrawals not enabled\");\n\n // The check that the requester has enough OETH is done in to later burn call\n\n requestId = withdrawalQueueMetadata.nextWithdrawalIndex;\n queued = withdrawalQueueMetadata.queued + _amount;\n\n // Store the next withdrawal request\n withdrawalQueueMetadata.nextWithdrawalIndex = SafeCast.toUint128(\n requestId + 1\n );\n // Store the updated queued amount which reserves WETH in the withdrawal queue\n // and reduces the vault's total assets\n withdrawalQueueMetadata.queued = SafeCast.toUint128(queued);\n // Store the user's withdrawal request\n withdrawalRequests[requestId] = WithdrawalRequest({\n withdrawer: msg.sender,\n claimed: false,\n timestamp: uint40(block.timestamp),\n amount: SafeCast.toUint128(_amount),\n queued: SafeCast.toUint128(queued)\n });\n\n // Burn the user's OETH\n oUSD.burn(msg.sender, _amount);\n\n // Prevent withdrawal if the vault is solvent by more than the allowed percentage\n _postRedeem(_amount);\n\n emit WithdrawalRequested(msg.sender, requestId, _amount, queued);\n }\n\n // slither-disable-start reentrancy-no-eth\n /**\n * @notice Claim a previously requested withdrawal once it is claimable.\n * This request can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal this request's `queued` amount and 10 minutes has passed.\n * If the requests is not claimable, the transaction will revert with `Queue pending liquidity`.\n * If the request is not older than 10 minutes, the transaction will revert with `Claim delay not met`.\n * OETH is converted to WETH at 1:1.\n * @param _requestId Unique ID for the withdrawal request\n * @return amount Amount of WETH transferred to the withdrawer\n */\n function claimWithdrawal(uint256 _requestId)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256 amount)\n {\n // Try and get more liquidity if there is not enough available\n if (\n withdrawalRequests[_requestId].queued >\n withdrawalQueueMetadata.claimable\n ) {\n // Add any WETH to the withdrawal queue\n // this needs to remain here as:\n // - Vault can be funded and `addWithdrawalQueueLiquidity` is not externally called\n // - funds can be withdrawn from a strategy\n //\n // Those funds need to be added to withdrawal queue liquidity\n _addWithdrawalQueueLiquidity();\n }\n\n amount = _claimWithdrawal(_requestId);\n\n // transfer WETH from the vault to the withdrawer\n IERC20(weth).safeTransfer(msg.sender, amount);\n\n // Prevent insolvency\n _postRedeem(amount);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @notice Claim a previously requested withdrawals once they are claimable.\n * This requests can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal each request's `queued` amount and 10 minutes has passed.\n * If one of the requests is not claimable, the whole transaction will revert with `Queue pending liquidity`.\n * If one of the requests is not older than 10 minutes,\n * the whole transaction will revert with `Claim delay not met`.\n * @param _requestIds Unique ID of each withdrawal request\n * @return amounts Amount of WETH received for each request\n * @return totalAmount Total amount of WETH transferred to the withdrawer\n */\n function claimWithdrawals(uint256[] calldata _requestIds)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256[] memory amounts, uint256 totalAmount)\n {\n // Add any WETH to the withdrawal queue\n // this needs to remain here as:\n // - Vault can be funded and `addWithdrawalQueueLiquidity` is not externally called\n // - funds can be withdrawn from a strategy\n //\n // Those funds need to be added to withdrawal queue liquidity\n _addWithdrawalQueueLiquidity();\n\n amounts = new uint256[](_requestIds.length);\n for (uint256 i; i < _requestIds.length; ++i) {\n amounts[i] = _claimWithdrawal(_requestIds[i]);\n totalAmount += amounts[i];\n }\n\n // transfer all the claimed WETH from the vault to the withdrawer\n IERC20(weth).safeTransfer(msg.sender, totalAmount);\n\n // Prevent insolvency\n _postRedeem(totalAmount);\n }\n\n function _claimWithdrawal(uint256 requestId)\n internal\n returns (uint256 amount)\n {\n require(withdrawalClaimDelay > 0, \"Async withdrawals not enabled\");\n\n // Load the structs from storage into memory\n WithdrawalRequest memory request = withdrawalRequests[requestId];\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n require(\n request.timestamp + withdrawalClaimDelay <= block.timestamp,\n \"Claim delay not met\"\n );\n // If there isn't enough reserved liquidity in the queue to claim\n require(request.queued <= queue.claimable, \"Queue pending liquidity\");\n require(request.withdrawer == msg.sender, \"Not requester\");\n require(request.claimed == false, \"Already claimed\");\n\n // Store the request as claimed\n withdrawalRequests[requestId].claimed = true;\n // Store the updated claimed amount\n withdrawalQueueMetadata.claimed = queue.claimed + request.amount;\n\n emit WithdrawalClaimed(msg.sender, requestId, request.amount);\n\n return request.amount;\n }\n\n /// @notice Adds WETH to the withdrawal queue if there is a funding shortfall.\n /// @dev is called from the Native Staking strategy when validator withdrawals are processed.\n /// It also called before any WETH is allocated to a strategy.\n function addWithdrawalQueueLiquidity() external {\n _addWithdrawalQueueLiquidity();\n }\n\n /// @dev Adds WETH to the withdrawal queue if there is a funding shortfall.\n /// This assumes 1 WETH equal 1 OETH.\n function _addWithdrawalQueueLiquidity()\n internal\n returns (uint256 addedClaimable)\n {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // Check if the claimable WETH is less than the queued amount\n uint256 queueShortfall = queue.queued - queue.claimable;\n\n // No need to do anything is the withdrawal queue is full funded\n if (queueShortfall == 0) {\n return 0;\n }\n\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // Of the claimable withdrawal requests, how much is unclaimed?\n // That is, the amount of WETH that is currently allocated for the withdrawal queue\n uint256 allocatedWeth = queue.claimable - queue.claimed;\n\n // If there is no unallocated WETH then there is nothing to add to the queue\n if (wethBalance <= allocatedWeth) {\n return 0;\n }\n\n uint256 unallocatedWeth = wethBalance - allocatedWeth;\n\n // the new claimable amount is the smaller of the queue shortfall or unallocated weth\n addedClaimable = queueShortfall < unallocatedWeth\n ? queueShortfall\n : unallocatedWeth;\n uint256 newClaimable = queue.claimable + addedClaimable;\n\n // Store the new claimable amount back to storage\n withdrawalQueueMetadata.claimable = SafeCast.toUint128(newClaimable);\n\n // emit a WithdrawalClaimable event\n emit WithdrawalClaimable(newClaimable, addedClaimable);\n }\n\n /***************************************\n View Functions\n ****************************************/\n\n /// @dev Calculate how much WETH in the vault is not reserved for the withdrawal queue.\n // That is, it is available to be redeemed or deposited into a strategy.\n function _wethAvailable() internal view returns (uint256 wethAvailable) {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n uint256 outstandingWithdrawals = queue.queued - queue.claimed;\n\n // The amount of sitting in WETH in the vault\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // If there is not enough WETH in the vault to cover the outstanding withdrawals\n if (wethBalance <= outstandingWithdrawals) {\n return 0;\n }\n\n return wethBalance - outstandingWithdrawals;\n }\n\n /// @dev Get the balance of an asset held in Vault and all strategies\n /// less any WETH that is reserved for the withdrawal queue.\n /// WETH is the only asset that can return a non-zero balance.\n /// All other assets will return 0 even if there is some dust amounts left in the Vault.\n /// For example, there is 1 wei left of stETH in the OETH Vault but will return 0 in this function.\n ///\n /// If there is not enough WETH in the vault and all strategies to cover all outstanding\n /// withdrawal requests then return a WETH balance of 0\n function _checkBalance(address _asset)\n internal\n view\n override\n returns (uint256 balance)\n {\n if (_asset != weth) {\n return 0;\n }\n\n // Get the WETH in the vault and the strategies\n balance = super._checkBalance(_asset);\n\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // If the vault becomes insolvent enough that the total value in the vault and all strategies\n // is less than the outstanding withdrawals.\n // For example, there was a mass slashing event and most users request a withdrawal.\n if (balance + queue.claimed < queue.queued) {\n return 0;\n }\n\n // Need to remove WETH that is reserved for the withdrawal queue\n return balance + queue.claimed - queue.queued;\n }\n\n /**\n * @notice Allocate unallocated funds on Vault to strategies.\n **/\n function allocate() external override whenNotCapitalPaused nonReentrant {\n // Add any unallocated WETH to the withdrawal queue first\n _addWithdrawalQueueLiquidity();\n\n _allocate();\n }\n\n /// @dev Allocate WETH to the default WETH strategy if there is excess to the Vault buffer.\n /// This is called from either `mint` or `allocate` and assumes `_addWithdrawalQueueLiquidity`\n /// has been called before this function.\n function _allocate() internal override {\n // No need to do anything if no default strategy for WETH\n address depositStrategyAddr = assetDefaultStrategies[weth];\n if (depositStrategyAddr == address(0)) return;\n\n uint256 wethAvailableInVault = _wethAvailable();\n // No need to do anything if there isn't any WETH in the vault to allocate\n if (wethAvailableInVault == 0) return;\n\n // Calculate the target buffer for the vault using the total supply\n uint256 totalSupply = oUSD.totalSupply();\n uint256 targetBuffer = totalSupply.mulTruncate(vaultBuffer);\n\n // If available WETH in the Vault is below or equal the target buffer then there's nothing to allocate\n if (wethAvailableInVault <= targetBuffer) return;\n\n // The amount of assets to allocate to the default strategy\n uint256 allocateAmount = wethAvailableInVault - targetBuffer;\n\n IStrategy strategy = IStrategy(depositStrategyAddr);\n // Transfer WETH to the strategy and call the strategy's deposit function\n IERC20(weth).safeTransfer(address(strategy), allocateAmount);\n strategy.deposit(weth, allocateAmount);\n\n emit AssetAllocated(weth, depositStrategyAddr, allocateAmount);\n }\n\n /// @dev The total value of all WETH held by the vault and all its strategies\n /// less any WETH that is reserved for the withdrawal queue.\n ///\n // If there is not enough WETH in the vault and all strategies to cover all outstanding\n // withdrawal requests then return a total value of 0.\n function _totalValue() internal view override returns (uint256 value) {\n // As WETH is the only asset, just return the WETH balance\n return _checkBalance(weth);\n }\n\n /// @dev Only WETH is supported in the OETH Vault so return the WETH balance only\n /// Any ETH balances in the Vault will be ignored.\n /// Amounts from previously supported vault assets will also be ignored.\n /// For example, there is 1 wei left of stETH in the OETH Vault but is will be ignored.\n function _totalValueInVault()\n internal\n view\n override\n returns (uint256 value)\n {\n value = IERC20(weth).balanceOf(address(this));\n }\n}\n" + }, + "contracts/vault/OETHZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { ISfrxETH } from \"../interfaces/ISfrxETH.sol\";\n\ncontract OETHZapper {\n IERC20 public immutable oeth;\n IVault public immutable vault;\n\n IWETH9 public constant weth =\n IWETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);\n IERC20 public constant frxeth =\n IERC20(0x5E8422345238F34275888049021821E8E08CAa1f);\n ISfrxETH public constant sfrxeth =\n ISfrxETH(0xac3E018457B222d93114458476f3E3416Abbe38F);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(address _oeth, address _vault) {\n oeth = IERC20(_oeth);\n vault = IVault(_vault);\n\n weth.approve(address(_vault), type(uint256).max);\n frxeth.approve(address(_vault), type(uint256).max);\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return.\n * Will verify that the user is sent 1:1 for ETH.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return\n * Will verify that the user is sent 1:1 for ETH.\n * @return Amount of OETH sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n weth.deposit{ value: balance }();\n emit Zap(msg.sender, ETH_MARKER, balance);\n return _mint(address(weth), balance);\n }\n\n /**\n * @dev Deposit SFRXETH to the vault and receive OETH in return\n * @param amount Amount of SFRXETH to deposit\n * @param minOETH Minimum amount of OETH to receive\n * @return Amount of OETH sent to user\n */\n function depositSFRXETH(uint256 amount, uint256 minOETH)\n external\n returns (uint256)\n {\n sfrxeth.redeem(amount, address(this), msg.sender);\n emit Zap(msg.sender, address(sfrxeth), amount);\n return _mint(address(frxeth), minOETH);\n }\n\n /**\n * @dev Internal function to mint OETH from an asset\n * @param asset Address of asset for the vault to mint from\n * @param minOETH Minimum amount of OETH to for user to receive\n * @return Amount of OETH sent to user\n */\n function _mint(address asset, uint256 minOETH) internal returns (uint256) {\n uint256 toMint = IERC20(asset).balanceOf(address(this));\n vault.mint(asset, toMint, minOETH);\n uint256 mintedAmount = oeth.balanceOf(address(this));\n require(mintedAmount >= minOETH, \"Zapper: not enough minted\");\n require(oeth.transfer(msg.sender, mintedAmount));\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/OSonicVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"./OETHVaultAdmin.sol\";\n\n/**\n * @title Origin Sonic VaultAdmin contract on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonicVaultAdmin is OETHVaultAdmin {\n /// @param _wS Sonic's Wrapped S token\n constructor(address _wS) OETHVaultAdmin(_wS) {}\n\n /***************************************\n Asset Config\n ****************************************/\n\n /**\n * @notice Add a supported asset to the contract, i.e. one that can be to mint OTokens.\n * @dev Overridden to remove price provider integration\n * @param _asset Address of asset\n * @param _unitConversion 0 decimals, 1 exchange rate\n */\n function supportAsset(address _asset, uint8 _unitConversion)\n external\n override\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Asset already supported\");\n\n assets[_asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(_unitConversion),\n decimals: 0, // will be overridden in _cacheDecimals\n allowedOracleSlippageBps: 0 // 0% by default\n });\n\n _cacheDecimals(_asset);\n allAssets.push(_asset);\n\n emit AssetSupported(_asset);\n }\n}\n" + }, + "contracts/vault/OSonicVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultCore } from \"./OETHVaultCore.sol\";\n\n/**\n * @title Origin Sonic VaultCore contract on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonicVaultCore is OETHVaultCore {\n /// @param _wS Sonic's Wrapped S token\n constructor(address _wS) OETHVaultCore(_wS) {}\n\n /**\n * @notice Instant redeem is not supported on Sonic.\n * Use the asynchronous `requestWithdrawal` a `claimWithdrawal` instead.\n */\n function _redeem(uint256, uint256) internal override {\n revert(\"unsupported function\");\n }\n}\n" + }, + "contracts/vault/OSonicZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWrappedSonic } from \"../interfaces/sonic/IWrappedSonic.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\n\n/**\n * @title Zapper for Origin Sonic (OS) tokens\n * @author Origin Protocol Inc\n */\ncontract OSonicZapper {\n IERC20 public immutable OS;\n IERC4626 public immutable wOS;\n IVault public immutable vault;\n\n IWrappedSonic public constant wS =\n IWrappedSonic(0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(\n address _OS,\n address _wOS,\n address _vault\n ) {\n OS = IERC20(_OS);\n wOS = IERC4626(_wOS);\n vault = IVault(_vault);\n\n wS.approve(address(_vault), type(uint256).max);\n IERC20(_OS).approve(_wOS, type(uint256).max);\n }\n\n /**\n * @dev Deposit native S currency and receive Origin Sonic (OS) tokens in return.\n * Will verify that the user is sent 1:1 for S.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit native S currency and receive Origin Sonic (OS) tokens in return.\n * Will verify that the user is sent 1:1 for S.\n * @return Amount of Origin Sonic (OS) tokens sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap native S\n wS.deposit{ value: balance }();\n\n // Mint Origin Sonic (OS) with Wrapped Sonic (wS)\n return _mint(balance, msg.sender);\n }\n\n /**\n * @dev Deposit S and receive Wrapped Origin Sonic (wOS) in return\n * @param minReceived min amount of Wrapped Origin Sonic (wOS) to receive\n * @return Amount of Wrapped Origin Sonic (wOS) tokens sent to user\n */\n function depositSForWrappedTokens(uint256 minReceived)\n external\n payable\n returns (uint256)\n {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap S\n wS.deposit{ value: balance }();\n\n // Mint with Wrapped Sonic\n uint256 mintOS = _mint(balance, address(this));\n\n // Wrap Origin Sonic (OS) into Wrapped Origin Sonic (wOS)\n uint256 mintedWOS = wOS.deposit(mintOS, msg.sender);\n\n require(mintedWOS >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWOS;\n }\n\n /**\n * @dev Deposit Wrapped Sonic (wS) tokens and receive Wrapped Origin Sonic (wOS) tokens in return\n * @param wSAmount Amount of Wrapped Sonic (wS) to deposit\n * @param minReceived min amount of Wrapped Origin Sonic (wOS) token to receive\n * @return Amount of Wrapped Origin Sonic (wOS) tokens sent to user\n */\n function depositWSForWrappedTokens(uint256 wSAmount, uint256 minReceived)\n external\n returns (uint256)\n {\n // slither-disable-next-line unchecked-transfer unused-return\n wS.transferFrom(msg.sender, address(this), wSAmount);\n\n emit Zap(msg.sender, address(wS), wSAmount);\n\n // Mint with Wrapped Sonic (wS)\n uint256 mintedOS = _mint(wSAmount, address(this));\n\n // Wrap Origin Sonic (OS) tokens into Wrapped Origin Sonic (wOS) tokens\n uint256 mintedWOS = wOS.deposit(mintedOS, msg.sender);\n\n require(mintedWOS >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWOS;\n }\n\n /**\n * @dev Internal function to mint Origin Sonic (OS) with Wrapped S (wS)\n * @param minOS Minimum amount of Origin Sonic (OS) tokens the user can receive\n * @param recipient Address that receives the tokens\n * @return Amount of Origin Sonic (OS) tokens sent to the recipient\n */\n function _mint(uint256 minOS, address recipient)\n internal\n returns (uint256)\n {\n uint256 toMint = wS.balanceOf(address(this));\n vault.mint(address(wS), toMint, minOS);\n uint256 mintedAmount = OS.balanceOf(address(this));\n require(mintedAmount >= minOS, \"Zapper: not enough minted\");\n\n if (recipient != address(this)) {\n require(OS.transfer(recipient, mintedAmount));\n }\n\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/Vault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD VaultInitializer Contract\n * @notice The VaultInitializer sets up the initial contract.\n * @author Origin Protocol Inc\n */\nimport { VaultInitializer } from \"./VaultInitializer.sol\";\nimport { VaultAdmin } from \"./VaultAdmin.sol\";\n\ncontract Vault is VaultInitializer, VaultAdmin {}\n" + }, + "contracts/vault/VaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultAdmin contract\n * @notice The VaultAdmin contract makes configuration and admin calls on the vault.\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport \"./VaultStorage.sol\";\n\ncontract VaultAdmin is VaultStorage {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /***************************************\n Configuration\n ****************************************/\n\n /**\n * @notice Set address of price provider.\n * @param _priceProvider Address of price provider\n */\n function setPriceProvider(address _priceProvider) external onlyGovernor {\n priceProvider = _priceProvider;\n emit PriceProviderUpdated(_priceProvider);\n }\n\n /**\n * @notice Set a fee in basis points to be charged for a redeem.\n * @param _redeemFeeBps Basis point fee to be charged\n */\n function setRedeemFeeBps(uint256 _redeemFeeBps) external onlyGovernor {\n require(_redeemFeeBps <= 1000, \"Redeem fee should not be over 10%\");\n redeemFeeBps = _redeemFeeBps;\n emit RedeemFeeUpdated(_redeemFeeBps);\n }\n\n /**\n * @notice Set a buffer of assets to keep in the Vault to handle most\n * redemptions without needing to spend gas unwinding assets from a Strategy.\n * @param _vaultBuffer Percentage using 18 decimals. 100% = 1e18.\n */\n function setVaultBuffer(uint256 _vaultBuffer)\n external\n onlyGovernorOrStrategist\n {\n require(_vaultBuffer <= 1e18, \"Invalid value\");\n vaultBuffer = _vaultBuffer;\n emit VaultBufferUpdated(_vaultBuffer);\n }\n\n /**\n * @notice Sets the minimum amount of OTokens in a mint to trigger an\n * automatic allocation of funds afterwords.\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setAutoAllocateThreshold(uint256 _threshold)\n external\n onlyGovernor\n {\n autoAllocateThreshold = _threshold;\n emit AllocateThresholdUpdated(_threshold);\n }\n\n /**\n * @notice Set a minimum amount of OTokens in a mint or redeem that triggers a\n * rebase\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setRebaseThreshold(uint256 _threshold) external onlyGovernor {\n rebaseThreshold = _threshold;\n emit RebaseThresholdUpdated(_threshold);\n }\n\n /**\n * @notice Set address of Strategist\n * @param _address Address of Strategist\n */\n function setStrategistAddr(address _address) external onlyGovernor {\n strategistAddr = _address;\n emit StrategistUpdated(_address);\n }\n\n /**\n * @notice Set the default Strategy for an asset, i.e. the one which the asset\n will be automatically allocated to and withdrawn from\n * @param _asset Address of the asset\n * @param _strategy Address of the Strategy\n */\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external\n onlyGovernorOrStrategist\n {\n emit AssetDefaultStrategyUpdated(_asset, _strategy);\n // If its a zero address being passed for the strategy we are removing\n // the default strategy\n if (_strategy != address(0)) {\n // Make sure the strategy meets some criteria\n require(strategies[_strategy].isSupported, \"Strategy not approved\");\n IStrategy strategy = IStrategy(_strategy);\n require(assets[_asset].isSupported, \"Asset is not supported\");\n require(\n strategy.supportsAsset(_asset),\n \"Asset not supported by Strategy\"\n );\n }\n assetDefaultStrategies[_asset] = _strategy;\n }\n\n /**\n * @notice Set maximum amount of OTokens that can at any point be minted and deployed\n * to strategy (used only by ConvexOUSDMetaStrategy for now).\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setNetOusdMintForStrategyThreshold(uint256 _threshold)\n external\n onlyGovernor\n {\n /**\n * Because `netOusdMintedForStrategy` check in vault core works both ways\n * (positive and negative) the actual impact of the amount of OToken minted\n * could be double the threshold. E.g.:\n * - contract has threshold set to 100\n * - state of netOusdMinted is -90\n * - in effect it can mint 190 OToken and still be within limits\n *\n * We are somewhat mitigating this behaviour by resetting the netOusdMinted\n * counter whenever new threshold is set. So it can only move one threshold\n * amount in each direction. This also enables us to reduce the threshold\n * amount and not have problems with current netOusdMinted being near\n * limits on either side.\n */\n netOusdMintedForStrategy = 0;\n netOusdMintForStrategyThreshold = _threshold;\n emit NetOusdMintForStrategyThresholdChanged(_threshold);\n }\n\n /**\n * @notice Changes the async withdrawal claim period for OETH & superOETHb\n * @param _delay Delay period (should be between 10 mins to 7 days).\n * Set to 0 to disable async withdrawals\n */\n function setWithdrawalClaimDelay(uint256 _delay) external onlyGovernor {\n require(\n _delay == 0 || (_delay >= 10 minutes && _delay <= 15 days),\n \"Invalid claim delay period\"\n );\n withdrawalClaimDelay = _delay;\n emit WithdrawalClaimDelayUpdated(_delay);\n }\n\n /**\n * @notice Set a yield streaming max rate. This spreads yield over\n * time if it is above the max rate.\n * @param yearlyApr in 1e18 notation. 3 * 1e18 = 3% APR\n */\n function setRebaseRateMax(uint256 yearlyApr)\n external\n onlyGovernorOrStrategist\n {\n // The old yield will be at the old rate\n IVault(address(this)).rebase();\n // Change the rate\n uint256 newPerSecond = yearlyApr / 100 / 365 days;\n require(newPerSecond <= MAX_REBASE_PER_SECOND, \"Rate too high\");\n rebasePerSecondMax = newPerSecond.toUint64();\n emit RebasePerSecondMaxChanged(newPerSecond);\n }\n\n /**\n * @notice Set the drip duration period\n * @param _dripDuration Time in seconds to target a constant yield rate\n */\n function setDripDuration(uint256 _dripDuration)\n external\n onlyGovernorOrStrategist\n {\n // The old yield will be at the old rate\n IVault(address(this)).rebase();\n dripDuration = _dripDuration.toUint64();\n emit DripDurationChanged(_dripDuration);\n }\n\n /***************************************\n Swaps\n ****************************************/\n\n /**\n * @notice Strategist swaps collateral assets sitting in the vault.\n * @param _fromAsset The token address of the asset being sold by the vault.\n * @param _toAsset The token address of the asset being purchased by the vault.\n * @param _fromAssetAmount The amount of assets being sold by the vault.\n * @param _minToAssetAmount The minimum amount of assets to be purchased.\n * @param _data implementation specific data. eg 1Inch swap data\n * @return toAssetAmount The amount of toAssets that was received from the swap\n */\n function swapCollateral(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n )\n external\n nonReentrant\n onlyGovernorOrStrategist\n returns (uint256 toAssetAmount)\n {\n toAssetAmount = _swapCollateral(\n _fromAsset,\n _toAsset,\n _fromAssetAmount,\n _minToAssetAmount,\n _data\n );\n }\n\n function _swapCollateral(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n ) internal virtual returns (uint256 toAssetAmount) {\n // Check fromAsset and toAsset are valid\n Asset memory fromAssetConfig = assets[_fromAsset];\n Asset memory toAssetConfig = assets[_toAsset];\n require(fromAssetConfig.isSupported, \"From asset is not supported\");\n require(toAssetConfig.isSupported, \"To asset is not supported\");\n\n // Load swap config into memory to avoid separate SLOADs\n SwapConfig memory config = swapConfig;\n\n // Scope a new block to remove toAssetBalBefore from the scope of swapCollateral.\n // This avoids a stack too deep error.\n {\n uint256 toAssetBalBefore = IERC20(_toAsset).balanceOf(\n address(this)\n );\n\n // Transfer from assets to the swapper contract\n IERC20(_fromAsset).safeTransfer(config.swapper, _fromAssetAmount);\n\n // Call to the Swapper contract to do the actual swap\n // The -1 is required for stETH which sometimes transfers 1 wei less than what was specified.\n // slither-disable-next-line unused-return\n ISwapper(config.swapper).swap(\n _fromAsset,\n _toAsset,\n _fromAssetAmount - 1,\n _minToAssetAmount,\n _data\n );\n\n // Compute the change in asset balance held by the Vault\n toAssetAmount =\n IERC20(_toAsset).balanceOf(address(this)) -\n toAssetBalBefore;\n }\n\n // Check the to assets returned is above slippage amount specified by the strategist\n require(\n toAssetAmount >= _minToAssetAmount,\n \"Strategist slippage limit\"\n );\n\n // Scope a new block to remove minOracleToAssetAmount from the scope of swapCollateral.\n // This avoids a stack too deep error.\n {\n // Check the slippage against the Oracle in case the strategist made a mistake or has become malicious.\n // to asset amount = from asset amount * from asset price / to asset price\n uint256 minOracleToAssetAmount = (_fromAssetAmount *\n (1e4 - fromAssetConfig.allowedOracleSlippageBps) *\n IOracle(priceProvider).price(_fromAsset)) /\n (IOracle(priceProvider).price(_toAsset) *\n (1e4 + toAssetConfig.allowedOracleSlippageBps));\n\n // Scale both sides up to 18 decimals to compare\n require(\n toAssetAmount.scaleBy(18, toAssetConfig.decimals) >=\n minOracleToAssetAmount.scaleBy(\n 18,\n fromAssetConfig.decimals\n ),\n \"Oracle slippage limit exceeded\"\n );\n }\n\n // Check the vault's total value hasn't gone below the OToken total supply\n // by more than the allowed percentage.\n require(\n IVault(address(this)).totalValue() >=\n (oUSD.totalSupply() * ((1e4 - config.allowedUndervalueBps))) /\n 1e4,\n \"Allowed value < supply\"\n );\n\n emit Swapped(_fromAsset, _toAsset, _fromAssetAmount, toAssetAmount);\n }\n\n /***************************************\n Swap Config\n ****************************************/\n\n /**\n * @notice Set the contract the performs swaps of collateral assets.\n * @param _swapperAddr Address of the Swapper contract that implements the ISwapper interface.\n */\n function setSwapper(address _swapperAddr) external onlyGovernor {\n swapConfig.swapper = _swapperAddr;\n emit SwapperChanged(_swapperAddr);\n }\n\n /// @notice Contract that swaps the vault's collateral assets\n function swapper() external view returns (address swapper_) {\n swapper_ = swapConfig.swapper;\n }\n\n /**\n * @notice Set max allowed percentage the vault total value can drop below the OToken total supply in basis points\n * when executing collateral swaps.\n * @param _basis Percentage in basis points. eg 100 == 1%\n */\n function setSwapAllowedUndervalue(uint16 _basis) external onlyGovernor {\n require(_basis < 10001, \"Invalid basis points\");\n swapConfig.allowedUndervalueBps = _basis;\n emit SwapAllowedUndervalueChanged(_basis);\n }\n\n /**\n * @notice Max allowed percentage the vault total value can drop below the OToken total supply in basis points\n * when executing a collateral swap.\n * For example 100 == 1%\n * @return value Percentage in basis points.\n */\n function allowedSwapUndervalue() external view returns (uint256 value) {\n value = swapConfig.allowedUndervalueBps;\n }\n\n /**\n * @notice Set the allowed slippage from the Oracle price for collateral asset swaps.\n * @param _asset Address of the asset token.\n * @param _allowedOracleSlippageBps allowed slippage from Oracle in basis points. eg 20 = 0.2%. Max 10%.\n */\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external\n onlyGovernor\n {\n require(assets[_asset].isSupported, \"Asset not supported\");\n require(_allowedOracleSlippageBps < 1000, \"Slippage too high\");\n\n assets[_asset].allowedOracleSlippageBps = _allowedOracleSlippageBps;\n\n emit SwapSlippageChanged(_asset, _allowedOracleSlippageBps);\n }\n\n /***************************************\n Asset Config\n ****************************************/\n\n /**\n * @notice Add a supported asset to the contract, i.e. one that can be\n * to mint OTokens.\n * @param _asset Address of asset\n */\n function supportAsset(address _asset, uint8 _unitConversion)\n external\n virtual\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Asset already supported\");\n\n assets[_asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(_unitConversion),\n decimals: 0, // will be overridden in _cacheDecimals\n allowedOracleSlippageBps: 0 // 0% by default\n });\n\n _cacheDecimals(_asset);\n allAssets.push(_asset);\n\n // Verify that our oracle supports the asset\n // slither-disable-next-line unused-return\n IOracle(priceProvider).price(_asset);\n\n emit AssetSupported(_asset);\n }\n\n /**\n * @notice Remove a supported asset from the Vault\n * @param _asset Address of asset\n */\n function removeAsset(address _asset) external onlyGovernor {\n require(assets[_asset].isSupported, \"Asset not supported\");\n\n // 1e13 for 18 decimals. And 10 for 6 decimals\n uint256 maxDustBalance = uint256(1e13).scaleBy(\n assets[_asset].decimals,\n 18\n );\n\n require(\n IVault(address(this)).checkBalance(_asset) <= maxDustBalance,\n \"Vault still holds asset\"\n );\n\n uint256 assetsCount = allAssets.length;\n uint256 assetIndex = assetsCount; // initialize at invalid index\n for (uint256 i = 0; i < assetsCount; ++i) {\n if (allAssets[i] == _asset) {\n assetIndex = i;\n break;\n }\n }\n\n // Note: If asset is not found in `allAssets`, the following line\n // will revert with an out-of-bound error. However, there's no\n // reason why an asset would have `Asset.isSupported = true` but\n // not exist in `allAssets`.\n\n // Update allAssets array\n allAssets[assetIndex] = allAssets[assetsCount - 1];\n allAssets.pop();\n\n // Reset default strategy\n assetDefaultStrategies[_asset] = address(0);\n emit AssetDefaultStrategyUpdated(_asset, address(0));\n\n // Remove asset from storage\n delete assets[_asset];\n\n emit AssetRemoved(_asset);\n }\n\n /**\n * @notice Cache decimals on OracleRouter for a particular asset. This action\n * is required before that asset's price can be accessed.\n * @param _asset Address of asset token\n */\n function cacheDecimals(address _asset) external onlyGovernor {\n _cacheDecimals(_asset);\n }\n\n /***************************************\n Strategy Config\n ****************************************/\n\n /**\n * @notice Add a strategy to the Vault.\n * @param _addr Address of the strategy to add\n */\n function approveStrategy(address _addr) external onlyGovernor {\n require(!strategies[_addr].isSupported, \"Strategy already approved\");\n strategies[_addr] = Strategy({ isSupported: true, _deprecated: 0 });\n allStrategies.push(_addr);\n emit StrategyApproved(_addr);\n }\n\n /**\n * @notice Remove a strategy from the Vault.\n * @param _addr Address of the strategy to remove\n */\n\n function removeStrategy(address _addr) external onlyGovernor {\n require(strategies[_addr].isSupported, \"Strategy not approved\");\n\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n require(\n assetDefaultStrategies[allAssets[i]] != _addr,\n \"Strategy is default for an asset\"\n );\n }\n\n // Initialize strategyIndex with out of bounds result so function will\n // revert if no valid index found\n uint256 stratCount = allStrategies.length;\n uint256 strategyIndex = stratCount;\n for (uint256 i = 0; i < stratCount; ++i) {\n if (allStrategies[i] == _addr) {\n strategyIndex = i;\n break;\n }\n }\n\n if (strategyIndex < stratCount) {\n allStrategies[strategyIndex] = allStrategies[stratCount - 1];\n allStrategies.pop();\n\n // Mark the strategy as not supported\n strategies[_addr].isSupported = false;\n\n // Withdraw all assets\n IStrategy strategy = IStrategy(_addr);\n strategy.withdrawAll();\n\n emit StrategyRemoved(_addr);\n }\n }\n\n /***************************************\n Strategies\n ****************************************/\n\n /**\n * @notice Deposit multiple assets from the vault into the strategy.\n * @param _strategyToAddress Address of the Strategy to deposit assets into.\n * @param _assets Array of asset address that will be deposited into the strategy.\n * @param _amounts Array of amounts of each corresponding asset to deposit.\n */\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external onlyGovernorOrStrategist nonReentrant {\n _depositToStrategy(_strategyToAddress, _assets, _amounts);\n }\n\n function _depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal virtual {\n require(\n strategies[_strategyToAddress].isSupported,\n \"Invalid to Strategy\"\n );\n require(_assets.length == _amounts.length, \"Parameter length mismatch\");\n\n uint256 assetCount = _assets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n address assetAddr = _assets[i];\n require(\n IStrategy(_strategyToAddress).supportsAsset(assetAddr),\n \"Asset unsupported\"\n );\n // Send required amount of funds to the strategy\n IERC20(assetAddr).safeTransfer(_strategyToAddress, _amounts[i]);\n }\n\n // Deposit all the funds that have been sent to the strategy\n IStrategy(_strategyToAddress).depositAll();\n }\n\n /**\n * @notice Withdraw multiple assets from the strategy to the vault.\n * @param _strategyFromAddress Address of the Strategy to withdraw assets from.\n * @param _assets Array of asset address that will be withdrawn from the strategy.\n * @param _amounts Array of amounts of each corresponding asset to withdraw.\n */\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external onlyGovernorOrStrategist nonReentrant {\n _withdrawFromStrategy(\n address(this),\n _strategyFromAddress,\n _assets,\n _amounts\n );\n }\n\n /**\n * @param _recipient can either be a strategy or the Vault\n */\n function _withdrawFromStrategy(\n address _recipient,\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal virtual {\n require(\n strategies[_strategyFromAddress].isSupported,\n \"Invalid from Strategy\"\n );\n require(_assets.length == _amounts.length, \"Parameter length mismatch\");\n\n uint256 assetCount = _assets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n // Withdraw from Strategy to the recipient\n IStrategy(_strategyFromAddress).withdraw(\n _recipient,\n _assets[i],\n _amounts[i]\n );\n }\n }\n\n /**\n * @notice Sets the maximum allowable difference between\n * total supply and backing assets' value.\n */\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external onlyGovernor {\n maxSupplyDiff = _maxSupplyDiff;\n emit MaxSupplyDiffChanged(_maxSupplyDiff);\n }\n\n /**\n * @notice Sets the trusteeAddress that can receive a portion of yield.\n * Setting to the zero address disables this feature.\n */\n function setTrusteeAddress(address _address) external onlyGovernor {\n trusteeAddress = _address;\n emit TrusteeAddressChanged(_address);\n }\n\n /**\n * @notice Sets the TrusteeFeeBps to the percentage of yield that should be\n * received in basis points.\n */\n function setTrusteeFeeBps(uint256 _basis) external onlyGovernor {\n require(_basis <= 5000, \"basis cannot exceed 50%\");\n trusteeFeeBps = _basis;\n emit TrusteeFeeBpsChanged(_basis);\n }\n\n /**\n * @notice Set OToken Metapool strategy\n * @param _ousdMetaStrategy Address of OToken metapool strategy\n */\n function setOusdMetaStrategy(address _ousdMetaStrategy)\n external\n onlyGovernor\n {\n ousdMetaStrategy = _ousdMetaStrategy;\n emit OusdMetaStrategyUpdated(_ousdMetaStrategy);\n }\n\n /***************************************\n Pause\n ****************************************/\n\n /**\n * @notice Set the deposit paused flag to true to prevent rebasing.\n */\n function pauseRebase() external onlyGovernorOrStrategist {\n rebasePaused = true;\n emit RebasePaused();\n }\n\n /**\n * @notice Set the deposit paused flag to true to allow rebasing.\n */\n function unpauseRebase() external onlyGovernorOrStrategist {\n rebasePaused = false;\n emit RebaseUnpaused();\n }\n\n /**\n * @notice Set the deposit paused flag to true to prevent capital movement.\n */\n function pauseCapital() external onlyGovernorOrStrategist {\n capitalPaused = true;\n emit CapitalPaused();\n }\n\n /**\n * @notice Set the deposit paused flag to false to enable capital movement.\n */\n function unpauseCapital() external onlyGovernorOrStrategist {\n capitalPaused = false;\n emit CapitalUnpaused();\n }\n\n /***************************************\n Utils\n ****************************************/\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Only unsupported assets\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /***************************************\n Strategies Admin\n ****************************************/\n\n /**\n * @notice Withdraws all assets from the strategy and sends assets to the Vault.\n * @param _strategyAddr Strategy address.\n */\n function withdrawAllFromStrategy(address _strategyAddr)\n external\n onlyGovernorOrStrategist\n {\n _withdrawAllFromStrategy(_strategyAddr);\n }\n\n function _withdrawAllFromStrategy(address _strategyAddr) internal virtual {\n require(\n strategies[_strategyAddr].isSupported,\n \"Strategy is not supported\"\n );\n IStrategy strategy = IStrategy(_strategyAddr);\n strategy.withdrawAll();\n }\n\n /**\n * @notice Withdraws all assets from all the strategies and sends assets to the Vault.\n */\n function withdrawAllFromStrategies() external onlyGovernorOrStrategist {\n _withdrawAllFromStrategies();\n }\n\n function _withdrawAllFromStrategies() internal virtual {\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n IStrategy(allStrategies[i]).withdrawAll();\n }\n }\n\n /***************************************\n Utils\n ****************************************/\n\n function _cacheDecimals(address token) internal {\n Asset storage tokenAsset = assets[token];\n if (tokenAsset.decimals != 0) {\n return;\n }\n uint8 decimals = IBasicToken(token).decimals();\n require(decimals >= 6 && decimals <= 18, \"Unexpected precision\");\n tokenAsset.decimals = decimals;\n }\n}\n" + }, + "contracts/vault/VaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultCore contract\n * @notice The Vault contract stores assets. On a deposit, OTokens will be minted\n and sent to the depositor. On a withdrawal, OTokens will be burned and\n assets will be sent to the withdrawer. The Vault accepts deposits of\n interest from yield bearing strategies which will modify the supply\n of OTokens.\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IGetExchangeRateToken } from \"../interfaces/IGetExchangeRateToken.sol\";\n\nimport \"./VaultInitializer.sol\";\n\ncontract VaultCore is VaultInitializer {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n /// @dev max signed int\n uint256 internal constant MAX_INT = uint256(type(int256).max);\n\n /**\n * @dev Verifies that the rebasing is not paused.\n */\n modifier whenNotRebasePaused() {\n require(!rebasePaused, \"Rebasing paused\");\n _;\n }\n\n /**\n * @dev Verifies that the deposits are not paused.\n */\n modifier whenNotCapitalPaused() {\n require(!capitalPaused, \"Capital paused\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the AMO strategy.\n */\n modifier onlyOusdMetaStrategy() {\n require(\n msg.sender == ousdMetaStrategy,\n \"Caller is not the OUSD meta strategy\"\n );\n _;\n }\n\n /**\n * @notice Deposit a supported asset and mint OTokens.\n * @param _asset Address of the asset being deposited\n * @param _amount Amount of the asset being deposited\n * @param _minimumOusdAmount Minimum OTokens to mint\n */\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external whenNotCapitalPaused nonReentrant {\n _mint(_asset, _amount, _minimumOusdAmount);\n }\n\n /**\n * @dev Deposit a supported asset and mint OTokens.\n * @param _asset Address of the asset being deposited\n * @param _amount Amount of the asset being deposited\n * @param _minimumOusdAmount Minimum OTokens to mint\n */\n function _mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) internal virtual {\n require(assets[_asset].isSupported, \"Asset is not supported\");\n require(_amount > 0, \"Amount must be greater than 0\");\n\n uint256 units = _toUnits(_amount, _asset);\n uint256 unitPrice = _toUnitPrice(_asset, true);\n uint256 priceAdjustedDeposit = (units * unitPrice) / 1e18;\n\n if (_minimumOusdAmount > 0) {\n require(\n priceAdjustedDeposit >= _minimumOusdAmount,\n \"Mint amount lower than minimum\"\n );\n }\n\n emit Mint(msg.sender, priceAdjustedDeposit);\n\n // Rebase must happen before any transfers occur.\n if (priceAdjustedDeposit >= rebaseThreshold && !rebasePaused) {\n _rebase();\n }\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, priceAdjustedDeposit);\n\n // Transfer the deposited coins to the vault\n IERC20 asset = IERC20(_asset);\n asset.safeTransferFrom(msg.sender, address(this), _amount);\n\n if (priceAdjustedDeposit >= autoAllocateThreshold) {\n _allocate();\n }\n }\n\n /**\n * @notice Mint OTokens for a Metapool Strategy\n * @param _amount Amount of the asset being deposited\n *\n * Notice: can't use `nonReentrant` modifier since the `mint` function can\n * call `allocate`, and that can trigger `ConvexOUSDMetaStrategy` to call this function\n * while the execution of the `mint` has not yet completed -> causing a `nonReentrant` collision.\n *\n * Also important to understand is that this is a limitation imposed by the test suite.\n * Production / mainnet contracts should never be configured in a way where mint/redeem functions\n * that are moving funds between the Vault and end user wallets can influence strategies\n * utilizing this function.\n */\n function mintForStrategy(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n onlyOusdMetaStrategy\n {\n require(_amount < MAX_INT, \"Amount too high\");\n\n emit Mint(msg.sender, _amount);\n\n // safe to cast because of the require check at the beginning of the function\n netOusdMintedForStrategy += int256(_amount);\n\n require(\n abs(netOusdMintedForStrategy) < netOusdMintForStrategyThreshold,\n \"Minted ousd surpassed netOusdMintForStrategyThreshold.\"\n );\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, _amount);\n }\n\n // In memoriam\n\n /**\n * @notice Withdraw a supported asset and burn OTokens.\n * @param _amount Amount of OTokens to burn\n * @param _minimumUnitAmount Minimum stablecoin units to receive in return\n */\n function redeem(uint256 _amount, uint256 _minimumUnitAmount)\n external\n whenNotCapitalPaused\n nonReentrant\n {\n _redeem(_amount, _minimumUnitAmount);\n }\n\n /**\n * @notice Withdraw a supported asset and burn OTokens.\n * @param _amount Amount of OTokens to burn\n * @param _minimumUnitAmount Minimum stablecoin units to receive in return\n */\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n {\n // Calculate redemption outputs\n uint256[] memory outputs = _calculateRedeemOutputs(_amount);\n\n emit Redeem(msg.sender, _amount);\n\n // Send outputs\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n if (outputs[i] == 0) continue;\n\n address assetAddr = allAssets[i];\n\n if (IERC20(assetAddr).balanceOf(address(this)) >= outputs[i]) {\n // Use Vault funds first if sufficient\n IERC20(assetAddr).safeTransfer(msg.sender, outputs[i]);\n } else {\n address strategyAddr = assetDefaultStrategies[assetAddr];\n if (strategyAddr != address(0)) {\n // Nothing in Vault, but something in Strategy, send from there\n IStrategy strategy = IStrategy(strategyAddr);\n strategy.withdraw(msg.sender, assetAddr, outputs[i]);\n } else {\n // Cant find funds anywhere\n revert(\"Liquidity error\");\n }\n }\n }\n\n if (_minimumUnitAmount > 0) {\n uint256 unitTotal = 0;\n for (uint256 i = 0; i < outputs.length; ++i) {\n unitTotal += _toUnits(outputs[i], allAssets[i]);\n }\n require(\n unitTotal >= _minimumUnitAmount,\n \"Redeem amount lower than minimum\"\n );\n }\n\n oUSD.burn(msg.sender, _amount);\n\n _postRedeem(_amount);\n }\n\n function _postRedeem(uint256 _amount) internal {\n // Until we can prove that we won't affect the prices of our assets\n // by withdrawing them, this should be here.\n // It's possible that a strategy was off on its asset total, perhaps\n // a reward token sold for more or for less than anticipated.\n uint256 totalUnits = 0;\n if (_amount >= rebaseThreshold && !rebasePaused) {\n totalUnits = _rebase();\n } else {\n totalUnits = _totalValue();\n }\n\n // Check that the OTokens are backed by enough assets\n if (maxSupplyDiff > 0) {\n // If there are more outstanding withdrawal requests than assets in the vault and strategies\n // then the available assets will be negative and totalUnits will be rounded up to zero.\n // As we don't know the exact shortfall amount, we will reject all redeem and withdrawals\n require(totalUnits > 0, \"Too many outstanding requests\");\n\n // Allow a max difference of maxSupplyDiff% between\n // backing assets value and OUSD total supply\n uint256 diff = oUSD.totalSupply().divPrecisely(totalUnits);\n require(\n (diff > 1e18 ? diff - 1e18 : 1e18 - diff) <= maxSupplyDiff,\n \"Backing supply liquidity error\"\n );\n }\n }\n\n /**\n * @notice Burn OTokens for Metapool Strategy\n * @param _amount Amount of OUSD to burn\n *\n * @dev Notice: can't use `nonReentrant` modifier since the `redeem` function could\n * require withdrawal on `ConvexOUSDMetaStrategy` and that one can call `burnForStrategy`\n * while the execution of the `redeem` has not yet completed -> causing a `nonReentrant` collision.\n *\n * Also important to understand is that this is a limitation imposed by the test suite.\n * Production / mainnet contracts should never be configured in a way where mint/redeem functions\n * that are moving funds between the Vault and end user wallets can influence strategies\n * utilizing this function.\n */\n function burnForStrategy(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n onlyOusdMetaStrategy\n {\n require(_amount < MAX_INT, \"Amount too high\");\n\n emit Redeem(msg.sender, _amount);\n\n // safe to cast because of the require check at the beginning of the function\n netOusdMintedForStrategy -= int256(_amount);\n\n require(\n abs(netOusdMintedForStrategy) < netOusdMintForStrategyThreshold,\n \"Attempting to burn too much OUSD.\"\n );\n\n // Burn OTokens\n oUSD.burn(msg.sender, _amount);\n }\n\n /**\n * @notice Allocate unallocated funds on Vault to strategies.\n **/\n function allocate() external virtual whenNotCapitalPaused nonReentrant {\n _allocate();\n }\n\n /**\n * @dev Allocate unallocated funds on Vault to strategies.\n **/\n function _allocate() internal virtual {\n uint256 vaultValue = _totalValueInVault();\n // Nothing in vault to allocate\n if (vaultValue == 0) return;\n uint256 strategiesValue = _totalValueInStrategies();\n // We have a method that does the same as this, gas optimisation\n uint256 calculatedTotalValue = vaultValue + strategiesValue;\n\n // We want to maintain a buffer on the Vault so calculate a percentage\n // modifier to multiply each amount being allocated by to enforce the\n // vault buffer\n uint256 vaultBufferModifier;\n if (strategiesValue == 0) {\n // Nothing in Strategies, allocate 100% minus the vault buffer to\n // strategies\n vaultBufferModifier = uint256(1e18) - vaultBuffer;\n } else {\n vaultBufferModifier =\n (vaultBuffer * calculatedTotalValue) /\n vaultValue;\n if (1e18 > vaultBufferModifier) {\n // E.g. 1e18 - (1e17 * 10e18)/5e18 = 8e17\n // (5e18 * 8e17) / 1e18 = 4e18 allocated from Vault\n vaultBufferModifier = uint256(1e18) - vaultBufferModifier;\n } else {\n // We need to let the buffer fill\n return;\n }\n }\n if (vaultBufferModifier == 0) return;\n\n // Iterate over all assets in the Vault and allocate to the appropriate\n // strategy\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(allAssets[i]);\n uint256 assetBalance = asset.balanceOf(address(this));\n // No balance, nothing to do here\n if (assetBalance == 0) continue;\n\n // Multiply the balance by the vault buffer modifier and truncate\n // to the scale of the asset decimals\n uint256 allocateAmount = assetBalance.mulTruncate(\n vaultBufferModifier\n );\n\n address depositStrategyAddr = assetDefaultStrategies[\n address(asset)\n ];\n\n if (depositStrategyAddr != address(0) && allocateAmount > 0) {\n IStrategy strategy = IStrategy(depositStrategyAddr);\n // Transfer asset to Strategy and call deposit method to\n // mint or take required action\n asset.safeTransfer(address(strategy), allocateAmount);\n strategy.deposit(address(asset), allocateAmount);\n emit AssetAllocated(\n address(asset),\n depositStrategyAddr,\n allocateAmount\n );\n }\n }\n }\n\n /**\n * @notice Calculate the total value of assets held by the Vault and all\n * strategies and update the supply of OTokens.\n */\n function rebase() external virtual nonReentrant {\n _rebase();\n }\n\n /**\n * @dev Calculate the total value of assets held by the Vault and all\n * strategies and update the supply of OTokens, optionally sending a\n * portion of the yield to the trustee.\n * @return totalUnits Total balance of Vault in units\n */\n function _rebase() internal whenNotRebasePaused returns (uint256) {\n uint256 supply = oUSD.totalSupply();\n uint256 vaultValue = _totalValue();\n // If no supply yet, do not rebase\n if (supply == 0) {\n return vaultValue;\n }\n\n // Calculate yield and new supply\n (uint256 yield, uint256 targetRate) = _nextYield(supply, vaultValue);\n uint256 newSupply = supply + yield;\n // Only rebase upwards and if we have enough backing funds\n if (newSupply <= supply || newSupply > vaultValue) {\n return vaultValue;\n }\n\n rebasePerSecondTarget = uint64(_min(targetRate, type(uint64).max));\n lastRebase = uint64(block.timestamp); // Intentional cast\n\n // Fee collection on yield\n address _trusteeAddress = trusteeAddress; // gas savings\n uint256 fee = 0;\n if (_trusteeAddress != address(0)) {\n fee = (yield * trusteeFeeBps) / 1e4;\n if (fee > 0) {\n require(fee < yield, \"Fee must not be greater than yield\");\n oUSD.mint(_trusteeAddress, fee);\n }\n }\n emit YieldDistribution(_trusteeAddress, yield, fee);\n\n // Only ratchet OToken supply upwards\n // Final check uses latest totalSupply\n if (newSupply > oUSD.totalSupply()) {\n oUSD.changeSupply(newSupply);\n }\n return vaultValue;\n }\n\n /**\n * @notice Calculates the amount that would rebase at next rebase.\n * This is before any fees.\n * @return yield amount of expected yield\n */\n function previewYield() external view returns (uint256 yield) {\n (yield, ) = _nextYield(oUSD.totalSupply(), _totalValue());\n return yield;\n }\n\n function _nextYield(uint256 supply, uint256 vaultValue)\n internal\n view\n virtual\n returns (uint256 yield, uint256 targetRate)\n {\n uint256 nonRebasing = oUSD.nonRebasingSupply();\n uint256 rebasing = supply - nonRebasing;\n uint256 elapsed = block.timestamp - lastRebase;\n targetRate = rebasePerSecondTarget;\n\n if (\n elapsed == 0 || // Yield only once per block.\n rebasing == 0 || // No yield if there are no rebasing tokens to give it to.\n supply > vaultValue || // No yield if we do not have yield to give.\n block.timestamp >= type(uint64).max // No yield if we are too far in the future to calculate it correctly.\n ) {\n return (0, targetRate);\n }\n\n // Start with the full difference available\n yield = vaultValue - supply;\n\n // Cap via optional automatic duration smoothing\n uint256 _dripDuration = dripDuration;\n if (_dripDuration > 1) {\n // If we are able to sustain an increased drip rate for\n // double the duration, then increase the target drip rate\n targetRate = _max(targetRate, yield / (_dripDuration * 2));\n // If we cannot sustain the target rate any more,\n // then rebase what we can, and reduce the target\n targetRate = _min(targetRate, yield / _dripDuration);\n // drip at the new target rate\n yield = _min(yield, targetRate * elapsed);\n }\n\n // Cap per second. elapsed is not 1e18 denominated\n yield = _min(yield, (rebasing * elapsed * rebasePerSecondMax) / 1e18);\n\n // Cap at a hard max per rebase, to avoid long durations resulting in huge rebases\n yield = _min(yield, (rebasing * MAX_REBASE) / 1e18);\n\n return (yield, targetRate);\n }\n\n /**\n * @notice Determine the total value of assets held by the vault and its\n * strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function totalValue() external view virtual returns (uint256 value) {\n value = _totalValue();\n }\n\n /**\n * @dev Internal Calculate the total value of the assets held by the\n * vault and its strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValue() internal view virtual returns (uint256 value) {\n return _totalValueInVault() + _totalValueInStrategies();\n }\n\n /**\n * @dev Internal to calculate total value of all assets held in Vault.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInVault()\n internal\n view\n virtual\n returns (uint256 value)\n {\n uint256 assetCount = allAssets.length;\n for (uint256 y; y < assetCount; ++y) {\n address assetAddr = allAssets[y];\n uint256 balance = IERC20(assetAddr).balanceOf(address(this));\n if (balance > 0) {\n value += _toUnits(balance, assetAddr);\n }\n }\n }\n\n /**\n * @dev Internal to calculate total value of all assets held in Strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInStrategies() internal view returns (uint256 value) {\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n value = value + _totalValueInStrategy(allStrategies[i]);\n }\n }\n\n /**\n * @dev Internal to calculate total value of all assets held by strategy.\n * @param _strategyAddr Address of the strategy\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInStrategy(address _strategyAddr)\n internal\n view\n returns (uint256 value)\n {\n IStrategy strategy = IStrategy(_strategyAddr);\n uint256 assetCount = allAssets.length;\n for (uint256 y; y < assetCount; ++y) {\n address assetAddr = allAssets[y];\n if (strategy.supportsAsset(assetAddr)) {\n uint256 balance = strategy.checkBalance(assetAddr);\n if (balance > 0) {\n value += _toUnits(balance, assetAddr);\n }\n }\n }\n }\n\n /**\n * @notice Get the balance of an asset held in Vault and all strategies.\n * @param _asset Address of asset\n * @return uint256 Balance of asset in decimals of asset\n */\n function checkBalance(address _asset) external view returns (uint256) {\n return _checkBalance(_asset);\n }\n\n /**\n * @notice Get the balance of an asset held in Vault and all strategies.\n * @param _asset Address of asset\n * @return balance Balance of asset in decimals of asset\n */\n function _checkBalance(address _asset)\n internal\n view\n virtual\n returns (uint256 balance)\n {\n IERC20 asset = IERC20(_asset);\n balance = asset.balanceOf(address(this));\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n IStrategy strategy = IStrategy(allStrategies[i]);\n if (strategy.supportsAsset(_asset)) {\n balance = balance + strategy.checkBalance(_asset);\n }\n }\n }\n\n /**\n * @notice Calculate the outputs for a redeem function, i.e. the mix of\n * coins that will be returned\n */\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory)\n {\n return _calculateRedeemOutputs(_amount);\n }\n\n /**\n * @dev Calculate the outputs for a redeem function, i.e. the mix of\n * coins that will be returned.\n * @return outputs Array of amounts respective to the supported assets\n */\n function _calculateRedeemOutputs(uint256 _amount)\n internal\n view\n virtual\n returns (uint256[] memory outputs)\n {\n // We always give out coins in proportion to how many we have,\n // Now if all coins were the same value, this math would easy,\n // just take the percentage of each coin, and multiply by the\n // value to be given out. But if coins are worth more than $1,\n // then we would end up handing out too many coins. We need to\n // adjust by the total value of coins.\n //\n // To do this, we total up the value of our coins, by their\n // percentages. Then divide what we would otherwise give out by\n // this number.\n //\n // Let say we have 100 DAI at $1.06 and 200 USDT at $1.00.\n // So for every 1 DAI we give out, we'll be handing out 2 USDT\n // Our total output ratio is: 33% * 1.06 + 66% * 1.00 = 1.02\n //\n // So when calculating the output, we take the percentage of\n // each coin, times the desired output value, divided by the\n // totalOutputRatio.\n //\n // For example, withdrawing: 30 OUSD:\n // DAI 33% * 30 / 1.02 = 9.80 DAI\n // USDT = 66 % * 30 / 1.02 = 19.60 USDT\n //\n // Checking these numbers:\n // 9.80 DAI * 1.06 = $10.40\n // 19.60 USDT * 1.00 = $19.60\n //\n // And so the user gets $10.40 + $19.60 = $30 worth of value.\n\n uint256 assetCount = allAssets.length;\n uint256[] memory assetUnits = new uint256[](assetCount);\n uint256[] memory assetBalances = new uint256[](assetCount);\n outputs = new uint256[](assetCount);\n\n // Calculate redeem fee\n if (redeemFeeBps > 0) {\n uint256 redeemFee = _amount.mulTruncateScale(redeemFeeBps, 1e4);\n _amount = _amount - redeemFee;\n }\n\n // Calculate assets balances and decimals once,\n // for a large gas savings.\n uint256 totalUnits = 0;\n for (uint256 i = 0; i < assetCount; ++i) {\n address assetAddr = allAssets[i];\n uint256 balance = _checkBalance(assetAddr);\n assetBalances[i] = balance;\n assetUnits[i] = _toUnits(balance, assetAddr);\n totalUnits = totalUnits + assetUnits[i];\n }\n // Calculate totalOutputRatio\n uint256 totalOutputRatio = 0;\n for (uint256 i = 0; i < assetCount; ++i) {\n uint256 unitPrice = _toUnitPrice(allAssets[i], false);\n uint256 ratio = (assetUnits[i] * unitPrice) / totalUnits;\n totalOutputRatio = totalOutputRatio + ratio;\n }\n // Calculate final outputs\n uint256 factor = _amount.divPrecisely(totalOutputRatio);\n for (uint256 i = 0; i < assetCount; ++i) {\n outputs[i] = (assetBalances[i] * factor) / totalUnits;\n }\n }\n\n /***************************************\n Pricing\n ****************************************/\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * Never goes above 1, since that is how we price mints.\n * @param asset address of the asset\n * @return price uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed\n */\n function priceUnitMint(address asset)\n external\n view\n returns (uint256 price)\n {\n /* need to supply 1 asset unit in asset's decimals and can not just hard-code\n * to 1e18 and ignore calling `_toUnits` since we need to consider assets\n * with the exchange rate\n */\n uint256 units = _toUnits(\n uint256(1e18).scaleBy(_getDecimals(asset), 18),\n asset\n );\n price = (_toUnitPrice(asset, true) * units) / 1e18;\n }\n\n /**\n * @notice Returns the total price in 18 digit unit for a given asset.\n * Never goes below 1, since that is how we price redeems\n * @param asset Address of the asset\n * @return price uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed\n */\n function priceUnitRedeem(address asset)\n external\n view\n returns (uint256 price)\n {\n /* need to supply 1 asset unit in asset's decimals and can not just hard-code\n * to 1e18 and ignore calling `_toUnits` since we need to consider assets\n * with the exchange rate\n */\n uint256 units = _toUnits(\n uint256(1e18).scaleBy(_getDecimals(asset), 18),\n asset\n );\n price = (_toUnitPrice(asset, false) * units) / 1e18;\n }\n\n /***************************************\n Utils\n ****************************************/\n\n /**\n * @dev Convert a quantity of a token into 1e18 fixed decimal \"units\"\n * in the underlying base (USD/ETH) used by the vault.\n * Price is not taken into account, only quantity.\n *\n * Examples of this conversion:\n *\n * - 1e18 DAI becomes 1e18 units (same decimals)\n * - 1e6 USDC becomes 1e18 units (decimal conversion)\n * - 1e18 rETH becomes 1.2e18 units (exchange rate conversion)\n *\n * @param _raw Quantity of asset\n * @param _asset Core Asset address\n * @return value 1e18 normalized quantity of units\n */\n function _toUnits(uint256 _raw, address _asset)\n internal\n view\n returns (uint256)\n {\n UnitConversion conversion = assets[_asset].unitConversion;\n if (conversion == UnitConversion.DECIMALS) {\n return _raw.scaleBy(18, _getDecimals(_asset));\n } else if (conversion == UnitConversion.GETEXCHANGERATE) {\n uint256 exchangeRate = IGetExchangeRateToken(_asset)\n .getExchangeRate();\n return (_raw * exchangeRate) / 1e18;\n } else {\n revert(\"Unsupported conversion type\");\n }\n }\n\n /**\n * @dev Returns asset's unit price accounting for different asset types\n * and takes into account the context in which that price exists -\n * - mint or redeem.\n *\n * Note: since we are returning the price of the unit and not the one of the\n * asset (see comment above how 1 rETH exchanges for 1.2 units) we need\n * to make the Oracle price adjustment as well since we are pricing the\n * units and not the assets.\n *\n * The price also snaps to a \"full unit price\" in case a mint or redeem\n * action would be unfavourable to the protocol.\n *\n */\n function _toUnitPrice(address _asset, bool isMint)\n internal\n view\n returns (uint256 price)\n {\n UnitConversion conversion = assets[_asset].unitConversion;\n price = IOracle(priceProvider).price(_asset);\n\n if (conversion == UnitConversion.GETEXCHANGERATE) {\n uint256 exchangeRate = IGetExchangeRateToken(_asset)\n .getExchangeRate();\n price = (price * 1e18) / exchangeRate;\n } else if (conversion != UnitConversion.DECIMALS) {\n revert(\"Unsupported conversion type\");\n }\n\n /* At this stage the price is already adjusted to the unit\n * so the price checks are agnostic to underlying asset being\n * pegged to a USD or to an ETH or having a custom exchange rate.\n */\n require(price <= MAX_UNIT_PRICE_DRIFT, \"Vault: Price exceeds max\");\n require(price >= MIN_UNIT_PRICE_DRIFT, \"Vault: Price under min\");\n\n if (isMint) {\n /* Never price a normalized unit price for more than one\n * unit of OETH/OUSD when minting.\n */\n if (price > 1e18) {\n price = 1e18;\n }\n require(price >= MINT_MINIMUM_UNIT_PRICE, \"Asset price below peg\");\n } else {\n /* Never give out more than 1 normalized unit amount of assets\n * for one unit of OETH/OUSD when redeeming.\n */\n if (price < 1e18) {\n price = 1e18;\n }\n }\n }\n\n /**\n * @dev Get the number of decimals of a token asset\n * @param _asset Address of the asset\n * @return decimals number of decimals\n */\n function _getDecimals(address _asset)\n internal\n view\n returns (uint256 decimals)\n {\n decimals = assets[_asset].decimals;\n require(decimals > 0, \"Decimals not cached\");\n }\n\n /**\n * @notice Return the number of assets supported by the Vault.\n */\n function getAssetCount() public view returns (uint256) {\n return allAssets.length;\n }\n\n /**\n * @notice Gets the vault configuration of a supported asset.\n * @param _asset Address of the token asset\n */\n function getAssetConfig(address _asset)\n public\n view\n returns (Asset memory config)\n {\n config = assets[_asset];\n }\n\n /**\n * @notice Return all vault asset addresses in order\n */\n function getAllAssets() external view returns (address[] memory) {\n return allAssets;\n }\n\n /**\n * @notice Return the number of strategies active on the Vault.\n */\n function getStrategyCount() external view returns (uint256) {\n return allStrategies.length;\n }\n\n /**\n * @notice Return the array of all strategies\n */\n function getAllStrategies() external view returns (address[] memory) {\n return allStrategies;\n }\n\n /**\n * @notice Returns whether the vault supports the asset\n * @param _asset address of the asset\n * @return true if supported\n */\n function isSupportedAsset(address _asset) external view returns (bool) {\n return assets[_asset].isSupported;\n }\n\n function ADMIN_IMPLEMENTATION() external view returns (address adminImpl) {\n bytes32 slot = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n adminImpl := sload(slot)\n }\n }\n\n /**\n * @dev Falldown to the admin implementation\n * @notice This is a catch all for all functions not declared in core\n */\n // solhint-disable-next-line no-complex-fallback\n fallback() external {\n bytes32 slot = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(\n gas(),\n sload(slot),\n 0,\n calldatasize(),\n 0,\n 0\n )\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n function abs(int256 x) private pure returns (uint256) {\n require(x < int256(MAX_INT), \"Amount too high\");\n return x >= 0 ? uint256(x) : uint256(-x);\n }\n\n function _min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n function _max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a > b ? a : b;\n }\n}\n" + }, + "contracts/vault/VaultInitializer.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultInitializer contract\n * @notice The Vault contract initializes the vault.\n * @author Origin Protocol Inc\n */\n\nimport \"./VaultStorage.sol\";\n\ncontract VaultInitializer is VaultStorage {\n function initialize(address _priceProvider, address _oToken)\n external\n onlyGovernor\n initializer\n {\n require(_priceProvider != address(0), \"PriceProvider address is zero\");\n require(_oToken != address(0), \"oToken address is zero\");\n\n oUSD = OUSD(_oToken);\n\n priceProvider = _priceProvider;\n\n rebasePaused = false;\n capitalPaused = true;\n\n // Initial redeem fee of 0 basis points\n redeemFeeBps = 0;\n // Initial Vault buffer of 0%\n vaultBuffer = 0;\n // Initial allocate threshold of 25,000 OUSD\n autoAllocateThreshold = 25000e18;\n // Threshold for rebasing\n rebaseThreshold = 1000e18;\n // Initialize all strategies\n allStrategies = new address[](0);\n // Start with drip duration disabled\n dripDuration = 1;\n }\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + }, + "contracts/zapper/WOETHCCIPZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IRouterClient } from \"@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol\";\nimport { Client } from \"@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol\";\n// solhint-disable-next-line max-line-length\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"./../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IOETHZapper } from \"./../interfaces/IOETHZapper.sol\";\n\n/**\n * @title WOETH CCIP Zapper Contract\n * @notice Helps to directly convert ETH on mainnet into WOETH on L2s.\n * @author Origin Protocol Inc\n */\n\ncontract WOETHCCIPZapper {\n /**\n * @dev Event emitted when a zap occurs\n * @param messageId Unique message identifier for each zap\n * @param sender Address initiating the zap\n * @param recipient Recipient address at destination chain\n * @param amount Amount of ETH zapped\n */\n event Zap(\n bytes32 indexed messageId,\n address sender,\n address recipient,\n uint256 amount\n );\n\n // @dev Thrown when Zap amount is less than fee.\n error AmountLessThanFee();\n\n /**\n * @dev The destination chain selector\n */\n uint64 public immutable destinationChainSelector;\n\n /**\n * @dev The WOETH source chain (Mainnet)\n */\n IERC4626 public immutable woethOnSourceChain;\n\n /**\n * @dev The WOETH destination chain (Arbitrum)\n */\n IERC20 public immutable woethOnDestinationChain;\n\n /**\n * @dev The OETH zapper contract address\n */\n IOETHZapper public immutable oethZapper;\n\n /**\n * @dev The CCIP router contract address\n */\n IRouterClient public immutable ccipRouter;\n\n /**\n * @dev The OETH token contract address\n */\n IERC20 public immutable oeth;\n\n constructor(\n address _ccipRouter,\n uint64 _destinationChainSelector,\n IERC4626 _woethOnSourceChain,\n IERC20 _woethOnDestinationChain,\n IOETHZapper _oethZapper,\n IERC20 _oeth\n ) {\n ccipRouter = IRouterClient(_ccipRouter);\n destinationChainSelector = _destinationChainSelector;\n woethOnSourceChain = _woethOnSourceChain;\n woethOnDestinationChain = _woethOnDestinationChain;\n oethZapper = _oethZapper;\n oeth = _oeth;\n\n // Max allowance for Router and WOETH contracts\n _oeth.approve(address(_woethOnSourceChain), type(uint256).max); // for wrapping\n _woethOnSourceChain.approve(address(_ccipRouter), type(uint256).max); // for zapping\n }\n\n /**\n * @notice Accepts ETH, zaps for OETH, wraps it for WOETH and sends it to the destination chain (arbitrum)\n * @param receiver The address of the EOA on the destination chain\n * @return messageId The ID of the message that was sent\n */\n function zap(address receiver)\n external\n payable\n returns (bytes32 messageId)\n {\n return _zap(receiver, msg.value);\n }\n\n /**\n * @notice Used to estimate fee for CCIP transaction\n * @param amount The amount of ETH to be zapped\n * @param receiver The address of the EOA on the destination chain\n * @return feeAmount The CCIP tx fee in ETH.\n */\n\n function getFee(uint256 amount, address receiver)\n public\n view\n returns (uint256 feeAmount)\n {\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(woethOnSourceChain),\n amount: amount\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({\n receiver: abi.encode(receiver), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n feeAmount = ccipRouter.getFee(destinationChainSelector, message);\n }\n\n /**\n * @dev Deposit ETH and receive WOETH in L2.\n * @dev Note that the WOETH will be sent to the msg.sender at destination chain as well.\n */\n receive() external payable {\n _zap(msg.sender, msg.value);\n }\n\n function _zap(address receiver, uint256 amount)\n internal\n returns (bytes32 messageId)\n {\n // Estimate fee for zapping.\n uint256 feeAmount = getFee(amount, receiver);\n if (amount < feeAmount) {\n revert AmountLessThanFee();\n }\n\n // Convert only the msg.value - fees amount to WOETH.\n amount -= feeAmount;\n\n // 1.) Zap for OETH\n uint256 oethReceived = oethZapper.deposit{ value: amount }();\n\n // 2.) Wrap the received woeth\n uint256 woethReceived = woethOnSourceChain.deposit(\n oethReceived,\n address(this)\n );\n\n // 3.) Setup params for CCIP transfer\n\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(woethOnSourceChain),\n amount: woethReceived\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({\n receiver: abi.encode(receiver), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n // See: https://docs.chain.link/ccip/best-practices#setting-gaslimit\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n // ZAP ϟ\n //slither-disable-next-line arbitrary-send-eth\n messageId = ccipRouter.ccipSend{ value: feeAmount }(\n destinationChainSelector,\n message\n );\n\n // Emit Zap event with message details\n emit Zap(messageId, msg.sender, receiver, amount);\n\n // Return the message ID\n return messageId;\n }\n}\n" + }, + "hardhat/console.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.4.22 <0.9.0;\n\nlibrary console {\n address constant CONSOLE_ADDRESS =\n 0x000000000000000000636F6e736F6c652e6c6f67;\n\n function _sendLogPayloadImplementation(bytes memory payload) internal view {\n address consoleAddress = CONSOLE_ADDRESS;\n /// @solidity memory-safe-assembly\n assembly {\n pop(\n staticcall(\n gas(),\n consoleAddress,\n add(payload, 32),\n mload(payload),\n 0,\n 0\n )\n )\n }\n }\n\n function _castToPure(\n function(bytes memory) internal view fnIn\n ) internal pure returns (function(bytes memory) pure fnOut) {\n assembly {\n fnOut := fnIn\n }\n }\n\n function _sendLogPayload(bytes memory payload) internal pure {\n _castToPure(_sendLogPayloadImplementation)(payload);\n }\n\n function log() internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log()\"));\n }\n\n function logInt(int256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(int256)\", p0));\n }\n\n function logUint(uint256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256)\", p0));\n }\n\n function logString(string memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n }\n\n function logBool(bool p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n }\n\n function logAddress(address p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n }\n\n function logBytes(bytes memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes)\", p0));\n }\n\n function logBytes1(bytes1 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes1)\", p0));\n }\n\n function logBytes2(bytes2 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes2)\", p0));\n }\n\n function logBytes3(bytes3 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes3)\", p0));\n }\n\n function logBytes4(bytes4 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes4)\", p0));\n }\n\n function logBytes5(bytes5 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes5)\", p0));\n }\n\n function logBytes6(bytes6 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes6)\", p0));\n }\n\n function logBytes7(bytes7 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes7)\", p0));\n }\n\n function logBytes8(bytes8 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes8)\", p0));\n }\n\n function logBytes9(bytes9 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes9)\", p0));\n }\n\n function logBytes10(bytes10 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes10)\", p0));\n }\n\n function logBytes11(bytes11 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes11)\", p0));\n }\n\n function logBytes12(bytes12 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes12)\", p0));\n }\n\n function logBytes13(bytes13 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes13)\", p0));\n }\n\n function logBytes14(bytes14 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes14)\", p0));\n }\n\n function logBytes15(bytes15 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes15)\", p0));\n }\n\n function logBytes16(bytes16 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes16)\", p0));\n }\n\n function logBytes17(bytes17 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes17)\", p0));\n }\n\n function logBytes18(bytes18 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes18)\", p0));\n }\n\n function logBytes19(bytes19 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes19)\", p0));\n }\n\n function logBytes20(bytes20 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes20)\", p0));\n }\n\n function logBytes21(bytes21 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes21)\", p0));\n }\n\n function logBytes22(bytes22 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes22)\", p0));\n }\n\n function logBytes23(bytes23 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes23)\", p0));\n }\n\n function logBytes24(bytes24 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes24)\", p0));\n }\n\n function logBytes25(bytes25 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes25)\", p0));\n }\n\n function logBytes26(bytes26 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes26)\", p0));\n }\n\n function logBytes27(bytes27 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes27)\", p0));\n }\n\n function logBytes28(bytes28 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes28)\", p0));\n }\n\n function logBytes29(bytes29 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes29)\", p0));\n }\n\n function logBytes30(bytes30 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes30)\", p0));\n }\n\n function logBytes31(bytes31 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes31)\", p0));\n }\n\n function logBytes32(bytes32 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes32)\", p0));\n }\n\n function log(uint256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256)\", p0));\n }\n\n function log(string memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n }\n\n function log(bool p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n }\n\n function log(address p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n }\n\n function log(uint256 p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256)\", p0, p1));\n }\n\n function log(uint256 p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string)\", p0, p1));\n }\n\n function log(uint256 p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool)\", p0, p1));\n }\n\n function log(uint256 p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address)\", p0, p1));\n }\n\n function log(string memory p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256)\", p0, p1));\n }\n\n function log(string memory p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string)\", p0, p1));\n }\n\n function log(string memory p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool)\", p0, p1));\n }\n\n function log(string memory p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address)\", p0, p1));\n }\n\n function log(bool p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256)\", p0, p1));\n }\n\n function log(bool p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string)\", p0, p1));\n }\n\n function log(bool p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool)\", p0, p1));\n }\n\n function log(bool p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address)\", p0, p1));\n }\n\n function log(address p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256)\", p0, p1));\n }\n\n function log(address p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string)\", p0, p1));\n }\n\n function log(address p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool)\", p0, p1));\n }\n\n function log(address p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address)\", p0, p1));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,address)\", p0, p1, p2, p3));\n }\n}\n" + }, + "lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC4626 } from \"../../../../interfaces/IERC4626.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\n// From Open Zeppelin draft PR commit:\n// fac43034dca85ff539db3fc8aa2a7084b843d454\n// https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3171\n\nabstract contract ERC4626 is ERC20, IERC4626 {\n IERC20Metadata private immutable _asset;\n\n constructor(IERC20Metadata __asset) {\n _asset = __asset;\n }\n\n /** @dev See {IERC4262-asset} */\n function asset() public view virtual override returns (address) {\n return address(_asset);\n }\n\n /** @dev See {IERC4262-totalAssets} */\n function totalAssets() public view virtual override returns (uint256) {\n return _asset.balanceOf(address(this));\n }\n\n /**\n * @dev See {IERC4262-convertToShares}\n *\n * Will revert if asserts > 0, totalSupply > 0 and totalAssets = 0. That corresponds to a case where any asset\n * would represent an infinite amout of shares.\n */\n function convertToShares(uint256 assets) public view virtual override returns (uint256 shares) {\n uint256 supply = totalSupply();\n\n return\n (assets == 0 || supply == 0)\n ? (assets * 10**decimals()) / 10**_asset.decimals()\n : (assets * supply) / totalAssets();\n }\n\n /** @dev See {IERC4262-convertToAssets} */\n function convertToAssets(uint256 shares) public view virtual override returns (uint256 assets) {\n uint256 supply = totalSupply();\n\n return (supply == 0) ? (shares * 10**_asset.decimals()) / 10**decimals() : (shares * totalAssets()) / supply;\n }\n\n /** @dev See {IERC4262-maxDeposit} */\n function maxDeposit(address) public view virtual override returns (uint256) {\n return type(uint256).max;\n }\n\n /** @dev See {IERC4262-maxMint} */\n function maxMint(address) public view virtual override returns (uint256) {\n return type(uint256).max;\n }\n\n /** @dev See {IERC4262-maxWithdraw} */\n function maxWithdraw(address owner) public view virtual override returns (uint256) {\n return convertToAssets(balanceOf(owner));\n }\n\n /** @dev See {IERC4262-maxRedeem} */\n function maxRedeem(address owner) public view virtual override returns (uint256) {\n return balanceOf(owner);\n }\n\n /** @dev See {IERC4262-previewDeposit} */\n function previewDeposit(uint256 assets) public view virtual override returns (uint256) {\n return convertToShares(assets);\n }\n\n /** @dev See {IERC4262-previewMint} */\n function previewMint(uint256 shares) public view virtual override returns (uint256) {\n uint256 assets = convertToAssets(shares);\n return assets + (convertToShares(assets) < shares ? 1 : 0);\n }\n\n /** @dev See {IERC4262-previewWithdraw} */\n function previewWithdraw(uint256 assets) public view virtual override returns (uint256) {\n uint256 shares = convertToShares(assets);\n return shares + (convertToAssets(shares) < assets ? 1 : 0);\n }\n\n /** @dev See {IERC4262-previewRedeem} */\n function previewRedeem(uint256 shares) public view virtual override returns (uint256) {\n return convertToAssets(shares);\n }\n\n /** @dev See {IERC4262-deposit} */\n function deposit(uint256 assets, address receiver) public virtual override returns (uint256) {\n require(assets <= maxDeposit(receiver), \"ERC4626: deposit more then max\");\n\n address caller = _msgSender();\n uint256 shares = previewDeposit(assets);\n\n // if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through\n // the tokensToSend hook, so we need to transfer before we mint to keep the invariants.\n SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);\n _mint(receiver, shares);\n\n emit Deposit(caller, receiver, assets, shares);\n\n return shares;\n }\n\n /** @dev See {IERC4262-mint} */\n function mint(uint256 shares, address receiver) public virtual override returns (uint256) {\n require(shares <= maxMint(receiver), \"ERC4626: mint more then max\");\n\n address caller = _msgSender();\n uint256 assets = previewMint(shares);\n\n // if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through\n // the tokensToSend hook, so we need to transfer before we mint to keep the invariants.\n SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);\n _mint(receiver, shares);\n\n emit Deposit(caller, receiver, assets, shares);\n\n return assets;\n }\n\n /** @dev See {IERC4262-withdraw} */\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) public virtual override returns (uint256) {\n require(assets <= maxWithdraw(owner), \"ERC4626: withdraw more then max\");\n\n address caller = _msgSender();\n uint256 shares = previewWithdraw(assets);\n\n if (caller != owner) {\n _spendAllowance(owner, caller, shares);\n }\n\n // if _asset is ERC777, transfer can call reenter AFTER the transfer happens through\n // the tokensReceived hook, so we need to transfer after we burn to keep the invariants.\n _burn(owner, shares);\n SafeERC20.safeTransfer(_asset, receiver, assets);\n\n emit Withdraw(caller, receiver, owner, assets, shares);\n\n return shares;\n }\n\n /** @dev See {IERC4262-redeem} */\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) public virtual override returns (uint256) {\n require(shares <= maxRedeem(owner), \"ERC4626: redeem more then max\");\n\n address caller = _msgSender();\n uint256 assets = previewRedeem(shares);\n\n if (caller != owner) {\n _spendAllowance(owner, caller, shares);\n }\n\n // if _asset is ERC777, transfer can call reenter AFTER the transfer happens through\n // the tokensReceived hook, so we need to transfer after we burn to keep the invariants.\n _burn(owner, shares);\n SafeERC20.safeTransfer(_asset, receiver, assets);\n\n emit Withdraw(caller, receiver, owner, assets, shares);\n\n return assets;\n }\n\n // Included here, since this method was not yet present in\n // the version of Open Zeppelin ERC20 code we use.\n function _spendAllowance(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n uint256 currentAllowance = allowance(owner, spender);\n if (currentAllowance != type(uint256).max) {\n require(currentAllowance >= amount, \"ERC20: insufficient allowance\");\n unchecked {\n _approve(owner, spender, currentAllowance - amount);\n }\n }\n }\n}" + }, + "lib/openzeppelin/interfaces/IERC4626.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IERC4626 is IERC20, IERC20Metadata {\n event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);\n\n event Withdraw(\n address indexed caller,\n address indexed receiver,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n\n /**\n * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.\n *\n * - MUST be an ERC-20 token contract.\n * - MUST NOT revert.\n */\n function asset() external view returns (address assetTokenAddress);\n\n /**\n * @dev Returns the total amount of the underlying asset that is “managed” by Vault.\n *\n * - SHOULD include any compounding that occurs from yield.\n * - MUST be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT revert.\n */\n function totalAssets() external view returns (uint256 totalManagedAssets);\n\n /**\n * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal\n * scenario where all the conditions are met.\n *\n * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT show any variations depending on the caller.\n * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n * - MUST NOT revert.\n *\n * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n * from.\n */\n function convertToShares(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal\n * scenario where all the conditions are met.\n *\n * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT show any variations depending on the caller.\n * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n * - MUST NOT revert.\n *\n * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n * from.\n */\n function convertToAssets(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,\n * through a deposit call.\n *\n * - MUST return a limited value if receiver is subject to some deposit limit.\n * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.\n * - MUST NOT revert.\n */\n function maxDeposit(address receiver) external view returns (uint256 maxAssets);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given\n * current on-chain conditions.\n *\n * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit\n * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called\n * in the same transaction.\n * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the\n * deposit would be accepted, regardless if the user has enough tokens approved, etc.\n * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by depositing.\n */\n function previewDeposit(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.\n *\n * - MUST emit the Deposit event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * deposit execution, and are accounted for during deposit.\n * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not\n * approving enough underlying tokens to the Vault contract, etc).\n *\n * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n */\n function deposit(uint256 assets, address receiver) external returns (uint256 shares);\n\n /**\n * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.\n * - MUST return a limited value if receiver is subject to some mint limit.\n * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.\n * - MUST NOT revert.\n */\n function maxMint(address receiver) external view returns (uint256 maxShares);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given\n * current on-chain conditions.\n *\n * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call\n * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the\n * same transaction.\n * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint\n * would be accepted, regardless if the user has enough tokens approved, etc.\n * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by minting.\n */\n function previewMint(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.\n *\n * - MUST emit the Deposit event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint\n * execution, and are accounted for during mint.\n * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not\n * approving enough underlying tokens to the Vault contract, etc).\n *\n * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n */\n function mint(uint256 shares, address receiver) external returns (uint256 assets);\n\n /**\n * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the\n * Vault, through a withdraw call.\n *\n * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n * - MUST NOT revert.\n */\n function maxWithdraw(address owner) external view returns (uint256 maxAssets);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,\n * given current on-chain conditions.\n *\n * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw\n * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if\n * called\n * in the same transaction.\n * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though\n * the withdrawal would be accepted, regardless if the user has enough shares, etc.\n * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by depositing.\n */\n function previewWithdraw(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.\n *\n * - MUST emit the Withdraw event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * withdraw execution, and are accounted for during withdraw.\n * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner\n * not having enough shares, etc).\n *\n * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n * Those methods should be performed separately.\n */\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares);\n\n /**\n * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,\n * through a redeem call.\n *\n * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.\n * - MUST NOT revert.\n */\n function maxRedeem(address owner) external view returns (uint256 maxShares);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,\n * given current on-chain conditions.\n *\n * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call\n * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the\n * same transaction.\n * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the\n * redemption would be accepted, regardless if the user has enough shares, etc.\n * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by redeeming.\n */\n function previewRedeem(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.\n *\n * - MUST emit the Withdraw event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * redeem execution, and are accounted for during redeem.\n * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner\n * not having enough shares, etc).\n *\n * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n * Those methods should be performed separately.\n */\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets);\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.20;\n\nimport {Panic} from \"../Panic.sol\";\nimport {SafeCast} from \"./SafeCast.sol\";\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n enum Rounding {\n Floor, // Toward negative infinity\n Ceil, // Toward positive infinity\n Trunc, // Toward zero\n Expand // Away from zero\n }\n\n /**\n * @dev Return the 512-bit addition of two uint256.\n *\n * The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low.\n */\n function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {\n assembly (\"memory-safe\") {\n low := add(a, b)\n high := lt(low, a)\n }\n }\n\n /**\n * @dev Return the 512-bit multiplication of two uint256.\n *\n * The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low.\n */\n function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {\n // 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use\n // the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n // variables such that product = high * 2²⁵⁶ + low.\n assembly (\"memory-safe\") {\n let mm := mulmod(a, b, not(0))\n low := mul(a, b)\n high := sub(sub(mm, low), lt(mm, low))\n }\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, with a success flag (no overflow).\n */\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a + b;\n success = c >= a;\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).\n */\n function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a - b;\n success = c <= a;\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).\n */\n function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a * b;\n assembly (\"memory-safe\") {\n // Only true when the multiplication doesn't overflow\n // (c / a == b) || (a == 0)\n success := or(eq(div(c, a), b), iszero(a))\n }\n // equivalent to: success ? c : 0\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the division of two unsigned integers, with a success flag (no division by zero).\n */\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n success = b > 0;\n assembly (\"memory-safe\") {\n // The `DIV` opcode returns zero when the denominator is 0.\n result := div(a, b)\n }\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).\n */\n function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n success = b > 0;\n assembly (\"memory-safe\") {\n // The `MOD` opcode returns zero when the denominator is 0.\n result := mod(a, b)\n }\n }\n }\n\n /**\n * @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing.\n */\n function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {\n (bool success, uint256 result) = tryAdd(a, b);\n return ternary(success, result, type(uint256).max);\n }\n\n /**\n * @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.\n */\n function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {\n (, uint256 result) = trySub(a, b);\n return result;\n }\n\n /**\n * @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing.\n */\n function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {\n (bool success, uint256 result) = tryMul(a, b);\n return ternary(success, result, type(uint256).max);\n }\n\n /**\n * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.\n *\n * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.\n * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute\n * one branch when needed, making this function more expensive.\n */\n function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {\n unchecked {\n // branchless ternary works because:\n // b ^ (a ^ b) == a\n // b ^ 0 == b\n return b ^ ((a ^ b) * SafeCast.toUint(condition));\n }\n }\n\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return ternary(a > b, a, b);\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return ternary(a < b, a, b);\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds towards infinity instead\n * of rounding towards zero.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n if (b == 0) {\n // Guarantee the same behavior as in a regular Solidity division.\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n\n // The following calculation ensures accurate ceiling division without overflow.\n // Since a is non-zero, (a - 1) / b will not overflow.\n // The largest possible result occurs when (a - 1) / b is type(uint256).max,\n // but the largest value we can obtain is type(uint256).max - 1, which happens\n // when a = type(uint256).max and b = 1.\n unchecked {\n return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);\n }\n }\n\n /**\n * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or\n * denominator == 0.\n *\n * Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by\n * Uniswap Labs also under MIT license.\n */\n function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {\n unchecked {\n (uint256 high, uint256 low) = mul512(x, y);\n\n // Handle non-overflow cases, 256 by 256 division.\n if (high == 0) {\n // Solidity will revert if denominator == 0, unlike the div opcode on its own.\n // The surrounding unchecked block does not change this fact.\n // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.\n return low / denominator;\n }\n\n // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.\n if (denominator <= high) {\n Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));\n }\n\n ///////////////////////////////////////////////\n // 512 by 256 division.\n ///////////////////////////////////////////////\n\n // Make division exact by subtracting the remainder from [high low].\n uint256 remainder;\n assembly (\"memory-safe\") {\n // Compute remainder using mulmod.\n remainder := mulmod(x, y, denominator)\n\n // Subtract 256 bit number from 512 bit number.\n high := sub(high, gt(remainder, low))\n low := sub(low, remainder)\n }\n\n // Factor powers of two out of denominator and compute largest power of two divisor of denominator.\n // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.\n\n uint256 twos = denominator & (0 - denominator);\n assembly (\"memory-safe\") {\n // Divide denominator by twos.\n denominator := div(denominator, twos)\n\n // Divide [high low] by twos.\n low := div(low, twos)\n\n // Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.\n twos := add(div(sub(0, twos), twos), 1)\n }\n\n // Shift in bits from high into low.\n low |= high * twos;\n\n // Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such\n // that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for\n // four bits. That is, denominator * inv ≡ 1 mod 2⁴.\n uint256 inverse = (3 * denominator) ^ 2;\n\n // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also\n // works in modular arithmetic, doubling the correct bits in each step.\n inverse *= 2 - denominator * inverse; // inverse mod 2⁸\n inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶\n inverse *= 2 - denominator * inverse; // inverse mod 2³²\n inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴\n inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸\n inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶\n\n // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n // This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is\n // less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and high\n // is no longer required.\n result = low * inverse;\n return result;\n }\n }\n\n /**\n * @dev Calculates x * y / denominator with full precision, following the selected rounding direction.\n */\n function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {\n return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);\n }\n\n /**\n * @dev Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256.\n */\n function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) {\n unchecked {\n (uint256 high, uint256 low) = mul512(x, y);\n if (high >= 1 << n) {\n Panic.panic(Panic.UNDER_OVERFLOW);\n }\n return (high << (256 - n)) | (low >> n);\n }\n }\n\n /**\n * @dev Calculates x * y >> n with full precision, following the selected rounding direction.\n */\n function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) {\n return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 0);\n }\n\n /**\n * @dev Calculate the modular multiplicative inverse of a number in Z/nZ.\n *\n * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.\n * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.\n *\n * If the input value is not inversible, 0 is returned.\n *\n * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the\n * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.\n */\n function invMod(uint256 a, uint256 n) internal pure returns (uint256) {\n unchecked {\n if (n == 0) return 0;\n\n // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)\n // Used to compute integers x and y such that: ax + ny = gcd(a, n).\n // When the gcd is 1, then the inverse of a modulo n exists and it's x.\n // ax + ny = 1\n // ax = 1 + (-y)n\n // ax ≡ 1 (mod n) # x is the inverse of a modulo n\n\n // If the remainder is 0 the gcd is n right away.\n uint256 remainder = a % n;\n uint256 gcd = n;\n\n // Therefore the initial coefficients are:\n // ax + ny = gcd(a, n) = n\n // 0a + 1n = n\n int256 x = 0;\n int256 y = 1;\n\n while (remainder != 0) {\n uint256 quotient = gcd / remainder;\n\n (gcd, remainder) = (\n // The old remainder is the next gcd to try.\n remainder,\n // Compute the next remainder.\n // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd\n // where gcd is at most n (capped to type(uint256).max)\n gcd - remainder * quotient\n );\n\n (x, y) = (\n // Increment the coefficient of a.\n y,\n // Decrement the coefficient of n.\n // Can overflow, but the result is casted to uint256 so that the\n // next value of y is \"wrapped around\" to a value between 0 and n - 1.\n x - y * int256(quotient)\n );\n }\n\n if (gcd != 1) return 0; // No inverse exists.\n return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.\n }\n }\n\n /**\n * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.\n *\n * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is\n * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that\n * `a**(p-2)` is the modular multiplicative inverse of a in Fp.\n *\n * NOTE: this function does NOT check that `p` is a prime greater than `2`.\n */\n function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {\n unchecked {\n return Math.modExp(a, p - 2, p);\n }\n }\n\n /**\n * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)\n *\n * Requirements:\n * - modulus can't be zero\n * - underlying staticcall to precompile must succeed\n *\n * IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make\n * sure the chain you're using it on supports the precompiled contract for modular exponentiation\n * at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,\n * the underlying function will succeed given the lack of a revert, but the result may be incorrectly\n * interpreted as 0.\n */\n function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {\n (bool success, uint256 result) = tryModExp(b, e, m);\n if (!success) {\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n return result;\n }\n\n /**\n * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).\n * It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying\n * to operate modulo 0 or if the underlying precompile reverted.\n *\n * IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain\n * you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in\n * https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack\n * of a revert, but the result may be incorrectly interpreted as 0.\n */\n function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {\n if (m == 0) return (false, 0);\n assembly (\"memory-safe\") {\n let ptr := mload(0x40)\n // | Offset | Content | Content (Hex) |\n // |-----------|------------|--------------------------------------------------------------------|\n // | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x60:0x7f | value of b | 0x<.............................................................b> |\n // | 0x80:0x9f | value of e | 0x<.............................................................e> |\n // | 0xa0:0xbf | value of m | 0x<.............................................................m> |\n mstore(ptr, 0x20)\n mstore(add(ptr, 0x20), 0x20)\n mstore(add(ptr, 0x40), 0x20)\n mstore(add(ptr, 0x60), b)\n mstore(add(ptr, 0x80), e)\n mstore(add(ptr, 0xa0), m)\n\n // Given the result < m, it's guaranteed to fit in 32 bytes,\n // so we can use the memory scratch space located at offset 0.\n success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)\n result := mload(0x00)\n }\n }\n\n /**\n * @dev Variant of {modExp} that supports inputs of arbitrary length.\n */\n function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {\n (bool success, bytes memory result) = tryModExp(b, e, m);\n if (!success) {\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n return result;\n }\n\n /**\n * @dev Variant of {tryModExp} that supports inputs of arbitrary length.\n */\n function tryModExp(\n bytes memory b,\n bytes memory e,\n bytes memory m\n ) internal view returns (bool success, bytes memory result) {\n if (_zeroBytes(m)) return (false, new bytes(0));\n\n uint256 mLen = m.length;\n\n // Encode call args in result and move the free memory pointer\n result = abi.encodePacked(b.length, e.length, mLen, b, e, m);\n\n assembly (\"memory-safe\") {\n let dataPtr := add(result, 0x20)\n // Write result on top of args to avoid allocating extra memory.\n success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)\n // Overwrite the length.\n // result.length > returndatasize() is guaranteed because returndatasize() == m.length\n mstore(result, mLen)\n // Set the memory pointer after the returned data.\n mstore(0x40, add(dataPtr, mLen))\n }\n }\n\n /**\n * @dev Returns whether the provided byte array is zero.\n */\n function _zeroBytes(bytes memory byteArray) private pure returns (bool) {\n for (uint256 i = 0; i < byteArray.length; ++i) {\n if (byteArray[i] != 0) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded\n * towards zero.\n *\n * This method is based on Newton's method for computing square roots; the algorithm is restricted to only\n * using integer operations.\n */\n function sqrt(uint256 a) internal pure returns (uint256) {\n unchecked {\n // Take care of easy edge cases when a == 0 or a == 1\n if (a <= 1) {\n return a;\n }\n\n // In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a\n // sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between\n // the current value as `ε_n = | x_n - sqrt(a) |`.\n //\n // For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root\n // of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is\n // bigger than any uint256.\n //\n // By noticing that\n // `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`\n // we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar\n // to the msb function.\n uint256 aa = a;\n uint256 xn = 1;\n\n if (aa >= (1 << 128)) {\n aa >>= 128;\n xn <<= 64;\n }\n if (aa >= (1 << 64)) {\n aa >>= 64;\n xn <<= 32;\n }\n if (aa >= (1 << 32)) {\n aa >>= 32;\n xn <<= 16;\n }\n if (aa >= (1 << 16)) {\n aa >>= 16;\n xn <<= 8;\n }\n if (aa >= (1 << 8)) {\n aa >>= 8;\n xn <<= 4;\n }\n if (aa >= (1 << 4)) {\n aa >>= 4;\n xn <<= 2;\n }\n if (aa >= (1 << 2)) {\n xn <<= 1;\n }\n\n // We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).\n //\n // We can refine our estimation by noticing that the middle of that interval minimizes the error.\n // If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).\n // This is going to be our x_0 (and ε_0)\n xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)\n\n // From here, Newton's method give us:\n // x_{n+1} = (x_n + a / x_n) / 2\n //\n // One should note that:\n // x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a\n // = ((x_n² + a) / (2 * x_n))² - a\n // = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a\n // = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)\n // = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)\n // = (x_n² - a)² / (2 * x_n)²\n // = ((x_n² - a) / (2 * x_n))²\n // ≥ 0\n // Which proves that for all n ≥ 1, sqrt(a) ≤ x_n\n //\n // This gives us the proof of quadratic convergence of the sequence:\n // ε_{n+1} = | x_{n+1} - sqrt(a) |\n // = | (x_n + a / x_n) / 2 - sqrt(a) |\n // = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |\n // = | (x_n - sqrt(a))² / (2 * x_n) |\n // = | ε_n² / (2 * x_n) |\n // = ε_n² / | (2 * x_n) |\n //\n // For the first iteration, we have a special case where x_0 is known:\n // ε_1 = ε_0² / | (2 * x_0) |\n // ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))\n // ≤ 2**(2*e-4) / (3 * 2**(e-1))\n // ≤ 2**(e-3) / 3\n // ≤ 2**(e-3-log2(3))\n // ≤ 2**(e-4.5)\n //\n // For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:\n // ε_{n+1} = ε_n² / | (2 * x_n) |\n // ≤ (2**(e-k))² / (2 * 2**(e-1))\n // ≤ 2**(2*e-2*k) / 2**e\n // ≤ 2**(e-2*k)\n xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above\n xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5\n xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9\n xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18\n xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36\n xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72\n\n // Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision\n // ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either\n // sqrt(a) or sqrt(a) + 1.\n return xn - SafeCast.toUint(xn > a / xn);\n }\n }\n\n /**\n * @dev Calculates sqrt(a), following the selected rounding direction.\n */\n function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = sqrt(a);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);\n }\n }\n\n /**\n * @dev Return the log in base 2 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n */\n function log2(uint256 x) internal pure returns (uint256 r) {\n // If value has upper 128 bits set, log2 result is at least 128\n r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;\n // If upper 64 bits of 128-bit half set, add 64 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;\n // If upper 32 bits of 64-bit half set, add 32 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;\n // If upper 16 bits of 32-bit half set, add 16 to result\n r |= SafeCast.toUint((x >> r) > 0xffff) << 4;\n // If upper 8 bits of 16-bit half set, add 8 to result\n r |= SafeCast.toUint((x >> r) > 0xff) << 3;\n // If upper 4 bits of 8-bit half set, add 4 to result\n r |= SafeCast.toUint((x >> r) > 0xf) << 2;\n\n // Shifts value right by the current result and use it as an index into this lookup table:\n //\n // | x (4 bits) | index | table[index] = MSB position |\n // |------------|---------|-----------------------------|\n // | 0000 | 0 | table[0] = 0 |\n // | 0001 | 1 | table[1] = 0 |\n // | 0010 | 2 | table[2] = 1 |\n // | 0011 | 3 | table[3] = 1 |\n // | 0100 | 4 | table[4] = 2 |\n // | 0101 | 5 | table[5] = 2 |\n // | 0110 | 6 | table[6] = 2 |\n // | 0111 | 7 | table[7] = 2 |\n // | 1000 | 8 | table[8] = 3 |\n // | 1001 | 9 | table[9] = 3 |\n // | 1010 | 10 | table[10] = 3 |\n // | 1011 | 11 | table[11] = 3 |\n // | 1100 | 12 | table[12] = 3 |\n // | 1101 | 13 | table[13] = 3 |\n // | 1110 | 14 | table[14] = 3 |\n // | 1111 | 15 | table[15] = 3 |\n //\n // The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.\n assembly (\"memory-safe\") {\n r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))\n }\n }\n\n /**\n * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log2(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);\n }\n }\n\n /**\n * @dev Return the log in base 10 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n */\n function log10(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >= 10 ** 64) {\n value /= 10 ** 64;\n result += 64;\n }\n if (value >= 10 ** 32) {\n value /= 10 ** 32;\n result += 32;\n }\n if (value >= 10 ** 16) {\n value /= 10 ** 16;\n result += 16;\n }\n if (value >= 10 ** 8) {\n value /= 10 ** 8;\n result += 8;\n }\n if (value >= 10 ** 4) {\n value /= 10 ** 4;\n result += 4;\n }\n if (value >= 10 ** 2) {\n value /= 10 ** 2;\n result += 2;\n }\n if (value >= 10 ** 1) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log10(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);\n }\n }\n\n /**\n * @dev Return the log in base 256 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n *\n * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n */\n function log256(uint256 x) internal pure returns (uint256 r) {\n // If value has upper 128 bits set, log2 result is at least 128\n r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;\n // If upper 64 bits of 128-bit half set, add 64 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;\n // If upper 32 bits of 64-bit half set, add 32 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;\n // If upper 16 bits of 32-bit half set, add 16 to result\n r |= SafeCast.toUint((x >> r) > 0xffff) << 4;\n // Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8\n return (r >> 3) | SafeCast.toUint((x >> r) > 0xff);\n }\n\n /**\n * @dev Return the log in base 256, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log256(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);\n }\n }\n\n /**\n * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.\n */\n function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {\n return uint8(rounding) % 2 == 1;\n }\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)\n// This file was procedurally generated from scripts/generate/templates/SafeCast.js.\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n */\nlibrary SafeCast {\n /**\n * @dev Value doesn't fit in an uint of `bits` size.\n */\n error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);\n\n /**\n * @dev An int value doesn't fit in an uint of `bits` size.\n */\n error SafeCastOverflowedIntToUint(int256 value);\n\n /**\n * @dev Value doesn't fit in an int of `bits` size.\n */\n error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);\n\n /**\n * @dev An uint value doesn't fit in an int of `bits` size.\n */\n error SafeCastOverflowedUintToInt(uint256 value);\n\n /**\n * @dev Returns the downcasted uint248 from uint256, reverting on\n * overflow (when the input is greater than largest uint248).\n *\n * Counterpart to Solidity's `uint248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n */\n function toUint248(uint256 value) internal pure returns (uint248) {\n if (value > type(uint248).max) {\n revert SafeCastOverflowedUintDowncast(248, value);\n }\n return uint248(value);\n }\n\n /**\n * @dev Returns the downcasted uint240 from uint256, reverting on\n * overflow (when the input is greater than largest uint240).\n *\n * Counterpart to Solidity's `uint240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n */\n function toUint240(uint256 value) internal pure returns (uint240) {\n if (value > type(uint240).max) {\n revert SafeCastOverflowedUintDowncast(240, value);\n }\n return uint240(value);\n }\n\n /**\n * @dev Returns the downcasted uint232 from uint256, reverting on\n * overflow (when the input is greater than largest uint232).\n *\n * Counterpart to Solidity's `uint232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n */\n function toUint232(uint256 value) internal pure returns (uint232) {\n if (value > type(uint232).max) {\n revert SafeCastOverflowedUintDowncast(232, value);\n }\n return uint232(value);\n }\n\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n if (value > type(uint224).max) {\n revert SafeCastOverflowedUintDowncast(224, value);\n }\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint216 from uint256, reverting on\n * overflow (when the input is greater than largest uint216).\n *\n * Counterpart to Solidity's `uint216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n */\n function toUint216(uint256 value) internal pure returns (uint216) {\n if (value > type(uint216).max) {\n revert SafeCastOverflowedUintDowncast(216, value);\n }\n return uint216(value);\n }\n\n /**\n * @dev Returns the downcasted uint208 from uint256, reverting on\n * overflow (when the input is greater than largest uint208).\n *\n * Counterpart to Solidity's `uint208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n */\n function toUint208(uint256 value) internal pure returns (uint208) {\n if (value > type(uint208).max) {\n revert SafeCastOverflowedUintDowncast(208, value);\n }\n return uint208(value);\n }\n\n /**\n * @dev Returns the downcasted uint200 from uint256, reverting on\n * overflow (when the input is greater than largest uint200).\n *\n * Counterpart to Solidity's `uint200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n */\n function toUint200(uint256 value) internal pure returns (uint200) {\n if (value > type(uint200).max) {\n revert SafeCastOverflowedUintDowncast(200, value);\n }\n return uint200(value);\n }\n\n /**\n * @dev Returns the downcasted uint192 from uint256, reverting on\n * overflow (when the input is greater than largest uint192).\n *\n * Counterpart to Solidity's `uint192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n */\n function toUint192(uint256 value) internal pure returns (uint192) {\n if (value > type(uint192).max) {\n revert SafeCastOverflowedUintDowncast(192, value);\n }\n return uint192(value);\n }\n\n /**\n * @dev Returns the downcasted uint184 from uint256, reverting on\n * overflow (when the input is greater than largest uint184).\n *\n * Counterpart to Solidity's `uint184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n */\n function toUint184(uint256 value) internal pure returns (uint184) {\n if (value > type(uint184).max) {\n revert SafeCastOverflowedUintDowncast(184, value);\n }\n return uint184(value);\n }\n\n /**\n * @dev Returns the downcasted uint176 from uint256, reverting on\n * overflow (when the input is greater than largest uint176).\n *\n * Counterpart to Solidity's `uint176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n */\n function toUint176(uint256 value) internal pure returns (uint176) {\n if (value > type(uint176).max) {\n revert SafeCastOverflowedUintDowncast(176, value);\n }\n return uint176(value);\n }\n\n /**\n * @dev Returns the downcasted uint168 from uint256, reverting on\n * overflow (when the input is greater than largest uint168).\n *\n * Counterpart to Solidity's `uint168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n */\n function toUint168(uint256 value) internal pure returns (uint168) {\n if (value > type(uint168).max) {\n revert SafeCastOverflowedUintDowncast(168, value);\n }\n return uint168(value);\n }\n\n /**\n * @dev Returns the downcasted uint160 from uint256, reverting on\n * overflow (when the input is greater than largest uint160).\n *\n * Counterpart to Solidity's `uint160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n */\n function toUint160(uint256 value) internal pure returns (uint160) {\n if (value > type(uint160).max) {\n revert SafeCastOverflowedUintDowncast(160, value);\n }\n return uint160(value);\n }\n\n /**\n * @dev Returns the downcasted uint152 from uint256, reverting on\n * overflow (when the input is greater than largest uint152).\n *\n * Counterpart to Solidity's `uint152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n */\n function toUint152(uint256 value) internal pure returns (uint152) {\n if (value > type(uint152).max) {\n revert SafeCastOverflowedUintDowncast(152, value);\n }\n return uint152(value);\n }\n\n /**\n * @dev Returns the downcasted uint144 from uint256, reverting on\n * overflow (when the input is greater than largest uint144).\n *\n * Counterpart to Solidity's `uint144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n */\n function toUint144(uint256 value) internal pure returns (uint144) {\n if (value > type(uint144).max) {\n revert SafeCastOverflowedUintDowncast(144, value);\n }\n return uint144(value);\n }\n\n /**\n * @dev Returns the downcasted uint136 from uint256, reverting on\n * overflow (when the input is greater than largest uint136).\n *\n * Counterpart to Solidity's `uint136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n */\n function toUint136(uint256 value) internal pure returns (uint136) {\n if (value > type(uint136).max) {\n revert SafeCastOverflowedUintDowncast(136, value);\n }\n return uint136(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n if (value > type(uint128).max) {\n revert SafeCastOverflowedUintDowncast(128, value);\n }\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint120 from uint256, reverting on\n * overflow (when the input is greater than largest uint120).\n *\n * Counterpart to Solidity's `uint120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n */\n function toUint120(uint256 value) internal pure returns (uint120) {\n if (value > type(uint120).max) {\n revert SafeCastOverflowedUintDowncast(120, value);\n }\n return uint120(value);\n }\n\n /**\n * @dev Returns the downcasted uint112 from uint256, reverting on\n * overflow (when the input is greater than largest uint112).\n *\n * Counterpart to Solidity's `uint112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n */\n function toUint112(uint256 value) internal pure returns (uint112) {\n if (value > type(uint112).max) {\n revert SafeCastOverflowedUintDowncast(112, value);\n }\n return uint112(value);\n }\n\n /**\n * @dev Returns the downcasted uint104 from uint256, reverting on\n * overflow (when the input is greater than largest uint104).\n *\n * Counterpart to Solidity's `uint104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n */\n function toUint104(uint256 value) internal pure returns (uint104) {\n if (value > type(uint104).max) {\n revert SafeCastOverflowedUintDowncast(104, value);\n }\n return uint104(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n if (value > type(uint96).max) {\n revert SafeCastOverflowedUintDowncast(96, value);\n }\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint88 from uint256, reverting on\n * overflow (when the input is greater than largest uint88).\n *\n * Counterpart to Solidity's `uint88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n */\n function toUint88(uint256 value) internal pure returns (uint88) {\n if (value > type(uint88).max) {\n revert SafeCastOverflowedUintDowncast(88, value);\n }\n return uint88(value);\n }\n\n /**\n * @dev Returns the downcasted uint80 from uint256, reverting on\n * overflow (when the input is greater than largest uint80).\n *\n * Counterpart to Solidity's `uint80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n */\n function toUint80(uint256 value) internal pure returns (uint80) {\n if (value > type(uint80).max) {\n revert SafeCastOverflowedUintDowncast(80, value);\n }\n return uint80(value);\n }\n\n /**\n * @dev Returns the downcasted uint72 from uint256, reverting on\n * overflow (when the input is greater than largest uint72).\n *\n * Counterpart to Solidity's `uint72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n */\n function toUint72(uint256 value) internal pure returns (uint72) {\n if (value > type(uint72).max) {\n revert SafeCastOverflowedUintDowncast(72, value);\n }\n return uint72(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n if (value > type(uint64).max) {\n revert SafeCastOverflowedUintDowncast(64, value);\n }\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint56 from uint256, reverting on\n * overflow (when the input is greater than largest uint56).\n *\n * Counterpart to Solidity's `uint56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n */\n function toUint56(uint256 value) internal pure returns (uint56) {\n if (value > type(uint56).max) {\n revert SafeCastOverflowedUintDowncast(56, value);\n }\n return uint56(value);\n }\n\n /**\n * @dev Returns the downcasted uint48 from uint256, reverting on\n * overflow (when the input is greater than largest uint48).\n *\n * Counterpart to Solidity's `uint48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n */\n function toUint48(uint256 value) internal pure returns (uint48) {\n if (value > type(uint48).max) {\n revert SafeCastOverflowedUintDowncast(48, value);\n }\n return uint48(value);\n }\n\n /**\n * @dev Returns the downcasted uint40 from uint256, reverting on\n * overflow (when the input is greater than largest uint40).\n *\n * Counterpart to Solidity's `uint40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n */\n function toUint40(uint256 value) internal pure returns (uint40) {\n if (value > type(uint40).max) {\n revert SafeCastOverflowedUintDowncast(40, value);\n }\n return uint40(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n if (value > type(uint32).max) {\n revert SafeCastOverflowedUintDowncast(32, value);\n }\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint24 from uint256, reverting on\n * overflow (when the input is greater than largest uint24).\n *\n * Counterpart to Solidity's `uint24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n */\n function toUint24(uint256 value) internal pure returns (uint24) {\n if (value > type(uint24).max) {\n revert SafeCastOverflowedUintDowncast(24, value);\n }\n return uint24(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n if (value > type(uint16).max) {\n revert SafeCastOverflowedUintDowncast(16, value);\n }\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n if (value > type(uint8).max) {\n revert SafeCastOverflowedUintDowncast(8, value);\n }\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n if (value < 0) {\n revert SafeCastOverflowedIntToUint(value);\n }\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int248 from int256, reverting on\n * overflow (when the input is less than smallest int248 or\n * greater than largest int248).\n *\n * Counterpart to Solidity's `int248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n */\n function toInt248(int256 value) internal pure returns (int248 downcasted) {\n downcasted = int248(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(248, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int240 from int256, reverting on\n * overflow (when the input is less than smallest int240 or\n * greater than largest int240).\n *\n * Counterpart to Solidity's `int240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n */\n function toInt240(int256 value) internal pure returns (int240 downcasted) {\n downcasted = int240(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(240, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int232 from int256, reverting on\n * overflow (when the input is less than smallest int232 or\n * greater than largest int232).\n *\n * Counterpart to Solidity's `int232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n */\n function toInt232(int256 value) internal pure returns (int232 downcasted) {\n downcasted = int232(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(232, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int224 from int256, reverting on\n * overflow (when the input is less than smallest int224 or\n * greater than largest int224).\n *\n * Counterpart to Solidity's `int224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toInt224(int256 value) internal pure returns (int224 downcasted) {\n downcasted = int224(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(224, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int216 from int256, reverting on\n * overflow (when the input is less than smallest int216 or\n * greater than largest int216).\n *\n * Counterpart to Solidity's `int216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n */\n function toInt216(int256 value) internal pure returns (int216 downcasted) {\n downcasted = int216(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(216, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int208 from int256, reverting on\n * overflow (when the input is less than smallest int208 or\n * greater than largest int208).\n *\n * Counterpart to Solidity's `int208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n */\n function toInt208(int256 value) internal pure returns (int208 downcasted) {\n downcasted = int208(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(208, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int200 from int256, reverting on\n * overflow (when the input is less than smallest int200 or\n * greater than largest int200).\n *\n * Counterpart to Solidity's `int200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n */\n function toInt200(int256 value) internal pure returns (int200 downcasted) {\n downcasted = int200(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(200, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int192 from int256, reverting on\n * overflow (when the input is less than smallest int192 or\n * greater than largest int192).\n *\n * Counterpart to Solidity's `int192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n */\n function toInt192(int256 value) internal pure returns (int192 downcasted) {\n downcasted = int192(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(192, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int184 from int256, reverting on\n * overflow (when the input is less than smallest int184 or\n * greater than largest int184).\n *\n * Counterpart to Solidity's `int184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n */\n function toInt184(int256 value) internal pure returns (int184 downcasted) {\n downcasted = int184(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(184, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int176 from int256, reverting on\n * overflow (when the input is less than smallest int176 or\n * greater than largest int176).\n *\n * Counterpart to Solidity's `int176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n */\n function toInt176(int256 value) internal pure returns (int176 downcasted) {\n downcasted = int176(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(176, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int168 from int256, reverting on\n * overflow (when the input is less than smallest int168 or\n * greater than largest int168).\n *\n * Counterpart to Solidity's `int168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n */\n function toInt168(int256 value) internal pure returns (int168 downcasted) {\n downcasted = int168(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(168, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int160 from int256, reverting on\n * overflow (when the input is less than smallest int160 or\n * greater than largest int160).\n *\n * Counterpart to Solidity's `int160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n */\n function toInt160(int256 value) internal pure returns (int160 downcasted) {\n downcasted = int160(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(160, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int152 from int256, reverting on\n * overflow (when the input is less than smallest int152 or\n * greater than largest int152).\n *\n * Counterpart to Solidity's `int152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n */\n function toInt152(int256 value) internal pure returns (int152 downcasted) {\n downcasted = int152(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(152, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int144 from int256, reverting on\n * overflow (when the input is less than smallest int144 or\n * greater than largest int144).\n *\n * Counterpart to Solidity's `int144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n */\n function toInt144(int256 value) internal pure returns (int144 downcasted) {\n downcasted = int144(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(144, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int136 from int256, reverting on\n * overflow (when the input is less than smallest int136 or\n * greater than largest int136).\n *\n * Counterpart to Solidity's `int136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n */\n function toInt136(int256 value) internal pure returns (int136 downcasted) {\n downcasted = int136(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(136, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toInt128(int256 value) internal pure returns (int128 downcasted) {\n downcasted = int128(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(128, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int120 from int256, reverting on\n * overflow (when the input is less than smallest int120 or\n * greater than largest int120).\n *\n * Counterpart to Solidity's `int120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n */\n function toInt120(int256 value) internal pure returns (int120 downcasted) {\n downcasted = int120(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(120, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int112 from int256, reverting on\n * overflow (when the input is less than smallest int112 or\n * greater than largest int112).\n *\n * Counterpart to Solidity's `int112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n */\n function toInt112(int256 value) internal pure returns (int112 downcasted) {\n downcasted = int112(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(112, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int104 from int256, reverting on\n * overflow (when the input is less than smallest int104 or\n * greater than largest int104).\n *\n * Counterpart to Solidity's `int104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n */\n function toInt104(int256 value) internal pure returns (int104 downcasted) {\n downcasted = int104(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(104, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int96 from int256, reverting on\n * overflow (when the input is less than smallest int96 or\n * greater than largest int96).\n *\n * Counterpart to Solidity's `int96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toInt96(int256 value) internal pure returns (int96 downcasted) {\n downcasted = int96(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(96, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int88 from int256, reverting on\n * overflow (when the input is less than smallest int88 or\n * greater than largest int88).\n *\n * Counterpart to Solidity's `int88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n */\n function toInt88(int256 value) internal pure returns (int88 downcasted) {\n downcasted = int88(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(88, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int80 from int256, reverting on\n * overflow (when the input is less than smallest int80 or\n * greater than largest int80).\n *\n * Counterpart to Solidity's `int80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n */\n function toInt80(int256 value) internal pure returns (int80 downcasted) {\n downcasted = int80(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(80, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int72 from int256, reverting on\n * overflow (when the input is less than smallest int72 or\n * greater than largest int72).\n *\n * Counterpart to Solidity's `int72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n */\n function toInt72(int256 value) internal pure returns (int72 downcasted) {\n downcasted = int72(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(72, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toInt64(int256 value) internal pure returns (int64 downcasted) {\n downcasted = int64(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(64, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int56 from int256, reverting on\n * overflow (when the input is less than smallest int56 or\n * greater than largest int56).\n *\n * Counterpart to Solidity's `int56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n */\n function toInt56(int256 value) internal pure returns (int56 downcasted) {\n downcasted = int56(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(56, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int48 from int256, reverting on\n * overflow (when the input is less than smallest int48 or\n * greater than largest int48).\n *\n * Counterpart to Solidity's `int48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n */\n function toInt48(int256 value) internal pure returns (int48 downcasted) {\n downcasted = int48(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(48, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int40 from int256, reverting on\n * overflow (when the input is less than smallest int40 or\n * greater than largest int40).\n *\n * Counterpart to Solidity's `int40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n */\n function toInt40(int256 value) internal pure returns (int40 downcasted) {\n downcasted = int40(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(40, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toInt32(int256 value) internal pure returns (int32 downcasted) {\n downcasted = int32(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(32, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int24 from int256, reverting on\n * overflow (when the input is less than smallest int24 or\n * greater than largest int24).\n *\n * Counterpart to Solidity's `int24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n */\n function toInt24(int256 value) internal pure returns (int24 downcasted) {\n downcasted = int24(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(24, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toInt16(int256 value) internal pure returns (int16 downcasted) {\n downcasted = int16(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(16, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n */\n function toInt8(int256 value) internal pure returns (int8 downcasted) {\n downcasted = int8(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(8, value);\n }\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n if (value > uint256(type(int256).max)) {\n revert SafeCastOverflowedUintToInt(value);\n }\n return int256(value);\n }\n\n /**\n * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.\n */\n function toUint(bool b) internal pure returns (uint256 u) {\n assembly (\"memory-safe\") {\n u := iszero(iszero(b))\n }\n }\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/Panic.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Helper library for emitting standardized panic codes.\n *\n * ```solidity\n * contract Example {\n * using Panic for uint256;\n *\n * // Use any of the declared internal constants\n * function foo() { Panic.GENERIC.panic(); }\n *\n * // Alternatively\n * function foo() { Panic.panic(Panic.GENERIC); }\n * }\n * ```\n *\n * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].\n *\n * _Available since v5.1._\n */\n// slither-disable-next-line unused-state\nlibrary Panic {\n /// @dev generic / unspecified error\n uint256 internal constant GENERIC = 0x00;\n /// @dev used by the assert() builtin\n uint256 internal constant ASSERT = 0x01;\n /// @dev arithmetic underflow or overflow\n uint256 internal constant UNDER_OVERFLOW = 0x11;\n /// @dev division or modulo by zero\n uint256 internal constant DIVISION_BY_ZERO = 0x12;\n /// @dev enum conversion error\n uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;\n /// @dev invalid encoding in storage\n uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;\n /// @dev empty array pop\n uint256 internal constant EMPTY_ARRAY_POP = 0x31;\n /// @dev array out of bounds access\n uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;\n /// @dev resource error (too large allocation or too large array)\n uint256 internal constant RESOURCE_ERROR = 0x41;\n /// @dev calling invalid internal function\n uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;\n\n /// @dev Reverts with a panic code. Recommended to use with\n /// the internal constants with predefined codes.\n function panic(uint256 code) internal pure {\n assembly (\"memory-safe\") {\n mstore(0x00, 0x4e487b71)\n mstore(0x20, code)\n revert(0x1c, 0x24)\n }\n }\n}" + }, + "lib/rooster/v2-common/libraries/Constants.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\n// factory contraints on pools\nuint8 constant MAX_PROTOCOL_FEE_RATIO_D3 = 0.25e3; // 25%\nuint256 constant MAX_PROTOCOL_LENDING_FEE_RATE_D18 = 0.02e18; // 2%\nuint64 constant MAX_POOL_FEE_D18 = 0.9e18; // 90%\nuint64 constant MIN_LOOKBACK = 1 seconds;\n\n// pool constraints\nuint8 constant NUMBER_OF_KINDS = 4;\nint32 constant NUMBER_OF_KINDS_32 = int32(int8(NUMBER_OF_KINDS));\nuint256 constant MAX_TICK = 322_378; // max price 1e14 in D18 scale\nint32 constant MAX_TICK_32 = int32(int256(MAX_TICK));\nint32 constant MIN_TICK_32 = int32(-int256(MAX_TICK));\nuint256 constant MAX_BINS_TO_MERGE = 3;\nuint128 constant MINIMUM_LIQUIDITY = 1e8;\n\n// accessor named constants\nuint8 constant ALL_KINDS_MASK = 0xF; // 0b1111\nuint8 constant PERMISSIONED_LIQUIDITY_MASK = 0x10; // 0b010000\nuint8 constant PERMISSIONED_SWAP_MASK = 0x20; // 0b100000\nuint8 constant OPTIONS_MASK = ALL_KINDS_MASK | PERMISSIONED_LIQUIDITY_MASK | PERMISSIONED_SWAP_MASK; // 0b111111\n\n// named values\naddress constant MERGED_LP_BALANCE_ADDRESS = address(0);\nuint256 constant MERGED_LP_BALANCE_SUBACCOUNT = 0;\nuint128 constant ONE = 1e18;\nuint128 constant ONE_SQUARED = 1e36;\nint256 constant INT256_ONE = 1e18;\nuint256 constant ONE_D8 = 1e8;\nuint256 constant ONE_D3 = 1e3;\nint40 constant INT_ONE_D8 = 1e8;\nint40 constant HALF_TICK_D8 = 0.5e8;\nuint8 constant DEFAULT_DECIMALS = 18;\nuint256 constant DEFAULT_SCALE = 1;\nbytes constant EMPTY_PRICE_BREAKS = hex\"010000000000000000000000\";" + }, + "lib/rooster/v2-common/libraries/Math.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport {Math as OzMath} from \"../../openzeppelin-custom/contracts/utils/math/Math.sol\";\n\nimport {ONE, DEFAULT_SCALE, DEFAULT_DECIMALS, INT_ONE_D8, ONE_SQUARED} from \"./Constants.sol\";\n\n/**\n * @notice Math functions.\n */\nlibrary Math {\n /**\n * @notice Returns the lesser of two values.\n * @param x First uint256 value.\n * @param y Second uint256 value.\n */\n function min(uint256 x, uint256 y) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), lt(y, x)))\n }\n }\n\n /**\n * @notice Returns the lesser of two uint128 values.\n * @param x First uint128 value.\n * @param y Second uint128 value.\n */\n function min128(uint128 x, uint128 y) internal pure returns (uint128 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), lt(y, x)))\n }\n }\n\n /**\n * @notice Returns the lesser of two int256 values.\n * @param x First int256 value.\n * @param y Second int256 value.\n */\n function min(int256 x, int256 y) internal pure returns (int256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), slt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two uint256 values.\n * @param x First uint256 value.\n * @param y Second uint256 value.\n */\n function max(uint256 x, uint256 y) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), gt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two int256 values.\n * @param x First int256 value.\n * @param y Second int256 value.\n */\n function max(int256 x, int256 y) internal pure returns (int256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), sgt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two uint128 values.\n * @param x First uint128 value.\n * @param y Second uint128 value.\n */\n function max128(uint128 x, uint128 y) internal pure returns (uint128 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), gt(y, x)))\n }\n }\n\n /**\n * @notice Thresholds a value to be within the specified bounds.\n * @param value The value to bound.\n * @param lowerLimit The minimum allowable value.\n * @param upperLimit The maximum allowable value.\n */\n function boundValue(\n uint256 value,\n uint256 lowerLimit,\n uint256 upperLimit\n ) internal pure returns (uint256 outputValue) {\n outputValue = min(max(value, lowerLimit), upperLimit);\n }\n\n /**\n * @notice Returns the difference between two uint128 values or zero if the result would be negative.\n * @param x The minuend.\n * @param y The subtrahend.\n */\n function clip128(uint128 x, uint128 y) internal pure returns (uint128) {\n unchecked {\n return x < y ? 0 : x - y;\n }\n }\n\n /**\n * @notice Returns the difference between two uint256 values or zero if the result would be negative.\n * @param x The minuend.\n * @param y The subtrahend.\n */\n function clip(uint256 x, uint256 y) internal pure returns (uint256) {\n unchecked {\n return x < y ? 0 : x - y;\n }\n }\n\n /**\n * @notice Divides one uint256 by another, rounding down to the nearest\n * integer.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divFloor(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivFloor(x, ONE, y);\n }\n\n /**\n * @notice Divides one uint256 by another, rounding up to the nearest integer.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divCeil(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivCeil(x, ONE, y);\n }\n\n /**\n * @notice Multiplies two uint256 values and then divides by ONE, rounding down.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulFloor(uint256 x, uint256 y) internal pure returns (uint256) {\n return OzMath.mulDiv(x, y, ONE);\n }\n\n /**\n * @notice Multiplies two uint256 values and then divides by ONE, rounding up.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulCeil(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivCeil(x, y, ONE);\n }\n\n /**\n * @notice Calculates the multiplicative inverse of a uint256, rounding down.\n * @param x The value to invert.\n */\n function invFloor(uint256 x) internal pure returns (uint256) {\n unchecked {\n return ONE_SQUARED / x;\n }\n }\n\n /**\n * @notice Calculates the multiplicative inverse of a uint256, rounding up.\n * @param denominator The value to invert.\n */\n function invCeil(uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // divide z - 1 by the denominator and add 1.\n z := add(div(sub(ONE_SQUARED, 1), denominator), 1)\n }\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding down.\n * @param x The multiplicand.\n * @param y The multiplier.\n * @param k The divisor.\n */\n function mulDivFloor(uint256 x, uint256 y, uint256 k) internal pure returns (uint256 result) {\n result = OzMath.mulDiv(x, y, max(1, k));\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding up if there's a remainder.\n * @param x The multiplicand.\n * @param y The multiplier.\n * @param k The divisor.\n */\n function mulDivCeil(uint256 x, uint256 y, uint256 k) internal pure returns (uint256 result) {\n result = mulDivFloor(x, y, k);\n if (mulmod(x, y, max(1, k)) != 0) result = result + 1;\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding\n * down. Will revert if `x * y` is larger than `type(uint256).max`.\n * @param x The first operand for multiplication.\n * @param y The second operand for multiplication.\n * @param denominator The divisor after multiplication.\n */\n function mulDivDown(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // Store x * y in z for now.\n z := mul(x, y)\n if iszero(denominator) {\n denominator := 1\n }\n\n if iszero(or(iszero(x), eq(div(z, x), y))) {\n revert(0, 0)\n }\n\n // Divide z by the denominator.\n z := div(z, denominator)\n }\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding\n * up. Will revert if `x * y` is larger than `type(uint256).max`.\n * @param x The first operand for multiplication.\n * @param y The second operand for multiplication.\n * @param denominator The divisor after multiplication.\n */\n function mulDivUp(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // Store x * y in z for now.\n z := mul(x, y)\n if iszero(denominator) {\n denominator := 1\n }\n\n if iszero(or(iszero(x), eq(div(z, x), y))) {\n revert(0, 0)\n }\n\n // First, divide z - 1 by the denominator and add 1.\n // We allow z - 1 to underflow if z is 0, because we multiply the\n // end result by 0 if z is zero, ensuring we return 0 if z is zero.\n z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))\n }\n }\n\n /**\n * @notice Multiplies a uint256 by another and divides by a constant,\n * rounding down. Will revert if `x * y` is larger than\n * `type(uint256).max`.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulDown(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivDown(x, y, ONE);\n }\n\n /**\n * @notice Divides a uint256 by another, rounding down the result. Will\n * revert if `x * 1e18` is larger than `type(uint256).max`.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divDown(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivDown(x, ONE, y);\n }\n\n /**\n * @notice Divides a uint256 by another, rounding up the result. Will\n * revert if `x * 1e18` is larger than `type(uint256).max`.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divUp(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivUp(x, ONE, y);\n }\n\n /**\n * @notice Scales a number based on a difference in decimals from a default.\n * @param decimals The new decimal precision.\n */\n function scale(uint8 decimals) internal pure returns (uint256) {\n unchecked {\n if (decimals == DEFAULT_DECIMALS) {\n return DEFAULT_SCALE;\n } else {\n return 10 ** (DEFAULT_DECIMALS - decimals);\n }\n }\n }\n\n /**\n * @notice Adjusts a scaled amount to the token decimal scale.\n * @param amount The scaled amount.\n * @param scaleFactor The scaling factor to adjust by.\n * @param ceil Whether to round up (true) or down (false).\n */\n function ammScaleToTokenScale(uint256 amount, uint256 scaleFactor, bool ceil) internal pure returns (uint256 z) {\n unchecked {\n if (scaleFactor == DEFAULT_SCALE || amount == 0) {\n return amount;\n } else {\n if (!ceil) return amount / scaleFactor;\n assembly (\"memory-safe\") {\n z := add(div(sub(amount, 1), scaleFactor), 1)\n }\n }\n }\n }\n\n /**\n * @notice Adjusts a token amount to the D18 AMM scale.\n * @param amount The amount in token scale.\n * @param scaleFactor The scale factor for adjustment.\n */\n function tokenScaleToAmmScale(uint256 amount, uint256 scaleFactor) internal pure returns (uint256) {\n if (scaleFactor == DEFAULT_SCALE) {\n return amount;\n } else {\n return amount * scaleFactor;\n }\n }\n\n /**\n * @notice Returns the absolute value of a signed 32-bit integer.\n * @param x The integer to take the absolute value of.\n */\n function abs32(int32 x) internal pure returns (uint32) {\n unchecked {\n return uint32(x < 0 ? -x : x);\n }\n }\n\n /**\n * @notice Returns the absolute value of a signed 256-bit integer.\n * @param x The integer to take the absolute value of.\n */\n function abs(int256 x) internal pure returns (uint256) {\n unchecked {\n return uint256(x < 0 ? -x : x);\n }\n }\n\n /**\n * @notice Calculates the integer square root of a uint256 rounded down.\n * @param x The number to take the square root of.\n */\n function sqrt(uint256 x) internal pure returns (uint256 z) {\n // from https://github.com/transmissions11/solmate/blob/e8f96f25d48fe702117ce76c79228ca4f20206cb/src/utils/FixedPointMathLib.sol\n assembly (\"memory-safe\") {\n let y := x\n z := 181\n\n if iszero(lt(y, 0x10000000000000000000000000000000000)) {\n y := shr(128, y)\n z := shl(64, z)\n }\n if iszero(lt(y, 0x1000000000000000000)) {\n y := shr(64, y)\n z := shl(32, z)\n }\n if iszero(lt(y, 0x10000000000)) {\n y := shr(32, y)\n z := shl(16, z)\n }\n if iszero(lt(y, 0x1000000)) {\n y := shr(16, y)\n z := shl(8, z)\n }\n\n z := shr(18, mul(z, add(y, 65536)))\n\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n\n z := sub(z, lt(div(x, z), z))\n }\n }\n\n /**\n * @notice Computes the floor of a D8-scaled number as an int32, ignoring\n * potential overflow in the cast.\n * @param val The D8-scaled number.\n */\n function floorD8Unchecked(int256 val) internal pure returns (int32) {\n int32 val32;\n bool check;\n unchecked {\n val32 = int32(val / INT_ONE_D8);\n check = (val < 0 && val % INT_ONE_D8 != 0);\n }\n return check ? val32 - 1 : val32;\n }\n}" + }, + "lib/rooster/v2-common/libraries/TickMath.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport {Math as OzMath} from \"../../openzeppelin-custom/contracts/utils/math/Math.sol\";\nimport {Math} from \"./Math.sol\";\nimport {MAX_TICK, ONE} from \"./Constants.sol\";\n\n/**\n * @notice Math functions related to tick operations.\n */\n// slither-disable-start divide-before-multiply\nlibrary TickMath {\n using Math for uint256;\n\n error TickMaxExceeded(int256 tick);\n\n /**\n * @notice Compute the lower and upper sqrtPrice of a tick.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n */\n function tickSqrtPrices(\n uint256 tickSpacing,\n int32 _tick\n ) internal pure returns (uint256 sqrtLowerPrice, uint256 sqrtUpperPrice) {\n unchecked {\n sqrtLowerPrice = tickSqrtPrice(tickSpacing, _tick);\n sqrtUpperPrice = tickSqrtPrice(tickSpacing, _tick + 1);\n }\n }\n\n /**\n * @notice Compute the base tick value from the pool tick and the\n * tickSpacing. Revert if base tick is beyond the max tick boundary.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n */\n function subTickIndex(uint256 tickSpacing, int32 _tick) internal pure returns (uint32 subTick) {\n subTick = Math.abs32(_tick);\n subTick *= uint32(tickSpacing);\n if (subTick > MAX_TICK) {\n revert TickMaxExceeded(_tick);\n }\n }\n\n /**\n * @notice Calculate the square root price for a given tick and tick spacing.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n * @return _result The square root price.\n */\n function tickSqrtPrice(uint256 tickSpacing, int32 _tick) internal pure returns (uint256 _result) {\n unchecked {\n uint256 tick = subTickIndex(tickSpacing, _tick);\n\n uint256 ratio = tick & 0x1 != 0 ? 0xfffcb933bd6fad9d3af5f0b9f25db4d6 : 0x100000000000000000000000000000000;\n if (tick & 0x2 != 0) ratio = (ratio * 0xfff97272373d41fd789c8cb37ffcaa1c) >> 128;\n if (tick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656ac9229c67059486f389) >> 128;\n if (tick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e81259b3cddc7a064941) >> 128;\n if (tick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f67b19e8887e0bd251eb7) >> 128;\n if (tick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98cd2e57b660be99eb2c4a) >> 128;\n if (tick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c9838804e327cb417cafcb) >> 128;\n if (tick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99d51e2cc356c2f617dbe0) >> 128;\n if (tick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900aecf64236ab31f1f9dcb5) >> 128;\n if (tick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac4d9194200696907cf2e37) >> 128;\n if (tick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b88206f8abe8a3b44dd9be) >> 128;\n if (tick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c578ef4f1d17b2b235d480) >> 128;\n if (tick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd254ee83bdd3f248e7e785e) >> 128;\n if (tick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d8f7dd10e744d913d033333) >> 128;\n if (tick & 0x4000 != 0) ratio = (ratio * 0x70d869a156ddd32a39e257bc3f50aa9b) >> 128;\n if (tick & 0x8000 != 0) ratio = (ratio * 0x31be135f97da6e09a19dc367e3b6da40) >> 128;\n if (tick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7e5a9780b0cc4e25d61a56) >> 128;\n if (tick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedbcb3a6ccb7ce618d14225) >> 128;\n if (tick & 0x40000 != 0) ratio = (ratio * 0x2216e584f630389b2052b8db590e) >> 128;\n if (_tick > 0) ratio = type(uint256).max / ratio;\n _result = (ratio * ONE) >> 128;\n }\n }\n\n /**\n * @notice Calculate liquidity of a tick.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n */\n function getTickL(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n ) internal pure returns (uint256 liquidity) {\n // known:\n // - sqrt price values are different\n // - reserveA and reserveB fit in 128 bit\n // - sqrt price is in (1e-7, 1e7)\n // - D18 max for uint256 is 1.15e59\n // - D18 min is 1e-18\n\n unchecked {\n // diff is in (5e-12, 4e6); max tick spacing is 10_000\n uint256 diff = sqrtUpperTickPrice - sqrtLowerTickPrice;\n\n // Need to maximize precision by shifting small values A and B up so\n // that they use more of the available bit range. Two constraints to\n // consider: we need A * B * diff / sqrtPrice to be bigger than 1e-18\n // when the bump is not in play. This constrains the threshold for\n // bumping to be at least 77 bit; ie, either a or b needs 2^77 which\n // means that term A * B * diff / sqrtPrice > 1e-18.\n //\n // At the other end, the second constraint is that b^2 needs to fit in\n // a 256-bit number, so, post bump, the max reserve value needs to be\n // less than 6e22. With a 78-bit threshold and a 57-bit bump, we have A\n // and B are in (1.4e-1, 4.4e22 (2^(78+57))) with bump, and one of A or\n // B is at least 2^78 without the bump, but the other reserve value may\n // be as small as 1 wei.\n uint256 precisionBump = 0;\n if ((reserveA >> 78) == 0 && (reserveB >> 78) == 0) {\n precisionBump = 57;\n reserveA <<= precisionBump;\n reserveB <<= precisionBump;\n }\n\n if (reserveB == 0) return Math.divDown(reserveA, diff) >> precisionBump;\n if (reserveA == 0)\n return Math.mulDivDown(reserveB.mulDown(sqrtLowerTickPrice), sqrtUpperTickPrice, diff) >> precisionBump;\n\n // b is in (7.2e-9 (2^57 / 1e7 / 2), 2.8e29 (2^(78+57) * 1e7 / 2)) with bump\n // b is in a subset of the same range without bump\n uint256 b = (reserveA.divDown(sqrtUpperTickPrice) + reserveB.mulDown(sqrtLowerTickPrice)) >> 1;\n\n // b^2 is in (5.1e-17, 4.8e58); and will not overflow on either end;\n // A*B is in (3e-13 (2^78 / 1e18 * 1e-18), 1.9e45) without bump and is in a subset range with bump\n // A*B*diff/sqrtUpper is in (1.5e-17 (3e-13 * 5e-12 * 1e7), 7.6e58);\n\n // Since b^2 is at the upper edge of the precision range, we are not\n // able to multiply the argument of the sqrt by 1e18, instead, we move\n // this factor outside of the sqrt. The resulting loss of precision\n // means that this liquidity value is a lower bound on the tick\n // liquidity\n return\n OzMath.mulDiv(\n b +\n Math.sqrt(\n (OzMath.mulDiv(b, b, ONE) +\n OzMath.mulDiv(reserveB.mulFloor(reserveA), diff, sqrtUpperTickPrice))\n ) *\n 1e9,\n sqrtUpperTickPrice,\n diff\n ) >> precisionBump;\n }\n }\n\n /**\n * @notice Calculate square root price of a tick. Returns left edge of the\n * tick if the tick has no reserves.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n * @return sqrtPrice The calculated square root price.\n */\n function getSqrtPrice(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice,\n uint256 liquidity\n ) internal pure returns (uint256 sqrtPrice) {\n unchecked {\n if (reserveA == 0) {\n return sqrtLowerTickPrice;\n }\n if (reserveB == 0) {\n return sqrtUpperTickPrice;\n }\n sqrtPrice = Math.sqrt(\n ONE *\n (reserveA + liquidity.mulDown(sqrtLowerTickPrice)).divDown(\n reserveB + liquidity.divDown(sqrtUpperTickPrice)\n )\n );\n sqrtPrice = Math.boundValue(sqrtPrice, sqrtLowerTickPrice, sqrtUpperTickPrice);\n }\n }\n\n /**\n * @notice Calculate square root price of a tick. Returns left edge of the\n * tick if the tick has no reserves.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n * @return sqrtPrice The calculated square root price.\n * @return liquidity The calculated liquidity.\n */\n function getTickSqrtPriceAndL(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n ) internal pure returns (uint256 sqrtPrice, uint256 liquidity) {\n liquidity = getTickL(reserveA, reserveB, sqrtLowerTickPrice, sqrtUpperTickPrice);\n sqrtPrice = getSqrtPrice(reserveA, reserveB, sqrtLowerTickPrice, sqrtUpperTickPrice, liquidity);\n }\n}\n// slither-disable-end divide-before-multiply" + }, + "solidity-bytes-utils/contracts/BytesLib.sol": { + "content": "// SPDX-License-Identifier: Unlicense\n/*\n * @title Solidity Bytes Arrays Utils\n * @author Gonçalo Sá \n *\n * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.\n * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.\n */\npragma solidity >=0.8.0 <0.9.0;\n\n\nlibrary BytesLib {\n function concat(\n bytes memory _preBytes,\n bytes memory _postBytes\n )\n internal\n pure\n returns (bytes memory)\n {\n bytes memory tempBytes;\n\n assembly {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // Store the length of the first bytes array at the beginning of\n // the memory for tempBytes.\n let length := mload(_preBytes)\n mstore(tempBytes, length)\n\n // Maintain a memory counter for the current write location in the\n // temp bytes array by adding the 32 bytes for the array length to\n // the starting location.\n let mc := add(tempBytes, 0x20)\n // Stop copying when the memory counter reaches the length of the\n // first bytes array.\n let end := add(mc, length)\n\n for {\n // Initialize a copy counter to the start of the _preBytes data,\n // 32 bytes into its memory.\n let cc := add(_preBytes, 0x20)\n } lt(mc, end) {\n // Increase both counters by 32 bytes each iteration.\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // Write the _preBytes data into the tempBytes memory 32 bytes\n // at a time.\n mstore(mc, mload(cc))\n }\n\n // Add the length of _postBytes to the current length of tempBytes\n // and store it as the new length in the first 32 bytes of the\n // tempBytes memory.\n length := mload(_postBytes)\n mstore(tempBytes, add(length, mload(tempBytes)))\n\n // Move the memory counter back from a multiple of 0x20 to the\n // actual end of the _preBytes data.\n mc := end\n // Stop copying when the memory counter reaches the new combined\n // length of the arrays.\n end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n // Update the free-memory pointer by padding our last write location\n // to 32 bytes: add 31 bytes to the end of tempBytes to move to the\n // next 32 byte block, then round down to the nearest multiple of\n // 32. If the sum of the length of the two arrays is zero then add\n // one before rounding down to leave a blank 32 bytes (the length block with 0).\n mstore(0x40, and(\n add(add(end, iszero(add(length, mload(_preBytes)))), 31),\n not(31) // Round down to the nearest 32 bytes.\n ))\n }\n\n return tempBytes;\n }\n\n function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {\n assembly {\n // Read the first 32 bytes of _preBytes storage, which is the length\n // of the array. (We don't need to use the offset into the slot\n // because arrays use the entire slot.)\n let fslot := sload(_preBytes.slot)\n // Arrays of 31 bytes or less have an even value in their slot,\n // while longer arrays have an odd value. The actual length is\n // the slot divided by two for odd values, and the lowest order\n // byte divided by two for even values.\n // If the slot is even, bitwise and the slot with 255 and divide by\n // two to get the length. If the slot is odd, bitwise and the slot\n // with -1 and divide by two.\n let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)\n let mlength := mload(_postBytes)\n let newlength := add(slength, mlength)\n // slength can contain both the length and contents of the array\n // if length < 32 bytes so let's prepare for that\n // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage\n switch add(lt(slength, 32), lt(newlength, 32))\n case 2 {\n // Since the new array still fits in the slot, we just need to\n // update the contents of the slot.\n // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length\n sstore(\n _preBytes.slot,\n // all the modifications to the slot are inside this\n // next block\n add(\n // we can just add to the slot contents because the\n // bytes we want to change are the LSBs\n fslot,\n add(\n mul(\n div(\n // load the bytes from memory\n mload(add(_postBytes, 0x20)),\n // zero all bytes to the right\n exp(0x100, sub(32, mlength))\n ),\n // and now shift left the number of bytes to\n // leave space for the length in the slot\n exp(0x100, sub(32, newlength))\n ),\n // increase length by the double of the memory\n // bytes length\n mul(mlength, 2)\n )\n )\n )\n }\n case 1 {\n // The stored value fits in the slot, but the combined value\n // will exceed it.\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n let sc := add(keccak256(0x0, 0x20), div(slength, 32))\n\n // save new length\n sstore(_preBytes.slot, add(mul(newlength, 2), 1))\n\n // The contents of the _postBytes array start 32 bytes into\n // the structure. Our first read should obtain the `submod`\n // bytes that can fit into the unused space in the last word\n // of the stored array. To get this, we read 32 bytes starting\n // from `submod`, so the data we read overlaps with the array\n // contents by `submod` bytes. Masking the lowest-order\n // `submod` bytes allows us to add that value directly to the\n // stored value.\n\n let submod := sub(32, slength)\n let mc := add(_postBytes, submod)\n let end := add(_postBytes, mlength)\n let mask := sub(exp(0x100, submod), 1)\n\n sstore(\n sc,\n add(\n and(\n fslot,\n 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00\n ),\n and(mload(mc), mask)\n )\n )\n\n for {\n mc := add(mc, 0x20)\n sc := add(sc, 1)\n } lt(mc, end) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n sstore(sc, mload(mc))\n }\n\n mask := exp(0x100, sub(mc, end))\n\n sstore(sc, mul(div(mload(mc), mask), mask))\n }\n default {\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n // Start copying to the last used word of the stored array.\n let sc := add(keccak256(0x0, 0x20), div(slength, 32))\n\n // save new length\n sstore(_preBytes.slot, add(mul(newlength, 2), 1))\n\n // Copy over the first `submod` bytes of the new data as in\n // case 1 above.\n let slengthmod := mod(slength, 32)\n let mlengthmod := mod(mlength, 32)\n let submod := sub(32, slengthmod)\n let mc := add(_postBytes, submod)\n let end := add(_postBytes, mlength)\n let mask := sub(exp(0x100, submod), 1)\n\n sstore(sc, add(sload(sc), and(mload(mc), mask)))\n\n for {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } lt(mc, end) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n sstore(sc, mload(mc))\n }\n\n mask := exp(0x100, sub(mc, end))\n\n sstore(sc, mul(div(mload(mc), mask), mask))\n }\n }\n }\n\n function slice(\n bytes memory _bytes,\n uint256 _start,\n uint256 _length\n )\n internal\n pure\n returns (bytes memory)\n {\n // We're using the unchecked block below because otherwise execution ends \n // with the native overflow error code.\n unchecked {\n require(_length + 31 >= _length, \"slice_overflow\");\n }\n require(_bytes.length >= _start + _length, \"slice_outOfBounds\");\n\n bytes memory tempBytes;\n\n assembly {\n switch iszero(_length)\n case 0 {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // The first word of the slice result is potentially a partial\n // word read from the original array. To read it, we calculate\n // the length of that partial word and start copying that many\n // bytes into the array. The first word we copy will start with\n // data we don't care about, but the last `lengthmod` bytes will\n // land at the beginning of the contents of the new array. When\n // we're done copying, we overwrite the full first word with\n // the actual length of the slice.\n let lengthmod := and(_length, 31)\n\n // The multiplication in the next line is necessary\n // because when slicing multiples of 32 bytes (lengthmod == 0)\n // the following copy loop was copying the origin's length\n // and then ending prematurely not copying everything it should.\n let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))\n let end := add(mc, _length)\n\n for {\n // The multiplication in the next line has the same exact purpose\n // as the one above.\n let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n mstore(tempBytes, _length)\n\n //update free-memory pointer\n //allocating the array padded to 32 bytes like the compiler does now\n mstore(0x40, and(add(mc, 31), not(31)))\n }\n //if we want a zero-length slice let's just return a zero-length array\n default {\n tempBytes := mload(0x40)\n //zero out the 32 bytes slice we are about to return\n //we need to do it because Solidity does not garbage collect\n mstore(tempBytes, 0)\n\n mstore(0x40, add(tempBytes, 0x20))\n }\n }\n\n return tempBytes;\n }\n\n function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {\n require(_bytes.length >= _start + 20, \"toAddress_outOfBounds\");\n address tempAddress;\n\n assembly {\n tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)\n }\n\n return tempAddress;\n }\n\n function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {\n require(_bytes.length >= _start + 1 , \"toUint8_outOfBounds\");\n uint8 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x1), _start))\n }\n\n return tempUint;\n }\n\n function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {\n require(_bytes.length >= _start + 2, \"toUint16_outOfBounds\");\n uint16 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x2), _start))\n }\n\n return tempUint;\n }\n\n function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {\n require(_bytes.length >= _start + 4, \"toUint32_outOfBounds\");\n uint32 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x4), _start))\n }\n\n return tempUint;\n }\n\n function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {\n require(_bytes.length >= _start + 8, \"toUint64_outOfBounds\");\n uint64 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x8), _start))\n }\n\n return tempUint;\n }\n\n function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {\n require(_bytes.length >= _start + 12, \"toUint96_outOfBounds\");\n uint96 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0xc), _start))\n }\n\n return tempUint;\n }\n\n function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {\n require(_bytes.length >= _start + 16, \"toUint128_outOfBounds\");\n uint128 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x10), _start))\n }\n\n return tempUint;\n }\n\n function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {\n require(_bytes.length >= _start + 32, \"toUint256_outOfBounds\");\n uint256 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x20), _start))\n }\n\n return tempUint;\n }\n\n function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {\n require(_bytes.length >= _start + 32, \"toBytes32_outOfBounds\");\n bytes32 tempBytes32;\n\n assembly {\n tempBytes32 := mload(add(add(_bytes, 0x20), _start))\n }\n\n return tempBytes32;\n }\n\n function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {\n bool success = true;\n\n assembly {\n let length := mload(_preBytes)\n\n // if lengths don't match the arrays are not equal\n switch eq(length, mload(_postBytes))\n case 1 {\n // cb is a circuit breaker in the for loop since there's\n // no said feature for inline assembly loops\n // cb = 1 - don't breaker\n // cb = 0 - break\n let cb := 1\n\n let mc := add(_preBytes, 0x20)\n let end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n // the next line is the loop condition:\n // while(uint256(mc < end) + cb == 2)\n } eq(add(lt(mc, end), cb), 2) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // if any of these checks fails then arrays are not equal\n if iszero(eq(mload(mc), mload(cc))) {\n // unsuccess:\n success := 0\n cb := 0\n }\n }\n }\n default {\n // unsuccess:\n success := 0\n }\n }\n\n return success;\n }\n\n function equalStorage(\n bytes storage _preBytes,\n bytes memory _postBytes\n )\n internal\n view\n returns (bool)\n {\n bool success = true;\n\n assembly {\n // we know _preBytes_offset is 0\n let fslot := sload(_preBytes.slot)\n // Decode the length of the stored array like in concatStorage().\n let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)\n let mlength := mload(_postBytes)\n\n // if lengths don't match the arrays are not equal\n switch eq(slength, mlength)\n case 1 {\n // slength can contain both the length and contents of the array\n // if length < 32 bytes so let's prepare for that\n // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage\n if iszero(iszero(slength)) {\n switch lt(slength, 32)\n case 1 {\n // blank the last byte which is the length\n fslot := mul(div(fslot, 0x100), 0x100)\n\n if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {\n // unsuccess:\n success := 0\n }\n }\n default {\n // cb is a circuit breaker in the for loop since there's\n // no said feature for inline assembly loops\n // cb = 1 - don't breaker\n // cb = 0 - break\n let cb := 1\n\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n let sc := keccak256(0x0, 0x20)\n\n let mc := add(_postBytes, 0x20)\n let end := add(mc, mlength)\n\n // the next line is the loop condition:\n // while(uint256(mc < end) + cb == 2)\n for {} eq(add(lt(mc, end), cb), 2) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n if iszero(eq(sload(sc), mload(mc))) {\n // unsuccess:\n success := 0\n cb := 0\n }\n }\n }\n }\n }\n default {\n // unsuccess:\n success := 0\n }\n }\n\n return success;\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/4c0caa5973b28fed75932718e80fd6d6.json b/contracts/deployments/hoodi/solcInputs/4c0caa5973b28fed75932718e80fd6d6.json new file mode 100644 index 0000000000..01242b962a --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/4c0caa5973b28fed75932718e80fd6d6.json @@ -0,0 +1,1155 @@ +{ + "language": "Solidity", + "sources": { + "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {Client} from \"../libraries/Client.sol\";\n\ninterface IRouterClient {\n error UnsupportedDestinationChain(uint64 destChainSelector);\n error InsufficientFeeTokenAmount();\n error InvalidMsgValue();\n\n /// @notice Checks if the given chain ID is supported for sending/receiving.\n /// @param chainSelector The chain to check.\n /// @return supported is true if it is supported, false if not.\n function isChainSupported(uint64 chainSelector) external view returns (bool supported);\n\n /// @notice Gets a list of all supported tokens which can be sent or received\n /// to/from a given chain id.\n /// @param chainSelector The chainSelector.\n /// @return tokens The addresses of all tokens that are supported.\n function getSupportedTokens(uint64 chainSelector) external view returns (address[] memory tokens);\n\n /// @param destinationChainSelector The destination chainSelector\n /// @param message The cross-chain CCIP message including data and/or tokens\n /// @return fee returns execution fee for the message\n /// delivery to destination chain, denominated in the feeToken specified in the message.\n /// @dev Reverts with appropriate reason upon invalid message.\n function getFee(\n uint64 destinationChainSelector,\n Client.EVM2AnyMessage memory message\n ) external view returns (uint256 fee);\n\n /// @notice Request a message to be sent to the destination chain\n /// @param destinationChainSelector The destination chain ID\n /// @param message The cross-chain CCIP message including data and/or tokens\n /// @return messageId The message ID\n /// @dev Note if msg.value is larger than the required fee (from getFee) we accept\n /// the overpayment with no refund.\n /// @dev Reverts with appropriate reason upon invalid message.\n function ccipSend(\n uint64 destinationChainSelector,\n Client.EVM2AnyMessage calldata message\n ) external payable returns (bytes32);\n}\n" + }, + "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// End consumer library.\nlibrary Client {\n /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.\n struct EVMTokenAmount {\n address token; // token address on the local chain.\n uint256 amount; // Amount of tokens.\n }\n\n struct Any2EVMMessage {\n bytes32 messageId; // MessageId corresponding to ccipSend on source.\n uint64 sourceChainSelector; // Source chain selector.\n bytes sender; // abi.decode(sender) if coming from an EVM chain.\n bytes data; // payload sent in original message.\n EVMTokenAmount[] destTokenAmounts; // Tokens and their amounts in their destination chain representation.\n }\n\n // If extraArgs is empty bytes, the default is 200k gas limit.\n struct EVM2AnyMessage {\n bytes receiver; // abi.encode(receiver address) for dest EVM chains\n bytes data; // Data payload\n EVMTokenAmount[] tokenAmounts; // Token transfers\n address feeToken; // Address of feeToken. address(0) means you will send msg.value.\n bytes extraArgs; // Populate this with _argsToBytes(EVMExtraArgsV1)\n }\n\n // bytes4(keccak256(\"CCIP EVMExtraArgsV1\"));\n bytes4 public constant EVM_EXTRA_ARGS_V1_TAG = 0x97a657c9;\n struct EVMExtraArgsV1 {\n uint256 gasLimit;\n }\n\n function _argsToBytes(EVMExtraArgsV1 memory extraArgs) internal pure returns (bytes memory bts) {\n return abi.encodeWithSelector(EVM_EXTRA_ARGS_V1_TAG, extraArgs);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport \"@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol\";\n\nlibrary ExecutorOptions {\n using CalldataBytesLib for bytes;\n\n uint8 internal constant WORKER_ID = 1;\n\n uint8 internal constant OPTION_TYPE_LZRECEIVE = 1;\n uint8 internal constant OPTION_TYPE_NATIVE_DROP = 2;\n uint8 internal constant OPTION_TYPE_LZCOMPOSE = 3;\n uint8 internal constant OPTION_TYPE_ORDERED_EXECUTION = 4;\n uint8 internal constant OPTION_TYPE_LZREAD = 5;\n\n error Executor_InvalidLzReceiveOption();\n error Executor_InvalidNativeDropOption();\n error Executor_InvalidLzComposeOption();\n error Executor_InvalidLzReadOption();\n\n /// @dev decode the next executor option from the options starting from the specified cursor\n /// @param _options [executor_id][executor_option][executor_id][executor_option]...\n /// executor_option = [option_size][option_type][option]\n /// option_size = len(option_type) + len(option)\n /// executor_id: uint8, option_size: uint16, option_type: uint8, option: bytes\n /// @param _cursor the cursor to start decoding from\n /// @return optionType the type of the option\n /// @return option the option of the executor\n /// @return cursor the cursor to start decoding the next executor option\n function nextExecutorOption(\n bytes calldata _options,\n uint256 _cursor\n ) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) {\n unchecked {\n // skip worker id\n cursor = _cursor + 1;\n\n // read option size\n uint16 size = _options.toU16(cursor);\n cursor += 2;\n\n // read option type\n optionType = _options.toU8(cursor);\n\n // startCursor and endCursor are used to slice the option from _options\n uint256 startCursor = cursor + 1; // skip option type\n uint256 endCursor = cursor + size;\n option = _options[startCursor:endCursor];\n cursor += size;\n }\n }\n\n function decodeLzReceiveOption(bytes calldata _option) internal pure returns (uint128 gas, uint128 value) {\n if (_option.length != 16 && _option.length != 32) revert Executor_InvalidLzReceiveOption();\n gas = _option.toU128(0);\n value = _option.length == 32 ? _option.toU128(16) : 0;\n }\n\n function decodeNativeDropOption(bytes calldata _option) internal pure returns (uint128 amount, bytes32 receiver) {\n if (_option.length != 48) revert Executor_InvalidNativeDropOption();\n amount = _option.toU128(0);\n receiver = _option.toB32(16);\n }\n\n function decodeLzComposeOption(\n bytes calldata _option\n ) internal pure returns (uint16 index, uint128 gas, uint128 value) {\n if (_option.length != 18 && _option.length != 34) revert Executor_InvalidLzComposeOption();\n index = _option.toU16(0);\n gas = _option.toU128(2);\n value = _option.length == 34 ? _option.toU128(18) : 0;\n }\n\n function decodeLzReadOption(\n bytes calldata _option\n ) internal pure returns (uint128 gas, uint32 calldataSize, uint128 value) {\n if (_option.length != 20 && _option.length != 36) revert Executor_InvalidLzReadOption();\n gas = _option.toU128(0);\n calldataSize = _option.toU32(16);\n value = _option.length == 36 ? _option.toU128(20) : 0;\n }\n\n function encodeLzReceiveOption(uint128 _gas, uint128 _value) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_gas) : abi.encodePacked(_gas, _value);\n }\n\n function encodeNativeDropOption(uint128 _amount, bytes32 _receiver) internal pure returns (bytes memory) {\n return abi.encodePacked(_amount, _receiver);\n }\n\n function encodeLzComposeOption(uint16 _index, uint128 _gas, uint128 _value) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_index, _gas) : abi.encodePacked(_index, _gas, _value);\n }\n\n function encodeLzReadOption(\n uint128 _gas,\n uint32 _calldataSize,\n uint128 _value\n ) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_gas, _calldataSize) : abi.encodePacked(_gas, _calldataSize, _value);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/DVNOptions.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport { BytesLib } from \"solidity-bytes-utils/contracts/BytesLib.sol\";\n\nimport { BitMap256 } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/BitMaps.sol\";\nimport { CalldataBytesLib } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol\";\n\nlibrary DVNOptions {\n using CalldataBytesLib for bytes;\n using BytesLib for bytes;\n\n uint8 internal constant WORKER_ID = 2;\n uint8 internal constant OPTION_TYPE_PRECRIME = 1;\n\n error DVN_InvalidDVNIdx();\n error DVN_InvalidDVNOptions(uint256 cursor);\n\n /// @dev group dvn options by its idx\n /// @param _options [dvn_id][dvn_option][dvn_id][dvn_option]...\n /// dvn_option = [option_size][dvn_idx][option_type][option]\n /// option_size = len(dvn_idx) + len(option_type) + len(option)\n /// dvn_id: uint8, dvn_idx: uint8, option_size: uint16, option_type: uint8, option: bytes\n /// @return dvnOptions the grouped options, still share the same format of _options\n /// @return dvnIndices the dvn indices\n function groupDVNOptionsByIdx(\n bytes memory _options\n ) internal pure returns (bytes[] memory dvnOptions, uint8[] memory dvnIndices) {\n if (_options.length == 0) return (dvnOptions, dvnIndices);\n\n uint8 numDVNs = getNumDVNs(_options);\n\n // if there is only 1 dvn, we can just return the whole options\n if (numDVNs == 1) {\n dvnOptions = new bytes[](1);\n dvnOptions[0] = _options;\n\n dvnIndices = new uint8[](1);\n dvnIndices[0] = _options.toUint8(3); // dvn idx\n return (dvnOptions, dvnIndices);\n }\n\n // otherwise, we need to group the options by dvn_idx\n dvnIndices = new uint8[](numDVNs);\n dvnOptions = new bytes[](numDVNs);\n unchecked {\n uint256 cursor = 0;\n uint256 start = 0;\n uint8 lastDVNIdx = 255; // 255 is an invalid dvn_idx\n\n while (cursor < _options.length) {\n ++cursor; // skip worker_id\n\n // optionLength asserted in getNumDVNs (skip check)\n uint16 optionLength = _options.toUint16(cursor);\n cursor += 2;\n\n // dvnIdx asserted in getNumDVNs (skip check)\n uint8 dvnIdx = _options.toUint8(cursor);\n\n // dvnIdx must equal to the lastDVNIdx for the first option\n // so it is always skipped in the first option\n // this operation slices out options whenever the scan finds a different lastDVNIdx\n if (lastDVNIdx == 255) {\n lastDVNIdx = dvnIdx;\n } else if (dvnIdx != lastDVNIdx) {\n uint256 len = cursor - start - 3; // 3 is for worker_id and option_length\n bytes memory opt = _options.slice(start, len);\n _insertDVNOptions(dvnOptions, dvnIndices, lastDVNIdx, opt);\n\n // reset the start and lastDVNIdx\n start += len;\n lastDVNIdx = dvnIdx;\n }\n\n cursor += optionLength;\n }\n\n // skip check the cursor here because the cursor is asserted in getNumDVNs\n // if we have reached the end of the options, we need to process the last dvn\n uint256 size = cursor - start;\n bytes memory op = _options.slice(start, size);\n _insertDVNOptions(dvnOptions, dvnIndices, lastDVNIdx, op);\n\n // revert dvnIndices to start from 0\n for (uint8 i = 0; i < numDVNs; ++i) {\n --dvnIndices[i];\n }\n }\n }\n\n function _insertDVNOptions(\n bytes[] memory _dvnOptions,\n uint8[] memory _dvnIndices,\n uint8 _dvnIdx,\n bytes memory _newOptions\n ) internal pure {\n // dvnIdx starts from 0 but default value of dvnIndices is 0,\n // so we tell if the slot is empty by adding 1 to dvnIdx\n if (_dvnIdx == 255) revert DVN_InvalidDVNIdx();\n uint8 dvnIdxAdj = _dvnIdx + 1;\n\n for (uint256 j = 0; j < _dvnIndices.length; ++j) {\n uint8 index = _dvnIndices[j];\n if (dvnIdxAdj == index) {\n _dvnOptions[j] = abi.encodePacked(_dvnOptions[j], _newOptions);\n break;\n } else if (index == 0) {\n // empty slot, that means it is the first time we see this dvn\n _dvnIndices[j] = dvnIdxAdj;\n _dvnOptions[j] = _newOptions;\n break;\n }\n }\n }\n\n /// @dev get the number of unique dvns\n /// @param _options the format is the same as groupDVNOptionsByIdx\n function getNumDVNs(bytes memory _options) internal pure returns (uint8 numDVNs) {\n uint256 cursor = 0;\n BitMap256 bitmap;\n\n // find number of unique dvn_idx\n unchecked {\n while (cursor < _options.length) {\n ++cursor; // skip worker_id\n\n uint16 optionLength = _options.toUint16(cursor);\n cursor += 2;\n if (optionLength < 2) revert DVN_InvalidDVNOptions(cursor); // at least 1 byte for dvn_idx and 1 byte for option_type\n\n uint8 dvnIdx = _options.toUint8(cursor);\n\n // if dvnIdx is not set, increment numDVNs\n // max num of dvns is 255, 255 is an invalid dvn_idx\n // The order of the dvnIdx is not required to be sequential, as enforcing the order may weaken\n // the composability of the options. e.g. if we refrain from enforcing the order, an OApp that has\n // already enforced certain options can append additional options to the end of the enforced\n // ones without restrictions.\n if (dvnIdx == 255) revert DVN_InvalidDVNIdx();\n if (!bitmap.get(dvnIdx)) {\n ++numDVNs;\n bitmap = bitmap.set(dvnIdx);\n }\n\n cursor += optionLength;\n }\n }\n if (cursor != _options.length) revert DVN_InvalidDVNOptions(cursor);\n }\n\n /// @dev decode the next dvn option from _options starting from the specified cursor\n /// @param _options the format is the same as groupDVNOptionsByIdx\n /// @param _cursor the cursor to start decoding\n /// @return optionType the type of the option\n /// @return option the option\n /// @return cursor the cursor to start decoding the next option\n function nextDVNOption(\n bytes calldata _options,\n uint256 _cursor\n ) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) {\n unchecked {\n // skip worker id\n cursor = _cursor + 1;\n\n // read option size\n uint16 size = _options.toU16(cursor);\n cursor += 2;\n\n // read option type\n optionType = _options.toU8(cursor + 1); // skip dvn_idx\n\n // startCursor and endCursor are used to slice the option from _options\n uint256 startCursor = cursor + 2; // skip option type and dvn_idx\n uint256 endCursor = cursor + size;\n option = _options[startCursor:endCursor];\n cursor += size;\n }\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { IMessageLibManager } from \"./IMessageLibManager.sol\";\nimport { IMessagingComposer } from \"./IMessagingComposer.sol\";\nimport { IMessagingChannel } from \"./IMessagingChannel.sol\";\nimport { IMessagingContext } from \"./IMessagingContext.sol\";\n\nstruct MessagingParams {\n uint32 dstEid;\n bytes32 receiver;\n bytes message;\n bytes options;\n bool payInLzToken;\n}\n\nstruct MessagingReceipt {\n bytes32 guid;\n uint64 nonce;\n MessagingFee fee;\n}\n\nstruct MessagingFee {\n uint256 nativeFee;\n uint256 lzTokenFee;\n}\n\nstruct Origin {\n uint32 srcEid;\n bytes32 sender;\n uint64 nonce;\n}\n\ninterface ILayerZeroEndpointV2 is IMessageLibManager, IMessagingComposer, IMessagingChannel, IMessagingContext {\n event PacketSent(bytes encodedPayload, bytes options, address sendLibrary);\n\n event PacketVerified(Origin origin, address receiver, bytes32 payloadHash);\n\n event PacketDelivered(Origin origin, address receiver);\n\n event LzReceiveAlert(\n address indexed receiver,\n address indexed executor,\n Origin origin,\n bytes32 guid,\n uint256 gas,\n uint256 value,\n bytes message,\n bytes extraData,\n bytes reason\n );\n\n event LzTokenSet(address token);\n\n event DelegateSet(address sender, address delegate);\n\n function quote(MessagingParams calldata _params, address _sender) external view returns (MessagingFee memory);\n\n function send(\n MessagingParams calldata _params,\n address _refundAddress\n ) external payable returns (MessagingReceipt memory);\n\n function verify(Origin calldata _origin, address _receiver, bytes32 _payloadHash) external;\n\n function verifiable(Origin calldata _origin, address _receiver) external view returns (bool);\n\n function initializable(Origin calldata _origin, address _receiver) external view returns (bool);\n\n function lzReceive(\n Origin calldata _origin,\n address _receiver,\n bytes32 _guid,\n bytes calldata _message,\n bytes calldata _extraData\n ) external payable;\n\n // oapp can burn messages partially by calling this function with its own business logic if messages are verified in order\n function clear(address _oapp, Origin calldata _origin, bytes32 _guid, bytes calldata _message) external;\n\n function setLzToken(address _lzToken) external;\n\n function lzToken() external view returns (address);\n\n function nativeToken() external view returns (address);\n\n function setDelegate(address _delegate) external;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { Origin } from \"./ILayerZeroEndpointV2.sol\";\n\ninterface ILayerZeroReceiver {\n function allowInitializePath(Origin calldata _origin) external view returns (bool);\n\n function nextNonce(uint32 _eid, bytes32 _sender) external view returns (uint64);\n\n function lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) external payable;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessageLib.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { IERC165 } from \"@openzeppelin/contracts/utils/introspection/IERC165.sol\";\n\nimport { SetConfigParam } from \"./IMessageLibManager.sol\";\n\nenum MessageLibType {\n Send,\n Receive,\n SendAndReceive\n}\n\ninterface IMessageLib is IERC165 {\n function setConfig(address _oapp, SetConfigParam[] calldata _config) external;\n\n function getConfig(uint32 _eid, address _oapp, uint32 _configType) external view returns (bytes memory config);\n\n function isSupportedEid(uint32 _eid) external view returns (bool);\n\n // message libs of same major version are compatible\n function version() external view returns (uint64 major, uint8 minor, uint8 endpointVersion);\n\n function messageLibType() external view returns (MessageLibType);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessageLibManager.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nstruct SetConfigParam {\n uint32 eid;\n uint32 configType;\n bytes config;\n}\n\ninterface IMessageLibManager {\n struct Timeout {\n address lib;\n uint256 expiry;\n }\n\n event LibraryRegistered(address newLib);\n event DefaultSendLibrarySet(uint32 eid, address newLib);\n event DefaultReceiveLibrarySet(uint32 eid, address newLib);\n event DefaultReceiveLibraryTimeoutSet(uint32 eid, address oldLib, uint256 expiry);\n event SendLibrarySet(address sender, uint32 eid, address newLib);\n event ReceiveLibrarySet(address receiver, uint32 eid, address newLib);\n event ReceiveLibraryTimeoutSet(address receiver, uint32 eid, address oldLib, uint256 timeout);\n\n function registerLibrary(address _lib) external;\n\n function isRegisteredLibrary(address _lib) external view returns (bool);\n\n function getRegisteredLibraries() external view returns (address[] memory);\n\n function setDefaultSendLibrary(uint32 _eid, address _newLib) external;\n\n function defaultSendLibrary(uint32 _eid) external view returns (address);\n\n function setDefaultReceiveLibrary(uint32 _eid, address _newLib, uint256 _gracePeriod) external;\n\n function defaultReceiveLibrary(uint32 _eid) external view returns (address);\n\n function setDefaultReceiveLibraryTimeout(uint32 _eid, address _lib, uint256 _expiry) external;\n\n function defaultReceiveLibraryTimeout(uint32 _eid) external view returns (address lib, uint256 expiry);\n\n function isSupportedEid(uint32 _eid) external view returns (bool);\n\n function isValidReceiveLibrary(address _receiver, uint32 _eid, address _lib) external view returns (bool);\n\n /// ------------------- OApp interfaces -------------------\n function setSendLibrary(address _oapp, uint32 _eid, address _newLib) external;\n\n function getSendLibrary(address _sender, uint32 _eid) external view returns (address lib);\n\n function isDefaultSendLibrary(address _sender, uint32 _eid) external view returns (bool);\n\n function setReceiveLibrary(address _oapp, uint32 _eid, address _newLib, uint256 _gracePeriod) external;\n\n function getReceiveLibrary(address _receiver, uint32 _eid) external view returns (address lib, bool isDefault);\n\n function setReceiveLibraryTimeout(address _oapp, uint32 _eid, address _lib, uint256 _expiry) external;\n\n function receiveLibraryTimeout(address _receiver, uint32 _eid) external view returns (address lib, uint256 expiry);\n\n function setConfig(address _oapp, address _lib, SetConfigParam[] calldata _params) external;\n\n function getConfig(\n address _oapp,\n address _lib,\n uint32 _eid,\n uint32 _configType\n ) external view returns (bytes memory config);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingChannel.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingChannel {\n event InboundNonceSkipped(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce);\n event PacketNilified(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);\n event PacketBurnt(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);\n\n function eid() external view returns (uint32);\n\n // this is an emergency function if a message cannot be verified for some reasons\n // required to provide _nextNonce to avoid race condition\n function skip(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce) external;\n\n function nilify(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;\n\n function burn(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;\n\n function nextGuid(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (bytes32);\n\n function inboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);\n\n function outboundNonce(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (uint64);\n\n function inboundPayloadHash(\n address _receiver,\n uint32 _srcEid,\n bytes32 _sender,\n uint64 _nonce\n ) external view returns (bytes32);\n\n function lazyInboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingComposer.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingComposer {\n event ComposeSent(address from, address to, bytes32 guid, uint16 index, bytes message);\n event ComposeDelivered(address from, address to, bytes32 guid, uint16 index);\n event LzComposeAlert(\n address indexed from,\n address indexed to,\n address indexed executor,\n bytes32 guid,\n uint16 index,\n uint256 gas,\n uint256 value,\n bytes message,\n bytes extraData,\n bytes reason\n );\n\n function composeQueue(\n address _from,\n address _to,\n bytes32 _guid,\n uint16 _index\n ) external view returns (bytes32 messageHash);\n\n function sendCompose(address _to, bytes32 _guid, uint16 _index, bytes calldata _message) external;\n\n function lzCompose(\n address _from,\n address _to,\n bytes32 _guid,\n uint16 _index,\n bytes calldata _message,\n bytes calldata _extraData\n ) external payable;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingContext.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingContext {\n function isSendingMessage() external view returns (bool);\n\n function getSendContext() external view returns (uint32 dstEid, address sender);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ISendLib.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { MessagingFee } from \"./ILayerZeroEndpointV2.sol\";\nimport { IMessageLib } from \"./IMessageLib.sol\";\n\nstruct Packet {\n uint64 nonce;\n uint32 srcEid;\n address sender;\n uint32 dstEid;\n bytes32 receiver;\n bytes32 guid;\n bytes message;\n}\n\ninterface ISendLib is IMessageLib {\n function send(\n Packet calldata _packet,\n bytes calldata _options,\n bool _payInLzToken\n ) external returns (MessagingFee memory, bytes memory encodedPacket);\n\n function quote(\n Packet calldata _packet,\n bytes calldata _options,\n bool _payInLzToken\n ) external view returns (MessagingFee memory);\n\n function setTreasury(address _treasury) external;\n\n function withdrawFee(address _to, uint256 _amount) external;\n\n function withdrawLzTokenFee(address _lzToken, address _to, uint256 _amount) external;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/AddressCast.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nlibrary AddressCast {\n error AddressCast_InvalidSizeForAddress();\n error AddressCast_InvalidAddress();\n\n function toBytes32(bytes calldata _addressBytes) internal pure returns (bytes32 result) {\n if (_addressBytes.length > 32) revert AddressCast_InvalidAddress();\n result = bytes32(_addressBytes);\n unchecked {\n uint256 offset = 32 - _addressBytes.length;\n result = result >> (offset * 8);\n }\n }\n\n function toBytes32(address _address) internal pure returns (bytes32 result) {\n result = bytes32(uint256(uint160(_address)));\n }\n\n function toBytes(bytes32 _addressBytes32, uint256 _size) internal pure returns (bytes memory result) {\n if (_size == 0 || _size > 32) revert AddressCast_InvalidSizeForAddress();\n result = new bytes(_size);\n unchecked {\n uint256 offset = 256 - _size * 8;\n assembly {\n mstore(add(result, 32), shl(offset, _addressBytes32))\n }\n }\n }\n\n function toAddress(bytes32 _addressBytes32) internal pure returns (address result) {\n result = address(uint160(uint256(_addressBytes32)));\n }\n\n function toAddress(bytes calldata _addressBytes) internal pure returns (address result) {\n if (_addressBytes.length != 20) revert AddressCast_InvalidAddress();\n result = address(bytes20(_addressBytes));\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nlibrary CalldataBytesLib {\n function toU8(bytes calldata _bytes, uint256 _start) internal pure returns (uint8) {\n return uint8(_bytes[_start]);\n }\n\n function toU16(bytes calldata _bytes, uint256 _start) internal pure returns (uint16) {\n unchecked {\n uint256 end = _start + 2;\n return uint16(bytes2(_bytes[_start:end]));\n }\n }\n\n function toU32(bytes calldata _bytes, uint256 _start) internal pure returns (uint32) {\n unchecked {\n uint256 end = _start + 4;\n return uint32(bytes4(_bytes[_start:end]));\n }\n }\n\n function toU64(bytes calldata _bytes, uint256 _start) internal pure returns (uint64) {\n unchecked {\n uint256 end = _start + 8;\n return uint64(bytes8(_bytes[_start:end]));\n }\n }\n\n function toU128(bytes calldata _bytes, uint256 _start) internal pure returns (uint128) {\n unchecked {\n uint256 end = _start + 16;\n return uint128(bytes16(_bytes[_start:end]));\n }\n }\n\n function toU256(bytes calldata _bytes, uint256 _start) internal pure returns (uint256) {\n unchecked {\n uint256 end = _start + 32;\n return uint256(bytes32(_bytes[_start:end]));\n }\n }\n\n function toAddr(bytes calldata _bytes, uint256 _start) internal pure returns (address) {\n unchecked {\n uint256 end = _start + 20;\n return address(bytes20(_bytes[_start:end]));\n }\n }\n\n function toB32(bytes calldata _bytes, uint256 _start) internal pure returns (bytes32) {\n unchecked {\n uint256 end = _start + 32;\n return bytes32(_bytes[_start:end]);\n }\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/BitMaps.sol": { + "content": "// SPDX-License-Identifier: MIT\n\n// modified from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/structs/BitMaps.sol\npragma solidity ^0.8.20;\n\ntype BitMap256 is uint256;\n\nusing BitMaps for BitMap256 global;\n\nlibrary BitMaps {\n /**\n * @dev Returns whether the bit at `index` is set.\n */\n function get(BitMap256 bitmap, uint8 index) internal pure returns (bool) {\n uint256 mask = 1 << index;\n return BitMap256.unwrap(bitmap) & mask != 0;\n }\n\n /**\n * @dev Sets the bit at `index`.\n */\n function set(BitMap256 bitmap, uint8 index) internal pure returns (BitMap256) {\n uint256 mask = 1 << index;\n return BitMap256.wrap(BitMap256.unwrap(bitmap) | mask);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport { Packet } from \"../../interfaces/ISendLib.sol\";\nimport { AddressCast } from \"../../libs/AddressCast.sol\";\n\nlibrary PacketV1Codec {\n using AddressCast for address;\n using AddressCast for bytes32;\n\n uint8 internal constant PACKET_VERSION = 1;\n\n // header (version + nonce + path)\n // version\n uint256 private constant PACKET_VERSION_OFFSET = 0;\n // nonce\n uint256 private constant NONCE_OFFSET = 1;\n // path\n uint256 private constant SRC_EID_OFFSET = 9;\n uint256 private constant SENDER_OFFSET = 13;\n uint256 private constant DST_EID_OFFSET = 45;\n uint256 private constant RECEIVER_OFFSET = 49;\n // payload (guid + message)\n uint256 private constant GUID_OFFSET = 81; // keccak256(nonce + path)\n uint256 private constant MESSAGE_OFFSET = 113;\n\n function encode(Packet memory _packet) internal pure returns (bytes memory encodedPacket) {\n encodedPacket = abi.encodePacked(\n PACKET_VERSION,\n _packet.nonce,\n _packet.srcEid,\n _packet.sender.toBytes32(),\n _packet.dstEid,\n _packet.receiver,\n _packet.guid,\n _packet.message\n );\n }\n\n function encodePacketHeader(Packet memory _packet) internal pure returns (bytes memory) {\n return\n abi.encodePacked(\n PACKET_VERSION,\n _packet.nonce,\n _packet.srcEid,\n _packet.sender.toBytes32(),\n _packet.dstEid,\n _packet.receiver\n );\n }\n\n function encodePayload(Packet memory _packet) internal pure returns (bytes memory) {\n return abi.encodePacked(_packet.guid, _packet.message);\n }\n\n function header(bytes calldata _packet) internal pure returns (bytes calldata) {\n return _packet[0:GUID_OFFSET];\n }\n\n function version(bytes calldata _packet) internal pure returns (uint8) {\n return uint8(bytes1(_packet[PACKET_VERSION_OFFSET:NONCE_OFFSET]));\n }\n\n function nonce(bytes calldata _packet) internal pure returns (uint64) {\n return uint64(bytes8(_packet[NONCE_OFFSET:SRC_EID_OFFSET]));\n }\n\n function srcEid(bytes calldata _packet) internal pure returns (uint32) {\n return uint32(bytes4(_packet[SRC_EID_OFFSET:SENDER_OFFSET]));\n }\n\n function sender(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[SENDER_OFFSET:DST_EID_OFFSET]);\n }\n\n function senderAddressB20(bytes calldata _packet) internal pure returns (address) {\n return sender(_packet).toAddress();\n }\n\n function dstEid(bytes calldata _packet) internal pure returns (uint32) {\n return uint32(bytes4(_packet[DST_EID_OFFSET:RECEIVER_OFFSET]));\n }\n\n function receiver(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[RECEIVER_OFFSET:GUID_OFFSET]);\n }\n\n function receiverB20(bytes calldata _packet) internal pure returns (address) {\n return receiver(_packet).toAddress();\n }\n\n function guid(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[GUID_OFFSET:MESSAGE_OFFSET]);\n }\n\n function message(bytes calldata _packet) internal pure returns (bytes calldata) {\n return bytes(_packet[MESSAGE_OFFSET:]);\n }\n\n function payload(bytes calldata _packet) internal pure returns (bytes calldata) {\n return bytes(_packet[GUID_OFFSET:]);\n }\n\n function payloadHash(bytes calldata _packet) internal pure returns (bytes32) {\n return keccak256(payload(_packet));\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { ILayerZeroEndpointV2 } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\n\n/**\n * @title IOAppCore\n */\ninterface IOAppCore {\n // Custom error messages\n error OnlyPeer(uint32 eid, bytes32 sender);\n error NoPeer(uint32 eid);\n error InvalidEndpointCall();\n error InvalidDelegate();\n\n // Event emitted when a peer (OApp) is set for a corresponding endpoint\n event PeerSet(uint32 eid, bytes32 peer);\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n */\n function oAppVersion() external view returns (uint64 senderVersion, uint64 receiverVersion);\n\n /**\n * @notice Retrieves the LayerZero endpoint associated with the OApp.\n * @return iEndpoint The LayerZero endpoint as an interface.\n */\n function endpoint() external view returns (ILayerZeroEndpointV2 iEndpoint);\n\n /**\n * @notice Retrieves the peer (OApp) associated with a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @return peer The peer address (OApp instance) associated with the corresponding endpoint.\n */\n function peers(uint32 _eid) external view returns (bytes32 peer);\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n */\n function setPeer(uint32 _eid, bytes32 _peer) external;\n\n /**\n * @notice Sets the delegate address for the OApp Core.\n * @param _delegate The address of the delegate to be set.\n */\n function setDelegate(address _delegate) external;\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppMsgInspector.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n/**\n * @title IOAppMsgInspector\n * @dev Interface for the OApp Message Inspector, allowing examination of message and options contents.\n */\ninterface IOAppMsgInspector {\n // Custom error message for inspection failure\n error InspectionFailed(bytes message, bytes options);\n\n /**\n * @notice Allows the inspector to examine LayerZero message contents and optionally throw a revert if invalid.\n * @param _message The message payload to be inspected.\n * @param _options Additional options or parameters for inspection.\n * @return valid A boolean indicating whether the inspection passed (true) or failed (false).\n *\n * @dev Optionally done as a revert, OR use the boolean provided to handle the failure.\n */\n function inspect(bytes calldata _message, bytes calldata _options) external view returns (bool valid);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppOptionsType3.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Struct representing enforced option parameters.\n */\nstruct EnforcedOptionParam {\n uint32 eid; // Endpoint ID\n uint16 msgType; // Message Type\n bytes options; // Additional options\n}\n\n/**\n * @title IOAppOptionsType3\n * @dev Interface for the OApp with Type 3 Options, allowing the setting and combining of enforced options.\n */\ninterface IOAppOptionsType3 {\n // Custom error message for invalid options\n error InvalidOptions(bytes options);\n\n // Event emitted when enforced options are set\n event EnforcedOptionSet(EnforcedOptionParam[] _enforcedOptions);\n\n /**\n * @notice Sets enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n */\n function setEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) external;\n\n /**\n * @notice Combines options for a given endpoint and message type.\n * @param _eid The endpoint ID.\n * @param _msgType The OApp message type.\n * @param _extraOptions Additional options passed by the caller.\n * @return options The combination of caller specified options AND enforced options.\n */\n function combineOptions(\n uint32 _eid,\n uint16 _msgType,\n bytes calldata _extraOptions\n ) external view returns (bytes memory options);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\nimport { ILayerZeroReceiver, Origin } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroReceiver.sol\";\n\ninterface IOAppReceiver is ILayerZeroReceiver {\n /**\n * @notice Indicates whether an address is an approved composeMsg sender to the Endpoint.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _message The lzReceive payload.\n * @param _sender The sender address.\n * @return isSender Is a valid sender.\n *\n * @dev Applications can optionally choose to implement a separate composeMsg sender that is NOT the bridging layer.\n * @dev The default sender IS the OAppReceiver implementer.\n */\n function isComposeMsgSender(\n Origin calldata _origin,\n bytes calldata _message,\n address _sender\n ) external view returns (bool isSender);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IOAppOptionsType3, EnforcedOptionParam } from \"../interfaces/IOAppOptionsType3.sol\";\n\n/**\n * @title OAppOptionsType3\n * @dev Abstract contract implementing the IOAppOptionsType3 interface with type 3 options.\n */\nabstract contract OAppOptionsType3 is IOAppOptionsType3, Ownable {\n uint16 internal constant OPTION_TYPE_3 = 3;\n\n // @dev The \"msgType\" should be defined in the child contract.\n mapping(uint32 eid => mapping(uint16 msgType => bytes enforcedOption)) public enforcedOptions;\n\n /**\n * @dev Sets the enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Provides a way for the OApp to enforce things like paying for PreCrime, AND/OR minimum dst lzReceive gas amounts etc.\n * @dev These enforced options can vary as the potential options/execution on the remote may differ as per the msgType.\n * eg. Amount of lzReceive() gas necessary to deliver a lzCompose() message adds overhead you dont want to pay\n * if you are only making a standard LayerZero message ie. lzReceive() WITHOUT sendCompose().\n */\n function setEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) public virtual onlyOwner {\n _setEnforcedOptions(_enforcedOptions);\n }\n\n /**\n * @dev Sets the enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n *\n * @dev Provides a way for the OApp to enforce things like paying for PreCrime, AND/OR minimum dst lzReceive gas amounts etc.\n * @dev These enforced options can vary as the potential options/execution on the remote may differ as per the msgType.\n * eg. Amount of lzReceive() gas necessary to deliver a lzCompose() message adds overhead you dont want to pay\n * if you are only making a standard LayerZero message ie. lzReceive() WITHOUT sendCompose().\n */\n function _setEnforcedOptions(EnforcedOptionParam[] memory _enforcedOptions) internal virtual {\n for (uint256 i = 0; i < _enforcedOptions.length; i++) {\n // @dev Enforced options are only available for optionType 3, as type 1 and 2 dont support combining.\n _assertOptionsType3(_enforcedOptions[i].options);\n enforcedOptions[_enforcedOptions[i].eid][_enforcedOptions[i].msgType] = _enforcedOptions[i].options;\n }\n\n emit EnforcedOptionSet(_enforcedOptions);\n }\n\n /**\n * @notice Combines options for a given endpoint and message type.\n * @param _eid The endpoint ID.\n * @param _msgType The OAPP message type.\n * @param _extraOptions Additional options passed by the caller.\n * @return options The combination of caller specified options AND enforced options.\n *\n * @dev If there is an enforced lzReceive option:\n * - {gasLimit: 200k, msg.value: 1 ether} AND a caller supplies a lzReceive option: {gasLimit: 100k, msg.value: 0.5 ether}\n * - The resulting options will be {gasLimit: 300k, msg.value: 1.5 ether} when the message is executed on the remote lzReceive() function.\n * @dev This presence of duplicated options is handled off-chain in the verifier/executor.\n */\n function combineOptions(\n uint32 _eid,\n uint16 _msgType,\n bytes calldata _extraOptions\n ) public view virtual returns (bytes memory) {\n bytes memory enforced = enforcedOptions[_eid][_msgType];\n\n // No enforced options, pass whatever the caller supplied, even if it's empty or legacy type 1/2 options.\n if (enforced.length == 0) return _extraOptions;\n\n // No caller options, return enforced\n if (_extraOptions.length == 0) return enforced;\n\n // @dev If caller provided _extraOptions, must be type 3 as its the ONLY type that can be combined.\n if (_extraOptions.length >= 2) {\n _assertOptionsType3(_extraOptions);\n // @dev Remove the first 2 bytes containing the type from the _extraOptions and combine with enforced.\n return bytes.concat(enforced, _extraOptions[2:]);\n }\n\n // No valid set of options was found.\n revert InvalidOptions(_extraOptions);\n }\n\n /**\n * @dev Internal function to assert that options are of type 3.\n * @param _options The options to be checked.\n */\n function _assertOptionsType3(bytes memory _options) internal pure virtual {\n uint16 optionsType;\n assembly {\n optionsType := mload(add(_options, 2))\n }\n if (optionsType != OPTION_TYPE_3) revert InvalidOptions(_options);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { BytesLib } from \"solidity-bytes-utils/contracts/BytesLib.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { ExecutorOptions } from \"@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol\";\nimport { DVNOptions } from \"@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/DVNOptions.sol\";\n\n/**\n * @title OptionsBuilder\n * @dev Library for building and encoding various message options.\n */\nlibrary OptionsBuilder {\n using SafeCast for uint256;\n using BytesLib for bytes;\n\n // Constants for options types\n uint16 internal constant TYPE_1 = 1; // legacy options type 1\n uint16 internal constant TYPE_2 = 2; // legacy options type 2\n uint16 internal constant TYPE_3 = 3;\n\n // Custom error message\n error InvalidSize(uint256 max, uint256 actual);\n error InvalidOptionType(uint16 optionType);\n\n // Modifier to ensure only options of type 3 are used\n modifier onlyType3(bytes memory _options) {\n if (_options.toUint16(0) != TYPE_3) revert InvalidOptionType(_options.toUint16(0));\n _;\n }\n\n /**\n * @dev Creates a new options container with type 3.\n * @return options The newly created options container.\n */\n function newOptions() internal pure returns (bytes memory) {\n return abi.encodePacked(TYPE_3);\n }\n\n /**\n * @dev Adds an executor LZ receive option to the existing options.\n * @param _options The existing options container.\n * @param _gas The gasLimit used on the lzReceive() function in the OApp.\n * @param _value The msg.value passed to the lzReceive() function in the OApp.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed by the executor\n * eg. if (_gas: 200k, and _value: 1 ether) AND (_gas: 100k, _value: 0.5 ether) are sent in an option to the LayerZeroEndpoint,\n * that becomes (300k, 1.5 ether) when the message is executed on the remote lzReceive() function.\n */\n function addExecutorLzReceiveOption(\n bytes memory _options,\n uint128 _gas,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzReceiveOption(_gas, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZRECEIVE, option);\n }\n\n /**\n * @dev Adds an executor native drop option to the existing options.\n * @param _options The existing options container.\n * @param _amount The amount for the native value that is airdropped to the 'receiver'.\n * @param _receiver The receiver address for the native drop option.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed by the executor on the remote chain.\n */\n function addExecutorNativeDropOption(\n bytes memory _options,\n uint128 _amount,\n bytes32 _receiver\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeNativeDropOption(_amount, _receiver);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_NATIVE_DROP, option);\n }\n\n // /**\n // * @dev Adds an executor native drop option to the existing options.\n // * @param _options The existing options container.\n // * @param _amount The amount for the native value that is airdropped to the 'receiver'.\n // * @param _receiver The receiver address for the native drop option.\n // * @return options The updated options container.\n // *\n // * @dev When multiples of this option are added, they are summed by the executor on the remote chain.\n // */\n function addExecutorLzReadOption(\n bytes memory _options,\n uint128 _gas,\n uint32 _size,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzReadOption(_gas, _size, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZREAD, option);\n }\n\n /**\n * @dev Adds an executor LZ compose option to the existing options.\n * @param _options The existing options container.\n * @param _index The index for the lzCompose() function call.\n * @param _gas The gasLimit for the lzCompose() function call.\n * @param _value The msg.value for the lzCompose() function call.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed PER index by the executor on the remote chain.\n * @dev If the OApp sends N lzCompose calls on the remote, you must provide N incremented indexes starting with 0.\n * ie. When your remote OApp composes (N = 3) messages, you must set this option for index 0,1,2\n */\n function addExecutorLzComposeOption(\n bytes memory _options,\n uint16 _index,\n uint128 _gas,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzComposeOption(_index, _gas, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZCOMPOSE, option);\n }\n\n /**\n * @dev Adds an executor ordered execution option to the existing options.\n * @param _options The existing options container.\n * @return options The updated options container.\n */\n function addExecutorOrderedExecutionOption(\n bytes memory _options\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_ORDERED_EXECUTION, bytes(\"\"));\n }\n\n /**\n * @dev Adds a DVN pre-crime option to the existing options.\n * @param _options The existing options container.\n * @param _dvnIdx The DVN index for the pre-crime option.\n * @return options The updated options container.\n */\n function addDVNPreCrimeOption(\n bytes memory _options,\n uint8 _dvnIdx\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return addDVNOption(_options, _dvnIdx, DVNOptions.OPTION_TYPE_PRECRIME, bytes(\"\"));\n }\n\n /**\n * @dev Adds an executor option to the existing options.\n * @param _options The existing options container.\n * @param _optionType The type of the executor option.\n * @param _option The encoded data for the executor option.\n * @return options The updated options container.\n */\n function addExecutorOption(\n bytes memory _options,\n uint8 _optionType,\n bytes memory _option\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return\n abi.encodePacked(\n _options,\n ExecutorOptions.WORKER_ID,\n _option.length.toUint16() + 1, // +1 for optionType\n _optionType,\n _option\n );\n }\n\n /**\n * @dev Adds a DVN option to the existing options.\n * @param _options The existing options container.\n * @param _dvnIdx The DVN index for the DVN option.\n * @param _optionType The type of the DVN option.\n * @param _option The encoded data for the DVN option.\n * @return options The updated options container.\n */\n function addDVNOption(\n bytes memory _options,\n uint8 _dvnIdx,\n uint8 _optionType,\n bytes memory _option\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return\n abi.encodePacked(\n _options,\n DVNOptions.WORKER_ID,\n _option.length.toUint16() + 2, // +2 for optionType and dvnIdx\n _dvnIdx,\n _optionType,\n _option\n );\n }\n\n /**\n * @dev Encodes legacy options of type 1.\n * @param _executionGas The gasLimit value passed to lzReceive().\n * @return legacyOptions The encoded legacy options.\n */\n function encodeLegacyOptionsType1(uint256 _executionGas) internal pure returns (bytes memory) {\n if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);\n return abi.encodePacked(TYPE_1, _executionGas);\n }\n\n /**\n * @dev Encodes legacy options of type 2.\n * @param _executionGas The gasLimit value passed to lzReceive().\n * @param _nativeForDst The amount of native air dropped to the receiver.\n * @param _receiver The _nativeForDst receiver address.\n * @return legacyOptions The encoded legacy options of type 2.\n */\n function encodeLegacyOptionsType2(\n uint256 _executionGas,\n uint256 _nativeForDst,\n bytes memory _receiver // @dev Use bytes instead of bytes32 in legacy type 2 for _receiver.\n ) internal pure returns (bytes memory) {\n if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);\n if (_nativeForDst > type(uint128).max) revert InvalidSize(type(uint128).max, _nativeForDst);\n if (_receiver.length > 32) revert InvalidSize(32, _receiver.length);\n return abi.encodePacked(TYPE_2, _executionGas, _nativeForDst, _receiver);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n// @dev Import the 'MessagingFee' and 'MessagingReceipt' so it's exposed to OApp implementers\n// solhint-disable-next-line no-unused-import\nimport { OAppSender, MessagingFee, MessagingReceipt } from \"./OAppSender.sol\";\n// @dev Import the 'Origin' so it's exposed to OApp implementers\n// solhint-disable-next-line no-unused-import\nimport { OAppReceiver, Origin } from \"./OAppReceiver.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OApp\n * @dev Abstract contract serving as the base for OApp implementation, combining OAppSender and OAppReceiver functionality.\n */\nabstract contract OApp is OAppSender, OAppReceiver {\n /**\n * @dev Constructor to initialize the OApp with the provided endpoint and owner.\n * @param _endpoint The address of the LOCAL LayerZero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(address _endpoint, address _delegate) OAppCore(_endpoint, _delegate) {}\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol implementation.\n * @return receiverVersion The version of the OAppReceiver.sol implementation.\n */\n function oAppVersion()\n public\n pure\n virtual\n override(OAppSender, OAppReceiver)\n returns (uint64 senderVersion, uint64 receiverVersion)\n {\n return (SENDER_VERSION, RECEIVER_VERSION);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IOAppCore, ILayerZeroEndpointV2 } from \"./interfaces/IOAppCore.sol\";\n\n/**\n * @title OAppCore\n * @dev Abstract contract implementing the IOAppCore interface with basic OApp configurations.\n */\nabstract contract OAppCore is IOAppCore, Ownable {\n // The LayerZero endpoint associated with the given OApp\n ILayerZeroEndpointV2 public immutable endpoint;\n\n // Mapping to store peers associated with corresponding endpoints\n mapping(uint32 eid => bytes32 peer) public peers;\n\n /**\n * @dev Constructor to initialize the OAppCore with the provided endpoint and delegate.\n * @param _endpoint The address of the LOCAL Layer Zero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n *\n * @dev The delegate typically should be set as the owner of the contract.\n */\n constructor(address _endpoint, address _delegate) {\n endpoint = ILayerZeroEndpointV2(_endpoint);\n\n if (_delegate == address(0)) revert InvalidDelegate();\n endpoint.setDelegate(_delegate);\n }\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.\n * @dev Set this to bytes32(0) to remove the peer address.\n * @dev Peer is a bytes32 to accommodate non-evm chains.\n */\n function setPeer(uint32 _eid, bytes32 _peer) public virtual onlyOwner {\n _setPeer(_eid, _peer);\n }\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n *\n * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.\n * @dev Set this to bytes32(0) to remove the peer address.\n * @dev Peer is a bytes32 to accommodate non-evm chains.\n */\n function _setPeer(uint32 _eid, bytes32 _peer) internal virtual {\n peers[_eid] = _peer;\n emit PeerSet(_eid, _peer);\n }\n\n /**\n * @notice Internal function to get the peer address associated with a specific endpoint; reverts if NOT set.\n * ie. the peer is set to bytes32(0).\n * @param _eid The endpoint ID.\n * @return peer The address of the peer associated with the specified endpoint.\n */\n function _getPeerOrRevert(uint32 _eid) internal view virtual returns (bytes32) {\n bytes32 peer = peers[_eid];\n if (peer == bytes32(0)) revert NoPeer(_eid);\n return peer;\n }\n\n /**\n * @notice Sets the delegate address for the OApp.\n * @param _delegate The address of the delegate to be set.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Provides the ability for a delegate to set configs, on behalf of the OApp, directly on the Endpoint contract.\n */\n function setDelegate(address _delegate) public onlyOwner {\n endpoint.setDelegate(_delegate);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IOAppReceiver, Origin } from \"./interfaces/IOAppReceiver.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OAppReceiver\n * @dev Abstract contract implementing the ILayerZeroReceiver interface and extending OAppCore for OApp receivers.\n */\nabstract contract OAppReceiver is IOAppReceiver, OAppCore {\n // Custom error message for when the caller is not the registered endpoint/\n error OnlyEndpoint(address addr);\n\n // @dev The version of the OAppReceiver implementation.\n // @dev Version is bumped when changes are made to this contract.\n uint64 internal constant RECEIVER_VERSION = 2;\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n *\n * @dev Providing 0 as the default for OAppSender version. Indicates that the OAppSender is not implemented.\n * ie. this is a RECEIVE only OApp.\n * @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions.\n */\n function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {\n return (0, RECEIVER_VERSION);\n }\n\n /**\n * @notice Indicates whether an address is an approved composeMsg sender to the Endpoint.\n * @dev _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @dev _message The lzReceive payload.\n * @param _sender The sender address.\n * @return isSender Is a valid sender.\n *\n * @dev Applications can optionally choose to implement separate composeMsg senders that are NOT the bridging layer.\n * @dev The default sender IS the OAppReceiver implementer.\n */\n function isComposeMsgSender(\n Origin calldata /*_origin*/,\n bytes calldata /*_message*/,\n address _sender\n ) public view virtual returns (bool) {\n return _sender == address(this);\n }\n\n /**\n * @notice Checks if the path initialization is allowed based on the provided origin.\n * @param origin The origin information containing the source endpoint and sender address.\n * @return Whether the path has been initialized.\n *\n * @dev This indicates to the endpoint that the OApp has enabled msgs for this particular path to be received.\n * @dev This defaults to assuming if a peer has been set, its initialized.\n * Can be overridden by the OApp if there is other logic to determine this.\n */\n function allowInitializePath(Origin calldata origin) public view virtual returns (bool) {\n return peers[origin.srcEid] == origin.sender;\n }\n\n /**\n * @notice Retrieves the next nonce for a given source endpoint and sender address.\n * @dev _srcEid The source endpoint ID.\n * @dev _sender The sender address.\n * @return nonce The next nonce.\n *\n * @dev The path nonce starts from 1. If 0 is returned it means that there is NO nonce ordered enforcement.\n * @dev Is required by the off-chain executor to determine the OApp expects msg execution is ordered.\n * @dev This is also enforced by the OApp.\n * @dev By default this is NOT enabled. ie. nextNonce is hardcoded to return 0.\n */\n function nextNonce(uint32 /*_srcEid*/, bytes32 /*_sender*/) public view virtual returns (uint64 nonce) {\n return 0;\n }\n\n /**\n * @dev Entry point for receiving messages or packets from the endpoint.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The payload of the received message.\n * @param _executor The address of the executor for the received message.\n * @param _extraData Additional arbitrary data provided by the corresponding executor.\n *\n * @dev Entry point for receiving msg/packet from the LayerZero endpoint.\n */\n function lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) public payable virtual {\n // Ensures that only the endpoint can attempt to lzReceive() messages to this OApp.\n if (address(endpoint) != msg.sender) revert OnlyEndpoint(msg.sender);\n\n // Ensure that the sender matches the expected peer for the source endpoint.\n if (_getPeerOrRevert(_origin.srcEid) != _origin.sender) revert OnlyPeer(_origin.srcEid, _origin.sender);\n\n // Call the internal OApp implementation of lzReceive.\n _lzReceive(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Internal function to implement lzReceive logic without needing to copy the basic parameter validation.\n */\n function _lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual;\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { SafeERC20, IERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { MessagingParams, MessagingFee, MessagingReceipt } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OAppSender\n * @dev Abstract contract implementing the OAppSender functionality for sending messages to a LayerZero endpoint.\n */\nabstract contract OAppSender is OAppCore {\n using SafeERC20 for IERC20;\n\n // Custom error messages\n error NotEnoughNative(uint256 msgValue);\n error LzTokenUnavailable();\n\n // @dev The version of the OAppSender implementation.\n // @dev Version is bumped when changes are made to this contract.\n uint64 internal constant SENDER_VERSION = 1;\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n *\n * @dev Providing 0 as the default for OAppReceiver version. Indicates that the OAppReceiver is not implemented.\n * ie. this is a SEND only OApp.\n * @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions\n */\n function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {\n return (SENDER_VERSION, 0);\n }\n\n /**\n * @dev Internal function to interact with the LayerZero EndpointV2.quote() for fee calculation.\n * @param _dstEid The destination endpoint ID.\n * @param _message The message payload.\n * @param _options Additional options for the message.\n * @param _payInLzToken Flag indicating whether to pay the fee in LZ tokens.\n * @return fee The calculated MessagingFee for the message.\n * - nativeFee: The native fee for the message.\n * - lzTokenFee: The LZ token fee for the message.\n */\n function _quote(\n uint32 _dstEid,\n bytes memory _message,\n bytes memory _options,\n bool _payInLzToken\n ) internal view virtual returns (MessagingFee memory fee) {\n return\n endpoint.quote(\n MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _payInLzToken),\n address(this)\n );\n }\n\n /**\n * @dev Internal function to interact with the LayerZero EndpointV2.send() for sending a message.\n * @param _dstEid The destination endpoint ID.\n * @param _message The message payload.\n * @param _options Additional options for the message.\n * @param _fee The calculated LayerZero fee for the message.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess fee values sent to the endpoint.\n * @return receipt The receipt for the sent message.\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function _lzSend(\n uint32 _dstEid,\n bytes memory _message,\n bytes memory _options,\n MessagingFee memory _fee,\n address _refundAddress\n ) internal virtual returns (MessagingReceipt memory receipt) {\n // @dev Push corresponding fees to the endpoint, any excess is sent back to the _refundAddress from the endpoint.\n uint256 messageValue = _payNative(_fee.nativeFee);\n if (_fee.lzTokenFee > 0) _payLzToken(_fee.lzTokenFee);\n\n return\n // solhint-disable-next-line check-send-result\n endpoint.send{ value: messageValue }(\n MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _fee.lzTokenFee > 0),\n _refundAddress\n );\n }\n\n /**\n * @dev Internal function to pay the native fee associated with the message.\n * @param _nativeFee The native fee to be paid.\n * @return nativeFee The amount of native currency paid.\n *\n * @dev If the OApp needs to initiate MULTIPLE LayerZero messages in a single transaction,\n * this will need to be overridden because msg.value would contain multiple lzFees.\n * @dev Should be overridden in the event the LayerZero endpoint requires a different native currency.\n * @dev Some EVMs use an ERC20 as a method for paying transactions/gasFees.\n * @dev The endpoint is EITHER/OR, ie. it will NOT support both types of native payment at a time.\n */\n function _payNative(uint256 _nativeFee) internal virtual returns (uint256 nativeFee) {\n if (msg.value != _nativeFee) revert NotEnoughNative(msg.value);\n return _nativeFee;\n }\n\n /**\n * @dev Internal function to pay the LZ token fee associated with the message.\n * @param _lzTokenFee The LZ token fee to be paid.\n *\n * @dev If the caller is trying to pay in the specified lzToken, then the lzTokenFee is passed to the endpoint.\n * @dev Any excess sent, is passed back to the specified _refundAddress in the _lzSend().\n */\n function _payLzToken(uint256 _lzTokenFee) internal virtual {\n // @dev Cannot cache the token because it is not immutable in the endpoint.\n address lzToken = endpoint.lzToken();\n if (lzToken == address(0)) revert LzTokenUnavailable();\n\n // Pay LZ token fee by sending tokens to the endpoint.\n IERC20(lzToken).safeTransferFrom(msg.sender, address(endpoint), _lzTokenFee);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/interfaces/IOAppPreCrimeSimulator.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n// @dev Import the Origin so it's exposed to OAppPreCrimeSimulator implementers.\n// solhint-disable-next-line no-unused-import\nimport { InboundPacket, Origin } from \"../libs/Packet.sol\";\n\n/**\n * @title IOAppPreCrimeSimulator Interface\n * @dev Interface for the preCrime simulation functionality in an OApp.\n */\ninterface IOAppPreCrimeSimulator {\n // @dev simulation result used in PreCrime implementation\n error SimulationResult(bytes result);\n error OnlySelf();\n\n /**\n * @dev Emitted when the preCrime contract address is set.\n * @param preCrimeAddress The address of the preCrime contract.\n */\n event PreCrimeSet(address preCrimeAddress);\n\n /**\n * @dev Retrieves the address of the preCrime contract implementation.\n * @return The address of the preCrime contract.\n */\n function preCrime() external view returns (address);\n\n /**\n * @dev Retrieves the address of the OApp contract.\n * @return The address of the OApp contract.\n */\n function oApp() external view returns (address);\n\n /**\n * @dev Sets the preCrime contract address.\n * @param _preCrime The address of the preCrime contract.\n */\n function setPreCrime(address _preCrime) external;\n\n /**\n * @dev Mocks receiving a packet, then reverts with a series of data to infer the state/result.\n * @param _packets An array of LayerZero InboundPacket objects representing received packets.\n */\n function lzReceiveAndRevert(InboundPacket[] calldata _packets) external payable;\n\n /**\n * @dev checks if the specified peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint Id to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n */\n function isPeer(uint32 _eid, bytes32 _peer) external view returns (bool);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/interfaces/IPreCrime.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\nstruct PreCrimePeer {\n uint32 eid;\n bytes32 preCrime;\n bytes32 oApp;\n}\n\n// TODO not done yet\ninterface IPreCrime {\n error OnlyOffChain();\n\n // for simulate()\n error PacketOversize(uint256 max, uint256 actual);\n error PacketUnsorted();\n error SimulationFailed(bytes reason);\n\n // for preCrime()\n error SimulationResultNotFound(uint32 eid);\n error InvalidSimulationResult(uint32 eid, bytes reason);\n error CrimeFound(bytes crime);\n\n function getConfig(bytes[] calldata _packets, uint256[] calldata _packetMsgValues) external returns (bytes memory);\n\n function simulate(\n bytes[] calldata _packets,\n uint256[] calldata _packetMsgValues\n ) external payable returns (bytes memory);\n\n function buildSimulationResult() external view returns (bytes memory);\n\n function preCrime(\n bytes[] calldata _packets,\n uint256[] calldata _packetMsgValues,\n bytes[] calldata _simulations\n ) external;\n\n function version() external view returns (uint64 major, uint8 minor);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/libs/Packet.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Origin } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\nimport { PacketV1Codec } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol\";\n\n/**\n * @title InboundPacket\n * @dev Structure representing an inbound packet received by the contract.\n */\nstruct InboundPacket {\n Origin origin; // Origin information of the packet.\n uint32 dstEid; // Destination endpointId of the packet.\n address receiver; // Receiver address for the packet.\n bytes32 guid; // Unique identifier of the packet.\n uint256 value; // msg.value of the packet.\n address executor; // Executor address for the packet.\n bytes message; // Message payload of the packet.\n bytes extraData; // Additional arbitrary data for the packet.\n}\n\n/**\n * @title PacketDecoder\n * @dev Library for decoding LayerZero packets.\n */\nlibrary PacketDecoder {\n using PacketV1Codec for bytes;\n\n /**\n * @dev Decode an inbound packet from the given packet data.\n * @param _packet The packet data to decode.\n * @return packet An InboundPacket struct representing the decoded packet.\n */\n function decode(bytes calldata _packet) internal pure returns (InboundPacket memory packet) {\n packet.origin = Origin(_packet.srcEid(), _packet.sender(), _packet.nonce());\n packet.dstEid = _packet.dstEid();\n packet.receiver = _packet.receiverB20();\n packet.guid = _packet.guid();\n packet.message = _packet.message();\n }\n\n /**\n * @dev Decode multiple inbound packets from the given packet data and associated message values.\n * @param _packets An array of packet data to decode.\n * @param _packetMsgValues An array of associated message values for each packet.\n * @return packets An array of InboundPacket structs representing the decoded packets.\n */\n function decode(\n bytes[] calldata _packets,\n uint256[] memory _packetMsgValues\n ) internal pure returns (InboundPacket[] memory packets) {\n packets = new InboundPacket[](_packets.length);\n for (uint256 i = 0; i < _packets.length; i++) {\n bytes calldata packet = _packets[i];\n packets[i] = PacketDecoder.decode(packet);\n // @dev Allows the verifier to specify the msg.value that gets passed in lzReceive.\n packets[i].value = _packetMsgValues[i];\n }\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/OAppPreCrimeSimulator.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IPreCrime } from \"./interfaces/IPreCrime.sol\";\nimport { IOAppPreCrimeSimulator, InboundPacket, Origin } from \"./interfaces/IOAppPreCrimeSimulator.sol\";\n\n/**\n * @title OAppPreCrimeSimulator\n * @dev Abstract contract serving as the base for preCrime simulation functionality in an OApp.\n */\nabstract contract OAppPreCrimeSimulator is IOAppPreCrimeSimulator, Ownable {\n // The address of the preCrime implementation.\n address public preCrime;\n\n /**\n * @dev Retrieves the address of the OApp contract.\n * @return The address of the OApp contract.\n *\n * @dev The simulator contract is the base contract for the OApp by default.\n * @dev If the simulator is a separate contract, override this function.\n */\n function oApp() external view virtual returns (address) {\n return address(this);\n }\n\n /**\n * @dev Sets the preCrime contract address.\n * @param _preCrime The address of the preCrime contract.\n */\n function setPreCrime(address _preCrime) public virtual onlyOwner {\n preCrime = _preCrime;\n emit PreCrimeSet(_preCrime);\n }\n\n /**\n * @dev Interface for pre-crime simulations. Always reverts at the end with the simulation results.\n * @param _packets An array of InboundPacket objects representing received packets to be delivered.\n *\n * @dev WARNING: MUST revert at the end with the simulation results.\n * @dev Gives the preCrime implementation the ability to mock sending packets to the lzReceive function,\n * WITHOUT actually executing them.\n */\n function lzReceiveAndRevert(InboundPacket[] calldata _packets) public payable virtual {\n for (uint256 i = 0; i < _packets.length; i++) {\n InboundPacket calldata packet = _packets[i];\n\n // Ignore packets that are not from trusted peers.\n if (!isPeer(packet.origin.srcEid, packet.origin.sender)) continue;\n\n // @dev Because a verifier is calling this function, it doesnt have access to executor params:\n // - address _executor\n // - bytes calldata _extraData\n // preCrime will NOT work for OApps that rely on these two parameters inside of their _lzReceive().\n // They are instead stubbed to default values, address(0) and bytes(\"\")\n // @dev Calling this.lzReceiveSimulate removes ability for assembly return 0 callstack exit,\n // which would cause the revert to be ignored.\n this.lzReceiveSimulate{ value: packet.value }(\n packet.origin,\n packet.guid,\n packet.message,\n packet.executor,\n packet.extraData\n );\n }\n\n // @dev Revert with the simulation results. msg.sender must implement IPreCrime.buildSimulationResult().\n revert SimulationResult(IPreCrime(msg.sender).buildSimulationResult());\n }\n\n /**\n * @dev Is effectively an internal function because msg.sender must be address(this).\n * Allows resetting the call stack for 'internal' calls.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _guid The unique identifier of the packet.\n * @param _message The message payload of the packet.\n * @param _executor The executor address for the packet.\n * @param _extraData Additional data for the packet.\n */\n function lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) external payable virtual {\n // @dev Ensure ONLY can be called 'internally'.\n if (msg.sender != address(this)) revert OnlySelf();\n _lzReceiveSimulate(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Internal function to handle the OAppPreCrimeSimulator simulated receive.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The GUID of the LayerZero message.\n * @param _message The LayerZero message.\n * @param _executor The address of the off-chain executor.\n * @param _extraData Arbitrary data passed by the msg executor.\n *\n * @dev Enables the preCrime simulator to mock sending lzReceive() messages,\n * routes the msg down from the OAppPreCrimeSimulator, and back up to the OAppReceiver.\n */\n function _lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual;\n\n /**\n * @dev checks if the specified peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint Id to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n */\n function isPeer(uint32 _eid, bytes32 _peer) public view virtual returns (bool);\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/interfaces/IMintableBurnable.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\n/// @title Interface for mintable and burnable tokens\ninterface IMintableBurnable {\n \n /**\n * @notice Burns tokens from a specified account\n * @param _from Address from which tokens will be burned\n * @param _amount Amount of tokens to be burned\n * @return success Indicates whether the operation was successful\n */\n function burn(address _from, uint256 _amount) external returns (bool success);\n\n /**\n * @notice Mints tokens to a specified account\n * @param _to Address to which tokens will be minted\n * @param _amount Amount of tokens to be minted\n * @return success Indicates whether the operation was successful\n */\n function mint(address _to, uint256 _amount) external returns (bool success);\n}" + }, + "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { MessagingReceipt, MessagingFee } from \"@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol\";\n\n/**\n * @dev Struct representing token parameters for the OFT send() operation.\n */\nstruct SendParam {\n uint32 dstEid; // Destination endpoint ID.\n bytes32 to; // Recipient address.\n uint256 amountLD; // Amount to send in local decimals.\n uint256 minAmountLD; // Minimum amount to send in local decimals.\n bytes extraOptions; // Additional options supplied by the caller to be used in the LayerZero message.\n bytes composeMsg; // The composed message for the send() operation.\n bytes oftCmd; // The OFT command to be executed, unused in default OFT implementations.\n}\n\n/**\n * @dev Struct representing OFT limit information.\n * @dev These amounts can change dynamically and are up the specific oft implementation.\n */\nstruct OFTLimit {\n uint256 minAmountLD; // Minimum amount in local decimals that can be sent to the recipient.\n uint256 maxAmountLD; // Maximum amount in local decimals that can be sent to the recipient.\n}\n\n/**\n * @dev Struct representing OFT receipt information.\n */\nstruct OFTReceipt {\n uint256 amountSentLD; // Amount of tokens ACTUALLY debited from the sender in local decimals.\n // @dev In non-default implementations, the amountReceivedLD COULD differ from this value.\n uint256 amountReceivedLD; // Amount of tokens to be received on the remote side.\n}\n\n/**\n * @dev Struct representing OFT fee details.\n * @dev Future proof mechanism to provide a standardized way to communicate fees to things like a UI.\n */\nstruct OFTFeeDetail {\n int256 feeAmountLD; // Amount of the fee in local decimals.\n string description; // Description of the fee.\n}\n\n/**\n * @title IOFT\n * @dev Interface for the OftChain (OFT) token.\n * @dev Does not inherit ERC20 to accommodate usage by OFTAdapter as well.\n * @dev This specific interface ID is '0x02e49c2c'.\n */\ninterface IOFT {\n // Custom error messages\n error InvalidLocalDecimals();\n error SlippageExceeded(uint256 amountLD, uint256 minAmountLD);\n\n // Events\n event OFTSent(\n bytes32 indexed guid, // GUID of the OFT message.\n uint32 dstEid, // Destination Endpoint ID.\n address indexed fromAddress, // Address of the sender on the src chain.\n uint256 amountSentLD, // Amount of tokens sent in local decimals.\n uint256 amountReceivedLD // Amount of tokens received in local decimals.\n );\n event OFTReceived(\n bytes32 indexed guid, // GUID of the OFT message.\n uint32 srcEid, // Source Endpoint ID.\n address indexed toAddress, // Address of the recipient on the dst chain.\n uint256 amountReceivedLD // Amount of tokens received in local decimals.\n );\n\n /**\n * @notice Retrieves interfaceID and the version of the OFT.\n * @return interfaceId The interface ID.\n * @return version The version.\n *\n * @dev interfaceId: This specific interface ID is '0x02e49c2c'.\n * @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.\n * @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.\n * ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)\n */\n function oftVersion() external view returns (bytes4 interfaceId, uint64 version);\n\n /**\n * @notice Retrieves the address of the token associated with the OFT.\n * @return token The address of the ERC20 token implementation.\n */\n function token() external view returns (address);\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the 'token()' to send.\n * @return requiresApproval Needs approval of the underlying token implementation.\n *\n * @dev Allows things like wallet implementers to determine integration requirements,\n * without understanding the underlying token implementation.\n */\n function approvalRequired() external view returns (bool);\n\n /**\n * @notice Retrieves the shared decimals of the OFT.\n * @return sharedDecimals The shared decimals of the OFT.\n */\n function sharedDecimals() external view returns (uint8);\n\n /**\n * @notice Provides the fee breakdown and settings data for an OFT. Unused in the default implementation.\n * @param _sendParam The parameters for the send operation.\n * @return limit The OFT limit information.\n * @return oftFeeDetails The details of OFT fees.\n * @return receipt The OFT receipt information.\n */\n function quoteOFT(\n SendParam calldata _sendParam\n ) external view returns (OFTLimit memory, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory);\n\n /**\n * @notice Provides a quote for the send() operation.\n * @param _sendParam The parameters for the send() operation.\n * @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.\n * @return fee The calculated LayerZero messaging fee from the send() operation.\n *\n * @dev MessagingFee: LayerZero msg fee\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n */\n function quoteSend(SendParam calldata _sendParam, bool _payInLzToken) external view returns (MessagingFee memory);\n\n /**\n * @notice Executes the send() operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The fee information supplied by the caller.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds from fees etc. on the src.\n * @return receipt The LayerZero messaging receipt from the send() operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) external payable returns (MessagingReceipt memory, OFTReceipt memory);\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/libs/OFTComposeMsgCodec.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nlibrary OFTComposeMsgCodec {\n // Offset constants for decoding composed messages\n uint8 private constant NONCE_OFFSET = 8;\n uint8 private constant SRC_EID_OFFSET = 12;\n uint8 private constant AMOUNT_LD_OFFSET = 44;\n uint8 private constant COMPOSE_FROM_OFFSET = 76;\n\n /**\n * @dev Encodes a OFT composed message.\n * @param _nonce The nonce value.\n * @param _srcEid The source endpoint ID.\n * @param _amountLD The amount in local decimals.\n * @param _composeMsg The composed message.\n * @return _msg The encoded Composed message.\n */\n function encode(\n uint64 _nonce,\n uint32 _srcEid,\n uint256 _amountLD,\n bytes memory _composeMsg // 0x[composeFrom][composeMsg]\n ) internal pure returns (bytes memory _msg) {\n _msg = abi.encodePacked(_nonce, _srcEid, _amountLD, _composeMsg);\n }\n\n /**\n * @dev Retrieves the nonce for the composed message.\n * @param _msg The message.\n * @return The nonce value.\n */\n function nonce(bytes calldata _msg) internal pure returns (uint64) {\n return uint64(bytes8(_msg[:NONCE_OFFSET]));\n }\n\n /**\n * @dev Retrieves the source endpoint ID for the composed message.\n * @param _msg The message.\n * @return The source endpoint ID.\n */\n function srcEid(bytes calldata _msg) internal pure returns (uint32) {\n return uint32(bytes4(_msg[NONCE_OFFSET:SRC_EID_OFFSET]));\n }\n\n /**\n * @dev Retrieves the amount in local decimals from the composed message.\n * @param _msg The message.\n * @return The amount in local decimals.\n */\n function amountLD(bytes calldata _msg) internal pure returns (uint256) {\n return uint256(bytes32(_msg[SRC_EID_OFFSET:AMOUNT_LD_OFFSET]));\n }\n\n /**\n * @dev Retrieves the composeFrom value from the composed message.\n * @param _msg The message.\n * @return The composeFrom value.\n */\n function composeFrom(bytes calldata _msg) internal pure returns (bytes32) {\n return bytes32(_msg[AMOUNT_LD_OFFSET:COMPOSE_FROM_OFFSET]);\n }\n\n /**\n * @dev Retrieves the composed message.\n * @param _msg The message.\n * @return The composed message.\n */\n function composeMsg(bytes calldata _msg) internal pure returns (bytes memory) {\n return _msg[COMPOSE_FROM_OFFSET:];\n }\n\n /**\n * @dev Converts an address to bytes32.\n * @param _addr The address to convert.\n * @return The bytes32 representation of the address.\n */\n function addressToBytes32(address _addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(_addr)));\n }\n\n /**\n * @dev Converts bytes32 to an address.\n * @param _b The bytes32 value to convert.\n * @return The address representation of bytes32.\n */\n function bytes32ToAddress(bytes32 _b) internal pure returns (address) {\n return address(uint160(uint256(_b)));\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/libs/OFTMsgCodec.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nlibrary OFTMsgCodec {\n // Offset constants for encoding and decoding OFT messages\n uint8 private constant SEND_TO_OFFSET = 32;\n uint8 private constant SEND_AMOUNT_SD_OFFSET = 40;\n\n /**\n * @dev Encodes an OFT LayerZero message.\n * @param _sendTo The recipient address.\n * @param _amountShared The amount in shared decimals.\n * @param _composeMsg The composed message.\n * @return _msg The encoded message.\n * @return hasCompose A boolean indicating whether the message has a composed payload.\n */\n function encode(\n bytes32 _sendTo,\n uint64 _amountShared,\n bytes memory _composeMsg\n ) internal view returns (bytes memory _msg, bool hasCompose) {\n hasCompose = _composeMsg.length > 0;\n // @dev Remote chains will want to know the composed function caller ie. msg.sender on the src.\n _msg = hasCompose\n ? abi.encodePacked(_sendTo, _amountShared, addressToBytes32(msg.sender), _composeMsg)\n : abi.encodePacked(_sendTo, _amountShared);\n }\n\n /**\n * @dev Checks if the OFT message is composed.\n * @param _msg The OFT message.\n * @return A boolean indicating whether the message is composed.\n */\n function isComposed(bytes calldata _msg) internal pure returns (bool) {\n return _msg.length > SEND_AMOUNT_SD_OFFSET;\n }\n\n /**\n * @dev Retrieves the recipient address from the OFT message.\n * @param _msg The OFT message.\n * @return The recipient address.\n */\n function sendTo(bytes calldata _msg) internal pure returns (bytes32) {\n return bytes32(_msg[:SEND_TO_OFFSET]);\n }\n\n /**\n * @dev Retrieves the amount in shared decimals from the OFT message.\n * @param _msg The OFT message.\n * @return The amount in shared decimals.\n */\n function amountSD(bytes calldata _msg) internal pure returns (uint64) {\n return uint64(bytes8(_msg[SEND_TO_OFFSET:SEND_AMOUNT_SD_OFFSET]));\n }\n\n /**\n * @dev Retrieves the composed message from the OFT message.\n * @param _msg The OFT message.\n * @return The composed message.\n */\n function composeMsg(bytes calldata _msg) internal pure returns (bytes memory) {\n return _msg[SEND_AMOUNT_SD_OFFSET:];\n }\n\n /**\n * @dev Converts an address to bytes32.\n * @param _addr The address to convert.\n * @return The bytes32 representation of the address.\n */\n function addressToBytes32(address _addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(_addr)));\n }\n\n /**\n * @dev Converts bytes32 to an address.\n * @param _b The bytes32 value to convert.\n * @return The address representation of bytes32.\n */\n function bytes32ToAddress(bytes32 _b) internal pure returns (address) {\n return address(uint160(uint256(_b)));\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/MintBurnOFTAdapter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\n// External imports\nimport { IERC20, IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\n// Local imports\nimport { IMintableBurnable } from \"./interfaces/IMintableBurnable.sol\";\nimport { OFTCore } from \"./OFTCore.sol\";\n\n/**\n * @title MintBurnOFTAdapter\n * @notice A variant of the standard OFT Adapter that uses an existing ERC20's mint and burn mechanisms for cross-chain transfers.\n *\n * @dev Inherits from OFTCore and provides implementations for _debit and _credit functions using a mintable and burnable token.\n */\nabstract contract MintBurnOFTAdapter is OFTCore {\n /// @dev The underlying ERC20 token.\n IERC20 internal immutable innerToken;\n\n /// @notice The contract responsible for minting and burning tokens.\n IMintableBurnable public immutable minterBurner;\n\n /**\n * @notice Initializes the MintBurnOFTAdapter contract.\n *\n * @param _token The address of the underlying ERC20 token.\n * @param _minterBurner The contract responsible for minting and burning tokens.\n * @param _lzEndpoint The LayerZero endpoint address.\n * @param _delegate The address of the delegate.\n *\n * @dev Calls the OFTCore constructor with the token's decimals, the endpoint, and the delegate.\n */\n constructor(\n address _token,\n IMintableBurnable _minterBurner,\n address _lzEndpoint,\n address _delegate\n ) OFTCore(IERC20Metadata(_token).decimals(), _lzEndpoint, _delegate) {\n innerToken = IERC20(_token);\n minterBurner = _minterBurner;\n }\n\n /**\n * @notice Retrieves the address of the underlying ERC20 token.\n *\n * @return The address of the adapted ERC20 token.\n *\n * @dev In the case of MintBurnOFTAdapter, address(this) and erc20 are NOT the same contract.\n */\n function token() public view returns (address) {\n return address(innerToken);\n }\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the underlying token to send.\n *\n * @return requiresApproval True if approval is required, false otherwise.\n *\n * @dev In this MintBurnOFTAdapter, approval is NOT required because it uses mint and burn privileges.\n */\n function approvalRequired() external pure virtual returns (bool) {\n return false;\n }\n\n /**\n * @notice Burns tokens from the sender's balance to prepare for sending.\n *\n * @param _from The address to debit the tokens from.\n * @param _amountLD The amount of tokens to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination chain ID.\n *\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, i.e., 1 token in, 1 token out.\n * If the 'innerToken' applies something like a transfer fee, the default will NOT work.\n * A pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual override returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n (amountSentLD, amountReceivedLD) = _debitView(_amountLD, _minAmountLD, _dstEid);\n // Burns tokens from the caller.\n minterBurner.burn(_from, amountSentLD);\n }\n\n /**\n * @notice Mints tokens to the specified address upon receiving them.\n *\n * @param _to The address to credit the tokens to.\n * @param _amountLD The amount of tokens to credit in local decimals.\n *\n * @return amountReceivedLD The amount of tokens actually received in local decimals.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, i.e., 1 token in, 1 token out.\n * If the 'innerToken' applies something like a transfer fee, the default will NOT work.\n * A pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /* _srcEid */\n ) internal virtual override returns (uint256 amountReceivedLD) {\n if (_to == address(0x0)) _to = address(0xdead); // _mint(...) does not support address(0x0)\n // Mints the tokens and transfers to the recipient.\n minterBurner.mint(_to, _amountLD);\n // In the case of NON-default OFTAdapter, the amountLD MIGHT not be equal to amountReceivedLD.\n return _amountLD;\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/OFTAdapter.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IERC20Metadata, IERC20 } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IOFT, OFTCore } from \"./OFTCore.sol\";\n\n/**\n * @title OFTAdapter Contract\n * @dev OFTAdapter is a contract that adapts an ERC-20 token to the OFT functionality.\n *\n * @dev For existing ERC20 tokens, this can be used to convert the token to crosschain compatibility.\n * @dev WARNING: ONLY 1 of these should exist for a given global mesh,\n * unless you make a NON-default implementation of OFT and needs to be done very carefully.\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountSentLD/amountReceivedLD.\n */\nabstract contract OFTAdapter is OFTCore {\n using SafeERC20 for IERC20;\n\n IERC20 internal immutable innerToken;\n\n /**\n * @dev Constructor for the OFTAdapter contract.\n * @param _token The address of the ERC-20 token to be adapted.\n * @param _lzEndpoint The LayerZero endpoint address.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(\n address _token,\n address _lzEndpoint,\n address _delegate\n ) OFTCore(IERC20Metadata(_token).decimals(), _lzEndpoint, _delegate) {\n innerToken = IERC20(_token);\n }\n\n /**\n * @dev Retrieves the address of the underlying ERC20 implementation.\n * @return The address of the adapted ERC-20 token.\n *\n * @dev In the case of OFTAdapter, address(this) and erc20 are NOT the same contract.\n */\n function token() public view returns (address) {\n return address(innerToken);\n }\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the 'token()' to send.\n * @return requiresApproval Needs approval of the underlying token implementation.\n *\n * @dev In the case of default OFTAdapter, approval is required.\n * @dev In non-default OFTAdapter contracts with something like mint and burn privileges, it would NOT need approval.\n */\n function approvalRequired() external pure virtual returns (bool) {\n return true;\n }\n\n /**\n * @dev Locks tokens from the sender's specified balance in this contract.\n * @param _from The address to debit from.\n * @param _amountLD The amount of tokens to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination chain ID.\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev msg.sender will need to approve this _amountLD of tokens to be locked inside of the contract.\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual override returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n (amountSentLD, amountReceivedLD) = _debitView(_amountLD, _minAmountLD, _dstEid);\n // @dev Lock tokens by moving them into this contract from the caller.\n innerToken.safeTransferFrom(_from, address(this), amountSentLD);\n }\n\n /**\n * @dev Credits tokens to the specified address.\n * @param _to The address to credit the tokens to.\n * @param _amountLD The amount of tokens to credit in local decimals.\n * @dev _srcEid The source chain ID.\n * @return amountReceivedLD The amount of tokens ACTUALLY received in local decimals.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /*_srcEid*/\n ) internal virtual override returns (uint256 amountReceivedLD) {\n // @dev Unlock the tokens and transfer to the recipient.\n innerToken.safeTransfer(_to, _amountLD);\n // @dev In the case of NON-default OFTAdapter, the amountLD MIGHT not be == amountReceivedLD.\n return _amountLD;\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/OFTCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { OApp, Origin } from \"@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol\";\nimport { OAppOptionsType3 } from \"@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol\";\nimport { IOAppMsgInspector } from \"@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppMsgInspector.sol\";\n\nimport { OAppPreCrimeSimulator } from \"@layerzerolabs/oapp-evm/contracts/precrime/OAppPreCrimeSimulator.sol\";\n\nimport { IOFT, SendParam, OFTLimit, OFTReceipt, OFTFeeDetail, MessagingReceipt, MessagingFee } from \"./interfaces/IOFT.sol\";\nimport { OFTMsgCodec } from \"./libs/OFTMsgCodec.sol\";\nimport { OFTComposeMsgCodec } from \"./libs/OFTComposeMsgCodec.sol\";\n\n/**\n * @title OFTCore\n * @dev Abstract contract for the OftChain (OFT) token.\n */\nabstract contract OFTCore is IOFT, OApp, OAppPreCrimeSimulator, OAppOptionsType3 {\n using OFTMsgCodec for bytes;\n using OFTMsgCodec for bytes32;\n\n // @notice Provides a conversion rate when swapping between denominations of SD and LD\n // - shareDecimals == SD == shared Decimals\n // - localDecimals == LD == local decimals\n // @dev Considers that tokens have different decimal amounts on various chains.\n // @dev eg.\n // For a token\n // - locally with 4 decimals --> 1.2345 => uint(12345)\n // - remotely with 2 decimals --> 1.23 => uint(123)\n // - The conversion rate would be 10 ** (4 - 2) = 100\n // @dev If you want to send 1.2345 -> (uint 12345), you CANNOT represent that value on the remote,\n // you can only display 1.23 -> uint(123).\n // @dev To preserve the dust that would otherwise be lost on that conversion,\n // we need to unify a denomination that can be represented on ALL chains inside of the OFT mesh\n uint256 public immutable decimalConversionRate;\n\n // @notice Msg types that are used to identify the various OFT operations.\n // @dev This can be extended in child contracts for non-default oft operations\n // @dev These values are used in things like combineOptions() in OAppOptionsType3.sol.\n uint16 public constant SEND = 1;\n uint16 public constant SEND_AND_CALL = 2;\n\n // Address of an optional contract to inspect both 'message' and 'options'\n address public msgInspector;\n event MsgInspectorSet(address inspector);\n\n /**\n * @dev Constructor.\n * @param _localDecimals The decimals of the token on the local chain (this chain).\n * @param _endpoint The address of the LayerZero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(uint8 _localDecimals, address _endpoint, address _delegate) OApp(_endpoint, _delegate) {\n if (_localDecimals < sharedDecimals()) revert InvalidLocalDecimals();\n decimalConversionRate = 10 ** (_localDecimals - sharedDecimals());\n }\n\n /**\n * @notice Retrieves interfaceID and the version of the OFT.\n * @return interfaceId The interface ID.\n * @return version The version.\n *\n * @dev interfaceId: This specific interface ID is '0x02e49c2c'.\n * @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.\n * @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.\n * ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)\n */\n function oftVersion() external pure virtual returns (bytes4 interfaceId, uint64 version) {\n return (type(IOFT).interfaceId, 1);\n }\n\n /**\n * @dev Retrieves the shared decimals of the OFT.\n * @return The shared decimals of the OFT.\n *\n * @dev Sets an implicit cap on the amount of tokens, over uint64.max() will need some sort of outbound cap / totalSupply cap\n * Lowest common decimal denominator between chains.\n * Defaults to 6 decimal places to provide up to 18,446,744,073,709.551615 units (max uint64).\n * For tokens exceeding this totalSupply(), they will need to override the sharedDecimals function with something smaller.\n * ie. 4 sharedDecimals would be 1,844,674,407,370,955.1615\n */\n function sharedDecimals() public view virtual returns (uint8) {\n return 6;\n }\n\n /**\n * @dev Sets the message inspector address for the OFT.\n * @param _msgInspector The address of the message inspector.\n *\n * @dev This is an optional contract that can be used to inspect both 'message' and 'options'.\n * @dev Set it to address(0) to disable it, or set it to a contract address to enable it.\n */\n function setMsgInspector(address _msgInspector) public virtual onlyOwner {\n msgInspector = _msgInspector;\n emit MsgInspectorSet(_msgInspector);\n }\n\n /**\n * @notice Provides the fee breakdown and settings data for an OFT. Unused in the default implementation.\n * @param _sendParam The parameters for the send operation.\n * @return oftLimit The OFT limit information.\n * @return oftFeeDetails The details of OFT fees.\n * @return oftReceipt The OFT receipt information.\n */\n function quoteOFT(\n SendParam calldata _sendParam\n )\n external\n view\n virtual\n returns (OFTLimit memory oftLimit, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory oftReceipt)\n {\n uint256 minAmountLD = 0; // Unused in the default implementation.\n uint256 maxAmountLD = IERC20(this.token()).totalSupply(); // Unused in the default implementation.\n oftLimit = OFTLimit(minAmountLD, maxAmountLD);\n\n // Unused in the default implementation; reserved for future complex fee details.\n oftFeeDetails = new OFTFeeDetail[](0);\n\n // @dev This is the same as the send() operation, but without the actual send.\n // - amountSentLD is the amount in local decimals that would be sent from the sender.\n // - amountReceivedLD is the amount in local decimals that will be credited to the recipient on the remote OFT instance.\n // @dev The amountSentLD MIGHT not equal the amount the user actually receives. HOWEVER, the default does.\n (uint256 amountSentLD, uint256 amountReceivedLD) = _debitView(\n _sendParam.amountLD,\n _sendParam.minAmountLD,\n _sendParam.dstEid\n );\n oftReceipt = OFTReceipt(amountSentLD, amountReceivedLD);\n }\n\n /**\n * @notice Provides a quote for the send() operation.\n * @param _sendParam The parameters for the send() operation.\n * @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.\n * @return msgFee The calculated LayerZero messaging fee from the send() operation.\n *\n * @dev MessagingFee: LayerZero msg fee\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n */\n function quoteSend(\n SendParam calldata _sendParam,\n bool _payInLzToken\n ) external view virtual returns (MessagingFee memory msgFee) {\n // @dev mock the amount to receive, this is the same operation used in the send().\n // The quote is as similar as possible to the actual send() operation.\n (, uint256 amountReceivedLD) = _debitView(_sendParam.amountLD, _sendParam.minAmountLD, _sendParam.dstEid);\n\n // @dev Builds the options and OFT message to quote in the endpoint.\n (bytes memory message, bytes memory options) = _buildMsgAndOptions(_sendParam, amountReceivedLD);\n\n // @dev Calculates the LayerZero fee for the send() operation.\n return _quote(_sendParam.dstEid, message, options, _payInLzToken);\n }\n\n /**\n * @dev Executes the send operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The calculated fee for the send() operation.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds.\n * @return msgReceipt The receipt for the send operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) external payable virtual returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) {\n return _send(_sendParam, _fee, _refundAddress);\n }\n\n /**\n * @dev Internal function to execute the send operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The calculated fee for the send() operation.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds.\n * @return msgReceipt The receipt for the send operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function _send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) internal virtual returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) {\n // @dev Applies the token transfers regarding this send() operation.\n // - amountSentLD is the amount in local decimals that was ACTUALLY sent/debited from the sender.\n // - amountReceivedLD is the amount in local decimals that will be received/credited to the recipient on the remote OFT instance.\n (uint256 amountSentLD, uint256 amountReceivedLD) = _debit(\n msg.sender,\n _sendParam.amountLD,\n _sendParam.minAmountLD,\n _sendParam.dstEid\n );\n\n // @dev Builds the options and OFT message to quote in the endpoint.\n (bytes memory message, bytes memory options) = _buildMsgAndOptions(_sendParam, amountReceivedLD);\n\n // @dev Sends the message to the LayerZero endpoint and returns the LayerZero msg receipt.\n msgReceipt = _lzSend(_sendParam.dstEid, message, options, _fee, _refundAddress);\n // @dev Formulate the OFT receipt.\n oftReceipt = OFTReceipt(amountSentLD, amountReceivedLD);\n\n emit OFTSent(msgReceipt.guid, _sendParam.dstEid, msg.sender, amountSentLD, amountReceivedLD);\n }\n\n /**\n * @dev Internal function to build the message and options.\n * @param _sendParam The parameters for the send() operation.\n * @param _amountLD The amount in local decimals.\n * @return message The encoded message.\n * @return options The encoded options.\n */\n function _buildMsgAndOptions(\n SendParam calldata _sendParam,\n uint256 _amountLD\n ) internal view virtual returns (bytes memory message, bytes memory options) {\n bool hasCompose;\n // @dev This generated message has the msg.sender encoded into the payload so the remote knows who the caller is.\n (message, hasCompose) = OFTMsgCodec.encode(\n _sendParam.to,\n _toSD(_amountLD),\n // @dev Must be include a non empty bytes if you want to compose, EVEN if you dont need it on the remote.\n // EVEN if you dont require an arbitrary payload to be sent... eg. '0x01'\n _sendParam.composeMsg\n );\n // @dev Change the msg type depending if its composed or not.\n uint16 msgType = hasCompose ? SEND_AND_CALL : SEND;\n // @dev Combine the callers _extraOptions with the enforced options via the OAppOptionsType3.\n options = combineOptions(_sendParam.dstEid, msgType, _sendParam.extraOptions);\n\n // @dev Optionally inspect the message and options depending if the OApp owner has set a msg inspector.\n // @dev If it fails inspection, needs to revert in the implementation. ie. does not rely on return boolean\n address inspector = msgInspector; // caches the msgInspector to avoid potential double storage read\n if (inspector != address(0)) IOAppMsgInspector(inspector).inspect(message, options);\n }\n\n /**\n * @dev Internal function to handle the receive on the LayerZero endpoint.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The encoded message.\n * @dev _executor The address of the executor.\n * @dev _extraData Additional data.\n */\n function _lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address /*_executor*/, // @dev unused in the default implementation.\n bytes calldata /*_extraData*/ // @dev unused in the default implementation.\n ) internal virtual override {\n // @dev The src sending chain doesnt know the address length on this chain (potentially non-evm)\n // Thus everything is bytes32() encoded in flight.\n address toAddress = _message.sendTo().bytes32ToAddress();\n // @dev Credit the amountLD to the recipient and return the ACTUAL amount the recipient received in local decimals\n uint256 amountReceivedLD = _credit(toAddress, _toLD(_message.amountSD()), _origin.srcEid);\n\n if (_message.isComposed()) {\n // @dev Proprietary composeMsg format for the OFT.\n bytes memory composeMsg = OFTComposeMsgCodec.encode(\n _origin.nonce,\n _origin.srcEid,\n amountReceivedLD,\n _message.composeMsg()\n );\n\n // @dev Stores the lzCompose payload that will be executed in a separate tx.\n // Standardizes functionality for executing arbitrary contract invocation on some non-evm chains.\n // @dev The off-chain executor will listen and process the msg based on the src-chain-callers compose options passed.\n // @dev The index is used when a OApp needs to compose multiple msgs on lzReceive.\n // For default OFT implementation there is only 1 compose msg per lzReceive, thus its always 0.\n endpoint.sendCompose(toAddress, _guid, 0 /* the index of the composed message*/, composeMsg);\n }\n\n emit OFTReceived(_guid, _origin.srcEid, toAddress, amountReceivedLD);\n }\n\n /**\n * @dev Internal function to handle the OAppPreCrimeSimulator simulated receive.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The LayerZero message.\n * @param _executor The address of the off-chain executor.\n * @param _extraData Arbitrary data passed by the msg executor.\n *\n * @dev Enables the preCrime simulator to mock sending lzReceive() messages,\n * routes the msg down from the OAppPreCrimeSimulator, and back up to the OAppReceiver.\n */\n function _lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual override {\n _lzReceive(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Check if the peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint ID to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n *\n * @dev Enables OAppPreCrimeSimulator to check whether a potential Inbound Packet is from a trusted source.\n */\n function isPeer(uint32 _eid, bytes32 _peer) public view virtual override returns (bool) {\n return peers[_eid] == _peer;\n }\n\n /**\n * @dev Internal function to remove dust from the given local decimal amount.\n * @param _amountLD The amount in local decimals.\n * @return amountLD The amount after removing dust.\n *\n * @dev Prevents the loss of dust when moving amounts between chains with different decimals.\n * @dev eg. uint(123) with a conversion rate of 100 becomes uint(100).\n */\n function _removeDust(uint256 _amountLD) internal view virtual returns (uint256 amountLD) {\n return (_amountLD / decimalConversionRate) * decimalConversionRate;\n }\n\n /**\n * @dev Internal function to convert an amount from shared decimals into local decimals.\n * @param _amountSD The amount in shared decimals.\n * @return amountLD The amount in local decimals.\n */\n function _toLD(uint64 _amountSD) internal view virtual returns (uint256 amountLD) {\n return _amountSD * decimalConversionRate;\n }\n\n /**\n * @dev Internal function to convert an amount from local decimals into shared decimals.\n * @param _amountLD The amount in local decimals.\n * @return amountSD The amount in shared decimals.\n */\n function _toSD(uint256 _amountLD) internal view virtual returns (uint64 amountSD) {\n return uint64(_amountLD / decimalConversionRate);\n }\n\n /**\n * @dev Internal function to mock the amount mutation from a OFT debit() operation.\n * @param _amountLD The amount to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @dev _dstEid The destination endpoint ID.\n * @return amountSentLD The amount sent, in local decimals.\n * @return amountReceivedLD The amount to be received on the remote chain, in local decimals.\n *\n * @dev This is where things like fees would be calculated and deducted from the amount to be received on the remote.\n */\n function _debitView(\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 /*_dstEid*/\n ) internal view virtual returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n // @dev Remove the dust so nothing is lost on the conversion between chains with different decimals for the token.\n amountSentLD = _removeDust(_amountLD);\n // @dev The amount to send is the same as amount received in the default implementation.\n amountReceivedLD = amountSentLD;\n\n // @dev Check for slippage.\n if (amountReceivedLD < _minAmountLD) {\n revert SlippageExceeded(amountReceivedLD, _minAmountLD);\n }\n }\n\n /**\n * @dev Internal function to perform a debit operation.\n * @param _from The address to debit.\n * @param _amountLD The amount to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination endpoint ID.\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev Defined here but are intended to be overriden depending on the OFT implementation.\n * @dev Depending on OFT implementation the _amountLD could differ from the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual returns (uint256 amountSentLD, uint256 amountReceivedLD);\n\n /**\n * @dev Internal function to perform a credit operation.\n * @param _to The address to credit.\n * @param _amountLD The amount to credit in local decimals.\n * @param _srcEid The source endpoint ID.\n * @return amountReceivedLD The amount ACTUALLY received in local decimals.\n *\n * @dev Defined here but are intended to be overriden depending on the OFT implementation.\n * @dev Depending on OFT implementation the _amountLD could differ from the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 _srcEid\n ) internal virtual returns (uint256 amountReceivedLD);\n}\n" + }, + "@openzeppelin/contracts/access/AccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/AccessControl.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\nimport \"../utils/Context.sol\";\nimport \"../utils/Strings.sol\";\nimport \"../utils/introspection/ERC165.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\n * members except through off-chain means by accessing the contract event logs. Some\n * applications may benefit from on-chain enumerability, for those cases see\n * {AccessControlEnumerable}.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```\n * function foo() public {\n * require(hasRole(MY_ROLE, msg.sender));\n * ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it.\n */\nabstract contract AccessControl is Context, IAccessControl, ERC165 {\n struct RoleData {\n mapping(address => bool) members;\n bytes32 adminRole;\n }\n\n mapping(bytes32 => RoleData) private _roles;\n\n bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n /**\n * @dev Modifier that checks that an account has a specific role. Reverts\n * with a standardized message including the required role.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n *\n * _Available since v4.1._\n */\n modifier onlyRole(bytes32 role) {\n _checkRole(role, _msgSender());\n _;\n }\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) public view override returns (bool) {\n return _roles[role].members[account];\n }\n\n /**\n * @dev Revert with a standard message if `account` is missing `role`.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n */\n function _checkRole(bytes32 role, address account) internal view {\n if (!hasRole(role, account)) {\n revert(\n string(\n abi.encodePacked(\n \"AccessControl: account \",\n Strings.toHexString(uint160(account), 20),\n \" is missing role \",\n Strings.toHexString(uint256(role), 32)\n )\n )\n );\n }\n }\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) public view override returns (bytes32) {\n return _roles[role].adminRole;\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _grantRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _revokeRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been revoked `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) public virtual override {\n require(account == _msgSender(), \"AccessControl: can only renounce roles for self\");\n\n _revokeRole(role, account);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event. Note that unlike {grantRole}, this function doesn't perform any\n * checks on the calling account.\n *\n * [WARNING]\n * ====\n * This function should only be called from the constructor when setting\n * up the initial roles for the system.\n *\n * Using this function in any other way is effectively circumventing the admin\n * system imposed by {AccessControl}.\n * ====\n *\n * NOTE: This function is deprecated in favor of {_grantRole}.\n */\n function _setupRole(bytes32 role, address account) internal virtual {\n _grantRole(role, account);\n }\n\n /**\n * @dev Sets `adminRole` as ``role``'s admin role.\n *\n * Emits a {RoleAdminChanged} event.\n */\n function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n bytes32 previousAdminRole = getRoleAdmin(role);\n _roles[role].adminRole = adminRole;\n emit RoleAdminChanged(role, previousAdminRole, adminRole);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * Internal function without access restriction.\n */\n function _grantRole(bytes32 role, address account) internal virtual {\n if (!hasRole(role, account)) {\n _roles[role].members[account] = true;\n emit RoleGranted(role, account, _msgSender());\n }\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * Internal function without access restriction.\n */\n function _revokeRole(bytes32 role, address account) internal virtual {\n if (hasRole(role, account)) {\n _roles[role].members[account] = false;\n emit RoleRevoked(role, account, _msgSender());\n }\n }\n}\n" + }, + "@openzeppelin/contracts/access/AccessControlEnumerable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/AccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControlEnumerable.sol\";\nimport \"./AccessControl.sol\";\nimport \"../utils/structs/EnumerableSet.sol\";\n\n/**\n * @dev Extension of {AccessControl} that allows enumerating the members of each role.\n */\nabstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {\n using EnumerableSet for EnumerableSet.AddressSet;\n\n mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) public view override returns (address) {\n return _roleMembers[role].at(index);\n }\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) public view override returns (uint256) {\n return _roleMembers[role].length();\n }\n\n /**\n * @dev Overload {_grantRole} to track enumerable memberships\n */\n function _grantRole(bytes32 role, address account) internal virtual override {\n super._grantRole(role, account);\n _roleMembers[role].add(account);\n }\n\n /**\n * @dev Overload {_revokeRole} to track enumerable memberships\n */\n function _revokeRole(bytes32 role, address account) internal virtual override {\n super._revokeRole(role, account);\n _roleMembers[role].remove(account);\n }\n}\n" + }, + "@openzeppelin/contracts/access/IAccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n /**\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n *\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n * {RoleAdminChanged} not being emitted signaling this.\n *\n * _Available since v3.1._\n */\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n /**\n * @dev Emitted when `account` is granted `role`.\n *\n * `sender` is the account that originated the contract call, an admin role\n * bearer except when using {AccessControl-_setupRole}.\n */\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Emitted when `account` is revoked `role`.\n *\n * `sender` is the account that originated the contract call:\n * - if using `revokeRole`, it is the admin role bearer\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\n */\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) external view returns (bool);\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been granted `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) external;\n}\n" + }, + "@openzeppelin/contracts/access/IAccessControlEnumerable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\n\n/**\n * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.\n */\ninterface IAccessControlEnumerable is IAccessControl {\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) external view returns (address);\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) external view returns (uint256);\n}\n" + }, + "@openzeppelin/contracts/access/Ownable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n" + }, + "@openzeppelin/contracts/security/Pausable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/ERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/ERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"./extensions/IERC20Metadata.sol\";\nimport \"../../utils/Context.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20, IERC20Metadata {\n mapping(address => uint256) private _balances;\n\n mapping(address => mapping(address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n\n /**\n * @dev Sets the values for {name} and {symbol}.\n *\n * The default value of {decimals} is 18. To select a different value for\n * {decimals} you should overload it.\n *\n * All two of these values are immutable: they can only be set once during\n * construction.\n */\n constructor(string memory name_, string memory symbol_) {\n _name = name_;\n _symbol = symbol_;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless this function is\n * overridden;\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `recipient` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n _approve(_msgSender(), spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * Requirements:\n *\n * - `sender` and `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n * - the caller must have allowance for ``sender``'s tokens of at least\n * `amount`.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public virtual override returns (bool) {\n _transfer(sender, recipient, amount);\n\n uint256 currentAllowance = _allowances[sender][_msgSender()];\n require(currentAllowance >= amount, \"ERC20: transfer amount exceeds allowance\");\n unchecked {\n _approve(sender, _msgSender(), currentAllowance - amount);\n }\n\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n uint256 currentAllowance = _allowances[_msgSender()][spender];\n require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n unchecked {\n _approve(_msgSender(), spender, currentAllowance - subtractedValue);\n }\n\n return true;\n }\n\n /**\n * @dev Moves `amount` of tokens from `sender` to `recipient`.\n *\n * This internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `sender` cannot be the zero address.\n * - `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n */\n function _transfer(\n address sender,\n address recipient,\n uint256 amount\n ) internal virtual {\n require(sender != address(0), \"ERC20: transfer from the zero address\");\n require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(sender, recipient, amount);\n\n uint256 senderBalance = _balances[sender];\n require(senderBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n unchecked {\n _balances[sender] = senderBalance - amount;\n }\n _balances[recipient] += amount;\n\n emit Transfer(sender, recipient, amount);\n\n _afterTokenTransfer(sender, recipient, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply += amount;\n _balances[account] += amount;\n emit Transfer(address(0), account, amount);\n\n _afterTokenTransfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n uint256 accountBalance = _balances[account];\n require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n unchecked {\n _balances[account] = accountBalance - amount;\n }\n _totalSupply -= amount;\n\n emit Transfer(account, address(0), amount);\n\n _afterTokenTransfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n\n /**\n * @dev Hook that is called after any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * has been transferred to `to`.\n * - when `from` is zero, `amount` tokens have been minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _afterTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC721 token receiver interface\n * @dev Interface for any contract that wants to support safeTransfers\n * from ERC721 asset contracts.\n */\ninterface IERC721Receiver {\n /**\n * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}\n * by `operator` from `from`, this function is called.\n *\n * It must return its Solidity selector to confirm the token transfer.\n * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.\n *\n * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.\n */\n function onERC721Received(\n address operator,\n address from,\n uint256 tokenId,\n bytes calldata data\n ) external returns (bytes4);\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/ERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC165.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n *\n * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.\n */\nabstract contract ERC165 is IERC165 {\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IERC165).interfaceId;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/IERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeMath.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)\n\npragma solidity ^0.8.0;\n\n// CAUTION\n// This version of SafeMath should only be used with Solidity 0.8 or later,\n// because it relies on the compiler's built in overflow checks.\n\n/**\n * @dev Wrappers over Solidity's arithmetic operations.\n *\n * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler\n * now has built in overflow checking.\n */\nlibrary SafeMath {\n /**\n * @dev Returns the addition of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n uint256 c = a + b;\n if (c < a) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the substraction of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b > a) return (false, 0);\n return (true, a - b);\n }\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n // benefit is lost if 'b' is also tested.\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n if (a == 0) return (true, 0);\n uint256 c = a * b;\n if (c / a != b) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the division of two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a / b);\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a % b);\n }\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `+` operator.\n *\n * Requirements:\n *\n * - Addition cannot overflow.\n */\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\n return a + b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting on\n * overflow (when the result is negative).\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\n return a - b;\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `*` operator.\n *\n * Requirements:\n *\n * - Multiplication cannot overflow.\n */\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\n return a * b;\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator.\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\n return a / b;\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting when dividing by zero.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\n return a % b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\n * overflow (when the result is negative).\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {trySub}.\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b <= a, errorMessage);\n return a - b;\n }\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting with custom message on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator. Note: this function uses a\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\n * uses an invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a / b;\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting with custom message when dividing by zero.\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {tryMod}.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a % b;\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Strings.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n bytes16 private constant _HEX_SYMBOLS = \"0123456789abcdef\";\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n // Inspired by OraclizeAPI's implementation - MIT licence\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\n\n if (value == 0) {\n return \"0\";\n }\n uint256 temp = value;\n uint256 digits;\n while (temp != 0) {\n digits++;\n temp /= 10;\n }\n bytes memory buffer = new bytes(digits);\n while (value != 0) {\n digits -= 1;\n buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\n value /= 10;\n }\n return string(buffer);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n if (value == 0) {\n return \"0x00\";\n }\n uint256 temp = value;\n uint256 length = 0;\n while (temp != 0) {\n length++;\n temp >>= 8;\n }\n return toHexString(value, length);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _HEX_SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/structs/EnumerableSet.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 => uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n" + }, + "contracts/automation/AbstractCCIPBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IRouterClient } from \"@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol\";\nimport { Client } from \"@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol\";\n\nabstract contract AbstractCCIPBridgeHelperModule is AbstractSafeModule {\n /**\n * @notice Bridges a token from the source chain to the destination chain using CCIP\n * @param ccipRouter The CCIP router contract\n * @param destinationChainSelector The selector for the destination chain\n * @param token The token to bridge\n * @param amount The amount of token to bridge\n */\n function _bridgeTokenWithCCIP(\n IRouterClient ccipRouter,\n uint64 destinationChainSelector,\n IERC20 token,\n uint256 amount\n ) internal {\n bool success;\n\n // Approve CCIP Router to move the token\n success = safeContract.execTransactionFromModule(\n address(token),\n 0, // Value\n abi.encodeWithSelector(token.approve.selector, ccipRouter, amount),\n 0 // Call\n );\n require(success, \"Failed to approve token\");\n\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(token),\n amount: amount\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory ccipMessage = Client.EVM2AnyMessage({\n receiver: abi.encode(address(safeContract)), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n // Get CCIP fee\n uint256 ccipFee = ccipRouter.getFee(\n destinationChainSelector,\n ccipMessage\n );\n\n // Send CCIP message\n success = safeContract.execTransactionFromModule(\n address(ccipRouter),\n ccipFee, // Value\n abi.encodeWithSelector(\n ccipRouter.ccipSend.selector,\n destinationChainSelector,\n ccipMessage\n ),\n 0 // Call\n );\n require(success, \"Failed to send CCIP message\");\n }\n}\n" + }, + "contracts/automation/AbstractLZBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\n\nimport { IOFT, SendParam } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\nimport { MessagingFee } from \"@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol\";\nimport { OptionsBuilder } from \"@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nabstract contract AbstractLZBridgeHelperModule is AbstractSafeModule {\n using OptionsBuilder for bytes;\n\n /**\n * @dev Bridges token using LayerZero.\n * @param lzEndpointId LayerZero endpoint id.\n * @param token Token to bridge.\n * @param lzAdapter LZ Adapter to use.\n * @param amount Amount of token to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n * @param isNativeToken Whether the token is native token.\n */\n function _bridgeTokenWithLz(\n uint32 lzEndpointId,\n IERC20 token,\n IOFT lzAdapter,\n uint256 amount,\n uint256 slippageBps,\n bool isNativeToken\n ) internal {\n bool success;\n\n if (!isNativeToken) {\n // Approve LZ Adapter to move the token\n success = safeContract.execTransactionFromModule(\n address(token),\n 0, // Value\n abi.encodeWithSelector(\n token.approve.selector,\n address(lzAdapter),\n amount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve token\");\n }\n\n // Calculate minimum amount to receive\n uint256 minAmount = (amount * (10000 - slippageBps)) / 10000;\n\n // Hardcoded gaslimit of 400k\n bytes memory options = OptionsBuilder\n .newOptions()\n .addExecutorLzReceiveOption(400000, 0);\n\n // Build send param\n SendParam memory sendParam = SendParam({\n dstEid: lzEndpointId,\n to: bytes32(uint256(uint160(address(safeContract)))),\n amountLD: amount,\n minAmountLD: minAmount,\n extraOptions: options,\n composeMsg: bytes(\"\"),\n oftCmd: bytes(\"\")\n });\n\n // Compute fees\n MessagingFee memory msgFee = lzAdapter.quoteSend(sendParam, false);\n\n uint256 value = isNativeToken\n ? amount + msgFee.nativeFee\n : msgFee.nativeFee;\n\n // Execute transaction\n success = safeContract.execTransactionFromModule(\n address(lzAdapter),\n value,\n abi.encodeWithSelector(\n lzAdapter.send.selector,\n sendParam,\n msgFee,\n address(safeContract)\n ),\n 0\n );\n require(success, \"Failed to bridge token\");\n }\n}\n" + }, + "contracts/automation/AbstractSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { ISafe } from \"../interfaces/ISafe.sol\";\n\nabstract contract AbstractSafeModule is AccessControlEnumerable {\n ISafe public immutable safeContract;\n\n bytes32 public constant OPERATOR_ROLE = keccak256(\"OPERATOR_ROLE\");\n\n modifier onlySafe() {\n require(\n msg.sender == address(safeContract),\n \"Caller is not the safe contract\"\n );\n _;\n }\n\n modifier onlyOperator() {\n require(\n hasRole(OPERATOR_ROLE, msg.sender),\n \"Caller is not an operator\"\n );\n _;\n }\n\n constructor(address _safeContract) {\n safeContract = ISafe(_safeContract);\n _grantRole(DEFAULT_ADMIN_ROLE, address(safeContract));\n _grantRole(OPERATOR_ROLE, address(safeContract));\n }\n\n /**\n * @dev Helps recovering any tokens accidentally sent to this module.\n * @param token Token to transfer. 0x0 to transfer Native token.\n * @param amount Amount to transfer. 0 to transfer all balance.\n */\n function transferTokens(address token, uint256 amount) external onlySafe {\n if (address(token) == address(0)) {\n // Move ETH\n amount = amount > 0 ? amount : address(this).balance;\n payable(address(safeContract)).transfer(amount);\n return;\n }\n\n // Move all balance if amount set to 0\n amount = amount > 0 ? amount : IERC20(token).balanceOf(address(this));\n\n // Transfer to Safe contract\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(token).transfer(address(safeContract), amount);\n }\n\n receive() external payable {\n // Accept ETH to pay for bridge fees\n }\n}\n" + }, + "contracts/automation/BaseBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// solhint-disable-next-line max-line-length\nimport { AbstractCCIPBridgeHelperModule, AbstractSafeModule, IRouterClient } from \"./AbstractCCIPBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { BridgedWOETHStrategy } from \"../strategies/BridgedWOETHStrategy.sol\";\n\ncontract BaseBridgeHelperModule is\n AccessControlEnumerable,\n AbstractCCIPBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0x98a0CbeF61bD2D21435f433bE4CD42B56B38CC93);\n IWETH9 public constant weth =\n IWETH9(0x4200000000000000000000000000000000000006);\n IERC20 public constant oethb =\n IERC20(0xDBFeFD2e8460a6Ee4955A68582F85708BAEA60A3);\n IERC4626 public constant bridgedWOETH =\n IERC4626(0xD8724322f44E5c58D7A815F542036fb17DbbF839);\n\n BridgedWOETHStrategy public constant bridgedWOETHStrategy =\n BridgedWOETHStrategy(0x80c864704DD06C3693ed5179190786EE38ACf835);\n\n IRouterClient public constant CCIP_ROUTER =\n IRouterClient(0x881e3A65B4d4a04dD529061dd0071cf975F58bCD);\n\n uint64 public constant CCIP_ETHEREUM_CHAIN_SELECTOR = 5009297550715157269;\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Ethereum.\n * @param woethAmount Amount of wOETH to bridge.\n */\n function bridgeWOETHToEthereum(uint256 woethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_ETHEREUM_CHAIN_SELECTOR,\n IERC20(address(bridgedWOETH)),\n woethAmount\n );\n }\n\n /**\n * @dev Bridges WETH to Ethereum.\n * @param wethAmount Amount of WETH to bridge.\n */\n function bridgeWETHToEthereum(uint256 wethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_ETHEREUM_CHAIN_SELECTOR,\n IERC20(address(weth)),\n wethAmount\n );\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem the wOETH for WETH using the Vault.\n * @return Amount of WETH received.\n */\n function depositWOETH(uint256 woethAmount, bool redeemWithVault)\n external\n onlyOperator\n returns (uint256)\n {\n return _depositWOETH(woethAmount, redeemWithVault);\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy and bridges it to Ethereum.\n * @param woethAmount Amount of wOETH to deposit.\n * @return Amount of WETH received.\n */\n function depositWOETHAndBridgeWETH(uint256 woethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n uint256 wethAmount = _depositWOETH(woethAmount, true);\n bridgeWETHToEthereum(wethAmount);\n return wethAmount;\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem the wOETH for WETH using the Vault.\n * @return Amount of WETH received.\n */\n function _depositWOETH(uint256 woethAmount, bool redeemWithVault)\n internal\n returns (uint256)\n {\n // Update oracle price\n bridgedWOETHStrategy.updateWOETHOraclePrice();\n\n // Rebase to account for any yields from price update\n vault.rebase();\n\n uint256 oethbAmount = oethb.balanceOf(address(safeContract));\n\n // Approve bridgedWOETH strategy to move wOETH\n bool success = safeContract.execTransactionFromModule(\n address(bridgedWOETH),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETH.approve.selector,\n address(bridgedWOETHStrategy),\n woethAmount\n ),\n 0 // Call\n );\n\n // Deposit to bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.depositBridgedWOETH.selector,\n woethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to deposit bridged WOETH\");\n\n oethbAmount = oethb.balanceOf(address(safeContract)) - oethbAmount;\n\n // Rebase to account for any yields from price update\n // and backing asset change from deposit\n vault.rebase();\n\n if (!redeemWithVault) {\n return oethbAmount;\n }\n\n // Redeem for WETH using Vault\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethbAmount,\n oethbAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETHb\");\n\n return oethbAmount;\n }\n\n /**\n * @dev Deposits WETH into the Vault and redeems wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to deposit.\n * @return Amount of wOETH received.\n */\n function depositWETHAndRedeemWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _withdrawWOETH(wethAmount);\n }\n\n function depositWETHAndBridgeWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n uint256 woethAmount = _withdrawWOETH(wethAmount);\n bridgeWOETHToEthereum(woethAmount);\n return woethAmount;\n }\n\n /**\n * @dev Withdraws wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to use to withdraw.\n * @return Amount of wOETH received.\n */\n function _withdrawWOETH(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETHb with WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETHb\");\n\n // Approve bridgedWOETH strategy to move OETHb\n success = safeContract.execTransactionFromModule(\n address(oethb),\n 0, // Value\n abi.encodeWithSelector(\n oethb.approve.selector,\n address(bridgedWOETHStrategy),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETHb\");\n\n uint256 woethAmount = bridgedWOETH.balanceOf(address(safeContract));\n\n // Withdraw from bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.withdrawBridgedWOETH.selector,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to withdraw bridged WOETH\");\n\n woethAmount =\n bridgedWOETH.balanceOf(address(safeContract)) -\n woethAmount;\n\n return woethAmount;\n }\n}\n" + }, + "contracts/automation/ClaimBribesSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { ICLGauge } from \"../interfaces/aerodrome/ICLGauge.sol\";\nimport { ICLPool } from \"../interfaces/aerodrome/ICLPool.sol\";\n\nstruct BribePoolInfo {\n address poolAddress;\n address rewardContractAddress;\n address[] rewardTokens;\n}\n\ninterface IAerodromeVoter {\n function claimBribes(\n address[] memory _bribes,\n address[][] memory _tokens,\n uint256 _tokenId\n ) external;\n}\n\ninterface IVeNFT {\n function ownerOf(uint256 tokenId) external view returns (address);\n\n function ownerToNFTokenIdList(address owner, uint256 index)\n external\n view\n returns (uint256);\n}\n\ninterface ICLRewardContract {\n function rewards(uint256 index) external view returns (address);\n\n function rewardsListLength() external view returns (uint256);\n}\n\ncontract ClaimBribesSafeModule is AbstractSafeModule {\n IAerodromeVoter public immutable voter;\n address public immutable veNFT;\n\n uint256[] nftIds;\n mapping(uint256 => uint256) nftIdIndex;\n\n BribePoolInfo[] bribePools;\n mapping(address => uint256) bribePoolIndex;\n\n event NFTIdAdded(uint256 nftId);\n event NFTIdRemoved(uint256 nftId);\n\n event BribePoolAdded(address bribePool);\n event BribePoolRemoved(address bribePool);\n\n constructor(\n address _safeContract,\n address _voter,\n address _veNFT\n ) AbstractSafeModule(_safeContract) {\n voter = IAerodromeVoter(_voter);\n veNFT = _veNFT;\n }\n\n /**\n * @dev Claim bribes for a range of NFTs\n * @param nftIndexStart The start index of the NFTs\n * @param nftIndexEnd The end index of the NFTs\n * @param silent Doesn't revert if the claim fails when true\n */\n function claimBribes(\n uint256 nftIndexStart,\n uint256 nftIndexEnd,\n bool silent\n ) external onlyOperator {\n if (nftIndexEnd < nftIndexStart) {\n (nftIndexStart, nftIndexEnd) = (nftIndexEnd, nftIndexStart);\n }\n uint256 nftCount = nftIds.length;\n nftIndexEnd = nftCount < nftIndexEnd ? nftCount : nftIndexEnd;\n\n (\n address[] memory rewardContractAddresses,\n address[][] memory rewardTokens\n ) = _getRewardsInfoArray();\n\n for (uint256 i = nftIndexStart; i < nftIndexEnd; i++) {\n uint256 nftId = nftIds[i];\n bool success = safeContract.execTransactionFromModule(\n address(voter),\n 0, // Value\n abi.encodeWithSelector(\n IAerodromeVoter.claimBribes.selector,\n rewardContractAddresses,\n rewardTokens,\n nftId\n ),\n 0 // Call\n );\n\n require(success || silent, \"ClaimBribes failed\");\n }\n }\n\n /**\n * @dev Get the reward contract address and reward tokens for all pools\n * @return rewardContractAddresses The reward contract addresses\n * @return rewardTokens The reward tokens\n */\n function _getRewardsInfoArray()\n internal\n view\n returns (\n address[] memory rewardContractAddresses,\n address[][] memory rewardTokens\n )\n {\n BribePoolInfo[] memory _bribePools = bribePools;\n uint256 bribePoolCount = _bribePools.length;\n rewardContractAddresses = new address[](bribePoolCount);\n rewardTokens = new address[][](bribePoolCount);\n\n for (uint256 i = 0; i < bribePoolCount; i++) {\n rewardContractAddresses[i] = _bribePools[i].rewardContractAddress;\n rewardTokens[i] = _bribePools[i].rewardTokens;\n }\n }\n\n /***************************************\n NFT Management\n ****************************************/\n /**\n * @dev Add NFT IDs to the list\n * @param _nftIds The NFT IDs to add\n */\n function addNFTIds(uint256[] memory _nftIds) external onlyOperator {\n for (uint256 i = 0; i < _nftIds.length; i++) {\n uint256 nftId = _nftIds[i];\n if (nftIdExists(nftId)) {\n // If it already exists, skip\n continue;\n }\n\n // Make sure the NFT is owned by the Safe\n require(\n IVeNFT(veNFT).ownerOf(nftId) == address(safeContract),\n \"NFT not owned by safe\"\n );\n\n nftIdIndex[nftId] = nftIds.length;\n nftIds.push(nftId);\n\n emit NFTIdAdded(nftId);\n }\n }\n\n /**\n * @dev Remove NFT IDs from the list\n * @param _nftIds The NFT IDs to remove\n */\n function removeNFTIds(uint256[] memory _nftIds) external onlyOperator {\n for (uint256 i = 0; i < _nftIds.length; i++) {\n uint256 nftId = _nftIds[i];\n if (!nftIdExists(nftId)) {\n // If it doesn't exist, skip\n continue;\n }\n\n uint256 index = nftIdIndex[nftId];\n uint256 lastNftId = nftIds[nftIds.length - 1];\n nftIds[index] = lastNftId;\n nftIdIndex[lastNftId] = index;\n nftIds.pop();\n\n emit NFTIdRemoved(nftId);\n }\n }\n\n /**\n * @dev Check if a NFT exists on the list\n * @param nftId The NFT ID to check\n * @return true if the NFT ID exists, false otherwise\n */\n function nftIdExists(uint256 nftId) public view returns (bool) {\n uint256 index = nftIdIndex[nftId];\n uint256[] memory _nftIds = nftIds;\n return (index < _nftIds.length) && _nftIds[index] == nftId;\n }\n\n /**\n * @dev Get the length of the nftIds list\n * @return The length of the nftIds list\n */\n function getNFTIdsLength() external view returns (uint256) {\n return nftIds.length;\n }\n\n /**\n * @dev Get all NFT IDs\n * @return The NFT IDs\n */\n function getAllNFTIds() external view returns (uint256[] memory) {\n return nftIds;\n }\n\n /**\n * @dev Fetch all NFT IDs from the veNFT contract\n * @notice This can revert if Safe owns too many NFTs since tx will be huge.\n * This function is public, anyone can call it, since it only fetches\n * the NFT IDs owned by the Safe. It shouldn't cause us any issues.\n */\n function fetchNFTIds() external {\n // Purge the array\n delete nftIds;\n\n uint256 i = 0;\n while (true) {\n uint256 nftId = IVeNFT(veNFT).ownerToNFTokenIdList(\n address(safeContract),\n i\n );\n if (nftId == 0) {\n break;\n }\n\n nftIdIndex[nftId] = nftIds.length;\n nftIds.push(nftId);\n i++;\n }\n }\n\n /**\n * @dev Remove all NFT IDs from the list\n */\n function removeAllNFTIds() external onlyOperator {\n uint256 length = nftIds.length;\n for (uint256 i = 0; i < length; i++) {\n uint256 nftId = nftIds[i];\n delete nftIdIndex[nftId];\n emit NFTIdRemoved(nftId);\n }\n\n delete nftIds;\n }\n\n /***************************************\n Bribe Pool Management\n ****************************************/\n // @dev Whitelist a pool to claim bribes from\n // @param _poolAddress The address of the pool to whitelist\n function addBribePool(address _poolAddress, bool _isVotingContract)\n external\n onlySafe\n {\n BribePoolInfo memory bribePool;\n\n if (_isVotingContract) {\n bribePool = BribePoolInfo({\n poolAddress: _poolAddress,\n rewardContractAddress: _poolAddress,\n rewardTokens: _getRewardTokenAddresses(_poolAddress)\n });\n } else {\n // Find the gauge address\n address _gaugeAddress = ICLPool(_poolAddress).gauge();\n // And the reward contract address\n address _rewardContractAddress = ICLGauge(_gaugeAddress)\n .feesVotingReward();\n\n bribePool = BribePoolInfo({\n poolAddress: _poolAddress,\n rewardContractAddress: _rewardContractAddress,\n rewardTokens: _getRewardTokenAddresses(_rewardContractAddress)\n });\n }\n\n if (bribePoolExists(_poolAddress)) {\n // Update if it already exists\n bribePools[bribePoolIndex[_poolAddress]] = bribePool;\n } else {\n // If not, Append to the list\n bribePoolIndex[_poolAddress] = bribePools.length;\n bribePools.push(bribePool);\n }\n\n emit BribePoolAdded(_poolAddress);\n }\n\n /**\n * @dev Update the reward token addresses for all pools\n */\n function updateRewardTokenAddresses() external onlyOperator {\n BribePoolInfo[] storage _bribePools = bribePools;\n for (uint256 i = 0; i < _bribePools.length; i++) {\n BribePoolInfo storage bribePool = _bribePools[i];\n bribePool.rewardTokens = _getRewardTokenAddresses(\n bribePool.rewardContractAddress == bribePool.poolAddress\n ? bribePool.poolAddress\n : bribePool.rewardContractAddress\n );\n }\n }\n\n /**\n * @dev Get the reward token addresses for a given reward contract address\n * @param _rewardContractAddress The address of the reward contract\n * @return _rewardTokens The reward token addresses\n */\n function _getRewardTokenAddresses(address _rewardContractAddress)\n internal\n view\n returns (address[] memory)\n {\n address[] memory _rewardTokens = new address[](\n ICLRewardContract(_rewardContractAddress).rewardsListLength()\n );\n for (uint256 i = 0; i < _rewardTokens.length; i++) {\n _rewardTokens[i] = ICLRewardContract(_rewardContractAddress)\n .rewards(i);\n }\n\n return _rewardTokens;\n }\n\n /**\n * @dev Remove a bribe pool from the list\n * @param _poolAddress The address of the pool to remove\n */\n function removeBribePool(address _poolAddress) external onlySafe {\n if (!bribePoolExists(_poolAddress)) {\n // If it doesn't exist, skip\n return;\n }\n\n uint256 index = bribePoolIndex[_poolAddress];\n BribePoolInfo memory lastBribePool = bribePools[bribePools.length - 1];\n bribePools[index] = lastBribePool;\n bribePoolIndex[lastBribePool.poolAddress] = index;\n bribePools.pop();\n\n emit BribePoolRemoved(_poolAddress);\n }\n\n /**\n * @dev Check if a bribe pool exists\n * @param bribePool The address of the pool to check\n * @return true if the pool exists, false otherwise\n */\n function bribePoolExists(address bribePool) public view returns (bool) {\n BribePoolInfo[] memory _bribePools = bribePools;\n uint256 poolIndex = bribePoolIndex[bribePool];\n return\n poolIndex < _bribePools.length &&\n _bribePools[poolIndex].poolAddress == bribePool;\n }\n\n /**\n * @dev Get the length of the bribe pools list\n * @return The length of the bribe pools list\n */\n function getBribePoolsLength() external view returns (uint256) {\n return bribePools.length;\n }\n}\n" + }, + "contracts/automation/ClaimStrategyRewardsSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\n\nimport { ISafe } from \"../interfaces/ISafe.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract ClaimStrategyRewardsSafeModule is AbstractSafeModule {\n using SafeERC20 for IERC20;\n\n mapping(address => bool) public isStrategyWhitelisted;\n address[] public strategies;\n\n event StrategyAdded(address strategy);\n event StrategyRemoved(address strategy);\n\n event ClaimRewardsFailed(address strategy);\n\n constructor(\n address _safeAddress,\n address operator,\n address[] memory _strategies\n ) AbstractSafeModule(_safeAddress) {\n _grantRole(OPERATOR_ROLE, operator);\n\n // Whitelist all strategies\n for (uint256 i = 0; i < _strategies.length; i++) {\n _addStrategy(_strategies[i]);\n }\n }\n\n /**\n * @dev Claim rewards from all whitelisted strategies\n * @param silent Doesn't revert on error if set to true\n */\n function claimRewards(bool silent) external onlyRole(OPERATOR_ROLE) {\n uint256 strategiesLength = strategies.length;\n for (uint256 i = 0; i < strategiesLength; i++) {\n address strategy = strategies[i];\n\n // Execute `collectRewardTokens` for all strategies\n bool success = safeContract.execTransactionFromModule(\n strategy, // To\n 0, // Value\n abi.encodeWithSelector(IStrategy.collectRewardTokens.selector),\n 0 // Call\n );\n\n if (!success) {\n emit ClaimRewardsFailed(strategy);\n }\n\n require(success || silent, \"Failed to claim rewards\");\n }\n }\n\n /**\n * @dev Add a strategy to the whitelist\n * @param _strategy The address of the strategy to add\n */\n function addStrategy(address _strategy)\n external\n onlyRole(DEFAULT_ADMIN_ROLE)\n {\n _addStrategy(_strategy);\n }\n\n function _addStrategy(address _strategy) internal {\n require(\n !isStrategyWhitelisted[_strategy],\n \"Strategy already whitelisted\"\n );\n isStrategyWhitelisted[_strategy] = true;\n strategies.push(_strategy);\n emit StrategyAdded(_strategy);\n }\n\n /**\n * @dev Remove a strategy from the whitelist\n * @param _strategy The address of the strategy to remove\n */\n function removeStrategy(address _strategy)\n external\n onlyRole(DEFAULT_ADMIN_ROLE)\n {\n require(isStrategyWhitelisted[_strategy], \"Strategy not whitelisted\");\n isStrategyWhitelisted[_strategy] = false;\n\n for (uint256 i = 0; i < strategies.length; i++) {\n if (strategies[i] == _strategy) {\n strategies[i] = strategies[strategies.length - 1];\n strategies.pop();\n break;\n }\n }\n\n emit StrategyRemoved(_strategy);\n }\n}\n" + }, + "contracts/automation/CollectXOGNRewardsModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IXOGN {\n function collectRewards() external;\n}\n\ncontract CollectXOGNRewardsModule is AbstractSafeModule {\n IXOGN public constant xogn =\n IXOGN(0x63898b3b6Ef3d39332082178656E9862bee45C57);\n address public constant rewardsSource =\n 0x7609c88E5880e934dd3A75bCFef44E31b1Badb8b;\n IERC20 public constant ogn =\n IERC20(0x8207c1FfC5B6804F6024322CcF34F29c3541Ae26);\n\n constructor(address _safeContract, address operator)\n AbstractSafeModule(_safeContract)\n {\n _grantRole(OPERATOR_ROLE, operator);\n }\n\n function collectRewards() external onlyOperator {\n uint256 balance = ogn.balanceOf(address(safeContract));\n\n bool success = safeContract.execTransactionFromModule(\n address(xogn),\n 0, // Value\n abi.encodeWithSelector(IXOGN.collectRewards.selector),\n 0 // Call\n );\n\n require(success, \"Failed to collect rewards\");\n\n balance = ogn.balanceOf(address(safeContract)) - balance;\n\n if (balance == 0) {\n return;\n }\n\n success = safeContract.execTransactionFromModule(\n address(ogn),\n 0, // Value\n abi.encodeWithSelector(\n IERC20.transfer.selector,\n rewardsSource,\n balance\n ),\n 0 // Call\n );\n\n require(success, \"Failed to collect rewards\");\n }\n}\n" + }, + "contracts/automation/CurvePoolBoosterBribesModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\n\ninterface ICurvePoolBooster {\n function manageTotalRewardAmount(\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external;\n\n function manageNumberOfPeriods(\n uint8 extraNumberOfPeriods,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external;\n}\n\ncontract CurvePoolBoosterBribesModule is AbstractSafeModule {\n address[] public POOLS;\n\n event PoolBoosterAddressAdded(address pool);\n event PoolBoosterAddressRemoved(address pool);\n\n constructor(\n address _safeContract,\n address _operator,\n address[] memory _pools\n ) AbstractSafeModule(_safeContract) {\n _grantRole(OPERATOR_ROLE, _operator);\n _addPoolBoosterAddress(_pools);\n }\n\n function addPoolBoosterAddress(address[] memory pools)\n external\n onlyOperator\n {\n _addPoolBoosterAddress(pools);\n }\n\n function _addPoolBoosterAddress(address[] memory pools) internal {\n for (uint256 i = 0; i < pools.length; i++) {\n POOLS.push(pools[i]);\n emit PoolBoosterAddressAdded(pools[i]);\n }\n }\n\n function removePoolBoosterAddress(address[] calldata pools)\n external\n onlyOperator\n {\n for (uint256 i = 0; i < pools.length; i++) {\n _removePoolBoosterAddress(pools[i]);\n }\n }\n\n function _removePoolBoosterAddress(address pool) internal {\n uint256 length = POOLS.length;\n for (uint256 i = 0; i < length; i++) {\n if (POOLS[i] == pool) {\n POOLS[i] = POOLS[length - 1];\n POOLS.pop();\n emit PoolBoosterAddressRemoved(pool);\n }\n }\n }\n\n function manageBribes() external onlyOperator {\n uint256 length = POOLS.length;\n for (uint256 i = 0; i < length; i++) {\n address poolBoosterAddress = POOLS[i];\n\n // PoolBooster need to have a balance of at least 0.002 ether to operate\n // 0.001 ether are used for the bridge fee\n require(\n poolBoosterAddress.balance > 0.002 ether,\n \"Insufficient balance for bribes\"\n );\n\n require(\n safeContract.execTransactionFromModule(\n poolBoosterAddress,\n 0, // Value\n abi.encodeWithSelector(\n ICurvePoolBooster.manageNumberOfPeriods.selector,\n 1, // extraNumberOfPeriods\n 0.001 ether, // bridgeFee\n 1000000 // additionalGasLimit\n ),\n 0\n ),\n \"Manage number of periods failed\"\n );\n\n require(\n safeContract.execTransactionFromModule(\n poolBoosterAddress,\n 0, // Value\n abi.encodeWithSelector(\n ICurvePoolBooster.manageTotalRewardAmount.selector,\n 0.001 ether, // bridgeFee\n 1000000 // additionalGasLimit\n ),\n 0\n ),\n \"Manage total reward failed\"\n );\n }\n }\n}\n" + }, + "contracts/automation/EthereumBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractLZBridgeHelperModule, AbstractSafeModule } from \"./AbstractLZBridgeHelperModule.sol\";\nimport { AbstractCCIPBridgeHelperModule, IRouterClient } from \"./AbstractCCIPBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IOFT } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract EthereumBridgeHelperModule is\n AccessControlEnumerable,\n AbstractLZBridgeHelperModule,\n AbstractCCIPBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0x39254033945AA2E4809Cc2977E7087BEE48bd7Ab);\n IWETH9 public constant weth =\n IWETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);\n IERC20 public constant oeth =\n IERC20(0x856c4Efb76C1D1AE02e20CEB03A2A6a08b0b8dC3);\n IERC4626 public constant woeth =\n IERC4626(0xDcEe70654261AF21C44c093C300eD3Bb97b78192);\n\n uint32 public constant LZ_PLUME_ENDPOINT_ID = 30370;\n IOFT public constant LZ_WOETH_OMNICHAIN_ADAPTER =\n IOFT(0x7d1bEa5807e6af125826d56ff477745BB89972b8);\n IOFT public constant LZ_ETH_OMNICHAIN_ADAPTER =\n IOFT(0x77b2043768d28E9C9aB44E1aBfC95944bcE57931);\n\n IRouterClient public constant CCIP_ROUTER =\n IRouterClient(0x80226fc0Ee2b096224EeAc085Bb9a8cba1146f7D);\n\n uint64 public constant CCIP_BASE_CHAIN_SELECTOR = 15971525489660198786;\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Plume.\n * @param woethAmount Amount of wOETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWOETHToPlume(uint256 woethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_PLUME_ENDPOINT_ID,\n woeth,\n LZ_WOETH_OMNICHAIN_ADAPTER,\n woethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Bridges wOETH to Base using CCIP.\n * @param woethAmount Amount of wOETH to bridge.\n */\n function bridgeWOETHToBase(uint256 woethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_BASE_CHAIN_SELECTOR,\n woeth,\n woethAmount\n );\n }\n\n /**\n * @dev Bridges wETH to Plume.\n * @param wethAmount Amount of wETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWETHToPlume(uint256 wethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n // Unwrap into ETH\n safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(weth.withdraw.selector, wethAmount),\n 0 // Call\n );\n\n _bridgeTokenWithLz(\n LZ_PLUME_ENDPOINT_ID,\n IERC20(address(weth)),\n LZ_ETH_OMNICHAIN_ADAPTER,\n wethAmount,\n slippageBps,\n true\n );\n }\n\n /**\n * @dev Bridges wETH to Base using CCIP.\n * @param wethAmount Amount of wETH to bridge.\n */\n function bridgeWETHToBase(uint256 wethAmount) public payable onlyOperator {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_BASE_CHAIN_SELECTOR,\n IERC20(address(weth)),\n wethAmount\n );\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH.\n * @param wethAmount Amount of WETH to mint.\n * @param useNativeToken Whether to use native token to mint.\n * @return Amount of wOETH minted.\n */\n function mintAndWrap(uint256 wethAmount, bool useNativeToken)\n external\n onlyOperator\n returns (uint256)\n {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n return _mintAndWrap(wethAmount);\n }\n\n function wrapETH(uint256 ethAmount) public payable onlyOperator {\n // Deposit ETH into WETH\n safeContract.execTransactionFromModule(\n address(weth),\n ethAmount, // Value\n abi.encodeWithSelector(weth.deposit.selector),\n 0 // Call\n );\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH.\n * @param wethAmount Amount of WETH to mint.\n * @return Amount of wOETH minted.\n */\n function _mintAndWrap(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETH\");\n\n // Approve wOETH to move OETH\n success = safeContract.execTransactionFromModule(\n address(oeth),\n 0, // Value\n abi.encodeWithSelector(\n oeth.approve.selector,\n address(woeth),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETH\");\n\n uint256 woethAmount = woeth.balanceOf(address(safeContract));\n\n // Wrap OETH into wOETH\n success = safeContract.execTransactionFromModule(\n address(woeth),\n 0, // Value\n abi.encodeWithSelector(\n woeth.deposit.selector,\n wethAmount,\n address(safeContract)\n ),\n 0 // Call\n );\n require(success, \"Failed to wrap OETH\");\n\n // Compute amount of wOETH minted\n return woeth.balanceOf(address(safeContract)) - woethAmount;\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH, then bridges it to Plume.\n * @param wethAmount Amount of WETH to mint.\n * @param slippageBps Bridge slippage in 10^4 basis points.\n * @param useNativeToken Whether to use native token to mint.\n */\n function mintWrapAndBridgeToPlume(\n uint256 wethAmount,\n uint256 slippageBps,\n bool useNativeToken\n ) external payable onlyOperator {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n uint256 woethAmount = _mintAndWrap(wethAmount);\n bridgeWOETHToPlume(woethAmount, slippageBps);\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH, then bridges it to Base using CCIP.\n * @param wethAmount Amount of WETH to mint.\n * @param useNativeToken Whether to use native token to mint.\n */\n function mintWrapAndBridgeToBase(uint256 wethAmount, bool useNativeToken)\n external\n payable\n onlyOperator\n {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n uint256 woethAmount = _mintAndWrap(wethAmount);\n bridgeWOETHToBase(woethAmount);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH.\n * @param woethAmount Amount of wOETH to unwrap.\n * @return Amount of WETH received.\n */\n function unwrapAndRedeem(uint256 woethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _unwrapAndRedeem(woethAmount);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH.\n * @param woethAmount Amount of wOETH to unwrap.\n * @return Amount of WETH received.\n */\n function _unwrapAndRedeem(uint256 woethAmount) internal returns (uint256) {\n uint256 oethAmount = oeth.balanceOf(address(safeContract));\n\n // Unwrap wOETH\n bool success = safeContract.execTransactionFromModule(\n address(woeth),\n 0, // Value\n abi.encodeWithSelector(\n woeth.redeem.selector,\n woethAmount,\n address(safeContract),\n address(safeContract)\n ),\n 0 // Call\n );\n require(success, \"Failed to unwrap wOETH\");\n\n oethAmount = oeth.balanceOf(address(safeContract)) - oethAmount;\n\n // Redeem OETH using Vault to get WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethAmount,\n oethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETH\");\n\n return oethAmount;\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH, then bridges it to Plume.\n * @param woethAmount Amount of wOETH to unwrap.\n * @param slippageBps Bridge slippage in 10^4 basis points.\n */\n function unwrapRedeemAndBridgeToPlume(\n uint256 woethAmount,\n uint256 slippageBps\n ) external payable onlyOperator {\n uint256 wethAmount = _unwrapAndRedeem(woethAmount);\n // Unwrap into ETH\n safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(weth.withdraw.selector, wethAmount),\n 0 // Call\n );\n\n bridgeWETHToPlume(wethAmount, slippageBps);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH, then bridges it to Base using CCIP.\n * @param woethAmount Amount of wOETH to unwrap.\n */\n function unwrapRedeemAndBridgeToBase(uint256 woethAmount)\n external\n payable\n onlyOperator\n {\n uint256 wethAmount = _unwrapAndRedeem(woethAmount);\n bridgeWETHToBase(wethAmount);\n }\n}\n" + }, + "contracts/automation/PlumeBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { AbstractLZBridgeHelperModule } from \"./AbstractLZBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IOFT } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { BridgedWOETHStrategy } from \"../strategies/BridgedWOETHStrategy.sol\";\n\ncontract PlumeBridgeHelperModule is\n AccessControlEnumerable,\n AbstractLZBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0xc8c8F8bEA5631A8AF26440AF32a55002138cB76a);\n IWETH9 public constant weth =\n IWETH9(0xca59cA09E5602fAe8B629DeE83FfA819741f14be);\n IERC20 public constant oethp =\n IERC20(0xFCbe50DbE43bF7E5C88C6F6Fb9ef432D4165406E);\n IERC4626 public constant bridgedWOETH =\n IERC4626(0xD8724322f44E5c58D7A815F542036fb17DbbF839);\n\n uint32 public constant LZ_ETHEREUM_ENDPOINT_ID = 30101;\n IOFT public constant LZ_WOETH_OMNICHAIN_ADAPTER =\n IOFT(0x592CB6A596E7919930bF49a27AdAeCA7C055e4DB);\n IOFT public constant LZ_ETH_OMNICHAIN_ADAPTER =\n IOFT(0x4683CE822272CD66CEa73F5F1f9f5cBcaEF4F066);\n\n BridgedWOETHStrategy public constant bridgedWOETHStrategy =\n BridgedWOETHStrategy(0x1E3EdD5e019207D6355Ea77F724b1F1BF639B569);\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Ethereum.\n * @param woethAmount Amount of wOETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWOETHToEthereum(uint256 woethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_ETHEREUM_ENDPOINT_ID,\n IERC20(address(bridgedWOETH)),\n LZ_WOETH_OMNICHAIN_ADAPTER,\n woethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Bridges wETH to Ethereum.\n * @param wethAmount Amount of wETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWETHToEthereum(uint256 wethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_ETHEREUM_ENDPOINT_ID,\n IERC20(address(weth)),\n LZ_ETH_OMNICHAIN_ADAPTER,\n wethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem with Vault.\n * @return Amount of OETHp received.\n */\n function depositWOETH(uint256 woethAmount, bool redeemWithVault)\n external\n onlyOperator\n returns (uint256)\n {\n return _depositWOETH(woethAmount, redeemWithVault);\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy and bridges it to Ethereum.\n * @param woethAmount Amount of wOETH to deposit.\n * @param slippageBps Slippage in 10^4 basis points.\n * @return Amount of WETH received.\n */\n function depositWOETHAndBridgeWETH(uint256 woethAmount, uint256 slippageBps)\n external\n payable\n onlyOperator\n returns (uint256)\n {\n uint256 wethAmount = _depositWOETH(woethAmount, true);\n bridgeWETHToEthereum(wethAmount, slippageBps);\n return wethAmount;\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem with Vault.\n * @return Amount of OETHp received.\n */\n function _depositWOETH(uint256 woethAmount, bool redeemWithVault)\n internal\n returns (uint256)\n {\n // Update oracle price\n bridgedWOETHStrategy.updateWOETHOraclePrice();\n\n // Rebase to account for any yields from price update\n vault.rebase();\n\n uint256 oethpAmount = oethp.balanceOf(address(safeContract));\n\n // Approve bridgedWOETH strategy to move wOETH\n bool success = safeContract.execTransactionFromModule(\n address(bridgedWOETH),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETH.approve.selector,\n address(bridgedWOETHStrategy),\n woethAmount\n ),\n 0 // Call\n );\n\n // Deposit to bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.depositBridgedWOETH.selector,\n woethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to deposit bridged WOETH\");\n\n oethpAmount = oethp.balanceOf(address(safeContract)) - oethpAmount;\n\n // Rebase to account for any yields from price update\n // and backing asset change from deposit\n vault.rebase();\n\n if (!redeemWithVault) {\n return oethpAmount;\n }\n\n // Redeem for WETH using Vault\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethpAmount,\n oethpAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETHp\");\n\n return oethpAmount;\n }\n\n /**\n * @dev Deposits wETH into the vault.\n * @param wethAmount Amount of wETH to deposit.\n * @return Amount of OETHp received.\n */\n function depositWETHAndRedeemWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _withdrawWOETH(wethAmount);\n }\n\n /**\n * @dev Deposits wETH into the vault and bridges it to Ethereum.\n * @param wethAmount Amount of wETH to deposit.\n * @param slippageBps Slippage in 10^4 basis points.\n * @return Amount of WOETH received.\n */\n function depositWETHAndBridgeWOETH(uint256 wethAmount, uint256 slippageBps)\n external\n payable\n onlyOperator\n returns (uint256)\n {\n uint256 woethAmount = _withdrawWOETH(wethAmount);\n bridgeWOETHToEthereum(woethAmount, slippageBps);\n return woethAmount;\n }\n\n /**\n * @dev Withdraws wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to use to withdraw.\n * @return Amount of wOETH received.\n */\n function _withdrawWOETH(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETHp with WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETHp\");\n\n // Approve bridgedWOETH strategy to move OETHp\n success = safeContract.execTransactionFromModule(\n address(oethp),\n 0, // Value\n abi.encodeWithSelector(\n oethp.approve.selector,\n address(bridgedWOETHStrategy),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETHp\");\n\n uint256 woethAmount = bridgedWOETH.balanceOf(address(safeContract));\n\n // Withdraw from bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.withdrawBridgedWOETH.selector,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to withdraw bridged WOETH\");\n\n woethAmount =\n bridgedWOETH.balanceOf(address(safeContract)) -\n woethAmount;\n\n return woethAmount;\n }\n}\n" + }, + "contracts/beacon/BeaconConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to request validator consolidation on the beacon chain.\n * @author Origin Protocol Inc\n */\nlibrary BeaconConsolidation {\n /// @notice The address the validator consolidation requests are sent\n /// See https://eips.ethereum.org/EIPS/eip-7251\n address internal constant CONSOLIDATION_REQUEST_ADDRESS =\n 0x0000BBdDc7CE488642fb579F8B00f3a590007251;\n\n function request(bytes calldata source, bytes calldata target)\n internal\n returns (uint256 fee_)\n {\n require(source.length == 48, \"Invalid source byte length\");\n require(target.length == 48, \"Invalid target byte length\");\n\n fee_ = fee();\n\n // Call the Consolidation Request contract with the public keys of the source and target\n // validators packed together.\n // This does not have a function signature, so we use a call\n (bool success, ) = CONSOLIDATION_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(source, target)\n );\n\n require(success, \"Consolidation request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the consolidation request contract\n (bool success, bytes memory result) = CONSOLIDATION_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/beacon/BeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"./BeaconProofsLib.sol\";\nimport { IBeaconProofs } from \"../interfaces/IBeaconProofs.sol\";\n\n/**\n * @title Verifies merkle proofs of beacon chain data.\n * @author Origin Protocol Inc\n */\ncontract BeaconProofs is IBeaconProofs {\n /// @notice Verifies the validator index is for the given validator public key.\n /// Also verify the validator's withdrawal credential points to the withdrawal address.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param proof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata proof,\n uint40 validatorIndex,\n address withdrawalAddress\n ) external view {\n BeaconProofsLib.verifyValidator(\n beaconBlockRoot,\n pubKeyHash,\n proof,\n validatorIndex,\n withdrawalAddress\n );\n }\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view {\n BeaconProofsLib.verifyValidatorWithdrawableEpoch(\n beaconBlockRoot,\n validatorIndex,\n withdrawableEpoch,\n withdrawableEpochProof\n );\n }\n\n /// @notice Verifies the balances container to the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerRoot The merkle root of the the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata balancesContainerProof\n ) external view {\n BeaconProofsLib.verifyBalancesContainer(\n beaconBlockRoot,\n balancesContainerRoot,\n balancesContainerProof\n );\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param balanceProof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint40 validatorIndex\n ) external view returns (uint256 validatorBalanceGwei) {\n validatorBalanceGwei = BeaconProofsLib.verifyValidatorBalance(\n balancesContainerRoot,\n validatorBalanceLeaf,\n balanceProof,\n validatorIndex\n );\n }\n\n /// @notice Verifies the pending deposits container to the beacon block root.\n /// BeaconBlock.state.pendingDeposits\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param pendingDepositsContainerRoot The merkle root of the the pending deposits container.\n /// @param proof The merkle proof for the pending deposits container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyPendingDepositsContainer(\n bytes32 beaconBlockRoot,\n bytes32 pendingDepositsContainerRoot,\n bytes calldata proof\n ) external view {\n BeaconProofsLib.verifyPendingDepositsContainer(\n beaconBlockRoot,\n pendingDepositsContainerRoot,\n proof\n );\n }\n\n /// @notice Verified a pending deposit to the root of the Pending Deposits container.\n /// @param pendingDepositsContainerRoot The merkle root of the Pending Deposits container.\n /// @param pendingDepositRoot The leaf node containing the validator balance with three other balances.\n /// @param proof The merkle proof for the pending deposit root to the Pending Deposits container root.\n /// This is 28 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param pendingDepositIndex The pending deposit index in the Pending Deposits container\n function verifyPendingDeposit(\n bytes32 pendingDepositsContainerRoot,\n bytes32 pendingDepositRoot,\n bytes calldata proof,\n uint64 pendingDepositIndex\n ) external view {\n BeaconProofsLib.verifyPendingDeposit(\n pendingDepositsContainerRoot,\n pendingDepositRoot,\n proof,\n pendingDepositIndex\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the slot of the first pending deposit to the beacon block root.\n /// BeaconBlock.state.pendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty.\n /// @param firstPendingDepositSlotProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view returns (bool isEmptyDepositQueue) {\n isEmptyDepositQueue = BeaconProofsLib.verifyFirstPendingDeposit(\n beaconBlockRoot,\n slot,\n firstPendingDepositSlotProof\n );\n }\n\n /// @notice Merkleizes a beacon chain pending deposit.\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param withdrawalCredentials The 32 byte withdrawal credentials.\n /// @param amountGwei The amount of the deposit in Gwei.\n /// @param signature The 96 byte BLS signature.\n /// @param slot The beacon chain slot the deposit was made in.\n /// @return root The merkle root of the pending deposit.\n function merkleizePendingDeposit(\n bytes32 pubKeyHash,\n bytes calldata withdrawalCredentials,\n uint64 amountGwei,\n bytes calldata signature,\n uint64 slot\n ) external pure returns (bytes32) {\n return\n BeaconProofsLib.merkleizePendingDeposit(\n pubKeyHash,\n withdrawalCredentials,\n amountGwei,\n signature,\n slot\n );\n }\n\n /// @notice Merkleizes a BLS signature used for validator deposits.\n /// @param signature The 96 byte BLS signature.\n /// @return root The merkle root of the signature.\n function merkleizeSignature(bytes calldata signature)\n external\n pure\n returns (bytes32 root)\n {\n return BeaconProofsLib.merkleizeSignature(signature);\n }\n}\n" + }, + "contracts/beacon/BeaconProofsLib.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Merkle } from \"./Merkle.sol\";\nimport { Endian } from \"./Endian.sol\";\n\n/**\n * @title Library to verify merkle proofs of beacon chain data.\n * @author Origin Protocol Inc\n */\nlibrary BeaconProofsLib {\n // Known generalized indices in the beacon block\n /// @dev BeaconBlock.state.PendingDeposits[0]\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, pending deposits at index 34\n /// Pending deposits container: height 28, first deposit at index 0\n /// ((2 ^ 3 + 3) * 2 ^ 6 + 34) * 2 ^ 28 + 0 = 198105366528\n uint256 internal constant FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX =\n 198105366528;\n /// @dev BeaconBlock.state.PendingDeposits[0].pubkey\n /// Pending Deposit container: height 3, pubkey at index 4\n /// (((2 ^ 3 + 3) * 2 ^ 6 + 34) * 2 ^ 28 + 0) * 2 ^ 3 + 4 = 1584842932228\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX =\n 1584842932228;\n /// @dev BeaconBlock.state.validators\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, validators at index 11\n /// (2 ^ 3 + 3) * 2 ^ 6 + 11 = 715\n uint256 internal constant VALIDATORS_CONTAINER_GENERALIZED_INDEX = 715;\n /// @dev BeaconBlock.state.balances\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, balances at index 12\n /// (2 ^ 3 + 3) * 2 ^ 6 + 12 = 716\n uint256 internal constant BALANCES_CONTAINER_GENERALIZED_INDEX = 716;\n\n /// @dev BeaconBlock.state.pendingDeposits\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, balances at index 34\n /// (2 ^ 3 + 3) * 2 ^ 6 + 34 = 738\n uint256 internal constant PENDING_DEPOSITS_CONTAINER_GENERALIZED_INDEX =\n 738;\n\n /// @dev Number of bytes in the proof to the first pending deposit.\n /// 37 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0]\n /// 37 * 32 bytes = 1184 bytes\n uint256 internal constant FIRST_PENDING_DEPOSIT_PROOF_LENGTH = 1184;\n /// @dev Number of bytes in the proof from the slot of the first pending deposit to the beacon block root.\n /// 40 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0].slot\n /// 40 * 32 bytes = 1280 bytes\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_PROOF_LENGTH = 1280;\n\n /// @dev Merkle height of the Balances container\n /// BeaconBlock.state.balances\n uint256 internal constant BALANCES_HEIGHT = 39;\n /// @dev Merkle height of the Validators container list\n /// BeaconBlock.state.validators\n uint256 internal constant VALIDATORS_LIST_HEIGHT = 41;\n /// @dev Merkle height of the Pending Deposits container list\n /// BeaconBlock.state.pendingDeposits\n uint256 internal constant PENDING_DEPOSITS_LIST_HEIGHT = 28;\n /// @dev Merkle height of the Validator container\n /// BeaconBlock.state.validators[validatorIndex]\n uint256 internal constant VALIDATOR_CONTAINER_HEIGHT = 3;\n\n /// @dev Position of the pubkey field in the Validator container.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;\n /// @dev Position of the withdrawable epoch field in the Validator container.\n /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n uint256 internal constant VALIDATOR_WITHDRAWABLE_EPOCH_INDEX = 7;\n\n /// @notice Verifies the validator index is for the given validator public key.\n /// Also verify the validator's withdrawal credential points to the withdrawal address.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param proof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata proof,\n uint40 validatorIndex,\n address withdrawalAddress\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.validators[validatorIndex]\n uint256 generalizedIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_LIST_HEIGHT,\n validatorIndex\n );\n // BeaconBlock.state.validators[validatorIndex].pubkey\n generalizedIndex = concatGenIndices(\n generalizedIndex,\n VALIDATOR_CONTAINER_HEIGHT,\n VALIDATOR_PUBKEY_INDEX\n );\n\n // Get the withdrawal credentials from the first witness in the pubkey merkle proof.\n bytes32 withdrawalCredentialsFromProof = bytes32(proof[:32]);\n bytes32 withdrawalCredentials = bytes32(\n abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(withdrawalAddress)\n )\n );\n\n require(\n withdrawalCredentialsFromProof == withdrawalCredentials,\n \"Invalid withdrawal cred\"\n );\n\n require(\n // 53 * 32 bytes = 1696 bytes\n proof.length == 1696 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: pubKeyHash,\n index: generalizedIndex\n }),\n \"Invalid validator proof\"\n );\n }\n\n /// @notice Verifies a validator's withdrawable epoch to the beacon block root\n /// for a given validator index.\n /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n /// @param beaconBlockRoot The root of the beacon block\n /// @param validatorIndex The validator index to verify the withdrawable epoch for.\n /// @param withdrawableEpoch The withdrawable epoch to verify in big endian uint64 format\n /// @param proof The merkle proof for the validator's withdrawable epoch to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyValidatorWithdrawableEpoch(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata proof\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.validators[validatorIndex]\n uint256 exitEpochGenIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_LIST_HEIGHT,\n validatorIndex\n );\n // BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n exitEpochGenIndex = concatGenIndices(\n exitEpochGenIndex,\n VALIDATOR_CONTAINER_HEIGHT,\n VALIDATOR_WITHDRAWABLE_EPOCH_INDEX\n );\n\n require(\n // 53 * 32 bytes = 1696 bytes\n proof.length == 1696 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: Endian.toLittleEndianUint64(withdrawableEpoch),\n index: exitEpochGenIndex\n }),\n \"Invalid withdrawable proof\"\n );\n }\n\n /// @notice Verifies the balances container to the beacon block root.\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param balancesContainerRoot The merkle root of the the balances container.\n /// @param proof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata proof\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.balances\n require(\n // 9 * 32 bytes = 288 bytes\n proof.length == 288 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: balancesContainerRoot,\n index: BALANCES_CONTAINER_GENERALIZED_INDEX\n }),\n \"Invalid balance container proof\"\n );\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param proof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for.\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index.\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata proof,\n uint40 validatorIndex\n ) internal view returns (uint256 validatorBalanceGwei) {\n require(balancesContainerRoot != bytes32(0), \"Invalid container root\");\n\n // Four balances are stored in each leaf so the validator index is divided by 4\n uint64 balanceIndex = validatorIndex / 4;\n\n // Get the index within the balances container, not the Beacon Block\n // BeaconBlock.state.balances[balanceIndex]\n uint256 generalizedIndex = concatGenIndices(\n 1,\n BALANCES_HEIGHT,\n balanceIndex\n );\n\n validatorBalanceGwei = balanceAtIndex(\n validatorBalanceLeaf,\n validatorIndex\n );\n\n require(\n // 39 * 32 bytes = 1248 bytes\n proof.length == 1248 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: balancesContainerRoot,\n leaf: validatorBalanceLeaf,\n index: generalizedIndex\n }),\n \"Invalid balance proof\"\n );\n }\n\n /// @notice Verifies the pending deposits container to the beacon block root.\n /// BeaconBlock.state.pendingDeposits\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param pendingDepositsContainerRoot The merkle root of the the pending deposits container.\n /// @param proof The merkle proof for the pending deposits container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyPendingDepositsContainer(\n bytes32 beaconBlockRoot,\n bytes32 pendingDepositsContainerRoot,\n bytes calldata proof\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.pendingDeposits\n require(\n // 9 * 32 bytes = 288 bytes\n proof.length == 288 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: pendingDepositsContainerRoot,\n index: PENDING_DEPOSITS_CONTAINER_GENERALIZED_INDEX\n }),\n \"Invalid deposit container proof\"\n );\n }\n\n /// @notice Verifies a pending deposit in the pending deposits container.\n /// BeaconBlock.state.pendingDeposits[depositIndex]\n /// @param pendingDepositsContainerRoot The merkle root of the pending deposits list container\n /// @param pendingDepositRoot The merkle root of the pending deposit to verify\n /// @param proof The merkle proof for the pending deposit root to the pending deposits list container root.\n /// This is 28 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param depositIndex The index in the pending deposits list container for the deposit to verify.\n function verifyPendingDeposit(\n bytes32 pendingDepositsContainerRoot,\n bytes32 pendingDepositRoot,\n bytes calldata proof,\n uint64 depositIndex\n ) internal view {\n require(pendingDepositsContainerRoot != bytes32(0), \"Invalid root\");\n\n // BeaconBlock.state.pendingDeposits[depositIndex]\n uint256 generalizedIndex = concatGenIndices(\n 1,\n PENDING_DEPOSITS_LIST_HEIGHT,\n depositIndex\n );\n\n require(\n // 28 * 32 bytes = 896 bytes\n proof.length == 896 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: pendingDepositsContainerRoot,\n leaf: pendingDepositRoot,\n index: generalizedIndex\n }),\n \"Invalid deposit proof\"\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the slot of the first pending deposit to the beacon block root.\n /// BeaconBlock.state.pendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty.\n /// @param proof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata proof\n ) internal view returns (bool isEmptyDepositQueue) {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // If the deposit queue is empty\n if (proof.length == FIRST_PENDING_DEPOSIT_PROOF_LENGTH) {\n require(\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: bytes32(0),\n index: FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX\n }),\n \"Invalid empty deposits proof\"\n );\n return true;\n }\n\n // Verify the public key of the first pending deposit\n // BeaconBlock.state.PendingDeposits[0].slot\n require(\n proof.length == FIRST_PENDING_DEPOSIT_SLOT_PROOF_LENGTH &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: Endian.toLittleEndianUint64(slot),\n index: FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX\n }),\n \"Invalid deposit slot proof\"\n );\n }\n\n /// @notice Merkleizes a beacon chain pending deposit.\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param withdrawalCredentials The 32 byte withdrawal credentials.\n /// @param amountGwei The amount of the deposit in Gwei.\n /// @param signature The 96 byte BLS signature.\n /// @param slot The beacon chain slot the deposit was made in.\n /// @return root The merkle root of the pending deposit.\n function merkleizePendingDeposit(\n bytes32 pubKeyHash,\n bytes calldata withdrawalCredentials,\n uint64 amountGwei,\n bytes calldata signature,\n uint64 slot\n ) internal pure returns (bytes32 root) {\n bytes32[] memory leaves = new bytes32[](8);\n leaves[0] = pubKeyHash;\n leaves[1] = bytes32(withdrawalCredentials[:32]);\n leaves[2] = Endian.toLittleEndianUint64(amountGwei);\n leaves[3] = merkleizeSignature(signature);\n leaves[4] = Endian.toLittleEndianUint64(slot);\n leaves[5] = bytes32(0);\n leaves[6] = bytes32(0);\n leaves[7] = bytes32(0);\n\n root = Merkle.merkleizeSha256(leaves);\n }\n\n /// @notice Merkleizes a BLS signature used for validator deposits.\n /// @param signature The 96 byte BLS signature.\n /// @return root The merkle root of the signature.\n function merkleizeSignature(bytes calldata signature)\n internal\n pure\n returns (bytes32)\n {\n require(signature.length == 96, \"Invalid signature\");\n\n bytes32[] memory leaves = new bytes32[](4);\n leaves[0] = bytes32(signature[:32]);\n leaves[1] = bytes32(signature[32:64]);\n leaves[2] = bytes32(signature[64:96]);\n leaves[3] = bytes32(0);\n\n return Merkle.merkleizeSha256(leaves);\n }\n\n ////////////////////////////////////////////////////\n /// Internal Helper Functions\n ////////////////////////////////////////////////////\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint40 validatorIndex)\n internal\n pure\n returns (uint256)\n {\n uint256 bitShiftAmount = (validatorIndex % 4) * 64;\n return\n Endian.fromLittleEndianUint64(\n bytes32((uint256(validatorBalanceLeaf) << bitShiftAmount))\n );\n }\n\n /// @notice Concatenates two beacon chain generalized indices into one.\n /// @param genIndex The first generalized index or 1 if calculating for a single container.\n /// @param height The merkle tree height of the second container. eg 39 for balances, 41 for validators.\n /// @param index The index within the second container. eg the validator index.\n /// @return genIndex The concatenated generalized index.\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) internal pure returns (uint256) {\n return (genIndex << height) | index;\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to retrieve beacon block roots.\n * @author Origin Protocol Inc\n */\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the beacon block root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Roots contract to get the parent block root.\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/Endian.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n/**\n * @title Library to handle conversion between little-endian and big-endian formats.\n * @author Origin Protocol Inc\n */\nlibrary Endian {\n /**\n * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64\n * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type\n * @return n The big endian-formatted uint64\n * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits),\n * but it is immediately truncated to a uint64 (i.e. 64 bits)\n * through a right-shift/shr operation.\n */\n function fromLittleEndianUint64(bytes32 lenum)\n internal\n pure\n returns (uint64 n)\n {\n // the number needs to be stored in little-endian encoding (ie in bytes 0-8)\n n = uint64(uint256(lenum >> 192));\n // forgefmt: disable-next-item\n return\n (n >> 56) |\n ((0x00FF000000000000 & n) >> 40) |\n ((0x0000FF0000000000 & n) >> 24) |\n ((0x000000FF00000000 & n) >> 8) |\n ((0x00000000FF000000 & n) << 8) |\n ((0x0000000000FF0000 & n) << 24) |\n ((0x000000000000FF00 & n) << 40) |\n ((0x00000000000000FF & n) << 56);\n }\n\n function toLittleEndianUint64(uint64 benum)\n internal\n pure\n returns (bytes32 n)\n {\n // Convert to little-endian by reversing byte order\n uint64 reversed = (benum >> 56) |\n ((0x00FF000000000000 & benum) >> 40) |\n ((0x0000FF0000000000 & benum) >> 24) |\n ((0x000000FF00000000 & benum) >> 8) |\n ((0x00000000FF000000 & benum) << 8) |\n ((0x0000000000FF0000 & benum) << 24) |\n ((0x000000000000FF00 & benum) << 40) |\n ((0x00000000000000FF & benum) << 56);\n\n // Store the little-endian uint64 in the least significant 64 bits of bytes32\n n = bytes32(uint256(reversed));\n // Shift to most significant bits\n n = n << 192;\n }\n}\n" + }, + "contracts/beacon/Merkle.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev These functions deal with verification of Merkle Tree proofs.\n *\n * The tree and the proofs can be generated using our\n * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].\n * You will find a quickstart guide in the readme.\n *\n * WARNING: You should avoid using leaf values that are 64 bytes long prior to\n * hashing, or use a hash function other than keccak256 for hashing leaves.\n * This is because the concatenation of a sorted pair of internal nodes in\n * the merkle tree could be reinterpreted as a leaf value.\n * OpenZeppelin's JavaScript library generates merkle trees that are safe\n * against this attack out of the box.\n */\nlibrary Merkle {\n error InvalidProofLength();\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function verifyInclusionSha256(\n bytes memory proof,\n bytes32 root,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bool) {\n return processInclusionProofSha256(proof, leaf, index) == root;\n }\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * _Available since v4.4._\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function processInclusionProofSha256(\n bytes memory proof,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bytes32) {\n require(\n proof.length != 0 && proof.length % 32 == 0,\n InvalidProofLength()\n );\n bytes32[1] memory computedHash = [leaf];\n for (uint256 i = 32; i <= proof.length; i += 32) {\n if (index % 2 == 0) {\n // if ith bit of index is 0, then computedHash is a left sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(computedHash))\n mstore(0x20, mload(add(proof, i)))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n }\n } else {\n // if ith bit of index is 1, then computedHash is a right sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(add(proof, i)))\n mstore(0x20, mload(computedHash))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n }\n }\n index = index / 2;\n }\n return computedHash[0];\n }\n\n /**\n * @notice Returns the merkle root of a tree created from a set of leaves using sha256 as its hash function.\n * @param leaves the leaves of the merkle tree\n * @return The computed Merkle root of the tree.\n * @dev A pre-condition to this function is that leaves.length is a power of two.\n * If not, the function will merkleize the inputs incorrectly.\n */\n function merkleizeSha256(bytes32[] memory leaves)\n internal\n pure\n returns (bytes32)\n {\n //there are half as many nodes in the layer above the leaves\n uint256 numNodesInLayer = leaves.length / 2;\n //create a layer to store the internal nodes\n bytes32[] memory layer = new bytes32[](numNodesInLayer);\n //fill the layer with the pairwise hashes of the leaves\n for (uint256 i = 0; i < numNodesInLayer; i++) {\n layer[i] = sha256(\n abi.encodePacked(leaves[2 * i], leaves[2 * i + 1])\n );\n }\n //the next layer above has half as many nodes\n numNodesInLayer /= 2;\n //while we haven't computed the root\n while (numNodesInLayer != 0) {\n //overwrite the first numNodesInLayer nodes in layer with the pairwise hashes of their children\n for (uint256 i = 0; i < numNodesInLayer; i++) {\n layer[i] = sha256(\n abi.encodePacked(layer[2 * i], layer[2 * i + 1])\n );\n }\n //the next layer above has half as many nodes\n numNodesInLayer /= 2;\n }\n //the first node in the layer is the root\n return layer[0];\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to request full or partial withdrawals from validators on the beacon chain.\n * @author Origin Protocol Inc\n */\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n /// @notice Requests a partial withdrawal for a given validator public key and amount.\n /// @param validatorPubKey The public key of the validator to withdraw from\n /// @param amount The amount of ETH to withdraw\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n /// @notice Gets fee for withdrawal requests contract on Beacon chain\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/bridges/OmnichainL2Adapter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.0;\n\nimport { MintBurnOFTAdapter } from \"@layerzerolabs/oft-evm/contracts/MintBurnOFTAdapter.sol\";\nimport { IMintableBurnable } from \"@layerzerolabs/oft-evm/contracts/interfaces/IMintableBurnable.sol\";\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\n\n/// NOTE: It's necessary to inherit from Ownable instead of Governable\n/// because OFTCore uses Ownable to manage the governor.\n/// Ownable uses slot 0 for storing the address, whereas Goveranble\n/// stores it in a computed slot.\n\n/// @notice Omnichain uses a deployed ERC-20 token and safeERC20\n/// to interact with the OFTCore contract.\n/// On L2, we follow the mint and burn mechanism.\n/// The adapter should have minter and burner roles.\n\n/// @title Omnichain L2 Adapter\ncontract OmnichainL2Adapter is MintBurnOFTAdapter {\n constructor(\n address _token,\n address _lzEndpoint,\n address _governor\n )\n MintBurnOFTAdapter(\n _token,\n IMintableBurnable(_token),\n _lzEndpoint,\n _governor\n )\n Ownable()\n {\n _transferOwnership(_governor);\n }\n\n /// @inheritdoc MintBurnOFTAdapter\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n )\n internal\n virtual\n override\n returns (uint256 amountSentLD, uint256 amountReceivedLD)\n {\n (amountSentLD, amountReceivedLD) = _debitView(\n _amountLD,\n _minAmountLD,\n _dstEid\n );\n // Burns tokens from the caller.\n IMintableERC20(address(minterBurner)).burn(_from, amountSentLD);\n }\n\n /// @inheritdoc MintBurnOFTAdapter\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /* _srcEid */\n ) internal virtual override returns (uint256 amountReceivedLD) {\n if (_to == address(0x0)) _to = address(0xdead); // _mint(...) does not support address(0x0)\n // Mints the tokens and transfers to the recipient.\n IMintableERC20(address(minterBurner)).mint(_to, _amountLD);\n // In the case of NON-default OFTAdapter, the amountLD MIGHT not be equal to amountReceivedLD.\n return _amountLD;\n }\n}\n\ninterface IMintableERC20 {\n function mint(address to, uint256 value) external;\n\n function burn(address to, uint256 value) external;\n}\n" + }, + "contracts/bridges/OmnichainMainnetAdapter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.0;\n\nimport { OFTAdapter } from \"@layerzerolabs/oft-evm/contracts/OFTAdapter.sol\";\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\n\n/// NOTE: It's necessary to inherit from Ownable instead of Governable\n/// because OFTCore uses Ownable to manage the governor.\n/// Ownable uses slot 0 for storing the address, whereas Goveranble\n/// stores it in a computed slot.\n\n/// @notice Omnichain uses a deployed ERC-20 token and safeERC20\n/// to interact with the OFTCore contract.\n/// On Ethereum, we follow the lock and unlock mechanism.\n\n/// @title Omnichain Mainnet Adapter\ncontract OmnichainMainnetAdapter is OFTAdapter {\n constructor(\n address _token,\n address _lzEndpoint,\n address _governor\n ) OFTAdapter(_token, _lzEndpoint, _governor) Ownable() {\n _transferOwnership(_governor);\n }\n}\n" + }, + "contracts/buyback/AbstractBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { ICVXLocker } from \"../interfaces/ICVXLocker.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\n\nabstract contract AbstractBuyback is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n event SwapRouterUpdated(address indexed _address);\n\n event RewardsSourceUpdated(address indexed _address);\n event TreasuryManagerUpdated(address indexed _address);\n event CVXShareBpsUpdated(uint256 bps);\n\n // Emitted whenever OUSD/OETH is swapped for OGN/CVX or any other token\n event OTokenBuyback(\n address indexed oToken,\n address indexed swappedFor,\n uint256 swapAmountIn,\n uint256 amountOut\n );\n\n // Address of 1-inch Swap Router\n address public swapRouter;\n\n // slither-disable-next-line constable-states\n address private __deprecated_ousd;\n // slither-disable-next-line constable-states\n address private __deprecated_ogv;\n // slither-disable-next-line constable-states\n address private __deprecated_usdt;\n // slither-disable-next-line constable-states\n address private __deprecated_weth9;\n\n // Address that receives OGN after swaps\n address public rewardsSource;\n\n // Address that receives all other tokens after swaps\n address public treasuryManager;\n\n // slither-disable-next-line constable-states\n uint256 private __deprecated_treasuryBps;\n\n address public immutable oToken;\n address public immutable ogn;\n address public immutable cvx;\n address public immutable cvxLocker;\n\n // Amount of `oToken` balance to use for OGN buyback\n uint256 public balanceForOGN;\n\n // Amount of `oToken` balance to use for CVX buyback\n uint256 public balanceForCVX;\n\n // Percentage of `oToken` balance to be used for CVX\n uint256 public cvxShareBps; // 10000 = 100%\n\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) {\n // Make sure nobody owns the implementation contract\n _setGovernor(address(0));\n\n oToken = _oToken;\n ogn = _ogn;\n cvx = _cvx;\n cvxLocker = _cvxLocker;\n }\n\n /**\n * @param _swapRouter Address of Uniswap V3 Router\n * @param _strategistAddr Address of Strategist multi-sig wallet\n * @param _treasuryManagerAddr Address that receives the treasury's share of OUSD\n * @param _rewardsSource Address of RewardsSource contract\n * @param _cvxShareBps Percentage of balance to use for CVX\n */\n function initialize(\n address _swapRouter,\n address _strategistAddr,\n address _treasuryManagerAddr,\n address _rewardsSource,\n uint256 _cvxShareBps\n ) external onlyGovernor initializer {\n _setStrategistAddr(_strategistAddr);\n\n _setSwapRouter(_swapRouter);\n _setRewardsSource(_rewardsSource);\n\n _setTreasuryManager(_treasuryManagerAddr);\n\n _setCVXShareBps(_cvxShareBps);\n }\n\n /**\n * @dev Set address of Uniswap Universal Router for performing liquidation\n * of platform fee tokens. Setting to 0x0 will pause swaps.\n *\n * @param _router Address of the Uniswap Universal router\n */\n function setSwapRouter(address _router) external onlyGovernor {\n _setSwapRouter(_router);\n }\n\n function _setSwapRouter(address _router) internal {\n address oldRouter = swapRouter;\n swapRouter = _router;\n\n if (oldRouter != address(0)) {\n // Remove allowance of old router, if any\n\n if (IERC20(ogn).allowance(address(this), oldRouter) != 0) {\n // slither-disable-next-line unused-return\n IERC20(ogn).safeApprove(oldRouter, 0);\n }\n\n if (IERC20(cvx).allowance(address(this), oldRouter) != 0) {\n // slither-disable-next-line unused-return\n IERC20(cvx).safeApprove(oldRouter, 0);\n }\n }\n\n emit SwapRouterUpdated(_router);\n }\n\n /**\n * @dev Sets the address that receives the OGN buyback rewards\n * @param _address Address\n */\n function setRewardsSource(address _address) external onlyGovernor {\n _setRewardsSource(_address);\n }\n\n function _setRewardsSource(address _address) internal {\n require(_address != address(0), \"Address not set\");\n rewardsSource = _address;\n emit RewardsSourceUpdated(_address);\n }\n\n /**\n * @dev Sets the address that can receive and manage the funds for Treasury\n * @param _address Address\n */\n function setTreasuryManager(address _address) external onlyGovernor {\n _setTreasuryManager(_address);\n }\n\n function _setTreasuryManager(address _address) internal {\n require(_address != address(0), \"Address not set\");\n treasuryManager = _address;\n emit TreasuryManagerUpdated(_address);\n }\n\n /**\n * @dev Sets the percentage of oToken to use for Flywheel tokens\n * @param _bps BPS, 10000 to 100%\n */\n function setCVXShareBps(uint256 _bps) external onlyGovernor {\n _setCVXShareBps(_bps);\n }\n\n function _setCVXShareBps(uint256 _bps) internal {\n require(_bps <= 10000, \"Invalid bps value\");\n cvxShareBps = _bps;\n emit CVXShareBpsUpdated(_bps);\n }\n\n /**\n * @dev Computes the split of oToken balance that can be\n * used for OGN and CVX buybacks.\n */\n function _updateBuybackSplits()\n internal\n returns (uint256 _balanceForOGN, uint256 _balanceForCVX)\n {\n _balanceForOGN = balanceForOGN;\n _balanceForCVX = balanceForCVX;\n\n uint256 totalBalance = IERC20(oToken).balanceOf(address(this));\n uint256 unsplitBalance = totalBalance - _balanceForOGN - _balanceForCVX;\n\n // Check if all balance is accounted for\n if (unsplitBalance != 0) {\n // If not, split unaccounted balance based on `cvxShareBps`\n uint256 addToCVX = (unsplitBalance * cvxShareBps) / 10000;\n _balanceForCVX = _balanceForCVX + addToCVX;\n _balanceForOGN = _balanceForOGN + unsplitBalance - addToCVX;\n\n // Update storage\n balanceForOGN = _balanceForOGN;\n balanceForCVX = _balanceForCVX;\n }\n }\n\n function updateBuybackSplits() external onlyGovernor {\n // slither-disable-next-line unused-return\n _updateBuybackSplits();\n }\n\n function _swapToken(\n address tokenOut,\n uint256 oTokenAmount,\n uint256 minAmountOut,\n bytes calldata swapData\n ) internal returns (uint256 amountOut) {\n require(oTokenAmount > 0, \"Invalid Swap Amount\");\n require(swapRouter != address(0), \"Swap Router not set\");\n require(minAmountOut > 0, \"Invalid minAmount\");\n\n // Transfer OToken to Swapper for swapping\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(oToken).transfer(swapRouter, oTokenAmount);\n\n // Swap\n amountOut = ISwapper(swapRouter).swap(\n oToken,\n tokenOut,\n oTokenAmount,\n minAmountOut,\n swapData\n );\n\n require(amountOut >= minAmountOut, \"Higher Slippage\");\n\n emit OTokenBuyback(oToken, tokenOut, oTokenAmount, amountOut);\n }\n\n /**\n * @dev Swaps `oTokenAmount` to OGN\n * @param oTokenAmount Amount of OUSD/OETH to swap\n * @param minOGN Minimum OGN to receive for oTokenAmount\n * @param swapData 1inch Swap Data\n */\n function swapForOGN(\n uint256 oTokenAmount,\n uint256 minOGN,\n bytes calldata swapData\n ) external onlyGovernorOrStrategist nonReentrant {\n (uint256 _amountForOGN, ) = _updateBuybackSplits();\n require(_amountForOGN >= oTokenAmount, \"Balance underflow\");\n require(rewardsSource != address(0), \"RewardsSource contract not set\");\n\n unchecked {\n // Subtract the amount to swap from net balance\n balanceForOGN = _amountForOGN - oTokenAmount;\n }\n\n uint256 ognReceived = _swapToken(ogn, oTokenAmount, minOGN, swapData);\n\n // Transfer OGN received to RewardsSource contract\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(ogn).transfer(rewardsSource, ognReceived);\n }\n\n /**\n * @dev Swaps `oTokenAmount` to CVX\n * @param oTokenAmount Amount of OUSD/OETH to swap\n * @param minCVX Minimum CVX to receive for oTokenAmount\n * @param swapData 1inch Swap Data\n */\n function swapForCVX(\n uint256 oTokenAmount,\n uint256 minCVX,\n bytes calldata swapData\n ) external onlyGovernorOrStrategist nonReentrant {\n (, uint256 _amountForCVX) = _updateBuybackSplits();\n require(_amountForCVX >= oTokenAmount, \"Balance underflow\");\n\n unchecked {\n // Subtract the amount to swap from net balance\n balanceForCVX = _amountForCVX - oTokenAmount;\n }\n\n uint256 cvxReceived = _swapToken(cvx, oTokenAmount, minCVX, swapData);\n\n // Lock all CVX\n _lockAllCVX(cvxReceived);\n }\n\n /**\n * @dev Locks all CVX held by the contract on behalf of the Treasury Manager\n */\n function lockAllCVX() external onlyGovernorOrStrategist {\n _lockAllCVX(IERC20(cvx).balanceOf(address(this)));\n }\n\n function _lockAllCVX(uint256 cvxAmount) internal {\n require(\n treasuryManager != address(0),\n \"Treasury manager address not set\"\n );\n\n // Lock all available CVX on behalf of `treasuryManager`\n ICVXLocker(cvxLocker).lock(treasuryManager, cvxAmount, 0);\n }\n\n /**\n * @dev Approve CVX Locker to move CVX held by this contract\n */\n function safeApproveAllTokens() external onlyGovernorOrStrategist {\n IERC20(cvx).safeApprove(cvxLocker, type(uint256).max);\n }\n\n /**\n * @notice Owner function to withdraw a specific amount of a token\n * @param token token to be transferered\n * @param amount amount of the token to be transferred\n */\n function transferToken(address token, uint256 amount)\n external\n onlyGovernor\n nonReentrant\n {\n IERC20(token).safeTransfer(_governor(), amount);\n }\n}\n" + }, + "contracts/buyback/ARMBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract ARMBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/buyback/OETHBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract OETHBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/buyback/OUSDBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract OUSDBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/echidna/Debugger.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary Debugger {\n event Debug(string debugString);\n event Debug(string description, string data);\n event Debug(string prefix, string description, string data);\n event Debug(string description, bytes32 data);\n event Debug(string prefix, string description, bytes32 data);\n event Debug(string description, uint256 data);\n event Debug(string prefix, string description, uint256 data);\n event Debug(string description, int256 data);\n event Debug(string prefix, string description, int256 data);\n event Debug(string description, address data);\n event Debug(string prefix, string description, address data);\n event Debug(string description, bool data);\n event Debug(string prefix, string description, bool data);\n\n function log(string memory debugString) internal {\n emit Debug(debugString);\n }\n\n function log(string memory description, string memory data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n string memory data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, bytes32 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n bytes32 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, uint256 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n uint256 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, int256 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n int256 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, address data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n address data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, bool data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n bool data\n ) internal {\n emit Debug(prefix, description, data);\n }\n}\n" + }, + "contracts/echidna/Echidna.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaTestApproval.sol\";\n\n/**\n * @title Echidna test contract for OUSD\n * @notice Target contract to be tested, containing all mixins\n * @author Rappie\n */\ncontract Echidna is EchidnaTestApproval {\n\n}\n" + }, + "contracts/echidna/EchidnaConfig.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Top-level mixin for configuring the desired fuzzing setup\n * @author Rappie\n */\ncontract EchidnaConfig {\n address internal constant ADDRESS_VAULT = address(0x10000);\n address internal constant ADDRESS_OUTSIDER_USER = address(0x20000);\n\n address internal constant ADDRESS_USER0 = address(0x30000);\n address internal constant ADDRESS_USER1 = address(0x40000);\n\n // Will be set in EchidnaSetup constructor\n address internal ADDRESS_OUTSIDER_CONTRACT;\n address internal ADDRESS_CONTRACT0;\n address internal ADDRESS_CONTRACT1;\n\n // Toggle known issues\n //\n // This can be used to skip tests that are known to fail. This is useful\n // when debugging a specific issue, but should be disabled when running\n // the full test suite.\n //\n // True => skip tests that are known to fail\n // False => run all tests\n //\n bool internal constant TOGGLE_KNOWN_ISSUES = false;\n\n // Toggle known issues within limits\n //\n // Same as TOGGLE_KNOWN_ISSUES, but also skip tests that are known to fail\n // within limits set by the variables below.\n //\n bool internal constant TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS = true;\n\n // Starting balance\n //\n // Gives OUSD a non-zero starting supply, which can be useful to ignore\n // certain edge cases.\n //\n // The starting balance is given to outsider accounts that are not used as\n // accounts while fuzzing.\n //\n bool internal constant TOGGLE_STARTING_BALANCE = true;\n uint256 internal constant STARTING_BALANCE = 1_000_000e18;\n\n // Change supply\n //\n // Set a limit to the amount of change per rebase, which can be useful to\n // ignore certain edge cases.\n //\n // True => limit the amount of change to a percentage of total supply\n // False => no limit\n //\n bool internal constant TOGGLE_CHANGESUPPLY_LIMIT = true;\n uint256 internal constant CHANGESUPPLY_DIVISOR = 10; // 10% of total supply\n\n // Mint limit\n //\n // Set a limit the amount minted per mint, which can be useful to\n // ignore certain edge cases.\n //\n // True => limit the amount of minted tokens\n // False => no limit\n //\n bool internal constant TOGGLE_MINT_LIMIT = true;\n uint256 internal constant MINT_MODULO = 1_000_000_000_000e18;\n\n // Known rounding errors\n uint256 internal constant TRANSFER_ROUNDING_ERROR = 1e18 - 1;\n uint256 internal constant OPT_IN_ROUNDING_ERROR = 1e18 - 1;\n uint256 internal constant MINT_ROUNDING_ERROR = 1e18 - 1;\n\n /**\n * @notice Modifier to skip tests that are known to fail\n * @dev see TOGGLE_KNOWN_ISSUES for more information\n */\n modifier hasKnownIssue() {\n if (TOGGLE_KNOWN_ISSUES) return;\n _;\n }\n\n /**\n * @notice Modifier to skip tests that are known to fail within limits\n * @dev see TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS for more information\n */\n modifier hasKnownIssueWithinLimits() {\n if (TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS) return;\n _;\n }\n\n /**\n * @notice Translate an account ID to an address\n * @param accountId The ID of the account\n * @return account The address of the account\n */\n function getAccount(uint8 accountId)\n internal\n view\n returns (address account)\n {\n accountId = accountId / 64;\n if (accountId == 0) return account = ADDRESS_USER0;\n if (accountId == 1) return account = ADDRESS_USER1;\n if (accountId == 2) return account = ADDRESS_CONTRACT0;\n if (accountId == 3) return account = ADDRESS_CONTRACT1;\n require(false, \"Unknown account ID\");\n }\n}\n" + }, + "contracts/echidna/EchidnaDebug.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport \"./EchidnaHelper.sol\";\nimport \"./Debugger.sol\";\n\nimport \"../token/OUSD.sol\";\n\n/**\n * @title Room for random debugging functions\n * @author Rappie\n */\ncontract EchidnaDebug is EchidnaHelper {\n function debugOUSD() public pure {\n // assert(ousd.balanceOf(ADDRESS_USER0) == 1000);\n // assert(ousd.rebaseState(ADDRESS_USER0) != OUSD.RebaseOptions.OptIn);\n // assert(Address.isContract(ADDRESS_CONTRACT0));\n // Debugger.log(\"nonRebasingSupply\", ousd.nonRebasingSupply());\n // assert(false);\n }\n}\n" + }, + "contracts/echidna/EchidnaHelper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaSetup.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin containing helper functions\n * @author Rappie\n */\ncontract EchidnaHelper is EchidnaSetup {\n /**\n * @notice Mint tokens to an account\n * @param toAcc Account to mint to\n * @param amount Amount to mint\n * @return Amount minted (in case of capped mint with modulo)\n */\n function mint(uint8 toAcc, uint256 amount) public returns (uint256) {\n address to = getAccount(toAcc);\n\n if (TOGGLE_MINT_LIMIT) {\n amount = amount % MINT_MODULO;\n }\n\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(to, amount);\n\n return amount;\n }\n\n /**\n * @notice Burn tokens from an account\n * @param fromAcc Account to burn from\n * @param amount Amount to burn\n */\n function burn(uint8 fromAcc, uint256 amount) public {\n address from = getAccount(fromAcc);\n hevm.prank(ADDRESS_VAULT);\n ousd.burn(from, amount);\n }\n\n /**\n * @notice Change the total supply of OUSD (rebase)\n * @param amount New total supply\n */\n function changeSupply(uint256 amount) public {\n if (TOGGLE_CHANGESUPPLY_LIMIT) {\n amount =\n ousd.totalSupply() +\n (amount % (ousd.totalSupply() / CHANGESUPPLY_DIVISOR));\n }\n\n hevm.prank(ADDRESS_VAULT);\n ousd.changeSupply(amount);\n }\n\n /**\n * @notice Transfer tokens between accounts\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function transfer(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n ousd.transfer(to, amount);\n }\n\n /**\n * @notice Transfer approved tokens between accounts\n * @param authorizedAcc Account that is authorized to transfer\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function transferFrom(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n ousd.transferFrom(from, to, amount);\n }\n\n /**\n * @notice Opt in to rebasing\n * @param targetAcc Account to opt in\n */\n function optIn(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n hevm.prank(target);\n ousd.rebaseOptIn();\n }\n\n /**\n * @notice Opt out of rebasing\n * @param targetAcc Account to opt out\n */\n function optOut(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n hevm.prank(target);\n ousd.rebaseOptOut();\n }\n\n /**\n * @notice Approve an account to spend OUSD\n * @param ownerAcc Account that owns the OUSD\n * @param spenderAcc Account that is approved to spend the OUSD\n * @param amount Amount to approve\n */\n function approve(\n uint8 ownerAcc,\n uint8 spenderAcc,\n uint256 amount\n ) public {\n address owner = getAccount(ownerAcc);\n address spender = getAccount(spenderAcc);\n hevm.prank(owner);\n // slither-disable-next-line unused-return\n ousd.approve(spender, amount);\n }\n\n /**\n * @notice Get the sum of all OUSD balances\n * @return total Total balance\n */\n function getTotalBalance() public view returns (uint256 total) {\n total += ousd.balanceOf(ADDRESS_VAULT);\n total += ousd.balanceOf(ADDRESS_OUTSIDER_USER);\n total += ousd.balanceOf(ADDRESS_OUTSIDER_CONTRACT);\n total += ousd.balanceOf(ADDRESS_USER0);\n total += ousd.balanceOf(ADDRESS_USER1);\n total += ousd.balanceOf(ADDRESS_CONTRACT0);\n total += ousd.balanceOf(ADDRESS_CONTRACT1);\n }\n\n /**\n * @notice Get the sum of all non-rebasing OUSD balances\n * @return total Total balance\n */\n function getTotalNonRebasingBalance() public returns (uint256 total) {\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_VAULT)\n ? ousd.balanceOf(ADDRESS_VAULT)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_OUTSIDER_USER)\n ? ousd.balanceOf(ADDRESS_OUTSIDER_USER)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_OUTSIDER_CONTRACT)\n ? ousd.balanceOf(ADDRESS_OUTSIDER_CONTRACT)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_USER0)\n ? ousd.balanceOf(ADDRESS_USER0)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_USER1)\n ? ousd.balanceOf(ADDRESS_USER1)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_CONTRACT0)\n ? ousd.balanceOf(ADDRESS_CONTRACT0)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_CONTRACT1)\n ? ousd.balanceOf(ADDRESS_CONTRACT1)\n : 0;\n }\n}\n" + }, + "contracts/echidna/EchidnaSetup.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./IHevm.sol\";\nimport \"./EchidnaConfig.sol\";\nimport \"./OUSDEchidna.sol\";\n\ncontract Dummy {}\n\n/**\n * @title Mixin for setup and deployment\n * @author Rappie\n */\ncontract EchidnaSetup is EchidnaConfig {\n IHevm hevm = IHevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);\n OUSDEchidna ousd = new OUSDEchidna();\n\n /**\n * @notice Deploy the OUSD contract and set up initial state\n */\n constructor() {\n ousd.initialize(ADDRESS_VAULT, 1e18);\n\n // Deploy dummny contracts as users\n Dummy outsider = new Dummy();\n ADDRESS_OUTSIDER_CONTRACT = address(outsider);\n Dummy dummy0 = new Dummy();\n ADDRESS_CONTRACT0 = address(dummy0);\n Dummy dummy1 = new Dummy();\n ADDRESS_CONTRACT1 = address(dummy1);\n\n // Start out with a reasonable amount of OUSD\n if (TOGGLE_STARTING_BALANCE) {\n // Rebasing tokens\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(ADDRESS_OUTSIDER_USER, STARTING_BALANCE / 2);\n\n // Non-rebasing tokens\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(ADDRESS_OUTSIDER_CONTRACT, STARTING_BALANCE / 2);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestAccounting.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestSupply.sol\";\n\n/**\n * @title Mixin for testing accounting functions\n * @author Rappie\n */\ncontract EchidnaTestAccounting is EchidnaTestSupply {\n /**\n * @notice After opting in, balance should not increase. (Ok to lose rounding funds doing this)\n * @param targetAcc Account to opt in\n */\n function testOptInBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optIn(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter <= balanceBefore);\n }\n\n /**\n * @notice After opting out, balance should remain the same\n * @param targetAcc Account to opt out\n */\n function testOptOutBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optOut(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Account balance should remain the same after opting in minus rounding error\n * @param targetAcc Account to opt in\n */\n function testOptInBalanceRounding(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optIn(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n int256 delta = int256(balanceAfter) - int256(balanceBefore);\n Debugger.log(\"delta\", delta);\n\n // slither-disable-next-line tautology\n assert(-1 * delta >= 0);\n assert(-1 * delta <= int256(OPT_IN_ROUNDING_ERROR));\n }\n\n /**\n * @notice After opting in, total supply should remain the same\n * @param targetAcc Account to opt in\n */\n function testOptInTotalSupply(uint8 targetAcc) public {\n uint256 totalSupplyBefore = ousd.totalSupply();\n optIn(targetAcc);\n uint256 totalSupplyAfter = ousd.totalSupply();\n\n assert(totalSupplyAfter == totalSupplyBefore);\n }\n\n /**\n * @notice After opting out, total supply should remain the same\n * @param targetAcc Account to opt out\n */\n function testOptOutTotalSupply(uint8 targetAcc) public {\n uint256 totalSupplyBefore = ousd.totalSupply();\n optOut(targetAcc);\n uint256 totalSupplyAfter = ousd.totalSupply();\n\n assert(totalSupplyAfter == totalSupplyBefore);\n }\n\n /**\n * @notice Account balance should remain the same when a smart contract auto converts\n * @param targetAcc Account to auto convert\n */\n function testAutoConvertBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n // slither-disable-next-line unused-return\n ousd._isNonRebasingAccountEchidna(target);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice The `balanceOf` function should never revert\n * @param targetAcc Account to check balance of\n */\n function testBalanceOfShouldNotRevert(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n // slither-disable-next-line unused-return\n try ousd.balanceOf(target) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestApproval.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaTestMintBurn.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin for testing approval related functions\n * @author Rappie\n */\ncontract EchidnaTestApproval is EchidnaTestMintBurn {\n /**\n * @notice Performing `transferFrom` with an amount inside the allowance should not revert\n * @param authorizedAcc The account that is authorized to transfer\n * @param fromAcc The account that is transferring\n * @param toAcc The account that is receiving\n * @param amount The amount to transfer\n */\n function testTransferFromShouldNotRevert(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount <= ousd.balanceOf(from));\n require(amount <= ousd.allowance(from, authorized));\n\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n try ousd.transferFrom(from, to, amount) {\n // pass\n } catch {\n assert(false);\n }\n }\n\n /**\n * @notice Performing `transferFrom` with an amount outside the allowance should revert\n * @param authorizedAcc The account that is authorized to transfer\n * @param fromAcc The account that is transferring\n * @param toAcc The account that is receiving\n * @param amount The amount to transfer\n */\n function testTransferFromShouldRevert(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount > 0);\n require(\n !(amount <= ousd.balanceOf(from) &&\n amount <= ousd.allowance(from, authorized))\n );\n\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n try ousd.transferFrom(from, to, amount) {\n assert(false);\n } catch {\n // pass\n }\n }\n\n /**\n * @notice Approving an amount should update the allowance and overwrite any previous allowance\n * @param ownerAcc The account that is approving\n * @param spenderAcc The account that is being approved\n * @param amount The amount to approve\n */\n function testApprove(\n uint8 ownerAcc,\n uint8 spenderAcc,\n uint256 amount\n ) public {\n address owner = getAccount(ownerAcc);\n address spender = getAccount(spenderAcc);\n\n approve(ownerAcc, spenderAcc, amount);\n uint256 allowanceAfter1 = ousd.allowance(owner, spender);\n\n assert(allowanceAfter1 == amount);\n\n approve(ownerAcc, spenderAcc, amount / 2);\n uint256 allowanceAfter2 = ousd.allowance(owner, spender);\n\n assert(allowanceAfter2 == amount / 2);\n }\n}\n" + }, + "contracts/echidna/EchidnaTestMintBurn.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestAccounting.sol\";\n\n/**\n * @title Mixin for testing Mint and Burn functions\n * @author Rappie\n */\ncontract EchidnaTestMintBurn is EchidnaTestAccounting {\n /**\n * @notice Minting 0 tokens should not affect account balance\n * @param targetAcc Account to mint to\n */\n function testMintZeroBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n mint(targetAcc, 0);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Burning 0 tokens should not affect account balance\n * @param targetAcc Account to burn from\n */\n function testBurnZeroBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n burn(targetAcc, 0);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Minting tokens must increase the account balance by at least amount\n * @param targetAcc Account to mint to\n * @param amount Amount to mint\n * @custom:error testMintBalance(uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * testMintBalance(0,1)\n * Event sequence:\n * Debug(«balanceBefore», 0)\n * Debug(«balanceAfter», 0)\n */\n function testMintBalance(uint8 targetAcc, uint256 amount)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n uint256 amountMinted = mint(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n Debugger.log(\"amountMinted\", amountMinted);\n Debugger.log(\"balanceBefore\", balanceBefore);\n Debugger.log(\"balanceAfter\", balanceAfter);\n\n assert(balanceAfter >= balanceBefore + amountMinted);\n }\n\n /**\n * @notice Burning tokens must decrease the account balance by at least amount\n * @param targetAcc Account to burn from\n * @param amount Amount to burn\n * @custom:error testBurnBalance(uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * mint(0,3)\n * testBurnBalance(0,1)\n * Event sequence:\n * Debug(«balanceBefore», 2)\n * Debug(«balanceAfter», 2)\n */\n function testBurnBalance(uint8 targetAcc, uint256 amount)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n burn(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n Debugger.log(\"balanceBefore\", balanceBefore);\n Debugger.log(\"balanceAfter\", balanceAfter);\n\n assert(balanceAfter <= balanceBefore - amount);\n }\n\n /**\n * @notice Minting tokens should not increase the account balance by less than rounding error above amount\n * @param targetAcc Account to mint to\n * @param amount Amount to mint\n */\n function testMintBalanceRounding(uint8 targetAcc, uint256 amount) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n uint256 amountMinted = mint(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n int256 delta = int256(balanceAfter) - int256(balanceBefore);\n\n // delta == amount, if no error\n // delta < amount, if too little is minted\n // delta > amount, if too much is minted\n int256 error = int256(amountMinted) - delta;\n\n assert(error >= 0);\n assert(error <= int256(MINT_ROUNDING_ERROR));\n }\n\n /**\n * @notice A burn of an account balance must result in a zero balance\n * @param targetAcc Account to burn from\n */\n function testBurnAllBalanceToZero(uint8 targetAcc) public hasKnownIssue {\n address target = getAccount(targetAcc);\n\n burn(targetAcc, ousd.balanceOf(target));\n assert(ousd.balanceOf(target) == 0);\n }\n\n /**\n * @notice You should always be able to burn an account's balance\n * @param targetAcc Account to burn from\n */\n function testBurnAllBalanceShouldNotRevert(uint8 targetAcc)\n public\n hasKnownIssue\n {\n address target = getAccount(targetAcc);\n uint256 balance = ousd.balanceOf(target);\n\n hevm.prank(ADDRESS_VAULT);\n try ousd.burn(target, balance) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestSupply.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestTransfer.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n/**\n * @title Mixin for testing supply related functions\n * @author Rappie\n */\ncontract EchidnaTestSupply is EchidnaTestTransfer {\n using StableMath for uint256;\n\n uint256 prevRebasingCreditsPerToken = type(uint256).max;\n\n /**\n * @notice After a `changeSupply`, the total supply should exactly\n * match the target total supply. (This is needed to ensure successive\n * rebases are correct).\n * @param supply New total supply\n * @custom:error testChangeSupply(uint256): failed!💥\n * Call sequence:\n * testChangeSupply(1044505275072865171609)\n * Event sequence:\n * TotalSupplyUpdatedHighres(1044505275072865171610, 1000000000000000000000000, 957391048054055578595)\n */\n function testChangeSupply(uint256 supply)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n hevm.prank(ADDRESS_VAULT);\n ousd.changeSupply(supply);\n\n assert(ousd.totalSupply() == supply);\n }\n\n /**\n * @notice The total supply must not be less than the sum of account balances.\n * (The difference will go into future rebases)\n * @custom:error testTotalSupplyLessThanTotalBalance(): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(1)\n * optOut(64)\n * transfer(0,64,1)\n * testTotalSupplyLessThanTotalBalance()\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000001000001)\n * Debug(«totalBalance», 1000000000000000001000002)\n */\n function testTotalSupplyLessThanTotalBalance()\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n uint256 totalSupply = ousd.totalSupply();\n uint256 totalBalance = getTotalBalance();\n\n Debugger.log(\"totalSupply\", totalSupply);\n Debugger.log(\"totalBalance\", totalBalance);\n\n assert(totalSupply >= totalBalance);\n }\n\n /**\n * @notice Non-rebasing supply should not be larger than total supply\n * @custom:error testNonRebasingSupplyVsTotalSupply(): failed!💥\n * Call sequence:\n * mint(0,2)\n * changeSupply(3)\n * burn(0,1)\n * optOut(0)\n * testNonRebasingSupplyVsTotalSupply()\n */\n function testNonRebasingSupplyVsTotalSupply() public hasKnownIssue {\n uint256 nonRebasingSupply = ousd.nonRebasingSupply();\n uint256 totalSupply = ousd.totalSupply();\n\n assert(nonRebasingSupply <= totalSupply);\n }\n\n /**\n * @notice Global `rebasingCreditsPerToken` should never increase\n * @custom:error testRebasingCreditsPerTokenNotIncreased(): failed!💥\n * Call sequence:\n * testRebasingCreditsPerTokenNotIncreased()\n * changeSupply(1)\n * testRebasingCreditsPerTokenNotIncreased()\n */\n function testRebasingCreditsPerTokenNotIncreased() public hasKnownIssue {\n uint256 curRebasingCreditsPerToken = ousd\n .rebasingCreditsPerTokenHighres();\n\n Debugger.log(\n \"prevRebasingCreditsPerToken\",\n prevRebasingCreditsPerToken\n );\n Debugger.log(\"curRebasingCreditsPerToken\", curRebasingCreditsPerToken);\n\n assert(curRebasingCreditsPerToken <= prevRebasingCreditsPerToken);\n\n prevRebasingCreditsPerToken = curRebasingCreditsPerToken;\n }\n\n /**\n * @notice The rebasing credits per token ratio must greater than zero\n */\n function testRebasingCreditsPerTokenAboveZero() public {\n assert(ousd.rebasingCreditsPerTokenHighres() > 0);\n }\n\n /**\n * @notice The sum of all non-rebasing balances should not be larger than\n * non-rebasing supply\n * @custom:error testTotalNonRebasingSupplyLessThanTotalBalance(): failed!💥\n * Call sequence\n * mint(0,2)\n * changeSupply(1)\n * optOut(0)\n * burn(0,1)\n * testTotalNonRebasingSupplyLessThanTotalBalance()\n * Event sequence:\n * Debug(«totalNonRebasingSupply», 500000000000000000000001)\n * Debug(«totalNonRebasingBalance», 500000000000000000000002)\n */\n function testTotalNonRebasingSupplyLessThanTotalBalance()\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n uint256 totalNonRebasingSupply = ousd.nonRebasingSupply();\n uint256 totalNonRebasingBalance = getTotalNonRebasingBalance();\n\n Debugger.log(\"totalNonRebasingSupply\", totalNonRebasingSupply);\n Debugger.log(\"totalNonRebasingBalance\", totalNonRebasingBalance);\n\n assert(totalNonRebasingSupply >= totalNonRebasingBalance);\n }\n\n /**\n * @notice An accounts credits / credits per token should not be larger it's balance\n * @param targetAcc The account to check\n */\n function testCreditsPerTokenVsBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n (uint256 credits, uint256 creditsPerToken, ) = ousd\n .creditsBalanceOfHighres(target);\n uint256 expectedBalance = credits.divPrecisely(creditsPerToken);\n\n uint256 balance = ousd.balanceOf(target);\n\n Debugger.log(\"credits\", credits);\n Debugger.log(\"creditsPerToken\", creditsPerToken);\n Debugger.log(\"expectedBalance\", expectedBalance);\n Debugger.log(\"balance\", balance);\n\n assert(expectedBalance == balance);\n }\n}\n" + }, + "contracts/echidna/EchidnaTestTransfer.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin for testing transfer related functions\n * @author Rappie\n */\ncontract EchidnaTestTransfer is EchidnaDebug {\n /**\n * @notice The receiving account's balance after a transfer must not increase by\n * less than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferBalanceReceivedLess(uint8,uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * mint(64,2)\n * testTransferBalanceReceivedLess(64,0,1)\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000000500002)\n * Debug(«toBalBefore», 0)\n * Debug(«toBalAfter», 0)\n */\n function testTransferBalanceReceivedLess(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue hasKnownIssueWithinLimits {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n\n assert(toBalAfter >= toBalBefore + amount);\n }\n\n /**\n * @notice The receiving account's balance after a transfer must not\n * increase by more than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceReceivedMore(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n\n assert(toBalAfter <= toBalBefore + amount);\n }\n\n /**\n * @notice The sending account's balance after a transfer must not\n * decrease by less than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferBalanceSentLess(uint8,uint8,uint256): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(1)\n * testTransferBalanceSentLess(0,64,1)\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000000500001)\n * Debug(«fromBalBefore», 1)\n * Debug(«fromBalAfter», 1)\n */\n function testTransferBalanceSentLess(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue hasKnownIssueWithinLimits {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n\n assert(fromBalAfter <= fromBalBefore - amount);\n }\n\n /**\n * @notice The sending account's balance after a transfer must not\n * decrease by more than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceSentMore(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n\n assert(fromBalAfter >= fromBalBefore - amount);\n }\n\n /**\n * @notice The receiving account's balance after a transfer must not\n * increase by less than the amount transferred (minus rounding error)\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceReceivedLessRounding(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n int256 toDelta = int256(toBalAfter) - int256(toBalBefore);\n\n // delta == amount, if no error\n // delta < amount, if too little is sent\n // delta > amount, if too much is sent\n int256 error = int256(amount) - toDelta;\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n Debugger.log(\"toDelta\", toDelta);\n Debugger.log(\"error\", error);\n\n assert(error >= 0);\n assert(error <= int256(TRANSFER_ROUNDING_ERROR));\n }\n\n /**\n * @notice The sending account's balance after a transfer must\n * not decrease by less than the amount transferred (minus rounding error)\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceSentLessRounding(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n int256 fromDelta = int256(fromBalAfter) - int256(fromBalBefore);\n\n // delta == -amount, if no error\n // delta < -amount, if too much is sent\n // delta > -amount, if too little is sent\n int256 error = int256(amount) + fromDelta;\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n Debugger.log(\"fromDelta\", fromDelta);\n Debugger.log(\"error\", error);\n\n assert(error >= 0);\n assert(error <= int256(TRANSFER_ROUNDING_ERROR));\n }\n\n /**\n * @notice An account should always be able to successfully transfer\n * an amount within its balance.\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferWithinBalanceDoesNotRevert(uint8,uint8,uint8): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(3)\n * optOut(0)\n * testTransferWithinBalanceDoesNotRevert(0,128,2)\n * optIn(0)\n * testTransferWithinBalanceDoesNotRevert(128,0,1)\n * Event sequence:\n * error Revert Panic(17): SafeMath over-/under-flows\n */\n function testTransferWithinBalanceDoesNotRevert(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount > 0);\n amount = amount % ousd.balanceOf(from);\n\n Debugger.log(\"Total supply\", ousd.totalSupply());\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(to, amount) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n\n /**\n * @notice An account should never be able to successfully transfer\n * an amount greater than their balance.\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferExceedingBalanceReverts(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n amount = ousd.balanceOf(from) + 1 + amount;\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(to, amount) {\n assert(false);\n } catch {\n assert(true);\n }\n }\n\n /**\n * @notice A transfer to the same account should not change that account's balance\n * @param targetAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferSelf(uint8 targetAcc, uint256 amount) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n transfer(targetAcc, targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceBefore == balanceAfter);\n }\n\n /**\n * @notice Transfers to the zero account revert\n * @param fromAcc Account to transfer from\n * @param amount Amount to transfer\n */\n function testTransferToZeroAddress(uint8 fromAcc, uint256 amount) public {\n address from = getAccount(fromAcc);\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(address(0), amount) {\n assert(false);\n } catch {\n assert(true);\n }\n }\n}\n" + }, + "contracts/echidna/IHevm.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// https://github.com/ethereum/hevm/blob/main/doc/src/controlling-the-unit-testing-environment.md#cheat-codes\n\ninterface IHevm {\n function warp(uint256 x) external;\n\n function roll(uint256 x) external;\n\n function store(\n address c,\n bytes32 loc,\n bytes32 val\n ) external;\n\n function load(address c, bytes32 loc) external returns (bytes32 val);\n\n function sign(uint256 sk, bytes32 digest)\n external\n returns (\n uint8 v,\n bytes32 r,\n bytes32 s\n );\n\n function addr(uint256 sk) external returns (address addr);\n\n function ffi(string[] calldata) external returns (bytes memory);\n\n function prank(address sender) external;\n}\n" + }, + "contracts/echidna/OUSDEchidna.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../token/OUSD.sol\";\n\ncontract OUSDEchidna is OUSD {\n constructor() OUSD() {}\n\n function _isNonRebasingAccountEchidna(address _account)\n public\n returns (bool)\n {\n _autoMigrate(_account);\n return alternativeCreditsPerToken[_account] > 0;\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/governance/InitializableGovernable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD InitializableGovernable Contract\n * @author Origin Protocol Inc\n */\nimport { Initializable } from \"../utils/Initializable.sol\";\n\nimport { Governable } from \"./Governable.sol\";\n\ncontract InitializableGovernable is Governable, Initializable {\n function _initialize(address _newGovernor) internal {\n _changeGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/governance/Strategizable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"./Governable.sol\";\n\ncontract Strategizable is Governable {\n event StrategistUpdated(address _address);\n\n // Address of strategist\n address public strategistAddr;\n\n // For future use\n uint256[50] private __gap;\n\n /**\n * @dev Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @dev Set address of Strategist\n * @param _address Address of Strategist\n */\n function setStrategistAddr(address _address) external onlyGovernor {\n _setStrategistAddr(_address);\n }\n\n /**\n * @dev Set address of Strategist\n * @param _address Address of Strategist\n */\n function _setStrategistAddr(address _address) internal {\n strategistAddr = _address;\n emit StrategistUpdated(_address);\n }\n}\n" + }, + "contracts/harvest/AbstractHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IUniswapV2Router } from \"../interfaces/uniswap/IUniswapV2Router02.sol\";\nimport { IUniswapV3Router } from \"../interfaces/uniswap/IUniswapV3Router.sol\";\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { ICurvePool } from \"../strategies/ICurvePool.sol\";\nimport \"../utils/Helpers.sol\";\n\nabstract contract AbstractHarvester is Governable {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using StableMath for uint256;\n\n enum SwapPlatform {\n UniswapV2Compatible,\n UniswapV3,\n Balancer,\n Curve\n }\n\n event SupportedStrategyUpdate(address strategyAddress, bool isSupported);\n event RewardTokenConfigUpdated(\n address tokenAddress,\n uint16 allowedSlippageBps,\n uint16 harvestRewardBps,\n SwapPlatform swapPlatform,\n address swapPlatformAddr,\n bytes swapData,\n uint256 liquidationLimit,\n bool doSwapRewardToken\n );\n event RewardTokenSwapped(\n address indexed rewardToken,\n address indexed swappedInto,\n SwapPlatform swapPlatform,\n uint256 amountIn,\n uint256 amountOut\n );\n event RewardProceedsTransferred(\n address indexed token,\n address farmer,\n uint256 protcolYield,\n uint256 farmerFee\n );\n event RewardProceedsAddressChanged(address newProceedsAddress);\n\n error EmptyAddress();\n error InvalidSlippageBps();\n error InvalidHarvestRewardBps();\n\n error InvalidSwapPlatform(SwapPlatform swapPlatform);\n\n error InvalidUniswapV2PathLength();\n error InvalidTokenInSwapPath(address token);\n error EmptyBalancerPoolId();\n error InvalidCurvePoolAssetIndex(address token);\n\n error UnsupportedStrategy(address strategyAddress);\n\n error SlippageError(uint256 actualBalance, uint256 minExpected);\n error BalanceMismatchAfterSwap(uint256 actualBalance, uint256 minExpected);\n\n // Configuration properties for harvesting logic of reward tokens\n struct RewardTokenConfig {\n // Max allowed slippage when swapping reward token for a stablecoin denominated in basis points.\n uint16 allowedSlippageBps;\n // Reward when calling a harvest function denominated in basis points.\n uint16 harvestRewardBps;\n // Address of compatible exchange protocol (Uniswap V2/V3, SushiSwap, Balancer and Curve).\n address swapPlatformAddr;\n /* When true the reward token is being swapped. In a need of (temporarily) disabling the swapping of\n * a reward token this needs to be set to false.\n */\n bool doSwapRewardToken;\n // Platform to use for Swapping\n SwapPlatform swapPlatform;\n /* How much token can be sold per one harvest call. If the balance of rewards tokens\n * exceeds that limit multiple harvest calls are required to harvest all of the tokens.\n * Set it to MAX_INT to effectively disable the limit.\n */\n uint256 liquidationLimit;\n }\n\n mapping(address => RewardTokenConfig) public rewardTokenConfigs;\n mapping(address => bool) public supportedStrategies;\n\n address public immutable vaultAddress;\n\n /**\n * Address receiving rewards proceeds. Initially the Vault contract later will possibly\n * be replaced by another contract that eases out rewards distribution.\n **/\n address public rewardProceedsAddress;\n\n /**\n * All tokens are swapped to this token before it gets transferred\n * to the `rewardProceedsAddress`. USDT for OUSD and WETH for OETH.\n **/\n address public immutable baseTokenAddress;\n // Cached decimals for `baseTokenAddress`\n uint256 public immutable baseTokenDecimals;\n\n // Uniswap V2 path for reward tokens using Uniswap V2 Router\n mapping(address => address[]) public uniswapV2Path;\n // Uniswap V3 path for reward tokens using Uniswap V3 Router\n mapping(address => bytes) public uniswapV3Path;\n // Pool ID to use for reward tokens on Balancer\n mapping(address => bytes32) public balancerPoolId;\n\n struct CurvePoolIndices {\n // Casted into uint128 and stored in a struct to save gas\n uint128 rewardTokenIndex;\n uint128 baseTokenIndex;\n }\n // Packed indices of assets on the Curve pool\n mapping(address => CurvePoolIndices) public curvePoolIndices;\n\n constructor(address _vaultAddress, address _baseTokenAddress) {\n require(_vaultAddress != address(0));\n require(_baseTokenAddress != address(0));\n\n vaultAddress = _vaultAddress;\n baseTokenAddress = _baseTokenAddress;\n\n // Cache decimals as well\n baseTokenDecimals = Helpers.getDecimals(_baseTokenAddress);\n }\n\n /***************************************\n Configuration\n ****************************************/\n\n /**\n * Set the Address receiving rewards proceeds.\n * @param _rewardProceedsAddress Address of the reward token\n */\n function setRewardProceedsAddress(address _rewardProceedsAddress)\n external\n onlyGovernor\n {\n if (_rewardProceedsAddress == address(0)) {\n revert EmptyAddress();\n }\n\n rewardProceedsAddress = _rewardProceedsAddress;\n emit RewardProceedsAddressChanged(_rewardProceedsAddress);\n }\n\n /**\n * @dev Add/update a reward token configuration that holds harvesting config variables\n * @param _tokenAddress Address of the reward token\n * @param tokenConfig.allowedSlippageBps uint16 maximum allowed slippage denominated in basis points.\n * Example: 300 == 3% slippage\n * @param tokenConfig.harvestRewardBps uint16 amount of reward tokens the caller of the function is rewarded.\n * Example: 100 == 1%\n * @param tokenConfig.swapPlatformAddr Address Address of a UniswapV2 compatible contract to perform\n * the exchange from reward tokens to stablecoin (currently hard-coded to USDT)\n * @param tokenConfig.liquidationLimit uint256 Maximum amount of token to be sold per one swap function call.\n * When value is 0 there is no limit.\n * @param tokenConfig.doSwapRewardToken bool Disables swapping of the token when set to true,\n * does not cause it to revert though.\n * @param tokenConfig.swapPlatform SwapPlatform to use for Swapping\n * @param swapData Additional data required for swapping\n */\n function setRewardTokenConfig(\n address _tokenAddress,\n RewardTokenConfig calldata tokenConfig,\n bytes calldata swapData\n ) external onlyGovernor {\n if (tokenConfig.allowedSlippageBps > 1000) {\n revert InvalidSlippageBps();\n }\n\n if (tokenConfig.harvestRewardBps > 1000) {\n revert InvalidHarvestRewardBps();\n }\n\n address newRouterAddress = tokenConfig.swapPlatformAddr;\n if (newRouterAddress == address(0)) {\n // Swap router address should be non zero address\n revert EmptyAddress();\n }\n\n address oldRouterAddress = rewardTokenConfigs[_tokenAddress]\n .swapPlatformAddr;\n rewardTokenConfigs[_tokenAddress] = tokenConfig;\n\n // Revert if feed does not exist\n // slither-disable-next-line unused-return\n IOracle(IVault(vaultAddress).priceProvider()).price(_tokenAddress);\n\n IERC20 token = IERC20(_tokenAddress);\n // if changing token swap provider cancel existing allowance\n if (\n /* oldRouterAddress == address(0) when there is no pre-existing\n * configuration for said rewards token\n */\n oldRouterAddress != address(0) &&\n oldRouterAddress != newRouterAddress\n ) {\n token.safeApprove(oldRouterAddress, 0);\n }\n\n // Give SwapRouter infinite approval when needed\n if (oldRouterAddress != newRouterAddress) {\n token.safeApprove(newRouterAddress, 0);\n token.safeApprove(newRouterAddress, type(uint256).max);\n }\n\n SwapPlatform _platform = tokenConfig.swapPlatform;\n if (_platform == SwapPlatform.UniswapV2Compatible) {\n uniswapV2Path[_tokenAddress] = _decodeUniswapV2Path(\n swapData,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.UniswapV3) {\n uniswapV3Path[_tokenAddress] = _decodeUniswapV3Path(\n swapData,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.Balancer) {\n balancerPoolId[_tokenAddress] = _decodeBalancerPoolId(\n swapData,\n newRouterAddress,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.Curve) {\n curvePoolIndices[_tokenAddress] = _decodeCurvePoolIndices(\n swapData,\n newRouterAddress,\n _tokenAddress\n );\n } else {\n // Note: This code is unreachable since Solidity reverts when\n // the value is outside the range of defined values of the enum\n // (even if it's under the max length of the base type)\n revert InvalidSwapPlatform(_platform);\n }\n\n emit RewardTokenConfigUpdated(\n _tokenAddress,\n tokenConfig.allowedSlippageBps,\n tokenConfig.harvestRewardBps,\n _platform,\n newRouterAddress,\n swapData,\n tokenConfig.liquidationLimit,\n tokenConfig.doSwapRewardToken\n );\n }\n\n /**\n * @dev Decodes the data passed into Uniswap V2 path and validates\n * it to make sure the path is for `token` to `baseToken`\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param token The address of the reward token\n * @return path The validated Uniswap V2 path\n */\n function _decodeUniswapV2Path(bytes calldata data, address token)\n internal\n view\n returns (address[] memory path)\n {\n (path) = abi.decode(data, (address[]));\n uint256 len = path.length;\n\n if (len < 2) {\n // Path should have at least two tokens\n revert InvalidUniswapV2PathLength();\n }\n\n // Do some validation\n if (path[0] != token) {\n revert InvalidTokenInSwapPath(path[0]);\n }\n\n if (path[len - 1] != baseTokenAddress) {\n revert InvalidTokenInSwapPath(path[len - 1]);\n }\n }\n\n /**\n * @dev Decodes the data passed into Uniswap V3 path and validates\n * it to make sure the path is for `token` to `baseToken`\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param token The address of the reward token\n * @return path The validated Uniswap V3 path\n */\n function _decodeUniswapV3Path(bytes calldata data, address token)\n internal\n view\n returns (bytes calldata path)\n {\n path = data;\n\n address decodedAddress = address(uint160(bytes20(data[0:20])));\n\n if (decodedAddress != token) {\n // Invalid Reward Token in swap path\n revert InvalidTokenInSwapPath(decodedAddress);\n }\n\n decodedAddress = address(uint160(bytes20(data[path.length - 20:])));\n if (decodedAddress != baseTokenAddress) {\n // Invalid Base Token in swap path\n revert InvalidTokenInSwapPath(decodedAddress);\n }\n }\n\n /**\n * @dev Decodes the data passed to Balancer Pool ID\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @return poolId The pool ID\n */\n function _decodeBalancerPoolId(\n bytes calldata data,\n address balancerVault,\n address token\n ) internal view returns (bytes32 poolId) {\n (poolId) = abi.decode(data, (bytes32));\n\n if (poolId == bytes32(0)) {\n revert EmptyBalancerPoolId();\n }\n\n IBalancerVault bVault = IBalancerVault(balancerVault);\n\n // Note: this reverts if token is not a pool asset\n // slither-disable-next-line unused-return\n bVault.getPoolTokenInfo(poolId, token);\n\n // slither-disable-next-line unused-return\n bVault.getPoolTokenInfo(poolId, baseTokenAddress);\n }\n\n /**\n * @dev Decodes the data passed to get the pool indices and\n * checks it against the Curve Pool to make sure it's\n * not misconfigured. The indices are packed into a single\n * uint256 for gas savings\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param poolAddress Curve pool address\n * @param token The address of the reward token\n * @return indices Packed pool asset indices\n */\n function _decodeCurvePoolIndices(\n bytes calldata data,\n address poolAddress,\n address token\n ) internal view returns (CurvePoolIndices memory indices) {\n indices = abi.decode(data, (CurvePoolIndices));\n\n ICurvePool pool = ICurvePool(poolAddress);\n if (token != pool.coins(indices.rewardTokenIndex)) {\n revert InvalidCurvePoolAssetIndex(token);\n }\n if (baseTokenAddress != pool.coins(indices.baseTokenIndex)) {\n revert InvalidCurvePoolAssetIndex(baseTokenAddress);\n }\n }\n\n /**\n * @dev Flags a strategy as supported or not supported one\n * @param _strategyAddress Address of the strategy\n * @param _isSupported Bool marking strategy as supported or not supported\n */\n function setSupportedStrategy(address _strategyAddress, bool _isSupported)\n external\n onlyGovernor\n {\n supportedStrategies[_strategyAddress] = _isSupported;\n emit SupportedStrategyUpdate(_strategyAddress, _isSupported);\n }\n\n /***************************************\n Rewards\n ****************************************/\n\n /**\n * @dev Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform. Can be called by anyone.\n * Rewards incentivizing the caller are sent to the caller of this function.\n * @param _strategyAddr Address of the strategy to collect rewards from\n */\n function harvestAndSwap(address _strategyAddr) external nonReentrant {\n // Remember _harvest function checks for the validity of _strategyAddr\n _harvestAndSwap(_strategyAddr, msg.sender);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform. Can be called by anyone\n * @param _strategyAddr Address of the strategy to collect rewards from\n * @param _rewardTo Address where to send a share of harvest rewards to as an incentive\n * for executing this function\n */\n function harvestAndSwap(address _strategyAddr, address _rewardTo)\n external\n nonReentrant\n {\n // Remember _harvest function checks for the validity of _strategyAddr\n _harvestAndSwap(_strategyAddr, _rewardTo);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform\n * @param _strategyAddr Address of the strategy to collect rewards from\n * @param _rewardTo Address where to send a share of harvest rewards to as an incentive\n * for executing this function\n */\n function _harvestAndSwap(address _strategyAddr, address _rewardTo)\n internal\n {\n _harvest(_strategyAddr);\n IStrategy strategy = IStrategy(_strategyAddr);\n address[] memory rewardTokens = strategy.getRewardTokenAddresses();\n IOracle priceProvider = IOracle(IVault(vaultAddress).priceProvider());\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; ++i) {\n _swap(rewardTokens[i], _rewardTo, priceProvider);\n }\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform\n * @param _strategyAddr Address of the strategy to collect rewards from.\n */\n function _harvest(address _strategyAddr) internal virtual {\n if (!supportedStrategies[_strategyAddr]) {\n revert UnsupportedStrategy(_strategyAddr);\n }\n\n IStrategy strategy = IStrategy(_strategyAddr);\n strategy.collectRewardTokens();\n }\n\n /**\n * @dev Swap a reward token for the base token on the configured\n * swap platform. The token must have a registered price feed\n * with the price provider\n * @param _swapToken Address of the token to swap\n * @param _rewardTo Address where to send the share of harvest rewards to\n * @param _priceProvider Oracle to get prices of the swap token\n */\n function _swap(\n address _swapToken,\n address _rewardTo,\n IOracle _priceProvider\n ) internal virtual {\n uint256 balance = IERC20(_swapToken).balanceOf(address(this));\n\n // No need to swap if the reward token is the base token. eg USDT or WETH.\n // There is also no limit on the transfer. Everything in the harvester will be transferred\n // to the Dripper regardless of the liquidationLimit config.\n if (_swapToken == baseTokenAddress) {\n IERC20(_swapToken).safeTransfer(rewardProceedsAddress, balance);\n // currently not paying the farmer any rewards as there is no swap\n emit RewardProceedsTransferred(\n baseTokenAddress,\n address(0),\n balance,\n 0\n );\n return;\n }\n\n RewardTokenConfig memory tokenConfig = rewardTokenConfigs[_swapToken];\n\n /* This will trigger a return when reward token configuration has not yet been set\n * or we have temporarily disabled swapping of specific reward token via setting\n * doSwapRewardToken to false.\n */\n if (!tokenConfig.doSwapRewardToken) {\n return;\n }\n\n if (balance == 0) {\n return;\n }\n\n if (tokenConfig.liquidationLimit > 0) {\n balance = Math.min(balance, tokenConfig.liquidationLimit);\n }\n\n // This'll revert if there is no price feed\n uint256 oraclePrice = _priceProvider.price(_swapToken);\n\n // Oracle price is 1e18\n uint256 minExpected = (balance *\n (1e4 - tokenConfig.allowedSlippageBps) * // max allowed slippage\n oraclePrice).scaleBy(\n baseTokenDecimals,\n Helpers.getDecimals(_swapToken)\n ) /\n 1e4 / // fix the max slippage decimal position\n 1e18; // and oracle price decimals position\n\n // Do the swap\n uint256 amountReceived = _doSwap(\n tokenConfig.swapPlatform,\n tokenConfig.swapPlatformAddr,\n _swapToken,\n balance,\n minExpected\n );\n\n if (amountReceived < minExpected) {\n revert SlippageError(amountReceived, minExpected);\n }\n\n emit RewardTokenSwapped(\n _swapToken,\n baseTokenAddress,\n tokenConfig.swapPlatform,\n balance,\n amountReceived\n );\n\n IERC20 baseToken = IERC20(baseTokenAddress);\n uint256 baseTokenBalance = baseToken.balanceOf(address(this));\n if (baseTokenBalance < amountReceived) {\n // Note: It's possible to bypass this check by transferring `baseToken`\n // directly to Harvester before calling the `harvestAndSwap`. However,\n // there's no incentive for an attacker to do that. Doing a balance diff\n // will increase the gas cost significantly\n revert BalanceMismatchAfterSwap(baseTokenBalance, amountReceived);\n }\n\n // Farmer only gets fee from the base amount they helped farm,\n // They do not get anything from anything that already was there\n // on the Harvester\n uint256 farmerFee = amountReceived.mulTruncateScale(\n tokenConfig.harvestRewardBps,\n 1e4\n );\n uint256 protocolYield = baseTokenBalance - farmerFee;\n\n baseToken.safeTransfer(rewardProceedsAddress, protocolYield);\n baseToken.safeTransfer(_rewardTo, farmerFee);\n emit RewardProceedsTransferred(\n baseTokenAddress,\n _rewardTo,\n protocolYield,\n farmerFee\n );\n }\n\n function _doSwap(\n SwapPlatform swapPlatform,\n address routerAddress,\n address rewardTokenAddress,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n if (swapPlatform == SwapPlatform.UniswapV2Compatible) {\n return\n _swapWithUniswapV2(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.UniswapV3) {\n return\n _swapWithUniswapV3(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.Balancer) {\n return\n _swapWithBalancer(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.Curve) {\n return\n _swapWithCurve(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else {\n // Should never be invoked since we catch invalid values\n // in the `setRewardTokenConfig` function before it's set\n revert InvalidSwapPlatform(swapPlatform);\n }\n }\n\n /**\n * @dev Swaps the token to `baseToken` with Uniswap V2\n *\n * @param routerAddress Uniswap V2 Router address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithUniswapV2(\n address routerAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n address[] memory path = uniswapV2Path[swapToken];\n\n uint256[] memory amounts = IUniswapV2Router(routerAddress)\n .swapExactTokensForTokens(\n amountIn,\n minAmountOut,\n path,\n address(this),\n block.timestamp\n );\n\n amountOut = amounts[amounts.length - 1];\n }\n\n /**\n * @dev Swaps the token to `baseToken` with Uniswap V3\n *\n * @param routerAddress Uniswap V3 Router address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithUniswapV3(\n address routerAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n bytes memory path = uniswapV3Path[swapToken];\n\n IUniswapV3Router.ExactInputParams memory params = IUniswapV3Router\n .ExactInputParams({\n path: path,\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: amountIn,\n amountOutMinimum: minAmountOut\n });\n amountOut = IUniswapV3Router(routerAddress).exactInput(params);\n }\n\n /**\n * @dev Swaps the token to `baseToken` on Balancer\n *\n * @param balancerVaultAddress BalancerVaultAddress\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithBalancer(\n address balancerVaultAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n bytes32 poolId = balancerPoolId[swapToken];\n\n IBalancerVault.SingleSwap memory singleSwap = IBalancerVault\n .SingleSwap({\n poolId: poolId,\n kind: IBalancerVault.SwapKind.GIVEN_IN,\n assetIn: swapToken,\n assetOut: baseTokenAddress,\n amount: amountIn,\n userData: hex\"\"\n });\n\n IBalancerVault.FundManagement memory fundMgmt = IBalancerVault\n .FundManagement({\n sender: address(this),\n fromInternalBalance: false,\n recipient: payable(address(this)),\n toInternalBalance: false\n });\n\n amountOut = IBalancerVault(balancerVaultAddress).swap(\n singleSwap,\n fundMgmt,\n minAmountOut,\n block.timestamp\n );\n }\n\n /**\n * @dev Swaps the token to `baseToken` on Curve\n *\n * @param poolAddress Curve Pool Address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithCurve(\n address poolAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n CurvePoolIndices memory indices = curvePoolIndices[swapToken];\n\n // Note: Not all CurvePools return the `amountOut`, make sure\n // to use only pool that do. Otherwise the swap would revert\n // always\n amountOut = ICurvePool(poolAddress).exchange(\n uint256(indices.rewardTokenIndex),\n uint256(indices.baseTokenIndex),\n amountIn,\n minAmountOut\n );\n }\n}\n" + }, + "contracts/harvest/Dripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\n/**\n * @title OUSD Dripper\n *\n * The dripper contract smooths out the yield from point-in-time yield events\n * and spreads the yield out over a configurable time period. This ensures a\n * continuous per block yield to makes users happy as their next rebase\n * amount is always moving up. Also, this makes historical day to day yields\n * smooth, rather than going from a near zero day, to a large APY day, then\n * back to a near zero day again.\n *\n *\n * Design notes\n * - USDT has a smaller resolution than the number of seconds\n * in a week, which can make per second payouts have a rounding error. However\n * the total effect is not large - cents per day, and this money is\n * not lost, just distributed in the future. While we could use a higher\n * decimal precision for the drip perSecond, we chose simpler code.\n * - By calculating the changing drip rates on collects only, harvests and yield\n * events don't have to call anything on this contract or pay any extra gas.\n * Collect() is already be paying for a single write, since it has to reset\n * the lastCollect time.\n * - By having a collectAndRebase method, and having our external systems call\n * that, the OUSD vault does not need any changes, not even to know the address\n * of the dripper.\n * - A rejected design was to retro-calculate the drip rate on each collect,\n * based on the balance at the time of the collect. While this would have\n * required less state, and would also have made the contract respond more quickly\n * to new income, it would break the predictability that is this contract's entire\n * purpose. If we did this, the amount of fundsAvailable() would make sharp increases\n * when funds were deposited.\n * - When the dripper recalculates the rate, it targets spending the balance over\n * the duration. This means that every time that collect is called, if no\n * new funds have been deposited the duration is being pushed back and the\n * rate decreases. This is expected, and ends up following a smoother but\n * longer curve the more collect() is called without incoming yield.\n *\n */\n\ncontract Dripper is Governable {\n using SafeERC20 for IERC20;\n\n struct Drip {\n uint64 lastCollect; // overflows 262 billion years after the sun dies\n uint192 perSecond; // drip rate per second\n }\n\n address immutable vault; // OUSD vault\n address immutable token; // token to drip out\n uint256 public dripDuration; // in seconds\n Drip public drip; // active drip parameters\n\n constructor(address _vault, address _token) {\n vault = _vault;\n token = _token;\n }\n\n /// @notice How much funds have dripped out already and are currently\n // available to be sent to the vault.\n /// @return The amount that would be sent if a collect was called\n function availableFunds() external view returns (uint256) {\n uint256 balance = IERC20(token).balanceOf(address(this));\n return _availableFunds(balance, drip);\n }\n\n /// @notice Collect all dripped funds and send to vault.\n /// Recalculate new drip rate.\n function collect() external {\n _collect();\n }\n\n /// @notice Collect all dripped funds, send to vault, recalculate new drip\n /// rate, and rebase OUSD.\n function collectAndRebase() external {\n _collect();\n IVault(vault).rebase();\n }\n\n /// @dev Change the drip duration. Governor only.\n /// @param _durationSeconds the number of seconds to drip out the entire\n /// balance over if no collects were called during that time.\n function setDripDuration(uint256 _durationSeconds)\n external\n virtual\n onlyGovernor\n {\n require(_durationSeconds > 0, \"duration must be non-zero\");\n dripDuration = _durationSeconds;\n _collect(); // duration change take immediate effect\n }\n\n /// @dev Transfer out ERC20 tokens held by the contract. Governor only.\n /// @param _asset ERC20 token address\n /// @param _amount amount to transfer\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /// @dev Calculate available funds by taking the lower of either the\n /// currently dripped out funds or the balance available.\n /// Uses passed in parameters to calculate with for gas savings.\n /// @param _balance current balance in contract\n /// @param _drip current drip parameters\n function _availableFunds(uint256 _balance, Drip memory _drip)\n internal\n view\n returns (uint256)\n {\n uint256 elapsed = block.timestamp - _drip.lastCollect;\n uint256 allowed = (elapsed * _drip.perSecond);\n return (allowed > _balance) ? _balance : allowed;\n }\n\n /// @dev Sends the currently dripped funds to be vault, and sets\n /// the new drip rate based on the new balance.\n function _collect() internal virtual {\n // Calculate send\n uint256 balance = IERC20(token).balanceOf(address(this));\n uint256 amountToSend = _availableFunds(balance, drip);\n uint256 remaining = balance - amountToSend;\n // Calculate new drip perSecond\n // Gas savings by setting entire struct at one time\n drip = Drip({\n perSecond: uint192(remaining / dripDuration),\n lastCollect: uint64(block.timestamp)\n });\n // Send funds\n IERC20(token).safeTransfer(vault, amountToSend);\n }\n\n /// @dev Transfer out all ERC20 held by the contract. Governor only.\n /// @param _asset ERC20 token address\n function transferAllToken(address _asset, address _receiver)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(\n _receiver,\n IERC20(_asset).balanceOf(address(this))\n );\n }\n}\n" + }, + "contracts/harvest/FixedRateDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Dripper } from \"./Dripper.sol\";\n\n/**\n * @title Fixed Rate Dripper\n *\n * Similar to the Dripper, Fixed Rate Dripper drips out yield per second.\n * However the Strategist decides the rate and it doesn't change after\n * a drip.\n *\n */\n\ncontract FixedRateDripper is Dripper {\n using SafeERC20 for IERC20;\n\n event DripRateUpdated(uint192 oldDripRate, uint192 newDripRate);\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vault).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n constructor(address _vault, address _token) Dripper(_vault, _token) {}\n\n /// @inheritdoc Dripper\n function setDripDuration(uint256) external virtual override {\n // Not used in FixedRateDripper\n revert(\"Drip duration disabled\");\n }\n\n /// @inheritdoc Dripper\n function _collect() internal virtual override {\n // Calculate amount to send\n uint256 balance = IERC20(token).balanceOf(address(this));\n uint256 amountToSend = _availableFunds(balance, drip);\n\n // Update timestamp\n drip.lastCollect = uint64(block.timestamp);\n\n // Send funds\n IERC20(token).safeTransfer(vault, amountToSend);\n }\n\n /**\n * @dev Sets the drip rate. Callable by Strategist or Governor.\n * Can be set to zero to stop dripper.\n * @param _perSecond Rate of WETH to drip per second\n */\n function setDripRate(uint192 _perSecond) external onlyGovernorOrStrategist {\n emit DripRateUpdated(_perSecond, drip.perSecond);\n\n /**\n * Note: It's important to call `_collect` before updating\n * the drip rate especially on a new proxy contract.\n * When `lastCollect` is not set/initialized, the elapsed\n * time would be calculated as `block.number` seconds,\n * resulting in a huge yield, if `collect` isn't called first.\n */\n // Collect at existing rate\n _collect();\n\n // Update rate\n drip.perSecond = _perSecond;\n }\n}\n" + }, + "contracts/harvest/Harvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractHarvester } from \"./AbstractHarvester.sol\";\n\ncontract Harvester is AbstractHarvester {\n constructor(address _vault, address _usdtAddress)\n AbstractHarvester(_vault, _usdtAddress)\n {}\n}\n" + }, + "contracts/harvest/OETHBaseHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { ISwapRouter } from \"../interfaces/aerodrome/ISwapRouter.sol\";\n\ncontract OETHBaseHarvester is Governable {\n using SafeERC20 for IERC20;\n\n IVault public immutable vault;\n IStrategy public immutable amoStrategy;\n IERC20 public immutable aero;\n IERC20 public immutable weth;\n ISwapRouter public immutable swapRouter;\n\n address public operatorAddr;\n\n // Similar sig to `AbstractHarvester.RewardTokenSwapped` for\n // future compatibility with monitoring\n event RewardTokenSwapped(\n address indexed rewardToken,\n address indexed swappedInto,\n uint8 swapPlatform,\n uint256 amountIn,\n uint256 amountOut\n );\n\n event OperatorChanged(address oldOperator, address newOperator);\n event YieldSent(address recipient, uint256 yield, uint256 fee);\n\n /**\n * @notice Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == vault.strategistAddr() || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @notice Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategistOrOperator() {\n require(\n msg.sender == operatorAddr ||\n msg.sender == vault.strategistAddr() ||\n isGovernor(),\n \"Caller is not the Operator or Strategist or Governor\"\n );\n _;\n }\n\n constructor(\n address _vault,\n address _amoStrategy,\n address _aero,\n address _weth,\n address _swapRouter\n ) {\n vault = IVault(_vault);\n amoStrategy = IStrategy(_amoStrategy);\n aero = IERC20(_aero);\n weth = IERC20(_weth);\n swapRouter = ISwapRouter(_swapRouter);\n }\n\n /**\n * @dev Changes the operator address which can call `harvest`\n * @param _operatorAddr New operator address\n */\n function setOperatorAddr(address _operatorAddr) external onlyGovernor {\n emit OperatorChanged(operatorAddr, _operatorAddr);\n operatorAddr = _operatorAddr;\n }\n\n /**\n * @notice Collects AERO from AMO strategy and\n * sends it to the Strategist multisig.\n * Anyone can call it.\n */\n function harvest() external onlyGovernorOrStrategistOrOperator {\n address strategistAddr = vault.strategistAddr();\n require(strategistAddr != address(0), \"Guardian address not set\");\n\n // Collect all AERO\n amoStrategy.collectRewardTokens();\n\n uint256 aeroBalance = aero.balanceOf(address(this));\n if (aeroBalance == 0) {\n // Do nothing if there's no AERO to transfer\n return;\n }\n\n // Transfer everything to Strategist\n aero.safeTransfer(strategistAddr, aeroBalance);\n }\n\n /**\n * @notice Harvests AERO from AMO strategy and then swaps some (or all)\n * of it into WETH to distribute yield and fee.\n * When `feeBps` is set to 10000 (100%), all WETH received is\n * sent to strategist.\n *\n * @param aeroToSwap Amount of AERO to swap\n * @param minWETHExpected Min. amount of WETH to expect\n * @param feeBps Performance fee bps (Sent to strategist)\n * @param sendYieldToDripper Sends yield to Dripper, if set to true.\n * Otherwise, to the Guardian\n */\n function harvestAndSwap(\n uint256 aeroToSwap,\n uint256 minWETHExpected,\n uint256 feeBps,\n bool sendYieldToDripper\n ) external onlyGovernorOrStrategist {\n address strategistAddr = vault.strategistAddr();\n require(strategistAddr != address(0), \"Guardian address not set\");\n\n // Yields can either be sent to the Dripper or Strategist\n address yieldRecipient = sendYieldToDripper\n ? vault.dripper()\n : strategistAddr;\n require(yieldRecipient != address(0), \"Yield recipient not set\");\n\n require(feeBps <= 10000, \"Invalid Fee Bps\");\n\n // Collect all AERO\n amoStrategy.collectRewardTokens();\n\n uint256 aeroBalance = aero.balanceOf(address(this));\n if (aeroBalance == 0) {\n // Do nothing if there's no AERO to transfer/swap\n return;\n }\n\n if (aeroToSwap > 0) {\n if (aeroBalance < aeroToSwap) {\n // Transfer in balance from the multisig as needed\n // slither-disable-next-line unchecked-transfer arbitrary-send-erc20\n aero.safeTransferFrom(\n strategistAddr,\n address(this),\n aeroToSwap - aeroBalance\n );\n }\n\n _doSwap(aeroToSwap, minWETHExpected);\n\n // Figure out AERO left in contract after swap\n aeroBalance = aero.balanceOf(address(this));\n }\n\n // Transfer out any leftover AERO after swap\n if (aeroBalance > 0) {\n aero.safeTransfer(strategistAddr, aeroBalance);\n }\n\n // Computes using all balance the contract holds,\n // not just the WETH received from swap. Use `transferToken`\n // if there's any WETH left that needs to be taken out\n uint256 availableWETHBalance = weth.balanceOf(address(this));\n // Computation rounds in favor of protocol\n uint256 fee = (availableWETHBalance * feeBps) / 10000;\n uint256 yield = availableWETHBalance - fee;\n\n // Transfer yield, if any\n if (yield > 0) {\n weth.safeTransfer(yieldRecipient, yield);\n }\n\n // Transfer fee to the Guardian, if any\n if (fee > 0) {\n weth.safeTransfer(strategistAddr, fee);\n }\n\n emit YieldSent(yieldRecipient, yield, fee);\n }\n\n /**\n * @notice Swaps AERO to WETH on Aerodrome\n * @param aeroToSwap Amount of AERO to swap\n * @param minWETHExpected Min. amount of WETH to expect\n */\n function _doSwap(uint256 aeroToSwap, uint256 minWETHExpected) internal {\n // Let the swap router move funds\n aero.approve(address(swapRouter), aeroToSwap);\n\n // Do the swap\n uint256 wethReceived = swapRouter.exactInputSingle(\n ISwapRouter.ExactInputSingleParams({\n tokenIn: address(aero),\n tokenOut: address(weth),\n tickSpacing: 200, // From AERO/WETH pool contract\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: aeroToSwap,\n amountOutMinimum: minWETHExpected,\n sqrtPriceLimitX96: 0\n })\n );\n\n emit RewardTokenSwapped(\n address(aero),\n address(weth),\n 0,\n aeroToSwap,\n wethReceived\n );\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * the contract, i.e. mistaken sends.\n * Also, allows to transfer any AERO left in the contract.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n virtual\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n}\n" + }, + "contracts/harvest/OETHDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Dripper } from \"./Dripper.sol\";\n\n/**\n * @title OETH Dripper Contract\n * @author Origin Protocol Inc\n */\ncontract OETHDripper is Dripper {\n constructor(address _vault, address _token) Dripper(_vault, _token) {}\n}\n" + }, + "contracts/harvest/OETHFixedRateDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { FixedRateDripper } from \"./FixedRateDripper.sol\";\n\n/**\n * @title OETH FixedRateDripper Contract\n * @author Origin Protocol Inc\n */\ncontract OETHFixedRateDripper is FixedRateDripper {\n constructor(address _vault, address _token)\n FixedRateDripper(_vault, _token)\n {}\n}\n" + }, + "contracts/harvest/OETHHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractHarvester } from \"./AbstractHarvester.sol\";\n\ncontract OETHHarvester is AbstractHarvester {\n constructor(address _vault, address _wethAddress)\n AbstractHarvester(_vault, _wethAddress)\n {}\n}\n" + }, + "contracts/harvest/OETHHarvesterSimple.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\n\n/// @title OETH Harvester Simple Contract\n/// @notice Contract to harvest rewards from strategies\n/// @author Origin Protocol Inc\ncontract OETHHarvesterSimple is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n ////////////////////////////////////////////////////\n /// --- CONSTANTS & IMMUTABLES\n ////////////////////////////////////////////////////\n /// @notice wrapped native token address (WETH or wS)\n address public immutable wrappedNativeToken;\n\n ////////////////////////////////////////////////////\n /// --- STORAGE\n ////////////////////////////////////////////////////\n /// @notice Dripper address\n address public dripper;\n\n /// @notice Mapping of supported strategies\n mapping(address => bool) public supportedStrategies;\n\n /// @notice Gap for upgrade safety\n uint256[48] private ___gap;\n\n ////////////////////////////////////////////////////\n /// --- EVENTS\n ////////////////////////////////////////////////////\n event Harvested(\n address indexed strategy,\n address token,\n uint256 amount,\n address indexed receiver\n );\n event SupportedStrategyUpdated(address strategy, bool status);\n event DripperUpdated(address dripper);\n\n ////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////\n constructor(address _wrappedNativeToken) {\n wrappedNativeToken = _wrappedNativeToken;\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /// @notice Initialize the contract\n function initialize() external onlyGovernor initializer {\n // Call it to set `initialized` to true and to prevent the implementation\n // from getting initialized in future through the proxy\n }\n\n ////////////////////////////////////////////////////\n /// --- MUTATIVE FUNCTIONS\n ////////////////////////////////////////////////////\n /// @notice Harvest rewards from a strategy and transfer to strategist or dripper\n /// @param _strategy Address of the strategy to harvest\n function harvestAndTransfer(address _strategy) external {\n _harvestAndTransfer(_strategy);\n }\n\n /// @notice Harvest rewards from multiple strategies and transfer to strategist or dripper\n /// @param _strategies Array of strategy addresses to harvest\n function harvestAndTransfer(address[] calldata _strategies) external {\n for (uint256 i = 0; i < _strategies.length; i++) {\n _harvestAndTransfer(_strategies[i]);\n }\n }\n\n /// @notice Internal logic to harvest rewards from a strategy\n function _harvestAndTransfer(address _strategy) internal virtual {\n // Ensure strategy is supported\n require(supportedStrategies[_strategy], \"Strategy not supported\");\n\n // Store locally for some gas savings\n address _strategist = strategistAddr;\n address _dripper = dripper;\n\n // Harvest rewards\n IStrategy(_strategy).collectRewardTokens();\n\n // Cache reward tokens\n address[] memory rewardTokens = IStrategy(_strategy)\n .getRewardTokenAddresses();\n\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; i++) {\n // Cache balance\n address token = rewardTokens[i];\n uint256 balance = IERC20(token).balanceOf(address(this));\n if (balance > 0) {\n // Determine receiver\n address receiver = token == wrappedNativeToken\n ? _dripper\n : _strategist;\n require(receiver != address(0), \"Invalid receiver\");\n\n // Transfer to the Strategist or the Dripper\n IERC20(token).safeTransfer(receiver, balance);\n emit Harvested(_strategy, token, balance, receiver);\n }\n }\n }\n\n ////////////////////////////////////////////////////\n /// --- GOVERNANCE\n ////////////////////////////////////////////////////\n /// @notice Set supported strategy\n /// @param _strategy Address of the strategy\n /// @param _isSupported Boolean indicating if strategy is supported\n function setSupportedStrategy(address _strategy, bool _isSupported)\n external\n onlyGovernorOrStrategist\n {\n require(_strategy != address(0), \"Invalid strategy\");\n supportedStrategies[_strategy] = _isSupported;\n emit SupportedStrategyUpdated(_strategy, _isSupported);\n }\n\n /// @notice Transfer tokens to strategist\n /// @param _asset Address of the token\n /// @param _amount Amount of tokens to transfer\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernorOrStrategist\n {\n IERC20(_asset).safeTransfer(strategistAddr, _amount);\n }\n\n /// @notice Set the dripper address\n /// @param _dripper Address of the dripper\n function setDripper(address _dripper) external onlyGovernor {\n _setDripper(_dripper);\n }\n\n /// @notice Internal logic to set the dripper address\n function _setDripper(address _dripper) internal {\n require(_dripper != address(0), \"Invalid dripper\");\n dripper = _dripper;\n emit DripperUpdated(_dripper);\n }\n}\n" + }, + "contracts/harvest/OSonicHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SuperOETHHarvester } from \"./SuperOETHHarvester.sol\";\n\ncontract OSonicHarvester is SuperOETHHarvester {\n /// @param _wrappedNativeToken Address of the native Wrapped S (wS) token\n constructor(address _wrappedNativeToken)\n SuperOETHHarvester(_wrappedNativeToken)\n {}\n}\n" + }, + "contracts/harvest/SuperOETHHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHHarvesterSimple, IERC20, IStrategy, SafeERC20 } from \"./OETHHarvesterSimple.sol\";\n\ncontract SuperOETHHarvester is OETHHarvesterSimple {\n using SafeERC20 for IERC20;\n\n constructor(address _wrappedNativeToken)\n OETHHarvesterSimple(_wrappedNativeToken)\n {}\n\n /// @inheritdoc OETHHarvesterSimple\n function _harvestAndTransfer(address _strategy) internal virtual override {\n // Ensure strategy is supported\n require(supportedStrategies[_strategy], \"Strategy not supported\");\n\n address receiver = strategistAddr;\n require(receiver != address(0), \"Invalid receiver\");\n\n // Harvest rewards\n IStrategy(_strategy).collectRewardTokens();\n\n // Cache reward tokens\n address[] memory rewardTokens = IStrategy(_strategy)\n .getRewardTokenAddresses();\n\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; i++) {\n // Cache balance\n address token = rewardTokens[i];\n uint256 balance = IERC20(token).balanceOf(address(this));\n if (balance > 0) {\n // Transfer everything to the strategist\n IERC20(token).safeTransfer(receiver, balance);\n emit Harvested(_strategy, token, balance, receiver);\n }\n }\n }\n}\n" + }, + "contracts/interfaces/aerodrome/IAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ICLPool } from \"./ICLPool.sol\";\n\ninterface IAMOStrategy {\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth);\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth);\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n error OutsideExpectedTickRange(int24 currentTick);\n\n function governor() external view returns (address);\n\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) external;\n\n function clPool() external view returns (ICLPool);\n\n function vaultAddress() external view returns (address);\n\n function poolWethShareVarianceAllowed() external view returns (uint256);\n\n function poolWethShare() external view returns (uint256);\n\n function tokenId() external view returns (uint256);\n\n function withdrawAll() external;\n\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external;\n\n function setWithdrawLiquidityShare(uint128 share) external;\n\n function lowerTick() external view returns (int24);\n\n function upperTick() external view returns (int24);\n\n function getPoolX96Price() external view returns (uint160 _sqrtRatioX96);\n\n function sqrtRatioX96TickLower() external view returns (uint160);\n\n function sqrtRatioX96TickHigher() external view returns (uint160);\n\n function tickSpacing() external view returns (int24);\n\n function allowedWethShareStart() external view returns (uint256);\n\n function allowedWethShareEnd() external view returns (uint256);\n\n function claimGovernance() external;\n\n function transferGovernance(address _governor) external;\n\n function getPositionPrincipal()\n external\n view\n returns (uint256 _amountWeth, uint256 _amountOethb);\n}\n" + }, + "contracts/interfaces/aerodrome/ICLGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\ninterface ICLGauge {\n /// @notice Returns the claimable rewards for a given account and tokenId\n /// @dev Throws if account is not the position owner\n /// @dev pool.updateRewardsGrowthGlobal() needs to be called first, to return the correct claimable rewards\n /// @param account The address of the user\n /// @param tokenId The tokenId of the position\n /// @return The amount of claimable reward\n function earned(address account, uint256 tokenId)\n external\n view\n returns (uint256);\n\n /// @notice Retrieve rewards for all tokens owned by an account\n /// @dev Throws if not called by the voter\n /// @param account The account of the user\n function getReward(address account) external;\n\n /// @notice Retrieve rewards for a tokenId\n /// @dev Throws if not called by the position owner\n /// @param tokenId The tokenId of the position\n function getReward(uint256 tokenId) external;\n\n /// @notice Notifies gauge of gauge rewards.\n /// @param amount Amount of gauge rewards (emissions) to notify. Must be greater than 604_800.\n function notifyRewardAmount(uint256 amount) external;\n\n /// @dev Notifies gauge of gauge rewards without distributing its fees.\n /// Assumes gauge reward tokens is 18 decimals.\n /// If not 18 decimals, rewardRate may have rounding issues.\n /// @param amount Amount of gauge rewards (emissions) to notify. Must be greater than 604_800.\n function notifyRewardWithoutClaim(uint256 amount) external;\n\n /// @notice Used to deposit a CL position into the gauge\n /// @notice Allows the user to receive emissions instead of fees\n /// @param tokenId The tokenId of the position\n function deposit(uint256 tokenId) external;\n\n /// @notice Used to withdraw a CL position from the gauge\n /// @notice Allows the user to receive fees instead of emissions\n /// @notice Outstanding emissions will be collected on withdrawal\n /// @param tokenId The tokenId of the position\n function withdraw(uint256 tokenId) external;\n\n // /// @notice Fetch all tokenIds staked by a given account\n // /// @param depositor The address of the user\n // /// @return The tokenIds of the staked positions\n // function stakedValues(address depositor) external view returns (uint256[] memory);\n\n // /// @notice Fetch a staked tokenId by index\n // /// @param depositor The address of the user\n // /// @param index The index of the staked tokenId\n // /// @return The tokenId of the staked position\n // function stakedByIndex(address depositor, uint256 index) external view returns (uint256);\n\n // /// @notice Check whether a position is staked in the gauge by a certain user\n // /// @param depositor The address of the user\n // /// @param tokenId The tokenId of the position\n // /// @return Whether the position is staked in the gauge\n // function stakedContains(address depositor, uint256 tokenId) external view returns (bool);\n\n // /// @notice The amount of positions staked in the gauge by a certain user\n // /// @param depositor The address of the user\n // /// @return The amount of positions staked in the gauge\n // function stakedLength(address depositor) external view returns (uint256);\n\n function feesVotingReward() external view returns (address);\n}\n" + }, + "contracts/interfaces/aerodrome/ICLPool.sol": { + "content": "pragma solidity >=0.5.0;\n\n/// @title The interface for a CL Pool\n/// @notice A CL pool facilitates swapping and automated market making between any two assets that strictly conform\n/// to the ERC20 specification\n/// @dev The pool interface is broken up into many smaller pieces\ninterface ICLPool {\n function slot0()\n external\n view\n returns (\n uint160 sqrtPriceX96,\n int24 tick,\n uint16 observationIndex,\n uint16 observationCardinality,\n uint16 observationCardinalityNext,\n bool unlocked\n );\n\n /// @notice The first of the two tokens of the pool, sorted by address\n /// @return The token contract address\n function token0() external view returns (address);\n\n /// @notice The second of the two tokens of the pool, sorted by address\n /// @return The token contract address\n function token1() external view returns (address);\n\n function tickSpacing() external view returns (int24);\n\n /// @notice The gauge corresponding to this pool\n /// @return The gauge contract address\n function gauge() external view returns (address);\n\n /// @notice The currently in range liquidity available to the pool\n /// @dev This value has no relationship to the total liquidity across all ticks\n /// @dev This value includes staked liquidity\n function liquidity() external view returns (uint128);\n\n /// @notice Look up information about a specific tick in the pool\n /// @param tick The tick to look up\n /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or\n /// tick upper,\n /// liquidityNet how much liquidity changes when the pool price crosses the tick,\n /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,\n /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,\n /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick\n /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from\n /// the current tick,\n /// secondsOutside the seconds spent on the other side of the tick from the current tick,\n /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise\n /// equal to false.\n /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.\n /// In addition, these values are only relative and must be used only in comparison to previous snapshots for\n /// a specific position.\n function ticks(int24 tick)\n external\n view\n returns (\n uint128 liquidityGross,\n int128 liquidityNet,\n uint256 feeGrowthOutside0X128,\n uint256 feeGrowthOutside1X128,\n int56 tickCumulativeOutside,\n uint160 secondsPerLiquidityOutsideX128,\n uint32 secondsOutside,\n bool initialized\n );\n}\n" + }, + "contracts/interfaces/aerodrome/INonfungiblePositionManager.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title Non-fungible token for positions\n/// @notice Wraps CL positions in a non-fungible token interface which allows for them to be transferred\n/// and authorized.\n// slither-disable-start erc20-interface\ninterface INonfungiblePositionManager {\n /**\n * @dev See {IERC721-approve}.\n */\n function approve(address to, uint256 tokenId) external;\n\n /**\n * @dev See {IERC721-getApproved}.\n */\n function getApproved(uint256 tokenId) external returns (address);\n\n /**\n * @dev See {IERC721-ownerOf}.\n */\n function ownerOf(uint256 tokenId) external view returns (address);\n\n /// @notice Returns the position information associated with a given token ID.\n /// @dev Throws if the token ID is not valid.\n /// @param tokenId The ID of the token that represents the position\n /// @return nonce The nonce for permits\n /// @return operator The address that is approved for spending\n /// @return token0 The address of the token0 for a specific pool\n /// @return token1 The address of the token1 for a specific pool\n /// @return tickSpacing The tick spacing associated with the pool\n /// @return tickLower The lower end of the tick range for the position\n /// @return tickUpper The higher end of the tick range for the position\n /// @return liquidity The liquidity of the position\n /// @return feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position\n /// @return feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position\n /// @return tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation\n /// @return tokensOwed1 The uncollected amount of token1 owed to the position as of the last computation\n function positions(uint256 tokenId)\n external\n view\n returns (\n uint96 nonce,\n address operator,\n address token0,\n address token1,\n int24 tickSpacing,\n int24 tickLower,\n int24 tickUpper,\n uint128 liquidity,\n uint256 feeGrowthInside0LastX128,\n uint256 feeGrowthInside1LastX128,\n uint128 tokensOwed0,\n uint128 tokensOwed1\n );\n\n struct MintParams {\n address token0;\n address token1;\n int24 tickSpacing;\n int24 tickLower;\n int24 tickUpper;\n uint256 amount0Desired;\n uint256 amount1Desired;\n uint256 amount0Min;\n uint256 amount1Min;\n address recipient;\n uint256 deadline;\n uint160 sqrtPriceX96;\n }\n\n /// @notice Creates a new position wrapped in a NFT\n /// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized\n /// a method does not exist, i.e. the pool is assumed to be initialized.\n /// @param params The params necessary to mint a position, encoded as `MintParams` in calldata\n /// @return tokenId The ID of the token that represents the minted position\n /// @return liquidity The amount of liquidity for this position\n /// @return amount0 The amount of token0\n /// @return amount1 The amount of token1\n function mint(MintParams calldata params)\n external\n payable\n returns (\n uint256 tokenId,\n uint128 liquidity,\n uint256 amount0,\n uint256 amount1\n );\n\n struct IncreaseLiquidityParams {\n uint256 tokenId;\n uint256 amount0Desired;\n uint256 amount1Desired;\n uint256 amount0Min;\n uint256 amount1Min;\n uint256 deadline;\n }\n\n /// @notice Increases the amount of liquidity in a position, with tokens paid by the `msg.sender`\n /// @param params tokenId The ID of the token for which liquidity is being increased,\n /// amount0Desired The desired amount of token0 to be spent,\n /// amount1Desired The desired amount of token1 to be spent,\n /// amount0Min The minimum amount of token0 to spend, which serves as a slippage check,\n /// amount1Min The minimum amount of token1 to spend, which serves as a slippage check,\n /// deadline The time by which the transaction must be included to effect the change\n /// @return liquidity The new liquidity amount as a result of the increase\n /// @return amount0 The amount of token0 to acheive resulting liquidity\n /// @return amount1 The amount of token1 to acheive resulting liquidity\n function increaseLiquidity(IncreaseLiquidityParams calldata params)\n external\n payable\n returns (\n uint128 liquidity,\n uint256 amount0,\n uint256 amount1\n );\n\n struct DecreaseLiquidityParams {\n uint256 tokenId;\n uint128 liquidity;\n uint256 amount0Min;\n uint256 amount1Min;\n uint256 deadline;\n }\n\n /// @notice Decreases the amount of liquidity in a position and accounts it to the position\n /// @param params tokenId The ID of the token for which liquidity is being decreased,\n /// amount The amount by which liquidity will be decreased,\n /// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity,\n /// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity,\n /// deadline The time by which the transaction must be included to effect the change\n /// @return amount0 The amount of token0 accounted to the position's tokens owed\n /// @return amount1 The amount of token1 accounted to the position's tokens owed\n /// @dev The use of this function can cause a loss to users of the NonfungiblePositionManager\n /// @dev for tokens that have very high decimals.\n /// @dev The amount of tokens necessary for the loss is: 3.4028237e+38.\n /// @dev This is equivalent to 1e20 value with 18 decimals.\n function decreaseLiquidity(DecreaseLiquidityParams calldata params)\n external\n payable\n returns (uint256 amount0, uint256 amount1);\n\n struct CollectParams {\n uint256 tokenId;\n address recipient;\n uint128 amount0Max;\n uint128 amount1Max;\n }\n\n /// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient\n /// @notice Used to update staked positions before deposit and withdraw\n /// @param params tokenId The ID of the NFT for which tokens are being collected,\n /// recipient The account that should receive the tokens,\n /// amount0Max The maximum amount of token0 to collect,\n /// amount1Max The maximum amount of token1 to collect\n /// @return amount0 The amount of fees collected in token0\n /// @return amount1 The amount of fees collected in token1\n function collect(CollectParams calldata params)\n external\n payable\n returns (uint256 amount0, uint256 amount1);\n\n /// @notice Burns a token ID, which deletes it from the NFT contract. The token must have 0 liquidity and all tokens\n /// must be collected first.\n /// @param tokenId The ID of the token that is being burned\n function burn(uint256 tokenId) external payable;\n\n /// @notice Sets a new Token Descriptor\n /// @param _tokenDescriptor Address of the new Token Descriptor to be chosen\n function setTokenDescriptor(address _tokenDescriptor) external;\n\n /// @notice Sets a new Owner address\n /// @param _owner Address of the new Owner to be chosen\n function setOwner(address _owner) external;\n}\n// slither-disable-end erc20-interface\n" + }, + "contracts/interfaces/aerodrome/IQuoterV2.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title QuoterV2 Interface\n/// @notice Supports quoting the calculated amounts from exact input or exact output swaps.\n/// @notice For each pool also tells you the number of initialized ticks crossed and the sqrt price of the\n/// pool after the swap.\n/// @dev These functions are not marked view because they rely on calling non-view functions and reverting\n/// to compute the result. They are also not gas efficient and should not be called on-chain.\ninterface IQuoterV2 {\n /// @notice Returns the amount out received for a given exact input swap without executing the swap\n /// @param path The path of the swap, i.e. each token pair and the pool tick spacing\n /// @param amountIn The amount of the first token to swap\n /// @return amountOut The amount of the last token that would be received\n /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path\n /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for\n /// each pool in the path\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactInput(bytes memory path, uint256 amountIn)\n external\n returns (\n uint256 amountOut,\n uint160[] memory sqrtPriceX96AfterList,\n uint32[] memory initializedTicksCrossedList,\n uint256 gasEstimate\n );\n\n struct QuoteExactInputSingleParams {\n address tokenIn;\n address tokenOut;\n uint256 amountIn;\n int24 tickSpacing;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Returns the amount out received for a given exact input but for a swap of a single pool\n /// @param params The params for the quote, encoded as `QuoteExactInputSingleParams`\n /// tokenIn The token being swapped in\n /// tokenOut The token being swapped out\n /// tickSpacing The tick spacing of the token pool to consider for the pair\n /// amountIn The desired input amount\n /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap\n /// @return amountOut The amount of `tokenOut` that would be received\n /// @return sqrtPriceX96After The sqrt price of the pool after the swap\n /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactInputSingle(QuoteExactInputSingleParams memory params)\n external\n returns (\n uint256 amountOut,\n uint160 sqrtPriceX96After,\n uint32 initializedTicksCrossed,\n uint256 gasEstimate\n );\n\n /// @notice Returns the amount in required for a given exact output swap without executing the swap\n /// @param path The path of the swap, i.e. each token pair and the pool tick spacing.\n /// Path must be provided in reverse order\n /// @param amountOut The amount of the last token to receive\n /// @return amountIn The amount of first token required to be paid\n /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path\n /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for\n /// each pool in the path\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactOutput(bytes memory path, uint256 amountOut)\n external\n returns (\n uint256 amountIn,\n uint160[] memory sqrtPriceX96AfterList,\n uint32[] memory initializedTicksCrossedList,\n uint256 gasEstimate\n );\n\n struct QuoteExactOutputSingleParams {\n address tokenIn;\n address tokenOut;\n uint256 amount;\n int24 tickSpacing;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Returns the amount in required to receive the given exact output amount but for a swap of a single pool\n /// @param params The params for the quote, encoded as `QuoteExactOutputSingleParams`\n /// tokenIn The token being swapped in\n /// tokenOut The token being swapped out\n /// tickSpacing The tick spacing of the token pool to consider for the pair\n /// amountOut The desired output amount\n /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap\n /// @return amountIn The amount required as the input for the swap in order to receive `amountOut`\n /// @return sqrtPriceX96After The sqrt price of the pool after the swap\n /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactOutputSingle(QuoteExactOutputSingleParams memory params)\n external\n returns (\n uint256 amountIn,\n uint160 sqrtPriceX96After,\n uint32 initializedTicksCrossed,\n uint256 gasEstimate\n );\n}\n" + }, + "contracts/interfaces/aerodrome/ISugarHelper.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\npragma abicoder v2;\n\nimport { INonfungiblePositionManager } from \"./INonfungiblePositionManager.sol\";\n\ninterface ISugarHelper {\n struct PopulatedTick {\n int24 tick;\n uint160 sqrtRatioX96;\n int128 liquidityNet;\n uint128 liquidityGross;\n }\n\n ///\n /// Wrappers for LiquidityAmounts\n ///\n\n function getAmountsForLiquidity(\n uint160 sqrtRatioX96,\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96,\n uint128 liquidity\n ) external pure returns (uint256 amount0, uint256 amount1);\n\n function getLiquidityForAmounts(\n uint256 amount0,\n uint256 amount1,\n uint160 sqrtRatioX96,\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96\n ) external pure returns (uint128 liquidity);\n\n /// @notice Computes the amount of token0 for a given amount of token1 and price range\n /// @param amount1 Amount of token1 to estimate liquidity\n /// @param pool Address of the pool to be used\n /// @param sqrtRatioX96 A sqrt price representing the current pool prices\n /// @param tickLow Lower tick boundary\n /// @param tickLow Upper tick boundary\n /// @dev If the given pool address is not the zero address, will fetch `sqrtRatioX96` from pool\n /// @return amount0 Estimated amount of token0\n function estimateAmount0(\n uint256 amount1,\n address pool,\n uint160 sqrtRatioX96,\n int24 tickLow,\n int24 tickHigh\n ) external view returns (uint256 amount0);\n\n /// @notice Computes the amount of token1 for a given amount of token0 and price range\n /// @param amount0 Amount of token0 to estimate liquidity\n /// @param pool Address of the pool to be used\n /// @param sqrtRatioX96 A sqrt price representing the current pool prices\n /// @param tickLow Lower tick boundary\n /// @param tickLow Upper tick boundary\n /// @dev If the given pool address is not the zero address, will fetch `sqrtRatioX96` from pool\n /// @return amount1 Estimated amount of token1\n function estimateAmount1(\n uint256 amount0,\n address pool,\n uint160 sqrtRatioX96,\n int24 tickLow,\n int24 tickHigh\n ) external view returns (uint256 amount1);\n\n ///\n /// Wrappers for PositionValue\n ///\n\n function principal(\n INonfungiblePositionManager positionManager,\n uint256 tokenId,\n uint160 sqrtRatioX96\n ) external view returns (uint256 amount0, uint256 amount1);\n\n function fees(INonfungiblePositionManager positionManager, uint256 tokenId)\n external\n view\n returns (uint256 amount0, uint256 amount1);\n\n ///\n /// Wrappers for TickMath\n ///\n\n function getSqrtRatioAtTick(int24 tick)\n external\n pure\n returns (uint160 sqrtRatioX96);\n\n function getTickAtSqrtRatio(uint160 sqrtRatioX96)\n external\n pure\n returns (int24 tick);\n\n /// @notice Fetches Tick Data for all populated Ticks in given bitmaps\n /// @param pool Address of the pool from which to fetch data\n /// @param startTick Tick from which the first bitmap will be fetched\n /// @dev The number of bitmaps fetched by this function should always be `MAX_BITMAPS`,\n /// unless there are less than `MAX_BITMAPS` left to iterate through\n /// @return populatedTicks Array of all Populated Ticks in the provided bitmaps\n function getPopulatedTicks(address pool, int24 startTick)\n external\n view\n returns (PopulatedTick[] memory populatedTicks);\n}\n" + }, + "contracts/interfaces/aerodrome/ISwapRouter.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title Router token swapping functionality\n/// @notice Functions for swapping tokens via CL\ninterface ISwapRouter {\n struct ExactInputSingleParams {\n address tokenIn;\n address tokenOut;\n int24 tickSpacing;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInputSingle(ExactInputSingleParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactOutputSingleParams {\n address tokenIn;\n address tokenOut;\n int24 tickSpacing;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutputSingle(ExactOutputSingleParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n\n struct ExactOutputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutput(ExactOutputParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n}\n" + }, + "contracts/interfaces/balancer/IBalancerVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"../../utils/InitializableAbstractStrategy.sol\";\n\ninterface IBalancerVault {\n enum WeightedPoolJoinKind {\n INIT,\n EXACT_TOKENS_IN_FOR_BPT_OUT,\n TOKEN_IN_FOR_EXACT_BPT_OUT,\n ALL_TOKENS_IN_FOR_EXACT_BPT_OUT,\n ADD_TOKEN\n }\n\n enum WeightedPoolExitKind {\n EXACT_BPT_IN_FOR_ONE_TOKEN_OUT,\n EXACT_BPT_IN_FOR_TOKENS_OUT,\n BPT_IN_FOR_EXACT_TOKENS_OUT,\n REMOVE_TOKEN\n }\n\n /**\n * @dev Called by users to join a Pool, which transfers tokens from `sender` into the Pool's balance. This will\n * trigger custom Pool behavior, which will typically grant something in return to `recipient` - often tokenized\n * Pool shares.\n *\n * If the caller is not `sender`, it must be an authorized relayer for them.\n *\n * The `assets` and `maxAmountsIn` arrays must have the same length, and each entry indicates the maximum amount\n * to send for each asset. The amounts to send are decided by the Pool and not the Vault: it just enforces\n * these maximums.\n *\n * If joining a Pool that holds WETH, it is possible to send ETH directly: the Vault will do the wrapping. To enable\n * this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead of the\n * WETH address. Note that it is not possible to combine ETH and WETH in the same join. Any excess ETH will be sent\n * back to the caller (not the sender, which is important for relayers).\n *\n * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when\n * interacting with Pools that register and deregister tokens frequently. If sending ETH however, the array must be\n * sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the final\n * `assets` array might not be sorted. Pools with no registered tokens cannot be joined.\n *\n * If `fromInternalBalance` is true, the caller's Internal Balance will be preferred: ERC20 transfers will only\n * be made for the difference between the requested amount and Internal Balance (if any). Note that ETH cannot be\n * withdrawn from Internal Balance: attempting to do so will trigger a revert.\n *\n * This causes the Vault to call the `IBasePool.onJoinPool` hook on the Pool's contract, where Pools implement\n * their own custom logic. This typically requires additional information from the user (such as the expected number\n * of Pool shares). This can be encoded in the `userData` argument, which is ignored by the Vault and passed\n * directly to the Pool's contract, as is `recipient`.\n *\n * Emits a `PoolBalanceChanged` event.\n */\n function joinPool(\n bytes32 poolId,\n address sender,\n address recipient,\n JoinPoolRequest memory request\n ) external payable;\n\n struct JoinPoolRequest {\n address[] assets;\n uint256[] maxAmountsIn;\n bytes userData;\n bool fromInternalBalance;\n }\n\n /**\n * @dev Called by users to exit a Pool, which transfers tokens from the Pool's balance to `recipient`. This will\n * trigger custom Pool behavior, which will typically ask for something in return from `sender` - often tokenized\n * Pool shares. The amount of tokens that can be withdrawn is limited by the Pool's `cash` balance (see\n * `getPoolTokenInfo`).\n *\n * If the caller is not `sender`, it must be an authorized relayer for them.\n *\n * The `tokens` and `minAmountsOut` arrays must have the same length, and each entry in these indicates the minimum\n * token amount to receive for each token contract. The amounts to send are decided by the Pool and not the Vault:\n * it just enforces these minimums.\n *\n * If exiting a Pool that holds WETH, it is possible to receive ETH directly: the Vault will do the unwrapping. To\n * enable this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead\n * of the WETH address. Note that it is not possible to combine ETH and WETH in the same exit.\n *\n * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when\n * interacting with Pools that register and deregister tokens frequently. If receiving ETH however, the array must\n * be sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the\n * final `assets` array might not be sorted. Pools with no registered tokens cannot be exited.\n *\n * If `toInternalBalance` is true, the tokens will be deposited to `recipient`'s Internal Balance. Otherwise,\n * an ERC20 transfer will be performed. Note that ETH cannot be deposited to Internal Balance: attempting to\n * do so will trigger a revert.\n *\n * `minAmountsOut` is the minimum amount of tokens the user expects to get out of the Pool, for each token in the\n * `tokens` array. This array must match the Pool's registered tokens.\n *\n * This causes the Vault to call the `IBasePool.onExitPool` hook on the Pool's contract, where Pools implement\n * their own custom logic. This typically requires additional information from the user (such as the expected number\n * of Pool shares to return). This can be encoded in the `userData` argument, which is ignored by the Vault and\n * passed directly to the Pool's contract.\n *\n * Emits a `PoolBalanceChanged` event.\n */\n function exitPool(\n bytes32 poolId,\n address sender,\n address payable recipient,\n ExitPoolRequest memory request\n ) external;\n\n struct ExitPoolRequest {\n address[] assets;\n uint256[] minAmountsOut;\n bytes userData;\n bool toInternalBalance;\n }\n\n /**\n * @dev Returns a Pool's registered tokens, the total balance for each, and the latest block when *any* of\n * the tokens' `balances` changed.\n *\n * The order of the `tokens` array is the same order that will be used in `joinPool`, `exitPool`, as well as in all\n * Pool hooks (where applicable). Calls to `registerTokens` and `deregisterTokens` may change this order.\n *\n * If a Pool only registers tokens once, and these are sorted in ascending order, they will be stored in the same\n * order as passed to `registerTokens`.\n *\n * Total balances include both tokens held by the Vault and those withdrawn by the Pool's Asset Managers. These are\n * the amounts used by joins, exits and swaps. For a detailed breakdown of token balances, use `getPoolTokenInfo`\n * instead.\n */\n function getPoolTokens(bytes32 poolId)\n external\n view\n returns (\n IERC20[] memory tokens,\n uint256[] memory balances,\n uint256 lastChangeBlock\n );\n\n /**\n * @dev Performs a set of user balance operations, which involve Internal Balance (deposit, withdraw or transfer)\n * and plain ERC20 transfers using the Vault's allowance. This last feature is particularly useful for relayers, as\n * it lets integrators reuse a user's Vault allowance.\n *\n * For each operation, if the caller is not `sender`, it must be an authorized relayer for them.\n */\n function manageUserBalance(UserBalanceOp[] memory ops) external payable;\n\n struct UserBalanceOp {\n UserBalanceOpKind kind;\n address asset;\n uint256 amount;\n address sender;\n address payable recipient;\n }\n\n enum UserBalanceOpKind {\n DEPOSIT_INTERNAL,\n WITHDRAW_INTERNAL,\n TRANSFER_INTERNAL,\n TRANSFER_EXTERNAL\n }\n\n enum SwapKind {\n GIVEN_IN,\n GIVEN_OUT\n }\n\n struct SingleSwap {\n bytes32 poolId;\n SwapKind kind;\n address assetIn;\n address assetOut;\n uint256 amount;\n bytes userData;\n }\n\n struct FundManagement {\n address sender;\n bool fromInternalBalance;\n address payable recipient;\n bool toInternalBalance;\n }\n\n function swap(\n SingleSwap calldata singleSwap,\n FundManagement calldata funds,\n uint256 limit,\n uint256 deadline\n ) external returns (uint256 amountCalculated);\n\n function getPoolTokenInfo(bytes32 poolId, address token)\n external\n view\n returns (\n uint256 cash,\n uint256 managed,\n uint256 lastChangeBlock,\n address assetManager\n );\n}\n" + }, + "contracts/interfaces/balancer/IMetaStablePool.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IRateProvider } from \"./IRateProvider.sol\";\n\ninterface IMetaStablePool {\n function getRateProviders()\n external\n view\n returns (IRateProvider[] memory providers);\n}\n" + }, + "contracts/interfaces/balancer/IOracleWeightedPool.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// The three values that can be queried:\n//\n// - PAIR_PRICE: the price of the tokens in the Pool, expressed as the price of the second token in units of the\n// first token. For example, if token A is worth $2, and token B is worth $4, the pair price will be 2.0.\n// Note that the price is computed *including* the tokens decimals. This means that the pair price of a Pool with\n// DAI and USDC will be close to 1.0, despite DAI having 18 decimals and USDC 6.\n//\n// - BPT_PRICE: the price of the Pool share token (BPT), in units of the first token.\n// Note that the price is computed *including* the tokens decimals. This means that the BPT price of a Pool with\n// USDC in which BPT is worth $5 will be 5.0, despite the BPT having 18 decimals and USDC 6.\n//\n// - INVARIANT: the value of the Pool's invariant, which serves as a measure of its liquidity.\nenum Variable {\n PAIR_PRICE,\n BPT_PRICE,\n INVARIANT\n}\n\n/**\n * @dev Information for a Time Weighted Average query.\n *\n * Each query computes the average over a window of duration `secs` seconds that ended `ago` seconds ago. For\n * example, the average over the past 30 minutes is computed by settings secs to 1800 and ago to 0. If secs is 1800\n * and ago is 1800 as well, the average between 60 and 30 minutes ago is computed instead.\n */\nstruct OracleAverageQuery {\n Variable variable;\n uint256 secs;\n uint256 ago;\n}\n\ninterface IOracleWeightedPool {\n /**\n * @dev Returns the time average weighted price corresponding to each of `queries`. Prices are represented as 18\n * decimal fixed point values.\n */\n function getTimeWeightedAverage(OracleAverageQuery[] memory queries)\n external\n view\n returns (uint256[] memory results);\n}\n" + }, + "contracts/interfaces/balancer/IRateProvider.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity ^0.8.0;\n\ninterface IRateProvider {\n function getRate() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/chainlink/AggregatorV3Interface.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorV3Interface {\n function decimals() external view returns (uint8);\n\n function description() external view returns (string memory);\n\n function version() external view returns (uint256);\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(uint80 _roundId)\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n\n function latestRoundData()\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint40 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint40 validatorIndex\n ) external view returns (uint256 validatorBalance);\n\n function verifyPendingDepositsContainer(\n bytes32 beaconBlockRoot,\n bytes32 pendingDepositsContainerRoot,\n bytes calldata proof\n ) external view;\n\n function verifyPendingDeposit(\n bytes32 pendingDepositsContainerRoot,\n bytes32 pendingDepositRoot,\n bytes calldata proof,\n uint64 depositIndex\n ) external view;\n\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view returns (bool isEmptyDepositQueue);\n\n function merkleizePendingDeposit(\n bytes32 pubKeyHash,\n bytes calldata withdrawalCredentials,\n uint64 amountGwei,\n bytes calldata signature,\n uint64 slot\n ) external pure returns (bytes32 root);\n\n function merkleizeSignature(bytes calldata signature)\n external\n pure\n returns (bytes32 root);\n}\n" + }, + "contracts/interfaces/IBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBuyback {\n function swap() external;\n}\n" + }, + "contracts/interfaces/ICampaignRemoteManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICampaignRemoteManager {\n function createCampaign(\n CampaignCreationParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n function manageCampaign(\n CampaignManagementParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n function closeCampaign(\n CampaignClosingParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n struct CampaignCreationParams {\n uint256 chainId;\n address gauge;\n address manager;\n address rewardToken;\n uint8 numberOfPeriods;\n uint256 maxRewardPerVote;\n uint256 totalRewardAmount;\n address[] addresses;\n address hook;\n bool isWhitelist;\n }\n\n struct CampaignManagementParams {\n uint256 campaignId;\n address rewardToken;\n uint8 numberOfPeriods;\n uint256 totalRewardAmount;\n uint256 maxRewardPerVote;\n }\n\n struct CampaignClosingParams {\n uint256 campaignId;\n }\n}\n" + }, + "contracts/interfaces/IChildLiquidityGaugeFactory.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IChildLiquidityGaugeFactory {\n event DeployedGauge(\n address indexed _implementation,\n address indexed _lp_token,\n address indexed _deployer,\n bytes32 _salt,\n address _gauge\n );\n event Minted(\n address indexed _user,\n address indexed _gauge,\n uint256 _new_total\n );\n event TransferOwnership(address _old_owner, address _new_owner);\n event UpdateCallProxy(address _old_call_proxy, address _new_call_proxy);\n event UpdateImplementation(\n address _old_implementation,\n address _new_implementation\n );\n event UpdateManager(address _manager);\n event UpdateMirrored(address indexed _gauge, bool _mirrored);\n event UpdateRoot(address _factory, address _implementation);\n event UpdateVotingEscrow(\n address _old_voting_escrow,\n address _new_voting_escrow\n );\n\n function accept_transfer_ownership() external;\n\n function call_proxy() external view returns (address);\n\n function commit_transfer_ownership(address _future_owner) external;\n\n function crv() external view returns (address);\n\n function deploy_gauge(address _lp_token, bytes32 _salt)\n external\n returns (address);\n\n function deploy_gauge(\n address _lp_token,\n bytes32 _salt,\n address _manager\n ) external returns (address);\n\n function future_owner() external view returns (address);\n\n function gauge_data(address arg0) external view returns (uint256);\n\n function get_gauge(uint256 arg0) external view returns (address);\n\n function get_gauge_count() external view returns (uint256);\n\n function get_gauge_from_lp_token(address arg0)\n external\n view\n returns (address);\n\n function get_implementation() external view returns (address);\n\n function is_mirrored(address _gauge) external view returns (bool);\n\n function is_valid_gauge(address _gauge) external view returns (bool);\n\n function last_request(address _gauge) external view returns (uint256);\n\n function manager() external view returns (address);\n\n function mint(address _gauge) external;\n\n function mint_many(address[32] memory _gauges) external;\n\n function minted(address arg0, address arg1) external view returns (uint256);\n\n function owner() external view returns (address);\n\n function root_factory() external view returns (address);\n\n function root_implementation() external view returns (address);\n\n function set_call_proxy(address _new_call_proxy) external;\n\n function set_crv(address _crv) external;\n\n function set_implementation(address _implementation) external;\n\n function set_manager(address _new_manager) external;\n\n function set_mirrored(address _gauge, bool _mirrored) external;\n\n function set_root(address _factory, address _implementation) external;\n\n function set_voting_escrow(address _voting_escrow) external;\n\n function version() external view returns (string memory);\n\n function voting_escrow() external view returns (address);\n}\n" + }, + "contracts/interfaces/IComptroller.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IComptroller {\n // Claim all the COMP accrued by specific holders in specific markets for their supplies and/or borrows\n function claimComp(\n address[] memory holders,\n address[] memory cTokens,\n bool borrowers,\n bool suppliers\n ) external;\n\n function oracle() external view returns (address);\n}\n" + }, + "contracts/interfaces/IConsolidation.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IConsolidationSource {\n function confirmConsolidation()\n external\n returns (uint256 consolidationCount);\n}\n\ninterface IConsolidationTarget {\n function requestConsolidation(\n bytes32 lastSourcePubKeyHash,\n bytes32 targetPubKeyHash\n ) external;\n}\n" + }, + "contracts/interfaces/ICurveLiquidityGaugeV6.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveLiquidityGaugeV6 {\n event ApplyOwnership(address admin);\n event Approval(\n address indexed _owner,\n address indexed _spender,\n uint256 _value\n );\n event CommitOwnership(address admin);\n event Deposit(address indexed provider, uint256 value);\n event SetGaugeManager(address _gauge_manager);\n event Transfer(address indexed _from, address indexed _to, uint256 _value);\n event UpdateLiquidityLimit(\n address indexed user,\n uint256 original_balance,\n uint256 original_supply,\n uint256 working_balance,\n uint256 working_supply\n );\n event Withdraw(address indexed provider, uint256 value);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_reward(address _reward_token, address _distributor) external;\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function claim_rewards() external;\n\n function claim_rewards(address _addr) external;\n\n function claim_rewards(address _addr, address _receiver) external;\n\n function claimable_reward(address _user, address _reward_token)\n external\n view\n returns (uint256);\n\n function claimable_tokens(address addr) external returns (uint256);\n\n function claimed_reward(address _addr, address _token)\n external\n view\n returns (uint256);\n\n function decimals() external view returns (uint256);\n\n function decreaseAllowance(address _spender, uint256 _subtracted_value)\n external\n returns (bool);\n\n function deposit(uint256 _value) external;\n\n function deposit(uint256 _value, address _addr) external;\n\n function deposit(\n uint256 _value,\n address _addr,\n bool _claim_rewards\n ) external;\n\n function deposit_reward_token(address _reward_token, uint256 _amount)\n external;\n\n function deposit_reward_token(\n address _reward_token,\n uint256 _amount,\n uint256 _epoch\n ) external;\n\n function factory() external view returns (address);\n\n function future_epoch_time() external view returns (uint256);\n\n function increaseAllowance(address _spender, uint256 _added_value)\n external\n returns (bool);\n\n function inflation_rate() external view returns (uint256);\n\n function integrate_checkpoint() external view returns (uint256);\n\n function integrate_checkpoint_of(address arg0)\n external\n view\n returns (uint256);\n\n function integrate_fraction(address arg0) external view returns (uint256);\n\n function integrate_inv_supply(uint256 arg0) external view returns (uint256);\n\n function integrate_inv_supply_of(address arg0)\n external\n view\n returns (uint256);\n\n function is_killed() external view returns (bool);\n\n function kick(address addr) external;\n\n function lp_token() external view returns (address);\n\n function manager() external view returns (address);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function period() external view returns (int128);\n\n function period_timestamp(uint256 arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function reward_count() external view returns (uint256);\n\n function reward_integral_for(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function reward_tokens(uint256 arg0) external view returns (address);\n\n function rewards_receiver(address arg0) external view returns (address);\n\n function salt() external view returns (bytes32);\n\n function set_gauge_manager(address _gauge_manager) external;\n\n function set_killed(bool _is_killed) external;\n\n function set_reward_distributor(address _reward_token, address _distributor)\n external;\n\n function set_rewards_receiver(address _receiver) external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function user_checkpoint(address addr) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw(uint256 _value) external;\n\n function withdraw(uint256 _value, bool _claim_rewards) external;\n\n function working_balances(address arg0) external view returns (uint256);\n\n function working_supply() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/ICurveMinter.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveMinter {\n event Minted(address indexed recipient, address gauge, uint256 minted);\n\n function allowed_to_mint_for(address arg0, address arg1)\n external\n view\n returns (bool);\n\n function controller() external view returns (address);\n\n function mint(address gauge_addr) external;\n\n function mint_for(address gauge_addr, address _for) external;\n\n function mint_many(address[8] memory gauge_addrs) external;\n\n function minted(address arg0, address arg1) external view returns (uint256);\n\n function toggle_approve_mint(address minting_user) external;\n\n function token() external view returns (address);\n}\n" + }, + "contracts/interfaces/ICurveStableSwapNG.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveStableSwapNG {\n event AddLiquidity(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event ApplyNewFee(uint256 fee, uint256 offpeg_fee_multiplier);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event RampA(\n uint256 old_A,\n uint256 new_A,\n uint256 initial_time,\n uint256 future_time\n );\n event RemoveLiquidity(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 token_supply\n );\n event RemoveLiquidityImbalance(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event RemoveLiquidityOne(\n address indexed provider,\n int128 token_id,\n uint256 token_amount,\n uint256 coin_amount,\n uint256 token_supply\n );\n event SetNewMATime(uint256 ma_exp_time, uint256 D_ma_time);\n event StopRampA(uint256 A, uint256 t);\n event TokenExchange(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event TokenExchangeUnderlying(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event Transfer(\n address indexed sender,\n address indexed receiver,\n uint256 value\n );\n\n function A() external view returns (uint256);\n\n function A_precise() external view returns (uint256);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function D_ma_time() external view returns (uint256);\n\n function D_oracle() external view returns (uint256);\n\n function N_COINS() external view returns (uint256);\n\n function add_liquidity(uint256[] memory _amounts, uint256 _min_mint_amount)\n external\n returns (uint256);\n\n function add_liquidity(\n uint256[] memory _amounts,\n uint256 _min_mint_amount,\n address _receiver\n ) external returns (uint256);\n\n function admin_balances(uint256 arg0) external view returns (uint256);\n\n function admin_fee() external view returns (uint256);\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function balances(uint256 i) external view returns (uint256);\n\n function calc_token_amount(uint256[] memory _amounts, bool _is_deposit)\n external\n view\n returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _burn_amount, int128 i)\n external\n view\n returns (uint256);\n\n function coins(uint256 arg0) external view returns (address);\n\n function decimals() external view returns (uint8);\n\n function dynamic_fee(int128 i, int128 j) external view returns (uint256);\n\n function ema_price(uint256 i) external view returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external returns (uint256);\n\n function exchange_received(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external returns (uint256);\n\n function exchange_received(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external returns (uint256);\n\n function fee() external view returns (uint256);\n\n function future_A() external view returns (uint256);\n\n function future_A_time() external view returns (uint256);\n\n function get_balances() external view returns (uint256[] memory);\n\n function get_dx(\n int128 i,\n int128 j,\n uint256 dy\n ) external view returns (uint256);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n\n function get_p(uint256 i) external view returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function initial_A() external view returns (uint256);\n\n function initial_A_time() external view returns (uint256);\n\n function last_price(uint256 i) external view returns (uint256);\n\n function ma_exp_time() external view returns (uint256);\n\n function ma_last_time() external view returns (uint256);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function offpeg_fee_multiplier() external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function price_oracle(uint256 i) external view returns (uint256);\n\n function ramp_A(uint256 _future_A, uint256 _future_time) external;\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts\n ) external returns (uint256[] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts,\n address _receiver\n ) external returns (uint256[] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts,\n address _receiver,\n bool _claim_admin_fees\n ) external returns (uint256[] memory);\n\n function remove_liquidity_imbalance(\n uint256[] memory _amounts,\n uint256 _max_burn_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[] memory _amounts,\n uint256 _max_burn_amount,\n address _receiver\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received,\n address _receiver\n ) external returns (uint256);\n\n function salt() external view returns (bytes32);\n\n function set_ma_exp_time(uint256 _ma_exp_time, uint256 _D_ma_time) external;\n\n function set_new_fee(uint256 _new_fee, uint256 _new_offpeg_fee_multiplier)\n external;\n\n function stop_ramp_A() external;\n\n function stored_rates() external view returns (uint256[] memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw_admin_fees() external;\n}\n" + }, + "contracts/interfaces/ICurveXChainLiquidityGauge.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveXChainLiquidityGauge {\n event Approval(\n address indexed _owner,\n address indexed _spender,\n uint256 _value\n );\n event Deposit(address indexed provider, uint256 value);\n event SetGaugeManager(address _gauge_manager);\n event Transfer(address indexed _from, address indexed _to, uint256 _value);\n event UpdateLiquidityLimit(\n address indexed user,\n uint256 original_balance,\n uint256 original_supply,\n uint256 working_balance,\n uint256 working_supply\n );\n event Withdraw(address indexed provider, uint256 value);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_reward(address _reward_token, address _distributor) external;\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function claim_rewards() external;\n\n function claim_rewards(address _addr) external;\n\n function claim_rewards(address _addr, address _receiver) external;\n\n function claimable_reward(address _user, address _reward_token)\n external\n view\n returns (uint256);\n\n function claimable_tokens(address addr) external returns (uint256);\n\n function claimed_reward(address _addr, address _token)\n external\n view\n returns (uint256);\n\n function decimals() external view returns (uint256);\n\n function decreaseAllowance(address _spender, uint256 _subtracted_value)\n external\n returns (bool);\n\n function deposit(uint256 _value) external;\n\n function deposit(uint256 _value, address _addr) external;\n\n function deposit(\n uint256 _value,\n address _addr,\n bool _claim_rewards\n ) external;\n\n function deposit_reward_token(address _reward_token, uint256 _amount)\n external;\n\n function deposit_reward_token(\n address _reward_token,\n uint256 _amount,\n uint256 _epoch\n ) external;\n\n function factory() external view returns (address);\n\n function increaseAllowance(address _spender, uint256 _added_value)\n external\n returns (bool);\n\n function inflation_rate(uint256 arg0) external view returns (uint256);\n\n function initialize(\n address _lp_token,\n address _root,\n address _manager\n ) external;\n\n function integrate_checkpoint() external view returns (uint256);\n\n function integrate_checkpoint_of(address arg0)\n external\n view\n returns (uint256);\n\n function integrate_fraction(address arg0) external view returns (uint256);\n\n function integrate_inv_supply(int128 arg0) external view returns (uint256);\n\n function integrate_inv_supply_of(address arg0)\n external\n view\n returns (uint256);\n\n function is_killed() external view returns (bool);\n\n function lp_token() external view returns (address);\n\n function manager() external view returns (address);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function period() external view returns (int128);\n\n function period_timestamp(int128 arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function recover_remaining(address _reward_token) external;\n\n function reward_count() external view returns (uint256);\n\n function reward_integral_for(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function reward_remaining(address arg0) external view returns (uint256);\n\n function reward_tokens(uint256 arg0) external view returns (address);\n\n function rewards_receiver(address arg0) external view returns (address);\n\n function root_gauge() external view returns (address);\n\n function set_gauge_manager(address _gauge_manager) external;\n\n function set_killed(bool _is_killed) external;\n\n function set_manager(address _gauge_manager) external;\n\n function set_reward_distributor(address _reward_token, address _distributor)\n external;\n\n function set_rewards_receiver(address _receiver) external;\n\n function set_root_gauge(address _root) external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function update_voting_escrow() external;\n\n function user_checkpoint(address addr) external returns (bool);\n\n function version() external view returns (string memory);\n\n function voting_escrow() external view returns (address);\n\n function withdraw(uint256 _value) external;\n\n function withdraw(uint256 _value, bool _claim_rewards) external;\n\n function withdraw(\n uint256 _value,\n bool _claim_rewards,\n address _receiver\n ) external;\n\n function working_balances(address arg0) external view returns (uint256);\n\n function working_supply() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/ICVXLocker.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface ICVXLocker {\n function lock(\n address _account,\n uint256 _amount,\n uint256 _spendRatio\n ) external;\n\n function lockedBalanceOf(address _account) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/IDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IDripper {\n /// @notice How much funds have dripped out already and are currently\n // available to be sent to the vault.\n /// @return The amount that would be sent if a collect was called\n function availableFunds() external view returns (uint256);\n\n /// @notice Collect all dripped funds and send to vault.\n /// Recalculate new drip rate.\n function collect() external;\n\n /// @notice Collect all dripped funds, send to vault, recalculate new drip\n /// rate, and rebase mToken.\n function collectAndRebase() external;\n\n /// @notice Change the drip duration. Governor only.\n /// @param _durationSeconds the number of seconds to drip out the entire\n /// balance over if no collects were called during that time.\n function setDripDuration(uint256 _durationSeconds) external;\n\n /// @dev Transfer out ERC20 tokens held by the contract. Governor only.\n /// @param _asset ERC20 token address\n /// @param _amount amount to transfer\n function transferToken(address _asset, uint256 _amount) external;\n}\n" + }, + "contracts/interfaces/IEthUsdOracle.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IEthUsdOracle {\n /**\n * @notice Returns ETH price in USD.\n * @return Price in USD with 6 decimal digits.\n */\n function ethUsdPrice() external view returns (uint256);\n\n /**\n * @notice Returns token price in USD.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in USD with 6 decimal digits.\n */\n function tokUsdPrice(string calldata symbol)\n external\n view\n returns (uint256);\n\n /**\n * @notice Returns the asset price in ETH.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in ETH with 8 decimal digits.\n */\n function tokEthPrice(string calldata symbol)\n external\n view\n returns (uint256);\n}\n\ninterface IViewEthUsdOracle {\n /**\n * @notice Returns ETH price in USD.\n * @return Price in USD with 6 decimal digits.\n */\n function ethUsdPrice() external view returns (uint256);\n\n /**\n * @notice Returns token price in USD.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in USD with 6 decimal digits.\n */\n function tokUsdPrice(string calldata symbol)\n external\n view\n returns (uint256);\n\n /**\n * @notice Returns the asset price in ETH.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in ETH with 8 decimal digits.\n */\n function tokEthPrice(string calldata symbol)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/IFraxETHMinter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IFraxETHMinter {\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares);\n}\n" + }, + "contracts/interfaces/IGetExchangeRateToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IGetExchangeRateToken {\n function getExchangeRate() external view returns (uint256 _exchangeRate);\n}\n" + }, + "contracts/interfaces/IMinMaxOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IMinMaxOracle {\n //Assuming 8 decimals\n function priceMin(string calldata symbol) external view returns (uint256);\n\n function priceMax(string calldata symbol) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IMockVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"./IVault.sol\";\n\ninterface IMockVault is IVault {\n function outstandingWithdrawalsAmount() external view returns (uint256);\n\n function wethAvailable() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IOETHZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOETHZapper {\n function deposit() external payable returns (uint256);\n}\n" + }, + "contracts/interfaces/IOneInch.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/// 1Inch swap data\nstruct SwapDescription {\n IERC20 srcToken; // contract address of a token to sell\n IERC20 dstToken; // contract address of a token to buy\n address payable srcReceiver;\n address payable dstReceiver; // Receiver of destination currency. default: fromAddress\n uint256 amount;\n uint256 minReturnAmount;\n uint256 flags;\n}\n\n/// @title Interface for making arbitrary calls during swap\ninterface IAggregationExecutor {\n /// @notice propagates information about original msg.sender and executes arbitrary data\n function execute(address msgSender) external payable; // 0x4b64e492\n}\n\ninterface IOneInchRouter {\n /// @notice Performs a swap, delegating all calls encoded in `data` to `executor`.\n function swap(\n IAggregationExecutor executor,\n SwapDescription calldata desc,\n bytes calldata permit,\n bytes calldata data\n ) external returns (uint256 returnAmount, uint256 spentAmount);\n\n /// @notice Performs swap using Uniswap exchange. Wraps and unwraps ETH if required.\n function unoswapTo(\n address payable recipient,\n IERC20 srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) external payable returns (uint256 returnAmount);\n\n /// @notice Performs swap using Uniswap V3 exchange. Wraps and unwraps ETH if required.\n function uniswapV3SwapTo(\n address payable recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) external payable returns (uint256 returnAmount);\n}\n" + }, + "contracts/interfaces/IOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOracle {\n /**\n * @dev returns the asset price in USD, in 8 decimal digits.\n *\n * The version of priceProvider deployed for OETH has 18 decimal digits\n */\n function price(address asset) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IOUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOUSD {\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n function _totalSupply() external view returns (uint256);\n\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address _account) external view returns (uint256);\n\n function burn(address account, uint256 amount) external;\n\n function changeSupply(uint256 _newTotalSupply) external;\n\n function claimGovernance() external;\n\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256);\n\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n );\n\n function decimals() external view returns (uint8);\n\n function decreaseAllowance(address _spender, uint256 _subtractedValue)\n external\n returns (bool);\n\n function governor() external view returns (address);\n\n function increaseAllowance(address _spender, uint256 _addedValue)\n external\n returns (bool);\n\n function initialize(\n string memory _nameArg,\n string memory _symbolArg,\n address _vaultAddress\n ) external;\n\n function isGovernor() external view returns (bool);\n\n function isUpgraded(address) external view returns (uint256);\n\n function mint(address _account, uint256 _amount) external;\n\n function name() external view returns (string memory);\n\n function nonRebasingCreditsPerToken(address)\n external\n view\n returns (uint256);\n\n function nonRebasingSupply() external view returns (uint256);\n\n function rebaseOptIn() external;\n\n function rebaseOptOut() external;\n\n function rebaseState(address) external view returns (uint8);\n\n function rebasingCredits() external view returns (uint256);\n\n function rebasingCreditsHighres() external view returns (uint256);\n\n function rebasingCreditsPerToken() external view returns (uint256);\n\n function rebasingCreditsPerTokenHighres() external view returns (uint256);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function transferGovernance(address _newGovernor) external;\n\n function vaultAddress() external view returns (address);\n}\n" + }, + "contracts/interfaces/IPriceOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPriceOracle {\n /**\n * @dev returns the asset price in USD, 6 decimal digits.\n * Compatible with the Open Price Feed.\n */\n function price(string calldata symbol) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IRETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IRETH {\n function getEthValue(uint256 _rethAmount) external view returns (uint256);\n\n function getRethValue(uint256 _ethAmount) external view returns (uint256);\n\n function getExchangeRate() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function balanceOf(address account) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 amount) external returns (bool);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/ISafe.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ISafe {\n function execTransactionFromModule(\n address,\n uint256,\n bytes memory,\n uint8\n ) external returns (bool);\n}\n" + }, + "contracts/interfaces/ISfrxETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface ISfrxETH {\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 amount\n );\n event Deposit(\n address indexed caller,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n event NewRewardsCycle(uint32 indexed cycleEnd, uint256 rewardAmount);\n event Transfer(address indexed from, address indexed to, uint256 amount);\n event Withdraw(\n address indexed caller,\n address indexed receiver,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address spender, uint256 amount) external returns (bool);\n\n function asset() external view returns (address);\n\n function balanceOf(address) external view returns (uint256);\n\n function convertToAssets(uint256 shares) external view returns (uint256);\n\n function convertToShares(uint256 assets) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit(uint256 assets, address receiver)\n external\n returns (uint256 shares);\n\n function depositWithSignature(\n uint256 assets,\n address receiver,\n uint256 deadline,\n bool approveMax,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external returns (uint256 shares);\n\n function lastRewardAmount() external view returns (uint192);\n\n function lastSync() external view returns (uint32);\n\n function maxDeposit(address) external view returns (uint256);\n\n function maxMint(address) external view returns (uint256);\n\n function maxRedeem(address owner) external view returns (uint256);\n\n function maxWithdraw(address owner) external view returns (uint256);\n\n function mint(uint256 shares, address receiver)\n external\n returns (uint256 assets);\n\n function name() external view returns (string memory);\n\n function nonces(address) external view returns (uint256);\n\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function previewDeposit(uint256 assets) external view returns (uint256);\n\n function previewMint(uint256 shares) external view returns (uint256);\n\n function previewRedeem(uint256 shares) external view returns (uint256);\n\n function previewWithdraw(uint256 assets) external view returns (uint256);\n\n function pricePerShare() external view returns (uint256);\n\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets);\n\n function rewardsCycleEnd() external view returns (uint32);\n\n function rewardsCycleLength() external view returns (uint32);\n\n function symbol() external view returns (string memory);\n\n function syncRewards() external;\n\n function totalAssets() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address to, uint256 amount) external returns (bool);\n\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/ISwapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ISwapper {\n /**\n * @param fromAsset The token address of the asset being sold.\n * @param toAsset The token address of the asset being purchased.\n * @param fromAssetAmount The amount of assets being sold.\n * @param minToAssetAmmount The minimum amount of assets to be purchased.\n * @param data tx.data returned from 1Inch's /v5.0/1/swap API\n */\n function swap(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n}\n" + }, + "contracts/interfaces/ITimelock.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ITimelock {\n function delay() external view returns (uint256);\n\n function GRACE_PERIOD() external view returns (uint256);\n\n function acceptAdmin() external;\n\n function queuedTransactions(bytes32 hash) external view returns (bool);\n\n function queueTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external returns (bytes32);\n\n function cancelTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external;\n\n function executeTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external payable returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ITimelockController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ITimelockController {\n function grantRole(bytes32 role, address account) external;\n\n function revokeRole(bytes32 role, address account) external;\n\n function renounceRole(bytes32 role, address account) external;\n\n function hasRole(bytes32 role, address account)\n external\n view\n returns (bool);\n\n function executeBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt\n ) external payable;\n\n function scheduleBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt,\n uint256 delay\n ) external;\n\n function hashOperationBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt\n ) external view returns (bytes32);\n\n function isOperationDone(bytes32 opHash) external view returns (bool);\n\n function isOperationReady(bytes32 opHash) external view returns (bool);\n\n function isOperation(bytes32 opHash) external view returns (bool);\n\n function getMinDelay() external view returns (uint256);\n\n function updateDelay(uint256 newDelay) external;\n\n function CANCELLER_ROLE() external view returns (bytes32);\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/interfaces/IWstETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWstETH {\n /**\n * @notice Get amount of wstETH for a given amount of stETH\n * @param _stETHAmount amount of stETH\n * @return Amount of wstETH for a given stETH amount\n */\n function getWstETHByStETH(uint256 _stETHAmount)\n external\n view\n returns (uint256);\n\n /**\n * @notice Get amount of stETH for a given amount of wstETH\n * @param _wstETHAmount amount of wstETH\n * @return Amount of stETH for a given wstETH amount\n */\n function getStETHByWstETH(uint256 _wstETHAmount)\n external\n view\n returns (uint256);\n\n /**\n * @notice Get amount of stETH for a one wstETH\n * @return Amount of stETH for 1 wstETH\n */\n function stEthPerToken() external view returns (uint256);\n\n /**\n * @notice Get amount of wstETH for a one stETH\n * @return Amount of wstETH for a 1 stETH\n */\n function tokensPerStEth() external view returns (uint256);\n\n /**\n * @notice Exchanges stETH to wstETH\n * @param _stETHAmount amount of stETH to wrap in exchange for wstETH\n * @dev Requirements:\n * - `_stETHAmount` must be non-zero\n * - msg.sender must approve at least `_stETHAmount` stETH to this\n * contract.\n * - msg.sender must have at least `_stETHAmount` of stETH.\n * User should first approve _stETHAmount to the WstETH contract\n * @return Amount of wstETH user receives after wrap\n */\n function wrap(uint256 _stETHAmount) external returns (uint256);\n\n /**\n * @notice Exchanges wstETH to stETH\n * @param _wstETHAmount amount of wstETH to uwrap in exchange for stETH\n * @dev Requirements:\n * - `_wstETHAmount` must be non-zero\n * - msg.sender must have at least `_wstETHAmount` wstETH.\n * @return Amount of stETH user receives after unwrap\n */\n function unwrap(uint256 _wstETHAmount) external returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/compound/ICompoundOracle.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\ninterface ICompoundOracle {\n function getUnderlyingPrice(address) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/ILens.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\nimport \"./compound/ICompoundOracle.sol\";\nimport \"./IMorpho.sol\";\n\ninterface ILens {\n /// STORAGE ///\n\n function MAX_BASIS_POINTS() external view returns (uint256);\n\n function WAD() external view returns (uint256);\n\n function morpho() external view returns (IMorpho);\n\n function comptroller() external view returns (IComptroller);\n\n /// GENERAL ///\n\n function getTotalSupply()\n external\n view\n returns (\n uint256 p2pSupplyAmount,\n uint256 poolSupplyAmount,\n uint256 totalSupplyAmount\n );\n\n function getTotalBorrow()\n external\n view\n returns (\n uint256 p2pBorrowAmount,\n uint256 poolBorrowAmount,\n uint256 totalBorrowAmount\n );\n\n /// MARKETS ///\n\n function isMarketCreated(address _poolToken) external view returns (bool);\n\n function isMarketCreatedAndNotPaused(address _poolToken)\n external\n view\n returns (bool);\n\n function isMarketCreatedAndNotPausedNorPartiallyPaused(address _poolToken)\n external\n view\n returns (bool);\n\n function getAllMarkets()\n external\n view\n returns (address[] memory marketsCreated_);\n\n function getMainMarketData(address _poolToken)\n external\n view\n returns (\n uint256 avgSupplyRatePerBlock,\n uint256 avgBorrowRatePerBlock,\n uint256 p2pSupplyAmount,\n uint256 p2pBorrowAmount,\n uint256 poolSupplyAmount,\n uint256 poolBorrowAmount\n );\n\n function getAdvancedMarketData(address _poolToken)\n external\n view\n returns (\n uint256 p2pSupplyIndex,\n uint256 p2pBorrowIndex,\n uint256 poolSupplyIndex,\n uint256 poolBorrowIndex,\n uint32 lastUpdateBlockNumber,\n uint256 p2pSupplyDelta,\n uint256 p2pBorrowDelta\n );\n\n function getMarketConfiguration(address _poolToken)\n external\n view\n returns (\n address underlying,\n bool isCreated,\n bool p2pDisabled,\n bool isPaused,\n bool isPartiallyPaused,\n uint16 reserveFactor,\n uint16 p2pIndexCursor,\n uint256 collateralFactor\n );\n\n function getTotalMarketSupply(address _poolToken)\n external\n view\n returns (uint256 p2pSupplyAmount, uint256 poolSupplyAmount);\n\n function getTotalMarketBorrow(address _poolToken)\n external\n view\n returns (uint256 p2pBorrowAmount, uint256 poolBorrowAmount);\n\n /// INDEXES ///\n\n function getCurrentP2PSupplyIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentP2PBorrowIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentPoolIndexes(address _poolToken)\n external\n view\n returns (\n uint256 currentPoolSupplyIndex,\n uint256 currentPoolBorrowIndex\n );\n\n function getIndexes(address _poolToken, bool _computeUpdatedIndexes)\n external\n view\n returns (\n uint256 p2pSupplyIndex,\n uint256 p2pBorrowIndex,\n uint256 poolSupplyIndex,\n uint256 poolBorrowIndex\n );\n\n /// USERS ///\n\n function getEnteredMarkets(address _user)\n external\n view\n returns (address[] memory enteredMarkets);\n\n function getUserHealthFactor(\n address _user,\n address[] calldata _updatedMarkets\n ) external view returns (uint256);\n\n function getUserBalanceStates(\n address _user,\n address[] calldata _updatedMarkets\n )\n external\n view\n returns (\n uint256 collateralValue,\n uint256 debtValue,\n uint256 maxDebtValue\n );\n\n function getCurrentSupplyBalanceInOf(address _poolToken, address _user)\n external\n view\n returns (\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getCurrentBorrowBalanceInOf(address _poolToken, address _user)\n external\n view\n returns (\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getUserMaxCapacitiesForAsset(address _user, address _poolToken)\n external\n view\n returns (uint256 withdrawable, uint256 borrowable);\n\n function getUserHypotheticalBalanceStates(\n address _user,\n address _poolToken,\n uint256 _withdrawnAmount,\n uint256 _borrowedAmount\n ) external view returns (uint256 debtValue, uint256 maxDebtValue);\n\n function getUserLiquidityDataForAsset(\n address _user,\n address _poolToken,\n bool _computeUpdatedIndexes,\n ICompoundOracle _oracle\n ) external view returns (Types.AssetLiquidityData memory assetData);\n\n function isLiquidatable(address _user, address[] memory _updatedMarkets)\n external\n view\n returns (bool);\n\n function computeLiquidationRepayAmount(\n address _user,\n address _poolTokenBorrowed,\n address _poolTokenCollateral,\n address[] calldata _updatedMarkets\n ) external view returns (uint256 toRepay);\n\n /// RATES ///\n\n function getAverageSupplyRatePerBlock(address _poolToken)\n external\n view\n returns (\n uint256 avgSupplyRatePerBlock,\n uint256 p2pSupplyAmount,\n uint256 poolSupplyAmount\n );\n\n function getAverageBorrowRatePerBlock(address _poolToken)\n external\n view\n returns (\n uint256 avgBorrowRatePerBlock,\n uint256 p2pBorrowAmount,\n uint256 poolBorrowAmount\n );\n\n function getNextUserSupplyRatePerBlock(\n address _poolToken,\n address _user,\n uint256 _amount\n )\n external\n view\n returns (\n uint256 nextSupplyRatePerBlock,\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getNextUserBorrowRatePerBlock(\n address _poolToken,\n address _user,\n uint256 _amount\n )\n external\n view\n returns (\n uint256 nextBorrowRatePerBlock,\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getCurrentUserSupplyRatePerBlock(address _poolToken, address _user)\n external\n view\n returns (uint256);\n\n function getCurrentUserBorrowRatePerBlock(address _poolToken, address _user)\n external\n view\n returns (uint256);\n\n function getRatesPerBlock(address _poolToken)\n external\n view\n returns (\n uint256 p2pSupplyRate,\n uint256 p2pBorrowRate,\n uint256 poolSupplyRate,\n uint256 poolBorrowRate\n );\n\n /// REWARDS ///\n\n function getUserUnclaimedRewards(\n address[] calldata _poolTokens,\n address _user\n ) external view returns (uint256 unclaimedRewards);\n\n function getAccruedSupplierComp(\n address _supplier,\n address _poolToken,\n uint256 _balance\n ) external view returns (uint256);\n\n function getAccruedBorrowerComp(\n address _borrower,\n address _poolToken,\n uint256 _balance\n ) external view returns (uint256);\n\n function getCurrentCompSupplyIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentCompBorrowIndex(address _poolToken)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/IMorpho.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\nimport \"./Types.sol\";\nimport \"../IComptroller.sol\";\nimport \"./compound/ICompoundOracle.sol\";\n\n// prettier-ignore\ninterface IMorpho {\n function comptroller() external view returns (IComptroller);\n function supply(address _poolTokenAddress, address _onBehalf, uint256 _amount) external;\n function supply(address _poolTokenAddress, address _onBehalf, uint256 _amount, uint256 _maxGasForMatching) external;\n function withdraw(address _poolTokenAddress, uint256 _amount) external;\n function claimRewards(\n address[] calldata _cTokenAddresses,\n bool _tradeForMorphoToken\n ) external returns (uint256 claimedAmount);\n}\n" + }, + "contracts/interfaces/morpho/Types.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\n/// @title Types.\n/// @author Morpho Labs.\n/// @custom:contact security@morpho.xyz\n/// @dev Common types and structs used in Moprho contracts.\nlibrary Types {\n /// ENUMS ///\n\n enum PositionType {\n SUPPLIERS_IN_P2P,\n SUPPLIERS_ON_POOL,\n BORROWERS_IN_P2P,\n BORROWERS_ON_POOL\n }\n\n /// STRUCTS ///\n\n struct SupplyBalance {\n uint256 inP2P; // In supplier's peer-to-peer unit, a unit that grows in underlying value, to keep track of the interests earned by suppliers in peer-to-peer. Multiply by the peer-to-peer supply index to get the underlying amount.\n uint256 onPool; // In cToken. Multiply by the pool supply index to get the underlying amount.\n }\n\n struct BorrowBalance {\n uint256 inP2P; // In borrower's peer-to-peer unit, a unit that grows in underlying value, to keep track of the interests paid by borrowers in peer-to-peer. Multiply by the peer-to-peer borrow index to get the underlying amount.\n uint256 onPool; // In cdUnit, a unit that grows in value, to keep track of the debt increase when borrowers are on Compound. Multiply by the pool borrow index to get the underlying amount.\n }\n\n // Max gas to consume during the matching process for supply, borrow, withdraw and repay functions.\n struct MaxGasForMatching {\n uint64 supply;\n uint64 borrow;\n uint64 withdraw;\n uint64 repay;\n }\n\n struct Delta {\n uint256 p2pSupplyDelta; // Difference between the stored peer-to-peer supply amount and the real peer-to-peer supply amount (in pool supply unit).\n uint256 p2pBorrowDelta; // Difference between the stored peer-to-peer borrow amount and the real peer-to-peer borrow amount (in pool borrow unit).\n uint256 p2pSupplyAmount; // Sum of all stored peer-to-peer supply (in peer-to-peer supply unit).\n uint256 p2pBorrowAmount; // Sum of all stored peer-to-peer borrow (in peer-to-peer borrow unit).\n }\n\n struct AssetLiquidityData {\n uint256 collateralValue; // The collateral value of the asset.\n uint256 maxDebtValue; // The maximum possible debt value of the asset.\n uint256 debtValue; // The debt value of the asset.\n uint256 underlyingPrice; // The price of the token.\n uint256 collateralFactor; // The liquidation threshold applied on this token.\n }\n\n struct LiquidityData {\n uint256 collateralValue; // The collateral value.\n uint256 maxDebtValue; // The maximum debt value possible.\n uint256 debtValue; // The debt value.\n }\n\n // Variables are packed together to save gas (will not exceed their limit during Morpho's lifetime).\n struct LastPoolIndexes {\n uint32 lastUpdateBlockNumber; // The last time the peer-to-peer indexes were updated.\n uint112 lastSupplyPoolIndex; // Last pool supply index.\n uint112 lastBorrowPoolIndex; // Last pool borrow index.\n }\n\n struct MarketParameters {\n uint16 reserveFactor; // Proportion of the interest earned by users sent to the DAO for each market, in basis point (100% = 10 000). The value is set at market creation.\n uint16 p2pIndexCursor; // Position of the peer-to-peer rate in the pool's spread. Determine the weights of the weighted arithmetic average in the indexes computations ((1 - p2pIndexCursor) * r^S + p2pIndexCursor * r^B) (in basis point).\n }\n\n struct MarketStatus {\n bool isCreated; // Whether or not this market is created.\n bool isPaused; // Whether the market is paused or not (all entry points on Morpho are frozen; supply, borrow, withdraw, repay and liquidate).\n bool isPartiallyPaused; // Whether the market is partially paused or not (only supply and borrow are frozen).\n }\n}\n" + }, + "contracts/interfaces/plume/IFeeRegistry.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\ninterface IFeeRegistry {\n function registerFee(\n bool isTokenA,\n uint32 binId,\n uint256 binFeeInQuote\n ) external;\n}\n" + }, + "contracts/interfaces/plume/ILiquidityRegistry.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface ILiquidityRegistry {\n function notifyBinLiquidity(\n IMaverickV2Pool pool,\n uint256 tokenId,\n uint32 binId,\n uint256 currentBinLpBalance\n ) external;\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Factory.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IFeeRegistry } from \"./IFeeRegistry.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2Factory {\n error FactorAlreadyInitialized();\n error FactorNotInitialized();\n error FactoryInvalidTokenOrder(IERC20 _tokenA, IERC20 _tokenB);\n error FactoryInvalidFee();\n error FactoryInvalidKinds(uint8 kinds);\n error FactoryInvalidTickSpacing(uint256 tickSpacing);\n error FactoryInvalidLookback(uint256 lookback);\n error FactoryInvalidTokenDecimals(uint8 decimalsA, uint8 decimalsB);\n error FactoryPoolAlreadyExists(\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds,\n address accessor\n );\n error FactoryAccessorMustBeNonZero();\n error NotImplemented();\n\n event PoolCreated(\n IMaverickV2Pool poolAddress,\n uint8 protocolFeeRatio,\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n int32 activeTick,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds,\n address accessor\n );\n event SetFactoryProtocolFeeReceiver(address receiver);\n event SetFactoryProtocolFeeRegistry(IFeeRegistry registry);\n\n struct DeployParameters {\n uint64 feeAIn;\n uint64 feeBIn;\n uint32 lookback;\n int32 activeTick;\n uint64 tokenAScale;\n uint64 tokenBScale;\n // slot\n IERC20 tokenA;\n // slot\n IERC20 tokenB;\n // slot\n uint16 tickSpacing;\n uint8 options;\n address accessor;\n }\n\n /**\n * @notice Called by deployer library to initialize a pool.\n */\n function deployParameters()\n external\n view\n returns (\n uint64 feeAIn,\n uint64 feeBIn,\n uint32 lookback,\n int32 activeTick,\n uint64 tokenAScale,\n uint64 tokenBScale,\n // slot\n IERC20 tokenA,\n // slot\n IERC20 tokenB,\n // slot\n uint16 tickSpacing,\n uint8 options,\n address accessor\n );\n\n /**\n * @notice Create a new MaverickV2Pool with symmetric swap fees.\n * @param fee Fraction of the pool swap amount that is retained as an LP in\n * D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * E.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function create(\n uint64 fee,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external returns (IMaverickV2Pool);\n\n /**\n * @notice Create a new MaverickV2Pool.\n * @param feeAIn Fraction of the pool swap amount for tokenA-input swaps\n * that is retained as an LP in D18 scale.\n * @param feeBIn Fraction of the pool swap amount for tokenB-input swaps\n * that is retained as an LP in D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * e.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function create(\n uint64 feeAIn,\n uint64 feeBIn,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external returns (IMaverickV2Pool);\n\n /**\n * @notice Bool indicating whether the pool was deployed from this factory.\n */\n function isFactoryPool(IMaverickV2Pool pool) external view returns (bool);\n\n /**\n * @notice Address that receives the protocol fee\n */\n function protocolFeeReceiver() external view returns (address);\n\n /**\n * @notice Address notified on swaps of the protocol fee\n */\n function protocolFeeRegistry() external view returns (IFeeRegistry);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds\n ) external view returns (IMaverickV2Pool);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(\n IERC20 _tokenA,\n IERC20 _tokenB,\n uint256 startIndex,\n uint256 endIndex\n ) external view returns (IMaverickV2Pool[] memory pools);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(uint256 startIndex, uint256 endIndex)\n external\n view\n returns (IMaverickV2Pool[] memory pools);\n\n /**\n * @notice Count of permissionless pools.\n */\n function poolCount() external view returns (uint256 _poolCount);\n\n /**\n * @notice Count of pools for a given accessor and token pair. For\n * permissionless pools, pass `accessor = address(0)`.\n */\n function poolByTokenCount(\n IERC20 _tokenA,\n IERC20 _tokenB,\n address accessor\n ) external view returns (uint256 _poolCount);\n\n /**\n * @notice Get the current factory owner.\n */\n function owner() external view returns (address);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2LiquidityManager.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\nimport { IMaverickV2Position } from \"./IMaverickV2Position.sol\";\nimport { IMaverickV2PoolLens } from \"./IMaverickV2PoolLens.sol\";\n\ninterface IMaverickV2LiquidityManager {\n error LiquidityManagerNotFactoryPool();\n error LiquidityManagerNotTokenIdOwner();\n\n /**\n * @notice Maverick V2 NFT position contract that tracks NFT-based\n * liquditiy positions.\n */\n function position() external view returns (IMaverickV2Position);\n\n /**\n * @notice Create Maverick V2 pool. Function is a pass through to the pool\n * factory and is provided here so that is can be assembled as part of a\n * multicall transaction.\n */\n function createPool(\n uint64 fee,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external payable returns (IMaverickV2Pool pool);\n\n /**\n * @notice Add Liquidity position NFT for msg.sender by specifying\n * msg.sender's token index.\n * @dev Token index is different from tokenId.\n * On the Position NFT contract a user can own multiple NFT tokenIds and\n * these are indexes by an enumeration index which is the `index` input\n * here.\n *\n * See addLiquidity for a description of the add params.\n */\n function addPositionLiquidityToSenderByTokenIndex(\n IMaverickV2Pool pool,\n uint256 index,\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs\n )\n external\n payable\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds\n );\n\n /**\n * @notice Mint new tokenId in the Position NFt contract to msg.sender.\n * Both mints an NFT and adds liquidity to the pool that is held by the\n * NFT.\n */\n function mintPositionNftToSender(\n IMaverickV2Pool pool,\n bytes calldata packedSqrtPriceBreaks,\n bytes[] calldata packedArgs\n )\n external\n payable\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds,\n uint256 tokenId\n );\n\n /**\n * @notice Donates liqudity to a pool that is held by the position contract\n * and will never be retrievable. Can be used to start a pool and ensure\n * there will always be a base level of liquditiy in the pool.\n */\n function donateLiquidity(\n IMaverickV2Pool pool,\n IMaverickV2Pool.AddLiquidityParams memory args\n ) external payable;\n\n /**\n * @notice Packs sqrtPrice breaks array with this format: [length,\n * array[0], array[1],..., array[length-1]] where length is 1 byte.\n */\n function packUint88Array(uint88[] memory fullArray)\n external\n pure\n returns (bytes memory packedArray);\n\n /**\n * @notice Packs addLiquidity paramters array element-wise.\n */\n function packAddLiquidityArgsArray(\n IMaverickV2Pool.AddLiquidityParams[] memory args\n ) external pure returns (bytes[] memory argsPacked);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Pool.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\n\ninterface IMaverickV2Pool {\n error PoolZeroLiquidityAdded();\n error PoolMinimumLiquidityNotMet();\n error PoolLocked();\n error PoolInvalidFee();\n error PoolTicksNotSorted(uint256 index, int256 previousTick, int256 tick);\n error PoolTicksAmountsLengthMismatch(\n uint256 ticksLength,\n uint256 amountsLength\n );\n error PoolBinIdsAmountsLengthMismatch(\n uint256 binIdsLength,\n uint256 amountsLength\n );\n error PoolKindNotSupported(uint256 kinds, uint256 kind);\n error PoolInsufficientBalance(\n uint256 deltaLpAmount,\n uint256 accountBalance\n );\n error PoolReservesExceedMaximum(uint256 amount);\n error PoolValueExceedsBits(uint256 amount, uint256 bits);\n error PoolTickMaxExceeded(uint256 tick);\n error PoolMigrateBinFirst();\n error PoolCurrentTickBeyondSwapLimit(int32 startingTick);\n error PoolSenderNotAccessor(address sender_, address accessor);\n error PoolSenderNotFactory(address sender_, address accessor);\n error PoolFunctionNotImplemented();\n error PoolTokenNotSolvent(\n uint256 internalReserve,\n uint256 tokenBalance,\n IERC20 token\n );\n error PoolNoProtocolFeeReceiverSet();\n\n event PoolSwap(\n address sender,\n address recipient,\n SwapParams params,\n uint256 amountIn,\n uint256 amountOut\n );\n event PoolFlashLoan(\n address sender,\n address recipient,\n uint256 amountA,\n uint256 amountB\n );\n event PoolProtocolFeeCollected(IERC20 token, uint256 protocolFee);\n\n event PoolAddLiquidity(\n address sender,\n address recipient,\n uint256 subaccount,\n AddLiquidityParams params,\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] binIds\n );\n\n event PoolMigrateBinsUpStack(\n address sender,\n uint32 binId,\n uint32 maxRecursion\n );\n\n event PoolRemoveLiquidity(\n address sender,\n address recipient,\n uint256 subaccount,\n RemoveLiquidityParams params,\n uint256 tokenAOut,\n uint256 tokenBOut\n );\n\n event PoolTickState(int32 tick, uint256 reserveA, uint256 reserveB);\n event PoolTickBinUpdate(int32 tick, uint8 kind, uint32 binId);\n event PoolSqrtPrice(uint256 sqrtPrice);\n\n /**\n * @notice Tick state parameters.\n */\n struct TickState {\n uint128 reserveA;\n uint128 reserveB;\n uint128 totalSupply;\n uint32[4] binIdsByTick;\n }\n\n /**\n * @notice Tick data parameters.\n * @param currentReserveA Current reserve of token A.\n * @param currentReserveB Current reserve of token B.\n * @param currentLiquidity Current liquidity amount.\n */\n struct TickData {\n uint256 currentReserveA;\n uint256 currentReserveB;\n uint256 currentLiquidity;\n }\n\n /**\n * @notice Bin state parameters.\n * @param mergeBinBalance LP token balance that this bin possesses of the merge bin.\n * @param mergeId Bin ID of the bin that this bin has merged into.\n * @param totalSupply Total amount of LP tokens in this bin.\n * @param kind One of the 4 kinds (0=static, 1=right, 2=left, 3=both).\n * @param tick The lower price tick of the bin in its current state.\n * @param tickBalance Balance of the tick.\n */\n struct BinState {\n uint128 mergeBinBalance;\n uint128 tickBalance;\n uint128 totalSupply;\n uint8 kind;\n int32 tick;\n uint32 mergeId;\n }\n\n /**\n * @notice Parameters for swap.\n * @param amount Amount of the token that is either the input if exactOutput is false\n * or the output if exactOutput is true.\n * @param tokenAIn Boolean indicating whether tokenA is the input.\n * @param exactOutput Boolean indicating whether the amount specified is\n * the exact output amount (true).\n * @param tickLimit The furthest tick a swap will execute in. If no limit\n * is desired, value should be set to type(int32).max for a tokenAIn swap\n * and type(int32).min for a swap where tokenB is the input.\n */\n struct SwapParams {\n uint256 amount;\n bool tokenAIn;\n bool exactOutput;\n int32 tickLimit;\n }\n\n /**\n * @notice Parameters associated with adding liquidity.\n * @param kind One of the 4 kinds (0=static, 1=right, 2=left, 3=both).\n * @param ticks Array of ticks to add liquidity to.\n * @param amounts Array of bin LP amounts to add.\n */\n struct AddLiquidityParams {\n uint8 kind;\n int32[] ticks;\n uint128[] amounts;\n }\n\n /**\n * @notice Parameters for each bin that will have liquidity removed.\n * @param binIds Index array of the bins losing liquidity.\n * @param amounts Array of bin LP amounts to remove.\n */\n struct RemoveLiquidityParams {\n uint32[] binIds;\n uint128[] amounts;\n }\n\n /**\n * @notice State of the pool.\n * @param reserveA Pool tokenA balanceOf at end of last operation\n * @param reserveB Pool tokenB balanceOf at end of last operation\n * @param lastTwaD8 Value of log time weighted average price at last block.\n * Value is 8-decimal scale and is in the fractional tick domain. E.g. a\n * value of 12.3e8 indicates the TWAP was 3/10ths of the way into the 12th\n * tick.\n * @param lastLogPriceD8 Value of log price at last block. Value is\n * 8-decimal scale and is in the fractional tick domain. E.g. a value of\n * 12.3e8 indicates the price was 3/10ths of the way into the 12th tick.\n * @param lastTimestamp Last block.timestamp value in seconds for latest\n * swap transaction.\n * @param activeTick Current tick position that contains the active bins.\n * @param isLocked Pool isLocked, E.g., locked or unlocked; isLocked values\n * defined in Pool.sol.\n * @param binCounter Index of the last bin created.\n * @param protocolFeeRatioD3 Ratio of the swap fee that is kept for the\n * protocol.\n */\n struct State {\n uint128 reserveA;\n uint128 reserveB;\n int64 lastTwaD8;\n int64 lastLogPriceD8;\n uint40 lastTimestamp;\n int32 activeTick;\n bool isLocked;\n uint32 binCounter;\n uint8 protocolFeeRatioD3;\n }\n\n /**\n * @notice Internal data used for data passing between Pool and Bin code.\n */\n struct BinDelta {\n uint128 deltaA;\n uint128 deltaB;\n }\n\n /**\n * @notice 1-15 number to represent the active kinds.\n * @notice 0b0001 = static;\n * @notice 0b0010 = right;\n * @notice 0b0100 = left;\n * @notice 0b1000 = both;\n *\n * E.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function kinds() external view returns (uint8 _kinds);\n\n /**\n * @notice Pool swap fee for the given direction (A-in or B-in swap) in\n * 18-decimal format. E.g. 0.01e18 is a 1% swap fee.\n */\n function fee(bool tokenAIn) external view returns (uint256);\n\n /**\n * @notice TickSpacing of pool where 1.0001^tickSpacing is the bin width.\n */\n function tickSpacing() external view returns (uint256);\n\n /**\n * @notice Lookback period of pool in seconds.\n */\n function lookback() external view returns (uint256);\n\n /**\n * @notice Address of Pool accessor. This is Zero address for\n * permissionless pools.\n */\n function accessor() external view returns (address);\n\n /**\n * @notice Pool tokenA. Address of tokenA is such that tokenA < tokenB.\n */\n function tokenA() external view returns (IERC20);\n\n /**\n * @notice Pool tokenB.\n */\n function tokenB() external view returns (IERC20);\n\n /**\n * @notice Deploying factory of the pool and also contract that has ability\n * to set and collect protocol fees for the pool.\n */\n function factory() external view returns (IMaverickV2Factory);\n\n /**\n * @notice Most significant bit of scale value is a flag to indicate whether\n * tokenA has more or less than 18 decimals. Scale is used in conjuction\n * with Math.toScale/Math.fromScale functions to convert from token amounts\n * to D18 scale internal pool accounting.\n */\n function tokenAScale() external view returns (uint256);\n\n /**\n * @notice Most significant bit of scale value is a flag to indicate whether\n * tokenA has more or less than 18 decimals. Scale is used in conjuction\n * with Math.toScale/Math.fromScale functions to convert from token amounts\n * to D18 scale internal pool accounting.\n */\n function tokenBScale() external view returns (uint256);\n\n /**\n * @notice ID of bin at input tick position and kind.\n */\n function binIdByTickKind(int32 tick, uint256 kind)\n external\n view\n returns (uint32);\n\n /**\n * @notice Accumulated tokenA protocol fee.\n */\n function protocolFeeA() external view returns (uint128);\n\n /**\n * @notice Accumulated tokenB protocol fee.\n */\n function protocolFeeB() external view returns (uint128);\n\n /**\n * @notice Lending fee rate on flash loans.\n */\n function lendingFeeRateD18() external view returns (uint256);\n\n /**\n * @notice External function to get the current time-weighted average price.\n */\n function getCurrentTwa() external view returns (int256);\n\n /**\n * @notice External function to get the state of the pool.\n */\n function getState() external view returns (State memory);\n\n /**\n * @notice Return state of Bin at input binId.\n */\n function getBin(uint32 binId) external view returns (BinState memory bin);\n\n /**\n * @notice Return state of Tick at input tick position.\n */\n function getTick(int32 tick)\n external\n view\n returns (TickState memory tickState);\n\n /**\n * @notice Retrieves the balance of a user within a bin.\n * @param user The user's address.\n * @param subaccount The subaccount for the user.\n * @param binId The ID of the bin.\n */\n function balanceOf(\n address user,\n uint256 subaccount,\n uint32 binId\n ) external view returns (uint128 lpToken);\n\n /**\n * @notice Add liquidity to a pool. This function allows users to deposit\n * tokens into a liquidity pool.\n * @dev This function will call `maverickV2AddLiquidityCallback` on the\n * calling contract to collect the tokenA/tokenB payment.\n * @param recipient The account that will receive credit for the added liquidity.\n * @param subaccount The account that will receive credit for the added liquidity.\n * @param params Parameters containing the details for adding liquidity,\n * such as token types and amounts.\n * @param data Bytes information that gets passed to the callback.\n * @return tokenAAmount The amount of token A added to the pool.\n * @return tokenBAmount The amount of token B added to the pool.\n * @return binIds An array of bin IDs where the liquidity is stored.\n */\n function addLiquidity(\n address recipient,\n uint256 subaccount,\n AddLiquidityParams calldata params,\n bytes calldata data\n )\n external\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds\n );\n\n /**\n * @notice Removes liquidity from the pool.\n * @dev Liquidy can only be removed from a bin that is either unmerged or\n * has a mergeId of an unmerged bin. If a bin is merged more than one\n * level deep, it must be migrated up the merge stack to the root bin\n * before liquidity removal.\n * @param recipient The address to receive the tokens.\n * @param subaccount The subaccount for the recipient.\n * @param params The parameters for removing liquidity.\n * @return tokenAOut The amount of token A received.\n * @return tokenBOut The amount of token B received.\n */\n function removeLiquidity(\n address recipient,\n uint256 subaccount,\n RemoveLiquidityParams calldata params\n ) external returns (uint256 tokenAOut, uint256 tokenBOut);\n\n /**\n * @notice Migrate bins up the linked list of merged bins so that its\n * mergeId is the currrent active bin.\n * @dev Liquidy can only be removed from a bin that is either unmerged or\n * has a mergeId of an unmerged bin. If a bin is merged more than one\n * level deep, it must be migrated up the merge stack to the root bin\n * before liquidity removal.\n * @param binId The ID of the bin to migrate.\n * @param maxRecursion The maximum recursion depth for the migration.\n */\n function migrateBinUpStack(uint32 binId, uint32 maxRecursion) external;\n\n /**\n * @notice Swap tokenA/tokenB assets in the pool. The swap user has two\n * options for funding their swap.\n * - The user can push the input token amount to the pool before calling\n * the swap function. In order to avoid having the pool call the callback,\n * the user should pass a zero-length `data` bytes object with the swap\n * call.\n * - The user can send the input token amount to the pool when the pool\n * calls the `maverickV2SwapCallback` function on the calling contract.\n * That callback has input parameters that specify the token address of the\n * input token, the input and output amounts, and the bytes data sent to\n * the swap function.\n * @dev If the users elects to do a callback-based swap, the output\n * assets will be sent before the callback is called, allowing the user to\n * execute flash swaps. However, the pool does have reentrancy protection,\n * so a swapper will not be able to interact with the same pool again\n * while they are in the callback function.\n * @param recipient The address to receive the output tokens.\n * @param params Parameters containing the details of the swap\n * @param data Bytes information that gets passed to the callback.\n */\n function swap(\n address recipient,\n SwapParams memory params,\n bytes calldata data\n ) external returns (uint256 amountIn, uint256 amountOut);\n\n /**\n * @notice Loan tokenA/tokenB assets from the pool to recipient. The fee\n * rate of a loan is determined by `lendingFeeRateD18`, which is set at the\n * protocol level by the factory. This function calls\n * `maverickV2FlashLoanCallback` on the calling contract. At the end of\n * the callback, the caller must pay back the loan with fee (if there is a\n * fee).\n * @param recipient The address to receive the loaned tokens.\n * @param amountB Loan amount of tokenA sent to recipient.\n * @param amountB Loan amount of tokenB sent to recipient.\n * @param data Bytes information that gets passed to the callback.\n */\n function flashLoan(\n address recipient,\n uint256 amountA,\n uint256 amountB,\n bytes calldata data\n ) external;\n\n /**\n * @notice Distributes accumulated protocol fee to factory protocolFeeReceiver\n */\n function distributeFees(bool isTokenA)\n external\n returns (uint256 protocolFee, IERC20 token);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2PoolLens.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2PoolLens {\n error LensTargetPriceOutOfBounds(\n uint256 targetSqrtPrice,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n );\n error LensTooLittleLiquidity(\n uint256 relativeLiquidityAmount,\n uint256 deltaA,\n uint256 deltaB\n );\n error LensTargetingTokenWithNoDelta(\n bool targetIsA,\n uint256 deltaA,\n uint256 deltaB\n );\n\n /**\n * @notice Add liquidity slippage parameters for a distribution of liquidity.\n * @param pool Pool where liquidity is being added.\n * @param kind Bin kind; all bins must have the same kind in a given call\n * to addLiquidity.\n * @param ticks Array of tick values to add liquidity to.\n * @param relativeLiquidityAmounts Relative liquidity amounts for the\n * specified ticks. Liquidity in this case is not bin LP balance, it is\n * the bin liquidity as defined by liquidity = deltaA / (sqrt(upper) -\n * sqrt(lower)) or deltaB = liquidity / sqrt(lower) - liquidity /\n * sqrt(upper).\n * @param addSpec Slippage specification.\n */\n struct AddParamsViewInputs {\n IMaverickV2Pool pool;\n uint8 kind;\n int32[] ticks;\n uint128[] relativeLiquidityAmounts;\n AddParamsSpecification addSpec;\n }\n\n /**\n * @notice Multi-price add param specification.\n * @param slippageFactorD18 Max slippage allowed as a percent in D18 scale. e.g. 1% slippage is 0.01e18\n * @param numberOfPriceBreaksPerSide Number of price break values on either\n * side of current price.\n * @param targetAmount Target token contribution amount in tokenA if\n * targetIsA is true, otherwise this is the target amount for tokenB.\n * @param targetIsA Indicates if the target amount is for tokenA or tokenB\n */\n struct AddParamsSpecification {\n uint256 slippageFactorD18;\n uint256 numberOfPriceBreaksPerSide;\n uint256 targetAmount;\n bool targetIsA;\n }\n\n /**\n * @notice Specification for deriving create pool parameters. Creating a\n * pool in the liquidity manager has several steps:\n *\n * - Deploy pool\n * - Donate a small amount of initial liquidity in the activeTick\n * - Execute a small swap to set the pool price to the desired value\n * - Add liquidity\n *\n * In order to execute these steps, the caller must specify the parameters\n * of each step. The PoolLens has helper function to derive the values\n * used by the LiquidityManager, but this struct is the input to that\n * helper function and represents the core intent of the pool creator.\n *\n * @param fee Fraction of the pool swap amount that is retained as an LP in\n * D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * e.g. a pool with all 4 modes will have kinds = b1111 = 15\n * @param initialTargetB Amount of B to be donated to the pool after pool\n * create. This amount needs to be big enough to meet the minimum bin\n * liquidity.\n * @param sqrtPrice Target sqrt price of the pool.\n * @param kind Bin kind; all bins must have the same kind in a given call\n * to addLiquidity.\n * @param ticks Array of tick values to add liquidity to.\n * @param relativeLiquidityAmounts Relative liquidity amounts for the\n * specified ticks. Liquidity in this case is not bin LP balance, it is\n * the bin liquidity as defined by liquidity = deltaA / (sqrt(upper) -\n * sqrt(lower)) or deltaB = liquidity / sqrt(lower) - liquidity /\n * sqrt(upper).\n * @param targetAmount Target token contribution amount in tokenA if\n * targetIsA is true, otherwise this is the target amount for tokenB.\n * @param targetIsA Indicates if the target amount is for tokenA or tokenB\n */\n struct CreateAndAddParamsViewInputs {\n uint64 feeAIn;\n uint64 feeBIn;\n uint16 tickSpacing;\n uint32 lookback;\n IERC20 tokenA;\n IERC20 tokenB;\n int32 activeTick;\n uint8 kinds;\n // donate params\n uint256 initialTargetB;\n uint256 sqrtPrice;\n // add target\n uint8 kind;\n int32[] ticks;\n uint128[] relativeLiquidityAmounts;\n uint256 targetAmount;\n bool targetIsA;\n }\n\n struct Output {\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256[] deltaAs;\n uint256[] deltaBs;\n uint128[] deltaLpBalances;\n }\n\n struct Reserves {\n uint256 amountA;\n uint256 amountB;\n }\n\n struct BinPositionKinds {\n uint128[4] values;\n }\n\n struct PoolState {\n IMaverickV2Pool.TickState[] tickStateMapping;\n IMaverickV2Pool.BinState[] binStateMapping;\n BinPositionKinds[] binIdByTickKindMapping;\n IMaverickV2Pool.State state;\n Reserves protocolFees;\n }\n\n struct BoostedPositionSpecification {\n IMaverickV2Pool pool;\n uint32[] binIds;\n uint128[] ratios;\n uint8 kind;\n }\n\n struct CreateAndAddParamsInputs {\n uint64 feeAIn;\n uint64 feeBIn;\n uint16 tickSpacing;\n uint32 lookback;\n IERC20 tokenA;\n IERC20 tokenB;\n int32 activeTick;\n uint8 kinds;\n // donate params\n IMaverickV2Pool.AddLiquidityParams donateParams;\n // swap params\n uint256 swapAmount;\n // add params\n IMaverickV2Pool.AddLiquidityParams addParams;\n bytes[] packedAddParams;\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256 preAddReserveA;\n uint256 preAddReserveB;\n }\n\n struct TickDeltas {\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256[] deltaAs;\n uint256[] deltaBs;\n }\n\n /**\n * @notice Converts add parameter slippage specification into add\n * parameters. The return values are given in both raw format and as packed\n * values that can be used in the LiquidityManager contract.\n */\n function getAddLiquidityParams(AddParamsViewInputs memory params)\n external\n view\n returns (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint88[] memory sqrtPriceBreaks,\n IMaverickV2Pool.AddLiquidityParams[] memory addParams,\n IMaverickV2PoolLens.TickDeltas[] memory tickDeltas\n );\n\n /**\n * @notice Converts add parameter slippage specification and new pool\n * specification into CreateAndAddParamsInputs parameters that can be used in the\n * LiquidityManager contract.\n */\n function getCreatePoolAtPriceAndAddLiquidityParams(\n CreateAndAddParamsViewInputs memory params\n ) external view returns (CreateAndAddParamsInputs memory output);\n\n /**\n * @notice View function that provides information about pool ticks within\n * a tick radius from the activeTick. Ticks with no reserves are not\n * included in part o f the return array.\n */\n function getTicksAroundActive(IMaverickV2Pool pool, int32 tickRadius)\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates\n );\n\n /**\n * @notice View function that provides information about pool ticks within\n * a range. Ticks with no reserves are not included in part o f the return\n * array.\n */\n function getTicks(\n IMaverickV2Pool pool,\n int32 tickStart,\n int32 tickEnd\n )\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates\n );\n\n /**\n * @notice View function that provides information about pool ticks within\n * a range. Information returned includes all pool state needed to emulate\n * a swap off chain. Ticks with no reserves are not included in part o f\n * the return array.\n */\n function getTicksAroundActiveWLiquidity(\n IMaverickV2Pool pool,\n int32 tickRadius\n )\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates,\n uint256[] memory liquidities,\n uint256[] memory sqrtLowerTickPrices,\n uint256[] memory sqrtUpperTickPrices,\n IMaverickV2Pool.State memory poolState,\n uint256 sqrtPrice,\n uint256 feeAIn,\n uint256 feeBIn\n );\n\n /**\n * @notice View function that provides pool state information.\n */\n function getFullPoolState(\n IMaverickV2Pool pool,\n uint32 binStart,\n uint32 binEnd\n ) external view returns (PoolState memory poolState);\n\n /**\n * @notice View function that provides price and liquidity of a given tick.\n */\n function getTickSqrtPriceAndL(IMaverickV2Pool pool, int32 tick)\n external\n view\n returns (uint256 sqrtPrice, uint256 liquidity);\n\n /**\n * @notice Pool sqrt price.\n */\n function getPoolSqrtPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 sqrtPrice);\n\n /**\n * @notice Pool price.\n */\n function getPoolPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 price);\n\n /**\n * @notice Token scale of two tokens in a pool.\n */\n function tokenScales(IMaverickV2Pool pool)\n external\n view\n returns (uint256 tokenAScale, uint256 tokenBScale);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Position.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\nimport { ILiquidityRegistry } from \"./ILiquidityRegistry.sol\";\n\ninterface IMaverickV2Position {\n event PositionClearData(uint256 indexed tokenId);\n event PositionSetData(\n uint256 indexed tokenId,\n uint256 index,\n PositionPoolBinIds newData\n );\n event SetLpReward(ILiquidityRegistry lpReward);\n\n error PositionDuplicatePool(uint256 index, IMaverickV2Pool pool);\n error PositionNotFactoryPool();\n error PositionPermissionedLiquidityPool();\n\n struct PositionPoolBinIds {\n IMaverickV2Pool pool;\n uint32[] binIds;\n }\n\n struct PositionFullInformation {\n PositionPoolBinIds poolBinIds;\n uint256 amountA;\n uint256 amountB;\n uint256[] binAAmounts;\n uint256[] binBAmounts;\n int32[] ticks;\n uint256[] liquidities;\n }\n\n /**\n * @notice Factory that tracks lp rewards.\n */\n function lpReward() external view returns (ILiquidityRegistry);\n\n /**\n * @notice Pool factory.\n */\n function factory() external view returns (IMaverickV2Factory);\n\n /**\n * @notice Mint NFT that holds liquidity in a Maverick V2 Pool. To mint\n * liquidity to an NFT, add liquidity to bins in a pool where the\n * add liquidity recipient is this contract and the subaccount is the\n * tokenId. LiquidityManager can be used to simplify minting Position NFTs.\n */\n function mint(\n address recipient,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external returns (uint256 tokenId);\n\n /**\n * @notice Overwrites tokenId pool/binId information for a given data index.\n */\n function setTokenIdData(\n uint256 tokenId,\n uint256 index,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n\n /**\n * @notice Overwrites entire pool/binId data set for a given tokenId.\n */\n function setTokenIdData(uint256 tokenId, PositionPoolBinIds[] memory data)\n external;\n\n /**\n * @notice Append new pool/binIds data array to tokenId.\n */\n function appendTokenIdData(\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n\n /**\n * @notice Get array pool/binIds data for a given tokenId.\n */\n function getTokenIdData(uint256 tokenId)\n external\n view\n returns (PositionPoolBinIds[] memory);\n\n /**\n * @notice Get value from array of pool/binIds data for a given tokenId.\n */\n function getTokenIdData(uint256 tokenId, uint256 index)\n external\n view\n returns (PositionPoolBinIds memory);\n\n /**\n * @notice Length of array of pool/binIds data for a given tokenId.\n */\n function tokenIdDataLength(uint256 tokenId)\n external\n view\n returns (uint256 length);\n\n /**\n * @notice Remove liquidity from tokenId for a given pool. User can\n * specify arbitrary bins to remove from for their subaccount in the pool\n * even if those bins are not in the tokenIdData set.\n */\n function removeLiquidity(\n uint256 tokenId,\n address recipient,\n IMaverickV2Pool pool,\n IMaverickV2Pool.RemoveLiquidityParams memory params\n ) external returns (uint256 tokenAAmount, uint256 tokenBAmount);\n\n /**\n * @notice Remove liquidity from tokenId for a given pool to sender. User\n * can specify arbitrary bins to remove from for their subaccount in the\n * pool even if those bins are not in the tokenIdData set.\n */\n function removeLiquidityToSender(\n uint256 tokenId,\n IMaverickV2Pool pool,\n IMaverickV2Pool.RemoveLiquidityParams memory params\n ) external returns (uint256 tokenAAmount, uint256 tokenBAmount);\n\n /**\n * @notice NFT asset information for a given range of pool/binIds indexes.\n * This function only returns the liquidity in the pools/binIds stored as\n * part of the tokenIdData, but it is possible that the NFT has additional\n * liquidity in pools/binIds that have not been recorded.\n */\n function tokenIdPositionInformation(\n uint256 tokenId,\n uint256 startIndex,\n uint256 stopIndex\n ) external view returns (PositionFullInformation[] memory output);\n\n /**\n * @notice NFT asset information for a given pool/binIds index. This\n * function only returns the liquidity in the pools/binIds stored as part\n * of the tokenIdData, but it is possible that the NFT has additional\n * liquidity in pools/binIds that have not been recorded.\n */\n function tokenIdPositionInformation(uint256 tokenId, uint256 index)\n external\n view\n returns (PositionFullInformation memory output);\n\n /**\n * @notice Get remove parameters for removing a fractional part of the\n * liquidity owned by a given tokenId. The fractional factor to remove is\n * given by proporationD18 in 18-decimal scale.\n */\n function getRemoveParams(\n uint256 tokenId,\n uint256 index,\n uint256 proportionD18\n )\n external\n view\n returns (IMaverickV2Pool.RemoveLiquidityParams memory params);\n\n /**\n * @notice Register the bin balances in the nft with the LpReward contract.\n */\n function checkpointBinLpBalance(\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Quoter.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2Quoter {\n error QuoterInvalidSwap();\n error QuoterInvalidAddLiquidity();\n\n /**\n * @notice Calculates a swap on a MaverickV2Pool and returns the resulting\n * amount and estimated gas. The gas estimate is only a rough estimate and\n * may not match a swap's gas.\n * @param pool The MaverickV2Pool to swap on.\n * @param amount The input amount.\n * @param tokenAIn Indicates if token A is the input token.\n * @param exactOutput Indicates if the amount is the output amount (true)\n * or input amount (false). If the tickLimit is reached, the full value of\n * the exactOutput may not be returned because the pool will stop swapping\n * before the whole order is filled.\n * @param tickLimit The tick limit for the swap. Once the swap lands in\n * this tick, it will stop and return the output amount swapped up to that\n * tick.\n */\n function calculateSwap(\n IMaverickV2Pool pool,\n uint128 amount,\n bool tokenAIn,\n bool exactOutput,\n int32 tickLimit\n )\n external\n returns (\n uint256 amountIn,\n uint256 amountOut,\n uint256 gasEstimate\n );\n\n /**\n * @notice Calculates a multihop swap and returns the resulting amount and\n * estimated gas. The gas estimate is only a rough estimate and\n * may not match a swap's gas.\n * @param path The path of pools to swap through. Path is given by an\n * packed array of (pool, tokenAIn) tuples. So each step in the path is 160\n * + 8 = 168 bits of data. e.g. path = abi.encodePacked(pool1, true, pool2, false);\n * @param amount The input amount.\n * @param exactOutput A boolean indicating if exact output is required.\n */\n function calculateMultiHopSwap(\n bytes memory path,\n uint256 amount,\n bool exactOutput\n ) external returns (uint256 returnAmount, uint256 gasEstimate);\n\n /**\n * @notice Computes the token amounts required for a given set of\n * addLiquidity parameters. The gas estimate is only a rough estimate and\n * may not match a add's gas.\n */\n function calculateAddLiquidity(\n IMaverickV2Pool pool,\n IMaverickV2Pool.AddLiquidityParams calldata params\n )\n external\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 gasEstimate\n );\n\n /**\n * @notice Pool's sqrt price.\n */\n function poolSqrtPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 sqrtPrice);\n}\n" + }, + "contracts/interfaces/plume/IPoolDistributor.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.0;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IPoolDistributor {\n function rewardToken() external view returns (address);\n\n function claimLp(\n address recipient,\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds,\n uint256 epoch\n ) external returns (uint256 amount);\n}\n" + }, + "contracts/interfaces/plume/IVotingDistributor.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.0;\n\ninterface IVotingDistributor {\n function lastEpoch() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/poolBooster/IMerklDistributor.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IMerklDistributor {\n struct CampaignParameters {\n // POPULATED ONCE CREATED\n\n // ID of the campaign. This can be left as a null bytes32 when creating campaigns\n // on Merkl.\n bytes32 campaignId;\n // CHOSEN BY CAMPAIGN CREATOR\n\n // Address of the campaign creator, if marked as address(0), it will be overriden with the\n // address of the `msg.sender` creating the campaign\n address creator;\n // Address of the token used as a reward\n address rewardToken;\n // Amount of `rewardToken` to distribute across all the epochs\n // Amount distributed per epoch is `amount/numEpoch`\n uint256 amount;\n // Type of campaign\n uint32 campaignType;\n // Timestamp at which the campaign should start\n uint32 startTimestamp;\n // Duration of the campaign in seconds. Has to be a multiple of EPOCH = 3600\n uint32 duration;\n // Extra data to pass to specify the campaign\n bytes campaignData;\n }\n\n function createCampaign(CampaignParameters memory newCampaign)\n external\n returns (bytes32);\n\n function signAndCreateCampaign(\n CampaignParameters memory newCampaign,\n bytes memory _signature\n ) external returns (bytes32);\n\n function sign(bytes memory _signature) external;\n\n function rewardTokenMinAmounts(address _rewardToken)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/poolBooster/IPoolBoostCentralRegistry.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPoolBoostCentralRegistry {\n /**\n * @dev all the supported pool booster types are listed here. It is possible\n * to have multiple versions of the factory that supports the same type of\n * pool booster. Factories are immutable and this can happen when a factory\n * or related pool booster required code update.\n * e.g. \"PoolBoosterSwapxDouble\" & \"PoolBoosterSwapxDouble_v2\"\n */\n enum PoolBoosterType {\n // Supports bribing 2 contracts per pool. Appropriate for Ichi vault concentrated\n // liquidity pools where (which is expected in most/all cases) both pool gauges\n // require bribing.\n SwapXDoubleBooster,\n // Supports bribing a single contract per pool. Appropriate for Classic Stable &\n // Classic Volatile pools and Ichi vaults where only 1 side (1 of the 2 gauges)\n // needs bribing\n SwapXSingleBooster,\n // Supports bribing a single contract per pool. Appropriate for Metropolis pools\n MetropolisBooster,\n // Supports creating a Merkl campaign.\n MerklBooster\n }\n\n struct PoolBoosterEntry {\n address boosterAddress;\n address ammPoolAddress;\n PoolBoosterType boosterType;\n }\n\n event PoolBoosterCreated(\n address poolBoosterAddress,\n address ammPoolAddress,\n PoolBoosterType poolBoosterType,\n address factoryAddress\n );\n event PoolBoosterRemoved(address poolBoosterAddress);\n\n function emitPoolBoosterCreated(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n PoolBoosterType _boosterType\n ) external;\n\n function emitPoolBoosterRemoved(address _poolBoosterAddress) external;\n}\n" + }, + "contracts/interfaces/poolBooster/IPoolBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPoolBooster {\n event BribeExecuted(uint256 amount);\n\n /// @notice Execute the bribe action\n function bribe() external;\n}\n" + }, + "contracts/interfaces/poolBooster/ISwapXAlgebraBribe.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBribe {\n /// @notice Notify a bribe amount\n /// @dev Rewards are saved into NEXT EPOCH mapping.\n function notifyRewardAmount(address _rewardsToken, uint256 reward) external;\n}\n" + }, + "contracts/interfaces/sonic/INodeDriver.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\n\ninterface INodeDriver {\n /// Seal epoch. Called BEFORE epoch sealing made by the client itself.\n function sealEpoch(\n uint256[] calldata offlineTimes,\n uint256[] calldata offlineBlocks,\n uint256[] calldata uptimes,\n uint256[] calldata originatedTxsFee\n ) external;\n}\n" + }, + "contracts/interfaces/sonic/ISFC.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\n\n/**\n * @title Special Fee Contract for Sonic network\n * @notice The SFC maintains a list of validators and delegators and distributes rewards to them.\n * @custom:security-contact security@fantom.foundation\n */\ninterface ISFC {\n error StakeIsFullySlashed();\n\n event CreatedValidator(\n uint256 indexed validatorID,\n address indexed auth,\n uint256 createdEpoch,\n uint256 createdTime\n );\n event Delegated(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 amount\n );\n event Undelegated(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 indexed wrID,\n uint256 amount\n );\n event Withdrawn(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 indexed wrID,\n uint256 amount,\n uint256 penalty\n );\n event ClaimedRewards(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 rewards\n );\n event RestakedRewards(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 rewards\n );\n event BurntFTM(uint256 amount);\n event UpdatedSlashingRefundRatio(\n uint256 indexed validatorID,\n uint256 refundRatio\n );\n event RefundedSlashedLegacyDelegation(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 amount\n );\n\n event DeactivatedValidator(\n uint256 indexed validatorID,\n uint256 deactivatedEpoch,\n uint256 deactivatedTime\n );\n event ChangedValidatorStatus(uint256 indexed validatorID, uint256 status);\n event AnnouncedRedirection(address indexed from, address indexed to);\n\n function currentSealedEpoch() external view returns (uint256);\n\n function getEpochSnapshot(uint256 epoch)\n external\n view\n returns (\n uint256 endTime,\n uint256 endBlock,\n uint256 epochFee,\n uint256 baseRewardPerSecond,\n uint256 totalStake,\n uint256 totalSupply\n );\n\n function getStake(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getValidator(uint256 validatorID)\n external\n view\n returns (\n uint256 status,\n uint256 receivedStake,\n address auth,\n uint256 createdEpoch,\n uint256 createdTime,\n uint256 deactivatedTime,\n uint256 deactivatedEpoch\n );\n\n function getValidatorID(address auth) external view returns (uint256);\n\n function getValidatorPubkey(uint256 validatorID)\n external\n view\n returns (bytes memory);\n\n function pubkeyAddressvalidatorID(address pubkeyAddress)\n external\n view\n returns (uint256);\n\n function getWithdrawalRequest(\n address delegator,\n uint256 validatorID,\n uint256 wrID\n )\n external\n view\n returns (\n uint256 epoch,\n uint256 time,\n uint256 amount\n );\n\n function isOwner() external view returns (bool);\n\n function lastValidatorID() external view returns (uint256);\n\n function minGasPrice() external view returns (uint256);\n\n function owner() external view returns (address);\n\n function renounceOwnership() external;\n\n function slashingRefundRatio(uint256 validatorID)\n external\n view\n returns (uint256);\n\n function stashedRewardsUntilEpoch(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function totalActiveStake() external view returns (uint256);\n\n function totalStake() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function transferOwnership(address newOwner) external;\n\n function treasuryAddress() external view returns (address);\n\n function version() external pure returns (bytes3);\n\n function currentEpoch() external view returns (uint256);\n\n function updateConstsAddress(address v) external;\n\n function constsAddress() external view returns (address);\n\n function getEpochValidatorIDs(uint256 epoch)\n external\n view\n returns (uint256[] memory);\n\n function getEpochReceivedStake(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochAccumulatedRewardPerToken(\n uint256 epoch,\n uint256 validatorID\n ) external view returns (uint256);\n\n function getEpochAccumulatedUptime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochAverageUptime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint32);\n\n function getEpochAccumulatedOriginatedTxsFee(\n uint256 epoch,\n uint256 validatorID\n ) external view returns (uint256);\n\n function getEpochOfflineTime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochOfflineBlocks(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochEndBlock(uint256 epoch) external view returns (uint256);\n\n function rewardsStash(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function createValidator(bytes calldata pubkey) external payable;\n\n function getSelfStake(uint256 validatorID) external view returns (uint256);\n\n function delegate(uint256 validatorID) external payable;\n\n function undelegate(\n uint256 validatorID,\n uint256 wrID,\n uint256 amount\n ) external;\n\n function isSlashed(uint256 validatorID) external view returns (bool);\n\n function withdraw(uint256 validatorID, uint256 wrID) external;\n\n function deactivateValidator(uint256 validatorID, uint256 status) external;\n\n function pendingRewards(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function stashRewards(address delegator, uint256 validatorID) external;\n\n function claimRewards(uint256 validatorID) external;\n\n function restakeRewards(uint256 validatorID) external;\n\n function updateSlashingRefundRatio(uint256 validatorID, uint256 refundRatio)\n external;\n\n function updateTreasuryAddress(address v) external;\n\n function burnFTM(uint256 amount) external;\n\n function sealEpoch(\n uint256[] calldata offlineTime,\n uint256[] calldata offlineBlocks,\n uint256[] calldata uptimes,\n uint256[] calldata originatedTxsFee\n ) external;\n\n function sealEpochValidators(uint256[] calldata nextValidatorIDs) external;\n\n function initialize(\n uint256 sealedEpoch,\n uint256 _totalSupply,\n address nodeDriver,\n address consts,\n address _owner\n ) external;\n\n function setGenesisValidator(\n address auth,\n uint256 validatorID,\n bytes calldata pubkey,\n uint256 createdTime\n ) external;\n\n function setGenesisDelegation(\n address delegator,\n uint256 validatorID,\n uint256 stake\n ) external;\n\n function updateStakeSubscriberAddress(address v) external;\n\n function stakeSubscriberAddress() external view returns (address);\n\n function setRedirectionAuthorizer(address v) external;\n\n function announceRedirection(address to) external;\n\n function initiateRedirection(address from, address to) external;\n\n function redirect(address to) external;\n}\n" + }, + "contracts/interfaces/sonic/ISwapXGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IGauge {\n function owner() external view returns (address);\n\n function TOKEN() external view returns (address);\n\n function DISTRIBUTION() external view returns (address);\n\n function balanceOf(address account) external view returns (uint256);\n\n function claimFees() external returns (uint256 claimed0, uint256 claimed1);\n\n function deposit(uint256 amount) external;\n\n function depositAll() external;\n\n function earned(address account) external view returns (uint256);\n\n function getReward() external;\n\n function getReward(address _user) external;\n\n function isForPair() external view returns (bool);\n\n function lastTimeRewardApplicable() external view returns (uint256);\n\n function lastUpdateTime() external view returns (uint256);\n\n function notifyRewardAmount(address token, uint256 reward) external;\n\n function periodFinish() external view returns (uint256);\n\n function rewardForDuration() external view returns (uint256);\n\n function rewardPerToken() external view returns (uint256);\n\n function rewardPerTokenStored() external view returns (uint256);\n\n function rewardRate() external view returns (uint256);\n\n function rewardToken() external view returns (address);\n\n function rewards(address) external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function userRewardPerTokenPaid(address) external view returns (uint256);\n\n function withdraw(uint256 amount) external;\n\n function withdrawAll() external;\n\n function withdrawAllAndHarvest() external;\n\n function withdrawExcess(address token, uint256 amount) external;\n\n function emergency() external returns (bool);\n\n function emergencyWithdraw() external;\n\n function activateEmergencyMode() external;\n\n function stopEmergencyMode() external;\n}\n" + }, + "contracts/interfaces/sonic/ISwapXPair.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IPair {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n\n event Mint(address indexed sender, uint256 amount0, uint256 amount1);\n event Burn(\n address indexed sender,\n uint256 amount0,\n uint256 amount1,\n address indexed to\n );\n event Swap(\n address indexed sender,\n uint256 amount0In,\n uint256 amount1In,\n uint256 amount0Out,\n uint256 amount1Out,\n address indexed to\n );\n event Claim(\n address indexed sender,\n address indexed recipient,\n uint256 amount0,\n uint256 amount1\n );\n\n function metadata()\n external\n view\n returns (\n uint256 dec0,\n uint256 dec1,\n uint256 r0,\n uint256 r1,\n bool st,\n address t0,\n address t1\n );\n\n function claimFees() external returns (uint256, uint256);\n\n function tokens() external view returns (address, address);\n\n function token0() external view returns (address);\n\n function token1() external view returns (address);\n\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function swap(\n uint256 amount0Out,\n uint256 amount1Out,\n address to,\n bytes calldata data\n ) external;\n\n function burn(address to)\n external\n returns (uint256 amount0, uint256 amount1);\n\n function mint(address to) external returns (uint256 liquidity);\n\n function getReserves()\n external\n view\n returns (\n uint256 _reserve0,\n uint256 _reserve1,\n uint256 _blockTimestampLast\n );\n\n function getAmountOut(uint256, address) external view returns (uint256);\n\n // ERC20 methods\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n\n function totalSupply() external view returns (uint256);\n\n function balanceOf(address) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 value) external returns (bool);\n\n function claimable0(address _user) external view returns (uint256);\n\n function claimable1(address _user) external view returns (uint256);\n\n function isStable() external view returns (bool);\n\n function skim(address to) external;\n\n function sync() external;\n}\n" + }, + "contracts/interfaces/sonic/IVoterV3.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IVoterV3 {\n /// @notice create a gauge\n function createGauge(address _pool, uint256 _gaugeType)\n external\n returns (\n address _gauge,\n address _internal_bribe,\n address _external_bribe\n );\n\n function gauges(address _pool) external view returns (address _gauge);\n}\n" + }, + "contracts/interfaces/sonic/IWrappedSonic.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWrappedSonic {\n event Deposit(address indexed account, uint256 value);\n event Withdrawal(address indexed account, uint256 value);\n event Transfer(address indexed from, address indexed to, uint256 value);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 value) external returns (bool);\n\n function balanceOf(address account) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function depositFor(address account) external payable returns (bool);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address to, uint256 value) external returns (bool);\n\n function transferFrom(\n address from,\n address to,\n uint256 value\n ) external returns (bool);\n\n function withdraw(uint256 value) external;\n\n function withdrawTo(address account, uint256 value) external returns (bool);\n}\n" + }, + "contracts/interfaces/Tether.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// slither-disable-start erc20-interface\ninterface Tether {\n function transfer(address to, uint256 value) external;\n\n function transferFrom(\n address from,\n address to,\n uint256 value\n ) external;\n\n function balanceOf(address) external view returns (uint256);\n\n function approve(address _spender, uint256 _value) external;\n}\n// slither-disable-end erc20-interface\n" + }, + "contracts/interfaces/uniswap/IUniswapUniversalRouter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapUniversalRouter {\n /// @notice Executes encoded commands along with provided inputs. Reverts if deadline has expired.\n /// @param commands A set of concatenated commands, each 1 byte in length\n /// @param inputs An array of byte strings containing abi encoded inputs for each command\n /// @param deadline The deadline by which the transaction must be executed\n function execute(\n bytes calldata commands,\n bytes[] calldata inputs,\n uint256 deadline\n ) external payable;\n\n function execute(bytes calldata commands, bytes[] calldata inputs)\n external\n payable;\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV2Pair.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapV2Pair {\n function token0() external view returns (address);\n\n function token1() external view returns (address);\n\n function getReserves()\n external\n view\n returns (\n uint112 reserve0,\n uint112 reserve1,\n uint32 blockTimestampLast\n );\n\n function price0CumulativeLast() external view returns (uint256);\n\n function price1CumulativeLast() external view returns (uint256);\n\n function sync() external;\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV2Router02.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapV2Router {\n function WETH() external pure returns (address);\n\n function swapExactTokensForTokens(\n uint256 amountIn,\n uint256 amountOutMin,\n address[] calldata path,\n address to,\n uint256 deadline\n ) external returns (uint256[] memory amounts);\n\n function addLiquidity(\n address tokenA,\n address tokenB,\n uint256 amountADesired,\n uint256 amountBDesired,\n uint256 amountAMin,\n uint256 amountBMin,\n address to,\n uint256 deadline\n )\n external\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 liquidity\n );\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV3Router.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// -- Solididy v0.5.x compatible interface\ninterface IUniswapV3Router {\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n}\n" + }, + "contracts/mocks/beacon/EnhancedBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"../../beacon/BeaconProofsLib.sol\";\nimport { BeaconProofs } from \"../../beacon/BeaconProofs.sol\";\n\ncontract EnhancedBeaconProofs is BeaconProofs {\n function concatGenIndices(\n uint256 index1,\n uint256 height2,\n uint256 index2\n ) external pure returns (uint256 genIndex) {\n return BeaconProofsLib.concatGenIndices(index1, height2, index2);\n }\n}\n" + }, + "contracts/mocks/beacon/ExecutionLayerConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { GeneralPurposeToConsensusLayerRequest } from \"./GeneralPurposeToConsensusLayerRequest.sol\";\n\ncontract ExecutionLayerConsolidation is GeneralPurposeToConsensusLayerRequest {\n event ConsolidationRequestIssued(bytes sourceKey, bytes targetKey);\n bytes public lastSource;\n bytes public lastTarget;\n\n function handleRequest(bytes calldata data) internal override {\n // parameters should consist of twice the 48 bytes for 2 public keys\n require(data.length == 96, \"Invalid Consolidation data\");\n lastSource = data[:48];\n lastTarget = data[48:];\n\n emit ConsolidationRequestIssued(lastSource, lastTarget);\n }\n}\n" + }, + "contracts/mocks/beacon/ExecutionLayerWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { GeneralPurposeToConsensusLayerRequest } from \"./GeneralPurposeToConsensusLayerRequest.sol\";\n\ncontract ExecutionLayerWithdrawal is GeneralPurposeToConsensusLayerRequest {\n event WithdrawalRequestIssued(bytes publicKey, uint64 amount);\n\n bytes public lastPublicKey;\n uint64 public lastAmount;\n\n function handleRequest(bytes calldata data) internal override {\n // parameters should consist of 48 bytes for public key and 8 bytes for uint64 amount\n require(data.length == 56, \"Invalid Withdrawal data\");\n lastPublicKey = data[:48];\n lastAmount = uint64(bytes8(data[48:]));\n emit WithdrawalRequestIssued(lastPublicKey, lastAmount);\n }\n}\n" + }, + "contracts/mocks/beacon/GeneralPurposeToConsensusLayerRequest.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nabstract contract GeneralPurposeToConsensusLayerRequest {\n // solhint-disable no-complex-fallback\n fallback() external payable {\n // fee requested\n if (msg.data.length == 0) {\n uint256 fee = _fee();\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Return a uint256 value\n mstore(0, fee)\n return(0, 32) // Return 32 bytes from memory\n }\n }\n\n // else handle request\n handleRequest(msg.data);\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _fee() internal virtual returns (uint256) {\n return 1;\n }\n\n function handleRequest(bytes calldata data) internal virtual;\n}\n" + }, + "contracts/mocks/beacon/MockBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\n// solhint-disable no-unused-vars\n\n/**\n * @title Mock contract for test purposes verifying Merkle proofs\n * @author Origin Protocol Inc\n */\ncontract MockBeaconProofs is IBeaconProofs {\n /// @dev Number of bytes in the proof to the first pending deposit.\n /// 37 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0]\n /// 37 * 32 bytes = 1184 bytes\n uint256 internal constant FIRST_PENDING_DEPOSIT_PROOF_LENGTH = 1184;\n\n uint256 internal constant DEFAULT_VALIDATOR_BALANCE = 32 ether;\n // mapping of validator indexes to validator balances\n mapping(uint40 => uint256) public validatorBalances;\n\n function setValidatorBalance(uint40 index, uint256 validatorBalance)\n external\n {\n // set special max value instead of 0\n if (validatorBalance == 0) {\n validatorBalances[index] = type(uint256).max;\n } else {\n validatorBalances[index] = validatorBalance;\n }\n }\n\n /// @notice Verifies the validator index is for the given validator public key.\n /// Also verify the validator's withdrawal credential points to the withdrawal address.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param proof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata proof,\n uint40 validatorIndex,\n address withdrawalAddress\n ) external view {\n // always pass\n }\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view {\n // always pass\n }\n\n /// @notice Verifies the balances container to the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerRoot The merkle root of the the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata balancesContainerProof\n ) external view {\n // always pass\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param balanceProof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint40 validatorIndex\n ) external view returns (uint256 validatorBalanceGwei) {\n uint256 validatorBalance = validatorBalances[validatorIndex];\n\n // special setting representing 0 balance\n if (validatorBalance == type(uint256).max) {\n return 0;\n }\n // validator balance not set by the test cases\n else if (validatorBalance == 0) {\n return DEFAULT_VALIDATOR_BALANCE;\n }\n\n return validatorBalance;\n }\n\n function verifyPendingDepositsContainer(\n bytes32 beaconBlockRoot,\n bytes32 pendingDepositsContainerRoot,\n bytes calldata proof\n ) external view {\n // always pass\n }\n\n function verifyPendingDeposit(\n bytes32 pendingDepositsContainerRoot,\n bytes32 pendingDepositRoot,\n bytes calldata proof,\n uint64 depositIndex\n ) external view {\n // always pass\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the slot of the first pending deposit to the beacon block root.\n /// BeaconBlock.state.pendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty.\n /// @param firstPendingDepositSlotProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view returns (bool isEmptyDepositQueue) {\n if (\n firstPendingDepositSlotProof.length ==\n FIRST_PENDING_DEPOSIT_PROOF_LENGTH\n ) {\n isEmptyDepositQueue = true;\n }\n }\n\n function merkleizePendingDeposit(\n bytes32 pubKeyHash,\n bytes calldata withdrawalCredentials,\n uint64 amountGwei,\n bytes calldata signature,\n uint64 slot\n ) external pure returns (bytes32) {\n return\n keccak256(\n abi.encodePacked(\n pubKeyHash,\n withdrawalCredentials,\n amountGwei,\n signature,\n slot\n )\n );\n }\n\n function merkleizeSignature(bytes calldata signature)\n external\n pure\n returns (bytes32 root)\n {\n return keccak256(abi.encodePacked(signature));\n }\n}\n" + }, + "contracts/mocks/beacon/MockBeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\n\ncontract MockBeaconRoots {\n // Mapping to simulate the ring buffer: timestamp => beacon block root\n mapping(uint256 => bytes32) internal _beaconRoots;\n\n // Event to log when a new root is set (for testing)\n event RootSet(uint256 indexed timestamp, bytes32 root);\n\n // Fallback function to handle raw 32-byte timestamp input\n // solhint-disable no-complex-fallback\n fallback() external {\n // Ensure input is exactly 32 bytes (big-endian encoded timestamp)\n require(msg.data.length == 32, \"Input must be 32 bytes\");\n\n // Decode the 32-byte input as a uint256 timestamp (big-endian)\n uint256 timestamp = abi.decode(msg.data, (uint256));\n\n // Don't do any validation of timestamp so we can test any block\n\n // Retrieve the root. Will return bytes32(0) if not set.\n bytes32 root = _beaconRoots[timestamp];\n\n // Return the 32-byte root directly\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0, root)\n return(0, 32)\n }\n }\n\n // Mock function to set a beacon block root (for testing)\n function setBeaconRoot(uint256 timestamp, bytes32 root) external {\n require(timestamp > 0, \"Invalid timestamp\");\n require(root != bytes32(0), \"Invalid root\");\n\n // Store the root at the given timestamp\n _beaconRoots[timestamp] = root;\n\n emit RootSet(timestamp, root);\n }\n\n function setBeaconRoot(bytes32 root) external {\n require(root != bytes32(0), \"Invalid root\");\n\n // Store the root at the given timestamp\n _beaconRoots[block.timestamp] = root;\n\n emit RootSet(block.timestamp, root);\n }\n\n function parentBlockRoot(uint64 timestamp)\n external\n view\n returns (bytes32 parentRoot)\n {\n return BeaconRoots.parentBlockRoot(timestamp);\n }\n\n function latestBlockRoot()\n external\n view\n returns (bytes32 parentRoot, uint64 timestamp)\n {\n timestamp = uint64(block.timestamp);\n parentRoot = BeaconRoots.parentBlockRoot(timestamp);\n }\n}\n" + }, + "contracts/mocks/BurnableERC20.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ninterface IBurnableERC20 {\n function burn(uint256 value) external returns (bool);\n\n function burnFrom(address account, uint256 value) external returns (bool);\n}\n\n/**\n * @title BurnableERC20\n * @dev Exposes the burn function of ERC20 for tests\n */\nabstract contract BurnableERC20 is IBurnableERC20, ERC20 {\n /**\n * @dev Function to burn tokens\n * @param value The amount of tokens to burn.\n * @return A boolean that indicates if the operation was successful.\n */\n function burn(uint256 value) public virtual override returns (bool) {\n _burn(msg.sender, value);\n return true;\n }\n\n /**\n * @dev Function to burn tokens from a specific account\n * @param account The address with the tokens to burn.\n * @param value The amount of tokens to burn.\n * @return A boolean that indicates if the operation was successful.\n */\n function burnFrom(address account, uint256 value)\n public\n override\n returns (bool)\n {\n _burn(account, value);\n return true;\n }\n}\n" + }, + "contracts/mocks/curve/Mock3CRV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20, MintableERC20 } from \"../MintableERC20.sol\";\nimport { BurnableERC20 } from \"../BurnableERC20.sol\";\n\ncontract Mock3CRV is MintableERC20, BurnableERC20 {\n constructor() ERC20(\"Curve.fi DAI/USDC/USDT\", \"3Crv\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/curve/MockBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { MockRewardPool } from \"./MockRewardPool.sol\";\n\nimport { IRewardStaking } from \"../../strategies/IRewardStaking.sol\";\nimport { IMintableERC20, MintableERC20, ERC20 } from \"../MintableERC20.sol\";\nimport { IBurnableERC20, BurnableERC20 } from \"../BurnableERC20.sol\";\n\ncontract MockDepositToken is MintableERC20, BurnableERC20 {\n constructor() ERC20(\"DCVX\", \"CVX Deposit Token\") {}\n}\n\ncontract MockBooster {\n using SafeERC20 for IERC20;\n\n struct PoolInfo {\n address lptoken;\n address token;\n address crvRewards;\n }\n\n address public minter; // this is CVx for the booster on live\n address public crv; // Curve rewards token\n address public cvx; // Convex rewards token\n mapping(uint256 => PoolInfo) public poolInfo;\n\n constructor(\n address _rewardsMinter,\n address _crv,\n address _cvx\n ) public {\n minter = _rewardsMinter;\n crv = _crv;\n cvx = _cvx;\n }\n\n function setPool(uint256 pid, address _lpToken)\n external\n returns (address rewards)\n {\n address token = address(new MockDepositToken());\n // Deploy a new Convex Rewards Pool\n rewards = address(\n new MockRewardPool(pid, token, crv, cvx, address(this))\n );\n\n poolInfo[pid] = PoolInfo({\n lptoken: _lpToken,\n token: token,\n crvRewards: rewards\n });\n }\n\n function deposit(\n uint256 _pid,\n uint256 _amount,\n bool _stake\n ) public returns (bool) {\n PoolInfo storage pool = poolInfo[_pid];\n\n address lptoken = pool.lptoken;\n\n // hold on to the Curve LP tokens\n IERC20(lptoken).safeTransferFrom(msg.sender, address(this), _amount);\n\n address token = pool.token;\n if (_stake) {\n // mint Convex pool LP tokens and stake in rewards contract on user behalf\n IMintableERC20(token).mint(_amount);\n address rewardContract = pool.crvRewards;\n IERC20(token).safeApprove(rewardContract, 0);\n IERC20(token).safeApprove(rewardContract, _amount);\n IRewardStaking(rewardContract).stakeFor(msg.sender, _amount);\n } else {\n // mint Convex pool LP tokens and send to user\n IMintableERC20(token).mint(_amount);\n IERC20(token).transfer(msg.sender, _amount);\n }\n return true;\n }\n\n // Deposit all Curve LP tokens and stake\n function depositAll(uint256 _pid, bool _stake) external returns (bool) {\n address lptoken = poolInfo[_pid].lptoken;\n uint256 balance = IERC20(lptoken).balanceOf(msg.sender);\n deposit(_pid, balance, _stake);\n return true;\n }\n\n // withdraw Curve LP tokens\n function _withdraw(\n uint256 _pid,\n uint256 _amount,\n address _from,\n address _to\n ) internal {\n PoolInfo storage pool = poolInfo[_pid];\n\n // burn the Convex pool LP tokens\n IBurnableERC20(pool.token).burnFrom(_from, _amount);\n\n // return the Curve LP tokens\n IERC20(pool.lptoken).safeTransfer(_to, _amount);\n }\n\n // withdraw Curve LP tokens\n function withdraw(uint256 _pid, uint256 _amount) public returns (bool) {\n _withdraw(_pid, _amount, msg.sender, msg.sender);\n return true;\n }\n\n // withdraw all Curve LP tokens\n function withdrawAll(uint256 _pid) public returns (bool) {\n address token = poolInfo[_pid].token;\n uint256 userBal = IERC20(token).balanceOf(msg.sender);\n withdraw(_pid, userBal);\n return true;\n }\n\n // allow reward contracts to send here and withdraw to user\n function withdrawTo(\n uint256 _pid,\n uint256 _amount,\n address _to\n ) external returns (bool) {\n address rewardContract = poolInfo[_pid].crvRewards;\n require(msg.sender == rewardContract, \"!auth\");\n\n _withdraw(_pid, _amount, msg.sender, _to);\n return true;\n }\n\n // callback from reward contract when crv is received.\n function rewardClaimed(\n uint256 _pid,\n // solhint-disable-next-line no-unused-vars\n address _address,\n uint256 _amount\n ) external returns (bool) {\n address rewardContract = poolInfo[_pid].crvRewards;\n require(msg.sender == rewardContract, \"!auth\");\n\n //mint reward tokens\n // and transfer it\n IMintableERC20(minter).mint(_amount);\n IERC20(minter).transfer(msg.sender, _amount);\n return true;\n }\n}\n" + }, + "contracts/mocks/curve/MockCRV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../MintableERC20.sol\";\n\ncontract MockCRV is MintableERC20 {\n constructor() ERC20(\"Curve DAO Token\", \"CRV\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/curve/MockCRVMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\n\ncontract MockCRVMinter {\n address crv;\n\n constructor(address _crv) {\n crv = _crv;\n }\n\n function mint(address _address) external {\n uint256 amount = 2e18;\n IMintableERC20(crv).mint(amount);\n IERC20(crv).transfer(_address, amount);\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveAbstractMetapool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { MintableERC20, IMintableERC20 } from \"../MintableERC20.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport \"../../utils/Helpers.sol\";\n\nabstract contract MockCurveAbstractMetapool is MintableERC20 {\n using StableMath for uint256;\n\n address[] public coins;\n uint256[2] public balances;\n\n // Returns the same amount of LP tokens in 1e18 decimals\n function add_liquidity(uint256[2] calldata _amounts, uint256 _minAmount)\n external\n returns (uint256 lpAmount)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transferFrom(\n msg.sender,\n address(this),\n _amounts[i]\n );\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to sum\n lpAmount += _amounts[i].scaleBy(18, assetDecimals);\n balances[i] = balances[i] + _amounts[i];\n }\n }\n // Hacky way of simulating slippage to check _minAmount\n if (lpAmount == 29000e18) lpAmount = 14500e18;\n require(lpAmount >= _minAmount, \"Slippage ruined your day\");\n // Send LP token to sender, e.g. 3CRV\n _mint(msg.sender, lpAmount);\n }\n\n // Dumb implementation that returns the same amount\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n public\n view\n returns (uint256 lpAmount)\n {\n uint256 assetDecimals = Helpers.getDecimals(coins[uint128(_index)]);\n lpAmount = _amount.scaleBy(assetDecimals, 18);\n }\n\n function remove_liquidity_one_coin(\n uint256 _lpAmount,\n int128 _index,\n // solhint-disable-next-line no-unused-vars\n uint256 _minAmount\n ) external returns (uint256 amount) {\n _burn(msg.sender, _lpAmount);\n uint256[] memory amounts = new uint256[](coins.length);\n amounts[uint128(_index)] = _lpAmount;\n amount = calc_withdraw_one_coin(_lpAmount, _index);\n balances[uint128(_index)] -= amount;\n IERC20(coins[uint128(_index)]).transfer(msg.sender, amount);\n }\n\n function get_virtual_price() external pure returns (uint256) {\n return 1e18;\n }\n\n // solhint-disable-next-line no-unused-vars\n function remove_liquidity(uint256 _amount, uint256[2] memory _min_amounts)\n public\n returns (uint256[2] memory amounts)\n {\n _burn(msg.sender, _amount);\n uint256 totalSupply = totalSupply();\n for (uint256 i = 0; i < 2; i++) {\n amounts[i] = totalSupply > 0\n ? (_amount * IERC20(coins[i]).balanceOf(address(this))) /\n totalSupply\n : IERC20(coins[i]).balanceOf(address(this));\n balances[i] -= amounts[i];\n IERC20(coins[i]).transfer(msg.sender, amounts[i]);\n }\n }\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens\n ) public returns (uint256) {\n return\n _remove_liquidity_imbalance(\n _amounts,\n _max_burned_tokens,\n msg.sender\n );\n }\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens,\n address _reveiver\n ) public returns (uint256) {\n return\n _remove_liquidity_imbalance(\n _amounts,\n _max_burned_tokens,\n _reveiver\n );\n }\n\n function _remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens,\n address _reveiver\n ) internal returns (uint256 lpTokens) {\n lpTokens = _max_burned_tokens;\n _burn(msg.sender, lpTokens);\n for (uint256 i = 0; i < _amounts.length; i++) {\n balances[i] -= _amounts[i];\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transfer(_reveiver, _amounts[i]);\n }\n }\n }\n\n // Dumb implementation that sums the scaled amounts\n function calc_token_amount(uint256[2] memory _amounts, bool)\n public\n view\n returns (uint256 lpTokens)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to lpTokens\n lpTokens += _amounts[i].scaleBy(18, assetDecimals);\n }\n }\n\n /// @notice 0.02% fee\n function fee() external pure returns (uint256) {\n return 2000000;\n }\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n\n function burnFrom(address from, uint256 value) public {\n _burn(from, value);\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveGauge.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { ICurveGauge } from \"../../strategies/ICurveGauge.sol\";\n\ncontract MockCurveGauge is ICurveGauge {\n mapping(address => uint256) private _balances;\n address lpToken;\n\n constructor(address _lpToken) {\n lpToken = _lpToken;\n }\n\n function balanceOf(address account) public view override returns (uint256) {\n return _balances[account];\n }\n\n function deposit(uint256 _value, address _account) external override {\n IERC20(lpToken).transferFrom(msg.sender, address(this), _value);\n _balances[_account] += _value;\n }\n\n function withdraw(uint256 _value) external override {\n IERC20(lpToken).transfer(msg.sender, _value);\n // solhint-disable-next-line reentrancy\n _balances[msg.sender] -= _value;\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveMetapool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { MockCurveAbstractMetapool } from \"./MockCurveAbstractMetapool.sol\";\nimport \"../MintableERC20.sol\";\n\ncontract MockCurveMetapool is MockCurveAbstractMetapool {\n constructor(address[2] memory _coins)\n ERC20(\"Curve.fi 3pool/OUSD metapool\", \"3crv_OUSD\")\n {\n coins = _coins;\n }\n}\n" + }, + "contracts/mocks/curve/MockCurvePool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IBurnableERC20 } from \"../BurnableERC20.sol\";\n\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\nimport { ICurvePool } from \"../../strategies/ICurvePool.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport \"../../utils/Helpers.sol\";\n\ncontract MockCurvePool {\n using StableMath for uint256;\n\n address[] public coins;\n uint256[3] public balances;\n address lpToken;\n uint256 public slippage = 1 ether;\n\n constructor(address[3] memory _coins, address _lpToken) {\n coins = _coins;\n lpToken = _lpToken;\n }\n\n function setCoins(address[] memory _coins) external {\n coins = _coins;\n }\n\n // Returns the same amount of LP tokens in 1e18 decimals\n function add_liquidity(uint256[3] calldata _amounts, uint256 _minAmount)\n external\n {\n uint256 sum = 0;\n for (uint256 i = 0; i < _amounts.length; i++) {\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transferFrom(\n msg.sender,\n address(this),\n _amounts[i]\n );\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to sum\n sum += _amounts[i].scaleBy(18, assetDecimals);\n balances[i] = balances[i] + _amounts[i];\n }\n }\n // Hacky way of simulating slippage to check _minAmount\n if (sum == 29000e18) sum = 14500e18;\n require(sum >= _minAmount, \"Slippage ruined your day\");\n // Send LP token to sender, e.g. 3CRV\n IMintableERC20(lpToken).mint(sum);\n IERC20(lpToken).transfer(msg.sender, sum);\n }\n\n // Dumb implementation that returns the same amount\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n public\n view\n returns (uint256)\n {\n uint256 assetDecimals = Helpers.getDecimals(coins[uint128(_index)]);\n return _amount.scaleBy(assetDecimals, 18);\n }\n\n function remove_liquidity_one_coin(\n uint256 _amount,\n int128 _index,\n // solhint-disable-next-line no-unused-vars\n uint256 _minAmount\n ) external {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _amount);\n uint256[] memory amounts = new uint256[](coins.length);\n amounts[uint128(_index)] = _amount;\n uint256 coinAmount = calc_withdraw_one_coin(_amount, _index);\n balances[uint128(_index)] -= coinAmount;\n IERC20(coins[uint128(_index)]).transfer(msg.sender, coinAmount);\n }\n\n function get_virtual_price() external pure returns (uint256) {\n return 1e18;\n }\n\n // solhint-disable-next-line no-unused-vars\n function remove_liquidity(uint256 _lpAmount, uint256[3] memory _min_amounts)\n public\n {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _lpAmount);\n uint256 totalSupply = IERC20(lpToken).totalSupply();\n for (uint256 i = 0; i < 3; i++) {\n uint256 coinAmount = totalSupply > 0\n ? (_lpAmount * IERC20(coins[i]).balanceOf(address(this))) /\n totalSupply\n : IERC20(coins[i]).balanceOf(address(this));\n balances[i] -= coinAmount;\n IERC20(coins[i]).transfer(msg.sender, coinAmount);\n }\n }\n\n function remove_liquidity_imbalance(\n uint256[3] memory _amounts,\n uint256 _max_burned_tokens\n ) public {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _max_burned_tokens);\n // For each coin, transfer to the caller\n for (uint256 i = 0; i < _amounts.length; i++) {\n balances[i] -= _amounts[i];\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transfer(msg.sender, _amounts[i]);\n }\n }\n }\n\n // Dumb implementation that sums the scaled amounts\n function calc_token_amount(uint256[3] memory _amounts, bool)\n public\n view\n returns (uint256 lpTokens)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to lpTokens\n lpTokens += _amounts[i].scaleBy(18, assetDecimals);\n }\n }\n\n function fee() external pure returns (uint256) {\n return 1000000;\n }\n\n function exchange(\n uint256 coin0,\n uint256 coin1,\n uint256 amountIn,\n uint256 minAmountOut\n ) external returns (uint256 amountOut) {\n IERC20(coins[coin0]).transferFrom(msg.sender, address(this), amountIn);\n amountOut = (minAmountOut * slippage) / 1 ether;\n require(amountOut >= minAmountOut, \"Slippage error\");\n IMintableERC20(coins[coin1]).mintTo(msg.sender, amountOut);\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n}\n" + }, + "contracts/mocks/curve/MockCVX.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../MintableERC20.sol\";\n\ncontract MockCVX is MintableERC20 {\n constructor() ERC20(\"CVX\", \"CVX DAO Token\") {}\n}\n" + }, + "contracts/mocks/curve/MockRewardPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\ninterface IDeposit {\n function poolInfo(uint256)\n external\n view\n returns (\n address,\n address,\n address,\n address,\n address,\n bool\n );\n\n function rewardClaimed(\n uint256,\n address,\n uint256\n ) external;\n\n function withdrawTo(\n uint256,\n uint256,\n address\n ) external;\n}\n\ncontract MockRewardPool {\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 public pid;\n address public stakingToken;\n address public rewardTokenA;\n address public rewardTokenB;\n address public operator;\n\n uint256 private _totalSupply;\n\n mapping(address => uint256) private _balances;\n mapping(address => uint256) public rewards;\n\n constructor(\n uint256 _pid,\n address _stakingToken,\n address _rewardTokenA,\n address _rewardTokenB,\n address _operator\n ) public {\n pid = _pid;\n stakingToken = _stakingToken;\n rewardTokenA = _rewardTokenA;\n rewardTokenB = _rewardTokenB;\n operator = _operator;\n }\n\n function totalSupply() public view returns (uint256) {\n return _totalSupply;\n }\n\n function balanceOf(address account) public view returns (uint256) {\n return _balances[account];\n }\n\n function stakeFor(address _for, uint256 _amount) public returns (bool) {\n require(_amount > 0, \"RewardPool : Cannot stake 0\");\n\n //give to _for\n _totalSupply = _totalSupply.add(_amount);\n _balances[_for] = _balances[_for].add(_amount);\n\n //take away from sender\n IERC20(stakingToken).safeTransferFrom(\n msg.sender,\n address(this),\n _amount\n );\n\n return true;\n }\n\n function withdrawAndUnwrap(uint256 amount, bool claim)\n public\n returns (bool)\n {\n _totalSupply = _totalSupply.sub(amount);\n _balances[msg.sender] = _balances[msg.sender].sub(amount);\n\n //tell operator to withdraw from here directly to user\n IDeposit(operator).withdrawTo(pid, amount, msg.sender);\n\n //get rewards too\n if (claim) {\n getReward(msg.sender, true);\n }\n return true;\n }\n\n function withdrawAllAndUnwrap(bool claim) external {\n withdrawAndUnwrap(_balances[msg.sender], claim);\n }\n\n // solhint-disable-next-line no-unused-vars\n function getReward(address _account, bool _claimExtras)\n public\n returns (bool)\n {\n IMintableERC20(rewardTokenA).mint(2 * 1e18);\n IERC20(rewardTokenA).transfer(_account, 2 * 1e18);\n\n IMintableERC20(rewardTokenB).mint(3 * 1e18);\n IERC20(rewardTokenB).transfer(_account, 3 * 1e18);\n\n return true;\n }\n\n function getReward() public returns (bool) {\n getReward(msg.sender, true);\n }\n}\n" + }, + "contracts/mocks/ForceEtherSender.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract ForceEtherSender {\n // Constructor to optionally receive Ether upon deployment\n constructor() payable {}\n\n // Function to allow the contract to receive Ether\n receive() external payable {}\n\n // Function to self-destruct and force-send Ether to an address\n function forceSend(address payable recipient) external {\n // Requires that the contract has a balance greater than 0\n require(address(this).balance > 0, \"No Ether to send\");\n\n // selfdestruct sends all Ether held by the contract to the recipient\n selfdestruct(recipient);\n }\n}\n" + }, + "contracts/mocks/MintableERC20.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ninterface IMintableERC20 {\n function mint(uint256 value) external;\n\n function mintTo(address to, uint256 value) external;\n}\n\n/**\n * @title MintableERC20\n * @dev Exposes the mint function of ERC20 for tests\n */\nabstract contract MintableERC20 is IMintableERC20, ERC20 {\n /**\n * @dev Function to mint tokens\n * @param _value The amount of tokens to mint.\n */\n function mint(uint256 _value) public virtual override {\n _mint(msg.sender, _value);\n }\n\n /**\n * @dev Function to mint tokens\n * @param _to Address to mint to.\n * @param _value The amount of tokens to mint.\n */\n function mintTo(address _to, uint256 _value) public virtual override {\n _mint(_to, _value);\n }\n}\n" + }, + "contracts/mocks/Mock1InchSwapRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { SwapDescription } from \"../interfaces/IOneInch.sol\";\n\ncontract Mock1InchSwapRouter {\n using SafeERC20 for IERC20;\n\n event MockSwap(address executor, bytes permitData, bytes executorData);\n\n event MockSwapDesc(\n address srcToken,\n address dstToken,\n address srcReceiver,\n address dstReceiver,\n uint256 amount,\n uint256 minReturnAmount,\n uint256 flags\n );\n\n event MockUnoswapTo(\n address recipient,\n address srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] pools\n );\n\n event MockUniswapV3SwapTo(\n address recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] pools\n );\n\n /**\n * @dev transfers the shource asset and returns the minReturnAmount of the destination asset.\n */\n function swap(\n address executor,\n SwapDescription calldata desc,\n bytes calldata permitData,\n bytes calldata executorData\n ) public returns (uint256 returnAmount, uint256 spentAmount) {\n // Transfer the source tokens to the receiver contract\n IERC20(desc.srcToken).safeTransferFrom(\n msg.sender,\n desc.srcReceiver,\n desc.amount\n );\n\n // Transfer the destination tokens to the recipient\n IERC20(desc.dstToken).safeTransfer(\n desc.dstReceiver,\n desc.minReturnAmount\n );\n\n emit MockSwap(executor, permitData, executorData);\n _swapDesc(desc);\n returnAmount = 0;\n spentAmount = 0;\n }\n\n function _swapDesc(SwapDescription calldata desc) public {\n emit MockSwapDesc(\n address(desc.srcToken),\n address(desc.dstToken),\n desc.srcReceiver,\n desc.dstReceiver,\n desc.amount,\n desc.minReturnAmount,\n desc.flags\n );\n }\n\n /**\n * @dev only transfers the source asset to this contract.\n * Ideally it would return the destination asset but that's encoded in the pools array.\n */\n function unoswapTo(\n address payable recipient,\n address srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) public returns (uint256 returnAmount) {\n // transfer the from asset from the caller\n IERC20(srcToken).safeTransferFrom(msg.sender, address(this), amount);\n\n emit MockUnoswapTo(recipient, srcToken, amount, minReturn, pools);\n returnAmount = 0;\n }\n\n /**\n * @dev does not do any transfers. Just emits MockUniswapV3SwapTo.\n */\n function uniswapV3SwapTo(\n address payable recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) public returns (uint256 returnAmount) {\n emit MockUniswapV3SwapTo(recipient, amount, minReturn, pools);\n returnAmount = 0;\n }\n}\n" + }, + "contracts/mocks/MockAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { IAaveLendingPool, ILendingPoolAddressesProvider } from \"../strategies/IAave.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// 1. User calls 'getLendingPool'\n// 2. User calls 'deposit' (Aave)\n// - Deposit their underlying\n// - Mint aToken to them\n// 3. User calls redeem (aToken)\n// - Retrieve their aToken\n// - Return equal amount of underlying\n\ncontract MockAToken is MintableERC20 {\n address public lendingPool;\n IERC20 public underlyingToken;\n using SafeERC20 for IERC20;\n\n constructor(\n address _lendingPool,\n string memory _name,\n string memory _symbol,\n IERC20 _underlyingToken\n ) ERC20(_name, _symbol) {\n lendingPool = _lendingPool;\n underlyingToken = _underlyingToken;\n // addMinter(_lendingPool);\n }\n\n function decimals() public view override returns (uint8) {\n return ERC20(address(underlyingToken)).decimals();\n }\n\n function poolRedeem(uint256 _amount, address _to) external {\n require(msg.sender == lendingPool, \"pool only\");\n // Redeem these a Tokens\n _burn(_to, _amount);\n // For the underlying\n underlyingToken.safeTransferFrom(lendingPool, _to, _amount);\n }\n}\n\ncontract MockAave is IAaveLendingPool, ILendingPoolAddressesProvider {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n mapping(address => address) reserveToAToken;\n address pool = address(this);\n address payable core = payable(address(this));\n uint256 factor;\n\n function addAToken(address _aToken, address _underlying) public {\n IERC20(_underlying).safeApprove(_aToken, 0);\n IERC20(_underlying).safeApprove(_aToken, type(uint256).max);\n reserveToAToken[_underlying] = _aToken;\n }\n\n // set the reserve factor / basically the interest on deposit\n // in 18 precision\n // so 0.5% would be 5 * 10 ^ 15\n function setFactor(uint256 factor_) public {\n factor = factor_;\n }\n\n function deposit(\n address _reserve,\n uint256 _amount,\n address _to,\n uint16 /*_referralCode*/\n ) external override {\n uint256 previousBal = IERC20(reserveToAToken[_reserve]).balanceOf(\n msg.sender\n );\n uint256 interest = previousBal.mulTruncate(factor);\n MintableERC20(reserveToAToken[_reserve]).mintTo(msg.sender, interest);\n // Take their reserve\n IERC20(_reserve).safeTransferFrom(msg.sender, address(this), _amount);\n // Credit them with aToken\n MintableERC20(reserveToAToken[_reserve]).mintTo(_to, _amount);\n }\n\n function withdraw(\n address asset,\n uint256 amount,\n address to\n ) external override returns (uint256) {\n MockAToken atoken = MockAToken(reserveToAToken[asset]);\n atoken.poolRedeem(amount, to);\n return amount;\n }\n\n function getLendingPool() external view override returns (address) {\n return pool;\n }\n}\n" + }, + "contracts/mocks/MockAaveIncentivesController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { MockStkAave } from \"./MockStkAave.sol\";\n\ncontract MockAaveIncentivesController {\n mapping(address => uint256) private rewards;\n MockStkAave public REWARD_TOKEN;\n\n constructor(address _reward_token) {\n REWARD_TOKEN = MockStkAave(_reward_token);\n }\n\n function setRewardsBalance(address user, uint256 amount) external {\n rewards[user] = amount;\n }\n\n /**\n * @dev Returns the total of rewards of an user, already accrued + not yet accrued\n * @param user The address of the user\n * @return The rewards\n **/\n // solhint-disable-next-line no-unused-vars\n function getRewardsBalance(address[] calldata assets, address user)\n external\n view\n returns (uint256)\n {\n return rewards[user];\n }\n\n /**\n * @dev Claims reward for an user, on all the assets of the lending pool, accumulating the pending rewards\n * @param amount Amount of rewards to claim\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewards(\n // solhint-disable-next-line no-unused-vars\n address[] calldata assets,\n uint256 amount,\n address to\n ) external returns (uint256) {\n require(amount > 0);\n require(rewards[to] == amount);\n REWARD_TOKEN.mint(amount);\n require(REWARD_TOKEN.transfer(to, amount));\n // solhint-disable-next-line reentrancy\n rewards[to] = 0;\n return amount;\n }\n}\n" + }, + "contracts/mocks/MockAAVEToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAAVEToken is MintableERC20 {\n constructor() ERC20(\"AAVE\", \"AAVE\") {}\n}\n" + }, + "contracts/mocks/MockAero.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAero is MintableERC20 {\n constructor() ERC20(\"Aerodrome\", \"AERO\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/MockAura.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAura is MintableERC20 {\n constructor() ERC20(\"Aura\", \"AURA\") {}\n}\n" + }, + "contracts/mocks/MockBAL.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockBAL is MintableERC20 {\n constructor() ERC20(\"Balancer\", \"BAL\") {}\n}\n" + }, + "contracts/mocks/MockBalancerVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// import \"hardhat/console.sol\";\n\ncontract MockBalancerVault {\n using StableMath for uint256;\n uint256 public slippage = 1 ether;\n bool public transferDisabled = false;\n bool public slippageErrorDisabled = false;\n\n function swap(\n IBalancerVault.SingleSwap calldata singleSwap,\n IBalancerVault.FundManagement calldata funds,\n uint256 minAmountOut,\n uint256\n ) external returns (uint256 amountCalculated) {\n amountCalculated = (minAmountOut * slippage) / 1 ether;\n if (!slippageErrorDisabled) {\n require(amountCalculated >= minAmountOut, \"Slippage error\");\n }\n IERC20(singleSwap.assetIn).transferFrom(\n funds.sender,\n address(this),\n singleSwap.amount\n );\n if (!transferDisabled) {\n MintableERC20(singleSwap.assetOut).mintTo(\n funds.recipient,\n amountCalculated\n );\n }\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n\n function getPoolTokenInfo(bytes32 poolId, address token)\n external\n view\n returns (\n uint256,\n uint256,\n uint256,\n address\n )\n {}\n\n function disableTransfer() external {\n transferDisabled = true;\n }\n\n function disableSlippageError() external {\n slippageErrorDisabled = true;\n }\n}\n" + }, + "contracts/mocks/MockBeaconConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconConsolidation } from \"../beacon/BeaconConsolidation.sol\";\n\ncontract MockBeaconConsolidation {\n function fee() external view returns (uint256) {\n return BeaconConsolidation.fee();\n }\n\n function request(bytes calldata source, bytes calldata target)\n external\n returns (uint256 fee_)\n {\n return BeaconConsolidation.request(source, target);\n }\n}\n" + }, + "contracts/mocks/MockChainlinkOracleFeed.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\ncontract MockChainlinkOracleFeed is AggregatorV3Interface {\n int256 price;\n uint8 numDecimals;\n\n constructor(int256 _price, uint8 _decimals) {\n price = _price;\n numDecimals = _decimals;\n }\n\n function decimals() external view override returns (uint8) {\n return numDecimals;\n }\n\n function description() external pure override returns (string memory) {\n return \"MockOracleEthFeed\";\n }\n\n function version() external pure override returns (uint256) {\n return 1;\n }\n\n function setPrice(int256 _price) public {\n price = _price;\n }\n\n function setDecimals(uint8 _decimals) public {\n numDecimals = _decimals;\n }\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(uint80 _roundId)\n external\n view\n override\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n )\n {\n roundId = _roundId;\n answer = price;\n startedAt = 0;\n updatedAt = 0;\n answeredInRound = 0;\n }\n\n function latestRoundData()\n external\n view\n override\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n )\n {\n roundId = 0;\n answer = price;\n startedAt = 0;\n updatedAt = block.timestamp;\n answeredInRound = 0;\n }\n}\n" + }, + "contracts/mocks/MockCOMP.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockCOMP is MintableERC20 {\n constructor() ERC20(\"COMP\", \"COMP\") {}\n}\n" + }, + "contracts/mocks/MockComptroller.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract MockComptroller {\n // Claim all the COMP accrued by specific holders in specific markets for their supplies and/or borrows\n function claimComp(\n address[] memory holders,\n address[] memory cTokens,\n bool borrowers,\n bool suppliers\n ) external {}\n}\n" + }, + "contracts/mocks/MockCToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { ICERC20 } from \"../strategies/ICompound.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockCToken is ICERC20, ERC20 {\n using StableMath for uint256;\n\n IERC20 public underlyingToken;\n // underlying = cToken * exchangeRate\n // cToken = underlying / exchangeRate\n uint256 exchangeRate;\n address public override comptroller;\n\n constructor(ERC20 _underlyingToken, address _comptroller)\n ERC20(\"cMock\", \"cMK\")\n {\n uint8 underlyingDecimals = _underlyingToken.decimals();\n // if has 18 dp, exchange rate should be 1e26\n // if has 8 dp, exchange rate should be 1e18\n if (underlyingDecimals > 8) {\n exchangeRate = 10**uint256(18 + underlyingDecimals - 10);\n } else if (underlyingDecimals < 8) {\n // e.g. 18-8+6 = 16\n exchangeRate = 10**uint256(18 - 8 + underlyingDecimals);\n } else {\n exchangeRate = 1e18;\n }\n underlyingToken = _underlyingToken;\n comptroller = _comptroller;\n }\n\n function decimals() public pure override returns (uint8) {\n return 8;\n }\n\n function mint(uint256 mintAmount) public override returns (uint256) {\n // Credit them with cToken\n _mint(msg.sender, mintAmount.divPrecisely(exchangeRate));\n // Take their reserve\n underlyingToken.transferFrom(msg.sender, address(this), mintAmount);\n return 0;\n }\n\n function redeem(uint256 redeemAmount) external override returns (uint256) {\n uint256 tokenAmount = redeemAmount.mulTruncate(exchangeRate);\n // Burn the cToken\n _burn(msg.sender, redeemAmount);\n // Transfer underlying to caller\n underlyingToken.transfer(msg.sender, tokenAmount);\n return 0;\n }\n\n function redeemUnderlying(uint256 redeemAmount)\n external\n override\n returns (uint256)\n {\n uint256 cTokens = redeemAmount.divPrecisely(exchangeRate);\n // Burn the cToken\n _burn(msg.sender, cTokens);\n // Transfer underlying to caller\n underlyingToken.transfer(msg.sender, redeemAmount);\n return 0;\n }\n\n function balanceOfUnderlying(address owner)\n external\n view\n override\n returns (uint256)\n {\n uint256 cTokenBal = this.balanceOf(owner);\n return cTokenBal.mulTruncate(exchangeRate);\n }\n\n function balanceOf(address owner)\n public\n view\n override(ICERC20, ERC20)\n returns (uint256)\n {\n return ERC20.balanceOf(owner);\n }\n\n function updateExchangeRate()\n internal\n view\n returns (uint256 newExchangeRate)\n {\n uint256 factor = 100002 * (10**13); // 0.002%\n newExchangeRate = exchangeRate.mulTruncate(factor);\n }\n\n function exchangeRateStored() external view override returns (uint256) {\n return exchangeRate;\n }\n\n function supplyRatePerBlock() external pure override returns (uint256) {\n return 141 * (10**8);\n }\n}\n" + }, + "contracts/mocks/MockCVXLocker.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MockCVXLocker {\n address public immutable cvx;\n mapping(address => uint256) public lockedBalanceOf;\n\n constructor(address _cvx) {\n cvx = _cvx;\n }\n\n function lock(\n address _account,\n uint256 _amount,\n uint256\n ) external {\n lockedBalanceOf[_account] += _amount;\n ERC20(cvx).transferFrom(msg.sender, address(this), _amount);\n }\n\n function unlockAllTokens(address _account) external {\n lockedBalanceOf[_account] = 0;\n ERC20(cvx).transfer(_account, lockedBalanceOf[_account]);\n }\n}\n" + }, + "contracts/mocks/MockDAI.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockDAI is MintableERC20 {\n constructor() ERC20(\"DAI\", \"DAI\") {}\n}\n" + }, + "contracts/mocks/MockDepositContract.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IDepositContract } from \"./../interfaces/IDepositContract.sol\";\n\ncontract MockDepositContract is IDepositContract {\n uint256 deposit_count;\n\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable override {\n require(pubkey.length == 48, \"DepositContract: invalid pubkey length\");\n require(\n withdrawal_credentials.length == 32,\n \"DepositContract: invalid withdrawal_credentials length\"\n );\n require(\n signature.length == 96,\n \"DepositContract: invalid signature length\"\n );\n\n // Check deposit amount\n require(msg.value >= 1 ether, \"DepositContract: deposit value too low\");\n require(\n msg.value % 1 gwei == 0,\n \"DepositContract: deposit value not multiple of gwei\"\n );\n uint256 deposit_amount = msg.value / 1 gwei;\n require(\n deposit_amount <= type(uint64).max,\n \"DepositContract: deposit value too high\"\n );\n\n // Emit `DepositEvent` log\n bytes memory amount = to_little_endian_64(uint64(deposit_amount));\n emit DepositEvent(\n pubkey,\n withdrawal_credentials,\n amount,\n signature,\n to_little_endian_64(uint64(deposit_count))\n );\n require(\n deposit_data_root != 0,\n \"DepositContract: invalid deposit_data_root\"\n );\n }\n\n function get_deposit_root() external view override returns (bytes32) {\n // just return some bytes32\n return sha256(abi.encodePacked(deposit_count, bytes16(0)));\n }\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view override returns (bytes memory) {\n return to_little_endian_64(uint64(deposit_count));\n }\n\n function to_little_endian_64(uint64 value)\n internal\n pure\n returns (bytes memory ret)\n {\n ret = new bytes(8);\n bytes8 bytesValue = bytes8(value);\n // Byteswapping during copying to bytes.\n ret[0] = bytesValue[7];\n ret[1] = bytesValue[6];\n ret[2] = bytesValue[5];\n ret[3] = bytesValue[4];\n ret[4] = bytesValue[3];\n ret[5] = bytesValue[2];\n ret[6] = bytesValue[1];\n ret[7] = bytesValue[0];\n }\n}\n" + }, + "contracts/mocks/MockEvilDAI.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract MockEvilDAI is MintableERC20 {\n address host;\n address realCoin;\n\n constructor(address _host, address _realCoin) ERC20(\"DAI\", \"DAI\") {\n host = _host;\n realCoin = _realCoin;\n }\n\n function transferFrom(\n // solhint-disable-next-line no-unused-vars\n address _from,\n // solhint-disable-next-line no-unused-vars\n address _to,\n uint256 _amount\n ) public override returns (bool) {\n // call mint again!\n if (_amount != 69) {\n IVault(host).mint(address(this), 69, 0);\n }\n return true;\n }\n}\n" + }, + "contracts/mocks/MockEvilReentrantContract.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IRateProvider } from \"../interfaces/balancer/IRateProvider.sol\";\n\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { IERC20 } from \"../utils/InitializableAbstractStrategy.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockEvilReentrantContract {\n using StableMath for uint256;\n\n IBalancerVault public immutable balancerVault;\n IERC20 public immutable reth;\n IERC20 public immutable weth;\n IVault public immutable oethVault;\n address public immutable poolAddress;\n bytes32 public immutable balancerPoolId;\n\n constructor(\n address _balancerVault,\n address _oethVault,\n address _reth,\n address _weth,\n address _poolAddress,\n bytes32 _poolId\n ) {\n balancerVault = IBalancerVault(_balancerVault);\n oethVault = IVault(_oethVault);\n reth = IERC20(_reth);\n weth = IERC20(_weth);\n poolAddress = _poolAddress;\n balancerPoolId = _poolId;\n }\n\n function doEvilStuff() public {\n address priceProvider = oethVault.priceProvider();\n uint256 rethPrice = IOracle(priceProvider).price(address(reth));\n\n // 1. Join pool\n uint256[] memory amounts = new uint256[](2);\n amounts[0] = uint256(10 ether);\n amounts[1] = rethPrice * 10;\n\n address[] memory assets = new address[](2);\n assets[0] = address(reth);\n assets[1] = address(weth);\n\n uint256 minBPT = getBPTExpected(assets, amounts).mulTruncate(\n 0.99 ether\n );\n\n bytes memory joinUserData = abi.encode(\n IBalancerVault.WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,\n amounts,\n minBPT\n );\n\n IBalancerVault.JoinPoolRequest memory joinRequest = IBalancerVault\n .JoinPoolRequest(assets, amounts, joinUserData, false);\n\n balancerVault.joinPool(\n balancerPoolId,\n address(this),\n address(this),\n joinRequest\n );\n\n uint256 bptTokenBalance = IERC20(poolAddress).balanceOf(address(this));\n\n // 2. Redeem as ETH\n bytes memory exitUserData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT,\n bptTokenBalance,\n 1\n );\n\n assets[1] = address(0); // Receive ETH instead of WETH\n uint256[] memory exitAmounts = new uint256[](2);\n exitAmounts[1] = 15 ether;\n IBalancerVault.ExitPoolRequest memory exitRequest = IBalancerVault\n .ExitPoolRequest(assets, exitAmounts, exitUserData, false);\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n payable(address(this)),\n exitRequest\n );\n bptTokenBalance = IERC20(poolAddress).balanceOf(address(this));\n }\n\n function getBPTExpected(address[] memory _assets, uint256[] memory _amounts)\n internal\n view\n virtual\n returns (uint256 bptExpected)\n {\n // Get the oracle from the OETH Vault\n address priceProvider = oethVault.priceProvider();\n\n for (uint256 i = 0; i < _assets.length; ++i) {\n uint256 strategyAssetMarketPrice = IOracle(priceProvider).price(\n _assets[i]\n );\n // convert asset amount to ETH amount\n bptExpected =\n bptExpected +\n _amounts[i].mulTruncate(strategyAssetMarketPrice);\n }\n\n uint256 bptRate = IRateProvider(poolAddress).getRate();\n // Convert ETH amount to BPT amount\n bptExpected = bptExpected.divPrecisely(bptRate);\n }\n\n function approveAllTokens() public {\n // Approve all tokens\n weth.approve(address(oethVault), type(uint256).max);\n reth.approve(poolAddress, type(uint256).max);\n weth.approve(poolAddress, type(uint256).max);\n reth.approve(address(balancerVault), type(uint256).max);\n weth.approve(address(balancerVault), type(uint256).max);\n }\n\n receive() external payable {\n // 3. Try to mint OETH\n oethVault.mint(address(weth), 1 ether, 0.9 ether);\n }\n}\n" + }, + "contracts/mocks/MockfrxETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockfrxETH is MintableERC20 {\n constructor() ERC20(\"frxETH\", \"frxETH\") {}\n}\n" + }, + "contracts/mocks/MockFrxETHMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockFrxETHMinter {\n address public immutable frxETH;\n address public immutable sfrxETH;\n\n constructor(address _frxETH, address _sfrxETH) {\n frxETH = _frxETH;\n sfrxETH = _sfrxETH;\n }\n\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares)\n {\n IMintableERC20(frxETH).mintTo(sfrxETH, msg.value);\n IMintableERC20(sfrxETH).mintTo(recipient, msg.value);\n shares = msg.value;\n }\n}\n" + }, + "contracts/mocks/MockLimitedWrappedOusd.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WrappedOusd } from \"../token/WrappedOusd.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MockLimitedWrappedOusd is WrappedOusd {\n constructor(ERC20 underlying_) WrappedOusd(underlying_) {}\n\n function maxDeposit(address)\n public\n view\n virtual\n override\n returns (uint256)\n {\n return 1e18;\n }\n}\n" + }, + "contracts/mocks/MockMaverickDistributor.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Pool } from \"../interfaces/plume/IMaverickV2Pool.sol\";\n\ncontract MockMaverickDistributor {\n IERC20 public immutable rewardToken;\n\n uint256 public lastEpoch;\n uint256 public rewardAmount;\n\n constructor(address _rewardToken) {\n rewardToken = IERC20(_rewardToken);\n }\n\n function setLastEpoch(uint256 _epoch) external {\n lastEpoch = _epoch;\n }\n\n function setRewardTokenAmount(uint256 _amount) external {\n rewardAmount = _amount;\n }\n\n function claimLp(\n address recipient,\n uint256,\n IMaverickV2Pool,\n uint32[] memory,\n uint256\n ) external returns (uint256 amount) {\n rewardToken.transfer(recipient, rewardAmount);\n return rewardAmount;\n }\n}\n" + }, + "contracts/mocks/MockMetadataToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// IERC20Metadata is used in the resolveAsset function in contracts/utils/assets.js\n// We just need to import it here to make its ABI available to Hardhat\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n" + }, + "contracts/mocks/MockMintableUniswapPair.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport \"./MockUniswapPair.sol\";\n\ncontract MockMintableUniswapPair is MockUniswapPair, MintableERC20 {\n constructor(\n address _token0,\n address _token1,\n uint112 _reserve0,\n uint112 _reserve1\n )\n MockUniswapPair(_token0, _token1, _reserve0, _reserve1)\n ERC20(\"Uniswap V2\", \"UNI-v2\")\n {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/MockNonRebasing.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { OUSD } from \"../token/OUSD.sol\";\n\ncontract MockNonRebasing {\n OUSD oUSD;\n\n function setOUSD(address _oUSDAddress) public {\n oUSD = OUSD(_oUSDAddress);\n }\n\n function rebaseOptIn() public {\n oUSD.rebaseOptIn();\n }\n\n function rebaseOptOut() public {\n oUSD.rebaseOptOut();\n }\n\n function transfer(address _to, uint256 _value) public {\n oUSD.transfer(_to, _value);\n }\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) public {\n oUSD.transferFrom(_from, _to, _value);\n }\n\n function approve(address _spender, uint256 _addedValue) public {\n oUSD.approve(_spender, _addedValue);\n }\n\n function mintOusd(\n address _vaultContract,\n address _asset,\n uint256 _amount\n ) public {\n IVault(_vaultContract).mint(_asset, _amount, 0);\n }\n\n function redeemOusd(address _vaultContract, uint256 _amount) public {\n IVault(_vaultContract).redeem(_amount, 0);\n }\n\n function approveFor(\n address _contract,\n address _spender,\n uint256 _addedValue\n ) public {\n IERC20(_contract).approve(_spender, _addedValue);\n }\n}\n" + }, + "contracts/mocks/MockNonStandardToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\nimport \"./MintableERC20.sol\";\n\n/**\n * Mock token contract to simulate tokens that don't\n * throw/revert when a transfer/transferFrom call fails\n */\ncontract MockNonStandardToken is MintableERC20 {\n using SafeMath for uint256;\n\n constructor() ERC20(\"NonStandardToken\", \"NonStandardToken\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n\n function transfer(address recipient, uint256 amount)\n public\n override\n returns (bool)\n {\n if (balanceOf(msg.sender) < amount) {\n // Fail silently\n return false;\n }\n\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public override returns (bool) {\n if (balanceOf(sender) < amount) {\n // Fail silently\n return false;\n }\n\n _transfer(sender, recipient, amount);\n _approve(\n sender,\n _msgSender(),\n allowance(sender, _msgSender()).sub(\n amount,\n \"ERC20: transfer amount exceeds allowance\"\n )\n );\n return true;\n }\n}\n" + }, + "contracts/mocks/MockOETHVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultCore } from \"../vault/OETHVaultCore.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MockOETHVault is OETHVaultCore {\n using StableMath for uint256;\n\n constructor(address _weth) OETHVaultCore(_weth) {\n _setGovernor(msg.sender);\n }\n\n function supportAsset(address asset) external {\n assets[asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(0),\n decimals: 18,\n allowedOracleSlippageBps: 0\n });\n\n allAssets.push(asset);\n }\n}\n" + }, + "contracts/mocks/MockOETHVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"../vault/OETHVaultAdmin.sol\";\n\ncontract MockOETHVaultAdmin is OETHVaultAdmin {\n constructor(address _weth) OETHVaultAdmin(_weth) {}\n\n // fetches the WETH amount in outstanding withdrawals\n function outstandingWithdrawalsAmount()\n external\n view\n returns (uint256 wethAmount)\n {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n wethAmount = queue.queued - queue.claimed;\n }\n\n function wethAvailable() external view returns (uint256) {\n return _wethAvailable();\n }\n}\n" + }, + "contracts/mocks/MockOGN.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./BurnableERC20.sol\";\nimport \"./MintableERC20.sol\";\n\n/**\n * @title Origin token (OGN).\n *\n * @dev Token that allows minting and burning.\n * @dev Important note:\n * @dev There is a known race condition in the ERC20 standard on the approve() method.\n * @dev See details: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n * @dev The Origin token contract implements the increaseApproval() and decreaseApproval() methods.\n * @dev It is strongly recommended to use those methods rather than approve()\n * @dev when updating the token allowance.\n */\ncontract MockOGN is MintableERC20, BurnableERC20 {\n event SetWhitelistExpiration(uint256 expiration);\n event AllowedTransactorAdded(address sender);\n event AllowedTransactorRemoved(address sender);\n event AddCallSpenderWhitelist(address enabler, address spender);\n event RemoveCallSpenderWhitelist(address disabler, address spender);\n\n mapping(address => bool) public callSpenderWhitelist;\n address public owner = msg.sender;\n // UNIX timestamp (in seconds) after which this whitelist no longer applies\n uint256 public whitelistExpiration;\n // While the whitelist is active, either the sender or recipient must be\n // in allowedTransactors.\n mapping(address => bool) public allowedTransactors;\n\n // @dev Constructor that gives msg.sender all initial tokens.\n constructor(uint256 _initialSupply) ERC20(\"OriginToken\", \"OGN\") {\n owner = msg.sender;\n _mint(owner, _initialSupply);\n }\n\n //\n // approveAndCall methods\n //\n\n // @dev Add spender to whitelist of spenders for approveAndCall\n // @param _spender Address to add\n function addCallSpenderWhitelist(address _spender) public onlyOwner {\n callSpenderWhitelist[_spender] = true;\n emit AddCallSpenderWhitelist(msg.sender, _spender);\n }\n\n // @dev Remove spender from whitelist of spenders for approveAndCall\n // @param _spender Address to remove\n function removeCallSpenderWhitelist(address _spender) public onlyOwner {\n delete callSpenderWhitelist[_spender];\n emit RemoveCallSpenderWhitelist(msg.sender, _spender);\n }\n\n // @dev Approve transfer of tokens and make a contract call in a single\n // @dev transaction. This allows a DApp to avoid requiring two MetaMask\n // @dev approvals for a single logical action, such as creating a listing,\n // @dev which requires the seller to approve a token transfer and the\n // @dev marketplace contract to transfer tokens from the seller.\n //\n // @dev This is based on the ERC827 function approveAndCall and avoids\n // @dev security issues by only working with a whitelisted set of _spender\n // @dev addresses. The other difference is that the combination of this\n // @dev function ensures that the proxied function call receives the\n // @dev msg.sender for this function as its first parameter.\n //\n // @param _spender The address that will spend the funds.\n // @param _value The amount of tokens to be spent.\n // @param _selector Function selector for function to be called.\n // @param _callParams Packed, encoded parameters, omitting the first parameter which is always msg.sender\n function approveAndCallWithSender(\n address _spender,\n uint256 _value,\n bytes4 _selector,\n bytes memory _callParams\n ) public payable returns (bool) {\n require(_spender != address(this), \"token contract can't be approved\");\n require(callSpenderWhitelist[_spender], \"spender not in whitelist\");\n\n require(super.approve(_spender, _value), \"approve failed\");\n\n bytes memory callData = abi.encodePacked(\n _selector,\n uint256(uint160(msg.sender)),\n _callParams\n );\n // solium-disable-next-line security/no-call-value\n (bool success, ) = _spender.call{ value: msg.value }(callData);\n require(success, \"proxied call failed\");\n return true;\n }\n\n //\n // Functions for maintaining whitelist\n //\n\n modifier onlyOwner() {\n require(msg.sender == owner);\n _;\n }\n modifier allowedTransfer(address _from, address _to) {\n require(\n // solium-disable-next-line operator-whitespace\n !whitelistActive() ||\n allowedTransactors[_from] ||\n allowedTransactors[_to],\n \"neither sender nor recipient are allowed\"\n );\n _;\n }\n\n function whitelistActive() public view returns (bool) {\n return block.timestamp < whitelistExpiration;\n }\n\n function addAllowedTransactor(address _transactor) public onlyOwner {\n emit AllowedTransactorAdded(_transactor);\n allowedTransactors[_transactor] = true;\n }\n\n function removeAllowedTransactor(address _transactor) public onlyOwner {\n emit AllowedTransactorRemoved(_transactor);\n delete allowedTransactors[_transactor];\n }\n\n /**\n * @dev Set the whitelist expiration, after which the whitelist no longer\n * applies.\n */\n function setWhitelistExpiration(uint256 _expiration) public onlyOwner {\n // allow only if whitelist expiration hasn't yet been set, or if the\n // whitelist expiration hasn't passed yet\n require(\n whitelistExpiration == 0 || whitelistActive(),\n \"an expired whitelist cannot be extended\"\n );\n // prevent possible mistakes in calling this function\n require(\n _expiration >= block.timestamp + 1 days,\n \"whitelist expiration not far enough into the future\"\n );\n emit SetWhitelistExpiration(_expiration);\n whitelistExpiration = _expiration;\n }\n\n //\n // ERC20 transfer functions that have been overridden to enforce the\n // whitelist.\n //\n\n function transfer(address _to, uint256 _value)\n public\n override\n allowedTransfer(msg.sender, _to)\n returns (bool)\n {\n return super.transfer(_to, _value);\n }\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) public override allowedTransfer(_from, _to) returns (bool) {\n return super.transferFrom(_from, _to, _value);\n }\n}\n" + }, + "contracts/mocks/MockOGV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockOGV is MintableERC20 {\n constructor() ERC20(\"OGV\", \"OGV\") {}\n}\n" + }, + "contracts/mocks/MockOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/IPriceOracle.sol\";\nimport \"../interfaces/IMinMaxOracle.sol\";\n\n/**\n * Mock of both price Oracle and min max oracles\n */\ncontract MockOracle is IPriceOracle, IMinMaxOracle {\n mapping(bytes32 => uint256) prices;\n mapping(bytes32 => uint256[]) pricesMinMax;\n uint256 ethMin;\n uint256 ethMax;\n\n /**\n * @dev returns the asset price in USD, 6 decimal digits.\n * Compatible with the Open Price Feed.\n */\n function price(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n return prices[keccak256(abi.encodePacked(symbol))];\n }\n\n /**\n * @dev sets the price of the asset in USD, 6 decimal digits\n *\n */\n function setPrice(string calldata symbol, uint256 _price) external {\n prices[keccak256(abi.encodePacked(symbol))] = _price;\n }\n\n /**\n * @dev sets the min and max price of ETH in USD, 6 decimal digits\n *\n */\n function setEthPriceMinMax(uint256 _min, uint256 _max) external {\n ethMin = _min;\n ethMax = _max;\n }\n\n /**\n * @dev sets the prices Min Max for a specific symbol in ETH, 8 decimal digits\n *\n */\n function setTokPriceMinMax(\n string calldata symbol,\n uint256 _min,\n uint256 _max\n ) external {\n pricesMinMax[keccak256(abi.encodePacked(symbol))] = [_min, _max];\n }\n\n /**\n * @dev get the price of asset in ETH, 8 decimal digits.\n */\n function priceMin(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n uint256[] storage pMinMax = pricesMinMax[\n keccak256(abi.encodePacked(symbol))\n ];\n return (pMinMax[0] * ethMin) / 1e6;\n }\n\n /**\n * @dev get the price of asset in USD, 8 decimal digits.\n * Not needed for now\n */\n function priceMax(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n uint256[] storage pMinMax = pricesMinMax[\n keccak256(abi.encodePacked(symbol))\n ];\n return (pMinMax[1] * ethMax) / 1e6;\n }\n}\n" + }, + "contracts/mocks/MockOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"../oracle/AbstractOracleRouter.sol\";\n\n// @notice Oracle Router required for testing environment\ncontract MockOracleRouter is AbstractOracleRouter {\n struct FeedMetadata {\n address feedAddress;\n uint256 maxStaleness;\n }\n\n mapping(address => FeedMetadata) public assetToFeedMetadata;\n\n /* @dev Override feed and maxStaleness information for a particular asset\n * @param _asset the asset to override feed for\n * @param _feed new feed\n * @param _maxStaleness new maximum time allowed for feed data to be stale\n */\n function setFeed(\n address _asset,\n address _feed,\n uint256 _maxStaleness\n ) external {\n assetToFeedMetadata[_asset] = FeedMetadata(_feed, _maxStaleness);\n }\n\n /*\n * The dev version of the Oracle doesn't need to gas optimize and cache the decimals\n */\n function getDecimals(address _feed) internal view override returns (uint8) {\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n return AggregatorV3Interface(_feed).decimals();\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n FeedMetadata storage fm = assetToFeedMetadata[asset];\n feedAddress = fm.feedAddress;\n maxStaleness = fm.maxStaleness;\n }\n}\n" + }, + "contracts/mocks/MockOracleRouterNoStale.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { OracleRouter } from \"../oracle/OracleRouter.sol\";\nimport { OETHOracleRouter } from \"../oracle/OETHOracleRouter.sol\";\n\n// @notice Oracle Router used to bypass staleness\ncontract MockOracleRouterNoStale is OracleRouter {\n function feedMetadata(address asset)\n internal\n pure\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n (feedAddress, ) = super.feedMetadata(asset);\n maxStaleness = 365 days;\n }\n}\n\n// @notice Oracle Router used to bypass staleness\ncontract MockOETHOracleRouterNoStale is OETHOracleRouter {\n constructor() OETHOracleRouter() {}\n\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n (feedAddress, ) = super.feedMetadata(asset);\n maxStaleness = 365 days;\n }\n}\n" + }, + "contracts/mocks/MockOracleWeightedPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Variable, OracleAverageQuery, IOracleWeightedPool } from \"../interfaces/balancer/IOracleWeightedPool.sol\";\n\ncontract MockOracleWeightedPool is IOracleWeightedPool {\n uint256[] public nextResults;\n\n constructor() {\n nextResults = [1 ether, 1 ether];\n }\n\n function getTimeWeightedAverage(OracleAverageQuery[] memory)\n external\n view\n override\n returns (uint256[] memory results)\n {\n return nextResults;\n }\n\n function setNextResults(uint256[] calldata results) external {\n nextResults = results;\n }\n}\n" + }, + "contracts/mocks/MockPartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PartialWithdrawal } from \"../beacon/PartialWithdrawal.sol\";\n\ncontract MockPartialWithdrawal {\n function fee() external view returns (uint256) {\n return PartialWithdrawal.fee();\n }\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n external\n returns (uint256 fee_)\n {\n return PartialWithdrawal.request(validatorPubKey, amount);\n }\n}\n" + }, + "contracts/mocks/MockRebornMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n// solhint-disable-next-line no-console\nimport \"hardhat/console.sol\";\n\ncontract Sanctum {\n address public asset;\n address public vault;\n address public reborner;\n bool public shouldAttack = false;\n // should selfdestruct in the constructor\n bool public shouldDestruct = false;\n uint256 public targetMethod;\n address public ousdContract;\n\n constructor(address _asset, address _vault) {\n asset = _asset;\n vault = _vault;\n }\n\n function deploy(uint256 salt, bytes memory bytecode)\n public\n returns (address addr)\n {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt)\n }\n require(addr != address(0), \"Create2: Failed on deploy\");\n }\n\n function computeAddress(uint256 salt, bytes memory bytecode)\n public\n view\n returns (address)\n {\n bytes32 bytecodeHashHash = keccak256(bytecode);\n bytes32 _data = keccak256(\n abi.encodePacked(\n bytes1(0xff),\n address(this),\n salt,\n bytecodeHashHash\n )\n );\n return address(bytes20(_data << 96));\n }\n\n function setShouldAttack(bool _shouldAttack) public {\n shouldAttack = _shouldAttack;\n }\n\n // should call selfdestruct in the constructor\n function setShouldDesctruct(bool _shouldDestruct) public {\n shouldDestruct = _shouldDestruct;\n }\n\n function setTargetMethod(uint256 target) public {\n targetMethod = target;\n }\n\n function setOUSDAddress(address _ousdContract) public {\n ousdContract = _ousdContract;\n }\n}\n\ncontract Reborner {\n Sanctum sanctum;\n bool logging = false;\n\n constructor(address _sanctum) {\n log(\"We are created...\");\n sanctum = Sanctum(_sanctum);\n if (sanctum.shouldAttack()) {\n log(\"We are attacking now...\");\n\n uint256 target = sanctum.targetMethod();\n\n if (target == 1) {\n redeem();\n } else if (target == 2) {\n transfer();\n } else {\n mint();\n }\n }\n\n if (sanctum.shouldDestruct()) {\n bye();\n }\n }\n\n function mint() public {\n log(\"We are attempting to mint..\");\n address asset = sanctum.asset();\n address vault = sanctum.vault();\n IERC20(asset).approve(vault, 1e18);\n IVault(vault).mint(asset, 1e18, 0);\n log(\"We are now minting..\");\n }\n\n function redeem() public {\n log(\"We are attempting to redeem..\");\n address vault = sanctum.vault();\n IVault(vault).redeem(1e18, 1e18);\n log(\"We are now redeeming..\");\n }\n\n function transfer() public {\n log(\"We are attempting to transfer..\");\n address ousd = sanctum.ousdContract();\n require(IERC20(ousd).transfer(address(1), 1e18), \"transfer failed\");\n log(\"We are now transfering..\");\n }\n\n function bye() public {\n log(\"We are now destructing..\");\n selfdestruct(payable(msg.sender));\n }\n\n function log(string memory message) internal view {\n if (logging) {\n // solhint-disable-next-line no-console\n console.log(message);\n }\n }\n}\n" + }, + "contracts/mocks/MockRETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport \"../interfaces/IGetExchangeRateToken.sol\";\n\ncontract MockRETH is MintableERC20, IGetExchangeRateToken {\n uint256 private exchangeRate = 12e17;\n\n constructor() ERC20(\"Rocket Pool ETH\", \"rETH\") {}\n\n function getExchangeRate() external view override returns (uint256) {\n return exchangeRate;\n }\n\n function setExchangeRate(uint256 _rate) external {\n exchangeRate = _rate;\n }\n}\n" + }, + "contracts/mocks/MockRoosterAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Rooster AMO strategy exposing extra functionality\n * @author Origin Protocol Inc\n */\n\nimport { RoosterAMOStrategy } from \"../strategies/plume/RoosterAMOStrategy.sol\";\nimport { IMaverickV2Pool } from \"../interfaces/plume/IMaverickV2Pool.sol\";\n\ncontract MockRoosterAMOStrategy is RoosterAMOStrategy {\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethpAddress,\n address _liquidityManager,\n address _poolLens,\n address _maverickPosition,\n address _maverickQuoter,\n address _mPool,\n bool _upperTickAtParity,\n address _votingDistributor,\n address _poolDistributor\n )\n RoosterAMOStrategy(\n _stratConfig,\n _wethAddress,\n _oethpAddress,\n _liquidityManager,\n _poolLens,\n _maverickPosition,\n _maverickQuoter,\n _mPool,\n _upperTickAtParity,\n _votingDistributor,\n _poolDistributor\n )\n {}\n\n function getCurrentWethShare() external view returns (uint256) {\n uint256 _currentPrice = getPoolSqrtPrice();\n\n return _getWethShare(_currentPrice);\n }\n}\n" + }, + "contracts/mocks/MockSFC.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockSFC {\n error ZeroAmount();\n error TransferFailed();\n error StakeIsFullySlashed();\n\n // Mapping of delegator address to validator ID to amount delegated\n mapping(address => mapping(uint256 => uint256)) public delegations;\n // Mapping of delegator address to validator ID to withdrawal request ID to amount\n mapping(address => mapping(uint256 => mapping(uint256 => uint256)))\n public withdraws;\n // validator ID -> slashing refund ratio (allows to withdraw slashed stake)\n mapping(uint256 => uint256) public slashingRefundRatio;\n\n function getStake(address delegator, uint256 validatorID)\n external\n view\n returns (uint256)\n {\n return delegations[delegator][validatorID];\n }\n\n function delegate(uint256 validatorID) external payable {\n if (msg.value == 0) {\n revert ZeroAmount();\n }\n delegations[msg.sender][validatorID] += msg.value;\n }\n\n function undelegate(\n uint256 validatorID,\n uint256 wrID,\n uint256 amount\n ) external {\n require(\n delegations[msg.sender][validatorID] >= amount,\n \"insufficient stake\"\n );\n require(\n withdraws[msg.sender][validatorID][wrID] == 0,\n \"withdrawal request already exists\"\n );\n\n delegations[msg.sender][validatorID] -= amount;\n withdraws[msg.sender][validatorID][wrID] = amount;\n }\n\n function withdraw(uint256 validatorID, uint256 wrID) external {\n require(withdraws[msg.sender][validatorID][wrID] > 0, \"no withdrawal\");\n\n uint256 withdrawAmount = withdraws[msg.sender][validatorID][wrID];\n uint256 penalty = (withdrawAmount *\n (1e18 - slashingRefundRatio[validatorID])) / 1e18;\n\n if (penalty >= withdrawAmount) {\n revert StakeIsFullySlashed();\n }\n\n (bool sent, ) = msg.sender.call{ value: withdrawAmount - penalty }(\"\");\n if (!sent) {\n revert TransferFailed();\n }\n }\n\n function pendingRewards(address delegator, uint256 validatorID)\n external\n view\n returns (uint256)\n {}\n\n function claimRewards(uint256 validatorID) external {}\n\n function restakeRewards(uint256 validatorID) external {}\n\n /// @param refundRatio the percentage of the staked amount that can be refunded. 0.1e18 = 10%, 1e18 = 100%\n function slashValidator(uint256 validatorID, uint256 refundRatio) external {\n require(refundRatio <= 1e18, \"invalid refund ratio\");\n slashingRefundRatio[validatorID] = refundRatio;\n }\n}\n" + }, + "contracts/mocks/MocksfrxETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MocksfrxETH is MintableERC20 {\n address public frxETH;\n\n constructor(address _frxETH) ERC20(\"sfrxETH\", \"sfrxETH\") {\n frxETH = _frxETH;\n }\n\n function setMockfrxETHAddress(address _frxETH) external {\n frxETH = _frxETH;\n }\n\n function deposit(uint256 assets, address receiver)\n external\n returns (uint256 shares)\n {\n ERC20(frxETH).transferFrom(msg.sender, address(this), assets);\n\n _mint(receiver, assets);\n\n return assets;\n }\n\n function maxWithdraw(address owner) external view returns (uint256) {\n return balanceOf(owner);\n }\n\n function setMaxWithdrawableBalance(address owner, uint256 balance)\n external\n {\n uint256 currentBalance = balanceOf(owner);\n if (currentBalance > balance) {\n _burn(owner, currentBalance - balance);\n } else if (balance > currentBalance) {\n _mint(owner, balance - currentBalance);\n }\n }\n\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets) {\n _burn(owner, shares);\n\n ERC20(frxETH).transfer(receiver, shares);\n\n assets = shares;\n }\n\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares) {\n _burn(owner, assets);\n\n ERC20(frxETH).transfer(receiver, assets);\n\n shares = assets;\n }\n\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares)\n {\n _mint(recipient, msg.value);\n shares = msg.value;\n }\n}\n" + }, + "contracts/mocks/MockSSV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockSSV is MintableERC20 {\n constructor() ERC20(\"SSV Token\", \"SSV\") {}\n}\n" + }, + "contracts/mocks/MockSSVNetwork.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Cluster } from \"./../interfaces/ISSVNetwork.sol\";\n\ncontract MockSSVNetwork {\n function registerValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function exitValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds\n ) external {}\n\n function removeValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external {}\n\n function deposit(\n address clusterOwner,\n uint64[] calldata operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function setFeeRecipientAddress(address recipient) external {}\n}\n" + }, + "contracts/mocks/MockstETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockstETH is MintableERC20 {\n constructor() ERC20(\"stETH\", \"stETH\") {}\n}\n" + }, + "contracts/mocks/MockStkAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"./MintableERC20.sol\";\n\ncontract MockStkAave is MintableERC20 {\n uint256 public COOLDOWN_SECONDS = 864000;\n uint256 public UNSTAKE_WINDOW = 172800;\n address public STAKED_TOKEN;\n\n mapping(address => uint256) public stakerRewardsToClaim;\n mapping(address => uint256) public stakersCooldowns;\n\n using SafeERC20 for IERC20;\n\n constructor(address _stakedToken) ERC20(\"Staked Aave\", \"stkAAVE\") {\n STAKED_TOKEN = _stakedToken;\n }\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n\n function setStakedToken(address _stakedToken) external {\n STAKED_TOKEN = _stakedToken;\n }\n\n /**\n * @dev Redeems staked tokens, and stop earning rewards\n * @param to Address to redeem to\n * @param amount Amount to redeem\n **/\n function redeem(address to, uint256 amount) external {\n uint256 cooldownStartTimestamp = stakersCooldowns[msg.sender];\n uint256 windowStart = cooldownStartTimestamp + COOLDOWN_SECONDS;\n require(amount != 0, \"INVALID_ZERO_AMOUNT\");\n require(block.timestamp > windowStart, \"INSUFFICIENT_COOLDOWN\");\n require(\n block.timestamp - windowStart <= UNSTAKE_WINDOW,\n \"UNSTAKE_WINDOW_FINISHED\"\n );\n uint256 balanceOfMessageSender = balanceOf(msg.sender);\n uint256 amountToRedeem = (amount > balanceOfMessageSender)\n ? balanceOfMessageSender\n : amount;\n\n stakersCooldowns[msg.sender] = 0;\n _burn(msg.sender, amountToRedeem);\n IERC20(STAKED_TOKEN).safeTransfer(to, amountToRedeem);\n }\n\n /**\n * @dev Activates the cooldown period to unstake\n * - It can't be called if the user is not staking\n **/\n function cooldown() external {\n require(balanceOf(msg.sender) != 0, \"INVALID_BALANCE_ON_COOLDOWN\");\n stakersCooldowns[msg.sender] = block.timestamp;\n }\n\n /**\n * @dev Test helper function to allow changing the cooldown\n **/\n function setCooldown(address account, uint256 _cooldown) external {\n stakersCooldowns[account] = _cooldown;\n }\n}\n" + }, + "contracts/mocks/MockStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract MockStrategy {\n address[] public assets;\n\n address public withdrawAllAsset;\n address public withdrawAllRecipient;\n\n constructor() {}\n\n function deposit(address asset, uint256 amount) external {}\n\n function depositAll() external {}\n\n function withdraw(\n address recipient,\n address asset,\n uint256 amount\n ) external {\n IERC20(asset).transfer(recipient, amount);\n }\n\n function withdrawAll() external {\n IERC20(withdrawAllAsset).transfer(\n withdrawAllRecipient,\n IERC20(withdrawAllAsset).balanceOf(address(this))\n );\n }\n\n function checkBalance(address asset)\n external\n view\n returns (uint256 balance)\n {\n balance = IERC20(asset).balanceOf(address(this));\n }\n\n function supportsAsset(address) external view returns (bool) {\n return true;\n }\n\n function collectRewardTokens() external {}\n\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return new address[](0);\n }\n\n function setWithdrawAll(address asset, address recipient) external {\n withdrawAllAsset = asset;\n withdrawAllRecipient = recipient;\n }\n}\n" + }, + "contracts/mocks/MockSwapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IMintableERC20 } from \"./MintableERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract MockSwapper {\n uint256 public nextOutAmount;\n\n function swap(\n // solhint-disable-next-line no-unused-vars\n address _fromAsset,\n address _toAsset,\n // solhint-disable-next-line no-unused-vars\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n // solhint-disable-next-line no-unused-vars\n bytes calldata _data\n ) external returns (uint256 toAssetAmount) {\n toAssetAmount = (nextOutAmount > 0) ? nextOutAmount : _minToAssetAmount;\n nextOutAmount = 0;\n IMintableERC20(_toAsset).mint(toAssetAmount);\n IERC20(_toAsset).transfer(msg.sender, toAssetAmount);\n }\n\n function setNextOutAmount(uint256 _nextOutAmount) public {\n nextOutAmount = _nextOutAmount;\n }\n}\n" + }, + "contracts/mocks/MockTUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockTUSD is MintableERC20 {\n constructor() ERC20(\"TrueUSD\", \"TUSD\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/MockUniswapPair.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IUniswapV2Pair } from \"../interfaces/uniswap/IUniswapV2Pair.sol\";\n\ncontract MockUniswapPair is IUniswapV2Pair {\n address tok0;\n address tok1;\n uint112 reserve0;\n uint112 reserve1;\n uint256 blockTimestampLast;\n\n bool public hasSynced = false;\n\n constructor(\n address _token0,\n address _token1,\n uint112 _reserve0,\n uint112 _reserve1\n ) {\n tok0 = _token0;\n tok1 = _token1;\n reserve0 = _reserve0;\n reserve1 = _reserve1;\n blockTimestampLast = block.timestamp;\n }\n\n function token0() external view override returns (address) {\n return tok0;\n }\n\n function token1() external view override returns (address) {\n return tok1;\n }\n\n function getReserves()\n external\n view\n override\n returns (\n uint112,\n uint112,\n uint32\n )\n {\n return (reserve0, reserve1, uint32(blockTimestampLast));\n }\n\n function setReserves(uint112 _reserve0, uint112 _reserve1) public {\n reserve0 = _reserve0;\n reserve1 = _reserve1;\n blockTimestampLast = block.timestamp;\n }\n\n // CAUTION This will not work if you setReserves multiple times over\n // multiple different blocks because then it wouldn't be a continuous\n // reserve factor over that blockTimestamp, this assumes an even reserve\n // ratio all the way through\n function price0CumulativeLast() external view override returns (uint256) {\n return\n uint256(FixedPoint.fraction(reserve1, reserve0)._x) *\n blockTimestampLast;\n }\n\n function price1CumulativeLast() external view override returns (uint256) {\n return\n uint256(FixedPoint.fraction(reserve0, reserve1)._x) *\n blockTimestampLast;\n }\n\n function sync() external override {\n hasSynced = true;\n }\n\n function checkHasSynced() external view {\n require(hasSynced, \"Not synced\");\n }\n}\n\n// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))\nlibrary FixedPoint {\n // range: [0, 2**112 - 1]\n // resolution: 1 / 2**112\n struct uq112x112 {\n uint224 _x;\n }\n\n // returns a uq112x112 which represents the ratio of the numerator to the denominator\n // equivalent to encode(numerator).div(denominator)\n function fraction(uint112 numerator, uint112 denominator)\n internal\n pure\n returns (uq112x112 memory)\n {\n require(denominator > 0, \"FixedPoint: DIV_BY_ZERO\");\n return uq112x112((uint224(numerator) << 112) / denominator);\n }\n}\n" + }, + "contracts/mocks/MockUniswapRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { IUniswapV2Router } from \"../interfaces/uniswap/IUniswapV2Router02.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockUniswapRouter is IUniswapV2Router {\n using StableMath for uint256;\n\n mapping(address => address) public pairMaps;\n uint256 public slippage = 1 ether;\n\n function initialize(\n address[] calldata _0tokens,\n address[] calldata _1tokens\n ) public {\n require(\n _0tokens.length == _1tokens.length,\n \"Mock token pairs should be of the same length\"\n );\n for (uint256 i = 0; i < _0tokens.length; i++) {\n pairMaps[_0tokens[i]] = _1tokens[i];\n }\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n\n function swapExactTokensForTokens(\n uint256 amountIn,\n uint256 amountOutMin,\n address[] calldata path,\n address to,\n // solhint-disable-next-line no-unused-vars\n uint256\n ) external override returns (uint256[] memory amountsOut) {\n address tok0 = path[0];\n address tok1 = path[path.length - 1];\n\n uint256 amountOut = (amountOutMin * slippage) / 1 ether;\n require(amountOut >= amountOutMin, \"Slippage error\");\n\n IERC20(tok0).transferFrom(msg.sender, address(this), amountIn);\n MintableERC20(tok1).mintTo(to, amountOut);\n\n amountsOut = new uint256[](path.length);\n amountsOut[path.length - 1] = amountOut;\n }\n\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut)\n {\n (address tok0, address tok1) = _getFirstAndLastToken(params.path);\n\n amountOut = (params.amountOutMinimum * slippage) / 1 ether;\n\n IERC20(tok0).transferFrom(msg.sender, address(this), params.amountIn);\n MintableERC20(tok1).mintTo(params.recipient, amountOut);\n\n require(\n amountOut >= params.amountOutMinimum,\n \"UniswapMock: amountOut less than amountOutMinimum\"\n );\n return amountOut;\n }\n\n function addLiquidity(\n address tokenA,\n address tokenB,\n uint256 amountADesired,\n uint256 amountBDesired,\n uint256 amountAMin,\n uint256 amountBMin,\n address to,\n uint256 deadline\n )\n external\n override\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 liquidity\n )\n {\n // this is needed to make this contract whole else it'd be just virtual\n }\n\n function WETH() external pure override returns (address) {\n return address(0);\n }\n\n // Universal router mock\n function execute(\n bytes calldata,\n bytes[] calldata inputs,\n uint256\n ) external payable {\n uint256 inLen = inputs.length;\n for (uint256 i = 0; i < inLen; ++i) {\n (\n address recipient,\n ,\n uint256 amountOutMinimum,\n bytes memory path,\n\n ) = abi.decode(inputs[i], (address, uint256, uint256, bytes, bool));\n\n (address token0, address token1) = _getFirstAndLastToken(path);\n\n amountOutMinimum = amountOutMinimum.scaleBy(\n Helpers.getDecimals(token0),\n Helpers.getDecimals(token1)\n );\n\n MintableERC20(token1).mintTo(recipient, amountOutMinimum);\n }\n }\n\n function _getFirstAndLastToken(bytes memory path)\n internal\n view\n returns (address token0, address token1)\n {\n bytes memory tok0Bytes = new bytes(20);\n for (uint256 j = 0; j < 20; ++j) {\n tok0Bytes[j] = path[j];\n }\n token0 = address(bytes20(tok0Bytes));\n\n if (pairMaps[token0] != address(0)) {\n token0 = pairMaps[token0];\n }\n\n bytes memory tok1Bytes = new bytes(20);\n uint256 tok1Offset = path.length - 20;\n for (uint256 j = 0; j < 20; ++j) {\n tok1Bytes[j] = path[j + tok1Offset];\n }\n token1 = address(bytes20(tok1Bytes));\n\n if (pairMaps[token1] != address(0)) {\n token1 = pairMaps[token1];\n }\n }\n}\n" + }, + "contracts/mocks/MockUSDC.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDC is MintableERC20 {\n constructor() ERC20(\"USDC Coin\", \"USDC\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n}\n" + }, + "contracts/mocks/MockUSDS.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDS is MintableERC20 {\n constructor() ERC20(\"USDS\", \"USDS\") {}\n}\n" + }, + "contracts/mocks/MockUSDT.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDT is MintableERC20 {\n constructor() ERC20(\"USDT Coin\", \"USDT\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n}\n" + }, + "contracts/mocks/MockVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultCore } from \"../vault/VaultCore.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { VaultInitializer } from \"../vault/VaultInitializer.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MockVault is VaultCore {\n using StableMath for uint256;\n\n uint256 storedTotalValue;\n\n function setTotalValue(uint256 _value) public {\n storedTotalValue = _value;\n }\n\n function totalValue() external view override returns (uint256) {\n return storedTotalValue;\n }\n\n function _totalValue() internal view override returns (uint256) {\n return storedTotalValue;\n }\n\n function _checkBalance(address _asset)\n internal\n view\n override\n returns (uint256 balance)\n {\n // Avoids rounding errors by returning the total value\n // in a single currency\n if (allAssets[0] == _asset) {\n uint256 decimals = Helpers.getDecimals(_asset);\n return storedTotalValue.scaleBy(decimals, 18);\n } else {\n return 0;\n }\n }\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external onlyGovernor {\n maxSupplyDiff = _maxSupplyDiff;\n }\n}\n" + }, + "contracts/mocks/MockVaultCoreInstantRebase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultCore } from \"../vault/VaultCore.sol\";\n\ncontract MockVaultCoreInstantRebase is VaultCore {\n function _nextYield(uint256 supply, uint256 vaultValue)\n internal\n view\n override\n returns (uint256 yield, uint256 targetRate)\n {\n if (vaultValue <= supply) {\n return (0, 0);\n }\n yield = vaultValue - supply;\n return (yield, 0);\n }\n}\n" + }, + "contracts/mocks/MockWETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\n// Just importing to \"unbreak\" coverage tests\nimport { IERC721Receiver } from \"@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol\";\n\ncontract MockWETH is MintableERC20 {\n constructor() ERC20(\"WETH\", \"WETH\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/MockWS.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockWS is MintableERC20 {\n constructor() ERC20(\"Wrapped Sonic\", \"wS\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/TestUpgradedOUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../token/OUSD.sol\";\n\n// used to alter internal state of OUSD contract\ncontract TestUpgradedOUSD is OUSD {\n constructor() OUSD() {}\n\n function overwriteCreditBalances(address _account, uint256 _creditBalance)\n public\n {\n creditBalances[_account] = _creditBalance;\n }\n\n function overwriteAlternativeCPT(address _account, uint256 _acpt) public {\n alternativeCreditsPerToken[_account] = _acpt;\n }\n\n function overwriteRebaseState(address _account, RebaseOptions _rebaseOption)\n public\n {\n rebaseState[_account] = _rebaseOption;\n }\n}\n" + }, + "contracts/oracle/AbstractOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\n// @notice Abstract functionality that is shared between various Oracle Routers\nabstract contract AbstractOracleRouter is IOracle {\n using StableMath for uint256;\n using SafeCast for int256;\n\n uint256 internal constant MIN_DRIFT = 0.7e18;\n uint256 internal constant MAX_DRIFT = 1.3e18;\n address internal constant FIXED_PRICE =\n 0x0000000000000000000000000000000000000001;\n // Maximum allowed staleness buffer above normal Oracle maximum staleness\n uint256 internal constant STALENESS_BUFFER = 1 days;\n mapping(address => uint8) internal decimalsCache;\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n returns (address feedAddress, uint256 maxStaleness);\n\n /**\n * @notice Returns the total price in 18 digit unit for a given asset.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n if (shouldBePegged(asset)) {\n require(_price <= MAX_DRIFT, \"Oracle: Price exceeds max\");\n require(_price >= MIN_DRIFT, \"Oracle: Price under min\");\n }\n return _price;\n }\n\n function getDecimals(address _feed) internal view virtual returns (uint8) {\n uint8 decimals = decimalsCache[_feed];\n require(decimals > 0, \"Oracle: Decimals not cached\");\n return decimals;\n }\n\n /**\n * @notice Before an asset/feed price is fetches for the first time the\n * decimals need to be cached. This is a gas optimization\n * @param asset address of the asset\n * @return uint8 corresponding asset decimals\n */\n function cacheDecimals(address asset) external returns (uint8) {\n (address _feed, ) = feedMetadata(asset);\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n uint8 decimals = AggregatorV3Interface(_feed).decimals();\n decimalsCache[_feed] = decimals;\n return decimals;\n }\n\n function shouldBePegged(address _asset) internal view returns (bool) {\n string memory symbol = Helpers.getSymbol(_asset);\n bytes32 symbolHash = keccak256(abi.encodePacked(symbol));\n return\n symbolHash == keccak256(abi.encodePacked(\"DAI\")) ||\n symbolHash == keccak256(abi.encodePacked(\"USDC\")) ||\n symbolHash == keccak256(abi.encodePacked(\"USDT\"));\n }\n}\n" + }, + "contracts/oracle/OETHBaseOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\n// @notice Oracle Router (for OETH on Base) that denominates all prices in ETH\ncontract OETHBaseOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n using SafeCast for int256;\n\n address constant WETH = 0x4200000000000000000000000000000000000006;\n address constant WOETH = 0xD8724322f44E5c58D7A815F542036fb17DbbF839;\n address constant WOETH_CHAINLINK_FEED =\n 0xe96EB1EDa83d18cbac224233319FA5071464e1b9;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == WETH) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == WOETH) {\n // Chainlink: https://data.chain.link/feeds/base/base/woeth-oeth-exchange-rate\n // Bridged wOETH/OETH\n feedAddress = WOETH_CHAINLINK_FEED;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OETHFixedOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHOracleRouter } from \"./OETHOracleRouter.sol\";\n\n// @notice Oracle Router that returns 1e18 for all prices\n// used solely for deployment to testnets\ncontract OETHFixedOracle is OETHOracleRouter {\n constructor() OETHOracleRouter() {}\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n // solhint-disable-next-line no-unused-vars\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n // fixes price for all of the assets\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n }\n}\n" + }, + "contracts/oracle/OETHOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// @notice Oracle Router that denominates all prices in ETH\ncontract OETHOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = uint256(_iprice).scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == 0x5E8422345238F34275888049021821E8E08CAa1f) {\n // frxETH/ETH\n feedAddress = 0xC58F3385FBc1C8AD2c0C9a061D7c13b141D7A5Df;\n maxStaleness = 18 hours + STALENESS_BUFFER;\n } else if (asset == 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/steth-eth\n // Chainlink: stETH/ETH\n feedAddress = 0x86392dC19c0b719886221c78AB11eb8Cf5c52812;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xae78736Cd615f374D3085123A210448E74Fc6393) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/reth-eth\n // Chainlink: rETH/ETH\n feedAddress = 0x536218f9E9Eb48863970252233c8F271f554C2d0;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xD533a949740bb3306d119CC777fa900bA034cd52) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/crv-eth\n // Chainlink: CRV/ETH\n feedAddress = 0x8a12Be339B0cD1829b91Adc01977caa5E9ac121e;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/cvx-eth\n // Chainlink: CVX/ETH\n feedAddress = 0xC9CbF687f43176B302F03f5e58470b77D07c61c6;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xBe9895146f7AF43049ca1c1AE358B0541Ea49704) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/cbeth-eth\n // Chainlink: cbETH/ETH\n feedAddress = 0xF017fcB346A1885194689bA23Eff2fE6fA5C483b;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xba100000625a3754423978a60c9317c58a424e3D) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/bal-eth\n // Chainlink: BAL/ETH\n feedAddress = 0xC1438AA3823A6Ba0C159CfA8D98dF5A994bA120b;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OETHPlumeOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\n// @notice Oracle Router (for OETH on Plume) that denominates all prices in ETH\ncontract OETHPlumeOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n using SafeCast for int256;\n\n address constant WETH = 0xca59cA09E5602fAe8B629DeE83FfA819741f14be;\n address constant WOETH = 0xD8724322f44E5c58D7A815F542036fb17DbbF839;\n // Ref: https://docs.eo.app/docs/eprice/feed-addresses/plume\n address constant WOETH_ORACLE_FEED =\n 0x4915600Ed7d85De62011433eEf0BD5399f677e9b;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == WETH) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == WOETH) {\n // Chainlink: https://data.chain.link/feeds/base/base/woeth-oeth-exchange-rate\n // Bridged wOETH/OETH\n feedAddress = WOETH_ORACLE_FEED;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\n\n// @notice Oracle Router that denominates all prices in USD\ncontract OracleRouter is AbstractOracleRouter {\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n pure\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n /* + STALENESS_BUFFER is added in case Oracle for some reason doesn't\n * update on heartbeat and we add a generous buffer amount.\n */\n if (asset == 0x6B175474E89094C44Da98b954EedeAC495271d0F) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/dai-usd\n // Chainlink: DAI/USD\n feedAddress = 0xAed0c38402a5d19df6E4c03F4E2DceD6e29c1ee9;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xdC035D45d973E3EC169d2276DDab16f1e407384F) {\n // https://data.chain.link/feeds/ethereum/mainnet/usds-usd\n // Chainlink: USDS/USD\n feedAddress = 0xfF30586cD0F29eD462364C7e81375FC0C71219b1;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/usdc-usd\n // Chainlink: USDC/USD\n feedAddress = 0x8fFfFfd4AfB6115b954Bd326cbe7B4BA576818f6;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xdAC17F958D2ee523a2206206994597C13D831ec7) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/usdt-usd\n // Chainlink: USDT/USD\n feedAddress = 0x3E7d1eAB13ad0104d2750B8863b489D65364e32D;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xc00e94Cb662C3520282E6f5717214004A7f26888) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/comp-usd\n // Chainlink: COMP/USD\n feedAddress = 0xdbd020CAeF83eFd542f4De03e3cF0C28A4428bd5;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/aave-usd\n // Chainlink: AAVE/USD\n feedAddress = 0x547a514d5e3769680Ce22B2361c10Ea13619e8a9;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xD533a949740bb3306d119CC777fa900bA034cd52) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/crv-usd\n // Chainlink: CRV/USD\n feedAddress = 0xCd627aA160A6fA45Eb793D19Ef54f5062F20f33f;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B) {\n // Chainlink: CVX/USD\n feedAddress = 0xd962fC30A72A84cE50161031391756Bf2876Af5D;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OSonicOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHFixedOracle } from \"./OETHFixedOracle.sol\";\n\n// @notice Oracle Router that returns 1e18 for all prices\n// used solely for deployment to testnets\ncontract OSonicOracleRouter is OETHFixedOracle {\n constructor() OETHFixedOracle() {}\n}\n" + }, + "contracts/poolBooster/AbstractPoolBoosterFactory.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IPoolBoostCentralRegistry } from \"../interfaces/poolBooster/IPoolBoostCentralRegistry.sol\";\n\n/**\n * @title Abstract Pool booster factory\n * @author Origin Protocol Inc\n */\ncontract AbstractPoolBoosterFactory is Governable {\n struct PoolBoosterEntry {\n address boosterAddress;\n address ammPoolAddress;\n IPoolBoostCentralRegistry.PoolBoosterType boosterType;\n }\n\n // @notice address of Origin Sonic\n address public immutable oSonic;\n // @notice Central registry contract\n IPoolBoostCentralRegistry public immutable centralRegistry;\n\n // @notice list of all the pool boosters created by this factory\n PoolBoosterEntry[] public poolBoosters;\n // @notice mapping of AMM pool to pool booster\n mapping(address => PoolBoosterEntry) public poolBoosterFromPool;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) {\n require(_oSonic != address(0), \"Invalid oSonic address\");\n require(_governor != address(0), \"Invalid governor address\");\n require(\n _centralRegistry != address(0),\n \"Invalid central registry address\"\n );\n\n oSonic = _oSonic;\n centralRegistry = IPoolBoostCentralRegistry(_centralRegistry);\n _setGovernor(_governor);\n }\n\n /**\n * @notice Goes over all the pool boosters created by this factory and\n * calls bribe() on them.\n * @param _exclusionList A list of pool booster addresses to skip when\n * calling this function.\n */\n function bribeAll(address[] memory _exclusionList) external {\n uint256 lengthI = poolBoosters.length;\n for (uint256 i = 0; i < lengthI; i++) {\n address poolBoosterAddress = poolBoosters[i].boosterAddress;\n bool skipBribeCall = false;\n uint256 lengthJ = _exclusionList.length;\n for (uint256 j = 0; j < lengthJ; j++) {\n // pool booster in exclusion list\n if (_exclusionList[j] == poolBoosterAddress) {\n skipBribeCall = true;\n break;\n }\n }\n\n if (!skipBribeCall) {\n IPoolBooster(poolBoosterAddress).bribe();\n }\n }\n }\n\n /**\n * @notice Removes the pool booster from the internal list of pool boosters.\n * @dev This action does not destroy the pool booster contract nor does it\n * stop the yield delegation to it.\n * @param _poolBoosterAddress address of the pool booster\n */\n function removePoolBooster(address _poolBoosterAddress)\n external\n onlyGovernor\n {\n uint256 boostersLen = poolBoosters.length;\n for (uint256 i = 0; i < boostersLen; ++i) {\n if (poolBoosters[i].boosterAddress == _poolBoosterAddress) {\n // erase mapping\n delete poolBoosterFromPool[poolBoosters[i].ammPoolAddress];\n\n // overwrite current pool booster with the last entry in the list\n poolBoosters[i] = poolBoosters[boostersLen - 1];\n // drop the last entry\n poolBoosters.pop();\n\n centralRegistry.emitPoolBoosterRemoved(_poolBoosterAddress);\n break;\n }\n }\n }\n\n function _storePoolBoosterEntry(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType _boosterType\n ) internal {\n PoolBoosterEntry memory entry = PoolBoosterEntry(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType\n );\n\n poolBoosters.push(entry);\n poolBoosterFromPool[_ammPoolAddress] = entry;\n\n // emit the events of the pool booster created\n centralRegistry.emitPoolBoosterCreated(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType\n );\n }\n\n function _deployContract(bytes memory _bytecode, uint256 _salt)\n internal\n returns (address _address)\n {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _address := create2(\n 0,\n add(_bytecode, 0x20),\n mload(_bytecode),\n _salt\n )\n }\n\n require(\n _address.code.length > 0 && _address != address(0),\n \"Failed creating a pool booster\"\n );\n }\n\n // pre-compute the address of the deployed contract that will be\n // created when create2 is called\n function _computeAddress(bytes memory _bytecode, uint256 _salt)\n internal\n view\n returns (address)\n {\n bytes32 hash = keccak256(\n abi.encodePacked(\n bytes1(0xff),\n address(this),\n _salt,\n keccak256(_bytecode)\n )\n );\n\n // cast last 20 bytes of hash to address\n return address(uint160(uint256(hash)));\n }\n\n function poolBoosterLength() external view returns (uint256) {\n return poolBoosters.length;\n }\n}\n" + }, + "contracts/poolBooster/PoolBoostCentralRegistry.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IPoolBoostCentralRegistry } from \"../interfaces/poolBooster/IPoolBoostCentralRegistry.sol\";\n\n/**\n * @title Contract that holds all governance approved pool booster Factory\n * implementation deployments\n * @author Origin Protocol Inc\n */\ncontract PoolBoostCentralRegistry is Governable, IPoolBoostCentralRegistry {\n event FactoryApproved(address factoryAddress);\n event FactoryRemoved(address factoryAddress);\n\n // @notice List of approved factories\n address[] public factories;\n\n modifier onlyApprovedFactories() {\n require(isApprovedFactory(msg.sender), \"Not an approved factory\");\n _;\n }\n\n constructor() {\n // set the governor of the implementation contract to zero address\n _setGovernor(address(0));\n }\n\n /**\n * @notice Adds a factory address to the approved factory addresses\n * @param _factoryAddress address of the factory\n */\n function approveFactory(address _factoryAddress) external onlyGovernor {\n require(_factoryAddress != address(0), \"Invalid address\");\n require(\n !isApprovedFactory(_factoryAddress),\n \"Factory already approved\"\n );\n\n factories.push(_factoryAddress);\n emit FactoryApproved(_factoryAddress);\n }\n\n /**\n * @notice Removes the factory from approved factory addresses\n * @param _factoryAddress address of the factory\n */\n function removeFactory(address _factoryAddress) external onlyGovernor {\n require(_factoryAddress != address(0), \"Invalid address\");\n\n uint256 length = factories.length;\n bool factoryRemoved = false;\n for (uint256 i = 0; i < length; i++) {\n if (factories[i] != _factoryAddress) {\n continue;\n }\n\n factories[i] = factories[length - 1];\n factories.pop();\n emit FactoryRemoved(_factoryAddress);\n factoryRemoved = true;\n break;\n }\n require(factoryRemoved, \"Not an approved factory\");\n\n emit FactoryRemoved(_factoryAddress);\n }\n\n /**\n * @notice Emits a pool booster created event\n * @dev This has been created as a convenience method for the monitoring to have\n * an index of all of the created pool boosters by only listening to the\n * events of this contract.\n * @param _poolBoosterAddress address of the pool booster created\n * @param _ammPoolAddress address of the AMM pool forwarding yield to the pool booster\n * @param _boosterType PoolBoosterType the type of the pool booster\n */\n function emitPoolBoosterCreated(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n PoolBoosterType _boosterType\n ) external onlyApprovedFactories {\n emit PoolBoosterCreated(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType,\n msg.sender // address of the factory\n );\n }\n\n /**\n * @notice Emits a pool booster removed event\n * @dev This has been created as a convenience method for the monitoring to have\n * an index of all of the removed pool boosters by only listening to the\n * events of this contract.\n * @param _poolBoosterAddress address of the pool booster to be removed\n */\n function emitPoolBoosterRemoved(address _poolBoosterAddress)\n external\n onlyApprovedFactories\n {\n emit PoolBoosterRemoved(_poolBoosterAddress);\n }\n\n /**\n * @notice Returns true if the factory is approved\n * @param _factoryAddress address of the factory\n */\n function isApprovedFactory(address _factoryAddress)\n public\n view\n returns (bool)\n {\n uint256 length = factories.length;\n for (uint256 i = 0; i < length; i++) {\n if (factories[i] == _factoryAddress) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * @notice Returns all supported factories\n */\n function getAllFactories() external view returns (address[] memory) {\n return factories;\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactoryMerkl.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterMerkl } from \"./PoolBoosterMerkl.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Merkl pool boosters.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactoryMerkl is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n /// @notice address of the Merkl distributor\n address public merklDistributor;\n\n /// @notice event emitted when the Merkl distributor is updated\n event MerklDistributorUpdated(address newDistributor);\n\n /**\n * @param _oSonic address of the OSonic token\n * @param _governor address governor\n * @param _centralRegistry address of the central registry\n * @param _merklDistributor address of the Merkl distributor\n */\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry,\n address _merklDistributor\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {\n _setMerklDistributor(_merklDistributor);\n }\n\n /**\n * @dev Create a Pool Booster for Merkl.\n * @param _campaignType The type of campaign to create. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _campaignDuration The duration of the campaign in seconds\n * @param campaignData The data to be used for the campaign. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * This should be fetched from the Merkl UI.\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterMerkl(\n uint32 _campaignType,\n address _ammPoolAddress,\n uint32 _campaignDuration,\n bytes calldata campaignData,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n require(_campaignDuration > 1 hours, \"Invalid campaign duration\");\n require(campaignData.length > 0, \"Invalid campaign data\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterMerkl).creationCode,\n abi.encode(\n oSonic,\n merklDistributor,\n _campaignDuration,\n _campaignType,\n governor(),\n campaignData\n )\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.MerklBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for Merkl.\n * @param _campaignType The type of campaign to create. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterMerkl` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n uint32 _campaignType,\n address _ammPoolAddress,\n uint32 _campaignDuration,\n bytes calldata campaignData,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n require(_campaignDuration > 1 hours, \"Invalid campaign duration\");\n require(campaignData.length > 0, \"Invalid campaign data\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterMerkl).creationCode,\n abi.encode(\n oSonic,\n merklDistributor,\n _campaignDuration,\n _campaignType,\n governor(),\n campaignData\n )\n ),\n _salt\n );\n }\n\n /**\n * @dev Set the address of the Merkl distributor\n * @param _merklDistributor The address of the Merkl distributor\n */\n function setMerklDistributor(address _merklDistributor)\n external\n onlyGovernor\n {\n _setMerklDistributor(_merklDistributor);\n }\n\n function _setMerklDistributor(address _merklDistributor) internal {\n require(\n _merklDistributor != address(0),\n \"Invalid merklDistributor address\"\n );\n merklDistributor = _merklDistributor;\n emit MerklDistributorUpdated(_merklDistributor);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactoryMetropolis.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterMetropolis } from \"./PoolBoosterMetropolis.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Metropolis pool boosters.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactoryMetropolis is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n address public immutable rewardFactory;\n address public immutable voter;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n // @param address _rewardFactory address of the Metropolis reward factory\n // @param address _voter address of the Metropolis voter\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry,\n address _rewardFactory,\n address _voter\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {\n rewardFactory = _rewardFactory;\n voter = _voter;\n }\n\n /**\n * @dev Create a Pool Booster for Metropolis pool.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterMetropolis(address _ammPoolAddress, uint256 _salt)\n external\n onlyGovernor\n {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterMetropolis).creationCode,\n abi.encode(oSonic, rewardFactory, _ammPoolAddress, voter)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.MetropolisBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for Metropolis pool.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterMetropolis` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(address _ammPoolAddress, uint256 _salt)\n external\n view\n returns (address)\n {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterMetropolis).creationCode,\n abi.encode(oSonic, rewardFactory, _ammPoolAddress, voter)\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactorySwapxDouble.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterSwapxDouble } from \"./PoolBoosterSwapxDouble.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Swapx Ichi pool boosters where both of the\n * gauges need incentivizing.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactorySwapxDouble is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {}\n\n /**\n * @dev Create a Pool Booster for SwapX Ichi vault based pool where 2 Bribe contracts need to be\n * bribed\n * @param _bribeAddressOS address of the Bribes.sol(Bribe) contract for the OS token side\n * @param _bribeAddressOther address of the Bribes.sol(Bribe) contract for the other token in the pool\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _split 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n * bribe contract and 60% to other bribe contract\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterSwapxDouble(\n address _bribeAddressOS,\n address _bribeAddressOther,\n address _ammPoolAddress,\n uint256 _split,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterSwapxDouble).creationCode,\n abi.encode(_bribeAddressOS, _bribeAddressOther, oSonic, _split)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.SwapXDoubleBooster\n );\n }\n\n /**\n * @dev Compute the address of the pool booster to be deployed.\n * @param _bribeAddressOS address of the Bribes.sol(Bribe) contract for the OS token side\n * @param _bribeAddressOther address of the Bribes.sol(Bribe) contract for the other token in the pool\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _split 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n * bribe contract and 60% to other bribe contract\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterSwapxDouble` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n address _bribeAddressOS,\n address _bribeAddressOther,\n address _ammPoolAddress,\n uint256 _split,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterSwapxDouble).creationCode,\n abi.encode(\n _bribeAddressOS,\n _bribeAddressOther,\n oSonic,\n _split\n )\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactorySwapxSingle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterSwapxSingle } from \"./PoolBoosterSwapxSingle.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Swapx Single pool boosters - where a single\n * gauge is created for a pool. this is appropriate for Classic Stable & Classic\n * Volatile SwapX pools.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactorySwapxSingle is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {}\n\n /**\n * @dev Create a Pool Booster for SwapX classic volatile or classic stable pools where\n * a single Bribe contract is incentivized.\n * @param _bribeAddress address of the Bribes.sol contract\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterSwapxSingle(\n address _bribeAddress,\n address _ammPoolAddress,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterSwapxSingle).creationCode,\n abi.encode(_bribeAddress, oSonic)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.SwapXSingleBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for SwapX classic volatile or classic stable pools where\n * a single Bribe contract is incentivized.\n * @param _bribeAddress address of the Bribes.sol contract\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterSwapxSingle` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n address _bribeAddress,\n address _ammPoolAddress,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterSwapxSingle).creationCode,\n abi.encode(_bribeAddress, oSonic)\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterMerkl.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IMerklDistributor } from \"../interfaces/poolBooster/IMerklDistributor.sol\";\n\ninterface IERC1271 {\n /**\n * @dev Should return whether the signature provided is valid for the provided data\n * @param hash Hash of the data to be signed\n * @param signature Signature byte array associated with _data\n */\n function isValidSignature(bytes32 hash, bytes memory signature)\n external\n view\n returns (bytes4 magicValue);\n}\n\n/**\n * @title Pool booster for Merkl distributor\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterMerkl is IPoolBooster, IERC1271 {\n /// @notice address of merkl distributor\n IMerklDistributor public immutable merklDistributor;\n /// @notice address of the OS token\n IERC20 public immutable rewardToken;\n /// @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n /// @notice Campaign duration in seconds\n uint32 public immutable duration; // -> should be immutable\n /// @notice Campaign type\n uint32 public immutable campaignType;\n /// @notice Owner of the campaign\n address public immutable creator;\n /// @notice Campaign data\n bytes public campaignData;\n\n constructor(\n address _rewardToken,\n address _merklDistributor,\n uint32 _duration,\n uint32 _campaignType,\n address _creator,\n bytes memory _campaignData\n ) {\n require(_rewardToken != address(0), \"Invalid rewardToken address\");\n require(\n _merklDistributor != address(0),\n \"Invalid merklDistributor address\"\n );\n require(_campaignData.length > 0, \"Invalid campaignData\");\n require(_duration > 1 hours, \"Invalid duration\");\n\n campaignType = _campaignType;\n duration = _duration;\n creator = _creator;\n\n merklDistributor = IMerklDistributor(_merklDistributor);\n rewardToken = IERC20(_rewardToken);\n campaignData = _campaignData;\n }\n\n /// @notice Create a campaign on the Merkl distributor\n function bribe() external override {\n // Ensure token is approved for the Merkl distributor\n uint256 minAmount = merklDistributor.rewardTokenMinAmounts(\n address(rewardToken)\n );\n require(minAmount > 0, \"Min reward amount must be > 0\");\n\n // if balance too small or below threshold, do no bribes\n uint256 balance = rewardToken.balanceOf(address(this));\n if (\n balance < MIN_BRIBE_AMOUNT ||\n (balance * 1 hours < minAmount * duration)\n ) {\n return;\n }\n\n // Approve the bribe contract to spend the reward token\n rewardToken.approve(address(merklDistributor), balance);\n\n // Notify the bribe contract of the reward amount\n merklDistributor.signAndCreateCampaign(\n IMerklDistributor.CampaignParameters({\n campaignId: bytes32(0),\n creator: creator,\n rewardToken: address(rewardToken),\n amount: balance,\n campaignType: campaignType,\n startTimestamp: getNextPeriodStartTime(),\n duration: duration,\n campaignData: campaignData\n }),\n bytes(\"\")\n );\n emit BribeExecuted(balance);\n }\n\n /// @notice Used to sign a campaign on the Merkl distributor\n function isValidSignature(bytes32, bytes memory)\n external\n view\n override\n returns (bytes4 magicValue)\n {\n require(msg.sender == address(merklDistributor), \"Invalid sender\");\n // bytes4(keccak256(\"isValidSignature(bytes32,bytes)\")) == 0x1626ba7e\n return bytes4(0x1626ba7e);\n }\n\n /// @notice Returns the timestamp for the start of the next period based on the configured duration\n function getNextPeriodStartTime() public view returns (uint32) {\n // Calculate the timestamp for the next period boundary\n return uint32((block.timestamp / duration + 1) * duration);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterMetropolis.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @title Pool booster for Metropolis pools\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterMetropolis is IPoolBooster {\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice address of the pool\n address public immutable pool;\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n IRewarderFactory public immutable rewardFactory;\n\n IVoter public immutable voter;\n\n constructor(\n address _osToken,\n address _rewardFactory,\n address _pool,\n address _voter\n ) {\n require(_pool != address(0), \"Invalid pool address\");\n pool = _pool;\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n\n rewardFactory = IRewarderFactory(_rewardFactory);\n\n voter = IVoter(_voter);\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n (, uint256 minBribeAmount) = rewardFactory.getWhitelistedTokenInfo(\n address(osToken)\n );\n if (balance < MIN_BRIBE_AMOUNT || balance < minBribeAmount) {\n return;\n }\n\n uint256 id = voter.getCurrentVotingPeriod() + 1;\n\n // Deploy a rewarder\n IRewarder rewarder = IRewarder(\n rewardFactory.createBribeRewarder(address(osToken), pool)\n );\n\n // Approve the rewarder to spend the balance\n osToken.approve(address(rewarder), balance);\n\n // Fund and bribe the rewarder\n rewarder.fundAndBribe(id, id, balance);\n\n emit BribeExecuted(balance);\n }\n}\n\ninterface IRewarderFactory {\n function createBribeRewarder(address token, address pool)\n external\n returns (address rewarder);\n\n function getWhitelistedTokenInfo(address token)\n external\n view\n returns (bool isWhitelisted, uint256 minBribeAmount);\n}\n\ninterface IRewarder {\n function fundAndBribe(\n uint256 startId,\n uint256 lastId,\n uint256 amountPerPeriod\n ) external payable;\n}\n\ninterface IVoter {\n function getCurrentVotingPeriod() external view returns (uint256);\n}\n" + }, + "contracts/poolBooster/PoolBoosterSwapxDouble.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBribe } from \"../interfaces/poolBooster/ISwapXAlgebraBribe.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n/**\n * @title Pool booster for SwapX concentrated liquidity where 2 gauges are created for\n * every pool. Ichi vaults currently have such setup.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterSwapxDouble is IPoolBooster {\n using StableMath for uint256;\n\n // @notice address of the Bribes.sol(Bribe) contract for the OS token side\n IBribe public immutable bribeContractOS;\n // @notice address of the Bribes.sol(Bribe) contract for the other token in the pool\n IBribe public immutable bribeContractOther;\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n // bribe contract and 60% to other bribe contract\n uint256 public immutable split;\n\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n constructor(\n address _bribeContractOS,\n address _bribeContractOther,\n address _osToken,\n uint256 _split\n ) {\n require(\n _bribeContractOS != address(0),\n \"Invalid bribeContractOS address\"\n );\n require(\n _bribeContractOther != address(0),\n \"Invalid bribeContractOther address\"\n );\n // expect it to be between 1% & 99%\n require(_split > 1e16 && _split < 99e16, \"Unexpected split amount\");\n\n bribeContractOS = IBribe(_bribeContractOS);\n bribeContractOther = IBribe(_bribeContractOther);\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n split = _split;\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n if (balance < MIN_BRIBE_AMOUNT) {\n return;\n }\n\n uint256 osBribeAmount = balance.mulTruncate(split);\n uint256 otherBribeAmount = balance - osBribeAmount;\n\n osToken.approve(address(bribeContractOS), osBribeAmount);\n osToken.approve(address(bribeContractOther), otherBribeAmount);\n\n bribeContractOS.notifyRewardAmount(address(osToken), osBribeAmount);\n bribeContractOther.notifyRewardAmount(\n address(osToken),\n otherBribeAmount\n );\n\n emit BribeExecuted(balance);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterSwapxSingle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBribe } from \"../interfaces/poolBooster/ISwapXAlgebraBribe.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @title Pool booster for SwapX for Classic Stable Pools and Classic Volatile Pools\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterSwapxSingle is IPoolBooster {\n // @notice address of the Bribes.sol(Bribe) contract\n IBribe public immutable bribeContract;\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n constructor(address _bribeContract, address _osToken) {\n require(_bribeContract != address(0), \"Invalid bribeContract address\");\n bribeContract = IBribe(_bribeContract);\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n if (balance < MIN_BRIBE_AMOUNT) {\n return;\n }\n\n osToken.approve(address(bribeContract), balance);\n\n bribeContract.notifyRewardAmount(address(osToken), balance);\n emit BribeExecuted(balance);\n }\n}\n" + }, + "contracts/proxies/BaseProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice BridgedBaseWOETHProxy delegates calls to BridgedWOETH implementation\n */\ncontract BridgedBaseWOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseVaultProxy delegates calls to OETHBaseVault implementation\n */\ncontract OETHBaseVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseProxy delegates calls to OETH implementation\n */\ncontract OETHBaseProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHBaseProxy delegates calls to WOETH implementation\n */\ncontract WOETHBaseProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseDripperProxy delegates calls to a FixedRateDripper implementation\n */\ncontract OETHBaseDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice AerodromeAMOStrategyProxy delegates calls to AerodromeAMOStrategy implementation\n */\ncontract AerodromeAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BridgedWOETHStrategyProxy delegates calls to BridgedWOETHStrategy implementation\n */\ncontract BridgedWOETHStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseHarvesterProxy delegates calls to a SuperOETHHarvester implementation\n */\ncontract OETHBaseHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseCurveAMOProxy delegates calls to a OETHBaseCurveAMO implementation\n */\ncontract OETHBaseCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/proxies/InitializeGovernedUpgradeabilityProxy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { Governable } from \"../governance/Governable.sol\";\n\n/**\n * @title BaseGovernedUpgradeabilityProxy\n * @dev This contract combines an upgradeability proxy with our governor system.\n * It is based on an older version of OpenZeppelins BaseUpgradeabilityProxy\n * with Solidity ^0.8.0.\n * @author Origin Protocol Inc\n */\ncontract InitializeGovernedUpgradeabilityProxy is Governable {\n /**\n * @dev Emitted when the implementation is upgraded.\n * @param implementation Address of the new implementation.\n */\n event Upgraded(address indexed implementation);\n\n constructor() {\n _setGovernor(msg.sender);\n }\n\n /**\n * @dev Contract initializer with Governor enforcement\n * @param _logic Address of the initial implementation.\n * @param _initGovernor Address of the initial Governor.\n * @param _data Data to send as msg.data to the implementation to initialize\n * the proxied contract.\n * It should include the signature and the parameters of the function to be\n * called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n * This parameter is optional, if no data is given the initialization call\n * to proxied contract will be skipped.\n */\n function initialize(\n address _logic,\n address _initGovernor,\n bytes calldata _data\n ) public payable onlyGovernor {\n require(_implementation() == address(0));\n require(_logic != address(0), \"Implementation not set\");\n assert(\n IMPLEMENTATION_SLOT ==\n bytes32(uint256(keccak256(\"eip1967.proxy.implementation\")) - 1)\n );\n _setImplementation(_logic);\n if (_data.length > 0) {\n (bool success, ) = _logic.delegatecall(_data);\n require(success);\n }\n _changeGovernor(_initGovernor);\n }\n\n /**\n * @return The address of the proxy admin/it's also the governor.\n */\n function admin() external view returns (address) {\n return _governor();\n }\n\n /**\n * @return The address of the implementation.\n */\n function implementation() external view returns (address) {\n return _implementation();\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy.\n * Only the admin can call this function.\n * @param _newImplementation Address of the new implementation.\n */\n function upgradeTo(address _newImplementation) external onlyGovernor {\n _upgradeTo(_newImplementation);\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy and call a function\n * on the new implementation.\n * This is useful to initialize the proxied contract.\n * @param newImplementation Address of the new implementation.\n * @param data Data to send as msg.data in the low level call.\n * It should include the signature and the parameters of the function to be called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n */\n function upgradeToAndCall(address newImplementation, bytes calldata data)\n external\n payable\n onlyGovernor\n {\n _upgradeTo(newImplementation);\n (bool success, ) = newImplementation.delegatecall(data);\n require(success);\n }\n\n /**\n * @dev Fallback function.\n * Implemented entirely in `_fallback`.\n */\n fallback() external payable {\n _fallback();\n }\n\n /**\n * @dev Delegates execution to an implementation contract.\n * This is a low level function that doesn't return to its internal call site.\n * It will return to the external caller whatever the implementation returns.\n * @param _impl Address to delegate.\n */\n function _delegate(address _impl) internal {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(gas(), _impl, 0, calldatasize(), 0, 0)\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n /**\n * @dev Function that is run as the first thing in the fallback function.\n * Can be redefined in derived contracts to add functionality.\n * Redefinitions must call super._willFallback().\n */\n function _willFallback() internal {}\n\n /**\n * @dev fallback implementation.\n * Extracted to enable manual triggering.\n */\n function _fallback() internal {\n _willFallback();\n _delegate(_implementation());\n }\n\n /**\n * @dev Storage slot with the address of the current implementation.\n * This is the keccak-256 hash of \"eip1967.proxy.implementation\" subtracted by 1, and is\n * validated in the constructor.\n */\n bytes32 internal constant IMPLEMENTATION_SLOT =\n 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n /**\n * @dev Returns the current implementation.\n * @return impl Address of the current implementation\n */\n function _implementation() internal view returns (address impl) {\n bytes32 slot = IMPLEMENTATION_SLOT;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n impl := sload(slot)\n }\n }\n\n /**\n * @dev Upgrades the proxy to a new implementation.\n * @param newImplementation Address of the new implementation.\n */\n function _upgradeTo(address newImplementation) internal {\n _setImplementation(newImplementation);\n emit Upgraded(newImplementation);\n }\n\n /**\n * @dev Sets the implementation address of the proxy.\n * @param newImplementation Address of the new implementation.\n */\n function _setImplementation(address newImplementation) internal {\n require(\n Address.isContract(newImplementation),\n \"Cannot set a proxy implementation to a non-contract address\"\n );\n\n bytes32 slot = IMPLEMENTATION_SLOT;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(slot, newImplementation)\n }\n }\n}\n" + }, + "contracts/proxies/PlumeProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OETHPlumeVaultProxy delegates calls to OETHPlumeVault implementation\n */\ncontract OETHPlumeVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHPlumeProxy delegates calls to OETH implementation\n */\ncontract OETHPlumeProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHPlumeProxy delegates calls to WOETH implementation\n */\ncontract WOETHPlumeProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice RoosterAMOStrategyProxy delegates calls to a RoosterAMOStrategy implementation\n */\ncontract RoosterAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/proxies/Proxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OUSDProxy delegates calls to an OUSD implementation\n */\ncontract OUSDProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WrappedOUSDProxy delegates calls to a WrappedOUSD implementation\n */\ncontract WrappedOUSDProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice VaultProxy delegates calls to a Vault implementation\n */\ncontract VaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CompoundStrategyProxy delegates calls to a CompoundStrategy implementation\n */\ncontract CompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice AaveStrategyProxy delegates calls to a AaveStrategy implementation\n */\ncontract AaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ConvexStrategyProxy delegates calls to a ConvexStrategy implementation\n */\ncontract ConvexStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice HarvesterProxy delegates calls to a Harvester implementation\n */\ncontract HarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice DripperProxy delegates calls to a Dripper implementation\n */\ncontract DripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoCompoundStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\n */\ncontract MorphoCompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ConvexOUSDMetaStrategyProxy delegates calls to a ConvexOUSDMetaStrategy implementation\n */\ncontract ConvexOUSDMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoAaveStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\n */\ncontract MorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHProxy delegates calls to nowhere for now\n */\ncontract OETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHProxy delegates calls to nowhere for now\n */\ncontract WOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHVaultProxy delegates calls to a Vault implementation\n */\ncontract OETHVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHDripperProxy delegates calls to a OETHDripper implementation\n */\ncontract OETHDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHHarvesterProxy delegates calls to a Harvester implementation\n */\ncontract OETHHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CurveEthStrategyProxy delegates calls to a CurveEthStrategy implementation\n */\ncontract ConvexEthMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BuybackProxy delegates calls to Buyback implementation\n */\ncontract BuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHMorphoAaveStrategyProxy delegates calls to a MorphoAaveStrategy implementation\n */\ncontract OETHMorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBalancerMetaPoolrEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\n */\ncontract OETHBalancerMetaPoolrEthStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice OETHBalancerMetaPoolwstEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\n */\ncontract OETHBalancerMetaPoolwstEthStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MakerDsrStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MakerDsrStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBuybackProxy delegates calls to Buyback implementation\n */\ncontract OETHBuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BridgedWOETHProxy delegates calls to BridgedWOETH implementation\n */\ncontract BridgedWOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice NativeStakingSSVStrategyProxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulatorProxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulatorProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingSSVStrategy2Proxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategy2Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulator2Proxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulator2Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingSSVStrategy3Proxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategy3Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulator3Proxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulator3Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MetaMorphoStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MetaMorphoStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ARMBuybackProxy delegates calls to Buyback implementation\n */\ncontract ARMBuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoGauntletPrimeUSDCStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MorphoGauntletPrimeUSDCStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MorphoGauntletPrimeUSDTStrategyProxy delegates calls to a Generalized4626USDTStrategy implementation\n */\ncontract MorphoGauntletPrimeUSDTStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice CurvePoolBoosterProxy delegates calls to a CurvePoolBooster implementation\n */\ncontract CurvePoolBoosterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHFixedRateDripperProxy delegates calls to a OETHFixedRateDripper implementation\n */\ncontract OETHFixedRateDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHSimpleHarvesterProxy delegates calls to a OETHSimpleHarvester implementation\n */\ncontract OETHSimpleHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice PoolBoostCentralRegistryProxy delegates calls to the PoolBoostCentralRegistry implementation\n */\ncontract PoolBoostCentralRegistryProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MakerSSRStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MakerSSRStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OUSDCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\n */\ncontract OUSDCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\n */\ncontract OETHCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CompoundingStakingSSVStrategyProxy delegates calls to a CompoundingStakingSSVStrategy implementation\n */\ncontract CompoundingStakingSSVStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n" + }, + "contracts/proxies/SonicProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OSonicVaultProxy delegates calls to OSonicVault implementation\n */\ncontract OSonicVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicProxy delegates calls to OSonic implementation\n */\ncontract OSonicProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOSonicProxy delegates calls to WOSonic implementation\n */\ncontract WOSonicProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicDripperProxy delegates calls to a FixedRateDripper implementation\n */\ncontract OSonicDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice SonicStakingStrategyProxy delegates calls to SonicStakingStrategy implementation\n */\ncontract SonicStakingStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicHarvesterProxy delegates calls to a OSonicHarvester implementation\n */\ncontract OSonicHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice SonicSwapXAMOStrategyProxy delegates calls to a SonicSwapXAMOStrategy implementation\n */\ncontract SonicSwapXAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/strategies/AaveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Aave Strategy\n * @notice Investment strategy for investing stablecoins via Aave\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport \"./IAave.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\nimport { IAaveStakedToken } from \"./IAaveStakeToken.sol\";\nimport { IAaveIncentivesController } from \"./IAaveIncentivesController.sol\";\n\ncontract AaveStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n\n uint16 constant referralCode = 92;\n\n IAaveIncentivesController public incentivesController;\n IAaveStakedToken public stkAave;\n\n /**\n * @param _stratConfig The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as AAVE needs several extra\n * addresses for the rewards program.\n * @param _rewardTokenAddresses Address of the AAVE token\n * @param _assets Addresses of supported assets\n * @param _pTokens Platform Token corresponding addresses\n * @param _incentivesAddress Address of the AAVE incentives controller\n * @param _stkAaveAddress Address of the stkAave contract\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // AAVE\n address[] calldata _assets,\n address[] calldata _pTokens,\n address _incentivesAddress,\n address _stkAaveAddress\n ) external onlyGovernor initializer {\n incentivesController = IAaveIncentivesController(_incentivesAddress);\n stkAave = IAaveStakedToken(_stkAaveAddress);\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Deposit asset into Aave\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Aave\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n // Following line also doubles as a check that we are depositing\n // an asset that we support.\n emit Deposit(_asset, _getATokenFor(_asset), _amount);\n _getLendingPool().deposit(_asset, _amount, address(this), referralCode);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Aave\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Aave\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n emit Withdrawal(_asset, _getATokenFor(_asset), _amount);\n uint256 actual = _getLendingPool().withdraw(\n _asset,\n _amount,\n address(this)\n );\n require(actual == _amount, \"Did not withdraw enough\");\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n // Redeem entire balance of aToken\n IERC20 asset = IERC20(assetsMapped[i]);\n address aToken = _getATokenFor(assetsMapped[i]);\n uint256 balance = IERC20(aToken).balanceOf(address(this));\n if (balance > 0) {\n uint256 actual = _getLendingPool().withdraw(\n address(asset),\n balance,\n address(this)\n );\n require(actual == balance, \"Did not withdraw enough\");\n\n uint256 assetBalance = asset.balanceOf(address(this));\n // Transfer entire balance to Vault\n asset.safeTransfer(vaultAddress, assetBalance);\n\n emit Withdrawal(address(asset), aToken, assetBalance);\n }\n }\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n // Balance is always with token aToken decimals\n address aToken = _getATokenFor(_asset);\n balance = IERC20(aToken).balanceOf(address(this));\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Approve the spending of all assets by their corresponding aToken,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n address lendingPool = address(_getLendingPool());\n // approve the pool to spend the Asset\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n address asset = assetsMapped[i];\n // Safe approval\n IERC20(asset).safeApprove(lendingPool, 0);\n IERC20(asset).safeApprove(lendingPool, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / aTokens\n We need to give the AAVE lending pool approval to transfer the\n asset.\n * @param _asset Address of the asset to approve\n * @param _aToken Address of the aToken\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _aToken)\n internal\n override\n {\n address lendingPool = address(_getLendingPool());\n IERC20(_asset).safeApprove(lendingPool, 0);\n IERC20(_asset).safeApprove(lendingPool, type(uint256).max);\n }\n\n /**\n * @dev Get the aToken wrapped in the IERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return Corresponding aToken to this asset\n */\n function _getATokenFor(address _asset) internal view returns (address) {\n address aToken = assetToPToken[_asset];\n require(aToken != address(0), \"aToken does not exist\");\n return aToken;\n }\n\n /**\n * @dev Get the current address of the Aave lending pool, which is the gateway to\n * depositing.\n * @return Current lending pool implementation\n */\n function _getLendingPool() internal view returns (IAaveLendingPool) {\n address lendingPool = ILendingPoolAddressesProvider(platformAddress)\n .getLendingPool();\n require(lendingPool != address(0), \"Lending pool does not exist\");\n return IAaveLendingPool(lendingPool);\n }\n\n /**\n * @dev Collect stkAave, convert it to AAVE send to Vault.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n if (address(stkAave) == address(0)) {\n return;\n }\n\n // Check staked AAVE cooldown timer\n uint256 cooldown = stkAave.stakersCooldowns(address(this));\n uint256 windowStart = cooldown + stkAave.COOLDOWN_SECONDS();\n uint256 windowEnd = windowStart + stkAave.UNSTAKE_WINDOW();\n\n // If inside the unlock window, then we can redeem stkAave\n // for AAVE and send it to the vault.\n if (block.timestamp > windowStart && block.timestamp <= windowEnd) {\n // Redeem to AAVE\n uint256 stkAaveBalance = stkAave.balanceOf(address(this));\n stkAave.redeem(address(this), stkAaveBalance);\n\n // Transfer AAVE to harvesterAddress\n uint256 aaveBalance = IERC20(rewardTokenAddresses[0]).balanceOf(\n address(this)\n );\n if (aaveBalance > 0) {\n IERC20(rewardTokenAddresses[0]).safeTransfer(\n harvesterAddress,\n aaveBalance\n );\n }\n }\n\n // Collect available rewards and restart the cooldown timer, if either of\n // those should be run.\n if (block.timestamp > windowStart || cooldown == 0) {\n uint256 assetsLen = assetsMapped.length;\n // aToken addresses for incentives controller\n address[] memory aTokens = new address[](assetsLen);\n for (uint256 i = 0; i < assetsLen; ++i) {\n aTokens[i] = _getATokenFor(assetsMapped[i]);\n }\n\n // 1. If we have rewards availabile, collect them\n uint256 pendingRewards = incentivesController.getRewardsBalance(\n aTokens,\n address(this)\n );\n if (pendingRewards > 0) {\n // Because getting more stkAAVE from the incentives controller\n // with claimRewards() may push the stkAAVE cooldown time\n // forward, it is called after stakedAAVE has been turned into\n // AAVE.\n uint256 collected = incentivesController.claimRewards(\n aTokens,\n pendingRewards,\n address(this)\n );\n require(collected == pendingRewards, \"AAVE reward difference\");\n }\n\n // 2. Start cooldown counting down.\n if (stkAave.balanceOf(address(this)) > 0) {\n // Protected with if since cooldown call would revert\n // if no stkAave balance.\n stkAave.cooldown();\n }\n }\n }\n}\n" + }, + "contracts/strategies/AbstractCompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base Compound Abstract Strategy\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { ICERC20 } from \"./ICompound.sol\";\nimport { IComptroller } from \"../interfaces/IComptroller.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\nabstract contract AbstractCompoundStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n\n int256[50] private __reserved;\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Get the cToken wrapped in the ICERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return Corresponding cToken to this asset\n */\n function _getCTokenFor(address _asset) internal view returns (ICERC20) {\n address cToken = assetToPToken[_asset];\n require(cToken != address(0), \"cToken does not exist\");\n return ICERC20(cToken);\n }\n\n /**\n * @dev Converts an underlying amount into cToken amount\n * cTokenAmt = (underlying * 1e18) / exchangeRate\n * @param _cToken cToken for which to change\n * @param _underlying Amount of underlying to convert\n * @return amount Equivalent amount of cTokens\n */\n function _convertUnderlyingToCToken(ICERC20 _cToken, uint256 _underlying)\n internal\n view\n returns (uint256 amount)\n {\n // e.g. 1e18*1e18 / 205316390724364402565641705 = 50e8\n // e.g. 1e8*1e18 / 205316390724364402565641705 = 0.45 or 0\n amount = (_underlying * 1e18) / _cToken.exchangeRateStored();\n }\n}\n" + }, + "contracts/strategies/AbstractConvexMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { ICurveMetaPool } from \"./ICurveMetaPool.sol\";\nimport { IERC20, AbstractCurveStrategy, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\nabstract contract AbstractConvexMetaStrategy is AbstractCurveStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n event MaxWithdrawalSlippageUpdated(\n uint256 _prevMaxSlippagePercentage,\n uint256 _newMaxSlippagePercentage\n );\n\n // used to circumvent the stack too deep issue\n struct InitConfig {\n address cvxDepositorAddress; //Address of the Convex depositor(AKA booster) for this pool\n address metapoolAddress; //Address of the Curve MetaPool\n address metapoolMainToken; //Address of Main metapool token\n address cvxRewardStakerAddress; //Address of the CVX rewards staker\n address metapoolLPToken; //Address of metapool LP token\n uint256 cvxDepositorPTokenId; //Pid of the pool referred to by Depositor and staker\n }\n\n address internal cvxDepositorAddress;\n address internal cvxRewardStakerAddress;\n uint256 internal cvxDepositorPTokenId;\n ICurveMetaPool internal metapool;\n IERC20 internal metapoolMainToken;\n IERC20 internal metapoolLPToken;\n // Ordered list of metapool assets\n address[] internal metapoolAssets;\n // Max withdrawal slippage denominated in 1e18 (1e18 == 100%)\n uint256 public maxWithdrawalSlippage;\n uint128 internal crvCoinIndex;\n uint128 internal mainCoinIndex;\n\n int256[41] private ___reserved;\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * DAI, USDC, USDT\n * @param _pTokens Platform Token corresponding addresses\n * @param initConfig Various addresses and info for initialization state\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets,\n address[] calldata _pTokens,\n InitConfig calldata initConfig\n ) external onlyGovernor initializer {\n require(_assets.length == 3, \"Must have exactly three assets\");\n // Should be set prior to abstract initialize call otherwise\n // abstractSetPToken calls will fail\n cvxDepositorAddress = initConfig.cvxDepositorAddress;\n pTokenAddress = _pTokens[0];\n metapool = ICurveMetaPool(initConfig.metapoolAddress);\n metapoolMainToken = IERC20(initConfig.metapoolMainToken);\n cvxRewardStakerAddress = initConfig.cvxRewardStakerAddress;\n metapoolLPToken = IERC20(initConfig.metapoolLPToken);\n cvxDepositorPTokenId = initConfig.cvxDepositorPTokenId;\n maxWithdrawalSlippage = 1e16;\n\n metapoolAssets = [metapool.coins(0), metapool.coins(1)];\n crvCoinIndex = _getMetapoolCoinIndex(pTokenAddress);\n mainCoinIndex = _getMetapoolCoinIndex(initConfig.metapoolMainToken);\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n balance = 0;\n\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 contractPTokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (contractPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = contractPTokens.mulTruncate(virtual_price);\n balance += value;\n }\n\n /* We intentionally omit the metapoolLp tokens held by the metastrategyContract\n * since the contract should never (except in the middle of deposit/withdrawal\n * transaction) hold any amount of those tokens in normal operation. There\n * could be tokens sent to it by a 3rd party and we decide to actively ignore\n * those.\n */\n uint256 metapoolGaugePTokens = IRewardStaking(cvxRewardStakerAddress)\n .balanceOf(address(this));\n\n if (metapoolGaugePTokens > 0) {\n uint256 value = metapoolGaugePTokens.mulTruncate(\n metapool.get_virtual_price()\n );\n balance += value;\n }\n\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = balance.scaleBy(assetDecimals, 18) / THREEPOOL_ASSET_COUNT;\n }\n\n /**\n * @dev This function is completely analogous to _calcCurveTokenAmount[AbstractCurveStrategy]\n * and just utilizes different Curve (meta)pool API\n */\n function _calcCurveMetaTokenAmount(uint128 _coinIndex, uint256 _amount)\n internal\n returns (uint256 requiredMetapoolLP)\n {\n uint256[2] memory _amounts = [uint256(0), uint256(0)];\n _amounts[uint256(_coinIndex)] = _amount;\n\n // LP required when removing required asset ignoring fees\n uint256 lpRequiredNoFees = metapool.calc_token_amount(_amounts, false);\n /* LP required if fees would apply to entirety of removed amount\n *\n * fee is 1e10 denominated number: https://curve.readthedocs.io/exchange-pools.html#StableSwap.fee\n */\n uint256 lpRequiredFullFees = lpRequiredNoFees.mulTruncateScale(\n 1e10 + metapool.fee(),\n 1e10\n );\n\n /* asset received when withdrawing full fee applicable LP accounting for\n * slippage and fees\n */\n uint256 assetReceivedForFullLPFees = metapool.calc_withdraw_one_coin(\n lpRequiredFullFees,\n int128(_coinIndex)\n );\n\n // exact amount of LP required\n requiredMetapoolLP =\n (lpRequiredFullFees * _amount) /\n assetReceivedForFullLPFees;\n }\n\n function _approveBase() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // 3Pool for LP token (required for removing liquidity)\n pToken.safeApprove(platformAddress, 0);\n pToken.safeApprove(platformAddress, type(uint256).max);\n // Gauge for LP token\n metapoolLPToken.safeApprove(cvxDepositorAddress, 0);\n metapoolLPToken.safeApprove(cvxDepositorAddress, type(uint256).max);\n // Metapool for LP token\n pToken.safeApprove(address(metapool), 0);\n pToken.safeApprove(address(metapool), type(uint256).max);\n // Metapool for Metapool main token\n metapoolMainToken.safeApprove(address(metapool), 0);\n metapoolMainToken.safeApprove(address(metapool), type(uint256).max);\n }\n\n /**\n * @dev Get the index of the coin\n */\n function _getMetapoolCoinIndex(address _asset)\n internal\n view\n returns (uint128)\n {\n for (uint128 i = 0; i < 2; i++) {\n if (metapoolAssets[i] == _asset) return i;\n }\n revert(\"Invalid Metapool asset\");\n }\n\n /**\n * @dev Sets max withdrawal slippage that is considered when removing\n * liquidity from Metapools.\n * @param _maxWithdrawalSlippage Max withdrawal slippage denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxWithdrawalSlippage should actually be 0.1% (1e15)\n * for production usage. Contract allows as low value as 0% for confirming\n * correct behavior in test suite.\n */\n function setMaxWithdrawalSlippage(uint256 _maxWithdrawalSlippage)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(\n _maxWithdrawalSlippage <= 1e18,\n \"Max withdrawal slippage needs to be between 0% - 100%\"\n );\n emit MaxWithdrawalSlippageUpdated(\n maxWithdrawalSlippage,\n _maxWithdrawalSlippage\n );\n maxWithdrawalSlippage = _maxWithdrawalSlippage;\n }\n\n /**\n * @dev Collect accumulated CRV and CVX and send to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n IRewardStaking(cvxRewardStakerAddress).getReward();\n _collectRewardTokens();\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/AbstractCurveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve 3Pool Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\nabstract contract AbstractCurveStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 internal constant MAX_SLIPPAGE = 1e16; // 1%, same as the Curve UI\n // number of assets in Curve 3Pool (USDC, DAI, USDT)\n uint256 internal constant THREEPOOL_ASSET_COUNT = 3;\n address internal pTokenAddress;\n\n int256[49] private __reserved;\n\n /**\n * @dev Deposit asset into the Curve 3Pool\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_amount > 0, \"Must deposit something\");\n emit Deposit(_asset, pTokenAddress, _amount);\n\n // 3Pool requires passing deposit amounts for all 3 assets, set to 0 for\n // all\n uint256[3] memory _amounts;\n uint256 poolCoinIndex = _getCoinIndex(_asset);\n // Set the amount on the asset we want to deposit\n _amounts[poolCoinIndex] = _amount;\n ICurvePool curvePool = ICurvePool(platformAddress);\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n uint256 depositValue = _amount.scaleBy(18, assetDecimals).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = depositValue.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n // Do the deposit to 3pool\n curvePool.add_liquidity(_amounts, minMintAmount);\n _lpDepositAll();\n }\n\n function _lpDepositAll() internal virtual;\n\n /**\n * @dev Deposit the entire balance of any supported asset into the Curve 3pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n uint256 depositValue = 0;\n ICurvePool curvePool = ICurvePool(platformAddress);\n uint256 curveVirtualPrice = curvePool.get_virtual_price();\n\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address assetAddress = assetsMapped[i];\n uint256 balance = IERC20(assetAddress).balanceOf(address(this));\n if (balance > 0) {\n uint256 poolCoinIndex = _getCoinIndex(assetAddress);\n // Set the amount on the asset we want to deposit\n _amounts[poolCoinIndex] = balance;\n uint256 assetDecimals = Helpers.getDecimals(assetAddress);\n // Get value of deposit in Curve LP token to later determine\n // the minMintAmount argument for add_liquidity\n depositValue =\n depositValue +\n balance.scaleBy(18, assetDecimals).divPrecisely(\n curveVirtualPrice\n );\n emit Deposit(assetAddress, pTokenAddress, balance);\n }\n }\n\n uint256 minMintAmount = depositValue.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n // Do the deposit to 3pool\n curvePool.add_liquidity(_amounts, minMintAmount);\n\n /* In case of Curve Strategy all assets are mapped to the same pToken (3CrvLP). Let\n * descendants further handle the pToken. By either deploying it to the metapool and\n * resulting tokens in Gauge. Or deploying pTokens directly to the Gauge.\n */\n _lpDepositAll();\n }\n\n function _lpWithdraw(uint256 numCrvTokens) internal virtual;\n\n function _lpWithdrawAll() internal virtual;\n\n /**\n * @dev Withdraw asset from Curve 3Pool\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Invalid amount\");\n\n emit Withdrawal(_asset, pTokenAddress, _amount);\n\n uint256 contractCrv3Tokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n\n uint256 coinIndex = _getCoinIndex(_asset);\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 requiredCrv3Tokens = _calcCurveTokenAmount(coinIndex, _amount);\n\n // We have enough LP tokens, make sure they are all on this contract\n if (contractCrv3Tokens < requiredCrv3Tokens) {\n _lpWithdraw(requiredCrv3Tokens - contractCrv3Tokens);\n }\n\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n _amounts[coinIndex] = _amount;\n\n curvePool.remove_liquidity_imbalance(_amounts, requiredCrv3Tokens);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Calculate amount of LP required when withdrawing specific amount of one\n * of the underlying assets accounting for fees and slippage.\n *\n * Curve pools unfortunately do not contain a calculation function for\n * amount of LP required when withdrawing a specific amount of one of the\n * underlying tokens and also accounting for fees (Curve's calc_token_amount\n * does account for slippage but not fees).\n *\n * Steps taken to calculate the metric:\n * - get amount of LP required if fees wouldn't apply\n * - increase the LP amount as if fees would apply to the entirety of the underlying\n * asset withdrawal. (when withdrawing only one coin fees apply only to amounts\n * of other assets pool would return in case of balanced removal - since those need\n * to be swapped for the single underlying asset being withdrawn)\n * - get amount of underlying asset withdrawn (this Curve function does consider slippage\n * and fees) when using the increased LP amount. As LP amount is slightly over-increased\n * so is amount of underlying assets returned.\n * - since we know exactly how much asset we require take the rate of LP required for asset\n * withdrawn to get the exact amount of LP.\n */\n function _calcCurveTokenAmount(uint256 _coinIndex, uint256 _amount)\n internal\n returns (uint256 required3Crv)\n {\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n _amounts[_coinIndex] = _amount;\n\n // LP required when removing required asset ignoring fees\n uint256 lpRequiredNoFees = curvePool.calc_token_amount(_amounts, false);\n /* LP required if fees would apply to entirety of removed amount\n *\n * fee is 1e10 denominated number: https://curve.readthedocs.io/exchange-pools.html#StableSwap.fee\n */\n uint256 lpRequiredFullFees = lpRequiredNoFees.mulTruncateScale(\n 1e10 + curvePool.fee(),\n 1e10\n );\n\n /* asset received when withdrawing full fee applicable LP accounting for\n * slippage and fees\n */\n uint256 assetReceivedForFullLPFees = curvePool.calc_withdraw_one_coin(\n lpRequiredFullFees,\n int128(uint128(_coinIndex))\n );\n\n // exact amount of LP required\n required3Crv =\n (lpRequiredFullFees * _amount) /\n assetReceivedForFullLPFees;\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n _lpWithdrawAll();\n // Withdraws are proportional to assets held by 3Pool\n uint256[3] memory minWithdrawAmounts = [\n uint256(0),\n uint256(0),\n uint256(0)\n ];\n\n // Remove liquidity\n ICurvePool threePool = ICurvePool(platformAddress);\n threePool.remove_liquidity(\n IERC20(pTokenAddress).balanceOf(address(this)),\n minWithdrawAmounts\n );\n // Transfer assets out of Vault\n // Note that Curve will provide all 3 of the assets in 3pool even if\n // we have not set PToken addresses for all of them in this strategy\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n IERC20 asset = IERC20(threePool.coins(i));\n asset.safeTransfer(vaultAddress, asset.balanceOf(address(this)));\n }\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 totalPTokens = IERC20(pTokenAddress).balanceOf(address(this));\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (totalPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = (totalPTokens * virtual_price) / 1e18;\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = value.scaleBy(assetDecimals, 18) / THREEPOOL_ASSET_COUNT;\n }\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n // This strategy is a special case since it only supports one asset\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n _approveAsset(assetsMapped[i]);\n }\n }\n\n /**\n * @dev Call the necessary approvals for the Curve pool and gauge\n * @param _asset Address of the asset\n */\n function _abstractSetPToken(address _asset, address) internal override {\n _approveAsset(_asset);\n }\n\n function _approveAsset(address _asset) internal {\n IERC20 asset = IERC20(_asset);\n // 3Pool for asset (required for adding liquidity)\n asset.safeApprove(platformAddress, 0);\n asset.safeApprove(platformAddress, type(uint256).max);\n }\n\n function _approveBase() internal virtual;\n\n /**\n * @dev Get the index of the coin\n */\n function _getCoinIndex(address _asset) internal view returns (uint256) {\n for (uint256 i = 0; i < 3; i++) {\n if (assetsMapped[i] == _asset) return i;\n }\n revert(\"Invalid 3pool asset\");\n }\n}\n" + }, + "contracts/strategies/aerodrome/AerodromeAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Aerodrome AMO strategy\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nimport { ISugarHelper } from \"../../interfaces/aerodrome/ISugarHelper.sol\";\nimport { INonfungiblePositionManager } from \"../../interfaces/aerodrome/INonfungiblePositionManager.sol\";\nimport { ISwapRouter } from \"../../interfaces/aerodrome/ISwapRouter.sol\";\nimport { ICLPool } from \"../../interfaces/aerodrome/ICLPool.sol\";\nimport { ICLGauge } from \"../../interfaces/aerodrome/ICLGauge.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\n\ncontract AerodromeAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n using SafeCast for uint256;\n\n /************************************************\n Important (!) setup configuration\n *************************************************/\n\n /**\n * In order to be able to remove a reasonable amount of complexity from the contract one of the\n * preconditions for this contract to function correctly is to have an outside account mint a small\n * amount of liquidity in the tick space where the contract will deploy's its liquidity and then send\n * that NFT LP position to a dead address (transfer to zero address not allowed.) See example of such\n * NFT LP token:\n * https://basescan.org/token/0x827922686190790b37229fd06084350e74485b72?a=413296#inventory\n */\n\n /***************************************\n Storage slot members\n ****************************************/\n\n /// @notice tokenId of the liquidity position\n uint256 public tokenId;\n /// @dev Minimum amount of tokens the strategy would be able to withdraw from the pool.\n /// minimum amount of tokens are withdrawn at a 1:1 price\n uint256 public underlyingAssets;\n /// @notice Marks the start of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareStart;\n /// @notice Marks the end of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareEnd;\n /// @dev reserved for inheritance\n int256[46] private __reserved;\n\n /***************************************\n Constants, structs and events\n ****************************************/\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the OETHb token contract\n address public immutable OETHb;\n /// @notice lower tick set to -1 representing the price of 1.0001 of WETH for 1 OETHb.\n int24 public immutable lowerTick;\n /// @notice lower tick set to 0 representing the price of 1.0000 of WETH for 1 OETHb.\n int24 public immutable upperTick;\n /// @notice tick spacing of the pool (set to 1)\n int24 public immutable tickSpacing;\n /// @notice the swapRouter for performing swaps\n ISwapRouter public immutable swapRouter;\n /// @notice the underlying AMO Slipstream pool\n ICLPool public immutable clPool;\n /// @notice the gauge for the corresponding Slipstream pool (clPool)\n /// @dev can become an immutable once the gauge is created on the base main-net\n ICLGauge public immutable clGauge;\n /// @notice the Position manager contract that is used to manage the pool's position\n INonfungiblePositionManager public immutable positionManager;\n /// @notice helper contract for liquidity and ticker math\n ISugarHelper public immutable helper;\n /// @notice sqrtRatioX96TickLower\n /// @dev tick lower has value -1 and represents the lowest price of WETH priced in OETHb. Meaning the pool\n /// offers less than 1 OETHb for 1 WETH. In other terms to get 1 OETHB the swap needs to offer 1.0001 WETH\n /// this is where purchasing OETHb with WETH within the liquidity position is most expensive\n uint160 public immutable sqrtRatioX96TickLower;\n /// @notice sqrtRatioX96TickHigher\n /// @dev tick higher has value 0 and represents 1:1 price parity of WETH to OETHb\n uint160 public immutable sqrtRatioX96TickHigher;\n /// @dev tick closest to 1:1 price parity\n /// Correctly assessing which tick is closer to 1:1 price parity is important since it affects\n /// the way we calculate the underlying assets in check Balance. The underlying aerodrome pool\n /// orders the tokens depending on the values of their addresses. If OETH token is token0 in the pool\n /// then sqrtRatioX96TickClosestToParity=sqrtRatioX96TickLower. If it is token1 in the pool then\n /// sqrtRatioX96TickClosestToParity=sqrtRatioX96TickHigher\n uint160 public immutable sqrtRatioX96TickClosestToParity;\n\n /// @dev a threshold under which the contract no longer allows for the protocol to rebalance. Guarding\n /// against a strategist / guardian being taken over and with multiple transactions draining the\n /// protocol funds.\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth); // 0x989e5ca8\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth); // 0xa6737d87\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n ); // 0x3681e8e0\n error OutsideExpectedTickRange(int24 currentTick); // 0x5a2eba75\n\n event PoolRebalanced(uint256 currentPoolWethShare);\n\n event PoolWethShareIntervalUpdated(\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n\n event LiquidityRemoved(\n uint256 withdrawLiquidityShare,\n uint256 removedWETHAmount,\n uint256 removedOETHbAmount,\n uint256 wethAmountCollected,\n uint256 oethbAmountCollected,\n uint256 underlyingAssets\n );\n\n event LiquidityAdded(\n uint256 wethAmountDesired,\n uint256 oethbAmountDesired,\n uint256 wethAmountSupplied,\n uint256 oethbAmountSupplied,\n uint256 tokenId,\n uint256 underlyingAssets\n );\n\n event UnderlyingAssetsUpdated(uint256 underlyingAssets);\n\n /**\n * @dev Un-stakes the token from the gauge for the execution duration of\n * the function and after that re-stakes it back in.\n *\n * It is important that the token is unstaked and owned by the strategy contract\n * during any liquidity altering operations and that it is re-staked back into the\n * gauge after liquidity changes. If the token fails to re-stake back to the\n * gauge it is not earning incentives.\n */\n // all functions using this modifier are used by functions with reentrancy check\n // slither-disable-start reentrancy-no-eth\n modifier gaugeUnstakeAndRestake() {\n // because of solidity short-circuit _isLpTokenStakedInGauge doesn't get called\n // when tokenId == 0\n if (tokenId != 0 && _isLpTokenStakedInGauge()) {\n clGauge.withdraw(tokenId);\n }\n _;\n // because of solidity short-circuit _isLpTokenStakedInGauge doesn't get called\n // when tokenId == 0\n if (tokenId != 0 && !_isLpTokenStakedInGauge()) {\n /**\n * It can happen that a withdrawal (or a full withdrawal) transactions would\n * remove all of the liquidity from the token with a NFT token still existing.\n * In that case the token can not be staked into the gauge, as some liquidity\n * needs to be added to it first.\n */\n if (_getLiquidity() > 0) {\n // if token liquidity changes the positionManager requires re-approval.\n // to any contract pre-approved to handle the token.\n positionManager.approve(address(clGauge), tokenId);\n clGauge.deposit(tokenId);\n }\n }\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice the constructor\n /// @dev This contract is intended to be used as a proxy. To prevent the\n /// potential confusion of having a functional implementation contract\n /// the constructor has the `initializer` modifier. This way the\n /// `initialize` function can not be called on the implementation contract.\n /// For the same reason the implementation contract also has the governor\n /// set to a zero address.\n /// @param _stratConfig the basic strategy configuration\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _oethbAddress Address of the Erc20 OETHb Token contract\n /// @param _swapRouter Address of the Aerodrome Universal Swap Router\n /// @param _nonfungiblePositionManager Address of position manager to add/remove\n /// the liquidity\n /// @param _clPool Address of the Aerodrome concentrated liquidity pool\n /// @param _clGauge Address of the Aerodrome slipstream pool gauge\n /// @param _sugarHelper Address of the Aerodrome Sugar helper contract\n /// @param _lowerBoundingTick Smaller bounding tick of our liquidity position\n /// @param _upperBoundingTick Larger bounding tick of our liquidity position\n /// @param _tickClosestToParity Tick that is closer to 1:1 price parity\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethbAddress,\n address _swapRouter,\n address _nonfungiblePositionManager,\n address _clPool,\n address _clGauge,\n address _sugarHelper,\n int24 _lowerBoundingTick,\n int24 _upperBoundingTick,\n int24 _tickClosestToParity\n ) initializer InitializableAbstractStrategy(_stratConfig) {\n require(\n _lowerBoundingTick == _tickClosestToParity ||\n _upperBoundingTick == _tickClosestToParity,\n \"Misconfigured tickClosestToParity\"\n );\n require(\n ICLPool(_clPool).token0() == _wethAddress,\n \"Only WETH supported as token0\"\n );\n require(\n ICLPool(_clPool).token1() == _oethbAddress,\n \"Only OETHb supported as token1\"\n );\n int24 _tickSpacing = ICLPool(_clPool).tickSpacing();\n // when we generalize AMO we might support other tick spacings\n require(_tickSpacing == 1, \"Unsupported tickSpacing\");\n\n WETH = _wethAddress;\n OETHb = _oethbAddress;\n swapRouter = ISwapRouter(_swapRouter);\n positionManager = INonfungiblePositionManager(\n _nonfungiblePositionManager\n );\n clPool = ICLPool(_clPool);\n clGauge = ICLGauge(_clGauge);\n helper = ISugarHelper(_sugarHelper);\n sqrtRatioX96TickLower = ISugarHelper(_sugarHelper).getSqrtRatioAtTick(\n _lowerBoundingTick\n );\n sqrtRatioX96TickHigher = ISugarHelper(_sugarHelper).getSqrtRatioAtTick(\n _upperBoundingTick\n );\n sqrtRatioX96TickClosestToParity = ISugarHelper(_sugarHelper)\n .getSqrtRatioAtTick(_tickClosestToParity);\n\n lowerTick = _lowerBoundingTick;\n upperTick = _upperBoundingTick;\n tickSpacing = _tickSpacing;\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /**\n * @notice initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n */\n function initialize(address[] memory _rewardTokenAddresses)\n external\n onlyGovernor\n initializer\n {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n new address[](0),\n new address[](0)\n );\n }\n\n /***************************************\n Configuration \n ****************************************/\n\n /**\n * @notice Set allowed pool weth share interval. After the rebalance happens\n * the share of WETH token in the ticker needs to be withing the specifications\n * of the interval.\n *\n * @param _allowedWethShareStart Start of WETH share interval expressed as 18 decimal amount\n * @param _allowedWethShareEnd End of WETH share interval expressed as 18 decimal amount\n */\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external onlyGovernor {\n require(\n _allowedWethShareStart < _allowedWethShareEnd,\n \"Invalid interval\"\n );\n // can not go below 1% weth share\n require(_allowedWethShareStart > 0.01 ether, \"Invalid interval start\");\n // can not go above 95% weth share\n require(_allowedWethShareEnd < 0.95 ether, \"Invalid interval end\");\n\n allowedWethShareStart = _allowedWethShareStart;\n allowedWethShareEnd = _allowedWethShareEnd;\n emit PoolWethShareIntervalUpdated(\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n\n /***************************************\n Periphery utils\n ****************************************/\n\n function _isLpTokenStakedInGauge() internal view returns (bool) {\n require(tokenId != 0, \"Missing NFT LP token\");\n\n address owner = positionManager.ownerOf(tokenId);\n require(\n owner == address(clGauge) || owner == address(this),\n \"Unexpected token owner\"\n );\n return owner == address(clGauge);\n }\n\n /***************************************\n Strategy overrides \n ****************************************/\n\n /**\n * @notice Deposit an amount of assets into the strategy contract. Calling deposit doesn't\n * automatically deposit funds into the underlying Aerodrome pool\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposit WETH to the strategy contract. This function does not add liquidity to the\n * underlying Aerodrome pool.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance > 1e12) {\n _deposit(WETH, _wethBalance);\n }\n }\n\n /**\n * @dev Deposit WETH to the contract. This function doesn't deposit the liquidity to the\n * pool, that is done via the rebalance call.\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n emit Deposit(_asset, address(0), _amount);\n\n // if the pool price is not within the expected interval leave the WETH on the contract\n // as to not break the mints\n (bool _isExpectedRange, ) = _checkForExpectedPoolPrice(false);\n if (_isExpectedRange) {\n // deposit funds into the underlying pool\n _rebalance(0, false, 0);\n }\n }\n\n /**\n * @notice Rebalance the pool to the desired token split and Deposit any WETH on the contract to the\n * underlying aerodrome pool. Print the required amount of corresponding OETHb. After the rebalancing is\n * done burn any potentially remaining OETHb tokens still on the strategy contract.\n *\n * This function has a slightly different behaviour depending on the status of the underlying Aerodrome\n * slipstream pool. The function consists of the following 3 steps:\n * 1. withdrawPartialLiquidity -> so that moving the activeTrading price via a swap is cheaper\n * 2. swapToDesiredPosition -> move active trading price in the pool to be able to deposit WETH & OETHb\n * tokens with the desired pre-configured shares\n * 3. addLiquidity -> add liquidity into the pool respecting share split configuration\n *\n * Scenario 1: When there is no liquidity in the pool from the strategy but there is from other LPs then\n * only step 1 is skipped. (It is important to note that liquidity needs to exist in the configured\n * strategy tick ranges in order for the swap to be possible) Step 3 mints new liquidity position\n * instead of adding to an existing one.\n * Scenario 2: When there is strategy's liquidity in the pool all 3 steps are taken\n *\n *\n * Exact _amountToSwap, _swapWeth & _minTokenReceived parameters shall be determined by simulating the\n * transaction off-chain. The strategy checks that after the swap the share of the tokens is in the\n * expected ranges.\n *\n * @param _amountToSwap The amount of the token to swap\n * @param _swapWeth Swap using WETH when true, use OETHb when false\n * @param _minTokenReceived Slippage check -> minimum amount of token expected in return\n */\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) external nonReentrant onlyGovernorOrStrategist {\n _rebalance(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n\n function _rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n /**\n * Would be nice to check if there is any total liquidity in the pool before performing this swap\n * but there is no easy way to do that in UniswapV3:\n * - clPool.liquidity() -> only liquidity in the active tick\n * - asset[1&2].balanceOf(address(clPool)) -> will include uncollected tokens of LP providers\n * after their liquidity position has been decreased\n */\n\n /**\n * When rebalance is called for the first time there is no strategy\n * liquidity in the pool yet. The liquidity removal is thus skipped.\n * Also execute this function when WETH is required for the swap.\n */\n if (tokenId != 0 && _swapWeth && _amountToSwap > 0) {\n _ensureWETHBalance(_amountToSwap);\n }\n\n // in some cases we will just want to add liquidity and not issue a swap to move the\n // active trading position within the pool\n if (_amountToSwap > 0) {\n _swapToDesiredPosition(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n // calling check liquidity early so we don't get unexpected errors when adding liquidity\n // in the later stages of this function\n _checkForExpectedPoolPrice(true);\n\n _addLiquidity();\n\n // this call shouldn't be necessary, since adding liquidity shouldn't affect the active\n // trading price. It is a defensive programming measure.\n (, uint256 _wethSharePct) = _checkForExpectedPoolPrice(true);\n\n // revert if protocol insolvent\n _solvencyAssert();\n\n emit PoolRebalanced(_wethSharePct);\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethbSupply = IERC20(OETHb).totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethbSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /**\n * @dev Decrease partial or all liquidity from the pool.\n * @param _liquidityToDecrease The amount of liquidity to remove expressed in 18 decimal point\n */\n function _removeLiquidity(uint256 _liquidityToDecrease)\n internal\n gaugeUnstakeAndRestake\n {\n require(_liquidityToDecrease > 0, \"Must remove some liquidity\");\n\n uint128 _liquidity = _getLiquidity();\n // need to convert to uint256 since intermittent result is to big for uint128 to handle\n uint128 _liquidityToRemove = uint256(_liquidity)\n .mulTruncate(_liquidityToDecrease)\n .toUint128();\n\n /**\n * There is no liquidity to remove -> exit function early. This can happen after a\n * withdraw/withdrawAll removes all of the liquidity while retaining the NFT token.\n */\n if (_liquidity == 0 || _liquidityToRemove == 0) {\n return;\n }\n\n (uint256 _amountWeth, uint256 _amountOethb) = positionManager\n .decreaseLiquidity(\n // Both expected amounts can be 0 since we don't really care if any swaps\n // happen just before the liquidity removal.\n INonfungiblePositionManager.DecreaseLiquidityParams({\n tokenId: tokenId,\n liquidity: _liquidityToRemove,\n amount0Min: 0,\n amount1Min: 0,\n deadline: block.timestamp\n })\n );\n\n (\n uint256 _amountWethCollected,\n uint256 _amountOethbCollected\n ) = positionManager.collect(\n INonfungiblePositionManager.CollectParams({\n tokenId: tokenId,\n recipient: address(this),\n amount0Max: type(uint128).max, // defaults to all tokens owed\n amount1Max: type(uint128).max // defaults to all tokens owed\n })\n );\n\n _updateUnderlyingAssets();\n\n emit LiquidityRemoved(\n _liquidityToDecrease,\n _amountWeth, //removedWethAmount\n _amountOethb, //removedOethbAmount\n _amountWethCollected,\n _amountOethbCollected,\n underlyingAssets\n );\n\n _burnOethbOnTheContract();\n }\n\n /**\n * @dev Perform a swap so that after the swap the ticker has the desired WETH to OETHb token share.\n */\n function _swapToDesiredPosition(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n IERC20 _tokenToSwap = IERC20(_swapWeth ? WETH : OETHb);\n uint256 _balance = _tokenToSwap.balanceOf(address(this));\n\n if (_balance < _amountToSwap) {\n // This should never trigger since _ensureWETHBalance will already\n // throw an error if there is not enough WETH\n if (_swapWeth) {\n revert NotEnoughWethForSwap(_balance, _amountToSwap);\n }\n // if swapping OETHb\n uint256 mintForSwap = _amountToSwap - _balance;\n IVault(vaultAddress).mintForStrategy(mintForSwap);\n }\n\n // approve the specific amount of WETH required\n if (_swapWeth) {\n IERC20(WETH).approve(address(swapRouter), _amountToSwap);\n }\n\n // Swap it\n swapRouter.exactInputSingle(\n // sqrtPriceLimitX96 is just a rough sanity check that we are within 0 -> 1 tick\n // a more fine check is performed in _checkForExpectedPoolPrice\n // Note: this needs further work if we want to generalize this approach\n ISwapRouter.ExactInputSingleParams({\n tokenIn: address(_tokenToSwap),\n tokenOut: _swapWeth ? OETHb : WETH,\n tickSpacing: tickSpacing, // set to 1\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: _amountToSwap,\n amountOutMinimum: _minTokenReceived, // slippage check\n sqrtPriceLimitX96: _swapWeth\n ? sqrtRatioX96TickLower\n : sqrtRatioX96TickHigher\n })\n );\n\n /**\n * In the interest of each function in _rebalance to leave the contract state as\n * clean as possible the OETHb tokens here are burned. This decreases the\n * dependence where `_swapToDesiredPosition` function relies on later functions\n * (`addLiquidity`) to burn the OETHb. Reducing the risk of error introduction.\n */\n _burnOethbOnTheContract();\n }\n\n /**\n * @dev Add liquidity into the pool in the pre-configured WETH to OETHb share ratios\n * defined by the allowedPoolWethShareStart|End interval. This function will respect\n * liquidity ratios when there is no liquidity yet in the pool. If liquidity is already\n * present then it relies on the `_swapToDesiredPosition` function in a step before\n * to already move the trading price to desired position (with some tolerance).\n */\n // rebalance already has re-entrency checks\n // slither-disable-start reentrancy-no-eth\n function _addLiquidity() internal gaugeUnstakeAndRestake {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n // don't deposit small liquidity amounts\n if (_wethBalance <= 1e12) {\n return;\n }\n\n uint160 _currentPrice = getPoolX96Price();\n /**\n * Sanity check active trading price is positioned within our desired tick.\n *\n * We revert when price is equal to the lower tick even though that is still\n * a valid amount in regards to ticker position by Sugar.estimateAmount call.\n * Current price equaling tick bound at the 1:1 price parity results in\n * uint overfow when calculating the OETHb balance to deposit.\n */\n if (\n _currentPrice <= sqrtRatioX96TickLower ||\n _currentPrice >= sqrtRatioX96TickHigher\n ) {\n revert OutsideExpectedTickRange(getCurrentTradingTick());\n }\n\n /**\n * If estimateAmount1 call fails it could be due to _currentPrice being really\n * close to a tick and amount1 is a larger number than the sugar helper is able\n * to compute.\n *\n * If token addresses were reversed estimateAmount0 would be required here\n */\n uint256 _oethbRequired = helper.estimateAmount1(\n _wethBalance,\n address(0), // no need to pass pool address when current price is specified\n _currentPrice,\n lowerTick,\n upperTick\n );\n\n if (_oethbRequired > _oethbBalance) {\n IVault(vaultAddress).mintForStrategy(\n _oethbRequired - _oethbBalance\n );\n }\n\n // approve the specific amount of WETH required\n IERC20(WETH).approve(address(positionManager), _wethBalance);\n\n uint256 _wethAmountSupplied;\n uint256 _oethbAmountSupplied;\n if (tokenId == 0) {\n (\n tokenId,\n ,\n _wethAmountSupplied,\n _oethbAmountSupplied\n ) = positionManager.mint(\n /** amount0Min & amount1Min are left at 0 because slippage protection is ensured by the\n * _checkForExpectedPoolPrice\n *›\n * Also sqrtPriceX96 is 0 because the pool is already created\n * non zero amount attempts to create a new instance of the pool\n */\n INonfungiblePositionManager.MintParams({\n token0: WETH,\n token1: OETHb,\n tickSpacing: tickSpacing,\n tickLower: lowerTick,\n tickUpper: upperTick,\n amount0Desired: _wethBalance,\n amount1Desired: _oethbRequired,\n amount0Min: 0,\n amount1Min: 0,\n recipient: address(this),\n deadline: block.timestamp,\n sqrtPriceX96: 0\n })\n );\n } else {\n (, _wethAmountSupplied, _oethbAmountSupplied) = positionManager\n .increaseLiquidity(\n /** amount0Min & amount1Min are left at 0 because slippage protection is ensured by the\n * _checkForExpectedPoolPrice\n */\n INonfungiblePositionManager.IncreaseLiquidityParams({\n tokenId: tokenId,\n amount0Desired: _wethBalance,\n amount1Desired: _oethbRequired,\n amount0Min: 0,\n amount1Min: 0,\n deadline: block.timestamp\n })\n );\n }\n\n _updateUnderlyingAssets();\n emit LiquidityAdded(\n _wethBalance, // wethAmountDesired\n _oethbRequired, // oethbAmountDesired\n _wethAmountSupplied, // wethAmountSupplied\n _oethbAmountSupplied, // oethbAmountSupplied\n tokenId, // tokenId\n underlyingAssets\n );\n\n // burn remaining OETHb\n _burnOethbOnTheContract();\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @dev Check that the Aerodrome pool price is within the expected\n * parameters.\n * This function works whether the strategy contract has liquidity\n * position in the pool or not. The function returns _wethSharePct\n * as a gas optimization measure.\n * @param throwException when set to true the function throws an exception\n * when pool's price is not within expected range.\n * @return _isExpectedRange Bool expressing price is within expected range\n * @return _wethSharePct Share of WETH owned by this strategy contract in the\n * configured ticker.\n */\n function _checkForExpectedPoolPrice(bool throwException)\n internal\n view\n returns (bool _isExpectedRange, uint256 _wethSharePct)\n {\n require(\n allowedWethShareStart != 0 && allowedWethShareEnd != 0,\n \"Weth share interval not set\"\n );\n\n uint160 _currentPrice = getPoolX96Price();\n\n /**\n * First check we are in expected tick range\n *\n * We revert even though price being equal to the lower tick would still\n * count being within lower tick for the purpose of Sugar.estimateAmount calls\n */\n if (\n _currentPrice <= sqrtRatioX96TickLower ||\n _currentPrice >= sqrtRatioX96TickHigher\n ) {\n if (throwException) {\n revert OutsideExpectedTickRange(getCurrentTradingTick());\n }\n return (false, 0);\n }\n\n // 18 decimal number expressed WETH tick share\n _wethSharePct = _getWethShare(_currentPrice);\n\n if (\n _wethSharePct < allowedWethShareStart ||\n _wethSharePct > allowedWethShareEnd\n ) {\n if (throwException) {\n revert PoolRebalanceOutOfBounds(\n _wethSharePct,\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n return (false, _wethSharePct);\n }\n\n return (true, _wethSharePct);\n }\n\n /**\n * Burns any OETHb tokens remaining on the strategy contract\n */\n function _burnOethbOnTheContract() internal {\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n if (_oethbBalance > 1e12) {\n IVault(vaultAddress).burnForStrategy(_oethbBalance);\n }\n }\n\n /// @dev This function assumes there are no uncollected tokens in the clPool owned by the strategy contract.\n /// For that reason any liquidity withdrawals must also collect the tokens.\n function _updateUnderlyingAssets() internal {\n if (tokenId == 0) {\n underlyingAssets = 0;\n emit UnderlyingAssetsUpdated(underlyingAssets);\n return;\n }\n\n uint128 _liquidity = _getLiquidity();\n\n /**\n * Our net value represent the smallest amount of tokens we are able to extract from the position\n * given our liquidity.\n *\n * The least amount of tokens extraditable from the position is where the active trading price is\n * at the ticker 0 meaning the pool is offering 1:1 trades between WETH & OETHb. At that moment the pool\n * consists completely of OETHb and no WETH.\n *\n * The more swaps from WETH -> OETHb happen on the pool the more the price starts to move towards the -1\n * ticker making OETHb (priced in WETH) more expensive.\n *\n * An additional note: when liquidity is 0 then the helper returns 0 for both token amounts. And the\n * function set underlying assets to 0.\n */\n (uint256 _wethAmount, uint256 _oethbAmount) = helper\n .getAmountsForLiquidity(\n sqrtRatioX96TickClosestToParity, // sqrtRatioX96\n sqrtRatioX96TickLower, // sqrtRatioAX96\n sqrtRatioX96TickHigher, // sqrtRatioBX96\n _liquidity\n );\n\n require(_wethAmount == 0, \"Non zero wethAmount\");\n underlyingAssets = _oethbAmount;\n emit UnderlyingAssetsUpdated(underlyingAssets);\n }\n\n /**\n * @dev This function removes the appropriate amount of liquidity to assure that the required\n * amount of WETH is available on the contract\n *\n * @param _amount WETH balance required on the contract\n */\n function _ensureWETHBalance(uint256 _amount) internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance >= _amount) {\n return;\n }\n\n require(tokenId != 0, \"No liquidity available\");\n uint256 _additionalWethRequired = _amount - _wethBalance;\n (uint256 _wethInThePool, ) = getPositionPrincipal();\n\n if (_wethInThePool < _additionalWethRequired) {\n revert NotEnoughWethLiquidity(\n _wethInThePool,\n _additionalWethRequired\n );\n }\n\n uint256 shareOfWethToRemove = Math.min(\n _additionalWethRequired.divPrecisely(_wethInThePool) + 1,\n 1e18\n );\n _removeLiquidity(shareOfWethToRemove);\n }\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset WETH address\n * @param _amount Amount of WETH to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n _ensureWETHBalance(_amount);\n\n _withdraw(_recipient, _amount);\n }\n\n /**\n * @notice Withdraw WETH and sends it to the Vault.\n */\n function withdrawAll() external override onlyVault nonReentrant {\n if (tokenId != 0) {\n _removeLiquidity(1e18);\n }\n\n uint256 _balance = IERC20(WETH).balanceOf(address(this));\n if (_balance > 0) {\n _withdraw(vaultAddress, _balance);\n }\n }\n\n function _withdraw(address _recipient, uint256 _amount) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n IERC20(WETH).safeTransfer(_recipient, _amount);\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /**\n * @dev Collect the AERO token from the gauge\n */\n function _collectRewardTokens() internal override {\n if (tokenId != 0 && _isLpTokenStakedInGauge()) {\n clGauge.getReward(tokenId);\n }\n super._collectRewardTokens();\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /**\n * @dev Approve the spending of all assets\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n // to add liquidity to the clPool\n IERC20(OETHb).approve(address(positionManager), type(uint256).max);\n // to be able to rebalance using the swapRouter\n IERC20(OETHb).approve(address(swapRouter), type(uint256).max);\n\n /* the behaviour of this strategy has slightly changed and WETH could be\n * present on the contract between the transactions. For that reason we are\n * un-approving WETH to the swapRouter & positionManager and only approving\n * the required amount before a transaction\n */\n IERC20(WETH).approve(address(swapRouter), 0);\n IERC20(WETH).approve(address(positionManager), 0);\n }\n\n /***************************************\n Balances and Fees\n ****************************************/\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256)\n {\n require(_asset == WETH, \"Only WETH supported\");\n\n // we could in theory deposit to the strategy and forget to call rebalance in the same\n // governance transaction batch. In that case the WETH that is on the strategy contract\n // also needs to be accounted for.\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n // just paranoia check, in case there is OETHb in the strategy that for some reason hasn't\n // been burned yet.\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n return underlyingAssets + _wethBalance + _oethbBalance;\n }\n\n /**\n * @dev Returns the balance of both tokens in a given position (excluding fees)\n * @return _amountWeth Amount of WETH in position\n * @return _amountOethb Amount of OETHb in position\n */\n function getPositionPrincipal()\n public\n view\n returns (uint256 _amountWeth, uint256 _amountOethb)\n {\n if (tokenId == 0) {\n return (0, 0);\n }\n\n uint160 _sqrtRatioX96 = getPoolX96Price();\n (_amountWeth, _amountOethb) = helper.principal(\n positionManager,\n tokenId,\n _sqrtRatioX96\n );\n }\n\n /**\n * @notice Returns the current pool price in X96 format\n * @return _sqrtRatioX96 Pool price\n */\n function getPoolX96Price() public view returns (uint160 _sqrtRatioX96) {\n (_sqrtRatioX96, , , , , ) = clPool.slot0();\n }\n\n /**\n * @notice Returns the current active trading tick of the underlying pool\n * @return _currentTick Current pool trading tick\n */\n function getCurrentTradingTick() public view returns (int24 _currentTick) {\n (, _currentTick, , , , ) = clPool.slot0();\n }\n\n /**\n * @notice Returns the percentage of WETH liquidity in the configured ticker\n * owned by this strategy contract.\n * @return uint256 1e18 denominated percentage expressing the share\n */\n function getWETHShare() external view returns (uint256) {\n uint160 _currentPrice = getPoolX96Price();\n return _getWethShare(_currentPrice);\n }\n\n /**\n * @notice Returns the amount of liquidity in the contract's LP position\n * @return _liquidity Amount of liquidity in the position\n */\n function _getLiquidity() internal view returns (uint128 _liquidity) {\n if (tokenId == 0) {\n revert(\"No LP position\");\n }\n\n (, , , , , , , _liquidity, , , , ) = positionManager.positions(tokenId);\n }\n\n function _getWethShare(uint160 _currentPrice)\n internal\n view\n returns (uint256)\n {\n /**\n * If estimateAmount1 call fails it could be due to _currentPrice being really\n * close to a tick and amount1 too big to compute.\n *\n * If token addresses were reversed estimateAmount0 would be required here\n */\n uint256 _normalizedWethAmount = 1 ether;\n uint256 _correspondingOethAmount = helper.estimateAmount1(\n _normalizedWethAmount,\n address(0), // no need to pass pool address when current price is specified\n _currentPrice,\n lowerTick,\n upperTick\n );\n\n // 18 decimal number expressed weth tick share\n return\n _normalizedWethAmount.divPrecisely(\n _normalizedWethAmount + _correspondingOethAmount\n );\n }\n\n /***************************************\n Hidden functions\n ****************************************/\n /// @inheritdoc InitializableAbstractStrategy\n function setPTokenAddress(address, address) external override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /// @inheritdoc InitializableAbstractStrategy\n function removePToken(uint256) external override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Not supported\n */\n function _abstractSetPToken(address, address) internal override {\n // the deployer shall call safeApproveAllTokens() to set necessary approvals\n revert(\"Unsupported method\");\n }\n\n /***************************************\n ERC721 management\n ****************************************/\n\n /// @notice Callback function for whenever a NFT is transferred to this contract\n // solhint-disable-next-line max-line-length\n /// Ref: https://docs.openzeppelin.com/contracts/3.x/api/token/erc721#IERC721Receiver-onERC721Received-address-address-uint256-bytes-\n function onERC721Received(\n address,\n address,\n uint256,\n bytes calldata\n ) external returns (bytes4) {\n return this.onERC721Received.selector;\n }\n}\n" + }, + "contracts/strategies/balancer/AbstractAuraStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Base Balancer Abstract Strategy\n * @author Origin Protocol Inc\n */\n\nimport { AbstractBalancerStrategy } from \"./AbstractBalancerStrategy.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IERC4626 } from \"../../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport { IRewardStaking } from \"../IRewardStaking.sol\";\n\nabstract contract AbstractAuraStrategy is AbstractBalancerStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n /// @notice Address of the Aura rewards pool\n address public immutable auraRewardPoolAddress;\n\n // renamed from __reserved to not shadow AbstractBalancerStrategy.__reserved,\n int256[50] private __reserved_baseAuraStrategy;\n\n constructor(address _auraRewardPoolAddress) {\n auraRewardPoolAddress = _auraRewardPoolAddress;\n }\n\n /**\n * @dev Deposit all Balancer Pool Tokens (BPT) in this strategy contract\n * to the Aura rewards pool.\n */\n function _lpDepositAll() internal virtual override {\n uint256 bptBalance = IERC20(platformAddress).balanceOf(address(this));\n uint256 auraLp = IERC4626(auraRewardPoolAddress).deposit(\n bptBalance,\n address(this)\n );\n require(bptBalance == auraLp, \"Aura LP != BPT\");\n }\n\n /**\n * @dev Withdraw `numBPTTokens` Balancer Pool Tokens (BPT) from\n * the Aura rewards pool to this strategy contract.\n * @param numBPTTokens Number of Balancer Pool Tokens (BPT) to withdraw\n */\n function _lpWithdraw(uint256 numBPTTokens) internal virtual override {\n IRewardStaking(auraRewardPoolAddress).withdrawAndUnwrap(\n numBPTTokens,\n true // also claim reward tokens\n );\n }\n\n /**\n * @dev Withdraw all Balancer Pool Tokens (BPT) from\n * the Aura rewards pool to this strategy contract.\n */\n function _lpWithdrawAll() internal virtual override {\n // Get all the strategy's BPTs in Aura\n // maxRedeem is implemented as balanceOf(address) in Aura\n uint256 bptBalance = IERC4626(auraRewardPoolAddress).maxRedeem(\n address(this)\n );\n\n IRewardStaking(auraRewardPoolAddress).withdrawAndUnwrap(\n bptBalance,\n true // also claim reward tokens\n );\n }\n\n /**\n * @notice Collects BAL and AURA tokens from the rewards pool.\n */\n function collectRewardTokens()\n external\n virtual\n override\n onlyHarvester\n nonReentrant\n {\n /* Similar to Convex, calling this function collects both of the\n * accrued BAL and AURA tokens.\n */\n IRewardStaking(auraRewardPoolAddress).getReward();\n _collectRewardTokens();\n }\n\n /// @notice Balancer Pool Tokens (BPT) in the Balancer pool and the Aura rewards pool.\n function _getBalancerPoolTokens()\n internal\n view\n override\n returns (uint256 balancerPoolTokens)\n {\n balancerPoolTokens =\n IERC20(platformAddress).balanceOf(address(this)) +\n // maxRedeem is implemented as balanceOf(address) in Aura\n IERC4626(auraRewardPoolAddress).maxRedeem(address(this));\n }\n\n function _approveBase() internal virtual override {\n super._approveBase();\n\n IERC20 pToken = IERC20(platformAddress);\n pToken.safeApprove(auraRewardPoolAddress, type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/balancer/AbstractBalancerStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Balancer Abstract Strategy\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\nimport { IRateProvider } from \"../../interfaces/balancer/IRateProvider.sol\";\nimport { VaultReentrancyLib } from \"./VaultReentrancyLib.sol\";\nimport { IOracle } from \"../../interfaces/IOracle.sol\";\nimport { IWstETH } from \"../../interfaces/IWstETH.sol\";\nimport { IERC4626 } from \"../../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nabstract contract AbstractBalancerStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n address public immutable rETH;\n address public immutable stETH;\n address public immutable wstETH;\n address public immutable frxETH;\n address public immutable sfrxETH;\n\n /// @notice Address of the Balancer vault\n IBalancerVault public immutable balancerVault;\n /// @notice Balancer pool identifier\n bytes32 public immutable balancerPoolId;\n\n // Max withdrawal deviation denominated in 1e18 (1e18 == 100%)\n uint256 public maxWithdrawalDeviation;\n // Max deposit deviation denominated in 1e18 (1e18 == 100%)\n uint256 public maxDepositDeviation;\n\n int256[48] private __reserved;\n\n struct BaseBalancerConfig {\n address rEthAddress; // Address of the rETH token\n address stEthAddress; // Address of the stETH token\n address wstEthAddress; // Address of the wstETH token\n address frxEthAddress; // Address of the frxEth token\n address sfrxEthAddress; // Address of the sfrxEth token\n address balancerVaultAddress; // Address of the Balancer vault\n bytes32 balancerPoolId; // Balancer pool identifier\n }\n\n event MaxWithdrawalDeviationUpdated(\n uint256 _prevMaxDeviationPercentage,\n uint256 _newMaxDeviationPercentage\n );\n event MaxDepositDeviationUpdated(\n uint256 _prevMaxDeviationPercentage,\n uint256 _newMaxDeviationPercentage\n );\n\n /**\n * @dev Ensure we are not in a Vault context when this function is called, by attempting a no-op internal\n * balance operation. If we are already in a Vault transaction (e.g., a swap, join, or exit), the Vault's\n * reentrancy protection will cause this function to revert.\n *\n * Use this modifier with any function that can cause a state change in a pool and is either public itself,\n * or called by a public function *outside* a Vault operation (e.g., join, exit, or swap).\n *\n * This is to protect against Balancer's read-only re-entrancy vulnerability:\n * https://www.notion.so/originprotocol/Balancer-read-only-reentrancy-c686e72c82414ef18fa34312bb02e11b\n */\n modifier whenNotInBalancerVaultContext() {\n VaultReentrancyLib.ensureNotInVaultContext(balancerVault);\n _;\n }\n\n constructor(BaseBalancerConfig memory _balancerConfig) {\n rETH = _balancerConfig.rEthAddress;\n stETH = _balancerConfig.stEthAddress;\n wstETH = _balancerConfig.wstEthAddress;\n frxETH = _balancerConfig.frxEthAddress;\n sfrxETH = _balancerConfig.sfrxEthAddress;\n\n balancerVault = IBalancerVault(_balancerConfig.balancerVaultAddress);\n balancerPoolId = _balancerConfig.balancerPoolId;\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Balancer's strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of BAL & AURA\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * WETH, stETH\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // BAL & AURA\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n maxWithdrawalDeviation = 1e16;\n maxDepositDeviation = 1e16;\n\n emit MaxWithdrawalDeviationUpdated(0, maxWithdrawalDeviation);\n emit MaxDepositDeviationUpdated(0, maxDepositDeviation);\n\n IERC20[] memory poolAssets = _getPoolAssets();\n require(\n poolAssets.length == _assets.length,\n \"Pool assets length mismatch\"\n );\n for (uint256 i = 0; i < _assets.length; ++i) {\n address asset = _fromPoolAsset(address(poolAssets[i]));\n require(_assets[i] == asset, \"Pool assets mismatch\");\n }\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @notice Get strategy's share of an assets in the Balancer pool.\n * This is not denominated in OUSD/ETH value of the assets in the Balancer pool.\n * @param _asset Address of the Vault collateral asset\n * @return amount the amount of vault collateral assets\n *\n * IMPORTANT if this function is overridden it needs to have a whenNotInBalancerVaultContext\n * modifier on it or it is susceptible to read-only re-entrancy attack\n *\n * @dev it is important that this function is not affected by reporting inflated\n * values of assets in case of any pool manipulation. Such a manipulation could easily\n * exploit the protocol by:\n * - minting OETH\n * - tilting Balancer pool to report higher balances of assets\n * - rebasing() -> all that extra token balances get distributed to OETH holders\n * - tilting pool back\n * - redeeming OETH\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n whenNotInBalancerVaultContext\n returns (uint256 amount)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n\n uint256 bptBalance = _getBalancerPoolTokens();\n\n /* To calculate the worth of queried asset:\n * - assume that all tokens normalized to their ETH value have an equal split balance\n * in the pool when it is balanced\n * - multiply the BPT amount with the bpt rate to get the ETH denominated amount\n * of strategy's holdings\n * - divide that by the number of tokens we support in the pool to get ETH denominated\n * amount that is applicable to each supported token in the pool.\n *\n * It would be possible to support only 1 asset in the pool (and be exposed to all\n * the assets while holding BPT tokens) and deposit/withdraw/checkBalance using only\n * that asset. TBD: changes to other functions still required if we ever decide to\n * go with such configuration.\n */\n amount = (bptBalance.mulTruncate(\n IRateProvider(platformAddress).getRate()\n ) / assetsMapped.length);\n\n /* If the pool asset is equal to (strategy )_asset it means that a rate\n * provider for that asset exists and that asset is not necessarily\n * pegged to a unit (ETH).\n *\n * Because this function returns the balance of the asset and is not denominated in\n * ETH units we need to convert the ETH denominated amount to asset amount.\n */\n if (_toPoolAsset(_asset) == _asset) {\n amount = amount.divPrecisely(_getRateProviderRate(_asset));\n }\n }\n\n /**\n * @notice Returns the value of all assets managed by this strategy.\n * Uses the Balancer pool's rate (virtual price) to convert the strategy's\n * Balancer Pool Tokens (BPT) to ETH value.\n * @return value The ETH value\n *\n * IMPORTANT if this function is overridden it needs to have a whenNotInBalancerVaultContext\n * modifier on it or it is susceptible to read-only re-entrancy attack\n */\n function checkBalance()\n external\n view\n virtual\n whenNotInBalancerVaultContext\n returns (uint256 value)\n {\n uint256 bptBalance = _getBalancerPoolTokens();\n\n // Convert BPT to ETH value\n value = bptBalance.mulTruncate(\n IRateProvider(platformAddress).getRate()\n );\n }\n\n /// @notice Balancer Pool Tokens (BPT) in the Balancer pool.\n function _getBalancerPoolTokens()\n internal\n view\n virtual\n returns (uint256 balancerPoolTokens)\n {\n balancerPoolTokens = IERC20(platformAddress).balanceOf(address(this));\n }\n\n /* solhint-disable max-line-length */\n /**\n * @notice BPT price is calculated by taking the rate from the rateProvider of the asset in\n * question. If one does not exist it defaults to 1e18. To get the final BPT expected that\n * is multiplied by the underlying asset amount divided by BPT token rate. BPT token rate is\n * similar to Curve's virtual_price and expresses how much has the price of BPT appreciated\n * (e.g. due to swap fees) in relation to the underlying assets\n *\n * Using the above approach makes the strategy vulnerable to a possible MEV attack using\n * flash loan to manipulate the pool before a deposit/withdrawal since the function ignores\n * market values of the assets being priced in BPT.\n *\n * At the time of writing there is no safe on-chain approach to pricing BPT in a way that it\n * would make it invulnerable to MEV pool manipulation. See recent Balancer exploit:\n * https://www.notion.so/originprotocol/Balancer-OETH-strategy-9becdea132704e588782a919d7d471eb?pvs=4#1cf07de12fc64f1888072321e0644348\n *\n * To mitigate MEV possibilities during deposits and withdraws, the VaultValueChecker will use checkBalance before and after the move\n * to ensure the expected changes took place.\n *\n * @param _asset Address of the Balancer pool asset\n * @param _amount Amount of the Balancer pool asset\n * @return bptExpected of BPT expected in exchange for the asset\n *\n * @dev\n * bptAssetPrice = 1e18 (asset peg) * pool_asset_rate\n *\n * bptExpected = bptAssetPrice * asset_amount / BPT_token_rate\n *\n * bptExpected = 1e18 (asset peg) * pool_asset_rate * asset_amount / BPT_token_rate\n * bptExpected = asset_amount * pool_asset_rate / BPT_token_rate\n *\n * further information available here:\n * https://www.notion.so/originprotocol/Balancer-OETH-strategy-9becdea132704e588782a919d7d471eb?pvs=4#ce01495ae70346d8971f5dced809fb83\n */\n /* solhint-enable max-line-length */\n function _getBPTExpected(address _asset, uint256 _amount)\n internal\n view\n virtual\n returns (uint256 bptExpected)\n {\n uint256 bptRate = IRateProvider(platformAddress).getRate();\n uint256 poolAssetRate = _getRateProviderRate(_asset);\n bptExpected = _amount.mulTruncate(poolAssetRate).divPrecisely(bptRate);\n }\n\n function _getBPTExpected(\n address[] memory _assets,\n uint256[] memory _amounts\n ) internal view virtual returns (uint256 bptExpected) {\n require(_assets.length == _amounts.length, \"Assets & amounts mismatch\");\n\n for (uint256 i = 0; i < _assets.length; ++i) {\n uint256 poolAssetRate = _getRateProviderRate(_assets[i]);\n // convert asset amount to ETH amount\n bptExpected += _amounts[i].mulTruncate(poolAssetRate);\n }\n\n uint256 bptRate = IRateProvider(platformAddress).getRate();\n // Convert ETH amount to BPT amount\n bptExpected = bptExpected.divPrecisely(bptRate);\n }\n\n function _lpDepositAll() internal virtual;\n\n function _lpWithdraw(uint256 numBPTTokens) internal virtual;\n\n function _lpWithdrawAll() internal virtual;\n\n /**\n * @notice Balancer returns assets and rateProviders for corresponding assets ordered\n * by numerical order.\n */\n function _getPoolAssets() internal view returns (IERC20[] memory assets) {\n // slither-disable-next-line unused-return\n (assets, , ) = balancerVault.getPoolTokens(balancerPoolId);\n }\n\n /**\n * @dev If an asset is rebasing the Balancer pools have a wrapped versions of assets\n * that the strategy supports. This function converts the pool(wrapped) asset\n * and corresponding amount to strategy asset.\n */\n function _toPoolAsset(address asset, uint256 amount)\n internal\n view\n returns (address poolAsset, uint256 poolAmount)\n {\n if (asset == stETH) {\n poolAsset = wstETH;\n if (amount > 0) {\n poolAmount = IWstETH(wstETH).getWstETHByStETH(amount);\n }\n } else if (asset == frxETH) {\n poolAsset = sfrxETH;\n if (amount > 0) {\n poolAmount = IERC4626(sfrxETH).convertToShares(amount);\n }\n } else {\n poolAsset = asset;\n poolAmount = amount;\n }\n }\n\n /**\n * @dev Converts a Vault collateral asset to a Balancer pool asset.\n * stETH becomes wstETH, frxETH becomes sfrxETH and everything else stays the same.\n * @param asset Address of the Vault collateral asset.\n * @return Address of the Balancer pool asset.\n */\n function _toPoolAsset(address asset) internal view returns (address) {\n if (asset == stETH) {\n return wstETH;\n } else if (asset == frxETH) {\n return sfrxETH;\n }\n return asset;\n }\n\n /**\n * @dev Converts rebasing asset to its wrapped counterpart.\n */\n function _wrapPoolAsset(address asset, uint256 amount)\n internal\n returns (address wrappedAsset, uint256 wrappedAmount)\n {\n if (asset == stETH) {\n wrappedAsset = wstETH;\n if (amount > 0) {\n wrappedAmount = IWstETH(wstETH).wrap(amount);\n }\n } else if (asset == frxETH) {\n wrappedAsset = sfrxETH;\n if (amount > 0) {\n wrappedAmount = IERC4626(sfrxETH).deposit(\n amount,\n address(this)\n );\n }\n } else {\n wrappedAsset = asset;\n wrappedAmount = amount;\n }\n }\n\n /**\n * @dev Converts wrapped asset to its rebasing counterpart.\n */\n function _unwrapPoolAsset(address asset, uint256 amount)\n internal\n returns (uint256 unwrappedAmount)\n {\n if (asset == stETH) {\n unwrappedAmount = IWstETH(wstETH).unwrap(amount);\n } else if (asset == frxETH) {\n unwrappedAmount = IERC4626(sfrxETH).withdraw(\n amount,\n address(this),\n address(this)\n );\n } else {\n unwrappedAmount = amount;\n }\n }\n\n /**\n * @dev If an asset is rebasing the Balancer pools have a wrapped versions of assets\n * that the strategy supports. This function converts the rebasing strategy asset\n * and corresponding amount to wrapped(pool) asset.\n */\n function _fromPoolAsset(address poolAsset, uint256 poolAmount)\n internal\n view\n returns (address asset, uint256 amount)\n {\n if (poolAsset == wstETH) {\n asset = stETH;\n if (poolAmount > 0) {\n amount = IWstETH(wstETH).getStETHByWstETH(poolAmount);\n }\n } else if (poolAsset == sfrxETH) {\n asset = frxETH;\n if (poolAmount > 0) {\n amount = IERC4626(sfrxETH).convertToAssets(poolAmount);\n }\n } else {\n asset = poolAsset;\n amount = poolAmount;\n }\n }\n\n function _fromPoolAsset(address poolAsset)\n internal\n view\n returns (address asset)\n {\n if (poolAsset == wstETH) {\n asset = stETH;\n } else if (poolAsset == sfrxETH) {\n asset = frxETH;\n } else {\n asset = poolAsset;\n }\n }\n\n /**\n * @notice Sets max withdrawal deviation that is considered when removing\n * liquidity from Balancer pools.\n * @param _maxWithdrawalDeviation Max withdrawal deviation denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxWithdrawalDeviation will be 1% (1e16) for production\n * usage. Vault value checker in combination with checkBalance will\n * catch any unexpected manipulation.\n */\n function setMaxWithdrawalDeviation(uint256 _maxWithdrawalDeviation)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(\n _maxWithdrawalDeviation <= 1e18,\n \"Withdrawal dev. out of bounds\"\n );\n emit MaxWithdrawalDeviationUpdated(\n maxWithdrawalDeviation,\n _maxWithdrawalDeviation\n );\n maxWithdrawalDeviation = _maxWithdrawalDeviation;\n }\n\n /**\n * @notice Sets max deposit deviation that is considered when adding\n * liquidity to Balancer pools.\n * @param _maxDepositDeviation Max deposit deviation denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxDepositDeviation will default to 1% (1e16)\n * for production usage. Vault value checker in combination with\n * checkBalance will catch any unexpected manipulation.\n */\n function setMaxDepositDeviation(uint256 _maxDepositDeviation)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(_maxDepositDeviation <= 1e18, \"Deposit dev. out of bounds\");\n emit MaxDepositDeviationUpdated(\n maxDepositDeviation,\n _maxDepositDeviation\n );\n maxDepositDeviation = _maxDepositDeviation;\n }\n\n function _approveBase() internal virtual {\n IERC20 pToken = IERC20(platformAddress);\n // Balancer vault for BPT token (required for removing liquidity)\n pToken.safeApprove(address(balancerVault), type(uint256).max);\n }\n\n function _getRateProviderRate(address _asset)\n internal\n view\n virtual\n returns (uint256);\n}\n" + }, + "contracts/strategies/balancer/BalancerMetaPoolStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Balancer MetaStablePool Strategy\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { AbstractAuraStrategy, AbstractBalancerStrategy } from \"./AbstractAuraStrategy.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\nimport { IRateProvider } from \"../../interfaces/balancer/IRateProvider.sol\";\nimport { IMetaStablePool } from \"../../interfaces/balancer/IMetaStablePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\ncontract BalancerMetaPoolStrategy is AbstractAuraStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(\n BaseStrategyConfig memory _stratConfig,\n BaseBalancerConfig memory _balancerConfig,\n address _auraRewardPoolAddress\n )\n InitializableAbstractStrategy(_stratConfig)\n AbstractBalancerStrategy(_balancerConfig)\n AbstractAuraStrategy(_auraRewardPoolAddress)\n {}\n\n /**\n * @notice There are no plans to configure BalancerMetaPool as a default\n * asset strategy. For that reason there is no need to support this\n * functionality.\n */\n function deposit(address, uint256)\n external\n override\n onlyVault\n nonReentrant\n {\n revert(\"Not supported\");\n }\n\n /**\n * @notice There are no plans to configure BalancerMetaPool as a default\n * asset strategy. For that reason there is no need to support this\n * functionality.\n */\n function deposit(address[] calldata, uint256[] calldata)\n external\n onlyVault\n nonReentrant\n {\n revert(\"Not supported\");\n }\n\n /**\n * @notice Deposits all supported assets in this strategy contract to the Balancer pool.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 assetsLength = assetsMapped.length;\n address[] memory strategyAssets = new address[](assetsLength);\n uint256[] memory strategyAmounts = new uint256[](assetsLength);\n\n // For each vault collateral asset\n for (uint256 i = 0; i < assetsLength; ++i) {\n strategyAssets[i] = assetsMapped[i];\n // Get the asset balance in this strategy contract\n strategyAmounts[i] = IERC20(strategyAssets[i]).balanceOf(\n address(this)\n );\n }\n _deposit(strategyAssets, strategyAmounts);\n }\n\n /*\n * _deposit doesn't require a read-only re-entrancy protection since during the deposit\n * the function enters the Balancer Vault Context. If this function were called as part of\n * the attacking contract (while intercepting execution flow upon receiving ETH) the read-only\n * protection of the Balancer Vault would be triggered. Since the attacking contract would\n * already be in the Balancer Vault context and wouldn't be able to enter it again.\n */\n function _deposit(\n address[] memory _strategyAssets,\n uint256[] memory _strategyAmounts\n ) internal {\n require(\n _strategyAssets.length == _strategyAmounts.length,\n \"Array length missmatch\"\n );\n\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n\n uint256[] memory strategyAssetAmountsToPoolAssetAmounts = new uint256[](\n _strategyAssets.length\n );\n address[] memory strategyAssetsToPoolAssets = new address[](\n _strategyAssets.length\n );\n\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n address strategyAsset = _strategyAssets[i];\n uint256 strategyAmount = _strategyAmounts[i];\n\n require(\n assetToPToken[strategyAsset] != address(0),\n \"Unsupported asset\"\n );\n strategyAssetsToPoolAssets[i] = _toPoolAsset(strategyAsset);\n\n if (strategyAmount > 0) {\n emit Deposit(strategyAsset, platformAddress, strategyAmount);\n\n // wrap rebasing assets like stETH and frxETH to wstETH and sfrxETH\n (, strategyAssetAmountsToPoolAssetAmounts[i]) = _wrapPoolAsset(\n strategyAsset,\n strategyAmount\n );\n }\n }\n\n uint256[] memory amountsIn = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n for (uint256 i = 0; i < tokens.length; ++i) {\n // Convert IERC20 type to address\n poolAssets[i] = address(tokens[i]);\n\n // For each of the mapped assets\n for (uint256 j = 0; j < strategyAssetsToPoolAssets.length; ++j) {\n // If the pool asset is the same as the mapped asset\n if (poolAssets[i] == strategyAssetsToPoolAssets[j]) {\n amountsIn[i] = strategyAssetAmountsToPoolAssetAmounts[j];\n }\n }\n }\n\n uint256 minBPT = _getBPTExpected(\n strategyAssetsToPoolAssets,\n strategyAssetAmountsToPoolAssetAmounts\n );\n uint256 minBPTwDeviation = minBPT.mulTruncate(\n 1e18 - maxDepositDeviation\n );\n\n /* EXACT_TOKENS_IN_FOR_BPT_OUT:\n * User sends precise quantities of tokens, and receives an\n * estimated but unknown (computed at run time) quantity of BPT.\n *\n * ['uint256', 'uint256[]', 'uint256']\n * [EXACT_TOKENS_IN_FOR_BPT_OUT, amountsIn, minimumBPT]\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,\n amountsIn,\n minBPTwDeviation\n );\n\n IBalancerVault.JoinPoolRequest memory request = IBalancerVault\n .JoinPoolRequest(poolAssets, amountsIn, userData, false);\n\n // Add the pool assets in this strategy to the balancer pool\n balancerVault.joinPool(\n balancerPoolId,\n address(this),\n address(this),\n request\n );\n\n // Deposit the Balancer Pool Tokens (BPT) into Aura\n _lpDepositAll();\n }\n\n /**\n * @notice Withdraw a Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAsset Address of the Vault collateral asset\n * @param _strategyAmount The amount of Vault collateral assets to withdraw\n */\n function withdraw(\n address _recipient,\n address _strategyAsset,\n uint256 _strategyAmount\n ) external override onlyVault nonReentrant {\n address[] memory strategyAssets = new address[](1);\n uint256[] memory strategyAmounts = new uint256[](1);\n strategyAssets[0] = _strategyAsset;\n strategyAmounts[0] = _strategyAmount;\n\n _withdraw(_recipient, strategyAssets, strategyAmounts);\n }\n\n /**\n * @notice Withdraw multiple Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAssets Addresses of the Vault collateral assets\n * @param _strategyAmounts The amounts of Vault collateral assets to withdraw\n */\n function withdraw(\n address _recipient,\n address[] calldata _strategyAssets,\n uint256[] calldata _strategyAmounts\n ) external onlyVault nonReentrant {\n _withdraw(_recipient, _strategyAssets, _strategyAmounts);\n }\n\n /**\n * @dev Withdraw multiple Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAssets Addresses of the Vault collateral assets\n * @param _strategyAmounts The amounts of Vault collateral assets to withdraw\n *\n * _withdrawal doesn't require a read-only re-entrancy protection since during the withdrawal\n * the function enters the Balancer Vault Context. If this function were called as part of\n * the attacking contract (while intercepting execution flow upon receiving ETH) the read-only\n * protection of the Balancer Vault would be triggered. Since the attacking contract would\n * already be in the Balancer Vault context and wouldn't be able to enter it again.\n */\n function _withdraw(\n address _recipient,\n address[] memory _strategyAssets,\n uint256[] memory _strategyAmounts\n ) internal {\n require(\n _strategyAssets.length == _strategyAmounts.length,\n \"Invalid input arrays\"\n );\n\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n require(\n assetToPToken[_strategyAssets[i]] != address(0),\n \"Unsupported asset\"\n );\n }\n\n // STEP 1 - Calculate the Balancer pool assets and amounts from the vault collateral assets\n\n // Get all the supported balancer pool assets\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n // Calculate the balancer pool assets and amounts to withdraw\n uint256[] memory poolAssetsAmountsOut = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n // Is the wrapped asset amount indexed by the assets array, not the order of the Balancer pool tokens\n // eg wstETH and sfrxETH amounts, not the stETH and frxETH amounts\n uint256[] memory strategyAssetsToPoolAssetsAmounts = new uint256[](\n _strategyAssets.length\n );\n\n // For each of the Balancer pool assets\n for (uint256 i = 0; i < tokens.length; ++i) {\n poolAssets[i] = address(tokens[i]);\n\n // Convert the Balancer pool asset back to a vault collateral asset\n address strategyAsset = _fromPoolAsset(poolAssets[i]);\n\n // for each of the vault assets\n for (uint256 j = 0; j < _strategyAssets.length; ++j) {\n // If the vault asset equals the vault asset mapped from the Balancer pool asset\n if (_strategyAssets[j] == strategyAsset) {\n (, poolAssetsAmountsOut[i]) = _toPoolAsset(\n strategyAsset,\n _strategyAmounts[j]\n );\n strategyAssetsToPoolAssetsAmounts[j] = poolAssetsAmountsOut[\n i\n ];\n\n /* Because of the potential Balancer rounding error mentioned below\n * the contract might receive 1-2 WEI smaller amount than required\n * in the withdraw user data encoding. If slightly lesser token amount\n * is received the strategy can not unwrap the pool asset as it is\n * smaller than expected.\n *\n * For that reason we `overshoot` the required tokens expected to\n * circumvent the error\n */\n if (poolAssetsAmountsOut[i] > 0) {\n poolAssetsAmountsOut[i] += 2;\n }\n }\n }\n }\n\n // STEP 2 - Calculate the max about of Balancer Pool Tokens (BPT) to withdraw\n\n // Estimate the required amount of Balancer Pool Tokens (BPT) for the assets\n uint256 maxBPTtoWithdraw = _getBPTExpected(\n poolAssets,\n /* all non 0 values are overshot by 2 WEI and with the expected mainnet\n * ~1% withdrawal deviation, the 2 WEI aren't important\n */\n poolAssetsAmountsOut\n );\n // Increase BPTs by the max allowed deviation\n // Any excess BPTs will be left in this strategy contract\n maxBPTtoWithdraw = maxBPTtoWithdraw.mulTruncate(\n 1e18 + maxWithdrawalDeviation\n );\n\n // STEP 3 - Withdraw the Balancer Pool Tokens (BPT) from Aura to this strategy contract\n\n // Withdraw BPT from Aura allowing for BPTs left in this strategy contract from previous withdrawals\n _lpWithdraw(\n maxBPTtoWithdraw - IERC20(platformAddress).balanceOf(address(this))\n );\n\n // STEP 4 - Withdraw the balancer pool assets from the pool\n\n /* Custom asset exit: BPT_IN_FOR_EXACT_TOKENS_OUT:\n * User sends an estimated but unknown (computed at run time) quantity of BPT,\n * and receives precise quantities of specified tokens.\n *\n * ['uint256', 'uint256[]', 'uint256']\n * [BPT_IN_FOR_EXACT_TOKENS_OUT, amountsOut, maxBPTAmountIn]\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.BPT_IN_FOR_EXACT_TOKENS_OUT,\n poolAssetsAmountsOut,\n maxBPTtoWithdraw\n );\n\n IBalancerVault.ExitPoolRequest memory request = IBalancerVault\n .ExitPoolRequest(\n poolAssets,\n /* We specify the exact amount of a tokens we are expecting in the encoded\n * userData, for that reason we don't need to specify the amountsOut here.\n *\n * Also Balancer has a rounding issue that can make a transaction fail:\n * https://github.com/balancer/balancer-v2-monorepo/issues/2541\n * which is an extra reason why this field is empty.\n */\n new uint256[](tokens.length),\n userData,\n false\n );\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n /* Payable keyword is required because of the IBalancerVault interface even though\n * this strategy shall never be receiving native ETH\n */\n payable(address(this)),\n request\n );\n\n // STEP 5 - Re-deposit any left over BPT tokens back into Aura\n /* When concluding how much of BPT we need to withdraw from Aura we overshoot by\n * roughly around 1% (initial mainnet setting of maxWithdrawalDeviation). After exiting\n * the pool strategy could have left over BPT tokens that are not earning boosted yield.\n * We re-deploy those back in.\n */\n _lpDepositAll();\n\n // STEP 6 - Unswap balancer pool assets to vault collateral assets and send to the vault.\n\n // For each of the specified assets\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n // Unwrap assets like wstETH and sfrxETH to rebasing assets stETH and frxETH\n if (strategyAssetsToPoolAssetsAmounts[i] > 0) {\n _unwrapPoolAsset(\n _strategyAssets[i],\n strategyAssetsToPoolAssetsAmounts[i]\n );\n }\n\n // Transfer the vault collateral assets to the recipient, which is typically the vault\n if (_strategyAmounts[i] > 0) {\n IERC20(_strategyAssets[i]).safeTransfer(\n _recipient,\n _strategyAmounts[i]\n );\n\n emit Withdrawal(\n _strategyAssets[i],\n platformAddress,\n _strategyAmounts[i]\n );\n }\n }\n }\n\n /**\n * @notice Withdraws all supported Vault collateral assets from the Balancer pool\n * and send to the OToken's Vault.\n *\n * Is only executable by the OToken's Vault or the Governor.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n // STEP 1 - Withdraw all Balancer Pool Tokens (BPT) from Aura to this strategy contract\n\n _lpWithdrawAll();\n // Get the BPTs withdrawn from Aura plus any that were already in this strategy contract\n uint256 BPTtoWithdraw = IERC20(platformAddress).balanceOf(\n address(this)\n );\n // Get the balancer pool assets and their total balances\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n uint256[] memory minAmountsOut = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n for (uint256 i = 0; i < tokens.length; ++i) {\n poolAssets[i] = address(tokens[i]);\n }\n\n // STEP 2 - Withdraw the Balancer pool assets from the pool\n /* Proportional exit: EXACT_BPT_IN_FOR_TOKENS_OUT:\n * User sends a precise quantity of BPT, and receives an estimated but unknown\n * (computed at run time) quantity of a single token\n *\n * ['uint256', 'uint256']\n * [EXACT_BPT_IN_FOR_TOKENS_OUT, bptAmountIn]\n *\n * It is ok to pass an empty minAmountsOut since tilting the pool in any direction\n * when doing a proportional exit can only be beneficial to the strategy. Since\n * it will receive more of the underlying tokens for the BPT traded in.\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.EXACT_BPT_IN_FOR_TOKENS_OUT,\n BPTtoWithdraw\n );\n\n IBalancerVault.ExitPoolRequest memory request = IBalancerVault\n .ExitPoolRequest(poolAssets, minAmountsOut, userData, false);\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n /* Payable keyword is required because of the IBalancerVault interface even though\n * this strategy shall never be receiving native ETH\n */\n payable(address(this)),\n request\n );\n\n // STEP 3 - Convert the balancer pool assets to the vault collateral assets and send to the vault\n // For each of the Balancer pool assets\n for (uint256 i = 0; i < tokens.length; ++i) {\n address poolAsset = address(tokens[i]);\n // Convert the balancer pool asset to the strategy asset\n address strategyAsset = _fromPoolAsset(poolAsset);\n // Get the balancer pool assets withdraw from the pool plus any that were already in this strategy contract\n uint256 poolAssetAmount = IERC20(poolAsset).balanceOf(\n address(this)\n );\n\n // Unwrap assets like wstETH and sfrxETH to rebasing assets stETH and frxETH\n uint256 unwrappedAmount = 0;\n if (poolAssetAmount > 0) {\n unwrappedAmount = _unwrapPoolAsset(\n strategyAsset,\n poolAssetAmount\n );\n }\n\n // Transfer the vault collateral assets to the vault\n if (unwrappedAmount > 0) {\n IERC20(strategyAsset).safeTransfer(\n vaultAddress,\n unwrappedAmount\n );\n emit Withdrawal(\n strategyAsset,\n platformAddress,\n unwrappedAmount\n );\n }\n }\n }\n\n /**\n * @notice Approves the Balancer Vault to transfer poolAsset counterparts\n * of all of the supported assets from this strategy. E.g. stETH is a supported\n * strategy and Balancer Vault gets unlimited approval to transfer wstETH.\n *\n * If Balancer pool uses a wrapped version of a supported asset then also approve\n * unlimited usage of an asset to the contract responsible for wrapping.\n *\n * Approve unlimited spending by Balancer Vault and Aura reward pool of the\n * pool BPT tokens.\n *\n * Is only executable by the Governor.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n _abstractSetPToken(assetsMapped[i], platformAddress);\n }\n _approveBase();\n }\n\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address) internal override {\n address poolAsset = _toPoolAsset(_asset);\n if (_asset == stETH) {\n // slither-disable-next-line unused-return\n IERC20(stETH).approve(wstETH, type(uint256).max);\n } else if (_asset == frxETH) {\n // slither-disable-next-line unused-return\n IERC20(frxETH).approve(sfrxETH, type(uint256).max);\n }\n _approveAsset(poolAsset);\n }\n\n /**\n * @dev Approves the Balancer Vault to transfer an asset from\n * this strategy. The assets could be a Vault collateral asset\n * like WETH or rETH; or a Balancer pool asset that wraps the vault asset\n * like wstETH or sfrxETH.\n */\n function _approveAsset(address _asset) internal {\n IERC20 asset = IERC20(_asset);\n // slither-disable-next-line unused-return\n asset.approve(address(balancerVault), type(uint256).max);\n }\n\n /**\n * @notice Returns the rate supplied by the Balancer configured rate\n * provider. Rate is used to normalize the token to common underlying\n * pool denominator. (ETH for ETH Liquid staking derivatives)\n *\n * @param _asset Address of the Balancer pool asset\n * @return rate of the corresponding asset\n */\n function _getRateProviderRate(address _asset)\n internal\n view\n override\n returns (uint256)\n {\n IMetaStablePool pool = IMetaStablePool(platformAddress);\n IRateProvider[] memory providers = pool.getRateProviders();\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n\n uint256 providersLength = providers.length;\n for (uint256 i = 0; i < providersLength; ++i) {\n // _assets and corresponding rate providers are all in the same order\n if (address(tokens[i]) == _asset) {\n // rate provider doesn't exist, defaults to 1e18\n if (address(providers[i]) == address(0)) {\n return 1e18;\n }\n return providers[i].getRate();\n }\n }\n\n // should never happen\n assert(false);\n }\n}\n" + }, + "contracts/strategies/balancer/VaultReentrancyLib.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity >=0.7.0 <0.9.0;\n\nimport \"../../utils/BalancerErrors.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\n\nlibrary VaultReentrancyLib {\n /**\n * @dev Ensure we are not in a Vault context when this function is called, by attempting a no-op internal\n * balance operation. If we are already in a Vault transaction (e.g., a swap, join, or exit), the Vault's\n * reentrancy protection will cause this function to revert.\n *\n * The exact function call doesn't really matter: we're just trying to trigger the Vault reentrancy check\n * (and not hurt anything in case it works). An empty operation array with no specific operation at all works\n * for that purpose, and is also the least expensive in terms of gas and bytecode size.\n *\n * Call this at the top of any function that can cause a state change in a pool and is either public itself,\n * or called by a public function *outside* a Vault operation (e.g., join, exit, or swap).\n *\n * If this is *not* called in functions that are vulnerable to the read-only reentrancy issue described\n * here (https://forum.balancer.fi/t/reentrancy-vulnerability-scope-expanded/4345), those functions are unsafe,\n * and subject to manipulation that may result in loss of funds.\n */\n function ensureNotInVaultContext(IBalancerVault vault) internal view {\n // Perform the following operation to trigger the Vault's reentrancy guard:\n //\n // IBalancerVault.UserBalanceOp[] memory noop = new IBalancerVault.UserBalanceOp[](0);\n // _vault.manageUserBalance(noop);\n //\n // However, use a static call so that it can be a view function (even though the function is non-view).\n // This allows the library to be used more widely, as some functions that need to be protected might be\n // view.\n //\n // This staticcall always reverts, but we need to make sure it doesn't fail due to a re-entrancy attack.\n // Staticcalls consume all gas forwarded to them on a revert caused by storage modification.\n // By default, almost the entire available gas is forwarded to the staticcall,\n // causing the entire call to revert with an 'out of gas' error.\n //\n // We set the gas limit to 10k for the staticcall to\n // avoid wasting gas when it reverts due to storage modification.\n // `manageUserBalance` is a non-reentrant function in the Vault, so calling it invokes `_enterNonReentrant`\n // in the `ReentrancyGuard` contract, reproduced here:\n //\n // function _enterNonReentrant() private {\n // // If the Vault is actually being reentered, it will revert in the first line, at the `_require` that\n // // checks the reentrancy flag, with \"BAL#400\" (corresponding to Errors.REENTRANCY) in the revertData.\n // // The full revertData will be: `abi.encodeWithSignature(\"Error(string)\", \"BAL#400\")`.\n // _require(_status != _ENTERED, Errors.REENTRANCY);\n //\n // // If the Vault is not being reentered, the check above will pass: but it will *still* revert,\n // // because the next line attempts to modify storage during a staticcall. However, this type of\n // // failure results in empty revertData.\n // _status = _ENTERED;\n // }\n //\n // So based on this analysis, there are only two possible revertData values: empty, or abi.encoded BAL#400.\n //\n // It is of course much more bytecode and gas efficient to check for zero-length revertData than to compare it\n // to the encoded REENTRANCY revertData.\n //\n // While it should be impossible for the call to fail in any other way (especially since it reverts before\n // `manageUserBalance` even gets called), any other error would generate non-zero revertData, so checking for\n // empty data guards against this case too.\n\n (, bytes memory revertData) = address(vault).staticcall{ gas: 10_000 }(\n abi.encodeWithSelector(vault.manageUserBalance.selector, 0)\n );\n\n _require(revertData.length == 0, Errors.REENTRANCY);\n }\n}\n" + }, + "contracts/strategies/BaseCurveAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for the Curve OETH/WETH pool\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { ICurveStableSwapNG } from \"../interfaces/ICurveStableSwapNG.sol\";\nimport { ICurveXChainLiquidityGauge } from \"../interfaces/ICurveXChainLiquidityGauge.sol\";\nimport { IChildLiquidityGaugeFactory } from \"../interfaces/IChildLiquidityGaugeFactory.sol\";\n\ncontract BaseCurveAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @dev a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n // New immutable variables that must be set in the constructor\n /**\n * @notice Address of the Wrapped ETH (WETH) contract.\n */\n IWETH9 public immutable weth;\n\n /**\n * @notice Address of the OETH token contract.\n */\n IERC20 public immutable oeth;\n\n /**\n * @notice Address of the LP (Liquidity Provider) token contract.\n */\n IERC20 public immutable lpToken;\n\n /**\n * @notice Address of the Curve StableSwap NG pool contract.\n */\n ICurveStableSwapNG public immutable curvePool;\n\n /**\n * @notice Address of the Curve X-Chain Liquidity Gauge contract.\n */\n ICurveXChainLiquidityGauge public immutable gauge;\n\n /**\n * @notice Address of the Child Liquidity Gauge Factory contract.\n */\n IChildLiquidityGaugeFactory public immutable gaugeFactory;\n\n // Ordered list of pool assets\n uint128 public immutable oethCoinIndex;\n uint128 public immutable wethCoinIndex;\n\n /**\n * @notice Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n uint256 public maxSlippage;\n\n event MaxSlippageUpdated(uint256 newMaxSlippage);\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balancesBefore = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffBefore = balancesBefore[wethCoinIndex].toInt256() -\n balancesBefore[oethCoinIndex].toInt256();\n\n _;\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balancesAfter = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffAfter = balancesAfter[wethCoinIndex].toInt256() -\n balancesAfter[oethCoinIndex].toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of ETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _oeth,\n address _weth,\n address _gauge,\n address _gaugeFactory,\n uint128 _oethCoinIndex,\n uint128 _wethCoinIndex\n ) InitializableAbstractStrategy(_baseConfig) {\n oethCoinIndex = _oethCoinIndex;\n wethCoinIndex = _wethCoinIndex;\n\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveStableSwapNG(_baseConfig.platformAddress);\n\n oeth = IERC20(_oeth);\n weth = IWETH9(_weth);\n gauge = ICurveXChainLiquidityGauge(_gauge);\n gaugeFactory = IChildLiquidityGaugeFactory(_gaugeFactory);\n\n _setGovernor(address(0));\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV\n * @param _maxSlippage Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV\n uint256 _maxSlippage\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n address[] memory _assets = new address[](1);\n _assets[0] = address(weth);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n _setMaxSlippage(_maxSlippage);\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit WETH into the Curve pool\n * @param _weth Address of Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to deposit.\n */\n function deposit(address _weth, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_weth, _amount);\n }\n\n function _deposit(address _weth, uint256 _wethAmount) internal {\n require(_wethAmount > 0, \"Must deposit something\");\n require(_weth == address(weth), \"Can only deposit WETH\");\n\n emit Deposit(_weth, address(lpToken), _wethAmount);\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 oethToAdd = uint256(\n _max(\n 0,\n balances[wethCoinIndex].toInt256() +\n _wethAmount.toInt256() -\n balances[oethCoinIndex].toInt256()\n )\n );\n\n /* Add so much OETH so that the pool ends up being balanced. And at minimum\n * add as much OETH as WETH and at maximum twice as much OETH.\n */\n oethToAdd = Math.max(oethToAdd, _wethAmount);\n oethToAdd = Math.min(oethToAdd, _wethAmount * 2);\n\n /* Mint OETH with a strategy that attempts to contribute to stability of OETH/WETH pool. Try\n * to mint so much OETH that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OETH minted will always be at least equal or greater\n * to WETH amount deployed. And never larger than twice the WETH amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(oethToAdd);\n\n emit Deposit(address(oeth), address(lpToken), oethToAdd);\n\n uint256[] memory _amounts = new uint256[](2);\n _amounts[wethCoinIndex] = _wethAmount;\n _amounts[oethCoinIndex] = oethToAdd;\n\n uint256 valueInLpTokens = (_wethAmount + oethToAdd).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Do the deposit to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(_amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool's LP tokens into the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n /**\n * @notice Deposit the strategy's entire balance of WETH into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = weth.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(weth), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _weth Address of the Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to withdraw.\n */\n function withdraw(\n address _recipient,\n address _weth,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_weth == address(weth), \"Can only withdraw WETH\");\n\n emit Withdrawal(_weth, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(_amount);\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough WETH on balanced removal\n */\n uint256[] memory _minWithdrawalAmounts = new uint256[](2);\n _minWithdrawalAmounts[wethCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OETH and any that was left in the strategy\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n\n // Transfer WETH to the recipient\n require(\n weth.transfer(_recipient, _amount),\n \"Transfer of WETH not successful\"\n );\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n function calcTokenToBurn(uint256 _wethAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much WETH\n * we want we can determine how much of OETH we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolWETHBalance = curvePool.balances(wethCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolWETHBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_wethAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = gauge.balanceOf(address(this));\n // Can not withdraw zero LP tokens from the gauge\n if (gaugeTokens == 0) return;\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[] memory minWithdrawAmounts = new uint256[](2);\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(\n lpToken.balanceOf(address(this)),\n minWithdrawAmounts\n );\n\n // Burn all OETH\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Get the strategy contract's WETH balance.\n // This includes all that was removed from the Curve pool and\n // any ether that was sitting in the strategy contract before the removal.\n uint256 ethBalance = weth.balanceOf(address(this));\n require(\n weth.transfer(vaultAddress, ethBalance),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethBalance);\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[] memory amounts = new uint256[](2);\n amounts[oethCoinIndex] = _oTokens;\n\n // Convert OETH to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool LP tokens to the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Deposit(address(oeth), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough ETH.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove OTokens from the Curve pool\n uint256 oethToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n oethCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /**\n * @notice One-sided remove of ETH from the Curve pool, convert to WETH\n * and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for ETH.\n * @dev Curve pool LP tokens is used rather than WETH assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of ETH. Curve's `calc_token_amount` functioun does not include fees.\n * A 3rd party libary can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * caclulate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Curve gauge and remove ETH from the Curve pool\n uint256 ethAmount = _withdrawAndRemoveFromPool(\n _lpTokens,\n wethCoinIndex\n );\n\n // Transfer WETH to the vault\n require(\n weth.transfer(vaultAddress, ethAmount),\n \"Transfer of WETH not successful\"\n );\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(weth), address(lpToken), ethAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the Convex pool and\n * do a one-sided remove of ETH or OETH from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the Convex pool.\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = ETH, 1 = OETH.\n * @return coinsRemoved The amount of ETH or OETH removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Curve gauge\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to ETH value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n // Apply slippage to ETH value\n uint256 minAmount = valueInEth.mulTruncate(uint256(1e18) - maxSlippage);\n\n // Remove just the ETH from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethbSupply = oeth.totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethbSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // CRV rewards flow.\n //---\n // CRV inflation:\n // Gauge receive CRV rewards from inflation.\n // Each checkpoint on the gauge send this CRV inflation to gauge factory.\n // This strategy should call mint on the gauge factory to collect the CRV rewards.\n // ---\n // Extra rewards:\n // Calling claim_rewards on the gauge will only claim extra rewards (outside of CRV).\n // ---\n\n // Mint CRV on Child Liquidity gauge factory\n gaugeFactory.mint(address(gauge));\n // Collect extra gauge rewards (outside of CRV)\n gauge.claim_rewards();\n\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _lpAmount) internal {\n // withdraw lp tokens from the gauge without claiming rewards\n gauge.withdraw(_lpAmount);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // WETH balance needed here for the balance check that happens from vault during depositing.\n balance = weth.balanceOf(address(this));\n uint256 lpTokens = gauge.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += (lpTokens * curvePool.get_virtual_price()) / 1e18;\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(weth);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Sets the maximum slippage allowed for any swap/liquidity operation\n * @param _maxSlippage Maximum slippage allowed, 1e18 = 100%.\n */\n function setMaxSlippage(uint256 _maxSlippage) external onlyGovernor {\n _setMaxSlippage(_maxSlippage);\n }\n\n function _setMaxSlippage(uint256 _maxSlippage) internal {\n require(_maxSlippage <= 5e16, \"Slippage must be less than 100%\");\n maxSlippage = _maxSlippage;\n emit MaxSlippageUpdated(_maxSlippage);\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OETH (required for adding liquidity)\n // slither-disable-next-line unused-return\n oeth.approve(platformAddress, type(uint256).max);\n\n // Approve Curve pool for WETH (required for adding liquidity)\n // slither-disable-next-line unused-return\n weth.approve(platformAddress, type(uint256).max);\n\n // Approve Curve gauge contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Curve gauge.\n // slither-disable-next-line unused-return\n lpToken.approve(address(gauge), type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/BridgedWOETHStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, SafeERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { AggregatorV3Interface } from \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\n\ncontract BridgedWOETHStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using StableMath for uint128;\n using SafeCast for uint256;\n using SafeERC20 for IERC20;\n\n event MaxPriceDiffBpsUpdated(uint128 oldValue, uint128 newValue);\n event WOETHPriceUpdated(uint128 oldValue, uint128 newValue);\n\n IWETH9 public immutable weth;\n IERC20 public immutable bridgedWOETH;\n IERC20 public immutable oethb;\n\n uint256 public constant MAX_PRICE_STALENESS = 2 days;\n\n uint128 public lastOraclePrice;\n uint128 public maxPriceDiffBps;\n\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _weth,\n address _bridgedWOETH,\n address _oethb\n ) InitializableAbstractStrategy(_stratConfig) {\n weth = IWETH9(_weth);\n bridgedWOETH = IERC20(_bridgedWOETH);\n oethb = IERC20(_oethb);\n }\n\n function initialize(uint128 _maxPriceDiffBps)\n external\n onlyGovernor\n initializer\n {\n InitializableAbstractStrategy._initialize(\n new address[](0), // No reward tokens\n new address[](0), // No assets\n new address[](0) // No pTokens\n );\n\n _setMaxPriceDiffBps(_maxPriceDiffBps);\n }\n\n /**\n * @dev Sets the max price diff bps for the wOETH value appreciation\n * @param _maxPriceDiffBps Bps value, 10k == 100%\n */\n function setMaxPriceDiffBps(uint128 _maxPriceDiffBps)\n external\n onlyGovernor\n {\n _setMaxPriceDiffBps(_maxPriceDiffBps);\n }\n\n /**\n * @dev Sets the max price diff bps for the wOETH value appreciation\n * @param _maxPriceDiffBps Bps value, 10k == 100%\n */\n function _setMaxPriceDiffBps(uint128 _maxPriceDiffBps) internal {\n require(\n _maxPriceDiffBps > 0 && _maxPriceDiffBps <= 10000,\n \"Invalid bps value\"\n );\n\n emit MaxPriceDiffBpsUpdated(maxPriceDiffBps, _maxPriceDiffBps);\n\n maxPriceDiffBps = _maxPriceDiffBps;\n }\n\n /**\n * @dev Wrapper for _updateWOETHOraclePrice with nonReentrant flag\n * @return The latest price of wOETH from Oracle\n */\n function updateWOETHOraclePrice() external nonReentrant returns (uint256) {\n return _updateWOETHOraclePrice();\n }\n\n /**\n * @dev Finds the value of bridged wOETH from the Oracle.\n * Ensures that it's within the bounds and reasonable.\n * And stores it.\n *\n * NOTE: Intentionally not caching `Vault.priceProvider` here,\n * since doing so would mean that we also have to update this\n * strategy every time there's a change in oracle router.\n * Besides on L2, the gas is considerably cheaper than mainnet.\n *\n * @return Latest price from oracle\n */\n function _updateWOETHOraclePrice() internal returns (uint256) {\n // WETH price per unit of bridged wOETH\n uint256 oraclePrice = IOracle(IVault(vaultAddress).priceProvider())\n .price(address(bridgedWOETH));\n\n // 1 wOETH > 1 WETH, always\n require(oraclePrice > 1 ether, \"Invalid wOETH value\");\n\n uint128 oraclePrice128 = oraclePrice.toUint128();\n\n // Do some checks\n if (lastOraclePrice > 0) {\n // Make sure the value only goes up\n require(oraclePrice128 >= lastOraclePrice, \"Negative wOETH yield\");\n\n // lastOraclePrice * (1 + maxPriceDiffBps)\n uint256 maxPrice = (lastOraclePrice * (1e4 + maxPriceDiffBps)) /\n 1e4;\n\n // And that it's within the bounds.\n require(oraclePrice128 <= maxPrice, \"Price diff beyond threshold\");\n }\n\n emit WOETHPriceUpdated(lastOraclePrice, oraclePrice128);\n\n // Store the price\n lastOraclePrice = oraclePrice128;\n\n return oraclePrice;\n }\n\n /**\n * @dev Computes & returns the value of given wOETH in WETH\n * @param woethAmount Amount of wOETH\n * @return Value of wOETH in WETH (using the last stored oracle price)\n */\n function getBridgedWOETHValue(uint256 woethAmount)\n public\n view\n returns (uint256)\n {\n return (woethAmount * lastOraclePrice) / 1 ether;\n }\n\n /**\n * @dev Takes in bridged wOETH and mints & returns\n * equivalent amount of OETHb.\n * @param woethAmount Amount of bridged wOETH to transfer in\n */\n function depositBridgedWOETH(uint256 woethAmount)\n external\n onlyGovernorOrStrategist\n nonReentrant\n {\n // Update wOETH price\n uint256 oraclePrice = _updateWOETHOraclePrice();\n\n // Figure out how much they are worth\n uint256 oethToMint = (woethAmount * oraclePrice) / 1 ether;\n\n require(oethToMint > 0, \"Invalid deposit amount\");\n\n // There's no pToken, however, it just uses WOETH address in the event\n emit Deposit(address(weth), address(bridgedWOETH), oethToMint);\n\n // Mint OETHb tokens and transfer it to the caller\n IVault(vaultAddress).mintForStrategy(oethToMint);\n\n // Transfer out minted OETHb\n // slither-disable-next-line unchecked-transfer unused-return\n oethb.transfer(msg.sender, oethToMint);\n\n // Transfer in all bridged wOETH tokens\n // slither-disable-next-line unchecked-transfer unused-return\n bridgedWOETH.transferFrom(msg.sender, address(this), woethAmount);\n }\n\n /**\n * @dev Takes in OETHb and burns it and returns\n * equivalent amount of bridged wOETH.\n * @param oethToBurn Amount of OETHb to burn\n */\n function withdrawBridgedWOETH(uint256 oethToBurn)\n external\n onlyGovernorOrStrategist\n nonReentrant\n {\n // Update wOETH price\n uint256 oraclePrice = _updateWOETHOraclePrice();\n\n // Figure out how much they are worth\n uint256 woethAmount = (oethToBurn * 1 ether) / oraclePrice;\n\n require(woethAmount > 0, \"Invalid withdraw amount\");\n\n // There's no pToken, however, it just uses WOETH address in the event\n emit Withdrawal(address(weth), address(bridgedWOETH), oethToBurn);\n\n // Transfer WOETH back\n // slither-disable-next-line unchecked-transfer unused-return\n bridgedWOETH.transfer(msg.sender, woethAmount);\n\n // Transfer in OETHb\n // slither-disable-next-line unchecked-transfer unused-return\n oethb.transferFrom(msg.sender, address(this), oethToBurn);\n\n // Burn OETHb\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n }\n\n /**\n * @notice Returns the amount of backing WETH the strategy holds\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // Figure out how much wOETH is worth at the time.\n // Always uses the last stored oracle price.\n // Call updateWOETHOraclePrice manually to pull in latest yields.\n\n // NOTE: If the contract has been deployed but the call to\n // `updateWOETHOraclePrice()` has never been made, then this\n // will return zero. It should be fine because the strategy\n // should update the price whenever a deposit/withdraw happens.\n\n // If `updateWOETHOraclePrice()` hasn't been called in a while,\n // the strategy will underreport its holdings but never overreport it.\n\n balance =\n (bridgedWOETH.balanceOf(address(this)) * lastOraclePrice) /\n 1 ether;\n }\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n // Strategist deposits bridged wOETH but the contract only\n // reports the balance in WETH. As far as Vault is concerned,\n // it isn't aware of bridged wOETH token\n return _asset == address(weth);\n }\n\n /***************************************\n Overridden methods\n ****************************************/\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function transferToken(address _asset, uint256 _amount)\n public\n override\n onlyGovernor\n {\n require(\n _asset != address(bridgedWOETH) && _asset != address(weth),\n \"Cannot transfer supported asset\"\n );\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice deposit() function not used for this strategy\n */\n function deposit(address, uint256)\n external\n override\n onlyVault\n nonReentrant\n {\n // Use depositBridgedWOETH() instead\n require(false, \"Deposit disabled\");\n }\n\n /**\n * @notice depositAll() function not used for this strategy\n */\n function depositAll() external override onlyVault nonReentrant {\n // Use depositBridgedWOETH() instead\n require(false, \"Deposit disabled\");\n }\n\n /**\n * @notice withdraw() function not used for this strategy\n */\n function withdraw(\n // solhint-disable-next-line no-unused-vars\n address _recipient,\n // solhint-disable-next-line no-unused-vars\n address _asset,\n // solhint-disable-next-line no-unused-vars\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(false, \"Withdrawal disabled\");\n }\n\n /**\n * @notice withdrawAll() function not used for this strategy\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n // Withdrawal disabled\n }\n\n function _abstractSetPToken(address, address) internal override {\n revert(\"No pTokens are used\");\n }\n\n function safeApproveAllTokens() external override {}\n\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function removePToken(uint256) external override {\n revert(\"No pTokens are used\");\n }\n\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function collectRewardTokens() external override {}\n}\n" + }, + "contracts/strategies/CompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Compound Strategy\n * @notice Investment strategy for Compound like lending platforms. eg Compound and Flux\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICERC20 } from \"./ICompound.sol\";\nimport { AbstractCompoundStrategy, InitializableAbstractStrategy } from \"./AbstractCompoundStrategy.sol\";\nimport { IComptroller } from \"../interfaces/IComptroller.sol\";\nimport { IERC20 } from \"../utils/InitializableAbstractStrategy.sol\";\n\ncontract CompoundStrategy is AbstractCompoundStrategy {\n using SafeERC20 for IERC20;\n event SkippedWithdrawal(address asset, uint256 amount);\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @notice initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @notice Collect accumulated COMP and send to Harvester.\n */\n function collectRewardTokens()\n external\n virtual\n override\n onlyHarvester\n nonReentrant\n {\n // Claim COMP from Comptroller\n ICERC20 cToken = _getCTokenFor(assetsMapped[0]);\n IComptroller comptroller = IComptroller(cToken.comptroller());\n // Only collect from active cTokens, saves gas\n address[] memory ctokensToCollect = new address[](assetsMapped.length);\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n ctokensToCollect[i] = address(_getCTokenFor(assetsMapped[i]));\n }\n // Claim only for this strategy\n address[] memory claimers = new address[](1);\n claimers[0] = address(this);\n // Claim COMP from Comptroller. Only collect for supply, saves gas\n comptroller.claimComp(claimers, ctokensToCollect, false, true);\n // Transfer COMP to Harvester\n IERC20 rewardToken = IERC20(rewardTokenAddresses[0]);\n uint256 balance = rewardToken.balanceOf(address(this));\n emit RewardTokenCollected(\n harvesterAddress,\n rewardTokenAddresses[0],\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n\n /**\n * @notice Deposit asset into the underlying platform\n * @param _asset Address of asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit an asset into the underlying platform\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n ICERC20 cToken = _getCTokenFor(_asset);\n emit Deposit(_asset, address(cToken), _amount);\n require(cToken.mint(_amount) == 0, \"cToken mint failed\");\n }\n\n /**\n * @notice Deposit the entire balance of any supported asset in the strategy into the underlying platform\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n uint256 assetBalance = asset.balanceOf(address(this));\n if (assetBalance > 0) {\n _deposit(address(asset), assetBalance);\n }\n }\n }\n\n /**\n * @notice Withdraw an asset from the underlying platform\n * @param _recipient Address to receive withdrawn assets\n * @param _asset Address of the asset to withdraw\n * @param _amount Amount of assets to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n ICERC20 cToken = _getCTokenFor(_asset);\n // If redeeming 0 cTokens, just skip, else COMP will revert\n uint256 cTokensToRedeem = _convertUnderlyingToCToken(cToken, _amount);\n if (cTokensToRedeem == 0) {\n emit SkippedWithdrawal(_asset, _amount);\n return;\n }\n\n emit Withdrawal(_asset, address(cToken), _amount);\n require(cToken.redeemUnderlying(_amount) == 0, \"Redeem failed\");\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / cTokens\n * We need to approve the cToken and give it permission to spend the asset\n * @param _asset Address of the asset to approve. eg DAI\n * @param _pToken The pToken for the approval. eg cDAI or fDAI\n */\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n // Safe approval\n IERC20(_asset).safeApprove(_pToken, 0);\n IERC20(_asset).safeApprove(_pToken, type(uint256).max);\n }\n\n /**\n * @notice Remove all supported assets from the underlying platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n // Redeem entire balance of cToken\n ICERC20 cToken = _getCTokenFor(address(asset));\n uint256 cTokenBalance = cToken.balanceOf(address(this));\n if (cTokenBalance > 0) {\n require(cToken.redeem(cTokenBalance) == 0, \"Redeem failed\");\n uint256 assetBalance = asset.balanceOf(address(this));\n // Transfer entire balance to Vault\n asset.safeTransfer(vaultAddress, assetBalance);\n\n emit Withdrawal(address(asset), address(cToken), assetBalance);\n }\n }\n }\n\n /**\n * @notice Get the total asset value held in the underlying platform\n * This includes any interest that was generated since depositing.\n * The exchange rate between the cToken and asset gradually increases,\n * causing the cToken to be worth more corresponding asset.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n // Balance is always with token cToken decimals\n ICERC20 cToken = _getCTokenFor(_asset);\n balance = _checkBalance(cToken);\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * underlying = (cTokenAmt * exchangeRate) / 1e18\n * @param _cToken cToken for which to check balance\n * @return balance Total value of the asset in the platform\n */\n function _checkBalance(ICERC20 _cToken)\n internal\n view\n returns (uint256 balance)\n {\n // e.g. 50e8*205316390724364402565641705 / 1e18 = 1.0265..e18\n balance =\n (_cToken.balanceOf(address(this)) * _cToken.exchangeRateStored()) /\n 1e18;\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding cToken,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens() external override {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n address cToken = assetToPToken[address(asset)];\n // Safe approval\n asset.safeApprove(cToken, 0);\n asset.safeApprove(cToken, type(uint256).max);\n }\n }\n}\n" + }, + "contracts/strategies/ConvexEthMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Convex Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for the Curve OETH/ETH pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { ICurveETHPoolV1 } from \"./ICurveETHPoolV1.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\n\ncontract ConvexEthMetaStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 public constant MAX_SLIPPAGE = 1e16; // 1%, same as the Curve UI\n address public constant ETH_ADDRESS =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n // The following slots have been deprecated with immutable variables\n // slither-disable-next-line constable-states\n address private _deprecated_cvxDepositorAddress;\n // slither-disable-next-line constable-states\n address private _deprecated_cvxRewardStaker;\n // slither-disable-next-line constable-states\n uint256 private _deprecated_cvxDepositorPTokenId;\n // slither-disable-next-line constable-states\n address private _deprecated_curvePool;\n // slither-disable-next-line constable-states\n address private _deprecated_lpToken;\n // slither-disable-next-line constable-states\n address private _deprecated_oeth;\n // slither-disable-next-line constable-states\n address private _deprecated_weth;\n\n // Ordered list of pool assets\n // slither-disable-next-line constable-states\n uint128 private _deprecated_oethCoinIndex;\n // slither-disable-next-line constable-states\n uint128 private _deprecated_ethCoinIndex;\n\n // New immutable variables that must be set in the constructor\n address public immutable cvxDepositorAddress;\n IRewardStaking public immutable cvxRewardStaker;\n uint256 public immutable cvxDepositorPTokenId;\n ICurveETHPoolV1 public immutable curvePool;\n IERC20 public immutable lpToken;\n IERC20 public immutable oeth;\n IWETH9 public immutable weth;\n\n // Ordered list of pool assets\n uint128 public constant oethCoinIndex = 1;\n uint128 public constant ethCoinIndex = 0;\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier only works on functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balancesBefore = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffBefore = int256(balancesBefore[ethCoinIndex]) -\n int256(balancesBefore[oethCoinIndex]);\n\n _;\n\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balancesAfter = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffAfter = int256(balancesAfter[ethCoinIndex]) -\n int256(balancesAfter[oethCoinIndex]);\n\n if (diffBefore <= 0) {\n // If the pool was originally imbalanced in favor of OETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n }\n if (diffBefore >= 0) {\n // If the pool was originally imbalanced in favor of ETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n // Used to circumvent the stack too deep issue\n struct ConvexEthMetaConfig {\n address cvxDepositorAddress; //Address of the Convex depositor(AKA booster) for this pool\n address cvxRewardStakerAddress; //Address of the CVX rewards staker\n uint256 cvxDepositorPTokenId; //Pid of the pool referred to by Depositor and staker\n address oethAddress; //Address of OETH token\n address wethAddress; //Address of WETH\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n ConvexEthMetaConfig memory _convexConfig\n ) InitializableAbstractStrategy(_baseConfig) {\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveETHPoolV1(_baseConfig.platformAddress);\n\n cvxDepositorAddress = _convexConfig.cvxDepositorAddress;\n cvxRewardStaker = IRewardStaking(_convexConfig.cvxRewardStakerAddress);\n cvxDepositorPTokenId = _convexConfig.cvxDepositorPTokenId;\n oeth = IERC20(_convexConfig.oethAddress);\n weth = IWETH9(_convexConfig.wethAddress);\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. eg WETH\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets // WETH\n ) external onlyGovernor initializer {\n require(_assets.length == 1, \"Must have exactly one asset\");\n require(_assets[0] == address(weth), \"Asset not WETH\");\n\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit WETH into the Curve pool\n * @param _weth Address of Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to deposit.\n */\n function deposit(address _weth, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_weth, _amount);\n }\n\n function _deposit(address _weth, uint256 _wethAmount) internal {\n require(_wethAmount > 0, \"Must deposit something\");\n require(_weth == address(weth), \"Can only deposit WETH\");\n weth.withdraw(_wethAmount);\n\n emit Deposit(_weth, address(lpToken), _wethAmount);\n\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 oethToAdd = uint256(\n _max(\n 0,\n int256(balances[ethCoinIndex]) +\n int256(_wethAmount) -\n int256(balances[oethCoinIndex])\n )\n );\n\n /* Add so much OETH so that the pool ends up being balanced. And at minimum\n * add as much OETH as WETH and at maximum twice as much OETH.\n */\n oethToAdd = Math.max(oethToAdd, _wethAmount);\n oethToAdd = Math.min(oethToAdd, _wethAmount * 2);\n\n /* Mint OETH with a strategy that attempts to contribute to stability of OETH/WETH pool. Try\n * to mint so much OETH that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OETH minted will always be at least equal or greater\n * to WETH amount deployed. And never larger than twice the WETH amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(oethToAdd);\n\n emit Deposit(address(oeth), address(lpToken), oethToAdd);\n\n uint256[2] memory _amounts;\n _amounts[ethCoinIndex] = _wethAmount;\n _amounts[oethCoinIndex] = oethToAdd;\n\n uint256 valueInLpTokens = (_wethAmount + oethToAdd).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Do the deposit to the Curve pool\n // slither-disable-next-line arbitrary-send\n uint256 lpDeposited = curvePool.add_liquidity{ value: _wethAmount }(\n _amounts,\n minMintAmount\n );\n\n // Deposit the Curve pool's LP tokens into the Convex rewards pool\n require(\n IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n lpDeposited,\n true // Deposit with staking\n ),\n \"Depositing LP to Convex not successful\"\n );\n }\n\n /**\n * @notice Deposit the strategy's entire balance of WETH into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = weth.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(weth), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _weth Address of the Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to withdraw.\n */\n function withdraw(\n address _recipient,\n address _weth,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Invalid amount\");\n require(_weth == address(weth), \"Can only withdraw WETH\");\n\n emit Withdrawal(_weth, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(_amount);\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough WETH on balanced removal\n */\n uint256[2] memory _minWithdrawalAmounts = [uint256(0), uint256(0)];\n _minWithdrawalAmounts[ethCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OETH and any that was left in the strategy\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n\n // Transfer WETH to the recipient\n weth.deposit{ value: _amount }();\n require(\n weth.transfer(_recipient, _amount),\n \"Transfer of WETH not successful\"\n );\n }\n\n function calcTokenToBurn(uint256 _wethAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much WETH\n * we want we can determine how much of OETH we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolWETHBalance = curvePool.balances(ethCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolWETHBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_wethAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = cvxRewardStaker.balanceOf(address(this));\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[2] memory minWithdrawAmounts = [uint256(0), uint256(0)];\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(\n lpToken.balanceOf(address(this)),\n minWithdrawAmounts\n );\n\n // Burn all OETH\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Get the strategy contract's ether balance.\n // This includes all that was removed from the Curve pool and\n // any ether that was sitting in the strategy contract before the removal.\n uint256 ethBalance = address(this).balance;\n // Convert all the strategy contract's ether to WETH and transfer to the vault.\n weth.deposit{ value: ethBalance }();\n require(\n weth.transfer(vaultAddress, ethBalance),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethBalance);\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[2] memory amounts = [uint256(0), uint256(0)];\n amounts[oethCoinIndex] = _oTokens;\n\n // Convert OETH to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n\n // Deposit the Curve pool LP tokens to the Convex rewards pool\n require(\n IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n lpDeposited,\n true // Deposit with staking\n ),\n \"Failed to Deposit LP to Convex\"\n );\n\n emit Deposit(address(oeth), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough ETH.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove OTokens from the Curve pool\n uint256 oethToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n oethCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /**\n * @notice One-sided remove of ETH from the Curve pool, convert to WETH\n * and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for ETH.\n * @dev Curve pool LP tokens is used rather than WETH assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of ETH. Curve's `calc_token_amount` functioun does not include fees.\n * A 3rd party libary can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * caclulate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove ETH from the Curve pool\n uint256 ethAmount = _withdrawAndRemoveFromPool(_lpTokens, ethCoinIndex);\n\n // Convert ETH to WETH and transfer to the vault\n weth.deposit{ value: ethAmount }();\n require(\n weth.transfer(vaultAddress, ethAmount),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the Convex pool and\n * do a one-sided remove of ETH or OETH from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the Convex pool.\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = ETH, 1 = OETH.\n * @return coinsRemoved The amount of ETH or OETH removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Convex pool\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to ETH value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n // Apply slippage to ETH value\n uint256 minAmount = valueInEth.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Remove just the ETH from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV and CVX rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n cvxRewardStaker.getReward();\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _wethAmount) internal {\n // withdraw and unwrap with claim takes back the lpTokens\n // and also collects the rewards for deposit\n cvxRewardStaker.withdrawAndUnwrap(_wethAmount, true);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // Eth balance needed here for the balance check that happens from vault during depositing.\n balance = address(this).balance;\n uint256 lpTokens = cvxRewardStaker.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += (lpTokens * curvePool.get_virtual_price()) / 1e18;\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(weth);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @notice Accept unwrapped WETH\n */\n receive() external payable {}\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OETH (required for adding liquidity)\n // No approval is needed for ETH\n // slither-disable-next-line unused-return\n oeth.approve(platformAddress, type(uint256).max);\n\n // Approve Convex deposit contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Convex rewards pool\n // slither-disable-next-line unused-return\n lpToken.approve(cvxDepositorAddress, type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/ConvexGeneralizedMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { AbstractConvexMetaStrategy } from \"./AbstractConvexMetaStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract ConvexGeneralizedMetaStrategy is AbstractConvexMetaStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /* Take 3pool LP and deposit it to metapool. Take the LP from metapool\n * and deposit them to Convex.\n */\n function _lpDepositAll() internal override {\n IERC20 threePoolLp = IERC20(pTokenAddress);\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 threePoolLpBalance = threePoolLp.balanceOf(address(this));\n uint256 curve3PoolVirtualPrice = curvePool.get_virtual_price();\n uint256 threePoolLpDollarValue = threePoolLpBalance.mulTruncate(\n curve3PoolVirtualPrice\n );\n\n uint256[2] memory _amounts = [0, threePoolLpBalance];\n\n uint256 metapoolVirtualPrice = metapool.get_virtual_price();\n /**\n * First convert all the deposited tokens to dollar values,\n * then divide by virtual price to convert to metapool LP tokens\n * and apply the max slippage\n */\n uint256 minReceived = threePoolLpDollarValue\n .divPrecisely(metapoolVirtualPrice)\n .mulTruncate(uint256(1e18) - MAX_SLIPPAGE);\n\n uint256 metapoolLp = metapool.add_liquidity(_amounts, minReceived);\n\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n metapoolLp,\n true // Deposit with staking\n );\n\n require(success, \"Failed to deposit to Convex\");\n }\n\n /**\n * Withdraw the specified amount of tokens from the gauge. And use all the resulting tokens\n * to remove liquidity from metapool\n * @param num3CrvTokens Number of Convex 3pool LP tokens to withdraw from metapool\n */\n function _lpWithdraw(uint256 num3CrvTokens) internal override {\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n uint256 requiredMetapoolLpTokens = _calcCurveMetaTokenAmount(\n crvCoinIndex,\n num3CrvTokens\n );\n\n require(\n requiredMetapoolLpTokens <= gaugeTokens,\n string(\n bytes.concat(\n bytes(\"Attempting to withdraw \"),\n bytes(Strings.toString(requiredMetapoolLpTokens)),\n bytes(\", metapoolLP but only \"),\n bytes(Strings.toString(gaugeTokens)),\n bytes(\" available.\")\n )\n )\n );\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards for deposit\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n requiredMetapoolLpTokens,\n true\n );\n\n if (requiredMetapoolLpTokens > 0) {\n // slither-disable-next-line unused-return\n metapool.remove_liquidity_one_coin(\n requiredMetapoolLpTokens,\n int128(crvCoinIndex),\n num3CrvTokens\n );\n }\n }\n\n function _lpWithdrawAll() internal override {\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n gaugeTokens,\n true\n );\n\n if (gaugeTokens > 0) {\n uint256 burnDollarAmount = gaugeTokens.mulTruncate(\n metapool.get_virtual_price()\n );\n uint256 curve3PoolExpected = burnDollarAmount.divPrecisely(\n ICurvePool(platformAddress).get_virtual_price()\n );\n\n // Always withdraw all of the available metapool LP tokens (similar to how we always deposit all)\n // slither-disable-next-line unused-return\n metapool.remove_liquidity_one_coin(\n gaugeTokens,\n int128(crvCoinIndex),\n curve3PoolExpected -\n curve3PoolExpected.mulTruncate(maxWithdrawalSlippage)\n );\n }\n }\n}\n" + }, + "contracts/strategies/ConvexOUSDMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { AbstractConvexMetaStrategy } from \"./AbstractConvexMetaStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract ConvexOUSDMetaStrategy is AbstractConvexMetaStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /* Take 3pool LP and mint the corresponding amount of ousd. Deposit and stake that to\n * ousd Curve Metapool. Take the LP from metapool and deposit them to Convex.\n */\n function _lpDepositAll() internal override {\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 threePoolLpBalance = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n uint256 curve3PoolVirtualPrice = curvePool.get_virtual_price();\n uint256 threePoolLpDollarValue = threePoolLpBalance.mulTruncate(\n curve3PoolVirtualPrice\n );\n\n // safe to cast since min value is at least 0\n uint256 ousdToAdd = uint256(\n _max(\n 0,\n int256(\n metapool.balances(crvCoinIndex).mulTruncate(\n curve3PoolVirtualPrice\n )\n ) -\n int256(metapool.balances(mainCoinIndex)) +\n int256(threePoolLpDollarValue)\n )\n );\n\n /* Add so much OUSD so that the pool ends up being balanced. And at minimum\n * add twice as much OUSD as 3poolLP and at maximum at twice as\n * much OUSD.\n */\n ousdToAdd = Math.max(ousdToAdd, threePoolLpDollarValue);\n ousdToAdd = Math.min(ousdToAdd, threePoolLpDollarValue * 2);\n\n /* Mint OUSD with a strategy that attempts to contribute to stability of OUSD metapool. Try\n * to mint so much OUSD that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OUSD minted will always be at least equal or greater\n * to stablecoin(DAI, USDC, USDT) amount of 3CRVLP deployed. And never larger than twice the\n * stablecoin amount of 3CRVLP deployed even if it would have a further beneficial effect\n * on pool stability.\n */\n if (ousdToAdd > 0) {\n IVault(vaultAddress).mintForStrategy(ousdToAdd);\n }\n\n uint256[2] memory _amounts = [ousdToAdd, threePoolLpBalance];\n\n uint256 metapoolVirtualPrice = metapool.get_virtual_price();\n /**\n * First convert all the deposited tokens to dollar values,\n * then divide by virtual price to convert to metapool LP tokens\n * and apply the max slippage\n */\n uint256 minReceived = (ousdToAdd + threePoolLpDollarValue)\n .divPrecisely(metapoolVirtualPrice)\n .mulTruncate(uint256(1e18) - MAX_SLIPPAGE);\n\n uint256 metapoolLp = metapool.add_liquidity(_amounts, minReceived);\n\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n metapoolLp,\n true // Deposit with staking\n );\n\n require(success, \"Failed to deposit to Convex\");\n }\n\n /**\n * Withdraw the specified amount of tokens from the gauge. And use all the resulting tokens\n * to remove liquidity from metapool\n * @param num3CrvTokens Number of 3CRV tokens to withdraw from metapool\n */\n function _lpWithdraw(uint256 num3CrvTokens) internal override {\n ICurvePool curvePool = ICurvePool(platformAddress);\n /* The rate between coins in the metapool determines the rate at which metapool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much 3crvLp\n * we want we can determine how much of OUSD we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 crvPoolBalance = metapool.balances(crvCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * metapool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * metapoolLPToken.totalSupply()) / crvPoolBalance;\n // simplifying below to: `uint256 diff = (num3CrvTokens - 1) * k` causes loss of precision\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = crvPoolBalance * k -\n (crvPoolBalance - num3CrvTokens - 1) * k;\n uint256 lpToBurn = diff / 1e36;\n\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n require(\n lpToBurn <= gaugeTokens,\n string(\n bytes.concat(\n bytes(\"Attempting to withdraw \"),\n bytes(Strings.toString(lpToBurn)),\n bytes(\", metapoolLP but only \"),\n bytes(Strings.toString(gaugeTokens)),\n bytes(\" available.\")\n )\n )\n );\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards for deposit\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n lpToBurn,\n true\n );\n\n // calculate the min amount of OUSD expected for the specified amount of LP tokens\n uint256 minOUSDAmount = lpToBurn.mulTruncate(\n metapool.get_virtual_price()\n ) -\n num3CrvTokens.mulTruncate(curvePool.get_virtual_price()) -\n 1;\n\n // withdraw the liquidity from metapool\n uint256[2] memory _removedAmounts = metapool.remove_liquidity(\n lpToBurn,\n [minOUSDAmount, num3CrvTokens]\n );\n\n IVault(vaultAddress).burnForStrategy(_removedAmounts[mainCoinIndex]);\n }\n\n function _lpWithdrawAll() internal override {\n IERC20 metapoolErc20 = IERC20(address(metapool));\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n gaugeTokens,\n true\n );\n\n uint256[2] memory _minAmounts = [uint256(0), uint256(0)];\n uint256[2] memory _removedAmounts = metapool.remove_liquidity(\n metapoolErc20.balanceOf(address(this)),\n _minAmounts\n );\n\n IVault(vaultAddress).burnForStrategy(_removedAmounts[mainCoinIndex]);\n }\n}\n" + }, + "contracts/strategies/ConvexStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { IERC20, AbstractCurveStrategy, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\n/*\n * IMPORTANT(!) If ConvexStrategy needs to be re-deployed, it requires new\n * proxy contract with fresh storage slots. Changes in `AbstractCurveStrategy`\n * storage slots would break existing implementation.\n *\n * Remove this notice if ConvexStrategy is re-deployed\n */\ncontract ConvexStrategy is AbstractCurveStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n address internal cvxDepositorAddress;\n address internal cvxRewardStakerAddress;\n // slither-disable-next-line constable-states\n address private _deprecated_cvxRewardTokenAddress;\n uint256 internal cvxDepositorPTokenId;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * DAI, USDC, USDT\n * @param _pTokens Platform Token corresponding addresses\n * @param _cvxDepositorAddress Address of the Convex depositor(AKA booster) for this pool\n * @param _cvxRewardStakerAddress Address of the CVX rewards staker\n * @param _cvxDepositorPTokenId Pid of the pool referred to by Depositor and staker\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets,\n address[] calldata _pTokens,\n address _cvxDepositorAddress,\n address _cvxRewardStakerAddress,\n uint256 _cvxDepositorPTokenId\n ) external onlyGovernor initializer {\n require(_assets.length == 3, \"Must have exactly three assets\");\n // Should be set prior to abstract initialize call otherwise\n // abstractSetPToken calls will fail\n cvxDepositorAddress = _cvxDepositorAddress;\n cvxRewardStakerAddress = _cvxRewardStakerAddress;\n cvxDepositorPTokenId = _cvxDepositorPTokenId;\n pTokenAddress = _pTokens[0];\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n function _lpDepositAll() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // Deposit with staking\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n pToken.balanceOf(address(this)),\n true\n );\n require(success, \"Failed to deposit to Convex\");\n }\n\n function _lpWithdraw(uint256 numCrvTokens) internal override {\n uint256 gaugePTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n // Not enough in this contract or in the Gauge, can't proceed\n require(numCrvTokens > gaugePTokens, \"Insufficient 3CRV balance\");\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards to this\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n numCrvTokens,\n true\n );\n }\n\n function _lpWithdrawAll() internal override {\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards to this\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n IRewardStaking(cvxRewardStakerAddress).balanceOf(address(this)),\n true\n );\n }\n\n function _approveBase() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // 3Pool for LP token (required for removing liquidity)\n pToken.safeApprove(platformAddress, 0);\n pToken.safeApprove(platformAddress, type(uint256).max);\n // Gauge for LP token\n pToken.safeApprove(cvxDepositorAddress, 0);\n pToken.safeApprove(cvxDepositorAddress, type(uint256).max);\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 contractPTokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n uint256 gaugePTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n uint256 totalPTokens = contractPTokens + gaugePTokens;\n\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (totalPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = (totalPTokens * virtual_price) / 1e18;\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = value.scaleBy(assetDecimals, 18) / 3;\n }\n }\n\n /**\n * @dev Collect accumulated CRV and CVX and send to Vault.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n IRewardStaking(cvxRewardStakerAddress).getReward();\n _collectRewardTokens();\n }\n}\n" + }, + "contracts/strategies/CurveAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for a Curve pool using an OToken.\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { ICurveStableSwapNG } from \"../interfaces/ICurveStableSwapNG.sol\";\nimport { ICurveLiquidityGaugeV6 } from \"../interfaces/ICurveLiquidityGaugeV6.sol\";\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\nimport { ICurveMinter } from \"../interfaces/ICurveMinter.sol\";\n\ncontract CurveAMOStrategy is InitializableAbstractStrategy {\n using SafeCast for uint256;\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n /**\n * @dev a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n // New immutable variables that must be set in the constructor\n /**\n * @notice Address of the hard asset (weth, usdt, usdc).\n */\n IERC20 public immutable hardAsset;\n\n /**\n * @notice Address of the OTOKEN token contract.\n */\n IERC20 public immutable oToken;\n\n /**\n * @notice Address of the LP (Liquidity Provider) token contract.\n */\n IERC20 public immutable lpToken;\n\n /**\n * @notice Address of the Curve StableSwap NG pool contract.\n */\n ICurveStableSwapNG public immutable curvePool;\n\n /**\n * @notice Address of the Curve X-Chain Liquidity Gauge contract.\n */\n ICurveLiquidityGaugeV6 public immutable gauge;\n\n /**\n * @notice Address of the Curve Minter contract.\n */\n ICurveMinter public immutable minter;\n\n /**\n * @notice Index of the OTOKEN and hardAsset in the Curve pool.\n */\n uint128 public immutable otokenCoinIndex;\n uint128 public immutable hardAssetCoinIndex;\n\n /**\n * @notice Decimals of the OTOKEN and hardAsset.\n */\n uint8 public immutable decimalsOToken;\n uint8 public immutable decimalsHardAsset;\n\n /**\n * @notice Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n uint256 public maxSlippage;\n\n event MaxSlippageUpdated(uint256 newMaxSlippage);\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the hard asset and OToken balances in the Curve pool\n uint256[] memory balancesBefore = curvePool.get_balances();\n // diff = hardAsset balance - OTOKEN balance\n int256 diffBefore = (\n balancesBefore[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() - balancesBefore[otokenCoinIndex].toInt256();\n\n _;\n\n // Get the hard asset and OToken balances in the Curve pool\n uint256[] memory balancesAfter = curvePool.get_balances();\n // diff = hardAsset balance - OTOKEN balance\n int256 diffAfter = (\n balancesAfter[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() - balancesAfter[otokenCoinIndex].toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OTOKEN, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of hardAsset, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _otoken,\n address _hardAsset,\n address _gauge,\n address _minter\n ) InitializableAbstractStrategy(_baseConfig) {\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveStableSwapNG(_baseConfig.platformAddress);\n minter = ICurveMinter(_minter);\n\n oToken = IERC20(_otoken);\n hardAsset = IERC20(_hardAsset);\n gauge = ICurveLiquidityGaugeV6(_gauge);\n decimalsHardAsset = IBasicToken(_hardAsset).decimals();\n decimalsOToken = IBasicToken(_otoken).decimals();\n\n (hardAssetCoinIndex, otokenCoinIndex) = curvePool.coins(0) == _hardAsset\n ? (0, 1)\n : (1, 0);\n require(\n curvePool.coins(otokenCoinIndex) == _otoken &&\n curvePool.coins(hardAssetCoinIndex) == _hardAsset,\n \"Invalid coin indexes\"\n );\n require(gauge.lp_token() == address(curvePool), \"Invalid pool\");\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV\n * @param _maxSlippage Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV\n uint256 _maxSlippage\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n address[] memory _assets = new address[](1);\n _assets[0] = address(hardAsset);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n _setMaxSlippage(_maxSlippage);\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit hard asset into the Curve pool\n * @param _hardAsset Address of hard asset contract.\n * @param _amount Amount of hard asset to deposit.\n */\n function deposit(address _hardAsset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_hardAsset, _amount);\n }\n\n function _deposit(address _hardAsset, uint256 _hardAssetAmount) internal {\n require(_hardAssetAmount > 0, \"Must deposit something\");\n require(_hardAsset == address(hardAsset), \"Unsupported asset\");\n\n emit Deposit(_hardAsset, address(lpToken), _hardAssetAmount);\n uint256 scaledHardAssetAmount = _hardAssetAmount.scaleBy(\n decimalsOToken,\n decimalsHardAsset\n );\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 otokenToAdd = uint256(\n _max(\n 0,\n (\n balances[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() +\n scaledHardAssetAmount.toInt256() -\n balances[otokenCoinIndex].toInt256()\n )\n );\n\n /* Add so much OTOKEN so that the pool ends up being balanced. And at minimum\n * add as much OTOKEN as hard asset and at maximum twice as much OTOKEN.\n */\n otokenToAdd = Math.max(otokenToAdd, scaledHardAssetAmount);\n otokenToAdd = Math.min(otokenToAdd, scaledHardAssetAmount * 2);\n\n /* Mint OTOKEN with a strategy that attempts to contribute to stability of OTOKEN/hardAsset pool. Try\n * to mint so much OTOKEN that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OTOKEN minted will always be at least equal or greater\n * to hardAsset amount deployed. And never larger than twice the hardAsset amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(otokenToAdd);\n\n emit Deposit(address(oToken), address(lpToken), otokenToAdd);\n\n uint256[] memory _amounts = new uint256[](2);\n _amounts[hardAssetCoinIndex] = _hardAssetAmount;\n _amounts[otokenCoinIndex] = otokenToAdd;\n\n uint256 valueInLpTokens = (scaledHardAssetAmount + otokenToAdd)\n .divPrecisely(curvePool.get_virtual_price());\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Do the deposit to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(_amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool's LP tokens into the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n /**\n * @notice Deposit the strategy's entire balance of hardAsset into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = hardAsset.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(hardAsset), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw hardAsset and OTOKEN from the Curve pool, burn the OTOKEN,\n * transfer hardAsset to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _hardAsset Address of the hardAsset contract.\n * @param _amount Amount of hardAsset to withdraw.\n */\n function withdraw(\n address _recipient,\n address _hardAsset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(\n _hardAsset == address(hardAsset),\n \"Can only withdraw hard asset\"\n );\n\n emit Withdrawal(_hardAsset, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(\n _amount.scaleBy(decimalsOToken, decimalsHardAsset)\n );\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough hardAsset on balanced removal\n */\n uint256[] memory _minWithdrawalAmounts = new uint256[](2);\n _minWithdrawalAmounts[hardAssetCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OTOKEN and any that was left in the strategy\n uint256 otokenToBurn = oToken.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n\n // Transfer hardAsset to the recipient\n hardAsset.safeTransfer(_recipient, _amount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n function calcTokenToBurn(uint256 _hardAssetAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much hardAsset\n * we want we can determine how much of OTOKEN we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolHardAssetBalance = curvePool\n .balances(hardAssetCoinIndex)\n .scaleBy(decimalsOToken, decimalsHardAsset);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolHardAssetBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_hardAssetAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all hardAsset and OTOKEN from the Curve pool, burn the OTOKEN,\n * transfer hardAsset to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = gauge.balanceOf(address(this));\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[] memory minWithdrawAmounts = new uint256[](2);\n\n // Check balance of LP tokens in the strategy, if 0 return\n uint256 lpBalance = lpToken.balanceOf(address(this));\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n if (lpBalance > 0) {\n curvePool.remove_liquidity(lpBalance, minWithdrawAmounts);\n }\n\n // Burn all OTOKEN\n uint256 otokenToBurn = oToken.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n // Get the strategy contract's hardAsset balance.\n // This includes all that was removed from the Curve pool and\n // any hardAsset that was sitting in the strategy contract before the removal.\n uint256 hardAssetBalance = hardAsset.balanceOf(address(this));\n hardAsset.safeTransfer(vaultAddress, hardAssetBalance);\n\n if (hardAssetBalance > 0)\n emit Withdrawal(\n address(hardAsset),\n address(lpToken),\n hardAssetBalance\n );\n if (otokenToBurn > 0)\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many hardAsset.\n * The OToken/Asset, eg OTOKEN/hardAsset, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[] memory amounts = new uint256[](2);\n amounts[otokenCoinIndex] = _oTokens;\n\n // Convert OTOKEN to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool LP tokens to the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Deposit(address(oToken), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough hardAsset.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from gauge and remove OTokens from the Curve pool\n uint256 otokenToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n otokenCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n }\n\n /**\n * @notice One-sided remove of hardAsset from the Curve pool and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many hardAsset.\n * The OToken/Asset, eg OTOKEN/hardAsset, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for hardAsset.\n * @dev Curve pool LP tokens is used rather than hardAsset assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of hardAsset. Curve's `calc_token_amount` function does not include fees.\n * A 3rd party library can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * calculate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Curve gauge and remove hardAsset from the Curve pool\n uint256 hardAssetAmount = _withdrawAndRemoveFromPool(\n _lpTokens,\n hardAssetCoinIndex\n );\n\n // Transfer hardAsset to the vault\n hardAsset.safeTransfer(vaultAddress, hardAssetAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(hardAsset), address(lpToken), hardAssetAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the gauge and\n * do a one-sided remove of hardAsset or OTOKEN from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the gauge\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = hardAsset, 1 = OTOKEN.\n * @return coinsRemoved The amount of hardAsset or OTOKEN removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Curve gauge\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to hardAsset value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n\n if (coinIndex == hardAssetCoinIndex) {\n valueInEth = valueInEth.scaleBy(decimalsHardAsset, decimalsOToken);\n }\n\n // Apply slippage to hardAsset value\n uint256 minAmount = valueInEth.mulTruncate(uint256(1e18) - maxSlippage);\n\n // Remove just the hardAsset from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOtokenSupply = oToken.totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOtokenSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV rewards from inflation\n minter.mint(address(gauge));\n\n // Collect extra gauge rewards (outside of CRV)\n gauge.claim_rewards();\n\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _lpAmount) internal {\n require(\n gauge.balanceOf(address(this)) >= _lpAmount,\n \"Insufficient LP tokens\"\n );\n // withdraw lp tokens from the gauge without claiming rewards\n gauge.withdraw(_lpAmount);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(hardAsset), \"Unsupported asset\");\n\n // hardAsset balance needed here for the balance check that happens from vault during depositing.\n balance = hardAsset.balanceOf(address(this));\n uint256 lpTokens = gauge.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += ((lpTokens * curvePool.get_virtual_price()) / 1e18)\n .scaleBy(decimalsHardAsset, decimalsOToken);\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(hardAsset);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Sets the maximum slippage allowed for any swap/liquidity operation\n * @param _maxSlippage Maximum slippage allowed, 1e18 = 100%.\n */\n function setMaxSlippage(uint256 _maxSlippage) external onlyGovernor {\n _setMaxSlippage(_maxSlippage);\n }\n\n function _setMaxSlippage(uint256 _maxSlippage) internal {\n require(_maxSlippage <= 5e16, \"Slippage must be less than 100%\");\n maxSlippage = _maxSlippage;\n emit MaxSlippageUpdated(_maxSlippage);\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OTOKEN (required for adding liquidity)\n // slither-disable-next-line unused-return\n oToken.approve(platformAddress, type(uint256).max);\n\n // Approve Curve pool for hardAsset (required for adding liquidity)\n // slither-disable-next-line unused-return\n hardAsset.safeApprove(platformAddress, type(uint256).max);\n\n // Approve Curve gauge contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Curve gauge.\n // slither-disable-next-line unused-return\n lpToken.approve(address(gauge), type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/CurvePoolBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport { ICampaignRemoteManager } from \"../interfaces/ICampaignRemoteManager.sol\";\n\n/// @title CurvePoolBooster\n/// @author Origin Protocol\n/// @notice Contract to manage interactions with VotemarketV2 for a dedicated Curve pool/gauge.\ncontract CurvePoolBooster is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n ////////////////////////////////////////////////////\n /// --- CONSTANTS && IMMUTABLES\n ////////////////////////////////////////////////////\n /// @notice Base fee for the contract, 100%\n uint16 public constant FEE_BASE = 10_000;\n\n /// @notice Address of the gauge to manage\n address public immutable gauge;\n\n /// @notice Address of the reward token\n address public immutable rewardToken;\n\n /// @notice Chain id of the target chain\n uint256 public immutable targetChainId;\n\n ////////////////////////////////////////////////////\n /// --- STORAGE\n ////////////////////////////////////////////////////\n /// @notice Fee in FEE_BASE unit payed when managing campaign.\n uint16 public fee;\n\n /// @notice Address of the fee collector\n address public feeCollector;\n\n /// @notice Address of the campaignRemoteManager linked to VotemarketV2\n address public campaignRemoteManager;\n\n /// @notice Address of votemarket in L2\n address public votemarket;\n\n /// @notice Id of the campaign created\n uint256 public campaignId;\n\n ////////////////////////////////////////////////////\n /// --- EVENTS\n ////////////////////////////////////////////////////\n event FeeUpdated(uint16 newFee);\n event FeeCollected(address feeCollector, uint256 feeAmount);\n event FeeCollectorUpdated(address newFeeCollector);\n event VotemarketUpdated(address newVotemarket);\n event CampaignRemoteManagerUpdated(address newCampaignRemoteManager);\n event CampaignCreated(\n address gauge,\n address rewardToken,\n uint256 maxRewardPerVote,\n uint256 totalRewardAmount\n );\n event CampaignIdUpdated(uint256 newId);\n event CampaignClosed(uint256 campaignId);\n event TotalRewardAmountUpdated(uint256 extraTotalRewardAmount);\n event NumberOfPeriodsUpdated(uint8 extraNumberOfPeriods);\n event RewardPerVoteUpdated(uint256 newMaxRewardPerVote);\n event TokensRescued(address token, uint256 amount, address receiver);\n\n ////////////////////////////////////////////////////\n /// --- CONSTRUCTOR && INITIALIZATION\n ////////////////////////////////////////////////////\n constructor(\n uint256 _targetChainId,\n address _rewardToken,\n address _gauge\n ) {\n targetChainId = _targetChainId;\n rewardToken = _rewardToken;\n gauge = _gauge;\n\n // Prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /// @notice initialize function, to set up initial internal state\n /// @param _strategist Address of the strategist\n /// @param _fee Fee in FEE_BASE unit payed when managing campaign\n /// @param _feeCollector Address of the fee collector\n function initialize(\n address _strategist,\n uint16 _fee,\n address _feeCollector,\n address _campaignRemoteManager,\n address _votemarket\n ) external onlyGovernor initializer {\n _setStrategistAddr(_strategist);\n _setFee(_fee);\n _setFeeCollector(_feeCollector);\n _setCampaignRemoteManager(_campaignRemoteManager);\n _setVotemarket(_votemarket);\n }\n\n ////////////////////////////////////////////////////\n /// --- MUTATIVE FUNCTIONS\n ////////////////////////////////////////////////////\n /// @notice Create a new campaign on VotemarketV2\n /// @dev This will use all token available in this contract\n /// @param numberOfPeriods Duration of the campaign in weeks\n /// @param maxRewardPerVote Maximum reward per vote to distribute, to avoid overspending\n /// @param blacklist List of addresses to exclude from the campaign\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function createCampaign(\n uint8 numberOfPeriods,\n uint256 maxRewardPerVote,\n address[] calldata blacklist,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId == 0, \"Campaign already created\");\n require(numberOfPeriods > 1, \"Invalid number of periods\");\n require(maxRewardPerVote > 0, \"Invalid reward per vote\");\n\n // Handle fee (if any)\n uint256 balanceSubFee = _handleFee();\n\n // Approve the balanceSubFee to the campaign manager\n IERC20(rewardToken).safeApprove(campaignRemoteManager, 0);\n IERC20(rewardToken).safeApprove(campaignRemoteManager, balanceSubFee);\n\n // Create a new campaign\n ICampaignRemoteManager(campaignRemoteManager).createCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignCreationParams({\n chainId: targetChainId,\n gauge: gauge,\n manager: address(this),\n rewardToken: rewardToken,\n numberOfPeriods: numberOfPeriods,\n maxRewardPerVote: maxRewardPerVote,\n totalRewardAmount: balanceSubFee,\n addresses: blacklist,\n hook: address(0),\n isWhitelist: false\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit CampaignCreated(\n gauge,\n rewardToken,\n maxRewardPerVote,\n balanceSubFee\n );\n }\n\n /// @notice Manage the total reward amount of the campaign\n /// @dev This function should be called after the campaign is created\n /// @dev This will use all the token available in this contract\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageTotalRewardAmount(\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n\n // Handle fee (if any)\n uint256 balanceSubFee = _handleFee();\n\n // Approve the total reward amount to the campaign manager\n IERC20(rewardToken).safeApprove(campaignRemoteManager, 0);\n IERC20(rewardToken).safeApprove(campaignRemoteManager, balanceSubFee);\n\n // Manage the campaign\n // https://github.com/stake-dao/votemarket-v2/blob/main/packages/votemarket/src/Votemarket.sol#L668\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: 0,\n totalRewardAmount: balanceSubFee,\n maxRewardPerVote: 0\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit TotalRewardAmountUpdated(balanceSubFee);\n }\n\n /// @notice Manage the number of periods of the campaign\n /// @dev This function should be called after the campaign is created\n /// @param extraNumberOfPeriods Number of additional periods (cannot be 0)\n /// that will be added to already existing amount of periods.\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageNumberOfPeriods(\n uint8 extraNumberOfPeriods,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n require(extraNumberOfPeriods > 0, \"Invalid number of periods\");\n\n // Manage the campaign\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: extraNumberOfPeriods,\n totalRewardAmount: 0,\n maxRewardPerVote: 0\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit NumberOfPeriodsUpdated(extraNumberOfPeriods);\n }\n\n /// @notice Manage the reward per vote of the campaign\n /// @dev This function should be called after the campaign is created\n /// @param newMaxRewardPerVote New maximum reward per vote\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageRewardPerVote(\n uint256 newMaxRewardPerVote,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n require(newMaxRewardPerVote > 0, \"Invalid reward per vote\");\n\n // Manage the campaign\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: 0,\n totalRewardAmount: 0,\n maxRewardPerVote: newMaxRewardPerVote\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit RewardPerVoteUpdated(newMaxRewardPerVote);\n }\n\n /// @notice Close the campaign.\n /// @dev This function only work on the L2 chain. Not on mainnet.\n /// @dev The _campaignId parameter is not related to the campaignId of this contract, allowing greater flexibility.\n /// @param _campaignId Id of the campaign to close\n function closeCampaign(\n uint256 _campaignId,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n ICampaignRemoteManager(campaignRemoteManager).closeCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignClosingParams({\n campaignId: campaignId\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n emit CampaignClosed(_campaignId);\n }\n\n /// @notice calculate the fee amount and transfer it to the feeCollector\n /// @return Balance after fee\n function _handleFee() internal returns (uint256) {\n // Cache current rewardToken balance\n uint256 balance = IERC20(rewardToken).balanceOf(address(this));\n require(balance > 0, \"No reward to manage\");\n\n uint256 feeAmount = (balance * fee) / FEE_BASE;\n\n // If there is a fee, transfer it to the feeCollector\n if (feeAmount > 0) {\n // Transfer the fee to the feeCollector\n IERC20(rewardToken).safeTransfer(feeCollector, feeAmount);\n emit FeeCollected(feeCollector, feeAmount);\n\n return IERC20(rewardToken).balanceOf(address(this));\n }\n\n // Return remaining balance\n return balance;\n }\n\n ////////////////////////////////////////////////////\n /// --- GOVERNANCE && OPERATION\n ////////////////////////////////////////////////////\n /// @notice Set the campaign id\n /// @dev Only callable by the governor or strategist\n /// @param _campaignId New campaign id\n function setCampaignId(uint256 _campaignId)\n external\n onlyGovernorOrStrategist\n {\n campaignId = _campaignId;\n emit CampaignIdUpdated(_campaignId);\n }\n\n /// @notice Rescue ETH from the contract\n /// @dev Only callable by the governor or strategist\n /// @param receiver Address to receive the ETH\n function rescueETH(address receiver)\n external\n nonReentrant\n onlyGovernorOrStrategist\n {\n require(receiver != address(0), \"Invalid receiver\");\n uint256 balance = address(this).balance;\n (bool success, ) = receiver.call{ value: balance }(\"\");\n require(success, \"Transfer failed\");\n emit TokensRescued(address(0), balance, receiver);\n }\n\n /// @notice Rescue ERC20 tokens from the contract\n /// @dev Only callable by the governor or strategist\n /// @param token Address of the token to rescue\n function rescueToken(address token, address receiver)\n external\n nonReentrant\n onlyGovernor\n {\n require(receiver != address(0), \"Invalid receiver\");\n uint256 balance = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransfer(receiver, balance);\n emit TokensRescued(token, balance, receiver);\n }\n\n /// @notice Set the fee\n /// @dev Only callable by the governor\n /// @param _fee New fee\n function setFee(uint16 _fee) external onlyGovernor {\n _setFee(_fee);\n }\n\n /// @notice Internal logic to set the fee\n function _setFee(uint16 _fee) internal {\n require(_fee <= FEE_BASE / 2, \"Fee too high\");\n fee = _fee;\n emit FeeUpdated(_fee);\n }\n\n /// @notice Set the fee collector\n /// @dev Only callable by the governor\n /// @param _feeCollector New fee collector\n function setFeeCollector(address _feeCollector) external onlyGovernor {\n _setFeeCollector(_feeCollector);\n }\n\n /// @notice Internal logic to set the fee collector\n function _setFeeCollector(address _feeCollector) internal {\n require(_feeCollector != address(0), \"Invalid fee collector\");\n feeCollector = _feeCollector;\n emit FeeCollectorUpdated(_feeCollector);\n }\n\n /// @notice Set the campaignRemoteManager\n /// @param _campaignRemoteManager New campaignRemoteManager address\n function setCampaignRemoteManager(address _campaignRemoteManager)\n external\n onlyGovernor\n {\n _setCampaignRemoteManager(_campaignRemoteManager);\n }\n\n /// @notice Internal logic to set the campaignRemoteManager\n /// @param _campaignRemoteManager New campaignRemoteManager address\n function _setCampaignRemoteManager(address _campaignRemoteManager)\n internal\n {\n require(\n _campaignRemoteManager != address(0),\n \"Invalid campaignRemoteManager\"\n );\n campaignRemoteManager = _campaignRemoteManager;\n emit CampaignRemoteManagerUpdated(_campaignRemoteManager);\n }\n\n /// @notice Set the votemarket address\n /// @param _votemarket New votemarket address\n function setVotemarket(address _votemarket) external onlyGovernor {\n _setVotemarket(_votemarket);\n }\n\n /// @notice Internal logic to set the votemarket address\n function _setVotemarket(address _votemarket) internal onlyGovernor {\n require(_votemarket != address(0), \"Invalid votemarket\");\n votemarket = _votemarket;\n emit VotemarketUpdated(_votemarket);\n }\n\n receive() external payable {}\n}\n" + }, + "contracts/strategies/Generalized4626Strategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Generalized 4626 Strategy\n * @notice Investment strategy for ERC-4626 Tokenized Vaults\n * @author Origin Protocol Inc\n */\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\ncontract Generalized4626Strategy is InitializableAbstractStrategy {\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecate_shareToken;\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecate_assetToken;\n\n IERC20 public immutable shareToken;\n IERC20 public immutable assetToken;\n\n // For future use\n uint256[50] private __gap;\n\n /**\n * @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n * and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n * @param _assetToken Address of the ERC-4626 asset token. eg frxETH or DAI\n */\n constructor(BaseStrategyConfig memory _baseConfig, address _assetToken)\n InitializableAbstractStrategy(_baseConfig)\n {\n shareToken = IERC20(_baseConfig.platformAddress);\n assetToken = IERC20(_assetToken);\n }\n\n function initialize() external virtual onlyGovernor initializer {\n address[] memory rewardTokens = new address[](0);\n address[] memory assets = new address[](1);\n address[] memory pTokens = new address[](1);\n\n assets[0] = address(assetToken);\n pTokens[0] = address(platformAddress);\n\n InitializableAbstractStrategy._initialize(\n rewardTokens,\n assets,\n pTokens\n );\n }\n\n /**\n * @dev Deposit assets by converting them to shares\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit assets by converting them to shares\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal virtual {\n require(_amount > 0, \"Must deposit something\");\n require(_asset == address(assetToken), \"Unexpected asset address\");\n\n // slither-disable-next-line unused-return\n IERC4626(platformAddress).deposit(_amount, address(this));\n emit Deposit(_asset, address(shareToken), _amount);\n }\n\n /**\n * @dev Deposit the entire balance of assetToken to gain shareToken\n */\n function depositAll() external virtual override onlyVault nonReentrant {\n uint256 balance = assetToken.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(assetToken), balance);\n }\n }\n\n /**\n * @dev Withdraw asset by burning shares\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n require(_asset == address(assetToken), \"Unexpected asset address\");\n\n // slither-disable-next-line unused-return\n IERC4626(platformAddress).withdraw(_amount, _recipient, address(this));\n emit Withdrawal(_asset, address(shareToken), _amount);\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / share tokens\n */\n function _abstractSetPToken(address, address) internal virtual override {\n _approveBase();\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll()\n external\n virtual\n override\n onlyVaultOrGovernor\n nonReentrant\n {\n uint256 shareBalance = shareToken.balanceOf(address(this));\n uint256 assetAmount = 0;\n if (shareBalance > 0) {\n assetAmount = IERC4626(platformAddress).redeem(\n shareBalance,\n vaultAddress,\n address(this)\n );\n emit Withdrawal(\n address(assetToken),\n address(shareToken),\n assetAmount\n );\n }\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(_asset == address(assetToken), \"Unexpected asset address\");\n /* We are intentionally not counting the amount of assetToken parked on the\n * contract toward the checkBalance. The deposit and withdraw functions\n * should not result in assetToken being unused and owned by this strategy\n * contract.\n */\n IERC4626 platform = IERC4626(platformAddress);\n return platform.previewRedeem(platform.balanceOf(address(this)));\n }\n\n /**\n * @notice Governor approves the ERC-4626 Tokenized Vault to spend the asset.\n */\n function safeApproveAllTokens() external override onlyGovernor {\n _approveBase();\n }\n\n function _approveBase() internal virtual {\n // Approval the asset to be transferred to the ERC-4626 Tokenized Vault.\n // Used by the ERC-4626 deposit() and mint() functions\n // slither-disable-next-line unused-return\n assetToken.approve(platformAddress, type(uint256).max);\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset)\n public\n view\n virtual\n override\n returns (bool)\n {\n return _asset == address(assetToken);\n }\n\n /**\n * @notice is not supported for this strategy as the asset and\n * ERC-4626 Tokenized Vault are set at deploy time.\n * @dev If the ERC-4626 Tokenized Vault needed to be changed, a new\n * contract would need to be deployed and the proxy updated.\n */\n function setPTokenAddress(address, address) external override onlyGovernor {\n revert(\"unsupported function\");\n }\n\n /**\n * @notice is not supported for this strategy as the asset and\n * ERC-4626 Tokenized Vault are set at deploy time.\n * @dev If the ERC-4626 Tokenized Vault needed to be changed, a new\n * contract would need to be deployed and the proxy updated.\n */\n function removePToken(uint256) external override onlyGovernor {\n revert(\"unsupported function\");\n }\n}\n" + }, + "contracts/strategies/Generalized4626USDTStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IUSDT {\n // Tether's approve does not return a bool like standard IERC20 contracts\n // slither-disable-next-line erc20-interface\n function approve(address _spender, uint256 _value) external;\n}\n\n/**\n * @title Generalized 4626 Strategy when asset is Tether USD (USDT)\n * @notice Investment strategy for ERC-4626 Tokenized Vaults for the USDT asset.\n * @author Origin Protocol Inc\n */\nimport { Generalized4626Strategy } from \"./Generalized4626Strategy.sol\";\n\ncontract Generalized4626USDTStrategy is Generalized4626Strategy {\n /**\n * @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n * and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n * @param _assetToken Address of the ERC-4626 asset token. eg frxETH or DAI\n */\n constructor(BaseStrategyConfig memory _baseConfig, address _assetToken)\n Generalized4626Strategy(_baseConfig, _assetToken)\n {}\n\n /// @dev Override for Tether as USDT does not return a bool on approve.\n /// Using assetToken.approve will fail as it expects a bool return value\n function _approveBase() internal virtual override {\n // Approval the asset to be transferred to the ERC-4626 Tokenized Vault.\n // Used by the ERC-4626 deposit() and mint() functions\n // slither-disable-next-line unused-return\n IUSDT(address(assetToken)).approve(platformAddress, type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/IAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface for Aaves Lending Pool\n * Documentation: https://developers.aave.com/#lendingpool\n */\ninterface IAaveLendingPool {\n /**\n * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.\n * - E.g. User deposits 100 USDC and gets in return 100 aUSDC\n * @param asset The address of the underlying asset to deposit\n * @param amount The amount to be deposited\n * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user\n * wants to receive them on his own wallet, or a different address if the beneficiary of aTokens\n * is a different wallet\n * @param referralCode Code used to register the integrator originating the operation, for potential rewards.\n * 0 if the action is executed directly by the user, without any middle-man\n **/\n function deposit(\n address asset,\n uint256 amount,\n address onBehalfOf,\n uint16 referralCode\n ) external;\n\n /**\n * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned\n * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC\n * @param asset The address of the underlying asset to withdraw\n * @param amount The underlying amount to be withdrawn\n * - Send the value type(uint256).max in order to withdraw the whole aToken balance\n * @param to Address that will receive the underlying, same as msg.sender if the user\n * wants to receive it on his own wallet, or a different address if the beneficiary is a\n * different wallet\n * @return The final amount withdrawn\n **/\n function withdraw(\n address asset,\n uint256 amount,\n address to\n ) external returns (uint256);\n}\n\n/**\n * @dev Interface for Aaves Lending Pool\n * Documentation: https://developers.aave.com/#lendingpooladdressesprovider\n */\ninterface ILendingPoolAddressesProvider {\n /**\n * @notice Get the current address for Aave LendingPool\n * @dev Lending pool is the core contract on which to call deposit\n */\n function getLendingPool() external view returns (address);\n}\n" + }, + "contracts/strategies/IAaveIncentivesController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IAaveIncentivesController {\n event RewardsAccrued(address indexed user, uint256 amount);\n\n event RewardsClaimed(\n address indexed user,\n address indexed to,\n uint256 amount\n );\n\n event RewardsClaimed(\n address indexed user,\n address indexed to,\n address indexed claimer,\n uint256 amount\n );\n\n event ClaimerSet(address indexed user, address indexed claimer);\n\n /*\n * @dev Returns the configuration of the distribution for a certain asset\n * @param asset The address of the reference asset of the distribution\n * @return The asset index, the emission per second and the last updated timestamp\n **/\n function getAssetData(address asset)\n external\n view\n returns (\n uint256,\n uint256,\n uint256\n );\n\n /**\n * @dev Whitelists an address to claim the rewards on behalf of another address\n * @param user The address of the user\n * @param claimer The address of the claimer\n */\n function setClaimer(address user, address claimer) external;\n\n /**\n * @dev Returns the whitelisted claimer for a certain address (0x0 if not set)\n * @param user The address of the user\n * @return The claimer address\n */\n function getClaimer(address user) external view returns (address);\n\n /**\n * @dev Configure assets for a certain rewards emission\n * @param assets The assets to incentivize\n * @param emissionsPerSecond The emission for each asset\n */\n function configureAssets(\n address[] calldata assets,\n uint256[] calldata emissionsPerSecond\n ) external;\n\n /**\n * @dev Called by the corresponding asset on any update that affects the rewards distribution\n * @param asset The address of the user\n * @param userBalance The balance of the user of the asset in the lending pool\n * @param totalSupply The total supply of the asset in the lending pool\n **/\n function handleAction(\n address asset,\n uint256 userBalance,\n uint256 totalSupply\n ) external;\n\n /**\n * @dev Returns the total of rewards of an user, already accrued + not yet accrued\n * @param user The address of the user\n * @return The rewards\n **/\n function getRewardsBalance(address[] calldata assets, address user)\n external\n view\n returns (uint256);\n\n /**\n * @dev Claims reward for an user, on all the assets of the lending pool,\n * accumulating the pending rewards\n * @param amount Amount of rewards to claim\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewards(\n address[] calldata assets,\n uint256 amount,\n address to\n ) external returns (uint256);\n\n /**\n * @dev Claims reward for an user on behalf, on all the assets of the\n * lending pool, accumulating the pending rewards. The caller must\n * be whitelisted via \"allowClaimOnBehalf\" function by the RewardsAdmin role manager\n * @param amount Amount of rewards to claim\n * @param user Address to check and claim rewards\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewardsOnBehalf(\n address[] calldata assets,\n uint256 amount,\n address user,\n address to\n ) external returns (uint256);\n\n /**\n * @dev returns the unclaimed rewards of the user\n * @param user the address of the user\n * @return the unclaimed user rewards\n */\n function getUserUnclaimedRewards(address user)\n external\n view\n returns (uint256);\n\n /**\n * @dev returns the unclaimed rewards of the user\n * @param user the address of the user\n * @param asset The asset to incentivize\n * @return the user index for the asset\n */\n function getUserAssetData(address user, address asset)\n external\n view\n returns (uint256);\n\n /**\n * @dev for backward compatibility with previous implementation of the Incentives controller\n */\n function REWARD_TOKEN() external view returns (address);\n\n /**\n * @dev for backward compatibility with previous implementation of the Incentives controller\n */\n function PRECISION() external view returns (uint8);\n\n /**\n * @dev Gets the distribution end timestamp of the emissions\n */\n function DISTRIBUTION_END() external view returns (uint256);\n}\n" + }, + "contracts/strategies/IAaveStakeToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IAaveStakedToken {\n function COOLDOWN_SECONDS() external returns (uint256);\n\n function UNSTAKE_WINDOW() external returns (uint256);\n\n function balanceOf(address addr) external returns (uint256);\n\n function redeem(address to, uint256 amount) external;\n\n function stakersCooldowns(address addr) external returns (uint256);\n\n function cooldown() external;\n}\n" + }, + "contracts/strategies/ICompound.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @dev Compound C Token interface\n * Documentation: https://compound.finance/developers/ctokens\n */\ninterface ICERC20 {\n /**\n * @notice The mint function transfers an asset into the protocol, which begins accumulating\n * interest based on the current Supply Rate for the asset. The user receives a quantity of\n * cTokens equal to the underlying tokens supplied, divided by the current Exchange Rate.\n * @param mintAmount The amount of the asset to be supplied, in units of the underlying asset.\n * @return 0 on success, otherwise an Error codes\n */\n function mint(uint256 mintAmount) external returns (uint256);\n\n /**\n * @notice Sender redeems cTokens in exchange for the underlying asset\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param redeemTokens The number of cTokens to redeem into underlying\n * @return uint 0=success, otherwise an error code.\n */\n function redeem(uint256 redeemTokens) external returns (uint256);\n\n /**\n * @notice The redeem underlying function converts cTokens into a specified quantity of the underlying\n * asset, and returns them to the user. The amount of cTokens redeemed is equal to the quantity of\n * underlying tokens received, divided by the current Exchange Rate. The amount redeemed must be less\n * than the user's Account Liquidity and the market's available liquidity.\n * @param redeemAmount The amount of underlying to be redeemed.\n * @return 0 on success, otherwise an error code.\n */\n function redeemUnderlying(uint256 redeemAmount) external returns (uint256);\n\n /**\n * @notice The user's underlying balance, representing their assets in the protocol, is equal to\n * the user's cToken balance multiplied by the Exchange Rate.\n * @param owner The account to get the underlying balance of.\n * @return The amount of underlying currently owned by the account.\n */\n function balanceOfUnderlying(address owner) external returns (uint256);\n\n /**\n * @notice Calculates the exchange rate from the underlying to the CToken\n * @dev This function does not accrue interest before calculating the exchange rate\n * @return Calculated exchange rate scaled by 1e18\n */\n function exchangeRateStored() external view returns (uint256);\n\n /**\n * @notice Get the token balance of the `owner`\n * @param owner The address of the account to query\n * @return The number of tokens owned by `owner`\n */\n function balanceOf(address owner) external view returns (uint256);\n\n /**\n * @notice Get the supply rate per block for supplying the token to Compound.\n */\n function supplyRatePerBlock() external view returns (uint256);\n\n /**\n * @notice Address of the Compound Comptroller.\n */\n function comptroller() external view returns (address);\n}\n" + }, + "contracts/strategies/IConvexDeposits.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IConvexDeposits {\n function deposit(\n uint256 _pid,\n uint256 _amount,\n bool _stake\n ) external returns (bool);\n\n function deposit(\n uint256 _amount,\n bool _lock,\n address _stakeAddress\n ) external;\n}\n" + }, + "contracts/strategies/ICRVMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICRVMinter {\n function mint(address gaugeAddress) external;\n}\n" + }, + "contracts/strategies/ICurveETHPoolV1.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurveETHPoolV1 {\n event AddLiquidity(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event ApplyNewFee(uint256 fee);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event CommitNewFee(uint256 new_fee);\n event RampA(\n uint256 old_A,\n uint256 new_A,\n uint256 initial_time,\n uint256 future_time\n );\n event RemoveLiquidity(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 token_supply\n );\n event RemoveLiquidityImbalance(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event RemoveLiquidityOne(\n address indexed provider,\n uint256 token_amount,\n uint256 coin_amount,\n uint256 token_supply\n );\n event StopRampA(uint256 A, uint256 t);\n event TokenExchange(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event Transfer(\n address indexed sender,\n address indexed receiver,\n uint256 value\n );\n\n function A() external view returns (uint256);\n\n function A_precise() external view returns (uint256);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_liquidity(uint256[2] memory _amounts, uint256 _min_mint_amount)\n external\n payable\n returns (uint256);\n\n function add_liquidity(\n uint256[2] memory _amounts,\n uint256 _min_mint_amount,\n address _receiver\n ) external payable returns (uint256);\n\n function admin_action_deadline() external view returns (uint256);\n\n function admin_balances(uint256 i) external view returns (uint256);\n\n function admin_fee() external view returns (uint256);\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function apply_new_fee() external;\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function balances(uint256 arg0) external view returns (uint256);\n\n function calc_token_amount(uint256[2] memory _amounts, bool _is_deposit)\n external\n view\n returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _burn_amount, int128 i)\n external\n view\n returns (uint256);\n\n function coins(uint256 arg0) external view returns (address);\n\n function commit_new_fee(uint256 _new_fee) external;\n\n function decimals() external view returns (uint256);\n\n function ema_price() external view returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external payable returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external payable returns (uint256);\n\n function fee() external view returns (uint256);\n\n function future_A() external view returns (uint256);\n\n function future_A_time() external view returns (uint256);\n\n function future_fee() external view returns (uint256);\n\n function get_balances() external view returns (uint256[2] memory);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n\n function get_p() external view returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function initial_A() external view returns (uint256);\n\n function initial_A_time() external view returns (uint256);\n\n function initialize(\n string memory _name,\n string memory _symbol,\n address[4] memory _coins,\n uint256[4] memory _rate_multipliers,\n uint256 _A,\n uint256 _fee\n ) external;\n\n function last_price() external view returns (uint256);\n\n function ma_exp_time() external view returns (uint256);\n\n function ma_last_time() external view returns (uint256);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function price_oracle() external view returns (uint256);\n\n function ramp_A(uint256 _future_A, uint256 _future_time) external;\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[2] memory _min_amounts\n ) external returns (uint256[2] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[2] memory _min_amounts,\n address _receiver\n ) external returns (uint256[2] memory);\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burn_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burn_amount,\n address _receiver\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received,\n address _receiver\n ) external returns (uint256);\n\n function set_ma_exp_time(uint256 _ma_exp_time) external;\n\n function stop_ramp_A() external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw_admin_fees() external;\n}\n" + }, + "contracts/strategies/ICurveGauge.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurveGauge {\n function balanceOf(address account) external view returns (uint256);\n\n function deposit(uint256 value, address account) external;\n\n function withdraw(uint256 value) external;\n}\n" + }, + "contracts/strategies/ICurveMetaPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface ICurveMetaPool {\n function add_liquidity(uint256[2] calldata amounts, uint256 min_mint_amount)\n external\n returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function remove_liquidity(uint256 _amount, uint256[2] calldata min_amounts)\n external\n returns (uint256[2] calldata);\n\n function remove_liquidity_one_coin(\n uint256 _token_amount,\n int128 i,\n uint256 min_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[2] calldata amounts,\n uint256 max_burn_amount\n ) external returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _token_amount, int128 i)\n external\n view\n returns (uint256);\n\n function balances(uint256 i) external view returns (uint256);\n\n function calc_token_amount(uint256[2] calldata amounts, bool deposit)\n external\n view\n returns (uint256);\n\n function base_pool() external view returns (address);\n\n function fee() external view returns (uint256);\n\n function coins(uint256 i) external view returns (address);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 dx,\n uint256 min_dy\n ) external returns (uint256);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n}\n" + }, + "contracts/strategies/ICurvePool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurvePool {\n function get_virtual_price() external view returns (uint256);\n\n function add_liquidity(uint256[3] calldata _amounts, uint256 _min) external;\n\n function balances(uint256) external view returns (uint256);\n\n function calc_token_amount(uint256[3] calldata _amounts, bool _deposit)\n external\n returns (uint256);\n\n function fee() external view returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _amount,\n int128 _index,\n uint256 _minAmount\n ) external;\n\n function remove_liquidity(\n uint256 _amount,\n uint256[3] calldata _minWithdrawAmounts\n ) external;\n\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n external\n view\n returns (uint256);\n\n function exchange(\n uint256 i,\n uint256 j,\n uint256 dx,\n uint256 min_dy\n ) external returns (uint256);\n\n function coins(uint256 _index) external view returns (address);\n\n function remove_liquidity_imbalance(\n uint256[3] calldata _amounts,\n uint256 maxBurnAmount\n ) external;\n}\n" + }, + "contracts/strategies/IRewardStaking.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IRewardStaking {\n function stakeFor(address, uint256) external;\n\n function stake(uint256) external;\n\n function withdraw(uint256 amount, bool claim) external;\n\n function withdrawAndUnwrap(uint256 amount, bool claim) external;\n\n function earned(address account) external view returns (uint256);\n\n function getReward() external;\n\n function getReward(address _account, bool _claimExtras) external;\n\n function extraRewardsLength() external returns (uint256);\n\n function extraRewards(uint256 _pid) external returns (address);\n\n function rewardToken() external returns (address);\n\n function balanceOf(address account) external view returns (uint256);\n}\n" + }, + "contracts/strategies/MorphoAaveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Morpho Aave Strategy\n * @notice Investment strategy for investing stablecoins via Morpho (Aave)\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { IMorpho } from \"../interfaces/morpho/IMorpho.sol\";\nimport { ILens } from \"../interfaces/morpho/ILens.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MorphoAaveStrategy is InitializableAbstractStrategy {\n address public constant MORPHO = 0x777777c9898D384F785Ee44Acfe945efDFf5f3E0;\n address public constant LENS = 0x507fA343d0A90786d86C7cd885f5C49263A91FF4;\n\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @dev Initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Approve the spending of all assets by main Morpho contract,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address asset = assetsMapped[i];\n\n // Safe approval\n IERC20(asset).safeApprove(MORPHO, 0);\n IERC20(asset).safeApprove(MORPHO, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset\n * We need to approve and allow Morpho to move them\n * @param _asset Address of the asset to approve\n * @param _pToken The pToken for the approval\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n IERC20(_asset).safeApprove(MORPHO, 0);\n IERC20(_asset).safeApprove(MORPHO, type(uint256).max);\n }\n\n /**\n * @dev Collect accumulated rewards and send them to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Morpho Aave-v2 doesn't distribute reward tokens\n // solhint-disable-next-line max-line-length\n // Ref: https://developers.morpho.xyz/interact-with-morpho/get-started/interact-with-morpho/claim-rewards#morpho-aave-v2\n }\n\n /**\n * @dev Get the amount of rewards pending to be collected from the protocol\n */\n function getPendingRewards() external view returns (uint256 balance) {\n // Morpho Aave-v2 doesn't distribute reward tokens\n // solhint-disable-next-line max-line-length\n // Ref: https://developers.morpho.xyz/interact-with-morpho/get-started/interact-with-morpho/claim-rewards#morpho-aave-v2\n return 0;\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n\n address pToken = address(_getPTokenFor(_asset));\n\n IMorpho(MORPHO).supply(\n pToken,\n address(this), // the address of the user you want to supply on behalf of\n _amount\n );\n emit Deposit(_asset, pToken, _amount);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Morpho\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Morpho\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n address pToken = address(_getPTokenFor(_asset));\n\n IMorpho(MORPHO).withdraw(pToken, _amount);\n emit Withdrawal(_asset, pToken, _amount);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = _checkBalance(assetsMapped[i]);\n if (balance > 0) {\n _withdraw(vaultAddress, assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Return total value of an asset held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n return _checkBalance(_asset);\n }\n\n function _checkBalance(address _asset)\n internal\n view\n returns (uint256 balance)\n {\n address pToken = address(_getPTokenFor(_asset));\n\n // Total value represented by decimal position of underlying token\n (, , balance) = ILens(LENS).getCurrentSupplyBalanceInOf(\n pToken,\n address(this)\n );\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Get the pToken wrapped in the IERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return pToken Corresponding pToken to this asset\n */\n function _getPTokenFor(address _asset) internal view returns (IERC20) {\n address pToken = assetToPToken[_asset];\n require(pToken != address(0), \"pToken does not exist\");\n return IERC20(pToken);\n }\n}\n" + }, + "contracts/strategies/MorphoCompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Morpho Compound Strategy\n * @notice Investment strategy for investing stablecoins via Morpho (Compound)\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, AbstractCompoundStrategy, InitializableAbstractStrategy } from \"./AbstractCompoundStrategy.sol\";\nimport { IMorpho } from \"../interfaces/morpho/IMorpho.sol\";\nimport { ILens } from \"../interfaces/morpho/ILens.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MorphoCompoundStrategy is AbstractCompoundStrategy {\n address public constant MORPHO = 0x8888882f8f843896699869179fB6E4f7e3B58888;\n address public constant LENS = 0x930f1b46e1D081Ec1524efD95752bE3eCe51EF67;\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @dev Initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Approve the spending of all assets by main Morpho contract,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address asset = assetsMapped[i];\n\n // Safe approval\n IERC20(asset).safeApprove(MORPHO, 0);\n IERC20(asset).safeApprove(MORPHO, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset\n * We need to approve and allow Morpho to move them\n * @param _asset Address of the asset to approve\n * @param _pToken The pToken for the approval\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n IERC20(_asset).safeApprove(MORPHO, 0);\n IERC20(_asset).safeApprove(MORPHO, type(uint256).max);\n }\n\n /**\n * @dev Collect accumulated rewards and send them to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n /**\n * Gas considerations. We could query Morpho LENS's `getUserUnclaimedRewards` for each\n * cToken separately and only claimRewards where it is economically feasible. Each call\n * (out of 3) costs ~60k gas extra.\n *\n * Each extra cToken in the `poolTokens` of `claimRewards` function makes that call\n * 89-120k more expensive gas wise.\n *\n * With Lens query in case where:\n * - there is only 1 reward token to collect. Net gas usage in best case would be\n * 3*60 - 2*120 = -60k -> saving 60k gas\n * - there are 2 reward tokens to collect. Net gas usage in best case would be\n * 3*60 - 120 = 60k -> more expensive for 60k gas\n * - there are 3 reward tokens to collect. Net gas usage in best case would be\n * 3*60 = 180k -> more expensive for 180k gas\n *\n * For the above reasoning such \"optimization\" is not implemented\n */\n\n address[] memory poolTokens = new address[](assetsMapped.length);\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n poolTokens[i] = assetToPToken[assetsMapped[i]];\n }\n\n // slither-disable-next-line unused-return\n IMorpho(MORPHO).claimRewards(\n poolTokens, // The addresses of the underlying protocol's pools to claim rewards from\n false // Whether to trade the accrued rewards for MORPHO token, with a premium\n );\n\n // Transfer COMP to Harvester\n IERC20 rewardToken = IERC20(rewardTokenAddresses[0]);\n uint256 balance = rewardToken.balanceOf(address(this));\n emit RewardTokenCollected(\n harvesterAddress,\n rewardTokenAddresses[0],\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n\n /**\n * @dev Get the amount of rewards pending to be collected from the protocol\n */\n function getPendingRewards() external view returns (uint256 balance) {\n address[] memory poolTokens = new address[](assetsMapped.length);\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n poolTokens[i] = assetToPToken[assetsMapped[i]];\n }\n\n return ILens(LENS).getUserUnclaimedRewards(poolTokens, address(this));\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n\n IMorpho(MORPHO).supply(\n address(_getCTokenFor(_asset)),\n address(this), // the address of the user you want to supply on behalf of\n _amount\n );\n emit Deposit(_asset, address(_getCTokenFor(_asset)), _amount);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Morpho\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Morpho\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n address pToken = assetToPToken[_asset];\n\n IMorpho(MORPHO).withdraw(pToken, _amount);\n emit Withdrawal(_asset, address(_getCTokenFor(_asset)), _amount);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = _checkBalance(assetsMapped[i]);\n if (balance > 0) {\n _withdraw(vaultAddress, assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Return total value of an asset held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n return _checkBalance(_asset);\n }\n\n function _checkBalance(address _asset)\n internal\n view\n returns (uint256 balance)\n {\n address pToken = assetToPToken[_asset];\n\n // Total value represented by decimal position of underlying token\n (, , balance) = ILens(LENS).getCurrentSupplyBalanceInOf(\n pToken,\n address(this)\n );\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with\n /// `platformAddress` not used so empty address\n /// `vaultAddress` the address of the OETH Vault contract\n /// @param _wethAddress Address of the WETH Token contract\n /// @param _ssvToken Address of the SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconProofs,\n _beaconGenesisTimestamp\n )\n {\n SSV_TOKEN = _ssvToken;\n\n // Make sure nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// @param _rewardTokenAddresses Not used so empty array\n /// @param _assets Not used so empty array\n /// @param _pTokens Not used so empty array\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n safeApproveAllTokens();\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH that was transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// @param _recipient Address to receive withdrawn assets.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH to withdraw.\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(WETH, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Accounts for all the assets managed by this strategy which includes:\n /// 1. The current WETH in this strategy contract\n /// 2. The last verified ETH balance, total deposits and total validator balances\n /// @param _asset Address of WETH asset.\n /// @return balance Total value in ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by the strategy.\n /// @param _asset The address of the WETH token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for validator registration.\n function safeApproveAllTokens() public override {\n // Approves the SSV Network contract to transfer SSV tokens when validators are registered\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n * The new ETH will be accounted for in `checkBalance` after the next snapBalances and verifyBalances txs.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n /// @notice is not supported for this strategy as there is no platform token.\n function setPTokenAddress(address, address) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @notice is not supported for this strategy as there is no platform token.\n function removePToken(uint256) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @dev This strategy does not use a platform token like the old Aave and Compound strategies.\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instead of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract.\n /// Withdrawals from validators also accumulate as ETH in this strategy contract.\n /// It's too complex to separate the rewards from withdrawals so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can now regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingView.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/**\n * @title Viewing contract for the Compounding Staking Strategy.\n * @author Origin Protocol Inc\n */\ncontract CompoundingStakingStrategyView {\n /// @notice The address of the Compounding Staking Strategy contract\n CompoundingValidatorManager public immutable stakingStrategy;\n\n constructor(address _stakingStrategy) {\n stakingStrategy = CompoundingValidatorManager(_stakingStrategy);\n }\n\n struct ValidatorView {\n bytes32 pubKeyHash;\n uint64 index;\n bool activated;\n CompoundingValidatorManager.ValidatorState state;\n }\n\n struct DepositView {\n bytes32 pendingDepositRoot;\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n }\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return validators An array of `ValidatorView` containing the public key hash, validator index and state.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorView[] memory validators)\n {\n uint256 validatorCount = stakingStrategy.verifiedValidatorsLength();\n validators = new ValidatorView[](validatorCount);\n for (uint256 i = 0; i < validatorCount; ++i) {\n bytes32 pubKeyHash = stakingStrategy.verifiedValidators(i);\n (\n CompoundingValidatorManager.ValidatorState state,\n uint64 index,\n bool activated\n ) = stakingStrategy.validator(pubKeyHash);\n validators[i] = ValidatorView({\n pubKeyHash: pubKeyHash,\n index: index,\n state: state,\n activated: activated\n });\n }\n }\n\n /// @notice Returns the deposits that are still to be verified.\n /// These may or may not have been processed by the beacon chain.\n /// @return pendingDeposits An array of `DepositView` containing the deposit ID, public key hash,\n /// amount in Gwei and the slot of the deposit.\n function getPendingDeposits()\n external\n view\n returns (DepositView[] memory pendingDeposits)\n {\n uint256 depositsCount = stakingStrategy.depositListLength();\n pendingDeposits = new DepositView[](depositsCount);\n for (uint256 i = 0; i < depositsCount; ++i) {\n (\n bytes32 pubKeyHash,\n uint64 amountGwei,\n uint64 slot,\n ,\n\n ) = stakingStrategy.deposits(stakingStrategy.depositList(i));\n pendingDeposits[i] = DepositView({\n pendingDepositRoot: stakingStrategy.depositList(i),\n pubKeyHash: pubKeyHash,\n amountGwei: amountGwei,\n slot: slot\n });\n }\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable, Pausable {\n using SafeERC20 for IERC20;\n\n /// @dev The amount of ETH in wei that is required for a deposit to a new validator.\n uint256 internal constant DEPOSIT_AMOUNT_WEI = 1 ether;\n /// @dev The amount of ETH balance in validator required for validator activation\n uint256 internal constant MIN_ACTIVATION_BALANCE_GWEI = 32 ether / 1e9;\n /// @dev The maximum number of deposits that are waiting to be verified as processed on the beacon chain.\n uint256 internal constant MAX_DEPOSITS = 12;\n /// @dev The maximum number of validators that can be verified.\n uint256 internal constant MAX_VERIFIED_VALIDATORS = 48;\n /// @dev The default withdrawable epoch value on the Beacon chain.\n /// A value in the far future means the validator is not exiting.\n uint64 internal constant FAR_FUTURE_EPOCH = type(uint64).max;\n /// @dev The number of seconds between each beacon chain slot.\n uint64 internal constant SLOT_DURATION = 12;\n /// @dev The number of slots in each beacon chain epoch.\n uint64 internal constant SLOTS_PER_EPOCH = 32;\n /// @dev Minimum time in seconds to allow snapped balances to be verified.\n /// Set to 35 slots which is 3 slots more than 1 epoch (32 slots). Deposits get processed\n /// once per epoch. This larger than 1 epoch delay should achieve that `snapBalances` sometimes\n /// get called in the middle (or towards the end) of the epoch. Giving the off-chain script\n /// sufficient time after the end of the epoch to prepare the proofs and call `verifyBalances`.\n /// This is considering a malicious actor would keep calling `snapBalances` as frequent as possible\n /// to disturb our operations.\n uint64 internal constant SNAP_BALANCES_DELAY = 35 * SLOT_DURATION;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n /// @notice The timestamp of the Beacon chain genesis.\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\n uint64 public immutable BEACON_GENESIS_TIMESTAMP;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// @notice Deposit data for new compounding validators.\n /// @dev A `VERIFIED` deposit can mean 3 separate things:\n /// - a deposit has been processed by the beacon chain and shall be included in the\n /// balance of the next verifyBalances call\n /// - a deposit has been done to a slashed validator and has probably been recovered\n /// back to this strategy. Probably because we can not know for certain. This contract\n /// only detects when the validator has passed its withdrawal epoch. It is close to impossible\n /// to prove with Merkle Proofs that the postponed deposit this contract is responsible for\n /// creating is not present anymore in BeaconChain.state.pending_deposits. This in effect\n /// means that there might be a period where this contract thinks the deposit has been already\n /// returned as ETH balance before it happens. This will result in some days (or weeks)\n /// -> depending on the size of deposit queue of showing a deficit when calling `checkBalance`.\n /// As this only offsets the yield and doesn't cause a critical double-counting we are not addressing\n /// this issue.\n /// - A deposit has been done to the validator, but our deposit has been front run by a malicious\n /// actor. Funds in the deposit this contract makes are not recoverable.\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified\n }\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountGwei Amount of ETH in gwei that has been deposited to the beacon chain deposit contract\n /// @param slot The beacon chain slot number when the deposit has been made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either UNKNOWN, PENDING or VERIFIED\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint32 depositIndex;\n DepositStatus status;\n }\n /// @notice Restricts to only one deposit to an unverified validator at a time.\n /// This is to limit front-running attacks of deposits to the beacon chain contract.\n ///\n /// @dev The value is set to true when a deposit to a new validator has been done that has\n /// not yet be verified.\n bool public firstDeposit;\n /// @notice Mapping of the pending deposit roots to the deposit data\n mapping(bytes32 => DepositData) public deposits;\n /// @notice List of strategy deposit IDs to a validator.\n /// The ID is the merkle root of the pending deposit data which is unique for each validator, amount and block.\n /// Duplicate pending deposit roots are prevented so can be used as an identifier to each strategy deposit.\n /// The list can be for deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n /// The list may not be ordered by time of deposit.\n /// Removed deposits will move the last deposit to the removed index.\n bytes32[] public depositList;\n\n enum ValidatorState {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITING, // The validator has been requested to exit or has been verified as forced exit\n EXITED, // The validator has been verified to have a zero balance\n REMOVED, // validator has funds withdrawn to this strategy contract and is removed from the SSV\n INVALID // The validator has been front-run and the withdrawal address is not this strategy\n }\n\n // Validator data\n struct ValidatorData {\n ValidatorState state; // The state of the validator known to this contract\n uint40 index; // The index of the validator on the beacon chain\n bool canActivate; // Has validator ever had minimal activation balance\n }\n /// @notice List of validator public key hashes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n bytes32[] public verifiedValidators;\n /// @notice Mapping of the hash of the validator's public key to the validator state and index.\n /// Uses the Beacon chain hashing for BLSPubkey which is sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => ValidatorData) public validator;\n\n /// @param blockRoot Beacon chain block root of the snapshot\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n bytes32 blockRoot;\n uint64 timestamp;\n uint128 ethBalance;\n }\n /// @notice Mapping of the block root to the balances at that slot\n Balances public snappedBalance;\n /// @notice The last verified ETH balance of the strategy\n uint256 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[41] private __gap;\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event FirstDepositReset();\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n bytes32 indexed pendingDepositRoot,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint40 indexed validatorIndex\n );\n event ValidatorInvalid(bytes32 indexed pubKeyHash);\n event DepositVerified(\n bytes32 indexed pendingDepositRoot,\n uint256 amountWei\n );\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(bytes32 indexed blockRoot, uint256 ethBalance);\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 ethBalance\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @dev Throws if called by any account other than the Registrator or Governor\n modifier onlyRegistratorOrGovernor() {\n require(\n msg.sender == validatorRegistrator || isGovernor(),\n \"Not Registrator or Governor\"\n );\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_PROOFS = _beaconProofs;\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\n\n require(\n block.timestamp > _beaconGenesisTimestamp,\n \"Invalid genesis timestamp\"\n );\n }\n\n /**\n *\n * Admin Functions\n *\n */\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Reset the `firstDeposit` flag to false so deposits to unverified validators can be made again.\n function resetFirstDeposit() external onlyGovernor {\n require(firstDeposit, \"No first deposit\");\n\n firstDeposit = false;\n\n emit FirstDepositReset();\n }\n\n function pause() external onlyRegistratorOrGovernor {\n _pause();\n }\n\n function unPause() external onlyGovernor {\n _unpause();\n }\n\n /**\n *\n * Validator Management\n *\n */\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validator[pubKeyHash].state == ValidatorState.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validator[pubKeyHash].state = ValidatorState.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n struct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n }\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// The the first deposit to a new validator, the amount must be 1 ETH.\n /// Another deposit of at least 31 ETH is required for the validator to be activated.\n /// This second deposit has to be done after the validator has been verified.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// There can not be two deposits to the same validator in the same block for the same amount.\n /// Function is pausable so in case a run-away Registrator can be prevented from continuing\n /// to deposit funds to slashed or undesired validators.\n /// @param validatorStakeData validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth,reentrancy-no-eth\n function stakeEth(\n ValidatorStakeData calldata validatorStakeData,\n uint64 depositAmountGwei\n ) external onlyRegistrator whenNotPaused {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(depositList.length < MAX_DEPOSITS, \"Max deposits\");\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validatorStakeData.pubkey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.VERIFIED),\n \"Not registered or verified\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n if (currentState == ValidatorState.REGISTERED) {\n // Can only have one pending deposit to an unverified validator at a time.\n // This is to limit front-running deposit attacks to a single deposit.\n // The exiting deposit needs to be verified before another deposit can be made.\n // If there was a front-running attack, the validator needs to be verified as invalid\n // and the Governor calls `resetFirstDeposit` to set `firstDeposit` to false.\n require(!firstDeposit, \"Existing first deposit\");\n // Limits the amount of ETH that can be at risk from a front-running deposit attack.\n require(\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n // Limits the number of validator balance proofs to verifyBalances\n require(\n verifiedValidators.length + 1 < MAX_VERIFIED_VALIDATORS,\n \"Max validators\"\n );\n\n // Flag a deposit to an unverified validator so only no other deposits can be made\n // to an unverified validator.\n firstDeposit = true;\n }\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == ValidatorState.REGISTERED) {\n validator[pubKeyHash].state = ValidatorState.STAKED;\n }\n\n /// After the Pectra upgrade the validators have a new restriction when proposing\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\n /// forward. Each slot is created at strict 12 second intervals and those slots can\n /// either have blocks attached to them or not. This way using the block.timestamp\n /// the slot number can easily be calculated.\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\n BEACON_GENESIS_TIMESTAMP) / SLOT_DURATION;\n\n // Calculate the merkle root of the beacon chain pending deposit data.\n // This is used as the unique ID of the deposit.\n bytes32 pendingDepositRoot = IBeaconProofs(BEACON_PROOFS)\n .merkleizePendingDeposit(\n pubKeyHash,\n withdrawalCredentials,\n depositAmountGwei,\n validatorStakeData.signature,\n depositSlot\n );\n require(\n deposits[pendingDepositRoot].status == DepositStatus.UNKNOWN,\n \"Duplicate deposit\"\n );\n\n // Store the deposit data for verifyDeposit and verifyBalances\n deposits[pendingDepositRoot] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n slot: depositSlot,\n depositIndex: SafeCast.toUint32(depositList.length),\n status: DepositStatus.PENDING\n });\n depositList.push(pendingDepositRoot);\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validatorStakeData.pubkey,\n withdrawalCredentials,\n validatorStakeData.signature,\n validatorStakeData.depositDataRoot\n );\n\n emit ETHStaked(\n pubKeyHash,\n pendingDepositRoot,\n validatorStakeData.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth,reentrancy-no-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then only the amount in excess of 32 ETH will be withdrawn.\n /// Only the Registrator can call this function.\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\n /// If no ETH balance, the tx will revert.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n payable\n onlyRegistrator\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorData memory validatorDataMem = validator[pubKeyHash];\n // Validator full withdrawal could be denied due to multiple reasons:\n // - the validator has not been activated or active long enough\n // (current_epoch < activation_epoch + SHARD_COMMITTEE_PERIOD)\n // - the validator has pending balance to withdraw from a previous partial withdrawal request\n //\n // Meaning that the on-chain to beacon chain full withdrawal request could fail. Instead\n // of adding complexity of verifying if a validator is eligible for a full exit, we allow\n // multiple full withdrawal requests per validator.\n require(\n validatorDataMem.state == ValidatorState.VERIFIED ||\n validatorDataMem.state == ValidatorState.EXITING,\n \"Validator not verified/exiting\"\n );\n\n // If a full withdrawal (validator exit)\n if (amountGwei == 0) {\n // For each staking strategy's deposits\n uint256 depositsCount = depositList.length;\n for (uint256 i = 0; i < depositsCount; ++i) {\n bytes32 pendingDepositRoot = depositList[i];\n // Check there is no pending deposits to the exiting validator\n require(\n pubKeyHash != deposits[pendingDepositRoot].pubKeyHash,\n \"Pending deposit\"\n );\n }\n\n // A validator that never had the minimal activation balance can not activate\n // and thus can not perform a full exit\n require(validatorDataMem.canActivate, \"Validator can not activate\");\n\n // Store the validator state as exiting so no more deposits can be made to it.\n validator[pubKeyHash].state = ValidatorState.EXITING;\n }\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove the validator from the SSV Cluster after:\n /// - the validator has been exited from `validatorWithdrawal` or slashed\n /// - the validator has incorrectly registered and can not be staked to\n /// - the initial deposit was front-run and the withdrawal address is not this strategy's address.\n /// Make sure `validatorWithdrawal` is called with a zero amount and the validator has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.EXITED ||\n currentState == ValidatorState.INVALID,\n \"Validator not regd or exited\"\n );\n\n validator[pubKeyHash].state = ValidatorState.REMOVED;\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /**\n *\n * SSV Management\n *\n */\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be withdrawn from the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /**\n *\n * Beacon Chain Proofs\n *\n */\n\n /// @notice Verifies a validator's index to its public key.\n /// Adds to the list of verified validators if the validator's withdrawal address is this strategy's address.\n /// Marks the validator as invalid and removes the deposit if the withdrawal address is not this strategy's address.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param withdrawalAddress The withdrawal address of the validator which should be this strategy's address.\n /// If the withdrawal address is not this strategy's address, the initial deposit was front-run\n /// and the validator is marked as invalid.\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint40 validatorIndex,\n bytes32 pubKeyHash,\n address withdrawalAddress,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validator[pubKeyHash].state == ValidatorState.STAKED,\n \"Validator not staked\"\n );\n\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key.\n // Also verify the validator's withdrawal credential points to the `withdrawalAddress`.\n IBeaconProofs(BEACON_PROOFS).verifyValidator(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n withdrawalAddress\n );\n\n // Store the validator state as verified\n validator[pubKeyHash] = ValidatorData({\n state: ValidatorState.VERIFIED,\n index: validatorIndex,\n canActivate: false\n });\n\n // If the initial deposit was front-run and the withdrawal address is not this strategy\n if (withdrawalAddress != address(this)) {\n // override the validator state\n validator[pubKeyHash].state = ValidatorState.INVALID;\n\n // Find and remove the deposit as the funds can not be recovered\n uint256 depositCount = depositList.length;\n for (uint256 i = 0; i < depositCount; i++) {\n DepositData memory deposit = deposits[depositList[i]];\n if (deposit.pubKeyHash == pubKeyHash) {\n // next verifyBalances will correctly account for the loss of a front-run\n // deposit. Doing it here accounts for the loss as soon as possible\n lastVerifiedEthBalance -= Math.min(\n lastVerifiedEthBalance,\n uint256(deposit.amountGwei) * 1 gwei\n );\n _removeDeposit(depositList[i], deposit);\n break;\n }\n }\n\n // Leave the `firstDeposit` flag as true so no more deposits to unverified validators can be made.\n // The Governor has to reset the `firstDeposit` to false before another deposit to\n // an unverified validator can be made.\n // The Governor can set a new `validatorRegistrator` if they suspect it has been compromised.\n\n emit ValidatorInvalid(pubKeyHash);\n return;\n }\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(pubKeyHash);\n\n // Reset the firstDeposit flag as the first deposit to an unverified validator has been verified.\n firstDeposit = false;\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n struct FirstPendingDepositSlotProofData {\n uint64 slot;\n bytes proof;\n }\n\n struct StrategyValidatorProofData {\n uint64 withdrawableEpoch;\n bytes withdrawableEpochProof;\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n ///\n /// Important: this function has a limitation where `depositProcessedSlot` that is passed by the off-chain\n /// verifier requires a slot immediately after it to propose a block otherwise the `BeaconRoots.parentBlockRoot`\n /// will fail. This shouldn't be a problem, since by the current behaviour of beacon chain only 1%-3% slots\n /// don't propose a block.\n /// @param pendingDepositRoot The unique identifier of the deposit emitted in `ETHStaked` from\n /// the `stakeEth` function.\n /// @param depositProcessedSlot Any slot on or after the strategy's deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\n /// set for the next block timestamp in 12 seconds time.\n /// @param firstPendingDeposit a `FirstPendingDepositSlotProofData` struct containing:\n /// - slot: The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be any non-zero value if the deposit queue is empty.\n /// - proof: The merkle proof of the first pending deposit's slot to the beacon block root.\n /// Can be either:\n /// * 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// * 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @param strategyValidatorData a `StrategyValidatorProofData` struct containing:\n /// - withdrawableEpoch: The withdrawable epoch of the validator the strategy is depositing to.\n /// - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the validator the strategy\n /// is depositing to, to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n bytes32 pendingDepositRoot,\n uint64 depositProcessedSlot,\n FirstPendingDepositSlotProofData calldata firstPendingDeposit,\n StrategyValidatorProofData calldata strategyValidatorData\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[pendingDepositRoot];\n ValidatorData memory strategyValidator = validator[deposit.pubKeyHash];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(firstPendingDeposit.slot != 0, \"Zero 1st pending deposit slot\");\n\n // We should allow the verification of deposits for validators that have been marked as exiting\n // to cover this situation:\n // - there are 2 pending deposits\n // - beacon chain has slashed the validator\n // - when verifyDeposit is called for the first deposit it sets the Validator state to EXITING\n // - verifyDeposit should allow a secondary call for the other deposit to a slashed validator\n require(\n strategyValidator.state == ValidatorState.VERIFIED ||\n strategyValidator.state == ValidatorState.EXITING,\n \"Validator not verified/exiting\"\n );\n // The verification slot must be after the deposit's slot.\n // This is needed for when the deposit queue is empty.\n require(deposit.slot < depositProcessedSlot, \"Slot not after deposit\");\n\n uint64 snapTimestamp = snappedBalance.timestamp;\n\n // This check prevents an accounting error that can happen if:\n // - snapBalances are snapped at the time of T\n // - deposit is processed on the beacon chain after time T and before verifyBalances()\n // - verifyDeposit is called before verifyBalances which removes a deposit from depositList\n // and deposit balance from totalDepositsWei\n // - verifyBalances is called under-reporting the strategy's balance\n require(\n (_calcNextBlockTimestamp(depositProcessedSlot) <= snapTimestamp) ||\n snapTimestamp == 0,\n \"Deposit after balance snapshot\"\n );\n\n // Get the parent beacon block root of the next block which is the block root of the deposit verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 depositBlockRoot = BeaconRoots.parentBlockRoot(\n _calcNextBlockTimestamp(depositProcessedSlot)\n );\n\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n depositBlockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.proof\n );\n\n // Verify the withdrawableEpoch on the validator of the strategy's deposit\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n depositBlockRoot,\n strategyValidator.index,\n strategyValidatorData.withdrawableEpoch,\n strategyValidatorData.withdrawableEpochProof\n );\n\n uint64 firstPendingDepositEpoch = firstPendingDeposit.slot /\n SLOTS_PER_EPOCH;\n\n // Validator can either be not exiting and no further checks are required\n // Or a validator is exiting then this function needs to make sure that the\n // pending deposit to an exited validator has certainly been processed. The\n // slot/epoch of first pending deposit is the one that contains the transaction\n // where the deposit to the ETH Deposit Contract has been made.\n //\n // Once the firstPendingDepositEpoch becomes greater than the withdrawableEpoch of\n // the slashed validator then the deposit has certainly been processed. When the beacon\n // chain reaches the withdrawableEpoch of the validator the deposit will no longer be\n // postponed. And any new deposits created (and present in the deposit queue)\n // will have an equal or larger withdrawableEpoch.\n require(\n strategyValidatorData.withdrawableEpoch == FAR_FUTURE_EPOCH ||\n strategyValidatorData.withdrawableEpoch <=\n firstPendingDepositEpoch,\n \"Exit Deposit likely not proc.\"\n );\n\n // solhint-disable max-line-length\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the deposit queue is empty then our deposit must have been processed on the beacon chain.\n // The deposit slot can be zero for validators consolidating to a compounding validator or 0x01 validator\n // being promoted to a compounding one. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // We can not guarantee that the deposit has been processed in that case.\n // solhint-enable max-line-length\n require(\n deposit.slot < firstPendingDeposit.slot || isDepositQueueEmpty,\n \"Deposit likely not processed\"\n );\n\n // Remove the deposit now it has been verified as processed on the beacon chain.\n _removeDeposit(pendingDepositRoot, deposit);\n\n emit DepositVerified(\n pendingDepositRoot,\n uint256(deposit.amountGwei) * 1 gwei\n );\n }\n\n function _removeDeposit(\n bytes32 pendingDepositRoot,\n DepositData memory deposit\n ) internal {\n // After verifying the proof, update the contract storage\n deposits[pendingDepositRoot].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n bytes32 lastDeposit = depositList[depositList.length - 1];\n depositList[deposit.depositIndex] = lastDeposit;\n deposits[lastDeposit].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositList.pop();\n }\n\n /// @dev Calculates the timestamp of the next execution block from the given slot.\n /// @param slot The beacon chain slot number used for merkle proof verification.\n function _calcNextBlockTimestamp(uint64 slot)\n internal\n view\n returns (uint64)\n {\n // Calculate the next block timestamp from the slot.\n return SLOT_DURATION * slot + BEACON_GENESIS_TIMESTAMP + SLOT_DURATION;\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block and beacon block root\n /// of the slot that is associated with the previous block.\n ///\n /// When snapping / verifying balance it is of a high importance that there is no\n /// miss-match in respect to ETH that is held by the contract and balances that are\n /// verified on the validators.\n ///\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\n /// constructing a block on the beacon chain consist of:\n /// - process_withdrawals: ETH is deducted from the validator's balance\n /// - process_execution_payload: immediately after the previous step executing all the\n /// transactions\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\n /// contained in the withdrawal credentials of the exited validators\n ///\n /// That means that balance increases which are part of the post-block execution state are\n /// done within the block, but the transaction that are contained within that block can not\n /// see / interact with the balance from the exited validators. Only transactions in the\n /// next block can do that.\n ///\n /// When snap balances is performed the state of the chain is snapped across 2 separate\n /// chain states:\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\n /// will be referred to as Y - 1 as it makes no difference in the argument\n ///\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\n /// miss-counting ETH or much more dangerous double counting of the ETH.\n ///\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\n /// - ETH balance in the contract on block X\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\n /// - ETH balance in validators that are active in slot Y - 1\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\n /// and have their balance visible to transactions in slot Y and corresponding block X\n /// (or sooner)\n ///\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\n ///\n /// *ETH balance in the contract on block X*\n ///\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\n ///\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\n /// would result in double counting the `stakedEth` since it would be present once in the\n /// snapped contract balance and the second time in deposit storage variables.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\n ///\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\n /// contract at block Z. The execution layer doesn't have direct access to the state of\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\n /// and could also be part of the validator balances. It does that by verifying that at\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\n /// the last snap till now all are still in queue. Which ensures they can not be part of\n /// the validator balances in later steps.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in validators that are active in slot Y - 1*\n ///\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\n /// correctness.\n ///\n /// This behaviour is correct.\n ///\n /// *The withdrawn validators*\n ///\n /// The withdrawn validators could have their balances deducted in any slot before slot\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\n /// look at the \"worst case scenario\" where the validator withdrawal is processed in the\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\n /// even if the validator exits at the latest possible time it is paramount that the ETH\n /// balance on the execution layer is recorded in the next block. Correctly accounting\n /// for the withdrawn ETH.\n ///\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\n /// validator balance verification as well as execution layer contract balance snap.\n ///\n /// This behaviour is correct.\n ///\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n function snapBalances() external {\n uint64 currentTimestamp = SafeCast.toUint64(block.timestamp);\n require(\n snappedBalance.timestamp + SNAP_BALANCES_DELAY < currentTimestamp,\n \"Snap too soon\"\n );\n\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(currentTimestamp);\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the snapped balance\n snappedBalance = Balances({\n blockRoot: blockRoot,\n timestamp: currentTimestamp,\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n emit BalancesSnapped(blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct BalanceProofs {\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n struct PendingDepositProofs {\n bytes32 pendingDepositContainerRoot;\n bytes pendingDepositContainerProof;\n uint40[] pendingDepositIndexes;\n bytes[] pendingDepositProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks each of the strategy's deposits are still to be processed by the beacon chain.\n /// @param balanceProofs a `BalanceProofs` struct containing the following:\n /// - balancesContainerRoot: The merkle root of the balances container\n /// - balancesContainerProof: The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// - validatorBalanceLeaves: Array of leaf nodes containing the validator balance with three other balances.\n /// - validatorBalanceProofs: Array of merkle proofs for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param pendingDepositProofs a `PendingDepositProofs` struct containing the following:\n /// - pendingDepositContainerRoot: The merkle root of the pending deposits list container\n /// - pendingDepositContainerProof: The merkle proof from the pending deposits list container\n /// to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// - pendingDepositIndexes: Array of indexes in the pending deposits list container for each\n /// of the strategy's deposits.\n /// - pendingDepositProofs: Array of merkle proofs for each strategy deposit in the\n /// beacon chain's pending deposit list container to the pending deposits list container root.\n /// These are 28 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(\n BalanceProofs calldata balanceProofs,\n PendingDepositProofs calldata pendingDepositProofs\n ) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalance;\n // Check the balances are the latest\n require(balancesMem.timestamp > 0, \"No snapped balances\");\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n uint256 depositsCount = depositList.length;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n balanceProofs.validatorBalanceProofs.length ==\n verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n balanceProofs.validatorBalanceLeaves.length ==\n verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n balancesMem.blockRoot,\n balanceProofs.balancesContainerRoot,\n balanceProofs.balancesContainerProof\n );\n\n bytes32[]\n memory validatorHashesMem = _getPendingDepositValidatorHashes(\n depositsCount\n );\n\n // for each validator in reverse order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n ValidatorData memory validatorDataMem = validator[\n verifiedValidators[i]\n ];\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n balanceProofs.balancesContainerRoot,\n balanceProofs.validatorBalanceLeaves[i],\n balanceProofs.validatorBalanceProofs[i],\n validatorDataMem.index\n );\n\n bool depositPending = false;\n for (uint256 j = 0; j < validatorHashesMem.length; j++) {\n if (validatorHashesMem[j] == verifiedValidators[i]) {\n depositPending = true;\n break;\n }\n }\n\n // If validator has a pending deposit we can not remove due to\n // the following situation:\n // - validator has a pending deposit\n // - validator has been slashed\n // - sweep cycle has withdrawn all ETH from the validator. Balance is 0\n // - beacon chain has processed the deposit and set the validator balance\n // to deposit amount\n // - if validator is no longer in the list of verifiedValidators its\n // balance will not be considered and be under-counted.\n if (validatorBalanceGwei == 0 && !depositPending) {\n // Store the validator state as exited\n // This could have been in VERIFIED or EXITING state\n validator[verifiedValidators[i]].state = ValidatorState\n .EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n } else if (\n validatorBalanceGwei >= MIN_ACTIVATION_BALANCE_GWEI &&\n !validatorDataMem.canActivate\n ) {\n validator[verifiedValidators[i]].canActivate = true;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += validatorBalanceGwei * 1 gwei;\n }\n }\n\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification.\n // This section is after the validator balance verifications so an exited validator will be marked\n // as EXITED before the deposits are verified. If there was a deposit to an exited validator\n // then the deposit can only be removed once the validator is fully exited.\n // It is possible that validator fully exits and a postponed deposit to an exited validator increases\n // its balance again. In such case the contract will erroneously consider a deposit applied before it\n // has been applied on the beacon chain showing a smaller than real `totalValidatorBalance`.\n if (depositsCount > 0) {\n require(\n pendingDepositProofs.pendingDepositProofs.length ==\n depositsCount,\n \"Invalid deposit proofs\"\n );\n require(\n pendingDepositProofs.pendingDepositIndexes.length ==\n depositsCount,\n \"Invalid deposit indexes\"\n );\n\n // Verify from the root of the pending deposit list container to the beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyPendingDepositsContainer(\n balancesMem.blockRoot,\n pendingDepositProofs.pendingDepositContainerRoot,\n pendingDepositProofs.pendingDepositContainerProof\n );\n\n // For each staking strategy's deposit.\n for (uint256 i = 0; i < depositsCount; ++i) {\n bytes32 pendingDepositRoot = depositList[i];\n\n // Verify the strategy's deposit is still pending on the beacon chain.\n IBeaconProofs(BEACON_PROOFS).verifyPendingDeposit(\n pendingDepositProofs.pendingDepositContainerRoot,\n pendingDepositRoot,\n pendingDepositProofs.pendingDepositProofs[i],\n pendingDepositProofs.pendingDepositIndexes[i]\n );\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei +=\n uint256(deposits[pendingDepositRoot].amountGwei) *\n 1 gwei;\n }\n }\n\n // Store the verified balance in storage\n lastVerifiedEthBalance =\n totalDepositsWei +\n totalValidatorBalance +\n balancesMem.ethBalance;\n // Reset the last snap timestamp so a new snapBalances has to be made\n snappedBalance.timestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice get a list of all validator hashes present in the pending deposits\n /// list can have duplicate entries\n function _getPendingDepositValidatorHashes(uint256 depositsCount)\n internal\n view\n returns (bytes32[] memory validatorHashes)\n {\n validatorHashes = new bytes32[](depositsCount);\n for (uint256 i = 0; i < depositsCount; i++) {\n validatorHashes[i] = deposits[depositList[i]].pubKeyHash;\n }\n }\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n require(pubKey.length == 48, \"Invalid public key length\");\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /**\n *\n * WETH and ETH Accounting\n *\n */\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n // The min is required as more WETH can be withdrawn than deposited\n // as the strategy earns consensus and execution rewards.\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance.\n // The ETH balance in this strategy contract can be more than the last verified ETH balance\n // due to partial withdrawals or full exits being processed by the beacon chain since the last snapBalances.\n // It can also happen from execution rewards (MEV) or ETH donations.\n lastVerifiedEthBalance -= Math.min(lastVerifiedEthBalance, _ethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n snappedBalance.timestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += _wethAmount;\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n snappedBalance.timestamp = 0;\n }\n\n /**\n *\n * View Functions\n *\n */\n\n /// @notice Returns the number of deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n function depositListLength() external view returns (uint256) {\n return depositList.length;\n }\n\n /// @notice Returns the number of verified validators.\n function verifiedValidatorsLength() external view returns (uint256) {\n return verifiedValidators.length;\n }\n}\n" + }, + "contracts/strategies/NativeStaking/FeeAccumulator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\n/**\n * @title Fee Accumulator for Native Staking SSV Strategy\n * @notice Receives execution rewards which includes tx fees and\n * MEV rewards like tx priority and tx ordering.\n * It does NOT include swept ETH from beacon chain consensus rewards or full validator withdrawals.\n * @author Origin Protocol Inc\n */\ncontract FeeAccumulator {\n /// @notice The address of the Native Staking Strategy\n address public immutable STRATEGY;\n\n event ExecutionRewardsCollected(address indexed strategy, uint256 amount);\n\n /**\n * @param _strategy Address of the Native Staking Strategy\n */\n constructor(address _strategy) {\n STRATEGY = _strategy;\n }\n\n /**\n * @notice sends all ETH in this FeeAccumulator contract to the Native Staking Strategy.\n * @return eth The amount of execution rewards that were sent to the Native Staking Strategy\n */\n function collect() external returns (uint256 eth) {\n require(msg.sender == STRATEGY, \"Caller is not the Strategy\");\n\n eth = address(this).balance;\n if (eth > 0) {\n // Send the ETH to the Native Staking Strategy\n Address.sendValue(payable(STRATEGY), eth);\n\n emit ExecutionRewardsCollected(STRATEGY, eth);\n }\n }\n\n /**\n * @dev Accept ETH\n */\n receive() external payable {}\n}\n" + }, + "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { FeeAccumulator } from \"./FeeAccumulator.sol\";\nimport { ValidatorAccountant } from \"./ValidatorAccountant.sol\";\nimport { ISSVNetwork } from \"../../interfaces/ISSVNetwork.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/// @title Native Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract NativeStakingSSVStrategy is\n ValidatorAccountant,\n InitializableAbstractStrategy\n{\n using SafeERC20 for IERC20;\n\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n /// @notice Fee collector address\n /// @dev this address will receive maximal extractable value (MEV) rewards. These are\n /// rewards for arranging transactions in a way that benefits the validator.\n address payable public immutable FEE_ACCUMULATOR_ADDRESS;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[49] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n /// @param _feeAccumulator Address of the fee accumulator receiving execution layer validator rewards\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n uint256 _maxValidators,\n address _feeAccumulator,\n address _beaconChainDepositContract\n )\n InitializableAbstractStrategy(_baseConfig)\n ValidatorAccountant(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _maxValidators\n )\n {\n SSV_TOKEN = _ssvToken;\n FEE_ACCUMULATOR_ADDRESS = payable(_feeAccumulator);\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract.\n /// @param _rewardTokenAddresses Address of reward token for platform\n /// @param _assets Addresses of initial supported assets\n /// @param _pTokens Platform Token corresponding addresses\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n\n // Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\n FEE_ACCUMULATOR_ADDRESS\n );\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n /// @param _asset Address of asset to deposit. Has to be WETH.\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n depositedWethAccountedFor += _amount;\n _deposit(_asset, _amount);\n }\n\n /// @dev Deposit WETH to this strategy so it can later be staked into a validator.\n /// @param _asset Address of WETH\n /// @param _amount Amount of WETH to deposit\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n /*\n * We could do a check here that would revert when \"_amount % 32 ether != 0\". With the idea of\n * not allowing deposits that will result in WETH sitting on the strategy after all the possible batches\n * of 32ETH have been staked.\n * But someone could mess with our strategy by sending some WETH to it. And we might want to deposit just\n * enough WETH to add it up to 32 so it can be staked. For that reason the check is left out.\n *\n * WETH sitting on the strategy won't interfere with the accounting since accounting only operates on ETH.\n */\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n depositedWethAccountedFor = wethBalance;\n\n _deposit(WETH, newWeth);\n }\n }\n\n /// @notice Withdraw WETH from this contract. Used only if some WETH for is lingering on the contract.\n /// That can happen when:\n /// - after mints if the strategy is the default\n /// - time between depositToStrategy and stakeEth\n /// - the deposit was not a multiple of 32 WETH\n /// - someone sent WETH directly to this contract\n /// Will NOT revert if the strategy is paused from an accounting failure.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n _wethWithdrawn(_amount);\n\n IERC20(_asset).safeTransfer(_recipient, _amount);\n emit Withdrawal(_asset, address(0), _amount);\n }\n\n /// @notice transfer all WETH deposits back to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `exitSsvValidator` and `removeSsvValidator` operations.\n /// This does not withdraw any execution rewards from the FeeAccumulator or\n /// consensus rewards in this strategy.\n /// Any ETH in this strategy that was swept from a full validator withdrawal will not be withdrawn.\n /// ETH from full validator withdrawals is sent to the Vault using `doAccounting`.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n if (wethBalance > 0) {\n _withdraw(vaultAddress, WETH, wethBalance);\n }\n }\n\n /// @notice Returns the total value of (W)ETH that is staked to the validators\n /// and WETH deposits that are still to be staked.\n /// This does not include ETH from consensus rewards sitting in this strategy\n /// or ETH from MEV rewards in the FeeAccumulator. These rewards are harvested\n /// and sent to the Dripper so will eventually be sent to the Vault as WETH.\n /// @param _asset Address of weth asset\n /// @return balance Total value of (W)ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n balance =\n // add the ETH that has been staked in validators\n activeDepositedValidators *\n FULL_STAKE +\n // add the WETH in the strategy from deposits that are still to be staked\n IERC20(WETH).balanceOf(address(this));\n }\n\n function pause() external onlyStrategist {\n _pause();\n }\n\n /// @notice Returns bool indicating whether asset is supported by strategy.\n /// @param _asset The address of the asset token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for deposits\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\n function setFeeRecipient() external {\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\n FEE_ACCUMULATOR_ADDRESS\n );\n }\n\n /**\n * @notice Only accept ETH from the FeeAccumulator and the WETH contract - required when\n * unwrapping WETH just before staking it to the validator.\n * The strategy will also receive ETH from the priority fees of transactions when producing blocks\n * as defined in EIP-1559.\n * The tx fees come from the Beacon chain so do not need any EVM level permissions to receive ETH.\n * The tx fees are paid with each block produced. They are not included in the consensus rewards\n * which are periodically swept from the validators to this strategy.\n * For accounting purposes, the priority fees of transactions will be considered consensus rewards\n * and will be included in the AccountingConsensusRewards event.\n * @dev don't want to receive donations from anyone else as donations over the fuse limits will\n * mess with the accounting of the consensus rewards and validator full withdrawals.\n */\n receive() external payable {\n require(\n msg.sender == FEE_ACCUMULATOR_ADDRESS || msg.sender == WETH,\n \"Eth not from allowed contracts\"\n );\n }\n\n /***************************************\n Internal functions\n ****************************************/\n\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Convert accumulated ETH to WETH and send to the Harvester.\n /// Will revert if the strategy is paused for accounting.\n function _collectRewardTokens() internal override whenNotPaused {\n // collect ETH from execution rewards from the fee accumulator\n uint256 executionRewards = FeeAccumulator(FEE_ACCUMULATOR_ADDRESS)\n .collect();\n\n // total ETH rewards to be harvested = execution rewards + consensus rewards\n uint256 ethRewards = executionRewards + consensusRewards;\n\n require(\n address(this).balance >= ethRewards,\n \"Insufficient eth balance\"\n );\n\n if (ethRewards > 0) {\n // reset the counter keeping track of beacon chain consensus rewards\n consensusRewards = 0;\n\n // Convert ETH rewards to WETH\n IWETH9(WETH).deposit{ value: ethRewards }();\n\n IERC20(WETH).safeTransfer(harvesterAddress, ethRewards);\n emit RewardTokenCollected(harvesterAddress, WETH, ethRewards);\n }\n }\n\n /// @dev emits Withdrawal event from NativeStakingSSVStrategy\n function _wethWithdrawnToVault(uint256 _amount) internal override {\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _wethWithdrawn(uint256 _amount) internal override {\n /* In an ideal world we wouldn't need to reduce the deduction amount when the\n * depositedWethAccountedFor is smaller than the _amount.\n *\n * The reason this is required is that a malicious actor could sent WETH directly\n * to this contract and that would circumvent the increase of depositedWethAccountedFor\n * property. When the ETH would be staked the depositedWethAccountedFor amount could\n * be deducted so much that it would be negative.\n */\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n }\n}\n" + }, + "contracts/strategies/NativeStaking/ValidatorAccountant.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ValidatorRegistrator } from \"./ValidatorRegistrator.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\n\n/// @title Validator Accountant\n/// @notice Attributes the ETH swept from beacon chain validators to this strategy contract\n/// as either full or partial withdrawals. Partial withdrawals being consensus rewards.\n/// Full withdrawals are from exited validators.\n/// @author Origin Protocol Inc\nabstract contract ValidatorAccountant is ValidatorRegistrator {\n /// @notice The minimum amount of blocks that need to pass between two calls to manuallyFixAccounting\n uint256 public constant MIN_FIX_ACCOUNTING_CADENCE = 7200; // 1 day\n\n /// @notice Keeps track of the total consensus rewards swept from the beacon chain\n uint256 public consensusRewards;\n\n /// @notice start of fuse interval\n uint256 public fuseIntervalStart;\n /// @notice end of fuse interval\n uint256 public fuseIntervalEnd;\n /// @notice last block number manuallyFixAccounting has been called\n uint256 public lastFixAccountingBlockNumber;\n\n uint256[49] private __gap;\n\n event FuseIntervalUpdated(uint256 start, uint256 end);\n event AccountingFullyWithdrawnValidator(\n uint256 noOfValidators,\n uint256 remainingValidators,\n uint256 wethSentToVault\n );\n event AccountingValidatorSlashed(\n uint256 remainingValidators,\n uint256 wethSentToVault\n );\n event AccountingConsensusRewards(uint256 amount);\n\n event AccountingManuallyFixed(\n int256 validatorsDelta,\n int256 consensusRewardsDelta,\n uint256 wethToVault\n );\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n uint256 _maxValidators\n )\n ValidatorRegistrator(\n _wethAddress,\n _vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _maxValidators\n )\n {}\n\n /// @notice set fuse interval values\n function setFuseInterval(\n uint256 _fuseIntervalStart,\n uint256 _fuseIntervalEnd\n ) external onlyGovernor {\n require(\n _fuseIntervalStart < _fuseIntervalEnd &&\n _fuseIntervalEnd < 32 ether &&\n _fuseIntervalEnd - _fuseIntervalStart >= 4 ether,\n \"Incorrect fuse interval\"\n );\n\n fuseIntervalStart = _fuseIntervalStart;\n fuseIntervalEnd = _fuseIntervalEnd;\n\n emit FuseIntervalUpdated(_fuseIntervalStart, _fuseIntervalEnd);\n }\n\n /* solhint-disable max-line-length */\n /// This notion page offers a good explanation of how the accounting functions\n /// https://www.notion.so/originprotocol/Limited-simplified-native-staking-accounting-67a217c8420d40678eb943b9da0ee77d\n /// In short, after dividing by 32, if the ETH remaining on the contract falls between 0 and fuseIntervalStart,\n /// the accounting function will treat that ETH as Beacon chain consensus rewards.\n /// On the contrary, if after dividing by 32, the ETH remaining on the contract falls between fuseIntervalEnd and 32,\n /// the accounting function will treat that as a validator slashing.\n /// @notice Perform the accounting attributing beacon chain ETH to either full or partial withdrawals. Returns true when\n /// accounting is valid and fuse isn't \"blown\". Returns false when fuse is blown.\n /// @dev This function could in theory be permission-less but lets allow only the Registrator (Defender Action) to call it\n /// for now.\n /// @return accountingValid true if accounting was successful, false if fuse is blown\n /* solhint-enable max-line-length */\n function doAccounting()\n external\n onlyRegistrator\n whenNotPaused\n nonReentrant\n returns (bool accountingValid)\n {\n // pause the accounting on failure\n accountingValid = _doAccounting(true);\n }\n\n // slither-disable-start reentrancy-eth\n function _doAccounting(bool pauseOnFail)\n internal\n returns (bool accountingValid)\n {\n if (address(this).balance < consensusRewards) {\n return _failAccounting(pauseOnFail);\n }\n\n // Calculate all the new ETH that has been swept to the contract since the last accounting\n uint256 newSweptETH = address(this).balance - consensusRewards;\n accountingValid = true;\n\n // send the ETH that is from fully withdrawn validators to the Vault\n if (newSweptETH >= FULL_STAKE) {\n uint256 fullyWithdrawnValidators;\n // explicitly cast to uint256 as we want to round to a whole number of validators\n fullyWithdrawnValidators = uint256(newSweptETH / FULL_STAKE);\n activeDepositedValidators -= fullyWithdrawnValidators;\n\n uint256 wethToVault = FULL_STAKE * fullyWithdrawnValidators;\n IWETH9(WETH).deposit{ value: wethToVault }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, wethToVault);\n _wethWithdrawnToVault(wethToVault);\n\n emit AccountingFullyWithdrawnValidator(\n fullyWithdrawnValidators,\n activeDepositedValidators,\n wethToVault\n );\n }\n\n uint256 ethRemaining = address(this).balance - consensusRewards;\n // should be less than a whole validator stake\n require(ethRemaining < FULL_STAKE, \"Unexpected accounting\");\n\n // If no Beacon chain consensus rewards swept\n if (ethRemaining == 0) {\n // do nothing\n return accountingValid;\n } else if (ethRemaining < fuseIntervalStart) {\n // Beacon chain consensus rewards swept (partial validator withdrawals)\n // solhint-disable-next-line reentrancy\n consensusRewards += ethRemaining;\n emit AccountingConsensusRewards(ethRemaining);\n } else if (ethRemaining > fuseIntervalEnd) {\n // Beacon chain consensus rewards swept but also a slashed validator fully exited\n IWETH9(WETH).deposit{ value: ethRemaining }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, ethRemaining);\n activeDepositedValidators -= 1;\n\n _wethWithdrawnToVault(ethRemaining);\n\n emit AccountingValidatorSlashed(\n activeDepositedValidators,\n ethRemaining\n );\n }\n // Oh no... Fuse is blown. The Strategist needs to adjust the accounting values.\n else {\n return _failAccounting(pauseOnFail);\n }\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @dev pause any further accounting if required and return false\n function _failAccounting(bool pauseOnFail)\n internal\n returns (bool accountingValid)\n {\n // pause if not already\n if (pauseOnFail) {\n _pause();\n }\n // fail the accounting\n accountingValid = false;\n }\n\n /// @notice Allow the Strategist to fix the accounting of this strategy and unpause.\n /// @param _validatorsDelta adjust the active validators by up to plus three or minus three\n /// @param _consensusRewardsDelta adjust the accounted for consensus rewards up or down\n /// @param _ethToVaultAmount the amount of ETH that gets wrapped into WETH and sent to the Vault\n /// @dev There is a case when a validator(s) gets slashed so much that the eth swept from\n /// the beacon chain enters the fuse area and there are no consensus rewards on the contract\n /// to \"dip into\"/use. To increase the amount of unaccounted ETH over the fuse end interval\n /// we need to reduce the amount of active deposited validators and immediately send WETH\n /// to the vault, so it doesn't interfere with further accounting.\n function manuallyFixAccounting(\n int256 _validatorsDelta,\n int256 _consensusRewardsDelta,\n uint256 _ethToVaultAmount\n ) external onlyStrategist whenPaused nonReentrant {\n require(\n lastFixAccountingBlockNumber + MIN_FIX_ACCOUNTING_CADENCE <\n block.number,\n \"Fix accounting called too soon\"\n );\n require(\n _validatorsDelta >= -3 &&\n _validatorsDelta <= 3 &&\n // new value must be positive\n int256(activeDepositedValidators) + _validatorsDelta >= 0,\n \"Invalid validatorsDelta\"\n );\n require(\n _consensusRewardsDelta >= -332 ether &&\n _consensusRewardsDelta <= 332 ether &&\n // new value must be positive\n int256(consensusRewards) + _consensusRewardsDelta >= 0,\n \"Invalid consensusRewardsDelta\"\n );\n require(_ethToVaultAmount <= 32 ether * 3, \"Invalid wethToVaultAmount\");\n\n activeDepositedValidators = uint256(\n int256(activeDepositedValidators) + _validatorsDelta\n );\n consensusRewards = uint256(\n int256(consensusRewards) + _consensusRewardsDelta\n );\n lastFixAccountingBlockNumber = block.number;\n if (_ethToVaultAmount > 0) {\n IWETH9(WETH).deposit{ value: _ethToVaultAmount }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, _ethToVaultAmount);\n _wethWithdrawnToVault(_ethToVaultAmount);\n }\n\n emit AccountingManuallyFixed(\n _validatorsDelta,\n _consensusRewardsDelta,\n _ethToVaultAmount\n );\n\n // rerun the accounting to see if it has now been fixed.\n // Do not pause the accounting on failure as it is already paused\n require(_doAccounting(false), \"Fuse still blown\");\n\n // unpause since doAccounting was successful\n _unpause();\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n /// @dev allows for NativeStakingSSVStrategy contract to emit the Withdrawal event\n function _wethWithdrawnToVault(uint256 _amount) internal virtual;\n}\n" + }, + "contracts/strategies/NativeStaking/ValidatorRegistrator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Registrator of the validators\n * @notice This contract implements all the required functionality to register, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract ValidatorRegistrator is Governable, Pausable {\n /// @notice The maximum amount of ETH that can be staked by a validator\n /// @dev this can change in the future with EIP-7251, Increase the MAX_EFFECTIVE_BALANCE\n uint256 public constant FULL_STAKE = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Maximum number of validators that can be registered in this strategy\n uint256 public immutable MAX_VALIDATORS;\n\n /// @notice Address of the registrator - allowed to register, exit and remove validators\n address public validatorRegistrator;\n /// @notice The number of validators that have 32 (!) ETH actively deposited. When a new deposit\n /// to a validator happens this number increases, when a validator exit is detected this number\n /// decreases.\n uint256 public activeDepositedValidators;\n /// @notice State of the validators keccak256(pubKey) => state\n mapping(bytes32 => VALIDATOR_STATE) public validatorsStates;\n /// @notice The account that is allowed to modify stakeETHThreshold and reset stakeETHTally\n address public stakingMonitor;\n /// @notice Amount of ETH that can be staked before staking on the contract is suspended\n /// and the `stakingMonitor` needs to approve further staking by calling `resetStakeETHTally`\n uint256 public stakeETHThreshold;\n /// @notice Amount of ETH that has been staked since the `stakingMonitor` last called `resetStakeETHTally`.\n /// This can not go above `stakeETHThreshold`.\n uint256 public stakeETHTally;\n\n // For future use\n uint256[47] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n EXITING, // exit message has been posted and validator is in the process of exiting\n EXIT_COMPLETE // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event ETHStaked(bytes32 indexed pubKeyHash, bytes pubKey, uint256 amount);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event SSVValidatorExitInitiated(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event SSVValidatorExitCompleted(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event StakeETHThresholdChanged(uint256 amount);\n event StakeETHTallyReset();\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(\n msg.sender == validatorRegistrator,\n \"Caller is not the Registrator\"\n );\n _;\n }\n\n /// @dev Throws if called by any account other than the Staking monitor\n modifier onlyStakingMonitor() {\n require(msg.sender == stakingMonitor, \"Caller is not the Monitor\");\n _;\n }\n\n /// @dev Throws if called by any account other than the Strategist\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(VAULT_ADDRESS).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n uint256 _maxValidators\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n MAX_VALIDATORS = _maxValidators;\n }\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Set the address of the staking monitor that is allowed to reset stakeETHTally\n function setStakingMonitor(address _address) external onlyGovernor {\n stakingMonitor = _address;\n emit StakingMonitorChanged(_address);\n }\n\n /// @notice Set the amount of ETH that can be staked before staking monitor\n // needs to a approve further staking by resetting the stake ETH tally\n function setStakeETHThreshold(uint256 _amount) external onlyGovernor {\n stakeETHThreshold = _amount;\n emit StakeETHThresholdChanged(_amount);\n }\n\n /// @notice Reset the stakeETHTally\n function resetStakeETHTally() external onlyStakingMonitor {\n stakeETHTally = 0;\n emit StakeETHTallyReset();\n }\n\n /// @notice Stakes WETH to the node validators\n /// @param validators A list of validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n // slither-disable-start reentrancy-eth\n function stakeEth(ValidatorStakeData[] calldata validators)\n external\n onlyRegistrator\n whenNotPaused\n nonReentrant\n {\n uint256 requiredETH = validators.length * FULL_STAKE;\n\n // Check there is enough WETH from the deposits sitting in this strategy contract\n require(\n requiredETH <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(\n activeDepositedValidators + validators.length <= MAX_VALIDATORS,\n \"Max validators reached\"\n );\n\n require(\n stakeETHTally + requiredETH <= stakeETHThreshold,\n \"Staking ETH over threshold\"\n );\n stakeETHTally += requiredETH;\n\n // Convert required ETH from WETH\n IWETH9(WETH).withdraw(requiredETH);\n _wethWithdrawn(requiredETH);\n\n /* 0x01 to indicate that withdrawal credentials will contain an EOA address that the sweeping function\n * can sweep funds to.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x01),\n bytes11(0),\n address(this)\n );\n\n // For each validator\n for (uint256 i = 0; i < validators.length; ++i) {\n bytes32 pubKeyHash = keccak256(validators[i].pubkey);\n\n require(\n validatorsStates[pubKeyHash] == VALIDATOR_STATE.REGISTERED,\n \"Validator not registered\"\n );\n\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: FULL_STAKE\n }(\n validators[i].pubkey,\n withdrawalCredentials,\n validators[i].signature,\n validators[i].depositDataRoot\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.STAKED;\n\n emit ETHStaked(pubKeyHash, validators[i].pubkey, FULL_STAKE);\n }\n // save gas by changing this storage variable only once rather each time in the loop.\n activeDepositedValidators += validators.length;\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Registers a new validator in the SSV Cluster.\n /// Only the registrator can call this function.\n /// @param publicKeys The public keys of the validators\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for each validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function registerSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n require(\n publicKeys.length == sharesData.length,\n \"Pubkey sharesData mismatch\"\n );\n // Check each public key has not already been used\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n emit SSVValidatorRegistered(pubKeyHash, publicKeys[i], operatorIds);\n }\n\n ISSVNetwork(SSV_NETWORK).bulkRegisterValidator(\n publicKeys,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n }\n\n /// @notice Exit validators from the Beacon chain.\n /// The staked ETH will eventually swept to this native staking strategy.\n /// Only the registrator can call this function.\n /// @param publicKeys List of SSV validator public keys\n /// @param operatorIds The operator IDs of the SSV Cluster\n function exitSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external onlyRegistrator whenNotPaused nonReentrant {\n ISSVNetwork(SSV_NETWORK).bulkExitValidator(publicKeys, operatorIds);\n\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n\n // Check each validator has not already been staked.\n // This would normally be done before the external call but is after\n // so only one for loop of the validators is needed.\n require(\n currentState == VALIDATOR_STATE.STAKED,\n \"Validator not staked\"\n );\n\n // Store the new validator state\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXITING;\n\n emit SSVValidatorExitInitiated(\n pubKeyHash,\n publicKeys[i],\n operatorIds\n );\n }\n }\n\n /// @notice Remove validators from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKeys List of SSV validator public keys\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function removeSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused nonReentrant {\n ISSVNetwork(SSV_NETWORK).bulkRemoveValidator(\n publicKeys,\n operatorIds,\n cluster\n );\n\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n\n // Check each validator is either registered or exited.\n // This would normally be done before the external call but is after\n // so only one for loop of the validators is needed.\n require(\n currentState == VALIDATOR_STATE.EXITING ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exiting\"\n );\n\n // Store the new validator state\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXIT_COMPLETE;\n\n emit SSVValidatorExitCompleted(\n pubKeyHash,\n publicKeys[i],\n operatorIds\n );\n }\n }\n\n /// @notice Deposits more SSV Tokens to the SSV Network contract which is used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// uses \"onlyStrategist\" modifier so continuous front-running can't DOS our maintenance service\n /// that tries to top up SSV tokens.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function depositSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyStrategist {\n ISSVNetwork(SSV_NETWORK).deposit(\n address(this),\n operatorIds,\n ssvAmount,\n cluster\n );\n }\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _wethWithdrawn(uint256 _amount) internal virtual;\n}\n" + }, + "contracts/strategies/plume/RoosterAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Rooster AMO strategy\n * @author Origin Protocol Inc\n * @custom:security-contact security@originprotocol.com\n */\nimport { Math as MathRooster } from \"../../../lib/rooster/v2-common/libraries/Math.sol\";\nimport { Math as Math_v5 } from \"../../../lib/rooster/openzeppelin-custom/contracts/utils/math/Math.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { IMaverickV2Pool } from \"../../interfaces/plume/IMaverickV2Pool.sol\";\nimport { IMaverickV2Quoter } from \"../../interfaces/plume/IMaverickV2Quoter.sol\";\nimport { IMaverickV2LiquidityManager } from \"../../interfaces/plume/IMaverickV2LiquidityManager.sol\";\nimport { IMaverickV2PoolLens } from \"../../interfaces/plume/IMaverickV2PoolLens.sol\";\nimport { IMaverickV2Position } from \"../../interfaces/plume/IMaverickV2Position.sol\";\nimport { IVotingDistributor } from \"../../interfaces/plume/IVotingDistributor.sol\";\nimport { IPoolDistributor } from \"../../interfaces/plume/IPoolDistributor.sol\";\n// importing custom version of rooster TickMath because of dependency collision. Maverick uses\n// a newer OpenZepplin Math library with functionality that is not present in 4.4.2 (the one we use)\nimport { TickMath } from \"../../../lib/rooster/v2-common/libraries/TickMath.sol\";\n\ncontract RoosterAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n using SafeCast for uint256;\n\n /***************************************\n Storage slot members\n ****************************************/\n\n /// @notice NFT tokenId of the liquidity position\n ///\n /// @dev starts with value of 1 and can not be 0\n // solhint-disable-next-line max-line-length\n /// https://github.com/rooster-protocol/rooster-contracts/blob/fbfecbc519e4495b12598024a42630b4a8ea4489/v2-common/contracts/base/Nft.sol#L14\n uint256 public tokenId;\n /// @dev Minimum amount of tokens the strategy would be able to withdraw from the pool.\n /// minimum amount of tokens are withdrawn at a 1:1 price\n /// Important: Underlying assets contains only assets that are deposited in the underlying Rooster pool.\n /// WETH or OETH held by this contract is not accounted for in underlying assets\n uint256 public underlyingAssets;\n /// @notice Marks the start of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareStart;\n /// @notice Marks the end of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareEnd;\n /// @dev reserved for inheritance\n int256[46] private __reserved;\n\n /***************************************\n Constants, structs and events\n ****************************************/\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the OETH token contract\n address public immutable OETH;\n /// @notice the underlying AMO Maverick (Rooster) pool\n IMaverickV2Pool public immutable mPool;\n /// @notice the Liquidity manager used to add liquidity to the pool\n IMaverickV2LiquidityManager public immutable liquidityManager;\n /// @notice the Maverick V2 poolLens\n ///\n /// @dev only used to provide the pool's current sqrtPrice\n IMaverickV2PoolLens public immutable poolLens;\n /// @notice the Maverick V2 position\n ///\n /// @dev provides details of the NFT LP position and offers functions to\n /// remove the liquidity.\n IMaverickV2Position public immutable maverickPosition;\n /// @notice the Maverick Quoter\n IMaverickV2Quoter public immutable quoter;\n /// @notice the Maverick Voting Distributor\n IVotingDistributor public immutable votingDistributor;\n /// @notice the Maverick Pool Distributor\n IPoolDistributor public immutable poolDistributor;\n\n /// @notice sqrtPriceTickLower\n /// @dev tick lower represents the lower price of OETH priced in WETH. Meaning the pool\n /// offers more than 1 OETH for 1 WETH. In other terms to get 1 OETH the swap needs to offer 0.9999 WETH\n /// this is where purchasing OETH with WETH within the liquidity position is the cheapest.\n ///\n /// _____________________\n /// | | |\n /// | WETH | OETH |\n /// | | |\n /// | | |\n /// --------- * ---- * ---------- * ---------\n /// currentPrice\n /// sqrtPriceHigher-(1:1 parity)\n /// sqrtPriceLower\n ///\n ///\n /// Price is defined as price of token1 in terms of token0. (token1 / token0)\n /// @notice sqrtPriceTickLower - OETH is priced 0.9999 WETH\n uint256 public immutable sqrtPriceTickLower;\n /// @notice sqrtPriceTickHigher\n /// @dev tick higher represents 1:1 price parity of WETH to OETH\n uint256 public immutable sqrtPriceTickHigher;\n /// @dev price at parity (in OETH this is equal to sqrtPriceTickHigher)\n uint256 public immutable sqrtPriceAtParity;\n /// @notice The tick where the strategy deploys the liquidity to\n int32 public constant TICK_NUMBER = -1;\n /// @notice Minimum liquidity that must be exceeded to continue with the action\n /// e.g. deposit, add liquidity\n uint256 public constant ACTION_THRESHOLD = 1e12;\n /// @notice Maverick pool static liquidity bin type\n uint8 public constant MAV_STATIC_BIN_KIND = 0;\n /// @dev a threshold under which the contract no longer allows for the protocol to rebalance. Guarding\n /// against a strategist / guardian being taken over and with multiple transactions draining the\n /// protocol funds.\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n /// @notice Emitted when the allowed interval within which the strategy contract is allowed to deposit\n /// liquidity to the underlying pool is updated.\n /// @param allowedWethShareStart The start of the interval\n /// @param allowedWethShareEnd The end of the interval\n event PoolWethShareIntervalUpdated(\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n /// @notice Emitted when liquidity is removed from the underlying pool\n /// @param withdrawLiquidityShare Share of strategy's liquidity that has been removed\n /// @param removedWETHAmount The amount of WETH removed\n /// @param removedOETHAmount The amount of OETH removed\n /// @param underlyingAssets Updated amount of strategy's underlying assets\n event LiquidityRemoved(\n uint256 withdrawLiquidityShare,\n uint256 removedWETHAmount,\n uint256 removedOETHAmount,\n uint256 underlyingAssets\n );\n\n /// @notice Emitted when the underlying pool is rebalanced\n /// @param currentPoolWethShare The resulting share of strategy's liquidity\n /// in the TICK_NUMBER\n event PoolRebalanced(uint256 currentPoolWethShare);\n\n /// @notice Emitted when the amount of underlying assets the strategy hold as\n /// liquidity in the pool is updated.\n /// @param underlyingAssets Updated amount of strategy's underlying assets\n event UnderlyingAssetsUpdated(uint256 underlyingAssets);\n\n /// @notice Emitted when liquidity is added to the underlying pool\n /// @param wethAmountDesired Amount of WETH desired to be deposited\n /// @param oethAmountDesired Amount of OETH desired to be deposited\n /// @param wethAmountSupplied Amount of WETH deposited\n /// @param oethAmountSupplied Amount of OETH deposited\n /// @param tokenId NFT liquidity token id\n /// @param underlyingAssets Updated amount of underlying assets\n event LiquidityAdded(\n uint256 wethAmountDesired,\n uint256 oethAmountDesired,\n uint256 wethAmountSupplied,\n uint256 oethAmountSupplied,\n uint256 tokenId,\n uint256 underlyingAssets\n ); // 0x1530ec74\n\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n ); // 0x3681e8e0\n\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth); // 0x989e5ca8\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth); // 0xa6737d87\n error OutsideExpectedTickRange(); // 0xa6e1bad2\n error SlippageCheck(uint256 tokenReceived); // 0x355cdb78\n\n /// @notice the constructor\n /// @dev This contract is intended to be used as a proxy. To prevent the\n /// potential confusion of having a functional implementation contract\n /// the constructor has the `initializer` modifier. This way the\n /// `initialize` function can not be called on the implementation contract.\n /// For the same reason the implementation contract also has the governor\n /// set to a zero address.\n /// @param _stratConfig the basic strategy configuration\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _oethAddress Address of the Erc20 OETH Token contract\n /// @param _liquidityManager Address of liquidity manager to add\n /// the liquidity\n /// @param _poolLens Address of the pool lens contract\n /// @param _maverickPosition Address of the Maverick's position contract\n /// @param _maverickQuoter Address of the Maverick's Quoter contract\n /// @param _mPool Address of the Rooster concentrated liquidity pool\n /// @param _upperTickAtParity Bool when true upperTick is the one where the\n /// price of OETH and WETH are at parity\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethAddress,\n address _liquidityManager,\n address _poolLens,\n address _maverickPosition,\n address _maverickQuoter,\n address _mPool,\n bool _upperTickAtParity,\n address _votingDistributor,\n address _poolDistributor\n ) initializer InitializableAbstractStrategy(_stratConfig) {\n require(\n address(IMaverickV2Pool(_mPool).tokenA()) == _wethAddress,\n \"WETH not TokenA\"\n );\n require(\n address(IMaverickV2Pool(_mPool).tokenB()) == _oethAddress,\n \"OETH not TokenB\"\n );\n require(\n _liquidityManager != address(0),\n \"LiquidityManager zero address not allowed\"\n );\n require(\n _maverickQuoter != address(0),\n \"Quoter zero address not allowed\"\n );\n require(_poolLens != address(0), \"PoolLens zero address not allowed\");\n require(\n _maverickPosition != address(0),\n \"Position zero address not allowed\"\n );\n require(\n _votingDistributor != address(0),\n \"Voting distributor zero address not allowed\"\n );\n require(\n _poolDistributor != address(0),\n \"Pool distributor zero address not allowed\"\n );\n\n uint256 _tickSpacing = IMaverickV2Pool(_mPool).tickSpacing();\n require(_tickSpacing == 1, \"Unsupported tickSpacing\");\n\n // tickSpacing == 1\n (sqrtPriceTickLower, sqrtPriceTickHigher) = TickMath.tickSqrtPrices(\n _tickSpacing,\n TICK_NUMBER\n );\n sqrtPriceAtParity = _upperTickAtParity\n ? sqrtPriceTickHigher\n : sqrtPriceTickLower;\n\n WETH = _wethAddress;\n OETH = _oethAddress;\n liquidityManager = IMaverickV2LiquidityManager(_liquidityManager);\n poolLens = IMaverickV2PoolLens(_poolLens);\n maverickPosition = IMaverickV2Position(_maverickPosition);\n quoter = IMaverickV2Quoter(_maverickQuoter);\n mPool = IMaverickV2Pool(_mPool);\n votingDistributor = IVotingDistributor(_votingDistributor);\n poolDistributor = IPoolDistributor(_poolDistributor);\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /**\n * @notice initialize function, to set up initial internal state\n */\n function initialize() external onlyGovernor initializer {\n // Read reward\n address[] memory _rewardTokens = new address[](1);\n _rewardTokens[0] = poolDistributor.rewardToken();\n\n require(_rewardTokens[0] != address(0), \"No reward token configured\");\n\n InitializableAbstractStrategy._initialize(\n _rewardTokens,\n new address[](0),\n new address[](0)\n );\n }\n\n /***************************************\n Configuration \n ****************************************/\n\n /**\n * @notice Set allowed pool weth share interval. After the rebalance happens\n * the share of WETH token in the ticker needs to be within the specifications\n * of the interval.\n *\n * @param _allowedWethShareStart Start of WETH share interval expressed as 18 decimal amount\n * @param _allowedWethShareEnd End of WETH share interval expressed as 18 decimal amount\n */\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external onlyGovernor {\n require(\n _allowedWethShareStart < _allowedWethShareEnd,\n \"Invalid interval\"\n );\n // can not go below 1% weth share\n require(_allowedWethShareStart > 0.01 ether, \"Invalid interval start\");\n // can not go above 95% weth share\n require(_allowedWethShareEnd < 0.95 ether, \"Invalid interval end\");\n\n allowedWethShareStart = _allowedWethShareStart;\n allowedWethShareEnd = _allowedWethShareEnd;\n emit PoolWethShareIntervalUpdated(\n _allowedWethShareStart,\n _allowedWethShareEnd\n );\n }\n\n /***************************************\n Strategy overrides \n ****************************************/\n\n /**\n * @notice Deposits funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposits all the funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n _deposit(WETH, _wethBalance);\n }\n\n /**\n * @dev Deposits funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n * Before this function can be called the initial pool position needs to already\n * be minted.\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n require(tokenId > 0, \"Initial position not minted\");\n emit Deposit(_asset, address(0), _amount);\n\n // if the pool price is not within the expected interval leave the WETH on the contract\n // as to not break the mints - in case it would be configured as a default asset strategy\n (bool _isExpectedRange, ) = _checkForExpectedPoolPrice(false);\n if (_isExpectedRange) {\n // deposit funds into the underlying pool. Because no swap is performed there is no\n // need to remove any of the liquidity beforehand.\n _rebalance(0, false, 0, 0);\n }\n }\n\n /**\n * @notice Withdraw an `amount` of WETH from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset WETH address\n * @param _amount Amount of WETH to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n _ensureWETHBalance(_amount);\n\n _withdraw(_recipient, _amount);\n }\n\n /**\n * @notice Withdraw WETH and sends it to the Vault.\n */\n function withdrawAll() external override onlyVault nonReentrant {\n if (tokenId != 0) {\n _removeLiquidity(1e18);\n }\n\n uint256 _balance = IERC20(WETH).balanceOf(address(this));\n if (_balance > 0) {\n _withdraw(vaultAddress, _balance);\n }\n }\n\n function _withdraw(address _recipient, uint256 _amount) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n * @return bool True when the _asset is WETH\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /**\n * @dev Approve the spending amounts for the assets\n */\n function _approveTokenAmounts(\n uint256 _wethAllowance,\n uint256 _oethAllowance\n ) internal {\n IERC20(WETH).approve(address(liquidityManager), _wethAllowance);\n IERC20(OETH).approve(address(liquidityManager), _oethAllowance);\n }\n\n /***************************************\n Liquidity management\n ****************************************/\n /**\n * @dev Add liquidity into the pool in the pre-configured WETH to OETH share ratios\n * defined by the allowedPoolWethShareStart|End interval.\n *\n * Normally a PoolLens contract is used to prepare the parameters to add liquidity to the\n * Rooster pools. It has some errors when doing those calculation and for that reason a\n * much more accurate Quoter contract is used. This is possible due to our requirement of\n * adding liquidity only to one tick - PoolLens supports adding liquidity into multiple ticks\n * using different distribution ratios.\n */\n function _addLiquidity() internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n // don't deposit small liquidity amounts\n if (_wethBalance <= ACTION_THRESHOLD) {\n return;\n }\n\n (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n ) = _getAddLiquidityParams(_wethBalance, 1e30);\n\n if (OETHRequired > _oethBalance) {\n IVault(vaultAddress).mintForStrategy(OETHRequired - _oethBalance);\n }\n\n _approveTokenAmounts(WETHRequired, OETHRequired);\n\n (\n uint256 _wethAmount,\n uint256 _oethAmount,\n uint32[] memory binIds\n ) = liquidityManager.addPositionLiquidityToSenderByTokenIndex(\n mPool,\n 0, // NFT token index\n packedSqrtPriceBreaks,\n packedArgs\n );\n\n require(binIds.length == 1, \"Unexpected binIds length\");\n\n // burn remaining OETH\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n\n // needs to be called after _updateUnderlyingAssets so the updated amount\n // is reflected in the event\n emit LiquidityAdded(\n _wethBalance, // wethAmountDesired\n OETHRequired, // oethAmountDesired\n _wethAmount, // wethAmountSupplied\n _oethAmount, // oethAmountSupplied\n tokenId, // tokenId\n underlyingAssets\n );\n }\n\n /**\n * @dev The function creates liquidity parameters required to be able to add liquidity to the pool.\n * The function needs to handle the 3 different cases of the way liquidity is added:\n * - only WETH present in the tick\n * - only OETH present in the tick\n * - both tokens present in the tick\n *\n */\n function _getAddLiquidityParams(uint256 _maxWETH, uint256 _maxOETH)\n internal\n returns (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n )\n {\n IMaverickV2Pool.AddLiquidityParams[]\n memory addParams = new IMaverickV2Pool.AddLiquidityParams[](1);\n int32[] memory ticks = new int32[](1);\n uint128[] memory amounts = new uint128[](1);\n ticks[0] = TICK_NUMBER;\n // arbitrary LP amount\n amounts[0] = 1e24;\n\n // construct value for Quoter with arbitrary LP amount\n IMaverickV2Pool.AddLiquidityParams memory addParam = IMaverickV2Pool\n .AddLiquidityParams({\n kind: MAV_STATIC_BIN_KIND,\n ticks: ticks,\n amounts: amounts\n });\n\n // get the WETH and OETH required to get the proportion of tokens required\n // given the arbitrary liquidity\n (WETHRequired, OETHRequired, ) = quoter.calculateAddLiquidity(\n mPool,\n addParam\n );\n\n /**\n * If either token required is 0 then the tick consists only of the other token. In that\n * case the liquidity calculations need to be done using the non 0 token. By setting the\n * tokenRequired from 0 to 1 the `min` in next step will ignore that (the bigger) value.\n */\n WETHRequired = WETHRequired == 0 ? 1 : WETHRequired;\n OETHRequired = OETHRequired == 0 ? 1 : OETHRequired;\n\n addParam.amounts[0] = Math_v5\n .min(\n ((_maxWETH - 1) * 1e24) / WETHRequired,\n ((_maxOETH - 1) * 1e24) / OETHRequired\n )\n .toUint128();\n\n // update the quotes with the actual amounts\n (WETHRequired, OETHRequired, ) = quoter.calculateAddLiquidity(\n mPool,\n addParam\n );\n\n require(_maxWETH >= WETHRequired, \"More WETH required than specified\");\n require(_maxOETH >= OETHRequired, \"More OETH required than specified\");\n\n // organize values to be used by manager\n addParams[0] = addParam;\n packedArgs = liquidityManager.packAddLiquidityArgsArray(addParams);\n // price can stay 0 if array only has one element\n packedSqrtPriceBreaks = liquidityManager.packUint88Array(\n new uint88[](1)\n );\n }\n\n /**\n * @dev Check that the Rooster pool price is within the expected\n * parameters.\n * This function works whether the strategy contract has liquidity\n * position in the pool or not. The function returns _wethSharePct\n * as a gas optimization measure.\n * @param _throwException when set to true the function throws an exception\n * when pool's price is not within expected range.\n * @return _isExpectedRange Bool expressing price is within expected range\n * @return _wethSharePct Share of WETH owned by this strategy contract in the\n * configured ticker.\n */\n function _checkForExpectedPoolPrice(bool _throwException)\n internal\n view\n returns (bool _isExpectedRange, uint256 _wethSharePct)\n {\n require(\n allowedWethShareStart != 0 && allowedWethShareEnd != 0,\n \"Weth share interval not set\"\n );\n\n uint256 _currentPrice = getPoolSqrtPrice();\n\n /**\n * First check pool price is in expected tick range\n *\n * A revert is issued even though price being equal to the lower bound as that can not\n * be within the approved tick range.\n */\n if (\n _currentPrice <= sqrtPriceTickLower ||\n _currentPrice >= sqrtPriceTickHigher\n ) {\n if (_throwException) {\n revert OutsideExpectedTickRange();\n }\n\n return (false, _currentPrice <= sqrtPriceTickLower ? 0 : 1e18);\n }\n\n // 18 decimal number expressed WETH tick share\n _wethSharePct = _getWethShare(_currentPrice);\n\n if (\n _wethSharePct < allowedWethShareStart ||\n _wethSharePct > allowedWethShareEnd\n ) {\n if (_throwException) {\n revert PoolRebalanceOutOfBounds(\n _wethSharePct,\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n return (false, _wethSharePct);\n }\n\n return (true, _wethSharePct);\n }\n\n /**\n * @notice Rebalance the pool to the desired token split and Deposit any WETH on the contract to the\n * underlying rooster pool. Print the required amount of corresponding OETH. After the rebalancing is\n * done burn any potentially remaining OETH tokens still on the strategy contract.\n *\n * This function has a slightly different behaviour depending on the status of the underlying Rooster\n * pool. The function consists of the following 3 steps:\n * 1. withdrawLiquidityOption -> this is a configurable option where either only part of the liquidity\n * necessary for the swap is removed, or all of it. This way the rebalance\n * is able to optimize for volume, for efficiency or anything in between\n * 2. swapToDesiredPosition -> move active trading price in the pool to be able to deposit WETH & OETH\n * tokens with the desired pre-configured ratios\n * 3. addLiquidity -> add liquidity into the pool respecting ratio split configuration\n *\n *\n * Exact _amountToSwap, _swapWeth & _minTokenReceived parameters shall be determined by simulating the\n * transaction off-chain. The strategy checks that after the swap the share of the tokens is in the\n * expected ranges.\n *\n * @param _amountToSwap The amount of the token to swap\n * @param _swapWeth Swap using WETH when true, use OETH when false\n * @param _minTokenReceived Slippage check -> minimum amount of token expected in return\n * @param _liquidityToRemovePct Percentage of liquidity to remove -> the percentage amount of liquidity to\n * remove before performing the swap. 1e18 denominated\n */\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived,\n uint256 _liquidityToRemovePct\n ) external nonReentrant onlyGovernorOrStrategist {\n _rebalance(\n _amountToSwap,\n _swapWeth,\n _minTokenReceived,\n _liquidityToRemovePct\n );\n }\n\n // slither-disable-start reentrancy-no-eth\n function _rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived,\n uint256 _liquidityToRemovePct\n ) internal {\n // Remove the required amount of liquidity\n if (_liquidityToRemovePct > 0) {\n _removeLiquidity(_liquidityToRemovePct);\n }\n\n // in some cases (e.g. deposits) we will just want to add liquidity and not\n // issue a swap to move the active trading position within the pool. Before or after a\n // deposit or as a standalone call the strategist might issue a rebalance to move the\n // active trading price to a more desired position.\n if (_amountToSwap > 0) {\n // In case liquidity has been removed and there is still not enough WETH owned by the\n // strategy contract remove additional required amount of WETH.\n if (_swapWeth) _ensureWETHBalance(_amountToSwap);\n\n _swapToDesiredPosition(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n\n // calling check liquidity early so we don't get unexpected errors when adding liquidity\n // in the later stages of this function\n _checkForExpectedPoolPrice(true);\n\n _addLiquidity();\n\n // this call shouldn't be necessary, since adding liquidity shouldn't affect the active\n // trading price. It is a defensive programming measure.\n (, uint256 _wethSharePct) = _checkForExpectedPoolPrice(true);\n\n // revert if protocol insolvent\n _solvencyAssert();\n\n emit PoolRebalanced(_wethSharePct);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @dev Perform a swap so that after the swap the tick has the desired WETH to OETH token share.\n */\n function _swapToDesiredPosition(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n IERC20 _tokenToSwap = IERC20(_swapWeth ? WETH : OETH);\n uint256 _balance = _tokenToSwap.balanceOf(address(this));\n\n if (_balance < _amountToSwap) {\n // This should never trigger since _ensureWETHBalance will already\n // throw an error if there is not enough WETH\n if (_swapWeth) {\n revert NotEnoughWethForSwap(_balance, _amountToSwap);\n }\n // if swapping OETH\n uint256 mintForSwap = _amountToSwap - _balance;\n IVault(vaultAddress).mintForStrategy(mintForSwap);\n }\n\n // SafeERC20 is used for IERC20 transfers. Not sure why slither complains\n // slither-disable-next-line unchecked-transfer\n _tokenToSwap.transfer(address(mPool), _amountToSwap);\n\n // tickLimit: the furthest tick a swap will execute in. If no limit is desired,\n // value should be set to type(int32).max for a tokenAIn (WETH) swap\n // and type(int32).min for a swap where tokenB (OETH) is the input\n\n IMaverickV2Pool.SwapParams memory swapParams = IMaverickV2Pool\n // exactOutput defines whether the amount specified is the output\n // or the input amount of the swap\n .SwapParams({\n amount: _amountToSwap,\n tokenAIn: _swapWeth,\n exactOutput: false,\n tickLimit: TICK_NUMBER\n });\n\n // swaps without a callback as the assets are already sent to the pool\n (, uint256 amountOut) = mPool.swap(\n address(this),\n swapParams,\n bytes(\"\")\n );\n\n /**\n * There could be additional checks here for validating minTokenReceived is within the\n * expected range (e.g. 99% - 101% of the token sent in). Though that doesn't provide\n * any additional security. After the swap the `_checkForExpectedPoolPrice` validates\n * that the swap has moved the price into the expected tick (# -1).\n *\n * If the guardian forgets to set a `_minTokenReceived` and a sandwich attack bends\n * the pool before the swap the `_checkForExpectedPoolPrice` will fail the transaction.\n *\n * A check would not prevent a compromised guardian from stealing funds as multiple\n * transactions each loosing smaller amount of funds are still possible.\n */\n if (amountOut < _minTokenReceived) {\n revert SlippageCheck(amountOut);\n }\n\n /**\n * In the interest of each function in `_rebalance` to leave the contract state as\n * clean as possible the OETH tokens here are burned. This decreases the\n * dependence where `_swapToDesiredPosition` function relies on later functions\n * (`addLiquidity`) to burn the OETH. Reducing the risk of error introduction.\n */\n _burnOethOnTheContract();\n }\n\n /**\n * @dev This function removes the appropriate amount of liquidity to ensure that the required\n * amount of WETH is available on the contract\n *\n * @param _amount WETH balance required on the contract\n */\n function _ensureWETHBalance(uint256 _amount) internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance >= _amount) {\n return;\n }\n\n require(tokenId != 0, \"No liquidity available\");\n uint256 _additionalWethRequired = _amount - _wethBalance;\n (uint256 _wethInThePool, ) = getPositionPrincipal();\n\n if (_wethInThePool < _additionalWethRequired) {\n revert NotEnoughWethLiquidity(\n _wethInThePool,\n _additionalWethRequired\n );\n }\n\n uint256 shareOfWethToRemove = _wethInThePool <= 1\n ? 1e18\n : Math_v5.min(\n /**\n * When dealing with shares of liquidity to remove there is always some\n * rounding involved. After extensive fuzz testing the below approach\n * yielded the best results where the strategy overdraws the least and\n * never removes insufficient amount of WETH.\n */\n (_additionalWethRequired + 2).divPrecisely(_wethInThePool - 1) +\n 2,\n 1e18\n );\n\n _removeLiquidity(shareOfWethToRemove);\n }\n\n /**\n * @dev Decrease partial or all liquidity from the pool.\n * @param _liquidityToDecrease The amount of liquidity to remove denominated in 1e18\n */\n function _removeLiquidity(uint256 _liquidityToDecrease) internal {\n require(_liquidityToDecrease > 0, \"Must remove some liquidity\");\n require(\n _liquidityToDecrease <= 1e18,\n \"Can not remove more than 100% of liquidity\"\n );\n\n // 0 indicates the first (and only) bin in the NFT LP position.\n IMaverickV2Pool.RemoveLiquidityParams memory params = maverickPosition\n .getRemoveParams(tokenId, 0, _liquidityToDecrease);\n (uint256 _amountWeth, uint256 _amountOeth) = maverickPosition\n .removeLiquidityToSender(tokenId, mPool, params);\n\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n\n // needs to be called after the _updateUnderlyingAssets so the updated amount is reflected\n // in the event\n emit LiquidityRemoved(\n _liquidityToDecrease,\n _amountWeth,\n _amountOeth,\n underlyingAssets\n );\n }\n\n /**\n * @dev Burns any OETH tokens remaining on the strategy contract if the balance is\n * above the action threshold.\n */\n function _burnOethOnTheContract() internal {\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(_oethBalance);\n }\n\n /**\n * @notice Returns the percentage of WETH liquidity in the configured ticker\n * owned by this strategy contract.\n * @return uint256 1e18 denominated percentage expressing the share\n */\n function getWETHShare() external view returns (uint256) {\n uint256 _currentPrice = getPoolSqrtPrice();\n return _getWethShare(_currentPrice);\n }\n\n /**\n * @dev Returns the share of WETH in tick denominated in 1e18\n */\n function _getWethShare(uint256 _currentPrice)\n internal\n view\n returns (uint256)\n {\n (\n uint256 wethAmount,\n uint256 oethAmount\n ) = _reservesInTickForGivenPriceAndLiquidity(\n sqrtPriceTickLower,\n sqrtPriceTickHigher,\n _currentPrice,\n 1e24\n );\n\n return wethAmount.divPrecisely(wethAmount + oethAmount);\n }\n\n /**\n * @notice Returns the current pool price in square root\n * @return Square root of the pool price\n */\n function getPoolSqrtPrice() public view returns (uint256) {\n return poolLens.getPoolSqrtPrice(mPool);\n }\n\n /**\n * @notice Returns the current active trading tick of the underlying pool\n * @return _currentTick Current pool trading tick\n */\n function getCurrentTradingTick() public view returns (int32 _currentTick) {\n _currentTick = mPool.getState().activeTick;\n }\n\n /**\n * @notice Mint the initial NFT position\n *\n * @dev This amount is \"gifted\" to the strategy contract and will count as a yield\n * surplus.\n */\n // slither-disable-start reentrancy-no-eth\n function mintInitialPosition() external onlyGovernor nonReentrant {\n require(tokenId == 0, \"Initial position already minted\");\n (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n ) = _getAddLiquidityParams(1e16, 1e16);\n\n // Mint rounded up OETH amount\n if (OETHRequired > 0) {\n IVault(vaultAddress).mintForStrategy(OETHRequired);\n }\n\n _approveTokenAmounts(WETHRequired, OETHRequired);\n\n // Store the tokenId before calling updateUnderlyingAssets as it relies on the tokenId\n // not being 0\n (, , , tokenId) = liquidityManager.mintPositionNftToSender(\n mPool,\n packedSqrtPriceBreaks,\n packedArgs\n );\n\n // burn remaining OETH\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @notice Returns the balance of tokens the strategy holds in the LP position\n * @return _amountWeth Amount of WETH in position\n * @return _amountOeth Amount of OETH in position\n */\n function getPositionPrincipal()\n public\n view\n returns (uint256 _amountWeth, uint256 _amountOeth)\n {\n if (tokenId == 0) {\n return (0, 0);\n }\n\n (_amountWeth, _amountOeth, ) = _getPositionInformation();\n }\n\n /**\n * @dev Returns the balance of tokens the strategy holds in the LP position\n * @return _amountWeth Amount of WETH in position\n * @return _amountOeth Amount of OETH in position\n * @return liquidity Amount of liquidity in the position\n */\n function _getPositionInformation()\n internal\n view\n returns (\n uint256 _amountWeth,\n uint256 _amountOeth,\n uint256 liquidity\n )\n {\n IMaverickV2Position.PositionFullInformation\n memory positionInfo = maverickPosition.tokenIdPositionInformation(\n tokenId,\n 0\n );\n\n require(\n positionInfo.liquidities.length == 1,\n \"Unexpected liquidities length\"\n );\n require(positionInfo.ticks.length == 1, \"Unexpected ticks length\");\n\n _amountWeth = positionInfo.amountA;\n _amountOeth = positionInfo.amountB;\n liquidity = positionInfo.liquidities[0];\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99.8%) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethSupply = IERC20(OETH).totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethSupply) < SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /**\n * @dev Collect Rooster reward token, and send it to the harvesterAddress\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Do nothing if there's no position minted\n if (tokenId > 0) {\n uint32[] memory binIds = new uint32[](1);\n IMaverickV2Pool.TickState memory tickState = mPool.getTick(\n TICK_NUMBER\n );\n // get the binId for the MAV_STATIC_BIN_KIND in tick TICK_NUMBER (-1)\n binIds[0] = tickState.binIdsByTick[0];\n\n uint256 lastEpoch = votingDistributor.lastEpoch();\n\n poolDistributor.claimLp(\n address(this),\n tokenId,\n mPool,\n binIds,\n lastEpoch\n );\n }\n\n // Run the internal inherited function\n _collectRewardTokens();\n }\n\n /***************************************\n Balances and Fees\n ****************************************/\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256)\n {\n require(_asset == WETH, \"Only WETH supported\");\n\n // because of PoolLens inaccuracy there is usually some dust WETH left on the contract\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n // just paranoia check, in case there is OETH in the strategy that for some reason hasn't\n // been burned yet. This should always be 0.\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n return underlyingAssets + _wethBalance + _oethBalance;\n }\n\n /// @dev This function updates the amount of underlying assets with the approach of the least possible\n /// total tokens extracted for the current liquidity in the pool.\n function _updateUnderlyingAssets() internal {\n /**\n * Our net value represent the smallest amount of tokens we are able to extract from the position\n * given our liquidity.\n *\n * The least amount of tokens ex-tractable from the position is where the active trading price is\n * at the edge between tick -1 & tick 0. There the pool is offering 1:1 trades between WETH & OETH.\n * At that moment the pool consists completely of WETH and no OETH.\n *\n * The more swaps from OETH -> WETH happen on the pool the more the price starts to move away from the tick 0\n * towards the middle of tick -1 making OETH (priced in WETH) cheaper.\n */\n\n uint256 _wethAmount = tokenId == 0 ? 0 : _balanceInPosition();\n\n underlyingAssets = _wethAmount;\n emit UnderlyingAssetsUpdated(_wethAmount);\n }\n\n /**\n * @dev Strategy reserves (which consist only of WETH in case of Rooster - Plume pool)\n * when the tick price is closest to parity - assuring the lowest amount of tokens\n * returned for the current position liquidity.\n */\n function _balanceInPosition() internal view returns (uint256 _wethBalance) {\n (, , uint256 liquidity) = _getPositionInformation();\n\n uint256 _oethBalance;\n\n (_wethBalance, _oethBalance) = _reservesInTickForGivenPriceAndLiquidity(\n sqrtPriceTickLower,\n sqrtPriceTickHigher,\n sqrtPriceAtParity,\n liquidity\n );\n\n require(_oethBalance == 0, \"Non zero oethBalance\");\n }\n\n /**\n * @notice Tick dominance denominated in 1e18\n * @return _tickDominance The share of liquidity in TICK_NUMBER tick owned\n * by the strategy contract denominated in 1e18\n */\n function tickDominance() public view returns (uint256 _tickDominance) {\n IMaverickV2Pool.TickState memory tickState = mPool.getTick(TICK_NUMBER);\n\n uint256 wethReserve = tickState.reserveA;\n uint256 oethReserve = tickState.reserveB;\n\n // prettier-ignore\n (uint256 _amountWeth, uint256 _amountOeth, ) = _getPositionInformation();\n\n if (wethReserve + oethReserve == 0) {\n return 0;\n }\n\n _tickDominance = (_amountWeth + _amountOeth).divPrecisely(\n wethReserve + oethReserve\n );\n }\n\n /***************************************\n Hidden functions\n ****************************************/\n /**\n * @dev Unsupported\n */\n function setPTokenAddress(address, address) external pure override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function removePToken(uint256) external pure override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function _abstractSetPToken(address, address) internal pure override {\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function safeApproveAllTokens() external pure override {\n // all the amounts are approved at the time required\n revert(\"Unsupported method\");\n }\n\n /***************************************\n Maverick liquidity utilities\n ****************************************/\n\n /// @notice Calculates deltaA = liquidity * (sqrt(upper) - sqrt(lower))\n /// Calculates deltaB = liquidity / sqrt(lower) - liquidity / sqrt(upper),\n /// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower))\n ///\n /// @dev refactored from here:\n // solhint-disable-next-line max-line-length\n /// https://github.com/rooster-protocol/rooster-contracts/blob/main/v2-supplemental/contracts/libraries/LiquidityUtilities.sol#L665-L695\n function _reservesInTickForGivenPriceAndLiquidity(\n uint256 _lowerSqrtPrice,\n uint256 _upperSqrtPrice,\n uint256 _newSqrtPrice,\n uint256 _liquidity\n ) internal pure returns (uint128 reserveA, uint128 reserveB) {\n if (_liquidity == 0) {\n (reserveA, reserveB) = (0, 0);\n } else {\n uint256 lowerEdge = MathRooster.max(_lowerSqrtPrice, _newSqrtPrice);\n\n reserveA = MathRooster\n .mulCeil(\n _liquidity,\n MathRooster.clip(\n MathRooster.min(_upperSqrtPrice, _newSqrtPrice),\n _lowerSqrtPrice\n )\n )\n .toUint128();\n reserveB = MathRooster\n .mulDivCeil(\n _liquidity,\n 1e18 * MathRooster.clip(_upperSqrtPrice, lowerEdge),\n _upperSqrtPrice * lowerEdge\n )\n .toUint128();\n }\n }\n}\n" + }, + "contracts/strategies/sonic/SonicStakingStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SonicValidatorDelegator } from \"./SonicValidatorDelegator.sol\";\nimport { IWrappedSonic } from \"../../interfaces/sonic/IWrappedSonic.sol\";\n\n/**\n * @title Staking Strategy for Sonic's native S currency\n * @author Origin Protocol Inc\n */\ncontract SonicStakingStrategy is SonicValidatorDelegator {\n // For future use\n uint256[50] private __gap;\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wrappedSonic,\n address _sfc\n ) SonicValidatorDelegator(_baseConfig, _wrappedSonic, _sfc) {}\n\n /// @notice Deposit wrapped S asset into the underlying platform.\n /// @param _asset Address of asset to deposit. Has to be Wrapped Sonic (wS).\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposit Wrapped Sonic (wS) to this strategy and delegate to a validator.\n * @param _asset Address of Wrapped Sonic (wS) token\n * @param _amount Amount of Wrapped Sonic (wS) to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal virtual {\n require(_amount > 0, \"Must deposit something\");\n\n _delegate(_amount);\n emit Deposit(_asset, address(0), _amount);\n }\n\n /**\n * @notice Deposit the entire balance of wrapped S in this strategy contract into\n * the underlying platform.\n */\n function depositAll() external virtual override onlyVault nonReentrant {\n uint256 wSBalance = IERC20(wrappedSonic).balanceOf(address(this));\n\n if (wSBalance > 0) {\n _deposit(wrappedSonic, wSBalance);\n }\n }\n\n /// @notice Withdraw Wrapped Sonic (wS) from this strategy contract.\n /// Used only if some wS is lingering on the contract.\n /// That can happen only when someone sends wS directly to this contract\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset Address of the Wrapped Sonic (wS) token\n /// @param _amount Amount of Wrapped Sonic (wS) to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal override {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(_asset).transfer(_recipient, _amount);\n\n emit Withdrawal(wrappedSonic, address(0), _amount);\n }\n\n /// @notice Transfer all Wrapped Sonic (wS) deposits back to the vault.\n /// This does not withdraw from delegated validators. That has to be done separately with `undelegate`.\n /// Any native S in this strategy will be withdrawn.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 balance = address(this).balance;\n if (balance > 0) {\n IWrappedSonic(wrappedSonic).deposit{ value: balance }();\n }\n uint256 wSBalance = IERC20(wrappedSonic).balanceOf(address(this));\n if (wSBalance > 0) {\n _withdraw(vaultAddress, wrappedSonic, wSBalance);\n }\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset token\n */\n function supportsAsset(address _asset)\n public\n view\n virtual\n override\n returns (bool)\n {\n return _asset == wrappedSonic;\n }\n\n /**\n * @notice is not supported for this strategy as the\n * Wrapped Sonic (wS) token is set at deploy time.\n */\n function setPTokenAddress(address, address)\n external\n view\n override\n onlyGovernor\n {\n revert(\"unsupported function\");\n }\n\n /// @notice is not used by this strategy as all staking rewards are restaked\n function collectRewardTokens() external override nonReentrant {\n revert(\"unsupported function\");\n }\n\n /**\n * @notice is not supported for this strategy as the\n * Wrapped Sonic (wS) token is set at deploy time.\n */\n function removePToken(uint256) external view override onlyGovernor {\n revert(\"unsupported function\");\n }\n\n /// @dev is not used by this strategy but must be implemented as it's abstract\n /// in the inherited `InitializableAbstractStrategy` contract.\n function _abstractSetPToken(address, address) internal virtual override {}\n\n /// @notice is not used by this strategy\n function safeApproveAllTokens() external override onlyGovernor {}\n}\n" + }, + "contracts/strategies/sonic/SonicSwapXAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title SwapX Algorithmic Market Maker (AMO) Strategy\n * @notice AMO strategy for the SwapX OS/wS stable pool\n * @author Origin Protocol Inc\n */\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport { sqrt } from \"../../utils/PRBMath.sol\";\nimport { IBasicToken } from \"../../interfaces/IBasicToken.sol\";\nimport { IPair } from \"../../interfaces/sonic/ISwapXPair.sol\";\nimport { IGauge } from \"../../interfaces/sonic/ISwapXGauge.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\n\ncontract SonicSwapXAMOStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @notice a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n /// @notice Precision for the SwapX Stable AMM (sAMM) invariant k.\n uint256 public constant PRECISION = 1e18;\n\n /// @notice Address of the Wrapped S (wS) token.\n address public immutable ws;\n\n /// @notice Address of the OS token contract.\n address public immutable os;\n\n /// @notice Address of the SwapX Stable pool contract.\n address public immutable pool;\n\n /// @notice Address of the SwapX Gauge contract.\n address public immutable gauge;\n\n /// @notice The max amount the OS/wS price can deviate from peg (1e18)\n /// before deposits are reverted scaled to 18 decimals.\n /// eg 0.01e18 or 1e16 is 1% which is 100 basis points.\n /// This is the amount below and above peg so a 50 basis point deviation (0.005e18)\n /// allows a price range from 0.995 to 1.005.\n uint256 public maxDepeg;\n\n event SwapOTokensToPool(\n uint256 osMinted,\n uint256 wsDepositAmount,\n uint256 osDepositAmount,\n uint256 lpTokens\n );\n event SwapAssetsToPool(\n uint256 wsSwapped,\n uint256 lpTokens,\n uint256 osBurnt\n );\n event MaxDepegUpdated(uint256 maxDepeg);\n\n /**\n * @dev Verifies that the caller is the Strategist of the Vault.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Skim the SwapX pool in case any extra wS or OS tokens were added\n */\n modifier skimPool() {\n IPair(pool).skim(address(this));\n _;\n }\n\n /**\n * @dev Checks the pool is balanced enough to allow deposits.\n */\n modifier nearBalancedPool() {\n // OS/wS price = wS / OS\n // Get the OS/wS price for selling 1 OS for wS\n // As OS is 1, the wS amount is the OS/wS price\n uint256 sellPrice = IPair(pool).getAmountOut(1e18, os);\n\n // Get the amount of OS received from selling 1 wS. This is buying OS.\n uint256 osAmount = IPair(pool).getAmountOut(1e18, ws);\n // Convert to a OS/wS price = wS / OS\n uint256 buyPrice = 1e36 / osAmount;\n\n uint256 pegPrice = 1e18;\n\n require(\n sellPrice >= pegPrice - maxDepeg && buyPrice <= pegPrice + maxDepeg,\n \"price out of range\"\n );\n _;\n }\n\n /**\n * @dev Checks the pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do swaps against the pool.\n * Deposits and withdrawals are proportional to the pool's balances hence don't need this check.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the pool\n (uint256 wsReservesBefore, uint256 osReservesBefore, ) = IPair(pool)\n .getReserves();\n // diff = wS balance - OS balance\n int256 diffBefore = wsReservesBefore.toInt256() -\n osReservesBefore.toInt256();\n\n _;\n\n // Get the asset and OToken balances in the pool\n (uint256 wsReservesAfter, uint256 osReservesAfter, ) = IPair(pool)\n .getReserves();\n // diff = wS balance - OS balance\n int256 diffAfter = wsReservesAfter.toInt256() -\n osReservesAfter.toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OS, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"Assets overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of wS, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"OTokens overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n /**\n * @param _baseConfig The `platformAddress` is the address of the SwapX pool.\n * The `vaultAddress` is the address of the Origin Sonic Vault.\n * @param _os Address of the OS token.\n * @param _ws Address of the Wrapped S (wS) token.\n * @param _gauge Address of the SwapX gauge for the pool.\n */\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _os,\n address _ws,\n address _gauge\n ) InitializableAbstractStrategy(_baseConfig) {\n // Check the pool tokens are correct\n require(\n IPair(_baseConfig.platformAddress).token0() == _ws &&\n IPair(_baseConfig.platformAddress).token1() == _os,\n \"Incorrect pool tokens\"\n );\n // Checked both tokens are to 18 decimals\n require(\n IBasicToken(_ws).decimals() == 18 &&\n IBasicToken(_os).decimals() == 18,\n \"Incorrect token decimals\"\n );\n // Check the SwapX pool is a Stable AMM (sAMM)\n require(\n IPair(_baseConfig.platformAddress).isStable() == true,\n \"Pool not stable\"\n );\n // Check the gauge is for the pool\n require(\n IGauge(_gauge).TOKEN() == _baseConfig.platformAddress,\n \"Incorrect gauge\"\n );\n\n // Set the immutable variables\n os = _os;\n ws = _ws;\n pool = _baseConfig.platformAddress;\n gauge = _gauge;\n\n // This is an implementation contract. The governor is set in the proxy contract.\n _setGovernor(address(0));\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as SwapX strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Array containing SWPx token address\n * @param _maxDepeg The max amount the OS/wS price can deviate from peg (1e18) before deposits are reverted.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n uint256 _maxDepeg\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = pool;\n\n address[] memory _assets = new address[](1);\n _assets[0] = ws;\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n maxDepeg = _maxDepeg;\n\n _approveBase();\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit an amount of Wrapped S (wS) into the SwapX pool.\n * Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @dev This tx must be wrapped by the VaultValueChecker.\n * To minimize loses, the pool should be rebalanced before depositing.\n * The pool's OS/wS price must be within the maxDepeg range.\n * @param _asset Address of Wrapped S (wS) token.\n * @param _wsAmount Amount of Wrapped S (wS) tokens to deposit.\n */\n function deposit(address _asset, uint256 _wsAmount)\n external\n override\n onlyVault\n nonReentrant\n skimPool\n nearBalancedPool\n {\n require(_asset == ws, \"Unsupported asset\");\n require(_wsAmount > 0, \"Must deposit something\");\n\n (uint256 osDepositAmount, ) = _deposit(_wsAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the deposited wS tokens\n emit Deposit(ws, pool, _wsAmount);\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osDepositAmount);\n }\n\n /**\n * @notice Deposit all the strategy's Wrapped S (wS) tokens into the SwapX pool.\n * Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @dev This tx must be wrapped by the VaultValueChecker.\n * To minimize loses, the pool should be rebalanced before depositing.\n * The pool's OS/wS price must be within the maxDepeg range.\n */\n function depositAll()\n external\n override\n onlyVault\n nonReentrant\n skimPool\n nearBalancedPool\n {\n uint256 wsBalance = IERC20(ws).balanceOf(address(this));\n if (wsBalance > 0) {\n (uint256 osDepositAmount, ) = _deposit(wsBalance);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the deposited wS tokens\n emit Deposit(ws, pool, wsBalance);\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osDepositAmount);\n }\n }\n\n /**\n * @dev Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @param _wsAmount Amount of Wrapped S (wS) tokens to deposit.\n * @return osDepositAmount Amount of OS tokens minted and deposited into the pool.\n * @return lpTokens Amount of SwapX pool LP tokens minted and deposited into the gauge.\n */\n function _deposit(uint256 _wsAmount)\n internal\n returns (uint256 osDepositAmount, uint256 lpTokens)\n {\n // Calculate the required amount of OS to mint based on the wS amount.\n osDepositAmount = _calcTokensToMint(_wsAmount);\n\n // Mint the required OS tokens to this strategy\n IVault(vaultAddress).mintForStrategy(osDepositAmount);\n\n // Add wS and OS liquidity to the pool and stake in gauge\n lpTokens = _depositToPoolAndGauge(_wsAmount, osDepositAmount);\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw wS and OS from the SwapX pool, burn the OS,\n * and transfer the wS to the recipient.\n * @param _recipient Address of the Vault.\n * @param _asset Address of the Wrapped S (wS) contract.\n * @param _wsAmount Amount of Wrapped S (wS) to withdraw.\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _wsAmount\n ) external override onlyVault nonReentrant skimPool {\n require(_wsAmount > 0, \"Must withdraw something\");\n require(_asset == ws, \"Unsupported asset\");\n // This strategy can't be set as a default strategy for wS in the Vault.\n // This means the recipient must always be the Vault.\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n // Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n uint256 lpTokens = _calcTokensToBurn(_wsAmount);\n\n // Withdraw pool LP tokens from the gauge and remove assets from from the pool\n _withdrawFromGaugeAndPool(lpTokens);\n\n // Burn all the removed OS and any that was left in the strategy\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Transfer wS to the recipient\n // Note there can be a dust amount of wS left in the strategy as\n // the burn of the pool's LP tokens is rounded up\n require(\n IERC20(ws).balanceOf(address(this)) >= _wsAmount,\n \"Not enough wS removed from pool\"\n );\n IERC20(ws).safeTransfer(_recipient, _wsAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the withdrawn wS tokens\n emit Withdrawal(ws, pool, _wsAmount);\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n }\n\n /**\n * @notice Withdraw all pool LP tokens from the gauge,\n * remove all wS and OS from the SwapX pool,\n * burn all the OS tokens,\n * and transfer all the wS to the Vault contract.\n * @dev There is no solvency check here as withdrawAll can be called to\n * quickly secure assets to the Vault in emergencies.\n */\n function withdrawAll()\n external\n override\n onlyVaultOrGovernor\n nonReentrant\n skimPool\n {\n // Get all the pool LP tokens the strategy has staked in the gauge\n uint256 lpTokens = IGauge(gauge).balanceOf(address(this));\n // Can not withdraw zero LP tokens from the gauge\n if (lpTokens == 0) return;\n\n if (IGauge(gauge).emergency()) {\n // The gauge is in emergency mode\n _emergencyWithdrawFromGaugeAndPool();\n } else {\n // Withdraw pool LP tokens from the gauge and remove assets from from the pool\n _withdrawFromGaugeAndPool(lpTokens);\n }\n\n // Burn all OS in this strategy contract\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Get the strategy contract's wS balance.\n // This includes all that was removed from the SwapX pool and\n // any that was sitting in the strategy contract before the removal.\n uint256 wsBalance = IERC20(ws).balanceOf(address(this));\n IERC20(ws).safeTransfer(vaultAddress, wsBalance);\n\n // Emit event for the withdrawn wS tokens\n emit Withdrawal(ws, pool, wsBalance);\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n }\n\n /***************************************\n Pool Rebalancing\n ****************************************/\n\n /** @notice Used when there is more OS than wS in the pool.\n * wS and OS is removed from the pool, the received wS is swapped for OS\n * and the left over OS in the strategy is burnt.\n * The OS/wS price is < 1.0 so OS is being bought at a discount.\n * @param _wsAmount Amount of Wrapped S (wS) to swap into the pool.\n */\n function swapAssetsToPool(uint256 _wsAmount)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n skimPool\n {\n require(_wsAmount > 0, \"Must swap something\");\n\n // 1. Partially remove liquidity so there’s enough wS for the swap\n\n // Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n uint256 lpTokens = _calcTokensToBurn(_wsAmount);\n require(lpTokens > 0, \"No LP tokens to burn\");\n\n _withdrawFromGaugeAndPool(lpTokens);\n\n // 2. Swap wS for OS against the pool\n // Swap exact amount of wS for OS against the pool\n // There can be a dust amount of wS left in the strategy as the burn of the pool's LP tokens is rounded up\n _swapExactTokensForTokens(_wsAmount, ws, os);\n\n // 3. Burn all the OS left in the strategy from the remove liquidity and swap\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n // Emit event for the swap\n emit SwapAssetsToPool(_wsAmount, lpTokens, osToBurn);\n }\n\n /**\n * @notice Used when there is more wS than OS in the pool.\n * OS is minted and swapped for wS against the pool,\n * more OS is minted and added back into the pool with the swapped out wS.\n * The OS/wS price is > 1.0 so OS is being sold at a premium.\n * @param _osAmount Amount of OS to swap into the pool.\n */\n function swapOTokensToPool(uint256 _osAmount)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n skimPool\n {\n require(_osAmount > 0, \"Must swap something\");\n\n // 1. Mint OS so it can be swapped into the pool\n\n // There can be OS in the strategy from skimming the pool\n uint256 osInStrategy = IERC20(os).balanceOf(address(this));\n require(_osAmount >= osInStrategy, \"Too much OS in strategy\");\n uint256 osToMint = _osAmount - osInStrategy;\n\n // Mint the required OS tokens to this strategy\n IVault(vaultAddress).mintForStrategy(osToMint);\n\n // 2. Swap OS for wS against the pool\n _swapExactTokensForTokens(_osAmount, os, ws);\n\n // The wS is from the swap and any wS that was sitting in the strategy\n uint256 wsDepositAmount = IERC20(ws).balanceOf(address(this));\n\n // 3. Add wS and OS back to the pool in proportion to the pool's reserves\n (uint256 osDepositAmount, uint256 lpTokens) = _deposit(wsDepositAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osToMint + osDepositAmount);\n // Emit event for the swap\n emit SwapOTokensToPool(\n osToMint,\n wsDepositAmount,\n osDepositAmount,\n lpTokens\n );\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Get the wS value of assets in the strategy and SwapX pool.\n * The value of the assets in the pool is calculated assuming the pool is balanced.\n * This way the value can not be manipulated by changing the pool's token balances.\n * @param _asset Address of the Wrapped S (wS) token\n * @return balance Total value in wS.\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == ws, \"Unsupported asset\");\n\n // wS balance needed here for the balance check that happens from vault during depositing.\n balance = IERC20(ws).balanceOf(address(this));\n\n // This assumes 1 gauge LP token = 1 pool LP token\n uint256 lpTokens = IGauge(gauge).balanceOf(address(this));\n if (lpTokens == 0) return balance;\n\n // Add the strategy’s share of the wS and OS tokens in the SwapX pool if the pool was balanced.\n balance += _lpValue(lpTokens);\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == ws;\n }\n\n /**\n * @notice Collect accumulated SWPx (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect SWPx rewards from the gauge\n IGauge(gauge).getReward();\n\n _collectRewardTokens();\n }\n\n /***************************************\n Internal SwapX Pool and Gauge Functions\n ****************************************/\n\n /**\n * @dev Calculate the required amount of OS to mint based on the wS amount.\n * This ensures the proportion of OS tokens being added to the pool matches the proportion of wS tokens.\n * For example, if the added wS tokens is 10% of existing wS tokens in the pool,\n * then the OS tokens being added should also be 10% of the OS tokens in the pool.\n * @param _wsAmount Amount of Wrapped S (wS) to be added to the pool.\n * @return osAmount Amount of OS to be minted and added to the pool.\n */\n function _calcTokensToMint(uint256 _wsAmount)\n internal\n view\n returns (uint256 osAmount)\n {\n (uint256 wsReserves, uint256 osReserves, ) = IPair(pool).getReserves();\n require(wsReserves > 0, \"Empty pool\");\n\n // OS to add = (wS being added * OS in pool) / wS in pool\n osAmount = (_wsAmount * osReserves) / wsReserves;\n }\n\n /**\n * @dev Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n * from the pool.\n * @param _wsAmount Amount of Wrapped S (wS) to be removed from the pool.\n * @return lpTokens Amount of SwapX pool LP tokens to burn.\n */\n function _calcTokensToBurn(uint256 _wsAmount)\n internal\n view\n returns (uint256 lpTokens)\n {\n /* The SwapX pool proportionally returns the reserve tokens when removing liquidity.\n * First, calculate the proportion of required wS tokens against the pools wS reserves.\n * That same proportion is used to calculate the required amount of pool LP tokens.\n * For example, if the required wS tokens is 10% of the pool's wS reserves,\n * then 10% of the pool's LP supply needs to be burned.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognizant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on, the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n (uint256 wsReserves, , ) = IPair(pool).getReserves();\n require(wsReserves > 0, \"Empty pool\");\n\n lpTokens = (_wsAmount * IPair(pool).totalSupply()) / wsReserves;\n lpTokens += 1; // Add 1 to ensure we get enough LP tokens with rounding\n }\n\n /**\n * @dev Deposit Wrapped S (wS) and OS liquidity to the SwapX pool\n * and stake the pool's LP token in the gauge.\n * @param _wsAmount Amount of Wrapped S (wS) to deposit.\n * @param _osAmount Amount of OS to deposit.\n * @return lpTokens Amount of SwapX pool LP tokens minted.\n */\n function _depositToPoolAndGauge(uint256 _wsAmount, uint256 _osAmount)\n internal\n returns (uint256 lpTokens)\n {\n // Transfer wS to the pool\n IERC20(ws).safeTransfer(pool, _wsAmount);\n // Transfer OS to the pool\n IERC20(os).safeTransfer(pool, _osAmount);\n\n // Mint LP tokens from the pool\n lpTokens = IPair(pool).mint(address(this));\n\n // Deposit the pool's LP tokens into the gauge\n IGauge(gauge).deposit(lpTokens);\n }\n\n /**\n * @dev Withdraw pool LP tokens from the gauge and remove wS and OS from the pool.\n * @param _lpTokens Amount of SwapX pool LP tokens to withdraw from the gauge\n */\n function _withdrawFromGaugeAndPool(uint256 _lpTokens) internal {\n require(\n IGauge(gauge).balanceOf(address(this)) >= _lpTokens,\n \"Not enough LP tokens in gauge\"\n );\n\n // Withdraw pool LP tokens from the gauge\n IGauge(gauge).withdraw(_lpTokens);\n\n // Transfer the pool LP tokens to the pool\n IERC20(pool).safeTransfer(pool, _lpTokens);\n\n // Burn the LP tokens and transfer the wS and OS back to the strategy\n IPair(pool).burn(address(this));\n }\n\n /**\n * @dev Withdraw all pool LP tokens from the gauge when it's in emergency mode\n * and remove wS and OS from the pool.\n */\n function _emergencyWithdrawFromGaugeAndPool() internal {\n // Withdraw all pool LP tokens from the gauge\n IGauge(gauge).emergencyWithdraw();\n\n // Get the pool LP tokens in strategy\n uint256 _lpTokens = IERC20(pool).balanceOf(address(this));\n\n // Transfer the pool LP tokens to the pool\n IERC20(pool).safeTransfer(pool, _lpTokens);\n\n // Burn the LP tokens and transfer the wS and OS back to the strategy\n IPair(pool).burn(address(this));\n }\n\n /**\n * @dev Swap exact amount of tokens for another token against the pool.\n * @param _amountIn Amount of tokens to swap into the pool.\n * @param _tokenIn Address of the token going into the pool.\n * @param _tokenOut Address of the token being swapped out of the pool.\n */\n function _swapExactTokensForTokens(\n uint256 _amountIn,\n address _tokenIn,\n address _tokenOut\n ) internal {\n // Transfer in tokens to the pool\n IERC20(_tokenIn).safeTransfer(pool, _amountIn);\n\n // Calculate how much out tokens we get from the swap\n uint256 amountOut = IPair(pool).getAmountOut(_amountIn, _tokenIn);\n\n // Safety check that we are dealing with the correct pool tokens\n require(\n (_tokenIn == ws && _tokenOut == os) ||\n (_tokenIn == os && _tokenOut == ws),\n \"Unsupported swap\"\n );\n\n // Work out the correct order of the amounts for the pool\n (uint256 amount0, uint256 amount1) = _tokenIn == ws\n ? (uint256(0), amountOut)\n : (amountOut, 0);\n\n // Perform the swap on the pool\n IPair(pool).swap(amount0, amount1, address(this), new bytes(0));\n\n // The slippage protection against the amount out is indirectly done\n // via the improvePoolBalance\n }\n\n /// @dev Calculate the value of a LP position in a SwapX stable pool\n /// if the pool was balanced.\n /// @param _lpTokens Amount of LP tokens in the SwapX pool\n /// @return value The wS value of the LP tokens when the pool is balanced\n function _lpValue(uint256 _lpTokens) internal view returns (uint256 value) {\n // Get total supply of LP tokens\n uint256 totalSupply = IPair(pool).totalSupply();\n if (totalSupply == 0) return 0;\n\n // Get the current reserves of the pool\n (uint256 wsReserves, uint256 osReserves, ) = IPair(pool).getReserves();\n\n // Calculate the invariant of the pool assuming both tokens have 18 decimals.\n // k is scaled to 18 decimals.\n uint256 k = _invariant(wsReserves, osReserves);\n\n // If x = y, let’s denote x = y = z (where z is the common reserve value)\n // Substitute z into the invariant:\n // k = z^3 * z + z * z^3\n // k = 2 * z^4\n // Going back the other way to calculate the common reserve value z\n // z = (k / 2) ^ (1/4)\n // the total value of the pool when x = y is 2 * z, which is 2 * (k / 2) ^ (1/4)\n uint256 zSquared = sqrt((k * 1e18) / 2); // 18 + 18 = 36 decimals becomes 18 decimals after sqrt\n uint256 z = sqrt(zSquared * 1e18); // 18 + 18 = 36 decimals becomes 18 decimals after sqrt\n uint256 totalValueOfPool = 2 * z;\n\n // lp value = lp tokens * value of pool / total supply\n value = (_lpTokens * totalValueOfPool) / totalSupply;\n }\n\n /**\n * @dev Compute the invariant for a SwapX stable pool.\n * This assumed both x and y tokens are to 18 decimals which is checked in the constructor.\n * invariant: k = x^3 * y + x * y^3\n * @dev This implementation is copied from SwapX's Pair contract.\n * @param _x The amount of Wrapped S (wS) tokens in the pool\n * @param _y The amount of the OS tokens in the pool\n * @return k The invariant of the SwapX stable pool\n */\n function _invariant(uint256 _x, uint256 _y)\n internal\n pure\n returns (uint256 k)\n {\n uint256 _a = (_x * _y) / PRECISION;\n uint256 _b = ((_x * _x) / PRECISION + (_y * _y) / PRECISION);\n // slither-disable-next-line divide-before-multiply\n k = (_a * _b) / PRECISION;\n }\n\n /**\n * @dev Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalSupply = IERC20(os).totalSupply();\n\n if (\n _totalSupply > 0 &&\n _totalVaultValue.divPrecisely(_totalSupply) < SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Setters\n ****************************************/\n\n /**\n * @notice Set the maximum deviation from the OS/wS peg (1e18) before deposits are reverted.\n * @param _maxDepeg the OS/wS price from peg (1e18) in 18 decimals.\n * eg 0.01e18 or 1e16 is 1% which is 100 basis points.\n */\n function setMaxDepeg(uint256 _maxDepeg) external onlyGovernor {\n maxDepeg = _maxDepeg;\n\n emit MaxDepegUpdated(_maxDepeg);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve SwapX gauge contract to transfer SwapX pool LP tokens\n // This is needed for deposits of SwapX pool LP tokens into the gauge.\n // slither-disable-next-line unused-return\n IPair(pool).approve(address(gauge), type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/sonic/SonicValidatorDelegator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { ISFC } from \"../../interfaces/sonic/ISFC.sol\";\nimport { IWrappedSonic } from \"../../interfaces/sonic/IWrappedSonic.sol\";\n\n/**\n * @title Manages delegation to Sonic validators\n * @notice This contract implements all the required functionality to delegate to,\n undelegate from and withdraw from validators.\n * @author Origin Protocol Inc\n */\nabstract contract SonicValidatorDelegator is InitializableAbstractStrategy {\n /// @notice Address of Sonic's wrapped S token\n address public immutable wrappedSonic;\n /// @notice Sonic's Special Fee Contract (SFC)\n ISFC public immutable sfc;\n\n /// @notice a unique ID for each withdrawal request\n uint256 public nextWithdrawId;\n /// @notice Sonic (S) that is pending withdrawal after undelegating\n uint256 public pendingWithdrawals;\n\n /// @notice List of supported validator IDs that can be delegated to\n uint256[] public supportedValidators;\n\n /// @notice Default validator id to deposit to\n uint256 public defaultValidatorId;\n\n struct WithdrawRequest {\n uint256 validatorId;\n uint256 undelegatedAmount;\n uint256 timestamp;\n }\n /// @notice Mapping of withdrawIds to validatorIds and undelegatedAmounts\n mapping(uint256 => WithdrawRequest) public withdrawals;\n\n /// @notice Address of the registrator - allowed to register, exit and remove validators\n address public validatorRegistrator;\n\n // For future use\n uint256[44] private __gap;\n\n event Delegated(uint256 indexed validatorId, uint256 delegatedAmount);\n event Undelegated(\n uint256 indexed withdrawId,\n uint256 indexed validatorId,\n uint256 undelegatedAmount\n );\n event Withdrawn(\n uint256 indexed withdrawId,\n uint256 indexed validatorId,\n uint256 undelegatedAmount,\n uint256 withdrawnAmount\n );\n event RegistratorChanged(address indexed newAddress);\n event SupportedValidator(uint256 indexed validatorId);\n event UnsupportedValidator(uint256 indexed validatorId);\n event DefaultValidatorIdChanged(uint256 indexed validatorId);\n\n /// @dev Throws if called by any account other than the Registrator or Strategist\n modifier onlyRegistratorOrStrategist() {\n require(\n msg.sender == validatorRegistrator ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Registrator or Strategist\"\n );\n _;\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wrappedSonic,\n address _sfc\n ) InitializableAbstractStrategy(_baseConfig) {\n wrappedSonic = _wrappedSonic;\n sfc = ISFC(_sfc);\n }\n\n function initialize() external virtual onlyGovernor initializer {\n address[] memory rewardTokens = new address[](0);\n address[] memory assets = new address[](1);\n address[] memory pTokens = new address[](1);\n\n assets[0] = address(wrappedSonic);\n pTokens[0] = address(platformAddress);\n\n InitializableAbstractStrategy._initialize(\n rewardTokens,\n assets,\n pTokens\n );\n }\n\n /// @notice Returns the total value of Sonic (S) that is delegated validators.\n /// Wrapped Sonic (wS) deposits that are still to be delegated and any undelegated amounts\n /// still pending a withdrawal.\n /// @param _asset Address of Wrapped Sonic (wS) token\n /// @return balance Total value managed by the strategy\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n\n // add the Wrapped Sonic (wS) in the strategy from deposits that are still to be delegated\n // and any undelegated amounts still pending a withdrawal\n balance =\n IERC20(wrappedSonic).balanceOf(address(this)) +\n pendingWithdrawals;\n\n // For each supported validator, get the staked amount and pending rewards\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; i++) {\n uint256 validator = supportedValidators[i];\n balance += sfc.getStake(address(this), validator);\n balance += sfc.pendingRewards(address(this), validator);\n }\n }\n\n /**\n * @dev Delegate from this strategy to a specific Sonic validator. Called\n * automatically on asset deposit\n * @param _amount the amount of Sonic (S) to delegate.\n */\n function _delegate(uint256 _amount) internal {\n require(\n isSupportedValidator(defaultValidatorId),\n \"Validator not supported\"\n );\n\n // unwrap Wrapped Sonic (wS) to native Sonic (S)\n IWrappedSonic(wrappedSonic).withdraw(_amount);\n\n //slither-disable-next-line arbitrary-send-eth\n sfc.delegate{ value: _amount }(defaultValidatorId);\n\n emit Delegated(defaultValidatorId, _amount);\n }\n\n /**\n * @notice Undelegate from a specific Sonic validator.\n * This needs to be followed by a `withdrawFromSFC` two weeks later.\n * @param _validatorId The Sonic validator ID to undelegate from.\n * @param _undelegateAmount the amount of Sonic (S) to undelegate.\n * @return withdrawId The unique ID of the withdrawal request.\n */\n function undelegate(uint256 _validatorId, uint256 _undelegateAmount)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n returns (uint256 withdrawId)\n {\n withdrawId = _undelegate(_validatorId, _undelegateAmount);\n }\n\n function _undelegate(uint256 _validatorId, uint256 _undelegateAmount)\n internal\n returns (uint256 withdrawId)\n {\n // Can still undelegate even if the validator is no longer supported\n require(_undelegateAmount > 0, \"Must undelegate something\");\n\n uint256 amountDelegated = sfc.getStake(address(this), _validatorId);\n require(\n _undelegateAmount <= amountDelegated,\n \"Insufficient delegation\"\n );\n\n withdrawId = nextWithdrawId++;\n\n withdrawals[withdrawId] = WithdrawRequest(\n _validatorId,\n _undelegateAmount,\n block.timestamp\n );\n pendingWithdrawals += _undelegateAmount;\n\n sfc.undelegate(_validatorId, withdrawId, _undelegateAmount);\n\n emit Undelegated(withdrawId, _validatorId, _undelegateAmount);\n }\n\n /**\n * @notice Withdraw native S from a previously undelegated validator.\n * The native S is wrapped wS and transferred to the Vault.\n * @param _withdrawId The unique withdraw ID used to `undelegate`\n * @return withdrawnAmount The amount of Sonic (S) withdrawn.\n * This can be less than the undelegated amount in the event of slashing.\n */\n function withdrawFromSFC(uint256 _withdrawId)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n returns (uint256 withdrawnAmount)\n {\n require(_withdrawId < nextWithdrawId, \"Invalid withdrawId\");\n\n // Can still withdraw even if the validator is no longer supported\n // Load the withdrawal from storage into memory\n WithdrawRequest memory withdrawal = withdrawals[_withdrawId];\n require(!isWithdrawnFromSFC(_withdrawId), \"Already withdrawn\");\n\n withdrawals[_withdrawId].undelegatedAmount = 0;\n pendingWithdrawals -= withdrawal.undelegatedAmount;\n\n uint256 sBalanceBefore = address(this).balance;\n\n // Try to withdraw from SFC\n try sfc.withdraw(withdrawal.validatorId, _withdrawId) {\n // continue below\n } catch (bytes memory err) {\n bytes4 errorSelector = bytes4(err);\n\n // If the validator has been fully slashed, SFC's withdraw function will\n // revert with a StakeIsFullySlashed custom error.\n if (errorSelector == ISFC.StakeIsFullySlashed.selector) {\n // The validator was fully slashed, so all the delegated amounts were lost.\n // Will swallow the error as we still want to update the\n // withdrawals and pendingWithdrawals storage variables.\n\n // The return param defaults to zero but lets set it explicitly so it's clear\n withdrawnAmount = 0;\n\n emit Withdrawn(\n _withdrawId,\n withdrawal.validatorId,\n withdrawal.undelegatedAmount,\n withdrawnAmount\n );\n\n // Exit here as there is nothing to transfer to the Vault\n return withdrawnAmount;\n } else {\n // Bubble up any other SFC custom errors.\n // Inline assembly is currently the only way to generically rethrow the exact same custom error\n // from the raw bytes err in a catch block while preserving its original selector and parameters.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n revert(add(32, err), mload(err))\n }\n }\n }\n\n // Set return parameter\n withdrawnAmount = address(this).balance - sBalanceBefore;\n\n // Wrap Sonic (S) to Wrapped Sonic (wS)\n IWrappedSonic(wrappedSonic).deposit{ value: withdrawnAmount }();\n\n // Transfer the Wrapped Sonic (wS) to the Vault\n _withdraw(vaultAddress, wrappedSonic, withdrawnAmount);\n\n // withdrawal.undelegatedAmount & withdrawnAmount can differ in case of slashing\n emit Withdrawn(\n _withdrawId,\n withdrawal.validatorId,\n withdrawal.undelegatedAmount,\n withdrawnAmount\n );\n }\n\n /// @notice returns a bool whether a withdrawalId has already been withdrawn or not\n /// @param _withdrawId The unique withdraw ID used to `undelegate`\n function isWithdrawnFromSFC(uint256 _withdrawId)\n public\n view\n returns (bool)\n {\n WithdrawRequest memory withdrawal = withdrawals[_withdrawId];\n require(withdrawal.validatorId > 0, \"Invalid withdrawId\");\n return withdrawal.undelegatedAmount == 0;\n }\n\n /**\n * @notice Restake any pending validator rewards for all supported validators\n * @param _validatorIds List of Sonic validator IDs to restake rewards\n */\n function restakeRewards(uint256[] calldata _validatorIds)\n external\n nonReentrant\n {\n for (uint256 i = 0; i < _validatorIds.length; ++i) {\n require(\n isSupportedValidator(_validatorIds[i]),\n \"Validator not supported\"\n );\n\n uint256 rewards = sfc.pendingRewards(\n address(this),\n _validatorIds[i]\n );\n\n if (rewards > 0) {\n sfc.restakeRewards(_validatorIds[i]);\n }\n }\n\n // The SFC contract will emit Delegated and RestakedRewards events.\n // The checkBalance function should not change as the pending rewards will moved to the staked amount.\n }\n\n /**\n * @notice Claim any pending rewards from validators\n * @param _validatorIds List of Sonic validator IDs to claim rewards\n */\n function collectRewards(uint256[] calldata _validatorIds)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n {\n uint256 sBalanceBefore = address(this).balance;\n\n for (uint256 i = 0; i < _validatorIds.length; ++i) {\n uint256 rewards = sfc.pendingRewards(\n address(this),\n _validatorIds[i]\n );\n\n if (rewards > 0) {\n // The SFC contract will emit ClaimedRewards(delegator (this), validatorId, rewards)\n sfc.claimRewards(_validatorIds[i]);\n }\n }\n\n uint256 rewardsAmount = address(this).balance - sBalanceBefore;\n\n // Wrap Sonic (S) to Wrapped Sonic (wS)\n IWrappedSonic(wrappedSonic).deposit{ value: rewardsAmount }();\n\n // Transfer the Wrapped Sonic (wS) to the Vault\n _withdraw(vaultAddress, wrappedSonic, rewardsAmount);\n }\n\n /**\n * @notice To receive native S from SFC and Wrapped Sonic (wS)\n *\n * @dev This does not prevent donating S tokens to the contract\n * as wrappedSonic has a `withdrawTo` function where a third party\n * owner of wrappedSonic can withdraw to this contract.\n */\n receive() external payable {\n require(\n msg.sender == address(sfc) || msg.sender == wrappedSonic,\n \"S not from allowed contracts\"\n );\n }\n\n /***************************************\n Admin functions\n ****************************************/\n\n /// @notice Set the address of the Registrator which can undelegate, withdraw and collect rewards\n /// @param _validatorRegistrator The address of the Registrator\n function setRegistrator(address _validatorRegistrator)\n external\n onlyGovernor\n {\n validatorRegistrator = _validatorRegistrator;\n emit RegistratorChanged(_validatorRegistrator);\n }\n\n /// @notice Set the default validatorId to delegate to on deposit\n /// @param _validatorId The validator identifier. eg 18\n function setDefaultValidatorId(uint256 _validatorId)\n external\n onlyRegistratorOrStrategist\n {\n require(isSupportedValidator(_validatorId), \"Validator not supported\");\n defaultValidatorId = _validatorId;\n emit DefaultValidatorIdChanged(_validatorId);\n }\n\n /// @notice Allows a validator to be delegated to by the Registrator\n /// @param _validatorId The validator identifier. eg 18\n function supportValidator(uint256 _validatorId) external onlyGovernor {\n require(\n !isSupportedValidator(_validatorId),\n \"Validator already supported\"\n );\n\n supportedValidators.push(_validatorId);\n\n emit SupportedValidator(_validatorId);\n }\n\n /// @notice Removes a validator from the supported list.\n /// Unsupported validators can still be undelegated from, withdrawn from and rewards collected.\n /// @param _validatorId The validator identifier. eg 18\n function unsupportValidator(uint256 _validatorId) external onlyGovernor {\n require(isSupportedValidator(_validatorId), \"Validator not supported\");\n\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; ++i) {\n if (supportedValidators[i] == _validatorId) {\n supportedValidators[i] = supportedValidators[validatorLen - 1];\n supportedValidators.pop();\n break;\n }\n }\n\n uint256 stake = sfc.getStake(address(this), _validatorId);\n\n // undelegate if validator still has funds staked\n if (stake > 0) {\n _undelegate(_validatorId, stake);\n }\n emit UnsupportedValidator(_validatorId);\n }\n\n /// @notice Returns the length of the supportedValidators array\n function supportedValidatorsLength() external view returns (uint256) {\n return supportedValidators.length;\n }\n\n /// @notice Returns whether a validator is supported by this strategy\n /// @param _validatorId The validator identifier\n function isSupportedValidator(uint256 _validatorId)\n public\n view\n returns (bool)\n {\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; ++i) {\n if (supportedValidators[i] == _validatorId) {\n return true;\n }\n }\n return false;\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal virtual;\n}\n" + }, + "contracts/strategies/VaultValueChecker.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IOUSD } from \"../interfaces/IOUSD.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract VaultValueChecker {\n IVault public immutable vault;\n IOUSD public immutable ousd;\n // Snapshot expiration time in seconds.\n // Used to prevent accidental use of an old snapshot, but\n // is not zero to allow easy testing of strategist actions in fork testing\n uint256 constant SNAPSHOT_EXPIRES = 5 * 60;\n\n struct Snapshot {\n uint256 vaultValue;\n uint256 totalSupply;\n uint256 time;\n }\n // By doing per user snapshots, we prevent a reentrancy attack\n // from a third party that updates the snapshot in the middle\n // of an allocation process\n\n mapping(address => Snapshot) public snapshots;\n\n constructor(address _vault, address _ousd) {\n vault = IVault(_vault);\n ousd = IOUSD(_ousd);\n }\n\n function takeSnapshot() external {\n snapshots[msg.sender] = Snapshot({\n vaultValue: vault.totalValue(),\n totalSupply: ousd.totalSupply(),\n time: block.timestamp\n });\n }\n\n function checkDelta(\n int256 expectedProfit,\n int256 profitVariance,\n int256 expectedVaultChange,\n int256 vaultChangeVariance\n ) external {\n // Intentionaly not view so that this method shows up in TX builders\n Snapshot memory snapshot = snapshots[msg.sender];\n int256 vaultChange = toInt256(vault.totalValue()) -\n toInt256(snapshot.vaultValue);\n int256 supplyChange = toInt256(ousd.totalSupply()) -\n toInt256(snapshot.totalSupply);\n int256 profit = vaultChange - supplyChange;\n\n require(\n snapshot.time >= block.timestamp - SNAPSHOT_EXPIRES,\n \"Snapshot too old\"\n );\n require(snapshot.time <= block.timestamp, \"Snapshot too new\");\n require(profit >= expectedProfit - profitVariance, \"Profit too low\");\n require(profit <= expectedProfit + profitVariance, \"Profit too high\");\n require(\n vaultChange >= expectedVaultChange - vaultChangeVariance,\n \"Vault value change too low\"\n );\n require(\n vaultChange <= expectedVaultChange + vaultChangeVariance,\n \"Vault value change too high\"\n );\n }\n\n function toInt256(uint256 value) internal pure returns (int256) {\n // From openzeppelin math/SafeCast.sol\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(\n value <= uint256(type(int256).max),\n \"SafeCast: value doesn't fit in an int256\"\n );\n return int256(value);\n }\n}\n\ncontract OETHVaultValueChecker is VaultValueChecker {\n constructor(address _vault, address _ousd)\n VaultValueChecker(_vault, _ousd)\n {}\n}\n" + }, + "contracts/swapper/Swapper1InchV5.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @notice 1Inch Pathfinder V5 implementation of the general ISwapper interface.\n * @author Origin Protocol Inc\n * @dev It is possible that dust token amounts are left in this contract after a swap.\n * This can happen with some tokens that don't send the full transfer amount.\n * These dust amounts can build up over time and be used by anyone who calls the `swap` function.\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IAggregationExecutor, IOneInchRouter, SwapDescription } from \"../interfaces/IOneInch.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\n\ncontract Swapper1InchV5 is ISwapper {\n using SafeERC20 for IERC20;\n\n /// @notice 1Inch router contract to give allowance to perform swaps\n address public constant SWAP_ROUTER =\n 0x1111111254EEB25477B68fb85Ed929f73A960582;\n\n // swap(address,(address,address,address,address,uint256,uint256,uint256),bytes,bytes)\n bytes4 internal constant SWAP_SELECTOR = 0x12aa3caf;\n // unoswapTo(address,address,uint256,uint256,uint256[])\n bytes4 internal constant UNISWAP_SELECTOR = 0xf78dc253;\n // uniswapV3SwapTo(address,uint256,uint256,uint256[])\n bytes4 internal constant UNISWAPV3_SELECTOR = 0xbc80f1a8;\n\n /**\n * @notice Strategist swaps assets sitting in the contract of the `assetHolder`.\n * @param _fromAsset The token address of the asset being sold by the vault.\n * @param _toAsset The token address of the asset being purchased by the vault.\n * @param _fromAssetAmount The amount of assets being sold by the vault.\n * @param _minToAssetAmount The minimum amount of assets to be purchased.\n * @param _data RLP encoded executer address and bytes data. This is re-encoded tx.data from 1Inch swap API\n */\n function swap(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n ) external override returns (uint256 toAssetAmount) {\n // Decode the function selector from the RLP encoded _data param\n bytes4 swapSelector = bytes4(_data[:4]);\n\n if (swapSelector == SWAP_SELECTOR) {\n // Decode the executer address and data from the RLP encoded _data param\n (, address executer, bytes memory executerData) = abi.decode(\n _data,\n (bytes4, address, bytes)\n );\n SwapDescription memory swapDesc = SwapDescription({\n srcToken: IERC20(_fromAsset),\n dstToken: IERC20(_toAsset),\n srcReceiver: payable(executer),\n dstReceiver: payable(msg.sender),\n amount: _fromAssetAmount,\n minReturnAmount: _minToAssetAmount,\n flags: 4 // 1st bit _PARTIAL_FILL, 2nd bit _REQUIRES_EXTRA_ETH, 3rd bit _SHOULD_CLAIM\n });\n (toAssetAmount, ) = IOneInchRouter(SWAP_ROUTER).swap(\n IAggregationExecutor(executer),\n swapDesc,\n hex\"\",\n executerData\n );\n } else if (swapSelector == UNISWAP_SELECTOR) {\n // Need to get the Uniswap pools data from the _data param\n (, uint256[] memory pools) = abi.decode(_data, (bytes4, uint256[]));\n toAssetAmount = IOneInchRouter(SWAP_ROUTER).unoswapTo(\n payable(msg.sender),\n IERC20(_fromAsset),\n _fromAssetAmount,\n _minToAssetAmount,\n pools\n );\n } else if (swapSelector == UNISWAPV3_SELECTOR) {\n // Need to get the Uniswap pools data from the _data param\n // slither-disable-next-line uninitialized-storage\n (, uint256[] memory pools) = abi.decode(_data, (bytes4, uint256[]));\n toAssetAmount = IOneInchRouter(SWAP_ROUTER).uniswapV3SwapTo(\n payable(msg.sender),\n _fromAssetAmount,\n _minToAssetAmount,\n pools\n );\n } else {\n revert(\"Unsupported swap function\");\n }\n }\n\n /**\n * @notice Approve assets for swapping.\n * @param _assets Array of token addresses to approve.\n * @dev unlimited approval is used as no tokens sit in this contract outside a transaction.\n */\n function approveAssets(address[] memory _assets) external {\n for (uint256 i = 0; i < _assets.length; ++i) {\n // Give the 1Inch router approval to transfer unlimited assets\n IERC20(_assets[i]).safeApprove(SWAP_ROUTER, type(uint256).max);\n }\n }\n}\n" + }, + "contracts/token/BridgedWOETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\n\ncontract BridgedWOETH is\n Governable,\n AccessControlEnumerable,\n Initializable,\n ERC20\n{\n bytes32 public constant MINTER_ROLE = keccak256(\"MINTER_ROLE\");\n bytes32 public constant BURNER_ROLE = keccak256(\"BURNER_ROLE\");\n\n constructor() ERC20(\"Wrapped OETH\", \"wOETH\") {\n // Nobody owns the implementation\n _setGovernor(address(0));\n }\n\n /**\n * @dev Initialize the proxy and set the Governor\n */\n function initialize() external initializer {\n // Governor can grant Minter/Burner roles\n _setupRole(DEFAULT_ADMIN_ROLE, _governor());\n }\n\n /**\n * @dev Mint tokens for `account`\n * @param account Address to mint tokens for\n * @param amount Amount of tokens to mint\n */\n function mint(address account, uint256 amount)\n external\n onlyRole(MINTER_ROLE)\n nonReentrant\n {\n _mint(account, amount);\n }\n\n /**\n * @dev Burns tokens from `account`\n * @param account Address to burn tokens from\n * @param amount Amount of tokens to burn\n */\n function burn(address account, uint256 amount)\n external\n onlyRole(BURNER_ROLE)\n nonReentrant\n {\n _burn(account, amount);\n }\n\n /**\n * @dev Burns tokens from `msg.sender`\n * @param amount Amount of tokens to burn\n */\n function burn(uint256 amount) external onlyRole(BURNER_ROLE) nonReentrant {\n _burn(msg.sender, amount);\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return \"Wrapped OETH\";\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return \"wOETH\";\n }\n\n /**\n * @dev Returns the decimals of the token\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETH is OUSD {\n function symbol() external pure override returns (string memory) {\n return \"OETH\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Origin Ether\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETHBase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBase is OUSD {\n constructor() {\n // Nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n function symbol() external pure override returns (string memory) {\n return \"superOETHb\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Super OETH\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETHPlume.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title Super OETH (Plume) Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETHPlume is OUSD {\n constructor() {\n // Nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n function symbol() external pure override returns (string memory) {\n return \"superOETHp\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Super OETH\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OSonic.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title Origin Sonic (OS) token on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonic is OUSD {\n function symbol() external pure override returns (string memory) {\n return \"OS\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Origin Sonic\";\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/token/OUSDResolutionUpgrade.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract OUSDResolutionUpgrade {\n enum RebaseOptions {\n NotSet,\n OptOut,\n OptIn\n }\n\n // From Initializable\n bool private initialized;\n bool private initializing;\n uint256[50] private ______igap;\n\n // From InitializableERC20Detailed\n uint256[100] private _____ugap;\n string private _name;\n string private _symbol;\n uint8 private _decimals;\n\n // From OUSD\n uint256 private constant MAX_SUPPLY = ~uint128(0); // (2^128) - 1\n uint256 public _totalSupply;\n mapping(address => mapping(address => uint256)) private _allowances;\n address public vaultAddress = address(0);\n mapping(address => uint256) private _creditBalances;\n uint256 private _rebasingCredits;\n uint256 private _rebasingCreditsPerToken;\n uint256 public nonRebasingSupply;\n mapping(address => uint256) public nonRebasingCreditsPerToken;\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) public isUpgraded;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() public view returns (uint256) {\n return _rebasingCreditsPerToken / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCredits() public view returns (uint256) {\n return _rebasingCredits / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() public view returns (uint256) {\n return _rebasingCreditsPerToken;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() public view returns (uint256) {\n return _rebasingCredits;\n }\n\n function upgradeGlobals() external {\n require(isUpgraded[address(0)] == 0, \"Globals already upgraded\");\n require(_rebasingCredits > 0, \"Sanity _rebasingCredits\");\n require(\n _rebasingCreditsPerToken > 0,\n \"Sanity _rebasingCreditsPerToken\"\n );\n isUpgraded[address(0)] = 1;\n _rebasingCredits = _rebasingCredits * RESOLUTION_INCREASE;\n _rebasingCreditsPerToken =\n _rebasingCreditsPerToken *\n RESOLUTION_INCREASE;\n }\n\n function upgradeAccounts(address[] calldata accounts) external {\n for (uint256 i = 0; i < accounts.length; i++) {\n address account = accounts[i];\n require(account != address(0), \"Reserved\");\n require(isUpgraded[account] == 0, \"Account already upgraded\");\n isUpgraded[account] = 1;\n\n // Handle special for non-rebasing accounts\n uint256 nrc = nonRebasingCreditsPerToken[account];\n if (nrc > 0) {\n require(nrc < 1e18, \"Account already highres\");\n nonRebasingCreditsPerToken[account] = nrc * RESOLUTION_INCREASE;\n }\n // Upgrade balance\n uint256 balance = _creditBalances[account];\n require(balance > 0, \"Will not upgrade zero balance\");\n _creditBalances[account] = balance * RESOLUTION_INCREASE;\n }\n }\n\n function creditsBalanceOfHighres(address _account)\n public\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n _creditBalances[_account],\n _creditsPerToken(_account),\n isUpgraded[_account] == 1\n );\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n if (nonRebasingCreditsPerToken[_account] != 0) {\n return nonRebasingCreditsPerToken[_account];\n } else {\n return _rebasingCreditsPerToken;\n }\n }\n}\n" + }, + "contracts/token/WOETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC4626 } from \"../../lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { OETH } from \"./OETH.sol\";\n\n/**\n * @title Wrapped OETH Token Contract\n * @author Origin Protocol Inc\n *\n * @dev An important capability of this contract is that it isn't susceptible to changes of the\n * exchange rate of WOETH/OETH if/when someone sends the underlying asset (OETH) to the contract.\n * If OETH weren't rebasing this could be achieved by solely tracking the ERC20 transfers of the OETH\n * token on mint, deposit, redeem, withdraw. The issue is that OETH is rebasing and OETH balances\n * will change when the token rebases.\n * For that reason the contract logic checks the actual underlying OETH token balance only once\n * (either on a fresh contract creation or upgrade) and considering the WOETH supply and\n * rebasingCreditsPerToken calculates the _adjuster. Once the adjuster is calculated any donations\n * to the contract are ignored. The totalSupply (instead of querying OETH balance) works off of\n * adjuster the current WOETH supply and rebasingCreditsPerToken. This makes WOETH value accrual\n * completely follow OETH's value accrual.\n * WOETH is safe to use in lending markets as the VualtCore's _rebase contains safeguards preventing\n * any sudden large rebases.\n */\n\ncontract WOETH is ERC4626, Governable, Initializable {\n using SafeERC20 for IERC20;\n /* This is a 1e27 adjustment constant that expresses the difference in exchange rate between\n * OETH's rebase since inception (expressed with rebasingCreditsPerToken) and WOETH to OETH\n * conversion.\n *\n * If WOETH and OETH are deployed at the same time, the value of adjuster is a neutral 1e27\n */\n uint256 public adjuster;\n uint256[49] private __gap;\n\n // no need to set ERC20 name and symbol since they are overridden in WOETH & WOETHBase\n constructor(ERC20 underlying_) ERC20(\"\", \"\") ERC4626(underlying_) {}\n\n /**\n * @notice Enable OETH rebasing for this contract\n */\n function initialize() external onlyGovernor initializer {\n OETH(address(asset())).rebaseOptIn();\n\n initialize2();\n }\n\n /**\n * @notice secondary initializer that newly deployed contracts will execute as part\n * of primary initialize function and the existing contracts will have it called\n * as a governance operation.\n */\n function initialize2() public onlyGovernor {\n require(adjuster == 0, \"Initialize2 already called\");\n\n if (totalSupply() == 0) {\n adjuster = 1e27;\n } else {\n adjuster =\n (rebasingCreditsPerTokenHighres() *\n ERC20(asset()).balanceOf(address(this))) /\n totalSupply();\n }\n }\n\n function name()\n public\n view\n virtual\n override(ERC20, IERC20Metadata)\n returns (string memory)\n {\n return \"Wrapped OETH\";\n }\n\n function symbol()\n public\n view\n virtual\n override(ERC20, IERC20Metadata)\n returns (string memory)\n {\n return \"wOETH\";\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends. Cannot transfer OETH\n * @param asset_ Address for the asset\n * @param amount_ Amount of the asset to transfer\n */\n function transferToken(address asset_, uint256 amount_)\n external\n onlyGovernor\n {\n require(asset_ != address(asset()), \"Cannot collect core asset\");\n IERC20(asset_).safeTransfer(governor(), amount_);\n }\n\n /// @inheritdoc ERC4626\n function convertToShares(uint256 assets)\n public\n view\n virtual\n override\n returns (uint256 shares)\n {\n return (assets * rebasingCreditsPerTokenHighres()) / adjuster;\n }\n\n /// @inheritdoc ERC4626\n function convertToAssets(uint256 shares)\n public\n view\n virtual\n override\n returns (uint256 assets)\n {\n return (shares * adjuster) / rebasingCreditsPerTokenHighres();\n }\n\n /// @inheritdoc ERC4626\n function totalAssets() public view override returns (uint256) {\n return (totalSupply() * adjuster) / rebasingCreditsPerTokenHighres();\n }\n\n function rebasingCreditsPerTokenHighres() internal view returns (uint256) {\n return OETH(asset()).rebasingCreditsPerTokenHighres();\n }\n}\n" + }, + "contracts/token/WOETHBase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WOETH } from \"./WOETH.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\n\ncontract WOETHBase is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name() public view virtual override returns (string memory) {\n return \"Wrapped Super OETH\";\n }\n\n function symbol() public view virtual override returns (string memory) {\n return \"wsuperOETHb\";\n }\n}\n" + }, + "contracts/token/WOETHPlume.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WOETH } from \"./WOETH.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @title wOETH (Plume) Token Contract\n * @author Origin Protocol Inc\n */\n\ncontract WOETHPlume is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name() public view virtual override returns (string memory) {\n return \"Wrapped Super OETH\";\n }\n\n function symbol() public view virtual override returns (string memory) {\n return \"wsuperOETHp\";\n }\n}\n" + }, + "contracts/token/WOSonic.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { WOETH } from \"./WOETH.sol\";\n\n/**\n * @title Wrapped Origin Sonic (wOS) token on Sonic\n * @author Origin Protocol Inc\n */\ncontract WOSonic is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"Wrapped OS\";\n }\n\n function symbol()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"wOS\";\n }\n}\n" + }, + "contracts/token/WrappedOusd.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { WOETH } from \"./WOETH.sol\";\n\n/**\n * @title Wrapped OUSD Token Contract\n * @author Origin Protocol Inc\n */\ncontract WrappedOusd is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"Wrapped OUSD\";\n }\n\n function symbol()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"WOUSD\";\n }\n}\n" + }, + "contracts/utils/AerodromeAMOQuoter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.7;\n\nimport { ICLPool } from \"../interfaces/aerodrome/ICLPool.sol\";\nimport { IQuoterV2 } from \"../interfaces/aerodrome/IQuoterV2.sol\";\nimport { IAMOStrategy } from \"../interfaces/aerodrome/IAMOStrategy.sol\";\n\n/// @title QuoterHelper\n/// @author Origin Protocol\n/// @notice Helper for Aerodrome AMO Quoter, as `_quoteAmountToSwapBeforeRebalance` use try/catch method and\n/// this can only be used when calling external contracts.\ncontract QuoterHelper {\n ////////////////////////////////////////////////////////////////\n /// --- STRUCTS & ENUMS\n ////////////////////////////////////////////////////////////////\n enum RevertReasons {\n DefaultStatus,\n RebalanceOutOfBounds,\n NotInExpectedTickRange,\n NotEnoughWethForSwap,\n NotEnoughWethLiquidity,\n UnexpectedError,\n Found,\n NotFound\n }\n\n struct RebalanceStatus {\n RevertReasons reason;\n uint256 currentPoolWETHShare; // Case 1\n uint256 allowedWETHShareStart; // Case 1\n uint256 allowedWETHShareEnd; // Case 1\n int24 currentTick; // Case 2\n uint256 balanceWETH; // Case 3\n uint256 amountWETH; // Case 3\n string revertMessage;\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTANT & IMMUTABLE\n ////////////////////////////////////////////////////////////////\n uint256 public constant BINARY_MIN_AMOUNT = 1 wei;\n\n uint256 public constant BINARY_MAX_ITERATIONS = 40;\n uint256 public constant PERCENTAGE_BASE = 1e18; // 100%\n uint256 public constant ALLOWED_VARIANCE_PERCENTAGE = 1e15; // 0.1%\n\n ////////////////////////////////////////////////////////////////\n /// --- VARIABLES STORAGE\n ////////////////////////////////////////////////////////////////\n ICLPool public immutable clPool;\n IQuoterV2 public immutable quoterV2;\n IAMOStrategy public immutable strategy;\n\n address public originalGovernor;\n\n ////////////////////////////////////////////////////////////////\n /// --- ERRORS & EVENTS\n ////////////////////////////////////////////////////////////////\n error UnexpectedError(string message);\n error OutOfIterations(uint256 iterations);\n error ValidAmount(\n uint256 amount,\n uint256 iterations,\n bool swapWETHForOETHB\n );\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////////////////\n constructor(IAMOStrategy _strategy, IQuoterV2 _quoterV2) {\n strategy = _strategy;\n quoterV2 = _quoterV2;\n clPool = _strategy.clPool();\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- FUNCTIONS\n ////////////////////////////////////////////////////////////////\n /// @notice This call can only end with a revert.\n function getAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) public {\n if (\n overrideBottomWethShare != type(uint256).max ||\n overrideTopWethShare != type(uint256).max\n ) {\n // Current values\n uint256 shareStart = strategy.allowedWethShareStart();\n uint256 shareEnd = strategy.allowedWethShareEnd();\n\n // Override values\n if (overrideBottomWethShare != type(uint256).max) {\n shareStart = overrideBottomWethShare;\n }\n if (overrideTopWethShare != type(uint256).max) {\n shareEnd = overrideTopWethShare;\n }\n\n strategy.setAllowedPoolWethShareInterval(shareStart, shareEnd);\n }\n\n uint256 iterations = 0;\n uint256 low = BINARY_MIN_AMOUNT;\n uint256 high;\n (high, ) = strategy.getPositionPrincipal();\n int24 lowerTick = strategy.lowerTick();\n int24 upperTick = strategy.upperTick();\n bool swapWETHForOETHB = getSwapDirectionForRebalance();\n\n while (low <= high && iterations < BINARY_MAX_ITERATIONS) {\n uint256 mid = (low + high) / 2;\n\n RebalanceStatus memory status = getRebalanceStatus(\n mid,\n swapWETHForOETHB\n );\n\n // Best case, we found the `amount` that will reach the target pool share!\n // We can revert with the amount and the number of iterations\n if (status.reason == RevertReasons.Found) {\n revert ValidAmount(mid, iterations, swapWETHForOETHB);\n }\n\n // If the rebalance failed then we should try to change the amount.\n // We will handle all possible revert reasons here.\n\n // Case 1: Rebalance out of bounds\n // If the pool is out of bounds, we need to adjust the amount to reach the target pool share\n if (status.reason == RevertReasons.RebalanceOutOfBounds) {\n // If the current pool share is less than the target pool share, we need to increase the amount\n if (\n swapWETHForOETHB\n ? status.currentPoolWETHShare <\n status.allowedWETHShareStart\n : status.currentPoolWETHShare >\n status.allowedWETHShareEnd\n ) {\n low = mid + 1;\n }\n // Else we need to decrease the amount\n else {\n high = mid;\n }\n }\n\n // Case 2: Not in expected tick range\n // If the pool is not in the expected tick range, we need to adjust the amount\n // to reach the target pool share\n if (status.reason == RevertReasons.NotInExpectedTickRange) {\n // If we are buying OETHb and the current tick is greater than the lower tick,\n //we need to increase the amount in order to continue to push price down.\n // If we are selling OETHb and the current tick is less than the upper tick,\n // we need to increase the amount in order to continue to push price up.\n if (\n swapWETHForOETHB\n ? status.currentTick > lowerTick\n : status.currentTick < upperTick\n ) {\n low = mid + 1;\n }\n // Else we need to decrease the amount\n else {\n high = mid;\n }\n }\n\n // Case 3: Not enough WETH for swap\n // If we don't have enough WETH to swap, we need to decrease the amount\n // This error can happen, when initial value of mid is too high, so we need to decrease it\n if (status.reason == RevertReasons.NotEnoughWethForSwap) {\n high = mid;\n }\n\n // Case 4: Not enough WETH liquidity\n // If we don't have enough WETH liquidity\n // Revert for the moment, we need to improve this\n if (status.reason == RevertReasons.NotEnoughWethLiquidity) {\n revert(\"Quoter: Not enough WETH liquidity\");\n }\n\n // Case 5: Unexpected error\n // Worst case, it reverted with an unexpected error.\n if (status.reason == RevertReasons.UnexpectedError) {\n revert UnexpectedError(status.revertMessage);\n }\n\n iterations++;\n }\n\n // Case 6: Out of iterations\n // If we didn't find the amount after the max iterations, we need to revert.\n revert OutOfIterations(iterations);\n }\n\n /// @notice Get the status of the rebalance\n /// @param amount The amount of token to swap\n /// @param swapWETH True if we need to swap WETH for OETHb, false otherwise\n /// @return status The status of the rebalance\n function getRebalanceStatus(uint256 amount, bool swapWETH)\n public\n returns (RebalanceStatus memory status)\n {\n try strategy.rebalance(amount, swapWETH, 0) {\n status.reason = RevertReasons.Found;\n return status;\n } catch Error(string memory reason) {\n status.reason = RevertReasons.UnexpectedError;\n status.revertMessage = reason;\n } catch (bytes memory reason) {\n bytes4 receivedSelector = bytes4(reason);\n\n // Case 1: Rebalance out of bounds\n if (\n receivedSelector ==\n IAMOStrategy.PoolRebalanceOutOfBounds.selector\n ) {\n uint256 currentPoolWETHShare;\n uint256 allowedWETHShareStart;\n uint256 allowedWETHShareEnd;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n currentPoolWETHShare := mload(add(reason, 0x24))\n allowedWETHShareStart := mload(add(reason, 0x44))\n allowedWETHShareEnd := mload(add(reason, 0x64))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.RebalanceOutOfBounds,\n currentPoolWETHShare: currentPoolWETHShare,\n allowedWETHShareStart: allowedWETHShareStart,\n allowedWETHShareEnd: allowedWETHShareEnd,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 2: Not in expected tick range\n if (\n receivedSelector ==\n IAMOStrategy.OutsideExpectedTickRange.selector\n ) {\n int24 currentTick;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n currentTick := mload(add(reason, 0x24))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.NotInExpectedTickRange,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: currentTick,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 3: Not enough WETH for swap\n if (\n receivedSelector == IAMOStrategy.NotEnoughWethForSwap.selector\n ) {\n uint256 balanceWETH;\n uint256 amountWETH;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n balanceWETH := mload(add(reason, 0x24))\n amountWETH := mload(add(reason, 0x44))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.NotEnoughWethForSwap,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: balanceWETH,\n amountWETH: amountWETH,\n revertMessage: \"\"\n });\n }\n\n // Case 4: Not enough WETH liquidity\n if (\n receivedSelector == IAMOStrategy.NotEnoughWethLiquidity.selector\n ) {\n return\n RebalanceStatus({\n reason: RevertReasons.NotEnoughWethLiquidity,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 5: Unexpected error\n return\n RebalanceStatus({\n reason: RevertReasons.UnexpectedError,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: abi.decode(reason, (string))\n });\n }\n }\n\n /// @notice Get the swap direction to reach the target price before rebalance.\n /// @return bool True if we need to swap WETH for OETHb, false otherwise.\n function getSwapDirectionForRebalance() public view returns (bool) {\n uint160 currentPrice = strategy.getPoolX96Price();\n uint160 ticker0Price = strategy.sqrtRatioX96TickLower();\n uint160 ticker1Price = strategy.sqrtRatioX96TickHigher();\n uint256 allowedWethShareStart = strategy.allowedWethShareStart();\n uint256 allowedWethShareEnd = strategy.allowedWethShareEnd();\n uint160 mid = uint160(allowedWethShareStart + allowedWethShareEnd) / 2;\n // slither-disable-start divide-before-multiply\n uint160 targetPrice = (ticker0Price *\n mid +\n ticker1Price *\n (1 ether - mid)) / 1 ether;\n // slither-disable-end divide-before-multiply\n\n return currentPrice > targetPrice;\n }\n\n // returns total amount in the position principal of the Aerodrome AMO strategy. Needed as a\n // separate function because of the limitation in local variable count in getAmountToSwapToReachPrice\n function getTotalStrategyPosition() internal returns (uint256) {\n (uint256 wethAmount, uint256 oethBalance) = strategy\n .getPositionPrincipal();\n return wethAmount + oethBalance;\n }\n\n /// @notice Get the amount of tokens to swap to reach the target price.\n /// @dev This act like a quoter, i.e. the transaction is not performed.\n /// @dev Because the amount to swap can be largely overestimated, because CLAMM alow partial orders,\n /// i.e. when we ask to swap a very large amount, with a close priceLimite, it will swap only a part of it,\n /// and not revert. So if overestimated amount is to high, use a custom maxAmount to avoid this issue.\n /// @param sqrtPriceTargetX96 The target price to reach.\n /// @return amount The amount of tokens to swap.\n /// @return iterations The number of iterations to find the amount.\n /// @return swapWETHForOETHB True if we need to swap WETH for OETHb, false otherwise.\n /// @return sqrtPriceX96After The price after the swap.\n function getAmountToSwapToReachPrice(uint160 sqrtPriceTargetX96)\n public\n returns (\n uint256,\n uint256,\n bool,\n uint160\n )\n {\n uint256 iterations = 0;\n uint256 low = BINARY_MIN_AMOUNT;\n // high search start is twice the position principle of Aerodrome AMO strategy.\n // should be more than enough\n uint256 high = getTotalStrategyPosition() * 2;\n bool swapWETHForOETHB = getSwapDirection(sqrtPriceTargetX96);\n\n while (low <= high && iterations < BINARY_MAX_ITERATIONS) {\n uint256 mid = (low + high) / 2;\n\n // Call QuoterV2 from SugarHelper\n (uint256 amountOut, uint160 sqrtPriceX96After, , ) = quoterV2\n .quoteExactInputSingle(\n IQuoterV2.QuoteExactInputSingleParams({\n tokenIn: swapWETHForOETHB\n ? clPool.token0()\n : clPool.token1(),\n tokenOut: swapWETHForOETHB\n ? clPool.token1()\n : clPool.token0(),\n amountIn: mid,\n tickSpacing: strategy.tickSpacing(),\n sqrtPriceLimitX96: sqrtPriceTargetX96\n })\n );\n\n if (\n isWithinAllowedVariance(sqrtPriceX96After, sqrtPriceTargetX96)\n ) {\n /** Very important to return `amountOut` instead of `mid` as the first return parameter.\n * The issues was that when quoting we impose a swap price limit (sqrtPriceLimitX96: sqrtPriceTargetX96)\n * and in that case the `amountIn` acts like a maximum amount to swap. And we don't know how much\n * of that amount was actually consumed. For that reason we \"estimate\" it by returning the\n * amountOut since that is only going to be a couple of basis point away from amountIn in the\n * worst cases.\n *\n * Note: we could be returning mid instead of amountOut in cases when those values are only basis\n * points apart (assuming that complete balance of amountIn has been consumed) but that might increase\n * complexity too much in an already complex contract.\n */\n return (\n amountOut,\n iterations,\n swapWETHForOETHB,\n sqrtPriceX96After\n );\n } else if (low == high) {\n // target swap amount not found.\n // might be that \"high\" amount is too low on start\n revert(\"SwapAmountNotFound\");\n } else if (\n swapWETHForOETHB\n ? sqrtPriceX96After > sqrtPriceTargetX96\n : sqrtPriceX96After < sqrtPriceTargetX96\n ) {\n low = mid + 1;\n } else {\n high = mid;\n }\n iterations++;\n }\n\n revert OutOfIterations(iterations);\n }\n\n /// @notice Check if the current price is within the allowed variance in comparison to the target price\n /// @return bool True if the current price is within the allowed variance, false otherwise\n function isWithinAllowedVariance(\n uint160 sqrtPriceCurrentX96,\n uint160 sqrtPriceTargetX96\n ) public view returns (bool) {\n uint160 range = strategy.sqrtRatioX96TickHigher() -\n strategy.sqrtRatioX96TickLower();\n if (sqrtPriceCurrentX96 > sqrtPriceTargetX96) {\n return\n (sqrtPriceCurrentX96 - sqrtPriceTargetX96) <=\n (ALLOWED_VARIANCE_PERCENTAGE * range) / PERCENTAGE_BASE;\n } else {\n return\n (sqrtPriceTargetX96 - sqrtPriceCurrentX96) <=\n (ALLOWED_VARIANCE_PERCENTAGE * range) / PERCENTAGE_BASE;\n }\n }\n\n /// @notice Get the swap direction to reach the target price.\n /// @param sqrtPriceTargetX96 The target price to reach.\n /// @return bool True if we need to swap WETH for OETHb, false otherwise.\n function getSwapDirection(uint160 sqrtPriceTargetX96)\n public\n view\n returns (bool)\n {\n uint160 currentPrice = strategy.getPoolX96Price();\n return currentPrice > sqrtPriceTargetX96;\n }\n\n function claimGovernanceOnAMO() public {\n originalGovernor = strategy.governor();\n strategy.claimGovernance();\n }\n\n function giveBackGovernanceOnAMO() public {\n require(\n originalGovernor != address(0),\n \"Quoter: Original governor not set\"\n );\n strategy.transferGovernance(originalGovernor);\n }\n}\n\n/// @title AerodromeAMOQuoter\n/// @author Origin Protocol\n/// @notice Quoter for Aerodrome AMO\ncontract AerodromeAMOQuoter {\n ////////////////////////////////////////////////////////////////\n /// --- STRUCTS & ENUMS\n ///////////////////////////////////////////////////////////////\n struct Data {\n uint256 amount;\n uint256 iterations;\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- VARIABLES STORAGE\n ////////////////////////////////////////////////////////////////\n QuoterHelper public immutable quoterHelper;\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////////////////\n constructor(address _strategy, address _quoterV2) {\n quoterHelper = new QuoterHelper(\n IAMOStrategy(_strategy),\n IQuoterV2(_quoterV2)\n );\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- ERRORS & EVENTS\n ////////////////////////////////////////////////////////////////\n event ValueFound(uint256 value, uint256 iterations, bool swapWETHForOETHB);\n event ValueFoundBis(\n uint256 value,\n uint256 iterations,\n bool swapWETHForOETHB,\n uint160 sqrtPriceAfterX96\n );\n event ValueNotFound(string message);\n\n ////////////////////////////////////////////////////////////////\n /// --- FUNCTIONS\n ////////////////////////////////////////////////////////////////\n /// @notice Use this to get the amount to swap before rebalance\n /// @dev This call will only revert, check the logs to get returned values.\n /// @dev Need to perform this call while impersonating the governor or strategist of AMO.\n /// @return data Data struct with the amount and the number of iterations\n function quoteAmountToSwapBeforeRebalance()\n public\n returns (Data memory data)\n {\n return\n _quoteAmountToSwapBeforeRebalance(\n type(uint256).max,\n type(uint256).max\n );\n }\n\n /// @notice Use this to get the amount to swap before rebalance and\n /// update allowedWethShareStart and allowedWethShareEnd on AMO.\n /// @dev This call will only revert, check the logs to get returned values.\n /// @dev Need to perform this call while impersonating the governor of AMO.\n /// @param overrideBottomWethShare New value for the allowedWethShareStart on AMO.\n /// Use type(uint256).max to keep same value.\n /// @param overrideTopWethShare New value for the allowedWethShareEnd on AMO.\n /// Use type(uint256).max to keep same value.\n /// @return data Data struct with the amount and the number of iterations\n function quoteAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) public returns (Data memory data) {\n return\n _quoteAmountToSwapBeforeRebalance(\n overrideBottomWethShare,\n overrideTopWethShare\n );\n }\n\n /// @notice Internal logic for quoteAmountToSwapBeforeRebalance.\n function _quoteAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) internal returns (Data memory data) {\n try\n quoterHelper.getAmountToSwapBeforeRebalance(\n overrideBottomWethShare,\n overrideTopWethShare\n )\n {\n revert(\"Previous call should only revert, it cannot succeed\");\n } catch (bytes memory reason) {\n bytes4 receivedSelector = bytes4(reason);\n if (receivedSelector == QuoterHelper.ValidAmount.selector) {\n uint256 value;\n uint256 iterations;\n bool swapWETHForOETHB;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n value := mload(add(reason, 0x24))\n iterations := mload(add(reason, 0x44))\n swapWETHForOETHB := mload(add(reason, 0x64))\n }\n emit ValueFound(value, iterations, swapWETHForOETHB);\n return Data({ amount: value, iterations: iterations });\n }\n\n if (receivedSelector == QuoterHelper.OutOfIterations.selector) {\n emit ValueNotFound(\"Out of iterations\");\n revert(\"Out of iterations\");\n }\n\n emit ValueNotFound(\"Unexpected error\");\n revert(abi.decode(reason, (string)));\n }\n }\n\n /// @notice Use this to get the amount to swap to reach the target price after swap.\n /// @dev This call will only revert, check the logs to get returned values.\n /// @param sqrtPriceTargetX96 The target price to reach.\n function quoteAmountToSwapToReachPrice(uint160 sqrtPriceTargetX96) public {\n (\n uint256 amount,\n uint256 iterations,\n bool swapWETHForOETHB,\n uint160 sqrtPriceAfterX96\n ) = quoterHelper.getAmountToSwapToReachPrice(sqrtPriceTargetX96);\n\n emit ValueFoundBis(\n amount,\n iterations,\n swapWETHForOETHB,\n sqrtPriceAfterX96\n );\n }\n\n function claimGovernance() public {\n quoterHelper.claimGovernanceOnAMO();\n }\n\n function giveBackGovernance() public {\n quoterHelper.giveBackGovernanceOnAMO();\n }\n}\n" + }, + "contracts/utils/BalancerErrors.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity >=0.7.4 <0.9.0;\n\n// solhint-disable\n\n/**\n * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are\n * supported.\n * Uses the default 'BAL' prefix for the error code\n */\nfunction _require(bool condition, uint256 errorCode) pure {\n if (!condition) _revert(errorCode);\n}\n\n/**\n * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are\n * supported.\n */\nfunction _require(\n bool condition,\n uint256 errorCode,\n bytes3 prefix\n) pure {\n if (!condition) _revert(errorCode, prefix);\n}\n\n/**\n * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported.\n * Uses the default 'BAL' prefix for the error code\n */\nfunction _revert(uint256 errorCode) pure {\n _revert(errorCode, 0x42414c); // This is the raw byte representation of \"BAL\"\n}\n\n/**\n * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported.\n */\nfunction _revert(uint256 errorCode, bytes3 prefix) pure {\n uint256 prefixUint = uint256(uint24(prefix));\n // We're going to dynamically create a revert string based on the error code, with the following format:\n // 'BAL#{errorCode}'\n // where the code is left-padded with zeroes to three digits (so they range from 000 to 999).\n //\n // We don't have revert strings embedded in the contract to save bytecode size: it takes much less space to store a\n // number (8 to 16 bits) than the individual string characters.\n //\n // The dynamic string creation algorithm that follows could be implemented in Solidity, but assembly allows for a\n // much denser implementation, again saving bytecode size. Given this function unconditionally reverts, this is a\n // safe place to rely on it without worrying about how its usage might affect e.g. memory contents.\n assembly {\n // First, we need to compute the ASCII representation of the error code. We assume that it is in the 0-999\n // range, so we only need to convert three digits. To convert the digits to ASCII, we add 0x30, the value for\n // the '0' character.\n\n let units := add(mod(errorCode, 10), 0x30)\n\n errorCode := div(errorCode, 10)\n let tenths := add(mod(errorCode, 10), 0x30)\n\n errorCode := div(errorCode, 10)\n let hundreds := add(mod(errorCode, 10), 0x30)\n\n // With the individual characters, we can now construct the full string.\n // We first append the '#' character (0x23) to the prefix. In the case of 'BAL', it results in 0x42414c23 ('BAL#')\n // Then, we shift this by 24 (to provide space for the 3 bytes of the error code), and add the\n // characters to it, each shifted by a multiple of 8.\n // The revert reason is then shifted left by 200 bits (256 minus the length of the string, 7 characters * 8 bits\n // per character = 56) to locate it in the most significant part of the 256 slot (the beginning of a byte\n // array).\n let formattedPrefix := shl(24, add(0x23, shl(8, prefixUint)))\n\n let revertReason := shl(\n 200,\n add(\n formattedPrefix,\n add(add(units, shl(8, tenths)), shl(16, hundreds))\n )\n )\n\n // We can now encode the reason in memory, which can be safely overwritten as we're about to revert. The encoded\n // message will have the following layout:\n // [ revert reason identifier ] [ string location offset ] [ string length ] [ string contents ]\n\n // The Solidity revert reason identifier is 0x08c739a0, the function selector of the Error(string) function. We\n // also write zeroes to the next 28 bytes of memory, but those are about to be overwritten.\n mstore(\n 0x0,\n 0x08c379a000000000000000000000000000000000000000000000000000000000\n )\n // Next is the offset to the location of the string, which will be placed immediately after (20 bytes away).\n mstore(\n 0x04,\n 0x0000000000000000000000000000000000000000000000000000000000000020\n )\n // The string length is fixed: 7 characters.\n mstore(0x24, 7)\n // Finally, the string itself is stored.\n mstore(0x44, revertReason)\n\n // Even if the string is only 7 bytes long, we need to return a full 32 byte slot containing it. The length of\n // the encoded message is therefore 4 + 32 + 32 + 32 = 100.\n revert(0, 100)\n }\n}\n\nlibrary Errors {\n // Math\n uint256 internal constant ADD_OVERFLOW = 0;\n uint256 internal constant SUB_OVERFLOW = 1;\n uint256 internal constant SUB_UNDERFLOW = 2;\n uint256 internal constant MUL_OVERFLOW = 3;\n uint256 internal constant ZERO_DIVISION = 4;\n uint256 internal constant DIV_INTERNAL = 5;\n uint256 internal constant X_OUT_OF_BOUNDS = 6;\n uint256 internal constant Y_OUT_OF_BOUNDS = 7;\n uint256 internal constant PRODUCT_OUT_OF_BOUNDS = 8;\n uint256 internal constant INVALID_EXPONENT = 9;\n\n // Input\n uint256 internal constant OUT_OF_BOUNDS = 100;\n uint256 internal constant UNSORTED_ARRAY = 101;\n uint256 internal constant UNSORTED_TOKENS = 102;\n uint256 internal constant INPUT_LENGTH_MISMATCH = 103;\n uint256 internal constant ZERO_TOKEN = 104;\n uint256 internal constant INSUFFICIENT_DATA = 105;\n\n // Shared pools\n uint256 internal constant MIN_TOKENS = 200;\n uint256 internal constant MAX_TOKENS = 201;\n uint256 internal constant MAX_SWAP_FEE_PERCENTAGE = 202;\n uint256 internal constant MIN_SWAP_FEE_PERCENTAGE = 203;\n uint256 internal constant MINIMUM_BPT = 204;\n uint256 internal constant CALLER_NOT_VAULT = 205;\n uint256 internal constant UNINITIALIZED = 206;\n uint256 internal constant BPT_IN_MAX_AMOUNT = 207;\n uint256 internal constant BPT_OUT_MIN_AMOUNT = 208;\n uint256 internal constant EXPIRED_PERMIT = 209;\n uint256 internal constant NOT_TWO_TOKENS = 210;\n uint256 internal constant DISABLED = 211;\n\n // Pools\n uint256 internal constant MIN_AMP = 300;\n uint256 internal constant MAX_AMP = 301;\n uint256 internal constant MIN_WEIGHT = 302;\n uint256 internal constant MAX_STABLE_TOKENS = 303;\n uint256 internal constant MAX_IN_RATIO = 304;\n uint256 internal constant MAX_OUT_RATIO = 305;\n uint256 internal constant MIN_BPT_IN_FOR_TOKEN_OUT = 306;\n uint256 internal constant MAX_OUT_BPT_FOR_TOKEN_IN = 307;\n uint256 internal constant NORMALIZED_WEIGHT_INVARIANT = 308;\n uint256 internal constant INVALID_TOKEN = 309;\n uint256 internal constant UNHANDLED_JOIN_KIND = 310;\n uint256 internal constant ZERO_INVARIANT = 311;\n uint256 internal constant ORACLE_INVALID_SECONDS_QUERY = 312;\n uint256 internal constant ORACLE_NOT_INITIALIZED = 313;\n uint256 internal constant ORACLE_QUERY_TOO_OLD = 314;\n uint256 internal constant ORACLE_INVALID_INDEX = 315;\n uint256 internal constant ORACLE_BAD_SECS = 316;\n uint256 internal constant AMP_END_TIME_TOO_CLOSE = 317;\n uint256 internal constant AMP_ONGOING_UPDATE = 318;\n uint256 internal constant AMP_RATE_TOO_HIGH = 319;\n uint256 internal constant AMP_NO_ONGOING_UPDATE = 320;\n uint256 internal constant STABLE_INVARIANT_DIDNT_CONVERGE = 321;\n uint256 internal constant STABLE_GET_BALANCE_DIDNT_CONVERGE = 322;\n uint256 internal constant RELAYER_NOT_CONTRACT = 323;\n uint256 internal constant BASE_POOL_RELAYER_NOT_CALLED = 324;\n uint256 internal constant REBALANCING_RELAYER_REENTERED = 325;\n uint256 internal constant GRADUAL_UPDATE_TIME_TRAVEL = 326;\n uint256 internal constant SWAPS_DISABLED = 327;\n uint256 internal constant CALLER_IS_NOT_LBP_OWNER = 328;\n uint256 internal constant PRICE_RATE_OVERFLOW = 329;\n uint256 internal constant INVALID_JOIN_EXIT_KIND_WHILE_SWAPS_DISABLED = 330;\n uint256 internal constant WEIGHT_CHANGE_TOO_FAST = 331;\n uint256 internal constant LOWER_GREATER_THAN_UPPER_TARGET = 332;\n uint256 internal constant UPPER_TARGET_TOO_HIGH = 333;\n uint256 internal constant UNHANDLED_BY_LINEAR_POOL = 334;\n uint256 internal constant OUT_OF_TARGET_RANGE = 335;\n uint256 internal constant UNHANDLED_EXIT_KIND = 336;\n uint256 internal constant UNAUTHORIZED_EXIT = 337;\n uint256 internal constant MAX_MANAGEMENT_SWAP_FEE_PERCENTAGE = 338;\n uint256 internal constant UNHANDLED_BY_MANAGED_POOL = 339;\n uint256 internal constant UNHANDLED_BY_PHANTOM_POOL = 340;\n uint256 internal constant TOKEN_DOES_NOT_HAVE_RATE_PROVIDER = 341;\n uint256 internal constant INVALID_INITIALIZATION = 342;\n uint256 internal constant OUT_OF_NEW_TARGET_RANGE = 343;\n uint256 internal constant FEATURE_DISABLED = 344;\n uint256 internal constant UNINITIALIZED_POOL_CONTROLLER = 345;\n uint256 internal constant SET_SWAP_FEE_DURING_FEE_CHANGE = 346;\n uint256 internal constant SET_SWAP_FEE_PENDING_FEE_CHANGE = 347;\n uint256 internal constant CHANGE_TOKENS_DURING_WEIGHT_CHANGE = 348;\n uint256 internal constant CHANGE_TOKENS_PENDING_WEIGHT_CHANGE = 349;\n uint256 internal constant MAX_WEIGHT = 350;\n uint256 internal constant UNAUTHORIZED_JOIN = 351;\n uint256 internal constant MAX_MANAGEMENT_AUM_FEE_PERCENTAGE = 352;\n uint256 internal constant FRACTIONAL_TARGET = 353;\n uint256 internal constant ADD_OR_REMOVE_BPT = 354;\n uint256 internal constant INVALID_CIRCUIT_BREAKER_BOUNDS = 355;\n uint256 internal constant CIRCUIT_BREAKER_TRIPPED = 356;\n uint256 internal constant MALICIOUS_QUERY_REVERT = 357;\n uint256 internal constant JOINS_EXITS_DISABLED = 358;\n\n // Lib\n uint256 internal constant REENTRANCY = 400;\n uint256 internal constant SENDER_NOT_ALLOWED = 401;\n uint256 internal constant PAUSED = 402;\n uint256 internal constant PAUSE_WINDOW_EXPIRED = 403;\n uint256 internal constant MAX_PAUSE_WINDOW_DURATION = 404;\n uint256 internal constant MAX_BUFFER_PERIOD_DURATION = 405;\n uint256 internal constant INSUFFICIENT_BALANCE = 406;\n uint256 internal constant INSUFFICIENT_ALLOWANCE = 407;\n uint256 internal constant ERC20_TRANSFER_FROM_ZERO_ADDRESS = 408;\n uint256 internal constant ERC20_TRANSFER_TO_ZERO_ADDRESS = 409;\n uint256 internal constant ERC20_MINT_TO_ZERO_ADDRESS = 410;\n uint256 internal constant ERC20_BURN_FROM_ZERO_ADDRESS = 411;\n uint256 internal constant ERC20_APPROVE_FROM_ZERO_ADDRESS = 412;\n uint256 internal constant ERC20_APPROVE_TO_ZERO_ADDRESS = 413;\n uint256 internal constant ERC20_TRANSFER_EXCEEDS_ALLOWANCE = 414;\n uint256 internal constant ERC20_DECREASED_ALLOWANCE_BELOW_ZERO = 415;\n uint256 internal constant ERC20_TRANSFER_EXCEEDS_BALANCE = 416;\n uint256 internal constant ERC20_BURN_EXCEEDS_ALLOWANCE = 417;\n uint256 internal constant SAFE_ERC20_CALL_FAILED = 418;\n uint256 internal constant ADDRESS_INSUFFICIENT_BALANCE = 419;\n uint256 internal constant ADDRESS_CANNOT_SEND_VALUE = 420;\n uint256 internal constant SAFE_CAST_VALUE_CANT_FIT_INT256 = 421;\n uint256 internal constant GRANT_SENDER_NOT_ADMIN = 422;\n uint256 internal constant REVOKE_SENDER_NOT_ADMIN = 423;\n uint256 internal constant RENOUNCE_SENDER_NOT_ALLOWED = 424;\n uint256 internal constant BUFFER_PERIOD_EXPIRED = 425;\n uint256 internal constant CALLER_IS_NOT_OWNER = 426;\n uint256 internal constant NEW_OWNER_IS_ZERO = 427;\n uint256 internal constant CODE_DEPLOYMENT_FAILED = 428;\n uint256 internal constant CALL_TO_NON_CONTRACT = 429;\n uint256 internal constant LOW_LEVEL_CALL_FAILED = 430;\n uint256 internal constant NOT_PAUSED = 431;\n uint256 internal constant ADDRESS_ALREADY_ALLOWLISTED = 432;\n uint256 internal constant ADDRESS_NOT_ALLOWLISTED = 433;\n uint256 internal constant ERC20_BURN_EXCEEDS_BALANCE = 434;\n uint256 internal constant INVALID_OPERATION = 435;\n uint256 internal constant CODEC_OVERFLOW = 436;\n uint256 internal constant IN_RECOVERY_MODE = 437;\n uint256 internal constant NOT_IN_RECOVERY_MODE = 438;\n uint256 internal constant INDUCED_FAILURE = 439;\n uint256 internal constant EXPIRED_SIGNATURE = 440;\n uint256 internal constant MALFORMED_SIGNATURE = 441;\n uint256 internal constant SAFE_CAST_VALUE_CANT_FIT_UINT64 = 442;\n uint256 internal constant UNHANDLED_FEE_TYPE = 443;\n uint256 internal constant BURN_FROM_ZERO = 444;\n\n // Vault\n uint256 internal constant INVALID_POOL_ID = 500;\n uint256 internal constant CALLER_NOT_POOL = 501;\n uint256 internal constant SENDER_NOT_ASSET_MANAGER = 502;\n uint256 internal constant USER_DOESNT_ALLOW_RELAYER = 503;\n uint256 internal constant INVALID_SIGNATURE = 504;\n uint256 internal constant EXIT_BELOW_MIN = 505;\n uint256 internal constant JOIN_ABOVE_MAX = 506;\n uint256 internal constant SWAP_LIMIT = 507;\n uint256 internal constant SWAP_DEADLINE = 508;\n uint256 internal constant CANNOT_SWAP_SAME_TOKEN = 509;\n uint256 internal constant UNKNOWN_AMOUNT_IN_FIRST_SWAP = 510;\n uint256 internal constant MALCONSTRUCTED_MULTIHOP_SWAP = 511;\n uint256 internal constant INTERNAL_BALANCE_OVERFLOW = 512;\n uint256 internal constant INSUFFICIENT_INTERNAL_BALANCE = 513;\n uint256 internal constant INVALID_ETH_INTERNAL_BALANCE = 514;\n uint256 internal constant INVALID_POST_LOAN_BALANCE = 515;\n uint256 internal constant INSUFFICIENT_ETH = 516;\n uint256 internal constant UNALLOCATED_ETH = 517;\n uint256 internal constant ETH_TRANSFER = 518;\n uint256 internal constant CANNOT_USE_ETH_SENTINEL = 519;\n uint256 internal constant TOKENS_MISMATCH = 520;\n uint256 internal constant TOKEN_NOT_REGISTERED = 521;\n uint256 internal constant TOKEN_ALREADY_REGISTERED = 522;\n uint256 internal constant TOKENS_ALREADY_SET = 523;\n uint256 internal constant TOKENS_LENGTH_MUST_BE_2 = 524;\n uint256 internal constant NONZERO_TOKEN_BALANCE = 525;\n uint256 internal constant BALANCE_TOTAL_OVERFLOW = 526;\n uint256 internal constant POOL_NO_TOKENS = 527;\n uint256 internal constant INSUFFICIENT_FLASH_LOAN_BALANCE = 528;\n\n // Fees\n uint256 internal constant SWAP_FEE_PERCENTAGE_TOO_HIGH = 600;\n uint256 internal constant FLASH_LOAN_FEE_PERCENTAGE_TOO_HIGH = 601;\n uint256 internal constant INSUFFICIENT_FLASH_LOAN_FEE_AMOUNT = 602;\n uint256 internal constant AUM_FEE_PERCENTAGE_TOO_HIGH = 603;\n\n // FeeSplitter\n uint256 internal constant SPLITTER_FEE_PERCENTAGE_TOO_HIGH = 700;\n\n // Misc\n uint256 internal constant UNIMPLEMENTED = 998;\n uint256 internal constant SHOULD_NOT_HAPPEN = 999;\n}\n" + }, + "contracts/utils/DepositContractUtils.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract DepositContractUtils {\n function calculateDepositDataRoot(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature\n ) public pure returns (bytes32 node) {\n uint256 deposit_amount = 32 ether / 1 gwei;\n bytes memory amount = to_little_endian_64(uint64(deposit_amount));\n\n // Compute deposit data root (`DepositData` hash tree root)\n bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, bytes16(0)));\n bytes32 signature_root = sha256(\n abi.encodePacked(\n sha256(abi.encodePacked(signature[:64])),\n sha256(abi.encodePacked(signature[64:], bytes32(0)))\n )\n );\n node = sha256(\n abi.encodePacked(\n sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)),\n sha256(abi.encodePacked(amount, bytes24(0), signature_root))\n )\n );\n }\n\n function to_little_endian_64(uint64 value)\n internal\n pure\n returns (bytes memory ret)\n {\n ret = new bytes(8);\n bytes8 bytesValue = bytes8(value);\n // Byteswapping during copying to bytes.\n ret[0] = bytesValue[7];\n ret[1] = bytesValue[6];\n ret[2] = bytesValue[5];\n ret[3] = bytesValue[4];\n ret[4] = bytesValue[3];\n ret[5] = bytesValue[2];\n ret[6] = bytesValue[1];\n ret[7] = bytesValue[0];\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/utils/InitializableERC20Detailed.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @dev Optional functions from the ERC20 standard.\n * Converted from openzeppelin/contracts/token/ERC20/ERC20Detailed.sol\n * @author Origin Protocol Inc\n */\nabstract contract InitializableERC20Detailed is IERC20 {\n // Storage gap to skip storage from prior to OUSD reset\n uint256[100] private _____gap;\n\n string private _name;\n string private _symbol;\n uint8 private _decimals;\n\n /**\n * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of\n * these values are immutable: they can only be set once during\n * construction.\n * @notice To avoid variable shadowing appended `Arg` after arguments name.\n */\n function _initialize(\n string memory nameArg,\n string memory symbolArg,\n uint8 decimalsArg\n ) internal {\n _name = nameArg;\n _symbol = symbolArg;\n _decimals = decimalsArg;\n }\n\n /**\n * @notice Returns the name of the token.\n */\n function name() public view returns (string memory) {\n return _name;\n }\n\n /**\n * @notice Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view returns (string memory) {\n return _symbol;\n }\n\n /**\n * @notice Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5,05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei.\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view returns (uint8) {\n return _decimals;\n }\n}\n" + }, + "contracts/utils/PRBMath.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// Copied from the PRBMath library\n// https://github.com/PaulRBerg/prb-math/blob/main/src/Common.sol\n\n/// @notice Calculates the square root of x using the Babylonian method.\n///\n/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.\n///\n/// Notes:\n/// - If x is not a perfect square, the result is rounded down.\n/// - Credits to OpenZeppelin for the explanations in comments below.\n///\n/// @param x The uint256 number for which to calculate the square root.\n/// @return result The result as a uint256.\n/// @custom:smtchecker abstract-function-nondet\nfunction sqrt(uint256 x) pure returns (uint256 result) {\n if (x == 0) {\n return 0;\n }\n\n // For our first guess, we calculate the biggest power of 2 which is smaller than the square root of x.\n //\n // We know that the \"msb\" (most significant bit) of x is a power of 2 such that we have:\n //\n // $$\n // msb(x) <= x <= 2*msb(x)$\n // $$\n //\n // We write $msb(x)$ as $2^k$, and we get:\n //\n // $$\n // k = log_2(x)\n // $$\n //\n // Thus, we can write the initial inequality as:\n //\n // $$\n // 2^{log_2(x)} <= x <= 2*2^{log_2(x)+1} \\\\\n // sqrt(2^k) <= sqrt(x) < sqrt(2^{k+1}) \\\\\n // 2^{k/2} <= sqrt(x) < 2^{(k+1)/2} <= 2^{(k/2)+1}\n // $$\n //\n // Consequently, $2^{log_2(x) /2} is a good first approximation of sqrt(x) with at least one correct bit.\n uint256 xAux = uint256(x);\n result = 1;\n if (xAux >= 2**128) {\n xAux >>= 128;\n result <<= 64;\n }\n if (xAux >= 2**64) {\n xAux >>= 64;\n result <<= 32;\n }\n if (xAux >= 2**32) {\n xAux >>= 32;\n result <<= 16;\n }\n if (xAux >= 2**16) {\n xAux >>= 16;\n result <<= 8;\n }\n if (xAux >= 2**8) {\n xAux >>= 8;\n result <<= 4;\n }\n if (xAux >= 2**4) {\n xAux >>= 4;\n result <<= 2;\n }\n if (xAux >= 2**2) {\n result <<= 1;\n }\n\n // At this point, `result` is an estimation with at least one bit of precision. We know the true value has at\n // most 128 bits, since it is the square root of a uint256. Newton's method converges quadratically (precision\n // doubles at every iteration). We thus need at most 7 iteration to turn our partial result with one bit of\n // precision into the expected uint128 result.\n unchecked {\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n\n // If x is not a perfect square, round the result toward zero.\n uint256 roundedResult = x / result;\n if (result >= roundedResult) {\n result = roundedResult;\n }\n }\n}\n" + }, + "contracts/utils/StableMath.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\n// Based on StableMath from Stability Labs Pty. Ltd.\n// https://github.com/mstable/mStable-contracts/blob/master/contracts/shared/StableMath.sol\n\nlibrary StableMath {\n using SafeMath for uint256;\n\n /**\n * @dev Scaling unit for use in specific calculations,\n * where 1 * 10**18, or 1e18 represents a unit '1'\n */\n uint256 private constant FULL_SCALE = 1e18;\n\n /***************************************\n Helpers\n ****************************************/\n\n /**\n * @dev Adjust the scale of an integer\n * @param to Decimals to scale to\n * @param from Decimals to scale from\n */\n function scaleBy(\n uint256 x,\n uint256 to,\n uint256 from\n ) internal pure returns (uint256) {\n if (to > from) {\n x = x.mul(10**(to - from));\n } else if (to < from) {\n // slither-disable-next-line divide-before-multiply\n x = x.div(10**(from - to));\n }\n return x;\n }\n\n /***************************************\n Precise Arithmetic\n ****************************************/\n\n /**\n * @dev Multiplies two precise units, and then truncates by the full scale\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit\n */\n function mulTruncate(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulTruncateScale(x, y, FULL_SCALE);\n }\n\n /**\n * @dev Multiplies two precise units, and then truncates by the given scale. For example,\n * when calculating 90% of 10e18, (10e18 * 9e17) / 1e18 = (9e36) / 1e18 = 9e18\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @param scale Scale unit\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit\n */\n function mulTruncateScale(\n uint256 x,\n uint256 y,\n uint256 scale\n ) internal pure returns (uint256) {\n // e.g. assume scale = fullScale\n // z = 10e18 * 9e17 = 9e36\n uint256 z = x.mul(y);\n // return 9e36 / 1e18 = 9e18\n return z.div(scale);\n }\n\n /**\n * @dev Multiplies two precise units, and then truncates by the full scale, rounding up the result\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit, rounded up to the closest base unit.\n */\n function mulTruncateCeil(uint256 x, uint256 y)\n internal\n pure\n returns (uint256)\n {\n // e.g. 8e17 * 17268172638 = 138145381104e17\n uint256 scaled = x.mul(y);\n // e.g. 138145381104e17 + 9.99...e17 = 138145381113.99...e17\n uint256 ceil = scaled.add(FULL_SCALE.sub(1));\n // e.g. 13814538111.399...e18 / 1e18 = 13814538111\n return ceil.div(FULL_SCALE);\n }\n\n /**\n * @dev Precisely divides two units, by first scaling the left hand operand. Useful\n * for finding percentage weightings, i.e. 8e18/10e18 = 80% (or 8e17)\n * @param x Left hand input to division\n * @param y Right hand input to division\n * @return Result after multiplying the left operand by the scale, and\n * executing the division on the right hand input.\n */\n function divPrecisely(uint256 x, uint256 y)\n internal\n pure\n returns (uint256)\n {\n // e.g. 8e18 * 1e18 = 8e36\n uint256 z = x.mul(FULL_SCALE);\n // e.g. 8e36 / 10e18 = 8e17\n return z.div(y);\n }\n}\n" + }, + "contracts/vault/OETHBaseVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"./OETHVaultAdmin.sol\";\n\n/**\n * @title OETH Base VaultAdmin Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBaseVaultAdmin is OETHVaultAdmin {\n constructor(address _weth) OETHVaultAdmin(_weth) {}\n}\n" + }, + "contracts/vault/OETHBaseVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { OETHVaultCore } from \"./OETHVaultCore.sol\";\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\n\n/**\n * @title OETH Base VaultCore Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBaseVaultCore is OETHVaultCore {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(address _weth) OETHVaultCore(_weth) {}\n\n // @inheritdoc OETHVaultCore\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n override\n {\n // Only Strategist or Governor can redeem using the Vault for now.\n // We don't have the onlyGovernorOrStrategist modifier on VaultCore.\n // Since we won't be using that modifier anywhere in the VaultCore as well,\n // the check has been added inline instead of moving it to VaultStorage.\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n\n super._redeem(_amount, _minimumUnitAmount);\n }\n}\n" + }, + "contracts/vault/OETHBaseZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\n\ncontract OETHBaseZapper {\n IERC20 public immutable oethb;\n IERC4626 public immutable woethb;\n IVault public immutable vault;\n\n IWETH9 public constant weth =\n IWETH9(0x4200000000000000000000000000000000000006);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(\n address _oethb,\n address _woethb,\n address _vault\n ) {\n oethb = IERC20(_oethb);\n woethb = IERC4626(_woethb);\n vault = IVault(_vault);\n\n weth.approve(address(_vault), type(uint256).max);\n IERC20(_oethb).approve(_woethb, type(uint256).max);\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return.\n * Will verify that the user is sent 1:1 for ETH.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit ETH and receive superOETHb in return\n * Will verify that the user is sent 1:1 for ETH.\n * @return Amount of OETH sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap ETH\n weth.deposit{ value: balance }();\n\n // Mint with WETH\n return _mint(balance, msg.sender);\n }\n\n /**\n * @dev Deposit ETH and receive superOETHb in return\n * @param minReceived min amount of wsuperOETHb to receive\n * @return Amount of wsuperOETHb sent to user\n */\n function depositETHForWrappedTokens(uint256 minReceived)\n external\n payable\n returns (uint256)\n {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap ETH\n weth.deposit{ value: balance }();\n\n // Mint with WETH\n uint256 mintedOethb = _mint(balance, address(this));\n\n // Wrap superOETHb into wsuperOETHb\n uint256 mintedWoethb = woethb.deposit(mintedOethb, msg.sender);\n\n require(mintedWoethb >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWoethb;\n }\n\n /**\n * @dev Deposit WETH and receive superOETHb in return\n * @param wethAmount Amount of WETH to deposit\n * @param minReceived min amount of wsuperOETHb to receive\n * @return Amount of wsuperOETHb sent to user\n */\n function depositWETHForWrappedTokens(\n uint256 wethAmount,\n uint256 minReceived\n ) external returns (uint256) {\n // slither-disable-next-line unchecked-transfer unused-return\n weth.transferFrom(msg.sender, address(this), wethAmount);\n\n emit Zap(msg.sender, address(weth), wethAmount);\n\n // Mint with WETH\n uint256 mintedOethb = _mint(wethAmount, address(this));\n\n // Wrap superOETHb into wsuperOETHb\n uint256 mintedWoethb = woethb.deposit(mintedOethb, msg.sender);\n\n require(mintedWoethb >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWoethb;\n }\n\n /**\n * @dev Internal function to mint superOETHb with WETH\n * @param minOETH Minimum amount of OETH to for user to receive\n * @param recipient Address that receives the tokens\n * @return Amount of OETH sent to user\n */\n function _mint(uint256 minOETH, address recipient)\n internal\n returns (uint256)\n {\n uint256 toMint = weth.balanceOf(address(this));\n vault.mint(address(weth), toMint, minOETH);\n uint256 mintedAmount = oethb.balanceOf(address(this));\n require(mintedAmount >= minOETH, \"Zapper: not enough minted\");\n\n if (recipient != address(this)) {\n require(oethb.transfer(recipient, mintedAmount));\n }\n\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/OETHVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Vault } from \"./Vault.sol\";\n\n/**\n * @title OETH Vault Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVault is Vault {\n\n}\n" + }, + "contracts/vault/OETHVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { VaultAdmin } from \"./VaultAdmin.sol\";\n\n/**\n * @title OETH VaultAdmin Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVaultAdmin is VaultAdmin {\n using SafeERC20 for IERC20;\n\n address public immutable weth;\n\n constructor(address _weth) {\n weth = _weth;\n }\n\n /**\n * @notice Adds a strategy to the mint whitelist.\n * Reverts if strategy isn't approved on Vault.\n * @param strategyAddr Strategy address\n */\n function addStrategyToMintWhitelist(address strategyAddr)\n external\n onlyGovernor\n {\n require(strategies[strategyAddr].isSupported, \"Strategy not approved\");\n\n require(\n !isMintWhitelistedStrategy[strategyAddr],\n \"Already whitelisted\"\n );\n\n isMintWhitelistedStrategy[strategyAddr] = true;\n\n emit StrategyAddedToMintWhitelist(strategyAddr);\n }\n\n /**\n * @notice Removes a strategy from the mint whitelist.\n * @param strategyAddr Strategy address\n */\n function removeStrategyFromMintWhitelist(address strategyAddr)\n external\n onlyGovernor\n {\n // Intentionally skipping `strategies.isSupported` check since\n // we may wanna remove an address even after removing the strategy\n\n require(isMintWhitelistedStrategy[strategyAddr], \"Not whitelisted\");\n\n isMintWhitelistedStrategy[strategyAddr] = false;\n\n emit StrategyRemovedFromMintWhitelist(strategyAddr);\n }\n\n /// @dev Simplified version of the deposit function as WETH is the only supported asset.\n function _depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal override {\n require(\n strategies[_strategyToAddress].isSupported,\n \"Invalid to Strategy\"\n );\n require(\n _assets.length == 1 && _amounts.length == 1 && _assets[0] == weth,\n \"Only WETH is supported\"\n );\n\n // Check the there is enough WETH to transfer once the WETH reserved for the withdrawal queue is accounted for\n require(_amounts[0] <= _wethAvailable(), \"Not enough WETH available\");\n\n // Send required amount of funds to the strategy\n IERC20(weth).safeTransfer(_strategyToAddress, _amounts[0]);\n\n // Deposit all the funds that have been sent to the strategy\n IStrategy(_strategyToAddress).depositAll();\n }\n\n function _withdrawFromStrategy(\n address _recipient,\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal override {\n super._withdrawFromStrategy(\n _recipient,\n _strategyFromAddress,\n _assets,\n _amounts\n );\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n function _withdrawAllFromStrategy(address _strategyAddr) internal override {\n super._withdrawAllFromStrategy(_strategyAddr);\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n function _withdrawAllFromStrategies() internal override {\n super._withdrawAllFromStrategies();\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n /// @dev Calculate how much WETH in the vault is not reserved for the withdrawal queue.\n // That is, it is available to be redeemed or deposited into a strategy.\n function _wethAvailable() internal view returns (uint256 wethAvailable) {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n uint256 outstandingWithdrawals = queue.queued - queue.claimed;\n\n // The amount of sitting in WETH in the vault\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // If there is not enough WETH in the vault to cover the outstanding withdrawals\n if (wethBalance <= outstandingWithdrawals) {\n return 0;\n }\n\n return wethBalance - outstandingWithdrawals;\n }\n\n function _swapCollateral(\n address,\n address,\n uint256,\n uint256,\n bytes calldata\n ) internal pure override returns (uint256) {\n revert(\"Collateral swap not supported\");\n }\n}\n" + }, + "contracts/vault/OETHVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { VaultCore } from \"./VaultCore.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\n\n/**\n * @title OETH VaultCore Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVaultCore is VaultCore {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n address public immutable weth;\n uint256 public wethAssetIndex;\n\n // For future use (because OETHBaseVaultCore inherits from this)\n uint256[50] private __gap;\n\n constructor(address _weth) {\n weth = _weth;\n }\n\n /**\n * @dev Caches WETH's index in `allAssets` variable.\n * Reduces gas usage by redeem by caching that.\n */\n function cacheWETHAssetIndex() external onlyGovernor {\n uint256 assetCount = allAssets.length;\n for (uint256 i; i < assetCount; ++i) {\n if (allAssets[i] == weth) {\n wethAssetIndex = i;\n break;\n }\n }\n\n require(allAssets[wethAssetIndex] == weth, \"Invalid WETH Asset Index\");\n }\n\n // @inheritdoc VaultCore\n function mintForStrategy(uint256 amount)\n external\n override\n whenNotCapitalPaused\n {\n require(\n strategies[msg.sender].isSupported == true,\n \"Unsupported strategy\"\n );\n require(\n isMintWhitelistedStrategy[msg.sender] == true,\n \"Not whitelisted strategy\"\n );\n\n emit Mint(msg.sender, amount);\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, amount);\n }\n\n // @inheritdoc VaultCore\n function burnForStrategy(uint256 amount)\n external\n override\n whenNotCapitalPaused\n {\n require(\n strategies[msg.sender].isSupported == true,\n \"Unsupported strategy\"\n );\n require(\n isMintWhitelistedStrategy[msg.sender] == true,\n \"Not whitelisted strategy\"\n );\n\n emit Redeem(msg.sender, amount);\n\n // Burn OTokens\n oUSD.burn(msg.sender, amount);\n }\n\n // @inheritdoc VaultCore\n // slither-disable-start reentrancy-no-eth\n function _mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) internal virtual override {\n require(_asset == weth, \"Unsupported asset for minting\");\n require(_amount > 0, \"Amount must be greater than 0\");\n require(\n _amount >= _minimumOusdAmount,\n \"Mint amount lower than minimum\"\n );\n\n emit Mint(msg.sender, _amount);\n\n // Rebase must happen before any transfers occur.\n if (!rebasePaused && _amount >= rebaseThreshold) {\n _rebase();\n }\n\n // Mint oTokens\n oUSD.mint(msg.sender, _amount);\n\n // Transfer the deposited coins to the vault\n IERC20(_asset).safeTransferFrom(msg.sender, address(this), _amount);\n\n // Give priority to the withdrawal queue for the new WETH liquidity\n _addWithdrawalQueueLiquidity();\n\n // Auto-allocate if necessary\n if (_amount >= autoAllocateThreshold) {\n _allocate();\n }\n }\n\n // slither-disable-end reentrancy-no-eth\n\n // @inheritdoc VaultCore\n function _calculateRedeemOutputs(uint256 _amount)\n internal\n view\n virtual\n override\n returns (uint256[] memory outputs)\n {\n // Overrides `VaultCore._calculateRedeemOutputs` to redeem with only\n // WETH instead of LST-mix. Doesn't change the function signature\n // for backward compatibility\n\n // Calculate redeem fee\n if (redeemFeeBps > 0) {\n uint256 redeemFee = _amount.mulTruncateScale(redeemFeeBps, 1e4);\n _amount = _amount - redeemFee;\n }\n\n // Ensure that the WETH index is cached\n uint256 _wethAssetIndex = wethAssetIndex;\n require(\n allAssets[_wethAssetIndex] == weth,\n \"WETH Asset index not cached\"\n );\n\n outputs = new uint256[](allAssets.length);\n outputs[_wethAssetIndex] = _amount;\n }\n\n // @inheritdoc VaultCore\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n override\n {\n // Override `VaultCore._redeem` to simplify it. Gets rid of oracle\n // usage and looping through all assets for LST-mix redeem. Instead\n // does a simple WETH-only redeem.\n emit Redeem(msg.sender, _amount);\n\n if (_amount == 0) {\n return;\n }\n\n // Amount excluding fees\n // No fee for the strategist or the governor, makes it easier to do operations\n uint256 amountMinusFee = (msg.sender == strategistAddr || isGovernor())\n ? _amount\n : _calculateRedeemOutputs(_amount)[wethAssetIndex];\n\n require(\n amountMinusFee >= _minimumUnitAmount,\n \"Redeem amount lower than minimum\"\n );\n\n // Is there enough WETH in the Vault available after accounting for the withdrawal queue\n require(_wethAvailable() >= amountMinusFee, \"Liquidity error\");\n\n // Transfer WETH minus the fee to the redeemer\n IERC20(weth).safeTransfer(msg.sender, amountMinusFee);\n\n // Burn OETH from user (including fees)\n oUSD.burn(msg.sender, _amount);\n\n // Prevent insolvency\n _postRedeem(_amount);\n }\n\n /**\n * @notice Request an asynchronous withdrawal of WETH in exchange for OETH.\n * The OETH is burned on request and the WETH is transferred to the withdrawer on claim.\n * This request can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal this request's `queued` amount.\n * There is a minimum of 10 minutes before a request can be claimed. After that, the request just needs\n * enough WETH liquidity in the Vault to satisfy all the outstanding requests to that point in the queue.\n * OETH is converted to WETH at 1:1.\n * @param _amount Amount of OETH to burn.\n * @return requestId Unique ID for the withdrawal request\n * @return queued Cumulative total of all WETH queued including already claimed requests.\n */\n function requestWithdrawal(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256 requestId, uint256 queued)\n {\n require(withdrawalClaimDelay > 0, \"Async withdrawals not enabled\");\n\n // The check that the requester has enough OETH is done in to later burn call\n\n requestId = withdrawalQueueMetadata.nextWithdrawalIndex;\n queued = withdrawalQueueMetadata.queued + _amount;\n\n // Store the next withdrawal request\n withdrawalQueueMetadata.nextWithdrawalIndex = SafeCast.toUint128(\n requestId + 1\n );\n // Store the updated queued amount which reserves WETH in the withdrawal queue\n // and reduces the vault's total assets\n withdrawalQueueMetadata.queued = SafeCast.toUint128(queued);\n // Store the user's withdrawal request\n withdrawalRequests[requestId] = WithdrawalRequest({\n withdrawer: msg.sender,\n claimed: false,\n timestamp: uint40(block.timestamp),\n amount: SafeCast.toUint128(_amount),\n queued: SafeCast.toUint128(queued)\n });\n\n // Burn the user's OETH\n oUSD.burn(msg.sender, _amount);\n\n // Prevent withdrawal if the vault is solvent by more than the allowed percentage\n _postRedeem(_amount);\n\n emit WithdrawalRequested(msg.sender, requestId, _amount, queued);\n }\n\n // slither-disable-start reentrancy-no-eth\n /**\n * @notice Claim a previously requested withdrawal once it is claimable.\n * This request can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal this request's `queued` amount and 10 minutes has passed.\n * If the requests is not claimable, the transaction will revert with `Queue pending liquidity`.\n * If the request is not older than 10 minutes, the transaction will revert with `Claim delay not met`.\n * OETH is converted to WETH at 1:1.\n * @param _requestId Unique ID for the withdrawal request\n * @return amount Amount of WETH transferred to the withdrawer\n */\n function claimWithdrawal(uint256 _requestId)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256 amount)\n {\n // Try and get more liquidity if there is not enough available\n if (\n withdrawalRequests[_requestId].queued >\n withdrawalQueueMetadata.claimable\n ) {\n // Add any WETH to the withdrawal queue\n // this needs to remain here as:\n // - Vault can be funded and `addWithdrawalQueueLiquidity` is not externally called\n // - funds can be withdrawn from a strategy\n //\n // Those funds need to be added to withdrawal queue liquidity\n _addWithdrawalQueueLiquidity();\n }\n\n amount = _claimWithdrawal(_requestId);\n\n // transfer WETH from the vault to the withdrawer\n IERC20(weth).safeTransfer(msg.sender, amount);\n\n // Prevent insolvency\n _postRedeem(amount);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @notice Claim a previously requested withdrawals once they are claimable.\n * This requests can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal each request's `queued` amount and 10 minutes has passed.\n * If one of the requests is not claimable, the whole transaction will revert with `Queue pending liquidity`.\n * If one of the requests is not older than 10 minutes,\n * the whole transaction will revert with `Claim delay not met`.\n * @param _requestIds Unique ID of each withdrawal request\n * @return amounts Amount of WETH received for each request\n * @return totalAmount Total amount of WETH transferred to the withdrawer\n */\n function claimWithdrawals(uint256[] calldata _requestIds)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256[] memory amounts, uint256 totalAmount)\n {\n // Add any WETH to the withdrawal queue\n // this needs to remain here as:\n // - Vault can be funded and `addWithdrawalQueueLiquidity` is not externally called\n // - funds can be withdrawn from a strategy\n //\n // Those funds need to be added to withdrawal queue liquidity\n _addWithdrawalQueueLiquidity();\n\n amounts = new uint256[](_requestIds.length);\n for (uint256 i; i < _requestIds.length; ++i) {\n amounts[i] = _claimWithdrawal(_requestIds[i]);\n totalAmount += amounts[i];\n }\n\n // transfer all the claimed WETH from the vault to the withdrawer\n IERC20(weth).safeTransfer(msg.sender, totalAmount);\n\n // Prevent insolvency\n _postRedeem(totalAmount);\n }\n\n function _claimWithdrawal(uint256 requestId)\n internal\n returns (uint256 amount)\n {\n require(withdrawalClaimDelay > 0, \"Async withdrawals not enabled\");\n\n // Load the structs from storage into memory\n WithdrawalRequest memory request = withdrawalRequests[requestId];\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n require(\n request.timestamp + withdrawalClaimDelay <= block.timestamp,\n \"Claim delay not met\"\n );\n // If there isn't enough reserved liquidity in the queue to claim\n require(request.queued <= queue.claimable, \"Queue pending liquidity\");\n require(request.withdrawer == msg.sender, \"Not requester\");\n require(request.claimed == false, \"Already claimed\");\n\n // Store the request as claimed\n withdrawalRequests[requestId].claimed = true;\n // Store the updated claimed amount\n withdrawalQueueMetadata.claimed = queue.claimed + request.amount;\n\n emit WithdrawalClaimed(msg.sender, requestId, request.amount);\n\n return request.amount;\n }\n\n /// @notice Adds WETH to the withdrawal queue if there is a funding shortfall.\n /// @dev is called from the Native Staking strategy when validator withdrawals are processed.\n /// It also called before any WETH is allocated to a strategy.\n function addWithdrawalQueueLiquidity() external {\n _addWithdrawalQueueLiquidity();\n }\n\n /// @dev Adds WETH to the withdrawal queue if there is a funding shortfall.\n /// This assumes 1 WETH equal 1 OETH.\n function _addWithdrawalQueueLiquidity()\n internal\n returns (uint256 addedClaimable)\n {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // Check if the claimable WETH is less than the queued amount\n uint256 queueShortfall = queue.queued - queue.claimable;\n\n // No need to do anything is the withdrawal queue is full funded\n if (queueShortfall == 0) {\n return 0;\n }\n\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // Of the claimable withdrawal requests, how much is unclaimed?\n // That is, the amount of WETH that is currently allocated for the withdrawal queue\n uint256 allocatedWeth = queue.claimable - queue.claimed;\n\n // If there is no unallocated WETH then there is nothing to add to the queue\n if (wethBalance <= allocatedWeth) {\n return 0;\n }\n\n uint256 unallocatedWeth = wethBalance - allocatedWeth;\n\n // the new claimable amount is the smaller of the queue shortfall or unallocated weth\n addedClaimable = queueShortfall < unallocatedWeth\n ? queueShortfall\n : unallocatedWeth;\n uint256 newClaimable = queue.claimable + addedClaimable;\n\n // Store the new claimable amount back to storage\n withdrawalQueueMetadata.claimable = SafeCast.toUint128(newClaimable);\n\n // emit a WithdrawalClaimable event\n emit WithdrawalClaimable(newClaimable, addedClaimable);\n }\n\n /***************************************\n View Functions\n ****************************************/\n\n /// @dev Calculate how much WETH in the vault is not reserved for the withdrawal queue.\n // That is, it is available to be redeemed or deposited into a strategy.\n function _wethAvailable() internal view returns (uint256 wethAvailable) {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n uint256 outstandingWithdrawals = queue.queued - queue.claimed;\n\n // The amount of sitting in WETH in the vault\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // If there is not enough WETH in the vault to cover the outstanding withdrawals\n if (wethBalance <= outstandingWithdrawals) {\n return 0;\n }\n\n return wethBalance - outstandingWithdrawals;\n }\n\n /// @dev Get the balance of an asset held in Vault and all strategies\n /// less any WETH that is reserved for the withdrawal queue.\n /// WETH is the only asset that can return a non-zero balance.\n /// All other assets will return 0 even if there is some dust amounts left in the Vault.\n /// For example, there is 1 wei left of stETH in the OETH Vault but will return 0 in this function.\n ///\n /// If there is not enough WETH in the vault and all strategies to cover all outstanding\n /// withdrawal requests then return a WETH balance of 0\n function _checkBalance(address _asset)\n internal\n view\n override\n returns (uint256 balance)\n {\n if (_asset != weth) {\n return 0;\n }\n\n // Get the WETH in the vault and the strategies\n balance = super._checkBalance(_asset);\n\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // If the vault becomes insolvent enough that the total value in the vault and all strategies\n // is less than the outstanding withdrawals.\n // For example, there was a mass slashing event and most users request a withdrawal.\n if (balance + queue.claimed < queue.queued) {\n return 0;\n }\n\n // Need to remove WETH that is reserved for the withdrawal queue\n return balance + queue.claimed - queue.queued;\n }\n\n /**\n * @notice Allocate unallocated funds on Vault to strategies.\n **/\n function allocate() external override whenNotCapitalPaused nonReentrant {\n // Add any unallocated WETH to the withdrawal queue first\n _addWithdrawalQueueLiquidity();\n\n _allocate();\n }\n\n /// @dev Allocate WETH to the default WETH strategy if there is excess to the Vault buffer.\n /// This is called from either `mint` or `allocate` and assumes `_addWithdrawalQueueLiquidity`\n /// has been called before this function.\n function _allocate() internal override {\n // No need to do anything if no default strategy for WETH\n address depositStrategyAddr = assetDefaultStrategies[weth];\n if (depositStrategyAddr == address(0)) return;\n\n uint256 wethAvailableInVault = _wethAvailable();\n // No need to do anything if there isn't any WETH in the vault to allocate\n if (wethAvailableInVault == 0) return;\n\n // Calculate the target buffer for the vault using the total supply\n uint256 totalSupply = oUSD.totalSupply();\n uint256 targetBuffer = totalSupply.mulTruncate(vaultBuffer);\n\n // If available WETH in the Vault is below or equal the target buffer then there's nothing to allocate\n if (wethAvailableInVault <= targetBuffer) return;\n\n // The amount of assets to allocate to the default strategy\n uint256 allocateAmount = wethAvailableInVault - targetBuffer;\n\n IStrategy strategy = IStrategy(depositStrategyAddr);\n // Transfer WETH to the strategy and call the strategy's deposit function\n IERC20(weth).safeTransfer(address(strategy), allocateAmount);\n strategy.deposit(weth, allocateAmount);\n\n emit AssetAllocated(weth, depositStrategyAddr, allocateAmount);\n }\n\n /// @dev The total value of all WETH held by the vault and all its strategies\n /// less any WETH that is reserved for the withdrawal queue.\n ///\n // If there is not enough WETH in the vault and all strategies to cover all outstanding\n // withdrawal requests then return a total value of 0.\n function _totalValue() internal view override returns (uint256 value) {\n // As WETH is the only asset, just return the WETH balance\n return _checkBalance(weth);\n }\n\n /// @dev Only WETH is supported in the OETH Vault so return the WETH balance only\n /// Any ETH balances in the Vault will be ignored.\n /// Amounts from previously supported vault assets will also be ignored.\n /// For example, there is 1 wei left of stETH in the OETH Vault but is will be ignored.\n function _totalValueInVault()\n internal\n view\n override\n returns (uint256 value)\n {\n value = IERC20(weth).balanceOf(address(this));\n }\n}\n" + }, + "contracts/vault/OETHZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { ISfrxETH } from \"../interfaces/ISfrxETH.sol\";\n\ncontract OETHZapper {\n IERC20 public immutable oeth;\n IVault public immutable vault;\n\n IWETH9 public constant weth =\n IWETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);\n IERC20 public constant frxeth =\n IERC20(0x5E8422345238F34275888049021821E8E08CAa1f);\n ISfrxETH public constant sfrxeth =\n ISfrxETH(0xac3E018457B222d93114458476f3E3416Abbe38F);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(address _oeth, address _vault) {\n oeth = IERC20(_oeth);\n vault = IVault(_vault);\n\n weth.approve(address(_vault), type(uint256).max);\n frxeth.approve(address(_vault), type(uint256).max);\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return.\n * Will verify that the user is sent 1:1 for ETH.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return\n * Will verify that the user is sent 1:1 for ETH.\n * @return Amount of OETH sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n weth.deposit{ value: balance }();\n emit Zap(msg.sender, ETH_MARKER, balance);\n return _mint(address(weth), balance);\n }\n\n /**\n * @dev Deposit SFRXETH to the vault and receive OETH in return\n * @param amount Amount of SFRXETH to deposit\n * @param minOETH Minimum amount of OETH to receive\n * @return Amount of OETH sent to user\n */\n function depositSFRXETH(uint256 amount, uint256 minOETH)\n external\n returns (uint256)\n {\n sfrxeth.redeem(amount, address(this), msg.sender);\n emit Zap(msg.sender, address(sfrxeth), amount);\n return _mint(address(frxeth), minOETH);\n }\n\n /**\n * @dev Internal function to mint OETH from an asset\n * @param asset Address of asset for the vault to mint from\n * @param minOETH Minimum amount of OETH to for user to receive\n * @return Amount of OETH sent to user\n */\n function _mint(address asset, uint256 minOETH) internal returns (uint256) {\n uint256 toMint = IERC20(asset).balanceOf(address(this));\n vault.mint(asset, toMint, minOETH);\n uint256 mintedAmount = oeth.balanceOf(address(this));\n require(mintedAmount >= minOETH, \"Zapper: not enough minted\");\n require(oeth.transfer(msg.sender, mintedAmount));\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/OSonicVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"./OETHVaultAdmin.sol\";\n\n/**\n * @title Origin Sonic VaultAdmin contract on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonicVaultAdmin is OETHVaultAdmin {\n /// @param _wS Sonic's Wrapped S token\n constructor(address _wS) OETHVaultAdmin(_wS) {}\n\n /***************************************\n Asset Config\n ****************************************/\n\n /**\n * @notice Add a supported asset to the contract, i.e. one that can be to mint OTokens.\n * @dev Overridden to remove price provider integration\n * @param _asset Address of asset\n * @param _unitConversion 0 decimals, 1 exchange rate\n */\n function supportAsset(address _asset, uint8 _unitConversion)\n external\n override\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Asset already supported\");\n\n assets[_asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(_unitConversion),\n decimals: 0, // will be overridden in _cacheDecimals\n allowedOracleSlippageBps: 0 // 0% by default\n });\n\n _cacheDecimals(_asset);\n allAssets.push(_asset);\n\n emit AssetSupported(_asset);\n }\n}\n" + }, + "contracts/vault/OSonicVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultCore } from \"./OETHVaultCore.sol\";\n\n/**\n * @title Origin Sonic VaultCore contract on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonicVaultCore is OETHVaultCore {\n /// @param _wS Sonic's Wrapped S token\n constructor(address _wS) OETHVaultCore(_wS) {}\n\n /**\n * @notice Instant redeem is not supported on Sonic.\n * Use the asynchronous `requestWithdrawal` a `claimWithdrawal` instead.\n */\n function _redeem(uint256, uint256) internal override {\n revert(\"unsupported function\");\n }\n}\n" + }, + "contracts/vault/OSonicZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWrappedSonic } from \"../interfaces/sonic/IWrappedSonic.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\n\n/**\n * @title Zapper for Origin Sonic (OS) tokens\n * @author Origin Protocol Inc\n */\ncontract OSonicZapper {\n IERC20 public immutable OS;\n IERC4626 public immutable wOS;\n IVault public immutable vault;\n\n IWrappedSonic public constant wS =\n IWrappedSonic(0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(\n address _OS,\n address _wOS,\n address _vault\n ) {\n OS = IERC20(_OS);\n wOS = IERC4626(_wOS);\n vault = IVault(_vault);\n\n wS.approve(address(_vault), type(uint256).max);\n IERC20(_OS).approve(_wOS, type(uint256).max);\n }\n\n /**\n * @dev Deposit native S currency and receive Origin Sonic (OS) tokens in return.\n * Will verify that the user is sent 1:1 for S.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit native S currency and receive Origin Sonic (OS) tokens in return.\n * Will verify that the user is sent 1:1 for S.\n * @return Amount of Origin Sonic (OS) tokens sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap native S\n wS.deposit{ value: balance }();\n\n // Mint Origin Sonic (OS) with Wrapped Sonic (wS)\n return _mint(balance, msg.sender);\n }\n\n /**\n * @dev Deposit S and receive Wrapped Origin Sonic (wOS) in return\n * @param minReceived min amount of Wrapped Origin Sonic (wOS) to receive\n * @return Amount of Wrapped Origin Sonic (wOS) tokens sent to user\n */\n function depositSForWrappedTokens(uint256 minReceived)\n external\n payable\n returns (uint256)\n {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap S\n wS.deposit{ value: balance }();\n\n // Mint with Wrapped Sonic\n uint256 mintOS = _mint(balance, address(this));\n\n // Wrap Origin Sonic (OS) into Wrapped Origin Sonic (wOS)\n uint256 mintedWOS = wOS.deposit(mintOS, msg.sender);\n\n require(mintedWOS >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWOS;\n }\n\n /**\n * @dev Deposit Wrapped Sonic (wS) tokens and receive Wrapped Origin Sonic (wOS) tokens in return\n * @param wSAmount Amount of Wrapped Sonic (wS) to deposit\n * @param minReceived min amount of Wrapped Origin Sonic (wOS) token to receive\n * @return Amount of Wrapped Origin Sonic (wOS) tokens sent to user\n */\n function depositWSForWrappedTokens(uint256 wSAmount, uint256 minReceived)\n external\n returns (uint256)\n {\n // slither-disable-next-line unchecked-transfer unused-return\n wS.transferFrom(msg.sender, address(this), wSAmount);\n\n emit Zap(msg.sender, address(wS), wSAmount);\n\n // Mint with Wrapped Sonic (wS)\n uint256 mintedOS = _mint(wSAmount, address(this));\n\n // Wrap Origin Sonic (OS) tokens into Wrapped Origin Sonic (wOS) tokens\n uint256 mintedWOS = wOS.deposit(mintedOS, msg.sender);\n\n require(mintedWOS >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWOS;\n }\n\n /**\n * @dev Internal function to mint Origin Sonic (OS) with Wrapped S (wS)\n * @param minOS Minimum amount of Origin Sonic (OS) tokens the user can receive\n * @param recipient Address that receives the tokens\n * @return Amount of Origin Sonic (OS) tokens sent to the recipient\n */\n function _mint(uint256 minOS, address recipient)\n internal\n returns (uint256)\n {\n uint256 toMint = wS.balanceOf(address(this));\n vault.mint(address(wS), toMint, minOS);\n uint256 mintedAmount = OS.balanceOf(address(this));\n require(mintedAmount >= minOS, \"Zapper: not enough minted\");\n\n if (recipient != address(this)) {\n require(OS.transfer(recipient, mintedAmount));\n }\n\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/Vault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD VaultInitializer Contract\n * @notice The VaultInitializer sets up the initial contract.\n * @author Origin Protocol Inc\n */\nimport { VaultInitializer } from \"./VaultInitializer.sol\";\nimport { VaultAdmin } from \"./VaultAdmin.sol\";\n\ncontract Vault is VaultInitializer, VaultAdmin {}\n" + }, + "contracts/vault/VaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultAdmin contract\n * @notice The VaultAdmin contract makes configuration and admin calls on the vault.\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport \"./VaultStorage.sol\";\n\ncontract VaultAdmin is VaultStorage {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /***************************************\n Configuration\n ****************************************/\n\n /**\n * @notice Set address of price provider.\n * @param _priceProvider Address of price provider\n */\n function setPriceProvider(address _priceProvider) external onlyGovernor {\n priceProvider = _priceProvider;\n emit PriceProviderUpdated(_priceProvider);\n }\n\n /**\n * @notice Set a fee in basis points to be charged for a redeem.\n * @param _redeemFeeBps Basis point fee to be charged\n */\n function setRedeemFeeBps(uint256 _redeemFeeBps) external onlyGovernor {\n require(_redeemFeeBps <= 1000, \"Redeem fee should not be over 10%\");\n redeemFeeBps = _redeemFeeBps;\n emit RedeemFeeUpdated(_redeemFeeBps);\n }\n\n /**\n * @notice Set a buffer of assets to keep in the Vault to handle most\n * redemptions without needing to spend gas unwinding assets from a Strategy.\n * @param _vaultBuffer Percentage using 18 decimals. 100% = 1e18.\n */\n function setVaultBuffer(uint256 _vaultBuffer)\n external\n onlyGovernorOrStrategist\n {\n require(_vaultBuffer <= 1e18, \"Invalid value\");\n vaultBuffer = _vaultBuffer;\n emit VaultBufferUpdated(_vaultBuffer);\n }\n\n /**\n * @notice Sets the minimum amount of OTokens in a mint to trigger an\n * automatic allocation of funds afterwords.\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setAutoAllocateThreshold(uint256 _threshold)\n external\n onlyGovernor\n {\n autoAllocateThreshold = _threshold;\n emit AllocateThresholdUpdated(_threshold);\n }\n\n /**\n * @notice Set a minimum amount of OTokens in a mint or redeem that triggers a\n * rebase\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setRebaseThreshold(uint256 _threshold) external onlyGovernor {\n rebaseThreshold = _threshold;\n emit RebaseThresholdUpdated(_threshold);\n }\n\n /**\n * @notice Set address of Strategist\n * @param _address Address of Strategist\n */\n function setStrategistAddr(address _address) external onlyGovernor {\n strategistAddr = _address;\n emit StrategistUpdated(_address);\n }\n\n /**\n * @notice Set the default Strategy for an asset, i.e. the one which the asset\n will be automatically allocated to and withdrawn from\n * @param _asset Address of the asset\n * @param _strategy Address of the Strategy\n */\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external\n onlyGovernorOrStrategist\n {\n emit AssetDefaultStrategyUpdated(_asset, _strategy);\n // If its a zero address being passed for the strategy we are removing\n // the default strategy\n if (_strategy != address(0)) {\n // Make sure the strategy meets some criteria\n require(strategies[_strategy].isSupported, \"Strategy not approved\");\n IStrategy strategy = IStrategy(_strategy);\n require(assets[_asset].isSupported, \"Asset is not supported\");\n require(\n strategy.supportsAsset(_asset),\n \"Asset not supported by Strategy\"\n );\n }\n assetDefaultStrategies[_asset] = _strategy;\n }\n\n /**\n * @notice Set maximum amount of OTokens that can at any point be minted and deployed\n * to strategy (used only by ConvexOUSDMetaStrategy for now).\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setNetOusdMintForStrategyThreshold(uint256 _threshold)\n external\n onlyGovernor\n {\n /**\n * Because `netOusdMintedForStrategy` check in vault core works both ways\n * (positive and negative) the actual impact of the amount of OToken minted\n * could be double the threshold. E.g.:\n * - contract has threshold set to 100\n * - state of netOusdMinted is -90\n * - in effect it can mint 190 OToken and still be within limits\n *\n * We are somewhat mitigating this behaviour by resetting the netOusdMinted\n * counter whenever new threshold is set. So it can only move one threshold\n * amount in each direction. This also enables us to reduce the threshold\n * amount and not have problems with current netOusdMinted being near\n * limits on either side.\n */\n netOusdMintedForStrategy = 0;\n netOusdMintForStrategyThreshold = _threshold;\n emit NetOusdMintForStrategyThresholdChanged(_threshold);\n }\n\n /**\n * @notice Changes the async withdrawal claim period for OETH & superOETHb\n * @param _delay Delay period (should be between 10 mins to 7 days).\n * Set to 0 to disable async withdrawals\n */\n function setWithdrawalClaimDelay(uint256 _delay) external onlyGovernor {\n require(\n _delay == 0 || (_delay >= 10 minutes && _delay <= 15 days),\n \"Invalid claim delay period\"\n );\n withdrawalClaimDelay = _delay;\n emit WithdrawalClaimDelayUpdated(_delay);\n }\n\n /**\n * @notice Set a yield streaming max rate. This spreads yield over\n * time if it is above the max rate.\n * @param yearlyApr in 1e18 notation. 3 * 1e18 = 3% APR\n */\n function setRebaseRateMax(uint256 yearlyApr)\n external\n onlyGovernorOrStrategist\n {\n // The old yield will be at the old rate\n IVault(address(this)).rebase();\n // Change the rate\n uint256 newPerSecond = yearlyApr / 100 / 365 days;\n require(newPerSecond <= MAX_REBASE_PER_SECOND, \"Rate too high\");\n rebasePerSecondMax = newPerSecond.toUint64();\n emit RebasePerSecondMaxChanged(newPerSecond);\n }\n\n /**\n * @notice Set the drip duration period\n * @param _dripDuration Time in seconds to target a constant yield rate\n */\n function setDripDuration(uint256 _dripDuration)\n external\n onlyGovernorOrStrategist\n {\n // The old yield will be at the old rate\n IVault(address(this)).rebase();\n dripDuration = _dripDuration.toUint64();\n emit DripDurationChanged(_dripDuration);\n }\n\n /***************************************\n Swaps\n ****************************************/\n\n /**\n * @notice Strategist swaps collateral assets sitting in the vault.\n * @param _fromAsset The token address of the asset being sold by the vault.\n * @param _toAsset The token address of the asset being purchased by the vault.\n * @param _fromAssetAmount The amount of assets being sold by the vault.\n * @param _minToAssetAmount The minimum amount of assets to be purchased.\n * @param _data implementation specific data. eg 1Inch swap data\n * @return toAssetAmount The amount of toAssets that was received from the swap\n */\n function swapCollateral(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n )\n external\n nonReentrant\n onlyGovernorOrStrategist\n returns (uint256 toAssetAmount)\n {\n toAssetAmount = _swapCollateral(\n _fromAsset,\n _toAsset,\n _fromAssetAmount,\n _minToAssetAmount,\n _data\n );\n }\n\n function _swapCollateral(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n ) internal virtual returns (uint256 toAssetAmount) {\n // Check fromAsset and toAsset are valid\n Asset memory fromAssetConfig = assets[_fromAsset];\n Asset memory toAssetConfig = assets[_toAsset];\n require(fromAssetConfig.isSupported, \"From asset is not supported\");\n require(toAssetConfig.isSupported, \"To asset is not supported\");\n\n // Load swap config into memory to avoid separate SLOADs\n SwapConfig memory config = swapConfig;\n\n // Scope a new block to remove toAssetBalBefore from the scope of swapCollateral.\n // This avoids a stack too deep error.\n {\n uint256 toAssetBalBefore = IERC20(_toAsset).balanceOf(\n address(this)\n );\n\n // Transfer from assets to the swapper contract\n IERC20(_fromAsset).safeTransfer(config.swapper, _fromAssetAmount);\n\n // Call to the Swapper contract to do the actual swap\n // The -1 is required for stETH which sometimes transfers 1 wei less than what was specified.\n // slither-disable-next-line unused-return\n ISwapper(config.swapper).swap(\n _fromAsset,\n _toAsset,\n _fromAssetAmount - 1,\n _minToAssetAmount,\n _data\n );\n\n // Compute the change in asset balance held by the Vault\n toAssetAmount =\n IERC20(_toAsset).balanceOf(address(this)) -\n toAssetBalBefore;\n }\n\n // Check the to assets returned is above slippage amount specified by the strategist\n require(\n toAssetAmount >= _minToAssetAmount,\n \"Strategist slippage limit\"\n );\n\n // Scope a new block to remove minOracleToAssetAmount from the scope of swapCollateral.\n // This avoids a stack too deep error.\n {\n // Check the slippage against the Oracle in case the strategist made a mistake or has become malicious.\n // to asset amount = from asset amount * from asset price / to asset price\n uint256 minOracleToAssetAmount = (_fromAssetAmount *\n (1e4 - fromAssetConfig.allowedOracleSlippageBps) *\n IOracle(priceProvider).price(_fromAsset)) /\n (IOracle(priceProvider).price(_toAsset) *\n (1e4 + toAssetConfig.allowedOracleSlippageBps));\n\n // Scale both sides up to 18 decimals to compare\n require(\n toAssetAmount.scaleBy(18, toAssetConfig.decimals) >=\n minOracleToAssetAmount.scaleBy(\n 18,\n fromAssetConfig.decimals\n ),\n \"Oracle slippage limit exceeded\"\n );\n }\n\n // Check the vault's total value hasn't gone below the OToken total supply\n // by more than the allowed percentage.\n require(\n IVault(address(this)).totalValue() >=\n (oUSD.totalSupply() * ((1e4 - config.allowedUndervalueBps))) /\n 1e4,\n \"Allowed value < supply\"\n );\n\n emit Swapped(_fromAsset, _toAsset, _fromAssetAmount, toAssetAmount);\n }\n\n /***************************************\n Swap Config\n ****************************************/\n\n /**\n * @notice Set the contract the performs swaps of collateral assets.\n * @param _swapperAddr Address of the Swapper contract that implements the ISwapper interface.\n */\n function setSwapper(address _swapperAddr) external onlyGovernor {\n swapConfig.swapper = _swapperAddr;\n emit SwapperChanged(_swapperAddr);\n }\n\n /// @notice Contract that swaps the vault's collateral assets\n function swapper() external view returns (address swapper_) {\n swapper_ = swapConfig.swapper;\n }\n\n /**\n * @notice Set max allowed percentage the vault total value can drop below the OToken total supply in basis points\n * when executing collateral swaps.\n * @param _basis Percentage in basis points. eg 100 == 1%\n */\n function setSwapAllowedUndervalue(uint16 _basis) external onlyGovernor {\n require(_basis < 10001, \"Invalid basis points\");\n swapConfig.allowedUndervalueBps = _basis;\n emit SwapAllowedUndervalueChanged(_basis);\n }\n\n /**\n * @notice Max allowed percentage the vault total value can drop below the OToken total supply in basis points\n * when executing a collateral swap.\n * For example 100 == 1%\n * @return value Percentage in basis points.\n */\n function allowedSwapUndervalue() external view returns (uint256 value) {\n value = swapConfig.allowedUndervalueBps;\n }\n\n /**\n * @notice Set the allowed slippage from the Oracle price for collateral asset swaps.\n * @param _asset Address of the asset token.\n * @param _allowedOracleSlippageBps allowed slippage from Oracle in basis points. eg 20 = 0.2%. Max 10%.\n */\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external\n onlyGovernor\n {\n require(assets[_asset].isSupported, \"Asset not supported\");\n require(_allowedOracleSlippageBps < 1000, \"Slippage too high\");\n\n assets[_asset].allowedOracleSlippageBps = _allowedOracleSlippageBps;\n\n emit SwapSlippageChanged(_asset, _allowedOracleSlippageBps);\n }\n\n /***************************************\n Asset Config\n ****************************************/\n\n /**\n * @notice Add a supported asset to the contract, i.e. one that can be\n * to mint OTokens.\n * @param _asset Address of asset\n */\n function supportAsset(address _asset, uint8 _unitConversion)\n external\n virtual\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Asset already supported\");\n\n assets[_asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(_unitConversion),\n decimals: 0, // will be overridden in _cacheDecimals\n allowedOracleSlippageBps: 0 // 0% by default\n });\n\n _cacheDecimals(_asset);\n allAssets.push(_asset);\n\n // Verify that our oracle supports the asset\n // slither-disable-next-line unused-return\n IOracle(priceProvider).price(_asset);\n\n emit AssetSupported(_asset);\n }\n\n /**\n * @notice Remove a supported asset from the Vault\n * @param _asset Address of asset\n */\n function removeAsset(address _asset) external onlyGovernor {\n require(assets[_asset].isSupported, \"Asset not supported\");\n\n // 1e13 for 18 decimals. And 10 for 6 decimals\n uint256 maxDustBalance = uint256(1e13).scaleBy(\n assets[_asset].decimals,\n 18\n );\n\n require(\n IVault(address(this)).checkBalance(_asset) <= maxDustBalance,\n \"Vault still holds asset\"\n );\n\n uint256 assetsCount = allAssets.length;\n uint256 assetIndex = assetsCount; // initialize at invalid index\n for (uint256 i = 0; i < assetsCount; ++i) {\n if (allAssets[i] == _asset) {\n assetIndex = i;\n break;\n }\n }\n\n // Note: If asset is not found in `allAssets`, the following line\n // will revert with an out-of-bound error. However, there's no\n // reason why an asset would have `Asset.isSupported = true` but\n // not exist in `allAssets`.\n\n // Update allAssets array\n allAssets[assetIndex] = allAssets[assetsCount - 1];\n allAssets.pop();\n\n // Reset default strategy\n assetDefaultStrategies[_asset] = address(0);\n emit AssetDefaultStrategyUpdated(_asset, address(0));\n\n // Remove asset from storage\n delete assets[_asset];\n\n emit AssetRemoved(_asset);\n }\n\n /**\n * @notice Cache decimals on OracleRouter for a particular asset. This action\n * is required before that asset's price can be accessed.\n * @param _asset Address of asset token\n */\n function cacheDecimals(address _asset) external onlyGovernor {\n _cacheDecimals(_asset);\n }\n\n /***************************************\n Strategy Config\n ****************************************/\n\n /**\n * @notice Add a strategy to the Vault.\n * @param _addr Address of the strategy to add\n */\n function approveStrategy(address _addr) external onlyGovernor {\n require(!strategies[_addr].isSupported, \"Strategy already approved\");\n strategies[_addr] = Strategy({ isSupported: true, _deprecated: 0 });\n allStrategies.push(_addr);\n emit StrategyApproved(_addr);\n }\n\n /**\n * @notice Remove a strategy from the Vault.\n * @param _addr Address of the strategy to remove\n */\n\n function removeStrategy(address _addr) external onlyGovernor {\n require(strategies[_addr].isSupported, \"Strategy not approved\");\n\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n require(\n assetDefaultStrategies[allAssets[i]] != _addr,\n \"Strategy is default for an asset\"\n );\n }\n\n // Initialize strategyIndex with out of bounds result so function will\n // revert if no valid index found\n uint256 stratCount = allStrategies.length;\n uint256 strategyIndex = stratCount;\n for (uint256 i = 0; i < stratCount; ++i) {\n if (allStrategies[i] == _addr) {\n strategyIndex = i;\n break;\n }\n }\n\n if (strategyIndex < stratCount) {\n allStrategies[strategyIndex] = allStrategies[stratCount - 1];\n allStrategies.pop();\n\n // Mark the strategy as not supported\n strategies[_addr].isSupported = false;\n\n // Withdraw all assets\n IStrategy strategy = IStrategy(_addr);\n strategy.withdrawAll();\n\n emit StrategyRemoved(_addr);\n }\n }\n\n /***************************************\n Strategies\n ****************************************/\n\n /**\n * @notice Deposit multiple assets from the vault into the strategy.\n * @param _strategyToAddress Address of the Strategy to deposit assets into.\n * @param _assets Array of asset address that will be deposited into the strategy.\n * @param _amounts Array of amounts of each corresponding asset to deposit.\n */\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external onlyGovernorOrStrategist nonReentrant {\n _depositToStrategy(_strategyToAddress, _assets, _amounts);\n }\n\n function _depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal virtual {\n require(\n strategies[_strategyToAddress].isSupported,\n \"Invalid to Strategy\"\n );\n require(_assets.length == _amounts.length, \"Parameter length mismatch\");\n\n uint256 assetCount = _assets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n address assetAddr = _assets[i];\n require(\n IStrategy(_strategyToAddress).supportsAsset(assetAddr),\n \"Asset unsupported\"\n );\n // Send required amount of funds to the strategy\n IERC20(assetAddr).safeTransfer(_strategyToAddress, _amounts[i]);\n }\n\n // Deposit all the funds that have been sent to the strategy\n IStrategy(_strategyToAddress).depositAll();\n }\n\n /**\n * @notice Withdraw multiple assets from the strategy to the vault.\n * @param _strategyFromAddress Address of the Strategy to withdraw assets from.\n * @param _assets Array of asset address that will be withdrawn from the strategy.\n * @param _amounts Array of amounts of each corresponding asset to withdraw.\n */\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external onlyGovernorOrStrategist nonReentrant {\n _withdrawFromStrategy(\n address(this),\n _strategyFromAddress,\n _assets,\n _amounts\n );\n }\n\n /**\n * @param _recipient can either be a strategy or the Vault\n */\n function _withdrawFromStrategy(\n address _recipient,\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal virtual {\n require(\n strategies[_strategyFromAddress].isSupported,\n \"Invalid from Strategy\"\n );\n require(_assets.length == _amounts.length, \"Parameter length mismatch\");\n\n uint256 assetCount = _assets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n // Withdraw from Strategy to the recipient\n IStrategy(_strategyFromAddress).withdraw(\n _recipient,\n _assets[i],\n _amounts[i]\n );\n }\n }\n\n /**\n * @notice Sets the maximum allowable difference between\n * total supply and backing assets' value.\n */\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external onlyGovernor {\n maxSupplyDiff = _maxSupplyDiff;\n emit MaxSupplyDiffChanged(_maxSupplyDiff);\n }\n\n /**\n * @notice Sets the trusteeAddress that can receive a portion of yield.\n * Setting to the zero address disables this feature.\n */\n function setTrusteeAddress(address _address) external onlyGovernor {\n trusteeAddress = _address;\n emit TrusteeAddressChanged(_address);\n }\n\n /**\n * @notice Sets the TrusteeFeeBps to the percentage of yield that should be\n * received in basis points.\n */\n function setTrusteeFeeBps(uint256 _basis) external onlyGovernor {\n require(_basis <= 5000, \"basis cannot exceed 50%\");\n trusteeFeeBps = _basis;\n emit TrusteeFeeBpsChanged(_basis);\n }\n\n /**\n * @notice Set OToken Metapool strategy\n * @param _ousdMetaStrategy Address of OToken metapool strategy\n */\n function setOusdMetaStrategy(address _ousdMetaStrategy)\n external\n onlyGovernor\n {\n ousdMetaStrategy = _ousdMetaStrategy;\n emit OusdMetaStrategyUpdated(_ousdMetaStrategy);\n }\n\n /***************************************\n Pause\n ****************************************/\n\n /**\n * @notice Set the deposit paused flag to true to prevent rebasing.\n */\n function pauseRebase() external onlyGovernorOrStrategist {\n rebasePaused = true;\n emit RebasePaused();\n }\n\n /**\n * @notice Set the deposit paused flag to true to allow rebasing.\n */\n function unpauseRebase() external onlyGovernorOrStrategist {\n rebasePaused = false;\n emit RebaseUnpaused();\n }\n\n /**\n * @notice Set the deposit paused flag to true to prevent capital movement.\n */\n function pauseCapital() external onlyGovernorOrStrategist {\n capitalPaused = true;\n emit CapitalPaused();\n }\n\n /**\n * @notice Set the deposit paused flag to false to enable capital movement.\n */\n function unpauseCapital() external onlyGovernorOrStrategist {\n capitalPaused = false;\n emit CapitalUnpaused();\n }\n\n /***************************************\n Utils\n ****************************************/\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Only unsupported assets\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /***************************************\n Strategies Admin\n ****************************************/\n\n /**\n * @notice Withdraws all assets from the strategy and sends assets to the Vault.\n * @param _strategyAddr Strategy address.\n */\n function withdrawAllFromStrategy(address _strategyAddr)\n external\n onlyGovernorOrStrategist\n {\n _withdrawAllFromStrategy(_strategyAddr);\n }\n\n function _withdrawAllFromStrategy(address _strategyAddr) internal virtual {\n require(\n strategies[_strategyAddr].isSupported,\n \"Strategy is not supported\"\n );\n IStrategy strategy = IStrategy(_strategyAddr);\n strategy.withdrawAll();\n }\n\n /**\n * @notice Withdraws all assets from all the strategies and sends assets to the Vault.\n */\n function withdrawAllFromStrategies() external onlyGovernorOrStrategist {\n _withdrawAllFromStrategies();\n }\n\n function _withdrawAllFromStrategies() internal virtual {\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n IStrategy(allStrategies[i]).withdrawAll();\n }\n }\n\n /***************************************\n Utils\n ****************************************/\n\n function _cacheDecimals(address token) internal {\n Asset storage tokenAsset = assets[token];\n if (tokenAsset.decimals != 0) {\n return;\n }\n uint8 decimals = IBasicToken(token).decimals();\n require(decimals >= 6 && decimals <= 18, \"Unexpected precision\");\n tokenAsset.decimals = decimals;\n }\n}\n" + }, + "contracts/vault/VaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultCore contract\n * @notice The Vault contract stores assets. On a deposit, OTokens will be minted\n and sent to the depositor. On a withdrawal, OTokens will be burned and\n assets will be sent to the withdrawer. The Vault accepts deposits of\n interest from yield bearing strategies which will modify the supply\n of OTokens.\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IGetExchangeRateToken } from \"../interfaces/IGetExchangeRateToken.sol\";\n\nimport \"./VaultInitializer.sol\";\n\ncontract VaultCore is VaultInitializer {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n /// @dev max signed int\n uint256 internal constant MAX_INT = uint256(type(int256).max);\n\n /**\n * @dev Verifies that the rebasing is not paused.\n */\n modifier whenNotRebasePaused() {\n require(!rebasePaused, \"Rebasing paused\");\n _;\n }\n\n /**\n * @dev Verifies that the deposits are not paused.\n */\n modifier whenNotCapitalPaused() {\n require(!capitalPaused, \"Capital paused\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the AMO strategy.\n */\n modifier onlyOusdMetaStrategy() {\n require(\n msg.sender == ousdMetaStrategy,\n \"Caller is not the OUSD meta strategy\"\n );\n _;\n }\n\n /**\n * @notice Deposit a supported asset and mint OTokens.\n * @param _asset Address of the asset being deposited\n * @param _amount Amount of the asset being deposited\n * @param _minimumOusdAmount Minimum OTokens to mint\n */\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external whenNotCapitalPaused nonReentrant {\n _mint(_asset, _amount, _minimumOusdAmount);\n }\n\n /**\n * @dev Deposit a supported asset and mint OTokens.\n * @param _asset Address of the asset being deposited\n * @param _amount Amount of the asset being deposited\n * @param _minimumOusdAmount Minimum OTokens to mint\n */\n function _mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) internal virtual {\n require(assets[_asset].isSupported, \"Asset is not supported\");\n require(_amount > 0, \"Amount must be greater than 0\");\n\n uint256 units = _toUnits(_amount, _asset);\n uint256 unitPrice = _toUnitPrice(_asset, true);\n uint256 priceAdjustedDeposit = (units * unitPrice) / 1e18;\n\n if (_minimumOusdAmount > 0) {\n require(\n priceAdjustedDeposit >= _minimumOusdAmount,\n \"Mint amount lower than minimum\"\n );\n }\n\n emit Mint(msg.sender, priceAdjustedDeposit);\n\n // Rebase must happen before any transfers occur.\n if (priceAdjustedDeposit >= rebaseThreshold && !rebasePaused) {\n _rebase();\n }\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, priceAdjustedDeposit);\n\n // Transfer the deposited coins to the vault\n IERC20 asset = IERC20(_asset);\n asset.safeTransferFrom(msg.sender, address(this), _amount);\n\n if (priceAdjustedDeposit >= autoAllocateThreshold) {\n _allocate();\n }\n }\n\n /**\n * @notice Mint OTokens for a Metapool Strategy\n * @param _amount Amount of the asset being deposited\n *\n * Notice: can't use `nonReentrant` modifier since the `mint` function can\n * call `allocate`, and that can trigger `ConvexOUSDMetaStrategy` to call this function\n * while the execution of the `mint` has not yet completed -> causing a `nonReentrant` collision.\n *\n * Also important to understand is that this is a limitation imposed by the test suite.\n * Production / mainnet contracts should never be configured in a way where mint/redeem functions\n * that are moving funds between the Vault and end user wallets can influence strategies\n * utilizing this function.\n */\n function mintForStrategy(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n onlyOusdMetaStrategy\n {\n require(_amount < MAX_INT, \"Amount too high\");\n\n emit Mint(msg.sender, _amount);\n\n // safe to cast because of the require check at the beginning of the function\n netOusdMintedForStrategy += int256(_amount);\n\n require(\n abs(netOusdMintedForStrategy) < netOusdMintForStrategyThreshold,\n \"Minted ousd surpassed netOusdMintForStrategyThreshold.\"\n );\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, _amount);\n }\n\n // In memoriam\n\n /**\n * @notice Withdraw a supported asset and burn OTokens.\n * @param _amount Amount of OTokens to burn\n * @param _minimumUnitAmount Minimum stablecoin units to receive in return\n */\n function redeem(uint256 _amount, uint256 _minimumUnitAmount)\n external\n whenNotCapitalPaused\n nonReentrant\n {\n _redeem(_amount, _minimumUnitAmount);\n }\n\n /**\n * @notice Withdraw a supported asset and burn OTokens.\n * @param _amount Amount of OTokens to burn\n * @param _minimumUnitAmount Minimum stablecoin units to receive in return\n */\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n {\n // Calculate redemption outputs\n uint256[] memory outputs = _calculateRedeemOutputs(_amount);\n\n emit Redeem(msg.sender, _amount);\n\n // Send outputs\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n if (outputs[i] == 0) continue;\n\n address assetAddr = allAssets[i];\n\n if (IERC20(assetAddr).balanceOf(address(this)) >= outputs[i]) {\n // Use Vault funds first if sufficient\n IERC20(assetAddr).safeTransfer(msg.sender, outputs[i]);\n } else {\n address strategyAddr = assetDefaultStrategies[assetAddr];\n if (strategyAddr != address(0)) {\n // Nothing in Vault, but something in Strategy, send from there\n IStrategy strategy = IStrategy(strategyAddr);\n strategy.withdraw(msg.sender, assetAddr, outputs[i]);\n } else {\n // Cant find funds anywhere\n revert(\"Liquidity error\");\n }\n }\n }\n\n if (_minimumUnitAmount > 0) {\n uint256 unitTotal = 0;\n for (uint256 i = 0; i < outputs.length; ++i) {\n unitTotal += _toUnits(outputs[i], allAssets[i]);\n }\n require(\n unitTotal >= _minimumUnitAmount,\n \"Redeem amount lower than minimum\"\n );\n }\n\n oUSD.burn(msg.sender, _amount);\n\n _postRedeem(_amount);\n }\n\n function _postRedeem(uint256 _amount) internal {\n // Until we can prove that we won't affect the prices of our assets\n // by withdrawing them, this should be here.\n // It's possible that a strategy was off on its asset total, perhaps\n // a reward token sold for more or for less than anticipated.\n uint256 totalUnits = 0;\n if (_amount >= rebaseThreshold && !rebasePaused) {\n totalUnits = _rebase();\n } else {\n totalUnits = _totalValue();\n }\n\n // Check that the OTokens are backed by enough assets\n if (maxSupplyDiff > 0) {\n // If there are more outstanding withdrawal requests than assets in the vault and strategies\n // then the available assets will be negative and totalUnits will be rounded up to zero.\n // As we don't know the exact shortfall amount, we will reject all redeem and withdrawals\n require(totalUnits > 0, \"Too many outstanding requests\");\n\n // Allow a max difference of maxSupplyDiff% between\n // backing assets value and OUSD total supply\n uint256 diff = oUSD.totalSupply().divPrecisely(totalUnits);\n require(\n (diff > 1e18 ? diff - 1e18 : 1e18 - diff) <= maxSupplyDiff,\n \"Backing supply liquidity error\"\n );\n }\n }\n\n /**\n * @notice Burn OTokens for Metapool Strategy\n * @param _amount Amount of OUSD to burn\n *\n * @dev Notice: can't use `nonReentrant` modifier since the `redeem` function could\n * require withdrawal on `ConvexOUSDMetaStrategy` and that one can call `burnForStrategy`\n * while the execution of the `redeem` has not yet completed -> causing a `nonReentrant` collision.\n *\n * Also important to understand is that this is a limitation imposed by the test suite.\n * Production / mainnet contracts should never be configured in a way where mint/redeem functions\n * that are moving funds between the Vault and end user wallets can influence strategies\n * utilizing this function.\n */\n function burnForStrategy(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n onlyOusdMetaStrategy\n {\n require(_amount < MAX_INT, \"Amount too high\");\n\n emit Redeem(msg.sender, _amount);\n\n // safe to cast because of the require check at the beginning of the function\n netOusdMintedForStrategy -= int256(_amount);\n\n require(\n abs(netOusdMintedForStrategy) < netOusdMintForStrategyThreshold,\n \"Attempting to burn too much OUSD.\"\n );\n\n // Burn OTokens\n oUSD.burn(msg.sender, _amount);\n }\n\n /**\n * @notice Allocate unallocated funds on Vault to strategies.\n **/\n function allocate() external virtual whenNotCapitalPaused nonReentrant {\n _allocate();\n }\n\n /**\n * @dev Allocate unallocated funds on Vault to strategies.\n **/\n function _allocate() internal virtual {\n uint256 vaultValue = _totalValueInVault();\n // Nothing in vault to allocate\n if (vaultValue == 0) return;\n uint256 strategiesValue = _totalValueInStrategies();\n // We have a method that does the same as this, gas optimisation\n uint256 calculatedTotalValue = vaultValue + strategiesValue;\n\n // We want to maintain a buffer on the Vault so calculate a percentage\n // modifier to multiply each amount being allocated by to enforce the\n // vault buffer\n uint256 vaultBufferModifier;\n if (strategiesValue == 0) {\n // Nothing in Strategies, allocate 100% minus the vault buffer to\n // strategies\n vaultBufferModifier = uint256(1e18) - vaultBuffer;\n } else {\n vaultBufferModifier =\n (vaultBuffer * calculatedTotalValue) /\n vaultValue;\n if (1e18 > vaultBufferModifier) {\n // E.g. 1e18 - (1e17 * 10e18)/5e18 = 8e17\n // (5e18 * 8e17) / 1e18 = 4e18 allocated from Vault\n vaultBufferModifier = uint256(1e18) - vaultBufferModifier;\n } else {\n // We need to let the buffer fill\n return;\n }\n }\n if (vaultBufferModifier == 0) return;\n\n // Iterate over all assets in the Vault and allocate to the appropriate\n // strategy\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(allAssets[i]);\n uint256 assetBalance = asset.balanceOf(address(this));\n // No balance, nothing to do here\n if (assetBalance == 0) continue;\n\n // Multiply the balance by the vault buffer modifier and truncate\n // to the scale of the asset decimals\n uint256 allocateAmount = assetBalance.mulTruncate(\n vaultBufferModifier\n );\n\n address depositStrategyAddr = assetDefaultStrategies[\n address(asset)\n ];\n\n if (depositStrategyAddr != address(0) && allocateAmount > 0) {\n IStrategy strategy = IStrategy(depositStrategyAddr);\n // Transfer asset to Strategy and call deposit method to\n // mint or take required action\n asset.safeTransfer(address(strategy), allocateAmount);\n strategy.deposit(address(asset), allocateAmount);\n emit AssetAllocated(\n address(asset),\n depositStrategyAddr,\n allocateAmount\n );\n }\n }\n }\n\n /**\n * @notice Calculate the total value of assets held by the Vault and all\n * strategies and update the supply of OTokens.\n */\n function rebase() external virtual nonReentrant {\n _rebase();\n }\n\n /**\n * @dev Calculate the total value of assets held by the Vault and all\n * strategies and update the supply of OTokens, optionally sending a\n * portion of the yield to the trustee.\n * @return totalUnits Total balance of Vault in units\n */\n function _rebase() internal whenNotRebasePaused returns (uint256) {\n uint256 supply = oUSD.totalSupply();\n uint256 vaultValue = _totalValue();\n // If no supply yet, do not rebase\n if (supply == 0) {\n return vaultValue;\n }\n\n // Calculate yield and new supply\n (uint256 yield, uint256 targetRate) = _nextYield(supply, vaultValue);\n uint256 newSupply = supply + yield;\n // Only rebase upwards and if we have enough backing funds\n if (newSupply <= supply || newSupply > vaultValue) {\n return vaultValue;\n }\n\n rebasePerSecondTarget = uint64(_min(targetRate, type(uint64).max));\n lastRebase = uint64(block.timestamp); // Intentional cast\n\n // Fee collection on yield\n address _trusteeAddress = trusteeAddress; // gas savings\n uint256 fee = 0;\n if (_trusteeAddress != address(0)) {\n fee = (yield * trusteeFeeBps) / 1e4;\n if (fee > 0) {\n require(fee < yield, \"Fee must not be greater than yield\");\n oUSD.mint(_trusteeAddress, fee);\n }\n }\n emit YieldDistribution(_trusteeAddress, yield, fee);\n\n // Only ratchet OToken supply upwards\n // Final check uses latest totalSupply\n if (newSupply > oUSD.totalSupply()) {\n oUSD.changeSupply(newSupply);\n }\n return vaultValue;\n }\n\n /**\n * @notice Calculates the amount that would rebase at next rebase.\n * This is before any fees.\n * @return yield amount of expected yield\n */\n function previewYield() external view returns (uint256 yield) {\n (yield, ) = _nextYield(oUSD.totalSupply(), _totalValue());\n return yield;\n }\n\n function _nextYield(uint256 supply, uint256 vaultValue)\n internal\n view\n virtual\n returns (uint256 yield, uint256 targetRate)\n {\n uint256 nonRebasing = oUSD.nonRebasingSupply();\n uint256 rebasing = supply - nonRebasing;\n uint256 elapsed = block.timestamp - lastRebase;\n targetRate = rebasePerSecondTarget;\n\n if (\n elapsed == 0 || // Yield only once per block.\n rebasing == 0 || // No yield if there are no rebasing tokens to give it to.\n supply > vaultValue || // No yield if we do not have yield to give.\n block.timestamp >= type(uint64).max // No yield if we are too far in the future to calculate it correctly.\n ) {\n return (0, targetRate);\n }\n\n // Start with the full difference available\n yield = vaultValue - supply;\n\n // Cap via optional automatic duration smoothing\n uint256 _dripDuration = dripDuration;\n if (_dripDuration > 1) {\n // If we are able to sustain an increased drip rate for\n // double the duration, then increase the target drip rate\n targetRate = _max(targetRate, yield / (_dripDuration * 2));\n // If we cannot sustain the target rate any more,\n // then rebase what we can, and reduce the target\n targetRate = _min(targetRate, yield / _dripDuration);\n // drip at the new target rate\n yield = _min(yield, targetRate * elapsed);\n }\n\n // Cap per second. elapsed is not 1e18 denominated\n yield = _min(yield, (rebasing * elapsed * rebasePerSecondMax) / 1e18);\n\n // Cap at a hard max per rebase, to avoid long durations resulting in huge rebases\n yield = _min(yield, (rebasing * MAX_REBASE) / 1e18);\n\n return (yield, targetRate);\n }\n\n /**\n * @notice Determine the total value of assets held by the vault and its\n * strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function totalValue() external view virtual returns (uint256 value) {\n value = _totalValue();\n }\n\n /**\n * @dev Internal Calculate the total value of the assets held by the\n * vault and its strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValue() internal view virtual returns (uint256 value) {\n return _totalValueInVault() + _totalValueInStrategies();\n }\n\n /**\n * @dev Internal to calculate total value of all assets held in Vault.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInVault()\n internal\n view\n virtual\n returns (uint256 value)\n {\n uint256 assetCount = allAssets.length;\n for (uint256 y; y < assetCount; ++y) {\n address assetAddr = allAssets[y];\n uint256 balance = IERC20(assetAddr).balanceOf(address(this));\n if (balance > 0) {\n value += _toUnits(balance, assetAddr);\n }\n }\n }\n\n /**\n * @dev Internal to calculate total value of all assets held in Strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInStrategies() internal view returns (uint256 value) {\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n value = value + _totalValueInStrategy(allStrategies[i]);\n }\n }\n\n /**\n * @dev Internal to calculate total value of all assets held by strategy.\n * @param _strategyAddr Address of the strategy\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInStrategy(address _strategyAddr)\n internal\n view\n returns (uint256 value)\n {\n IStrategy strategy = IStrategy(_strategyAddr);\n uint256 assetCount = allAssets.length;\n for (uint256 y; y < assetCount; ++y) {\n address assetAddr = allAssets[y];\n if (strategy.supportsAsset(assetAddr)) {\n uint256 balance = strategy.checkBalance(assetAddr);\n if (balance > 0) {\n value += _toUnits(balance, assetAddr);\n }\n }\n }\n }\n\n /**\n * @notice Get the balance of an asset held in Vault and all strategies.\n * @param _asset Address of asset\n * @return uint256 Balance of asset in decimals of asset\n */\n function checkBalance(address _asset) external view returns (uint256) {\n return _checkBalance(_asset);\n }\n\n /**\n * @notice Get the balance of an asset held in Vault and all strategies.\n * @param _asset Address of asset\n * @return balance Balance of asset in decimals of asset\n */\n function _checkBalance(address _asset)\n internal\n view\n virtual\n returns (uint256 balance)\n {\n IERC20 asset = IERC20(_asset);\n balance = asset.balanceOf(address(this));\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n IStrategy strategy = IStrategy(allStrategies[i]);\n if (strategy.supportsAsset(_asset)) {\n balance = balance + strategy.checkBalance(_asset);\n }\n }\n }\n\n /**\n * @notice Calculate the outputs for a redeem function, i.e. the mix of\n * coins that will be returned\n */\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory)\n {\n return _calculateRedeemOutputs(_amount);\n }\n\n /**\n * @dev Calculate the outputs for a redeem function, i.e. the mix of\n * coins that will be returned.\n * @return outputs Array of amounts respective to the supported assets\n */\n function _calculateRedeemOutputs(uint256 _amount)\n internal\n view\n virtual\n returns (uint256[] memory outputs)\n {\n // We always give out coins in proportion to how many we have,\n // Now if all coins were the same value, this math would easy,\n // just take the percentage of each coin, and multiply by the\n // value to be given out. But if coins are worth more than $1,\n // then we would end up handing out too many coins. We need to\n // adjust by the total value of coins.\n //\n // To do this, we total up the value of our coins, by their\n // percentages. Then divide what we would otherwise give out by\n // this number.\n //\n // Let say we have 100 DAI at $1.06 and 200 USDT at $1.00.\n // So for every 1 DAI we give out, we'll be handing out 2 USDT\n // Our total output ratio is: 33% * 1.06 + 66% * 1.00 = 1.02\n //\n // So when calculating the output, we take the percentage of\n // each coin, times the desired output value, divided by the\n // totalOutputRatio.\n //\n // For example, withdrawing: 30 OUSD:\n // DAI 33% * 30 / 1.02 = 9.80 DAI\n // USDT = 66 % * 30 / 1.02 = 19.60 USDT\n //\n // Checking these numbers:\n // 9.80 DAI * 1.06 = $10.40\n // 19.60 USDT * 1.00 = $19.60\n //\n // And so the user gets $10.40 + $19.60 = $30 worth of value.\n\n uint256 assetCount = allAssets.length;\n uint256[] memory assetUnits = new uint256[](assetCount);\n uint256[] memory assetBalances = new uint256[](assetCount);\n outputs = new uint256[](assetCount);\n\n // Calculate redeem fee\n if (redeemFeeBps > 0) {\n uint256 redeemFee = _amount.mulTruncateScale(redeemFeeBps, 1e4);\n _amount = _amount - redeemFee;\n }\n\n // Calculate assets balances and decimals once,\n // for a large gas savings.\n uint256 totalUnits = 0;\n for (uint256 i = 0; i < assetCount; ++i) {\n address assetAddr = allAssets[i];\n uint256 balance = _checkBalance(assetAddr);\n assetBalances[i] = balance;\n assetUnits[i] = _toUnits(balance, assetAddr);\n totalUnits = totalUnits + assetUnits[i];\n }\n // Calculate totalOutputRatio\n uint256 totalOutputRatio = 0;\n for (uint256 i = 0; i < assetCount; ++i) {\n uint256 unitPrice = _toUnitPrice(allAssets[i], false);\n uint256 ratio = (assetUnits[i] * unitPrice) / totalUnits;\n totalOutputRatio = totalOutputRatio + ratio;\n }\n // Calculate final outputs\n uint256 factor = _amount.divPrecisely(totalOutputRatio);\n for (uint256 i = 0; i < assetCount; ++i) {\n outputs[i] = (assetBalances[i] * factor) / totalUnits;\n }\n }\n\n /***************************************\n Pricing\n ****************************************/\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * Never goes above 1, since that is how we price mints.\n * @param asset address of the asset\n * @return price uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed\n */\n function priceUnitMint(address asset)\n external\n view\n returns (uint256 price)\n {\n /* need to supply 1 asset unit in asset's decimals and can not just hard-code\n * to 1e18 and ignore calling `_toUnits` since we need to consider assets\n * with the exchange rate\n */\n uint256 units = _toUnits(\n uint256(1e18).scaleBy(_getDecimals(asset), 18),\n asset\n );\n price = (_toUnitPrice(asset, true) * units) / 1e18;\n }\n\n /**\n * @notice Returns the total price in 18 digit unit for a given asset.\n * Never goes below 1, since that is how we price redeems\n * @param asset Address of the asset\n * @return price uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed\n */\n function priceUnitRedeem(address asset)\n external\n view\n returns (uint256 price)\n {\n /* need to supply 1 asset unit in asset's decimals and can not just hard-code\n * to 1e18 and ignore calling `_toUnits` since we need to consider assets\n * with the exchange rate\n */\n uint256 units = _toUnits(\n uint256(1e18).scaleBy(_getDecimals(asset), 18),\n asset\n );\n price = (_toUnitPrice(asset, false) * units) / 1e18;\n }\n\n /***************************************\n Utils\n ****************************************/\n\n /**\n * @dev Convert a quantity of a token into 1e18 fixed decimal \"units\"\n * in the underlying base (USD/ETH) used by the vault.\n * Price is not taken into account, only quantity.\n *\n * Examples of this conversion:\n *\n * - 1e18 DAI becomes 1e18 units (same decimals)\n * - 1e6 USDC becomes 1e18 units (decimal conversion)\n * - 1e18 rETH becomes 1.2e18 units (exchange rate conversion)\n *\n * @param _raw Quantity of asset\n * @param _asset Core Asset address\n * @return value 1e18 normalized quantity of units\n */\n function _toUnits(uint256 _raw, address _asset)\n internal\n view\n returns (uint256)\n {\n UnitConversion conversion = assets[_asset].unitConversion;\n if (conversion == UnitConversion.DECIMALS) {\n return _raw.scaleBy(18, _getDecimals(_asset));\n } else if (conversion == UnitConversion.GETEXCHANGERATE) {\n uint256 exchangeRate = IGetExchangeRateToken(_asset)\n .getExchangeRate();\n return (_raw * exchangeRate) / 1e18;\n } else {\n revert(\"Unsupported conversion type\");\n }\n }\n\n /**\n * @dev Returns asset's unit price accounting for different asset types\n * and takes into account the context in which that price exists -\n * - mint or redeem.\n *\n * Note: since we are returning the price of the unit and not the one of the\n * asset (see comment above how 1 rETH exchanges for 1.2 units) we need\n * to make the Oracle price adjustment as well since we are pricing the\n * units and not the assets.\n *\n * The price also snaps to a \"full unit price\" in case a mint or redeem\n * action would be unfavourable to the protocol.\n *\n */\n function _toUnitPrice(address _asset, bool isMint)\n internal\n view\n returns (uint256 price)\n {\n UnitConversion conversion = assets[_asset].unitConversion;\n price = IOracle(priceProvider).price(_asset);\n\n if (conversion == UnitConversion.GETEXCHANGERATE) {\n uint256 exchangeRate = IGetExchangeRateToken(_asset)\n .getExchangeRate();\n price = (price * 1e18) / exchangeRate;\n } else if (conversion != UnitConversion.DECIMALS) {\n revert(\"Unsupported conversion type\");\n }\n\n /* At this stage the price is already adjusted to the unit\n * so the price checks are agnostic to underlying asset being\n * pegged to a USD or to an ETH or having a custom exchange rate.\n */\n require(price <= MAX_UNIT_PRICE_DRIFT, \"Vault: Price exceeds max\");\n require(price >= MIN_UNIT_PRICE_DRIFT, \"Vault: Price under min\");\n\n if (isMint) {\n /* Never price a normalized unit price for more than one\n * unit of OETH/OUSD when minting.\n */\n if (price > 1e18) {\n price = 1e18;\n }\n require(price >= MINT_MINIMUM_UNIT_PRICE, \"Asset price below peg\");\n } else {\n /* Never give out more than 1 normalized unit amount of assets\n * for one unit of OETH/OUSD when redeeming.\n */\n if (price < 1e18) {\n price = 1e18;\n }\n }\n }\n\n /**\n * @dev Get the number of decimals of a token asset\n * @param _asset Address of the asset\n * @return decimals number of decimals\n */\n function _getDecimals(address _asset)\n internal\n view\n returns (uint256 decimals)\n {\n decimals = assets[_asset].decimals;\n require(decimals > 0, \"Decimals not cached\");\n }\n\n /**\n * @notice Return the number of assets supported by the Vault.\n */\n function getAssetCount() public view returns (uint256) {\n return allAssets.length;\n }\n\n /**\n * @notice Gets the vault configuration of a supported asset.\n * @param _asset Address of the token asset\n */\n function getAssetConfig(address _asset)\n public\n view\n returns (Asset memory config)\n {\n config = assets[_asset];\n }\n\n /**\n * @notice Return all vault asset addresses in order\n */\n function getAllAssets() external view returns (address[] memory) {\n return allAssets;\n }\n\n /**\n * @notice Return the number of strategies active on the Vault.\n */\n function getStrategyCount() external view returns (uint256) {\n return allStrategies.length;\n }\n\n /**\n * @notice Return the array of all strategies\n */\n function getAllStrategies() external view returns (address[] memory) {\n return allStrategies;\n }\n\n /**\n * @notice Returns whether the vault supports the asset\n * @param _asset address of the asset\n * @return true if supported\n */\n function isSupportedAsset(address _asset) external view returns (bool) {\n return assets[_asset].isSupported;\n }\n\n function ADMIN_IMPLEMENTATION() external view returns (address adminImpl) {\n bytes32 slot = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n adminImpl := sload(slot)\n }\n }\n\n /**\n * @dev Falldown to the admin implementation\n * @notice This is a catch all for all functions not declared in core\n */\n // solhint-disable-next-line no-complex-fallback\n fallback() external {\n bytes32 slot = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(\n gas(),\n sload(slot),\n 0,\n calldatasize(),\n 0,\n 0\n )\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n function abs(int256 x) private pure returns (uint256) {\n require(x < int256(MAX_INT), \"Amount too high\");\n return x >= 0 ? uint256(x) : uint256(-x);\n }\n\n function _min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n function _max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a > b ? a : b;\n }\n}\n" + }, + "contracts/vault/VaultInitializer.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultInitializer contract\n * @notice The Vault contract initializes the vault.\n * @author Origin Protocol Inc\n */\n\nimport \"./VaultStorage.sol\";\n\ncontract VaultInitializer is VaultStorage {\n function initialize(address _priceProvider, address _oToken)\n external\n onlyGovernor\n initializer\n {\n require(_priceProvider != address(0), \"PriceProvider address is zero\");\n require(_oToken != address(0), \"oToken address is zero\");\n\n oUSD = OUSD(_oToken);\n\n priceProvider = _priceProvider;\n\n rebasePaused = false;\n capitalPaused = true;\n\n // Initial redeem fee of 0 basis points\n redeemFeeBps = 0;\n // Initial Vault buffer of 0%\n vaultBuffer = 0;\n // Initial allocate threshold of 25,000 OUSD\n autoAllocateThreshold = 25000e18;\n // Threshold for rebasing\n rebaseThreshold = 1000e18;\n // Initialize all strategies\n allStrategies = new address[](0);\n // Start with drip duration disabled\n dripDuration = 1;\n }\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + }, + "contracts/zapper/WOETHCCIPZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IRouterClient } from \"@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol\";\nimport { Client } from \"@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol\";\n// solhint-disable-next-line max-line-length\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"./../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IOETHZapper } from \"./../interfaces/IOETHZapper.sol\";\n\n/**\n * @title WOETH CCIP Zapper Contract\n * @notice Helps to directly convert ETH on mainnet into WOETH on L2s.\n * @author Origin Protocol Inc\n */\n\ncontract WOETHCCIPZapper {\n /**\n * @dev Event emitted when a zap occurs\n * @param messageId Unique message identifier for each zap\n * @param sender Address initiating the zap\n * @param recipient Recipient address at destination chain\n * @param amount Amount of ETH zapped\n */\n event Zap(\n bytes32 indexed messageId,\n address sender,\n address recipient,\n uint256 amount\n );\n\n // @dev Thrown when Zap amount is less than fee.\n error AmountLessThanFee();\n\n /**\n * @dev The destination chain selector\n */\n uint64 public immutable destinationChainSelector;\n\n /**\n * @dev The WOETH source chain (Mainnet)\n */\n IERC4626 public immutable woethOnSourceChain;\n\n /**\n * @dev The WOETH destination chain (Arbitrum)\n */\n IERC20 public immutable woethOnDestinationChain;\n\n /**\n * @dev The OETH zapper contract address\n */\n IOETHZapper public immutable oethZapper;\n\n /**\n * @dev The CCIP router contract address\n */\n IRouterClient public immutable ccipRouter;\n\n /**\n * @dev The OETH token contract address\n */\n IERC20 public immutable oeth;\n\n constructor(\n address _ccipRouter,\n uint64 _destinationChainSelector,\n IERC4626 _woethOnSourceChain,\n IERC20 _woethOnDestinationChain,\n IOETHZapper _oethZapper,\n IERC20 _oeth\n ) {\n ccipRouter = IRouterClient(_ccipRouter);\n destinationChainSelector = _destinationChainSelector;\n woethOnSourceChain = _woethOnSourceChain;\n woethOnDestinationChain = _woethOnDestinationChain;\n oethZapper = _oethZapper;\n oeth = _oeth;\n\n // Max allowance for Router and WOETH contracts\n _oeth.approve(address(_woethOnSourceChain), type(uint256).max); // for wrapping\n _woethOnSourceChain.approve(address(_ccipRouter), type(uint256).max); // for zapping\n }\n\n /**\n * @notice Accepts ETH, zaps for OETH, wraps it for WOETH and sends it to the destination chain (arbitrum)\n * @param receiver The address of the EOA on the destination chain\n * @return messageId The ID of the message that was sent\n */\n function zap(address receiver)\n external\n payable\n returns (bytes32 messageId)\n {\n return _zap(receiver, msg.value);\n }\n\n /**\n * @notice Used to estimate fee for CCIP transaction\n * @param amount The amount of ETH to be zapped\n * @param receiver The address of the EOA on the destination chain\n * @return feeAmount The CCIP tx fee in ETH.\n */\n\n function getFee(uint256 amount, address receiver)\n public\n view\n returns (uint256 feeAmount)\n {\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(woethOnSourceChain),\n amount: amount\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({\n receiver: abi.encode(receiver), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n feeAmount = ccipRouter.getFee(destinationChainSelector, message);\n }\n\n /**\n * @dev Deposit ETH and receive WOETH in L2.\n * @dev Note that the WOETH will be sent to the msg.sender at destination chain as well.\n */\n receive() external payable {\n _zap(msg.sender, msg.value);\n }\n\n function _zap(address receiver, uint256 amount)\n internal\n returns (bytes32 messageId)\n {\n // Estimate fee for zapping.\n uint256 feeAmount = getFee(amount, receiver);\n if (amount < feeAmount) {\n revert AmountLessThanFee();\n }\n\n // Convert only the msg.value - fees amount to WOETH.\n amount -= feeAmount;\n\n // 1.) Zap for OETH\n uint256 oethReceived = oethZapper.deposit{ value: amount }();\n\n // 2.) Wrap the received woeth\n uint256 woethReceived = woethOnSourceChain.deposit(\n oethReceived,\n address(this)\n );\n\n // 3.) Setup params for CCIP transfer\n\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(woethOnSourceChain),\n amount: woethReceived\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({\n receiver: abi.encode(receiver), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n // See: https://docs.chain.link/ccip/best-practices#setting-gaslimit\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n // ZAP ϟ\n //slither-disable-next-line arbitrary-send-eth\n messageId = ccipRouter.ccipSend{ value: feeAmount }(\n destinationChainSelector,\n message\n );\n\n // Emit Zap event with message details\n emit Zap(messageId, msg.sender, receiver, amount);\n\n // Return the message ID\n return messageId;\n }\n}\n" + }, + "hardhat/console.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.4.22 <0.9.0;\n\nlibrary console {\n address constant CONSOLE_ADDRESS =\n 0x000000000000000000636F6e736F6c652e6c6f67;\n\n function _sendLogPayloadImplementation(bytes memory payload) internal view {\n address consoleAddress = CONSOLE_ADDRESS;\n /// @solidity memory-safe-assembly\n assembly {\n pop(\n staticcall(\n gas(),\n consoleAddress,\n add(payload, 32),\n mload(payload),\n 0,\n 0\n )\n )\n }\n }\n\n function _castToPure(\n function(bytes memory) internal view fnIn\n ) internal pure returns (function(bytes memory) pure fnOut) {\n assembly {\n fnOut := fnIn\n }\n }\n\n function _sendLogPayload(bytes memory payload) internal pure {\n _castToPure(_sendLogPayloadImplementation)(payload);\n }\n\n function log() internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log()\"));\n }\n\n function logInt(int256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(int256)\", p0));\n }\n\n function logUint(uint256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256)\", p0));\n }\n\n function logString(string memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n }\n\n function logBool(bool p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n }\n\n function logAddress(address p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n }\n\n function logBytes(bytes memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes)\", p0));\n }\n\n function logBytes1(bytes1 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes1)\", p0));\n }\n\n function logBytes2(bytes2 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes2)\", p0));\n }\n\n function logBytes3(bytes3 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes3)\", p0));\n }\n\n function logBytes4(bytes4 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes4)\", p0));\n }\n\n function logBytes5(bytes5 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes5)\", p0));\n }\n\n function logBytes6(bytes6 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes6)\", p0));\n }\n\n function logBytes7(bytes7 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes7)\", p0));\n }\n\n function logBytes8(bytes8 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes8)\", p0));\n }\n\n function logBytes9(bytes9 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes9)\", p0));\n }\n\n function logBytes10(bytes10 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes10)\", p0));\n }\n\n function logBytes11(bytes11 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes11)\", p0));\n }\n\n function logBytes12(bytes12 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes12)\", p0));\n }\n\n function logBytes13(bytes13 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes13)\", p0));\n }\n\n function logBytes14(bytes14 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes14)\", p0));\n }\n\n function logBytes15(bytes15 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes15)\", p0));\n }\n\n function logBytes16(bytes16 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes16)\", p0));\n }\n\n function logBytes17(bytes17 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes17)\", p0));\n }\n\n function logBytes18(bytes18 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes18)\", p0));\n }\n\n function logBytes19(bytes19 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes19)\", p0));\n }\n\n function logBytes20(bytes20 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes20)\", p0));\n }\n\n function logBytes21(bytes21 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes21)\", p0));\n }\n\n function logBytes22(bytes22 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes22)\", p0));\n }\n\n function logBytes23(bytes23 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes23)\", p0));\n }\n\n function logBytes24(bytes24 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes24)\", p0));\n }\n\n function logBytes25(bytes25 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes25)\", p0));\n }\n\n function logBytes26(bytes26 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes26)\", p0));\n }\n\n function logBytes27(bytes27 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes27)\", p0));\n }\n\n function logBytes28(bytes28 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes28)\", p0));\n }\n\n function logBytes29(bytes29 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes29)\", p0));\n }\n\n function logBytes30(bytes30 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes30)\", p0));\n }\n\n function logBytes31(bytes31 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes31)\", p0));\n }\n\n function logBytes32(bytes32 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes32)\", p0));\n }\n\n function log(uint256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256)\", p0));\n }\n\n function log(string memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n }\n\n function log(bool p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n }\n\n function log(address p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n }\n\n function log(uint256 p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256)\", p0, p1));\n }\n\n function log(uint256 p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string)\", p0, p1));\n }\n\n function log(uint256 p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool)\", p0, p1));\n }\n\n function log(uint256 p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address)\", p0, p1));\n }\n\n function log(string memory p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256)\", p0, p1));\n }\n\n function log(string memory p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string)\", p0, p1));\n }\n\n function log(string memory p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool)\", p0, p1));\n }\n\n function log(string memory p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address)\", p0, p1));\n }\n\n function log(bool p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256)\", p0, p1));\n }\n\n function log(bool p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string)\", p0, p1));\n }\n\n function log(bool p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool)\", p0, p1));\n }\n\n function log(bool p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address)\", p0, p1));\n }\n\n function log(address p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256)\", p0, p1));\n }\n\n function log(address p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string)\", p0, p1));\n }\n\n function log(address p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool)\", p0, p1));\n }\n\n function log(address p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address)\", p0, p1));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,address)\", p0, p1, p2, p3));\n }\n}\n" + }, + "lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC4626 } from \"../../../../interfaces/IERC4626.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\n// From Open Zeppelin draft PR commit:\n// fac43034dca85ff539db3fc8aa2a7084b843d454\n// https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3171\n\nabstract contract ERC4626 is ERC20, IERC4626 {\n IERC20Metadata private immutable _asset;\n\n constructor(IERC20Metadata __asset) {\n _asset = __asset;\n }\n\n /** @dev See {IERC4262-asset} */\n function asset() public view virtual override returns (address) {\n return address(_asset);\n }\n\n /** @dev See {IERC4262-totalAssets} */\n function totalAssets() public view virtual override returns (uint256) {\n return _asset.balanceOf(address(this));\n }\n\n /**\n * @dev See {IERC4262-convertToShares}\n *\n * Will revert if asserts > 0, totalSupply > 0 and totalAssets = 0. That corresponds to a case where any asset\n * would represent an infinite amout of shares.\n */\n function convertToShares(uint256 assets) public view virtual override returns (uint256 shares) {\n uint256 supply = totalSupply();\n\n return\n (assets == 0 || supply == 0)\n ? (assets * 10**decimals()) / 10**_asset.decimals()\n : (assets * supply) / totalAssets();\n }\n\n /** @dev See {IERC4262-convertToAssets} */\n function convertToAssets(uint256 shares) public view virtual override returns (uint256 assets) {\n uint256 supply = totalSupply();\n\n return (supply == 0) ? (shares * 10**_asset.decimals()) / 10**decimals() : (shares * totalAssets()) / supply;\n }\n\n /** @dev See {IERC4262-maxDeposit} */\n function maxDeposit(address) public view virtual override returns (uint256) {\n return type(uint256).max;\n }\n\n /** @dev See {IERC4262-maxMint} */\n function maxMint(address) public view virtual override returns (uint256) {\n return type(uint256).max;\n }\n\n /** @dev See {IERC4262-maxWithdraw} */\n function maxWithdraw(address owner) public view virtual override returns (uint256) {\n return convertToAssets(balanceOf(owner));\n }\n\n /** @dev See {IERC4262-maxRedeem} */\n function maxRedeem(address owner) public view virtual override returns (uint256) {\n return balanceOf(owner);\n }\n\n /** @dev See {IERC4262-previewDeposit} */\n function previewDeposit(uint256 assets) public view virtual override returns (uint256) {\n return convertToShares(assets);\n }\n\n /** @dev See {IERC4262-previewMint} */\n function previewMint(uint256 shares) public view virtual override returns (uint256) {\n uint256 assets = convertToAssets(shares);\n return assets + (convertToShares(assets) < shares ? 1 : 0);\n }\n\n /** @dev See {IERC4262-previewWithdraw} */\n function previewWithdraw(uint256 assets) public view virtual override returns (uint256) {\n uint256 shares = convertToShares(assets);\n return shares + (convertToAssets(shares) < assets ? 1 : 0);\n }\n\n /** @dev See {IERC4262-previewRedeem} */\n function previewRedeem(uint256 shares) public view virtual override returns (uint256) {\n return convertToAssets(shares);\n }\n\n /** @dev See {IERC4262-deposit} */\n function deposit(uint256 assets, address receiver) public virtual override returns (uint256) {\n require(assets <= maxDeposit(receiver), \"ERC4626: deposit more then max\");\n\n address caller = _msgSender();\n uint256 shares = previewDeposit(assets);\n\n // if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through\n // the tokensToSend hook, so we need to transfer before we mint to keep the invariants.\n SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);\n _mint(receiver, shares);\n\n emit Deposit(caller, receiver, assets, shares);\n\n return shares;\n }\n\n /** @dev See {IERC4262-mint} */\n function mint(uint256 shares, address receiver) public virtual override returns (uint256) {\n require(shares <= maxMint(receiver), \"ERC4626: mint more then max\");\n\n address caller = _msgSender();\n uint256 assets = previewMint(shares);\n\n // if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through\n // the tokensToSend hook, so we need to transfer before we mint to keep the invariants.\n SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);\n _mint(receiver, shares);\n\n emit Deposit(caller, receiver, assets, shares);\n\n return assets;\n }\n\n /** @dev See {IERC4262-withdraw} */\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) public virtual override returns (uint256) {\n require(assets <= maxWithdraw(owner), \"ERC4626: withdraw more then max\");\n\n address caller = _msgSender();\n uint256 shares = previewWithdraw(assets);\n\n if (caller != owner) {\n _spendAllowance(owner, caller, shares);\n }\n\n // if _asset is ERC777, transfer can call reenter AFTER the transfer happens through\n // the tokensReceived hook, so we need to transfer after we burn to keep the invariants.\n _burn(owner, shares);\n SafeERC20.safeTransfer(_asset, receiver, assets);\n\n emit Withdraw(caller, receiver, owner, assets, shares);\n\n return shares;\n }\n\n /** @dev See {IERC4262-redeem} */\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) public virtual override returns (uint256) {\n require(shares <= maxRedeem(owner), \"ERC4626: redeem more then max\");\n\n address caller = _msgSender();\n uint256 assets = previewRedeem(shares);\n\n if (caller != owner) {\n _spendAllowance(owner, caller, shares);\n }\n\n // if _asset is ERC777, transfer can call reenter AFTER the transfer happens through\n // the tokensReceived hook, so we need to transfer after we burn to keep the invariants.\n _burn(owner, shares);\n SafeERC20.safeTransfer(_asset, receiver, assets);\n\n emit Withdraw(caller, receiver, owner, assets, shares);\n\n return assets;\n }\n\n // Included here, since this method was not yet present in\n // the version of Open Zeppelin ERC20 code we use.\n function _spendAllowance(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n uint256 currentAllowance = allowance(owner, spender);\n if (currentAllowance != type(uint256).max) {\n require(currentAllowance >= amount, \"ERC20: insufficient allowance\");\n unchecked {\n _approve(owner, spender, currentAllowance - amount);\n }\n }\n }\n}" + }, + "lib/openzeppelin/interfaces/IERC4626.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IERC4626 is IERC20, IERC20Metadata {\n event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);\n\n event Withdraw(\n address indexed caller,\n address indexed receiver,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n\n /**\n * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.\n *\n * - MUST be an ERC-20 token contract.\n * - MUST NOT revert.\n */\n function asset() external view returns (address assetTokenAddress);\n\n /**\n * @dev Returns the total amount of the underlying asset that is “managed” by Vault.\n *\n * - SHOULD include any compounding that occurs from yield.\n * - MUST be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT revert.\n */\n function totalAssets() external view returns (uint256 totalManagedAssets);\n\n /**\n * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal\n * scenario where all the conditions are met.\n *\n * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT show any variations depending on the caller.\n * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n * - MUST NOT revert.\n *\n * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n * from.\n */\n function convertToShares(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal\n * scenario where all the conditions are met.\n *\n * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT show any variations depending on the caller.\n * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n * - MUST NOT revert.\n *\n * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n * from.\n */\n function convertToAssets(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,\n * through a deposit call.\n *\n * - MUST return a limited value if receiver is subject to some deposit limit.\n * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.\n * - MUST NOT revert.\n */\n function maxDeposit(address receiver) external view returns (uint256 maxAssets);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given\n * current on-chain conditions.\n *\n * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit\n * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called\n * in the same transaction.\n * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the\n * deposit would be accepted, regardless if the user has enough tokens approved, etc.\n * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by depositing.\n */\n function previewDeposit(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.\n *\n * - MUST emit the Deposit event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * deposit execution, and are accounted for during deposit.\n * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not\n * approving enough underlying tokens to the Vault contract, etc).\n *\n * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n */\n function deposit(uint256 assets, address receiver) external returns (uint256 shares);\n\n /**\n * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.\n * - MUST return a limited value if receiver is subject to some mint limit.\n * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.\n * - MUST NOT revert.\n */\n function maxMint(address receiver) external view returns (uint256 maxShares);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given\n * current on-chain conditions.\n *\n * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call\n * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the\n * same transaction.\n * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint\n * would be accepted, regardless if the user has enough tokens approved, etc.\n * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by minting.\n */\n function previewMint(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.\n *\n * - MUST emit the Deposit event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint\n * execution, and are accounted for during mint.\n * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not\n * approving enough underlying tokens to the Vault contract, etc).\n *\n * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n */\n function mint(uint256 shares, address receiver) external returns (uint256 assets);\n\n /**\n * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the\n * Vault, through a withdraw call.\n *\n * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n * - MUST NOT revert.\n */\n function maxWithdraw(address owner) external view returns (uint256 maxAssets);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,\n * given current on-chain conditions.\n *\n * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw\n * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if\n * called\n * in the same transaction.\n * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though\n * the withdrawal would be accepted, regardless if the user has enough shares, etc.\n * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by depositing.\n */\n function previewWithdraw(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.\n *\n * - MUST emit the Withdraw event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * withdraw execution, and are accounted for during withdraw.\n * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner\n * not having enough shares, etc).\n *\n * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n * Those methods should be performed separately.\n */\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares);\n\n /**\n * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,\n * through a redeem call.\n *\n * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.\n * - MUST NOT revert.\n */\n function maxRedeem(address owner) external view returns (uint256 maxShares);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,\n * given current on-chain conditions.\n *\n * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call\n * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the\n * same transaction.\n * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the\n * redemption would be accepted, regardless if the user has enough shares, etc.\n * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by redeeming.\n */\n function previewRedeem(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.\n *\n * - MUST emit the Withdraw event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * redeem execution, and are accounted for during redeem.\n * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner\n * not having enough shares, etc).\n *\n * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n * Those methods should be performed separately.\n */\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets);\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.20;\n\nimport {Panic} from \"../Panic.sol\";\nimport {SafeCast} from \"./SafeCast.sol\";\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n enum Rounding {\n Floor, // Toward negative infinity\n Ceil, // Toward positive infinity\n Trunc, // Toward zero\n Expand // Away from zero\n }\n\n /**\n * @dev Return the 512-bit addition of two uint256.\n *\n * The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low.\n */\n function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {\n assembly (\"memory-safe\") {\n low := add(a, b)\n high := lt(low, a)\n }\n }\n\n /**\n * @dev Return the 512-bit multiplication of two uint256.\n *\n * The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low.\n */\n function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {\n // 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use\n // the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n // variables such that product = high * 2²⁵⁶ + low.\n assembly (\"memory-safe\") {\n let mm := mulmod(a, b, not(0))\n low := mul(a, b)\n high := sub(sub(mm, low), lt(mm, low))\n }\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, with a success flag (no overflow).\n */\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a + b;\n success = c >= a;\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).\n */\n function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a - b;\n success = c <= a;\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).\n */\n function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a * b;\n assembly (\"memory-safe\") {\n // Only true when the multiplication doesn't overflow\n // (c / a == b) || (a == 0)\n success := or(eq(div(c, a), b), iszero(a))\n }\n // equivalent to: success ? c : 0\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the division of two unsigned integers, with a success flag (no division by zero).\n */\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n success = b > 0;\n assembly (\"memory-safe\") {\n // The `DIV` opcode returns zero when the denominator is 0.\n result := div(a, b)\n }\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).\n */\n function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n success = b > 0;\n assembly (\"memory-safe\") {\n // The `MOD` opcode returns zero when the denominator is 0.\n result := mod(a, b)\n }\n }\n }\n\n /**\n * @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing.\n */\n function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {\n (bool success, uint256 result) = tryAdd(a, b);\n return ternary(success, result, type(uint256).max);\n }\n\n /**\n * @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.\n */\n function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {\n (, uint256 result) = trySub(a, b);\n return result;\n }\n\n /**\n * @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing.\n */\n function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {\n (bool success, uint256 result) = tryMul(a, b);\n return ternary(success, result, type(uint256).max);\n }\n\n /**\n * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.\n *\n * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.\n * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute\n * one branch when needed, making this function more expensive.\n */\n function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {\n unchecked {\n // branchless ternary works because:\n // b ^ (a ^ b) == a\n // b ^ 0 == b\n return b ^ ((a ^ b) * SafeCast.toUint(condition));\n }\n }\n\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return ternary(a > b, a, b);\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return ternary(a < b, a, b);\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds towards infinity instead\n * of rounding towards zero.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n if (b == 0) {\n // Guarantee the same behavior as in a regular Solidity division.\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n\n // The following calculation ensures accurate ceiling division without overflow.\n // Since a is non-zero, (a - 1) / b will not overflow.\n // The largest possible result occurs when (a - 1) / b is type(uint256).max,\n // but the largest value we can obtain is type(uint256).max - 1, which happens\n // when a = type(uint256).max and b = 1.\n unchecked {\n return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);\n }\n }\n\n /**\n * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or\n * denominator == 0.\n *\n * Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by\n * Uniswap Labs also under MIT license.\n */\n function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {\n unchecked {\n (uint256 high, uint256 low) = mul512(x, y);\n\n // Handle non-overflow cases, 256 by 256 division.\n if (high == 0) {\n // Solidity will revert if denominator == 0, unlike the div opcode on its own.\n // The surrounding unchecked block does not change this fact.\n // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.\n return low / denominator;\n }\n\n // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.\n if (denominator <= high) {\n Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));\n }\n\n ///////////////////////////////////////////////\n // 512 by 256 division.\n ///////////////////////////////////////////////\n\n // Make division exact by subtracting the remainder from [high low].\n uint256 remainder;\n assembly (\"memory-safe\") {\n // Compute remainder using mulmod.\n remainder := mulmod(x, y, denominator)\n\n // Subtract 256 bit number from 512 bit number.\n high := sub(high, gt(remainder, low))\n low := sub(low, remainder)\n }\n\n // Factor powers of two out of denominator and compute largest power of two divisor of denominator.\n // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.\n\n uint256 twos = denominator & (0 - denominator);\n assembly (\"memory-safe\") {\n // Divide denominator by twos.\n denominator := div(denominator, twos)\n\n // Divide [high low] by twos.\n low := div(low, twos)\n\n // Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.\n twos := add(div(sub(0, twos), twos), 1)\n }\n\n // Shift in bits from high into low.\n low |= high * twos;\n\n // Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such\n // that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for\n // four bits. That is, denominator * inv ≡ 1 mod 2⁴.\n uint256 inverse = (3 * denominator) ^ 2;\n\n // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also\n // works in modular arithmetic, doubling the correct bits in each step.\n inverse *= 2 - denominator * inverse; // inverse mod 2⁸\n inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶\n inverse *= 2 - denominator * inverse; // inverse mod 2³²\n inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴\n inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸\n inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶\n\n // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n // This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is\n // less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and high\n // is no longer required.\n result = low * inverse;\n return result;\n }\n }\n\n /**\n * @dev Calculates x * y / denominator with full precision, following the selected rounding direction.\n */\n function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {\n return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);\n }\n\n /**\n * @dev Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256.\n */\n function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) {\n unchecked {\n (uint256 high, uint256 low) = mul512(x, y);\n if (high >= 1 << n) {\n Panic.panic(Panic.UNDER_OVERFLOW);\n }\n return (high << (256 - n)) | (low >> n);\n }\n }\n\n /**\n * @dev Calculates x * y >> n with full precision, following the selected rounding direction.\n */\n function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) {\n return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 0);\n }\n\n /**\n * @dev Calculate the modular multiplicative inverse of a number in Z/nZ.\n *\n * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.\n * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.\n *\n * If the input value is not inversible, 0 is returned.\n *\n * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the\n * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.\n */\n function invMod(uint256 a, uint256 n) internal pure returns (uint256) {\n unchecked {\n if (n == 0) return 0;\n\n // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)\n // Used to compute integers x and y such that: ax + ny = gcd(a, n).\n // When the gcd is 1, then the inverse of a modulo n exists and it's x.\n // ax + ny = 1\n // ax = 1 + (-y)n\n // ax ≡ 1 (mod n) # x is the inverse of a modulo n\n\n // If the remainder is 0 the gcd is n right away.\n uint256 remainder = a % n;\n uint256 gcd = n;\n\n // Therefore the initial coefficients are:\n // ax + ny = gcd(a, n) = n\n // 0a + 1n = n\n int256 x = 0;\n int256 y = 1;\n\n while (remainder != 0) {\n uint256 quotient = gcd / remainder;\n\n (gcd, remainder) = (\n // The old remainder is the next gcd to try.\n remainder,\n // Compute the next remainder.\n // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd\n // where gcd is at most n (capped to type(uint256).max)\n gcd - remainder * quotient\n );\n\n (x, y) = (\n // Increment the coefficient of a.\n y,\n // Decrement the coefficient of n.\n // Can overflow, but the result is casted to uint256 so that the\n // next value of y is \"wrapped around\" to a value between 0 and n - 1.\n x - y * int256(quotient)\n );\n }\n\n if (gcd != 1) return 0; // No inverse exists.\n return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.\n }\n }\n\n /**\n * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.\n *\n * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is\n * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that\n * `a**(p-2)` is the modular multiplicative inverse of a in Fp.\n *\n * NOTE: this function does NOT check that `p` is a prime greater than `2`.\n */\n function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {\n unchecked {\n return Math.modExp(a, p - 2, p);\n }\n }\n\n /**\n * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)\n *\n * Requirements:\n * - modulus can't be zero\n * - underlying staticcall to precompile must succeed\n *\n * IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make\n * sure the chain you're using it on supports the precompiled contract for modular exponentiation\n * at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,\n * the underlying function will succeed given the lack of a revert, but the result may be incorrectly\n * interpreted as 0.\n */\n function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {\n (bool success, uint256 result) = tryModExp(b, e, m);\n if (!success) {\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n return result;\n }\n\n /**\n * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).\n * It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying\n * to operate modulo 0 or if the underlying precompile reverted.\n *\n * IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain\n * you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in\n * https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack\n * of a revert, but the result may be incorrectly interpreted as 0.\n */\n function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {\n if (m == 0) return (false, 0);\n assembly (\"memory-safe\") {\n let ptr := mload(0x40)\n // | Offset | Content | Content (Hex) |\n // |-----------|------------|--------------------------------------------------------------------|\n // | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x60:0x7f | value of b | 0x<.............................................................b> |\n // | 0x80:0x9f | value of e | 0x<.............................................................e> |\n // | 0xa0:0xbf | value of m | 0x<.............................................................m> |\n mstore(ptr, 0x20)\n mstore(add(ptr, 0x20), 0x20)\n mstore(add(ptr, 0x40), 0x20)\n mstore(add(ptr, 0x60), b)\n mstore(add(ptr, 0x80), e)\n mstore(add(ptr, 0xa0), m)\n\n // Given the result < m, it's guaranteed to fit in 32 bytes,\n // so we can use the memory scratch space located at offset 0.\n success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)\n result := mload(0x00)\n }\n }\n\n /**\n * @dev Variant of {modExp} that supports inputs of arbitrary length.\n */\n function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {\n (bool success, bytes memory result) = tryModExp(b, e, m);\n if (!success) {\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n return result;\n }\n\n /**\n * @dev Variant of {tryModExp} that supports inputs of arbitrary length.\n */\n function tryModExp(\n bytes memory b,\n bytes memory e,\n bytes memory m\n ) internal view returns (bool success, bytes memory result) {\n if (_zeroBytes(m)) return (false, new bytes(0));\n\n uint256 mLen = m.length;\n\n // Encode call args in result and move the free memory pointer\n result = abi.encodePacked(b.length, e.length, mLen, b, e, m);\n\n assembly (\"memory-safe\") {\n let dataPtr := add(result, 0x20)\n // Write result on top of args to avoid allocating extra memory.\n success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)\n // Overwrite the length.\n // result.length > returndatasize() is guaranteed because returndatasize() == m.length\n mstore(result, mLen)\n // Set the memory pointer after the returned data.\n mstore(0x40, add(dataPtr, mLen))\n }\n }\n\n /**\n * @dev Returns whether the provided byte array is zero.\n */\n function _zeroBytes(bytes memory byteArray) private pure returns (bool) {\n for (uint256 i = 0; i < byteArray.length; ++i) {\n if (byteArray[i] != 0) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded\n * towards zero.\n *\n * This method is based on Newton's method for computing square roots; the algorithm is restricted to only\n * using integer operations.\n */\n function sqrt(uint256 a) internal pure returns (uint256) {\n unchecked {\n // Take care of easy edge cases when a == 0 or a == 1\n if (a <= 1) {\n return a;\n }\n\n // In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a\n // sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between\n // the current value as `ε_n = | x_n - sqrt(a) |`.\n //\n // For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root\n // of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is\n // bigger than any uint256.\n //\n // By noticing that\n // `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`\n // we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar\n // to the msb function.\n uint256 aa = a;\n uint256 xn = 1;\n\n if (aa >= (1 << 128)) {\n aa >>= 128;\n xn <<= 64;\n }\n if (aa >= (1 << 64)) {\n aa >>= 64;\n xn <<= 32;\n }\n if (aa >= (1 << 32)) {\n aa >>= 32;\n xn <<= 16;\n }\n if (aa >= (1 << 16)) {\n aa >>= 16;\n xn <<= 8;\n }\n if (aa >= (1 << 8)) {\n aa >>= 8;\n xn <<= 4;\n }\n if (aa >= (1 << 4)) {\n aa >>= 4;\n xn <<= 2;\n }\n if (aa >= (1 << 2)) {\n xn <<= 1;\n }\n\n // We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).\n //\n // We can refine our estimation by noticing that the middle of that interval minimizes the error.\n // If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).\n // This is going to be our x_0 (and ε_0)\n xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)\n\n // From here, Newton's method give us:\n // x_{n+1} = (x_n + a / x_n) / 2\n //\n // One should note that:\n // x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a\n // = ((x_n² + a) / (2 * x_n))² - a\n // = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a\n // = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)\n // = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)\n // = (x_n² - a)² / (2 * x_n)²\n // = ((x_n² - a) / (2 * x_n))²\n // ≥ 0\n // Which proves that for all n ≥ 1, sqrt(a) ≤ x_n\n //\n // This gives us the proof of quadratic convergence of the sequence:\n // ε_{n+1} = | x_{n+1} - sqrt(a) |\n // = | (x_n + a / x_n) / 2 - sqrt(a) |\n // = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |\n // = | (x_n - sqrt(a))² / (2 * x_n) |\n // = | ε_n² / (2 * x_n) |\n // = ε_n² / | (2 * x_n) |\n //\n // For the first iteration, we have a special case where x_0 is known:\n // ε_1 = ε_0² / | (2 * x_0) |\n // ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))\n // ≤ 2**(2*e-4) / (3 * 2**(e-1))\n // ≤ 2**(e-3) / 3\n // ≤ 2**(e-3-log2(3))\n // ≤ 2**(e-4.5)\n //\n // For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:\n // ε_{n+1} = ε_n² / | (2 * x_n) |\n // ≤ (2**(e-k))² / (2 * 2**(e-1))\n // ≤ 2**(2*e-2*k) / 2**e\n // ≤ 2**(e-2*k)\n xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above\n xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5\n xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9\n xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18\n xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36\n xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72\n\n // Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision\n // ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either\n // sqrt(a) or sqrt(a) + 1.\n return xn - SafeCast.toUint(xn > a / xn);\n }\n }\n\n /**\n * @dev Calculates sqrt(a), following the selected rounding direction.\n */\n function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = sqrt(a);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);\n }\n }\n\n /**\n * @dev Return the log in base 2 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n */\n function log2(uint256 x) internal pure returns (uint256 r) {\n // If value has upper 128 bits set, log2 result is at least 128\n r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;\n // If upper 64 bits of 128-bit half set, add 64 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;\n // If upper 32 bits of 64-bit half set, add 32 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;\n // If upper 16 bits of 32-bit half set, add 16 to result\n r |= SafeCast.toUint((x >> r) > 0xffff) << 4;\n // If upper 8 bits of 16-bit half set, add 8 to result\n r |= SafeCast.toUint((x >> r) > 0xff) << 3;\n // If upper 4 bits of 8-bit half set, add 4 to result\n r |= SafeCast.toUint((x >> r) > 0xf) << 2;\n\n // Shifts value right by the current result and use it as an index into this lookup table:\n //\n // | x (4 bits) | index | table[index] = MSB position |\n // |------------|---------|-----------------------------|\n // | 0000 | 0 | table[0] = 0 |\n // | 0001 | 1 | table[1] = 0 |\n // | 0010 | 2 | table[2] = 1 |\n // | 0011 | 3 | table[3] = 1 |\n // | 0100 | 4 | table[4] = 2 |\n // | 0101 | 5 | table[5] = 2 |\n // | 0110 | 6 | table[6] = 2 |\n // | 0111 | 7 | table[7] = 2 |\n // | 1000 | 8 | table[8] = 3 |\n // | 1001 | 9 | table[9] = 3 |\n // | 1010 | 10 | table[10] = 3 |\n // | 1011 | 11 | table[11] = 3 |\n // | 1100 | 12 | table[12] = 3 |\n // | 1101 | 13 | table[13] = 3 |\n // | 1110 | 14 | table[14] = 3 |\n // | 1111 | 15 | table[15] = 3 |\n //\n // The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.\n assembly (\"memory-safe\") {\n r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))\n }\n }\n\n /**\n * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log2(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);\n }\n }\n\n /**\n * @dev Return the log in base 10 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n */\n function log10(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >= 10 ** 64) {\n value /= 10 ** 64;\n result += 64;\n }\n if (value >= 10 ** 32) {\n value /= 10 ** 32;\n result += 32;\n }\n if (value >= 10 ** 16) {\n value /= 10 ** 16;\n result += 16;\n }\n if (value >= 10 ** 8) {\n value /= 10 ** 8;\n result += 8;\n }\n if (value >= 10 ** 4) {\n value /= 10 ** 4;\n result += 4;\n }\n if (value >= 10 ** 2) {\n value /= 10 ** 2;\n result += 2;\n }\n if (value >= 10 ** 1) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log10(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);\n }\n }\n\n /**\n * @dev Return the log in base 256 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n *\n * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n */\n function log256(uint256 x) internal pure returns (uint256 r) {\n // If value has upper 128 bits set, log2 result is at least 128\n r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;\n // If upper 64 bits of 128-bit half set, add 64 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;\n // If upper 32 bits of 64-bit half set, add 32 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;\n // If upper 16 bits of 32-bit half set, add 16 to result\n r |= SafeCast.toUint((x >> r) > 0xffff) << 4;\n // Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8\n return (r >> 3) | SafeCast.toUint((x >> r) > 0xff);\n }\n\n /**\n * @dev Return the log in base 256, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log256(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);\n }\n }\n\n /**\n * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.\n */\n function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {\n return uint8(rounding) % 2 == 1;\n }\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)\n// This file was procedurally generated from scripts/generate/templates/SafeCast.js.\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n */\nlibrary SafeCast {\n /**\n * @dev Value doesn't fit in an uint of `bits` size.\n */\n error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);\n\n /**\n * @dev An int value doesn't fit in an uint of `bits` size.\n */\n error SafeCastOverflowedIntToUint(int256 value);\n\n /**\n * @dev Value doesn't fit in an int of `bits` size.\n */\n error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);\n\n /**\n * @dev An uint value doesn't fit in an int of `bits` size.\n */\n error SafeCastOverflowedUintToInt(uint256 value);\n\n /**\n * @dev Returns the downcasted uint248 from uint256, reverting on\n * overflow (when the input is greater than largest uint248).\n *\n * Counterpart to Solidity's `uint248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n */\n function toUint248(uint256 value) internal pure returns (uint248) {\n if (value > type(uint248).max) {\n revert SafeCastOverflowedUintDowncast(248, value);\n }\n return uint248(value);\n }\n\n /**\n * @dev Returns the downcasted uint240 from uint256, reverting on\n * overflow (when the input is greater than largest uint240).\n *\n * Counterpart to Solidity's `uint240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n */\n function toUint240(uint256 value) internal pure returns (uint240) {\n if (value > type(uint240).max) {\n revert SafeCastOverflowedUintDowncast(240, value);\n }\n return uint240(value);\n }\n\n /**\n * @dev Returns the downcasted uint232 from uint256, reverting on\n * overflow (when the input is greater than largest uint232).\n *\n * Counterpart to Solidity's `uint232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n */\n function toUint232(uint256 value) internal pure returns (uint232) {\n if (value > type(uint232).max) {\n revert SafeCastOverflowedUintDowncast(232, value);\n }\n return uint232(value);\n }\n\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n if (value > type(uint224).max) {\n revert SafeCastOverflowedUintDowncast(224, value);\n }\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint216 from uint256, reverting on\n * overflow (when the input is greater than largest uint216).\n *\n * Counterpart to Solidity's `uint216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n */\n function toUint216(uint256 value) internal pure returns (uint216) {\n if (value > type(uint216).max) {\n revert SafeCastOverflowedUintDowncast(216, value);\n }\n return uint216(value);\n }\n\n /**\n * @dev Returns the downcasted uint208 from uint256, reverting on\n * overflow (when the input is greater than largest uint208).\n *\n * Counterpart to Solidity's `uint208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n */\n function toUint208(uint256 value) internal pure returns (uint208) {\n if (value > type(uint208).max) {\n revert SafeCastOverflowedUintDowncast(208, value);\n }\n return uint208(value);\n }\n\n /**\n * @dev Returns the downcasted uint200 from uint256, reverting on\n * overflow (when the input is greater than largest uint200).\n *\n * Counterpart to Solidity's `uint200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n */\n function toUint200(uint256 value) internal pure returns (uint200) {\n if (value > type(uint200).max) {\n revert SafeCastOverflowedUintDowncast(200, value);\n }\n return uint200(value);\n }\n\n /**\n * @dev Returns the downcasted uint192 from uint256, reverting on\n * overflow (when the input is greater than largest uint192).\n *\n * Counterpart to Solidity's `uint192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n */\n function toUint192(uint256 value) internal pure returns (uint192) {\n if (value > type(uint192).max) {\n revert SafeCastOverflowedUintDowncast(192, value);\n }\n return uint192(value);\n }\n\n /**\n * @dev Returns the downcasted uint184 from uint256, reverting on\n * overflow (when the input is greater than largest uint184).\n *\n * Counterpart to Solidity's `uint184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n */\n function toUint184(uint256 value) internal pure returns (uint184) {\n if (value > type(uint184).max) {\n revert SafeCastOverflowedUintDowncast(184, value);\n }\n return uint184(value);\n }\n\n /**\n * @dev Returns the downcasted uint176 from uint256, reverting on\n * overflow (when the input is greater than largest uint176).\n *\n * Counterpart to Solidity's `uint176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n */\n function toUint176(uint256 value) internal pure returns (uint176) {\n if (value > type(uint176).max) {\n revert SafeCastOverflowedUintDowncast(176, value);\n }\n return uint176(value);\n }\n\n /**\n * @dev Returns the downcasted uint168 from uint256, reverting on\n * overflow (when the input is greater than largest uint168).\n *\n * Counterpart to Solidity's `uint168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n */\n function toUint168(uint256 value) internal pure returns (uint168) {\n if (value > type(uint168).max) {\n revert SafeCastOverflowedUintDowncast(168, value);\n }\n return uint168(value);\n }\n\n /**\n * @dev Returns the downcasted uint160 from uint256, reverting on\n * overflow (when the input is greater than largest uint160).\n *\n * Counterpart to Solidity's `uint160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n */\n function toUint160(uint256 value) internal pure returns (uint160) {\n if (value > type(uint160).max) {\n revert SafeCastOverflowedUintDowncast(160, value);\n }\n return uint160(value);\n }\n\n /**\n * @dev Returns the downcasted uint152 from uint256, reverting on\n * overflow (when the input is greater than largest uint152).\n *\n * Counterpart to Solidity's `uint152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n */\n function toUint152(uint256 value) internal pure returns (uint152) {\n if (value > type(uint152).max) {\n revert SafeCastOverflowedUintDowncast(152, value);\n }\n return uint152(value);\n }\n\n /**\n * @dev Returns the downcasted uint144 from uint256, reverting on\n * overflow (when the input is greater than largest uint144).\n *\n * Counterpart to Solidity's `uint144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n */\n function toUint144(uint256 value) internal pure returns (uint144) {\n if (value > type(uint144).max) {\n revert SafeCastOverflowedUintDowncast(144, value);\n }\n return uint144(value);\n }\n\n /**\n * @dev Returns the downcasted uint136 from uint256, reverting on\n * overflow (when the input is greater than largest uint136).\n *\n * Counterpart to Solidity's `uint136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n */\n function toUint136(uint256 value) internal pure returns (uint136) {\n if (value > type(uint136).max) {\n revert SafeCastOverflowedUintDowncast(136, value);\n }\n return uint136(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n if (value > type(uint128).max) {\n revert SafeCastOverflowedUintDowncast(128, value);\n }\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint120 from uint256, reverting on\n * overflow (when the input is greater than largest uint120).\n *\n * Counterpart to Solidity's `uint120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n */\n function toUint120(uint256 value) internal pure returns (uint120) {\n if (value > type(uint120).max) {\n revert SafeCastOverflowedUintDowncast(120, value);\n }\n return uint120(value);\n }\n\n /**\n * @dev Returns the downcasted uint112 from uint256, reverting on\n * overflow (when the input is greater than largest uint112).\n *\n * Counterpart to Solidity's `uint112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n */\n function toUint112(uint256 value) internal pure returns (uint112) {\n if (value > type(uint112).max) {\n revert SafeCastOverflowedUintDowncast(112, value);\n }\n return uint112(value);\n }\n\n /**\n * @dev Returns the downcasted uint104 from uint256, reverting on\n * overflow (when the input is greater than largest uint104).\n *\n * Counterpart to Solidity's `uint104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n */\n function toUint104(uint256 value) internal pure returns (uint104) {\n if (value > type(uint104).max) {\n revert SafeCastOverflowedUintDowncast(104, value);\n }\n return uint104(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n if (value > type(uint96).max) {\n revert SafeCastOverflowedUintDowncast(96, value);\n }\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint88 from uint256, reverting on\n * overflow (when the input is greater than largest uint88).\n *\n * Counterpart to Solidity's `uint88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n */\n function toUint88(uint256 value) internal pure returns (uint88) {\n if (value > type(uint88).max) {\n revert SafeCastOverflowedUintDowncast(88, value);\n }\n return uint88(value);\n }\n\n /**\n * @dev Returns the downcasted uint80 from uint256, reverting on\n * overflow (when the input is greater than largest uint80).\n *\n * Counterpart to Solidity's `uint80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n */\n function toUint80(uint256 value) internal pure returns (uint80) {\n if (value > type(uint80).max) {\n revert SafeCastOverflowedUintDowncast(80, value);\n }\n return uint80(value);\n }\n\n /**\n * @dev Returns the downcasted uint72 from uint256, reverting on\n * overflow (when the input is greater than largest uint72).\n *\n * Counterpart to Solidity's `uint72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n */\n function toUint72(uint256 value) internal pure returns (uint72) {\n if (value > type(uint72).max) {\n revert SafeCastOverflowedUintDowncast(72, value);\n }\n return uint72(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n if (value > type(uint64).max) {\n revert SafeCastOverflowedUintDowncast(64, value);\n }\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint56 from uint256, reverting on\n * overflow (when the input is greater than largest uint56).\n *\n * Counterpart to Solidity's `uint56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n */\n function toUint56(uint256 value) internal pure returns (uint56) {\n if (value > type(uint56).max) {\n revert SafeCastOverflowedUintDowncast(56, value);\n }\n return uint56(value);\n }\n\n /**\n * @dev Returns the downcasted uint48 from uint256, reverting on\n * overflow (when the input is greater than largest uint48).\n *\n * Counterpart to Solidity's `uint48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n */\n function toUint48(uint256 value) internal pure returns (uint48) {\n if (value > type(uint48).max) {\n revert SafeCastOverflowedUintDowncast(48, value);\n }\n return uint48(value);\n }\n\n /**\n * @dev Returns the downcasted uint40 from uint256, reverting on\n * overflow (when the input is greater than largest uint40).\n *\n * Counterpart to Solidity's `uint40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n */\n function toUint40(uint256 value) internal pure returns (uint40) {\n if (value > type(uint40).max) {\n revert SafeCastOverflowedUintDowncast(40, value);\n }\n return uint40(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n if (value > type(uint32).max) {\n revert SafeCastOverflowedUintDowncast(32, value);\n }\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint24 from uint256, reverting on\n * overflow (when the input is greater than largest uint24).\n *\n * Counterpart to Solidity's `uint24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n */\n function toUint24(uint256 value) internal pure returns (uint24) {\n if (value > type(uint24).max) {\n revert SafeCastOverflowedUintDowncast(24, value);\n }\n return uint24(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n if (value > type(uint16).max) {\n revert SafeCastOverflowedUintDowncast(16, value);\n }\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n if (value > type(uint8).max) {\n revert SafeCastOverflowedUintDowncast(8, value);\n }\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n if (value < 0) {\n revert SafeCastOverflowedIntToUint(value);\n }\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int248 from int256, reverting on\n * overflow (when the input is less than smallest int248 or\n * greater than largest int248).\n *\n * Counterpart to Solidity's `int248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n */\n function toInt248(int256 value) internal pure returns (int248 downcasted) {\n downcasted = int248(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(248, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int240 from int256, reverting on\n * overflow (when the input is less than smallest int240 or\n * greater than largest int240).\n *\n * Counterpart to Solidity's `int240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n */\n function toInt240(int256 value) internal pure returns (int240 downcasted) {\n downcasted = int240(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(240, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int232 from int256, reverting on\n * overflow (when the input is less than smallest int232 or\n * greater than largest int232).\n *\n * Counterpart to Solidity's `int232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n */\n function toInt232(int256 value) internal pure returns (int232 downcasted) {\n downcasted = int232(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(232, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int224 from int256, reverting on\n * overflow (when the input is less than smallest int224 or\n * greater than largest int224).\n *\n * Counterpart to Solidity's `int224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toInt224(int256 value) internal pure returns (int224 downcasted) {\n downcasted = int224(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(224, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int216 from int256, reverting on\n * overflow (when the input is less than smallest int216 or\n * greater than largest int216).\n *\n * Counterpart to Solidity's `int216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n */\n function toInt216(int256 value) internal pure returns (int216 downcasted) {\n downcasted = int216(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(216, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int208 from int256, reverting on\n * overflow (when the input is less than smallest int208 or\n * greater than largest int208).\n *\n * Counterpart to Solidity's `int208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n */\n function toInt208(int256 value) internal pure returns (int208 downcasted) {\n downcasted = int208(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(208, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int200 from int256, reverting on\n * overflow (when the input is less than smallest int200 or\n * greater than largest int200).\n *\n * Counterpart to Solidity's `int200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n */\n function toInt200(int256 value) internal pure returns (int200 downcasted) {\n downcasted = int200(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(200, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int192 from int256, reverting on\n * overflow (when the input is less than smallest int192 or\n * greater than largest int192).\n *\n * Counterpart to Solidity's `int192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n */\n function toInt192(int256 value) internal pure returns (int192 downcasted) {\n downcasted = int192(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(192, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int184 from int256, reverting on\n * overflow (when the input is less than smallest int184 or\n * greater than largest int184).\n *\n * Counterpart to Solidity's `int184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n */\n function toInt184(int256 value) internal pure returns (int184 downcasted) {\n downcasted = int184(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(184, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int176 from int256, reverting on\n * overflow (when the input is less than smallest int176 or\n * greater than largest int176).\n *\n * Counterpart to Solidity's `int176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n */\n function toInt176(int256 value) internal pure returns (int176 downcasted) {\n downcasted = int176(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(176, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int168 from int256, reverting on\n * overflow (when the input is less than smallest int168 or\n * greater than largest int168).\n *\n * Counterpart to Solidity's `int168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n */\n function toInt168(int256 value) internal pure returns (int168 downcasted) {\n downcasted = int168(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(168, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int160 from int256, reverting on\n * overflow (when the input is less than smallest int160 or\n * greater than largest int160).\n *\n * Counterpart to Solidity's `int160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n */\n function toInt160(int256 value) internal pure returns (int160 downcasted) {\n downcasted = int160(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(160, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int152 from int256, reverting on\n * overflow (when the input is less than smallest int152 or\n * greater than largest int152).\n *\n * Counterpart to Solidity's `int152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n */\n function toInt152(int256 value) internal pure returns (int152 downcasted) {\n downcasted = int152(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(152, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int144 from int256, reverting on\n * overflow (when the input is less than smallest int144 or\n * greater than largest int144).\n *\n * Counterpart to Solidity's `int144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n */\n function toInt144(int256 value) internal pure returns (int144 downcasted) {\n downcasted = int144(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(144, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int136 from int256, reverting on\n * overflow (when the input is less than smallest int136 or\n * greater than largest int136).\n *\n * Counterpart to Solidity's `int136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n */\n function toInt136(int256 value) internal pure returns (int136 downcasted) {\n downcasted = int136(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(136, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toInt128(int256 value) internal pure returns (int128 downcasted) {\n downcasted = int128(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(128, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int120 from int256, reverting on\n * overflow (when the input is less than smallest int120 or\n * greater than largest int120).\n *\n * Counterpart to Solidity's `int120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n */\n function toInt120(int256 value) internal pure returns (int120 downcasted) {\n downcasted = int120(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(120, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int112 from int256, reverting on\n * overflow (when the input is less than smallest int112 or\n * greater than largest int112).\n *\n * Counterpart to Solidity's `int112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n */\n function toInt112(int256 value) internal pure returns (int112 downcasted) {\n downcasted = int112(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(112, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int104 from int256, reverting on\n * overflow (when the input is less than smallest int104 or\n * greater than largest int104).\n *\n * Counterpart to Solidity's `int104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n */\n function toInt104(int256 value) internal pure returns (int104 downcasted) {\n downcasted = int104(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(104, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int96 from int256, reverting on\n * overflow (when the input is less than smallest int96 or\n * greater than largest int96).\n *\n * Counterpart to Solidity's `int96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toInt96(int256 value) internal pure returns (int96 downcasted) {\n downcasted = int96(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(96, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int88 from int256, reverting on\n * overflow (when the input is less than smallest int88 or\n * greater than largest int88).\n *\n * Counterpart to Solidity's `int88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n */\n function toInt88(int256 value) internal pure returns (int88 downcasted) {\n downcasted = int88(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(88, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int80 from int256, reverting on\n * overflow (when the input is less than smallest int80 or\n * greater than largest int80).\n *\n * Counterpart to Solidity's `int80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n */\n function toInt80(int256 value) internal pure returns (int80 downcasted) {\n downcasted = int80(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(80, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int72 from int256, reverting on\n * overflow (when the input is less than smallest int72 or\n * greater than largest int72).\n *\n * Counterpart to Solidity's `int72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n */\n function toInt72(int256 value) internal pure returns (int72 downcasted) {\n downcasted = int72(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(72, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toInt64(int256 value) internal pure returns (int64 downcasted) {\n downcasted = int64(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(64, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int56 from int256, reverting on\n * overflow (when the input is less than smallest int56 or\n * greater than largest int56).\n *\n * Counterpart to Solidity's `int56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n */\n function toInt56(int256 value) internal pure returns (int56 downcasted) {\n downcasted = int56(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(56, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int48 from int256, reverting on\n * overflow (when the input is less than smallest int48 or\n * greater than largest int48).\n *\n * Counterpart to Solidity's `int48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n */\n function toInt48(int256 value) internal pure returns (int48 downcasted) {\n downcasted = int48(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(48, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int40 from int256, reverting on\n * overflow (when the input is less than smallest int40 or\n * greater than largest int40).\n *\n * Counterpart to Solidity's `int40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n */\n function toInt40(int256 value) internal pure returns (int40 downcasted) {\n downcasted = int40(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(40, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toInt32(int256 value) internal pure returns (int32 downcasted) {\n downcasted = int32(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(32, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int24 from int256, reverting on\n * overflow (when the input is less than smallest int24 or\n * greater than largest int24).\n *\n * Counterpart to Solidity's `int24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n */\n function toInt24(int256 value) internal pure returns (int24 downcasted) {\n downcasted = int24(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(24, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toInt16(int256 value) internal pure returns (int16 downcasted) {\n downcasted = int16(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(16, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n */\n function toInt8(int256 value) internal pure returns (int8 downcasted) {\n downcasted = int8(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(8, value);\n }\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n if (value > uint256(type(int256).max)) {\n revert SafeCastOverflowedUintToInt(value);\n }\n return int256(value);\n }\n\n /**\n * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.\n */\n function toUint(bool b) internal pure returns (uint256 u) {\n assembly (\"memory-safe\") {\n u := iszero(iszero(b))\n }\n }\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/Panic.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Helper library for emitting standardized panic codes.\n *\n * ```solidity\n * contract Example {\n * using Panic for uint256;\n *\n * // Use any of the declared internal constants\n * function foo() { Panic.GENERIC.panic(); }\n *\n * // Alternatively\n * function foo() { Panic.panic(Panic.GENERIC); }\n * }\n * ```\n *\n * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].\n *\n * _Available since v5.1._\n */\n// slither-disable-next-line unused-state\nlibrary Panic {\n /// @dev generic / unspecified error\n uint256 internal constant GENERIC = 0x00;\n /// @dev used by the assert() builtin\n uint256 internal constant ASSERT = 0x01;\n /// @dev arithmetic underflow or overflow\n uint256 internal constant UNDER_OVERFLOW = 0x11;\n /// @dev division or modulo by zero\n uint256 internal constant DIVISION_BY_ZERO = 0x12;\n /// @dev enum conversion error\n uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;\n /// @dev invalid encoding in storage\n uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;\n /// @dev empty array pop\n uint256 internal constant EMPTY_ARRAY_POP = 0x31;\n /// @dev array out of bounds access\n uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;\n /// @dev resource error (too large allocation or too large array)\n uint256 internal constant RESOURCE_ERROR = 0x41;\n /// @dev calling invalid internal function\n uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;\n\n /// @dev Reverts with a panic code. Recommended to use with\n /// the internal constants with predefined codes.\n function panic(uint256 code) internal pure {\n assembly (\"memory-safe\") {\n mstore(0x00, 0x4e487b71)\n mstore(0x20, code)\n revert(0x1c, 0x24)\n }\n }\n}" + }, + "lib/rooster/v2-common/libraries/Constants.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\n// factory contraints on pools\nuint8 constant MAX_PROTOCOL_FEE_RATIO_D3 = 0.25e3; // 25%\nuint256 constant MAX_PROTOCOL_LENDING_FEE_RATE_D18 = 0.02e18; // 2%\nuint64 constant MAX_POOL_FEE_D18 = 0.9e18; // 90%\nuint64 constant MIN_LOOKBACK = 1 seconds;\n\n// pool constraints\nuint8 constant NUMBER_OF_KINDS = 4;\nint32 constant NUMBER_OF_KINDS_32 = int32(int8(NUMBER_OF_KINDS));\nuint256 constant MAX_TICK = 322_378; // max price 1e14 in D18 scale\nint32 constant MAX_TICK_32 = int32(int256(MAX_TICK));\nint32 constant MIN_TICK_32 = int32(-int256(MAX_TICK));\nuint256 constant MAX_BINS_TO_MERGE = 3;\nuint128 constant MINIMUM_LIQUIDITY = 1e8;\n\n// accessor named constants\nuint8 constant ALL_KINDS_MASK = 0xF; // 0b1111\nuint8 constant PERMISSIONED_LIQUIDITY_MASK = 0x10; // 0b010000\nuint8 constant PERMISSIONED_SWAP_MASK = 0x20; // 0b100000\nuint8 constant OPTIONS_MASK = ALL_KINDS_MASK | PERMISSIONED_LIQUIDITY_MASK | PERMISSIONED_SWAP_MASK; // 0b111111\n\n// named values\naddress constant MERGED_LP_BALANCE_ADDRESS = address(0);\nuint256 constant MERGED_LP_BALANCE_SUBACCOUNT = 0;\nuint128 constant ONE = 1e18;\nuint128 constant ONE_SQUARED = 1e36;\nint256 constant INT256_ONE = 1e18;\nuint256 constant ONE_D8 = 1e8;\nuint256 constant ONE_D3 = 1e3;\nint40 constant INT_ONE_D8 = 1e8;\nint40 constant HALF_TICK_D8 = 0.5e8;\nuint8 constant DEFAULT_DECIMALS = 18;\nuint256 constant DEFAULT_SCALE = 1;\nbytes constant EMPTY_PRICE_BREAKS = hex\"010000000000000000000000\";" + }, + "lib/rooster/v2-common/libraries/Math.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport {Math as OzMath} from \"../../openzeppelin-custom/contracts/utils/math/Math.sol\";\n\nimport {ONE, DEFAULT_SCALE, DEFAULT_DECIMALS, INT_ONE_D8, ONE_SQUARED} from \"./Constants.sol\";\n\n/**\n * @notice Math functions.\n */\nlibrary Math {\n /**\n * @notice Returns the lesser of two values.\n * @param x First uint256 value.\n * @param y Second uint256 value.\n */\n function min(uint256 x, uint256 y) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), lt(y, x)))\n }\n }\n\n /**\n * @notice Returns the lesser of two uint128 values.\n * @param x First uint128 value.\n * @param y Second uint128 value.\n */\n function min128(uint128 x, uint128 y) internal pure returns (uint128 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), lt(y, x)))\n }\n }\n\n /**\n * @notice Returns the lesser of two int256 values.\n * @param x First int256 value.\n * @param y Second int256 value.\n */\n function min(int256 x, int256 y) internal pure returns (int256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), slt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two uint256 values.\n * @param x First uint256 value.\n * @param y Second uint256 value.\n */\n function max(uint256 x, uint256 y) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), gt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two int256 values.\n * @param x First int256 value.\n * @param y Second int256 value.\n */\n function max(int256 x, int256 y) internal pure returns (int256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), sgt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two uint128 values.\n * @param x First uint128 value.\n * @param y Second uint128 value.\n */\n function max128(uint128 x, uint128 y) internal pure returns (uint128 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), gt(y, x)))\n }\n }\n\n /**\n * @notice Thresholds a value to be within the specified bounds.\n * @param value The value to bound.\n * @param lowerLimit The minimum allowable value.\n * @param upperLimit The maximum allowable value.\n */\n function boundValue(\n uint256 value,\n uint256 lowerLimit,\n uint256 upperLimit\n ) internal pure returns (uint256 outputValue) {\n outputValue = min(max(value, lowerLimit), upperLimit);\n }\n\n /**\n * @notice Returns the difference between two uint128 values or zero if the result would be negative.\n * @param x The minuend.\n * @param y The subtrahend.\n */\n function clip128(uint128 x, uint128 y) internal pure returns (uint128) {\n unchecked {\n return x < y ? 0 : x - y;\n }\n }\n\n /**\n * @notice Returns the difference between two uint256 values or zero if the result would be negative.\n * @param x The minuend.\n * @param y The subtrahend.\n */\n function clip(uint256 x, uint256 y) internal pure returns (uint256) {\n unchecked {\n return x < y ? 0 : x - y;\n }\n }\n\n /**\n * @notice Divides one uint256 by another, rounding down to the nearest\n * integer.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divFloor(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivFloor(x, ONE, y);\n }\n\n /**\n * @notice Divides one uint256 by another, rounding up to the nearest integer.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divCeil(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivCeil(x, ONE, y);\n }\n\n /**\n * @notice Multiplies two uint256 values and then divides by ONE, rounding down.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulFloor(uint256 x, uint256 y) internal pure returns (uint256) {\n return OzMath.mulDiv(x, y, ONE);\n }\n\n /**\n * @notice Multiplies two uint256 values and then divides by ONE, rounding up.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulCeil(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivCeil(x, y, ONE);\n }\n\n /**\n * @notice Calculates the multiplicative inverse of a uint256, rounding down.\n * @param x The value to invert.\n */\n function invFloor(uint256 x) internal pure returns (uint256) {\n unchecked {\n return ONE_SQUARED / x;\n }\n }\n\n /**\n * @notice Calculates the multiplicative inverse of a uint256, rounding up.\n * @param denominator The value to invert.\n */\n function invCeil(uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // divide z - 1 by the denominator and add 1.\n z := add(div(sub(ONE_SQUARED, 1), denominator), 1)\n }\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding down.\n * @param x The multiplicand.\n * @param y The multiplier.\n * @param k The divisor.\n */\n function mulDivFloor(uint256 x, uint256 y, uint256 k) internal pure returns (uint256 result) {\n result = OzMath.mulDiv(x, y, max(1, k));\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding up if there's a remainder.\n * @param x The multiplicand.\n * @param y The multiplier.\n * @param k The divisor.\n */\n function mulDivCeil(uint256 x, uint256 y, uint256 k) internal pure returns (uint256 result) {\n result = mulDivFloor(x, y, k);\n if (mulmod(x, y, max(1, k)) != 0) result = result + 1;\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding\n * down. Will revert if `x * y` is larger than `type(uint256).max`.\n * @param x The first operand for multiplication.\n * @param y The second operand for multiplication.\n * @param denominator The divisor after multiplication.\n */\n function mulDivDown(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // Store x * y in z for now.\n z := mul(x, y)\n if iszero(denominator) {\n denominator := 1\n }\n\n if iszero(or(iszero(x), eq(div(z, x), y))) {\n revert(0, 0)\n }\n\n // Divide z by the denominator.\n z := div(z, denominator)\n }\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding\n * up. Will revert if `x * y` is larger than `type(uint256).max`.\n * @param x The first operand for multiplication.\n * @param y The second operand for multiplication.\n * @param denominator The divisor after multiplication.\n */\n function mulDivUp(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // Store x * y in z for now.\n z := mul(x, y)\n if iszero(denominator) {\n denominator := 1\n }\n\n if iszero(or(iszero(x), eq(div(z, x), y))) {\n revert(0, 0)\n }\n\n // First, divide z - 1 by the denominator and add 1.\n // We allow z - 1 to underflow if z is 0, because we multiply the\n // end result by 0 if z is zero, ensuring we return 0 if z is zero.\n z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))\n }\n }\n\n /**\n * @notice Multiplies a uint256 by another and divides by a constant,\n * rounding down. Will revert if `x * y` is larger than\n * `type(uint256).max`.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulDown(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivDown(x, y, ONE);\n }\n\n /**\n * @notice Divides a uint256 by another, rounding down the result. Will\n * revert if `x * 1e18` is larger than `type(uint256).max`.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divDown(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivDown(x, ONE, y);\n }\n\n /**\n * @notice Divides a uint256 by another, rounding up the result. Will\n * revert if `x * 1e18` is larger than `type(uint256).max`.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divUp(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivUp(x, ONE, y);\n }\n\n /**\n * @notice Scales a number based on a difference in decimals from a default.\n * @param decimals The new decimal precision.\n */\n function scale(uint8 decimals) internal pure returns (uint256) {\n unchecked {\n if (decimals == DEFAULT_DECIMALS) {\n return DEFAULT_SCALE;\n } else {\n return 10 ** (DEFAULT_DECIMALS - decimals);\n }\n }\n }\n\n /**\n * @notice Adjusts a scaled amount to the token decimal scale.\n * @param amount The scaled amount.\n * @param scaleFactor The scaling factor to adjust by.\n * @param ceil Whether to round up (true) or down (false).\n */\n function ammScaleToTokenScale(uint256 amount, uint256 scaleFactor, bool ceil) internal pure returns (uint256 z) {\n unchecked {\n if (scaleFactor == DEFAULT_SCALE || amount == 0) {\n return amount;\n } else {\n if (!ceil) return amount / scaleFactor;\n assembly (\"memory-safe\") {\n z := add(div(sub(amount, 1), scaleFactor), 1)\n }\n }\n }\n }\n\n /**\n * @notice Adjusts a token amount to the D18 AMM scale.\n * @param amount The amount in token scale.\n * @param scaleFactor The scale factor for adjustment.\n */\n function tokenScaleToAmmScale(uint256 amount, uint256 scaleFactor) internal pure returns (uint256) {\n if (scaleFactor == DEFAULT_SCALE) {\n return amount;\n } else {\n return amount * scaleFactor;\n }\n }\n\n /**\n * @notice Returns the absolute value of a signed 32-bit integer.\n * @param x The integer to take the absolute value of.\n */\n function abs32(int32 x) internal pure returns (uint32) {\n unchecked {\n return uint32(x < 0 ? -x : x);\n }\n }\n\n /**\n * @notice Returns the absolute value of a signed 256-bit integer.\n * @param x The integer to take the absolute value of.\n */\n function abs(int256 x) internal pure returns (uint256) {\n unchecked {\n return uint256(x < 0 ? -x : x);\n }\n }\n\n /**\n * @notice Calculates the integer square root of a uint256 rounded down.\n * @param x The number to take the square root of.\n */\n function sqrt(uint256 x) internal pure returns (uint256 z) {\n // from https://github.com/transmissions11/solmate/blob/e8f96f25d48fe702117ce76c79228ca4f20206cb/src/utils/FixedPointMathLib.sol\n assembly (\"memory-safe\") {\n let y := x\n z := 181\n\n if iszero(lt(y, 0x10000000000000000000000000000000000)) {\n y := shr(128, y)\n z := shl(64, z)\n }\n if iszero(lt(y, 0x1000000000000000000)) {\n y := shr(64, y)\n z := shl(32, z)\n }\n if iszero(lt(y, 0x10000000000)) {\n y := shr(32, y)\n z := shl(16, z)\n }\n if iszero(lt(y, 0x1000000)) {\n y := shr(16, y)\n z := shl(8, z)\n }\n\n z := shr(18, mul(z, add(y, 65536)))\n\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n\n z := sub(z, lt(div(x, z), z))\n }\n }\n\n /**\n * @notice Computes the floor of a D8-scaled number as an int32, ignoring\n * potential overflow in the cast.\n * @param val The D8-scaled number.\n */\n function floorD8Unchecked(int256 val) internal pure returns (int32) {\n int32 val32;\n bool check;\n unchecked {\n val32 = int32(val / INT_ONE_D8);\n check = (val < 0 && val % INT_ONE_D8 != 0);\n }\n return check ? val32 - 1 : val32;\n }\n}" + }, + "lib/rooster/v2-common/libraries/TickMath.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport {Math as OzMath} from \"../../openzeppelin-custom/contracts/utils/math/Math.sol\";\nimport {Math} from \"./Math.sol\";\nimport {MAX_TICK, ONE} from \"./Constants.sol\";\n\n/**\n * @notice Math functions related to tick operations.\n */\n// slither-disable-start divide-before-multiply\nlibrary TickMath {\n using Math for uint256;\n\n error TickMaxExceeded(int256 tick);\n\n /**\n * @notice Compute the lower and upper sqrtPrice of a tick.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n */\n function tickSqrtPrices(\n uint256 tickSpacing,\n int32 _tick\n ) internal pure returns (uint256 sqrtLowerPrice, uint256 sqrtUpperPrice) {\n unchecked {\n sqrtLowerPrice = tickSqrtPrice(tickSpacing, _tick);\n sqrtUpperPrice = tickSqrtPrice(tickSpacing, _tick + 1);\n }\n }\n\n /**\n * @notice Compute the base tick value from the pool tick and the\n * tickSpacing. Revert if base tick is beyond the max tick boundary.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n */\n function subTickIndex(uint256 tickSpacing, int32 _tick) internal pure returns (uint32 subTick) {\n subTick = Math.abs32(_tick);\n subTick *= uint32(tickSpacing);\n if (subTick > MAX_TICK) {\n revert TickMaxExceeded(_tick);\n }\n }\n\n /**\n * @notice Calculate the square root price for a given tick and tick spacing.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n * @return _result The square root price.\n */\n function tickSqrtPrice(uint256 tickSpacing, int32 _tick) internal pure returns (uint256 _result) {\n unchecked {\n uint256 tick = subTickIndex(tickSpacing, _tick);\n\n uint256 ratio = tick & 0x1 != 0 ? 0xfffcb933bd6fad9d3af5f0b9f25db4d6 : 0x100000000000000000000000000000000;\n if (tick & 0x2 != 0) ratio = (ratio * 0xfff97272373d41fd789c8cb37ffcaa1c) >> 128;\n if (tick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656ac9229c67059486f389) >> 128;\n if (tick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e81259b3cddc7a064941) >> 128;\n if (tick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f67b19e8887e0bd251eb7) >> 128;\n if (tick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98cd2e57b660be99eb2c4a) >> 128;\n if (tick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c9838804e327cb417cafcb) >> 128;\n if (tick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99d51e2cc356c2f617dbe0) >> 128;\n if (tick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900aecf64236ab31f1f9dcb5) >> 128;\n if (tick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac4d9194200696907cf2e37) >> 128;\n if (tick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b88206f8abe8a3b44dd9be) >> 128;\n if (tick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c578ef4f1d17b2b235d480) >> 128;\n if (tick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd254ee83bdd3f248e7e785e) >> 128;\n if (tick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d8f7dd10e744d913d033333) >> 128;\n if (tick & 0x4000 != 0) ratio = (ratio * 0x70d869a156ddd32a39e257bc3f50aa9b) >> 128;\n if (tick & 0x8000 != 0) ratio = (ratio * 0x31be135f97da6e09a19dc367e3b6da40) >> 128;\n if (tick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7e5a9780b0cc4e25d61a56) >> 128;\n if (tick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedbcb3a6ccb7ce618d14225) >> 128;\n if (tick & 0x40000 != 0) ratio = (ratio * 0x2216e584f630389b2052b8db590e) >> 128;\n if (_tick > 0) ratio = type(uint256).max / ratio;\n _result = (ratio * ONE) >> 128;\n }\n }\n\n /**\n * @notice Calculate liquidity of a tick.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n */\n function getTickL(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n ) internal pure returns (uint256 liquidity) {\n // known:\n // - sqrt price values are different\n // - reserveA and reserveB fit in 128 bit\n // - sqrt price is in (1e-7, 1e7)\n // - D18 max for uint256 is 1.15e59\n // - D18 min is 1e-18\n\n unchecked {\n // diff is in (5e-12, 4e6); max tick spacing is 10_000\n uint256 diff = sqrtUpperTickPrice - sqrtLowerTickPrice;\n\n // Need to maximize precision by shifting small values A and B up so\n // that they use more of the available bit range. Two constraints to\n // consider: we need A * B * diff / sqrtPrice to be bigger than 1e-18\n // when the bump is not in play. This constrains the threshold for\n // bumping to be at least 77 bit; ie, either a or b needs 2^77 which\n // means that term A * B * diff / sqrtPrice > 1e-18.\n //\n // At the other end, the second constraint is that b^2 needs to fit in\n // a 256-bit number, so, post bump, the max reserve value needs to be\n // less than 6e22. With a 78-bit threshold and a 57-bit bump, we have A\n // and B are in (1.4e-1, 4.4e22 (2^(78+57))) with bump, and one of A or\n // B is at least 2^78 without the bump, but the other reserve value may\n // be as small as 1 wei.\n uint256 precisionBump = 0;\n if ((reserveA >> 78) == 0 && (reserveB >> 78) == 0) {\n precisionBump = 57;\n reserveA <<= precisionBump;\n reserveB <<= precisionBump;\n }\n\n if (reserveB == 0) return Math.divDown(reserveA, diff) >> precisionBump;\n if (reserveA == 0)\n return Math.mulDivDown(reserveB.mulDown(sqrtLowerTickPrice), sqrtUpperTickPrice, diff) >> precisionBump;\n\n // b is in (7.2e-9 (2^57 / 1e7 / 2), 2.8e29 (2^(78+57) * 1e7 / 2)) with bump\n // b is in a subset of the same range without bump\n uint256 b = (reserveA.divDown(sqrtUpperTickPrice) + reserveB.mulDown(sqrtLowerTickPrice)) >> 1;\n\n // b^2 is in (5.1e-17, 4.8e58); and will not overflow on either end;\n // A*B is in (3e-13 (2^78 / 1e18 * 1e-18), 1.9e45) without bump and is in a subset range with bump\n // A*B*diff/sqrtUpper is in (1.5e-17 (3e-13 * 5e-12 * 1e7), 7.6e58);\n\n // Since b^2 is at the upper edge of the precision range, we are not\n // able to multiply the argument of the sqrt by 1e18, instead, we move\n // this factor outside of the sqrt. The resulting loss of precision\n // means that this liquidity value is a lower bound on the tick\n // liquidity\n return\n OzMath.mulDiv(\n b +\n Math.sqrt(\n (OzMath.mulDiv(b, b, ONE) +\n OzMath.mulDiv(reserveB.mulFloor(reserveA), diff, sqrtUpperTickPrice))\n ) *\n 1e9,\n sqrtUpperTickPrice,\n diff\n ) >> precisionBump;\n }\n }\n\n /**\n * @notice Calculate square root price of a tick. Returns left edge of the\n * tick if the tick has no reserves.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n * @return sqrtPrice The calculated square root price.\n */\n function getSqrtPrice(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice,\n uint256 liquidity\n ) internal pure returns (uint256 sqrtPrice) {\n unchecked {\n if (reserveA == 0) {\n return sqrtLowerTickPrice;\n }\n if (reserveB == 0) {\n return sqrtUpperTickPrice;\n }\n sqrtPrice = Math.sqrt(\n ONE *\n (reserveA + liquidity.mulDown(sqrtLowerTickPrice)).divDown(\n reserveB + liquidity.divDown(sqrtUpperTickPrice)\n )\n );\n sqrtPrice = Math.boundValue(sqrtPrice, sqrtLowerTickPrice, sqrtUpperTickPrice);\n }\n }\n\n /**\n * @notice Calculate square root price of a tick. Returns left edge of the\n * tick if the tick has no reserves.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n * @return sqrtPrice The calculated square root price.\n * @return liquidity The calculated liquidity.\n */\n function getTickSqrtPriceAndL(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n ) internal pure returns (uint256 sqrtPrice, uint256 liquidity) {\n liquidity = getTickL(reserveA, reserveB, sqrtLowerTickPrice, sqrtUpperTickPrice);\n sqrtPrice = getSqrtPrice(reserveA, reserveB, sqrtLowerTickPrice, sqrtUpperTickPrice, liquidity);\n }\n}\n// slither-disable-end divide-before-multiply" + }, + "solidity-bytes-utils/contracts/BytesLib.sol": { + "content": "// SPDX-License-Identifier: Unlicense\n/*\n * @title Solidity Bytes Arrays Utils\n * @author Gonçalo Sá \n *\n * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.\n * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.\n */\npragma solidity >=0.8.0 <0.9.0;\n\n\nlibrary BytesLib {\n function concat(\n bytes memory _preBytes,\n bytes memory _postBytes\n )\n internal\n pure\n returns (bytes memory)\n {\n bytes memory tempBytes;\n\n assembly {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // Store the length of the first bytes array at the beginning of\n // the memory for tempBytes.\n let length := mload(_preBytes)\n mstore(tempBytes, length)\n\n // Maintain a memory counter for the current write location in the\n // temp bytes array by adding the 32 bytes for the array length to\n // the starting location.\n let mc := add(tempBytes, 0x20)\n // Stop copying when the memory counter reaches the length of the\n // first bytes array.\n let end := add(mc, length)\n\n for {\n // Initialize a copy counter to the start of the _preBytes data,\n // 32 bytes into its memory.\n let cc := add(_preBytes, 0x20)\n } lt(mc, end) {\n // Increase both counters by 32 bytes each iteration.\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // Write the _preBytes data into the tempBytes memory 32 bytes\n // at a time.\n mstore(mc, mload(cc))\n }\n\n // Add the length of _postBytes to the current length of tempBytes\n // and store it as the new length in the first 32 bytes of the\n // tempBytes memory.\n length := mload(_postBytes)\n mstore(tempBytes, add(length, mload(tempBytes)))\n\n // Move the memory counter back from a multiple of 0x20 to the\n // actual end of the _preBytes data.\n mc := end\n // Stop copying when the memory counter reaches the new combined\n // length of the arrays.\n end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n // Update the free-memory pointer by padding our last write location\n // to 32 bytes: add 31 bytes to the end of tempBytes to move to the\n // next 32 byte block, then round down to the nearest multiple of\n // 32. If the sum of the length of the two arrays is zero then add\n // one before rounding down to leave a blank 32 bytes (the length block with 0).\n mstore(0x40, and(\n add(add(end, iszero(add(length, mload(_preBytes)))), 31),\n not(31) // Round down to the nearest 32 bytes.\n ))\n }\n\n return tempBytes;\n }\n\n function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {\n assembly {\n // Read the first 32 bytes of _preBytes storage, which is the length\n // of the array. (We don't need to use the offset into the slot\n // because arrays use the entire slot.)\n let fslot := sload(_preBytes.slot)\n // Arrays of 31 bytes or less have an even value in their slot,\n // while longer arrays have an odd value. The actual length is\n // the slot divided by two for odd values, and the lowest order\n // byte divided by two for even values.\n // If the slot is even, bitwise and the slot with 255 and divide by\n // two to get the length. If the slot is odd, bitwise and the slot\n // with -1 and divide by two.\n let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)\n let mlength := mload(_postBytes)\n let newlength := add(slength, mlength)\n // slength can contain both the length and contents of the array\n // if length < 32 bytes so let's prepare for that\n // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage\n switch add(lt(slength, 32), lt(newlength, 32))\n case 2 {\n // Since the new array still fits in the slot, we just need to\n // update the contents of the slot.\n // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length\n sstore(\n _preBytes.slot,\n // all the modifications to the slot are inside this\n // next block\n add(\n // we can just add to the slot contents because the\n // bytes we want to change are the LSBs\n fslot,\n add(\n mul(\n div(\n // load the bytes from memory\n mload(add(_postBytes, 0x20)),\n // zero all bytes to the right\n exp(0x100, sub(32, mlength))\n ),\n // and now shift left the number of bytes to\n // leave space for the length in the slot\n exp(0x100, sub(32, newlength))\n ),\n // increase length by the double of the memory\n // bytes length\n mul(mlength, 2)\n )\n )\n )\n }\n case 1 {\n // The stored value fits in the slot, but the combined value\n // will exceed it.\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n let sc := add(keccak256(0x0, 0x20), div(slength, 32))\n\n // save new length\n sstore(_preBytes.slot, add(mul(newlength, 2), 1))\n\n // The contents of the _postBytes array start 32 bytes into\n // the structure. Our first read should obtain the `submod`\n // bytes that can fit into the unused space in the last word\n // of the stored array. To get this, we read 32 bytes starting\n // from `submod`, so the data we read overlaps with the array\n // contents by `submod` bytes. Masking the lowest-order\n // `submod` bytes allows us to add that value directly to the\n // stored value.\n\n let submod := sub(32, slength)\n let mc := add(_postBytes, submod)\n let end := add(_postBytes, mlength)\n let mask := sub(exp(0x100, submod), 1)\n\n sstore(\n sc,\n add(\n and(\n fslot,\n 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00\n ),\n and(mload(mc), mask)\n )\n )\n\n for {\n mc := add(mc, 0x20)\n sc := add(sc, 1)\n } lt(mc, end) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n sstore(sc, mload(mc))\n }\n\n mask := exp(0x100, sub(mc, end))\n\n sstore(sc, mul(div(mload(mc), mask), mask))\n }\n default {\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n // Start copying to the last used word of the stored array.\n let sc := add(keccak256(0x0, 0x20), div(slength, 32))\n\n // save new length\n sstore(_preBytes.slot, add(mul(newlength, 2), 1))\n\n // Copy over the first `submod` bytes of the new data as in\n // case 1 above.\n let slengthmod := mod(slength, 32)\n let mlengthmod := mod(mlength, 32)\n let submod := sub(32, slengthmod)\n let mc := add(_postBytes, submod)\n let end := add(_postBytes, mlength)\n let mask := sub(exp(0x100, submod), 1)\n\n sstore(sc, add(sload(sc), and(mload(mc), mask)))\n\n for {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } lt(mc, end) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n sstore(sc, mload(mc))\n }\n\n mask := exp(0x100, sub(mc, end))\n\n sstore(sc, mul(div(mload(mc), mask), mask))\n }\n }\n }\n\n function slice(\n bytes memory _bytes,\n uint256 _start,\n uint256 _length\n )\n internal\n pure\n returns (bytes memory)\n {\n // We're using the unchecked block below because otherwise execution ends \n // with the native overflow error code.\n unchecked {\n require(_length + 31 >= _length, \"slice_overflow\");\n }\n require(_bytes.length >= _start + _length, \"slice_outOfBounds\");\n\n bytes memory tempBytes;\n\n assembly {\n switch iszero(_length)\n case 0 {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // The first word of the slice result is potentially a partial\n // word read from the original array. To read it, we calculate\n // the length of that partial word and start copying that many\n // bytes into the array. The first word we copy will start with\n // data we don't care about, but the last `lengthmod` bytes will\n // land at the beginning of the contents of the new array. When\n // we're done copying, we overwrite the full first word with\n // the actual length of the slice.\n let lengthmod := and(_length, 31)\n\n // The multiplication in the next line is necessary\n // because when slicing multiples of 32 bytes (lengthmod == 0)\n // the following copy loop was copying the origin's length\n // and then ending prematurely not copying everything it should.\n let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))\n let end := add(mc, _length)\n\n for {\n // The multiplication in the next line has the same exact purpose\n // as the one above.\n let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n mstore(tempBytes, _length)\n\n //update free-memory pointer\n //allocating the array padded to 32 bytes like the compiler does now\n mstore(0x40, and(add(mc, 31), not(31)))\n }\n //if we want a zero-length slice let's just return a zero-length array\n default {\n tempBytes := mload(0x40)\n //zero out the 32 bytes slice we are about to return\n //we need to do it because Solidity does not garbage collect\n mstore(tempBytes, 0)\n\n mstore(0x40, add(tempBytes, 0x20))\n }\n }\n\n return tempBytes;\n }\n\n function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {\n require(_bytes.length >= _start + 20, \"toAddress_outOfBounds\");\n address tempAddress;\n\n assembly {\n tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)\n }\n\n return tempAddress;\n }\n\n function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {\n require(_bytes.length >= _start + 1 , \"toUint8_outOfBounds\");\n uint8 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x1), _start))\n }\n\n return tempUint;\n }\n\n function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {\n require(_bytes.length >= _start + 2, \"toUint16_outOfBounds\");\n uint16 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x2), _start))\n }\n\n return tempUint;\n }\n\n function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {\n require(_bytes.length >= _start + 4, \"toUint32_outOfBounds\");\n uint32 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x4), _start))\n }\n\n return tempUint;\n }\n\n function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {\n require(_bytes.length >= _start + 8, \"toUint64_outOfBounds\");\n uint64 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x8), _start))\n }\n\n return tempUint;\n }\n\n function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {\n require(_bytes.length >= _start + 12, \"toUint96_outOfBounds\");\n uint96 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0xc), _start))\n }\n\n return tempUint;\n }\n\n function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {\n require(_bytes.length >= _start + 16, \"toUint128_outOfBounds\");\n uint128 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x10), _start))\n }\n\n return tempUint;\n }\n\n function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {\n require(_bytes.length >= _start + 32, \"toUint256_outOfBounds\");\n uint256 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x20), _start))\n }\n\n return tempUint;\n }\n\n function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {\n require(_bytes.length >= _start + 32, \"toBytes32_outOfBounds\");\n bytes32 tempBytes32;\n\n assembly {\n tempBytes32 := mload(add(add(_bytes, 0x20), _start))\n }\n\n return tempBytes32;\n }\n\n function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {\n bool success = true;\n\n assembly {\n let length := mload(_preBytes)\n\n // if lengths don't match the arrays are not equal\n switch eq(length, mload(_postBytes))\n case 1 {\n // cb is a circuit breaker in the for loop since there's\n // no said feature for inline assembly loops\n // cb = 1 - don't breaker\n // cb = 0 - break\n let cb := 1\n\n let mc := add(_preBytes, 0x20)\n let end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n // the next line is the loop condition:\n // while(uint256(mc < end) + cb == 2)\n } eq(add(lt(mc, end), cb), 2) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // if any of these checks fails then arrays are not equal\n if iszero(eq(mload(mc), mload(cc))) {\n // unsuccess:\n success := 0\n cb := 0\n }\n }\n }\n default {\n // unsuccess:\n success := 0\n }\n }\n\n return success;\n }\n\n function equalStorage(\n bytes storage _preBytes,\n bytes memory _postBytes\n )\n internal\n view\n returns (bool)\n {\n bool success = true;\n\n assembly {\n // we know _preBytes_offset is 0\n let fslot := sload(_preBytes.slot)\n // Decode the length of the stored array like in concatStorage().\n let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)\n let mlength := mload(_postBytes)\n\n // if lengths don't match the arrays are not equal\n switch eq(slength, mlength)\n case 1 {\n // slength can contain both the length and contents of the array\n // if length < 32 bytes so let's prepare for that\n // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage\n if iszero(iszero(slength)) {\n switch lt(slength, 32)\n case 1 {\n // blank the last byte which is the length\n fslot := mul(div(fslot, 0x100), 0x100)\n\n if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {\n // unsuccess:\n success := 0\n }\n }\n default {\n // cb is a circuit breaker in the for loop since there's\n // no said feature for inline assembly loops\n // cb = 1 - don't breaker\n // cb = 0 - break\n let cb := 1\n\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n let sc := keccak256(0x0, 0x20)\n\n let mc := add(_postBytes, 0x20)\n let end := add(mc, mlength)\n\n // the next line is the loop condition:\n // while(uint256(mc < end) + cb == 2)\n for {} eq(add(lt(mc, end), cb), 2) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n if iszero(eq(sload(sc), mload(mc))) {\n // unsuccess:\n success := 0\n cb := 0\n }\n }\n }\n }\n }\n default {\n // unsuccess:\n success := 0\n }\n }\n\n return success;\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/503c3faac303304c9ba2b2c2f7f66eb2.json b/contracts/deployments/hoodi/solcInputs/503c3faac303304c9ba2b2c2f7f66eb2.json new file mode 100644 index 0000000000..2f18b3db08 --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/503c3faac303304c9ba2b2c2f7f66eb2.json @@ -0,0 +1,102 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to retrieve beacon block roots.\n * @author Origin Protocol Inc\n */\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the beacon block root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Roots contract to get the parent block root.\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to request full or partial withdrawals from validators on the beacon chain.\n * @author Origin Protocol Inc\n */\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n /// @notice Requests a partial withdrawal for a given validator public key and amount.\n /// @param validatorPubKey The public key of the validator to withdraw from\n /// @param amount The amount of ETH to withdraw\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n /// @notice Gets fee for withdrawal requests contract on Beacon chain\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint40 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n bytes32 pubKeyHash,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof,\n bytes calldata validatorPubKeyProof\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint40 validatorIndex\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes32 pubKeyHash,\n bytes calldata firstPendingDepositPubKeyProof\n ) external view returns (bool isEmptyDepositQueue);\n\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view returns (bool isEmptyDepositQueue);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with\n /// `platformAddress` not used so empty address\n /// `vaultAddress` the address of the OETH Vault contract\n /// @param _wethAddress Address of the WETH Token contract\n /// @param _ssvToken Address of the SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconProofs,\n _beaconGenesisTimestamp\n )\n {\n SSV_TOKEN = _ssvToken;\n\n // Make sure nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// @param _rewardTokenAddresses Not used so empty array\n /// @param _assets Not used so empty array\n /// @param _pTokens Not used so empty array\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n safeApproveAllTokens();\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH that was transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// @param _recipient Address to receive withdrawn assets.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH to withdraw.\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(WETH, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Accounts for all the assets managed by this strategy which includes:\n /// 1. The current WETH in this strategy contract\n /// 2. The last verified ETH balance, total deposits and total validator balances\n /// @param _asset Address of WETH asset.\n /// @return balance Total value in ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by the strategy.\n /// @param _asset The address of the WETH token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for validator registration.\n function safeApproveAllTokens() public override {\n // Approves the SSV Network contract to transfer SSV tokens when validators are registered\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n * The new ETH will be accounted for in `checkBalance` after the next snapBalances and verifyBalances txs.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n /// @notice is not supported for this strategy as there is no platform token.\n function setPTokenAddress(address, address) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @notice is not supported for this strategy as there is no platform token.\n function removePToken(uint256) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @dev This strategy does not use a platform token like the old Aave and Compound strategies.\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instead of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract.\n /// Withdrawals from validators also accumulate as ETH in this strategy contract.\n /// It's too complex to separate the rewards from withdrawals so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can now regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingView.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/**\n * @title Viewing contract for the Compounding Staking Strategy.\n * @author Origin Protocol Inc\n */\ncontract CompoundingStakingStrategyView {\n /// @notice The address of the Compounding Staking Strategy contract\n CompoundingValidatorManager public immutable stakingStrategy;\n\n constructor(address _stakingStrategy) {\n stakingStrategy = CompoundingValidatorManager(_stakingStrategy);\n }\n\n struct ValidatorView {\n bytes32 pubKeyHash;\n uint64 index;\n CompoundingValidatorManager.ValidatorState state;\n }\n\n struct DepositView {\n uint256 depositID;\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint256 withdrawableEpoch;\n }\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return validators An array of `ValidatorView` containing the public key hash, validator index and state.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorView[] memory validators)\n {\n uint256 validatorCount = stakingStrategy.verifiedValidatorsLength();\n validators = new ValidatorView[](validatorCount);\n for (uint256 i = 0; i < validatorCount; ++i) {\n bytes32 pubKeyHash = stakingStrategy.verifiedValidators(i);\n (\n CompoundingValidatorManager.ValidatorState state,\n uint64 index\n ) = stakingStrategy.validator(pubKeyHash);\n validators[i] = ValidatorView({\n pubKeyHash: pubKeyHash,\n index: index,\n state: state\n });\n }\n }\n\n /// @notice Returns the deposits that are still to be verified.\n /// These may or may not have been processed by the beacon chain.\n /// @return pendingDeposits An array of `DepositView` containing the deposit ID, public key hash,\n /// amount in Gwei and the slot of the deposit.\n function getPendingDeposits()\n external\n view\n returns (DepositView[] memory pendingDeposits)\n {\n uint256 depositsCount = stakingStrategy.depositListLength();\n pendingDeposits = new DepositView[](depositsCount);\n for (uint256 i = 0; i < depositsCount; ++i) {\n (\n bytes32 pubKeyHash,\n uint64 amountGwei,\n uint64 slot,\n ,\n ,\n uint256 withdrawableEpoch\n ) = stakingStrategy.deposits(stakingStrategy.depositList(i));\n pendingDeposits[i] = DepositView({\n depositID: stakingStrategy.depositList(i),\n pubKeyHash: pubKeyHash,\n amountGwei: amountGwei,\n slot: slot,\n withdrawableEpoch: withdrawableEpoch\n });\n }\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable {\n using SafeERC20 for IERC20;\n\n /// @dev The amount of ETH in wei that is required for a deposit to a new validator.\n uint256 internal constant DEPOSIT_AMOUNT_WEI = 1 ether;\n /// @dev The maximum number of deposits that are waiting to be verified as processed on the beacon chain.\n uint256 internal constant MAX_DEPOSITS = 12;\n /// @dev The maximum number of validators that can be verified.\n uint256 internal constant MAX_VERIFIED_VALIDATORS = 48;\n /// @dev The default withdrawable epoch value on the Beacon chain.\n /// A value in the far future means the validator is not exiting.\n uint64 internal constant FAR_FUTURE_EPOCH = type(uint64).max;\n /// @dev The number of seconds between each beacon chain slot.\n uint64 internal constant SLOT_DURATION = 12;\n /// @dev The number of slots in each beacon chain epoch.\n uint64 internal constant SLOTS_PER_EPOCH = 32;\n /// @dev Minimum time in seconds to allow snapped balances to be verified.\n /// Set to 35 slots which is 3 slots more than 1 epoch (32 slots). Deposits get processed\n /// once per epoch. This larger than 1 epoch delay should achieve that `snapBalances` sometimes\n /// get called in the middle (or towards the end) of the epoch. Giving the off-chain script\n /// sufficient time after the end of the epoch to prepare the proofs and call `verifyBalances`.\n /// This is considering a malicious actor would keep calling `snapBalances` as frequent as possible\n /// to disturb our operations.\n uint64 internal constant SNAP_BALANCES_DELAY = 35 * SLOT_DURATION;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n /// @notice The timestamp of the Beacon chain genesis.\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\n uint64 public immutable BEACON_GENESIS_TIMESTAMP;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// @notice Deposit data for new compounding validators.\n /// @dev A `VERIFIED` deposit can mean 3 separate things:\n /// - a deposit has been processed by the beacon chain and shall be included in the\n /// balance of the next verifyBalances call\n /// - a deposit has been done to a slashed validator and has probably been recovered\n /// back to this strategy. Probably because we can not know for certain. This contract\n /// only detects when the validator has passed its withdrawal epoch. It is close to impossible\n /// to prove with Merkle Proofs that the postponed deposit this contract is responsible for\n /// creating is not present anymore in BeaconChain.state.pending_deposits. This in effect\n /// means that there might be a period where this contract thinks the deposit has been already\n /// returned as ETH balance before it happens. This will result in some days (or weeks)\n /// -> depending on the size of deposit queue of showing a deficit when calling `checkBalance`.\n /// As this only offsets the yield and doesn't cause a critical double-counting we are not addressing\n /// this issue.\n /// - A deposit has been done to the validator, but our deposit has been front run by a malicious\n /// actor. Funds in the deposit this contract makes are not recoverable.\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified\n }\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountGwei Amount of ETH in gwei that has been deposited to the beacon chain deposit contract\n /// @param slot The beacon chain slot number when the deposit has been made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either UNKNOWN, PENDING or VERIFIED\n /// @param withdrawableEpoch The withdrawableEpoch of the validator which is being deposited to.\n /// At deposit time this is set to max default value (FAR_FUTURE_EPOCH). If a deposit has\n /// made to a slashed validator the `withdrawableEpoch` will be set to the epoch of that\n /// validator.\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint32 depositIndex;\n DepositStatus status;\n uint64 withdrawableEpoch;\n }\n /// @notice Restricts to only one deposit to an unverified validator at a time.\n /// This is to limit front-running attacks of deposits to the beacon chain contract.\n ///\n /// @dev The value is set to true when a deposit to a new validator has been done that has\n /// not yet be verified.\n bool public firstDeposit;\n /// @notice Unique identifier of the next validator deposit.\n uint128 public nextDepositID;\n /// @notice Mapping of the deposit ID to the deposit data\n mapping(uint256 => DepositData) public deposits;\n /// @notice List of strategy deposit IDs to a validator.\n /// The list can be for deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n /// The list may not be ordered by time of deposit.\n /// Removed deposits will move the last deposit to the removed index.\n uint256[] public depositList;\n\n enum ValidatorState {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITING, // The validator has been requested to exit or has been verified as forced exit\n EXITED, // The validator has been verified to have a zero balance\n REMOVED, // validator has funds withdrawn to this strategy contract and is removed from the SSV\n INVALID // The validator has been front-run and the withdrawal address is not this strategy\n }\n\n // Validator data\n struct ValidatorData {\n ValidatorState state; // The state of the validator known to this contract\n uint40 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n bytes32[] public verifiedValidators;\n /// @notice Mapping of the hash of the validator's public key to the validator state and index.\n /// Uses the Beacon chain hashing for BLSPubkey which is sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => ValidatorData) public validator;\n\n /// @param blockRoot Beacon chain block root of the snapshot\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n bytes32 blockRoot;\n uint64 timestamp;\n uint128 ethBalance;\n }\n /// @notice Mapping of the block root to the balances at that slot\n Balances public snappedBalance;\n /// @notice The last verified ETH balance of the strategy\n uint256 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[40] private __gap;\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event FirstDepositReset();\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n uint256 indexed depositID,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint40 indexed validatorIndex\n );\n event ValidatorInvalid(bytes32 indexed pubKeyHash);\n event DepositVerified(uint256 indexed depositID, uint256 amountWei);\n event DepositToValidatorExiting(\n uint256 indexed depositID,\n uint256 amountWei,\n uint64 withdrawableEpoch\n );\n event DepositValidatorExited(uint256 indexed depositID, uint256 amountWei);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(bytes32 indexed blockRoot, uint256 ethBalance);\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 ethBalance\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_PROOFS = _beaconProofs;\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\n\n require(\n block.timestamp > _beaconGenesisTimestamp,\n \"Invalid genesis timestamp\"\n );\n }\n\n /**\n *\n * Admin Functions\n *\n */\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Reset the `firstDeposit` flag to false so deposits to unverified validators can be made again.\n function resetFirstDeposit() external onlyGovernor {\n require(firstDeposit, \"No first deposit\");\n\n firstDeposit = false;\n\n emit FirstDepositReset();\n }\n\n /**\n *\n * Validator Management\n *\n */\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validator[pubKeyHash].state == ValidatorState.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validator[pubKeyHash].state = ValidatorState.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n struct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n }\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// The the first deposit to a new validator, the amount must be 1 ETH.\n /// Another deposit of at least 31 ETH is required for the validator to be activated.\n /// This second deposit has to be done after the validator has been verified.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validatorStakeData validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth,reentrancy-no-eth\n function stakeEth(\n ValidatorStakeData calldata validatorStakeData,\n uint64 depositAmountGwei\n ) external onlyRegistrator {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(depositList.length < MAX_DEPOSITS, \"Max deposits\");\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validatorStakeData.pubkey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.VERIFIED),\n \"Not registered or verified\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n if (currentState == ValidatorState.REGISTERED) {\n // Can only have one pending deposit to an unverified validator at a time.\n // This is to limit front-running deposit attacks to a single deposit.\n // The exiting deposit needs to be verified before another deposit can be made.\n // If there was a front-running attack, the validator needs to be verified as invalid\n // and the Governor calls `resetFirstDeposit` to set `firstDeposit` to false.\n require(!firstDeposit, \"Existing first deposit\");\n // Limits the amount of ETH that can be at risk from a front-running deposit attack.\n require(\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n // Limits the number of validator balance proofs to verifyBalances\n require(\n verifiedValidators.length + 1 < MAX_VERIFIED_VALIDATORS,\n \"Max validators\"\n );\n\n // Flag a deposit to an unverified validator so only no other deposits can be made\n // to an unverified validator.\n firstDeposit = true;\n }\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == ValidatorState.REGISTERED) {\n validator[pubKeyHash].state = ValidatorState.STAKED;\n }\n\n /// After the Pectra upgrade the validators have a new restriction when proposing\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\n /// forward. Each slot is created at strict 12 second intervals and those slots can\n /// either have blocks attached to them or not. This way using the block.timestamp\n /// the slot number can easily be calculated.\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\n BEACON_GENESIS_TIMESTAMP) / SLOT_DURATION;\n\n // Store the deposit data for verifyDeposit and verifyBalances\n uint256 depositID = nextDepositID++;\n deposits[depositID] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n slot: depositSlot,\n depositIndex: SafeCast.toUint32(depositList.length),\n status: DepositStatus.PENDING,\n withdrawableEpoch: FAR_FUTURE_EPOCH\n });\n depositList.push(depositID);\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validatorStakeData.pubkey,\n withdrawalCredentials,\n validatorStakeData.signature,\n validatorStakeData.depositDataRoot\n );\n\n emit ETHStaked(\n pubKeyHash,\n depositID,\n validatorStakeData.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth,reentrancy-no-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then only the amount in excess of 32 ETH will be withdrawn.\n /// Only the Registrator can call this function.\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\n /// If no ETH balance, the tx will revert.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n payable\n onlyRegistrator\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Validator full withdrawal could be denied due to multiple reasons:\n // - the validator has not been activated or active long enough\n // (current_epoch < activation_epoch + SHARD_COMMITTEE_PERIOD)\n // - the validator has pending balance to withdraw from a previous partial withdrawal request\n //\n // Meaning that the on-chain to beacon chain full withdrawal request could fail. Instead\n // of adding complexity of verifying if a validator is eligible for a full exit, we allow\n // multiple full withdrawal requests per validator.\n require(\n currentState == ValidatorState.VERIFIED ||\n currentState == ValidatorState.EXITING,\n \"Validator not verified/exiting\"\n );\n\n // If a full withdrawal (validator exit)\n if (amountGwei == 0) {\n // For each staking strategy's deposits\n uint256 depositsCount = depositList.length;\n for (uint256 i = 0; i < depositsCount; ++i) {\n uint256 depositID = depositList[i];\n // Check there is no pending deposits to the exiting validator\n require(\n pubKeyHash != deposits[depositID].pubKeyHash,\n \"Pending deposit\"\n );\n }\n\n // Store the validator state as exiting so no more deposits can be made to it.\n validator[pubKeyHash].state = ValidatorState.EXITING;\n }\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove the validator from the SSV Cluster after:\n /// - the validator has been exited from `validatorWithdrawal` or slashed\n /// - the validator has incorrectly registered and can not be staked to\n /// - the initial deposit was front-run and the withdrawal address is not this strategy's address.\n /// Make sure `validatorWithdrawal` is called with a zero amount and the validator has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.EXITED ||\n currentState == ValidatorState.INVALID,\n \"Validator not regd or exited\"\n );\n\n validator[pubKeyHash].state = ValidatorState.REMOVED;\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /**\n *\n * SSV Management\n *\n */\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be withdrawn from the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /**\n *\n * Beacon Chain Proofs\n *\n */\n\n /// @notice Verifies a validator's index to its public key.\n /// Adds to the list of verified validators if the validator's withdrawal address is this strategy's address.\n /// Marks the validator as invalid and removes the deposit if the withdrawal address is not this strategy's address.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param withdrawalAddress The withdrawal address of the validator which should be this strategy's address.\n /// If the withdrawal address is not this strategy's address, the initial deposit was front-run\n /// and the validator is marked as invalid.\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint40 validatorIndex,\n bytes32 pubKeyHash,\n address withdrawalAddress,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validator[pubKeyHash].state == ValidatorState.STAKED,\n \"Validator not staked\"\n );\n\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key.\n // Also verify the validator's withdrawal credential points to the `withdrawalAddress`.\n IBeaconProofs(BEACON_PROOFS).verifyValidator(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n withdrawalAddress\n );\n\n // Store the validator state as verified\n validator[pubKeyHash] = ValidatorData({\n state: ValidatorState.VERIFIED,\n index: validatorIndex\n });\n\n // If the initial deposit was front-run and the withdrawal address is not this strategy\n if (withdrawalAddress != address(this)) {\n // override the validator state\n validator[pubKeyHash].state = ValidatorState.INVALID;\n\n // Find and remove the deposit as the funds can not be recovered\n uint256 depositCount = depositList.length;\n for (uint256 i = 0; i < depositCount; i++) {\n DepositData memory deposit = deposits[depositList[i]];\n if (deposit.pubKeyHash == pubKeyHash) {\n // next verifyBalances will correctly account for the loss of a front-run\n // deposit. Doing it here accounts for the loss as soon as possible\n lastVerifiedEthBalance -= Math.min(\n lastVerifiedEthBalance,\n uint256(deposit.amountGwei) * 1 gwei\n );\n _removeDeposit(depositList[i], deposit);\n break;\n }\n }\n\n // Leave the `firstDeposit` flag as true so no more deposits to unverified validators can be made.\n // The Governor has to reset the `firstDeposit` to false before another deposit to\n // an unverified validator can be made.\n // The Governor can set a new `validatorRegistrator` if they suspect it has been compromised.\n\n emit ValidatorInvalid(pubKeyHash);\n return;\n }\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(pubKeyHash);\n\n // Reset the firstDeposit flag as the first deposit to an unverified validator has been verified.\n firstDeposit = false;\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n struct FirstPendingDepositSlotProofData {\n uint64 slot;\n bytes proof;\n }\n struct FirstPendingDepositWithdrawableProofData {\n uint64 slot;\n uint40 validatorIndex;\n bytes32 pubKeyHash;\n bytes pendingDepositPubKeyProof;\n bytes withdrawableEpochProof;\n bytes validatorPubKeyProof;\n }\n\n struct StrategyValidatorProofData {\n uint64 withdrawableEpoch;\n bytes withdrawableEpochProof;\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n ///\n /// Important: this function has a limitation where `depositProcessedSlot` and `firstDepositValidatorCreatedSlot`\n /// that is passed by the off-chain verifier requires a slot immediately after them to propose a block otherwise\n /// the `BeaconRoots.parentBlockRoot` will fail. This shouldn't be a problem, since by the current behaviour of\n /// beacon chain only 1%-3% slots don't propose a block.\n /// @param depositID The deposit ID emitted in `ETHStaked` from the `stakeEth` function.\n /// @param depositProcessedSlot Any slot on or after the strategy's deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\n /// set for the next block timestamp in 12 seconds time.\n /// @param firstPendingDeposit a `FirstPendingDepositSlotProofData` struct containing:\n /// - slot: The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be any non-zero value if the deposit queue is empty.\n /// - proof: The merkle proof of the first pending deposit's slot to the beacon block root.\n /// Can be either:\n /// * 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// * 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @param strategyValidatorData a `StrategyValidatorProofData` struct containing:\n /// - withdrawableEpoch: The withdrawable epoch of the validator the strategy is depositing to.\n /// - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the validator the strategy\n /// is depositing to, to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n uint256 depositID,\n uint64 depositProcessedSlot,\n FirstPendingDepositSlotProofData calldata firstPendingDeposit,\n StrategyValidatorProofData calldata strategyValidatorData\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositID];\n ValidatorData memory strategyValidator = validator[deposit.pubKeyHash];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(firstPendingDeposit.slot != 0, \"Zero 1st pending deposit slot\");\n\n // We should allow the verification of deposits for validators that have been marked as exiting\n // to cover this situation:\n // - there are have 2 pending deposits\n // - beacon chain has slashed the validator\n // - when verifyDeposit is called for the first deposit it sets the `withdrawableEpoch` for that\n // deposit and mark validator as exiting\n // - the verifyDeposit also needs to be called for the second deposit so it can have the\n // `withdrawableEpoch` set.\n require(\n strategyValidator.state == ValidatorState.VERIFIED ||\n strategyValidator.state == ValidatorState.EXITING,\n \"Validator not verified/exiting\"\n );\n // The verification slot must be after the deposit's slot.\n // This is needed for when the deposit queue is empty.\n require(deposit.slot < depositProcessedSlot, \"Slot not after deposit\");\n uint64 snapTimestamp = snappedBalance.timestamp;\n\n // This check prevents an accounting error that can happen if:\n // - snapBalances are snapped at the time of T\n // - deposit is processed on the beacon chain after time T and before verifyBalances()\n // - verifyDeposit is called before verifyBalances which removes a deposit from depositList\n // and deposit balance from totalDepositsWei\n // - verifyBalances is called under-reporting the strategy's balance\n require(\n (_calcNextBlockTimestamp(depositProcessedSlot) <= snapTimestamp) ||\n snapTimestamp == 0,\n \"Deposit after balance snapshot\"\n );\n\n // Get the parent beacon block root of the next block which is the block root of the deposit verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 depositBlockRoot = BeaconRoots.parentBlockRoot(\n _calcNextBlockTimestamp(depositProcessedSlot)\n );\n\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n depositBlockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.proof\n );\n\n // Verify the withdrawableEpoch on the validator of the strategy's deposit\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n depositBlockRoot,\n strategyValidator.index,\n strategyValidatorData.withdrawableEpoch,\n strategyValidatorData.withdrawableEpochProof\n );\n\n // If the validator is exiting because it has been slashed\n if (strategyValidatorData.withdrawableEpoch != FAR_FUTURE_EPOCH) {\n // Store the exit epoch in the deposit data\n deposits[depositID].withdrawableEpoch = strategyValidatorData\n .withdrawableEpoch;\n\n emit DepositToValidatorExiting(\n depositID,\n uint256(deposit.amountGwei) * 1 gwei,\n strategyValidatorData.withdrawableEpoch\n );\n\n validator[deposit.pubKeyHash].state = ValidatorState.EXITING;\n\n // Leave the deposit status as PENDING\n return;\n }\n\n // solhint-disable max-line-length\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the deposit queue is empty then our deposit must have been processed on the beacon chain.\n // The deposit slot can be zero for validators consolidating to a compounding validator or 0x01 validator\n // being promoted to a compounding one. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // We can not guarantee that the deposit has been processed in that case.\n // solhint-enable max-line-length\n require(\n deposit.slot < firstPendingDeposit.slot || isDepositQueueEmpty,\n \"Deposit likely not processed\"\n );\n\n // Remove the deposit now it has been verified as processed on the beacon chain.\n _removeDeposit(depositID, deposit);\n\n emit DepositVerified(depositID, uint256(deposit.amountGwei) * 1 gwei);\n }\n\n function _removeDeposit(uint256 depositID, DepositData memory deposit)\n internal\n {\n // After verifying the proof, update the contract storage\n deposits[depositID].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n uint256 lastDeposit = depositList[depositList.length - 1];\n depositList[deposit.depositIndex] = lastDeposit;\n deposits[lastDeposit].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositList.pop();\n }\n\n /// @dev Calculates the timestamp of the next execution block from the given slot.\n /// @param slot The beacon chain slot number used for merkle proof verification.\n function _calcNextBlockTimestamp(uint64 slot)\n internal\n view\n returns (uint64)\n {\n // Calculate the next block timestamp from the slot.\n return SLOT_DURATION * slot + BEACON_GENESIS_TIMESTAMP + SLOT_DURATION;\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block and beacon block root\n /// of the slot that is associated with the previous block.\n ///\n /// When snapping / verifying balance it is of a high importance that there is no\n /// miss-match in respect to ETH that is held by the contract and balances that are\n /// verified on the validators.\n ///\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\n /// constructing a block on the beacon chain consist of:\n /// - process_withdrawals: ETH is deducted from the validator's balance\n /// - process_execution_payload: immediately after the previous step executing all the\n /// transactions\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\n /// contained in the withdrawal credentials of the exited validators\n ///\n /// That means that balance increases which are part of the post-block execution state are\n /// done within the block, but the transaction that are contained within that block can not\n /// see / interact with the balance from the exited validators. Only transactions in the\n /// next block can do that.\n ///\n /// When snap balances is performed the state of the chain is snapped across 2 separate\n /// chain states:\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\n /// will be referred to as Y - 1 as it makes no difference in the argument\n ///\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\n /// miss-counting ETH or much more dangerous double counting of the ETH.\n ///\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\n /// - ETH balance in the contract on block X\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\n /// - ETH balance in validators that are active in slot Y - 1\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\n /// and have their balance visible to transactions in slot Y and corresponding block X\n /// (or sooner)\n ///\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\n ///\n /// *ETH balance in the contract on block X*\n ///\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\n ///\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\n /// would result in double counting the `stakedEth` since it would be present once in the\n /// snapped contract balance and the second time in deposit storage variables.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\n ///\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\n /// contract at block Z. The execution layer doesn't have direct access to the state of\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\n /// and could also be part of the validator balances. It does that by verifying that at\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\n /// the last snap till now all are still in queue. Which ensures they can not be part of\n /// the validator balances in later steps.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in validators that are active in slot Y - 1*\n ///\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\n /// correctness.\n ///\n /// This behaviour is correct.\n ///\n /// *The withdrawn validators*\n ///\n /// The withdrawn validators could have their balances deducted in any slot before slot\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\n /// look at the \"worst case scenario\" where the validator withdrawal is processed in the\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\n /// even if the validator exits at the latest possible time it is paramount that the ETH\n /// balance on the execution layer is recorded in the next block. Correctly accounting\n /// for the withdrawn ETH.\n ///\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\n /// validator balance verification as well as execution layer contract balance snap.\n ///\n /// This behaviour is correct.\n ///\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n function snapBalances() external {\n uint64 currentTimestamp = SafeCast.toUint64(block.timestamp);\n require(\n snappedBalance.timestamp + SNAP_BALANCES_DELAY < currentTimestamp,\n \"Snap too soon\"\n );\n\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(currentTimestamp);\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the snapped balance\n snappedBalance = Balances({\n blockRoot: blockRoot,\n timestamp: currentTimestamp,\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n emit BalancesSnapped(blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct BalanceProofs {\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n /// @param validatorVerificationBlockTimestamp next block's timestamp of a slot that has the first pending\n /// deposit already applied to the validator.\n /// @param firstPendingDeposit a `FirstPendingDepositWithdrawableProofData` struct containing:\n /// - slot: The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero is a good choice.\n /// - validatorIndex: The index of the validator of the first pending deposit.\n /// Can be anything if the deposit queue is empty, but zero is a good choice.\n /// - pubKeyHash: The hash of the public key of the validator of the first pending deposit.\n /// Can be anything if the deposit queue is empty, but 32 zero bytes is a good choice\n /// - pendingDepositPubKeyProof: The merkle proof of the first pending deposit to the beacon block root.\n /// Can be either:\n /// * 40 witness hashes for BeaconBlock.state.PendingDeposits[0].pubKey when the deposit queue is not empty.\n /// * 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the first pending deposit's validator\n /// to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// - validatorPubKeyProof The merkle proof for the public key of the first pending deposit's validator\n /// to the this witness hash of withdrawableEpochProof.\n /// This is 2 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param balanceProofs a `BalanceProofs` struct containing the following:\n /// - balancesContainerRoot: The merkle root of the balances container\n /// - balancesContainerProof: The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// - validatorBalanceLeaves: Array of leaf nodes containing the validator balance with three other balances.\n /// - validatorBalanceProofs: Array of merkle proofs for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(\n uint64 validatorVerificationBlockTimestamp,\n FirstPendingDepositWithdrawableProofData calldata firstPendingDeposit,\n BalanceProofs calldata balanceProofs\n ) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalance;\n // Check the balances are the latest\n require(balancesMem.timestamp > 0, \"No snapped balances\");\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n balanceProofs.validatorBalanceProofs.length ==\n verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n balanceProofs.validatorBalanceLeaves.length ==\n verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n balancesMem.blockRoot,\n balanceProofs.balancesContainerRoot,\n balanceProofs.balancesContainerProof\n );\n\n // for each validator in reverse order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n balanceProofs.balancesContainerRoot,\n balanceProofs.validatorBalanceLeaves[i],\n balanceProofs.validatorBalanceProofs[i],\n validator[verifiedValidators[i]].index\n );\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n // This could have been in VERIFIED or EXITING state\n validator[verifiedValidators[i]].state = ValidatorState\n .EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += validatorBalanceGwei * 1 gwei;\n }\n }\n\n uint256 depositsCount = depositList.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification.\n // This section is after the validator balance verifications so an exited validator will be marked\n // as EXITED before the deposits are verified. If there was a deposit to an exited validator\n // then the deposit can only be removed once the validator is fully exited.\n // It is possible that validator fully exits and a postponed deposit to an exited validator increases\n // its balance again. In such case the contract will erroneously consider a deposit applied before it\n // has been applied on the beacon chain showing a smaller than real `totalValidatorBalance`.\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n balancesMem.blockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.pubKeyHash,\n firstPendingDeposit.pendingDepositPubKeyProof\n );\n\n // If there are no deposits in the beacon chain queue then our deposits must have been processed.\n // If the deposits have been processed, each deposit will need to be verified with `verifyDeposit`\n require(!isDepositQueueEmpty, \"Deposits have been processed\");\n\n // The verification of the validator the first pending deposit is for must be on or after when\n // `snapBalances` was called.\n require(\n balancesMem.timestamp <= validatorVerificationBlockTimestamp,\n \"Invalid validator timestamp\"\n );\n\n // Verify the validator of the first pending deposit is not exiting by checking\n // the withdrawable epoch is far into the future.\n // If it is exiting we can't be sure this deposit has not been postponed in the deposit queue.\n // Hence we can not verify if the strategy's deposit has been processed or not.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n // Get the parent beacon block root of the next block which is\n // the block root of the validator verification slot.\n // This will revert if the slot after the verification slot was missed.\n BeaconRoots.parentBlockRoot(\n validatorVerificationBlockTimestamp\n ),\n firstPendingDeposit.validatorIndex,\n firstPendingDeposit.pubKeyHash,\n // Validator is not exiting\n FAR_FUTURE_EPOCH,\n firstPendingDeposit.withdrawableEpochProof,\n firstPendingDeposit.validatorPubKeyProof\n );\n\n // solhint-disable max-line-length\n // If a validator is converted from a sweeping validator to a compounding validator, any balance in excess\n // of the min 32 ETH is put in the pending deposit queue. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // This will have a slot value of zero unfortunately.\n // We can not prove the strategy's deposits are still pending with a zero slot value so revert the tx.\n // Another snapBalances will need to be taken that does not have consolidation deposits at the front of the\n // beacon chain deposit queue.\n // solhint-enable max-line-length\n require(\n firstPendingDeposit.slot > 0,\n \"Invalid first pending deposit\"\n );\n\n // Calculate the epoch for the slot before snapBalances which is 12 seconds before the snap timestamp.\n // The block root of the snapped balances is the parent block root so is for the previous slot.\n // If the parent slot was missed, then the block root will belong to the last non-missed slot. This could\n // result in a verificationEpoch that is 1 more than the actual epoch of the slot before snapBalances.\n // This means verifyBalances fails with `Deposit likely processed` and a new snapBalances has to be taken\n // after the exiting validator's balance has been swept.\n uint64 verificationEpoch = (SafeCast.toUint64(\n balancesMem.timestamp - 12\n ) - BEACON_GENESIS_TIMESTAMP) / (SLOT_DURATION * SLOTS_PER_EPOCH);\n\n // For each staking strategy's deposit.\n // Iterate in reverse order so we can pop off deposits at the end of the storage array.\n for (uint256 i = depositsCount; i > 0; ) {\n --i;\n uint256 depositID = depositList[i];\n DepositData memory depositData = deposits[depositID];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit slot is before the slot of the staking strategy's deposit.\n // If the deposit has been processed, it will need to be verified with `verifyDeposit`.\n // OR the deposit is to an exiting validator so check it is still not withdrawable.\n // If the validator is not withdrawable, then the deposit can not have been processed yet.\n // If the validator is now withdrawable, then the deposit may have been processed. The strategy\n // now has to wait until the validator's balance is verified to be zero.\n // OR the validator has exited and the deposit is now verified as processed.\n require(\n firstPendingDeposit.slot < depositData.slot ||\n (verificationEpoch < depositData.withdrawableEpoch &&\n depositData.withdrawableEpoch !=\n FAR_FUTURE_EPOCH) ||\n validator[depositData.pubKeyHash].state ==\n ValidatorState.EXITED,\n \"Deposit likely processed\"\n );\n\n // Remove the deposit if the validator has exited.\n if (\n validator[depositData.pubKeyHash].state ==\n ValidatorState.EXITED\n ) {\n _removeDeposit(depositID, depositData);\n\n // Reduce the count of deposits that needs to be iterated over\n depositsCount -= 1;\n\n emit DepositValidatorExited(\n depositID,\n uint256(depositData.amountGwei) * 1 gwei\n );\n\n // Skip to the next deposit as the deposit amount is now in the strategy's ETH balance\n continue;\n }\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei += uint256(depositData.amountGwei) * 1 gwei;\n }\n }\n\n // Store the verified balance in storage\n lastVerifiedEthBalance =\n totalDepositsWei +\n totalValidatorBalance +\n balancesMem.ethBalance;\n // Reset the last snap timestamp so a new snapBalances has to be made\n snappedBalance.timestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n require(pubKey.length == 48, \"Invalid public key length\");\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /**\n *\n * WETH and ETH Accounting\n *\n */\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n // The min is required as more WETH can be withdrawn than deposited\n // as the strategy earns consensus and execution rewards.\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance.\n // The ETH balance in this strategy contract can be more than the last verified ETH balance\n // due to partial withdrawals or full exits being processed by the beacon chain since the last snapBalances.\n // It can also happen from execution rewards (MEV) or ETH donations.\n lastVerifiedEthBalance -= Math.min(lastVerifiedEthBalance, _ethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n snappedBalance.timestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += _wethAmount;\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n snappedBalance.timestamp = 0;\n }\n\n /**\n *\n * View Functions\n *\n */\n\n /// @notice Returns the number of deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n function depositListLength() external view returns (uint256) {\n return depositList.length;\n }\n\n /// @notice Returns the number of verified validators.\n function verifiedValidatorsLength() external view returns (uint256) {\n return verifiedValidators.length;\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/5518586a577179807341642341c56a4f.json b/contracts/deployments/hoodi/solcInputs/5518586a577179807341642341c56a4f.json new file mode 100644 index 0000000000..98569f8a4c --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/5518586a577179807341642341c56a4f.json @@ -0,0 +1,720 @@ +{ + "language": "Solidity", + "sources": { + "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {Client} from \"../libraries/Client.sol\";\n\ninterface IRouterClient {\n error UnsupportedDestinationChain(uint64 destChainSelector);\n error InsufficientFeeTokenAmount();\n error InvalidMsgValue();\n\n /// @notice Checks if the given chain ID is supported for sending/receiving.\n /// @param chainSelector The chain to check.\n /// @return supported is true if it is supported, false if not.\n function isChainSupported(uint64 chainSelector) external view returns (bool supported);\n\n /// @notice Gets a list of all supported tokens which can be sent or received\n /// to/from a given chain id.\n /// @param chainSelector The chainSelector.\n /// @return tokens The addresses of all tokens that are supported.\n function getSupportedTokens(uint64 chainSelector) external view returns (address[] memory tokens);\n\n /// @param destinationChainSelector The destination chainSelector\n /// @param message The cross-chain CCIP message including data and/or tokens\n /// @return fee returns execution fee for the message\n /// delivery to destination chain, denominated in the feeToken specified in the message.\n /// @dev Reverts with appropriate reason upon invalid message.\n function getFee(\n uint64 destinationChainSelector,\n Client.EVM2AnyMessage memory message\n ) external view returns (uint256 fee);\n\n /// @notice Request a message to be sent to the destination chain\n /// @param destinationChainSelector The destination chain ID\n /// @param message The cross-chain CCIP message including data and/or tokens\n /// @return messageId The message ID\n /// @dev Note if msg.value is larger than the required fee (from getFee) we accept\n /// the overpayment with no refund.\n /// @dev Reverts with appropriate reason upon invalid message.\n function ccipSend(\n uint64 destinationChainSelector,\n Client.EVM2AnyMessage calldata message\n ) external payable returns (bytes32);\n}\n" + }, + "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// End consumer library.\nlibrary Client {\n /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.\n struct EVMTokenAmount {\n address token; // token address on the local chain.\n uint256 amount; // Amount of tokens.\n }\n\n struct Any2EVMMessage {\n bytes32 messageId; // MessageId corresponding to ccipSend on source.\n uint64 sourceChainSelector; // Source chain selector.\n bytes sender; // abi.decode(sender) if coming from an EVM chain.\n bytes data; // payload sent in original message.\n EVMTokenAmount[] destTokenAmounts; // Tokens and their amounts in their destination chain representation.\n }\n\n // If extraArgs is empty bytes, the default is 200k gas limit.\n struct EVM2AnyMessage {\n bytes receiver; // abi.encode(receiver address) for dest EVM chains\n bytes data; // Data payload\n EVMTokenAmount[] tokenAmounts; // Token transfers\n address feeToken; // Address of feeToken. address(0) means you will send msg.value.\n bytes extraArgs; // Populate this with _argsToBytes(EVMExtraArgsV1)\n }\n\n // bytes4(keccak256(\"CCIP EVMExtraArgsV1\"));\n bytes4 public constant EVM_EXTRA_ARGS_V1_TAG = 0x97a657c9;\n struct EVMExtraArgsV1 {\n uint256 gasLimit;\n }\n\n function _argsToBytes(EVMExtraArgsV1 memory extraArgs) internal pure returns (bytes memory bts) {\n return abi.encodeWithSelector(EVM_EXTRA_ARGS_V1_TAG, extraArgs);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport \"@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol\";\n\nlibrary ExecutorOptions {\n using CalldataBytesLib for bytes;\n\n uint8 internal constant WORKER_ID = 1;\n\n uint8 internal constant OPTION_TYPE_LZRECEIVE = 1;\n uint8 internal constant OPTION_TYPE_NATIVE_DROP = 2;\n uint8 internal constant OPTION_TYPE_LZCOMPOSE = 3;\n uint8 internal constant OPTION_TYPE_ORDERED_EXECUTION = 4;\n uint8 internal constant OPTION_TYPE_LZREAD = 5;\n\n error Executor_InvalidLzReceiveOption();\n error Executor_InvalidNativeDropOption();\n error Executor_InvalidLzComposeOption();\n error Executor_InvalidLzReadOption();\n\n /// @dev decode the next executor option from the options starting from the specified cursor\n /// @param _options [executor_id][executor_option][executor_id][executor_option]...\n /// executor_option = [option_size][option_type][option]\n /// option_size = len(option_type) + len(option)\n /// executor_id: uint8, option_size: uint16, option_type: uint8, option: bytes\n /// @param _cursor the cursor to start decoding from\n /// @return optionType the type of the option\n /// @return option the option of the executor\n /// @return cursor the cursor to start decoding the next executor option\n function nextExecutorOption(\n bytes calldata _options,\n uint256 _cursor\n ) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) {\n unchecked {\n // skip worker id\n cursor = _cursor + 1;\n\n // read option size\n uint16 size = _options.toU16(cursor);\n cursor += 2;\n\n // read option type\n optionType = _options.toU8(cursor);\n\n // startCursor and endCursor are used to slice the option from _options\n uint256 startCursor = cursor + 1; // skip option type\n uint256 endCursor = cursor + size;\n option = _options[startCursor:endCursor];\n cursor += size;\n }\n }\n\n function decodeLzReceiveOption(bytes calldata _option) internal pure returns (uint128 gas, uint128 value) {\n if (_option.length != 16 && _option.length != 32) revert Executor_InvalidLzReceiveOption();\n gas = _option.toU128(0);\n value = _option.length == 32 ? _option.toU128(16) : 0;\n }\n\n function decodeNativeDropOption(bytes calldata _option) internal pure returns (uint128 amount, bytes32 receiver) {\n if (_option.length != 48) revert Executor_InvalidNativeDropOption();\n amount = _option.toU128(0);\n receiver = _option.toB32(16);\n }\n\n function decodeLzComposeOption(\n bytes calldata _option\n ) internal pure returns (uint16 index, uint128 gas, uint128 value) {\n if (_option.length != 18 && _option.length != 34) revert Executor_InvalidLzComposeOption();\n index = _option.toU16(0);\n gas = _option.toU128(2);\n value = _option.length == 34 ? _option.toU128(18) : 0;\n }\n\n function decodeLzReadOption(\n bytes calldata _option\n ) internal pure returns (uint128 gas, uint32 calldataSize, uint128 value) {\n if (_option.length != 20 && _option.length != 36) revert Executor_InvalidLzReadOption();\n gas = _option.toU128(0);\n calldataSize = _option.toU32(16);\n value = _option.length == 36 ? _option.toU128(20) : 0;\n }\n\n function encodeLzReceiveOption(uint128 _gas, uint128 _value) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_gas) : abi.encodePacked(_gas, _value);\n }\n\n function encodeNativeDropOption(uint128 _amount, bytes32 _receiver) internal pure returns (bytes memory) {\n return abi.encodePacked(_amount, _receiver);\n }\n\n function encodeLzComposeOption(uint16 _index, uint128 _gas, uint128 _value) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_index, _gas) : abi.encodePacked(_index, _gas, _value);\n }\n\n function encodeLzReadOption(\n uint128 _gas,\n uint32 _calldataSize,\n uint128 _value\n ) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_gas, _calldataSize) : abi.encodePacked(_gas, _calldataSize, _value);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/DVNOptions.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport { BytesLib } from \"solidity-bytes-utils/contracts/BytesLib.sol\";\n\nimport { BitMap256 } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/BitMaps.sol\";\nimport { CalldataBytesLib } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol\";\n\nlibrary DVNOptions {\n using CalldataBytesLib for bytes;\n using BytesLib for bytes;\n\n uint8 internal constant WORKER_ID = 2;\n uint8 internal constant OPTION_TYPE_PRECRIME = 1;\n\n error DVN_InvalidDVNIdx();\n error DVN_InvalidDVNOptions(uint256 cursor);\n\n /// @dev group dvn options by its idx\n /// @param _options [dvn_id][dvn_option][dvn_id][dvn_option]...\n /// dvn_option = [option_size][dvn_idx][option_type][option]\n /// option_size = len(dvn_idx) + len(option_type) + len(option)\n /// dvn_id: uint8, dvn_idx: uint8, option_size: uint16, option_type: uint8, option: bytes\n /// @return dvnOptions the grouped options, still share the same format of _options\n /// @return dvnIndices the dvn indices\n function groupDVNOptionsByIdx(\n bytes memory _options\n ) internal pure returns (bytes[] memory dvnOptions, uint8[] memory dvnIndices) {\n if (_options.length == 0) return (dvnOptions, dvnIndices);\n\n uint8 numDVNs = getNumDVNs(_options);\n\n // if there is only 1 dvn, we can just return the whole options\n if (numDVNs == 1) {\n dvnOptions = new bytes[](1);\n dvnOptions[0] = _options;\n\n dvnIndices = new uint8[](1);\n dvnIndices[0] = _options.toUint8(3); // dvn idx\n return (dvnOptions, dvnIndices);\n }\n\n // otherwise, we need to group the options by dvn_idx\n dvnIndices = new uint8[](numDVNs);\n dvnOptions = new bytes[](numDVNs);\n unchecked {\n uint256 cursor = 0;\n uint256 start = 0;\n uint8 lastDVNIdx = 255; // 255 is an invalid dvn_idx\n\n while (cursor < _options.length) {\n ++cursor; // skip worker_id\n\n // optionLength asserted in getNumDVNs (skip check)\n uint16 optionLength = _options.toUint16(cursor);\n cursor += 2;\n\n // dvnIdx asserted in getNumDVNs (skip check)\n uint8 dvnIdx = _options.toUint8(cursor);\n\n // dvnIdx must equal to the lastDVNIdx for the first option\n // so it is always skipped in the first option\n // this operation slices out options whenever the scan finds a different lastDVNIdx\n if (lastDVNIdx == 255) {\n lastDVNIdx = dvnIdx;\n } else if (dvnIdx != lastDVNIdx) {\n uint256 len = cursor - start - 3; // 3 is for worker_id and option_length\n bytes memory opt = _options.slice(start, len);\n _insertDVNOptions(dvnOptions, dvnIndices, lastDVNIdx, opt);\n\n // reset the start and lastDVNIdx\n start += len;\n lastDVNIdx = dvnIdx;\n }\n\n cursor += optionLength;\n }\n\n // skip check the cursor here because the cursor is asserted in getNumDVNs\n // if we have reached the end of the options, we need to process the last dvn\n uint256 size = cursor - start;\n bytes memory op = _options.slice(start, size);\n _insertDVNOptions(dvnOptions, dvnIndices, lastDVNIdx, op);\n\n // revert dvnIndices to start from 0\n for (uint8 i = 0; i < numDVNs; ++i) {\n --dvnIndices[i];\n }\n }\n }\n\n function _insertDVNOptions(\n bytes[] memory _dvnOptions,\n uint8[] memory _dvnIndices,\n uint8 _dvnIdx,\n bytes memory _newOptions\n ) internal pure {\n // dvnIdx starts from 0 but default value of dvnIndices is 0,\n // so we tell if the slot is empty by adding 1 to dvnIdx\n if (_dvnIdx == 255) revert DVN_InvalidDVNIdx();\n uint8 dvnIdxAdj = _dvnIdx + 1;\n\n for (uint256 j = 0; j < _dvnIndices.length; ++j) {\n uint8 index = _dvnIndices[j];\n if (dvnIdxAdj == index) {\n _dvnOptions[j] = abi.encodePacked(_dvnOptions[j], _newOptions);\n break;\n } else if (index == 0) {\n // empty slot, that means it is the first time we see this dvn\n _dvnIndices[j] = dvnIdxAdj;\n _dvnOptions[j] = _newOptions;\n break;\n }\n }\n }\n\n /// @dev get the number of unique dvns\n /// @param _options the format is the same as groupDVNOptionsByIdx\n function getNumDVNs(bytes memory _options) internal pure returns (uint8 numDVNs) {\n uint256 cursor = 0;\n BitMap256 bitmap;\n\n // find number of unique dvn_idx\n unchecked {\n while (cursor < _options.length) {\n ++cursor; // skip worker_id\n\n uint16 optionLength = _options.toUint16(cursor);\n cursor += 2;\n if (optionLength < 2) revert DVN_InvalidDVNOptions(cursor); // at least 1 byte for dvn_idx and 1 byte for option_type\n\n uint8 dvnIdx = _options.toUint8(cursor);\n\n // if dvnIdx is not set, increment numDVNs\n // max num of dvns is 255, 255 is an invalid dvn_idx\n // The order of the dvnIdx is not required to be sequential, as enforcing the order may weaken\n // the composability of the options. e.g. if we refrain from enforcing the order, an OApp that has\n // already enforced certain options can append additional options to the end of the enforced\n // ones without restrictions.\n if (dvnIdx == 255) revert DVN_InvalidDVNIdx();\n if (!bitmap.get(dvnIdx)) {\n ++numDVNs;\n bitmap = bitmap.set(dvnIdx);\n }\n\n cursor += optionLength;\n }\n }\n if (cursor != _options.length) revert DVN_InvalidDVNOptions(cursor);\n }\n\n /// @dev decode the next dvn option from _options starting from the specified cursor\n /// @param _options the format is the same as groupDVNOptionsByIdx\n /// @param _cursor the cursor to start decoding\n /// @return optionType the type of the option\n /// @return option the option\n /// @return cursor the cursor to start decoding the next option\n function nextDVNOption(\n bytes calldata _options,\n uint256 _cursor\n ) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) {\n unchecked {\n // skip worker id\n cursor = _cursor + 1;\n\n // read option size\n uint16 size = _options.toU16(cursor);\n cursor += 2;\n\n // read option type\n optionType = _options.toU8(cursor + 1); // skip dvn_idx\n\n // startCursor and endCursor are used to slice the option from _options\n uint256 startCursor = cursor + 2; // skip option type and dvn_idx\n uint256 endCursor = cursor + size;\n option = _options[startCursor:endCursor];\n cursor += size;\n }\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { IMessageLibManager } from \"./IMessageLibManager.sol\";\nimport { IMessagingComposer } from \"./IMessagingComposer.sol\";\nimport { IMessagingChannel } from \"./IMessagingChannel.sol\";\nimport { IMessagingContext } from \"./IMessagingContext.sol\";\n\nstruct MessagingParams {\n uint32 dstEid;\n bytes32 receiver;\n bytes message;\n bytes options;\n bool payInLzToken;\n}\n\nstruct MessagingReceipt {\n bytes32 guid;\n uint64 nonce;\n MessagingFee fee;\n}\n\nstruct MessagingFee {\n uint256 nativeFee;\n uint256 lzTokenFee;\n}\n\nstruct Origin {\n uint32 srcEid;\n bytes32 sender;\n uint64 nonce;\n}\n\ninterface ILayerZeroEndpointV2 is IMessageLibManager, IMessagingComposer, IMessagingChannel, IMessagingContext {\n event PacketSent(bytes encodedPayload, bytes options, address sendLibrary);\n\n event PacketVerified(Origin origin, address receiver, bytes32 payloadHash);\n\n event PacketDelivered(Origin origin, address receiver);\n\n event LzReceiveAlert(\n address indexed receiver,\n address indexed executor,\n Origin origin,\n bytes32 guid,\n uint256 gas,\n uint256 value,\n bytes message,\n bytes extraData,\n bytes reason\n );\n\n event LzTokenSet(address token);\n\n event DelegateSet(address sender, address delegate);\n\n function quote(MessagingParams calldata _params, address _sender) external view returns (MessagingFee memory);\n\n function send(\n MessagingParams calldata _params,\n address _refundAddress\n ) external payable returns (MessagingReceipt memory);\n\n function verify(Origin calldata _origin, address _receiver, bytes32 _payloadHash) external;\n\n function verifiable(Origin calldata _origin, address _receiver) external view returns (bool);\n\n function initializable(Origin calldata _origin, address _receiver) external view returns (bool);\n\n function lzReceive(\n Origin calldata _origin,\n address _receiver,\n bytes32 _guid,\n bytes calldata _message,\n bytes calldata _extraData\n ) external payable;\n\n // oapp can burn messages partially by calling this function with its own business logic if messages are verified in order\n function clear(address _oapp, Origin calldata _origin, bytes32 _guid, bytes calldata _message) external;\n\n function setLzToken(address _lzToken) external;\n\n function lzToken() external view returns (address);\n\n function nativeToken() external view returns (address);\n\n function setDelegate(address _delegate) external;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessageLibManager.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nstruct SetConfigParam {\n uint32 eid;\n uint32 configType;\n bytes config;\n}\n\ninterface IMessageLibManager {\n struct Timeout {\n address lib;\n uint256 expiry;\n }\n\n event LibraryRegistered(address newLib);\n event DefaultSendLibrarySet(uint32 eid, address newLib);\n event DefaultReceiveLibrarySet(uint32 eid, address newLib);\n event DefaultReceiveLibraryTimeoutSet(uint32 eid, address oldLib, uint256 expiry);\n event SendLibrarySet(address sender, uint32 eid, address newLib);\n event ReceiveLibrarySet(address receiver, uint32 eid, address newLib);\n event ReceiveLibraryTimeoutSet(address receiver, uint32 eid, address oldLib, uint256 timeout);\n\n function registerLibrary(address _lib) external;\n\n function isRegisteredLibrary(address _lib) external view returns (bool);\n\n function getRegisteredLibraries() external view returns (address[] memory);\n\n function setDefaultSendLibrary(uint32 _eid, address _newLib) external;\n\n function defaultSendLibrary(uint32 _eid) external view returns (address);\n\n function setDefaultReceiveLibrary(uint32 _eid, address _newLib, uint256 _gracePeriod) external;\n\n function defaultReceiveLibrary(uint32 _eid) external view returns (address);\n\n function setDefaultReceiveLibraryTimeout(uint32 _eid, address _lib, uint256 _expiry) external;\n\n function defaultReceiveLibraryTimeout(uint32 _eid) external view returns (address lib, uint256 expiry);\n\n function isSupportedEid(uint32 _eid) external view returns (bool);\n\n function isValidReceiveLibrary(address _receiver, uint32 _eid, address _lib) external view returns (bool);\n\n /// ------------------- OApp interfaces -------------------\n function setSendLibrary(address _oapp, uint32 _eid, address _newLib) external;\n\n function getSendLibrary(address _sender, uint32 _eid) external view returns (address lib);\n\n function isDefaultSendLibrary(address _sender, uint32 _eid) external view returns (bool);\n\n function setReceiveLibrary(address _oapp, uint32 _eid, address _newLib, uint256 _gracePeriod) external;\n\n function getReceiveLibrary(address _receiver, uint32 _eid) external view returns (address lib, bool isDefault);\n\n function setReceiveLibraryTimeout(address _oapp, uint32 _eid, address _lib, uint256 _expiry) external;\n\n function receiveLibraryTimeout(address _receiver, uint32 _eid) external view returns (address lib, uint256 expiry);\n\n function setConfig(address _oapp, address _lib, SetConfigParam[] calldata _params) external;\n\n function getConfig(\n address _oapp,\n address _lib,\n uint32 _eid,\n uint32 _configType\n ) external view returns (bytes memory config);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingChannel.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingChannel {\n event InboundNonceSkipped(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce);\n event PacketNilified(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);\n event PacketBurnt(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);\n\n function eid() external view returns (uint32);\n\n // this is an emergency function if a message cannot be verified for some reasons\n // required to provide _nextNonce to avoid race condition\n function skip(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce) external;\n\n function nilify(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;\n\n function burn(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;\n\n function nextGuid(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (bytes32);\n\n function inboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);\n\n function outboundNonce(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (uint64);\n\n function inboundPayloadHash(\n address _receiver,\n uint32 _srcEid,\n bytes32 _sender,\n uint64 _nonce\n ) external view returns (bytes32);\n\n function lazyInboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingComposer.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingComposer {\n event ComposeSent(address from, address to, bytes32 guid, uint16 index, bytes message);\n event ComposeDelivered(address from, address to, bytes32 guid, uint16 index);\n event LzComposeAlert(\n address indexed from,\n address indexed to,\n address indexed executor,\n bytes32 guid,\n uint16 index,\n uint256 gas,\n uint256 value,\n bytes message,\n bytes extraData,\n bytes reason\n );\n\n function composeQueue(\n address _from,\n address _to,\n bytes32 _guid,\n uint16 _index\n ) external view returns (bytes32 messageHash);\n\n function sendCompose(address _to, bytes32 _guid, uint16 _index, bytes calldata _message) external;\n\n function lzCompose(\n address _from,\n address _to,\n bytes32 _guid,\n uint16 _index,\n bytes calldata _message,\n bytes calldata _extraData\n ) external payable;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingContext.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingContext {\n function isSendingMessage() external view returns (bool);\n\n function getSendContext() external view returns (uint32 dstEid, address sender);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nlibrary CalldataBytesLib {\n function toU8(bytes calldata _bytes, uint256 _start) internal pure returns (uint8) {\n return uint8(_bytes[_start]);\n }\n\n function toU16(bytes calldata _bytes, uint256 _start) internal pure returns (uint16) {\n unchecked {\n uint256 end = _start + 2;\n return uint16(bytes2(_bytes[_start:end]));\n }\n }\n\n function toU32(bytes calldata _bytes, uint256 _start) internal pure returns (uint32) {\n unchecked {\n uint256 end = _start + 4;\n return uint32(bytes4(_bytes[_start:end]));\n }\n }\n\n function toU64(bytes calldata _bytes, uint256 _start) internal pure returns (uint64) {\n unchecked {\n uint256 end = _start + 8;\n return uint64(bytes8(_bytes[_start:end]));\n }\n }\n\n function toU128(bytes calldata _bytes, uint256 _start) internal pure returns (uint128) {\n unchecked {\n uint256 end = _start + 16;\n return uint128(bytes16(_bytes[_start:end]));\n }\n }\n\n function toU256(bytes calldata _bytes, uint256 _start) internal pure returns (uint256) {\n unchecked {\n uint256 end = _start + 32;\n return uint256(bytes32(_bytes[_start:end]));\n }\n }\n\n function toAddr(bytes calldata _bytes, uint256 _start) internal pure returns (address) {\n unchecked {\n uint256 end = _start + 20;\n return address(bytes20(_bytes[_start:end]));\n }\n }\n\n function toB32(bytes calldata _bytes, uint256 _start) internal pure returns (bytes32) {\n unchecked {\n uint256 end = _start + 32;\n return bytes32(_bytes[_start:end]);\n }\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/BitMaps.sol": { + "content": "// SPDX-License-Identifier: MIT\n\n// modified from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/structs/BitMaps.sol\npragma solidity ^0.8.20;\n\ntype BitMap256 is uint256;\n\nusing BitMaps for BitMap256 global;\n\nlibrary BitMaps {\n /**\n * @dev Returns whether the bit at `index` is set.\n */\n function get(BitMap256 bitmap, uint8 index) internal pure returns (bool) {\n uint256 mask = 1 << index;\n return BitMap256.unwrap(bitmap) & mask != 0;\n }\n\n /**\n * @dev Sets the bit at `index`.\n */\n function set(BitMap256 bitmap, uint8 index) internal pure returns (BitMap256) {\n uint256 mask = 1 << index;\n return BitMap256.wrap(BitMap256.unwrap(bitmap) | mask);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { ILayerZeroEndpointV2 } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\n\n/**\n * @title IOAppCore\n */\ninterface IOAppCore {\n // Custom error messages\n error OnlyPeer(uint32 eid, bytes32 sender);\n error NoPeer(uint32 eid);\n error InvalidEndpointCall();\n error InvalidDelegate();\n\n // Event emitted when a peer (OApp) is set for a corresponding endpoint\n event PeerSet(uint32 eid, bytes32 peer);\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n */\n function oAppVersion() external view returns (uint64 senderVersion, uint64 receiverVersion);\n\n /**\n * @notice Retrieves the LayerZero endpoint associated with the OApp.\n * @return iEndpoint The LayerZero endpoint as an interface.\n */\n function endpoint() external view returns (ILayerZeroEndpointV2 iEndpoint);\n\n /**\n * @notice Retrieves the peer (OApp) associated with a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @return peer The peer address (OApp instance) associated with the corresponding endpoint.\n */\n function peers(uint32 _eid) external view returns (bytes32 peer);\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n */\n function setPeer(uint32 _eid, bytes32 _peer) external;\n\n /**\n * @notice Sets the delegate address for the OApp Core.\n * @param _delegate The address of the delegate to be set.\n */\n function setDelegate(address _delegate) external;\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { BytesLib } from \"solidity-bytes-utils/contracts/BytesLib.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { ExecutorOptions } from \"@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol\";\nimport { DVNOptions } from \"@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/DVNOptions.sol\";\n\n/**\n * @title OptionsBuilder\n * @dev Library for building and encoding various message options.\n */\nlibrary OptionsBuilder {\n using SafeCast for uint256;\n using BytesLib for bytes;\n\n // Constants for options types\n uint16 internal constant TYPE_1 = 1; // legacy options type 1\n uint16 internal constant TYPE_2 = 2; // legacy options type 2\n uint16 internal constant TYPE_3 = 3;\n\n // Custom error message\n error InvalidSize(uint256 max, uint256 actual);\n error InvalidOptionType(uint16 optionType);\n\n // Modifier to ensure only options of type 3 are used\n modifier onlyType3(bytes memory _options) {\n if (_options.toUint16(0) != TYPE_3) revert InvalidOptionType(_options.toUint16(0));\n _;\n }\n\n /**\n * @dev Creates a new options container with type 3.\n * @return options The newly created options container.\n */\n function newOptions() internal pure returns (bytes memory) {\n return abi.encodePacked(TYPE_3);\n }\n\n /**\n * @dev Adds an executor LZ receive option to the existing options.\n * @param _options The existing options container.\n * @param _gas The gasLimit used on the lzReceive() function in the OApp.\n * @param _value The msg.value passed to the lzReceive() function in the OApp.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed by the executor\n * eg. if (_gas: 200k, and _value: 1 ether) AND (_gas: 100k, _value: 0.5 ether) are sent in an option to the LayerZeroEndpoint,\n * that becomes (300k, 1.5 ether) when the message is executed on the remote lzReceive() function.\n */\n function addExecutorLzReceiveOption(\n bytes memory _options,\n uint128 _gas,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzReceiveOption(_gas, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZRECEIVE, option);\n }\n\n /**\n * @dev Adds an executor native drop option to the existing options.\n * @param _options The existing options container.\n * @param _amount The amount for the native value that is airdropped to the 'receiver'.\n * @param _receiver The receiver address for the native drop option.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed by the executor on the remote chain.\n */\n function addExecutorNativeDropOption(\n bytes memory _options,\n uint128 _amount,\n bytes32 _receiver\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeNativeDropOption(_amount, _receiver);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_NATIVE_DROP, option);\n }\n\n // /**\n // * @dev Adds an executor native drop option to the existing options.\n // * @param _options The existing options container.\n // * @param _amount The amount for the native value that is airdropped to the 'receiver'.\n // * @param _receiver The receiver address for the native drop option.\n // * @return options The updated options container.\n // *\n // * @dev When multiples of this option are added, they are summed by the executor on the remote chain.\n // */\n function addExecutorLzReadOption(\n bytes memory _options,\n uint128 _gas,\n uint32 _size,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzReadOption(_gas, _size, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZREAD, option);\n }\n\n /**\n * @dev Adds an executor LZ compose option to the existing options.\n * @param _options The existing options container.\n * @param _index The index for the lzCompose() function call.\n * @param _gas The gasLimit for the lzCompose() function call.\n * @param _value The msg.value for the lzCompose() function call.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed PER index by the executor on the remote chain.\n * @dev If the OApp sends N lzCompose calls on the remote, you must provide N incremented indexes starting with 0.\n * ie. When your remote OApp composes (N = 3) messages, you must set this option for index 0,1,2\n */\n function addExecutorLzComposeOption(\n bytes memory _options,\n uint16 _index,\n uint128 _gas,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzComposeOption(_index, _gas, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZCOMPOSE, option);\n }\n\n /**\n * @dev Adds an executor ordered execution option to the existing options.\n * @param _options The existing options container.\n * @return options The updated options container.\n */\n function addExecutorOrderedExecutionOption(\n bytes memory _options\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_ORDERED_EXECUTION, bytes(\"\"));\n }\n\n /**\n * @dev Adds a DVN pre-crime option to the existing options.\n * @param _options The existing options container.\n * @param _dvnIdx The DVN index for the pre-crime option.\n * @return options The updated options container.\n */\n function addDVNPreCrimeOption(\n bytes memory _options,\n uint8 _dvnIdx\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return addDVNOption(_options, _dvnIdx, DVNOptions.OPTION_TYPE_PRECRIME, bytes(\"\"));\n }\n\n /**\n * @dev Adds an executor option to the existing options.\n * @param _options The existing options container.\n * @param _optionType The type of the executor option.\n * @param _option The encoded data for the executor option.\n * @return options The updated options container.\n */\n function addExecutorOption(\n bytes memory _options,\n uint8 _optionType,\n bytes memory _option\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return\n abi.encodePacked(\n _options,\n ExecutorOptions.WORKER_ID,\n _option.length.toUint16() + 1, // +1 for optionType\n _optionType,\n _option\n );\n }\n\n /**\n * @dev Adds a DVN option to the existing options.\n * @param _options The existing options container.\n * @param _dvnIdx The DVN index for the DVN option.\n * @param _optionType The type of the DVN option.\n * @param _option The encoded data for the DVN option.\n * @return options The updated options container.\n */\n function addDVNOption(\n bytes memory _options,\n uint8 _dvnIdx,\n uint8 _optionType,\n bytes memory _option\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return\n abi.encodePacked(\n _options,\n DVNOptions.WORKER_ID,\n _option.length.toUint16() + 2, // +2 for optionType and dvnIdx\n _dvnIdx,\n _optionType,\n _option\n );\n }\n\n /**\n * @dev Encodes legacy options of type 1.\n * @param _executionGas The gasLimit value passed to lzReceive().\n * @return legacyOptions The encoded legacy options.\n */\n function encodeLegacyOptionsType1(uint256 _executionGas) internal pure returns (bytes memory) {\n if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);\n return abi.encodePacked(TYPE_1, _executionGas);\n }\n\n /**\n * @dev Encodes legacy options of type 2.\n * @param _executionGas The gasLimit value passed to lzReceive().\n * @param _nativeForDst The amount of native air dropped to the receiver.\n * @param _receiver The _nativeForDst receiver address.\n * @return legacyOptions The encoded legacy options of type 2.\n */\n function encodeLegacyOptionsType2(\n uint256 _executionGas,\n uint256 _nativeForDst,\n bytes memory _receiver // @dev Use bytes instead of bytes32 in legacy type 2 for _receiver.\n ) internal pure returns (bytes memory) {\n if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);\n if (_nativeForDst > type(uint128).max) revert InvalidSize(type(uint128).max, _nativeForDst);\n if (_receiver.length > 32) revert InvalidSize(32, _receiver.length);\n return abi.encodePacked(TYPE_2, _executionGas, _nativeForDst, _receiver);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IOAppCore, ILayerZeroEndpointV2 } from \"./interfaces/IOAppCore.sol\";\n\n/**\n * @title OAppCore\n * @dev Abstract contract implementing the IOAppCore interface with basic OApp configurations.\n */\nabstract contract OAppCore is IOAppCore, Ownable {\n // The LayerZero endpoint associated with the given OApp\n ILayerZeroEndpointV2 public immutable endpoint;\n\n // Mapping to store peers associated with corresponding endpoints\n mapping(uint32 eid => bytes32 peer) public peers;\n\n /**\n * @dev Constructor to initialize the OAppCore with the provided endpoint and delegate.\n * @param _endpoint The address of the LOCAL Layer Zero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n *\n * @dev The delegate typically should be set as the owner of the contract.\n */\n constructor(address _endpoint, address _delegate) {\n endpoint = ILayerZeroEndpointV2(_endpoint);\n\n if (_delegate == address(0)) revert InvalidDelegate();\n endpoint.setDelegate(_delegate);\n }\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.\n * @dev Set this to bytes32(0) to remove the peer address.\n * @dev Peer is a bytes32 to accommodate non-evm chains.\n */\n function setPeer(uint32 _eid, bytes32 _peer) public virtual onlyOwner {\n _setPeer(_eid, _peer);\n }\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n *\n * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.\n * @dev Set this to bytes32(0) to remove the peer address.\n * @dev Peer is a bytes32 to accommodate non-evm chains.\n */\n function _setPeer(uint32 _eid, bytes32 _peer) internal virtual {\n peers[_eid] = _peer;\n emit PeerSet(_eid, _peer);\n }\n\n /**\n * @notice Internal function to get the peer address associated with a specific endpoint; reverts if NOT set.\n * ie. the peer is set to bytes32(0).\n * @param _eid The endpoint ID.\n * @return peer The address of the peer associated with the specified endpoint.\n */\n function _getPeerOrRevert(uint32 _eid) internal view virtual returns (bytes32) {\n bytes32 peer = peers[_eid];\n if (peer == bytes32(0)) revert NoPeer(_eid);\n return peer;\n }\n\n /**\n * @notice Sets the delegate address for the OApp.\n * @param _delegate The address of the delegate to be set.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Provides the ability for a delegate to set configs, on behalf of the OApp, directly on the Endpoint contract.\n */\n function setDelegate(address _delegate) public onlyOwner {\n endpoint.setDelegate(_delegate);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { SafeERC20, IERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { MessagingParams, MessagingFee, MessagingReceipt } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OAppSender\n * @dev Abstract contract implementing the OAppSender functionality for sending messages to a LayerZero endpoint.\n */\nabstract contract OAppSender is OAppCore {\n using SafeERC20 for IERC20;\n\n // Custom error messages\n error NotEnoughNative(uint256 msgValue);\n error LzTokenUnavailable();\n\n // @dev The version of the OAppSender implementation.\n // @dev Version is bumped when changes are made to this contract.\n uint64 internal constant SENDER_VERSION = 1;\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n *\n * @dev Providing 0 as the default for OAppReceiver version. Indicates that the OAppReceiver is not implemented.\n * ie. this is a SEND only OApp.\n * @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions\n */\n function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {\n return (SENDER_VERSION, 0);\n }\n\n /**\n * @dev Internal function to interact with the LayerZero EndpointV2.quote() for fee calculation.\n * @param _dstEid The destination endpoint ID.\n * @param _message The message payload.\n * @param _options Additional options for the message.\n * @param _payInLzToken Flag indicating whether to pay the fee in LZ tokens.\n * @return fee The calculated MessagingFee for the message.\n * - nativeFee: The native fee for the message.\n * - lzTokenFee: The LZ token fee for the message.\n */\n function _quote(\n uint32 _dstEid,\n bytes memory _message,\n bytes memory _options,\n bool _payInLzToken\n ) internal view virtual returns (MessagingFee memory fee) {\n return\n endpoint.quote(\n MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _payInLzToken),\n address(this)\n );\n }\n\n /**\n * @dev Internal function to interact with the LayerZero EndpointV2.send() for sending a message.\n * @param _dstEid The destination endpoint ID.\n * @param _message The message payload.\n * @param _options Additional options for the message.\n * @param _fee The calculated LayerZero fee for the message.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess fee values sent to the endpoint.\n * @return receipt The receipt for the sent message.\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function _lzSend(\n uint32 _dstEid,\n bytes memory _message,\n bytes memory _options,\n MessagingFee memory _fee,\n address _refundAddress\n ) internal virtual returns (MessagingReceipt memory receipt) {\n // @dev Push corresponding fees to the endpoint, any excess is sent back to the _refundAddress from the endpoint.\n uint256 messageValue = _payNative(_fee.nativeFee);\n if (_fee.lzTokenFee > 0) _payLzToken(_fee.lzTokenFee);\n\n return\n // solhint-disable-next-line check-send-result\n endpoint.send{ value: messageValue }(\n MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _fee.lzTokenFee > 0),\n _refundAddress\n );\n }\n\n /**\n * @dev Internal function to pay the native fee associated with the message.\n * @param _nativeFee The native fee to be paid.\n * @return nativeFee The amount of native currency paid.\n *\n * @dev If the OApp needs to initiate MULTIPLE LayerZero messages in a single transaction,\n * this will need to be overridden because msg.value would contain multiple lzFees.\n * @dev Should be overridden in the event the LayerZero endpoint requires a different native currency.\n * @dev Some EVMs use an ERC20 as a method for paying transactions/gasFees.\n * @dev The endpoint is EITHER/OR, ie. it will NOT support both types of native payment at a time.\n */\n function _payNative(uint256 _nativeFee) internal virtual returns (uint256 nativeFee) {\n if (msg.value != _nativeFee) revert NotEnoughNative(msg.value);\n return _nativeFee;\n }\n\n /**\n * @dev Internal function to pay the LZ token fee associated with the message.\n * @param _lzTokenFee The LZ token fee to be paid.\n *\n * @dev If the caller is trying to pay in the specified lzToken, then the lzTokenFee is passed to the endpoint.\n * @dev Any excess sent, is passed back to the specified _refundAddress in the _lzSend().\n */\n function _payLzToken(uint256 _lzTokenFee) internal virtual {\n // @dev Cannot cache the token because it is not immutable in the endpoint.\n address lzToken = endpoint.lzToken();\n if (lzToken == address(0)) revert LzTokenUnavailable();\n\n // Pay LZ token fee by sending tokens to the endpoint.\n IERC20(lzToken).safeTransferFrom(msg.sender, address(endpoint), _lzTokenFee);\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { MessagingReceipt, MessagingFee } from \"@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol\";\n\n/**\n * @dev Struct representing token parameters for the OFT send() operation.\n */\nstruct SendParam {\n uint32 dstEid; // Destination endpoint ID.\n bytes32 to; // Recipient address.\n uint256 amountLD; // Amount to send in local decimals.\n uint256 minAmountLD; // Minimum amount to send in local decimals.\n bytes extraOptions; // Additional options supplied by the caller to be used in the LayerZero message.\n bytes composeMsg; // The composed message for the send() operation.\n bytes oftCmd; // The OFT command to be executed, unused in default OFT implementations.\n}\n\n/**\n * @dev Struct representing OFT limit information.\n * @dev These amounts can change dynamically and are up the specific oft implementation.\n */\nstruct OFTLimit {\n uint256 minAmountLD; // Minimum amount in local decimals that can be sent to the recipient.\n uint256 maxAmountLD; // Maximum amount in local decimals that can be sent to the recipient.\n}\n\n/**\n * @dev Struct representing OFT receipt information.\n */\nstruct OFTReceipt {\n uint256 amountSentLD; // Amount of tokens ACTUALLY debited from the sender in local decimals.\n // @dev In non-default implementations, the amountReceivedLD COULD differ from this value.\n uint256 amountReceivedLD; // Amount of tokens to be received on the remote side.\n}\n\n/**\n * @dev Struct representing OFT fee details.\n * @dev Future proof mechanism to provide a standardized way to communicate fees to things like a UI.\n */\nstruct OFTFeeDetail {\n int256 feeAmountLD; // Amount of the fee in local decimals.\n string description; // Description of the fee.\n}\n\n/**\n * @title IOFT\n * @dev Interface for the OftChain (OFT) token.\n * @dev Does not inherit ERC20 to accommodate usage by OFTAdapter as well.\n * @dev This specific interface ID is '0x02e49c2c'.\n */\ninterface IOFT {\n // Custom error messages\n error InvalidLocalDecimals();\n error SlippageExceeded(uint256 amountLD, uint256 minAmountLD);\n\n // Events\n event OFTSent(\n bytes32 indexed guid, // GUID of the OFT message.\n uint32 dstEid, // Destination Endpoint ID.\n address indexed fromAddress, // Address of the sender on the src chain.\n uint256 amountSentLD, // Amount of tokens sent in local decimals.\n uint256 amountReceivedLD // Amount of tokens received in local decimals.\n );\n event OFTReceived(\n bytes32 indexed guid, // GUID of the OFT message.\n uint32 srcEid, // Source Endpoint ID.\n address indexed toAddress, // Address of the recipient on the dst chain.\n uint256 amountReceivedLD // Amount of tokens received in local decimals.\n );\n\n /**\n * @notice Retrieves interfaceID and the version of the OFT.\n * @return interfaceId The interface ID.\n * @return version The version.\n *\n * @dev interfaceId: This specific interface ID is '0x02e49c2c'.\n * @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.\n * @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.\n * ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)\n */\n function oftVersion() external view returns (bytes4 interfaceId, uint64 version);\n\n /**\n * @notice Retrieves the address of the token associated with the OFT.\n * @return token The address of the ERC20 token implementation.\n */\n function token() external view returns (address);\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the 'token()' to send.\n * @return requiresApproval Needs approval of the underlying token implementation.\n *\n * @dev Allows things like wallet implementers to determine integration requirements,\n * without understanding the underlying token implementation.\n */\n function approvalRequired() external view returns (bool);\n\n /**\n * @notice Retrieves the shared decimals of the OFT.\n * @return sharedDecimals The shared decimals of the OFT.\n */\n function sharedDecimals() external view returns (uint8);\n\n /**\n * @notice Provides the fee breakdown and settings data for an OFT. Unused in the default implementation.\n * @param _sendParam The parameters for the send operation.\n * @return limit The OFT limit information.\n * @return oftFeeDetails The details of OFT fees.\n * @return receipt The OFT receipt information.\n */\n function quoteOFT(\n SendParam calldata _sendParam\n ) external view returns (OFTLimit memory, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory);\n\n /**\n * @notice Provides a quote for the send() operation.\n * @param _sendParam The parameters for the send() operation.\n * @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.\n * @return fee The calculated LayerZero messaging fee from the send() operation.\n *\n * @dev MessagingFee: LayerZero msg fee\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n */\n function quoteSend(SendParam calldata _sendParam, bool _payInLzToken) external view returns (MessagingFee memory);\n\n /**\n * @notice Executes the send() operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The fee information supplied by the caller.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds from fees etc. on the src.\n * @return receipt The LayerZero messaging receipt from the send() operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) external payable returns (MessagingReceipt memory, OFTReceipt memory);\n}\n" + }, + "@openzeppelin/contracts/access/AccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/AccessControl.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\nimport \"../utils/Context.sol\";\nimport \"../utils/Strings.sol\";\nimport \"../utils/introspection/ERC165.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\n * members except through off-chain means by accessing the contract event logs. Some\n * applications may benefit from on-chain enumerability, for those cases see\n * {AccessControlEnumerable}.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```\n * function foo() public {\n * require(hasRole(MY_ROLE, msg.sender));\n * ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it.\n */\nabstract contract AccessControl is Context, IAccessControl, ERC165 {\n struct RoleData {\n mapping(address => bool) members;\n bytes32 adminRole;\n }\n\n mapping(bytes32 => RoleData) private _roles;\n\n bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n /**\n * @dev Modifier that checks that an account has a specific role. Reverts\n * with a standardized message including the required role.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n *\n * _Available since v4.1._\n */\n modifier onlyRole(bytes32 role) {\n _checkRole(role, _msgSender());\n _;\n }\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) public view override returns (bool) {\n return _roles[role].members[account];\n }\n\n /**\n * @dev Revert with a standard message if `account` is missing `role`.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n */\n function _checkRole(bytes32 role, address account) internal view {\n if (!hasRole(role, account)) {\n revert(\n string(\n abi.encodePacked(\n \"AccessControl: account \",\n Strings.toHexString(uint160(account), 20),\n \" is missing role \",\n Strings.toHexString(uint256(role), 32)\n )\n )\n );\n }\n }\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) public view override returns (bytes32) {\n return _roles[role].adminRole;\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _grantRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _revokeRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been revoked `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) public virtual override {\n require(account == _msgSender(), \"AccessControl: can only renounce roles for self\");\n\n _revokeRole(role, account);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event. Note that unlike {grantRole}, this function doesn't perform any\n * checks on the calling account.\n *\n * [WARNING]\n * ====\n * This function should only be called from the constructor when setting\n * up the initial roles for the system.\n *\n * Using this function in any other way is effectively circumventing the admin\n * system imposed by {AccessControl}.\n * ====\n *\n * NOTE: This function is deprecated in favor of {_grantRole}.\n */\n function _setupRole(bytes32 role, address account) internal virtual {\n _grantRole(role, account);\n }\n\n /**\n * @dev Sets `adminRole` as ``role``'s admin role.\n *\n * Emits a {RoleAdminChanged} event.\n */\n function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n bytes32 previousAdminRole = getRoleAdmin(role);\n _roles[role].adminRole = adminRole;\n emit RoleAdminChanged(role, previousAdminRole, adminRole);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * Internal function without access restriction.\n */\n function _grantRole(bytes32 role, address account) internal virtual {\n if (!hasRole(role, account)) {\n _roles[role].members[account] = true;\n emit RoleGranted(role, account, _msgSender());\n }\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * Internal function without access restriction.\n */\n function _revokeRole(bytes32 role, address account) internal virtual {\n if (hasRole(role, account)) {\n _roles[role].members[account] = false;\n emit RoleRevoked(role, account, _msgSender());\n }\n }\n}\n" + }, + "@openzeppelin/contracts/access/AccessControlEnumerable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/AccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControlEnumerable.sol\";\nimport \"./AccessControl.sol\";\nimport \"../utils/structs/EnumerableSet.sol\";\n\n/**\n * @dev Extension of {AccessControl} that allows enumerating the members of each role.\n */\nabstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {\n using EnumerableSet for EnumerableSet.AddressSet;\n\n mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) public view override returns (address) {\n return _roleMembers[role].at(index);\n }\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) public view override returns (uint256) {\n return _roleMembers[role].length();\n }\n\n /**\n * @dev Overload {_grantRole} to track enumerable memberships\n */\n function _grantRole(bytes32 role, address account) internal virtual override {\n super._grantRole(role, account);\n _roleMembers[role].add(account);\n }\n\n /**\n * @dev Overload {_revokeRole} to track enumerable memberships\n */\n function _revokeRole(bytes32 role, address account) internal virtual override {\n super._revokeRole(role, account);\n _roleMembers[role].remove(account);\n }\n}\n" + }, + "@openzeppelin/contracts/access/IAccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n /**\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n *\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n * {RoleAdminChanged} not being emitted signaling this.\n *\n * _Available since v3.1._\n */\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n /**\n * @dev Emitted when `account` is granted `role`.\n *\n * `sender` is the account that originated the contract call, an admin role\n * bearer except when using {AccessControl-_setupRole}.\n */\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Emitted when `account` is revoked `role`.\n *\n * `sender` is the account that originated the contract call:\n * - if using `revokeRole`, it is the admin role bearer\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\n */\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) external view returns (bool);\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been granted `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) external;\n}\n" + }, + "@openzeppelin/contracts/access/IAccessControlEnumerable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\n\n/**\n * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.\n */\ninterface IAccessControlEnumerable is IAccessControl {\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) external view returns (address);\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) external view returns (uint256);\n}\n" + }, + "@openzeppelin/contracts/access/Ownable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n" + }, + "@openzeppelin/contracts/security/Pausable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/ERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/ERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"./extensions/IERC20Metadata.sol\";\nimport \"../../utils/Context.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20, IERC20Metadata {\n mapping(address => uint256) private _balances;\n\n mapping(address => mapping(address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n\n /**\n * @dev Sets the values for {name} and {symbol}.\n *\n * The default value of {decimals} is 18. To select a different value for\n * {decimals} you should overload it.\n *\n * All two of these values are immutable: they can only be set once during\n * construction.\n */\n constructor(string memory name_, string memory symbol_) {\n _name = name_;\n _symbol = symbol_;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless this function is\n * overridden;\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `recipient` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n _approve(_msgSender(), spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * Requirements:\n *\n * - `sender` and `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n * - the caller must have allowance for ``sender``'s tokens of at least\n * `amount`.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public virtual override returns (bool) {\n _transfer(sender, recipient, amount);\n\n uint256 currentAllowance = _allowances[sender][_msgSender()];\n require(currentAllowance >= amount, \"ERC20: transfer amount exceeds allowance\");\n unchecked {\n _approve(sender, _msgSender(), currentAllowance - amount);\n }\n\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n uint256 currentAllowance = _allowances[_msgSender()][spender];\n require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n unchecked {\n _approve(_msgSender(), spender, currentAllowance - subtractedValue);\n }\n\n return true;\n }\n\n /**\n * @dev Moves `amount` of tokens from `sender` to `recipient`.\n *\n * This internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `sender` cannot be the zero address.\n * - `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n */\n function _transfer(\n address sender,\n address recipient,\n uint256 amount\n ) internal virtual {\n require(sender != address(0), \"ERC20: transfer from the zero address\");\n require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(sender, recipient, amount);\n\n uint256 senderBalance = _balances[sender];\n require(senderBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n unchecked {\n _balances[sender] = senderBalance - amount;\n }\n _balances[recipient] += amount;\n\n emit Transfer(sender, recipient, amount);\n\n _afterTokenTransfer(sender, recipient, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply += amount;\n _balances[account] += amount;\n emit Transfer(address(0), account, amount);\n\n _afterTokenTransfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n uint256 accountBalance = _balances[account];\n require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n unchecked {\n _balances[account] = accountBalance - amount;\n }\n _totalSupply -= amount;\n\n emit Transfer(account, address(0), amount);\n\n _afterTokenTransfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n\n /**\n * @dev Hook that is called after any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * has been transferred to `to`.\n * - when `from` is zero, `amount` tokens have been minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _afterTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/ERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC165.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n *\n * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.\n */\nabstract contract ERC165 is IERC165 {\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IERC165).interfaceId;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/IERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeMath.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)\n\npragma solidity ^0.8.0;\n\n// CAUTION\n// This version of SafeMath should only be used with Solidity 0.8 or later,\n// because it relies on the compiler's built in overflow checks.\n\n/**\n * @dev Wrappers over Solidity's arithmetic operations.\n *\n * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler\n * now has built in overflow checking.\n */\nlibrary SafeMath {\n /**\n * @dev Returns the addition of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n uint256 c = a + b;\n if (c < a) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the substraction of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b > a) return (false, 0);\n return (true, a - b);\n }\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n // benefit is lost if 'b' is also tested.\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n if (a == 0) return (true, 0);\n uint256 c = a * b;\n if (c / a != b) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the division of two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a / b);\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a % b);\n }\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `+` operator.\n *\n * Requirements:\n *\n * - Addition cannot overflow.\n */\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\n return a + b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting on\n * overflow (when the result is negative).\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\n return a - b;\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `*` operator.\n *\n * Requirements:\n *\n * - Multiplication cannot overflow.\n */\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\n return a * b;\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator.\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\n return a / b;\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting when dividing by zero.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\n return a % b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\n * overflow (when the result is negative).\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {trySub}.\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b <= a, errorMessage);\n return a - b;\n }\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting with custom message on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator. Note: this function uses a\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\n * uses an invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a / b;\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting with custom message when dividing by zero.\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {tryMod}.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a % b;\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Strings.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n bytes16 private constant _HEX_SYMBOLS = \"0123456789abcdef\";\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n // Inspired by OraclizeAPI's implementation - MIT licence\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\n\n if (value == 0) {\n return \"0\";\n }\n uint256 temp = value;\n uint256 digits;\n while (temp != 0) {\n digits++;\n temp /= 10;\n }\n bytes memory buffer = new bytes(digits);\n while (value != 0) {\n digits -= 1;\n buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\n value /= 10;\n }\n return string(buffer);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n if (value == 0) {\n return \"0x00\";\n }\n uint256 temp = value;\n uint256 length = 0;\n while (temp != 0) {\n length++;\n temp >>= 8;\n }\n return toHexString(value, length);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _HEX_SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/structs/EnumerableSet.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 => uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n" + }, + "contracts/automation/AbstractCCIPBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IRouterClient } from \"@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol\";\nimport { Client } from \"@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol\";\n\nabstract contract AbstractCCIPBridgeHelperModule is AbstractSafeModule {\n /**\n * @notice Bridges a token from the source chain to the destination chain using CCIP\n * @param ccipRouter The CCIP router contract\n * @param destinationChainSelector The selector for the destination chain\n * @param token The token to bridge\n * @param amount The amount of token to bridge\n */\n function _bridgeTokenWithCCIP(\n IRouterClient ccipRouter,\n uint64 destinationChainSelector,\n IERC20 token,\n uint256 amount\n ) internal {\n bool success;\n\n // Approve CCIP Router to move the token\n success = safeContract.execTransactionFromModule(\n address(token),\n 0, // Value\n abi.encodeWithSelector(token.approve.selector, ccipRouter, amount),\n 0 // Call\n );\n require(success, \"Failed to approve token\");\n\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(token),\n amount: amount\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory ccipMessage = Client.EVM2AnyMessage({\n receiver: abi.encode(address(safeContract)), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n // Get CCIP fee\n uint256 ccipFee = ccipRouter.getFee(\n destinationChainSelector,\n ccipMessage\n );\n\n // Send CCIP message\n success = safeContract.execTransactionFromModule(\n address(ccipRouter),\n ccipFee, // Value\n abi.encodeWithSelector(\n ccipRouter.ccipSend.selector,\n destinationChainSelector,\n ccipMessage\n ),\n 0 // Call\n );\n require(success, \"Failed to send CCIP message\");\n }\n}\n" + }, + "contracts/automation/AbstractLZBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\n\nimport { IOFT, SendParam } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\nimport { MessagingFee } from \"@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol\";\nimport { OptionsBuilder } from \"@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nabstract contract AbstractLZBridgeHelperModule is AbstractSafeModule {\n using OptionsBuilder for bytes;\n\n /**\n * @dev Bridges token using LayerZero.\n * @param lzEndpointId LayerZero endpoint id.\n * @param token Token to bridge.\n * @param lzAdapter LZ Adapter to use.\n * @param amount Amount of token to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n * @param isNativeToken Whether the token is native token.\n */\n function _bridgeTokenWithLz(\n uint32 lzEndpointId,\n IERC20 token,\n IOFT lzAdapter,\n uint256 amount,\n uint256 slippageBps,\n bool isNativeToken\n ) internal {\n bool success;\n\n if (!isNativeToken) {\n // Approve LZ Adapter to move the token\n success = safeContract.execTransactionFromModule(\n address(token),\n 0, // Value\n abi.encodeWithSelector(\n token.approve.selector,\n address(lzAdapter),\n amount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve token\");\n }\n\n // Calculate minimum amount to receive\n uint256 minAmount = (amount * (10000 - slippageBps)) / 10000;\n\n // Hardcoded gaslimit of 400k\n bytes memory options = OptionsBuilder\n .newOptions()\n .addExecutorLzReceiveOption(400000, 0);\n\n // Build send param\n SendParam memory sendParam = SendParam({\n dstEid: lzEndpointId,\n to: bytes32(uint256(uint160(address(safeContract)))),\n amountLD: amount,\n minAmountLD: minAmount,\n extraOptions: options,\n composeMsg: bytes(\"\"),\n oftCmd: bytes(\"\")\n });\n\n // Compute fees\n MessagingFee memory msgFee = lzAdapter.quoteSend(sendParam, false);\n\n uint256 value = isNativeToken\n ? amount + msgFee.nativeFee\n : msgFee.nativeFee;\n\n // Execute transaction\n success = safeContract.execTransactionFromModule(\n address(lzAdapter),\n value,\n abi.encodeWithSelector(\n lzAdapter.send.selector,\n sendParam,\n msgFee,\n address(safeContract)\n ),\n 0\n );\n require(success, \"Failed to bridge token\");\n }\n}\n" + }, + "contracts/automation/AbstractSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { ISafe } from \"../interfaces/ISafe.sol\";\n\nabstract contract AbstractSafeModule is AccessControlEnumerable {\n ISafe public immutable safeContract;\n\n bytes32 public constant OPERATOR_ROLE = keccak256(\"OPERATOR_ROLE\");\n\n modifier onlySafe() {\n require(\n msg.sender == address(safeContract),\n \"Caller is not the safe contract\"\n );\n _;\n }\n\n modifier onlyOperator() {\n require(\n hasRole(OPERATOR_ROLE, msg.sender),\n \"Caller is not an operator\"\n );\n _;\n }\n\n constructor(address _safeContract) {\n safeContract = ISafe(_safeContract);\n _grantRole(DEFAULT_ADMIN_ROLE, address(safeContract));\n _grantRole(OPERATOR_ROLE, address(safeContract));\n }\n\n /**\n * @dev Helps recovering any tokens accidentally sent to this module.\n * @param token Token to transfer. 0x0 to transfer Native token.\n * @param amount Amount to transfer. 0 to transfer all balance.\n */\n function transferTokens(address token, uint256 amount) external onlySafe {\n if (address(token) == address(0)) {\n // Move ETH\n amount = amount > 0 ? amount : address(this).balance;\n payable(address(safeContract)).transfer(amount);\n return;\n }\n\n // Move all balance if amount set to 0\n amount = amount > 0 ? amount : IERC20(token).balanceOf(address(this));\n\n // Transfer to Safe contract\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(token).transfer(address(safeContract), amount);\n }\n\n receive() external payable {\n // Accept ETH to pay for bridge fees\n }\n}\n" + }, + "contracts/automation/BaseBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// solhint-disable-next-line max-line-length\nimport { AbstractCCIPBridgeHelperModule, AbstractSafeModule, IRouterClient } from \"./AbstractCCIPBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { BridgedWOETHStrategy } from \"../strategies/BridgedWOETHStrategy.sol\";\n\ncontract BaseBridgeHelperModule is\n AccessControlEnumerable,\n AbstractCCIPBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0x98a0CbeF61bD2D21435f433bE4CD42B56B38CC93);\n IWETH9 public constant weth =\n IWETH9(0x4200000000000000000000000000000000000006);\n IERC20 public constant oethb =\n IERC20(0xDBFeFD2e8460a6Ee4955A68582F85708BAEA60A3);\n IERC4626 public constant bridgedWOETH =\n IERC4626(0xD8724322f44E5c58D7A815F542036fb17DbbF839);\n\n BridgedWOETHStrategy public constant bridgedWOETHStrategy =\n BridgedWOETHStrategy(0x80c864704DD06C3693ed5179190786EE38ACf835);\n\n IRouterClient public constant CCIP_ROUTER =\n IRouterClient(0x881e3A65B4d4a04dD529061dd0071cf975F58bCD);\n\n uint64 public constant CCIP_ETHEREUM_CHAIN_SELECTOR = 5009297550715157269;\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Ethereum.\n * @param woethAmount Amount of wOETH to bridge.\n */\n function bridgeWOETHToEthereum(uint256 woethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_ETHEREUM_CHAIN_SELECTOR,\n IERC20(address(bridgedWOETH)),\n woethAmount\n );\n }\n\n /**\n * @dev Bridges WETH to Ethereum.\n * @param wethAmount Amount of WETH to bridge.\n */\n function bridgeWETHToEthereum(uint256 wethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_ETHEREUM_CHAIN_SELECTOR,\n IERC20(address(weth)),\n wethAmount\n );\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem the wOETH for WETH using the Vault.\n * @return Amount of WETH received.\n */\n function depositWOETH(uint256 woethAmount, bool redeemWithVault)\n external\n onlyOperator\n returns (uint256)\n {\n return _depositWOETH(woethAmount, redeemWithVault);\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy and bridges it to Ethereum.\n * @param woethAmount Amount of wOETH to deposit.\n * @return Amount of WETH received.\n */\n function depositWOETHAndBridgeWETH(uint256 woethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n uint256 wethAmount = _depositWOETH(woethAmount, true);\n bridgeWETHToEthereum(wethAmount);\n return wethAmount;\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem the wOETH for WETH using the Vault.\n * @return Amount of WETH received.\n */\n function _depositWOETH(uint256 woethAmount, bool redeemWithVault)\n internal\n returns (uint256)\n {\n // Update oracle price\n bridgedWOETHStrategy.updateWOETHOraclePrice();\n\n // Rebase to account for any yields from price update\n vault.rebase();\n\n uint256 oethbAmount = oethb.balanceOf(address(safeContract));\n\n // Approve bridgedWOETH strategy to move wOETH\n bool success = safeContract.execTransactionFromModule(\n address(bridgedWOETH),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETH.approve.selector,\n address(bridgedWOETHStrategy),\n woethAmount\n ),\n 0 // Call\n );\n\n // Deposit to bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.depositBridgedWOETH.selector,\n woethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to deposit bridged WOETH\");\n\n oethbAmount = oethb.balanceOf(address(safeContract)) - oethbAmount;\n\n // Rebase to account for any yields from price update\n // and backing asset change from deposit\n vault.rebase();\n\n if (!redeemWithVault) {\n return oethbAmount;\n }\n\n // Redeem for WETH using Vault\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethbAmount,\n oethbAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETHb\");\n\n return oethbAmount;\n }\n\n /**\n * @dev Deposits WETH into the Vault and redeems wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to deposit.\n * @return Amount of wOETH received.\n */\n function depositWETHAndRedeemWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _withdrawWOETH(wethAmount);\n }\n\n function depositWETHAndBridgeWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n uint256 woethAmount = _withdrawWOETH(wethAmount);\n bridgeWOETHToEthereum(woethAmount);\n return woethAmount;\n }\n\n /**\n * @dev Withdraws wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to use to withdraw.\n * @return Amount of wOETH received.\n */\n function _withdrawWOETH(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETHb with WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETHb\");\n\n // Approve bridgedWOETH strategy to move OETHb\n success = safeContract.execTransactionFromModule(\n address(oethb),\n 0, // Value\n abi.encodeWithSelector(\n oethb.approve.selector,\n address(bridgedWOETHStrategy),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETHb\");\n\n uint256 woethAmount = bridgedWOETH.balanceOf(address(safeContract));\n\n // Withdraw from bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.withdrawBridgedWOETH.selector,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to withdraw bridged WOETH\");\n\n woethAmount =\n bridgedWOETH.balanceOf(address(safeContract)) -\n woethAmount;\n\n return woethAmount;\n }\n}\n" + }, + "contracts/automation/EthereumBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractLZBridgeHelperModule, AbstractSafeModule } from \"./AbstractLZBridgeHelperModule.sol\";\nimport { AbstractCCIPBridgeHelperModule, IRouterClient } from \"./AbstractCCIPBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IOFT } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract EthereumBridgeHelperModule is\n AccessControlEnumerable,\n AbstractLZBridgeHelperModule,\n AbstractCCIPBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0x39254033945AA2E4809Cc2977E7087BEE48bd7Ab);\n IWETH9 public constant weth =\n IWETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);\n IERC20 public constant oeth =\n IERC20(0x856c4Efb76C1D1AE02e20CEB03A2A6a08b0b8dC3);\n IERC4626 public constant woeth =\n IERC4626(0xDcEe70654261AF21C44c093C300eD3Bb97b78192);\n\n uint32 public constant LZ_PLUME_ENDPOINT_ID = 30370;\n IOFT public constant LZ_WOETH_OMNICHAIN_ADAPTER =\n IOFT(0x7d1bEa5807e6af125826d56ff477745BB89972b8);\n IOFT public constant LZ_ETH_OMNICHAIN_ADAPTER =\n IOFT(0x77b2043768d28E9C9aB44E1aBfC95944bcE57931);\n\n IRouterClient public constant CCIP_ROUTER =\n IRouterClient(0x80226fc0Ee2b096224EeAc085Bb9a8cba1146f7D);\n\n uint64 public constant CCIP_BASE_CHAIN_SELECTOR = 15971525489660198786;\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Plume.\n * @param woethAmount Amount of wOETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWOETHToPlume(uint256 woethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_PLUME_ENDPOINT_ID,\n woeth,\n LZ_WOETH_OMNICHAIN_ADAPTER,\n woethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Bridges wOETH to Base using CCIP.\n * @param woethAmount Amount of wOETH to bridge.\n */\n function bridgeWOETHToBase(uint256 woethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_BASE_CHAIN_SELECTOR,\n woeth,\n woethAmount\n );\n }\n\n /**\n * @dev Bridges wETH to Plume.\n * @param wethAmount Amount of wETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWETHToPlume(uint256 wethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n // Unwrap into ETH\n safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(weth.withdraw.selector, wethAmount),\n 0 // Call\n );\n\n _bridgeTokenWithLz(\n LZ_PLUME_ENDPOINT_ID,\n IERC20(address(weth)),\n LZ_ETH_OMNICHAIN_ADAPTER,\n wethAmount,\n slippageBps,\n true\n );\n }\n\n /**\n * @dev Bridges wETH to Base using CCIP.\n * @param wethAmount Amount of wETH to bridge.\n */\n function bridgeWETHToBase(uint256 wethAmount) public payable onlyOperator {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_BASE_CHAIN_SELECTOR,\n IERC20(address(weth)),\n wethAmount\n );\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH.\n * @param wethAmount Amount of WETH to mint.\n * @param useNativeToken Whether to use native token to mint.\n * @return Amount of wOETH minted.\n */\n function mintAndWrap(uint256 wethAmount, bool useNativeToken)\n external\n onlyOperator\n returns (uint256)\n {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n return _mintAndWrap(wethAmount);\n }\n\n function wrapETH(uint256 ethAmount) public payable onlyOperator {\n // Deposit ETH into WETH\n safeContract.execTransactionFromModule(\n address(weth),\n ethAmount, // Value\n abi.encodeWithSelector(weth.deposit.selector),\n 0 // Call\n );\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH.\n * @param wethAmount Amount of WETH to mint.\n * @return Amount of wOETH minted.\n */\n function _mintAndWrap(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETH\");\n\n // Approve wOETH to move OETH\n success = safeContract.execTransactionFromModule(\n address(oeth),\n 0, // Value\n abi.encodeWithSelector(\n oeth.approve.selector,\n address(woeth),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETH\");\n\n uint256 woethAmount = woeth.balanceOf(address(safeContract));\n\n // Wrap OETH into wOETH\n success = safeContract.execTransactionFromModule(\n address(woeth),\n 0, // Value\n abi.encodeWithSelector(\n woeth.deposit.selector,\n wethAmount,\n address(safeContract)\n ),\n 0 // Call\n );\n require(success, \"Failed to wrap OETH\");\n\n // Compute amount of wOETH minted\n return woeth.balanceOf(address(safeContract)) - woethAmount;\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH, then bridges it to Plume.\n * @param wethAmount Amount of WETH to mint.\n * @param slippageBps Bridge slippage in 10^4 basis points.\n * @param useNativeToken Whether to use native token to mint.\n */\n function mintWrapAndBridgeToPlume(\n uint256 wethAmount,\n uint256 slippageBps,\n bool useNativeToken\n ) external payable onlyOperator {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n uint256 woethAmount = _mintAndWrap(wethAmount);\n bridgeWOETHToPlume(woethAmount, slippageBps);\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH, then bridges it to Base using CCIP.\n * @param wethAmount Amount of WETH to mint.\n * @param useNativeToken Whether to use native token to mint.\n */\n function mintWrapAndBridgeToBase(uint256 wethAmount, bool useNativeToken)\n external\n payable\n onlyOperator\n {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n uint256 woethAmount = _mintAndWrap(wethAmount);\n bridgeWOETHToBase(woethAmount);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH.\n * @param woethAmount Amount of wOETH to unwrap.\n * @return Amount of WETH received.\n */\n function unwrapAndRedeem(uint256 woethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _unwrapAndRedeem(woethAmount);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH.\n * @param woethAmount Amount of wOETH to unwrap.\n * @return Amount of WETH received.\n */\n function _unwrapAndRedeem(uint256 woethAmount) internal returns (uint256) {\n uint256 oethAmount = oeth.balanceOf(address(safeContract));\n\n // Unwrap wOETH\n bool success = safeContract.execTransactionFromModule(\n address(woeth),\n 0, // Value\n abi.encodeWithSelector(\n woeth.redeem.selector,\n woethAmount,\n address(safeContract),\n address(safeContract)\n ),\n 0 // Call\n );\n require(success, \"Failed to unwrap wOETH\");\n\n oethAmount = oeth.balanceOf(address(safeContract)) - oethAmount;\n\n // Redeem OETH using Vault to get WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethAmount,\n oethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETH\");\n\n return oethAmount;\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH, then bridges it to Plume.\n * @param woethAmount Amount of wOETH to unwrap.\n * @param slippageBps Bridge slippage in 10^4 basis points.\n */\n function unwrapRedeemAndBridgeToPlume(\n uint256 woethAmount,\n uint256 slippageBps\n ) external payable onlyOperator {\n uint256 wethAmount = _unwrapAndRedeem(woethAmount);\n // Unwrap into ETH\n safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(weth.withdraw.selector, wethAmount),\n 0 // Call\n );\n\n bridgeWETHToPlume(wethAmount, slippageBps);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH, then bridges it to Base using CCIP.\n * @param woethAmount Amount of wOETH to unwrap.\n */\n function unwrapRedeemAndBridgeToBase(uint256 woethAmount)\n external\n payable\n onlyOperator\n {\n uint256 wethAmount = _unwrapAndRedeem(woethAmount);\n bridgeWETHToBase(wethAmount);\n }\n}\n" + }, + "contracts/automation/PlumeBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { AbstractLZBridgeHelperModule } from \"./AbstractLZBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IOFT } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { BridgedWOETHStrategy } from \"../strategies/BridgedWOETHStrategy.sol\";\n\ncontract PlumeBridgeHelperModule is\n AccessControlEnumerable,\n AbstractLZBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0xc8c8F8bEA5631A8AF26440AF32a55002138cB76a);\n IWETH9 public constant weth =\n IWETH9(0xca59cA09E5602fAe8B629DeE83FfA819741f14be);\n IERC20 public constant oethp =\n IERC20(0xFCbe50DbE43bF7E5C88C6F6Fb9ef432D4165406E);\n IERC4626 public constant bridgedWOETH =\n IERC4626(0xD8724322f44E5c58D7A815F542036fb17DbbF839);\n\n uint32 public constant LZ_ETHEREUM_ENDPOINT_ID = 30101;\n IOFT public constant LZ_WOETH_OMNICHAIN_ADAPTER =\n IOFT(0x592CB6A596E7919930bF49a27AdAeCA7C055e4DB);\n IOFT public constant LZ_ETH_OMNICHAIN_ADAPTER =\n IOFT(0x4683CE822272CD66CEa73F5F1f9f5cBcaEF4F066);\n\n BridgedWOETHStrategy public constant bridgedWOETHStrategy =\n BridgedWOETHStrategy(0x1E3EdD5e019207D6355Ea77F724b1F1BF639B569);\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Ethereum.\n * @param woethAmount Amount of wOETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWOETHToEthereum(uint256 woethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_ETHEREUM_ENDPOINT_ID,\n IERC20(address(bridgedWOETH)),\n LZ_WOETH_OMNICHAIN_ADAPTER,\n woethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Bridges wETH to Ethereum.\n * @param wethAmount Amount of wETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWETHToEthereum(uint256 wethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_ETHEREUM_ENDPOINT_ID,\n IERC20(address(weth)),\n LZ_ETH_OMNICHAIN_ADAPTER,\n wethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem with Vault.\n * @return Amount of OETHp received.\n */\n function depositWOETH(uint256 woethAmount, bool redeemWithVault)\n external\n onlyOperator\n returns (uint256)\n {\n return _depositWOETH(woethAmount, redeemWithVault);\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy and bridges it to Ethereum.\n * @param woethAmount Amount of wOETH to deposit.\n * @param slippageBps Slippage in 10^4 basis points.\n * @return Amount of WETH received.\n */\n function depositWOETHAndBridgeWETH(uint256 woethAmount, uint256 slippageBps)\n external\n payable\n onlyOperator\n returns (uint256)\n {\n uint256 wethAmount = _depositWOETH(woethAmount, true);\n bridgeWETHToEthereum(wethAmount, slippageBps);\n return wethAmount;\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem with Vault.\n * @return Amount of OETHp received.\n */\n function _depositWOETH(uint256 woethAmount, bool redeemWithVault)\n internal\n returns (uint256)\n {\n // Update oracle price\n bridgedWOETHStrategy.updateWOETHOraclePrice();\n\n // Rebase to account for any yields from price update\n vault.rebase();\n\n uint256 oethpAmount = oethp.balanceOf(address(safeContract));\n\n // Approve bridgedWOETH strategy to move wOETH\n bool success = safeContract.execTransactionFromModule(\n address(bridgedWOETH),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETH.approve.selector,\n address(bridgedWOETHStrategy),\n woethAmount\n ),\n 0 // Call\n );\n\n // Deposit to bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.depositBridgedWOETH.selector,\n woethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to deposit bridged WOETH\");\n\n oethpAmount = oethp.balanceOf(address(safeContract)) - oethpAmount;\n\n // Rebase to account for any yields from price update\n // and backing asset change from deposit\n vault.rebase();\n\n if (!redeemWithVault) {\n return oethpAmount;\n }\n\n // Redeem for WETH using Vault\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethpAmount,\n oethpAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETHp\");\n\n return oethpAmount;\n }\n\n /**\n * @dev Deposits wETH into the vault.\n * @param wethAmount Amount of wETH to deposit.\n * @return Amount of OETHp received.\n */\n function depositWETHAndRedeemWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _withdrawWOETH(wethAmount);\n }\n\n /**\n * @dev Deposits wETH into the vault and bridges it to Ethereum.\n * @param wethAmount Amount of wETH to deposit.\n * @param slippageBps Slippage in 10^4 basis points.\n * @return Amount of WOETH received.\n */\n function depositWETHAndBridgeWOETH(uint256 wethAmount, uint256 slippageBps)\n external\n payable\n onlyOperator\n returns (uint256)\n {\n uint256 woethAmount = _withdrawWOETH(wethAmount);\n bridgeWOETHToEthereum(woethAmount, slippageBps);\n return woethAmount;\n }\n\n /**\n * @dev Withdraws wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to use to withdraw.\n * @return Amount of wOETH received.\n */\n function _withdrawWOETH(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETHp with WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETHp\");\n\n // Approve bridgedWOETH strategy to move OETHp\n success = safeContract.execTransactionFromModule(\n address(oethp),\n 0, // Value\n abi.encodeWithSelector(\n oethp.approve.selector,\n address(bridgedWOETHStrategy),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETHp\");\n\n uint256 woethAmount = bridgedWOETH.balanceOf(address(safeContract));\n\n // Withdraw from bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.withdrawBridgedWOETH.selector,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to withdraw bridged WOETH\");\n\n woethAmount =\n bridgedWOETH.balanceOf(address(safeContract)) -\n woethAmount;\n\n return woethAmount;\n }\n}\n" + }, + "contracts/beacon/BeaconConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary BeaconConsolidation {\n /// @notice The address the validator consolidation requests are sent\n /// See https://eips.ethereum.org/EIPS/eip-7251\n address internal constant CONSOLIDATION_REQUEST_ADDRESS =\n 0x0000BBdDc7CE488642fb579F8B00f3a590007251;\n\n function request(bytes calldata source, bytes calldata target)\n internal\n returns (uint256 fee_)\n {\n require(source.length == 48, \"Invalid source byte length\");\n require(target.length == 48, \"Invalid target byte length\");\n\n fee_ = fee();\n\n // Call the Consolidation Request contract with the public keys of the source and target\n // validators packed together.\n // This does not have a function signature, so we use a call\n (bool success, ) = CONSOLIDATION_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(source, target)\n );\n\n require(success, \"Consolidation request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the consolidation request contract\n (bool success, bytes memory result) = CONSOLIDATION_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/beacon/BeaconOracle.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"./BeaconProofsLib.sol\";\nimport { BeaconRoots } from \"./BeaconRoots.sol\";\n\n/// @title Beacon Chain Oracle\n/// @notice An Oracle for mapping execution layer block numbers to beacon chain slots.\n/// @author Origin Protocol Inc\ncontract BeaconOracle {\n /// @notice Maps a block number to slot\n mapping(uint64 => uint64) internal _blockToSlot;\n /// @notice Maps a slot to a number\n mapping(uint64 => uint64) internal _slotToBlock;\n /// @notice Maps a slot to a beacon block root\n mapping(uint64 => bytes32) internal _slotToRoot;\n\n event BlockToSlot(\n bytes32 indexed blockRoot,\n uint64 indexed blockNumber,\n uint64 indexed slot\n );\n\n /// @notice Uses merkle a proof against the beacon block root to link\n /// an execution layer block number to a beacon chain slot.\n /// @param nextBlockTimestamp The timestamp of the block after the one being proven.\n /// @param blockNumber The execution layer block number.\n /// @param slot The beacon chain slot.\n /// @param slotProof The merkle proof witnesses for the slot against the beacon block root.\n /// @param blockProof The merkle proof witnesses for the block number against the beacon block root.\n function verifySlot(\n uint64 nextBlockTimestamp,\n uint64 blockNumber,\n uint64 slot,\n bytes calldata slotProof,\n bytes calldata blockProof\n ) external returns (bytes32 blockRoot) {\n require(_blockToSlot[blockNumber] == 0, \"Block already mapped\");\n\n // Get the parent beacon block root for the given timestamp.\n // This is the beacon block root of the previous slot.\n blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the slot to the beacon block root\n BeaconProofsLib.verifySlot(blockRoot, slot, slotProof);\n\n // Verify the block number to the beacon block root\n BeaconProofsLib.verifyBlockNumber(blockRoot, blockNumber, blockProof);\n\n // Store mappings\n _blockToSlot[blockNumber] = slot;\n _slotToBlock[slot] = blockNumber;\n _slotToRoot[slot] = blockRoot;\n\n emit BlockToSlot(blockRoot, blockNumber, slot);\n }\n\n /// @notice Returns the beacon chain slot for a given execution layer block number.\n function blockToSlot(uint64 blockNumber)\n external\n view\n returns (uint64 slot)\n {\n slot = _blockToSlot[blockNumber];\n\n require(slot != 0, \"Block not mapped\");\n }\n\n /// @notice Returns the execution layer block number for a given beacon chain slot.\n function slotToBlock(uint64 slot)\n external\n view\n returns (uint64 blockNumber)\n {\n blockNumber = _slotToBlock[slot];\n\n require(blockNumber != 0, \"Slot not mapped\");\n }\n\n /// @notice Returns the beacon block root for a given beacon chain slot.\n function slotToRoot(uint64 slot) external view returns (bytes32 blockRoot) {\n blockRoot = _slotToRoot[slot];\n\n require(blockRoot != 0, \"Slot not mapped\");\n }\n\n /// @notice Returns true if an execution layer block number has been mapped to a beacon chain slot.\n function isBlockMapped(uint64 blockNumber) external view returns (bool) {\n return _blockToSlot[blockNumber] != 0;\n }\n\n /// @notice Returns true if a beacon chain slot has been mapped to an execution layer block number.\n function isSlotMapped(uint64 slot) external view returns (bool) {\n return _slotToBlock[slot] != 0;\n }\n}\n" + }, + "contracts/beacon/BeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"./BeaconProofsLib.sol\";\n\ncontract BeaconProofs {\n /// @notice Verifies the validator public key against the beacon block root\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash The beacon chain hash of the validator public key\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex\n ) external view {\n BeaconProofsLib.verifyValidatorPubkey(\n beaconBlockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex\n );\n }\n\n /// @notice Verifies the balances container against the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerLeaf The leaf node containing the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view {\n BeaconProofsLib.verifyBalancesContainer(\n beaconBlockRoot,\n balancesContainerLeaf,\n balancesContainerProof\n );\n }\n\n /// @notice Verifies the validator balance against the root of the Balances container\n /// or the beacon block root\n /// @param root The root of the Balances container or the beacon block root\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances\n /// @param balanceProof The merkle proof for the validator balance against the root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @param level The level of the balance proof, either Container or BeaconBlock\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 root,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex,\n BeaconProofsLib.BalanceProofLevel level\n ) external view returns (uint256 validatorBalanceGwei) {\n validatorBalanceGwei = BeaconProofsLib.verifyValidatorBalance(\n root,\n validatorBalanceLeaf,\n balanceProof,\n validatorIndex,\n level\n );\n }\n\n /// @notice Verifies the slot of the first pending deposit against the beacon block root\n /// BeaconBlock.state.PendingDeposits[0].slot\n /// @param beaconBlockRoot The root of the beacon block\n /// @param slot The beacon chain slot to verify\n /// @param firstPendingDepositSlotProof The merkle proof for the first pending deposit's slot\n /// against the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view {\n BeaconProofsLib.verifyFirstPendingDepositSlot(\n beaconBlockRoot,\n slot,\n firstPendingDepositSlotProof\n );\n }\n\n /// @notice Verifies the block number to the the beacon block root\n /// BeaconBlock.body.executionPayload.blockNumber\n /// @param beaconBlockRoot The root of the beacon block\n /// @param blockNumber The execution layer block number to verify\n /// @param blockNumberProof The merkle proof for the block number against the beacon block\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyBlockNumber(\n bytes32 beaconBlockRoot,\n uint256 blockNumber,\n bytes calldata blockNumberProof\n ) external view {\n BeaconProofsLib.verifyBlockNumber(\n beaconBlockRoot,\n blockNumber,\n blockNumberProof\n );\n }\n\n /// @notice Verifies the slot number against the beacon block root.\n /// BeaconBlock.slot\n /// @param beaconBlockRoot The root of the beacon block\n /// @param slot The beacon chain slot to verify\n /// @param slotProof The merkle proof for the slot against the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifySlot(\n bytes32 beaconBlockRoot,\n uint256 slot,\n bytes calldata slotProof\n ) external view {\n BeaconProofsLib.verifySlot(beaconBlockRoot, slot, slotProof);\n }\n}\n" + }, + "contracts/beacon/BeaconProofsLib.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Merkle } from \"./Merkle.sol\";\nimport { Endian } from \"./Endian.sol\";\n\nlibrary BeaconProofsLib {\n // Known generalized indices in the beacon block\n // BeaconBlock.slot\n uint256 internal constant SLOT_GENERALIZED_INDEX = 8;\n // BeaconBlock.state.PendingDeposits[0].slot\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX =\n 1584842932228;\n // BeaconBlock.body.executionPayload.blockNumber\n uint256 internal constant BLOCK_NUMBER_GENERALIZED_INDEX = 6438;\n // BeaconBlock.state.validators\n uint256 internal constant VALIDATORS_CONTAINER_GENERALIZED_INDEX = 715;\n // BeaconBlock.state.balances\n uint256 internal constant BALANCES_CONTAINER_GENERALIZED_INDEX = 716;\n\n // Beacon Container Tree Heights\n uint256 internal constant BALANCES_HEIGHT = 39;\n uint256 internal constant VALIDATORS_HEIGHT = 41;\n uint256 internal constant VALIDATOR_HEIGHT = 3;\n\n /// @notice Fields in the Validator container for phase 0\n /// See https://ethereum.github.io/consensus-specs/specs/phase0/beacon-chain/#validator\n uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;\n\n enum BalanceProofLevel {\n Container,\n BeaconBlock\n }\n\n /// @notice Verifies the validator public key against the beacon block root\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash The beacon chain hash of the validator public key\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex\n ) internal view {\n // BeaconBlock.state.validators[validatorIndex].pubkey\n uint256 generalizedIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_HEIGHT,\n validatorIndex\n );\n generalizedIndex = concatGenIndices(\n generalizedIndex,\n VALIDATOR_HEIGHT,\n VALIDATOR_PUBKEY_INDEX\n );\n\n require(\n Merkle.verifyInclusionSha256({\n proof: validatorPubKeyProof,\n root: beaconBlockRoot,\n leaf: pubKeyHash,\n index: generalizedIndex\n }),\n \"Invalid validator pubkey proof\"\n );\n }\n\n /// @notice Verifies the balances container against the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerLeaf The leaf node containing the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) internal view {\n // BeaconBlock.state.balances\n require(\n Merkle.verifyInclusionSha256({\n proof: balancesContainerProof,\n root: beaconBlockRoot,\n leaf: balancesContainerLeaf,\n index: BALANCES_CONTAINER_GENERALIZED_INDEX\n }),\n \"Invalid balance container proof\"\n );\n }\n\n /// @notice Verifies the validator balance against the root of the Balances container\n /// or the beacon block root\n /// @param root The root of the Balances container or the beacon block root\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances\n /// @param balanceProof The merkle proof for the validator balance against the root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @param level The level of the balance proof, either Container or BeaconBlock\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 root,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex,\n BalanceProofLevel level\n ) internal view returns (uint256 validatorBalanceGwei) {\n // Four balances are stored in each leaf so the validator index is divided by 4\n uint64 balanceIndex = validatorIndex / 4;\n\n uint256 generalizedIndex;\n if (level == BalanceProofLevel.Container) {\n // Get the index within the balances container, not the Beacon Block\n // BeaconBlock.state.balances[balanceIndex]\n generalizedIndex = concatGenIndices(\n 1,\n BALANCES_HEIGHT,\n balanceIndex\n );\n }\n\n if (level == BalanceProofLevel.BeaconBlock) {\n generalizedIndex = concatGenIndices(\n BALANCES_CONTAINER_GENERALIZED_INDEX,\n BALANCES_HEIGHT,\n balanceIndex\n );\n }\n\n validatorBalanceGwei = balanceAtIndex(\n validatorBalanceLeaf,\n validatorIndex\n );\n\n require(\n Merkle.verifyInclusionSha256({\n proof: balanceProof,\n root: root,\n leaf: validatorBalanceLeaf,\n index: generalizedIndex\n }),\n \"Invalid balance container proof\"\n );\n }\n\n /// @notice Verifies the slot of the first pending deposit against the beacon block root\n /// BeaconBlock.state.PendingDeposits[0].slot\n /// @param beaconBlockRoot The root of the beacon block\n /// @param slot The beacon chain slot to verify\n /// @param firstPendingDepositSlotProof The merkle proof for the first pending deposit's slot\n /// against the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) internal view {\n // Convert uint64 slot number to a little endian bytes32\n bytes32 slotLeaf = Endian.toLittleEndianUint64(slot);\n\n require(\n Merkle.verifyInclusionSha256({\n proof: firstPendingDepositSlotProof,\n root: beaconBlockRoot,\n leaf: slotLeaf,\n index: FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX\n }),\n \"Invalid pending deposit proof\"\n );\n }\n\n /// @notice Verifies the block number to the the beacon block root\n /// BeaconBlock.body.executionPayload.blockNumber\n /// @param beaconBlockRoot The root of the beacon block\n /// @param blockNumber The execution layer block number to verify\n /// @param blockNumberProof The merkle proof for the block number against the beacon block\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyBlockNumber(\n bytes32 beaconBlockRoot,\n uint256 blockNumber,\n bytes calldata blockNumberProof\n ) internal view {\n // Convert uint64 block number to a little endian bytes32\n bytes32 blockNumberLeaf = Endian.toLittleEndianUint64(\n uint64(blockNumber)\n );\n require(\n Merkle.verifyInclusionSha256({\n proof: blockNumberProof,\n root: beaconBlockRoot,\n leaf: blockNumberLeaf,\n index: BLOCK_NUMBER_GENERALIZED_INDEX\n }),\n \"Invalid block number proof\"\n );\n }\n\n /// @notice Verifies the slot number against the beacon block root.\n /// BeaconBlock.slot\n /// @param beaconBlockRoot The root of the beacon block\n /// @param slot The beacon chain slot to verify\n /// @param slotProof The merkle proof for the slot against the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifySlot(\n bytes32 beaconBlockRoot,\n uint256 slot,\n bytes calldata slotProof\n ) internal view {\n require(\n Merkle.verifyInclusionSha256({\n proof: slotProof,\n root: beaconBlockRoot,\n leaf: Endian.toLittleEndianUint64(uint64(slot)),\n index: SLOT_GENERALIZED_INDEX\n }),\n \"Invalid slot number proof\"\n );\n }\n\n ////////////////////////////////////////////////////\n /// Internal Helper Functions\n ////////////////////////////////////////////////////\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n internal\n pure\n returns (uint256)\n {\n uint256 bitShiftAmount = (validatorIndex % 4) * 64;\n return\n Endian.fromLittleEndianUint64(\n bytes32((uint256(validatorBalanceLeaf) << bitShiftAmount))\n );\n }\n\n /// @notice Concatenates two beacon chain generalized indices into one.\n /// @param genIndex The first generalized index or 1 if calculating for a single container.\n /// @param height The merkle tree height of the second container. eg 39 for balances, 41 for validators.\n /// @param index The index within the second container. eg the validator index.\n /// @return genIndex The concatenated generalized index.\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) internal pure returns (uint256) {\n return (genIndex << height) | index;\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice The length of the beacon block root ring buffer\n uint256 internal constant BEACON_ROOTS_HISTORY_BUFFER_LENGTH = 8191;\n\n /// @notice Returns the Beacon Block Root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // require(block.timestamp >= timestamp, \"Timestamp in future\");\n require(\n block.timestamp - timestamp <\n BEACON_ROOTS_HISTORY_BUFFER_LENGTH * 12,\n \"Timestamp too old\"\n );\n\n // Call the Beacon Block Root Oracle to get the parent block root\n // This does not have a function signature, so we use a staticcall\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/Endian.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary Endian {\n /**\n * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64\n * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type\n * @return n The big endian-formatted uint64\n * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits),\n * but it is immediately truncated to a uint64 (i.e. 64 bits)\n * through a right-shift/shr operation.\n */\n function fromLittleEndianUint64(bytes32 lenum)\n internal\n pure\n returns (uint64 n)\n {\n // the number needs to be stored in little-endian encoding (ie in bytes 0-8)\n n = uint64(uint256(lenum >> 192));\n // forgefmt: disable-next-item\n return\n (n >> 56) |\n ((0x00FF000000000000 & n) >> 40) |\n ((0x0000FF0000000000 & n) >> 24) |\n ((0x000000FF00000000 & n) >> 8) |\n ((0x00000000FF000000 & n) << 8) |\n ((0x0000000000FF0000 & n) << 24) |\n ((0x000000000000FF00 & n) << 40) |\n ((0x00000000000000FF & n) << 56);\n }\n\n function toLittleEndianUint64(uint64 benum)\n internal\n pure\n returns (bytes32 n)\n {\n // Convert to little-endian by reversing byte order\n uint64 reversed = (benum >> 56) |\n ((0x00FF000000000000 & benum) >> 40) |\n ((0x0000FF0000000000 & benum) >> 24) |\n ((0x000000FF00000000 & benum) >> 8) |\n ((0x00000000FF000000 & benum) << 8) |\n ((0x0000000000FF0000 & benum) << 24) |\n ((0x000000000000FF00 & benum) << 40) |\n ((0x00000000000000FF & benum) << 56);\n\n // Store the little-endian uint64 in the least significant 64 bits of bytes32\n n = bytes32(uint256(reversed));\n // Shift to most significant bits\n n = n << 192;\n }\n}\n" + }, + "contracts/beacon/Merkle.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev These functions deal with verification of Merkle Tree proofs.\n *\n * The tree and the proofs can be generated using our\n * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].\n * You will find a quickstart guide in the readme.\n *\n * WARNING: You should avoid using leaf values that are 64 bytes long prior to\n * hashing, or use a hash function other than keccak256 for hashing leaves.\n * This is because the concatenation of a sorted pair of internal nodes in\n * the merkle tree could be reinterpreted as a leaf value.\n * OpenZeppelin's JavaScript library generates merkle trees that are safe\n * against this attack out of the box.\n */\nlibrary Merkle {\n error InvalidProofLength();\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function verifyInclusionSha256(\n bytes memory proof,\n bytes32 root,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bool) {\n return processInclusionProofSha256(proof, leaf, index) == root;\n }\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * _Available since v4.4._\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function processInclusionProofSha256(\n bytes memory proof,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bytes32) {\n require(\n proof.length != 0 && proof.length % 32 == 0,\n InvalidProofLength()\n );\n bytes32[1] memory computedHash = [leaf];\n for (uint256 i = 32; i <= proof.length; i += 32) {\n if (index % 2 == 0) {\n // if ith bit of index is 0, then computedHash is a left sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(computedHash))\n mstore(0x20, mload(add(proof, i)))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n index := div(index, 2)\n }\n } else {\n // if ith bit of index is 1, then computedHash is a right sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(add(proof, i)))\n mstore(0x20, mload(computedHash))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n index := div(index, 2)\n }\n }\n }\n return computedHash[0];\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/echidna/Debugger.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary Debugger {\n event Debug(string debugString);\n event Debug(string description, string data);\n event Debug(string prefix, string description, string data);\n event Debug(string description, bytes32 data);\n event Debug(string prefix, string description, bytes32 data);\n event Debug(string description, uint256 data);\n event Debug(string prefix, string description, uint256 data);\n event Debug(string description, int256 data);\n event Debug(string prefix, string description, int256 data);\n event Debug(string description, address data);\n event Debug(string prefix, string description, address data);\n event Debug(string description, bool data);\n event Debug(string prefix, string description, bool data);\n\n function log(string memory debugString) internal {\n emit Debug(debugString);\n }\n\n function log(string memory description, string memory data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n string memory data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, bytes32 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n bytes32 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, uint256 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n uint256 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, int256 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n int256 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, address data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n address data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, bool data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n bool data\n ) internal {\n emit Debug(prefix, description, data);\n }\n}\n" + }, + "contracts/echidna/Echidna.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaTestApproval.sol\";\n\n/**\n * @title Echidna test contract for OUSD\n * @notice Target contract to be tested, containing all mixins\n * @author Rappie\n */\ncontract Echidna is EchidnaTestApproval {\n\n}\n" + }, + "contracts/echidna/EchidnaConfig.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Top-level mixin for configuring the desired fuzzing setup\n * @author Rappie\n */\ncontract EchidnaConfig {\n address internal constant ADDRESS_VAULT = address(0x10000);\n address internal constant ADDRESS_OUTSIDER_USER = address(0x20000);\n\n address internal constant ADDRESS_USER0 = address(0x30000);\n address internal constant ADDRESS_USER1 = address(0x40000);\n\n // Will be set in EchidnaSetup constructor\n address internal ADDRESS_OUTSIDER_CONTRACT;\n address internal ADDRESS_CONTRACT0;\n address internal ADDRESS_CONTRACT1;\n\n // Toggle known issues\n //\n // This can be used to skip tests that are known to fail. This is useful\n // when debugging a specific issue, but should be disabled when running\n // the full test suite.\n //\n // True => skip tests that are known to fail\n // False => run all tests\n //\n bool internal constant TOGGLE_KNOWN_ISSUES = false;\n\n // Toggle known issues within limits\n //\n // Same as TOGGLE_KNOWN_ISSUES, but also skip tests that are known to fail\n // within limits set by the variables below.\n //\n bool internal constant TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS = true;\n\n // Starting balance\n //\n // Gives OUSD a non-zero starting supply, which can be useful to ignore\n // certain edge cases.\n //\n // The starting balance is given to outsider accounts that are not used as\n // accounts while fuzzing.\n //\n bool internal constant TOGGLE_STARTING_BALANCE = true;\n uint256 internal constant STARTING_BALANCE = 1_000_000e18;\n\n // Change supply\n //\n // Set a limit to the amount of change per rebase, which can be useful to\n // ignore certain edge cases.\n //\n // True => limit the amount of change to a percentage of total supply\n // False => no limit\n //\n bool internal constant TOGGLE_CHANGESUPPLY_LIMIT = true;\n uint256 internal constant CHANGESUPPLY_DIVISOR = 10; // 10% of total supply\n\n // Mint limit\n //\n // Set a limit the the amount minted per mint, which can be useful to\n // ignore certain edge cases.\n //\n // True => limit the amount of minted tokens\n // False => no limit\n //\n bool internal constant TOGGLE_MINT_LIMIT = true;\n uint256 internal constant MINT_MODULO = 1_000_000_000_000e18;\n\n // Known rounding errors\n uint256 internal constant TRANSFER_ROUNDING_ERROR = 1e18 - 1;\n uint256 internal constant OPT_IN_ROUNDING_ERROR = 1e18 - 1;\n uint256 internal constant MINT_ROUNDING_ERROR = 1e18 - 1;\n\n /**\n * @notice Modifier to skip tests that are known to fail\n * @dev see TOGGLE_KNOWN_ISSUES for more information\n */\n modifier hasKnownIssue() {\n if (TOGGLE_KNOWN_ISSUES) return;\n _;\n }\n\n /**\n * @notice Modifier to skip tests that are known to fail within limits\n * @dev see TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS for more information\n */\n modifier hasKnownIssueWithinLimits() {\n if (TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS) return;\n _;\n }\n\n /**\n * @notice Translate an account ID to an address\n * @param accountId The ID of the account\n * @return account The address of the account\n */\n function getAccount(uint8 accountId)\n internal\n view\n returns (address account)\n {\n accountId = accountId / 64;\n if (accountId == 0) return account = ADDRESS_USER0;\n if (accountId == 1) return account = ADDRESS_USER1;\n if (accountId == 2) return account = ADDRESS_CONTRACT0;\n if (accountId == 3) return account = ADDRESS_CONTRACT1;\n require(false, \"Unknown account ID\");\n }\n}\n" + }, + "contracts/echidna/EchidnaDebug.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport \"./EchidnaHelper.sol\";\nimport \"./Debugger.sol\";\n\nimport \"../token/OUSD.sol\";\n\n/**\n * @title Room for random debugging functions\n * @author Rappie\n */\ncontract EchidnaDebug is EchidnaHelper {\n function debugOUSD() public pure {\n // assert(ousd.balanceOf(ADDRESS_USER0) == 1000);\n // assert(ousd.rebaseState(ADDRESS_USER0) != OUSD.RebaseOptions.OptIn);\n // assert(Address.isContract(ADDRESS_CONTRACT0));\n // Debugger.log(\"nonRebasingSupply\", ousd.nonRebasingSupply());\n // assert(false);\n }\n}\n" + }, + "contracts/echidna/EchidnaHelper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaSetup.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin containing helper functions\n * @author Rappie\n */\ncontract EchidnaHelper is EchidnaSetup {\n /**\n * @notice Mint tokens to an account\n * @param toAcc Account to mint to\n * @param amount Amount to mint\n * @return Amount minted (in case of capped mint with modulo)\n */\n function mint(uint8 toAcc, uint256 amount) public returns (uint256) {\n address to = getAccount(toAcc);\n\n if (TOGGLE_MINT_LIMIT) {\n amount = amount % MINT_MODULO;\n }\n\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(to, amount);\n\n return amount;\n }\n\n /**\n * @notice Burn tokens from an account\n * @param fromAcc Account to burn from\n * @param amount Amount to burn\n */\n function burn(uint8 fromAcc, uint256 amount) public {\n address from = getAccount(fromAcc);\n hevm.prank(ADDRESS_VAULT);\n ousd.burn(from, amount);\n }\n\n /**\n * @notice Change the total supply of OUSD (rebase)\n * @param amount New total supply\n */\n function changeSupply(uint256 amount) public {\n if (TOGGLE_CHANGESUPPLY_LIMIT) {\n amount =\n ousd.totalSupply() +\n (amount % (ousd.totalSupply() / CHANGESUPPLY_DIVISOR));\n }\n\n hevm.prank(ADDRESS_VAULT);\n ousd.changeSupply(amount);\n }\n\n /**\n * @notice Transfer tokens between accounts\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function transfer(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n ousd.transfer(to, amount);\n }\n\n /**\n * @notice Transfer approved tokens between accounts\n * @param authorizedAcc Account that is authorized to transfer\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function transferFrom(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n ousd.transferFrom(from, to, amount);\n }\n\n /**\n * @notice Opt in to rebasing\n * @param targetAcc Account to opt in\n */\n function optIn(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n hevm.prank(target);\n ousd.rebaseOptIn();\n }\n\n /**\n * @notice Opt out of rebasing\n * @param targetAcc Account to opt out\n */\n function optOut(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n hevm.prank(target);\n ousd.rebaseOptOut();\n }\n\n /**\n * @notice Approve an account to spend OUSD\n * @param ownerAcc Account that owns the OUSD\n * @param spenderAcc Account that is approved to spend the OUSD\n * @param amount Amount to approve\n */\n function approve(\n uint8 ownerAcc,\n uint8 spenderAcc,\n uint256 amount\n ) public {\n address owner = getAccount(ownerAcc);\n address spender = getAccount(spenderAcc);\n hevm.prank(owner);\n // slither-disable-next-line unused-return\n ousd.approve(spender, amount);\n }\n\n /**\n * @notice Get the sum of all OUSD balances\n * @return total Total balance\n */\n function getTotalBalance() public view returns (uint256 total) {\n total += ousd.balanceOf(ADDRESS_VAULT);\n total += ousd.balanceOf(ADDRESS_OUTSIDER_USER);\n total += ousd.balanceOf(ADDRESS_OUTSIDER_CONTRACT);\n total += ousd.balanceOf(ADDRESS_USER0);\n total += ousd.balanceOf(ADDRESS_USER1);\n total += ousd.balanceOf(ADDRESS_CONTRACT0);\n total += ousd.balanceOf(ADDRESS_CONTRACT1);\n }\n\n /**\n * @notice Get the sum of all non-rebasing OUSD balances\n * @return total Total balance\n */\n function getTotalNonRebasingBalance() public returns (uint256 total) {\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_VAULT)\n ? ousd.balanceOf(ADDRESS_VAULT)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_OUTSIDER_USER)\n ? ousd.balanceOf(ADDRESS_OUTSIDER_USER)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_OUTSIDER_CONTRACT)\n ? ousd.balanceOf(ADDRESS_OUTSIDER_CONTRACT)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_USER0)\n ? ousd.balanceOf(ADDRESS_USER0)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_USER1)\n ? ousd.balanceOf(ADDRESS_USER1)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_CONTRACT0)\n ? ousd.balanceOf(ADDRESS_CONTRACT0)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_CONTRACT1)\n ? ousd.balanceOf(ADDRESS_CONTRACT1)\n : 0;\n }\n}\n" + }, + "contracts/echidna/EchidnaSetup.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./IHevm.sol\";\nimport \"./EchidnaConfig.sol\";\nimport \"./OUSDEchidna.sol\";\n\ncontract Dummy {}\n\n/**\n * @title Mixin for setup and deployment\n * @author Rappie\n */\ncontract EchidnaSetup is EchidnaConfig {\n IHevm hevm = IHevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);\n OUSDEchidna ousd = new OUSDEchidna();\n\n /**\n * @notice Deploy the OUSD contract and set up initial state\n */\n constructor() {\n ousd.initialize(ADDRESS_VAULT, 1e18);\n\n // Deploy dummny contracts as users\n Dummy outsider = new Dummy();\n ADDRESS_OUTSIDER_CONTRACT = address(outsider);\n Dummy dummy0 = new Dummy();\n ADDRESS_CONTRACT0 = address(dummy0);\n Dummy dummy1 = new Dummy();\n ADDRESS_CONTRACT1 = address(dummy1);\n\n // Start out with a reasonable amount of OUSD\n if (TOGGLE_STARTING_BALANCE) {\n // Rebasing tokens\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(ADDRESS_OUTSIDER_USER, STARTING_BALANCE / 2);\n\n // Non-rebasing tokens\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(ADDRESS_OUTSIDER_CONTRACT, STARTING_BALANCE / 2);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestAccounting.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestSupply.sol\";\n\n/**\n * @title Mixin for testing accounting functions\n * @author Rappie\n */\ncontract EchidnaTestAccounting is EchidnaTestSupply {\n /**\n * @notice After opting in, balance should not increase. (Ok to lose rounding funds doing this)\n * @param targetAcc Account to opt in\n */\n function testOptInBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optIn(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter <= balanceBefore);\n }\n\n /**\n * @notice After opting out, balance should remain the same\n * @param targetAcc Account to opt out\n */\n function testOptOutBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optOut(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Account balance should remain the same after opting in minus rounding error\n * @param targetAcc Account to opt in\n */\n function testOptInBalanceRounding(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optIn(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n int256 delta = int256(balanceAfter) - int256(balanceBefore);\n Debugger.log(\"delta\", delta);\n\n // slither-disable-next-line tautology\n assert(-1 * delta >= 0);\n assert(-1 * delta <= int256(OPT_IN_ROUNDING_ERROR));\n }\n\n /**\n * @notice After opting in, total supply should remain the same\n * @param targetAcc Account to opt in\n */\n function testOptInTotalSupply(uint8 targetAcc) public {\n uint256 totalSupplyBefore = ousd.totalSupply();\n optIn(targetAcc);\n uint256 totalSupplyAfter = ousd.totalSupply();\n\n assert(totalSupplyAfter == totalSupplyBefore);\n }\n\n /**\n * @notice After opting out, total supply should remain the same\n * @param targetAcc Account to opt out\n */\n function testOptOutTotalSupply(uint8 targetAcc) public {\n uint256 totalSupplyBefore = ousd.totalSupply();\n optOut(targetAcc);\n uint256 totalSupplyAfter = ousd.totalSupply();\n\n assert(totalSupplyAfter == totalSupplyBefore);\n }\n\n /**\n * @notice Account balance should remain the same when a smart contract auto converts\n * @param targetAcc Account to auto convert\n */\n function testAutoConvertBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n // slither-disable-next-line unused-return\n ousd._isNonRebasingAccountEchidna(target);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice The `balanceOf` function should never revert\n * @param targetAcc Account to check balance of\n */\n function testBalanceOfShouldNotRevert(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n // slither-disable-next-line unused-return\n try ousd.balanceOf(target) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestApproval.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaTestMintBurn.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin for testing approval related functions\n * @author Rappie\n */\ncontract EchidnaTestApproval is EchidnaTestMintBurn {\n /**\n * @notice Performing `transferFrom` with an amount inside the allowance should not revert\n * @param authorizedAcc The account that is authorized to transfer\n * @param fromAcc The account that is transferring\n * @param toAcc The account that is receiving\n * @param amount The amount to transfer\n */\n function testTransferFromShouldNotRevert(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount <= ousd.balanceOf(from));\n require(amount <= ousd.allowance(from, authorized));\n\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n try ousd.transferFrom(from, to, amount) {\n // pass\n } catch {\n assert(false);\n }\n }\n\n /**\n * @notice Performing `transferFrom` with an amount outside the allowance should revert\n * @param authorizedAcc The account that is authorized to transfer\n * @param fromAcc The account that is transferring\n * @param toAcc The account that is receiving\n * @param amount The amount to transfer\n */\n function testTransferFromShouldRevert(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount > 0);\n require(\n !(amount <= ousd.balanceOf(from) &&\n amount <= ousd.allowance(from, authorized))\n );\n\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n try ousd.transferFrom(from, to, amount) {\n assert(false);\n } catch {\n // pass\n }\n }\n\n /**\n * @notice Approving an amount should update the allowance and overwrite any previous allowance\n * @param ownerAcc The account that is approving\n * @param spenderAcc The account that is being approved\n * @param amount The amount to approve\n */\n function testApprove(\n uint8 ownerAcc,\n uint8 spenderAcc,\n uint256 amount\n ) public {\n address owner = getAccount(ownerAcc);\n address spender = getAccount(spenderAcc);\n\n approve(ownerAcc, spenderAcc, amount);\n uint256 allowanceAfter1 = ousd.allowance(owner, spender);\n\n assert(allowanceAfter1 == amount);\n\n approve(ownerAcc, spenderAcc, amount / 2);\n uint256 allowanceAfter2 = ousd.allowance(owner, spender);\n\n assert(allowanceAfter2 == amount / 2);\n }\n}\n" + }, + "contracts/echidna/EchidnaTestMintBurn.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestAccounting.sol\";\n\n/**\n * @title Mixin for testing Mint and Burn functions\n * @author Rappie\n */\ncontract EchidnaTestMintBurn is EchidnaTestAccounting {\n /**\n * @notice Minting 0 tokens should not affect account balance\n * @param targetAcc Account to mint to\n */\n function testMintZeroBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n mint(targetAcc, 0);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Burning 0 tokens should not affect account balance\n * @param targetAcc Account to burn from\n */\n function testBurnZeroBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n burn(targetAcc, 0);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Minting tokens must increase the account balance by at least amount\n * @param targetAcc Account to mint to\n * @param amount Amount to mint\n * @custom:error testMintBalance(uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * testMintBalance(0,1)\n * Event sequence:\n * Debug(«balanceBefore», 0)\n * Debug(«balanceAfter», 0)\n */\n function testMintBalance(uint8 targetAcc, uint256 amount)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n uint256 amountMinted = mint(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n Debugger.log(\"amountMinted\", amountMinted);\n Debugger.log(\"balanceBefore\", balanceBefore);\n Debugger.log(\"balanceAfter\", balanceAfter);\n\n assert(balanceAfter >= balanceBefore + amountMinted);\n }\n\n /**\n * @notice Burning tokens must decrease the account balance by at least amount\n * @param targetAcc Account to burn from\n * @param amount Amount to burn\n * @custom:error testBurnBalance(uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * mint(0,3)\n * testBurnBalance(0,1)\n * Event sequence:\n * Debug(«balanceBefore», 2)\n * Debug(«balanceAfter», 2)\n */\n function testBurnBalance(uint8 targetAcc, uint256 amount)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n burn(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n Debugger.log(\"balanceBefore\", balanceBefore);\n Debugger.log(\"balanceAfter\", balanceAfter);\n\n assert(balanceAfter <= balanceBefore - amount);\n }\n\n /**\n * @notice Minting tokens should not increase the account balance by less than rounding error above amount\n * @param targetAcc Account to mint to\n * @param amount Amount to mint\n */\n function testMintBalanceRounding(uint8 targetAcc, uint256 amount) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n uint256 amountMinted = mint(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n int256 delta = int256(balanceAfter) - int256(balanceBefore);\n\n // delta == amount, if no error\n // delta < amount, if too little is minted\n // delta > amount, if too much is minted\n int256 error = int256(amountMinted) - delta;\n\n assert(error >= 0);\n assert(error <= int256(MINT_ROUNDING_ERROR));\n }\n\n /**\n * @notice A burn of an account balance must result in a zero balance\n * @param targetAcc Account to burn from\n */\n function testBurnAllBalanceToZero(uint8 targetAcc) public hasKnownIssue {\n address target = getAccount(targetAcc);\n\n burn(targetAcc, ousd.balanceOf(target));\n assert(ousd.balanceOf(target) == 0);\n }\n\n /**\n * @notice You should always be able to burn an account's balance\n * @param targetAcc Account to burn from\n */\n function testBurnAllBalanceShouldNotRevert(uint8 targetAcc)\n public\n hasKnownIssue\n {\n address target = getAccount(targetAcc);\n uint256 balance = ousd.balanceOf(target);\n\n hevm.prank(ADDRESS_VAULT);\n try ousd.burn(target, balance) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestSupply.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestTransfer.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n/**\n * @title Mixin for testing supply related functions\n * @author Rappie\n */\ncontract EchidnaTestSupply is EchidnaTestTransfer {\n using StableMath for uint256;\n\n uint256 prevRebasingCreditsPerToken = type(uint256).max;\n\n /**\n * @notice After a `changeSupply`, the total supply should exactly\n * match the target total supply. (This is needed to ensure successive\n * rebases are correct).\n * @param supply New total supply\n * @custom:error testChangeSupply(uint256): failed!💥\n * Call sequence:\n * testChangeSupply(1044505275072865171609)\n * Event sequence:\n * TotalSupplyUpdatedHighres(1044505275072865171610, 1000000000000000000000000, 957391048054055578595)\n */\n function testChangeSupply(uint256 supply)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n hevm.prank(ADDRESS_VAULT);\n ousd.changeSupply(supply);\n\n assert(ousd.totalSupply() == supply);\n }\n\n /**\n * @notice The total supply must not be less than the sum of account balances.\n * (The difference will go into future rebases)\n * @custom:error testTotalSupplyLessThanTotalBalance(): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(1)\n * optOut(64)\n * transfer(0,64,1)\n * testTotalSupplyLessThanTotalBalance()\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000001000001)\n * Debug(«totalBalance», 1000000000000000001000002)\n */\n function testTotalSupplyLessThanTotalBalance()\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n uint256 totalSupply = ousd.totalSupply();\n uint256 totalBalance = getTotalBalance();\n\n Debugger.log(\"totalSupply\", totalSupply);\n Debugger.log(\"totalBalance\", totalBalance);\n\n assert(totalSupply >= totalBalance);\n }\n\n /**\n * @notice Non-rebasing supply should not be larger than total supply\n * @custom:error testNonRebasingSupplyVsTotalSupply(): failed!💥\n * Call sequence:\n * mint(0,2)\n * changeSupply(3)\n * burn(0,1)\n * optOut(0)\n * testNonRebasingSupplyVsTotalSupply()\n */\n function testNonRebasingSupplyVsTotalSupply() public hasKnownIssue {\n uint256 nonRebasingSupply = ousd.nonRebasingSupply();\n uint256 totalSupply = ousd.totalSupply();\n\n assert(nonRebasingSupply <= totalSupply);\n }\n\n /**\n * @notice Global `rebasingCreditsPerToken` should never increase\n * @custom:error testRebasingCreditsPerTokenNotIncreased(): failed!💥\n * Call sequence:\n * testRebasingCreditsPerTokenNotIncreased()\n * changeSupply(1)\n * testRebasingCreditsPerTokenNotIncreased()\n */\n function testRebasingCreditsPerTokenNotIncreased() public hasKnownIssue {\n uint256 curRebasingCreditsPerToken = ousd\n .rebasingCreditsPerTokenHighres();\n\n Debugger.log(\n \"prevRebasingCreditsPerToken\",\n prevRebasingCreditsPerToken\n );\n Debugger.log(\"curRebasingCreditsPerToken\", curRebasingCreditsPerToken);\n\n assert(curRebasingCreditsPerToken <= prevRebasingCreditsPerToken);\n\n prevRebasingCreditsPerToken = curRebasingCreditsPerToken;\n }\n\n /**\n * @notice The rebasing credits per token ratio must greater than zero\n */\n function testRebasingCreditsPerTokenAboveZero() public {\n assert(ousd.rebasingCreditsPerTokenHighres() > 0);\n }\n\n /**\n * @notice The sum of all non-rebasing balances should not be larger than\n * non-rebasing supply\n * @custom:error testTotalNonRebasingSupplyLessThanTotalBalance(): failed!💥\n * Call sequence\n * mint(0,2)\n * changeSupply(1)\n * optOut(0)\n * burn(0,1)\n * testTotalNonRebasingSupplyLessThanTotalBalance()\n * Event sequence:\n * Debug(«totalNonRebasingSupply», 500000000000000000000001)\n * Debug(«totalNonRebasingBalance», 500000000000000000000002)\n */\n function testTotalNonRebasingSupplyLessThanTotalBalance()\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n uint256 totalNonRebasingSupply = ousd.nonRebasingSupply();\n uint256 totalNonRebasingBalance = getTotalNonRebasingBalance();\n\n Debugger.log(\"totalNonRebasingSupply\", totalNonRebasingSupply);\n Debugger.log(\"totalNonRebasingBalance\", totalNonRebasingBalance);\n\n assert(totalNonRebasingSupply >= totalNonRebasingBalance);\n }\n\n /**\n * @notice An accounts credits / credits per token should not be larger it's balance\n * @param targetAcc The account to check\n */\n function testCreditsPerTokenVsBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n (uint256 credits, uint256 creditsPerToken, ) = ousd\n .creditsBalanceOfHighres(target);\n uint256 expectedBalance = credits.divPrecisely(creditsPerToken);\n\n uint256 balance = ousd.balanceOf(target);\n\n Debugger.log(\"credits\", credits);\n Debugger.log(\"creditsPerToken\", creditsPerToken);\n Debugger.log(\"expectedBalance\", expectedBalance);\n Debugger.log(\"balance\", balance);\n\n assert(expectedBalance == balance);\n }\n}\n" + }, + "contracts/echidna/EchidnaTestTransfer.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin for testing transfer related functions\n * @author Rappie\n */\ncontract EchidnaTestTransfer is EchidnaDebug {\n /**\n * @notice The receiving account's balance after a transfer must not increase by\n * less than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferBalanceReceivedLess(uint8,uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * mint(64,2)\n * testTransferBalanceReceivedLess(64,0,1)\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000000500002)\n * Debug(«toBalBefore», 0)\n * Debug(«toBalAfter», 0)\n */\n function testTransferBalanceReceivedLess(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue hasKnownIssueWithinLimits {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n\n assert(toBalAfter >= toBalBefore + amount);\n }\n\n /**\n * @notice The receiving account's balance after a transfer must not\n * increase by more than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceReceivedMore(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n\n assert(toBalAfter <= toBalBefore + amount);\n }\n\n /**\n * @notice The sending account's balance after a transfer must not\n * decrease by less than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferBalanceSentLess(uint8,uint8,uint256): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(1)\n * testTransferBalanceSentLess(0,64,1)\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000000500001)\n * Debug(«fromBalBefore», 1)\n * Debug(«fromBalAfter», 1)\n */\n function testTransferBalanceSentLess(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue hasKnownIssueWithinLimits {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n\n assert(fromBalAfter <= fromBalBefore - amount);\n }\n\n /**\n * @notice The sending account's balance after a transfer must not\n * decrease by more than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceSentMore(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n\n assert(fromBalAfter >= fromBalBefore - amount);\n }\n\n /**\n * @notice The receiving account's balance after a transfer must not\n * increase by less than the amount transferred (minus rounding error)\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceReceivedLessRounding(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n int256 toDelta = int256(toBalAfter) - int256(toBalBefore);\n\n // delta == amount, if no error\n // delta < amount, if too little is sent\n // delta > amount, if too much is sent\n int256 error = int256(amount) - toDelta;\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n Debugger.log(\"toDelta\", toDelta);\n Debugger.log(\"error\", error);\n\n assert(error >= 0);\n assert(error <= int256(TRANSFER_ROUNDING_ERROR));\n }\n\n /**\n * @notice The sending account's balance after a transfer must\n * not decrease by less than the amount transferred (minus rounding error)\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceSentLessRounding(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n int256 fromDelta = int256(fromBalAfter) - int256(fromBalBefore);\n\n // delta == -amount, if no error\n // delta < -amount, if too much is sent\n // delta > -amount, if too little is sent\n int256 error = int256(amount) + fromDelta;\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n Debugger.log(\"fromDelta\", fromDelta);\n Debugger.log(\"error\", error);\n\n assert(error >= 0);\n assert(error <= int256(TRANSFER_ROUNDING_ERROR));\n }\n\n /**\n * @notice An account should always be able to successfully transfer\n * an amount within its balance.\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferWithinBalanceDoesNotRevert(uint8,uint8,uint8): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(3)\n * optOut(0)\n * testTransferWithinBalanceDoesNotRevert(0,128,2)\n * optIn(0)\n * testTransferWithinBalanceDoesNotRevert(128,0,1)\n * Event sequence:\n * error Revert Panic(17): SafeMath over-/under-flows\n */\n function testTransferWithinBalanceDoesNotRevert(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount > 0);\n amount = amount % ousd.balanceOf(from);\n\n Debugger.log(\"Total supply\", ousd.totalSupply());\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(to, amount) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n\n /**\n * @notice An account should never be able to successfully transfer\n * an amount greater than their balance.\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferExceedingBalanceReverts(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n amount = ousd.balanceOf(from) + 1 + amount;\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(to, amount) {\n assert(false);\n } catch {\n assert(true);\n }\n }\n\n /**\n * @notice A transfer to the same account should not change that account's balance\n * @param targetAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferSelf(uint8 targetAcc, uint256 amount) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n transfer(targetAcc, targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceBefore == balanceAfter);\n }\n\n /**\n * @notice Transfers to the zero account revert\n * @param fromAcc Account to transfer from\n * @param amount Amount to transfer\n */\n function testTransferToZeroAddress(uint8 fromAcc, uint256 amount) public {\n address from = getAccount(fromAcc);\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(address(0), amount) {\n assert(false);\n } catch {\n assert(true);\n }\n }\n}\n" + }, + "contracts/echidna/IHevm.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// https://github.com/ethereum/hevm/blob/main/doc/src/controlling-the-unit-testing-environment.md#cheat-codes\n\ninterface IHevm {\n function warp(uint256 x) external;\n\n function roll(uint256 x) external;\n\n function store(\n address c,\n bytes32 loc,\n bytes32 val\n ) external;\n\n function load(address c, bytes32 loc) external returns (bytes32 val);\n\n function sign(uint256 sk, bytes32 digest)\n external\n returns (\n uint8 v,\n bytes32 r,\n bytes32 s\n );\n\n function addr(uint256 sk) external returns (address addr);\n\n function ffi(string[] calldata) external returns (bytes memory);\n\n function prank(address sender) external;\n}\n" + }, + "contracts/echidna/OUSDEchidna.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../token/OUSD.sol\";\n\ncontract OUSDEchidna is OUSD {\n constructor() OUSD() {}\n\n function _isNonRebasingAccountEchidna(address _account)\n public\n returns (bool)\n {\n _autoMigrate(_account);\n return alternativeCreditsPerToken[_account] > 0;\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/harvest/AbstractHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IUniswapV2Router } from \"../interfaces/uniswap/IUniswapV2Router02.sol\";\nimport { IUniswapV3Router } from \"../interfaces/uniswap/IUniswapV3Router.sol\";\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { ICurvePool } from \"../strategies/ICurvePool.sol\";\nimport \"../utils/Helpers.sol\";\n\nabstract contract AbstractHarvester is Governable {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using StableMath for uint256;\n\n enum SwapPlatform {\n UniswapV2Compatible,\n UniswapV3,\n Balancer,\n Curve\n }\n\n event SupportedStrategyUpdate(address strategyAddress, bool isSupported);\n event RewardTokenConfigUpdated(\n address tokenAddress,\n uint16 allowedSlippageBps,\n uint16 harvestRewardBps,\n SwapPlatform swapPlatform,\n address swapPlatformAddr,\n bytes swapData,\n uint256 liquidationLimit,\n bool doSwapRewardToken\n );\n event RewardTokenSwapped(\n address indexed rewardToken,\n address indexed swappedInto,\n SwapPlatform swapPlatform,\n uint256 amountIn,\n uint256 amountOut\n );\n event RewardProceedsTransferred(\n address indexed token,\n address farmer,\n uint256 protcolYield,\n uint256 farmerFee\n );\n event RewardProceedsAddressChanged(address newProceedsAddress);\n\n error EmptyAddress();\n error InvalidSlippageBps();\n error InvalidHarvestRewardBps();\n\n error InvalidSwapPlatform(SwapPlatform swapPlatform);\n\n error InvalidUniswapV2PathLength();\n error InvalidTokenInSwapPath(address token);\n error EmptyBalancerPoolId();\n error InvalidCurvePoolAssetIndex(address token);\n\n error UnsupportedStrategy(address strategyAddress);\n\n error SlippageError(uint256 actualBalance, uint256 minExpected);\n error BalanceMismatchAfterSwap(uint256 actualBalance, uint256 minExpected);\n\n // Configuration properties for harvesting logic of reward tokens\n struct RewardTokenConfig {\n // Max allowed slippage when swapping reward token for a stablecoin denominated in basis points.\n uint16 allowedSlippageBps;\n // Reward when calling a harvest function denominated in basis points.\n uint16 harvestRewardBps;\n // Address of compatible exchange protocol (Uniswap V2/V3, SushiSwap, Balancer and Curve).\n address swapPlatformAddr;\n /* When true the reward token is being swapped. In a need of (temporarily) disabling the swapping of\n * a reward token this needs to be set to false.\n */\n bool doSwapRewardToken;\n // Platform to use for Swapping\n SwapPlatform swapPlatform;\n /* How much token can be sold per one harvest call. If the balance of rewards tokens\n * exceeds that limit multiple harvest calls are required to harvest all of the tokens.\n * Set it to MAX_INT to effectively disable the limit.\n */\n uint256 liquidationLimit;\n }\n\n mapping(address => RewardTokenConfig) public rewardTokenConfigs;\n mapping(address => bool) public supportedStrategies;\n\n address public immutable vaultAddress;\n\n /**\n * Address receiving rewards proceeds. Initially the Vault contract later will possibly\n * be replaced by another contract that eases out rewards distribution.\n **/\n address public rewardProceedsAddress;\n\n /**\n * All tokens are swapped to this token before it gets transferred\n * to the `rewardProceedsAddress`. USDT for OUSD and WETH for OETH.\n **/\n address public immutable baseTokenAddress;\n // Cached decimals for `baseTokenAddress`\n uint256 public immutable baseTokenDecimals;\n\n // Uniswap V2 path for reward tokens using Uniswap V2 Router\n mapping(address => address[]) public uniswapV2Path;\n // Uniswap V3 path for reward tokens using Uniswap V3 Router\n mapping(address => bytes) public uniswapV3Path;\n // Pool ID to use for reward tokens on Balancer\n mapping(address => bytes32) public balancerPoolId;\n\n struct CurvePoolIndices {\n // Casted into uint128 and stored in a struct to save gas\n uint128 rewardTokenIndex;\n uint128 baseTokenIndex;\n }\n // Packed indices of assets on the Curve pool\n mapping(address => CurvePoolIndices) public curvePoolIndices;\n\n constructor(address _vaultAddress, address _baseTokenAddress) {\n require(_vaultAddress != address(0));\n require(_baseTokenAddress != address(0));\n\n vaultAddress = _vaultAddress;\n baseTokenAddress = _baseTokenAddress;\n\n // Cache decimals as well\n baseTokenDecimals = Helpers.getDecimals(_baseTokenAddress);\n }\n\n /***************************************\n Configuration\n ****************************************/\n\n /**\n * Set the Address receiving rewards proceeds.\n * @param _rewardProceedsAddress Address of the reward token\n */\n function setRewardProceedsAddress(address _rewardProceedsAddress)\n external\n onlyGovernor\n {\n if (_rewardProceedsAddress == address(0)) {\n revert EmptyAddress();\n }\n\n rewardProceedsAddress = _rewardProceedsAddress;\n emit RewardProceedsAddressChanged(_rewardProceedsAddress);\n }\n\n /**\n * @dev Add/update a reward token configuration that holds harvesting config variables\n * @param _tokenAddress Address of the reward token\n * @param tokenConfig.allowedSlippageBps uint16 maximum allowed slippage denominated in basis points.\n * Example: 300 == 3% slippage\n * @param tokenConfig.harvestRewardBps uint16 amount of reward tokens the caller of the function is rewarded.\n * Example: 100 == 1%\n * @param tokenConfig.swapPlatformAddr Address Address of a UniswapV2 compatible contract to perform\n * the exchange from reward tokens to stablecoin (currently hard-coded to USDT)\n * @param tokenConfig.liquidationLimit uint256 Maximum amount of token to be sold per one swap function call.\n * When value is 0 there is no limit.\n * @param tokenConfig.doSwapRewardToken bool Disables swapping of the token when set to true,\n * does not cause it to revert though.\n * @param tokenConfig.swapPlatform SwapPlatform to use for Swapping\n * @param swapData Additional data required for swapping\n */\n function setRewardTokenConfig(\n address _tokenAddress,\n RewardTokenConfig calldata tokenConfig,\n bytes calldata swapData\n ) external onlyGovernor {\n if (tokenConfig.allowedSlippageBps > 1000) {\n revert InvalidSlippageBps();\n }\n\n if (tokenConfig.harvestRewardBps > 1000) {\n revert InvalidHarvestRewardBps();\n }\n\n address newRouterAddress = tokenConfig.swapPlatformAddr;\n if (newRouterAddress == address(0)) {\n // Swap router address should be non zero address\n revert EmptyAddress();\n }\n\n address oldRouterAddress = rewardTokenConfigs[_tokenAddress]\n .swapPlatformAddr;\n rewardTokenConfigs[_tokenAddress] = tokenConfig;\n\n // Revert if feed does not exist\n // slither-disable-next-line unused-return\n IOracle(IVault(vaultAddress).priceProvider()).price(_tokenAddress);\n\n IERC20 token = IERC20(_tokenAddress);\n // if changing token swap provider cancel existing allowance\n if (\n /* oldRouterAddress == address(0) when there is no pre-existing\n * configuration for said rewards token\n */\n oldRouterAddress != address(0) &&\n oldRouterAddress != newRouterAddress\n ) {\n token.safeApprove(oldRouterAddress, 0);\n }\n\n // Give SwapRouter infinite approval when needed\n if (oldRouterAddress != newRouterAddress) {\n token.safeApprove(newRouterAddress, 0);\n token.safeApprove(newRouterAddress, type(uint256).max);\n }\n\n SwapPlatform _platform = tokenConfig.swapPlatform;\n if (_platform == SwapPlatform.UniswapV2Compatible) {\n uniswapV2Path[_tokenAddress] = _decodeUniswapV2Path(\n swapData,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.UniswapV3) {\n uniswapV3Path[_tokenAddress] = _decodeUniswapV3Path(\n swapData,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.Balancer) {\n balancerPoolId[_tokenAddress] = _decodeBalancerPoolId(\n swapData,\n newRouterAddress,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.Curve) {\n curvePoolIndices[_tokenAddress] = _decodeCurvePoolIndices(\n swapData,\n newRouterAddress,\n _tokenAddress\n );\n } else {\n // Note: This code is unreachable since Solidity reverts when\n // the value is outside the range of defined values of the enum\n // (even if it's under the max length of the base type)\n revert InvalidSwapPlatform(_platform);\n }\n\n emit RewardTokenConfigUpdated(\n _tokenAddress,\n tokenConfig.allowedSlippageBps,\n tokenConfig.harvestRewardBps,\n _platform,\n newRouterAddress,\n swapData,\n tokenConfig.liquidationLimit,\n tokenConfig.doSwapRewardToken\n );\n }\n\n /**\n * @dev Decodes the data passed into Uniswap V2 path and validates\n * it to make sure the path is for `token` to `baseToken`\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param token The address of the reward token\n * @return path The validated Uniswap V2 path\n */\n function _decodeUniswapV2Path(bytes calldata data, address token)\n internal\n view\n returns (address[] memory path)\n {\n (path) = abi.decode(data, (address[]));\n uint256 len = path.length;\n\n if (len < 2) {\n // Path should have at least two tokens\n revert InvalidUniswapV2PathLength();\n }\n\n // Do some validation\n if (path[0] != token) {\n revert InvalidTokenInSwapPath(path[0]);\n }\n\n if (path[len - 1] != baseTokenAddress) {\n revert InvalidTokenInSwapPath(path[len - 1]);\n }\n }\n\n /**\n * @dev Decodes the data passed into Uniswap V3 path and validates\n * it to make sure the path is for `token` to `baseToken`\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param token The address of the reward token\n * @return path The validated Uniswap V3 path\n */\n function _decodeUniswapV3Path(bytes calldata data, address token)\n internal\n view\n returns (bytes calldata path)\n {\n path = data;\n\n address decodedAddress = address(uint160(bytes20(data[0:20])));\n\n if (decodedAddress != token) {\n // Invalid Reward Token in swap path\n revert InvalidTokenInSwapPath(decodedAddress);\n }\n\n decodedAddress = address(uint160(bytes20(data[path.length - 20:])));\n if (decodedAddress != baseTokenAddress) {\n // Invalid Base Token in swap path\n revert InvalidTokenInSwapPath(decodedAddress);\n }\n }\n\n /**\n * @dev Decodes the data passed to Balancer Pool ID\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @return poolId The pool ID\n */\n function _decodeBalancerPoolId(\n bytes calldata data,\n address balancerVault,\n address token\n ) internal view returns (bytes32 poolId) {\n (poolId) = abi.decode(data, (bytes32));\n\n if (poolId == bytes32(0)) {\n revert EmptyBalancerPoolId();\n }\n\n IBalancerVault bVault = IBalancerVault(balancerVault);\n\n // Note: this reverts if token is not a pool asset\n // slither-disable-next-line unused-return\n bVault.getPoolTokenInfo(poolId, token);\n\n // slither-disable-next-line unused-return\n bVault.getPoolTokenInfo(poolId, baseTokenAddress);\n }\n\n /**\n * @dev Decodes the data passed to get the pool indices and\n * checks it against the Curve Pool to make sure it's\n * not misconfigured. The indices are packed into a single\n * uint256 for gas savings\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param poolAddress Curve pool address\n * @param token The address of the reward token\n * @return indices Packed pool asset indices\n */\n function _decodeCurvePoolIndices(\n bytes calldata data,\n address poolAddress,\n address token\n ) internal view returns (CurvePoolIndices memory indices) {\n indices = abi.decode(data, (CurvePoolIndices));\n\n ICurvePool pool = ICurvePool(poolAddress);\n if (token != pool.coins(indices.rewardTokenIndex)) {\n revert InvalidCurvePoolAssetIndex(token);\n }\n if (baseTokenAddress != pool.coins(indices.baseTokenIndex)) {\n revert InvalidCurvePoolAssetIndex(baseTokenAddress);\n }\n }\n\n /**\n * @dev Flags a strategy as supported or not supported one\n * @param _strategyAddress Address of the strategy\n * @param _isSupported Bool marking strategy as supported or not supported\n */\n function setSupportedStrategy(address _strategyAddress, bool _isSupported)\n external\n onlyGovernor\n {\n supportedStrategies[_strategyAddress] = _isSupported;\n emit SupportedStrategyUpdate(_strategyAddress, _isSupported);\n }\n\n /***************************************\n Rewards\n ****************************************/\n\n /**\n * @dev Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform. Can be called by anyone.\n * Rewards incentivizing the caller are sent to the caller of this function.\n * @param _strategyAddr Address of the strategy to collect rewards from\n */\n function harvestAndSwap(address _strategyAddr) external nonReentrant {\n // Remember _harvest function checks for the validity of _strategyAddr\n _harvestAndSwap(_strategyAddr, msg.sender);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform. Can be called by anyone\n * @param _strategyAddr Address of the strategy to collect rewards from\n * @param _rewardTo Address where to send a share of harvest rewards to as an incentive\n * for executing this function\n */\n function harvestAndSwap(address _strategyAddr, address _rewardTo)\n external\n nonReentrant\n {\n // Remember _harvest function checks for the validity of _strategyAddr\n _harvestAndSwap(_strategyAddr, _rewardTo);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform\n * @param _strategyAddr Address of the strategy to collect rewards from\n * @param _rewardTo Address where to send a share of harvest rewards to as an incentive\n * for executing this function\n */\n function _harvestAndSwap(address _strategyAddr, address _rewardTo)\n internal\n {\n _harvest(_strategyAddr);\n IStrategy strategy = IStrategy(_strategyAddr);\n address[] memory rewardTokens = strategy.getRewardTokenAddresses();\n IOracle priceProvider = IOracle(IVault(vaultAddress).priceProvider());\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; ++i) {\n _swap(rewardTokens[i], _rewardTo, priceProvider);\n }\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform\n * @param _strategyAddr Address of the strategy to collect rewards from.\n */\n function _harvest(address _strategyAddr) internal virtual {\n if (!supportedStrategies[_strategyAddr]) {\n revert UnsupportedStrategy(_strategyAddr);\n }\n\n IStrategy strategy = IStrategy(_strategyAddr);\n strategy.collectRewardTokens();\n }\n\n /**\n * @dev Swap a reward token for the base token on the configured\n * swap platform. The token must have a registered price feed\n * with the price provider\n * @param _swapToken Address of the token to swap\n * @param _rewardTo Address where to send the share of harvest rewards to\n * @param _priceProvider Oracle to get prices of the swap token\n */\n function _swap(\n address _swapToken,\n address _rewardTo,\n IOracle _priceProvider\n ) internal virtual {\n uint256 balance = IERC20(_swapToken).balanceOf(address(this));\n\n // No need to swap if the reward token is the base token. eg USDT or WETH.\n // There is also no limit on the transfer. Everything in the harvester will be transferred\n // to the Dripper regardless of the liquidationLimit config.\n if (_swapToken == baseTokenAddress) {\n IERC20(_swapToken).safeTransfer(rewardProceedsAddress, balance);\n // currently not paying the farmer any rewards as there is no swap\n emit RewardProceedsTransferred(\n baseTokenAddress,\n address(0),\n balance,\n 0\n );\n return;\n }\n\n RewardTokenConfig memory tokenConfig = rewardTokenConfigs[_swapToken];\n\n /* This will trigger a return when reward token configuration has not yet been set\n * or we have temporarily disabled swapping of specific reward token via setting\n * doSwapRewardToken to false.\n */\n if (!tokenConfig.doSwapRewardToken) {\n return;\n }\n\n if (balance == 0) {\n return;\n }\n\n if (tokenConfig.liquidationLimit > 0) {\n balance = Math.min(balance, tokenConfig.liquidationLimit);\n }\n\n // This'll revert if there is no price feed\n uint256 oraclePrice = _priceProvider.price(_swapToken);\n\n // Oracle price is 1e18\n uint256 minExpected = (balance *\n (1e4 - tokenConfig.allowedSlippageBps) * // max allowed slippage\n oraclePrice).scaleBy(\n baseTokenDecimals,\n Helpers.getDecimals(_swapToken)\n ) /\n 1e4 / // fix the max slippage decimal position\n 1e18; // and oracle price decimals position\n\n // Do the swap\n uint256 amountReceived = _doSwap(\n tokenConfig.swapPlatform,\n tokenConfig.swapPlatformAddr,\n _swapToken,\n balance,\n minExpected\n );\n\n if (amountReceived < minExpected) {\n revert SlippageError(amountReceived, minExpected);\n }\n\n emit RewardTokenSwapped(\n _swapToken,\n baseTokenAddress,\n tokenConfig.swapPlatform,\n balance,\n amountReceived\n );\n\n IERC20 baseToken = IERC20(baseTokenAddress);\n uint256 baseTokenBalance = baseToken.balanceOf(address(this));\n if (baseTokenBalance < amountReceived) {\n // Note: It's possible to bypass this check by transferring `baseToken`\n // directly to Harvester before calling the `harvestAndSwap`. However,\n // there's no incentive for an attacker to do that. Doing a balance diff\n // will increase the gas cost significantly\n revert BalanceMismatchAfterSwap(baseTokenBalance, amountReceived);\n }\n\n // Farmer only gets fee from the base amount they helped farm,\n // They do not get anything from anything that already was there\n // on the Harvester\n uint256 farmerFee = amountReceived.mulTruncateScale(\n tokenConfig.harvestRewardBps,\n 1e4\n );\n uint256 protocolYield = baseTokenBalance - farmerFee;\n\n baseToken.safeTransfer(rewardProceedsAddress, protocolYield);\n baseToken.safeTransfer(_rewardTo, farmerFee);\n emit RewardProceedsTransferred(\n baseTokenAddress,\n _rewardTo,\n protocolYield,\n farmerFee\n );\n }\n\n function _doSwap(\n SwapPlatform swapPlatform,\n address routerAddress,\n address rewardTokenAddress,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n if (swapPlatform == SwapPlatform.UniswapV2Compatible) {\n return\n _swapWithUniswapV2(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.UniswapV3) {\n return\n _swapWithUniswapV3(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.Balancer) {\n return\n _swapWithBalancer(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.Curve) {\n return\n _swapWithCurve(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else {\n // Should never be invoked since we catch invalid values\n // in the `setRewardTokenConfig` function before it's set\n revert InvalidSwapPlatform(swapPlatform);\n }\n }\n\n /**\n * @dev Swaps the token to `baseToken` with Uniswap V2\n *\n * @param routerAddress Uniswap V2 Router address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithUniswapV2(\n address routerAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n address[] memory path = uniswapV2Path[swapToken];\n\n uint256[] memory amounts = IUniswapV2Router(routerAddress)\n .swapExactTokensForTokens(\n amountIn,\n minAmountOut,\n path,\n address(this),\n block.timestamp\n );\n\n amountOut = amounts[amounts.length - 1];\n }\n\n /**\n * @dev Swaps the token to `baseToken` with Uniswap V3\n *\n * @param routerAddress Uniswap V3 Router address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithUniswapV3(\n address routerAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n bytes memory path = uniswapV3Path[swapToken];\n\n IUniswapV3Router.ExactInputParams memory params = IUniswapV3Router\n .ExactInputParams({\n path: path,\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: amountIn,\n amountOutMinimum: minAmountOut\n });\n amountOut = IUniswapV3Router(routerAddress).exactInput(params);\n }\n\n /**\n * @dev Swaps the token to `baseToken` on Balancer\n *\n * @param balancerVaultAddress BalancerVaultAddress\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithBalancer(\n address balancerVaultAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n bytes32 poolId = balancerPoolId[swapToken];\n\n IBalancerVault.SingleSwap memory singleSwap = IBalancerVault\n .SingleSwap({\n poolId: poolId,\n kind: IBalancerVault.SwapKind.GIVEN_IN,\n assetIn: swapToken,\n assetOut: baseTokenAddress,\n amount: amountIn,\n userData: hex\"\"\n });\n\n IBalancerVault.FundManagement memory fundMgmt = IBalancerVault\n .FundManagement({\n sender: address(this),\n fromInternalBalance: false,\n recipient: payable(address(this)),\n toInternalBalance: false\n });\n\n amountOut = IBalancerVault(balancerVaultAddress).swap(\n singleSwap,\n fundMgmt,\n minAmountOut,\n block.timestamp\n );\n }\n\n /**\n * @dev Swaps the token to `baseToken` on Curve\n *\n * @param poolAddress Curve Pool Address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithCurve(\n address poolAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n CurvePoolIndices memory indices = curvePoolIndices[swapToken];\n\n // Note: Not all CurvePools return the `amountOut`, make sure\n // to use only pool that do. Otherwise the swap would revert\n // always\n amountOut = ICurvePool(poolAddress).exchange(\n uint256(indices.rewardTokenIndex),\n uint256(indices.baseTokenIndex),\n amountIn,\n minAmountOut\n );\n }\n}\n" + }, + "contracts/harvest/Dripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\n/**\n * @title OUSD Dripper\n *\n * The dripper contract smooths out the yield from point-in-time yield events\n * and spreads the yield out over a configurable time period. This ensures a\n * continuous per block yield to makes users happy as their next rebase\n * amount is always moving up. Also, this makes historical day to day yields\n * smooth, rather than going from a near zero day, to a large APY day, then\n * back to a near zero day again.\n *\n *\n * Design notes\n * - USDT has a smaller resolution than the number of seconds\n * in a week, which can make per second payouts have a rounding error. However\n * the total effect is not large - cents per day, and this money is\n * not lost, just distributed in the future. While we could use a higher\n * decimal precision for the drip perSecond, we chose simpler code.\n * - By calculating the changing drip rates on collects only, harvests and yield\n * events don't have to call anything on this contract or pay any extra gas.\n * Collect() is already be paying for a single write, since it has to reset\n * the lastCollect time.\n * - By having a collectAndRebase method, and having our external systems call\n * that, the OUSD vault does not need any changes, not even to know the address\n * of the dripper.\n * - A rejected design was to retro-calculate the drip rate on each collect,\n * based on the balance at the time of the collect. While this would have\n * required less state, and would also have made the contract respond more quickly\n * to new income, it would break the predictability that is this contract's entire\n * purpose. If we did this, the amount of fundsAvailable() would make sharp increases\n * when funds were deposited.\n * - When the dripper recalculates the rate, it targets spending the balance over\n * the duration. This means that every time that collect is called, if no\n * new funds have been deposited the duration is being pushed back and the\n * rate decreases. This is expected, and ends up following a smoother but\n * longer curve the more collect() is called without incoming yield.\n *\n */\n\ncontract Dripper is Governable {\n using SafeERC20 for IERC20;\n\n struct Drip {\n uint64 lastCollect; // overflows 262 billion years after the sun dies\n uint192 perSecond; // drip rate per second\n }\n\n address immutable vault; // OUSD vault\n address immutable token; // token to drip out\n uint256 public dripDuration; // in seconds\n Drip public drip; // active drip parameters\n\n constructor(address _vault, address _token) {\n vault = _vault;\n token = _token;\n }\n\n /// @notice How much funds have dripped out already and are currently\n // available to be sent to the vault.\n /// @return The amount that would be sent if a collect was called\n function availableFunds() external view returns (uint256) {\n uint256 balance = IERC20(token).balanceOf(address(this));\n return _availableFunds(balance, drip);\n }\n\n /// @notice Collect all dripped funds and send to vault.\n /// Recalculate new drip rate.\n function collect() external {\n _collect();\n }\n\n /// @notice Collect all dripped funds, send to vault, recalculate new drip\n /// rate, and rebase OUSD.\n function collectAndRebase() external {\n _collect();\n IVault(vault).rebase();\n }\n\n /// @dev Change the drip duration. Governor only.\n /// @param _durationSeconds the number of seconds to drip out the entire\n /// balance over if no collects were called during that time.\n function setDripDuration(uint256 _durationSeconds)\n external\n virtual\n onlyGovernor\n {\n require(_durationSeconds > 0, \"duration must be non-zero\");\n dripDuration = _durationSeconds;\n _collect(); // duration change take immediate effect\n }\n\n /// @dev Transfer out ERC20 tokens held by the contract. Governor only.\n /// @param _asset ERC20 token address\n /// @param _amount amount to transfer\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /// @dev Calculate available funds by taking the lower of either the\n /// currently dripped out funds or the balance available.\n /// Uses passed in parameters to calculate with for gas savings.\n /// @param _balance current balance in contract\n /// @param _drip current drip parameters\n function _availableFunds(uint256 _balance, Drip memory _drip)\n internal\n view\n returns (uint256)\n {\n uint256 elapsed = block.timestamp - _drip.lastCollect;\n uint256 allowed = (elapsed * _drip.perSecond);\n return (allowed > _balance) ? _balance : allowed;\n }\n\n /// @dev Sends the currently dripped funds to be vault, and sets\n /// the new drip rate based on the new balance.\n function _collect() internal virtual {\n // Calculate send\n uint256 balance = IERC20(token).balanceOf(address(this));\n uint256 amountToSend = _availableFunds(balance, drip);\n uint256 remaining = balance - amountToSend;\n // Calculate new drip perSecond\n // Gas savings by setting entire struct at one time\n drip = Drip({\n perSecond: uint192(remaining / dripDuration),\n lastCollect: uint64(block.timestamp)\n });\n // Send funds\n IERC20(token).safeTransfer(vault, amountToSend);\n }\n\n /// @dev Transfer out all ERC20 held by the contract. Governor only.\n /// @param _asset ERC20 token address\n function transferAllToken(address _asset, address _receiver)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(\n _receiver,\n IERC20(_asset).balanceOf(address(this))\n );\n }\n}\n" + }, + "contracts/harvest/FixedRateDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Dripper } from \"./Dripper.sol\";\n\n/**\n * @title Fixed Rate Dripper\n *\n * Similar to the Dripper, Fixed Rate Dripper drips out yield per second.\n * However the Strategist decides the rate and it doesn't change after\n * a drip.\n *\n */\n\ncontract FixedRateDripper is Dripper {\n using SafeERC20 for IERC20;\n\n event DripRateUpdated(uint192 oldDripRate, uint192 newDripRate);\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vault).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n constructor(address _vault, address _token) Dripper(_vault, _token) {}\n\n /// @inheritdoc Dripper\n function setDripDuration(uint256) external virtual override {\n // Not used in FixedRateDripper\n revert(\"Drip duration disabled\");\n }\n\n /// @inheritdoc Dripper\n function _collect() internal virtual override {\n // Calculate amount to send\n uint256 balance = IERC20(token).balanceOf(address(this));\n uint256 amountToSend = _availableFunds(balance, drip);\n\n // Update timestamp\n drip.lastCollect = uint64(block.timestamp);\n\n // Send funds\n IERC20(token).safeTransfer(vault, amountToSend);\n }\n\n /**\n * @dev Sets the drip rate. Callable by Strategist or Governor.\n * Can be set to zero to stop dripper.\n * @param _perSecond Rate of WETH to drip per second\n */\n function setDripRate(uint192 _perSecond) external onlyGovernorOrStrategist {\n emit DripRateUpdated(_perSecond, drip.perSecond);\n\n /**\n * Note: It's important to call `_collect` before updating\n * the drip rate especially on a new proxy contract.\n * When `lastCollect` is not set/initialized, the elapsed\n * time would be calculated as `block.number` seconds,\n * resulting in a huge yield, if `collect` isn't called first.\n */\n // Collect at existing rate\n _collect();\n\n // Update rate\n drip.perSecond = _perSecond;\n }\n}\n" + }, + "contracts/harvest/Harvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractHarvester } from \"./AbstractHarvester.sol\";\n\ncontract Harvester is AbstractHarvester {\n constructor(address _vault, address _usdtAddress)\n AbstractHarvester(_vault, _usdtAddress)\n {}\n}\n" + }, + "contracts/harvest/OETHBaseHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { ISwapRouter } from \"../interfaces/aerodrome/ISwapRouter.sol\";\n\ncontract OETHBaseHarvester is Governable {\n using SafeERC20 for IERC20;\n\n IVault public immutable vault;\n IStrategy public immutable amoStrategy;\n IERC20 public immutable aero;\n IERC20 public immutable weth;\n ISwapRouter public immutable swapRouter;\n\n address public operatorAddr;\n\n // Similar sig to `AbstractHarvester.RewardTokenSwapped` for\n // future compatibility with monitoring\n event RewardTokenSwapped(\n address indexed rewardToken,\n address indexed swappedInto,\n uint8 swapPlatform,\n uint256 amountIn,\n uint256 amountOut\n );\n\n event OperatorChanged(address oldOperator, address newOperator);\n event YieldSent(address recipient, uint256 yield, uint256 fee);\n\n /**\n * @notice Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == vault.strategistAddr() || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @notice Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategistOrOperator() {\n require(\n msg.sender == operatorAddr ||\n msg.sender == vault.strategistAddr() ||\n isGovernor(),\n \"Caller is not the Operator or Strategist or Governor\"\n );\n _;\n }\n\n constructor(\n address _vault,\n address _amoStrategy,\n address _aero,\n address _weth,\n address _swapRouter\n ) {\n vault = IVault(_vault);\n amoStrategy = IStrategy(_amoStrategy);\n aero = IERC20(_aero);\n weth = IERC20(_weth);\n swapRouter = ISwapRouter(_swapRouter);\n }\n\n /**\n * @dev Changes the operator address which can call `harvest`\n * @param _operatorAddr New operator address\n */\n function setOperatorAddr(address _operatorAddr) external onlyGovernor {\n emit OperatorChanged(operatorAddr, _operatorAddr);\n operatorAddr = _operatorAddr;\n }\n\n /**\n * @notice Collects AERO from AMO strategy and\n * sends it to the Strategist multisig.\n * Anyone can call it.\n */\n function harvest() external onlyGovernorOrStrategistOrOperator {\n address strategistAddr = vault.strategistAddr();\n require(strategistAddr != address(0), \"Guardian address not set\");\n\n // Collect all AERO\n amoStrategy.collectRewardTokens();\n\n uint256 aeroBalance = aero.balanceOf(address(this));\n if (aeroBalance == 0) {\n // Do nothing if there's no AERO to transfer\n return;\n }\n\n // Transfer everything to Strategist\n aero.safeTransfer(strategistAddr, aeroBalance);\n }\n\n /**\n * @notice Harvests AERO from AMO strategy and then swaps some (or all)\n * of it into WETH to distribute yield and fee.\n * When `feeBps` is set to 10000 (100%), all WETH received is\n * sent to strategist.\n *\n * @param aeroToSwap Amount of AERO to swap\n * @param minWETHExpected Min. amount of WETH to expect\n * @param feeBps Performance fee bps (Sent to strategist)\n * @param sendYieldToDripper Sends yield to Dripper, if set to true.\n * Otherwise, to the Guardian\n */\n function harvestAndSwap(\n uint256 aeroToSwap,\n uint256 minWETHExpected,\n uint256 feeBps,\n bool sendYieldToDripper\n ) external onlyGovernorOrStrategist {\n address strategistAddr = vault.strategistAddr();\n require(strategistAddr != address(0), \"Guardian address not set\");\n\n // Yields can either be sent to the Dripper or Strategist\n address yieldRecipient = sendYieldToDripper\n ? vault.dripper()\n : strategistAddr;\n require(yieldRecipient != address(0), \"Yield recipient not set\");\n\n require(feeBps <= 10000, \"Invalid Fee Bps\");\n\n // Collect all AERO\n amoStrategy.collectRewardTokens();\n\n uint256 aeroBalance = aero.balanceOf(address(this));\n if (aeroBalance == 0) {\n // Do nothing if there's no AERO to transfer/swap\n return;\n }\n\n if (aeroToSwap > 0) {\n if (aeroBalance < aeroToSwap) {\n // Transfer in balance from the multisig as needed\n // slither-disable-next-line unchecked-transfer arbitrary-send-erc20\n aero.safeTransferFrom(\n strategistAddr,\n address(this),\n aeroToSwap - aeroBalance\n );\n }\n\n _doSwap(aeroToSwap, minWETHExpected);\n\n // Figure out AERO left in contract after swap\n aeroBalance = aero.balanceOf(address(this));\n }\n\n // Transfer out any leftover AERO after swap\n if (aeroBalance > 0) {\n aero.safeTransfer(strategistAddr, aeroBalance);\n }\n\n // Computes using all balance the contract holds,\n // not just the WETH received from swap. Use `transferToken`\n // if there's any WETH left that needs to be taken out\n uint256 availableWETHBalance = weth.balanceOf(address(this));\n // Computation rounds in favor of protocol\n uint256 fee = (availableWETHBalance * feeBps) / 10000;\n uint256 yield = availableWETHBalance - fee;\n\n // Transfer yield, if any\n if (yield > 0) {\n weth.safeTransfer(yieldRecipient, yield);\n }\n\n // Transfer fee to the Guardian, if any\n if (fee > 0) {\n weth.safeTransfer(strategistAddr, fee);\n }\n\n emit YieldSent(yieldRecipient, yield, fee);\n }\n\n /**\n * @notice Swaps AERO to WETH on Aerodrome\n * @param aeroToSwap Amount of AERO to swap\n * @param minWETHExpected Min. amount of WETH to expect\n */\n function _doSwap(uint256 aeroToSwap, uint256 minWETHExpected) internal {\n // Let the swap router move funds\n aero.approve(address(swapRouter), aeroToSwap);\n\n // Do the swap\n uint256 wethReceived = swapRouter.exactInputSingle(\n ISwapRouter.ExactInputSingleParams({\n tokenIn: address(aero),\n tokenOut: address(weth),\n tickSpacing: 200, // From AERO/WETH pool contract\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: aeroToSwap,\n amountOutMinimum: minWETHExpected,\n sqrtPriceLimitX96: 0\n })\n );\n\n emit RewardTokenSwapped(\n address(aero),\n address(weth),\n 0,\n aeroToSwap,\n wethReceived\n );\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * the contract, i.e. mistaken sends.\n * Also, allows to transfer any AERO left in the contract.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n virtual\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n}\n" + }, + "contracts/harvest/OETHDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Dripper } from \"./Dripper.sol\";\n\n/**\n * @title OETH Dripper Contract\n * @author Origin Protocol Inc\n */\ncontract OETHDripper is Dripper {\n constructor(address _vault, address _token) Dripper(_vault, _token) {}\n}\n" + }, + "contracts/harvest/OETHFixedRateDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { FixedRateDripper } from \"./FixedRateDripper.sol\";\n\n/**\n * @title OETH FixedRateDripper Contract\n * @author Origin Protocol Inc\n */\ncontract OETHFixedRateDripper is FixedRateDripper {\n constructor(address _vault, address _token)\n FixedRateDripper(_vault, _token)\n {}\n}\n" + }, + "contracts/harvest/OETHHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractHarvester } from \"./AbstractHarvester.sol\";\n\ncontract OETHHarvester is AbstractHarvester {\n constructor(address _vault, address _wethAddress)\n AbstractHarvester(_vault, _wethAddress)\n {}\n}\n" + }, + "contracts/interfaces/aerodrome/ICLGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\ninterface ICLGauge {\n /// @notice Returns the claimable rewards for a given account and tokenId\n /// @dev Throws if account is not the position owner\n /// @dev pool.updateRewardsGrowthGlobal() needs to be called first, to return the correct claimable rewards\n /// @param account The address of the user\n /// @param tokenId The tokenId of the position\n /// @return The amount of claimable reward\n function earned(address account, uint256 tokenId)\n external\n view\n returns (uint256);\n\n /// @notice Retrieve rewards for all tokens owned by an account\n /// @dev Throws if not called by the voter\n /// @param account The account of the user\n function getReward(address account) external;\n\n /// @notice Retrieve rewards for a tokenId\n /// @dev Throws if not called by the position owner\n /// @param tokenId The tokenId of the position\n function getReward(uint256 tokenId) external;\n\n /// @notice Notifies gauge of gauge rewards.\n /// @param amount Amount of gauge rewards (emissions) to notify. Must be greater than 604_800.\n function notifyRewardAmount(uint256 amount) external;\n\n /// @dev Notifies gauge of gauge rewards without distributing its fees.\n /// Assumes gauge reward tokens is 18 decimals.\n /// If not 18 decimals, rewardRate may have rounding issues.\n /// @param amount Amount of gauge rewards (emissions) to notify. Must be greater than 604_800.\n function notifyRewardWithoutClaim(uint256 amount) external;\n\n /// @notice Used to deposit a CL position into the gauge\n /// @notice Allows the user to receive emissions instead of fees\n /// @param tokenId The tokenId of the position\n function deposit(uint256 tokenId) external;\n\n /// @notice Used to withdraw a CL position from the gauge\n /// @notice Allows the user to receive fees instead of emissions\n /// @notice Outstanding emissions will be collected on withdrawal\n /// @param tokenId The tokenId of the position\n function withdraw(uint256 tokenId) external;\n\n // /// @notice Fetch all tokenIds staked by a given account\n // /// @param depositor The address of the user\n // /// @return The tokenIds of the staked positions\n // function stakedValues(address depositor) external view returns (uint256[] memory);\n\n // /// @notice Fetch a staked tokenId by index\n // /// @param depositor The address of the user\n // /// @param index The index of the staked tokenId\n // /// @return The tokenId of the staked position\n // function stakedByIndex(address depositor, uint256 index) external view returns (uint256);\n\n // /// @notice Check whether a position is staked in the gauge by a certain user\n // /// @param depositor The address of the user\n // /// @param tokenId The tokenId of the position\n // /// @return Whether the position is staked in the gauge\n // function stakedContains(address depositor, uint256 tokenId) external view returns (bool);\n\n // /// @notice The amount of positions staked in the gauge by a certain user\n // /// @param depositor The address of the user\n // /// @return The amount of positions staked in the gauge\n // function stakedLength(address depositor) external view returns (uint256);\n\n function feesVotingReward() external view returns (address);\n}\n" + }, + "contracts/interfaces/aerodrome/ICLPool.sol": { + "content": "pragma solidity >=0.5.0;\n\n/// @title The interface for a CL Pool\n/// @notice A CL pool facilitates swapping and automated market making between any two assets that strictly conform\n/// to the ERC20 specification\n/// @dev The pool interface is broken up into many smaller pieces\ninterface ICLPool {\n function slot0()\n external\n view\n returns (\n uint160 sqrtPriceX96,\n int24 tick,\n uint16 observationIndex,\n uint16 observationCardinality,\n uint16 observationCardinalityNext,\n bool unlocked\n );\n\n /// @notice The first of the two tokens of the pool, sorted by address\n /// @return The token contract address\n function token0() external view returns (address);\n\n /// @notice The second of the two tokens of the pool, sorted by address\n /// @return The token contract address\n function token1() external view returns (address);\n\n function tickSpacing() external view returns (int24);\n\n /// @notice The gauge corresponding to this pool\n /// @return The gauge contract address\n function gauge() external view returns (address);\n\n /// @notice The currently in range liquidity available to the pool\n /// @dev This value has no relationship to the total liquidity across all ticks\n /// @dev This value includes staked liquidity\n function liquidity() external view returns (uint128);\n\n /// @notice Look up information about a specific tick in the pool\n /// @param tick The tick to look up\n /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or\n /// tick upper,\n /// liquidityNet how much liquidity changes when the pool price crosses the tick,\n /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,\n /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,\n /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick\n /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from\n /// the current tick,\n /// secondsOutside the seconds spent on the other side of the tick from the current tick,\n /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise\n /// equal to false.\n /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.\n /// In addition, these values are only relative and must be used only in comparison to previous snapshots for\n /// a specific position.\n function ticks(int24 tick)\n external\n view\n returns (\n uint128 liquidityGross,\n int128 liquidityNet,\n uint256 feeGrowthOutside0X128,\n uint256 feeGrowthOutside1X128,\n int56 tickCumulativeOutside,\n uint160 secondsPerLiquidityOutsideX128,\n uint32 secondsOutside,\n bool initialized\n );\n}\n" + }, + "contracts/interfaces/aerodrome/INonfungiblePositionManager.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title Non-fungible token for positions\n/// @notice Wraps CL positions in a non-fungible token interface which allows for them to be transferred\n/// and authorized.\n// slither-disable-start erc20-interface\ninterface INonfungiblePositionManager {\n /**\n * @dev See {IERC721-approve}.\n */\n function approve(address to, uint256 tokenId) external;\n\n /**\n * @dev See {IERC721-getApproved}.\n */\n function getApproved(uint256 tokenId) external returns (address);\n\n /**\n * @dev See {IERC721-ownerOf}.\n */\n function ownerOf(uint256 tokenId) external view returns (address);\n\n /// @notice Returns the position information associated with a given token ID.\n /// @dev Throws if the token ID is not valid.\n /// @param tokenId The ID of the token that represents the position\n /// @return nonce The nonce for permits\n /// @return operator The address that is approved for spending\n /// @return token0 The address of the token0 for a specific pool\n /// @return token1 The address of the token1 for a specific pool\n /// @return tickSpacing The tick spacing associated with the pool\n /// @return tickLower The lower end of the tick range for the position\n /// @return tickUpper The higher end of the tick range for the position\n /// @return liquidity The liquidity of the position\n /// @return feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position\n /// @return feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position\n /// @return tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation\n /// @return tokensOwed1 The uncollected amount of token1 owed to the position as of the last computation\n function positions(uint256 tokenId)\n external\n view\n returns (\n uint96 nonce,\n address operator,\n address token0,\n address token1,\n int24 tickSpacing,\n int24 tickLower,\n int24 tickUpper,\n uint128 liquidity,\n uint256 feeGrowthInside0LastX128,\n uint256 feeGrowthInside1LastX128,\n uint128 tokensOwed0,\n uint128 tokensOwed1\n );\n\n struct MintParams {\n address token0;\n address token1;\n int24 tickSpacing;\n int24 tickLower;\n int24 tickUpper;\n uint256 amount0Desired;\n uint256 amount1Desired;\n uint256 amount0Min;\n uint256 amount1Min;\n address recipient;\n uint256 deadline;\n uint160 sqrtPriceX96;\n }\n\n /// @notice Creates a new position wrapped in a NFT\n /// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized\n /// a method does not exist, i.e. the pool is assumed to be initialized.\n /// @param params The params necessary to mint a position, encoded as `MintParams` in calldata\n /// @return tokenId The ID of the token that represents the minted position\n /// @return liquidity The amount of liquidity for this position\n /// @return amount0 The amount of token0\n /// @return amount1 The amount of token1\n function mint(MintParams calldata params)\n external\n payable\n returns (\n uint256 tokenId,\n uint128 liquidity,\n uint256 amount0,\n uint256 amount1\n );\n\n struct IncreaseLiquidityParams {\n uint256 tokenId;\n uint256 amount0Desired;\n uint256 amount1Desired;\n uint256 amount0Min;\n uint256 amount1Min;\n uint256 deadline;\n }\n\n /// @notice Increases the amount of liquidity in a position, with tokens paid by the `msg.sender`\n /// @param params tokenId The ID of the token for which liquidity is being increased,\n /// amount0Desired The desired amount of token0 to be spent,\n /// amount1Desired The desired amount of token1 to be spent,\n /// amount0Min The minimum amount of token0 to spend, which serves as a slippage check,\n /// amount1Min The minimum amount of token1 to spend, which serves as a slippage check,\n /// deadline The time by which the transaction must be included to effect the change\n /// @return liquidity The new liquidity amount as a result of the increase\n /// @return amount0 The amount of token0 to acheive resulting liquidity\n /// @return amount1 The amount of token1 to acheive resulting liquidity\n function increaseLiquidity(IncreaseLiquidityParams calldata params)\n external\n payable\n returns (\n uint128 liquidity,\n uint256 amount0,\n uint256 amount1\n );\n\n struct DecreaseLiquidityParams {\n uint256 tokenId;\n uint128 liquidity;\n uint256 amount0Min;\n uint256 amount1Min;\n uint256 deadline;\n }\n\n /// @notice Decreases the amount of liquidity in a position and accounts it to the position\n /// @param params tokenId The ID of the token for which liquidity is being decreased,\n /// amount The amount by which liquidity will be decreased,\n /// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity,\n /// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity,\n /// deadline The time by which the transaction must be included to effect the change\n /// @return amount0 The amount of token0 accounted to the position's tokens owed\n /// @return amount1 The amount of token1 accounted to the position's tokens owed\n /// @dev The use of this function can cause a loss to users of the NonfungiblePositionManager\n /// @dev for tokens that have very high decimals.\n /// @dev The amount of tokens necessary for the loss is: 3.4028237e+38.\n /// @dev This is equivalent to 1e20 value with 18 decimals.\n function decreaseLiquidity(DecreaseLiquidityParams calldata params)\n external\n payable\n returns (uint256 amount0, uint256 amount1);\n\n struct CollectParams {\n uint256 tokenId;\n address recipient;\n uint128 amount0Max;\n uint128 amount1Max;\n }\n\n /// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient\n /// @notice Used to update staked positions before deposit and withdraw\n /// @param params tokenId The ID of the NFT for which tokens are being collected,\n /// recipient The account that should receive the tokens,\n /// amount0Max The maximum amount of token0 to collect,\n /// amount1Max The maximum amount of token1 to collect\n /// @return amount0 The amount of fees collected in token0\n /// @return amount1 The amount of fees collected in token1\n function collect(CollectParams calldata params)\n external\n payable\n returns (uint256 amount0, uint256 amount1);\n\n /// @notice Burns a token ID, which deletes it from the NFT contract. The token must have 0 liquidity and all tokens\n /// must be collected first.\n /// @param tokenId The ID of the token that is being burned\n function burn(uint256 tokenId) external payable;\n\n /// @notice Sets a new Token Descriptor\n /// @param _tokenDescriptor Address of the new Token Descriptor to be chosen\n function setTokenDescriptor(address _tokenDescriptor) external;\n\n /// @notice Sets a new Owner address\n /// @param _owner Address of the new Owner to be chosen\n function setOwner(address _owner) external;\n}\n// slither-disable-end erc20-interface\n" + }, + "contracts/interfaces/aerodrome/ISugarHelper.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\npragma abicoder v2;\n\nimport { INonfungiblePositionManager } from \"./INonfungiblePositionManager.sol\";\n\ninterface ISugarHelper {\n struct PopulatedTick {\n int24 tick;\n uint160 sqrtRatioX96;\n int128 liquidityNet;\n uint128 liquidityGross;\n }\n\n ///\n /// Wrappers for LiquidityAmounts\n ///\n\n function getAmountsForLiquidity(\n uint160 sqrtRatioX96,\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96,\n uint128 liquidity\n ) external pure returns (uint256 amount0, uint256 amount1);\n\n function getLiquidityForAmounts(\n uint256 amount0,\n uint256 amount1,\n uint160 sqrtRatioX96,\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96\n ) external pure returns (uint128 liquidity);\n\n /// @notice Computes the amount of token0 for a given amount of token1 and price range\n /// @param amount1 Amount of token1 to estimate liquidity\n /// @param pool Address of the pool to be used\n /// @param sqrtRatioX96 A sqrt price representing the current pool prices\n /// @param tickLow Lower tick boundary\n /// @param tickLow Upper tick boundary\n /// @dev If the given pool address is not the zero address, will fetch `sqrtRatioX96` from pool\n /// @return amount0 Estimated amount of token0\n function estimateAmount0(\n uint256 amount1,\n address pool,\n uint160 sqrtRatioX96,\n int24 tickLow,\n int24 tickHigh\n ) external view returns (uint256 amount0);\n\n /// @notice Computes the amount of token1 for a given amount of token0 and price range\n /// @param amount0 Amount of token0 to estimate liquidity\n /// @param pool Address of the pool to be used\n /// @param sqrtRatioX96 A sqrt price representing the current pool prices\n /// @param tickLow Lower tick boundary\n /// @param tickLow Upper tick boundary\n /// @dev If the given pool address is not the zero address, will fetch `sqrtRatioX96` from pool\n /// @return amount1 Estimated amount of token1\n function estimateAmount1(\n uint256 amount0,\n address pool,\n uint160 sqrtRatioX96,\n int24 tickLow,\n int24 tickHigh\n ) external view returns (uint256 amount1);\n\n ///\n /// Wrappers for PositionValue\n ///\n\n function principal(\n INonfungiblePositionManager positionManager,\n uint256 tokenId,\n uint160 sqrtRatioX96\n ) external view returns (uint256 amount0, uint256 amount1);\n\n function fees(INonfungiblePositionManager positionManager, uint256 tokenId)\n external\n view\n returns (uint256 amount0, uint256 amount1);\n\n ///\n /// Wrappers for TickMath\n ///\n\n function getSqrtRatioAtTick(int24 tick)\n external\n pure\n returns (uint160 sqrtRatioX96);\n\n function getTickAtSqrtRatio(uint160 sqrtRatioX96)\n external\n pure\n returns (int24 tick);\n\n /// @notice Fetches Tick Data for all populated Ticks in given bitmaps\n /// @param pool Address of the pool from which to fetch data\n /// @param startTick Tick from which the first bitmap will be fetched\n /// @dev The number of bitmaps fetched by this function should always be `MAX_BITMAPS`,\n /// unless there are less than `MAX_BITMAPS` left to iterate through\n /// @return populatedTicks Array of all Populated Ticks in the provided bitmaps\n function getPopulatedTicks(address pool, int24 startTick)\n external\n view\n returns (PopulatedTick[] memory populatedTicks);\n}\n" + }, + "contracts/interfaces/aerodrome/ISwapRouter.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title Router token swapping functionality\n/// @notice Functions for swapping tokens via CL\ninterface ISwapRouter {\n struct ExactInputSingleParams {\n address tokenIn;\n address tokenOut;\n int24 tickSpacing;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInputSingle(ExactInputSingleParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactOutputSingleParams {\n address tokenIn;\n address tokenOut;\n int24 tickSpacing;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutputSingle(ExactOutputSingleParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n\n struct ExactOutputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutput(ExactOutputParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n}\n" + }, + "contracts/interfaces/balancer/IBalancerVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"../../utils/InitializableAbstractStrategy.sol\";\n\ninterface IBalancerVault {\n enum WeightedPoolJoinKind {\n INIT,\n EXACT_TOKENS_IN_FOR_BPT_OUT,\n TOKEN_IN_FOR_EXACT_BPT_OUT,\n ALL_TOKENS_IN_FOR_EXACT_BPT_OUT,\n ADD_TOKEN\n }\n\n enum WeightedPoolExitKind {\n EXACT_BPT_IN_FOR_ONE_TOKEN_OUT,\n EXACT_BPT_IN_FOR_TOKENS_OUT,\n BPT_IN_FOR_EXACT_TOKENS_OUT,\n REMOVE_TOKEN\n }\n\n /**\n * @dev Called by users to join a Pool, which transfers tokens from `sender` into the Pool's balance. This will\n * trigger custom Pool behavior, which will typically grant something in return to `recipient` - often tokenized\n * Pool shares.\n *\n * If the caller is not `sender`, it must be an authorized relayer for them.\n *\n * The `assets` and `maxAmountsIn` arrays must have the same length, and each entry indicates the maximum amount\n * to send for each asset. The amounts to send are decided by the Pool and not the Vault: it just enforces\n * these maximums.\n *\n * If joining a Pool that holds WETH, it is possible to send ETH directly: the Vault will do the wrapping. To enable\n * this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead of the\n * WETH address. Note that it is not possible to combine ETH and WETH in the same join. Any excess ETH will be sent\n * back to the caller (not the sender, which is important for relayers).\n *\n * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when\n * interacting with Pools that register and deregister tokens frequently. If sending ETH however, the array must be\n * sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the final\n * `assets` array might not be sorted. Pools with no registered tokens cannot be joined.\n *\n * If `fromInternalBalance` is true, the caller's Internal Balance will be preferred: ERC20 transfers will only\n * be made for the difference between the requested amount and Internal Balance (if any). Note that ETH cannot be\n * withdrawn from Internal Balance: attempting to do so will trigger a revert.\n *\n * This causes the Vault to call the `IBasePool.onJoinPool` hook on the Pool's contract, where Pools implement\n * their own custom logic. This typically requires additional information from the user (such as the expected number\n * of Pool shares). This can be encoded in the `userData` argument, which is ignored by the Vault and passed\n * directly to the Pool's contract, as is `recipient`.\n *\n * Emits a `PoolBalanceChanged` event.\n */\n function joinPool(\n bytes32 poolId,\n address sender,\n address recipient,\n JoinPoolRequest memory request\n ) external payable;\n\n struct JoinPoolRequest {\n address[] assets;\n uint256[] maxAmountsIn;\n bytes userData;\n bool fromInternalBalance;\n }\n\n /**\n * @dev Called by users to exit a Pool, which transfers tokens from the Pool's balance to `recipient`. This will\n * trigger custom Pool behavior, which will typically ask for something in return from `sender` - often tokenized\n * Pool shares. The amount of tokens that can be withdrawn is limited by the Pool's `cash` balance (see\n * `getPoolTokenInfo`).\n *\n * If the caller is not `sender`, it must be an authorized relayer for them.\n *\n * The `tokens` and `minAmountsOut` arrays must have the same length, and each entry in these indicates the minimum\n * token amount to receive for each token contract. The amounts to send are decided by the Pool and not the Vault:\n * it just enforces these minimums.\n *\n * If exiting a Pool that holds WETH, it is possible to receive ETH directly: the Vault will do the unwrapping. To\n * enable this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead\n * of the WETH address. Note that it is not possible to combine ETH and WETH in the same exit.\n *\n * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when\n * interacting with Pools that register and deregister tokens frequently. If receiving ETH however, the array must\n * be sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the\n * final `assets` array might not be sorted. Pools with no registered tokens cannot be exited.\n *\n * If `toInternalBalance` is true, the tokens will be deposited to `recipient`'s Internal Balance. Otherwise,\n * an ERC20 transfer will be performed. Note that ETH cannot be deposited to Internal Balance: attempting to\n * do so will trigger a revert.\n *\n * `minAmountsOut` is the minimum amount of tokens the user expects to get out of the Pool, for each token in the\n * `tokens` array. This array must match the Pool's registered tokens.\n *\n * This causes the Vault to call the `IBasePool.onExitPool` hook on the Pool's contract, where Pools implement\n * their own custom logic. This typically requires additional information from the user (such as the expected number\n * of Pool shares to return). This can be encoded in the `userData` argument, which is ignored by the Vault and\n * passed directly to the Pool's contract.\n *\n * Emits a `PoolBalanceChanged` event.\n */\n function exitPool(\n bytes32 poolId,\n address sender,\n address payable recipient,\n ExitPoolRequest memory request\n ) external;\n\n struct ExitPoolRequest {\n address[] assets;\n uint256[] minAmountsOut;\n bytes userData;\n bool toInternalBalance;\n }\n\n /**\n * @dev Returns a Pool's registered tokens, the total balance for each, and the latest block when *any* of\n * the tokens' `balances` changed.\n *\n * The order of the `tokens` array is the same order that will be used in `joinPool`, `exitPool`, as well as in all\n * Pool hooks (where applicable). Calls to `registerTokens` and `deregisterTokens` may change this order.\n *\n * If a Pool only registers tokens once, and these are sorted in ascending order, they will be stored in the same\n * order as passed to `registerTokens`.\n *\n * Total balances include both tokens held by the Vault and those withdrawn by the Pool's Asset Managers. These are\n * the amounts used by joins, exits and swaps. For a detailed breakdown of token balances, use `getPoolTokenInfo`\n * instead.\n */\n function getPoolTokens(bytes32 poolId)\n external\n view\n returns (\n IERC20[] memory tokens,\n uint256[] memory balances,\n uint256 lastChangeBlock\n );\n\n /**\n * @dev Performs a set of user balance operations, which involve Internal Balance (deposit, withdraw or transfer)\n * and plain ERC20 transfers using the Vault's allowance. This last feature is particularly useful for relayers, as\n * it lets integrators reuse a user's Vault allowance.\n *\n * For each operation, if the caller is not `sender`, it must be an authorized relayer for them.\n */\n function manageUserBalance(UserBalanceOp[] memory ops) external payable;\n\n struct UserBalanceOp {\n UserBalanceOpKind kind;\n address asset;\n uint256 amount;\n address sender;\n address payable recipient;\n }\n\n enum UserBalanceOpKind {\n DEPOSIT_INTERNAL,\n WITHDRAW_INTERNAL,\n TRANSFER_INTERNAL,\n TRANSFER_EXTERNAL\n }\n\n enum SwapKind {\n GIVEN_IN,\n GIVEN_OUT\n }\n\n struct SingleSwap {\n bytes32 poolId;\n SwapKind kind;\n address assetIn;\n address assetOut;\n uint256 amount;\n bytes userData;\n }\n\n struct FundManagement {\n address sender;\n bool fromInternalBalance;\n address payable recipient;\n bool toInternalBalance;\n }\n\n function swap(\n SingleSwap calldata singleSwap,\n FundManagement calldata funds,\n uint256 limit,\n uint256 deadline\n ) external returns (uint256 amountCalculated);\n\n function getPoolTokenInfo(bytes32 poolId, address token)\n external\n view\n returns (\n uint256 cash,\n uint256 managed,\n uint256 lastChangeBlock,\n address assetManager\n );\n}\n" + }, + "contracts/interfaces/balancer/IMetaStablePool.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IRateProvider } from \"./IRateProvider.sol\";\n\ninterface IMetaStablePool {\n function getRateProviders()\n external\n view\n returns (IRateProvider[] memory providers);\n}\n" + }, + "contracts/interfaces/balancer/IRateProvider.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity ^0.8.0;\n\ninterface IRateProvider {\n function getRate() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/chainlink/AggregatorV3Interface.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorV3Interface {\n function decimals() external view returns (uint8);\n\n function description() external view returns (string memory);\n\n function version() external view returns (uint256);\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(uint80 _roundId)\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n\n function latestRoundData()\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconOracle {\n function blockToSlot(uint64 blockNumber) external returns (uint64 slot);\n\n function slotToBlock(uint64 slot) external returns (uint64 blockNumber);\n\n function slotToRoot(uint64 slot) external returns (bytes32 blockRoot);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n enum BalanceProofLevel {\n Container,\n BeaconBlock\n }\n\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 root,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex,\n BalanceProofLevel level\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view;\n\n function verifyBlockNumber(\n bytes32 beaconBlockRoot,\n uint256 blockNumber,\n bytes calldata blockNumberProof\n ) external view;\n\n function verifySlot(\n bytes32 beaconBlockRoot,\n uint256 slot,\n bytes calldata slotProof\n ) external view;\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n external\n pure\n returns (uint256);\n\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) external pure returns (uint256);\n}\n" + }, + "contracts/interfaces/IChildLiquidityGaugeFactory.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IChildLiquidityGaugeFactory {\n event DeployedGauge(\n address indexed _implementation,\n address indexed _lp_token,\n address indexed _deployer,\n bytes32 _salt,\n address _gauge\n );\n event Minted(\n address indexed _user,\n address indexed _gauge,\n uint256 _new_total\n );\n event TransferOwnership(address _old_owner, address _new_owner);\n event UpdateCallProxy(address _old_call_proxy, address _new_call_proxy);\n event UpdateImplementation(\n address _old_implementation,\n address _new_implementation\n );\n event UpdateManager(address _manager);\n event UpdateMirrored(address indexed _gauge, bool _mirrored);\n event UpdateRoot(address _factory, address _implementation);\n event UpdateVotingEscrow(\n address _old_voting_escrow,\n address _new_voting_escrow\n );\n\n function accept_transfer_ownership() external;\n\n function call_proxy() external view returns (address);\n\n function commit_transfer_ownership(address _future_owner) external;\n\n function crv() external view returns (address);\n\n function deploy_gauge(address _lp_token, bytes32 _salt)\n external\n returns (address);\n\n function deploy_gauge(\n address _lp_token,\n bytes32 _salt,\n address _manager\n ) external returns (address);\n\n function future_owner() external view returns (address);\n\n function gauge_data(address arg0) external view returns (uint256);\n\n function get_gauge(uint256 arg0) external view returns (address);\n\n function get_gauge_count() external view returns (uint256);\n\n function get_gauge_from_lp_token(address arg0)\n external\n view\n returns (address);\n\n function get_implementation() external view returns (address);\n\n function is_mirrored(address _gauge) external view returns (bool);\n\n function is_valid_gauge(address _gauge) external view returns (bool);\n\n function last_request(address _gauge) external view returns (uint256);\n\n function manager() external view returns (address);\n\n function mint(address _gauge) external;\n\n function mint_many(address[32] memory _gauges) external;\n\n function minted(address arg0, address arg1) external view returns (uint256);\n\n function owner() external view returns (address);\n\n function root_factory() external view returns (address);\n\n function root_implementation() external view returns (address);\n\n function set_call_proxy(address _new_call_proxy) external;\n\n function set_crv(address _crv) external;\n\n function set_implementation(address _implementation) external;\n\n function set_manager(address _new_manager) external;\n\n function set_mirrored(address _gauge, bool _mirrored) external;\n\n function set_root(address _factory, address _implementation) external;\n\n function set_voting_escrow(address _voting_escrow) external;\n\n function version() external view returns (string memory);\n\n function voting_escrow() external view returns (address);\n}\n" + }, + "contracts/interfaces/IComptroller.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IComptroller {\n // Claim all the COMP accrued by specific holders in specific markets for their supplies and/or borrows\n function claimComp(\n address[] memory holders,\n address[] memory cTokens,\n bool borrowers,\n bool suppliers\n ) external;\n\n function oracle() external view returns (address);\n}\n" + }, + "contracts/interfaces/IConsolidation.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IConsolidationSource {\n function confirmConsolidation()\n external\n returns (uint256 consolidationCount);\n}\n\ninterface IConsolidationTarget {\n function requestConsolidation(\n bytes32 lastSourcePubKeyHash,\n bytes32 targetPubKeyHash\n ) external;\n}\n" + }, + "contracts/interfaces/ICurveLiquidityGaugeV6.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveLiquidityGaugeV6 {\n event ApplyOwnership(address admin);\n event Approval(\n address indexed _owner,\n address indexed _spender,\n uint256 _value\n );\n event CommitOwnership(address admin);\n event Deposit(address indexed provider, uint256 value);\n event SetGaugeManager(address _gauge_manager);\n event Transfer(address indexed _from, address indexed _to, uint256 _value);\n event UpdateLiquidityLimit(\n address indexed user,\n uint256 original_balance,\n uint256 original_supply,\n uint256 working_balance,\n uint256 working_supply\n );\n event Withdraw(address indexed provider, uint256 value);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_reward(address _reward_token, address _distributor) external;\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function claim_rewards() external;\n\n function claim_rewards(address _addr) external;\n\n function claim_rewards(address _addr, address _receiver) external;\n\n function claimable_reward(address _user, address _reward_token)\n external\n view\n returns (uint256);\n\n function claimable_tokens(address addr) external returns (uint256);\n\n function claimed_reward(address _addr, address _token)\n external\n view\n returns (uint256);\n\n function decimals() external view returns (uint256);\n\n function decreaseAllowance(address _spender, uint256 _subtracted_value)\n external\n returns (bool);\n\n function deposit(uint256 _value) external;\n\n function deposit(uint256 _value, address _addr) external;\n\n function deposit(\n uint256 _value,\n address _addr,\n bool _claim_rewards\n ) external;\n\n function deposit_reward_token(address _reward_token, uint256 _amount)\n external;\n\n function deposit_reward_token(\n address _reward_token,\n uint256 _amount,\n uint256 _epoch\n ) external;\n\n function factory() external view returns (address);\n\n function future_epoch_time() external view returns (uint256);\n\n function increaseAllowance(address _spender, uint256 _added_value)\n external\n returns (bool);\n\n function inflation_rate() external view returns (uint256);\n\n function integrate_checkpoint() external view returns (uint256);\n\n function integrate_checkpoint_of(address arg0)\n external\n view\n returns (uint256);\n\n function integrate_fraction(address arg0) external view returns (uint256);\n\n function integrate_inv_supply(uint256 arg0) external view returns (uint256);\n\n function integrate_inv_supply_of(address arg0)\n external\n view\n returns (uint256);\n\n function is_killed() external view returns (bool);\n\n function kick(address addr) external;\n\n function lp_token() external view returns (address);\n\n function manager() external view returns (address);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function period() external view returns (int128);\n\n function period_timestamp(uint256 arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function reward_count() external view returns (uint256);\n\n function reward_integral_for(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function reward_tokens(uint256 arg0) external view returns (address);\n\n function rewards_receiver(address arg0) external view returns (address);\n\n function salt() external view returns (bytes32);\n\n function set_gauge_manager(address _gauge_manager) external;\n\n function set_killed(bool _is_killed) external;\n\n function set_reward_distributor(address _reward_token, address _distributor)\n external;\n\n function set_rewards_receiver(address _receiver) external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function user_checkpoint(address addr) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw(uint256 _value) external;\n\n function withdraw(uint256 _value, bool _claim_rewards) external;\n\n function working_balances(address arg0) external view returns (uint256);\n\n function working_supply() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/ICurveMinter.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveMinter {\n event Minted(address indexed recipient, address gauge, uint256 minted);\n\n function allowed_to_mint_for(address arg0, address arg1)\n external\n view\n returns (bool);\n\n function controller() external view returns (address);\n\n function mint(address gauge_addr) external;\n\n function mint_for(address gauge_addr, address _for) external;\n\n function mint_many(address[8] memory gauge_addrs) external;\n\n function minted(address arg0, address arg1) external view returns (uint256);\n\n function toggle_approve_mint(address minting_user) external;\n\n function token() external view returns (address);\n}\n" + }, + "contracts/interfaces/ICurveStableSwapNG.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveStableSwapNG {\n event AddLiquidity(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event ApplyNewFee(uint256 fee, uint256 offpeg_fee_multiplier);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event RampA(\n uint256 old_A,\n uint256 new_A,\n uint256 initial_time,\n uint256 future_time\n );\n event RemoveLiquidity(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 token_supply\n );\n event RemoveLiquidityImbalance(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event RemoveLiquidityOne(\n address indexed provider,\n int128 token_id,\n uint256 token_amount,\n uint256 coin_amount,\n uint256 token_supply\n );\n event SetNewMATime(uint256 ma_exp_time, uint256 D_ma_time);\n event StopRampA(uint256 A, uint256 t);\n event TokenExchange(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event TokenExchangeUnderlying(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event Transfer(\n address indexed sender,\n address indexed receiver,\n uint256 value\n );\n\n function A() external view returns (uint256);\n\n function A_precise() external view returns (uint256);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function D_ma_time() external view returns (uint256);\n\n function D_oracle() external view returns (uint256);\n\n function N_COINS() external view returns (uint256);\n\n function add_liquidity(uint256[] memory _amounts, uint256 _min_mint_amount)\n external\n returns (uint256);\n\n function add_liquidity(\n uint256[] memory _amounts,\n uint256 _min_mint_amount,\n address _receiver\n ) external returns (uint256);\n\n function admin_balances(uint256 arg0) external view returns (uint256);\n\n function admin_fee() external view returns (uint256);\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function balances(uint256 i) external view returns (uint256);\n\n function calc_token_amount(uint256[] memory _amounts, bool _is_deposit)\n external\n view\n returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _burn_amount, int128 i)\n external\n view\n returns (uint256);\n\n function coins(uint256 arg0) external view returns (address);\n\n function decimals() external view returns (uint8);\n\n function dynamic_fee(int128 i, int128 j) external view returns (uint256);\n\n function ema_price(uint256 i) external view returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external returns (uint256);\n\n function exchange_received(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external returns (uint256);\n\n function exchange_received(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external returns (uint256);\n\n function fee() external view returns (uint256);\n\n function future_A() external view returns (uint256);\n\n function future_A_time() external view returns (uint256);\n\n function get_balances() external view returns (uint256[] memory);\n\n function get_dx(\n int128 i,\n int128 j,\n uint256 dy\n ) external view returns (uint256);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n\n function get_p(uint256 i) external view returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function initial_A() external view returns (uint256);\n\n function initial_A_time() external view returns (uint256);\n\n function last_price(uint256 i) external view returns (uint256);\n\n function ma_exp_time() external view returns (uint256);\n\n function ma_last_time() external view returns (uint256);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function offpeg_fee_multiplier() external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function price_oracle(uint256 i) external view returns (uint256);\n\n function ramp_A(uint256 _future_A, uint256 _future_time) external;\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts\n ) external returns (uint256[] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts,\n address _receiver\n ) external returns (uint256[] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts,\n address _receiver,\n bool _claim_admin_fees\n ) external returns (uint256[] memory);\n\n function remove_liquidity_imbalance(\n uint256[] memory _amounts,\n uint256 _max_burn_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[] memory _amounts,\n uint256 _max_burn_amount,\n address _receiver\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received,\n address _receiver\n ) external returns (uint256);\n\n function salt() external view returns (bytes32);\n\n function set_ma_exp_time(uint256 _ma_exp_time, uint256 _D_ma_time) external;\n\n function set_new_fee(uint256 _new_fee, uint256 _new_offpeg_fee_multiplier)\n external;\n\n function stop_ramp_A() external;\n\n function stored_rates() external view returns (uint256[] memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw_admin_fees() external;\n}\n" + }, + "contracts/interfaces/ICurveXChainLiquidityGauge.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveXChainLiquidityGauge {\n event Approval(\n address indexed _owner,\n address indexed _spender,\n uint256 _value\n );\n event Deposit(address indexed provider, uint256 value);\n event SetGaugeManager(address _gauge_manager);\n event Transfer(address indexed _from, address indexed _to, uint256 _value);\n event UpdateLiquidityLimit(\n address indexed user,\n uint256 original_balance,\n uint256 original_supply,\n uint256 working_balance,\n uint256 working_supply\n );\n event Withdraw(address indexed provider, uint256 value);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_reward(address _reward_token, address _distributor) external;\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function claim_rewards() external;\n\n function claim_rewards(address _addr) external;\n\n function claim_rewards(address _addr, address _receiver) external;\n\n function claimable_reward(address _user, address _reward_token)\n external\n view\n returns (uint256);\n\n function claimable_tokens(address addr) external returns (uint256);\n\n function claimed_reward(address _addr, address _token)\n external\n view\n returns (uint256);\n\n function decimals() external view returns (uint256);\n\n function decreaseAllowance(address _spender, uint256 _subtracted_value)\n external\n returns (bool);\n\n function deposit(uint256 _value) external;\n\n function deposit(uint256 _value, address _addr) external;\n\n function deposit(\n uint256 _value,\n address _addr,\n bool _claim_rewards\n ) external;\n\n function deposit_reward_token(address _reward_token, uint256 _amount)\n external;\n\n function deposit_reward_token(\n address _reward_token,\n uint256 _amount,\n uint256 _epoch\n ) external;\n\n function factory() external view returns (address);\n\n function increaseAllowance(address _spender, uint256 _added_value)\n external\n returns (bool);\n\n function inflation_rate(uint256 arg0) external view returns (uint256);\n\n function initialize(\n address _lp_token,\n address _root,\n address _manager\n ) external;\n\n function integrate_checkpoint() external view returns (uint256);\n\n function integrate_checkpoint_of(address arg0)\n external\n view\n returns (uint256);\n\n function integrate_fraction(address arg0) external view returns (uint256);\n\n function integrate_inv_supply(int128 arg0) external view returns (uint256);\n\n function integrate_inv_supply_of(address arg0)\n external\n view\n returns (uint256);\n\n function is_killed() external view returns (bool);\n\n function lp_token() external view returns (address);\n\n function manager() external view returns (address);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function period() external view returns (int128);\n\n function period_timestamp(int128 arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function recover_remaining(address _reward_token) external;\n\n function reward_count() external view returns (uint256);\n\n function reward_integral_for(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function reward_remaining(address arg0) external view returns (uint256);\n\n function reward_tokens(uint256 arg0) external view returns (address);\n\n function rewards_receiver(address arg0) external view returns (address);\n\n function root_gauge() external view returns (address);\n\n function set_gauge_manager(address _gauge_manager) external;\n\n function set_killed(bool _is_killed) external;\n\n function set_manager(address _gauge_manager) external;\n\n function set_reward_distributor(address _reward_token, address _distributor)\n external;\n\n function set_rewards_receiver(address _receiver) external;\n\n function set_root_gauge(address _root) external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function update_voting_escrow() external;\n\n function user_checkpoint(address addr) external returns (bool);\n\n function version() external view returns (string memory);\n\n function voting_escrow() external view returns (address);\n\n function withdraw(uint256 _value) external;\n\n function withdraw(uint256 _value, bool _claim_rewards) external;\n\n function withdraw(\n uint256 _value,\n bool _claim_rewards,\n address _receiver\n ) external;\n\n function working_balances(address arg0) external view returns (uint256);\n\n function working_supply() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/IDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IDripper {\n /// @notice How much funds have dripped out already and are currently\n // available to be sent to the vault.\n /// @return The amount that would be sent if a collect was called\n function availableFunds() external view returns (uint256);\n\n /// @notice Collect all dripped funds and send to vault.\n /// Recalculate new drip rate.\n function collect() external;\n\n /// @notice Collect all dripped funds, send to vault, recalculate new drip\n /// rate, and rebase mToken.\n function collectAndRebase() external;\n\n /// @notice Change the drip duration. Governor only.\n /// @param _durationSeconds the number of seconds to drip out the entire\n /// balance over if no collects were called during that time.\n function setDripDuration(uint256 _durationSeconds) external;\n\n /// @dev Transfer out ERC20 tokens held by the contract. Governor only.\n /// @param _asset ERC20 token address\n /// @param _amount amount to transfer\n function transferToken(address _asset, uint256 _amount) external;\n}\n" + }, + "contracts/interfaces/IGetExchangeRateToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IGetExchangeRateToken {\n function getExchangeRate() external view returns (uint256 _exchangeRate);\n}\n" + }, + "contracts/interfaces/IMockVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"./IVault.sol\";\n\ninterface IMockVault is IVault {\n function outstandingWithdrawalsAmount() external view returns (uint256);\n\n function wethAvailable() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOracle {\n /**\n * @dev returns the asset price in USD, in 8 decimal digits.\n *\n * The version of priceProvider deployed for OETH has 18 decimal digits\n */\n function price(address asset) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IOUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOUSD {\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n function _totalSupply() external view returns (uint256);\n\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address _account) external view returns (uint256);\n\n function burn(address account, uint256 amount) external;\n\n function changeSupply(uint256 _newTotalSupply) external;\n\n function claimGovernance() external;\n\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256);\n\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n );\n\n function decimals() external view returns (uint8);\n\n function decreaseAllowance(address _spender, uint256 _subtractedValue)\n external\n returns (bool);\n\n function governor() external view returns (address);\n\n function increaseAllowance(address _spender, uint256 _addedValue)\n external\n returns (bool);\n\n function initialize(\n string memory _nameArg,\n string memory _symbolArg,\n address _vaultAddress\n ) external;\n\n function isGovernor() external view returns (bool);\n\n function isUpgraded(address) external view returns (uint256);\n\n function mint(address _account, uint256 _amount) external;\n\n function name() external view returns (string memory);\n\n function nonRebasingCreditsPerToken(address)\n external\n view\n returns (uint256);\n\n function nonRebasingSupply() external view returns (uint256);\n\n function rebaseOptIn() external;\n\n function rebaseOptOut() external;\n\n function rebaseState(address) external view returns (uint8);\n\n function rebasingCredits() external view returns (uint256);\n\n function rebasingCreditsHighres() external view returns (uint256);\n\n function rebasingCreditsPerToken() external view returns (uint256);\n\n function rebasingCreditsPerTokenHighres() external view returns (uint256);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function transferGovernance(address _newGovernor) external;\n\n function vaultAddress() external view returns (address);\n}\n" + }, + "contracts/interfaces/ISafe.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ISafe {\n function execTransactionFromModule(\n address,\n uint256,\n bytes memory,\n uint8\n ) external returns (bool);\n}\n" + }, + "contracts/interfaces/ISfrxETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface ISfrxETH {\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 amount\n );\n event Deposit(\n address indexed caller,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n event NewRewardsCycle(uint32 indexed cycleEnd, uint256 rewardAmount);\n event Transfer(address indexed from, address indexed to, uint256 amount);\n event Withdraw(\n address indexed caller,\n address indexed receiver,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address spender, uint256 amount) external returns (bool);\n\n function asset() external view returns (address);\n\n function balanceOf(address) external view returns (uint256);\n\n function convertToAssets(uint256 shares) external view returns (uint256);\n\n function convertToShares(uint256 assets) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit(uint256 assets, address receiver)\n external\n returns (uint256 shares);\n\n function depositWithSignature(\n uint256 assets,\n address receiver,\n uint256 deadline,\n bool approveMax,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external returns (uint256 shares);\n\n function lastRewardAmount() external view returns (uint192);\n\n function lastSync() external view returns (uint32);\n\n function maxDeposit(address) external view returns (uint256);\n\n function maxMint(address) external view returns (uint256);\n\n function maxRedeem(address owner) external view returns (uint256);\n\n function maxWithdraw(address owner) external view returns (uint256);\n\n function mint(uint256 shares, address receiver)\n external\n returns (uint256 assets);\n\n function name() external view returns (string memory);\n\n function nonces(address) external view returns (uint256);\n\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function previewDeposit(uint256 assets) external view returns (uint256);\n\n function previewMint(uint256 shares) external view returns (uint256);\n\n function previewRedeem(uint256 shares) external view returns (uint256);\n\n function previewWithdraw(uint256 assets) external view returns (uint256);\n\n function pricePerShare() external view returns (uint256);\n\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets);\n\n function rewardsCycleEnd() external view returns (uint32);\n\n function rewardsCycleLength() external view returns (uint32);\n\n function symbol() external view returns (string memory);\n\n function syncRewards() external;\n\n function totalAssets() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address to, uint256 amount) external returns (bool);\n\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/ISwapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ISwapper {\n /**\n * @param fromAsset The token address of the asset being sold.\n * @param toAsset The token address of the asset being purchased.\n * @param fromAssetAmount The amount of assets being sold.\n * @param minToAssetAmmount The minimum amount of assets to be purchased.\n * @param data tx.data returned from 1Inch's /v5.0/1/swap API\n */\n function swap(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/interfaces/IWstETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWstETH {\n /**\n * @notice Get amount of wstETH for a given amount of stETH\n * @param _stETHAmount amount of stETH\n * @return Amount of wstETH for a given stETH amount\n */\n function getWstETHByStETH(uint256 _stETHAmount)\n external\n view\n returns (uint256);\n\n /**\n * @notice Get amount of stETH for a given amount of wstETH\n * @param _wstETHAmount amount of wstETH\n * @return Amount of stETH for a given wstETH amount\n */\n function getStETHByWstETH(uint256 _wstETHAmount)\n external\n view\n returns (uint256);\n\n /**\n * @notice Get amount of stETH for a one wstETH\n * @return Amount of stETH for 1 wstETH\n */\n function stEthPerToken() external view returns (uint256);\n\n /**\n * @notice Get amount of wstETH for a one stETH\n * @return Amount of wstETH for a 1 stETH\n */\n function tokensPerStEth() external view returns (uint256);\n\n /**\n * @notice Exchanges stETH to wstETH\n * @param _stETHAmount amount of stETH to wrap in exchange for wstETH\n * @dev Requirements:\n * - `_stETHAmount` must be non-zero\n * - msg.sender must approve at least `_stETHAmount` stETH to this\n * contract.\n * - msg.sender must have at least `_stETHAmount` of stETH.\n * User should first approve _stETHAmount to the WstETH contract\n * @return Amount of wstETH user receives after wrap\n */\n function wrap(uint256 _stETHAmount) external returns (uint256);\n\n /**\n * @notice Exchanges wstETH to stETH\n * @param _wstETHAmount amount of wstETH to uwrap in exchange for stETH\n * @dev Requirements:\n * - `_wstETHAmount` must be non-zero\n * - msg.sender must have at least `_wstETHAmount` wstETH.\n * @return Amount of stETH user receives after unwrap\n */\n function unwrap(uint256 _wstETHAmount) external returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/compound/ICompoundOracle.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\ninterface ICompoundOracle {\n function getUnderlyingPrice(address) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/ILens.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\nimport \"./compound/ICompoundOracle.sol\";\nimport \"./IMorpho.sol\";\n\ninterface ILens {\n /// STORAGE ///\n\n function MAX_BASIS_POINTS() external view returns (uint256);\n\n function WAD() external view returns (uint256);\n\n function morpho() external view returns (IMorpho);\n\n function comptroller() external view returns (IComptroller);\n\n /// GENERAL ///\n\n function getTotalSupply()\n external\n view\n returns (\n uint256 p2pSupplyAmount,\n uint256 poolSupplyAmount,\n uint256 totalSupplyAmount\n );\n\n function getTotalBorrow()\n external\n view\n returns (\n uint256 p2pBorrowAmount,\n uint256 poolBorrowAmount,\n uint256 totalBorrowAmount\n );\n\n /// MARKETS ///\n\n function isMarketCreated(address _poolToken) external view returns (bool);\n\n function isMarketCreatedAndNotPaused(address _poolToken)\n external\n view\n returns (bool);\n\n function isMarketCreatedAndNotPausedNorPartiallyPaused(address _poolToken)\n external\n view\n returns (bool);\n\n function getAllMarkets()\n external\n view\n returns (address[] memory marketsCreated_);\n\n function getMainMarketData(address _poolToken)\n external\n view\n returns (\n uint256 avgSupplyRatePerBlock,\n uint256 avgBorrowRatePerBlock,\n uint256 p2pSupplyAmount,\n uint256 p2pBorrowAmount,\n uint256 poolSupplyAmount,\n uint256 poolBorrowAmount\n );\n\n function getAdvancedMarketData(address _poolToken)\n external\n view\n returns (\n uint256 p2pSupplyIndex,\n uint256 p2pBorrowIndex,\n uint256 poolSupplyIndex,\n uint256 poolBorrowIndex,\n uint32 lastUpdateBlockNumber,\n uint256 p2pSupplyDelta,\n uint256 p2pBorrowDelta\n );\n\n function getMarketConfiguration(address _poolToken)\n external\n view\n returns (\n address underlying,\n bool isCreated,\n bool p2pDisabled,\n bool isPaused,\n bool isPartiallyPaused,\n uint16 reserveFactor,\n uint16 p2pIndexCursor,\n uint256 collateralFactor\n );\n\n function getTotalMarketSupply(address _poolToken)\n external\n view\n returns (uint256 p2pSupplyAmount, uint256 poolSupplyAmount);\n\n function getTotalMarketBorrow(address _poolToken)\n external\n view\n returns (uint256 p2pBorrowAmount, uint256 poolBorrowAmount);\n\n /// INDEXES ///\n\n function getCurrentP2PSupplyIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentP2PBorrowIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentPoolIndexes(address _poolToken)\n external\n view\n returns (\n uint256 currentPoolSupplyIndex,\n uint256 currentPoolBorrowIndex\n );\n\n function getIndexes(address _poolToken, bool _computeUpdatedIndexes)\n external\n view\n returns (\n uint256 p2pSupplyIndex,\n uint256 p2pBorrowIndex,\n uint256 poolSupplyIndex,\n uint256 poolBorrowIndex\n );\n\n /// USERS ///\n\n function getEnteredMarkets(address _user)\n external\n view\n returns (address[] memory enteredMarkets);\n\n function getUserHealthFactor(\n address _user,\n address[] calldata _updatedMarkets\n ) external view returns (uint256);\n\n function getUserBalanceStates(\n address _user,\n address[] calldata _updatedMarkets\n )\n external\n view\n returns (\n uint256 collateralValue,\n uint256 debtValue,\n uint256 maxDebtValue\n );\n\n function getCurrentSupplyBalanceInOf(address _poolToken, address _user)\n external\n view\n returns (\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getCurrentBorrowBalanceInOf(address _poolToken, address _user)\n external\n view\n returns (\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getUserMaxCapacitiesForAsset(address _user, address _poolToken)\n external\n view\n returns (uint256 withdrawable, uint256 borrowable);\n\n function getUserHypotheticalBalanceStates(\n address _user,\n address _poolToken,\n uint256 _withdrawnAmount,\n uint256 _borrowedAmount\n ) external view returns (uint256 debtValue, uint256 maxDebtValue);\n\n function getUserLiquidityDataForAsset(\n address _user,\n address _poolToken,\n bool _computeUpdatedIndexes,\n ICompoundOracle _oracle\n ) external view returns (Types.AssetLiquidityData memory assetData);\n\n function isLiquidatable(address _user, address[] memory _updatedMarkets)\n external\n view\n returns (bool);\n\n function computeLiquidationRepayAmount(\n address _user,\n address _poolTokenBorrowed,\n address _poolTokenCollateral,\n address[] calldata _updatedMarkets\n ) external view returns (uint256 toRepay);\n\n /// RATES ///\n\n function getAverageSupplyRatePerBlock(address _poolToken)\n external\n view\n returns (\n uint256 avgSupplyRatePerBlock,\n uint256 p2pSupplyAmount,\n uint256 poolSupplyAmount\n );\n\n function getAverageBorrowRatePerBlock(address _poolToken)\n external\n view\n returns (\n uint256 avgBorrowRatePerBlock,\n uint256 p2pBorrowAmount,\n uint256 poolBorrowAmount\n );\n\n function getNextUserSupplyRatePerBlock(\n address _poolToken,\n address _user,\n uint256 _amount\n )\n external\n view\n returns (\n uint256 nextSupplyRatePerBlock,\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getNextUserBorrowRatePerBlock(\n address _poolToken,\n address _user,\n uint256 _amount\n )\n external\n view\n returns (\n uint256 nextBorrowRatePerBlock,\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getCurrentUserSupplyRatePerBlock(address _poolToken, address _user)\n external\n view\n returns (uint256);\n\n function getCurrentUserBorrowRatePerBlock(address _poolToken, address _user)\n external\n view\n returns (uint256);\n\n function getRatesPerBlock(address _poolToken)\n external\n view\n returns (\n uint256 p2pSupplyRate,\n uint256 p2pBorrowRate,\n uint256 poolSupplyRate,\n uint256 poolBorrowRate\n );\n\n /// REWARDS ///\n\n function getUserUnclaimedRewards(\n address[] calldata _poolTokens,\n address _user\n ) external view returns (uint256 unclaimedRewards);\n\n function getAccruedSupplierComp(\n address _supplier,\n address _poolToken,\n uint256 _balance\n ) external view returns (uint256);\n\n function getAccruedBorrowerComp(\n address _borrower,\n address _poolToken,\n uint256 _balance\n ) external view returns (uint256);\n\n function getCurrentCompSupplyIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentCompBorrowIndex(address _poolToken)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/IMorpho.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\nimport \"./Types.sol\";\nimport \"../IComptroller.sol\";\nimport \"./compound/ICompoundOracle.sol\";\n\n// prettier-ignore\ninterface IMorpho {\n function comptroller() external view returns (IComptroller);\n function supply(address _poolTokenAddress, address _onBehalf, uint256 _amount) external;\n function supply(address _poolTokenAddress, address _onBehalf, uint256 _amount, uint256 _maxGasForMatching) external;\n function withdraw(address _poolTokenAddress, uint256 _amount) external;\n function claimRewards(\n address[] calldata _cTokenAddresses,\n bool _tradeForMorphoToken\n ) external returns (uint256 claimedAmount);\n}\n" + }, + "contracts/interfaces/morpho/Types.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\n/// @title Types.\n/// @author Morpho Labs.\n/// @custom:contact security@morpho.xyz\n/// @dev Common types and structs used in Moprho contracts.\nlibrary Types {\n /// ENUMS ///\n\n enum PositionType {\n SUPPLIERS_IN_P2P,\n SUPPLIERS_ON_POOL,\n BORROWERS_IN_P2P,\n BORROWERS_ON_POOL\n }\n\n /// STRUCTS ///\n\n struct SupplyBalance {\n uint256 inP2P; // In supplier's peer-to-peer unit, a unit that grows in underlying value, to keep track of the interests earned by suppliers in peer-to-peer. Multiply by the peer-to-peer supply index to get the underlying amount.\n uint256 onPool; // In cToken. Multiply by the pool supply index to get the underlying amount.\n }\n\n struct BorrowBalance {\n uint256 inP2P; // In borrower's peer-to-peer unit, a unit that grows in underlying value, to keep track of the interests paid by borrowers in peer-to-peer. Multiply by the peer-to-peer borrow index to get the underlying amount.\n uint256 onPool; // In cdUnit, a unit that grows in value, to keep track of the debt increase when borrowers are on Compound. Multiply by the pool borrow index to get the underlying amount.\n }\n\n // Max gas to consume during the matching process for supply, borrow, withdraw and repay functions.\n struct MaxGasForMatching {\n uint64 supply;\n uint64 borrow;\n uint64 withdraw;\n uint64 repay;\n }\n\n struct Delta {\n uint256 p2pSupplyDelta; // Difference between the stored peer-to-peer supply amount and the real peer-to-peer supply amount (in pool supply unit).\n uint256 p2pBorrowDelta; // Difference between the stored peer-to-peer borrow amount and the real peer-to-peer borrow amount (in pool borrow unit).\n uint256 p2pSupplyAmount; // Sum of all stored peer-to-peer supply (in peer-to-peer supply unit).\n uint256 p2pBorrowAmount; // Sum of all stored peer-to-peer borrow (in peer-to-peer borrow unit).\n }\n\n struct AssetLiquidityData {\n uint256 collateralValue; // The collateral value of the asset.\n uint256 maxDebtValue; // The maximum possible debt value of the asset.\n uint256 debtValue; // The debt value of the asset.\n uint256 underlyingPrice; // The price of the token.\n uint256 collateralFactor; // The liquidation threshold applied on this token.\n }\n\n struct LiquidityData {\n uint256 collateralValue; // The collateral value.\n uint256 maxDebtValue; // The maximum debt value possible.\n uint256 debtValue; // The debt value.\n }\n\n // Variables are packed together to save gas (will not exceed their limit during Morpho's lifetime).\n struct LastPoolIndexes {\n uint32 lastUpdateBlockNumber; // The last time the peer-to-peer indexes were updated.\n uint112 lastSupplyPoolIndex; // Last pool supply index.\n uint112 lastBorrowPoolIndex; // Last pool borrow index.\n }\n\n struct MarketParameters {\n uint16 reserveFactor; // Proportion of the interest earned by users sent to the DAO for each market, in basis point (100% = 10 000). The value is set at market creation.\n uint16 p2pIndexCursor; // Position of the peer-to-peer rate in the pool's spread. Determine the weights of the weighted arithmetic average in the indexes computations ((1 - p2pIndexCursor) * r^S + p2pIndexCursor * r^B) (in basis point).\n }\n\n struct MarketStatus {\n bool isCreated; // Whether or not this market is created.\n bool isPaused; // Whether the market is paused or not (all entry points on Morpho are frozen; supply, borrow, withdraw, repay and liquidate).\n bool isPartiallyPaused; // Whether the market is partially paused or not (only supply and borrow are frozen).\n }\n}\n" + }, + "contracts/interfaces/plume/IFeeRegistry.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\ninterface IFeeRegistry {\n function registerFee(\n bool isTokenA,\n uint32 binId,\n uint256 binFeeInQuote\n ) external;\n}\n" + }, + "contracts/interfaces/plume/ILiquidityRegistry.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface ILiquidityRegistry {\n function notifyBinLiquidity(\n IMaverickV2Pool pool,\n uint256 tokenId,\n uint32 binId,\n uint256 currentBinLpBalance\n ) external;\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Factory.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IFeeRegistry } from \"./IFeeRegistry.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2Factory {\n error FactorAlreadyInitialized();\n error FactorNotInitialized();\n error FactoryInvalidTokenOrder(IERC20 _tokenA, IERC20 _tokenB);\n error FactoryInvalidFee();\n error FactoryInvalidKinds(uint8 kinds);\n error FactoryInvalidTickSpacing(uint256 tickSpacing);\n error FactoryInvalidLookback(uint256 lookback);\n error FactoryInvalidTokenDecimals(uint8 decimalsA, uint8 decimalsB);\n error FactoryPoolAlreadyExists(\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds,\n address accessor\n );\n error FactoryAccessorMustBeNonZero();\n error NotImplemented();\n\n event PoolCreated(\n IMaverickV2Pool poolAddress,\n uint8 protocolFeeRatio,\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n int32 activeTick,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds,\n address accessor\n );\n event SetFactoryProtocolFeeReceiver(address receiver);\n event SetFactoryProtocolFeeRegistry(IFeeRegistry registry);\n\n struct DeployParameters {\n uint64 feeAIn;\n uint64 feeBIn;\n uint32 lookback;\n int32 activeTick;\n uint64 tokenAScale;\n uint64 tokenBScale;\n // slot\n IERC20 tokenA;\n // slot\n IERC20 tokenB;\n // slot\n uint16 tickSpacing;\n uint8 options;\n address accessor;\n }\n\n /**\n * @notice Called by deployer library to initialize a pool.\n */\n function deployParameters()\n external\n view\n returns (\n uint64 feeAIn,\n uint64 feeBIn,\n uint32 lookback,\n int32 activeTick,\n uint64 tokenAScale,\n uint64 tokenBScale,\n // slot\n IERC20 tokenA,\n // slot\n IERC20 tokenB,\n // slot\n uint16 tickSpacing,\n uint8 options,\n address accessor\n );\n\n /**\n * @notice Create a new MaverickV2Pool with symmetric swap fees.\n * @param fee Fraction of the pool swap amount that is retained as an LP in\n * D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * E.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function create(\n uint64 fee,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external returns (IMaverickV2Pool);\n\n /**\n * @notice Create a new MaverickV2Pool.\n * @param feeAIn Fraction of the pool swap amount for tokenA-input swaps\n * that is retained as an LP in D18 scale.\n * @param feeBIn Fraction of the pool swap amount for tokenB-input swaps\n * that is retained as an LP in D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * e.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function create(\n uint64 feeAIn,\n uint64 feeBIn,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external returns (IMaverickV2Pool);\n\n /**\n * @notice Bool indicating whether the pool was deployed from this factory.\n */\n function isFactoryPool(IMaverickV2Pool pool) external view returns (bool);\n\n /**\n * @notice Address that receives the protocol fee\n */\n function protocolFeeReceiver() external view returns (address);\n\n /**\n * @notice Address notified on swaps of the protocol fee\n */\n function protocolFeeRegistry() external view returns (IFeeRegistry);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds\n ) external view returns (IMaverickV2Pool);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(\n IERC20 _tokenA,\n IERC20 _tokenB,\n uint256 startIndex,\n uint256 endIndex\n ) external view returns (IMaverickV2Pool[] memory pools);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(uint256 startIndex, uint256 endIndex)\n external\n view\n returns (IMaverickV2Pool[] memory pools);\n\n /**\n * @notice Count of permissionless pools.\n */\n function poolCount() external view returns (uint256 _poolCount);\n\n /**\n * @notice Count of pools for a given accessor and token pair. For\n * permissionless pools, pass `accessor = address(0)`.\n */\n function poolByTokenCount(\n IERC20 _tokenA,\n IERC20 _tokenB,\n address accessor\n ) external view returns (uint256 _poolCount);\n\n /**\n * @notice Get the current factory owner.\n */\n function owner() external view returns (address);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2LiquidityManager.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\nimport { IMaverickV2Position } from \"./IMaverickV2Position.sol\";\nimport { IMaverickV2PoolLens } from \"./IMaverickV2PoolLens.sol\";\n\ninterface IMaverickV2LiquidityManager {\n error LiquidityManagerNotFactoryPool();\n error LiquidityManagerNotTokenIdOwner();\n\n /**\n * @notice Maverick V2 NFT position contract that tracks NFT-based\n * liquditiy positions.\n */\n function position() external view returns (IMaverickV2Position);\n\n /**\n * @notice Create Maverick V2 pool. Function is a pass through to the pool\n * factory and is provided here so that is can be assembled as part of a\n * multicall transaction.\n */\n function createPool(\n uint64 fee,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external payable returns (IMaverickV2Pool pool);\n\n /**\n * @notice Add Liquidity position NFT for msg.sender by specifying\n * msg.sender's token index.\n * @dev Token index is different from tokenId.\n * On the Position NFT contract a user can own multiple NFT tokenIds and\n * these are indexes by an enumeration index which is the `index` input\n * here.\n *\n * See addLiquidity for a description of the add params.\n */\n function addPositionLiquidityToSenderByTokenIndex(\n IMaverickV2Pool pool,\n uint256 index,\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs\n )\n external\n payable\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds\n );\n\n /**\n * @notice Mint new tokenId in the Position NFt contract to msg.sender.\n * Both mints an NFT and adds liquidity to the pool that is held by the\n * NFT.\n */\n function mintPositionNftToSender(\n IMaverickV2Pool pool,\n bytes calldata packedSqrtPriceBreaks,\n bytes[] calldata packedArgs\n )\n external\n payable\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds,\n uint256 tokenId\n );\n\n /**\n * @notice Donates liqudity to a pool that is held by the position contract\n * and will never be retrievable. Can be used to start a pool and ensure\n * there will always be a base level of liquditiy in the pool.\n */\n function donateLiquidity(\n IMaverickV2Pool pool,\n IMaverickV2Pool.AddLiquidityParams memory args\n ) external payable;\n\n /**\n * @notice Packs sqrtPrice breaks array with this format: [length,\n * array[0], array[1],..., array[length-1]] where length is 1 byte.\n */\n function packUint88Array(uint88[] memory fullArray)\n external\n pure\n returns (bytes memory packedArray);\n\n /**\n * @notice Packs addLiquidity paramters array element-wise.\n */\n function packAddLiquidityArgsArray(\n IMaverickV2Pool.AddLiquidityParams[] memory args\n ) external pure returns (bytes[] memory argsPacked);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Pool.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\n\ninterface IMaverickV2Pool {\n error PoolZeroLiquidityAdded();\n error PoolMinimumLiquidityNotMet();\n error PoolLocked();\n error PoolInvalidFee();\n error PoolTicksNotSorted(uint256 index, int256 previousTick, int256 tick);\n error PoolTicksAmountsLengthMismatch(\n uint256 ticksLength,\n uint256 amountsLength\n );\n error PoolBinIdsAmountsLengthMismatch(\n uint256 binIdsLength,\n uint256 amountsLength\n );\n error PoolKindNotSupported(uint256 kinds, uint256 kind);\n error PoolInsufficientBalance(\n uint256 deltaLpAmount,\n uint256 accountBalance\n );\n error PoolReservesExceedMaximum(uint256 amount);\n error PoolValueExceedsBits(uint256 amount, uint256 bits);\n error PoolTickMaxExceeded(uint256 tick);\n error PoolMigrateBinFirst();\n error PoolCurrentTickBeyondSwapLimit(int32 startingTick);\n error PoolSenderNotAccessor(address sender_, address accessor);\n error PoolSenderNotFactory(address sender_, address accessor);\n error PoolFunctionNotImplemented();\n error PoolTokenNotSolvent(\n uint256 internalReserve,\n uint256 tokenBalance,\n IERC20 token\n );\n error PoolNoProtocolFeeReceiverSet();\n\n event PoolSwap(\n address sender,\n address recipient,\n SwapParams params,\n uint256 amountIn,\n uint256 amountOut\n );\n event PoolFlashLoan(\n address sender,\n address recipient,\n uint256 amountA,\n uint256 amountB\n );\n event PoolProtocolFeeCollected(IERC20 token, uint256 protocolFee);\n\n event PoolAddLiquidity(\n address sender,\n address recipient,\n uint256 subaccount,\n AddLiquidityParams params,\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] binIds\n );\n\n event PoolMigrateBinsUpStack(\n address sender,\n uint32 binId,\n uint32 maxRecursion\n );\n\n event PoolRemoveLiquidity(\n address sender,\n address recipient,\n uint256 subaccount,\n RemoveLiquidityParams params,\n uint256 tokenAOut,\n uint256 tokenBOut\n );\n\n event PoolTickState(int32 tick, uint256 reserveA, uint256 reserveB);\n event PoolTickBinUpdate(int32 tick, uint8 kind, uint32 binId);\n event PoolSqrtPrice(uint256 sqrtPrice);\n\n /**\n * @notice Tick state parameters.\n */\n struct TickState {\n uint128 reserveA;\n uint128 reserveB;\n uint128 totalSupply;\n uint32[4] binIdsByTick;\n }\n\n /**\n * @notice Tick data parameters.\n * @param currentReserveA Current reserve of token A.\n * @param currentReserveB Current reserve of token B.\n * @param currentLiquidity Current liquidity amount.\n */\n struct TickData {\n uint256 currentReserveA;\n uint256 currentReserveB;\n uint256 currentLiquidity;\n }\n\n /**\n * @notice Bin state parameters.\n * @param mergeBinBalance LP token balance that this bin possesses of the merge bin.\n * @param mergeId Bin ID of the bin that this bin has merged into.\n * @param totalSupply Total amount of LP tokens in this bin.\n * @param kind One of the 4 kinds (0=static, 1=right, 2=left, 3=both).\n * @param tick The lower price tick of the bin in its current state.\n * @param tickBalance Balance of the tick.\n */\n struct BinState {\n uint128 mergeBinBalance;\n uint128 tickBalance;\n uint128 totalSupply;\n uint8 kind;\n int32 tick;\n uint32 mergeId;\n }\n\n /**\n * @notice Parameters for swap.\n * @param amount Amount of the token that is either the input if exactOutput is false\n * or the output if exactOutput is true.\n * @param tokenAIn Boolean indicating whether tokenA is the input.\n * @param exactOutput Boolean indicating whether the amount specified is\n * the exact output amount (true).\n * @param tickLimit The furthest tick a swap will execute in. If no limit\n * is desired, value should be set to type(int32).max for a tokenAIn swap\n * and type(int32).min for a swap where tokenB is the input.\n */\n struct SwapParams {\n uint256 amount;\n bool tokenAIn;\n bool exactOutput;\n int32 tickLimit;\n }\n\n /**\n * @notice Parameters associated with adding liquidity.\n * @param kind One of the 4 kinds (0=static, 1=right, 2=left, 3=both).\n * @param ticks Array of ticks to add liquidity to.\n * @param amounts Array of bin LP amounts to add.\n */\n struct AddLiquidityParams {\n uint8 kind;\n int32[] ticks;\n uint128[] amounts;\n }\n\n /**\n * @notice Parameters for each bin that will have liquidity removed.\n * @param binIds Index array of the bins losing liquidity.\n * @param amounts Array of bin LP amounts to remove.\n */\n struct RemoveLiquidityParams {\n uint32[] binIds;\n uint128[] amounts;\n }\n\n /**\n * @notice State of the pool.\n * @param reserveA Pool tokenA balanceOf at end of last operation\n * @param reserveB Pool tokenB balanceOf at end of last operation\n * @param lastTwaD8 Value of log time weighted average price at last block.\n * Value is 8-decimal scale and is in the fractional tick domain. E.g. a\n * value of 12.3e8 indicates the TWAP was 3/10ths of the way into the 12th\n * tick.\n * @param lastLogPriceD8 Value of log price at last block. Value is\n * 8-decimal scale and is in the fractional tick domain. E.g. a value of\n * 12.3e8 indicates the price was 3/10ths of the way into the 12th tick.\n * @param lastTimestamp Last block.timestamp value in seconds for latest\n * swap transaction.\n * @param activeTick Current tick position that contains the active bins.\n * @param isLocked Pool isLocked, E.g., locked or unlocked; isLocked values\n * defined in Pool.sol.\n * @param binCounter Index of the last bin created.\n * @param protocolFeeRatioD3 Ratio of the swap fee that is kept for the\n * protocol.\n */\n struct State {\n uint128 reserveA;\n uint128 reserveB;\n int64 lastTwaD8;\n int64 lastLogPriceD8;\n uint40 lastTimestamp;\n int32 activeTick;\n bool isLocked;\n uint32 binCounter;\n uint8 protocolFeeRatioD3;\n }\n\n /**\n * @notice Internal data used for data passing between Pool and Bin code.\n */\n struct BinDelta {\n uint128 deltaA;\n uint128 deltaB;\n }\n\n /**\n * @notice 1-15 number to represent the active kinds.\n * @notice 0b0001 = static;\n * @notice 0b0010 = right;\n * @notice 0b0100 = left;\n * @notice 0b1000 = both;\n *\n * E.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function kinds() external view returns (uint8 _kinds);\n\n /**\n * @notice Pool swap fee for the given direction (A-in or B-in swap) in\n * 18-decimal format. E.g. 0.01e18 is a 1% swap fee.\n */\n function fee(bool tokenAIn) external view returns (uint256);\n\n /**\n * @notice TickSpacing of pool where 1.0001^tickSpacing is the bin width.\n */\n function tickSpacing() external view returns (uint256);\n\n /**\n * @notice Lookback period of pool in seconds.\n */\n function lookback() external view returns (uint256);\n\n /**\n * @notice Address of Pool accessor. This is Zero address for\n * permissionless pools.\n */\n function accessor() external view returns (address);\n\n /**\n * @notice Pool tokenA. Address of tokenA is such that tokenA < tokenB.\n */\n function tokenA() external view returns (IERC20);\n\n /**\n * @notice Pool tokenB.\n */\n function tokenB() external view returns (IERC20);\n\n /**\n * @notice Deploying factory of the pool and also contract that has ability\n * to set and collect protocol fees for the pool.\n */\n function factory() external view returns (IMaverickV2Factory);\n\n /**\n * @notice Most significant bit of scale value is a flag to indicate whether\n * tokenA has more or less than 18 decimals. Scale is used in conjuction\n * with Math.toScale/Math.fromScale functions to convert from token amounts\n * to D18 scale internal pool accounting.\n */\n function tokenAScale() external view returns (uint256);\n\n /**\n * @notice Most significant bit of scale value is a flag to indicate whether\n * tokenA has more or less than 18 decimals. Scale is used in conjuction\n * with Math.toScale/Math.fromScale functions to convert from token amounts\n * to D18 scale internal pool accounting.\n */\n function tokenBScale() external view returns (uint256);\n\n /**\n * @notice ID of bin at input tick position and kind.\n */\n function binIdByTickKind(int32 tick, uint256 kind)\n external\n view\n returns (uint32);\n\n /**\n * @notice Accumulated tokenA protocol fee.\n */\n function protocolFeeA() external view returns (uint128);\n\n /**\n * @notice Accumulated tokenB protocol fee.\n */\n function protocolFeeB() external view returns (uint128);\n\n /**\n * @notice Lending fee rate on flash loans.\n */\n function lendingFeeRateD18() external view returns (uint256);\n\n /**\n * @notice External function to get the current time-weighted average price.\n */\n function getCurrentTwa() external view returns (int256);\n\n /**\n * @notice External function to get the state of the pool.\n */\n function getState() external view returns (State memory);\n\n /**\n * @notice Return state of Bin at input binId.\n */\n function getBin(uint32 binId) external view returns (BinState memory bin);\n\n /**\n * @notice Return state of Tick at input tick position.\n */\n function getTick(int32 tick)\n external\n view\n returns (TickState memory tickState);\n\n /**\n * @notice Retrieves the balance of a user within a bin.\n * @param user The user's address.\n * @param subaccount The subaccount for the user.\n * @param binId The ID of the bin.\n */\n function balanceOf(\n address user,\n uint256 subaccount,\n uint32 binId\n ) external view returns (uint128 lpToken);\n\n /**\n * @notice Add liquidity to a pool. This function allows users to deposit\n * tokens into a liquidity pool.\n * @dev This function will call `maverickV2AddLiquidityCallback` on the\n * calling contract to collect the tokenA/tokenB payment.\n * @param recipient The account that will receive credit for the added liquidity.\n * @param subaccount The account that will receive credit for the added liquidity.\n * @param params Parameters containing the details for adding liquidity,\n * such as token types and amounts.\n * @param data Bytes information that gets passed to the callback.\n * @return tokenAAmount The amount of token A added to the pool.\n * @return tokenBAmount The amount of token B added to the pool.\n * @return binIds An array of bin IDs where the liquidity is stored.\n */\n function addLiquidity(\n address recipient,\n uint256 subaccount,\n AddLiquidityParams calldata params,\n bytes calldata data\n )\n external\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds\n );\n\n /**\n * @notice Removes liquidity from the pool.\n * @dev Liquidy can only be removed from a bin that is either unmerged or\n * has a mergeId of an unmerged bin. If a bin is merged more than one\n * level deep, it must be migrated up the merge stack to the root bin\n * before liquidity removal.\n * @param recipient The address to receive the tokens.\n * @param subaccount The subaccount for the recipient.\n * @param params The parameters for removing liquidity.\n * @return tokenAOut The amount of token A received.\n * @return tokenBOut The amount of token B received.\n */\n function removeLiquidity(\n address recipient,\n uint256 subaccount,\n RemoveLiquidityParams calldata params\n ) external returns (uint256 tokenAOut, uint256 tokenBOut);\n\n /**\n * @notice Migrate bins up the linked list of merged bins so that its\n * mergeId is the currrent active bin.\n * @dev Liquidy can only be removed from a bin that is either unmerged or\n * has a mergeId of an unmerged bin. If a bin is merged more than one\n * level deep, it must be migrated up the merge stack to the root bin\n * before liquidity removal.\n * @param binId The ID of the bin to migrate.\n * @param maxRecursion The maximum recursion depth for the migration.\n */\n function migrateBinUpStack(uint32 binId, uint32 maxRecursion) external;\n\n /**\n * @notice Swap tokenA/tokenB assets in the pool. The swap user has two\n * options for funding their swap.\n * - The user can push the input token amount to the pool before calling\n * the swap function. In order to avoid having the pool call the callback,\n * the user should pass a zero-length `data` bytes object with the swap\n * call.\n * - The user can send the input token amount to the pool when the pool\n * calls the `maverickV2SwapCallback` function on the calling contract.\n * That callback has input parameters that specify the token address of the\n * input token, the input and output amounts, and the bytes data sent to\n * the swap function.\n * @dev If the users elects to do a callback-based swap, the output\n * assets will be sent before the callback is called, allowing the user to\n * execute flash swaps. However, the pool does have reentrancy protection,\n * so a swapper will not be able to interact with the same pool again\n * while they are in the callback function.\n * @param recipient The address to receive the output tokens.\n * @param params Parameters containing the details of the swap\n * @param data Bytes information that gets passed to the callback.\n */\n function swap(\n address recipient,\n SwapParams memory params,\n bytes calldata data\n ) external returns (uint256 amountIn, uint256 amountOut);\n\n /**\n * @notice Loan tokenA/tokenB assets from the pool to recipient. The fee\n * rate of a loan is determined by `lendingFeeRateD18`, which is set at the\n * protocol level by the factory. This function calls\n * `maverickV2FlashLoanCallback` on the calling contract. At the end of\n * the callback, the caller must pay back the loan with fee (if there is a\n * fee).\n * @param recipient The address to receive the loaned tokens.\n * @param amountB Loan amount of tokenA sent to recipient.\n * @param amountB Loan amount of tokenB sent to recipient.\n * @param data Bytes information that gets passed to the callback.\n */\n function flashLoan(\n address recipient,\n uint256 amountA,\n uint256 amountB,\n bytes calldata data\n ) external;\n\n /**\n * @notice Distributes accumulated protocol fee to factory protocolFeeReceiver\n */\n function distributeFees(bool isTokenA)\n external\n returns (uint256 protocolFee, IERC20 token);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2PoolLens.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2PoolLens {\n error LensTargetPriceOutOfBounds(\n uint256 targetSqrtPrice,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n );\n error LensTooLittleLiquidity(\n uint256 relativeLiquidityAmount,\n uint256 deltaA,\n uint256 deltaB\n );\n error LensTargetingTokenWithNoDelta(\n bool targetIsA,\n uint256 deltaA,\n uint256 deltaB\n );\n\n /**\n * @notice Add liquidity slippage parameters for a distribution of liquidity.\n * @param pool Pool where liquidity is being added.\n * @param kind Bin kind; all bins must have the same kind in a given call\n * to addLiquidity.\n * @param ticks Array of tick values to add liquidity to.\n * @param relativeLiquidityAmounts Relative liquidity amounts for the\n * specified ticks. Liquidity in this case is not bin LP balance, it is\n * the bin liquidity as defined by liquidity = deltaA / (sqrt(upper) -\n * sqrt(lower)) or deltaB = liquidity / sqrt(lower) - liquidity /\n * sqrt(upper).\n * @param addSpec Slippage specification.\n */\n struct AddParamsViewInputs {\n IMaverickV2Pool pool;\n uint8 kind;\n int32[] ticks;\n uint128[] relativeLiquidityAmounts;\n AddParamsSpecification addSpec;\n }\n\n /**\n * @notice Multi-price add param specification.\n * @param slippageFactorD18 Max slippage allowed as a percent in D18 scale. e.g. 1% slippage is 0.01e18\n * @param numberOfPriceBreaksPerSide Number of price break values on either\n * side of current price.\n * @param targetAmount Target token contribution amount in tokenA if\n * targetIsA is true, otherwise this is the target amount for tokenB.\n * @param targetIsA Indicates if the target amount is for tokenA or tokenB\n */\n struct AddParamsSpecification {\n uint256 slippageFactorD18;\n uint256 numberOfPriceBreaksPerSide;\n uint256 targetAmount;\n bool targetIsA;\n }\n\n /**\n * @notice Specification for deriving create pool parameters. Creating a\n * pool in the liquidity manager has several steps:\n *\n * - Deploy pool\n * - Donate a small amount of initial liquidity in the activeTick\n * - Execute a small swap to set the pool price to the desired value\n * - Add liquidity\n *\n * In order to execute these steps, the caller must specify the parameters\n * of each step. The PoolLens has helper function to derive the values\n * used by the LiquidityManager, but this struct is the input to that\n * helper function and represents the core intent of the pool creator.\n *\n * @param fee Fraction of the pool swap amount that is retained as an LP in\n * D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * e.g. a pool with all 4 modes will have kinds = b1111 = 15\n * @param initialTargetB Amount of B to be donated to the pool after pool\n * create. This amount needs to be big enough to meet the minimum bin\n * liquidity.\n * @param sqrtPrice Target sqrt price of the pool.\n * @param kind Bin kind; all bins must have the same kind in a given call\n * to addLiquidity.\n * @param ticks Array of tick values to add liquidity to.\n * @param relativeLiquidityAmounts Relative liquidity amounts for the\n * specified ticks. Liquidity in this case is not bin LP balance, it is\n * the bin liquidity as defined by liquidity = deltaA / (sqrt(upper) -\n * sqrt(lower)) or deltaB = liquidity / sqrt(lower) - liquidity /\n * sqrt(upper).\n * @param targetAmount Target token contribution amount in tokenA if\n * targetIsA is true, otherwise this is the target amount for tokenB.\n * @param targetIsA Indicates if the target amount is for tokenA or tokenB\n */\n struct CreateAndAddParamsViewInputs {\n uint64 feeAIn;\n uint64 feeBIn;\n uint16 tickSpacing;\n uint32 lookback;\n IERC20 tokenA;\n IERC20 tokenB;\n int32 activeTick;\n uint8 kinds;\n // donate params\n uint256 initialTargetB;\n uint256 sqrtPrice;\n // add target\n uint8 kind;\n int32[] ticks;\n uint128[] relativeLiquidityAmounts;\n uint256 targetAmount;\n bool targetIsA;\n }\n\n struct Output {\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256[] deltaAs;\n uint256[] deltaBs;\n uint128[] deltaLpBalances;\n }\n\n struct Reserves {\n uint256 amountA;\n uint256 amountB;\n }\n\n struct BinPositionKinds {\n uint128[4] values;\n }\n\n struct PoolState {\n IMaverickV2Pool.TickState[] tickStateMapping;\n IMaverickV2Pool.BinState[] binStateMapping;\n BinPositionKinds[] binIdByTickKindMapping;\n IMaverickV2Pool.State state;\n Reserves protocolFees;\n }\n\n struct BoostedPositionSpecification {\n IMaverickV2Pool pool;\n uint32[] binIds;\n uint128[] ratios;\n uint8 kind;\n }\n\n struct CreateAndAddParamsInputs {\n uint64 feeAIn;\n uint64 feeBIn;\n uint16 tickSpacing;\n uint32 lookback;\n IERC20 tokenA;\n IERC20 tokenB;\n int32 activeTick;\n uint8 kinds;\n // donate params\n IMaverickV2Pool.AddLiquidityParams donateParams;\n // swap params\n uint256 swapAmount;\n // add params\n IMaverickV2Pool.AddLiquidityParams addParams;\n bytes[] packedAddParams;\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256 preAddReserveA;\n uint256 preAddReserveB;\n }\n\n struct TickDeltas {\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256[] deltaAs;\n uint256[] deltaBs;\n }\n\n /**\n * @notice Converts add parameter slippage specification into add\n * parameters. The return values are given in both raw format and as packed\n * values that can be used in the LiquidityManager contract.\n */\n function getAddLiquidityParams(AddParamsViewInputs memory params)\n external\n view\n returns (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint88[] memory sqrtPriceBreaks,\n IMaverickV2Pool.AddLiquidityParams[] memory addParams,\n IMaverickV2PoolLens.TickDeltas[] memory tickDeltas\n );\n\n /**\n * @notice Converts add parameter slippage specification and new pool\n * specification into CreateAndAddParamsInputs parameters that can be used in the\n * LiquidityManager contract.\n */\n function getCreatePoolAtPriceAndAddLiquidityParams(\n CreateAndAddParamsViewInputs memory params\n ) external view returns (CreateAndAddParamsInputs memory output);\n\n /**\n * @notice View function that provides information about pool ticks within\n * a tick radius from the activeTick. Ticks with no reserves are not\n * included in part o f the return array.\n */\n function getTicksAroundActive(IMaverickV2Pool pool, int32 tickRadius)\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates\n );\n\n /**\n * @notice View function that provides information about pool ticks within\n * a range. Ticks with no reserves are not included in part o f the return\n * array.\n */\n function getTicks(\n IMaverickV2Pool pool,\n int32 tickStart,\n int32 tickEnd\n )\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates\n );\n\n /**\n * @notice View function that provides information about pool ticks within\n * a range. Information returned includes all pool state needed to emulate\n * a swap off chain. Ticks with no reserves are not included in part o f\n * the return array.\n */\n function getTicksAroundActiveWLiquidity(\n IMaverickV2Pool pool,\n int32 tickRadius\n )\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates,\n uint256[] memory liquidities,\n uint256[] memory sqrtLowerTickPrices,\n uint256[] memory sqrtUpperTickPrices,\n IMaverickV2Pool.State memory poolState,\n uint256 sqrtPrice,\n uint256 feeAIn,\n uint256 feeBIn\n );\n\n /**\n * @notice View function that provides pool state information.\n */\n function getFullPoolState(\n IMaverickV2Pool pool,\n uint32 binStart,\n uint32 binEnd\n ) external view returns (PoolState memory poolState);\n\n /**\n * @notice View function that provides price and liquidity of a given tick.\n */\n function getTickSqrtPriceAndL(IMaverickV2Pool pool, int32 tick)\n external\n view\n returns (uint256 sqrtPrice, uint256 liquidity);\n\n /**\n * @notice Pool sqrt price.\n */\n function getPoolSqrtPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 sqrtPrice);\n\n /**\n * @notice Pool price.\n */\n function getPoolPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 price);\n\n /**\n * @notice Token scale of two tokens in a pool.\n */\n function tokenScales(IMaverickV2Pool pool)\n external\n view\n returns (uint256 tokenAScale, uint256 tokenBScale);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Position.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\nimport { ILiquidityRegistry } from \"./ILiquidityRegistry.sol\";\n\ninterface IMaverickV2Position {\n event PositionClearData(uint256 indexed tokenId);\n event PositionSetData(\n uint256 indexed tokenId,\n uint256 index,\n PositionPoolBinIds newData\n );\n event SetLpReward(ILiquidityRegistry lpReward);\n\n error PositionDuplicatePool(uint256 index, IMaverickV2Pool pool);\n error PositionNotFactoryPool();\n error PositionPermissionedLiquidityPool();\n\n struct PositionPoolBinIds {\n IMaverickV2Pool pool;\n uint32[] binIds;\n }\n\n struct PositionFullInformation {\n PositionPoolBinIds poolBinIds;\n uint256 amountA;\n uint256 amountB;\n uint256[] binAAmounts;\n uint256[] binBAmounts;\n int32[] ticks;\n uint256[] liquidities;\n }\n\n /**\n * @notice Factory that tracks lp rewards.\n */\n function lpReward() external view returns (ILiquidityRegistry);\n\n /**\n * @notice Pool factory.\n */\n function factory() external view returns (IMaverickV2Factory);\n\n /**\n * @notice Mint NFT that holds liquidity in a Maverick V2 Pool. To mint\n * liquidity to an NFT, add liquidity to bins in a pool where the\n * add liquidity recipient is this contract and the subaccount is the\n * tokenId. LiquidityManager can be used to simplify minting Position NFTs.\n */\n function mint(\n address recipient,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external returns (uint256 tokenId);\n\n /**\n * @notice Overwrites tokenId pool/binId information for a given data index.\n */\n function setTokenIdData(\n uint256 tokenId,\n uint256 index,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n\n /**\n * @notice Overwrites entire pool/binId data set for a given tokenId.\n */\n function setTokenIdData(uint256 tokenId, PositionPoolBinIds[] memory data)\n external;\n\n /**\n * @notice Append new pool/binIds data array to tokenId.\n */\n function appendTokenIdData(\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n\n /**\n * @notice Get array pool/binIds data for a given tokenId.\n */\n function getTokenIdData(uint256 tokenId)\n external\n view\n returns (PositionPoolBinIds[] memory);\n\n /**\n * @notice Get value from array of pool/binIds data for a given tokenId.\n */\n function getTokenIdData(uint256 tokenId, uint256 index)\n external\n view\n returns (PositionPoolBinIds memory);\n\n /**\n * @notice Length of array of pool/binIds data for a given tokenId.\n */\n function tokenIdDataLength(uint256 tokenId)\n external\n view\n returns (uint256 length);\n\n /**\n * @notice Remove liquidity from tokenId for a given pool. User can\n * specify arbitrary bins to remove from for their subaccount in the pool\n * even if those bins are not in the tokenIdData set.\n */\n function removeLiquidity(\n uint256 tokenId,\n address recipient,\n IMaverickV2Pool pool,\n IMaverickV2Pool.RemoveLiquidityParams memory params\n ) external returns (uint256 tokenAAmount, uint256 tokenBAmount);\n\n /**\n * @notice Remove liquidity from tokenId for a given pool to sender. User\n * can specify arbitrary bins to remove from for their subaccount in the\n * pool even if those bins are not in the tokenIdData set.\n */\n function removeLiquidityToSender(\n uint256 tokenId,\n IMaverickV2Pool pool,\n IMaverickV2Pool.RemoveLiquidityParams memory params\n ) external returns (uint256 tokenAAmount, uint256 tokenBAmount);\n\n /**\n * @notice NFT asset information for a given range of pool/binIds indexes.\n * This function only returns the liquidity in the pools/binIds stored as\n * part of the tokenIdData, but it is possible that the NFT has additional\n * liquidity in pools/binIds that have not been recorded.\n */\n function tokenIdPositionInformation(\n uint256 tokenId,\n uint256 startIndex,\n uint256 stopIndex\n ) external view returns (PositionFullInformation[] memory output);\n\n /**\n * @notice NFT asset information for a given pool/binIds index. This\n * function only returns the liquidity in the pools/binIds stored as part\n * of the tokenIdData, but it is possible that the NFT has additional\n * liquidity in pools/binIds that have not been recorded.\n */\n function tokenIdPositionInformation(uint256 tokenId, uint256 index)\n external\n view\n returns (PositionFullInformation memory output);\n\n /**\n * @notice Get remove parameters for removing a fractional part of the\n * liquidity owned by a given tokenId. The fractional factor to remove is\n * given by proporationD18 in 18-decimal scale.\n */\n function getRemoveParams(\n uint256 tokenId,\n uint256 index,\n uint256 proportionD18\n )\n external\n view\n returns (IMaverickV2Pool.RemoveLiquidityParams memory params);\n\n /**\n * @notice Register the bin balances in the nft with the LpReward contract.\n */\n function checkpointBinLpBalance(\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Quoter.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2Quoter {\n error QuoterInvalidSwap();\n error QuoterInvalidAddLiquidity();\n\n /**\n * @notice Calculates a swap on a MaverickV2Pool and returns the resulting\n * amount and estimated gas. The gas estimate is only a rough estimate and\n * may not match a swap's gas.\n * @param pool The MaverickV2Pool to swap on.\n * @param amount The input amount.\n * @param tokenAIn Indicates if token A is the input token.\n * @param exactOutput Indicates if the amount is the output amount (true)\n * or input amount (false). If the tickLimit is reached, the full value of\n * the exactOutput may not be returned because the pool will stop swapping\n * before the whole order is filled.\n * @param tickLimit The tick limit for the swap. Once the swap lands in\n * this tick, it will stop and return the output amount swapped up to that\n * tick.\n */\n function calculateSwap(\n IMaverickV2Pool pool,\n uint128 amount,\n bool tokenAIn,\n bool exactOutput,\n int32 tickLimit\n )\n external\n returns (\n uint256 amountIn,\n uint256 amountOut,\n uint256 gasEstimate\n );\n\n /**\n * @notice Calculates a multihop swap and returns the resulting amount and\n * estimated gas. The gas estimate is only a rough estimate and\n * may not match a swap's gas.\n * @param path The path of pools to swap through. Path is given by an\n * packed array of (pool, tokenAIn) tuples. So each step in the path is 160\n * + 8 = 168 bits of data. e.g. path = abi.encodePacked(pool1, true, pool2, false);\n * @param amount The input amount.\n * @param exactOutput A boolean indicating if exact output is required.\n */\n function calculateMultiHopSwap(\n bytes memory path,\n uint256 amount,\n bool exactOutput\n ) external returns (uint256 returnAmount, uint256 gasEstimate);\n\n /**\n * @notice Computes the token amounts required for a given set of\n * addLiquidity parameters. The gas estimate is only a rough estimate and\n * may not match a add's gas.\n */\n function calculateAddLiquidity(\n IMaverickV2Pool pool,\n IMaverickV2Pool.AddLiquidityParams calldata params\n )\n external\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 gasEstimate\n );\n\n /**\n * @notice Pool's sqrt price.\n */\n function poolSqrtPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 sqrtPrice);\n}\n" + }, + "contracts/interfaces/plume/IPoolDistributor.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.0;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IPoolDistributor {\n function rewardToken() external view returns (address);\n\n function claimLp(\n address recipient,\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds,\n uint256 epoch\n ) external returns (uint256 amount);\n}\n" + }, + "contracts/interfaces/plume/IVotingDistributor.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.0;\n\ninterface IVotingDistributor {\n function lastEpoch() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/sonic/ISFC.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\n\n/**\n * @title Special Fee Contract for Sonic network\n * @notice The SFC maintains a list of validators and delegators and distributes rewards to them.\n * @custom:security-contact security@fantom.foundation\n */\ninterface ISFC {\n error StakeIsFullySlashed();\n\n event CreatedValidator(\n uint256 indexed validatorID,\n address indexed auth,\n uint256 createdEpoch,\n uint256 createdTime\n );\n event Delegated(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 amount\n );\n event Undelegated(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 indexed wrID,\n uint256 amount\n );\n event Withdrawn(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 indexed wrID,\n uint256 amount,\n uint256 penalty\n );\n event ClaimedRewards(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 rewards\n );\n event RestakedRewards(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 rewards\n );\n event BurntFTM(uint256 amount);\n event UpdatedSlashingRefundRatio(\n uint256 indexed validatorID,\n uint256 refundRatio\n );\n event RefundedSlashedLegacyDelegation(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 amount\n );\n\n event DeactivatedValidator(\n uint256 indexed validatorID,\n uint256 deactivatedEpoch,\n uint256 deactivatedTime\n );\n event ChangedValidatorStatus(uint256 indexed validatorID, uint256 status);\n event AnnouncedRedirection(address indexed from, address indexed to);\n\n function currentSealedEpoch() external view returns (uint256);\n\n function getEpochSnapshot(uint256 epoch)\n external\n view\n returns (\n uint256 endTime,\n uint256 endBlock,\n uint256 epochFee,\n uint256 baseRewardPerSecond,\n uint256 totalStake,\n uint256 totalSupply\n );\n\n function getStake(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getValidator(uint256 validatorID)\n external\n view\n returns (\n uint256 status,\n uint256 receivedStake,\n address auth,\n uint256 createdEpoch,\n uint256 createdTime,\n uint256 deactivatedTime,\n uint256 deactivatedEpoch\n );\n\n function getValidatorID(address auth) external view returns (uint256);\n\n function getValidatorPubkey(uint256 validatorID)\n external\n view\n returns (bytes memory);\n\n function pubkeyAddressvalidatorID(address pubkeyAddress)\n external\n view\n returns (uint256);\n\n function getWithdrawalRequest(\n address delegator,\n uint256 validatorID,\n uint256 wrID\n )\n external\n view\n returns (\n uint256 epoch,\n uint256 time,\n uint256 amount\n );\n\n function isOwner() external view returns (bool);\n\n function lastValidatorID() external view returns (uint256);\n\n function minGasPrice() external view returns (uint256);\n\n function owner() external view returns (address);\n\n function renounceOwnership() external;\n\n function slashingRefundRatio(uint256 validatorID)\n external\n view\n returns (uint256);\n\n function stashedRewardsUntilEpoch(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function totalActiveStake() external view returns (uint256);\n\n function totalStake() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function transferOwnership(address newOwner) external;\n\n function treasuryAddress() external view returns (address);\n\n function version() external pure returns (bytes3);\n\n function currentEpoch() external view returns (uint256);\n\n function updateConstsAddress(address v) external;\n\n function constsAddress() external view returns (address);\n\n function getEpochValidatorIDs(uint256 epoch)\n external\n view\n returns (uint256[] memory);\n\n function getEpochReceivedStake(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochAccumulatedRewardPerToken(\n uint256 epoch,\n uint256 validatorID\n ) external view returns (uint256);\n\n function getEpochAccumulatedUptime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochAverageUptime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint32);\n\n function getEpochAccumulatedOriginatedTxsFee(\n uint256 epoch,\n uint256 validatorID\n ) external view returns (uint256);\n\n function getEpochOfflineTime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochOfflineBlocks(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochEndBlock(uint256 epoch) external view returns (uint256);\n\n function rewardsStash(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function createValidator(bytes calldata pubkey) external payable;\n\n function getSelfStake(uint256 validatorID) external view returns (uint256);\n\n function delegate(uint256 validatorID) external payable;\n\n function undelegate(\n uint256 validatorID,\n uint256 wrID,\n uint256 amount\n ) external;\n\n function isSlashed(uint256 validatorID) external view returns (bool);\n\n function withdraw(uint256 validatorID, uint256 wrID) external;\n\n function deactivateValidator(uint256 validatorID, uint256 status) external;\n\n function pendingRewards(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function stashRewards(address delegator, uint256 validatorID) external;\n\n function claimRewards(uint256 validatorID) external;\n\n function restakeRewards(uint256 validatorID) external;\n\n function updateSlashingRefundRatio(uint256 validatorID, uint256 refundRatio)\n external;\n\n function updateTreasuryAddress(address v) external;\n\n function burnFTM(uint256 amount) external;\n\n function sealEpoch(\n uint256[] calldata offlineTime,\n uint256[] calldata offlineBlocks,\n uint256[] calldata uptimes,\n uint256[] calldata originatedTxsFee\n ) external;\n\n function sealEpochValidators(uint256[] calldata nextValidatorIDs) external;\n\n function initialize(\n uint256 sealedEpoch,\n uint256 _totalSupply,\n address nodeDriver,\n address consts,\n address _owner\n ) external;\n\n function setGenesisValidator(\n address auth,\n uint256 validatorID,\n bytes calldata pubkey,\n uint256 createdTime\n ) external;\n\n function setGenesisDelegation(\n address delegator,\n uint256 validatorID,\n uint256 stake\n ) external;\n\n function updateStakeSubscriberAddress(address v) external;\n\n function stakeSubscriberAddress() external view returns (address);\n\n function setRedirectionAuthorizer(address v) external;\n\n function announceRedirection(address to) external;\n\n function initiateRedirection(address from, address to) external;\n\n function redirect(address to) external;\n}\n" + }, + "contracts/interfaces/sonic/ISwapXGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IGauge {\n function owner() external view returns (address);\n\n function TOKEN() external view returns (address);\n\n function DISTRIBUTION() external view returns (address);\n\n function balanceOf(address account) external view returns (uint256);\n\n function claimFees() external returns (uint256 claimed0, uint256 claimed1);\n\n function deposit(uint256 amount) external;\n\n function depositAll() external;\n\n function earned(address account) external view returns (uint256);\n\n function getReward() external;\n\n function getReward(address _user) external;\n\n function isForPair() external view returns (bool);\n\n function lastTimeRewardApplicable() external view returns (uint256);\n\n function lastUpdateTime() external view returns (uint256);\n\n function notifyRewardAmount(address token, uint256 reward) external;\n\n function periodFinish() external view returns (uint256);\n\n function rewardForDuration() external view returns (uint256);\n\n function rewardPerToken() external view returns (uint256);\n\n function rewardPerTokenStored() external view returns (uint256);\n\n function rewardRate() external view returns (uint256);\n\n function rewardToken() external view returns (address);\n\n function rewards(address) external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function userRewardPerTokenPaid(address) external view returns (uint256);\n\n function withdraw(uint256 amount) external;\n\n function withdrawAll() external;\n\n function withdrawAllAndHarvest() external;\n\n function withdrawExcess(address token, uint256 amount) external;\n\n function emergency() external returns (bool);\n\n function emergencyWithdraw() external;\n\n function activateEmergencyMode() external;\n\n function stopEmergencyMode() external;\n}\n" + }, + "contracts/interfaces/sonic/ISwapXPair.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IPair {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n\n event Mint(address indexed sender, uint256 amount0, uint256 amount1);\n event Burn(\n address indexed sender,\n uint256 amount0,\n uint256 amount1,\n address indexed to\n );\n event Swap(\n address indexed sender,\n uint256 amount0In,\n uint256 amount1In,\n uint256 amount0Out,\n uint256 amount1Out,\n address indexed to\n );\n event Claim(\n address indexed sender,\n address indexed recipient,\n uint256 amount0,\n uint256 amount1\n );\n\n function metadata()\n external\n view\n returns (\n uint256 dec0,\n uint256 dec1,\n uint256 r0,\n uint256 r1,\n bool st,\n address t0,\n address t1\n );\n\n function claimFees() external returns (uint256, uint256);\n\n function tokens() external view returns (address, address);\n\n function token0() external view returns (address);\n\n function token1() external view returns (address);\n\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function swap(\n uint256 amount0Out,\n uint256 amount1Out,\n address to,\n bytes calldata data\n ) external;\n\n function burn(address to)\n external\n returns (uint256 amount0, uint256 amount1);\n\n function mint(address to) external returns (uint256 liquidity);\n\n function getReserves()\n external\n view\n returns (\n uint256 _reserve0,\n uint256 _reserve1,\n uint256 _blockTimestampLast\n );\n\n function getAmountOut(uint256, address) external view returns (uint256);\n\n // ERC20 methods\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n\n function totalSupply() external view returns (uint256);\n\n function balanceOf(address) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 value) external returns (bool);\n\n function claimable0(address _user) external view returns (uint256);\n\n function claimable1(address _user) external view returns (uint256);\n\n function isStable() external view returns (bool);\n\n function skim(address to) external;\n\n function sync() external;\n}\n" + }, + "contracts/interfaces/sonic/IWrappedSonic.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWrappedSonic {\n event Deposit(address indexed account, uint256 value);\n event Withdrawal(address indexed account, uint256 value);\n event Transfer(address indexed from, address indexed to, uint256 value);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 value) external returns (bool);\n\n function balanceOf(address account) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function depositFor(address account) external payable returns (bool);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address to, uint256 value) external returns (bool);\n\n function transferFrom(\n address from,\n address to,\n uint256 value\n ) external returns (bool);\n\n function withdraw(uint256 value) external;\n\n function withdrawTo(address account, uint256 value) external returns (bool);\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV2Router02.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapV2Router {\n function WETH() external pure returns (address);\n\n function swapExactTokensForTokens(\n uint256 amountIn,\n uint256 amountOutMin,\n address[] calldata path,\n address to,\n uint256 deadline\n ) external returns (uint256[] memory amounts);\n\n function addLiquidity(\n address tokenA,\n address tokenB,\n uint256 amountADesired,\n uint256 amountBDesired,\n uint256 amountAMin,\n uint256 amountBMin,\n address to,\n uint256 deadline\n )\n external\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 liquidity\n );\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV3Router.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// -- Solididy v0.5.x compatible interface\ninterface IUniswapV3Router {\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n}\n" + }, + "contracts/mocks/beacon/ExecutionLayerConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { GeneralPurposeToConsensusLayerRequest } from \"./GeneralPurposeToConsensusLayerRequest.sol\";\n\ncontract ExecutionLayerConsolidation is GeneralPurposeToConsensusLayerRequest {\n event ConsolidationRequestIssued(bytes sourceKey, bytes targetKey);\n bytes public lastSource;\n bytes public lastTarget;\n\n function handleRequest(bytes calldata data) internal override {\n // parameters should consist of twice the 48 bytes for 2 public keys\n require(data.length == 96, \"Invalid Consolidation data\");\n lastSource = data[:48];\n lastTarget = data[48:];\n\n emit ConsolidationRequestIssued(lastSource, lastTarget);\n }\n}\n" + }, + "contracts/mocks/beacon/ExecutionLayerWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { GeneralPurposeToConsensusLayerRequest } from \"./GeneralPurposeToConsensusLayerRequest.sol\";\n\ncontract ExecutionLayerWithdrawal is GeneralPurposeToConsensusLayerRequest {\n event WithdrawalRequestIssued(bytes publicKey, uint64 amount);\n\n bytes public lastPublicKey;\n uint64 public lastAmount;\n\n function handleRequest(bytes calldata data) internal override {\n // parameters should consist of 48 bytes for public key and 8 bytes for uint64 amount\n require(data.length == 56, \"Invalid Withdrawal data\");\n lastPublicKey = data[:48];\n lastAmount = uint64(bytes8(data[48:]));\n emit WithdrawalRequestIssued(lastPublicKey, lastAmount);\n }\n}\n" + }, + "contracts/mocks/beacon/GeneralPurposeToConsensusLayerRequest.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nabstract contract GeneralPurposeToConsensusLayerRequest {\n // solhint-disable no-complex-fallback\n fallback() external payable {\n // fee requested\n if (msg.data.length == 0) {\n uint256 fee = _fee();\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Return a uint256 value\n mstore(0, fee)\n return(0, 32) // Return 32 bytes from memory\n }\n }\n\n // else handle request\n handleRequest(msg.data);\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _fee() internal virtual returns (uint256) {\n return 1;\n }\n\n function handleRequest(bytes calldata data) internal virtual;\n}\n" + }, + "contracts/mocks/MintableERC20.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ninterface IMintableERC20 {\n function mint(uint256 value) external;\n\n function mintTo(address to, uint256 value) external;\n}\n\n/**\n * @title MintableERC20\n * @dev Exposes the mint function of ERC20 for tests\n */\nabstract contract MintableERC20 is IMintableERC20, ERC20 {\n /**\n * @dev Function to mint tokens\n * @param _value The amount of tokens to mint.\n */\n function mint(uint256 _value) public virtual override {\n _mint(msg.sender, _value);\n }\n\n /**\n * @dev Function to mint tokens\n * @param _to Address to mint to.\n * @param _value The amount of tokens to mint.\n */\n function mintTo(address _to, uint256 _value) public virtual override {\n _mint(_to, _value);\n }\n}\n" + }, + "contracts/mocks/MockBalancerVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// import \"hardhat/console.sol\";\n\ncontract MockBalancerVault {\n using StableMath for uint256;\n uint256 public slippage = 1 ether;\n bool public transferDisabled = false;\n bool public slippageErrorDisabled = false;\n\n function swap(\n IBalancerVault.SingleSwap calldata singleSwap,\n IBalancerVault.FundManagement calldata funds,\n uint256 minAmountOut,\n uint256\n ) external returns (uint256 amountCalculated) {\n amountCalculated = (minAmountOut * slippage) / 1 ether;\n if (!slippageErrorDisabled) {\n require(amountCalculated >= minAmountOut, \"Slippage error\");\n }\n IERC20(singleSwap.assetIn).transferFrom(\n funds.sender,\n address(this),\n singleSwap.amount\n );\n if (!transferDisabled) {\n MintableERC20(singleSwap.assetOut).mintTo(\n funds.recipient,\n amountCalculated\n );\n }\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n\n function getPoolTokenInfo(bytes32 poolId, address token)\n external\n view\n returns (\n uint256,\n uint256,\n uint256,\n address\n )\n {}\n\n function disableTransfer() external {\n transferDisabled = true;\n }\n\n function disableSlippageError() external {\n slippageErrorDisabled = true;\n }\n}\n" + }, + "contracts/mocks/MockBeaconConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconConsolidation } from \"../beacon/BeaconConsolidation.sol\";\n\ncontract MockBeaconConsolidation {\n function fee() external view returns (uint256) {\n return BeaconConsolidation.fee();\n }\n\n function request(bytes calldata source, bytes calldata target)\n external\n returns (uint256 fee_)\n {\n return BeaconConsolidation.request(source, target);\n }\n}\n" + }, + "contracts/mocks/MockBeaconOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconOracle } from \"../beacon/BeaconOracle.sol\";\n\ncontract MockBeaconOracle is BeaconOracle {\n function mapSlot(\n uint64 blockNumber,\n uint64 slot,\n bytes32 _blockRoot\n ) external {\n _blockToSlot[blockNumber] = slot;\n _slotToBlock[slot] = blockNumber;\n _slotToRoot[slot] = _blockRoot;\n }\n}\n" + }, + "contracts/mocks/MockBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"../beacon/BeaconProofsLib.sol\";\n\ncontract MockBeaconProofs {\n function concatGenIndices(\n uint256 index1,\n uint256 height2,\n uint256 index2\n ) external pure returns (uint256 genIndex) {\n return BeaconProofsLib.concatGenIndices(index1, height2, index2);\n }\n\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex\n ) external view {\n BeaconProofsLib.verifyValidatorPubkey(\n beaconBlockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex\n );\n }\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view {\n BeaconProofsLib.verifyBalancesContainer(\n beaconBlockRoot,\n balancesContainerLeaf,\n balancesContainerProof\n );\n }\n\n function verifyValidatorBalanceInContainer(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalance) {\n return\n BeaconProofsLib.verifyValidatorBalance(\n balancesContainerRoot,\n validatorBalanceLeaf,\n balanceProof,\n validatorIndex,\n BeaconProofsLib.BalanceProofLevel.Container\n );\n }\n\n function verifyValidatorBalanceInBeaconBlock(\n bytes32 beaconBlockRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalance) {\n return\n BeaconProofsLib.verifyValidatorBalance(\n beaconBlockRoot,\n validatorBalanceLeaf,\n balanceProof,\n validatorIndex,\n BeaconProofsLib.BalanceProofLevel.BeaconBlock\n );\n }\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n internal\n pure\n returns (uint256)\n {\n return\n BeaconProofsLib.balanceAtIndex(\n validatorBalanceLeaf,\n validatorIndex\n );\n }\n\n function verifyFirstPendingDepositSlot(\n bytes32 blockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view {\n BeaconProofsLib.verifyFirstPendingDepositSlot(\n blockRoot,\n slot,\n firstPendingDepositSlotProof\n );\n }\n\n function verifyBlockNumber(\n bytes32 beaconBlockRoot,\n uint256 blockNumber,\n bytes calldata blockNumberProof\n ) external view {\n BeaconProofsLib.verifyBlockNumber(\n beaconBlockRoot,\n blockNumber,\n blockNumberProof\n );\n }\n\n function verifySlot(\n bytes32 beaconBlockRoot,\n uint256 slot,\n bytes calldata slotProof\n ) external view {\n BeaconProofsLib.verifySlot(beaconBlockRoot, slot, slotProof);\n }\n}\n" + }, + "contracts/mocks/MockBeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconRoots } from \"../beacon/BeaconRoots.sol\";\n\ncontract MockBeaconRoots {\n // Mapping to simulate the ring buffer: timestamp => beacon block root\n mapping(uint256 => bytes32) internal _beaconRoots;\n\n // Event to log when a new root is set (for testing)\n event RootSet(uint256 indexed timestamp, bytes32 root);\n\n // Fallback function to handle raw 32-byte timestamp input\n // solhint-disable no-complex-fallback\n fallback() external {\n // Ensure input is exactly 32 bytes (big-endian encoded timestamp)\n require(msg.data.length == 32, \"Input must be 32 bytes\");\n\n // Decode the 32-byte input as a uint256 timestamp (big-endian)\n uint256 timestamp = abi.decode(msg.data, (uint256));\n\n // Don't do any validation of timestamp so we can test any block\n\n // Retrieve the root. Will return bytes32(0) if not set.\n bytes32 root = _beaconRoots[timestamp];\n\n // Return the 32-byte root directly\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0, root)\n return(0, 32)\n }\n }\n\n // Mock function to set a beacon block root (for testing)\n function setBeaconRoot(uint256 timestamp, bytes32 root) external {\n require(timestamp > 0, \"Invalid timestamp\");\n require(root != bytes32(0), \"Invalid root\");\n\n // Store the root at the given timestamp\n _beaconRoots[timestamp] = root;\n\n emit RootSet(timestamp, root);\n }\n\n function setBeaconRoot(bytes32 root) external {\n require(root != bytes32(0), \"Invalid root\");\n\n // Store the root at the given timestamp\n _beaconRoots[block.timestamp] = root;\n\n emit RootSet(block.timestamp, root);\n }\n\n function parentBlockRoot(uint64 timestamp)\n external\n view\n returns (bytes32 parentRoot)\n {\n return BeaconRoots.parentBlockRoot(timestamp);\n }\n\n function latestBlockRoot()\n external\n view\n returns (bytes32 parentRoot, uint64 timestamp)\n {\n timestamp = uint64(block.timestamp);\n parentRoot = BeaconRoots.parentBlockRoot(timestamp);\n }\n}\n" + }, + "contracts/mocks/MockEvilDAI.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract MockEvilDAI is MintableERC20 {\n address host;\n address realCoin;\n\n constructor(address _host, address _realCoin) ERC20(\"DAI\", \"DAI\") {\n host = _host;\n realCoin = _realCoin;\n }\n\n function transferFrom(\n // solhint-disable-next-line no-unused-vars\n address _from,\n // solhint-disable-next-line no-unused-vars\n address _to,\n uint256 _amount\n ) public override returns (bool) {\n // call mint again!\n if (_amount != 69) {\n IVault(host).mint(address(this), 69, 0);\n }\n return true;\n }\n}\n" + }, + "contracts/mocks/MockEvilReentrantContract.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IRateProvider } from \"../interfaces/balancer/IRateProvider.sol\";\n\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { IERC20 } from \"../utils/InitializableAbstractStrategy.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockEvilReentrantContract {\n using StableMath for uint256;\n\n IBalancerVault public immutable balancerVault;\n IERC20 public immutable reth;\n IERC20 public immutable weth;\n IVault public immutable oethVault;\n address public immutable poolAddress;\n bytes32 public immutable balancerPoolId;\n\n constructor(\n address _balancerVault,\n address _oethVault,\n address _reth,\n address _weth,\n address _poolAddress,\n bytes32 _poolId\n ) {\n balancerVault = IBalancerVault(_balancerVault);\n oethVault = IVault(_oethVault);\n reth = IERC20(_reth);\n weth = IERC20(_weth);\n poolAddress = _poolAddress;\n balancerPoolId = _poolId;\n }\n\n function doEvilStuff() public {\n address priceProvider = oethVault.priceProvider();\n uint256 rethPrice = IOracle(priceProvider).price(address(reth));\n\n // 1. Join pool\n uint256[] memory amounts = new uint256[](2);\n amounts[0] = uint256(10 ether);\n amounts[1] = rethPrice * 10;\n\n address[] memory assets = new address[](2);\n assets[0] = address(reth);\n assets[1] = address(weth);\n\n uint256 minBPT = getBPTExpected(assets, amounts).mulTruncate(\n 0.99 ether\n );\n\n bytes memory joinUserData = abi.encode(\n IBalancerVault.WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,\n amounts,\n minBPT\n );\n\n IBalancerVault.JoinPoolRequest memory joinRequest = IBalancerVault\n .JoinPoolRequest(assets, amounts, joinUserData, false);\n\n balancerVault.joinPool(\n balancerPoolId,\n address(this),\n address(this),\n joinRequest\n );\n\n uint256 bptTokenBalance = IERC20(poolAddress).balanceOf(address(this));\n\n // 2. Redeem as ETH\n bytes memory exitUserData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT,\n bptTokenBalance,\n 1\n );\n\n assets[1] = address(0); // Receive ETH instead of WETH\n uint256[] memory exitAmounts = new uint256[](2);\n exitAmounts[1] = 15 ether;\n IBalancerVault.ExitPoolRequest memory exitRequest = IBalancerVault\n .ExitPoolRequest(assets, exitAmounts, exitUserData, false);\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n payable(address(this)),\n exitRequest\n );\n bptTokenBalance = IERC20(poolAddress).balanceOf(address(this));\n }\n\n function getBPTExpected(address[] memory _assets, uint256[] memory _amounts)\n internal\n view\n virtual\n returns (uint256 bptExpected)\n {\n // Get the oracle from the OETH Vault\n address priceProvider = oethVault.priceProvider();\n\n for (uint256 i = 0; i < _assets.length; ++i) {\n uint256 strategyAssetMarketPrice = IOracle(priceProvider).price(\n _assets[i]\n );\n // convert asset amount to ETH amount\n bptExpected =\n bptExpected +\n _amounts[i].mulTruncate(strategyAssetMarketPrice);\n }\n\n uint256 bptRate = IRateProvider(poolAddress).getRate();\n // Convert ETH amount to BPT amount\n bptExpected = bptExpected.divPrecisely(bptRate);\n }\n\n function approveAllTokens() public {\n // Approve all tokens\n weth.approve(address(oethVault), type(uint256).max);\n reth.approve(poolAddress, type(uint256).max);\n weth.approve(poolAddress, type(uint256).max);\n reth.approve(address(balancerVault), type(uint256).max);\n weth.approve(address(balancerVault), type(uint256).max);\n }\n\n receive() external payable {\n // 3. Try to mint OETH\n oethVault.mint(address(weth), 1 ether, 0.9 ether);\n }\n}\n" + }, + "contracts/mocks/MockLimitedWrappedOusd.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WrappedOusd } from \"../token/WrappedOusd.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MockLimitedWrappedOusd is WrappedOusd {\n constructor(ERC20 underlying_) WrappedOusd(underlying_) {}\n\n function maxDeposit(address)\n public\n view\n virtual\n override\n returns (uint256)\n {\n return 1e18;\n }\n}\n" + }, + "contracts/mocks/MockNonRebasing.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { OUSD } from \"../token/OUSD.sol\";\n\ncontract MockNonRebasing {\n OUSD oUSD;\n\n function setOUSD(address _oUSDAddress) public {\n oUSD = OUSD(_oUSDAddress);\n }\n\n function rebaseOptIn() public {\n oUSD.rebaseOptIn();\n }\n\n function rebaseOptOut() public {\n oUSD.rebaseOptOut();\n }\n\n function transfer(address _to, uint256 _value) public {\n oUSD.transfer(_to, _value);\n }\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) public {\n oUSD.transferFrom(_from, _to, _value);\n }\n\n function approve(address _spender, uint256 _addedValue) public {\n oUSD.approve(_spender, _addedValue);\n }\n\n function mintOusd(\n address _vaultContract,\n address _asset,\n uint256 _amount\n ) public {\n IVault(_vaultContract).mint(_asset, _amount, 0);\n }\n\n function redeemOusd(address _vaultContract, uint256 _amount) public {\n IVault(_vaultContract).redeem(_amount, 0);\n }\n\n function approveFor(\n address _contract,\n address _spender,\n uint256 _addedValue\n ) public {\n IERC20(_contract).approve(_spender, _addedValue);\n }\n}\n" + }, + "contracts/mocks/MockOETHVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultCore } from \"../vault/OETHVaultCore.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MockOETHVault is OETHVaultCore {\n using StableMath for uint256;\n\n constructor(address _weth) OETHVaultCore(_weth) {\n _setGovernor(msg.sender);\n }\n\n function supportAsset(address asset) external {\n assets[asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(0),\n decimals: 18,\n allowedOracleSlippageBps: 0\n });\n\n allAssets.push(asset);\n }\n}\n" + }, + "contracts/mocks/MockOETHVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"../vault/OETHVaultAdmin.sol\";\n\ncontract MockOETHVaultAdmin is OETHVaultAdmin {\n constructor(address _weth) OETHVaultAdmin(_weth) {}\n\n // fetches the WETH amount in outstanding withdrawals\n function outstandingWithdrawalsAmount()\n external\n view\n returns (uint256 wethAmount)\n {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n wethAmount = queue.queued - queue.claimed;\n }\n\n function wethAvailable() external view returns (uint256) {\n return _wethAvailable();\n }\n}\n" + }, + "contracts/mocks/MockPartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PartialWithdrawal } from \"../beacon/PartialWithdrawal.sol\";\n\ncontract MockPartialWithdrawal {\n function fee() external view returns (uint256) {\n return PartialWithdrawal.fee();\n }\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n external\n returns (uint256 fee_)\n {\n return PartialWithdrawal.request(validatorPubKey, amount);\n }\n}\n" + }, + "contracts/mocks/MockRebornMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n// solhint-disable-next-line no-console\nimport \"hardhat/console.sol\";\n\ncontract Sanctum {\n address public asset;\n address public vault;\n address public reborner;\n bool public shouldAttack = false;\n // should selfdestruct in the constructor\n bool public shouldDestruct = false;\n uint256 public targetMethod;\n address public ousdContract;\n\n constructor(address _asset, address _vault) {\n asset = _asset;\n vault = _vault;\n }\n\n function deploy(uint256 salt, bytes memory bytecode)\n public\n returns (address addr)\n {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt)\n }\n require(addr != address(0), \"Create2: Failed on deploy\");\n }\n\n function computeAddress(uint256 salt, bytes memory bytecode)\n public\n view\n returns (address)\n {\n bytes32 bytecodeHashHash = keccak256(bytecode);\n bytes32 _data = keccak256(\n abi.encodePacked(\n bytes1(0xff),\n address(this),\n salt,\n bytecodeHashHash\n )\n );\n return address(bytes20(_data << 96));\n }\n\n function setShouldAttack(bool _shouldAttack) public {\n shouldAttack = _shouldAttack;\n }\n\n // should call selfdestruct in the constructor\n function setShouldDesctruct(bool _shouldDestruct) public {\n shouldDestruct = _shouldDestruct;\n }\n\n function setTargetMethod(uint256 target) public {\n targetMethod = target;\n }\n\n function setOUSDAddress(address _ousdContract) public {\n ousdContract = _ousdContract;\n }\n}\n\ncontract Reborner {\n Sanctum sanctum;\n bool logging = false;\n\n constructor(address _sanctum) {\n log(\"We are created...\");\n sanctum = Sanctum(_sanctum);\n if (sanctum.shouldAttack()) {\n log(\"We are attacking now...\");\n\n uint256 target = sanctum.targetMethod();\n\n if (target == 1) {\n redeem();\n } else if (target == 2) {\n transfer();\n } else {\n mint();\n }\n }\n\n if (sanctum.shouldDestruct()) {\n bye();\n }\n }\n\n function mint() public {\n log(\"We are attempting to mint..\");\n address asset = sanctum.asset();\n address vault = sanctum.vault();\n IERC20(asset).approve(vault, 1e18);\n IVault(vault).mint(asset, 1e18, 0);\n log(\"We are now minting..\");\n }\n\n function redeem() public {\n log(\"We are attempting to redeem..\");\n address vault = sanctum.vault();\n IVault(vault).redeem(1e18, 1e18);\n log(\"We are now redeeming..\");\n }\n\n function transfer() public {\n log(\"We are attempting to transfer..\");\n address ousd = sanctum.ousdContract();\n require(IERC20(ousd).transfer(address(1), 1e18), \"transfer failed\");\n log(\"We are now transfering..\");\n }\n\n function bye() public {\n log(\"We are now destructing..\");\n selfdestruct(payable(msg.sender));\n }\n\n function log(string memory message) internal view {\n if (logging) {\n // solhint-disable-next-line no-console\n console.log(message);\n }\n }\n}\n" + }, + "contracts/mocks/MockRoosterAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Rooster AMO strategy exposing extra functionality\n * @author Origin Protocol Inc\n */\n\nimport { RoosterAMOStrategy } from \"../strategies/plume/RoosterAMOStrategy.sol\";\nimport { IMaverickV2Pool } from \"../interfaces/plume/IMaverickV2Pool.sol\";\n\ncontract MockRoosterAMOStrategy is RoosterAMOStrategy {\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethpAddress,\n address _liquidityManager,\n address _poolLens,\n address _maverickPosition,\n address _maverickQuoter,\n address _mPool,\n bool _upperTickAtParity,\n address _votingDistributor,\n address _poolDistributor\n )\n RoosterAMOStrategy(\n _stratConfig,\n _wethAddress,\n _oethpAddress,\n _liquidityManager,\n _poolLens,\n _maverickPosition,\n _maverickQuoter,\n _mPool,\n _upperTickAtParity,\n _votingDistributor,\n _poolDistributor\n )\n {}\n\n function getCurrentWethShare() external view returns (uint256) {\n uint256 _currentPrice = getPoolSqrtPrice();\n\n return _getWethShare(_currentPrice);\n }\n}\n" + }, + "contracts/mocks/MockVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultCore } from \"../vault/VaultCore.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { VaultInitializer } from \"../vault/VaultInitializer.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MockVault is VaultCore {\n using StableMath for uint256;\n\n uint256 storedTotalValue;\n\n function setTotalValue(uint256 _value) public {\n storedTotalValue = _value;\n }\n\n function totalValue() external view override returns (uint256) {\n return storedTotalValue;\n }\n\n function _totalValue() internal view override returns (uint256) {\n return storedTotalValue;\n }\n\n function _checkBalance(address _asset)\n internal\n view\n override\n returns (uint256 balance)\n {\n // Avoids rounding errors by returning the total value\n // in a single currency\n if (allAssets[0] == _asset) {\n uint256 decimals = Helpers.getDecimals(_asset);\n return storedTotalValue.scaleBy(decimals, 18);\n } else {\n return 0;\n }\n }\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external onlyGovernor {\n maxSupplyDiff = _maxSupplyDiff;\n }\n}\n" + }, + "contracts/mocks/MockVaultCoreInstantRebase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultCore } from \"../vault/VaultCore.sol\";\n\ncontract MockVaultCoreInstantRebase is VaultCore {\n function _nextYield(uint256 supply, uint256 vaultValue)\n internal\n view\n override\n returns (uint256 yield, uint256 targetRate)\n {\n if (vaultValue <= supply) {\n return (0, 0);\n }\n yield = vaultValue - supply;\n return (yield, 0);\n }\n}\n" + }, + "contracts/mocks/TestUpgradedOUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../token/OUSD.sol\";\n\n// used to alter internal state of OUSD contract\ncontract TestUpgradedOUSD is OUSD {\n constructor() OUSD() {}\n\n function overwriteCreditBalances(address _account, uint256 _creditBalance)\n public\n {\n creditBalances[_account] = _creditBalance;\n }\n\n function overwriteAlternativeCPT(address _account, uint256 _acpt) public {\n alternativeCreditsPerToken[_account] = _acpt;\n }\n\n function overwriteRebaseState(address _account, RebaseOptions _rebaseOption)\n public\n {\n rebaseState[_account] = _rebaseOption;\n }\n}\n" + }, + "contracts/proxies/InitializeGovernedUpgradeabilityProxy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { Governable } from \"../governance/Governable.sol\";\n\n/**\n * @title BaseGovernedUpgradeabilityProxy\n * @dev This contract combines an upgradeability proxy with our governor system.\n * It is based on an older version of OpenZeppelins BaseUpgradeabilityProxy\n * with Solidity ^0.8.0.\n * @author Origin Protocol Inc\n */\ncontract InitializeGovernedUpgradeabilityProxy is Governable {\n /**\n * @dev Emitted when the implementation is upgraded.\n * @param implementation Address of the new implementation.\n */\n event Upgraded(address indexed implementation);\n\n constructor() {\n _setGovernor(msg.sender);\n }\n\n /**\n * @dev Contract initializer with Governor enforcement\n * @param _logic Address of the initial implementation.\n * @param _initGovernor Address of the initial Governor.\n * @param _data Data to send as msg.data to the implementation to initialize\n * the proxied contract.\n * It should include the signature and the parameters of the function to be\n * called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n * This parameter is optional, if no data is given the initialization call\n * to proxied contract will be skipped.\n */\n function initialize(\n address _logic,\n address _initGovernor,\n bytes calldata _data\n ) public payable onlyGovernor {\n require(_implementation() == address(0));\n require(_logic != address(0), \"Implementation not set\");\n assert(\n IMPLEMENTATION_SLOT ==\n bytes32(uint256(keccak256(\"eip1967.proxy.implementation\")) - 1)\n );\n _setImplementation(_logic);\n if (_data.length > 0) {\n (bool success, ) = _logic.delegatecall(_data);\n require(success);\n }\n _changeGovernor(_initGovernor);\n }\n\n /**\n * @return The address of the proxy admin/it's also the governor.\n */\n function admin() external view returns (address) {\n return _governor();\n }\n\n /**\n * @return The address of the implementation.\n */\n function implementation() external view returns (address) {\n return _implementation();\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy.\n * Only the admin can call this function.\n * @param _newImplementation Address of the new implementation.\n */\n function upgradeTo(address _newImplementation) external onlyGovernor {\n _upgradeTo(_newImplementation);\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy and call a function\n * on the new implementation.\n * This is useful to initialize the proxied contract.\n * @param newImplementation Address of the new implementation.\n * @param data Data to send as msg.data in the low level call.\n * It should include the signature and the parameters of the function to be called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n */\n function upgradeToAndCall(address newImplementation, bytes calldata data)\n external\n payable\n onlyGovernor\n {\n _upgradeTo(newImplementation);\n (bool success, ) = newImplementation.delegatecall(data);\n require(success);\n }\n\n /**\n * @dev Fallback function.\n * Implemented entirely in `_fallback`.\n */\n fallback() external payable {\n _fallback();\n }\n\n /**\n * @dev Delegates execution to an implementation contract.\n * This is a low level function that doesn't return to its internal call site.\n * It will return to the external caller whatever the implementation returns.\n * @param _impl Address to delegate.\n */\n function _delegate(address _impl) internal {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(gas(), _impl, 0, calldatasize(), 0, 0)\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n /**\n * @dev Function that is run as the first thing in the fallback function.\n * Can be redefined in derived contracts to add functionality.\n * Redefinitions must call super._willFallback().\n */\n function _willFallback() internal {}\n\n /**\n * @dev fallback implementation.\n * Extracted to enable manual triggering.\n */\n function _fallback() internal {\n _willFallback();\n _delegate(_implementation());\n }\n\n /**\n * @dev Storage slot with the address of the current implementation.\n * This is the keccak-256 hash of \"eip1967.proxy.implementation\" subtracted by 1, and is\n * validated in the constructor.\n */\n bytes32 internal constant IMPLEMENTATION_SLOT =\n 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n /**\n * @dev Returns the current implementation.\n * @return impl Address of the current implementation\n */\n function _implementation() internal view returns (address impl) {\n bytes32 slot = IMPLEMENTATION_SLOT;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n impl := sload(slot)\n }\n }\n\n /**\n * @dev Upgrades the proxy to a new implementation.\n * @param newImplementation Address of the new implementation.\n */\n function _upgradeTo(address newImplementation) internal {\n _setImplementation(newImplementation);\n emit Upgraded(newImplementation);\n }\n\n /**\n * @dev Sets the implementation address of the proxy.\n * @param newImplementation Address of the new implementation.\n */\n function _setImplementation(address newImplementation) internal {\n require(\n Address.isContract(newImplementation),\n \"Cannot set a proxy implementation to a non-contract address\"\n );\n\n bytes32 slot = IMPLEMENTATION_SLOT;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(slot, newImplementation)\n }\n }\n}\n" + }, + "contracts/proxies/Proxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OUSDProxy delegates calls to an OUSD implementation\n */\ncontract OUSDProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WrappedOUSDProxy delegates calls to a WrappedOUSD implementation\n */\ncontract WrappedOUSDProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice VaultProxy delegates calls to a Vault implementation\n */\ncontract VaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CompoundStrategyProxy delegates calls to a CompoundStrategy implementation\n */\ncontract CompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice AaveStrategyProxy delegates calls to a AaveStrategy implementation\n */\ncontract AaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ConvexStrategyProxy delegates calls to a ConvexStrategy implementation\n */\ncontract ConvexStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice HarvesterProxy delegates calls to a Harvester implementation\n */\ncontract HarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice DripperProxy delegates calls to a Dripper implementation\n */\ncontract DripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoCompoundStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\n */\ncontract MorphoCompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ConvexOUSDMetaStrategyProxy delegates calls to a ConvexOUSDMetaStrategy implementation\n */\ncontract ConvexOUSDMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoAaveStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\n */\ncontract MorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHProxy delegates calls to nowhere for now\n */\ncontract OETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHProxy delegates calls to nowhere for now\n */\ncontract WOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHVaultProxy delegates calls to a Vault implementation\n */\ncontract OETHVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHDripperProxy delegates calls to a OETHDripper implementation\n */\ncontract OETHDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHHarvesterProxy delegates calls to a Harvester implementation\n */\ncontract OETHHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CurveEthStrategyProxy delegates calls to a CurveEthStrategy implementation\n */\ncontract ConvexEthMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BuybackProxy delegates calls to Buyback implementation\n */\ncontract BuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHMorphoAaveStrategyProxy delegates calls to a MorphoAaveStrategy implementation\n */\ncontract OETHMorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBalancerMetaPoolrEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\n */\ncontract OETHBalancerMetaPoolrEthStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice OETHBalancerMetaPoolwstEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\n */\ncontract OETHBalancerMetaPoolwstEthStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MakerDsrStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MakerDsrStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBuybackProxy delegates calls to Buyback implementation\n */\ncontract OETHBuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BridgedWOETHProxy delegates calls to BridgedWOETH implementation\n */\ncontract BridgedWOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice NativeStakingSSVStrategyProxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulatorProxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulatorProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingSSVStrategy2Proxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategy2Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulator2Proxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulator2Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingSSVStrategy3Proxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategy3Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulator3Proxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulator3Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MetaMorphoStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MetaMorphoStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ARMBuybackProxy delegates calls to Buyback implementation\n */\ncontract ARMBuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoGauntletPrimeUSDCStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MorphoGauntletPrimeUSDCStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MorphoGauntletPrimeUSDTStrategyProxy delegates calls to a Generalized4626USDTStrategy implementation\n */\ncontract MorphoGauntletPrimeUSDTStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice CurvePoolBoosterProxy delegates calls to a CurvePoolBooster implementation\n */\ncontract CurvePoolBoosterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHFixedRateDripperProxy delegates calls to a OETHFixedRateDripper implementation\n */\ncontract OETHFixedRateDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHSimpleHarvesterProxy delegates calls to a OETHSimpleHarvester implementation\n */\ncontract OETHSimpleHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice PoolBoostCentralRegistryProxy delegates calls to the PoolBoostCentralRegistry implementation\n */\ncontract PoolBoostCentralRegistryProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MakerSSRStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MakerSSRStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OUSDCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\n */\ncontract OUSDCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\n */\ncontract OETHCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CompoundingStakingSSVStrategyProxy delegates calls to a CompoundingStakingSSVStrategy implementation\n */\ncontract CompoundingStakingSSVStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n" + }, + "contracts/strategies/AaveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Aave Strategy\n * @notice Investment strategy for investing stablecoins via Aave\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport \"./IAave.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\nimport { IAaveStakedToken } from \"./IAaveStakeToken.sol\";\nimport { IAaveIncentivesController } from \"./IAaveIncentivesController.sol\";\n\ncontract AaveStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n\n uint16 constant referralCode = 92;\n\n IAaveIncentivesController public incentivesController;\n IAaveStakedToken public stkAave;\n\n /**\n * @param _stratConfig The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as AAVE needs several extra\n * addresses for the rewards program.\n * @param _rewardTokenAddresses Address of the AAVE token\n * @param _assets Addresses of supported assets\n * @param _pTokens Platform Token corresponding addresses\n * @param _incentivesAddress Address of the AAVE incentives controller\n * @param _stkAaveAddress Address of the stkAave contract\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // AAVE\n address[] calldata _assets,\n address[] calldata _pTokens,\n address _incentivesAddress,\n address _stkAaveAddress\n ) external onlyGovernor initializer {\n incentivesController = IAaveIncentivesController(_incentivesAddress);\n stkAave = IAaveStakedToken(_stkAaveAddress);\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Deposit asset into Aave\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Aave\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n // Following line also doubles as a check that we are depositing\n // an asset that we support.\n emit Deposit(_asset, _getATokenFor(_asset), _amount);\n _getLendingPool().deposit(_asset, _amount, address(this), referralCode);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Aave\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Aave\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n emit Withdrawal(_asset, _getATokenFor(_asset), _amount);\n uint256 actual = _getLendingPool().withdraw(\n _asset,\n _amount,\n address(this)\n );\n require(actual == _amount, \"Did not withdraw enough\");\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n // Redeem entire balance of aToken\n IERC20 asset = IERC20(assetsMapped[i]);\n address aToken = _getATokenFor(assetsMapped[i]);\n uint256 balance = IERC20(aToken).balanceOf(address(this));\n if (balance > 0) {\n uint256 actual = _getLendingPool().withdraw(\n address(asset),\n balance,\n address(this)\n );\n require(actual == balance, \"Did not withdraw enough\");\n\n uint256 assetBalance = asset.balanceOf(address(this));\n // Transfer entire balance to Vault\n asset.safeTransfer(vaultAddress, assetBalance);\n\n emit Withdrawal(address(asset), aToken, assetBalance);\n }\n }\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n // Balance is always with token aToken decimals\n address aToken = _getATokenFor(_asset);\n balance = IERC20(aToken).balanceOf(address(this));\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Approve the spending of all assets by their corresponding aToken,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n address lendingPool = address(_getLendingPool());\n // approve the pool to spend the Asset\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n address asset = assetsMapped[i];\n // Safe approval\n IERC20(asset).safeApprove(lendingPool, 0);\n IERC20(asset).safeApprove(lendingPool, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / aTokens\n We need to give the AAVE lending pool approval to transfer the\n asset.\n * @param _asset Address of the asset to approve\n * @param _aToken Address of the aToken\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _aToken)\n internal\n override\n {\n address lendingPool = address(_getLendingPool());\n IERC20(_asset).safeApprove(lendingPool, 0);\n IERC20(_asset).safeApprove(lendingPool, type(uint256).max);\n }\n\n /**\n * @dev Get the aToken wrapped in the IERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return Corresponding aToken to this asset\n */\n function _getATokenFor(address _asset) internal view returns (address) {\n address aToken = assetToPToken[_asset];\n require(aToken != address(0), \"aToken does not exist\");\n return aToken;\n }\n\n /**\n * @dev Get the current address of the Aave lending pool, which is the gateway to\n * depositing.\n * @return Current lending pool implementation\n */\n function _getLendingPool() internal view returns (IAaveLendingPool) {\n address lendingPool = ILendingPoolAddressesProvider(platformAddress)\n .getLendingPool();\n require(lendingPool != address(0), \"Lending pool does not exist\");\n return IAaveLendingPool(lendingPool);\n }\n\n /**\n * @dev Collect stkAave, convert it to AAVE send to Vault.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n if (address(stkAave) == address(0)) {\n return;\n }\n\n // Check staked AAVE cooldown timer\n uint256 cooldown = stkAave.stakersCooldowns(address(this));\n uint256 windowStart = cooldown + stkAave.COOLDOWN_SECONDS();\n uint256 windowEnd = windowStart + stkAave.UNSTAKE_WINDOW();\n\n // If inside the unlock window, then we can redeem stkAave\n // for AAVE and send it to the vault.\n if (block.timestamp > windowStart && block.timestamp <= windowEnd) {\n // Redeem to AAVE\n uint256 stkAaveBalance = stkAave.balanceOf(address(this));\n stkAave.redeem(address(this), stkAaveBalance);\n\n // Transfer AAVE to harvesterAddress\n uint256 aaveBalance = IERC20(rewardTokenAddresses[0]).balanceOf(\n address(this)\n );\n if (aaveBalance > 0) {\n IERC20(rewardTokenAddresses[0]).safeTransfer(\n harvesterAddress,\n aaveBalance\n );\n }\n }\n\n // Collect available rewards and restart the cooldown timer, if either of\n // those should be run.\n if (block.timestamp > windowStart || cooldown == 0) {\n uint256 assetsLen = assetsMapped.length;\n // aToken addresses for incentives controller\n address[] memory aTokens = new address[](assetsLen);\n for (uint256 i = 0; i < assetsLen; ++i) {\n aTokens[i] = _getATokenFor(assetsMapped[i]);\n }\n\n // 1. If we have rewards availabile, collect them\n uint256 pendingRewards = incentivesController.getRewardsBalance(\n aTokens,\n address(this)\n );\n if (pendingRewards > 0) {\n // Because getting more stkAAVE from the incentives controller\n // with claimRewards() may push the stkAAVE cooldown time\n // forward, it is called after stakedAAVE has been turned into\n // AAVE.\n uint256 collected = incentivesController.claimRewards(\n aTokens,\n pendingRewards,\n address(this)\n );\n require(collected == pendingRewards, \"AAVE reward difference\");\n }\n\n // 2. Start cooldown counting down.\n if (stkAave.balanceOf(address(this)) > 0) {\n // Protected with if since cooldown call would revert\n // if no stkAave balance.\n stkAave.cooldown();\n }\n }\n }\n}\n" + }, + "contracts/strategies/AbstractCompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base Compound Abstract Strategy\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { ICERC20 } from \"./ICompound.sol\";\nimport { IComptroller } from \"../interfaces/IComptroller.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\nabstract contract AbstractCompoundStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n\n int256[50] private __reserved;\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Get the cToken wrapped in the ICERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return Corresponding cToken to this asset\n */\n function _getCTokenFor(address _asset) internal view returns (ICERC20) {\n address cToken = assetToPToken[_asset];\n require(cToken != address(0), \"cToken does not exist\");\n return ICERC20(cToken);\n }\n\n /**\n * @dev Converts an underlying amount into cToken amount\n * cTokenAmt = (underlying * 1e18) / exchangeRate\n * @param _cToken cToken for which to change\n * @param _underlying Amount of underlying to convert\n * @return amount Equivalent amount of cTokens\n */\n function _convertUnderlyingToCToken(ICERC20 _cToken, uint256 _underlying)\n internal\n view\n returns (uint256 amount)\n {\n // e.g. 1e18*1e18 / 205316390724364402565641705 = 50e8\n // e.g. 1e8*1e18 / 205316390724364402565641705 = 0.45 or 0\n amount = (_underlying * 1e18) / _cToken.exchangeRateStored();\n }\n}\n" + }, + "contracts/strategies/AbstractConvexMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { ICurveMetaPool } from \"./ICurveMetaPool.sol\";\nimport { IERC20, AbstractCurveStrategy, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\nabstract contract AbstractConvexMetaStrategy is AbstractCurveStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n event MaxWithdrawalSlippageUpdated(\n uint256 _prevMaxSlippagePercentage,\n uint256 _newMaxSlippagePercentage\n );\n\n // used to circumvent the stack too deep issue\n struct InitConfig {\n address cvxDepositorAddress; //Address of the Convex depositor(AKA booster) for this pool\n address metapoolAddress; //Address of the Curve MetaPool\n address metapoolMainToken; //Address of Main metapool token\n address cvxRewardStakerAddress; //Address of the CVX rewards staker\n address metapoolLPToken; //Address of metapool LP token\n uint256 cvxDepositorPTokenId; //Pid of the pool referred to by Depositor and staker\n }\n\n address internal cvxDepositorAddress;\n address internal cvxRewardStakerAddress;\n uint256 internal cvxDepositorPTokenId;\n ICurveMetaPool internal metapool;\n IERC20 internal metapoolMainToken;\n IERC20 internal metapoolLPToken;\n // Ordered list of metapool assets\n address[] internal metapoolAssets;\n // Max withdrawal slippage denominated in 1e18 (1e18 == 100%)\n uint256 public maxWithdrawalSlippage;\n uint128 internal crvCoinIndex;\n uint128 internal mainCoinIndex;\n\n int256[41] private ___reserved;\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * DAI, USDC, USDT\n * @param _pTokens Platform Token corresponding addresses\n * @param initConfig Various addresses and info for initialization state\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets,\n address[] calldata _pTokens,\n InitConfig calldata initConfig\n ) external onlyGovernor initializer {\n require(_assets.length == 3, \"Must have exactly three assets\");\n // Should be set prior to abstract initialize call otherwise\n // abstractSetPToken calls will fail\n cvxDepositorAddress = initConfig.cvxDepositorAddress;\n pTokenAddress = _pTokens[0];\n metapool = ICurveMetaPool(initConfig.metapoolAddress);\n metapoolMainToken = IERC20(initConfig.metapoolMainToken);\n cvxRewardStakerAddress = initConfig.cvxRewardStakerAddress;\n metapoolLPToken = IERC20(initConfig.metapoolLPToken);\n cvxDepositorPTokenId = initConfig.cvxDepositorPTokenId;\n maxWithdrawalSlippage = 1e16;\n\n metapoolAssets = [metapool.coins(0), metapool.coins(1)];\n crvCoinIndex = _getMetapoolCoinIndex(pTokenAddress);\n mainCoinIndex = _getMetapoolCoinIndex(initConfig.metapoolMainToken);\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n balance = 0;\n\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 contractPTokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (contractPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = contractPTokens.mulTruncate(virtual_price);\n balance += value;\n }\n\n /* We intentionally omit the metapoolLp tokens held by the metastrategyContract\n * since the contract should never (except in the middle of deposit/withdrawal\n * transaction) hold any amount of those tokens in normal operation. There\n * could be tokens sent to it by a 3rd party and we decide to actively ignore\n * those.\n */\n uint256 metapoolGaugePTokens = IRewardStaking(cvxRewardStakerAddress)\n .balanceOf(address(this));\n\n if (metapoolGaugePTokens > 0) {\n uint256 value = metapoolGaugePTokens.mulTruncate(\n metapool.get_virtual_price()\n );\n balance += value;\n }\n\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = balance.scaleBy(assetDecimals, 18) / THREEPOOL_ASSET_COUNT;\n }\n\n /**\n * @dev This function is completely analogous to _calcCurveTokenAmount[AbstractCurveStrategy]\n * and just utilizes different Curve (meta)pool API\n */\n function _calcCurveMetaTokenAmount(uint128 _coinIndex, uint256 _amount)\n internal\n returns (uint256 requiredMetapoolLP)\n {\n uint256[2] memory _amounts = [uint256(0), uint256(0)];\n _amounts[uint256(_coinIndex)] = _amount;\n\n // LP required when removing required asset ignoring fees\n uint256 lpRequiredNoFees = metapool.calc_token_amount(_amounts, false);\n /* LP required if fees would apply to entirety of removed amount\n *\n * fee is 1e10 denominated number: https://curve.readthedocs.io/exchange-pools.html#StableSwap.fee\n */\n uint256 lpRequiredFullFees = lpRequiredNoFees.mulTruncateScale(\n 1e10 + metapool.fee(),\n 1e10\n );\n\n /* asset received when withdrawing full fee applicable LP accounting for\n * slippage and fees\n */\n uint256 assetReceivedForFullLPFees = metapool.calc_withdraw_one_coin(\n lpRequiredFullFees,\n int128(_coinIndex)\n );\n\n // exact amount of LP required\n requiredMetapoolLP =\n (lpRequiredFullFees * _amount) /\n assetReceivedForFullLPFees;\n }\n\n function _approveBase() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // 3Pool for LP token (required for removing liquidity)\n pToken.safeApprove(platformAddress, 0);\n pToken.safeApprove(platformAddress, type(uint256).max);\n // Gauge for LP token\n metapoolLPToken.safeApprove(cvxDepositorAddress, 0);\n metapoolLPToken.safeApprove(cvxDepositorAddress, type(uint256).max);\n // Metapool for LP token\n pToken.safeApprove(address(metapool), 0);\n pToken.safeApprove(address(metapool), type(uint256).max);\n // Metapool for Metapool main token\n metapoolMainToken.safeApprove(address(metapool), 0);\n metapoolMainToken.safeApprove(address(metapool), type(uint256).max);\n }\n\n /**\n * @dev Get the index of the coin\n */\n function _getMetapoolCoinIndex(address _asset)\n internal\n view\n returns (uint128)\n {\n for (uint128 i = 0; i < 2; i++) {\n if (metapoolAssets[i] == _asset) return i;\n }\n revert(\"Invalid Metapool asset\");\n }\n\n /**\n * @dev Sets max withdrawal slippage that is considered when removing\n * liquidity from Metapools.\n * @param _maxWithdrawalSlippage Max withdrawal slippage denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxWithdrawalSlippage should actually be 0.1% (1e15)\n * for production usage. Contract allows as low value as 0% for confirming\n * correct behavior in test suite.\n */\n function setMaxWithdrawalSlippage(uint256 _maxWithdrawalSlippage)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(\n _maxWithdrawalSlippage <= 1e18,\n \"Max withdrawal slippage needs to be between 0% - 100%\"\n );\n emit MaxWithdrawalSlippageUpdated(\n maxWithdrawalSlippage,\n _maxWithdrawalSlippage\n );\n maxWithdrawalSlippage = _maxWithdrawalSlippage;\n }\n\n /**\n * @dev Collect accumulated CRV and CVX and send to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n IRewardStaking(cvxRewardStakerAddress).getReward();\n _collectRewardTokens();\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/AbstractCurveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve 3Pool Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\nabstract contract AbstractCurveStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 internal constant MAX_SLIPPAGE = 1e16; // 1%, same as the Curve UI\n // number of assets in Curve 3Pool (USDC, DAI, USDT)\n uint256 internal constant THREEPOOL_ASSET_COUNT = 3;\n address internal pTokenAddress;\n\n int256[49] private __reserved;\n\n /**\n * @dev Deposit asset into the Curve 3Pool\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_amount > 0, \"Must deposit something\");\n emit Deposit(_asset, pTokenAddress, _amount);\n\n // 3Pool requires passing deposit amounts for all 3 assets, set to 0 for\n // all\n uint256[3] memory _amounts;\n uint256 poolCoinIndex = _getCoinIndex(_asset);\n // Set the amount on the asset we want to deposit\n _amounts[poolCoinIndex] = _amount;\n ICurvePool curvePool = ICurvePool(platformAddress);\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n uint256 depositValue = _amount.scaleBy(18, assetDecimals).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = depositValue.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n // Do the deposit to 3pool\n curvePool.add_liquidity(_amounts, minMintAmount);\n _lpDepositAll();\n }\n\n function _lpDepositAll() internal virtual;\n\n /**\n * @dev Deposit the entire balance of any supported asset into the Curve 3pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n uint256 depositValue = 0;\n ICurvePool curvePool = ICurvePool(platformAddress);\n uint256 curveVirtualPrice = curvePool.get_virtual_price();\n\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address assetAddress = assetsMapped[i];\n uint256 balance = IERC20(assetAddress).balanceOf(address(this));\n if (balance > 0) {\n uint256 poolCoinIndex = _getCoinIndex(assetAddress);\n // Set the amount on the asset we want to deposit\n _amounts[poolCoinIndex] = balance;\n uint256 assetDecimals = Helpers.getDecimals(assetAddress);\n // Get value of deposit in Curve LP token to later determine\n // the minMintAmount argument for add_liquidity\n depositValue =\n depositValue +\n balance.scaleBy(18, assetDecimals).divPrecisely(\n curveVirtualPrice\n );\n emit Deposit(assetAddress, pTokenAddress, balance);\n }\n }\n\n uint256 minMintAmount = depositValue.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n // Do the deposit to 3pool\n curvePool.add_liquidity(_amounts, minMintAmount);\n\n /* In case of Curve Strategy all assets are mapped to the same pToken (3CrvLP). Let\n * descendants further handle the pToken. By either deploying it to the metapool and\n * resulting tokens in Gauge. Or deploying pTokens directly to the Gauge.\n */\n _lpDepositAll();\n }\n\n function _lpWithdraw(uint256 numCrvTokens) internal virtual;\n\n function _lpWithdrawAll() internal virtual;\n\n /**\n * @dev Withdraw asset from Curve 3Pool\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Invalid amount\");\n\n emit Withdrawal(_asset, pTokenAddress, _amount);\n\n uint256 contractCrv3Tokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n\n uint256 coinIndex = _getCoinIndex(_asset);\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 requiredCrv3Tokens = _calcCurveTokenAmount(coinIndex, _amount);\n\n // We have enough LP tokens, make sure they are all on this contract\n if (contractCrv3Tokens < requiredCrv3Tokens) {\n _lpWithdraw(requiredCrv3Tokens - contractCrv3Tokens);\n }\n\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n _amounts[coinIndex] = _amount;\n\n curvePool.remove_liquidity_imbalance(_amounts, requiredCrv3Tokens);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Calculate amount of LP required when withdrawing specific amount of one\n * of the underlying assets accounting for fees and slippage.\n *\n * Curve pools unfortunately do not contain a calculation function for\n * amount of LP required when withdrawing a specific amount of one of the\n * underlying tokens and also accounting for fees (Curve's calc_token_amount\n * does account for slippage but not fees).\n *\n * Steps taken to calculate the metric:\n * - get amount of LP required if fees wouldn't apply\n * - increase the LP amount as if fees would apply to the entirety of the underlying\n * asset withdrawal. (when withdrawing only one coin fees apply only to amounts\n * of other assets pool would return in case of balanced removal - since those need\n * to be swapped for the single underlying asset being withdrawn)\n * - get amount of underlying asset withdrawn (this Curve function does consider slippage\n * and fees) when using the increased LP amount. As LP amount is slightly over-increased\n * so is amount of underlying assets returned.\n * - since we know exactly how much asset we require take the rate of LP required for asset\n * withdrawn to get the exact amount of LP.\n */\n function _calcCurveTokenAmount(uint256 _coinIndex, uint256 _amount)\n internal\n returns (uint256 required3Crv)\n {\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n _amounts[_coinIndex] = _amount;\n\n // LP required when removing required asset ignoring fees\n uint256 lpRequiredNoFees = curvePool.calc_token_amount(_amounts, false);\n /* LP required if fees would apply to entirety of removed amount\n *\n * fee is 1e10 denominated number: https://curve.readthedocs.io/exchange-pools.html#StableSwap.fee\n */\n uint256 lpRequiredFullFees = lpRequiredNoFees.mulTruncateScale(\n 1e10 + curvePool.fee(),\n 1e10\n );\n\n /* asset received when withdrawing full fee applicable LP accounting for\n * slippage and fees\n */\n uint256 assetReceivedForFullLPFees = curvePool.calc_withdraw_one_coin(\n lpRequiredFullFees,\n int128(uint128(_coinIndex))\n );\n\n // exact amount of LP required\n required3Crv =\n (lpRequiredFullFees * _amount) /\n assetReceivedForFullLPFees;\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n _lpWithdrawAll();\n // Withdraws are proportional to assets held by 3Pool\n uint256[3] memory minWithdrawAmounts = [\n uint256(0),\n uint256(0),\n uint256(0)\n ];\n\n // Remove liquidity\n ICurvePool threePool = ICurvePool(platformAddress);\n threePool.remove_liquidity(\n IERC20(pTokenAddress).balanceOf(address(this)),\n minWithdrawAmounts\n );\n // Transfer assets out of Vault\n // Note that Curve will provide all 3 of the assets in 3pool even if\n // we have not set PToken addresses for all of them in this strategy\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n IERC20 asset = IERC20(threePool.coins(i));\n asset.safeTransfer(vaultAddress, asset.balanceOf(address(this)));\n }\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 totalPTokens = IERC20(pTokenAddress).balanceOf(address(this));\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (totalPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = (totalPTokens * virtual_price) / 1e18;\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = value.scaleBy(assetDecimals, 18) / THREEPOOL_ASSET_COUNT;\n }\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n // This strategy is a special case since it only supports one asset\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n _approveAsset(assetsMapped[i]);\n }\n }\n\n /**\n * @dev Call the necessary approvals for the Curve pool and gauge\n * @param _asset Address of the asset\n */\n function _abstractSetPToken(address _asset, address) internal override {\n _approveAsset(_asset);\n }\n\n function _approveAsset(address _asset) internal {\n IERC20 asset = IERC20(_asset);\n // 3Pool for asset (required for adding liquidity)\n asset.safeApprove(platformAddress, 0);\n asset.safeApprove(platformAddress, type(uint256).max);\n }\n\n function _approveBase() internal virtual;\n\n /**\n * @dev Get the index of the coin\n */\n function _getCoinIndex(address _asset) internal view returns (uint256) {\n for (uint256 i = 0; i < 3; i++) {\n if (assetsMapped[i] == _asset) return i;\n }\n revert(\"Invalid 3pool asset\");\n }\n}\n" + }, + "contracts/strategies/aerodrome/AerodromeAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Aerodrome AMO strategy\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nimport { ISugarHelper } from \"../../interfaces/aerodrome/ISugarHelper.sol\";\nimport { INonfungiblePositionManager } from \"../../interfaces/aerodrome/INonfungiblePositionManager.sol\";\nimport { ISwapRouter } from \"../../interfaces/aerodrome/ISwapRouter.sol\";\nimport { ICLPool } from \"../../interfaces/aerodrome/ICLPool.sol\";\nimport { ICLGauge } from \"../../interfaces/aerodrome/ICLGauge.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\n\ncontract AerodromeAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n using SafeCast for uint256;\n\n /************************************************\n Important (!) setup configuration\n *************************************************/\n\n /**\n * In order to be able to remove a reasonable amount of complexity from the contract one of the\n * preconditions for this contract to function correctly is to have an outside account mint a small\n * amount of liquidity in the tick space where the contract will deploy's its liquidity and then send\n * that NFT LP position to a dead address (transfer to zero address not allowed.) See example of such\n * NFT LP token:\n * https://basescan.org/token/0x827922686190790b37229fd06084350e74485b72?a=413296#inventory\n */\n\n /***************************************\n Storage slot members\n ****************************************/\n\n /// @notice tokenId of the liquidity position\n uint256 public tokenId;\n /// @dev Minimum amount of tokens the strategy would be able to withdraw from the pool.\n /// minimum amount of tokens are withdrawn at a 1:1 price\n uint256 public underlyingAssets;\n /// @notice Marks the start of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareStart;\n /// @notice Marks the end of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareEnd;\n /// @dev reserved for inheritance\n int256[46] private __reserved;\n\n /***************************************\n Constants, structs and events\n ****************************************/\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the OETHb token contract\n address public immutable OETHb;\n /// @notice lower tick set to -1 representing the price of 1.0001 of WETH for 1 OETHb.\n int24 public immutable lowerTick;\n /// @notice lower tick set to 0 representing the price of 1.0000 of WETH for 1 OETHb.\n int24 public immutable upperTick;\n /// @notice tick spacing of the pool (set to 1)\n int24 public immutable tickSpacing;\n /// @notice the swapRouter for performing swaps\n ISwapRouter public immutable swapRouter;\n /// @notice the underlying AMO Slipstream pool\n ICLPool public immutable clPool;\n /// @notice the gauge for the corresponding Slipstream pool (clPool)\n /// @dev can become an immutable once the gauge is created on the base main-net\n ICLGauge public immutable clGauge;\n /// @notice the Position manager contract that is used to manage the pool's position\n INonfungiblePositionManager public immutable positionManager;\n /// @notice helper contract for liquidity and ticker math\n ISugarHelper public immutable helper;\n /// @notice sqrtRatioX96TickLower\n /// @dev tick lower has value -1 and represents the lowest price of WETH priced in OETHb. Meaning the pool\n /// offers less than 1 OETHb for 1 WETH. In other terms to get 1 OETHB the swap needs to offer 1.0001 WETH\n /// this is where purchasing OETHb with WETH within the liquidity position is most expensive\n uint160 public immutable sqrtRatioX96TickLower;\n /// @notice sqrtRatioX96TickHigher\n /// @dev tick higher has value 0 and represents 1:1 price parity of WETH to OETHb\n uint160 public immutable sqrtRatioX96TickHigher;\n /// @dev tick closest to 1:1 price parity\n /// Correctly assessing which tick is closer to 1:1 price parity is important since it affects\n /// the way we calculate the underlying assets in check Balance. The underlying aerodrome pool\n /// orders the tokens depending on the values of their addresses. If OETH token is token0 in the pool\n /// then sqrtRatioX96TickClosestToParity=sqrtRatioX96TickLower. If it is token1 in the pool then\n /// sqrtRatioX96TickClosestToParity=sqrtRatioX96TickHigher\n uint160 public immutable sqrtRatioX96TickClosestToParity;\n\n /// @dev a threshold under which the contract no longer allows for the protocol to rebalance. Guarding\n /// against a strategist / guardian being taken over and with multiple transactions draining the\n /// protocol funds.\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth); // 0x989e5ca8\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth); // 0xa6737d87\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n ); // 0x3681e8e0\n error OutsideExpectedTickRange(int24 currentTick); // 0x5a2eba75\n\n event PoolRebalanced(uint256 currentPoolWethShare);\n\n event PoolWethShareIntervalUpdated(\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n\n event LiquidityRemoved(\n uint256 withdrawLiquidityShare,\n uint256 removedWETHAmount,\n uint256 removedOETHbAmount,\n uint256 wethAmountCollected,\n uint256 oethbAmountCollected,\n uint256 underlyingAssets\n );\n\n event LiquidityAdded(\n uint256 wethAmountDesired,\n uint256 oethbAmountDesired,\n uint256 wethAmountSupplied,\n uint256 oethbAmountSupplied,\n uint256 tokenId,\n uint256 underlyingAssets\n );\n\n event UnderlyingAssetsUpdated(uint256 underlyingAssets);\n\n /**\n * @dev Un-stakes the token from the gauge for the execution duration of\n * the function and after that re-stakes it back in.\n *\n * It is important that the token is unstaked and owned by the strategy contract\n * during any liquidity altering operations and that it is re-staked back into the\n * gauge after liquidity changes. If the token fails to re-stake back to the\n * gauge it is not earning incentives.\n */\n // all functions using this modifier are used by functions with reentrancy check\n // slither-disable-start reentrancy-no-eth\n modifier gaugeUnstakeAndRestake() {\n // because of solidity short-circuit _isLpTokenStakedInGauge doesn't get called\n // when tokenId == 0\n if (tokenId != 0 && _isLpTokenStakedInGauge()) {\n clGauge.withdraw(tokenId);\n }\n _;\n // because of solidity short-circuit _isLpTokenStakedInGauge doesn't get called\n // when tokenId == 0\n if (tokenId != 0 && !_isLpTokenStakedInGauge()) {\n /**\n * It can happen that a withdrawal (or a full withdrawal) transactions would\n * remove all of the liquidity from the token with a NFT token still existing.\n * In that case the token can not be staked into the gauge, as some liquidity\n * needs to be added to it first.\n */\n if (_getLiquidity() > 0) {\n // if token liquidity changes the positionManager requires re-approval.\n // to any contract pre-approved to handle the token.\n positionManager.approve(address(clGauge), tokenId);\n clGauge.deposit(tokenId);\n }\n }\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice the constructor\n /// @dev This contract is intended to be used as a proxy. To prevent the\n /// potential confusion of having a functional implementation contract\n /// the constructor has the `initializer` modifier. This way the\n /// `initialize` function can not be called on the implementation contract.\n /// For the same reason the implementation contract also has the governor\n /// set to a zero address.\n /// @param _stratConfig the basic strategy configuration\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _oethbAddress Address of the Erc20 OETHb Token contract\n /// @param _swapRouter Address of the Aerodrome Universal Swap Router\n /// @param _nonfungiblePositionManager Address of position manager to add/remove\n /// the liquidity\n /// @param _clPool Address of the Aerodrome concentrated liquidity pool\n /// @param _clGauge Address of the Aerodrome slipstream pool gauge\n /// @param _sugarHelper Address of the Aerodrome Sugar helper contract\n /// @param _lowerBoundingTick Smaller bounding tick of our liquidity position\n /// @param _upperBoundingTick Larger bounding tick of our liquidity position\n /// @param _tickClosestToParity Tick that is closer to 1:1 price parity\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethbAddress,\n address _swapRouter,\n address _nonfungiblePositionManager,\n address _clPool,\n address _clGauge,\n address _sugarHelper,\n int24 _lowerBoundingTick,\n int24 _upperBoundingTick,\n int24 _tickClosestToParity\n ) initializer InitializableAbstractStrategy(_stratConfig) {\n require(\n _lowerBoundingTick == _tickClosestToParity ||\n _upperBoundingTick == _tickClosestToParity,\n \"Misconfigured tickClosestToParity\"\n );\n require(\n ICLPool(_clPool).token0() == _wethAddress,\n \"Only WETH supported as token0\"\n );\n require(\n ICLPool(_clPool).token1() == _oethbAddress,\n \"Only OETHb supported as token1\"\n );\n int24 _tickSpacing = ICLPool(_clPool).tickSpacing();\n // when we generalize AMO we might support other tick spacings\n require(_tickSpacing == 1, \"Unsupported tickSpacing\");\n\n WETH = _wethAddress;\n OETHb = _oethbAddress;\n swapRouter = ISwapRouter(_swapRouter);\n positionManager = INonfungiblePositionManager(\n _nonfungiblePositionManager\n );\n clPool = ICLPool(_clPool);\n clGauge = ICLGauge(_clGauge);\n helper = ISugarHelper(_sugarHelper);\n sqrtRatioX96TickLower = ISugarHelper(_sugarHelper).getSqrtRatioAtTick(\n _lowerBoundingTick\n );\n sqrtRatioX96TickHigher = ISugarHelper(_sugarHelper).getSqrtRatioAtTick(\n _upperBoundingTick\n );\n sqrtRatioX96TickClosestToParity = ISugarHelper(_sugarHelper)\n .getSqrtRatioAtTick(_tickClosestToParity);\n\n lowerTick = _lowerBoundingTick;\n upperTick = _upperBoundingTick;\n tickSpacing = _tickSpacing;\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /**\n * @notice initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n */\n function initialize(address[] memory _rewardTokenAddresses)\n external\n onlyGovernor\n initializer\n {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n new address[](0),\n new address[](0)\n );\n }\n\n /***************************************\n Configuration \n ****************************************/\n\n /**\n * @notice Set allowed pool weth share interval. After the rebalance happens\n * the share of WETH token in the ticker needs to be withing the specifications\n * of the interval.\n *\n * @param _allowedWethShareStart Start of WETH share interval expressed as 18 decimal amount\n * @param _allowedWethShareEnd End of WETH share interval expressed as 18 decimal amount\n */\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external onlyGovernor {\n require(\n _allowedWethShareStart < _allowedWethShareEnd,\n \"Invalid interval\"\n );\n // can not go below 1% weth share\n require(_allowedWethShareStart > 0.01 ether, \"Invalid interval start\");\n // can not go above 95% weth share\n require(_allowedWethShareEnd < 0.95 ether, \"Invalid interval end\");\n\n allowedWethShareStart = _allowedWethShareStart;\n allowedWethShareEnd = _allowedWethShareEnd;\n emit PoolWethShareIntervalUpdated(\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n\n /***************************************\n Periphery utils\n ****************************************/\n\n function _isLpTokenStakedInGauge() internal view returns (bool) {\n require(tokenId != 0, \"Missing NFT LP token\");\n\n address owner = positionManager.ownerOf(tokenId);\n require(\n owner == address(clGauge) || owner == address(this),\n \"Unexpected token owner\"\n );\n return owner == address(clGauge);\n }\n\n /***************************************\n Strategy overrides \n ****************************************/\n\n /**\n * @notice Deposit an amount of assets into the strategy contract. Calling deposit doesn't\n * automatically deposit funds into the underlying Aerodrome pool\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposit WETH to the strategy contract. This function does not add liquidity to the\n * underlying Aerodrome pool.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance > 1e12) {\n _deposit(WETH, _wethBalance);\n }\n }\n\n /**\n * @dev Deposit WETH to the contract. This function doesn't deposit the liquidity to the\n * pool, that is done via the rebalance call.\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n emit Deposit(_asset, address(0), _amount);\n\n // if the pool price is not within the expected interval leave the WETH on the contract\n // as to not break the mints\n (bool _isExpectedRange, ) = _checkForExpectedPoolPrice(false);\n if (_isExpectedRange) {\n // deposit funds into the underlying pool\n _rebalance(0, false, 0);\n }\n }\n\n /**\n * @notice Rebalance the pool to the desired token split and Deposit any WETH on the contract to the\n * underlying aerodrome pool. Print the required amount of corresponding OETHb. After the rebalancing is\n * done burn any potentially remaining OETHb tokens still on the strategy contract.\n *\n * This function has a slightly different behaviour depending on the status of the underlying Aerodrome\n * slipstream pool. The function consists of the following 3 steps:\n * 1. withdrawPartialLiquidity -> so that moving the activeTrading price via a swap is cheaper\n * 2. swapToDesiredPosition -> move active trading price in the pool to be able to deposit WETH & OETHb\n * tokens with the desired pre-configured shares\n * 3. addLiquidity -> add liquidity into the pool respecting share split configuration\n *\n * Scenario 1: When there is no liquidity in the pool from the strategy but there is from other LPs then\n * only step 1 is skipped. (It is important to note that liquidity needs to exist in the configured\n * strategy tick ranges in order for the swap to be possible) Step 3 mints new liquidity position\n * instead of adding to an existing one.\n * Scenario 2: When there is strategy's liquidity in the pool all 3 steps are taken\n *\n *\n * Exact _amountToSwap, _swapWeth & _minTokenReceived parameters shall be determined by simulating the\n * transaction off-chain. The strategy checks that after the swap the share of the tokens is in the\n * expected ranges.\n *\n * @param _amountToSwap The amount of the token to swap\n * @param _swapWeth Swap using WETH when true, use OETHb when false\n * @param _minTokenReceived Slippage check -> minimum amount of token expected in return\n */\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) external nonReentrant onlyGovernorOrStrategist {\n _rebalance(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n\n function _rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n /**\n * Would be nice to check if there is any total liquidity in the pool before performing this swap\n * but there is no easy way to do that in UniswapV3:\n * - clPool.liquidity() -> only liquidity in the active tick\n * - asset[1&2].balanceOf(address(clPool)) -> will include uncollected tokens of LP providers\n * after their liquidity position has been decreased\n */\n\n /**\n * When rebalance is called for the first time there is no strategy\n * liquidity in the pool yet. The liquidity removal is thus skipped.\n * Also execute this function when WETH is required for the swap.\n */\n if (tokenId != 0 && _swapWeth && _amountToSwap > 0) {\n _ensureWETHBalance(_amountToSwap);\n }\n\n // in some cases we will just want to add liquidity and not issue a swap to move the\n // active trading position within the pool\n if (_amountToSwap > 0) {\n _swapToDesiredPosition(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n // calling check liquidity early so we don't get unexpected errors when adding liquidity\n // in the later stages of this function\n _checkForExpectedPoolPrice(true);\n\n _addLiquidity();\n\n // this call shouldn't be necessary, since adding liquidity shouldn't affect the active\n // trading price. It is a defensive programming measure.\n (, uint256 _wethSharePct) = _checkForExpectedPoolPrice(true);\n\n // revert if protocol insolvent\n _solvencyAssert();\n\n emit PoolRebalanced(_wethSharePct);\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethbSupply = IERC20(OETHb).totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethbSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /**\n * @dev Decrease partial or all liquidity from the pool.\n * @param _liquidityToDecrease The amount of liquidity to remove expressed in 18 decimal point\n */\n function _removeLiquidity(uint256 _liquidityToDecrease)\n internal\n gaugeUnstakeAndRestake\n {\n require(_liquidityToDecrease > 0, \"Must remove some liquidity\");\n\n uint128 _liquidity = _getLiquidity();\n // need to convert to uint256 since intermittent result is to big for uint128 to handle\n uint128 _liquidityToRemove = uint256(_liquidity)\n .mulTruncate(_liquidityToDecrease)\n .toUint128();\n\n /**\n * There is no liquidity to remove -> exit function early. This can happen after a\n * withdraw/withdrawAll removes all of the liquidity while retaining the NFT token.\n */\n if (_liquidity == 0 || _liquidityToRemove == 0) {\n return;\n }\n\n (uint256 _amountWeth, uint256 _amountOethb) = positionManager\n .decreaseLiquidity(\n // Both expected amounts can be 0 since we don't really care if any swaps\n // happen just before the liquidity removal.\n INonfungiblePositionManager.DecreaseLiquidityParams({\n tokenId: tokenId,\n liquidity: _liquidityToRemove,\n amount0Min: 0,\n amount1Min: 0,\n deadline: block.timestamp\n })\n );\n\n (\n uint256 _amountWethCollected,\n uint256 _amountOethbCollected\n ) = positionManager.collect(\n INonfungiblePositionManager.CollectParams({\n tokenId: tokenId,\n recipient: address(this),\n amount0Max: type(uint128).max, // defaults to all tokens owed\n amount1Max: type(uint128).max // defaults to all tokens owed\n })\n );\n\n _updateUnderlyingAssets();\n\n emit LiquidityRemoved(\n _liquidityToDecrease,\n _amountWeth, //removedWethAmount\n _amountOethb, //removedOethbAmount\n _amountWethCollected,\n _amountOethbCollected,\n underlyingAssets\n );\n\n _burnOethbOnTheContract();\n }\n\n /**\n * @dev Perform a swap so that after the swap the ticker has the desired WETH to OETHb token share.\n */\n function _swapToDesiredPosition(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n IERC20 _tokenToSwap = IERC20(_swapWeth ? WETH : OETHb);\n uint256 _balance = _tokenToSwap.balanceOf(address(this));\n\n if (_balance < _amountToSwap) {\n // This should never trigger since _ensureWETHBalance will already\n // throw an error if there is not enough WETH\n if (_swapWeth) {\n revert NotEnoughWethForSwap(_balance, _amountToSwap);\n }\n // if swapping OETHb\n uint256 mintForSwap = _amountToSwap - _balance;\n IVault(vaultAddress).mintForStrategy(mintForSwap);\n }\n\n // approve the specific amount of WETH required\n if (_swapWeth) {\n IERC20(WETH).approve(address(swapRouter), _amountToSwap);\n }\n\n // Swap it\n swapRouter.exactInputSingle(\n // sqrtPriceLimitX96 is just a rough sanity check that we are within 0 -> 1 tick\n // a more fine check is performed in _checkForExpectedPoolPrice\n // Note: this needs further work if we want to generalize this approach\n ISwapRouter.ExactInputSingleParams({\n tokenIn: address(_tokenToSwap),\n tokenOut: _swapWeth ? OETHb : WETH,\n tickSpacing: tickSpacing, // set to 1\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: _amountToSwap,\n amountOutMinimum: _minTokenReceived, // slippage check\n sqrtPriceLimitX96: _swapWeth\n ? sqrtRatioX96TickLower\n : sqrtRatioX96TickHigher\n })\n );\n\n /**\n * In the interest of each function in _rebalance to leave the contract state as\n * clean as possible the OETHb tokens here are burned. This decreases the\n * dependence where `_swapToDesiredPosition` function relies on later functions\n * (`addLiquidity`) to burn the OETHb. Reducing the risk of error introduction.\n */\n _burnOethbOnTheContract();\n }\n\n /**\n * @dev Add liquidity into the pool in the pre-configured WETH to OETHb share ratios\n * defined by the allowedPoolWethShareStart|End interval. This function will respect\n * liquidity ratios when there is no liquidity yet in the pool. If liquidity is already\n * present then it relies on the `_swapToDesiredPosition` function in a step before\n * to already move the trading price to desired position (with some tolerance).\n */\n // rebalance already has re-entrency checks\n // slither-disable-start reentrancy-no-eth\n function _addLiquidity() internal gaugeUnstakeAndRestake {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n // don't deposit small liquidity amounts\n if (_wethBalance <= 1e12) {\n return;\n }\n\n uint160 _currentPrice = getPoolX96Price();\n /**\n * Sanity check active trading price is positioned within our desired tick.\n *\n * We revert when price is equal to the lower tick even though that is still\n * a valid amount in regards to ticker position by Sugar.estimateAmount call.\n * Current price equaling tick bound at the 1:1 price parity results in\n * uint overfow when calculating the OETHb balance to deposit.\n */\n if (\n _currentPrice <= sqrtRatioX96TickLower ||\n _currentPrice >= sqrtRatioX96TickHigher\n ) {\n revert OutsideExpectedTickRange(getCurrentTradingTick());\n }\n\n /**\n * If estimateAmount1 call fails it could be due to _currentPrice being really\n * close to a tick and amount1 is a larger number than the sugar helper is able\n * to compute.\n *\n * If token addresses were reversed estimateAmount0 would be required here\n */\n uint256 _oethbRequired = helper.estimateAmount1(\n _wethBalance,\n address(0), // no need to pass pool address when current price is specified\n _currentPrice,\n lowerTick,\n upperTick\n );\n\n if (_oethbRequired > _oethbBalance) {\n IVault(vaultAddress).mintForStrategy(\n _oethbRequired - _oethbBalance\n );\n }\n\n // approve the specific amount of WETH required\n IERC20(WETH).approve(address(positionManager), _wethBalance);\n\n uint256 _wethAmountSupplied;\n uint256 _oethbAmountSupplied;\n if (tokenId == 0) {\n (\n tokenId,\n ,\n _wethAmountSupplied,\n _oethbAmountSupplied\n ) = positionManager.mint(\n /** amount0Min & amount1Min are left at 0 because slippage protection is ensured by the\n * _checkForExpectedPoolPrice\n *›\n * Also sqrtPriceX96 is 0 because the pool is already created\n * non zero amount attempts to create a new instance of the pool\n */\n INonfungiblePositionManager.MintParams({\n token0: WETH,\n token1: OETHb,\n tickSpacing: tickSpacing,\n tickLower: lowerTick,\n tickUpper: upperTick,\n amount0Desired: _wethBalance,\n amount1Desired: _oethbRequired,\n amount0Min: 0,\n amount1Min: 0,\n recipient: address(this),\n deadline: block.timestamp,\n sqrtPriceX96: 0\n })\n );\n } else {\n (, _wethAmountSupplied, _oethbAmountSupplied) = positionManager\n .increaseLiquidity(\n /** amount0Min & amount1Min are left at 0 because slippage protection is ensured by the\n * _checkForExpectedPoolPrice\n */\n INonfungiblePositionManager.IncreaseLiquidityParams({\n tokenId: tokenId,\n amount0Desired: _wethBalance,\n amount1Desired: _oethbRequired,\n amount0Min: 0,\n amount1Min: 0,\n deadline: block.timestamp\n })\n );\n }\n\n _updateUnderlyingAssets();\n emit LiquidityAdded(\n _wethBalance, // wethAmountDesired\n _oethbRequired, // oethbAmountDesired\n _wethAmountSupplied, // wethAmountSupplied\n _oethbAmountSupplied, // oethbAmountSupplied\n tokenId, // tokenId\n underlyingAssets\n );\n\n // burn remaining OETHb\n _burnOethbOnTheContract();\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @dev Check that the Aerodrome pool price is within the expected\n * parameters.\n * This function works whether the strategy contract has liquidity\n * position in the pool or not. The function returns _wethSharePct\n * as a gas optimization measure.\n * @param throwException when set to true the function throws an exception\n * when pool's price is not within expected range.\n * @return _isExpectedRange Bool expressing price is within expected range\n * @return _wethSharePct Share of WETH owned by this strategy contract in the\n * configured ticker.\n */\n function _checkForExpectedPoolPrice(bool throwException)\n internal\n view\n returns (bool _isExpectedRange, uint256 _wethSharePct)\n {\n require(\n allowedWethShareStart != 0 && allowedWethShareEnd != 0,\n \"Weth share interval not set\"\n );\n\n uint160 _currentPrice = getPoolX96Price();\n\n /**\n * First check we are in expected tick range\n *\n * We revert even though price being equal to the lower tick would still\n * count being within lower tick for the purpose of Sugar.estimateAmount calls\n */\n if (\n _currentPrice <= sqrtRatioX96TickLower ||\n _currentPrice >= sqrtRatioX96TickHigher\n ) {\n if (throwException) {\n revert OutsideExpectedTickRange(getCurrentTradingTick());\n }\n return (false, 0);\n }\n\n // 18 decimal number expressed WETH tick share\n _wethSharePct = _getWethShare(_currentPrice);\n\n if (\n _wethSharePct < allowedWethShareStart ||\n _wethSharePct > allowedWethShareEnd\n ) {\n if (throwException) {\n revert PoolRebalanceOutOfBounds(\n _wethSharePct,\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n return (false, _wethSharePct);\n }\n\n return (true, _wethSharePct);\n }\n\n /**\n * Burns any OETHb tokens remaining on the strategy contract\n */\n function _burnOethbOnTheContract() internal {\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n if (_oethbBalance > 1e12) {\n IVault(vaultAddress).burnForStrategy(_oethbBalance);\n }\n }\n\n /// @dev This function assumes there are no uncollected tokens in the clPool owned by the strategy contract.\n /// For that reason any liquidity withdrawals must also collect the tokens.\n function _updateUnderlyingAssets() internal {\n if (tokenId == 0) {\n underlyingAssets = 0;\n emit UnderlyingAssetsUpdated(underlyingAssets);\n return;\n }\n\n uint128 _liquidity = _getLiquidity();\n\n /**\n * Our net value represent the smallest amount of tokens we are able to extract from the position\n * given our liquidity.\n *\n * The least amount of tokens extraditable from the position is where the active trading price is\n * at the ticker 0 meaning the pool is offering 1:1 trades between WETH & OETHb. At that moment the pool\n * consists completely of OETHb and no WETH.\n *\n * The more swaps from WETH -> OETHb happen on the pool the more the price starts to move towards the -1\n * ticker making OETHb (priced in WETH) more expensive.\n *\n * An additional note: when liquidity is 0 then the helper returns 0 for both token amounts. And the\n * function set underlying assets to 0.\n */\n (uint256 _wethAmount, uint256 _oethbAmount) = helper\n .getAmountsForLiquidity(\n sqrtRatioX96TickClosestToParity, // sqrtRatioX96\n sqrtRatioX96TickLower, // sqrtRatioAX96\n sqrtRatioX96TickHigher, // sqrtRatioBX96\n _liquidity\n );\n\n require(_wethAmount == 0, \"Non zero wethAmount\");\n underlyingAssets = _oethbAmount;\n emit UnderlyingAssetsUpdated(underlyingAssets);\n }\n\n /**\n * @dev This function removes the appropriate amount of liquidity to assure that the required\n * amount of WETH is available on the contract\n *\n * @param _amount WETH balance required on the contract\n */\n function _ensureWETHBalance(uint256 _amount) internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance >= _amount) {\n return;\n }\n\n require(tokenId != 0, \"No liquidity available\");\n uint256 _additionalWethRequired = _amount - _wethBalance;\n (uint256 _wethInThePool, ) = getPositionPrincipal();\n\n if (_wethInThePool < _additionalWethRequired) {\n revert NotEnoughWethLiquidity(\n _wethInThePool,\n _additionalWethRequired\n );\n }\n\n uint256 shareOfWethToRemove = Math.min(\n _additionalWethRequired.divPrecisely(_wethInThePool) + 1,\n 1e18\n );\n _removeLiquidity(shareOfWethToRemove);\n }\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset WETH address\n * @param _amount Amount of WETH to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n _ensureWETHBalance(_amount);\n\n _withdraw(_recipient, _amount);\n }\n\n /**\n * @notice Withdraw WETH and sends it to the Vault.\n */\n function withdrawAll() external override onlyVault nonReentrant {\n if (tokenId != 0) {\n _removeLiquidity(1e18);\n }\n\n uint256 _balance = IERC20(WETH).balanceOf(address(this));\n if (_balance > 0) {\n _withdraw(vaultAddress, _balance);\n }\n }\n\n function _withdraw(address _recipient, uint256 _amount) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n IERC20(WETH).safeTransfer(_recipient, _amount);\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /**\n * @dev Collect the AERO token from the gauge\n */\n function _collectRewardTokens() internal override {\n if (tokenId != 0 && _isLpTokenStakedInGauge()) {\n clGauge.getReward(tokenId);\n }\n super._collectRewardTokens();\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /**\n * @dev Approve the spending of all assets\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n // to add liquidity to the clPool\n IERC20(OETHb).approve(address(positionManager), type(uint256).max);\n // to be able to rebalance using the swapRouter\n IERC20(OETHb).approve(address(swapRouter), type(uint256).max);\n\n /* the behaviour of this strategy has slightly changed and WETH could be\n * present on the contract between the transactions. For that reason we are\n * un-approving WETH to the swapRouter & positionManager and only approving\n * the required amount before a transaction\n */\n IERC20(WETH).approve(address(swapRouter), 0);\n IERC20(WETH).approve(address(positionManager), 0);\n }\n\n /***************************************\n Balances and Fees\n ****************************************/\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256)\n {\n require(_asset == WETH, \"Only WETH supported\");\n\n // we could in theory deposit to the strategy and forget to call rebalance in the same\n // governance transaction batch. In that case the WETH that is on the strategy contract\n // also needs to be accounted for.\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n // just paranoia check, in case there is OETHb in the strategy that for some reason hasn't\n // been burned yet.\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n return underlyingAssets + _wethBalance + _oethbBalance;\n }\n\n /**\n * @dev Returns the balance of both tokens in a given position (excluding fees)\n * @return _amountWeth Amount of WETH in position\n * @return _amountOethb Amount of OETHb in position\n */\n function getPositionPrincipal()\n public\n view\n returns (uint256 _amountWeth, uint256 _amountOethb)\n {\n if (tokenId == 0) {\n return (0, 0);\n }\n\n uint160 _sqrtRatioX96 = getPoolX96Price();\n (_amountWeth, _amountOethb) = helper.principal(\n positionManager,\n tokenId,\n _sqrtRatioX96\n );\n }\n\n /**\n * @notice Returns the current pool price in X96 format\n * @return _sqrtRatioX96 Pool price\n */\n function getPoolX96Price() public view returns (uint160 _sqrtRatioX96) {\n (_sqrtRatioX96, , , , , ) = clPool.slot0();\n }\n\n /**\n * @notice Returns the current active trading tick of the underlying pool\n * @return _currentTick Current pool trading tick\n */\n function getCurrentTradingTick() public view returns (int24 _currentTick) {\n (, _currentTick, , , , ) = clPool.slot0();\n }\n\n /**\n * @notice Returns the percentage of WETH liquidity in the configured ticker\n * owned by this strategy contract.\n * @return uint256 1e18 denominated percentage expressing the share\n */\n function getWETHShare() external view returns (uint256) {\n uint160 _currentPrice = getPoolX96Price();\n return _getWethShare(_currentPrice);\n }\n\n /**\n * @notice Returns the amount of liquidity in the contract's LP position\n * @return _liquidity Amount of liquidity in the position\n */\n function _getLiquidity() internal view returns (uint128 _liquidity) {\n if (tokenId == 0) {\n revert(\"No LP position\");\n }\n\n (, , , , , , , _liquidity, , , , ) = positionManager.positions(tokenId);\n }\n\n function _getWethShare(uint160 _currentPrice)\n internal\n view\n returns (uint256)\n {\n /**\n * If estimateAmount1 call fails it could be due to _currentPrice being really\n * close to a tick and amount1 too big to compute.\n *\n * If token addresses were reversed estimateAmount0 would be required here\n */\n uint256 _normalizedWethAmount = 1 ether;\n uint256 _correspondingOethAmount = helper.estimateAmount1(\n _normalizedWethAmount,\n address(0), // no need to pass pool address when current price is specified\n _currentPrice,\n lowerTick,\n upperTick\n );\n\n // 18 decimal number expressed weth tick share\n return\n _normalizedWethAmount.divPrecisely(\n _normalizedWethAmount + _correspondingOethAmount\n );\n }\n\n /***************************************\n Hidden functions\n ****************************************/\n /// @inheritdoc InitializableAbstractStrategy\n function setPTokenAddress(address, address) external override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /// @inheritdoc InitializableAbstractStrategy\n function removePToken(uint256) external override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Not supported\n */\n function _abstractSetPToken(address, address) internal override {\n // the deployer shall call safeApproveAllTokens() to set necessary approvals\n revert(\"Unsupported method\");\n }\n\n /***************************************\n ERC721 management\n ****************************************/\n\n /// @notice Callback function for whenever a NFT is transferred to this contract\n // solhint-disable-next-line max-line-length\n /// Ref: https://docs.openzeppelin.com/contracts/3.x/api/token/erc721#IERC721Receiver-onERC721Received-address-address-uint256-bytes-\n function onERC721Received(\n address,\n address,\n uint256,\n bytes calldata\n ) external returns (bytes4) {\n return this.onERC721Received.selector;\n }\n}\n" + }, + "contracts/strategies/balancer/AbstractAuraStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Base Balancer Abstract Strategy\n * @author Origin Protocol Inc\n */\n\nimport { AbstractBalancerStrategy } from \"./AbstractBalancerStrategy.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IERC4626 } from \"../../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport { IRewardStaking } from \"../IRewardStaking.sol\";\n\nabstract contract AbstractAuraStrategy is AbstractBalancerStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n /// @notice Address of the Aura rewards pool\n address public immutable auraRewardPoolAddress;\n\n // renamed from __reserved to not shadow AbstractBalancerStrategy.__reserved,\n int256[50] private __reserved_baseAuraStrategy;\n\n constructor(address _auraRewardPoolAddress) {\n auraRewardPoolAddress = _auraRewardPoolAddress;\n }\n\n /**\n * @dev Deposit all Balancer Pool Tokens (BPT) in this strategy contract\n * to the Aura rewards pool.\n */\n function _lpDepositAll() internal virtual override {\n uint256 bptBalance = IERC20(platformAddress).balanceOf(address(this));\n uint256 auraLp = IERC4626(auraRewardPoolAddress).deposit(\n bptBalance,\n address(this)\n );\n require(bptBalance == auraLp, \"Aura LP != BPT\");\n }\n\n /**\n * @dev Withdraw `numBPTTokens` Balancer Pool Tokens (BPT) from\n * the Aura rewards pool to this strategy contract.\n * @param numBPTTokens Number of Balancer Pool Tokens (BPT) to withdraw\n */\n function _lpWithdraw(uint256 numBPTTokens) internal virtual override {\n IRewardStaking(auraRewardPoolAddress).withdrawAndUnwrap(\n numBPTTokens,\n true // also claim reward tokens\n );\n }\n\n /**\n * @dev Withdraw all Balancer Pool Tokens (BPT) from\n * the Aura rewards pool to this strategy contract.\n */\n function _lpWithdrawAll() internal virtual override {\n // Get all the strategy's BPTs in Aura\n // maxRedeem is implemented as balanceOf(address) in Aura\n uint256 bptBalance = IERC4626(auraRewardPoolAddress).maxRedeem(\n address(this)\n );\n\n IRewardStaking(auraRewardPoolAddress).withdrawAndUnwrap(\n bptBalance,\n true // also claim reward tokens\n );\n }\n\n /**\n * @notice Collects BAL and AURA tokens from the rewards pool.\n */\n function collectRewardTokens()\n external\n virtual\n override\n onlyHarvester\n nonReentrant\n {\n /* Similar to Convex, calling this function collects both of the\n * accrued BAL and AURA tokens.\n */\n IRewardStaking(auraRewardPoolAddress).getReward();\n _collectRewardTokens();\n }\n\n /// @notice Balancer Pool Tokens (BPT) in the Balancer pool and the Aura rewards pool.\n function _getBalancerPoolTokens()\n internal\n view\n override\n returns (uint256 balancerPoolTokens)\n {\n balancerPoolTokens =\n IERC20(platformAddress).balanceOf(address(this)) +\n // maxRedeem is implemented as balanceOf(address) in Aura\n IERC4626(auraRewardPoolAddress).maxRedeem(address(this));\n }\n\n function _approveBase() internal virtual override {\n super._approveBase();\n\n IERC20 pToken = IERC20(platformAddress);\n pToken.safeApprove(auraRewardPoolAddress, type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/balancer/AbstractBalancerStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Balancer Abstract Strategy\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\nimport { IRateProvider } from \"../../interfaces/balancer/IRateProvider.sol\";\nimport { VaultReentrancyLib } from \"./VaultReentrancyLib.sol\";\nimport { IOracle } from \"../../interfaces/IOracle.sol\";\nimport { IWstETH } from \"../../interfaces/IWstETH.sol\";\nimport { IERC4626 } from \"../../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nabstract contract AbstractBalancerStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n address public immutable rETH;\n address public immutable stETH;\n address public immutable wstETH;\n address public immutable frxETH;\n address public immutable sfrxETH;\n\n /// @notice Address of the Balancer vault\n IBalancerVault public immutable balancerVault;\n /// @notice Balancer pool identifier\n bytes32 public immutable balancerPoolId;\n\n // Max withdrawal deviation denominated in 1e18 (1e18 == 100%)\n uint256 public maxWithdrawalDeviation;\n // Max deposit deviation denominated in 1e18 (1e18 == 100%)\n uint256 public maxDepositDeviation;\n\n int256[48] private __reserved;\n\n struct BaseBalancerConfig {\n address rEthAddress; // Address of the rETH token\n address stEthAddress; // Address of the stETH token\n address wstEthAddress; // Address of the wstETH token\n address frxEthAddress; // Address of the frxEth token\n address sfrxEthAddress; // Address of the sfrxEth token\n address balancerVaultAddress; // Address of the Balancer vault\n bytes32 balancerPoolId; // Balancer pool identifier\n }\n\n event MaxWithdrawalDeviationUpdated(\n uint256 _prevMaxDeviationPercentage,\n uint256 _newMaxDeviationPercentage\n );\n event MaxDepositDeviationUpdated(\n uint256 _prevMaxDeviationPercentage,\n uint256 _newMaxDeviationPercentage\n );\n\n /**\n * @dev Ensure we are not in a Vault context when this function is called, by attempting a no-op internal\n * balance operation. If we are already in a Vault transaction (e.g., a swap, join, or exit), the Vault's\n * reentrancy protection will cause this function to revert.\n *\n * Use this modifier with any function that can cause a state change in a pool and is either public itself,\n * or called by a public function *outside* a Vault operation (e.g., join, exit, or swap).\n *\n * This is to protect against Balancer's read-only re-entrancy vulnerability:\n * https://www.notion.so/originprotocol/Balancer-read-only-reentrancy-c686e72c82414ef18fa34312bb02e11b\n */\n modifier whenNotInBalancerVaultContext() {\n VaultReentrancyLib.ensureNotInVaultContext(balancerVault);\n _;\n }\n\n constructor(BaseBalancerConfig memory _balancerConfig) {\n rETH = _balancerConfig.rEthAddress;\n stETH = _balancerConfig.stEthAddress;\n wstETH = _balancerConfig.wstEthAddress;\n frxETH = _balancerConfig.frxEthAddress;\n sfrxETH = _balancerConfig.sfrxEthAddress;\n\n balancerVault = IBalancerVault(_balancerConfig.balancerVaultAddress);\n balancerPoolId = _balancerConfig.balancerPoolId;\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Balancer's strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of BAL & AURA\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * WETH, stETH\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // BAL & AURA\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n maxWithdrawalDeviation = 1e16;\n maxDepositDeviation = 1e16;\n\n emit MaxWithdrawalDeviationUpdated(0, maxWithdrawalDeviation);\n emit MaxDepositDeviationUpdated(0, maxDepositDeviation);\n\n IERC20[] memory poolAssets = _getPoolAssets();\n require(\n poolAssets.length == _assets.length,\n \"Pool assets length mismatch\"\n );\n for (uint256 i = 0; i < _assets.length; ++i) {\n address asset = _fromPoolAsset(address(poolAssets[i]));\n require(_assets[i] == asset, \"Pool assets mismatch\");\n }\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @notice Get strategy's share of an assets in the Balancer pool.\n * This is not denominated in OUSD/ETH value of the assets in the Balancer pool.\n * @param _asset Address of the Vault collateral asset\n * @return amount the amount of vault collateral assets\n *\n * IMPORTANT if this function is overridden it needs to have a whenNotInBalancerVaultContext\n * modifier on it or it is susceptible to read-only re-entrancy attack\n *\n * @dev it is important that this function is not affected by reporting inflated\n * values of assets in case of any pool manipulation. Such a manipulation could easily\n * exploit the protocol by:\n * - minting OETH\n * - tilting Balancer pool to report higher balances of assets\n * - rebasing() -> all that extra token balances get distributed to OETH holders\n * - tilting pool back\n * - redeeming OETH\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n whenNotInBalancerVaultContext\n returns (uint256 amount)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n\n uint256 bptBalance = _getBalancerPoolTokens();\n\n /* To calculate the worth of queried asset:\n * - assume that all tokens normalized to their ETH value have an equal split balance\n * in the pool when it is balanced\n * - multiply the BPT amount with the bpt rate to get the ETH denominated amount\n * of strategy's holdings\n * - divide that by the number of tokens we support in the pool to get ETH denominated\n * amount that is applicable to each supported token in the pool.\n *\n * It would be possible to support only 1 asset in the pool (and be exposed to all\n * the assets while holding BPT tokens) and deposit/withdraw/checkBalance using only\n * that asset. TBD: changes to other functions still required if we ever decide to\n * go with such configuration.\n */\n amount = (bptBalance.mulTruncate(\n IRateProvider(platformAddress).getRate()\n ) / assetsMapped.length);\n\n /* If the pool asset is equal to (strategy )_asset it means that a rate\n * provider for that asset exists and that asset is not necessarily\n * pegged to a unit (ETH).\n *\n * Because this function returns the balance of the asset and is not denominated in\n * ETH units we need to convert the ETH denominated amount to asset amount.\n */\n if (_toPoolAsset(_asset) == _asset) {\n amount = amount.divPrecisely(_getRateProviderRate(_asset));\n }\n }\n\n /**\n * @notice Returns the value of all assets managed by this strategy.\n * Uses the Balancer pool's rate (virtual price) to convert the strategy's\n * Balancer Pool Tokens (BPT) to ETH value.\n * @return value The ETH value\n *\n * IMPORTANT if this function is overridden it needs to have a whenNotInBalancerVaultContext\n * modifier on it or it is susceptible to read-only re-entrancy attack\n */\n function checkBalance()\n external\n view\n virtual\n whenNotInBalancerVaultContext\n returns (uint256 value)\n {\n uint256 bptBalance = _getBalancerPoolTokens();\n\n // Convert BPT to ETH value\n value = bptBalance.mulTruncate(\n IRateProvider(platformAddress).getRate()\n );\n }\n\n /// @notice Balancer Pool Tokens (BPT) in the Balancer pool.\n function _getBalancerPoolTokens()\n internal\n view\n virtual\n returns (uint256 balancerPoolTokens)\n {\n balancerPoolTokens = IERC20(platformAddress).balanceOf(address(this));\n }\n\n /* solhint-disable max-line-length */\n /**\n * @notice BPT price is calculated by taking the rate from the rateProvider of the asset in\n * question. If one does not exist it defaults to 1e18. To get the final BPT expected that\n * is multiplied by the underlying asset amount divided by BPT token rate. BPT token rate is\n * similar to Curve's virtual_price and expresses how much has the price of BPT appreciated\n * (e.g. due to swap fees) in relation to the underlying assets\n *\n * Using the above approach makes the strategy vulnerable to a possible MEV attack using\n * flash loan to manipulate the pool before a deposit/withdrawal since the function ignores\n * market values of the assets being priced in BPT.\n *\n * At the time of writing there is no safe on-chain approach to pricing BPT in a way that it\n * would make it invulnerable to MEV pool manipulation. See recent Balancer exploit:\n * https://www.notion.so/originprotocol/Balancer-OETH-strategy-9becdea132704e588782a919d7d471eb?pvs=4#1cf07de12fc64f1888072321e0644348\n *\n * To mitigate MEV possibilities during deposits and withdraws, the VaultValueChecker will use checkBalance before and after the move\n * to ensure the expected changes took place.\n *\n * @param _asset Address of the Balancer pool asset\n * @param _amount Amount of the Balancer pool asset\n * @return bptExpected of BPT expected in exchange for the asset\n *\n * @dev\n * bptAssetPrice = 1e18 (asset peg) * pool_asset_rate\n *\n * bptExpected = bptAssetPrice * asset_amount / BPT_token_rate\n *\n * bptExpected = 1e18 (asset peg) * pool_asset_rate * asset_amount / BPT_token_rate\n * bptExpected = asset_amount * pool_asset_rate / BPT_token_rate\n *\n * further information available here:\n * https://www.notion.so/originprotocol/Balancer-OETH-strategy-9becdea132704e588782a919d7d471eb?pvs=4#ce01495ae70346d8971f5dced809fb83\n */\n /* solhint-enable max-line-length */\n function _getBPTExpected(address _asset, uint256 _amount)\n internal\n view\n virtual\n returns (uint256 bptExpected)\n {\n uint256 bptRate = IRateProvider(platformAddress).getRate();\n uint256 poolAssetRate = _getRateProviderRate(_asset);\n bptExpected = _amount.mulTruncate(poolAssetRate).divPrecisely(bptRate);\n }\n\n function _getBPTExpected(\n address[] memory _assets,\n uint256[] memory _amounts\n ) internal view virtual returns (uint256 bptExpected) {\n require(_assets.length == _amounts.length, \"Assets & amounts mismatch\");\n\n for (uint256 i = 0; i < _assets.length; ++i) {\n uint256 poolAssetRate = _getRateProviderRate(_assets[i]);\n // convert asset amount to ETH amount\n bptExpected += _amounts[i].mulTruncate(poolAssetRate);\n }\n\n uint256 bptRate = IRateProvider(platformAddress).getRate();\n // Convert ETH amount to BPT amount\n bptExpected = bptExpected.divPrecisely(bptRate);\n }\n\n function _lpDepositAll() internal virtual;\n\n function _lpWithdraw(uint256 numBPTTokens) internal virtual;\n\n function _lpWithdrawAll() internal virtual;\n\n /**\n * @notice Balancer returns assets and rateProviders for corresponding assets ordered\n * by numerical order.\n */\n function _getPoolAssets() internal view returns (IERC20[] memory assets) {\n // slither-disable-next-line unused-return\n (assets, , ) = balancerVault.getPoolTokens(balancerPoolId);\n }\n\n /**\n * @dev If an asset is rebasing the Balancer pools have a wrapped versions of assets\n * that the strategy supports. This function converts the pool(wrapped) asset\n * and corresponding amount to strategy asset.\n */\n function _toPoolAsset(address asset, uint256 amount)\n internal\n view\n returns (address poolAsset, uint256 poolAmount)\n {\n if (asset == stETH) {\n poolAsset = wstETH;\n if (amount > 0) {\n poolAmount = IWstETH(wstETH).getWstETHByStETH(amount);\n }\n } else if (asset == frxETH) {\n poolAsset = sfrxETH;\n if (amount > 0) {\n poolAmount = IERC4626(sfrxETH).convertToShares(amount);\n }\n } else {\n poolAsset = asset;\n poolAmount = amount;\n }\n }\n\n /**\n * @dev Converts a Vault collateral asset to a Balancer pool asset.\n * stETH becomes wstETH, frxETH becomes sfrxETH and everything else stays the same.\n * @param asset Address of the Vault collateral asset.\n * @return Address of the Balancer pool asset.\n */\n function _toPoolAsset(address asset) internal view returns (address) {\n if (asset == stETH) {\n return wstETH;\n } else if (asset == frxETH) {\n return sfrxETH;\n }\n return asset;\n }\n\n /**\n * @dev Converts rebasing asset to its wrapped counterpart.\n */\n function _wrapPoolAsset(address asset, uint256 amount)\n internal\n returns (address wrappedAsset, uint256 wrappedAmount)\n {\n if (asset == stETH) {\n wrappedAsset = wstETH;\n if (amount > 0) {\n wrappedAmount = IWstETH(wstETH).wrap(amount);\n }\n } else if (asset == frxETH) {\n wrappedAsset = sfrxETH;\n if (amount > 0) {\n wrappedAmount = IERC4626(sfrxETH).deposit(\n amount,\n address(this)\n );\n }\n } else {\n wrappedAsset = asset;\n wrappedAmount = amount;\n }\n }\n\n /**\n * @dev Converts wrapped asset to its rebasing counterpart.\n */\n function _unwrapPoolAsset(address asset, uint256 amount)\n internal\n returns (uint256 unwrappedAmount)\n {\n if (asset == stETH) {\n unwrappedAmount = IWstETH(wstETH).unwrap(amount);\n } else if (asset == frxETH) {\n unwrappedAmount = IERC4626(sfrxETH).withdraw(\n amount,\n address(this),\n address(this)\n );\n } else {\n unwrappedAmount = amount;\n }\n }\n\n /**\n * @dev If an asset is rebasing the Balancer pools have a wrapped versions of assets\n * that the strategy supports. This function converts the rebasing strategy asset\n * and corresponding amount to wrapped(pool) asset.\n */\n function _fromPoolAsset(address poolAsset, uint256 poolAmount)\n internal\n view\n returns (address asset, uint256 amount)\n {\n if (poolAsset == wstETH) {\n asset = stETH;\n if (poolAmount > 0) {\n amount = IWstETH(wstETH).getStETHByWstETH(poolAmount);\n }\n } else if (poolAsset == sfrxETH) {\n asset = frxETH;\n if (poolAmount > 0) {\n amount = IERC4626(sfrxETH).convertToAssets(poolAmount);\n }\n } else {\n asset = poolAsset;\n amount = poolAmount;\n }\n }\n\n function _fromPoolAsset(address poolAsset)\n internal\n view\n returns (address asset)\n {\n if (poolAsset == wstETH) {\n asset = stETH;\n } else if (poolAsset == sfrxETH) {\n asset = frxETH;\n } else {\n asset = poolAsset;\n }\n }\n\n /**\n * @notice Sets max withdrawal deviation that is considered when removing\n * liquidity from Balancer pools.\n * @param _maxWithdrawalDeviation Max withdrawal deviation denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxWithdrawalDeviation will be 1% (1e16) for production\n * usage. Vault value checker in combination with checkBalance will\n * catch any unexpected manipulation.\n */\n function setMaxWithdrawalDeviation(uint256 _maxWithdrawalDeviation)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(\n _maxWithdrawalDeviation <= 1e18,\n \"Withdrawal dev. out of bounds\"\n );\n emit MaxWithdrawalDeviationUpdated(\n maxWithdrawalDeviation,\n _maxWithdrawalDeviation\n );\n maxWithdrawalDeviation = _maxWithdrawalDeviation;\n }\n\n /**\n * @notice Sets max deposit deviation that is considered when adding\n * liquidity to Balancer pools.\n * @param _maxDepositDeviation Max deposit deviation denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxDepositDeviation will default to 1% (1e16)\n * for production usage. Vault value checker in combination with\n * checkBalance will catch any unexpected manipulation.\n */\n function setMaxDepositDeviation(uint256 _maxDepositDeviation)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(_maxDepositDeviation <= 1e18, \"Deposit dev. out of bounds\");\n emit MaxDepositDeviationUpdated(\n maxDepositDeviation,\n _maxDepositDeviation\n );\n maxDepositDeviation = _maxDepositDeviation;\n }\n\n function _approveBase() internal virtual {\n IERC20 pToken = IERC20(platformAddress);\n // Balancer vault for BPT token (required for removing liquidity)\n pToken.safeApprove(address(balancerVault), type(uint256).max);\n }\n\n function _getRateProviderRate(address _asset)\n internal\n view\n virtual\n returns (uint256);\n}\n" + }, + "contracts/strategies/balancer/BalancerMetaPoolStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Balancer MetaStablePool Strategy\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { AbstractAuraStrategy, AbstractBalancerStrategy } from \"./AbstractAuraStrategy.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\nimport { IRateProvider } from \"../../interfaces/balancer/IRateProvider.sol\";\nimport { IMetaStablePool } from \"../../interfaces/balancer/IMetaStablePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\ncontract BalancerMetaPoolStrategy is AbstractAuraStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(\n BaseStrategyConfig memory _stratConfig,\n BaseBalancerConfig memory _balancerConfig,\n address _auraRewardPoolAddress\n )\n InitializableAbstractStrategy(_stratConfig)\n AbstractBalancerStrategy(_balancerConfig)\n AbstractAuraStrategy(_auraRewardPoolAddress)\n {}\n\n /**\n * @notice There are no plans to configure BalancerMetaPool as a default\n * asset strategy. For that reason there is no need to support this\n * functionality.\n */\n function deposit(address, uint256)\n external\n override\n onlyVault\n nonReentrant\n {\n revert(\"Not supported\");\n }\n\n /**\n * @notice There are no plans to configure BalancerMetaPool as a default\n * asset strategy. For that reason there is no need to support this\n * functionality.\n */\n function deposit(address[] calldata, uint256[] calldata)\n external\n onlyVault\n nonReentrant\n {\n revert(\"Not supported\");\n }\n\n /**\n * @notice Deposits all supported assets in this strategy contract to the Balancer pool.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 assetsLength = assetsMapped.length;\n address[] memory strategyAssets = new address[](assetsLength);\n uint256[] memory strategyAmounts = new uint256[](assetsLength);\n\n // For each vault collateral asset\n for (uint256 i = 0; i < assetsLength; ++i) {\n strategyAssets[i] = assetsMapped[i];\n // Get the asset balance in this strategy contract\n strategyAmounts[i] = IERC20(strategyAssets[i]).balanceOf(\n address(this)\n );\n }\n _deposit(strategyAssets, strategyAmounts);\n }\n\n /*\n * _deposit doesn't require a read-only re-entrancy protection since during the deposit\n * the function enters the Balancer Vault Context. If this function were called as part of\n * the attacking contract (while intercepting execution flow upon receiving ETH) the read-only\n * protection of the Balancer Vault would be triggered. Since the attacking contract would\n * already be in the Balancer Vault context and wouldn't be able to enter it again.\n */\n function _deposit(\n address[] memory _strategyAssets,\n uint256[] memory _strategyAmounts\n ) internal {\n require(\n _strategyAssets.length == _strategyAmounts.length,\n \"Array length missmatch\"\n );\n\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n\n uint256[] memory strategyAssetAmountsToPoolAssetAmounts = new uint256[](\n _strategyAssets.length\n );\n address[] memory strategyAssetsToPoolAssets = new address[](\n _strategyAssets.length\n );\n\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n address strategyAsset = _strategyAssets[i];\n uint256 strategyAmount = _strategyAmounts[i];\n\n require(\n assetToPToken[strategyAsset] != address(0),\n \"Unsupported asset\"\n );\n strategyAssetsToPoolAssets[i] = _toPoolAsset(strategyAsset);\n\n if (strategyAmount > 0) {\n emit Deposit(strategyAsset, platformAddress, strategyAmount);\n\n // wrap rebasing assets like stETH and frxETH to wstETH and sfrxETH\n (, strategyAssetAmountsToPoolAssetAmounts[i]) = _wrapPoolAsset(\n strategyAsset,\n strategyAmount\n );\n }\n }\n\n uint256[] memory amountsIn = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n for (uint256 i = 0; i < tokens.length; ++i) {\n // Convert IERC20 type to address\n poolAssets[i] = address(tokens[i]);\n\n // For each of the mapped assets\n for (uint256 j = 0; j < strategyAssetsToPoolAssets.length; ++j) {\n // If the pool asset is the same as the mapped asset\n if (poolAssets[i] == strategyAssetsToPoolAssets[j]) {\n amountsIn[i] = strategyAssetAmountsToPoolAssetAmounts[j];\n }\n }\n }\n\n uint256 minBPT = _getBPTExpected(\n strategyAssetsToPoolAssets,\n strategyAssetAmountsToPoolAssetAmounts\n );\n uint256 minBPTwDeviation = minBPT.mulTruncate(\n 1e18 - maxDepositDeviation\n );\n\n /* EXACT_TOKENS_IN_FOR_BPT_OUT:\n * User sends precise quantities of tokens, and receives an\n * estimated but unknown (computed at run time) quantity of BPT.\n *\n * ['uint256', 'uint256[]', 'uint256']\n * [EXACT_TOKENS_IN_FOR_BPT_OUT, amountsIn, minimumBPT]\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,\n amountsIn,\n minBPTwDeviation\n );\n\n IBalancerVault.JoinPoolRequest memory request = IBalancerVault\n .JoinPoolRequest(poolAssets, amountsIn, userData, false);\n\n // Add the pool assets in this strategy to the balancer pool\n balancerVault.joinPool(\n balancerPoolId,\n address(this),\n address(this),\n request\n );\n\n // Deposit the Balancer Pool Tokens (BPT) into Aura\n _lpDepositAll();\n }\n\n /**\n * @notice Withdraw a Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAsset Address of the Vault collateral asset\n * @param _strategyAmount The amount of Vault collateral assets to withdraw\n */\n function withdraw(\n address _recipient,\n address _strategyAsset,\n uint256 _strategyAmount\n ) external override onlyVault nonReentrant {\n address[] memory strategyAssets = new address[](1);\n uint256[] memory strategyAmounts = new uint256[](1);\n strategyAssets[0] = _strategyAsset;\n strategyAmounts[0] = _strategyAmount;\n\n _withdraw(_recipient, strategyAssets, strategyAmounts);\n }\n\n /**\n * @notice Withdraw multiple Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAssets Addresses of the Vault collateral assets\n * @param _strategyAmounts The amounts of Vault collateral assets to withdraw\n */\n function withdraw(\n address _recipient,\n address[] calldata _strategyAssets,\n uint256[] calldata _strategyAmounts\n ) external onlyVault nonReentrant {\n _withdraw(_recipient, _strategyAssets, _strategyAmounts);\n }\n\n /**\n * @dev Withdraw multiple Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAssets Addresses of the Vault collateral assets\n * @param _strategyAmounts The amounts of Vault collateral assets to withdraw\n *\n * _withdrawal doesn't require a read-only re-entrancy protection since during the withdrawal\n * the function enters the Balancer Vault Context. If this function were called as part of\n * the attacking contract (while intercepting execution flow upon receiving ETH) the read-only\n * protection of the Balancer Vault would be triggered. Since the attacking contract would\n * already be in the Balancer Vault context and wouldn't be able to enter it again.\n */\n function _withdraw(\n address _recipient,\n address[] memory _strategyAssets,\n uint256[] memory _strategyAmounts\n ) internal {\n require(\n _strategyAssets.length == _strategyAmounts.length,\n \"Invalid input arrays\"\n );\n\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n require(\n assetToPToken[_strategyAssets[i]] != address(0),\n \"Unsupported asset\"\n );\n }\n\n // STEP 1 - Calculate the Balancer pool assets and amounts from the vault collateral assets\n\n // Get all the supported balancer pool assets\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n // Calculate the balancer pool assets and amounts to withdraw\n uint256[] memory poolAssetsAmountsOut = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n // Is the wrapped asset amount indexed by the assets array, not the order of the Balancer pool tokens\n // eg wstETH and sfrxETH amounts, not the stETH and frxETH amounts\n uint256[] memory strategyAssetsToPoolAssetsAmounts = new uint256[](\n _strategyAssets.length\n );\n\n // For each of the Balancer pool assets\n for (uint256 i = 0; i < tokens.length; ++i) {\n poolAssets[i] = address(tokens[i]);\n\n // Convert the Balancer pool asset back to a vault collateral asset\n address strategyAsset = _fromPoolAsset(poolAssets[i]);\n\n // for each of the vault assets\n for (uint256 j = 0; j < _strategyAssets.length; ++j) {\n // If the vault asset equals the vault asset mapped from the Balancer pool asset\n if (_strategyAssets[j] == strategyAsset) {\n (, poolAssetsAmountsOut[i]) = _toPoolAsset(\n strategyAsset,\n _strategyAmounts[j]\n );\n strategyAssetsToPoolAssetsAmounts[j] = poolAssetsAmountsOut[\n i\n ];\n\n /* Because of the potential Balancer rounding error mentioned below\n * the contract might receive 1-2 WEI smaller amount than required\n * in the withdraw user data encoding. If slightly lesser token amount\n * is received the strategy can not unwrap the pool asset as it is\n * smaller than expected.\n *\n * For that reason we `overshoot` the required tokens expected to\n * circumvent the error\n */\n if (poolAssetsAmountsOut[i] > 0) {\n poolAssetsAmountsOut[i] += 2;\n }\n }\n }\n }\n\n // STEP 2 - Calculate the max about of Balancer Pool Tokens (BPT) to withdraw\n\n // Estimate the required amount of Balancer Pool Tokens (BPT) for the assets\n uint256 maxBPTtoWithdraw = _getBPTExpected(\n poolAssets,\n /* all non 0 values are overshot by 2 WEI and with the expected mainnet\n * ~1% withdrawal deviation, the 2 WEI aren't important\n */\n poolAssetsAmountsOut\n );\n // Increase BPTs by the max allowed deviation\n // Any excess BPTs will be left in this strategy contract\n maxBPTtoWithdraw = maxBPTtoWithdraw.mulTruncate(\n 1e18 + maxWithdrawalDeviation\n );\n\n // STEP 3 - Withdraw the Balancer Pool Tokens (BPT) from Aura to this strategy contract\n\n // Withdraw BPT from Aura allowing for BPTs left in this strategy contract from previous withdrawals\n _lpWithdraw(\n maxBPTtoWithdraw - IERC20(platformAddress).balanceOf(address(this))\n );\n\n // STEP 4 - Withdraw the balancer pool assets from the pool\n\n /* Custom asset exit: BPT_IN_FOR_EXACT_TOKENS_OUT:\n * User sends an estimated but unknown (computed at run time) quantity of BPT,\n * and receives precise quantities of specified tokens.\n *\n * ['uint256', 'uint256[]', 'uint256']\n * [BPT_IN_FOR_EXACT_TOKENS_OUT, amountsOut, maxBPTAmountIn]\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.BPT_IN_FOR_EXACT_TOKENS_OUT,\n poolAssetsAmountsOut,\n maxBPTtoWithdraw\n );\n\n IBalancerVault.ExitPoolRequest memory request = IBalancerVault\n .ExitPoolRequest(\n poolAssets,\n /* We specify the exact amount of a tokens we are expecting in the encoded\n * userData, for that reason we don't need to specify the amountsOut here.\n *\n * Also Balancer has a rounding issue that can make a transaction fail:\n * https://github.com/balancer/balancer-v2-monorepo/issues/2541\n * which is an extra reason why this field is empty.\n */\n new uint256[](tokens.length),\n userData,\n false\n );\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n /* Payable keyword is required because of the IBalancerVault interface even though\n * this strategy shall never be receiving native ETH\n */\n payable(address(this)),\n request\n );\n\n // STEP 5 - Re-deposit any left over BPT tokens back into Aura\n /* When concluding how much of BPT we need to withdraw from Aura we overshoot by\n * roughly around 1% (initial mainnet setting of maxWithdrawalDeviation). After exiting\n * the pool strategy could have left over BPT tokens that are not earning boosted yield.\n * We re-deploy those back in.\n */\n _lpDepositAll();\n\n // STEP 6 - Unswap balancer pool assets to vault collateral assets and send to the vault.\n\n // For each of the specified assets\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n // Unwrap assets like wstETH and sfrxETH to rebasing assets stETH and frxETH\n if (strategyAssetsToPoolAssetsAmounts[i] > 0) {\n _unwrapPoolAsset(\n _strategyAssets[i],\n strategyAssetsToPoolAssetsAmounts[i]\n );\n }\n\n // Transfer the vault collateral assets to the recipient, which is typically the vault\n if (_strategyAmounts[i] > 0) {\n IERC20(_strategyAssets[i]).safeTransfer(\n _recipient,\n _strategyAmounts[i]\n );\n\n emit Withdrawal(\n _strategyAssets[i],\n platformAddress,\n _strategyAmounts[i]\n );\n }\n }\n }\n\n /**\n * @notice Withdraws all supported Vault collateral assets from the Balancer pool\n * and send to the OToken's Vault.\n *\n * Is only executable by the OToken's Vault or the Governor.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n // STEP 1 - Withdraw all Balancer Pool Tokens (BPT) from Aura to this strategy contract\n\n _lpWithdrawAll();\n // Get the BPTs withdrawn from Aura plus any that were already in this strategy contract\n uint256 BPTtoWithdraw = IERC20(platformAddress).balanceOf(\n address(this)\n );\n // Get the balancer pool assets and their total balances\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n uint256[] memory minAmountsOut = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n for (uint256 i = 0; i < tokens.length; ++i) {\n poolAssets[i] = address(tokens[i]);\n }\n\n // STEP 2 - Withdraw the Balancer pool assets from the pool\n /* Proportional exit: EXACT_BPT_IN_FOR_TOKENS_OUT:\n * User sends a precise quantity of BPT, and receives an estimated but unknown\n * (computed at run time) quantity of a single token\n *\n * ['uint256', 'uint256']\n * [EXACT_BPT_IN_FOR_TOKENS_OUT, bptAmountIn]\n *\n * It is ok to pass an empty minAmountsOut since tilting the pool in any direction\n * when doing a proportional exit can only be beneficial to the strategy. Since\n * it will receive more of the underlying tokens for the BPT traded in.\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.EXACT_BPT_IN_FOR_TOKENS_OUT,\n BPTtoWithdraw\n );\n\n IBalancerVault.ExitPoolRequest memory request = IBalancerVault\n .ExitPoolRequest(poolAssets, minAmountsOut, userData, false);\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n /* Payable keyword is required because of the IBalancerVault interface even though\n * this strategy shall never be receiving native ETH\n */\n payable(address(this)),\n request\n );\n\n // STEP 3 - Convert the balancer pool assets to the vault collateral assets and send to the vault\n // For each of the Balancer pool assets\n for (uint256 i = 0; i < tokens.length; ++i) {\n address poolAsset = address(tokens[i]);\n // Convert the balancer pool asset to the strategy asset\n address strategyAsset = _fromPoolAsset(poolAsset);\n // Get the balancer pool assets withdraw from the pool plus any that were already in this strategy contract\n uint256 poolAssetAmount = IERC20(poolAsset).balanceOf(\n address(this)\n );\n\n // Unwrap assets like wstETH and sfrxETH to rebasing assets stETH and frxETH\n uint256 unwrappedAmount = 0;\n if (poolAssetAmount > 0) {\n unwrappedAmount = _unwrapPoolAsset(\n strategyAsset,\n poolAssetAmount\n );\n }\n\n // Transfer the vault collateral assets to the vault\n if (unwrappedAmount > 0) {\n IERC20(strategyAsset).safeTransfer(\n vaultAddress,\n unwrappedAmount\n );\n emit Withdrawal(\n strategyAsset,\n platformAddress,\n unwrappedAmount\n );\n }\n }\n }\n\n /**\n * @notice Approves the Balancer Vault to transfer poolAsset counterparts\n * of all of the supported assets from this strategy. E.g. stETH is a supported\n * strategy and Balancer Vault gets unlimited approval to transfer wstETH.\n *\n * If Balancer pool uses a wrapped version of a supported asset then also approve\n * unlimited usage of an asset to the contract responsible for wrapping.\n *\n * Approve unlimited spending by Balancer Vault and Aura reward pool of the\n * pool BPT tokens.\n *\n * Is only executable by the Governor.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n _abstractSetPToken(assetsMapped[i], platformAddress);\n }\n _approveBase();\n }\n\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address) internal override {\n address poolAsset = _toPoolAsset(_asset);\n if (_asset == stETH) {\n // slither-disable-next-line unused-return\n IERC20(stETH).approve(wstETH, type(uint256).max);\n } else if (_asset == frxETH) {\n // slither-disable-next-line unused-return\n IERC20(frxETH).approve(sfrxETH, type(uint256).max);\n }\n _approveAsset(poolAsset);\n }\n\n /**\n * @dev Approves the Balancer Vault to transfer an asset from\n * this strategy. The assets could be a Vault collateral asset\n * like WETH or rETH; or a Balancer pool asset that wraps the vault asset\n * like wstETH or sfrxETH.\n */\n function _approveAsset(address _asset) internal {\n IERC20 asset = IERC20(_asset);\n // slither-disable-next-line unused-return\n asset.approve(address(balancerVault), type(uint256).max);\n }\n\n /**\n * @notice Returns the rate supplied by the Balancer configured rate\n * provider. Rate is used to normalize the token to common underlying\n * pool denominator. (ETH for ETH Liquid staking derivatives)\n *\n * @param _asset Address of the Balancer pool asset\n * @return rate of the corresponding asset\n */\n function _getRateProviderRate(address _asset)\n internal\n view\n override\n returns (uint256)\n {\n IMetaStablePool pool = IMetaStablePool(platformAddress);\n IRateProvider[] memory providers = pool.getRateProviders();\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n\n uint256 providersLength = providers.length;\n for (uint256 i = 0; i < providersLength; ++i) {\n // _assets and corresponding rate providers are all in the same order\n if (address(tokens[i]) == _asset) {\n // rate provider doesn't exist, defaults to 1e18\n if (address(providers[i]) == address(0)) {\n return 1e18;\n }\n return providers[i].getRate();\n }\n }\n\n // should never happen\n assert(false);\n }\n}\n" + }, + "contracts/strategies/balancer/VaultReentrancyLib.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity >=0.7.0 <0.9.0;\n\nimport \"../../utils/BalancerErrors.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\n\nlibrary VaultReentrancyLib {\n /**\n * @dev Ensure we are not in a Vault context when this function is called, by attempting a no-op internal\n * balance operation. If we are already in a Vault transaction (e.g., a swap, join, or exit), the Vault's\n * reentrancy protection will cause this function to revert.\n *\n * The exact function call doesn't really matter: we're just trying to trigger the Vault reentrancy check\n * (and not hurt anything in case it works). An empty operation array with no specific operation at all works\n * for that purpose, and is also the least expensive in terms of gas and bytecode size.\n *\n * Call this at the top of any function that can cause a state change in a pool and is either public itself,\n * or called by a public function *outside* a Vault operation (e.g., join, exit, or swap).\n *\n * If this is *not* called in functions that are vulnerable to the read-only reentrancy issue described\n * here (https://forum.balancer.fi/t/reentrancy-vulnerability-scope-expanded/4345), those functions are unsafe,\n * and subject to manipulation that may result in loss of funds.\n */\n function ensureNotInVaultContext(IBalancerVault vault) internal view {\n // Perform the following operation to trigger the Vault's reentrancy guard:\n //\n // IBalancerVault.UserBalanceOp[] memory noop = new IBalancerVault.UserBalanceOp[](0);\n // _vault.manageUserBalance(noop);\n //\n // However, use a static call so that it can be a view function (even though the function is non-view).\n // This allows the library to be used more widely, as some functions that need to be protected might be\n // view.\n //\n // This staticcall always reverts, but we need to make sure it doesn't fail due to a re-entrancy attack.\n // Staticcalls consume all gas forwarded to them on a revert caused by storage modification.\n // By default, almost the entire available gas is forwarded to the staticcall,\n // causing the entire call to revert with an 'out of gas' error.\n //\n // We set the gas limit to 10k for the staticcall to\n // avoid wasting gas when it reverts due to storage modification.\n // `manageUserBalance` is a non-reentrant function in the Vault, so calling it invokes `_enterNonReentrant`\n // in the `ReentrancyGuard` contract, reproduced here:\n //\n // function _enterNonReentrant() private {\n // // If the Vault is actually being reentered, it will revert in the first line, at the `_require` that\n // // checks the reentrancy flag, with \"BAL#400\" (corresponding to Errors.REENTRANCY) in the revertData.\n // // The full revertData will be: `abi.encodeWithSignature(\"Error(string)\", \"BAL#400\")`.\n // _require(_status != _ENTERED, Errors.REENTRANCY);\n //\n // // If the Vault is not being reentered, the check above will pass: but it will *still* revert,\n // // because the next line attempts to modify storage during a staticcall. However, this type of\n // // failure results in empty revertData.\n // _status = _ENTERED;\n // }\n //\n // So based on this analysis, there are only two possible revertData values: empty, or abi.encoded BAL#400.\n //\n // It is of course much more bytecode and gas efficient to check for zero-length revertData than to compare it\n // to the encoded REENTRANCY revertData.\n //\n // While it should be impossible for the call to fail in any other way (especially since it reverts before\n // `manageUserBalance` even gets called), any other error would generate non-zero revertData, so checking for\n // empty data guards against this case too.\n\n (, bytes memory revertData) = address(vault).staticcall{ gas: 10_000 }(\n abi.encodeWithSelector(vault.manageUserBalance.selector, 0)\n );\n\n _require(revertData.length == 0, Errors.REENTRANCY);\n }\n}\n" + }, + "contracts/strategies/BaseCurveAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for the Curve OETH/WETH pool\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { ICurveStableSwapNG } from \"../interfaces/ICurveStableSwapNG.sol\";\nimport { ICurveXChainLiquidityGauge } from \"../interfaces/ICurveXChainLiquidityGauge.sol\";\nimport { IChildLiquidityGaugeFactory } from \"../interfaces/IChildLiquidityGaugeFactory.sol\";\n\ncontract BaseCurveAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @dev a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n // New immutable variables that must be set in the constructor\n /**\n * @notice Address of the Wrapped ETH (WETH) contract.\n */\n IWETH9 public immutable weth;\n\n /**\n * @notice Address of the OETH token contract.\n */\n IERC20 public immutable oeth;\n\n /**\n * @notice Address of the LP (Liquidity Provider) token contract.\n */\n IERC20 public immutable lpToken;\n\n /**\n * @notice Address of the Curve StableSwap NG pool contract.\n */\n ICurveStableSwapNG public immutable curvePool;\n\n /**\n * @notice Address of the Curve X-Chain Liquidity Gauge contract.\n */\n ICurveXChainLiquidityGauge public immutable gauge;\n\n /**\n * @notice Address of the Child Liquidity Gauge Factory contract.\n */\n IChildLiquidityGaugeFactory public immutable gaugeFactory;\n\n // Ordered list of pool assets\n uint128 public immutable oethCoinIndex;\n uint128 public immutable wethCoinIndex;\n\n /**\n * @notice Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n uint256 public maxSlippage;\n\n event MaxSlippageUpdated(uint256 newMaxSlippage);\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balancesBefore = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffBefore = balancesBefore[wethCoinIndex].toInt256() -\n balancesBefore[oethCoinIndex].toInt256();\n\n _;\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balancesAfter = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffAfter = balancesAfter[wethCoinIndex].toInt256() -\n balancesAfter[oethCoinIndex].toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of ETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _oeth,\n address _weth,\n address _gauge,\n address _gaugeFactory,\n uint128 _oethCoinIndex,\n uint128 _wethCoinIndex\n ) InitializableAbstractStrategy(_baseConfig) {\n oethCoinIndex = _oethCoinIndex;\n wethCoinIndex = _wethCoinIndex;\n\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveStableSwapNG(_baseConfig.platformAddress);\n\n oeth = IERC20(_oeth);\n weth = IWETH9(_weth);\n gauge = ICurveXChainLiquidityGauge(_gauge);\n gaugeFactory = IChildLiquidityGaugeFactory(_gaugeFactory);\n\n _setGovernor(address(0));\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV\n * @param _maxSlippage Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV\n uint256 _maxSlippage\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n address[] memory _assets = new address[](1);\n _assets[0] = address(weth);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n _setMaxSlippage(_maxSlippage);\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit WETH into the Curve pool\n * @param _weth Address of Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to deposit.\n */\n function deposit(address _weth, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_weth, _amount);\n }\n\n function _deposit(address _weth, uint256 _wethAmount) internal {\n require(_wethAmount > 0, \"Must deposit something\");\n require(_weth == address(weth), \"Can only deposit WETH\");\n\n emit Deposit(_weth, address(lpToken), _wethAmount);\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 oethToAdd = uint256(\n _max(\n 0,\n balances[wethCoinIndex].toInt256() +\n _wethAmount.toInt256() -\n balances[oethCoinIndex].toInt256()\n )\n );\n\n /* Add so much OETH so that the pool ends up being balanced. And at minimum\n * add as much OETH as WETH and at maximum twice as much OETH.\n */\n oethToAdd = Math.max(oethToAdd, _wethAmount);\n oethToAdd = Math.min(oethToAdd, _wethAmount * 2);\n\n /* Mint OETH with a strategy that attempts to contribute to stability of OETH/WETH pool. Try\n * to mint so much OETH that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OETH minted will always be at least equal or greater\n * to WETH amount deployed. And never larger than twice the WETH amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(oethToAdd);\n\n emit Deposit(address(oeth), address(lpToken), oethToAdd);\n\n uint256[] memory _amounts = new uint256[](2);\n _amounts[wethCoinIndex] = _wethAmount;\n _amounts[oethCoinIndex] = oethToAdd;\n\n uint256 valueInLpTokens = (_wethAmount + oethToAdd).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Do the deposit to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(_amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool's LP tokens into the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n /**\n * @notice Deposit the strategy's entire balance of WETH into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = weth.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(weth), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _weth Address of the Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to withdraw.\n */\n function withdraw(\n address _recipient,\n address _weth,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_weth == address(weth), \"Can only withdraw WETH\");\n\n emit Withdrawal(_weth, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(_amount);\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough WETH on balanced removal\n */\n uint256[] memory _minWithdrawalAmounts = new uint256[](2);\n _minWithdrawalAmounts[wethCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OETH and any that was left in the strategy\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n\n // Transfer WETH to the recipient\n require(\n weth.transfer(_recipient, _amount),\n \"Transfer of WETH not successful\"\n );\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n function calcTokenToBurn(uint256 _wethAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much WETH\n * we want we can determine how much of OETH we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolWETHBalance = curvePool.balances(wethCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolWETHBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_wethAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = gauge.balanceOf(address(this));\n // Can not withdraw zero LP tokens from the gauge\n if (gaugeTokens == 0) return;\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[] memory minWithdrawAmounts = new uint256[](2);\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(\n lpToken.balanceOf(address(this)),\n minWithdrawAmounts\n );\n\n // Burn all OETH\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Get the strategy contract's WETH balance.\n // This includes all that was removed from the Curve pool and\n // any ether that was sitting in the strategy contract before the removal.\n uint256 ethBalance = weth.balanceOf(address(this));\n require(\n weth.transfer(vaultAddress, ethBalance),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethBalance);\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[] memory amounts = new uint256[](2);\n amounts[oethCoinIndex] = _oTokens;\n\n // Convert OETH to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool LP tokens to the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Deposit(address(oeth), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough ETH.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove OTokens from the Curve pool\n uint256 oethToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n oethCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /**\n * @notice One-sided remove of ETH from the Curve pool, convert to WETH\n * and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for ETH.\n * @dev Curve pool LP tokens is used rather than WETH assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of ETH. Curve's `calc_token_amount` functioun does not include fees.\n * A 3rd party libary can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * caclulate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Curve gauge and remove ETH from the Curve pool\n uint256 ethAmount = _withdrawAndRemoveFromPool(\n _lpTokens,\n wethCoinIndex\n );\n\n // Transfer WETH to the vault\n require(\n weth.transfer(vaultAddress, ethAmount),\n \"Transfer of WETH not successful\"\n );\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(weth), address(lpToken), ethAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the Convex pool and\n * do a one-sided remove of ETH or OETH from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the Convex pool.\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = ETH, 1 = OETH.\n * @return coinsRemoved The amount of ETH or OETH removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Curve gauge\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to ETH value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n // Apply slippage to ETH value\n uint256 minAmount = valueInEth.mulTruncate(uint256(1e18) - maxSlippage);\n\n // Remove just the ETH from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethbSupply = oeth.totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethbSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // CRV rewards flow.\n //---\n // CRV inflation:\n // Gauge receive CRV rewards from inflation.\n // Each checkpoint on the gauge send this CRV inflation to gauge factory.\n // This strategy should call mint on the gauge factory to collect the CRV rewards.\n // ---\n // Extra rewards:\n // Calling claim_rewards on the gauge will only claim extra rewards (outside of CRV).\n // ---\n\n // Mint CRV on Child Liquidity gauge factory\n gaugeFactory.mint(address(gauge));\n // Collect extra gauge rewards (outside of CRV)\n gauge.claim_rewards();\n\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _lpAmount) internal {\n // withdraw lp tokens from the gauge without claiming rewards\n gauge.withdraw(_lpAmount);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // WETH balance needed here for the balance check that happens from vault during depositing.\n balance = weth.balanceOf(address(this));\n uint256 lpTokens = gauge.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += (lpTokens * curvePool.get_virtual_price()) / 1e18;\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(weth);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Sets the maximum slippage allowed for any swap/liquidity operation\n * @param _maxSlippage Maximum slippage allowed, 1e18 = 100%.\n */\n function setMaxSlippage(uint256 _maxSlippage) external onlyGovernor {\n _setMaxSlippage(_maxSlippage);\n }\n\n function _setMaxSlippage(uint256 _maxSlippage) internal {\n require(_maxSlippage <= 5e16, \"Slippage must be less than 100%\");\n maxSlippage = _maxSlippage;\n emit MaxSlippageUpdated(_maxSlippage);\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OETH (required for adding liquidity)\n // slither-disable-next-line unused-return\n oeth.approve(platformAddress, type(uint256).max);\n\n // Approve Curve pool for WETH (required for adding liquidity)\n // slither-disable-next-line unused-return\n weth.approve(platformAddress, type(uint256).max);\n\n // Approve Curve gauge contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Curve gauge.\n // slither-disable-next-line unused-return\n lpToken.approve(address(gauge), type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/BridgedWOETHStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, SafeERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { AggregatorV3Interface } from \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\n\ncontract BridgedWOETHStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using StableMath for uint128;\n using SafeCast for uint256;\n using SafeERC20 for IERC20;\n\n event MaxPriceDiffBpsUpdated(uint128 oldValue, uint128 newValue);\n event WOETHPriceUpdated(uint128 oldValue, uint128 newValue);\n\n IWETH9 public immutable weth;\n IERC20 public immutable bridgedWOETH;\n IERC20 public immutable oethb;\n\n uint256 public constant MAX_PRICE_STALENESS = 2 days;\n\n uint128 public lastOraclePrice;\n uint128 public maxPriceDiffBps;\n\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _weth,\n address _bridgedWOETH,\n address _oethb\n ) InitializableAbstractStrategy(_stratConfig) {\n weth = IWETH9(_weth);\n bridgedWOETH = IERC20(_bridgedWOETH);\n oethb = IERC20(_oethb);\n }\n\n function initialize(uint128 _maxPriceDiffBps)\n external\n onlyGovernor\n initializer\n {\n InitializableAbstractStrategy._initialize(\n new address[](0), // No reward tokens\n new address[](0), // No assets\n new address[](0) // No pTokens\n );\n\n _setMaxPriceDiffBps(_maxPriceDiffBps);\n }\n\n /**\n * @dev Sets the max price diff bps for the wOETH value appreciation\n * @param _maxPriceDiffBps Bps value, 10k == 100%\n */\n function setMaxPriceDiffBps(uint128 _maxPriceDiffBps)\n external\n onlyGovernor\n {\n _setMaxPriceDiffBps(_maxPriceDiffBps);\n }\n\n /**\n * @dev Sets the max price diff bps for the wOETH value appreciation\n * @param _maxPriceDiffBps Bps value, 10k == 100%\n */\n function _setMaxPriceDiffBps(uint128 _maxPriceDiffBps) internal {\n require(\n _maxPriceDiffBps > 0 && _maxPriceDiffBps <= 10000,\n \"Invalid bps value\"\n );\n\n emit MaxPriceDiffBpsUpdated(maxPriceDiffBps, _maxPriceDiffBps);\n\n maxPriceDiffBps = _maxPriceDiffBps;\n }\n\n /**\n * @dev Wrapper for _updateWOETHOraclePrice with nonReentrant flag\n * @return The latest price of wOETH from Oracle\n */\n function updateWOETHOraclePrice() external nonReentrant returns (uint256) {\n return _updateWOETHOraclePrice();\n }\n\n /**\n * @dev Finds the value of bridged wOETH from the Oracle.\n * Ensures that it's within the bounds and reasonable.\n * And stores it.\n *\n * NOTE: Intentionally not caching `Vault.priceProvider` here,\n * since doing so would mean that we also have to update this\n * strategy every time there's a change in oracle router.\n * Besides on L2, the gas is considerably cheaper than mainnet.\n *\n * @return Latest price from oracle\n */\n function _updateWOETHOraclePrice() internal returns (uint256) {\n // WETH price per unit of bridged wOETH\n uint256 oraclePrice = IOracle(IVault(vaultAddress).priceProvider())\n .price(address(bridgedWOETH));\n\n // 1 wOETH > 1 WETH, always\n require(oraclePrice > 1 ether, \"Invalid wOETH value\");\n\n uint128 oraclePrice128 = oraclePrice.toUint128();\n\n // Do some checks\n if (lastOraclePrice > 0) {\n // Make sure the value only goes up\n require(oraclePrice128 >= lastOraclePrice, \"Negative wOETH yield\");\n\n // lastOraclePrice * (1 + maxPriceDiffBps)\n uint256 maxPrice = (lastOraclePrice * (1e4 + maxPriceDiffBps)) /\n 1e4;\n\n // And that it's within the bounds.\n require(oraclePrice128 <= maxPrice, \"Price diff beyond threshold\");\n }\n\n emit WOETHPriceUpdated(lastOraclePrice, oraclePrice128);\n\n // Store the price\n lastOraclePrice = oraclePrice128;\n\n return oraclePrice;\n }\n\n /**\n * @dev Computes & returns the value of given wOETH in WETH\n * @param woethAmount Amount of wOETH\n * @return Value of wOETH in WETH (using the last stored oracle price)\n */\n function getBridgedWOETHValue(uint256 woethAmount)\n public\n view\n returns (uint256)\n {\n return (woethAmount * lastOraclePrice) / 1 ether;\n }\n\n /**\n * @dev Takes in bridged wOETH and mints & returns\n * equivalent amount of OETHb.\n * @param woethAmount Amount of bridged wOETH to transfer in\n */\n function depositBridgedWOETH(uint256 woethAmount)\n external\n onlyGovernorOrStrategist\n nonReentrant\n {\n // Update wOETH price\n uint256 oraclePrice = _updateWOETHOraclePrice();\n\n // Figure out how much they are worth\n uint256 oethToMint = (woethAmount * oraclePrice) / 1 ether;\n\n require(oethToMint > 0, \"Invalid deposit amount\");\n\n // There's no pToken, however, it just uses WOETH address in the event\n emit Deposit(address(weth), address(bridgedWOETH), oethToMint);\n\n // Mint OETHb tokens and transfer it to the caller\n IVault(vaultAddress).mintForStrategy(oethToMint);\n\n // Transfer out minted OETHb\n // slither-disable-next-line unchecked-transfer unused-return\n oethb.transfer(msg.sender, oethToMint);\n\n // Transfer in all bridged wOETH tokens\n // slither-disable-next-line unchecked-transfer unused-return\n bridgedWOETH.transferFrom(msg.sender, address(this), woethAmount);\n }\n\n /**\n * @dev Takes in OETHb and burns it and returns\n * equivalent amount of bridged wOETH.\n * @param oethToBurn Amount of OETHb to burn\n */\n function withdrawBridgedWOETH(uint256 oethToBurn)\n external\n onlyGovernorOrStrategist\n nonReentrant\n {\n // Update wOETH price\n uint256 oraclePrice = _updateWOETHOraclePrice();\n\n // Figure out how much they are worth\n uint256 woethAmount = (oethToBurn * 1 ether) / oraclePrice;\n\n require(woethAmount > 0, \"Invalid withdraw amount\");\n\n // There's no pToken, however, it just uses WOETH address in the event\n emit Withdrawal(address(weth), address(bridgedWOETH), oethToBurn);\n\n // Transfer WOETH back\n // slither-disable-next-line unchecked-transfer unused-return\n bridgedWOETH.transfer(msg.sender, woethAmount);\n\n // Transfer in OETHb\n // slither-disable-next-line unchecked-transfer unused-return\n oethb.transferFrom(msg.sender, address(this), oethToBurn);\n\n // Burn OETHb\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n }\n\n /**\n * @notice Returns the amount of backing WETH the strategy holds\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // Figure out how much wOETH is worth at the time.\n // Always uses the last stored oracle price.\n // Call updateWOETHOraclePrice manually to pull in latest yields.\n\n // NOTE: If the contract has been deployed but the call to\n // `updateWOETHOraclePrice()` has never been made, then this\n // will return zero. It should be fine because the strategy\n // should update the price whenever a deposit/withdraw happens.\n\n // If `updateWOETHOraclePrice()` hasn't been called in a while,\n // the strategy will underreport its holdings but never overreport it.\n\n balance =\n (bridgedWOETH.balanceOf(address(this)) * lastOraclePrice) /\n 1 ether;\n }\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n // Strategist deposits bridged wOETH but the contract only\n // reports the balance in WETH. As far as Vault is concerned,\n // it isn't aware of bridged wOETH token\n return _asset == address(weth);\n }\n\n /***************************************\n Overridden methods\n ****************************************/\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function transferToken(address _asset, uint256 _amount)\n public\n override\n onlyGovernor\n {\n require(\n _asset != address(bridgedWOETH) && _asset != address(weth),\n \"Cannot transfer supported asset\"\n );\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice deposit() function not used for this strategy\n */\n function deposit(address, uint256)\n external\n override\n onlyVault\n nonReentrant\n {\n // Use depositBridgedWOETH() instead\n require(false, \"Deposit disabled\");\n }\n\n /**\n * @notice depositAll() function not used for this strategy\n */\n function depositAll() external override onlyVault nonReentrant {\n // Use depositBridgedWOETH() instead\n require(false, \"Deposit disabled\");\n }\n\n /**\n * @notice withdraw() function not used for this strategy\n */\n function withdraw(\n // solhint-disable-next-line no-unused-vars\n address _recipient,\n // solhint-disable-next-line no-unused-vars\n address _asset,\n // solhint-disable-next-line no-unused-vars\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(false, \"Withdrawal disabled\");\n }\n\n /**\n * @notice withdrawAll() function not used for this strategy\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n // Withdrawal disabled\n }\n\n function _abstractSetPToken(address, address) internal override {\n revert(\"No pTokens are used\");\n }\n\n function safeApproveAllTokens() external override {}\n\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function removePToken(uint256) external override {\n revert(\"No pTokens are used\");\n }\n\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function collectRewardTokens() external override {}\n}\n" + }, + "contracts/strategies/CompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Compound Strategy\n * @notice Investment strategy for Compound like lending platforms. eg Compound and Flux\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICERC20 } from \"./ICompound.sol\";\nimport { AbstractCompoundStrategy, InitializableAbstractStrategy } from \"./AbstractCompoundStrategy.sol\";\nimport { IComptroller } from \"../interfaces/IComptroller.sol\";\nimport { IERC20 } from \"../utils/InitializableAbstractStrategy.sol\";\n\ncontract CompoundStrategy is AbstractCompoundStrategy {\n using SafeERC20 for IERC20;\n event SkippedWithdrawal(address asset, uint256 amount);\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @notice initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @notice Collect accumulated COMP and send to Harvester.\n */\n function collectRewardTokens()\n external\n virtual\n override\n onlyHarvester\n nonReentrant\n {\n // Claim COMP from Comptroller\n ICERC20 cToken = _getCTokenFor(assetsMapped[0]);\n IComptroller comptroller = IComptroller(cToken.comptroller());\n // Only collect from active cTokens, saves gas\n address[] memory ctokensToCollect = new address[](assetsMapped.length);\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n ctokensToCollect[i] = address(_getCTokenFor(assetsMapped[i]));\n }\n // Claim only for this strategy\n address[] memory claimers = new address[](1);\n claimers[0] = address(this);\n // Claim COMP from Comptroller. Only collect for supply, saves gas\n comptroller.claimComp(claimers, ctokensToCollect, false, true);\n // Transfer COMP to Harvester\n IERC20 rewardToken = IERC20(rewardTokenAddresses[0]);\n uint256 balance = rewardToken.balanceOf(address(this));\n emit RewardTokenCollected(\n harvesterAddress,\n rewardTokenAddresses[0],\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n\n /**\n * @notice Deposit asset into the underlying platform\n * @param _asset Address of asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit an asset into the underlying platform\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n ICERC20 cToken = _getCTokenFor(_asset);\n emit Deposit(_asset, address(cToken), _amount);\n require(cToken.mint(_amount) == 0, \"cToken mint failed\");\n }\n\n /**\n * @notice Deposit the entire balance of any supported asset in the strategy into the underlying platform\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n uint256 assetBalance = asset.balanceOf(address(this));\n if (assetBalance > 0) {\n _deposit(address(asset), assetBalance);\n }\n }\n }\n\n /**\n * @notice Withdraw an asset from the underlying platform\n * @param _recipient Address to receive withdrawn assets\n * @param _asset Address of the asset to withdraw\n * @param _amount Amount of assets to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n ICERC20 cToken = _getCTokenFor(_asset);\n // If redeeming 0 cTokens, just skip, else COMP will revert\n uint256 cTokensToRedeem = _convertUnderlyingToCToken(cToken, _amount);\n if (cTokensToRedeem == 0) {\n emit SkippedWithdrawal(_asset, _amount);\n return;\n }\n\n emit Withdrawal(_asset, address(cToken), _amount);\n require(cToken.redeemUnderlying(_amount) == 0, \"Redeem failed\");\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / cTokens\n * We need to approve the cToken and give it permission to spend the asset\n * @param _asset Address of the asset to approve. eg DAI\n * @param _pToken The pToken for the approval. eg cDAI or fDAI\n */\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n // Safe approval\n IERC20(_asset).safeApprove(_pToken, 0);\n IERC20(_asset).safeApprove(_pToken, type(uint256).max);\n }\n\n /**\n * @notice Remove all supported assets from the underlying platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n // Redeem entire balance of cToken\n ICERC20 cToken = _getCTokenFor(address(asset));\n uint256 cTokenBalance = cToken.balanceOf(address(this));\n if (cTokenBalance > 0) {\n require(cToken.redeem(cTokenBalance) == 0, \"Redeem failed\");\n uint256 assetBalance = asset.balanceOf(address(this));\n // Transfer entire balance to Vault\n asset.safeTransfer(vaultAddress, assetBalance);\n\n emit Withdrawal(address(asset), address(cToken), assetBalance);\n }\n }\n }\n\n /**\n * @notice Get the total asset value held in the underlying platform\n * This includes any interest that was generated since depositing.\n * The exchange rate between the cToken and asset gradually increases,\n * causing the cToken to be worth more corresponding asset.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n // Balance is always with token cToken decimals\n ICERC20 cToken = _getCTokenFor(_asset);\n balance = _checkBalance(cToken);\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * underlying = (cTokenAmt * exchangeRate) / 1e18\n * @param _cToken cToken for which to check balance\n * @return balance Total value of the asset in the platform\n */\n function _checkBalance(ICERC20 _cToken)\n internal\n view\n returns (uint256 balance)\n {\n // e.g. 50e8*205316390724364402565641705 / 1e18 = 1.0265..e18\n balance =\n (_cToken.balanceOf(address(this)) * _cToken.exchangeRateStored()) /\n 1e18;\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding cToken,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens() external override {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n address cToken = assetToPToken[address(asset)];\n // Safe approval\n asset.safeApprove(cToken, 0);\n asset.safeApprove(cToken, type(uint256).max);\n }\n }\n}\n" + }, + "contracts/strategies/ConvexEthMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Convex Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for the Curve OETH/ETH pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { ICurveETHPoolV1 } from \"./ICurveETHPoolV1.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\n\ncontract ConvexEthMetaStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 public constant MAX_SLIPPAGE = 1e16; // 1%, same as the Curve UI\n address public constant ETH_ADDRESS =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n // The following slots have been deprecated with immutable variables\n // slither-disable-next-line constable-states\n address private _deprecated_cvxDepositorAddress;\n // slither-disable-next-line constable-states\n address private _deprecated_cvxRewardStaker;\n // slither-disable-next-line constable-states\n uint256 private _deprecated_cvxDepositorPTokenId;\n // slither-disable-next-line constable-states\n address private _deprecated_curvePool;\n // slither-disable-next-line constable-states\n address private _deprecated_lpToken;\n // slither-disable-next-line constable-states\n address private _deprecated_oeth;\n // slither-disable-next-line constable-states\n address private _deprecated_weth;\n\n // Ordered list of pool assets\n // slither-disable-next-line constable-states\n uint128 private _deprecated_oethCoinIndex;\n // slither-disable-next-line constable-states\n uint128 private _deprecated_ethCoinIndex;\n\n // New immutable variables that must be set in the constructor\n address public immutable cvxDepositorAddress;\n IRewardStaking public immutable cvxRewardStaker;\n uint256 public immutable cvxDepositorPTokenId;\n ICurveETHPoolV1 public immutable curvePool;\n IERC20 public immutable lpToken;\n IERC20 public immutable oeth;\n IWETH9 public immutable weth;\n\n // Ordered list of pool assets\n uint128 public constant oethCoinIndex = 1;\n uint128 public constant ethCoinIndex = 0;\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier only works on functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balancesBefore = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffBefore = int256(balancesBefore[ethCoinIndex]) -\n int256(balancesBefore[oethCoinIndex]);\n\n _;\n\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balancesAfter = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffAfter = int256(balancesAfter[ethCoinIndex]) -\n int256(balancesAfter[oethCoinIndex]);\n\n if (diffBefore <= 0) {\n // If the pool was originally imbalanced in favor of OETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n }\n if (diffBefore >= 0) {\n // If the pool was originally imbalanced in favor of ETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n // Used to circumvent the stack too deep issue\n struct ConvexEthMetaConfig {\n address cvxDepositorAddress; //Address of the Convex depositor(AKA booster) for this pool\n address cvxRewardStakerAddress; //Address of the CVX rewards staker\n uint256 cvxDepositorPTokenId; //Pid of the pool referred to by Depositor and staker\n address oethAddress; //Address of OETH token\n address wethAddress; //Address of WETH\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n ConvexEthMetaConfig memory _convexConfig\n ) InitializableAbstractStrategy(_baseConfig) {\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveETHPoolV1(_baseConfig.platformAddress);\n\n cvxDepositorAddress = _convexConfig.cvxDepositorAddress;\n cvxRewardStaker = IRewardStaking(_convexConfig.cvxRewardStakerAddress);\n cvxDepositorPTokenId = _convexConfig.cvxDepositorPTokenId;\n oeth = IERC20(_convexConfig.oethAddress);\n weth = IWETH9(_convexConfig.wethAddress);\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. eg WETH\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets // WETH\n ) external onlyGovernor initializer {\n require(_assets.length == 1, \"Must have exactly one asset\");\n require(_assets[0] == address(weth), \"Asset not WETH\");\n\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit WETH into the Curve pool\n * @param _weth Address of Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to deposit.\n */\n function deposit(address _weth, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_weth, _amount);\n }\n\n function _deposit(address _weth, uint256 _wethAmount) internal {\n require(_wethAmount > 0, \"Must deposit something\");\n require(_weth == address(weth), \"Can only deposit WETH\");\n weth.withdraw(_wethAmount);\n\n emit Deposit(_weth, address(lpToken), _wethAmount);\n\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 oethToAdd = uint256(\n _max(\n 0,\n int256(balances[ethCoinIndex]) +\n int256(_wethAmount) -\n int256(balances[oethCoinIndex])\n )\n );\n\n /* Add so much OETH so that the pool ends up being balanced. And at minimum\n * add as much OETH as WETH and at maximum twice as much OETH.\n */\n oethToAdd = Math.max(oethToAdd, _wethAmount);\n oethToAdd = Math.min(oethToAdd, _wethAmount * 2);\n\n /* Mint OETH with a strategy that attempts to contribute to stability of OETH/WETH pool. Try\n * to mint so much OETH that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OETH minted will always be at least equal or greater\n * to WETH amount deployed. And never larger than twice the WETH amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(oethToAdd);\n\n emit Deposit(address(oeth), address(lpToken), oethToAdd);\n\n uint256[2] memory _amounts;\n _amounts[ethCoinIndex] = _wethAmount;\n _amounts[oethCoinIndex] = oethToAdd;\n\n uint256 valueInLpTokens = (_wethAmount + oethToAdd).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Do the deposit to the Curve pool\n // slither-disable-next-line arbitrary-send\n uint256 lpDeposited = curvePool.add_liquidity{ value: _wethAmount }(\n _amounts,\n minMintAmount\n );\n\n // Deposit the Curve pool's LP tokens into the Convex rewards pool\n require(\n IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n lpDeposited,\n true // Deposit with staking\n ),\n \"Depositing LP to Convex not successful\"\n );\n }\n\n /**\n * @notice Deposit the strategy's entire balance of WETH into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = weth.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(weth), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _weth Address of the Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to withdraw.\n */\n function withdraw(\n address _recipient,\n address _weth,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Invalid amount\");\n require(_weth == address(weth), \"Can only withdraw WETH\");\n\n emit Withdrawal(_weth, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(_amount);\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough WETH on balanced removal\n */\n uint256[2] memory _minWithdrawalAmounts = [uint256(0), uint256(0)];\n _minWithdrawalAmounts[ethCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OETH and any that was left in the strategy\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n\n // Transfer WETH to the recipient\n weth.deposit{ value: _amount }();\n require(\n weth.transfer(_recipient, _amount),\n \"Transfer of WETH not successful\"\n );\n }\n\n function calcTokenToBurn(uint256 _wethAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much WETH\n * we want we can determine how much of OETH we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolWETHBalance = curvePool.balances(ethCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolWETHBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_wethAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = cvxRewardStaker.balanceOf(address(this));\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[2] memory minWithdrawAmounts = [uint256(0), uint256(0)];\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(\n lpToken.balanceOf(address(this)),\n minWithdrawAmounts\n );\n\n // Burn all OETH\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Get the strategy contract's ether balance.\n // This includes all that was removed from the Curve pool and\n // any ether that was sitting in the strategy contract before the removal.\n uint256 ethBalance = address(this).balance;\n // Convert all the strategy contract's ether to WETH and transfer to the vault.\n weth.deposit{ value: ethBalance }();\n require(\n weth.transfer(vaultAddress, ethBalance),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethBalance);\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[2] memory amounts = [uint256(0), uint256(0)];\n amounts[oethCoinIndex] = _oTokens;\n\n // Convert OETH to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n\n // Deposit the Curve pool LP tokens to the Convex rewards pool\n require(\n IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n lpDeposited,\n true // Deposit with staking\n ),\n \"Failed to Deposit LP to Convex\"\n );\n\n emit Deposit(address(oeth), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough ETH.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove OTokens from the Curve pool\n uint256 oethToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n oethCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /**\n * @notice One-sided remove of ETH from the Curve pool, convert to WETH\n * and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for ETH.\n * @dev Curve pool LP tokens is used rather than WETH assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of ETH. Curve's `calc_token_amount` functioun does not include fees.\n * A 3rd party libary can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * caclulate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove ETH from the Curve pool\n uint256 ethAmount = _withdrawAndRemoveFromPool(_lpTokens, ethCoinIndex);\n\n // Convert ETH to WETH and transfer to the vault\n weth.deposit{ value: ethAmount }();\n require(\n weth.transfer(vaultAddress, ethAmount),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the Convex pool and\n * do a one-sided remove of ETH or OETH from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the Convex pool.\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = ETH, 1 = OETH.\n * @return coinsRemoved The amount of ETH or OETH removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Convex pool\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to ETH value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n // Apply slippage to ETH value\n uint256 minAmount = valueInEth.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Remove just the ETH from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV and CVX rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n cvxRewardStaker.getReward();\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _wethAmount) internal {\n // withdraw and unwrap with claim takes back the lpTokens\n // and also collects the rewards for deposit\n cvxRewardStaker.withdrawAndUnwrap(_wethAmount, true);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // Eth balance needed here for the balance check that happens from vault during depositing.\n balance = address(this).balance;\n uint256 lpTokens = cvxRewardStaker.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += (lpTokens * curvePool.get_virtual_price()) / 1e18;\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(weth);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @notice Accept unwrapped WETH\n */\n receive() external payable {}\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OETH (required for adding liquidity)\n // No approval is needed for ETH\n // slither-disable-next-line unused-return\n oeth.approve(platformAddress, type(uint256).max);\n\n // Approve Convex deposit contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Convex rewards pool\n // slither-disable-next-line unused-return\n lpToken.approve(cvxDepositorAddress, type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/ConvexGeneralizedMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { AbstractConvexMetaStrategy } from \"./AbstractConvexMetaStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract ConvexGeneralizedMetaStrategy is AbstractConvexMetaStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /* Take 3pool LP and deposit it to metapool. Take the LP from metapool\n * and deposit them to Convex.\n */\n function _lpDepositAll() internal override {\n IERC20 threePoolLp = IERC20(pTokenAddress);\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 threePoolLpBalance = threePoolLp.balanceOf(address(this));\n uint256 curve3PoolVirtualPrice = curvePool.get_virtual_price();\n uint256 threePoolLpDollarValue = threePoolLpBalance.mulTruncate(\n curve3PoolVirtualPrice\n );\n\n uint256[2] memory _amounts = [0, threePoolLpBalance];\n\n uint256 metapoolVirtualPrice = metapool.get_virtual_price();\n /**\n * First convert all the deposited tokens to dollar values,\n * then divide by virtual price to convert to metapool LP tokens\n * and apply the max slippage\n */\n uint256 minReceived = threePoolLpDollarValue\n .divPrecisely(metapoolVirtualPrice)\n .mulTruncate(uint256(1e18) - MAX_SLIPPAGE);\n\n uint256 metapoolLp = metapool.add_liquidity(_amounts, minReceived);\n\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n metapoolLp,\n true // Deposit with staking\n );\n\n require(success, \"Failed to deposit to Convex\");\n }\n\n /**\n * Withdraw the specified amount of tokens from the gauge. And use all the resulting tokens\n * to remove liquidity from metapool\n * @param num3CrvTokens Number of Convex 3pool LP tokens to withdraw from metapool\n */\n function _lpWithdraw(uint256 num3CrvTokens) internal override {\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n uint256 requiredMetapoolLpTokens = _calcCurveMetaTokenAmount(\n crvCoinIndex,\n num3CrvTokens\n );\n\n require(\n requiredMetapoolLpTokens <= gaugeTokens,\n string(\n bytes.concat(\n bytes(\"Attempting to withdraw \"),\n bytes(Strings.toString(requiredMetapoolLpTokens)),\n bytes(\", metapoolLP but only \"),\n bytes(Strings.toString(gaugeTokens)),\n bytes(\" available.\")\n )\n )\n );\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards for deposit\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n requiredMetapoolLpTokens,\n true\n );\n\n if (requiredMetapoolLpTokens > 0) {\n // slither-disable-next-line unused-return\n metapool.remove_liquidity_one_coin(\n requiredMetapoolLpTokens,\n int128(crvCoinIndex),\n num3CrvTokens\n );\n }\n }\n\n function _lpWithdrawAll() internal override {\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n gaugeTokens,\n true\n );\n\n if (gaugeTokens > 0) {\n uint256 burnDollarAmount = gaugeTokens.mulTruncate(\n metapool.get_virtual_price()\n );\n uint256 curve3PoolExpected = burnDollarAmount.divPrecisely(\n ICurvePool(platformAddress).get_virtual_price()\n );\n\n // Always withdraw all of the available metapool LP tokens (similar to how we always deposit all)\n // slither-disable-next-line unused-return\n metapool.remove_liquidity_one_coin(\n gaugeTokens,\n int128(crvCoinIndex),\n curve3PoolExpected -\n curve3PoolExpected.mulTruncate(maxWithdrawalSlippage)\n );\n }\n }\n}\n" + }, + "contracts/strategies/ConvexOUSDMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { AbstractConvexMetaStrategy } from \"./AbstractConvexMetaStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract ConvexOUSDMetaStrategy is AbstractConvexMetaStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /* Take 3pool LP and mint the corresponding amount of ousd. Deposit and stake that to\n * ousd Curve Metapool. Take the LP from metapool and deposit them to Convex.\n */\n function _lpDepositAll() internal override {\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 threePoolLpBalance = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n uint256 curve3PoolVirtualPrice = curvePool.get_virtual_price();\n uint256 threePoolLpDollarValue = threePoolLpBalance.mulTruncate(\n curve3PoolVirtualPrice\n );\n\n // safe to cast since min value is at least 0\n uint256 ousdToAdd = uint256(\n _max(\n 0,\n int256(\n metapool.balances(crvCoinIndex).mulTruncate(\n curve3PoolVirtualPrice\n )\n ) -\n int256(metapool.balances(mainCoinIndex)) +\n int256(threePoolLpDollarValue)\n )\n );\n\n /* Add so much OUSD so that the pool ends up being balanced. And at minimum\n * add twice as much OUSD as 3poolLP and at maximum at twice as\n * much OUSD.\n */\n ousdToAdd = Math.max(ousdToAdd, threePoolLpDollarValue);\n ousdToAdd = Math.min(ousdToAdd, threePoolLpDollarValue * 2);\n\n /* Mint OUSD with a strategy that attempts to contribute to stability of OUSD metapool. Try\n * to mint so much OUSD that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OUSD minted will always be at least equal or greater\n * to stablecoin(DAI, USDC, USDT) amount of 3CRVLP deployed. And never larger than twice the\n * stablecoin amount of 3CRVLP deployed even if it would have a further beneficial effect\n * on pool stability.\n */\n if (ousdToAdd > 0) {\n IVault(vaultAddress).mintForStrategy(ousdToAdd);\n }\n\n uint256[2] memory _amounts = [ousdToAdd, threePoolLpBalance];\n\n uint256 metapoolVirtualPrice = metapool.get_virtual_price();\n /**\n * First convert all the deposited tokens to dollar values,\n * then divide by virtual price to convert to metapool LP tokens\n * and apply the max slippage\n */\n uint256 minReceived = (ousdToAdd + threePoolLpDollarValue)\n .divPrecisely(metapoolVirtualPrice)\n .mulTruncate(uint256(1e18) - MAX_SLIPPAGE);\n\n uint256 metapoolLp = metapool.add_liquidity(_amounts, minReceived);\n\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n metapoolLp,\n true // Deposit with staking\n );\n\n require(success, \"Failed to deposit to Convex\");\n }\n\n /**\n * Withdraw the specified amount of tokens from the gauge. And use all the resulting tokens\n * to remove liquidity from metapool\n * @param num3CrvTokens Number of 3CRV tokens to withdraw from metapool\n */\n function _lpWithdraw(uint256 num3CrvTokens) internal override {\n ICurvePool curvePool = ICurvePool(platformAddress);\n /* The rate between coins in the metapool determines the rate at which metapool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much 3crvLp\n * we want we can determine how much of OUSD we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 crvPoolBalance = metapool.balances(crvCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * metapool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * metapoolLPToken.totalSupply()) / crvPoolBalance;\n // simplifying below to: `uint256 diff = (num3CrvTokens - 1) * k` causes loss of precision\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = crvPoolBalance * k -\n (crvPoolBalance - num3CrvTokens - 1) * k;\n uint256 lpToBurn = diff / 1e36;\n\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n require(\n lpToBurn <= gaugeTokens,\n string(\n bytes.concat(\n bytes(\"Attempting to withdraw \"),\n bytes(Strings.toString(lpToBurn)),\n bytes(\", metapoolLP but only \"),\n bytes(Strings.toString(gaugeTokens)),\n bytes(\" available.\")\n )\n )\n );\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards for deposit\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n lpToBurn,\n true\n );\n\n // calculate the min amount of OUSD expected for the specified amount of LP tokens\n uint256 minOUSDAmount = lpToBurn.mulTruncate(\n metapool.get_virtual_price()\n ) -\n num3CrvTokens.mulTruncate(curvePool.get_virtual_price()) -\n 1;\n\n // withdraw the liquidity from metapool\n uint256[2] memory _removedAmounts = metapool.remove_liquidity(\n lpToBurn,\n [minOUSDAmount, num3CrvTokens]\n );\n\n IVault(vaultAddress).burnForStrategy(_removedAmounts[mainCoinIndex]);\n }\n\n function _lpWithdrawAll() internal override {\n IERC20 metapoolErc20 = IERC20(address(metapool));\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n gaugeTokens,\n true\n );\n\n uint256[2] memory _minAmounts = [uint256(0), uint256(0)];\n uint256[2] memory _removedAmounts = metapool.remove_liquidity(\n metapoolErc20.balanceOf(address(this)),\n _minAmounts\n );\n\n IVault(vaultAddress).burnForStrategy(_removedAmounts[mainCoinIndex]);\n }\n}\n" + }, + "contracts/strategies/ConvexStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { IERC20, AbstractCurveStrategy, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\n/*\n * IMPORTANT(!) If ConvexStrategy needs to be re-deployed, it requires new\n * proxy contract with fresh storage slots. Changes in `AbstractCurveStrategy`\n * storage slots would break existing implementation.\n *\n * Remove this notice if ConvexStrategy is re-deployed\n */\ncontract ConvexStrategy is AbstractCurveStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n address internal cvxDepositorAddress;\n address internal cvxRewardStakerAddress;\n // slither-disable-next-line constable-states\n address private _deprecated_cvxRewardTokenAddress;\n uint256 internal cvxDepositorPTokenId;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * DAI, USDC, USDT\n * @param _pTokens Platform Token corresponding addresses\n * @param _cvxDepositorAddress Address of the Convex depositor(AKA booster) for this pool\n * @param _cvxRewardStakerAddress Address of the CVX rewards staker\n * @param _cvxDepositorPTokenId Pid of the pool referred to by Depositor and staker\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets,\n address[] calldata _pTokens,\n address _cvxDepositorAddress,\n address _cvxRewardStakerAddress,\n uint256 _cvxDepositorPTokenId\n ) external onlyGovernor initializer {\n require(_assets.length == 3, \"Must have exactly three assets\");\n // Should be set prior to abstract initialize call otherwise\n // abstractSetPToken calls will fail\n cvxDepositorAddress = _cvxDepositorAddress;\n cvxRewardStakerAddress = _cvxRewardStakerAddress;\n cvxDepositorPTokenId = _cvxDepositorPTokenId;\n pTokenAddress = _pTokens[0];\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n function _lpDepositAll() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // Deposit with staking\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n pToken.balanceOf(address(this)),\n true\n );\n require(success, \"Failed to deposit to Convex\");\n }\n\n function _lpWithdraw(uint256 numCrvTokens) internal override {\n uint256 gaugePTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n // Not enough in this contract or in the Gauge, can't proceed\n require(numCrvTokens > gaugePTokens, \"Insufficient 3CRV balance\");\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards to this\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n numCrvTokens,\n true\n );\n }\n\n function _lpWithdrawAll() internal override {\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards to this\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n IRewardStaking(cvxRewardStakerAddress).balanceOf(address(this)),\n true\n );\n }\n\n function _approveBase() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // 3Pool for LP token (required for removing liquidity)\n pToken.safeApprove(platformAddress, 0);\n pToken.safeApprove(platformAddress, type(uint256).max);\n // Gauge for LP token\n pToken.safeApprove(cvxDepositorAddress, 0);\n pToken.safeApprove(cvxDepositorAddress, type(uint256).max);\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 contractPTokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n uint256 gaugePTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n uint256 totalPTokens = contractPTokens + gaugePTokens;\n\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (totalPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = (totalPTokens * virtual_price) / 1e18;\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = value.scaleBy(assetDecimals, 18) / 3;\n }\n }\n\n /**\n * @dev Collect accumulated CRV and CVX and send to Vault.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n IRewardStaking(cvxRewardStakerAddress).getReward();\n _collectRewardTokens();\n }\n}\n" + }, + "contracts/strategies/CurveAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for a Curve pool using an OToken.\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { ICurveStableSwapNG } from \"../interfaces/ICurveStableSwapNG.sol\";\nimport { ICurveLiquidityGaugeV6 } from \"../interfaces/ICurveLiquidityGaugeV6.sol\";\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\nimport { ICurveMinter } from \"../interfaces/ICurveMinter.sol\";\n\ncontract CurveAMOStrategy is InitializableAbstractStrategy {\n using SafeCast for uint256;\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n /**\n * @dev a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n // New immutable variables that must be set in the constructor\n /**\n * @notice Address of the hard asset (weth, usdt, usdc).\n */\n IERC20 public immutable hardAsset;\n\n /**\n * @notice Address of the OTOKEN token contract.\n */\n IERC20 public immutable oToken;\n\n /**\n * @notice Address of the LP (Liquidity Provider) token contract.\n */\n IERC20 public immutable lpToken;\n\n /**\n * @notice Address of the Curve StableSwap NG pool contract.\n */\n ICurveStableSwapNG public immutable curvePool;\n\n /**\n * @notice Address of the Curve X-Chain Liquidity Gauge contract.\n */\n ICurveLiquidityGaugeV6 public immutable gauge;\n\n /**\n * @notice Address of the Curve Minter contract.\n */\n ICurveMinter public immutable minter;\n\n /**\n * @notice Index of the OTOKEN and hardAsset in the Curve pool.\n */\n uint128 public immutable otokenCoinIndex;\n uint128 public immutable hardAssetCoinIndex;\n\n /**\n * @notice Decimals of the OTOKEN and hardAsset.\n */\n uint8 public immutable decimalsOToken;\n uint8 public immutable decimalsHardAsset;\n\n /**\n * @notice Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n uint256 public maxSlippage;\n\n event MaxSlippageUpdated(uint256 newMaxSlippage);\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the hard asset and OToken balances in the Curve pool\n uint256[] memory balancesBefore = curvePool.get_balances();\n // diff = hardAsset balance - OTOKEN balance\n int256 diffBefore = (\n balancesBefore[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() - balancesBefore[otokenCoinIndex].toInt256();\n\n _;\n\n // Get the hard asset and OToken balances in the Curve pool\n uint256[] memory balancesAfter = curvePool.get_balances();\n // diff = hardAsset balance - OTOKEN balance\n int256 diffAfter = (\n balancesAfter[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() - balancesAfter[otokenCoinIndex].toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OTOKEN, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of hardAsset, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _otoken,\n address _hardAsset,\n address _gauge,\n address _minter\n ) InitializableAbstractStrategy(_baseConfig) {\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveStableSwapNG(_baseConfig.platformAddress);\n minter = ICurveMinter(_minter);\n\n oToken = IERC20(_otoken);\n hardAsset = IERC20(_hardAsset);\n gauge = ICurveLiquidityGaugeV6(_gauge);\n decimalsHardAsset = IBasicToken(_hardAsset).decimals();\n decimalsOToken = IBasicToken(_otoken).decimals();\n\n (hardAssetCoinIndex, otokenCoinIndex) = curvePool.coins(0) == _hardAsset\n ? (0, 1)\n : (1, 0);\n require(\n curvePool.coins(otokenCoinIndex) == _otoken &&\n curvePool.coins(hardAssetCoinIndex) == _hardAsset,\n \"Invalid coin indexes\"\n );\n require(gauge.lp_token() == address(curvePool), \"Invalid pool\");\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV\n * @param _maxSlippage Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV\n uint256 _maxSlippage\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n address[] memory _assets = new address[](1);\n _assets[0] = address(hardAsset);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n _setMaxSlippage(_maxSlippage);\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit hard asset into the Curve pool\n * @param _hardAsset Address of hard asset contract.\n * @param _amount Amount of hard asset to deposit.\n */\n function deposit(address _hardAsset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_hardAsset, _amount);\n }\n\n function _deposit(address _hardAsset, uint256 _hardAssetAmount) internal {\n require(_hardAssetAmount > 0, \"Must deposit something\");\n require(_hardAsset == address(hardAsset), \"Unsupported asset\");\n\n emit Deposit(_hardAsset, address(lpToken), _hardAssetAmount);\n uint256 scaledHardAssetAmount = _hardAssetAmount.scaleBy(\n decimalsOToken,\n decimalsHardAsset\n );\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 otokenToAdd = uint256(\n _max(\n 0,\n (\n balances[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() +\n scaledHardAssetAmount.toInt256() -\n balances[otokenCoinIndex].toInt256()\n )\n );\n\n /* Add so much OTOKEN so that the pool ends up being balanced. And at minimum\n * add as much OTOKEN as hard asset and at maximum twice as much OTOKEN.\n */\n otokenToAdd = Math.max(otokenToAdd, scaledHardAssetAmount);\n otokenToAdd = Math.min(otokenToAdd, scaledHardAssetAmount * 2);\n\n /* Mint OTOKEN with a strategy that attempts to contribute to stability of OTOKEN/hardAsset pool. Try\n * to mint so much OTOKEN that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OTOKEN minted will always be at least equal or greater\n * to hardAsset amount deployed. And never larger than twice the hardAsset amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(otokenToAdd);\n\n emit Deposit(address(oToken), address(lpToken), otokenToAdd);\n\n uint256[] memory _amounts = new uint256[](2);\n _amounts[hardAssetCoinIndex] = _hardAssetAmount;\n _amounts[otokenCoinIndex] = otokenToAdd;\n\n uint256 valueInLpTokens = (scaledHardAssetAmount + otokenToAdd)\n .divPrecisely(curvePool.get_virtual_price());\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Do the deposit to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(_amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool's LP tokens into the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n /**\n * @notice Deposit the strategy's entire balance of hardAsset into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = hardAsset.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(hardAsset), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw hardAsset and OTOKEN from the Curve pool, burn the OTOKEN,\n * transfer hardAsset to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _hardAsset Address of the hardAsset contract.\n * @param _amount Amount of hardAsset to withdraw.\n */\n function withdraw(\n address _recipient,\n address _hardAsset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(\n _hardAsset == address(hardAsset),\n \"Can only withdraw hard asset\"\n );\n\n emit Withdrawal(_hardAsset, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(\n _amount.scaleBy(decimalsOToken, decimalsHardAsset)\n );\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough hardAsset on balanced removal\n */\n uint256[] memory _minWithdrawalAmounts = new uint256[](2);\n _minWithdrawalAmounts[hardAssetCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OTOKEN and any that was left in the strategy\n uint256 otokenToBurn = oToken.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n\n // Transfer hardAsset to the recipient\n hardAsset.safeTransfer(_recipient, _amount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n function calcTokenToBurn(uint256 _hardAssetAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much hardAsset\n * we want we can determine how much of OTOKEN we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolHardAssetBalance = curvePool\n .balances(hardAssetCoinIndex)\n .scaleBy(decimalsOToken, decimalsHardAsset);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolHardAssetBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_hardAssetAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all hardAsset and OTOKEN from the Curve pool, burn the OTOKEN,\n * transfer hardAsset to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = gauge.balanceOf(address(this));\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[] memory minWithdrawAmounts = new uint256[](2);\n\n // Check balance of LP tokens in the strategy, if 0 return\n uint256 lpBalance = lpToken.balanceOf(address(this));\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n if (lpBalance > 0) {\n curvePool.remove_liquidity(lpBalance, minWithdrawAmounts);\n }\n\n // Burn all OTOKEN\n uint256 otokenToBurn = oToken.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n // Get the strategy contract's hardAsset balance.\n // This includes all that was removed from the Curve pool and\n // any hardAsset that was sitting in the strategy contract before the removal.\n uint256 hardAssetBalance = hardAsset.balanceOf(address(this));\n hardAsset.safeTransfer(vaultAddress, hardAssetBalance);\n\n if (hardAssetBalance > 0)\n emit Withdrawal(\n address(hardAsset),\n address(lpToken),\n hardAssetBalance\n );\n if (otokenToBurn > 0)\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many hardAsset.\n * The OToken/Asset, eg OTOKEN/hardAsset, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[] memory amounts = new uint256[](2);\n amounts[otokenCoinIndex] = _oTokens;\n\n // Convert OTOKEN to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool LP tokens to the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Deposit(address(oToken), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough hardAsset.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from gauge and remove OTokens from the Curve pool\n uint256 otokenToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n otokenCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n }\n\n /**\n * @notice One-sided remove of hardAsset from the Curve pool and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many hardAsset.\n * The OToken/Asset, eg OTOKEN/hardAsset, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for hardAsset.\n * @dev Curve pool LP tokens is used rather than hardAsset assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of hardAsset. Curve's `calc_token_amount` function does not include fees.\n * A 3rd party library can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * calculate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Curve gauge and remove hardAsset from the Curve pool\n uint256 hardAssetAmount = _withdrawAndRemoveFromPool(\n _lpTokens,\n hardAssetCoinIndex\n );\n\n // Transfer hardAsset to the vault\n hardAsset.safeTransfer(vaultAddress, hardAssetAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(hardAsset), address(lpToken), hardAssetAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the gauge and\n * do a one-sided remove of hardAsset or OTOKEN from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the gauge\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = hardAsset, 1 = OTOKEN.\n * @return coinsRemoved The amount of hardAsset or OTOKEN removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Curve gauge\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to hardAsset value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n\n if (coinIndex == hardAssetCoinIndex) {\n valueInEth = valueInEth.scaleBy(decimalsHardAsset, decimalsOToken);\n }\n\n // Apply slippage to hardAsset value\n uint256 minAmount = valueInEth.mulTruncate(uint256(1e18) - maxSlippage);\n\n // Remove just the hardAsset from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOtokenSupply = oToken.totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOtokenSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV rewards from inflation\n minter.mint(address(gauge));\n\n // Collect extra gauge rewards (outside of CRV)\n gauge.claim_rewards();\n\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _lpAmount) internal {\n require(\n gauge.balanceOf(address(this)) >= _lpAmount,\n \"Insufficient LP tokens\"\n );\n // withdraw lp tokens from the gauge without claiming rewards\n gauge.withdraw(_lpAmount);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(hardAsset), \"Unsupported asset\");\n\n // hardAsset balance needed here for the balance check that happens from vault during depositing.\n balance = hardAsset.balanceOf(address(this));\n uint256 lpTokens = gauge.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += ((lpTokens * curvePool.get_virtual_price()) / 1e18)\n .scaleBy(decimalsHardAsset, decimalsOToken);\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(hardAsset);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Sets the maximum slippage allowed for any swap/liquidity operation\n * @param _maxSlippage Maximum slippage allowed, 1e18 = 100%.\n */\n function setMaxSlippage(uint256 _maxSlippage) external onlyGovernor {\n _setMaxSlippage(_maxSlippage);\n }\n\n function _setMaxSlippage(uint256 _maxSlippage) internal {\n require(_maxSlippage <= 5e16, \"Slippage must be less than 100%\");\n maxSlippage = _maxSlippage;\n emit MaxSlippageUpdated(_maxSlippage);\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OTOKEN (required for adding liquidity)\n // slither-disable-next-line unused-return\n oToken.approve(platformAddress, type(uint256).max);\n\n // Approve Curve pool for hardAsset (required for adding liquidity)\n // slither-disable-next-line unused-return\n hardAsset.safeApprove(platformAddress, type(uint256).max);\n\n // Approve Curve gauge contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Curve gauge.\n // slither-disable-next-line unused-return\n lpToken.approve(address(gauge), type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/Generalized4626Strategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Generalized 4626 Strategy\n * @notice Investment strategy for ERC-4626 Tokenized Vaults\n * @author Origin Protocol Inc\n */\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\ncontract Generalized4626Strategy is InitializableAbstractStrategy {\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecate_shareToken;\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecate_assetToken;\n\n IERC20 public immutable shareToken;\n IERC20 public immutable assetToken;\n\n // For future use\n uint256[50] private __gap;\n\n /**\n * @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n * and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n * @param _assetToken Address of the ERC-4626 asset token. eg frxETH or DAI\n */\n constructor(BaseStrategyConfig memory _baseConfig, address _assetToken)\n InitializableAbstractStrategy(_baseConfig)\n {\n shareToken = IERC20(_baseConfig.platformAddress);\n assetToken = IERC20(_assetToken);\n }\n\n function initialize() external virtual onlyGovernor initializer {\n address[] memory rewardTokens = new address[](0);\n address[] memory assets = new address[](1);\n address[] memory pTokens = new address[](1);\n\n assets[0] = address(assetToken);\n pTokens[0] = address(platformAddress);\n\n InitializableAbstractStrategy._initialize(\n rewardTokens,\n assets,\n pTokens\n );\n }\n\n /**\n * @dev Deposit assets by converting them to shares\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit assets by converting them to shares\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal virtual {\n require(_amount > 0, \"Must deposit something\");\n require(_asset == address(assetToken), \"Unexpected asset address\");\n\n // slither-disable-next-line unused-return\n IERC4626(platformAddress).deposit(_amount, address(this));\n emit Deposit(_asset, address(shareToken), _amount);\n }\n\n /**\n * @dev Deposit the entire balance of assetToken to gain shareToken\n */\n function depositAll() external virtual override onlyVault nonReentrant {\n uint256 balance = assetToken.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(assetToken), balance);\n }\n }\n\n /**\n * @dev Withdraw asset by burning shares\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n require(_asset == address(assetToken), \"Unexpected asset address\");\n\n // slither-disable-next-line unused-return\n IERC4626(platformAddress).withdraw(_amount, _recipient, address(this));\n emit Withdrawal(_asset, address(shareToken), _amount);\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / share tokens\n */\n function _abstractSetPToken(address, address) internal virtual override {\n _approveBase();\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll()\n external\n virtual\n override\n onlyVaultOrGovernor\n nonReentrant\n {\n uint256 shareBalance = shareToken.balanceOf(address(this));\n uint256 assetAmount = 0;\n if (shareBalance > 0) {\n assetAmount = IERC4626(platformAddress).redeem(\n shareBalance,\n vaultAddress,\n address(this)\n );\n emit Withdrawal(\n address(assetToken),\n address(shareToken),\n assetAmount\n );\n }\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(_asset == address(assetToken), \"Unexpected asset address\");\n /* We are intentionally not counting the amount of assetToken parked on the\n * contract toward the checkBalance. The deposit and withdraw functions\n * should not result in assetToken being unused and owned by this strategy\n * contract.\n */\n IERC4626 platform = IERC4626(platformAddress);\n return platform.previewRedeem(platform.balanceOf(address(this)));\n }\n\n /**\n * @notice Governor approves the the ERC-4626 Tokenized Vault to spend the asset.\n */\n function safeApproveAllTokens() external override onlyGovernor {\n _approveBase();\n }\n\n function _approveBase() internal virtual {\n // Approval the asset to be transferred to the ERC-4626 Tokenized Vault.\n // Used by the ERC-4626 deposit() and mint() functions\n // slither-disable-next-line unused-return\n assetToken.approve(platformAddress, type(uint256).max);\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset)\n public\n view\n virtual\n override\n returns (bool)\n {\n return _asset == address(assetToken);\n }\n\n /**\n * @notice is not supported for this strategy as the asset and\n * ERC-4626 Tokenized Vault are set at deploy time.\n * @dev If the ERC-4626 Tokenized Vault needed to be changed, a new\n * contract would need to be deployed and the proxy updated.\n */\n function setPTokenAddress(address, address) external override onlyGovernor {\n revert(\"unsupported function\");\n }\n\n /**\n * @notice is not supported for this strategy as the asset and\n * ERC-4626 Tokenized Vault are set at deploy time.\n * @dev If the ERC-4626 Tokenized Vault needed to be changed, a new\n * contract would need to be deployed and the proxy updated.\n */\n function removePToken(uint256) external override onlyGovernor {\n revert(\"unsupported function\");\n }\n}\n" + }, + "contracts/strategies/Generalized4626USDTStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IUSDT {\n // Tether's approve does not return a bool like standard IERC20 contracts\n // slither-disable-next-line erc20-interface\n function approve(address _spender, uint256 _value) external;\n}\n\n/**\n * @title Generalized 4626 Strategy when asset is Tether USD (USDT)\n * @notice Investment strategy for ERC-4626 Tokenized Vaults for the USDT asset.\n * @author Origin Protocol Inc\n */\nimport { Generalized4626Strategy } from \"./Generalized4626Strategy.sol\";\n\ncontract Generalized4626USDTStrategy is Generalized4626Strategy {\n /**\n * @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n * and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n * @param _assetToken Address of the ERC-4626 asset token. eg frxETH or DAI\n */\n constructor(BaseStrategyConfig memory _baseConfig, address _assetToken)\n Generalized4626Strategy(_baseConfig, _assetToken)\n {}\n\n /// @dev Override for Tether as USDT does not return a bool on approve.\n /// Using assetToken.approve will fail as it expects a bool return value\n function _approveBase() internal virtual override {\n // Approval the asset to be transferred to the ERC-4626 Tokenized Vault.\n // Used by the ERC-4626 deposit() and mint() functions\n // slither-disable-next-line unused-return\n IUSDT(address(assetToken)).approve(platformAddress, type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/IAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface for Aaves Lending Pool\n * Documentation: https://developers.aave.com/#lendingpool\n */\ninterface IAaveLendingPool {\n /**\n * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.\n * - E.g. User deposits 100 USDC and gets in return 100 aUSDC\n * @param asset The address of the underlying asset to deposit\n * @param amount The amount to be deposited\n * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user\n * wants to receive them on his own wallet, or a different address if the beneficiary of aTokens\n * is a different wallet\n * @param referralCode Code used to register the integrator originating the operation, for potential rewards.\n * 0 if the action is executed directly by the user, without any middle-man\n **/\n function deposit(\n address asset,\n uint256 amount,\n address onBehalfOf,\n uint16 referralCode\n ) external;\n\n /**\n * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned\n * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC\n * @param asset The address of the underlying asset to withdraw\n * @param amount The underlying amount to be withdrawn\n * - Send the value type(uint256).max in order to withdraw the whole aToken balance\n * @param to Address that will receive the underlying, same as msg.sender if the user\n * wants to receive it on his own wallet, or a different address if the beneficiary is a\n * different wallet\n * @return The final amount withdrawn\n **/\n function withdraw(\n address asset,\n uint256 amount,\n address to\n ) external returns (uint256);\n}\n\n/**\n * @dev Interface for Aaves Lending Pool\n * Documentation: https://developers.aave.com/#lendingpooladdressesprovider\n */\ninterface ILendingPoolAddressesProvider {\n /**\n * @notice Get the current address for Aave LendingPool\n * @dev Lending pool is the core contract on which to call deposit\n */\n function getLendingPool() external view returns (address);\n}\n" + }, + "contracts/strategies/IAaveIncentivesController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IAaveIncentivesController {\n event RewardsAccrued(address indexed user, uint256 amount);\n\n event RewardsClaimed(\n address indexed user,\n address indexed to,\n uint256 amount\n );\n\n event RewardsClaimed(\n address indexed user,\n address indexed to,\n address indexed claimer,\n uint256 amount\n );\n\n event ClaimerSet(address indexed user, address indexed claimer);\n\n /*\n * @dev Returns the configuration of the distribution for a certain asset\n * @param asset The address of the reference asset of the distribution\n * @return The asset index, the emission per second and the last updated timestamp\n **/\n function getAssetData(address asset)\n external\n view\n returns (\n uint256,\n uint256,\n uint256\n );\n\n /**\n * @dev Whitelists an address to claim the rewards on behalf of another address\n * @param user The address of the user\n * @param claimer The address of the claimer\n */\n function setClaimer(address user, address claimer) external;\n\n /**\n * @dev Returns the whitelisted claimer for a certain address (0x0 if not set)\n * @param user The address of the user\n * @return The claimer address\n */\n function getClaimer(address user) external view returns (address);\n\n /**\n * @dev Configure assets for a certain rewards emission\n * @param assets The assets to incentivize\n * @param emissionsPerSecond The emission for each asset\n */\n function configureAssets(\n address[] calldata assets,\n uint256[] calldata emissionsPerSecond\n ) external;\n\n /**\n * @dev Called by the corresponding asset on any update that affects the rewards distribution\n * @param asset The address of the user\n * @param userBalance The balance of the user of the asset in the lending pool\n * @param totalSupply The total supply of the asset in the lending pool\n **/\n function handleAction(\n address asset,\n uint256 userBalance,\n uint256 totalSupply\n ) external;\n\n /**\n * @dev Returns the total of rewards of an user, already accrued + not yet accrued\n * @param user The address of the user\n * @return The rewards\n **/\n function getRewardsBalance(address[] calldata assets, address user)\n external\n view\n returns (uint256);\n\n /**\n * @dev Claims reward for an user, on all the assets of the lending pool,\n * accumulating the pending rewards\n * @param amount Amount of rewards to claim\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewards(\n address[] calldata assets,\n uint256 amount,\n address to\n ) external returns (uint256);\n\n /**\n * @dev Claims reward for an user on behalf, on all the assets of the\n * lending pool, accumulating the pending rewards. The caller must\n * be whitelisted via \"allowClaimOnBehalf\" function by the RewardsAdmin role manager\n * @param amount Amount of rewards to claim\n * @param user Address to check and claim rewards\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewardsOnBehalf(\n address[] calldata assets,\n uint256 amount,\n address user,\n address to\n ) external returns (uint256);\n\n /**\n * @dev returns the unclaimed rewards of the user\n * @param user the address of the user\n * @return the unclaimed user rewards\n */\n function getUserUnclaimedRewards(address user)\n external\n view\n returns (uint256);\n\n /**\n * @dev returns the unclaimed rewards of the user\n * @param user the address of the user\n * @param asset The asset to incentivize\n * @return the user index for the asset\n */\n function getUserAssetData(address user, address asset)\n external\n view\n returns (uint256);\n\n /**\n * @dev for backward compatibility with previous implementation of the Incentives controller\n */\n function REWARD_TOKEN() external view returns (address);\n\n /**\n * @dev for backward compatibility with previous implementation of the Incentives controller\n */\n function PRECISION() external view returns (uint8);\n\n /**\n * @dev Gets the distribution end timestamp of the emissions\n */\n function DISTRIBUTION_END() external view returns (uint256);\n}\n" + }, + "contracts/strategies/IAaveStakeToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IAaveStakedToken {\n function COOLDOWN_SECONDS() external returns (uint256);\n\n function UNSTAKE_WINDOW() external returns (uint256);\n\n function balanceOf(address addr) external returns (uint256);\n\n function redeem(address to, uint256 amount) external;\n\n function stakersCooldowns(address addr) external returns (uint256);\n\n function cooldown() external;\n}\n" + }, + "contracts/strategies/ICompound.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @dev Compound C Token interface\n * Documentation: https://compound.finance/developers/ctokens\n */\ninterface ICERC20 {\n /**\n * @notice The mint function transfers an asset into the protocol, which begins accumulating\n * interest based on the current Supply Rate for the asset. The user receives a quantity of\n * cTokens equal to the underlying tokens supplied, divided by the current Exchange Rate.\n * @param mintAmount The amount of the asset to be supplied, in units of the underlying asset.\n * @return 0 on success, otherwise an Error codes\n */\n function mint(uint256 mintAmount) external returns (uint256);\n\n /**\n * @notice Sender redeems cTokens in exchange for the underlying asset\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param redeemTokens The number of cTokens to redeem into underlying\n * @return uint 0=success, otherwise an error code.\n */\n function redeem(uint256 redeemTokens) external returns (uint256);\n\n /**\n * @notice The redeem underlying function converts cTokens into a specified quantity of the underlying\n * asset, and returns them to the user. The amount of cTokens redeemed is equal to the quantity of\n * underlying tokens received, divided by the current Exchange Rate. The amount redeemed must be less\n * than the user's Account Liquidity and the market's available liquidity.\n * @param redeemAmount The amount of underlying to be redeemed.\n * @return 0 on success, otherwise an error code.\n */\n function redeemUnderlying(uint256 redeemAmount) external returns (uint256);\n\n /**\n * @notice The user's underlying balance, representing their assets in the protocol, is equal to\n * the user's cToken balance multiplied by the Exchange Rate.\n * @param owner The account to get the underlying balance of.\n * @return The amount of underlying currently owned by the account.\n */\n function balanceOfUnderlying(address owner) external returns (uint256);\n\n /**\n * @notice Calculates the exchange rate from the underlying to the CToken\n * @dev This function does not accrue interest before calculating the exchange rate\n * @return Calculated exchange rate scaled by 1e18\n */\n function exchangeRateStored() external view returns (uint256);\n\n /**\n * @notice Get the token balance of the `owner`\n * @param owner The address of the account to query\n * @return The number of tokens owned by `owner`\n */\n function balanceOf(address owner) external view returns (uint256);\n\n /**\n * @notice Get the supply rate per block for supplying the token to Compound.\n */\n function supplyRatePerBlock() external view returns (uint256);\n\n /**\n * @notice Address of the Compound Comptroller.\n */\n function comptroller() external view returns (address);\n}\n" + }, + "contracts/strategies/IConvexDeposits.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IConvexDeposits {\n function deposit(\n uint256 _pid,\n uint256 _amount,\n bool _stake\n ) external returns (bool);\n\n function deposit(\n uint256 _amount,\n bool _lock,\n address _stakeAddress\n ) external;\n}\n" + }, + "contracts/strategies/ICurveETHPoolV1.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurveETHPoolV1 {\n event AddLiquidity(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event ApplyNewFee(uint256 fee);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event CommitNewFee(uint256 new_fee);\n event RampA(\n uint256 old_A,\n uint256 new_A,\n uint256 initial_time,\n uint256 future_time\n );\n event RemoveLiquidity(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 token_supply\n );\n event RemoveLiquidityImbalance(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event RemoveLiquidityOne(\n address indexed provider,\n uint256 token_amount,\n uint256 coin_amount,\n uint256 token_supply\n );\n event StopRampA(uint256 A, uint256 t);\n event TokenExchange(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event Transfer(\n address indexed sender,\n address indexed receiver,\n uint256 value\n );\n\n function A() external view returns (uint256);\n\n function A_precise() external view returns (uint256);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_liquidity(uint256[2] memory _amounts, uint256 _min_mint_amount)\n external\n payable\n returns (uint256);\n\n function add_liquidity(\n uint256[2] memory _amounts,\n uint256 _min_mint_amount,\n address _receiver\n ) external payable returns (uint256);\n\n function admin_action_deadline() external view returns (uint256);\n\n function admin_balances(uint256 i) external view returns (uint256);\n\n function admin_fee() external view returns (uint256);\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function apply_new_fee() external;\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function balances(uint256 arg0) external view returns (uint256);\n\n function calc_token_amount(uint256[2] memory _amounts, bool _is_deposit)\n external\n view\n returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _burn_amount, int128 i)\n external\n view\n returns (uint256);\n\n function coins(uint256 arg0) external view returns (address);\n\n function commit_new_fee(uint256 _new_fee) external;\n\n function decimals() external view returns (uint256);\n\n function ema_price() external view returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external payable returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external payable returns (uint256);\n\n function fee() external view returns (uint256);\n\n function future_A() external view returns (uint256);\n\n function future_A_time() external view returns (uint256);\n\n function future_fee() external view returns (uint256);\n\n function get_balances() external view returns (uint256[2] memory);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n\n function get_p() external view returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function initial_A() external view returns (uint256);\n\n function initial_A_time() external view returns (uint256);\n\n function initialize(\n string memory _name,\n string memory _symbol,\n address[4] memory _coins,\n uint256[4] memory _rate_multipliers,\n uint256 _A,\n uint256 _fee\n ) external;\n\n function last_price() external view returns (uint256);\n\n function ma_exp_time() external view returns (uint256);\n\n function ma_last_time() external view returns (uint256);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function price_oracle() external view returns (uint256);\n\n function ramp_A(uint256 _future_A, uint256 _future_time) external;\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[2] memory _min_amounts\n ) external returns (uint256[2] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[2] memory _min_amounts,\n address _receiver\n ) external returns (uint256[2] memory);\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burn_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burn_amount,\n address _receiver\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received,\n address _receiver\n ) external returns (uint256);\n\n function set_ma_exp_time(uint256 _ma_exp_time) external;\n\n function stop_ramp_A() external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw_admin_fees() external;\n}\n" + }, + "contracts/strategies/ICurveMetaPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface ICurveMetaPool {\n function add_liquidity(uint256[2] calldata amounts, uint256 min_mint_amount)\n external\n returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function remove_liquidity(uint256 _amount, uint256[2] calldata min_amounts)\n external\n returns (uint256[2] calldata);\n\n function remove_liquidity_one_coin(\n uint256 _token_amount,\n int128 i,\n uint256 min_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[2] calldata amounts,\n uint256 max_burn_amount\n ) external returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _token_amount, int128 i)\n external\n view\n returns (uint256);\n\n function balances(uint256 i) external view returns (uint256);\n\n function calc_token_amount(uint256[2] calldata amounts, bool deposit)\n external\n view\n returns (uint256);\n\n function base_pool() external view returns (address);\n\n function fee() external view returns (uint256);\n\n function coins(uint256 i) external view returns (address);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 dx,\n uint256 min_dy\n ) external returns (uint256);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n}\n" + }, + "contracts/strategies/ICurvePool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurvePool {\n function get_virtual_price() external view returns (uint256);\n\n function add_liquidity(uint256[3] calldata _amounts, uint256 _min) external;\n\n function balances(uint256) external view returns (uint256);\n\n function calc_token_amount(uint256[3] calldata _amounts, bool _deposit)\n external\n returns (uint256);\n\n function fee() external view returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _amount,\n int128 _index,\n uint256 _minAmount\n ) external;\n\n function remove_liquidity(\n uint256 _amount,\n uint256[3] calldata _minWithdrawAmounts\n ) external;\n\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n external\n view\n returns (uint256);\n\n function exchange(\n uint256 i,\n uint256 j,\n uint256 dx,\n uint256 min_dy\n ) external returns (uint256);\n\n function coins(uint256 _index) external view returns (address);\n\n function remove_liquidity_imbalance(\n uint256[3] calldata _amounts,\n uint256 maxBurnAmount\n ) external;\n}\n" + }, + "contracts/strategies/IRewardStaking.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IRewardStaking {\n function stakeFor(address, uint256) external;\n\n function stake(uint256) external;\n\n function withdraw(uint256 amount, bool claim) external;\n\n function withdrawAndUnwrap(uint256 amount, bool claim) external;\n\n function earned(address account) external view returns (uint256);\n\n function getReward() external;\n\n function getReward(address _account, bool _claimExtras) external;\n\n function extraRewardsLength() external returns (uint256);\n\n function extraRewards(uint256 _pid) external returns (address);\n\n function rewardToken() external returns (address);\n\n function balanceOf(address account) external view returns (uint256);\n}\n" + }, + "contracts/strategies/MorphoAaveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Morpho Aave Strategy\n * @notice Investment strategy for investing stablecoins via Morpho (Aave)\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { IMorpho } from \"../interfaces/morpho/IMorpho.sol\";\nimport { ILens } from \"../interfaces/morpho/ILens.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MorphoAaveStrategy is InitializableAbstractStrategy {\n address public constant MORPHO = 0x777777c9898D384F785Ee44Acfe945efDFf5f3E0;\n address public constant LENS = 0x507fA343d0A90786d86C7cd885f5C49263A91FF4;\n\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @dev Initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Approve the spending of all assets by main Morpho contract,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address asset = assetsMapped[i];\n\n // Safe approval\n IERC20(asset).safeApprove(MORPHO, 0);\n IERC20(asset).safeApprove(MORPHO, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset\n * We need to approve and allow Morpho to move them\n * @param _asset Address of the asset to approve\n * @param _pToken The pToken for the approval\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n IERC20(_asset).safeApprove(MORPHO, 0);\n IERC20(_asset).safeApprove(MORPHO, type(uint256).max);\n }\n\n /**\n * @dev Collect accumulated rewards and send them to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Morpho Aave-v2 doesn't distribute reward tokens\n // solhint-disable-next-line max-line-length\n // Ref: https://developers.morpho.xyz/interact-with-morpho/get-started/interact-with-morpho/claim-rewards#morpho-aave-v2\n }\n\n /**\n * @dev Get the amount of rewards pending to be collected from the protocol\n */\n function getPendingRewards() external view returns (uint256 balance) {\n // Morpho Aave-v2 doesn't distribute reward tokens\n // solhint-disable-next-line max-line-length\n // Ref: https://developers.morpho.xyz/interact-with-morpho/get-started/interact-with-morpho/claim-rewards#morpho-aave-v2\n return 0;\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n\n address pToken = address(_getPTokenFor(_asset));\n\n IMorpho(MORPHO).supply(\n pToken,\n address(this), // the address of the user you want to supply on behalf of\n _amount\n );\n emit Deposit(_asset, pToken, _amount);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Morpho\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Morpho\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n address pToken = address(_getPTokenFor(_asset));\n\n IMorpho(MORPHO).withdraw(pToken, _amount);\n emit Withdrawal(_asset, pToken, _amount);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = _checkBalance(assetsMapped[i]);\n if (balance > 0) {\n _withdraw(vaultAddress, assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Return total value of an asset held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n return _checkBalance(_asset);\n }\n\n function _checkBalance(address _asset)\n internal\n view\n returns (uint256 balance)\n {\n address pToken = address(_getPTokenFor(_asset));\n\n // Total value represented by decimal position of underlying token\n (, , balance) = ILens(LENS).getCurrentSupplyBalanceInOf(\n pToken,\n address(this)\n );\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Get the pToken wrapped in the IERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return pToken Corresponding pToken to this asset\n */\n function _getPTokenFor(address _asset) internal view returns (IERC20) {\n address pToken = assetToPToken[_asset];\n require(pToken != address(0), \"pToken does not exist\");\n return IERC20(pToken);\n }\n}\n" + }, + "contracts/strategies/MorphoCompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Morpho Compound Strategy\n * @notice Investment strategy for investing stablecoins via Morpho (Compound)\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, AbstractCompoundStrategy, InitializableAbstractStrategy } from \"./AbstractCompoundStrategy.sol\";\nimport { IMorpho } from \"../interfaces/morpho/IMorpho.sol\";\nimport { ILens } from \"../interfaces/morpho/ILens.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MorphoCompoundStrategy is AbstractCompoundStrategy {\n address public constant MORPHO = 0x8888882f8f843896699869179fB6E4f7e3B58888;\n address public constant LENS = 0x930f1b46e1D081Ec1524efD95752bE3eCe51EF67;\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @dev Initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Approve the spending of all assets by main Morpho contract,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address asset = assetsMapped[i];\n\n // Safe approval\n IERC20(asset).safeApprove(MORPHO, 0);\n IERC20(asset).safeApprove(MORPHO, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset\n * We need to approve and allow Morpho to move them\n * @param _asset Address of the asset to approve\n * @param _pToken The pToken for the approval\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n IERC20(_asset).safeApprove(MORPHO, 0);\n IERC20(_asset).safeApprove(MORPHO, type(uint256).max);\n }\n\n /**\n * @dev Collect accumulated rewards and send them to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n /**\n * Gas considerations. We could query Morpho LENS's `getUserUnclaimedRewards` for each\n * cToken separately and only claimRewards where it is economically feasible. Each call\n * (out of 3) costs ~60k gas extra.\n *\n * Each extra cToken in the `poolTokens` of `claimRewards` function makes that call\n * 89-120k more expensive gas wise.\n *\n * With Lens query in case where:\n * - there is only 1 reward token to collect. Net gas usage in best case would be\n * 3*60 - 2*120 = -60k -> saving 60k gas\n * - there are 2 reward tokens to collect. Net gas usage in best case would be\n * 3*60 - 120 = 60k -> more expensive for 60k gas\n * - there are 3 reward tokens to collect. Net gas usage in best case would be\n * 3*60 = 180k -> more expensive for 180k gas\n *\n * For the above reasoning such \"optimization\" is not implemented\n */\n\n address[] memory poolTokens = new address[](assetsMapped.length);\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n poolTokens[i] = assetToPToken[assetsMapped[i]];\n }\n\n // slither-disable-next-line unused-return\n IMorpho(MORPHO).claimRewards(\n poolTokens, // The addresses of the underlying protocol's pools to claim rewards from\n false // Whether to trade the accrued rewards for MORPHO token, with a premium\n );\n\n // Transfer COMP to Harvester\n IERC20 rewardToken = IERC20(rewardTokenAddresses[0]);\n uint256 balance = rewardToken.balanceOf(address(this));\n emit RewardTokenCollected(\n harvesterAddress,\n rewardTokenAddresses[0],\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n\n /**\n * @dev Get the amount of rewards pending to be collected from the protocol\n */\n function getPendingRewards() external view returns (uint256 balance) {\n address[] memory poolTokens = new address[](assetsMapped.length);\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n poolTokens[i] = assetToPToken[assetsMapped[i]];\n }\n\n return ILens(LENS).getUserUnclaimedRewards(poolTokens, address(this));\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n\n IMorpho(MORPHO).supply(\n address(_getCTokenFor(_asset)),\n address(this), // the address of the user you want to supply on behalf of\n _amount\n );\n emit Deposit(_asset, address(_getCTokenFor(_asset)), _amount);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Morpho\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Morpho\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n address pToken = assetToPToken[_asset];\n\n IMorpho(MORPHO).withdraw(pToken, _amount);\n emit Withdrawal(_asset, address(_getCTokenFor(_asset)), _amount);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = _checkBalance(assetsMapped[i]);\n if (balance > 0) {\n _withdraw(vaultAddress, assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Return total value of an asset held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n return _checkBalance(_asset);\n }\n\n function _checkBalance(address _asset)\n internal\n view\n returns (uint256 balance)\n {\n address pToken = assetToPToken[_asset];\n\n // Total value represented by decimal position of underlying token\n (, , balance) = ILens(LENS).getCurrentSupplyBalanceInOf(\n pToken,\n address(this)\n );\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconOracle Address of the Beacon Oracle contract that maps block numbers to slots\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconOracle,\n address _beaconProofs\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconOracle,\n _beaconProofs\n )\n {\n SSV_TOKEN = _ssvToken;\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract.\n /// @param _rewardTokenAddresses Address of reward token for platform\n /// @param _assets Addresses of initial supported assets\n /// @param _pTokens Platform Token corresponding addresses\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused for validator consolidation.\n /// @param _asset Address of asset to deposit. Has to be WETH.\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused for validator consolidation.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// Will revert if the strategy is paused for validator consolidation.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant whenNotPaused {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _asset, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertWethToEth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(_asset, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n /// Will revert if the strategy is paused for validator consolidation.\n function withdrawAll()\n external\n override\n onlyVaultOrGovernor\n nonReentrant\n whenNotPaused\n {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, WETH, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Returns the last verified balance of validator deposits, validator balance,\n /// WETH and ETH in the strategy contract.\n /// @param _asset Address of weth asset\n /// @return balance Total value of (W)ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by strategy.\n /// @param _asset The address of the asset token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for deposits\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instread of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract,\n /// but so does withdrawals from validators. It's too complex to separate the two\n /// so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IConsolidationSource } from \"../../interfaces/IConsolidation.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\nimport { IBeaconOracle } from \"../../interfaces/IBeaconOracle.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit, remove and consolidate validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable, Pausable {\n using SafeERC20 for IERC20;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Oracle contract that maps block numbers to slots\n address public immutable BEACON_ORACLE;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// Deposit data for new compounding validators.\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountWei Amount of ETH in wei that has been deposited to the beacon chain deposit contract\n /// @param blockNumber Block number when the deposit was made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either PENDING or PROVEN\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 blockNumber;\n uint32 depositIndex;\n DepositStatus status;\n }\n /// @notice Mapping of the root of a deposit (depositDataRoot) to its data\n mapping(bytes32 => DepositData) public deposits;\n /// @notice List of deposit roots that are still to be verified as processed on the beacon chain\n bytes32[] public depositsRoots;\n\n // Validator data\n\n struct ValidatorData {\n bytes32 pubKeyHash; // Hash of the validator's public key using the Beacon Chain's format\n uint64 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes and indexes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n ValidatorData[] public verifiedValidators;\n /// @notice State of the new compounding validators with a 0x02 withdrawal credential prefix.\n /// Uses the Beacon chain hashing for BLSPubkey which is\n /// sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => VALIDATOR_STATE) public validatorState;\n\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n uint64 timestamp;\n uint128 ethBalance;\n }\n // TODO is it more efficient to use the block root rather than hashing it?\n /// @notice Mapping of the block root to the balances at that slot\n mapping(bytes32 => Balances) public snappedBalances;\n uint64 public lastSnapTimestamp;\n uint128 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n bytes32 public consolidationLastPubKeyHash;\n address public consolidationSourceStrategy;\n mapping(address => bool) public consolidationSourceStrategies;\n\n // For future use\n uint256[50] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITED, // The validator has been verified to have a zero balance\n REMOVED // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified and is ready to be staked\n }\n\n event RegistratorChanged(address indexed newAddress);\n event SourceStrategyAdded(address indexed strategy);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n bytes32 indexed depositDataRoot,\n bytes pubKey,\n uint256 amountWei\n );\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint64 indexed validatorIndex\n );\n event DepositVerified(bytes32 indexed depositDataRoot, uint256 amountWei);\n event BalancesSnapped(\n uint256 indexed timestamp,\n bytes32 indexed blockRoot,\n uint256 ethBalance\n );\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 wethBalance,\n uint256 ethBalance\n );\n event ConsolidationRequested(\n bytes32 indexed lastSourcePubKeyHash,\n bytes32 indexed targetPubKeyHash,\n address indexed sourceStrategy\n );\n event ConsolidationVerified(\n bytes32 indexed lastSourcePubKeyHash,\n uint64 indexed lastValidatorIndex,\n uint256 consolidationCount\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconOracle Address of the Beacon Oracle contract that maps block numbers to slots\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconOracle,\n address _beaconProofs\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_ORACLE = _beaconOracle;\n BEACON_PROOFS = _beaconProofs;\n }\n\n /***************************************\n Admin Functions\n ****************************************/\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Adds support for a legacy staking strategy that can be used for consolidation.\n function addSourceStrategy(address _strategy) external onlyGovernor {\n consolidationSourceStrategies[_strategy] = true;\n\n emit SourceStrategyAdded(_strategy);\n }\n\n /***************************************`\n Validator Management\n ****************************************/\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validatorState[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validator validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validator,\n uint64 depositAmountGwei\n ) external onlyRegistrator whenNotPaused {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertEthToWeth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validator.pubkey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == VALIDATOR_STATE.REGISTERED ||\n currentState == VALIDATOR_STATE.VERIFIED),\n \"Not registered or verified\"\n );\n require(\n currentState == VALIDATOR_STATE.VERIFIED ||\n depositAmountWei == 1 ether,\n \"First deposit not 1 ETH\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validator.pubkey,\n withdrawalCredentials,\n validator.signature,\n validator.depositDataRoot\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == VALIDATOR_STATE.REGISTERED) {\n validatorState[pubKeyHash] = VALIDATOR_STATE.STAKED;\n }\n // Store the deposit data for verifyDeposit and verifyBalances\n deposits[validator.depositDataRoot] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n blockNumber: SafeCast.toUint64(block.number),\n depositIndex: SafeCast.toUint32(depositsRoots.length),\n status: DepositStatus.PENDING\n });\n depositsRoots.push(validator.depositDataRoot);\n\n emit ETHStaked(\n pubKeyHash,\n validator.depositDataRoot,\n validator.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// If the remaining balance is < 32 ETH then the validator will be exited.\n /// That can result in the ETH sent to the strategy being more than the requested amount.\n /// The staked ETH will eventually be withdrawn to this staking strategy.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param amount The amount of ETH to be withdrawn from the validator in Gwei\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amount)\n external\n onlyRegistrator\n whenNotPaused\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n\n PartialWithdrawal.request(publicKey, amount);\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n emit ValidatorWithdraw(pubKeyHash, amount);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove a sweeping validator from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == VALIDATOR_STATE.EXITED ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exited\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validatorState[pubKeyHash] = VALIDATOR_STATE.REMOVED;\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /// @notice Receives requests from supported legacy strategies to consolidate sweeping validators to\n /// a new compounding validator on this new strategy.\n /// @param lastSourcePubKeyHash The last source validator to be consolidated hashed using the Beacon Chain's format.\n /// @param targetPubKeyHash The target validator's public key hash using the Beacon Chain's format.\n function requestConsolidation(\n bytes32 lastSourcePubKeyHash,\n bytes32 targetPubKeyHash\n ) external whenNotPaused {\n require(\n consolidationSourceStrategies[msg.sender],\n \"Not a source strategy\"\n );\n\n // The target validator must be a compounding validator that has been verified\n require(\n validatorState[targetPubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Target validator not verified\"\n );\n\n // Ensure balances can not be verified until after the consolidation has been verified.\n lastSnapTimestamp = 0;\n\n // Store consolidation state\n consolidationLastPubKeyHash = lastSourcePubKeyHash;\n consolidationSourceStrategy = msg.sender;\n\n // Pause the strategy while the consolidation is in progress\n _pause();\n\n emit ConsolidationRequested(\n lastSourcePubKeyHash,\n targetPubKeyHash,\n msg.sender\n );\n }\n\n /***************************************\n SSV Management\n ****************************************/\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n Beacon Chain Proofs\n ****************************************/\n\n /// @notice Verifies a validator's index to its public key.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param validatorPubKeyProof The merkle proof that the validator's index matches its public key\n /// on the beacon chain.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.STAKED,\n \"Validator not staked\"\n );\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key\n IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex\n );\n\n // TODO verify the validator's withdrawal credential points to this strategy\n\n // Store the validator state as verified\n validatorState[pubKeyHash] = VALIDATOR_STATE.VERIFIED;\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(\n ValidatorData({ pubKeyHash: pubKeyHash, index: validatorIndex })\n );\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n /// @param depositDataRoot The root of the deposit data that was stored when\n /// the deposit was made on the execution layer.\n /// @param depositBlockNumber A block number that is on or after the block the deposit\n /// was made on the execution layer.\n /// @param processedSlot Any slot on or after the deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// @param firstPendingDepositSlot The slot of the first pending deposit in the beacon chain\n /// @param firstPendingDepositSlotProof The merkle proof that the slot of the first pending deposit\n /// matches the beacon chain.\n /// BeaconBlock.BeaconBlockBody.deposits[0].slot\n function verifyDeposit(\n bytes32 depositDataRoot,\n uint64 depositBlockNumber,\n uint64 processedSlot,\n uint64 firstPendingDepositSlot,\n bytes calldata firstPendingDepositSlotProof\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositDataRoot];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n validatorState[deposit.pubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n // The block number mapped to a slot needs to be the same block or after the deposit\n // was created on the execution layer.\n require(\n deposit.blockNumber <= depositBlockNumber,\n \"Deposit block before deposit\"\n );\n\n // Get the slot that is on or after the deposit was made on the execution layer.\n uint64 depositSlot = IBeaconOracle(BEACON_ORACLE).blockToSlot(\n depositBlockNumber\n );\n\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the first pending deposit's slot is 0 then there are no pending deposits so\n // our deposit must have been processed on the beacon chain.\n require(\n depositSlot < firstPendingDepositSlot ||\n firstPendingDepositSlot == 0,\n \"Deposit not processed\"\n );\n\n // The slot of the execution layer deposit must be before\n // the slot that the deposit was processed on the beacon chain.\n require(depositSlot < processedSlot, \"Slot not after deposit\");\n\n // Get the beacon block root for the slot that is on or after the deposit was processed on the beacon chain.\n bytes32 blockRoot = IBeaconOracle(BEACON_ORACLE).slotToRoot(\n processedSlot\n );\n\n // Verify the slot of the first pending deposit matches the beacon chain\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n blockRoot,\n firstPendingDepositSlot,\n firstPendingDepositSlotProof\n );\n\n // After verifying the proof, update the contract storage\n deposits[depositDataRoot].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n depositsRoots[deposit.depositIndex] = depositsRoots[\n depositsRoots.length - 1\n ];\n // Delete the last deposit from the list\n depositsRoots.pop();\n\n emit DepositVerified(\n depositDataRoot,\n uint256(deposit.amountGwei) * 1 gwei\n );\n }\n\n // TODO what if the last validator was exited rather than consolidated?\n function verifyConsolidation(\n uint64 parentBlockTimestamp,\n uint64 lastValidatorIndex,\n bytes calldata validatorPubKeyProof,\n bytes32 balancesLeaf,\n bytes calldata validatorBalanceProof\n ) external onlyRegistrator {\n bytes32 consolidationLastPubKeyHashMem = consolidationLastPubKeyHash;\n require(\n consolidationLastPubKeyHashMem != bytes32(0),\n \"No consolidations\"\n );\n\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(parentBlockTimestamp);\n // Verify the validator index has the same public key as the last source validator\n IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n blockRoot,\n consolidationLastPubKeyHashMem,\n validatorPubKeyProof,\n lastValidatorIndex\n );\n\n // Verify the balance of the last validator in the consolidation batch\n // is zero. If its not then the consolidation has not been completed.\n // This proof is to the beacon block root, not the balances container root.\n uint256 validatorBalance = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n blockRoot,\n balancesLeaf,\n validatorBalanceProof,\n lastValidatorIndex,\n IBeaconProofs.BalanceProofLevel.BeaconBlock\n );\n require(validatorBalance == 0, \"Last validator balance not zero\");\n\n // Call the old sweeping strategy to confirm the consolidation has been completed.\n // This will decrease the balance of the source strategy by 32 ETH for each validator being consolidated.\n uint256 consolidationCount = IConsolidationSource(\n consolidationSourceStrategy\n ).confirmConsolidation();\n\n // Increase the balance of this strategy by 32 ETH for each validator being consolidated.\n // This nets out the decrease in the source strategy's balance.\n lastVerifiedEthBalance += SafeCast.toUint128(\n consolidationCount * 32 ether\n );\n\n // Reset the stored consolidation state\n consolidationLastPubKeyHash = bytes32(0);\n consolidationSourceStrategy = address(0);\n\n emit ConsolidationVerified(\n consolidationLastPubKeyHashMem,\n lastValidatorIndex,\n consolidationCount\n );\n\n // Unpause now the balance of the target validator has been verified\n _unpause();\n\n // Take a snap of the balances so the actual balances of the new validator balances can be verified\n _snapBalances();\n }\n\n /// @notice Stores the current ETH balance at the current block.\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n /// Can not be called while a consolidation is in progress.\n function snapBalances() public whenNotPaused {\n _snapBalances();\n }\n\n function _snapBalances() internal {\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(\n SafeCast.toUint64(block.timestamp)\n );\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the balances in the mapping\n snappedBalances[blockRoot] = Balances({\n timestamp: SafeCast.toUint64(block.timestamp),\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n // Store the snapped timestamp\n lastSnapTimestamp = SafeCast.toUint64(block.timestamp);\n\n emit BalancesSnapped(block.timestamp, blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct VerifyBalancesParams {\n bytes32 blockRoot;\n uint64 firstPendingDepositSlot;\n // BeaconBlock.BeaconBlockBody.deposits[0].slot\n bytes firstPendingDepositSlotProof;\n bytes32 balancesContainerRoot;\n // BeaconBlock.state.validators\n bytes validatorContainerProof;\n bytes32[] validatorBalanceLeaves;\n // BeaconBlock.state.validators[validatorIndex].balance\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n function verifyBalances(VerifyBalancesParams calldata params) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalances[params.blockRoot];\n // Check the balances are the latest\n require(lastSnapTimestamp > 0, \"No snapped balances\");\n require(balancesMem.timestamp == lastSnapTimestamp, \"Stale snap\");\n\n uint256 depositsCount = depositsRoots.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit to the beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n params.blockRoot,\n params.firstPendingDepositSlot,\n params.firstPendingDepositSlotProof\n );\n\n uint64 firstPendingDepositBlockNumber = IBeaconOracle(BEACON_ORACLE)\n .slotToBlock(params.firstPendingDepositSlot);\n\n // For each native staking contract's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n bytes32 depositDataRoot = depositsRoots[i];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit block number is before the\n // block number of the staking strategy's deposit.\n // If it has it will need to be verified with `verifyDeposit`\n require(\n firstPendingDepositBlockNumber <\n deposits[depositDataRoot].blockNumber,\n \"Deposit has been processed\"\n );\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei +=\n uint256(deposits[depositDataRoot].amountGwei) *\n 1 gwei;\n }\n }\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n params.blockRoot,\n params.balancesContainerRoot,\n params.validatorContainerProof\n );\n\n // for each validator\n for (uint256 i = 0; i < verifiedValidatorsCount; ++i) {\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n params.balancesContainerRoot,\n params.validatorBalanceLeaves[i],\n params.validatorBalanceProofs[i],\n verifiedValidators[i].index,\n IBeaconProofs.BalanceProofLevel.Container\n );\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += uint256(validatorBalanceGwei) * 1 gwei;\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n validatorState[\n verifiedValidators[i].pubKeyHash\n ] = VALIDATOR_STATE.EXITED;\n\n // Remove the validator from the list of verified validators.\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n // Remove the validator with a zero balance from the list of verified validators\n // Move the last validator to the current index\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n }\n }\n }\n\n uint256 wethBalance = IWETH9(WETH).balanceOf(address(this));\n\n // Store the verified balance in storage\n lastVerifiedEthBalance = SafeCast.toUint128(\n totalDepositsWei +\n totalValidatorBalance +\n wethBalance +\n balancesMem.ethBalance\n );\n // Reset the last snap timestamp so a new snapBalances has to be made\n lastSnapTimestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n wethBalance,\n balancesMem.ethBalance\n );\n }\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /***************************************\n WETH and ETH Accounting\n ****************************************/\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n function _convertWethToEth(uint256 _ethAmount) internal {\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance\n if (lastVerifiedEthBalance > _ethAmount) {\n lastVerifiedEthBalance -= SafeCast.toUint128(_ethAmount);\n } else {\n // This can happen if all ETH in the validators was withdrawn\n // and there was more consensus rewards since the last balance verification.\n // Or it can happen if ETH is donated to this strategy.\n lastVerifiedEthBalance = 0;\n }\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n function _convertEthToWeth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += SafeCast.toUint128(_wethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n}\n" + }, + "contracts/strategies/NativeStaking/FeeAccumulator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\n/**\n * @title Fee Accumulator for Native Staking SSV Strategy\n * @notice Receives execution rewards which includes tx fees and\n * MEV rewards like tx priority and tx ordering.\n * It does NOT include swept ETH from beacon chain consensus rewards or full validator withdrawals.\n * @author Origin Protocol Inc\n */\ncontract FeeAccumulator {\n /// @notice The address of the Native Staking Strategy\n address public immutable STRATEGY;\n\n event ExecutionRewardsCollected(address indexed strategy, uint256 amount);\n\n /**\n * @param _strategy Address of the Native Staking Strategy\n */\n constructor(address _strategy) {\n STRATEGY = _strategy;\n }\n\n /**\n * @notice sends all ETH in this FeeAccumulator contract to the Native Staking Strategy.\n * @return eth The amount of execution rewards that were sent to the Native Staking Strategy\n */\n function collect() external returns (uint256 eth) {\n require(msg.sender == STRATEGY, \"Caller is not the Strategy\");\n\n eth = address(this).balance;\n if (eth > 0) {\n // Send the ETH to the Native Staking Strategy\n Address.sendValue(payable(STRATEGY), eth);\n\n emit ExecutionRewardsCollected(STRATEGY, eth);\n }\n }\n\n /**\n * @dev Accept ETH\n */\n receive() external payable {}\n}\n" + }, + "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { FeeAccumulator } from \"./FeeAccumulator.sol\";\nimport { ValidatorAccountant } from \"./ValidatorAccountant.sol\";\nimport { ISSVNetwork } from \"../../interfaces/ISSVNetwork.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/// @title Native Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract NativeStakingSSVStrategy is\n ValidatorAccountant,\n InitializableAbstractStrategy\n{\n using SafeERC20 for IERC20;\n\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n /// @notice Fee collector address\n /// @dev this address will receive maximal extractable value (MEV) rewards. These are\n /// rewards for arranging transactions in a way that benefits the validator.\n address payable public immutable FEE_ACCUMULATOR_ADDRESS;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[49] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n /// @param _feeAccumulator Address of the fee accumulator receiving execution layer validator rewards\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n uint256 _maxValidators,\n address _feeAccumulator,\n address _beaconChainDepositContract\n )\n InitializableAbstractStrategy(_baseConfig)\n ValidatorAccountant(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _maxValidators\n )\n {\n SSV_TOKEN = _ssvToken;\n FEE_ACCUMULATOR_ADDRESS = payable(_feeAccumulator);\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract.\n /// @param _rewardTokenAddresses Address of reward token for platform\n /// @param _assets Addresses of initial supported assets\n /// @param _pTokens Platform Token corresponding addresses\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n\n // Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\n FEE_ACCUMULATOR_ADDRESS\n );\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n /// @param _asset Address of asset to deposit. Has to be WETH.\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n depositedWethAccountedFor += _amount;\n _deposit(_asset, _amount);\n }\n\n /// @dev Deposit WETH to this strategy so it can later be staked into a validator.\n /// @param _asset Address of WETH\n /// @param _amount Amount of WETH to deposit\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n /*\n * We could do a check here that would revert when \"_amount % 32 ether != 0\". With the idea of\n * not allowing deposits that will result in WETH sitting on the strategy after all the possible batches\n * of 32ETH have been staked.\n * But someone could mess with our strategy by sending some WETH to it. And we might want to deposit just\n * enough WETH to add it up to 32 so it can be staked. For that reason the check is left out.\n *\n * WETH sitting on the strategy won't interfere with the accounting since accounting only operates on ETH.\n */\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n depositedWethAccountedFor = wethBalance;\n\n _deposit(WETH, newWeth);\n }\n }\n\n /// @notice Withdraw WETH from this contract. Used only if some WETH for is lingering on the contract.\n /// That can happen when:\n /// - after mints if the strategy is the default\n /// - time between depositToStrategy and stakeEth\n /// - the deposit was not a multiple of 32 WETH\n /// - someone sent WETH directly to this contract\n /// Will NOT revert if the strategy is paused from an accounting failure.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n _wethWithdrawn(_amount);\n\n IERC20(_asset).safeTransfer(_recipient, _amount);\n emit Withdrawal(_asset, address(0), _amount);\n }\n\n /// @notice transfer all WETH deposits back to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `exitSsvValidator` and `removeSsvValidator` operations.\n /// This does not withdraw any execution rewards from the FeeAccumulator or\n /// consensus rewards in this strategy.\n /// Any ETH in this strategy that was swept from a full validator withdrawal will not be withdrawn.\n /// ETH from full validator withdrawals is sent to the Vault using `doAccounting`.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n if (wethBalance > 0) {\n _withdraw(vaultAddress, WETH, wethBalance);\n }\n }\n\n /// @notice Returns the total value of (W)ETH that is staked to the validators\n /// and WETH deposits that are still to be staked.\n /// This does not include ETH from consensus rewards sitting in this strategy\n /// or ETH from MEV rewards in the FeeAccumulator. These rewards are harvested\n /// and sent to the Dripper so will eventually be sent to the Vault as WETH.\n /// @param _asset Address of weth asset\n /// @return balance Total value of (W)ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n balance =\n // add the ETH that has been staked in validators\n activeDepositedValidators *\n FULL_STAKE +\n // add the WETH in the strategy from deposits that are still to be staked\n IERC20(WETH).balanceOf(address(this));\n }\n\n function pause() external onlyStrategist {\n _pause();\n }\n\n /// @notice Returns bool indicating whether asset is supported by strategy.\n /// @param _asset The address of the asset token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for deposits\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\n function setFeeRecipient() external {\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\n FEE_ACCUMULATOR_ADDRESS\n );\n }\n\n /**\n * @notice Only accept ETH from the FeeAccumulator and the WETH contract - required when\n * unwrapping WETH just before staking it to the validator.\n * The strategy will also receive ETH from the priority fees of transactions when producing blocks\n * as defined in EIP-1559.\n * The tx fees come from the Beacon chain so do not need any EVM level permissions to receive ETH.\n * The tx fees are paid with each block produced. They are not included in the consensus rewards\n * which are periodically swept from the validators to this strategy.\n * For accounting purposes, the priority fees of transactions will be considered consensus rewards\n * and will be included in the AccountingConsensusRewards event.\n * @dev don't want to receive donations from anyone else as donations over the fuse limits will\n * mess with the accounting of the consensus rewards and validator full withdrawals.\n */\n receive() external payable {\n require(\n msg.sender == FEE_ACCUMULATOR_ADDRESS || msg.sender == WETH,\n \"Eth not from allowed contracts\"\n );\n }\n\n /***************************************\n Internal functions\n ****************************************/\n\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Convert accumulated ETH to WETH and send to the Harvester.\n /// Will revert if the strategy is paused for accounting.\n function _collectRewardTokens() internal override whenNotPaused {\n // collect ETH from execution rewards from the fee accumulator\n uint256 executionRewards = FeeAccumulator(FEE_ACCUMULATOR_ADDRESS)\n .collect();\n\n // total ETH rewards to be harvested = execution rewards + consensus rewards\n uint256 ethRewards = executionRewards + consensusRewards;\n\n require(\n address(this).balance >= ethRewards,\n \"Insufficient eth balance\"\n );\n\n if (ethRewards > 0) {\n // reset the counter keeping track of beacon chain consensus rewards\n consensusRewards = 0;\n\n // Convert ETH rewards to WETH\n IWETH9(WETH).deposit{ value: ethRewards }();\n\n IERC20(WETH).safeTransfer(harvesterAddress, ethRewards);\n emit RewardTokenCollected(harvesterAddress, WETH, ethRewards);\n }\n }\n\n /// @dev emits Withdrawal event from NativeStakingSSVStrategy\n function _wethWithdrawnToVault(uint256 _amount) internal override {\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _wethWithdrawn(uint256 _amount) internal override {\n /* In an ideal world we wouldn't need to reduce the deduction amount when the\n * depositedWethAccountedFor is smaller than the _amount.\n *\n * The reason this is required is that a malicious actor could sent WETH directly\n * to this contract and that would circumvent the increase of depositedWethAccountedFor\n * property. When the ETH would be staked the depositedWethAccountedFor amount could\n * be deducted so much that it would be negative.\n */\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n }\n}\n" + }, + "contracts/strategies/NativeStaking/ValidatorAccountant.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ValidatorRegistrator } from \"./ValidatorRegistrator.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\n\n/// @title Validator Accountant\n/// @notice Attributes the ETH swept from beacon chain validators to this strategy contract\n/// as either full or partial withdrawals. Partial withdrawals being consensus rewards.\n/// Full withdrawals are from exited validators.\n/// @author Origin Protocol Inc\nabstract contract ValidatorAccountant is ValidatorRegistrator {\n /// @notice The minimum amount of blocks that need to pass between two calls to manuallyFixAccounting\n uint256 public constant MIN_FIX_ACCOUNTING_CADENCE = 7200; // 1 day\n\n /// @notice Keeps track of the total consensus rewards swept from the beacon chain\n uint256 public consensusRewards;\n\n /// @notice start of fuse interval\n uint256 public fuseIntervalStart;\n /// @notice end of fuse interval\n uint256 public fuseIntervalEnd;\n /// @notice last block number manuallyFixAccounting has been called\n uint256 public lastFixAccountingBlockNumber;\n\n uint256[49] private __gap;\n\n event FuseIntervalUpdated(uint256 start, uint256 end);\n event AccountingFullyWithdrawnValidator(\n uint256 noOfValidators,\n uint256 remainingValidators,\n uint256 wethSentToVault\n );\n event AccountingValidatorSlashed(\n uint256 remainingValidators,\n uint256 wethSentToVault\n );\n event AccountingConsensusRewards(uint256 amount);\n\n event AccountingManuallyFixed(\n int256 validatorsDelta,\n int256 consensusRewardsDelta,\n uint256 wethToVault\n );\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n uint256 _maxValidators\n )\n ValidatorRegistrator(\n _wethAddress,\n _vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _maxValidators\n )\n {}\n\n /// @notice set fuse interval values\n function setFuseInterval(\n uint256 _fuseIntervalStart,\n uint256 _fuseIntervalEnd\n ) external onlyGovernor {\n require(\n _fuseIntervalStart < _fuseIntervalEnd &&\n _fuseIntervalEnd < 32 ether &&\n _fuseIntervalEnd - _fuseIntervalStart >= 4 ether,\n \"Incorrect fuse interval\"\n );\n\n fuseIntervalStart = _fuseIntervalStart;\n fuseIntervalEnd = _fuseIntervalEnd;\n\n emit FuseIntervalUpdated(_fuseIntervalStart, _fuseIntervalEnd);\n }\n\n /* solhint-disable max-line-length */\n /// This notion page offers a good explanation of how the accounting functions\n /// https://www.notion.so/originprotocol/Limited-simplified-native-staking-accounting-67a217c8420d40678eb943b9da0ee77d\n /// In short, after dividing by 32, if the ETH remaining on the contract falls between 0 and fuseIntervalStart,\n /// the accounting function will treat that ETH as Beacon chain consensus rewards.\n /// On the contrary, if after dividing by 32, the ETH remaining on the contract falls between fuseIntervalEnd and 32,\n /// the accounting function will treat that as a validator slashing.\n /// @notice Perform the accounting attributing beacon chain ETH to either full or partial withdrawals. Returns true when\n /// accounting is valid and fuse isn't \"blown\". Returns false when fuse is blown.\n /// @dev This function could in theory be permission-less but lets allow only the Registrator (Defender Action) to call it\n /// for now.\n /// @return accountingValid true if accounting was successful, false if fuse is blown\n /* solhint-enable max-line-length */\n function doAccounting()\n external\n onlyRegistrator\n whenNotPaused\n nonReentrant\n returns (bool accountingValid)\n {\n // pause the accounting on failure\n accountingValid = _doAccounting(true);\n }\n\n // slither-disable-start reentrancy-eth\n function _doAccounting(bool pauseOnFail)\n internal\n returns (bool accountingValid)\n {\n if (address(this).balance < consensusRewards) {\n return _failAccounting(pauseOnFail);\n }\n\n // Calculate all the new ETH that has been swept to the contract since the last accounting\n uint256 newSweptETH = address(this).balance - consensusRewards;\n accountingValid = true;\n\n // send the ETH that is from fully withdrawn validators to the Vault\n if (newSweptETH >= FULL_STAKE) {\n uint256 fullyWithdrawnValidators;\n // explicitly cast to uint256 as we want to round to a whole number of validators\n fullyWithdrawnValidators = uint256(newSweptETH / FULL_STAKE);\n activeDepositedValidators -= fullyWithdrawnValidators;\n\n uint256 wethToVault = FULL_STAKE * fullyWithdrawnValidators;\n IWETH9(WETH).deposit{ value: wethToVault }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, wethToVault);\n _wethWithdrawnToVault(wethToVault);\n\n emit AccountingFullyWithdrawnValidator(\n fullyWithdrawnValidators,\n activeDepositedValidators,\n wethToVault\n );\n }\n\n uint256 ethRemaining = address(this).balance - consensusRewards;\n // should be less than a whole validator stake\n require(ethRemaining < FULL_STAKE, \"Unexpected accounting\");\n\n // If no Beacon chain consensus rewards swept\n if (ethRemaining == 0) {\n // do nothing\n return accountingValid;\n } else if (ethRemaining < fuseIntervalStart) {\n // Beacon chain consensus rewards swept (partial validator withdrawals)\n // solhint-disable-next-line reentrancy\n consensusRewards += ethRemaining;\n emit AccountingConsensusRewards(ethRemaining);\n } else if (ethRemaining > fuseIntervalEnd) {\n // Beacon chain consensus rewards swept but also a slashed validator fully exited\n IWETH9(WETH).deposit{ value: ethRemaining }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, ethRemaining);\n activeDepositedValidators -= 1;\n\n _wethWithdrawnToVault(ethRemaining);\n\n emit AccountingValidatorSlashed(\n activeDepositedValidators,\n ethRemaining\n );\n }\n // Oh no... Fuse is blown. The Strategist needs to adjust the accounting values.\n else {\n return _failAccounting(pauseOnFail);\n }\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @dev pause any further accounting if required and return false\n function _failAccounting(bool pauseOnFail)\n internal\n returns (bool accountingValid)\n {\n // pause if not already\n if (pauseOnFail) {\n _pause();\n }\n // fail the accounting\n accountingValid = false;\n }\n\n /// @notice Allow the Strategist to fix the accounting of this strategy and unpause.\n /// @param _validatorsDelta adjust the active validators by up to plus three or minus three\n /// @param _consensusRewardsDelta adjust the accounted for consensus rewards up or down\n /// @param _ethToVaultAmount the amount of ETH that gets wrapped into WETH and sent to the Vault\n /// @dev There is a case when a validator(s) gets slashed so much that the eth swept from\n /// the beacon chain enters the fuse area and there are no consensus rewards on the contract\n /// to \"dip into\"/use. To increase the amount of unaccounted ETH over the fuse end interval\n /// we need to reduce the amount of active deposited validators and immediately send WETH\n /// to the vault, so it doesn't interfere with further accounting.\n function manuallyFixAccounting(\n int256 _validatorsDelta,\n int256 _consensusRewardsDelta,\n uint256 _ethToVaultAmount\n ) external onlyStrategist whenPaused nonReentrant {\n require(\n lastFixAccountingBlockNumber + MIN_FIX_ACCOUNTING_CADENCE <\n block.number,\n \"Fix accounting called too soon\"\n );\n require(\n _validatorsDelta >= -3 &&\n _validatorsDelta <= 3 &&\n // new value must be positive\n int256(activeDepositedValidators) + _validatorsDelta >= 0,\n \"Invalid validatorsDelta\"\n );\n require(\n _consensusRewardsDelta >= -332 ether &&\n _consensusRewardsDelta <= 332 ether &&\n // new value must be positive\n int256(consensusRewards) + _consensusRewardsDelta >= 0,\n \"Invalid consensusRewardsDelta\"\n );\n require(_ethToVaultAmount <= 32 ether * 3, \"Invalid wethToVaultAmount\");\n\n activeDepositedValidators = uint256(\n int256(activeDepositedValidators) + _validatorsDelta\n );\n consensusRewards = uint256(\n int256(consensusRewards) + _consensusRewardsDelta\n );\n lastFixAccountingBlockNumber = block.number;\n if (_ethToVaultAmount > 0) {\n IWETH9(WETH).deposit{ value: _ethToVaultAmount }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, _ethToVaultAmount);\n _wethWithdrawnToVault(_ethToVaultAmount);\n }\n\n emit AccountingManuallyFixed(\n _validatorsDelta,\n _consensusRewardsDelta,\n _ethToVaultAmount\n );\n\n // rerun the accounting to see if it has now been fixed.\n // Do not pause the accounting on failure as it is already paused\n require(_doAccounting(false), \"Fuse still blown\");\n\n // unpause since doAccounting was successful\n _unpause();\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n /// @dev allows for NativeStakingSSVStrategy contract to emit the Withdrawal event\n function _wethWithdrawnToVault(uint256 _amount) internal virtual;\n}\n" + }, + "contracts/strategies/NativeStaking/ValidatorRegistrator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IConsolidationTarget } from \"../../interfaces/IConsolidation.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconConsolidation } from \"../../beacon/BeaconConsolidation.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Registrator of the validators\n * @notice This contract implements all the required functionality to register, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract ValidatorRegistrator is Governable, Pausable {\n /// @notice The maximum amount of ETH that can be staked by a validator\n /// @dev this can change in the future with EIP-7251, Increase the MAX_EFFECTIVE_BALANCE\n uint256 public constant FULL_STAKE = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Maximum number of validators that can be registered in this strategy\n uint256 public immutable MAX_VALIDATORS;\n\n /// @notice Address of the registrator - allowed to register, exit and remove validators\n address public validatorRegistrator;\n /// @notice The number of validators that have 32 (!) ETH actively deposited. When a new deposit\n /// to a validator happens this number increases, when a validator exit is detected this number\n /// decreases.\n uint256 public activeDepositedValidators;\n /// @notice State of the validators keccak256(pubKey) => state\n mapping(bytes32 => VALIDATOR_STATE) public validatorsStates;\n /// @notice The account that is allowed to modify stakeETHThreshold and reset stakeETHTally\n address public stakingMonitor;\n /// @notice Amount of ETH that can be staked before staking on the contract is suspended\n /// and the `stakingMonitor` needs to approve further staking by calling `resetStakeETHTally`\n uint256 public stakeETHThreshold;\n /// @notice Amount of ETH that has been staked since the `stakingMonitor` last called `resetStakeETHTally`.\n /// This can not go above `stakeETHThreshold`.\n uint256 public stakeETHTally;\n\n /// @notice Number of validators currently being consolidated\n uint256 public consolidationCount;\n address public consolidationTargetStrategy;\n /// @notice Mapping of support target staking strategies that can be used for consolidation\n mapping(address => bool) public consolidationTargetStrategies;\n\n // For future use\n uint256[44] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n EXITING, // exit message has been posted and validator is in the process of exiting\n EXIT_COMPLETE // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event ETHStaked(bytes32 indexed pubKeyHash, bytes pubKey, uint256 amount);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event SSVValidatorExitInitiated(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event SSVValidatorExitCompleted(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event ConsolidationRequested(\n bytes[] sourcePubKeys,\n bytes targetPubKey,\n address targetStakingStrategy,\n uint256 consolidationCount\n );\n event ConsolidationConfirmed(\n uint256 consolidationCount,\n uint256 activeDepositedValidators\n );\n event StakeETHThresholdChanged(uint256 amount);\n event StakeETHTallyReset();\n event TargetStrategyAdded(address indexed strategy);\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(\n msg.sender == validatorRegistrator,\n \"Caller is not the Registrator\"\n );\n _;\n }\n\n /// @dev Throws if called by any account other than the Staking monitor\n modifier onlyStakingMonitor() {\n require(msg.sender == stakingMonitor, \"Caller is not the Monitor\");\n _;\n }\n\n /// @dev Throws if called by any account other than the Strategist\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(VAULT_ADDRESS).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n uint256 _maxValidators\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n MAX_VALIDATORS = _maxValidators;\n }\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Set the address of the staking monitor that is allowed to reset stakeETHTally\n function setStakingMonitor(address _address) external onlyGovernor {\n stakingMonitor = _address;\n emit StakingMonitorChanged(_address);\n }\n\n /// @notice Set the amount of ETH that can be staked before staking monitor\n // needs to a approve further staking by resetting the stake ETH tally\n function setStakeETHThreshold(uint256 _amount) external onlyGovernor {\n stakeETHThreshold = _amount;\n emit StakeETHThresholdChanged(_amount);\n }\n\n /// @notice Reset the stakeETHTally\n function resetStakeETHTally() external onlyStakingMonitor {\n stakeETHTally = 0;\n emit StakeETHTallyReset();\n }\n\n /// @notice Adds support for a new staking strategy that can be used for consolidation.\n function addTargetStrategy(address _strategy) external onlyGovernor {\n consolidationTargetStrategies[_strategy] = true;\n\n emit TargetStrategyAdded(_strategy);\n }\n\n /// @notice Stakes WETH to the node validators\n /// @param validators A list of validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n // slither-disable-start reentrancy-eth\n function stakeEth(ValidatorStakeData[] calldata validators)\n external\n onlyRegistrator\n whenNotPaused\n nonReentrant\n {\n uint256 requiredETH = validators.length * FULL_STAKE;\n\n // Check there is enough WETH from the deposits sitting in this strategy contract\n require(\n requiredETH <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(\n activeDepositedValidators + validators.length <= MAX_VALIDATORS,\n \"Max validators reached\"\n );\n\n require(\n stakeETHTally + requiredETH <= stakeETHThreshold,\n \"Staking ETH over threshold\"\n );\n stakeETHTally += requiredETH;\n\n // Convert required ETH from WETH\n IWETH9(WETH).withdraw(requiredETH);\n _wethWithdrawn(requiredETH);\n\n /* 0x01 to indicate that withdrawal credentials will contain an EOA address that the sweeping function\n * can sweep funds to.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x01),\n bytes11(0),\n address(this)\n );\n\n // For each validator\n for (uint256 i = 0; i < validators.length; ++i) {\n bytes32 pubKeyHash = keccak256(validators[i].pubkey);\n\n require(\n validatorsStates[pubKeyHash] == VALIDATOR_STATE.REGISTERED,\n \"Validator not registered\"\n );\n\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: FULL_STAKE\n }(\n validators[i].pubkey,\n withdrawalCredentials,\n validators[i].signature,\n validators[i].depositDataRoot\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.STAKED;\n\n emit ETHStaked(pubKeyHash, validators[i].pubkey, FULL_STAKE);\n }\n // save gas by changing this storage variable only once rather each time in the loop.\n activeDepositedValidators += validators.length;\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Registers a new validator in the SSV Cluster.\n /// Only the registrator can call this function.\n /// @param publicKeys The public keys of the validators\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for each validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n require(\n publicKeys.length == sharesData.length,\n \"Pubkey sharesData mismatch\"\n );\n // Check each public key has not already been used\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n emit SSVValidatorRegistered(pubKeyHash, publicKeys[i], operatorIds);\n }\n\n ISSVNetwork(SSV_NETWORK).bulkRegisterValidator(\n publicKeys,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Exit a validator from the Beacon chain.\n /// The staked ETH will eventually swept to this native staking strategy.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n // slither-disable-start reentrancy-no-eth\n function exitSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds\n ) external onlyRegistrator whenNotPaused {\n bytes32 pubKeyHash = keccak256(publicKey);\n VALIDATOR_STATE currentState = validatorsStates[pubKeyHash];\n require(currentState == VALIDATOR_STATE.STAKED, \"Validator not staked\");\n\n ISSVNetwork(SSV_NETWORK).exitValidator(publicKey, operatorIds);\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXITING;\n\n emit SSVValidatorExitInitiated(pubKeyHash, publicKey, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove a validator from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n bytes32 pubKeyHash = keccak256(publicKey);\n VALIDATOR_STATE currentState = validatorsStates[pubKeyHash];\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == VALIDATOR_STATE.EXITING ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exiting\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXIT_COMPLETE;\n\n emit SSVValidatorExitCompleted(pubKeyHash, publicKey, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Deposits more SSV Tokens to the SSV Network contract which is used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// uses \"onlyStrategist\" modifier so continuous front-running can't DOS our maintenance service\n /// that tries to top up SSV tokens.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function depositSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyStrategist {\n ISSVNetwork(SSV_NETWORK).deposit(\n address(this),\n operatorIds,\n ssvAmount,\n cluster\n );\n }\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n New Consolidation\n ****************************************/\n\n function requestConsolidation(\n bytes[] calldata sourcePubKeys,\n bytes calldata targetPubKey,\n address targetStakingStrategy\n ) external nonReentrant whenNotPaused onlyRegistrator {\n require(\n consolidationTargetStrategies[targetStakingStrategy],\n \"Invalid target strategy\"\n );\n\n bytes32 targetPubKeyHash = keccak256(targetPubKey);\n bytes32 sourcePubKeyHash;\n for (uint256 i = 0; i < sourcePubKeys.length; ++i) {\n // hash the source validator's public key using the Beacon Chain's format\n sourcePubKeyHash = keccak256(sourcePubKeys[i]);\n require(sourcePubKeyHash != targetPubKeyHash, \"Self consolidation\");\n require(\n validatorsStates[sourcePubKeyHash] == VALIDATOR_STATE.STAKED,\n \"Source validator not staked\"\n );\n\n // Request consolidation from source to target validator\n BeaconConsolidation.request(sourcePubKeys[i], targetPubKey);\n\n // Store the state of the source validator as exiting so it can be removed\n // after the consolidation is confirmed\n validatorsStates[sourcePubKeyHash] == VALIDATOR_STATE.EXITING;\n }\n\n // Hash using the Beacon Chain's format\n bytes32 lastSourcePubKeyHash = _hashPubKey(\n sourcePubKeys[sourcePubKeys.length - 1]\n );\n // Call the new compounding staking strategy to validate the target validator\n IConsolidationTarget(targetStakingStrategy).requestConsolidation(\n lastSourcePubKeyHash,\n targetPubKeyHash\n );\n\n // Store the consolidation state\n consolidationCount = sourcePubKeys.length;\n consolidationTargetStrategy = targetStakingStrategy;\n\n // Pause the strategy to prevent further consolidations or validator exits\n _pause();\n\n emit ConsolidationRequested(\n sourcePubKeys,\n targetPubKey,\n targetStakingStrategy,\n sourcePubKeys.length\n );\n }\n\n function confirmConsolidation()\n external\n nonReentrant\n whenPaused\n returns (uint256 consolidationCount_)\n {\n // Check the caller is the target staking strategy\n require(\n msg.sender == consolidationTargetStrategy,\n \"Not target strategy\"\n );\n\n // Load the number of validators being consolidated into memory\n consolidationCount_ = consolidationCount;\n\n // Need to check this is from the new staking strategy\n require(consolidationCount_ > 0, \"No consolidation in progress\");\n\n // Store the reduced number of active deposited validators\n // managed by this strategy\n activeDepositedValidators -= consolidationCount_;\n\n // Reset the consolidation count\n consolidationCount = 0;\n consolidationTargetStrategy = address(0);\n\n // Unpause the strategy to allow further operations\n _unpause();\n\n emit ConsolidationConfirmed(\n consolidationCount_,\n activeDepositedValidators\n );\n }\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _wethWithdrawn(uint256 _amount) internal virtual;\n}\n" + }, + "contracts/strategies/plume/RoosterAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Rooster AMO strategy\n * @author Origin Protocol Inc\n * @custom:security-contact security@originprotocol.com\n */\nimport { Math as MathRooster } from \"../../../lib/rooster/v2-common/libraries/Math.sol\";\nimport { Math as Math_v5 } from \"../../../lib/rooster/openzeppelin-custom/contracts/utils/math/Math.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { IMaverickV2Pool } from \"../../interfaces/plume/IMaverickV2Pool.sol\";\nimport { IMaverickV2Quoter } from \"../../interfaces/plume/IMaverickV2Quoter.sol\";\nimport { IMaverickV2LiquidityManager } from \"../../interfaces/plume/IMaverickV2LiquidityManager.sol\";\nimport { IMaverickV2PoolLens } from \"../../interfaces/plume/IMaverickV2PoolLens.sol\";\nimport { IMaverickV2Position } from \"../../interfaces/plume/IMaverickV2Position.sol\";\nimport { IVotingDistributor } from \"../../interfaces/plume/IVotingDistributor.sol\";\nimport { IPoolDistributor } from \"../../interfaces/plume/IPoolDistributor.sol\";\n// importing custom version of rooster TickMath because of dependency collision. Maverick uses\n// a newer OpenZepplin Math library with functionality that is not present in 4.4.2 (the one we use)\nimport { TickMath } from \"../../../lib/rooster/v2-common/libraries/TickMath.sol\";\n\ncontract RoosterAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n using SafeCast for uint256;\n\n /***************************************\n Storage slot members\n ****************************************/\n\n /// @notice NFT tokenId of the liquidity position\n ///\n /// @dev starts with value of 1 and can not be 0\n // solhint-disable-next-line max-line-length\n /// https://github.com/rooster-protocol/rooster-contracts/blob/fbfecbc519e4495b12598024a42630b4a8ea4489/v2-common/contracts/base/Nft.sol#L14\n uint256 public tokenId;\n /// @dev Minimum amount of tokens the strategy would be able to withdraw from the pool.\n /// minimum amount of tokens are withdrawn at a 1:1 price\n /// Important: Underlying assets contains only assets that are deposited in the underlying Rooster pool.\n /// WETH or OETH held by this contract is not accounted for in underlying assets\n uint256 public underlyingAssets;\n /// @notice Marks the start of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareStart;\n /// @notice Marks the end of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareEnd;\n /// @dev reserved for inheritance\n int256[46] private __reserved;\n\n /***************************************\n Constants, structs and events\n ****************************************/\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the OETH token contract\n address public immutable OETH;\n /// @notice the underlying AMO Maverick (Rooster) pool\n IMaverickV2Pool public immutable mPool;\n /// @notice the Liquidity manager used to add liquidity to the pool\n IMaverickV2LiquidityManager public immutable liquidityManager;\n /// @notice the Maverick V2 poolLens\n ///\n /// @dev only used to provide the pool's current sqrtPrice\n IMaverickV2PoolLens public immutable poolLens;\n /// @notice the Maverick V2 position\n ///\n /// @dev provides details of the NFT LP position and offers functions to\n /// remove the liquidity.\n IMaverickV2Position public immutable maverickPosition;\n /// @notice the Maverick Quoter\n IMaverickV2Quoter public immutable quoter;\n /// @notice the Maverick Voting Distributor\n IVotingDistributor public immutable votingDistributor;\n /// @notice the Maverick Pool Distributor\n IPoolDistributor public immutable poolDistributor;\n\n /// @notice sqrtPriceTickLower\n /// @dev tick lower represents the lower price of OETH priced in WETH. Meaning the pool\n /// offers more than 1 OETH for 1 WETH. In other terms to get 1 OETH the swap needs to offer 0.9999 WETH\n /// this is where purchasing OETH with WETH within the liquidity position is the cheapest.\n ///\n /// _____________________\n /// | | |\n /// | WETH | OETH |\n /// | | |\n /// | | |\n /// --------- * ---- * ---------- * ---------\n /// currentPrice\n /// sqrtPriceHigher-(1:1 parity)\n /// sqrtPriceLower\n ///\n ///\n /// Price is defined as price of token1 in terms of token0. (token1 / token0)\n /// @notice sqrtPriceTickLower - OETH is priced 0.9999 WETH\n uint256 public immutable sqrtPriceTickLower;\n /// @notice sqrtPriceTickHigher\n /// @dev tick higher represents 1:1 price parity of WETH to OETH\n uint256 public immutable sqrtPriceTickHigher;\n /// @dev price at parity (in OETH this is equal to sqrtPriceTickHigher)\n uint256 public immutable sqrtPriceAtParity;\n /// @notice The tick where the strategy deploys the liquidity to\n int32 public constant TICK_NUMBER = -1;\n /// @notice Minimum liquidity that must be exceeded to continue with the action\n /// e.g. deposit, add liquidity\n uint256 public constant ACTION_THRESHOLD = 1e12;\n /// @notice Maverick pool static liquidity bin type\n uint8 public constant MAV_STATIC_BIN_KIND = 0;\n /// @dev a threshold under which the contract no longer allows for the protocol to rebalance. Guarding\n /// against a strategist / guardian being taken over and with multiple transactions draining the\n /// protocol funds.\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n /// @notice Emitted when the allowed interval within which the strategy contract is allowed to deposit\n /// liquidity to the underlying pool is updated.\n /// @param allowedWethShareStart The start of the interval\n /// @param allowedWethShareEnd The end of the interval\n event PoolWethShareIntervalUpdated(\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n /// @notice Emitted when liquidity is removed from the underlying pool\n /// @param withdrawLiquidityShare Share of strategy's liquidity that has been removed\n /// @param removedWETHAmount The amount of WETH removed\n /// @param removedOETHAmount The amount of OETH removed\n /// @param underlyingAssets Updated amount of strategy's underlying assets\n event LiquidityRemoved(\n uint256 withdrawLiquidityShare,\n uint256 removedWETHAmount,\n uint256 removedOETHAmount,\n uint256 underlyingAssets\n );\n\n /// @notice Emitted when the underlying pool is rebalanced\n /// @param currentPoolWethShare The resulting share of strategy's liquidity\n /// in the TICK_NUMBER\n event PoolRebalanced(uint256 currentPoolWethShare);\n\n /// @notice Emitted when the amount of underlying assets the strategy hold as\n /// liquidity in the pool is updated.\n /// @param underlyingAssets Updated amount of strategy's underlying assets\n event UnderlyingAssetsUpdated(uint256 underlyingAssets);\n\n /// @notice Emitted when liquidity is added to the underlying pool\n /// @param wethAmountDesired Amount of WETH desired to be deposited\n /// @param oethAmountDesired Amount of OETH desired to be deposited\n /// @param wethAmountSupplied Amount of WETH deposited\n /// @param oethAmountSupplied Amount of OETH deposited\n /// @param tokenId NFT liquidity token id\n /// @param underlyingAssets Updated amount of underlying assets\n event LiquidityAdded(\n uint256 wethAmountDesired,\n uint256 oethAmountDesired,\n uint256 wethAmountSupplied,\n uint256 oethAmountSupplied,\n uint256 tokenId,\n uint256 underlyingAssets\n ); // 0x1530ec74\n\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n ); // 0x3681e8e0\n\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth); // 0x989e5ca8\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth); // 0xa6737d87\n error OutsideExpectedTickRange(); // 0xa6e1bad2\n error SlippageCheck(uint256 tokenReceived); // 0x355cdb78\n\n /// @notice the constructor\n /// @dev This contract is intended to be used as a proxy. To prevent the\n /// potential confusion of having a functional implementation contract\n /// the constructor has the `initializer` modifier. This way the\n /// `initialize` function can not be called on the implementation contract.\n /// For the same reason the implementation contract also has the governor\n /// set to a zero address.\n /// @param _stratConfig the basic strategy configuration\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _oethAddress Address of the Erc20 OETH Token contract\n /// @param _liquidityManager Address of liquidity manager to add\n /// the liquidity\n /// @param _poolLens Address of the pool lens contract\n /// @param _maverickPosition Address of the Maverick's position contract\n /// @param _maverickQuoter Address of the Maverick's Quoter contract\n /// @param _mPool Address of the Rooster concentrated liquidity pool\n /// @param _upperTickAtParity Bool when true upperTick is the one where the\n /// price of OETH and WETH are at parity\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethAddress,\n address _liquidityManager,\n address _poolLens,\n address _maverickPosition,\n address _maverickQuoter,\n address _mPool,\n bool _upperTickAtParity,\n address _votingDistributor,\n address _poolDistributor\n ) initializer InitializableAbstractStrategy(_stratConfig) {\n require(\n address(IMaverickV2Pool(_mPool).tokenA()) == _wethAddress,\n \"WETH not TokenA\"\n );\n require(\n address(IMaverickV2Pool(_mPool).tokenB()) == _oethAddress,\n \"OETH not TokenB\"\n );\n require(\n _liquidityManager != address(0),\n \"LiquidityManager zero address not allowed\"\n );\n require(\n _maverickQuoter != address(0),\n \"Quoter zero address not allowed\"\n );\n require(_poolLens != address(0), \"PoolLens zero address not allowed\");\n require(\n _maverickPosition != address(0),\n \"Position zero address not allowed\"\n );\n require(\n _votingDistributor != address(0),\n \"Voting distributor zero address not allowed\"\n );\n require(\n _poolDistributor != address(0),\n \"Pool distributor zero address not allowed\"\n );\n\n uint256 _tickSpacing = IMaverickV2Pool(_mPool).tickSpacing();\n require(_tickSpacing == 1, \"Unsupported tickSpacing\");\n\n // tickSpacing == 1\n (sqrtPriceTickLower, sqrtPriceTickHigher) = TickMath.tickSqrtPrices(\n _tickSpacing,\n TICK_NUMBER\n );\n sqrtPriceAtParity = _upperTickAtParity\n ? sqrtPriceTickHigher\n : sqrtPriceTickLower;\n\n WETH = _wethAddress;\n OETH = _oethAddress;\n liquidityManager = IMaverickV2LiquidityManager(_liquidityManager);\n poolLens = IMaverickV2PoolLens(_poolLens);\n maverickPosition = IMaverickV2Position(_maverickPosition);\n quoter = IMaverickV2Quoter(_maverickQuoter);\n mPool = IMaverickV2Pool(_mPool);\n votingDistributor = IVotingDistributor(_votingDistributor);\n poolDistributor = IPoolDistributor(_poolDistributor);\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /**\n * @notice initialize function, to set up initial internal state\n */\n function initialize() external onlyGovernor initializer {\n // Read reward\n address[] memory _rewardTokens = new address[](1);\n _rewardTokens[0] = poolDistributor.rewardToken();\n\n require(_rewardTokens[0] != address(0), \"No reward token configured\");\n\n InitializableAbstractStrategy._initialize(\n _rewardTokens,\n new address[](0),\n new address[](0)\n );\n }\n\n /***************************************\n Configuration \n ****************************************/\n\n /**\n * @notice Set allowed pool weth share interval. After the rebalance happens\n * the share of WETH token in the ticker needs to be within the specifications\n * of the interval.\n *\n * @param _allowedWethShareStart Start of WETH share interval expressed as 18 decimal amount\n * @param _allowedWethShareEnd End of WETH share interval expressed as 18 decimal amount\n */\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external onlyGovernor {\n require(\n _allowedWethShareStart < _allowedWethShareEnd,\n \"Invalid interval\"\n );\n // can not go below 1% weth share\n require(_allowedWethShareStart > 0.01 ether, \"Invalid interval start\");\n // can not go above 95% weth share\n require(_allowedWethShareEnd < 0.95 ether, \"Invalid interval end\");\n\n allowedWethShareStart = _allowedWethShareStart;\n allowedWethShareEnd = _allowedWethShareEnd;\n emit PoolWethShareIntervalUpdated(\n _allowedWethShareStart,\n _allowedWethShareEnd\n );\n }\n\n /***************************************\n Strategy overrides \n ****************************************/\n\n /**\n * @notice Deposits funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposits all the funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n _deposit(WETH, _wethBalance);\n }\n\n /**\n * @dev Deposits funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n * Before this function can be called the initial pool position needs to already\n * be minted.\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n require(tokenId > 0, \"Initial position not minted\");\n emit Deposit(_asset, address(0), _amount);\n\n // if the pool price is not within the expected interval leave the WETH on the contract\n // as to not break the mints - in case it would be configured as a default asset strategy\n (bool _isExpectedRange, ) = _checkForExpectedPoolPrice(false);\n if (_isExpectedRange) {\n // deposit funds into the underlying pool. Because no swap is performed there is no\n // need to remove any of the liquidity beforehand.\n _rebalance(0, false, 0, 0);\n }\n }\n\n /**\n * @notice Withdraw an `amount` of WETH from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset WETH address\n * @param _amount Amount of WETH to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n _ensureWETHBalance(_amount);\n\n _withdraw(_recipient, _amount);\n }\n\n /**\n * @notice Withdraw WETH and sends it to the Vault.\n */\n function withdrawAll() external override onlyVault nonReentrant {\n if (tokenId != 0) {\n _removeLiquidity(1e18);\n }\n\n uint256 _balance = IERC20(WETH).balanceOf(address(this));\n if (_balance > 0) {\n _withdraw(vaultAddress, _balance);\n }\n }\n\n function _withdraw(address _recipient, uint256 _amount) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n * @return bool True when the _asset is WETH\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /**\n * @dev Approve the spending amounts for the assets\n */\n function _approveTokenAmounts(\n uint256 _wethAllowance,\n uint256 _oethAllowance\n ) internal {\n IERC20(WETH).approve(address(liquidityManager), _wethAllowance);\n IERC20(OETH).approve(address(liquidityManager), _oethAllowance);\n }\n\n /***************************************\n Liquidity management\n ****************************************/\n /**\n * @dev Add liquidity into the pool in the pre-configured WETH to OETH share ratios\n * defined by the allowedPoolWethShareStart|End interval.\n *\n * Normally a PoolLens contract is used to prepare the parameters to add liquidity to the\n * Rooster pools. It has some errors when doing those calculation and for that reason a\n * much more accurate Quoter contract is used. This is possible due to our requirement of\n * adding liquidity only to one tick - PoolLens supports adding liquidity into multiple ticks\n * using different distribution ratios.\n */\n function _addLiquidity() internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n // don't deposit small liquidity amounts\n if (_wethBalance <= ACTION_THRESHOLD) {\n return;\n }\n\n (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n ) = _getAddLiquidityParams(_wethBalance, 1e30);\n\n if (OETHRequired > _oethBalance) {\n IVault(vaultAddress).mintForStrategy(OETHRequired - _oethBalance);\n }\n\n _approveTokenAmounts(WETHRequired, OETHRequired);\n\n (\n uint256 _wethAmount,\n uint256 _oethAmount,\n uint32[] memory binIds\n ) = liquidityManager.addPositionLiquidityToSenderByTokenIndex(\n mPool,\n 0, // NFT token index\n packedSqrtPriceBreaks,\n packedArgs\n );\n\n require(binIds.length == 1, \"Unexpected binIds length\");\n\n // burn remaining OETH\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n\n // needs to be called after _updateUnderlyingAssets so the updated amount\n // is reflected in the event\n emit LiquidityAdded(\n _wethBalance, // wethAmountDesired\n OETHRequired, // oethAmountDesired\n _wethAmount, // wethAmountSupplied\n _oethAmount, // oethAmountSupplied\n tokenId, // tokenId\n underlyingAssets\n );\n }\n\n /**\n * @dev The function creates liquidity parameters required to be able to add liquidity to the pool.\n * The function needs to handle the 3 different cases of the way liquidity is added:\n * - only WETH present in the tick\n * - only OETH present in the tick\n * - both tokens present in the tick\n *\n */\n function _getAddLiquidityParams(uint256 _maxWETH, uint256 _maxOETH)\n internal\n returns (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n )\n {\n IMaverickV2Pool.AddLiquidityParams[]\n memory addParams = new IMaverickV2Pool.AddLiquidityParams[](1);\n int32[] memory ticks = new int32[](1);\n uint128[] memory amounts = new uint128[](1);\n ticks[0] = TICK_NUMBER;\n // arbitrary LP amount\n amounts[0] = 1e24;\n\n // construct value for Quoter with arbitrary LP amount\n IMaverickV2Pool.AddLiquidityParams memory addParam = IMaverickV2Pool\n .AddLiquidityParams({\n kind: MAV_STATIC_BIN_KIND,\n ticks: ticks,\n amounts: amounts\n });\n\n // get the WETH and OETH required to get the proportion of tokens required\n // given the arbitrary liquidity\n (WETHRequired, OETHRequired, ) = quoter.calculateAddLiquidity(\n mPool,\n addParam\n );\n\n /**\n * If either token required is 0 then the tick consists only of the other token. In that\n * case the liquidity calculations need to be done using the non 0 token. By setting the\n * tokenRequired from 0 to 1 the `min` in next step will ignore that (the bigger) value.\n */\n WETHRequired = WETHRequired == 0 ? 1 : WETHRequired;\n OETHRequired = OETHRequired == 0 ? 1 : OETHRequired;\n\n addParam.amounts[0] = Math_v5\n .min(\n ((_maxWETH - 1) * 1e24) / WETHRequired,\n ((_maxOETH - 1) * 1e24) / OETHRequired\n )\n .toUint128();\n\n // update the quotes with the actual amounts\n (WETHRequired, OETHRequired, ) = quoter.calculateAddLiquidity(\n mPool,\n addParam\n );\n\n require(_maxWETH >= WETHRequired, \"More WETH required than specified\");\n require(_maxOETH >= OETHRequired, \"More OETH required than specified\");\n\n // organize values to be used by manager\n addParams[0] = addParam;\n packedArgs = liquidityManager.packAddLiquidityArgsArray(addParams);\n // price can stay 0 if array only has one element\n packedSqrtPriceBreaks = liquidityManager.packUint88Array(\n new uint88[](1)\n );\n }\n\n /**\n * @dev Check that the Rooster pool price is within the expected\n * parameters.\n * This function works whether the strategy contract has liquidity\n * position in the pool or not. The function returns _wethSharePct\n * as a gas optimization measure.\n * @param _throwException when set to true the function throws an exception\n * when pool's price is not within expected range.\n * @return _isExpectedRange Bool expressing price is within expected range\n * @return _wethSharePct Share of WETH owned by this strategy contract in the\n * configured ticker.\n */\n function _checkForExpectedPoolPrice(bool _throwException)\n internal\n view\n returns (bool _isExpectedRange, uint256 _wethSharePct)\n {\n require(\n allowedWethShareStart != 0 && allowedWethShareEnd != 0,\n \"Weth share interval not set\"\n );\n\n uint256 _currentPrice = getPoolSqrtPrice();\n\n /**\n * First check pool price is in expected tick range\n *\n * A revert is issued even though price being equal to the lower bound as that can not\n * be within the approved tick range.\n */\n if (\n _currentPrice <= sqrtPriceTickLower ||\n _currentPrice >= sqrtPriceTickHigher\n ) {\n if (_throwException) {\n revert OutsideExpectedTickRange();\n }\n\n return (false, _currentPrice <= sqrtPriceTickLower ? 0 : 1e18);\n }\n\n // 18 decimal number expressed WETH tick share\n _wethSharePct = _getWethShare(_currentPrice);\n\n if (\n _wethSharePct < allowedWethShareStart ||\n _wethSharePct > allowedWethShareEnd\n ) {\n if (_throwException) {\n revert PoolRebalanceOutOfBounds(\n _wethSharePct,\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n return (false, _wethSharePct);\n }\n\n return (true, _wethSharePct);\n }\n\n /**\n * @notice Rebalance the pool to the desired token split and Deposit any WETH on the contract to the\n * underlying rooster pool. Print the required amount of corresponding OETH. After the rebalancing is\n * done burn any potentially remaining OETH tokens still on the strategy contract.\n *\n * This function has a slightly different behaviour depending on the status of the underlying Rooster\n * pool. The function consists of the following 3 steps:\n * 1. withdrawLiquidityOption -> this is a configurable option where either only part of the liquidity\n * necessary for the swap is removed, or all of it. This way the rebalance\n * is able to optimize for volume, for efficiency or anything in between\n * 2. swapToDesiredPosition -> move active trading price in the pool to be able to deposit WETH & OETH\n * tokens with the desired pre-configured ratios\n * 3. addLiquidity -> add liquidity into the pool respecting ratio split configuration\n *\n *\n * Exact _amountToSwap, _swapWeth & _minTokenReceived parameters shall be determined by simulating the\n * transaction off-chain. The strategy checks that after the swap the share of the tokens is in the\n * expected ranges.\n *\n * @param _amountToSwap The amount of the token to swap\n * @param _swapWeth Swap using WETH when true, use OETH when false\n * @param _minTokenReceived Slippage check -> minimum amount of token expected in return\n * @param _liquidityToRemovePct Percentage of liquidity to remove -> the percentage amount of liquidity to\n * remove before performing the swap. 1e18 denominated\n */\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived,\n uint256 _liquidityToRemovePct\n ) external nonReentrant onlyGovernorOrStrategist {\n _rebalance(\n _amountToSwap,\n _swapWeth,\n _minTokenReceived,\n _liquidityToRemovePct\n );\n }\n\n // slither-disable-start reentrancy-no-eth\n function _rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived,\n uint256 _liquidityToRemovePct\n ) internal {\n // Remove the required amount of liquidity\n if (_liquidityToRemovePct > 0) {\n _removeLiquidity(_liquidityToRemovePct);\n }\n\n // in some cases (e.g. deposits) we will just want to add liquidity and not\n // issue a swap to move the active trading position within the pool. Before or after a\n // deposit or as a standalone call the strategist might issue a rebalance to move the\n // active trading price to a more desired position.\n if (_amountToSwap > 0) {\n // In case liquidity has been removed and there is still not enough WETH owned by the\n // strategy contract remove additional required amount of WETH.\n if (_swapWeth) _ensureWETHBalance(_amountToSwap);\n\n _swapToDesiredPosition(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n\n // calling check liquidity early so we don't get unexpected errors when adding liquidity\n // in the later stages of this function\n _checkForExpectedPoolPrice(true);\n\n _addLiquidity();\n\n // this call shouldn't be necessary, since adding liquidity shouldn't affect the active\n // trading price. It is a defensive programming measure.\n (, uint256 _wethSharePct) = _checkForExpectedPoolPrice(true);\n\n // revert if protocol insolvent\n _solvencyAssert();\n\n emit PoolRebalanced(_wethSharePct);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @dev Perform a swap so that after the swap the tick has the desired WETH to OETH token share.\n */\n function _swapToDesiredPosition(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n IERC20 _tokenToSwap = IERC20(_swapWeth ? WETH : OETH);\n uint256 _balance = _tokenToSwap.balanceOf(address(this));\n\n if (_balance < _amountToSwap) {\n // This should never trigger since _ensureWETHBalance will already\n // throw an error if there is not enough WETH\n if (_swapWeth) {\n revert NotEnoughWethForSwap(_balance, _amountToSwap);\n }\n // if swapping OETH\n uint256 mintForSwap = _amountToSwap - _balance;\n IVault(vaultAddress).mintForStrategy(mintForSwap);\n }\n\n // SafeERC20 is used for IERC20 transfers. Not sure why slither complains\n // slither-disable-next-line unchecked-transfer\n _tokenToSwap.transfer(address(mPool), _amountToSwap);\n\n // tickLimit: the furthest tick a swap will execute in. If no limit is desired,\n // value should be set to type(int32).max for a tokenAIn (WETH) swap\n // and type(int32).min for a swap where tokenB (OETH) is the input\n\n IMaverickV2Pool.SwapParams memory swapParams = IMaverickV2Pool\n // exactOutput defines whether the amount specified is the output\n // or the input amount of the swap\n .SwapParams({\n amount: _amountToSwap,\n tokenAIn: _swapWeth,\n exactOutput: false,\n tickLimit: TICK_NUMBER\n });\n\n // swaps without a callback as the assets are already sent to the pool\n (, uint256 amountOut) = mPool.swap(\n address(this),\n swapParams,\n bytes(\"\")\n );\n\n /**\n * There could be additional checks here for validating minTokenReceived is within the\n * expected range (e.g. 99% - 101% of the token sent in). Though that doesn't provide\n * any additional security. After the swap the `_checkForExpectedPoolPrice` validates\n * that the swap has moved the price into the expected tick (# -1).\n *\n * If the guardian forgets to set a `_minTokenReceived` and a sandwich attack bends\n * the pool before the swap the `_checkForExpectedPoolPrice` will fail the transaction.\n *\n * A check would not prevent a compromised guardian from stealing funds as multiple\n * transactions each loosing smaller amount of funds are still possible.\n */\n if (amountOut < _minTokenReceived) {\n revert SlippageCheck(amountOut);\n }\n\n /**\n * In the interest of each function in `_rebalance` to leave the contract state as\n * clean as possible the OETH tokens here are burned. This decreases the\n * dependence where `_swapToDesiredPosition` function relies on later functions\n * (`addLiquidity`) to burn the OETH. Reducing the risk of error introduction.\n */\n _burnOethOnTheContract();\n }\n\n /**\n * @dev This function removes the appropriate amount of liquidity to ensure that the required\n * amount of WETH is available on the contract\n *\n * @param _amount WETH balance required on the contract\n */\n function _ensureWETHBalance(uint256 _amount) internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance >= _amount) {\n return;\n }\n\n require(tokenId != 0, \"No liquidity available\");\n uint256 _additionalWethRequired = _amount - _wethBalance;\n (uint256 _wethInThePool, ) = getPositionPrincipal();\n\n if (_wethInThePool < _additionalWethRequired) {\n revert NotEnoughWethLiquidity(\n _wethInThePool,\n _additionalWethRequired\n );\n }\n\n uint256 shareOfWethToRemove = _wethInThePool <= 1\n ? 1e18\n : Math_v5.min(\n /**\n * When dealing with shares of liquidity to remove there is always some\n * rounding involved. After extensive fuzz testing the below approach\n * yielded the best results where the strategy overdraws the least and\n * never removes insufficient amount of WETH.\n */\n (_additionalWethRequired + 2).divPrecisely(_wethInThePool - 1) +\n 2,\n 1e18\n );\n\n _removeLiquidity(shareOfWethToRemove);\n }\n\n /**\n * @dev Decrease partial or all liquidity from the pool.\n * @param _liquidityToDecrease The amount of liquidity to remove denominated in 1e18\n */\n function _removeLiquidity(uint256 _liquidityToDecrease) internal {\n require(_liquidityToDecrease > 0, \"Must remove some liquidity\");\n require(\n _liquidityToDecrease <= 1e18,\n \"Can not remove more than 100% of liquidity\"\n );\n\n // 0 indicates the first (and only) bin in the NFT LP position.\n IMaverickV2Pool.RemoveLiquidityParams memory params = maverickPosition\n .getRemoveParams(tokenId, 0, _liquidityToDecrease);\n (uint256 _amountWeth, uint256 _amountOeth) = maverickPosition\n .removeLiquidityToSender(tokenId, mPool, params);\n\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n\n // needs to be called after the _updateUnderlyingAssets so the updated amount is reflected\n // in the event\n emit LiquidityRemoved(\n _liquidityToDecrease,\n _amountWeth,\n _amountOeth,\n underlyingAssets\n );\n }\n\n /**\n * @dev Burns any OETH tokens remaining on the strategy contract if the balance is\n * above the action threshold.\n */\n function _burnOethOnTheContract() internal {\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(_oethBalance);\n }\n\n /**\n * @notice Returns the percentage of WETH liquidity in the configured ticker\n * owned by this strategy contract.\n * @return uint256 1e18 denominated percentage expressing the share\n */\n function getWETHShare() external view returns (uint256) {\n uint256 _currentPrice = getPoolSqrtPrice();\n return _getWethShare(_currentPrice);\n }\n\n /**\n * @dev Returns the share of WETH in tick denominated in 1e18\n */\n function _getWethShare(uint256 _currentPrice)\n internal\n view\n returns (uint256)\n {\n (\n uint256 wethAmount,\n uint256 oethAmount\n ) = _reservesInTickForGivenPriceAndLiquidity(\n sqrtPriceTickLower,\n sqrtPriceTickHigher,\n _currentPrice,\n 1e24\n );\n\n return wethAmount.divPrecisely(wethAmount + oethAmount);\n }\n\n /**\n * @notice Returns the current pool price in square root\n * @return Square root of the pool price\n */\n function getPoolSqrtPrice() public view returns (uint256) {\n return poolLens.getPoolSqrtPrice(mPool);\n }\n\n /**\n * @notice Returns the current active trading tick of the underlying pool\n * @return _currentTick Current pool trading tick\n */\n function getCurrentTradingTick() public view returns (int32 _currentTick) {\n _currentTick = mPool.getState().activeTick;\n }\n\n /**\n * @notice Mint the initial NFT position\n *\n * @dev This amount is \"gifted\" to the strategy contract and will count as a yield\n * surplus.\n */\n // slither-disable-start reentrancy-no-eth\n function mintInitialPosition() external onlyGovernor nonReentrant {\n require(tokenId == 0, \"Initial position already minted\");\n (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n ) = _getAddLiquidityParams(1e16, 1e16);\n\n // Mint rounded up OETH amount\n if (OETHRequired > 0) {\n IVault(vaultAddress).mintForStrategy(OETHRequired);\n }\n\n _approveTokenAmounts(WETHRequired, OETHRequired);\n\n // Store the tokenId before calling updateUnderlyingAssets as it relies on the tokenId\n // not being 0\n (, , , tokenId) = liquidityManager.mintPositionNftToSender(\n mPool,\n packedSqrtPriceBreaks,\n packedArgs\n );\n\n // burn remaining OETH\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @notice Returns the balance of tokens the strategy holds in the LP position\n * @return _amountWeth Amount of WETH in position\n * @return _amountOeth Amount of OETH in position\n */\n function getPositionPrincipal()\n public\n view\n returns (uint256 _amountWeth, uint256 _amountOeth)\n {\n if (tokenId == 0) {\n return (0, 0);\n }\n\n (_amountWeth, _amountOeth, ) = _getPositionInformation();\n }\n\n /**\n * @dev Returns the balance of tokens the strategy holds in the LP position\n * @return _amountWeth Amount of WETH in position\n * @return _amountOeth Amount of OETH in position\n * @return liquidity Amount of liquidity in the position\n */\n function _getPositionInformation()\n internal\n view\n returns (\n uint256 _amountWeth,\n uint256 _amountOeth,\n uint256 liquidity\n )\n {\n IMaverickV2Position.PositionFullInformation\n memory positionInfo = maverickPosition.tokenIdPositionInformation(\n tokenId,\n 0\n );\n\n require(\n positionInfo.liquidities.length == 1,\n \"Unexpected liquidities length\"\n );\n require(positionInfo.ticks.length == 1, \"Unexpected ticks length\");\n\n _amountWeth = positionInfo.amountA;\n _amountOeth = positionInfo.amountB;\n liquidity = positionInfo.liquidities[0];\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99.8%) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethSupply = IERC20(OETH).totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethSupply) < SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /**\n * @dev Collect Rooster reward token, and send it to the harvesterAddress\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Do nothing if there's no position minted\n if (tokenId > 0) {\n uint32[] memory binIds = new uint32[](1);\n IMaverickV2Pool.TickState memory tickState = mPool.getTick(\n TICK_NUMBER\n );\n // get the binId for the MAV_STATIC_BIN_KIND in tick TICK_NUMBER (-1)\n binIds[0] = tickState.binIdsByTick[0];\n\n uint256 lastEpoch = votingDistributor.lastEpoch();\n\n poolDistributor.claimLp(\n address(this),\n tokenId,\n mPool,\n binIds,\n lastEpoch\n );\n }\n\n // Run the internal inherited function\n _collectRewardTokens();\n }\n\n /***************************************\n Balances and Fees\n ****************************************/\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256)\n {\n require(_asset == WETH, \"Only WETH supported\");\n\n // because of PoolLens inaccuracy there is usually some dust WETH left on the contract\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n // just paranoia check, in case there is OETH in the strategy that for some reason hasn't\n // been burned yet. This should always be 0.\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n return underlyingAssets + _wethBalance + _oethBalance;\n }\n\n /// @dev This function updates the amount of underlying assets with the approach of the least possible\n /// total tokens extracted for the current liquidity in the pool.\n function _updateUnderlyingAssets() internal {\n /**\n * Our net value represent the smallest amount of tokens we are able to extract from the position\n * given our liquidity.\n *\n * The least amount of tokens ex-tractable from the position is where the active trading price is\n * at the edge between tick -1 & tick 0. There the pool is offering 1:1 trades between WETH & OETH.\n * At that moment the pool consists completely of WETH and no OETH.\n *\n * The more swaps from OETH -> WETH happen on the pool the more the price starts to move away from the tick 0\n * towards the middle of tick -1 making OETH (priced in WETH) cheaper.\n */\n\n uint256 _wethAmount = tokenId == 0 ? 0 : _balanceInPosition();\n\n underlyingAssets = _wethAmount;\n emit UnderlyingAssetsUpdated(_wethAmount);\n }\n\n /**\n * @dev Strategy reserves (which consist only of WETH in case of Rooster - Plume pool)\n * when the tick price is closest to parity - assuring the lowest amount of tokens\n * returned for the current position liquidity.\n */\n function _balanceInPosition() internal view returns (uint256 _wethBalance) {\n (, , uint256 liquidity) = _getPositionInformation();\n\n uint256 _oethBalance;\n\n (_wethBalance, _oethBalance) = _reservesInTickForGivenPriceAndLiquidity(\n sqrtPriceTickLower,\n sqrtPriceTickHigher,\n sqrtPriceAtParity,\n liquidity\n );\n\n require(_oethBalance == 0, \"Non zero oethBalance\");\n }\n\n /**\n * @notice Tick dominance denominated in 1e18\n * @return _tickDominance The share of liquidity in TICK_NUMBER tick owned\n * by the strategy contract denominated in 1e18\n */\n function tickDominance() public view returns (uint256 _tickDominance) {\n IMaverickV2Pool.TickState memory tickState = mPool.getTick(TICK_NUMBER);\n\n uint256 wethReserve = tickState.reserveA;\n uint256 oethReserve = tickState.reserveB;\n\n // prettier-ignore\n (uint256 _amountWeth, uint256 _amountOeth, ) = _getPositionInformation();\n\n if (wethReserve + oethReserve == 0) {\n return 0;\n }\n\n _tickDominance = (_amountWeth + _amountOeth).divPrecisely(\n wethReserve + oethReserve\n );\n }\n\n /***************************************\n Hidden functions\n ****************************************/\n /**\n * @dev Unsupported\n */\n function setPTokenAddress(address, address) external pure override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function removePToken(uint256) external pure override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function _abstractSetPToken(address, address) internal pure override {\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function safeApproveAllTokens() external pure override {\n // all the amounts are approved at the time required\n revert(\"Unsupported method\");\n }\n\n /***************************************\n Maverick liquidity utilities\n ****************************************/\n\n /// @notice Calculates deltaA = liquidity * (sqrt(upper) - sqrt(lower))\n /// Calculates deltaB = liquidity / sqrt(lower) - liquidity / sqrt(upper),\n /// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower))\n ///\n /// @dev refactored from here:\n // solhint-disable-next-line max-line-length\n /// https://github.com/rooster-protocol/rooster-contracts/blob/main/v2-supplemental/contracts/libraries/LiquidityUtilities.sol#L665-L695\n function _reservesInTickForGivenPriceAndLiquidity(\n uint256 _lowerSqrtPrice,\n uint256 _upperSqrtPrice,\n uint256 _newSqrtPrice,\n uint256 _liquidity\n ) internal pure returns (uint128 reserveA, uint128 reserveB) {\n if (_liquidity == 0) {\n (reserveA, reserveB) = (0, 0);\n } else {\n uint256 lowerEdge = MathRooster.max(_lowerSqrtPrice, _newSqrtPrice);\n\n reserveA = MathRooster\n .mulCeil(\n _liquidity,\n MathRooster.clip(\n MathRooster.min(_upperSqrtPrice, _newSqrtPrice),\n _lowerSqrtPrice\n )\n )\n .toUint128();\n reserveB = MathRooster\n .mulDivCeil(\n _liquidity,\n 1e18 * MathRooster.clip(_upperSqrtPrice, lowerEdge),\n _upperSqrtPrice * lowerEdge\n )\n .toUint128();\n }\n }\n}\n" + }, + "contracts/strategies/sonic/SonicStakingStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SonicValidatorDelegator } from \"./SonicValidatorDelegator.sol\";\nimport { IWrappedSonic } from \"../../interfaces/sonic/IWrappedSonic.sol\";\n\n/**\n * @title Staking Strategy for Sonic's native S currency\n * @author Origin Protocol Inc\n */\ncontract SonicStakingStrategy is SonicValidatorDelegator {\n // For future use\n uint256[50] private __gap;\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wrappedSonic,\n address _sfc\n ) SonicValidatorDelegator(_baseConfig, _wrappedSonic, _sfc) {}\n\n /// @notice Deposit wrapped S asset into the underlying platform.\n /// @param _asset Address of asset to deposit. Has to be Wrapped Sonic (wS).\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposit Wrapped Sonic (wS) to this strategy and delegate to a validator.\n * @param _asset Address of Wrapped Sonic (wS) token\n * @param _amount Amount of Wrapped Sonic (wS) to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal virtual {\n require(_amount > 0, \"Must deposit something\");\n\n _delegate(_amount);\n emit Deposit(_asset, address(0), _amount);\n }\n\n /**\n * @notice Deposit the entire balance of wrapped S in this strategy contract into\n * the underlying platform.\n */\n function depositAll() external virtual override onlyVault nonReentrant {\n uint256 wSBalance = IERC20(wrappedSonic).balanceOf(address(this));\n\n if (wSBalance > 0) {\n _deposit(wrappedSonic, wSBalance);\n }\n }\n\n /// @notice Withdraw Wrapped Sonic (wS) from this strategy contract.\n /// Used only if some wS is lingering on the contract.\n /// That can happen only when someone sends wS directly to this contract\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset Address of the Wrapped Sonic (wS) token\n /// @param _amount Amount of Wrapped Sonic (wS) to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal override {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(_asset).transfer(_recipient, _amount);\n\n emit Withdrawal(wrappedSonic, address(0), _amount);\n }\n\n /// @notice Transfer all Wrapped Sonic (wS) deposits back to the vault.\n /// This does not withdraw from delegated validators. That has to be done separately with `undelegate`.\n /// Any native S in this strategy will be withdrawn.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 balance = address(this).balance;\n if (balance > 0) {\n IWrappedSonic(wrappedSonic).deposit{ value: balance }();\n }\n uint256 wSBalance = IERC20(wrappedSonic).balanceOf(address(this));\n if (wSBalance > 0) {\n _withdraw(vaultAddress, wrappedSonic, wSBalance);\n }\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset token\n */\n function supportsAsset(address _asset)\n public\n view\n virtual\n override\n returns (bool)\n {\n return _asset == wrappedSonic;\n }\n\n /**\n * @notice is not supported for this strategy as the\n * Wrapped Sonic (wS) token is set at deploy time.\n */\n function setPTokenAddress(address, address)\n external\n view\n override\n onlyGovernor\n {\n revert(\"unsupported function\");\n }\n\n /// @notice is not used by this strategy as all staking rewards are restaked\n function collectRewardTokens() external override nonReentrant {\n revert(\"unsupported function\");\n }\n\n /**\n * @notice is not supported for this strategy as the\n * Wrapped Sonic (wS) token is set at deploy time.\n */\n function removePToken(uint256) external view override onlyGovernor {\n revert(\"unsupported function\");\n }\n\n /// @dev is not used by this strategy but must be implemented as it's abstract\n /// in the inherited `InitializableAbstractStrategy` contract.\n function _abstractSetPToken(address, address) internal virtual override {}\n\n /// @notice is not used by this strategy\n function safeApproveAllTokens() external override onlyGovernor {}\n}\n" + }, + "contracts/strategies/sonic/SonicSwapXAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title SwapX Algorithmic Market Maker (AMO) Strategy\n * @notice AMO strategy for the SwapX OS/wS stable pool\n * @author Origin Protocol Inc\n */\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport { sqrt } from \"../../utils/PRBMath.sol\";\nimport { IBasicToken } from \"../../interfaces/IBasicToken.sol\";\nimport { IPair } from \"../../interfaces/sonic/ISwapXPair.sol\";\nimport { IGauge } from \"../../interfaces/sonic/ISwapXGauge.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\n\ncontract SonicSwapXAMOStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @notice a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n /// @notice Precision for the SwapX Stable AMM (sAMM) invariant k.\n uint256 public constant PRECISION = 1e18;\n\n /// @notice Address of the Wrapped S (wS) token.\n address public immutable ws;\n\n /// @notice Address of the OS token contract.\n address public immutable os;\n\n /// @notice Address of the SwapX Stable pool contract.\n address public immutable pool;\n\n /// @notice Address of the SwapX Gauge contract.\n address public immutable gauge;\n\n /// @notice The max amount the OS/wS price can deviate from peg (1e18)\n /// before deposits are reverted scaled to 18 decimals.\n /// eg 0.01e18 or 1e16 is 1% which is 100 basis points.\n /// This is the amount below and above peg so a 50 basis point deviation (0.005e18)\n /// allows a price range from 0.995 to 1.005.\n uint256 public maxDepeg;\n\n event SwapOTokensToPool(\n uint256 osMinted,\n uint256 wsDepositAmount,\n uint256 osDepositAmount,\n uint256 lpTokens\n );\n event SwapAssetsToPool(\n uint256 wsSwapped,\n uint256 lpTokens,\n uint256 osBurnt\n );\n event MaxDepegUpdated(uint256 maxDepeg);\n\n /**\n * @dev Verifies that the caller is the Strategist of the Vault.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Skim the SwapX pool in case any extra wS or OS tokens were added\n */\n modifier skimPool() {\n IPair(pool).skim(address(this));\n _;\n }\n\n /**\n * @dev Checks the pool is balanced enough to allow deposits.\n */\n modifier nearBalancedPool() {\n // OS/wS price = wS / OS\n // Get the OS/wS price for selling 1 OS for wS\n // As OS is 1, the wS amount is the OS/wS price\n uint256 sellPrice = IPair(pool).getAmountOut(1e18, os);\n\n // Get the amount of OS received from selling 1 wS. This is buying OS.\n uint256 osAmount = IPair(pool).getAmountOut(1e18, ws);\n // Convert to a OS/wS price = wS / OS\n uint256 buyPrice = 1e36 / osAmount;\n\n uint256 pegPrice = 1e18;\n\n require(\n sellPrice >= pegPrice - maxDepeg && buyPrice <= pegPrice + maxDepeg,\n \"price out of range\"\n );\n _;\n }\n\n /**\n * @dev Checks the pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do swaps against the pool.\n * Deposits and withdrawals are proportional to the pool's balances hence don't need this check.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the pool\n (uint256 wsReservesBefore, uint256 osReservesBefore, ) = IPair(pool)\n .getReserves();\n // diff = wS balance - OS balance\n int256 diffBefore = wsReservesBefore.toInt256() -\n osReservesBefore.toInt256();\n\n _;\n\n // Get the asset and OToken balances in the pool\n (uint256 wsReservesAfter, uint256 osReservesAfter, ) = IPair(pool)\n .getReserves();\n // diff = wS balance - OS balance\n int256 diffAfter = wsReservesAfter.toInt256() -\n osReservesAfter.toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OS, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"Assets overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of wS, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"OTokens overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n /**\n * @param _baseConfig The `platformAddress` is the address of the SwapX pool.\n * The `vaultAddress` is the address of the Origin Sonic Vault.\n * @param _os Address of the OS token.\n * @param _ws Address of the Wrapped S (wS) token.\n * @param _gauge Address of the SwapX gauge for the pool.\n */\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _os,\n address _ws,\n address _gauge\n ) InitializableAbstractStrategy(_baseConfig) {\n // Check the pool tokens are correct\n require(\n IPair(_baseConfig.platformAddress).token0() == _ws &&\n IPair(_baseConfig.platformAddress).token1() == _os,\n \"Incorrect pool tokens\"\n );\n // Checked both tokens are to 18 decimals\n require(\n IBasicToken(_ws).decimals() == 18 &&\n IBasicToken(_os).decimals() == 18,\n \"Incorrect token decimals\"\n );\n // Check the SwapX pool is a Stable AMM (sAMM)\n require(\n IPair(_baseConfig.platformAddress).isStable() == true,\n \"Pool not stable\"\n );\n // Check the gauge is for the pool\n require(\n IGauge(_gauge).TOKEN() == _baseConfig.platformAddress,\n \"Incorrect gauge\"\n );\n\n // Set the immutable variables\n os = _os;\n ws = _ws;\n pool = _baseConfig.platformAddress;\n gauge = _gauge;\n\n // This is an implementation contract. The governor is set in the proxy contract.\n _setGovernor(address(0));\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as SwapX strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Array containing SWPx token address\n * @param _maxDepeg The max amount the OS/wS price can deviate from peg (1e18) before deposits are reverted.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n uint256 _maxDepeg\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = pool;\n\n address[] memory _assets = new address[](1);\n _assets[0] = ws;\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n maxDepeg = _maxDepeg;\n\n _approveBase();\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit an amount of Wrapped S (wS) into the SwapX pool.\n * Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @dev This tx must be wrapped by the VaultValueChecker.\n * To minimize loses, the pool should be rebalanced before depositing.\n * The pool's OS/wS price must be within the maxDepeg range.\n * @param _asset Address of Wrapped S (wS) token.\n * @param _wsAmount Amount of Wrapped S (wS) tokens to deposit.\n */\n function deposit(address _asset, uint256 _wsAmount)\n external\n override\n onlyVault\n nonReentrant\n skimPool\n nearBalancedPool\n {\n require(_asset == ws, \"Unsupported asset\");\n require(_wsAmount > 0, \"Must deposit something\");\n\n (uint256 osDepositAmount, ) = _deposit(_wsAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the deposited wS tokens\n emit Deposit(ws, pool, _wsAmount);\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osDepositAmount);\n }\n\n /**\n * @notice Deposit all the strategy's Wrapped S (wS) tokens into the SwapX pool.\n * Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @dev This tx must be wrapped by the VaultValueChecker.\n * To minimize loses, the pool should be rebalanced before depositing.\n * The pool's OS/wS price must be within the maxDepeg range.\n */\n function depositAll()\n external\n override\n onlyVault\n nonReentrant\n skimPool\n nearBalancedPool\n {\n uint256 wsBalance = IERC20(ws).balanceOf(address(this));\n if (wsBalance > 0) {\n (uint256 osDepositAmount, ) = _deposit(wsBalance);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the deposited wS tokens\n emit Deposit(ws, pool, wsBalance);\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osDepositAmount);\n }\n }\n\n /**\n * @dev Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @param _wsAmount Amount of Wrapped S (wS) tokens to deposit.\n * @return osDepositAmount Amount of OS tokens minted and deposited into the pool.\n * @return lpTokens Amount of SwapX pool LP tokens minted and deposited into the gauge.\n */\n function _deposit(uint256 _wsAmount)\n internal\n returns (uint256 osDepositAmount, uint256 lpTokens)\n {\n // Calculate the required amount of OS to mint based on the wS amount.\n osDepositAmount = _calcTokensToMint(_wsAmount);\n\n // Mint the required OS tokens to this strategy\n IVault(vaultAddress).mintForStrategy(osDepositAmount);\n\n // Add wS and OS liquidity to the pool and stake in gauge\n lpTokens = _depositToPoolAndGauge(_wsAmount, osDepositAmount);\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw wS and OS from the SwapX pool, burn the OS,\n * and transfer the wS to the recipient.\n * @param _recipient Address of the Vault.\n * @param _asset Address of the Wrapped S (wS) contract.\n * @param _wsAmount Amount of Wrapped S (wS) to withdraw.\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _wsAmount\n ) external override onlyVault nonReentrant skimPool {\n require(_wsAmount > 0, \"Must withdraw something\");\n require(_asset == ws, \"Unsupported asset\");\n // This strategy can't be set as a default strategy for wS in the Vault.\n // This means the recipient must always be the Vault.\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n // Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n uint256 lpTokens = _calcTokensToBurn(_wsAmount);\n\n // Withdraw pool LP tokens from the gauge and remove assets from from the pool\n _withdrawFromGaugeAndPool(lpTokens);\n\n // Burn all the removed OS and any that was left in the strategy\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Transfer wS to the recipient\n // Note there can be a dust amount of wS left in the strategy as\n // the burn of the pool's LP tokens is rounded up\n require(\n IERC20(ws).balanceOf(address(this)) >= _wsAmount,\n \"Not enough wS removed from pool\"\n );\n IERC20(ws).safeTransfer(_recipient, _wsAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the withdrawn wS tokens\n emit Withdrawal(ws, pool, _wsAmount);\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n }\n\n /**\n * @notice Withdraw all pool LP tokens from the gauge,\n * remove all wS and OS from the SwapX pool,\n * burn all the OS tokens,\n * and transfer all the wS to the Vault contract.\n * @dev There is no solvency check here as withdrawAll can be called to\n * quickly secure assets to the Vault in emergencies.\n */\n function withdrawAll()\n external\n override\n onlyVaultOrGovernor\n nonReentrant\n skimPool\n {\n // Get all the pool LP tokens the strategy has staked in the gauge\n uint256 lpTokens = IGauge(gauge).balanceOf(address(this));\n // Can not withdraw zero LP tokens from the gauge\n if (lpTokens == 0) return;\n\n if (IGauge(gauge).emergency()) {\n // The gauge is in emergency mode\n _emergencyWithdrawFromGaugeAndPool();\n } else {\n // Withdraw pool LP tokens from the gauge and remove assets from from the pool\n _withdrawFromGaugeAndPool(lpTokens);\n }\n\n // Burn all OS in this strategy contract\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Get the strategy contract's wS balance.\n // This includes all that was removed from the SwapX pool and\n // any that was sitting in the strategy contract before the removal.\n uint256 wsBalance = IERC20(ws).balanceOf(address(this));\n IERC20(ws).safeTransfer(vaultAddress, wsBalance);\n\n // Emit event for the withdrawn wS tokens\n emit Withdrawal(ws, pool, wsBalance);\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n }\n\n /***************************************\n Pool Rebalancing\n ****************************************/\n\n /** @notice Used when there is more OS than wS in the pool.\n * wS and OS is removed from the pool, the received wS is swapped for OS\n * and the left over OS in the strategy is burnt.\n * The OS/wS price is < 1.0 so OS is being bought at a discount.\n * @param _wsAmount Amount of Wrapped S (wS) to swap into the pool.\n */\n function swapAssetsToPool(uint256 _wsAmount)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n skimPool\n {\n require(_wsAmount > 0, \"Must swap something\");\n\n // 1. Partially remove liquidity so there’s enough wS for the swap\n\n // Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n uint256 lpTokens = _calcTokensToBurn(_wsAmount);\n require(lpTokens > 0, \"No LP tokens to burn\");\n\n _withdrawFromGaugeAndPool(lpTokens);\n\n // 2. Swap wS for OS against the pool\n // Swap exact amount of wS for OS against the pool\n // There can be a dust amount of wS left in the strategy as the burn of the pool's LP tokens is rounded up\n _swapExactTokensForTokens(_wsAmount, ws, os);\n\n // 3. Burn all the OS left in the strategy from the remove liquidity and swap\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n // Emit event for the swap\n emit SwapAssetsToPool(_wsAmount, lpTokens, osToBurn);\n }\n\n /**\n * @notice Used when there is more wS than OS in the pool.\n * OS is minted and swapped for wS against the pool,\n * more OS is minted and added back into the pool with the swapped out wS.\n * The OS/wS price is > 1.0 so OS is being sold at a premium.\n * @param _osAmount Amount of OS to swap into the pool.\n */\n function swapOTokensToPool(uint256 _osAmount)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n skimPool\n {\n require(_osAmount > 0, \"Must swap something\");\n\n // 1. Mint OS so it can be swapped into the pool\n\n // There can be OS in the strategy from skimming the pool\n uint256 osInStrategy = IERC20(os).balanceOf(address(this));\n require(_osAmount >= osInStrategy, \"Too much OS in strategy\");\n uint256 osToMint = _osAmount - osInStrategy;\n\n // Mint the required OS tokens to this strategy\n IVault(vaultAddress).mintForStrategy(osToMint);\n\n // 2. Swap OS for wS against the pool\n _swapExactTokensForTokens(_osAmount, os, ws);\n\n // The wS is from the swap and any wS that was sitting in the strategy\n uint256 wsDepositAmount = IERC20(ws).balanceOf(address(this));\n\n // 3. Add wS and OS back to the pool in proportion to the pool's reserves\n (uint256 osDepositAmount, uint256 lpTokens) = _deposit(wsDepositAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osToMint + osDepositAmount);\n // Emit event for the swap\n emit SwapOTokensToPool(\n osToMint,\n wsDepositAmount,\n osDepositAmount,\n lpTokens\n );\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Get the wS value of assets in the strategy and SwapX pool.\n * The value of the assets in the pool is calculated assuming the pool is balanced.\n * This way the value can not be manipulated by changing the pool's token balances.\n * @param _asset Address of the Wrapped S (wS) token\n * @return balance Total value in wS.\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == ws, \"Unsupported asset\");\n\n // wS balance needed here for the balance check that happens from vault during depositing.\n balance = IERC20(ws).balanceOf(address(this));\n\n // This assumes 1 gauge LP token = 1 pool LP token\n uint256 lpTokens = IGauge(gauge).balanceOf(address(this));\n if (lpTokens == 0) return balance;\n\n // Add the strategy’s share of the wS and OS tokens in the SwapX pool if the pool was balanced.\n balance += _lpValue(lpTokens);\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == ws;\n }\n\n /**\n * @notice Collect accumulated SWPx (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect SWPx rewards from the gauge\n IGauge(gauge).getReward();\n\n _collectRewardTokens();\n }\n\n /***************************************\n Internal SwapX Pool and Gauge Functions\n ****************************************/\n\n /**\n * @dev Calculate the required amount of OS to mint based on the wS amount.\n * This ensures the proportion of OS tokens being added to the pool matches the proportion of wS tokens.\n * For example, if the added wS tokens is 10% of existing wS tokens in the pool,\n * then the OS tokens being added should also be 10% of the OS tokens in the pool.\n * @param _wsAmount Amount of Wrapped S (wS) to be added to the pool.\n * @return osAmount Amount of OS to be minted and added to the pool.\n */\n function _calcTokensToMint(uint256 _wsAmount)\n internal\n view\n returns (uint256 osAmount)\n {\n (uint256 wsReserves, uint256 osReserves, ) = IPair(pool).getReserves();\n require(wsReserves > 0, \"Empty pool\");\n\n // OS to add = (wS being added * OS in pool) / wS in pool\n osAmount = (_wsAmount * osReserves) / wsReserves;\n }\n\n /**\n * @dev Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n * from the pool.\n * @param _wsAmount Amount of Wrapped S (wS) to be removed from the pool.\n * @return lpTokens Amount of SwapX pool LP tokens to burn.\n */\n function _calcTokensToBurn(uint256 _wsAmount)\n internal\n view\n returns (uint256 lpTokens)\n {\n /* The SwapX pool proportionally returns the reserve tokens when removing liquidity.\n * First, calculate the proportion of required wS tokens against the pools wS reserves.\n * That same proportion is used to calculate the required amount of pool LP tokens.\n * For example, if the required wS tokens is 10% of the pool's wS reserves,\n * then 10% of the pool's LP supply needs to be burned.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognizant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on, the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n (uint256 wsReserves, , ) = IPair(pool).getReserves();\n require(wsReserves > 0, \"Empty pool\");\n\n lpTokens = (_wsAmount * IPair(pool).totalSupply()) / wsReserves;\n lpTokens += 1; // Add 1 to ensure we get enough LP tokens with rounding\n }\n\n /**\n * @dev Deposit Wrapped S (wS) and OS liquidity to the SwapX pool\n * and stake the pool's LP token in the gauge.\n * @param _wsAmount Amount of Wrapped S (wS) to deposit.\n * @param _osAmount Amount of OS to deposit.\n * @return lpTokens Amount of SwapX pool LP tokens minted.\n */\n function _depositToPoolAndGauge(uint256 _wsAmount, uint256 _osAmount)\n internal\n returns (uint256 lpTokens)\n {\n // Transfer wS to the pool\n IERC20(ws).safeTransfer(pool, _wsAmount);\n // Transfer OS to the pool\n IERC20(os).safeTransfer(pool, _osAmount);\n\n // Mint LP tokens from the pool\n lpTokens = IPair(pool).mint(address(this));\n\n // Deposit the pool's LP tokens into the gauge\n IGauge(gauge).deposit(lpTokens);\n }\n\n /**\n * @dev Withdraw pool LP tokens from the gauge and remove wS and OS from the pool.\n * @param _lpTokens Amount of SwapX pool LP tokens to withdraw from the gauge\n */\n function _withdrawFromGaugeAndPool(uint256 _lpTokens) internal {\n require(\n IGauge(gauge).balanceOf(address(this)) >= _lpTokens,\n \"Not enough LP tokens in gauge\"\n );\n\n // Withdraw pool LP tokens from the gauge\n IGauge(gauge).withdraw(_lpTokens);\n\n // Transfer the pool LP tokens to the pool\n IERC20(pool).safeTransfer(pool, _lpTokens);\n\n // Burn the LP tokens and transfer the wS and OS back to the strategy\n IPair(pool).burn(address(this));\n }\n\n /**\n * @dev Withdraw all pool LP tokens from the gauge when it's in emergency mode\n * and remove wS and OS from the pool.\n */\n function _emergencyWithdrawFromGaugeAndPool() internal {\n // Withdraw all pool LP tokens from the gauge\n IGauge(gauge).emergencyWithdraw();\n\n // Get the pool LP tokens in strategy\n uint256 _lpTokens = IERC20(pool).balanceOf(address(this));\n\n // Transfer the pool LP tokens to the pool\n IERC20(pool).safeTransfer(pool, _lpTokens);\n\n // Burn the LP tokens and transfer the wS and OS back to the strategy\n IPair(pool).burn(address(this));\n }\n\n /**\n * @dev Swap exact amount of tokens for another token against the pool.\n * @param _amountIn Amount of tokens to swap into the pool.\n * @param _tokenIn Address of the token going into the pool.\n * @param _tokenOut Address of the token being swapped out of the pool.\n */\n function _swapExactTokensForTokens(\n uint256 _amountIn,\n address _tokenIn,\n address _tokenOut\n ) internal {\n // Transfer in tokens to the pool\n IERC20(_tokenIn).safeTransfer(pool, _amountIn);\n\n // Calculate how much out tokens we get from the swap\n uint256 amountOut = IPair(pool).getAmountOut(_amountIn, _tokenIn);\n\n // Safety check that we are dealing with the correct pool tokens\n require(\n (_tokenIn == ws && _tokenOut == os) ||\n (_tokenIn == os && _tokenOut == ws),\n \"Unsupported swap\"\n );\n\n // Work out the correct order of the amounts for the pool\n (uint256 amount0, uint256 amount1) = _tokenIn == ws\n ? (uint256(0), amountOut)\n : (amountOut, 0);\n\n // Perform the swap on the pool\n IPair(pool).swap(amount0, amount1, address(this), new bytes(0));\n\n // The slippage protection against the amount out is indirectly done\n // via the improvePoolBalance\n }\n\n /// @dev Calculate the value of a LP position in a SwapX stable pool\n /// if the pool was balanced.\n /// @param _lpTokens Amount of LP tokens in the SwapX pool\n /// @return value The wS value of the LP tokens when the pool is balanced\n function _lpValue(uint256 _lpTokens) internal view returns (uint256 value) {\n // Get total supply of LP tokens\n uint256 totalSupply = IPair(pool).totalSupply();\n if (totalSupply == 0) return 0;\n\n // Get the current reserves of the pool\n (uint256 wsReserves, uint256 osReserves, ) = IPair(pool).getReserves();\n\n // Calculate the invariant of the pool assuming both tokens have 18 decimals.\n // k is scaled to 18 decimals.\n uint256 k = _invariant(wsReserves, osReserves);\n\n // If x = y, let’s denote x = y = z (where z is the common reserve value)\n // Substitute z into the invariant:\n // k = z^3 * z + z * z^3\n // k = 2 * z^4\n // Going back the other way to calculate the common reserve value z\n // z = (k / 2) ^ (1/4)\n // the total value of the pool when x = y is 2 * z, which is 2 * (k / 2) ^ (1/4)\n uint256 zSquared = sqrt((k * 1e18) / 2); // 18 + 18 = 36 decimals becomes 18 decimals after sqrt\n uint256 z = sqrt(zSquared * 1e18); // 18 + 18 = 36 decimals becomes 18 decimals after sqrt\n uint256 totalValueOfPool = 2 * z;\n\n // lp value = lp tokens * value of pool / total supply\n value = (_lpTokens * totalValueOfPool) / totalSupply;\n }\n\n /**\n * @dev Compute the invariant for a SwapX stable pool.\n * This assumed both x and y tokens are to 18 decimals which is checked in the constructor.\n * invariant: k = x^3 * y + x * y^3\n * @dev This implementation is copied from SwapX's Pair contract.\n * @param _x The amount of Wrapped S (wS) tokens in the pool\n * @param _y The amount of the OS tokens in the pool\n * @return k The invariant of the SwapX stable pool\n */\n function _invariant(uint256 _x, uint256 _y)\n internal\n pure\n returns (uint256 k)\n {\n uint256 _a = (_x * _y) / PRECISION;\n uint256 _b = ((_x * _x) / PRECISION + (_y * _y) / PRECISION);\n // slither-disable-next-line divide-before-multiply\n k = (_a * _b) / PRECISION;\n }\n\n /**\n * @dev Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalSupply = IERC20(os).totalSupply();\n\n if (\n _totalSupply > 0 &&\n _totalVaultValue.divPrecisely(_totalSupply) < SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Setters\n ****************************************/\n\n /**\n * @notice Set the maximum deviation from the OS/wS peg (1e18) before deposits are reverted.\n * @param _maxDepeg the OS/wS price from peg (1e18) in 18 decimals.\n * eg 0.01e18 or 1e16 is 1% which is 100 basis points.\n */\n function setMaxDepeg(uint256 _maxDepeg) external onlyGovernor {\n maxDepeg = _maxDepeg;\n\n emit MaxDepegUpdated(_maxDepeg);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve SwapX gauge contract to transfer SwapX pool LP tokens\n // This is needed for deposits of SwapX pool LP tokens into the gauge.\n // slither-disable-next-line unused-return\n IPair(pool).approve(address(gauge), type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/sonic/SonicValidatorDelegator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { ISFC } from \"../../interfaces/sonic/ISFC.sol\";\nimport { IWrappedSonic } from \"../../interfaces/sonic/IWrappedSonic.sol\";\n\n/**\n * @title Manages delegation to Sonic validators\n * @notice This contract implements all the required functionality to delegate to,\n undelegate from and withdraw from validators.\n * @author Origin Protocol Inc\n */\nabstract contract SonicValidatorDelegator is InitializableAbstractStrategy {\n /// @notice Address of Sonic's wrapped S token\n address public immutable wrappedSonic;\n /// @notice Sonic's Special Fee Contract (SFC)\n ISFC public immutable sfc;\n\n /// @notice a unique ID for each withdrawal request\n uint256 public nextWithdrawId;\n /// @notice Sonic (S) that is pending withdrawal after undelegating\n uint256 public pendingWithdrawals;\n\n /// @notice List of supported validator IDs that can be delegated to\n uint256[] public supportedValidators;\n\n /// @notice Default validator id to deposit to\n uint256 public defaultValidatorId;\n\n struct WithdrawRequest {\n uint256 validatorId;\n uint256 undelegatedAmount;\n uint256 timestamp;\n }\n /// @notice Mapping of withdrawIds to validatorIds and undelegatedAmounts\n mapping(uint256 => WithdrawRequest) public withdrawals;\n\n /// @notice Address of the registrator - allowed to register, exit and remove validators\n address public validatorRegistrator;\n\n // For future use\n uint256[44] private __gap;\n\n event Delegated(uint256 indexed validatorId, uint256 delegatedAmount);\n event Undelegated(\n uint256 indexed withdrawId,\n uint256 indexed validatorId,\n uint256 undelegatedAmount\n );\n event Withdrawn(\n uint256 indexed withdrawId,\n uint256 indexed validatorId,\n uint256 undelegatedAmount,\n uint256 withdrawnAmount\n );\n event RegistratorChanged(address indexed newAddress);\n event SupportedValidator(uint256 indexed validatorId);\n event UnsupportedValidator(uint256 indexed validatorId);\n event DefaultValidatorIdChanged(uint256 indexed validatorId);\n\n /// @dev Throws if called by any account other than the Registrator or Strategist\n modifier onlyRegistratorOrStrategist() {\n require(\n msg.sender == validatorRegistrator ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Registrator or Strategist\"\n );\n _;\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wrappedSonic,\n address _sfc\n ) InitializableAbstractStrategy(_baseConfig) {\n wrappedSonic = _wrappedSonic;\n sfc = ISFC(_sfc);\n }\n\n function initialize() external virtual onlyGovernor initializer {\n address[] memory rewardTokens = new address[](0);\n address[] memory assets = new address[](1);\n address[] memory pTokens = new address[](1);\n\n assets[0] = address(wrappedSonic);\n pTokens[0] = address(platformAddress);\n\n InitializableAbstractStrategy._initialize(\n rewardTokens,\n assets,\n pTokens\n );\n }\n\n /// @notice Returns the total value of Sonic (S) that is delegated validators.\n /// Wrapped Sonic (wS) deposits that are still to be delegated and any undelegated amounts\n /// still pending a withdrawal.\n /// @param _asset Address of Wrapped Sonic (wS) token\n /// @return balance Total value managed by the strategy\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n\n // add the Wrapped Sonic (wS) in the strategy from deposits that are still to be delegated\n // and any undelegated amounts still pending a withdrawal\n balance =\n IERC20(wrappedSonic).balanceOf(address(this)) +\n pendingWithdrawals;\n\n // For each supported validator, get the staked amount and pending rewards\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; i++) {\n uint256 validator = supportedValidators[i];\n balance += sfc.getStake(address(this), validator);\n balance += sfc.pendingRewards(address(this), validator);\n }\n }\n\n /**\n * @dev Delegate from this strategy to a specific Sonic validator. Called\n * automatically on asset deposit\n * @param _amount the amount of Sonic (S) to delegate.\n */\n function _delegate(uint256 _amount) internal {\n require(\n isSupportedValidator(defaultValidatorId),\n \"Validator not supported\"\n );\n\n // unwrap Wrapped Sonic (wS) to native Sonic (S)\n IWrappedSonic(wrappedSonic).withdraw(_amount);\n\n //slither-disable-next-line arbitrary-send-eth\n sfc.delegate{ value: _amount }(defaultValidatorId);\n\n emit Delegated(defaultValidatorId, _amount);\n }\n\n /**\n * @notice Undelegate from a specific Sonic validator.\n * This needs to be followed by a `withdrawFromSFC` two weeks later.\n * @param _validatorId The Sonic validator ID to undelegate from.\n * @param _undelegateAmount the amount of Sonic (S) to undelegate.\n * @return withdrawId The unique ID of the withdrawal request.\n */\n function undelegate(uint256 _validatorId, uint256 _undelegateAmount)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n returns (uint256 withdrawId)\n {\n withdrawId = _undelegate(_validatorId, _undelegateAmount);\n }\n\n function _undelegate(uint256 _validatorId, uint256 _undelegateAmount)\n internal\n returns (uint256 withdrawId)\n {\n // Can still undelegate even if the validator is no longer supported\n require(_undelegateAmount > 0, \"Must undelegate something\");\n\n uint256 amountDelegated = sfc.getStake(address(this), _validatorId);\n require(\n _undelegateAmount <= amountDelegated,\n \"Insufficient delegation\"\n );\n\n withdrawId = nextWithdrawId++;\n\n withdrawals[withdrawId] = WithdrawRequest(\n _validatorId,\n _undelegateAmount,\n block.timestamp\n );\n pendingWithdrawals += _undelegateAmount;\n\n sfc.undelegate(_validatorId, withdrawId, _undelegateAmount);\n\n emit Undelegated(withdrawId, _validatorId, _undelegateAmount);\n }\n\n /**\n * @notice Withdraw native S from a previously undelegated validator.\n * The native S is wrapped wS and transferred to the Vault.\n * @param _withdrawId The unique withdraw ID used to `undelegate`\n * @return withdrawnAmount The amount of Sonic (S) withdrawn.\n * This can be less than the undelegated amount in the event of slashing.\n */\n function withdrawFromSFC(uint256 _withdrawId)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n returns (uint256 withdrawnAmount)\n {\n require(_withdrawId < nextWithdrawId, \"Invalid withdrawId\");\n\n // Can still withdraw even if the validator is no longer supported\n // Load the withdrawal from storage into memory\n WithdrawRequest memory withdrawal = withdrawals[_withdrawId];\n require(!isWithdrawnFromSFC(_withdrawId), \"Already withdrawn\");\n\n withdrawals[_withdrawId].undelegatedAmount = 0;\n pendingWithdrawals -= withdrawal.undelegatedAmount;\n\n uint256 sBalanceBefore = address(this).balance;\n\n // Try to withdraw from SFC\n try sfc.withdraw(withdrawal.validatorId, _withdrawId) {\n // continue below\n } catch (bytes memory err) {\n bytes4 errorSelector = bytes4(err);\n\n // If the validator has been fully slashed, SFC's withdraw function will\n // revert with a StakeIsFullySlashed custom error.\n if (errorSelector == ISFC.StakeIsFullySlashed.selector) {\n // The validator was fully slashed, so all the delegated amounts were lost.\n // Will swallow the error as we still want to update the\n // withdrawals and pendingWithdrawals storage variables.\n\n // The return param defaults to zero but lets set it explicitly so it's clear\n withdrawnAmount = 0;\n\n emit Withdrawn(\n _withdrawId,\n withdrawal.validatorId,\n withdrawal.undelegatedAmount,\n withdrawnAmount\n );\n\n // Exit here as there is nothing to transfer to the Vault\n return withdrawnAmount;\n } else {\n // Bubble up any other SFC custom errors.\n // Inline assembly is currently the only way to generically rethrow the exact same custom error\n // from the raw bytes err in a catch block while preserving its original selector and parameters.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n revert(add(32, err), mload(err))\n }\n }\n }\n\n // Set return parameter\n withdrawnAmount = address(this).balance - sBalanceBefore;\n\n // Wrap Sonic (S) to Wrapped Sonic (wS)\n IWrappedSonic(wrappedSonic).deposit{ value: withdrawnAmount }();\n\n // Transfer the Wrapped Sonic (wS) to the Vault\n _withdraw(vaultAddress, wrappedSonic, withdrawnAmount);\n\n // withdrawal.undelegatedAmount & withdrawnAmount can differ in case of slashing\n emit Withdrawn(\n _withdrawId,\n withdrawal.validatorId,\n withdrawal.undelegatedAmount,\n withdrawnAmount\n );\n }\n\n /// @notice returns a bool whether a withdrawalId has already been withdrawn or not\n /// @param _withdrawId The unique withdraw ID used to `undelegate`\n function isWithdrawnFromSFC(uint256 _withdrawId)\n public\n view\n returns (bool)\n {\n WithdrawRequest memory withdrawal = withdrawals[_withdrawId];\n require(withdrawal.validatorId > 0, \"Invalid withdrawId\");\n return withdrawal.undelegatedAmount == 0;\n }\n\n /**\n * @notice Restake any pending validator rewards for all supported validators\n * @param _validatorIds List of Sonic validator IDs to restake rewards\n */\n function restakeRewards(uint256[] calldata _validatorIds)\n external\n nonReentrant\n {\n for (uint256 i = 0; i < _validatorIds.length; ++i) {\n require(\n isSupportedValidator(_validatorIds[i]),\n \"Validator not supported\"\n );\n\n uint256 rewards = sfc.pendingRewards(\n address(this),\n _validatorIds[i]\n );\n\n if (rewards > 0) {\n sfc.restakeRewards(_validatorIds[i]);\n }\n }\n\n // The SFC contract will emit Delegated and RestakedRewards events.\n // The checkBalance function should not change as the pending rewards will moved to the staked amount.\n }\n\n /**\n * @notice Claim any pending rewards from validators\n * @param _validatorIds List of Sonic validator IDs to claim rewards\n */\n function collectRewards(uint256[] calldata _validatorIds)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n {\n uint256 sBalanceBefore = address(this).balance;\n\n for (uint256 i = 0; i < _validatorIds.length; ++i) {\n uint256 rewards = sfc.pendingRewards(\n address(this),\n _validatorIds[i]\n );\n\n if (rewards > 0) {\n // The SFC contract will emit ClaimedRewards(delegator (this), validatorId, rewards)\n sfc.claimRewards(_validatorIds[i]);\n }\n }\n\n uint256 rewardsAmount = address(this).balance - sBalanceBefore;\n\n // Wrap Sonic (S) to Wrapped Sonic (wS)\n IWrappedSonic(wrappedSonic).deposit{ value: rewardsAmount }();\n\n // Transfer the Wrapped Sonic (wS) to the Vault\n _withdraw(vaultAddress, wrappedSonic, rewardsAmount);\n }\n\n /**\n * @notice To receive native S from SFC and Wrapped Sonic (wS)\n *\n * @dev This does not prevent donating S tokens to the contract\n * as wrappedSonic has a `withdrawTo` function where a third party\n * owner of wrappedSonic can withdraw to this contract.\n */\n receive() external payable {\n require(\n msg.sender == address(sfc) || msg.sender == wrappedSonic,\n \"S not from allowed contracts\"\n );\n }\n\n /***************************************\n Admin functions\n ****************************************/\n\n /// @notice Set the address of the Registrator which can undelegate, withdraw and collect rewards\n /// @param _validatorRegistrator The address of the Registrator\n function setRegistrator(address _validatorRegistrator)\n external\n onlyGovernor\n {\n validatorRegistrator = _validatorRegistrator;\n emit RegistratorChanged(_validatorRegistrator);\n }\n\n /// @notice Set the default validatorId to delegate to on deposit\n /// @param _validatorId The validator identifier. eg 18\n function setDefaultValidatorId(uint256 _validatorId)\n external\n onlyRegistratorOrStrategist\n {\n require(isSupportedValidator(_validatorId), \"Validator not supported\");\n defaultValidatorId = _validatorId;\n emit DefaultValidatorIdChanged(_validatorId);\n }\n\n /// @notice Allows a validator to be delegated to by the Registrator\n /// @param _validatorId The validator identifier. eg 18\n function supportValidator(uint256 _validatorId) external onlyGovernor {\n require(\n !isSupportedValidator(_validatorId),\n \"Validator already supported\"\n );\n\n supportedValidators.push(_validatorId);\n\n emit SupportedValidator(_validatorId);\n }\n\n /// @notice Removes a validator from the supported list.\n /// Unsupported validators can still be undelegated from, withdrawn from and rewards collected.\n /// @param _validatorId The validator identifier. eg 18\n function unsupportValidator(uint256 _validatorId) external onlyGovernor {\n require(isSupportedValidator(_validatorId), \"Validator not supported\");\n\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; ++i) {\n if (supportedValidators[i] == _validatorId) {\n supportedValidators[i] = supportedValidators[validatorLen - 1];\n supportedValidators.pop();\n break;\n }\n }\n\n uint256 stake = sfc.getStake(address(this), _validatorId);\n\n // undelegate if validator still has funds staked\n if (stake > 0) {\n _undelegate(_validatorId, stake);\n }\n emit UnsupportedValidator(_validatorId);\n }\n\n /// @notice Returns the length of the supportedValidators array\n function supportedValidatorsLength() external view returns (uint256) {\n return supportedValidators.length;\n }\n\n /// @notice Returns whether a validator is supported by this strategy\n /// @param _validatorId The validator identifier\n function isSupportedValidator(uint256 _validatorId)\n public\n view\n returns (bool)\n {\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; ++i) {\n if (supportedValidators[i] == _validatorId) {\n return true;\n }\n }\n return false;\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal virtual;\n}\n" + }, + "contracts/strategies/VaultValueChecker.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IOUSD } from \"../interfaces/IOUSD.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract VaultValueChecker {\n IVault public immutable vault;\n IOUSD public immutable ousd;\n // Snapshot expiration time in seconds.\n // Used to prevent accidental use of an old snapshot, but\n // is not zero to allow easy testing of strategist actions in fork testing\n uint256 constant SNAPSHOT_EXPIRES = 5 * 60;\n\n struct Snapshot {\n uint256 vaultValue;\n uint256 totalSupply;\n uint256 time;\n }\n // By doing per user snapshots, we prevent a reentrancy attack\n // from a third party that updates the snapshot in the middle\n // of an allocation process\n\n mapping(address => Snapshot) public snapshots;\n\n constructor(address _vault, address _ousd) {\n vault = IVault(_vault);\n ousd = IOUSD(_ousd);\n }\n\n function takeSnapshot() external {\n snapshots[msg.sender] = Snapshot({\n vaultValue: vault.totalValue(),\n totalSupply: ousd.totalSupply(),\n time: block.timestamp\n });\n }\n\n function checkDelta(\n int256 expectedProfit,\n int256 profitVariance,\n int256 expectedVaultChange,\n int256 vaultChangeVariance\n ) external {\n // Intentionaly not view so that this method shows up in TX builders\n Snapshot memory snapshot = snapshots[msg.sender];\n int256 vaultChange = toInt256(vault.totalValue()) -\n toInt256(snapshot.vaultValue);\n int256 supplyChange = toInt256(ousd.totalSupply()) -\n toInt256(snapshot.totalSupply);\n int256 profit = vaultChange - supplyChange;\n\n require(\n snapshot.time >= block.timestamp - SNAPSHOT_EXPIRES,\n \"Snapshot too old\"\n );\n require(snapshot.time <= block.timestamp, \"Snapshot too new\");\n require(profit >= expectedProfit - profitVariance, \"Profit too low\");\n require(profit <= expectedProfit + profitVariance, \"Profit too high\");\n require(\n vaultChange >= expectedVaultChange - vaultChangeVariance,\n \"Vault value change too low\"\n );\n require(\n vaultChange <= expectedVaultChange + vaultChangeVariance,\n \"Vault value change too high\"\n );\n }\n\n function toInt256(uint256 value) internal pure returns (int256) {\n // From openzeppelin math/SafeCast.sol\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(\n value <= uint256(type(int256).max),\n \"SafeCast: value doesn't fit in an int256\"\n );\n return int256(value);\n }\n}\n\ncontract OETHVaultValueChecker is VaultValueChecker {\n constructor(address _vault, address _ousd)\n VaultValueChecker(_vault, _ousd)\n {}\n}\n" + }, + "contracts/token/OETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETH is OUSD {\n function symbol() external pure override returns (string memory) {\n return \"OETH\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Origin Ether\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETHBase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBase is OUSD {\n constructor() {\n // Nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n function symbol() external pure override returns (string memory) {\n return \"superOETHb\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Super OETH\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETHPlume.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title Super OETH (Plume) Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETHPlume is OUSD {\n constructor() {\n // Nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n function symbol() external pure override returns (string memory) {\n return \"superOETHp\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Super OETH\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OSonic.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title Origin Sonic (OS) token on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonic is OUSD {\n function symbol() external pure override returns (string memory) {\n return \"OS\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Origin Sonic\";\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/token/WOETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC4626 } from \"../../lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { OETH } from \"./OETH.sol\";\n\n/**\n * @title Wrapped OETH Token Contract\n * @author Origin Protocol Inc\n *\n * @dev An important capability of this contract is that it isn't susceptible to changes of the\n * exchange rate of WOETH/OETH if/when someone sends the underlying asset (OETH) to the contract.\n * If OETH weren't rebasing this could be achieved by solely tracking the ERC20 transfers of the OETH\n * token on mint, deposit, redeem, withdraw. The issue is that OETH is rebasing and OETH balances\n * will change when the token rebases.\n * For that reason the contract logic checks the actual underlying OETH token balance only once\n * (either on a fresh contract creation or upgrade) and considering the WOETH supply and\n * rebasingCreditsPerToken calculates the _adjuster. Once the adjuster is calculated any donations\n * to the contract are ignored. The totalSupply (instead of querying OETH balance) works off of\n * adjuster the current WOETH supply and rebasingCreditsPerToken. This makes WOETH value accrual\n * completely follow OETH's value accrual.\n * WOETH is safe to use in lending markets as the VualtCore's _rebase contains safeguards preventing\n * any sudden large rebases.\n */\n\ncontract WOETH is ERC4626, Governable, Initializable {\n using SafeERC20 for IERC20;\n /* This is a 1e27 adjustment constant that expresses the difference in exchange rate between\n * OETH's rebase since inception (expressed with rebasingCreditsPerToken) and WOETH to OETH\n * conversion.\n *\n * If WOETH and OETH are deployed at the same time, the value of adjuster is a neutral 1e27\n */\n uint256 public adjuster;\n uint256[49] private __gap;\n\n // no need to set ERC20 name and symbol since they are overridden in WOETH & WOETHBase\n constructor(ERC20 underlying_) ERC20(\"\", \"\") ERC4626(underlying_) {}\n\n /**\n * @notice Enable OETH rebasing for this contract\n */\n function initialize() external onlyGovernor initializer {\n OETH(address(asset())).rebaseOptIn();\n\n initialize2();\n }\n\n /**\n * @notice secondary initializer that newly deployed contracts will execute as part\n * of primary initialize function and the existing contracts will have it called\n * as a governance operation.\n */\n function initialize2() public onlyGovernor {\n require(adjuster == 0, \"Initialize2 already called\");\n\n if (totalSupply() == 0) {\n adjuster = 1e27;\n } else {\n adjuster =\n (rebasingCreditsPerTokenHighres() *\n ERC20(asset()).balanceOf(address(this))) /\n totalSupply();\n }\n }\n\n function name()\n public\n view\n virtual\n override(ERC20, IERC20Metadata)\n returns (string memory)\n {\n return \"Wrapped OETH\";\n }\n\n function symbol()\n public\n view\n virtual\n override(ERC20, IERC20Metadata)\n returns (string memory)\n {\n return \"wOETH\";\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends. Cannot transfer OETH\n * @param asset_ Address for the asset\n * @param amount_ Amount of the asset to transfer\n */\n function transferToken(address asset_, uint256 amount_)\n external\n onlyGovernor\n {\n require(asset_ != address(asset()), \"Cannot collect core asset\");\n IERC20(asset_).safeTransfer(governor(), amount_);\n }\n\n /// @inheritdoc ERC4626\n function convertToShares(uint256 assets)\n public\n view\n virtual\n override\n returns (uint256 shares)\n {\n return (assets * rebasingCreditsPerTokenHighres()) / adjuster;\n }\n\n /// @inheritdoc ERC4626\n function convertToAssets(uint256 shares)\n public\n view\n virtual\n override\n returns (uint256 assets)\n {\n return (shares * adjuster) / rebasingCreditsPerTokenHighres();\n }\n\n /// @inheritdoc ERC4626\n function totalAssets() public view override returns (uint256) {\n return (totalSupply() * adjuster) / rebasingCreditsPerTokenHighres();\n }\n\n function rebasingCreditsPerTokenHighres() internal view returns (uint256) {\n return OETH(asset()).rebasingCreditsPerTokenHighres();\n }\n}\n" + }, + "contracts/token/WOETHBase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WOETH } from \"./WOETH.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\n\ncontract WOETHBase is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name() public view virtual override returns (string memory) {\n return \"Wrapped Super OETH\";\n }\n\n function symbol() public view virtual override returns (string memory) {\n return \"wsuperOETHb\";\n }\n}\n" + }, + "contracts/token/WOETHPlume.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WOETH } from \"./WOETH.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @title wOETH (Plume) Token Contract\n * @author Origin Protocol Inc\n */\n\ncontract WOETHPlume is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name() public view virtual override returns (string memory) {\n return \"Wrapped Super OETH\";\n }\n\n function symbol() public view virtual override returns (string memory) {\n return \"wsuperOETHp\";\n }\n}\n" + }, + "contracts/token/WOSonic.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { WOETH } from \"./WOETH.sol\";\n\n/**\n * @title Wrapped Origin Sonic (wOS) token on Sonic\n * @author Origin Protocol Inc\n */\ncontract WOSonic is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"Wrapped OS\";\n }\n\n function symbol()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"wOS\";\n }\n}\n" + }, + "contracts/token/WrappedOusd.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { WOETH } from \"./WOETH.sol\";\n\n/**\n * @title Wrapped OUSD Token Contract\n * @author Origin Protocol Inc\n */\ncontract WrappedOusd is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"Wrapped OUSD\";\n }\n\n function symbol()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"WOUSD\";\n }\n}\n" + }, + "contracts/utils/BalancerErrors.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity >=0.7.4 <0.9.0;\n\n// solhint-disable\n\n/**\n * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are\n * supported.\n * Uses the default 'BAL' prefix for the error code\n */\nfunction _require(bool condition, uint256 errorCode) pure {\n if (!condition) _revert(errorCode);\n}\n\n/**\n * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are\n * supported.\n */\nfunction _require(\n bool condition,\n uint256 errorCode,\n bytes3 prefix\n) pure {\n if (!condition) _revert(errorCode, prefix);\n}\n\n/**\n * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported.\n * Uses the default 'BAL' prefix for the error code\n */\nfunction _revert(uint256 errorCode) pure {\n _revert(errorCode, 0x42414c); // This is the raw byte representation of \"BAL\"\n}\n\n/**\n * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported.\n */\nfunction _revert(uint256 errorCode, bytes3 prefix) pure {\n uint256 prefixUint = uint256(uint24(prefix));\n // We're going to dynamically create a revert string based on the error code, with the following format:\n // 'BAL#{errorCode}'\n // where the code is left-padded with zeroes to three digits (so they range from 000 to 999).\n //\n // We don't have revert strings embedded in the contract to save bytecode size: it takes much less space to store a\n // number (8 to 16 bits) than the individual string characters.\n //\n // The dynamic string creation algorithm that follows could be implemented in Solidity, but assembly allows for a\n // much denser implementation, again saving bytecode size. Given this function unconditionally reverts, this is a\n // safe place to rely on it without worrying about how its usage might affect e.g. memory contents.\n assembly {\n // First, we need to compute the ASCII representation of the error code. We assume that it is in the 0-999\n // range, so we only need to convert three digits. To convert the digits to ASCII, we add 0x30, the value for\n // the '0' character.\n\n let units := add(mod(errorCode, 10), 0x30)\n\n errorCode := div(errorCode, 10)\n let tenths := add(mod(errorCode, 10), 0x30)\n\n errorCode := div(errorCode, 10)\n let hundreds := add(mod(errorCode, 10), 0x30)\n\n // With the individual characters, we can now construct the full string.\n // We first append the '#' character (0x23) to the prefix. In the case of 'BAL', it results in 0x42414c23 ('BAL#')\n // Then, we shift this by 24 (to provide space for the 3 bytes of the error code), and add the\n // characters to it, each shifted by a multiple of 8.\n // The revert reason is then shifted left by 200 bits (256 minus the length of the string, 7 characters * 8 bits\n // per character = 56) to locate it in the most significant part of the 256 slot (the beginning of a byte\n // array).\n let formattedPrefix := shl(24, add(0x23, shl(8, prefixUint)))\n\n let revertReason := shl(\n 200,\n add(\n formattedPrefix,\n add(add(units, shl(8, tenths)), shl(16, hundreds))\n )\n )\n\n // We can now encode the reason in memory, which can be safely overwritten as we're about to revert. The encoded\n // message will have the following layout:\n // [ revert reason identifier ] [ string location offset ] [ string length ] [ string contents ]\n\n // The Solidity revert reason identifier is 0x08c739a0, the function selector of the Error(string) function. We\n // also write zeroes to the next 28 bytes of memory, but those are about to be overwritten.\n mstore(\n 0x0,\n 0x08c379a000000000000000000000000000000000000000000000000000000000\n )\n // Next is the offset to the location of the string, which will be placed immediately after (20 bytes away).\n mstore(\n 0x04,\n 0x0000000000000000000000000000000000000000000000000000000000000020\n )\n // The string length is fixed: 7 characters.\n mstore(0x24, 7)\n // Finally, the string itself is stored.\n mstore(0x44, revertReason)\n\n // Even if the string is only 7 bytes long, we need to return a full 32 byte slot containing it. The length of\n // the encoded message is therefore 4 + 32 + 32 + 32 = 100.\n revert(0, 100)\n }\n}\n\nlibrary Errors {\n // Math\n uint256 internal constant ADD_OVERFLOW = 0;\n uint256 internal constant SUB_OVERFLOW = 1;\n uint256 internal constant SUB_UNDERFLOW = 2;\n uint256 internal constant MUL_OVERFLOW = 3;\n uint256 internal constant ZERO_DIVISION = 4;\n uint256 internal constant DIV_INTERNAL = 5;\n uint256 internal constant X_OUT_OF_BOUNDS = 6;\n uint256 internal constant Y_OUT_OF_BOUNDS = 7;\n uint256 internal constant PRODUCT_OUT_OF_BOUNDS = 8;\n uint256 internal constant INVALID_EXPONENT = 9;\n\n // Input\n uint256 internal constant OUT_OF_BOUNDS = 100;\n uint256 internal constant UNSORTED_ARRAY = 101;\n uint256 internal constant UNSORTED_TOKENS = 102;\n uint256 internal constant INPUT_LENGTH_MISMATCH = 103;\n uint256 internal constant ZERO_TOKEN = 104;\n uint256 internal constant INSUFFICIENT_DATA = 105;\n\n // Shared pools\n uint256 internal constant MIN_TOKENS = 200;\n uint256 internal constant MAX_TOKENS = 201;\n uint256 internal constant MAX_SWAP_FEE_PERCENTAGE = 202;\n uint256 internal constant MIN_SWAP_FEE_PERCENTAGE = 203;\n uint256 internal constant MINIMUM_BPT = 204;\n uint256 internal constant CALLER_NOT_VAULT = 205;\n uint256 internal constant UNINITIALIZED = 206;\n uint256 internal constant BPT_IN_MAX_AMOUNT = 207;\n uint256 internal constant BPT_OUT_MIN_AMOUNT = 208;\n uint256 internal constant EXPIRED_PERMIT = 209;\n uint256 internal constant NOT_TWO_TOKENS = 210;\n uint256 internal constant DISABLED = 211;\n\n // Pools\n uint256 internal constant MIN_AMP = 300;\n uint256 internal constant MAX_AMP = 301;\n uint256 internal constant MIN_WEIGHT = 302;\n uint256 internal constant MAX_STABLE_TOKENS = 303;\n uint256 internal constant MAX_IN_RATIO = 304;\n uint256 internal constant MAX_OUT_RATIO = 305;\n uint256 internal constant MIN_BPT_IN_FOR_TOKEN_OUT = 306;\n uint256 internal constant MAX_OUT_BPT_FOR_TOKEN_IN = 307;\n uint256 internal constant NORMALIZED_WEIGHT_INVARIANT = 308;\n uint256 internal constant INVALID_TOKEN = 309;\n uint256 internal constant UNHANDLED_JOIN_KIND = 310;\n uint256 internal constant ZERO_INVARIANT = 311;\n uint256 internal constant ORACLE_INVALID_SECONDS_QUERY = 312;\n uint256 internal constant ORACLE_NOT_INITIALIZED = 313;\n uint256 internal constant ORACLE_QUERY_TOO_OLD = 314;\n uint256 internal constant ORACLE_INVALID_INDEX = 315;\n uint256 internal constant ORACLE_BAD_SECS = 316;\n uint256 internal constant AMP_END_TIME_TOO_CLOSE = 317;\n uint256 internal constant AMP_ONGOING_UPDATE = 318;\n uint256 internal constant AMP_RATE_TOO_HIGH = 319;\n uint256 internal constant AMP_NO_ONGOING_UPDATE = 320;\n uint256 internal constant STABLE_INVARIANT_DIDNT_CONVERGE = 321;\n uint256 internal constant STABLE_GET_BALANCE_DIDNT_CONVERGE = 322;\n uint256 internal constant RELAYER_NOT_CONTRACT = 323;\n uint256 internal constant BASE_POOL_RELAYER_NOT_CALLED = 324;\n uint256 internal constant REBALANCING_RELAYER_REENTERED = 325;\n uint256 internal constant GRADUAL_UPDATE_TIME_TRAVEL = 326;\n uint256 internal constant SWAPS_DISABLED = 327;\n uint256 internal constant CALLER_IS_NOT_LBP_OWNER = 328;\n uint256 internal constant PRICE_RATE_OVERFLOW = 329;\n uint256 internal constant INVALID_JOIN_EXIT_KIND_WHILE_SWAPS_DISABLED = 330;\n uint256 internal constant WEIGHT_CHANGE_TOO_FAST = 331;\n uint256 internal constant LOWER_GREATER_THAN_UPPER_TARGET = 332;\n uint256 internal constant UPPER_TARGET_TOO_HIGH = 333;\n uint256 internal constant UNHANDLED_BY_LINEAR_POOL = 334;\n uint256 internal constant OUT_OF_TARGET_RANGE = 335;\n uint256 internal constant UNHANDLED_EXIT_KIND = 336;\n uint256 internal constant UNAUTHORIZED_EXIT = 337;\n uint256 internal constant MAX_MANAGEMENT_SWAP_FEE_PERCENTAGE = 338;\n uint256 internal constant UNHANDLED_BY_MANAGED_POOL = 339;\n uint256 internal constant UNHANDLED_BY_PHANTOM_POOL = 340;\n uint256 internal constant TOKEN_DOES_NOT_HAVE_RATE_PROVIDER = 341;\n uint256 internal constant INVALID_INITIALIZATION = 342;\n uint256 internal constant OUT_OF_NEW_TARGET_RANGE = 343;\n uint256 internal constant FEATURE_DISABLED = 344;\n uint256 internal constant UNINITIALIZED_POOL_CONTROLLER = 345;\n uint256 internal constant SET_SWAP_FEE_DURING_FEE_CHANGE = 346;\n uint256 internal constant SET_SWAP_FEE_PENDING_FEE_CHANGE = 347;\n uint256 internal constant CHANGE_TOKENS_DURING_WEIGHT_CHANGE = 348;\n uint256 internal constant CHANGE_TOKENS_PENDING_WEIGHT_CHANGE = 349;\n uint256 internal constant MAX_WEIGHT = 350;\n uint256 internal constant UNAUTHORIZED_JOIN = 351;\n uint256 internal constant MAX_MANAGEMENT_AUM_FEE_PERCENTAGE = 352;\n uint256 internal constant FRACTIONAL_TARGET = 353;\n uint256 internal constant ADD_OR_REMOVE_BPT = 354;\n uint256 internal constant INVALID_CIRCUIT_BREAKER_BOUNDS = 355;\n uint256 internal constant CIRCUIT_BREAKER_TRIPPED = 356;\n uint256 internal constant MALICIOUS_QUERY_REVERT = 357;\n uint256 internal constant JOINS_EXITS_DISABLED = 358;\n\n // Lib\n uint256 internal constant REENTRANCY = 400;\n uint256 internal constant SENDER_NOT_ALLOWED = 401;\n uint256 internal constant PAUSED = 402;\n uint256 internal constant PAUSE_WINDOW_EXPIRED = 403;\n uint256 internal constant MAX_PAUSE_WINDOW_DURATION = 404;\n uint256 internal constant MAX_BUFFER_PERIOD_DURATION = 405;\n uint256 internal constant INSUFFICIENT_BALANCE = 406;\n uint256 internal constant INSUFFICIENT_ALLOWANCE = 407;\n uint256 internal constant ERC20_TRANSFER_FROM_ZERO_ADDRESS = 408;\n uint256 internal constant ERC20_TRANSFER_TO_ZERO_ADDRESS = 409;\n uint256 internal constant ERC20_MINT_TO_ZERO_ADDRESS = 410;\n uint256 internal constant ERC20_BURN_FROM_ZERO_ADDRESS = 411;\n uint256 internal constant ERC20_APPROVE_FROM_ZERO_ADDRESS = 412;\n uint256 internal constant ERC20_APPROVE_TO_ZERO_ADDRESS = 413;\n uint256 internal constant ERC20_TRANSFER_EXCEEDS_ALLOWANCE = 414;\n uint256 internal constant ERC20_DECREASED_ALLOWANCE_BELOW_ZERO = 415;\n uint256 internal constant ERC20_TRANSFER_EXCEEDS_BALANCE = 416;\n uint256 internal constant ERC20_BURN_EXCEEDS_ALLOWANCE = 417;\n uint256 internal constant SAFE_ERC20_CALL_FAILED = 418;\n uint256 internal constant ADDRESS_INSUFFICIENT_BALANCE = 419;\n uint256 internal constant ADDRESS_CANNOT_SEND_VALUE = 420;\n uint256 internal constant SAFE_CAST_VALUE_CANT_FIT_INT256 = 421;\n uint256 internal constant GRANT_SENDER_NOT_ADMIN = 422;\n uint256 internal constant REVOKE_SENDER_NOT_ADMIN = 423;\n uint256 internal constant RENOUNCE_SENDER_NOT_ALLOWED = 424;\n uint256 internal constant BUFFER_PERIOD_EXPIRED = 425;\n uint256 internal constant CALLER_IS_NOT_OWNER = 426;\n uint256 internal constant NEW_OWNER_IS_ZERO = 427;\n uint256 internal constant CODE_DEPLOYMENT_FAILED = 428;\n uint256 internal constant CALL_TO_NON_CONTRACT = 429;\n uint256 internal constant LOW_LEVEL_CALL_FAILED = 430;\n uint256 internal constant NOT_PAUSED = 431;\n uint256 internal constant ADDRESS_ALREADY_ALLOWLISTED = 432;\n uint256 internal constant ADDRESS_NOT_ALLOWLISTED = 433;\n uint256 internal constant ERC20_BURN_EXCEEDS_BALANCE = 434;\n uint256 internal constant INVALID_OPERATION = 435;\n uint256 internal constant CODEC_OVERFLOW = 436;\n uint256 internal constant IN_RECOVERY_MODE = 437;\n uint256 internal constant NOT_IN_RECOVERY_MODE = 438;\n uint256 internal constant INDUCED_FAILURE = 439;\n uint256 internal constant EXPIRED_SIGNATURE = 440;\n uint256 internal constant MALFORMED_SIGNATURE = 441;\n uint256 internal constant SAFE_CAST_VALUE_CANT_FIT_UINT64 = 442;\n uint256 internal constant UNHANDLED_FEE_TYPE = 443;\n uint256 internal constant BURN_FROM_ZERO = 444;\n\n // Vault\n uint256 internal constant INVALID_POOL_ID = 500;\n uint256 internal constant CALLER_NOT_POOL = 501;\n uint256 internal constant SENDER_NOT_ASSET_MANAGER = 502;\n uint256 internal constant USER_DOESNT_ALLOW_RELAYER = 503;\n uint256 internal constant INVALID_SIGNATURE = 504;\n uint256 internal constant EXIT_BELOW_MIN = 505;\n uint256 internal constant JOIN_ABOVE_MAX = 506;\n uint256 internal constant SWAP_LIMIT = 507;\n uint256 internal constant SWAP_DEADLINE = 508;\n uint256 internal constant CANNOT_SWAP_SAME_TOKEN = 509;\n uint256 internal constant UNKNOWN_AMOUNT_IN_FIRST_SWAP = 510;\n uint256 internal constant MALCONSTRUCTED_MULTIHOP_SWAP = 511;\n uint256 internal constant INTERNAL_BALANCE_OVERFLOW = 512;\n uint256 internal constant INSUFFICIENT_INTERNAL_BALANCE = 513;\n uint256 internal constant INVALID_ETH_INTERNAL_BALANCE = 514;\n uint256 internal constant INVALID_POST_LOAN_BALANCE = 515;\n uint256 internal constant INSUFFICIENT_ETH = 516;\n uint256 internal constant UNALLOCATED_ETH = 517;\n uint256 internal constant ETH_TRANSFER = 518;\n uint256 internal constant CANNOT_USE_ETH_SENTINEL = 519;\n uint256 internal constant TOKENS_MISMATCH = 520;\n uint256 internal constant TOKEN_NOT_REGISTERED = 521;\n uint256 internal constant TOKEN_ALREADY_REGISTERED = 522;\n uint256 internal constant TOKENS_ALREADY_SET = 523;\n uint256 internal constant TOKENS_LENGTH_MUST_BE_2 = 524;\n uint256 internal constant NONZERO_TOKEN_BALANCE = 525;\n uint256 internal constant BALANCE_TOTAL_OVERFLOW = 526;\n uint256 internal constant POOL_NO_TOKENS = 527;\n uint256 internal constant INSUFFICIENT_FLASH_LOAN_BALANCE = 528;\n\n // Fees\n uint256 internal constant SWAP_FEE_PERCENTAGE_TOO_HIGH = 600;\n uint256 internal constant FLASH_LOAN_FEE_PERCENTAGE_TOO_HIGH = 601;\n uint256 internal constant INSUFFICIENT_FLASH_LOAN_FEE_AMOUNT = 602;\n uint256 internal constant AUM_FEE_PERCENTAGE_TOO_HIGH = 603;\n\n // FeeSplitter\n uint256 internal constant SPLITTER_FEE_PERCENTAGE_TOO_HIGH = 700;\n\n // Misc\n uint256 internal constant UNIMPLEMENTED = 998;\n uint256 internal constant SHOULD_NOT_HAPPEN = 999;\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/utils/PRBMath.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// Copied from the PRBMath library\n// https://github.com/PaulRBerg/prb-math/blob/main/src/Common.sol\n\n/// @notice Calculates the square root of x using the Babylonian method.\n///\n/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.\n///\n/// Notes:\n/// - If x is not a perfect square, the result is rounded down.\n/// - Credits to OpenZeppelin for the explanations in comments below.\n///\n/// @param x The uint256 number for which to calculate the square root.\n/// @return result The result as a uint256.\n/// @custom:smtchecker abstract-function-nondet\nfunction sqrt(uint256 x) pure returns (uint256 result) {\n if (x == 0) {\n return 0;\n }\n\n // For our first guess, we calculate the biggest power of 2 which is smaller than the square root of x.\n //\n // We know that the \"msb\" (most significant bit) of x is a power of 2 such that we have:\n //\n // $$\n // msb(x) <= x <= 2*msb(x)$\n // $$\n //\n // We write $msb(x)$ as $2^k$, and we get:\n //\n // $$\n // k = log_2(x)\n // $$\n //\n // Thus, we can write the initial inequality as:\n //\n // $$\n // 2^{log_2(x)} <= x <= 2*2^{log_2(x)+1} \\\\\n // sqrt(2^k) <= sqrt(x) < sqrt(2^{k+1}) \\\\\n // 2^{k/2} <= sqrt(x) < 2^{(k+1)/2} <= 2^{(k/2)+1}\n // $$\n //\n // Consequently, $2^{log_2(x) /2} is a good first approximation of sqrt(x) with at least one correct bit.\n uint256 xAux = uint256(x);\n result = 1;\n if (xAux >= 2**128) {\n xAux >>= 128;\n result <<= 64;\n }\n if (xAux >= 2**64) {\n xAux >>= 64;\n result <<= 32;\n }\n if (xAux >= 2**32) {\n xAux >>= 32;\n result <<= 16;\n }\n if (xAux >= 2**16) {\n xAux >>= 16;\n result <<= 8;\n }\n if (xAux >= 2**8) {\n xAux >>= 8;\n result <<= 4;\n }\n if (xAux >= 2**4) {\n xAux >>= 4;\n result <<= 2;\n }\n if (xAux >= 2**2) {\n result <<= 1;\n }\n\n // At this point, `result` is an estimation with at least one bit of precision. We know the true value has at\n // most 128 bits, since it is the square root of a uint256. Newton's method converges quadratically (precision\n // doubles at every iteration). We thus need at most 7 iteration to turn our partial result with one bit of\n // precision into the expected uint128 result.\n unchecked {\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n\n // If x is not a perfect square, round the result toward zero.\n uint256 roundedResult = x / result;\n if (result >= roundedResult) {\n result = roundedResult;\n }\n }\n}\n" + }, + "contracts/utils/StableMath.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\n// Based on StableMath from Stability Labs Pty. Ltd.\n// https://github.com/mstable/mStable-contracts/blob/master/contracts/shared/StableMath.sol\n\nlibrary StableMath {\n using SafeMath for uint256;\n\n /**\n * @dev Scaling unit for use in specific calculations,\n * where 1 * 10**18, or 1e18 represents a unit '1'\n */\n uint256 private constant FULL_SCALE = 1e18;\n\n /***************************************\n Helpers\n ****************************************/\n\n /**\n * @dev Adjust the scale of an integer\n * @param to Decimals to scale to\n * @param from Decimals to scale from\n */\n function scaleBy(\n uint256 x,\n uint256 to,\n uint256 from\n ) internal pure returns (uint256) {\n if (to > from) {\n x = x.mul(10**(to - from));\n } else if (to < from) {\n // slither-disable-next-line divide-before-multiply\n x = x.div(10**(from - to));\n }\n return x;\n }\n\n /***************************************\n Precise Arithmetic\n ****************************************/\n\n /**\n * @dev Multiplies two precise units, and then truncates by the full scale\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit\n */\n function mulTruncate(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulTruncateScale(x, y, FULL_SCALE);\n }\n\n /**\n * @dev Multiplies two precise units, and then truncates by the given scale. For example,\n * when calculating 90% of 10e18, (10e18 * 9e17) / 1e18 = (9e36) / 1e18 = 9e18\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @param scale Scale unit\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit\n */\n function mulTruncateScale(\n uint256 x,\n uint256 y,\n uint256 scale\n ) internal pure returns (uint256) {\n // e.g. assume scale = fullScale\n // z = 10e18 * 9e17 = 9e36\n uint256 z = x.mul(y);\n // return 9e36 / 1e18 = 9e18\n return z.div(scale);\n }\n\n /**\n * @dev Multiplies two precise units, and then truncates by the full scale, rounding up the result\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit, rounded up to the closest base unit.\n */\n function mulTruncateCeil(uint256 x, uint256 y)\n internal\n pure\n returns (uint256)\n {\n // e.g. 8e17 * 17268172638 = 138145381104e17\n uint256 scaled = x.mul(y);\n // e.g. 138145381104e17 + 9.99...e17 = 138145381113.99...e17\n uint256 ceil = scaled.add(FULL_SCALE.sub(1));\n // e.g. 13814538111.399...e18 / 1e18 = 13814538111\n return ceil.div(FULL_SCALE);\n }\n\n /**\n * @dev Precisely divides two units, by first scaling the left hand operand. Useful\n * for finding percentage weightings, i.e. 8e18/10e18 = 80% (or 8e17)\n * @param x Left hand input to division\n * @param y Right hand input to division\n * @return Result after multiplying the left operand by the scale, and\n * executing the division on the right hand input.\n */\n function divPrecisely(uint256 x, uint256 y)\n internal\n pure\n returns (uint256)\n {\n // e.g. 8e18 * 1e18 = 8e36\n uint256 z = x.mul(FULL_SCALE);\n // e.g. 8e36 / 10e18 = 8e17\n return z.div(y);\n }\n}\n" + }, + "contracts/vault/OETHBaseVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"./OETHVaultAdmin.sol\";\n\n/**\n * @title OETH Base VaultAdmin Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBaseVaultAdmin is OETHVaultAdmin {\n constructor(address _weth) OETHVaultAdmin(_weth) {}\n}\n" + }, + "contracts/vault/OETHBaseVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { OETHVaultCore } from \"./OETHVaultCore.sol\";\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\n\n/**\n * @title OETH Base VaultCore Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBaseVaultCore is OETHVaultCore {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(address _weth) OETHVaultCore(_weth) {}\n\n // @inheritdoc OETHVaultCore\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n override\n {\n // Only Strategist or Governor can redeem using the Vault for now.\n // We don't have the onlyGovernorOrStrategist modifier on VaultCore.\n // Since we won't be using that modifier anywhere in the VaultCore as well,\n // the check has been added inline instead of moving it to VaultStorage.\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n\n super._redeem(_amount, _minimumUnitAmount);\n }\n}\n" + }, + "contracts/vault/OETHBaseZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\n\ncontract OETHBaseZapper {\n IERC20 public immutable oethb;\n IERC4626 public immutable woethb;\n IVault public immutable vault;\n\n IWETH9 public constant weth =\n IWETH9(0x4200000000000000000000000000000000000006);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(\n address _oethb,\n address _woethb,\n address _vault\n ) {\n oethb = IERC20(_oethb);\n woethb = IERC4626(_woethb);\n vault = IVault(_vault);\n\n weth.approve(address(_vault), type(uint256).max);\n IERC20(_oethb).approve(_woethb, type(uint256).max);\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return.\n * Will verify that the user is sent 1:1 for ETH.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit ETH and receive superOETHb in return\n * Will verify that the user is sent 1:1 for ETH.\n * @return Amount of OETH sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap ETH\n weth.deposit{ value: balance }();\n\n // Mint with WETH\n return _mint(balance, msg.sender);\n }\n\n /**\n * @dev Deposit ETH and receive superOETHb in return\n * @param minReceived min amount of wsuperOETHb to receive\n * @return Amount of wsuperOETHb sent to user\n */\n function depositETHForWrappedTokens(uint256 minReceived)\n external\n payable\n returns (uint256)\n {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap ETH\n weth.deposit{ value: balance }();\n\n // Mint with WETH\n uint256 mintedOethb = _mint(balance, address(this));\n\n // Wrap superOETHb into wsuperOETHb\n uint256 mintedWoethb = woethb.deposit(mintedOethb, msg.sender);\n\n require(mintedWoethb >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWoethb;\n }\n\n /**\n * @dev Deposit WETH and receive superOETHb in return\n * @param wethAmount Amount of WETH to deposit\n * @param minReceived min amount of wsuperOETHb to receive\n * @return Amount of wsuperOETHb sent to user\n */\n function depositWETHForWrappedTokens(\n uint256 wethAmount,\n uint256 minReceived\n ) external returns (uint256) {\n // slither-disable-next-line unchecked-transfer unused-return\n weth.transferFrom(msg.sender, address(this), wethAmount);\n\n emit Zap(msg.sender, address(weth), wethAmount);\n\n // Mint with WETH\n uint256 mintedOethb = _mint(wethAmount, address(this));\n\n // Wrap superOETHb into wsuperOETHb\n uint256 mintedWoethb = woethb.deposit(mintedOethb, msg.sender);\n\n require(mintedWoethb >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWoethb;\n }\n\n /**\n * @dev Internal function to mint superOETHb with WETH\n * @param minOETH Minimum amount of OETH to for user to receive\n * @param recipient Address that receives the tokens\n * @return Amount of OETH sent to user\n */\n function _mint(uint256 minOETH, address recipient)\n internal\n returns (uint256)\n {\n uint256 toMint = weth.balanceOf(address(this));\n vault.mint(address(weth), toMint, minOETH);\n uint256 mintedAmount = oethb.balanceOf(address(this));\n require(mintedAmount >= minOETH, \"Zapper: not enough minted\");\n\n if (recipient != address(this)) {\n require(oethb.transfer(recipient, mintedAmount));\n }\n\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/OETHVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Vault } from \"./Vault.sol\";\n\n/**\n * @title OETH Vault Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVault is Vault {\n\n}\n" + }, + "contracts/vault/OETHVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { VaultAdmin } from \"./VaultAdmin.sol\";\n\n/**\n * @title OETH VaultAdmin Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVaultAdmin is VaultAdmin {\n using SafeERC20 for IERC20;\n\n address public immutable weth;\n\n constructor(address _weth) {\n weth = _weth;\n }\n\n /**\n * @notice Adds a strategy to the mint whitelist.\n * Reverts if strategy isn't approved on Vault.\n * @param strategyAddr Strategy address\n */\n function addStrategyToMintWhitelist(address strategyAddr)\n external\n onlyGovernor\n {\n require(strategies[strategyAddr].isSupported, \"Strategy not approved\");\n\n require(\n !isMintWhitelistedStrategy[strategyAddr],\n \"Already whitelisted\"\n );\n\n isMintWhitelistedStrategy[strategyAddr] = true;\n\n emit StrategyAddedToMintWhitelist(strategyAddr);\n }\n\n /**\n * @notice Removes a strategy from the mint whitelist.\n * @param strategyAddr Strategy address\n */\n function removeStrategyFromMintWhitelist(address strategyAddr)\n external\n onlyGovernor\n {\n // Intentionally skipping `strategies.isSupported` check since\n // we may wanna remove an address even after removing the strategy\n\n require(isMintWhitelistedStrategy[strategyAddr], \"Not whitelisted\");\n\n isMintWhitelistedStrategy[strategyAddr] = false;\n\n emit StrategyRemovedFromMintWhitelist(strategyAddr);\n }\n\n /// @dev Simplified version of the deposit function as WETH is the only supported asset.\n function _depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal override {\n require(\n strategies[_strategyToAddress].isSupported,\n \"Invalid to Strategy\"\n );\n require(\n _assets.length == 1 && _amounts.length == 1 && _assets[0] == weth,\n \"Only WETH is supported\"\n );\n\n // Check the there is enough WETH to transfer once the WETH reserved for the withdrawal queue is accounted for\n require(_amounts[0] <= _wethAvailable(), \"Not enough WETH available\");\n\n // Send required amount of funds to the strategy\n IERC20(weth).safeTransfer(_strategyToAddress, _amounts[0]);\n\n // Deposit all the funds that have been sent to the strategy\n IStrategy(_strategyToAddress).depositAll();\n }\n\n function _withdrawFromStrategy(\n address _recipient,\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal override {\n super._withdrawFromStrategy(\n _recipient,\n _strategyFromAddress,\n _assets,\n _amounts\n );\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n function _withdrawAllFromStrategy(address _strategyAddr) internal override {\n super._withdrawAllFromStrategy(_strategyAddr);\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n function _withdrawAllFromStrategies() internal override {\n super._withdrawAllFromStrategies();\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n /// @dev Calculate how much WETH in the vault is not reserved for the withdrawal queue.\n // That is, it is available to be redeemed or deposited into a strategy.\n function _wethAvailable() internal view returns (uint256 wethAvailable) {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n uint256 outstandingWithdrawals = queue.queued - queue.claimed;\n\n // The amount of sitting in WETH in the vault\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // If there is not enough WETH in the vault to cover the outstanding withdrawals\n if (wethBalance <= outstandingWithdrawals) {\n return 0;\n }\n\n return wethBalance - outstandingWithdrawals;\n }\n\n function _swapCollateral(\n address,\n address,\n uint256,\n uint256,\n bytes calldata\n ) internal pure override returns (uint256) {\n revert(\"Collateral swap not supported\");\n }\n}\n" + }, + "contracts/vault/OETHVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { VaultCore } from \"./VaultCore.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IDripper } from \"../interfaces/IDripper.sol\";\n\n/**\n * @title OETH VaultCore Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVaultCore is VaultCore {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n address public immutable weth;\n uint256 public wethAssetIndex;\n\n // For future use (because OETHBaseVaultCore inherits from this)\n uint256[50] private __gap;\n\n constructor(address _weth) {\n weth = _weth;\n }\n\n /**\n * @dev Caches WETH's index in `allAssets` variable.\n * Reduces gas usage by redeem by caching that.\n */\n function cacheWETHAssetIndex() external onlyGovernor {\n uint256 assetCount = allAssets.length;\n for (uint256 i; i < assetCount; ++i) {\n if (allAssets[i] == weth) {\n wethAssetIndex = i;\n break;\n }\n }\n\n require(allAssets[wethAssetIndex] == weth, \"Invalid WETH Asset Index\");\n }\n\n // @inheritdoc VaultCore\n function mintForStrategy(uint256 amount)\n external\n override\n whenNotCapitalPaused\n {\n require(\n strategies[msg.sender].isSupported == true,\n \"Unsupported strategy\"\n );\n require(\n isMintWhitelistedStrategy[msg.sender] == true,\n \"Not whitelisted strategy\"\n );\n\n emit Mint(msg.sender, amount);\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, amount);\n }\n\n // @inheritdoc VaultCore\n function burnForStrategy(uint256 amount)\n external\n override\n whenNotCapitalPaused\n {\n require(\n strategies[msg.sender].isSupported == true,\n \"Unsupported strategy\"\n );\n require(\n isMintWhitelistedStrategy[msg.sender] == true,\n \"Not whitelisted strategy\"\n );\n\n emit Redeem(msg.sender, amount);\n\n // Burn OTokens\n oUSD.burn(msg.sender, amount);\n }\n\n // @inheritdoc VaultCore\n // slither-disable-start reentrancy-no-eth\n function _mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) internal virtual override {\n require(_asset == weth, \"Unsupported asset for minting\");\n require(_amount > 0, \"Amount must be greater than 0\");\n require(\n _amount >= _minimumOusdAmount,\n \"Mint amount lower than minimum\"\n );\n\n emit Mint(msg.sender, _amount);\n\n // Rebase must happen before any transfers occur.\n if (!rebasePaused && _amount >= rebaseThreshold) {\n // Stream any harvested rewards (WETH) that are available to the Vault\n IDripper(dripper).collect();\n\n _rebase();\n }\n\n // Mint oTokens\n oUSD.mint(msg.sender, _amount);\n\n // Transfer the deposited coins to the vault\n IERC20(_asset).safeTransferFrom(msg.sender, address(this), _amount);\n\n // Give priority to the withdrawal queue for the new WETH liquidity\n _addWithdrawalQueueLiquidity();\n\n // Auto-allocate if necessary\n if (_amount >= autoAllocateThreshold) {\n _allocate();\n }\n }\n\n // slither-disable-end reentrancy-no-eth\n\n // @inheritdoc VaultCore\n function _calculateRedeemOutputs(uint256 _amount)\n internal\n view\n virtual\n override\n returns (uint256[] memory outputs)\n {\n // Overrides `VaultCore._calculateRedeemOutputs` to redeem with only\n // WETH instead of LST-mix. Doesn't change the function signature\n // for backward compatibility\n\n // Calculate redeem fee\n if (redeemFeeBps > 0) {\n uint256 redeemFee = _amount.mulTruncateScale(redeemFeeBps, 1e4);\n _amount = _amount - redeemFee;\n }\n\n // Ensure that the WETH index is cached\n uint256 _wethAssetIndex = wethAssetIndex;\n require(\n allAssets[_wethAssetIndex] == weth,\n \"WETH Asset index not cached\"\n );\n\n outputs = new uint256[](allAssets.length);\n outputs[_wethAssetIndex] = _amount;\n }\n\n // @inheritdoc VaultCore\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n override\n {\n // Override `VaultCore._redeem` to simplify it. Gets rid of oracle\n // usage and looping through all assets for LST-mix redeem. Instead\n // does a simple WETH-only redeem.\n emit Redeem(msg.sender, _amount);\n\n if (_amount == 0) {\n return;\n }\n\n // Amount excluding fees\n // No fee for the strategist or the governor, makes it easier to do operations\n uint256 amountMinusFee = (msg.sender == strategistAddr || isGovernor())\n ? _amount\n : _calculateRedeemOutputs(_amount)[wethAssetIndex];\n\n require(\n amountMinusFee >= _minimumUnitAmount,\n \"Redeem amount lower than minimum\"\n );\n\n // Is there enough WETH in the Vault available after accounting for the withdrawal queue\n require(_wethAvailable() >= amountMinusFee, \"Liquidity error\");\n\n // Transfer WETH minus the fee to the redeemer\n IERC20(weth).safeTransfer(msg.sender, amountMinusFee);\n\n // Burn OETH from user (including fees)\n oUSD.burn(msg.sender, _amount);\n\n // Prevent insolvency\n _postRedeem(_amount);\n }\n\n /**\n * @notice Request an asynchronous withdrawal of WETH in exchange for OETH.\n * The OETH is burned on request and the WETH is transferred to the withdrawer on claim.\n * This request can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal this request's `queued` amount.\n * There is a minimum of 10 minutes before a request can be claimed. After that, the request just needs\n * enough WETH liquidity in the Vault to satisfy all the outstanding requests to that point in the queue.\n * OETH is converted to WETH at 1:1.\n * @param _amount Amount of OETH to burn.\n * @return requestId Unique ID for the withdrawal request\n * @return queued Cumulative total of all WETH queued including already claimed requests.\n */\n function requestWithdrawal(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256 requestId, uint256 queued)\n {\n require(withdrawalClaimDelay > 0, \"Async withdrawals not enabled\");\n\n // The check that the requester has enough OETH is done in to later burn call\n\n requestId = withdrawalQueueMetadata.nextWithdrawalIndex;\n queued = withdrawalQueueMetadata.queued + _amount;\n\n // Store the next withdrawal request\n withdrawalQueueMetadata.nextWithdrawalIndex = SafeCast.toUint128(\n requestId + 1\n );\n // Store the updated queued amount which reserves WETH in the withdrawal queue\n // and reduces the vault's total assets\n withdrawalQueueMetadata.queued = SafeCast.toUint128(queued);\n // Store the user's withdrawal request\n withdrawalRequests[requestId] = WithdrawalRequest({\n withdrawer: msg.sender,\n claimed: false,\n timestamp: uint40(block.timestamp),\n amount: SafeCast.toUint128(_amount),\n queued: SafeCast.toUint128(queued)\n });\n\n // Burn the user's OETH\n oUSD.burn(msg.sender, _amount);\n\n // Prevent withdrawal if the vault is solvent by more than the the allowed percentage\n _postRedeem(_amount);\n\n emit WithdrawalRequested(msg.sender, requestId, _amount, queued);\n }\n\n // slither-disable-start reentrancy-no-eth\n /**\n * @notice Claim a previously requested withdrawal once it is claimable.\n * This request can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal this request's `queued` amount and 10 minutes has passed.\n * If the requests is not claimable, the transaction will revert with `Queue pending liquidity`.\n * If the request is not older than 10 minutes, the transaction will revert with `Claim delay not met`.\n * OETH is converted to WETH at 1:1.\n * @param _requestId Unique ID for the withdrawal request\n * @return amount Amount of WETH transferred to the withdrawer\n */\n function claimWithdrawal(uint256 _requestId)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256 amount)\n {\n // Try and get more liquidity if there is not enough available\n if (\n withdrawalRequests[_requestId].queued >\n withdrawalQueueMetadata.claimable\n ) {\n // Stream any harvested rewards (WETH) that are available to the Vault\n IDripper(dripper).collect();\n\n // Add any WETH from the Dripper to the withdrawal queue\n _addWithdrawalQueueLiquidity();\n }\n\n amount = _claimWithdrawal(_requestId);\n\n // transfer WETH from the vault to the withdrawer\n IERC20(weth).safeTransfer(msg.sender, amount);\n\n // Prevent insolvency\n _postRedeem(amount);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @notice Claim a previously requested withdrawals once they are claimable.\n * This requests can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal each request's `queued` amount and 10 minutes has passed.\n * If one of the requests is not claimable, the whole transaction will revert with `Queue pending liquidity`.\n * If one of the requests is not older than 10 minutes,\n * the whole transaction will revert with `Claim delay not met`.\n * @param _requestIds Unique ID of each withdrawal request\n * @return amounts Amount of WETH received for each request\n * @return totalAmount Total amount of WETH transferred to the withdrawer\n */\n function claimWithdrawals(uint256[] calldata _requestIds)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256[] memory amounts, uint256 totalAmount)\n {\n // Just call the Dripper instead of looping through _requestIds to find the highest id\n // and checking it's queued amount is > the queue's claimable amount.\n\n // Stream any harvested rewards (WETH) that are available to the Vault\n IDripper(dripper).collect();\n\n // Add any WETH from the Dripper to the withdrawal queue\n _addWithdrawalQueueLiquidity();\n\n amounts = new uint256[](_requestIds.length);\n for (uint256 i; i < _requestIds.length; ++i) {\n amounts[i] = _claimWithdrawal(_requestIds[i]);\n totalAmount += amounts[i];\n }\n\n // transfer all the claimed WETH from the vault to the withdrawer\n IERC20(weth).safeTransfer(msg.sender, totalAmount);\n\n // Prevent insolvency\n _postRedeem(totalAmount);\n }\n\n function _claimWithdrawal(uint256 requestId)\n internal\n returns (uint256 amount)\n {\n require(withdrawalClaimDelay > 0, \"Async withdrawals not enabled\");\n\n // Load the structs from storage into memory\n WithdrawalRequest memory request = withdrawalRequests[requestId];\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n require(\n request.timestamp + withdrawalClaimDelay <= block.timestamp,\n \"Claim delay not met\"\n );\n // If there isn't enough reserved liquidity in the queue to claim\n require(request.queued <= queue.claimable, \"Queue pending liquidity\");\n require(request.withdrawer == msg.sender, \"Not requester\");\n require(request.claimed == false, \"Already claimed\");\n\n // Store the request as claimed\n withdrawalRequests[requestId].claimed = true;\n // Store the updated claimed amount\n withdrawalQueueMetadata.claimed = queue.claimed + request.amount;\n\n emit WithdrawalClaimed(msg.sender, requestId, request.amount);\n\n return request.amount;\n }\n\n /// @notice Collects harvested rewards from the Dripper as WETH then\n /// adds WETH to the withdrawal queue if there is a funding shortfall.\n /// @dev is called from the Native Staking strategy when validator withdrawals are processed.\n /// It also called before any WETH is allocated to a strategy.\n function addWithdrawalQueueLiquidity() external {\n // Stream any harvested rewards (WETH) that are available to the Vault\n IDripper(dripper).collect();\n\n _addWithdrawalQueueLiquidity();\n }\n\n /// @dev Adds WETH to the withdrawal queue if there is a funding shortfall.\n /// This assumes 1 WETH equal 1 OETH.\n function _addWithdrawalQueueLiquidity()\n internal\n returns (uint256 addedClaimable)\n {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // Check if the claimable WETH is less than the queued amount\n uint256 queueShortfall = queue.queued - queue.claimable;\n\n // No need to do anything is the withdrawal queue is full funded\n if (queueShortfall == 0) {\n return 0;\n }\n\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // Of the claimable withdrawal requests, how much is unclaimed?\n // That is, the amount of WETH that is currently allocated for the withdrawal queue\n uint256 allocatedWeth = queue.claimable - queue.claimed;\n\n // If there is no unallocated WETH then there is nothing to add to the queue\n if (wethBalance <= allocatedWeth) {\n return 0;\n }\n\n uint256 unallocatedWeth = wethBalance - allocatedWeth;\n\n // the new claimable amount is the smaller of the queue shortfall or unallocated weth\n addedClaimable = queueShortfall < unallocatedWeth\n ? queueShortfall\n : unallocatedWeth;\n uint256 newClaimable = queue.claimable + addedClaimable;\n\n // Store the new claimable amount back to storage\n withdrawalQueueMetadata.claimable = SafeCast.toUint128(newClaimable);\n\n // emit a WithdrawalClaimable event\n emit WithdrawalClaimable(newClaimable, addedClaimable);\n }\n\n /***************************************\n View Functions\n ****************************************/\n\n /// @dev Calculate how much WETH in the vault is not reserved for the withdrawal queue.\n // That is, it is available to be redeemed or deposited into a strategy.\n function _wethAvailable() internal view returns (uint256 wethAvailable) {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n uint256 outstandingWithdrawals = queue.queued - queue.claimed;\n\n // The amount of sitting in WETH in the vault\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // If there is not enough WETH in the vault to cover the outstanding withdrawals\n if (wethBalance <= outstandingWithdrawals) {\n return 0;\n }\n\n return wethBalance - outstandingWithdrawals;\n }\n\n /// @dev Get the balance of an asset held in Vault and all strategies\n /// less any WETH that is reserved for the withdrawal queue.\n /// WETH is the only asset that can return a non-zero balance.\n /// All other assets will return 0 even if there is some dust amounts left in the Vault.\n /// For example, there is 1 wei left of stETH in the OETH Vault but will return 0 in this function.\n ///\n /// If there is not enough WETH in the vault and all strategies to cover all outstanding\n /// withdrawal requests then return a WETH balance of 0\n function _checkBalance(address _asset)\n internal\n view\n override\n returns (uint256 balance)\n {\n if (_asset != weth) {\n return 0;\n }\n\n // Get the WETH in the vault and the strategies\n balance = super._checkBalance(_asset);\n\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // If the vault becomes insolvent enough that the total value in the vault and all strategies\n // is less than the outstanding withdrawals.\n // For example, there was a mass slashing event and most users request a withdrawal.\n if (balance + queue.claimed < queue.queued) {\n return 0;\n }\n\n // Need to remove WETH that is reserved for the withdrawal queue\n return balance + queue.claimed - queue.queued;\n }\n\n /**\n * @notice Allocate unallocated funds on Vault to strategies.\n **/\n function allocate() external override whenNotCapitalPaused nonReentrant {\n // Add any unallocated WETH to the withdrawal queue first\n _addWithdrawalQueueLiquidity();\n\n _allocate();\n }\n\n /// @dev Allocate WETH to the default WETH strategy if there is excess to the Vault buffer.\n /// This is called from either `mint` or `allocate` and assumes `_addWithdrawalQueueLiquidity`\n /// has been called before this function.\n function _allocate() internal override {\n // No need to do anything if no default strategy for WETH\n address depositStrategyAddr = assetDefaultStrategies[weth];\n if (depositStrategyAddr == address(0)) return;\n\n uint256 wethAvailableInVault = _wethAvailable();\n // No need to do anything if there isn't any WETH in the vault to allocate\n if (wethAvailableInVault == 0) return;\n\n // Calculate the target buffer for the vault using the total supply\n uint256 totalSupply = oUSD.totalSupply();\n uint256 targetBuffer = totalSupply.mulTruncate(vaultBuffer);\n\n // If available WETH in the Vault is below or equal the target buffer then there's nothing to allocate\n if (wethAvailableInVault <= targetBuffer) return;\n\n // The amount of assets to allocate to the default strategy\n uint256 allocateAmount = wethAvailableInVault - targetBuffer;\n\n IStrategy strategy = IStrategy(depositStrategyAddr);\n // Transfer WETH to the strategy and call the strategy's deposit function\n IERC20(weth).safeTransfer(address(strategy), allocateAmount);\n strategy.deposit(weth, allocateAmount);\n\n emit AssetAllocated(weth, depositStrategyAddr, allocateAmount);\n }\n\n /// @dev The total value of all WETH held by the vault and all its strategies\n /// less any WETH that is reserved for the withdrawal queue.\n ///\n // If there is not enough WETH in the vault and all strategies to cover all outstanding\n // withdrawal requests then return a total value of 0.\n function _totalValue() internal view override returns (uint256 value) {\n // As WETH is the only asset, just return the WETH balance\n return _checkBalance(weth);\n }\n\n /// @dev Only WETH is supported in the OETH Vault so return the WETH balance only\n /// Any ETH balances in the Vault will be ignored.\n /// Amounts from previously supported vault assets will also be ignored.\n /// For example, there is 1 wei left of stETH in the OETH Vault but is will be ignored.\n function _totalValueInVault()\n internal\n view\n override\n returns (uint256 value)\n {\n value = IERC20(weth).balanceOf(address(this));\n }\n}\n" + }, + "contracts/vault/OETHZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { ISfrxETH } from \"../interfaces/ISfrxETH.sol\";\n\ncontract OETHZapper {\n IERC20 public immutable oeth;\n IVault public immutable vault;\n\n IWETH9 public constant weth =\n IWETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);\n IERC20 public constant frxeth =\n IERC20(0x5E8422345238F34275888049021821E8E08CAa1f);\n ISfrxETH public constant sfrxeth =\n ISfrxETH(0xac3E018457B222d93114458476f3E3416Abbe38F);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(address _oeth, address _vault) {\n oeth = IERC20(_oeth);\n vault = IVault(_vault);\n\n weth.approve(address(_vault), type(uint256).max);\n frxeth.approve(address(_vault), type(uint256).max);\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return.\n * Will verify that the user is sent 1:1 for ETH.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return\n * Will verify that the user is sent 1:1 for ETH.\n * @return Amount of OETH sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n weth.deposit{ value: balance }();\n emit Zap(msg.sender, ETH_MARKER, balance);\n return _mint(address(weth), balance);\n }\n\n /**\n * @dev Deposit SFRXETH to the vault and receive OETH in return\n * @param amount Amount of SFRXETH to deposit\n * @param minOETH Minimum amount of OETH to receive\n * @return Amount of OETH sent to user\n */\n function depositSFRXETH(uint256 amount, uint256 minOETH)\n external\n returns (uint256)\n {\n sfrxeth.redeem(amount, address(this), msg.sender);\n emit Zap(msg.sender, address(sfrxeth), amount);\n return _mint(address(frxeth), minOETH);\n }\n\n /**\n * @dev Internal function to mint OETH from an asset\n * @param asset Address of asset for the vault to mint from\n * @param minOETH Minimum amount of OETH to for user to receive\n * @return Amount of OETH sent to user\n */\n function _mint(address asset, uint256 minOETH) internal returns (uint256) {\n uint256 toMint = IERC20(asset).balanceOf(address(this));\n vault.mint(asset, toMint, minOETH);\n uint256 mintedAmount = oeth.balanceOf(address(this));\n require(mintedAmount >= minOETH, \"Zapper: not enough minted\");\n require(oeth.transfer(msg.sender, mintedAmount));\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/OSonicVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"./OETHVaultAdmin.sol\";\n\n/**\n * @title Origin Sonic VaultAdmin contract on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonicVaultAdmin is OETHVaultAdmin {\n /// @param _wS Sonic's Wrapped S token\n constructor(address _wS) OETHVaultAdmin(_wS) {}\n\n /***************************************\n Asset Config\n ****************************************/\n\n /**\n * @notice Add a supported asset to the contract, i.e. one that can be to mint OTokens.\n * @dev Overridden to remove price provider integration\n * @param _asset Address of asset\n * @param _unitConversion 0 decimals, 1 exchange rate\n */\n function supportAsset(address _asset, uint8 _unitConversion)\n external\n override\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Asset already supported\");\n\n assets[_asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(_unitConversion),\n decimals: 0, // will be overridden in _cacheDecimals\n allowedOracleSlippageBps: 0 // 0% by default\n });\n\n _cacheDecimals(_asset);\n allAssets.push(_asset);\n\n emit AssetSupported(_asset);\n }\n}\n" + }, + "contracts/vault/OSonicVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultCore } from \"./OETHVaultCore.sol\";\n\n/**\n * @title Origin Sonic VaultCore contract on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonicVaultCore is OETHVaultCore {\n /// @param _wS Sonic's Wrapped S token\n constructor(address _wS) OETHVaultCore(_wS) {}\n\n /**\n * @notice Instant redeem is not supported on Sonic.\n * Use the asynchronous `requestWithdrawal` a `claimWithdrawal` instead.\n */\n function _redeem(uint256, uint256) internal override {\n revert(\"unsupported function\");\n }\n}\n" + }, + "contracts/vault/OSonicZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWrappedSonic } from \"../interfaces/sonic/IWrappedSonic.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\n\n/**\n * @title Zapper for Origin Sonic (OS) tokens\n * @author Origin Protocol Inc\n */\ncontract OSonicZapper {\n IERC20 public immutable OS;\n IERC4626 public immutable wOS;\n IVault public immutable vault;\n\n IWrappedSonic public constant wS =\n IWrappedSonic(0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(\n address _OS,\n address _wOS,\n address _vault\n ) {\n OS = IERC20(_OS);\n wOS = IERC4626(_wOS);\n vault = IVault(_vault);\n\n wS.approve(address(_vault), type(uint256).max);\n IERC20(_OS).approve(_wOS, type(uint256).max);\n }\n\n /**\n * @dev Deposit native S currency and receive Origin Sonic (OS) tokens in return.\n * Will verify that the user is sent 1:1 for S.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit native S currency and receive Origin Sonic (OS) tokens in return.\n * Will verify that the user is sent 1:1 for S.\n * @return Amount of Origin Sonic (OS) tokens sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap native S\n wS.deposit{ value: balance }();\n\n // Mint Origin Sonic (OS) with Wrapped Sonic (wS)\n return _mint(balance, msg.sender);\n }\n\n /**\n * @dev Deposit S and receive Wrapped Origin Sonic (wOS) in return\n * @param minReceived min amount of Wrapped Origin Sonic (wOS) to receive\n * @return Amount of Wrapped Origin Sonic (wOS) tokens sent to user\n */\n function depositSForWrappedTokens(uint256 minReceived)\n external\n payable\n returns (uint256)\n {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap S\n wS.deposit{ value: balance }();\n\n // Mint with Wrapped Sonic\n uint256 mintOS = _mint(balance, address(this));\n\n // Wrap Origin Sonic (OS) into Wrapped Origin Sonic (wOS)\n uint256 mintedWOS = wOS.deposit(mintOS, msg.sender);\n\n require(mintedWOS >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWOS;\n }\n\n /**\n * @dev Deposit Wrapped Sonic (wS) tokens and receive Wrapped Origin Sonic (wOS) tokens in return\n * @param wSAmount Amount of Wrapped Sonic (wS) to deposit\n * @param minReceived min amount of Wrapped Origin Sonic (wOS) token to receive\n * @return Amount of Wrapped Origin Sonic (wOS) tokens sent to user\n */\n function depositWSForWrappedTokens(uint256 wSAmount, uint256 minReceived)\n external\n returns (uint256)\n {\n // slither-disable-next-line unchecked-transfer unused-return\n wS.transferFrom(msg.sender, address(this), wSAmount);\n\n emit Zap(msg.sender, address(wS), wSAmount);\n\n // Mint with Wrapped Sonic (wS)\n uint256 mintedOS = _mint(wSAmount, address(this));\n\n // Wrap Origin Sonic (OS) tokens into Wrapped Origin Sonic (wOS) tokens\n uint256 mintedWOS = wOS.deposit(mintedOS, msg.sender);\n\n require(mintedWOS >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWOS;\n }\n\n /**\n * @dev Internal function to mint Origin Sonic (OS) with Wrapped S (wS)\n * @param minOS Minimum amount of Origin Sonic (OS) tokens the user can receive\n * @param recipient Address that receives the tokens\n * @return Amount of Origin Sonic (OS) tokens sent to the recipient\n */\n function _mint(uint256 minOS, address recipient)\n internal\n returns (uint256)\n {\n uint256 toMint = wS.balanceOf(address(this));\n vault.mint(address(wS), toMint, minOS);\n uint256 mintedAmount = OS.balanceOf(address(this));\n require(mintedAmount >= minOS, \"Zapper: not enough minted\");\n\n if (recipient != address(this)) {\n require(OS.transfer(recipient, mintedAmount));\n }\n\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/Vault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD VaultInitializer Contract\n * @notice The VaultInitializer sets up the initial contract.\n * @author Origin Protocol Inc\n */\nimport { VaultInitializer } from \"./VaultInitializer.sol\";\nimport { VaultAdmin } from \"./VaultAdmin.sol\";\n\ncontract Vault is VaultInitializer, VaultAdmin {}\n" + }, + "contracts/vault/VaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultAdmin contract\n * @notice The VaultAdmin contract makes configuration and admin calls on the vault.\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport \"./VaultStorage.sol\";\n\ncontract VaultAdmin is VaultStorage {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /***************************************\n Configuration\n ****************************************/\n\n /**\n * @notice Set address of price provider.\n * @param _priceProvider Address of price provider\n */\n function setPriceProvider(address _priceProvider) external onlyGovernor {\n priceProvider = _priceProvider;\n emit PriceProviderUpdated(_priceProvider);\n }\n\n /**\n * @notice Set a fee in basis points to be charged for a redeem.\n * @param _redeemFeeBps Basis point fee to be charged\n */\n function setRedeemFeeBps(uint256 _redeemFeeBps) external onlyGovernor {\n require(_redeemFeeBps <= 1000, \"Redeem fee should not be over 10%\");\n redeemFeeBps = _redeemFeeBps;\n emit RedeemFeeUpdated(_redeemFeeBps);\n }\n\n /**\n * @notice Set a buffer of assets to keep in the Vault to handle most\n * redemptions without needing to spend gas unwinding assets from a Strategy.\n * @param _vaultBuffer Percentage using 18 decimals. 100% = 1e18.\n */\n function setVaultBuffer(uint256 _vaultBuffer)\n external\n onlyGovernorOrStrategist\n {\n require(_vaultBuffer <= 1e18, \"Invalid value\");\n vaultBuffer = _vaultBuffer;\n emit VaultBufferUpdated(_vaultBuffer);\n }\n\n /**\n * @notice Sets the minimum amount of OTokens in a mint to trigger an\n * automatic allocation of funds afterwords.\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setAutoAllocateThreshold(uint256 _threshold)\n external\n onlyGovernor\n {\n autoAllocateThreshold = _threshold;\n emit AllocateThresholdUpdated(_threshold);\n }\n\n /**\n * @notice Set a minimum amount of OTokens in a mint or redeem that triggers a\n * rebase\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setRebaseThreshold(uint256 _threshold) external onlyGovernor {\n rebaseThreshold = _threshold;\n emit RebaseThresholdUpdated(_threshold);\n }\n\n /**\n * @notice Set address of Strategist\n * @param _address Address of Strategist\n */\n function setStrategistAddr(address _address) external onlyGovernor {\n strategistAddr = _address;\n emit StrategistUpdated(_address);\n }\n\n /**\n * @notice Set the default Strategy for an asset, i.e. the one which the asset\n will be automatically allocated to and withdrawn from\n * @param _asset Address of the asset\n * @param _strategy Address of the Strategy\n */\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external\n onlyGovernorOrStrategist\n {\n emit AssetDefaultStrategyUpdated(_asset, _strategy);\n // If its a zero address being passed for the strategy we are removing\n // the default strategy\n if (_strategy != address(0)) {\n // Make sure the strategy meets some criteria\n require(strategies[_strategy].isSupported, \"Strategy not approved\");\n IStrategy strategy = IStrategy(_strategy);\n require(assets[_asset].isSupported, \"Asset is not supported\");\n require(\n strategy.supportsAsset(_asset),\n \"Asset not supported by Strategy\"\n );\n }\n assetDefaultStrategies[_asset] = _strategy;\n }\n\n /**\n * @notice Set maximum amount of OTokens that can at any point be minted and deployed\n * to strategy (used only by ConvexOUSDMetaStrategy for now).\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setNetOusdMintForStrategyThreshold(uint256 _threshold)\n external\n onlyGovernor\n {\n /**\n * Because `netOusdMintedForStrategy` check in vault core works both ways\n * (positive and negative) the actual impact of the amount of OToken minted\n * could be double the threshold. E.g.:\n * - contract has threshold set to 100\n * - state of netOusdMinted is -90\n * - in effect it can mint 190 OToken and still be within limits\n *\n * We are somewhat mitigating this behaviour by resetting the netOusdMinted\n * counter whenever new threshold is set. So it can only move one threshold\n * amount in each direction. This also enables us to reduce the threshold\n * amount and not have problems with current netOusdMinted being near\n * limits on either side.\n */\n netOusdMintedForStrategy = 0;\n netOusdMintForStrategyThreshold = _threshold;\n emit NetOusdMintForStrategyThresholdChanged(_threshold);\n }\n\n /**\n * @notice Set the Dripper contract that streams harvested rewards to the vault.\n * @param _dripper Address of the Dripper contract.\n */\n function setDripper(address _dripper) external onlyGovernor {\n dripper = _dripper;\n emit DripperChanged(_dripper);\n }\n\n /**\n * @notice Changes the async withdrawal claim period for OETH & superOETHb\n * @param _delay Delay period (should be between 10 mins to 7 days).\n * Set to 0 to disable async withdrawals\n */\n function setWithdrawalClaimDelay(uint256 _delay) external onlyGovernor {\n require(\n _delay == 0 || (_delay >= 10 minutes && _delay <= 15 days),\n \"Invalid claim delay period\"\n );\n withdrawalClaimDelay = _delay;\n emit WithdrawalClaimDelayUpdated(_delay);\n }\n\n /**\n * @notice Set a yield streaming max rate. This spreads yield over\n * time if it is above the max rate.\n * @param yearlyApr in 1e18 notation. 3 * 1e18 = 3% APR\n */\n function setRebaseRateMax(uint256 yearlyApr)\n external\n onlyGovernorOrStrategist\n {\n // The old yield will be at the old rate\n IVault(address(this)).rebase();\n // Change the rate\n uint256 newPerSecond = yearlyApr / 100 / 365 days;\n require(newPerSecond <= MAX_REBASE_PER_SECOND, \"Rate too high\");\n rebasePerSecondMax = newPerSecond.toUint64();\n emit RebasePerSecondMaxChanged(newPerSecond);\n }\n\n /**\n * @notice Set the drip duration period\n * @param _dripDuration Time in seconds to target a constant yield rate\n */\n function setDripDuration(uint256 _dripDuration)\n external\n onlyGovernorOrStrategist\n {\n // The old yield will be at the old rate\n IVault(address(this)).rebase();\n dripDuration = _dripDuration.toUint64();\n emit DripDurationChanged(_dripDuration);\n }\n\n /***************************************\n Swaps\n ****************************************/\n\n /**\n * @notice Strategist swaps collateral assets sitting in the vault.\n * @param _fromAsset The token address of the asset being sold by the vault.\n * @param _toAsset The token address of the asset being purchased by the vault.\n * @param _fromAssetAmount The amount of assets being sold by the vault.\n * @param _minToAssetAmount The minimum amount of assets to be purchased.\n * @param _data implementation specific data. eg 1Inch swap data\n * @return toAssetAmount The amount of toAssets that was received from the swap\n */\n function swapCollateral(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n )\n external\n nonReentrant\n onlyGovernorOrStrategist\n returns (uint256 toAssetAmount)\n {\n toAssetAmount = _swapCollateral(\n _fromAsset,\n _toAsset,\n _fromAssetAmount,\n _minToAssetAmount,\n _data\n );\n }\n\n function _swapCollateral(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n ) internal virtual returns (uint256 toAssetAmount) {\n // Check fromAsset and toAsset are valid\n Asset memory fromAssetConfig = assets[_fromAsset];\n Asset memory toAssetConfig = assets[_toAsset];\n require(fromAssetConfig.isSupported, \"From asset is not supported\");\n require(toAssetConfig.isSupported, \"To asset is not supported\");\n\n // Load swap config into memory to avoid separate SLOADs\n SwapConfig memory config = swapConfig;\n\n // Scope a new block to remove toAssetBalBefore from the scope of swapCollateral.\n // This avoids a stack too deep error.\n {\n uint256 toAssetBalBefore = IERC20(_toAsset).balanceOf(\n address(this)\n );\n\n // Transfer from assets to the swapper contract\n IERC20(_fromAsset).safeTransfer(config.swapper, _fromAssetAmount);\n\n // Call to the Swapper contract to do the actual swap\n // The -1 is required for stETH which sometimes transfers 1 wei less than what was specified.\n // slither-disable-next-line unused-return\n ISwapper(config.swapper).swap(\n _fromAsset,\n _toAsset,\n _fromAssetAmount - 1,\n _minToAssetAmount,\n _data\n );\n\n // Compute the change in asset balance held by the Vault\n toAssetAmount =\n IERC20(_toAsset).balanceOf(address(this)) -\n toAssetBalBefore;\n }\n\n // Check the to assets returned is above slippage amount specified by the strategist\n require(\n toAssetAmount >= _minToAssetAmount,\n \"Strategist slippage limit\"\n );\n\n // Scope a new block to remove minOracleToAssetAmount from the scope of swapCollateral.\n // This avoids a stack too deep error.\n {\n // Check the slippage against the Oracle in case the strategist made a mistake or has become malicious.\n // to asset amount = from asset amount * from asset price / to asset price\n uint256 minOracleToAssetAmount = (_fromAssetAmount *\n (1e4 - fromAssetConfig.allowedOracleSlippageBps) *\n IOracle(priceProvider).price(_fromAsset)) /\n (IOracle(priceProvider).price(_toAsset) *\n (1e4 + toAssetConfig.allowedOracleSlippageBps));\n\n // Scale both sides up to 18 decimals to compare\n require(\n toAssetAmount.scaleBy(18, toAssetConfig.decimals) >=\n minOracleToAssetAmount.scaleBy(\n 18,\n fromAssetConfig.decimals\n ),\n \"Oracle slippage limit exceeded\"\n );\n }\n\n // Check the vault's total value hasn't gone below the OToken total supply\n // by more than the allowed percentage.\n require(\n IVault(address(this)).totalValue() >=\n (oUSD.totalSupply() * ((1e4 - config.allowedUndervalueBps))) /\n 1e4,\n \"Allowed value < supply\"\n );\n\n emit Swapped(_fromAsset, _toAsset, _fromAssetAmount, toAssetAmount);\n }\n\n /***************************************\n Swap Config\n ****************************************/\n\n /**\n * @notice Set the contract the performs swaps of collateral assets.\n * @param _swapperAddr Address of the Swapper contract that implements the ISwapper interface.\n */\n function setSwapper(address _swapperAddr) external onlyGovernor {\n swapConfig.swapper = _swapperAddr;\n emit SwapperChanged(_swapperAddr);\n }\n\n /// @notice Contract that swaps the vault's collateral assets\n function swapper() external view returns (address swapper_) {\n swapper_ = swapConfig.swapper;\n }\n\n /**\n * @notice Set max allowed percentage the vault total value can drop below the OToken total supply in basis points\n * when executing collateral swaps.\n * @param _basis Percentage in basis points. eg 100 == 1%\n */\n function setSwapAllowedUndervalue(uint16 _basis) external onlyGovernor {\n require(_basis < 10001, \"Invalid basis points\");\n swapConfig.allowedUndervalueBps = _basis;\n emit SwapAllowedUndervalueChanged(_basis);\n }\n\n /**\n * @notice Max allowed percentage the vault total value can drop below the OToken total supply in basis points\n * when executing a collateral swap.\n * For example 100 == 1%\n * @return value Percentage in basis points.\n */\n function allowedSwapUndervalue() external view returns (uint256 value) {\n value = swapConfig.allowedUndervalueBps;\n }\n\n /**\n * @notice Set the allowed slippage from the Oracle price for collateral asset swaps.\n * @param _asset Address of the asset token.\n * @param _allowedOracleSlippageBps allowed slippage from Oracle in basis points. eg 20 = 0.2%. Max 10%.\n */\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external\n onlyGovernor\n {\n require(assets[_asset].isSupported, \"Asset not supported\");\n require(_allowedOracleSlippageBps < 1000, \"Slippage too high\");\n\n assets[_asset].allowedOracleSlippageBps = _allowedOracleSlippageBps;\n\n emit SwapSlippageChanged(_asset, _allowedOracleSlippageBps);\n }\n\n /***************************************\n Asset Config\n ****************************************/\n\n /**\n * @notice Add a supported asset to the contract, i.e. one that can be\n * to mint OTokens.\n * @param _asset Address of asset\n */\n function supportAsset(address _asset, uint8 _unitConversion)\n external\n virtual\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Asset already supported\");\n\n assets[_asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(_unitConversion),\n decimals: 0, // will be overridden in _cacheDecimals\n allowedOracleSlippageBps: 0 // 0% by default\n });\n\n _cacheDecimals(_asset);\n allAssets.push(_asset);\n\n // Verify that our oracle supports the asset\n // slither-disable-next-line unused-return\n IOracle(priceProvider).price(_asset);\n\n emit AssetSupported(_asset);\n }\n\n /**\n * @notice Remove a supported asset from the Vault\n * @param _asset Address of asset\n */\n function removeAsset(address _asset) external onlyGovernor {\n require(assets[_asset].isSupported, \"Asset not supported\");\n\n // 1e13 for 18 decimals. And 10 for 6 decimals\n uint256 maxDustBalance = uint256(1e13).scaleBy(\n assets[_asset].decimals,\n 18\n );\n\n require(\n IVault(address(this)).checkBalance(_asset) <= maxDustBalance,\n \"Vault still holds asset\"\n );\n\n uint256 assetsCount = allAssets.length;\n uint256 assetIndex = assetsCount; // initialize at invalid index\n for (uint256 i = 0; i < assetsCount; ++i) {\n if (allAssets[i] == _asset) {\n assetIndex = i;\n break;\n }\n }\n\n // Note: If asset is not found in `allAssets`, the following line\n // will revert with an out-of-bound error. However, there's no\n // reason why an asset would have `Asset.isSupported = true` but\n // not exist in `allAssets`.\n\n // Update allAssets array\n allAssets[assetIndex] = allAssets[assetsCount - 1];\n allAssets.pop();\n\n // Reset default strategy\n assetDefaultStrategies[_asset] = address(0);\n emit AssetDefaultStrategyUpdated(_asset, address(0));\n\n // Remove asset from storage\n delete assets[_asset];\n\n emit AssetRemoved(_asset);\n }\n\n /**\n * @notice Cache decimals on OracleRouter for a particular asset. This action\n * is required before that asset's price can be accessed.\n * @param _asset Address of asset token\n */\n function cacheDecimals(address _asset) external onlyGovernor {\n _cacheDecimals(_asset);\n }\n\n /***************************************\n Strategy Config\n ****************************************/\n\n /**\n * @notice Add a strategy to the Vault.\n * @param _addr Address of the strategy to add\n */\n function approveStrategy(address _addr) external onlyGovernor {\n require(!strategies[_addr].isSupported, \"Strategy already approved\");\n strategies[_addr] = Strategy({ isSupported: true, _deprecated: 0 });\n allStrategies.push(_addr);\n emit StrategyApproved(_addr);\n }\n\n /**\n * @notice Remove a strategy from the Vault.\n * @param _addr Address of the strategy to remove\n */\n\n function removeStrategy(address _addr) external onlyGovernor {\n require(strategies[_addr].isSupported, \"Strategy not approved\");\n\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n require(\n assetDefaultStrategies[allAssets[i]] != _addr,\n \"Strategy is default for an asset\"\n );\n }\n\n // Initialize strategyIndex with out of bounds result so function will\n // revert if no valid index found\n uint256 stratCount = allStrategies.length;\n uint256 strategyIndex = stratCount;\n for (uint256 i = 0; i < stratCount; ++i) {\n if (allStrategies[i] == _addr) {\n strategyIndex = i;\n break;\n }\n }\n\n if (strategyIndex < stratCount) {\n allStrategies[strategyIndex] = allStrategies[stratCount - 1];\n allStrategies.pop();\n\n // Mark the strategy as not supported\n strategies[_addr].isSupported = false;\n\n // Withdraw all assets\n IStrategy strategy = IStrategy(_addr);\n strategy.withdrawAll();\n\n emit StrategyRemoved(_addr);\n }\n }\n\n /***************************************\n Strategies\n ****************************************/\n\n /**\n * @notice Deposit multiple assets from the vault into the strategy.\n * @param _strategyToAddress Address of the Strategy to deposit assets into.\n * @param _assets Array of asset address that will be deposited into the strategy.\n * @param _amounts Array of amounts of each corresponding asset to deposit.\n */\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external onlyGovernorOrStrategist nonReentrant {\n _depositToStrategy(_strategyToAddress, _assets, _amounts);\n }\n\n function _depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal virtual {\n require(\n strategies[_strategyToAddress].isSupported,\n \"Invalid to Strategy\"\n );\n require(_assets.length == _amounts.length, \"Parameter length mismatch\");\n\n uint256 assetCount = _assets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n address assetAddr = _assets[i];\n require(\n IStrategy(_strategyToAddress).supportsAsset(assetAddr),\n \"Asset unsupported\"\n );\n // Send required amount of funds to the strategy\n IERC20(assetAddr).safeTransfer(_strategyToAddress, _amounts[i]);\n }\n\n // Deposit all the funds that have been sent to the strategy\n IStrategy(_strategyToAddress).depositAll();\n }\n\n /**\n * @notice Withdraw multiple assets from the strategy to the vault.\n * @param _strategyFromAddress Address of the Strategy to withdraw assets from.\n * @param _assets Array of asset address that will be withdrawn from the strategy.\n * @param _amounts Array of amounts of each corresponding asset to withdraw.\n */\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external onlyGovernorOrStrategist nonReentrant {\n _withdrawFromStrategy(\n address(this),\n _strategyFromAddress,\n _assets,\n _amounts\n );\n }\n\n /**\n * @param _recipient can either be a strategy or the Vault\n */\n function _withdrawFromStrategy(\n address _recipient,\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal virtual {\n require(\n strategies[_strategyFromAddress].isSupported,\n \"Invalid from Strategy\"\n );\n require(_assets.length == _amounts.length, \"Parameter length mismatch\");\n\n uint256 assetCount = _assets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n // Withdraw from Strategy to the recipient\n IStrategy(_strategyFromAddress).withdraw(\n _recipient,\n _assets[i],\n _amounts[i]\n );\n }\n }\n\n /**\n * @notice Sets the maximum allowable difference between\n * total supply and backing assets' value.\n */\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external onlyGovernor {\n maxSupplyDiff = _maxSupplyDiff;\n emit MaxSupplyDiffChanged(_maxSupplyDiff);\n }\n\n /**\n * @notice Sets the trusteeAddress that can receive a portion of yield.\n * Setting to the zero address disables this feature.\n */\n function setTrusteeAddress(address _address) external onlyGovernor {\n trusteeAddress = _address;\n emit TrusteeAddressChanged(_address);\n }\n\n /**\n * @notice Sets the TrusteeFeeBps to the percentage of yield that should be\n * received in basis points.\n */\n function setTrusteeFeeBps(uint256 _basis) external onlyGovernor {\n require(_basis <= 5000, \"basis cannot exceed 50%\");\n trusteeFeeBps = _basis;\n emit TrusteeFeeBpsChanged(_basis);\n }\n\n /**\n * @notice Set OToken Metapool strategy\n * @param _ousdMetaStrategy Address of OToken metapool strategy\n */\n function setOusdMetaStrategy(address _ousdMetaStrategy)\n external\n onlyGovernor\n {\n ousdMetaStrategy = _ousdMetaStrategy;\n emit OusdMetaStrategyUpdated(_ousdMetaStrategy);\n }\n\n /***************************************\n Pause\n ****************************************/\n\n /**\n * @notice Set the deposit paused flag to true to prevent rebasing.\n */\n function pauseRebase() external onlyGovernorOrStrategist {\n rebasePaused = true;\n emit RebasePaused();\n }\n\n /**\n * @notice Set the deposit paused flag to true to allow rebasing.\n */\n function unpauseRebase() external onlyGovernorOrStrategist {\n rebasePaused = false;\n emit RebaseUnpaused();\n }\n\n /**\n * @notice Set the deposit paused flag to true to prevent capital movement.\n */\n function pauseCapital() external onlyGovernorOrStrategist {\n capitalPaused = true;\n emit CapitalPaused();\n }\n\n /**\n * @notice Set the deposit paused flag to false to enable capital movement.\n */\n function unpauseCapital() external onlyGovernorOrStrategist {\n capitalPaused = false;\n emit CapitalUnpaused();\n }\n\n /***************************************\n Utils\n ****************************************/\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Only unsupported assets\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /***************************************\n Strategies Admin\n ****************************************/\n\n /**\n * @notice Withdraws all assets from the strategy and sends assets to the Vault.\n * @param _strategyAddr Strategy address.\n */\n function withdrawAllFromStrategy(address _strategyAddr)\n external\n onlyGovernorOrStrategist\n {\n _withdrawAllFromStrategy(_strategyAddr);\n }\n\n function _withdrawAllFromStrategy(address _strategyAddr) internal virtual {\n require(\n strategies[_strategyAddr].isSupported,\n \"Strategy is not supported\"\n );\n IStrategy strategy = IStrategy(_strategyAddr);\n strategy.withdrawAll();\n }\n\n /**\n * @notice Withdraws all assets from all the strategies and sends assets to the Vault.\n */\n function withdrawAllFromStrategies() external onlyGovernorOrStrategist {\n _withdrawAllFromStrategies();\n }\n\n function _withdrawAllFromStrategies() internal virtual {\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n IStrategy(allStrategies[i]).withdrawAll();\n }\n }\n\n /***************************************\n Utils\n ****************************************/\n\n function _cacheDecimals(address token) internal {\n Asset storage tokenAsset = assets[token];\n if (tokenAsset.decimals != 0) {\n return;\n }\n uint8 decimals = IBasicToken(token).decimals();\n require(decimals >= 6 && decimals <= 18, \"Unexpected precision\");\n tokenAsset.decimals = decimals;\n }\n}\n" + }, + "contracts/vault/VaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultCore contract\n * @notice The Vault contract stores assets. On a deposit, OTokens will be minted\n and sent to the depositor. On a withdrawal, OTokens will be burned and\n assets will be sent to the withdrawer. The Vault accepts deposits of\n interest from yield bearing strategies which will modify the supply\n of OTokens.\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IGetExchangeRateToken } from \"../interfaces/IGetExchangeRateToken.sol\";\nimport { IDripper } from \"../interfaces/IDripper.sol\";\n\nimport \"./VaultInitializer.sol\";\n\ncontract VaultCore is VaultInitializer {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n /// @dev max signed int\n uint256 internal constant MAX_INT = uint256(type(int256).max);\n\n /**\n * @dev Verifies that the rebasing is not paused.\n */\n modifier whenNotRebasePaused() {\n require(!rebasePaused, \"Rebasing paused\");\n _;\n }\n\n /**\n * @dev Verifies that the deposits are not paused.\n */\n modifier whenNotCapitalPaused() {\n require(!capitalPaused, \"Capital paused\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the AMO strategy.\n */\n modifier onlyOusdMetaStrategy() {\n require(\n msg.sender == ousdMetaStrategy,\n \"Caller is not the OUSD meta strategy\"\n );\n _;\n }\n\n /**\n * @notice Deposit a supported asset and mint OTokens.\n * @param _asset Address of the asset being deposited\n * @param _amount Amount of the asset being deposited\n * @param _minimumOusdAmount Minimum OTokens to mint\n */\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external whenNotCapitalPaused nonReentrant {\n _mint(_asset, _amount, _minimumOusdAmount);\n }\n\n /**\n * @dev Deposit a supported asset and mint OTokens.\n * @param _asset Address of the asset being deposited\n * @param _amount Amount of the asset being deposited\n * @param _minimumOusdAmount Minimum OTokens to mint\n */\n function _mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) internal virtual {\n require(assets[_asset].isSupported, \"Asset is not supported\");\n require(_amount > 0, \"Amount must be greater than 0\");\n\n uint256 units = _toUnits(_amount, _asset);\n uint256 unitPrice = _toUnitPrice(_asset, true);\n uint256 priceAdjustedDeposit = (units * unitPrice) / 1e18;\n\n if (_minimumOusdAmount > 0) {\n require(\n priceAdjustedDeposit >= _minimumOusdAmount,\n \"Mint amount lower than minimum\"\n );\n }\n\n emit Mint(msg.sender, priceAdjustedDeposit);\n\n // Rebase must happen before any transfers occur.\n if (priceAdjustedDeposit >= rebaseThreshold && !rebasePaused) {\n if (dripper != address(0)) {\n // Stream any harvested rewards that are available\n IDripper(dripper).collect();\n }\n _rebase();\n }\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, priceAdjustedDeposit);\n\n // Transfer the deposited coins to the vault\n IERC20 asset = IERC20(_asset);\n asset.safeTransferFrom(msg.sender, address(this), _amount);\n\n if (priceAdjustedDeposit >= autoAllocateThreshold) {\n _allocate();\n }\n }\n\n /**\n * @notice Mint OTokens for a Metapool Strategy\n * @param _amount Amount of the asset being deposited\n *\n * Notice: can't use `nonReentrant` modifier since the `mint` function can\n * call `allocate`, and that can trigger `ConvexOUSDMetaStrategy` to call this function\n * while the execution of the `mint` has not yet completed -> causing a `nonReentrant` collision.\n *\n * Also important to understand is that this is a limitation imposed by the test suite.\n * Production / mainnet contracts should never be configured in a way where mint/redeem functions\n * that are moving funds between the Vault and end user wallets can influence strategies\n * utilizing this function.\n */\n function mintForStrategy(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n onlyOusdMetaStrategy\n {\n require(_amount < MAX_INT, \"Amount too high\");\n\n emit Mint(msg.sender, _amount);\n\n // safe to cast because of the require check at the beginning of the function\n netOusdMintedForStrategy += int256(_amount);\n\n require(\n abs(netOusdMintedForStrategy) < netOusdMintForStrategyThreshold,\n \"Minted ousd surpassed netOusdMintForStrategyThreshold.\"\n );\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, _amount);\n }\n\n // In memoriam\n\n /**\n * @notice Withdraw a supported asset and burn OTokens.\n * @param _amount Amount of OTokens to burn\n * @param _minimumUnitAmount Minimum stablecoin units to receive in return\n */\n function redeem(uint256 _amount, uint256 _minimumUnitAmount)\n external\n whenNotCapitalPaused\n nonReentrant\n {\n _redeem(_amount, _minimumUnitAmount);\n }\n\n /**\n * @notice Withdraw a supported asset and burn OTokens.\n * @param _amount Amount of OTokens to burn\n * @param _minimumUnitAmount Minimum stablecoin units to receive in return\n */\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n {\n // Calculate redemption outputs\n uint256[] memory outputs = _calculateRedeemOutputs(_amount);\n\n emit Redeem(msg.sender, _amount);\n\n // Send outputs\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n if (outputs[i] == 0) continue;\n\n address assetAddr = allAssets[i];\n\n if (IERC20(assetAddr).balanceOf(address(this)) >= outputs[i]) {\n // Use Vault funds first if sufficient\n IERC20(assetAddr).safeTransfer(msg.sender, outputs[i]);\n } else {\n address strategyAddr = assetDefaultStrategies[assetAddr];\n if (strategyAddr != address(0)) {\n // Nothing in Vault, but something in Strategy, send from there\n IStrategy strategy = IStrategy(strategyAddr);\n strategy.withdraw(msg.sender, assetAddr, outputs[i]);\n } else {\n // Cant find funds anywhere\n revert(\"Liquidity error\");\n }\n }\n }\n\n if (_minimumUnitAmount > 0) {\n uint256 unitTotal = 0;\n for (uint256 i = 0; i < outputs.length; ++i) {\n unitTotal += _toUnits(outputs[i], allAssets[i]);\n }\n require(\n unitTotal >= _minimumUnitAmount,\n \"Redeem amount lower than minimum\"\n );\n }\n\n oUSD.burn(msg.sender, _amount);\n\n _postRedeem(_amount);\n }\n\n function _postRedeem(uint256 _amount) internal {\n // Until we can prove that we won't affect the prices of our assets\n // by withdrawing them, this should be here.\n // It's possible that a strategy was off on its asset total, perhaps\n // a reward token sold for more or for less than anticipated.\n uint256 totalUnits = 0;\n if (_amount >= rebaseThreshold && !rebasePaused) {\n totalUnits = _rebase();\n } else {\n totalUnits = _totalValue();\n }\n\n // Check that the OTokens are backed by enough assets\n if (maxSupplyDiff > 0) {\n // If there are more outstanding withdrawal requests than assets in the vault and strategies\n // then the available assets will be negative and totalUnits will be rounded up to zero.\n // As we don't know the exact shortfall amount, we will reject all redeem and withdrawals\n require(totalUnits > 0, \"Too many outstanding requests\");\n\n // Allow a max difference of maxSupplyDiff% between\n // backing assets value and OUSD total supply\n uint256 diff = oUSD.totalSupply().divPrecisely(totalUnits);\n require(\n (diff > 1e18 ? diff - 1e18 : 1e18 - diff) <= maxSupplyDiff,\n \"Backing supply liquidity error\"\n );\n }\n }\n\n /**\n * @notice Burn OTokens for Metapool Strategy\n * @param _amount Amount of OUSD to burn\n *\n * @dev Notice: can't use `nonReentrant` modifier since the `redeem` function could\n * require withdrawal on `ConvexOUSDMetaStrategy` and that one can call `burnForStrategy`\n * while the execution of the `redeem` has not yet completed -> causing a `nonReentrant` collision.\n *\n * Also important to understand is that this is a limitation imposed by the test suite.\n * Production / mainnet contracts should never be configured in a way where mint/redeem functions\n * that are moving funds between the Vault and end user wallets can influence strategies\n * utilizing this function.\n */\n function burnForStrategy(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n onlyOusdMetaStrategy\n {\n require(_amount < MAX_INT, \"Amount too high\");\n\n emit Redeem(msg.sender, _amount);\n\n // safe to cast because of the require check at the beginning of the function\n netOusdMintedForStrategy -= int256(_amount);\n\n require(\n abs(netOusdMintedForStrategy) < netOusdMintForStrategyThreshold,\n \"Attempting to burn too much OUSD.\"\n );\n\n // Burn OTokens\n oUSD.burn(msg.sender, _amount);\n }\n\n /**\n * @notice Allocate unallocated funds on Vault to strategies.\n **/\n function allocate() external virtual whenNotCapitalPaused nonReentrant {\n _allocate();\n }\n\n /**\n * @dev Allocate unallocated funds on Vault to strategies.\n **/\n function _allocate() internal virtual {\n uint256 vaultValue = _totalValueInVault();\n // Nothing in vault to allocate\n if (vaultValue == 0) return;\n uint256 strategiesValue = _totalValueInStrategies();\n // We have a method that does the same as this, gas optimisation\n uint256 calculatedTotalValue = vaultValue + strategiesValue;\n\n // We want to maintain a buffer on the Vault so calculate a percentage\n // modifier to multiply each amount being allocated by to enforce the\n // vault buffer\n uint256 vaultBufferModifier;\n if (strategiesValue == 0) {\n // Nothing in Strategies, allocate 100% minus the vault buffer to\n // strategies\n vaultBufferModifier = uint256(1e18) - vaultBuffer;\n } else {\n vaultBufferModifier =\n (vaultBuffer * calculatedTotalValue) /\n vaultValue;\n if (1e18 > vaultBufferModifier) {\n // E.g. 1e18 - (1e17 * 10e18)/5e18 = 8e17\n // (5e18 * 8e17) / 1e18 = 4e18 allocated from Vault\n vaultBufferModifier = uint256(1e18) - vaultBufferModifier;\n } else {\n // We need to let the buffer fill\n return;\n }\n }\n if (vaultBufferModifier == 0) return;\n\n // Iterate over all assets in the Vault and allocate to the appropriate\n // strategy\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(allAssets[i]);\n uint256 assetBalance = asset.balanceOf(address(this));\n // No balance, nothing to do here\n if (assetBalance == 0) continue;\n\n // Multiply the balance by the vault buffer modifier and truncate\n // to the scale of the asset decimals\n uint256 allocateAmount = assetBalance.mulTruncate(\n vaultBufferModifier\n );\n\n address depositStrategyAddr = assetDefaultStrategies[\n address(asset)\n ];\n\n if (depositStrategyAddr != address(0) && allocateAmount > 0) {\n IStrategy strategy = IStrategy(depositStrategyAddr);\n // Transfer asset to Strategy and call deposit method to\n // mint or take required action\n asset.safeTransfer(address(strategy), allocateAmount);\n strategy.deposit(address(asset), allocateAmount);\n emit AssetAllocated(\n address(asset),\n depositStrategyAddr,\n allocateAmount\n );\n }\n }\n }\n\n /**\n * @notice Calculate the total value of assets held by the Vault and all\n * strategies and update the supply of OTokens.\n */\n function rebase() external virtual nonReentrant {\n _rebase();\n }\n\n /**\n * @dev Calculate the total value of assets held by the Vault and all\n * strategies and update the supply of OTokens, optionally sending a\n * portion of the yield to the trustee.\n * @return totalUnits Total balance of Vault in units\n */\n function _rebase() internal whenNotRebasePaused returns (uint256) {\n uint256 supply = oUSD.totalSupply();\n uint256 vaultValue = _totalValue();\n // If no supply yet, do not rebase\n if (supply == 0) {\n return vaultValue;\n }\n\n // Calculate yield and new supply\n (uint256 yield, uint256 targetRate) = _nextYield(supply, vaultValue);\n uint256 newSupply = supply + yield;\n // Only rebase upwards and if we have enough backing funds\n if (newSupply <= supply || newSupply > vaultValue) {\n return vaultValue;\n }\n\n rebasePerSecondTarget = uint64(_min(targetRate, type(uint64).max));\n lastRebase = uint64(block.timestamp); // Intentional cast\n\n // Fee collection on yield\n address _trusteeAddress = trusteeAddress; // gas savings\n uint256 fee = 0;\n if (_trusteeAddress != address(0)) {\n fee = (yield * trusteeFeeBps) / 1e4;\n if (fee > 0) {\n require(fee < yield, \"Fee must not be greater than yield\");\n oUSD.mint(_trusteeAddress, fee);\n }\n }\n emit YieldDistribution(_trusteeAddress, yield, fee);\n\n // Only ratchet OToken supply upwards\n // Final check uses latest totalSupply\n if (newSupply > oUSD.totalSupply()) {\n oUSD.changeSupply(newSupply);\n }\n return vaultValue;\n }\n\n /**\n * @notice Calculates the amount that would rebase at at next rebase.\n * This is before any fees.\n * @return yield amount of expected yield\n */\n function previewYield() external view returns (uint256 yield) {\n (yield, ) = _nextYield(oUSD.totalSupply(), _totalValue());\n return yield;\n }\n\n function _nextYield(uint256 supply, uint256 vaultValue)\n internal\n view\n virtual\n returns (uint256 yield, uint256 targetRate)\n {\n uint256 nonRebasing = oUSD.nonRebasingSupply();\n uint256 rebasing = supply - nonRebasing;\n uint256 elapsed = block.timestamp - lastRebase;\n targetRate = rebasePerSecondTarget;\n\n if (\n elapsed == 0 || // Yield only once per block.\n rebasing == 0 || // No yield if there are no rebasing tokens to give it to.\n supply > vaultValue || // No yield if we do not have yield to give.\n block.timestamp >= type(uint64).max // No yield if we are too far in the future to calculate it correctly.\n ) {\n return (0, targetRate);\n }\n\n // Start with the full difference available\n yield = vaultValue - supply;\n\n // Cap via optional automatic duration smoothing\n uint256 _dripDuration = dripDuration;\n if (_dripDuration > 1) {\n // If we are able to sustain an increased drip rate for\n // double the duration, then increase the target drip rate\n targetRate = _max(targetRate, yield / (_dripDuration * 2));\n // If we cannot sustain the target rate any more,\n // then rebase what we can, and reduce the target\n targetRate = _min(targetRate, yield / _dripDuration);\n // drip at the new target rate\n yield = _min(yield, targetRate * elapsed);\n }\n\n // Cap per second. elapsed is not 1e18 denominated\n yield = _min(yield, (rebasing * elapsed * rebasePerSecondMax) / 1e18);\n\n // Cap at a hard max per rebase, to avoid long durations resulting in huge rebases\n yield = _min(yield, (rebasing * MAX_REBASE) / 1e18);\n\n return (yield, targetRate);\n }\n\n /**\n * @notice Determine the total value of assets held by the vault and its\n * strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function totalValue() external view virtual returns (uint256 value) {\n value = _totalValue();\n }\n\n /**\n * @dev Internal Calculate the total value of the assets held by the\n * vault and its strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValue() internal view virtual returns (uint256 value) {\n return _totalValueInVault() + _totalValueInStrategies();\n }\n\n /**\n * @dev Internal to calculate total value of all assets held in Vault.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInVault()\n internal\n view\n virtual\n returns (uint256 value)\n {\n uint256 assetCount = allAssets.length;\n for (uint256 y; y < assetCount; ++y) {\n address assetAddr = allAssets[y];\n uint256 balance = IERC20(assetAddr).balanceOf(address(this));\n if (balance > 0) {\n value += _toUnits(balance, assetAddr);\n }\n }\n }\n\n /**\n * @dev Internal to calculate total value of all assets held in Strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInStrategies() internal view returns (uint256 value) {\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n value = value + _totalValueInStrategy(allStrategies[i]);\n }\n }\n\n /**\n * @dev Internal to calculate total value of all assets held by strategy.\n * @param _strategyAddr Address of the strategy\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInStrategy(address _strategyAddr)\n internal\n view\n returns (uint256 value)\n {\n IStrategy strategy = IStrategy(_strategyAddr);\n uint256 assetCount = allAssets.length;\n for (uint256 y; y < assetCount; ++y) {\n address assetAddr = allAssets[y];\n if (strategy.supportsAsset(assetAddr)) {\n uint256 balance = strategy.checkBalance(assetAddr);\n if (balance > 0) {\n value += _toUnits(balance, assetAddr);\n }\n }\n }\n }\n\n /**\n * @notice Get the balance of an asset held in Vault and all strategies.\n * @param _asset Address of asset\n * @return uint256 Balance of asset in decimals of asset\n */\n function checkBalance(address _asset) external view returns (uint256) {\n return _checkBalance(_asset);\n }\n\n /**\n * @notice Get the balance of an asset held in Vault and all strategies.\n * @param _asset Address of asset\n * @return balance Balance of asset in decimals of asset\n */\n function _checkBalance(address _asset)\n internal\n view\n virtual\n returns (uint256 balance)\n {\n IERC20 asset = IERC20(_asset);\n balance = asset.balanceOf(address(this));\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n IStrategy strategy = IStrategy(allStrategies[i]);\n if (strategy.supportsAsset(_asset)) {\n balance = balance + strategy.checkBalance(_asset);\n }\n }\n }\n\n /**\n * @notice Calculate the outputs for a redeem function, i.e. the mix of\n * coins that will be returned\n */\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory)\n {\n return _calculateRedeemOutputs(_amount);\n }\n\n /**\n * @dev Calculate the outputs for a redeem function, i.e. the mix of\n * coins that will be returned.\n * @return outputs Array of amounts respective to the supported assets\n */\n function _calculateRedeemOutputs(uint256 _amount)\n internal\n view\n virtual\n returns (uint256[] memory outputs)\n {\n // We always give out coins in proportion to how many we have,\n // Now if all coins were the same value, this math would easy,\n // just take the percentage of each coin, and multiply by the\n // value to be given out. But if coins are worth more than $1,\n // then we would end up handing out too many coins. We need to\n // adjust by the total value of coins.\n //\n // To do this, we total up the value of our coins, by their\n // percentages. Then divide what we would otherwise give out by\n // this number.\n //\n // Let say we have 100 DAI at $1.06 and 200 USDT at $1.00.\n // So for every 1 DAI we give out, we'll be handing out 2 USDT\n // Our total output ratio is: 33% * 1.06 + 66% * 1.00 = 1.02\n //\n // So when calculating the output, we take the percentage of\n // each coin, times the desired output value, divided by the\n // totalOutputRatio.\n //\n // For example, withdrawing: 30 OUSD:\n // DAI 33% * 30 / 1.02 = 9.80 DAI\n // USDT = 66 % * 30 / 1.02 = 19.60 USDT\n //\n // Checking these numbers:\n // 9.80 DAI * 1.06 = $10.40\n // 19.60 USDT * 1.00 = $19.60\n //\n // And so the user gets $10.40 + $19.60 = $30 worth of value.\n\n uint256 assetCount = allAssets.length;\n uint256[] memory assetUnits = new uint256[](assetCount);\n uint256[] memory assetBalances = new uint256[](assetCount);\n outputs = new uint256[](assetCount);\n\n // Calculate redeem fee\n if (redeemFeeBps > 0) {\n uint256 redeemFee = _amount.mulTruncateScale(redeemFeeBps, 1e4);\n _amount = _amount - redeemFee;\n }\n\n // Calculate assets balances and decimals once,\n // for a large gas savings.\n uint256 totalUnits = 0;\n for (uint256 i = 0; i < assetCount; ++i) {\n address assetAddr = allAssets[i];\n uint256 balance = _checkBalance(assetAddr);\n assetBalances[i] = balance;\n assetUnits[i] = _toUnits(balance, assetAddr);\n totalUnits = totalUnits + assetUnits[i];\n }\n // Calculate totalOutputRatio\n uint256 totalOutputRatio = 0;\n for (uint256 i = 0; i < assetCount; ++i) {\n uint256 unitPrice = _toUnitPrice(allAssets[i], false);\n uint256 ratio = (assetUnits[i] * unitPrice) / totalUnits;\n totalOutputRatio = totalOutputRatio + ratio;\n }\n // Calculate final outputs\n uint256 factor = _amount.divPrecisely(totalOutputRatio);\n for (uint256 i = 0; i < assetCount; ++i) {\n outputs[i] = (assetBalances[i] * factor) / totalUnits;\n }\n }\n\n /***************************************\n Pricing\n ****************************************/\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * Never goes above 1, since that is how we price mints.\n * @param asset address of the asset\n * @return price uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed\n */\n function priceUnitMint(address asset)\n external\n view\n returns (uint256 price)\n {\n /* need to supply 1 asset unit in asset's decimals and can not just hard-code\n * to 1e18 and ignore calling `_toUnits` since we need to consider assets\n * with the exchange rate\n */\n uint256 units = _toUnits(\n uint256(1e18).scaleBy(_getDecimals(asset), 18),\n asset\n );\n price = (_toUnitPrice(asset, true) * units) / 1e18;\n }\n\n /**\n * @notice Returns the total price in 18 digit unit for a given asset.\n * Never goes below 1, since that is how we price redeems\n * @param asset Address of the asset\n * @return price uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed\n */\n function priceUnitRedeem(address asset)\n external\n view\n returns (uint256 price)\n {\n /* need to supply 1 asset unit in asset's decimals and can not just hard-code\n * to 1e18 and ignore calling `_toUnits` since we need to consider assets\n * with the exchange rate\n */\n uint256 units = _toUnits(\n uint256(1e18).scaleBy(_getDecimals(asset), 18),\n asset\n );\n price = (_toUnitPrice(asset, false) * units) / 1e18;\n }\n\n /***************************************\n Utils\n ****************************************/\n\n /**\n * @dev Convert a quantity of a token into 1e18 fixed decimal \"units\"\n * in the underlying base (USD/ETH) used by the vault.\n * Price is not taken into account, only quantity.\n *\n * Examples of this conversion:\n *\n * - 1e18 DAI becomes 1e18 units (same decimals)\n * - 1e6 USDC becomes 1e18 units (decimal conversion)\n * - 1e18 rETH becomes 1.2e18 units (exchange rate conversion)\n *\n * @param _raw Quantity of asset\n * @param _asset Core Asset address\n * @return value 1e18 normalized quantity of units\n */\n function _toUnits(uint256 _raw, address _asset)\n internal\n view\n returns (uint256)\n {\n UnitConversion conversion = assets[_asset].unitConversion;\n if (conversion == UnitConversion.DECIMALS) {\n return _raw.scaleBy(18, _getDecimals(_asset));\n } else if (conversion == UnitConversion.GETEXCHANGERATE) {\n uint256 exchangeRate = IGetExchangeRateToken(_asset)\n .getExchangeRate();\n return (_raw * exchangeRate) / 1e18;\n } else {\n revert(\"Unsupported conversion type\");\n }\n }\n\n /**\n * @dev Returns asset's unit price accounting for different asset types\n * and takes into account the context in which that price exists -\n * - mint or redeem.\n *\n * Note: since we are returning the price of the unit and not the one of the\n * asset (see comment above how 1 rETH exchanges for 1.2 units) we need\n * to make the Oracle price adjustment as well since we are pricing the\n * units and not the assets.\n *\n * The price also snaps to a \"full unit price\" in case a mint or redeem\n * action would be unfavourable to the protocol.\n *\n */\n function _toUnitPrice(address _asset, bool isMint)\n internal\n view\n returns (uint256 price)\n {\n UnitConversion conversion = assets[_asset].unitConversion;\n price = IOracle(priceProvider).price(_asset);\n\n if (conversion == UnitConversion.GETEXCHANGERATE) {\n uint256 exchangeRate = IGetExchangeRateToken(_asset)\n .getExchangeRate();\n price = (price * 1e18) / exchangeRate;\n } else if (conversion != UnitConversion.DECIMALS) {\n revert(\"Unsupported conversion type\");\n }\n\n /* At this stage the price is already adjusted to the unit\n * so the price checks are agnostic to underlying asset being\n * pegged to a USD or to an ETH or having a custom exchange rate.\n */\n require(price <= MAX_UNIT_PRICE_DRIFT, \"Vault: Price exceeds max\");\n require(price >= MIN_UNIT_PRICE_DRIFT, \"Vault: Price under min\");\n\n if (isMint) {\n /* Never price a normalized unit price for more than one\n * unit of OETH/OUSD when minting.\n */\n if (price > 1e18) {\n price = 1e18;\n }\n require(price >= MINT_MINIMUM_UNIT_PRICE, \"Asset price below peg\");\n } else {\n /* Never give out more than 1 normalized unit amount of assets\n * for one unit of OETH/OUSD when redeeming.\n */\n if (price < 1e18) {\n price = 1e18;\n }\n }\n }\n\n /**\n * @dev Get the number of decimals of a token asset\n * @param _asset Address of the asset\n * @return decimals number of decimals\n */\n function _getDecimals(address _asset)\n internal\n view\n returns (uint256 decimals)\n {\n decimals = assets[_asset].decimals;\n require(decimals > 0, \"Decimals not cached\");\n }\n\n /**\n * @notice Return the number of assets supported by the Vault.\n */\n function getAssetCount() public view returns (uint256) {\n return allAssets.length;\n }\n\n /**\n * @notice Gets the vault configuration of a supported asset.\n * @param _asset Address of the token asset\n */\n function getAssetConfig(address _asset)\n public\n view\n returns (Asset memory config)\n {\n config = assets[_asset];\n }\n\n /**\n * @notice Return all vault asset addresses in order\n */\n function getAllAssets() external view returns (address[] memory) {\n return allAssets;\n }\n\n /**\n * @notice Return the number of strategies active on the Vault.\n */\n function getStrategyCount() external view returns (uint256) {\n return allStrategies.length;\n }\n\n /**\n * @notice Return the array of all strategies\n */\n function getAllStrategies() external view returns (address[] memory) {\n return allStrategies;\n }\n\n /**\n * @notice Returns whether the vault supports the asset\n * @param _asset address of the asset\n * @return true if supported\n */\n function isSupportedAsset(address _asset) external view returns (bool) {\n return assets[_asset].isSupported;\n }\n\n function ADMIN_IMPLEMENTATION() external view returns (address adminImpl) {\n bytes32 slot = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n adminImpl := sload(slot)\n }\n }\n\n /**\n * @dev Falldown to the admin implementation\n * @notice This is a catch all for all functions not declared in core\n */\n // solhint-disable-next-line no-complex-fallback\n fallback() external {\n bytes32 slot = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(\n gas(),\n sload(slot),\n 0,\n calldatasize(),\n 0,\n 0\n )\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n function abs(int256 x) private pure returns (uint256) {\n require(x < int256(MAX_INT), \"Amount too high\");\n return x >= 0 ? uint256(x) : uint256(-x);\n }\n\n function _min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n function _max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a > b ? a : b;\n }\n}\n" + }, + "contracts/vault/VaultInitializer.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultInitializer contract\n * @notice The Vault contract initializes the vault.\n * @author Origin Protocol Inc\n */\n\nimport \"./VaultStorage.sol\";\n\ncontract VaultInitializer is VaultStorage {\n function initialize(address _priceProvider, address _oToken)\n external\n onlyGovernor\n initializer\n {\n require(_priceProvider != address(0), \"PriceProvider address is zero\");\n require(_oToken != address(0), \"oToken address is zero\");\n\n oUSD = OUSD(_oToken);\n\n priceProvider = _priceProvider;\n\n rebasePaused = false;\n capitalPaused = true;\n\n // Initial redeem fee of 0 basis points\n redeemFeeBps = 0;\n // Initial Vault buffer of 0%\n vaultBuffer = 0;\n // Initial allocate threshold of 25,000 OUSD\n autoAllocateThreshold = 25000e18;\n // Threshold for rebasing\n rebaseThreshold = 1000e18;\n // Initialize all strategies\n allStrategies = new address[](0);\n // Start with drip duration disabled\n dripDuration = 1;\n }\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address public dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + }, + "hardhat/console.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.4.22 <0.9.0;\n\nlibrary console {\n address constant CONSOLE_ADDRESS =\n 0x000000000000000000636F6e736F6c652e6c6f67;\n\n function _sendLogPayloadImplementation(bytes memory payload) internal view {\n address consoleAddress = CONSOLE_ADDRESS;\n /// @solidity memory-safe-assembly\n assembly {\n pop(\n staticcall(\n gas(),\n consoleAddress,\n add(payload, 32),\n mload(payload),\n 0,\n 0\n )\n )\n }\n }\n\n function _castToPure(\n function(bytes memory) internal view fnIn\n ) internal pure returns (function(bytes memory) pure fnOut) {\n assembly {\n fnOut := fnIn\n }\n }\n\n function _sendLogPayload(bytes memory payload) internal pure {\n _castToPure(_sendLogPayloadImplementation)(payload);\n }\n\n function log() internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log()\"));\n }\n\n function logInt(int256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(int256)\", p0));\n }\n\n function logUint(uint256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256)\", p0));\n }\n\n function logString(string memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n }\n\n function logBool(bool p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n }\n\n function logAddress(address p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n }\n\n function logBytes(bytes memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes)\", p0));\n }\n\n function logBytes1(bytes1 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes1)\", p0));\n }\n\n function logBytes2(bytes2 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes2)\", p0));\n }\n\n function logBytes3(bytes3 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes3)\", p0));\n }\n\n function logBytes4(bytes4 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes4)\", p0));\n }\n\n function logBytes5(bytes5 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes5)\", p0));\n }\n\n function logBytes6(bytes6 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes6)\", p0));\n }\n\n function logBytes7(bytes7 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes7)\", p0));\n }\n\n function logBytes8(bytes8 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes8)\", p0));\n }\n\n function logBytes9(bytes9 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes9)\", p0));\n }\n\n function logBytes10(bytes10 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes10)\", p0));\n }\n\n function logBytes11(bytes11 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes11)\", p0));\n }\n\n function logBytes12(bytes12 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes12)\", p0));\n }\n\n function logBytes13(bytes13 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes13)\", p0));\n }\n\n function logBytes14(bytes14 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes14)\", p0));\n }\n\n function logBytes15(bytes15 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes15)\", p0));\n }\n\n function logBytes16(bytes16 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes16)\", p0));\n }\n\n function logBytes17(bytes17 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes17)\", p0));\n }\n\n function logBytes18(bytes18 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes18)\", p0));\n }\n\n function logBytes19(bytes19 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes19)\", p0));\n }\n\n function logBytes20(bytes20 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes20)\", p0));\n }\n\n function logBytes21(bytes21 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes21)\", p0));\n }\n\n function logBytes22(bytes22 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes22)\", p0));\n }\n\n function logBytes23(bytes23 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes23)\", p0));\n }\n\n function logBytes24(bytes24 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes24)\", p0));\n }\n\n function logBytes25(bytes25 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes25)\", p0));\n }\n\n function logBytes26(bytes26 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes26)\", p0));\n }\n\n function logBytes27(bytes27 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes27)\", p0));\n }\n\n function logBytes28(bytes28 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes28)\", p0));\n }\n\n function logBytes29(bytes29 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes29)\", p0));\n }\n\n function logBytes30(bytes30 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes30)\", p0));\n }\n\n function logBytes31(bytes31 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes31)\", p0));\n }\n\n function logBytes32(bytes32 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes32)\", p0));\n }\n\n function log(uint256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256)\", p0));\n }\n\n function log(string memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n }\n\n function log(bool p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n }\n\n function log(address p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n }\n\n function log(uint256 p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256)\", p0, p1));\n }\n\n function log(uint256 p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string)\", p0, p1));\n }\n\n function log(uint256 p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool)\", p0, p1));\n }\n\n function log(uint256 p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address)\", p0, p1));\n }\n\n function log(string memory p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256)\", p0, p1));\n }\n\n function log(string memory p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string)\", p0, p1));\n }\n\n function log(string memory p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool)\", p0, p1));\n }\n\n function log(string memory p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address)\", p0, p1));\n }\n\n function log(bool p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256)\", p0, p1));\n }\n\n function log(bool p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string)\", p0, p1));\n }\n\n function log(bool p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool)\", p0, p1));\n }\n\n function log(bool p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address)\", p0, p1));\n }\n\n function log(address p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256)\", p0, p1));\n }\n\n function log(address p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string)\", p0, p1));\n }\n\n function log(address p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool)\", p0, p1));\n }\n\n function log(address p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address)\", p0, p1));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,address)\", p0, p1, p2, p3));\n }\n}\n" + }, + "lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC4626 } from \"../../../../interfaces/IERC4626.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\n// From Open Zeppelin draft PR commit:\n// fac43034dca85ff539db3fc8aa2a7084b843d454\n// https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3171\n\nabstract contract ERC4626 is ERC20, IERC4626 {\n IERC20Metadata private immutable _asset;\n\n constructor(IERC20Metadata __asset) {\n _asset = __asset;\n }\n\n /** @dev See {IERC4262-asset} */\n function asset() public view virtual override returns (address) {\n return address(_asset);\n }\n\n /** @dev See {IERC4262-totalAssets} */\n function totalAssets() public view virtual override returns (uint256) {\n return _asset.balanceOf(address(this));\n }\n\n /**\n * @dev See {IERC4262-convertToShares}\n *\n * Will revert if asserts > 0, totalSupply > 0 and totalAssets = 0. That corresponds to a case where any asset\n * would represent an infinite amout of shares.\n */\n function convertToShares(uint256 assets) public view virtual override returns (uint256 shares) {\n uint256 supply = totalSupply();\n\n return\n (assets == 0 || supply == 0)\n ? (assets * 10**decimals()) / 10**_asset.decimals()\n : (assets * supply) / totalAssets();\n }\n\n /** @dev See {IERC4262-convertToAssets} */\n function convertToAssets(uint256 shares) public view virtual override returns (uint256 assets) {\n uint256 supply = totalSupply();\n\n return (supply == 0) ? (shares * 10**_asset.decimals()) / 10**decimals() : (shares * totalAssets()) / supply;\n }\n\n /** @dev See {IERC4262-maxDeposit} */\n function maxDeposit(address) public view virtual override returns (uint256) {\n return type(uint256).max;\n }\n\n /** @dev See {IERC4262-maxMint} */\n function maxMint(address) public view virtual override returns (uint256) {\n return type(uint256).max;\n }\n\n /** @dev See {IERC4262-maxWithdraw} */\n function maxWithdraw(address owner) public view virtual override returns (uint256) {\n return convertToAssets(balanceOf(owner));\n }\n\n /** @dev See {IERC4262-maxRedeem} */\n function maxRedeem(address owner) public view virtual override returns (uint256) {\n return balanceOf(owner);\n }\n\n /** @dev See {IERC4262-previewDeposit} */\n function previewDeposit(uint256 assets) public view virtual override returns (uint256) {\n return convertToShares(assets);\n }\n\n /** @dev See {IERC4262-previewMint} */\n function previewMint(uint256 shares) public view virtual override returns (uint256) {\n uint256 assets = convertToAssets(shares);\n return assets + (convertToShares(assets) < shares ? 1 : 0);\n }\n\n /** @dev See {IERC4262-previewWithdraw} */\n function previewWithdraw(uint256 assets) public view virtual override returns (uint256) {\n uint256 shares = convertToShares(assets);\n return shares + (convertToAssets(shares) < assets ? 1 : 0);\n }\n\n /** @dev See {IERC4262-previewRedeem} */\n function previewRedeem(uint256 shares) public view virtual override returns (uint256) {\n return convertToAssets(shares);\n }\n\n /** @dev See {IERC4262-deposit} */\n function deposit(uint256 assets, address receiver) public virtual override returns (uint256) {\n require(assets <= maxDeposit(receiver), \"ERC4626: deposit more then max\");\n\n address caller = _msgSender();\n uint256 shares = previewDeposit(assets);\n\n // if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through\n // the tokensToSend hook, so we need to transfer before we mint to keep the invariants.\n SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);\n _mint(receiver, shares);\n\n emit Deposit(caller, receiver, assets, shares);\n\n return shares;\n }\n\n /** @dev See {IERC4262-mint} */\n function mint(uint256 shares, address receiver) public virtual override returns (uint256) {\n require(shares <= maxMint(receiver), \"ERC4626: mint more then max\");\n\n address caller = _msgSender();\n uint256 assets = previewMint(shares);\n\n // if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through\n // the tokensToSend hook, so we need to transfer before we mint to keep the invariants.\n SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);\n _mint(receiver, shares);\n\n emit Deposit(caller, receiver, assets, shares);\n\n return assets;\n }\n\n /** @dev See {IERC4262-withdraw} */\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) public virtual override returns (uint256) {\n require(assets <= maxWithdraw(owner), \"ERC4626: withdraw more then max\");\n\n address caller = _msgSender();\n uint256 shares = previewWithdraw(assets);\n\n if (caller != owner) {\n _spendAllowance(owner, caller, shares);\n }\n\n // if _asset is ERC777, transfer can call reenter AFTER the transfer happens through\n // the tokensReceived hook, so we need to transfer after we burn to keep the invariants.\n _burn(owner, shares);\n SafeERC20.safeTransfer(_asset, receiver, assets);\n\n emit Withdraw(caller, receiver, owner, assets, shares);\n\n return shares;\n }\n\n /** @dev See {IERC4262-redeem} */\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) public virtual override returns (uint256) {\n require(shares <= maxRedeem(owner), \"ERC4626: redeem more then max\");\n\n address caller = _msgSender();\n uint256 assets = previewRedeem(shares);\n\n if (caller != owner) {\n _spendAllowance(owner, caller, shares);\n }\n\n // if _asset is ERC777, transfer can call reenter AFTER the transfer happens through\n // the tokensReceived hook, so we need to transfer after we burn to keep the invariants.\n _burn(owner, shares);\n SafeERC20.safeTransfer(_asset, receiver, assets);\n\n emit Withdraw(caller, receiver, owner, assets, shares);\n\n return assets;\n }\n\n // Included here, since this method was not yet present in\n // the version of Open Zeppelin ERC20 code we use.\n function _spendAllowance(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n uint256 currentAllowance = allowance(owner, spender);\n if (currentAllowance != type(uint256).max) {\n require(currentAllowance >= amount, \"ERC20: insufficient allowance\");\n unchecked {\n _approve(owner, spender, currentAllowance - amount);\n }\n }\n }\n}" + }, + "lib/openzeppelin/interfaces/IERC4626.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IERC4626 is IERC20, IERC20Metadata {\n event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);\n\n event Withdraw(\n address indexed caller,\n address indexed receiver,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n\n /**\n * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.\n *\n * - MUST be an ERC-20 token contract.\n * - MUST NOT revert.\n */\n function asset() external view returns (address assetTokenAddress);\n\n /**\n * @dev Returns the total amount of the underlying asset that is “managed” by Vault.\n *\n * - SHOULD include any compounding that occurs from yield.\n * - MUST be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT revert.\n */\n function totalAssets() external view returns (uint256 totalManagedAssets);\n\n /**\n * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal\n * scenario where all the conditions are met.\n *\n * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT show any variations depending on the caller.\n * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n * - MUST NOT revert.\n *\n * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n * from.\n */\n function convertToShares(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal\n * scenario where all the conditions are met.\n *\n * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT show any variations depending on the caller.\n * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n * - MUST NOT revert.\n *\n * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n * from.\n */\n function convertToAssets(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,\n * through a deposit call.\n *\n * - MUST return a limited value if receiver is subject to some deposit limit.\n * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.\n * - MUST NOT revert.\n */\n function maxDeposit(address receiver) external view returns (uint256 maxAssets);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given\n * current on-chain conditions.\n *\n * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit\n * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called\n * in the same transaction.\n * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the\n * deposit would be accepted, regardless if the user has enough tokens approved, etc.\n * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by depositing.\n */\n function previewDeposit(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.\n *\n * - MUST emit the Deposit event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * deposit execution, and are accounted for during deposit.\n * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not\n * approving enough underlying tokens to the Vault contract, etc).\n *\n * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n */\n function deposit(uint256 assets, address receiver) external returns (uint256 shares);\n\n /**\n * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.\n * - MUST return a limited value if receiver is subject to some mint limit.\n * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.\n * - MUST NOT revert.\n */\n function maxMint(address receiver) external view returns (uint256 maxShares);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given\n * current on-chain conditions.\n *\n * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call\n * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the\n * same transaction.\n * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint\n * would be accepted, regardless if the user has enough tokens approved, etc.\n * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by minting.\n */\n function previewMint(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.\n *\n * - MUST emit the Deposit event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint\n * execution, and are accounted for during mint.\n * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not\n * approving enough underlying tokens to the Vault contract, etc).\n *\n * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n */\n function mint(uint256 shares, address receiver) external returns (uint256 assets);\n\n /**\n * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the\n * Vault, through a withdraw call.\n *\n * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n * - MUST NOT revert.\n */\n function maxWithdraw(address owner) external view returns (uint256 maxAssets);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,\n * given current on-chain conditions.\n *\n * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw\n * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if\n * called\n * in the same transaction.\n * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though\n * the withdrawal would be accepted, regardless if the user has enough shares, etc.\n * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by depositing.\n */\n function previewWithdraw(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.\n *\n * - MUST emit the Withdraw event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * withdraw execution, and are accounted for during withdraw.\n * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner\n * not having enough shares, etc).\n *\n * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n * Those methods should be performed separately.\n */\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares);\n\n /**\n * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,\n * through a redeem call.\n *\n * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.\n * - MUST NOT revert.\n */\n function maxRedeem(address owner) external view returns (uint256 maxShares);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,\n * given current on-chain conditions.\n *\n * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call\n * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the\n * same transaction.\n * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the\n * redemption would be accepted, regardless if the user has enough shares, etc.\n * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by redeeming.\n */\n function previewRedeem(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.\n *\n * - MUST emit the Withdraw event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * redeem execution, and are accounted for during redeem.\n * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner\n * not having enough shares, etc).\n *\n * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n * Those methods should be performed separately.\n */\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets);\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.20;\n\nimport {Panic} from \"../Panic.sol\";\nimport {SafeCast} from \"./SafeCast.sol\";\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n enum Rounding {\n Floor, // Toward negative infinity\n Ceil, // Toward positive infinity\n Trunc, // Toward zero\n Expand // Away from zero\n }\n\n /**\n * @dev Return the 512-bit addition of two uint256.\n *\n * The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low.\n */\n function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {\n assembly (\"memory-safe\") {\n low := add(a, b)\n high := lt(low, a)\n }\n }\n\n /**\n * @dev Return the 512-bit multiplication of two uint256.\n *\n * The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low.\n */\n function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {\n // 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use\n // the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n // variables such that product = high * 2²⁵⁶ + low.\n assembly (\"memory-safe\") {\n let mm := mulmod(a, b, not(0))\n low := mul(a, b)\n high := sub(sub(mm, low), lt(mm, low))\n }\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, with a success flag (no overflow).\n */\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a + b;\n success = c >= a;\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).\n */\n function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a - b;\n success = c <= a;\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).\n */\n function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a * b;\n assembly (\"memory-safe\") {\n // Only true when the multiplication doesn't overflow\n // (c / a == b) || (a == 0)\n success := or(eq(div(c, a), b), iszero(a))\n }\n // equivalent to: success ? c : 0\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the division of two unsigned integers, with a success flag (no division by zero).\n */\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n success = b > 0;\n assembly (\"memory-safe\") {\n // The `DIV` opcode returns zero when the denominator is 0.\n result := div(a, b)\n }\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).\n */\n function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n success = b > 0;\n assembly (\"memory-safe\") {\n // The `MOD` opcode returns zero when the denominator is 0.\n result := mod(a, b)\n }\n }\n }\n\n /**\n * @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing.\n */\n function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {\n (bool success, uint256 result) = tryAdd(a, b);\n return ternary(success, result, type(uint256).max);\n }\n\n /**\n * @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.\n */\n function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {\n (, uint256 result) = trySub(a, b);\n return result;\n }\n\n /**\n * @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing.\n */\n function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {\n (bool success, uint256 result) = tryMul(a, b);\n return ternary(success, result, type(uint256).max);\n }\n\n /**\n * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.\n *\n * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.\n * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute\n * one branch when needed, making this function more expensive.\n */\n function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {\n unchecked {\n // branchless ternary works because:\n // b ^ (a ^ b) == a\n // b ^ 0 == b\n return b ^ ((a ^ b) * SafeCast.toUint(condition));\n }\n }\n\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return ternary(a > b, a, b);\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return ternary(a < b, a, b);\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds towards infinity instead\n * of rounding towards zero.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n if (b == 0) {\n // Guarantee the same behavior as in a regular Solidity division.\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n\n // The following calculation ensures accurate ceiling division without overflow.\n // Since a is non-zero, (a - 1) / b will not overflow.\n // The largest possible result occurs when (a - 1) / b is type(uint256).max,\n // but the largest value we can obtain is type(uint256).max - 1, which happens\n // when a = type(uint256).max and b = 1.\n unchecked {\n return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);\n }\n }\n\n /**\n * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or\n * denominator == 0.\n *\n * Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by\n * Uniswap Labs also under MIT license.\n */\n function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {\n unchecked {\n (uint256 high, uint256 low) = mul512(x, y);\n\n // Handle non-overflow cases, 256 by 256 division.\n if (high == 0) {\n // Solidity will revert if denominator == 0, unlike the div opcode on its own.\n // The surrounding unchecked block does not change this fact.\n // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.\n return low / denominator;\n }\n\n // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.\n if (denominator <= high) {\n Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));\n }\n\n ///////////////////////////////////////////////\n // 512 by 256 division.\n ///////////////////////////////////////////////\n\n // Make division exact by subtracting the remainder from [high low].\n uint256 remainder;\n assembly (\"memory-safe\") {\n // Compute remainder using mulmod.\n remainder := mulmod(x, y, denominator)\n\n // Subtract 256 bit number from 512 bit number.\n high := sub(high, gt(remainder, low))\n low := sub(low, remainder)\n }\n\n // Factor powers of two out of denominator and compute largest power of two divisor of denominator.\n // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.\n\n uint256 twos = denominator & (0 - denominator);\n assembly (\"memory-safe\") {\n // Divide denominator by twos.\n denominator := div(denominator, twos)\n\n // Divide [high low] by twos.\n low := div(low, twos)\n\n // Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.\n twos := add(div(sub(0, twos), twos), 1)\n }\n\n // Shift in bits from high into low.\n low |= high * twos;\n\n // Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such\n // that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for\n // four bits. That is, denominator * inv ≡ 1 mod 2⁴.\n uint256 inverse = (3 * denominator) ^ 2;\n\n // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also\n // works in modular arithmetic, doubling the correct bits in each step.\n inverse *= 2 - denominator * inverse; // inverse mod 2⁸\n inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶\n inverse *= 2 - denominator * inverse; // inverse mod 2³²\n inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴\n inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸\n inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶\n\n // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n // This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is\n // less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and high\n // is no longer required.\n result = low * inverse;\n return result;\n }\n }\n\n /**\n * @dev Calculates x * y / denominator with full precision, following the selected rounding direction.\n */\n function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {\n return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);\n }\n\n /**\n * @dev Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256.\n */\n function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) {\n unchecked {\n (uint256 high, uint256 low) = mul512(x, y);\n if (high >= 1 << n) {\n Panic.panic(Panic.UNDER_OVERFLOW);\n }\n return (high << (256 - n)) | (low >> n);\n }\n }\n\n /**\n * @dev Calculates x * y >> n with full precision, following the selected rounding direction.\n */\n function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) {\n return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 0);\n }\n\n /**\n * @dev Calculate the modular multiplicative inverse of a number in Z/nZ.\n *\n * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.\n * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.\n *\n * If the input value is not inversible, 0 is returned.\n *\n * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the\n * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.\n */\n function invMod(uint256 a, uint256 n) internal pure returns (uint256) {\n unchecked {\n if (n == 0) return 0;\n\n // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)\n // Used to compute integers x and y such that: ax + ny = gcd(a, n).\n // When the gcd is 1, then the inverse of a modulo n exists and it's x.\n // ax + ny = 1\n // ax = 1 + (-y)n\n // ax ≡ 1 (mod n) # x is the inverse of a modulo n\n\n // If the remainder is 0 the gcd is n right away.\n uint256 remainder = a % n;\n uint256 gcd = n;\n\n // Therefore the initial coefficients are:\n // ax + ny = gcd(a, n) = n\n // 0a + 1n = n\n int256 x = 0;\n int256 y = 1;\n\n while (remainder != 0) {\n uint256 quotient = gcd / remainder;\n\n (gcd, remainder) = (\n // The old remainder is the next gcd to try.\n remainder,\n // Compute the next remainder.\n // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd\n // where gcd is at most n (capped to type(uint256).max)\n gcd - remainder * quotient\n );\n\n (x, y) = (\n // Increment the coefficient of a.\n y,\n // Decrement the coefficient of n.\n // Can overflow, but the result is casted to uint256 so that the\n // next value of y is \"wrapped around\" to a value between 0 and n - 1.\n x - y * int256(quotient)\n );\n }\n\n if (gcd != 1) return 0; // No inverse exists.\n return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.\n }\n }\n\n /**\n * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.\n *\n * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is\n * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that\n * `a**(p-2)` is the modular multiplicative inverse of a in Fp.\n *\n * NOTE: this function does NOT check that `p` is a prime greater than `2`.\n */\n function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {\n unchecked {\n return Math.modExp(a, p - 2, p);\n }\n }\n\n /**\n * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)\n *\n * Requirements:\n * - modulus can't be zero\n * - underlying staticcall to precompile must succeed\n *\n * IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make\n * sure the chain you're using it on supports the precompiled contract for modular exponentiation\n * at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,\n * the underlying function will succeed given the lack of a revert, but the result may be incorrectly\n * interpreted as 0.\n */\n function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {\n (bool success, uint256 result) = tryModExp(b, e, m);\n if (!success) {\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n return result;\n }\n\n /**\n * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).\n * It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying\n * to operate modulo 0 or if the underlying precompile reverted.\n *\n * IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain\n * you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in\n * https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack\n * of a revert, but the result may be incorrectly interpreted as 0.\n */\n function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {\n if (m == 0) return (false, 0);\n assembly (\"memory-safe\") {\n let ptr := mload(0x40)\n // | Offset | Content | Content (Hex) |\n // |-----------|------------|--------------------------------------------------------------------|\n // | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x60:0x7f | value of b | 0x<.............................................................b> |\n // | 0x80:0x9f | value of e | 0x<.............................................................e> |\n // | 0xa0:0xbf | value of m | 0x<.............................................................m> |\n mstore(ptr, 0x20)\n mstore(add(ptr, 0x20), 0x20)\n mstore(add(ptr, 0x40), 0x20)\n mstore(add(ptr, 0x60), b)\n mstore(add(ptr, 0x80), e)\n mstore(add(ptr, 0xa0), m)\n\n // Given the result < m, it's guaranteed to fit in 32 bytes,\n // so we can use the memory scratch space located at offset 0.\n success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)\n result := mload(0x00)\n }\n }\n\n /**\n * @dev Variant of {modExp} that supports inputs of arbitrary length.\n */\n function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {\n (bool success, bytes memory result) = tryModExp(b, e, m);\n if (!success) {\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n return result;\n }\n\n /**\n * @dev Variant of {tryModExp} that supports inputs of arbitrary length.\n */\n function tryModExp(\n bytes memory b,\n bytes memory e,\n bytes memory m\n ) internal view returns (bool success, bytes memory result) {\n if (_zeroBytes(m)) return (false, new bytes(0));\n\n uint256 mLen = m.length;\n\n // Encode call args in result and move the free memory pointer\n result = abi.encodePacked(b.length, e.length, mLen, b, e, m);\n\n assembly (\"memory-safe\") {\n let dataPtr := add(result, 0x20)\n // Write result on top of args to avoid allocating extra memory.\n success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)\n // Overwrite the length.\n // result.length > returndatasize() is guaranteed because returndatasize() == m.length\n mstore(result, mLen)\n // Set the memory pointer after the returned data.\n mstore(0x40, add(dataPtr, mLen))\n }\n }\n\n /**\n * @dev Returns whether the provided byte array is zero.\n */\n function _zeroBytes(bytes memory byteArray) private pure returns (bool) {\n for (uint256 i = 0; i < byteArray.length; ++i) {\n if (byteArray[i] != 0) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded\n * towards zero.\n *\n * This method is based on Newton's method for computing square roots; the algorithm is restricted to only\n * using integer operations.\n */\n function sqrt(uint256 a) internal pure returns (uint256) {\n unchecked {\n // Take care of easy edge cases when a == 0 or a == 1\n if (a <= 1) {\n return a;\n }\n\n // In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a\n // sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between\n // the current value as `ε_n = | x_n - sqrt(a) |`.\n //\n // For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root\n // of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is\n // bigger than any uint256.\n //\n // By noticing that\n // `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`\n // we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar\n // to the msb function.\n uint256 aa = a;\n uint256 xn = 1;\n\n if (aa >= (1 << 128)) {\n aa >>= 128;\n xn <<= 64;\n }\n if (aa >= (1 << 64)) {\n aa >>= 64;\n xn <<= 32;\n }\n if (aa >= (1 << 32)) {\n aa >>= 32;\n xn <<= 16;\n }\n if (aa >= (1 << 16)) {\n aa >>= 16;\n xn <<= 8;\n }\n if (aa >= (1 << 8)) {\n aa >>= 8;\n xn <<= 4;\n }\n if (aa >= (1 << 4)) {\n aa >>= 4;\n xn <<= 2;\n }\n if (aa >= (1 << 2)) {\n xn <<= 1;\n }\n\n // We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).\n //\n // We can refine our estimation by noticing that the middle of that interval minimizes the error.\n // If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).\n // This is going to be our x_0 (and ε_0)\n xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)\n\n // From here, Newton's method give us:\n // x_{n+1} = (x_n + a / x_n) / 2\n //\n // One should note that:\n // x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a\n // = ((x_n² + a) / (2 * x_n))² - a\n // = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a\n // = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)\n // = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)\n // = (x_n² - a)² / (2 * x_n)²\n // = ((x_n² - a) / (2 * x_n))²\n // ≥ 0\n // Which proves that for all n ≥ 1, sqrt(a) ≤ x_n\n //\n // This gives us the proof of quadratic convergence of the sequence:\n // ε_{n+1} = | x_{n+1} - sqrt(a) |\n // = | (x_n + a / x_n) / 2 - sqrt(a) |\n // = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |\n // = | (x_n - sqrt(a))² / (2 * x_n) |\n // = | ε_n² / (2 * x_n) |\n // = ε_n² / | (2 * x_n) |\n //\n // For the first iteration, we have a special case where x_0 is known:\n // ε_1 = ε_0² / | (2 * x_0) |\n // ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))\n // ≤ 2**(2*e-4) / (3 * 2**(e-1))\n // ≤ 2**(e-3) / 3\n // ≤ 2**(e-3-log2(3))\n // ≤ 2**(e-4.5)\n //\n // For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:\n // ε_{n+1} = ε_n² / | (2 * x_n) |\n // ≤ (2**(e-k))² / (2 * 2**(e-1))\n // ≤ 2**(2*e-2*k) / 2**e\n // ≤ 2**(e-2*k)\n xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above\n xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5\n xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9\n xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18\n xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36\n xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72\n\n // Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision\n // ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either\n // sqrt(a) or sqrt(a) + 1.\n return xn - SafeCast.toUint(xn > a / xn);\n }\n }\n\n /**\n * @dev Calculates sqrt(a), following the selected rounding direction.\n */\n function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = sqrt(a);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);\n }\n }\n\n /**\n * @dev Return the log in base 2 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n */\n function log2(uint256 x) internal pure returns (uint256 r) {\n // If value has upper 128 bits set, log2 result is at least 128\n r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;\n // If upper 64 bits of 128-bit half set, add 64 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;\n // If upper 32 bits of 64-bit half set, add 32 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;\n // If upper 16 bits of 32-bit half set, add 16 to result\n r |= SafeCast.toUint((x >> r) > 0xffff) << 4;\n // If upper 8 bits of 16-bit half set, add 8 to result\n r |= SafeCast.toUint((x >> r) > 0xff) << 3;\n // If upper 4 bits of 8-bit half set, add 4 to result\n r |= SafeCast.toUint((x >> r) > 0xf) << 2;\n\n // Shifts value right by the current result and use it as an index into this lookup table:\n //\n // | x (4 bits) | index | table[index] = MSB position |\n // |------------|---------|-----------------------------|\n // | 0000 | 0 | table[0] = 0 |\n // | 0001 | 1 | table[1] = 0 |\n // | 0010 | 2 | table[2] = 1 |\n // | 0011 | 3 | table[3] = 1 |\n // | 0100 | 4 | table[4] = 2 |\n // | 0101 | 5 | table[5] = 2 |\n // | 0110 | 6 | table[6] = 2 |\n // | 0111 | 7 | table[7] = 2 |\n // | 1000 | 8 | table[8] = 3 |\n // | 1001 | 9 | table[9] = 3 |\n // | 1010 | 10 | table[10] = 3 |\n // | 1011 | 11 | table[11] = 3 |\n // | 1100 | 12 | table[12] = 3 |\n // | 1101 | 13 | table[13] = 3 |\n // | 1110 | 14 | table[14] = 3 |\n // | 1111 | 15 | table[15] = 3 |\n //\n // The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.\n assembly (\"memory-safe\") {\n r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))\n }\n }\n\n /**\n * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log2(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);\n }\n }\n\n /**\n * @dev Return the log in base 10 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n */\n function log10(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >= 10 ** 64) {\n value /= 10 ** 64;\n result += 64;\n }\n if (value >= 10 ** 32) {\n value /= 10 ** 32;\n result += 32;\n }\n if (value >= 10 ** 16) {\n value /= 10 ** 16;\n result += 16;\n }\n if (value >= 10 ** 8) {\n value /= 10 ** 8;\n result += 8;\n }\n if (value >= 10 ** 4) {\n value /= 10 ** 4;\n result += 4;\n }\n if (value >= 10 ** 2) {\n value /= 10 ** 2;\n result += 2;\n }\n if (value >= 10 ** 1) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log10(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);\n }\n }\n\n /**\n * @dev Return the log in base 256 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n *\n * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n */\n function log256(uint256 x) internal pure returns (uint256 r) {\n // If value has upper 128 bits set, log2 result is at least 128\n r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;\n // If upper 64 bits of 128-bit half set, add 64 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;\n // If upper 32 bits of 64-bit half set, add 32 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;\n // If upper 16 bits of 32-bit half set, add 16 to result\n r |= SafeCast.toUint((x >> r) > 0xffff) << 4;\n // Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8\n return (r >> 3) | SafeCast.toUint((x >> r) > 0xff);\n }\n\n /**\n * @dev Return the log in base 256, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log256(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);\n }\n }\n\n /**\n * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.\n */\n function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {\n return uint8(rounding) % 2 == 1;\n }\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)\n// This file was procedurally generated from scripts/generate/templates/SafeCast.js.\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n */\nlibrary SafeCast {\n /**\n * @dev Value doesn't fit in an uint of `bits` size.\n */\n error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);\n\n /**\n * @dev An int value doesn't fit in an uint of `bits` size.\n */\n error SafeCastOverflowedIntToUint(int256 value);\n\n /**\n * @dev Value doesn't fit in an int of `bits` size.\n */\n error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);\n\n /**\n * @dev An uint value doesn't fit in an int of `bits` size.\n */\n error SafeCastOverflowedUintToInt(uint256 value);\n\n /**\n * @dev Returns the downcasted uint248 from uint256, reverting on\n * overflow (when the input is greater than largest uint248).\n *\n * Counterpart to Solidity's `uint248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n */\n function toUint248(uint256 value) internal pure returns (uint248) {\n if (value > type(uint248).max) {\n revert SafeCastOverflowedUintDowncast(248, value);\n }\n return uint248(value);\n }\n\n /**\n * @dev Returns the downcasted uint240 from uint256, reverting on\n * overflow (when the input is greater than largest uint240).\n *\n * Counterpart to Solidity's `uint240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n */\n function toUint240(uint256 value) internal pure returns (uint240) {\n if (value > type(uint240).max) {\n revert SafeCastOverflowedUintDowncast(240, value);\n }\n return uint240(value);\n }\n\n /**\n * @dev Returns the downcasted uint232 from uint256, reverting on\n * overflow (when the input is greater than largest uint232).\n *\n * Counterpart to Solidity's `uint232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n */\n function toUint232(uint256 value) internal pure returns (uint232) {\n if (value > type(uint232).max) {\n revert SafeCastOverflowedUintDowncast(232, value);\n }\n return uint232(value);\n }\n\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n if (value > type(uint224).max) {\n revert SafeCastOverflowedUintDowncast(224, value);\n }\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint216 from uint256, reverting on\n * overflow (when the input is greater than largest uint216).\n *\n * Counterpart to Solidity's `uint216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n */\n function toUint216(uint256 value) internal pure returns (uint216) {\n if (value > type(uint216).max) {\n revert SafeCastOverflowedUintDowncast(216, value);\n }\n return uint216(value);\n }\n\n /**\n * @dev Returns the downcasted uint208 from uint256, reverting on\n * overflow (when the input is greater than largest uint208).\n *\n * Counterpart to Solidity's `uint208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n */\n function toUint208(uint256 value) internal pure returns (uint208) {\n if (value > type(uint208).max) {\n revert SafeCastOverflowedUintDowncast(208, value);\n }\n return uint208(value);\n }\n\n /**\n * @dev Returns the downcasted uint200 from uint256, reverting on\n * overflow (when the input is greater than largest uint200).\n *\n * Counterpart to Solidity's `uint200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n */\n function toUint200(uint256 value) internal pure returns (uint200) {\n if (value > type(uint200).max) {\n revert SafeCastOverflowedUintDowncast(200, value);\n }\n return uint200(value);\n }\n\n /**\n * @dev Returns the downcasted uint192 from uint256, reverting on\n * overflow (when the input is greater than largest uint192).\n *\n * Counterpart to Solidity's `uint192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n */\n function toUint192(uint256 value) internal pure returns (uint192) {\n if (value > type(uint192).max) {\n revert SafeCastOverflowedUintDowncast(192, value);\n }\n return uint192(value);\n }\n\n /**\n * @dev Returns the downcasted uint184 from uint256, reverting on\n * overflow (when the input is greater than largest uint184).\n *\n * Counterpart to Solidity's `uint184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n */\n function toUint184(uint256 value) internal pure returns (uint184) {\n if (value > type(uint184).max) {\n revert SafeCastOverflowedUintDowncast(184, value);\n }\n return uint184(value);\n }\n\n /**\n * @dev Returns the downcasted uint176 from uint256, reverting on\n * overflow (when the input is greater than largest uint176).\n *\n * Counterpart to Solidity's `uint176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n */\n function toUint176(uint256 value) internal pure returns (uint176) {\n if (value > type(uint176).max) {\n revert SafeCastOverflowedUintDowncast(176, value);\n }\n return uint176(value);\n }\n\n /**\n * @dev Returns the downcasted uint168 from uint256, reverting on\n * overflow (when the input is greater than largest uint168).\n *\n * Counterpart to Solidity's `uint168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n */\n function toUint168(uint256 value) internal pure returns (uint168) {\n if (value > type(uint168).max) {\n revert SafeCastOverflowedUintDowncast(168, value);\n }\n return uint168(value);\n }\n\n /**\n * @dev Returns the downcasted uint160 from uint256, reverting on\n * overflow (when the input is greater than largest uint160).\n *\n * Counterpart to Solidity's `uint160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n */\n function toUint160(uint256 value) internal pure returns (uint160) {\n if (value > type(uint160).max) {\n revert SafeCastOverflowedUintDowncast(160, value);\n }\n return uint160(value);\n }\n\n /**\n * @dev Returns the downcasted uint152 from uint256, reverting on\n * overflow (when the input is greater than largest uint152).\n *\n * Counterpart to Solidity's `uint152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n */\n function toUint152(uint256 value) internal pure returns (uint152) {\n if (value > type(uint152).max) {\n revert SafeCastOverflowedUintDowncast(152, value);\n }\n return uint152(value);\n }\n\n /**\n * @dev Returns the downcasted uint144 from uint256, reverting on\n * overflow (when the input is greater than largest uint144).\n *\n * Counterpart to Solidity's `uint144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n */\n function toUint144(uint256 value) internal pure returns (uint144) {\n if (value > type(uint144).max) {\n revert SafeCastOverflowedUintDowncast(144, value);\n }\n return uint144(value);\n }\n\n /**\n * @dev Returns the downcasted uint136 from uint256, reverting on\n * overflow (when the input is greater than largest uint136).\n *\n * Counterpart to Solidity's `uint136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n */\n function toUint136(uint256 value) internal pure returns (uint136) {\n if (value > type(uint136).max) {\n revert SafeCastOverflowedUintDowncast(136, value);\n }\n return uint136(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n if (value > type(uint128).max) {\n revert SafeCastOverflowedUintDowncast(128, value);\n }\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint120 from uint256, reverting on\n * overflow (when the input is greater than largest uint120).\n *\n * Counterpart to Solidity's `uint120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n */\n function toUint120(uint256 value) internal pure returns (uint120) {\n if (value > type(uint120).max) {\n revert SafeCastOverflowedUintDowncast(120, value);\n }\n return uint120(value);\n }\n\n /**\n * @dev Returns the downcasted uint112 from uint256, reverting on\n * overflow (when the input is greater than largest uint112).\n *\n * Counterpart to Solidity's `uint112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n */\n function toUint112(uint256 value) internal pure returns (uint112) {\n if (value > type(uint112).max) {\n revert SafeCastOverflowedUintDowncast(112, value);\n }\n return uint112(value);\n }\n\n /**\n * @dev Returns the downcasted uint104 from uint256, reverting on\n * overflow (when the input is greater than largest uint104).\n *\n * Counterpart to Solidity's `uint104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n */\n function toUint104(uint256 value) internal pure returns (uint104) {\n if (value > type(uint104).max) {\n revert SafeCastOverflowedUintDowncast(104, value);\n }\n return uint104(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n if (value > type(uint96).max) {\n revert SafeCastOverflowedUintDowncast(96, value);\n }\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint88 from uint256, reverting on\n * overflow (when the input is greater than largest uint88).\n *\n * Counterpart to Solidity's `uint88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n */\n function toUint88(uint256 value) internal pure returns (uint88) {\n if (value > type(uint88).max) {\n revert SafeCastOverflowedUintDowncast(88, value);\n }\n return uint88(value);\n }\n\n /**\n * @dev Returns the downcasted uint80 from uint256, reverting on\n * overflow (when the input is greater than largest uint80).\n *\n * Counterpart to Solidity's `uint80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n */\n function toUint80(uint256 value) internal pure returns (uint80) {\n if (value > type(uint80).max) {\n revert SafeCastOverflowedUintDowncast(80, value);\n }\n return uint80(value);\n }\n\n /**\n * @dev Returns the downcasted uint72 from uint256, reverting on\n * overflow (when the input is greater than largest uint72).\n *\n * Counterpart to Solidity's `uint72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n */\n function toUint72(uint256 value) internal pure returns (uint72) {\n if (value > type(uint72).max) {\n revert SafeCastOverflowedUintDowncast(72, value);\n }\n return uint72(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n if (value > type(uint64).max) {\n revert SafeCastOverflowedUintDowncast(64, value);\n }\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint56 from uint256, reverting on\n * overflow (when the input is greater than largest uint56).\n *\n * Counterpart to Solidity's `uint56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n */\n function toUint56(uint256 value) internal pure returns (uint56) {\n if (value > type(uint56).max) {\n revert SafeCastOverflowedUintDowncast(56, value);\n }\n return uint56(value);\n }\n\n /**\n * @dev Returns the downcasted uint48 from uint256, reverting on\n * overflow (when the input is greater than largest uint48).\n *\n * Counterpart to Solidity's `uint48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n */\n function toUint48(uint256 value) internal pure returns (uint48) {\n if (value > type(uint48).max) {\n revert SafeCastOverflowedUintDowncast(48, value);\n }\n return uint48(value);\n }\n\n /**\n * @dev Returns the downcasted uint40 from uint256, reverting on\n * overflow (when the input is greater than largest uint40).\n *\n * Counterpart to Solidity's `uint40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n */\n function toUint40(uint256 value) internal pure returns (uint40) {\n if (value > type(uint40).max) {\n revert SafeCastOverflowedUintDowncast(40, value);\n }\n return uint40(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n if (value > type(uint32).max) {\n revert SafeCastOverflowedUintDowncast(32, value);\n }\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint24 from uint256, reverting on\n * overflow (when the input is greater than largest uint24).\n *\n * Counterpart to Solidity's `uint24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n */\n function toUint24(uint256 value) internal pure returns (uint24) {\n if (value > type(uint24).max) {\n revert SafeCastOverflowedUintDowncast(24, value);\n }\n return uint24(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n if (value > type(uint16).max) {\n revert SafeCastOverflowedUintDowncast(16, value);\n }\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n if (value > type(uint8).max) {\n revert SafeCastOverflowedUintDowncast(8, value);\n }\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n if (value < 0) {\n revert SafeCastOverflowedIntToUint(value);\n }\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int248 from int256, reverting on\n * overflow (when the input is less than smallest int248 or\n * greater than largest int248).\n *\n * Counterpart to Solidity's `int248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n */\n function toInt248(int256 value) internal pure returns (int248 downcasted) {\n downcasted = int248(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(248, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int240 from int256, reverting on\n * overflow (when the input is less than smallest int240 or\n * greater than largest int240).\n *\n * Counterpart to Solidity's `int240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n */\n function toInt240(int256 value) internal pure returns (int240 downcasted) {\n downcasted = int240(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(240, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int232 from int256, reverting on\n * overflow (when the input is less than smallest int232 or\n * greater than largest int232).\n *\n * Counterpart to Solidity's `int232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n */\n function toInt232(int256 value) internal pure returns (int232 downcasted) {\n downcasted = int232(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(232, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int224 from int256, reverting on\n * overflow (when the input is less than smallest int224 or\n * greater than largest int224).\n *\n * Counterpart to Solidity's `int224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toInt224(int256 value) internal pure returns (int224 downcasted) {\n downcasted = int224(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(224, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int216 from int256, reverting on\n * overflow (when the input is less than smallest int216 or\n * greater than largest int216).\n *\n * Counterpart to Solidity's `int216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n */\n function toInt216(int256 value) internal pure returns (int216 downcasted) {\n downcasted = int216(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(216, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int208 from int256, reverting on\n * overflow (when the input is less than smallest int208 or\n * greater than largest int208).\n *\n * Counterpart to Solidity's `int208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n */\n function toInt208(int256 value) internal pure returns (int208 downcasted) {\n downcasted = int208(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(208, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int200 from int256, reverting on\n * overflow (when the input is less than smallest int200 or\n * greater than largest int200).\n *\n * Counterpart to Solidity's `int200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n */\n function toInt200(int256 value) internal pure returns (int200 downcasted) {\n downcasted = int200(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(200, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int192 from int256, reverting on\n * overflow (when the input is less than smallest int192 or\n * greater than largest int192).\n *\n * Counterpart to Solidity's `int192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n */\n function toInt192(int256 value) internal pure returns (int192 downcasted) {\n downcasted = int192(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(192, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int184 from int256, reverting on\n * overflow (when the input is less than smallest int184 or\n * greater than largest int184).\n *\n * Counterpart to Solidity's `int184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n */\n function toInt184(int256 value) internal pure returns (int184 downcasted) {\n downcasted = int184(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(184, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int176 from int256, reverting on\n * overflow (when the input is less than smallest int176 or\n * greater than largest int176).\n *\n * Counterpart to Solidity's `int176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n */\n function toInt176(int256 value) internal pure returns (int176 downcasted) {\n downcasted = int176(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(176, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int168 from int256, reverting on\n * overflow (when the input is less than smallest int168 or\n * greater than largest int168).\n *\n * Counterpart to Solidity's `int168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n */\n function toInt168(int256 value) internal pure returns (int168 downcasted) {\n downcasted = int168(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(168, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int160 from int256, reverting on\n * overflow (when the input is less than smallest int160 or\n * greater than largest int160).\n *\n * Counterpart to Solidity's `int160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n */\n function toInt160(int256 value) internal pure returns (int160 downcasted) {\n downcasted = int160(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(160, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int152 from int256, reverting on\n * overflow (when the input is less than smallest int152 or\n * greater than largest int152).\n *\n * Counterpart to Solidity's `int152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n */\n function toInt152(int256 value) internal pure returns (int152 downcasted) {\n downcasted = int152(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(152, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int144 from int256, reverting on\n * overflow (when the input is less than smallest int144 or\n * greater than largest int144).\n *\n * Counterpart to Solidity's `int144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n */\n function toInt144(int256 value) internal pure returns (int144 downcasted) {\n downcasted = int144(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(144, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int136 from int256, reverting on\n * overflow (when the input is less than smallest int136 or\n * greater than largest int136).\n *\n * Counterpart to Solidity's `int136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n */\n function toInt136(int256 value) internal pure returns (int136 downcasted) {\n downcasted = int136(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(136, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toInt128(int256 value) internal pure returns (int128 downcasted) {\n downcasted = int128(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(128, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int120 from int256, reverting on\n * overflow (when the input is less than smallest int120 or\n * greater than largest int120).\n *\n * Counterpart to Solidity's `int120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n */\n function toInt120(int256 value) internal pure returns (int120 downcasted) {\n downcasted = int120(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(120, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int112 from int256, reverting on\n * overflow (when the input is less than smallest int112 or\n * greater than largest int112).\n *\n * Counterpart to Solidity's `int112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n */\n function toInt112(int256 value) internal pure returns (int112 downcasted) {\n downcasted = int112(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(112, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int104 from int256, reverting on\n * overflow (when the input is less than smallest int104 or\n * greater than largest int104).\n *\n * Counterpart to Solidity's `int104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n */\n function toInt104(int256 value) internal pure returns (int104 downcasted) {\n downcasted = int104(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(104, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int96 from int256, reverting on\n * overflow (when the input is less than smallest int96 or\n * greater than largest int96).\n *\n * Counterpart to Solidity's `int96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toInt96(int256 value) internal pure returns (int96 downcasted) {\n downcasted = int96(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(96, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int88 from int256, reverting on\n * overflow (when the input is less than smallest int88 or\n * greater than largest int88).\n *\n * Counterpart to Solidity's `int88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n */\n function toInt88(int256 value) internal pure returns (int88 downcasted) {\n downcasted = int88(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(88, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int80 from int256, reverting on\n * overflow (when the input is less than smallest int80 or\n * greater than largest int80).\n *\n * Counterpart to Solidity's `int80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n */\n function toInt80(int256 value) internal pure returns (int80 downcasted) {\n downcasted = int80(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(80, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int72 from int256, reverting on\n * overflow (when the input is less than smallest int72 or\n * greater than largest int72).\n *\n * Counterpart to Solidity's `int72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n */\n function toInt72(int256 value) internal pure returns (int72 downcasted) {\n downcasted = int72(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(72, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toInt64(int256 value) internal pure returns (int64 downcasted) {\n downcasted = int64(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(64, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int56 from int256, reverting on\n * overflow (when the input is less than smallest int56 or\n * greater than largest int56).\n *\n * Counterpart to Solidity's `int56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n */\n function toInt56(int256 value) internal pure returns (int56 downcasted) {\n downcasted = int56(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(56, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int48 from int256, reverting on\n * overflow (when the input is less than smallest int48 or\n * greater than largest int48).\n *\n * Counterpart to Solidity's `int48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n */\n function toInt48(int256 value) internal pure returns (int48 downcasted) {\n downcasted = int48(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(48, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int40 from int256, reverting on\n * overflow (when the input is less than smallest int40 or\n * greater than largest int40).\n *\n * Counterpart to Solidity's `int40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n */\n function toInt40(int256 value) internal pure returns (int40 downcasted) {\n downcasted = int40(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(40, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toInt32(int256 value) internal pure returns (int32 downcasted) {\n downcasted = int32(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(32, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int24 from int256, reverting on\n * overflow (when the input is less than smallest int24 or\n * greater than largest int24).\n *\n * Counterpart to Solidity's `int24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n */\n function toInt24(int256 value) internal pure returns (int24 downcasted) {\n downcasted = int24(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(24, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toInt16(int256 value) internal pure returns (int16 downcasted) {\n downcasted = int16(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(16, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n */\n function toInt8(int256 value) internal pure returns (int8 downcasted) {\n downcasted = int8(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(8, value);\n }\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n if (value > uint256(type(int256).max)) {\n revert SafeCastOverflowedUintToInt(value);\n }\n return int256(value);\n }\n\n /**\n * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.\n */\n function toUint(bool b) internal pure returns (uint256 u) {\n assembly (\"memory-safe\") {\n u := iszero(iszero(b))\n }\n }\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/Panic.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Helper library for emitting standardized panic codes.\n *\n * ```solidity\n * contract Example {\n * using Panic for uint256;\n *\n * // Use any of the declared internal constants\n * function foo() { Panic.GENERIC.panic(); }\n *\n * // Alternatively\n * function foo() { Panic.panic(Panic.GENERIC); }\n * }\n * ```\n *\n * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].\n *\n * _Available since v5.1._\n */\n// slither-disable-next-line unused-state\nlibrary Panic {\n /// @dev generic / unspecified error\n uint256 internal constant GENERIC = 0x00;\n /// @dev used by the assert() builtin\n uint256 internal constant ASSERT = 0x01;\n /// @dev arithmetic underflow or overflow\n uint256 internal constant UNDER_OVERFLOW = 0x11;\n /// @dev division or modulo by zero\n uint256 internal constant DIVISION_BY_ZERO = 0x12;\n /// @dev enum conversion error\n uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;\n /// @dev invalid encoding in storage\n uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;\n /// @dev empty array pop\n uint256 internal constant EMPTY_ARRAY_POP = 0x31;\n /// @dev array out of bounds access\n uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;\n /// @dev resource error (too large allocation or too large array)\n uint256 internal constant RESOURCE_ERROR = 0x41;\n /// @dev calling invalid internal function\n uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;\n\n /// @dev Reverts with a panic code. Recommended to use with\n /// the internal constants with predefined codes.\n function panic(uint256 code) internal pure {\n assembly (\"memory-safe\") {\n mstore(0x00, 0x4e487b71)\n mstore(0x20, code)\n revert(0x1c, 0x24)\n }\n }\n}" + }, + "lib/rooster/v2-common/libraries/Constants.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\n// factory contraints on pools\nuint8 constant MAX_PROTOCOL_FEE_RATIO_D3 = 0.25e3; // 25%\nuint256 constant MAX_PROTOCOL_LENDING_FEE_RATE_D18 = 0.02e18; // 2%\nuint64 constant MAX_POOL_FEE_D18 = 0.9e18; // 90%\nuint64 constant MIN_LOOKBACK = 1 seconds;\n\n// pool constraints\nuint8 constant NUMBER_OF_KINDS = 4;\nint32 constant NUMBER_OF_KINDS_32 = int32(int8(NUMBER_OF_KINDS));\nuint256 constant MAX_TICK = 322_378; // max price 1e14 in D18 scale\nint32 constant MAX_TICK_32 = int32(int256(MAX_TICK));\nint32 constant MIN_TICK_32 = int32(-int256(MAX_TICK));\nuint256 constant MAX_BINS_TO_MERGE = 3;\nuint128 constant MINIMUM_LIQUIDITY = 1e8;\n\n// accessor named constants\nuint8 constant ALL_KINDS_MASK = 0xF; // 0b1111\nuint8 constant PERMISSIONED_LIQUIDITY_MASK = 0x10; // 0b010000\nuint8 constant PERMISSIONED_SWAP_MASK = 0x20; // 0b100000\nuint8 constant OPTIONS_MASK = ALL_KINDS_MASK | PERMISSIONED_LIQUIDITY_MASK | PERMISSIONED_SWAP_MASK; // 0b111111\n\n// named values\naddress constant MERGED_LP_BALANCE_ADDRESS = address(0);\nuint256 constant MERGED_LP_BALANCE_SUBACCOUNT = 0;\nuint128 constant ONE = 1e18;\nuint128 constant ONE_SQUARED = 1e36;\nint256 constant INT256_ONE = 1e18;\nuint256 constant ONE_D8 = 1e8;\nuint256 constant ONE_D3 = 1e3;\nint40 constant INT_ONE_D8 = 1e8;\nint40 constant HALF_TICK_D8 = 0.5e8;\nuint8 constant DEFAULT_DECIMALS = 18;\nuint256 constant DEFAULT_SCALE = 1;\nbytes constant EMPTY_PRICE_BREAKS = hex\"010000000000000000000000\";" + }, + "lib/rooster/v2-common/libraries/Math.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport {Math as OzMath} from \"../../openzeppelin-custom/contracts/utils/math/Math.sol\";\n\nimport {ONE, DEFAULT_SCALE, DEFAULT_DECIMALS, INT_ONE_D8, ONE_SQUARED} from \"./Constants.sol\";\n\n/**\n * @notice Math functions.\n */\nlibrary Math {\n /**\n * @notice Returns the lesser of two values.\n * @param x First uint256 value.\n * @param y Second uint256 value.\n */\n function min(uint256 x, uint256 y) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), lt(y, x)))\n }\n }\n\n /**\n * @notice Returns the lesser of two uint128 values.\n * @param x First uint128 value.\n * @param y Second uint128 value.\n */\n function min128(uint128 x, uint128 y) internal pure returns (uint128 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), lt(y, x)))\n }\n }\n\n /**\n * @notice Returns the lesser of two int256 values.\n * @param x First int256 value.\n * @param y Second int256 value.\n */\n function min(int256 x, int256 y) internal pure returns (int256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), slt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two uint256 values.\n * @param x First uint256 value.\n * @param y Second uint256 value.\n */\n function max(uint256 x, uint256 y) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), gt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two int256 values.\n * @param x First int256 value.\n * @param y Second int256 value.\n */\n function max(int256 x, int256 y) internal pure returns (int256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), sgt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two uint128 values.\n * @param x First uint128 value.\n * @param y Second uint128 value.\n */\n function max128(uint128 x, uint128 y) internal pure returns (uint128 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), gt(y, x)))\n }\n }\n\n /**\n * @notice Thresholds a value to be within the specified bounds.\n * @param value The value to bound.\n * @param lowerLimit The minimum allowable value.\n * @param upperLimit The maximum allowable value.\n */\n function boundValue(\n uint256 value,\n uint256 lowerLimit,\n uint256 upperLimit\n ) internal pure returns (uint256 outputValue) {\n outputValue = min(max(value, lowerLimit), upperLimit);\n }\n\n /**\n * @notice Returns the difference between two uint128 values or zero if the result would be negative.\n * @param x The minuend.\n * @param y The subtrahend.\n */\n function clip128(uint128 x, uint128 y) internal pure returns (uint128) {\n unchecked {\n return x < y ? 0 : x - y;\n }\n }\n\n /**\n * @notice Returns the difference between two uint256 values or zero if the result would be negative.\n * @param x The minuend.\n * @param y The subtrahend.\n */\n function clip(uint256 x, uint256 y) internal pure returns (uint256) {\n unchecked {\n return x < y ? 0 : x - y;\n }\n }\n\n /**\n * @notice Divides one uint256 by another, rounding down to the nearest\n * integer.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divFloor(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivFloor(x, ONE, y);\n }\n\n /**\n * @notice Divides one uint256 by another, rounding up to the nearest integer.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divCeil(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivCeil(x, ONE, y);\n }\n\n /**\n * @notice Multiplies two uint256 values and then divides by ONE, rounding down.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulFloor(uint256 x, uint256 y) internal pure returns (uint256) {\n return OzMath.mulDiv(x, y, ONE);\n }\n\n /**\n * @notice Multiplies two uint256 values and then divides by ONE, rounding up.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulCeil(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivCeil(x, y, ONE);\n }\n\n /**\n * @notice Calculates the multiplicative inverse of a uint256, rounding down.\n * @param x The value to invert.\n */\n function invFloor(uint256 x) internal pure returns (uint256) {\n unchecked {\n return ONE_SQUARED / x;\n }\n }\n\n /**\n * @notice Calculates the multiplicative inverse of a uint256, rounding up.\n * @param denominator The value to invert.\n */\n function invCeil(uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // divide z - 1 by the denominator and add 1.\n z := add(div(sub(ONE_SQUARED, 1), denominator), 1)\n }\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding down.\n * @param x The multiplicand.\n * @param y The multiplier.\n * @param k The divisor.\n */\n function mulDivFloor(uint256 x, uint256 y, uint256 k) internal pure returns (uint256 result) {\n result = OzMath.mulDiv(x, y, max(1, k));\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding up if there's a remainder.\n * @param x The multiplicand.\n * @param y The multiplier.\n * @param k The divisor.\n */\n function mulDivCeil(uint256 x, uint256 y, uint256 k) internal pure returns (uint256 result) {\n result = mulDivFloor(x, y, k);\n if (mulmod(x, y, max(1, k)) != 0) result = result + 1;\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding\n * down. Will revert if `x * y` is larger than `type(uint256).max`.\n * @param x The first operand for multiplication.\n * @param y The second operand for multiplication.\n * @param denominator The divisor after multiplication.\n */\n function mulDivDown(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // Store x * y in z for now.\n z := mul(x, y)\n if iszero(denominator) {\n denominator := 1\n }\n\n if iszero(or(iszero(x), eq(div(z, x), y))) {\n revert(0, 0)\n }\n\n // Divide z by the denominator.\n z := div(z, denominator)\n }\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding\n * up. Will revert if `x * y` is larger than `type(uint256).max`.\n * @param x The first operand for multiplication.\n * @param y The second operand for multiplication.\n * @param denominator The divisor after multiplication.\n */\n function mulDivUp(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // Store x * y in z for now.\n z := mul(x, y)\n if iszero(denominator) {\n denominator := 1\n }\n\n if iszero(or(iszero(x), eq(div(z, x), y))) {\n revert(0, 0)\n }\n\n // First, divide z - 1 by the denominator and add 1.\n // We allow z - 1 to underflow if z is 0, because we multiply the\n // end result by 0 if z is zero, ensuring we return 0 if z is zero.\n z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))\n }\n }\n\n /**\n * @notice Multiplies a uint256 by another and divides by a constant,\n * rounding down. Will revert if `x * y` is larger than\n * `type(uint256).max`.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulDown(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivDown(x, y, ONE);\n }\n\n /**\n * @notice Divides a uint256 by another, rounding down the result. Will\n * revert if `x * 1e18` is larger than `type(uint256).max`.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divDown(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivDown(x, ONE, y);\n }\n\n /**\n * @notice Divides a uint256 by another, rounding up the result. Will\n * revert if `x * 1e18` is larger than `type(uint256).max`.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divUp(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivUp(x, ONE, y);\n }\n\n /**\n * @notice Scales a number based on a difference in decimals from a default.\n * @param decimals The new decimal precision.\n */\n function scale(uint8 decimals) internal pure returns (uint256) {\n unchecked {\n if (decimals == DEFAULT_DECIMALS) {\n return DEFAULT_SCALE;\n } else {\n return 10 ** (DEFAULT_DECIMALS - decimals);\n }\n }\n }\n\n /**\n * @notice Adjusts a scaled amount to the token decimal scale.\n * @param amount The scaled amount.\n * @param scaleFactor The scaling factor to adjust by.\n * @param ceil Whether to round up (true) or down (false).\n */\n function ammScaleToTokenScale(uint256 amount, uint256 scaleFactor, bool ceil) internal pure returns (uint256 z) {\n unchecked {\n if (scaleFactor == DEFAULT_SCALE || amount == 0) {\n return amount;\n } else {\n if (!ceil) return amount / scaleFactor;\n assembly (\"memory-safe\") {\n z := add(div(sub(amount, 1), scaleFactor), 1)\n }\n }\n }\n }\n\n /**\n * @notice Adjusts a token amount to the D18 AMM scale.\n * @param amount The amount in token scale.\n * @param scaleFactor The scale factor for adjustment.\n */\n function tokenScaleToAmmScale(uint256 amount, uint256 scaleFactor) internal pure returns (uint256) {\n if (scaleFactor == DEFAULT_SCALE) {\n return amount;\n } else {\n return amount * scaleFactor;\n }\n }\n\n /**\n * @notice Returns the absolute value of a signed 32-bit integer.\n * @param x The integer to take the absolute value of.\n */\n function abs32(int32 x) internal pure returns (uint32) {\n unchecked {\n return uint32(x < 0 ? -x : x);\n }\n }\n\n /**\n * @notice Returns the absolute value of a signed 256-bit integer.\n * @param x The integer to take the absolute value of.\n */\n function abs(int256 x) internal pure returns (uint256) {\n unchecked {\n return uint256(x < 0 ? -x : x);\n }\n }\n\n /**\n * @notice Calculates the integer square root of a uint256 rounded down.\n * @param x The number to take the square root of.\n */\n function sqrt(uint256 x) internal pure returns (uint256 z) {\n // from https://github.com/transmissions11/solmate/blob/e8f96f25d48fe702117ce76c79228ca4f20206cb/src/utils/FixedPointMathLib.sol\n assembly (\"memory-safe\") {\n let y := x\n z := 181\n\n if iszero(lt(y, 0x10000000000000000000000000000000000)) {\n y := shr(128, y)\n z := shl(64, z)\n }\n if iszero(lt(y, 0x1000000000000000000)) {\n y := shr(64, y)\n z := shl(32, z)\n }\n if iszero(lt(y, 0x10000000000)) {\n y := shr(32, y)\n z := shl(16, z)\n }\n if iszero(lt(y, 0x1000000)) {\n y := shr(16, y)\n z := shl(8, z)\n }\n\n z := shr(18, mul(z, add(y, 65536)))\n\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n\n z := sub(z, lt(div(x, z), z))\n }\n }\n\n /**\n * @notice Computes the floor of a D8-scaled number as an int32, ignoring\n * potential overflow in the cast.\n * @param val The D8-scaled number.\n */\n function floorD8Unchecked(int256 val) internal pure returns (int32) {\n int32 val32;\n bool check;\n unchecked {\n val32 = int32(val / INT_ONE_D8);\n check = (val < 0 && val % INT_ONE_D8 != 0);\n }\n return check ? val32 - 1 : val32;\n }\n}" + }, + "lib/rooster/v2-common/libraries/TickMath.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport {Math as OzMath} from \"../../openzeppelin-custom/contracts/utils/math/Math.sol\";\nimport {Math} from \"./Math.sol\";\nimport {MAX_TICK, ONE} from \"./Constants.sol\";\n\n/**\n * @notice Math functions related to tick operations.\n */\n// slither-disable-start divide-before-multiply\nlibrary TickMath {\n using Math for uint256;\n\n error TickMaxExceeded(int256 tick);\n\n /**\n * @notice Compute the lower and upper sqrtPrice of a tick.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n */\n function tickSqrtPrices(\n uint256 tickSpacing,\n int32 _tick\n ) internal pure returns (uint256 sqrtLowerPrice, uint256 sqrtUpperPrice) {\n unchecked {\n sqrtLowerPrice = tickSqrtPrice(tickSpacing, _tick);\n sqrtUpperPrice = tickSqrtPrice(tickSpacing, _tick + 1);\n }\n }\n\n /**\n * @notice Compute the base tick value from the pool tick and the\n * tickSpacing. Revert if base tick is beyond the max tick boundary.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n */\n function subTickIndex(uint256 tickSpacing, int32 _tick) internal pure returns (uint32 subTick) {\n subTick = Math.abs32(_tick);\n subTick *= uint32(tickSpacing);\n if (subTick > MAX_TICK) {\n revert TickMaxExceeded(_tick);\n }\n }\n\n /**\n * @notice Calculate the square root price for a given tick and tick spacing.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n * @return _result The square root price.\n */\n function tickSqrtPrice(uint256 tickSpacing, int32 _tick) internal pure returns (uint256 _result) {\n unchecked {\n uint256 tick = subTickIndex(tickSpacing, _tick);\n\n uint256 ratio = tick & 0x1 != 0 ? 0xfffcb933bd6fad9d3af5f0b9f25db4d6 : 0x100000000000000000000000000000000;\n if (tick & 0x2 != 0) ratio = (ratio * 0xfff97272373d41fd789c8cb37ffcaa1c) >> 128;\n if (tick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656ac9229c67059486f389) >> 128;\n if (tick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e81259b3cddc7a064941) >> 128;\n if (tick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f67b19e8887e0bd251eb7) >> 128;\n if (tick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98cd2e57b660be99eb2c4a) >> 128;\n if (tick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c9838804e327cb417cafcb) >> 128;\n if (tick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99d51e2cc356c2f617dbe0) >> 128;\n if (tick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900aecf64236ab31f1f9dcb5) >> 128;\n if (tick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac4d9194200696907cf2e37) >> 128;\n if (tick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b88206f8abe8a3b44dd9be) >> 128;\n if (tick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c578ef4f1d17b2b235d480) >> 128;\n if (tick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd254ee83bdd3f248e7e785e) >> 128;\n if (tick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d8f7dd10e744d913d033333) >> 128;\n if (tick & 0x4000 != 0) ratio = (ratio * 0x70d869a156ddd32a39e257bc3f50aa9b) >> 128;\n if (tick & 0x8000 != 0) ratio = (ratio * 0x31be135f97da6e09a19dc367e3b6da40) >> 128;\n if (tick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7e5a9780b0cc4e25d61a56) >> 128;\n if (tick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedbcb3a6ccb7ce618d14225) >> 128;\n if (tick & 0x40000 != 0) ratio = (ratio * 0x2216e584f630389b2052b8db590e) >> 128;\n if (_tick > 0) ratio = type(uint256).max / ratio;\n _result = (ratio * ONE) >> 128;\n }\n }\n\n /**\n * @notice Calculate liquidity of a tick.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n */\n function getTickL(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n ) internal pure returns (uint256 liquidity) {\n // known:\n // - sqrt price values are different\n // - reserveA and reserveB fit in 128 bit\n // - sqrt price is in (1e-7, 1e7)\n // - D18 max for uint256 is 1.15e59\n // - D18 min is 1e-18\n\n unchecked {\n // diff is in (5e-12, 4e6); max tick spacing is 10_000\n uint256 diff = sqrtUpperTickPrice - sqrtLowerTickPrice;\n\n // Need to maximize precision by shifting small values A and B up so\n // that they use more of the available bit range. Two constraints to\n // consider: we need A * B * diff / sqrtPrice to be bigger than 1e-18\n // when the bump is not in play. This constrains the threshold for\n // bumping to be at least 77 bit; ie, either a or b needs 2^77 which\n // means that term A * B * diff / sqrtPrice > 1e-18.\n //\n // At the other end, the second constraint is that b^2 needs to fit in\n // a 256-bit number, so, post bump, the max reserve value needs to be\n // less than 6e22. With a 78-bit threshold and a 57-bit bump, we have A\n // and B are in (1.4e-1, 4.4e22 (2^(78+57))) with bump, and one of A or\n // B is at least 2^78 without the bump, but the other reserve value may\n // be as small as 1 wei.\n uint256 precisionBump = 0;\n if ((reserveA >> 78) == 0 && (reserveB >> 78) == 0) {\n precisionBump = 57;\n reserveA <<= precisionBump;\n reserveB <<= precisionBump;\n }\n\n if (reserveB == 0) return Math.divDown(reserveA, diff) >> precisionBump;\n if (reserveA == 0)\n return Math.mulDivDown(reserveB.mulDown(sqrtLowerTickPrice), sqrtUpperTickPrice, diff) >> precisionBump;\n\n // b is in (7.2e-9 (2^57 / 1e7 / 2), 2.8e29 (2^(78+57) * 1e7 / 2)) with bump\n // b is in a subset of the same range without bump\n uint256 b = (reserveA.divDown(sqrtUpperTickPrice) + reserveB.mulDown(sqrtLowerTickPrice)) >> 1;\n\n // b^2 is in (5.1e-17, 4.8e58); and will not overflow on either end;\n // A*B is in (3e-13 (2^78 / 1e18 * 1e-18), 1.9e45) without bump and is in a subset range with bump\n // A*B*diff/sqrtUpper is in (1.5e-17 (3e-13 * 5e-12 * 1e7), 7.6e58);\n\n // Since b^2 is at the upper edge of the precision range, we are not\n // able to multiply the argument of the sqrt by 1e18, instead, we move\n // this factor outside of the sqrt. The resulting loss of precision\n // means that this liquidity value is a lower bound on the tick\n // liquidity\n return\n OzMath.mulDiv(\n b +\n Math.sqrt(\n (OzMath.mulDiv(b, b, ONE) +\n OzMath.mulDiv(reserveB.mulFloor(reserveA), diff, sqrtUpperTickPrice))\n ) *\n 1e9,\n sqrtUpperTickPrice,\n diff\n ) >> precisionBump;\n }\n }\n\n /**\n * @notice Calculate square root price of a tick. Returns left edge of the\n * tick if the tick has no reserves.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n * @return sqrtPrice The calculated square root price.\n */\n function getSqrtPrice(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice,\n uint256 liquidity\n ) internal pure returns (uint256 sqrtPrice) {\n unchecked {\n if (reserveA == 0) {\n return sqrtLowerTickPrice;\n }\n if (reserveB == 0) {\n return sqrtUpperTickPrice;\n }\n sqrtPrice = Math.sqrt(\n ONE *\n (reserveA + liquidity.mulDown(sqrtLowerTickPrice)).divDown(\n reserveB + liquidity.divDown(sqrtUpperTickPrice)\n )\n );\n sqrtPrice = Math.boundValue(sqrtPrice, sqrtLowerTickPrice, sqrtUpperTickPrice);\n }\n }\n\n /**\n * @notice Calculate square root price of a tick. Returns left edge of the\n * tick if the tick has no reserves.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n * @return sqrtPrice The calculated square root price.\n * @return liquidity The calculated liquidity.\n */\n function getTickSqrtPriceAndL(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n ) internal pure returns (uint256 sqrtPrice, uint256 liquidity) {\n liquidity = getTickL(reserveA, reserveB, sqrtLowerTickPrice, sqrtUpperTickPrice);\n sqrtPrice = getSqrtPrice(reserveA, reserveB, sqrtLowerTickPrice, sqrtUpperTickPrice, liquidity);\n }\n}\n// slither-disable-end divide-before-multiply" + }, + "solidity-bytes-utils/contracts/BytesLib.sol": { + "content": "// SPDX-License-Identifier: Unlicense\n/*\n * @title Solidity Bytes Arrays Utils\n * @author Gonçalo Sá \n *\n * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.\n * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.\n */\npragma solidity >=0.8.0 <0.9.0;\n\n\nlibrary BytesLib {\n function concat(\n bytes memory _preBytes,\n bytes memory _postBytes\n )\n internal\n pure\n returns (bytes memory)\n {\n bytes memory tempBytes;\n\n assembly {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // Store the length of the first bytes array at the beginning of\n // the memory for tempBytes.\n let length := mload(_preBytes)\n mstore(tempBytes, length)\n\n // Maintain a memory counter for the current write location in the\n // temp bytes array by adding the 32 bytes for the array length to\n // the starting location.\n let mc := add(tempBytes, 0x20)\n // Stop copying when the memory counter reaches the length of the\n // first bytes array.\n let end := add(mc, length)\n\n for {\n // Initialize a copy counter to the start of the _preBytes data,\n // 32 bytes into its memory.\n let cc := add(_preBytes, 0x20)\n } lt(mc, end) {\n // Increase both counters by 32 bytes each iteration.\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // Write the _preBytes data into the tempBytes memory 32 bytes\n // at a time.\n mstore(mc, mload(cc))\n }\n\n // Add the length of _postBytes to the current length of tempBytes\n // and store it as the new length in the first 32 bytes of the\n // tempBytes memory.\n length := mload(_postBytes)\n mstore(tempBytes, add(length, mload(tempBytes)))\n\n // Move the memory counter back from a multiple of 0x20 to the\n // actual end of the _preBytes data.\n mc := end\n // Stop copying when the memory counter reaches the new combined\n // length of the arrays.\n end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n // Update the free-memory pointer by padding our last write location\n // to 32 bytes: add 31 bytes to the end of tempBytes to move to the\n // next 32 byte block, then round down to the nearest multiple of\n // 32. If the sum of the length of the two arrays is zero then add\n // one before rounding down to leave a blank 32 bytes (the length block with 0).\n mstore(0x40, and(\n add(add(end, iszero(add(length, mload(_preBytes)))), 31),\n not(31) // Round down to the nearest 32 bytes.\n ))\n }\n\n return tempBytes;\n }\n\n function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {\n assembly {\n // Read the first 32 bytes of _preBytes storage, which is the length\n // of the array. (We don't need to use the offset into the slot\n // because arrays use the entire slot.)\n let fslot := sload(_preBytes.slot)\n // Arrays of 31 bytes or less have an even value in their slot,\n // while longer arrays have an odd value. The actual length is\n // the slot divided by two for odd values, and the lowest order\n // byte divided by two for even values.\n // If the slot is even, bitwise and the slot with 255 and divide by\n // two to get the length. If the slot is odd, bitwise and the slot\n // with -1 and divide by two.\n let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)\n let mlength := mload(_postBytes)\n let newlength := add(slength, mlength)\n // slength can contain both the length and contents of the array\n // if length < 32 bytes so let's prepare for that\n // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage\n switch add(lt(slength, 32), lt(newlength, 32))\n case 2 {\n // Since the new array still fits in the slot, we just need to\n // update the contents of the slot.\n // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length\n sstore(\n _preBytes.slot,\n // all the modifications to the slot are inside this\n // next block\n add(\n // we can just add to the slot contents because the\n // bytes we want to change are the LSBs\n fslot,\n add(\n mul(\n div(\n // load the bytes from memory\n mload(add(_postBytes, 0x20)),\n // zero all bytes to the right\n exp(0x100, sub(32, mlength))\n ),\n // and now shift left the number of bytes to\n // leave space for the length in the slot\n exp(0x100, sub(32, newlength))\n ),\n // increase length by the double of the memory\n // bytes length\n mul(mlength, 2)\n )\n )\n )\n }\n case 1 {\n // The stored value fits in the slot, but the combined value\n // will exceed it.\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n let sc := add(keccak256(0x0, 0x20), div(slength, 32))\n\n // save new length\n sstore(_preBytes.slot, add(mul(newlength, 2), 1))\n\n // The contents of the _postBytes array start 32 bytes into\n // the structure. Our first read should obtain the `submod`\n // bytes that can fit into the unused space in the last word\n // of the stored array. To get this, we read 32 bytes starting\n // from `submod`, so the data we read overlaps with the array\n // contents by `submod` bytes. Masking the lowest-order\n // `submod` bytes allows us to add that value directly to the\n // stored value.\n\n let submod := sub(32, slength)\n let mc := add(_postBytes, submod)\n let end := add(_postBytes, mlength)\n let mask := sub(exp(0x100, submod), 1)\n\n sstore(\n sc,\n add(\n and(\n fslot,\n 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00\n ),\n and(mload(mc), mask)\n )\n )\n\n for {\n mc := add(mc, 0x20)\n sc := add(sc, 1)\n } lt(mc, end) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n sstore(sc, mload(mc))\n }\n\n mask := exp(0x100, sub(mc, end))\n\n sstore(sc, mul(div(mload(mc), mask), mask))\n }\n default {\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n // Start copying to the last used word of the stored array.\n let sc := add(keccak256(0x0, 0x20), div(slength, 32))\n\n // save new length\n sstore(_preBytes.slot, add(mul(newlength, 2), 1))\n\n // Copy over the first `submod` bytes of the new data as in\n // case 1 above.\n let slengthmod := mod(slength, 32)\n let mlengthmod := mod(mlength, 32)\n let submod := sub(32, slengthmod)\n let mc := add(_postBytes, submod)\n let end := add(_postBytes, mlength)\n let mask := sub(exp(0x100, submod), 1)\n\n sstore(sc, add(sload(sc), and(mload(mc), mask)))\n\n for {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } lt(mc, end) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n sstore(sc, mload(mc))\n }\n\n mask := exp(0x100, sub(mc, end))\n\n sstore(sc, mul(div(mload(mc), mask), mask))\n }\n }\n }\n\n function slice(\n bytes memory _bytes,\n uint256 _start,\n uint256 _length\n )\n internal\n pure\n returns (bytes memory)\n {\n // We're using the unchecked block below because otherwise execution ends \n // with the native overflow error code.\n unchecked {\n require(_length + 31 >= _length, \"slice_overflow\");\n }\n require(_bytes.length >= _start + _length, \"slice_outOfBounds\");\n\n bytes memory tempBytes;\n\n assembly {\n switch iszero(_length)\n case 0 {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // The first word of the slice result is potentially a partial\n // word read from the original array. To read it, we calculate\n // the length of that partial word and start copying that many\n // bytes into the array. The first word we copy will start with\n // data we don't care about, but the last `lengthmod` bytes will\n // land at the beginning of the contents of the new array. When\n // we're done copying, we overwrite the full first word with\n // the actual length of the slice.\n let lengthmod := and(_length, 31)\n\n // The multiplication in the next line is necessary\n // because when slicing multiples of 32 bytes (lengthmod == 0)\n // the following copy loop was copying the origin's length\n // and then ending prematurely not copying everything it should.\n let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))\n let end := add(mc, _length)\n\n for {\n // The multiplication in the next line has the same exact purpose\n // as the one above.\n let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n mstore(tempBytes, _length)\n\n //update free-memory pointer\n //allocating the array padded to 32 bytes like the compiler does now\n mstore(0x40, and(add(mc, 31), not(31)))\n }\n //if we want a zero-length slice let's just return a zero-length array\n default {\n tempBytes := mload(0x40)\n //zero out the 32 bytes slice we are about to return\n //we need to do it because Solidity does not garbage collect\n mstore(tempBytes, 0)\n\n mstore(0x40, add(tempBytes, 0x20))\n }\n }\n\n return tempBytes;\n }\n\n function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {\n require(_bytes.length >= _start + 20, \"toAddress_outOfBounds\");\n address tempAddress;\n\n assembly {\n tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)\n }\n\n return tempAddress;\n }\n\n function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {\n require(_bytes.length >= _start + 1 , \"toUint8_outOfBounds\");\n uint8 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x1), _start))\n }\n\n return tempUint;\n }\n\n function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {\n require(_bytes.length >= _start + 2, \"toUint16_outOfBounds\");\n uint16 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x2), _start))\n }\n\n return tempUint;\n }\n\n function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {\n require(_bytes.length >= _start + 4, \"toUint32_outOfBounds\");\n uint32 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x4), _start))\n }\n\n return tempUint;\n }\n\n function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {\n require(_bytes.length >= _start + 8, \"toUint64_outOfBounds\");\n uint64 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x8), _start))\n }\n\n return tempUint;\n }\n\n function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {\n require(_bytes.length >= _start + 12, \"toUint96_outOfBounds\");\n uint96 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0xc), _start))\n }\n\n return tempUint;\n }\n\n function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {\n require(_bytes.length >= _start + 16, \"toUint128_outOfBounds\");\n uint128 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x10), _start))\n }\n\n return tempUint;\n }\n\n function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {\n require(_bytes.length >= _start + 32, \"toUint256_outOfBounds\");\n uint256 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x20), _start))\n }\n\n return tempUint;\n }\n\n function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {\n require(_bytes.length >= _start + 32, \"toBytes32_outOfBounds\");\n bytes32 tempBytes32;\n\n assembly {\n tempBytes32 := mload(add(add(_bytes, 0x20), _start))\n }\n\n return tempBytes32;\n }\n\n function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {\n bool success = true;\n\n assembly {\n let length := mload(_preBytes)\n\n // if lengths don't match the arrays are not equal\n switch eq(length, mload(_postBytes))\n case 1 {\n // cb is a circuit breaker in the for loop since there's\n // no said feature for inline assembly loops\n // cb = 1 - don't breaker\n // cb = 0 - break\n let cb := 1\n\n let mc := add(_preBytes, 0x20)\n let end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n // the next line is the loop condition:\n // while(uint256(mc < end) + cb == 2)\n } eq(add(lt(mc, end), cb), 2) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // if any of these checks fails then arrays are not equal\n if iszero(eq(mload(mc), mload(cc))) {\n // unsuccess:\n success := 0\n cb := 0\n }\n }\n }\n default {\n // unsuccess:\n success := 0\n }\n }\n\n return success;\n }\n\n function equalStorage(\n bytes storage _preBytes,\n bytes memory _postBytes\n )\n internal\n view\n returns (bool)\n {\n bool success = true;\n\n assembly {\n // we know _preBytes_offset is 0\n let fslot := sload(_preBytes.slot)\n // Decode the length of the stored array like in concatStorage().\n let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)\n let mlength := mload(_postBytes)\n\n // if lengths don't match the arrays are not equal\n switch eq(slength, mlength)\n case 1 {\n // slength can contain both the length and contents of the array\n // if length < 32 bytes so let's prepare for that\n // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage\n if iszero(iszero(slength)) {\n switch lt(slength, 32)\n case 1 {\n // blank the last byte which is the length\n fslot := mul(div(fslot, 0x100), 0x100)\n\n if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {\n // unsuccess:\n success := 0\n }\n }\n default {\n // cb is a circuit breaker in the for loop since there's\n // no said feature for inline assembly loops\n // cb = 1 - don't breaker\n // cb = 0 - break\n let cb := 1\n\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n let sc := keccak256(0x0, 0x20)\n\n let mc := add(_postBytes, 0x20)\n let end := add(mc, mlength)\n\n // the next line is the loop condition:\n // while(uint256(mc < end) + cb == 2)\n for {} eq(add(lt(mc, end), cb), 2) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n if iszero(eq(sload(sc), mload(mc))) {\n // unsuccess:\n success := 0\n cb := 0\n }\n }\n }\n }\n }\n default {\n // unsuccess:\n success := 0\n }\n }\n\n return success;\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/57cc28372fe42448eae078d6972e3bd4.json b/contracts/deployments/hoodi/solcInputs/57cc28372fe42448eae078d6972e3bd4.json new file mode 100644 index 0000000000..e1bc5ca8d7 --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/57cc28372fe42448eae078d6972e3bd4.json @@ -0,0 +1,99 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the Beacon Block Root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Block Root Oracle to get the parent block root\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n enum BalanceProofLevel {\n Container,\n BeaconBlock\n }\n\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view;\n\n function verifyBlockNumber(\n bytes32 beaconBlockRoot,\n uint256 blockNumber,\n bytes calldata blockNumberProof\n ) external view;\n\n function verifySlot(\n bytes32 beaconBlockRoot,\n uint256 slot,\n bytes calldata slotProof\n ) external view;\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n external\n pure\n returns (uint256);\n\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) external pure returns (uint256);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconProofs,\n _beaconGenesisTimestamp\n )\n {\n SSV_TOKEN = _ssvToken;\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// @param _rewardTokenAddresses Not used so empty array\n /// @param _assets Not used so empty array\n /// @param _pTokens Not used so empty array\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH that was transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _asset, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(_asset, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, WETH, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Accounts for all the assets managed by this strategy which includes:\n /// 1. The current WETH in this strategy contract\n /// 2. The last verified ETH balance, total deposits and total validator balances\n /// @param _asset Address of WETH asset.\n /// @return balance Total value in ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by the strategy.\n /// @param _asset The address of the WETH token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for validator registration.\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens when validators are registered\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n * The new ETH will be accounted for in `checkBalance` after the next snapBalances and verifyBalances txs.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n /// @dev This strategy does not use a platform token like the old Aave and Compound strategies.\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instead of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract.\n /// Withdrawals from validators also accumulate as ETH in this strategy contract.\n /// It's too complex to separate the rewards from withdrawals so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can now regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable {\n using SafeERC20 for IERC20;\n\n /// @notice The amount of ETH in wei that is required for a deposit to a new validator.\n /// Initially this is 32 ETH, but will be increased to 1 ETH after P2P's APIs have been updated\n /// to support deposits of 1 ETH.\n uint256 public constant DEPOSIT_AMOUNT_WEI = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n /// @notice The timestamp of the Beacon chain genesis.\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\n uint64 public immutable BEACON_GENESIS_TIMESTAMP;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// Deposit data for new compounding validators.\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountWei Amount of ETH in wei that has been deposited to the beacon chain deposit contract\n /// @param blockNumber Block number when the deposit was made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either PENDING or PROVEN\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint32 depositIndex;\n DepositStatus status;\n }\n /// @notice Mapping of the root of a deposit (depositDataRoot) to its data\n mapping(bytes32 => DepositData) public deposits;\n /// @notice List of deposit roots that are still to be verified as processed on the beacon chain\n bytes32[] public depositsRoots;\n\n // Validator data\n\n struct ValidatorData {\n bytes32 pubKeyHash; // Hash of the validator's public key using the Beacon Chain's format\n uint64 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes and indexes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n ValidatorData[] internal verifiedValidators;\n /// @notice State of the new compounding validators with a 0x02 withdrawal credential prefix.\n /// Uses the Beacon chain hashing for BLSPubkey which is\n /// sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => VALIDATOR_STATE) public validatorState;\n\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n uint64 timestamp;\n uint128 ethBalance;\n }\n // TODO is it more efficient to use the block root rather than hashing it?\n /// @notice Mapping of the block root to the balances at that slot\n mapping(bytes32 => Balances) public snappedBalances;\n uint64 public lastSnapTimestamp;\n uint128 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[50] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITED, // The validator has been verified to have a zero balance\n REMOVED // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified and is ready to be staked\n }\n\n event RegistratorChanged(address indexed newAddress);\n event SourceStrategyAdded(address indexed strategy);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n bytes32 indexed depositDataRoot,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint64 indexed validatorIndex\n );\n event DepositVerified(bytes32 indexed depositDataRoot, uint256 amountWei);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(\n uint256 indexed timestamp,\n bytes32 indexed blockRoot,\n uint256 ethBalance\n );\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 wethBalance,\n uint256 ethBalance\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_PROOFS = _beaconProofs;\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\n\n require(\n block.timestamp > _beaconGenesisTimestamp,\n \"Invalid genesis timestamp\"\n );\n }\n\n /***************************************\n Admin Functions\n ****************************************/\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /***************************************`\n Validator Management\n ****************************************/\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validatorState[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validator validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validator,\n uint64 depositAmountGwei\n ) external onlyRegistrator {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validator.pubkey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == VALIDATOR_STATE.REGISTERED ||\n currentState == VALIDATOR_STATE.VERIFIED),\n \"Not registered or verified\"\n );\n require(\n currentState == VALIDATOR_STATE.VERIFIED ||\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validator.pubkey,\n withdrawalCredentials,\n validator.signature,\n validator.depositDataRoot\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == VALIDATOR_STATE.REGISTERED) {\n validatorState[pubKeyHash] = VALIDATOR_STATE.STAKED;\n }\n\n /// After the Pectra upgrade the validators have a new restriction when proposing\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\n /// forward. Each slot is created at strict 12 second intervals and those slots can\n /// either have blocks attached to them or not. This way using the block.timestamp\n /// the slot number can easily be calculated.\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\n BEACON_GENESIS_TIMESTAMP) / 12;\n\n // Store the deposit data for verifyDeposit and verifyBalances\n deposits[validator.depositDataRoot] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n slot: depositSlot,\n depositIndex: SafeCast.toUint32(depositsRoots.length),\n status: DepositStatus.PENDING\n });\n depositsRoots.push(validator.depositDataRoot);\n\n emit ETHStaked(\n pubKeyHash,\n validator.depositDataRoot,\n validator.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then the request will fail.\n /// Only the Registrator can call this function.\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\n /// If no ETH balance, the tx will revert.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n payable\n onlyRegistrator\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove a sweeping validator from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == VALIDATOR_STATE.EXITED ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exited\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validatorState[pubKeyHash] = VALIDATOR_STATE.REMOVED;\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /***************************************\n SSV Management\n ****************************************/\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n Beacon Chain Proofs\n ****************************************/\n\n /// @notice Verifies a validator's index to its public key.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param validatorPubKeyProof The merkle proof that the validator's index matches its public key\n /// on the beacon chain.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.STAKED,\n \"Validator not staked\"\n );\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key.\n // Also verify the validator's withdrawal credential points to this strategy.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n address(this) // Withdrawal address is this strategy\n );\n\n // Store the validator state as verified\n validatorState[pubKeyHash] = VALIDATOR_STATE.VERIFIED;\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(\n ValidatorData({ pubKeyHash: pubKeyHash, index: validatorIndex })\n );\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n ///\n /// Important: this function has a limitation where the `verificationSlot` that is passed by the off-chain\n /// verifier requires a slot immediately after it to propose a block otherwise the `BeaconRoots.parentBlockRoot`\n /// will fail. This shouldn't be a problem, since by the current behaviour of beacon chain only 1%-3% slots\n /// don't propose a block.\n /// @param depositDataRoot The root of the deposit data that was stored when\n /// the deposit was made on the execution layer.\n /// @param verificationSlot Any slot on or after the deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\n /// set for the next block timestamp in 12 seconds time.\n /// @param firstPendingDepositSlot The slot of the first pending deposit in the beacon chain\n /// @param firstPendingDepositSlotProof The merkle proof that the slot of the first pending deposit\n /// matches the beacon chain.\n /// BeaconBlock.BeaconBlockBody.deposits[0].slot\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n bytes32 depositDataRoot,\n uint64 verificationSlot,\n uint64 firstPendingDepositSlot,\n bytes calldata firstPendingDepositSlotProof\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositDataRoot];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n validatorState[deposit.pubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the first pending deposit's slot is 0 then there are no pending deposits so\n // our deposit must have been processed on the beacon chain.\n require(\n deposit.slot < firstPendingDepositSlot ||\n firstPendingDepositSlot == 0,\n \"Deposit not processed\"\n );\n\n // The verification slot must be after the deposit's slot.\n // This is needed for when the deposit queue is empty. That is, firstPendingDepositSlot is 0.\n require(deposit.slot < verificationSlot, \"Slot not after deposit\");\n\n // Calculate the next block timestamp from the verification slot.\n uint64 nextBlockTimestamp = 12 *\n verificationSlot +\n BEACON_GENESIS_TIMESTAMP +\n // Add 12 seconds for the next block\n 12;\n // Get the parent beacon block root of the next block which is the block root of the verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the slot of the first pending deposit matches the beacon chain\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n blockRoot,\n firstPendingDepositSlot,\n firstPendingDepositSlotProof\n );\n\n // After verifying the proof, update the contract storage\n deposits[depositDataRoot].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n bytes32 lastDepositDataRoot = depositsRoots[depositsRoots.length - 1];\n depositsRoots[deposit.depositIndex] = lastDepositDataRoot;\n deposits[lastDepositDataRoot].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositsRoots.pop();\n\n emit DepositVerified(\n depositDataRoot,\n uint256(deposit.amountGwei) * 1 gwei\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block and beacon block root\n /// of the slot that is associated with the previous block.\n ///\n /// When snapping / verifying balance it is of a high importance that there is no\n /// miss-match in respect to ETH that is held by the contract and balances that are\n /// verified on the validators.\n ///\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\n /// constructing a block on the beacon chain consist of:\n /// - process_withdrawals: ETH is deducted from the validator's balance\n /// - process_execution_payload: immediately after the previous step executing all the\n /// transactions\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\n /// contained in the withdrawal credentials of the exited validators\n ///\n /// That means that balance increases which are part of the post-block execution state are\n /// done within the block, but the transaction that are contained within that block can not\n /// see / interact with the balance from the exited validators. Only transactions in the\n /// next block can do that.\n ///\n /// When snap balances is performed the state of the chain is snapped across 2 separate\n /// chain states:\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\n /// will be referred to as Y - 1 as it makes no difference in the argument\n ///\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\n /// miss-counting ETH or much more dangerous double counting of the ETH.\n ///\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\n /// - ETH balance in the contract on block X\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\n /// - ETH balance in validators that are active in slot Y - 1\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\n /// and have their balance visible to transactions in slot Y and corresponding block X\n /// (or sooner)\n ///\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\n ///\n /// *ETH balance in the contract on block X*\n ///\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\n ///\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\n /// would result in double counting the `stakedEth` since it would be present once in the\n /// snapped contract balance and the second time in deposit storage variables.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\n ///\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\n /// contract at block Z. The execution layer doesn't have direct access to the state of\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\n /// and could also be part of the validator balances. It does that by verifying that at\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\n /// the last snap till now all are still in queue. Which ensures they can not be part of\n /// the validator balances in later steps.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in validators that are active in slot Y - 1*\n ///\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\n /// correctness.\n ///\n /// This behaviour is correct.\n ///\n /// *The withdrawn validators*\n ///\n /// The withdrawn validators could have their balances deducted in any slot before slot\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\n /// look at the \"worst case scenario\" where the validator withdrawal is processed in the\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\n /// even if the validator exits at the latest possible time it is paramount that the ETH\n /// balance on the execution layer is recorded in the next block. Correctly accounting\n /// for the withdrawn ETH.\n ///\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\n /// validator balance verification as well as execution layer contract balance snap.\n ///\n /// This behaviour is correct.\n ///\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n /// Can only be called by the registrator.\n function snapBalances() external onlyRegistrator {\n _snapBalances();\n }\n\n function _snapBalances() internal {\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(\n SafeCast.toUint64(block.timestamp)\n );\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the balances in the mapping\n snappedBalances[blockRoot] = Balances({\n timestamp: SafeCast.toUint64(block.timestamp),\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n // Store the snapped timestamp\n lastSnapTimestamp = SafeCast.toUint64(block.timestamp);\n\n emit BalancesSnapped(block.timestamp, blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct VerifyBalancesParams {\n bytes32 blockRoot;\n uint64 firstPendingDepositSlot;\n // BeaconBlock.BeaconBlockBody.deposits[0].slot\n bytes firstPendingDepositSlotProof;\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(VerifyBalancesParams calldata params) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalances[params.blockRoot];\n // Check the balances are the latest\n require(lastSnapTimestamp > 0, \"No snapped balances\");\n require(balancesMem.timestamp == lastSnapTimestamp, \"Stale snap\");\n\n uint256 depositsCount = depositsRoots.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit to the beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n params.blockRoot,\n params.firstPendingDepositSlot,\n params.firstPendingDepositSlotProof\n );\n\n // For each native staking contract's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n bytes32 depositDataRoot = depositsRoots[i];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit slot is before the\n // slot of the staking strategy's deposit.\n // If the deposit has been processed, it will need to be verified with `verifyDeposit`\n require(\n params.firstPendingDepositSlot <\n deposits[depositDataRoot].slot,\n \"Deposit has been processed\"\n );\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei +=\n uint256(deposits[depositDataRoot].amountGwei) *\n 1 gwei;\n }\n }\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n params.validatorBalanceProofs.length == verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n params.validatorBalanceLeaves.length == verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n params.blockRoot,\n params.balancesContainerRoot,\n params.balancesContainerProof\n );\n\n // for each validator in reserve order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n params.balancesContainerRoot,\n params.validatorBalanceLeaves[i],\n params.validatorBalanceProofs[i],\n verifiedValidators[i].index\n );\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n validatorState[\n verifiedValidators[i].pubKeyHash\n ] = VALIDATOR_STATE.EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += uint256(validatorBalanceGwei) * 1 gwei;\n }\n }\n\n uint256 wethBalance = IWETH9(WETH).balanceOf(address(this));\n\n // Store the verified balance in storage\n lastVerifiedEthBalance = SafeCast.toUint128(\n totalDepositsWei + totalValidatorBalance + balancesMem.ethBalance\n );\n // Reset the last snap timestamp so a new snapBalances has to be made\n lastSnapTimestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n wethBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /***************************************\n WETH and ETH Accounting\n ****************************************/\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance\n if (lastVerifiedEthBalance > _ethAmount) {\n lastVerifiedEthBalance -= SafeCast.toUint128(_ethAmount);\n } else {\n // This can happen if all ETH in the validators was withdrawn\n // and there was more consensus rewards since the last balance verification.\n // Or it can happen if ETH is donated to this strategy.\n lastVerifiedEthBalance = 0;\n }\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += SafeCast.toUint128(_wethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /***************************************\n View Functions\n ****************************************/\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return An array of `ValidatorData` containing the public key hash and validator index.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorData[] memory)\n {\n return verifiedValidators;\n }\n\n /// @notice Returns the deposits that are still to be verified.\n /// These may or may not have been processed by the beacon chain.\n function getPendingDeposits()\n external\n view\n returns (DepositData[] memory pendingDeposits)\n {\n pendingDeposits = new DepositData[](depositsRoots.length);\n for (uint256 i = 0; i < depositsRoots.length; ++i) {\n pendingDeposits[i] = deposits[depositsRoots[i]];\n }\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/591dbf810f743105fcf16d59a6f0c2f9.json b/contracts/deployments/hoodi/solcInputs/591dbf810f743105fcf16d59a6f0c2f9.json new file mode 100644 index 0000000000..fb0c061e83 --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/591dbf810f743105fcf16d59a6f0c2f9.json @@ -0,0 +1,117 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/beacon/BeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"./BeaconProofsLib.sol\";\nimport { IBeaconProofs } from \"../interfaces/IBeaconProofs.sol\";\n\n/**\n * @title Verifies merkle proofs of beacon chain data.\n * @author Origin Protocol Inc\n */\ncontract BeaconProofs is IBeaconProofs {\n /// @notice Verifies the validator index is for the given validator public key.\n /// Also verify the validator's withdrawal credential points to the withdrawal address.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param proof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata proof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view {\n BeaconProofsLib.verifyValidator(\n beaconBlockRoot,\n pubKeyHash,\n proof,\n validatorIndex,\n withdrawalAddress\n );\n }\n\n /// @notice Verifies a validator's withdrawable epoch to the beacon block root\n /// for a given validator index.\n /// Also verifies the validator's public key for the given validator index.\n /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n /// @param beaconBlockRoot The root of the beacon block\n /// @param validatorIndex The validator index to verify the withdrawable epoch for.\n /// @param withdrawableEpoch The withdrawable epoch to verify in big endian uint64 format\n /// @param withdrawableEpochProof The merkle proof for the validator's withdrawable epoch to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorPubKeyProof The merkle proof for the validator public key in a sub tree of height two.\n /// This is 2 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof,\n bytes calldata validatorPubKeyProof\n ) external view {\n BeaconProofsLib.verifyValidatorWithdrawableEpoch(\n beaconBlockRoot,\n validatorIndex,\n withdrawableEpoch,\n withdrawableEpochProof\n );\n\n // Get the third 32 byte witness from the withdrawable epoch proof\n // 2 * 32 bytes = 64 bytes offset\n bytes32 subTreeRoot = bytes32(withdrawableEpochProof[64:96]);\n\n BeaconProofsLib.verifyValidatorPubKeySubTree(\n subTreeRoot,\n pubKeyHash,\n validatorPubKeyProof\n );\n }\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint64 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view {\n BeaconProofsLib.verifyValidatorWithdrawableEpoch(\n beaconBlockRoot,\n validatorIndex,\n withdrawableEpoch,\n withdrawableEpochProof\n );\n }\n\n /// @notice Verifies the balances container to the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerRoot The merkle root of the the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata balancesContainerProof\n ) external view {\n BeaconProofsLib.verifyBalancesContainer(\n beaconBlockRoot,\n balancesContainerRoot,\n balancesContainerProof\n );\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param balanceProof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalanceGwei) {\n validatorBalanceGwei = BeaconProofsLib.verifyValidatorBalance(\n balancesContainerRoot,\n validatorBalanceLeaf,\n balanceProof,\n validatorIndex\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the pubKey and slot of the first pending deposit to the beacon block root.\n /// BeaconBlock.state.PendingDeposits[0].pubKey\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero would be a good choice.\n /// @param pubKeyHash The hash of the validator public key for the first pending deposit.\n /// Use zero bytes if the deposit queue is empty.\n /// @param firstPendingDepositProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].pubKey when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes32 pubKeyHash,\n bytes calldata firstPendingDepositProof\n ) external view returns (bool isEmptyDepositQueue) {\n isEmptyDepositQueue = BeaconProofsLib.verifyFirstPendingDeposit(\n beaconBlockRoot,\n slot,\n pubKeyHash,\n firstPendingDepositProof\n );\n }\n}\n" + }, + "contracts/beacon/BeaconProofsLib.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Merkle } from \"./Merkle.sol\";\nimport { Endian } from \"./Endian.sol\";\n\n/**\n * @title Library to verify merkle proofs of beacon chain data.\n * @author Origin Protocol Inc\n */\nlibrary BeaconProofsLib {\n // Known generalized indices in the beacon block\n /// @dev BeaconBlock.state.PendingDeposits[0]\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, pending deposits at index 34\n /// Pending deposits container: height 28, first deposit at index 0\n /// ((2 ^ 3 + 3) * 2 ^ 6 + 34) * 2 ^ 28 + 0 = 198105366528\n uint256 internal constant FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX =\n 198105366528;\n /// @dev BeaconBlock.state.PendingDeposits[0].pubkey\n /// Pending Deposit container: height 3, pubkey at index 0\n /// (((2 ^ 3 + 3) * 2 ^ 6 + 34) * 2 ^ 28 + 0) * 2 ^ 3 + 0 = 1584842932224\n uint256 internal constant FIRST_PENDING_DEPOSIT_PUBKEY_GENERALIZED_INDEX =\n 1584842932224;\n /// @dev BeaconBlock.state.validators\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, validators at index 11\n /// (2 ^ 3 + 3) * 2 ^ 6 + 11 = 715\n uint256 internal constant VALIDATORS_CONTAINER_GENERALIZED_INDEX = 715;\n /// @dev BeaconBlock.state.balances\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, balances at index 13\n /// (2 ^ 3 + 3) * 2 ^ 6 + 13 = 716\n uint256 internal constant BALANCES_CONTAINER_GENERALIZED_INDEX = 716;\n\n /// @dev Number of bytes in the proof to the first pending deposit.\n /// 37 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0]\n /// 37 * 32 bytes = 1184 bytes\n uint256 internal constant FIRST_PENDING_DEPOSIT_PROOF_LENGTH = 1184;\n /// @dev Number of bytes in the proof from the pubKey of the first pending deposit to the beacon block root.\n /// 40 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0].pubKey\n /// 40 * 32 bytes = 1280 bytes\n uint256 internal constant FIRST_PENDING_DEPOSIT_PUBKEY_PROOF_LENGTH = 1280;\n /// The slot is at index 4 in the Pending Deposits container.\n /// The sub tree from the right node from the root is a tree of height 2.\n /// The first 32 bytes witness is an empty bytes32 as there are\n /// no items after the slot in the Pending Deposits container.\n /// The second 32 bytes witness is a hash or two empty bytes32.\n bytes internal constant PENDING_DEPOSIT_SLOT_PROOF =\n // solhint-disable-next-line max-line-length\n hex\"0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b\";\n\n /// @dev Merkle height of the Balances container\n /// BeaconBlock.state.balances\n uint256 internal constant BALANCES_HEIGHT = 39;\n /// @dev Merkle height of the Validators container list\n /// BeaconBlock.state.validators\n uint256 internal constant VALIDATORS_LIST_HEIGHT = 41;\n /// @dev Merkle height of the Validator container\n /// BeaconBlock.state.validators[validatorIndex]\n uint256 internal constant VALIDATOR_CONTAINER_HEIGHT = 3;\n\n /// @dev Position of the pubkey field in the Validator container.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;\n /// @dev Position of the withdrawable epoch field in the Validator container.\n /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n uint256 internal constant VALIDATOR_WITHDRAWABLE_EPOCH_INDEX = 7;\n\n /// @notice Verifies the validator index is for the given validator public key.\n /// Also verify the validator's withdrawal credential points to the withdrawal address.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param proof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata proof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.validators[validatorIndex]\n uint256 generalizedIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_LIST_HEIGHT,\n validatorIndex\n );\n // BeaconBlock.state.validators[validatorIndex].pubkey\n generalizedIndex = concatGenIndices(\n generalizedIndex,\n VALIDATOR_CONTAINER_HEIGHT,\n VALIDATOR_PUBKEY_INDEX\n );\n\n // Get the withdrawal address from the first witness in the pubkey merkle proof.\n address withdrawalAddressFromProof;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // The first 32 bytes of the proof is the withdrawal credential so load it into memory.\n calldatacopy(0, proof.offset, 32)\n // Cast the 32 bytes in memory to an address which is the last 20 bytes.\n withdrawalAddressFromProof := mload(0)\n }\n require(\n withdrawalAddressFromProof == withdrawalAddress,\n \"Invalid withdrawal address\"\n );\n\n require(\n // 53 * 32 bytes = 1696 bytes\n proof.length == 1696 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: pubKeyHash,\n index: generalizedIndex\n }),\n \"Invalid validator proof\"\n );\n }\n\n /// @notice Verifies a validator's withdrawable epoch to the beacon block root\n /// for a given validator index.\n /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n /// @param beaconBlockRoot The root of the beacon block\n /// @param validatorIndex The validator index to verify the withdrawable epoch for.\n /// @param withdrawableEpoch The withdrawable epoch to verify in big endian uint64 format\n /// @param proof The merkle proof for the validator's withdrawable epoch to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyValidatorWithdrawableEpoch(\n bytes32 beaconBlockRoot,\n uint64 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata proof\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.validators[validatorIndex]\n uint256 exitEpochGenIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_LIST_HEIGHT,\n validatorIndex\n );\n // BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n exitEpochGenIndex = concatGenIndices(\n exitEpochGenIndex,\n VALIDATOR_CONTAINER_HEIGHT,\n VALIDATOR_WITHDRAWABLE_EPOCH_INDEX\n );\n\n require(\n // 53 * 32 bytes = 1696 bytes\n proof.length == 1696 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: Endian.toLittleEndianUint64(withdrawableEpoch),\n index: exitEpochGenIndex\n }),\n \"Invalid withdrawable proof\"\n );\n }\n\n /// @param subTreeRoot The third 32 byte witness from the withdrawable epoch proof\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param proof The merkle proof for the validator public key in a sub tree of height two.\n /// This is 2 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyValidatorPubKeySubTree(\n bytes32 subTreeRoot,\n bytes32 pubKeyHash,\n bytes calldata proof\n ) internal view {\n // Tree height 2 and pub key is at index 0\n // index = 2 ^ 2 + 0 = 4\n require(\n // 2 * 32 bytes = 64 bytes\n proof.length == 64 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: subTreeRoot,\n leaf: pubKeyHash,\n index: 4\n }),\n \"Invalid pub key proof\"\n );\n }\n\n /// @notice Verifies the balances container to the beacon block root.\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param balancesContainerRoot The merkle root of the the balances container.\n /// @param proof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata proof\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.balances\n require(\n // 9 * 32 bytes = 288 bytes\n proof.length == 288 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: balancesContainerRoot,\n index: BALANCES_CONTAINER_GENERALIZED_INDEX\n }),\n \"Invalid balance container proof\"\n );\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param proof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for.\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index.\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata proof,\n uint64 validatorIndex\n ) internal view returns (uint256 validatorBalanceGwei) {\n require(balancesContainerRoot != bytes32(0), \"Invalid container root\");\n\n // Four balances are stored in each leaf so the validator index is divided by 4\n uint64 balanceIndex = validatorIndex / 4;\n\n // Get the index within the balances container, not the Beacon Block\n // BeaconBlock.state.balances[balanceIndex]\n uint256 generalizedIndex = concatGenIndices(\n 1,\n BALANCES_HEIGHT,\n balanceIndex\n );\n\n validatorBalanceGwei = balanceAtIndex(\n validatorBalanceLeaf,\n validatorIndex\n );\n\n require(\n // 39 * 32 bytes = 1248 bytes\n proof.length == 1248 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: balancesContainerRoot,\n leaf: validatorBalanceLeaf,\n index: generalizedIndex\n }),\n \"Invalid balance proof\"\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the pubKey and slot of the first pending deposit to the beacon block root.\n /// BeaconBlock.state.pendingDeposits[0].pubKey\n /// BeaconBlock.state.pendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero would be a good choice.\n /// @param pubKeyHash The hash of the validator public key for the first pending deposit.\n /// Use zero bytes if the deposit queue is empty.\n /// @param proof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].pubKey when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes32 pubKeyHash,\n bytes calldata proof\n ) internal view returns (bool isEmptyDepositQueue) {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // If the deposit queue is empty\n if (proof.length == FIRST_PENDING_DEPOSIT_PROOF_LENGTH) {\n require(\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: bytes32(0),\n index: FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX\n }),\n \"Invalid empty deposits proof\"\n );\n return true;\n }\n\n // Verify the public key of the first pending deposit\n // BeaconBlock.state.PendingDeposits[0].pubKey\n require(\n proof.length == FIRST_PENDING_DEPOSIT_PUBKEY_PROOF_LENGTH &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: pubKeyHash,\n index: FIRST_PENDING_DEPOSIT_PUBKEY_GENERALIZED_INDEX\n }),\n \"Invalid deposit pub key proof\"\n );\n\n // Now verify the slot of the first pending deposit\n\n // Get the third 32 bytes witness from the first pending deposit pubKey proof\n // 2 * 32 bytes = 64 bytes offset\n bytes32 slotRoot = bytes32(proof[64:96]);\n\n // Sub tree height 2 and slot is at index 0 in the sub tree\n // index = 2 ^ 2 + 0 = 4\n require(\n Merkle.verifyInclusionSha256({\n proof: PENDING_DEPOSIT_SLOT_PROOF,\n root: slotRoot,\n leaf: Endian.toLittleEndianUint64(slot),\n index: 4\n }),\n \"Invalid deposit slot\"\n );\n }\n\n ////////////////////////////////////////////////////\n /// Internal Helper Functions\n ////////////////////////////////////////////////////\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n internal\n pure\n returns (uint256)\n {\n uint256 bitShiftAmount = (validatorIndex % 4) * 64;\n return\n Endian.fromLittleEndianUint64(\n bytes32((uint256(validatorBalanceLeaf) << bitShiftAmount))\n );\n }\n\n /// @notice Concatenates two beacon chain generalized indices into one.\n /// @param genIndex The first generalized index or 1 if calculating for a single container.\n /// @param height The merkle tree height of the second container. eg 39 for balances, 41 for validators.\n /// @param index The index within the second container. eg the validator index.\n /// @return genIndex The concatenated generalized index.\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) internal pure returns (uint256) {\n return (genIndex << height) | index;\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to retrieve beacon block roots.\n * @author Origin Protocol Inc\n */\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the beacon block root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Roots contract to get the parent block root.\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/Endian.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n/**\n * @title Library to handle conversion between little-endian and big-endian formats.\n * @author Origin Protocol Inc\n */\nlibrary Endian {\n /**\n * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64\n * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type\n * @return n The big endian-formatted uint64\n * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits),\n * but it is immediately truncated to a uint64 (i.e. 64 bits)\n * through a right-shift/shr operation.\n */\n function fromLittleEndianUint64(bytes32 lenum)\n internal\n pure\n returns (uint64 n)\n {\n // the number needs to be stored in little-endian encoding (ie in bytes 0-8)\n n = uint64(uint256(lenum >> 192));\n // forgefmt: disable-next-item\n return\n (n >> 56) |\n ((0x00FF000000000000 & n) >> 40) |\n ((0x0000FF0000000000 & n) >> 24) |\n ((0x000000FF00000000 & n) >> 8) |\n ((0x00000000FF000000 & n) << 8) |\n ((0x0000000000FF0000 & n) << 24) |\n ((0x000000000000FF00 & n) << 40) |\n ((0x00000000000000FF & n) << 56);\n }\n\n function toLittleEndianUint64(uint64 benum)\n internal\n pure\n returns (bytes32 n)\n {\n // Convert to little-endian by reversing byte order\n uint64 reversed = (benum >> 56) |\n ((0x00FF000000000000 & benum) >> 40) |\n ((0x0000FF0000000000 & benum) >> 24) |\n ((0x000000FF00000000 & benum) >> 8) |\n ((0x00000000FF000000 & benum) << 8) |\n ((0x0000000000FF0000 & benum) << 24) |\n ((0x000000000000FF00 & benum) << 40) |\n ((0x00000000000000FF & benum) << 56);\n\n // Store the little-endian uint64 in the least significant 64 bits of bytes32\n n = bytes32(uint256(reversed));\n // Shift to most significant bits\n n = n << 192;\n }\n}\n" + }, + "contracts/beacon/Merkle.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev These functions deal with verification of Merkle Tree proofs.\n *\n * The tree and the proofs can be generated using our\n * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].\n * You will find a quickstart guide in the readme.\n *\n * WARNING: You should avoid using leaf values that are 64 bytes long prior to\n * hashing, or use a hash function other than keccak256 for hashing leaves.\n * This is because the concatenation of a sorted pair of internal nodes in\n * the merkle tree could be reinterpreted as a leaf value.\n * OpenZeppelin's JavaScript library generates merkle trees that are safe\n * against this attack out of the box.\n */\nlibrary Merkle {\n error InvalidProofLength();\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function verifyInclusionSha256(\n bytes memory proof,\n bytes32 root,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bool) {\n return processInclusionProofSha256(proof, leaf, index) == root;\n }\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * _Available since v4.4._\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function processInclusionProofSha256(\n bytes memory proof,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bytes32) {\n require(\n proof.length != 0 && proof.length % 32 == 0,\n InvalidProofLength()\n );\n bytes32[1] memory computedHash = [leaf];\n for (uint256 i = 32; i <= proof.length; i += 32) {\n if (index % 2 == 0) {\n // if ith bit of index is 0, then computedHash is a left sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(computedHash))\n mstore(0x20, mload(add(proof, i)))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n }\n } else {\n // if ith bit of index is 1, then computedHash is a right sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(add(proof, i)))\n mstore(0x20, mload(computedHash))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n }\n }\n index = index / 2;\n }\n return computedHash[0];\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to request full or partial withdrawals from validators on the beacon chain.\n * @author Origin Protocol Inc\n */\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof,\n bytes calldata validatorPubKeyProof\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint64 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes32 pubKeyHash,\n bytes calldata firstPendingDepositProof\n ) external view returns (bool isEmptyDepositQueue);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/mocks/MockBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"../beacon/BeaconProofsLib.sol\";\nimport { BeaconProofs } from \"../beacon/BeaconProofs.sol\";\n\ncontract MockBeaconProofs is BeaconProofs {\n function concatGenIndices(\n uint256 index1,\n uint256 height2,\n uint256 index2\n ) external pure returns (uint256 genIndex) {\n return BeaconProofsLib.concatGenIndices(index1, height2, index2);\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with\n /// `platformAddress` not used so empty address\n /// `vaultAddress` the address of the OETH Vault contract\n /// @param _wethAddress Address of the WETH Token contract\n /// @param _ssvToken Address of the SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconProofs,\n _beaconGenesisTimestamp\n )\n {\n SSV_TOKEN = _ssvToken;\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// @param _rewardTokenAddresses Not used so empty array\n /// @param _assets Not used so empty array\n /// @param _pTokens Not used so empty array\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n safeApproveAllTokens();\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH that was transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// @param _recipient Address to receive withdrawn assets.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH to withdraw.\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(WETH, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Accounts for all the assets managed by this strategy which includes:\n /// 1. The current WETH in this strategy contract\n /// 2. The last verified ETH balance, total deposits and total validator balances\n /// @param _asset Address of WETH asset.\n /// @return balance Total value in ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by the strategy.\n /// @param _asset The address of the WETH token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for validator registration.\n function safeApproveAllTokens() public override {\n // Approves the SSV Network contract to transfer SSV tokens when validators are registered\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n * The new ETH will be accounted for in `checkBalance` after the next snapBalances and verifyBalances txs.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n /// @notice is not supported for this strategy as there is no platform token.\n function setPTokenAddress(address, address) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @notice is not supported for this strategy as there is no platform token.\n function removePToken(uint256) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @dev This strategy does not use a platform token like the old Aave and Compound strategies.\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instead of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract.\n /// Withdrawals from validators also accumulate as ETH in this strategy contract.\n /// It's too complex to separate the rewards from withdrawals so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can now regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingView.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/**\n * @title Viewing contract for the Compounding Staking Strategy.\n * @author Origin Protocol Inc\n */\ncontract CompoundingStakingStrategyView {\n /// @notice The address of the Compounding Staking Strategy contract\n CompoundingValidatorManager public immutable stakingStrategy;\n\n constructor(address _stakingStrategy) {\n stakingStrategy = CompoundingValidatorManager(_stakingStrategy);\n }\n\n struct ValidatorView {\n bytes32 pubKeyHash;\n uint64 index;\n CompoundingValidatorManager.ValidatorState state;\n }\n\n struct DepositView {\n uint256 depositID;\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint256 withdrawableEpoch;\n }\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return validators An array of `ValidatorView` containing the public key hash, validator index and state.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorView[] memory validators)\n {\n uint256 validatorCount = stakingStrategy.verifiedValidatorsLength();\n validators = new ValidatorView[](validatorCount);\n for (uint256 i = 0; i < validatorCount; ++i) {\n bytes32 pubKeyHash = stakingStrategy.verifiedValidators(i);\n (\n CompoundingValidatorManager.ValidatorState state,\n uint64 index\n ) = stakingStrategy.validator(pubKeyHash);\n validators[i] = ValidatorView({\n pubKeyHash: pubKeyHash,\n index: index,\n state: state\n });\n }\n }\n\n /// @notice Returns the deposits that are still to be verified.\n /// These may or may not have been processed by the beacon chain.\n /// @return pendingDeposits An array of `DepositView` containing the deposit ID, public key hash,\n /// amount in Gwei and the slot of the deposit.\n function getPendingDeposits()\n external\n view\n returns (DepositView[] memory pendingDeposits)\n {\n uint256 depositsCount = stakingStrategy.depositListLength();\n pendingDeposits = new DepositView[](depositsCount);\n for (uint256 i = 0; i < depositsCount; ++i) {\n (\n bytes32 pubKeyHash,\n uint64 amountGwei,\n uint64 slot,\n ,\n ,\n uint256 withdrawableEpoch\n ) = stakingStrategy.deposits(stakingStrategy.depositList(i));\n pendingDeposits[i] = DepositView({\n depositID: stakingStrategy.depositList(i),\n pubKeyHash: pubKeyHash,\n amountGwei: amountGwei,\n slot: slot,\n withdrawableEpoch: withdrawableEpoch\n });\n }\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable {\n using SafeERC20 for IERC20;\n\n /// @dev The amount of ETH in wei that is required for a deposit to a new validator.\n /// Initially this is 32 ETH, but will be reduced to 1 ETH after P2P's APIs have been updated\n /// to support deposits of 1 ETH.\n uint256 internal constant DEPOSIT_AMOUNT_WEI = 32 ether;\n /// @dev The maximum number of deposits that are waiting to be verified as processed on the beacon chain.\n uint256 internal constant MAX_DEPOSITS = 12;\n /// @dev The maximum number of validators that can be verified.\n uint256 internal constant MAX_VERIFIED_VALIDATORS = 48;\n /// @dev The default withdrawable epoch value on the Beacon chain.\n /// A value in the far future means the validator is not exiting.\n uint64 internal constant FAR_FUTURE_EPOCH = type(uint64).max;\n /// @dev The number of seconds between each beacon chain slot.\n uint64 internal constant SLOT_DURATION = 12;\n /// @dev The number of slots in each beacon chain epoch.\n uint64 internal constant SLOTS_PER_EPOCH = 32;\n /// @dev Minimum time in seconds to allow snapped balances to be verified.\n uint64 internal constant SNAP_BALANCES_DELAY =\n 2 * SLOTS_PER_EPOCH * SLOT_DURATION;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n /// @notice The timestamp of the Beacon chain genesis.\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\n uint64 public immutable BEACON_GENESIS_TIMESTAMP;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// Deposit data for new compounding validators.\n\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified and is ready to be staked\n }\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountWei Amount of ETH in wei that has been deposited to the beacon chain deposit contract\n /// @param blockNumber Block number when the deposit was made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either PENDING or PROVEN\n\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint32 depositIndex;\n DepositStatus status;\n uint64 withdrawableEpoch;\n }\n /// @notice Restricts to only one deposit to an unverified validator at a time.\n /// This is to limit front-running attacks of deposits to the beacon chain contract.\n\n bool public firstDeposit;\n /// @notice Unique identifier of the next validator deposit.\n uint128 public nextDepositID;\n /// @notice Mapping of the deposit ID to the deposit data\n mapping(uint256 => DepositData) public deposits;\n /// @notice List of strategy deposit IDs to a validator.\n /// The list can be for deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n /// The list may not be ordered by time of deposit.\n /// Removed deposits will move the last deposit to the removed index.\n uint256[] public depositList;\n\n // Validator data\n\n enum ValidatorState {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITING, // The validator has been requested to exit or has been verified as forced exit\n EXITED, // The validator has been verified to have a zero balance\n REMOVED, // validator has funds withdrawn to the EigenPod and is removed from the SSV\n INVALID // The validator has been front-run and the withdrawal address is not this strategy\n }\n\n struct ValidatorData {\n ValidatorState state;\n uint64 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n\n bytes32[] public verifiedValidators;\n /// @notice Mapping of the hash of the validator's public key to the validator state and index.\n /// Uses the Beacon chain hashing for BLSPubkey which is sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => ValidatorData) public validator;\n\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n uint64 timestamp;\n uint128 ethBalance;\n }\n /// @notice Mapping of the block root to the balances at that slot\n\n mapping(bytes32 => Balances) public snappedBalances;\n uint64 public lastSnapTimestamp;\n uint128 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[50] private __gap;\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event FirstDepositReset();\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n uint256 indexed depositID,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint64 indexed validatorIndex\n );\n event ValidatorInvalid(bytes32 indexed pubKeyHash);\n event DepositVerified(uint256 indexed depositID, uint256 amountWei);\n event DepositToValidatorExiting(\n uint256 indexed depositID,\n uint256 amountWei,\n uint64 withdrawableEpoch\n );\n event DepositValidatorExited(uint256 indexed depositID, uint256 amountWei);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(\n uint256 indexed timestamp,\n bytes32 indexed blockRoot,\n uint256 ethBalance\n );\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 ethBalance\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_PROOFS = _beaconProofs;\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\n\n require(\n block.timestamp > _beaconGenesisTimestamp,\n \"Invalid genesis timestamp\"\n );\n }\n\n /**\n *\n * Admin Functions\n *\n */\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Reset the `firstDeposit` flag to false so deposits to unverified validators can be made again.\n function resetFirstDeposit() external onlyGovernor {\n require(firstDeposit, \"No first deposit\");\n\n firstDeposit = false;\n\n emit FirstDepositReset();\n }\n\n /**\n *\n * Validator Management\n *\n */\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validator[pubKeyHash].state == ValidatorState.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validator[pubKeyHash].state = ValidatorState.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n struct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n }\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validatorStakeData validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validatorStakeData,\n uint64 depositAmountGwei\n ) external onlyRegistrator {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(depositList.length < MAX_DEPOSITS, \"Max deposits\");\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validatorStakeData.pubkey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.VERIFIED),\n \"Not registered or verified\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n if (currentState == ValidatorState.REGISTERED) {\n // Can only have one pending deposit to an unverified validator at a time.\n // This is to limit front-running deposit attacks to a single deposit.\n // The exiting deposit needs to be verified before another deposit can be made.\n // If there was a front-running attack, the validator needs to be verified as invalid\n // and the Governor calls `resetFirstDeposit` to set `firstDeposit` to false.\n require(!firstDeposit, \"Existing first deposit\");\n // Limits the amount of ETH that can be at risk from a front-running deposit attack.\n require(\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n // Limits the number of validator balance proofs to verifyBalances\n require(\n verifiedValidators.length + 1 < MAX_VERIFIED_VALIDATORS,\n \"Max validators\"\n );\n\n // Flag a deposit to an unverified validator so only no other deposits can be made\n // to an unverified validator.\n firstDeposit = true;\n }\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validatorStakeData.pubkey,\n withdrawalCredentials,\n validatorStakeData.signature,\n validatorStakeData.depositDataRoot\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == ValidatorState.REGISTERED) {\n validator[pubKeyHash].state = ValidatorState.STAKED;\n }\n\n /// After the Pectra upgrade the validators have a new restriction when proposing\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\n /// forward. Each slot is created at strict 12 second intervals and those slots can\n /// either have blocks attached to them or not. This way using the block.timestamp\n /// the slot number can easily be calculated.\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\n BEACON_GENESIS_TIMESTAMP) / SLOT_DURATION;\n\n // Store the deposit data for verifyDeposit and verifyBalances\n uint256 depositID = nextDepositID++;\n deposits[depositID] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n slot: depositSlot,\n depositIndex: SafeCast.toUint32(depositList.length),\n status: DepositStatus.PENDING,\n withdrawableEpoch: FAR_FUTURE_EPOCH\n });\n depositList.push(depositID);\n\n emit ETHStaked(\n pubKeyHash,\n depositID,\n validatorStakeData.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then only the amount in excess of 32 ETH will be withdrawn.\n /// Only the Registrator can call this function.\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\n /// If no ETH balance, the tx will revert.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n payable\n onlyRegistrator\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n require(\n currentState == ValidatorState.VERIFIED,\n \"Validator not verified\"\n );\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n // If a full withdrawal (validator exit)\n if (amountGwei == 0) {\n // Store the validator state as exiting so no more deposits can be made to it.\n validator[pubKeyHash].state = ValidatorState.EXITING;\n }\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove the validator from the SSV Cluster after:\n /// - the validator has been exited from `validatorWithdrawal` or slashed\n /// - the validator has incorrectly registered and can not be staked to\n /// - the initial deposit was front-run and the withdrawal address is not this strategy's address.\n /// Make sure `validatorWithdrawal` is called with a zero amount and the validator has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.EXITED ||\n currentState == ValidatorState.INVALID,\n \"Validator not regd or exited\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validator[pubKeyHash].state = ValidatorState.REMOVED;\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /**\n *\n * SSV Management\n *\n */\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /**\n *\n * Beacon Chain Proofs\n *\n */\n\n /// @notice Verifies a validator's index to its public key.\n /// Adds to the list of verified validators if the validator's withdrawal address is this strategy's address.\n /// Marks the validator as invalid and removes the deposit if the withdrawal address is not this strategy's address.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param withdrawalAddress The withdrawal address of the validator which should be this strategy's address.\n /// If the withdrawal address is not this strategy's address, the initial deposit was front-run\n /// and the validator is marked as invalid.\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n address withdrawalAddress,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validator[pubKeyHash].state == ValidatorState.STAKED,\n \"Validator not staked\"\n );\n\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key.\n // Also verify the validator's withdrawal credential points to the `withdrawalAddress`.\n IBeaconProofs(BEACON_PROOFS).verifyValidator(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n withdrawalAddress\n );\n\n // If the initial deposit was front-run and the withdrawal address is not this strategy\n if (withdrawalAddress != address(this)) {\n validator[pubKeyHash] = ValidatorData({\n state: ValidatorState.INVALID,\n index: validatorIndex\n });\n\n // Find and remove the deposit as the funds can not be recovered\n uint256 depositCount = depositList.length;\n for (uint256 i = 0; i < depositCount; i++) {\n DepositData memory deposit = deposits[depositList[i]];\n if (deposit.pubKeyHash == pubKeyHash) {\n _removeDeposit(depositList[i], deposit);\n break;\n }\n }\n\n // Leave the `firstDeposit` flag as true so no more deposits to unverified validators can be made.\n // The Governor has to reset the `firstDeposit` to false before another deposit to\n // an unverified validator can be made.\n // The Governor can set a new `validatorRegistrator` if they suspect it has been compromised.\n\n emit ValidatorInvalid(pubKeyHash);\n return;\n }\n\n // Store the validator state as verified\n validator[pubKeyHash] = ValidatorData({\n state: ValidatorState.VERIFIED,\n index: validatorIndex\n });\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(pubKeyHash);\n\n // Reset the firstDeposit flag as the first deposit to an unverified validator has been verified.\n firstDeposit = false;\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n struct FirstPendingDepositProofData {\n uint64 slot;\n uint64 validatorIndex;\n bytes32 pubKeyHash;\n bytes pendingDepositPubKeyProof;\n bytes withdrawableEpochProof;\n bytes validatorPubKeyProof;\n }\n\n struct DepositValidatorProofData {\n uint64 withdrawableEpoch;\n bytes withdrawableEpochProof;\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n ///\n /// Important: this function has a limitation where the `verificationSlot` that is passed by the off-chain\n /// verifier requires a slot immediately after it to propose a block otherwise the `BeaconRoots.parentBlockRoot`\n /// will fail. This shouldn't be a problem, since by the current behaviour of beacon chain only 1%-3% slots\n /// don't propose a block.\n /// @param depositID The deposit ID emitted in `ETHStaked` from the `stakeEth` function.\n /// @param depositProcessedSlot Any slot on or after the strategy's deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\n /// set for the next block timestamp in 12 seconds time.\n /// @param firstDepositValidatorCreatedSlot The slot on or after when the validator of the first pending deposit\n /// was created on the beacon chain. This is used to verify the validator has not exited.\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n uint256 depositID,\n uint64 depositProcessedSlot,\n uint64 firstDepositValidatorCreatedSlot,\n FirstPendingDepositProofData calldata firstPendingDeposit,\n DepositValidatorProofData calldata strategyValidatorData\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositID];\n ValidatorData memory strategyValidator = validator[deposit.pubKeyHash];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n strategyValidator.state == ValidatorState.VERIFIED,\n \"Validator not verified\"\n );\n // The verification slot must be after the deposit's slot.\n // This is needed for when the deposit queue is empty.\n require(deposit.slot < depositProcessedSlot, \"Slot not after deposit\");\n require(\n depositProcessedSlot <= firstDepositValidatorCreatedSlot,\n \"Invalid verification slots\"\n );\n\n // Get the parent beacon block root of the next block which is the block root of the deposit verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 depositBlockRoot = BeaconRoots.parentBlockRoot(\n _calcNextBlockTimestamp(depositProcessedSlot)\n );\n\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n depositBlockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.pubKeyHash,\n firstPendingDeposit.pendingDepositPubKeyProof\n );\n\n // If the deposit queue is not empty\n if (!isDepositQueueEmpty) {\n // Get the parent beacon block root of the next block which is\n // the block root of the validator verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 validatorBlockRoot = BeaconRoots.parentBlockRoot(\n _calcNextBlockTimestamp(firstDepositValidatorCreatedSlot)\n );\n\n // Verify the validator of the first pending deposit is not exiting.\n // If it is exiting we can't be sure this deposit has not been postponed in the deposit queue.\n // Hence we can not verify if the strategy's deposit has been processed or not.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n validatorBlockRoot,\n firstPendingDeposit.validatorIndex,\n firstPendingDeposit.pubKeyHash,\n FAR_FUTURE_EPOCH,\n firstPendingDeposit.withdrawableEpochProof,\n firstPendingDeposit.validatorPubKeyProof\n );\n }\n\n // Verify the withdrawableEpoch on the validator of the strategy's deposit\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n depositBlockRoot,\n strategyValidator.index,\n strategyValidatorData.withdrawableEpoch,\n strategyValidatorData.withdrawableEpochProof\n );\n\n // If the validator is exiting because it has been slashed\n if (strategyValidatorData.withdrawableEpoch != FAR_FUTURE_EPOCH) {\n // Store the exit epoch in the deposit data\n deposit.withdrawableEpoch = strategyValidatorData.withdrawableEpoch;\n\n emit DepositToValidatorExiting(\n depositID,\n uint256(deposit.amountGwei) * 1 gwei,\n strategyValidatorData.withdrawableEpoch\n );\n\n validator[deposit.pubKeyHash].state = ValidatorState.EXITING;\n\n // Leave the deposit status as PENDING\n return;\n }\n\n // solhint-disable max-line-length\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the deposit queue is empty then our deposit must have been processed on the beacon chain.\n // The deposit slot can be zero for validators consolidating to a compounding validator or 0x01 validator\n // being promoted to a compounding one. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // We can not guarantee that the deposit has been processed in that case.\n // solhint-enable max-line-length\n require(\n deposit.slot < firstPendingDeposit.slot || isDepositQueueEmpty,\n \"Deposit likely not processed\"\n );\n\n // Remove the deposit now it has been verified as processed on the beacon chain.\n _removeDeposit(depositID, deposit);\n\n emit DepositVerified(depositID, uint256(deposit.amountGwei) * 1 gwei);\n }\n\n function _removeDeposit(uint256 depositID, DepositData memory deposit)\n internal\n {\n // After verifying the proof, update the contract storage\n deposits[depositID].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n uint256 lastDeposit = depositList[depositList.length - 1];\n depositList[deposit.depositIndex] = lastDeposit;\n deposits[lastDeposit].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositList.pop();\n }\n\n /// @dev Calculates the timestamp of the next execution block from the given slot.\n /// @param slot The beacon chain slot number used for merkle proof verification.\n function _calcNextBlockTimestamp(uint64 slot)\n internal\n view\n returns (uint64)\n {\n // Calculate the next block timestamp from the slot.\n return SLOT_DURATION * slot + BEACON_GENESIS_TIMESTAMP + SLOT_DURATION;\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block and beacon block root\n /// of the slot that is associated with the previous block.\n ///\n /// When snapping / verifying balance it is of a high importance that there is no\n /// miss-match in respect to ETH that is held by the contract and balances that are\n /// verified on the validators.\n ///\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\n /// constructing a block on the beacon chain consist of:\n /// - process_withdrawals: ETH is deducted from the validator's balance\n /// - process_execution_payload: immediately after the previous step executing all the\n /// transactions\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\n /// contained in the withdrawal credentials of the exited validators\n ///\n /// That means that balance increases which are part of the post-block execution state are\n /// done within the block, but the transaction that are contained within that block can not\n /// see / interact with the balance from the exited validators. Only transactions in the\n /// next block can do that.\n ///\n /// When snap balances is performed the state of the chain is snapped across 2 separate\n /// chain states:\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\n /// will be referred to as Y - 1 as it makes no difference in the argument\n ///\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\n /// miss-counting ETH or much more dangerous double counting of the ETH.\n ///\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\n /// - ETH balance in the contract on block X\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\n /// - ETH balance in validators that are active in slot Y - 1\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\n /// and have their balance visible to transactions in slot Y and corresponding block X\n /// (or sooner)\n ///\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\n ///\n /// *ETH balance in the contract on block X*\n ///\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\n ///\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\n /// would result in double counting the `stakedEth` since it would be present once in the\n /// snapped contract balance and the second time in deposit storage variables.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\n ///\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\n /// contract at block Z. The execution layer doesn't have direct access to the state of\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\n /// and could also be part of the validator balances. It does that by verifying that at\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\n /// the last snap till now all are still in queue. Which ensures they can not be part of\n /// the validator balances in later steps.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in validators that are active in slot Y - 1*\n ///\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\n /// correctness.\n ///\n /// This behaviour is correct.\n ///\n /// *The withdrawn validators*\n ///\n /// The withdrawn validators could have their balances deducted in any slot before slot\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\n /// look at the \"worst case scenario\" where the validator withdrawal is processed in the\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\n /// even if the validator exits at the latest possible time it is paramount that the ETH\n /// balance on the execution layer is recorded in the next block. Correctly accounting\n /// for the withdrawn ETH.\n ///\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\n /// validator balance verification as well as execution layer contract balance snap.\n ///\n /// This behaviour is correct.\n ///\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n function snapBalances() external {\n uint64 currentTimestamp = SafeCast.toUint64(block.timestamp);\n require(\n lastSnapTimestamp + SNAP_BALANCES_DELAY < currentTimestamp,\n \"Snap too soon\"\n );\n\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(currentTimestamp);\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the balances in the mapping\n snappedBalances[blockRoot] = Balances({\n timestamp: currentTimestamp,\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n // Store the snapped timestamp\n lastSnapTimestamp = currentTimestamp;\n\n emit BalancesSnapped(currentTimestamp, blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct BalanceProofs {\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n /// @param snapBlockRoot The beacon block root emitted from `snapBalance` in `BalancesSnapped`.\n /// @param balanceProofs a `BalanceProofs` struct containing the following:\n /// balancesContainerRoot - the merkle root of the balances container\n /// balancesContainerProof - The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// validatorBalanceLeaves - Array of leaf nodes containing the validator balance with three other balances.\n /// validatorBalanceProofs - Array of merkle proofs for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(\n bytes32 snapBlockRoot,\n uint64 validatorVerificationBlockTimestamp,\n FirstPendingDepositProofData calldata firstPendingDeposit,\n BalanceProofs calldata balanceProofs\n ) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalances[snapBlockRoot];\n // Check the balances are the latest\n require(lastSnapTimestamp > 0, \"No snapped balances\");\n require(balancesMem.timestamp == lastSnapTimestamp, \"Stale snap\");\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n balanceProofs.validatorBalanceProofs.length ==\n verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n balanceProofs.validatorBalanceLeaves.length ==\n verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n snapBlockRoot,\n balanceProofs.balancesContainerRoot,\n balanceProofs.balancesContainerProof\n );\n\n // for each validator in reserve order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n balanceProofs.balancesContainerRoot,\n balanceProofs.validatorBalanceLeaves[i],\n balanceProofs.validatorBalanceProofs[i],\n validator[verifiedValidators[i]].index\n );\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n // This could have been in VERIFIED or EXITING state\n validator[verifiedValidators[i]].state = ValidatorState\n .EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += validatorBalanceGwei * 1 gwei;\n }\n }\n\n uint256 depositsCount = depositList.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification.\n // This section is after the validator balance verifications so an exited validator will be marked\n // as EXITED before the deposits are verified. If there was a deposit to an exited validator\n // then the deposit can only be removed once the validator is fully exited.\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n snapBlockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.pubKeyHash,\n firstPendingDeposit.pendingDepositPubKeyProof\n );\n\n // If there are no deposits in the beacon chain queue then our deposits must have been processed.\n // If the deposits have been processed, each deposit will need to be verified with `verifyDeposit`\n require(!isDepositQueueEmpty, \"Deposits have been processed\");\n\n // The verification of the validator the first pending deposit is for must be on or after when\n // `snapBalances` was called.\n require(\n balancesMem.timestamp <= validatorVerificationBlockTimestamp,\n \"Invalid validator timestamp\"\n );\n\n // Verify the validator of the first pending deposit is not exiting by checking\n // the withdrawable epoch is far into the future.\n // If it is exiting we can't be sure this deposit has not been postponed in the deposit queue.\n // Hence we can not verify if the strategy's deposit has been processed or not.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n // Get the parent beacon block root of the next block which is\n // the block root of the validator verification slot.\n // This will revert if the slot after the verification slot was missed.\n BeaconRoots.parentBlockRoot(\n validatorVerificationBlockTimestamp\n ),\n firstPendingDeposit.validatorIndex,\n firstPendingDeposit.pubKeyHash,\n // Validator is not exiting\n FAR_FUTURE_EPOCH,\n firstPendingDeposit.withdrawableEpochProof,\n firstPendingDeposit.validatorPubKeyProof\n );\n\n // solhint-disable max-line-length\n // If a validator is converted from a sweeping validator to a compounding validator, any balance in excess\n // of the min 32 ETH is put in the pending deposit queue. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // This will have a slot value of zero unfortunately.\n // We can not prove the strategy's deposits are still pending with a zero slot value so revert the tx.\n // Another snapBalances will need to be taken that does not have consolidation deposits at the front of the\n // beacon chain deposit queue.\n // solhint-enable max-line-length\n require(\n firstPendingDeposit.slot > 0,\n \"Invalid first pending deposit\"\n );\n\n // Calculate the epoch at the time of the snapBalances\n uint64 verificationEpoch = (SafeCast.toUint64(\n balancesMem.timestamp\n ) - BEACON_GENESIS_TIMESTAMP) / (SLOT_DURATION * SLOTS_PER_EPOCH);\n\n // For each staking strategy's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n uint256 depositID = depositList[i];\n DepositData memory depositData = deposits[depositID];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit slot is before the slot of the staking strategy's deposit.\n // If the deposit has been processed, it will need to be verified with `verifyDeposit`.\n // OR the deposit is to an exiting validator so check it is still not withdrawable.\n // If the validator is not withdrawable, then the deposit can not have been processed yet.\n // If the validator is now withdrawable, then the deposit may have been processed. The strategy\n // now has to wait until the validator's balance is verified to be zero.\n // OR the validator has exited and the deposit is now verified as processed.\n require(\n firstPendingDeposit.slot < depositData.slot ||\n (verificationEpoch < depositData.withdrawableEpoch &&\n depositData.withdrawableEpoch !=\n FAR_FUTURE_EPOCH) ||\n validator[depositData.pubKeyHash].state ==\n ValidatorState.EXITED,\n \"Deposit likely processed\"\n );\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei += uint256(depositData.amountGwei) * 1 gwei;\n\n // Remove the deposit if the validator has exited.\n if (\n validator[depositData.pubKeyHash].state ==\n ValidatorState.EXITED\n ) {\n _removeDeposit(depositID, depositData);\n\n emit DepositValidatorExited(\n depositID,\n uint256(depositData.amountGwei) * 1 gwei\n );\n }\n }\n }\n\n // Store the verified balance in storage\n lastVerifiedEthBalance = SafeCast.toUint128(\n totalDepositsWei + totalValidatorBalance + balancesMem.ethBalance\n );\n // Reset the last snap timestamp so a new snapBalances has to be made\n lastSnapTimestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n require(pubKey.length == 48, \"Invalid public key length\");\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /**\n *\n * WETH and ETH Accounting\n *\n */\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n // The min is required as more WETH can be withdrawn than deposited\n // as the strategy earns consensus and execution rewards.\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance.\n // The ETH balance in this strategy contract can be more than the last verified ETH balance\n // due to partial withdrawals or full exits being processed by the beacon chain since the last snapBalances.\n // It can also happen from execution rewards (MEV) or ETH donations.\n lastVerifiedEthBalance -= SafeCast.toUint128(\n Math.min(uint256(lastVerifiedEthBalance), _ethAmount)\n );\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += SafeCast.toUint128(_wethAmount);\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /**\n *\n * View Functions\n *\n */\n\n /// @notice Returns the number of deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n function depositListLength() external view returns (uint256) {\n return depositList.length;\n }\n\n /// @notice Returns the number of verified validators.\n function verifiedValidatorsLength() external view returns (uint256) {\n return verifiedValidators.length;\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/63d9212a45af1e87875339eef226f4c8.json b/contracts/deployments/hoodi/solcInputs/63d9212a45af1e87875339eef226f4c8.json new file mode 100644 index 0000000000..ec60f30834 --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/63d9212a45af1e87875339eef226f4c8.json @@ -0,0 +1,102 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to retrieve beacon block roots.\n * @author Origin Protocol Inc\n */\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the beacon block root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Roots contract to get the parent block root.\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to request full or partial withdrawals from validators on the beacon chain.\n * @author Origin Protocol Inc\n */\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n /// @notice Requests a partial withdrawal for a given validator public key and amount.\n /// @param validatorPubKey The public key of the validator to withdraw from\n /// @param amount The amount of ETH to withdraw\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n /// @notice Gets fee for withdrawal requests contract on Beacon chain\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof,\n bytes calldata validatorPubKeyProof\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint64 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes32 pubKeyHash,\n bytes calldata firstPendingDepositProof\n ) external view returns (bool isEmptyDepositQueue);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with\n /// `platformAddress` not used so empty address\n /// `vaultAddress` the address of the OETH Vault contract\n /// @param _wethAddress Address of the WETH Token contract\n /// @param _ssvToken Address of the SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconProofs,\n _beaconGenesisTimestamp\n )\n {\n SSV_TOKEN = _ssvToken;\n\n // Make sure nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// @param _rewardTokenAddresses Not used so empty array\n /// @param _assets Not used so empty array\n /// @param _pTokens Not used so empty array\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n safeApproveAllTokens();\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH that was transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// @param _recipient Address to receive withdrawn assets.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH to withdraw.\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(WETH, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Accounts for all the assets managed by this strategy which includes:\n /// 1. The current WETH in this strategy contract\n /// 2. The last verified ETH balance, total deposits and total validator balances\n /// @param _asset Address of WETH asset.\n /// @return balance Total value in ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by the strategy.\n /// @param _asset The address of the WETH token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for validator registration.\n function safeApproveAllTokens() public override {\n // Approves the SSV Network contract to transfer SSV tokens when validators are registered\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n * The new ETH will be accounted for in `checkBalance` after the next snapBalances and verifyBalances txs.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n /// @notice is not supported for this strategy as there is no platform token.\n function setPTokenAddress(address, address) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @notice is not supported for this strategy as there is no platform token.\n function removePToken(uint256) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @dev This strategy does not use a platform token like the old Aave and Compound strategies.\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instead of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract.\n /// Withdrawals from validators also accumulate as ETH in this strategy contract.\n /// It's too complex to separate the rewards from withdrawals so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can now regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingView.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/**\n * @title Viewing contract for the Compounding Staking Strategy.\n * @author Origin Protocol Inc\n */\ncontract CompoundingStakingStrategyView {\n /// @notice The address of the Compounding Staking Strategy contract\n CompoundingValidatorManager public immutable stakingStrategy;\n\n constructor(address _stakingStrategy) {\n stakingStrategy = CompoundingValidatorManager(_stakingStrategy);\n }\n\n struct ValidatorView {\n bytes32 pubKeyHash;\n uint64 index;\n CompoundingValidatorManager.ValidatorState state;\n }\n\n struct DepositView {\n uint256 depositID;\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint256 withdrawableEpoch;\n }\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return validators An array of `ValidatorView` containing the public key hash, validator index and state.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorView[] memory validators)\n {\n uint256 validatorCount = stakingStrategy.verifiedValidatorsLength();\n validators = new ValidatorView[](validatorCount);\n for (uint256 i = 0; i < validatorCount; ++i) {\n bytes32 pubKeyHash = stakingStrategy.verifiedValidators(i);\n (\n CompoundingValidatorManager.ValidatorState state,\n uint64 index\n ) = stakingStrategy.validator(pubKeyHash);\n validators[i] = ValidatorView({\n pubKeyHash: pubKeyHash,\n index: index,\n state: state\n });\n }\n }\n\n /// @notice Returns the deposits that are still to be verified.\n /// These may or may not have been processed by the beacon chain.\n /// @return pendingDeposits An array of `DepositView` containing the deposit ID, public key hash,\n /// amount in Gwei and the slot of the deposit.\n function getPendingDeposits()\n external\n view\n returns (DepositView[] memory pendingDeposits)\n {\n uint256 depositsCount = stakingStrategy.depositListLength();\n pendingDeposits = new DepositView[](depositsCount);\n for (uint256 i = 0; i < depositsCount; ++i) {\n (\n bytes32 pubKeyHash,\n uint64 amountGwei,\n uint64 slot,\n ,\n ,\n uint256 withdrawableEpoch\n ) = stakingStrategy.deposits(stakingStrategy.depositList(i));\n pendingDeposits[i] = DepositView({\n depositID: stakingStrategy.depositList(i),\n pubKeyHash: pubKeyHash,\n amountGwei: amountGwei,\n slot: slot,\n withdrawableEpoch: withdrawableEpoch\n });\n }\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable {\n using SafeERC20 for IERC20;\n\n /// @dev The amount of ETH in wei that is required for a deposit to a new validator.\n /// Initially this is 32 ETH, but will be reduced to 1 ETH after P2P's APIs have been updated\n /// to support deposits of 1 ETH.\n uint256 internal constant DEPOSIT_AMOUNT_WEI = 32 ether;\n /// @dev The maximum number of deposits that are waiting to be verified as processed on the beacon chain.\n uint256 internal constant MAX_DEPOSITS = 12;\n /// @dev The maximum number of validators that can be verified.\n uint256 internal constant MAX_VERIFIED_VALIDATORS = 48;\n /// @dev The default withdrawable epoch value on the Beacon chain.\n /// A value in the far future means the validator is not exiting.\n uint64 internal constant FAR_FUTURE_EPOCH = type(uint64).max;\n /// @dev The number of seconds between each beacon chain slot.\n uint64 internal constant SLOT_DURATION = 12;\n /// @dev The number of slots in each beacon chain epoch.\n uint64 internal constant SLOTS_PER_EPOCH = 32;\n /// @dev Minimum time in seconds to allow snapped balances to be verified.\n /// Set to 1 epoch as the pending deposits only changes every epoch.\n /// That's also enough time to generate the proofs and call `verifyBalances`.\n uint64 internal constant SNAP_BALANCES_DELAY =\n SLOTS_PER_EPOCH * SLOT_DURATION;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n /// @notice The timestamp of the Beacon chain genesis.\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\n uint64 public immutable BEACON_GENESIS_TIMESTAMP;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// Deposit data for new compounding validators.\n\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified and is ready to be staked\n }\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountWei Amount of ETH in wei that has been deposited to the beacon chain deposit contract\n /// @param blockNumber Block number when the deposit was made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either PENDING or PROVEN\n\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint32 depositIndex;\n DepositStatus status;\n uint64 withdrawableEpoch;\n }\n /// @notice Mapping of the deposit ID to the deposit data\n mapping(uint256 => DepositData) public deposits;\n /// @notice List of strategy deposit IDs to a validator.\n /// The list can be for deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n /// The list may not be ordered by time of deposit.\n /// Removed deposits will move the last deposit to the removed index.\n uint256[] public depositList;\n\n // Validator data\n\n enum ValidatorState {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITING, // The validator has been requested to exit or has been verified as forced exit\n EXITED, // The validator has been verified to have a zero balance\n REMOVED, // validator has funds withdrawn to the EigenPod and is removed from the SSV\n INVALID // The validator has been front-run and the withdrawal address is not this strategy\n }\n\n struct ValidatorData {\n ValidatorState state;\n uint64 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n\n bytes32[] public verifiedValidators;\n /// @notice Mapping of the hash of the validator's public key to the validator state and index.\n /// Uses the Beacon chain hashing for BLSPubkey which is sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => ValidatorData) public validator;\n\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n uint64 timestamp;\n uint128 ethBalance;\n }\n /// @notice Mapping of the block root to the balances at that slot\n\n mapping(bytes32 => Balances) public snappedBalances;\n /// @notice The timestamp of the last snapshot taken\n uint64 public lastSnapTimestamp;\n /// @notice The last verified ETH balance of the strategy\n uint128 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n /// The following deposit storage variables are down here for backwards compatibility on Hoodi.\n\n /// @notice Restricts to only one deposit to an unverified validator at a time.\n /// This is to limit front-running attacks of deposits to the beacon chain contract.\n bool public firstDeposit;\n /// @notice Unique identifier of the next validator deposit.\n uint128 public nextDepositID;\n\n // For future use\n uint256[50] private __gap;\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event FirstDepositReset();\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n uint256 indexed depositID,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint64 indexed validatorIndex\n );\n event ValidatorInvalid(bytes32 indexed pubKeyHash);\n event DepositVerified(uint256 indexed depositID, uint256 amountWei);\n event DepositToValidatorExiting(\n uint256 indexed depositID,\n uint256 amountWei,\n uint64 withdrawableEpoch\n );\n event DepositValidatorExited(uint256 indexed depositID, uint256 amountWei);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(bytes32 indexed blockRoot, uint256 ethBalance);\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 ethBalance\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_PROOFS = _beaconProofs;\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\n\n require(\n block.timestamp > _beaconGenesisTimestamp,\n \"Invalid genesis timestamp\"\n );\n }\n\n /**\n *\n * Admin Functions\n *\n */\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Reset the `firstDeposit` flag to false so deposits to unverified validators can be made again.\n function resetFirstDeposit() external onlyGovernor {\n require(firstDeposit, \"No first deposit\");\n\n firstDeposit = false;\n\n emit FirstDepositReset();\n }\n\n /**\n *\n * Validator Management\n *\n */\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validator[pubKeyHash].state == ValidatorState.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validator[pubKeyHash].state = ValidatorState.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n struct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n }\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validatorStakeData validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validatorStakeData,\n uint64 depositAmountGwei\n ) external onlyRegistrator {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(depositList.length < MAX_DEPOSITS, \"Max deposits\");\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validatorStakeData.pubkey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.VERIFIED),\n \"Not registered or verified\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n if (currentState == ValidatorState.REGISTERED) {\n // Can only have one pending deposit to an unverified validator at a time.\n // This is to limit front-running deposit attacks to a single deposit.\n // The exiting deposit needs to be verified before another deposit can be made.\n // If there was a front-running attack, the validator needs to be verified as invalid\n // and the Governor calls `resetFirstDeposit` to set `firstDeposit` to false.\n require(!firstDeposit, \"Existing first deposit\");\n // Limits the amount of ETH that can be at risk from a front-running deposit attack.\n require(\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n // Limits the number of validator balance proofs to verifyBalances\n require(\n verifiedValidators.length + 1 < MAX_VERIFIED_VALIDATORS,\n \"Max validators\"\n );\n\n // Flag a deposit to an unverified validator so only no other deposits can be made\n // to an unverified validator.\n firstDeposit = true;\n }\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validatorStakeData.pubkey,\n withdrawalCredentials,\n validatorStakeData.signature,\n validatorStakeData.depositDataRoot\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == ValidatorState.REGISTERED) {\n validator[pubKeyHash].state = ValidatorState.STAKED;\n }\n\n /// After the Pectra upgrade the validators have a new restriction when proposing\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\n /// forward. Each slot is created at strict 12 second intervals and those slots can\n /// either have blocks attached to them or not. This way using the block.timestamp\n /// the slot number can easily be calculated.\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\n BEACON_GENESIS_TIMESTAMP) / SLOT_DURATION;\n\n // Store the deposit data for verifyDeposit and verifyBalances\n uint256 depositID = nextDepositID++;\n deposits[depositID] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n slot: depositSlot,\n depositIndex: SafeCast.toUint32(depositList.length),\n status: DepositStatus.PENDING,\n withdrawableEpoch: FAR_FUTURE_EPOCH\n });\n depositList.push(depositID);\n\n emit ETHStaked(\n pubKeyHash,\n depositID,\n validatorStakeData.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then only the amount in excess of 32 ETH will be withdrawn.\n /// Only the Registrator can call this function.\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\n /// If no ETH balance, the tx will revert.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n payable\n onlyRegistrator\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n require(\n currentState == ValidatorState.VERIFIED,\n \"Validator not verified\"\n );\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n // If a full withdrawal (validator exit)\n if (amountGwei == 0) {\n // Store the validator state as exiting so no more deposits can be made to it.\n validator[pubKeyHash].state = ValidatorState.EXITING;\n }\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove the validator from the SSV Cluster after:\n /// - the validator has been exited from `validatorWithdrawal` or slashed\n /// - the validator has incorrectly registered and can not be staked to\n /// - the initial deposit was front-run and the withdrawal address is not this strategy's address.\n /// Make sure `validatorWithdrawal` is called with a zero amount and the validator has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.EXITED ||\n currentState == ValidatorState.INVALID,\n \"Validator not regd or exited\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validator[pubKeyHash].state = ValidatorState.REMOVED;\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /**\n *\n * SSV Management\n *\n */\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be withdrawn from the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /**\n *\n * Beacon Chain Proofs\n *\n */\n\n /// @notice Verifies a validator's index to its public key.\n /// Adds to the list of verified validators if the validator's withdrawal address is this strategy's address.\n /// Marks the validator as invalid and removes the deposit if the withdrawal address is not this strategy's address.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param withdrawalAddress The withdrawal address of the validator which should be this strategy's address.\n /// If the withdrawal address is not this strategy's address, the initial deposit was front-run\n /// and the validator is marked as invalid.\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n address withdrawalAddress,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validator[pubKeyHash].state == ValidatorState.STAKED,\n \"Validator not staked\"\n );\n\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key.\n // Also verify the validator's withdrawal credential points to the `withdrawalAddress`.\n IBeaconProofs(BEACON_PROOFS).verifyValidator(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n withdrawalAddress\n );\n\n // If the initial deposit was front-run and the withdrawal address is not this strategy\n if (withdrawalAddress != address(this)) {\n validator[pubKeyHash] = ValidatorData({\n state: ValidatorState.INVALID,\n index: validatorIndex\n });\n\n // Find and remove the deposit as the funds can not be recovered\n uint256 depositCount = depositList.length;\n for (uint256 i = 0; i < depositCount; i++) {\n DepositData memory deposit = deposits[depositList[i]];\n if (deposit.pubKeyHash == pubKeyHash) {\n _removeDeposit(depositList[i], deposit);\n break;\n }\n }\n\n // Leave the `firstDeposit` flag as true so no more deposits to unverified validators can be made.\n // The Governor has to reset the `firstDeposit` to false before another deposit to\n // an unverified validator can be made.\n // The Governor can set a new `validatorRegistrator` if they suspect it has been compromised.\n\n emit ValidatorInvalid(pubKeyHash);\n return;\n }\n\n // Store the validator state as verified\n validator[pubKeyHash] = ValidatorData({\n state: ValidatorState.VERIFIED,\n index: validatorIndex\n });\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(pubKeyHash);\n\n // Reset the firstDeposit flag as the first deposit to an unverified validator has been verified.\n firstDeposit = false;\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n struct FirstPendingDepositProofData {\n uint64 slot;\n uint64 validatorIndex;\n bytes32 pubKeyHash;\n bytes pendingDepositPubKeyProof;\n bytes withdrawableEpochProof;\n bytes validatorPubKeyProof;\n }\n\n struct DepositValidatorProofData {\n uint64 withdrawableEpoch;\n bytes withdrawableEpochProof;\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n ///\n /// Important: this function has a limitation where the `verificationSlot` that is passed by the off-chain\n /// verifier requires a slot immediately after it to propose a block otherwise the `BeaconRoots.parentBlockRoot`\n /// will fail. This shouldn't be a problem, since by the current behaviour of beacon chain only 1%-3% slots\n /// don't propose a block.\n /// @param depositID The deposit ID emitted in `ETHStaked` from the `stakeEth` function.\n /// @param depositProcessedSlot Any slot on or after the strategy's deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\n /// set for the next block timestamp in 12 seconds time.\n /// @param firstDepositValidatorCreatedSlot The slot on or after when the validator of the first pending deposit\n /// was created on the beacon chain. This is used to verify the validator has not exited.\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n uint256 depositID,\n uint64 depositProcessedSlot,\n uint64 firstDepositValidatorCreatedSlot,\n FirstPendingDepositProofData calldata firstPendingDeposit,\n DepositValidatorProofData calldata strategyValidatorData\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositID];\n ValidatorData memory strategyValidator = validator[deposit.pubKeyHash];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n strategyValidator.state == ValidatorState.VERIFIED,\n \"Validator not verified\"\n );\n // The verification slot must be after the deposit's slot.\n // This is needed for when the deposit queue is empty.\n require(deposit.slot < depositProcessedSlot, \"Slot not after deposit\");\n require(\n depositProcessedSlot <= firstDepositValidatorCreatedSlot,\n \"Invalid verification slots\"\n );\n\n // Get the parent beacon block root of the next block which is the block root of the deposit verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 depositBlockRoot = BeaconRoots.parentBlockRoot(\n _calcNextBlockTimestamp(depositProcessedSlot)\n );\n\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n depositBlockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.pubKeyHash,\n firstPendingDeposit.pendingDepositPubKeyProof\n );\n\n // If the deposit queue is not empty\n if (!isDepositQueueEmpty) {\n // Get the parent beacon block root of the next block which is\n // the block root of the validator verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 validatorBlockRoot = BeaconRoots.parentBlockRoot(\n _calcNextBlockTimestamp(firstDepositValidatorCreatedSlot)\n );\n\n // Verify the validator of the first pending deposit is not exiting.\n // If it is exiting we can't be sure this deposit has not been postponed in the deposit queue.\n // Hence we can not verify if the strategy's deposit has been processed or not.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n validatorBlockRoot,\n firstPendingDeposit.validatorIndex,\n firstPendingDeposit.pubKeyHash,\n FAR_FUTURE_EPOCH,\n firstPendingDeposit.withdrawableEpochProof,\n firstPendingDeposit.validatorPubKeyProof\n );\n }\n\n // Verify the withdrawableEpoch on the validator of the strategy's deposit\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n depositBlockRoot,\n strategyValidator.index,\n strategyValidatorData.withdrawableEpoch,\n strategyValidatorData.withdrawableEpochProof\n );\n\n // If the validator is exiting because it has been slashed\n if (strategyValidatorData.withdrawableEpoch != FAR_FUTURE_EPOCH) {\n // Store the exit epoch in the deposit data\n deposit.withdrawableEpoch = strategyValidatorData.withdrawableEpoch;\n\n emit DepositToValidatorExiting(\n depositID,\n uint256(deposit.amountGwei) * 1 gwei,\n strategyValidatorData.withdrawableEpoch\n );\n\n validator[deposit.pubKeyHash].state = ValidatorState.EXITING;\n\n // Leave the deposit status as PENDING\n return;\n }\n\n // solhint-disable max-line-length\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the deposit queue is empty then our deposit must have been processed on the beacon chain.\n // The deposit slot can be zero for validators consolidating to a compounding validator or 0x01 validator\n // being promoted to a compounding one. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // We can not guarantee that the deposit has been processed in that case.\n // solhint-enable max-line-length\n require(\n deposit.slot < firstPendingDeposit.slot || isDepositQueueEmpty,\n \"Deposit likely not processed\"\n );\n\n // Remove the deposit now it has been verified as processed on the beacon chain.\n _removeDeposit(depositID, deposit);\n\n emit DepositVerified(depositID, uint256(deposit.amountGwei) * 1 gwei);\n }\n\n function _removeDeposit(uint256 depositID, DepositData memory deposit)\n internal\n {\n // After verifying the proof, update the contract storage\n deposits[depositID].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n uint256 lastDeposit = depositList[depositList.length - 1];\n depositList[deposit.depositIndex] = lastDeposit;\n deposits[lastDeposit].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositList.pop();\n }\n\n /// @dev Calculates the timestamp of the next execution block from the given slot.\n /// @param slot The beacon chain slot number used for merkle proof verification.\n function _calcNextBlockTimestamp(uint64 slot)\n internal\n view\n returns (uint64)\n {\n // Calculate the next block timestamp from the slot.\n return SLOT_DURATION * slot + BEACON_GENESIS_TIMESTAMP + SLOT_DURATION;\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block and beacon block root\n /// of the slot that is associated with the previous block.\n ///\n /// When snapping / verifying balance it is of a high importance that there is no\n /// miss-match in respect to ETH that is held by the contract and balances that are\n /// verified on the validators.\n ///\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\n /// constructing a block on the beacon chain consist of:\n /// - process_withdrawals: ETH is deducted from the validator's balance\n /// - process_execution_payload: immediately after the previous step executing all the\n /// transactions\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\n /// contained in the withdrawal credentials of the exited validators\n ///\n /// That means that balance increases which are part of the post-block execution state are\n /// done within the block, but the transaction that are contained within that block can not\n /// see / interact with the balance from the exited validators. Only transactions in the\n /// next block can do that.\n ///\n /// When snap balances is performed the state of the chain is snapped across 2 separate\n /// chain states:\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\n /// will be referred to as Y - 1 as it makes no difference in the argument\n ///\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\n /// miss-counting ETH or much more dangerous double counting of the ETH.\n ///\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\n /// - ETH balance in the contract on block X\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\n /// - ETH balance in validators that are active in slot Y - 1\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\n /// and have their balance visible to transactions in slot Y and corresponding block X\n /// (or sooner)\n ///\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\n ///\n /// *ETH balance in the contract on block X*\n ///\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\n ///\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\n /// would result in double counting the `stakedEth` since it would be present once in the\n /// snapped contract balance and the second time in deposit storage variables.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\n ///\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\n /// contract at block Z. The execution layer doesn't have direct access to the state of\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\n /// and could also be part of the validator balances. It does that by verifying that at\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\n /// the last snap till now all are still in queue. Which ensures they can not be part of\n /// the validator balances in later steps.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in validators that are active in slot Y - 1*\n ///\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\n /// correctness.\n ///\n /// This behaviour is correct.\n ///\n /// *The withdrawn validators*\n ///\n /// The withdrawn validators could have their balances deducted in any slot before slot\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\n /// look at the \"worst case scenario\" where the validator withdrawal is processed in the\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\n /// even if the validator exits at the latest possible time it is paramount that the ETH\n /// balance on the execution layer is recorded in the next block. Correctly accounting\n /// for the withdrawn ETH.\n ///\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\n /// validator balance verification as well as execution layer contract balance snap.\n ///\n /// This behaviour is correct.\n ///\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n function snapBalances() external {\n uint64 currentTimestamp = SafeCast.toUint64(block.timestamp);\n require(\n lastSnapTimestamp + SNAP_BALANCES_DELAY < currentTimestamp,\n \"Snap too soon\"\n );\n\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(currentTimestamp);\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the balances in the mapping\n snappedBalances[blockRoot] = Balances({\n timestamp: currentTimestamp,\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n // Store the snapped timestamp\n lastSnapTimestamp = currentTimestamp;\n\n emit BalancesSnapped(blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct BalanceProofs {\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n /// @param snapBlockRoot The beacon block root emitted from `snapBalance` in `BalancesSnapped`.\n /// @param balanceProofs a `BalanceProofs` struct containing the following:\n /// balancesContainerRoot - the merkle root of the balances container\n /// balancesContainerProof - The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// validatorBalanceLeaves - Array of leaf nodes containing the validator balance with three other balances.\n /// validatorBalanceProofs - Array of merkle proofs for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(\n bytes32 snapBlockRoot,\n uint64 validatorVerificationBlockTimestamp,\n FirstPendingDepositProofData calldata firstPendingDeposit,\n BalanceProofs calldata balanceProofs\n ) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalances[snapBlockRoot];\n // Check the balances are the latest\n require(lastSnapTimestamp > 0, \"No snapped balances\");\n require(balancesMem.timestamp == lastSnapTimestamp, \"Stale snap\");\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n balanceProofs.validatorBalanceProofs.length ==\n verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n balanceProofs.validatorBalanceLeaves.length ==\n verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n snapBlockRoot,\n balanceProofs.balancesContainerRoot,\n balanceProofs.balancesContainerProof\n );\n\n // for each validator in reserve order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n balanceProofs.balancesContainerRoot,\n balanceProofs.validatorBalanceLeaves[i],\n balanceProofs.validatorBalanceProofs[i],\n validator[verifiedValidators[i]].index\n );\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n // This could have been in VERIFIED or EXITING state\n validator[verifiedValidators[i]].state = ValidatorState\n .EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += validatorBalanceGwei * 1 gwei;\n }\n }\n\n uint256 depositsCount = depositList.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification.\n // This section is after the validator balance verifications so an exited validator will be marked\n // as EXITED before the deposits are verified. If there was a deposit to an exited validator\n // then the deposit can only be removed once the validator is fully exited.\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n snapBlockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.pubKeyHash,\n firstPendingDeposit.pendingDepositPubKeyProof\n );\n\n // If there are no deposits in the beacon chain queue then our deposits must have been processed.\n // If the deposits have been processed, each deposit will need to be verified with `verifyDeposit`\n require(!isDepositQueueEmpty, \"Deposits have been processed\");\n\n // The verification of the validator the first pending deposit is for must be on or after when\n // `snapBalances` was called.\n require(\n balancesMem.timestamp <= validatorVerificationBlockTimestamp,\n \"Invalid validator timestamp\"\n );\n\n // Verify the validator of the first pending deposit is not exiting by checking\n // the withdrawable epoch is far into the future.\n // If it is exiting we can't be sure this deposit has not been postponed in the deposit queue.\n // Hence we can not verify if the strategy's deposit has been processed or not.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n // Get the parent beacon block root of the next block which is\n // the block root of the validator verification slot.\n // This will revert if the slot after the verification slot was missed.\n BeaconRoots.parentBlockRoot(\n validatorVerificationBlockTimestamp\n ),\n firstPendingDeposit.validatorIndex,\n firstPendingDeposit.pubKeyHash,\n // Validator is not exiting\n FAR_FUTURE_EPOCH,\n firstPendingDeposit.withdrawableEpochProof,\n firstPendingDeposit.validatorPubKeyProof\n );\n\n // solhint-disable max-line-length\n // If a validator is converted from a sweeping validator to a compounding validator, any balance in excess\n // of the min 32 ETH is put in the pending deposit queue. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // This will have a slot value of zero unfortunately.\n // We can not prove the strategy's deposits are still pending with a zero slot value so revert the tx.\n // Another snapBalances will need to be taken that does not have consolidation deposits at the front of the\n // beacon chain deposit queue.\n // solhint-enable max-line-length\n require(\n firstPendingDeposit.slot > 0,\n \"Invalid first pending deposit\"\n );\n\n // Calculate the epoch at the time of the snapBalances\n uint64 verificationEpoch = (SafeCast.toUint64(\n balancesMem.timestamp\n ) - BEACON_GENESIS_TIMESTAMP) / (SLOT_DURATION * SLOTS_PER_EPOCH);\n\n // For each staking strategy's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n uint256 depositID = depositList[i];\n DepositData memory depositData = deposits[depositID];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit slot is before the slot of the staking strategy's deposit.\n // If the deposit has been processed, it will need to be verified with `verifyDeposit`.\n // OR the deposit is to an exiting validator so check it is still not withdrawable.\n // If the validator is not withdrawable, then the deposit can not have been processed yet.\n // If the validator is now withdrawable, then the deposit may have been processed. The strategy\n // now has to wait until the validator's balance is verified to be zero.\n // OR the validator has exited and the deposit is now verified as processed.\n require(\n firstPendingDeposit.slot < depositData.slot ||\n (verificationEpoch < depositData.withdrawableEpoch &&\n depositData.withdrawableEpoch !=\n FAR_FUTURE_EPOCH) ||\n validator[depositData.pubKeyHash].state ==\n ValidatorState.EXITED,\n \"Deposit likely processed\"\n );\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei += uint256(depositData.amountGwei) * 1 gwei;\n\n // Remove the deposit if the validator has exited.\n if (\n validator[depositData.pubKeyHash].state ==\n ValidatorState.EXITED\n ) {\n _removeDeposit(depositID, depositData);\n\n emit DepositValidatorExited(\n depositID,\n uint256(depositData.amountGwei) * 1 gwei\n );\n }\n }\n }\n\n // Store the verified balance in storage\n lastVerifiedEthBalance = SafeCast.toUint128(\n totalDepositsWei + totalValidatorBalance + balancesMem.ethBalance\n );\n // Reset the last snap timestamp so a new snapBalances has to be made\n lastSnapTimestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n require(pubKey.length == 48, \"Invalid public key length\");\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /**\n *\n * WETH and ETH Accounting\n *\n */\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n // The min is required as more WETH can be withdrawn than deposited\n // as the strategy earns consensus and execution rewards.\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance.\n // The ETH balance in this strategy contract can be more than the last verified ETH balance\n // due to partial withdrawals or full exits being processed by the beacon chain since the last snapBalances.\n // It can also happen from execution rewards (MEV) or ETH donations.\n lastVerifiedEthBalance -= SafeCast.toUint128(\n Math.min(uint256(lastVerifiedEthBalance), _ethAmount)\n );\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += SafeCast.toUint128(_wethAmount);\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /**\n *\n * View Functions\n *\n */\n\n /// @notice Returns the number of deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n function depositListLength() external view returns (uint256) {\n return depositList.length;\n }\n\n /// @notice Returns the number of verified validators.\n function verifiedValidatorsLength() external view returns (uint256) {\n return verifiedValidators.length;\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/6e33f6818c72ee8e9621da9fde27c881.json b/contracts/deployments/hoodi/solcInputs/6e33f6818c72ee8e9621da9fde27c881.json new file mode 100644 index 0000000000..e04614058b --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/6e33f6818c72ee8e9621da9fde27c881.json @@ -0,0 +1,111 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/security/Pausable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the Beacon Block Root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Block Root Oracle to get the parent block root\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconOracle {\n function blockToSlot(uint64 blockNumber) external returns (uint64 slot);\n\n function slotToBlock(uint64 slot) external returns (uint64 blockNumber);\n\n function slotToRoot(uint64 slot) external returns (bytes32 blockRoot);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n enum BalanceProofLevel {\n Container,\n BeaconBlock\n }\n\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 root,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex,\n BalanceProofLevel level\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view;\n\n function verifyBlockNumber(\n bytes32 beaconBlockRoot,\n uint256 blockNumber,\n bytes calldata blockNumberProof\n ) external view;\n\n function verifySlot(\n bytes32 beaconBlockRoot,\n uint256 slot,\n bytes calldata slotProof\n ) external view;\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n external\n pure\n returns (uint256);\n\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) external pure returns (uint256);\n}\n" + }, + "contracts/interfaces/IConsolidation.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IConsolidationSource {\n function confirmConsolidation()\n external\n returns (uint256 consolidationCount);\n}\n\ninterface IConsolidationTarget {\n function requestConsolidation(\n bytes32 lastSourcePubKeyHash,\n bytes32 targetPubKeyHash\n ) external;\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconOracle Address of the Beacon Oracle contract that maps block numbers to slots\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconOracle,\n address _beaconProofs\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconOracle,\n _beaconProofs\n )\n {\n SSV_TOKEN = _ssvToken;\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract.\n /// @param _rewardTokenAddresses Address of reward token for platform\n /// @param _assets Addresses of initial supported assets\n /// @param _pTokens Platform Token corresponding addresses\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused for validator consolidation.\n /// @param _asset Address of asset to deposit. Has to be WETH.\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused for validator consolidation.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// Will revert if the strategy is paused for validator consolidation.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant whenNotPaused {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _asset, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(_asset, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n /// Will revert if the strategy is paused for validator consolidation.\n function withdrawAll()\n external\n override\n onlyVaultOrGovernor\n nonReentrant\n whenNotPaused\n {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, WETH, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Returns the last verified balance of validator deposits, validator balance,\n /// WETH and ETH in the strategy contract.\n /// @param _asset Address of weth asset\n /// @return balance Total value of (W)ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by strategy.\n /// @param _asset The address of the asset token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for deposits\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instread of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract,\n /// but so does withdrawals from validators. It's too complex to separate the two\n /// so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IConsolidationSource } from \"../../interfaces/IConsolidation.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\nimport { IBeaconOracle } from \"../../interfaces/IBeaconOracle.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit, remove and consolidate validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable, Pausable {\n using SafeERC20 for IERC20;\n\n /// @notice The amount of ETH in wei that is required for a deposit to a new validator.\n /// Initially this is 32 ETH, but will be increased to 1 ETH after P2P's APIs have been updated\n /// to support deposits of 1 ETH.\n uint256 public constant DEPOSIT_AMOUNT_WEI = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Oracle contract that maps block numbers to slots\n address public immutable BEACON_ORACLE;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// Deposit data for new compounding validators.\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountWei Amount of ETH in wei that has been deposited to the beacon chain deposit contract\n /// @param blockNumber Block number when the deposit was made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either PENDING or PROVEN\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 blockNumber;\n uint32 depositIndex;\n DepositStatus status;\n }\n /// @notice Mapping of the root of a deposit (depositDataRoot) to its data\n mapping(bytes32 => DepositData) public deposits;\n /// @notice List of deposit roots that are still to be verified as processed on the beacon chain\n bytes32[] public depositsRoots;\n\n // Validator data\n\n struct ValidatorData {\n bytes32 pubKeyHash; // Hash of the validator's public key using the Beacon Chain's format\n uint64 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes and indexes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n ValidatorData[] internal verifiedValidators;\n /// @notice State of the new compounding validators with a 0x02 withdrawal credential prefix.\n /// Uses the Beacon chain hashing for BLSPubkey which is\n /// sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => VALIDATOR_STATE) public validatorState;\n\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n uint64 timestamp;\n uint128 ethBalance;\n }\n // TODO is it more efficient to use the block root rather than hashing it?\n /// @notice Mapping of the block root to the balances at that slot\n mapping(bytes32 => Balances) public snappedBalances;\n uint64 public lastSnapTimestamp;\n uint128 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n bytes32 public consolidationLastPubKeyHash;\n address public consolidationSourceStrategy;\n mapping(address => bool) public consolidationSourceStrategies;\n\n // For future use\n uint256[50] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITED, // The validator has been verified to have a zero balance\n REMOVED // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified and is ready to be staked\n }\n\n event RegistratorChanged(address indexed newAddress);\n event SourceStrategyAdded(address indexed strategy);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n bytes32 indexed depositDataRoot,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint64 indexed validatorIndex\n );\n event DepositVerified(bytes32 indexed depositDataRoot, uint256 amountWei);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(\n uint256 indexed timestamp,\n bytes32 indexed blockRoot,\n uint256 ethBalance\n );\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 wethBalance,\n uint256 ethBalance\n );\n event ConsolidationRequested(\n bytes32 indexed lastSourcePubKeyHash,\n bytes32 indexed targetPubKeyHash,\n address indexed sourceStrategy\n );\n event ConsolidationVerified(\n bytes32 indexed lastSourcePubKeyHash,\n uint64 indexed lastValidatorIndex,\n uint256 consolidationCount\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconOracle Address of the Beacon Oracle contract that maps block numbers to slots\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconOracle,\n address _beaconProofs\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_ORACLE = _beaconOracle;\n BEACON_PROOFS = _beaconProofs;\n }\n\n /***************************************\n Admin Functions\n ****************************************/\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Adds support for a legacy staking strategy that can be used for consolidation.\n function addSourceStrategy(address _strategy) external onlyGovernor {\n consolidationSourceStrategies[_strategy] = true;\n\n emit SourceStrategyAdded(_strategy);\n }\n\n /***************************************`\n Validator Management\n ****************************************/\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validatorState[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validator validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validator,\n uint64 depositAmountGwei\n ) external onlyRegistrator whenNotPaused {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validator.pubkey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == VALIDATOR_STATE.REGISTERED ||\n currentState == VALIDATOR_STATE.VERIFIED),\n \"Not registered or verified\"\n );\n require(\n currentState == VALIDATOR_STATE.VERIFIED ||\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validator.pubkey,\n withdrawalCredentials,\n validator.signature,\n validator.depositDataRoot\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == VALIDATOR_STATE.REGISTERED) {\n validatorState[pubKeyHash] = VALIDATOR_STATE.STAKED;\n }\n // Store the deposit data for verifyDeposit and verifyBalances\n deposits[validator.depositDataRoot] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n blockNumber: SafeCast.toUint64(block.number),\n depositIndex: SafeCast.toUint32(depositsRoots.length),\n status: DepositStatus.PENDING\n });\n depositsRoots.push(validator.depositDataRoot);\n\n emit ETHStaked(\n pubKeyHash,\n validator.depositDataRoot,\n validator.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then the request will fail.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n onlyRegistrator\n whenNotPaused\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove a sweeping validator from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == VALIDATOR_STATE.EXITED ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exited\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validatorState[pubKeyHash] = VALIDATOR_STATE.REMOVED;\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /// @notice Receives requests from supported legacy strategies to consolidate sweeping validators to\n /// a new compounding validator on this new strategy.\n /// @param lastSourcePubKeyHash The last source validator to be consolidated hashed using the Beacon Chain's format.\n /// @param targetPubKeyHash The target validator's public key hash using the Beacon Chain's format.\n function requestConsolidation(\n bytes32 lastSourcePubKeyHash,\n bytes32 targetPubKeyHash\n ) external whenNotPaused {\n require(\n consolidationSourceStrategies[msg.sender],\n \"Not a source strategy\"\n );\n\n // The target validator must be a compounding validator that has been verified\n require(\n validatorState[targetPubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Target validator not verified\"\n );\n\n // Ensure balances can not be verified until after the consolidation has been verified.\n lastSnapTimestamp = 0;\n\n // Store consolidation state\n consolidationLastPubKeyHash = lastSourcePubKeyHash;\n consolidationSourceStrategy = msg.sender;\n\n // Pause the strategy while the consolidation is in progress\n _pause();\n\n emit ConsolidationRequested(\n lastSourcePubKeyHash,\n targetPubKeyHash,\n msg.sender\n );\n }\n\n /***************************************\n SSV Management\n ****************************************/\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n Beacon Chain Proofs\n ****************************************/\n\n /// @notice Verifies a validator's index to its public key.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param validatorPubKeyProof The merkle proof that the validator's index matches its public key\n /// on the beacon chain.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.STAKED,\n \"Validator not staked\"\n );\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key\n IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n address(this) // Withdrawal address is this strategy\n );\n\n // TODO verify the validator's withdrawal credential points to this strategy\n\n // Store the validator state as verified\n validatorState[pubKeyHash] = VALIDATOR_STATE.VERIFIED;\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(\n ValidatorData({ pubKeyHash: pubKeyHash, index: validatorIndex })\n );\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n /// @param depositDataRoot The root of the deposit data that was stored when\n /// the deposit was made on the execution layer.\n /// @param depositBlockNumber A block number that is on or after the block the deposit\n /// was made on the execution layer.\n /// @param processedSlot Any slot on or after the deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// @param firstPendingDepositSlot The slot of the first pending deposit in the beacon chain\n /// @param firstPendingDepositSlotProof The merkle proof that the slot of the first pending deposit\n /// matches the beacon chain.\n /// BeaconBlock.BeaconBlockBody.deposits[0].slot\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n bytes32 depositDataRoot,\n uint64 depositBlockNumber,\n uint64 processedSlot,\n uint64 firstPendingDepositSlot,\n bytes calldata firstPendingDepositSlotProof\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositDataRoot];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n validatorState[deposit.pubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n // The deposit block number mapped to a slot needs to be the same block or after\n // the deposit in `stakeETH` was created on the execution layer.\n require(\n deposit.blockNumber <= depositBlockNumber,\n \"Deposit block before deposit\"\n );\n\n // Get the slot that is on or after the deposit was made on the execution layer.\n uint64 depositSlot = IBeaconOracle(BEACON_ORACLE).blockToSlot(\n depositBlockNumber\n );\n\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the first pending deposit's slot is 0 then there are no pending deposits so\n // our deposit must have been processed on the beacon chain.\n require(\n depositSlot < firstPendingDepositSlot ||\n firstPendingDepositSlot == 0,\n \"Deposit not processed\"\n );\n\n // The slot of the execution layer deposit must be before\n // the slot that the deposit was processed on the beacon chain.\n require(depositSlot < processedSlot, \"Slot not after deposit\");\n\n // Get the beacon block root for the slot that is on or after the deposit was processed on the beacon chain.\n bytes32 blockRoot = IBeaconOracle(BEACON_ORACLE).slotToRoot(\n processedSlot\n );\n\n // Verify the slot of the first pending deposit matches the beacon chain\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n blockRoot,\n firstPendingDepositSlot,\n firstPendingDepositSlotProof\n );\n\n // After verifying the proof, update the contract storage\n deposits[depositDataRoot].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n bytes32 lastDepositDataRoot = depositsRoots[depositsRoots.length - 1];\n depositsRoots[deposit.depositIndex] = lastDepositDataRoot;\n deposits[lastDepositDataRoot].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositsRoots.pop();\n\n emit DepositVerified(\n depositDataRoot,\n uint256(deposit.amountGwei) * 1 gwei\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n // TODO what if the last validator was exited rather than consolidated?\n // slither-disable-start reentrancy-no-eth\n // function verifyConsolidation(\n // uint64 parentBlockTimestamp,\n // uint64 lastValidatorIndex,\n // bytes calldata validatorPubKeyProof,\n // bytes32 balancesLeaf,\n // bytes calldata validatorBalanceProof\n // ) external onlyRegistrator {\n // bytes32 consolidationLastPubKeyHashMem = consolidationLastPubKeyHash;\n // require(\n // consolidationLastPubKeyHashMem != bytes32(0),\n // \"No consolidations\"\n // );\n\n // bytes32 blockRoot = BeaconRoots.parentBlockRoot(parentBlockTimestamp);\n // // Verify the validator index has the same public key as the last source validator\n // IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n // blockRoot,\n // consolidationLastPubKeyHashMem,\n // validatorPubKeyProof,\n // lastValidatorIndex,\n // address(this) // Withdrawal address is this strategy\n // );\n\n // // Verify the balance of the last validator in the consolidation batch\n // // is zero. If its not then the consolidation has not been completed.\n // // This proof is to the beacon block root, not the balances container root.\n // uint256 validatorBalance = IBeaconProofs(BEACON_PROOFS)\n // .verifyValidatorBalance(\n // blockRoot,\n // balancesLeaf,\n // validatorBalanceProof,\n // lastValidatorIndex,\n // IBeaconProofs.BalanceProofLevel.BeaconBlock\n // );\n // require(validatorBalance == 0, \"Last validator balance not zero\");\n\n // // Call the old sweeping strategy to confirm the consolidation has been completed.\n // // This will decrease the balance of the source strategy by 32 ETH for each validator being consolidated.\n // uint256 consolidationCount = IConsolidationSource(\n // consolidationSourceStrategy\n // ).confirmConsolidation();\n\n // // Increase the balance of this strategy by 32 ETH for each validator being consolidated.\n // // This nets out the decrease in the source strategy's balance.\n // lastVerifiedEthBalance += SafeCast.toUint128(\n // consolidationCount * 32 ether\n // );\n\n // // Reset the stored consolidation state\n // consolidationLastPubKeyHash = bytes32(0);\n // consolidationSourceStrategy = address(0);\n\n // emit ConsolidationVerified(\n // consolidationLastPubKeyHashMem,\n // lastValidatorIndex,\n // consolidationCount\n // );\n\n // // Unpause now the balance of the target validator has been verified\n // _unpause();\n\n // // Take a snap of the balances so the actual balances of the new validator balances can be verified\n // _snapBalances();\n // }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block.\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n /// Can only be called by the registrator.\n /// Can not be called while a consolidation is in progress.\n function snapBalances() external whenNotPaused onlyRegistrator {\n _snapBalances();\n }\n\n function _snapBalances() internal {\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(\n SafeCast.toUint64(block.timestamp)\n );\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the balances in the mapping\n snappedBalances[blockRoot] = Balances({\n timestamp: SafeCast.toUint64(block.timestamp),\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n // Store the snapped timestamp\n lastSnapTimestamp = SafeCast.toUint64(block.timestamp);\n\n emit BalancesSnapped(block.timestamp, blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct VerifyBalancesParams {\n bytes32 blockRoot;\n uint64 mappedDepositSlot;\n uint64 firstPendingDepositSlot;\n // BeaconBlock.BeaconBlockBody.deposits[0].slot\n bytes firstPendingDepositSlotProof;\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(VerifyBalancesParams calldata params) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalances[params.blockRoot];\n // Check the balances are the latest\n require(lastSnapTimestamp > 0, \"No snapped balances\");\n require(balancesMem.timestamp == lastSnapTimestamp, \"Stale snap\");\n\n uint256 depositsCount = depositsRoots.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit to the beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n params.blockRoot,\n params.firstPendingDepositSlot,\n params.firstPendingDepositSlotProof\n );\n\n // As the BeaconOracle probably doesn't have the slot of the first pending deposit mapped to a block,\n // use any slot that is on or after the slot of the first pending deposit. That is,\n // firstPendingDepositSlot <= mappedDepositSlot < deposits[depositDataRoot].blockNumber\n require(\n params.firstPendingDepositSlot <= params.mappedDepositSlot,\n \"Invalid deposit slot\"\n );\n uint64 firstPendingDepositBlockNumber = IBeaconOracle(BEACON_ORACLE)\n .slotToBlock(params.mappedDepositSlot);\n\n // For each native staking contract's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n bytes32 depositDataRoot = depositsRoots[i];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit block number is before the\n // block number of the staking strategy's deposit.\n // If it has it will need to be verified with `verifyDeposit`\n require(\n firstPendingDepositBlockNumber <\n deposits[depositDataRoot].blockNumber,\n \"Deposit has been processed\"\n );\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei +=\n uint256(deposits[depositDataRoot].amountGwei) *\n 1 gwei;\n }\n }\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n params.validatorBalanceProofs.length == verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n params.validatorBalanceLeaves.length == verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n params.blockRoot,\n params.balancesContainerRoot,\n params.balancesContainerProof\n );\n\n // for each validator in reserve order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n params.balancesContainerRoot,\n params.validatorBalanceLeaves[i],\n params.validatorBalanceProofs[i],\n verifiedValidators[i].index,\n IBeaconProofs.BalanceProofLevel.Container\n );\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n validatorState[\n verifiedValidators[i].pubKeyHash\n ] = VALIDATOR_STATE.EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += uint256(validatorBalanceGwei) * 1 gwei;\n }\n }\n\n uint256 wethBalance = IWETH9(WETH).balanceOf(address(this));\n\n // Store the verified balance in storage\n lastVerifiedEthBalance = SafeCast.toUint128(\n totalDepositsWei + totalValidatorBalance + balancesMem.ethBalance\n );\n // Reset the last snap timestamp so a new snapBalances has to be made\n lastSnapTimestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n wethBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /***************************************\n WETH and ETH Accounting\n ****************************************/\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance\n if (lastVerifiedEthBalance > _ethAmount) {\n lastVerifiedEthBalance -= SafeCast.toUint128(_ethAmount);\n } else {\n // This can happen if all ETH in the validators was withdrawn\n // and there was more consensus rewards since the last balance verification.\n // Or it can happen if ETH is donated to this strategy.\n lastVerifiedEthBalance = 0;\n }\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += SafeCast.toUint128(_wethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /***************************************\n View Functions\n ****************************************/\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return An array of `ValidatorData` containing the public key hash and validator index.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorData[] memory)\n {\n return verifiedValidators;\n }\n\n /// @notice Returns the deposits that are still to be verified.\n /// These may or may not have been processed by the beacon chain.\n function getPendingDeposits()\n external\n view\n returns (DepositData[] memory pendingDeposits)\n {\n pendingDeposits = new DepositData[](depositsRoots.length);\n for (uint256 i = 0; i < depositsRoots.length; ++i) {\n pendingDeposits[i] = deposits[depositsRoots[i]];\n }\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/74152299d545d185cd39c02ad22eda4d.json b/contracts/deployments/hoodi/solcInputs/74152299d545d185cd39c02ad22eda4d.json new file mode 100644 index 0000000000..750b2cd48d --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/74152299d545d185cd39c02ad22eda4d.json @@ -0,0 +1,114 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/beacon/BeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"./BeaconProofsLib.sol\";\n\n/**\n * @title Verifies merkle proofs of beacon chain data.\n * @author Origin Protocol Inc\n */\ncontract BeaconProofs {\n /// @notice Verifies the validator public key to the beacon block root\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view {\n BeaconProofsLib.verifyValidatorPubkey(\n beaconBlockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n withdrawalAddress\n );\n }\n\n /// @notice Verifies the balances container to the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerRoot The merkle root of the the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata balancesContainerProof\n ) external view {\n BeaconProofsLib.verifyBalancesContainer(\n beaconBlockRoot,\n balancesContainerRoot,\n balancesContainerProof\n );\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param balanceProof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalanceGwei) {\n validatorBalanceGwei = BeaconProofsLib.verifyValidatorBalance(\n balancesContainerRoot,\n validatorBalanceLeaf,\n balanceProof,\n validatorIndex\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the slot of the first pending deposit to the beacon block root\n /// BeaconBlock.state.PendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero would be a good choice.\n /// @param firstPendingDepositSlotProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view returns (bool isEmptyDepositQueue) {\n isEmptyDepositQueue = BeaconProofsLib.verifyFirstPendingDepositSlot(\n beaconBlockRoot,\n slot,\n firstPendingDepositSlotProof\n );\n }\n}\n" + }, + "contracts/beacon/BeaconProofsLib.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Merkle } from \"./Merkle.sol\";\nimport { Endian } from \"./Endian.sol\";\n\n/**\n * @title Library to verify merkle proofs of beacon chain data.\n * @author Origin Protocol Inc\n */\nlibrary BeaconProofsLib {\n // Known generalized indices in the beacon block\n /// @dev BeaconBlock.slot\n uint256 internal constant SLOT_GENERALIZED_INDEX = 8;\n /// @dev BeaconBlock.state.PendingDeposits[0]\n uint256 internal constant FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX =\n 198105366528;\n /// @dev BeaconBlock.state.PendingDeposits[0].slot\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX =\n 1584842932228;\n /// @dev BeaconBlock.body.executionPayload.blockNumber\n uint256 internal constant BLOCK_NUMBER_GENERALIZED_INDEX = 6438;\n /// @dev BeaconBlock.state.validators\n uint256 internal constant VALIDATORS_CONTAINER_GENERALIZED_INDEX = 715;\n /// @dev BeaconBlock.state.balances\n uint256 internal constant BALANCES_CONTAINER_GENERALIZED_INDEX = 716;\n\n /// @dev Number of bytes in the proof to the first pending deposit.\n /// 37 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0]\n uint256 internal constant FIRST_PENDING_DEPOSIT_PROOF_LENGTH = 37 * 32;\n /// @dev Number of bytes in the proof to the slot of the first pending deposit.\n /// 40 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0].slot\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_PROOF_LENGTH = 40 * 32;\n\n /// @dev Merkle height of the Balances container\n /// BeaconBlock.state.balances\n uint256 internal constant BALANCES_HEIGHT = 39;\n /// @dev Merkle height of the Validators container\n /// BeaconBlock.state.validators\n uint256 internal constant VALIDATORS_HEIGHT = 41;\n /// @dev Merkle height of the Validator container\n /// BeaconBlock.state.validators[validatorIndex]\n uint256 internal constant VALIDATOR_HEIGHT = 3;\n\n /// @dev Position of the pubkey field in the Validator container.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;\n\n /// @notice Verifies the validator public key to the beacon block root\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) internal view {\n require(\n // 53 * 32 bytes = 1696 bytes\n validatorPubKeyProof.length == 1696,\n \"Invalid proof length\"\n );\n\n // BeaconBlock.state.validators[validatorIndex]\n uint256 generalizedIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_HEIGHT,\n validatorIndex\n );\n // BeaconBlock.state.validators[validatorIndex].pubkey\n generalizedIndex = concatGenIndices(\n generalizedIndex,\n VALIDATOR_HEIGHT,\n VALIDATOR_PUBKEY_INDEX\n );\n\n // Get the withdrawal address from the first witness in the pubkey merkle proof.\n address withdrawalAddressFromProof;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // The first 32 bytes of the proof is the withdrawal credential so load it into memory.\n calldatacopy(0, validatorPubKeyProof.offset, 32)\n // Cast the 32 bytes in memory to an address which is the last 20 bytes.\n withdrawalAddressFromProof := mload(0)\n }\n require(\n withdrawalAddressFromProof == withdrawalAddress,\n \"Invalid withdrawal address\"\n );\n\n require(\n Merkle.verifyInclusionSha256({\n proof: validatorPubKeyProof,\n root: beaconBlockRoot,\n leaf: pubKeyHash,\n index: generalizedIndex\n }),\n \"Invalid validator pubkey proof\"\n );\n }\n\n /// @notice Verifies the balances container to the beacon block root.\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param balancesContainerRoot The merkle root of the the balances container.\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata balancesContainerProof\n ) internal view {\n require(\n // 9 * 32 bytes = 288 bytes\n balancesContainerProof.length == 288,\n \"Invalid proof length\"\n );\n\n // BeaconBlock.state.balances\n require(\n Merkle.verifyInclusionSha256({\n proof: balancesContainerProof,\n root: beaconBlockRoot,\n leaf: balancesContainerRoot,\n index: BALANCES_CONTAINER_GENERALIZED_INDEX\n }),\n \"Invalid balance container proof\"\n );\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param balanceProof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for.\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index.\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) internal view returns (uint256 validatorBalanceGwei) {\n require(\n // 39 * 32 bytes = 1248 bytes\n balanceProof.length == 1248,\n \"Invalid proof length\"\n );\n\n // Four balances are stored in each leaf so the validator index is divided by 4\n uint64 balanceIndex = validatorIndex / 4;\n\n // Get the index within the balances container, not the Beacon Block\n // BeaconBlock.state.balances[balanceIndex]\n uint256 generalizedIndex = concatGenIndices(\n 1,\n BALANCES_HEIGHT,\n balanceIndex\n );\n\n validatorBalanceGwei = balanceAtIndex(\n validatorBalanceLeaf,\n validatorIndex\n );\n\n require(\n Merkle.verifyInclusionSha256({\n proof: balanceProof,\n root: balancesContainerRoot,\n leaf: validatorBalanceLeaf,\n index: generalizedIndex\n }),\n \"Invalid balance proof\"\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the slot of the first pending deposit to the beacon block root\n /// BeaconBlock.state.PendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero would be a good choice.\n /// @param firstPendingDepositSlotProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) internal view returns (bool isEmptyDepositQueue) {\n require(\n // 40 * 32 bytes = 1280 bytes\n firstPendingDepositSlotProof.length == 1280 ||\n // 37 * 32 bytes = 1184 bytes\n firstPendingDepositSlotProof.length == 1184,\n \"Invalid proof length\"\n );\n\n uint256 generalizedIndex;\n bytes32 leaf;\n // If the deposit queue is empty\n if (\n firstPendingDepositSlotProof.length ==\n FIRST_PENDING_DEPOSIT_PROOF_LENGTH\n ) {\n isEmptyDepositQueue = true;\n // use an empty leaf node as the root of the first pending deposit\n // when the deposit queue is empty\n leaf = bytes32(0);\n // BeaconBlock.state.PendingDeposits[0]\n generalizedIndex = FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX;\n } else if (\n firstPendingDepositSlotProof.length ==\n FIRST_PENDING_DEPOSIT_SLOT_PROOF_LENGTH\n ) {\n // Convert uint64 slot number to a little endian bytes32\n leaf = Endian.toLittleEndianUint64(slot);\n // BeaconBlock.state.PendingDeposits[0].slot\n generalizedIndex = FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX;\n } else {\n revert(\"Invalid proof length\");\n }\n\n require(\n Merkle.verifyInclusionSha256({\n proof: firstPendingDepositSlotProof,\n root: beaconBlockRoot,\n leaf: leaf,\n index: generalizedIndex\n }),\n \"Invalid pending deposit proof\"\n );\n }\n\n ////////////////////////////////////////////////////\n /// Internal Helper Functions\n ////////////////////////////////////////////////////\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n internal\n pure\n returns (uint256)\n {\n uint256 bitShiftAmount = (validatorIndex % 4) * 64;\n return\n Endian.fromLittleEndianUint64(\n bytes32((uint256(validatorBalanceLeaf) << bitShiftAmount))\n );\n }\n\n /// @notice Concatenates two beacon chain generalized indices into one.\n /// @param genIndex The first generalized index or 1 if calculating for a single container.\n /// @param height The merkle tree height of the second container. eg 39 for balances, 41 for validators.\n /// @param index The index within the second container. eg the validator index.\n /// @return genIndex The concatenated generalized index.\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) internal pure returns (uint256) {\n return (genIndex << height) | index;\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to retrieve beacon block roots.\n * @author Origin Protocol Inc\n */\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the beacon block root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Roots contract to get the parent block root.\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/Endian.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n/**\n * @title Library to handle conversion between little-endian and big-endian formats.\n * @author Origin Protocol Inc\n */\nlibrary Endian {\n /**\n * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64\n * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type\n * @return n The big endian-formatted uint64\n * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits),\n * but it is immediately truncated to a uint64 (i.e. 64 bits)\n * through a right-shift/shr operation.\n */\n function fromLittleEndianUint64(bytes32 lenum)\n internal\n pure\n returns (uint64 n)\n {\n // the number needs to be stored in little-endian encoding (ie in bytes 0-8)\n n = uint64(uint256(lenum >> 192));\n // forgefmt: disable-next-item\n return\n (n >> 56) |\n ((0x00FF000000000000 & n) >> 40) |\n ((0x0000FF0000000000 & n) >> 24) |\n ((0x000000FF00000000 & n) >> 8) |\n ((0x00000000FF000000 & n) << 8) |\n ((0x0000000000FF0000 & n) << 24) |\n ((0x000000000000FF00 & n) << 40) |\n ((0x00000000000000FF & n) << 56);\n }\n\n function toLittleEndianUint64(uint64 benum)\n internal\n pure\n returns (bytes32 n)\n {\n // Convert to little-endian by reversing byte order\n uint64 reversed = (benum >> 56) |\n ((0x00FF000000000000 & benum) >> 40) |\n ((0x0000FF0000000000 & benum) >> 24) |\n ((0x000000FF00000000 & benum) >> 8) |\n ((0x00000000FF000000 & benum) << 8) |\n ((0x0000000000FF0000 & benum) << 24) |\n ((0x000000000000FF00 & benum) << 40) |\n ((0x00000000000000FF & benum) << 56);\n\n // Store the little-endian uint64 in the least significant 64 bits of bytes32\n n = bytes32(uint256(reversed));\n // Shift to most significant bits\n n = n << 192;\n }\n}\n" + }, + "contracts/beacon/Merkle.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev These functions deal with verification of Merkle Tree proofs.\n *\n * The tree and the proofs can be generated using our\n * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].\n * You will find a quickstart guide in the readme.\n *\n * WARNING: You should avoid using leaf values that are 64 bytes long prior to\n * hashing, or use a hash function other than keccak256 for hashing leaves.\n * This is because the concatenation of a sorted pair of internal nodes in\n * the merkle tree could be reinterpreted as a leaf value.\n * OpenZeppelin's JavaScript library generates merkle trees that are safe\n * against this attack out of the box.\n */\nlibrary Merkle {\n error InvalidProofLength();\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function verifyInclusionSha256(\n bytes memory proof,\n bytes32 root,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bool) {\n return processInclusionProofSha256(proof, leaf, index) == root;\n }\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * _Available since v4.4._\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function processInclusionProofSha256(\n bytes memory proof,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bytes32) {\n require(\n proof.length != 0 && proof.length % 32 == 0,\n InvalidProofLength()\n );\n bytes32[1] memory computedHash = [leaf];\n for (uint256 i = 32; i <= proof.length; i += 32) {\n if (index % 2 == 0) {\n // if ith bit of index is 0, then computedHash is a left sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(computedHash))\n mstore(0x20, mload(add(proof, i)))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n index := div(index, 2)\n }\n } else {\n // if ith bit of index is 1, then computedHash is a right sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(add(proof, i)))\n mstore(0x20, mload(computedHash))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n index := div(index, 2)\n }\n }\n }\n return computedHash[0];\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to request full or partial withdrawals from validators on the beacon chain.\n * @author Origin Protocol Inc\n */\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n enum BalanceProofLevel {\n Container,\n BeaconBlock\n }\n\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view returns (bool isEmptyDepositQueue);\n\n function verifyBlockNumber(\n bytes32 beaconBlockRoot,\n uint256 blockNumber,\n bytes calldata blockNumberProof\n ) external view;\n\n function verifySlot(\n bytes32 beaconBlockRoot,\n uint256 slot,\n bytes calldata slotProof\n ) external view;\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n external\n pure\n returns (uint256);\n\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) external pure returns (uint256);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/mocks/MockBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"../beacon/BeaconProofsLib.sol\";\nimport { BeaconProofs } from \"../beacon/BeaconProofs.sol\";\n\ncontract MockBeaconProofs is BeaconProofs {\n function concatGenIndices(\n uint256 index1,\n uint256 height2,\n uint256 index2\n ) external pure returns (uint256 genIndex) {\n return BeaconProofsLib.concatGenIndices(index1, height2, index2);\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconProofs,\n _beaconGenesisTimestamp\n )\n {\n SSV_TOKEN = _ssvToken;\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// @param _rewardTokenAddresses Not used so empty array\n /// @param _assets Not used so empty array\n /// @param _pTokens Not used so empty array\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH that was transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _asset, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(_asset, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, WETH, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Accounts for all the assets managed by this strategy which includes:\n /// 1. The current WETH in this strategy contract\n /// 2. The last verified ETH balance, total deposits and total validator balances\n /// @param _asset Address of WETH asset.\n /// @return balance Total value in ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by the strategy.\n /// @param _asset The address of the WETH token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for validator registration.\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens when validators are registered\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n * The new ETH will be accounted for in `checkBalance` after the next snapBalances and verifyBalances txs.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n /// @dev This strategy does not use a platform token like the old Aave and Compound strategies.\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instead of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract.\n /// Withdrawals from validators also accumulate as ETH in this strategy contract.\n /// It's too complex to separate the rewards from withdrawals so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can now regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable {\n using SafeERC20 for IERC20;\n\n /// @notice The amount of ETH in wei that is required for a deposit to a new validator.\n /// Initially this is 32 ETH, but will be increased to 1 ETH after P2P's APIs have been updated\n /// to support deposits of 1 ETH.\n uint256 public constant DEPOSIT_AMOUNT_WEI = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n /// @notice The timestamp of the Beacon chain genesis.\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\n uint64 public immutable BEACON_GENESIS_TIMESTAMP;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// Deposit data for new compounding validators.\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountWei Amount of ETH in wei that has been deposited to the beacon chain deposit contract\n /// @param blockNumber Block number when the deposit was made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either PENDING or PROVEN\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint32 depositIndex;\n DepositStatus status;\n }\n /// @notice Mapping of the root of a deposit (depositDataRoot) to its data\n mapping(bytes32 => DepositData) public deposits;\n /// @notice List of deposit roots that are still to be verified as processed on the beacon chain\n bytes32[] public depositsRoots;\n\n // Validator data\n\n struct ValidatorData {\n bytes32 pubKeyHash; // Hash of the validator's public key using the Beacon Chain's format\n uint64 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes and indexes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n ValidatorData[] internal verifiedValidators;\n /// @notice State of the new compounding validators with a 0x02 withdrawal credential prefix.\n /// Uses the Beacon chain hashing for BLSPubkey which is\n /// sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => VALIDATOR_STATE) public validatorState;\n\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n uint64 timestamp;\n uint128 ethBalance;\n }\n // TODO is it more efficient to use the block root rather than hashing it?\n /// @notice Mapping of the block root to the balances at that slot\n mapping(bytes32 => Balances) public snappedBalances;\n uint64 public lastSnapTimestamp;\n uint128 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[50] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITED, // The validator has been verified to have a zero balance\n REMOVED // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified and is ready to be staked\n }\n\n event RegistratorChanged(address indexed newAddress);\n event SourceStrategyAdded(address indexed strategy);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n bytes32 indexed depositDataRoot,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint64 indexed validatorIndex\n );\n event DepositVerified(bytes32 indexed depositDataRoot, uint256 amountWei);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(\n uint256 indexed timestamp,\n bytes32 indexed blockRoot,\n uint256 ethBalance\n );\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 wethBalance,\n uint256 ethBalance\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_PROOFS = _beaconProofs;\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\n\n require(\n block.timestamp > _beaconGenesisTimestamp,\n \"Invalid genesis timestamp\"\n );\n }\n\n /***************************************\n Admin Functions\n ****************************************/\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /***************************************`\n Validator Management\n ****************************************/\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validatorState[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validator validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validator,\n uint64 depositAmountGwei\n ) external onlyRegistrator {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validator.pubkey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == VALIDATOR_STATE.REGISTERED ||\n currentState == VALIDATOR_STATE.VERIFIED),\n \"Not registered or verified\"\n );\n require(\n currentState == VALIDATOR_STATE.VERIFIED ||\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validator.pubkey,\n withdrawalCredentials,\n validator.signature,\n validator.depositDataRoot\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == VALIDATOR_STATE.REGISTERED) {\n validatorState[pubKeyHash] = VALIDATOR_STATE.STAKED;\n }\n\n /// After the Pectra upgrade the validators have a new restriction when proposing\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\n /// forward. Each slot is created at strict 12 second intervals and those slots can\n /// either have blocks attached to them or not. This way using the block.timestamp\n /// the slot number can easily be calculated.\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\n BEACON_GENESIS_TIMESTAMP) / 12;\n\n // Store the deposit data for verifyDeposit and verifyBalances\n deposits[validator.depositDataRoot] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n slot: depositSlot,\n depositIndex: SafeCast.toUint32(depositsRoots.length),\n status: DepositStatus.PENDING\n });\n depositsRoots.push(validator.depositDataRoot);\n\n emit ETHStaked(\n pubKeyHash,\n validator.depositDataRoot,\n validator.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then the request will fail.\n /// Only the Registrator can call this function.\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\n /// If no ETH balance, the tx will revert.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n payable\n onlyRegistrator\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove a sweeping validator from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == VALIDATOR_STATE.EXITED ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exited\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validatorState[pubKeyHash] = VALIDATOR_STATE.REMOVED;\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /***************************************\n SSV Management\n ****************************************/\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n Beacon Chain Proofs\n ****************************************/\n\n /// @notice Verifies a validator's index to its public key.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.STAKED,\n \"Validator not staked\"\n );\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key.\n // Also verify the validator's withdrawal credential points to this strategy.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n address(this) // Withdrawal address is this strategy\n );\n\n // Store the validator state as verified\n validatorState[pubKeyHash] = VALIDATOR_STATE.VERIFIED;\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(\n ValidatorData({ pubKeyHash: pubKeyHash, index: validatorIndex })\n );\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n ///\n /// Important: this function has a limitation where the `verificationSlot` that is passed by the off-chain\n /// verifier requires a slot immediately after it to propose a block otherwise the `BeaconRoots.parentBlockRoot`\n /// will fail. This shouldn't be a problem, since by the current behaviour of beacon chain only 1%-3% slots\n /// don't propose a block.\n /// @param depositDataRoot The root of the deposit data that was stored when\n /// the deposit was made on the execution layer.\n /// @param verificationSlot Any slot on or after the deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\n /// set for the next block timestamp in 12 seconds time.\n /// @param firstPendingDepositSlot The slot of the first pending deposit in the beacon chain.\n /// Can be anything if the deposit queue is empty, but zero is a good choice.\n /// @param firstPendingDepositSlotProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n bytes32 depositDataRoot,\n uint64 verificationSlot,\n uint64 firstPendingDepositSlot,\n bytes calldata firstPendingDepositSlotProof\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositDataRoot];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n validatorState[deposit.pubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n // The verification slot must be after the deposit's slot.\n // This is needed for when the deposit queue is empty.\n require(deposit.slot < verificationSlot, \"Slot not after deposit\");\n\n // Calculate the next block timestamp from the verification slot.\n uint64 nextBlockTimestamp = 12 *\n verificationSlot +\n BEACON_GENESIS_TIMESTAMP +\n // Add 12 seconds for the next block\n 12;\n // Get the parent beacon block root of the next block which is the block root of the verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDepositSlot(\n blockRoot,\n firstPendingDepositSlot,\n firstPendingDepositSlotProof\n );\n\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the deposit queue is empty then our deposit must have been processed on the beacon chain.\n // The deposit slot can be zero for validators consolidating to a compounding validator. We can\n // not guarantee that the deposit has been processed in that case.\n require(\n deposit.slot < firstPendingDepositSlot || isDepositQueueEmpty,\n \"Deposit likely not processed\"\n );\n\n // After verifying the proof, update the contract storage\n deposits[depositDataRoot].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n bytes32 lastDepositDataRoot = depositsRoots[depositsRoots.length - 1];\n depositsRoots[deposit.depositIndex] = lastDepositDataRoot;\n deposits[lastDepositDataRoot].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositsRoots.pop();\n\n emit DepositVerified(\n depositDataRoot,\n uint256(deposit.amountGwei) * 1 gwei\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block and beacon block root\n /// of the slot that is associated with the previous block.\n ///\n /// When snapping / verifying balance it is of a high importance that there is no\n /// miss-match in respect to ETH that is held by the contract and balances that are\n /// verified on the validators.\n ///\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\n /// constructing a block on the beacon chain consist of:\n /// - process_withdrawals: ETH is deducted from the validator's balance\n /// - process_execution_payload: immediately after the previous step executing all the\n /// transactions\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\n /// contained in the withdrawal credentials of the exited validators\n ///\n /// That means that balance increases which are part of the post-block execution state are\n /// done within the block, but the transaction that are contained within that block can not\n /// see / interact with the balance from the exited validators. Only transactions in the\n /// next block can do that.\n ///\n /// When snap balances is performed the state of the chain is snapped across 2 separate\n /// chain states:\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\n /// will be referred to as Y - 1 as it makes no difference in the argument\n ///\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\n /// miss-counting ETH or much more dangerous double counting of the ETH.\n ///\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\n /// - ETH balance in the contract on block X\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\n /// - ETH balance in validators that are active in slot Y - 1\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\n /// and have their balance visible to transactions in slot Y and corresponding block X\n /// (or sooner)\n ///\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\n ///\n /// *ETH balance in the contract on block X*\n ///\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\n ///\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\n /// would result in double counting the `stakedEth` since it would be present once in the\n /// snapped contract balance and the second time in deposit storage variables.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\n ///\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\n /// contract at block Z. The execution layer doesn't have direct access to the state of\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\n /// and could also be part of the validator balances. It does that by verifying that at\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\n /// the last snap till now all are still in queue. Which ensures they can not be part of\n /// the validator balances in later steps.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in validators that are active in slot Y - 1*\n ///\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\n /// correctness.\n ///\n /// This behaviour is correct.\n ///\n /// *The withdrawn validators*\n ///\n /// The withdrawn validators could have their balances deducted in any slot before slot\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\n /// look at the \"worst case scenario\" where the validator withdrawal is processed in the\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\n /// even if the validator exits at the latest possible time it is paramount that the ETH\n /// balance on the execution layer is recorded in the next block. Correctly accounting\n /// for the withdrawn ETH.\n ///\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\n /// validator balance verification as well as execution layer contract balance snap.\n ///\n /// This behaviour is correct.\n ///\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n /// Can only be called by the registrator.\n function snapBalances() external onlyRegistrator {\n _snapBalances();\n }\n\n function _snapBalances() internal {\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(\n SafeCast.toUint64(block.timestamp)\n );\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the balances in the mapping\n snappedBalances[blockRoot] = Balances({\n timestamp: SafeCast.toUint64(block.timestamp),\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n // Store the snapped timestamp\n lastSnapTimestamp = SafeCast.toUint64(block.timestamp);\n\n emit BalancesSnapped(block.timestamp, blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct VerifyBalancesParams {\n bytes32 blockRoot;\n uint64 firstPendingDepositSlot;\n // BeaconBlock.BeaconBlockBody.deposits[0].slot\n bytes firstPendingDepositSlotProof;\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n /// @param params a `VerifyBalancesParams` struct containing the following:\n /// blockRoot - the beacon block root emitted from `snapBalance` in `BalancesSnapped`\n /// firstPendingDepositSlot - The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero would be a good choice.\n /// firstPendingDepositSlotProof - The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// balancesContainerRoot - the merkle root of the balances container\n /// balancesContainerProof - The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// validatorBalanceLeaves - Array of leaf nodes containing the validator balance with three other balances.\n /// validatorBalanceProofs - Array of merkle proofs for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(VerifyBalancesParams calldata params) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalances[params.blockRoot];\n // Check the balances are the latest\n require(lastSnapTimestamp > 0, \"No snapped balances\");\n require(balancesMem.timestamp == lastSnapTimestamp, \"Stale snap\");\n\n uint256 depositsCount = depositsRoots.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit to the beacon block root\n bool isEmptyDepositQueue = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDepositSlot(\n params.blockRoot,\n params.firstPendingDepositSlot,\n params.firstPendingDepositSlotProof\n );\n\n // If there are no deposits in the beacon chain queue then our deposits must have been processed.\n // If the deposits have been processed, each deposit will need to be verified with `verifyDeposit`\n require(!isEmptyDepositQueue, \"Deposits have been processed\");\n // If a validator is converted from a sweeping validator to a compounding validator, any balance in excess\n // of the min 32 ETH is put in the pending deposit queue. This will have a slot value of zero unfortunately.\n // We can not prove the strategy's deposits are still pending with a zero slot value so revert the tx.\n // Another snapBalances will need to be taken that does not have consolidation deposits at the front of the\n // beacon chain deposit queue.\n require(\n params.firstPendingDepositSlot > 0,\n \"Invalid first pending deposit\"\n );\n\n // For each staking strategy's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n bytes32 depositDataRoot = depositsRoots[i];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit slot is before the\n // slot of the staking strategy's deposit.\n // If the deposit has been processed, it will need to be verified with `verifyDeposit`\n require(\n params.firstPendingDepositSlot <\n deposits[depositDataRoot].slot,\n \"Deposit has been processed\"\n );\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei +=\n uint256(deposits[depositDataRoot].amountGwei) *\n 1 gwei;\n }\n }\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n params.validatorBalanceProofs.length == verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n params.validatorBalanceLeaves.length == verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n params.blockRoot,\n params.balancesContainerRoot,\n params.balancesContainerProof\n );\n\n // for each validator in reserve order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n params.balancesContainerRoot,\n params.validatorBalanceLeaves[i],\n params.validatorBalanceProofs[i],\n verifiedValidators[i].index\n );\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n validatorState[\n verifiedValidators[i].pubKeyHash\n ] = VALIDATOR_STATE.EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += uint256(validatorBalanceGwei) * 1 gwei;\n }\n }\n\n uint256 wethBalance = IWETH9(WETH).balanceOf(address(this));\n\n // Store the verified balance in storage\n lastVerifiedEthBalance = SafeCast.toUint128(\n totalDepositsWei + totalValidatorBalance + balancesMem.ethBalance\n );\n // Reset the last snap timestamp so a new snapBalances has to be made\n lastSnapTimestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n wethBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /***************************************\n WETH and ETH Accounting\n ****************************************/\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance\n if (lastVerifiedEthBalance > _ethAmount) {\n lastVerifiedEthBalance -= SafeCast.toUint128(_ethAmount);\n } else {\n // This can happen if all ETH in the validators was withdrawn\n // and there was more consensus rewards since the last balance verification.\n // Or it can happen if ETH is donated to this strategy.\n lastVerifiedEthBalance = 0;\n }\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += SafeCast.toUint128(_wethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /***************************************\n View Functions\n ****************************************/\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return An array of `ValidatorData` containing the public key hash and validator index.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorData[] memory)\n {\n return verifiedValidators;\n }\n\n struct DepositView {\n bytes32 root;\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n }\n\n /// @notice Returns the deposits that are still to be verified.\n /// These may or may not have been processed by the beacon chain.\n /// @return pendingDeposits An array of `DepositView` containing the deposit root, public key hash,\n /// amount in Gwei and the slot of the deposit.\n function getPendingDeposits()\n external\n view\n returns (DepositView[] memory pendingDeposits)\n {\n pendingDeposits = new DepositView[](depositsRoots.length);\n for (uint256 i = 0; i < depositsRoots.length; ++i) {\n DepositData memory deposit = deposits[depositsRoots[i]];\n pendingDeposits[i] = DepositView({\n root: depositsRoots[i],\n pubKeyHash: deposit.pubKeyHash,\n amountGwei: deposit.amountGwei,\n slot: deposit.slot\n });\n }\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/7db9dc6a86e3e674de8d1c89071004cf.json b/contracts/deployments/hoodi/solcInputs/7db9dc6a86e3e674de8d1c89071004cf.json new file mode 100644 index 0000000000..cd619eb74e --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/7db9dc6a86e3e674de8d1c89071004cf.json @@ -0,0 +1,111 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/security/Pausable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the Beacon Block Root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Block Root Oracle to get the parent block root\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconOracle {\n function blockToSlot(uint64 blockNumber) external returns (uint64 slot);\n\n function slotToBlock(uint64 slot) external returns (uint64 blockNumber);\n\n function slotToRoot(uint64 slot) external returns (bytes32 blockRoot);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n enum BalanceProofLevel {\n Container,\n BeaconBlock\n }\n\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 root,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex,\n BalanceProofLevel level\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view;\n\n function verifyBlockNumber(\n bytes32 beaconBlockRoot,\n uint256 blockNumber,\n bytes calldata blockNumberProof\n ) external view;\n\n function verifySlot(\n bytes32 beaconBlockRoot,\n uint256 slot,\n bytes calldata slotProof\n ) external view;\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n external\n pure\n returns (uint256);\n\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) external pure returns (uint256);\n}\n" + }, + "contracts/interfaces/IConsolidation.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IConsolidationSource {\n function confirmConsolidation()\n external\n returns (uint256 consolidationCount);\n}\n\ninterface IConsolidationTarget {\n function requestConsolidation(\n bytes32 lastSourcePubKeyHash,\n bytes32 targetPubKeyHash\n ) external;\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconOracle Address of the Beacon Oracle contract that maps block numbers to slots\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconOracle,\n address _beaconProofs\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconOracle,\n _beaconProofs\n )\n {\n SSV_TOKEN = _ssvToken;\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract.\n /// @param _rewardTokenAddresses Address of reward token for platform\n /// @param _assets Addresses of initial supported assets\n /// @param _pTokens Platform Token corresponding addresses\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused for validator consolidation.\n /// @param _asset Address of asset to deposit. Has to be WETH.\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused for validator consolidation.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// Will revert if the strategy is paused for validator consolidation.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant whenNotPaused {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _asset, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(_asset, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n /// Will revert if the strategy is paused for validator consolidation.\n function withdrawAll()\n external\n override\n onlyVaultOrGovernor\n nonReentrant\n whenNotPaused\n {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, WETH, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Returns the last verified balance of validator deposits, validator balance,\n /// WETH and ETH in the strategy contract.\n /// @param _asset Address of weth asset\n /// @return balance Total value of (W)ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by strategy.\n /// @param _asset The address of the asset token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for deposits\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instread of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract,\n /// but so does withdrawals from validators. It's too complex to separate the two\n /// so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IConsolidationSource } from \"../../interfaces/IConsolidation.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\nimport { IBeaconOracle } from \"../../interfaces/IBeaconOracle.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit, remove and consolidate validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable, Pausable {\n using SafeERC20 for IERC20;\n\n /// @notice The amount of ETH in wei that is required for a deposit to a new validator.\n /// Initially this is 32 ETH, but will be increased to 1 ETH after P2P's APIs have been updated\n /// to support deposits of 1 ETH.\n uint256 public constant DEPOSIT_AMOUNT_WEI = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Oracle contract that maps block numbers to slots\n address public immutable BEACON_ORACLE;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// Deposit data for new compounding validators.\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountWei Amount of ETH in wei that has been deposited to the beacon chain deposit contract\n /// @param blockNumber Block number when the deposit was made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either PENDING or PROVEN\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 blockNumber;\n uint32 depositIndex;\n DepositStatus status;\n }\n /// @notice Mapping of the root of a deposit (depositDataRoot) to its data\n mapping(bytes32 => DepositData) public deposits;\n /// @notice List of deposit roots that are still to be verified as processed on the beacon chain\n bytes32[] public depositsRoots;\n\n // Validator data\n\n struct ValidatorData {\n bytes32 pubKeyHash; // Hash of the validator's public key using the Beacon Chain's format\n uint64 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes and indexes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n ValidatorData[] internal verifiedValidators;\n /// @notice State of the new compounding validators with a 0x02 withdrawal credential prefix.\n /// Uses the Beacon chain hashing for BLSPubkey which is\n /// sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => VALIDATOR_STATE) public validatorState;\n\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n uint64 timestamp;\n uint128 ethBalance;\n }\n // TODO is it more efficient to use the block root rather than hashing it?\n /// @notice Mapping of the block root to the balances at that slot\n mapping(bytes32 => Balances) public snappedBalances;\n uint64 public lastSnapTimestamp;\n uint128 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n bytes32 public consolidationLastPubKeyHash;\n address public consolidationSourceStrategy;\n mapping(address => bool) public consolidationSourceStrategies;\n\n // For future use\n uint256[50] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITED, // The validator has been verified to have a zero balance\n REMOVED // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified and is ready to be staked\n }\n\n event RegistratorChanged(address indexed newAddress);\n event SourceStrategyAdded(address indexed strategy);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n bytes32 indexed depositDataRoot,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint64 indexed validatorIndex\n );\n event DepositVerified(bytes32 indexed depositDataRoot, uint256 amountWei);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(\n uint256 indexed timestamp,\n bytes32 indexed blockRoot,\n uint256 ethBalance\n );\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 wethBalance,\n uint256 ethBalance\n );\n event ConsolidationRequested(\n bytes32 indexed lastSourcePubKeyHash,\n bytes32 indexed targetPubKeyHash,\n address indexed sourceStrategy\n );\n event ConsolidationVerified(\n bytes32 indexed lastSourcePubKeyHash,\n uint64 indexed lastValidatorIndex,\n uint256 consolidationCount\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconOracle Address of the Beacon Oracle contract that maps block numbers to slots\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconOracle,\n address _beaconProofs\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_ORACLE = _beaconOracle;\n BEACON_PROOFS = _beaconProofs;\n }\n\n /***************************************\n Admin Functions\n ****************************************/\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Adds support for a legacy staking strategy that can be used for consolidation.\n function addSourceStrategy(address _strategy) external onlyGovernor {\n consolidationSourceStrategies[_strategy] = true;\n\n emit SourceStrategyAdded(_strategy);\n }\n\n /***************************************`\n Validator Management\n ****************************************/\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validatorState[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validator validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validator,\n uint64 depositAmountGwei\n ) external onlyRegistrator whenNotPaused {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validator.pubkey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == VALIDATOR_STATE.REGISTERED ||\n currentState == VALIDATOR_STATE.VERIFIED),\n \"Not registered or verified\"\n );\n require(\n currentState == VALIDATOR_STATE.VERIFIED ||\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validator.pubkey,\n withdrawalCredentials,\n validator.signature,\n validator.depositDataRoot\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == VALIDATOR_STATE.REGISTERED) {\n validatorState[pubKeyHash] = VALIDATOR_STATE.STAKED;\n }\n // Store the deposit data for verifyDeposit and verifyBalances\n deposits[validator.depositDataRoot] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n blockNumber: SafeCast.toUint64(block.number),\n depositIndex: SafeCast.toUint32(depositsRoots.length),\n status: DepositStatus.PENDING\n });\n depositsRoots.push(validator.depositDataRoot);\n\n emit ETHStaked(\n pubKeyHash,\n validator.depositDataRoot,\n validator.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then the request will fail.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n onlyRegistrator\n whenNotPaused\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove a sweeping validator from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == VALIDATOR_STATE.EXITED ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exited\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validatorState[pubKeyHash] = VALIDATOR_STATE.REMOVED;\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /// @notice Receives requests from supported legacy strategies to consolidate sweeping validators to\n /// a new compounding validator on this new strategy.\n /// @param lastSourcePubKeyHash The last source validator to be consolidated hashed using the Beacon Chain's format.\n /// @param targetPubKeyHash The target validator's public key hash using the Beacon Chain's format.\n function requestConsolidation(\n bytes32 lastSourcePubKeyHash,\n bytes32 targetPubKeyHash\n ) external whenNotPaused {\n require(\n consolidationSourceStrategies[msg.sender],\n \"Not a source strategy\"\n );\n\n // The target validator must be a compounding validator that has been verified\n require(\n validatorState[targetPubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Target validator not verified\"\n );\n\n // Ensure balances can not be verified until after the consolidation has been verified.\n lastSnapTimestamp = 0;\n\n // Store consolidation state\n consolidationLastPubKeyHash = lastSourcePubKeyHash;\n consolidationSourceStrategy = msg.sender;\n\n // Pause the strategy while the consolidation is in progress\n _pause();\n\n emit ConsolidationRequested(\n lastSourcePubKeyHash,\n targetPubKeyHash,\n msg.sender\n );\n }\n\n /***************************************\n SSV Management\n ****************************************/\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n Beacon Chain Proofs\n ****************************************/\n\n /// @notice Verifies a validator's index to its public key.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param validatorPubKeyProof The merkle proof that the validator's index matches its public key\n /// on the beacon chain.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.STAKED,\n \"Validator not staked\"\n );\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key\n IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n address(this) // Withdrawal address is this strategy\n );\n\n // TODO verify the validator's withdrawal credential points to this strategy\n\n // Store the validator state as verified\n validatorState[pubKeyHash] = VALIDATOR_STATE.VERIFIED;\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(\n ValidatorData({ pubKeyHash: pubKeyHash, index: validatorIndex })\n );\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n /// @param depositDataRoot The root of the deposit data that was stored when\n /// the deposit was made on the execution layer.\n /// @param depositBlockNumber A block number that is on or after the block the deposit\n /// was made on the execution layer.\n /// @param processedSlot Any slot on or after the deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// @param firstPendingDepositSlot The slot of the first pending deposit in the beacon chain\n /// @param firstPendingDepositSlotProof The merkle proof that the slot of the first pending deposit\n /// matches the beacon chain.\n /// BeaconBlock.BeaconBlockBody.deposits[0].slot\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n bytes32 depositDataRoot,\n uint64 depositBlockNumber,\n uint64 processedSlot,\n uint64 firstPendingDepositSlot,\n bytes calldata firstPendingDepositSlotProof\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositDataRoot];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n validatorState[deposit.pubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n // The deposit block number mapped to a slot needs to be the same block or after\n // the deposit in `stakeETH` was created on the execution layer.\n require(\n deposit.blockNumber <= depositBlockNumber,\n \"Deposit block before deposit\"\n );\n\n // Get the slot that is on or after the deposit was made on the execution layer.\n uint64 depositSlot = IBeaconOracle(BEACON_ORACLE).blockToSlot(\n depositBlockNumber\n );\n\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the first pending deposit's slot is 0 then there are no pending deposits so\n // our deposit must have been processed on the beacon chain.\n require(\n depositSlot < firstPendingDepositSlot ||\n firstPendingDepositSlot == 0,\n \"Deposit not processed\"\n );\n\n // The slot of the execution layer deposit must be before\n // the slot that the deposit was processed on the beacon chain.\n require(depositSlot < processedSlot, \"Slot not after deposit\");\n\n // Get the beacon block root for the slot that is on or after the deposit was processed on the beacon chain.\n bytes32 blockRoot = IBeaconOracle(BEACON_ORACLE).slotToRoot(\n processedSlot\n );\n\n // Verify the slot of the first pending deposit matches the beacon chain\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n blockRoot,\n firstPendingDepositSlot,\n firstPendingDepositSlotProof\n );\n\n // After verifying the proof, update the contract storage\n deposits[depositDataRoot].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n bytes32 lastDepositDataRoot = depositsRoots[depositsRoots.length - 1];\n depositsRoots[deposit.depositIndex] = lastDepositDataRoot;\n deposits[lastDepositDataRoot].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositsRoots.pop();\n\n emit DepositVerified(\n depositDataRoot,\n uint256(deposit.amountGwei) * 1 gwei\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n // TODO what if the last validator was exited rather than consolidated?\n // slither-disable-start reentrancy-no-eth\n // function verifyConsolidation(\n // uint64 parentBlockTimestamp,\n // uint64 lastValidatorIndex,\n // bytes calldata validatorPubKeyProof,\n // bytes32 balancesLeaf,\n // bytes calldata validatorBalanceProof\n // ) external onlyRegistrator {\n // bytes32 consolidationLastPubKeyHashMem = consolidationLastPubKeyHash;\n // require(\n // consolidationLastPubKeyHashMem != bytes32(0),\n // \"No consolidations\"\n // );\n\n // bytes32 blockRoot = BeaconRoots.parentBlockRoot(parentBlockTimestamp);\n // // Verify the validator index has the same public key as the last source validator\n // IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n // blockRoot,\n // consolidationLastPubKeyHashMem,\n // validatorPubKeyProof,\n // lastValidatorIndex,\n // address(this) // Withdrawal address is this strategy\n // );\n\n // // Verify the balance of the last validator in the consolidation batch\n // // is zero. If its not then the consolidation has not been completed.\n // // This proof is to the beacon block root, not the balances container root.\n // uint256 validatorBalance = IBeaconProofs(BEACON_PROOFS)\n // .verifyValidatorBalance(\n // blockRoot,\n // balancesLeaf,\n // validatorBalanceProof,\n // lastValidatorIndex,\n // IBeaconProofs.BalanceProofLevel.BeaconBlock\n // );\n // require(validatorBalance == 0, \"Last validator balance not zero\");\n\n // // Call the old sweeping strategy to confirm the consolidation has been completed.\n // // This will decrease the balance of the source strategy by 32 ETH for each validator being consolidated.\n // uint256 consolidationCount = IConsolidationSource(\n // consolidationSourceStrategy\n // ).confirmConsolidation();\n\n // // Increase the balance of this strategy by 32 ETH for each validator being consolidated.\n // // This nets out the decrease in the source strategy's balance.\n // lastVerifiedEthBalance += SafeCast.toUint128(\n // consolidationCount * 32 ether\n // );\n\n // // Reset the stored consolidation state\n // consolidationLastPubKeyHash = bytes32(0);\n // consolidationSourceStrategy = address(0);\n\n // emit ConsolidationVerified(\n // consolidationLastPubKeyHashMem,\n // lastValidatorIndex,\n // consolidationCount\n // );\n\n // // Unpause now the balance of the target validator has been verified\n // _unpause();\n\n // // Take a snap of the balances so the actual balances of the new validator balances can be verified\n // _snapBalances();\n // }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block.\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n /// Can only be called by the registrator.\n /// Can not be called while a consolidation is in progress.\n function snapBalances() external whenNotPaused onlyRegistrator {\n _snapBalances();\n }\n\n function _snapBalances() internal {\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(\n SafeCast.toUint64(block.timestamp)\n );\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the balances in the mapping\n snappedBalances[blockRoot] = Balances({\n timestamp: SafeCast.toUint64(block.timestamp),\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n // Store the snapped timestamp\n lastSnapTimestamp = SafeCast.toUint64(block.timestamp);\n\n emit BalancesSnapped(block.timestamp, blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct VerifyBalancesParams {\n bytes32 blockRoot;\n uint64 mappedDepositSlot;\n uint64 firstPendingDepositSlot;\n // BeaconBlock.BeaconBlockBody.deposits[0].slot\n bytes firstPendingDepositSlotProof;\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(VerifyBalancesParams calldata params) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalances[params.blockRoot];\n // Check the balances are the latest\n require(lastSnapTimestamp > 0, \"No snapped balances\");\n require(balancesMem.timestamp == lastSnapTimestamp, \"Stale snap\");\n\n uint256 depositsCount = depositsRoots.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit to the beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n params.blockRoot,\n params.firstPendingDepositSlot,\n params.firstPendingDepositSlotProof\n );\n\n // As the probably don't have the slot of the first pending deposit mapped to a block in the BeaconOracle,\n // we use any slot that is on or before the slot of the first pending deposit.\n require(\n params.mappedDepositSlot <= params.firstPendingDepositSlot,\n \"Invalid deposit slot\"\n );\n uint64 firstPendingDepositBlockNumber = IBeaconOracle(BEACON_ORACLE)\n .slotToBlock(params.mappedDepositSlot);\n\n // For each native staking contract's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n bytes32 depositDataRoot = depositsRoots[i];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit block number is before the\n // block number of the staking strategy's deposit.\n // If it has it will need to be verified with `verifyDeposit`\n require(\n firstPendingDepositBlockNumber <\n deposits[depositDataRoot].blockNumber,\n \"Deposit has been processed\"\n );\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei +=\n uint256(deposits[depositDataRoot].amountGwei) *\n 1 gwei;\n }\n }\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n params.validatorBalanceProofs.length == verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n params.validatorBalanceLeaves.length == verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n params.blockRoot,\n params.balancesContainerRoot,\n params.balancesContainerProof\n );\n\n // for each validator in reserve order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n params.balancesContainerRoot,\n params.validatorBalanceLeaves[i],\n params.validatorBalanceProofs[i],\n verifiedValidators[i].index,\n IBeaconProofs.BalanceProofLevel.Container\n );\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n validatorState[\n verifiedValidators[i].pubKeyHash\n ] = VALIDATOR_STATE.EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += uint256(validatorBalanceGwei) * 1 gwei;\n }\n }\n\n uint256 wethBalance = IWETH9(WETH).balanceOf(address(this));\n\n // Store the verified balance in storage\n lastVerifiedEthBalance = SafeCast.toUint128(\n totalDepositsWei + totalValidatorBalance + balancesMem.ethBalance\n );\n // Reset the last snap timestamp so a new snapBalances has to be made\n lastSnapTimestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n wethBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /***************************************\n WETH and ETH Accounting\n ****************************************/\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance\n if (lastVerifiedEthBalance > _ethAmount) {\n lastVerifiedEthBalance -= SafeCast.toUint128(_ethAmount);\n } else {\n // This can happen if all ETH in the validators was withdrawn\n // and there was more consensus rewards since the last balance verification.\n // Or it can happen if ETH is donated to this strategy.\n lastVerifiedEthBalance = 0;\n }\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += SafeCast.toUint128(_wethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /***************************************\n View Functions\n ****************************************/\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return An array of `ValidatorData` containing the public key hash and validator index.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorData[] memory)\n {\n return verifiedValidators;\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/7de90067c6ddbeeea4788e7c02ed9ce8.json b/contracts/deployments/hoodi/solcInputs/7de90067c6ddbeeea4788e7c02ed9ce8.json new file mode 100644 index 0000000000..71f3dcaa37 --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/7de90067c6ddbeeea4788e7c02ed9ce8.json @@ -0,0 +1,126 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/security/Pausable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/beacon/BeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"./BeaconProofsLib.sol\";\nimport { IBeaconProofs } from \"../interfaces/IBeaconProofs.sol\";\n\n/**\n * @title Verifies merkle proofs of beacon chain data.\n * @author Origin Protocol Inc\n */\ncontract BeaconProofs is IBeaconProofs {\n /// @notice Verifies the validator index is for the given validator public key.\n /// Also verify the validator's withdrawal credential points to the withdrawal address.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param proof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalCredentials a value containing the validator type and withdrawal address.\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata proof,\n uint40 validatorIndex,\n bytes32 withdrawalCredentials\n ) external view {\n BeaconProofsLib.verifyValidator(\n beaconBlockRoot,\n pubKeyHash,\n proof,\n validatorIndex,\n withdrawalCredentials\n );\n }\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view {\n BeaconProofsLib.verifyValidatorWithdrawableEpoch(\n beaconBlockRoot,\n validatorIndex,\n withdrawableEpoch,\n withdrawableEpochProof\n );\n }\n\n /// @notice Verifies the balances container to the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerRoot The merkle root of the the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata balancesContainerProof\n ) external view {\n BeaconProofsLib.verifyBalancesContainer(\n beaconBlockRoot,\n balancesContainerRoot,\n balancesContainerProof\n );\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param balanceProof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint40 validatorIndex\n ) external view returns (uint256 validatorBalanceGwei) {\n validatorBalanceGwei = BeaconProofsLib.verifyValidatorBalance(\n balancesContainerRoot,\n validatorBalanceLeaf,\n balanceProof,\n validatorIndex\n );\n }\n\n /// @notice Verifies the pending deposits container to the beacon block root.\n /// BeaconBlock.state.pendingDeposits\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param pendingDepositsContainerRoot The merkle root of the the pending deposits container.\n /// @param proof The merkle proof for the pending deposits container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyPendingDepositsContainer(\n bytes32 beaconBlockRoot,\n bytes32 pendingDepositsContainerRoot,\n bytes calldata proof\n ) external view {\n BeaconProofsLib.verifyPendingDepositsContainer(\n beaconBlockRoot,\n pendingDepositsContainerRoot,\n proof\n );\n }\n\n /// @notice Verified a pending deposit to the root of the Pending Deposits container.\n /// @param pendingDepositsContainerRoot The merkle root of the Pending Deposits container.\n /// @param pendingDepositRoot The leaf node containing the validator balance with three other balances.\n /// @param proof The merkle proof for the pending deposit root to the Pending Deposits container root.\n /// This is 28 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param pendingDepositIndex The pending deposit index in the Pending Deposits container\n function verifyPendingDeposit(\n bytes32 pendingDepositsContainerRoot,\n bytes32 pendingDepositRoot,\n bytes calldata proof,\n uint32 pendingDepositIndex\n ) external view {\n BeaconProofsLib.verifyPendingDeposit(\n pendingDepositsContainerRoot,\n pendingDepositRoot,\n proof,\n pendingDepositIndex\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the slot of the first pending deposit to the beacon block root.\n /// BeaconBlock.state.pendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty.\n /// @param firstPendingDepositSlotProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view returns (bool isEmptyDepositQueue) {\n isEmptyDepositQueue = BeaconProofsLib.verifyFirstPendingDeposit(\n beaconBlockRoot,\n slot,\n firstPendingDepositSlotProof\n );\n }\n\n /// @notice Merkleizes a beacon chain pending deposit.\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param withdrawalCredentials The 32 byte withdrawal credentials.\n /// @param amountGwei The amount of the deposit in Gwei.\n /// @param signature The 96 byte BLS signature.\n /// @param slot The beacon chain slot the deposit was made in.\n /// @return root The merkle root of the pending deposit.\n function merkleizePendingDeposit(\n bytes32 pubKeyHash,\n bytes calldata withdrawalCredentials,\n uint64 amountGwei,\n bytes calldata signature,\n uint64 slot\n ) external pure returns (bytes32) {\n return\n BeaconProofsLib.merkleizePendingDeposit(\n pubKeyHash,\n withdrawalCredentials,\n amountGwei,\n signature,\n slot\n );\n }\n\n /// @notice Merkleizes a BLS signature used for validator deposits.\n /// @param signature The 96 byte BLS signature.\n /// @return root The merkle root of the signature.\n function merkleizeSignature(bytes calldata signature)\n external\n pure\n returns (bytes32 root)\n {\n return BeaconProofsLib.merkleizeSignature(signature);\n }\n}\n" + }, + "contracts/beacon/BeaconProofsLib.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Merkle } from \"./Merkle.sol\";\nimport { Endian } from \"./Endian.sol\";\n\n/**\n * @title Library to verify merkle proofs of beacon chain data.\n * @author Origin Protocol Inc\n */\nlibrary BeaconProofsLib {\n // Known generalized indices in the beacon block\n /// @dev BeaconBlock.state.PendingDeposits[0]\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, pending deposits at index 34\n /// Pending deposits container: height 28, first deposit at index 0\n /// ((2 ^ 3 + 3) * 2 ^ 6 + 34) * 2 ^ 28 + 0 = 198105366528\n uint256 internal constant FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX =\n 198105366528;\n /// @dev BeaconBlock.state.PendingDeposits[0].pubkey\n /// Pending Deposit container: height 3, pubkey at index 4\n /// (((2 ^ 3 + 3) * 2 ^ 6 + 34) * 2 ^ 28 + 0) * 2 ^ 3 + 4 = 1584842932228\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX =\n 1584842932228;\n /// @dev BeaconBlock.state.validators\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, validators at index 11\n /// (2 ^ 3 + 3) * 2 ^ 6 + 11 = 715\n uint256 internal constant VALIDATORS_CONTAINER_GENERALIZED_INDEX = 715;\n /// @dev BeaconBlock.state.balances\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, balances at index 12\n /// (2 ^ 3 + 3) * 2 ^ 6 + 12 = 716\n uint256 internal constant BALANCES_CONTAINER_GENERALIZED_INDEX = 716;\n\n /// @dev BeaconBlock.state.pendingDeposits\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, balances at index 34\n /// (2 ^ 3 + 3) * 2 ^ 6 + 34 = 738\n uint256 internal constant PENDING_DEPOSITS_CONTAINER_GENERALIZED_INDEX =\n 738;\n\n /// @dev Number of bytes in the proof to the first pending deposit.\n /// 37 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0]\n /// 37 * 32 bytes = 1184 bytes\n uint256 internal constant FIRST_PENDING_DEPOSIT_PROOF_LENGTH = 1184;\n /// @dev Number of bytes in the proof from the slot of the first pending deposit to the beacon block root.\n /// 40 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0].slot\n /// 40 * 32 bytes = 1280 bytes\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_PROOF_LENGTH = 1280;\n\n /// @dev Merkle height of the Balances container\n /// BeaconBlock.state.balances\n uint256 internal constant BALANCES_HEIGHT = 39;\n /// @dev Merkle height of the Validators container list\n /// BeaconBlock.state.validators\n uint256 internal constant VALIDATORS_LIST_HEIGHT = 41;\n /// @dev Merkle height of the Pending Deposits container list\n /// BeaconBlock.state.pendingDeposits\n uint256 internal constant PENDING_DEPOSITS_LIST_HEIGHT = 28;\n /// @dev Merkle height of the Validator container\n /// BeaconBlock.state.validators[validatorIndex]\n uint256 internal constant VALIDATOR_CONTAINER_HEIGHT = 3;\n\n /// @dev Position of the pubkey field in the Validator container.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;\n /// @dev Position of the withdrawable epoch field in the Validator container.\n /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n uint256 internal constant VALIDATOR_WITHDRAWABLE_EPOCH_INDEX = 7;\n\n /// @notice Verifies the validator index is for the given validator public key.\n /// Also verify the validator's withdrawal credential points to the withdrawal address.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param proof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalCredentials a value containing the validator type and withdrawal address.\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata proof,\n uint40 validatorIndex,\n bytes32 withdrawalCredentials\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.validators[validatorIndex]\n uint256 generalizedIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_LIST_HEIGHT,\n validatorIndex\n );\n // BeaconBlock.state.validators[validatorIndex].pubkey\n generalizedIndex = concatGenIndices(\n generalizedIndex,\n VALIDATOR_CONTAINER_HEIGHT,\n VALIDATOR_PUBKEY_INDEX\n );\n\n // Get the withdrawal credentials from the first witness in the pubkey merkle proof.\n bytes32 withdrawalCredentialsFromProof = bytes32(proof[:32]);\n\n require(\n withdrawalCredentialsFromProof == withdrawalCredentials,\n \"Invalid withdrawal cred\"\n );\n\n require(\n // 53 * 32 bytes = 1696 bytes\n proof.length == 1696 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: pubKeyHash,\n index: generalizedIndex\n }),\n \"Invalid validator proof\"\n );\n }\n\n /// @notice Verifies a validator's withdrawable epoch to the beacon block root\n /// for a given validator index.\n /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n /// @param beaconBlockRoot The root of the beacon block\n /// @param validatorIndex The validator index to verify the withdrawable epoch for.\n /// @param withdrawableEpoch The withdrawable epoch to verify in big endian uint64 format\n /// @param proof The merkle proof for the validator's withdrawable epoch to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyValidatorWithdrawableEpoch(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata proof\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.validators[validatorIndex]\n uint256 exitEpochGenIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_LIST_HEIGHT,\n validatorIndex\n );\n // BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n exitEpochGenIndex = concatGenIndices(\n exitEpochGenIndex,\n VALIDATOR_CONTAINER_HEIGHT,\n VALIDATOR_WITHDRAWABLE_EPOCH_INDEX\n );\n\n require(\n // 53 * 32 bytes = 1696 bytes\n proof.length == 1696 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: Endian.toLittleEndianUint64(withdrawableEpoch),\n index: exitEpochGenIndex\n }),\n \"Invalid withdrawable proof\"\n );\n }\n\n /// @notice Verifies the balances container to the beacon block root.\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param balancesContainerRoot The merkle root of the the balances container.\n /// @param proof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata proof\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.balances\n require(\n // 9 * 32 bytes = 288 bytes\n proof.length == 288 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: balancesContainerRoot,\n index: BALANCES_CONTAINER_GENERALIZED_INDEX\n }),\n \"Invalid balance container proof\"\n );\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param proof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for.\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index.\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata proof,\n uint40 validatorIndex\n ) internal view returns (uint256 validatorBalanceGwei) {\n require(balancesContainerRoot != bytes32(0), \"Invalid container root\");\n\n // Four balances are stored in each leaf so the validator index is divided by 4\n uint64 balanceIndex = validatorIndex / 4;\n\n // Get the index within the balances container, not the Beacon Block\n // BeaconBlock.state.balances[balanceIndex]\n uint256 generalizedIndex = concatGenIndices(\n 1,\n BALANCES_HEIGHT,\n balanceIndex\n );\n\n validatorBalanceGwei = balanceAtIndex(\n validatorBalanceLeaf,\n validatorIndex\n );\n\n require(\n // 39 * 32 bytes = 1248 bytes\n proof.length == 1248 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: balancesContainerRoot,\n leaf: validatorBalanceLeaf,\n index: generalizedIndex\n }),\n \"Invalid balance proof\"\n );\n }\n\n /// @notice Verifies the pending deposits container to the beacon block root.\n /// BeaconBlock.state.pendingDeposits\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param pendingDepositsContainerRoot The merkle root of the the pending deposits container.\n /// @param proof The merkle proof for the pending deposits container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyPendingDepositsContainer(\n bytes32 beaconBlockRoot,\n bytes32 pendingDepositsContainerRoot,\n bytes calldata proof\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.pendingDeposits\n require(\n // 9 * 32 bytes = 288 bytes\n proof.length == 288 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: pendingDepositsContainerRoot,\n index: PENDING_DEPOSITS_CONTAINER_GENERALIZED_INDEX\n }),\n \"Invalid deposit container proof\"\n );\n }\n\n /// @notice Verifies a pending deposit in the pending deposits container.\n /// BeaconBlock.state.pendingDeposits[depositIndex]\n /// @param pendingDepositsContainerRoot The merkle root of the pending deposits list container\n /// @param pendingDepositRoot The merkle root of the pending deposit to verify\n /// @param proof The merkle proof for the pending deposit root to the pending deposits list container root.\n /// This is 28 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param pendingDepositIndex The index in the pending deposits list container for the deposit to verify.\n function verifyPendingDeposit(\n bytes32 pendingDepositsContainerRoot,\n bytes32 pendingDepositRoot,\n bytes calldata proof,\n uint32 pendingDepositIndex\n ) internal view {\n require(pendingDepositsContainerRoot != bytes32(0), \"Invalid root\");\n // ssz-merkleizing a list which has a variable length, an additional\n // sha256(pending_deposits_root, pending_deposits_length) operation is done to get the\n // actual pending deposits root so the max pending deposit index is 2^(28 - 1)\n require(\n pendingDepositIndex < 2**(PENDING_DEPOSITS_LIST_HEIGHT - 1),\n \"Invalid deposit index\"\n );\n\n // BeaconBlock.state.pendingDeposits[depositIndex]\n uint256 generalizedIndex = concatGenIndices(\n 1,\n PENDING_DEPOSITS_LIST_HEIGHT,\n pendingDepositIndex\n );\n\n require(\n // 28 * 32 bytes = 896 bytes\n proof.length == 896 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: pendingDepositsContainerRoot,\n leaf: pendingDepositRoot,\n index: generalizedIndex\n }),\n \"Invalid deposit proof\"\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the slot of the first pending deposit to the beacon block root.\n /// BeaconBlock.state.pendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty.\n /// @param proof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata proof\n ) internal view returns (bool isEmptyDepositQueue) {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // If the deposit queue is empty\n if (proof.length == FIRST_PENDING_DEPOSIT_PROOF_LENGTH) {\n require(\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: bytes32(0),\n index: FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX\n }),\n \"Invalid empty deposits proof\"\n );\n return true;\n }\n\n // Verify the public key of the first pending deposit\n // BeaconBlock.state.PendingDeposits[0].slot\n require(\n proof.length == FIRST_PENDING_DEPOSIT_SLOT_PROOF_LENGTH &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: Endian.toLittleEndianUint64(slot),\n index: FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX\n }),\n \"Invalid deposit slot proof\"\n );\n }\n\n /// @notice Merkleizes a beacon chain pending deposit.\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param withdrawalCredentials The 32 byte withdrawal credentials.\n /// @param amountGwei The amount of the deposit in Gwei.\n /// @param signature The 96 byte BLS signature.\n /// @param slot The beacon chain slot the deposit was made in.\n /// @return root The merkle root of the pending deposit.\n function merkleizePendingDeposit(\n bytes32 pubKeyHash,\n bytes calldata withdrawalCredentials,\n uint64 amountGwei,\n bytes calldata signature,\n uint64 slot\n ) internal pure returns (bytes32 root) {\n bytes32[] memory leaves = new bytes32[](8);\n leaves[0] = pubKeyHash;\n leaves[1] = bytes32(withdrawalCredentials[:32]);\n leaves[2] = Endian.toLittleEndianUint64(amountGwei);\n leaves[3] = merkleizeSignature(signature);\n leaves[4] = Endian.toLittleEndianUint64(slot);\n leaves[5] = bytes32(0);\n leaves[6] = bytes32(0);\n leaves[7] = bytes32(0);\n\n root = Merkle.merkleizeSha256(leaves);\n }\n\n /// @notice Merkleizes a BLS signature used for validator deposits.\n /// @param signature The 96 byte BLS signature.\n /// @return root The merkle root of the signature.\n function merkleizeSignature(bytes calldata signature)\n internal\n pure\n returns (bytes32)\n {\n require(signature.length == 96, \"Invalid signature\");\n\n bytes32[] memory leaves = new bytes32[](4);\n leaves[0] = bytes32(signature[:32]);\n leaves[1] = bytes32(signature[32:64]);\n leaves[2] = bytes32(signature[64:96]);\n leaves[3] = bytes32(0);\n\n return Merkle.merkleizeSha256(leaves);\n }\n\n ////////////////////////////////////////////////////\n /// Internal Helper Functions\n ////////////////////////////////////////////////////\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint40 validatorIndex)\n internal\n pure\n returns (uint256)\n {\n uint256 bitShiftAmount = (validatorIndex % 4) * 64;\n return\n Endian.fromLittleEndianUint64(\n bytes32((uint256(validatorBalanceLeaf) << bitShiftAmount))\n );\n }\n\n /// @notice Concatenates two beacon chain generalized indices into one.\n /// @param genIndex The first generalized index or 1 if calculating for a single container.\n /// @param height The merkle tree height of the second container. eg 39 for balances, 41 for validators.\n /// @param index The index within the second container. eg the validator index.\n /// @return genIndex The concatenated generalized index.\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) internal pure returns (uint256) {\n return (genIndex << height) | index;\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to retrieve beacon block roots.\n * @author Origin Protocol Inc\n */\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the beacon block root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Roots contract to get the parent block root.\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/Endian.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n/**\n * @title Library to handle conversion between little-endian and big-endian formats.\n * @author Origin Protocol Inc\n */\nlibrary Endian {\n /**\n * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64\n * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type\n * @return n The big endian-formatted uint64\n * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits),\n * but it is immediately truncated to a uint64 (i.e. 64 bits)\n * through a right-shift/shr operation.\n */\n function fromLittleEndianUint64(bytes32 lenum)\n internal\n pure\n returns (uint64 n)\n {\n // the number needs to be stored in little-endian encoding (ie in bytes 0-8)\n n = uint64(uint256(lenum >> 192));\n // forgefmt: disable-next-item\n return\n (n >> 56) |\n ((0x00FF000000000000 & n) >> 40) |\n ((0x0000FF0000000000 & n) >> 24) |\n ((0x000000FF00000000 & n) >> 8) |\n ((0x00000000FF000000 & n) << 8) |\n ((0x0000000000FF0000 & n) << 24) |\n ((0x000000000000FF00 & n) << 40) |\n ((0x00000000000000FF & n) << 56);\n }\n\n function toLittleEndianUint64(uint64 benum)\n internal\n pure\n returns (bytes32 n)\n {\n // Convert to little-endian by reversing byte order\n uint64 reversed = (benum >> 56) |\n ((0x00FF000000000000 & benum) >> 40) |\n ((0x0000FF0000000000 & benum) >> 24) |\n ((0x000000FF00000000 & benum) >> 8) |\n ((0x00000000FF000000 & benum) << 8) |\n ((0x0000000000FF0000 & benum) << 24) |\n ((0x000000000000FF00 & benum) << 40) |\n ((0x00000000000000FF & benum) << 56);\n\n // Store the little-endian uint64 in the least significant 64 bits of bytes32\n n = bytes32(uint256(reversed));\n // Shift to most significant bits\n n = n << 192;\n }\n}\n" + }, + "contracts/beacon/Merkle.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev These functions deal with verification of Merkle Tree proofs.\n *\n * The tree and the proofs can be generated using our\n * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].\n * You will find a quickstart guide in the readme.\n *\n * WARNING: You should avoid using leaf values that are 64 bytes long prior to\n * hashing, or use a hash function other than keccak256 for hashing leaves.\n * This is because the concatenation of a sorted pair of internal nodes in\n * the merkle tree could be reinterpreted as a leaf value.\n * OpenZeppelin's JavaScript library generates merkle trees that are safe\n * against this attack out of the box.\n */\nlibrary Merkle {\n error InvalidProofLength();\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function verifyInclusionSha256(\n bytes memory proof,\n bytes32 root,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bool) {\n return processInclusionProofSha256(proof, leaf, index) == root;\n }\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * _Available since v4.4._\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function processInclusionProofSha256(\n bytes memory proof,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bytes32) {\n require(\n proof.length != 0 && proof.length % 32 == 0,\n InvalidProofLength()\n );\n bytes32[1] memory computedHash = [leaf];\n for (uint256 i = 32; i <= proof.length; i += 32) {\n if (index % 2 == 0) {\n // if ith bit of index is 0, then computedHash is a left sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(computedHash))\n mstore(0x20, mload(add(proof, i)))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n }\n } else {\n // if ith bit of index is 1, then computedHash is a right sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(add(proof, i)))\n mstore(0x20, mload(computedHash))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n }\n }\n index = index / 2;\n }\n return computedHash[0];\n }\n\n /**\n * @notice Returns the merkle root of a tree created from a set of leaves using sha256 as its hash function.\n * @param leaves the leaves of the merkle tree\n * @return The computed Merkle root of the tree.\n * @dev A pre-condition to this function is that leaves.length is a power of two.\n * If not, the function will merkleize the inputs incorrectly.\n */\n function merkleizeSha256(bytes32[] memory leaves)\n internal\n pure\n returns (bytes32)\n {\n //there are half as many nodes in the layer above the leaves\n uint256 numNodesInLayer = leaves.length / 2;\n //create a layer to store the internal nodes\n bytes32[] memory layer = new bytes32[](numNodesInLayer);\n //fill the layer with the pairwise hashes of the leaves\n for (uint256 i = 0; i < numNodesInLayer; i++) {\n layer[i] = sha256(\n abi.encodePacked(leaves[2 * i], leaves[2 * i + 1])\n );\n }\n //the next layer above has half as many nodes\n numNodesInLayer /= 2;\n //while we haven't computed the root\n while (numNodesInLayer != 0) {\n //overwrite the first numNodesInLayer nodes in layer with the pairwise hashes of their children\n for (uint256 i = 0; i < numNodesInLayer; i++) {\n layer[i] = sha256(\n abi.encodePacked(layer[2 * i], layer[2 * i + 1])\n );\n }\n //the next layer above has half as many nodes\n numNodesInLayer /= 2;\n }\n //the first node in the layer is the root\n return layer[0];\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to request full or partial withdrawals from validators on the beacon chain.\n * @author Origin Protocol Inc\n */\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n /// @notice Requests a partial withdrawal for a given validator public key and amount.\n /// @param validatorPubKey The public key of the validator to withdraw from\n /// @param amount The amount of ETH to withdraw\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n /// @notice Gets fee for withdrawal requests contract on Beacon chain\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint40 validatorIndex,\n bytes32 withdrawalCredentials\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint40 validatorIndex\n ) external view returns (uint256 validatorBalance);\n\n function verifyPendingDepositsContainer(\n bytes32 beaconBlockRoot,\n bytes32 pendingDepositsContainerRoot,\n bytes calldata proof\n ) external view;\n\n function verifyPendingDeposit(\n bytes32 pendingDepositsContainerRoot,\n bytes32 pendingDepositRoot,\n bytes calldata proof,\n uint32 pendingDepositIndex\n ) external view;\n\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view returns (bool isEmptyDepositQueue);\n\n function merkleizePendingDeposit(\n bytes32 pubKeyHash,\n bytes calldata withdrawalCredentials,\n uint64 amountGwei,\n bytes calldata signature,\n uint64 slot\n ) external pure returns (bytes32 root);\n\n function merkleizeSignature(bytes calldata signature)\n external\n pure\n returns (bytes32 root);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/mocks/beacon/EnhancedBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"../../beacon/BeaconProofsLib.sol\";\nimport { BeaconProofs } from \"../../beacon/BeaconProofs.sol\";\n\ncontract EnhancedBeaconProofs is BeaconProofs {\n function concatGenIndices(\n uint256 index1,\n uint256 height2,\n uint256 index2\n ) external pure returns (uint256 genIndex) {\n return BeaconProofsLib.concatGenIndices(index1, height2, index2);\n }\n}\n" + }, + "contracts/mocks/beacon/MockBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\n// solhint-disable no-unused-vars\n\n/**\n * @title Mock contract for test purposes verifying Merkle proofs\n * @author Origin Protocol Inc\n */\ncontract MockBeaconProofs is IBeaconProofs {\n /// @dev Number of bytes in the proof to the first pending deposit.\n /// 37 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0]\n /// 37 * 32 bytes = 1184 bytes\n uint256 internal constant FIRST_PENDING_DEPOSIT_PROOF_LENGTH = 1184;\n\n uint256 internal constant DEFAULT_VALIDATOR_BALANCE_GWEI = 33 ether / 1e9;\n // mapping of validator indexes to validator balances\n mapping(uint40 => uint256) public validatorBalances;\n\n function setValidatorBalance(uint40 index, uint256 validatorBalanceGwei)\n external\n {\n // set special max value instead of 0\n if (validatorBalanceGwei == 0) {\n validatorBalances[index] = type(uint256).max;\n } else {\n validatorBalances[index] = validatorBalanceGwei;\n }\n }\n\n /// @notice Verifies the validator index is for the given validator public key.\n /// Also verify the validator's withdrawal credential points to the withdrawal address.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param proof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalCredentials a value containing the validator type and withdrawal address.\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata proof,\n uint40 validatorIndex,\n bytes32 withdrawalCredentials\n ) external view {\n // always pass\n }\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view {\n // always pass\n }\n\n /// @notice Verifies the balances container to the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerRoot The merkle root of the the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata balancesContainerProof\n ) external view {\n // always pass\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param balanceProof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint40 validatorIndex\n ) external view returns (uint256 validatorBalanceGwei) {\n uint256 validatorBalance = validatorBalances[validatorIndex];\n\n // special setting representing 0 balance\n if (validatorBalance == type(uint256).max) {\n return 0;\n }\n // validator balance not set by the test cases\n else if (validatorBalance == 0) {\n return DEFAULT_VALIDATOR_BALANCE_GWEI;\n }\n\n return validatorBalance;\n }\n\n function verifyPendingDepositsContainer(\n bytes32 beaconBlockRoot,\n bytes32 pendingDepositsContainerRoot,\n bytes calldata proof\n ) external view {\n // always pass\n }\n\n function verifyPendingDeposit(\n bytes32 pendingDepositsContainerRoot,\n bytes32 pendingDepositRoot,\n bytes calldata proof,\n uint32 pendingDepositIndex\n ) external view {\n // always pass\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the slot of the first pending deposit to the beacon block root.\n /// BeaconBlock.state.pendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty.\n /// @param firstPendingDepositSlotProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view returns (bool isEmptyDepositQueue) {\n if (\n firstPendingDepositSlotProof.length ==\n FIRST_PENDING_DEPOSIT_PROOF_LENGTH\n ) {\n isEmptyDepositQueue = true;\n }\n }\n\n function merkleizePendingDeposit(\n bytes32 pubKeyHash,\n bytes calldata withdrawalCredentials,\n uint64 amountGwei,\n bytes calldata signature,\n uint64 slot\n ) external pure returns (bytes32) {\n return\n keccak256(\n abi.encodePacked(\n pubKeyHash,\n withdrawalCredentials,\n amountGwei,\n signature,\n slot\n )\n );\n }\n\n function merkleizeSignature(bytes calldata signature)\n external\n pure\n returns (bytes32 root)\n {\n return keccak256(abi.encodePacked(signature));\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with\n /// `platformAddress` not used so empty address\n /// `vaultAddress` the address of the OETH Vault contract\n /// @param _wethAddress Address of the WETH Token contract\n /// @param _ssvToken Address of the SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconProofs,\n _beaconGenesisTimestamp\n )\n {\n SSV_TOKEN = _ssvToken;\n\n // Make sure nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// @param _rewardTokenAddresses Not used so empty array\n /// @param _assets Not used so empty array\n /// @param _pTokens Not used so empty array\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n safeApproveAllTokens();\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH that was transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// @param _recipient Address to receive withdrawn assets.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH to withdraw.\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(WETH, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Accounts for all the assets managed by this strategy which includes:\n /// 1. The current WETH in this strategy contract\n /// 2. The last verified ETH balance, total deposits and total validator balances\n /// @param _asset Address of WETH asset.\n /// @return balance Total value in ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by the strategy.\n /// @param _asset The address of the WETH token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for validator registration.\n function safeApproveAllTokens() public override {\n // Approves the SSV Network contract to transfer SSV tokens when validators are registered\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n * The new ETH will be accounted for in `checkBalance` after the next snapBalances and verifyBalances txs.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n /// @notice is not supported for this strategy as there is no platform token.\n function setPTokenAddress(address, address) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @notice is not supported for this strategy as there is no platform token.\n function removePToken(uint256) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @dev This strategy does not use a platform token like the old Aave and Compound strategies.\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instead of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract.\n /// Withdrawals from validators also accumulate as ETH in this strategy contract.\n /// It's too complex to separate the rewards from withdrawals so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can now regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingView.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/**\n * @title Viewing contract for the Compounding Staking Strategy.\n * @author Origin Protocol Inc\n */\ncontract CompoundingStakingStrategyView {\n /// @notice The address of the Compounding Staking Strategy contract\n CompoundingValidatorManager public immutable stakingStrategy;\n\n constructor(address _stakingStrategy) {\n stakingStrategy = CompoundingValidatorManager(_stakingStrategy);\n }\n\n struct ValidatorView {\n bytes32 pubKeyHash;\n uint64 index;\n CompoundingValidatorManager.ValidatorState state;\n }\n\n struct DepositView {\n bytes32 pendingDepositRoot;\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n }\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return validators An array of `ValidatorView` containing the public key hash, validator index and state.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorView[] memory validators)\n {\n uint256 validatorCount = stakingStrategy.verifiedValidatorsLength();\n validators = new ValidatorView[](validatorCount);\n for (uint256 i = 0; i < validatorCount; ++i) {\n bytes32 pubKeyHash = stakingStrategy.verifiedValidators(i);\n (\n CompoundingValidatorManager.ValidatorState state,\n uint64 index\n ) = stakingStrategy.validator(pubKeyHash);\n validators[i] = ValidatorView({\n pubKeyHash: pubKeyHash,\n index: index,\n state: state\n });\n }\n }\n\n /// @notice Returns the deposits that are still to be verified.\n /// These may or may not have been processed by the beacon chain.\n /// @return pendingDeposits An array of `DepositView` containing the deposit ID, public key hash,\n /// amount in Gwei and the slot of the deposit.\n function getPendingDeposits()\n external\n view\n returns (DepositView[] memory pendingDeposits)\n {\n uint256 depositsCount = stakingStrategy.depositListLength();\n pendingDeposits = new DepositView[](depositsCount);\n for (uint256 i = 0; i < depositsCount; ++i) {\n (\n bytes32 pubKeyHash,\n uint64 amountGwei,\n uint64 slot,\n ,\n\n ) = stakingStrategy.deposits(stakingStrategy.depositList(i));\n pendingDeposits[i] = DepositView({\n pendingDepositRoot: stakingStrategy.depositList(i),\n pubKeyHash: pubKeyHash,\n amountGwei: amountGwei,\n slot: slot\n });\n }\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable, Pausable {\n using SafeERC20 for IERC20;\n\n /// @dev The amount of ETH in wei that is required for a deposit to a new validator.\n uint256 internal constant DEPOSIT_AMOUNT_WEI = 1 ether;\n /// @dev Validator balances over this amount will eventually become active on the beacon chain.\n /// Due to hysteresis, if the effective balance is 31 ETH, the actual balance\n /// must rise to 32.25 ETH to trigger an effective balance update to 32 ETH.\n /// https://eth2book.info/capella/part2/incentives/balances/#hysteresis\n uint256 internal constant MIN_ACTIVATION_BALANCE_GWEI = 32.25 ether / 1e9;\n /// @dev The maximum number of deposits that are waiting to be verified as processed on the beacon chain.\n uint256 internal constant MAX_DEPOSITS = 12;\n /// @dev The maximum number of validators that can be verified.\n uint256 internal constant MAX_VERIFIED_VALIDATORS = 48;\n /// @dev The default withdrawable epoch value on the Beacon chain.\n /// A value in the far future means the validator is not exiting.\n uint64 internal constant FAR_FUTURE_EPOCH = type(uint64).max;\n /// @dev The number of seconds between each beacon chain slot.\n uint64 internal constant SLOT_DURATION = 12;\n /// @dev The number of slots in each beacon chain epoch.\n uint64 internal constant SLOTS_PER_EPOCH = 32;\n /// @dev Minimum time in seconds to allow snapped balances to be verified.\n /// Set to 35 slots which is 3 slots more than 1 epoch (32 slots). Deposits get processed\n /// once per epoch. This larger than 1 epoch delay should achieve that `snapBalances` sometimes\n /// get called in the middle (or towards the end) of the epoch. Giving the off-chain script\n /// sufficient time after the end of the epoch to prepare the proofs and call `verifyBalances`.\n /// This is considering a malicious actor would keep calling `snapBalances` as frequent as possible\n /// to disturb our operations.\n uint64 internal constant SNAP_BALANCES_DELAY = 35 * SLOT_DURATION;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address internal immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address internal immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address internal immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n /// @notice The timestamp of the Beacon chain genesis.\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\n uint64 internal immutable BEACON_GENESIS_TIMESTAMP;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// @notice Deposit data for new compounding validators.\n /// @dev A `VERIFIED` deposit can mean 3 separate things:\n /// - a deposit has been processed by the beacon chain and shall be included in the\n /// balance of the next verifyBalances call\n /// - a deposit has been done to a slashed validator and has probably been recovered\n /// back to this strategy. Probably because we can not know for certain. This contract\n /// only detects when the validator has passed its withdrawal epoch. It is close to impossible\n /// to prove with Merkle Proofs that the postponed deposit this contract is responsible for\n /// creating is not present anymore in BeaconChain.state.pending_deposits. This in effect\n /// means that there might be a period where this contract thinks the deposit has been already\n /// returned as ETH balance before it happens. This will result in some days (or weeks)\n /// -> depending on the size of deposit queue of showing a deficit when calling `checkBalance`.\n /// As this only offsets the yield and doesn't cause a critical double-counting we are not addressing\n /// this issue.\n /// - A deposit has been done to the validator, but our deposit has been front run by a malicious\n /// actor. Funds in the deposit this contract makes are not recoverable.\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified\n }\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountGwei Amount of ETH in gwei that has been deposited to the beacon chain deposit contract\n /// @param slot The beacon chain slot number when the deposit has been made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either UNKNOWN, PENDING or VERIFIED\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint32 depositIndex;\n DepositStatus status;\n }\n /// @notice Restricts to only one deposit to an unverified validator at a time.\n /// This is to limit front-running attacks of deposits to the beacon chain contract.\n ///\n /// @dev The value is set to true when a deposit to a new validator has been done that has\n /// not yet be verified.\n bool public firstDeposit;\n /// @notice Mapping of the pending deposit roots to the deposit data\n mapping(bytes32 => DepositData) public deposits;\n /// @notice List of strategy deposit IDs to a validator.\n /// The ID is the merkle root of the pending deposit data which is unique for each validator, amount and block.\n /// Duplicate pending deposit roots are prevented so can be used as an identifier to each strategy deposit.\n /// The list can be for deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n /// The list may not be ordered by time of deposit.\n /// Removed deposits will move the last deposit to the removed index.\n bytes32[] public depositList;\n\n enum ValidatorState {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n ACTIVE, // The validator balance is at least 32 ETH. The validator may not yet be active on the beacon chain.\n EXITING, // The validator has been requested to exit or has been verified as forced exit\n EXITED, // The validator has been verified to have a zero balance\n REMOVED, // validator has funds withdrawn to this strategy contract and is removed from the SSV\n INVALID // The validator has been front-run and the withdrawal address is not this strategy\n }\n\n // Validator data\n struct ValidatorData {\n ValidatorState state; // The state of the validator known to this contract\n uint40 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n bytes32[] public verifiedValidators;\n /// @notice Mapping of the hash of the validator's public key to the validator state and index.\n /// Uses the Beacon chain hashing for BLSPubkey which is sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => ValidatorData) public validator;\n\n /// @param blockRoot Beacon chain block root of the snapshot\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n bytes32 blockRoot;\n uint64 timestamp;\n uint128 ethBalance;\n }\n /// @notice Mapping of the block root to the balances at that slot\n Balances public snappedBalance;\n /// @notice The last verified ETH balance of the strategy\n uint256 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[41] private __gap;\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event FirstDepositReset();\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n bytes32 indexed pendingDepositRoot,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint40 indexed validatorIndex\n );\n event ValidatorInvalid(bytes32 indexed pubKeyHash);\n event DepositVerified(\n bytes32 indexed pendingDepositRoot,\n uint256 amountWei\n );\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(bytes32 indexed blockRoot, uint256 ethBalance);\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 ethBalance\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @dev Throws if called by any account other than the Registrator or Governor\n modifier onlyRegistratorOrGovernor() {\n require(\n msg.sender == validatorRegistrator || isGovernor(),\n \"Not Registrator or Governor\"\n );\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_PROOFS = _beaconProofs;\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\n\n require(\n block.timestamp > _beaconGenesisTimestamp,\n \"Invalid genesis timestamp\"\n );\n }\n\n /**\n *\n * Admin Functions\n *\n */\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Reset the `firstDeposit` flag to false so deposits to unverified validators can be made again.\n function resetFirstDeposit() external onlyGovernor {\n require(firstDeposit, \"No first deposit\");\n\n firstDeposit = false;\n\n emit FirstDepositReset();\n }\n\n function pause() external onlyRegistratorOrGovernor {\n _pause();\n }\n\n function unPause() external onlyGovernor {\n _unpause();\n }\n\n /**\n *\n * Validator Management\n *\n */\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validator[pubKeyHash].state == ValidatorState.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validator[pubKeyHash].state = ValidatorState.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n struct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n }\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// The the first deposit to a new validator, the amount must be 1 ETH.\n /// Another deposit of at least 31 ETH is required for the validator to be activated.\n /// This second deposit has to be done after the validator has been verified.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// There can not be two deposits to the same validator in the same block for the same amount.\n /// Function is pausable so in case a run-away Registrator can be prevented from continuing\n /// to deposit funds to slashed or undesired validators.\n /// @param validatorStakeData validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth,reentrancy-no-eth\n function stakeEth(\n ValidatorStakeData calldata validatorStakeData,\n uint64 depositAmountGwei\n ) external onlyRegistrator whenNotPaused {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(depositList.length < MAX_DEPOSITS, \"Max deposits\");\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validatorStakeData.pubkey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can only stake to a validator has have been registered, verified or active.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.VERIFIED ||\n currentState == ValidatorState.ACTIVE),\n \"Not registered or verified\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n if (currentState == ValidatorState.REGISTERED) {\n // Can only have one pending deposit to an unverified validator at a time.\n // This is to limit front-running deposit attacks to a single deposit.\n // The exiting deposit needs to be verified before another deposit can be made.\n // If there was a front-running attack, the validator needs to be verified as invalid\n // and the Governor calls `resetFirstDeposit` to set `firstDeposit` to false.\n require(!firstDeposit, \"Existing first deposit\");\n // Limits the amount of ETH that can be at risk from a front-running deposit attack.\n require(\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n // Limits the number of validator balance proofs to verifyBalances\n require(\n verifiedValidators.length + 1 < MAX_VERIFIED_VALIDATORS,\n \"Max validators\"\n );\n\n // Flag a deposit to an unverified validator so only no other deposits can be made\n // to an unverified validator.\n firstDeposit = true;\n validator[pubKeyHash].state = ValidatorState.STAKED;\n }\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n /// After the Pectra upgrade the validators have a new restriction when proposing\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\n /// forward. Each slot is created at strict 12 second intervals and those slots can\n /// either have blocks attached to them or not. This way using the block.timestamp\n /// the slot number can easily be calculated.\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\n BEACON_GENESIS_TIMESTAMP) / SLOT_DURATION;\n\n // Calculate the merkle root of the beacon chain pending deposit data.\n // This is used as the unique ID of the deposit.\n bytes32 pendingDepositRoot = IBeaconProofs(BEACON_PROOFS)\n .merkleizePendingDeposit(\n pubKeyHash,\n withdrawalCredentials,\n depositAmountGwei,\n validatorStakeData.signature,\n depositSlot\n );\n require(\n deposits[pendingDepositRoot].status == DepositStatus.UNKNOWN,\n \"Duplicate deposit\"\n );\n\n // Store the deposit data for verifyDeposit and verifyBalances\n deposits[pendingDepositRoot] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n slot: depositSlot,\n depositIndex: SafeCast.toUint32(depositList.length),\n status: DepositStatus.PENDING\n });\n depositList.push(pendingDepositRoot);\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validatorStakeData.pubkey,\n withdrawalCredentials,\n validatorStakeData.signature,\n validatorStakeData.depositDataRoot\n );\n\n emit ETHStaked(\n pubKeyHash,\n pendingDepositRoot,\n validatorStakeData.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth,reentrancy-no-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then only the amount in excess of 32 ETH will be withdrawn.\n /// Only the Registrator can call this function.\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\n /// If no ETH balance, the tx will revert.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n payable\n onlyRegistrator\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorData memory validatorDataMem = validator[pubKeyHash];\n // Validator full withdrawal could be denied due to multiple reasons:\n // - the validator has not been activated or active long enough\n // (current_epoch < activation_epoch + SHARD_COMMITTEE_PERIOD)\n // - the validator has pending balance to withdraw from a previous partial withdrawal request\n //\n // Meaning that the on-chain to beacon chain full withdrawal request could fail. Instead\n // of adding complexity of verifying if a validator is eligible for a full exit, we allow\n // multiple full withdrawal requests per validator.\n require(\n validatorDataMem.state == ValidatorState.ACTIVE ||\n validatorDataMem.state == ValidatorState.EXITING,\n \"Validator not active/exiting\"\n );\n\n // If a full withdrawal (validator exit)\n if (amountGwei == 0) {\n // For each staking strategy's deposits\n uint256 depositsCount = depositList.length;\n for (uint256 i = 0; i < depositsCount; ++i) {\n bytes32 pendingDepositRoot = depositList[i];\n // Check there is no pending deposits to the exiting validator\n require(\n pubKeyHash != deposits[pendingDepositRoot].pubKeyHash,\n \"Pending deposit\"\n );\n }\n\n // Store the validator state as exiting so no more deposits can be made to it.\n // This may already be EXITING if the previous exit request failed. eg the validator\n // was not active long enough.\n validator[pubKeyHash].state = ValidatorState.EXITING;\n }\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove the validator from the SSV Cluster after:\n /// - the validator has been exited from `validatorWithdrawal` or slashed\n /// - the validator has incorrectly registered and can not be staked to\n /// - the initial deposit was front-run and the withdrawal address is not this strategy's address.\n /// Make sure `validatorWithdrawal` is called with a zero amount and the validator has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.EXITED ||\n currentState == ValidatorState.INVALID,\n \"Validator not regd or exited\"\n );\n\n validator[pubKeyHash].state = ValidatorState.REMOVED;\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /**\n *\n * SSV Management\n *\n */\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be withdrawn from the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /**\n *\n * Beacon Chain Proofs\n *\n */\n\n /// @notice Verifies a validator's index to its public key.\n /// Adds to the list of verified validators if the validator's withdrawal address is this strategy's address.\n /// Marks the validator as invalid and removes the deposit if the withdrawal address is not this strategy's address.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param withdrawalCredentials contain the validator type and withdrawal address. These can be incorrect and/or\n /// malformed. In case of incorrect withdrawalCredentials the validator deposit has been front run\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint40 validatorIndex,\n bytes32 pubKeyHash,\n bytes32 withdrawalCredentials,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validator[pubKeyHash].state == ValidatorState.STAKED,\n \"Validator not staked\"\n );\n\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key.\n // Also verify the validator's withdrawal credentials\n IBeaconProofs(BEACON_PROOFS).verifyValidator(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n withdrawalCredentials\n );\n\n // Store the validator state as verified\n validator[pubKeyHash] = ValidatorData({\n state: ValidatorState.VERIFIED,\n index: validatorIndex\n });\n\n bytes32 expectedWithdrawalCredentials = bytes32(\n abi.encodePacked(bytes1(0x02), bytes11(0), address(this))\n );\n\n // If the initial deposit was front-run and the withdrawal address is not this strategy\n // or the validator type is not a compounding validator (0x02)\n if (expectedWithdrawalCredentials != withdrawalCredentials) {\n // override the validator state\n validator[pubKeyHash].state = ValidatorState.INVALID;\n\n // Find and remove the deposit as the funds can not be recovered\n uint256 depositCount = depositList.length;\n for (uint256 i = 0; i < depositCount; i++) {\n DepositData memory deposit = deposits[depositList[i]];\n if (deposit.pubKeyHash == pubKeyHash) {\n // next verifyBalances will correctly account for the loss of a front-run\n // deposit. Doing it here accounts for the loss as soon as possible\n lastVerifiedEthBalance -= Math.min(\n lastVerifiedEthBalance,\n uint256(deposit.amountGwei) * 1 gwei\n );\n _removeDeposit(depositList[i], deposit);\n break;\n }\n }\n\n // Leave the `firstDeposit` flag as true so no more deposits to unverified validators can be made.\n // The Governor has to reset the `firstDeposit` to false before another deposit to\n // an unverified validator can be made.\n // The Governor can set a new `validatorRegistrator` if they suspect it has been compromised.\n\n emit ValidatorInvalid(pubKeyHash);\n return;\n }\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(pubKeyHash);\n\n // Reset the firstDeposit flag as the first deposit to an unverified validator has been verified.\n firstDeposit = false;\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n struct FirstPendingDepositSlotProofData {\n uint64 slot;\n bytes proof;\n }\n\n struct StrategyValidatorProofData {\n uint64 withdrawableEpoch;\n bytes withdrawableEpochProof;\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n ///\n /// Important: this function has a limitation where `depositProcessedSlot` that is passed by the off-chain\n /// verifier requires a slot immediately after it to propose a block otherwise the `BeaconRoots.parentBlockRoot`\n /// will fail. This shouldn't be a problem, since by the current behaviour of beacon chain only 1%-3% slots\n /// don't propose a block.\n /// @param pendingDepositRoot The unique identifier of the deposit emitted in `ETHStaked` from\n /// the `stakeEth` function.\n /// @param depositProcessedSlot Any slot on or after the strategy's deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\n /// set for the next block timestamp in 12 seconds time.\n /// @param firstPendingDeposit a `FirstPendingDepositSlotProofData` struct containing:\n /// - slot: The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be any non-zero value if the deposit queue is empty.\n /// - proof: The merkle proof of the first pending deposit's slot to the beacon block root.\n /// Can be either:\n /// * 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// * 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @param strategyValidatorData a `StrategyValidatorProofData` struct containing:\n /// - withdrawableEpoch: The withdrawable epoch of the validator the strategy is depositing to.\n /// - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the validator the strategy\n /// is depositing to, to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n bytes32 pendingDepositRoot,\n uint64 depositProcessedSlot,\n FirstPendingDepositSlotProofData calldata firstPendingDeposit,\n StrategyValidatorProofData calldata strategyValidatorData\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[pendingDepositRoot];\n ValidatorData memory strategyValidator = validator[deposit.pubKeyHash];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(firstPendingDeposit.slot != 0, \"Zero 1st pending deposit slot\");\n\n // We should allow the verification of deposits for validators that have been marked as exiting\n // to cover this situation:\n // - there are 2 pending deposits\n // - beacon chain has slashed the validator\n // - when verifyDeposit is called for the first deposit it sets the Validator state to EXITING\n // - verifyDeposit should allow a secondary call for the other deposit to a slashed validator\n require(\n strategyValidator.state == ValidatorState.VERIFIED ||\n strategyValidator.state == ValidatorState.ACTIVE ||\n strategyValidator.state == ValidatorState.EXITING,\n \"Not verified/active/exiting\"\n );\n // The verification slot must be after the deposit's slot.\n // This is needed for when the deposit queue is empty.\n require(deposit.slot < depositProcessedSlot, \"Slot not after deposit\");\n\n uint64 snapTimestamp = snappedBalance.timestamp;\n\n // This check prevents an accounting error that can happen if:\n // - snapBalances are snapped at the time of T\n // - deposit is processed on the beacon chain after time T and before verifyBalances()\n // - verifyDeposit is called before verifyBalances which removes a deposit from depositList\n // and deposit balance from totalDepositsWei\n // - verifyBalances is called under-reporting the strategy's balance\n require(\n (_calcNextBlockTimestamp(depositProcessedSlot) <= snapTimestamp) ||\n snapTimestamp == 0,\n \"Deposit after balance snapshot\"\n );\n\n // Get the parent beacon block root of the next block which is the block root of the deposit verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 depositBlockRoot = BeaconRoots.parentBlockRoot(\n _calcNextBlockTimestamp(depositProcessedSlot)\n );\n\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n depositBlockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.proof\n );\n\n // Verify the withdrawableEpoch on the validator of the strategy's deposit\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n depositBlockRoot,\n strategyValidator.index,\n strategyValidatorData.withdrawableEpoch,\n strategyValidatorData.withdrawableEpochProof\n );\n\n uint64 firstPendingDepositEpoch = firstPendingDeposit.slot /\n SLOTS_PER_EPOCH;\n\n // If deposit queue is empty all deposits have certainly been processed. If not\n // a validator can either be not exiting and no further checks are required.\n // Or a validator is exiting then this function needs to make sure that the\n // pending deposit to an exited validator has certainly been processed. The\n // slot/epoch of first pending deposit is the one that contains the transaction\n // where the deposit to the ETH Deposit Contract has been made.\n //\n // Once the firstPendingDepositEpoch becomes greater than the withdrawableEpoch of\n // the slashed validator then the deposit has certainly been processed. When the beacon\n // chain reaches the withdrawableEpoch of the validator the deposit will no longer be\n // postponed. And any new deposits created (and present in the deposit queue)\n // will have an equal or larger withdrawableEpoch.\n require(\n strategyValidatorData.withdrawableEpoch == FAR_FUTURE_EPOCH ||\n strategyValidatorData.withdrawableEpoch <=\n firstPendingDepositEpoch ||\n isDepositQueueEmpty,\n \"Exit Deposit likely not proc.\"\n );\n\n // solhint-disable max-line-length\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the deposit queue is empty then our deposit must have been processed on the beacon chain.\n // The deposit slot can be zero for validators consolidating to a compounding validator or 0x01 validator\n // being promoted to a compounding one. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // We can not guarantee that the deposit has been processed in that case.\n // solhint-enable max-line-length\n require(\n deposit.slot < firstPendingDeposit.slot || isDepositQueueEmpty,\n \"Deposit likely not processed\"\n );\n\n // Remove the deposit now it has been verified as processed on the beacon chain.\n _removeDeposit(pendingDepositRoot, deposit);\n\n emit DepositVerified(\n pendingDepositRoot,\n uint256(deposit.amountGwei) * 1 gwei\n );\n }\n\n function _removeDeposit(\n bytes32 pendingDepositRoot,\n DepositData memory deposit\n ) internal {\n // After verifying the proof, update the contract storage\n deposits[pendingDepositRoot].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n bytes32 lastDeposit = depositList[depositList.length - 1];\n depositList[deposit.depositIndex] = lastDeposit;\n deposits[lastDeposit].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositList.pop();\n }\n\n /// @dev Calculates the timestamp of the next execution block from the given slot.\n /// @param slot The beacon chain slot number used for merkle proof verification.\n function _calcNextBlockTimestamp(uint64 slot)\n internal\n view\n returns (uint64)\n {\n // Calculate the next block timestamp from the slot.\n return SLOT_DURATION * slot + BEACON_GENESIS_TIMESTAMP + SLOT_DURATION;\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block and beacon block root\n /// of the slot that is associated with the previous block.\n ///\n /// When snapping / verifying balance it is of a high importance that there is no\n /// miss-match in respect to ETH that is held by the contract and balances that are\n /// verified on the validators.\n ///\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\n /// constructing a block on the beacon chain consist of:\n /// - process_withdrawals: ETH is deducted from the validator's balance\n /// - process_execution_payload: immediately after the previous step executing all the\n /// transactions\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\n /// contained in the withdrawal credentials of the exited validators\n ///\n /// That means that balance increases which are part of the post-block execution state are\n /// done within the block, but the transaction that are contained within that block can not\n /// see / interact with the balance from the exited validators. Only transactions in the\n /// next block can do that.\n ///\n /// When snap balances is performed the state of the chain is snapped across 2 separate\n /// chain states:\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\n /// will be referred to as Y - 1 as it makes no difference in the argument\n ///\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\n /// miss-counting ETH or much more dangerous double counting of the ETH.\n ///\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\n /// - ETH balance in the contract on block X\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\n /// - ETH balance in validators that are active in slot Y - 1\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\n /// and have their balance visible to transactions in slot Y and corresponding block X\n /// (or sooner)\n ///\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\n ///\n /// *ETH balance in the contract on block X*\n ///\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\n ///\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\n /// would result in double counting the `stakedEth` since it would be present once in the\n /// snapped contract balance and the second time in deposit storage variables.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\n ///\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\n /// contract at block Z. The execution layer doesn't have direct access to the state of\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\n /// and could also be part of the validator balances. It does that by verifying that at\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\n /// the last snap till now all are still in queue. Which ensures they can not be part of\n /// the validator balances in later steps.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in validators that are active in slot Y - 1*\n ///\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\n /// correctness.\n ///\n /// This behaviour is correct.\n ///\n /// *The withdrawn validators*\n ///\n /// The withdrawn validators could have their balances deducted in any slot before slot\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\n /// look at the \"worst case scenario\" where the validator withdrawal is processed in the\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\n /// even if the validator exits at the latest possible time it is paramount that the ETH\n /// balance on the execution layer is recorded in the next block. Correctly accounting\n /// for the withdrawn ETH.\n ///\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\n /// validator balance verification as well as execution layer contract balance snap.\n ///\n /// This behaviour is correct.\n ///\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n function snapBalances() external {\n uint64 currentTimestamp = SafeCast.toUint64(block.timestamp);\n require(\n snappedBalance.timestamp + SNAP_BALANCES_DELAY < currentTimestamp,\n \"Snap too soon\"\n );\n\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(currentTimestamp);\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the snapped balance\n snappedBalance = Balances({\n blockRoot: blockRoot,\n timestamp: currentTimestamp,\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n emit BalancesSnapped(blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct BalanceProofs {\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n struct PendingDepositProofs {\n bytes32 pendingDepositContainerRoot;\n bytes pendingDepositContainerProof;\n uint32[] pendingDepositIndexes;\n bytes[] pendingDepositProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks each of the strategy's deposits are still to be processed by the beacon chain.\n /// @param balanceProofs a `BalanceProofs` struct containing the following:\n /// - balancesContainerRoot: The merkle root of the balances container\n /// - balancesContainerProof: The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// - validatorBalanceLeaves: Array of leaf nodes containing the validator balance with three other balances.\n /// - validatorBalanceProofs: Array of merkle proofs for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param pendingDepositProofs a `PendingDepositProofs` struct containing the following:\n /// - pendingDepositContainerRoot: The merkle root of the pending deposits list container\n /// - pendingDepositContainerProof: The merkle proof from the pending deposits list container\n /// to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// - pendingDepositIndexes: Array of indexes in the pending deposits list container for each\n /// of the strategy's deposits.\n /// - pendingDepositProofs: Array of merkle proofs for each strategy deposit in the\n /// beacon chain's pending deposit list container to the pending deposits list container root.\n /// These are 28 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(\n BalanceProofs calldata balanceProofs,\n PendingDepositProofs calldata pendingDepositProofs\n ) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalance;\n // Check the balances are the latest\n require(balancesMem.timestamp > 0, \"No snapped balances\");\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n uint256 depositsCount = depositList.length;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n balanceProofs.validatorBalanceProofs.length ==\n verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n balanceProofs.validatorBalanceLeaves.length ==\n verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n balancesMem.blockRoot,\n balanceProofs.balancesContainerRoot,\n balanceProofs.balancesContainerProof\n );\n\n bytes32[]\n memory validatorHashesMem = _getPendingDepositValidatorHashes(\n depositsCount\n );\n\n // for each validator in reverse order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n ValidatorData memory validatorDataMem = validator[\n verifiedValidators[i]\n ];\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n balanceProofs.balancesContainerRoot,\n balanceProofs.validatorBalanceLeaves[i],\n balanceProofs.validatorBalanceProofs[i],\n validatorDataMem.index\n );\n\n // If the validator has exited and the balance is now zero\n if (validatorBalanceGwei == 0) {\n // Check if there are any pending deposits to this validator\n bool depositPending = false;\n for (uint256 j = 0; j < validatorHashesMem.length; j++) {\n if (validatorHashesMem[j] == verifiedValidators[i]) {\n depositPending = true;\n break;\n }\n }\n\n // If validator has a pending deposit we can not remove due to\n // the following situation:\n // - validator has a pending deposit\n // - validator has been slashed\n // - sweep cycle has withdrawn all ETH from the validator. Balance is 0\n // - beacon chain has processed the deposit and set the validator balance\n // to deposit amount\n // - if validator is no longer in the list of verifiedValidators its\n // balance will not be considered and be under-counted.\n if (!depositPending) {\n // Store the validator state as exited\n // This could have been in VERIFIED, ACTIVE or EXITING state\n validator[verifiedValidators[i]].state = ValidatorState\n .EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n }\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n } else if (\n validatorDataMem.state == ValidatorState.VERIFIED &&\n validatorBalanceGwei > MIN_ACTIVATION_BALANCE_GWEI\n ) {\n // Store the validator state as active. This does not necessarily mean the\n // validator is active on the beacon chain yet. It just means the validator has\n // enough balance that it can become active.\n validator[verifiedValidators[i]].state = ValidatorState\n .ACTIVE;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += validatorBalanceGwei * 1 gwei;\n }\n }\n\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification.\n // This section is after the validator balance verifications so an exited validator will be marked\n // as EXITED before the deposits are verified. If there was a deposit to an exited validator\n // then the deposit can only be removed once the validator is fully exited.\n // It is possible that validator fully exits and a postponed deposit to an exited validator increases\n // its balance again. In such case the contract will erroneously consider a deposit applied before it\n // has been applied on the beacon chain showing a smaller than real `totalValidatorBalance`.\n if (depositsCount > 0) {\n require(\n pendingDepositProofs.pendingDepositProofs.length ==\n depositsCount,\n \"Invalid deposit proofs\"\n );\n require(\n pendingDepositProofs.pendingDepositIndexes.length ==\n depositsCount,\n \"Invalid deposit indexes\"\n );\n\n // Verify from the root of the pending deposit list container to the beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyPendingDepositsContainer(\n balancesMem.blockRoot,\n pendingDepositProofs.pendingDepositContainerRoot,\n pendingDepositProofs.pendingDepositContainerProof\n );\n\n // For each staking strategy's deposit.\n for (uint256 i = 0; i < depositsCount; ++i) {\n bytes32 pendingDepositRoot = depositList[i];\n\n // Verify the strategy's deposit is still pending on the beacon chain.\n IBeaconProofs(BEACON_PROOFS).verifyPendingDeposit(\n pendingDepositProofs.pendingDepositContainerRoot,\n pendingDepositRoot,\n pendingDepositProofs.pendingDepositProofs[i],\n pendingDepositProofs.pendingDepositIndexes[i]\n );\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei +=\n uint256(deposits[pendingDepositRoot].amountGwei) *\n 1 gwei;\n }\n }\n\n // Store the verified balance in storage\n lastVerifiedEthBalance =\n totalDepositsWei +\n totalValidatorBalance +\n balancesMem.ethBalance;\n // Reset the last snap timestamp so a new snapBalances has to be made\n snappedBalance.timestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice get a list of all validator hashes present in the pending deposits\n /// list can have duplicate entries\n function _getPendingDepositValidatorHashes(uint256 depositsCount)\n internal\n view\n returns (bytes32[] memory validatorHashes)\n {\n validatorHashes = new bytes32[](depositsCount);\n for (uint256 i = 0; i < depositsCount; i++) {\n validatorHashes[i] = deposits[depositList[i]].pubKeyHash;\n }\n }\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n require(pubKey.length == 48, \"Invalid public key\");\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /**\n *\n * WETH and ETH Accounting\n *\n */\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n // The min is required as more WETH can be withdrawn than deposited\n // as the strategy earns consensus and execution rewards.\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance.\n // The ETH balance in this strategy contract can be more than the last verified ETH balance\n // due to partial withdrawals or full exits being processed by the beacon chain since the last snapBalances.\n // It can also happen from execution rewards (MEV) or ETH donations.\n lastVerifiedEthBalance -= Math.min(lastVerifiedEthBalance, _ethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n snappedBalance.timestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += _wethAmount;\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n snappedBalance.timestamp = 0;\n }\n\n /**\n *\n * View Functions\n *\n */\n\n /// @notice Returns the number of deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n function depositListLength() external view returns (uint256) {\n return depositList.length;\n }\n\n /// @notice Returns the number of verified validators.\n function verifiedValidatorsLength() external view returns (uint256) {\n return verifiedValidators.length;\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/7fa100e84a92f118bfb2e3f652f49c29.json b/contracts/deployments/hoodi/solcInputs/7fa100e84a92f118bfb2e3f652f49c29.json new file mode 100644 index 0000000000..749c76493b --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/7fa100e84a92f118bfb2e3f652f49c29.json @@ -0,0 +1,108 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/security/Pausable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to retrieve beacon block roots.\n * @author Origin Protocol Inc\n */\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the beacon block root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Roots contract to get the parent block root.\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to request full or partial withdrawals from validators on the beacon chain.\n * @author Origin Protocol Inc\n */\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n /// @notice Requests a partial withdrawal for a given validator public key and amount.\n /// @param validatorPubKey The public key of the validator to withdraw from\n /// @param amount The amount of ETH to withdraw\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n /// @notice Gets fee for withdrawal requests contract on Beacon chain\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint40 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint40 validatorIndex\n ) external view returns (uint256 validatorBalance);\n\n function verifyPendingDepositsContainer(\n bytes32 beaconBlockRoot,\n bytes32 pendingDepositsContainerRoot,\n bytes calldata proof\n ) external view;\n\n function verifyPendingDeposit(\n bytes32 pendingDepositsContainerRoot,\n bytes32 pendingDepositRoot,\n bytes calldata proof,\n uint64 depositIndex\n ) external view;\n\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view returns (bool isEmptyDepositQueue);\n\n function merkleizePendingDeposit(\n bytes32 pubKeyHash,\n bytes calldata withdrawalCredentials,\n uint64 amountGwei,\n bytes calldata signature,\n uint64 slot\n ) external pure returns (bytes32 root);\n\n function merkleizeSignature(bytes calldata signature)\n external\n pure\n returns (bytes32 root);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with\n /// `platformAddress` not used so empty address\n /// `vaultAddress` the address of the OETH Vault contract\n /// @param _wethAddress Address of the WETH Token contract\n /// @param _ssvToken Address of the SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconProofs,\n _beaconGenesisTimestamp\n )\n {\n SSV_TOKEN = _ssvToken;\n\n // Make sure nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// @param _rewardTokenAddresses Not used so empty array\n /// @param _assets Not used so empty array\n /// @param _pTokens Not used so empty array\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n safeApproveAllTokens();\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH that was transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// @param _recipient Address to receive withdrawn assets.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH to withdraw.\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(WETH, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Accounts for all the assets managed by this strategy which includes:\n /// 1. The current WETH in this strategy contract\n /// 2. The last verified ETH balance, total deposits and total validator balances\n /// @param _asset Address of WETH asset.\n /// @return balance Total value in ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by the strategy.\n /// @param _asset The address of the WETH token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for validator registration.\n function safeApproveAllTokens() public override {\n // Approves the SSV Network contract to transfer SSV tokens when validators are registered\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n * The new ETH will be accounted for in `checkBalance` after the next snapBalances and verifyBalances txs.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n /// @notice is not supported for this strategy as there is no platform token.\n function setPTokenAddress(address, address) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @notice is not supported for this strategy as there is no platform token.\n function removePToken(uint256) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @dev This strategy does not use a platform token like the old Aave and Compound strategies.\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instead of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract.\n /// Withdrawals from validators also accumulate as ETH in this strategy contract.\n /// It's too complex to separate the rewards from withdrawals so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can now regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingView.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/**\n * @title Viewing contract for the Compounding Staking Strategy.\n * @author Origin Protocol Inc\n */\ncontract CompoundingStakingStrategyView {\n /// @notice The address of the Compounding Staking Strategy contract\n CompoundingValidatorManager public immutable stakingStrategy;\n\n constructor(address _stakingStrategy) {\n stakingStrategy = CompoundingValidatorManager(_stakingStrategy);\n }\n\n struct ValidatorView {\n bytes32 pubKeyHash;\n uint64 index;\n CompoundingValidatorManager.ValidatorState state;\n }\n\n struct DepositView {\n bytes32 pendingDepositRoot;\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n }\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return validators An array of `ValidatorView` containing the public key hash, validator index and state.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorView[] memory validators)\n {\n uint256 validatorCount = stakingStrategy.verifiedValidatorsLength();\n validators = new ValidatorView[](validatorCount);\n for (uint256 i = 0; i < validatorCount; ++i) {\n bytes32 pubKeyHash = stakingStrategy.verifiedValidators(i);\n (\n CompoundingValidatorManager.ValidatorState state,\n uint64 index\n ) = stakingStrategy.validator(pubKeyHash);\n validators[i] = ValidatorView({\n pubKeyHash: pubKeyHash,\n index: index,\n state: state\n });\n }\n }\n\n /// @notice Returns the deposits that are still to be verified.\n /// These may or may not have been processed by the beacon chain.\n /// @return pendingDeposits An array of `DepositView` containing the deposit ID, public key hash,\n /// amount in Gwei and the slot of the deposit.\n function getPendingDeposits()\n external\n view\n returns (DepositView[] memory pendingDeposits)\n {\n uint256 depositsCount = stakingStrategy.depositListLength();\n pendingDeposits = new DepositView[](depositsCount);\n for (uint256 i = 0; i < depositsCount; ++i) {\n (\n bytes32 pubKeyHash,\n uint64 amountGwei,\n uint64 slot,\n ,\n\n ) = stakingStrategy.deposits(stakingStrategy.depositList(i));\n pendingDeposits[i] = DepositView({\n pendingDepositRoot: stakingStrategy.depositList(i),\n pubKeyHash: pubKeyHash,\n amountGwei: amountGwei,\n slot: slot\n });\n }\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable, Pausable {\n using SafeERC20 for IERC20;\n\n /// @dev The amount of ETH in wei that is required for a deposit to a new validator.\n uint256 internal constant DEPOSIT_AMOUNT_WEI = 1 ether;\n /// @dev The amount of ETH balance in validator required for validator activation\n uint256 internal constant MIN_ACTIVATION_BALANCE_GWEI = 32 ether / 1e9;\n /// @dev The maximum number of deposits that are waiting to be verified as processed on the beacon chain.\n uint256 internal constant MAX_DEPOSITS = 12;\n /// @dev The maximum number of validators that can be verified.\n uint256 internal constant MAX_VERIFIED_VALIDATORS = 48;\n /// @dev The default withdrawable epoch value on the Beacon chain.\n /// A value in the far future means the validator is not exiting.\n uint64 internal constant FAR_FUTURE_EPOCH = type(uint64).max;\n /// @dev The number of seconds between each beacon chain slot.\n uint64 internal constant SLOT_DURATION = 12;\n /// @dev The number of slots in each beacon chain epoch.\n uint64 internal constant SLOTS_PER_EPOCH = 32;\n /// @dev Minimum time in seconds to allow snapped balances to be verified.\n /// Set to 35 slots which is 3 slots more than 1 epoch (32 slots). Deposits get processed\n /// once per epoch. This larger than 1 epoch delay should achieve that `snapBalances` sometimes\n /// get called in the middle (or towards the end) of the epoch. Giving the off-chain script\n /// sufficient time after the end of the epoch to prepare the proofs and call `verifyBalances`.\n /// This is considering a malicious actor would keep calling `snapBalances` as frequent as possible\n /// to disturb our operations.\n uint64 internal constant SNAP_BALANCES_DELAY = 35 * SLOT_DURATION;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address internal immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address internal immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address internal immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n /// @notice The timestamp of the Beacon chain genesis.\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\n uint64 internal immutable BEACON_GENESIS_TIMESTAMP;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// @notice Deposit data for new compounding validators.\n /// @dev A `VERIFIED` deposit can mean 3 separate things:\n /// - a deposit has been processed by the beacon chain and shall be included in the\n /// balance of the next verifyBalances call\n /// - a deposit has been done to a slashed validator and has probably been recovered\n /// back to this strategy. Probably because we can not know for certain. This contract\n /// only detects when the validator has passed its withdrawal epoch. It is close to impossible\n /// to prove with Merkle Proofs that the postponed deposit this contract is responsible for\n /// creating is not present anymore in BeaconChain.state.pending_deposits. This in effect\n /// means that there might be a period where this contract thinks the deposit has been already\n /// returned as ETH balance before it happens. This will result in some days (or weeks)\n /// -> depending on the size of deposit queue of showing a deficit when calling `checkBalance`.\n /// As this only offsets the yield and doesn't cause a critical double-counting we are not addressing\n /// this issue.\n /// - A deposit has been done to the validator, but our deposit has been front run by a malicious\n /// actor. Funds in the deposit this contract makes are not recoverable.\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified\n }\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountGwei Amount of ETH in gwei that has been deposited to the beacon chain deposit contract\n /// @param slot The beacon chain slot number when the deposit has been made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either UNKNOWN, PENDING or VERIFIED\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint32 depositIndex;\n DepositStatus status;\n }\n /// @notice Restricts to only one deposit to an unverified validator at a time.\n /// This is to limit front-running attacks of deposits to the beacon chain contract.\n ///\n /// @dev The value is set to true when a deposit to a new validator has been done that has\n /// not yet be verified.\n bool public firstDeposit;\n /// @notice Mapping of the pending deposit roots to the deposit data\n mapping(bytes32 => DepositData) public deposits;\n /// @notice List of strategy deposit IDs to a validator.\n /// The ID is the merkle root of the pending deposit data which is unique for each validator, amount and block.\n /// Duplicate pending deposit roots are prevented so can be used as an identifier to each strategy deposit.\n /// The list can be for deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n /// The list may not be ordered by time of deposit.\n /// Removed deposits will move the last deposit to the removed index.\n bytes32[] public depositList;\n\n enum ValidatorState {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n ACTIVE, // The validator balance is at least 32 ETH\n EXITING, // The validator has been requested to exit or has been verified as forced exit\n EXITED, // The validator has been verified to have a zero balance\n REMOVED, // validator has funds withdrawn to this strategy contract and is removed from the SSV\n INVALID // The validator has been front-run and the withdrawal address is not this strategy\n }\n\n // Validator data\n struct ValidatorData {\n ValidatorState state; // The state of the validator known to this contract\n uint40 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n bytes32[] public verifiedValidators;\n /// @notice Mapping of the hash of the validator's public key to the validator state and index.\n /// Uses the Beacon chain hashing for BLSPubkey which is sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => ValidatorData) public validator;\n\n /// @param blockRoot Beacon chain block root of the snapshot\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n bytes32 blockRoot;\n uint64 timestamp;\n uint128 ethBalance;\n }\n /// @notice Mapping of the block root to the balances at that slot\n Balances public snappedBalance;\n /// @notice The last verified ETH balance of the strategy\n uint256 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[41] private __gap;\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event FirstDepositReset();\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n bytes32 indexed pendingDepositRoot,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint40 indexed validatorIndex\n );\n event ValidatorInvalid(bytes32 indexed pubKeyHash);\n event DepositVerified(\n bytes32 indexed pendingDepositRoot,\n uint256 amountWei\n );\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(bytes32 indexed blockRoot, uint256 ethBalance);\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 ethBalance\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @dev Throws if called by any account other than the Registrator or Governor\n modifier onlyRegistratorOrGovernor() {\n require(\n msg.sender == validatorRegistrator || isGovernor(),\n \"Not Registrator or Governor\"\n );\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_PROOFS = _beaconProofs;\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\n\n require(\n block.timestamp > _beaconGenesisTimestamp,\n \"Invalid genesis timestamp\"\n );\n }\n\n /**\n *\n * Admin Functions\n *\n */\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Reset the `firstDeposit` flag to false so deposits to unverified validators can be made again.\n function resetFirstDeposit() external onlyGovernor {\n require(firstDeposit, \"No first deposit\");\n\n firstDeposit = false;\n\n emit FirstDepositReset();\n }\n\n function pause() external onlyRegistratorOrGovernor {\n _pause();\n }\n\n function unPause() external onlyGovernor {\n _unpause();\n }\n\n /**\n *\n * Validator Management\n *\n */\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validator[pubKeyHash].state == ValidatorState.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validator[pubKeyHash].state = ValidatorState.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n struct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n }\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// The the first deposit to a new validator, the amount must be 1 ETH.\n /// Another deposit of at least 31 ETH is required for the validator to be activated.\n /// This second deposit has to be done after the validator has been verified.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// There can not be two deposits to the same validator in the same block for the same amount.\n /// Function is pausable so in case a run-away Registrator can be prevented from continuing\n /// to deposit funds to slashed or undesired validators.\n /// @param validatorStakeData validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth,reentrancy-no-eth\n function stakeEth(\n ValidatorStakeData calldata validatorStakeData,\n uint64 depositAmountGwei\n ) external onlyRegistrator whenNotPaused {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(depositList.length < MAX_DEPOSITS, \"Max deposits\");\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validatorStakeData.pubkey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.VERIFIED ||\n currentState == ValidatorState.ACTIVE),\n \"Not registered or verified\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n if (currentState == ValidatorState.REGISTERED) {\n // Can only have one pending deposit to an unverified validator at a time.\n // This is to limit front-running deposit attacks to a single deposit.\n // The exiting deposit needs to be verified before another deposit can be made.\n // If there was a front-running attack, the validator needs to be verified as invalid\n // and the Governor calls `resetFirstDeposit` to set `firstDeposit` to false.\n require(!firstDeposit, \"Existing first deposit\");\n // Limits the amount of ETH that can be at risk from a front-running deposit attack.\n require(\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n // Limits the number of validator balance proofs to verifyBalances\n require(\n verifiedValidators.length + 1 < MAX_VERIFIED_VALIDATORS,\n \"Max validators\"\n );\n\n // Flag a deposit to an unverified validator so only no other deposits can be made\n // to an unverified validator.\n firstDeposit = true;\n validator[pubKeyHash].state = ValidatorState.STAKED;\n }\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n /// After the Pectra upgrade the validators have a new restriction when proposing\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\n /// forward. Each slot is created at strict 12 second intervals and those slots can\n /// either have blocks attached to them or not. This way using the block.timestamp\n /// the slot number can easily be calculated.\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\n BEACON_GENESIS_TIMESTAMP) / SLOT_DURATION;\n\n // Calculate the merkle root of the beacon chain pending deposit data.\n // This is used as the unique ID of the deposit.\n bytes32 pendingDepositRoot = IBeaconProofs(BEACON_PROOFS)\n .merkleizePendingDeposit(\n pubKeyHash,\n withdrawalCredentials,\n depositAmountGwei,\n validatorStakeData.signature,\n depositSlot\n );\n require(\n deposits[pendingDepositRoot].status == DepositStatus.UNKNOWN,\n \"Duplicate deposit\"\n );\n\n // Store the deposit data for verifyDeposit and verifyBalances\n deposits[pendingDepositRoot] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n slot: depositSlot,\n depositIndex: SafeCast.toUint32(depositList.length),\n status: DepositStatus.PENDING\n });\n depositList.push(pendingDepositRoot);\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validatorStakeData.pubkey,\n withdrawalCredentials,\n validatorStakeData.signature,\n validatorStakeData.depositDataRoot\n );\n\n emit ETHStaked(\n pubKeyHash,\n pendingDepositRoot,\n validatorStakeData.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth,reentrancy-no-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then only the amount in excess of 32 ETH will be withdrawn.\n /// Only the Registrator can call this function.\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\n /// If no ETH balance, the tx will revert.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n payable\n onlyRegistrator\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorData memory validatorDataMem = validator[pubKeyHash];\n // Validator full withdrawal could be denied due to multiple reasons:\n // - the validator has not been activated or active long enough\n // (current_epoch < activation_epoch + SHARD_COMMITTEE_PERIOD)\n // - the validator has pending balance to withdraw from a previous partial withdrawal request\n //\n // Meaning that the on-chain to beacon chain full withdrawal request could fail. Instead\n // of adding complexity of verifying if a validator is eligible for a full exit, we allow\n // multiple full withdrawal requests per validator.\n require(\n validatorDataMem.state == ValidatorState.ACTIVE ||\n validatorDataMem.state == ValidatorState.EXITING,\n \"Validator not active/exiting\"\n );\n\n // If a full withdrawal (validator exit)\n if (amountGwei == 0) {\n // For each staking strategy's deposits\n uint256 depositsCount = depositList.length;\n for (uint256 i = 0; i < depositsCount; ++i) {\n bytes32 pendingDepositRoot = depositList[i];\n // Check there is no pending deposits to the exiting validator\n require(\n pubKeyHash != deposits[pendingDepositRoot].pubKeyHash,\n \"Pending deposit\"\n );\n }\n\n // A validator that never had the minimal activation balance can not activate\n // and thus can not perform a full exit\n require(\n validatorDataMem.state == ValidatorState.ACTIVE,\n \"Validator not active\"\n );\n\n // Store the validator state as exiting so no more deposits can be made to it.\n validator[pubKeyHash].state = ValidatorState.EXITING;\n }\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove the validator from the SSV Cluster after:\n /// - the validator has been exited from `validatorWithdrawal` or slashed\n /// - the validator has incorrectly registered and can not be staked to\n /// - the initial deposit was front-run and the withdrawal address is not this strategy's address.\n /// Make sure `validatorWithdrawal` is called with a zero amount and the validator has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.EXITED ||\n currentState == ValidatorState.INVALID,\n \"Validator not regd or exited\"\n );\n\n validator[pubKeyHash].state = ValidatorState.REMOVED;\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /**\n *\n * SSV Management\n *\n */\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be withdrawn from the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /**\n *\n * Beacon Chain Proofs\n *\n */\n\n /// @notice Verifies a validator's index to its public key.\n /// Adds to the list of verified validators if the validator's withdrawal address is this strategy's address.\n /// Marks the validator as invalid and removes the deposit if the withdrawal address is not this strategy's address.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param withdrawalAddress The withdrawal address of the validator which should be this strategy's address.\n /// If the withdrawal address is not this strategy's address, the initial deposit was front-run\n /// and the validator is marked as invalid.\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint40 validatorIndex,\n bytes32 pubKeyHash,\n address withdrawalAddress,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validator[pubKeyHash].state == ValidatorState.STAKED,\n \"Validator not staked\"\n );\n\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key.\n // Also verify the validator's withdrawal credential points to the `withdrawalAddress`.\n IBeaconProofs(BEACON_PROOFS).verifyValidator(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n withdrawalAddress\n );\n\n // Store the validator state as verified\n validator[pubKeyHash] = ValidatorData({\n state: ValidatorState.VERIFIED,\n index: validatorIndex\n });\n\n // If the initial deposit was front-run and the withdrawal address is not this strategy\n if (withdrawalAddress != address(this)) {\n // override the validator state\n validator[pubKeyHash].state = ValidatorState.INVALID;\n\n // Find and remove the deposit as the funds can not be recovered\n uint256 depositCount = depositList.length;\n for (uint256 i = 0; i < depositCount; i++) {\n DepositData memory deposit = deposits[depositList[i]];\n if (deposit.pubKeyHash == pubKeyHash) {\n // next verifyBalances will correctly account for the loss of a front-run\n // deposit. Doing it here accounts for the loss as soon as possible\n lastVerifiedEthBalance -= Math.min(\n lastVerifiedEthBalance,\n uint256(deposit.amountGwei) * 1 gwei\n );\n _removeDeposit(depositList[i], deposit);\n break;\n }\n }\n\n // Leave the `firstDeposit` flag as true so no more deposits to unverified validators can be made.\n // The Governor has to reset the `firstDeposit` to false before another deposit to\n // an unverified validator can be made.\n // The Governor can set a new `validatorRegistrator` if they suspect it has been compromised.\n\n emit ValidatorInvalid(pubKeyHash);\n return;\n }\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(pubKeyHash);\n\n // Reset the firstDeposit flag as the first deposit to an unverified validator has been verified.\n firstDeposit = false;\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n struct FirstPendingDepositSlotProofData {\n uint64 slot;\n bytes proof;\n }\n\n struct StrategyValidatorProofData {\n uint64 withdrawableEpoch;\n bytes withdrawableEpochProof;\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n ///\n /// Important: this function has a limitation where `depositProcessedSlot` that is passed by the off-chain\n /// verifier requires a slot immediately after it to propose a block otherwise the `BeaconRoots.parentBlockRoot`\n /// will fail. This shouldn't be a problem, since by the current behaviour of beacon chain only 1%-3% slots\n /// don't propose a block.\n /// @param pendingDepositRoot The unique identifier of the deposit emitted in `ETHStaked` from\n /// the `stakeEth` function.\n /// @param depositProcessedSlot Any slot on or after the strategy's deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\n /// set for the next block timestamp in 12 seconds time.\n /// @param firstPendingDeposit a `FirstPendingDepositSlotProofData` struct containing:\n /// - slot: The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be any non-zero value if the deposit queue is empty.\n /// - proof: The merkle proof of the first pending deposit's slot to the beacon block root.\n /// Can be either:\n /// * 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// * 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @param strategyValidatorData a `StrategyValidatorProofData` struct containing:\n /// - withdrawableEpoch: The withdrawable epoch of the validator the strategy is depositing to.\n /// - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the validator the strategy\n /// is depositing to, to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n bytes32 pendingDepositRoot,\n uint64 depositProcessedSlot,\n FirstPendingDepositSlotProofData calldata firstPendingDeposit,\n StrategyValidatorProofData calldata strategyValidatorData\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[pendingDepositRoot];\n ValidatorData memory strategyValidator = validator[deposit.pubKeyHash];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(firstPendingDeposit.slot != 0, \"Zero 1st pending deposit slot\");\n\n // We should allow the verification of deposits for validators that have been marked as exiting\n // to cover this situation:\n // - there are 2 pending deposits\n // - beacon chain has slashed the validator\n // - when verifyDeposit is called for the first deposit it sets the Validator state to EXITING\n // - verifyDeposit should allow a secondary call for the other deposit to a slashed validator\n require(\n strategyValidator.state == ValidatorState.VERIFIED ||\n strategyValidator.state == ValidatorState.ACTIVE ||\n strategyValidator.state == ValidatorState.EXITING,\n \"Not verified/active/exiting\"\n );\n // The verification slot must be after the deposit's slot.\n // This is needed for when the deposit queue is empty.\n require(deposit.slot < depositProcessedSlot, \"Slot not after deposit\");\n\n uint64 snapTimestamp = snappedBalance.timestamp;\n\n // This check prevents an accounting error that can happen if:\n // - snapBalances are snapped at the time of T\n // - deposit is processed on the beacon chain after time T and before verifyBalances()\n // - verifyDeposit is called before verifyBalances which removes a deposit from depositList\n // and deposit balance from totalDepositsWei\n // - verifyBalances is called under-reporting the strategy's balance\n require(\n (_calcNextBlockTimestamp(depositProcessedSlot) <= snapTimestamp) ||\n snapTimestamp == 0,\n \"Deposit after balance snapshot\"\n );\n\n // Get the parent beacon block root of the next block which is the block root of the deposit verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 depositBlockRoot = BeaconRoots.parentBlockRoot(\n _calcNextBlockTimestamp(depositProcessedSlot)\n );\n\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n depositBlockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.proof\n );\n\n // Verify the withdrawableEpoch on the validator of the strategy's deposit\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n depositBlockRoot,\n strategyValidator.index,\n strategyValidatorData.withdrawableEpoch,\n strategyValidatorData.withdrawableEpochProof\n );\n\n uint64 firstPendingDepositEpoch = firstPendingDeposit.slot /\n SLOTS_PER_EPOCH;\n\n // Validator can either be not exiting and no further checks are required\n // Or a validator is exiting then this function needs to make sure that the\n // pending deposit to an exited validator has certainly been processed. The\n // slot/epoch of first pending deposit is the one that contains the transaction\n // where the deposit to the ETH Deposit Contract has been made.\n //\n // Once the firstPendingDepositEpoch becomes greater than the withdrawableEpoch of\n // the slashed validator then the deposit has certainly been processed. When the beacon\n // chain reaches the withdrawableEpoch of the validator the deposit will no longer be\n // postponed. And any new deposits created (and present in the deposit queue)\n // will have an equal or larger withdrawableEpoch.\n require(\n strategyValidatorData.withdrawableEpoch == FAR_FUTURE_EPOCH ||\n strategyValidatorData.withdrawableEpoch <=\n firstPendingDepositEpoch,\n \"Exit Deposit likely not proc.\"\n );\n\n // solhint-disable max-line-length\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the deposit queue is empty then our deposit must have been processed on the beacon chain.\n // The deposit slot can be zero for validators consolidating to a compounding validator or 0x01 validator\n // being promoted to a compounding one. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // We can not guarantee that the deposit has been processed in that case.\n // solhint-enable max-line-length\n require(\n deposit.slot < firstPendingDeposit.slot || isDepositQueueEmpty,\n \"Deposit likely not processed\"\n );\n\n // Remove the deposit now it has been verified as processed on the beacon chain.\n _removeDeposit(pendingDepositRoot, deposit);\n\n emit DepositVerified(\n pendingDepositRoot,\n uint256(deposit.amountGwei) * 1 gwei\n );\n }\n\n function _removeDeposit(\n bytes32 pendingDepositRoot,\n DepositData memory deposit\n ) internal {\n // After verifying the proof, update the contract storage\n deposits[pendingDepositRoot].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n bytes32 lastDeposit = depositList[depositList.length - 1];\n depositList[deposit.depositIndex] = lastDeposit;\n deposits[lastDeposit].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositList.pop();\n }\n\n /// @dev Calculates the timestamp of the next execution block from the given slot.\n /// @param slot The beacon chain slot number used for merkle proof verification.\n function _calcNextBlockTimestamp(uint64 slot)\n internal\n view\n returns (uint64)\n {\n // Calculate the next block timestamp from the slot.\n return SLOT_DURATION * slot + BEACON_GENESIS_TIMESTAMP + SLOT_DURATION;\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block and beacon block root\n /// of the slot that is associated with the previous block.\n ///\n /// When snapping / verifying balance it is of a high importance that there is no\n /// miss-match in respect to ETH that is held by the contract and balances that are\n /// verified on the validators.\n ///\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\n /// constructing a block on the beacon chain consist of:\n /// - process_withdrawals: ETH is deducted from the validator's balance\n /// - process_execution_payload: immediately after the previous step executing all the\n /// transactions\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\n /// contained in the withdrawal credentials of the exited validators\n ///\n /// That means that balance increases which are part of the post-block execution state are\n /// done within the block, but the transaction that are contained within that block can not\n /// see / interact with the balance from the exited validators. Only transactions in the\n /// next block can do that.\n ///\n /// When snap balances is performed the state of the chain is snapped across 2 separate\n /// chain states:\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\n /// will be referred to as Y - 1 as it makes no difference in the argument\n ///\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\n /// miss-counting ETH or much more dangerous double counting of the ETH.\n ///\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\n /// - ETH balance in the contract on block X\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\n /// - ETH balance in validators that are active in slot Y - 1\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\n /// and have their balance visible to transactions in slot Y and corresponding block X\n /// (or sooner)\n ///\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\n ///\n /// *ETH balance in the contract on block X*\n ///\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\n ///\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\n /// would result in double counting the `stakedEth` since it would be present once in the\n /// snapped contract balance and the second time in deposit storage variables.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\n ///\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\n /// contract at block Z. The execution layer doesn't have direct access to the state of\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\n /// and could also be part of the validator balances. It does that by verifying that at\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\n /// the last snap till now all are still in queue. Which ensures they can not be part of\n /// the validator balances in later steps.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in validators that are active in slot Y - 1*\n ///\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\n /// correctness.\n ///\n /// This behaviour is correct.\n ///\n /// *The withdrawn validators*\n ///\n /// The withdrawn validators could have their balances deducted in any slot before slot\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\n /// look at the \"worst case scenario\" where the validator withdrawal is processed in the\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\n /// even if the validator exits at the latest possible time it is paramount that the ETH\n /// balance on the execution layer is recorded in the next block. Correctly accounting\n /// for the withdrawn ETH.\n ///\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\n /// validator balance verification as well as execution layer contract balance snap.\n ///\n /// This behaviour is correct.\n ///\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n function snapBalances() external {\n uint64 currentTimestamp = SafeCast.toUint64(block.timestamp);\n require(\n snappedBalance.timestamp + SNAP_BALANCES_DELAY < currentTimestamp,\n \"Snap too soon\"\n );\n\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(currentTimestamp);\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the snapped balance\n snappedBalance = Balances({\n blockRoot: blockRoot,\n timestamp: currentTimestamp,\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n emit BalancesSnapped(blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct BalanceProofs {\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n struct PendingDepositProofs {\n bytes32 pendingDepositContainerRoot;\n bytes pendingDepositContainerProof;\n uint40[] pendingDepositIndexes;\n bytes[] pendingDepositProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks each of the strategy's deposits are still to be processed by the beacon chain.\n /// @param balanceProofs a `BalanceProofs` struct containing the following:\n /// - balancesContainerRoot: The merkle root of the balances container\n /// - balancesContainerProof: The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// - validatorBalanceLeaves: Array of leaf nodes containing the validator balance with three other balances.\n /// - validatorBalanceProofs: Array of merkle proofs for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param pendingDepositProofs a `PendingDepositProofs` struct containing the following:\n /// - pendingDepositContainerRoot: The merkle root of the pending deposits list container\n /// - pendingDepositContainerProof: The merkle proof from the pending deposits list container\n /// to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// - pendingDepositIndexes: Array of indexes in the pending deposits list container for each\n /// of the strategy's deposits.\n /// - pendingDepositProofs: Array of merkle proofs for each strategy deposit in the\n /// beacon chain's pending deposit list container to the pending deposits list container root.\n /// These are 28 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(\n BalanceProofs calldata balanceProofs,\n PendingDepositProofs calldata pendingDepositProofs\n ) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalance;\n // Check the balances are the latest\n require(balancesMem.timestamp > 0, \"No snapped balances\");\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n uint256 depositsCount = depositList.length;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n balanceProofs.validatorBalanceProofs.length ==\n verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n balanceProofs.validatorBalanceLeaves.length ==\n verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n balancesMem.blockRoot,\n balanceProofs.balancesContainerRoot,\n balanceProofs.balancesContainerProof\n );\n\n bytes32[]\n memory validatorHashesMem = _getPendingDepositValidatorHashes(\n depositsCount\n );\n\n // for each validator in reverse order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n ValidatorData memory validatorDataMem = validator[\n verifiedValidators[i]\n ];\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n balanceProofs.balancesContainerRoot,\n balanceProofs.validatorBalanceLeaves[i],\n balanceProofs.validatorBalanceProofs[i],\n validatorDataMem.index\n );\n\n bool depositPending = false;\n for (uint256 j = 0; j < validatorHashesMem.length; j++) {\n if (validatorHashesMem[j] == verifiedValidators[i]) {\n depositPending = true;\n break;\n }\n }\n\n // If validator has a pending deposit we can not remove due to\n // the following situation:\n // - validator has a pending deposit\n // - validator has been slashed\n // - sweep cycle has withdrawn all ETH from the validator. Balance is 0\n // - beacon chain has processed the deposit and set the validator balance\n // to deposit amount\n // - if validator is no longer in the list of verifiedValidators its\n // balance will not be considered and be under-counted.\n if (validatorBalanceGwei == 0 && !depositPending) {\n // Store the validator state as exited\n // This could have been in VERIFIED, ACTIVE or EXITING state\n validator[verifiedValidators[i]].state = ValidatorState\n .EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n } else if (\n validatorDataMem.state == ValidatorState.VERIFIED &&\n validatorBalanceGwei >= MIN_ACTIVATION_BALANCE_GWEI\n ) {\n validator[verifiedValidators[i]].state = ValidatorState\n .ACTIVE;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += validatorBalanceGwei * 1 gwei;\n }\n }\n\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification.\n // This section is after the validator balance verifications so an exited validator will be marked\n // as EXITED before the deposits are verified. If there was a deposit to an exited validator\n // then the deposit can only be removed once the validator is fully exited.\n // It is possible that validator fully exits and a postponed deposit to an exited validator increases\n // its balance again. In such case the contract will erroneously consider a deposit applied before it\n // has been applied on the beacon chain showing a smaller than real `totalValidatorBalance`.\n if (depositsCount > 0) {\n require(\n pendingDepositProofs.pendingDepositProofs.length ==\n depositsCount,\n \"Invalid deposit proofs\"\n );\n require(\n pendingDepositProofs.pendingDepositIndexes.length ==\n depositsCount,\n \"Invalid deposit indexes\"\n );\n\n // Verify from the root of the pending deposit list container to the beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyPendingDepositsContainer(\n balancesMem.blockRoot,\n pendingDepositProofs.pendingDepositContainerRoot,\n pendingDepositProofs.pendingDepositContainerProof\n );\n\n // For each staking strategy's deposit.\n for (uint256 i = 0; i < depositsCount; ++i) {\n bytes32 pendingDepositRoot = depositList[i];\n\n // Verify the strategy's deposit is still pending on the beacon chain.\n IBeaconProofs(BEACON_PROOFS).verifyPendingDeposit(\n pendingDepositProofs.pendingDepositContainerRoot,\n pendingDepositRoot,\n pendingDepositProofs.pendingDepositProofs[i],\n pendingDepositProofs.pendingDepositIndexes[i]\n );\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei +=\n uint256(deposits[pendingDepositRoot].amountGwei) *\n 1 gwei;\n }\n }\n\n // Store the verified balance in storage\n lastVerifiedEthBalance =\n totalDepositsWei +\n totalValidatorBalance +\n balancesMem.ethBalance;\n // Reset the last snap timestamp so a new snapBalances has to be made\n snappedBalance.timestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice get a list of all validator hashes present in the pending deposits\n /// list can have duplicate entries\n function _getPendingDepositValidatorHashes(uint256 depositsCount)\n internal\n view\n returns (bytes32[] memory validatorHashes)\n {\n validatorHashes = new bytes32[](depositsCount);\n for (uint256 i = 0; i < depositsCount; i++) {\n validatorHashes[i] = deposits[depositList[i]].pubKeyHash;\n }\n }\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n require(pubKey.length == 48, \"Invalid public key\");\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /**\n *\n * WETH and ETH Accounting\n *\n */\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n // The min is required as more WETH can be withdrawn than deposited\n // as the strategy earns consensus and execution rewards.\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance.\n // The ETH balance in this strategy contract can be more than the last verified ETH balance\n // due to partial withdrawals or full exits being processed by the beacon chain since the last snapBalances.\n // It can also happen from execution rewards (MEV) or ETH donations.\n lastVerifiedEthBalance -= Math.min(lastVerifiedEthBalance, _ethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n snappedBalance.timestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += _wethAmount;\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n snappedBalance.timestamp = 0;\n }\n\n /**\n *\n * View Functions\n *\n */\n\n /// @notice Returns the number of deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n function depositListLength() external view returns (uint256) {\n return depositList.length;\n }\n\n /// @notice Returns the number of verified validators.\n function verifiedValidatorsLength() external view returns (uint256) {\n return verifiedValidators.length;\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/96086a45653be1c24ccc04a751597ca0.json b/contracts/deployments/hoodi/solcInputs/96086a45653be1c24ccc04a751597ca0.json new file mode 100644 index 0000000000..44faca2eaa --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/96086a45653be1c24ccc04a751597ca0.json @@ -0,0 +1,99 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the Beacon Block Root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Block Root Oracle to get the parent block root\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n enum BalanceProofLevel {\n Container,\n BeaconBlock\n }\n\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view;\n\n function verifyBlockNumber(\n bytes32 beaconBlockRoot,\n uint256 blockNumber,\n bytes calldata blockNumberProof\n ) external view;\n\n function verifySlot(\n bytes32 beaconBlockRoot,\n uint256 slot,\n bytes calldata slotProof\n ) external view;\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n external\n pure\n returns (uint256);\n\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) external pure returns (uint256);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconProofs,\n _beaconGenesisTimestamp\n )\n {\n SSV_TOKEN = _ssvToken;\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// @param _rewardTokenAddresses Not used so empty array\n /// @param _assets Not used so empty array\n /// @param _pTokens Not used so empty array\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH that was transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _asset, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(_asset, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, WETH, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Accounts for all the assets managed by this strategy which includes:\n /// 1. The current WETH in this strategy contract\n /// 2. The last verified ETH balance, total deposits and total validator balances\n /// @param _asset Address of WETH asset.\n /// @return balance Total value in ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by the strategy.\n /// @param _asset The address of the WETH token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for validator registration.\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens when validators are registered\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n * The new ETH will be accounted for in `checkBalance` after the next snapBalances and verifyBalances txs.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n /// @dev This strategy does not use a platform token like the old Aave and Compound strategies.\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instead of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract.\n /// Withdrawals from validators also accumulate as ETH in this strategy contract.\n /// It's too complex to separate the rewards from withdrawals so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can now regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable {\n using SafeERC20 for IERC20;\n\n /// @notice The amount of ETH in wei that is required for a deposit to a new validator.\n /// Initially this is 32 ETH, but will be increased to 1 ETH after P2P's APIs have been updated\n /// to support deposits of 1 ETH.\n uint256 public constant DEPOSIT_AMOUNT_WEI = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n /// @notice The timestamp of the Beacon chain genesis.\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\n uint64 public immutable BEACON_GENESIS_TIMESTAMP;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// Deposit data for new compounding validators.\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountWei Amount of ETH in wei that has been deposited to the beacon chain deposit contract\n /// @param blockNumber Block number when the deposit was made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either PENDING or PROVEN\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint32 depositIndex;\n DepositStatus status;\n }\n /// @notice Mapping of the root of a deposit (depositDataRoot) to its data\n mapping(bytes32 => DepositData) public deposits;\n /// @notice List of deposit roots that are still to be verified as processed on the beacon chain\n bytes32[] public depositsRoots;\n\n // Validator data\n\n struct ValidatorData {\n bytes32 pubKeyHash; // Hash of the validator's public key using the Beacon Chain's format\n uint64 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes and indexes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n ValidatorData[] internal verifiedValidators;\n /// @notice State of the new compounding validators with a 0x02 withdrawal credential prefix.\n /// Uses the Beacon chain hashing for BLSPubkey which is\n /// sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => VALIDATOR_STATE) public validatorState;\n\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n uint64 timestamp;\n uint128 ethBalance;\n }\n // TODO is it more efficient to use the block root rather than hashing it?\n /// @notice Mapping of the block root to the balances at that slot\n mapping(bytes32 => Balances) public snappedBalances;\n uint64 public lastSnapTimestamp;\n uint128 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[50] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITED, // The validator has been verified to have a zero balance\n REMOVED // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified and is ready to be staked\n }\n\n event RegistratorChanged(address indexed newAddress);\n event SourceStrategyAdded(address indexed strategy);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n bytes32 indexed depositDataRoot,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint64 indexed validatorIndex\n );\n event DepositVerified(bytes32 indexed depositDataRoot, uint256 amountWei);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(\n uint256 indexed timestamp,\n bytes32 indexed blockRoot,\n uint256 ethBalance\n );\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 wethBalance,\n uint256 ethBalance\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_PROOFS = _beaconProofs;\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\n\n require(\n block.timestamp > _beaconGenesisTimestamp,\n \"Invalid genesis timestamp\"\n );\n }\n\n /***************************************\n Admin Functions\n ****************************************/\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /***************************************`\n Validator Management\n ****************************************/\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validatorState[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validator validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validator,\n uint64 depositAmountGwei\n ) external onlyRegistrator {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validator.pubkey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == VALIDATOR_STATE.REGISTERED ||\n currentState == VALIDATOR_STATE.VERIFIED),\n \"Not registered or verified\"\n );\n require(\n currentState == VALIDATOR_STATE.VERIFIED ||\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validator.pubkey,\n withdrawalCredentials,\n validator.signature,\n validator.depositDataRoot\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == VALIDATOR_STATE.REGISTERED) {\n validatorState[pubKeyHash] = VALIDATOR_STATE.STAKED;\n }\n\n /// After the Pectra upgrade the validators have a new restriction when proposing\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\n /// forward. Each slot is created at strict 12 second intervals and those slots can\n /// either have blocks attached to them or not. This way using the block.timestamp\n /// the slot number can easily be calculated.\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\n BEACON_GENESIS_TIMESTAMP) / 12;\n\n // Store the deposit data for verifyDeposit and verifyBalances\n deposits[validator.depositDataRoot] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n slot: depositSlot,\n depositIndex: SafeCast.toUint32(depositsRoots.length),\n status: DepositStatus.PENDING\n });\n depositsRoots.push(validator.depositDataRoot);\n\n emit ETHStaked(\n pubKeyHash,\n validator.depositDataRoot,\n validator.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then the request will fail.\n /// Only the Registrator can call this function.\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\n /// If no ETH balance, the tx will revert.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n payable\n onlyRegistrator\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove a sweeping validator from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == VALIDATOR_STATE.EXITED ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exited\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validatorState[pubKeyHash] = VALIDATOR_STATE.REMOVED;\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /***************************************\n SSV Management\n ****************************************/\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n Beacon Chain Proofs\n ****************************************/\n\n /// @notice Verifies a validator's index to its public key.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param validatorPubKeyProof The merkle proof that the validator's index matches its public key\n /// on the beacon chain.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.STAKED,\n \"Validator not staked\"\n );\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key.\n // Also verify the validator's withdrawal credential points to this strategy.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n address(this) // Withdrawal address is this strategy\n );\n\n // Store the validator state as verified\n validatorState[pubKeyHash] = VALIDATOR_STATE.VERIFIED;\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(\n ValidatorData({ pubKeyHash: pubKeyHash, index: validatorIndex })\n );\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n ///\n /// Important: this function has a limitation where the `verificationSlot` that is passed by the off-chain\n /// verifier requires a slot immediately after it to propose a block otherwise the `BeaconRoots.parentBlockRoot`\n /// will fail. This shouldn't be a problem, since by the current behaviour of beacon chain only 1%-3% slots\n /// don't propose a block.\n /// @param depositDataRoot The root of the deposit data that was stored when\n /// the deposit was made on the execution layer.\n /// @param verificationSlot Any slot on or after the deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\n /// set for the next block timestamp in 12 seconds time.\n /// @param firstPendingDepositSlot The slot of the first pending deposit in the beacon chain\n /// @param firstPendingDepositSlotProof The merkle proof that the slot of the first pending deposit\n /// matches the beacon chain.\n /// BeaconBlock.BeaconBlockBody.deposits[0].slot\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n bytes32 depositDataRoot,\n uint64 verificationSlot,\n uint64 firstPendingDepositSlot,\n bytes calldata firstPendingDepositSlotProof\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositDataRoot];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n validatorState[deposit.pubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the first pending deposit's slot is 0 then there are no pending deposits so\n // our deposit must have been processed on the beacon chain.\n require(\n deposit.slot < firstPendingDepositSlot ||\n firstPendingDepositSlot == 0,\n \"Deposit not processed\"\n );\n\n // The verification slot must be after the deposit's slot.\n // This is needed for when the deposit queue is empty. That is, firstPendingDepositSlot is 0.\n require(deposit.slot < verificationSlot, \"Slot not after deposit\");\n\n // Calculate the next block timestamp from the verification slot.\n uint64 nextBlockTimestamp = 12 *\n verificationSlot +\n BEACON_GENESIS_TIMESTAMP +\n // Add 12 seconds for the next block\n 12;\n // Get the parent beacon block root of the next block which is the block root of the verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the slot of the first pending deposit matches the beacon chain\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n blockRoot,\n firstPendingDepositSlot,\n firstPendingDepositSlotProof\n );\n\n // After verifying the proof, update the contract storage\n deposits[depositDataRoot].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n bytes32 lastDepositDataRoot = depositsRoots[depositsRoots.length - 1];\n depositsRoots[deposit.depositIndex] = lastDepositDataRoot;\n deposits[lastDepositDataRoot].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositsRoots.pop();\n\n emit DepositVerified(\n depositDataRoot,\n uint256(deposit.amountGwei) * 1 gwei\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block and beacon block root\n /// of the slot that is associated with the previous block.\n ///\n /// When snapping / verifying balance it is of a high importance that there is no\n /// miss-match in respect to ETH that is held by the contract and balances that are\n /// verified on the validators.\n ///\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\n /// constructing a block on the beacon chain consist of:\n /// - process_withdrawals: ETH is deducted from the validator's balance\n /// - process_execution_payload: immediately after the previous step executing all the\n /// transactions\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\n /// contained in the withdrawal credentials of the exited validators\n ///\n /// That means that balance increases which are part of the post-block execution state are\n /// done within the block, but the transaction that are contained within that block can not\n /// see / interact with the balance from the exited validators. Only transactions in the\n /// next block can do that.\n ///\n /// When snap balances is performed the state of the chain is snapped across 2 separate\n /// chain states:\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\n /// will be referred to as Y - 1 as it makes no difference in the argument\n ///\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\n /// miss-counting ETH or much more dangerous double counting of the ETH.\n ///\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\n /// - ETH balance in the contract on block X\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\n /// - ETH balance in validators that are active in slot Y - 1\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\n /// and have their balance visible to transactions in slot Y and corresponding block X\n /// (or sooner)\n ///\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\n ///\n /// *ETH balance in the contract on block X*\n ///\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\n ///\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\n /// would result in double counting the `stakedEth` since it would be present once in the\n /// snapped contract balance and the second time in deposit storage variables.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\n ///\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\n /// contract at block Z. The execution layer doesn't have direct access to the state of\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\n /// and could also be part of the validator balances. It does that by verifying that at\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\n /// the last snap till now all are still in queue. Which ensures they can not be part of\n /// the validator balances in later steps.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in validators that are active in slot Y - 1*\n ///\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\n /// correctness.\n ///\n /// This behaviour is correct.\n ///\n /// *The withdrawn validators*\n ///\n /// The withdrawn validators could have their balances deducted in any slot before slot\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\n /// look at the \"worst case scenario\" where the validator withdrawal is processed in the\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\n /// even if the validator exits at the latest possible time it is paramount that the ETH\n /// balance on the execution layer is recorded in the next block. Correctly accounting\n /// for the withdrawn ETH.\n ///\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\n /// validator balance verification as well as execution layer contract balance snap.\n ///\n /// This behaviour is correct.\n ///\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n /// Can only be called by the registrator.\n function snapBalances() external onlyRegistrator {\n _snapBalances();\n }\n\n function _snapBalances() internal {\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(\n SafeCast.toUint64(block.timestamp)\n );\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the balances in the mapping\n snappedBalances[blockRoot] = Balances({\n timestamp: SafeCast.toUint64(block.timestamp),\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n // Store the snapped timestamp\n lastSnapTimestamp = SafeCast.toUint64(block.timestamp);\n\n emit BalancesSnapped(block.timestamp, blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct VerifyBalancesParams {\n bytes32 blockRoot;\n uint64 firstPendingDepositSlot;\n // BeaconBlock.BeaconBlockBody.deposits[0].slot\n bytes firstPendingDepositSlotProof;\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(VerifyBalancesParams calldata params) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalances[params.blockRoot];\n // Check the balances are the latest\n require(lastSnapTimestamp > 0, \"No snapped balances\");\n require(balancesMem.timestamp == lastSnapTimestamp, \"Stale snap\");\n\n uint256 depositsCount = depositsRoots.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit to the beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n params.blockRoot,\n params.firstPendingDepositSlot,\n params.firstPendingDepositSlotProof\n );\n\n // For each native staking contract's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n bytes32 depositDataRoot = depositsRoots[i];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit slot is before the\n // slot of the staking strategy's deposit.\n // If the deposit has been processed, it will need to be verified with `verifyDeposit`\n require(\n params.firstPendingDepositSlot <\n deposits[depositDataRoot].slot,\n \"Deposit has been processed\"\n );\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei +=\n uint256(deposits[depositDataRoot].amountGwei) *\n 1 gwei;\n }\n }\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n params.validatorBalanceProofs.length == verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n params.validatorBalanceLeaves.length == verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n params.blockRoot,\n params.balancesContainerRoot,\n params.balancesContainerProof\n );\n\n // for each validator in reserve order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n params.balancesContainerRoot,\n params.validatorBalanceLeaves[i],\n params.validatorBalanceProofs[i],\n verifiedValidators[i].index\n );\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n validatorState[\n verifiedValidators[i].pubKeyHash\n ] = VALIDATOR_STATE.EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += uint256(validatorBalanceGwei) * 1 gwei;\n }\n }\n\n uint256 wethBalance = IWETH9(WETH).balanceOf(address(this));\n\n // Store the verified balance in storage\n lastVerifiedEthBalance = SafeCast.toUint128(\n totalDepositsWei + totalValidatorBalance + balancesMem.ethBalance\n );\n // Reset the last snap timestamp so a new snapBalances has to be made\n lastSnapTimestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n wethBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /***************************************\n WETH and ETH Accounting\n ****************************************/\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance\n if (lastVerifiedEthBalance > _ethAmount) {\n lastVerifiedEthBalance -= SafeCast.toUint128(_ethAmount);\n } else {\n // This can happen if all ETH in the validators was withdrawn\n // and there was more consensus rewards since the last balance verification.\n // Or it can happen if ETH is donated to this strategy.\n lastVerifiedEthBalance = 0;\n }\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += SafeCast.toUint128(_wethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /***************************************\n View Functions\n ****************************************/\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return An array of `ValidatorData` containing the public key hash and validator index.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorData[] memory)\n {\n return verifiedValidators;\n }\n\n struct DepositView {\n bytes32 root;\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n }\n\n /// @notice Returns the deposits that are still to be verified.\n /// These may or may not have been processed by the beacon chain.\n /// @return pendingDeposits An array of `DepositView` containing the deposit root, public key hash,\n /// amount in Gwei and the slot of the deposit.\n function getPendingDeposits()\n external\n view\n returns (DepositView[] memory pendingDeposits)\n {\n pendingDeposits = new DepositView[](depositsRoots.length);\n for (uint256 i = 0; i < depositsRoots.length; ++i) {\n DepositData memory deposit = deposits[depositsRoots[i]];\n pendingDeposits[i] = DepositView({\n root: depositsRoots[i],\n pubKeyHash: deposit.pubKeyHash,\n amountGwei: deposit.amountGwei,\n slot: deposit.slot\n });\n }\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/a4cf2397e59aecfd08078ec66ccb9b2b.json b/contracts/deployments/hoodi/solcInputs/a4cf2397e59aecfd08078ec66ccb9b2b.json new file mode 100644 index 0000000000..4795819ebd --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/a4cf2397e59aecfd08078ec66ccb9b2b.json @@ -0,0 +1,117 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/beacon/BeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"./BeaconProofsLib.sol\";\nimport { IBeaconProofs } from \"../interfaces/IBeaconProofs.sol\";\n\n/**\n * @title Verifies merkle proofs of beacon chain data.\n * @author Origin Protocol Inc\n */\ncontract BeaconProofs is IBeaconProofs {\n /// @notice Verifies the validator index is for the given validator public key.\n /// Also verify the validator's withdrawal credential points to the withdrawal address.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param proof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata proof,\n uint40 validatorIndex,\n address withdrawalAddress\n ) external view {\n BeaconProofsLib.verifyValidator(\n beaconBlockRoot,\n pubKeyHash,\n proof,\n validatorIndex,\n withdrawalAddress\n );\n }\n\n /// @notice Verifies a validator's withdrawable epoch to the beacon block root\n /// for a given validator index.\n /// Also verifies the validator's public key for the given validator index.\n /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n /// @param beaconBlockRoot The root of the beacon block\n /// @param validatorIndex The validator index to verify the withdrawable epoch for.\n /// @param withdrawableEpoch The withdrawable epoch to verify in big endian uint64 format\n /// @param withdrawableEpochProof The merkle proof for the validator's withdrawable epoch to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorPubKeyProof The merkle proof for the validator public key in a sub tree of height two.\n /// This is 2 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n bytes32 pubKeyHash,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof,\n bytes calldata validatorPubKeyProof\n ) external view {\n BeaconProofsLib.verifyValidatorWithdrawableEpoch(\n beaconBlockRoot,\n validatorIndex,\n withdrawableEpoch,\n withdrawableEpochProof\n );\n\n // Get the third 32 byte witness from the withdrawable epoch proof\n // 2 * 32 bytes = 64 bytes offset\n bytes32 subTreeRoot = bytes32(withdrawableEpochProof[64:96]);\n\n BeaconProofsLib.verifyValidatorPubKeySubTree(\n subTreeRoot,\n pubKeyHash,\n validatorPubKeyProof\n );\n }\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view {\n BeaconProofsLib.verifyValidatorWithdrawableEpoch(\n beaconBlockRoot,\n validatorIndex,\n withdrawableEpoch,\n withdrawableEpochProof\n );\n }\n\n /// @notice Verifies the balances container to the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerRoot The merkle root of the the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata balancesContainerProof\n ) external view {\n BeaconProofsLib.verifyBalancesContainer(\n beaconBlockRoot,\n balancesContainerRoot,\n balancesContainerProof\n );\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param balanceProof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint40 validatorIndex\n ) external view returns (uint256 validatorBalanceGwei) {\n validatorBalanceGwei = BeaconProofsLib.verifyValidatorBalance(\n balancesContainerRoot,\n validatorBalanceLeaf,\n balanceProof,\n validatorIndex\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the pubKey and slot of the first pending deposit to the beacon block root.\n /// BeaconBlock.state.PendingDeposits[0].pubKey\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero would be a good choice.\n /// @param pubKeyHash The hash of the validator public key for the first pending deposit.\n /// Use zero bytes if the deposit queue is empty.\n /// @param firstPendingDepositPubKeyProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].pubKey when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes32 pubKeyHash,\n bytes calldata firstPendingDepositPubKeyProof\n ) external view returns (bool isEmptyDepositQueue) {\n isEmptyDepositQueue = BeaconProofsLib.verifyFirstPendingDeposit(\n beaconBlockRoot,\n slot,\n pubKeyHash,\n firstPendingDepositPubKeyProof\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the slot of the first pending deposit to the beacon block root.\n /// BeaconBlock.state.pendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero would be a good choice.\n /// @param firstPendingDepositSlotProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view returns (bool isEmptyDepositQueue) {\n isEmptyDepositQueue = BeaconProofsLib.verifyFirstPendingDeposit(\n beaconBlockRoot,\n slot,\n firstPendingDepositSlotProof\n );\n }\n}\n" + }, + "contracts/beacon/BeaconProofsLib.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Merkle } from \"./Merkle.sol\";\nimport { Endian } from \"./Endian.sol\";\n\n/**\n * @title Library to verify merkle proofs of beacon chain data.\n * @author Origin Protocol Inc\n */\nlibrary BeaconProofsLib {\n // Known generalized indices in the beacon block\n /// @dev BeaconBlock.state.PendingDeposits[0]\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, pending deposits at index 34\n /// Pending deposits container: height 28, first deposit at index 0\n /// ((2 ^ 3 + 3) * 2 ^ 6 + 34) * 2 ^ 28 + 0 = 198105366528\n uint256 internal constant FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX =\n 198105366528;\n /// @dev BeaconBlock.state.PendingDeposits[0].pubkey\n /// Pending Deposit container: height 3, pubkey at index 0\n /// (((2 ^ 3 + 3) * 2 ^ 6 + 34) * 2 ^ 28 + 0) * 2 ^ 3 + 0 = 1584842932224\n uint256 internal constant FIRST_PENDING_DEPOSIT_PUBKEY_GENERALIZED_INDEX =\n 1584842932224;\n /// @dev BeaconBlock.state.PendingDeposits[0].pubkey\n /// Pending Deposit container: height 3, pubkey at index 4\n /// (((2 ^ 3 + 3) * 2 ^ 6 + 34) * 2 ^ 28 + 0) * 2 ^ 3 + 4 = 1584842932228\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX =\n 1584842932228;\n /// @dev BeaconBlock.state.validators\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, validators at index 11\n /// (2 ^ 3 + 3) * 2 ^ 6 + 11 = 715\n uint256 internal constant VALIDATORS_CONTAINER_GENERALIZED_INDEX = 715;\n /// @dev BeaconBlock.state.balances\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, balances at index 13\n /// (2 ^ 3 + 3) * 2 ^ 6 + 13 = 716\n uint256 internal constant BALANCES_CONTAINER_GENERALIZED_INDEX = 716;\n\n /// @dev Number of bytes in the proof to the first pending deposit.\n /// 37 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0]\n /// 37 * 32 bytes = 1184 bytes\n uint256 internal constant FIRST_PENDING_DEPOSIT_PROOF_LENGTH = 1184;\n /// @dev Number of bytes in the proof from the pubKey of the first pending deposit to the beacon block root.\n /// 40 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0].pubKey\n /// 40 * 32 bytes = 1280 bytes\n uint256 internal constant FIRST_PENDING_DEPOSIT_PUBKEY_PROOF_LENGTH = 1280;\n /// @dev Number of bytes in the proof from the slot of the first pending deposit to the beacon block root.\n /// 40 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0].slot\n /// 40 * 32 bytes = 1280 bytes\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_PROOF_LENGTH = 1280;\n /// The slot is at index 4 in the Pending Deposits container.\n /// The sub tree from the right node from the root is a tree of height 2.\n /// The first 32 bytes witness is an empty bytes32 as there are\n /// no items after the slot in the Pending Deposits container.\n /// The second 32 bytes witness is a hash or two empty bytes32.\n bytes internal constant PENDING_DEPOSIT_SLOT_PROOF =\n // solhint-disable-next-line max-line-length\n hex\"0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b\";\n\n /// @dev Merkle height of the Balances container\n /// BeaconBlock.state.balances\n uint256 internal constant BALANCES_HEIGHT = 39;\n /// @dev Merkle height of the Validators container list\n /// BeaconBlock.state.validators\n uint256 internal constant VALIDATORS_LIST_HEIGHT = 41;\n /// @dev Merkle height of the Validator container\n /// BeaconBlock.state.validators[validatorIndex]\n uint256 internal constant VALIDATOR_CONTAINER_HEIGHT = 3;\n\n /// @dev Position of the pubkey field in the Validator container.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;\n /// @dev Position of the withdrawable epoch field in the Validator container.\n /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n uint256 internal constant VALIDATOR_WITHDRAWABLE_EPOCH_INDEX = 7;\n\n /// @notice Verifies the validator index is for the given validator public key.\n /// Also verify the validator's withdrawal credential points to the withdrawal address.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param proof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata proof,\n uint40 validatorIndex,\n address withdrawalAddress\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.validators[validatorIndex]\n uint256 generalizedIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_LIST_HEIGHT,\n validatorIndex\n );\n // BeaconBlock.state.validators[validatorIndex].pubkey\n generalizedIndex = concatGenIndices(\n generalizedIndex,\n VALIDATOR_CONTAINER_HEIGHT,\n VALIDATOR_PUBKEY_INDEX\n );\n\n // Get the withdrawal address from the first witness in the pubkey merkle proof.\n address withdrawalAddressFromProof;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // The first 32 bytes of the proof is the withdrawal credential so load it into memory.\n calldatacopy(0, proof.offset, 32)\n // Cast the 32 bytes in memory to an address which is the last 20 bytes.\n withdrawalAddressFromProof := mload(0)\n }\n require(\n withdrawalAddressFromProof == withdrawalAddress,\n \"Invalid withdrawal address\"\n );\n require(proof[0] == 0x02, \"Invalid validator type\");\n\n require(\n // 53 * 32 bytes = 1696 bytes\n proof.length == 1696 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: pubKeyHash,\n index: generalizedIndex\n }),\n \"Invalid validator proof\"\n );\n }\n\n /// @notice Verifies a validator's withdrawable epoch to the beacon block root\n /// for a given validator index.\n /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n /// @param beaconBlockRoot The root of the beacon block\n /// @param validatorIndex The validator index to verify the withdrawable epoch for.\n /// @param withdrawableEpoch The withdrawable epoch to verify in big endian uint64 format\n /// @param proof The merkle proof for the validator's withdrawable epoch to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyValidatorWithdrawableEpoch(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata proof\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.validators[validatorIndex]\n uint256 exitEpochGenIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_LIST_HEIGHT,\n validatorIndex\n );\n // BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n exitEpochGenIndex = concatGenIndices(\n exitEpochGenIndex,\n VALIDATOR_CONTAINER_HEIGHT,\n VALIDATOR_WITHDRAWABLE_EPOCH_INDEX\n );\n\n require(\n // 53 * 32 bytes = 1696 bytes\n proof.length == 1696 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: Endian.toLittleEndianUint64(withdrawableEpoch),\n index: exitEpochGenIndex\n }),\n \"Invalid withdrawable proof\"\n );\n }\n\n /// @param subTreeRoot The third 32 byte witness from the withdrawable epoch proof\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param proof The merkle proof for the validator public key in a sub tree of height two.\n /// This is 2 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyValidatorPubKeySubTree(\n bytes32 subTreeRoot,\n bytes32 pubKeyHash,\n bytes calldata proof\n ) internal view {\n // Tree height 2 and pub key is at index 0\n // index = 2 ^ 2 + 0 = 4\n require(\n // 2 * 32 bytes = 64 bytes\n proof.length == 64 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: subTreeRoot,\n leaf: pubKeyHash,\n index: 4\n }),\n \"Invalid pub key proof\"\n );\n }\n\n /// @notice Verifies the balances container to the beacon block root.\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param balancesContainerRoot The merkle root of the the balances container.\n /// @param proof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata proof\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.balances\n require(\n // 9 * 32 bytes = 288 bytes\n proof.length == 288 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: balancesContainerRoot,\n index: BALANCES_CONTAINER_GENERALIZED_INDEX\n }),\n \"Invalid balance container proof\"\n );\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param proof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for.\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index.\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata proof,\n uint40 validatorIndex\n ) internal view returns (uint256 validatorBalanceGwei) {\n require(balancesContainerRoot != bytes32(0), \"Invalid container root\");\n\n // Four balances are stored in each leaf so the validator index is divided by 4\n uint64 balanceIndex = validatorIndex / 4;\n\n // Get the index within the balances container, not the Beacon Block\n // BeaconBlock.state.balances[balanceIndex]\n uint256 generalizedIndex = concatGenIndices(\n 1,\n BALANCES_HEIGHT,\n balanceIndex\n );\n\n validatorBalanceGwei = balanceAtIndex(\n validatorBalanceLeaf,\n validatorIndex\n );\n\n require(\n // 39 * 32 bytes = 1248 bytes\n proof.length == 1248 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: balancesContainerRoot,\n leaf: validatorBalanceLeaf,\n index: generalizedIndex\n }),\n \"Invalid balance proof\"\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the pubKey and slot of the first pending deposit to the beacon block root.\n /// BeaconBlock.state.pendingDeposits[0].pubKey\n /// BeaconBlock.state.pendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero would be a good choice.\n /// @param pubKeyHash The hash of the validator public key for the first pending deposit.\n /// Use zero bytes if the deposit queue is empty.\n /// @param proof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].pubKey when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes32 pubKeyHash,\n bytes calldata proof\n ) internal view returns (bool isEmptyDepositQueue) {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // If the deposit queue is empty\n if (proof.length == FIRST_PENDING_DEPOSIT_PROOF_LENGTH) {\n require(\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: bytes32(0),\n index: FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX\n }),\n \"Invalid empty deposits proof\"\n );\n return true;\n }\n\n // Verify the public key of the first pending deposit\n // BeaconBlock.state.PendingDeposits[0].pubKey\n require(\n proof.length == FIRST_PENDING_DEPOSIT_PUBKEY_PROOF_LENGTH &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: pubKeyHash,\n index: FIRST_PENDING_DEPOSIT_PUBKEY_GENERALIZED_INDEX\n }),\n \"Invalid deposit pub key proof\"\n );\n\n // Now verify the slot of the first pending deposit\n\n // Get the third 32 bytes witness from the first pending deposit pubKey proof\n // 2 * 32 bytes = 64 bytes offset\n bytes32 slotRoot = bytes32(proof[64:96]);\n\n // Sub tree height 2 and slot is at index 0 in the sub tree\n // index = 2 ^ 2 + 0 = 4\n require(\n Merkle.verifyInclusionSha256({\n proof: PENDING_DEPOSIT_SLOT_PROOF,\n root: slotRoot,\n leaf: Endian.toLittleEndianUint64(slot),\n index: 4\n }),\n \"Invalid deposit slot\"\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the slot of the first pending deposit to the beacon block root.\n /// BeaconBlock.state.pendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero would be a good choice.\n /// @param proof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata proof\n ) internal view returns (bool isEmptyDepositQueue) {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // If the deposit queue is empty\n if (proof.length == FIRST_PENDING_DEPOSIT_PROOF_LENGTH) {\n require(\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: bytes32(0),\n index: FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX\n }),\n \"Invalid empty deposits proof\"\n );\n return true;\n }\n\n // Verify the public key of the first pending deposit\n // BeaconBlock.state.PendingDeposits[0].slot\n require(\n proof.length == FIRST_PENDING_DEPOSIT_SLOT_PROOF_LENGTH &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: Endian.toLittleEndianUint64(slot),\n index: FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX\n }),\n \"Invalid deposit slot proof\"\n );\n }\n\n ////////////////////////////////////////////////////\n /// Internal Helper Functions\n ////////////////////////////////////////////////////\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint40 validatorIndex)\n internal\n pure\n returns (uint256)\n {\n uint256 bitShiftAmount = (validatorIndex % 4) * 64;\n return\n Endian.fromLittleEndianUint64(\n bytes32((uint256(validatorBalanceLeaf) << bitShiftAmount))\n );\n }\n\n /// @notice Concatenates two beacon chain generalized indices into one.\n /// @param genIndex The first generalized index or 1 if calculating for a single container.\n /// @param height The merkle tree height of the second container. eg 39 for balances, 41 for validators.\n /// @param index The index within the second container. eg the validator index.\n /// @return genIndex The concatenated generalized index.\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) internal pure returns (uint256) {\n return (genIndex << height) | index;\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to retrieve beacon block roots.\n * @author Origin Protocol Inc\n */\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the beacon block root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Roots contract to get the parent block root.\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/Endian.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n/**\n * @title Library to handle conversion between little-endian and big-endian formats.\n * @author Origin Protocol Inc\n */\nlibrary Endian {\n /**\n * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64\n * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type\n * @return n The big endian-formatted uint64\n * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits),\n * but it is immediately truncated to a uint64 (i.e. 64 bits)\n * through a right-shift/shr operation.\n */\n function fromLittleEndianUint64(bytes32 lenum)\n internal\n pure\n returns (uint64 n)\n {\n // the number needs to be stored in little-endian encoding (ie in bytes 0-8)\n n = uint64(uint256(lenum >> 192));\n // forgefmt: disable-next-item\n return\n (n >> 56) |\n ((0x00FF000000000000 & n) >> 40) |\n ((0x0000FF0000000000 & n) >> 24) |\n ((0x000000FF00000000 & n) >> 8) |\n ((0x00000000FF000000 & n) << 8) |\n ((0x0000000000FF0000 & n) << 24) |\n ((0x000000000000FF00 & n) << 40) |\n ((0x00000000000000FF & n) << 56);\n }\n\n function toLittleEndianUint64(uint64 benum)\n internal\n pure\n returns (bytes32 n)\n {\n // Convert to little-endian by reversing byte order\n uint64 reversed = (benum >> 56) |\n ((0x00FF000000000000 & benum) >> 40) |\n ((0x0000FF0000000000 & benum) >> 24) |\n ((0x000000FF00000000 & benum) >> 8) |\n ((0x00000000FF000000 & benum) << 8) |\n ((0x0000000000FF0000 & benum) << 24) |\n ((0x000000000000FF00 & benum) << 40) |\n ((0x00000000000000FF & benum) << 56);\n\n // Store the little-endian uint64 in the least significant 64 bits of bytes32\n n = bytes32(uint256(reversed));\n // Shift to most significant bits\n n = n << 192;\n }\n}\n" + }, + "contracts/beacon/Merkle.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev These functions deal with verification of Merkle Tree proofs.\n *\n * The tree and the proofs can be generated using our\n * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].\n * You will find a quickstart guide in the readme.\n *\n * WARNING: You should avoid using leaf values that are 64 bytes long prior to\n * hashing, or use a hash function other than keccak256 for hashing leaves.\n * This is because the concatenation of a sorted pair of internal nodes in\n * the merkle tree could be reinterpreted as a leaf value.\n * OpenZeppelin's JavaScript library generates merkle trees that are safe\n * against this attack out of the box.\n */\nlibrary Merkle {\n error InvalidProofLength();\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function verifyInclusionSha256(\n bytes memory proof,\n bytes32 root,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bool) {\n return processInclusionProofSha256(proof, leaf, index) == root;\n }\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * _Available since v4.4._\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function processInclusionProofSha256(\n bytes memory proof,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bytes32) {\n require(\n proof.length != 0 && proof.length % 32 == 0,\n InvalidProofLength()\n );\n bytes32[1] memory computedHash = [leaf];\n for (uint256 i = 32; i <= proof.length; i += 32) {\n if (index % 2 == 0) {\n // if ith bit of index is 0, then computedHash is a left sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(computedHash))\n mstore(0x20, mload(add(proof, i)))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n }\n } else {\n // if ith bit of index is 1, then computedHash is a right sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(add(proof, i)))\n mstore(0x20, mload(computedHash))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n }\n }\n index = index / 2;\n }\n return computedHash[0];\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to request full or partial withdrawals from validators on the beacon chain.\n * @author Origin Protocol Inc\n */\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n /// @notice Requests a partial withdrawal for a given validator public key and amount.\n /// @param validatorPubKey The public key of the validator to withdraw from\n /// @param amount The amount of ETH to withdraw\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n /// @notice Gets fee for withdrawal requests contract on Beacon chain\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint40 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n bytes32 pubKeyHash,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof,\n bytes calldata validatorPubKeyProof\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint40 validatorIndex\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes32 pubKeyHash,\n bytes calldata firstPendingDepositPubKeyProof\n ) external view returns (bool isEmptyDepositQueue);\n\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view returns (bool isEmptyDepositQueue);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/mocks/MockBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"../beacon/BeaconProofsLib.sol\";\nimport { BeaconProofs } from \"../beacon/BeaconProofs.sol\";\n\ncontract MockBeaconProofs is BeaconProofs {\n function concatGenIndices(\n uint256 index1,\n uint256 height2,\n uint256 index2\n ) external pure returns (uint256 genIndex) {\n return BeaconProofsLib.concatGenIndices(index1, height2, index2);\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with\n /// `platformAddress` not used so empty address\n /// `vaultAddress` the address of the OETH Vault contract\n /// @param _wethAddress Address of the WETH Token contract\n /// @param _ssvToken Address of the SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconProofs,\n _beaconGenesisTimestamp\n )\n {\n SSV_TOKEN = _ssvToken;\n\n // Make sure nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// @param _rewardTokenAddresses Not used so empty array\n /// @param _assets Not used so empty array\n /// @param _pTokens Not used so empty array\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n safeApproveAllTokens();\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH that was transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// @param _recipient Address to receive withdrawn assets.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH to withdraw.\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(WETH, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Accounts for all the assets managed by this strategy which includes:\n /// 1. The current WETH in this strategy contract\n /// 2. The last verified ETH balance, total deposits and total validator balances\n /// @param _asset Address of WETH asset.\n /// @return balance Total value in ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by the strategy.\n /// @param _asset The address of the WETH token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for validator registration.\n function safeApproveAllTokens() public override {\n // Approves the SSV Network contract to transfer SSV tokens when validators are registered\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n * The new ETH will be accounted for in `checkBalance` after the next snapBalances and verifyBalances txs.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n /// @notice is not supported for this strategy as there is no platform token.\n function setPTokenAddress(address, address) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @notice is not supported for this strategy as there is no platform token.\n function removePToken(uint256) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @dev This strategy does not use a platform token like the old Aave and Compound strategies.\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instead of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract.\n /// Withdrawals from validators also accumulate as ETH in this strategy contract.\n /// It's too complex to separate the rewards from withdrawals so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can now regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingView.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/**\n * @title Viewing contract for the Compounding Staking Strategy.\n * @author Origin Protocol Inc\n */\ncontract CompoundingStakingStrategyView {\n /// @notice The address of the Compounding Staking Strategy contract\n CompoundingValidatorManager public immutable stakingStrategy;\n\n constructor(address _stakingStrategy) {\n stakingStrategy = CompoundingValidatorManager(_stakingStrategy);\n }\n\n struct ValidatorView {\n bytes32 pubKeyHash;\n uint64 index;\n CompoundingValidatorManager.ValidatorState state;\n }\n\n struct DepositView {\n uint256 depositID;\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint256 withdrawableEpoch;\n }\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return validators An array of `ValidatorView` containing the public key hash, validator index and state.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorView[] memory validators)\n {\n uint256 validatorCount = stakingStrategy.verifiedValidatorsLength();\n validators = new ValidatorView[](validatorCount);\n for (uint256 i = 0; i < validatorCount; ++i) {\n bytes32 pubKeyHash = stakingStrategy.verifiedValidators(i);\n (\n CompoundingValidatorManager.ValidatorState state,\n uint64 index\n ) = stakingStrategy.validator(pubKeyHash);\n validators[i] = ValidatorView({\n pubKeyHash: pubKeyHash,\n index: index,\n state: state\n });\n }\n }\n\n /// @notice Returns the deposits that are still to be verified.\n /// These may or may not have been processed by the beacon chain.\n /// @return pendingDeposits An array of `DepositView` containing the deposit ID, public key hash,\n /// amount in Gwei and the slot of the deposit.\n function getPendingDeposits()\n external\n view\n returns (DepositView[] memory pendingDeposits)\n {\n uint256 depositsCount = stakingStrategy.depositListLength();\n pendingDeposits = new DepositView[](depositsCount);\n for (uint256 i = 0; i < depositsCount; ++i) {\n (\n bytes32 pubKeyHash,\n uint64 amountGwei,\n uint64 slot,\n ,\n ,\n uint256 withdrawableEpoch\n ) = stakingStrategy.deposits(stakingStrategy.depositList(i));\n pendingDeposits[i] = DepositView({\n depositID: stakingStrategy.depositList(i),\n pubKeyHash: pubKeyHash,\n amountGwei: amountGwei,\n slot: slot,\n withdrawableEpoch: withdrawableEpoch\n });\n }\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable {\n using SafeERC20 for IERC20;\n\n /// @dev The amount of ETH in wei that is required for a deposit to a new validator.\n /// Initially this is 32 ETH, but will be reduced to 1 ETH after P2P's APIs have been updated\n /// to support deposits of 1 ETH.\n uint256 internal constant DEPOSIT_AMOUNT_WEI = 32 ether;\n /// @dev The maximum number of deposits that are waiting to be verified as processed on the beacon chain.\n uint256 internal constant MAX_DEPOSITS = 12;\n /// @dev The maximum number of validators that can be verified.\n uint256 internal constant MAX_VERIFIED_VALIDATORS = 48;\n /// @dev The default withdrawable epoch value on the Beacon chain.\n /// A value in the far future means the validator is not exiting.\n uint64 internal constant FAR_FUTURE_EPOCH = type(uint64).max;\n /// @dev The number of seconds between each beacon chain slot.\n uint64 internal constant SLOT_DURATION = 12;\n /// @dev The number of slots in each beacon chain epoch.\n uint64 internal constant SLOTS_PER_EPOCH = 32;\n /// @dev Minimum time in seconds to allow snapped balances to be verified.\n /// Set to 35 slots which is 3 slots more than 1 epoch (32 slots). Deposits get processed\n /// once per epoch. This larger than 1 epoch delay should achieve that `snapBalances` sometimes\n /// get called in the middle (or towards the end) of the epoch. Giving the off-chain script\n /// sufficient time after the end of the epoch to prepare the proofs and call `verifyBalances`.\n /// This is considering a malicious actor would keep calling `snapBalances` as frequent as possible\n /// to disturb our operations.\n uint64 internal constant SNAP_BALANCES_DELAY = 35 * SLOT_DURATION;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n /// @notice The timestamp of the Beacon chain genesis.\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\n uint64 public immutable BEACON_GENESIS_TIMESTAMP;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// @notice Deposit data for new compounding validators.\n /// @dev A `VERIFIED` deposit can mean 3 separate things:\n /// - a deposit has been processed by the beacon chain and shall be included in the\n /// balance of the next verifyBalances call\n /// - a deposit has been done to a slashed validator and has probably been recovered\n /// back to this strategy. Probably because we can not know for certain. This contract\n /// only detects when the validator has passed its withdrawal epoch. It is close to impossible\n /// to prove with Merkle Proofs that the postponed deposit this contract is responsible for\n /// creating is not present anymore in BeaconChain.state.pending_deposits. This in effect\n /// means that there might be a period where this contract thinks the deposit has been already\n /// returned as ETH balance before it happens. This will result in some days (or weeks)\n /// -> depending on the size of deposit queue of showing a deficit when calling `checkBalance`.\n /// As this only offsets the yield and doesn't cause a critical double-counting we are not addressing\n /// this issue.\n /// - A deposit has been done to the validator, but our deposit has been front run by a malicious\n /// actor. Funds in the deposit this contract makes are not recoverable.\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified\n }\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountGwei Amount of ETH in gwei that has been deposited to the beacon chain deposit contract\n /// @param slot The beacon chain slot number when the deposit has been made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either UNKNOWN, PENDING or VERIFIED\n /// @param withdrawableEpoch The withdrawableEpoch of the validator which is being deposited to.\n /// At deposit time this is set to max default value (FAR_FUTURE_EPOCH). If a deposit has\n /// made to a slashed validator the `withdrawableEpoch` will be set to the epoch of that\n /// validator.\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint32 depositIndex;\n DepositStatus status;\n uint64 withdrawableEpoch;\n }\n /// @notice Restricts to only one deposit to an unverified validator at a time.\n /// This is to limit front-running attacks of deposits to the beacon chain contract.\n ///\n /// @dev The value is set to true when a deposit to a new validator has been done that has\n /// not yet be verified.\n bool public firstDeposit;\n /// @notice Unique identifier of the next validator deposit.\n uint128 public nextDepositID;\n /// @notice Mapping of the deposit ID to the deposit data\n mapping(uint256 => DepositData) public deposits;\n /// @notice List of strategy deposit IDs to a validator.\n /// The list can be for deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n /// The list may not be ordered by time of deposit.\n /// Removed deposits will move the last deposit to the removed index.\n uint256[] public depositList;\n\n enum ValidatorState {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITING, // The validator has been requested to exit or has been verified as forced exit\n EXITED, // The validator has been verified to have a zero balance\n REMOVED, // validator has funds withdrawn to this strategy contract and is removed from the SSV\n INVALID // The validator has been front-run and the withdrawal address is not this strategy\n }\n\n // Validator data\n struct ValidatorData {\n ValidatorState state; // The state of the validator known to this contract\n uint40 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n bytes32[] public verifiedValidators;\n /// @notice Mapping of the hash of the validator's public key to the validator state and index.\n /// Uses the Beacon chain hashing for BLSPubkey which is sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => ValidatorData) public validator;\n\n /// @param blockRoot Beacon chain block root of the snapshot\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n bytes32 blockRoot;\n uint64 timestamp;\n uint128 ethBalance;\n }\n /// @notice Mapping of the block root to the balances at that slot\n Balances public snappedBalance;\n /// @notice The last verified ETH balance of the strategy\n uint256 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[50] private __gap;\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event FirstDepositReset();\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n uint256 indexed depositID,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint40 indexed validatorIndex\n );\n event ValidatorInvalid(bytes32 indexed pubKeyHash);\n event DepositVerified(uint256 indexed depositID, uint256 amountWei);\n event DepositToValidatorExiting(\n uint256 indexed depositID,\n uint256 amountWei,\n uint64 withdrawableEpoch\n );\n event DepositValidatorExited(uint256 indexed depositID, uint256 amountWei);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(bytes32 indexed blockRoot, uint256 ethBalance);\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 ethBalance\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_PROOFS = _beaconProofs;\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\n\n require(\n block.timestamp > _beaconGenesisTimestamp,\n \"Invalid genesis timestamp\"\n );\n }\n\n /**\n *\n * Admin Functions\n *\n */\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Reset the `firstDeposit` flag to false so deposits to unverified validators can be made again.\n function resetFirstDeposit() external onlyGovernor {\n require(firstDeposit, \"No first deposit\");\n\n firstDeposit = false;\n\n emit FirstDepositReset();\n }\n\n /**\n *\n * Validator Management\n *\n */\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validator[pubKeyHash].state == ValidatorState.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validator[pubKeyHash].state = ValidatorState.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n struct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n }\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validatorStakeData validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validatorStakeData,\n uint64 depositAmountGwei\n ) external onlyRegistrator {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(depositList.length < MAX_DEPOSITS, \"Max deposits\");\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validatorStakeData.pubkey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.VERIFIED),\n \"Not registered or verified\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n if (currentState == ValidatorState.REGISTERED) {\n // Can only have one pending deposit to an unverified validator at a time.\n // This is to limit front-running deposit attacks to a single deposit.\n // The exiting deposit needs to be verified before another deposit can be made.\n // If there was a front-running attack, the validator needs to be verified as invalid\n // and the Governor calls `resetFirstDeposit` to set `firstDeposit` to false.\n require(!firstDeposit, \"Existing first deposit\");\n // Limits the amount of ETH that can be at risk from a front-running deposit attack.\n require(\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n // Limits the number of validator balance proofs to verifyBalances\n require(\n verifiedValidators.length + 1 < MAX_VERIFIED_VALIDATORS,\n \"Max validators\"\n );\n\n // Flag a deposit to an unverified validator so only no other deposits can be made\n // to an unverified validator.\n firstDeposit = true;\n }\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == ValidatorState.REGISTERED) {\n validator[pubKeyHash].state = ValidatorState.STAKED;\n }\n\n /// After the Pectra upgrade the validators have a new restriction when proposing\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\n /// forward. Each slot is created at strict 12 second intervals and those slots can\n /// either have blocks attached to them or not. This way using the block.timestamp\n /// the slot number can easily be calculated.\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\n BEACON_GENESIS_TIMESTAMP) / SLOT_DURATION;\n\n // Store the deposit data for verifyDeposit and verifyBalances\n uint256 depositID = nextDepositID++;\n deposits[depositID] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n slot: depositSlot,\n depositIndex: SafeCast.toUint32(depositList.length),\n status: DepositStatus.PENDING,\n withdrawableEpoch: FAR_FUTURE_EPOCH\n });\n depositList.push(depositID);\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validatorStakeData.pubkey,\n withdrawalCredentials,\n validatorStakeData.signature,\n validatorStakeData.depositDataRoot\n );\n\n emit ETHStaked(\n pubKeyHash,\n depositID,\n validatorStakeData.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then only the amount in excess of 32 ETH will be withdrawn.\n /// Only the Registrator can call this function.\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\n /// If no ETH balance, the tx will revert.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n payable\n onlyRegistrator\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Validator full withdrawal could be denied due to multiple reasons:\n // - the validator has not been activated or active long enough\n // (current_epoch < activation_epoch + SHARD_COMMITTEE_PERIOD)\n // - the validator has pending balance to withdraw from a previous partial withdrawal request\n //\n // Meaning that the on-chain to beacon chain full withdrawal request could fail. Instead\n // of adding complexity of verifying if a validator is eligible for a full exit, we allow\n // multiple full withdrawal requests per validator.\n require(\n currentState == ValidatorState.VERIFIED ||\n currentState == ValidatorState.EXITING,\n \"Validator not verified/exiting\"\n );\n\n // If a full withdrawal (validator exit)\n if (amountGwei == 0) {\n // Store the validator state as exiting so no more deposits can be made to it.\n validator[pubKeyHash].state = ValidatorState.EXITING;\n }\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove the validator from the SSV Cluster after:\n /// - the validator has been exited from `validatorWithdrawal` or slashed\n /// - the validator has incorrectly registered and can not be staked to\n /// - the initial deposit was front-run and the withdrawal address is not this strategy's address.\n /// Make sure `validatorWithdrawal` is called with a zero amount and the validator has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.EXITED ||\n currentState == ValidatorState.INVALID,\n \"Validator not regd or exited\"\n );\n\n validator[pubKeyHash].state = ValidatorState.REMOVED;\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /**\n *\n * SSV Management\n *\n */\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be withdrawn from the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /**\n *\n * Beacon Chain Proofs\n *\n */\n\n /// @notice Verifies a validator's index to its public key.\n /// Adds to the list of verified validators if the validator's withdrawal address is this strategy's address.\n /// Marks the validator as invalid and removes the deposit if the withdrawal address is not this strategy's address.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param withdrawalAddress The withdrawal address of the validator which should be this strategy's address.\n /// If the withdrawal address is not this strategy's address, the initial deposit was front-run\n /// and the validator is marked as invalid.\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint40 validatorIndex,\n bytes32 pubKeyHash,\n address withdrawalAddress,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validator[pubKeyHash].state == ValidatorState.STAKED,\n \"Validator not staked\"\n );\n\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key.\n // Also verify the validator's withdrawal credential points to the `withdrawalAddress`.\n IBeaconProofs(BEACON_PROOFS).verifyValidator(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n withdrawalAddress\n );\n\n // Store the validator state as verified\n validator[pubKeyHash] = ValidatorData({\n state: ValidatorState.VERIFIED,\n index: validatorIndex\n });\n\n // If the initial deposit was front-run and the withdrawal address is not this strategy\n if (withdrawalAddress != address(this)) {\n // override the validator state\n validator[pubKeyHash].state = ValidatorState.INVALID;\n\n // Find and remove the deposit as the funds can not be recovered\n uint256 depositCount = depositList.length;\n for (uint256 i = 0; i < depositCount; i++) {\n DepositData memory deposit = deposits[depositList[i]];\n if (deposit.pubKeyHash == pubKeyHash) {\n // next verifyBalances will correctly account for the loss of a front-run\n // deposit. Doing it here accounts for the loss as soon as possible\n lastVerifiedEthBalance -= Math.min(\n lastVerifiedEthBalance,\n uint256(deposit.amountGwei) * 1 gwei\n );\n _removeDeposit(depositList[i], deposit);\n break;\n }\n }\n\n // Leave the `firstDeposit` flag as true so no more deposits to unverified validators can be made.\n // The Governor has to reset the `firstDeposit` to false before another deposit to\n // an unverified validator can be made.\n // The Governor can set a new `validatorRegistrator` if they suspect it has been compromised.\n\n emit ValidatorInvalid(pubKeyHash);\n return;\n }\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(pubKeyHash);\n\n // Reset the firstDeposit flag as the first deposit to an unverified validator has been verified.\n firstDeposit = false;\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n struct FirstPendingDepositSlotProofData {\n uint64 slot;\n bytes proof;\n }\n struct FirstPendingDepositWithdrawableProofData {\n uint64 slot;\n uint40 validatorIndex;\n bytes32 pubKeyHash;\n bytes pendingDepositPubKeyProof;\n bytes withdrawableEpochProof;\n bytes validatorPubKeyProof;\n }\n\n struct StrategyValidatorProofData {\n uint64 withdrawableEpoch;\n bytes withdrawableEpochProof;\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n ///\n /// Important: this function has a limitation where `depositProcessedSlot` and `firstDepositValidatorCreatedSlot`\n /// that is passed by the off-chain verifier requires a slot immediately after them to propose a block otherwise\n /// the `BeaconRoots.parentBlockRoot` will fail. This shouldn't be a problem, since by the current behaviour of\n /// beacon chain only 1%-3% slots don't propose a block.\n /// @param depositID The deposit ID emitted in `ETHStaked` from the `stakeEth` function.\n /// @param depositProcessedSlot Any slot on or after the strategy's deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\n /// set for the next block timestamp in 12 seconds time.\n /// @param firstPendingDeposit a `FirstPendingDepositSlotProofData` struct containing:\n /// - slot: The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero is a good choice.\n /// - proof: The merkle proof of the first pending deposit's slot to the beacon block root.\n /// Can be either:\n /// * 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// * 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @param strategyValidatorData a `StrategyValidatorProofData` struct containing:\n /// - withdrawableEpoch: The withdrawable epoch of the validator the strategy is depositing to.\n /// - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the validator the strategy\n /// is depositing to, to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n uint256 depositID,\n uint64 depositProcessedSlot,\n FirstPendingDepositSlotProofData calldata firstPendingDeposit,\n StrategyValidatorProofData calldata strategyValidatorData\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositID];\n ValidatorData memory strategyValidator = validator[deposit.pubKeyHash];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n strategyValidator.state == ValidatorState.VERIFIED,\n \"Validator not verified\"\n );\n // The verification slot must be after the deposit's slot.\n // This is needed for when the deposit queue is empty.\n require(deposit.slot < depositProcessedSlot, \"Slot not after deposit\");\n\n // Get the parent beacon block root of the next block which is the block root of the deposit verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 depositBlockRoot = BeaconRoots.parentBlockRoot(\n _calcNextBlockTimestamp(depositProcessedSlot)\n );\n\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n depositBlockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.proof\n );\n\n // Verify the withdrawableEpoch on the validator of the strategy's deposit\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n depositBlockRoot,\n strategyValidator.index,\n strategyValidatorData.withdrawableEpoch,\n strategyValidatorData.withdrawableEpochProof\n );\n\n // If the validator is exiting because it has been slashed\n if (strategyValidatorData.withdrawableEpoch != FAR_FUTURE_EPOCH) {\n // Store the exit epoch in the deposit data\n deposit.withdrawableEpoch = strategyValidatorData.withdrawableEpoch;\n\n emit DepositToValidatorExiting(\n depositID,\n uint256(deposit.amountGwei) * 1 gwei,\n strategyValidatorData.withdrawableEpoch\n );\n\n validator[deposit.pubKeyHash].state = ValidatorState.EXITING;\n\n // Leave the deposit status as PENDING\n return;\n }\n\n // solhint-disable max-line-length\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the deposit queue is empty then our deposit must have been processed on the beacon chain.\n // The deposit slot can be zero for validators consolidating to a compounding validator or 0x01 validator\n // being promoted to a compounding one. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // We can not guarantee that the deposit has been processed in that case.\n // solhint-enable max-line-length\n require(\n deposit.slot < firstPendingDeposit.slot || isDepositQueueEmpty,\n \"Deposit likely not processed\"\n );\n\n // Remove the deposit now it has been verified as processed on the beacon chain.\n _removeDeposit(depositID, deposit);\n\n emit DepositVerified(depositID, uint256(deposit.amountGwei) * 1 gwei);\n }\n\n function _removeDeposit(uint256 depositID, DepositData memory deposit)\n internal\n {\n // After verifying the proof, update the contract storage\n deposits[depositID].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n uint256 lastDeposit = depositList[depositList.length - 1];\n depositList[deposit.depositIndex] = lastDeposit;\n deposits[lastDeposit].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositList.pop();\n }\n\n /// @dev Calculates the timestamp of the next execution block from the given slot.\n /// @param slot The beacon chain slot number used for merkle proof verification.\n function _calcNextBlockTimestamp(uint64 slot)\n internal\n view\n returns (uint64)\n {\n // Calculate the next block timestamp from the slot.\n return SLOT_DURATION * slot + BEACON_GENESIS_TIMESTAMP + SLOT_DURATION;\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block and beacon block root\n /// of the slot that is associated with the previous block.\n ///\n /// When snapping / verifying balance it is of a high importance that there is no\n /// miss-match in respect to ETH that is held by the contract and balances that are\n /// verified on the validators.\n ///\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\n /// constructing a block on the beacon chain consist of:\n /// - process_withdrawals: ETH is deducted from the validator's balance\n /// - process_execution_payload: immediately after the previous step executing all the\n /// transactions\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\n /// contained in the withdrawal credentials of the exited validators\n ///\n /// That means that balance increases which are part of the post-block execution state are\n /// done within the block, but the transaction that are contained within that block can not\n /// see / interact with the balance from the exited validators. Only transactions in the\n /// next block can do that.\n ///\n /// When snap balances is performed the state of the chain is snapped across 2 separate\n /// chain states:\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\n /// will be referred to as Y - 1 as it makes no difference in the argument\n ///\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\n /// miss-counting ETH or much more dangerous double counting of the ETH.\n ///\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\n /// - ETH balance in the contract on block X\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\n /// - ETH balance in validators that are active in slot Y - 1\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\n /// and have their balance visible to transactions in slot Y and corresponding block X\n /// (or sooner)\n ///\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\n ///\n /// *ETH balance in the contract on block X*\n ///\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\n ///\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\n /// would result in double counting the `stakedEth` since it would be present once in the\n /// snapped contract balance and the second time in deposit storage variables.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\n ///\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\n /// contract at block Z. The execution layer doesn't have direct access to the state of\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\n /// and could also be part of the validator balances. It does that by verifying that at\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\n /// the last snap till now all are still in queue. Which ensures they can not be part of\n /// the validator balances in later steps.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in validators that are active in slot Y - 1*\n ///\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\n /// correctness.\n ///\n /// This behaviour is correct.\n ///\n /// *The withdrawn validators*\n ///\n /// The withdrawn validators could have their balances deducted in any slot before slot\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\n /// look at the \"worst case scenario\" where the validator withdrawal is processed in the\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\n /// even if the validator exits at the latest possible time it is paramount that the ETH\n /// balance on the execution layer is recorded in the next block. Correctly accounting\n /// for the withdrawn ETH.\n ///\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\n /// validator balance verification as well as execution layer contract balance snap.\n ///\n /// This behaviour is correct.\n ///\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n function snapBalances() external {\n uint64 currentTimestamp = SafeCast.toUint64(block.timestamp);\n require(\n snappedBalance.timestamp + SNAP_BALANCES_DELAY < currentTimestamp,\n \"Snap too soon\"\n );\n\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(currentTimestamp);\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the snapped balance\n snappedBalance = Balances({\n blockRoot: blockRoot,\n timestamp: currentTimestamp,\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n emit BalancesSnapped(blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct BalanceProofs {\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n /// @param validatorVerificationBlockTimestamp next block's timestamp of a slot that has the first pending\n /// deposit already applied to the validator.\n /// @param firstPendingDeposit a `FirstPendingDepositWithdrawableProofData` struct containing:\n /// - slot: The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero is a good choice.\n /// - validatorIndex: The index of the validator of the first pending deposit.\n /// Can be anything if the deposit queue is empty, but zero is a good choice.\n /// - pubKeyHash: The hash of the public key of the validator of the first pending deposit.\n /// Can be anything if the deposit queue is empty, but 32 zero bytes is a good choice\n /// - pendingDepositPubKeyProof: The merkle proof of the first pending deposit to the beacon block root.\n /// Can be either:\n /// * 40 witness hashes for BeaconBlock.state.PendingDeposits[0].pubKey when the deposit queue is not empty.\n /// * 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the first pending deposit's validator\n /// to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// - validatorPubKeyProof The merkle proof for the public key of the first pending deposit's validator\n /// to the this witness hash of withdrawableEpochProof.\n /// This is 2 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param balanceProofs a `BalanceProofs` struct containing the following:\n /// - balancesContainerRoot: The merkle root of the balances container\n /// - balancesContainerProof: The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// - validatorBalanceLeaves: Array of leaf nodes containing the validator balance with three other balances.\n /// - validatorBalanceProofs: Array of merkle proofs for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(\n uint64 validatorVerificationBlockTimestamp,\n FirstPendingDepositWithdrawableProofData calldata firstPendingDeposit,\n BalanceProofs calldata balanceProofs\n ) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalance;\n // Check the balances are the latest\n require(balancesMem.timestamp > 0, \"No snapped balances\");\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n balanceProofs.validatorBalanceProofs.length ==\n verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n balanceProofs.validatorBalanceLeaves.length ==\n verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n balancesMem.blockRoot,\n balanceProofs.balancesContainerRoot,\n balanceProofs.balancesContainerProof\n );\n\n // for each validator in reverse order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n balanceProofs.balancesContainerRoot,\n balanceProofs.validatorBalanceLeaves[i],\n balanceProofs.validatorBalanceProofs[i],\n validator[verifiedValidators[i]].index\n );\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n // This could have been in VERIFIED or EXITING state\n validator[verifiedValidators[i]].state = ValidatorState\n .EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += validatorBalanceGwei * 1 gwei;\n }\n }\n\n uint256 depositsCount = depositList.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification.\n // This section is after the validator balance verifications so an exited validator will be marked\n // as EXITED before the deposits are verified. If there was a deposit to an exited validator\n // then the deposit can only be removed once the validator is fully exited.\n // It is possible that validator fully exits and a postponed deposit to an exited validator increases\n // its balance again. In such case the contract will erroneously consider a deposit applied before it\n // has been applied on the beacon chain showing a smaller than real `totalValidatorBalance`.\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n balancesMem.blockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.pubKeyHash,\n firstPendingDeposit.pendingDepositPubKeyProof\n );\n\n // If there are no deposits in the beacon chain queue then our deposits must have been processed.\n // If the deposits have been processed, each deposit will need to be verified with `verifyDeposit`\n require(!isDepositQueueEmpty, \"Deposits have been processed\");\n\n // The verification of the validator the first pending deposit is for must be on or after when\n // `snapBalances` was called.\n require(\n balancesMem.timestamp <= validatorVerificationBlockTimestamp,\n \"Invalid validator timestamp\"\n );\n\n // Verify the validator of the first pending deposit is not exiting by checking\n // the withdrawable epoch is far into the future.\n // If it is exiting we can't be sure this deposit has not been postponed in the deposit queue.\n // Hence we can not verify if the strategy's deposit has been processed or not.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n // Get the parent beacon block root of the next block which is\n // the block root of the validator verification slot.\n // This will revert if the slot after the verification slot was missed.\n BeaconRoots.parentBlockRoot(\n validatorVerificationBlockTimestamp\n ),\n firstPendingDeposit.validatorIndex,\n firstPendingDeposit.pubKeyHash,\n // Validator is not exiting\n FAR_FUTURE_EPOCH,\n firstPendingDeposit.withdrawableEpochProof,\n firstPendingDeposit.validatorPubKeyProof\n );\n\n // solhint-disable max-line-length\n // If a validator is converted from a sweeping validator to a compounding validator, any balance in excess\n // of the min 32 ETH is put in the pending deposit queue. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // This will have a slot value of zero unfortunately.\n // We can not prove the strategy's deposits are still pending with a zero slot value so revert the tx.\n // Another snapBalances will need to be taken that does not have consolidation deposits at the front of the\n // beacon chain deposit queue.\n // solhint-enable max-line-length\n require(\n firstPendingDeposit.slot > 0,\n \"Invalid first pending deposit\"\n );\n\n // Calculate the epoch at the time of the snapBalances\n uint64 verificationEpoch = (SafeCast.toUint64(\n balancesMem.timestamp\n ) - BEACON_GENESIS_TIMESTAMP) / (SLOT_DURATION * SLOTS_PER_EPOCH);\n\n // For each staking strategy's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n uint256 depositID = depositList[i];\n DepositData memory depositData = deposits[depositID];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit slot is before the slot of the staking strategy's deposit.\n // If the deposit has been processed, it will need to be verified with `verifyDeposit`.\n // OR the deposit is to an exiting validator so check it is still not withdrawable.\n // If the validator is not withdrawable, then the deposit can not have been processed yet.\n // If the validator is now withdrawable, then the deposit may have been processed. The strategy\n // now has to wait until the validator's balance is verified to be zero.\n // OR the validator has exited and the deposit is now verified as processed.\n require(\n firstPendingDeposit.slot < depositData.slot ||\n (verificationEpoch < depositData.withdrawableEpoch &&\n depositData.withdrawableEpoch !=\n FAR_FUTURE_EPOCH) ||\n validator[depositData.pubKeyHash].state ==\n ValidatorState.EXITED,\n \"Deposit likely processed\"\n );\n\n // Remove the deposit if the validator has exited.\n if (\n validator[depositData.pubKeyHash].state ==\n ValidatorState.EXITED\n ) {\n _removeDeposit(depositID, depositData);\n\n emit DepositValidatorExited(\n depositID,\n uint256(depositData.amountGwei) * 1 gwei\n );\n\n // Skip to the next deposit as the deposit amount is now in the strategy's ETH balance\n continue;\n }\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei += uint256(depositData.amountGwei) * 1 gwei;\n }\n }\n\n // Store the verified balance in storage\n lastVerifiedEthBalance =\n totalDepositsWei +\n totalValidatorBalance +\n balancesMem.ethBalance;\n // Reset the last snap timestamp so a new snapBalances has to be made\n snappedBalance.timestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n require(pubKey.length == 48, \"Invalid public key length\");\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /**\n *\n * WETH and ETH Accounting\n *\n */\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n // The min is required as more WETH can be withdrawn than deposited\n // as the strategy earns consensus and execution rewards.\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance.\n // The ETH balance in this strategy contract can be more than the last verified ETH balance\n // due to partial withdrawals or full exits being processed by the beacon chain since the last snapBalances.\n // It can also happen from execution rewards (MEV) or ETH donations.\n lastVerifiedEthBalance -= Math.min(lastVerifiedEthBalance, _ethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n snappedBalance.timestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += _wethAmount;\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n snappedBalance.timestamp = 0;\n }\n\n /**\n *\n * View Functions\n *\n */\n\n /// @notice Returns the number of deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n function depositListLength() external view returns (uint256) {\n return depositList.length;\n }\n\n /// @notice Returns the number of verified validators.\n function verifiedValidatorsLength() external view returns (uint256) {\n return verifiedValidators.length;\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/a5de2bf4884568e79cc9a8215a939c59.json b/contracts/deployments/hoodi/solcInputs/a5de2bf4884568e79cc9a8215a939c59.json new file mode 100644 index 0000000000..68367009bc --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/a5de2bf4884568e79cc9a8215a939c59.json @@ -0,0 +1,1155 @@ +{ + "language": "Solidity", + "sources": { + "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {Client} from \"../libraries/Client.sol\";\n\ninterface IRouterClient {\n error UnsupportedDestinationChain(uint64 destChainSelector);\n error InsufficientFeeTokenAmount();\n error InvalidMsgValue();\n\n /// @notice Checks if the given chain ID is supported for sending/receiving.\n /// @param chainSelector The chain to check.\n /// @return supported is true if it is supported, false if not.\n function isChainSupported(uint64 chainSelector) external view returns (bool supported);\n\n /// @notice Gets a list of all supported tokens which can be sent or received\n /// to/from a given chain id.\n /// @param chainSelector The chainSelector.\n /// @return tokens The addresses of all tokens that are supported.\n function getSupportedTokens(uint64 chainSelector) external view returns (address[] memory tokens);\n\n /// @param destinationChainSelector The destination chainSelector\n /// @param message The cross-chain CCIP message including data and/or tokens\n /// @return fee returns execution fee for the message\n /// delivery to destination chain, denominated in the feeToken specified in the message.\n /// @dev Reverts with appropriate reason upon invalid message.\n function getFee(\n uint64 destinationChainSelector,\n Client.EVM2AnyMessage memory message\n ) external view returns (uint256 fee);\n\n /// @notice Request a message to be sent to the destination chain\n /// @param destinationChainSelector The destination chain ID\n /// @param message The cross-chain CCIP message including data and/or tokens\n /// @return messageId The message ID\n /// @dev Note if msg.value is larger than the required fee (from getFee) we accept\n /// the overpayment with no refund.\n /// @dev Reverts with appropriate reason upon invalid message.\n function ccipSend(\n uint64 destinationChainSelector,\n Client.EVM2AnyMessage calldata message\n ) external payable returns (bytes32);\n}\n" + }, + "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// End consumer library.\nlibrary Client {\n /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.\n struct EVMTokenAmount {\n address token; // token address on the local chain.\n uint256 amount; // Amount of tokens.\n }\n\n struct Any2EVMMessage {\n bytes32 messageId; // MessageId corresponding to ccipSend on source.\n uint64 sourceChainSelector; // Source chain selector.\n bytes sender; // abi.decode(sender) if coming from an EVM chain.\n bytes data; // payload sent in original message.\n EVMTokenAmount[] destTokenAmounts; // Tokens and their amounts in their destination chain representation.\n }\n\n // If extraArgs is empty bytes, the default is 200k gas limit.\n struct EVM2AnyMessage {\n bytes receiver; // abi.encode(receiver address) for dest EVM chains\n bytes data; // Data payload\n EVMTokenAmount[] tokenAmounts; // Token transfers\n address feeToken; // Address of feeToken. address(0) means you will send msg.value.\n bytes extraArgs; // Populate this with _argsToBytes(EVMExtraArgsV1)\n }\n\n // bytes4(keccak256(\"CCIP EVMExtraArgsV1\"));\n bytes4 public constant EVM_EXTRA_ARGS_V1_TAG = 0x97a657c9;\n struct EVMExtraArgsV1 {\n uint256 gasLimit;\n }\n\n function _argsToBytes(EVMExtraArgsV1 memory extraArgs) internal pure returns (bytes memory bts) {\n return abi.encodeWithSelector(EVM_EXTRA_ARGS_V1_TAG, extraArgs);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport \"@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol\";\n\nlibrary ExecutorOptions {\n using CalldataBytesLib for bytes;\n\n uint8 internal constant WORKER_ID = 1;\n\n uint8 internal constant OPTION_TYPE_LZRECEIVE = 1;\n uint8 internal constant OPTION_TYPE_NATIVE_DROP = 2;\n uint8 internal constant OPTION_TYPE_LZCOMPOSE = 3;\n uint8 internal constant OPTION_TYPE_ORDERED_EXECUTION = 4;\n uint8 internal constant OPTION_TYPE_LZREAD = 5;\n\n error Executor_InvalidLzReceiveOption();\n error Executor_InvalidNativeDropOption();\n error Executor_InvalidLzComposeOption();\n error Executor_InvalidLzReadOption();\n\n /// @dev decode the next executor option from the options starting from the specified cursor\n /// @param _options [executor_id][executor_option][executor_id][executor_option]...\n /// executor_option = [option_size][option_type][option]\n /// option_size = len(option_type) + len(option)\n /// executor_id: uint8, option_size: uint16, option_type: uint8, option: bytes\n /// @param _cursor the cursor to start decoding from\n /// @return optionType the type of the option\n /// @return option the option of the executor\n /// @return cursor the cursor to start decoding the next executor option\n function nextExecutorOption(\n bytes calldata _options,\n uint256 _cursor\n ) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) {\n unchecked {\n // skip worker id\n cursor = _cursor + 1;\n\n // read option size\n uint16 size = _options.toU16(cursor);\n cursor += 2;\n\n // read option type\n optionType = _options.toU8(cursor);\n\n // startCursor and endCursor are used to slice the option from _options\n uint256 startCursor = cursor + 1; // skip option type\n uint256 endCursor = cursor + size;\n option = _options[startCursor:endCursor];\n cursor += size;\n }\n }\n\n function decodeLzReceiveOption(bytes calldata _option) internal pure returns (uint128 gas, uint128 value) {\n if (_option.length != 16 && _option.length != 32) revert Executor_InvalidLzReceiveOption();\n gas = _option.toU128(0);\n value = _option.length == 32 ? _option.toU128(16) : 0;\n }\n\n function decodeNativeDropOption(bytes calldata _option) internal pure returns (uint128 amount, bytes32 receiver) {\n if (_option.length != 48) revert Executor_InvalidNativeDropOption();\n amount = _option.toU128(0);\n receiver = _option.toB32(16);\n }\n\n function decodeLzComposeOption(\n bytes calldata _option\n ) internal pure returns (uint16 index, uint128 gas, uint128 value) {\n if (_option.length != 18 && _option.length != 34) revert Executor_InvalidLzComposeOption();\n index = _option.toU16(0);\n gas = _option.toU128(2);\n value = _option.length == 34 ? _option.toU128(18) : 0;\n }\n\n function decodeLzReadOption(\n bytes calldata _option\n ) internal pure returns (uint128 gas, uint32 calldataSize, uint128 value) {\n if (_option.length != 20 && _option.length != 36) revert Executor_InvalidLzReadOption();\n gas = _option.toU128(0);\n calldataSize = _option.toU32(16);\n value = _option.length == 36 ? _option.toU128(20) : 0;\n }\n\n function encodeLzReceiveOption(uint128 _gas, uint128 _value) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_gas) : abi.encodePacked(_gas, _value);\n }\n\n function encodeNativeDropOption(uint128 _amount, bytes32 _receiver) internal pure returns (bytes memory) {\n return abi.encodePacked(_amount, _receiver);\n }\n\n function encodeLzComposeOption(uint16 _index, uint128 _gas, uint128 _value) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_index, _gas) : abi.encodePacked(_index, _gas, _value);\n }\n\n function encodeLzReadOption(\n uint128 _gas,\n uint32 _calldataSize,\n uint128 _value\n ) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_gas, _calldataSize) : abi.encodePacked(_gas, _calldataSize, _value);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/DVNOptions.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport { BytesLib } from \"solidity-bytes-utils/contracts/BytesLib.sol\";\n\nimport { BitMap256 } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/BitMaps.sol\";\nimport { CalldataBytesLib } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol\";\n\nlibrary DVNOptions {\n using CalldataBytesLib for bytes;\n using BytesLib for bytes;\n\n uint8 internal constant WORKER_ID = 2;\n uint8 internal constant OPTION_TYPE_PRECRIME = 1;\n\n error DVN_InvalidDVNIdx();\n error DVN_InvalidDVNOptions(uint256 cursor);\n\n /// @dev group dvn options by its idx\n /// @param _options [dvn_id][dvn_option][dvn_id][dvn_option]...\n /// dvn_option = [option_size][dvn_idx][option_type][option]\n /// option_size = len(dvn_idx) + len(option_type) + len(option)\n /// dvn_id: uint8, dvn_idx: uint8, option_size: uint16, option_type: uint8, option: bytes\n /// @return dvnOptions the grouped options, still share the same format of _options\n /// @return dvnIndices the dvn indices\n function groupDVNOptionsByIdx(\n bytes memory _options\n ) internal pure returns (bytes[] memory dvnOptions, uint8[] memory dvnIndices) {\n if (_options.length == 0) return (dvnOptions, dvnIndices);\n\n uint8 numDVNs = getNumDVNs(_options);\n\n // if there is only 1 dvn, we can just return the whole options\n if (numDVNs == 1) {\n dvnOptions = new bytes[](1);\n dvnOptions[0] = _options;\n\n dvnIndices = new uint8[](1);\n dvnIndices[0] = _options.toUint8(3); // dvn idx\n return (dvnOptions, dvnIndices);\n }\n\n // otherwise, we need to group the options by dvn_idx\n dvnIndices = new uint8[](numDVNs);\n dvnOptions = new bytes[](numDVNs);\n unchecked {\n uint256 cursor = 0;\n uint256 start = 0;\n uint8 lastDVNIdx = 255; // 255 is an invalid dvn_idx\n\n while (cursor < _options.length) {\n ++cursor; // skip worker_id\n\n // optionLength asserted in getNumDVNs (skip check)\n uint16 optionLength = _options.toUint16(cursor);\n cursor += 2;\n\n // dvnIdx asserted in getNumDVNs (skip check)\n uint8 dvnIdx = _options.toUint8(cursor);\n\n // dvnIdx must equal to the lastDVNIdx for the first option\n // so it is always skipped in the first option\n // this operation slices out options whenever the scan finds a different lastDVNIdx\n if (lastDVNIdx == 255) {\n lastDVNIdx = dvnIdx;\n } else if (dvnIdx != lastDVNIdx) {\n uint256 len = cursor - start - 3; // 3 is for worker_id and option_length\n bytes memory opt = _options.slice(start, len);\n _insertDVNOptions(dvnOptions, dvnIndices, lastDVNIdx, opt);\n\n // reset the start and lastDVNIdx\n start += len;\n lastDVNIdx = dvnIdx;\n }\n\n cursor += optionLength;\n }\n\n // skip check the cursor here because the cursor is asserted in getNumDVNs\n // if we have reached the end of the options, we need to process the last dvn\n uint256 size = cursor - start;\n bytes memory op = _options.slice(start, size);\n _insertDVNOptions(dvnOptions, dvnIndices, lastDVNIdx, op);\n\n // revert dvnIndices to start from 0\n for (uint8 i = 0; i < numDVNs; ++i) {\n --dvnIndices[i];\n }\n }\n }\n\n function _insertDVNOptions(\n bytes[] memory _dvnOptions,\n uint8[] memory _dvnIndices,\n uint8 _dvnIdx,\n bytes memory _newOptions\n ) internal pure {\n // dvnIdx starts from 0 but default value of dvnIndices is 0,\n // so we tell if the slot is empty by adding 1 to dvnIdx\n if (_dvnIdx == 255) revert DVN_InvalidDVNIdx();\n uint8 dvnIdxAdj = _dvnIdx + 1;\n\n for (uint256 j = 0; j < _dvnIndices.length; ++j) {\n uint8 index = _dvnIndices[j];\n if (dvnIdxAdj == index) {\n _dvnOptions[j] = abi.encodePacked(_dvnOptions[j], _newOptions);\n break;\n } else if (index == 0) {\n // empty slot, that means it is the first time we see this dvn\n _dvnIndices[j] = dvnIdxAdj;\n _dvnOptions[j] = _newOptions;\n break;\n }\n }\n }\n\n /// @dev get the number of unique dvns\n /// @param _options the format is the same as groupDVNOptionsByIdx\n function getNumDVNs(bytes memory _options) internal pure returns (uint8 numDVNs) {\n uint256 cursor = 0;\n BitMap256 bitmap;\n\n // find number of unique dvn_idx\n unchecked {\n while (cursor < _options.length) {\n ++cursor; // skip worker_id\n\n uint16 optionLength = _options.toUint16(cursor);\n cursor += 2;\n if (optionLength < 2) revert DVN_InvalidDVNOptions(cursor); // at least 1 byte for dvn_idx and 1 byte for option_type\n\n uint8 dvnIdx = _options.toUint8(cursor);\n\n // if dvnIdx is not set, increment numDVNs\n // max num of dvns is 255, 255 is an invalid dvn_idx\n // The order of the dvnIdx is not required to be sequential, as enforcing the order may weaken\n // the composability of the options. e.g. if we refrain from enforcing the order, an OApp that has\n // already enforced certain options can append additional options to the end of the enforced\n // ones without restrictions.\n if (dvnIdx == 255) revert DVN_InvalidDVNIdx();\n if (!bitmap.get(dvnIdx)) {\n ++numDVNs;\n bitmap = bitmap.set(dvnIdx);\n }\n\n cursor += optionLength;\n }\n }\n if (cursor != _options.length) revert DVN_InvalidDVNOptions(cursor);\n }\n\n /// @dev decode the next dvn option from _options starting from the specified cursor\n /// @param _options the format is the same as groupDVNOptionsByIdx\n /// @param _cursor the cursor to start decoding\n /// @return optionType the type of the option\n /// @return option the option\n /// @return cursor the cursor to start decoding the next option\n function nextDVNOption(\n bytes calldata _options,\n uint256 _cursor\n ) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) {\n unchecked {\n // skip worker id\n cursor = _cursor + 1;\n\n // read option size\n uint16 size = _options.toU16(cursor);\n cursor += 2;\n\n // read option type\n optionType = _options.toU8(cursor + 1); // skip dvn_idx\n\n // startCursor and endCursor are used to slice the option from _options\n uint256 startCursor = cursor + 2; // skip option type and dvn_idx\n uint256 endCursor = cursor + size;\n option = _options[startCursor:endCursor];\n cursor += size;\n }\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { IMessageLibManager } from \"./IMessageLibManager.sol\";\nimport { IMessagingComposer } from \"./IMessagingComposer.sol\";\nimport { IMessagingChannel } from \"./IMessagingChannel.sol\";\nimport { IMessagingContext } from \"./IMessagingContext.sol\";\n\nstruct MessagingParams {\n uint32 dstEid;\n bytes32 receiver;\n bytes message;\n bytes options;\n bool payInLzToken;\n}\n\nstruct MessagingReceipt {\n bytes32 guid;\n uint64 nonce;\n MessagingFee fee;\n}\n\nstruct MessagingFee {\n uint256 nativeFee;\n uint256 lzTokenFee;\n}\n\nstruct Origin {\n uint32 srcEid;\n bytes32 sender;\n uint64 nonce;\n}\n\ninterface ILayerZeroEndpointV2 is IMessageLibManager, IMessagingComposer, IMessagingChannel, IMessagingContext {\n event PacketSent(bytes encodedPayload, bytes options, address sendLibrary);\n\n event PacketVerified(Origin origin, address receiver, bytes32 payloadHash);\n\n event PacketDelivered(Origin origin, address receiver);\n\n event LzReceiveAlert(\n address indexed receiver,\n address indexed executor,\n Origin origin,\n bytes32 guid,\n uint256 gas,\n uint256 value,\n bytes message,\n bytes extraData,\n bytes reason\n );\n\n event LzTokenSet(address token);\n\n event DelegateSet(address sender, address delegate);\n\n function quote(MessagingParams calldata _params, address _sender) external view returns (MessagingFee memory);\n\n function send(\n MessagingParams calldata _params,\n address _refundAddress\n ) external payable returns (MessagingReceipt memory);\n\n function verify(Origin calldata _origin, address _receiver, bytes32 _payloadHash) external;\n\n function verifiable(Origin calldata _origin, address _receiver) external view returns (bool);\n\n function initializable(Origin calldata _origin, address _receiver) external view returns (bool);\n\n function lzReceive(\n Origin calldata _origin,\n address _receiver,\n bytes32 _guid,\n bytes calldata _message,\n bytes calldata _extraData\n ) external payable;\n\n // oapp can burn messages partially by calling this function with its own business logic if messages are verified in order\n function clear(address _oapp, Origin calldata _origin, bytes32 _guid, bytes calldata _message) external;\n\n function setLzToken(address _lzToken) external;\n\n function lzToken() external view returns (address);\n\n function nativeToken() external view returns (address);\n\n function setDelegate(address _delegate) external;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { Origin } from \"./ILayerZeroEndpointV2.sol\";\n\ninterface ILayerZeroReceiver {\n function allowInitializePath(Origin calldata _origin) external view returns (bool);\n\n function nextNonce(uint32 _eid, bytes32 _sender) external view returns (uint64);\n\n function lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) external payable;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessageLib.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { IERC165 } from \"@openzeppelin/contracts/utils/introspection/IERC165.sol\";\n\nimport { SetConfigParam } from \"./IMessageLibManager.sol\";\n\nenum MessageLibType {\n Send,\n Receive,\n SendAndReceive\n}\n\ninterface IMessageLib is IERC165 {\n function setConfig(address _oapp, SetConfigParam[] calldata _config) external;\n\n function getConfig(uint32 _eid, address _oapp, uint32 _configType) external view returns (bytes memory config);\n\n function isSupportedEid(uint32 _eid) external view returns (bool);\n\n // message libs of same major version are compatible\n function version() external view returns (uint64 major, uint8 minor, uint8 endpointVersion);\n\n function messageLibType() external view returns (MessageLibType);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessageLibManager.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nstruct SetConfigParam {\n uint32 eid;\n uint32 configType;\n bytes config;\n}\n\ninterface IMessageLibManager {\n struct Timeout {\n address lib;\n uint256 expiry;\n }\n\n event LibraryRegistered(address newLib);\n event DefaultSendLibrarySet(uint32 eid, address newLib);\n event DefaultReceiveLibrarySet(uint32 eid, address newLib);\n event DefaultReceiveLibraryTimeoutSet(uint32 eid, address oldLib, uint256 expiry);\n event SendLibrarySet(address sender, uint32 eid, address newLib);\n event ReceiveLibrarySet(address receiver, uint32 eid, address newLib);\n event ReceiveLibraryTimeoutSet(address receiver, uint32 eid, address oldLib, uint256 timeout);\n\n function registerLibrary(address _lib) external;\n\n function isRegisteredLibrary(address _lib) external view returns (bool);\n\n function getRegisteredLibraries() external view returns (address[] memory);\n\n function setDefaultSendLibrary(uint32 _eid, address _newLib) external;\n\n function defaultSendLibrary(uint32 _eid) external view returns (address);\n\n function setDefaultReceiveLibrary(uint32 _eid, address _newLib, uint256 _gracePeriod) external;\n\n function defaultReceiveLibrary(uint32 _eid) external view returns (address);\n\n function setDefaultReceiveLibraryTimeout(uint32 _eid, address _lib, uint256 _expiry) external;\n\n function defaultReceiveLibraryTimeout(uint32 _eid) external view returns (address lib, uint256 expiry);\n\n function isSupportedEid(uint32 _eid) external view returns (bool);\n\n function isValidReceiveLibrary(address _receiver, uint32 _eid, address _lib) external view returns (bool);\n\n /// ------------------- OApp interfaces -------------------\n function setSendLibrary(address _oapp, uint32 _eid, address _newLib) external;\n\n function getSendLibrary(address _sender, uint32 _eid) external view returns (address lib);\n\n function isDefaultSendLibrary(address _sender, uint32 _eid) external view returns (bool);\n\n function setReceiveLibrary(address _oapp, uint32 _eid, address _newLib, uint256 _gracePeriod) external;\n\n function getReceiveLibrary(address _receiver, uint32 _eid) external view returns (address lib, bool isDefault);\n\n function setReceiveLibraryTimeout(address _oapp, uint32 _eid, address _lib, uint256 _expiry) external;\n\n function receiveLibraryTimeout(address _receiver, uint32 _eid) external view returns (address lib, uint256 expiry);\n\n function setConfig(address _oapp, address _lib, SetConfigParam[] calldata _params) external;\n\n function getConfig(\n address _oapp,\n address _lib,\n uint32 _eid,\n uint32 _configType\n ) external view returns (bytes memory config);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingChannel.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingChannel {\n event InboundNonceSkipped(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce);\n event PacketNilified(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);\n event PacketBurnt(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);\n\n function eid() external view returns (uint32);\n\n // this is an emergency function if a message cannot be verified for some reasons\n // required to provide _nextNonce to avoid race condition\n function skip(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce) external;\n\n function nilify(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;\n\n function burn(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;\n\n function nextGuid(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (bytes32);\n\n function inboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);\n\n function outboundNonce(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (uint64);\n\n function inboundPayloadHash(\n address _receiver,\n uint32 _srcEid,\n bytes32 _sender,\n uint64 _nonce\n ) external view returns (bytes32);\n\n function lazyInboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingComposer.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingComposer {\n event ComposeSent(address from, address to, bytes32 guid, uint16 index, bytes message);\n event ComposeDelivered(address from, address to, bytes32 guid, uint16 index);\n event LzComposeAlert(\n address indexed from,\n address indexed to,\n address indexed executor,\n bytes32 guid,\n uint16 index,\n uint256 gas,\n uint256 value,\n bytes message,\n bytes extraData,\n bytes reason\n );\n\n function composeQueue(\n address _from,\n address _to,\n bytes32 _guid,\n uint16 _index\n ) external view returns (bytes32 messageHash);\n\n function sendCompose(address _to, bytes32 _guid, uint16 _index, bytes calldata _message) external;\n\n function lzCompose(\n address _from,\n address _to,\n bytes32 _guid,\n uint16 _index,\n bytes calldata _message,\n bytes calldata _extraData\n ) external payable;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingContext.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingContext {\n function isSendingMessage() external view returns (bool);\n\n function getSendContext() external view returns (uint32 dstEid, address sender);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ISendLib.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { MessagingFee } from \"./ILayerZeroEndpointV2.sol\";\nimport { IMessageLib } from \"./IMessageLib.sol\";\n\nstruct Packet {\n uint64 nonce;\n uint32 srcEid;\n address sender;\n uint32 dstEid;\n bytes32 receiver;\n bytes32 guid;\n bytes message;\n}\n\ninterface ISendLib is IMessageLib {\n function send(\n Packet calldata _packet,\n bytes calldata _options,\n bool _payInLzToken\n ) external returns (MessagingFee memory, bytes memory encodedPacket);\n\n function quote(\n Packet calldata _packet,\n bytes calldata _options,\n bool _payInLzToken\n ) external view returns (MessagingFee memory);\n\n function setTreasury(address _treasury) external;\n\n function withdrawFee(address _to, uint256 _amount) external;\n\n function withdrawLzTokenFee(address _lzToken, address _to, uint256 _amount) external;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/AddressCast.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nlibrary AddressCast {\n error AddressCast_InvalidSizeForAddress();\n error AddressCast_InvalidAddress();\n\n function toBytes32(bytes calldata _addressBytes) internal pure returns (bytes32 result) {\n if (_addressBytes.length > 32) revert AddressCast_InvalidAddress();\n result = bytes32(_addressBytes);\n unchecked {\n uint256 offset = 32 - _addressBytes.length;\n result = result >> (offset * 8);\n }\n }\n\n function toBytes32(address _address) internal pure returns (bytes32 result) {\n result = bytes32(uint256(uint160(_address)));\n }\n\n function toBytes(bytes32 _addressBytes32, uint256 _size) internal pure returns (bytes memory result) {\n if (_size == 0 || _size > 32) revert AddressCast_InvalidSizeForAddress();\n result = new bytes(_size);\n unchecked {\n uint256 offset = 256 - _size * 8;\n assembly {\n mstore(add(result, 32), shl(offset, _addressBytes32))\n }\n }\n }\n\n function toAddress(bytes32 _addressBytes32) internal pure returns (address result) {\n result = address(uint160(uint256(_addressBytes32)));\n }\n\n function toAddress(bytes calldata _addressBytes) internal pure returns (address result) {\n if (_addressBytes.length != 20) revert AddressCast_InvalidAddress();\n result = address(bytes20(_addressBytes));\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nlibrary CalldataBytesLib {\n function toU8(bytes calldata _bytes, uint256 _start) internal pure returns (uint8) {\n return uint8(_bytes[_start]);\n }\n\n function toU16(bytes calldata _bytes, uint256 _start) internal pure returns (uint16) {\n unchecked {\n uint256 end = _start + 2;\n return uint16(bytes2(_bytes[_start:end]));\n }\n }\n\n function toU32(bytes calldata _bytes, uint256 _start) internal pure returns (uint32) {\n unchecked {\n uint256 end = _start + 4;\n return uint32(bytes4(_bytes[_start:end]));\n }\n }\n\n function toU64(bytes calldata _bytes, uint256 _start) internal pure returns (uint64) {\n unchecked {\n uint256 end = _start + 8;\n return uint64(bytes8(_bytes[_start:end]));\n }\n }\n\n function toU128(bytes calldata _bytes, uint256 _start) internal pure returns (uint128) {\n unchecked {\n uint256 end = _start + 16;\n return uint128(bytes16(_bytes[_start:end]));\n }\n }\n\n function toU256(bytes calldata _bytes, uint256 _start) internal pure returns (uint256) {\n unchecked {\n uint256 end = _start + 32;\n return uint256(bytes32(_bytes[_start:end]));\n }\n }\n\n function toAddr(bytes calldata _bytes, uint256 _start) internal pure returns (address) {\n unchecked {\n uint256 end = _start + 20;\n return address(bytes20(_bytes[_start:end]));\n }\n }\n\n function toB32(bytes calldata _bytes, uint256 _start) internal pure returns (bytes32) {\n unchecked {\n uint256 end = _start + 32;\n return bytes32(_bytes[_start:end]);\n }\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/BitMaps.sol": { + "content": "// SPDX-License-Identifier: MIT\n\n// modified from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/structs/BitMaps.sol\npragma solidity ^0.8.20;\n\ntype BitMap256 is uint256;\n\nusing BitMaps for BitMap256 global;\n\nlibrary BitMaps {\n /**\n * @dev Returns whether the bit at `index` is set.\n */\n function get(BitMap256 bitmap, uint8 index) internal pure returns (bool) {\n uint256 mask = 1 << index;\n return BitMap256.unwrap(bitmap) & mask != 0;\n }\n\n /**\n * @dev Sets the bit at `index`.\n */\n function set(BitMap256 bitmap, uint8 index) internal pure returns (BitMap256) {\n uint256 mask = 1 << index;\n return BitMap256.wrap(BitMap256.unwrap(bitmap) | mask);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport { Packet } from \"../../interfaces/ISendLib.sol\";\nimport { AddressCast } from \"../../libs/AddressCast.sol\";\n\nlibrary PacketV1Codec {\n using AddressCast for address;\n using AddressCast for bytes32;\n\n uint8 internal constant PACKET_VERSION = 1;\n\n // header (version + nonce + path)\n // version\n uint256 private constant PACKET_VERSION_OFFSET = 0;\n // nonce\n uint256 private constant NONCE_OFFSET = 1;\n // path\n uint256 private constant SRC_EID_OFFSET = 9;\n uint256 private constant SENDER_OFFSET = 13;\n uint256 private constant DST_EID_OFFSET = 45;\n uint256 private constant RECEIVER_OFFSET = 49;\n // payload (guid + message)\n uint256 private constant GUID_OFFSET = 81; // keccak256(nonce + path)\n uint256 private constant MESSAGE_OFFSET = 113;\n\n function encode(Packet memory _packet) internal pure returns (bytes memory encodedPacket) {\n encodedPacket = abi.encodePacked(\n PACKET_VERSION,\n _packet.nonce,\n _packet.srcEid,\n _packet.sender.toBytes32(),\n _packet.dstEid,\n _packet.receiver,\n _packet.guid,\n _packet.message\n );\n }\n\n function encodePacketHeader(Packet memory _packet) internal pure returns (bytes memory) {\n return\n abi.encodePacked(\n PACKET_VERSION,\n _packet.nonce,\n _packet.srcEid,\n _packet.sender.toBytes32(),\n _packet.dstEid,\n _packet.receiver\n );\n }\n\n function encodePayload(Packet memory _packet) internal pure returns (bytes memory) {\n return abi.encodePacked(_packet.guid, _packet.message);\n }\n\n function header(bytes calldata _packet) internal pure returns (bytes calldata) {\n return _packet[0:GUID_OFFSET];\n }\n\n function version(bytes calldata _packet) internal pure returns (uint8) {\n return uint8(bytes1(_packet[PACKET_VERSION_OFFSET:NONCE_OFFSET]));\n }\n\n function nonce(bytes calldata _packet) internal pure returns (uint64) {\n return uint64(bytes8(_packet[NONCE_OFFSET:SRC_EID_OFFSET]));\n }\n\n function srcEid(bytes calldata _packet) internal pure returns (uint32) {\n return uint32(bytes4(_packet[SRC_EID_OFFSET:SENDER_OFFSET]));\n }\n\n function sender(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[SENDER_OFFSET:DST_EID_OFFSET]);\n }\n\n function senderAddressB20(bytes calldata _packet) internal pure returns (address) {\n return sender(_packet).toAddress();\n }\n\n function dstEid(bytes calldata _packet) internal pure returns (uint32) {\n return uint32(bytes4(_packet[DST_EID_OFFSET:RECEIVER_OFFSET]));\n }\n\n function receiver(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[RECEIVER_OFFSET:GUID_OFFSET]);\n }\n\n function receiverB20(bytes calldata _packet) internal pure returns (address) {\n return receiver(_packet).toAddress();\n }\n\n function guid(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[GUID_OFFSET:MESSAGE_OFFSET]);\n }\n\n function message(bytes calldata _packet) internal pure returns (bytes calldata) {\n return bytes(_packet[MESSAGE_OFFSET:]);\n }\n\n function payload(bytes calldata _packet) internal pure returns (bytes calldata) {\n return bytes(_packet[GUID_OFFSET:]);\n }\n\n function payloadHash(bytes calldata _packet) internal pure returns (bytes32) {\n return keccak256(payload(_packet));\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { ILayerZeroEndpointV2 } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\n\n/**\n * @title IOAppCore\n */\ninterface IOAppCore {\n // Custom error messages\n error OnlyPeer(uint32 eid, bytes32 sender);\n error NoPeer(uint32 eid);\n error InvalidEndpointCall();\n error InvalidDelegate();\n\n // Event emitted when a peer (OApp) is set for a corresponding endpoint\n event PeerSet(uint32 eid, bytes32 peer);\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n */\n function oAppVersion() external view returns (uint64 senderVersion, uint64 receiverVersion);\n\n /**\n * @notice Retrieves the LayerZero endpoint associated with the OApp.\n * @return iEndpoint The LayerZero endpoint as an interface.\n */\n function endpoint() external view returns (ILayerZeroEndpointV2 iEndpoint);\n\n /**\n * @notice Retrieves the peer (OApp) associated with a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @return peer The peer address (OApp instance) associated with the corresponding endpoint.\n */\n function peers(uint32 _eid) external view returns (bytes32 peer);\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n */\n function setPeer(uint32 _eid, bytes32 _peer) external;\n\n /**\n * @notice Sets the delegate address for the OApp Core.\n * @param _delegate The address of the delegate to be set.\n */\n function setDelegate(address _delegate) external;\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppMsgInspector.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n/**\n * @title IOAppMsgInspector\n * @dev Interface for the OApp Message Inspector, allowing examination of message and options contents.\n */\ninterface IOAppMsgInspector {\n // Custom error message for inspection failure\n error InspectionFailed(bytes message, bytes options);\n\n /**\n * @notice Allows the inspector to examine LayerZero message contents and optionally throw a revert if invalid.\n * @param _message The message payload to be inspected.\n * @param _options Additional options or parameters for inspection.\n * @return valid A boolean indicating whether the inspection passed (true) or failed (false).\n *\n * @dev Optionally done as a revert, OR use the boolean provided to handle the failure.\n */\n function inspect(bytes calldata _message, bytes calldata _options) external view returns (bool valid);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppOptionsType3.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Struct representing enforced option parameters.\n */\nstruct EnforcedOptionParam {\n uint32 eid; // Endpoint ID\n uint16 msgType; // Message Type\n bytes options; // Additional options\n}\n\n/**\n * @title IOAppOptionsType3\n * @dev Interface for the OApp with Type 3 Options, allowing the setting and combining of enforced options.\n */\ninterface IOAppOptionsType3 {\n // Custom error message for invalid options\n error InvalidOptions(bytes options);\n\n // Event emitted when enforced options are set\n event EnforcedOptionSet(EnforcedOptionParam[] _enforcedOptions);\n\n /**\n * @notice Sets enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n */\n function setEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) external;\n\n /**\n * @notice Combines options for a given endpoint and message type.\n * @param _eid The endpoint ID.\n * @param _msgType The OApp message type.\n * @param _extraOptions Additional options passed by the caller.\n * @return options The combination of caller specified options AND enforced options.\n */\n function combineOptions(\n uint32 _eid,\n uint16 _msgType,\n bytes calldata _extraOptions\n ) external view returns (bytes memory options);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\nimport { ILayerZeroReceiver, Origin } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroReceiver.sol\";\n\ninterface IOAppReceiver is ILayerZeroReceiver {\n /**\n * @notice Indicates whether an address is an approved composeMsg sender to the Endpoint.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _message The lzReceive payload.\n * @param _sender The sender address.\n * @return isSender Is a valid sender.\n *\n * @dev Applications can optionally choose to implement a separate composeMsg sender that is NOT the bridging layer.\n * @dev The default sender IS the OAppReceiver implementer.\n */\n function isComposeMsgSender(\n Origin calldata _origin,\n bytes calldata _message,\n address _sender\n ) external view returns (bool isSender);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IOAppOptionsType3, EnforcedOptionParam } from \"../interfaces/IOAppOptionsType3.sol\";\n\n/**\n * @title OAppOptionsType3\n * @dev Abstract contract implementing the IOAppOptionsType3 interface with type 3 options.\n */\nabstract contract OAppOptionsType3 is IOAppOptionsType3, Ownable {\n uint16 internal constant OPTION_TYPE_3 = 3;\n\n // @dev The \"msgType\" should be defined in the child contract.\n mapping(uint32 eid => mapping(uint16 msgType => bytes enforcedOption)) public enforcedOptions;\n\n /**\n * @dev Sets the enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Provides a way for the OApp to enforce things like paying for PreCrime, AND/OR minimum dst lzReceive gas amounts etc.\n * @dev These enforced options can vary as the potential options/execution on the remote may differ as per the msgType.\n * eg. Amount of lzReceive() gas necessary to deliver a lzCompose() message adds overhead you dont want to pay\n * if you are only making a standard LayerZero message ie. lzReceive() WITHOUT sendCompose().\n */\n function setEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) public virtual onlyOwner {\n _setEnforcedOptions(_enforcedOptions);\n }\n\n /**\n * @dev Sets the enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n *\n * @dev Provides a way for the OApp to enforce things like paying for PreCrime, AND/OR minimum dst lzReceive gas amounts etc.\n * @dev These enforced options can vary as the potential options/execution on the remote may differ as per the msgType.\n * eg. Amount of lzReceive() gas necessary to deliver a lzCompose() message adds overhead you dont want to pay\n * if you are only making a standard LayerZero message ie. lzReceive() WITHOUT sendCompose().\n */\n function _setEnforcedOptions(EnforcedOptionParam[] memory _enforcedOptions) internal virtual {\n for (uint256 i = 0; i < _enforcedOptions.length; i++) {\n // @dev Enforced options are only available for optionType 3, as type 1 and 2 dont support combining.\n _assertOptionsType3(_enforcedOptions[i].options);\n enforcedOptions[_enforcedOptions[i].eid][_enforcedOptions[i].msgType] = _enforcedOptions[i].options;\n }\n\n emit EnforcedOptionSet(_enforcedOptions);\n }\n\n /**\n * @notice Combines options for a given endpoint and message type.\n * @param _eid The endpoint ID.\n * @param _msgType The OAPP message type.\n * @param _extraOptions Additional options passed by the caller.\n * @return options The combination of caller specified options AND enforced options.\n *\n * @dev If there is an enforced lzReceive option:\n * - {gasLimit: 200k, msg.value: 1 ether} AND a caller supplies a lzReceive option: {gasLimit: 100k, msg.value: 0.5 ether}\n * - The resulting options will be {gasLimit: 300k, msg.value: 1.5 ether} when the message is executed on the remote lzReceive() function.\n * @dev This presence of duplicated options is handled off-chain in the verifier/executor.\n */\n function combineOptions(\n uint32 _eid,\n uint16 _msgType,\n bytes calldata _extraOptions\n ) public view virtual returns (bytes memory) {\n bytes memory enforced = enforcedOptions[_eid][_msgType];\n\n // No enforced options, pass whatever the caller supplied, even if it's empty or legacy type 1/2 options.\n if (enforced.length == 0) return _extraOptions;\n\n // No caller options, return enforced\n if (_extraOptions.length == 0) return enforced;\n\n // @dev If caller provided _extraOptions, must be type 3 as its the ONLY type that can be combined.\n if (_extraOptions.length >= 2) {\n _assertOptionsType3(_extraOptions);\n // @dev Remove the first 2 bytes containing the type from the _extraOptions and combine with enforced.\n return bytes.concat(enforced, _extraOptions[2:]);\n }\n\n // No valid set of options was found.\n revert InvalidOptions(_extraOptions);\n }\n\n /**\n * @dev Internal function to assert that options are of type 3.\n * @param _options The options to be checked.\n */\n function _assertOptionsType3(bytes memory _options) internal pure virtual {\n uint16 optionsType;\n assembly {\n optionsType := mload(add(_options, 2))\n }\n if (optionsType != OPTION_TYPE_3) revert InvalidOptions(_options);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { BytesLib } from \"solidity-bytes-utils/contracts/BytesLib.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { ExecutorOptions } from \"@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol\";\nimport { DVNOptions } from \"@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/DVNOptions.sol\";\n\n/**\n * @title OptionsBuilder\n * @dev Library for building and encoding various message options.\n */\nlibrary OptionsBuilder {\n using SafeCast for uint256;\n using BytesLib for bytes;\n\n // Constants for options types\n uint16 internal constant TYPE_1 = 1; // legacy options type 1\n uint16 internal constant TYPE_2 = 2; // legacy options type 2\n uint16 internal constant TYPE_3 = 3;\n\n // Custom error message\n error InvalidSize(uint256 max, uint256 actual);\n error InvalidOptionType(uint16 optionType);\n\n // Modifier to ensure only options of type 3 are used\n modifier onlyType3(bytes memory _options) {\n if (_options.toUint16(0) != TYPE_3) revert InvalidOptionType(_options.toUint16(0));\n _;\n }\n\n /**\n * @dev Creates a new options container with type 3.\n * @return options The newly created options container.\n */\n function newOptions() internal pure returns (bytes memory) {\n return abi.encodePacked(TYPE_3);\n }\n\n /**\n * @dev Adds an executor LZ receive option to the existing options.\n * @param _options The existing options container.\n * @param _gas The gasLimit used on the lzReceive() function in the OApp.\n * @param _value The msg.value passed to the lzReceive() function in the OApp.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed by the executor\n * eg. if (_gas: 200k, and _value: 1 ether) AND (_gas: 100k, _value: 0.5 ether) are sent in an option to the LayerZeroEndpoint,\n * that becomes (300k, 1.5 ether) when the message is executed on the remote lzReceive() function.\n */\n function addExecutorLzReceiveOption(\n bytes memory _options,\n uint128 _gas,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzReceiveOption(_gas, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZRECEIVE, option);\n }\n\n /**\n * @dev Adds an executor native drop option to the existing options.\n * @param _options The existing options container.\n * @param _amount The amount for the native value that is airdropped to the 'receiver'.\n * @param _receiver The receiver address for the native drop option.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed by the executor on the remote chain.\n */\n function addExecutorNativeDropOption(\n bytes memory _options,\n uint128 _amount,\n bytes32 _receiver\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeNativeDropOption(_amount, _receiver);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_NATIVE_DROP, option);\n }\n\n // /**\n // * @dev Adds an executor native drop option to the existing options.\n // * @param _options The existing options container.\n // * @param _amount The amount for the native value that is airdropped to the 'receiver'.\n // * @param _receiver The receiver address for the native drop option.\n // * @return options The updated options container.\n // *\n // * @dev When multiples of this option are added, they are summed by the executor on the remote chain.\n // */\n function addExecutorLzReadOption(\n bytes memory _options,\n uint128 _gas,\n uint32 _size,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzReadOption(_gas, _size, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZREAD, option);\n }\n\n /**\n * @dev Adds an executor LZ compose option to the existing options.\n * @param _options The existing options container.\n * @param _index The index for the lzCompose() function call.\n * @param _gas The gasLimit for the lzCompose() function call.\n * @param _value The msg.value for the lzCompose() function call.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed PER index by the executor on the remote chain.\n * @dev If the OApp sends N lzCompose calls on the remote, you must provide N incremented indexes starting with 0.\n * ie. When your remote OApp composes (N = 3) messages, you must set this option for index 0,1,2\n */\n function addExecutorLzComposeOption(\n bytes memory _options,\n uint16 _index,\n uint128 _gas,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzComposeOption(_index, _gas, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZCOMPOSE, option);\n }\n\n /**\n * @dev Adds an executor ordered execution option to the existing options.\n * @param _options The existing options container.\n * @return options The updated options container.\n */\n function addExecutorOrderedExecutionOption(\n bytes memory _options\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_ORDERED_EXECUTION, bytes(\"\"));\n }\n\n /**\n * @dev Adds a DVN pre-crime option to the existing options.\n * @param _options The existing options container.\n * @param _dvnIdx The DVN index for the pre-crime option.\n * @return options The updated options container.\n */\n function addDVNPreCrimeOption(\n bytes memory _options,\n uint8 _dvnIdx\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return addDVNOption(_options, _dvnIdx, DVNOptions.OPTION_TYPE_PRECRIME, bytes(\"\"));\n }\n\n /**\n * @dev Adds an executor option to the existing options.\n * @param _options The existing options container.\n * @param _optionType The type of the executor option.\n * @param _option The encoded data for the executor option.\n * @return options The updated options container.\n */\n function addExecutorOption(\n bytes memory _options,\n uint8 _optionType,\n bytes memory _option\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return\n abi.encodePacked(\n _options,\n ExecutorOptions.WORKER_ID,\n _option.length.toUint16() + 1, // +1 for optionType\n _optionType,\n _option\n );\n }\n\n /**\n * @dev Adds a DVN option to the existing options.\n * @param _options The existing options container.\n * @param _dvnIdx The DVN index for the DVN option.\n * @param _optionType The type of the DVN option.\n * @param _option The encoded data for the DVN option.\n * @return options The updated options container.\n */\n function addDVNOption(\n bytes memory _options,\n uint8 _dvnIdx,\n uint8 _optionType,\n bytes memory _option\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return\n abi.encodePacked(\n _options,\n DVNOptions.WORKER_ID,\n _option.length.toUint16() + 2, // +2 for optionType and dvnIdx\n _dvnIdx,\n _optionType,\n _option\n );\n }\n\n /**\n * @dev Encodes legacy options of type 1.\n * @param _executionGas The gasLimit value passed to lzReceive().\n * @return legacyOptions The encoded legacy options.\n */\n function encodeLegacyOptionsType1(uint256 _executionGas) internal pure returns (bytes memory) {\n if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);\n return abi.encodePacked(TYPE_1, _executionGas);\n }\n\n /**\n * @dev Encodes legacy options of type 2.\n * @param _executionGas The gasLimit value passed to lzReceive().\n * @param _nativeForDst The amount of native air dropped to the receiver.\n * @param _receiver The _nativeForDst receiver address.\n * @return legacyOptions The encoded legacy options of type 2.\n */\n function encodeLegacyOptionsType2(\n uint256 _executionGas,\n uint256 _nativeForDst,\n bytes memory _receiver // @dev Use bytes instead of bytes32 in legacy type 2 for _receiver.\n ) internal pure returns (bytes memory) {\n if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);\n if (_nativeForDst > type(uint128).max) revert InvalidSize(type(uint128).max, _nativeForDst);\n if (_receiver.length > 32) revert InvalidSize(32, _receiver.length);\n return abi.encodePacked(TYPE_2, _executionGas, _nativeForDst, _receiver);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n// @dev Import the 'MessagingFee' and 'MessagingReceipt' so it's exposed to OApp implementers\n// solhint-disable-next-line no-unused-import\nimport { OAppSender, MessagingFee, MessagingReceipt } from \"./OAppSender.sol\";\n// @dev Import the 'Origin' so it's exposed to OApp implementers\n// solhint-disable-next-line no-unused-import\nimport { OAppReceiver, Origin } from \"./OAppReceiver.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OApp\n * @dev Abstract contract serving as the base for OApp implementation, combining OAppSender and OAppReceiver functionality.\n */\nabstract contract OApp is OAppSender, OAppReceiver {\n /**\n * @dev Constructor to initialize the OApp with the provided endpoint and owner.\n * @param _endpoint The address of the LOCAL LayerZero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(address _endpoint, address _delegate) OAppCore(_endpoint, _delegate) {}\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol implementation.\n * @return receiverVersion The version of the OAppReceiver.sol implementation.\n */\n function oAppVersion()\n public\n pure\n virtual\n override(OAppSender, OAppReceiver)\n returns (uint64 senderVersion, uint64 receiverVersion)\n {\n return (SENDER_VERSION, RECEIVER_VERSION);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IOAppCore, ILayerZeroEndpointV2 } from \"./interfaces/IOAppCore.sol\";\n\n/**\n * @title OAppCore\n * @dev Abstract contract implementing the IOAppCore interface with basic OApp configurations.\n */\nabstract contract OAppCore is IOAppCore, Ownable {\n // The LayerZero endpoint associated with the given OApp\n ILayerZeroEndpointV2 public immutable endpoint;\n\n // Mapping to store peers associated with corresponding endpoints\n mapping(uint32 eid => bytes32 peer) public peers;\n\n /**\n * @dev Constructor to initialize the OAppCore with the provided endpoint and delegate.\n * @param _endpoint The address of the LOCAL Layer Zero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n *\n * @dev The delegate typically should be set as the owner of the contract.\n */\n constructor(address _endpoint, address _delegate) {\n endpoint = ILayerZeroEndpointV2(_endpoint);\n\n if (_delegate == address(0)) revert InvalidDelegate();\n endpoint.setDelegate(_delegate);\n }\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.\n * @dev Set this to bytes32(0) to remove the peer address.\n * @dev Peer is a bytes32 to accommodate non-evm chains.\n */\n function setPeer(uint32 _eid, bytes32 _peer) public virtual onlyOwner {\n _setPeer(_eid, _peer);\n }\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n *\n * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.\n * @dev Set this to bytes32(0) to remove the peer address.\n * @dev Peer is a bytes32 to accommodate non-evm chains.\n */\n function _setPeer(uint32 _eid, bytes32 _peer) internal virtual {\n peers[_eid] = _peer;\n emit PeerSet(_eid, _peer);\n }\n\n /**\n * @notice Internal function to get the peer address associated with a specific endpoint; reverts if NOT set.\n * ie. the peer is set to bytes32(0).\n * @param _eid The endpoint ID.\n * @return peer The address of the peer associated with the specified endpoint.\n */\n function _getPeerOrRevert(uint32 _eid) internal view virtual returns (bytes32) {\n bytes32 peer = peers[_eid];\n if (peer == bytes32(0)) revert NoPeer(_eid);\n return peer;\n }\n\n /**\n * @notice Sets the delegate address for the OApp.\n * @param _delegate The address of the delegate to be set.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Provides the ability for a delegate to set configs, on behalf of the OApp, directly on the Endpoint contract.\n */\n function setDelegate(address _delegate) public onlyOwner {\n endpoint.setDelegate(_delegate);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IOAppReceiver, Origin } from \"./interfaces/IOAppReceiver.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OAppReceiver\n * @dev Abstract contract implementing the ILayerZeroReceiver interface and extending OAppCore for OApp receivers.\n */\nabstract contract OAppReceiver is IOAppReceiver, OAppCore {\n // Custom error message for when the caller is not the registered endpoint/\n error OnlyEndpoint(address addr);\n\n // @dev The version of the OAppReceiver implementation.\n // @dev Version is bumped when changes are made to this contract.\n uint64 internal constant RECEIVER_VERSION = 2;\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n *\n * @dev Providing 0 as the default for OAppSender version. Indicates that the OAppSender is not implemented.\n * ie. this is a RECEIVE only OApp.\n * @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions.\n */\n function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {\n return (0, RECEIVER_VERSION);\n }\n\n /**\n * @notice Indicates whether an address is an approved composeMsg sender to the Endpoint.\n * @dev _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @dev _message The lzReceive payload.\n * @param _sender The sender address.\n * @return isSender Is a valid sender.\n *\n * @dev Applications can optionally choose to implement separate composeMsg senders that are NOT the bridging layer.\n * @dev The default sender IS the OAppReceiver implementer.\n */\n function isComposeMsgSender(\n Origin calldata /*_origin*/,\n bytes calldata /*_message*/,\n address _sender\n ) public view virtual returns (bool) {\n return _sender == address(this);\n }\n\n /**\n * @notice Checks if the path initialization is allowed based on the provided origin.\n * @param origin The origin information containing the source endpoint and sender address.\n * @return Whether the path has been initialized.\n *\n * @dev This indicates to the endpoint that the OApp has enabled msgs for this particular path to be received.\n * @dev This defaults to assuming if a peer has been set, its initialized.\n * Can be overridden by the OApp if there is other logic to determine this.\n */\n function allowInitializePath(Origin calldata origin) public view virtual returns (bool) {\n return peers[origin.srcEid] == origin.sender;\n }\n\n /**\n * @notice Retrieves the next nonce for a given source endpoint and sender address.\n * @dev _srcEid The source endpoint ID.\n * @dev _sender The sender address.\n * @return nonce The next nonce.\n *\n * @dev The path nonce starts from 1. If 0 is returned it means that there is NO nonce ordered enforcement.\n * @dev Is required by the off-chain executor to determine the OApp expects msg execution is ordered.\n * @dev This is also enforced by the OApp.\n * @dev By default this is NOT enabled. ie. nextNonce is hardcoded to return 0.\n */\n function nextNonce(uint32 /*_srcEid*/, bytes32 /*_sender*/) public view virtual returns (uint64 nonce) {\n return 0;\n }\n\n /**\n * @dev Entry point for receiving messages or packets from the endpoint.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The payload of the received message.\n * @param _executor The address of the executor for the received message.\n * @param _extraData Additional arbitrary data provided by the corresponding executor.\n *\n * @dev Entry point for receiving msg/packet from the LayerZero endpoint.\n */\n function lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) public payable virtual {\n // Ensures that only the endpoint can attempt to lzReceive() messages to this OApp.\n if (address(endpoint) != msg.sender) revert OnlyEndpoint(msg.sender);\n\n // Ensure that the sender matches the expected peer for the source endpoint.\n if (_getPeerOrRevert(_origin.srcEid) != _origin.sender) revert OnlyPeer(_origin.srcEid, _origin.sender);\n\n // Call the internal OApp implementation of lzReceive.\n _lzReceive(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Internal function to implement lzReceive logic without needing to copy the basic parameter validation.\n */\n function _lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual;\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { SafeERC20, IERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { MessagingParams, MessagingFee, MessagingReceipt } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OAppSender\n * @dev Abstract contract implementing the OAppSender functionality for sending messages to a LayerZero endpoint.\n */\nabstract contract OAppSender is OAppCore {\n using SafeERC20 for IERC20;\n\n // Custom error messages\n error NotEnoughNative(uint256 msgValue);\n error LzTokenUnavailable();\n\n // @dev The version of the OAppSender implementation.\n // @dev Version is bumped when changes are made to this contract.\n uint64 internal constant SENDER_VERSION = 1;\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n *\n * @dev Providing 0 as the default for OAppReceiver version. Indicates that the OAppReceiver is not implemented.\n * ie. this is a SEND only OApp.\n * @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions\n */\n function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {\n return (SENDER_VERSION, 0);\n }\n\n /**\n * @dev Internal function to interact with the LayerZero EndpointV2.quote() for fee calculation.\n * @param _dstEid The destination endpoint ID.\n * @param _message The message payload.\n * @param _options Additional options for the message.\n * @param _payInLzToken Flag indicating whether to pay the fee in LZ tokens.\n * @return fee The calculated MessagingFee for the message.\n * - nativeFee: The native fee for the message.\n * - lzTokenFee: The LZ token fee for the message.\n */\n function _quote(\n uint32 _dstEid,\n bytes memory _message,\n bytes memory _options,\n bool _payInLzToken\n ) internal view virtual returns (MessagingFee memory fee) {\n return\n endpoint.quote(\n MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _payInLzToken),\n address(this)\n );\n }\n\n /**\n * @dev Internal function to interact with the LayerZero EndpointV2.send() for sending a message.\n * @param _dstEid The destination endpoint ID.\n * @param _message The message payload.\n * @param _options Additional options for the message.\n * @param _fee The calculated LayerZero fee for the message.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess fee values sent to the endpoint.\n * @return receipt The receipt for the sent message.\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function _lzSend(\n uint32 _dstEid,\n bytes memory _message,\n bytes memory _options,\n MessagingFee memory _fee,\n address _refundAddress\n ) internal virtual returns (MessagingReceipt memory receipt) {\n // @dev Push corresponding fees to the endpoint, any excess is sent back to the _refundAddress from the endpoint.\n uint256 messageValue = _payNative(_fee.nativeFee);\n if (_fee.lzTokenFee > 0) _payLzToken(_fee.lzTokenFee);\n\n return\n // solhint-disable-next-line check-send-result\n endpoint.send{ value: messageValue }(\n MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _fee.lzTokenFee > 0),\n _refundAddress\n );\n }\n\n /**\n * @dev Internal function to pay the native fee associated with the message.\n * @param _nativeFee The native fee to be paid.\n * @return nativeFee The amount of native currency paid.\n *\n * @dev If the OApp needs to initiate MULTIPLE LayerZero messages in a single transaction,\n * this will need to be overridden because msg.value would contain multiple lzFees.\n * @dev Should be overridden in the event the LayerZero endpoint requires a different native currency.\n * @dev Some EVMs use an ERC20 as a method for paying transactions/gasFees.\n * @dev The endpoint is EITHER/OR, ie. it will NOT support both types of native payment at a time.\n */\n function _payNative(uint256 _nativeFee) internal virtual returns (uint256 nativeFee) {\n if (msg.value != _nativeFee) revert NotEnoughNative(msg.value);\n return _nativeFee;\n }\n\n /**\n * @dev Internal function to pay the LZ token fee associated with the message.\n * @param _lzTokenFee The LZ token fee to be paid.\n *\n * @dev If the caller is trying to pay in the specified lzToken, then the lzTokenFee is passed to the endpoint.\n * @dev Any excess sent, is passed back to the specified _refundAddress in the _lzSend().\n */\n function _payLzToken(uint256 _lzTokenFee) internal virtual {\n // @dev Cannot cache the token because it is not immutable in the endpoint.\n address lzToken = endpoint.lzToken();\n if (lzToken == address(0)) revert LzTokenUnavailable();\n\n // Pay LZ token fee by sending tokens to the endpoint.\n IERC20(lzToken).safeTransferFrom(msg.sender, address(endpoint), _lzTokenFee);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/interfaces/IOAppPreCrimeSimulator.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n// @dev Import the Origin so it's exposed to OAppPreCrimeSimulator implementers.\n// solhint-disable-next-line no-unused-import\nimport { InboundPacket, Origin } from \"../libs/Packet.sol\";\n\n/**\n * @title IOAppPreCrimeSimulator Interface\n * @dev Interface for the preCrime simulation functionality in an OApp.\n */\ninterface IOAppPreCrimeSimulator {\n // @dev simulation result used in PreCrime implementation\n error SimulationResult(bytes result);\n error OnlySelf();\n\n /**\n * @dev Emitted when the preCrime contract address is set.\n * @param preCrimeAddress The address of the preCrime contract.\n */\n event PreCrimeSet(address preCrimeAddress);\n\n /**\n * @dev Retrieves the address of the preCrime contract implementation.\n * @return The address of the preCrime contract.\n */\n function preCrime() external view returns (address);\n\n /**\n * @dev Retrieves the address of the OApp contract.\n * @return The address of the OApp contract.\n */\n function oApp() external view returns (address);\n\n /**\n * @dev Sets the preCrime contract address.\n * @param _preCrime The address of the preCrime contract.\n */\n function setPreCrime(address _preCrime) external;\n\n /**\n * @dev Mocks receiving a packet, then reverts with a series of data to infer the state/result.\n * @param _packets An array of LayerZero InboundPacket objects representing received packets.\n */\n function lzReceiveAndRevert(InboundPacket[] calldata _packets) external payable;\n\n /**\n * @dev checks if the specified peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint Id to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n */\n function isPeer(uint32 _eid, bytes32 _peer) external view returns (bool);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/interfaces/IPreCrime.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\nstruct PreCrimePeer {\n uint32 eid;\n bytes32 preCrime;\n bytes32 oApp;\n}\n\n// TODO not done yet\ninterface IPreCrime {\n error OnlyOffChain();\n\n // for simulate()\n error PacketOversize(uint256 max, uint256 actual);\n error PacketUnsorted();\n error SimulationFailed(bytes reason);\n\n // for preCrime()\n error SimulationResultNotFound(uint32 eid);\n error InvalidSimulationResult(uint32 eid, bytes reason);\n error CrimeFound(bytes crime);\n\n function getConfig(bytes[] calldata _packets, uint256[] calldata _packetMsgValues) external returns (bytes memory);\n\n function simulate(\n bytes[] calldata _packets,\n uint256[] calldata _packetMsgValues\n ) external payable returns (bytes memory);\n\n function buildSimulationResult() external view returns (bytes memory);\n\n function preCrime(\n bytes[] calldata _packets,\n uint256[] calldata _packetMsgValues,\n bytes[] calldata _simulations\n ) external;\n\n function version() external view returns (uint64 major, uint8 minor);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/libs/Packet.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Origin } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\nimport { PacketV1Codec } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol\";\n\n/**\n * @title InboundPacket\n * @dev Structure representing an inbound packet received by the contract.\n */\nstruct InboundPacket {\n Origin origin; // Origin information of the packet.\n uint32 dstEid; // Destination endpointId of the packet.\n address receiver; // Receiver address for the packet.\n bytes32 guid; // Unique identifier of the packet.\n uint256 value; // msg.value of the packet.\n address executor; // Executor address for the packet.\n bytes message; // Message payload of the packet.\n bytes extraData; // Additional arbitrary data for the packet.\n}\n\n/**\n * @title PacketDecoder\n * @dev Library for decoding LayerZero packets.\n */\nlibrary PacketDecoder {\n using PacketV1Codec for bytes;\n\n /**\n * @dev Decode an inbound packet from the given packet data.\n * @param _packet The packet data to decode.\n * @return packet An InboundPacket struct representing the decoded packet.\n */\n function decode(bytes calldata _packet) internal pure returns (InboundPacket memory packet) {\n packet.origin = Origin(_packet.srcEid(), _packet.sender(), _packet.nonce());\n packet.dstEid = _packet.dstEid();\n packet.receiver = _packet.receiverB20();\n packet.guid = _packet.guid();\n packet.message = _packet.message();\n }\n\n /**\n * @dev Decode multiple inbound packets from the given packet data and associated message values.\n * @param _packets An array of packet data to decode.\n * @param _packetMsgValues An array of associated message values for each packet.\n * @return packets An array of InboundPacket structs representing the decoded packets.\n */\n function decode(\n bytes[] calldata _packets,\n uint256[] memory _packetMsgValues\n ) internal pure returns (InboundPacket[] memory packets) {\n packets = new InboundPacket[](_packets.length);\n for (uint256 i = 0; i < _packets.length; i++) {\n bytes calldata packet = _packets[i];\n packets[i] = PacketDecoder.decode(packet);\n // @dev Allows the verifier to specify the msg.value that gets passed in lzReceive.\n packets[i].value = _packetMsgValues[i];\n }\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/OAppPreCrimeSimulator.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IPreCrime } from \"./interfaces/IPreCrime.sol\";\nimport { IOAppPreCrimeSimulator, InboundPacket, Origin } from \"./interfaces/IOAppPreCrimeSimulator.sol\";\n\n/**\n * @title OAppPreCrimeSimulator\n * @dev Abstract contract serving as the base for preCrime simulation functionality in an OApp.\n */\nabstract contract OAppPreCrimeSimulator is IOAppPreCrimeSimulator, Ownable {\n // The address of the preCrime implementation.\n address public preCrime;\n\n /**\n * @dev Retrieves the address of the OApp contract.\n * @return The address of the OApp contract.\n *\n * @dev The simulator contract is the base contract for the OApp by default.\n * @dev If the simulator is a separate contract, override this function.\n */\n function oApp() external view virtual returns (address) {\n return address(this);\n }\n\n /**\n * @dev Sets the preCrime contract address.\n * @param _preCrime The address of the preCrime contract.\n */\n function setPreCrime(address _preCrime) public virtual onlyOwner {\n preCrime = _preCrime;\n emit PreCrimeSet(_preCrime);\n }\n\n /**\n * @dev Interface for pre-crime simulations. Always reverts at the end with the simulation results.\n * @param _packets An array of InboundPacket objects representing received packets to be delivered.\n *\n * @dev WARNING: MUST revert at the end with the simulation results.\n * @dev Gives the preCrime implementation the ability to mock sending packets to the lzReceive function,\n * WITHOUT actually executing them.\n */\n function lzReceiveAndRevert(InboundPacket[] calldata _packets) public payable virtual {\n for (uint256 i = 0; i < _packets.length; i++) {\n InboundPacket calldata packet = _packets[i];\n\n // Ignore packets that are not from trusted peers.\n if (!isPeer(packet.origin.srcEid, packet.origin.sender)) continue;\n\n // @dev Because a verifier is calling this function, it doesnt have access to executor params:\n // - address _executor\n // - bytes calldata _extraData\n // preCrime will NOT work for OApps that rely on these two parameters inside of their _lzReceive().\n // They are instead stubbed to default values, address(0) and bytes(\"\")\n // @dev Calling this.lzReceiveSimulate removes ability for assembly return 0 callstack exit,\n // which would cause the revert to be ignored.\n this.lzReceiveSimulate{ value: packet.value }(\n packet.origin,\n packet.guid,\n packet.message,\n packet.executor,\n packet.extraData\n );\n }\n\n // @dev Revert with the simulation results. msg.sender must implement IPreCrime.buildSimulationResult().\n revert SimulationResult(IPreCrime(msg.sender).buildSimulationResult());\n }\n\n /**\n * @dev Is effectively an internal function because msg.sender must be address(this).\n * Allows resetting the call stack for 'internal' calls.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _guid The unique identifier of the packet.\n * @param _message The message payload of the packet.\n * @param _executor The executor address for the packet.\n * @param _extraData Additional data for the packet.\n */\n function lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) external payable virtual {\n // @dev Ensure ONLY can be called 'internally'.\n if (msg.sender != address(this)) revert OnlySelf();\n _lzReceiveSimulate(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Internal function to handle the OAppPreCrimeSimulator simulated receive.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The GUID of the LayerZero message.\n * @param _message The LayerZero message.\n * @param _executor The address of the off-chain executor.\n * @param _extraData Arbitrary data passed by the msg executor.\n *\n * @dev Enables the preCrime simulator to mock sending lzReceive() messages,\n * routes the msg down from the OAppPreCrimeSimulator, and back up to the OAppReceiver.\n */\n function _lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual;\n\n /**\n * @dev checks if the specified peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint Id to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n */\n function isPeer(uint32 _eid, bytes32 _peer) public view virtual returns (bool);\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/interfaces/IMintableBurnable.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\n/// @title Interface for mintable and burnable tokens\ninterface IMintableBurnable {\n \n /**\n * @notice Burns tokens from a specified account\n * @param _from Address from which tokens will be burned\n * @param _amount Amount of tokens to be burned\n * @return success Indicates whether the operation was successful\n */\n function burn(address _from, uint256 _amount) external returns (bool success);\n\n /**\n * @notice Mints tokens to a specified account\n * @param _to Address to which tokens will be minted\n * @param _amount Amount of tokens to be minted\n * @return success Indicates whether the operation was successful\n */\n function mint(address _to, uint256 _amount) external returns (bool success);\n}" + }, + "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { MessagingReceipt, MessagingFee } from \"@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol\";\n\n/**\n * @dev Struct representing token parameters for the OFT send() operation.\n */\nstruct SendParam {\n uint32 dstEid; // Destination endpoint ID.\n bytes32 to; // Recipient address.\n uint256 amountLD; // Amount to send in local decimals.\n uint256 minAmountLD; // Minimum amount to send in local decimals.\n bytes extraOptions; // Additional options supplied by the caller to be used in the LayerZero message.\n bytes composeMsg; // The composed message for the send() operation.\n bytes oftCmd; // The OFT command to be executed, unused in default OFT implementations.\n}\n\n/**\n * @dev Struct representing OFT limit information.\n * @dev These amounts can change dynamically and are up the specific oft implementation.\n */\nstruct OFTLimit {\n uint256 minAmountLD; // Minimum amount in local decimals that can be sent to the recipient.\n uint256 maxAmountLD; // Maximum amount in local decimals that can be sent to the recipient.\n}\n\n/**\n * @dev Struct representing OFT receipt information.\n */\nstruct OFTReceipt {\n uint256 amountSentLD; // Amount of tokens ACTUALLY debited from the sender in local decimals.\n // @dev In non-default implementations, the amountReceivedLD COULD differ from this value.\n uint256 amountReceivedLD; // Amount of tokens to be received on the remote side.\n}\n\n/**\n * @dev Struct representing OFT fee details.\n * @dev Future proof mechanism to provide a standardized way to communicate fees to things like a UI.\n */\nstruct OFTFeeDetail {\n int256 feeAmountLD; // Amount of the fee in local decimals.\n string description; // Description of the fee.\n}\n\n/**\n * @title IOFT\n * @dev Interface for the OftChain (OFT) token.\n * @dev Does not inherit ERC20 to accommodate usage by OFTAdapter as well.\n * @dev This specific interface ID is '0x02e49c2c'.\n */\ninterface IOFT {\n // Custom error messages\n error InvalidLocalDecimals();\n error SlippageExceeded(uint256 amountLD, uint256 minAmountLD);\n\n // Events\n event OFTSent(\n bytes32 indexed guid, // GUID of the OFT message.\n uint32 dstEid, // Destination Endpoint ID.\n address indexed fromAddress, // Address of the sender on the src chain.\n uint256 amountSentLD, // Amount of tokens sent in local decimals.\n uint256 amountReceivedLD // Amount of tokens received in local decimals.\n );\n event OFTReceived(\n bytes32 indexed guid, // GUID of the OFT message.\n uint32 srcEid, // Source Endpoint ID.\n address indexed toAddress, // Address of the recipient on the dst chain.\n uint256 amountReceivedLD // Amount of tokens received in local decimals.\n );\n\n /**\n * @notice Retrieves interfaceID and the version of the OFT.\n * @return interfaceId The interface ID.\n * @return version The version.\n *\n * @dev interfaceId: This specific interface ID is '0x02e49c2c'.\n * @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.\n * @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.\n * ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)\n */\n function oftVersion() external view returns (bytes4 interfaceId, uint64 version);\n\n /**\n * @notice Retrieves the address of the token associated with the OFT.\n * @return token The address of the ERC20 token implementation.\n */\n function token() external view returns (address);\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the 'token()' to send.\n * @return requiresApproval Needs approval of the underlying token implementation.\n *\n * @dev Allows things like wallet implementers to determine integration requirements,\n * without understanding the underlying token implementation.\n */\n function approvalRequired() external view returns (bool);\n\n /**\n * @notice Retrieves the shared decimals of the OFT.\n * @return sharedDecimals The shared decimals of the OFT.\n */\n function sharedDecimals() external view returns (uint8);\n\n /**\n * @notice Provides the fee breakdown and settings data for an OFT. Unused in the default implementation.\n * @param _sendParam The parameters for the send operation.\n * @return limit The OFT limit information.\n * @return oftFeeDetails The details of OFT fees.\n * @return receipt The OFT receipt information.\n */\n function quoteOFT(\n SendParam calldata _sendParam\n ) external view returns (OFTLimit memory, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory);\n\n /**\n * @notice Provides a quote for the send() operation.\n * @param _sendParam The parameters for the send() operation.\n * @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.\n * @return fee The calculated LayerZero messaging fee from the send() operation.\n *\n * @dev MessagingFee: LayerZero msg fee\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n */\n function quoteSend(SendParam calldata _sendParam, bool _payInLzToken) external view returns (MessagingFee memory);\n\n /**\n * @notice Executes the send() operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The fee information supplied by the caller.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds from fees etc. on the src.\n * @return receipt The LayerZero messaging receipt from the send() operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) external payable returns (MessagingReceipt memory, OFTReceipt memory);\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/libs/OFTComposeMsgCodec.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nlibrary OFTComposeMsgCodec {\n // Offset constants for decoding composed messages\n uint8 private constant NONCE_OFFSET = 8;\n uint8 private constant SRC_EID_OFFSET = 12;\n uint8 private constant AMOUNT_LD_OFFSET = 44;\n uint8 private constant COMPOSE_FROM_OFFSET = 76;\n\n /**\n * @dev Encodes a OFT composed message.\n * @param _nonce The nonce value.\n * @param _srcEid The source endpoint ID.\n * @param _amountLD The amount in local decimals.\n * @param _composeMsg The composed message.\n * @return _msg The encoded Composed message.\n */\n function encode(\n uint64 _nonce,\n uint32 _srcEid,\n uint256 _amountLD,\n bytes memory _composeMsg // 0x[composeFrom][composeMsg]\n ) internal pure returns (bytes memory _msg) {\n _msg = abi.encodePacked(_nonce, _srcEid, _amountLD, _composeMsg);\n }\n\n /**\n * @dev Retrieves the nonce for the composed message.\n * @param _msg The message.\n * @return The nonce value.\n */\n function nonce(bytes calldata _msg) internal pure returns (uint64) {\n return uint64(bytes8(_msg[:NONCE_OFFSET]));\n }\n\n /**\n * @dev Retrieves the source endpoint ID for the composed message.\n * @param _msg The message.\n * @return The source endpoint ID.\n */\n function srcEid(bytes calldata _msg) internal pure returns (uint32) {\n return uint32(bytes4(_msg[NONCE_OFFSET:SRC_EID_OFFSET]));\n }\n\n /**\n * @dev Retrieves the amount in local decimals from the composed message.\n * @param _msg The message.\n * @return The amount in local decimals.\n */\n function amountLD(bytes calldata _msg) internal pure returns (uint256) {\n return uint256(bytes32(_msg[SRC_EID_OFFSET:AMOUNT_LD_OFFSET]));\n }\n\n /**\n * @dev Retrieves the composeFrom value from the composed message.\n * @param _msg The message.\n * @return The composeFrom value.\n */\n function composeFrom(bytes calldata _msg) internal pure returns (bytes32) {\n return bytes32(_msg[AMOUNT_LD_OFFSET:COMPOSE_FROM_OFFSET]);\n }\n\n /**\n * @dev Retrieves the composed message.\n * @param _msg The message.\n * @return The composed message.\n */\n function composeMsg(bytes calldata _msg) internal pure returns (bytes memory) {\n return _msg[COMPOSE_FROM_OFFSET:];\n }\n\n /**\n * @dev Converts an address to bytes32.\n * @param _addr The address to convert.\n * @return The bytes32 representation of the address.\n */\n function addressToBytes32(address _addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(_addr)));\n }\n\n /**\n * @dev Converts bytes32 to an address.\n * @param _b The bytes32 value to convert.\n * @return The address representation of bytes32.\n */\n function bytes32ToAddress(bytes32 _b) internal pure returns (address) {\n return address(uint160(uint256(_b)));\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/libs/OFTMsgCodec.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nlibrary OFTMsgCodec {\n // Offset constants for encoding and decoding OFT messages\n uint8 private constant SEND_TO_OFFSET = 32;\n uint8 private constant SEND_AMOUNT_SD_OFFSET = 40;\n\n /**\n * @dev Encodes an OFT LayerZero message.\n * @param _sendTo The recipient address.\n * @param _amountShared The amount in shared decimals.\n * @param _composeMsg The composed message.\n * @return _msg The encoded message.\n * @return hasCompose A boolean indicating whether the message has a composed payload.\n */\n function encode(\n bytes32 _sendTo,\n uint64 _amountShared,\n bytes memory _composeMsg\n ) internal view returns (bytes memory _msg, bool hasCompose) {\n hasCompose = _composeMsg.length > 0;\n // @dev Remote chains will want to know the composed function caller ie. msg.sender on the src.\n _msg = hasCompose\n ? abi.encodePacked(_sendTo, _amountShared, addressToBytes32(msg.sender), _composeMsg)\n : abi.encodePacked(_sendTo, _amountShared);\n }\n\n /**\n * @dev Checks if the OFT message is composed.\n * @param _msg The OFT message.\n * @return A boolean indicating whether the message is composed.\n */\n function isComposed(bytes calldata _msg) internal pure returns (bool) {\n return _msg.length > SEND_AMOUNT_SD_OFFSET;\n }\n\n /**\n * @dev Retrieves the recipient address from the OFT message.\n * @param _msg The OFT message.\n * @return The recipient address.\n */\n function sendTo(bytes calldata _msg) internal pure returns (bytes32) {\n return bytes32(_msg[:SEND_TO_OFFSET]);\n }\n\n /**\n * @dev Retrieves the amount in shared decimals from the OFT message.\n * @param _msg The OFT message.\n * @return The amount in shared decimals.\n */\n function amountSD(bytes calldata _msg) internal pure returns (uint64) {\n return uint64(bytes8(_msg[SEND_TO_OFFSET:SEND_AMOUNT_SD_OFFSET]));\n }\n\n /**\n * @dev Retrieves the composed message from the OFT message.\n * @param _msg The OFT message.\n * @return The composed message.\n */\n function composeMsg(bytes calldata _msg) internal pure returns (bytes memory) {\n return _msg[SEND_AMOUNT_SD_OFFSET:];\n }\n\n /**\n * @dev Converts an address to bytes32.\n * @param _addr The address to convert.\n * @return The bytes32 representation of the address.\n */\n function addressToBytes32(address _addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(_addr)));\n }\n\n /**\n * @dev Converts bytes32 to an address.\n * @param _b The bytes32 value to convert.\n * @return The address representation of bytes32.\n */\n function bytes32ToAddress(bytes32 _b) internal pure returns (address) {\n return address(uint160(uint256(_b)));\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/MintBurnOFTAdapter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\n// External imports\nimport { IERC20, IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\n// Local imports\nimport { IMintableBurnable } from \"./interfaces/IMintableBurnable.sol\";\nimport { OFTCore } from \"./OFTCore.sol\";\n\n/**\n * @title MintBurnOFTAdapter\n * @notice A variant of the standard OFT Adapter that uses an existing ERC20's mint and burn mechanisms for cross-chain transfers.\n *\n * @dev Inherits from OFTCore and provides implementations for _debit and _credit functions using a mintable and burnable token.\n */\nabstract contract MintBurnOFTAdapter is OFTCore {\n /// @dev The underlying ERC20 token.\n IERC20 internal immutable innerToken;\n\n /// @notice The contract responsible for minting and burning tokens.\n IMintableBurnable public immutable minterBurner;\n\n /**\n * @notice Initializes the MintBurnOFTAdapter contract.\n *\n * @param _token The address of the underlying ERC20 token.\n * @param _minterBurner The contract responsible for minting and burning tokens.\n * @param _lzEndpoint The LayerZero endpoint address.\n * @param _delegate The address of the delegate.\n *\n * @dev Calls the OFTCore constructor with the token's decimals, the endpoint, and the delegate.\n */\n constructor(\n address _token,\n IMintableBurnable _minterBurner,\n address _lzEndpoint,\n address _delegate\n ) OFTCore(IERC20Metadata(_token).decimals(), _lzEndpoint, _delegate) {\n innerToken = IERC20(_token);\n minterBurner = _minterBurner;\n }\n\n /**\n * @notice Retrieves the address of the underlying ERC20 token.\n *\n * @return The address of the adapted ERC20 token.\n *\n * @dev In the case of MintBurnOFTAdapter, address(this) and erc20 are NOT the same contract.\n */\n function token() public view returns (address) {\n return address(innerToken);\n }\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the underlying token to send.\n *\n * @return requiresApproval True if approval is required, false otherwise.\n *\n * @dev In this MintBurnOFTAdapter, approval is NOT required because it uses mint and burn privileges.\n */\n function approvalRequired() external pure virtual returns (bool) {\n return false;\n }\n\n /**\n * @notice Burns tokens from the sender's balance to prepare for sending.\n *\n * @param _from The address to debit the tokens from.\n * @param _amountLD The amount of tokens to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination chain ID.\n *\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, i.e., 1 token in, 1 token out.\n * If the 'innerToken' applies something like a transfer fee, the default will NOT work.\n * A pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual override returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n (amountSentLD, amountReceivedLD) = _debitView(_amountLD, _minAmountLD, _dstEid);\n // Burns tokens from the caller.\n minterBurner.burn(_from, amountSentLD);\n }\n\n /**\n * @notice Mints tokens to the specified address upon receiving them.\n *\n * @param _to The address to credit the tokens to.\n * @param _amountLD The amount of tokens to credit in local decimals.\n *\n * @return amountReceivedLD The amount of tokens actually received in local decimals.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, i.e., 1 token in, 1 token out.\n * If the 'innerToken' applies something like a transfer fee, the default will NOT work.\n * A pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /* _srcEid */\n ) internal virtual override returns (uint256 amountReceivedLD) {\n if (_to == address(0x0)) _to = address(0xdead); // _mint(...) does not support address(0x0)\n // Mints the tokens and transfers to the recipient.\n minterBurner.mint(_to, _amountLD);\n // In the case of NON-default OFTAdapter, the amountLD MIGHT not be equal to amountReceivedLD.\n return _amountLD;\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/OFTAdapter.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IERC20Metadata, IERC20 } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IOFT, OFTCore } from \"./OFTCore.sol\";\n\n/**\n * @title OFTAdapter Contract\n * @dev OFTAdapter is a contract that adapts an ERC-20 token to the OFT functionality.\n *\n * @dev For existing ERC20 tokens, this can be used to convert the token to crosschain compatibility.\n * @dev WARNING: ONLY 1 of these should exist for a given global mesh,\n * unless you make a NON-default implementation of OFT and needs to be done very carefully.\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountSentLD/amountReceivedLD.\n */\nabstract contract OFTAdapter is OFTCore {\n using SafeERC20 for IERC20;\n\n IERC20 internal immutable innerToken;\n\n /**\n * @dev Constructor for the OFTAdapter contract.\n * @param _token The address of the ERC-20 token to be adapted.\n * @param _lzEndpoint The LayerZero endpoint address.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(\n address _token,\n address _lzEndpoint,\n address _delegate\n ) OFTCore(IERC20Metadata(_token).decimals(), _lzEndpoint, _delegate) {\n innerToken = IERC20(_token);\n }\n\n /**\n * @dev Retrieves the address of the underlying ERC20 implementation.\n * @return The address of the adapted ERC-20 token.\n *\n * @dev In the case of OFTAdapter, address(this) and erc20 are NOT the same contract.\n */\n function token() public view returns (address) {\n return address(innerToken);\n }\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the 'token()' to send.\n * @return requiresApproval Needs approval of the underlying token implementation.\n *\n * @dev In the case of default OFTAdapter, approval is required.\n * @dev In non-default OFTAdapter contracts with something like mint and burn privileges, it would NOT need approval.\n */\n function approvalRequired() external pure virtual returns (bool) {\n return true;\n }\n\n /**\n * @dev Locks tokens from the sender's specified balance in this contract.\n * @param _from The address to debit from.\n * @param _amountLD The amount of tokens to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination chain ID.\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev msg.sender will need to approve this _amountLD of tokens to be locked inside of the contract.\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual override returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n (amountSentLD, amountReceivedLD) = _debitView(_amountLD, _minAmountLD, _dstEid);\n // @dev Lock tokens by moving them into this contract from the caller.\n innerToken.safeTransferFrom(_from, address(this), amountSentLD);\n }\n\n /**\n * @dev Credits tokens to the specified address.\n * @param _to The address to credit the tokens to.\n * @param _amountLD The amount of tokens to credit in local decimals.\n * @dev _srcEid The source chain ID.\n * @return amountReceivedLD The amount of tokens ACTUALLY received in local decimals.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /*_srcEid*/\n ) internal virtual override returns (uint256 amountReceivedLD) {\n // @dev Unlock the tokens and transfer to the recipient.\n innerToken.safeTransfer(_to, _amountLD);\n // @dev In the case of NON-default OFTAdapter, the amountLD MIGHT not be == amountReceivedLD.\n return _amountLD;\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/OFTCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { OApp, Origin } from \"@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol\";\nimport { OAppOptionsType3 } from \"@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol\";\nimport { IOAppMsgInspector } from \"@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppMsgInspector.sol\";\n\nimport { OAppPreCrimeSimulator } from \"@layerzerolabs/oapp-evm/contracts/precrime/OAppPreCrimeSimulator.sol\";\n\nimport { IOFT, SendParam, OFTLimit, OFTReceipt, OFTFeeDetail, MessagingReceipt, MessagingFee } from \"./interfaces/IOFT.sol\";\nimport { OFTMsgCodec } from \"./libs/OFTMsgCodec.sol\";\nimport { OFTComposeMsgCodec } from \"./libs/OFTComposeMsgCodec.sol\";\n\n/**\n * @title OFTCore\n * @dev Abstract contract for the OftChain (OFT) token.\n */\nabstract contract OFTCore is IOFT, OApp, OAppPreCrimeSimulator, OAppOptionsType3 {\n using OFTMsgCodec for bytes;\n using OFTMsgCodec for bytes32;\n\n // @notice Provides a conversion rate when swapping between denominations of SD and LD\n // - shareDecimals == SD == shared Decimals\n // - localDecimals == LD == local decimals\n // @dev Considers that tokens have different decimal amounts on various chains.\n // @dev eg.\n // For a token\n // - locally with 4 decimals --> 1.2345 => uint(12345)\n // - remotely with 2 decimals --> 1.23 => uint(123)\n // - The conversion rate would be 10 ** (4 - 2) = 100\n // @dev If you want to send 1.2345 -> (uint 12345), you CANNOT represent that value on the remote,\n // you can only display 1.23 -> uint(123).\n // @dev To preserve the dust that would otherwise be lost on that conversion,\n // we need to unify a denomination that can be represented on ALL chains inside of the OFT mesh\n uint256 public immutable decimalConversionRate;\n\n // @notice Msg types that are used to identify the various OFT operations.\n // @dev This can be extended in child contracts for non-default oft operations\n // @dev These values are used in things like combineOptions() in OAppOptionsType3.sol.\n uint16 public constant SEND = 1;\n uint16 public constant SEND_AND_CALL = 2;\n\n // Address of an optional contract to inspect both 'message' and 'options'\n address public msgInspector;\n event MsgInspectorSet(address inspector);\n\n /**\n * @dev Constructor.\n * @param _localDecimals The decimals of the token on the local chain (this chain).\n * @param _endpoint The address of the LayerZero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(uint8 _localDecimals, address _endpoint, address _delegate) OApp(_endpoint, _delegate) {\n if (_localDecimals < sharedDecimals()) revert InvalidLocalDecimals();\n decimalConversionRate = 10 ** (_localDecimals - sharedDecimals());\n }\n\n /**\n * @notice Retrieves interfaceID and the version of the OFT.\n * @return interfaceId The interface ID.\n * @return version The version.\n *\n * @dev interfaceId: This specific interface ID is '0x02e49c2c'.\n * @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.\n * @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.\n * ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)\n */\n function oftVersion() external pure virtual returns (bytes4 interfaceId, uint64 version) {\n return (type(IOFT).interfaceId, 1);\n }\n\n /**\n * @dev Retrieves the shared decimals of the OFT.\n * @return The shared decimals of the OFT.\n *\n * @dev Sets an implicit cap on the amount of tokens, over uint64.max() will need some sort of outbound cap / totalSupply cap\n * Lowest common decimal denominator between chains.\n * Defaults to 6 decimal places to provide up to 18,446,744,073,709.551615 units (max uint64).\n * For tokens exceeding this totalSupply(), they will need to override the sharedDecimals function with something smaller.\n * ie. 4 sharedDecimals would be 1,844,674,407,370,955.1615\n */\n function sharedDecimals() public view virtual returns (uint8) {\n return 6;\n }\n\n /**\n * @dev Sets the message inspector address for the OFT.\n * @param _msgInspector The address of the message inspector.\n *\n * @dev This is an optional contract that can be used to inspect both 'message' and 'options'.\n * @dev Set it to address(0) to disable it, or set it to a contract address to enable it.\n */\n function setMsgInspector(address _msgInspector) public virtual onlyOwner {\n msgInspector = _msgInspector;\n emit MsgInspectorSet(_msgInspector);\n }\n\n /**\n * @notice Provides the fee breakdown and settings data for an OFT. Unused in the default implementation.\n * @param _sendParam The parameters for the send operation.\n * @return oftLimit The OFT limit information.\n * @return oftFeeDetails The details of OFT fees.\n * @return oftReceipt The OFT receipt information.\n */\n function quoteOFT(\n SendParam calldata _sendParam\n )\n external\n view\n virtual\n returns (OFTLimit memory oftLimit, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory oftReceipt)\n {\n uint256 minAmountLD = 0; // Unused in the default implementation.\n uint256 maxAmountLD = IERC20(this.token()).totalSupply(); // Unused in the default implementation.\n oftLimit = OFTLimit(minAmountLD, maxAmountLD);\n\n // Unused in the default implementation; reserved for future complex fee details.\n oftFeeDetails = new OFTFeeDetail[](0);\n\n // @dev This is the same as the send() operation, but without the actual send.\n // - amountSentLD is the amount in local decimals that would be sent from the sender.\n // - amountReceivedLD is the amount in local decimals that will be credited to the recipient on the remote OFT instance.\n // @dev The amountSentLD MIGHT not equal the amount the user actually receives. HOWEVER, the default does.\n (uint256 amountSentLD, uint256 amountReceivedLD) = _debitView(\n _sendParam.amountLD,\n _sendParam.minAmountLD,\n _sendParam.dstEid\n );\n oftReceipt = OFTReceipt(amountSentLD, amountReceivedLD);\n }\n\n /**\n * @notice Provides a quote for the send() operation.\n * @param _sendParam The parameters for the send() operation.\n * @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.\n * @return msgFee The calculated LayerZero messaging fee from the send() operation.\n *\n * @dev MessagingFee: LayerZero msg fee\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n */\n function quoteSend(\n SendParam calldata _sendParam,\n bool _payInLzToken\n ) external view virtual returns (MessagingFee memory msgFee) {\n // @dev mock the amount to receive, this is the same operation used in the send().\n // The quote is as similar as possible to the actual send() operation.\n (, uint256 amountReceivedLD) = _debitView(_sendParam.amountLD, _sendParam.minAmountLD, _sendParam.dstEid);\n\n // @dev Builds the options and OFT message to quote in the endpoint.\n (bytes memory message, bytes memory options) = _buildMsgAndOptions(_sendParam, amountReceivedLD);\n\n // @dev Calculates the LayerZero fee for the send() operation.\n return _quote(_sendParam.dstEid, message, options, _payInLzToken);\n }\n\n /**\n * @dev Executes the send operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The calculated fee for the send() operation.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds.\n * @return msgReceipt The receipt for the send operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) external payable virtual returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) {\n return _send(_sendParam, _fee, _refundAddress);\n }\n\n /**\n * @dev Internal function to execute the send operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The calculated fee for the send() operation.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds.\n * @return msgReceipt The receipt for the send operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function _send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) internal virtual returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) {\n // @dev Applies the token transfers regarding this send() operation.\n // - amountSentLD is the amount in local decimals that was ACTUALLY sent/debited from the sender.\n // - amountReceivedLD is the amount in local decimals that will be received/credited to the recipient on the remote OFT instance.\n (uint256 amountSentLD, uint256 amountReceivedLD) = _debit(\n msg.sender,\n _sendParam.amountLD,\n _sendParam.minAmountLD,\n _sendParam.dstEid\n );\n\n // @dev Builds the options and OFT message to quote in the endpoint.\n (bytes memory message, bytes memory options) = _buildMsgAndOptions(_sendParam, amountReceivedLD);\n\n // @dev Sends the message to the LayerZero endpoint and returns the LayerZero msg receipt.\n msgReceipt = _lzSend(_sendParam.dstEid, message, options, _fee, _refundAddress);\n // @dev Formulate the OFT receipt.\n oftReceipt = OFTReceipt(amountSentLD, amountReceivedLD);\n\n emit OFTSent(msgReceipt.guid, _sendParam.dstEid, msg.sender, amountSentLD, amountReceivedLD);\n }\n\n /**\n * @dev Internal function to build the message and options.\n * @param _sendParam The parameters for the send() operation.\n * @param _amountLD The amount in local decimals.\n * @return message The encoded message.\n * @return options The encoded options.\n */\n function _buildMsgAndOptions(\n SendParam calldata _sendParam,\n uint256 _amountLD\n ) internal view virtual returns (bytes memory message, bytes memory options) {\n bool hasCompose;\n // @dev This generated message has the msg.sender encoded into the payload so the remote knows who the caller is.\n (message, hasCompose) = OFTMsgCodec.encode(\n _sendParam.to,\n _toSD(_amountLD),\n // @dev Must be include a non empty bytes if you want to compose, EVEN if you dont need it on the remote.\n // EVEN if you dont require an arbitrary payload to be sent... eg. '0x01'\n _sendParam.composeMsg\n );\n // @dev Change the msg type depending if its composed or not.\n uint16 msgType = hasCompose ? SEND_AND_CALL : SEND;\n // @dev Combine the callers _extraOptions with the enforced options via the OAppOptionsType3.\n options = combineOptions(_sendParam.dstEid, msgType, _sendParam.extraOptions);\n\n // @dev Optionally inspect the message and options depending if the OApp owner has set a msg inspector.\n // @dev If it fails inspection, needs to revert in the implementation. ie. does not rely on return boolean\n address inspector = msgInspector; // caches the msgInspector to avoid potential double storage read\n if (inspector != address(0)) IOAppMsgInspector(inspector).inspect(message, options);\n }\n\n /**\n * @dev Internal function to handle the receive on the LayerZero endpoint.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The encoded message.\n * @dev _executor The address of the executor.\n * @dev _extraData Additional data.\n */\n function _lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address /*_executor*/, // @dev unused in the default implementation.\n bytes calldata /*_extraData*/ // @dev unused in the default implementation.\n ) internal virtual override {\n // @dev The src sending chain doesnt know the address length on this chain (potentially non-evm)\n // Thus everything is bytes32() encoded in flight.\n address toAddress = _message.sendTo().bytes32ToAddress();\n // @dev Credit the amountLD to the recipient and return the ACTUAL amount the recipient received in local decimals\n uint256 amountReceivedLD = _credit(toAddress, _toLD(_message.amountSD()), _origin.srcEid);\n\n if (_message.isComposed()) {\n // @dev Proprietary composeMsg format for the OFT.\n bytes memory composeMsg = OFTComposeMsgCodec.encode(\n _origin.nonce,\n _origin.srcEid,\n amountReceivedLD,\n _message.composeMsg()\n );\n\n // @dev Stores the lzCompose payload that will be executed in a separate tx.\n // Standardizes functionality for executing arbitrary contract invocation on some non-evm chains.\n // @dev The off-chain executor will listen and process the msg based on the src-chain-callers compose options passed.\n // @dev The index is used when a OApp needs to compose multiple msgs on lzReceive.\n // For default OFT implementation there is only 1 compose msg per lzReceive, thus its always 0.\n endpoint.sendCompose(toAddress, _guid, 0 /* the index of the composed message*/, composeMsg);\n }\n\n emit OFTReceived(_guid, _origin.srcEid, toAddress, amountReceivedLD);\n }\n\n /**\n * @dev Internal function to handle the OAppPreCrimeSimulator simulated receive.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The LayerZero message.\n * @param _executor The address of the off-chain executor.\n * @param _extraData Arbitrary data passed by the msg executor.\n *\n * @dev Enables the preCrime simulator to mock sending lzReceive() messages,\n * routes the msg down from the OAppPreCrimeSimulator, and back up to the OAppReceiver.\n */\n function _lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual override {\n _lzReceive(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Check if the peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint ID to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n *\n * @dev Enables OAppPreCrimeSimulator to check whether a potential Inbound Packet is from a trusted source.\n */\n function isPeer(uint32 _eid, bytes32 _peer) public view virtual override returns (bool) {\n return peers[_eid] == _peer;\n }\n\n /**\n * @dev Internal function to remove dust from the given local decimal amount.\n * @param _amountLD The amount in local decimals.\n * @return amountLD The amount after removing dust.\n *\n * @dev Prevents the loss of dust when moving amounts between chains with different decimals.\n * @dev eg. uint(123) with a conversion rate of 100 becomes uint(100).\n */\n function _removeDust(uint256 _amountLD) internal view virtual returns (uint256 amountLD) {\n return (_amountLD / decimalConversionRate) * decimalConversionRate;\n }\n\n /**\n * @dev Internal function to convert an amount from shared decimals into local decimals.\n * @param _amountSD The amount in shared decimals.\n * @return amountLD The amount in local decimals.\n */\n function _toLD(uint64 _amountSD) internal view virtual returns (uint256 amountLD) {\n return _amountSD * decimalConversionRate;\n }\n\n /**\n * @dev Internal function to convert an amount from local decimals into shared decimals.\n * @param _amountLD The amount in local decimals.\n * @return amountSD The amount in shared decimals.\n */\n function _toSD(uint256 _amountLD) internal view virtual returns (uint64 amountSD) {\n return uint64(_amountLD / decimalConversionRate);\n }\n\n /**\n * @dev Internal function to mock the amount mutation from a OFT debit() operation.\n * @param _amountLD The amount to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @dev _dstEid The destination endpoint ID.\n * @return amountSentLD The amount sent, in local decimals.\n * @return amountReceivedLD The amount to be received on the remote chain, in local decimals.\n *\n * @dev This is where things like fees would be calculated and deducted from the amount to be received on the remote.\n */\n function _debitView(\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 /*_dstEid*/\n ) internal view virtual returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n // @dev Remove the dust so nothing is lost on the conversion between chains with different decimals for the token.\n amountSentLD = _removeDust(_amountLD);\n // @dev The amount to send is the same as amount received in the default implementation.\n amountReceivedLD = amountSentLD;\n\n // @dev Check for slippage.\n if (amountReceivedLD < _minAmountLD) {\n revert SlippageExceeded(amountReceivedLD, _minAmountLD);\n }\n }\n\n /**\n * @dev Internal function to perform a debit operation.\n * @param _from The address to debit.\n * @param _amountLD The amount to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination endpoint ID.\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev Defined here but are intended to be overriden depending on the OFT implementation.\n * @dev Depending on OFT implementation the _amountLD could differ from the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual returns (uint256 amountSentLD, uint256 amountReceivedLD);\n\n /**\n * @dev Internal function to perform a credit operation.\n * @param _to The address to credit.\n * @param _amountLD The amount to credit in local decimals.\n * @param _srcEid The source endpoint ID.\n * @return amountReceivedLD The amount ACTUALLY received in local decimals.\n *\n * @dev Defined here but are intended to be overriden depending on the OFT implementation.\n * @dev Depending on OFT implementation the _amountLD could differ from the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 _srcEid\n ) internal virtual returns (uint256 amountReceivedLD);\n}\n" + }, + "@openzeppelin/contracts/access/AccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/AccessControl.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\nimport \"../utils/Context.sol\";\nimport \"../utils/Strings.sol\";\nimport \"../utils/introspection/ERC165.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\n * members except through off-chain means by accessing the contract event logs. Some\n * applications may benefit from on-chain enumerability, for those cases see\n * {AccessControlEnumerable}.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```\n * function foo() public {\n * require(hasRole(MY_ROLE, msg.sender));\n * ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it.\n */\nabstract contract AccessControl is Context, IAccessControl, ERC165 {\n struct RoleData {\n mapping(address => bool) members;\n bytes32 adminRole;\n }\n\n mapping(bytes32 => RoleData) private _roles;\n\n bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n /**\n * @dev Modifier that checks that an account has a specific role. Reverts\n * with a standardized message including the required role.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n *\n * _Available since v4.1._\n */\n modifier onlyRole(bytes32 role) {\n _checkRole(role, _msgSender());\n _;\n }\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) public view override returns (bool) {\n return _roles[role].members[account];\n }\n\n /**\n * @dev Revert with a standard message if `account` is missing `role`.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n */\n function _checkRole(bytes32 role, address account) internal view {\n if (!hasRole(role, account)) {\n revert(\n string(\n abi.encodePacked(\n \"AccessControl: account \",\n Strings.toHexString(uint160(account), 20),\n \" is missing role \",\n Strings.toHexString(uint256(role), 32)\n )\n )\n );\n }\n }\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) public view override returns (bytes32) {\n return _roles[role].adminRole;\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _grantRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _revokeRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been revoked `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) public virtual override {\n require(account == _msgSender(), \"AccessControl: can only renounce roles for self\");\n\n _revokeRole(role, account);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event. Note that unlike {grantRole}, this function doesn't perform any\n * checks on the calling account.\n *\n * [WARNING]\n * ====\n * This function should only be called from the constructor when setting\n * up the initial roles for the system.\n *\n * Using this function in any other way is effectively circumventing the admin\n * system imposed by {AccessControl}.\n * ====\n *\n * NOTE: This function is deprecated in favor of {_grantRole}.\n */\n function _setupRole(bytes32 role, address account) internal virtual {\n _grantRole(role, account);\n }\n\n /**\n * @dev Sets `adminRole` as ``role``'s admin role.\n *\n * Emits a {RoleAdminChanged} event.\n */\n function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n bytes32 previousAdminRole = getRoleAdmin(role);\n _roles[role].adminRole = adminRole;\n emit RoleAdminChanged(role, previousAdminRole, adminRole);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * Internal function without access restriction.\n */\n function _grantRole(bytes32 role, address account) internal virtual {\n if (!hasRole(role, account)) {\n _roles[role].members[account] = true;\n emit RoleGranted(role, account, _msgSender());\n }\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * Internal function without access restriction.\n */\n function _revokeRole(bytes32 role, address account) internal virtual {\n if (hasRole(role, account)) {\n _roles[role].members[account] = false;\n emit RoleRevoked(role, account, _msgSender());\n }\n }\n}\n" + }, + "@openzeppelin/contracts/access/AccessControlEnumerable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/AccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControlEnumerable.sol\";\nimport \"./AccessControl.sol\";\nimport \"../utils/structs/EnumerableSet.sol\";\n\n/**\n * @dev Extension of {AccessControl} that allows enumerating the members of each role.\n */\nabstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {\n using EnumerableSet for EnumerableSet.AddressSet;\n\n mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) public view override returns (address) {\n return _roleMembers[role].at(index);\n }\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) public view override returns (uint256) {\n return _roleMembers[role].length();\n }\n\n /**\n * @dev Overload {_grantRole} to track enumerable memberships\n */\n function _grantRole(bytes32 role, address account) internal virtual override {\n super._grantRole(role, account);\n _roleMembers[role].add(account);\n }\n\n /**\n * @dev Overload {_revokeRole} to track enumerable memberships\n */\n function _revokeRole(bytes32 role, address account) internal virtual override {\n super._revokeRole(role, account);\n _roleMembers[role].remove(account);\n }\n}\n" + }, + "@openzeppelin/contracts/access/IAccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n /**\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n *\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n * {RoleAdminChanged} not being emitted signaling this.\n *\n * _Available since v3.1._\n */\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n /**\n * @dev Emitted when `account` is granted `role`.\n *\n * `sender` is the account that originated the contract call, an admin role\n * bearer except when using {AccessControl-_setupRole}.\n */\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Emitted when `account` is revoked `role`.\n *\n * `sender` is the account that originated the contract call:\n * - if using `revokeRole`, it is the admin role bearer\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\n */\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) external view returns (bool);\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been granted `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) external;\n}\n" + }, + "@openzeppelin/contracts/access/IAccessControlEnumerable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\n\n/**\n * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.\n */\ninterface IAccessControlEnumerable is IAccessControl {\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) external view returns (address);\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) external view returns (uint256);\n}\n" + }, + "@openzeppelin/contracts/access/Ownable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n" + }, + "@openzeppelin/contracts/security/Pausable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/ERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/ERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"./extensions/IERC20Metadata.sol\";\nimport \"../../utils/Context.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20, IERC20Metadata {\n mapping(address => uint256) private _balances;\n\n mapping(address => mapping(address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n\n /**\n * @dev Sets the values for {name} and {symbol}.\n *\n * The default value of {decimals} is 18. To select a different value for\n * {decimals} you should overload it.\n *\n * All two of these values are immutable: they can only be set once during\n * construction.\n */\n constructor(string memory name_, string memory symbol_) {\n _name = name_;\n _symbol = symbol_;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless this function is\n * overridden;\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `recipient` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n _approve(_msgSender(), spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * Requirements:\n *\n * - `sender` and `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n * - the caller must have allowance for ``sender``'s tokens of at least\n * `amount`.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public virtual override returns (bool) {\n _transfer(sender, recipient, amount);\n\n uint256 currentAllowance = _allowances[sender][_msgSender()];\n require(currentAllowance >= amount, \"ERC20: transfer amount exceeds allowance\");\n unchecked {\n _approve(sender, _msgSender(), currentAllowance - amount);\n }\n\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n uint256 currentAllowance = _allowances[_msgSender()][spender];\n require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n unchecked {\n _approve(_msgSender(), spender, currentAllowance - subtractedValue);\n }\n\n return true;\n }\n\n /**\n * @dev Moves `amount` of tokens from `sender` to `recipient`.\n *\n * This internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `sender` cannot be the zero address.\n * - `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n */\n function _transfer(\n address sender,\n address recipient,\n uint256 amount\n ) internal virtual {\n require(sender != address(0), \"ERC20: transfer from the zero address\");\n require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(sender, recipient, amount);\n\n uint256 senderBalance = _balances[sender];\n require(senderBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n unchecked {\n _balances[sender] = senderBalance - amount;\n }\n _balances[recipient] += amount;\n\n emit Transfer(sender, recipient, amount);\n\n _afterTokenTransfer(sender, recipient, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply += amount;\n _balances[account] += amount;\n emit Transfer(address(0), account, amount);\n\n _afterTokenTransfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n uint256 accountBalance = _balances[account];\n require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n unchecked {\n _balances[account] = accountBalance - amount;\n }\n _totalSupply -= amount;\n\n emit Transfer(account, address(0), amount);\n\n _afterTokenTransfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n\n /**\n * @dev Hook that is called after any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * has been transferred to `to`.\n * - when `from` is zero, `amount` tokens have been minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _afterTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC721 token receiver interface\n * @dev Interface for any contract that wants to support safeTransfers\n * from ERC721 asset contracts.\n */\ninterface IERC721Receiver {\n /**\n * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}\n * by `operator` from `from`, this function is called.\n *\n * It must return its Solidity selector to confirm the token transfer.\n * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.\n *\n * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.\n */\n function onERC721Received(\n address operator,\n address from,\n uint256 tokenId,\n bytes calldata data\n ) external returns (bytes4);\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/ERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC165.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n *\n * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.\n */\nabstract contract ERC165 is IERC165 {\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IERC165).interfaceId;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/IERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeMath.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)\n\npragma solidity ^0.8.0;\n\n// CAUTION\n// This version of SafeMath should only be used with Solidity 0.8 or later,\n// because it relies on the compiler's built in overflow checks.\n\n/**\n * @dev Wrappers over Solidity's arithmetic operations.\n *\n * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler\n * now has built in overflow checking.\n */\nlibrary SafeMath {\n /**\n * @dev Returns the addition of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n uint256 c = a + b;\n if (c < a) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the substraction of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b > a) return (false, 0);\n return (true, a - b);\n }\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n // benefit is lost if 'b' is also tested.\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n if (a == 0) return (true, 0);\n uint256 c = a * b;\n if (c / a != b) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the division of two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a / b);\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a % b);\n }\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `+` operator.\n *\n * Requirements:\n *\n * - Addition cannot overflow.\n */\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\n return a + b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting on\n * overflow (when the result is negative).\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\n return a - b;\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `*` operator.\n *\n * Requirements:\n *\n * - Multiplication cannot overflow.\n */\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\n return a * b;\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator.\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\n return a / b;\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting when dividing by zero.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\n return a % b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\n * overflow (when the result is negative).\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {trySub}.\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b <= a, errorMessage);\n return a - b;\n }\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting with custom message on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator. Note: this function uses a\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\n * uses an invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a / b;\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting with custom message when dividing by zero.\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {tryMod}.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a % b;\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Strings.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n bytes16 private constant _HEX_SYMBOLS = \"0123456789abcdef\";\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n // Inspired by OraclizeAPI's implementation - MIT licence\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\n\n if (value == 0) {\n return \"0\";\n }\n uint256 temp = value;\n uint256 digits;\n while (temp != 0) {\n digits++;\n temp /= 10;\n }\n bytes memory buffer = new bytes(digits);\n while (value != 0) {\n digits -= 1;\n buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\n value /= 10;\n }\n return string(buffer);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n if (value == 0) {\n return \"0x00\";\n }\n uint256 temp = value;\n uint256 length = 0;\n while (temp != 0) {\n length++;\n temp >>= 8;\n }\n return toHexString(value, length);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _HEX_SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/structs/EnumerableSet.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 => uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n" + }, + "contracts/automation/AbstractCCIPBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IRouterClient } from \"@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol\";\nimport { Client } from \"@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol\";\n\nabstract contract AbstractCCIPBridgeHelperModule is AbstractSafeModule {\n /**\n * @notice Bridges a token from the source chain to the destination chain using CCIP\n * @param ccipRouter The CCIP router contract\n * @param destinationChainSelector The selector for the destination chain\n * @param token The token to bridge\n * @param amount The amount of token to bridge\n */\n function _bridgeTokenWithCCIP(\n IRouterClient ccipRouter,\n uint64 destinationChainSelector,\n IERC20 token,\n uint256 amount\n ) internal {\n bool success;\n\n // Approve CCIP Router to move the token\n success = safeContract.execTransactionFromModule(\n address(token),\n 0, // Value\n abi.encodeWithSelector(token.approve.selector, ccipRouter, amount),\n 0 // Call\n );\n require(success, \"Failed to approve token\");\n\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(token),\n amount: amount\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory ccipMessage = Client.EVM2AnyMessage({\n receiver: abi.encode(address(safeContract)), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n // Get CCIP fee\n uint256 ccipFee = ccipRouter.getFee(\n destinationChainSelector,\n ccipMessage\n );\n\n // Send CCIP message\n success = safeContract.execTransactionFromModule(\n address(ccipRouter),\n ccipFee, // Value\n abi.encodeWithSelector(\n ccipRouter.ccipSend.selector,\n destinationChainSelector,\n ccipMessage\n ),\n 0 // Call\n );\n require(success, \"Failed to send CCIP message\");\n }\n}\n" + }, + "contracts/automation/AbstractLZBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\n\nimport { IOFT, SendParam } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\nimport { MessagingFee } from \"@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol\";\nimport { OptionsBuilder } from \"@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nabstract contract AbstractLZBridgeHelperModule is AbstractSafeModule {\n using OptionsBuilder for bytes;\n\n /**\n * @dev Bridges token using LayerZero.\n * @param lzEndpointId LayerZero endpoint id.\n * @param token Token to bridge.\n * @param lzAdapter LZ Adapter to use.\n * @param amount Amount of token to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n * @param isNativeToken Whether the token is native token.\n */\n function _bridgeTokenWithLz(\n uint32 lzEndpointId,\n IERC20 token,\n IOFT lzAdapter,\n uint256 amount,\n uint256 slippageBps,\n bool isNativeToken\n ) internal {\n bool success;\n\n if (!isNativeToken) {\n // Approve LZ Adapter to move the token\n success = safeContract.execTransactionFromModule(\n address(token),\n 0, // Value\n abi.encodeWithSelector(\n token.approve.selector,\n address(lzAdapter),\n amount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve token\");\n }\n\n // Calculate minimum amount to receive\n uint256 minAmount = (amount * (10000 - slippageBps)) / 10000;\n\n // Hardcoded gaslimit of 400k\n bytes memory options = OptionsBuilder\n .newOptions()\n .addExecutorLzReceiveOption(400000, 0);\n\n // Build send param\n SendParam memory sendParam = SendParam({\n dstEid: lzEndpointId,\n to: bytes32(uint256(uint160(address(safeContract)))),\n amountLD: amount,\n minAmountLD: minAmount,\n extraOptions: options,\n composeMsg: bytes(\"\"),\n oftCmd: bytes(\"\")\n });\n\n // Compute fees\n MessagingFee memory msgFee = lzAdapter.quoteSend(sendParam, false);\n\n uint256 value = isNativeToken\n ? amount + msgFee.nativeFee\n : msgFee.nativeFee;\n\n // Execute transaction\n success = safeContract.execTransactionFromModule(\n address(lzAdapter),\n value,\n abi.encodeWithSelector(\n lzAdapter.send.selector,\n sendParam,\n msgFee,\n address(safeContract)\n ),\n 0\n );\n require(success, \"Failed to bridge token\");\n }\n}\n" + }, + "contracts/automation/AbstractSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { ISafe } from \"../interfaces/ISafe.sol\";\n\nabstract contract AbstractSafeModule is AccessControlEnumerable {\n ISafe public immutable safeContract;\n\n bytes32 public constant OPERATOR_ROLE = keccak256(\"OPERATOR_ROLE\");\n\n modifier onlySafe() {\n require(\n msg.sender == address(safeContract),\n \"Caller is not the safe contract\"\n );\n _;\n }\n\n modifier onlyOperator() {\n require(\n hasRole(OPERATOR_ROLE, msg.sender),\n \"Caller is not an operator\"\n );\n _;\n }\n\n constructor(address _safeContract) {\n safeContract = ISafe(_safeContract);\n _grantRole(DEFAULT_ADMIN_ROLE, address(safeContract));\n _grantRole(OPERATOR_ROLE, address(safeContract));\n }\n\n /**\n * @dev Helps recovering any tokens accidentally sent to this module.\n * @param token Token to transfer. 0x0 to transfer Native token.\n * @param amount Amount to transfer. 0 to transfer all balance.\n */\n function transferTokens(address token, uint256 amount) external onlySafe {\n if (address(token) == address(0)) {\n // Move ETH\n amount = amount > 0 ? amount : address(this).balance;\n payable(address(safeContract)).transfer(amount);\n return;\n }\n\n // Move all balance if amount set to 0\n amount = amount > 0 ? amount : IERC20(token).balanceOf(address(this));\n\n // Transfer to Safe contract\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(token).transfer(address(safeContract), amount);\n }\n\n receive() external payable {\n // Accept ETH to pay for bridge fees\n }\n}\n" + }, + "contracts/automation/BaseBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// solhint-disable-next-line max-line-length\nimport { AbstractCCIPBridgeHelperModule, AbstractSafeModule, IRouterClient } from \"./AbstractCCIPBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { BridgedWOETHStrategy } from \"../strategies/BridgedWOETHStrategy.sol\";\n\ncontract BaseBridgeHelperModule is\n AccessControlEnumerable,\n AbstractCCIPBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0x98a0CbeF61bD2D21435f433bE4CD42B56B38CC93);\n IWETH9 public constant weth =\n IWETH9(0x4200000000000000000000000000000000000006);\n IERC20 public constant oethb =\n IERC20(0xDBFeFD2e8460a6Ee4955A68582F85708BAEA60A3);\n IERC4626 public constant bridgedWOETH =\n IERC4626(0xD8724322f44E5c58D7A815F542036fb17DbbF839);\n\n BridgedWOETHStrategy public constant bridgedWOETHStrategy =\n BridgedWOETHStrategy(0x80c864704DD06C3693ed5179190786EE38ACf835);\n\n IRouterClient public constant CCIP_ROUTER =\n IRouterClient(0x881e3A65B4d4a04dD529061dd0071cf975F58bCD);\n\n uint64 public constant CCIP_ETHEREUM_CHAIN_SELECTOR = 5009297550715157269;\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Ethereum.\n * @param woethAmount Amount of wOETH to bridge.\n */\n function bridgeWOETHToEthereum(uint256 woethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_ETHEREUM_CHAIN_SELECTOR,\n IERC20(address(bridgedWOETH)),\n woethAmount\n );\n }\n\n /**\n * @dev Bridges WETH to Ethereum.\n * @param wethAmount Amount of WETH to bridge.\n */\n function bridgeWETHToEthereum(uint256 wethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_ETHEREUM_CHAIN_SELECTOR,\n IERC20(address(weth)),\n wethAmount\n );\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem the wOETH for WETH using the Vault.\n * @return Amount of WETH received.\n */\n function depositWOETH(uint256 woethAmount, bool redeemWithVault)\n external\n onlyOperator\n returns (uint256)\n {\n return _depositWOETH(woethAmount, redeemWithVault);\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy and bridges it to Ethereum.\n * @param woethAmount Amount of wOETH to deposit.\n * @return Amount of WETH received.\n */\n function depositWOETHAndBridgeWETH(uint256 woethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n uint256 wethAmount = _depositWOETH(woethAmount, true);\n bridgeWETHToEthereum(wethAmount);\n return wethAmount;\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem the wOETH for WETH using the Vault.\n * @return Amount of WETH received.\n */\n function _depositWOETH(uint256 woethAmount, bool redeemWithVault)\n internal\n returns (uint256)\n {\n // Update oracle price\n bridgedWOETHStrategy.updateWOETHOraclePrice();\n\n // Rebase to account for any yields from price update\n vault.rebase();\n\n uint256 oethbAmount = oethb.balanceOf(address(safeContract));\n\n // Approve bridgedWOETH strategy to move wOETH\n bool success = safeContract.execTransactionFromModule(\n address(bridgedWOETH),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETH.approve.selector,\n address(bridgedWOETHStrategy),\n woethAmount\n ),\n 0 // Call\n );\n\n // Deposit to bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.depositBridgedWOETH.selector,\n woethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to deposit bridged WOETH\");\n\n oethbAmount = oethb.balanceOf(address(safeContract)) - oethbAmount;\n\n // Rebase to account for any yields from price update\n // and backing asset change from deposit\n vault.rebase();\n\n if (!redeemWithVault) {\n return oethbAmount;\n }\n\n // Redeem for WETH using Vault\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethbAmount,\n oethbAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETHb\");\n\n return oethbAmount;\n }\n\n /**\n * @dev Deposits WETH into the Vault and redeems wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to deposit.\n * @return Amount of wOETH received.\n */\n function depositWETHAndRedeemWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _withdrawWOETH(wethAmount);\n }\n\n function depositWETHAndBridgeWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n uint256 woethAmount = _withdrawWOETH(wethAmount);\n bridgeWOETHToEthereum(woethAmount);\n return woethAmount;\n }\n\n /**\n * @dev Withdraws wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to use to withdraw.\n * @return Amount of wOETH received.\n */\n function _withdrawWOETH(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETHb with WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETHb\");\n\n // Approve bridgedWOETH strategy to move OETHb\n success = safeContract.execTransactionFromModule(\n address(oethb),\n 0, // Value\n abi.encodeWithSelector(\n oethb.approve.selector,\n address(bridgedWOETHStrategy),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETHb\");\n\n uint256 woethAmount = bridgedWOETH.balanceOf(address(safeContract));\n\n // Withdraw from bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.withdrawBridgedWOETH.selector,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to withdraw bridged WOETH\");\n\n woethAmount =\n bridgedWOETH.balanceOf(address(safeContract)) -\n woethAmount;\n\n return woethAmount;\n }\n}\n" + }, + "contracts/automation/ClaimBribesSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { ICLGauge } from \"../interfaces/aerodrome/ICLGauge.sol\";\nimport { ICLPool } from \"../interfaces/aerodrome/ICLPool.sol\";\n\nstruct BribePoolInfo {\n address poolAddress;\n address rewardContractAddress;\n address[] rewardTokens;\n}\n\ninterface IAerodromeVoter {\n function claimBribes(\n address[] memory _bribes,\n address[][] memory _tokens,\n uint256 _tokenId\n ) external;\n}\n\ninterface IVeNFT {\n function ownerOf(uint256 tokenId) external view returns (address);\n\n function ownerToNFTokenIdList(address owner, uint256 index)\n external\n view\n returns (uint256);\n}\n\ninterface ICLRewardContract {\n function rewards(uint256 index) external view returns (address);\n\n function rewardsListLength() external view returns (uint256);\n}\n\ncontract ClaimBribesSafeModule is AbstractSafeModule {\n IAerodromeVoter public immutable voter;\n address public immutable veNFT;\n\n uint256[] nftIds;\n mapping(uint256 => uint256) nftIdIndex;\n\n BribePoolInfo[] bribePools;\n mapping(address => uint256) bribePoolIndex;\n\n event NFTIdAdded(uint256 nftId);\n event NFTIdRemoved(uint256 nftId);\n\n event BribePoolAdded(address bribePool);\n event BribePoolRemoved(address bribePool);\n\n constructor(\n address _safeContract,\n address _voter,\n address _veNFT\n ) AbstractSafeModule(_safeContract) {\n voter = IAerodromeVoter(_voter);\n veNFT = _veNFT;\n }\n\n /**\n * @dev Claim bribes for a range of NFTs\n * @param nftIndexStart The start index of the NFTs\n * @param nftIndexEnd The end index of the NFTs\n * @param silent Doesn't revert if the claim fails when true\n */\n function claimBribes(\n uint256 nftIndexStart,\n uint256 nftIndexEnd,\n bool silent\n ) external onlyOperator {\n if (nftIndexEnd < nftIndexStart) {\n (nftIndexStart, nftIndexEnd) = (nftIndexEnd, nftIndexStart);\n }\n uint256 nftCount = nftIds.length;\n nftIndexEnd = nftCount < nftIndexEnd ? nftCount : nftIndexEnd;\n\n (\n address[] memory rewardContractAddresses,\n address[][] memory rewardTokens\n ) = _getRewardsInfoArray();\n\n for (uint256 i = nftIndexStart; i < nftIndexEnd; i++) {\n uint256 nftId = nftIds[i];\n bool success = safeContract.execTransactionFromModule(\n address(voter),\n 0, // Value\n abi.encodeWithSelector(\n IAerodromeVoter.claimBribes.selector,\n rewardContractAddresses,\n rewardTokens,\n nftId\n ),\n 0 // Call\n );\n\n require(success || silent, \"ClaimBribes failed\");\n }\n }\n\n /**\n * @dev Get the reward contract address and reward tokens for all pools\n * @return rewardContractAddresses The reward contract addresses\n * @return rewardTokens The reward tokens\n */\n function _getRewardsInfoArray()\n internal\n view\n returns (\n address[] memory rewardContractAddresses,\n address[][] memory rewardTokens\n )\n {\n BribePoolInfo[] memory _bribePools = bribePools;\n uint256 bribePoolCount = _bribePools.length;\n rewardContractAddresses = new address[](bribePoolCount);\n rewardTokens = new address[][](bribePoolCount);\n\n for (uint256 i = 0; i < bribePoolCount; i++) {\n rewardContractAddresses[i] = _bribePools[i].rewardContractAddress;\n rewardTokens[i] = _bribePools[i].rewardTokens;\n }\n }\n\n /***************************************\n NFT Management\n ****************************************/\n /**\n * @dev Add NFT IDs to the list\n * @param _nftIds The NFT IDs to add\n */\n function addNFTIds(uint256[] memory _nftIds) external onlyOperator {\n for (uint256 i = 0; i < _nftIds.length; i++) {\n uint256 nftId = _nftIds[i];\n if (nftIdExists(nftId)) {\n // If it already exists, skip\n continue;\n }\n\n // Make sure the NFT is owned by the Safe\n require(\n IVeNFT(veNFT).ownerOf(nftId) == address(safeContract),\n \"NFT not owned by safe\"\n );\n\n nftIdIndex[nftId] = nftIds.length;\n nftIds.push(nftId);\n\n emit NFTIdAdded(nftId);\n }\n }\n\n /**\n * @dev Remove NFT IDs from the list\n * @param _nftIds The NFT IDs to remove\n */\n function removeNFTIds(uint256[] memory _nftIds) external onlyOperator {\n for (uint256 i = 0; i < _nftIds.length; i++) {\n uint256 nftId = _nftIds[i];\n if (!nftIdExists(nftId)) {\n // If it doesn't exist, skip\n continue;\n }\n\n uint256 index = nftIdIndex[nftId];\n uint256 lastNftId = nftIds[nftIds.length - 1];\n nftIds[index] = lastNftId;\n nftIdIndex[lastNftId] = index;\n nftIds.pop();\n\n emit NFTIdRemoved(nftId);\n }\n }\n\n /**\n * @dev Check if a NFT exists on the list\n * @param nftId The NFT ID to check\n * @return true if the NFT ID exists, false otherwise\n */\n function nftIdExists(uint256 nftId) public view returns (bool) {\n uint256 index = nftIdIndex[nftId];\n uint256[] memory _nftIds = nftIds;\n return (index < _nftIds.length) && _nftIds[index] == nftId;\n }\n\n /**\n * @dev Get the length of the nftIds list\n * @return The length of the nftIds list\n */\n function getNFTIdsLength() external view returns (uint256) {\n return nftIds.length;\n }\n\n /**\n * @dev Get all NFT IDs\n * @return The NFT IDs\n */\n function getAllNFTIds() external view returns (uint256[] memory) {\n return nftIds;\n }\n\n /**\n * @dev Fetch all NFT IDs from the veNFT contract\n * @notice This can revert if Safe owns too many NFTs since tx will be huge.\n * This function is public, anyone can call it, since it only fetches\n * the NFT IDs owned by the Safe. It shouldn't cause us any issues.\n */\n function fetchNFTIds() external {\n // Purge the array\n delete nftIds;\n\n uint256 i = 0;\n while (true) {\n uint256 nftId = IVeNFT(veNFT).ownerToNFTokenIdList(\n address(safeContract),\n i\n );\n if (nftId == 0) {\n break;\n }\n\n nftIdIndex[nftId] = nftIds.length;\n nftIds.push(nftId);\n i++;\n }\n }\n\n /**\n * @dev Remove all NFT IDs from the list\n */\n function removeAllNFTIds() external onlyOperator {\n uint256 length = nftIds.length;\n for (uint256 i = 0; i < length; i++) {\n uint256 nftId = nftIds[i];\n delete nftIdIndex[nftId];\n emit NFTIdRemoved(nftId);\n }\n\n delete nftIds;\n }\n\n /***************************************\n Bribe Pool Management\n ****************************************/\n // @dev Whitelist a pool to claim bribes from\n // @param _poolAddress The address of the pool to whitelist\n function addBribePool(address _poolAddress, bool _isVotingContract)\n external\n onlySafe\n {\n BribePoolInfo memory bribePool;\n\n if (_isVotingContract) {\n bribePool = BribePoolInfo({\n poolAddress: _poolAddress,\n rewardContractAddress: _poolAddress,\n rewardTokens: _getRewardTokenAddresses(_poolAddress)\n });\n } else {\n // Find the gauge address\n address _gaugeAddress = ICLPool(_poolAddress).gauge();\n // And the reward contract address\n address _rewardContractAddress = ICLGauge(_gaugeAddress)\n .feesVotingReward();\n\n bribePool = BribePoolInfo({\n poolAddress: _poolAddress,\n rewardContractAddress: _rewardContractAddress,\n rewardTokens: _getRewardTokenAddresses(_rewardContractAddress)\n });\n }\n\n if (bribePoolExists(_poolAddress)) {\n // Update if it already exists\n bribePools[bribePoolIndex[_poolAddress]] = bribePool;\n } else {\n // If not, Append to the list\n bribePoolIndex[_poolAddress] = bribePools.length;\n bribePools.push(bribePool);\n }\n\n emit BribePoolAdded(_poolAddress);\n }\n\n /**\n * @dev Update the reward token addresses for all pools\n */\n function updateRewardTokenAddresses() external onlyOperator {\n BribePoolInfo[] storage _bribePools = bribePools;\n for (uint256 i = 0; i < _bribePools.length; i++) {\n BribePoolInfo storage bribePool = _bribePools[i];\n bribePool.rewardTokens = _getRewardTokenAddresses(\n bribePool.rewardContractAddress == bribePool.poolAddress\n ? bribePool.poolAddress\n : bribePool.rewardContractAddress\n );\n }\n }\n\n /**\n * @dev Get the reward token addresses for a given reward contract address\n * @param _rewardContractAddress The address of the reward contract\n * @return _rewardTokens The reward token addresses\n */\n function _getRewardTokenAddresses(address _rewardContractAddress)\n internal\n view\n returns (address[] memory)\n {\n address[] memory _rewardTokens = new address[](\n ICLRewardContract(_rewardContractAddress).rewardsListLength()\n );\n for (uint256 i = 0; i < _rewardTokens.length; i++) {\n _rewardTokens[i] = ICLRewardContract(_rewardContractAddress)\n .rewards(i);\n }\n\n return _rewardTokens;\n }\n\n /**\n * @dev Remove a bribe pool from the list\n * @param _poolAddress The address of the pool to remove\n */\n function removeBribePool(address _poolAddress) external onlySafe {\n if (!bribePoolExists(_poolAddress)) {\n // If it doesn't exist, skip\n return;\n }\n\n uint256 index = bribePoolIndex[_poolAddress];\n BribePoolInfo memory lastBribePool = bribePools[bribePools.length - 1];\n bribePools[index] = lastBribePool;\n bribePoolIndex[lastBribePool.poolAddress] = index;\n bribePools.pop();\n\n emit BribePoolRemoved(_poolAddress);\n }\n\n /**\n * @dev Check if a bribe pool exists\n * @param bribePool The address of the pool to check\n * @return true if the pool exists, false otherwise\n */\n function bribePoolExists(address bribePool) public view returns (bool) {\n BribePoolInfo[] memory _bribePools = bribePools;\n uint256 poolIndex = bribePoolIndex[bribePool];\n return\n poolIndex < _bribePools.length &&\n _bribePools[poolIndex].poolAddress == bribePool;\n }\n\n /**\n * @dev Get the length of the bribe pools list\n * @return The length of the bribe pools list\n */\n function getBribePoolsLength() external view returns (uint256) {\n return bribePools.length;\n }\n}\n" + }, + "contracts/automation/ClaimStrategyRewardsSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\n\nimport { ISafe } from \"../interfaces/ISafe.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract ClaimStrategyRewardsSafeModule is AbstractSafeModule {\n using SafeERC20 for IERC20;\n\n mapping(address => bool) public isStrategyWhitelisted;\n address[] public strategies;\n\n event StrategyAdded(address strategy);\n event StrategyRemoved(address strategy);\n\n event ClaimRewardsFailed(address strategy);\n\n constructor(\n address _safeAddress,\n address operator,\n address[] memory _strategies\n ) AbstractSafeModule(_safeAddress) {\n _grantRole(OPERATOR_ROLE, operator);\n\n // Whitelist all strategies\n for (uint256 i = 0; i < _strategies.length; i++) {\n _addStrategy(_strategies[i]);\n }\n }\n\n /**\n * @dev Claim rewards from all whitelisted strategies\n * @param silent Doesn't revert on error if set to true\n */\n function claimRewards(bool silent) external onlyRole(OPERATOR_ROLE) {\n uint256 strategiesLength = strategies.length;\n for (uint256 i = 0; i < strategiesLength; i++) {\n address strategy = strategies[i];\n\n // Execute `collectRewardTokens` for all strategies\n bool success = safeContract.execTransactionFromModule(\n strategy, // To\n 0, // Value\n abi.encodeWithSelector(IStrategy.collectRewardTokens.selector),\n 0 // Call\n );\n\n if (!success) {\n emit ClaimRewardsFailed(strategy);\n }\n\n require(success || silent, \"Failed to claim rewards\");\n }\n }\n\n /**\n * @dev Add a strategy to the whitelist\n * @param _strategy The address of the strategy to add\n */\n function addStrategy(address _strategy)\n external\n onlyRole(DEFAULT_ADMIN_ROLE)\n {\n _addStrategy(_strategy);\n }\n\n function _addStrategy(address _strategy) internal {\n require(\n !isStrategyWhitelisted[_strategy],\n \"Strategy already whitelisted\"\n );\n isStrategyWhitelisted[_strategy] = true;\n strategies.push(_strategy);\n emit StrategyAdded(_strategy);\n }\n\n /**\n * @dev Remove a strategy from the whitelist\n * @param _strategy The address of the strategy to remove\n */\n function removeStrategy(address _strategy)\n external\n onlyRole(DEFAULT_ADMIN_ROLE)\n {\n require(isStrategyWhitelisted[_strategy], \"Strategy not whitelisted\");\n isStrategyWhitelisted[_strategy] = false;\n\n for (uint256 i = 0; i < strategies.length; i++) {\n if (strategies[i] == _strategy) {\n strategies[i] = strategies[strategies.length - 1];\n strategies.pop();\n break;\n }\n }\n\n emit StrategyRemoved(_strategy);\n }\n}\n" + }, + "contracts/automation/CollectXOGNRewardsModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IXOGN {\n function collectRewards() external;\n}\n\ncontract CollectXOGNRewardsModule is AbstractSafeModule {\n IXOGN public constant xogn =\n IXOGN(0x63898b3b6Ef3d39332082178656E9862bee45C57);\n address public constant rewardsSource =\n 0x7609c88E5880e934dd3A75bCFef44E31b1Badb8b;\n IERC20 public constant ogn =\n IERC20(0x8207c1FfC5B6804F6024322CcF34F29c3541Ae26);\n\n constructor(address _safeContract, address operator)\n AbstractSafeModule(_safeContract)\n {\n _grantRole(OPERATOR_ROLE, operator);\n }\n\n function collectRewards() external onlyOperator {\n uint256 balance = ogn.balanceOf(address(safeContract));\n\n bool success = safeContract.execTransactionFromModule(\n address(xogn),\n 0, // Value\n abi.encodeWithSelector(IXOGN.collectRewards.selector),\n 0 // Call\n );\n\n require(success, \"Failed to collect rewards\");\n\n balance = ogn.balanceOf(address(safeContract)) - balance;\n\n if (balance == 0) {\n return;\n }\n\n success = safeContract.execTransactionFromModule(\n address(ogn),\n 0, // Value\n abi.encodeWithSelector(\n IERC20.transfer.selector,\n rewardsSource,\n balance\n ),\n 0 // Call\n );\n\n require(success, \"Failed to collect rewards\");\n }\n}\n" + }, + "contracts/automation/CurvePoolBoosterBribesModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\n\ninterface ICurvePoolBooster {\n function manageTotalRewardAmount(\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external;\n\n function manageNumberOfPeriods(\n uint8 extraNumberOfPeriods,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external;\n}\n\ncontract CurvePoolBoosterBribesModule is AbstractSafeModule {\n address[] public POOLS;\n\n event PoolBoosterAddressAdded(address pool);\n event PoolBoosterAddressRemoved(address pool);\n\n constructor(\n address _safeContract,\n address _operator,\n address[] memory _pools\n ) AbstractSafeModule(_safeContract) {\n _grantRole(OPERATOR_ROLE, _operator);\n _addPoolBoosterAddress(_pools);\n }\n\n function addPoolBoosterAddress(address[] memory pools)\n external\n onlyOperator\n {\n _addPoolBoosterAddress(pools);\n }\n\n function _addPoolBoosterAddress(address[] memory pools) internal {\n for (uint256 i = 0; i < pools.length; i++) {\n POOLS.push(pools[i]);\n emit PoolBoosterAddressAdded(pools[i]);\n }\n }\n\n function removePoolBoosterAddress(address[] calldata pools)\n external\n onlyOperator\n {\n for (uint256 i = 0; i < pools.length; i++) {\n _removePoolBoosterAddress(pools[i]);\n }\n }\n\n function _removePoolBoosterAddress(address pool) internal {\n uint256 length = POOLS.length;\n for (uint256 i = 0; i < length; i++) {\n if (POOLS[i] == pool) {\n POOLS[i] = POOLS[length - 1];\n POOLS.pop();\n emit PoolBoosterAddressRemoved(pool);\n }\n }\n }\n\n function manageBribes() external onlyOperator {\n uint256 length = POOLS.length;\n for (uint256 i = 0; i < length; i++) {\n address poolBoosterAddress = POOLS[i];\n\n // PoolBooster need to have a balance of at least 0.002 ether to operate\n // 0.001 ether are used for the bridge fee\n require(\n poolBoosterAddress.balance > 0.002 ether,\n \"Insufficient balance for bribes\"\n );\n\n require(\n safeContract.execTransactionFromModule(\n poolBoosterAddress,\n 0, // Value\n abi.encodeWithSelector(\n ICurvePoolBooster.manageNumberOfPeriods.selector,\n 1, // extraNumberOfPeriods\n 0.001 ether, // bridgeFee\n 1000000 // additionalGasLimit\n ),\n 0\n ),\n \"Manage number of periods failed\"\n );\n\n require(\n safeContract.execTransactionFromModule(\n poolBoosterAddress,\n 0, // Value\n abi.encodeWithSelector(\n ICurvePoolBooster.manageTotalRewardAmount.selector,\n 0.001 ether, // bridgeFee\n 1000000 // additionalGasLimit\n ),\n 0\n ),\n \"Manage total reward failed\"\n );\n }\n }\n}\n" + }, + "contracts/automation/EthereumBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractLZBridgeHelperModule, AbstractSafeModule } from \"./AbstractLZBridgeHelperModule.sol\";\nimport { AbstractCCIPBridgeHelperModule, IRouterClient } from \"./AbstractCCIPBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IOFT } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract EthereumBridgeHelperModule is\n AccessControlEnumerable,\n AbstractLZBridgeHelperModule,\n AbstractCCIPBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0x39254033945AA2E4809Cc2977E7087BEE48bd7Ab);\n IWETH9 public constant weth =\n IWETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);\n IERC20 public constant oeth =\n IERC20(0x856c4Efb76C1D1AE02e20CEB03A2A6a08b0b8dC3);\n IERC4626 public constant woeth =\n IERC4626(0xDcEe70654261AF21C44c093C300eD3Bb97b78192);\n\n uint32 public constant LZ_PLUME_ENDPOINT_ID = 30370;\n IOFT public constant LZ_WOETH_OMNICHAIN_ADAPTER =\n IOFT(0x7d1bEa5807e6af125826d56ff477745BB89972b8);\n IOFT public constant LZ_ETH_OMNICHAIN_ADAPTER =\n IOFT(0x77b2043768d28E9C9aB44E1aBfC95944bcE57931);\n\n IRouterClient public constant CCIP_ROUTER =\n IRouterClient(0x80226fc0Ee2b096224EeAc085Bb9a8cba1146f7D);\n\n uint64 public constant CCIP_BASE_CHAIN_SELECTOR = 15971525489660198786;\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Plume.\n * @param woethAmount Amount of wOETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWOETHToPlume(uint256 woethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_PLUME_ENDPOINT_ID,\n woeth,\n LZ_WOETH_OMNICHAIN_ADAPTER,\n woethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Bridges wOETH to Base using CCIP.\n * @param woethAmount Amount of wOETH to bridge.\n */\n function bridgeWOETHToBase(uint256 woethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_BASE_CHAIN_SELECTOR,\n woeth,\n woethAmount\n );\n }\n\n /**\n * @dev Bridges wETH to Plume.\n * @param wethAmount Amount of wETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWETHToPlume(uint256 wethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n // Unwrap into ETH\n safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(weth.withdraw.selector, wethAmount),\n 0 // Call\n );\n\n _bridgeTokenWithLz(\n LZ_PLUME_ENDPOINT_ID,\n IERC20(address(weth)),\n LZ_ETH_OMNICHAIN_ADAPTER,\n wethAmount,\n slippageBps,\n true\n );\n }\n\n /**\n * @dev Bridges wETH to Base using CCIP.\n * @param wethAmount Amount of wETH to bridge.\n */\n function bridgeWETHToBase(uint256 wethAmount) public payable onlyOperator {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_BASE_CHAIN_SELECTOR,\n IERC20(address(weth)),\n wethAmount\n );\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH.\n * @param wethAmount Amount of WETH to mint.\n * @param useNativeToken Whether to use native token to mint.\n * @return Amount of wOETH minted.\n */\n function mintAndWrap(uint256 wethAmount, bool useNativeToken)\n external\n onlyOperator\n returns (uint256)\n {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n return _mintAndWrap(wethAmount);\n }\n\n function wrapETH(uint256 ethAmount) public payable onlyOperator {\n // Deposit ETH into WETH\n safeContract.execTransactionFromModule(\n address(weth),\n ethAmount, // Value\n abi.encodeWithSelector(weth.deposit.selector),\n 0 // Call\n );\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH.\n * @param wethAmount Amount of WETH to mint.\n * @return Amount of wOETH minted.\n */\n function _mintAndWrap(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETH\");\n\n // Approve wOETH to move OETH\n success = safeContract.execTransactionFromModule(\n address(oeth),\n 0, // Value\n abi.encodeWithSelector(\n oeth.approve.selector,\n address(woeth),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETH\");\n\n uint256 woethAmount = woeth.balanceOf(address(safeContract));\n\n // Wrap OETH into wOETH\n success = safeContract.execTransactionFromModule(\n address(woeth),\n 0, // Value\n abi.encodeWithSelector(\n woeth.deposit.selector,\n wethAmount,\n address(safeContract)\n ),\n 0 // Call\n );\n require(success, \"Failed to wrap OETH\");\n\n // Compute amount of wOETH minted\n return woeth.balanceOf(address(safeContract)) - woethAmount;\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH, then bridges it to Plume.\n * @param wethAmount Amount of WETH to mint.\n * @param slippageBps Bridge slippage in 10^4 basis points.\n * @param useNativeToken Whether to use native token to mint.\n */\n function mintWrapAndBridgeToPlume(\n uint256 wethAmount,\n uint256 slippageBps,\n bool useNativeToken\n ) external payable onlyOperator {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n uint256 woethAmount = _mintAndWrap(wethAmount);\n bridgeWOETHToPlume(woethAmount, slippageBps);\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH, then bridges it to Base using CCIP.\n * @param wethAmount Amount of WETH to mint.\n * @param useNativeToken Whether to use native token to mint.\n */\n function mintWrapAndBridgeToBase(uint256 wethAmount, bool useNativeToken)\n external\n payable\n onlyOperator\n {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n uint256 woethAmount = _mintAndWrap(wethAmount);\n bridgeWOETHToBase(woethAmount);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH.\n * @param woethAmount Amount of wOETH to unwrap.\n * @return Amount of WETH received.\n */\n function unwrapAndRedeem(uint256 woethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _unwrapAndRedeem(woethAmount);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH.\n * @param woethAmount Amount of wOETH to unwrap.\n * @return Amount of WETH received.\n */\n function _unwrapAndRedeem(uint256 woethAmount) internal returns (uint256) {\n uint256 oethAmount = oeth.balanceOf(address(safeContract));\n\n // Unwrap wOETH\n bool success = safeContract.execTransactionFromModule(\n address(woeth),\n 0, // Value\n abi.encodeWithSelector(\n woeth.redeem.selector,\n woethAmount,\n address(safeContract),\n address(safeContract)\n ),\n 0 // Call\n );\n require(success, \"Failed to unwrap wOETH\");\n\n oethAmount = oeth.balanceOf(address(safeContract)) - oethAmount;\n\n // Redeem OETH using Vault to get WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethAmount,\n oethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETH\");\n\n return oethAmount;\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH, then bridges it to Plume.\n * @param woethAmount Amount of wOETH to unwrap.\n * @param slippageBps Bridge slippage in 10^4 basis points.\n */\n function unwrapRedeemAndBridgeToPlume(\n uint256 woethAmount,\n uint256 slippageBps\n ) external payable onlyOperator {\n uint256 wethAmount = _unwrapAndRedeem(woethAmount);\n // Unwrap into ETH\n safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(weth.withdraw.selector, wethAmount),\n 0 // Call\n );\n\n bridgeWETHToPlume(wethAmount, slippageBps);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH, then bridges it to Base using CCIP.\n * @param woethAmount Amount of wOETH to unwrap.\n */\n function unwrapRedeemAndBridgeToBase(uint256 woethAmount)\n external\n payable\n onlyOperator\n {\n uint256 wethAmount = _unwrapAndRedeem(woethAmount);\n bridgeWETHToBase(wethAmount);\n }\n}\n" + }, + "contracts/automation/PlumeBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { AbstractLZBridgeHelperModule } from \"./AbstractLZBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IOFT } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { BridgedWOETHStrategy } from \"../strategies/BridgedWOETHStrategy.sol\";\n\ncontract PlumeBridgeHelperModule is\n AccessControlEnumerable,\n AbstractLZBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0xc8c8F8bEA5631A8AF26440AF32a55002138cB76a);\n IWETH9 public constant weth =\n IWETH9(0xca59cA09E5602fAe8B629DeE83FfA819741f14be);\n IERC20 public constant oethp =\n IERC20(0xFCbe50DbE43bF7E5C88C6F6Fb9ef432D4165406E);\n IERC4626 public constant bridgedWOETH =\n IERC4626(0xD8724322f44E5c58D7A815F542036fb17DbbF839);\n\n uint32 public constant LZ_ETHEREUM_ENDPOINT_ID = 30101;\n IOFT public constant LZ_WOETH_OMNICHAIN_ADAPTER =\n IOFT(0x592CB6A596E7919930bF49a27AdAeCA7C055e4DB);\n IOFT public constant LZ_ETH_OMNICHAIN_ADAPTER =\n IOFT(0x4683CE822272CD66CEa73F5F1f9f5cBcaEF4F066);\n\n BridgedWOETHStrategy public constant bridgedWOETHStrategy =\n BridgedWOETHStrategy(0x1E3EdD5e019207D6355Ea77F724b1F1BF639B569);\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Ethereum.\n * @param woethAmount Amount of wOETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWOETHToEthereum(uint256 woethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_ETHEREUM_ENDPOINT_ID,\n IERC20(address(bridgedWOETH)),\n LZ_WOETH_OMNICHAIN_ADAPTER,\n woethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Bridges wETH to Ethereum.\n * @param wethAmount Amount of wETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWETHToEthereum(uint256 wethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_ETHEREUM_ENDPOINT_ID,\n IERC20(address(weth)),\n LZ_ETH_OMNICHAIN_ADAPTER,\n wethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem with Vault.\n * @return Amount of OETHp received.\n */\n function depositWOETH(uint256 woethAmount, bool redeemWithVault)\n external\n onlyOperator\n returns (uint256)\n {\n return _depositWOETH(woethAmount, redeemWithVault);\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy and bridges it to Ethereum.\n * @param woethAmount Amount of wOETH to deposit.\n * @param slippageBps Slippage in 10^4 basis points.\n * @return Amount of WETH received.\n */\n function depositWOETHAndBridgeWETH(uint256 woethAmount, uint256 slippageBps)\n external\n payable\n onlyOperator\n returns (uint256)\n {\n uint256 wethAmount = _depositWOETH(woethAmount, true);\n bridgeWETHToEthereum(wethAmount, slippageBps);\n return wethAmount;\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem with Vault.\n * @return Amount of OETHp received.\n */\n function _depositWOETH(uint256 woethAmount, bool redeemWithVault)\n internal\n returns (uint256)\n {\n // Update oracle price\n bridgedWOETHStrategy.updateWOETHOraclePrice();\n\n // Rebase to account for any yields from price update\n vault.rebase();\n\n uint256 oethpAmount = oethp.balanceOf(address(safeContract));\n\n // Approve bridgedWOETH strategy to move wOETH\n bool success = safeContract.execTransactionFromModule(\n address(bridgedWOETH),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETH.approve.selector,\n address(bridgedWOETHStrategy),\n woethAmount\n ),\n 0 // Call\n );\n\n // Deposit to bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.depositBridgedWOETH.selector,\n woethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to deposit bridged WOETH\");\n\n oethpAmount = oethp.balanceOf(address(safeContract)) - oethpAmount;\n\n // Rebase to account for any yields from price update\n // and backing asset change from deposit\n vault.rebase();\n\n if (!redeemWithVault) {\n return oethpAmount;\n }\n\n // Redeem for WETH using Vault\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethpAmount,\n oethpAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETHp\");\n\n return oethpAmount;\n }\n\n /**\n * @dev Deposits wETH into the vault.\n * @param wethAmount Amount of wETH to deposit.\n * @return Amount of OETHp received.\n */\n function depositWETHAndRedeemWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _withdrawWOETH(wethAmount);\n }\n\n /**\n * @dev Deposits wETH into the vault and bridges it to Ethereum.\n * @param wethAmount Amount of wETH to deposit.\n * @param slippageBps Slippage in 10^4 basis points.\n * @return Amount of WOETH received.\n */\n function depositWETHAndBridgeWOETH(uint256 wethAmount, uint256 slippageBps)\n external\n payable\n onlyOperator\n returns (uint256)\n {\n uint256 woethAmount = _withdrawWOETH(wethAmount);\n bridgeWOETHToEthereum(woethAmount, slippageBps);\n return woethAmount;\n }\n\n /**\n * @dev Withdraws wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to use to withdraw.\n * @return Amount of wOETH received.\n */\n function _withdrawWOETH(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETHp with WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETHp\");\n\n // Approve bridgedWOETH strategy to move OETHp\n success = safeContract.execTransactionFromModule(\n address(oethp),\n 0, // Value\n abi.encodeWithSelector(\n oethp.approve.selector,\n address(bridgedWOETHStrategy),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETHp\");\n\n uint256 woethAmount = bridgedWOETH.balanceOf(address(safeContract));\n\n // Withdraw from bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.withdrawBridgedWOETH.selector,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to withdraw bridged WOETH\");\n\n woethAmount =\n bridgedWOETH.balanceOf(address(safeContract)) -\n woethAmount;\n\n return woethAmount;\n }\n}\n" + }, + "contracts/beacon/BeaconConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to request validator consolidation on the beacon chain.\n * @author Origin Protocol Inc\n */\nlibrary BeaconConsolidation {\n /// @notice The address the validator consolidation requests are sent\n /// See https://eips.ethereum.org/EIPS/eip-7251\n address internal constant CONSOLIDATION_REQUEST_ADDRESS =\n 0x0000BBdDc7CE488642fb579F8B00f3a590007251;\n\n function request(bytes calldata source, bytes calldata target)\n internal\n returns (uint256 fee_)\n {\n require(source.length == 48, \"Invalid source byte length\");\n require(target.length == 48, \"Invalid target byte length\");\n\n fee_ = fee();\n\n // Call the Consolidation Request contract with the public keys of the source and target\n // validators packed together.\n // This does not have a function signature, so we use a call\n (bool success, ) = CONSOLIDATION_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(source, target)\n );\n\n require(success, \"Consolidation request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the consolidation request contract\n (bool success, bytes memory result) = CONSOLIDATION_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/beacon/BeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"./BeaconProofsLib.sol\";\nimport { IBeaconProofs } from \"../interfaces/IBeaconProofs.sol\";\n\n/**\n * @title Verifies merkle proofs of beacon chain data.\n * @author Origin Protocol Inc\n */\ncontract BeaconProofs is IBeaconProofs {\n /// @notice Verifies the validator index is for the given validator public key.\n /// Also verify the validator's withdrawal credential points to the withdrawal address.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param proof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata proof,\n uint40 validatorIndex,\n address withdrawalAddress\n ) external view {\n BeaconProofsLib.verifyValidator(\n beaconBlockRoot,\n pubKeyHash,\n proof,\n validatorIndex,\n withdrawalAddress\n );\n }\n\n /// @notice Verifies a validator's withdrawable epoch to the beacon block root\n /// for a given validator index.\n /// Also verifies the validator's public key for the given validator index.\n /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n /// @param beaconBlockRoot The root of the beacon block\n /// @param validatorIndex The validator index to verify the withdrawable epoch for.\n /// @param withdrawableEpoch The withdrawable epoch to verify in big endian uint64 format\n /// @param withdrawableEpochProof The merkle proof for the validator's withdrawable epoch to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorPubKeyProof The merkle proof for the validator public key in a sub tree of height two.\n /// This is 2 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n bytes32 pubKeyHash,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof,\n bytes calldata validatorPubKeyProof\n ) external view {\n BeaconProofsLib.verifyValidatorWithdrawableEpoch(\n beaconBlockRoot,\n validatorIndex,\n withdrawableEpoch,\n withdrawableEpochProof\n );\n\n // Get the third 32 byte witness from the withdrawable epoch proof\n // 2 * 32 bytes = 64 bytes offset\n bytes32 subTreeRoot = bytes32(withdrawableEpochProof[64:96]);\n\n BeaconProofsLib.verifyValidatorPubKeySubTree(\n subTreeRoot,\n pubKeyHash,\n validatorPubKeyProof\n );\n }\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view {\n BeaconProofsLib.verifyValidatorWithdrawableEpoch(\n beaconBlockRoot,\n validatorIndex,\n withdrawableEpoch,\n withdrawableEpochProof\n );\n }\n\n /// @notice Verifies the balances container to the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerRoot The merkle root of the the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata balancesContainerProof\n ) external view {\n BeaconProofsLib.verifyBalancesContainer(\n beaconBlockRoot,\n balancesContainerRoot,\n balancesContainerProof\n );\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param balanceProof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint40 validatorIndex\n ) external view returns (uint256 validatorBalanceGwei) {\n validatorBalanceGwei = BeaconProofsLib.verifyValidatorBalance(\n balancesContainerRoot,\n validatorBalanceLeaf,\n balanceProof,\n validatorIndex\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the pubKey and slot of the first pending deposit to the beacon block root.\n /// BeaconBlock.state.PendingDeposits[0].pubKey\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty.\n /// @param pubKeyHash The hash of the validator public key for the first pending deposit.\n /// Use zero bytes if the deposit queue is empty.\n /// @param firstPendingDepositPubKeyProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].pubKey when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes32 pubKeyHash,\n bytes calldata firstPendingDepositPubKeyProof\n ) external view returns (bool isEmptyDepositQueue) {\n isEmptyDepositQueue = BeaconProofsLib.verifyFirstPendingDeposit(\n beaconBlockRoot,\n slot,\n pubKeyHash,\n firstPendingDepositPubKeyProof\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the slot of the first pending deposit to the beacon block root.\n /// BeaconBlock.state.pendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty.\n /// @param firstPendingDepositSlotProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view returns (bool isEmptyDepositQueue) {\n isEmptyDepositQueue = BeaconProofsLib.verifyFirstPendingDeposit(\n beaconBlockRoot,\n slot,\n firstPendingDepositSlotProof\n );\n }\n}\n" + }, + "contracts/beacon/BeaconProofsLib.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Merkle } from \"./Merkle.sol\";\nimport { Endian } from \"./Endian.sol\";\n\n/**\n * @title Library to verify merkle proofs of beacon chain data.\n * @author Origin Protocol Inc\n */\nlibrary BeaconProofsLib {\n // Known generalized indices in the beacon block\n /// @dev BeaconBlock.state.PendingDeposits[0]\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, pending deposits at index 34\n /// Pending deposits container: height 28, first deposit at index 0\n /// ((2 ^ 3 + 3) * 2 ^ 6 + 34) * 2 ^ 28 + 0 = 198105366528\n uint256 internal constant FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX =\n 198105366528;\n /// @dev BeaconBlock.state.PendingDeposits[0].pubkey\n /// Pending Deposit container: height 3, pubkey at index 0\n /// (((2 ^ 3 + 3) * 2 ^ 6 + 34) * 2 ^ 28 + 0) * 2 ^ 3 + 0 = 1584842932224\n uint256 internal constant FIRST_PENDING_DEPOSIT_PUBKEY_GENERALIZED_INDEX =\n 1584842932224;\n /// @dev BeaconBlock.state.PendingDeposits[0].pubkey\n /// Pending Deposit container: height 3, pubkey at index 4\n /// (((2 ^ 3 + 3) * 2 ^ 6 + 34) * 2 ^ 28 + 0) * 2 ^ 3 + 4 = 1584842932228\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX =\n 1584842932228;\n /// @dev BeaconBlock.state.validators\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, validators at index 11\n /// (2 ^ 3 + 3) * 2 ^ 6 + 11 = 715\n uint256 internal constant VALIDATORS_CONTAINER_GENERALIZED_INDEX = 715;\n /// @dev BeaconBlock.state.balances\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, balances at index 12\n /// (2 ^ 3 + 3) * 2 ^ 6 + 12 = 716\n uint256 internal constant BALANCES_CONTAINER_GENERALIZED_INDEX = 716;\n\n /// @dev Number of bytes in the proof to the first pending deposit.\n /// 37 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0]\n /// 37 * 32 bytes = 1184 bytes\n uint256 internal constant FIRST_PENDING_DEPOSIT_PROOF_LENGTH = 1184;\n /// @dev Number of bytes in the proof from the pubKey of the first pending deposit to the beacon block root.\n /// 40 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0].pubKey\n /// 40 * 32 bytes = 1280 bytes\n uint256 internal constant FIRST_PENDING_DEPOSIT_PUBKEY_PROOF_LENGTH = 1280;\n /// @dev Number of bytes in the proof from the slot of the first pending deposit to the beacon block root.\n /// 40 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0].slot\n /// 40 * 32 bytes = 1280 bytes\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_PROOF_LENGTH = 1280;\n /// The slot is at index 4 in the Pending Deposits container.\n /// The sub tree from the right node from the root is a tree of height 2.\n /// The first 32 bytes witness is an empty bytes32 as there are\n /// no items after the slot in the Pending Deposits container.\n /// The second 32 bytes witness is a hash or two empty bytes32.\n bytes internal constant PENDING_DEPOSIT_SLOT_PROOF =\n // solhint-disable-next-line max-line-length\n hex\"0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b\";\n\n /// @dev Merkle height of the Balances container\n /// BeaconBlock.state.balances\n uint256 internal constant BALANCES_HEIGHT = 39;\n /// @dev Merkle height of the Validators container list\n /// BeaconBlock.state.validators\n uint256 internal constant VALIDATORS_LIST_HEIGHT = 41;\n /// @dev Merkle height of the Validator container\n /// BeaconBlock.state.validators[validatorIndex]\n uint256 internal constant VALIDATOR_CONTAINER_HEIGHT = 3;\n\n /// @dev Position of the pubkey field in the Validator container.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;\n /// @dev Position of the withdrawable epoch field in the Validator container.\n /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n uint256 internal constant VALIDATOR_WITHDRAWABLE_EPOCH_INDEX = 7;\n\n /// @notice Verifies the validator index is for the given validator public key.\n /// Also verify the validator's withdrawal credential points to the withdrawal address.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param proof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata proof,\n uint40 validatorIndex,\n address withdrawalAddress\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.validators[validatorIndex]\n uint256 generalizedIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_LIST_HEIGHT,\n validatorIndex\n );\n // BeaconBlock.state.validators[validatorIndex].pubkey\n generalizedIndex = concatGenIndices(\n generalizedIndex,\n VALIDATOR_CONTAINER_HEIGHT,\n VALIDATOR_PUBKEY_INDEX\n );\n\n // Get the withdrawal address from the first witness in the pubkey merkle proof.\n bytes32 withdrawalCredentialsFromProofHash;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // The first 32 bytes of the proof is the withdrawal credential so load it into memory.\n calldatacopy(0, proof.offset, 32)\n // Compute keccak256 hash directly on the scratch space\n withdrawalCredentialsFromProofHash := keccak256(0, 32)\n }\n\n bytes32 withdrawalCredentialsHash = keccak256(\n abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(withdrawalAddress)\n )\n );\n\n require(\n withdrawalCredentialsFromProofHash == withdrawalCredentialsHash,\n \"Invalid withdrawal cred\"\n );\n\n require(\n // 53 * 32 bytes = 1696 bytes\n proof.length == 1696 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: pubKeyHash,\n index: generalizedIndex\n }),\n \"Invalid validator proof\"\n );\n }\n\n /// @notice Verifies a validator's withdrawable epoch to the beacon block root\n /// for a given validator index.\n /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n /// @param beaconBlockRoot The root of the beacon block\n /// @param validatorIndex The validator index to verify the withdrawable epoch for.\n /// @param withdrawableEpoch The withdrawable epoch to verify in big endian uint64 format\n /// @param proof The merkle proof for the validator's withdrawable epoch to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyValidatorWithdrawableEpoch(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata proof\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.validators[validatorIndex]\n uint256 exitEpochGenIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_LIST_HEIGHT,\n validatorIndex\n );\n // BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n exitEpochGenIndex = concatGenIndices(\n exitEpochGenIndex,\n VALIDATOR_CONTAINER_HEIGHT,\n VALIDATOR_WITHDRAWABLE_EPOCH_INDEX\n );\n\n require(\n // 53 * 32 bytes = 1696 bytes\n proof.length == 1696 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: Endian.toLittleEndianUint64(withdrawableEpoch),\n index: exitEpochGenIndex\n }),\n \"Invalid withdrawable proof\"\n );\n }\n\n /// @param subTreeRoot The third 32 byte witness from the withdrawable epoch proof\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param proof The merkle proof for the validator public key in a sub tree of height two.\n /// This is 2 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyValidatorPubKeySubTree(\n bytes32 subTreeRoot,\n bytes32 pubKeyHash,\n bytes calldata proof\n ) internal view {\n // Tree height 2 and pub key is at index 0\n // index = 2 ^ 2 + 0 = 4\n require(\n // 2 * 32 bytes = 64 bytes\n proof.length == 64 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: subTreeRoot,\n leaf: pubKeyHash,\n index: 4\n }),\n \"Invalid pub key proof\"\n );\n }\n\n /// @notice Verifies the balances container to the beacon block root.\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param balancesContainerRoot The merkle root of the the balances container.\n /// @param proof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata proof\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.balances\n require(\n // 9 * 32 bytes = 288 bytes\n proof.length == 288 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: balancesContainerRoot,\n index: BALANCES_CONTAINER_GENERALIZED_INDEX\n }),\n \"Invalid balance container proof\"\n );\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param proof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for.\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index.\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata proof,\n uint40 validatorIndex\n ) internal view returns (uint256 validatorBalanceGwei) {\n require(balancesContainerRoot != bytes32(0), \"Invalid container root\");\n\n // Four balances are stored in each leaf so the validator index is divided by 4\n uint64 balanceIndex = validatorIndex / 4;\n\n // Get the index within the balances container, not the Beacon Block\n // BeaconBlock.state.balances[balanceIndex]\n uint256 generalizedIndex = concatGenIndices(\n 1,\n BALANCES_HEIGHT,\n balanceIndex\n );\n\n validatorBalanceGwei = balanceAtIndex(\n validatorBalanceLeaf,\n validatorIndex\n );\n\n require(\n // 39 * 32 bytes = 1248 bytes\n proof.length == 1248 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: balancesContainerRoot,\n leaf: validatorBalanceLeaf,\n index: generalizedIndex\n }),\n \"Invalid balance proof\"\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the pubKey and slot of the first pending deposit to the beacon block root.\n /// BeaconBlock.state.pendingDeposits[0].pubKey\n /// BeaconBlock.state.pendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty.\n /// @param pubKeyHash The hash of the validator public key for the first pending deposit.\n /// Use zero bytes if the deposit queue is empty.\n /// @param proof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].pubKey when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes32 pubKeyHash,\n bytes calldata proof\n ) internal view returns (bool isEmptyDepositQueue) {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // If the deposit queue is empty\n if (proof.length == FIRST_PENDING_DEPOSIT_PROOF_LENGTH) {\n require(\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: bytes32(0),\n index: FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX\n }),\n \"Invalid empty deposits proof\"\n );\n return true;\n }\n\n // Verify the public key of the first pending deposit\n // BeaconBlock.state.PendingDeposits[0].pubKey\n require(\n proof.length == FIRST_PENDING_DEPOSIT_PUBKEY_PROOF_LENGTH &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: pubKeyHash,\n index: FIRST_PENDING_DEPOSIT_PUBKEY_GENERALIZED_INDEX\n }),\n \"Invalid deposit pub key proof\"\n );\n\n // Now verify the slot of the first pending deposit\n\n // Get the third 32 bytes witness from the first pending deposit pubKey proof\n // 2 * 32 bytes = 64 bytes offset\n bytes32 slotRoot = bytes32(proof[64:96]);\n\n // Sub tree height 2 and slot is at index 0 in the sub tree\n // index = 2 ^ 2 + 0 = 4\n require(\n Merkle.verifyInclusionSha256({\n proof: PENDING_DEPOSIT_SLOT_PROOF,\n root: slotRoot,\n leaf: Endian.toLittleEndianUint64(slot),\n index: 4\n }),\n \"Invalid deposit slot\"\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the slot of the first pending deposit to the beacon block root.\n /// BeaconBlock.state.pendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty.\n /// @param proof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata proof\n ) internal view returns (bool isEmptyDepositQueue) {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // If the deposit queue is empty\n if (proof.length == FIRST_PENDING_DEPOSIT_PROOF_LENGTH) {\n require(\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: bytes32(0),\n index: FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX\n }),\n \"Invalid empty deposits proof\"\n );\n return true;\n }\n\n // Verify the public key of the first pending deposit\n // BeaconBlock.state.PendingDeposits[0].slot\n require(\n proof.length == FIRST_PENDING_DEPOSIT_SLOT_PROOF_LENGTH &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: Endian.toLittleEndianUint64(slot),\n index: FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX\n }),\n \"Invalid deposit slot proof\"\n );\n }\n\n ////////////////////////////////////////////////////\n /// Internal Helper Functions\n ////////////////////////////////////////////////////\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint40 validatorIndex)\n internal\n pure\n returns (uint256)\n {\n uint256 bitShiftAmount = (validatorIndex % 4) * 64;\n return\n Endian.fromLittleEndianUint64(\n bytes32((uint256(validatorBalanceLeaf) << bitShiftAmount))\n );\n }\n\n /// @notice Concatenates two beacon chain generalized indices into one.\n /// @param genIndex The first generalized index or 1 if calculating for a single container.\n /// @param height The merkle tree height of the second container. eg 39 for balances, 41 for validators.\n /// @param index The index within the second container. eg the validator index.\n /// @return genIndex The concatenated generalized index.\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) internal pure returns (uint256) {\n return (genIndex << height) | index;\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to retrieve beacon block roots.\n * @author Origin Protocol Inc\n */\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the beacon block root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Roots contract to get the parent block root.\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/Endian.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n/**\n * @title Library to handle conversion between little-endian and big-endian formats.\n * @author Origin Protocol Inc\n */\nlibrary Endian {\n /**\n * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64\n * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type\n * @return n The big endian-formatted uint64\n * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits),\n * but it is immediately truncated to a uint64 (i.e. 64 bits)\n * through a right-shift/shr operation.\n */\n function fromLittleEndianUint64(bytes32 lenum)\n internal\n pure\n returns (uint64 n)\n {\n // the number needs to be stored in little-endian encoding (ie in bytes 0-8)\n n = uint64(uint256(lenum >> 192));\n // forgefmt: disable-next-item\n return\n (n >> 56) |\n ((0x00FF000000000000 & n) >> 40) |\n ((0x0000FF0000000000 & n) >> 24) |\n ((0x000000FF00000000 & n) >> 8) |\n ((0x00000000FF000000 & n) << 8) |\n ((0x0000000000FF0000 & n) << 24) |\n ((0x000000000000FF00 & n) << 40) |\n ((0x00000000000000FF & n) << 56);\n }\n\n function toLittleEndianUint64(uint64 benum)\n internal\n pure\n returns (bytes32 n)\n {\n // Convert to little-endian by reversing byte order\n uint64 reversed = (benum >> 56) |\n ((0x00FF000000000000 & benum) >> 40) |\n ((0x0000FF0000000000 & benum) >> 24) |\n ((0x000000FF00000000 & benum) >> 8) |\n ((0x00000000FF000000 & benum) << 8) |\n ((0x0000000000FF0000 & benum) << 24) |\n ((0x000000000000FF00 & benum) << 40) |\n ((0x00000000000000FF & benum) << 56);\n\n // Store the little-endian uint64 in the least significant 64 bits of bytes32\n n = bytes32(uint256(reversed));\n // Shift to most significant bits\n n = n << 192;\n }\n}\n" + }, + "contracts/beacon/Merkle.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev These functions deal with verification of Merkle Tree proofs.\n *\n * The tree and the proofs can be generated using our\n * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].\n * You will find a quickstart guide in the readme.\n *\n * WARNING: You should avoid using leaf values that are 64 bytes long prior to\n * hashing, or use a hash function other than keccak256 for hashing leaves.\n * This is because the concatenation of a sorted pair of internal nodes in\n * the merkle tree could be reinterpreted as a leaf value.\n * OpenZeppelin's JavaScript library generates merkle trees that are safe\n * against this attack out of the box.\n */\nlibrary Merkle {\n error InvalidProofLength();\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function verifyInclusionSha256(\n bytes memory proof,\n bytes32 root,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bool) {\n return processInclusionProofSha256(proof, leaf, index) == root;\n }\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * _Available since v4.4._\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function processInclusionProofSha256(\n bytes memory proof,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bytes32) {\n require(\n proof.length != 0 && proof.length % 32 == 0,\n InvalidProofLength()\n );\n bytes32[1] memory computedHash = [leaf];\n for (uint256 i = 32; i <= proof.length; i += 32) {\n if (index % 2 == 0) {\n // if ith bit of index is 0, then computedHash is a left sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(computedHash))\n mstore(0x20, mload(add(proof, i)))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n }\n } else {\n // if ith bit of index is 1, then computedHash is a right sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(add(proof, i)))\n mstore(0x20, mload(computedHash))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n }\n }\n index = index / 2;\n }\n return computedHash[0];\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to request full or partial withdrawals from validators on the beacon chain.\n * @author Origin Protocol Inc\n */\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n /// @notice Requests a partial withdrawal for a given validator public key and amount.\n /// @param validatorPubKey The public key of the validator to withdraw from\n /// @param amount The amount of ETH to withdraw\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n /// @notice Gets fee for withdrawal requests contract on Beacon chain\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/bridges/OmnichainL2Adapter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.0;\n\nimport { MintBurnOFTAdapter } from \"@layerzerolabs/oft-evm/contracts/MintBurnOFTAdapter.sol\";\nimport { IMintableBurnable } from \"@layerzerolabs/oft-evm/contracts/interfaces/IMintableBurnable.sol\";\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\n\n/// NOTE: It's necessary to inherit from Ownable instead of Governable\n/// because OFTCore uses Ownable to manage the governor.\n/// Ownable uses slot 0 for storing the address, whereas Goveranble\n/// stores it in a computed slot.\n\n/// @notice Omnichain uses a deployed ERC-20 token and safeERC20\n/// to interact with the OFTCore contract.\n/// On L2, we follow the mint and burn mechanism.\n/// The adapter should have minter and burner roles.\n\n/// @title Omnichain L2 Adapter\ncontract OmnichainL2Adapter is MintBurnOFTAdapter {\n constructor(\n address _token,\n address _lzEndpoint,\n address _governor\n )\n MintBurnOFTAdapter(\n _token,\n IMintableBurnable(_token),\n _lzEndpoint,\n _governor\n )\n Ownable()\n {\n _transferOwnership(_governor);\n }\n\n /// @inheritdoc MintBurnOFTAdapter\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n )\n internal\n virtual\n override\n returns (uint256 amountSentLD, uint256 amountReceivedLD)\n {\n (amountSentLD, amountReceivedLD) = _debitView(\n _amountLD,\n _minAmountLD,\n _dstEid\n );\n // Burns tokens from the caller.\n IMintableERC20(address(minterBurner)).burn(_from, amountSentLD);\n }\n\n /// @inheritdoc MintBurnOFTAdapter\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /* _srcEid */\n ) internal virtual override returns (uint256 amountReceivedLD) {\n if (_to == address(0x0)) _to = address(0xdead); // _mint(...) does not support address(0x0)\n // Mints the tokens and transfers to the recipient.\n IMintableERC20(address(minterBurner)).mint(_to, _amountLD);\n // In the case of NON-default OFTAdapter, the amountLD MIGHT not be equal to amountReceivedLD.\n return _amountLD;\n }\n}\n\ninterface IMintableERC20 {\n function mint(address to, uint256 value) external;\n\n function burn(address to, uint256 value) external;\n}\n" + }, + "contracts/bridges/OmnichainMainnetAdapter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.0;\n\nimport { OFTAdapter } from \"@layerzerolabs/oft-evm/contracts/OFTAdapter.sol\";\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\n\n/// NOTE: It's necessary to inherit from Ownable instead of Governable\n/// because OFTCore uses Ownable to manage the governor.\n/// Ownable uses slot 0 for storing the address, whereas Goveranble\n/// stores it in a computed slot.\n\n/// @notice Omnichain uses a deployed ERC-20 token and safeERC20\n/// to interact with the OFTCore contract.\n/// On Ethereum, we follow the lock and unlock mechanism.\n\n/// @title Omnichain Mainnet Adapter\ncontract OmnichainMainnetAdapter is OFTAdapter {\n constructor(\n address _token,\n address _lzEndpoint,\n address _governor\n ) OFTAdapter(_token, _lzEndpoint, _governor) Ownable() {\n _transferOwnership(_governor);\n }\n}\n" + }, + "contracts/buyback/AbstractBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { ICVXLocker } from \"../interfaces/ICVXLocker.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\n\nabstract contract AbstractBuyback is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n event SwapRouterUpdated(address indexed _address);\n\n event RewardsSourceUpdated(address indexed _address);\n event TreasuryManagerUpdated(address indexed _address);\n event CVXShareBpsUpdated(uint256 bps);\n\n // Emitted whenever OUSD/OETH is swapped for OGN/CVX or any other token\n event OTokenBuyback(\n address indexed oToken,\n address indexed swappedFor,\n uint256 swapAmountIn,\n uint256 amountOut\n );\n\n // Address of 1-inch Swap Router\n address public swapRouter;\n\n // slither-disable-next-line constable-states\n address private __deprecated_ousd;\n // slither-disable-next-line constable-states\n address private __deprecated_ogv;\n // slither-disable-next-line constable-states\n address private __deprecated_usdt;\n // slither-disable-next-line constable-states\n address private __deprecated_weth9;\n\n // Address that receives OGN after swaps\n address public rewardsSource;\n\n // Address that receives all other tokens after swaps\n address public treasuryManager;\n\n // slither-disable-next-line constable-states\n uint256 private __deprecated_treasuryBps;\n\n address public immutable oToken;\n address public immutable ogn;\n address public immutable cvx;\n address public immutable cvxLocker;\n\n // Amount of `oToken` balance to use for OGN buyback\n uint256 public balanceForOGN;\n\n // Amount of `oToken` balance to use for CVX buyback\n uint256 public balanceForCVX;\n\n // Percentage of `oToken` balance to be used for CVX\n uint256 public cvxShareBps; // 10000 = 100%\n\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) {\n // Make sure nobody owns the implementation contract\n _setGovernor(address(0));\n\n oToken = _oToken;\n ogn = _ogn;\n cvx = _cvx;\n cvxLocker = _cvxLocker;\n }\n\n /**\n * @param _swapRouter Address of Uniswap V3 Router\n * @param _strategistAddr Address of Strategist multi-sig wallet\n * @param _treasuryManagerAddr Address that receives the treasury's share of OUSD\n * @param _rewardsSource Address of RewardsSource contract\n * @param _cvxShareBps Percentage of balance to use for CVX\n */\n function initialize(\n address _swapRouter,\n address _strategistAddr,\n address _treasuryManagerAddr,\n address _rewardsSource,\n uint256 _cvxShareBps\n ) external onlyGovernor initializer {\n _setStrategistAddr(_strategistAddr);\n\n _setSwapRouter(_swapRouter);\n _setRewardsSource(_rewardsSource);\n\n _setTreasuryManager(_treasuryManagerAddr);\n\n _setCVXShareBps(_cvxShareBps);\n }\n\n /**\n * @dev Set address of Uniswap Universal Router for performing liquidation\n * of platform fee tokens. Setting to 0x0 will pause swaps.\n *\n * @param _router Address of the Uniswap Universal router\n */\n function setSwapRouter(address _router) external onlyGovernor {\n _setSwapRouter(_router);\n }\n\n function _setSwapRouter(address _router) internal {\n address oldRouter = swapRouter;\n swapRouter = _router;\n\n if (oldRouter != address(0)) {\n // Remove allowance of old router, if any\n\n if (IERC20(ogn).allowance(address(this), oldRouter) != 0) {\n // slither-disable-next-line unused-return\n IERC20(ogn).safeApprove(oldRouter, 0);\n }\n\n if (IERC20(cvx).allowance(address(this), oldRouter) != 0) {\n // slither-disable-next-line unused-return\n IERC20(cvx).safeApprove(oldRouter, 0);\n }\n }\n\n emit SwapRouterUpdated(_router);\n }\n\n /**\n * @dev Sets the address that receives the OGN buyback rewards\n * @param _address Address\n */\n function setRewardsSource(address _address) external onlyGovernor {\n _setRewardsSource(_address);\n }\n\n function _setRewardsSource(address _address) internal {\n require(_address != address(0), \"Address not set\");\n rewardsSource = _address;\n emit RewardsSourceUpdated(_address);\n }\n\n /**\n * @dev Sets the address that can receive and manage the funds for Treasury\n * @param _address Address\n */\n function setTreasuryManager(address _address) external onlyGovernor {\n _setTreasuryManager(_address);\n }\n\n function _setTreasuryManager(address _address) internal {\n require(_address != address(0), \"Address not set\");\n treasuryManager = _address;\n emit TreasuryManagerUpdated(_address);\n }\n\n /**\n * @dev Sets the percentage of oToken to use for Flywheel tokens\n * @param _bps BPS, 10000 to 100%\n */\n function setCVXShareBps(uint256 _bps) external onlyGovernor {\n _setCVXShareBps(_bps);\n }\n\n function _setCVXShareBps(uint256 _bps) internal {\n require(_bps <= 10000, \"Invalid bps value\");\n cvxShareBps = _bps;\n emit CVXShareBpsUpdated(_bps);\n }\n\n /**\n * @dev Computes the split of oToken balance that can be\n * used for OGN and CVX buybacks.\n */\n function _updateBuybackSplits()\n internal\n returns (uint256 _balanceForOGN, uint256 _balanceForCVX)\n {\n _balanceForOGN = balanceForOGN;\n _balanceForCVX = balanceForCVX;\n\n uint256 totalBalance = IERC20(oToken).balanceOf(address(this));\n uint256 unsplitBalance = totalBalance - _balanceForOGN - _balanceForCVX;\n\n // Check if all balance is accounted for\n if (unsplitBalance != 0) {\n // If not, split unaccounted balance based on `cvxShareBps`\n uint256 addToCVX = (unsplitBalance * cvxShareBps) / 10000;\n _balanceForCVX = _balanceForCVX + addToCVX;\n _balanceForOGN = _balanceForOGN + unsplitBalance - addToCVX;\n\n // Update storage\n balanceForOGN = _balanceForOGN;\n balanceForCVX = _balanceForCVX;\n }\n }\n\n function updateBuybackSplits() external onlyGovernor {\n // slither-disable-next-line unused-return\n _updateBuybackSplits();\n }\n\n function _swapToken(\n address tokenOut,\n uint256 oTokenAmount,\n uint256 minAmountOut,\n bytes calldata swapData\n ) internal returns (uint256 amountOut) {\n require(oTokenAmount > 0, \"Invalid Swap Amount\");\n require(swapRouter != address(0), \"Swap Router not set\");\n require(minAmountOut > 0, \"Invalid minAmount\");\n\n // Transfer OToken to Swapper for swapping\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(oToken).transfer(swapRouter, oTokenAmount);\n\n // Swap\n amountOut = ISwapper(swapRouter).swap(\n oToken,\n tokenOut,\n oTokenAmount,\n minAmountOut,\n swapData\n );\n\n require(amountOut >= minAmountOut, \"Higher Slippage\");\n\n emit OTokenBuyback(oToken, tokenOut, oTokenAmount, amountOut);\n }\n\n /**\n * @dev Swaps `oTokenAmount` to OGN\n * @param oTokenAmount Amount of OUSD/OETH to swap\n * @param minOGN Minimum OGN to receive for oTokenAmount\n * @param swapData 1inch Swap Data\n */\n function swapForOGN(\n uint256 oTokenAmount,\n uint256 minOGN,\n bytes calldata swapData\n ) external onlyGovernorOrStrategist nonReentrant {\n (uint256 _amountForOGN, ) = _updateBuybackSplits();\n require(_amountForOGN >= oTokenAmount, \"Balance underflow\");\n require(rewardsSource != address(0), \"RewardsSource contract not set\");\n\n unchecked {\n // Subtract the amount to swap from net balance\n balanceForOGN = _amountForOGN - oTokenAmount;\n }\n\n uint256 ognReceived = _swapToken(ogn, oTokenAmount, minOGN, swapData);\n\n // Transfer OGN received to RewardsSource contract\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(ogn).transfer(rewardsSource, ognReceived);\n }\n\n /**\n * @dev Swaps `oTokenAmount` to CVX\n * @param oTokenAmount Amount of OUSD/OETH to swap\n * @param minCVX Minimum CVX to receive for oTokenAmount\n * @param swapData 1inch Swap Data\n */\n function swapForCVX(\n uint256 oTokenAmount,\n uint256 minCVX,\n bytes calldata swapData\n ) external onlyGovernorOrStrategist nonReentrant {\n (, uint256 _amountForCVX) = _updateBuybackSplits();\n require(_amountForCVX >= oTokenAmount, \"Balance underflow\");\n\n unchecked {\n // Subtract the amount to swap from net balance\n balanceForCVX = _amountForCVX - oTokenAmount;\n }\n\n uint256 cvxReceived = _swapToken(cvx, oTokenAmount, minCVX, swapData);\n\n // Lock all CVX\n _lockAllCVX(cvxReceived);\n }\n\n /**\n * @dev Locks all CVX held by the contract on behalf of the Treasury Manager\n */\n function lockAllCVX() external onlyGovernorOrStrategist {\n _lockAllCVX(IERC20(cvx).balanceOf(address(this)));\n }\n\n function _lockAllCVX(uint256 cvxAmount) internal {\n require(\n treasuryManager != address(0),\n \"Treasury manager address not set\"\n );\n\n // Lock all available CVX on behalf of `treasuryManager`\n ICVXLocker(cvxLocker).lock(treasuryManager, cvxAmount, 0);\n }\n\n /**\n * @dev Approve CVX Locker to move CVX held by this contract\n */\n function safeApproveAllTokens() external onlyGovernorOrStrategist {\n IERC20(cvx).safeApprove(cvxLocker, type(uint256).max);\n }\n\n /**\n * @notice Owner function to withdraw a specific amount of a token\n * @param token token to be transferered\n * @param amount amount of the token to be transferred\n */\n function transferToken(address token, uint256 amount)\n external\n onlyGovernor\n nonReentrant\n {\n IERC20(token).safeTransfer(_governor(), amount);\n }\n}\n" + }, + "contracts/buyback/ARMBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract ARMBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/buyback/OETHBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract OETHBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/buyback/OUSDBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract OUSDBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/echidna/Debugger.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary Debugger {\n event Debug(string debugString);\n event Debug(string description, string data);\n event Debug(string prefix, string description, string data);\n event Debug(string description, bytes32 data);\n event Debug(string prefix, string description, bytes32 data);\n event Debug(string description, uint256 data);\n event Debug(string prefix, string description, uint256 data);\n event Debug(string description, int256 data);\n event Debug(string prefix, string description, int256 data);\n event Debug(string description, address data);\n event Debug(string prefix, string description, address data);\n event Debug(string description, bool data);\n event Debug(string prefix, string description, bool data);\n\n function log(string memory debugString) internal {\n emit Debug(debugString);\n }\n\n function log(string memory description, string memory data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n string memory data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, bytes32 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n bytes32 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, uint256 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n uint256 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, int256 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n int256 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, address data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n address data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, bool data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n bool data\n ) internal {\n emit Debug(prefix, description, data);\n }\n}\n" + }, + "contracts/echidna/Echidna.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaTestApproval.sol\";\n\n/**\n * @title Echidna test contract for OUSD\n * @notice Target contract to be tested, containing all mixins\n * @author Rappie\n */\ncontract Echidna is EchidnaTestApproval {\n\n}\n" + }, + "contracts/echidna/EchidnaConfig.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Top-level mixin for configuring the desired fuzzing setup\n * @author Rappie\n */\ncontract EchidnaConfig {\n address internal constant ADDRESS_VAULT = address(0x10000);\n address internal constant ADDRESS_OUTSIDER_USER = address(0x20000);\n\n address internal constant ADDRESS_USER0 = address(0x30000);\n address internal constant ADDRESS_USER1 = address(0x40000);\n\n // Will be set in EchidnaSetup constructor\n address internal ADDRESS_OUTSIDER_CONTRACT;\n address internal ADDRESS_CONTRACT0;\n address internal ADDRESS_CONTRACT1;\n\n // Toggle known issues\n //\n // This can be used to skip tests that are known to fail. This is useful\n // when debugging a specific issue, but should be disabled when running\n // the full test suite.\n //\n // True => skip tests that are known to fail\n // False => run all tests\n //\n bool internal constant TOGGLE_KNOWN_ISSUES = false;\n\n // Toggle known issues within limits\n //\n // Same as TOGGLE_KNOWN_ISSUES, but also skip tests that are known to fail\n // within limits set by the variables below.\n //\n bool internal constant TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS = true;\n\n // Starting balance\n //\n // Gives OUSD a non-zero starting supply, which can be useful to ignore\n // certain edge cases.\n //\n // The starting balance is given to outsider accounts that are not used as\n // accounts while fuzzing.\n //\n bool internal constant TOGGLE_STARTING_BALANCE = true;\n uint256 internal constant STARTING_BALANCE = 1_000_000e18;\n\n // Change supply\n //\n // Set a limit to the amount of change per rebase, which can be useful to\n // ignore certain edge cases.\n //\n // True => limit the amount of change to a percentage of total supply\n // False => no limit\n //\n bool internal constant TOGGLE_CHANGESUPPLY_LIMIT = true;\n uint256 internal constant CHANGESUPPLY_DIVISOR = 10; // 10% of total supply\n\n // Mint limit\n //\n // Set a limit the amount minted per mint, which can be useful to\n // ignore certain edge cases.\n //\n // True => limit the amount of minted tokens\n // False => no limit\n //\n bool internal constant TOGGLE_MINT_LIMIT = true;\n uint256 internal constant MINT_MODULO = 1_000_000_000_000e18;\n\n // Known rounding errors\n uint256 internal constant TRANSFER_ROUNDING_ERROR = 1e18 - 1;\n uint256 internal constant OPT_IN_ROUNDING_ERROR = 1e18 - 1;\n uint256 internal constant MINT_ROUNDING_ERROR = 1e18 - 1;\n\n /**\n * @notice Modifier to skip tests that are known to fail\n * @dev see TOGGLE_KNOWN_ISSUES for more information\n */\n modifier hasKnownIssue() {\n if (TOGGLE_KNOWN_ISSUES) return;\n _;\n }\n\n /**\n * @notice Modifier to skip tests that are known to fail within limits\n * @dev see TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS for more information\n */\n modifier hasKnownIssueWithinLimits() {\n if (TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS) return;\n _;\n }\n\n /**\n * @notice Translate an account ID to an address\n * @param accountId The ID of the account\n * @return account The address of the account\n */\n function getAccount(uint8 accountId)\n internal\n view\n returns (address account)\n {\n accountId = accountId / 64;\n if (accountId == 0) return account = ADDRESS_USER0;\n if (accountId == 1) return account = ADDRESS_USER1;\n if (accountId == 2) return account = ADDRESS_CONTRACT0;\n if (accountId == 3) return account = ADDRESS_CONTRACT1;\n require(false, \"Unknown account ID\");\n }\n}\n" + }, + "contracts/echidna/EchidnaDebug.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport \"./EchidnaHelper.sol\";\nimport \"./Debugger.sol\";\n\nimport \"../token/OUSD.sol\";\n\n/**\n * @title Room for random debugging functions\n * @author Rappie\n */\ncontract EchidnaDebug is EchidnaHelper {\n function debugOUSD() public pure {\n // assert(ousd.balanceOf(ADDRESS_USER0) == 1000);\n // assert(ousd.rebaseState(ADDRESS_USER0) != OUSD.RebaseOptions.OptIn);\n // assert(Address.isContract(ADDRESS_CONTRACT0));\n // Debugger.log(\"nonRebasingSupply\", ousd.nonRebasingSupply());\n // assert(false);\n }\n}\n" + }, + "contracts/echidna/EchidnaHelper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaSetup.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin containing helper functions\n * @author Rappie\n */\ncontract EchidnaHelper is EchidnaSetup {\n /**\n * @notice Mint tokens to an account\n * @param toAcc Account to mint to\n * @param amount Amount to mint\n * @return Amount minted (in case of capped mint with modulo)\n */\n function mint(uint8 toAcc, uint256 amount) public returns (uint256) {\n address to = getAccount(toAcc);\n\n if (TOGGLE_MINT_LIMIT) {\n amount = amount % MINT_MODULO;\n }\n\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(to, amount);\n\n return amount;\n }\n\n /**\n * @notice Burn tokens from an account\n * @param fromAcc Account to burn from\n * @param amount Amount to burn\n */\n function burn(uint8 fromAcc, uint256 amount) public {\n address from = getAccount(fromAcc);\n hevm.prank(ADDRESS_VAULT);\n ousd.burn(from, amount);\n }\n\n /**\n * @notice Change the total supply of OUSD (rebase)\n * @param amount New total supply\n */\n function changeSupply(uint256 amount) public {\n if (TOGGLE_CHANGESUPPLY_LIMIT) {\n amount =\n ousd.totalSupply() +\n (amount % (ousd.totalSupply() / CHANGESUPPLY_DIVISOR));\n }\n\n hevm.prank(ADDRESS_VAULT);\n ousd.changeSupply(amount);\n }\n\n /**\n * @notice Transfer tokens between accounts\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function transfer(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n ousd.transfer(to, amount);\n }\n\n /**\n * @notice Transfer approved tokens between accounts\n * @param authorizedAcc Account that is authorized to transfer\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function transferFrom(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n ousd.transferFrom(from, to, amount);\n }\n\n /**\n * @notice Opt in to rebasing\n * @param targetAcc Account to opt in\n */\n function optIn(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n hevm.prank(target);\n ousd.rebaseOptIn();\n }\n\n /**\n * @notice Opt out of rebasing\n * @param targetAcc Account to opt out\n */\n function optOut(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n hevm.prank(target);\n ousd.rebaseOptOut();\n }\n\n /**\n * @notice Approve an account to spend OUSD\n * @param ownerAcc Account that owns the OUSD\n * @param spenderAcc Account that is approved to spend the OUSD\n * @param amount Amount to approve\n */\n function approve(\n uint8 ownerAcc,\n uint8 spenderAcc,\n uint256 amount\n ) public {\n address owner = getAccount(ownerAcc);\n address spender = getAccount(spenderAcc);\n hevm.prank(owner);\n // slither-disable-next-line unused-return\n ousd.approve(spender, amount);\n }\n\n /**\n * @notice Get the sum of all OUSD balances\n * @return total Total balance\n */\n function getTotalBalance() public view returns (uint256 total) {\n total += ousd.balanceOf(ADDRESS_VAULT);\n total += ousd.balanceOf(ADDRESS_OUTSIDER_USER);\n total += ousd.balanceOf(ADDRESS_OUTSIDER_CONTRACT);\n total += ousd.balanceOf(ADDRESS_USER0);\n total += ousd.balanceOf(ADDRESS_USER1);\n total += ousd.balanceOf(ADDRESS_CONTRACT0);\n total += ousd.balanceOf(ADDRESS_CONTRACT1);\n }\n\n /**\n * @notice Get the sum of all non-rebasing OUSD balances\n * @return total Total balance\n */\n function getTotalNonRebasingBalance() public returns (uint256 total) {\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_VAULT)\n ? ousd.balanceOf(ADDRESS_VAULT)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_OUTSIDER_USER)\n ? ousd.balanceOf(ADDRESS_OUTSIDER_USER)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_OUTSIDER_CONTRACT)\n ? ousd.balanceOf(ADDRESS_OUTSIDER_CONTRACT)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_USER0)\n ? ousd.balanceOf(ADDRESS_USER0)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_USER1)\n ? ousd.balanceOf(ADDRESS_USER1)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_CONTRACT0)\n ? ousd.balanceOf(ADDRESS_CONTRACT0)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_CONTRACT1)\n ? ousd.balanceOf(ADDRESS_CONTRACT1)\n : 0;\n }\n}\n" + }, + "contracts/echidna/EchidnaSetup.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./IHevm.sol\";\nimport \"./EchidnaConfig.sol\";\nimport \"./OUSDEchidna.sol\";\n\ncontract Dummy {}\n\n/**\n * @title Mixin for setup and deployment\n * @author Rappie\n */\ncontract EchidnaSetup is EchidnaConfig {\n IHevm hevm = IHevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);\n OUSDEchidna ousd = new OUSDEchidna();\n\n /**\n * @notice Deploy the OUSD contract and set up initial state\n */\n constructor() {\n ousd.initialize(ADDRESS_VAULT, 1e18);\n\n // Deploy dummny contracts as users\n Dummy outsider = new Dummy();\n ADDRESS_OUTSIDER_CONTRACT = address(outsider);\n Dummy dummy0 = new Dummy();\n ADDRESS_CONTRACT0 = address(dummy0);\n Dummy dummy1 = new Dummy();\n ADDRESS_CONTRACT1 = address(dummy1);\n\n // Start out with a reasonable amount of OUSD\n if (TOGGLE_STARTING_BALANCE) {\n // Rebasing tokens\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(ADDRESS_OUTSIDER_USER, STARTING_BALANCE / 2);\n\n // Non-rebasing tokens\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(ADDRESS_OUTSIDER_CONTRACT, STARTING_BALANCE / 2);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestAccounting.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestSupply.sol\";\n\n/**\n * @title Mixin for testing accounting functions\n * @author Rappie\n */\ncontract EchidnaTestAccounting is EchidnaTestSupply {\n /**\n * @notice After opting in, balance should not increase. (Ok to lose rounding funds doing this)\n * @param targetAcc Account to opt in\n */\n function testOptInBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optIn(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter <= balanceBefore);\n }\n\n /**\n * @notice After opting out, balance should remain the same\n * @param targetAcc Account to opt out\n */\n function testOptOutBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optOut(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Account balance should remain the same after opting in minus rounding error\n * @param targetAcc Account to opt in\n */\n function testOptInBalanceRounding(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optIn(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n int256 delta = int256(balanceAfter) - int256(balanceBefore);\n Debugger.log(\"delta\", delta);\n\n // slither-disable-next-line tautology\n assert(-1 * delta >= 0);\n assert(-1 * delta <= int256(OPT_IN_ROUNDING_ERROR));\n }\n\n /**\n * @notice After opting in, total supply should remain the same\n * @param targetAcc Account to opt in\n */\n function testOptInTotalSupply(uint8 targetAcc) public {\n uint256 totalSupplyBefore = ousd.totalSupply();\n optIn(targetAcc);\n uint256 totalSupplyAfter = ousd.totalSupply();\n\n assert(totalSupplyAfter == totalSupplyBefore);\n }\n\n /**\n * @notice After opting out, total supply should remain the same\n * @param targetAcc Account to opt out\n */\n function testOptOutTotalSupply(uint8 targetAcc) public {\n uint256 totalSupplyBefore = ousd.totalSupply();\n optOut(targetAcc);\n uint256 totalSupplyAfter = ousd.totalSupply();\n\n assert(totalSupplyAfter == totalSupplyBefore);\n }\n\n /**\n * @notice Account balance should remain the same when a smart contract auto converts\n * @param targetAcc Account to auto convert\n */\n function testAutoConvertBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n // slither-disable-next-line unused-return\n ousd._isNonRebasingAccountEchidna(target);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice The `balanceOf` function should never revert\n * @param targetAcc Account to check balance of\n */\n function testBalanceOfShouldNotRevert(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n // slither-disable-next-line unused-return\n try ousd.balanceOf(target) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestApproval.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaTestMintBurn.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin for testing approval related functions\n * @author Rappie\n */\ncontract EchidnaTestApproval is EchidnaTestMintBurn {\n /**\n * @notice Performing `transferFrom` with an amount inside the allowance should not revert\n * @param authorizedAcc The account that is authorized to transfer\n * @param fromAcc The account that is transferring\n * @param toAcc The account that is receiving\n * @param amount The amount to transfer\n */\n function testTransferFromShouldNotRevert(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount <= ousd.balanceOf(from));\n require(amount <= ousd.allowance(from, authorized));\n\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n try ousd.transferFrom(from, to, amount) {\n // pass\n } catch {\n assert(false);\n }\n }\n\n /**\n * @notice Performing `transferFrom` with an amount outside the allowance should revert\n * @param authorizedAcc The account that is authorized to transfer\n * @param fromAcc The account that is transferring\n * @param toAcc The account that is receiving\n * @param amount The amount to transfer\n */\n function testTransferFromShouldRevert(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount > 0);\n require(\n !(amount <= ousd.balanceOf(from) &&\n amount <= ousd.allowance(from, authorized))\n );\n\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n try ousd.transferFrom(from, to, amount) {\n assert(false);\n } catch {\n // pass\n }\n }\n\n /**\n * @notice Approving an amount should update the allowance and overwrite any previous allowance\n * @param ownerAcc The account that is approving\n * @param spenderAcc The account that is being approved\n * @param amount The amount to approve\n */\n function testApprove(\n uint8 ownerAcc,\n uint8 spenderAcc,\n uint256 amount\n ) public {\n address owner = getAccount(ownerAcc);\n address spender = getAccount(spenderAcc);\n\n approve(ownerAcc, spenderAcc, amount);\n uint256 allowanceAfter1 = ousd.allowance(owner, spender);\n\n assert(allowanceAfter1 == amount);\n\n approve(ownerAcc, spenderAcc, amount / 2);\n uint256 allowanceAfter2 = ousd.allowance(owner, spender);\n\n assert(allowanceAfter2 == amount / 2);\n }\n}\n" + }, + "contracts/echidna/EchidnaTestMintBurn.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestAccounting.sol\";\n\n/**\n * @title Mixin for testing Mint and Burn functions\n * @author Rappie\n */\ncontract EchidnaTestMintBurn is EchidnaTestAccounting {\n /**\n * @notice Minting 0 tokens should not affect account balance\n * @param targetAcc Account to mint to\n */\n function testMintZeroBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n mint(targetAcc, 0);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Burning 0 tokens should not affect account balance\n * @param targetAcc Account to burn from\n */\n function testBurnZeroBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n burn(targetAcc, 0);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Minting tokens must increase the account balance by at least amount\n * @param targetAcc Account to mint to\n * @param amount Amount to mint\n * @custom:error testMintBalance(uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * testMintBalance(0,1)\n * Event sequence:\n * Debug(«balanceBefore», 0)\n * Debug(«balanceAfter», 0)\n */\n function testMintBalance(uint8 targetAcc, uint256 amount)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n uint256 amountMinted = mint(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n Debugger.log(\"amountMinted\", amountMinted);\n Debugger.log(\"balanceBefore\", balanceBefore);\n Debugger.log(\"balanceAfter\", balanceAfter);\n\n assert(balanceAfter >= balanceBefore + amountMinted);\n }\n\n /**\n * @notice Burning tokens must decrease the account balance by at least amount\n * @param targetAcc Account to burn from\n * @param amount Amount to burn\n * @custom:error testBurnBalance(uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * mint(0,3)\n * testBurnBalance(0,1)\n * Event sequence:\n * Debug(«balanceBefore», 2)\n * Debug(«balanceAfter», 2)\n */\n function testBurnBalance(uint8 targetAcc, uint256 amount)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n burn(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n Debugger.log(\"balanceBefore\", balanceBefore);\n Debugger.log(\"balanceAfter\", balanceAfter);\n\n assert(balanceAfter <= balanceBefore - amount);\n }\n\n /**\n * @notice Minting tokens should not increase the account balance by less than rounding error above amount\n * @param targetAcc Account to mint to\n * @param amount Amount to mint\n */\n function testMintBalanceRounding(uint8 targetAcc, uint256 amount) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n uint256 amountMinted = mint(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n int256 delta = int256(balanceAfter) - int256(balanceBefore);\n\n // delta == amount, if no error\n // delta < amount, if too little is minted\n // delta > amount, if too much is minted\n int256 error = int256(amountMinted) - delta;\n\n assert(error >= 0);\n assert(error <= int256(MINT_ROUNDING_ERROR));\n }\n\n /**\n * @notice A burn of an account balance must result in a zero balance\n * @param targetAcc Account to burn from\n */\n function testBurnAllBalanceToZero(uint8 targetAcc) public hasKnownIssue {\n address target = getAccount(targetAcc);\n\n burn(targetAcc, ousd.balanceOf(target));\n assert(ousd.balanceOf(target) == 0);\n }\n\n /**\n * @notice You should always be able to burn an account's balance\n * @param targetAcc Account to burn from\n */\n function testBurnAllBalanceShouldNotRevert(uint8 targetAcc)\n public\n hasKnownIssue\n {\n address target = getAccount(targetAcc);\n uint256 balance = ousd.balanceOf(target);\n\n hevm.prank(ADDRESS_VAULT);\n try ousd.burn(target, balance) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestSupply.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestTransfer.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n/**\n * @title Mixin for testing supply related functions\n * @author Rappie\n */\ncontract EchidnaTestSupply is EchidnaTestTransfer {\n using StableMath for uint256;\n\n uint256 prevRebasingCreditsPerToken = type(uint256).max;\n\n /**\n * @notice After a `changeSupply`, the total supply should exactly\n * match the target total supply. (This is needed to ensure successive\n * rebases are correct).\n * @param supply New total supply\n * @custom:error testChangeSupply(uint256): failed!💥\n * Call sequence:\n * testChangeSupply(1044505275072865171609)\n * Event sequence:\n * TotalSupplyUpdatedHighres(1044505275072865171610, 1000000000000000000000000, 957391048054055578595)\n */\n function testChangeSupply(uint256 supply)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n hevm.prank(ADDRESS_VAULT);\n ousd.changeSupply(supply);\n\n assert(ousd.totalSupply() == supply);\n }\n\n /**\n * @notice The total supply must not be less than the sum of account balances.\n * (The difference will go into future rebases)\n * @custom:error testTotalSupplyLessThanTotalBalance(): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(1)\n * optOut(64)\n * transfer(0,64,1)\n * testTotalSupplyLessThanTotalBalance()\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000001000001)\n * Debug(«totalBalance», 1000000000000000001000002)\n */\n function testTotalSupplyLessThanTotalBalance()\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n uint256 totalSupply = ousd.totalSupply();\n uint256 totalBalance = getTotalBalance();\n\n Debugger.log(\"totalSupply\", totalSupply);\n Debugger.log(\"totalBalance\", totalBalance);\n\n assert(totalSupply >= totalBalance);\n }\n\n /**\n * @notice Non-rebasing supply should not be larger than total supply\n * @custom:error testNonRebasingSupplyVsTotalSupply(): failed!💥\n * Call sequence:\n * mint(0,2)\n * changeSupply(3)\n * burn(0,1)\n * optOut(0)\n * testNonRebasingSupplyVsTotalSupply()\n */\n function testNonRebasingSupplyVsTotalSupply() public hasKnownIssue {\n uint256 nonRebasingSupply = ousd.nonRebasingSupply();\n uint256 totalSupply = ousd.totalSupply();\n\n assert(nonRebasingSupply <= totalSupply);\n }\n\n /**\n * @notice Global `rebasingCreditsPerToken` should never increase\n * @custom:error testRebasingCreditsPerTokenNotIncreased(): failed!💥\n * Call sequence:\n * testRebasingCreditsPerTokenNotIncreased()\n * changeSupply(1)\n * testRebasingCreditsPerTokenNotIncreased()\n */\n function testRebasingCreditsPerTokenNotIncreased() public hasKnownIssue {\n uint256 curRebasingCreditsPerToken = ousd\n .rebasingCreditsPerTokenHighres();\n\n Debugger.log(\n \"prevRebasingCreditsPerToken\",\n prevRebasingCreditsPerToken\n );\n Debugger.log(\"curRebasingCreditsPerToken\", curRebasingCreditsPerToken);\n\n assert(curRebasingCreditsPerToken <= prevRebasingCreditsPerToken);\n\n prevRebasingCreditsPerToken = curRebasingCreditsPerToken;\n }\n\n /**\n * @notice The rebasing credits per token ratio must greater than zero\n */\n function testRebasingCreditsPerTokenAboveZero() public {\n assert(ousd.rebasingCreditsPerTokenHighres() > 0);\n }\n\n /**\n * @notice The sum of all non-rebasing balances should not be larger than\n * non-rebasing supply\n * @custom:error testTotalNonRebasingSupplyLessThanTotalBalance(): failed!💥\n * Call sequence\n * mint(0,2)\n * changeSupply(1)\n * optOut(0)\n * burn(0,1)\n * testTotalNonRebasingSupplyLessThanTotalBalance()\n * Event sequence:\n * Debug(«totalNonRebasingSupply», 500000000000000000000001)\n * Debug(«totalNonRebasingBalance», 500000000000000000000002)\n */\n function testTotalNonRebasingSupplyLessThanTotalBalance()\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n uint256 totalNonRebasingSupply = ousd.nonRebasingSupply();\n uint256 totalNonRebasingBalance = getTotalNonRebasingBalance();\n\n Debugger.log(\"totalNonRebasingSupply\", totalNonRebasingSupply);\n Debugger.log(\"totalNonRebasingBalance\", totalNonRebasingBalance);\n\n assert(totalNonRebasingSupply >= totalNonRebasingBalance);\n }\n\n /**\n * @notice An accounts credits / credits per token should not be larger it's balance\n * @param targetAcc The account to check\n */\n function testCreditsPerTokenVsBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n (uint256 credits, uint256 creditsPerToken, ) = ousd\n .creditsBalanceOfHighres(target);\n uint256 expectedBalance = credits.divPrecisely(creditsPerToken);\n\n uint256 balance = ousd.balanceOf(target);\n\n Debugger.log(\"credits\", credits);\n Debugger.log(\"creditsPerToken\", creditsPerToken);\n Debugger.log(\"expectedBalance\", expectedBalance);\n Debugger.log(\"balance\", balance);\n\n assert(expectedBalance == balance);\n }\n}\n" + }, + "contracts/echidna/EchidnaTestTransfer.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin for testing transfer related functions\n * @author Rappie\n */\ncontract EchidnaTestTransfer is EchidnaDebug {\n /**\n * @notice The receiving account's balance after a transfer must not increase by\n * less than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferBalanceReceivedLess(uint8,uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * mint(64,2)\n * testTransferBalanceReceivedLess(64,0,1)\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000000500002)\n * Debug(«toBalBefore», 0)\n * Debug(«toBalAfter», 0)\n */\n function testTransferBalanceReceivedLess(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue hasKnownIssueWithinLimits {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n\n assert(toBalAfter >= toBalBefore + amount);\n }\n\n /**\n * @notice The receiving account's balance after a transfer must not\n * increase by more than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceReceivedMore(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n\n assert(toBalAfter <= toBalBefore + amount);\n }\n\n /**\n * @notice The sending account's balance after a transfer must not\n * decrease by less than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferBalanceSentLess(uint8,uint8,uint256): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(1)\n * testTransferBalanceSentLess(0,64,1)\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000000500001)\n * Debug(«fromBalBefore», 1)\n * Debug(«fromBalAfter», 1)\n */\n function testTransferBalanceSentLess(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue hasKnownIssueWithinLimits {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n\n assert(fromBalAfter <= fromBalBefore - amount);\n }\n\n /**\n * @notice The sending account's balance after a transfer must not\n * decrease by more than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceSentMore(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n\n assert(fromBalAfter >= fromBalBefore - amount);\n }\n\n /**\n * @notice The receiving account's balance after a transfer must not\n * increase by less than the amount transferred (minus rounding error)\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceReceivedLessRounding(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n int256 toDelta = int256(toBalAfter) - int256(toBalBefore);\n\n // delta == amount, if no error\n // delta < amount, if too little is sent\n // delta > amount, if too much is sent\n int256 error = int256(amount) - toDelta;\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n Debugger.log(\"toDelta\", toDelta);\n Debugger.log(\"error\", error);\n\n assert(error >= 0);\n assert(error <= int256(TRANSFER_ROUNDING_ERROR));\n }\n\n /**\n * @notice The sending account's balance after a transfer must\n * not decrease by less than the amount transferred (minus rounding error)\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceSentLessRounding(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n int256 fromDelta = int256(fromBalAfter) - int256(fromBalBefore);\n\n // delta == -amount, if no error\n // delta < -amount, if too much is sent\n // delta > -amount, if too little is sent\n int256 error = int256(amount) + fromDelta;\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n Debugger.log(\"fromDelta\", fromDelta);\n Debugger.log(\"error\", error);\n\n assert(error >= 0);\n assert(error <= int256(TRANSFER_ROUNDING_ERROR));\n }\n\n /**\n * @notice An account should always be able to successfully transfer\n * an amount within its balance.\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferWithinBalanceDoesNotRevert(uint8,uint8,uint8): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(3)\n * optOut(0)\n * testTransferWithinBalanceDoesNotRevert(0,128,2)\n * optIn(0)\n * testTransferWithinBalanceDoesNotRevert(128,0,1)\n * Event sequence:\n * error Revert Panic(17): SafeMath over-/under-flows\n */\n function testTransferWithinBalanceDoesNotRevert(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount > 0);\n amount = amount % ousd.balanceOf(from);\n\n Debugger.log(\"Total supply\", ousd.totalSupply());\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(to, amount) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n\n /**\n * @notice An account should never be able to successfully transfer\n * an amount greater than their balance.\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferExceedingBalanceReverts(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n amount = ousd.balanceOf(from) + 1 + amount;\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(to, amount) {\n assert(false);\n } catch {\n assert(true);\n }\n }\n\n /**\n * @notice A transfer to the same account should not change that account's balance\n * @param targetAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferSelf(uint8 targetAcc, uint256 amount) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n transfer(targetAcc, targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceBefore == balanceAfter);\n }\n\n /**\n * @notice Transfers to the zero account revert\n * @param fromAcc Account to transfer from\n * @param amount Amount to transfer\n */\n function testTransferToZeroAddress(uint8 fromAcc, uint256 amount) public {\n address from = getAccount(fromAcc);\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(address(0), amount) {\n assert(false);\n } catch {\n assert(true);\n }\n }\n}\n" + }, + "contracts/echidna/IHevm.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// https://github.com/ethereum/hevm/blob/main/doc/src/controlling-the-unit-testing-environment.md#cheat-codes\n\ninterface IHevm {\n function warp(uint256 x) external;\n\n function roll(uint256 x) external;\n\n function store(\n address c,\n bytes32 loc,\n bytes32 val\n ) external;\n\n function load(address c, bytes32 loc) external returns (bytes32 val);\n\n function sign(uint256 sk, bytes32 digest)\n external\n returns (\n uint8 v,\n bytes32 r,\n bytes32 s\n );\n\n function addr(uint256 sk) external returns (address addr);\n\n function ffi(string[] calldata) external returns (bytes memory);\n\n function prank(address sender) external;\n}\n" + }, + "contracts/echidna/OUSDEchidna.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../token/OUSD.sol\";\n\ncontract OUSDEchidna is OUSD {\n constructor() OUSD() {}\n\n function _isNonRebasingAccountEchidna(address _account)\n public\n returns (bool)\n {\n _autoMigrate(_account);\n return alternativeCreditsPerToken[_account] > 0;\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/governance/InitializableGovernable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD InitializableGovernable Contract\n * @author Origin Protocol Inc\n */\nimport { Initializable } from \"../utils/Initializable.sol\";\n\nimport { Governable } from \"./Governable.sol\";\n\ncontract InitializableGovernable is Governable, Initializable {\n function _initialize(address _newGovernor) internal {\n _changeGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/governance/Strategizable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"./Governable.sol\";\n\ncontract Strategizable is Governable {\n event StrategistUpdated(address _address);\n\n // Address of strategist\n address public strategistAddr;\n\n // For future use\n uint256[50] private __gap;\n\n /**\n * @dev Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @dev Set address of Strategist\n * @param _address Address of Strategist\n */\n function setStrategistAddr(address _address) external onlyGovernor {\n _setStrategistAddr(_address);\n }\n\n /**\n * @dev Set address of Strategist\n * @param _address Address of Strategist\n */\n function _setStrategistAddr(address _address) internal {\n strategistAddr = _address;\n emit StrategistUpdated(_address);\n }\n}\n" + }, + "contracts/harvest/AbstractHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IUniswapV2Router } from \"../interfaces/uniswap/IUniswapV2Router02.sol\";\nimport { IUniswapV3Router } from \"../interfaces/uniswap/IUniswapV3Router.sol\";\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { ICurvePool } from \"../strategies/ICurvePool.sol\";\nimport \"../utils/Helpers.sol\";\n\nabstract contract AbstractHarvester is Governable {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using StableMath for uint256;\n\n enum SwapPlatform {\n UniswapV2Compatible,\n UniswapV3,\n Balancer,\n Curve\n }\n\n event SupportedStrategyUpdate(address strategyAddress, bool isSupported);\n event RewardTokenConfigUpdated(\n address tokenAddress,\n uint16 allowedSlippageBps,\n uint16 harvestRewardBps,\n SwapPlatform swapPlatform,\n address swapPlatformAddr,\n bytes swapData,\n uint256 liquidationLimit,\n bool doSwapRewardToken\n );\n event RewardTokenSwapped(\n address indexed rewardToken,\n address indexed swappedInto,\n SwapPlatform swapPlatform,\n uint256 amountIn,\n uint256 amountOut\n );\n event RewardProceedsTransferred(\n address indexed token,\n address farmer,\n uint256 protcolYield,\n uint256 farmerFee\n );\n event RewardProceedsAddressChanged(address newProceedsAddress);\n\n error EmptyAddress();\n error InvalidSlippageBps();\n error InvalidHarvestRewardBps();\n\n error InvalidSwapPlatform(SwapPlatform swapPlatform);\n\n error InvalidUniswapV2PathLength();\n error InvalidTokenInSwapPath(address token);\n error EmptyBalancerPoolId();\n error InvalidCurvePoolAssetIndex(address token);\n\n error UnsupportedStrategy(address strategyAddress);\n\n error SlippageError(uint256 actualBalance, uint256 minExpected);\n error BalanceMismatchAfterSwap(uint256 actualBalance, uint256 minExpected);\n\n // Configuration properties for harvesting logic of reward tokens\n struct RewardTokenConfig {\n // Max allowed slippage when swapping reward token for a stablecoin denominated in basis points.\n uint16 allowedSlippageBps;\n // Reward when calling a harvest function denominated in basis points.\n uint16 harvestRewardBps;\n // Address of compatible exchange protocol (Uniswap V2/V3, SushiSwap, Balancer and Curve).\n address swapPlatformAddr;\n /* When true the reward token is being swapped. In a need of (temporarily) disabling the swapping of\n * a reward token this needs to be set to false.\n */\n bool doSwapRewardToken;\n // Platform to use for Swapping\n SwapPlatform swapPlatform;\n /* How much token can be sold per one harvest call. If the balance of rewards tokens\n * exceeds that limit multiple harvest calls are required to harvest all of the tokens.\n * Set it to MAX_INT to effectively disable the limit.\n */\n uint256 liquidationLimit;\n }\n\n mapping(address => RewardTokenConfig) public rewardTokenConfigs;\n mapping(address => bool) public supportedStrategies;\n\n address public immutable vaultAddress;\n\n /**\n * Address receiving rewards proceeds. Initially the Vault contract later will possibly\n * be replaced by another contract that eases out rewards distribution.\n **/\n address public rewardProceedsAddress;\n\n /**\n * All tokens are swapped to this token before it gets transferred\n * to the `rewardProceedsAddress`. USDT for OUSD and WETH for OETH.\n **/\n address public immutable baseTokenAddress;\n // Cached decimals for `baseTokenAddress`\n uint256 public immutable baseTokenDecimals;\n\n // Uniswap V2 path for reward tokens using Uniswap V2 Router\n mapping(address => address[]) public uniswapV2Path;\n // Uniswap V3 path for reward tokens using Uniswap V3 Router\n mapping(address => bytes) public uniswapV3Path;\n // Pool ID to use for reward tokens on Balancer\n mapping(address => bytes32) public balancerPoolId;\n\n struct CurvePoolIndices {\n // Casted into uint128 and stored in a struct to save gas\n uint128 rewardTokenIndex;\n uint128 baseTokenIndex;\n }\n // Packed indices of assets on the Curve pool\n mapping(address => CurvePoolIndices) public curvePoolIndices;\n\n constructor(address _vaultAddress, address _baseTokenAddress) {\n require(_vaultAddress != address(0));\n require(_baseTokenAddress != address(0));\n\n vaultAddress = _vaultAddress;\n baseTokenAddress = _baseTokenAddress;\n\n // Cache decimals as well\n baseTokenDecimals = Helpers.getDecimals(_baseTokenAddress);\n }\n\n /***************************************\n Configuration\n ****************************************/\n\n /**\n * Set the Address receiving rewards proceeds.\n * @param _rewardProceedsAddress Address of the reward token\n */\n function setRewardProceedsAddress(address _rewardProceedsAddress)\n external\n onlyGovernor\n {\n if (_rewardProceedsAddress == address(0)) {\n revert EmptyAddress();\n }\n\n rewardProceedsAddress = _rewardProceedsAddress;\n emit RewardProceedsAddressChanged(_rewardProceedsAddress);\n }\n\n /**\n * @dev Add/update a reward token configuration that holds harvesting config variables\n * @param _tokenAddress Address of the reward token\n * @param tokenConfig.allowedSlippageBps uint16 maximum allowed slippage denominated in basis points.\n * Example: 300 == 3% slippage\n * @param tokenConfig.harvestRewardBps uint16 amount of reward tokens the caller of the function is rewarded.\n * Example: 100 == 1%\n * @param tokenConfig.swapPlatformAddr Address Address of a UniswapV2 compatible contract to perform\n * the exchange from reward tokens to stablecoin (currently hard-coded to USDT)\n * @param tokenConfig.liquidationLimit uint256 Maximum amount of token to be sold per one swap function call.\n * When value is 0 there is no limit.\n * @param tokenConfig.doSwapRewardToken bool Disables swapping of the token when set to true,\n * does not cause it to revert though.\n * @param tokenConfig.swapPlatform SwapPlatform to use for Swapping\n * @param swapData Additional data required for swapping\n */\n function setRewardTokenConfig(\n address _tokenAddress,\n RewardTokenConfig calldata tokenConfig,\n bytes calldata swapData\n ) external onlyGovernor {\n if (tokenConfig.allowedSlippageBps > 1000) {\n revert InvalidSlippageBps();\n }\n\n if (tokenConfig.harvestRewardBps > 1000) {\n revert InvalidHarvestRewardBps();\n }\n\n address newRouterAddress = tokenConfig.swapPlatformAddr;\n if (newRouterAddress == address(0)) {\n // Swap router address should be non zero address\n revert EmptyAddress();\n }\n\n address oldRouterAddress = rewardTokenConfigs[_tokenAddress]\n .swapPlatformAddr;\n rewardTokenConfigs[_tokenAddress] = tokenConfig;\n\n // Revert if feed does not exist\n // slither-disable-next-line unused-return\n IOracle(IVault(vaultAddress).priceProvider()).price(_tokenAddress);\n\n IERC20 token = IERC20(_tokenAddress);\n // if changing token swap provider cancel existing allowance\n if (\n /* oldRouterAddress == address(0) when there is no pre-existing\n * configuration for said rewards token\n */\n oldRouterAddress != address(0) &&\n oldRouterAddress != newRouterAddress\n ) {\n token.safeApprove(oldRouterAddress, 0);\n }\n\n // Give SwapRouter infinite approval when needed\n if (oldRouterAddress != newRouterAddress) {\n token.safeApprove(newRouterAddress, 0);\n token.safeApprove(newRouterAddress, type(uint256).max);\n }\n\n SwapPlatform _platform = tokenConfig.swapPlatform;\n if (_platform == SwapPlatform.UniswapV2Compatible) {\n uniswapV2Path[_tokenAddress] = _decodeUniswapV2Path(\n swapData,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.UniswapV3) {\n uniswapV3Path[_tokenAddress] = _decodeUniswapV3Path(\n swapData,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.Balancer) {\n balancerPoolId[_tokenAddress] = _decodeBalancerPoolId(\n swapData,\n newRouterAddress,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.Curve) {\n curvePoolIndices[_tokenAddress] = _decodeCurvePoolIndices(\n swapData,\n newRouterAddress,\n _tokenAddress\n );\n } else {\n // Note: This code is unreachable since Solidity reverts when\n // the value is outside the range of defined values of the enum\n // (even if it's under the max length of the base type)\n revert InvalidSwapPlatform(_platform);\n }\n\n emit RewardTokenConfigUpdated(\n _tokenAddress,\n tokenConfig.allowedSlippageBps,\n tokenConfig.harvestRewardBps,\n _platform,\n newRouterAddress,\n swapData,\n tokenConfig.liquidationLimit,\n tokenConfig.doSwapRewardToken\n );\n }\n\n /**\n * @dev Decodes the data passed into Uniswap V2 path and validates\n * it to make sure the path is for `token` to `baseToken`\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param token The address of the reward token\n * @return path The validated Uniswap V2 path\n */\n function _decodeUniswapV2Path(bytes calldata data, address token)\n internal\n view\n returns (address[] memory path)\n {\n (path) = abi.decode(data, (address[]));\n uint256 len = path.length;\n\n if (len < 2) {\n // Path should have at least two tokens\n revert InvalidUniswapV2PathLength();\n }\n\n // Do some validation\n if (path[0] != token) {\n revert InvalidTokenInSwapPath(path[0]);\n }\n\n if (path[len - 1] != baseTokenAddress) {\n revert InvalidTokenInSwapPath(path[len - 1]);\n }\n }\n\n /**\n * @dev Decodes the data passed into Uniswap V3 path and validates\n * it to make sure the path is for `token` to `baseToken`\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param token The address of the reward token\n * @return path The validated Uniswap V3 path\n */\n function _decodeUniswapV3Path(bytes calldata data, address token)\n internal\n view\n returns (bytes calldata path)\n {\n path = data;\n\n address decodedAddress = address(uint160(bytes20(data[0:20])));\n\n if (decodedAddress != token) {\n // Invalid Reward Token in swap path\n revert InvalidTokenInSwapPath(decodedAddress);\n }\n\n decodedAddress = address(uint160(bytes20(data[path.length - 20:])));\n if (decodedAddress != baseTokenAddress) {\n // Invalid Base Token in swap path\n revert InvalidTokenInSwapPath(decodedAddress);\n }\n }\n\n /**\n * @dev Decodes the data passed to Balancer Pool ID\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @return poolId The pool ID\n */\n function _decodeBalancerPoolId(\n bytes calldata data,\n address balancerVault,\n address token\n ) internal view returns (bytes32 poolId) {\n (poolId) = abi.decode(data, (bytes32));\n\n if (poolId == bytes32(0)) {\n revert EmptyBalancerPoolId();\n }\n\n IBalancerVault bVault = IBalancerVault(balancerVault);\n\n // Note: this reverts if token is not a pool asset\n // slither-disable-next-line unused-return\n bVault.getPoolTokenInfo(poolId, token);\n\n // slither-disable-next-line unused-return\n bVault.getPoolTokenInfo(poolId, baseTokenAddress);\n }\n\n /**\n * @dev Decodes the data passed to get the pool indices and\n * checks it against the Curve Pool to make sure it's\n * not misconfigured. The indices are packed into a single\n * uint256 for gas savings\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param poolAddress Curve pool address\n * @param token The address of the reward token\n * @return indices Packed pool asset indices\n */\n function _decodeCurvePoolIndices(\n bytes calldata data,\n address poolAddress,\n address token\n ) internal view returns (CurvePoolIndices memory indices) {\n indices = abi.decode(data, (CurvePoolIndices));\n\n ICurvePool pool = ICurvePool(poolAddress);\n if (token != pool.coins(indices.rewardTokenIndex)) {\n revert InvalidCurvePoolAssetIndex(token);\n }\n if (baseTokenAddress != pool.coins(indices.baseTokenIndex)) {\n revert InvalidCurvePoolAssetIndex(baseTokenAddress);\n }\n }\n\n /**\n * @dev Flags a strategy as supported or not supported one\n * @param _strategyAddress Address of the strategy\n * @param _isSupported Bool marking strategy as supported or not supported\n */\n function setSupportedStrategy(address _strategyAddress, bool _isSupported)\n external\n onlyGovernor\n {\n supportedStrategies[_strategyAddress] = _isSupported;\n emit SupportedStrategyUpdate(_strategyAddress, _isSupported);\n }\n\n /***************************************\n Rewards\n ****************************************/\n\n /**\n * @dev Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform. Can be called by anyone.\n * Rewards incentivizing the caller are sent to the caller of this function.\n * @param _strategyAddr Address of the strategy to collect rewards from\n */\n function harvestAndSwap(address _strategyAddr) external nonReentrant {\n // Remember _harvest function checks for the validity of _strategyAddr\n _harvestAndSwap(_strategyAddr, msg.sender);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform. Can be called by anyone\n * @param _strategyAddr Address of the strategy to collect rewards from\n * @param _rewardTo Address where to send a share of harvest rewards to as an incentive\n * for executing this function\n */\n function harvestAndSwap(address _strategyAddr, address _rewardTo)\n external\n nonReentrant\n {\n // Remember _harvest function checks for the validity of _strategyAddr\n _harvestAndSwap(_strategyAddr, _rewardTo);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform\n * @param _strategyAddr Address of the strategy to collect rewards from\n * @param _rewardTo Address where to send a share of harvest rewards to as an incentive\n * for executing this function\n */\n function _harvestAndSwap(address _strategyAddr, address _rewardTo)\n internal\n {\n _harvest(_strategyAddr);\n IStrategy strategy = IStrategy(_strategyAddr);\n address[] memory rewardTokens = strategy.getRewardTokenAddresses();\n IOracle priceProvider = IOracle(IVault(vaultAddress).priceProvider());\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; ++i) {\n _swap(rewardTokens[i], _rewardTo, priceProvider);\n }\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform\n * @param _strategyAddr Address of the strategy to collect rewards from.\n */\n function _harvest(address _strategyAddr) internal virtual {\n if (!supportedStrategies[_strategyAddr]) {\n revert UnsupportedStrategy(_strategyAddr);\n }\n\n IStrategy strategy = IStrategy(_strategyAddr);\n strategy.collectRewardTokens();\n }\n\n /**\n * @dev Swap a reward token for the base token on the configured\n * swap platform. The token must have a registered price feed\n * with the price provider\n * @param _swapToken Address of the token to swap\n * @param _rewardTo Address where to send the share of harvest rewards to\n * @param _priceProvider Oracle to get prices of the swap token\n */\n function _swap(\n address _swapToken,\n address _rewardTo,\n IOracle _priceProvider\n ) internal virtual {\n uint256 balance = IERC20(_swapToken).balanceOf(address(this));\n\n // No need to swap if the reward token is the base token. eg USDT or WETH.\n // There is also no limit on the transfer. Everything in the harvester will be transferred\n // to the Dripper regardless of the liquidationLimit config.\n if (_swapToken == baseTokenAddress) {\n IERC20(_swapToken).safeTransfer(rewardProceedsAddress, balance);\n // currently not paying the farmer any rewards as there is no swap\n emit RewardProceedsTransferred(\n baseTokenAddress,\n address(0),\n balance,\n 0\n );\n return;\n }\n\n RewardTokenConfig memory tokenConfig = rewardTokenConfigs[_swapToken];\n\n /* This will trigger a return when reward token configuration has not yet been set\n * or we have temporarily disabled swapping of specific reward token via setting\n * doSwapRewardToken to false.\n */\n if (!tokenConfig.doSwapRewardToken) {\n return;\n }\n\n if (balance == 0) {\n return;\n }\n\n if (tokenConfig.liquidationLimit > 0) {\n balance = Math.min(balance, tokenConfig.liquidationLimit);\n }\n\n // This'll revert if there is no price feed\n uint256 oraclePrice = _priceProvider.price(_swapToken);\n\n // Oracle price is 1e18\n uint256 minExpected = (balance *\n (1e4 - tokenConfig.allowedSlippageBps) * // max allowed slippage\n oraclePrice).scaleBy(\n baseTokenDecimals,\n Helpers.getDecimals(_swapToken)\n ) /\n 1e4 / // fix the max slippage decimal position\n 1e18; // and oracle price decimals position\n\n // Do the swap\n uint256 amountReceived = _doSwap(\n tokenConfig.swapPlatform,\n tokenConfig.swapPlatformAddr,\n _swapToken,\n balance,\n minExpected\n );\n\n if (amountReceived < minExpected) {\n revert SlippageError(amountReceived, minExpected);\n }\n\n emit RewardTokenSwapped(\n _swapToken,\n baseTokenAddress,\n tokenConfig.swapPlatform,\n balance,\n amountReceived\n );\n\n IERC20 baseToken = IERC20(baseTokenAddress);\n uint256 baseTokenBalance = baseToken.balanceOf(address(this));\n if (baseTokenBalance < amountReceived) {\n // Note: It's possible to bypass this check by transferring `baseToken`\n // directly to Harvester before calling the `harvestAndSwap`. However,\n // there's no incentive for an attacker to do that. Doing a balance diff\n // will increase the gas cost significantly\n revert BalanceMismatchAfterSwap(baseTokenBalance, amountReceived);\n }\n\n // Farmer only gets fee from the base amount they helped farm,\n // They do not get anything from anything that already was there\n // on the Harvester\n uint256 farmerFee = amountReceived.mulTruncateScale(\n tokenConfig.harvestRewardBps,\n 1e4\n );\n uint256 protocolYield = baseTokenBalance - farmerFee;\n\n baseToken.safeTransfer(rewardProceedsAddress, protocolYield);\n baseToken.safeTransfer(_rewardTo, farmerFee);\n emit RewardProceedsTransferred(\n baseTokenAddress,\n _rewardTo,\n protocolYield,\n farmerFee\n );\n }\n\n function _doSwap(\n SwapPlatform swapPlatform,\n address routerAddress,\n address rewardTokenAddress,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n if (swapPlatform == SwapPlatform.UniswapV2Compatible) {\n return\n _swapWithUniswapV2(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.UniswapV3) {\n return\n _swapWithUniswapV3(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.Balancer) {\n return\n _swapWithBalancer(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.Curve) {\n return\n _swapWithCurve(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else {\n // Should never be invoked since we catch invalid values\n // in the `setRewardTokenConfig` function before it's set\n revert InvalidSwapPlatform(swapPlatform);\n }\n }\n\n /**\n * @dev Swaps the token to `baseToken` with Uniswap V2\n *\n * @param routerAddress Uniswap V2 Router address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithUniswapV2(\n address routerAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n address[] memory path = uniswapV2Path[swapToken];\n\n uint256[] memory amounts = IUniswapV2Router(routerAddress)\n .swapExactTokensForTokens(\n amountIn,\n minAmountOut,\n path,\n address(this),\n block.timestamp\n );\n\n amountOut = amounts[amounts.length - 1];\n }\n\n /**\n * @dev Swaps the token to `baseToken` with Uniswap V3\n *\n * @param routerAddress Uniswap V3 Router address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithUniswapV3(\n address routerAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n bytes memory path = uniswapV3Path[swapToken];\n\n IUniswapV3Router.ExactInputParams memory params = IUniswapV3Router\n .ExactInputParams({\n path: path,\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: amountIn,\n amountOutMinimum: minAmountOut\n });\n amountOut = IUniswapV3Router(routerAddress).exactInput(params);\n }\n\n /**\n * @dev Swaps the token to `baseToken` on Balancer\n *\n * @param balancerVaultAddress BalancerVaultAddress\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithBalancer(\n address balancerVaultAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n bytes32 poolId = balancerPoolId[swapToken];\n\n IBalancerVault.SingleSwap memory singleSwap = IBalancerVault\n .SingleSwap({\n poolId: poolId,\n kind: IBalancerVault.SwapKind.GIVEN_IN,\n assetIn: swapToken,\n assetOut: baseTokenAddress,\n amount: amountIn,\n userData: hex\"\"\n });\n\n IBalancerVault.FundManagement memory fundMgmt = IBalancerVault\n .FundManagement({\n sender: address(this),\n fromInternalBalance: false,\n recipient: payable(address(this)),\n toInternalBalance: false\n });\n\n amountOut = IBalancerVault(balancerVaultAddress).swap(\n singleSwap,\n fundMgmt,\n minAmountOut,\n block.timestamp\n );\n }\n\n /**\n * @dev Swaps the token to `baseToken` on Curve\n *\n * @param poolAddress Curve Pool Address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithCurve(\n address poolAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n CurvePoolIndices memory indices = curvePoolIndices[swapToken];\n\n // Note: Not all CurvePools return the `amountOut`, make sure\n // to use only pool that do. Otherwise the swap would revert\n // always\n amountOut = ICurvePool(poolAddress).exchange(\n uint256(indices.rewardTokenIndex),\n uint256(indices.baseTokenIndex),\n amountIn,\n minAmountOut\n );\n }\n}\n" + }, + "contracts/harvest/Dripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\n/**\n * @title OUSD Dripper\n *\n * The dripper contract smooths out the yield from point-in-time yield events\n * and spreads the yield out over a configurable time period. This ensures a\n * continuous per block yield to makes users happy as their next rebase\n * amount is always moving up. Also, this makes historical day to day yields\n * smooth, rather than going from a near zero day, to a large APY day, then\n * back to a near zero day again.\n *\n *\n * Design notes\n * - USDT has a smaller resolution than the number of seconds\n * in a week, which can make per second payouts have a rounding error. However\n * the total effect is not large - cents per day, and this money is\n * not lost, just distributed in the future. While we could use a higher\n * decimal precision for the drip perSecond, we chose simpler code.\n * - By calculating the changing drip rates on collects only, harvests and yield\n * events don't have to call anything on this contract or pay any extra gas.\n * Collect() is already be paying for a single write, since it has to reset\n * the lastCollect time.\n * - By having a collectAndRebase method, and having our external systems call\n * that, the OUSD vault does not need any changes, not even to know the address\n * of the dripper.\n * - A rejected design was to retro-calculate the drip rate on each collect,\n * based on the balance at the time of the collect. While this would have\n * required less state, and would also have made the contract respond more quickly\n * to new income, it would break the predictability that is this contract's entire\n * purpose. If we did this, the amount of fundsAvailable() would make sharp increases\n * when funds were deposited.\n * - When the dripper recalculates the rate, it targets spending the balance over\n * the duration. This means that every time that collect is called, if no\n * new funds have been deposited the duration is being pushed back and the\n * rate decreases. This is expected, and ends up following a smoother but\n * longer curve the more collect() is called without incoming yield.\n *\n */\n\ncontract Dripper is Governable {\n using SafeERC20 for IERC20;\n\n struct Drip {\n uint64 lastCollect; // overflows 262 billion years after the sun dies\n uint192 perSecond; // drip rate per second\n }\n\n address immutable vault; // OUSD vault\n address immutable token; // token to drip out\n uint256 public dripDuration; // in seconds\n Drip public drip; // active drip parameters\n\n constructor(address _vault, address _token) {\n vault = _vault;\n token = _token;\n }\n\n /// @notice How much funds have dripped out already and are currently\n // available to be sent to the vault.\n /// @return The amount that would be sent if a collect was called\n function availableFunds() external view returns (uint256) {\n uint256 balance = IERC20(token).balanceOf(address(this));\n return _availableFunds(balance, drip);\n }\n\n /// @notice Collect all dripped funds and send to vault.\n /// Recalculate new drip rate.\n function collect() external {\n _collect();\n }\n\n /// @notice Collect all dripped funds, send to vault, recalculate new drip\n /// rate, and rebase OUSD.\n function collectAndRebase() external {\n _collect();\n IVault(vault).rebase();\n }\n\n /// @dev Change the drip duration. Governor only.\n /// @param _durationSeconds the number of seconds to drip out the entire\n /// balance over if no collects were called during that time.\n function setDripDuration(uint256 _durationSeconds)\n external\n virtual\n onlyGovernor\n {\n require(_durationSeconds > 0, \"duration must be non-zero\");\n dripDuration = _durationSeconds;\n _collect(); // duration change take immediate effect\n }\n\n /// @dev Transfer out ERC20 tokens held by the contract. Governor only.\n /// @param _asset ERC20 token address\n /// @param _amount amount to transfer\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /// @dev Calculate available funds by taking the lower of either the\n /// currently dripped out funds or the balance available.\n /// Uses passed in parameters to calculate with for gas savings.\n /// @param _balance current balance in contract\n /// @param _drip current drip parameters\n function _availableFunds(uint256 _balance, Drip memory _drip)\n internal\n view\n returns (uint256)\n {\n uint256 elapsed = block.timestamp - _drip.lastCollect;\n uint256 allowed = (elapsed * _drip.perSecond);\n return (allowed > _balance) ? _balance : allowed;\n }\n\n /// @dev Sends the currently dripped funds to be vault, and sets\n /// the new drip rate based on the new balance.\n function _collect() internal virtual {\n // Calculate send\n uint256 balance = IERC20(token).balanceOf(address(this));\n uint256 amountToSend = _availableFunds(balance, drip);\n uint256 remaining = balance - amountToSend;\n // Calculate new drip perSecond\n // Gas savings by setting entire struct at one time\n drip = Drip({\n perSecond: uint192(remaining / dripDuration),\n lastCollect: uint64(block.timestamp)\n });\n // Send funds\n IERC20(token).safeTransfer(vault, amountToSend);\n }\n\n /// @dev Transfer out all ERC20 held by the contract. Governor only.\n /// @param _asset ERC20 token address\n function transferAllToken(address _asset, address _receiver)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(\n _receiver,\n IERC20(_asset).balanceOf(address(this))\n );\n }\n}\n" + }, + "contracts/harvest/FixedRateDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Dripper } from \"./Dripper.sol\";\n\n/**\n * @title Fixed Rate Dripper\n *\n * Similar to the Dripper, Fixed Rate Dripper drips out yield per second.\n * However the Strategist decides the rate and it doesn't change after\n * a drip.\n *\n */\n\ncontract FixedRateDripper is Dripper {\n using SafeERC20 for IERC20;\n\n event DripRateUpdated(uint192 oldDripRate, uint192 newDripRate);\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vault).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n constructor(address _vault, address _token) Dripper(_vault, _token) {}\n\n /// @inheritdoc Dripper\n function setDripDuration(uint256) external virtual override {\n // Not used in FixedRateDripper\n revert(\"Drip duration disabled\");\n }\n\n /// @inheritdoc Dripper\n function _collect() internal virtual override {\n // Calculate amount to send\n uint256 balance = IERC20(token).balanceOf(address(this));\n uint256 amountToSend = _availableFunds(balance, drip);\n\n // Update timestamp\n drip.lastCollect = uint64(block.timestamp);\n\n // Send funds\n IERC20(token).safeTransfer(vault, amountToSend);\n }\n\n /**\n * @dev Sets the drip rate. Callable by Strategist or Governor.\n * Can be set to zero to stop dripper.\n * @param _perSecond Rate of WETH to drip per second\n */\n function setDripRate(uint192 _perSecond) external onlyGovernorOrStrategist {\n emit DripRateUpdated(_perSecond, drip.perSecond);\n\n /**\n * Note: It's important to call `_collect` before updating\n * the drip rate especially on a new proxy contract.\n * When `lastCollect` is not set/initialized, the elapsed\n * time would be calculated as `block.number` seconds,\n * resulting in a huge yield, if `collect` isn't called first.\n */\n // Collect at existing rate\n _collect();\n\n // Update rate\n drip.perSecond = _perSecond;\n }\n}\n" + }, + "contracts/harvest/Harvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractHarvester } from \"./AbstractHarvester.sol\";\n\ncontract Harvester is AbstractHarvester {\n constructor(address _vault, address _usdtAddress)\n AbstractHarvester(_vault, _usdtAddress)\n {}\n}\n" + }, + "contracts/harvest/OETHBaseHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { ISwapRouter } from \"../interfaces/aerodrome/ISwapRouter.sol\";\n\ncontract OETHBaseHarvester is Governable {\n using SafeERC20 for IERC20;\n\n IVault public immutable vault;\n IStrategy public immutable amoStrategy;\n IERC20 public immutable aero;\n IERC20 public immutable weth;\n ISwapRouter public immutable swapRouter;\n\n address public operatorAddr;\n\n // Similar sig to `AbstractHarvester.RewardTokenSwapped` for\n // future compatibility with monitoring\n event RewardTokenSwapped(\n address indexed rewardToken,\n address indexed swappedInto,\n uint8 swapPlatform,\n uint256 amountIn,\n uint256 amountOut\n );\n\n event OperatorChanged(address oldOperator, address newOperator);\n event YieldSent(address recipient, uint256 yield, uint256 fee);\n\n /**\n * @notice Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == vault.strategistAddr() || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @notice Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategistOrOperator() {\n require(\n msg.sender == operatorAddr ||\n msg.sender == vault.strategistAddr() ||\n isGovernor(),\n \"Caller is not the Operator or Strategist or Governor\"\n );\n _;\n }\n\n constructor(\n address _vault,\n address _amoStrategy,\n address _aero,\n address _weth,\n address _swapRouter\n ) {\n vault = IVault(_vault);\n amoStrategy = IStrategy(_amoStrategy);\n aero = IERC20(_aero);\n weth = IERC20(_weth);\n swapRouter = ISwapRouter(_swapRouter);\n }\n\n /**\n * @dev Changes the operator address which can call `harvest`\n * @param _operatorAddr New operator address\n */\n function setOperatorAddr(address _operatorAddr) external onlyGovernor {\n emit OperatorChanged(operatorAddr, _operatorAddr);\n operatorAddr = _operatorAddr;\n }\n\n /**\n * @notice Collects AERO from AMO strategy and\n * sends it to the Strategist multisig.\n * Anyone can call it.\n */\n function harvest() external onlyGovernorOrStrategistOrOperator {\n address strategistAddr = vault.strategistAddr();\n require(strategistAddr != address(0), \"Guardian address not set\");\n\n // Collect all AERO\n amoStrategy.collectRewardTokens();\n\n uint256 aeroBalance = aero.balanceOf(address(this));\n if (aeroBalance == 0) {\n // Do nothing if there's no AERO to transfer\n return;\n }\n\n // Transfer everything to Strategist\n aero.safeTransfer(strategistAddr, aeroBalance);\n }\n\n /**\n * @notice Harvests AERO from AMO strategy and then swaps some (or all)\n * of it into WETH to distribute yield and fee.\n * When `feeBps` is set to 10000 (100%), all WETH received is\n * sent to strategist.\n *\n * @param aeroToSwap Amount of AERO to swap\n * @param minWETHExpected Min. amount of WETH to expect\n * @param feeBps Performance fee bps (Sent to strategist)\n * @param sendYieldToDripper Sends yield to Dripper, if set to true.\n * Otherwise, to the Guardian\n */\n function harvestAndSwap(\n uint256 aeroToSwap,\n uint256 minWETHExpected,\n uint256 feeBps,\n bool sendYieldToDripper\n ) external onlyGovernorOrStrategist {\n address strategistAddr = vault.strategistAddr();\n require(strategistAddr != address(0), \"Guardian address not set\");\n\n // Yields can either be sent to the Dripper or Strategist\n address yieldRecipient = sendYieldToDripper\n ? vault.dripper()\n : strategistAddr;\n require(yieldRecipient != address(0), \"Yield recipient not set\");\n\n require(feeBps <= 10000, \"Invalid Fee Bps\");\n\n // Collect all AERO\n amoStrategy.collectRewardTokens();\n\n uint256 aeroBalance = aero.balanceOf(address(this));\n if (aeroBalance == 0) {\n // Do nothing if there's no AERO to transfer/swap\n return;\n }\n\n if (aeroToSwap > 0) {\n if (aeroBalance < aeroToSwap) {\n // Transfer in balance from the multisig as needed\n // slither-disable-next-line unchecked-transfer arbitrary-send-erc20\n aero.safeTransferFrom(\n strategistAddr,\n address(this),\n aeroToSwap - aeroBalance\n );\n }\n\n _doSwap(aeroToSwap, minWETHExpected);\n\n // Figure out AERO left in contract after swap\n aeroBalance = aero.balanceOf(address(this));\n }\n\n // Transfer out any leftover AERO after swap\n if (aeroBalance > 0) {\n aero.safeTransfer(strategistAddr, aeroBalance);\n }\n\n // Computes using all balance the contract holds,\n // not just the WETH received from swap. Use `transferToken`\n // if there's any WETH left that needs to be taken out\n uint256 availableWETHBalance = weth.balanceOf(address(this));\n // Computation rounds in favor of protocol\n uint256 fee = (availableWETHBalance * feeBps) / 10000;\n uint256 yield = availableWETHBalance - fee;\n\n // Transfer yield, if any\n if (yield > 0) {\n weth.safeTransfer(yieldRecipient, yield);\n }\n\n // Transfer fee to the Guardian, if any\n if (fee > 0) {\n weth.safeTransfer(strategistAddr, fee);\n }\n\n emit YieldSent(yieldRecipient, yield, fee);\n }\n\n /**\n * @notice Swaps AERO to WETH on Aerodrome\n * @param aeroToSwap Amount of AERO to swap\n * @param minWETHExpected Min. amount of WETH to expect\n */\n function _doSwap(uint256 aeroToSwap, uint256 minWETHExpected) internal {\n // Let the swap router move funds\n aero.approve(address(swapRouter), aeroToSwap);\n\n // Do the swap\n uint256 wethReceived = swapRouter.exactInputSingle(\n ISwapRouter.ExactInputSingleParams({\n tokenIn: address(aero),\n tokenOut: address(weth),\n tickSpacing: 200, // From AERO/WETH pool contract\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: aeroToSwap,\n amountOutMinimum: minWETHExpected,\n sqrtPriceLimitX96: 0\n })\n );\n\n emit RewardTokenSwapped(\n address(aero),\n address(weth),\n 0,\n aeroToSwap,\n wethReceived\n );\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * the contract, i.e. mistaken sends.\n * Also, allows to transfer any AERO left in the contract.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n virtual\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n}\n" + }, + "contracts/harvest/OETHDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Dripper } from \"./Dripper.sol\";\n\n/**\n * @title OETH Dripper Contract\n * @author Origin Protocol Inc\n */\ncontract OETHDripper is Dripper {\n constructor(address _vault, address _token) Dripper(_vault, _token) {}\n}\n" + }, + "contracts/harvest/OETHFixedRateDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { FixedRateDripper } from \"./FixedRateDripper.sol\";\n\n/**\n * @title OETH FixedRateDripper Contract\n * @author Origin Protocol Inc\n */\ncontract OETHFixedRateDripper is FixedRateDripper {\n constructor(address _vault, address _token)\n FixedRateDripper(_vault, _token)\n {}\n}\n" + }, + "contracts/harvest/OETHHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractHarvester } from \"./AbstractHarvester.sol\";\n\ncontract OETHHarvester is AbstractHarvester {\n constructor(address _vault, address _wethAddress)\n AbstractHarvester(_vault, _wethAddress)\n {}\n}\n" + }, + "contracts/harvest/OETHHarvesterSimple.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\n\n/// @title OETH Harvester Simple Contract\n/// @notice Contract to harvest rewards from strategies\n/// @author Origin Protocol Inc\ncontract OETHHarvesterSimple is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n ////////////////////////////////////////////////////\n /// --- CONSTANTS & IMMUTABLES\n ////////////////////////////////////////////////////\n /// @notice wrapped native token address (WETH or wS)\n address public immutable wrappedNativeToken;\n\n ////////////////////////////////////////////////////\n /// --- STORAGE\n ////////////////////////////////////////////////////\n /// @notice Dripper address\n address public dripper;\n\n /// @notice Mapping of supported strategies\n mapping(address => bool) public supportedStrategies;\n\n /// @notice Gap for upgrade safety\n uint256[48] private ___gap;\n\n ////////////////////////////////////////////////////\n /// --- EVENTS\n ////////////////////////////////////////////////////\n event Harvested(\n address indexed strategy,\n address token,\n uint256 amount,\n address indexed receiver\n );\n event SupportedStrategyUpdated(address strategy, bool status);\n event DripperUpdated(address dripper);\n\n ////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////\n constructor(address _wrappedNativeToken) {\n wrappedNativeToken = _wrappedNativeToken;\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /// @notice Initialize the contract\n function initialize() external onlyGovernor initializer {\n // Call it to set `initialized` to true and to prevent the implementation\n // from getting initialized in future through the proxy\n }\n\n ////////////////////////////////////////////////////\n /// --- MUTATIVE FUNCTIONS\n ////////////////////////////////////////////////////\n /// @notice Harvest rewards from a strategy and transfer to strategist or dripper\n /// @param _strategy Address of the strategy to harvest\n function harvestAndTransfer(address _strategy) external {\n _harvestAndTransfer(_strategy);\n }\n\n /// @notice Harvest rewards from multiple strategies and transfer to strategist or dripper\n /// @param _strategies Array of strategy addresses to harvest\n function harvestAndTransfer(address[] calldata _strategies) external {\n for (uint256 i = 0; i < _strategies.length; i++) {\n _harvestAndTransfer(_strategies[i]);\n }\n }\n\n /// @notice Internal logic to harvest rewards from a strategy\n function _harvestAndTransfer(address _strategy) internal virtual {\n // Ensure strategy is supported\n require(supportedStrategies[_strategy], \"Strategy not supported\");\n\n // Store locally for some gas savings\n address _strategist = strategistAddr;\n address _dripper = dripper;\n\n // Harvest rewards\n IStrategy(_strategy).collectRewardTokens();\n\n // Cache reward tokens\n address[] memory rewardTokens = IStrategy(_strategy)\n .getRewardTokenAddresses();\n\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; i++) {\n // Cache balance\n address token = rewardTokens[i];\n uint256 balance = IERC20(token).balanceOf(address(this));\n if (balance > 0) {\n // Determine receiver\n address receiver = token == wrappedNativeToken\n ? _dripper\n : _strategist;\n require(receiver != address(0), \"Invalid receiver\");\n\n // Transfer to the Strategist or the Dripper\n IERC20(token).safeTransfer(receiver, balance);\n emit Harvested(_strategy, token, balance, receiver);\n }\n }\n }\n\n ////////////////////////////////////////////////////\n /// --- GOVERNANCE\n ////////////////////////////////////////////////////\n /// @notice Set supported strategy\n /// @param _strategy Address of the strategy\n /// @param _isSupported Boolean indicating if strategy is supported\n function setSupportedStrategy(address _strategy, bool _isSupported)\n external\n onlyGovernorOrStrategist\n {\n require(_strategy != address(0), \"Invalid strategy\");\n supportedStrategies[_strategy] = _isSupported;\n emit SupportedStrategyUpdated(_strategy, _isSupported);\n }\n\n /// @notice Transfer tokens to strategist\n /// @param _asset Address of the token\n /// @param _amount Amount of tokens to transfer\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernorOrStrategist\n {\n IERC20(_asset).safeTransfer(strategistAddr, _amount);\n }\n\n /// @notice Set the dripper address\n /// @param _dripper Address of the dripper\n function setDripper(address _dripper) external onlyGovernor {\n _setDripper(_dripper);\n }\n\n /// @notice Internal logic to set the dripper address\n function _setDripper(address _dripper) internal {\n require(_dripper != address(0), \"Invalid dripper\");\n dripper = _dripper;\n emit DripperUpdated(_dripper);\n }\n}\n" + }, + "contracts/harvest/OSonicHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SuperOETHHarvester } from \"./SuperOETHHarvester.sol\";\n\ncontract OSonicHarvester is SuperOETHHarvester {\n /// @param _wrappedNativeToken Address of the native Wrapped S (wS) token\n constructor(address _wrappedNativeToken)\n SuperOETHHarvester(_wrappedNativeToken)\n {}\n}\n" + }, + "contracts/harvest/SuperOETHHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHHarvesterSimple, IERC20, IStrategy, SafeERC20 } from \"./OETHHarvesterSimple.sol\";\n\ncontract SuperOETHHarvester is OETHHarvesterSimple {\n using SafeERC20 for IERC20;\n\n constructor(address _wrappedNativeToken)\n OETHHarvesterSimple(_wrappedNativeToken)\n {}\n\n /// @inheritdoc OETHHarvesterSimple\n function _harvestAndTransfer(address _strategy) internal virtual override {\n // Ensure strategy is supported\n require(supportedStrategies[_strategy], \"Strategy not supported\");\n\n address receiver = strategistAddr;\n require(receiver != address(0), \"Invalid receiver\");\n\n // Harvest rewards\n IStrategy(_strategy).collectRewardTokens();\n\n // Cache reward tokens\n address[] memory rewardTokens = IStrategy(_strategy)\n .getRewardTokenAddresses();\n\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; i++) {\n // Cache balance\n address token = rewardTokens[i];\n uint256 balance = IERC20(token).balanceOf(address(this));\n if (balance > 0) {\n // Transfer everything to the strategist\n IERC20(token).safeTransfer(receiver, balance);\n emit Harvested(_strategy, token, balance, receiver);\n }\n }\n }\n}\n" + }, + "contracts/interfaces/aerodrome/IAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ICLPool } from \"./ICLPool.sol\";\n\ninterface IAMOStrategy {\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth);\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth);\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n error OutsideExpectedTickRange(int24 currentTick);\n\n function governor() external view returns (address);\n\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) external;\n\n function clPool() external view returns (ICLPool);\n\n function vaultAddress() external view returns (address);\n\n function poolWethShareVarianceAllowed() external view returns (uint256);\n\n function poolWethShare() external view returns (uint256);\n\n function tokenId() external view returns (uint256);\n\n function withdrawAll() external;\n\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external;\n\n function setWithdrawLiquidityShare(uint128 share) external;\n\n function lowerTick() external view returns (int24);\n\n function upperTick() external view returns (int24);\n\n function getPoolX96Price() external view returns (uint160 _sqrtRatioX96);\n\n function sqrtRatioX96TickLower() external view returns (uint160);\n\n function sqrtRatioX96TickHigher() external view returns (uint160);\n\n function tickSpacing() external view returns (int24);\n\n function allowedWethShareStart() external view returns (uint256);\n\n function allowedWethShareEnd() external view returns (uint256);\n\n function claimGovernance() external;\n\n function transferGovernance(address _governor) external;\n\n function getPositionPrincipal()\n external\n view\n returns (uint256 _amountWeth, uint256 _amountOethb);\n}\n" + }, + "contracts/interfaces/aerodrome/ICLGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\ninterface ICLGauge {\n /// @notice Returns the claimable rewards for a given account and tokenId\n /// @dev Throws if account is not the position owner\n /// @dev pool.updateRewardsGrowthGlobal() needs to be called first, to return the correct claimable rewards\n /// @param account The address of the user\n /// @param tokenId The tokenId of the position\n /// @return The amount of claimable reward\n function earned(address account, uint256 tokenId)\n external\n view\n returns (uint256);\n\n /// @notice Retrieve rewards for all tokens owned by an account\n /// @dev Throws if not called by the voter\n /// @param account The account of the user\n function getReward(address account) external;\n\n /// @notice Retrieve rewards for a tokenId\n /// @dev Throws if not called by the position owner\n /// @param tokenId The tokenId of the position\n function getReward(uint256 tokenId) external;\n\n /// @notice Notifies gauge of gauge rewards.\n /// @param amount Amount of gauge rewards (emissions) to notify. Must be greater than 604_800.\n function notifyRewardAmount(uint256 amount) external;\n\n /// @dev Notifies gauge of gauge rewards without distributing its fees.\n /// Assumes gauge reward tokens is 18 decimals.\n /// If not 18 decimals, rewardRate may have rounding issues.\n /// @param amount Amount of gauge rewards (emissions) to notify. Must be greater than 604_800.\n function notifyRewardWithoutClaim(uint256 amount) external;\n\n /// @notice Used to deposit a CL position into the gauge\n /// @notice Allows the user to receive emissions instead of fees\n /// @param tokenId The tokenId of the position\n function deposit(uint256 tokenId) external;\n\n /// @notice Used to withdraw a CL position from the gauge\n /// @notice Allows the user to receive fees instead of emissions\n /// @notice Outstanding emissions will be collected on withdrawal\n /// @param tokenId The tokenId of the position\n function withdraw(uint256 tokenId) external;\n\n // /// @notice Fetch all tokenIds staked by a given account\n // /// @param depositor The address of the user\n // /// @return The tokenIds of the staked positions\n // function stakedValues(address depositor) external view returns (uint256[] memory);\n\n // /// @notice Fetch a staked tokenId by index\n // /// @param depositor The address of the user\n // /// @param index The index of the staked tokenId\n // /// @return The tokenId of the staked position\n // function stakedByIndex(address depositor, uint256 index) external view returns (uint256);\n\n // /// @notice Check whether a position is staked in the gauge by a certain user\n // /// @param depositor The address of the user\n // /// @param tokenId The tokenId of the position\n // /// @return Whether the position is staked in the gauge\n // function stakedContains(address depositor, uint256 tokenId) external view returns (bool);\n\n // /// @notice The amount of positions staked in the gauge by a certain user\n // /// @param depositor The address of the user\n // /// @return The amount of positions staked in the gauge\n // function stakedLength(address depositor) external view returns (uint256);\n\n function feesVotingReward() external view returns (address);\n}\n" + }, + "contracts/interfaces/aerodrome/ICLPool.sol": { + "content": "pragma solidity >=0.5.0;\n\n/// @title The interface for a CL Pool\n/// @notice A CL pool facilitates swapping and automated market making between any two assets that strictly conform\n/// to the ERC20 specification\n/// @dev The pool interface is broken up into many smaller pieces\ninterface ICLPool {\n function slot0()\n external\n view\n returns (\n uint160 sqrtPriceX96,\n int24 tick,\n uint16 observationIndex,\n uint16 observationCardinality,\n uint16 observationCardinalityNext,\n bool unlocked\n );\n\n /// @notice The first of the two tokens of the pool, sorted by address\n /// @return The token contract address\n function token0() external view returns (address);\n\n /// @notice The second of the two tokens of the pool, sorted by address\n /// @return The token contract address\n function token1() external view returns (address);\n\n function tickSpacing() external view returns (int24);\n\n /// @notice The gauge corresponding to this pool\n /// @return The gauge contract address\n function gauge() external view returns (address);\n\n /// @notice The currently in range liquidity available to the pool\n /// @dev This value has no relationship to the total liquidity across all ticks\n /// @dev This value includes staked liquidity\n function liquidity() external view returns (uint128);\n\n /// @notice Look up information about a specific tick in the pool\n /// @param tick The tick to look up\n /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or\n /// tick upper,\n /// liquidityNet how much liquidity changes when the pool price crosses the tick,\n /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,\n /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,\n /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick\n /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from\n /// the current tick,\n /// secondsOutside the seconds spent on the other side of the tick from the current tick,\n /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise\n /// equal to false.\n /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.\n /// In addition, these values are only relative and must be used only in comparison to previous snapshots for\n /// a specific position.\n function ticks(int24 tick)\n external\n view\n returns (\n uint128 liquidityGross,\n int128 liquidityNet,\n uint256 feeGrowthOutside0X128,\n uint256 feeGrowthOutside1X128,\n int56 tickCumulativeOutside,\n uint160 secondsPerLiquidityOutsideX128,\n uint32 secondsOutside,\n bool initialized\n );\n}\n" + }, + "contracts/interfaces/aerodrome/INonfungiblePositionManager.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title Non-fungible token for positions\n/// @notice Wraps CL positions in a non-fungible token interface which allows for them to be transferred\n/// and authorized.\n// slither-disable-start erc20-interface\ninterface INonfungiblePositionManager {\n /**\n * @dev See {IERC721-approve}.\n */\n function approve(address to, uint256 tokenId) external;\n\n /**\n * @dev See {IERC721-getApproved}.\n */\n function getApproved(uint256 tokenId) external returns (address);\n\n /**\n * @dev See {IERC721-ownerOf}.\n */\n function ownerOf(uint256 tokenId) external view returns (address);\n\n /// @notice Returns the position information associated with a given token ID.\n /// @dev Throws if the token ID is not valid.\n /// @param tokenId The ID of the token that represents the position\n /// @return nonce The nonce for permits\n /// @return operator The address that is approved for spending\n /// @return token0 The address of the token0 for a specific pool\n /// @return token1 The address of the token1 for a specific pool\n /// @return tickSpacing The tick spacing associated with the pool\n /// @return tickLower The lower end of the tick range for the position\n /// @return tickUpper The higher end of the tick range for the position\n /// @return liquidity The liquidity of the position\n /// @return feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position\n /// @return feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position\n /// @return tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation\n /// @return tokensOwed1 The uncollected amount of token1 owed to the position as of the last computation\n function positions(uint256 tokenId)\n external\n view\n returns (\n uint96 nonce,\n address operator,\n address token0,\n address token1,\n int24 tickSpacing,\n int24 tickLower,\n int24 tickUpper,\n uint128 liquidity,\n uint256 feeGrowthInside0LastX128,\n uint256 feeGrowthInside1LastX128,\n uint128 tokensOwed0,\n uint128 tokensOwed1\n );\n\n struct MintParams {\n address token0;\n address token1;\n int24 tickSpacing;\n int24 tickLower;\n int24 tickUpper;\n uint256 amount0Desired;\n uint256 amount1Desired;\n uint256 amount0Min;\n uint256 amount1Min;\n address recipient;\n uint256 deadline;\n uint160 sqrtPriceX96;\n }\n\n /// @notice Creates a new position wrapped in a NFT\n /// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized\n /// a method does not exist, i.e. the pool is assumed to be initialized.\n /// @param params The params necessary to mint a position, encoded as `MintParams` in calldata\n /// @return tokenId The ID of the token that represents the minted position\n /// @return liquidity The amount of liquidity for this position\n /// @return amount0 The amount of token0\n /// @return amount1 The amount of token1\n function mint(MintParams calldata params)\n external\n payable\n returns (\n uint256 tokenId,\n uint128 liquidity,\n uint256 amount0,\n uint256 amount1\n );\n\n struct IncreaseLiquidityParams {\n uint256 tokenId;\n uint256 amount0Desired;\n uint256 amount1Desired;\n uint256 amount0Min;\n uint256 amount1Min;\n uint256 deadline;\n }\n\n /// @notice Increases the amount of liquidity in a position, with tokens paid by the `msg.sender`\n /// @param params tokenId The ID of the token for which liquidity is being increased,\n /// amount0Desired The desired amount of token0 to be spent,\n /// amount1Desired The desired amount of token1 to be spent,\n /// amount0Min The minimum amount of token0 to spend, which serves as a slippage check,\n /// amount1Min The minimum amount of token1 to spend, which serves as a slippage check,\n /// deadline The time by which the transaction must be included to effect the change\n /// @return liquidity The new liquidity amount as a result of the increase\n /// @return amount0 The amount of token0 to acheive resulting liquidity\n /// @return amount1 The amount of token1 to acheive resulting liquidity\n function increaseLiquidity(IncreaseLiquidityParams calldata params)\n external\n payable\n returns (\n uint128 liquidity,\n uint256 amount0,\n uint256 amount1\n );\n\n struct DecreaseLiquidityParams {\n uint256 tokenId;\n uint128 liquidity;\n uint256 amount0Min;\n uint256 amount1Min;\n uint256 deadline;\n }\n\n /// @notice Decreases the amount of liquidity in a position and accounts it to the position\n /// @param params tokenId The ID of the token for which liquidity is being decreased,\n /// amount The amount by which liquidity will be decreased,\n /// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity,\n /// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity,\n /// deadline The time by which the transaction must be included to effect the change\n /// @return amount0 The amount of token0 accounted to the position's tokens owed\n /// @return amount1 The amount of token1 accounted to the position's tokens owed\n /// @dev The use of this function can cause a loss to users of the NonfungiblePositionManager\n /// @dev for tokens that have very high decimals.\n /// @dev The amount of tokens necessary for the loss is: 3.4028237e+38.\n /// @dev This is equivalent to 1e20 value with 18 decimals.\n function decreaseLiquidity(DecreaseLiquidityParams calldata params)\n external\n payable\n returns (uint256 amount0, uint256 amount1);\n\n struct CollectParams {\n uint256 tokenId;\n address recipient;\n uint128 amount0Max;\n uint128 amount1Max;\n }\n\n /// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient\n /// @notice Used to update staked positions before deposit and withdraw\n /// @param params tokenId The ID of the NFT for which tokens are being collected,\n /// recipient The account that should receive the tokens,\n /// amount0Max The maximum amount of token0 to collect,\n /// amount1Max The maximum amount of token1 to collect\n /// @return amount0 The amount of fees collected in token0\n /// @return amount1 The amount of fees collected in token1\n function collect(CollectParams calldata params)\n external\n payable\n returns (uint256 amount0, uint256 amount1);\n\n /// @notice Burns a token ID, which deletes it from the NFT contract. The token must have 0 liquidity and all tokens\n /// must be collected first.\n /// @param tokenId The ID of the token that is being burned\n function burn(uint256 tokenId) external payable;\n\n /// @notice Sets a new Token Descriptor\n /// @param _tokenDescriptor Address of the new Token Descriptor to be chosen\n function setTokenDescriptor(address _tokenDescriptor) external;\n\n /// @notice Sets a new Owner address\n /// @param _owner Address of the new Owner to be chosen\n function setOwner(address _owner) external;\n}\n// slither-disable-end erc20-interface\n" + }, + "contracts/interfaces/aerodrome/IQuoterV2.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title QuoterV2 Interface\n/// @notice Supports quoting the calculated amounts from exact input or exact output swaps.\n/// @notice For each pool also tells you the number of initialized ticks crossed and the sqrt price of the\n/// pool after the swap.\n/// @dev These functions are not marked view because they rely on calling non-view functions and reverting\n/// to compute the result. They are also not gas efficient and should not be called on-chain.\ninterface IQuoterV2 {\n /// @notice Returns the amount out received for a given exact input swap without executing the swap\n /// @param path The path of the swap, i.e. each token pair and the pool tick spacing\n /// @param amountIn The amount of the first token to swap\n /// @return amountOut The amount of the last token that would be received\n /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path\n /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for\n /// each pool in the path\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactInput(bytes memory path, uint256 amountIn)\n external\n returns (\n uint256 amountOut,\n uint160[] memory sqrtPriceX96AfterList,\n uint32[] memory initializedTicksCrossedList,\n uint256 gasEstimate\n );\n\n struct QuoteExactInputSingleParams {\n address tokenIn;\n address tokenOut;\n uint256 amountIn;\n int24 tickSpacing;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Returns the amount out received for a given exact input but for a swap of a single pool\n /// @param params The params for the quote, encoded as `QuoteExactInputSingleParams`\n /// tokenIn The token being swapped in\n /// tokenOut The token being swapped out\n /// tickSpacing The tick spacing of the token pool to consider for the pair\n /// amountIn The desired input amount\n /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap\n /// @return amountOut The amount of `tokenOut` that would be received\n /// @return sqrtPriceX96After The sqrt price of the pool after the swap\n /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactInputSingle(QuoteExactInputSingleParams memory params)\n external\n returns (\n uint256 amountOut,\n uint160 sqrtPriceX96After,\n uint32 initializedTicksCrossed,\n uint256 gasEstimate\n );\n\n /// @notice Returns the amount in required for a given exact output swap without executing the swap\n /// @param path The path of the swap, i.e. each token pair and the pool tick spacing.\n /// Path must be provided in reverse order\n /// @param amountOut The amount of the last token to receive\n /// @return amountIn The amount of first token required to be paid\n /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path\n /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for\n /// each pool in the path\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactOutput(bytes memory path, uint256 amountOut)\n external\n returns (\n uint256 amountIn,\n uint160[] memory sqrtPriceX96AfterList,\n uint32[] memory initializedTicksCrossedList,\n uint256 gasEstimate\n );\n\n struct QuoteExactOutputSingleParams {\n address tokenIn;\n address tokenOut;\n uint256 amount;\n int24 tickSpacing;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Returns the amount in required to receive the given exact output amount but for a swap of a single pool\n /// @param params The params for the quote, encoded as `QuoteExactOutputSingleParams`\n /// tokenIn The token being swapped in\n /// tokenOut The token being swapped out\n /// tickSpacing The tick spacing of the token pool to consider for the pair\n /// amountOut The desired output amount\n /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap\n /// @return amountIn The amount required as the input for the swap in order to receive `amountOut`\n /// @return sqrtPriceX96After The sqrt price of the pool after the swap\n /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactOutputSingle(QuoteExactOutputSingleParams memory params)\n external\n returns (\n uint256 amountIn,\n uint160 sqrtPriceX96After,\n uint32 initializedTicksCrossed,\n uint256 gasEstimate\n );\n}\n" + }, + "contracts/interfaces/aerodrome/ISugarHelper.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\npragma abicoder v2;\n\nimport { INonfungiblePositionManager } from \"./INonfungiblePositionManager.sol\";\n\ninterface ISugarHelper {\n struct PopulatedTick {\n int24 tick;\n uint160 sqrtRatioX96;\n int128 liquidityNet;\n uint128 liquidityGross;\n }\n\n ///\n /// Wrappers for LiquidityAmounts\n ///\n\n function getAmountsForLiquidity(\n uint160 sqrtRatioX96,\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96,\n uint128 liquidity\n ) external pure returns (uint256 amount0, uint256 amount1);\n\n function getLiquidityForAmounts(\n uint256 amount0,\n uint256 amount1,\n uint160 sqrtRatioX96,\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96\n ) external pure returns (uint128 liquidity);\n\n /// @notice Computes the amount of token0 for a given amount of token1 and price range\n /// @param amount1 Amount of token1 to estimate liquidity\n /// @param pool Address of the pool to be used\n /// @param sqrtRatioX96 A sqrt price representing the current pool prices\n /// @param tickLow Lower tick boundary\n /// @param tickLow Upper tick boundary\n /// @dev If the given pool address is not the zero address, will fetch `sqrtRatioX96` from pool\n /// @return amount0 Estimated amount of token0\n function estimateAmount0(\n uint256 amount1,\n address pool,\n uint160 sqrtRatioX96,\n int24 tickLow,\n int24 tickHigh\n ) external view returns (uint256 amount0);\n\n /// @notice Computes the amount of token1 for a given amount of token0 and price range\n /// @param amount0 Amount of token0 to estimate liquidity\n /// @param pool Address of the pool to be used\n /// @param sqrtRatioX96 A sqrt price representing the current pool prices\n /// @param tickLow Lower tick boundary\n /// @param tickLow Upper tick boundary\n /// @dev If the given pool address is not the zero address, will fetch `sqrtRatioX96` from pool\n /// @return amount1 Estimated amount of token1\n function estimateAmount1(\n uint256 amount0,\n address pool,\n uint160 sqrtRatioX96,\n int24 tickLow,\n int24 tickHigh\n ) external view returns (uint256 amount1);\n\n ///\n /// Wrappers for PositionValue\n ///\n\n function principal(\n INonfungiblePositionManager positionManager,\n uint256 tokenId,\n uint160 sqrtRatioX96\n ) external view returns (uint256 amount0, uint256 amount1);\n\n function fees(INonfungiblePositionManager positionManager, uint256 tokenId)\n external\n view\n returns (uint256 amount0, uint256 amount1);\n\n ///\n /// Wrappers for TickMath\n ///\n\n function getSqrtRatioAtTick(int24 tick)\n external\n pure\n returns (uint160 sqrtRatioX96);\n\n function getTickAtSqrtRatio(uint160 sqrtRatioX96)\n external\n pure\n returns (int24 tick);\n\n /// @notice Fetches Tick Data for all populated Ticks in given bitmaps\n /// @param pool Address of the pool from which to fetch data\n /// @param startTick Tick from which the first bitmap will be fetched\n /// @dev The number of bitmaps fetched by this function should always be `MAX_BITMAPS`,\n /// unless there are less than `MAX_BITMAPS` left to iterate through\n /// @return populatedTicks Array of all Populated Ticks in the provided bitmaps\n function getPopulatedTicks(address pool, int24 startTick)\n external\n view\n returns (PopulatedTick[] memory populatedTicks);\n}\n" + }, + "contracts/interfaces/aerodrome/ISwapRouter.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title Router token swapping functionality\n/// @notice Functions for swapping tokens via CL\ninterface ISwapRouter {\n struct ExactInputSingleParams {\n address tokenIn;\n address tokenOut;\n int24 tickSpacing;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInputSingle(ExactInputSingleParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactOutputSingleParams {\n address tokenIn;\n address tokenOut;\n int24 tickSpacing;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutputSingle(ExactOutputSingleParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n\n struct ExactOutputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutput(ExactOutputParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n}\n" + }, + "contracts/interfaces/balancer/IBalancerVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"../../utils/InitializableAbstractStrategy.sol\";\n\ninterface IBalancerVault {\n enum WeightedPoolJoinKind {\n INIT,\n EXACT_TOKENS_IN_FOR_BPT_OUT,\n TOKEN_IN_FOR_EXACT_BPT_OUT,\n ALL_TOKENS_IN_FOR_EXACT_BPT_OUT,\n ADD_TOKEN\n }\n\n enum WeightedPoolExitKind {\n EXACT_BPT_IN_FOR_ONE_TOKEN_OUT,\n EXACT_BPT_IN_FOR_TOKENS_OUT,\n BPT_IN_FOR_EXACT_TOKENS_OUT,\n REMOVE_TOKEN\n }\n\n /**\n * @dev Called by users to join a Pool, which transfers tokens from `sender` into the Pool's balance. This will\n * trigger custom Pool behavior, which will typically grant something in return to `recipient` - often tokenized\n * Pool shares.\n *\n * If the caller is not `sender`, it must be an authorized relayer for them.\n *\n * The `assets` and `maxAmountsIn` arrays must have the same length, and each entry indicates the maximum amount\n * to send for each asset. The amounts to send are decided by the Pool and not the Vault: it just enforces\n * these maximums.\n *\n * If joining a Pool that holds WETH, it is possible to send ETH directly: the Vault will do the wrapping. To enable\n * this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead of the\n * WETH address. Note that it is not possible to combine ETH and WETH in the same join. Any excess ETH will be sent\n * back to the caller (not the sender, which is important for relayers).\n *\n * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when\n * interacting with Pools that register and deregister tokens frequently. If sending ETH however, the array must be\n * sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the final\n * `assets` array might not be sorted. Pools with no registered tokens cannot be joined.\n *\n * If `fromInternalBalance` is true, the caller's Internal Balance will be preferred: ERC20 transfers will only\n * be made for the difference between the requested amount and Internal Balance (if any). Note that ETH cannot be\n * withdrawn from Internal Balance: attempting to do so will trigger a revert.\n *\n * This causes the Vault to call the `IBasePool.onJoinPool` hook on the Pool's contract, where Pools implement\n * their own custom logic. This typically requires additional information from the user (such as the expected number\n * of Pool shares). This can be encoded in the `userData` argument, which is ignored by the Vault and passed\n * directly to the Pool's contract, as is `recipient`.\n *\n * Emits a `PoolBalanceChanged` event.\n */\n function joinPool(\n bytes32 poolId,\n address sender,\n address recipient,\n JoinPoolRequest memory request\n ) external payable;\n\n struct JoinPoolRequest {\n address[] assets;\n uint256[] maxAmountsIn;\n bytes userData;\n bool fromInternalBalance;\n }\n\n /**\n * @dev Called by users to exit a Pool, which transfers tokens from the Pool's balance to `recipient`. This will\n * trigger custom Pool behavior, which will typically ask for something in return from `sender` - often tokenized\n * Pool shares. The amount of tokens that can be withdrawn is limited by the Pool's `cash` balance (see\n * `getPoolTokenInfo`).\n *\n * If the caller is not `sender`, it must be an authorized relayer for them.\n *\n * The `tokens` and `minAmountsOut` arrays must have the same length, and each entry in these indicates the minimum\n * token amount to receive for each token contract. The amounts to send are decided by the Pool and not the Vault:\n * it just enforces these minimums.\n *\n * If exiting a Pool that holds WETH, it is possible to receive ETH directly: the Vault will do the unwrapping. To\n * enable this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead\n * of the WETH address. Note that it is not possible to combine ETH and WETH in the same exit.\n *\n * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when\n * interacting with Pools that register and deregister tokens frequently. If receiving ETH however, the array must\n * be sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the\n * final `assets` array might not be sorted. Pools with no registered tokens cannot be exited.\n *\n * If `toInternalBalance` is true, the tokens will be deposited to `recipient`'s Internal Balance. Otherwise,\n * an ERC20 transfer will be performed. Note that ETH cannot be deposited to Internal Balance: attempting to\n * do so will trigger a revert.\n *\n * `minAmountsOut` is the minimum amount of tokens the user expects to get out of the Pool, for each token in the\n * `tokens` array. This array must match the Pool's registered tokens.\n *\n * This causes the Vault to call the `IBasePool.onExitPool` hook on the Pool's contract, where Pools implement\n * their own custom logic. This typically requires additional information from the user (such as the expected number\n * of Pool shares to return). This can be encoded in the `userData` argument, which is ignored by the Vault and\n * passed directly to the Pool's contract.\n *\n * Emits a `PoolBalanceChanged` event.\n */\n function exitPool(\n bytes32 poolId,\n address sender,\n address payable recipient,\n ExitPoolRequest memory request\n ) external;\n\n struct ExitPoolRequest {\n address[] assets;\n uint256[] minAmountsOut;\n bytes userData;\n bool toInternalBalance;\n }\n\n /**\n * @dev Returns a Pool's registered tokens, the total balance for each, and the latest block when *any* of\n * the tokens' `balances` changed.\n *\n * The order of the `tokens` array is the same order that will be used in `joinPool`, `exitPool`, as well as in all\n * Pool hooks (where applicable). Calls to `registerTokens` and `deregisterTokens` may change this order.\n *\n * If a Pool only registers tokens once, and these are sorted in ascending order, they will be stored in the same\n * order as passed to `registerTokens`.\n *\n * Total balances include both tokens held by the Vault and those withdrawn by the Pool's Asset Managers. These are\n * the amounts used by joins, exits and swaps. For a detailed breakdown of token balances, use `getPoolTokenInfo`\n * instead.\n */\n function getPoolTokens(bytes32 poolId)\n external\n view\n returns (\n IERC20[] memory tokens,\n uint256[] memory balances,\n uint256 lastChangeBlock\n );\n\n /**\n * @dev Performs a set of user balance operations, which involve Internal Balance (deposit, withdraw or transfer)\n * and plain ERC20 transfers using the Vault's allowance. This last feature is particularly useful for relayers, as\n * it lets integrators reuse a user's Vault allowance.\n *\n * For each operation, if the caller is not `sender`, it must be an authorized relayer for them.\n */\n function manageUserBalance(UserBalanceOp[] memory ops) external payable;\n\n struct UserBalanceOp {\n UserBalanceOpKind kind;\n address asset;\n uint256 amount;\n address sender;\n address payable recipient;\n }\n\n enum UserBalanceOpKind {\n DEPOSIT_INTERNAL,\n WITHDRAW_INTERNAL,\n TRANSFER_INTERNAL,\n TRANSFER_EXTERNAL\n }\n\n enum SwapKind {\n GIVEN_IN,\n GIVEN_OUT\n }\n\n struct SingleSwap {\n bytes32 poolId;\n SwapKind kind;\n address assetIn;\n address assetOut;\n uint256 amount;\n bytes userData;\n }\n\n struct FundManagement {\n address sender;\n bool fromInternalBalance;\n address payable recipient;\n bool toInternalBalance;\n }\n\n function swap(\n SingleSwap calldata singleSwap,\n FundManagement calldata funds,\n uint256 limit,\n uint256 deadline\n ) external returns (uint256 amountCalculated);\n\n function getPoolTokenInfo(bytes32 poolId, address token)\n external\n view\n returns (\n uint256 cash,\n uint256 managed,\n uint256 lastChangeBlock,\n address assetManager\n );\n}\n" + }, + "contracts/interfaces/balancer/IMetaStablePool.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IRateProvider } from \"./IRateProvider.sol\";\n\ninterface IMetaStablePool {\n function getRateProviders()\n external\n view\n returns (IRateProvider[] memory providers);\n}\n" + }, + "contracts/interfaces/balancer/IOracleWeightedPool.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// The three values that can be queried:\n//\n// - PAIR_PRICE: the price of the tokens in the Pool, expressed as the price of the second token in units of the\n// first token. For example, if token A is worth $2, and token B is worth $4, the pair price will be 2.0.\n// Note that the price is computed *including* the tokens decimals. This means that the pair price of a Pool with\n// DAI and USDC will be close to 1.0, despite DAI having 18 decimals and USDC 6.\n//\n// - BPT_PRICE: the price of the Pool share token (BPT), in units of the first token.\n// Note that the price is computed *including* the tokens decimals. This means that the BPT price of a Pool with\n// USDC in which BPT is worth $5 will be 5.0, despite the BPT having 18 decimals and USDC 6.\n//\n// - INVARIANT: the value of the Pool's invariant, which serves as a measure of its liquidity.\nenum Variable {\n PAIR_PRICE,\n BPT_PRICE,\n INVARIANT\n}\n\n/**\n * @dev Information for a Time Weighted Average query.\n *\n * Each query computes the average over a window of duration `secs` seconds that ended `ago` seconds ago. For\n * example, the average over the past 30 minutes is computed by settings secs to 1800 and ago to 0. If secs is 1800\n * and ago is 1800 as well, the average between 60 and 30 minutes ago is computed instead.\n */\nstruct OracleAverageQuery {\n Variable variable;\n uint256 secs;\n uint256 ago;\n}\n\ninterface IOracleWeightedPool {\n /**\n * @dev Returns the time average weighted price corresponding to each of `queries`. Prices are represented as 18\n * decimal fixed point values.\n */\n function getTimeWeightedAverage(OracleAverageQuery[] memory queries)\n external\n view\n returns (uint256[] memory results);\n}\n" + }, + "contracts/interfaces/balancer/IRateProvider.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity ^0.8.0;\n\ninterface IRateProvider {\n function getRate() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/chainlink/AggregatorV3Interface.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorV3Interface {\n function decimals() external view returns (uint8);\n\n function description() external view returns (string memory);\n\n function version() external view returns (uint256);\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(uint80 _roundId)\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n\n function latestRoundData()\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint40 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n bytes32 pubKeyHash,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof,\n bytes calldata validatorPubKeyProof\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint40 validatorIndex\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes32 pubKeyHash,\n bytes calldata firstPendingDepositPubKeyProof\n ) external view returns (bool isEmptyDepositQueue);\n\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view returns (bool isEmptyDepositQueue);\n}\n" + }, + "contracts/interfaces/IBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBuyback {\n function swap() external;\n}\n" + }, + "contracts/interfaces/ICampaignRemoteManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICampaignRemoteManager {\n function createCampaign(\n CampaignCreationParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n function manageCampaign(\n CampaignManagementParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n function closeCampaign(\n CampaignClosingParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n struct CampaignCreationParams {\n uint256 chainId;\n address gauge;\n address manager;\n address rewardToken;\n uint8 numberOfPeriods;\n uint256 maxRewardPerVote;\n uint256 totalRewardAmount;\n address[] addresses;\n address hook;\n bool isWhitelist;\n }\n\n struct CampaignManagementParams {\n uint256 campaignId;\n address rewardToken;\n uint8 numberOfPeriods;\n uint256 totalRewardAmount;\n uint256 maxRewardPerVote;\n }\n\n struct CampaignClosingParams {\n uint256 campaignId;\n }\n}\n" + }, + "contracts/interfaces/IChildLiquidityGaugeFactory.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IChildLiquidityGaugeFactory {\n event DeployedGauge(\n address indexed _implementation,\n address indexed _lp_token,\n address indexed _deployer,\n bytes32 _salt,\n address _gauge\n );\n event Minted(\n address indexed _user,\n address indexed _gauge,\n uint256 _new_total\n );\n event TransferOwnership(address _old_owner, address _new_owner);\n event UpdateCallProxy(address _old_call_proxy, address _new_call_proxy);\n event UpdateImplementation(\n address _old_implementation,\n address _new_implementation\n );\n event UpdateManager(address _manager);\n event UpdateMirrored(address indexed _gauge, bool _mirrored);\n event UpdateRoot(address _factory, address _implementation);\n event UpdateVotingEscrow(\n address _old_voting_escrow,\n address _new_voting_escrow\n );\n\n function accept_transfer_ownership() external;\n\n function call_proxy() external view returns (address);\n\n function commit_transfer_ownership(address _future_owner) external;\n\n function crv() external view returns (address);\n\n function deploy_gauge(address _lp_token, bytes32 _salt)\n external\n returns (address);\n\n function deploy_gauge(\n address _lp_token,\n bytes32 _salt,\n address _manager\n ) external returns (address);\n\n function future_owner() external view returns (address);\n\n function gauge_data(address arg0) external view returns (uint256);\n\n function get_gauge(uint256 arg0) external view returns (address);\n\n function get_gauge_count() external view returns (uint256);\n\n function get_gauge_from_lp_token(address arg0)\n external\n view\n returns (address);\n\n function get_implementation() external view returns (address);\n\n function is_mirrored(address _gauge) external view returns (bool);\n\n function is_valid_gauge(address _gauge) external view returns (bool);\n\n function last_request(address _gauge) external view returns (uint256);\n\n function manager() external view returns (address);\n\n function mint(address _gauge) external;\n\n function mint_many(address[32] memory _gauges) external;\n\n function minted(address arg0, address arg1) external view returns (uint256);\n\n function owner() external view returns (address);\n\n function root_factory() external view returns (address);\n\n function root_implementation() external view returns (address);\n\n function set_call_proxy(address _new_call_proxy) external;\n\n function set_crv(address _crv) external;\n\n function set_implementation(address _implementation) external;\n\n function set_manager(address _new_manager) external;\n\n function set_mirrored(address _gauge, bool _mirrored) external;\n\n function set_root(address _factory, address _implementation) external;\n\n function set_voting_escrow(address _voting_escrow) external;\n\n function version() external view returns (string memory);\n\n function voting_escrow() external view returns (address);\n}\n" + }, + "contracts/interfaces/IComptroller.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IComptroller {\n // Claim all the COMP accrued by specific holders in specific markets for their supplies and/or borrows\n function claimComp(\n address[] memory holders,\n address[] memory cTokens,\n bool borrowers,\n bool suppliers\n ) external;\n\n function oracle() external view returns (address);\n}\n" + }, + "contracts/interfaces/IConsolidation.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IConsolidationSource {\n function confirmConsolidation()\n external\n returns (uint256 consolidationCount);\n}\n\ninterface IConsolidationTarget {\n function requestConsolidation(\n bytes32 lastSourcePubKeyHash,\n bytes32 targetPubKeyHash\n ) external;\n}\n" + }, + "contracts/interfaces/ICurveLiquidityGaugeV6.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveLiquidityGaugeV6 {\n event ApplyOwnership(address admin);\n event Approval(\n address indexed _owner,\n address indexed _spender,\n uint256 _value\n );\n event CommitOwnership(address admin);\n event Deposit(address indexed provider, uint256 value);\n event SetGaugeManager(address _gauge_manager);\n event Transfer(address indexed _from, address indexed _to, uint256 _value);\n event UpdateLiquidityLimit(\n address indexed user,\n uint256 original_balance,\n uint256 original_supply,\n uint256 working_balance,\n uint256 working_supply\n );\n event Withdraw(address indexed provider, uint256 value);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_reward(address _reward_token, address _distributor) external;\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function claim_rewards() external;\n\n function claim_rewards(address _addr) external;\n\n function claim_rewards(address _addr, address _receiver) external;\n\n function claimable_reward(address _user, address _reward_token)\n external\n view\n returns (uint256);\n\n function claimable_tokens(address addr) external returns (uint256);\n\n function claimed_reward(address _addr, address _token)\n external\n view\n returns (uint256);\n\n function decimals() external view returns (uint256);\n\n function decreaseAllowance(address _spender, uint256 _subtracted_value)\n external\n returns (bool);\n\n function deposit(uint256 _value) external;\n\n function deposit(uint256 _value, address _addr) external;\n\n function deposit(\n uint256 _value,\n address _addr,\n bool _claim_rewards\n ) external;\n\n function deposit_reward_token(address _reward_token, uint256 _amount)\n external;\n\n function deposit_reward_token(\n address _reward_token,\n uint256 _amount,\n uint256 _epoch\n ) external;\n\n function factory() external view returns (address);\n\n function future_epoch_time() external view returns (uint256);\n\n function increaseAllowance(address _spender, uint256 _added_value)\n external\n returns (bool);\n\n function inflation_rate() external view returns (uint256);\n\n function integrate_checkpoint() external view returns (uint256);\n\n function integrate_checkpoint_of(address arg0)\n external\n view\n returns (uint256);\n\n function integrate_fraction(address arg0) external view returns (uint256);\n\n function integrate_inv_supply(uint256 arg0) external view returns (uint256);\n\n function integrate_inv_supply_of(address arg0)\n external\n view\n returns (uint256);\n\n function is_killed() external view returns (bool);\n\n function kick(address addr) external;\n\n function lp_token() external view returns (address);\n\n function manager() external view returns (address);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function period() external view returns (int128);\n\n function period_timestamp(uint256 arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function reward_count() external view returns (uint256);\n\n function reward_integral_for(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function reward_tokens(uint256 arg0) external view returns (address);\n\n function rewards_receiver(address arg0) external view returns (address);\n\n function salt() external view returns (bytes32);\n\n function set_gauge_manager(address _gauge_manager) external;\n\n function set_killed(bool _is_killed) external;\n\n function set_reward_distributor(address _reward_token, address _distributor)\n external;\n\n function set_rewards_receiver(address _receiver) external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function user_checkpoint(address addr) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw(uint256 _value) external;\n\n function withdraw(uint256 _value, bool _claim_rewards) external;\n\n function working_balances(address arg0) external view returns (uint256);\n\n function working_supply() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/ICurveMinter.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveMinter {\n event Minted(address indexed recipient, address gauge, uint256 minted);\n\n function allowed_to_mint_for(address arg0, address arg1)\n external\n view\n returns (bool);\n\n function controller() external view returns (address);\n\n function mint(address gauge_addr) external;\n\n function mint_for(address gauge_addr, address _for) external;\n\n function mint_many(address[8] memory gauge_addrs) external;\n\n function minted(address arg0, address arg1) external view returns (uint256);\n\n function toggle_approve_mint(address minting_user) external;\n\n function token() external view returns (address);\n}\n" + }, + "contracts/interfaces/ICurveStableSwapNG.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveStableSwapNG {\n event AddLiquidity(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event ApplyNewFee(uint256 fee, uint256 offpeg_fee_multiplier);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event RampA(\n uint256 old_A,\n uint256 new_A,\n uint256 initial_time,\n uint256 future_time\n );\n event RemoveLiquidity(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 token_supply\n );\n event RemoveLiquidityImbalance(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event RemoveLiquidityOne(\n address indexed provider,\n int128 token_id,\n uint256 token_amount,\n uint256 coin_amount,\n uint256 token_supply\n );\n event SetNewMATime(uint256 ma_exp_time, uint256 D_ma_time);\n event StopRampA(uint256 A, uint256 t);\n event TokenExchange(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event TokenExchangeUnderlying(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event Transfer(\n address indexed sender,\n address indexed receiver,\n uint256 value\n );\n\n function A() external view returns (uint256);\n\n function A_precise() external view returns (uint256);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function D_ma_time() external view returns (uint256);\n\n function D_oracle() external view returns (uint256);\n\n function N_COINS() external view returns (uint256);\n\n function add_liquidity(uint256[] memory _amounts, uint256 _min_mint_amount)\n external\n returns (uint256);\n\n function add_liquidity(\n uint256[] memory _amounts,\n uint256 _min_mint_amount,\n address _receiver\n ) external returns (uint256);\n\n function admin_balances(uint256 arg0) external view returns (uint256);\n\n function admin_fee() external view returns (uint256);\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function balances(uint256 i) external view returns (uint256);\n\n function calc_token_amount(uint256[] memory _amounts, bool _is_deposit)\n external\n view\n returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _burn_amount, int128 i)\n external\n view\n returns (uint256);\n\n function coins(uint256 arg0) external view returns (address);\n\n function decimals() external view returns (uint8);\n\n function dynamic_fee(int128 i, int128 j) external view returns (uint256);\n\n function ema_price(uint256 i) external view returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external returns (uint256);\n\n function exchange_received(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external returns (uint256);\n\n function exchange_received(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external returns (uint256);\n\n function fee() external view returns (uint256);\n\n function future_A() external view returns (uint256);\n\n function future_A_time() external view returns (uint256);\n\n function get_balances() external view returns (uint256[] memory);\n\n function get_dx(\n int128 i,\n int128 j,\n uint256 dy\n ) external view returns (uint256);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n\n function get_p(uint256 i) external view returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function initial_A() external view returns (uint256);\n\n function initial_A_time() external view returns (uint256);\n\n function last_price(uint256 i) external view returns (uint256);\n\n function ma_exp_time() external view returns (uint256);\n\n function ma_last_time() external view returns (uint256);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function offpeg_fee_multiplier() external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function price_oracle(uint256 i) external view returns (uint256);\n\n function ramp_A(uint256 _future_A, uint256 _future_time) external;\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts\n ) external returns (uint256[] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts,\n address _receiver\n ) external returns (uint256[] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts,\n address _receiver,\n bool _claim_admin_fees\n ) external returns (uint256[] memory);\n\n function remove_liquidity_imbalance(\n uint256[] memory _amounts,\n uint256 _max_burn_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[] memory _amounts,\n uint256 _max_burn_amount,\n address _receiver\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received,\n address _receiver\n ) external returns (uint256);\n\n function salt() external view returns (bytes32);\n\n function set_ma_exp_time(uint256 _ma_exp_time, uint256 _D_ma_time) external;\n\n function set_new_fee(uint256 _new_fee, uint256 _new_offpeg_fee_multiplier)\n external;\n\n function stop_ramp_A() external;\n\n function stored_rates() external view returns (uint256[] memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw_admin_fees() external;\n}\n" + }, + "contracts/interfaces/ICurveXChainLiquidityGauge.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveXChainLiquidityGauge {\n event Approval(\n address indexed _owner,\n address indexed _spender,\n uint256 _value\n );\n event Deposit(address indexed provider, uint256 value);\n event SetGaugeManager(address _gauge_manager);\n event Transfer(address indexed _from, address indexed _to, uint256 _value);\n event UpdateLiquidityLimit(\n address indexed user,\n uint256 original_balance,\n uint256 original_supply,\n uint256 working_balance,\n uint256 working_supply\n );\n event Withdraw(address indexed provider, uint256 value);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_reward(address _reward_token, address _distributor) external;\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function claim_rewards() external;\n\n function claim_rewards(address _addr) external;\n\n function claim_rewards(address _addr, address _receiver) external;\n\n function claimable_reward(address _user, address _reward_token)\n external\n view\n returns (uint256);\n\n function claimable_tokens(address addr) external returns (uint256);\n\n function claimed_reward(address _addr, address _token)\n external\n view\n returns (uint256);\n\n function decimals() external view returns (uint256);\n\n function decreaseAllowance(address _spender, uint256 _subtracted_value)\n external\n returns (bool);\n\n function deposit(uint256 _value) external;\n\n function deposit(uint256 _value, address _addr) external;\n\n function deposit(\n uint256 _value,\n address _addr,\n bool _claim_rewards\n ) external;\n\n function deposit_reward_token(address _reward_token, uint256 _amount)\n external;\n\n function deposit_reward_token(\n address _reward_token,\n uint256 _amount,\n uint256 _epoch\n ) external;\n\n function factory() external view returns (address);\n\n function increaseAllowance(address _spender, uint256 _added_value)\n external\n returns (bool);\n\n function inflation_rate(uint256 arg0) external view returns (uint256);\n\n function initialize(\n address _lp_token,\n address _root,\n address _manager\n ) external;\n\n function integrate_checkpoint() external view returns (uint256);\n\n function integrate_checkpoint_of(address arg0)\n external\n view\n returns (uint256);\n\n function integrate_fraction(address arg0) external view returns (uint256);\n\n function integrate_inv_supply(int128 arg0) external view returns (uint256);\n\n function integrate_inv_supply_of(address arg0)\n external\n view\n returns (uint256);\n\n function is_killed() external view returns (bool);\n\n function lp_token() external view returns (address);\n\n function manager() external view returns (address);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function period() external view returns (int128);\n\n function period_timestamp(int128 arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function recover_remaining(address _reward_token) external;\n\n function reward_count() external view returns (uint256);\n\n function reward_integral_for(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function reward_remaining(address arg0) external view returns (uint256);\n\n function reward_tokens(uint256 arg0) external view returns (address);\n\n function rewards_receiver(address arg0) external view returns (address);\n\n function root_gauge() external view returns (address);\n\n function set_gauge_manager(address _gauge_manager) external;\n\n function set_killed(bool _is_killed) external;\n\n function set_manager(address _gauge_manager) external;\n\n function set_reward_distributor(address _reward_token, address _distributor)\n external;\n\n function set_rewards_receiver(address _receiver) external;\n\n function set_root_gauge(address _root) external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function update_voting_escrow() external;\n\n function user_checkpoint(address addr) external returns (bool);\n\n function version() external view returns (string memory);\n\n function voting_escrow() external view returns (address);\n\n function withdraw(uint256 _value) external;\n\n function withdraw(uint256 _value, bool _claim_rewards) external;\n\n function withdraw(\n uint256 _value,\n bool _claim_rewards,\n address _receiver\n ) external;\n\n function working_balances(address arg0) external view returns (uint256);\n\n function working_supply() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/ICVXLocker.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface ICVXLocker {\n function lock(\n address _account,\n uint256 _amount,\n uint256 _spendRatio\n ) external;\n\n function lockedBalanceOf(address _account) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/IDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IDripper {\n /// @notice How much funds have dripped out already and are currently\n // available to be sent to the vault.\n /// @return The amount that would be sent if a collect was called\n function availableFunds() external view returns (uint256);\n\n /// @notice Collect all dripped funds and send to vault.\n /// Recalculate new drip rate.\n function collect() external;\n\n /// @notice Collect all dripped funds, send to vault, recalculate new drip\n /// rate, and rebase mToken.\n function collectAndRebase() external;\n\n /// @notice Change the drip duration. Governor only.\n /// @param _durationSeconds the number of seconds to drip out the entire\n /// balance over if no collects were called during that time.\n function setDripDuration(uint256 _durationSeconds) external;\n\n /// @dev Transfer out ERC20 tokens held by the contract. Governor only.\n /// @param _asset ERC20 token address\n /// @param _amount amount to transfer\n function transferToken(address _asset, uint256 _amount) external;\n}\n" + }, + "contracts/interfaces/IEthUsdOracle.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IEthUsdOracle {\n /**\n * @notice Returns ETH price in USD.\n * @return Price in USD with 6 decimal digits.\n */\n function ethUsdPrice() external view returns (uint256);\n\n /**\n * @notice Returns token price in USD.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in USD with 6 decimal digits.\n */\n function tokUsdPrice(string calldata symbol)\n external\n view\n returns (uint256);\n\n /**\n * @notice Returns the asset price in ETH.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in ETH with 8 decimal digits.\n */\n function tokEthPrice(string calldata symbol)\n external\n view\n returns (uint256);\n}\n\ninterface IViewEthUsdOracle {\n /**\n * @notice Returns ETH price in USD.\n * @return Price in USD with 6 decimal digits.\n */\n function ethUsdPrice() external view returns (uint256);\n\n /**\n * @notice Returns token price in USD.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in USD with 6 decimal digits.\n */\n function tokUsdPrice(string calldata symbol)\n external\n view\n returns (uint256);\n\n /**\n * @notice Returns the asset price in ETH.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in ETH with 8 decimal digits.\n */\n function tokEthPrice(string calldata symbol)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/IFraxETHMinter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IFraxETHMinter {\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares);\n}\n" + }, + "contracts/interfaces/IGetExchangeRateToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IGetExchangeRateToken {\n function getExchangeRate() external view returns (uint256 _exchangeRate);\n}\n" + }, + "contracts/interfaces/IMinMaxOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IMinMaxOracle {\n //Assuming 8 decimals\n function priceMin(string calldata symbol) external view returns (uint256);\n\n function priceMax(string calldata symbol) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IMockVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"./IVault.sol\";\n\ninterface IMockVault is IVault {\n function outstandingWithdrawalsAmount() external view returns (uint256);\n\n function wethAvailable() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IOETHZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOETHZapper {\n function deposit() external payable returns (uint256);\n}\n" + }, + "contracts/interfaces/IOneInch.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/// 1Inch swap data\nstruct SwapDescription {\n IERC20 srcToken; // contract address of a token to sell\n IERC20 dstToken; // contract address of a token to buy\n address payable srcReceiver;\n address payable dstReceiver; // Receiver of destination currency. default: fromAddress\n uint256 amount;\n uint256 minReturnAmount;\n uint256 flags;\n}\n\n/// @title Interface for making arbitrary calls during swap\ninterface IAggregationExecutor {\n /// @notice propagates information about original msg.sender and executes arbitrary data\n function execute(address msgSender) external payable; // 0x4b64e492\n}\n\ninterface IOneInchRouter {\n /// @notice Performs a swap, delegating all calls encoded in `data` to `executor`.\n function swap(\n IAggregationExecutor executor,\n SwapDescription calldata desc,\n bytes calldata permit,\n bytes calldata data\n ) external returns (uint256 returnAmount, uint256 spentAmount);\n\n /// @notice Performs swap using Uniswap exchange. Wraps and unwraps ETH if required.\n function unoswapTo(\n address payable recipient,\n IERC20 srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) external payable returns (uint256 returnAmount);\n\n /// @notice Performs swap using Uniswap V3 exchange. Wraps and unwraps ETH if required.\n function uniswapV3SwapTo(\n address payable recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) external payable returns (uint256 returnAmount);\n}\n" + }, + "contracts/interfaces/IOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOracle {\n /**\n * @dev returns the asset price in USD, in 8 decimal digits.\n *\n * The version of priceProvider deployed for OETH has 18 decimal digits\n */\n function price(address asset) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IOUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOUSD {\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n function _totalSupply() external view returns (uint256);\n\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address _account) external view returns (uint256);\n\n function burn(address account, uint256 amount) external;\n\n function changeSupply(uint256 _newTotalSupply) external;\n\n function claimGovernance() external;\n\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256);\n\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n );\n\n function decimals() external view returns (uint8);\n\n function decreaseAllowance(address _spender, uint256 _subtractedValue)\n external\n returns (bool);\n\n function governor() external view returns (address);\n\n function increaseAllowance(address _spender, uint256 _addedValue)\n external\n returns (bool);\n\n function initialize(\n string memory _nameArg,\n string memory _symbolArg,\n address _vaultAddress\n ) external;\n\n function isGovernor() external view returns (bool);\n\n function isUpgraded(address) external view returns (uint256);\n\n function mint(address _account, uint256 _amount) external;\n\n function name() external view returns (string memory);\n\n function nonRebasingCreditsPerToken(address)\n external\n view\n returns (uint256);\n\n function nonRebasingSupply() external view returns (uint256);\n\n function rebaseOptIn() external;\n\n function rebaseOptOut() external;\n\n function rebaseState(address) external view returns (uint8);\n\n function rebasingCredits() external view returns (uint256);\n\n function rebasingCreditsHighres() external view returns (uint256);\n\n function rebasingCreditsPerToken() external view returns (uint256);\n\n function rebasingCreditsPerTokenHighres() external view returns (uint256);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function transferGovernance(address _newGovernor) external;\n\n function vaultAddress() external view returns (address);\n}\n" + }, + "contracts/interfaces/IPriceOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPriceOracle {\n /**\n * @dev returns the asset price in USD, 6 decimal digits.\n * Compatible with the Open Price Feed.\n */\n function price(string calldata symbol) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IRETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IRETH {\n function getEthValue(uint256 _rethAmount) external view returns (uint256);\n\n function getRethValue(uint256 _ethAmount) external view returns (uint256);\n\n function getExchangeRate() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function balanceOf(address account) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 amount) external returns (bool);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/ISafe.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ISafe {\n function execTransactionFromModule(\n address,\n uint256,\n bytes memory,\n uint8\n ) external returns (bool);\n}\n" + }, + "contracts/interfaces/ISfrxETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface ISfrxETH {\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 amount\n );\n event Deposit(\n address indexed caller,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n event NewRewardsCycle(uint32 indexed cycleEnd, uint256 rewardAmount);\n event Transfer(address indexed from, address indexed to, uint256 amount);\n event Withdraw(\n address indexed caller,\n address indexed receiver,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address spender, uint256 amount) external returns (bool);\n\n function asset() external view returns (address);\n\n function balanceOf(address) external view returns (uint256);\n\n function convertToAssets(uint256 shares) external view returns (uint256);\n\n function convertToShares(uint256 assets) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit(uint256 assets, address receiver)\n external\n returns (uint256 shares);\n\n function depositWithSignature(\n uint256 assets,\n address receiver,\n uint256 deadline,\n bool approveMax,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external returns (uint256 shares);\n\n function lastRewardAmount() external view returns (uint192);\n\n function lastSync() external view returns (uint32);\n\n function maxDeposit(address) external view returns (uint256);\n\n function maxMint(address) external view returns (uint256);\n\n function maxRedeem(address owner) external view returns (uint256);\n\n function maxWithdraw(address owner) external view returns (uint256);\n\n function mint(uint256 shares, address receiver)\n external\n returns (uint256 assets);\n\n function name() external view returns (string memory);\n\n function nonces(address) external view returns (uint256);\n\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function previewDeposit(uint256 assets) external view returns (uint256);\n\n function previewMint(uint256 shares) external view returns (uint256);\n\n function previewRedeem(uint256 shares) external view returns (uint256);\n\n function previewWithdraw(uint256 assets) external view returns (uint256);\n\n function pricePerShare() external view returns (uint256);\n\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets);\n\n function rewardsCycleEnd() external view returns (uint32);\n\n function rewardsCycleLength() external view returns (uint32);\n\n function symbol() external view returns (string memory);\n\n function syncRewards() external;\n\n function totalAssets() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address to, uint256 amount) external returns (bool);\n\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/ISwapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ISwapper {\n /**\n * @param fromAsset The token address of the asset being sold.\n * @param toAsset The token address of the asset being purchased.\n * @param fromAssetAmount The amount of assets being sold.\n * @param minToAssetAmmount The minimum amount of assets to be purchased.\n * @param data tx.data returned from 1Inch's /v5.0/1/swap API\n */\n function swap(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n}\n" + }, + "contracts/interfaces/ITimelock.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ITimelock {\n function delay() external view returns (uint256);\n\n function GRACE_PERIOD() external view returns (uint256);\n\n function acceptAdmin() external;\n\n function queuedTransactions(bytes32 hash) external view returns (bool);\n\n function queueTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external returns (bytes32);\n\n function cancelTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external;\n\n function executeTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external payable returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ITimelockController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ITimelockController {\n function grantRole(bytes32 role, address account) external;\n\n function revokeRole(bytes32 role, address account) external;\n\n function renounceRole(bytes32 role, address account) external;\n\n function hasRole(bytes32 role, address account)\n external\n view\n returns (bool);\n\n function executeBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt\n ) external payable;\n\n function scheduleBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt,\n uint256 delay\n ) external;\n\n function hashOperationBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt\n ) external view returns (bytes32);\n\n function isOperationDone(bytes32 opHash) external view returns (bool);\n\n function isOperationReady(bytes32 opHash) external view returns (bool);\n\n function isOperation(bytes32 opHash) external view returns (bool);\n\n function getMinDelay() external view returns (uint256);\n\n function updateDelay(uint256 newDelay) external;\n\n function CANCELLER_ROLE() external view returns (bytes32);\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/interfaces/IWstETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWstETH {\n /**\n * @notice Get amount of wstETH for a given amount of stETH\n * @param _stETHAmount amount of stETH\n * @return Amount of wstETH for a given stETH amount\n */\n function getWstETHByStETH(uint256 _stETHAmount)\n external\n view\n returns (uint256);\n\n /**\n * @notice Get amount of stETH for a given amount of wstETH\n * @param _wstETHAmount amount of wstETH\n * @return Amount of stETH for a given wstETH amount\n */\n function getStETHByWstETH(uint256 _wstETHAmount)\n external\n view\n returns (uint256);\n\n /**\n * @notice Get amount of stETH for a one wstETH\n * @return Amount of stETH for 1 wstETH\n */\n function stEthPerToken() external view returns (uint256);\n\n /**\n * @notice Get amount of wstETH for a one stETH\n * @return Amount of wstETH for a 1 stETH\n */\n function tokensPerStEth() external view returns (uint256);\n\n /**\n * @notice Exchanges stETH to wstETH\n * @param _stETHAmount amount of stETH to wrap in exchange for wstETH\n * @dev Requirements:\n * - `_stETHAmount` must be non-zero\n * - msg.sender must approve at least `_stETHAmount` stETH to this\n * contract.\n * - msg.sender must have at least `_stETHAmount` of stETH.\n * User should first approve _stETHAmount to the WstETH contract\n * @return Amount of wstETH user receives after wrap\n */\n function wrap(uint256 _stETHAmount) external returns (uint256);\n\n /**\n * @notice Exchanges wstETH to stETH\n * @param _wstETHAmount amount of wstETH to uwrap in exchange for stETH\n * @dev Requirements:\n * - `_wstETHAmount` must be non-zero\n * - msg.sender must have at least `_wstETHAmount` wstETH.\n * @return Amount of stETH user receives after unwrap\n */\n function unwrap(uint256 _wstETHAmount) external returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/compound/ICompoundOracle.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\ninterface ICompoundOracle {\n function getUnderlyingPrice(address) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/ILens.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\nimport \"./compound/ICompoundOracle.sol\";\nimport \"./IMorpho.sol\";\n\ninterface ILens {\n /// STORAGE ///\n\n function MAX_BASIS_POINTS() external view returns (uint256);\n\n function WAD() external view returns (uint256);\n\n function morpho() external view returns (IMorpho);\n\n function comptroller() external view returns (IComptroller);\n\n /// GENERAL ///\n\n function getTotalSupply()\n external\n view\n returns (\n uint256 p2pSupplyAmount,\n uint256 poolSupplyAmount,\n uint256 totalSupplyAmount\n );\n\n function getTotalBorrow()\n external\n view\n returns (\n uint256 p2pBorrowAmount,\n uint256 poolBorrowAmount,\n uint256 totalBorrowAmount\n );\n\n /// MARKETS ///\n\n function isMarketCreated(address _poolToken) external view returns (bool);\n\n function isMarketCreatedAndNotPaused(address _poolToken)\n external\n view\n returns (bool);\n\n function isMarketCreatedAndNotPausedNorPartiallyPaused(address _poolToken)\n external\n view\n returns (bool);\n\n function getAllMarkets()\n external\n view\n returns (address[] memory marketsCreated_);\n\n function getMainMarketData(address _poolToken)\n external\n view\n returns (\n uint256 avgSupplyRatePerBlock,\n uint256 avgBorrowRatePerBlock,\n uint256 p2pSupplyAmount,\n uint256 p2pBorrowAmount,\n uint256 poolSupplyAmount,\n uint256 poolBorrowAmount\n );\n\n function getAdvancedMarketData(address _poolToken)\n external\n view\n returns (\n uint256 p2pSupplyIndex,\n uint256 p2pBorrowIndex,\n uint256 poolSupplyIndex,\n uint256 poolBorrowIndex,\n uint32 lastUpdateBlockNumber,\n uint256 p2pSupplyDelta,\n uint256 p2pBorrowDelta\n );\n\n function getMarketConfiguration(address _poolToken)\n external\n view\n returns (\n address underlying,\n bool isCreated,\n bool p2pDisabled,\n bool isPaused,\n bool isPartiallyPaused,\n uint16 reserveFactor,\n uint16 p2pIndexCursor,\n uint256 collateralFactor\n );\n\n function getTotalMarketSupply(address _poolToken)\n external\n view\n returns (uint256 p2pSupplyAmount, uint256 poolSupplyAmount);\n\n function getTotalMarketBorrow(address _poolToken)\n external\n view\n returns (uint256 p2pBorrowAmount, uint256 poolBorrowAmount);\n\n /// INDEXES ///\n\n function getCurrentP2PSupplyIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentP2PBorrowIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentPoolIndexes(address _poolToken)\n external\n view\n returns (\n uint256 currentPoolSupplyIndex,\n uint256 currentPoolBorrowIndex\n );\n\n function getIndexes(address _poolToken, bool _computeUpdatedIndexes)\n external\n view\n returns (\n uint256 p2pSupplyIndex,\n uint256 p2pBorrowIndex,\n uint256 poolSupplyIndex,\n uint256 poolBorrowIndex\n );\n\n /// USERS ///\n\n function getEnteredMarkets(address _user)\n external\n view\n returns (address[] memory enteredMarkets);\n\n function getUserHealthFactor(\n address _user,\n address[] calldata _updatedMarkets\n ) external view returns (uint256);\n\n function getUserBalanceStates(\n address _user,\n address[] calldata _updatedMarkets\n )\n external\n view\n returns (\n uint256 collateralValue,\n uint256 debtValue,\n uint256 maxDebtValue\n );\n\n function getCurrentSupplyBalanceInOf(address _poolToken, address _user)\n external\n view\n returns (\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getCurrentBorrowBalanceInOf(address _poolToken, address _user)\n external\n view\n returns (\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getUserMaxCapacitiesForAsset(address _user, address _poolToken)\n external\n view\n returns (uint256 withdrawable, uint256 borrowable);\n\n function getUserHypotheticalBalanceStates(\n address _user,\n address _poolToken,\n uint256 _withdrawnAmount,\n uint256 _borrowedAmount\n ) external view returns (uint256 debtValue, uint256 maxDebtValue);\n\n function getUserLiquidityDataForAsset(\n address _user,\n address _poolToken,\n bool _computeUpdatedIndexes,\n ICompoundOracle _oracle\n ) external view returns (Types.AssetLiquidityData memory assetData);\n\n function isLiquidatable(address _user, address[] memory _updatedMarkets)\n external\n view\n returns (bool);\n\n function computeLiquidationRepayAmount(\n address _user,\n address _poolTokenBorrowed,\n address _poolTokenCollateral,\n address[] calldata _updatedMarkets\n ) external view returns (uint256 toRepay);\n\n /// RATES ///\n\n function getAverageSupplyRatePerBlock(address _poolToken)\n external\n view\n returns (\n uint256 avgSupplyRatePerBlock,\n uint256 p2pSupplyAmount,\n uint256 poolSupplyAmount\n );\n\n function getAverageBorrowRatePerBlock(address _poolToken)\n external\n view\n returns (\n uint256 avgBorrowRatePerBlock,\n uint256 p2pBorrowAmount,\n uint256 poolBorrowAmount\n );\n\n function getNextUserSupplyRatePerBlock(\n address _poolToken,\n address _user,\n uint256 _amount\n )\n external\n view\n returns (\n uint256 nextSupplyRatePerBlock,\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getNextUserBorrowRatePerBlock(\n address _poolToken,\n address _user,\n uint256 _amount\n )\n external\n view\n returns (\n uint256 nextBorrowRatePerBlock,\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getCurrentUserSupplyRatePerBlock(address _poolToken, address _user)\n external\n view\n returns (uint256);\n\n function getCurrentUserBorrowRatePerBlock(address _poolToken, address _user)\n external\n view\n returns (uint256);\n\n function getRatesPerBlock(address _poolToken)\n external\n view\n returns (\n uint256 p2pSupplyRate,\n uint256 p2pBorrowRate,\n uint256 poolSupplyRate,\n uint256 poolBorrowRate\n );\n\n /// REWARDS ///\n\n function getUserUnclaimedRewards(\n address[] calldata _poolTokens,\n address _user\n ) external view returns (uint256 unclaimedRewards);\n\n function getAccruedSupplierComp(\n address _supplier,\n address _poolToken,\n uint256 _balance\n ) external view returns (uint256);\n\n function getAccruedBorrowerComp(\n address _borrower,\n address _poolToken,\n uint256 _balance\n ) external view returns (uint256);\n\n function getCurrentCompSupplyIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentCompBorrowIndex(address _poolToken)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/IMorpho.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\nimport \"./Types.sol\";\nimport \"../IComptroller.sol\";\nimport \"./compound/ICompoundOracle.sol\";\n\n// prettier-ignore\ninterface IMorpho {\n function comptroller() external view returns (IComptroller);\n function supply(address _poolTokenAddress, address _onBehalf, uint256 _amount) external;\n function supply(address _poolTokenAddress, address _onBehalf, uint256 _amount, uint256 _maxGasForMatching) external;\n function withdraw(address _poolTokenAddress, uint256 _amount) external;\n function claimRewards(\n address[] calldata _cTokenAddresses,\n bool _tradeForMorphoToken\n ) external returns (uint256 claimedAmount);\n}\n" + }, + "contracts/interfaces/morpho/Types.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\n/// @title Types.\n/// @author Morpho Labs.\n/// @custom:contact security@morpho.xyz\n/// @dev Common types and structs used in Moprho contracts.\nlibrary Types {\n /// ENUMS ///\n\n enum PositionType {\n SUPPLIERS_IN_P2P,\n SUPPLIERS_ON_POOL,\n BORROWERS_IN_P2P,\n BORROWERS_ON_POOL\n }\n\n /// STRUCTS ///\n\n struct SupplyBalance {\n uint256 inP2P; // In supplier's peer-to-peer unit, a unit that grows in underlying value, to keep track of the interests earned by suppliers in peer-to-peer. Multiply by the peer-to-peer supply index to get the underlying amount.\n uint256 onPool; // In cToken. Multiply by the pool supply index to get the underlying amount.\n }\n\n struct BorrowBalance {\n uint256 inP2P; // In borrower's peer-to-peer unit, a unit that grows in underlying value, to keep track of the interests paid by borrowers in peer-to-peer. Multiply by the peer-to-peer borrow index to get the underlying amount.\n uint256 onPool; // In cdUnit, a unit that grows in value, to keep track of the debt increase when borrowers are on Compound. Multiply by the pool borrow index to get the underlying amount.\n }\n\n // Max gas to consume during the matching process for supply, borrow, withdraw and repay functions.\n struct MaxGasForMatching {\n uint64 supply;\n uint64 borrow;\n uint64 withdraw;\n uint64 repay;\n }\n\n struct Delta {\n uint256 p2pSupplyDelta; // Difference between the stored peer-to-peer supply amount and the real peer-to-peer supply amount (in pool supply unit).\n uint256 p2pBorrowDelta; // Difference between the stored peer-to-peer borrow amount and the real peer-to-peer borrow amount (in pool borrow unit).\n uint256 p2pSupplyAmount; // Sum of all stored peer-to-peer supply (in peer-to-peer supply unit).\n uint256 p2pBorrowAmount; // Sum of all stored peer-to-peer borrow (in peer-to-peer borrow unit).\n }\n\n struct AssetLiquidityData {\n uint256 collateralValue; // The collateral value of the asset.\n uint256 maxDebtValue; // The maximum possible debt value of the asset.\n uint256 debtValue; // The debt value of the asset.\n uint256 underlyingPrice; // The price of the token.\n uint256 collateralFactor; // The liquidation threshold applied on this token.\n }\n\n struct LiquidityData {\n uint256 collateralValue; // The collateral value.\n uint256 maxDebtValue; // The maximum debt value possible.\n uint256 debtValue; // The debt value.\n }\n\n // Variables are packed together to save gas (will not exceed their limit during Morpho's lifetime).\n struct LastPoolIndexes {\n uint32 lastUpdateBlockNumber; // The last time the peer-to-peer indexes were updated.\n uint112 lastSupplyPoolIndex; // Last pool supply index.\n uint112 lastBorrowPoolIndex; // Last pool borrow index.\n }\n\n struct MarketParameters {\n uint16 reserveFactor; // Proportion of the interest earned by users sent to the DAO for each market, in basis point (100% = 10 000). The value is set at market creation.\n uint16 p2pIndexCursor; // Position of the peer-to-peer rate in the pool's spread. Determine the weights of the weighted arithmetic average in the indexes computations ((1 - p2pIndexCursor) * r^S + p2pIndexCursor * r^B) (in basis point).\n }\n\n struct MarketStatus {\n bool isCreated; // Whether or not this market is created.\n bool isPaused; // Whether the market is paused or not (all entry points on Morpho are frozen; supply, borrow, withdraw, repay and liquidate).\n bool isPartiallyPaused; // Whether the market is partially paused or not (only supply and borrow are frozen).\n }\n}\n" + }, + "contracts/interfaces/plume/IFeeRegistry.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\ninterface IFeeRegistry {\n function registerFee(\n bool isTokenA,\n uint32 binId,\n uint256 binFeeInQuote\n ) external;\n}\n" + }, + "contracts/interfaces/plume/ILiquidityRegistry.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface ILiquidityRegistry {\n function notifyBinLiquidity(\n IMaverickV2Pool pool,\n uint256 tokenId,\n uint32 binId,\n uint256 currentBinLpBalance\n ) external;\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Factory.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IFeeRegistry } from \"./IFeeRegistry.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2Factory {\n error FactorAlreadyInitialized();\n error FactorNotInitialized();\n error FactoryInvalidTokenOrder(IERC20 _tokenA, IERC20 _tokenB);\n error FactoryInvalidFee();\n error FactoryInvalidKinds(uint8 kinds);\n error FactoryInvalidTickSpacing(uint256 tickSpacing);\n error FactoryInvalidLookback(uint256 lookback);\n error FactoryInvalidTokenDecimals(uint8 decimalsA, uint8 decimalsB);\n error FactoryPoolAlreadyExists(\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds,\n address accessor\n );\n error FactoryAccessorMustBeNonZero();\n error NotImplemented();\n\n event PoolCreated(\n IMaverickV2Pool poolAddress,\n uint8 protocolFeeRatio,\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n int32 activeTick,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds,\n address accessor\n );\n event SetFactoryProtocolFeeReceiver(address receiver);\n event SetFactoryProtocolFeeRegistry(IFeeRegistry registry);\n\n struct DeployParameters {\n uint64 feeAIn;\n uint64 feeBIn;\n uint32 lookback;\n int32 activeTick;\n uint64 tokenAScale;\n uint64 tokenBScale;\n // slot\n IERC20 tokenA;\n // slot\n IERC20 tokenB;\n // slot\n uint16 tickSpacing;\n uint8 options;\n address accessor;\n }\n\n /**\n * @notice Called by deployer library to initialize a pool.\n */\n function deployParameters()\n external\n view\n returns (\n uint64 feeAIn,\n uint64 feeBIn,\n uint32 lookback,\n int32 activeTick,\n uint64 tokenAScale,\n uint64 tokenBScale,\n // slot\n IERC20 tokenA,\n // slot\n IERC20 tokenB,\n // slot\n uint16 tickSpacing,\n uint8 options,\n address accessor\n );\n\n /**\n * @notice Create a new MaverickV2Pool with symmetric swap fees.\n * @param fee Fraction of the pool swap amount that is retained as an LP in\n * D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * E.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function create(\n uint64 fee,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external returns (IMaverickV2Pool);\n\n /**\n * @notice Create a new MaverickV2Pool.\n * @param feeAIn Fraction of the pool swap amount for tokenA-input swaps\n * that is retained as an LP in D18 scale.\n * @param feeBIn Fraction of the pool swap amount for tokenB-input swaps\n * that is retained as an LP in D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * e.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function create(\n uint64 feeAIn,\n uint64 feeBIn,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external returns (IMaverickV2Pool);\n\n /**\n * @notice Bool indicating whether the pool was deployed from this factory.\n */\n function isFactoryPool(IMaverickV2Pool pool) external view returns (bool);\n\n /**\n * @notice Address that receives the protocol fee\n */\n function protocolFeeReceiver() external view returns (address);\n\n /**\n * @notice Address notified on swaps of the protocol fee\n */\n function protocolFeeRegistry() external view returns (IFeeRegistry);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds\n ) external view returns (IMaverickV2Pool);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(\n IERC20 _tokenA,\n IERC20 _tokenB,\n uint256 startIndex,\n uint256 endIndex\n ) external view returns (IMaverickV2Pool[] memory pools);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(uint256 startIndex, uint256 endIndex)\n external\n view\n returns (IMaverickV2Pool[] memory pools);\n\n /**\n * @notice Count of permissionless pools.\n */\n function poolCount() external view returns (uint256 _poolCount);\n\n /**\n * @notice Count of pools for a given accessor and token pair. For\n * permissionless pools, pass `accessor = address(0)`.\n */\n function poolByTokenCount(\n IERC20 _tokenA,\n IERC20 _tokenB,\n address accessor\n ) external view returns (uint256 _poolCount);\n\n /**\n * @notice Get the current factory owner.\n */\n function owner() external view returns (address);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2LiquidityManager.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\nimport { IMaverickV2Position } from \"./IMaverickV2Position.sol\";\nimport { IMaverickV2PoolLens } from \"./IMaverickV2PoolLens.sol\";\n\ninterface IMaverickV2LiquidityManager {\n error LiquidityManagerNotFactoryPool();\n error LiquidityManagerNotTokenIdOwner();\n\n /**\n * @notice Maverick V2 NFT position contract that tracks NFT-based\n * liquditiy positions.\n */\n function position() external view returns (IMaverickV2Position);\n\n /**\n * @notice Create Maverick V2 pool. Function is a pass through to the pool\n * factory and is provided here so that is can be assembled as part of a\n * multicall transaction.\n */\n function createPool(\n uint64 fee,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external payable returns (IMaverickV2Pool pool);\n\n /**\n * @notice Add Liquidity position NFT for msg.sender by specifying\n * msg.sender's token index.\n * @dev Token index is different from tokenId.\n * On the Position NFT contract a user can own multiple NFT tokenIds and\n * these are indexes by an enumeration index which is the `index` input\n * here.\n *\n * See addLiquidity for a description of the add params.\n */\n function addPositionLiquidityToSenderByTokenIndex(\n IMaverickV2Pool pool,\n uint256 index,\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs\n )\n external\n payable\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds\n );\n\n /**\n * @notice Mint new tokenId in the Position NFt contract to msg.sender.\n * Both mints an NFT and adds liquidity to the pool that is held by the\n * NFT.\n */\n function mintPositionNftToSender(\n IMaverickV2Pool pool,\n bytes calldata packedSqrtPriceBreaks,\n bytes[] calldata packedArgs\n )\n external\n payable\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds,\n uint256 tokenId\n );\n\n /**\n * @notice Donates liqudity to a pool that is held by the position contract\n * and will never be retrievable. Can be used to start a pool and ensure\n * there will always be a base level of liquditiy in the pool.\n */\n function donateLiquidity(\n IMaverickV2Pool pool,\n IMaverickV2Pool.AddLiquidityParams memory args\n ) external payable;\n\n /**\n * @notice Packs sqrtPrice breaks array with this format: [length,\n * array[0], array[1],..., array[length-1]] where length is 1 byte.\n */\n function packUint88Array(uint88[] memory fullArray)\n external\n pure\n returns (bytes memory packedArray);\n\n /**\n * @notice Packs addLiquidity paramters array element-wise.\n */\n function packAddLiquidityArgsArray(\n IMaverickV2Pool.AddLiquidityParams[] memory args\n ) external pure returns (bytes[] memory argsPacked);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Pool.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\n\ninterface IMaverickV2Pool {\n error PoolZeroLiquidityAdded();\n error PoolMinimumLiquidityNotMet();\n error PoolLocked();\n error PoolInvalidFee();\n error PoolTicksNotSorted(uint256 index, int256 previousTick, int256 tick);\n error PoolTicksAmountsLengthMismatch(\n uint256 ticksLength,\n uint256 amountsLength\n );\n error PoolBinIdsAmountsLengthMismatch(\n uint256 binIdsLength,\n uint256 amountsLength\n );\n error PoolKindNotSupported(uint256 kinds, uint256 kind);\n error PoolInsufficientBalance(\n uint256 deltaLpAmount,\n uint256 accountBalance\n );\n error PoolReservesExceedMaximum(uint256 amount);\n error PoolValueExceedsBits(uint256 amount, uint256 bits);\n error PoolTickMaxExceeded(uint256 tick);\n error PoolMigrateBinFirst();\n error PoolCurrentTickBeyondSwapLimit(int32 startingTick);\n error PoolSenderNotAccessor(address sender_, address accessor);\n error PoolSenderNotFactory(address sender_, address accessor);\n error PoolFunctionNotImplemented();\n error PoolTokenNotSolvent(\n uint256 internalReserve,\n uint256 tokenBalance,\n IERC20 token\n );\n error PoolNoProtocolFeeReceiverSet();\n\n event PoolSwap(\n address sender,\n address recipient,\n SwapParams params,\n uint256 amountIn,\n uint256 amountOut\n );\n event PoolFlashLoan(\n address sender,\n address recipient,\n uint256 amountA,\n uint256 amountB\n );\n event PoolProtocolFeeCollected(IERC20 token, uint256 protocolFee);\n\n event PoolAddLiquidity(\n address sender,\n address recipient,\n uint256 subaccount,\n AddLiquidityParams params,\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] binIds\n );\n\n event PoolMigrateBinsUpStack(\n address sender,\n uint32 binId,\n uint32 maxRecursion\n );\n\n event PoolRemoveLiquidity(\n address sender,\n address recipient,\n uint256 subaccount,\n RemoveLiquidityParams params,\n uint256 tokenAOut,\n uint256 tokenBOut\n );\n\n event PoolTickState(int32 tick, uint256 reserveA, uint256 reserveB);\n event PoolTickBinUpdate(int32 tick, uint8 kind, uint32 binId);\n event PoolSqrtPrice(uint256 sqrtPrice);\n\n /**\n * @notice Tick state parameters.\n */\n struct TickState {\n uint128 reserveA;\n uint128 reserveB;\n uint128 totalSupply;\n uint32[4] binIdsByTick;\n }\n\n /**\n * @notice Tick data parameters.\n * @param currentReserveA Current reserve of token A.\n * @param currentReserveB Current reserve of token B.\n * @param currentLiquidity Current liquidity amount.\n */\n struct TickData {\n uint256 currentReserveA;\n uint256 currentReserveB;\n uint256 currentLiquidity;\n }\n\n /**\n * @notice Bin state parameters.\n * @param mergeBinBalance LP token balance that this bin possesses of the merge bin.\n * @param mergeId Bin ID of the bin that this bin has merged into.\n * @param totalSupply Total amount of LP tokens in this bin.\n * @param kind One of the 4 kinds (0=static, 1=right, 2=left, 3=both).\n * @param tick The lower price tick of the bin in its current state.\n * @param tickBalance Balance of the tick.\n */\n struct BinState {\n uint128 mergeBinBalance;\n uint128 tickBalance;\n uint128 totalSupply;\n uint8 kind;\n int32 tick;\n uint32 mergeId;\n }\n\n /**\n * @notice Parameters for swap.\n * @param amount Amount of the token that is either the input if exactOutput is false\n * or the output if exactOutput is true.\n * @param tokenAIn Boolean indicating whether tokenA is the input.\n * @param exactOutput Boolean indicating whether the amount specified is\n * the exact output amount (true).\n * @param tickLimit The furthest tick a swap will execute in. If no limit\n * is desired, value should be set to type(int32).max for a tokenAIn swap\n * and type(int32).min for a swap where tokenB is the input.\n */\n struct SwapParams {\n uint256 amount;\n bool tokenAIn;\n bool exactOutput;\n int32 tickLimit;\n }\n\n /**\n * @notice Parameters associated with adding liquidity.\n * @param kind One of the 4 kinds (0=static, 1=right, 2=left, 3=both).\n * @param ticks Array of ticks to add liquidity to.\n * @param amounts Array of bin LP amounts to add.\n */\n struct AddLiquidityParams {\n uint8 kind;\n int32[] ticks;\n uint128[] amounts;\n }\n\n /**\n * @notice Parameters for each bin that will have liquidity removed.\n * @param binIds Index array of the bins losing liquidity.\n * @param amounts Array of bin LP amounts to remove.\n */\n struct RemoveLiquidityParams {\n uint32[] binIds;\n uint128[] amounts;\n }\n\n /**\n * @notice State of the pool.\n * @param reserveA Pool tokenA balanceOf at end of last operation\n * @param reserveB Pool tokenB balanceOf at end of last operation\n * @param lastTwaD8 Value of log time weighted average price at last block.\n * Value is 8-decimal scale and is in the fractional tick domain. E.g. a\n * value of 12.3e8 indicates the TWAP was 3/10ths of the way into the 12th\n * tick.\n * @param lastLogPriceD8 Value of log price at last block. Value is\n * 8-decimal scale and is in the fractional tick domain. E.g. a value of\n * 12.3e8 indicates the price was 3/10ths of the way into the 12th tick.\n * @param lastTimestamp Last block.timestamp value in seconds for latest\n * swap transaction.\n * @param activeTick Current tick position that contains the active bins.\n * @param isLocked Pool isLocked, E.g., locked or unlocked; isLocked values\n * defined in Pool.sol.\n * @param binCounter Index of the last bin created.\n * @param protocolFeeRatioD3 Ratio of the swap fee that is kept for the\n * protocol.\n */\n struct State {\n uint128 reserveA;\n uint128 reserveB;\n int64 lastTwaD8;\n int64 lastLogPriceD8;\n uint40 lastTimestamp;\n int32 activeTick;\n bool isLocked;\n uint32 binCounter;\n uint8 protocolFeeRatioD3;\n }\n\n /**\n * @notice Internal data used for data passing between Pool and Bin code.\n */\n struct BinDelta {\n uint128 deltaA;\n uint128 deltaB;\n }\n\n /**\n * @notice 1-15 number to represent the active kinds.\n * @notice 0b0001 = static;\n * @notice 0b0010 = right;\n * @notice 0b0100 = left;\n * @notice 0b1000 = both;\n *\n * E.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function kinds() external view returns (uint8 _kinds);\n\n /**\n * @notice Pool swap fee for the given direction (A-in or B-in swap) in\n * 18-decimal format. E.g. 0.01e18 is a 1% swap fee.\n */\n function fee(bool tokenAIn) external view returns (uint256);\n\n /**\n * @notice TickSpacing of pool where 1.0001^tickSpacing is the bin width.\n */\n function tickSpacing() external view returns (uint256);\n\n /**\n * @notice Lookback period of pool in seconds.\n */\n function lookback() external view returns (uint256);\n\n /**\n * @notice Address of Pool accessor. This is Zero address for\n * permissionless pools.\n */\n function accessor() external view returns (address);\n\n /**\n * @notice Pool tokenA. Address of tokenA is such that tokenA < tokenB.\n */\n function tokenA() external view returns (IERC20);\n\n /**\n * @notice Pool tokenB.\n */\n function tokenB() external view returns (IERC20);\n\n /**\n * @notice Deploying factory of the pool and also contract that has ability\n * to set and collect protocol fees for the pool.\n */\n function factory() external view returns (IMaverickV2Factory);\n\n /**\n * @notice Most significant bit of scale value is a flag to indicate whether\n * tokenA has more or less than 18 decimals. Scale is used in conjuction\n * with Math.toScale/Math.fromScale functions to convert from token amounts\n * to D18 scale internal pool accounting.\n */\n function tokenAScale() external view returns (uint256);\n\n /**\n * @notice Most significant bit of scale value is a flag to indicate whether\n * tokenA has more or less than 18 decimals. Scale is used in conjuction\n * with Math.toScale/Math.fromScale functions to convert from token amounts\n * to D18 scale internal pool accounting.\n */\n function tokenBScale() external view returns (uint256);\n\n /**\n * @notice ID of bin at input tick position and kind.\n */\n function binIdByTickKind(int32 tick, uint256 kind)\n external\n view\n returns (uint32);\n\n /**\n * @notice Accumulated tokenA protocol fee.\n */\n function protocolFeeA() external view returns (uint128);\n\n /**\n * @notice Accumulated tokenB protocol fee.\n */\n function protocolFeeB() external view returns (uint128);\n\n /**\n * @notice Lending fee rate on flash loans.\n */\n function lendingFeeRateD18() external view returns (uint256);\n\n /**\n * @notice External function to get the current time-weighted average price.\n */\n function getCurrentTwa() external view returns (int256);\n\n /**\n * @notice External function to get the state of the pool.\n */\n function getState() external view returns (State memory);\n\n /**\n * @notice Return state of Bin at input binId.\n */\n function getBin(uint32 binId) external view returns (BinState memory bin);\n\n /**\n * @notice Return state of Tick at input tick position.\n */\n function getTick(int32 tick)\n external\n view\n returns (TickState memory tickState);\n\n /**\n * @notice Retrieves the balance of a user within a bin.\n * @param user The user's address.\n * @param subaccount The subaccount for the user.\n * @param binId The ID of the bin.\n */\n function balanceOf(\n address user,\n uint256 subaccount,\n uint32 binId\n ) external view returns (uint128 lpToken);\n\n /**\n * @notice Add liquidity to a pool. This function allows users to deposit\n * tokens into a liquidity pool.\n * @dev This function will call `maverickV2AddLiquidityCallback` on the\n * calling contract to collect the tokenA/tokenB payment.\n * @param recipient The account that will receive credit for the added liquidity.\n * @param subaccount The account that will receive credit for the added liquidity.\n * @param params Parameters containing the details for adding liquidity,\n * such as token types and amounts.\n * @param data Bytes information that gets passed to the callback.\n * @return tokenAAmount The amount of token A added to the pool.\n * @return tokenBAmount The amount of token B added to the pool.\n * @return binIds An array of bin IDs where the liquidity is stored.\n */\n function addLiquidity(\n address recipient,\n uint256 subaccount,\n AddLiquidityParams calldata params,\n bytes calldata data\n )\n external\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds\n );\n\n /**\n * @notice Removes liquidity from the pool.\n * @dev Liquidy can only be removed from a bin that is either unmerged or\n * has a mergeId of an unmerged bin. If a bin is merged more than one\n * level deep, it must be migrated up the merge stack to the root bin\n * before liquidity removal.\n * @param recipient The address to receive the tokens.\n * @param subaccount The subaccount for the recipient.\n * @param params The parameters for removing liquidity.\n * @return tokenAOut The amount of token A received.\n * @return tokenBOut The amount of token B received.\n */\n function removeLiquidity(\n address recipient,\n uint256 subaccount,\n RemoveLiquidityParams calldata params\n ) external returns (uint256 tokenAOut, uint256 tokenBOut);\n\n /**\n * @notice Migrate bins up the linked list of merged bins so that its\n * mergeId is the currrent active bin.\n * @dev Liquidy can only be removed from a bin that is either unmerged or\n * has a mergeId of an unmerged bin. If a bin is merged more than one\n * level deep, it must be migrated up the merge stack to the root bin\n * before liquidity removal.\n * @param binId The ID of the bin to migrate.\n * @param maxRecursion The maximum recursion depth for the migration.\n */\n function migrateBinUpStack(uint32 binId, uint32 maxRecursion) external;\n\n /**\n * @notice Swap tokenA/tokenB assets in the pool. The swap user has two\n * options for funding their swap.\n * - The user can push the input token amount to the pool before calling\n * the swap function. In order to avoid having the pool call the callback,\n * the user should pass a zero-length `data` bytes object with the swap\n * call.\n * - The user can send the input token amount to the pool when the pool\n * calls the `maverickV2SwapCallback` function on the calling contract.\n * That callback has input parameters that specify the token address of the\n * input token, the input and output amounts, and the bytes data sent to\n * the swap function.\n * @dev If the users elects to do a callback-based swap, the output\n * assets will be sent before the callback is called, allowing the user to\n * execute flash swaps. However, the pool does have reentrancy protection,\n * so a swapper will not be able to interact with the same pool again\n * while they are in the callback function.\n * @param recipient The address to receive the output tokens.\n * @param params Parameters containing the details of the swap\n * @param data Bytes information that gets passed to the callback.\n */\n function swap(\n address recipient,\n SwapParams memory params,\n bytes calldata data\n ) external returns (uint256 amountIn, uint256 amountOut);\n\n /**\n * @notice Loan tokenA/tokenB assets from the pool to recipient. The fee\n * rate of a loan is determined by `lendingFeeRateD18`, which is set at the\n * protocol level by the factory. This function calls\n * `maverickV2FlashLoanCallback` on the calling contract. At the end of\n * the callback, the caller must pay back the loan with fee (if there is a\n * fee).\n * @param recipient The address to receive the loaned tokens.\n * @param amountB Loan amount of tokenA sent to recipient.\n * @param amountB Loan amount of tokenB sent to recipient.\n * @param data Bytes information that gets passed to the callback.\n */\n function flashLoan(\n address recipient,\n uint256 amountA,\n uint256 amountB,\n bytes calldata data\n ) external;\n\n /**\n * @notice Distributes accumulated protocol fee to factory protocolFeeReceiver\n */\n function distributeFees(bool isTokenA)\n external\n returns (uint256 protocolFee, IERC20 token);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2PoolLens.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2PoolLens {\n error LensTargetPriceOutOfBounds(\n uint256 targetSqrtPrice,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n );\n error LensTooLittleLiquidity(\n uint256 relativeLiquidityAmount,\n uint256 deltaA,\n uint256 deltaB\n );\n error LensTargetingTokenWithNoDelta(\n bool targetIsA,\n uint256 deltaA,\n uint256 deltaB\n );\n\n /**\n * @notice Add liquidity slippage parameters for a distribution of liquidity.\n * @param pool Pool where liquidity is being added.\n * @param kind Bin kind; all bins must have the same kind in a given call\n * to addLiquidity.\n * @param ticks Array of tick values to add liquidity to.\n * @param relativeLiquidityAmounts Relative liquidity amounts for the\n * specified ticks. Liquidity in this case is not bin LP balance, it is\n * the bin liquidity as defined by liquidity = deltaA / (sqrt(upper) -\n * sqrt(lower)) or deltaB = liquidity / sqrt(lower) - liquidity /\n * sqrt(upper).\n * @param addSpec Slippage specification.\n */\n struct AddParamsViewInputs {\n IMaverickV2Pool pool;\n uint8 kind;\n int32[] ticks;\n uint128[] relativeLiquidityAmounts;\n AddParamsSpecification addSpec;\n }\n\n /**\n * @notice Multi-price add param specification.\n * @param slippageFactorD18 Max slippage allowed as a percent in D18 scale. e.g. 1% slippage is 0.01e18\n * @param numberOfPriceBreaksPerSide Number of price break values on either\n * side of current price.\n * @param targetAmount Target token contribution amount in tokenA if\n * targetIsA is true, otherwise this is the target amount for tokenB.\n * @param targetIsA Indicates if the target amount is for tokenA or tokenB\n */\n struct AddParamsSpecification {\n uint256 slippageFactorD18;\n uint256 numberOfPriceBreaksPerSide;\n uint256 targetAmount;\n bool targetIsA;\n }\n\n /**\n * @notice Specification for deriving create pool parameters. Creating a\n * pool in the liquidity manager has several steps:\n *\n * - Deploy pool\n * - Donate a small amount of initial liquidity in the activeTick\n * - Execute a small swap to set the pool price to the desired value\n * - Add liquidity\n *\n * In order to execute these steps, the caller must specify the parameters\n * of each step. The PoolLens has helper function to derive the values\n * used by the LiquidityManager, but this struct is the input to that\n * helper function and represents the core intent of the pool creator.\n *\n * @param fee Fraction of the pool swap amount that is retained as an LP in\n * D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * e.g. a pool with all 4 modes will have kinds = b1111 = 15\n * @param initialTargetB Amount of B to be donated to the pool after pool\n * create. This amount needs to be big enough to meet the minimum bin\n * liquidity.\n * @param sqrtPrice Target sqrt price of the pool.\n * @param kind Bin kind; all bins must have the same kind in a given call\n * to addLiquidity.\n * @param ticks Array of tick values to add liquidity to.\n * @param relativeLiquidityAmounts Relative liquidity amounts for the\n * specified ticks. Liquidity in this case is not bin LP balance, it is\n * the bin liquidity as defined by liquidity = deltaA / (sqrt(upper) -\n * sqrt(lower)) or deltaB = liquidity / sqrt(lower) - liquidity /\n * sqrt(upper).\n * @param targetAmount Target token contribution amount in tokenA if\n * targetIsA is true, otherwise this is the target amount for tokenB.\n * @param targetIsA Indicates if the target amount is for tokenA or tokenB\n */\n struct CreateAndAddParamsViewInputs {\n uint64 feeAIn;\n uint64 feeBIn;\n uint16 tickSpacing;\n uint32 lookback;\n IERC20 tokenA;\n IERC20 tokenB;\n int32 activeTick;\n uint8 kinds;\n // donate params\n uint256 initialTargetB;\n uint256 sqrtPrice;\n // add target\n uint8 kind;\n int32[] ticks;\n uint128[] relativeLiquidityAmounts;\n uint256 targetAmount;\n bool targetIsA;\n }\n\n struct Output {\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256[] deltaAs;\n uint256[] deltaBs;\n uint128[] deltaLpBalances;\n }\n\n struct Reserves {\n uint256 amountA;\n uint256 amountB;\n }\n\n struct BinPositionKinds {\n uint128[4] values;\n }\n\n struct PoolState {\n IMaverickV2Pool.TickState[] tickStateMapping;\n IMaverickV2Pool.BinState[] binStateMapping;\n BinPositionKinds[] binIdByTickKindMapping;\n IMaverickV2Pool.State state;\n Reserves protocolFees;\n }\n\n struct BoostedPositionSpecification {\n IMaverickV2Pool pool;\n uint32[] binIds;\n uint128[] ratios;\n uint8 kind;\n }\n\n struct CreateAndAddParamsInputs {\n uint64 feeAIn;\n uint64 feeBIn;\n uint16 tickSpacing;\n uint32 lookback;\n IERC20 tokenA;\n IERC20 tokenB;\n int32 activeTick;\n uint8 kinds;\n // donate params\n IMaverickV2Pool.AddLiquidityParams donateParams;\n // swap params\n uint256 swapAmount;\n // add params\n IMaverickV2Pool.AddLiquidityParams addParams;\n bytes[] packedAddParams;\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256 preAddReserveA;\n uint256 preAddReserveB;\n }\n\n struct TickDeltas {\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256[] deltaAs;\n uint256[] deltaBs;\n }\n\n /**\n * @notice Converts add parameter slippage specification into add\n * parameters. The return values are given in both raw format and as packed\n * values that can be used in the LiquidityManager contract.\n */\n function getAddLiquidityParams(AddParamsViewInputs memory params)\n external\n view\n returns (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint88[] memory sqrtPriceBreaks,\n IMaverickV2Pool.AddLiquidityParams[] memory addParams,\n IMaverickV2PoolLens.TickDeltas[] memory tickDeltas\n );\n\n /**\n * @notice Converts add parameter slippage specification and new pool\n * specification into CreateAndAddParamsInputs parameters that can be used in the\n * LiquidityManager contract.\n */\n function getCreatePoolAtPriceAndAddLiquidityParams(\n CreateAndAddParamsViewInputs memory params\n ) external view returns (CreateAndAddParamsInputs memory output);\n\n /**\n * @notice View function that provides information about pool ticks within\n * a tick radius from the activeTick. Ticks with no reserves are not\n * included in part o f the return array.\n */\n function getTicksAroundActive(IMaverickV2Pool pool, int32 tickRadius)\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates\n );\n\n /**\n * @notice View function that provides information about pool ticks within\n * a range. Ticks with no reserves are not included in part o f the return\n * array.\n */\n function getTicks(\n IMaverickV2Pool pool,\n int32 tickStart,\n int32 tickEnd\n )\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates\n );\n\n /**\n * @notice View function that provides information about pool ticks within\n * a range. Information returned includes all pool state needed to emulate\n * a swap off chain. Ticks with no reserves are not included in part o f\n * the return array.\n */\n function getTicksAroundActiveWLiquidity(\n IMaverickV2Pool pool,\n int32 tickRadius\n )\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates,\n uint256[] memory liquidities,\n uint256[] memory sqrtLowerTickPrices,\n uint256[] memory sqrtUpperTickPrices,\n IMaverickV2Pool.State memory poolState,\n uint256 sqrtPrice,\n uint256 feeAIn,\n uint256 feeBIn\n );\n\n /**\n * @notice View function that provides pool state information.\n */\n function getFullPoolState(\n IMaverickV2Pool pool,\n uint32 binStart,\n uint32 binEnd\n ) external view returns (PoolState memory poolState);\n\n /**\n * @notice View function that provides price and liquidity of a given tick.\n */\n function getTickSqrtPriceAndL(IMaverickV2Pool pool, int32 tick)\n external\n view\n returns (uint256 sqrtPrice, uint256 liquidity);\n\n /**\n * @notice Pool sqrt price.\n */\n function getPoolSqrtPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 sqrtPrice);\n\n /**\n * @notice Pool price.\n */\n function getPoolPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 price);\n\n /**\n * @notice Token scale of two tokens in a pool.\n */\n function tokenScales(IMaverickV2Pool pool)\n external\n view\n returns (uint256 tokenAScale, uint256 tokenBScale);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Position.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\nimport { ILiquidityRegistry } from \"./ILiquidityRegistry.sol\";\n\ninterface IMaverickV2Position {\n event PositionClearData(uint256 indexed tokenId);\n event PositionSetData(\n uint256 indexed tokenId,\n uint256 index,\n PositionPoolBinIds newData\n );\n event SetLpReward(ILiquidityRegistry lpReward);\n\n error PositionDuplicatePool(uint256 index, IMaverickV2Pool pool);\n error PositionNotFactoryPool();\n error PositionPermissionedLiquidityPool();\n\n struct PositionPoolBinIds {\n IMaverickV2Pool pool;\n uint32[] binIds;\n }\n\n struct PositionFullInformation {\n PositionPoolBinIds poolBinIds;\n uint256 amountA;\n uint256 amountB;\n uint256[] binAAmounts;\n uint256[] binBAmounts;\n int32[] ticks;\n uint256[] liquidities;\n }\n\n /**\n * @notice Factory that tracks lp rewards.\n */\n function lpReward() external view returns (ILiquidityRegistry);\n\n /**\n * @notice Pool factory.\n */\n function factory() external view returns (IMaverickV2Factory);\n\n /**\n * @notice Mint NFT that holds liquidity in a Maverick V2 Pool. To mint\n * liquidity to an NFT, add liquidity to bins in a pool where the\n * add liquidity recipient is this contract and the subaccount is the\n * tokenId. LiquidityManager can be used to simplify minting Position NFTs.\n */\n function mint(\n address recipient,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external returns (uint256 tokenId);\n\n /**\n * @notice Overwrites tokenId pool/binId information for a given data index.\n */\n function setTokenIdData(\n uint256 tokenId,\n uint256 index,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n\n /**\n * @notice Overwrites entire pool/binId data set for a given tokenId.\n */\n function setTokenIdData(uint256 tokenId, PositionPoolBinIds[] memory data)\n external;\n\n /**\n * @notice Append new pool/binIds data array to tokenId.\n */\n function appendTokenIdData(\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n\n /**\n * @notice Get array pool/binIds data for a given tokenId.\n */\n function getTokenIdData(uint256 tokenId)\n external\n view\n returns (PositionPoolBinIds[] memory);\n\n /**\n * @notice Get value from array of pool/binIds data for a given tokenId.\n */\n function getTokenIdData(uint256 tokenId, uint256 index)\n external\n view\n returns (PositionPoolBinIds memory);\n\n /**\n * @notice Length of array of pool/binIds data for a given tokenId.\n */\n function tokenIdDataLength(uint256 tokenId)\n external\n view\n returns (uint256 length);\n\n /**\n * @notice Remove liquidity from tokenId for a given pool. User can\n * specify arbitrary bins to remove from for their subaccount in the pool\n * even if those bins are not in the tokenIdData set.\n */\n function removeLiquidity(\n uint256 tokenId,\n address recipient,\n IMaverickV2Pool pool,\n IMaverickV2Pool.RemoveLiquidityParams memory params\n ) external returns (uint256 tokenAAmount, uint256 tokenBAmount);\n\n /**\n * @notice Remove liquidity from tokenId for a given pool to sender. User\n * can specify arbitrary bins to remove from for their subaccount in the\n * pool even if those bins are not in the tokenIdData set.\n */\n function removeLiquidityToSender(\n uint256 tokenId,\n IMaverickV2Pool pool,\n IMaverickV2Pool.RemoveLiquidityParams memory params\n ) external returns (uint256 tokenAAmount, uint256 tokenBAmount);\n\n /**\n * @notice NFT asset information for a given range of pool/binIds indexes.\n * This function only returns the liquidity in the pools/binIds stored as\n * part of the tokenIdData, but it is possible that the NFT has additional\n * liquidity in pools/binIds that have not been recorded.\n */\n function tokenIdPositionInformation(\n uint256 tokenId,\n uint256 startIndex,\n uint256 stopIndex\n ) external view returns (PositionFullInformation[] memory output);\n\n /**\n * @notice NFT asset information for a given pool/binIds index. This\n * function only returns the liquidity in the pools/binIds stored as part\n * of the tokenIdData, but it is possible that the NFT has additional\n * liquidity in pools/binIds that have not been recorded.\n */\n function tokenIdPositionInformation(uint256 tokenId, uint256 index)\n external\n view\n returns (PositionFullInformation memory output);\n\n /**\n * @notice Get remove parameters for removing a fractional part of the\n * liquidity owned by a given tokenId. The fractional factor to remove is\n * given by proporationD18 in 18-decimal scale.\n */\n function getRemoveParams(\n uint256 tokenId,\n uint256 index,\n uint256 proportionD18\n )\n external\n view\n returns (IMaverickV2Pool.RemoveLiquidityParams memory params);\n\n /**\n * @notice Register the bin balances in the nft with the LpReward contract.\n */\n function checkpointBinLpBalance(\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Quoter.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2Quoter {\n error QuoterInvalidSwap();\n error QuoterInvalidAddLiquidity();\n\n /**\n * @notice Calculates a swap on a MaverickV2Pool and returns the resulting\n * amount and estimated gas. The gas estimate is only a rough estimate and\n * may not match a swap's gas.\n * @param pool The MaverickV2Pool to swap on.\n * @param amount The input amount.\n * @param tokenAIn Indicates if token A is the input token.\n * @param exactOutput Indicates if the amount is the output amount (true)\n * or input amount (false). If the tickLimit is reached, the full value of\n * the exactOutput may not be returned because the pool will stop swapping\n * before the whole order is filled.\n * @param tickLimit The tick limit for the swap. Once the swap lands in\n * this tick, it will stop and return the output amount swapped up to that\n * tick.\n */\n function calculateSwap(\n IMaverickV2Pool pool,\n uint128 amount,\n bool tokenAIn,\n bool exactOutput,\n int32 tickLimit\n )\n external\n returns (\n uint256 amountIn,\n uint256 amountOut,\n uint256 gasEstimate\n );\n\n /**\n * @notice Calculates a multihop swap and returns the resulting amount and\n * estimated gas. The gas estimate is only a rough estimate and\n * may not match a swap's gas.\n * @param path The path of pools to swap through. Path is given by an\n * packed array of (pool, tokenAIn) tuples. So each step in the path is 160\n * + 8 = 168 bits of data. e.g. path = abi.encodePacked(pool1, true, pool2, false);\n * @param amount The input amount.\n * @param exactOutput A boolean indicating if exact output is required.\n */\n function calculateMultiHopSwap(\n bytes memory path,\n uint256 amount,\n bool exactOutput\n ) external returns (uint256 returnAmount, uint256 gasEstimate);\n\n /**\n * @notice Computes the token amounts required for a given set of\n * addLiquidity parameters. The gas estimate is only a rough estimate and\n * may not match a add's gas.\n */\n function calculateAddLiquidity(\n IMaverickV2Pool pool,\n IMaverickV2Pool.AddLiquidityParams calldata params\n )\n external\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 gasEstimate\n );\n\n /**\n * @notice Pool's sqrt price.\n */\n function poolSqrtPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 sqrtPrice);\n}\n" + }, + "contracts/interfaces/plume/IPoolDistributor.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.0;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IPoolDistributor {\n function rewardToken() external view returns (address);\n\n function claimLp(\n address recipient,\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds,\n uint256 epoch\n ) external returns (uint256 amount);\n}\n" + }, + "contracts/interfaces/plume/IVotingDistributor.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.0;\n\ninterface IVotingDistributor {\n function lastEpoch() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/poolBooster/IMerklDistributor.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IMerklDistributor {\n struct CampaignParameters {\n // POPULATED ONCE CREATED\n\n // ID of the campaign. This can be left as a null bytes32 when creating campaigns\n // on Merkl.\n bytes32 campaignId;\n // CHOSEN BY CAMPAIGN CREATOR\n\n // Address of the campaign creator, if marked as address(0), it will be overriden with the\n // address of the `msg.sender` creating the campaign\n address creator;\n // Address of the token used as a reward\n address rewardToken;\n // Amount of `rewardToken` to distribute across all the epochs\n // Amount distributed per epoch is `amount/numEpoch`\n uint256 amount;\n // Type of campaign\n uint32 campaignType;\n // Timestamp at which the campaign should start\n uint32 startTimestamp;\n // Duration of the campaign in seconds. Has to be a multiple of EPOCH = 3600\n uint32 duration;\n // Extra data to pass to specify the campaign\n bytes campaignData;\n }\n\n function createCampaign(CampaignParameters memory newCampaign)\n external\n returns (bytes32);\n\n function signAndCreateCampaign(\n CampaignParameters memory newCampaign,\n bytes memory _signature\n ) external returns (bytes32);\n\n function sign(bytes memory _signature) external;\n\n function rewardTokenMinAmounts(address _rewardToken)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/poolBooster/IPoolBoostCentralRegistry.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPoolBoostCentralRegistry {\n /**\n * @dev all the supported pool booster types are listed here. It is possible\n * to have multiple versions of the factory that supports the same type of\n * pool booster. Factories are immutable and this can happen when a factory\n * or related pool booster required code update.\n * e.g. \"PoolBoosterSwapxDouble\" & \"PoolBoosterSwapxDouble_v2\"\n */\n enum PoolBoosterType {\n // Supports bribing 2 contracts per pool. Appropriate for Ichi vault concentrated\n // liquidity pools where (which is expected in most/all cases) both pool gauges\n // require bribing.\n SwapXDoubleBooster,\n // Supports bribing a single contract per pool. Appropriate for Classic Stable &\n // Classic Volatile pools and Ichi vaults where only 1 side (1 of the 2 gauges)\n // needs bribing\n SwapXSingleBooster,\n // Supports bribing a single contract per pool. Appropriate for Metropolis pools\n MetropolisBooster,\n // Supports creating a Merkl campaign.\n MerklBooster\n }\n\n struct PoolBoosterEntry {\n address boosterAddress;\n address ammPoolAddress;\n PoolBoosterType boosterType;\n }\n\n event PoolBoosterCreated(\n address poolBoosterAddress,\n address ammPoolAddress,\n PoolBoosterType poolBoosterType,\n address factoryAddress\n );\n event PoolBoosterRemoved(address poolBoosterAddress);\n\n function emitPoolBoosterCreated(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n PoolBoosterType _boosterType\n ) external;\n\n function emitPoolBoosterRemoved(address _poolBoosterAddress) external;\n}\n" + }, + "contracts/interfaces/poolBooster/IPoolBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPoolBooster {\n event BribeExecuted(uint256 amount);\n\n /// @notice Execute the bribe action\n function bribe() external;\n}\n" + }, + "contracts/interfaces/poolBooster/ISwapXAlgebraBribe.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBribe {\n /// @notice Notify a bribe amount\n /// @dev Rewards are saved into NEXT EPOCH mapping.\n function notifyRewardAmount(address _rewardsToken, uint256 reward) external;\n}\n" + }, + "contracts/interfaces/sonic/INodeDriver.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\n\ninterface INodeDriver {\n /// Seal epoch. Called BEFORE epoch sealing made by the client itself.\n function sealEpoch(\n uint256[] calldata offlineTimes,\n uint256[] calldata offlineBlocks,\n uint256[] calldata uptimes,\n uint256[] calldata originatedTxsFee\n ) external;\n}\n" + }, + "contracts/interfaces/sonic/ISFC.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\n\n/**\n * @title Special Fee Contract for Sonic network\n * @notice The SFC maintains a list of validators and delegators and distributes rewards to them.\n * @custom:security-contact security@fantom.foundation\n */\ninterface ISFC {\n error StakeIsFullySlashed();\n\n event CreatedValidator(\n uint256 indexed validatorID,\n address indexed auth,\n uint256 createdEpoch,\n uint256 createdTime\n );\n event Delegated(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 amount\n );\n event Undelegated(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 indexed wrID,\n uint256 amount\n );\n event Withdrawn(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 indexed wrID,\n uint256 amount,\n uint256 penalty\n );\n event ClaimedRewards(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 rewards\n );\n event RestakedRewards(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 rewards\n );\n event BurntFTM(uint256 amount);\n event UpdatedSlashingRefundRatio(\n uint256 indexed validatorID,\n uint256 refundRatio\n );\n event RefundedSlashedLegacyDelegation(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 amount\n );\n\n event DeactivatedValidator(\n uint256 indexed validatorID,\n uint256 deactivatedEpoch,\n uint256 deactivatedTime\n );\n event ChangedValidatorStatus(uint256 indexed validatorID, uint256 status);\n event AnnouncedRedirection(address indexed from, address indexed to);\n\n function currentSealedEpoch() external view returns (uint256);\n\n function getEpochSnapshot(uint256 epoch)\n external\n view\n returns (\n uint256 endTime,\n uint256 endBlock,\n uint256 epochFee,\n uint256 baseRewardPerSecond,\n uint256 totalStake,\n uint256 totalSupply\n );\n\n function getStake(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getValidator(uint256 validatorID)\n external\n view\n returns (\n uint256 status,\n uint256 receivedStake,\n address auth,\n uint256 createdEpoch,\n uint256 createdTime,\n uint256 deactivatedTime,\n uint256 deactivatedEpoch\n );\n\n function getValidatorID(address auth) external view returns (uint256);\n\n function getValidatorPubkey(uint256 validatorID)\n external\n view\n returns (bytes memory);\n\n function pubkeyAddressvalidatorID(address pubkeyAddress)\n external\n view\n returns (uint256);\n\n function getWithdrawalRequest(\n address delegator,\n uint256 validatorID,\n uint256 wrID\n )\n external\n view\n returns (\n uint256 epoch,\n uint256 time,\n uint256 amount\n );\n\n function isOwner() external view returns (bool);\n\n function lastValidatorID() external view returns (uint256);\n\n function minGasPrice() external view returns (uint256);\n\n function owner() external view returns (address);\n\n function renounceOwnership() external;\n\n function slashingRefundRatio(uint256 validatorID)\n external\n view\n returns (uint256);\n\n function stashedRewardsUntilEpoch(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function totalActiveStake() external view returns (uint256);\n\n function totalStake() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function transferOwnership(address newOwner) external;\n\n function treasuryAddress() external view returns (address);\n\n function version() external pure returns (bytes3);\n\n function currentEpoch() external view returns (uint256);\n\n function updateConstsAddress(address v) external;\n\n function constsAddress() external view returns (address);\n\n function getEpochValidatorIDs(uint256 epoch)\n external\n view\n returns (uint256[] memory);\n\n function getEpochReceivedStake(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochAccumulatedRewardPerToken(\n uint256 epoch,\n uint256 validatorID\n ) external view returns (uint256);\n\n function getEpochAccumulatedUptime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochAverageUptime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint32);\n\n function getEpochAccumulatedOriginatedTxsFee(\n uint256 epoch,\n uint256 validatorID\n ) external view returns (uint256);\n\n function getEpochOfflineTime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochOfflineBlocks(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochEndBlock(uint256 epoch) external view returns (uint256);\n\n function rewardsStash(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function createValidator(bytes calldata pubkey) external payable;\n\n function getSelfStake(uint256 validatorID) external view returns (uint256);\n\n function delegate(uint256 validatorID) external payable;\n\n function undelegate(\n uint256 validatorID,\n uint256 wrID,\n uint256 amount\n ) external;\n\n function isSlashed(uint256 validatorID) external view returns (bool);\n\n function withdraw(uint256 validatorID, uint256 wrID) external;\n\n function deactivateValidator(uint256 validatorID, uint256 status) external;\n\n function pendingRewards(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function stashRewards(address delegator, uint256 validatorID) external;\n\n function claimRewards(uint256 validatorID) external;\n\n function restakeRewards(uint256 validatorID) external;\n\n function updateSlashingRefundRatio(uint256 validatorID, uint256 refundRatio)\n external;\n\n function updateTreasuryAddress(address v) external;\n\n function burnFTM(uint256 amount) external;\n\n function sealEpoch(\n uint256[] calldata offlineTime,\n uint256[] calldata offlineBlocks,\n uint256[] calldata uptimes,\n uint256[] calldata originatedTxsFee\n ) external;\n\n function sealEpochValidators(uint256[] calldata nextValidatorIDs) external;\n\n function initialize(\n uint256 sealedEpoch,\n uint256 _totalSupply,\n address nodeDriver,\n address consts,\n address _owner\n ) external;\n\n function setGenesisValidator(\n address auth,\n uint256 validatorID,\n bytes calldata pubkey,\n uint256 createdTime\n ) external;\n\n function setGenesisDelegation(\n address delegator,\n uint256 validatorID,\n uint256 stake\n ) external;\n\n function updateStakeSubscriberAddress(address v) external;\n\n function stakeSubscriberAddress() external view returns (address);\n\n function setRedirectionAuthorizer(address v) external;\n\n function announceRedirection(address to) external;\n\n function initiateRedirection(address from, address to) external;\n\n function redirect(address to) external;\n}\n" + }, + "contracts/interfaces/sonic/ISwapXGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IGauge {\n function owner() external view returns (address);\n\n function TOKEN() external view returns (address);\n\n function DISTRIBUTION() external view returns (address);\n\n function balanceOf(address account) external view returns (uint256);\n\n function claimFees() external returns (uint256 claimed0, uint256 claimed1);\n\n function deposit(uint256 amount) external;\n\n function depositAll() external;\n\n function earned(address account) external view returns (uint256);\n\n function getReward() external;\n\n function getReward(address _user) external;\n\n function isForPair() external view returns (bool);\n\n function lastTimeRewardApplicable() external view returns (uint256);\n\n function lastUpdateTime() external view returns (uint256);\n\n function notifyRewardAmount(address token, uint256 reward) external;\n\n function periodFinish() external view returns (uint256);\n\n function rewardForDuration() external view returns (uint256);\n\n function rewardPerToken() external view returns (uint256);\n\n function rewardPerTokenStored() external view returns (uint256);\n\n function rewardRate() external view returns (uint256);\n\n function rewardToken() external view returns (address);\n\n function rewards(address) external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function userRewardPerTokenPaid(address) external view returns (uint256);\n\n function withdraw(uint256 amount) external;\n\n function withdrawAll() external;\n\n function withdrawAllAndHarvest() external;\n\n function withdrawExcess(address token, uint256 amount) external;\n\n function emergency() external returns (bool);\n\n function emergencyWithdraw() external;\n\n function activateEmergencyMode() external;\n\n function stopEmergencyMode() external;\n}\n" + }, + "contracts/interfaces/sonic/ISwapXPair.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IPair {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n\n event Mint(address indexed sender, uint256 amount0, uint256 amount1);\n event Burn(\n address indexed sender,\n uint256 amount0,\n uint256 amount1,\n address indexed to\n );\n event Swap(\n address indexed sender,\n uint256 amount0In,\n uint256 amount1In,\n uint256 amount0Out,\n uint256 amount1Out,\n address indexed to\n );\n event Claim(\n address indexed sender,\n address indexed recipient,\n uint256 amount0,\n uint256 amount1\n );\n\n function metadata()\n external\n view\n returns (\n uint256 dec0,\n uint256 dec1,\n uint256 r0,\n uint256 r1,\n bool st,\n address t0,\n address t1\n );\n\n function claimFees() external returns (uint256, uint256);\n\n function tokens() external view returns (address, address);\n\n function token0() external view returns (address);\n\n function token1() external view returns (address);\n\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function swap(\n uint256 amount0Out,\n uint256 amount1Out,\n address to,\n bytes calldata data\n ) external;\n\n function burn(address to)\n external\n returns (uint256 amount0, uint256 amount1);\n\n function mint(address to) external returns (uint256 liquidity);\n\n function getReserves()\n external\n view\n returns (\n uint256 _reserve0,\n uint256 _reserve1,\n uint256 _blockTimestampLast\n );\n\n function getAmountOut(uint256, address) external view returns (uint256);\n\n // ERC20 methods\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n\n function totalSupply() external view returns (uint256);\n\n function balanceOf(address) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 value) external returns (bool);\n\n function claimable0(address _user) external view returns (uint256);\n\n function claimable1(address _user) external view returns (uint256);\n\n function isStable() external view returns (bool);\n\n function skim(address to) external;\n\n function sync() external;\n}\n" + }, + "contracts/interfaces/sonic/IVoterV3.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IVoterV3 {\n /// @notice create a gauge\n function createGauge(address _pool, uint256 _gaugeType)\n external\n returns (\n address _gauge,\n address _internal_bribe,\n address _external_bribe\n );\n\n function gauges(address _pool) external view returns (address _gauge);\n}\n" + }, + "contracts/interfaces/sonic/IWrappedSonic.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWrappedSonic {\n event Deposit(address indexed account, uint256 value);\n event Withdrawal(address indexed account, uint256 value);\n event Transfer(address indexed from, address indexed to, uint256 value);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 value) external returns (bool);\n\n function balanceOf(address account) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function depositFor(address account) external payable returns (bool);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address to, uint256 value) external returns (bool);\n\n function transferFrom(\n address from,\n address to,\n uint256 value\n ) external returns (bool);\n\n function withdraw(uint256 value) external;\n\n function withdrawTo(address account, uint256 value) external returns (bool);\n}\n" + }, + "contracts/interfaces/Tether.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// slither-disable-start erc20-interface\ninterface Tether {\n function transfer(address to, uint256 value) external;\n\n function transferFrom(\n address from,\n address to,\n uint256 value\n ) external;\n\n function balanceOf(address) external view returns (uint256);\n\n function approve(address _spender, uint256 _value) external;\n}\n// slither-disable-end erc20-interface\n" + }, + "contracts/interfaces/uniswap/IUniswapUniversalRouter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapUniversalRouter {\n /// @notice Executes encoded commands along with provided inputs. Reverts if deadline has expired.\n /// @param commands A set of concatenated commands, each 1 byte in length\n /// @param inputs An array of byte strings containing abi encoded inputs for each command\n /// @param deadline The deadline by which the transaction must be executed\n function execute(\n bytes calldata commands,\n bytes[] calldata inputs,\n uint256 deadline\n ) external payable;\n\n function execute(bytes calldata commands, bytes[] calldata inputs)\n external\n payable;\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV2Pair.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapV2Pair {\n function token0() external view returns (address);\n\n function token1() external view returns (address);\n\n function getReserves()\n external\n view\n returns (\n uint112 reserve0,\n uint112 reserve1,\n uint32 blockTimestampLast\n );\n\n function price0CumulativeLast() external view returns (uint256);\n\n function price1CumulativeLast() external view returns (uint256);\n\n function sync() external;\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV2Router02.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapV2Router {\n function WETH() external pure returns (address);\n\n function swapExactTokensForTokens(\n uint256 amountIn,\n uint256 amountOutMin,\n address[] calldata path,\n address to,\n uint256 deadline\n ) external returns (uint256[] memory amounts);\n\n function addLiquidity(\n address tokenA,\n address tokenB,\n uint256 amountADesired,\n uint256 amountBDesired,\n uint256 amountAMin,\n uint256 amountBMin,\n address to,\n uint256 deadline\n )\n external\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 liquidity\n );\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV3Router.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// -- Solididy v0.5.x compatible interface\ninterface IUniswapV3Router {\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n}\n" + }, + "contracts/mocks/beacon/EnhancedBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"../../beacon/BeaconProofsLib.sol\";\nimport { BeaconProofs } from \"../../beacon/BeaconProofs.sol\";\n\ncontract EnhancedBeaconProofs is BeaconProofs {\n function concatGenIndices(\n uint256 index1,\n uint256 height2,\n uint256 index2\n ) external pure returns (uint256 genIndex) {\n return BeaconProofsLib.concatGenIndices(index1, height2, index2);\n }\n}\n" + }, + "contracts/mocks/beacon/ExecutionLayerConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { GeneralPurposeToConsensusLayerRequest } from \"./GeneralPurposeToConsensusLayerRequest.sol\";\n\ncontract ExecutionLayerConsolidation is GeneralPurposeToConsensusLayerRequest {\n event ConsolidationRequestIssued(bytes sourceKey, bytes targetKey);\n bytes public lastSource;\n bytes public lastTarget;\n\n function handleRequest(bytes calldata data) internal override {\n // parameters should consist of twice the 48 bytes for 2 public keys\n require(data.length == 96, \"Invalid Consolidation data\");\n lastSource = data[:48];\n lastTarget = data[48:];\n\n emit ConsolidationRequestIssued(lastSource, lastTarget);\n }\n}\n" + }, + "contracts/mocks/beacon/ExecutionLayerWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { GeneralPurposeToConsensusLayerRequest } from \"./GeneralPurposeToConsensusLayerRequest.sol\";\n\ncontract ExecutionLayerWithdrawal is GeneralPurposeToConsensusLayerRequest {\n event WithdrawalRequestIssued(bytes publicKey, uint64 amount);\n\n bytes public lastPublicKey;\n uint64 public lastAmount;\n\n function handleRequest(bytes calldata data) internal override {\n // parameters should consist of 48 bytes for public key and 8 bytes for uint64 amount\n require(data.length == 56, \"Invalid Withdrawal data\");\n lastPublicKey = data[:48];\n lastAmount = uint64(bytes8(data[48:]));\n emit WithdrawalRequestIssued(lastPublicKey, lastAmount);\n }\n}\n" + }, + "contracts/mocks/beacon/GeneralPurposeToConsensusLayerRequest.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nabstract contract GeneralPurposeToConsensusLayerRequest {\n // solhint-disable no-complex-fallback\n fallback() external payable {\n // fee requested\n if (msg.data.length == 0) {\n uint256 fee = _fee();\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Return a uint256 value\n mstore(0, fee)\n return(0, 32) // Return 32 bytes from memory\n }\n }\n\n // else handle request\n handleRequest(msg.data);\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _fee() internal virtual returns (uint256) {\n return 1;\n }\n\n function handleRequest(bytes calldata data) internal virtual;\n}\n" + }, + "contracts/mocks/beacon/MockBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\n// solhint-disable no-unused-vars\n\n/**\n * @title Mock contract for test purposes verifying Merkle proofs\n * @author Origin Protocol Inc\n */\ncontract MockBeaconProofs is IBeaconProofs {\n /// @dev Number of bytes in the proof to the first pending deposit.\n /// 37 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0]\n /// 37 * 32 bytes = 1184 bytes\n uint256 internal constant FIRST_PENDING_DEPOSIT_PROOF_LENGTH = 1184;\n\n uint256 internal constant DEFAULT_VALIDATOR_BALANCE = 32 ether;\n // mapping of validator indexes to validator balances\n mapping(uint40 => uint256) public validatorBalances;\n\n function setValidatorBalance(uint40 index, uint256 validatorBalance)\n external\n {\n // set special max value instead of 0\n if (validatorBalance == 0) {\n validatorBalances[index] = type(uint256).max;\n } else {\n validatorBalances[index] = validatorBalance;\n }\n }\n\n /// @notice Verifies the validator index is for the given validator public key.\n /// Also verify the validator's withdrawal credential points to the withdrawal address.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param proof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata proof,\n uint40 validatorIndex,\n address withdrawalAddress\n ) external view {\n // always pass\n }\n\n /// @notice Verifies a validator's withdrawable epoch to the beacon block root\n /// for a given validator index.\n /// Also verifies the validator's public key for the given validator index.\n /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n /// @param beaconBlockRoot The root of the beacon block\n /// @param validatorIndex The validator index to verify the withdrawable epoch for.\n /// @param withdrawableEpoch The withdrawable epoch to verify in big endian uint64 format\n /// @param withdrawableEpochProof The merkle proof for the validator's withdrawable epoch to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorPubKeyProof The merkle proof for the validator public key in a sub tree of height two.\n /// This is 2 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n bytes32 pubKeyHash,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof,\n bytes calldata validatorPubKeyProof\n ) external view {\n // always pass\n }\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view {\n // always pass\n }\n\n /// @notice Verifies the balances container to the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerRoot The merkle root of the the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata balancesContainerProof\n ) external view {\n // always pass\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param balanceProof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint40 validatorIndex\n ) external view returns (uint256 validatorBalanceGwei) {\n uint256 validatorBalance = validatorBalances[validatorIndex];\n\n // special setting representing 0 balance\n if (validatorBalance == type(uint256).max) {\n return 0;\n }\n // validator balance not set by the test cases\n else if (validatorBalance == 0) {\n return DEFAULT_VALIDATOR_BALANCE;\n }\n\n return validatorBalance;\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the pubKey and slot of the first pending deposit to the beacon block root.\n /// BeaconBlock.state.PendingDeposits[0].pubKey\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty.\n /// @param pubKeyHash The hash of the validator public key for the first pending deposit.\n /// Use zero bytes if the deposit queue is empty.\n /// @param firstPendingDepositPubKeyProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].pubKey when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes32 pubKeyHash,\n bytes calldata firstPendingDepositPubKeyProof\n ) external view returns (bool isEmptyDepositQueue) {\n if (\n firstPendingDepositPubKeyProof.length ==\n FIRST_PENDING_DEPOSIT_PROOF_LENGTH\n ) {\n isEmptyDepositQueue = true;\n }\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the slot of the first pending deposit to the beacon block root.\n /// BeaconBlock.state.pendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty.\n /// @param firstPendingDepositSlotProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view returns (bool isEmptyDepositQueue) {\n if (\n firstPendingDepositSlotProof.length ==\n FIRST_PENDING_DEPOSIT_PROOF_LENGTH\n ) {\n isEmptyDepositQueue = true;\n }\n }\n}\n" + }, + "contracts/mocks/beacon/MockBeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\n\ncontract MockBeaconRoots {\n // Mapping to simulate the ring buffer: timestamp => beacon block root\n mapping(uint256 => bytes32) internal _beaconRoots;\n\n // Event to log when a new root is set (for testing)\n event RootSet(uint256 indexed timestamp, bytes32 root);\n\n // Fallback function to handle raw 32-byte timestamp input\n // solhint-disable no-complex-fallback\n fallback() external {\n // Ensure input is exactly 32 bytes (big-endian encoded timestamp)\n require(msg.data.length == 32, \"Input must be 32 bytes\");\n\n // Decode the 32-byte input as a uint256 timestamp (big-endian)\n uint256 timestamp = abi.decode(msg.data, (uint256));\n\n // Don't do any validation of timestamp so we can test any block\n\n // Retrieve the root. Will return bytes32(0) if not set.\n bytes32 root = _beaconRoots[timestamp];\n\n // Return the 32-byte root directly\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0, root)\n return(0, 32)\n }\n }\n\n // Mock function to set a beacon block root (for testing)\n function setBeaconRoot(uint256 timestamp, bytes32 root) external {\n require(timestamp > 0, \"Invalid timestamp\");\n require(root != bytes32(0), \"Invalid root\");\n\n // Store the root at the given timestamp\n _beaconRoots[timestamp] = root;\n\n emit RootSet(timestamp, root);\n }\n\n function setBeaconRoot(bytes32 root) external {\n require(root != bytes32(0), \"Invalid root\");\n\n // Store the root at the given timestamp\n _beaconRoots[block.timestamp] = root;\n\n emit RootSet(block.timestamp, root);\n }\n\n function parentBlockRoot(uint64 timestamp)\n external\n view\n returns (bytes32 parentRoot)\n {\n return BeaconRoots.parentBlockRoot(timestamp);\n }\n\n function latestBlockRoot()\n external\n view\n returns (bytes32 parentRoot, uint64 timestamp)\n {\n timestamp = uint64(block.timestamp);\n parentRoot = BeaconRoots.parentBlockRoot(timestamp);\n }\n}\n" + }, + "contracts/mocks/BurnableERC20.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ninterface IBurnableERC20 {\n function burn(uint256 value) external returns (bool);\n\n function burnFrom(address account, uint256 value) external returns (bool);\n}\n\n/**\n * @title BurnableERC20\n * @dev Exposes the burn function of ERC20 for tests\n */\nabstract contract BurnableERC20 is IBurnableERC20, ERC20 {\n /**\n * @dev Function to burn tokens\n * @param value The amount of tokens to burn.\n * @return A boolean that indicates if the operation was successful.\n */\n function burn(uint256 value) public virtual override returns (bool) {\n _burn(msg.sender, value);\n return true;\n }\n\n /**\n * @dev Function to burn tokens from a specific account\n * @param account The address with the tokens to burn.\n * @param value The amount of tokens to burn.\n * @return A boolean that indicates if the operation was successful.\n */\n function burnFrom(address account, uint256 value)\n public\n override\n returns (bool)\n {\n _burn(account, value);\n return true;\n }\n}\n" + }, + "contracts/mocks/curve/Mock3CRV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20, MintableERC20 } from \"../MintableERC20.sol\";\nimport { BurnableERC20 } from \"../BurnableERC20.sol\";\n\ncontract Mock3CRV is MintableERC20, BurnableERC20 {\n constructor() ERC20(\"Curve.fi DAI/USDC/USDT\", \"3Crv\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/curve/MockBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { MockRewardPool } from \"./MockRewardPool.sol\";\n\nimport { IRewardStaking } from \"../../strategies/IRewardStaking.sol\";\nimport { IMintableERC20, MintableERC20, ERC20 } from \"../MintableERC20.sol\";\nimport { IBurnableERC20, BurnableERC20 } from \"../BurnableERC20.sol\";\n\ncontract MockDepositToken is MintableERC20, BurnableERC20 {\n constructor() ERC20(\"DCVX\", \"CVX Deposit Token\") {}\n}\n\ncontract MockBooster {\n using SafeERC20 for IERC20;\n\n struct PoolInfo {\n address lptoken;\n address token;\n address crvRewards;\n }\n\n address public minter; // this is CVx for the booster on live\n address public crv; // Curve rewards token\n address public cvx; // Convex rewards token\n mapping(uint256 => PoolInfo) public poolInfo;\n\n constructor(\n address _rewardsMinter,\n address _crv,\n address _cvx\n ) public {\n minter = _rewardsMinter;\n crv = _crv;\n cvx = _cvx;\n }\n\n function setPool(uint256 pid, address _lpToken)\n external\n returns (address rewards)\n {\n address token = address(new MockDepositToken());\n // Deploy a new Convex Rewards Pool\n rewards = address(\n new MockRewardPool(pid, token, crv, cvx, address(this))\n );\n\n poolInfo[pid] = PoolInfo({\n lptoken: _lpToken,\n token: token,\n crvRewards: rewards\n });\n }\n\n function deposit(\n uint256 _pid,\n uint256 _amount,\n bool _stake\n ) public returns (bool) {\n PoolInfo storage pool = poolInfo[_pid];\n\n address lptoken = pool.lptoken;\n\n // hold on to the Curve LP tokens\n IERC20(lptoken).safeTransferFrom(msg.sender, address(this), _amount);\n\n address token = pool.token;\n if (_stake) {\n // mint Convex pool LP tokens and stake in rewards contract on user behalf\n IMintableERC20(token).mint(_amount);\n address rewardContract = pool.crvRewards;\n IERC20(token).safeApprove(rewardContract, 0);\n IERC20(token).safeApprove(rewardContract, _amount);\n IRewardStaking(rewardContract).stakeFor(msg.sender, _amount);\n } else {\n // mint Convex pool LP tokens and send to user\n IMintableERC20(token).mint(_amount);\n IERC20(token).transfer(msg.sender, _amount);\n }\n return true;\n }\n\n // Deposit all Curve LP tokens and stake\n function depositAll(uint256 _pid, bool _stake) external returns (bool) {\n address lptoken = poolInfo[_pid].lptoken;\n uint256 balance = IERC20(lptoken).balanceOf(msg.sender);\n deposit(_pid, balance, _stake);\n return true;\n }\n\n // withdraw Curve LP tokens\n function _withdraw(\n uint256 _pid,\n uint256 _amount,\n address _from,\n address _to\n ) internal {\n PoolInfo storage pool = poolInfo[_pid];\n\n // burn the Convex pool LP tokens\n IBurnableERC20(pool.token).burnFrom(_from, _amount);\n\n // return the Curve LP tokens\n IERC20(pool.lptoken).safeTransfer(_to, _amount);\n }\n\n // withdraw Curve LP tokens\n function withdraw(uint256 _pid, uint256 _amount) public returns (bool) {\n _withdraw(_pid, _amount, msg.sender, msg.sender);\n return true;\n }\n\n // withdraw all Curve LP tokens\n function withdrawAll(uint256 _pid) public returns (bool) {\n address token = poolInfo[_pid].token;\n uint256 userBal = IERC20(token).balanceOf(msg.sender);\n withdraw(_pid, userBal);\n return true;\n }\n\n // allow reward contracts to send here and withdraw to user\n function withdrawTo(\n uint256 _pid,\n uint256 _amount,\n address _to\n ) external returns (bool) {\n address rewardContract = poolInfo[_pid].crvRewards;\n require(msg.sender == rewardContract, \"!auth\");\n\n _withdraw(_pid, _amount, msg.sender, _to);\n return true;\n }\n\n // callback from reward contract when crv is received.\n function rewardClaimed(\n uint256 _pid,\n // solhint-disable-next-line no-unused-vars\n address _address,\n uint256 _amount\n ) external returns (bool) {\n address rewardContract = poolInfo[_pid].crvRewards;\n require(msg.sender == rewardContract, \"!auth\");\n\n //mint reward tokens\n // and transfer it\n IMintableERC20(minter).mint(_amount);\n IERC20(minter).transfer(msg.sender, _amount);\n return true;\n }\n}\n" + }, + "contracts/mocks/curve/MockCRV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../MintableERC20.sol\";\n\ncontract MockCRV is MintableERC20 {\n constructor() ERC20(\"Curve DAO Token\", \"CRV\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/curve/MockCRVMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\n\ncontract MockCRVMinter {\n address crv;\n\n constructor(address _crv) {\n crv = _crv;\n }\n\n function mint(address _address) external {\n uint256 amount = 2e18;\n IMintableERC20(crv).mint(amount);\n IERC20(crv).transfer(_address, amount);\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveAbstractMetapool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { MintableERC20, IMintableERC20 } from \"../MintableERC20.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport \"../../utils/Helpers.sol\";\n\nabstract contract MockCurveAbstractMetapool is MintableERC20 {\n using StableMath for uint256;\n\n address[] public coins;\n uint256[2] public balances;\n\n // Returns the same amount of LP tokens in 1e18 decimals\n function add_liquidity(uint256[2] calldata _amounts, uint256 _minAmount)\n external\n returns (uint256 lpAmount)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transferFrom(\n msg.sender,\n address(this),\n _amounts[i]\n );\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to sum\n lpAmount += _amounts[i].scaleBy(18, assetDecimals);\n balances[i] = balances[i] + _amounts[i];\n }\n }\n // Hacky way of simulating slippage to check _minAmount\n if (lpAmount == 29000e18) lpAmount = 14500e18;\n require(lpAmount >= _minAmount, \"Slippage ruined your day\");\n // Send LP token to sender, e.g. 3CRV\n _mint(msg.sender, lpAmount);\n }\n\n // Dumb implementation that returns the same amount\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n public\n view\n returns (uint256 lpAmount)\n {\n uint256 assetDecimals = Helpers.getDecimals(coins[uint128(_index)]);\n lpAmount = _amount.scaleBy(assetDecimals, 18);\n }\n\n function remove_liquidity_one_coin(\n uint256 _lpAmount,\n int128 _index,\n // solhint-disable-next-line no-unused-vars\n uint256 _minAmount\n ) external returns (uint256 amount) {\n _burn(msg.sender, _lpAmount);\n uint256[] memory amounts = new uint256[](coins.length);\n amounts[uint128(_index)] = _lpAmount;\n amount = calc_withdraw_one_coin(_lpAmount, _index);\n balances[uint128(_index)] -= amount;\n IERC20(coins[uint128(_index)]).transfer(msg.sender, amount);\n }\n\n function get_virtual_price() external pure returns (uint256) {\n return 1e18;\n }\n\n // solhint-disable-next-line no-unused-vars\n function remove_liquidity(uint256 _amount, uint256[2] memory _min_amounts)\n public\n returns (uint256[2] memory amounts)\n {\n _burn(msg.sender, _amount);\n uint256 totalSupply = totalSupply();\n for (uint256 i = 0; i < 2; i++) {\n amounts[i] = totalSupply > 0\n ? (_amount * IERC20(coins[i]).balanceOf(address(this))) /\n totalSupply\n : IERC20(coins[i]).balanceOf(address(this));\n balances[i] -= amounts[i];\n IERC20(coins[i]).transfer(msg.sender, amounts[i]);\n }\n }\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens\n ) public returns (uint256) {\n return\n _remove_liquidity_imbalance(\n _amounts,\n _max_burned_tokens,\n msg.sender\n );\n }\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens,\n address _reveiver\n ) public returns (uint256) {\n return\n _remove_liquidity_imbalance(\n _amounts,\n _max_burned_tokens,\n _reveiver\n );\n }\n\n function _remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens,\n address _reveiver\n ) internal returns (uint256 lpTokens) {\n lpTokens = _max_burned_tokens;\n _burn(msg.sender, lpTokens);\n for (uint256 i = 0; i < _amounts.length; i++) {\n balances[i] -= _amounts[i];\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transfer(_reveiver, _amounts[i]);\n }\n }\n }\n\n // Dumb implementation that sums the scaled amounts\n function calc_token_amount(uint256[2] memory _amounts, bool)\n public\n view\n returns (uint256 lpTokens)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to lpTokens\n lpTokens += _amounts[i].scaleBy(18, assetDecimals);\n }\n }\n\n /// @notice 0.02% fee\n function fee() external pure returns (uint256) {\n return 2000000;\n }\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n\n function burnFrom(address from, uint256 value) public {\n _burn(from, value);\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveGauge.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { ICurveGauge } from \"../../strategies/ICurveGauge.sol\";\n\ncontract MockCurveGauge is ICurveGauge {\n mapping(address => uint256) private _balances;\n address lpToken;\n\n constructor(address _lpToken) {\n lpToken = _lpToken;\n }\n\n function balanceOf(address account) public view override returns (uint256) {\n return _balances[account];\n }\n\n function deposit(uint256 _value, address _account) external override {\n IERC20(lpToken).transferFrom(msg.sender, address(this), _value);\n _balances[_account] += _value;\n }\n\n function withdraw(uint256 _value) external override {\n IERC20(lpToken).transfer(msg.sender, _value);\n // solhint-disable-next-line reentrancy\n _balances[msg.sender] -= _value;\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveMetapool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { MockCurveAbstractMetapool } from \"./MockCurveAbstractMetapool.sol\";\nimport \"../MintableERC20.sol\";\n\ncontract MockCurveMetapool is MockCurveAbstractMetapool {\n constructor(address[2] memory _coins)\n ERC20(\"Curve.fi 3pool/OUSD metapool\", \"3crv_OUSD\")\n {\n coins = _coins;\n }\n}\n" + }, + "contracts/mocks/curve/MockCurvePool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IBurnableERC20 } from \"../BurnableERC20.sol\";\n\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\nimport { ICurvePool } from \"../../strategies/ICurvePool.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport \"../../utils/Helpers.sol\";\n\ncontract MockCurvePool {\n using StableMath for uint256;\n\n address[] public coins;\n uint256[3] public balances;\n address lpToken;\n uint256 public slippage = 1 ether;\n\n constructor(address[3] memory _coins, address _lpToken) {\n coins = _coins;\n lpToken = _lpToken;\n }\n\n function setCoins(address[] memory _coins) external {\n coins = _coins;\n }\n\n // Returns the same amount of LP tokens in 1e18 decimals\n function add_liquidity(uint256[3] calldata _amounts, uint256 _minAmount)\n external\n {\n uint256 sum = 0;\n for (uint256 i = 0; i < _amounts.length; i++) {\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transferFrom(\n msg.sender,\n address(this),\n _amounts[i]\n );\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to sum\n sum += _amounts[i].scaleBy(18, assetDecimals);\n balances[i] = balances[i] + _amounts[i];\n }\n }\n // Hacky way of simulating slippage to check _minAmount\n if (sum == 29000e18) sum = 14500e18;\n require(sum >= _minAmount, \"Slippage ruined your day\");\n // Send LP token to sender, e.g. 3CRV\n IMintableERC20(lpToken).mint(sum);\n IERC20(lpToken).transfer(msg.sender, sum);\n }\n\n // Dumb implementation that returns the same amount\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n public\n view\n returns (uint256)\n {\n uint256 assetDecimals = Helpers.getDecimals(coins[uint128(_index)]);\n return _amount.scaleBy(assetDecimals, 18);\n }\n\n function remove_liquidity_one_coin(\n uint256 _amount,\n int128 _index,\n // solhint-disable-next-line no-unused-vars\n uint256 _minAmount\n ) external {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _amount);\n uint256[] memory amounts = new uint256[](coins.length);\n amounts[uint128(_index)] = _amount;\n uint256 coinAmount = calc_withdraw_one_coin(_amount, _index);\n balances[uint128(_index)] -= coinAmount;\n IERC20(coins[uint128(_index)]).transfer(msg.sender, coinAmount);\n }\n\n function get_virtual_price() external pure returns (uint256) {\n return 1e18;\n }\n\n // solhint-disable-next-line no-unused-vars\n function remove_liquidity(uint256 _lpAmount, uint256[3] memory _min_amounts)\n public\n {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _lpAmount);\n uint256 totalSupply = IERC20(lpToken).totalSupply();\n for (uint256 i = 0; i < 3; i++) {\n uint256 coinAmount = totalSupply > 0\n ? (_lpAmount * IERC20(coins[i]).balanceOf(address(this))) /\n totalSupply\n : IERC20(coins[i]).balanceOf(address(this));\n balances[i] -= coinAmount;\n IERC20(coins[i]).transfer(msg.sender, coinAmount);\n }\n }\n\n function remove_liquidity_imbalance(\n uint256[3] memory _amounts,\n uint256 _max_burned_tokens\n ) public {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _max_burned_tokens);\n // For each coin, transfer to the caller\n for (uint256 i = 0; i < _amounts.length; i++) {\n balances[i] -= _amounts[i];\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transfer(msg.sender, _amounts[i]);\n }\n }\n }\n\n // Dumb implementation that sums the scaled amounts\n function calc_token_amount(uint256[3] memory _amounts, bool)\n public\n view\n returns (uint256 lpTokens)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to lpTokens\n lpTokens += _amounts[i].scaleBy(18, assetDecimals);\n }\n }\n\n function fee() external pure returns (uint256) {\n return 1000000;\n }\n\n function exchange(\n uint256 coin0,\n uint256 coin1,\n uint256 amountIn,\n uint256 minAmountOut\n ) external returns (uint256 amountOut) {\n IERC20(coins[coin0]).transferFrom(msg.sender, address(this), amountIn);\n amountOut = (minAmountOut * slippage) / 1 ether;\n require(amountOut >= minAmountOut, \"Slippage error\");\n IMintableERC20(coins[coin1]).mintTo(msg.sender, amountOut);\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n}\n" + }, + "contracts/mocks/curve/MockCVX.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../MintableERC20.sol\";\n\ncontract MockCVX is MintableERC20 {\n constructor() ERC20(\"CVX\", \"CVX DAO Token\") {}\n}\n" + }, + "contracts/mocks/curve/MockRewardPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\ninterface IDeposit {\n function poolInfo(uint256)\n external\n view\n returns (\n address,\n address,\n address,\n address,\n address,\n bool\n );\n\n function rewardClaimed(\n uint256,\n address,\n uint256\n ) external;\n\n function withdrawTo(\n uint256,\n uint256,\n address\n ) external;\n}\n\ncontract MockRewardPool {\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 public pid;\n address public stakingToken;\n address public rewardTokenA;\n address public rewardTokenB;\n address public operator;\n\n uint256 private _totalSupply;\n\n mapping(address => uint256) private _balances;\n mapping(address => uint256) public rewards;\n\n constructor(\n uint256 _pid,\n address _stakingToken,\n address _rewardTokenA,\n address _rewardTokenB,\n address _operator\n ) public {\n pid = _pid;\n stakingToken = _stakingToken;\n rewardTokenA = _rewardTokenA;\n rewardTokenB = _rewardTokenB;\n operator = _operator;\n }\n\n function totalSupply() public view returns (uint256) {\n return _totalSupply;\n }\n\n function balanceOf(address account) public view returns (uint256) {\n return _balances[account];\n }\n\n function stakeFor(address _for, uint256 _amount) public returns (bool) {\n require(_amount > 0, \"RewardPool : Cannot stake 0\");\n\n //give to _for\n _totalSupply = _totalSupply.add(_amount);\n _balances[_for] = _balances[_for].add(_amount);\n\n //take away from sender\n IERC20(stakingToken).safeTransferFrom(\n msg.sender,\n address(this),\n _amount\n );\n\n return true;\n }\n\n function withdrawAndUnwrap(uint256 amount, bool claim)\n public\n returns (bool)\n {\n _totalSupply = _totalSupply.sub(amount);\n _balances[msg.sender] = _balances[msg.sender].sub(amount);\n\n //tell operator to withdraw from here directly to user\n IDeposit(operator).withdrawTo(pid, amount, msg.sender);\n\n //get rewards too\n if (claim) {\n getReward(msg.sender, true);\n }\n return true;\n }\n\n function withdrawAllAndUnwrap(bool claim) external {\n withdrawAndUnwrap(_balances[msg.sender], claim);\n }\n\n // solhint-disable-next-line no-unused-vars\n function getReward(address _account, bool _claimExtras)\n public\n returns (bool)\n {\n IMintableERC20(rewardTokenA).mint(2 * 1e18);\n IERC20(rewardTokenA).transfer(_account, 2 * 1e18);\n\n IMintableERC20(rewardTokenB).mint(3 * 1e18);\n IERC20(rewardTokenB).transfer(_account, 3 * 1e18);\n\n return true;\n }\n\n function getReward() public returns (bool) {\n getReward(msg.sender, true);\n }\n}\n" + }, + "contracts/mocks/ForceEtherSender.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract ForceEtherSender {\n // Constructor to optionally receive Ether upon deployment\n constructor() payable {}\n\n // Function to allow the contract to receive Ether\n receive() external payable {}\n\n // Function to self-destruct and force-send Ether to an address\n function forceSend(address payable recipient) external {\n // Requires that the contract has a balance greater than 0\n require(address(this).balance > 0, \"No Ether to send\");\n\n // selfdestruct sends all Ether held by the contract to the recipient\n selfdestruct(recipient);\n }\n}\n" + }, + "contracts/mocks/MintableERC20.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ninterface IMintableERC20 {\n function mint(uint256 value) external;\n\n function mintTo(address to, uint256 value) external;\n}\n\n/**\n * @title MintableERC20\n * @dev Exposes the mint function of ERC20 for tests\n */\nabstract contract MintableERC20 is IMintableERC20, ERC20 {\n /**\n * @dev Function to mint tokens\n * @param _value The amount of tokens to mint.\n */\n function mint(uint256 _value) public virtual override {\n _mint(msg.sender, _value);\n }\n\n /**\n * @dev Function to mint tokens\n * @param _to Address to mint to.\n * @param _value The amount of tokens to mint.\n */\n function mintTo(address _to, uint256 _value) public virtual override {\n _mint(_to, _value);\n }\n}\n" + }, + "contracts/mocks/Mock1InchSwapRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { SwapDescription } from \"../interfaces/IOneInch.sol\";\n\ncontract Mock1InchSwapRouter {\n using SafeERC20 for IERC20;\n\n event MockSwap(address executor, bytes permitData, bytes executorData);\n\n event MockSwapDesc(\n address srcToken,\n address dstToken,\n address srcReceiver,\n address dstReceiver,\n uint256 amount,\n uint256 minReturnAmount,\n uint256 flags\n );\n\n event MockUnoswapTo(\n address recipient,\n address srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] pools\n );\n\n event MockUniswapV3SwapTo(\n address recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] pools\n );\n\n /**\n * @dev transfers the shource asset and returns the minReturnAmount of the destination asset.\n */\n function swap(\n address executor,\n SwapDescription calldata desc,\n bytes calldata permitData,\n bytes calldata executorData\n ) public returns (uint256 returnAmount, uint256 spentAmount) {\n // Transfer the source tokens to the receiver contract\n IERC20(desc.srcToken).safeTransferFrom(\n msg.sender,\n desc.srcReceiver,\n desc.amount\n );\n\n // Transfer the destination tokens to the recipient\n IERC20(desc.dstToken).safeTransfer(\n desc.dstReceiver,\n desc.minReturnAmount\n );\n\n emit MockSwap(executor, permitData, executorData);\n _swapDesc(desc);\n returnAmount = 0;\n spentAmount = 0;\n }\n\n function _swapDesc(SwapDescription calldata desc) public {\n emit MockSwapDesc(\n address(desc.srcToken),\n address(desc.dstToken),\n desc.srcReceiver,\n desc.dstReceiver,\n desc.amount,\n desc.minReturnAmount,\n desc.flags\n );\n }\n\n /**\n * @dev only transfers the source asset to this contract.\n * Ideally it would return the destination asset but that's encoded in the pools array.\n */\n function unoswapTo(\n address payable recipient,\n address srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) public returns (uint256 returnAmount) {\n // transfer the from asset from the caller\n IERC20(srcToken).safeTransferFrom(msg.sender, address(this), amount);\n\n emit MockUnoswapTo(recipient, srcToken, amount, minReturn, pools);\n returnAmount = 0;\n }\n\n /**\n * @dev does not do any transfers. Just emits MockUniswapV3SwapTo.\n */\n function uniswapV3SwapTo(\n address payable recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) public returns (uint256 returnAmount) {\n emit MockUniswapV3SwapTo(recipient, amount, minReturn, pools);\n returnAmount = 0;\n }\n}\n" + }, + "contracts/mocks/MockAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { IAaveLendingPool, ILendingPoolAddressesProvider } from \"../strategies/IAave.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// 1. User calls 'getLendingPool'\n// 2. User calls 'deposit' (Aave)\n// - Deposit their underlying\n// - Mint aToken to them\n// 3. User calls redeem (aToken)\n// - Retrieve their aToken\n// - Return equal amount of underlying\n\ncontract MockAToken is MintableERC20 {\n address public lendingPool;\n IERC20 public underlyingToken;\n using SafeERC20 for IERC20;\n\n constructor(\n address _lendingPool,\n string memory _name,\n string memory _symbol,\n IERC20 _underlyingToken\n ) ERC20(_name, _symbol) {\n lendingPool = _lendingPool;\n underlyingToken = _underlyingToken;\n // addMinter(_lendingPool);\n }\n\n function decimals() public view override returns (uint8) {\n return ERC20(address(underlyingToken)).decimals();\n }\n\n function poolRedeem(uint256 _amount, address _to) external {\n require(msg.sender == lendingPool, \"pool only\");\n // Redeem these a Tokens\n _burn(_to, _amount);\n // For the underlying\n underlyingToken.safeTransferFrom(lendingPool, _to, _amount);\n }\n}\n\ncontract MockAave is IAaveLendingPool, ILendingPoolAddressesProvider {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n mapping(address => address) reserveToAToken;\n address pool = address(this);\n address payable core = payable(address(this));\n uint256 factor;\n\n function addAToken(address _aToken, address _underlying) public {\n IERC20(_underlying).safeApprove(_aToken, 0);\n IERC20(_underlying).safeApprove(_aToken, type(uint256).max);\n reserveToAToken[_underlying] = _aToken;\n }\n\n // set the reserve factor / basically the interest on deposit\n // in 18 precision\n // so 0.5% would be 5 * 10 ^ 15\n function setFactor(uint256 factor_) public {\n factor = factor_;\n }\n\n function deposit(\n address _reserve,\n uint256 _amount,\n address _to,\n uint16 /*_referralCode*/\n ) external override {\n uint256 previousBal = IERC20(reserveToAToken[_reserve]).balanceOf(\n msg.sender\n );\n uint256 interest = previousBal.mulTruncate(factor);\n MintableERC20(reserveToAToken[_reserve]).mintTo(msg.sender, interest);\n // Take their reserve\n IERC20(_reserve).safeTransferFrom(msg.sender, address(this), _amount);\n // Credit them with aToken\n MintableERC20(reserveToAToken[_reserve]).mintTo(_to, _amount);\n }\n\n function withdraw(\n address asset,\n uint256 amount,\n address to\n ) external override returns (uint256) {\n MockAToken atoken = MockAToken(reserveToAToken[asset]);\n atoken.poolRedeem(amount, to);\n return amount;\n }\n\n function getLendingPool() external view override returns (address) {\n return pool;\n }\n}\n" + }, + "contracts/mocks/MockAaveIncentivesController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { MockStkAave } from \"./MockStkAave.sol\";\n\ncontract MockAaveIncentivesController {\n mapping(address => uint256) private rewards;\n MockStkAave public REWARD_TOKEN;\n\n constructor(address _reward_token) {\n REWARD_TOKEN = MockStkAave(_reward_token);\n }\n\n function setRewardsBalance(address user, uint256 amount) external {\n rewards[user] = amount;\n }\n\n /**\n * @dev Returns the total of rewards of an user, already accrued + not yet accrued\n * @param user The address of the user\n * @return The rewards\n **/\n // solhint-disable-next-line no-unused-vars\n function getRewardsBalance(address[] calldata assets, address user)\n external\n view\n returns (uint256)\n {\n return rewards[user];\n }\n\n /**\n * @dev Claims reward for an user, on all the assets of the lending pool, accumulating the pending rewards\n * @param amount Amount of rewards to claim\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewards(\n // solhint-disable-next-line no-unused-vars\n address[] calldata assets,\n uint256 amount,\n address to\n ) external returns (uint256) {\n require(amount > 0);\n require(rewards[to] == amount);\n REWARD_TOKEN.mint(amount);\n require(REWARD_TOKEN.transfer(to, amount));\n // solhint-disable-next-line reentrancy\n rewards[to] = 0;\n return amount;\n }\n}\n" + }, + "contracts/mocks/MockAAVEToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAAVEToken is MintableERC20 {\n constructor() ERC20(\"AAVE\", \"AAVE\") {}\n}\n" + }, + "contracts/mocks/MockAero.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAero is MintableERC20 {\n constructor() ERC20(\"Aerodrome\", \"AERO\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/MockAura.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAura is MintableERC20 {\n constructor() ERC20(\"Aura\", \"AURA\") {}\n}\n" + }, + "contracts/mocks/MockBAL.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockBAL is MintableERC20 {\n constructor() ERC20(\"Balancer\", \"BAL\") {}\n}\n" + }, + "contracts/mocks/MockBalancerVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// import \"hardhat/console.sol\";\n\ncontract MockBalancerVault {\n using StableMath for uint256;\n uint256 public slippage = 1 ether;\n bool public transferDisabled = false;\n bool public slippageErrorDisabled = false;\n\n function swap(\n IBalancerVault.SingleSwap calldata singleSwap,\n IBalancerVault.FundManagement calldata funds,\n uint256 minAmountOut,\n uint256\n ) external returns (uint256 amountCalculated) {\n amountCalculated = (minAmountOut * slippage) / 1 ether;\n if (!slippageErrorDisabled) {\n require(amountCalculated >= minAmountOut, \"Slippage error\");\n }\n IERC20(singleSwap.assetIn).transferFrom(\n funds.sender,\n address(this),\n singleSwap.amount\n );\n if (!transferDisabled) {\n MintableERC20(singleSwap.assetOut).mintTo(\n funds.recipient,\n amountCalculated\n );\n }\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n\n function getPoolTokenInfo(bytes32 poolId, address token)\n external\n view\n returns (\n uint256,\n uint256,\n uint256,\n address\n )\n {}\n\n function disableTransfer() external {\n transferDisabled = true;\n }\n\n function disableSlippageError() external {\n slippageErrorDisabled = true;\n }\n}\n" + }, + "contracts/mocks/MockBeaconConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconConsolidation } from \"../beacon/BeaconConsolidation.sol\";\n\ncontract MockBeaconConsolidation {\n function fee() external view returns (uint256) {\n return BeaconConsolidation.fee();\n }\n\n function request(bytes calldata source, bytes calldata target)\n external\n returns (uint256 fee_)\n {\n return BeaconConsolidation.request(source, target);\n }\n}\n" + }, + "contracts/mocks/MockChainlinkOracleFeed.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\ncontract MockChainlinkOracleFeed is AggregatorV3Interface {\n int256 price;\n uint8 numDecimals;\n\n constructor(int256 _price, uint8 _decimals) {\n price = _price;\n numDecimals = _decimals;\n }\n\n function decimals() external view override returns (uint8) {\n return numDecimals;\n }\n\n function description() external pure override returns (string memory) {\n return \"MockOracleEthFeed\";\n }\n\n function version() external pure override returns (uint256) {\n return 1;\n }\n\n function setPrice(int256 _price) public {\n price = _price;\n }\n\n function setDecimals(uint8 _decimals) public {\n numDecimals = _decimals;\n }\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(uint80 _roundId)\n external\n view\n override\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n )\n {\n roundId = _roundId;\n answer = price;\n startedAt = 0;\n updatedAt = 0;\n answeredInRound = 0;\n }\n\n function latestRoundData()\n external\n view\n override\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n )\n {\n roundId = 0;\n answer = price;\n startedAt = 0;\n updatedAt = block.timestamp;\n answeredInRound = 0;\n }\n}\n" + }, + "contracts/mocks/MockCOMP.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockCOMP is MintableERC20 {\n constructor() ERC20(\"COMP\", \"COMP\") {}\n}\n" + }, + "contracts/mocks/MockComptroller.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract MockComptroller {\n // Claim all the COMP accrued by specific holders in specific markets for their supplies and/or borrows\n function claimComp(\n address[] memory holders,\n address[] memory cTokens,\n bool borrowers,\n bool suppliers\n ) external {}\n}\n" + }, + "contracts/mocks/MockCToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { ICERC20 } from \"../strategies/ICompound.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockCToken is ICERC20, ERC20 {\n using StableMath for uint256;\n\n IERC20 public underlyingToken;\n // underlying = cToken * exchangeRate\n // cToken = underlying / exchangeRate\n uint256 exchangeRate;\n address public override comptroller;\n\n constructor(ERC20 _underlyingToken, address _comptroller)\n ERC20(\"cMock\", \"cMK\")\n {\n uint8 underlyingDecimals = _underlyingToken.decimals();\n // if has 18 dp, exchange rate should be 1e26\n // if has 8 dp, exchange rate should be 1e18\n if (underlyingDecimals > 8) {\n exchangeRate = 10**uint256(18 + underlyingDecimals - 10);\n } else if (underlyingDecimals < 8) {\n // e.g. 18-8+6 = 16\n exchangeRate = 10**uint256(18 - 8 + underlyingDecimals);\n } else {\n exchangeRate = 1e18;\n }\n underlyingToken = _underlyingToken;\n comptroller = _comptroller;\n }\n\n function decimals() public pure override returns (uint8) {\n return 8;\n }\n\n function mint(uint256 mintAmount) public override returns (uint256) {\n // Credit them with cToken\n _mint(msg.sender, mintAmount.divPrecisely(exchangeRate));\n // Take their reserve\n underlyingToken.transferFrom(msg.sender, address(this), mintAmount);\n return 0;\n }\n\n function redeem(uint256 redeemAmount) external override returns (uint256) {\n uint256 tokenAmount = redeemAmount.mulTruncate(exchangeRate);\n // Burn the cToken\n _burn(msg.sender, redeemAmount);\n // Transfer underlying to caller\n underlyingToken.transfer(msg.sender, tokenAmount);\n return 0;\n }\n\n function redeemUnderlying(uint256 redeemAmount)\n external\n override\n returns (uint256)\n {\n uint256 cTokens = redeemAmount.divPrecisely(exchangeRate);\n // Burn the cToken\n _burn(msg.sender, cTokens);\n // Transfer underlying to caller\n underlyingToken.transfer(msg.sender, redeemAmount);\n return 0;\n }\n\n function balanceOfUnderlying(address owner)\n external\n view\n override\n returns (uint256)\n {\n uint256 cTokenBal = this.balanceOf(owner);\n return cTokenBal.mulTruncate(exchangeRate);\n }\n\n function balanceOf(address owner)\n public\n view\n override(ICERC20, ERC20)\n returns (uint256)\n {\n return ERC20.balanceOf(owner);\n }\n\n function updateExchangeRate()\n internal\n view\n returns (uint256 newExchangeRate)\n {\n uint256 factor = 100002 * (10**13); // 0.002%\n newExchangeRate = exchangeRate.mulTruncate(factor);\n }\n\n function exchangeRateStored() external view override returns (uint256) {\n return exchangeRate;\n }\n\n function supplyRatePerBlock() external pure override returns (uint256) {\n return 141 * (10**8);\n }\n}\n" + }, + "contracts/mocks/MockCVXLocker.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MockCVXLocker {\n address public immutable cvx;\n mapping(address => uint256) public lockedBalanceOf;\n\n constructor(address _cvx) {\n cvx = _cvx;\n }\n\n function lock(\n address _account,\n uint256 _amount,\n uint256\n ) external {\n lockedBalanceOf[_account] += _amount;\n ERC20(cvx).transferFrom(msg.sender, address(this), _amount);\n }\n\n function unlockAllTokens(address _account) external {\n lockedBalanceOf[_account] = 0;\n ERC20(cvx).transfer(_account, lockedBalanceOf[_account]);\n }\n}\n" + }, + "contracts/mocks/MockDAI.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockDAI is MintableERC20 {\n constructor() ERC20(\"DAI\", \"DAI\") {}\n}\n" + }, + "contracts/mocks/MockDepositContract.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IDepositContract } from \"./../interfaces/IDepositContract.sol\";\n\ncontract MockDepositContract is IDepositContract {\n uint256 deposit_count;\n\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable override {\n require(pubkey.length == 48, \"DepositContract: invalid pubkey length\");\n require(\n withdrawal_credentials.length == 32,\n \"DepositContract: invalid withdrawal_credentials length\"\n );\n require(\n signature.length == 96,\n \"DepositContract: invalid signature length\"\n );\n\n // Check deposit amount\n require(msg.value >= 1 ether, \"DepositContract: deposit value too low\");\n require(\n msg.value % 1 gwei == 0,\n \"DepositContract: deposit value not multiple of gwei\"\n );\n uint256 deposit_amount = msg.value / 1 gwei;\n require(\n deposit_amount <= type(uint64).max,\n \"DepositContract: deposit value too high\"\n );\n\n // Emit `DepositEvent` log\n bytes memory amount = to_little_endian_64(uint64(deposit_amount));\n emit DepositEvent(\n pubkey,\n withdrawal_credentials,\n amount,\n signature,\n to_little_endian_64(uint64(deposit_count))\n );\n require(\n deposit_data_root != 0,\n \"DepositContract: invalid deposit_data_root\"\n );\n }\n\n function get_deposit_root() external view override returns (bytes32) {\n // just return some bytes32\n return sha256(abi.encodePacked(deposit_count, bytes16(0)));\n }\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view override returns (bytes memory) {\n return to_little_endian_64(uint64(deposit_count));\n }\n\n function to_little_endian_64(uint64 value)\n internal\n pure\n returns (bytes memory ret)\n {\n ret = new bytes(8);\n bytes8 bytesValue = bytes8(value);\n // Byteswapping during copying to bytes.\n ret[0] = bytesValue[7];\n ret[1] = bytesValue[6];\n ret[2] = bytesValue[5];\n ret[3] = bytesValue[4];\n ret[4] = bytesValue[3];\n ret[5] = bytesValue[2];\n ret[6] = bytesValue[1];\n ret[7] = bytesValue[0];\n }\n}\n" + }, + "contracts/mocks/MockEvilDAI.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract MockEvilDAI is MintableERC20 {\n address host;\n address realCoin;\n\n constructor(address _host, address _realCoin) ERC20(\"DAI\", \"DAI\") {\n host = _host;\n realCoin = _realCoin;\n }\n\n function transferFrom(\n // solhint-disable-next-line no-unused-vars\n address _from,\n // solhint-disable-next-line no-unused-vars\n address _to,\n uint256 _amount\n ) public override returns (bool) {\n // call mint again!\n if (_amount != 69) {\n IVault(host).mint(address(this), 69, 0);\n }\n return true;\n }\n}\n" + }, + "contracts/mocks/MockEvilReentrantContract.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IRateProvider } from \"../interfaces/balancer/IRateProvider.sol\";\n\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { IERC20 } from \"../utils/InitializableAbstractStrategy.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockEvilReentrantContract {\n using StableMath for uint256;\n\n IBalancerVault public immutable balancerVault;\n IERC20 public immutable reth;\n IERC20 public immutable weth;\n IVault public immutable oethVault;\n address public immutable poolAddress;\n bytes32 public immutable balancerPoolId;\n\n constructor(\n address _balancerVault,\n address _oethVault,\n address _reth,\n address _weth,\n address _poolAddress,\n bytes32 _poolId\n ) {\n balancerVault = IBalancerVault(_balancerVault);\n oethVault = IVault(_oethVault);\n reth = IERC20(_reth);\n weth = IERC20(_weth);\n poolAddress = _poolAddress;\n balancerPoolId = _poolId;\n }\n\n function doEvilStuff() public {\n address priceProvider = oethVault.priceProvider();\n uint256 rethPrice = IOracle(priceProvider).price(address(reth));\n\n // 1. Join pool\n uint256[] memory amounts = new uint256[](2);\n amounts[0] = uint256(10 ether);\n amounts[1] = rethPrice * 10;\n\n address[] memory assets = new address[](2);\n assets[0] = address(reth);\n assets[1] = address(weth);\n\n uint256 minBPT = getBPTExpected(assets, amounts).mulTruncate(\n 0.99 ether\n );\n\n bytes memory joinUserData = abi.encode(\n IBalancerVault.WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,\n amounts,\n minBPT\n );\n\n IBalancerVault.JoinPoolRequest memory joinRequest = IBalancerVault\n .JoinPoolRequest(assets, amounts, joinUserData, false);\n\n balancerVault.joinPool(\n balancerPoolId,\n address(this),\n address(this),\n joinRequest\n );\n\n uint256 bptTokenBalance = IERC20(poolAddress).balanceOf(address(this));\n\n // 2. Redeem as ETH\n bytes memory exitUserData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT,\n bptTokenBalance,\n 1\n );\n\n assets[1] = address(0); // Receive ETH instead of WETH\n uint256[] memory exitAmounts = new uint256[](2);\n exitAmounts[1] = 15 ether;\n IBalancerVault.ExitPoolRequest memory exitRequest = IBalancerVault\n .ExitPoolRequest(assets, exitAmounts, exitUserData, false);\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n payable(address(this)),\n exitRequest\n );\n bptTokenBalance = IERC20(poolAddress).balanceOf(address(this));\n }\n\n function getBPTExpected(address[] memory _assets, uint256[] memory _amounts)\n internal\n view\n virtual\n returns (uint256 bptExpected)\n {\n // Get the oracle from the OETH Vault\n address priceProvider = oethVault.priceProvider();\n\n for (uint256 i = 0; i < _assets.length; ++i) {\n uint256 strategyAssetMarketPrice = IOracle(priceProvider).price(\n _assets[i]\n );\n // convert asset amount to ETH amount\n bptExpected =\n bptExpected +\n _amounts[i].mulTruncate(strategyAssetMarketPrice);\n }\n\n uint256 bptRate = IRateProvider(poolAddress).getRate();\n // Convert ETH amount to BPT amount\n bptExpected = bptExpected.divPrecisely(bptRate);\n }\n\n function approveAllTokens() public {\n // Approve all tokens\n weth.approve(address(oethVault), type(uint256).max);\n reth.approve(poolAddress, type(uint256).max);\n weth.approve(poolAddress, type(uint256).max);\n reth.approve(address(balancerVault), type(uint256).max);\n weth.approve(address(balancerVault), type(uint256).max);\n }\n\n receive() external payable {\n // 3. Try to mint OETH\n oethVault.mint(address(weth), 1 ether, 0.9 ether);\n }\n}\n" + }, + "contracts/mocks/MockfrxETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockfrxETH is MintableERC20 {\n constructor() ERC20(\"frxETH\", \"frxETH\") {}\n}\n" + }, + "contracts/mocks/MockFrxETHMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockFrxETHMinter {\n address public immutable frxETH;\n address public immutable sfrxETH;\n\n constructor(address _frxETH, address _sfrxETH) {\n frxETH = _frxETH;\n sfrxETH = _sfrxETH;\n }\n\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares)\n {\n IMintableERC20(frxETH).mintTo(sfrxETH, msg.value);\n IMintableERC20(sfrxETH).mintTo(recipient, msg.value);\n shares = msg.value;\n }\n}\n" + }, + "contracts/mocks/MockLimitedWrappedOusd.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WrappedOusd } from \"../token/WrappedOusd.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MockLimitedWrappedOusd is WrappedOusd {\n constructor(ERC20 underlying_) WrappedOusd(underlying_) {}\n\n function maxDeposit(address)\n public\n view\n virtual\n override\n returns (uint256)\n {\n return 1e18;\n }\n}\n" + }, + "contracts/mocks/MockMaverickDistributor.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Pool } from \"../interfaces/plume/IMaverickV2Pool.sol\";\n\ncontract MockMaverickDistributor {\n IERC20 public immutable rewardToken;\n\n uint256 public lastEpoch;\n uint256 public rewardAmount;\n\n constructor(address _rewardToken) {\n rewardToken = IERC20(_rewardToken);\n }\n\n function setLastEpoch(uint256 _epoch) external {\n lastEpoch = _epoch;\n }\n\n function setRewardTokenAmount(uint256 _amount) external {\n rewardAmount = _amount;\n }\n\n function claimLp(\n address recipient,\n uint256,\n IMaverickV2Pool,\n uint32[] memory,\n uint256\n ) external returns (uint256 amount) {\n rewardToken.transfer(recipient, rewardAmount);\n return rewardAmount;\n }\n}\n" + }, + "contracts/mocks/MockMetadataToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// IERC20Metadata is used in the resolveAsset function in contracts/utils/assets.js\n// We just need to import it here to make its ABI available to Hardhat\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n" + }, + "contracts/mocks/MockMintableUniswapPair.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport \"./MockUniswapPair.sol\";\n\ncontract MockMintableUniswapPair is MockUniswapPair, MintableERC20 {\n constructor(\n address _token0,\n address _token1,\n uint112 _reserve0,\n uint112 _reserve1\n )\n MockUniswapPair(_token0, _token1, _reserve0, _reserve1)\n ERC20(\"Uniswap V2\", \"UNI-v2\")\n {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/MockNonRebasing.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { OUSD } from \"../token/OUSD.sol\";\n\ncontract MockNonRebasing {\n OUSD oUSD;\n\n function setOUSD(address _oUSDAddress) public {\n oUSD = OUSD(_oUSDAddress);\n }\n\n function rebaseOptIn() public {\n oUSD.rebaseOptIn();\n }\n\n function rebaseOptOut() public {\n oUSD.rebaseOptOut();\n }\n\n function transfer(address _to, uint256 _value) public {\n oUSD.transfer(_to, _value);\n }\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) public {\n oUSD.transferFrom(_from, _to, _value);\n }\n\n function approve(address _spender, uint256 _addedValue) public {\n oUSD.approve(_spender, _addedValue);\n }\n\n function mintOusd(\n address _vaultContract,\n address _asset,\n uint256 _amount\n ) public {\n IVault(_vaultContract).mint(_asset, _amount, 0);\n }\n\n function redeemOusd(address _vaultContract, uint256 _amount) public {\n IVault(_vaultContract).redeem(_amount, 0);\n }\n\n function approveFor(\n address _contract,\n address _spender,\n uint256 _addedValue\n ) public {\n IERC20(_contract).approve(_spender, _addedValue);\n }\n}\n" + }, + "contracts/mocks/MockNonStandardToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\nimport \"./MintableERC20.sol\";\n\n/**\n * Mock token contract to simulate tokens that don't\n * throw/revert when a transfer/transferFrom call fails\n */\ncontract MockNonStandardToken is MintableERC20 {\n using SafeMath for uint256;\n\n constructor() ERC20(\"NonStandardToken\", \"NonStandardToken\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n\n function transfer(address recipient, uint256 amount)\n public\n override\n returns (bool)\n {\n if (balanceOf(msg.sender) < amount) {\n // Fail silently\n return false;\n }\n\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public override returns (bool) {\n if (balanceOf(sender) < amount) {\n // Fail silently\n return false;\n }\n\n _transfer(sender, recipient, amount);\n _approve(\n sender,\n _msgSender(),\n allowance(sender, _msgSender()).sub(\n amount,\n \"ERC20: transfer amount exceeds allowance\"\n )\n );\n return true;\n }\n}\n" + }, + "contracts/mocks/MockOETHVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultCore } from \"../vault/OETHVaultCore.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MockOETHVault is OETHVaultCore {\n using StableMath for uint256;\n\n constructor(address _weth) OETHVaultCore(_weth) {\n _setGovernor(msg.sender);\n }\n\n function supportAsset(address asset) external {\n assets[asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(0),\n decimals: 18,\n allowedOracleSlippageBps: 0\n });\n\n allAssets.push(asset);\n }\n}\n" + }, + "contracts/mocks/MockOETHVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"../vault/OETHVaultAdmin.sol\";\n\ncontract MockOETHVaultAdmin is OETHVaultAdmin {\n constructor(address _weth) OETHVaultAdmin(_weth) {}\n\n // fetches the WETH amount in outstanding withdrawals\n function outstandingWithdrawalsAmount()\n external\n view\n returns (uint256 wethAmount)\n {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n wethAmount = queue.queued - queue.claimed;\n }\n\n function wethAvailable() external view returns (uint256) {\n return _wethAvailable();\n }\n}\n" + }, + "contracts/mocks/MockOGN.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./BurnableERC20.sol\";\nimport \"./MintableERC20.sol\";\n\n/**\n * @title Origin token (OGN).\n *\n * @dev Token that allows minting and burning.\n * @dev Important note:\n * @dev There is a known race condition in the ERC20 standard on the approve() method.\n * @dev See details: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n * @dev The Origin token contract implements the increaseApproval() and decreaseApproval() methods.\n * @dev It is strongly recommended to use those methods rather than approve()\n * @dev when updating the token allowance.\n */\ncontract MockOGN is MintableERC20, BurnableERC20 {\n event SetWhitelistExpiration(uint256 expiration);\n event AllowedTransactorAdded(address sender);\n event AllowedTransactorRemoved(address sender);\n event AddCallSpenderWhitelist(address enabler, address spender);\n event RemoveCallSpenderWhitelist(address disabler, address spender);\n\n mapping(address => bool) public callSpenderWhitelist;\n address public owner = msg.sender;\n // UNIX timestamp (in seconds) after which this whitelist no longer applies\n uint256 public whitelistExpiration;\n // While the whitelist is active, either the sender or recipient must be\n // in allowedTransactors.\n mapping(address => bool) public allowedTransactors;\n\n // @dev Constructor that gives msg.sender all initial tokens.\n constructor(uint256 _initialSupply) ERC20(\"OriginToken\", \"OGN\") {\n owner = msg.sender;\n _mint(owner, _initialSupply);\n }\n\n //\n // approveAndCall methods\n //\n\n // @dev Add spender to whitelist of spenders for approveAndCall\n // @param _spender Address to add\n function addCallSpenderWhitelist(address _spender) public onlyOwner {\n callSpenderWhitelist[_spender] = true;\n emit AddCallSpenderWhitelist(msg.sender, _spender);\n }\n\n // @dev Remove spender from whitelist of spenders for approveAndCall\n // @param _spender Address to remove\n function removeCallSpenderWhitelist(address _spender) public onlyOwner {\n delete callSpenderWhitelist[_spender];\n emit RemoveCallSpenderWhitelist(msg.sender, _spender);\n }\n\n // @dev Approve transfer of tokens and make a contract call in a single\n // @dev transaction. This allows a DApp to avoid requiring two MetaMask\n // @dev approvals for a single logical action, such as creating a listing,\n // @dev which requires the seller to approve a token transfer and the\n // @dev marketplace contract to transfer tokens from the seller.\n //\n // @dev This is based on the ERC827 function approveAndCall and avoids\n // @dev security issues by only working with a whitelisted set of _spender\n // @dev addresses. The other difference is that the combination of this\n // @dev function ensures that the proxied function call receives the\n // @dev msg.sender for this function as its first parameter.\n //\n // @param _spender The address that will spend the funds.\n // @param _value The amount of tokens to be spent.\n // @param _selector Function selector for function to be called.\n // @param _callParams Packed, encoded parameters, omitting the first parameter which is always msg.sender\n function approveAndCallWithSender(\n address _spender,\n uint256 _value,\n bytes4 _selector,\n bytes memory _callParams\n ) public payable returns (bool) {\n require(_spender != address(this), \"token contract can't be approved\");\n require(callSpenderWhitelist[_spender], \"spender not in whitelist\");\n\n require(super.approve(_spender, _value), \"approve failed\");\n\n bytes memory callData = abi.encodePacked(\n _selector,\n uint256(uint160(msg.sender)),\n _callParams\n );\n // solium-disable-next-line security/no-call-value\n (bool success, ) = _spender.call{ value: msg.value }(callData);\n require(success, \"proxied call failed\");\n return true;\n }\n\n //\n // Functions for maintaining whitelist\n //\n\n modifier onlyOwner() {\n require(msg.sender == owner);\n _;\n }\n modifier allowedTransfer(address _from, address _to) {\n require(\n // solium-disable-next-line operator-whitespace\n !whitelistActive() ||\n allowedTransactors[_from] ||\n allowedTransactors[_to],\n \"neither sender nor recipient are allowed\"\n );\n _;\n }\n\n function whitelistActive() public view returns (bool) {\n return block.timestamp < whitelistExpiration;\n }\n\n function addAllowedTransactor(address _transactor) public onlyOwner {\n emit AllowedTransactorAdded(_transactor);\n allowedTransactors[_transactor] = true;\n }\n\n function removeAllowedTransactor(address _transactor) public onlyOwner {\n emit AllowedTransactorRemoved(_transactor);\n delete allowedTransactors[_transactor];\n }\n\n /**\n * @dev Set the whitelist expiration, after which the whitelist no longer\n * applies.\n */\n function setWhitelistExpiration(uint256 _expiration) public onlyOwner {\n // allow only if whitelist expiration hasn't yet been set, or if the\n // whitelist expiration hasn't passed yet\n require(\n whitelistExpiration == 0 || whitelistActive(),\n \"an expired whitelist cannot be extended\"\n );\n // prevent possible mistakes in calling this function\n require(\n _expiration >= block.timestamp + 1 days,\n \"whitelist expiration not far enough into the future\"\n );\n emit SetWhitelistExpiration(_expiration);\n whitelistExpiration = _expiration;\n }\n\n //\n // ERC20 transfer functions that have been overridden to enforce the\n // whitelist.\n //\n\n function transfer(address _to, uint256 _value)\n public\n override\n allowedTransfer(msg.sender, _to)\n returns (bool)\n {\n return super.transfer(_to, _value);\n }\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) public override allowedTransfer(_from, _to) returns (bool) {\n return super.transferFrom(_from, _to, _value);\n }\n}\n" + }, + "contracts/mocks/MockOGV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockOGV is MintableERC20 {\n constructor() ERC20(\"OGV\", \"OGV\") {}\n}\n" + }, + "contracts/mocks/MockOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/IPriceOracle.sol\";\nimport \"../interfaces/IMinMaxOracle.sol\";\n\n/**\n * Mock of both price Oracle and min max oracles\n */\ncontract MockOracle is IPriceOracle, IMinMaxOracle {\n mapping(bytes32 => uint256) prices;\n mapping(bytes32 => uint256[]) pricesMinMax;\n uint256 ethMin;\n uint256 ethMax;\n\n /**\n * @dev returns the asset price in USD, 6 decimal digits.\n * Compatible with the Open Price Feed.\n */\n function price(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n return prices[keccak256(abi.encodePacked(symbol))];\n }\n\n /**\n * @dev sets the price of the asset in USD, 6 decimal digits\n *\n */\n function setPrice(string calldata symbol, uint256 _price) external {\n prices[keccak256(abi.encodePacked(symbol))] = _price;\n }\n\n /**\n * @dev sets the min and max price of ETH in USD, 6 decimal digits\n *\n */\n function setEthPriceMinMax(uint256 _min, uint256 _max) external {\n ethMin = _min;\n ethMax = _max;\n }\n\n /**\n * @dev sets the prices Min Max for a specific symbol in ETH, 8 decimal digits\n *\n */\n function setTokPriceMinMax(\n string calldata symbol,\n uint256 _min,\n uint256 _max\n ) external {\n pricesMinMax[keccak256(abi.encodePacked(symbol))] = [_min, _max];\n }\n\n /**\n * @dev get the price of asset in ETH, 8 decimal digits.\n */\n function priceMin(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n uint256[] storage pMinMax = pricesMinMax[\n keccak256(abi.encodePacked(symbol))\n ];\n return (pMinMax[0] * ethMin) / 1e6;\n }\n\n /**\n * @dev get the price of asset in USD, 8 decimal digits.\n * Not needed for now\n */\n function priceMax(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n uint256[] storage pMinMax = pricesMinMax[\n keccak256(abi.encodePacked(symbol))\n ];\n return (pMinMax[1] * ethMax) / 1e6;\n }\n}\n" + }, + "contracts/mocks/MockOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"../oracle/AbstractOracleRouter.sol\";\n\n// @notice Oracle Router required for testing environment\ncontract MockOracleRouter is AbstractOracleRouter {\n struct FeedMetadata {\n address feedAddress;\n uint256 maxStaleness;\n }\n\n mapping(address => FeedMetadata) public assetToFeedMetadata;\n\n /* @dev Override feed and maxStaleness information for a particular asset\n * @param _asset the asset to override feed for\n * @param _feed new feed\n * @param _maxStaleness new maximum time allowed for feed data to be stale\n */\n function setFeed(\n address _asset,\n address _feed,\n uint256 _maxStaleness\n ) external {\n assetToFeedMetadata[_asset] = FeedMetadata(_feed, _maxStaleness);\n }\n\n /*\n * The dev version of the Oracle doesn't need to gas optimize and cache the decimals\n */\n function getDecimals(address _feed) internal view override returns (uint8) {\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n return AggregatorV3Interface(_feed).decimals();\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n FeedMetadata storage fm = assetToFeedMetadata[asset];\n feedAddress = fm.feedAddress;\n maxStaleness = fm.maxStaleness;\n }\n}\n" + }, + "contracts/mocks/MockOracleRouterNoStale.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { OracleRouter } from \"../oracle/OracleRouter.sol\";\nimport { OETHOracleRouter } from \"../oracle/OETHOracleRouter.sol\";\n\n// @notice Oracle Router used to bypass staleness\ncontract MockOracleRouterNoStale is OracleRouter {\n function feedMetadata(address asset)\n internal\n pure\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n (feedAddress, ) = super.feedMetadata(asset);\n maxStaleness = 365 days;\n }\n}\n\n// @notice Oracle Router used to bypass staleness\ncontract MockOETHOracleRouterNoStale is OETHOracleRouter {\n constructor() OETHOracleRouter() {}\n\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n (feedAddress, ) = super.feedMetadata(asset);\n maxStaleness = 365 days;\n }\n}\n" + }, + "contracts/mocks/MockOracleWeightedPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Variable, OracleAverageQuery, IOracleWeightedPool } from \"../interfaces/balancer/IOracleWeightedPool.sol\";\n\ncontract MockOracleWeightedPool is IOracleWeightedPool {\n uint256[] public nextResults;\n\n constructor() {\n nextResults = [1 ether, 1 ether];\n }\n\n function getTimeWeightedAverage(OracleAverageQuery[] memory)\n external\n view\n override\n returns (uint256[] memory results)\n {\n return nextResults;\n }\n\n function setNextResults(uint256[] calldata results) external {\n nextResults = results;\n }\n}\n" + }, + "contracts/mocks/MockPartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PartialWithdrawal } from \"../beacon/PartialWithdrawal.sol\";\n\ncontract MockPartialWithdrawal {\n function fee() external view returns (uint256) {\n return PartialWithdrawal.fee();\n }\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n external\n returns (uint256 fee_)\n {\n return PartialWithdrawal.request(validatorPubKey, amount);\n }\n}\n" + }, + "contracts/mocks/MockRebornMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n// solhint-disable-next-line no-console\nimport \"hardhat/console.sol\";\n\ncontract Sanctum {\n address public asset;\n address public vault;\n address public reborner;\n bool public shouldAttack = false;\n // should selfdestruct in the constructor\n bool public shouldDestruct = false;\n uint256 public targetMethod;\n address public ousdContract;\n\n constructor(address _asset, address _vault) {\n asset = _asset;\n vault = _vault;\n }\n\n function deploy(uint256 salt, bytes memory bytecode)\n public\n returns (address addr)\n {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt)\n }\n require(addr != address(0), \"Create2: Failed on deploy\");\n }\n\n function computeAddress(uint256 salt, bytes memory bytecode)\n public\n view\n returns (address)\n {\n bytes32 bytecodeHashHash = keccak256(bytecode);\n bytes32 _data = keccak256(\n abi.encodePacked(\n bytes1(0xff),\n address(this),\n salt,\n bytecodeHashHash\n )\n );\n return address(bytes20(_data << 96));\n }\n\n function setShouldAttack(bool _shouldAttack) public {\n shouldAttack = _shouldAttack;\n }\n\n // should call selfdestruct in the constructor\n function setShouldDesctruct(bool _shouldDestruct) public {\n shouldDestruct = _shouldDestruct;\n }\n\n function setTargetMethod(uint256 target) public {\n targetMethod = target;\n }\n\n function setOUSDAddress(address _ousdContract) public {\n ousdContract = _ousdContract;\n }\n}\n\ncontract Reborner {\n Sanctum sanctum;\n bool logging = false;\n\n constructor(address _sanctum) {\n log(\"We are created...\");\n sanctum = Sanctum(_sanctum);\n if (sanctum.shouldAttack()) {\n log(\"We are attacking now...\");\n\n uint256 target = sanctum.targetMethod();\n\n if (target == 1) {\n redeem();\n } else if (target == 2) {\n transfer();\n } else {\n mint();\n }\n }\n\n if (sanctum.shouldDestruct()) {\n bye();\n }\n }\n\n function mint() public {\n log(\"We are attempting to mint..\");\n address asset = sanctum.asset();\n address vault = sanctum.vault();\n IERC20(asset).approve(vault, 1e18);\n IVault(vault).mint(asset, 1e18, 0);\n log(\"We are now minting..\");\n }\n\n function redeem() public {\n log(\"We are attempting to redeem..\");\n address vault = sanctum.vault();\n IVault(vault).redeem(1e18, 1e18);\n log(\"We are now redeeming..\");\n }\n\n function transfer() public {\n log(\"We are attempting to transfer..\");\n address ousd = sanctum.ousdContract();\n require(IERC20(ousd).transfer(address(1), 1e18), \"transfer failed\");\n log(\"We are now transfering..\");\n }\n\n function bye() public {\n log(\"We are now destructing..\");\n selfdestruct(payable(msg.sender));\n }\n\n function log(string memory message) internal view {\n if (logging) {\n // solhint-disable-next-line no-console\n console.log(message);\n }\n }\n}\n" + }, + "contracts/mocks/MockRETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport \"../interfaces/IGetExchangeRateToken.sol\";\n\ncontract MockRETH is MintableERC20, IGetExchangeRateToken {\n uint256 private exchangeRate = 12e17;\n\n constructor() ERC20(\"Rocket Pool ETH\", \"rETH\") {}\n\n function getExchangeRate() external view override returns (uint256) {\n return exchangeRate;\n }\n\n function setExchangeRate(uint256 _rate) external {\n exchangeRate = _rate;\n }\n}\n" + }, + "contracts/mocks/MockRoosterAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Rooster AMO strategy exposing extra functionality\n * @author Origin Protocol Inc\n */\n\nimport { RoosterAMOStrategy } from \"../strategies/plume/RoosterAMOStrategy.sol\";\nimport { IMaverickV2Pool } from \"../interfaces/plume/IMaverickV2Pool.sol\";\n\ncontract MockRoosterAMOStrategy is RoosterAMOStrategy {\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethpAddress,\n address _liquidityManager,\n address _poolLens,\n address _maverickPosition,\n address _maverickQuoter,\n address _mPool,\n bool _upperTickAtParity,\n address _votingDistributor,\n address _poolDistributor\n )\n RoosterAMOStrategy(\n _stratConfig,\n _wethAddress,\n _oethpAddress,\n _liquidityManager,\n _poolLens,\n _maverickPosition,\n _maverickQuoter,\n _mPool,\n _upperTickAtParity,\n _votingDistributor,\n _poolDistributor\n )\n {}\n\n function getCurrentWethShare() external view returns (uint256) {\n uint256 _currentPrice = getPoolSqrtPrice();\n\n return _getWethShare(_currentPrice);\n }\n}\n" + }, + "contracts/mocks/MockSFC.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockSFC {\n error ZeroAmount();\n error TransferFailed();\n error StakeIsFullySlashed();\n\n // Mapping of delegator address to validator ID to amount delegated\n mapping(address => mapping(uint256 => uint256)) public delegations;\n // Mapping of delegator address to validator ID to withdrawal request ID to amount\n mapping(address => mapping(uint256 => mapping(uint256 => uint256)))\n public withdraws;\n // validator ID -> slashing refund ratio (allows to withdraw slashed stake)\n mapping(uint256 => uint256) public slashingRefundRatio;\n\n function getStake(address delegator, uint256 validatorID)\n external\n view\n returns (uint256)\n {\n return delegations[delegator][validatorID];\n }\n\n function delegate(uint256 validatorID) external payable {\n if (msg.value == 0) {\n revert ZeroAmount();\n }\n delegations[msg.sender][validatorID] += msg.value;\n }\n\n function undelegate(\n uint256 validatorID,\n uint256 wrID,\n uint256 amount\n ) external {\n require(\n delegations[msg.sender][validatorID] >= amount,\n \"insufficient stake\"\n );\n require(\n withdraws[msg.sender][validatorID][wrID] == 0,\n \"withdrawal request already exists\"\n );\n\n delegations[msg.sender][validatorID] -= amount;\n withdraws[msg.sender][validatorID][wrID] = amount;\n }\n\n function withdraw(uint256 validatorID, uint256 wrID) external {\n require(withdraws[msg.sender][validatorID][wrID] > 0, \"no withdrawal\");\n\n uint256 withdrawAmount = withdraws[msg.sender][validatorID][wrID];\n uint256 penalty = (withdrawAmount *\n (1e18 - slashingRefundRatio[validatorID])) / 1e18;\n\n if (penalty >= withdrawAmount) {\n revert StakeIsFullySlashed();\n }\n\n (bool sent, ) = msg.sender.call{ value: withdrawAmount - penalty }(\"\");\n if (!sent) {\n revert TransferFailed();\n }\n }\n\n function pendingRewards(address delegator, uint256 validatorID)\n external\n view\n returns (uint256)\n {}\n\n function claimRewards(uint256 validatorID) external {}\n\n function restakeRewards(uint256 validatorID) external {}\n\n /// @param refundRatio the percentage of the staked amount that can be refunded. 0.1e18 = 10%, 1e18 = 100%\n function slashValidator(uint256 validatorID, uint256 refundRatio) external {\n require(refundRatio <= 1e18, \"invalid refund ratio\");\n slashingRefundRatio[validatorID] = refundRatio;\n }\n}\n" + }, + "contracts/mocks/MocksfrxETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MocksfrxETH is MintableERC20 {\n address public frxETH;\n\n constructor(address _frxETH) ERC20(\"sfrxETH\", \"sfrxETH\") {\n frxETH = _frxETH;\n }\n\n function setMockfrxETHAddress(address _frxETH) external {\n frxETH = _frxETH;\n }\n\n function deposit(uint256 assets, address receiver)\n external\n returns (uint256 shares)\n {\n ERC20(frxETH).transferFrom(msg.sender, address(this), assets);\n\n _mint(receiver, assets);\n\n return assets;\n }\n\n function maxWithdraw(address owner) external view returns (uint256) {\n return balanceOf(owner);\n }\n\n function setMaxWithdrawableBalance(address owner, uint256 balance)\n external\n {\n uint256 currentBalance = balanceOf(owner);\n if (currentBalance > balance) {\n _burn(owner, currentBalance - balance);\n } else if (balance > currentBalance) {\n _mint(owner, balance - currentBalance);\n }\n }\n\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets) {\n _burn(owner, shares);\n\n ERC20(frxETH).transfer(receiver, shares);\n\n assets = shares;\n }\n\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares) {\n _burn(owner, assets);\n\n ERC20(frxETH).transfer(receiver, assets);\n\n shares = assets;\n }\n\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares)\n {\n _mint(recipient, msg.value);\n shares = msg.value;\n }\n}\n" + }, + "contracts/mocks/MockSSV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockSSV is MintableERC20 {\n constructor() ERC20(\"SSV Token\", \"SSV\") {}\n}\n" + }, + "contracts/mocks/MockSSVNetwork.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Cluster } from \"./../interfaces/ISSVNetwork.sol\";\n\ncontract MockSSVNetwork {\n function registerValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function exitValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds\n ) external {}\n\n function removeValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external {}\n\n function deposit(\n address clusterOwner,\n uint64[] calldata operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function setFeeRecipientAddress(address recipient) external {}\n}\n" + }, + "contracts/mocks/MockstETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockstETH is MintableERC20 {\n constructor() ERC20(\"stETH\", \"stETH\") {}\n}\n" + }, + "contracts/mocks/MockStkAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"./MintableERC20.sol\";\n\ncontract MockStkAave is MintableERC20 {\n uint256 public COOLDOWN_SECONDS = 864000;\n uint256 public UNSTAKE_WINDOW = 172800;\n address public STAKED_TOKEN;\n\n mapping(address => uint256) public stakerRewardsToClaim;\n mapping(address => uint256) public stakersCooldowns;\n\n using SafeERC20 for IERC20;\n\n constructor(address _stakedToken) ERC20(\"Staked Aave\", \"stkAAVE\") {\n STAKED_TOKEN = _stakedToken;\n }\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n\n function setStakedToken(address _stakedToken) external {\n STAKED_TOKEN = _stakedToken;\n }\n\n /**\n * @dev Redeems staked tokens, and stop earning rewards\n * @param to Address to redeem to\n * @param amount Amount to redeem\n **/\n function redeem(address to, uint256 amount) external {\n uint256 cooldownStartTimestamp = stakersCooldowns[msg.sender];\n uint256 windowStart = cooldownStartTimestamp + COOLDOWN_SECONDS;\n require(amount != 0, \"INVALID_ZERO_AMOUNT\");\n require(block.timestamp > windowStart, \"INSUFFICIENT_COOLDOWN\");\n require(\n block.timestamp - windowStart <= UNSTAKE_WINDOW,\n \"UNSTAKE_WINDOW_FINISHED\"\n );\n uint256 balanceOfMessageSender = balanceOf(msg.sender);\n uint256 amountToRedeem = (amount > balanceOfMessageSender)\n ? balanceOfMessageSender\n : amount;\n\n stakersCooldowns[msg.sender] = 0;\n _burn(msg.sender, amountToRedeem);\n IERC20(STAKED_TOKEN).safeTransfer(to, amountToRedeem);\n }\n\n /**\n * @dev Activates the cooldown period to unstake\n * - It can't be called if the user is not staking\n **/\n function cooldown() external {\n require(balanceOf(msg.sender) != 0, \"INVALID_BALANCE_ON_COOLDOWN\");\n stakersCooldowns[msg.sender] = block.timestamp;\n }\n\n /**\n * @dev Test helper function to allow changing the cooldown\n **/\n function setCooldown(address account, uint256 _cooldown) external {\n stakersCooldowns[account] = _cooldown;\n }\n}\n" + }, + "contracts/mocks/MockStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract MockStrategy {\n address[] public assets;\n\n address public withdrawAllAsset;\n address public withdrawAllRecipient;\n\n constructor() {}\n\n function deposit(address asset, uint256 amount) external {}\n\n function depositAll() external {}\n\n function withdraw(\n address recipient,\n address asset,\n uint256 amount\n ) external {\n IERC20(asset).transfer(recipient, amount);\n }\n\n function withdrawAll() external {\n IERC20(withdrawAllAsset).transfer(\n withdrawAllRecipient,\n IERC20(withdrawAllAsset).balanceOf(address(this))\n );\n }\n\n function checkBalance(address asset)\n external\n view\n returns (uint256 balance)\n {\n balance = IERC20(asset).balanceOf(address(this));\n }\n\n function supportsAsset(address) external view returns (bool) {\n return true;\n }\n\n function collectRewardTokens() external {}\n\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return new address[](0);\n }\n\n function setWithdrawAll(address asset, address recipient) external {\n withdrawAllAsset = asset;\n withdrawAllRecipient = recipient;\n }\n}\n" + }, + "contracts/mocks/MockSwapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IMintableERC20 } from \"./MintableERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract MockSwapper {\n uint256 public nextOutAmount;\n\n function swap(\n // solhint-disable-next-line no-unused-vars\n address _fromAsset,\n address _toAsset,\n // solhint-disable-next-line no-unused-vars\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n // solhint-disable-next-line no-unused-vars\n bytes calldata _data\n ) external returns (uint256 toAssetAmount) {\n toAssetAmount = (nextOutAmount > 0) ? nextOutAmount : _minToAssetAmount;\n nextOutAmount = 0;\n IMintableERC20(_toAsset).mint(toAssetAmount);\n IERC20(_toAsset).transfer(msg.sender, toAssetAmount);\n }\n\n function setNextOutAmount(uint256 _nextOutAmount) public {\n nextOutAmount = _nextOutAmount;\n }\n}\n" + }, + "contracts/mocks/MockTUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockTUSD is MintableERC20 {\n constructor() ERC20(\"TrueUSD\", \"TUSD\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/MockUniswapPair.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IUniswapV2Pair } from \"../interfaces/uniswap/IUniswapV2Pair.sol\";\n\ncontract MockUniswapPair is IUniswapV2Pair {\n address tok0;\n address tok1;\n uint112 reserve0;\n uint112 reserve1;\n uint256 blockTimestampLast;\n\n bool public hasSynced = false;\n\n constructor(\n address _token0,\n address _token1,\n uint112 _reserve0,\n uint112 _reserve1\n ) {\n tok0 = _token0;\n tok1 = _token1;\n reserve0 = _reserve0;\n reserve1 = _reserve1;\n blockTimestampLast = block.timestamp;\n }\n\n function token0() external view override returns (address) {\n return tok0;\n }\n\n function token1() external view override returns (address) {\n return tok1;\n }\n\n function getReserves()\n external\n view\n override\n returns (\n uint112,\n uint112,\n uint32\n )\n {\n return (reserve0, reserve1, uint32(blockTimestampLast));\n }\n\n function setReserves(uint112 _reserve0, uint112 _reserve1) public {\n reserve0 = _reserve0;\n reserve1 = _reserve1;\n blockTimestampLast = block.timestamp;\n }\n\n // CAUTION This will not work if you setReserves multiple times over\n // multiple different blocks because then it wouldn't be a continuous\n // reserve factor over that blockTimestamp, this assumes an even reserve\n // ratio all the way through\n function price0CumulativeLast() external view override returns (uint256) {\n return\n uint256(FixedPoint.fraction(reserve1, reserve0)._x) *\n blockTimestampLast;\n }\n\n function price1CumulativeLast() external view override returns (uint256) {\n return\n uint256(FixedPoint.fraction(reserve0, reserve1)._x) *\n blockTimestampLast;\n }\n\n function sync() external override {\n hasSynced = true;\n }\n\n function checkHasSynced() external view {\n require(hasSynced, \"Not synced\");\n }\n}\n\n// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))\nlibrary FixedPoint {\n // range: [0, 2**112 - 1]\n // resolution: 1 / 2**112\n struct uq112x112 {\n uint224 _x;\n }\n\n // returns a uq112x112 which represents the ratio of the numerator to the denominator\n // equivalent to encode(numerator).div(denominator)\n function fraction(uint112 numerator, uint112 denominator)\n internal\n pure\n returns (uq112x112 memory)\n {\n require(denominator > 0, \"FixedPoint: DIV_BY_ZERO\");\n return uq112x112((uint224(numerator) << 112) / denominator);\n }\n}\n" + }, + "contracts/mocks/MockUniswapRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { IUniswapV2Router } from \"../interfaces/uniswap/IUniswapV2Router02.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockUniswapRouter is IUniswapV2Router {\n using StableMath for uint256;\n\n mapping(address => address) public pairMaps;\n uint256 public slippage = 1 ether;\n\n function initialize(\n address[] calldata _0tokens,\n address[] calldata _1tokens\n ) public {\n require(\n _0tokens.length == _1tokens.length,\n \"Mock token pairs should be of the same length\"\n );\n for (uint256 i = 0; i < _0tokens.length; i++) {\n pairMaps[_0tokens[i]] = _1tokens[i];\n }\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n\n function swapExactTokensForTokens(\n uint256 amountIn,\n uint256 amountOutMin,\n address[] calldata path,\n address to,\n // solhint-disable-next-line no-unused-vars\n uint256\n ) external override returns (uint256[] memory amountsOut) {\n address tok0 = path[0];\n address tok1 = path[path.length - 1];\n\n uint256 amountOut = (amountOutMin * slippage) / 1 ether;\n require(amountOut >= amountOutMin, \"Slippage error\");\n\n IERC20(tok0).transferFrom(msg.sender, address(this), amountIn);\n MintableERC20(tok1).mintTo(to, amountOut);\n\n amountsOut = new uint256[](path.length);\n amountsOut[path.length - 1] = amountOut;\n }\n\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut)\n {\n (address tok0, address tok1) = _getFirstAndLastToken(params.path);\n\n amountOut = (params.amountOutMinimum * slippage) / 1 ether;\n\n IERC20(tok0).transferFrom(msg.sender, address(this), params.amountIn);\n MintableERC20(tok1).mintTo(params.recipient, amountOut);\n\n require(\n amountOut >= params.amountOutMinimum,\n \"UniswapMock: amountOut less than amountOutMinimum\"\n );\n return amountOut;\n }\n\n function addLiquidity(\n address tokenA,\n address tokenB,\n uint256 amountADesired,\n uint256 amountBDesired,\n uint256 amountAMin,\n uint256 amountBMin,\n address to,\n uint256 deadline\n )\n external\n override\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 liquidity\n )\n {\n // this is needed to make this contract whole else it'd be just virtual\n }\n\n function WETH() external pure override returns (address) {\n return address(0);\n }\n\n // Universal router mock\n function execute(\n bytes calldata,\n bytes[] calldata inputs,\n uint256\n ) external payable {\n uint256 inLen = inputs.length;\n for (uint256 i = 0; i < inLen; ++i) {\n (\n address recipient,\n ,\n uint256 amountOutMinimum,\n bytes memory path,\n\n ) = abi.decode(inputs[i], (address, uint256, uint256, bytes, bool));\n\n (address token0, address token1) = _getFirstAndLastToken(path);\n\n amountOutMinimum = amountOutMinimum.scaleBy(\n Helpers.getDecimals(token0),\n Helpers.getDecimals(token1)\n );\n\n MintableERC20(token1).mintTo(recipient, amountOutMinimum);\n }\n }\n\n function _getFirstAndLastToken(bytes memory path)\n internal\n view\n returns (address token0, address token1)\n {\n bytes memory tok0Bytes = new bytes(20);\n for (uint256 j = 0; j < 20; ++j) {\n tok0Bytes[j] = path[j];\n }\n token0 = address(bytes20(tok0Bytes));\n\n if (pairMaps[token0] != address(0)) {\n token0 = pairMaps[token0];\n }\n\n bytes memory tok1Bytes = new bytes(20);\n uint256 tok1Offset = path.length - 20;\n for (uint256 j = 0; j < 20; ++j) {\n tok1Bytes[j] = path[j + tok1Offset];\n }\n token1 = address(bytes20(tok1Bytes));\n\n if (pairMaps[token1] != address(0)) {\n token1 = pairMaps[token1];\n }\n }\n}\n" + }, + "contracts/mocks/MockUSDC.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDC is MintableERC20 {\n constructor() ERC20(\"USDC Coin\", \"USDC\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n}\n" + }, + "contracts/mocks/MockUSDS.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDS is MintableERC20 {\n constructor() ERC20(\"USDS\", \"USDS\") {}\n}\n" + }, + "contracts/mocks/MockUSDT.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDT is MintableERC20 {\n constructor() ERC20(\"USDT Coin\", \"USDT\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n}\n" + }, + "contracts/mocks/MockVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultCore } from \"../vault/VaultCore.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { VaultInitializer } from \"../vault/VaultInitializer.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MockVault is VaultCore {\n using StableMath for uint256;\n\n uint256 storedTotalValue;\n\n function setTotalValue(uint256 _value) public {\n storedTotalValue = _value;\n }\n\n function totalValue() external view override returns (uint256) {\n return storedTotalValue;\n }\n\n function _totalValue() internal view override returns (uint256) {\n return storedTotalValue;\n }\n\n function _checkBalance(address _asset)\n internal\n view\n override\n returns (uint256 balance)\n {\n // Avoids rounding errors by returning the total value\n // in a single currency\n if (allAssets[0] == _asset) {\n uint256 decimals = Helpers.getDecimals(_asset);\n return storedTotalValue.scaleBy(decimals, 18);\n } else {\n return 0;\n }\n }\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external onlyGovernor {\n maxSupplyDiff = _maxSupplyDiff;\n }\n}\n" + }, + "contracts/mocks/MockVaultCoreInstantRebase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultCore } from \"../vault/VaultCore.sol\";\n\ncontract MockVaultCoreInstantRebase is VaultCore {\n function _nextYield(uint256 supply, uint256 vaultValue)\n internal\n view\n override\n returns (uint256 yield, uint256 targetRate)\n {\n if (vaultValue <= supply) {\n return (0, 0);\n }\n yield = vaultValue - supply;\n return (yield, 0);\n }\n}\n" + }, + "contracts/mocks/MockWETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\n// Just importing to \"unbreak\" coverage tests\nimport { IERC721Receiver } from \"@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol\";\n\ncontract MockWETH is MintableERC20 {\n constructor() ERC20(\"WETH\", \"WETH\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/MockWS.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockWS is MintableERC20 {\n constructor() ERC20(\"Wrapped Sonic\", \"wS\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/TestUpgradedOUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../token/OUSD.sol\";\n\n// used to alter internal state of OUSD contract\ncontract TestUpgradedOUSD is OUSD {\n constructor() OUSD() {}\n\n function overwriteCreditBalances(address _account, uint256 _creditBalance)\n public\n {\n creditBalances[_account] = _creditBalance;\n }\n\n function overwriteAlternativeCPT(address _account, uint256 _acpt) public {\n alternativeCreditsPerToken[_account] = _acpt;\n }\n\n function overwriteRebaseState(address _account, RebaseOptions _rebaseOption)\n public\n {\n rebaseState[_account] = _rebaseOption;\n }\n}\n" + }, + "contracts/oracle/AbstractOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\n// @notice Abstract functionality that is shared between various Oracle Routers\nabstract contract AbstractOracleRouter is IOracle {\n using StableMath for uint256;\n using SafeCast for int256;\n\n uint256 internal constant MIN_DRIFT = 0.7e18;\n uint256 internal constant MAX_DRIFT = 1.3e18;\n address internal constant FIXED_PRICE =\n 0x0000000000000000000000000000000000000001;\n // Maximum allowed staleness buffer above normal Oracle maximum staleness\n uint256 internal constant STALENESS_BUFFER = 1 days;\n mapping(address => uint8) internal decimalsCache;\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n returns (address feedAddress, uint256 maxStaleness);\n\n /**\n * @notice Returns the total price in 18 digit unit for a given asset.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n if (shouldBePegged(asset)) {\n require(_price <= MAX_DRIFT, \"Oracle: Price exceeds max\");\n require(_price >= MIN_DRIFT, \"Oracle: Price under min\");\n }\n return _price;\n }\n\n function getDecimals(address _feed) internal view virtual returns (uint8) {\n uint8 decimals = decimalsCache[_feed];\n require(decimals > 0, \"Oracle: Decimals not cached\");\n return decimals;\n }\n\n /**\n * @notice Before an asset/feed price is fetches for the first time the\n * decimals need to be cached. This is a gas optimization\n * @param asset address of the asset\n * @return uint8 corresponding asset decimals\n */\n function cacheDecimals(address asset) external returns (uint8) {\n (address _feed, ) = feedMetadata(asset);\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n uint8 decimals = AggregatorV3Interface(_feed).decimals();\n decimalsCache[_feed] = decimals;\n return decimals;\n }\n\n function shouldBePegged(address _asset) internal view returns (bool) {\n string memory symbol = Helpers.getSymbol(_asset);\n bytes32 symbolHash = keccak256(abi.encodePacked(symbol));\n return\n symbolHash == keccak256(abi.encodePacked(\"DAI\")) ||\n symbolHash == keccak256(abi.encodePacked(\"USDC\")) ||\n symbolHash == keccak256(abi.encodePacked(\"USDT\"));\n }\n}\n" + }, + "contracts/oracle/OETHBaseOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\n// @notice Oracle Router (for OETH on Base) that denominates all prices in ETH\ncontract OETHBaseOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n using SafeCast for int256;\n\n address constant WETH = 0x4200000000000000000000000000000000000006;\n address constant WOETH = 0xD8724322f44E5c58D7A815F542036fb17DbbF839;\n address constant WOETH_CHAINLINK_FEED =\n 0xe96EB1EDa83d18cbac224233319FA5071464e1b9;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == WETH) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == WOETH) {\n // Chainlink: https://data.chain.link/feeds/base/base/woeth-oeth-exchange-rate\n // Bridged wOETH/OETH\n feedAddress = WOETH_CHAINLINK_FEED;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OETHFixedOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHOracleRouter } from \"./OETHOracleRouter.sol\";\n\n// @notice Oracle Router that returns 1e18 for all prices\n// used solely for deployment to testnets\ncontract OETHFixedOracle is OETHOracleRouter {\n constructor() OETHOracleRouter() {}\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n // solhint-disable-next-line no-unused-vars\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n // fixes price for all of the assets\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n }\n}\n" + }, + "contracts/oracle/OETHOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// @notice Oracle Router that denominates all prices in ETH\ncontract OETHOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = uint256(_iprice).scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == 0x5E8422345238F34275888049021821E8E08CAa1f) {\n // frxETH/ETH\n feedAddress = 0xC58F3385FBc1C8AD2c0C9a061D7c13b141D7A5Df;\n maxStaleness = 18 hours + STALENESS_BUFFER;\n } else if (asset == 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/steth-eth\n // Chainlink: stETH/ETH\n feedAddress = 0x86392dC19c0b719886221c78AB11eb8Cf5c52812;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xae78736Cd615f374D3085123A210448E74Fc6393) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/reth-eth\n // Chainlink: rETH/ETH\n feedAddress = 0x536218f9E9Eb48863970252233c8F271f554C2d0;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xD533a949740bb3306d119CC777fa900bA034cd52) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/crv-eth\n // Chainlink: CRV/ETH\n feedAddress = 0x8a12Be339B0cD1829b91Adc01977caa5E9ac121e;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/cvx-eth\n // Chainlink: CVX/ETH\n feedAddress = 0xC9CbF687f43176B302F03f5e58470b77D07c61c6;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xBe9895146f7AF43049ca1c1AE358B0541Ea49704) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/cbeth-eth\n // Chainlink: cbETH/ETH\n feedAddress = 0xF017fcB346A1885194689bA23Eff2fE6fA5C483b;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xba100000625a3754423978a60c9317c58a424e3D) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/bal-eth\n // Chainlink: BAL/ETH\n feedAddress = 0xC1438AA3823A6Ba0C159CfA8D98dF5A994bA120b;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OETHPlumeOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\n// @notice Oracle Router (for OETH on Plume) that denominates all prices in ETH\ncontract OETHPlumeOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n using SafeCast for int256;\n\n address constant WETH = 0xca59cA09E5602fAe8B629DeE83FfA819741f14be;\n address constant WOETH = 0xD8724322f44E5c58D7A815F542036fb17DbbF839;\n // Ref: https://docs.eo.app/docs/eprice/feed-addresses/plume\n address constant WOETH_ORACLE_FEED =\n 0x4915600Ed7d85De62011433eEf0BD5399f677e9b;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == WETH) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == WOETH) {\n // Chainlink: https://data.chain.link/feeds/base/base/woeth-oeth-exchange-rate\n // Bridged wOETH/OETH\n feedAddress = WOETH_ORACLE_FEED;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\n\n// @notice Oracle Router that denominates all prices in USD\ncontract OracleRouter is AbstractOracleRouter {\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n pure\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n /* + STALENESS_BUFFER is added in case Oracle for some reason doesn't\n * update on heartbeat and we add a generous buffer amount.\n */\n if (asset == 0x6B175474E89094C44Da98b954EedeAC495271d0F) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/dai-usd\n // Chainlink: DAI/USD\n feedAddress = 0xAed0c38402a5d19df6E4c03F4E2DceD6e29c1ee9;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xdC035D45d973E3EC169d2276DDab16f1e407384F) {\n // https://data.chain.link/feeds/ethereum/mainnet/usds-usd\n // Chainlink: USDS/USD\n feedAddress = 0xfF30586cD0F29eD462364C7e81375FC0C71219b1;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/usdc-usd\n // Chainlink: USDC/USD\n feedAddress = 0x8fFfFfd4AfB6115b954Bd326cbe7B4BA576818f6;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xdAC17F958D2ee523a2206206994597C13D831ec7) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/usdt-usd\n // Chainlink: USDT/USD\n feedAddress = 0x3E7d1eAB13ad0104d2750B8863b489D65364e32D;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xc00e94Cb662C3520282E6f5717214004A7f26888) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/comp-usd\n // Chainlink: COMP/USD\n feedAddress = 0xdbd020CAeF83eFd542f4De03e3cF0C28A4428bd5;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/aave-usd\n // Chainlink: AAVE/USD\n feedAddress = 0x547a514d5e3769680Ce22B2361c10Ea13619e8a9;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xD533a949740bb3306d119CC777fa900bA034cd52) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/crv-usd\n // Chainlink: CRV/USD\n feedAddress = 0xCd627aA160A6fA45Eb793D19Ef54f5062F20f33f;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B) {\n // Chainlink: CVX/USD\n feedAddress = 0xd962fC30A72A84cE50161031391756Bf2876Af5D;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OSonicOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHFixedOracle } from \"./OETHFixedOracle.sol\";\n\n// @notice Oracle Router that returns 1e18 for all prices\n// used solely for deployment to testnets\ncontract OSonicOracleRouter is OETHFixedOracle {\n constructor() OETHFixedOracle() {}\n}\n" + }, + "contracts/poolBooster/AbstractPoolBoosterFactory.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IPoolBoostCentralRegistry } from \"../interfaces/poolBooster/IPoolBoostCentralRegistry.sol\";\n\n/**\n * @title Abstract Pool booster factory\n * @author Origin Protocol Inc\n */\ncontract AbstractPoolBoosterFactory is Governable {\n struct PoolBoosterEntry {\n address boosterAddress;\n address ammPoolAddress;\n IPoolBoostCentralRegistry.PoolBoosterType boosterType;\n }\n\n // @notice address of Origin Sonic\n address public immutable oSonic;\n // @notice Central registry contract\n IPoolBoostCentralRegistry public immutable centralRegistry;\n\n // @notice list of all the pool boosters created by this factory\n PoolBoosterEntry[] public poolBoosters;\n // @notice mapping of AMM pool to pool booster\n mapping(address => PoolBoosterEntry) public poolBoosterFromPool;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) {\n require(_oSonic != address(0), \"Invalid oSonic address\");\n require(_governor != address(0), \"Invalid governor address\");\n require(\n _centralRegistry != address(0),\n \"Invalid central registry address\"\n );\n\n oSonic = _oSonic;\n centralRegistry = IPoolBoostCentralRegistry(_centralRegistry);\n _setGovernor(_governor);\n }\n\n /**\n * @notice Goes over all the pool boosters created by this factory and\n * calls bribe() on them.\n * @param _exclusionList A list of pool booster addresses to skip when\n * calling this function.\n */\n function bribeAll(address[] memory _exclusionList) external {\n uint256 lengthI = poolBoosters.length;\n for (uint256 i = 0; i < lengthI; i++) {\n address poolBoosterAddress = poolBoosters[i].boosterAddress;\n bool skipBribeCall = false;\n uint256 lengthJ = _exclusionList.length;\n for (uint256 j = 0; j < lengthJ; j++) {\n // pool booster in exclusion list\n if (_exclusionList[j] == poolBoosterAddress) {\n skipBribeCall = true;\n break;\n }\n }\n\n if (!skipBribeCall) {\n IPoolBooster(poolBoosterAddress).bribe();\n }\n }\n }\n\n /**\n * @notice Removes the pool booster from the internal list of pool boosters.\n * @dev This action does not destroy the pool booster contract nor does it\n * stop the yield delegation to it.\n * @param _poolBoosterAddress address of the pool booster\n */\n function removePoolBooster(address _poolBoosterAddress)\n external\n onlyGovernor\n {\n uint256 boostersLen = poolBoosters.length;\n for (uint256 i = 0; i < boostersLen; ++i) {\n if (poolBoosters[i].boosterAddress == _poolBoosterAddress) {\n // erase mapping\n delete poolBoosterFromPool[poolBoosters[i].ammPoolAddress];\n\n // overwrite current pool booster with the last entry in the list\n poolBoosters[i] = poolBoosters[boostersLen - 1];\n // drop the last entry\n poolBoosters.pop();\n\n centralRegistry.emitPoolBoosterRemoved(_poolBoosterAddress);\n break;\n }\n }\n }\n\n function _storePoolBoosterEntry(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType _boosterType\n ) internal {\n PoolBoosterEntry memory entry = PoolBoosterEntry(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType\n );\n\n poolBoosters.push(entry);\n poolBoosterFromPool[_ammPoolAddress] = entry;\n\n // emit the events of the pool booster created\n centralRegistry.emitPoolBoosterCreated(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType\n );\n }\n\n function _deployContract(bytes memory _bytecode, uint256 _salt)\n internal\n returns (address _address)\n {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _address := create2(\n 0,\n add(_bytecode, 0x20),\n mload(_bytecode),\n _salt\n )\n }\n\n require(\n _address.code.length > 0 && _address != address(0),\n \"Failed creating a pool booster\"\n );\n }\n\n // pre-compute the address of the deployed contract that will be\n // created when create2 is called\n function _computeAddress(bytes memory _bytecode, uint256 _salt)\n internal\n view\n returns (address)\n {\n bytes32 hash = keccak256(\n abi.encodePacked(\n bytes1(0xff),\n address(this),\n _salt,\n keccak256(_bytecode)\n )\n );\n\n // cast last 20 bytes of hash to address\n return address(uint160(uint256(hash)));\n }\n\n function poolBoosterLength() external view returns (uint256) {\n return poolBoosters.length;\n }\n}\n" + }, + "contracts/poolBooster/PoolBoostCentralRegistry.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IPoolBoostCentralRegistry } from \"../interfaces/poolBooster/IPoolBoostCentralRegistry.sol\";\n\n/**\n * @title Contract that holds all governance approved pool booster Factory\n * implementation deployments\n * @author Origin Protocol Inc\n */\ncontract PoolBoostCentralRegistry is Governable, IPoolBoostCentralRegistry {\n event FactoryApproved(address factoryAddress);\n event FactoryRemoved(address factoryAddress);\n\n // @notice List of approved factories\n address[] public factories;\n\n modifier onlyApprovedFactories() {\n require(isApprovedFactory(msg.sender), \"Not an approved factory\");\n _;\n }\n\n constructor() {\n // set the governor of the implementation contract to zero address\n _setGovernor(address(0));\n }\n\n /**\n * @notice Adds a factory address to the approved factory addresses\n * @param _factoryAddress address of the factory\n */\n function approveFactory(address _factoryAddress) external onlyGovernor {\n require(_factoryAddress != address(0), \"Invalid address\");\n require(\n !isApprovedFactory(_factoryAddress),\n \"Factory already approved\"\n );\n\n factories.push(_factoryAddress);\n emit FactoryApproved(_factoryAddress);\n }\n\n /**\n * @notice Removes the factory from approved factory addresses\n * @param _factoryAddress address of the factory\n */\n function removeFactory(address _factoryAddress) external onlyGovernor {\n require(_factoryAddress != address(0), \"Invalid address\");\n\n uint256 length = factories.length;\n bool factoryRemoved = false;\n for (uint256 i = 0; i < length; i++) {\n if (factories[i] != _factoryAddress) {\n continue;\n }\n\n factories[i] = factories[length - 1];\n factories.pop();\n emit FactoryRemoved(_factoryAddress);\n factoryRemoved = true;\n break;\n }\n require(factoryRemoved, \"Not an approved factory\");\n\n emit FactoryRemoved(_factoryAddress);\n }\n\n /**\n * @notice Emits a pool booster created event\n * @dev This has been created as a convenience method for the monitoring to have\n * an index of all of the created pool boosters by only listening to the\n * events of this contract.\n * @param _poolBoosterAddress address of the pool booster created\n * @param _ammPoolAddress address of the AMM pool forwarding yield to the pool booster\n * @param _boosterType PoolBoosterType the type of the pool booster\n */\n function emitPoolBoosterCreated(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n PoolBoosterType _boosterType\n ) external onlyApprovedFactories {\n emit PoolBoosterCreated(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType,\n msg.sender // address of the factory\n );\n }\n\n /**\n * @notice Emits a pool booster removed event\n * @dev This has been created as a convenience method for the monitoring to have\n * an index of all of the removed pool boosters by only listening to the\n * events of this contract.\n * @param _poolBoosterAddress address of the pool booster to be removed\n */\n function emitPoolBoosterRemoved(address _poolBoosterAddress)\n external\n onlyApprovedFactories\n {\n emit PoolBoosterRemoved(_poolBoosterAddress);\n }\n\n /**\n * @notice Returns true if the factory is approved\n * @param _factoryAddress address of the factory\n */\n function isApprovedFactory(address _factoryAddress)\n public\n view\n returns (bool)\n {\n uint256 length = factories.length;\n for (uint256 i = 0; i < length; i++) {\n if (factories[i] == _factoryAddress) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * @notice Returns all supported factories\n */\n function getAllFactories() external view returns (address[] memory) {\n return factories;\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactoryMerkl.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterMerkl } from \"./PoolBoosterMerkl.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Merkl pool boosters.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactoryMerkl is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n /// @notice address of the Merkl distributor\n address public merklDistributor;\n\n /// @notice event emitted when the Merkl distributor is updated\n event MerklDistributorUpdated(address newDistributor);\n\n /**\n * @param _oSonic address of the OSonic token\n * @param _governor address governor\n * @param _centralRegistry address of the central registry\n * @param _merklDistributor address of the Merkl distributor\n */\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry,\n address _merklDistributor\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {\n _setMerklDistributor(_merklDistributor);\n }\n\n /**\n * @dev Create a Pool Booster for Merkl.\n * @param _campaignType The type of campaign to create. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _campaignDuration The duration of the campaign in seconds\n * @param campaignData The data to be used for the campaign. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * This should be fetched from the Merkl UI.\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterMerkl(\n uint32 _campaignType,\n address _ammPoolAddress,\n uint32 _campaignDuration,\n bytes calldata campaignData,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n require(_campaignDuration > 1 hours, \"Invalid campaign duration\");\n require(campaignData.length > 0, \"Invalid campaign data\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterMerkl).creationCode,\n abi.encode(\n oSonic,\n merklDistributor,\n _campaignDuration,\n _campaignType,\n governor(),\n campaignData\n )\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.MerklBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for Merkl.\n * @param _campaignType The type of campaign to create. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterMerkl` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n uint32 _campaignType,\n address _ammPoolAddress,\n uint32 _campaignDuration,\n bytes calldata campaignData,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n require(_campaignDuration > 1 hours, \"Invalid campaign duration\");\n require(campaignData.length > 0, \"Invalid campaign data\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterMerkl).creationCode,\n abi.encode(\n oSonic,\n merklDistributor,\n _campaignDuration,\n _campaignType,\n governor(),\n campaignData\n )\n ),\n _salt\n );\n }\n\n /**\n * @dev Set the address of the Merkl distributor\n * @param _merklDistributor The address of the Merkl distributor\n */\n function setMerklDistributor(address _merklDistributor)\n external\n onlyGovernor\n {\n _setMerklDistributor(_merklDistributor);\n }\n\n function _setMerklDistributor(address _merklDistributor) internal {\n require(\n _merklDistributor != address(0),\n \"Invalid merklDistributor address\"\n );\n merklDistributor = _merklDistributor;\n emit MerklDistributorUpdated(_merklDistributor);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactoryMetropolis.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterMetropolis } from \"./PoolBoosterMetropolis.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Metropolis pool boosters.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactoryMetropolis is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n address public immutable rewardFactory;\n address public immutable voter;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n // @param address _rewardFactory address of the Metropolis reward factory\n // @param address _voter address of the Metropolis voter\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry,\n address _rewardFactory,\n address _voter\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {\n rewardFactory = _rewardFactory;\n voter = _voter;\n }\n\n /**\n * @dev Create a Pool Booster for Metropolis pool.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterMetropolis(address _ammPoolAddress, uint256 _salt)\n external\n onlyGovernor\n {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterMetropolis).creationCode,\n abi.encode(oSonic, rewardFactory, _ammPoolAddress, voter)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.MetropolisBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for Metropolis pool.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterMetropolis` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(address _ammPoolAddress, uint256 _salt)\n external\n view\n returns (address)\n {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterMetropolis).creationCode,\n abi.encode(oSonic, rewardFactory, _ammPoolAddress, voter)\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactorySwapxDouble.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterSwapxDouble } from \"./PoolBoosterSwapxDouble.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Swapx Ichi pool boosters where both of the\n * gauges need incentivizing.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactorySwapxDouble is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {}\n\n /**\n * @dev Create a Pool Booster for SwapX Ichi vault based pool where 2 Bribe contracts need to be\n * bribed\n * @param _bribeAddressOS address of the Bribes.sol(Bribe) contract for the OS token side\n * @param _bribeAddressOther address of the Bribes.sol(Bribe) contract for the other token in the pool\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _split 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n * bribe contract and 60% to other bribe contract\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterSwapxDouble(\n address _bribeAddressOS,\n address _bribeAddressOther,\n address _ammPoolAddress,\n uint256 _split,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterSwapxDouble).creationCode,\n abi.encode(_bribeAddressOS, _bribeAddressOther, oSonic, _split)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.SwapXDoubleBooster\n );\n }\n\n /**\n * @dev Compute the address of the pool booster to be deployed.\n * @param _bribeAddressOS address of the Bribes.sol(Bribe) contract for the OS token side\n * @param _bribeAddressOther address of the Bribes.sol(Bribe) contract for the other token in the pool\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _split 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n * bribe contract and 60% to other bribe contract\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterSwapxDouble` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n address _bribeAddressOS,\n address _bribeAddressOther,\n address _ammPoolAddress,\n uint256 _split,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterSwapxDouble).creationCode,\n abi.encode(\n _bribeAddressOS,\n _bribeAddressOther,\n oSonic,\n _split\n )\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactorySwapxSingle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterSwapxSingle } from \"./PoolBoosterSwapxSingle.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Swapx Single pool boosters - where a single\n * gauge is created for a pool. this is appropriate for Classic Stable & Classic\n * Volatile SwapX pools.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactorySwapxSingle is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {}\n\n /**\n * @dev Create a Pool Booster for SwapX classic volatile or classic stable pools where\n * a single Bribe contract is incentivized.\n * @param _bribeAddress address of the Bribes.sol contract\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterSwapxSingle(\n address _bribeAddress,\n address _ammPoolAddress,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterSwapxSingle).creationCode,\n abi.encode(_bribeAddress, oSonic)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.SwapXSingleBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for SwapX classic volatile or classic stable pools where\n * a single Bribe contract is incentivized.\n * @param _bribeAddress address of the Bribes.sol contract\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterSwapxSingle` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n address _bribeAddress,\n address _ammPoolAddress,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterSwapxSingle).creationCode,\n abi.encode(_bribeAddress, oSonic)\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterMerkl.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IMerklDistributor } from \"../interfaces/poolBooster/IMerklDistributor.sol\";\n\ninterface IERC1271 {\n /**\n * @dev Should return whether the signature provided is valid for the provided data\n * @param hash Hash of the data to be signed\n * @param signature Signature byte array associated with _data\n */\n function isValidSignature(bytes32 hash, bytes memory signature)\n external\n view\n returns (bytes4 magicValue);\n}\n\n/**\n * @title Pool booster for Merkl distributor\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterMerkl is IPoolBooster, IERC1271 {\n /// @notice address of merkl distributor\n IMerklDistributor public immutable merklDistributor;\n /// @notice address of the OS token\n IERC20 public immutable rewardToken;\n /// @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n /// @notice Campaign duration in seconds\n uint32 public immutable duration; // -> should be immutable\n /// @notice Campaign type\n uint32 public immutable campaignType;\n /// @notice Owner of the campaign\n address public immutable creator;\n /// @notice Campaign data\n bytes public campaignData;\n\n constructor(\n address _rewardToken,\n address _merklDistributor,\n uint32 _duration,\n uint32 _campaignType,\n address _creator,\n bytes memory _campaignData\n ) {\n require(_rewardToken != address(0), \"Invalid rewardToken address\");\n require(\n _merklDistributor != address(0),\n \"Invalid merklDistributor address\"\n );\n require(_campaignData.length > 0, \"Invalid campaignData\");\n require(_duration > 1 hours, \"Invalid duration\");\n\n campaignType = _campaignType;\n duration = _duration;\n creator = _creator;\n\n merklDistributor = IMerklDistributor(_merklDistributor);\n rewardToken = IERC20(_rewardToken);\n campaignData = _campaignData;\n }\n\n /// @notice Create a campaign on the Merkl distributor\n function bribe() external override {\n // Ensure token is approved for the Merkl distributor\n uint256 minAmount = merklDistributor.rewardTokenMinAmounts(\n address(rewardToken)\n );\n require(minAmount > 0, \"Min reward amount must be > 0\");\n\n // if balance too small or below threshold, do no bribes\n uint256 balance = rewardToken.balanceOf(address(this));\n if (\n balance < MIN_BRIBE_AMOUNT ||\n (balance * 1 hours < minAmount * duration)\n ) {\n return;\n }\n\n // Approve the bribe contract to spend the reward token\n rewardToken.approve(address(merklDistributor), balance);\n\n // Notify the bribe contract of the reward amount\n merklDistributor.signAndCreateCampaign(\n IMerklDistributor.CampaignParameters({\n campaignId: bytes32(0),\n creator: creator,\n rewardToken: address(rewardToken),\n amount: balance,\n campaignType: campaignType,\n startTimestamp: getNextPeriodStartTime(),\n duration: duration,\n campaignData: campaignData\n }),\n bytes(\"\")\n );\n emit BribeExecuted(balance);\n }\n\n /// @notice Used to sign a campaign on the Merkl distributor\n function isValidSignature(bytes32, bytes memory)\n external\n view\n override\n returns (bytes4 magicValue)\n {\n require(msg.sender == address(merklDistributor), \"Invalid sender\");\n // bytes4(keccak256(\"isValidSignature(bytes32,bytes)\")) == 0x1626ba7e\n return bytes4(0x1626ba7e);\n }\n\n /// @notice Returns the timestamp for the start of the next period based on the configured duration\n function getNextPeriodStartTime() public view returns (uint32) {\n // Calculate the timestamp for the next period boundary\n return uint32((block.timestamp / duration + 1) * duration);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterMetropolis.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @title Pool booster for Metropolis pools\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterMetropolis is IPoolBooster {\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice address of the pool\n address public immutable pool;\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n IRewarderFactory public immutable rewardFactory;\n\n IVoter public immutable voter;\n\n constructor(\n address _osToken,\n address _rewardFactory,\n address _pool,\n address _voter\n ) {\n require(_pool != address(0), \"Invalid pool address\");\n pool = _pool;\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n\n rewardFactory = IRewarderFactory(_rewardFactory);\n\n voter = IVoter(_voter);\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n (, uint256 minBribeAmount) = rewardFactory.getWhitelistedTokenInfo(\n address(osToken)\n );\n if (balance < MIN_BRIBE_AMOUNT || balance < minBribeAmount) {\n return;\n }\n\n uint256 id = voter.getCurrentVotingPeriod() + 1;\n\n // Deploy a rewarder\n IRewarder rewarder = IRewarder(\n rewardFactory.createBribeRewarder(address(osToken), pool)\n );\n\n // Approve the rewarder to spend the balance\n osToken.approve(address(rewarder), balance);\n\n // Fund and bribe the rewarder\n rewarder.fundAndBribe(id, id, balance);\n\n emit BribeExecuted(balance);\n }\n}\n\ninterface IRewarderFactory {\n function createBribeRewarder(address token, address pool)\n external\n returns (address rewarder);\n\n function getWhitelistedTokenInfo(address token)\n external\n view\n returns (bool isWhitelisted, uint256 minBribeAmount);\n}\n\ninterface IRewarder {\n function fundAndBribe(\n uint256 startId,\n uint256 lastId,\n uint256 amountPerPeriod\n ) external payable;\n}\n\ninterface IVoter {\n function getCurrentVotingPeriod() external view returns (uint256);\n}\n" + }, + "contracts/poolBooster/PoolBoosterSwapxDouble.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBribe } from \"../interfaces/poolBooster/ISwapXAlgebraBribe.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n/**\n * @title Pool booster for SwapX concentrated liquidity where 2 gauges are created for\n * every pool. Ichi vaults currently have such setup.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterSwapxDouble is IPoolBooster {\n using StableMath for uint256;\n\n // @notice address of the Bribes.sol(Bribe) contract for the OS token side\n IBribe public immutable bribeContractOS;\n // @notice address of the Bribes.sol(Bribe) contract for the other token in the pool\n IBribe public immutable bribeContractOther;\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n // bribe contract and 60% to other bribe contract\n uint256 public immutable split;\n\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n constructor(\n address _bribeContractOS,\n address _bribeContractOther,\n address _osToken,\n uint256 _split\n ) {\n require(\n _bribeContractOS != address(0),\n \"Invalid bribeContractOS address\"\n );\n require(\n _bribeContractOther != address(0),\n \"Invalid bribeContractOther address\"\n );\n // expect it to be between 1% & 99%\n require(_split > 1e16 && _split < 99e16, \"Unexpected split amount\");\n\n bribeContractOS = IBribe(_bribeContractOS);\n bribeContractOther = IBribe(_bribeContractOther);\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n split = _split;\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n if (balance < MIN_BRIBE_AMOUNT) {\n return;\n }\n\n uint256 osBribeAmount = balance.mulTruncate(split);\n uint256 otherBribeAmount = balance - osBribeAmount;\n\n osToken.approve(address(bribeContractOS), osBribeAmount);\n osToken.approve(address(bribeContractOther), otherBribeAmount);\n\n bribeContractOS.notifyRewardAmount(address(osToken), osBribeAmount);\n bribeContractOther.notifyRewardAmount(\n address(osToken),\n otherBribeAmount\n );\n\n emit BribeExecuted(balance);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterSwapxSingle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBribe } from \"../interfaces/poolBooster/ISwapXAlgebraBribe.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @title Pool booster for SwapX for Classic Stable Pools and Classic Volatile Pools\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterSwapxSingle is IPoolBooster {\n // @notice address of the Bribes.sol(Bribe) contract\n IBribe public immutable bribeContract;\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n constructor(address _bribeContract, address _osToken) {\n require(_bribeContract != address(0), \"Invalid bribeContract address\");\n bribeContract = IBribe(_bribeContract);\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n if (balance < MIN_BRIBE_AMOUNT) {\n return;\n }\n\n osToken.approve(address(bribeContract), balance);\n\n bribeContract.notifyRewardAmount(address(osToken), balance);\n emit BribeExecuted(balance);\n }\n}\n" + }, + "contracts/proxies/BaseProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice BridgedBaseWOETHProxy delegates calls to BridgedWOETH implementation\n */\ncontract BridgedBaseWOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseVaultProxy delegates calls to OETHBaseVault implementation\n */\ncontract OETHBaseVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseProxy delegates calls to OETH implementation\n */\ncontract OETHBaseProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHBaseProxy delegates calls to WOETH implementation\n */\ncontract WOETHBaseProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseDripperProxy delegates calls to a FixedRateDripper implementation\n */\ncontract OETHBaseDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice AerodromeAMOStrategyProxy delegates calls to AerodromeAMOStrategy implementation\n */\ncontract AerodromeAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BridgedWOETHStrategyProxy delegates calls to BridgedWOETHStrategy implementation\n */\ncontract BridgedWOETHStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseHarvesterProxy delegates calls to a SuperOETHHarvester implementation\n */\ncontract OETHBaseHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseCurveAMOProxy delegates calls to a OETHBaseCurveAMO implementation\n */\ncontract OETHBaseCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/proxies/InitializeGovernedUpgradeabilityProxy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { Governable } from \"../governance/Governable.sol\";\n\n/**\n * @title BaseGovernedUpgradeabilityProxy\n * @dev This contract combines an upgradeability proxy with our governor system.\n * It is based on an older version of OpenZeppelins BaseUpgradeabilityProxy\n * with Solidity ^0.8.0.\n * @author Origin Protocol Inc\n */\ncontract InitializeGovernedUpgradeabilityProxy is Governable {\n /**\n * @dev Emitted when the implementation is upgraded.\n * @param implementation Address of the new implementation.\n */\n event Upgraded(address indexed implementation);\n\n constructor() {\n _setGovernor(msg.sender);\n }\n\n /**\n * @dev Contract initializer with Governor enforcement\n * @param _logic Address of the initial implementation.\n * @param _initGovernor Address of the initial Governor.\n * @param _data Data to send as msg.data to the implementation to initialize\n * the proxied contract.\n * It should include the signature and the parameters of the function to be\n * called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n * This parameter is optional, if no data is given the initialization call\n * to proxied contract will be skipped.\n */\n function initialize(\n address _logic,\n address _initGovernor,\n bytes calldata _data\n ) public payable onlyGovernor {\n require(_implementation() == address(0));\n require(_logic != address(0), \"Implementation not set\");\n assert(\n IMPLEMENTATION_SLOT ==\n bytes32(uint256(keccak256(\"eip1967.proxy.implementation\")) - 1)\n );\n _setImplementation(_logic);\n if (_data.length > 0) {\n (bool success, ) = _logic.delegatecall(_data);\n require(success);\n }\n _changeGovernor(_initGovernor);\n }\n\n /**\n * @return The address of the proxy admin/it's also the governor.\n */\n function admin() external view returns (address) {\n return _governor();\n }\n\n /**\n * @return The address of the implementation.\n */\n function implementation() external view returns (address) {\n return _implementation();\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy.\n * Only the admin can call this function.\n * @param _newImplementation Address of the new implementation.\n */\n function upgradeTo(address _newImplementation) external onlyGovernor {\n _upgradeTo(_newImplementation);\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy and call a function\n * on the new implementation.\n * This is useful to initialize the proxied contract.\n * @param newImplementation Address of the new implementation.\n * @param data Data to send as msg.data in the low level call.\n * It should include the signature and the parameters of the function to be called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n */\n function upgradeToAndCall(address newImplementation, bytes calldata data)\n external\n payable\n onlyGovernor\n {\n _upgradeTo(newImplementation);\n (bool success, ) = newImplementation.delegatecall(data);\n require(success);\n }\n\n /**\n * @dev Fallback function.\n * Implemented entirely in `_fallback`.\n */\n fallback() external payable {\n _fallback();\n }\n\n /**\n * @dev Delegates execution to an implementation contract.\n * This is a low level function that doesn't return to its internal call site.\n * It will return to the external caller whatever the implementation returns.\n * @param _impl Address to delegate.\n */\n function _delegate(address _impl) internal {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(gas(), _impl, 0, calldatasize(), 0, 0)\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n /**\n * @dev Function that is run as the first thing in the fallback function.\n * Can be redefined in derived contracts to add functionality.\n * Redefinitions must call super._willFallback().\n */\n function _willFallback() internal {}\n\n /**\n * @dev fallback implementation.\n * Extracted to enable manual triggering.\n */\n function _fallback() internal {\n _willFallback();\n _delegate(_implementation());\n }\n\n /**\n * @dev Storage slot with the address of the current implementation.\n * This is the keccak-256 hash of \"eip1967.proxy.implementation\" subtracted by 1, and is\n * validated in the constructor.\n */\n bytes32 internal constant IMPLEMENTATION_SLOT =\n 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n /**\n * @dev Returns the current implementation.\n * @return impl Address of the current implementation\n */\n function _implementation() internal view returns (address impl) {\n bytes32 slot = IMPLEMENTATION_SLOT;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n impl := sload(slot)\n }\n }\n\n /**\n * @dev Upgrades the proxy to a new implementation.\n * @param newImplementation Address of the new implementation.\n */\n function _upgradeTo(address newImplementation) internal {\n _setImplementation(newImplementation);\n emit Upgraded(newImplementation);\n }\n\n /**\n * @dev Sets the implementation address of the proxy.\n * @param newImplementation Address of the new implementation.\n */\n function _setImplementation(address newImplementation) internal {\n require(\n Address.isContract(newImplementation),\n \"Cannot set a proxy implementation to a non-contract address\"\n );\n\n bytes32 slot = IMPLEMENTATION_SLOT;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(slot, newImplementation)\n }\n }\n}\n" + }, + "contracts/proxies/PlumeProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OETHPlumeVaultProxy delegates calls to OETHPlumeVault implementation\n */\ncontract OETHPlumeVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHPlumeProxy delegates calls to OETH implementation\n */\ncontract OETHPlumeProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHPlumeProxy delegates calls to WOETH implementation\n */\ncontract WOETHPlumeProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice RoosterAMOStrategyProxy delegates calls to a RoosterAMOStrategy implementation\n */\ncontract RoosterAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/proxies/Proxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OUSDProxy delegates calls to an OUSD implementation\n */\ncontract OUSDProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WrappedOUSDProxy delegates calls to a WrappedOUSD implementation\n */\ncontract WrappedOUSDProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice VaultProxy delegates calls to a Vault implementation\n */\ncontract VaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CompoundStrategyProxy delegates calls to a CompoundStrategy implementation\n */\ncontract CompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice AaveStrategyProxy delegates calls to a AaveStrategy implementation\n */\ncontract AaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ConvexStrategyProxy delegates calls to a ConvexStrategy implementation\n */\ncontract ConvexStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice HarvesterProxy delegates calls to a Harvester implementation\n */\ncontract HarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice DripperProxy delegates calls to a Dripper implementation\n */\ncontract DripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoCompoundStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\n */\ncontract MorphoCompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ConvexOUSDMetaStrategyProxy delegates calls to a ConvexOUSDMetaStrategy implementation\n */\ncontract ConvexOUSDMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoAaveStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\n */\ncontract MorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHProxy delegates calls to nowhere for now\n */\ncontract OETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHProxy delegates calls to nowhere for now\n */\ncontract WOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHVaultProxy delegates calls to a Vault implementation\n */\ncontract OETHVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHDripperProxy delegates calls to a OETHDripper implementation\n */\ncontract OETHDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHHarvesterProxy delegates calls to a Harvester implementation\n */\ncontract OETHHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CurveEthStrategyProxy delegates calls to a CurveEthStrategy implementation\n */\ncontract ConvexEthMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BuybackProxy delegates calls to Buyback implementation\n */\ncontract BuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHMorphoAaveStrategyProxy delegates calls to a MorphoAaveStrategy implementation\n */\ncontract OETHMorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBalancerMetaPoolrEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\n */\ncontract OETHBalancerMetaPoolrEthStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice OETHBalancerMetaPoolwstEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\n */\ncontract OETHBalancerMetaPoolwstEthStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MakerDsrStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MakerDsrStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBuybackProxy delegates calls to Buyback implementation\n */\ncontract OETHBuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BridgedWOETHProxy delegates calls to BridgedWOETH implementation\n */\ncontract BridgedWOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice NativeStakingSSVStrategyProxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulatorProxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulatorProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingSSVStrategy2Proxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategy2Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulator2Proxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulator2Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingSSVStrategy3Proxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategy3Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulator3Proxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulator3Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MetaMorphoStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MetaMorphoStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ARMBuybackProxy delegates calls to Buyback implementation\n */\ncontract ARMBuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoGauntletPrimeUSDCStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MorphoGauntletPrimeUSDCStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MorphoGauntletPrimeUSDTStrategyProxy delegates calls to a Generalized4626USDTStrategy implementation\n */\ncontract MorphoGauntletPrimeUSDTStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice CurvePoolBoosterProxy delegates calls to a CurvePoolBooster implementation\n */\ncontract CurvePoolBoosterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHFixedRateDripperProxy delegates calls to a OETHFixedRateDripper implementation\n */\ncontract OETHFixedRateDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHSimpleHarvesterProxy delegates calls to a OETHSimpleHarvester implementation\n */\ncontract OETHSimpleHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice PoolBoostCentralRegistryProxy delegates calls to the PoolBoostCentralRegistry implementation\n */\ncontract PoolBoostCentralRegistryProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MakerSSRStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MakerSSRStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OUSDCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\n */\ncontract OUSDCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\n */\ncontract OETHCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CompoundingStakingSSVStrategyProxy delegates calls to a CompoundingStakingSSVStrategy implementation\n */\ncontract CompoundingStakingSSVStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n" + }, + "contracts/proxies/SonicProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OSonicVaultProxy delegates calls to OSonicVault implementation\n */\ncontract OSonicVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicProxy delegates calls to OSonic implementation\n */\ncontract OSonicProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOSonicProxy delegates calls to WOSonic implementation\n */\ncontract WOSonicProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicDripperProxy delegates calls to a FixedRateDripper implementation\n */\ncontract OSonicDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice SonicStakingStrategyProxy delegates calls to SonicStakingStrategy implementation\n */\ncontract SonicStakingStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicHarvesterProxy delegates calls to a OSonicHarvester implementation\n */\ncontract OSonicHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice SonicSwapXAMOStrategyProxy delegates calls to a SonicSwapXAMOStrategy implementation\n */\ncontract SonicSwapXAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/strategies/AaveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Aave Strategy\n * @notice Investment strategy for investing stablecoins via Aave\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport \"./IAave.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\nimport { IAaveStakedToken } from \"./IAaveStakeToken.sol\";\nimport { IAaveIncentivesController } from \"./IAaveIncentivesController.sol\";\n\ncontract AaveStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n\n uint16 constant referralCode = 92;\n\n IAaveIncentivesController public incentivesController;\n IAaveStakedToken public stkAave;\n\n /**\n * @param _stratConfig The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as AAVE needs several extra\n * addresses for the rewards program.\n * @param _rewardTokenAddresses Address of the AAVE token\n * @param _assets Addresses of supported assets\n * @param _pTokens Platform Token corresponding addresses\n * @param _incentivesAddress Address of the AAVE incentives controller\n * @param _stkAaveAddress Address of the stkAave contract\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // AAVE\n address[] calldata _assets,\n address[] calldata _pTokens,\n address _incentivesAddress,\n address _stkAaveAddress\n ) external onlyGovernor initializer {\n incentivesController = IAaveIncentivesController(_incentivesAddress);\n stkAave = IAaveStakedToken(_stkAaveAddress);\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Deposit asset into Aave\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Aave\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n // Following line also doubles as a check that we are depositing\n // an asset that we support.\n emit Deposit(_asset, _getATokenFor(_asset), _amount);\n _getLendingPool().deposit(_asset, _amount, address(this), referralCode);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Aave\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Aave\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n emit Withdrawal(_asset, _getATokenFor(_asset), _amount);\n uint256 actual = _getLendingPool().withdraw(\n _asset,\n _amount,\n address(this)\n );\n require(actual == _amount, \"Did not withdraw enough\");\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n // Redeem entire balance of aToken\n IERC20 asset = IERC20(assetsMapped[i]);\n address aToken = _getATokenFor(assetsMapped[i]);\n uint256 balance = IERC20(aToken).balanceOf(address(this));\n if (balance > 0) {\n uint256 actual = _getLendingPool().withdraw(\n address(asset),\n balance,\n address(this)\n );\n require(actual == balance, \"Did not withdraw enough\");\n\n uint256 assetBalance = asset.balanceOf(address(this));\n // Transfer entire balance to Vault\n asset.safeTransfer(vaultAddress, assetBalance);\n\n emit Withdrawal(address(asset), aToken, assetBalance);\n }\n }\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n // Balance is always with token aToken decimals\n address aToken = _getATokenFor(_asset);\n balance = IERC20(aToken).balanceOf(address(this));\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Approve the spending of all assets by their corresponding aToken,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n address lendingPool = address(_getLendingPool());\n // approve the pool to spend the Asset\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n address asset = assetsMapped[i];\n // Safe approval\n IERC20(asset).safeApprove(lendingPool, 0);\n IERC20(asset).safeApprove(lendingPool, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / aTokens\n We need to give the AAVE lending pool approval to transfer the\n asset.\n * @param _asset Address of the asset to approve\n * @param _aToken Address of the aToken\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _aToken)\n internal\n override\n {\n address lendingPool = address(_getLendingPool());\n IERC20(_asset).safeApprove(lendingPool, 0);\n IERC20(_asset).safeApprove(lendingPool, type(uint256).max);\n }\n\n /**\n * @dev Get the aToken wrapped in the IERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return Corresponding aToken to this asset\n */\n function _getATokenFor(address _asset) internal view returns (address) {\n address aToken = assetToPToken[_asset];\n require(aToken != address(0), \"aToken does not exist\");\n return aToken;\n }\n\n /**\n * @dev Get the current address of the Aave lending pool, which is the gateway to\n * depositing.\n * @return Current lending pool implementation\n */\n function _getLendingPool() internal view returns (IAaveLendingPool) {\n address lendingPool = ILendingPoolAddressesProvider(platformAddress)\n .getLendingPool();\n require(lendingPool != address(0), \"Lending pool does not exist\");\n return IAaveLendingPool(lendingPool);\n }\n\n /**\n * @dev Collect stkAave, convert it to AAVE send to Vault.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n if (address(stkAave) == address(0)) {\n return;\n }\n\n // Check staked AAVE cooldown timer\n uint256 cooldown = stkAave.stakersCooldowns(address(this));\n uint256 windowStart = cooldown + stkAave.COOLDOWN_SECONDS();\n uint256 windowEnd = windowStart + stkAave.UNSTAKE_WINDOW();\n\n // If inside the unlock window, then we can redeem stkAave\n // for AAVE and send it to the vault.\n if (block.timestamp > windowStart && block.timestamp <= windowEnd) {\n // Redeem to AAVE\n uint256 stkAaveBalance = stkAave.balanceOf(address(this));\n stkAave.redeem(address(this), stkAaveBalance);\n\n // Transfer AAVE to harvesterAddress\n uint256 aaveBalance = IERC20(rewardTokenAddresses[0]).balanceOf(\n address(this)\n );\n if (aaveBalance > 0) {\n IERC20(rewardTokenAddresses[0]).safeTransfer(\n harvesterAddress,\n aaveBalance\n );\n }\n }\n\n // Collect available rewards and restart the cooldown timer, if either of\n // those should be run.\n if (block.timestamp > windowStart || cooldown == 0) {\n uint256 assetsLen = assetsMapped.length;\n // aToken addresses for incentives controller\n address[] memory aTokens = new address[](assetsLen);\n for (uint256 i = 0; i < assetsLen; ++i) {\n aTokens[i] = _getATokenFor(assetsMapped[i]);\n }\n\n // 1. If we have rewards availabile, collect them\n uint256 pendingRewards = incentivesController.getRewardsBalance(\n aTokens,\n address(this)\n );\n if (pendingRewards > 0) {\n // Because getting more stkAAVE from the incentives controller\n // with claimRewards() may push the stkAAVE cooldown time\n // forward, it is called after stakedAAVE has been turned into\n // AAVE.\n uint256 collected = incentivesController.claimRewards(\n aTokens,\n pendingRewards,\n address(this)\n );\n require(collected == pendingRewards, \"AAVE reward difference\");\n }\n\n // 2. Start cooldown counting down.\n if (stkAave.balanceOf(address(this)) > 0) {\n // Protected with if since cooldown call would revert\n // if no stkAave balance.\n stkAave.cooldown();\n }\n }\n }\n}\n" + }, + "contracts/strategies/AbstractCompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base Compound Abstract Strategy\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { ICERC20 } from \"./ICompound.sol\";\nimport { IComptroller } from \"../interfaces/IComptroller.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\nabstract contract AbstractCompoundStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n\n int256[50] private __reserved;\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Get the cToken wrapped in the ICERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return Corresponding cToken to this asset\n */\n function _getCTokenFor(address _asset) internal view returns (ICERC20) {\n address cToken = assetToPToken[_asset];\n require(cToken != address(0), \"cToken does not exist\");\n return ICERC20(cToken);\n }\n\n /**\n * @dev Converts an underlying amount into cToken amount\n * cTokenAmt = (underlying * 1e18) / exchangeRate\n * @param _cToken cToken for which to change\n * @param _underlying Amount of underlying to convert\n * @return amount Equivalent amount of cTokens\n */\n function _convertUnderlyingToCToken(ICERC20 _cToken, uint256 _underlying)\n internal\n view\n returns (uint256 amount)\n {\n // e.g. 1e18*1e18 / 205316390724364402565641705 = 50e8\n // e.g. 1e8*1e18 / 205316390724364402565641705 = 0.45 or 0\n amount = (_underlying * 1e18) / _cToken.exchangeRateStored();\n }\n}\n" + }, + "contracts/strategies/AbstractConvexMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { ICurveMetaPool } from \"./ICurveMetaPool.sol\";\nimport { IERC20, AbstractCurveStrategy, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\nabstract contract AbstractConvexMetaStrategy is AbstractCurveStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n event MaxWithdrawalSlippageUpdated(\n uint256 _prevMaxSlippagePercentage,\n uint256 _newMaxSlippagePercentage\n );\n\n // used to circumvent the stack too deep issue\n struct InitConfig {\n address cvxDepositorAddress; //Address of the Convex depositor(AKA booster) for this pool\n address metapoolAddress; //Address of the Curve MetaPool\n address metapoolMainToken; //Address of Main metapool token\n address cvxRewardStakerAddress; //Address of the CVX rewards staker\n address metapoolLPToken; //Address of metapool LP token\n uint256 cvxDepositorPTokenId; //Pid of the pool referred to by Depositor and staker\n }\n\n address internal cvxDepositorAddress;\n address internal cvxRewardStakerAddress;\n uint256 internal cvxDepositorPTokenId;\n ICurveMetaPool internal metapool;\n IERC20 internal metapoolMainToken;\n IERC20 internal metapoolLPToken;\n // Ordered list of metapool assets\n address[] internal metapoolAssets;\n // Max withdrawal slippage denominated in 1e18 (1e18 == 100%)\n uint256 public maxWithdrawalSlippage;\n uint128 internal crvCoinIndex;\n uint128 internal mainCoinIndex;\n\n int256[41] private ___reserved;\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * DAI, USDC, USDT\n * @param _pTokens Platform Token corresponding addresses\n * @param initConfig Various addresses and info for initialization state\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets,\n address[] calldata _pTokens,\n InitConfig calldata initConfig\n ) external onlyGovernor initializer {\n require(_assets.length == 3, \"Must have exactly three assets\");\n // Should be set prior to abstract initialize call otherwise\n // abstractSetPToken calls will fail\n cvxDepositorAddress = initConfig.cvxDepositorAddress;\n pTokenAddress = _pTokens[0];\n metapool = ICurveMetaPool(initConfig.metapoolAddress);\n metapoolMainToken = IERC20(initConfig.metapoolMainToken);\n cvxRewardStakerAddress = initConfig.cvxRewardStakerAddress;\n metapoolLPToken = IERC20(initConfig.metapoolLPToken);\n cvxDepositorPTokenId = initConfig.cvxDepositorPTokenId;\n maxWithdrawalSlippage = 1e16;\n\n metapoolAssets = [metapool.coins(0), metapool.coins(1)];\n crvCoinIndex = _getMetapoolCoinIndex(pTokenAddress);\n mainCoinIndex = _getMetapoolCoinIndex(initConfig.metapoolMainToken);\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n balance = 0;\n\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 contractPTokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (contractPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = contractPTokens.mulTruncate(virtual_price);\n balance += value;\n }\n\n /* We intentionally omit the metapoolLp tokens held by the metastrategyContract\n * since the contract should never (except in the middle of deposit/withdrawal\n * transaction) hold any amount of those tokens in normal operation. There\n * could be tokens sent to it by a 3rd party and we decide to actively ignore\n * those.\n */\n uint256 metapoolGaugePTokens = IRewardStaking(cvxRewardStakerAddress)\n .balanceOf(address(this));\n\n if (metapoolGaugePTokens > 0) {\n uint256 value = metapoolGaugePTokens.mulTruncate(\n metapool.get_virtual_price()\n );\n balance += value;\n }\n\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = balance.scaleBy(assetDecimals, 18) / THREEPOOL_ASSET_COUNT;\n }\n\n /**\n * @dev This function is completely analogous to _calcCurveTokenAmount[AbstractCurveStrategy]\n * and just utilizes different Curve (meta)pool API\n */\n function _calcCurveMetaTokenAmount(uint128 _coinIndex, uint256 _amount)\n internal\n returns (uint256 requiredMetapoolLP)\n {\n uint256[2] memory _amounts = [uint256(0), uint256(0)];\n _amounts[uint256(_coinIndex)] = _amount;\n\n // LP required when removing required asset ignoring fees\n uint256 lpRequiredNoFees = metapool.calc_token_amount(_amounts, false);\n /* LP required if fees would apply to entirety of removed amount\n *\n * fee is 1e10 denominated number: https://curve.readthedocs.io/exchange-pools.html#StableSwap.fee\n */\n uint256 lpRequiredFullFees = lpRequiredNoFees.mulTruncateScale(\n 1e10 + metapool.fee(),\n 1e10\n );\n\n /* asset received when withdrawing full fee applicable LP accounting for\n * slippage and fees\n */\n uint256 assetReceivedForFullLPFees = metapool.calc_withdraw_one_coin(\n lpRequiredFullFees,\n int128(_coinIndex)\n );\n\n // exact amount of LP required\n requiredMetapoolLP =\n (lpRequiredFullFees * _amount) /\n assetReceivedForFullLPFees;\n }\n\n function _approveBase() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // 3Pool for LP token (required for removing liquidity)\n pToken.safeApprove(platformAddress, 0);\n pToken.safeApprove(platformAddress, type(uint256).max);\n // Gauge for LP token\n metapoolLPToken.safeApprove(cvxDepositorAddress, 0);\n metapoolLPToken.safeApprove(cvxDepositorAddress, type(uint256).max);\n // Metapool for LP token\n pToken.safeApprove(address(metapool), 0);\n pToken.safeApprove(address(metapool), type(uint256).max);\n // Metapool for Metapool main token\n metapoolMainToken.safeApprove(address(metapool), 0);\n metapoolMainToken.safeApprove(address(metapool), type(uint256).max);\n }\n\n /**\n * @dev Get the index of the coin\n */\n function _getMetapoolCoinIndex(address _asset)\n internal\n view\n returns (uint128)\n {\n for (uint128 i = 0; i < 2; i++) {\n if (metapoolAssets[i] == _asset) return i;\n }\n revert(\"Invalid Metapool asset\");\n }\n\n /**\n * @dev Sets max withdrawal slippage that is considered when removing\n * liquidity from Metapools.\n * @param _maxWithdrawalSlippage Max withdrawal slippage denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxWithdrawalSlippage should actually be 0.1% (1e15)\n * for production usage. Contract allows as low value as 0% for confirming\n * correct behavior in test suite.\n */\n function setMaxWithdrawalSlippage(uint256 _maxWithdrawalSlippage)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(\n _maxWithdrawalSlippage <= 1e18,\n \"Max withdrawal slippage needs to be between 0% - 100%\"\n );\n emit MaxWithdrawalSlippageUpdated(\n maxWithdrawalSlippage,\n _maxWithdrawalSlippage\n );\n maxWithdrawalSlippage = _maxWithdrawalSlippage;\n }\n\n /**\n * @dev Collect accumulated CRV and CVX and send to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n IRewardStaking(cvxRewardStakerAddress).getReward();\n _collectRewardTokens();\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/AbstractCurveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve 3Pool Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\nabstract contract AbstractCurveStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 internal constant MAX_SLIPPAGE = 1e16; // 1%, same as the Curve UI\n // number of assets in Curve 3Pool (USDC, DAI, USDT)\n uint256 internal constant THREEPOOL_ASSET_COUNT = 3;\n address internal pTokenAddress;\n\n int256[49] private __reserved;\n\n /**\n * @dev Deposit asset into the Curve 3Pool\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_amount > 0, \"Must deposit something\");\n emit Deposit(_asset, pTokenAddress, _amount);\n\n // 3Pool requires passing deposit amounts for all 3 assets, set to 0 for\n // all\n uint256[3] memory _amounts;\n uint256 poolCoinIndex = _getCoinIndex(_asset);\n // Set the amount on the asset we want to deposit\n _amounts[poolCoinIndex] = _amount;\n ICurvePool curvePool = ICurvePool(platformAddress);\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n uint256 depositValue = _amount.scaleBy(18, assetDecimals).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = depositValue.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n // Do the deposit to 3pool\n curvePool.add_liquidity(_amounts, minMintAmount);\n _lpDepositAll();\n }\n\n function _lpDepositAll() internal virtual;\n\n /**\n * @dev Deposit the entire balance of any supported asset into the Curve 3pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n uint256 depositValue = 0;\n ICurvePool curvePool = ICurvePool(platformAddress);\n uint256 curveVirtualPrice = curvePool.get_virtual_price();\n\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address assetAddress = assetsMapped[i];\n uint256 balance = IERC20(assetAddress).balanceOf(address(this));\n if (balance > 0) {\n uint256 poolCoinIndex = _getCoinIndex(assetAddress);\n // Set the amount on the asset we want to deposit\n _amounts[poolCoinIndex] = balance;\n uint256 assetDecimals = Helpers.getDecimals(assetAddress);\n // Get value of deposit in Curve LP token to later determine\n // the minMintAmount argument for add_liquidity\n depositValue =\n depositValue +\n balance.scaleBy(18, assetDecimals).divPrecisely(\n curveVirtualPrice\n );\n emit Deposit(assetAddress, pTokenAddress, balance);\n }\n }\n\n uint256 minMintAmount = depositValue.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n // Do the deposit to 3pool\n curvePool.add_liquidity(_amounts, minMintAmount);\n\n /* In case of Curve Strategy all assets are mapped to the same pToken (3CrvLP). Let\n * descendants further handle the pToken. By either deploying it to the metapool and\n * resulting tokens in Gauge. Or deploying pTokens directly to the Gauge.\n */\n _lpDepositAll();\n }\n\n function _lpWithdraw(uint256 numCrvTokens) internal virtual;\n\n function _lpWithdrawAll() internal virtual;\n\n /**\n * @dev Withdraw asset from Curve 3Pool\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Invalid amount\");\n\n emit Withdrawal(_asset, pTokenAddress, _amount);\n\n uint256 contractCrv3Tokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n\n uint256 coinIndex = _getCoinIndex(_asset);\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 requiredCrv3Tokens = _calcCurveTokenAmount(coinIndex, _amount);\n\n // We have enough LP tokens, make sure they are all on this contract\n if (contractCrv3Tokens < requiredCrv3Tokens) {\n _lpWithdraw(requiredCrv3Tokens - contractCrv3Tokens);\n }\n\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n _amounts[coinIndex] = _amount;\n\n curvePool.remove_liquidity_imbalance(_amounts, requiredCrv3Tokens);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Calculate amount of LP required when withdrawing specific amount of one\n * of the underlying assets accounting for fees and slippage.\n *\n * Curve pools unfortunately do not contain a calculation function for\n * amount of LP required when withdrawing a specific amount of one of the\n * underlying tokens and also accounting for fees (Curve's calc_token_amount\n * does account for slippage but not fees).\n *\n * Steps taken to calculate the metric:\n * - get amount of LP required if fees wouldn't apply\n * - increase the LP amount as if fees would apply to the entirety of the underlying\n * asset withdrawal. (when withdrawing only one coin fees apply only to amounts\n * of other assets pool would return in case of balanced removal - since those need\n * to be swapped for the single underlying asset being withdrawn)\n * - get amount of underlying asset withdrawn (this Curve function does consider slippage\n * and fees) when using the increased LP amount. As LP amount is slightly over-increased\n * so is amount of underlying assets returned.\n * - since we know exactly how much asset we require take the rate of LP required for asset\n * withdrawn to get the exact amount of LP.\n */\n function _calcCurveTokenAmount(uint256 _coinIndex, uint256 _amount)\n internal\n returns (uint256 required3Crv)\n {\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n _amounts[_coinIndex] = _amount;\n\n // LP required when removing required asset ignoring fees\n uint256 lpRequiredNoFees = curvePool.calc_token_amount(_amounts, false);\n /* LP required if fees would apply to entirety of removed amount\n *\n * fee is 1e10 denominated number: https://curve.readthedocs.io/exchange-pools.html#StableSwap.fee\n */\n uint256 lpRequiredFullFees = lpRequiredNoFees.mulTruncateScale(\n 1e10 + curvePool.fee(),\n 1e10\n );\n\n /* asset received when withdrawing full fee applicable LP accounting for\n * slippage and fees\n */\n uint256 assetReceivedForFullLPFees = curvePool.calc_withdraw_one_coin(\n lpRequiredFullFees,\n int128(uint128(_coinIndex))\n );\n\n // exact amount of LP required\n required3Crv =\n (lpRequiredFullFees * _amount) /\n assetReceivedForFullLPFees;\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n _lpWithdrawAll();\n // Withdraws are proportional to assets held by 3Pool\n uint256[3] memory minWithdrawAmounts = [\n uint256(0),\n uint256(0),\n uint256(0)\n ];\n\n // Remove liquidity\n ICurvePool threePool = ICurvePool(platformAddress);\n threePool.remove_liquidity(\n IERC20(pTokenAddress).balanceOf(address(this)),\n minWithdrawAmounts\n );\n // Transfer assets out of Vault\n // Note that Curve will provide all 3 of the assets in 3pool even if\n // we have not set PToken addresses for all of them in this strategy\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n IERC20 asset = IERC20(threePool.coins(i));\n asset.safeTransfer(vaultAddress, asset.balanceOf(address(this)));\n }\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 totalPTokens = IERC20(pTokenAddress).balanceOf(address(this));\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (totalPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = (totalPTokens * virtual_price) / 1e18;\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = value.scaleBy(assetDecimals, 18) / THREEPOOL_ASSET_COUNT;\n }\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n // This strategy is a special case since it only supports one asset\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n _approveAsset(assetsMapped[i]);\n }\n }\n\n /**\n * @dev Call the necessary approvals for the Curve pool and gauge\n * @param _asset Address of the asset\n */\n function _abstractSetPToken(address _asset, address) internal override {\n _approveAsset(_asset);\n }\n\n function _approveAsset(address _asset) internal {\n IERC20 asset = IERC20(_asset);\n // 3Pool for asset (required for adding liquidity)\n asset.safeApprove(platformAddress, 0);\n asset.safeApprove(platformAddress, type(uint256).max);\n }\n\n function _approveBase() internal virtual;\n\n /**\n * @dev Get the index of the coin\n */\n function _getCoinIndex(address _asset) internal view returns (uint256) {\n for (uint256 i = 0; i < 3; i++) {\n if (assetsMapped[i] == _asset) return i;\n }\n revert(\"Invalid 3pool asset\");\n }\n}\n" + }, + "contracts/strategies/aerodrome/AerodromeAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Aerodrome AMO strategy\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nimport { ISugarHelper } from \"../../interfaces/aerodrome/ISugarHelper.sol\";\nimport { INonfungiblePositionManager } from \"../../interfaces/aerodrome/INonfungiblePositionManager.sol\";\nimport { ISwapRouter } from \"../../interfaces/aerodrome/ISwapRouter.sol\";\nimport { ICLPool } from \"../../interfaces/aerodrome/ICLPool.sol\";\nimport { ICLGauge } from \"../../interfaces/aerodrome/ICLGauge.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\n\ncontract AerodromeAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n using SafeCast for uint256;\n\n /************************************************\n Important (!) setup configuration\n *************************************************/\n\n /**\n * In order to be able to remove a reasonable amount of complexity from the contract one of the\n * preconditions for this contract to function correctly is to have an outside account mint a small\n * amount of liquidity in the tick space where the contract will deploy's its liquidity and then send\n * that NFT LP position to a dead address (transfer to zero address not allowed.) See example of such\n * NFT LP token:\n * https://basescan.org/token/0x827922686190790b37229fd06084350e74485b72?a=413296#inventory\n */\n\n /***************************************\n Storage slot members\n ****************************************/\n\n /// @notice tokenId of the liquidity position\n uint256 public tokenId;\n /// @dev Minimum amount of tokens the strategy would be able to withdraw from the pool.\n /// minimum amount of tokens are withdrawn at a 1:1 price\n uint256 public underlyingAssets;\n /// @notice Marks the start of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareStart;\n /// @notice Marks the end of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareEnd;\n /// @dev reserved for inheritance\n int256[46] private __reserved;\n\n /***************************************\n Constants, structs and events\n ****************************************/\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the OETHb token contract\n address public immutable OETHb;\n /// @notice lower tick set to -1 representing the price of 1.0001 of WETH for 1 OETHb.\n int24 public immutable lowerTick;\n /// @notice lower tick set to 0 representing the price of 1.0000 of WETH for 1 OETHb.\n int24 public immutable upperTick;\n /// @notice tick spacing of the pool (set to 1)\n int24 public immutable tickSpacing;\n /// @notice the swapRouter for performing swaps\n ISwapRouter public immutable swapRouter;\n /// @notice the underlying AMO Slipstream pool\n ICLPool public immutable clPool;\n /// @notice the gauge for the corresponding Slipstream pool (clPool)\n /// @dev can become an immutable once the gauge is created on the base main-net\n ICLGauge public immutable clGauge;\n /// @notice the Position manager contract that is used to manage the pool's position\n INonfungiblePositionManager public immutable positionManager;\n /// @notice helper contract for liquidity and ticker math\n ISugarHelper public immutable helper;\n /// @notice sqrtRatioX96TickLower\n /// @dev tick lower has value -1 and represents the lowest price of WETH priced in OETHb. Meaning the pool\n /// offers less than 1 OETHb for 1 WETH. In other terms to get 1 OETHB the swap needs to offer 1.0001 WETH\n /// this is where purchasing OETHb with WETH within the liquidity position is most expensive\n uint160 public immutable sqrtRatioX96TickLower;\n /// @notice sqrtRatioX96TickHigher\n /// @dev tick higher has value 0 and represents 1:1 price parity of WETH to OETHb\n uint160 public immutable sqrtRatioX96TickHigher;\n /// @dev tick closest to 1:1 price parity\n /// Correctly assessing which tick is closer to 1:1 price parity is important since it affects\n /// the way we calculate the underlying assets in check Balance. The underlying aerodrome pool\n /// orders the tokens depending on the values of their addresses. If OETH token is token0 in the pool\n /// then sqrtRatioX96TickClosestToParity=sqrtRatioX96TickLower. If it is token1 in the pool then\n /// sqrtRatioX96TickClosestToParity=sqrtRatioX96TickHigher\n uint160 public immutable sqrtRatioX96TickClosestToParity;\n\n /// @dev a threshold under which the contract no longer allows for the protocol to rebalance. Guarding\n /// against a strategist / guardian being taken over and with multiple transactions draining the\n /// protocol funds.\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth); // 0x989e5ca8\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth); // 0xa6737d87\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n ); // 0x3681e8e0\n error OutsideExpectedTickRange(int24 currentTick); // 0x5a2eba75\n\n event PoolRebalanced(uint256 currentPoolWethShare);\n\n event PoolWethShareIntervalUpdated(\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n\n event LiquidityRemoved(\n uint256 withdrawLiquidityShare,\n uint256 removedWETHAmount,\n uint256 removedOETHbAmount,\n uint256 wethAmountCollected,\n uint256 oethbAmountCollected,\n uint256 underlyingAssets\n );\n\n event LiquidityAdded(\n uint256 wethAmountDesired,\n uint256 oethbAmountDesired,\n uint256 wethAmountSupplied,\n uint256 oethbAmountSupplied,\n uint256 tokenId,\n uint256 underlyingAssets\n );\n\n event UnderlyingAssetsUpdated(uint256 underlyingAssets);\n\n /**\n * @dev Un-stakes the token from the gauge for the execution duration of\n * the function and after that re-stakes it back in.\n *\n * It is important that the token is unstaked and owned by the strategy contract\n * during any liquidity altering operations and that it is re-staked back into the\n * gauge after liquidity changes. If the token fails to re-stake back to the\n * gauge it is not earning incentives.\n */\n // all functions using this modifier are used by functions with reentrancy check\n // slither-disable-start reentrancy-no-eth\n modifier gaugeUnstakeAndRestake() {\n // because of solidity short-circuit _isLpTokenStakedInGauge doesn't get called\n // when tokenId == 0\n if (tokenId != 0 && _isLpTokenStakedInGauge()) {\n clGauge.withdraw(tokenId);\n }\n _;\n // because of solidity short-circuit _isLpTokenStakedInGauge doesn't get called\n // when tokenId == 0\n if (tokenId != 0 && !_isLpTokenStakedInGauge()) {\n /**\n * It can happen that a withdrawal (or a full withdrawal) transactions would\n * remove all of the liquidity from the token with a NFT token still existing.\n * In that case the token can not be staked into the gauge, as some liquidity\n * needs to be added to it first.\n */\n if (_getLiquidity() > 0) {\n // if token liquidity changes the positionManager requires re-approval.\n // to any contract pre-approved to handle the token.\n positionManager.approve(address(clGauge), tokenId);\n clGauge.deposit(tokenId);\n }\n }\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice the constructor\n /// @dev This contract is intended to be used as a proxy. To prevent the\n /// potential confusion of having a functional implementation contract\n /// the constructor has the `initializer` modifier. This way the\n /// `initialize` function can not be called on the implementation contract.\n /// For the same reason the implementation contract also has the governor\n /// set to a zero address.\n /// @param _stratConfig the basic strategy configuration\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _oethbAddress Address of the Erc20 OETHb Token contract\n /// @param _swapRouter Address of the Aerodrome Universal Swap Router\n /// @param _nonfungiblePositionManager Address of position manager to add/remove\n /// the liquidity\n /// @param _clPool Address of the Aerodrome concentrated liquidity pool\n /// @param _clGauge Address of the Aerodrome slipstream pool gauge\n /// @param _sugarHelper Address of the Aerodrome Sugar helper contract\n /// @param _lowerBoundingTick Smaller bounding tick of our liquidity position\n /// @param _upperBoundingTick Larger bounding tick of our liquidity position\n /// @param _tickClosestToParity Tick that is closer to 1:1 price parity\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethbAddress,\n address _swapRouter,\n address _nonfungiblePositionManager,\n address _clPool,\n address _clGauge,\n address _sugarHelper,\n int24 _lowerBoundingTick,\n int24 _upperBoundingTick,\n int24 _tickClosestToParity\n ) initializer InitializableAbstractStrategy(_stratConfig) {\n require(\n _lowerBoundingTick == _tickClosestToParity ||\n _upperBoundingTick == _tickClosestToParity,\n \"Misconfigured tickClosestToParity\"\n );\n require(\n ICLPool(_clPool).token0() == _wethAddress,\n \"Only WETH supported as token0\"\n );\n require(\n ICLPool(_clPool).token1() == _oethbAddress,\n \"Only OETHb supported as token1\"\n );\n int24 _tickSpacing = ICLPool(_clPool).tickSpacing();\n // when we generalize AMO we might support other tick spacings\n require(_tickSpacing == 1, \"Unsupported tickSpacing\");\n\n WETH = _wethAddress;\n OETHb = _oethbAddress;\n swapRouter = ISwapRouter(_swapRouter);\n positionManager = INonfungiblePositionManager(\n _nonfungiblePositionManager\n );\n clPool = ICLPool(_clPool);\n clGauge = ICLGauge(_clGauge);\n helper = ISugarHelper(_sugarHelper);\n sqrtRatioX96TickLower = ISugarHelper(_sugarHelper).getSqrtRatioAtTick(\n _lowerBoundingTick\n );\n sqrtRatioX96TickHigher = ISugarHelper(_sugarHelper).getSqrtRatioAtTick(\n _upperBoundingTick\n );\n sqrtRatioX96TickClosestToParity = ISugarHelper(_sugarHelper)\n .getSqrtRatioAtTick(_tickClosestToParity);\n\n lowerTick = _lowerBoundingTick;\n upperTick = _upperBoundingTick;\n tickSpacing = _tickSpacing;\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /**\n * @notice initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n */\n function initialize(address[] memory _rewardTokenAddresses)\n external\n onlyGovernor\n initializer\n {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n new address[](0),\n new address[](0)\n );\n }\n\n /***************************************\n Configuration \n ****************************************/\n\n /**\n * @notice Set allowed pool weth share interval. After the rebalance happens\n * the share of WETH token in the ticker needs to be withing the specifications\n * of the interval.\n *\n * @param _allowedWethShareStart Start of WETH share interval expressed as 18 decimal amount\n * @param _allowedWethShareEnd End of WETH share interval expressed as 18 decimal amount\n */\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external onlyGovernor {\n require(\n _allowedWethShareStart < _allowedWethShareEnd,\n \"Invalid interval\"\n );\n // can not go below 1% weth share\n require(_allowedWethShareStart > 0.01 ether, \"Invalid interval start\");\n // can not go above 95% weth share\n require(_allowedWethShareEnd < 0.95 ether, \"Invalid interval end\");\n\n allowedWethShareStart = _allowedWethShareStart;\n allowedWethShareEnd = _allowedWethShareEnd;\n emit PoolWethShareIntervalUpdated(\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n\n /***************************************\n Periphery utils\n ****************************************/\n\n function _isLpTokenStakedInGauge() internal view returns (bool) {\n require(tokenId != 0, \"Missing NFT LP token\");\n\n address owner = positionManager.ownerOf(tokenId);\n require(\n owner == address(clGauge) || owner == address(this),\n \"Unexpected token owner\"\n );\n return owner == address(clGauge);\n }\n\n /***************************************\n Strategy overrides \n ****************************************/\n\n /**\n * @notice Deposit an amount of assets into the strategy contract. Calling deposit doesn't\n * automatically deposit funds into the underlying Aerodrome pool\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposit WETH to the strategy contract. This function does not add liquidity to the\n * underlying Aerodrome pool.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance > 1e12) {\n _deposit(WETH, _wethBalance);\n }\n }\n\n /**\n * @dev Deposit WETH to the contract. This function doesn't deposit the liquidity to the\n * pool, that is done via the rebalance call.\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n emit Deposit(_asset, address(0), _amount);\n\n // if the pool price is not within the expected interval leave the WETH on the contract\n // as to not break the mints\n (bool _isExpectedRange, ) = _checkForExpectedPoolPrice(false);\n if (_isExpectedRange) {\n // deposit funds into the underlying pool\n _rebalance(0, false, 0);\n }\n }\n\n /**\n * @notice Rebalance the pool to the desired token split and Deposit any WETH on the contract to the\n * underlying aerodrome pool. Print the required amount of corresponding OETHb. After the rebalancing is\n * done burn any potentially remaining OETHb tokens still on the strategy contract.\n *\n * This function has a slightly different behaviour depending on the status of the underlying Aerodrome\n * slipstream pool. The function consists of the following 3 steps:\n * 1. withdrawPartialLiquidity -> so that moving the activeTrading price via a swap is cheaper\n * 2. swapToDesiredPosition -> move active trading price in the pool to be able to deposit WETH & OETHb\n * tokens with the desired pre-configured shares\n * 3. addLiquidity -> add liquidity into the pool respecting share split configuration\n *\n * Scenario 1: When there is no liquidity in the pool from the strategy but there is from other LPs then\n * only step 1 is skipped. (It is important to note that liquidity needs to exist in the configured\n * strategy tick ranges in order for the swap to be possible) Step 3 mints new liquidity position\n * instead of adding to an existing one.\n * Scenario 2: When there is strategy's liquidity in the pool all 3 steps are taken\n *\n *\n * Exact _amountToSwap, _swapWeth & _minTokenReceived parameters shall be determined by simulating the\n * transaction off-chain. The strategy checks that after the swap the share of the tokens is in the\n * expected ranges.\n *\n * @param _amountToSwap The amount of the token to swap\n * @param _swapWeth Swap using WETH when true, use OETHb when false\n * @param _minTokenReceived Slippage check -> minimum amount of token expected in return\n */\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) external nonReentrant onlyGovernorOrStrategist {\n _rebalance(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n\n function _rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n /**\n * Would be nice to check if there is any total liquidity in the pool before performing this swap\n * but there is no easy way to do that in UniswapV3:\n * - clPool.liquidity() -> only liquidity in the active tick\n * - asset[1&2].balanceOf(address(clPool)) -> will include uncollected tokens of LP providers\n * after their liquidity position has been decreased\n */\n\n /**\n * When rebalance is called for the first time there is no strategy\n * liquidity in the pool yet. The liquidity removal is thus skipped.\n * Also execute this function when WETH is required for the swap.\n */\n if (tokenId != 0 && _swapWeth && _amountToSwap > 0) {\n _ensureWETHBalance(_amountToSwap);\n }\n\n // in some cases we will just want to add liquidity and not issue a swap to move the\n // active trading position within the pool\n if (_amountToSwap > 0) {\n _swapToDesiredPosition(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n // calling check liquidity early so we don't get unexpected errors when adding liquidity\n // in the later stages of this function\n _checkForExpectedPoolPrice(true);\n\n _addLiquidity();\n\n // this call shouldn't be necessary, since adding liquidity shouldn't affect the active\n // trading price. It is a defensive programming measure.\n (, uint256 _wethSharePct) = _checkForExpectedPoolPrice(true);\n\n // revert if protocol insolvent\n _solvencyAssert();\n\n emit PoolRebalanced(_wethSharePct);\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethbSupply = IERC20(OETHb).totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethbSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /**\n * @dev Decrease partial or all liquidity from the pool.\n * @param _liquidityToDecrease The amount of liquidity to remove expressed in 18 decimal point\n */\n function _removeLiquidity(uint256 _liquidityToDecrease)\n internal\n gaugeUnstakeAndRestake\n {\n require(_liquidityToDecrease > 0, \"Must remove some liquidity\");\n\n uint128 _liquidity = _getLiquidity();\n // need to convert to uint256 since intermittent result is to big for uint128 to handle\n uint128 _liquidityToRemove = uint256(_liquidity)\n .mulTruncate(_liquidityToDecrease)\n .toUint128();\n\n /**\n * There is no liquidity to remove -> exit function early. This can happen after a\n * withdraw/withdrawAll removes all of the liquidity while retaining the NFT token.\n */\n if (_liquidity == 0 || _liquidityToRemove == 0) {\n return;\n }\n\n (uint256 _amountWeth, uint256 _amountOethb) = positionManager\n .decreaseLiquidity(\n // Both expected amounts can be 0 since we don't really care if any swaps\n // happen just before the liquidity removal.\n INonfungiblePositionManager.DecreaseLiquidityParams({\n tokenId: tokenId,\n liquidity: _liquidityToRemove,\n amount0Min: 0,\n amount1Min: 0,\n deadline: block.timestamp\n })\n );\n\n (\n uint256 _amountWethCollected,\n uint256 _amountOethbCollected\n ) = positionManager.collect(\n INonfungiblePositionManager.CollectParams({\n tokenId: tokenId,\n recipient: address(this),\n amount0Max: type(uint128).max, // defaults to all tokens owed\n amount1Max: type(uint128).max // defaults to all tokens owed\n })\n );\n\n _updateUnderlyingAssets();\n\n emit LiquidityRemoved(\n _liquidityToDecrease,\n _amountWeth, //removedWethAmount\n _amountOethb, //removedOethbAmount\n _amountWethCollected,\n _amountOethbCollected,\n underlyingAssets\n );\n\n _burnOethbOnTheContract();\n }\n\n /**\n * @dev Perform a swap so that after the swap the ticker has the desired WETH to OETHb token share.\n */\n function _swapToDesiredPosition(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n IERC20 _tokenToSwap = IERC20(_swapWeth ? WETH : OETHb);\n uint256 _balance = _tokenToSwap.balanceOf(address(this));\n\n if (_balance < _amountToSwap) {\n // This should never trigger since _ensureWETHBalance will already\n // throw an error if there is not enough WETH\n if (_swapWeth) {\n revert NotEnoughWethForSwap(_balance, _amountToSwap);\n }\n // if swapping OETHb\n uint256 mintForSwap = _amountToSwap - _balance;\n IVault(vaultAddress).mintForStrategy(mintForSwap);\n }\n\n // approve the specific amount of WETH required\n if (_swapWeth) {\n IERC20(WETH).approve(address(swapRouter), _amountToSwap);\n }\n\n // Swap it\n swapRouter.exactInputSingle(\n // sqrtPriceLimitX96 is just a rough sanity check that we are within 0 -> 1 tick\n // a more fine check is performed in _checkForExpectedPoolPrice\n // Note: this needs further work if we want to generalize this approach\n ISwapRouter.ExactInputSingleParams({\n tokenIn: address(_tokenToSwap),\n tokenOut: _swapWeth ? OETHb : WETH,\n tickSpacing: tickSpacing, // set to 1\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: _amountToSwap,\n amountOutMinimum: _minTokenReceived, // slippage check\n sqrtPriceLimitX96: _swapWeth\n ? sqrtRatioX96TickLower\n : sqrtRatioX96TickHigher\n })\n );\n\n /**\n * In the interest of each function in _rebalance to leave the contract state as\n * clean as possible the OETHb tokens here are burned. This decreases the\n * dependence where `_swapToDesiredPosition` function relies on later functions\n * (`addLiquidity`) to burn the OETHb. Reducing the risk of error introduction.\n */\n _burnOethbOnTheContract();\n }\n\n /**\n * @dev Add liquidity into the pool in the pre-configured WETH to OETHb share ratios\n * defined by the allowedPoolWethShareStart|End interval. This function will respect\n * liquidity ratios when there is no liquidity yet in the pool. If liquidity is already\n * present then it relies on the `_swapToDesiredPosition` function in a step before\n * to already move the trading price to desired position (with some tolerance).\n */\n // rebalance already has re-entrency checks\n // slither-disable-start reentrancy-no-eth\n function _addLiquidity() internal gaugeUnstakeAndRestake {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n // don't deposit small liquidity amounts\n if (_wethBalance <= 1e12) {\n return;\n }\n\n uint160 _currentPrice = getPoolX96Price();\n /**\n * Sanity check active trading price is positioned within our desired tick.\n *\n * We revert when price is equal to the lower tick even though that is still\n * a valid amount in regards to ticker position by Sugar.estimateAmount call.\n * Current price equaling tick bound at the 1:1 price parity results in\n * uint overfow when calculating the OETHb balance to deposit.\n */\n if (\n _currentPrice <= sqrtRatioX96TickLower ||\n _currentPrice >= sqrtRatioX96TickHigher\n ) {\n revert OutsideExpectedTickRange(getCurrentTradingTick());\n }\n\n /**\n * If estimateAmount1 call fails it could be due to _currentPrice being really\n * close to a tick and amount1 is a larger number than the sugar helper is able\n * to compute.\n *\n * If token addresses were reversed estimateAmount0 would be required here\n */\n uint256 _oethbRequired = helper.estimateAmount1(\n _wethBalance,\n address(0), // no need to pass pool address when current price is specified\n _currentPrice,\n lowerTick,\n upperTick\n );\n\n if (_oethbRequired > _oethbBalance) {\n IVault(vaultAddress).mintForStrategy(\n _oethbRequired - _oethbBalance\n );\n }\n\n // approve the specific amount of WETH required\n IERC20(WETH).approve(address(positionManager), _wethBalance);\n\n uint256 _wethAmountSupplied;\n uint256 _oethbAmountSupplied;\n if (tokenId == 0) {\n (\n tokenId,\n ,\n _wethAmountSupplied,\n _oethbAmountSupplied\n ) = positionManager.mint(\n /** amount0Min & amount1Min are left at 0 because slippage protection is ensured by the\n * _checkForExpectedPoolPrice\n *›\n * Also sqrtPriceX96 is 0 because the pool is already created\n * non zero amount attempts to create a new instance of the pool\n */\n INonfungiblePositionManager.MintParams({\n token0: WETH,\n token1: OETHb,\n tickSpacing: tickSpacing,\n tickLower: lowerTick,\n tickUpper: upperTick,\n amount0Desired: _wethBalance,\n amount1Desired: _oethbRequired,\n amount0Min: 0,\n amount1Min: 0,\n recipient: address(this),\n deadline: block.timestamp,\n sqrtPriceX96: 0\n })\n );\n } else {\n (, _wethAmountSupplied, _oethbAmountSupplied) = positionManager\n .increaseLiquidity(\n /** amount0Min & amount1Min are left at 0 because slippage protection is ensured by the\n * _checkForExpectedPoolPrice\n */\n INonfungiblePositionManager.IncreaseLiquidityParams({\n tokenId: tokenId,\n amount0Desired: _wethBalance,\n amount1Desired: _oethbRequired,\n amount0Min: 0,\n amount1Min: 0,\n deadline: block.timestamp\n })\n );\n }\n\n _updateUnderlyingAssets();\n emit LiquidityAdded(\n _wethBalance, // wethAmountDesired\n _oethbRequired, // oethbAmountDesired\n _wethAmountSupplied, // wethAmountSupplied\n _oethbAmountSupplied, // oethbAmountSupplied\n tokenId, // tokenId\n underlyingAssets\n );\n\n // burn remaining OETHb\n _burnOethbOnTheContract();\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @dev Check that the Aerodrome pool price is within the expected\n * parameters.\n * This function works whether the strategy contract has liquidity\n * position in the pool or not. The function returns _wethSharePct\n * as a gas optimization measure.\n * @param throwException when set to true the function throws an exception\n * when pool's price is not within expected range.\n * @return _isExpectedRange Bool expressing price is within expected range\n * @return _wethSharePct Share of WETH owned by this strategy contract in the\n * configured ticker.\n */\n function _checkForExpectedPoolPrice(bool throwException)\n internal\n view\n returns (bool _isExpectedRange, uint256 _wethSharePct)\n {\n require(\n allowedWethShareStart != 0 && allowedWethShareEnd != 0,\n \"Weth share interval not set\"\n );\n\n uint160 _currentPrice = getPoolX96Price();\n\n /**\n * First check we are in expected tick range\n *\n * We revert even though price being equal to the lower tick would still\n * count being within lower tick for the purpose of Sugar.estimateAmount calls\n */\n if (\n _currentPrice <= sqrtRatioX96TickLower ||\n _currentPrice >= sqrtRatioX96TickHigher\n ) {\n if (throwException) {\n revert OutsideExpectedTickRange(getCurrentTradingTick());\n }\n return (false, 0);\n }\n\n // 18 decimal number expressed WETH tick share\n _wethSharePct = _getWethShare(_currentPrice);\n\n if (\n _wethSharePct < allowedWethShareStart ||\n _wethSharePct > allowedWethShareEnd\n ) {\n if (throwException) {\n revert PoolRebalanceOutOfBounds(\n _wethSharePct,\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n return (false, _wethSharePct);\n }\n\n return (true, _wethSharePct);\n }\n\n /**\n * Burns any OETHb tokens remaining on the strategy contract\n */\n function _burnOethbOnTheContract() internal {\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n if (_oethbBalance > 1e12) {\n IVault(vaultAddress).burnForStrategy(_oethbBalance);\n }\n }\n\n /// @dev This function assumes there are no uncollected tokens in the clPool owned by the strategy contract.\n /// For that reason any liquidity withdrawals must also collect the tokens.\n function _updateUnderlyingAssets() internal {\n if (tokenId == 0) {\n underlyingAssets = 0;\n emit UnderlyingAssetsUpdated(underlyingAssets);\n return;\n }\n\n uint128 _liquidity = _getLiquidity();\n\n /**\n * Our net value represent the smallest amount of tokens we are able to extract from the position\n * given our liquidity.\n *\n * The least amount of tokens extraditable from the position is where the active trading price is\n * at the ticker 0 meaning the pool is offering 1:1 trades between WETH & OETHb. At that moment the pool\n * consists completely of OETHb and no WETH.\n *\n * The more swaps from WETH -> OETHb happen on the pool the more the price starts to move towards the -1\n * ticker making OETHb (priced in WETH) more expensive.\n *\n * An additional note: when liquidity is 0 then the helper returns 0 for both token amounts. And the\n * function set underlying assets to 0.\n */\n (uint256 _wethAmount, uint256 _oethbAmount) = helper\n .getAmountsForLiquidity(\n sqrtRatioX96TickClosestToParity, // sqrtRatioX96\n sqrtRatioX96TickLower, // sqrtRatioAX96\n sqrtRatioX96TickHigher, // sqrtRatioBX96\n _liquidity\n );\n\n require(_wethAmount == 0, \"Non zero wethAmount\");\n underlyingAssets = _oethbAmount;\n emit UnderlyingAssetsUpdated(underlyingAssets);\n }\n\n /**\n * @dev This function removes the appropriate amount of liquidity to assure that the required\n * amount of WETH is available on the contract\n *\n * @param _amount WETH balance required on the contract\n */\n function _ensureWETHBalance(uint256 _amount) internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance >= _amount) {\n return;\n }\n\n require(tokenId != 0, \"No liquidity available\");\n uint256 _additionalWethRequired = _amount - _wethBalance;\n (uint256 _wethInThePool, ) = getPositionPrincipal();\n\n if (_wethInThePool < _additionalWethRequired) {\n revert NotEnoughWethLiquidity(\n _wethInThePool,\n _additionalWethRequired\n );\n }\n\n uint256 shareOfWethToRemove = Math.min(\n _additionalWethRequired.divPrecisely(_wethInThePool) + 1,\n 1e18\n );\n _removeLiquidity(shareOfWethToRemove);\n }\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset WETH address\n * @param _amount Amount of WETH to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n _ensureWETHBalance(_amount);\n\n _withdraw(_recipient, _amount);\n }\n\n /**\n * @notice Withdraw WETH and sends it to the Vault.\n */\n function withdrawAll() external override onlyVault nonReentrant {\n if (tokenId != 0) {\n _removeLiquidity(1e18);\n }\n\n uint256 _balance = IERC20(WETH).balanceOf(address(this));\n if (_balance > 0) {\n _withdraw(vaultAddress, _balance);\n }\n }\n\n function _withdraw(address _recipient, uint256 _amount) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n IERC20(WETH).safeTransfer(_recipient, _amount);\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /**\n * @dev Collect the AERO token from the gauge\n */\n function _collectRewardTokens() internal override {\n if (tokenId != 0 && _isLpTokenStakedInGauge()) {\n clGauge.getReward(tokenId);\n }\n super._collectRewardTokens();\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /**\n * @dev Approve the spending of all assets\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n // to add liquidity to the clPool\n IERC20(OETHb).approve(address(positionManager), type(uint256).max);\n // to be able to rebalance using the swapRouter\n IERC20(OETHb).approve(address(swapRouter), type(uint256).max);\n\n /* the behaviour of this strategy has slightly changed and WETH could be\n * present on the contract between the transactions. For that reason we are\n * un-approving WETH to the swapRouter & positionManager and only approving\n * the required amount before a transaction\n */\n IERC20(WETH).approve(address(swapRouter), 0);\n IERC20(WETH).approve(address(positionManager), 0);\n }\n\n /***************************************\n Balances and Fees\n ****************************************/\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256)\n {\n require(_asset == WETH, \"Only WETH supported\");\n\n // we could in theory deposit to the strategy and forget to call rebalance in the same\n // governance transaction batch. In that case the WETH that is on the strategy contract\n // also needs to be accounted for.\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n // just paranoia check, in case there is OETHb in the strategy that for some reason hasn't\n // been burned yet.\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n return underlyingAssets + _wethBalance + _oethbBalance;\n }\n\n /**\n * @dev Returns the balance of both tokens in a given position (excluding fees)\n * @return _amountWeth Amount of WETH in position\n * @return _amountOethb Amount of OETHb in position\n */\n function getPositionPrincipal()\n public\n view\n returns (uint256 _amountWeth, uint256 _amountOethb)\n {\n if (tokenId == 0) {\n return (0, 0);\n }\n\n uint160 _sqrtRatioX96 = getPoolX96Price();\n (_amountWeth, _amountOethb) = helper.principal(\n positionManager,\n tokenId,\n _sqrtRatioX96\n );\n }\n\n /**\n * @notice Returns the current pool price in X96 format\n * @return _sqrtRatioX96 Pool price\n */\n function getPoolX96Price() public view returns (uint160 _sqrtRatioX96) {\n (_sqrtRatioX96, , , , , ) = clPool.slot0();\n }\n\n /**\n * @notice Returns the current active trading tick of the underlying pool\n * @return _currentTick Current pool trading tick\n */\n function getCurrentTradingTick() public view returns (int24 _currentTick) {\n (, _currentTick, , , , ) = clPool.slot0();\n }\n\n /**\n * @notice Returns the percentage of WETH liquidity in the configured ticker\n * owned by this strategy contract.\n * @return uint256 1e18 denominated percentage expressing the share\n */\n function getWETHShare() external view returns (uint256) {\n uint160 _currentPrice = getPoolX96Price();\n return _getWethShare(_currentPrice);\n }\n\n /**\n * @notice Returns the amount of liquidity in the contract's LP position\n * @return _liquidity Amount of liquidity in the position\n */\n function _getLiquidity() internal view returns (uint128 _liquidity) {\n if (tokenId == 0) {\n revert(\"No LP position\");\n }\n\n (, , , , , , , _liquidity, , , , ) = positionManager.positions(tokenId);\n }\n\n function _getWethShare(uint160 _currentPrice)\n internal\n view\n returns (uint256)\n {\n /**\n * If estimateAmount1 call fails it could be due to _currentPrice being really\n * close to a tick and amount1 too big to compute.\n *\n * If token addresses were reversed estimateAmount0 would be required here\n */\n uint256 _normalizedWethAmount = 1 ether;\n uint256 _correspondingOethAmount = helper.estimateAmount1(\n _normalizedWethAmount,\n address(0), // no need to pass pool address when current price is specified\n _currentPrice,\n lowerTick,\n upperTick\n );\n\n // 18 decimal number expressed weth tick share\n return\n _normalizedWethAmount.divPrecisely(\n _normalizedWethAmount + _correspondingOethAmount\n );\n }\n\n /***************************************\n Hidden functions\n ****************************************/\n /// @inheritdoc InitializableAbstractStrategy\n function setPTokenAddress(address, address) external override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /// @inheritdoc InitializableAbstractStrategy\n function removePToken(uint256) external override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Not supported\n */\n function _abstractSetPToken(address, address) internal override {\n // the deployer shall call safeApproveAllTokens() to set necessary approvals\n revert(\"Unsupported method\");\n }\n\n /***************************************\n ERC721 management\n ****************************************/\n\n /// @notice Callback function for whenever a NFT is transferred to this contract\n // solhint-disable-next-line max-line-length\n /// Ref: https://docs.openzeppelin.com/contracts/3.x/api/token/erc721#IERC721Receiver-onERC721Received-address-address-uint256-bytes-\n function onERC721Received(\n address,\n address,\n uint256,\n bytes calldata\n ) external returns (bytes4) {\n return this.onERC721Received.selector;\n }\n}\n" + }, + "contracts/strategies/balancer/AbstractAuraStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Base Balancer Abstract Strategy\n * @author Origin Protocol Inc\n */\n\nimport { AbstractBalancerStrategy } from \"./AbstractBalancerStrategy.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IERC4626 } from \"../../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport { IRewardStaking } from \"../IRewardStaking.sol\";\n\nabstract contract AbstractAuraStrategy is AbstractBalancerStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n /// @notice Address of the Aura rewards pool\n address public immutable auraRewardPoolAddress;\n\n // renamed from __reserved to not shadow AbstractBalancerStrategy.__reserved,\n int256[50] private __reserved_baseAuraStrategy;\n\n constructor(address _auraRewardPoolAddress) {\n auraRewardPoolAddress = _auraRewardPoolAddress;\n }\n\n /**\n * @dev Deposit all Balancer Pool Tokens (BPT) in this strategy contract\n * to the Aura rewards pool.\n */\n function _lpDepositAll() internal virtual override {\n uint256 bptBalance = IERC20(platformAddress).balanceOf(address(this));\n uint256 auraLp = IERC4626(auraRewardPoolAddress).deposit(\n bptBalance,\n address(this)\n );\n require(bptBalance == auraLp, \"Aura LP != BPT\");\n }\n\n /**\n * @dev Withdraw `numBPTTokens` Balancer Pool Tokens (BPT) from\n * the Aura rewards pool to this strategy contract.\n * @param numBPTTokens Number of Balancer Pool Tokens (BPT) to withdraw\n */\n function _lpWithdraw(uint256 numBPTTokens) internal virtual override {\n IRewardStaking(auraRewardPoolAddress).withdrawAndUnwrap(\n numBPTTokens,\n true // also claim reward tokens\n );\n }\n\n /**\n * @dev Withdraw all Balancer Pool Tokens (BPT) from\n * the Aura rewards pool to this strategy contract.\n */\n function _lpWithdrawAll() internal virtual override {\n // Get all the strategy's BPTs in Aura\n // maxRedeem is implemented as balanceOf(address) in Aura\n uint256 bptBalance = IERC4626(auraRewardPoolAddress).maxRedeem(\n address(this)\n );\n\n IRewardStaking(auraRewardPoolAddress).withdrawAndUnwrap(\n bptBalance,\n true // also claim reward tokens\n );\n }\n\n /**\n * @notice Collects BAL and AURA tokens from the rewards pool.\n */\n function collectRewardTokens()\n external\n virtual\n override\n onlyHarvester\n nonReentrant\n {\n /* Similar to Convex, calling this function collects both of the\n * accrued BAL and AURA tokens.\n */\n IRewardStaking(auraRewardPoolAddress).getReward();\n _collectRewardTokens();\n }\n\n /// @notice Balancer Pool Tokens (BPT) in the Balancer pool and the Aura rewards pool.\n function _getBalancerPoolTokens()\n internal\n view\n override\n returns (uint256 balancerPoolTokens)\n {\n balancerPoolTokens =\n IERC20(platformAddress).balanceOf(address(this)) +\n // maxRedeem is implemented as balanceOf(address) in Aura\n IERC4626(auraRewardPoolAddress).maxRedeem(address(this));\n }\n\n function _approveBase() internal virtual override {\n super._approveBase();\n\n IERC20 pToken = IERC20(platformAddress);\n pToken.safeApprove(auraRewardPoolAddress, type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/balancer/AbstractBalancerStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Balancer Abstract Strategy\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\nimport { IRateProvider } from \"../../interfaces/balancer/IRateProvider.sol\";\nimport { VaultReentrancyLib } from \"./VaultReentrancyLib.sol\";\nimport { IOracle } from \"../../interfaces/IOracle.sol\";\nimport { IWstETH } from \"../../interfaces/IWstETH.sol\";\nimport { IERC4626 } from \"../../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nabstract contract AbstractBalancerStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n address public immutable rETH;\n address public immutable stETH;\n address public immutable wstETH;\n address public immutable frxETH;\n address public immutable sfrxETH;\n\n /// @notice Address of the Balancer vault\n IBalancerVault public immutable balancerVault;\n /// @notice Balancer pool identifier\n bytes32 public immutable balancerPoolId;\n\n // Max withdrawal deviation denominated in 1e18 (1e18 == 100%)\n uint256 public maxWithdrawalDeviation;\n // Max deposit deviation denominated in 1e18 (1e18 == 100%)\n uint256 public maxDepositDeviation;\n\n int256[48] private __reserved;\n\n struct BaseBalancerConfig {\n address rEthAddress; // Address of the rETH token\n address stEthAddress; // Address of the stETH token\n address wstEthAddress; // Address of the wstETH token\n address frxEthAddress; // Address of the frxEth token\n address sfrxEthAddress; // Address of the sfrxEth token\n address balancerVaultAddress; // Address of the Balancer vault\n bytes32 balancerPoolId; // Balancer pool identifier\n }\n\n event MaxWithdrawalDeviationUpdated(\n uint256 _prevMaxDeviationPercentage,\n uint256 _newMaxDeviationPercentage\n );\n event MaxDepositDeviationUpdated(\n uint256 _prevMaxDeviationPercentage,\n uint256 _newMaxDeviationPercentage\n );\n\n /**\n * @dev Ensure we are not in a Vault context when this function is called, by attempting a no-op internal\n * balance operation. If we are already in a Vault transaction (e.g., a swap, join, or exit), the Vault's\n * reentrancy protection will cause this function to revert.\n *\n * Use this modifier with any function that can cause a state change in a pool and is either public itself,\n * or called by a public function *outside* a Vault operation (e.g., join, exit, or swap).\n *\n * This is to protect against Balancer's read-only re-entrancy vulnerability:\n * https://www.notion.so/originprotocol/Balancer-read-only-reentrancy-c686e72c82414ef18fa34312bb02e11b\n */\n modifier whenNotInBalancerVaultContext() {\n VaultReentrancyLib.ensureNotInVaultContext(balancerVault);\n _;\n }\n\n constructor(BaseBalancerConfig memory _balancerConfig) {\n rETH = _balancerConfig.rEthAddress;\n stETH = _balancerConfig.stEthAddress;\n wstETH = _balancerConfig.wstEthAddress;\n frxETH = _balancerConfig.frxEthAddress;\n sfrxETH = _balancerConfig.sfrxEthAddress;\n\n balancerVault = IBalancerVault(_balancerConfig.balancerVaultAddress);\n balancerPoolId = _balancerConfig.balancerPoolId;\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Balancer's strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of BAL & AURA\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * WETH, stETH\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // BAL & AURA\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n maxWithdrawalDeviation = 1e16;\n maxDepositDeviation = 1e16;\n\n emit MaxWithdrawalDeviationUpdated(0, maxWithdrawalDeviation);\n emit MaxDepositDeviationUpdated(0, maxDepositDeviation);\n\n IERC20[] memory poolAssets = _getPoolAssets();\n require(\n poolAssets.length == _assets.length,\n \"Pool assets length mismatch\"\n );\n for (uint256 i = 0; i < _assets.length; ++i) {\n address asset = _fromPoolAsset(address(poolAssets[i]));\n require(_assets[i] == asset, \"Pool assets mismatch\");\n }\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @notice Get strategy's share of an assets in the Balancer pool.\n * This is not denominated in OUSD/ETH value of the assets in the Balancer pool.\n * @param _asset Address of the Vault collateral asset\n * @return amount the amount of vault collateral assets\n *\n * IMPORTANT if this function is overridden it needs to have a whenNotInBalancerVaultContext\n * modifier on it or it is susceptible to read-only re-entrancy attack\n *\n * @dev it is important that this function is not affected by reporting inflated\n * values of assets in case of any pool manipulation. Such a manipulation could easily\n * exploit the protocol by:\n * - minting OETH\n * - tilting Balancer pool to report higher balances of assets\n * - rebasing() -> all that extra token balances get distributed to OETH holders\n * - tilting pool back\n * - redeeming OETH\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n whenNotInBalancerVaultContext\n returns (uint256 amount)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n\n uint256 bptBalance = _getBalancerPoolTokens();\n\n /* To calculate the worth of queried asset:\n * - assume that all tokens normalized to their ETH value have an equal split balance\n * in the pool when it is balanced\n * - multiply the BPT amount with the bpt rate to get the ETH denominated amount\n * of strategy's holdings\n * - divide that by the number of tokens we support in the pool to get ETH denominated\n * amount that is applicable to each supported token in the pool.\n *\n * It would be possible to support only 1 asset in the pool (and be exposed to all\n * the assets while holding BPT tokens) and deposit/withdraw/checkBalance using only\n * that asset. TBD: changes to other functions still required if we ever decide to\n * go with such configuration.\n */\n amount = (bptBalance.mulTruncate(\n IRateProvider(platformAddress).getRate()\n ) / assetsMapped.length);\n\n /* If the pool asset is equal to (strategy )_asset it means that a rate\n * provider for that asset exists and that asset is not necessarily\n * pegged to a unit (ETH).\n *\n * Because this function returns the balance of the asset and is not denominated in\n * ETH units we need to convert the ETH denominated amount to asset amount.\n */\n if (_toPoolAsset(_asset) == _asset) {\n amount = amount.divPrecisely(_getRateProviderRate(_asset));\n }\n }\n\n /**\n * @notice Returns the value of all assets managed by this strategy.\n * Uses the Balancer pool's rate (virtual price) to convert the strategy's\n * Balancer Pool Tokens (BPT) to ETH value.\n * @return value The ETH value\n *\n * IMPORTANT if this function is overridden it needs to have a whenNotInBalancerVaultContext\n * modifier on it or it is susceptible to read-only re-entrancy attack\n */\n function checkBalance()\n external\n view\n virtual\n whenNotInBalancerVaultContext\n returns (uint256 value)\n {\n uint256 bptBalance = _getBalancerPoolTokens();\n\n // Convert BPT to ETH value\n value = bptBalance.mulTruncate(\n IRateProvider(platformAddress).getRate()\n );\n }\n\n /// @notice Balancer Pool Tokens (BPT) in the Balancer pool.\n function _getBalancerPoolTokens()\n internal\n view\n virtual\n returns (uint256 balancerPoolTokens)\n {\n balancerPoolTokens = IERC20(platformAddress).balanceOf(address(this));\n }\n\n /* solhint-disable max-line-length */\n /**\n * @notice BPT price is calculated by taking the rate from the rateProvider of the asset in\n * question. If one does not exist it defaults to 1e18. To get the final BPT expected that\n * is multiplied by the underlying asset amount divided by BPT token rate. BPT token rate is\n * similar to Curve's virtual_price and expresses how much has the price of BPT appreciated\n * (e.g. due to swap fees) in relation to the underlying assets\n *\n * Using the above approach makes the strategy vulnerable to a possible MEV attack using\n * flash loan to manipulate the pool before a deposit/withdrawal since the function ignores\n * market values of the assets being priced in BPT.\n *\n * At the time of writing there is no safe on-chain approach to pricing BPT in a way that it\n * would make it invulnerable to MEV pool manipulation. See recent Balancer exploit:\n * https://www.notion.so/originprotocol/Balancer-OETH-strategy-9becdea132704e588782a919d7d471eb?pvs=4#1cf07de12fc64f1888072321e0644348\n *\n * To mitigate MEV possibilities during deposits and withdraws, the VaultValueChecker will use checkBalance before and after the move\n * to ensure the expected changes took place.\n *\n * @param _asset Address of the Balancer pool asset\n * @param _amount Amount of the Balancer pool asset\n * @return bptExpected of BPT expected in exchange for the asset\n *\n * @dev\n * bptAssetPrice = 1e18 (asset peg) * pool_asset_rate\n *\n * bptExpected = bptAssetPrice * asset_amount / BPT_token_rate\n *\n * bptExpected = 1e18 (asset peg) * pool_asset_rate * asset_amount / BPT_token_rate\n * bptExpected = asset_amount * pool_asset_rate / BPT_token_rate\n *\n * further information available here:\n * https://www.notion.so/originprotocol/Balancer-OETH-strategy-9becdea132704e588782a919d7d471eb?pvs=4#ce01495ae70346d8971f5dced809fb83\n */\n /* solhint-enable max-line-length */\n function _getBPTExpected(address _asset, uint256 _amount)\n internal\n view\n virtual\n returns (uint256 bptExpected)\n {\n uint256 bptRate = IRateProvider(platformAddress).getRate();\n uint256 poolAssetRate = _getRateProviderRate(_asset);\n bptExpected = _amount.mulTruncate(poolAssetRate).divPrecisely(bptRate);\n }\n\n function _getBPTExpected(\n address[] memory _assets,\n uint256[] memory _amounts\n ) internal view virtual returns (uint256 bptExpected) {\n require(_assets.length == _amounts.length, \"Assets & amounts mismatch\");\n\n for (uint256 i = 0; i < _assets.length; ++i) {\n uint256 poolAssetRate = _getRateProviderRate(_assets[i]);\n // convert asset amount to ETH amount\n bptExpected += _amounts[i].mulTruncate(poolAssetRate);\n }\n\n uint256 bptRate = IRateProvider(platformAddress).getRate();\n // Convert ETH amount to BPT amount\n bptExpected = bptExpected.divPrecisely(bptRate);\n }\n\n function _lpDepositAll() internal virtual;\n\n function _lpWithdraw(uint256 numBPTTokens) internal virtual;\n\n function _lpWithdrawAll() internal virtual;\n\n /**\n * @notice Balancer returns assets and rateProviders for corresponding assets ordered\n * by numerical order.\n */\n function _getPoolAssets() internal view returns (IERC20[] memory assets) {\n // slither-disable-next-line unused-return\n (assets, , ) = balancerVault.getPoolTokens(balancerPoolId);\n }\n\n /**\n * @dev If an asset is rebasing the Balancer pools have a wrapped versions of assets\n * that the strategy supports. This function converts the pool(wrapped) asset\n * and corresponding amount to strategy asset.\n */\n function _toPoolAsset(address asset, uint256 amount)\n internal\n view\n returns (address poolAsset, uint256 poolAmount)\n {\n if (asset == stETH) {\n poolAsset = wstETH;\n if (amount > 0) {\n poolAmount = IWstETH(wstETH).getWstETHByStETH(amount);\n }\n } else if (asset == frxETH) {\n poolAsset = sfrxETH;\n if (amount > 0) {\n poolAmount = IERC4626(sfrxETH).convertToShares(amount);\n }\n } else {\n poolAsset = asset;\n poolAmount = amount;\n }\n }\n\n /**\n * @dev Converts a Vault collateral asset to a Balancer pool asset.\n * stETH becomes wstETH, frxETH becomes sfrxETH and everything else stays the same.\n * @param asset Address of the Vault collateral asset.\n * @return Address of the Balancer pool asset.\n */\n function _toPoolAsset(address asset) internal view returns (address) {\n if (asset == stETH) {\n return wstETH;\n } else if (asset == frxETH) {\n return sfrxETH;\n }\n return asset;\n }\n\n /**\n * @dev Converts rebasing asset to its wrapped counterpart.\n */\n function _wrapPoolAsset(address asset, uint256 amount)\n internal\n returns (address wrappedAsset, uint256 wrappedAmount)\n {\n if (asset == stETH) {\n wrappedAsset = wstETH;\n if (amount > 0) {\n wrappedAmount = IWstETH(wstETH).wrap(amount);\n }\n } else if (asset == frxETH) {\n wrappedAsset = sfrxETH;\n if (amount > 0) {\n wrappedAmount = IERC4626(sfrxETH).deposit(\n amount,\n address(this)\n );\n }\n } else {\n wrappedAsset = asset;\n wrappedAmount = amount;\n }\n }\n\n /**\n * @dev Converts wrapped asset to its rebasing counterpart.\n */\n function _unwrapPoolAsset(address asset, uint256 amount)\n internal\n returns (uint256 unwrappedAmount)\n {\n if (asset == stETH) {\n unwrappedAmount = IWstETH(wstETH).unwrap(amount);\n } else if (asset == frxETH) {\n unwrappedAmount = IERC4626(sfrxETH).withdraw(\n amount,\n address(this),\n address(this)\n );\n } else {\n unwrappedAmount = amount;\n }\n }\n\n /**\n * @dev If an asset is rebasing the Balancer pools have a wrapped versions of assets\n * that the strategy supports. This function converts the rebasing strategy asset\n * and corresponding amount to wrapped(pool) asset.\n */\n function _fromPoolAsset(address poolAsset, uint256 poolAmount)\n internal\n view\n returns (address asset, uint256 amount)\n {\n if (poolAsset == wstETH) {\n asset = stETH;\n if (poolAmount > 0) {\n amount = IWstETH(wstETH).getStETHByWstETH(poolAmount);\n }\n } else if (poolAsset == sfrxETH) {\n asset = frxETH;\n if (poolAmount > 0) {\n amount = IERC4626(sfrxETH).convertToAssets(poolAmount);\n }\n } else {\n asset = poolAsset;\n amount = poolAmount;\n }\n }\n\n function _fromPoolAsset(address poolAsset)\n internal\n view\n returns (address asset)\n {\n if (poolAsset == wstETH) {\n asset = stETH;\n } else if (poolAsset == sfrxETH) {\n asset = frxETH;\n } else {\n asset = poolAsset;\n }\n }\n\n /**\n * @notice Sets max withdrawal deviation that is considered when removing\n * liquidity from Balancer pools.\n * @param _maxWithdrawalDeviation Max withdrawal deviation denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxWithdrawalDeviation will be 1% (1e16) for production\n * usage. Vault value checker in combination with checkBalance will\n * catch any unexpected manipulation.\n */\n function setMaxWithdrawalDeviation(uint256 _maxWithdrawalDeviation)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(\n _maxWithdrawalDeviation <= 1e18,\n \"Withdrawal dev. out of bounds\"\n );\n emit MaxWithdrawalDeviationUpdated(\n maxWithdrawalDeviation,\n _maxWithdrawalDeviation\n );\n maxWithdrawalDeviation = _maxWithdrawalDeviation;\n }\n\n /**\n * @notice Sets max deposit deviation that is considered when adding\n * liquidity to Balancer pools.\n * @param _maxDepositDeviation Max deposit deviation denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxDepositDeviation will default to 1% (1e16)\n * for production usage. Vault value checker in combination with\n * checkBalance will catch any unexpected manipulation.\n */\n function setMaxDepositDeviation(uint256 _maxDepositDeviation)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(_maxDepositDeviation <= 1e18, \"Deposit dev. out of bounds\");\n emit MaxDepositDeviationUpdated(\n maxDepositDeviation,\n _maxDepositDeviation\n );\n maxDepositDeviation = _maxDepositDeviation;\n }\n\n function _approveBase() internal virtual {\n IERC20 pToken = IERC20(platformAddress);\n // Balancer vault for BPT token (required for removing liquidity)\n pToken.safeApprove(address(balancerVault), type(uint256).max);\n }\n\n function _getRateProviderRate(address _asset)\n internal\n view\n virtual\n returns (uint256);\n}\n" + }, + "contracts/strategies/balancer/BalancerMetaPoolStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Balancer MetaStablePool Strategy\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { AbstractAuraStrategy, AbstractBalancerStrategy } from \"./AbstractAuraStrategy.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\nimport { IRateProvider } from \"../../interfaces/balancer/IRateProvider.sol\";\nimport { IMetaStablePool } from \"../../interfaces/balancer/IMetaStablePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\ncontract BalancerMetaPoolStrategy is AbstractAuraStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(\n BaseStrategyConfig memory _stratConfig,\n BaseBalancerConfig memory _balancerConfig,\n address _auraRewardPoolAddress\n )\n InitializableAbstractStrategy(_stratConfig)\n AbstractBalancerStrategy(_balancerConfig)\n AbstractAuraStrategy(_auraRewardPoolAddress)\n {}\n\n /**\n * @notice There are no plans to configure BalancerMetaPool as a default\n * asset strategy. For that reason there is no need to support this\n * functionality.\n */\n function deposit(address, uint256)\n external\n override\n onlyVault\n nonReentrant\n {\n revert(\"Not supported\");\n }\n\n /**\n * @notice There are no plans to configure BalancerMetaPool as a default\n * asset strategy. For that reason there is no need to support this\n * functionality.\n */\n function deposit(address[] calldata, uint256[] calldata)\n external\n onlyVault\n nonReentrant\n {\n revert(\"Not supported\");\n }\n\n /**\n * @notice Deposits all supported assets in this strategy contract to the Balancer pool.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 assetsLength = assetsMapped.length;\n address[] memory strategyAssets = new address[](assetsLength);\n uint256[] memory strategyAmounts = new uint256[](assetsLength);\n\n // For each vault collateral asset\n for (uint256 i = 0; i < assetsLength; ++i) {\n strategyAssets[i] = assetsMapped[i];\n // Get the asset balance in this strategy contract\n strategyAmounts[i] = IERC20(strategyAssets[i]).balanceOf(\n address(this)\n );\n }\n _deposit(strategyAssets, strategyAmounts);\n }\n\n /*\n * _deposit doesn't require a read-only re-entrancy protection since during the deposit\n * the function enters the Balancer Vault Context. If this function were called as part of\n * the attacking contract (while intercepting execution flow upon receiving ETH) the read-only\n * protection of the Balancer Vault would be triggered. Since the attacking contract would\n * already be in the Balancer Vault context and wouldn't be able to enter it again.\n */\n function _deposit(\n address[] memory _strategyAssets,\n uint256[] memory _strategyAmounts\n ) internal {\n require(\n _strategyAssets.length == _strategyAmounts.length,\n \"Array length missmatch\"\n );\n\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n\n uint256[] memory strategyAssetAmountsToPoolAssetAmounts = new uint256[](\n _strategyAssets.length\n );\n address[] memory strategyAssetsToPoolAssets = new address[](\n _strategyAssets.length\n );\n\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n address strategyAsset = _strategyAssets[i];\n uint256 strategyAmount = _strategyAmounts[i];\n\n require(\n assetToPToken[strategyAsset] != address(0),\n \"Unsupported asset\"\n );\n strategyAssetsToPoolAssets[i] = _toPoolAsset(strategyAsset);\n\n if (strategyAmount > 0) {\n emit Deposit(strategyAsset, platformAddress, strategyAmount);\n\n // wrap rebasing assets like stETH and frxETH to wstETH and sfrxETH\n (, strategyAssetAmountsToPoolAssetAmounts[i]) = _wrapPoolAsset(\n strategyAsset,\n strategyAmount\n );\n }\n }\n\n uint256[] memory amountsIn = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n for (uint256 i = 0; i < tokens.length; ++i) {\n // Convert IERC20 type to address\n poolAssets[i] = address(tokens[i]);\n\n // For each of the mapped assets\n for (uint256 j = 0; j < strategyAssetsToPoolAssets.length; ++j) {\n // If the pool asset is the same as the mapped asset\n if (poolAssets[i] == strategyAssetsToPoolAssets[j]) {\n amountsIn[i] = strategyAssetAmountsToPoolAssetAmounts[j];\n }\n }\n }\n\n uint256 minBPT = _getBPTExpected(\n strategyAssetsToPoolAssets,\n strategyAssetAmountsToPoolAssetAmounts\n );\n uint256 minBPTwDeviation = minBPT.mulTruncate(\n 1e18 - maxDepositDeviation\n );\n\n /* EXACT_TOKENS_IN_FOR_BPT_OUT:\n * User sends precise quantities of tokens, and receives an\n * estimated but unknown (computed at run time) quantity of BPT.\n *\n * ['uint256', 'uint256[]', 'uint256']\n * [EXACT_TOKENS_IN_FOR_BPT_OUT, amountsIn, minimumBPT]\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,\n amountsIn,\n minBPTwDeviation\n );\n\n IBalancerVault.JoinPoolRequest memory request = IBalancerVault\n .JoinPoolRequest(poolAssets, amountsIn, userData, false);\n\n // Add the pool assets in this strategy to the balancer pool\n balancerVault.joinPool(\n balancerPoolId,\n address(this),\n address(this),\n request\n );\n\n // Deposit the Balancer Pool Tokens (BPT) into Aura\n _lpDepositAll();\n }\n\n /**\n * @notice Withdraw a Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAsset Address of the Vault collateral asset\n * @param _strategyAmount The amount of Vault collateral assets to withdraw\n */\n function withdraw(\n address _recipient,\n address _strategyAsset,\n uint256 _strategyAmount\n ) external override onlyVault nonReentrant {\n address[] memory strategyAssets = new address[](1);\n uint256[] memory strategyAmounts = new uint256[](1);\n strategyAssets[0] = _strategyAsset;\n strategyAmounts[0] = _strategyAmount;\n\n _withdraw(_recipient, strategyAssets, strategyAmounts);\n }\n\n /**\n * @notice Withdraw multiple Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAssets Addresses of the Vault collateral assets\n * @param _strategyAmounts The amounts of Vault collateral assets to withdraw\n */\n function withdraw(\n address _recipient,\n address[] calldata _strategyAssets,\n uint256[] calldata _strategyAmounts\n ) external onlyVault nonReentrant {\n _withdraw(_recipient, _strategyAssets, _strategyAmounts);\n }\n\n /**\n * @dev Withdraw multiple Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAssets Addresses of the Vault collateral assets\n * @param _strategyAmounts The amounts of Vault collateral assets to withdraw\n *\n * _withdrawal doesn't require a read-only re-entrancy protection since during the withdrawal\n * the function enters the Balancer Vault Context. If this function were called as part of\n * the attacking contract (while intercepting execution flow upon receiving ETH) the read-only\n * protection of the Balancer Vault would be triggered. Since the attacking contract would\n * already be in the Balancer Vault context and wouldn't be able to enter it again.\n */\n function _withdraw(\n address _recipient,\n address[] memory _strategyAssets,\n uint256[] memory _strategyAmounts\n ) internal {\n require(\n _strategyAssets.length == _strategyAmounts.length,\n \"Invalid input arrays\"\n );\n\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n require(\n assetToPToken[_strategyAssets[i]] != address(0),\n \"Unsupported asset\"\n );\n }\n\n // STEP 1 - Calculate the Balancer pool assets and amounts from the vault collateral assets\n\n // Get all the supported balancer pool assets\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n // Calculate the balancer pool assets and amounts to withdraw\n uint256[] memory poolAssetsAmountsOut = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n // Is the wrapped asset amount indexed by the assets array, not the order of the Balancer pool tokens\n // eg wstETH and sfrxETH amounts, not the stETH and frxETH amounts\n uint256[] memory strategyAssetsToPoolAssetsAmounts = new uint256[](\n _strategyAssets.length\n );\n\n // For each of the Balancer pool assets\n for (uint256 i = 0; i < tokens.length; ++i) {\n poolAssets[i] = address(tokens[i]);\n\n // Convert the Balancer pool asset back to a vault collateral asset\n address strategyAsset = _fromPoolAsset(poolAssets[i]);\n\n // for each of the vault assets\n for (uint256 j = 0; j < _strategyAssets.length; ++j) {\n // If the vault asset equals the vault asset mapped from the Balancer pool asset\n if (_strategyAssets[j] == strategyAsset) {\n (, poolAssetsAmountsOut[i]) = _toPoolAsset(\n strategyAsset,\n _strategyAmounts[j]\n );\n strategyAssetsToPoolAssetsAmounts[j] = poolAssetsAmountsOut[\n i\n ];\n\n /* Because of the potential Balancer rounding error mentioned below\n * the contract might receive 1-2 WEI smaller amount than required\n * in the withdraw user data encoding. If slightly lesser token amount\n * is received the strategy can not unwrap the pool asset as it is\n * smaller than expected.\n *\n * For that reason we `overshoot` the required tokens expected to\n * circumvent the error\n */\n if (poolAssetsAmountsOut[i] > 0) {\n poolAssetsAmountsOut[i] += 2;\n }\n }\n }\n }\n\n // STEP 2 - Calculate the max about of Balancer Pool Tokens (BPT) to withdraw\n\n // Estimate the required amount of Balancer Pool Tokens (BPT) for the assets\n uint256 maxBPTtoWithdraw = _getBPTExpected(\n poolAssets,\n /* all non 0 values are overshot by 2 WEI and with the expected mainnet\n * ~1% withdrawal deviation, the 2 WEI aren't important\n */\n poolAssetsAmountsOut\n );\n // Increase BPTs by the max allowed deviation\n // Any excess BPTs will be left in this strategy contract\n maxBPTtoWithdraw = maxBPTtoWithdraw.mulTruncate(\n 1e18 + maxWithdrawalDeviation\n );\n\n // STEP 3 - Withdraw the Balancer Pool Tokens (BPT) from Aura to this strategy contract\n\n // Withdraw BPT from Aura allowing for BPTs left in this strategy contract from previous withdrawals\n _lpWithdraw(\n maxBPTtoWithdraw - IERC20(platformAddress).balanceOf(address(this))\n );\n\n // STEP 4 - Withdraw the balancer pool assets from the pool\n\n /* Custom asset exit: BPT_IN_FOR_EXACT_TOKENS_OUT:\n * User sends an estimated but unknown (computed at run time) quantity of BPT,\n * and receives precise quantities of specified tokens.\n *\n * ['uint256', 'uint256[]', 'uint256']\n * [BPT_IN_FOR_EXACT_TOKENS_OUT, amountsOut, maxBPTAmountIn]\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.BPT_IN_FOR_EXACT_TOKENS_OUT,\n poolAssetsAmountsOut,\n maxBPTtoWithdraw\n );\n\n IBalancerVault.ExitPoolRequest memory request = IBalancerVault\n .ExitPoolRequest(\n poolAssets,\n /* We specify the exact amount of a tokens we are expecting in the encoded\n * userData, for that reason we don't need to specify the amountsOut here.\n *\n * Also Balancer has a rounding issue that can make a transaction fail:\n * https://github.com/balancer/balancer-v2-monorepo/issues/2541\n * which is an extra reason why this field is empty.\n */\n new uint256[](tokens.length),\n userData,\n false\n );\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n /* Payable keyword is required because of the IBalancerVault interface even though\n * this strategy shall never be receiving native ETH\n */\n payable(address(this)),\n request\n );\n\n // STEP 5 - Re-deposit any left over BPT tokens back into Aura\n /* When concluding how much of BPT we need to withdraw from Aura we overshoot by\n * roughly around 1% (initial mainnet setting of maxWithdrawalDeviation). After exiting\n * the pool strategy could have left over BPT tokens that are not earning boosted yield.\n * We re-deploy those back in.\n */\n _lpDepositAll();\n\n // STEP 6 - Unswap balancer pool assets to vault collateral assets and send to the vault.\n\n // For each of the specified assets\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n // Unwrap assets like wstETH and sfrxETH to rebasing assets stETH and frxETH\n if (strategyAssetsToPoolAssetsAmounts[i] > 0) {\n _unwrapPoolAsset(\n _strategyAssets[i],\n strategyAssetsToPoolAssetsAmounts[i]\n );\n }\n\n // Transfer the vault collateral assets to the recipient, which is typically the vault\n if (_strategyAmounts[i] > 0) {\n IERC20(_strategyAssets[i]).safeTransfer(\n _recipient,\n _strategyAmounts[i]\n );\n\n emit Withdrawal(\n _strategyAssets[i],\n platformAddress,\n _strategyAmounts[i]\n );\n }\n }\n }\n\n /**\n * @notice Withdraws all supported Vault collateral assets from the Balancer pool\n * and send to the OToken's Vault.\n *\n * Is only executable by the OToken's Vault or the Governor.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n // STEP 1 - Withdraw all Balancer Pool Tokens (BPT) from Aura to this strategy contract\n\n _lpWithdrawAll();\n // Get the BPTs withdrawn from Aura plus any that were already in this strategy contract\n uint256 BPTtoWithdraw = IERC20(platformAddress).balanceOf(\n address(this)\n );\n // Get the balancer pool assets and their total balances\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n uint256[] memory minAmountsOut = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n for (uint256 i = 0; i < tokens.length; ++i) {\n poolAssets[i] = address(tokens[i]);\n }\n\n // STEP 2 - Withdraw the Balancer pool assets from the pool\n /* Proportional exit: EXACT_BPT_IN_FOR_TOKENS_OUT:\n * User sends a precise quantity of BPT, and receives an estimated but unknown\n * (computed at run time) quantity of a single token\n *\n * ['uint256', 'uint256']\n * [EXACT_BPT_IN_FOR_TOKENS_OUT, bptAmountIn]\n *\n * It is ok to pass an empty minAmountsOut since tilting the pool in any direction\n * when doing a proportional exit can only be beneficial to the strategy. Since\n * it will receive more of the underlying tokens for the BPT traded in.\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.EXACT_BPT_IN_FOR_TOKENS_OUT,\n BPTtoWithdraw\n );\n\n IBalancerVault.ExitPoolRequest memory request = IBalancerVault\n .ExitPoolRequest(poolAssets, minAmountsOut, userData, false);\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n /* Payable keyword is required because of the IBalancerVault interface even though\n * this strategy shall never be receiving native ETH\n */\n payable(address(this)),\n request\n );\n\n // STEP 3 - Convert the balancer pool assets to the vault collateral assets and send to the vault\n // For each of the Balancer pool assets\n for (uint256 i = 0; i < tokens.length; ++i) {\n address poolAsset = address(tokens[i]);\n // Convert the balancer pool asset to the strategy asset\n address strategyAsset = _fromPoolAsset(poolAsset);\n // Get the balancer pool assets withdraw from the pool plus any that were already in this strategy contract\n uint256 poolAssetAmount = IERC20(poolAsset).balanceOf(\n address(this)\n );\n\n // Unwrap assets like wstETH and sfrxETH to rebasing assets stETH and frxETH\n uint256 unwrappedAmount = 0;\n if (poolAssetAmount > 0) {\n unwrappedAmount = _unwrapPoolAsset(\n strategyAsset,\n poolAssetAmount\n );\n }\n\n // Transfer the vault collateral assets to the vault\n if (unwrappedAmount > 0) {\n IERC20(strategyAsset).safeTransfer(\n vaultAddress,\n unwrappedAmount\n );\n emit Withdrawal(\n strategyAsset,\n platformAddress,\n unwrappedAmount\n );\n }\n }\n }\n\n /**\n * @notice Approves the Balancer Vault to transfer poolAsset counterparts\n * of all of the supported assets from this strategy. E.g. stETH is a supported\n * strategy and Balancer Vault gets unlimited approval to transfer wstETH.\n *\n * If Balancer pool uses a wrapped version of a supported asset then also approve\n * unlimited usage of an asset to the contract responsible for wrapping.\n *\n * Approve unlimited spending by Balancer Vault and Aura reward pool of the\n * pool BPT tokens.\n *\n * Is only executable by the Governor.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n _abstractSetPToken(assetsMapped[i], platformAddress);\n }\n _approveBase();\n }\n\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address) internal override {\n address poolAsset = _toPoolAsset(_asset);\n if (_asset == stETH) {\n // slither-disable-next-line unused-return\n IERC20(stETH).approve(wstETH, type(uint256).max);\n } else if (_asset == frxETH) {\n // slither-disable-next-line unused-return\n IERC20(frxETH).approve(sfrxETH, type(uint256).max);\n }\n _approveAsset(poolAsset);\n }\n\n /**\n * @dev Approves the Balancer Vault to transfer an asset from\n * this strategy. The assets could be a Vault collateral asset\n * like WETH or rETH; or a Balancer pool asset that wraps the vault asset\n * like wstETH or sfrxETH.\n */\n function _approveAsset(address _asset) internal {\n IERC20 asset = IERC20(_asset);\n // slither-disable-next-line unused-return\n asset.approve(address(balancerVault), type(uint256).max);\n }\n\n /**\n * @notice Returns the rate supplied by the Balancer configured rate\n * provider. Rate is used to normalize the token to common underlying\n * pool denominator. (ETH for ETH Liquid staking derivatives)\n *\n * @param _asset Address of the Balancer pool asset\n * @return rate of the corresponding asset\n */\n function _getRateProviderRate(address _asset)\n internal\n view\n override\n returns (uint256)\n {\n IMetaStablePool pool = IMetaStablePool(platformAddress);\n IRateProvider[] memory providers = pool.getRateProviders();\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n\n uint256 providersLength = providers.length;\n for (uint256 i = 0; i < providersLength; ++i) {\n // _assets and corresponding rate providers are all in the same order\n if (address(tokens[i]) == _asset) {\n // rate provider doesn't exist, defaults to 1e18\n if (address(providers[i]) == address(0)) {\n return 1e18;\n }\n return providers[i].getRate();\n }\n }\n\n // should never happen\n assert(false);\n }\n}\n" + }, + "contracts/strategies/balancer/VaultReentrancyLib.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity >=0.7.0 <0.9.0;\n\nimport \"../../utils/BalancerErrors.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\n\nlibrary VaultReentrancyLib {\n /**\n * @dev Ensure we are not in a Vault context when this function is called, by attempting a no-op internal\n * balance operation. If we are already in a Vault transaction (e.g., a swap, join, or exit), the Vault's\n * reentrancy protection will cause this function to revert.\n *\n * The exact function call doesn't really matter: we're just trying to trigger the Vault reentrancy check\n * (and not hurt anything in case it works). An empty operation array with no specific operation at all works\n * for that purpose, and is also the least expensive in terms of gas and bytecode size.\n *\n * Call this at the top of any function that can cause a state change in a pool and is either public itself,\n * or called by a public function *outside* a Vault operation (e.g., join, exit, or swap).\n *\n * If this is *not* called in functions that are vulnerable to the read-only reentrancy issue described\n * here (https://forum.balancer.fi/t/reentrancy-vulnerability-scope-expanded/4345), those functions are unsafe,\n * and subject to manipulation that may result in loss of funds.\n */\n function ensureNotInVaultContext(IBalancerVault vault) internal view {\n // Perform the following operation to trigger the Vault's reentrancy guard:\n //\n // IBalancerVault.UserBalanceOp[] memory noop = new IBalancerVault.UserBalanceOp[](0);\n // _vault.manageUserBalance(noop);\n //\n // However, use a static call so that it can be a view function (even though the function is non-view).\n // This allows the library to be used more widely, as some functions that need to be protected might be\n // view.\n //\n // This staticcall always reverts, but we need to make sure it doesn't fail due to a re-entrancy attack.\n // Staticcalls consume all gas forwarded to them on a revert caused by storage modification.\n // By default, almost the entire available gas is forwarded to the staticcall,\n // causing the entire call to revert with an 'out of gas' error.\n //\n // We set the gas limit to 10k for the staticcall to\n // avoid wasting gas when it reverts due to storage modification.\n // `manageUserBalance` is a non-reentrant function in the Vault, so calling it invokes `_enterNonReentrant`\n // in the `ReentrancyGuard` contract, reproduced here:\n //\n // function _enterNonReentrant() private {\n // // If the Vault is actually being reentered, it will revert in the first line, at the `_require` that\n // // checks the reentrancy flag, with \"BAL#400\" (corresponding to Errors.REENTRANCY) in the revertData.\n // // The full revertData will be: `abi.encodeWithSignature(\"Error(string)\", \"BAL#400\")`.\n // _require(_status != _ENTERED, Errors.REENTRANCY);\n //\n // // If the Vault is not being reentered, the check above will pass: but it will *still* revert,\n // // because the next line attempts to modify storage during a staticcall. However, this type of\n // // failure results in empty revertData.\n // _status = _ENTERED;\n // }\n //\n // So based on this analysis, there are only two possible revertData values: empty, or abi.encoded BAL#400.\n //\n // It is of course much more bytecode and gas efficient to check for zero-length revertData than to compare it\n // to the encoded REENTRANCY revertData.\n //\n // While it should be impossible for the call to fail in any other way (especially since it reverts before\n // `manageUserBalance` even gets called), any other error would generate non-zero revertData, so checking for\n // empty data guards against this case too.\n\n (, bytes memory revertData) = address(vault).staticcall{ gas: 10_000 }(\n abi.encodeWithSelector(vault.manageUserBalance.selector, 0)\n );\n\n _require(revertData.length == 0, Errors.REENTRANCY);\n }\n}\n" + }, + "contracts/strategies/BaseCurveAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for the Curve OETH/WETH pool\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { ICurveStableSwapNG } from \"../interfaces/ICurveStableSwapNG.sol\";\nimport { ICurveXChainLiquidityGauge } from \"../interfaces/ICurveXChainLiquidityGauge.sol\";\nimport { IChildLiquidityGaugeFactory } from \"../interfaces/IChildLiquidityGaugeFactory.sol\";\n\ncontract BaseCurveAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @dev a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n // New immutable variables that must be set in the constructor\n /**\n * @notice Address of the Wrapped ETH (WETH) contract.\n */\n IWETH9 public immutable weth;\n\n /**\n * @notice Address of the OETH token contract.\n */\n IERC20 public immutable oeth;\n\n /**\n * @notice Address of the LP (Liquidity Provider) token contract.\n */\n IERC20 public immutable lpToken;\n\n /**\n * @notice Address of the Curve StableSwap NG pool contract.\n */\n ICurveStableSwapNG public immutable curvePool;\n\n /**\n * @notice Address of the Curve X-Chain Liquidity Gauge contract.\n */\n ICurveXChainLiquidityGauge public immutable gauge;\n\n /**\n * @notice Address of the Child Liquidity Gauge Factory contract.\n */\n IChildLiquidityGaugeFactory public immutable gaugeFactory;\n\n // Ordered list of pool assets\n uint128 public immutable oethCoinIndex;\n uint128 public immutable wethCoinIndex;\n\n /**\n * @notice Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n uint256 public maxSlippage;\n\n event MaxSlippageUpdated(uint256 newMaxSlippage);\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balancesBefore = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffBefore = balancesBefore[wethCoinIndex].toInt256() -\n balancesBefore[oethCoinIndex].toInt256();\n\n _;\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balancesAfter = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffAfter = balancesAfter[wethCoinIndex].toInt256() -\n balancesAfter[oethCoinIndex].toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of ETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _oeth,\n address _weth,\n address _gauge,\n address _gaugeFactory,\n uint128 _oethCoinIndex,\n uint128 _wethCoinIndex\n ) InitializableAbstractStrategy(_baseConfig) {\n oethCoinIndex = _oethCoinIndex;\n wethCoinIndex = _wethCoinIndex;\n\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveStableSwapNG(_baseConfig.platformAddress);\n\n oeth = IERC20(_oeth);\n weth = IWETH9(_weth);\n gauge = ICurveXChainLiquidityGauge(_gauge);\n gaugeFactory = IChildLiquidityGaugeFactory(_gaugeFactory);\n\n _setGovernor(address(0));\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV\n * @param _maxSlippage Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV\n uint256 _maxSlippage\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n address[] memory _assets = new address[](1);\n _assets[0] = address(weth);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n _setMaxSlippage(_maxSlippage);\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit WETH into the Curve pool\n * @param _weth Address of Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to deposit.\n */\n function deposit(address _weth, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_weth, _amount);\n }\n\n function _deposit(address _weth, uint256 _wethAmount) internal {\n require(_wethAmount > 0, \"Must deposit something\");\n require(_weth == address(weth), \"Can only deposit WETH\");\n\n emit Deposit(_weth, address(lpToken), _wethAmount);\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 oethToAdd = uint256(\n _max(\n 0,\n balances[wethCoinIndex].toInt256() +\n _wethAmount.toInt256() -\n balances[oethCoinIndex].toInt256()\n )\n );\n\n /* Add so much OETH so that the pool ends up being balanced. And at minimum\n * add as much OETH as WETH and at maximum twice as much OETH.\n */\n oethToAdd = Math.max(oethToAdd, _wethAmount);\n oethToAdd = Math.min(oethToAdd, _wethAmount * 2);\n\n /* Mint OETH with a strategy that attempts to contribute to stability of OETH/WETH pool. Try\n * to mint so much OETH that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OETH minted will always be at least equal or greater\n * to WETH amount deployed. And never larger than twice the WETH amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(oethToAdd);\n\n emit Deposit(address(oeth), address(lpToken), oethToAdd);\n\n uint256[] memory _amounts = new uint256[](2);\n _amounts[wethCoinIndex] = _wethAmount;\n _amounts[oethCoinIndex] = oethToAdd;\n\n uint256 valueInLpTokens = (_wethAmount + oethToAdd).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Do the deposit to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(_amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool's LP tokens into the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n /**\n * @notice Deposit the strategy's entire balance of WETH into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = weth.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(weth), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _weth Address of the Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to withdraw.\n */\n function withdraw(\n address _recipient,\n address _weth,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_weth == address(weth), \"Can only withdraw WETH\");\n\n emit Withdrawal(_weth, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(_amount);\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough WETH on balanced removal\n */\n uint256[] memory _minWithdrawalAmounts = new uint256[](2);\n _minWithdrawalAmounts[wethCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OETH and any that was left in the strategy\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n\n // Transfer WETH to the recipient\n require(\n weth.transfer(_recipient, _amount),\n \"Transfer of WETH not successful\"\n );\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n function calcTokenToBurn(uint256 _wethAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much WETH\n * we want we can determine how much of OETH we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolWETHBalance = curvePool.balances(wethCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolWETHBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_wethAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = gauge.balanceOf(address(this));\n // Can not withdraw zero LP tokens from the gauge\n if (gaugeTokens == 0) return;\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[] memory minWithdrawAmounts = new uint256[](2);\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(\n lpToken.balanceOf(address(this)),\n minWithdrawAmounts\n );\n\n // Burn all OETH\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Get the strategy contract's WETH balance.\n // This includes all that was removed from the Curve pool and\n // any ether that was sitting in the strategy contract before the removal.\n uint256 ethBalance = weth.balanceOf(address(this));\n require(\n weth.transfer(vaultAddress, ethBalance),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethBalance);\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[] memory amounts = new uint256[](2);\n amounts[oethCoinIndex] = _oTokens;\n\n // Convert OETH to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool LP tokens to the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Deposit(address(oeth), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough ETH.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove OTokens from the Curve pool\n uint256 oethToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n oethCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /**\n * @notice One-sided remove of ETH from the Curve pool, convert to WETH\n * and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for ETH.\n * @dev Curve pool LP tokens is used rather than WETH assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of ETH. Curve's `calc_token_amount` functioun does not include fees.\n * A 3rd party libary can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * caclulate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Curve gauge and remove ETH from the Curve pool\n uint256 ethAmount = _withdrawAndRemoveFromPool(\n _lpTokens,\n wethCoinIndex\n );\n\n // Transfer WETH to the vault\n require(\n weth.transfer(vaultAddress, ethAmount),\n \"Transfer of WETH not successful\"\n );\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(weth), address(lpToken), ethAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the Convex pool and\n * do a one-sided remove of ETH or OETH from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the Convex pool.\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = ETH, 1 = OETH.\n * @return coinsRemoved The amount of ETH or OETH removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Curve gauge\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to ETH value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n // Apply slippage to ETH value\n uint256 minAmount = valueInEth.mulTruncate(uint256(1e18) - maxSlippage);\n\n // Remove just the ETH from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethbSupply = oeth.totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethbSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // CRV rewards flow.\n //---\n // CRV inflation:\n // Gauge receive CRV rewards from inflation.\n // Each checkpoint on the gauge send this CRV inflation to gauge factory.\n // This strategy should call mint on the gauge factory to collect the CRV rewards.\n // ---\n // Extra rewards:\n // Calling claim_rewards on the gauge will only claim extra rewards (outside of CRV).\n // ---\n\n // Mint CRV on Child Liquidity gauge factory\n gaugeFactory.mint(address(gauge));\n // Collect extra gauge rewards (outside of CRV)\n gauge.claim_rewards();\n\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _lpAmount) internal {\n // withdraw lp tokens from the gauge without claiming rewards\n gauge.withdraw(_lpAmount);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // WETH balance needed here for the balance check that happens from vault during depositing.\n balance = weth.balanceOf(address(this));\n uint256 lpTokens = gauge.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += (lpTokens * curvePool.get_virtual_price()) / 1e18;\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(weth);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Sets the maximum slippage allowed for any swap/liquidity operation\n * @param _maxSlippage Maximum slippage allowed, 1e18 = 100%.\n */\n function setMaxSlippage(uint256 _maxSlippage) external onlyGovernor {\n _setMaxSlippage(_maxSlippage);\n }\n\n function _setMaxSlippage(uint256 _maxSlippage) internal {\n require(_maxSlippage <= 5e16, \"Slippage must be less than 100%\");\n maxSlippage = _maxSlippage;\n emit MaxSlippageUpdated(_maxSlippage);\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OETH (required for adding liquidity)\n // slither-disable-next-line unused-return\n oeth.approve(platformAddress, type(uint256).max);\n\n // Approve Curve pool for WETH (required for adding liquidity)\n // slither-disable-next-line unused-return\n weth.approve(platformAddress, type(uint256).max);\n\n // Approve Curve gauge contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Curve gauge.\n // slither-disable-next-line unused-return\n lpToken.approve(address(gauge), type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/BridgedWOETHStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, SafeERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { AggregatorV3Interface } from \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\n\ncontract BridgedWOETHStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using StableMath for uint128;\n using SafeCast for uint256;\n using SafeERC20 for IERC20;\n\n event MaxPriceDiffBpsUpdated(uint128 oldValue, uint128 newValue);\n event WOETHPriceUpdated(uint128 oldValue, uint128 newValue);\n\n IWETH9 public immutable weth;\n IERC20 public immutable bridgedWOETH;\n IERC20 public immutable oethb;\n\n uint256 public constant MAX_PRICE_STALENESS = 2 days;\n\n uint128 public lastOraclePrice;\n uint128 public maxPriceDiffBps;\n\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _weth,\n address _bridgedWOETH,\n address _oethb\n ) InitializableAbstractStrategy(_stratConfig) {\n weth = IWETH9(_weth);\n bridgedWOETH = IERC20(_bridgedWOETH);\n oethb = IERC20(_oethb);\n }\n\n function initialize(uint128 _maxPriceDiffBps)\n external\n onlyGovernor\n initializer\n {\n InitializableAbstractStrategy._initialize(\n new address[](0), // No reward tokens\n new address[](0), // No assets\n new address[](0) // No pTokens\n );\n\n _setMaxPriceDiffBps(_maxPriceDiffBps);\n }\n\n /**\n * @dev Sets the max price diff bps for the wOETH value appreciation\n * @param _maxPriceDiffBps Bps value, 10k == 100%\n */\n function setMaxPriceDiffBps(uint128 _maxPriceDiffBps)\n external\n onlyGovernor\n {\n _setMaxPriceDiffBps(_maxPriceDiffBps);\n }\n\n /**\n * @dev Sets the max price diff bps for the wOETH value appreciation\n * @param _maxPriceDiffBps Bps value, 10k == 100%\n */\n function _setMaxPriceDiffBps(uint128 _maxPriceDiffBps) internal {\n require(\n _maxPriceDiffBps > 0 && _maxPriceDiffBps <= 10000,\n \"Invalid bps value\"\n );\n\n emit MaxPriceDiffBpsUpdated(maxPriceDiffBps, _maxPriceDiffBps);\n\n maxPriceDiffBps = _maxPriceDiffBps;\n }\n\n /**\n * @dev Wrapper for _updateWOETHOraclePrice with nonReentrant flag\n * @return The latest price of wOETH from Oracle\n */\n function updateWOETHOraclePrice() external nonReentrant returns (uint256) {\n return _updateWOETHOraclePrice();\n }\n\n /**\n * @dev Finds the value of bridged wOETH from the Oracle.\n * Ensures that it's within the bounds and reasonable.\n * And stores it.\n *\n * NOTE: Intentionally not caching `Vault.priceProvider` here,\n * since doing so would mean that we also have to update this\n * strategy every time there's a change in oracle router.\n * Besides on L2, the gas is considerably cheaper than mainnet.\n *\n * @return Latest price from oracle\n */\n function _updateWOETHOraclePrice() internal returns (uint256) {\n // WETH price per unit of bridged wOETH\n uint256 oraclePrice = IOracle(IVault(vaultAddress).priceProvider())\n .price(address(bridgedWOETH));\n\n // 1 wOETH > 1 WETH, always\n require(oraclePrice > 1 ether, \"Invalid wOETH value\");\n\n uint128 oraclePrice128 = oraclePrice.toUint128();\n\n // Do some checks\n if (lastOraclePrice > 0) {\n // Make sure the value only goes up\n require(oraclePrice128 >= lastOraclePrice, \"Negative wOETH yield\");\n\n // lastOraclePrice * (1 + maxPriceDiffBps)\n uint256 maxPrice = (lastOraclePrice * (1e4 + maxPriceDiffBps)) /\n 1e4;\n\n // And that it's within the bounds.\n require(oraclePrice128 <= maxPrice, \"Price diff beyond threshold\");\n }\n\n emit WOETHPriceUpdated(lastOraclePrice, oraclePrice128);\n\n // Store the price\n lastOraclePrice = oraclePrice128;\n\n return oraclePrice;\n }\n\n /**\n * @dev Computes & returns the value of given wOETH in WETH\n * @param woethAmount Amount of wOETH\n * @return Value of wOETH in WETH (using the last stored oracle price)\n */\n function getBridgedWOETHValue(uint256 woethAmount)\n public\n view\n returns (uint256)\n {\n return (woethAmount * lastOraclePrice) / 1 ether;\n }\n\n /**\n * @dev Takes in bridged wOETH and mints & returns\n * equivalent amount of OETHb.\n * @param woethAmount Amount of bridged wOETH to transfer in\n */\n function depositBridgedWOETH(uint256 woethAmount)\n external\n onlyGovernorOrStrategist\n nonReentrant\n {\n // Update wOETH price\n uint256 oraclePrice = _updateWOETHOraclePrice();\n\n // Figure out how much they are worth\n uint256 oethToMint = (woethAmount * oraclePrice) / 1 ether;\n\n require(oethToMint > 0, \"Invalid deposit amount\");\n\n // There's no pToken, however, it just uses WOETH address in the event\n emit Deposit(address(weth), address(bridgedWOETH), oethToMint);\n\n // Mint OETHb tokens and transfer it to the caller\n IVault(vaultAddress).mintForStrategy(oethToMint);\n\n // Transfer out minted OETHb\n // slither-disable-next-line unchecked-transfer unused-return\n oethb.transfer(msg.sender, oethToMint);\n\n // Transfer in all bridged wOETH tokens\n // slither-disable-next-line unchecked-transfer unused-return\n bridgedWOETH.transferFrom(msg.sender, address(this), woethAmount);\n }\n\n /**\n * @dev Takes in OETHb and burns it and returns\n * equivalent amount of bridged wOETH.\n * @param oethToBurn Amount of OETHb to burn\n */\n function withdrawBridgedWOETH(uint256 oethToBurn)\n external\n onlyGovernorOrStrategist\n nonReentrant\n {\n // Update wOETH price\n uint256 oraclePrice = _updateWOETHOraclePrice();\n\n // Figure out how much they are worth\n uint256 woethAmount = (oethToBurn * 1 ether) / oraclePrice;\n\n require(woethAmount > 0, \"Invalid withdraw amount\");\n\n // There's no pToken, however, it just uses WOETH address in the event\n emit Withdrawal(address(weth), address(bridgedWOETH), oethToBurn);\n\n // Transfer WOETH back\n // slither-disable-next-line unchecked-transfer unused-return\n bridgedWOETH.transfer(msg.sender, woethAmount);\n\n // Transfer in OETHb\n // slither-disable-next-line unchecked-transfer unused-return\n oethb.transferFrom(msg.sender, address(this), oethToBurn);\n\n // Burn OETHb\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n }\n\n /**\n * @notice Returns the amount of backing WETH the strategy holds\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // Figure out how much wOETH is worth at the time.\n // Always uses the last stored oracle price.\n // Call updateWOETHOraclePrice manually to pull in latest yields.\n\n // NOTE: If the contract has been deployed but the call to\n // `updateWOETHOraclePrice()` has never been made, then this\n // will return zero. It should be fine because the strategy\n // should update the price whenever a deposit/withdraw happens.\n\n // If `updateWOETHOraclePrice()` hasn't been called in a while,\n // the strategy will underreport its holdings but never overreport it.\n\n balance =\n (bridgedWOETH.balanceOf(address(this)) * lastOraclePrice) /\n 1 ether;\n }\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n // Strategist deposits bridged wOETH but the contract only\n // reports the balance in WETH. As far as Vault is concerned,\n // it isn't aware of bridged wOETH token\n return _asset == address(weth);\n }\n\n /***************************************\n Overridden methods\n ****************************************/\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function transferToken(address _asset, uint256 _amount)\n public\n override\n onlyGovernor\n {\n require(\n _asset != address(bridgedWOETH) && _asset != address(weth),\n \"Cannot transfer supported asset\"\n );\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice deposit() function not used for this strategy\n */\n function deposit(address, uint256)\n external\n override\n onlyVault\n nonReentrant\n {\n // Use depositBridgedWOETH() instead\n require(false, \"Deposit disabled\");\n }\n\n /**\n * @notice depositAll() function not used for this strategy\n */\n function depositAll() external override onlyVault nonReentrant {\n // Use depositBridgedWOETH() instead\n require(false, \"Deposit disabled\");\n }\n\n /**\n * @notice withdraw() function not used for this strategy\n */\n function withdraw(\n // solhint-disable-next-line no-unused-vars\n address _recipient,\n // solhint-disable-next-line no-unused-vars\n address _asset,\n // solhint-disable-next-line no-unused-vars\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(false, \"Withdrawal disabled\");\n }\n\n /**\n * @notice withdrawAll() function not used for this strategy\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n // Withdrawal disabled\n }\n\n function _abstractSetPToken(address, address) internal override {\n revert(\"No pTokens are used\");\n }\n\n function safeApproveAllTokens() external override {}\n\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function removePToken(uint256) external override {\n revert(\"No pTokens are used\");\n }\n\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function collectRewardTokens() external override {}\n}\n" + }, + "contracts/strategies/CompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Compound Strategy\n * @notice Investment strategy for Compound like lending platforms. eg Compound and Flux\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICERC20 } from \"./ICompound.sol\";\nimport { AbstractCompoundStrategy, InitializableAbstractStrategy } from \"./AbstractCompoundStrategy.sol\";\nimport { IComptroller } from \"../interfaces/IComptroller.sol\";\nimport { IERC20 } from \"../utils/InitializableAbstractStrategy.sol\";\n\ncontract CompoundStrategy is AbstractCompoundStrategy {\n using SafeERC20 for IERC20;\n event SkippedWithdrawal(address asset, uint256 amount);\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @notice initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @notice Collect accumulated COMP and send to Harvester.\n */\n function collectRewardTokens()\n external\n virtual\n override\n onlyHarvester\n nonReentrant\n {\n // Claim COMP from Comptroller\n ICERC20 cToken = _getCTokenFor(assetsMapped[0]);\n IComptroller comptroller = IComptroller(cToken.comptroller());\n // Only collect from active cTokens, saves gas\n address[] memory ctokensToCollect = new address[](assetsMapped.length);\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n ctokensToCollect[i] = address(_getCTokenFor(assetsMapped[i]));\n }\n // Claim only for this strategy\n address[] memory claimers = new address[](1);\n claimers[0] = address(this);\n // Claim COMP from Comptroller. Only collect for supply, saves gas\n comptroller.claimComp(claimers, ctokensToCollect, false, true);\n // Transfer COMP to Harvester\n IERC20 rewardToken = IERC20(rewardTokenAddresses[0]);\n uint256 balance = rewardToken.balanceOf(address(this));\n emit RewardTokenCollected(\n harvesterAddress,\n rewardTokenAddresses[0],\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n\n /**\n * @notice Deposit asset into the underlying platform\n * @param _asset Address of asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit an asset into the underlying platform\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n ICERC20 cToken = _getCTokenFor(_asset);\n emit Deposit(_asset, address(cToken), _amount);\n require(cToken.mint(_amount) == 0, \"cToken mint failed\");\n }\n\n /**\n * @notice Deposit the entire balance of any supported asset in the strategy into the underlying platform\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n uint256 assetBalance = asset.balanceOf(address(this));\n if (assetBalance > 0) {\n _deposit(address(asset), assetBalance);\n }\n }\n }\n\n /**\n * @notice Withdraw an asset from the underlying platform\n * @param _recipient Address to receive withdrawn assets\n * @param _asset Address of the asset to withdraw\n * @param _amount Amount of assets to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n ICERC20 cToken = _getCTokenFor(_asset);\n // If redeeming 0 cTokens, just skip, else COMP will revert\n uint256 cTokensToRedeem = _convertUnderlyingToCToken(cToken, _amount);\n if (cTokensToRedeem == 0) {\n emit SkippedWithdrawal(_asset, _amount);\n return;\n }\n\n emit Withdrawal(_asset, address(cToken), _amount);\n require(cToken.redeemUnderlying(_amount) == 0, \"Redeem failed\");\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / cTokens\n * We need to approve the cToken and give it permission to spend the asset\n * @param _asset Address of the asset to approve. eg DAI\n * @param _pToken The pToken for the approval. eg cDAI or fDAI\n */\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n // Safe approval\n IERC20(_asset).safeApprove(_pToken, 0);\n IERC20(_asset).safeApprove(_pToken, type(uint256).max);\n }\n\n /**\n * @notice Remove all supported assets from the underlying platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n // Redeem entire balance of cToken\n ICERC20 cToken = _getCTokenFor(address(asset));\n uint256 cTokenBalance = cToken.balanceOf(address(this));\n if (cTokenBalance > 0) {\n require(cToken.redeem(cTokenBalance) == 0, \"Redeem failed\");\n uint256 assetBalance = asset.balanceOf(address(this));\n // Transfer entire balance to Vault\n asset.safeTransfer(vaultAddress, assetBalance);\n\n emit Withdrawal(address(asset), address(cToken), assetBalance);\n }\n }\n }\n\n /**\n * @notice Get the total asset value held in the underlying platform\n * This includes any interest that was generated since depositing.\n * The exchange rate between the cToken and asset gradually increases,\n * causing the cToken to be worth more corresponding asset.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n // Balance is always with token cToken decimals\n ICERC20 cToken = _getCTokenFor(_asset);\n balance = _checkBalance(cToken);\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * underlying = (cTokenAmt * exchangeRate) / 1e18\n * @param _cToken cToken for which to check balance\n * @return balance Total value of the asset in the platform\n */\n function _checkBalance(ICERC20 _cToken)\n internal\n view\n returns (uint256 balance)\n {\n // e.g. 50e8*205316390724364402565641705 / 1e18 = 1.0265..e18\n balance =\n (_cToken.balanceOf(address(this)) * _cToken.exchangeRateStored()) /\n 1e18;\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding cToken,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens() external override {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n address cToken = assetToPToken[address(asset)];\n // Safe approval\n asset.safeApprove(cToken, 0);\n asset.safeApprove(cToken, type(uint256).max);\n }\n }\n}\n" + }, + "contracts/strategies/ConvexEthMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Convex Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for the Curve OETH/ETH pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { ICurveETHPoolV1 } from \"./ICurveETHPoolV1.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\n\ncontract ConvexEthMetaStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 public constant MAX_SLIPPAGE = 1e16; // 1%, same as the Curve UI\n address public constant ETH_ADDRESS =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n // The following slots have been deprecated with immutable variables\n // slither-disable-next-line constable-states\n address private _deprecated_cvxDepositorAddress;\n // slither-disable-next-line constable-states\n address private _deprecated_cvxRewardStaker;\n // slither-disable-next-line constable-states\n uint256 private _deprecated_cvxDepositorPTokenId;\n // slither-disable-next-line constable-states\n address private _deprecated_curvePool;\n // slither-disable-next-line constable-states\n address private _deprecated_lpToken;\n // slither-disable-next-line constable-states\n address private _deprecated_oeth;\n // slither-disable-next-line constable-states\n address private _deprecated_weth;\n\n // Ordered list of pool assets\n // slither-disable-next-line constable-states\n uint128 private _deprecated_oethCoinIndex;\n // slither-disable-next-line constable-states\n uint128 private _deprecated_ethCoinIndex;\n\n // New immutable variables that must be set in the constructor\n address public immutable cvxDepositorAddress;\n IRewardStaking public immutable cvxRewardStaker;\n uint256 public immutable cvxDepositorPTokenId;\n ICurveETHPoolV1 public immutable curvePool;\n IERC20 public immutable lpToken;\n IERC20 public immutable oeth;\n IWETH9 public immutable weth;\n\n // Ordered list of pool assets\n uint128 public constant oethCoinIndex = 1;\n uint128 public constant ethCoinIndex = 0;\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier only works on functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balancesBefore = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffBefore = int256(balancesBefore[ethCoinIndex]) -\n int256(balancesBefore[oethCoinIndex]);\n\n _;\n\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balancesAfter = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffAfter = int256(balancesAfter[ethCoinIndex]) -\n int256(balancesAfter[oethCoinIndex]);\n\n if (diffBefore <= 0) {\n // If the pool was originally imbalanced in favor of OETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n }\n if (diffBefore >= 0) {\n // If the pool was originally imbalanced in favor of ETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n // Used to circumvent the stack too deep issue\n struct ConvexEthMetaConfig {\n address cvxDepositorAddress; //Address of the Convex depositor(AKA booster) for this pool\n address cvxRewardStakerAddress; //Address of the CVX rewards staker\n uint256 cvxDepositorPTokenId; //Pid of the pool referred to by Depositor and staker\n address oethAddress; //Address of OETH token\n address wethAddress; //Address of WETH\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n ConvexEthMetaConfig memory _convexConfig\n ) InitializableAbstractStrategy(_baseConfig) {\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveETHPoolV1(_baseConfig.platformAddress);\n\n cvxDepositorAddress = _convexConfig.cvxDepositorAddress;\n cvxRewardStaker = IRewardStaking(_convexConfig.cvxRewardStakerAddress);\n cvxDepositorPTokenId = _convexConfig.cvxDepositorPTokenId;\n oeth = IERC20(_convexConfig.oethAddress);\n weth = IWETH9(_convexConfig.wethAddress);\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. eg WETH\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets // WETH\n ) external onlyGovernor initializer {\n require(_assets.length == 1, \"Must have exactly one asset\");\n require(_assets[0] == address(weth), \"Asset not WETH\");\n\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit WETH into the Curve pool\n * @param _weth Address of Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to deposit.\n */\n function deposit(address _weth, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_weth, _amount);\n }\n\n function _deposit(address _weth, uint256 _wethAmount) internal {\n require(_wethAmount > 0, \"Must deposit something\");\n require(_weth == address(weth), \"Can only deposit WETH\");\n weth.withdraw(_wethAmount);\n\n emit Deposit(_weth, address(lpToken), _wethAmount);\n\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 oethToAdd = uint256(\n _max(\n 0,\n int256(balances[ethCoinIndex]) +\n int256(_wethAmount) -\n int256(balances[oethCoinIndex])\n )\n );\n\n /* Add so much OETH so that the pool ends up being balanced. And at minimum\n * add as much OETH as WETH and at maximum twice as much OETH.\n */\n oethToAdd = Math.max(oethToAdd, _wethAmount);\n oethToAdd = Math.min(oethToAdd, _wethAmount * 2);\n\n /* Mint OETH with a strategy that attempts to contribute to stability of OETH/WETH pool. Try\n * to mint so much OETH that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OETH minted will always be at least equal or greater\n * to WETH amount deployed. And never larger than twice the WETH amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(oethToAdd);\n\n emit Deposit(address(oeth), address(lpToken), oethToAdd);\n\n uint256[2] memory _amounts;\n _amounts[ethCoinIndex] = _wethAmount;\n _amounts[oethCoinIndex] = oethToAdd;\n\n uint256 valueInLpTokens = (_wethAmount + oethToAdd).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Do the deposit to the Curve pool\n // slither-disable-next-line arbitrary-send\n uint256 lpDeposited = curvePool.add_liquidity{ value: _wethAmount }(\n _amounts,\n minMintAmount\n );\n\n // Deposit the Curve pool's LP tokens into the Convex rewards pool\n require(\n IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n lpDeposited,\n true // Deposit with staking\n ),\n \"Depositing LP to Convex not successful\"\n );\n }\n\n /**\n * @notice Deposit the strategy's entire balance of WETH into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = weth.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(weth), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _weth Address of the Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to withdraw.\n */\n function withdraw(\n address _recipient,\n address _weth,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Invalid amount\");\n require(_weth == address(weth), \"Can only withdraw WETH\");\n\n emit Withdrawal(_weth, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(_amount);\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough WETH on balanced removal\n */\n uint256[2] memory _minWithdrawalAmounts = [uint256(0), uint256(0)];\n _minWithdrawalAmounts[ethCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OETH and any that was left in the strategy\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n\n // Transfer WETH to the recipient\n weth.deposit{ value: _amount }();\n require(\n weth.transfer(_recipient, _amount),\n \"Transfer of WETH not successful\"\n );\n }\n\n function calcTokenToBurn(uint256 _wethAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much WETH\n * we want we can determine how much of OETH we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolWETHBalance = curvePool.balances(ethCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolWETHBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_wethAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = cvxRewardStaker.balanceOf(address(this));\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[2] memory minWithdrawAmounts = [uint256(0), uint256(0)];\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(\n lpToken.balanceOf(address(this)),\n minWithdrawAmounts\n );\n\n // Burn all OETH\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Get the strategy contract's ether balance.\n // This includes all that was removed from the Curve pool and\n // any ether that was sitting in the strategy contract before the removal.\n uint256 ethBalance = address(this).balance;\n // Convert all the strategy contract's ether to WETH and transfer to the vault.\n weth.deposit{ value: ethBalance }();\n require(\n weth.transfer(vaultAddress, ethBalance),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethBalance);\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[2] memory amounts = [uint256(0), uint256(0)];\n amounts[oethCoinIndex] = _oTokens;\n\n // Convert OETH to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n\n // Deposit the Curve pool LP tokens to the Convex rewards pool\n require(\n IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n lpDeposited,\n true // Deposit with staking\n ),\n \"Failed to Deposit LP to Convex\"\n );\n\n emit Deposit(address(oeth), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough ETH.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove OTokens from the Curve pool\n uint256 oethToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n oethCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /**\n * @notice One-sided remove of ETH from the Curve pool, convert to WETH\n * and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for ETH.\n * @dev Curve pool LP tokens is used rather than WETH assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of ETH. Curve's `calc_token_amount` functioun does not include fees.\n * A 3rd party libary can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * caclulate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove ETH from the Curve pool\n uint256 ethAmount = _withdrawAndRemoveFromPool(_lpTokens, ethCoinIndex);\n\n // Convert ETH to WETH and transfer to the vault\n weth.deposit{ value: ethAmount }();\n require(\n weth.transfer(vaultAddress, ethAmount),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the Convex pool and\n * do a one-sided remove of ETH or OETH from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the Convex pool.\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = ETH, 1 = OETH.\n * @return coinsRemoved The amount of ETH or OETH removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Convex pool\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to ETH value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n // Apply slippage to ETH value\n uint256 minAmount = valueInEth.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Remove just the ETH from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV and CVX rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n cvxRewardStaker.getReward();\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _wethAmount) internal {\n // withdraw and unwrap with claim takes back the lpTokens\n // and also collects the rewards for deposit\n cvxRewardStaker.withdrawAndUnwrap(_wethAmount, true);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // Eth balance needed here for the balance check that happens from vault during depositing.\n balance = address(this).balance;\n uint256 lpTokens = cvxRewardStaker.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += (lpTokens * curvePool.get_virtual_price()) / 1e18;\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(weth);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @notice Accept unwrapped WETH\n */\n receive() external payable {}\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OETH (required for adding liquidity)\n // No approval is needed for ETH\n // slither-disable-next-line unused-return\n oeth.approve(platformAddress, type(uint256).max);\n\n // Approve Convex deposit contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Convex rewards pool\n // slither-disable-next-line unused-return\n lpToken.approve(cvxDepositorAddress, type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/ConvexGeneralizedMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { AbstractConvexMetaStrategy } from \"./AbstractConvexMetaStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract ConvexGeneralizedMetaStrategy is AbstractConvexMetaStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /* Take 3pool LP and deposit it to metapool. Take the LP from metapool\n * and deposit them to Convex.\n */\n function _lpDepositAll() internal override {\n IERC20 threePoolLp = IERC20(pTokenAddress);\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 threePoolLpBalance = threePoolLp.balanceOf(address(this));\n uint256 curve3PoolVirtualPrice = curvePool.get_virtual_price();\n uint256 threePoolLpDollarValue = threePoolLpBalance.mulTruncate(\n curve3PoolVirtualPrice\n );\n\n uint256[2] memory _amounts = [0, threePoolLpBalance];\n\n uint256 metapoolVirtualPrice = metapool.get_virtual_price();\n /**\n * First convert all the deposited tokens to dollar values,\n * then divide by virtual price to convert to metapool LP tokens\n * and apply the max slippage\n */\n uint256 minReceived = threePoolLpDollarValue\n .divPrecisely(metapoolVirtualPrice)\n .mulTruncate(uint256(1e18) - MAX_SLIPPAGE);\n\n uint256 metapoolLp = metapool.add_liquidity(_amounts, minReceived);\n\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n metapoolLp,\n true // Deposit with staking\n );\n\n require(success, \"Failed to deposit to Convex\");\n }\n\n /**\n * Withdraw the specified amount of tokens from the gauge. And use all the resulting tokens\n * to remove liquidity from metapool\n * @param num3CrvTokens Number of Convex 3pool LP tokens to withdraw from metapool\n */\n function _lpWithdraw(uint256 num3CrvTokens) internal override {\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n uint256 requiredMetapoolLpTokens = _calcCurveMetaTokenAmount(\n crvCoinIndex,\n num3CrvTokens\n );\n\n require(\n requiredMetapoolLpTokens <= gaugeTokens,\n string(\n bytes.concat(\n bytes(\"Attempting to withdraw \"),\n bytes(Strings.toString(requiredMetapoolLpTokens)),\n bytes(\", metapoolLP but only \"),\n bytes(Strings.toString(gaugeTokens)),\n bytes(\" available.\")\n )\n )\n );\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards for deposit\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n requiredMetapoolLpTokens,\n true\n );\n\n if (requiredMetapoolLpTokens > 0) {\n // slither-disable-next-line unused-return\n metapool.remove_liquidity_one_coin(\n requiredMetapoolLpTokens,\n int128(crvCoinIndex),\n num3CrvTokens\n );\n }\n }\n\n function _lpWithdrawAll() internal override {\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n gaugeTokens,\n true\n );\n\n if (gaugeTokens > 0) {\n uint256 burnDollarAmount = gaugeTokens.mulTruncate(\n metapool.get_virtual_price()\n );\n uint256 curve3PoolExpected = burnDollarAmount.divPrecisely(\n ICurvePool(platformAddress).get_virtual_price()\n );\n\n // Always withdraw all of the available metapool LP tokens (similar to how we always deposit all)\n // slither-disable-next-line unused-return\n metapool.remove_liquidity_one_coin(\n gaugeTokens,\n int128(crvCoinIndex),\n curve3PoolExpected -\n curve3PoolExpected.mulTruncate(maxWithdrawalSlippage)\n );\n }\n }\n}\n" + }, + "contracts/strategies/ConvexOUSDMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { AbstractConvexMetaStrategy } from \"./AbstractConvexMetaStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract ConvexOUSDMetaStrategy is AbstractConvexMetaStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /* Take 3pool LP and mint the corresponding amount of ousd. Deposit and stake that to\n * ousd Curve Metapool. Take the LP from metapool and deposit them to Convex.\n */\n function _lpDepositAll() internal override {\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 threePoolLpBalance = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n uint256 curve3PoolVirtualPrice = curvePool.get_virtual_price();\n uint256 threePoolLpDollarValue = threePoolLpBalance.mulTruncate(\n curve3PoolVirtualPrice\n );\n\n // safe to cast since min value is at least 0\n uint256 ousdToAdd = uint256(\n _max(\n 0,\n int256(\n metapool.balances(crvCoinIndex).mulTruncate(\n curve3PoolVirtualPrice\n )\n ) -\n int256(metapool.balances(mainCoinIndex)) +\n int256(threePoolLpDollarValue)\n )\n );\n\n /* Add so much OUSD so that the pool ends up being balanced. And at minimum\n * add twice as much OUSD as 3poolLP and at maximum at twice as\n * much OUSD.\n */\n ousdToAdd = Math.max(ousdToAdd, threePoolLpDollarValue);\n ousdToAdd = Math.min(ousdToAdd, threePoolLpDollarValue * 2);\n\n /* Mint OUSD with a strategy that attempts to contribute to stability of OUSD metapool. Try\n * to mint so much OUSD that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OUSD minted will always be at least equal or greater\n * to stablecoin(DAI, USDC, USDT) amount of 3CRVLP deployed. And never larger than twice the\n * stablecoin amount of 3CRVLP deployed even if it would have a further beneficial effect\n * on pool stability.\n */\n if (ousdToAdd > 0) {\n IVault(vaultAddress).mintForStrategy(ousdToAdd);\n }\n\n uint256[2] memory _amounts = [ousdToAdd, threePoolLpBalance];\n\n uint256 metapoolVirtualPrice = metapool.get_virtual_price();\n /**\n * First convert all the deposited tokens to dollar values,\n * then divide by virtual price to convert to metapool LP tokens\n * and apply the max slippage\n */\n uint256 minReceived = (ousdToAdd + threePoolLpDollarValue)\n .divPrecisely(metapoolVirtualPrice)\n .mulTruncate(uint256(1e18) - MAX_SLIPPAGE);\n\n uint256 metapoolLp = metapool.add_liquidity(_amounts, minReceived);\n\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n metapoolLp,\n true // Deposit with staking\n );\n\n require(success, \"Failed to deposit to Convex\");\n }\n\n /**\n * Withdraw the specified amount of tokens from the gauge. And use all the resulting tokens\n * to remove liquidity from metapool\n * @param num3CrvTokens Number of 3CRV tokens to withdraw from metapool\n */\n function _lpWithdraw(uint256 num3CrvTokens) internal override {\n ICurvePool curvePool = ICurvePool(platformAddress);\n /* The rate between coins in the metapool determines the rate at which metapool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much 3crvLp\n * we want we can determine how much of OUSD we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 crvPoolBalance = metapool.balances(crvCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * metapool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * metapoolLPToken.totalSupply()) / crvPoolBalance;\n // simplifying below to: `uint256 diff = (num3CrvTokens - 1) * k` causes loss of precision\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = crvPoolBalance * k -\n (crvPoolBalance - num3CrvTokens - 1) * k;\n uint256 lpToBurn = diff / 1e36;\n\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n require(\n lpToBurn <= gaugeTokens,\n string(\n bytes.concat(\n bytes(\"Attempting to withdraw \"),\n bytes(Strings.toString(lpToBurn)),\n bytes(\", metapoolLP but only \"),\n bytes(Strings.toString(gaugeTokens)),\n bytes(\" available.\")\n )\n )\n );\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards for deposit\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n lpToBurn,\n true\n );\n\n // calculate the min amount of OUSD expected for the specified amount of LP tokens\n uint256 minOUSDAmount = lpToBurn.mulTruncate(\n metapool.get_virtual_price()\n ) -\n num3CrvTokens.mulTruncate(curvePool.get_virtual_price()) -\n 1;\n\n // withdraw the liquidity from metapool\n uint256[2] memory _removedAmounts = metapool.remove_liquidity(\n lpToBurn,\n [minOUSDAmount, num3CrvTokens]\n );\n\n IVault(vaultAddress).burnForStrategy(_removedAmounts[mainCoinIndex]);\n }\n\n function _lpWithdrawAll() internal override {\n IERC20 metapoolErc20 = IERC20(address(metapool));\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n gaugeTokens,\n true\n );\n\n uint256[2] memory _minAmounts = [uint256(0), uint256(0)];\n uint256[2] memory _removedAmounts = metapool.remove_liquidity(\n metapoolErc20.balanceOf(address(this)),\n _minAmounts\n );\n\n IVault(vaultAddress).burnForStrategy(_removedAmounts[mainCoinIndex]);\n }\n}\n" + }, + "contracts/strategies/ConvexStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { IERC20, AbstractCurveStrategy, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\n/*\n * IMPORTANT(!) If ConvexStrategy needs to be re-deployed, it requires new\n * proxy contract with fresh storage slots. Changes in `AbstractCurveStrategy`\n * storage slots would break existing implementation.\n *\n * Remove this notice if ConvexStrategy is re-deployed\n */\ncontract ConvexStrategy is AbstractCurveStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n address internal cvxDepositorAddress;\n address internal cvxRewardStakerAddress;\n // slither-disable-next-line constable-states\n address private _deprecated_cvxRewardTokenAddress;\n uint256 internal cvxDepositorPTokenId;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * DAI, USDC, USDT\n * @param _pTokens Platform Token corresponding addresses\n * @param _cvxDepositorAddress Address of the Convex depositor(AKA booster) for this pool\n * @param _cvxRewardStakerAddress Address of the CVX rewards staker\n * @param _cvxDepositorPTokenId Pid of the pool referred to by Depositor and staker\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets,\n address[] calldata _pTokens,\n address _cvxDepositorAddress,\n address _cvxRewardStakerAddress,\n uint256 _cvxDepositorPTokenId\n ) external onlyGovernor initializer {\n require(_assets.length == 3, \"Must have exactly three assets\");\n // Should be set prior to abstract initialize call otherwise\n // abstractSetPToken calls will fail\n cvxDepositorAddress = _cvxDepositorAddress;\n cvxRewardStakerAddress = _cvxRewardStakerAddress;\n cvxDepositorPTokenId = _cvxDepositorPTokenId;\n pTokenAddress = _pTokens[0];\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n function _lpDepositAll() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // Deposit with staking\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n pToken.balanceOf(address(this)),\n true\n );\n require(success, \"Failed to deposit to Convex\");\n }\n\n function _lpWithdraw(uint256 numCrvTokens) internal override {\n uint256 gaugePTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n // Not enough in this contract or in the Gauge, can't proceed\n require(numCrvTokens > gaugePTokens, \"Insufficient 3CRV balance\");\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards to this\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n numCrvTokens,\n true\n );\n }\n\n function _lpWithdrawAll() internal override {\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards to this\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n IRewardStaking(cvxRewardStakerAddress).balanceOf(address(this)),\n true\n );\n }\n\n function _approveBase() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // 3Pool for LP token (required for removing liquidity)\n pToken.safeApprove(platformAddress, 0);\n pToken.safeApprove(platformAddress, type(uint256).max);\n // Gauge for LP token\n pToken.safeApprove(cvxDepositorAddress, 0);\n pToken.safeApprove(cvxDepositorAddress, type(uint256).max);\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 contractPTokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n uint256 gaugePTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n uint256 totalPTokens = contractPTokens + gaugePTokens;\n\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (totalPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = (totalPTokens * virtual_price) / 1e18;\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = value.scaleBy(assetDecimals, 18) / 3;\n }\n }\n\n /**\n * @dev Collect accumulated CRV and CVX and send to Vault.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n IRewardStaking(cvxRewardStakerAddress).getReward();\n _collectRewardTokens();\n }\n}\n" + }, + "contracts/strategies/CurveAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for a Curve pool using an OToken.\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { ICurveStableSwapNG } from \"../interfaces/ICurveStableSwapNG.sol\";\nimport { ICurveLiquidityGaugeV6 } from \"../interfaces/ICurveLiquidityGaugeV6.sol\";\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\nimport { ICurveMinter } from \"../interfaces/ICurveMinter.sol\";\n\ncontract CurveAMOStrategy is InitializableAbstractStrategy {\n using SafeCast for uint256;\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n /**\n * @dev a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n // New immutable variables that must be set in the constructor\n /**\n * @notice Address of the hard asset (weth, usdt, usdc).\n */\n IERC20 public immutable hardAsset;\n\n /**\n * @notice Address of the OTOKEN token contract.\n */\n IERC20 public immutable oToken;\n\n /**\n * @notice Address of the LP (Liquidity Provider) token contract.\n */\n IERC20 public immutable lpToken;\n\n /**\n * @notice Address of the Curve StableSwap NG pool contract.\n */\n ICurveStableSwapNG public immutable curvePool;\n\n /**\n * @notice Address of the Curve X-Chain Liquidity Gauge contract.\n */\n ICurveLiquidityGaugeV6 public immutable gauge;\n\n /**\n * @notice Address of the Curve Minter contract.\n */\n ICurveMinter public immutable minter;\n\n /**\n * @notice Index of the OTOKEN and hardAsset in the Curve pool.\n */\n uint128 public immutable otokenCoinIndex;\n uint128 public immutable hardAssetCoinIndex;\n\n /**\n * @notice Decimals of the OTOKEN and hardAsset.\n */\n uint8 public immutable decimalsOToken;\n uint8 public immutable decimalsHardAsset;\n\n /**\n * @notice Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n uint256 public maxSlippage;\n\n event MaxSlippageUpdated(uint256 newMaxSlippage);\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the hard asset and OToken balances in the Curve pool\n uint256[] memory balancesBefore = curvePool.get_balances();\n // diff = hardAsset balance - OTOKEN balance\n int256 diffBefore = (\n balancesBefore[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() - balancesBefore[otokenCoinIndex].toInt256();\n\n _;\n\n // Get the hard asset and OToken balances in the Curve pool\n uint256[] memory balancesAfter = curvePool.get_balances();\n // diff = hardAsset balance - OTOKEN balance\n int256 diffAfter = (\n balancesAfter[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() - balancesAfter[otokenCoinIndex].toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OTOKEN, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of hardAsset, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _otoken,\n address _hardAsset,\n address _gauge,\n address _minter\n ) InitializableAbstractStrategy(_baseConfig) {\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveStableSwapNG(_baseConfig.platformAddress);\n minter = ICurveMinter(_minter);\n\n oToken = IERC20(_otoken);\n hardAsset = IERC20(_hardAsset);\n gauge = ICurveLiquidityGaugeV6(_gauge);\n decimalsHardAsset = IBasicToken(_hardAsset).decimals();\n decimalsOToken = IBasicToken(_otoken).decimals();\n\n (hardAssetCoinIndex, otokenCoinIndex) = curvePool.coins(0) == _hardAsset\n ? (0, 1)\n : (1, 0);\n require(\n curvePool.coins(otokenCoinIndex) == _otoken &&\n curvePool.coins(hardAssetCoinIndex) == _hardAsset,\n \"Invalid coin indexes\"\n );\n require(gauge.lp_token() == address(curvePool), \"Invalid pool\");\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV\n * @param _maxSlippage Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV\n uint256 _maxSlippage\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n address[] memory _assets = new address[](1);\n _assets[0] = address(hardAsset);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n _setMaxSlippage(_maxSlippage);\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit hard asset into the Curve pool\n * @param _hardAsset Address of hard asset contract.\n * @param _amount Amount of hard asset to deposit.\n */\n function deposit(address _hardAsset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_hardAsset, _amount);\n }\n\n function _deposit(address _hardAsset, uint256 _hardAssetAmount) internal {\n require(_hardAssetAmount > 0, \"Must deposit something\");\n require(_hardAsset == address(hardAsset), \"Unsupported asset\");\n\n emit Deposit(_hardAsset, address(lpToken), _hardAssetAmount);\n uint256 scaledHardAssetAmount = _hardAssetAmount.scaleBy(\n decimalsOToken,\n decimalsHardAsset\n );\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 otokenToAdd = uint256(\n _max(\n 0,\n (\n balances[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() +\n scaledHardAssetAmount.toInt256() -\n balances[otokenCoinIndex].toInt256()\n )\n );\n\n /* Add so much OTOKEN so that the pool ends up being balanced. And at minimum\n * add as much OTOKEN as hard asset and at maximum twice as much OTOKEN.\n */\n otokenToAdd = Math.max(otokenToAdd, scaledHardAssetAmount);\n otokenToAdd = Math.min(otokenToAdd, scaledHardAssetAmount * 2);\n\n /* Mint OTOKEN with a strategy that attempts to contribute to stability of OTOKEN/hardAsset pool. Try\n * to mint so much OTOKEN that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OTOKEN minted will always be at least equal or greater\n * to hardAsset amount deployed. And never larger than twice the hardAsset amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(otokenToAdd);\n\n emit Deposit(address(oToken), address(lpToken), otokenToAdd);\n\n uint256[] memory _amounts = new uint256[](2);\n _amounts[hardAssetCoinIndex] = _hardAssetAmount;\n _amounts[otokenCoinIndex] = otokenToAdd;\n\n uint256 valueInLpTokens = (scaledHardAssetAmount + otokenToAdd)\n .divPrecisely(curvePool.get_virtual_price());\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Do the deposit to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(_amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool's LP tokens into the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n /**\n * @notice Deposit the strategy's entire balance of hardAsset into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = hardAsset.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(hardAsset), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw hardAsset and OTOKEN from the Curve pool, burn the OTOKEN,\n * transfer hardAsset to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _hardAsset Address of the hardAsset contract.\n * @param _amount Amount of hardAsset to withdraw.\n */\n function withdraw(\n address _recipient,\n address _hardAsset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(\n _hardAsset == address(hardAsset),\n \"Can only withdraw hard asset\"\n );\n\n emit Withdrawal(_hardAsset, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(\n _amount.scaleBy(decimalsOToken, decimalsHardAsset)\n );\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough hardAsset on balanced removal\n */\n uint256[] memory _minWithdrawalAmounts = new uint256[](2);\n _minWithdrawalAmounts[hardAssetCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OTOKEN and any that was left in the strategy\n uint256 otokenToBurn = oToken.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n\n // Transfer hardAsset to the recipient\n hardAsset.safeTransfer(_recipient, _amount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n function calcTokenToBurn(uint256 _hardAssetAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much hardAsset\n * we want we can determine how much of OTOKEN we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolHardAssetBalance = curvePool\n .balances(hardAssetCoinIndex)\n .scaleBy(decimalsOToken, decimalsHardAsset);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolHardAssetBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_hardAssetAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all hardAsset and OTOKEN from the Curve pool, burn the OTOKEN,\n * transfer hardAsset to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = gauge.balanceOf(address(this));\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[] memory minWithdrawAmounts = new uint256[](2);\n\n // Check balance of LP tokens in the strategy, if 0 return\n uint256 lpBalance = lpToken.balanceOf(address(this));\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n if (lpBalance > 0) {\n curvePool.remove_liquidity(lpBalance, minWithdrawAmounts);\n }\n\n // Burn all OTOKEN\n uint256 otokenToBurn = oToken.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n // Get the strategy contract's hardAsset balance.\n // This includes all that was removed from the Curve pool and\n // any hardAsset that was sitting in the strategy contract before the removal.\n uint256 hardAssetBalance = hardAsset.balanceOf(address(this));\n hardAsset.safeTransfer(vaultAddress, hardAssetBalance);\n\n if (hardAssetBalance > 0)\n emit Withdrawal(\n address(hardAsset),\n address(lpToken),\n hardAssetBalance\n );\n if (otokenToBurn > 0)\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many hardAsset.\n * The OToken/Asset, eg OTOKEN/hardAsset, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[] memory amounts = new uint256[](2);\n amounts[otokenCoinIndex] = _oTokens;\n\n // Convert OTOKEN to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool LP tokens to the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Deposit(address(oToken), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough hardAsset.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from gauge and remove OTokens from the Curve pool\n uint256 otokenToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n otokenCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n }\n\n /**\n * @notice One-sided remove of hardAsset from the Curve pool and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many hardAsset.\n * The OToken/Asset, eg OTOKEN/hardAsset, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for hardAsset.\n * @dev Curve pool LP tokens is used rather than hardAsset assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of hardAsset. Curve's `calc_token_amount` function does not include fees.\n * A 3rd party library can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * calculate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Curve gauge and remove hardAsset from the Curve pool\n uint256 hardAssetAmount = _withdrawAndRemoveFromPool(\n _lpTokens,\n hardAssetCoinIndex\n );\n\n // Transfer hardAsset to the vault\n hardAsset.safeTransfer(vaultAddress, hardAssetAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(hardAsset), address(lpToken), hardAssetAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the gauge and\n * do a one-sided remove of hardAsset or OTOKEN from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the gauge\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = hardAsset, 1 = OTOKEN.\n * @return coinsRemoved The amount of hardAsset or OTOKEN removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Curve gauge\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to hardAsset value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n\n if (coinIndex == hardAssetCoinIndex) {\n valueInEth = valueInEth.scaleBy(decimalsHardAsset, decimalsOToken);\n }\n\n // Apply slippage to hardAsset value\n uint256 minAmount = valueInEth.mulTruncate(uint256(1e18) - maxSlippage);\n\n // Remove just the hardAsset from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOtokenSupply = oToken.totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOtokenSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV rewards from inflation\n minter.mint(address(gauge));\n\n // Collect extra gauge rewards (outside of CRV)\n gauge.claim_rewards();\n\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _lpAmount) internal {\n require(\n gauge.balanceOf(address(this)) >= _lpAmount,\n \"Insufficient LP tokens\"\n );\n // withdraw lp tokens from the gauge without claiming rewards\n gauge.withdraw(_lpAmount);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(hardAsset), \"Unsupported asset\");\n\n // hardAsset balance needed here for the balance check that happens from vault during depositing.\n balance = hardAsset.balanceOf(address(this));\n uint256 lpTokens = gauge.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += ((lpTokens * curvePool.get_virtual_price()) / 1e18)\n .scaleBy(decimalsHardAsset, decimalsOToken);\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(hardAsset);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Sets the maximum slippage allowed for any swap/liquidity operation\n * @param _maxSlippage Maximum slippage allowed, 1e18 = 100%.\n */\n function setMaxSlippage(uint256 _maxSlippage) external onlyGovernor {\n _setMaxSlippage(_maxSlippage);\n }\n\n function _setMaxSlippage(uint256 _maxSlippage) internal {\n require(_maxSlippage <= 5e16, \"Slippage must be less than 100%\");\n maxSlippage = _maxSlippage;\n emit MaxSlippageUpdated(_maxSlippage);\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OTOKEN (required for adding liquidity)\n // slither-disable-next-line unused-return\n oToken.approve(platformAddress, type(uint256).max);\n\n // Approve Curve pool for hardAsset (required for adding liquidity)\n // slither-disable-next-line unused-return\n hardAsset.safeApprove(platformAddress, type(uint256).max);\n\n // Approve Curve gauge contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Curve gauge.\n // slither-disable-next-line unused-return\n lpToken.approve(address(gauge), type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/CurvePoolBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport { ICampaignRemoteManager } from \"../interfaces/ICampaignRemoteManager.sol\";\n\n/// @title CurvePoolBooster\n/// @author Origin Protocol\n/// @notice Contract to manage interactions with VotemarketV2 for a dedicated Curve pool/gauge.\ncontract CurvePoolBooster is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n ////////////////////////////////////////////////////\n /// --- CONSTANTS && IMMUTABLES\n ////////////////////////////////////////////////////\n /// @notice Base fee for the contract, 100%\n uint16 public constant FEE_BASE = 10_000;\n\n /// @notice Address of the gauge to manage\n address public immutable gauge;\n\n /// @notice Address of the reward token\n address public immutable rewardToken;\n\n /// @notice Chain id of the target chain\n uint256 public immutable targetChainId;\n\n ////////////////////////////////////////////////////\n /// --- STORAGE\n ////////////////////////////////////////////////////\n /// @notice Fee in FEE_BASE unit payed when managing campaign.\n uint16 public fee;\n\n /// @notice Address of the fee collector\n address public feeCollector;\n\n /// @notice Address of the campaignRemoteManager linked to VotemarketV2\n address public campaignRemoteManager;\n\n /// @notice Address of votemarket in L2\n address public votemarket;\n\n /// @notice Id of the campaign created\n uint256 public campaignId;\n\n ////////////////////////////////////////////////////\n /// --- EVENTS\n ////////////////////////////////////////////////////\n event FeeUpdated(uint16 newFee);\n event FeeCollected(address feeCollector, uint256 feeAmount);\n event FeeCollectorUpdated(address newFeeCollector);\n event VotemarketUpdated(address newVotemarket);\n event CampaignRemoteManagerUpdated(address newCampaignRemoteManager);\n event CampaignCreated(\n address gauge,\n address rewardToken,\n uint256 maxRewardPerVote,\n uint256 totalRewardAmount\n );\n event CampaignIdUpdated(uint256 newId);\n event CampaignClosed(uint256 campaignId);\n event TotalRewardAmountUpdated(uint256 extraTotalRewardAmount);\n event NumberOfPeriodsUpdated(uint8 extraNumberOfPeriods);\n event RewardPerVoteUpdated(uint256 newMaxRewardPerVote);\n event TokensRescued(address token, uint256 amount, address receiver);\n\n ////////////////////////////////////////////////////\n /// --- CONSTRUCTOR && INITIALIZATION\n ////////////////////////////////////////////////////\n constructor(\n uint256 _targetChainId,\n address _rewardToken,\n address _gauge\n ) {\n targetChainId = _targetChainId;\n rewardToken = _rewardToken;\n gauge = _gauge;\n\n // Prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /// @notice initialize function, to set up initial internal state\n /// @param _strategist Address of the strategist\n /// @param _fee Fee in FEE_BASE unit payed when managing campaign\n /// @param _feeCollector Address of the fee collector\n function initialize(\n address _strategist,\n uint16 _fee,\n address _feeCollector,\n address _campaignRemoteManager,\n address _votemarket\n ) external onlyGovernor initializer {\n _setStrategistAddr(_strategist);\n _setFee(_fee);\n _setFeeCollector(_feeCollector);\n _setCampaignRemoteManager(_campaignRemoteManager);\n _setVotemarket(_votemarket);\n }\n\n ////////////////////////////////////////////////////\n /// --- MUTATIVE FUNCTIONS\n ////////////////////////////////////////////////////\n /// @notice Create a new campaign on VotemarketV2\n /// @dev This will use all token available in this contract\n /// @param numberOfPeriods Duration of the campaign in weeks\n /// @param maxRewardPerVote Maximum reward per vote to distribute, to avoid overspending\n /// @param blacklist List of addresses to exclude from the campaign\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function createCampaign(\n uint8 numberOfPeriods,\n uint256 maxRewardPerVote,\n address[] calldata blacklist,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId == 0, \"Campaign already created\");\n require(numberOfPeriods > 1, \"Invalid number of periods\");\n require(maxRewardPerVote > 0, \"Invalid reward per vote\");\n\n // Handle fee (if any)\n uint256 balanceSubFee = _handleFee();\n\n // Approve the balanceSubFee to the campaign manager\n IERC20(rewardToken).safeApprove(campaignRemoteManager, 0);\n IERC20(rewardToken).safeApprove(campaignRemoteManager, balanceSubFee);\n\n // Create a new campaign\n ICampaignRemoteManager(campaignRemoteManager).createCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignCreationParams({\n chainId: targetChainId,\n gauge: gauge,\n manager: address(this),\n rewardToken: rewardToken,\n numberOfPeriods: numberOfPeriods,\n maxRewardPerVote: maxRewardPerVote,\n totalRewardAmount: balanceSubFee,\n addresses: blacklist,\n hook: address(0),\n isWhitelist: false\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit CampaignCreated(\n gauge,\n rewardToken,\n maxRewardPerVote,\n balanceSubFee\n );\n }\n\n /// @notice Manage the total reward amount of the campaign\n /// @dev This function should be called after the campaign is created\n /// @dev This will use all the token available in this contract\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageTotalRewardAmount(\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n\n // Handle fee (if any)\n uint256 balanceSubFee = _handleFee();\n\n // Approve the total reward amount to the campaign manager\n IERC20(rewardToken).safeApprove(campaignRemoteManager, 0);\n IERC20(rewardToken).safeApprove(campaignRemoteManager, balanceSubFee);\n\n // Manage the campaign\n // https://github.com/stake-dao/votemarket-v2/blob/main/packages/votemarket/src/Votemarket.sol#L668\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: 0,\n totalRewardAmount: balanceSubFee,\n maxRewardPerVote: 0\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit TotalRewardAmountUpdated(balanceSubFee);\n }\n\n /// @notice Manage the number of periods of the campaign\n /// @dev This function should be called after the campaign is created\n /// @param extraNumberOfPeriods Number of additional periods (cannot be 0)\n /// that will be added to already existing amount of periods.\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageNumberOfPeriods(\n uint8 extraNumberOfPeriods,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n require(extraNumberOfPeriods > 0, \"Invalid number of periods\");\n\n // Manage the campaign\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: extraNumberOfPeriods,\n totalRewardAmount: 0,\n maxRewardPerVote: 0\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit NumberOfPeriodsUpdated(extraNumberOfPeriods);\n }\n\n /// @notice Manage the reward per vote of the campaign\n /// @dev This function should be called after the campaign is created\n /// @param newMaxRewardPerVote New maximum reward per vote\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageRewardPerVote(\n uint256 newMaxRewardPerVote,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n require(newMaxRewardPerVote > 0, \"Invalid reward per vote\");\n\n // Manage the campaign\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: 0,\n totalRewardAmount: 0,\n maxRewardPerVote: newMaxRewardPerVote\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit RewardPerVoteUpdated(newMaxRewardPerVote);\n }\n\n /// @notice Close the campaign.\n /// @dev This function only work on the L2 chain. Not on mainnet.\n /// @dev The _campaignId parameter is not related to the campaignId of this contract, allowing greater flexibility.\n /// @param _campaignId Id of the campaign to close\n function closeCampaign(\n uint256 _campaignId,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n ICampaignRemoteManager(campaignRemoteManager).closeCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignClosingParams({\n campaignId: campaignId\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n emit CampaignClosed(_campaignId);\n }\n\n /// @notice calculate the fee amount and transfer it to the feeCollector\n /// @return Balance after fee\n function _handleFee() internal returns (uint256) {\n // Cache current rewardToken balance\n uint256 balance = IERC20(rewardToken).balanceOf(address(this));\n require(balance > 0, \"No reward to manage\");\n\n uint256 feeAmount = (balance * fee) / FEE_BASE;\n\n // If there is a fee, transfer it to the feeCollector\n if (feeAmount > 0) {\n // Transfer the fee to the feeCollector\n IERC20(rewardToken).safeTransfer(feeCollector, feeAmount);\n emit FeeCollected(feeCollector, feeAmount);\n\n return IERC20(rewardToken).balanceOf(address(this));\n }\n\n // Return remaining balance\n return balance;\n }\n\n ////////////////////////////////////////////////////\n /// --- GOVERNANCE && OPERATION\n ////////////////////////////////////////////////////\n /// @notice Set the campaign id\n /// @dev Only callable by the governor or strategist\n /// @param _campaignId New campaign id\n function setCampaignId(uint256 _campaignId)\n external\n onlyGovernorOrStrategist\n {\n campaignId = _campaignId;\n emit CampaignIdUpdated(_campaignId);\n }\n\n /// @notice Rescue ETH from the contract\n /// @dev Only callable by the governor or strategist\n /// @param receiver Address to receive the ETH\n function rescueETH(address receiver)\n external\n nonReentrant\n onlyGovernorOrStrategist\n {\n require(receiver != address(0), \"Invalid receiver\");\n uint256 balance = address(this).balance;\n (bool success, ) = receiver.call{ value: balance }(\"\");\n require(success, \"Transfer failed\");\n emit TokensRescued(address(0), balance, receiver);\n }\n\n /// @notice Rescue ERC20 tokens from the contract\n /// @dev Only callable by the governor or strategist\n /// @param token Address of the token to rescue\n function rescueToken(address token, address receiver)\n external\n nonReentrant\n onlyGovernor\n {\n require(receiver != address(0), \"Invalid receiver\");\n uint256 balance = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransfer(receiver, balance);\n emit TokensRescued(token, balance, receiver);\n }\n\n /// @notice Set the fee\n /// @dev Only callable by the governor\n /// @param _fee New fee\n function setFee(uint16 _fee) external onlyGovernor {\n _setFee(_fee);\n }\n\n /// @notice Internal logic to set the fee\n function _setFee(uint16 _fee) internal {\n require(_fee <= FEE_BASE / 2, \"Fee too high\");\n fee = _fee;\n emit FeeUpdated(_fee);\n }\n\n /// @notice Set the fee collector\n /// @dev Only callable by the governor\n /// @param _feeCollector New fee collector\n function setFeeCollector(address _feeCollector) external onlyGovernor {\n _setFeeCollector(_feeCollector);\n }\n\n /// @notice Internal logic to set the fee collector\n function _setFeeCollector(address _feeCollector) internal {\n require(_feeCollector != address(0), \"Invalid fee collector\");\n feeCollector = _feeCollector;\n emit FeeCollectorUpdated(_feeCollector);\n }\n\n /// @notice Set the campaignRemoteManager\n /// @param _campaignRemoteManager New campaignRemoteManager address\n function setCampaignRemoteManager(address _campaignRemoteManager)\n external\n onlyGovernor\n {\n _setCampaignRemoteManager(_campaignRemoteManager);\n }\n\n /// @notice Internal logic to set the campaignRemoteManager\n /// @param _campaignRemoteManager New campaignRemoteManager address\n function _setCampaignRemoteManager(address _campaignRemoteManager)\n internal\n {\n require(\n _campaignRemoteManager != address(0),\n \"Invalid campaignRemoteManager\"\n );\n campaignRemoteManager = _campaignRemoteManager;\n emit CampaignRemoteManagerUpdated(_campaignRemoteManager);\n }\n\n /// @notice Set the votemarket address\n /// @param _votemarket New votemarket address\n function setVotemarket(address _votemarket) external onlyGovernor {\n _setVotemarket(_votemarket);\n }\n\n /// @notice Internal logic to set the votemarket address\n function _setVotemarket(address _votemarket) internal onlyGovernor {\n require(_votemarket != address(0), \"Invalid votemarket\");\n votemarket = _votemarket;\n emit VotemarketUpdated(_votemarket);\n }\n\n receive() external payable {}\n}\n" + }, + "contracts/strategies/Generalized4626Strategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Generalized 4626 Strategy\n * @notice Investment strategy for ERC-4626 Tokenized Vaults\n * @author Origin Protocol Inc\n */\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\ncontract Generalized4626Strategy is InitializableAbstractStrategy {\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecate_shareToken;\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecate_assetToken;\n\n IERC20 public immutable shareToken;\n IERC20 public immutable assetToken;\n\n // For future use\n uint256[50] private __gap;\n\n /**\n * @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n * and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n * @param _assetToken Address of the ERC-4626 asset token. eg frxETH or DAI\n */\n constructor(BaseStrategyConfig memory _baseConfig, address _assetToken)\n InitializableAbstractStrategy(_baseConfig)\n {\n shareToken = IERC20(_baseConfig.platformAddress);\n assetToken = IERC20(_assetToken);\n }\n\n function initialize() external virtual onlyGovernor initializer {\n address[] memory rewardTokens = new address[](0);\n address[] memory assets = new address[](1);\n address[] memory pTokens = new address[](1);\n\n assets[0] = address(assetToken);\n pTokens[0] = address(platformAddress);\n\n InitializableAbstractStrategy._initialize(\n rewardTokens,\n assets,\n pTokens\n );\n }\n\n /**\n * @dev Deposit assets by converting them to shares\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit assets by converting them to shares\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal virtual {\n require(_amount > 0, \"Must deposit something\");\n require(_asset == address(assetToken), \"Unexpected asset address\");\n\n // slither-disable-next-line unused-return\n IERC4626(platformAddress).deposit(_amount, address(this));\n emit Deposit(_asset, address(shareToken), _amount);\n }\n\n /**\n * @dev Deposit the entire balance of assetToken to gain shareToken\n */\n function depositAll() external virtual override onlyVault nonReentrant {\n uint256 balance = assetToken.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(assetToken), balance);\n }\n }\n\n /**\n * @dev Withdraw asset by burning shares\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n require(_asset == address(assetToken), \"Unexpected asset address\");\n\n // slither-disable-next-line unused-return\n IERC4626(platformAddress).withdraw(_amount, _recipient, address(this));\n emit Withdrawal(_asset, address(shareToken), _amount);\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / share tokens\n */\n function _abstractSetPToken(address, address) internal virtual override {\n _approveBase();\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll()\n external\n virtual\n override\n onlyVaultOrGovernor\n nonReentrant\n {\n uint256 shareBalance = shareToken.balanceOf(address(this));\n uint256 assetAmount = 0;\n if (shareBalance > 0) {\n assetAmount = IERC4626(platformAddress).redeem(\n shareBalance,\n vaultAddress,\n address(this)\n );\n emit Withdrawal(\n address(assetToken),\n address(shareToken),\n assetAmount\n );\n }\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(_asset == address(assetToken), \"Unexpected asset address\");\n /* We are intentionally not counting the amount of assetToken parked on the\n * contract toward the checkBalance. The deposit and withdraw functions\n * should not result in assetToken being unused and owned by this strategy\n * contract.\n */\n IERC4626 platform = IERC4626(platformAddress);\n return platform.previewRedeem(platform.balanceOf(address(this)));\n }\n\n /**\n * @notice Governor approves the ERC-4626 Tokenized Vault to spend the asset.\n */\n function safeApproveAllTokens() external override onlyGovernor {\n _approveBase();\n }\n\n function _approveBase() internal virtual {\n // Approval the asset to be transferred to the ERC-4626 Tokenized Vault.\n // Used by the ERC-4626 deposit() and mint() functions\n // slither-disable-next-line unused-return\n assetToken.approve(platformAddress, type(uint256).max);\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset)\n public\n view\n virtual\n override\n returns (bool)\n {\n return _asset == address(assetToken);\n }\n\n /**\n * @notice is not supported for this strategy as the asset and\n * ERC-4626 Tokenized Vault are set at deploy time.\n * @dev If the ERC-4626 Tokenized Vault needed to be changed, a new\n * contract would need to be deployed and the proxy updated.\n */\n function setPTokenAddress(address, address) external override onlyGovernor {\n revert(\"unsupported function\");\n }\n\n /**\n * @notice is not supported for this strategy as the asset and\n * ERC-4626 Tokenized Vault are set at deploy time.\n * @dev If the ERC-4626 Tokenized Vault needed to be changed, a new\n * contract would need to be deployed and the proxy updated.\n */\n function removePToken(uint256) external override onlyGovernor {\n revert(\"unsupported function\");\n }\n}\n" + }, + "contracts/strategies/Generalized4626USDTStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IUSDT {\n // Tether's approve does not return a bool like standard IERC20 contracts\n // slither-disable-next-line erc20-interface\n function approve(address _spender, uint256 _value) external;\n}\n\n/**\n * @title Generalized 4626 Strategy when asset is Tether USD (USDT)\n * @notice Investment strategy for ERC-4626 Tokenized Vaults for the USDT asset.\n * @author Origin Protocol Inc\n */\nimport { Generalized4626Strategy } from \"./Generalized4626Strategy.sol\";\n\ncontract Generalized4626USDTStrategy is Generalized4626Strategy {\n /**\n * @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n * and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n * @param _assetToken Address of the ERC-4626 asset token. eg frxETH or DAI\n */\n constructor(BaseStrategyConfig memory _baseConfig, address _assetToken)\n Generalized4626Strategy(_baseConfig, _assetToken)\n {}\n\n /// @dev Override for Tether as USDT does not return a bool on approve.\n /// Using assetToken.approve will fail as it expects a bool return value\n function _approveBase() internal virtual override {\n // Approval the asset to be transferred to the ERC-4626 Tokenized Vault.\n // Used by the ERC-4626 deposit() and mint() functions\n // slither-disable-next-line unused-return\n IUSDT(address(assetToken)).approve(platformAddress, type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/IAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface for Aaves Lending Pool\n * Documentation: https://developers.aave.com/#lendingpool\n */\ninterface IAaveLendingPool {\n /**\n * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.\n * - E.g. User deposits 100 USDC and gets in return 100 aUSDC\n * @param asset The address of the underlying asset to deposit\n * @param amount The amount to be deposited\n * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user\n * wants to receive them on his own wallet, or a different address if the beneficiary of aTokens\n * is a different wallet\n * @param referralCode Code used to register the integrator originating the operation, for potential rewards.\n * 0 if the action is executed directly by the user, without any middle-man\n **/\n function deposit(\n address asset,\n uint256 amount,\n address onBehalfOf,\n uint16 referralCode\n ) external;\n\n /**\n * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned\n * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC\n * @param asset The address of the underlying asset to withdraw\n * @param amount The underlying amount to be withdrawn\n * - Send the value type(uint256).max in order to withdraw the whole aToken balance\n * @param to Address that will receive the underlying, same as msg.sender if the user\n * wants to receive it on his own wallet, or a different address if the beneficiary is a\n * different wallet\n * @return The final amount withdrawn\n **/\n function withdraw(\n address asset,\n uint256 amount,\n address to\n ) external returns (uint256);\n}\n\n/**\n * @dev Interface for Aaves Lending Pool\n * Documentation: https://developers.aave.com/#lendingpooladdressesprovider\n */\ninterface ILendingPoolAddressesProvider {\n /**\n * @notice Get the current address for Aave LendingPool\n * @dev Lending pool is the core contract on which to call deposit\n */\n function getLendingPool() external view returns (address);\n}\n" + }, + "contracts/strategies/IAaveIncentivesController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IAaveIncentivesController {\n event RewardsAccrued(address indexed user, uint256 amount);\n\n event RewardsClaimed(\n address indexed user,\n address indexed to,\n uint256 amount\n );\n\n event RewardsClaimed(\n address indexed user,\n address indexed to,\n address indexed claimer,\n uint256 amount\n );\n\n event ClaimerSet(address indexed user, address indexed claimer);\n\n /*\n * @dev Returns the configuration of the distribution for a certain asset\n * @param asset The address of the reference asset of the distribution\n * @return The asset index, the emission per second and the last updated timestamp\n **/\n function getAssetData(address asset)\n external\n view\n returns (\n uint256,\n uint256,\n uint256\n );\n\n /**\n * @dev Whitelists an address to claim the rewards on behalf of another address\n * @param user The address of the user\n * @param claimer The address of the claimer\n */\n function setClaimer(address user, address claimer) external;\n\n /**\n * @dev Returns the whitelisted claimer for a certain address (0x0 if not set)\n * @param user The address of the user\n * @return The claimer address\n */\n function getClaimer(address user) external view returns (address);\n\n /**\n * @dev Configure assets for a certain rewards emission\n * @param assets The assets to incentivize\n * @param emissionsPerSecond The emission for each asset\n */\n function configureAssets(\n address[] calldata assets,\n uint256[] calldata emissionsPerSecond\n ) external;\n\n /**\n * @dev Called by the corresponding asset on any update that affects the rewards distribution\n * @param asset The address of the user\n * @param userBalance The balance of the user of the asset in the lending pool\n * @param totalSupply The total supply of the asset in the lending pool\n **/\n function handleAction(\n address asset,\n uint256 userBalance,\n uint256 totalSupply\n ) external;\n\n /**\n * @dev Returns the total of rewards of an user, already accrued + not yet accrued\n * @param user The address of the user\n * @return The rewards\n **/\n function getRewardsBalance(address[] calldata assets, address user)\n external\n view\n returns (uint256);\n\n /**\n * @dev Claims reward for an user, on all the assets of the lending pool,\n * accumulating the pending rewards\n * @param amount Amount of rewards to claim\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewards(\n address[] calldata assets,\n uint256 amount,\n address to\n ) external returns (uint256);\n\n /**\n * @dev Claims reward for an user on behalf, on all the assets of the\n * lending pool, accumulating the pending rewards. The caller must\n * be whitelisted via \"allowClaimOnBehalf\" function by the RewardsAdmin role manager\n * @param amount Amount of rewards to claim\n * @param user Address to check and claim rewards\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewardsOnBehalf(\n address[] calldata assets,\n uint256 amount,\n address user,\n address to\n ) external returns (uint256);\n\n /**\n * @dev returns the unclaimed rewards of the user\n * @param user the address of the user\n * @return the unclaimed user rewards\n */\n function getUserUnclaimedRewards(address user)\n external\n view\n returns (uint256);\n\n /**\n * @dev returns the unclaimed rewards of the user\n * @param user the address of the user\n * @param asset The asset to incentivize\n * @return the user index for the asset\n */\n function getUserAssetData(address user, address asset)\n external\n view\n returns (uint256);\n\n /**\n * @dev for backward compatibility with previous implementation of the Incentives controller\n */\n function REWARD_TOKEN() external view returns (address);\n\n /**\n * @dev for backward compatibility with previous implementation of the Incentives controller\n */\n function PRECISION() external view returns (uint8);\n\n /**\n * @dev Gets the distribution end timestamp of the emissions\n */\n function DISTRIBUTION_END() external view returns (uint256);\n}\n" + }, + "contracts/strategies/IAaveStakeToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IAaveStakedToken {\n function COOLDOWN_SECONDS() external returns (uint256);\n\n function UNSTAKE_WINDOW() external returns (uint256);\n\n function balanceOf(address addr) external returns (uint256);\n\n function redeem(address to, uint256 amount) external;\n\n function stakersCooldowns(address addr) external returns (uint256);\n\n function cooldown() external;\n}\n" + }, + "contracts/strategies/ICompound.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @dev Compound C Token interface\n * Documentation: https://compound.finance/developers/ctokens\n */\ninterface ICERC20 {\n /**\n * @notice The mint function transfers an asset into the protocol, which begins accumulating\n * interest based on the current Supply Rate for the asset. The user receives a quantity of\n * cTokens equal to the underlying tokens supplied, divided by the current Exchange Rate.\n * @param mintAmount The amount of the asset to be supplied, in units of the underlying asset.\n * @return 0 on success, otherwise an Error codes\n */\n function mint(uint256 mintAmount) external returns (uint256);\n\n /**\n * @notice Sender redeems cTokens in exchange for the underlying asset\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param redeemTokens The number of cTokens to redeem into underlying\n * @return uint 0=success, otherwise an error code.\n */\n function redeem(uint256 redeemTokens) external returns (uint256);\n\n /**\n * @notice The redeem underlying function converts cTokens into a specified quantity of the underlying\n * asset, and returns them to the user. The amount of cTokens redeemed is equal to the quantity of\n * underlying tokens received, divided by the current Exchange Rate. The amount redeemed must be less\n * than the user's Account Liquidity and the market's available liquidity.\n * @param redeemAmount The amount of underlying to be redeemed.\n * @return 0 on success, otherwise an error code.\n */\n function redeemUnderlying(uint256 redeemAmount) external returns (uint256);\n\n /**\n * @notice The user's underlying balance, representing their assets in the protocol, is equal to\n * the user's cToken balance multiplied by the Exchange Rate.\n * @param owner The account to get the underlying balance of.\n * @return The amount of underlying currently owned by the account.\n */\n function balanceOfUnderlying(address owner) external returns (uint256);\n\n /**\n * @notice Calculates the exchange rate from the underlying to the CToken\n * @dev This function does not accrue interest before calculating the exchange rate\n * @return Calculated exchange rate scaled by 1e18\n */\n function exchangeRateStored() external view returns (uint256);\n\n /**\n * @notice Get the token balance of the `owner`\n * @param owner The address of the account to query\n * @return The number of tokens owned by `owner`\n */\n function balanceOf(address owner) external view returns (uint256);\n\n /**\n * @notice Get the supply rate per block for supplying the token to Compound.\n */\n function supplyRatePerBlock() external view returns (uint256);\n\n /**\n * @notice Address of the Compound Comptroller.\n */\n function comptroller() external view returns (address);\n}\n" + }, + "contracts/strategies/IConvexDeposits.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IConvexDeposits {\n function deposit(\n uint256 _pid,\n uint256 _amount,\n bool _stake\n ) external returns (bool);\n\n function deposit(\n uint256 _amount,\n bool _lock,\n address _stakeAddress\n ) external;\n}\n" + }, + "contracts/strategies/ICRVMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICRVMinter {\n function mint(address gaugeAddress) external;\n}\n" + }, + "contracts/strategies/ICurveETHPoolV1.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurveETHPoolV1 {\n event AddLiquidity(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event ApplyNewFee(uint256 fee);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event CommitNewFee(uint256 new_fee);\n event RampA(\n uint256 old_A,\n uint256 new_A,\n uint256 initial_time,\n uint256 future_time\n );\n event RemoveLiquidity(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 token_supply\n );\n event RemoveLiquidityImbalance(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event RemoveLiquidityOne(\n address indexed provider,\n uint256 token_amount,\n uint256 coin_amount,\n uint256 token_supply\n );\n event StopRampA(uint256 A, uint256 t);\n event TokenExchange(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event Transfer(\n address indexed sender,\n address indexed receiver,\n uint256 value\n );\n\n function A() external view returns (uint256);\n\n function A_precise() external view returns (uint256);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_liquidity(uint256[2] memory _amounts, uint256 _min_mint_amount)\n external\n payable\n returns (uint256);\n\n function add_liquidity(\n uint256[2] memory _amounts,\n uint256 _min_mint_amount,\n address _receiver\n ) external payable returns (uint256);\n\n function admin_action_deadline() external view returns (uint256);\n\n function admin_balances(uint256 i) external view returns (uint256);\n\n function admin_fee() external view returns (uint256);\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function apply_new_fee() external;\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function balances(uint256 arg0) external view returns (uint256);\n\n function calc_token_amount(uint256[2] memory _amounts, bool _is_deposit)\n external\n view\n returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _burn_amount, int128 i)\n external\n view\n returns (uint256);\n\n function coins(uint256 arg0) external view returns (address);\n\n function commit_new_fee(uint256 _new_fee) external;\n\n function decimals() external view returns (uint256);\n\n function ema_price() external view returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external payable returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external payable returns (uint256);\n\n function fee() external view returns (uint256);\n\n function future_A() external view returns (uint256);\n\n function future_A_time() external view returns (uint256);\n\n function future_fee() external view returns (uint256);\n\n function get_balances() external view returns (uint256[2] memory);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n\n function get_p() external view returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function initial_A() external view returns (uint256);\n\n function initial_A_time() external view returns (uint256);\n\n function initialize(\n string memory _name,\n string memory _symbol,\n address[4] memory _coins,\n uint256[4] memory _rate_multipliers,\n uint256 _A,\n uint256 _fee\n ) external;\n\n function last_price() external view returns (uint256);\n\n function ma_exp_time() external view returns (uint256);\n\n function ma_last_time() external view returns (uint256);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function price_oracle() external view returns (uint256);\n\n function ramp_A(uint256 _future_A, uint256 _future_time) external;\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[2] memory _min_amounts\n ) external returns (uint256[2] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[2] memory _min_amounts,\n address _receiver\n ) external returns (uint256[2] memory);\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burn_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burn_amount,\n address _receiver\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received,\n address _receiver\n ) external returns (uint256);\n\n function set_ma_exp_time(uint256 _ma_exp_time) external;\n\n function stop_ramp_A() external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw_admin_fees() external;\n}\n" + }, + "contracts/strategies/ICurveGauge.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurveGauge {\n function balanceOf(address account) external view returns (uint256);\n\n function deposit(uint256 value, address account) external;\n\n function withdraw(uint256 value) external;\n}\n" + }, + "contracts/strategies/ICurveMetaPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface ICurveMetaPool {\n function add_liquidity(uint256[2] calldata amounts, uint256 min_mint_amount)\n external\n returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function remove_liquidity(uint256 _amount, uint256[2] calldata min_amounts)\n external\n returns (uint256[2] calldata);\n\n function remove_liquidity_one_coin(\n uint256 _token_amount,\n int128 i,\n uint256 min_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[2] calldata amounts,\n uint256 max_burn_amount\n ) external returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _token_amount, int128 i)\n external\n view\n returns (uint256);\n\n function balances(uint256 i) external view returns (uint256);\n\n function calc_token_amount(uint256[2] calldata amounts, bool deposit)\n external\n view\n returns (uint256);\n\n function base_pool() external view returns (address);\n\n function fee() external view returns (uint256);\n\n function coins(uint256 i) external view returns (address);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 dx,\n uint256 min_dy\n ) external returns (uint256);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n}\n" + }, + "contracts/strategies/ICurvePool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurvePool {\n function get_virtual_price() external view returns (uint256);\n\n function add_liquidity(uint256[3] calldata _amounts, uint256 _min) external;\n\n function balances(uint256) external view returns (uint256);\n\n function calc_token_amount(uint256[3] calldata _amounts, bool _deposit)\n external\n returns (uint256);\n\n function fee() external view returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _amount,\n int128 _index,\n uint256 _minAmount\n ) external;\n\n function remove_liquidity(\n uint256 _amount,\n uint256[3] calldata _minWithdrawAmounts\n ) external;\n\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n external\n view\n returns (uint256);\n\n function exchange(\n uint256 i,\n uint256 j,\n uint256 dx,\n uint256 min_dy\n ) external returns (uint256);\n\n function coins(uint256 _index) external view returns (address);\n\n function remove_liquidity_imbalance(\n uint256[3] calldata _amounts,\n uint256 maxBurnAmount\n ) external;\n}\n" + }, + "contracts/strategies/IRewardStaking.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IRewardStaking {\n function stakeFor(address, uint256) external;\n\n function stake(uint256) external;\n\n function withdraw(uint256 amount, bool claim) external;\n\n function withdrawAndUnwrap(uint256 amount, bool claim) external;\n\n function earned(address account) external view returns (uint256);\n\n function getReward() external;\n\n function getReward(address _account, bool _claimExtras) external;\n\n function extraRewardsLength() external returns (uint256);\n\n function extraRewards(uint256 _pid) external returns (address);\n\n function rewardToken() external returns (address);\n\n function balanceOf(address account) external view returns (uint256);\n}\n" + }, + "contracts/strategies/MorphoAaveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Morpho Aave Strategy\n * @notice Investment strategy for investing stablecoins via Morpho (Aave)\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { IMorpho } from \"../interfaces/morpho/IMorpho.sol\";\nimport { ILens } from \"../interfaces/morpho/ILens.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MorphoAaveStrategy is InitializableAbstractStrategy {\n address public constant MORPHO = 0x777777c9898D384F785Ee44Acfe945efDFf5f3E0;\n address public constant LENS = 0x507fA343d0A90786d86C7cd885f5C49263A91FF4;\n\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @dev Initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Approve the spending of all assets by main Morpho contract,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address asset = assetsMapped[i];\n\n // Safe approval\n IERC20(asset).safeApprove(MORPHO, 0);\n IERC20(asset).safeApprove(MORPHO, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset\n * We need to approve and allow Morpho to move them\n * @param _asset Address of the asset to approve\n * @param _pToken The pToken for the approval\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n IERC20(_asset).safeApprove(MORPHO, 0);\n IERC20(_asset).safeApprove(MORPHO, type(uint256).max);\n }\n\n /**\n * @dev Collect accumulated rewards and send them to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Morpho Aave-v2 doesn't distribute reward tokens\n // solhint-disable-next-line max-line-length\n // Ref: https://developers.morpho.xyz/interact-with-morpho/get-started/interact-with-morpho/claim-rewards#morpho-aave-v2\n }\n\n /**\n * @dev Get the amount of rewards pending to be collected from the protocol\n */\n function getPendingRewards() external view returns (uint256 balance) {\n // Morpho Aave-v2 doesn't distribute reward tokens\n // solhint-disable-next-line max-line-length\n // Ref: https://developers.morpho.xyz/interact-with-morpho/get-started/interact-with-morpho/claim-rewards#morpho-aave-v2\n return 0;\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n\n address pToken = address(_getPTokenFor(_asset));\n\n IMorpho(MORPHO).supply(\n pToken,\n address(this), // the address of the user you want to supply on behalf of\n _amount\n );\n emit Deposit(_asset, pToken, _amount);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Morpho\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Morpho\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n address pToken = address(_getPTokenFor(_asset));\n\n IMorpho(MORPHO).withdraw(pToken, _amount);\n emit Withdrawal(_asset, pToken, _amount);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = _checkBalance(assetsMapped[i]);\n if (balance > 0) {\n _withdraw(vaultAddress, assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Return total value of an asset held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n return _checkBalance(_asset);\n }\n\n function _checkBalance(address _asset)\n internal\n view\n returns (uint256 balance)\n {\n address pToken = address(_getPTokenFor(_asset));\n\n // Total value represented by decimal position of underlying token\n (, , balance) = ILens(LENS).getCurrentSupplyBalanceInOf(\n pToken,\n address(this)\n );\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Get the pToken wrapped in the IERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return pToken Corresponding pToken to this asset\n */\n function _getPTokenFor(address _asset) internal view returns (IERC20) {\n address pToken = assetToPToken[_asset];\n require(pToken != address(0), \"pToken does not exist\");\n return IERC20(pToken);\n }\n}\n" + }, + "contracts/strategies/MorphoCompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Morpho Compound Strategy\n * @notice Investment strategy for investing stablecoins via Morpho (Compound)\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, AbstractCompoundStrategy, InitializableAbstractStrategy } from \"./AbstractCompoundStrategy.sol\";\nimport { IMorpho } from \"../interfaces/morpho/IMorpho.sol\";\nimport { ILens } from \"../interfaces/morpho/ILens.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MorphoCompoundStrategy is AbstractCompoundStrategy {\n address public constant MORPHO = 0x8888882f8f843896699869179fB6E4f7e3B58888;\n address public constant LENS = 0x930f1b46e1D081Ec1524efD95752bE3eCe51EF67;\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @dev Initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Approve the spending of all assets by main Morpho contract,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address asset = assetsMapped[i];\n\n // Safe approval\n IERC20(asset).safeApprove(MORPHO, 0);\n IERC20(asset).safeApprove(MORPHO, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset\n * We need to approve and allow Morpho to move them\n * @param _asset Address of the asset to approve\n * @param _pToken The pToken for the approval\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n IERC20(_asset).safeApprove(MORPHO, 0);\n IERC20(_asset).safeApprove(MORPHO, type(uint256).max);\n }\n\n /**\n * @dev Collect accumulated rewards and send them to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n /**\n * Gas considerations. We could query Morpho LENS's `getUserUnclaimedRewards` for each\n * cToken separately and only claimRewards where it is economically feasible. Each call\n * (out of 3) costs ~60k gas extra.\n *\n * Each extra cToken in the `poolTokens` of `claimRewards` function makes that call\n * 89-120k more expensive gas wise.\n *\n * With Lens query in case where:\n * - there is only 1 reward token to collect. Net gas usage in best case would be\n * 3*60 - 2*120 = -60k -> saving 60k gas\n * - there are 2 reward tokens to collect. Net gas usage in best case would be\n * 3*60 - 120 = 60k -> more expensive for 60k gas\n * - there are 3 reward tokens to collect. Net gas usage in best case would be\n * 3*60 = 180k -> more expensive for 180k gas\n *\n * For the above reasoning such \"optimization\" is not implemented\n */\n\n address[] memory poolTokens = new address[](assetsMapped.length);\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n poolTokens[i] = assetToPToken[assetsMapped[i]];\n }\n\n // slither-disable-next-line unused-return\n IMorpho(MORPHO).claimRewards(\n poolTokens, // The addresses of the underlying protocol's pools to claim rewards from\n false // Whether to trade the accrued rewards for MORPHO token, with a premium\n );\n\n // Transfer COMP to Harvester\n IERC20 rewardToken = IERC20(rewardTokenAddresses[0]);\n uint256 balance = rewardToken.balanceOf(address(this));\n emit RewardTokenCollected(\n harvesterAddress,\n rewardTokenAddresses[0],\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n\n /**\n * @dev Get the amount of rewards pending to be collected from the protocol\n */\n function getPendingRewards() external view returns (uint256 balance) {\n address[] memory poolTokens = new address[](assetsMapped.length);\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n poolTokens[i] = assetToPToken[assetsMapped[i]];\n }\n\n return ILens(LENS).getUserUnclaimedRewards(poolTokens, address(this));\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n\n IMorpho(MORPHO).supply(\n address(_getCTokenFor(_asset)),\n address(this), // the address of the user you want to supply on behalf of\n _amount\n );\n emit Deposit(_asset, address(_getCTokenFor(_asset)), _amount);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Morpho\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Morpho\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n address pToken = assetToPToken[_asset];\n\n IMorpho(MORPHO).withdraw(pToken, _amount);\n emit Withdrawal(_asset, address(_getCTokenFor(_asset)), _amount);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = _checkBalance(assetsMapped[i]);\n if (balance > 0) {\n _withdraw(vaultAddress, assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Return total value of an asset held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n return _checkBalance(_asset);\n }\n\n function _checkBalance(address _asset)\n internal\n view\n returns (uint256 balance)\n {\n address pToken = assetToPToken[_asset];\n\n // Total value represented by decimal position of underlying token\n (, , balance) = ILens(LENS).getCurrentSupplyBalanceInOf(\n pToken,\n address(this)\n );\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with\n /// `platformAddress` not used so empty address\n /// `vaultAddress` the address of the OETH Vault contract\n /// @param _wethAddress Address of the WETH Token contract\n /// @param _ssvToken Address of the SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconProofs,\n _beaconGenesisTimestamp\n )\n {\n SSV_TOKEN = _ssvToken;\n\n // Make sure nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// @param _rewardTokenAddresses Not used so empty array\n /// @param _assets Not used so empty array\n /// @param _pTokens Not used so empty array\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n safeApproveAllTokens();\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH that was transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// @param _recipient Address to receive withdrawn assets.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH to withdraw.\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(WETH, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Accounts for all the assets managed by this strategy which includes:\n /// 1. The current WETH in this strategy contract\n /// 2. The last verified ETH balance, total deposits and total validator balances\n /// @param _asset Address of WETH asset.\n /// @return balance Total value in ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by the strategy.\n /// @param _asset The address of the WETH token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for validator registration.\n function safeApproveAllTokens() public override {\n // Approves the SSV Network contract to transfer SSV tokens when validators are registered\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n * The new ETH will be accounted for in `checkBalance` after the next snapBalances and verifyBalances txs.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n /// @notice is not supported for this strategy as there is no platform token.\n function setPTokenAddress(address, address) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @notice is not supported for this strategy as there is no platform token.\n function removePToken(uint256) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @dev This strategy does not use a platform token like the old Aave and Compound strategies.\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instead of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract.\n /// Withdrawals from validators also accumulate as ETH in this strategy contract.\n /// It's too complex to separate the rewards from withdrawals so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can now regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingView.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/**\n * @title Viewing contract for the Compounding Staking Strategy.\n * @author Origin Protocol Inc\n */\ncontract CompoundingStakingStrategyView {\n /// @notice The address of the Compounding Staking Strategy contract\n CompoundingValidatorManager public immutable stakingStrategy;\n\n constructor(address _stakingStrategy) {\n stakingStrategy = CompoundingValidatorManager(_stakingStrategy);\n }\n\n struct ValidatorView {\n bytes32 pubKeyHash;\n uint64 index;\n CompoundingValidatorManager.ValidatorState state;\n }\n\n struct DepositView {\n uint256 depositID;\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint256 withdrawableEpoch;\n }\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return validators An array of `ValidatorView` containing the public key hash, validator index and state.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorView[] memory validators)\n {\n uint256 validatorCount = stakingStrategy.verifiedValidatorsLength();\n validators = new ValidatorView[](validatorCount);\n for (uint256 i = 0; i < validatorCount; ++i) {\n bytes32 pubKeyHash = stakingStrategy.verifiedValidators(i);\n (\n CompoundingValidatorManager.ValidatorState state,\n uint64 index\n ) = stakingStrategy.validator(pubKeyHash);\n validators[i] = ValidatorView({\n pubKeyHash: pubKeyHash,\n index: index,\n state: state\n });\n }\n }\n\n /// @notice Returns the deposits that are still to be verified.\n /// These may or may not have been processed by the beacon chain.\n /// @return pendingDeposits An array of `DepositView` containing the deposit ID, public key hash,\n /// amount in Gwei and the slot of the deposit.\n function getPendingDeposits()\n external\n view\n returns (DepositView[] memory pendingDeposits)\n {\n uint256 depositsCount = stakingStrategy.depositListLength();\n pendingDeposits = new DepositView[](depositsCount);\n for (uint256 i = 0; i < depositsCount; ++i) {\n (\n bytes32 pubKeyHash,\n uint64 amountGwei,\n uint64 slot,\n ,\n ,\n uint256 withdrawableEpoch\n ) = stakingStrategy.deposits(stakingStrategy.depositList(i));\n pendingDeposits[i] = DepositView({\n depositID: stakingStrategy.depositList(i),\n pubKeyHash: pubKeyHash,\n amountGwei: amountGwei,\n slot: slot,\n withdrawableEpoch: withdrawableEpoch\n });\n }\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable {\n using SafeERC20 for IERC20;\n\n /// @dev The amount of ETH in wei that is required for a deposit to a new validator.\n uint256 internal constant DEPOSIT_AMOUNT_WEI = 1 ether;\n /// @dev The maximum number of deposits that are waiting to be verified as processed on the beacon chain.\n uint256 internal constant MAX_DEPOSITS = 12;\n /// @dev The maximum number of validators that can be verified.\n uint256 internal constant MAX_VERIFIED_VALIDATORS = 48;\n /// @dev The default withdrawable epoch value on the Beacon chain.\n /// A value in the far future means the validator is not exiting.\n uint64 internal constant FAR_FUTURE_EPOCH = type(uint64).max;\n /// @dev The number of seconds between each beacon chain slot.\n uint64 internal constant SLOT_DURATION = 12;\n /// @dev The number of slots in each beacon chain epoch.\n uint64 internal constant SLOTS_PER_EPOCH = 32;\n /// @dev Minimum time in seconds to allow snapped balances to be verified.\n /// Set to 35 slots which is 3 slots more than 1 epoch (32 slots). Deposits get processed\n /// once per epoch. This larger than 1 epoch delay should achieve that `snapBalances` sometimes\n /// get called in the middle (or towards the end) of the epoch. Giving the off-chain script\n /// sufficient time after the end of the epoch to prepare the proofs and call `verifyBalances`.\n /// This is considering a malicious actor would keep calling `snapBalances` as frequent as possible\n /// to disturb our operations.\n uint64 internal constant SNAP_BALANCES_DELAY = 35 * SLOT_DURATION;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n /// @notice The timestamp of the Beacon chain genesis.\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\n uint64 public immutable BEACON_GENESIS_TIMESTAMP;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// @notice Deposit data for new compounding validators.\n /// @dev A `VERIFIED` deposit can mean 3 separate things:\n /// - a deposit has been processed by the beacon chain and shall be included in the\n /// balance of the next verifyBalances call\n /// - a deposit has been done to a slashed validator and has probably been recovered\n /// back to this strategy. Probably because we can not know for certain. This contract\n /// only detects when the validator has passed its withdrawal epoch. It is close to impossible\n /// to prove with Merkle Proofs that the postponed deposit this contract is responsible for\n /// creating is not present anymore in BeaconChain.state.pending_deposits. This in effect\n /// means that there might be a period where this contract thinks the deposit has been already\n /// returned as ETH balance before it happens. This will result in some days (or weeks)\n /// -> depending on the size of deposit queue of showing a deficit when calling `checkBalance`.\n /// As this only offsets the yield and doesn't cause a critical double-counting we are not addressing\n /// this issue.\n /// - A deposit has been done to the validator, but our deposit has been front run by a malicious\n /// actor. Funds in the deposit this contract makes are not recoverable.\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified\n }\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountGwei Amount of ETH in gwei that has been deposited to the beacon chain deposit contract\n /// @param slot The beacon chain slot number when the deposit has been made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either UNKNOWN, PENDING or VERIFIED\n /// @param withdrawableEpoch The withdrawableEpoch of the validator which is being deposited to.\n /// At deposit time this is set to max default value (FAR_FUTURE_EPOCH). If a deposit has\n /// made to a slashed validator the `withdrawableEpoch` will be set to the epoch of that\n /// validator.\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint32 depositIndex;\n DepositStatus status;\n uint64 withdrawableEpoch;\n }\n /// @notice Restricts to only one deposit to an unverified validator at a time.\n /// This is to limit front-running attacks of deposits to the beacon chain contract.\n ///\n /// @dev The value is set to true when a deposit to a new validator has been done that has\n /// not yet be verified.\n bool public firstDeposit;\n /// @notice Unique identifier of the next validator deposit.\n uint128 public nextDepositID;\n /// @notice Mapping of the deposit ID to the deposit data\n mapping(uint256 => DepositData) public deposits;\n /// @notice List of strategy deposit IDs to a validator.\n /// The list can be for deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n /// The list may not be ordered by time of deposit.\n /// Removed deposits will move the last deposit to the removed index.\n uint256[] public depositList;\n\n enum ValidatorState {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITING, // The validator has been requested to exit or has been verified as forced exit\n EXITED, // The validator has been verified to have a zero balance\n REMOVED, // validator has funds withdrawn to this strategy contract and is removed from the SSV\n INVALID // The validator has been front-run and the withdrawal address is not this strategy\n }\n\n // Validator data\n struct ValidatorData {\n ValidatorState state; // The state of the validator known to this contract\n uint40 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n bytes32[] public verifiedValidators;\n /// @notice Mapping of the hash of the validator's public key to the validator state and index.\n /// Uses the Beacon chain hashing for BLSPubkey which is sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => ValidatorData) public validator;\n\n /// @param blockRoot Beacon chain block root of the snapshot\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n bytes32 blockRoot;\n uint64 timestamp;\n uint128 ethBalance;\n }\n /// @notice Mapping of the block root to the balances at that slot\n Balances public snappedBalance;\n /// @notice The last verified ETH balance of the strategy\n uint256 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[40] private __gap;\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event FirstDepositReset();\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n uint256 indexed depositID,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint40 indexed validatorIndex\n );\n event ValidatorInvalid(bytes32 indexed pubKeyHash);\n event DepositVerified(uint256 indexed depositID, uint256 amountWei);\n event DepositToValidatorExiting(\n uint256 indexed depositID,\n uint256 amountWei,\n uint64 withdrawableEpoch\n );\n event DepositValidatorExited(uint256 indexed depositID, uint256 amountWei);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(bytes32 indexed blockRoot, uint256 ethBalance);\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 ethBalance\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_PROOFS = _beaconProofs;\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\n\n require(\n block.timestamp > _beaconGenesisTimestamp,\n \"Invalid genesis timestamp\"\n );\n }\n\n /**\n *\n * Admin Functions\n *\n */\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Reset the `firstDeposit` flag to false so deposits to unverified validators can be made again.\n function resetFirstDeposit() external onlyGovernor {\n require(firstDeposit, \"No first deposit\");\n\n firstDeposit = false;\n\n emit FirstDepositReset();\n }\n\n /**\n *\n * Validator Management\n *\n */\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validator[pubKeyHash].state == ValidatorState.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validator[pubKeyHash].state = ValidatorState.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n struct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n }\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// The the first deposit to a new validator, the amount must be 1 ETH.\n /// Another deposit of at least 31 ETH is required for the validator to be activated.\n /// This second deposit has to be done after the validator has been verified.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validatorStakeData validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth,reentrancy-no-eth\n function stakeEth(\n ValidatorStakeData calldata validatorStakeData,\n uint64 depositAmountGwei\n ) external onlyRegistrator {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(depositList.length < MAX_DEPOSITS, \"Max deposits\");\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validatorStakeData.pubkey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.VERIFIED),\n \"Not registered or verified\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n if (currentState == ValidatorState.REGISTERED) {\n // Can only have one pending deposit to an unverified validator at a time.\n // This is to limit front-running deposit attacks to a single deposit.\n // The exiting deposit needs to be verified before another deposit can be made.\n // If there was a front-running attack, the validator needs to be verified as invalid\n // and the Governor calls `resetFirstDeposit` to set `firstDeposit` to false.\n require(!firstDeposit, \"Existing first deposit\");\n // Limits the amount of ETH that can be at risk from a front-running deposit attack.\n require(\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n // Limits the number of validator balance proofs to verifyBalances\n require(\n verifiedValidators.length + 1 < MAX_VERIFIED_VALIDATORS,\n \"Max validators\"\n );\n\n // Flag a deposit to an unverified validator so only no other deposits can be made\n // to an unverified validator.\n firstDeposit = true;\n }\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == ValidatorState.REGISTERED) {\n validator[pubKeyHash].state = ValidatorState.STAKED;\n }\n\n /// After the Pectra upgrade the validators have a new restriction when proposing\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\n /// forward. Each slot is created at strict 12 second intervals and those slots can\n /// either have blocks attached to them or not. This way using the block.timestamp\n /// the slot number can easily be calculated.\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\n BEACON_GENESIS_TIMESTAMP) / SLOT_DURATION;\n\n // Store the deposit data for verifyDeposit and verifyBalances\n uint256 depositID = nextDepositID++;\n deposits[depositID] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n slot: depositSlot,\n depositIndex: SafeCast.toUint32(depositList.length),\n status: DepositStatus.PENDING,\n withdrawableEpoch: FAR_FUTURE_EPOCH\n });\n depositList.push(depositID);\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validatorStakeData.pubkey,\n withdrawalCredentials,\n validatorStakeData.signature,\n validatorStakeData.depositDataRoot\n );\n\n emit ETHStaked(\n pubKeyHash,\n depositID,\n validatorStakeData.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth,reentrancy-no-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then only the amount in excess of 32 ETH will be withdrawn.\n /// Only the Registrator can call this function.\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\n /// If no ETH balance, the tx will revert.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n payable\n onlyRegistrator\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Validator full withdrawal could be denied due to multiple reasons:\n // - the validator has not been activated or active long enough\n // (current_epoch < activation_epoch + SHARD_COMMITTEE_PERIOD)\n // - the validator has pending balance to withdraw from a previous partial withdrawal request\n //\n // Meaning that the on-chain to beacon chain full withdrawal request could fail. Instead\n // of adding complexity of verifying if a validator is eligible for a full exit, we allow\n // multiple full withdrawal requests per validator.\n require(\n currentState == ValidatorState.VERIFIED ||\n currentState == ValidatorState.EXITING,\n \"Validator not verified/exiting\"\n );\n\n // If a full withdrawal (validator exit)\n if (amountGwei == 0) {\n // For each staking strategy's deposits\n uint256 depositsCount = depositList.length;\n for (uint256 i = 0; i < depositsCount; ++i) {\n uint256 depositID = depositList[i];\n // Check there is no pending deposits to the exiting validator\n require(\n pubKeyHash != deposits[depositID].pubKeyHash,\n \"Pending deposit\"\n );\n }\n\n // Store the validator state as exiting so no more deposits can be made to it.\n validator[pubKeyHash].state = ValidatorState.EXITING;\n }\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove the validator from the SSV Cluster after:\n /// - the validator has been exited from `validatorWithdrawal` or slashed\n /// - the validator has incorrectly registered and can not be staked to\n /// - the initial deposit was front-run and the withdrawal address is not this strategy's address.\n /// Make sure `validatorWithdrawal` is called with a zero amount and the validator has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.EXITED ||\n currentState == ValidatorState.INVALID,\n \"Validator not regd or exited\"\n );\n\n validator[pubKeyHash].state = ValidatorState.REMOVED;\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /**\n *\n * SSV Management\n *\n */\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be withdrawn from the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /**\n *\n * Beacon Chain Proofs\n *\n */\n\n /// @notice Verifies a validator's index to its public key.\n /// Adds to the list of verified validators if the validator's withdrawal address is this strategy's address.\n /// Marks the validator as invalid and removes the deposit if the withdrawal address is not this strategy's address.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param withdrawalAddress The withdrawal address of the validator which should be this strategy's address.\n /// If the withdrawal address is not this strategy's address, the initial deposit was front-run\n /// and the validator is marked as invalid.\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint40 validatorIndex,\n bytes32 pubKeyHash,\n address withdrawalAddress,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validator[pubKeyHash].state == ValidatorState.STAKED,\n \"Validator not staked\"\n );\n\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key.\n // Also verify the validator's withdrawal credential points to the `withdrawalAddress`.\n IBeaconProofs(BEACON_PROOFS).verifyValidator(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n withdrawalAddress\n );\n\n // Store the validator state as verified\n validator[pubKeyHash] = ValidatorData({\n state: ValidatorState.VERIFIED,\n index: validatorIndex\n });\n\n // If the initial deposit was front-run and the withdrawal address is not this strategy\n if (withdrawalAddress != address(this)) {\n // override the validator state\n validator[pubKeyHash].state = ValidatorState.INVALID;\n\n // Find and remove the deposit as the funds can not be recovered\n uint256 depositCount = depositList.length;\n for (uint256 i = 0; i < depositCount; i++) {\n DepositData memory deposit = deposits[depositList[i]];\n if (deposit.pubKeyHash == pubKeyHash) {\n // next verifyBalances will correctly account for the loss of a front-run\n // deposit. Doing it here accounts for the loss as soon as possible\n lastVerifiedEthBalance -= Math.min(\n lastVerifiedEthBalance,\n uint256(deposit.amountGwei) * 1 gwei\n );\n _removeDeposit(depositList[i], deposit);\n break;\n }\n }\n\n // Leave the `firstDeposit` flag as true so no more deposits to unverified validators can be made.\n // The Governor has to reset the `firstDeposit` to false before another deposit to\n // an unverified validator can be made.\n // The Governor can set a new `validatorRegistrator` if they suspect it has been compromised.\n\n emit ValidatorInvalid(pubKeyHash);\n return;\n }\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(pubKeyHash);\n\n // Reset the firstDeposit flag as the first deposit to an unverified validator has been verified.\n firstDeposit = false;\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n struct FirstPendingDepositSlotProofData {\n uint64 slot;\n bytes proof;\n }\n struct FirstPendingDepositWithdrawableProofData {\n uint64 slot;\n uint40 validatorIndex;\n bytes32 pubKeyHash;\n bytes pendingDepositPubKeyProof;\n bytes withdrawableEpochProof;\n bytes validatorPubKeyProof;\n }\n\n struct StrategyValidatorProofData {\n uint64 withdrawableEpoch;\n bytes withdrawableEpochProof;\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n ///\n /// Important: this function has a limitation where `depositProcessedSlot` and `firstDepositValidatorCreatedSlot`\n /// that is passed by the off-chain verifier requires a slot immediately after them to propose a block otherwise\n /// the `BeaconRoots.parentBlockRoot` will fail. This shouldn't be a problem, since by the current behaviour of\n /// beacon chain only 1%-3% slots don't propose a block.\n /// @param depositID The deposit ID emitted in `ETHStaked` from the `stakeEth` function.\n /// @param depositProcessedSlot Any slot on or after the strategy's deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\n /// set for the next block timestamp in 12 seconds time.\n /// @param firstPendingDeposit a `FirstPendingDepositSlotProofData` struct containing:\n /// - slot: The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be any non-zero value if the deposit queue is empty.\n /// - proof: The merkle proof of the first pending deposit's slot to the beacon block root.\n /// Can be either:\n /// * 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// * 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @param strategyValidatorData a `StrategyValidatorProofData` struct containing:\n /// - withdrawableEpoch: The withdrawable epoch of the validator the strategy is depositing to.\n /// - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the validator the strategy\n /// is depositing to, to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n uint256 depositID,\n uint64 depositProcessedSlot,\n FirstPendingDepositSlotProofData calldata firstPendingDeposit,\n StrategyValidatorProofData calldata strategyValidatorData\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositID];\n ValidatorData memory strategyValidator = validator[deposit.pubKeyHash];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(firstPendingDeposit.slot != 0, \"Zero 1st pending deposit slot\");\n\n // We should allow the verification of deposits for validators that have been marked as exiting\n // to cover this situation:\n // - there are have 2 pending deposits\n // - beacon chain has slashed the validator\n // - when verifyDeposit is called for the first deposit it sets the `withdrawableEpoch` for that\n // deposit and mark validator as exiting\n // - the verifyDeposit also needs to be called for the second deposit so it can have the\n // `withdrawableEpoch` set.\n require(\n strategyValidator.state == ValidatorState.VERIFIED ||\n strategyValidator.state == ValidatorState.EXITING,\n \"Validator not verified/exiting\"\n );\n // The verification slot must be after the deposit's slot.\n // This is needed for when the deposit queue is empty.\n require(deposit.slot < depositProcessedSlot, \"Slot not after deposit\");\n uint64 snapTimestamp = snappedBalance.timestamp;\n\n // This check prevents an accounting error that can happen if:\n // - snapBalances are snapped at the time of T\n // - deposit is processed on the beacon chain after time T and before verifyBalances()\n // - verifyDeposit is called before verifyBalances which removes a deposit from depositList\n // and deposit balance from totalDepositsWei\n // - verifyBalances is called under-reporting the strategy's balance\n require(\n (_calcNextBlockTimestamp(depositProcessedSlot) <= snapTimestamp) ||\n snapTimestamp == 0,\n \"Deposit after balance snapshot\"\n );\n\n // Get the parent beacon block root of the next block which is the block root of the deposit verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 depositBlockRoot = BeaconRoots.parentBlockRoot(\n _calcNextBlockTimestamp(depositProcessedSlot)\n );\n\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n depositBlockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.proof\n );\n\n // Verify the withdrawableEpoch on the validator of the strategy's deposit\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n depositBlockRoot,\n strategyValidator.index,\n strategyValidatorData.withdrawableEpoch,\n strategyValidatorData.withdrawableEpochProof\n );\n\n // If the validator is exiting because it has been slashed\n if (strategyValidatorData.withdrawableEpoch != FAR_FUTURE_EPOCH) {\n // Store the exit epoch in the deposit data\n deposits[depositID].withdrawableEpoch = strategyValidatorData\n .withdrawableEpoch;\n\n emit DepositToValidatorExiting(\n depositID,\n uint256(deposit.amountGwei) * 1 gwei,\n strategyValidatorData.withdrawableEpoch\n );\n\n validator[deposit.pubKeyHash].state = ValidatorState.EXITING;\n\n // Leave the deposit status as PENDING\n return;\n }\n\n // solhint-disable max-line-length\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the deposit queue is empty then our deposit must have been processed on the beacon chain.\n // The deposit slot can be zero for validators consolidating to a compounding validator or 0x01 validator\n // being promoted to a compounding one. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // We can not guarantee that the deposit has been processed in that case.\n // solhint-enable max-line-length\n require(\n deposit.slot < firstPendingDeposit.slot || isDepositQueueEmpty,\n \"Deposit likely not processed\"\n );\n\n // Remove the deposit now it has been verified as processed on the beacon chain.\n _removeDeposit(depositID, deposit);\n\n emit DepositVerified(depositID, uint256(deposit.amountGwei) * 1 gwei);\n }\n\n function _removeDeposit(uint256 depositID, DepositData memory deposit)\n internal\n {\n // After verifying the proof, update the contract storage\n deposits[depositID].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n uint256 lastDeposit = depositList[depositList.length - 1];\n depositList[deposit.depositIndex] = lastDeposit;\n deposits[lastDeposit].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositList.pop();\n }\n\n /// @dev Calculates the timestamp of the next execution block from the given slot.\n /// @param slot The beacon chain slot number used for merkle proof verification.\n function _calcNextBlockTimestamp(uint64 slot)\n internal\n view\n returns (uint64)\n {\n // Calculate the next block timestamp from the slot.\n return SLOT_DURATION * slot + BEACON_GENESIS_TIMESTAMP + SLOT_DURATION;\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block and beacon block root\n /// of the slot that is associated with the previous block.\n ///\n /// When snapping / verifying balance it is of a high importance that there is no\n /// miss-match in respect to ETH that is held by the contract and balances that are\n /// verified on the validators.\n ///\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\n /// constructing a block on the beacon chain consist of:\n /// - process_withdrawals: ETH is deducted from the validator's balance\n /// - process_execution_payload: immediately after the previous step executing all the\n /// transactions\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\n /// contained in the withdrawal credentials of the exited validators\n ///\n /// That means that balance increases which are part of the post-block execution state are\n /// done within the block, but the transaction that are contained within that block can not\n /// see / interact with the balance from the exited validators. Only transactions in the\n /// next block can do that.\n ///\n /// When snap balances is performed the state of the chain is snapped across 2 separate\n /// chain states:\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\n /// will be referred to as Y - 1 as it makes no difference in the argument\n ///\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\n /// miss-counting ETH or much more dangerous double counting of the ETH.\n ///\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\n /// - ETH balance in the contract on block X\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\n /// - ETH balance in validators that are active in slot Y - 1\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\n /// and have their balance visible to transactions in slot Y and corresponding block X\n /// (or sooner)\n ///\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\n ///\n /// *ETH balance in the contract on block X*\n ///\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\n ///\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\n /// would result in double counting the `stakedEth` since it would be present once in the\n /// snapped contract balance and the second time in deposit storage variables.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\n ///\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\n /// contract at block Z. The execution layer doesn't have direct access to the state of\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\n /// and could also be part of the validator balances. It does that by verifying that at\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\n /// the last snap till now all are still in queue. Which ensures they can not be part of\n /// the validator balances in later steps.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in validators that are active in slot Y - 1*\n ///\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\n /// correctness.\n ///\n /// This behaviour is correct.\n ///\n /// *The withdrawn validators*\n ///\n /// The withdrawn validators could have their balances deducted in any slot before slot\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\n /// look at the \"worst case scenario\" where the validator withdrawal is processed in the\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\n /// even if the validator exits at the latest possible time it is paramount that the ETH\n /// balance on the execution layer is recorded in the next block. Correctly accounting\n /// for the withdrawn ETH.\n ///\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\n /// validator balance verification as well as execution layer contract balance snap.\n ///\n /// This behaviour is correct.\n ///\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n function snapBalances() external {\n uint64 currentTimestamp = SafeCast.toUint64(block.timestamp);\n require(\n snappedBalance.timestamp + SNAP_BALANCES_DELAY < currentTimestamp,\n \"Snap too soon\"\n );\n\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(currentTimestamp);\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the snapped balance\n snappedBalance = Balances({\n blockRoot: blockRoot,\n timestamp: currentTimestamp,\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n emit BalancesSnapped(blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct BalanceProofs {\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n /// @param validatorVerificationBlockTimestamp next block's timestamp of a slot that has the first pending\n /// deposit already applied to the validator.\n /// @param firstPendingDeposit a `FirstPendingDepositWithdrawableProofData` struct containing:\n /// - slot: The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero is a good choice.\n /// - validatorIndex: The index of the validator of the first pending deposit.\n /// Can be anything if the deposit queue is empty, but zero is a good choice.\n /// - pubKeyHash: The hash of the public key of the validator of the first pending deposit.\n /// Can be anything if the deposit queue is empty, but 32 zero bytes is a good choice\n /// - pendingDepositPubKeyProof: The merkle proof of the first pending deposit to the beacon block root.\n /// Can be either:\n /// * 40 witness hashes for BeaconBlock.state.PendingDeposits[0].pubKey when the deposit queue is not empty.\n /// * 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the first pending deposit's validator\n /// to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// - validatorPubKeyProof The merkle proof for the public key of the first pending deposit's validator\n /// to the this witness hash of withdrawableEpochProof.\n /// This is 2 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param balanceProofs a `BalanceProofs` struct containing the following:\n /// - balancesContainerRoot: The merkle root of the balances container\n /// - balancesContainerProof: The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// - validatorBalanceLeaves: Array of leaf nodes containing the validator balance with three other balances.\n /// - validatorBalanceProofs: Array of merkle proofs for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(\n uint64 validatorVerificationBlockTimestamp,\n FirstPendingDepositWithdrawableProofData calldata firstPendingDeposit,\n BalanceProofs calldata balanceProofs\n ) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalance;\n // Check the balances are the latest\n require(balancesMem.timestamp > 0, \"No snapped balances\");\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n balanceProofs.validatorBalanceProofs.length ==\n verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n balanceProofs.validatorBalanceLeaves.length ==\n verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n balancesMem.blockRoot,\n balanceProofs.balancesContainerRoot,\n balanceProofs.balancesContainerProof\n );\n\n // for each validator in reverse order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n balanceProofs.balancesContainerRoot,\n balanceProofs.validatorBalanceLeaves[i],\n balanceProofs.validatorBalanceProofs[i],\n validator[verifiedValidators[i]].index\n );\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n // This could have been in VERIFIED or EXITING state\n validator[verifiedValidators[i]].state = ValidatorState\n .EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += validatorBalanceGwei * 1 gwei;\n }\n }\n\n uint256 depositsCount = depositList.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification.\n // This section is after the validator balance verifications so an exited validator will be marked\n // as EXITED before the deposits are verified. If there was a deposit to an exited validator\n // then the deposit can only be removed once the validator is fully exited.\n // It is possible that validator fully exits and a postponed deposit to an exited validator increases\n // its balance again. In such case the contract will erroneously consider a deposit applied before it\n // has been applied on the beacon chain showing a smaller than real `totalValidatorBalance`.\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n balancesMem.blockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.pubKeyHash,\n firstPendingDeposit.pendingDepositPubKeyProof\n );\n\n // If there are no deposits in the beacon chain queue then our deposits must have been processed.\n // If the deposits have been processed, each deposit will need to be verified with `verifyDeposit`\n require(!isDepositQueueEmpty, \"Deposits have been processed\");\n\n // The verification of the validator the first pending deposit is for must be on or after when\n // `snapBalances` was called.\n require(\n balancesMem.timestamp <= validatorVerificationBlockTimestamp,\n \"Invalid validator timestamp\"\n );\n\n // Verify the validator of the first pending deposit is not exiting by checking\n // the withdrawable epoch is far into the future.\n // If it is exiting we can't be sure this deposit has not been postponed in the deposit queue.\n // Hence we can not verify if the strategy's deposit has been processed or not.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n // Get the parent beacon block root of the next block which is\n // the block root of the validator verification slot.\n // This will revert if the slot after the verification slot was missed.\n BeaconRoots.parentBlockRoot(\n validatorVerificationBlockTimestamp\n ),\n firstPendingDeposit.validatorIndex,\n firstPendingDeposit.pubKeyHash,\n // Validator is not exiting\n FAR_FUTURE_EPOCH,\n firstPendingDeposit.withdrawableEpochProof,\n firstPendingDeposit.validatorPubKeyProof\n );\n\n // solhint-disable max-line-length\n // If a validator is converted from a sweeping validator to a compounding validator, any balance in excess\n // of the min 32 ETH is put in the pending deposit queue. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // This will have a slot value of zero unfortunately.\n // We can not prove the strategy's deposits are still pending with a zero slot value so revert the tx.\n // Another snapBalances will need to be taken that does not have consolidation deposits at the front of the\n // beacon chain deposit queue.\n // solhint-enable max-line-length\n require(\n firstPendingDeposit.slot > 0,\n \"Invalid first pending deposit\"\n );\n\n // Calculate the epoch for the slot before snapBalances which is 12 seconds before the snap timestamp.\n // The block root of the snapped balances is the parent block root so is for the previous slot.\n // If the parent slot was missed, then the block root will belong to the last non-missed slot. This could\n // result in a verificationEpoch that is 1 more than the actual epoch of the slot before snapBalances.\n // This means verifyBalances fails with `Deposit likely processed` and a new snapBalances has to be taken\n // after the exiting validator's balance has been swept.\n uint64 verificationEpoch = (SafeCast.toUint64(\n balancesMem.timestamp - 12\n ) - BEACON_GENESIS_TIMESTAMP) / (SLOT_DURATION * SLOTS_PER_EPOCH);\n\n // For each staking strategy's deposit.\n // Iterate in reverse order so we can pop off deposits at the end of the storage array.\n for (uint256 i = depositsCount; i > 0; ) {\n --i;\n uint256 depositID = depositList[i];\n DepositData memory depositData = deposits[depositID];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit slot is before the slot of the staking strategy's deposit.\n // If the deposit has been processed, it will need to be verified with `verifyDeposit`.\n // OR the deposit is to an exiting validator so check it is still not withdrawable.\n // If the validator is not withdrawable, then the deposit can not have been processed yet.\n // If the validator is now withdrawable, then the deposit may have been processed. The strategy\n // now has to wait until the validator's balance is verified to be zero.\n // OR the validator has exited and the deposit is now verified as processed.\n require(\n firstPendingDeposit.slot < depositData.slot ||\n (verificationEpoch < depositData.withdrawableEpoch &&\n depositData.withdrawableEpoch !=\n FAR_FUTURE_EPOCH) ||\n validator[depositData.pubKeyHash].state ==\n ValidatorState.EXITED,\n \"Deposit likely processed\"\n );\n\n // Remove the deposit if the validator has exited.\n if (\n validator[depositData.pubKeyHash].state ==\n ValidatorState.EXITED\n ) {\n _removeDeposit(depositID, depositData);\n\n // Reduce the count of deposits that needs to be iterated over\n depositsCount -= 1;\n\n emit DepositValidatorExited(\n depositID,\n uint256(depositData.amountGwei) * 1 gwei\n );\n\n // Skip to the next deposit as the deposit amount is now in the strategy's ETH balance\n continue;\n }\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei += uint256(depositData.amountGwei) * 1 gwei;\n }\n }\n\n // Store the verified balance in storage\n lastVerifiedEthBalance =\n totalDepositsWei +\n totalValidatorBalance +\n balancesMem.ethBalance;\n // Reset the last snap timestamp so a new snapBalances has to be made\n snappedBalance.timestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n require(pubKey.length == 48, \"Invalid public key length\");\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /**\n *\n * WETH and ETH Accounting\n *\n */\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n // The min is required as more WETH can be withdrawn than deposited\n // as the strategy earns consensus and execution rewards.\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance.\n // The ETH balance in this strategy contract can be more than the last verified ETH balance\n // due to partial withdrawals or full exits being processed by the beacon chain since the last snapBalances.\n // It can also happen from execution rewards (MEV) or ETH donations.\n lastVerifiedEthBalance -= Math.min(lastVerifiedEthBalance, _ethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n snappedBalance.timestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += _wethAmount;\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n snappedBalance.timestamp = 0;\n }\n\n /**\n *\n * View Functions\n *\n */\n\n /// @notice Returns the number of deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n function depositListLength() external view returns (uint256) {\n return depositList.length;\n }\n\n /// @notice Returns the number of verified validators.\n function verifiedValidatorsLength() external view returns (uint256) {\n return verifiedValidators.length;\n }\n}\n" + }, + "contracts/strategies/NativeStaking/FeeAccumulator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\n/**\n * @title Fee Accumulator for Native Staking SSV Strategy\n * @notice Receives execution rewards which includes tx fees and\n * MEV rewards like tx priority and tx ordering.\n * It does NOT include swept ETH from beacon chain consensus rewards or full validator withdrawals.\n * @author Origin Protocol Inc\n */\ncontract FeeAccumulator {\n /// @notice The address of the Native Staking Strategy\n address public immutable STRATEGY;\n\n event ExecutionRewardsCollected(address indexed strategy, uint256 amount);\n\n /**\n * @param _strategy Address of the Native Staking Strategy\n */\n constructor(address _strategy) {\n STRATEGY = _strategy;\n }\n\n /**\n * @notice sends all ETH in this FeeAccumulator contract to the Native Staking Strategy.\n * @return eth The amount of execution rewards that were sent to the Native Staking Strategy\n */\n function collect() external returns (uint256 eth) {\n require(msg.sender == STRATEGY, \"Caller is not the Strategy\");\n\n eth = address(this).balance;\n if (eth > 0) {\n // Send the ETH to the Native Staking Strategy\n Address.sendValue(payable(STRATEGY), eth);\n\n emit ExecutionRewardsCollected(STRATEGY, eth);\n }\n }\n\n /**\n * @dev Accept ETH\n */\n receive() external payable {}\n}\n" + }, + "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { FeeAccumulator } from \"./FeeAccumulator.sol\";\nimport { ValidatorAccountant } from \"./ValidatorAccountant.sol\";\nimport { ISSVNetwork } from \"../../interfaces/ISSVNetwork.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/// @title Native Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract NativeStakingSSVStrategy is\n ValidatorAccountant,\n InitializableAbstractStrategy\n{\n using SafeERC20 for IERC20;\n\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n /// @notice Fee collector address\n /// @dev this address will receive maximal extractable value (MEV) rewards. These are\n /// rewards for arranging transactions in a way that benefits the validator.\n address payable public immutable FEE_ACCUMULATOR_ADDRESS;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[49] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n /// @param _feeAccumulator Address of the fee accumulator receiving execution layer validator rewards\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n uint256 _maxValidators,\n address _feeAccumulator,\n address _beaconChainDepositContract\n )\n InitializableAbstractStrategy(_baseConfig)\n ValidatorAccountant(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _maxValidators\n )\n {\n SSV_TOKEN = _ssvToken;\n FEE_ACCUMULATOR_ADDRESS = payable(_feeAccumulator);\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract.\n /// @param _rewardTokenAddresses Address of reward token for platform\n /// @param _assets Addresses of initial supported assets\n /// @param _pTokens Platform Token corresponding addresses\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n\n // Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\n FEE_ACCUMULATOR_ADDRESS\n );\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n /// @param _asset Address of asset to deposit. Has to be WETH.\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n depositedWethAccountedFor += _amount;\n _deposit(_asset, _amount);\n }\n\n /// @dev Deposit WETH to this strategy so it can later be staked into a validator.\n /// @param _asset Address of WETH\n /// @param _amount Amount of WETH to deposit\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n /*\n * We could do a check here that would revert when \"_amount % 32 ether != 0\". With the idea of\n * not allowing deposits that will result in WETH sitting on the strategy after all the possible batches\n * of 32ETH have been staked.\n * But someone could mess with our strategy by sending some WETH to it. And we might want to deposit just\n * enough WETH to add it up to 32 so it can be staked. For that reason the check is left out.\n *\n * WETH sitting on the strategy won't interfere with the accounting since accounting only operates on ETH.\n */\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n depositedWethAccountedFor = wethBalance;\n\n _deposit(WETH, newWeth);\n }\n }\n\n /// @notice Withdraw WETH from this contract. Used only if some WETH for is lingering on the contract.\n /// That can happen when:\n /// - after mints if the strategy is the default\n /// - time between depositToStrategy and stakeEth\n /// - the deposit was not a multiple of 32 WETH\n /// - someone sent WETH directly to this contract\n /// Will NOT revert if the strategy is paused from an accounting failure.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n _wethWithdrawn(_amount);\n\n IERC20(_asset).safeTransfer(_recipient, _amount);\n emit Withdrawal(_asset, address(0), _amount);\n }\n\n /// @notice transfer all WETH deposits back to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `exitSsvValidator` and `removeSsvValidator` operations.\n /// This does not withdraw any execution rewards from the FeeAccumulator or\n /// consensus rewards in this strategy.\n /// Any ETH in this strategy that was swept from a full validator withdrawal will not be withdrawn.\n /// ETH from full validator withdrawals is sent to the Vault using `doAccounting`.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n if (wethBalance > 0) {\n _withdraw(vaultAddress, WETH, wethBalance);\n }\n }\n\n /// @notice Returns the total value of (W)ETH that is staked to the validators\n /// and WETH deposits that are still to be staked.\n /// This does not include ETH from consensus rewards sitting in this strategy\n /// or ETH from MEV rewards in the FeeAccumulator. These rewards are harvested\n /// and sent to the Dripper so will eventually be sent to the Vault as WETH.\n /// @param _asset Address of weth asset\n /// @return balance Total value of (W)ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n balance =\n // add the ETH that has been staked in validators\n activeDepositedValidators *\n FULL_STAKE +\n // add the WETH in the strategy from deposits that are still to be staked\n IERC20(WETH).balanceOf(address(this));\n }\n\n function pause() external onlyStrategist {\n _pause();\n }\n\n /// @notice Returns bool indicating whether asset is supported by strategy.\n /// @param _asset The address of the asset token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for deposits\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\n function setFeeRecipient() external {\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\n FEE_ACCUMULATOR_ADDRESS\n );\n }\n\n /**\n * @notice Only accept ETH from the FeeAccumulator and the WETH contract - required when\n * unwrapping WETH just before staking it to the validator.\n * The strategy will also receive ETH from the priority fees of transactions when producing blocks\n * as defined in EIP-1559.\n * The tx fees come from the Beacon chain so do not need any EVM level permissions to receive ETH.\n * The tx fees are paid with each block produced. They are not included in the consensus rewards\n * which are periodically swept from the validators to this strategy.\n * For accounting purposes, the priority fees of transactions will be considered consensus rewards\n * and will be included in the AccountingConsensusRewards event.\n * @dev don't want to receive donations from anyone else as donations over the fuse limits will\n * mess with the accounting of the consensus rewards and validator full withdrawals.\n */\n receive() external payable {\n require(\n msg.sender == FEE_ACCUMULATOR_ADDRESS || msg.sender == WETH,\n \"Eth not from allowed contracts\"\n );\n }\n\n /***************************************\n Internal functions\n ****************************************/\n\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Convert accumulated ETH to WETH and send to the Harvester.\n /// Will revert if the strategy is paused for accounting.\n function _collectRewardTokens() internal override whenNotPaused {\n // collect ETH from execution rewards from the fee accumulator\n uint256 executionRewards = FeeAccumulator(FEE_ACCUMULATOR_ADDRESS)\n .collect();\n\n // total ETH rewards to be harvested = execution rewards + consensus rewards\n uint256 ethRewards = executionRewards + consensusRewards;\n\n require(\n address(this).balance >= ethRewards,\n \"Insufficient eth balance\"\n );\n\n if (ethRewards > 0) {\n // reset the counter keeping track of beacon chain consensus rewards\n consensusRewards = 0;\n\n // Convert ETH rewards to WETH\n IWETH9(WETH).deposit{ value: ethRewards }();\n\n IERC20(WETH).safeTransfer(harvesterAddress, ethRewards);\n emit RewardTokenCollected(harvesterAddress, WETH, ethRewards);\n }\n }\n\n /// @dev emits Withdrawal event from NativeStakingSSVStrategy\n function _wethWithdrawnToVault(uint256 _amount) internal override {\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _wethWithdrawn(uint256 _amount) internal override {\n /* In an ideal world we wouldn't need to reduce the deduction amount when the\n * depositedWethAccountedFor is smaller than the _amount.\n *\n * The reason this is required is that a malicious actor could sent WETH directly\n * to this contract and that would circumvent the increase of depositedWethAccountedFor\n * property. When the ETH would be staked the depositedWethAccountedFor amount could\n * be deducted so much that it would be negative.\n */\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n }\n}\n" + }, + "contracts/strategies/NativeStaking/ValidatorAccountant.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ValidatorRegistrator } from \"./ValidatorRegistrator.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\n\n/// @title Validator Accountant\n/// @notice Attributes the ETH swept from beacon chain validators to this strategy contract\n/// as either full or partial withdrawals. Partial withdrawals being consensus rewards.\n/// Full withdrawals are from exited validators.\n/// @author Origin Protocol Inc\nabstract contract ValidatorAccountant is ValidatorRegistrator {\n /// @notice The minimum amount of blocks that need to pass between two calls to manuallyFixAccounting\n uint256 public constant MIN_FIX_ACCOUNTING_CADENCE = 7200; // 1 day\n\n /// @notice Keeps track of the total consensus rewards swept from the beacon chain\n uint256 public consensusRewards;\n\n /// @notice start of fuse interval\n uint256 public fuseIntervalStart;\n /// @notice end of fuse interval\n uint256 public fuseIntervalEnd;\n /// @notice last block number manuallyFixAccounting has been called\n uint256 public lastFixAccountingBlockNumber;\n\n uint256[49] private __gap;\n\n event FuseIntervalUpdated(uint256 start, uint256 end);\n event AccountingFullyWithdrawnValidator(\n uint256 noOfValidators,\n uint256 remainingValidators,\n uint256 wethSentToVault\n );\n event AccountingValidatorSlashed(\n uint256 remainingValidators,\n uint256 wethSentToVault\n );\n event AccountingConsensusRewards(uint256 amount);\n\n event AccountingManuallyFixed(\n int256 validatorsDelta,\n int256 consensusRewardsDelta,\n uint256 wethToVault\n );\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n uint256 _maxValidators\n )\n ValidatorRegistrator(\n _wethAddress,\n _vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _maxValidators\n )\n {}\n\n /// @notice set fuse interval values\n function setFuseInterval(\n uint256 _fuseIntervalStart,\n uint256 _fuseIntervalEnd\n ) external onlyGovernor {\n require(\n _fuseIntervalStart < _fuseIntervalEnd &&\n _fuseIntervalEnd < 32 ether &&\n _fuseIntervalEnd - _fuseIntervalStart >= 4 ether,\n \"Incorrect fuse interval\"\n );\n\n fuseIntervalStart = _fuseIntervalStart;\n fuseIntervalEnd = _fuseIntervalEnd;\n\n emit FuseIntervalUpdated(_fuseIntervalStart, _fuseIntervalEnd);\n }\n\n /* solhint-disable max-line-length */\n /// This notion page offers a good explanation of how the accounting functions\n /// https://www.notion.so/originprotocol/Limited-simplified-native-staking-accounting-67a217c8420d40678eb943b9da0ee77d\n /// In short, after dividing by 32, if the ETH remaining on the contract falls between 0 and fuseIntervalStart,\n /// the accounting function will treat that ETH as Beacon chain consensus rewards.\n /// On the contrary, if after dividing by 32, the ETH remaining on the contract falls between fuseIntervalEnd and 32,\n /// the accounting function will treat that as a validator slashing.\n /// @notice Perform the accounting attributing beacon chain ETH to either full or partial withdrawals. Returns true when\n /// accounting is valid and fuse isn't \"blown\". Returns false when fuse is blown.\n /// @dev This function could in theory be permission-less but lets allow only the Registrator (Defender Action) to call it\n /// for now.\n /// @return accountingValid true if accounting was successful, false if fuse is blown\n /* solhint-enable max-line-length */\n function doAccounting()\n external\n onlyRegistrator\n whenNotPaused\n nonReentrant\n returns (bool accountingValid)\n {\n // pause the accounting on failure\n accountingValid = _doAccounting(true);\n }\n\n // slither-disable-start reentrancy-eth\n function _doAccounting(bool pauseOnFail)\n internal\n returns (bool accountingValid)\n {\n if (address(this).balance < consensusRewards) {\n return _failAccounting(pauseOnFail);\n }\n\n // Calculate all the new ETH that has been swept to the contract since the last accounting\n uint256 newSweptETH = address(this).balance - consensusRewards;\n accountingValid = true;\n\n // send the ETH that is from fully withdrawn validators to the Vault\n if (newSweptETH >= FULL_STAKE) {\n uint256 fullyWithdrawnValidators;\n // explicitly cast to uint256 as we want to round to a whole number of validators\n fullyWithdrawnValidators = uint256(newSweptETH / FULL_STAKE);\n activeDepositedValidators -= fullyWithdrawnValidators;\n\n uint256 wethToVault = FULL_STAKE * fullyWithdrawnValidators;\n IWETH9(WETH).deposit{ value: wethToVault }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, wethToVault);\n _wethWithdrawnToVault(wethToVault);\n\n emit AccountingFullyWithdrawnValidator(\n fullyWithdrawnValidators,\n activeDepositedValidators,\n wethToVault\n );\n }\n\n uint256 ethRemaining = address(this).balance - consensusRewards;\n // should be less than a whole validator stake\n require(ethRemaining < FULL_STAKE, \"Unexpected accounting\");\n\n // If no Beacon chain consensus rewards swept\n if (ethRemaining == 0) {\n // do nothing\n return accountingValid;\n } else if (ethRemaining < fuseIntervalStart) {\n // Beacon chain consensus rewards swept (partial validator withdrawals)\n // solhint-disable-next-line reentrancy\n consensusRewards += ethRemaining;\n emit AccountingConsensusRewards(ethRemaining);\n } else if (ethRemaining > fuseIntervalEnd) {\n // Beacon chain consensus rewards swept but also a slashed validator fully exited\n IWETH9(WETH).deposit{ value: ethRemaining }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, ethRemaining);\n activeDepositedValidators -= 1;\n\n _wethWithdrawnToVault(ethRemaining);\n\n emit AccountingValidatorSlashed(\n activeDepositedValidators,\n ethRemaining\n );\n }\n // Oh no... Fuse is blown. The Strategist needs to adjust the accounting values.\n else {\n return _failAccounting(pauseOnFail);\n }\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @dev pause any further accounting if required and return false\n function _failAccounting(bool pauseOnFail)\n internal\n returns (bool accountingValid)\n {\n // pause if not already\n if (pauseOnFail) {\n _pause();\n }\n // fail the accounting\n accountingValid = false;\n }\n\n /// @notice Allow the Strategist to fix the accounting of this strategy and unpause.\n /// @param _validatorsDelta adjust the active validators by up to plus three or minus three\n /// @param _consensusRewardsDelta adjust the accounted for consensus rewards up or down\n /// @param _ethToVaultAmount the amount of ETH that gets wrapped into WETH and sent to the Vault\n /// @dev There is a case when a validator(s) gets slashed so much that the eth swept from\n /// the beacon chain enters the fuse area and there are no consensus rewards on the contract\n /// to \"dip into\"/use. To increase the amount of unaccounted ETH over the fuse end interval\n /// we need to reduce the amount of active deposited validators and immediately send WETH\n /// to the vault, so it doesn't interfere with further accounting.\n function manuallyFixAccounting(\n int256 _validatorsDelta,\n int256 _consensusRewardsDelta,\n uint256 _ethToVaultAmount\n ) external onlyStrategist whenPaused nonReentrant {\n require(\n lastFixAccountingBlockNumber + MIN_FIX_ACCOUNTING_CADENCE <\n block.number,\n \"Fix accounting called too soon\"\n );\n require(\n _validatorsDelta >= -3 &&\n _validatorsDelta <= 3 &&\n // new value must be positive\n int256(activeDepositedValidators) + _validatorsDelta >= 0,\n \"Invalid validatorsDelta\"\n );\n require(\n _consensusRewardsDelta >= -332 ether &&\n _consensusRewardsDelta <= 332 ether &&\n // new value must be positive\n int256(consensusRewards) + _consensusRewardsDelta >= 0,\n \"Invalid consensusRewardsDelta\"\n );\n require(_ethToVaultAmount <= 32 ether * 3, \"Invalid wethToVaultAmount\");\n\n activeDepositedValidators = uint256(\n int256(activeDepositedValidators) + _validatorsDelta\n );\n consensusRewards = uint256(\n int256(consensusRewards) + _consensusRewardsDelta\n );\n lastFixAccountingBlockNumber = block.number;\n if (_ethToVaultAmount > 0) {\n IWETH9(WETH).deposit{ value: _ethToVaultAmount }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, _ethToVaultAmount);\n _wethWithdrawnToVault(_ethToVaultAmount);\n }\n\n emit AccountingManuallyFixed(\n _validatorsDelta,\n _consensusRewardsDelta,\n _ethToVaultAmount\n );\n\n // rerun the accounting to see if it has now been fixed.\n // Do not pause the accounting on failure as it is already paused\n require(_doAccounting(false), \"Fuse still blown\");\n\n // unpause since doAccounting was successful\n _unpause();\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n /// @dev allows for NativeStakingSSVStrategy contract to emit the Withdrawal event\n function _wethWithdrawnToVault(uint256 _amount) internal virtual;\n}\n" + }, + "contracts/strategies/NativeStaking/ValidatorRegistrator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Registrator of the validators\n * @notice This contract implements all the required functionality to register, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract ValidatorRegistrator is Governable, Pausable {\n /// @notice The maximum amount of ETH that can be staked by a validator\n /// @dev this can change in the future with EIP-7251, Increase the MAX_EFFECTIVE_BALANCE\n uint256 public constant FULL_STAKE = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Maximum number of validators that can be registered in this strategy\n uint256 public immutable MAX_VALIDATORS;\n\n /// @notice Address of the registrator - allowed to register, exit and remove validators\n address public validatorRegistrator;\n /// @notice The number of validators that have 32 (!) ETH actively deposited. When a new deposit\n /// to a validator happens this number increases, when a validator exit is detected this number\n /// decreases.\n uint256 public activeDepositedValidators;\n /// @notice State of the validators keccak256(pubKey) => state\n mapping(bytes32 => VALIDATOR_STATE) public validatorsStates;\n /// @notice The account that is allowed to modify stakeETHThreshold and reset stakeETHTally\n address public stakingMonitor;\n /// @notice Amount of ETH that can be staked before staking on the contract is suspended\n /// and the `stakingMonitor` needs to approve further staking by calling `resetStakeETHTally`\n uint256 public stakeETHThreshold;\n /// @notice Amount of ETH that has been staked since the `stakingMonitor` last called `resetStakeETHTally`.\n /// This can not go above `stakeETHThreshold`.\n uint256 public stakeETHTally;\n\n // For future use\n uint256[47] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n EXITING, // exit message has been posted and validator is in the process of exiting\n EXIT_COMPLETE // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event ETHStaked(bytes32 indexed pubKeyHash, bytes pubKey, uint256 amount);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event SSVValidatorExitInitiated(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event SSVValidatorExitCompleted(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event StakeETHThresholdChanged(uint256 amount);\n event StakeETHTallyReset();\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(\n msg.sender == validatorRegistrator,\n \"Caller is not the Registrator\"\n );\n _;\n }\n\n /// @dev Throws if called by any account other than the Staking monitor\n modifier onlyStakingMonitor() {\n require(msg.sender == stakingMonitor, \"Caller is not the Monitor\");\n _;\n }\n\n /// @dev Throws if called by any account other than the Strategist\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(VAULT_ADDRESS).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n uint256 _maxValidators\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n MAX_VALIDATORS = _maxValidators;\n }\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Set the address of the staking monitor that is allowed to reset stakeETHTally\n function setStakingMonitor(address _address) external onlyGovernor {\n stakingMonitor = _address;\n emit StakingMonitorChanged(_address);\n }\n\n /// @notice Set the amount of ETH that can be staked before staking monitor\n // needs to a approve further staking by resetting the stake ETH tally\n function setStakeETHThreshold(uint256 _amount) external onlyGovernor {\n stakeETHThreshold = _amount;\n emit StakeETHThresholdChanged(_amount);\n }\n\n /// @notice Reset the stakeETHTally\n function resetStakeETHTally() external onlyStakingMonitor {\n stakeETHTally = 0;\n emit StakeETHTallyReset();\n }\n\n /// @notice Stakes WETH to the node validators\n /// @param validators A list of validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n // slither-disable-start reentrancy-eth\n function stakeEth(ValidatorStakeData[] calldata validators)\n external\n onlyRegistrator\n whenNotPaused\n nonReentrant\n {\n uint256 requiredETH = validators.length * FULL_STAKE;\n\n // Check there is enough WETH from the deposits sitting in this strategy contract\n require(\n requiredETH <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(\n activeDepositedValidators + validators.length <= MAX_VALIDATORS,\n \"Max validators reached\"\n );\n\n require(\n stakeETHTally + requiredETH <= stakeETHThreshold,\n \"Staking ETH over threshold\"\n );\n stakeETHTally += requiredETH;\n\n // Convert required ETH from WETH\n IWETH9(WETH).withdraw(requiredETH);\n _wethWithdrawn(requiredETH);\n\n /* 0x01 to indicate that withdrawal credentials will contain an EOA address that the sweeping function\n * can sweep funds to.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x01),\n bytes11(0),\n address(this)\n );\n\n // For each validator\n for (uint256 i = 0; i < validators.length; ++i) {\n bytes32 pubKeyHash = keccak256(validators[i].pubkey);\n\n require(\n validatorsStates[pubKeyHash] == VALIDATOR_STATE.REGISTERED,\n \"Validator not registered\"\n );\n\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: FULL_STAKE\n }(\n validators[i].pubkey,\n withdrawalCredentials,\n validators[i].signature,\n validators[i].depositDataRoot\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.STAKED;\n\n emit ETHStaked(pubKeyHash, validators[i].pubkey, FULL_STAKE);\n }\n // save gas by changing this storage variable only once rather each time in the loop.\n activeDepositedValidators += validators.length;\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Registers a new validator in the SSV Cluster.\n /// Only the registrator can call this function.\n /// @param publicKeys The public keys of the validators\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for each validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function registerSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n require(\n publicKeys.length == sharesData.length,\n \"Pubkey sharesData mismatch\"\n );\n // Check each public key has not already been used\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n emit SSVValidatorRegistered(pubKeyHash, publicKeys[i], operatorIds);\n }\n\n ISSVNetwork(SSV_NETWORK).bulkRegisterValidator(\n publicKeys,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n }\n\n /// @notice Exit validators from the Beacon chain.\n /// The staked ETH will eventually swept to this native staking strategy.\n /// Only the registrator can call this function.\n /// @param publicKeys List of SSV validator public keys\n /// @param operatorIds The operator IDs of the SSV Cluster\n function exitSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external onlyRegistrator whenNotPaused nonReentrant {\n ISSVNetwork(SSV_NETWORK).bulkExitValidator(publicKeys, operatorIds);\n\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n\n // Check each validator has not already been staked.\n // This would normally be done before the external call but is after\n // so only one for loop of the validators is needed.\n require(\n currentState == VALIDATOR_STATE.STAKED,\n \"Validator not staked\"\n );\n\n // Store the new validator state\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXITING;\n\n emit SSVValidatorExitInitiated(\n pubKeyHash,\n publicKeys[i],\n operatorIds\n );\n }\n }\n\n /// @notice Remove validators from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKeys List of SSV validator public keys\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function removeSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused nonReentrant {\n ISSVNetwork(SSV_NETWORK).bulkRemoveValidator(\n publicKeys,\n operatorIds,\n cluster\n );\n\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n\n // Check each validator is either registered or exited.\n // This would normally be done before the external call but is after\n // so only one for loop of the validators is needed.\n require(\n currentState == VALIDATOR_STATE.EXITING ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exiting\"\n );\n\n // Store the new validator state\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXIT_COMPLETE;\n\n emit SSVValidatorExitCompleted(\n pubKeyHash,\n publicKeys[i],\n operatorIds\n );\n }\n }\n\n /// @notice Deposits more SSV Tokens to the SSV Network contract which is used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// uses \"onlyStrategist\" modifier so continuous front-running can't DOS our maintenance service\n /// that tries to top up SSV tokens.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function depositSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyStrategist {\n ISSVNetwork(SSV_NETWORK).deposit(\n address(this),\n operatorIds,\n ssvAmount,\n cluster\n );\n }\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _wethWithdrawn(uint256 _amount) internal virtual;\n}\n" + }, + "contracts/strategies/plume/RoosterAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Rooster AMO strategy\n * @author Origin Protocol Inc\n * @custom:security-contact security@originprotocol.com\n */\nimport { Math as MathRooster } from \"../../../lib/rooster/v2-common/libraries/Math.sol\";\nimport { Math as Math_v5 } from \"../../../lib/rooster/openzeppelin-custom/contracts/utils/math/Math.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { IMaverickV2Pool } from \"../../interfaces/plume/IMaverickV2Pool.sol\";\nimport { IMaverickV2Quoter } from \"../../interfaces/plume/IMaverickV2Quoter.sol\";\nimport { IMaverickV2LiquidityManager } from \"../../interfaces/plume/IMaverickV2LiquidityManager.sol\";\nimport { IMaverickV2PoolLens } from \"../../interfaces/plume/IMaverickV2PoolLens.sol\";\nimport { IMaverickV2Position } from \"../../interfaces/plume/IMaverickV2Position.sol\";\nimport { IVotingDistributor } from \"../../interfaces/plume/IVotingDistributor.sol\";\nimport { IPoolDistributor } from \"../../interfaces/plume/IPoolDistributor.sol\";\n// importing custom version of rooster TickMath because of dependency collision. Maverick uses\n// a newer OpenZepplin Math library with functionality that is not present in 4.4.2 (the one we use)\nimport { TickMath } from \"../../../lib/rooster/v2-common/libraries/TickMath.sol\";\n\ncontract RoosterAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n using SafeCast for uint256;\n\n /***************************************\n Storage slot members\n ****************************************/\n\n /// @notice NFT tokenId of the liquidity position\n ///\n /// @dev starts with value of 1 and can not be 0\n // solhint-disable-next-line max-line-length\n /// https://github.com/rooster-protocol/rooster-contracts/blob/fbfecbc519e4495b12598024a42630b4a8ea4489/v2-common/contracts/base/Nft.sol#L14\n uint256 public tokenId;\n /// @dev Minimum amount of tokens the strategy would be able to withdraw from the pool.\n /// minimum amount of tokens are withdrawn at a 1:1 price\n /// Important: Underlying assets contains only assets that are deposited in the underlying Rooster pool.\n /// WETH or OETH held by this contract is not accounted for in underlying assets\n uint256 public underlyingAssets;\n /// @notice Marks the start of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareStart;\n /// @notice Marks the end of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareEnd;\n /// @dev reserved for inheritance\n int256[46] private __reserved;\n\n /***************************************\n Constants, structs and events\n ****************************************/\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the OETH token contract\n address public immutable OETH;\n /// @notice the underlying AMO Maverick (Rooster) pool\n IMaverickV2Pool public immutable mPool;\n /// @notice the Liquidity manager used to add liquidity to the pool\n IMaverickV2LiquidityManager public immutable liquidityManager;\n /// @notice the Maverick V2 poolLens\n ///\n /// @dev only used to provide the pool's current sqrtPrice\n IMaverickV2PoolLens public immutable poolLens;\n /// @notice the Maverick V2 position\n ///\n /// @dev provides details of the NFT LP position and offers functions to\n /// remove the liquidity.\n IMaverickV2Position public immutable maverickPosition;\n /// @notice the Maverick Quoter\n IMaverickV2Quoter public immutable quoter;\n /// @notice the Maverick Voting Distributor\n IVotingDistributor public immutable votingDistributor;\n /// @notice the Maverick Pool Distributor\n IPoolDistributor public immutable poolDistributor;\n\n /// @notice sqrtPriceTickLower\n /// @dev tick lower represents the lower price of OETH priced in WETH. Meaning the pool\n /// offers more than 1 OETH for 1 WETH. In other terms to get 1 OETH the swap needs to offer 0.9999 WETH\n /// this is where purchasing OETH with WETH within the liquidity position is the cheapest.\n ///\n /// _____________________\n /// | | |\n /// | WETH | OETH |\n /// | | |\n /// | | |\n /// --------- * ---- * ---------- * ---------\n /// currentPrice\n /// sqrtPriceHigher-(1:1 parity)\n /// sqrtPriceLower\n ///\n ///\n /// Price is defined as price of token1 in terms of token0. (token1 / token0)\n /// @notice sqrtPriceTickLower - OETH is priced 0.9999 WETH\n uint256 public immutable sqrtPriceTickLower;\n /// @notice sqrtPriceTickHigher\n /// @dev tick higher represents 1:1 price parity of WETH to OETH\n uint256 public immutable sqrtPriceTickHigher;\n /// @dev price at parity (in OETH this is equal to sqrtPriceTickHigher)\n uint256 public immutable sqrtPriceAtParity;\n /// @notice The tick where the strategy deploys the liquidity to\n int32 public constant TICK_NUMBER = -1;\n /// @notice Minimum liquidity that must be exceeded to continue with the action\n /// e.g. deposit, add liquidity\n uint256 public constant ACTION_THRESHOLD = 1e12;\n /// @notice Maverick pool static liquidity bin type\n uint8 public constant MAV_STATIC_BIN_KIND = 0;\n /// @dev a threshold under which the contract no longer allows for the protocol to rebalance. Guarding\n /// against a strategist / guardian being taken over and with multiple transactions draining the\n /// protocol funds.\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n /// @notice Emitted when the allowed interval within which the strategy contract is allowed to deposit\n /// liquidity to the underlying pool is updated.\n /// @param allowedWethShareStart The start of the interval\n /// @param allowedWethShareEnd The end of the interval\n event PoolWethShareIntervalUpdated(\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n /// @notice Emitted when liquidity is removed from the underlying pool\n /// @param withdrawLiquidityShare Share of strategy's liquidity that has been removed\n /// @param removedWETHAmount The amount of WETH removed\n /// @param removedOETHAmount The amount of OETH removed\n /// @param underlyingAssets Updated amount of strategy's underlying assets\n event LiquidityRemoved(\n uint256 withdrawLiquidityShare,\n uint256 removedWETHAmount,\n uint256 removedOETHAmount,\n uint256 underlyingAssets\n );\n\n /// @notice Emitted when the underlying pool is rebalanced\n /// @param currentPoolWethShare The resulting share of strategy's liquidity\n /// in the TICK_NUMBER\n event PoolRebalanced(uint256 currentPoolWethShare);\n\n /// @notice Emitted when the amount of underlying assets the strategy hold as\n /// liquidity in the pool is updated.\n /// @param underlyingAssets Updated amount of strategy's underlying assets\n event UnderlyingAssetsUpdated(uint256 underlyingAssets);\n\n /// @notice Emitted when liquidity is added to the underlying pool\n /// @param wethAmountDesired Amount of WETH desired to be deposited\n /// @param oethAmountDesired Amount of OETH desired to be deposited\n /// @param wethAmountSupplied Amount of WETH deposited\n /// @param oethAmountSupplied Amount of OETH deposited\n /// @param tokenId NFT liquidity token id\n /// @param underlyingAssets Updated amount of underlying assets\n event LiquidityAdded(\n uint256 wethAmountDesired,\n uint256 oethAmountDesired,\n uint256 wethAmountSupplied,\n uint256 oethAmountSupplied,\n uint256 tokenId,\n uint256 underlyingAssets\n ); // 0x1530ec74\n\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n ); // 0x3681e8e0\n\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth); // 0x989e5ca8\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth); // 0xa6737d87\n error OutsideExpectedTickRange(); // 0xa6e1bad2\n error SlippageCheck(uint256 tokenReceived); // 0x355cdb78\n\n /// @notice the constructor\n /// @dev This contract is intended to be used as a proxy. To prevent the\n /// potential confusion of having a functional implementation contract\n /// the constructor has the `initializer` modifier. This way the\n /// `initialize` function can not be called on the implementation contract.\n /// For the same reason the implementation contract also has the governor\n /// set to a zero address.\n /// @param _stratConfig the basic strategy configuration\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _oethAddress Address of the Erc20 OETH Token contract\n /// @param _liquidityManager Address of liquidity manager to add\n /// the liquidity\n /// @param _poolLens Address of the pool lens contract\n /// @param _maverickPosition Address of the Maverick's position contract\n /// @param _maverickQuoter Address of the Maverick's Quoter contract\n /// @param _mPool Address of the Rooster concentrated liquidity pool\n /// @param _upperTickAtParity Bool when true upperTick is the one where the\n /// price of OETH and WETH are at parity\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethAddress,\n address _liquidityManager,\n address _poolLens,\n address _maverickPosition,\n address _maverickQuoter,\n address _mPool,\n bool _upperTickAtParity,\n address _votingDistributor,\n address _poolDistributor\n ) initializer InitializableAbstractStrategy(_stratConfig) {\n require(\n address(IMaverickV2Pool(_mPool).tokenA()) == _wethAddress,\n \"WETH not TokenA\"\n );\n require(\n address(IMaverickV2Pool(_mPool).tokenB()) == _oethAddress,\n \"OETH not TokenB\"\n );\n require(\n _liquidityManager != address(0),\n \"LiquidityManager zero address not allowed\"\n );\n require(\n _maverickQuoter != address(0),\n \"Quoter zero address not allowed\"\n );\n require(_poolLens != address(0), \"PoolLens zero address not allowed\");\n require(\n _maverickPosition != address(0),\n \"Position zero address not allowed\"\n );\n require(\n _votingDistributor != address(0),\n \"Voting distributor zero address not allowed\"\n );\n require(\n _poolDistributor != address(0),\n \"Pool distributor zero address not allowed\"\n );\n\n uint256 _tickSpacing = IMaverickV2Pool(_mPool).tickSpacing();\n require(_tickSpacing == 1, \"Unsupported tickSpacing\");\n\n // tickSpacing == 1\n (sqrtPriceTickLower, sqrtPriceTickHigher) = TickMath.tickSqrtPrices(\n _tickSpacing,\n TICK_NUMBER\n );\n sqrtPriceAtParity = _upperTickAtParity\n ? sqrtPriceTickHigher\n : sqrtPriceTickLower;\n\n WETH = _wethAddress;\n OETH = _oethAddress;\n liquidityManager = IMaverickV2LiquidityManager(_liquidityManager);\n poolLens = IMaverickV2PoolLens(_poolLens);\n maverickPosition = IMaverickV2Position(_maverickPosition);\n quoter = IMaverickV2Quoter(_maverickQuoter);\n mPool = IMaverickV2Pool(_mPool);\n votingDistributor = IVotingDistributor(_votingDistributor);\n poolDistributor = IPoolDistributor(_poolDistributor);\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /**\n * @notice initialize function, to set up initial internal state\n */\n function initialize() external onlyGovernor initializer {\n // Read reward\n address[] memory _rewardTokens = new address[](1);\n _rewardTokens[0] = poolDistributor.rewardToken();\n\n require(_rewardTokens[0] != address(0), \"No reward token configured\");\n\n InitializableAbstractStrategy._initialize(\n _rewardTokens,\n new address[](0),\n new address[](0)\n );\n }\n\n /***************************************\n Configuration \n ****************************************/\n\n /**\n * @notice Set allowed pool weth share interval. After the rebalance happens\n * the share of WETH token in the ticker needs to be within the specifications\n * of the interval.\n *\n * @param _allowedWethShareStart Start of WETH share interval expressed as 18 decimal amount\n * @param _allowedWethShareEnd End of WETH share interval expressed as 18 decimal amount\n */\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external onlyGovernor {\n require(\n _allowedWethShareStart < _allowedWethShareEnd,\n \"Invalid interval\"\n );\n // can not go below 1% weth share\n require(_allowedWethShareStart > 0.01 ether, \"Invalid interval start\");\n // can not go above 95% weth share\n require(_allowedWethShareEnd < 0.95 ether, \"Invalid interval end\");\n\n allowedWethShareStart = _allowedWethShareStart;\n allowedWethShareEnd = _allowedWethShareEnd;\n emit PoolWethShareIntervalUpdated(\n _allowedWethShareStart,\n _allowedWethShareEnd\n );\n }\n\n /***************************************\n Strategy overrides \n ****************************************/\n\n /**\n * @notice Deposits funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposits all the funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n _deposit(WETH, _wethBalance);\n }\n\n /**\n * @dev Deposits funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n * Before this function can be called the initial pool position needs to already\n * be minted.\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n require(tokenId > 0, \"Initial position not minted\");\n emit Deposit(_asset, address(0), _amount);\n\n // if the pool price is not within the expected interval leave the WETH on the contract\n // as to not break the mints - in case it would be configured as a default asset strategy\n (bool _isExpectedRange, ) = _checkForExpectedPoolPrice(false);\n if (_isExpectedRange) {\n // deposit funds into the underlying pool. Because no swap is performed there is no\n // need to remove any of the liquidity beforehand.\n _rebalance(0, false, 0, 0);\n }\n }\n\n /**\n * @notice Withdraw an `amount` of WETH from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset WETH address\n * @param _amount Amount of WETH to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n _ensureWETHBalance(_amount);\n\n _withdraw(_recipient, _amount);\n }\n\n /**\n * @notice Withdraw WETH and sends it to the Vault.\n */\n function withdrawAll() external override onlyVault nonReentrant {\n if (tokenId != 0) {\n _removeLiquidity(1e18);\n }\n\n uint256 _balance = IERC20(WETH).balanceOf(address(this));\n if (_balance > 0) {\n _withdraw(vaultAddress, _balance);\n }\n }\n\n function _withdraw(address _recipient, uint256 _amount) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n * @return bool True when the _asset is WETH\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /**\n * @dev Approve the spending amounts for the assets\n */\n function _approveTokenAmounts(\n uint256 _wethAllowance,\n uint256 _oethAllowance\n ) internal {\n IERC20(WETH).approve(address(liquidityManager), _wethAllowance);\n IERC20(OETH).approve(address(liquidityManager), _oethAllowance);\n }\n\n /***************************************\n Liquidity management\n ****************************************/\n /**\n * @dev Add liquidity into the pool in the pre-configured WETH to OETH share ratios\n * defined by the allowedPoolWethShareStart|End interval.\n *\n * Normally a PoolLens contract is used to prepare the parameters to add liquidity to the\n * Rooster pools. It has some errors when doing those calculation and for that reason a\n * much more accurate Quoter contract is used. This is possible due to our requirement of\n * adding liquidity only to one tick - PoolLens supports adding liquidity into multiple ticks\n * using different distribution ratios.\n */\n function _addLiquidity() internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n // don't deposit small liquidity amounts\n if (_wethBalance <= ACTION_THRESHOLD) {\n return;\n }\n\n (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n ) = _getAddLiquidityParams(_wethBalance, 1e30);\n\n if (OETHRequired > _oethBalance) {\n IVault(vaultAddress).mintForStrategy(OETHRequired - _oethBalance);\n }\n\n _approveTokenAmounts(WETHRequired, OETHRequired);\n\n (\n uint256 _wethAmount,\n uint256 _oethAmount,\n uint32[] memory binIds\n ) = liquidityManager.addPositionLiquidityToSenderByTokenIndex(\n mPool,\n 0, // NFT token index\n packedSqrtPriceBreaks,\n packedArgs\n );\n\n require(binIds.length == 1, \"Unexpected binIds length\");\n\n // burn remaining OETH\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n\n // needs to be called after _updateUnderlyingAssets so the updated amount\n // is reflected in the event\n emit LiquidityAdded(\n _wethBalance, // wethAmountDesired\n OETHRequired, // oethAmountDesired\n _wethAmount, // wethAmountSupplied\n _oethAmount, // oethAmountSupplied\n tokenId, // tokenId\n underlyingAssets\n );\n }\n\n /**\n * @dev The function creates liquidity parameters required to be able to add liquidity to the pool.\n * The function needs to handle the 3 different cases of the way liquidity is added:\n * - only WETH present in the tick\n * - only OETH present in the tick\n * - both tokens present in the tick\n *\n */\n function _getAddLiquidityParams(uint256 _maxWETH, uint256 _maxOETH)\n internal\n returns (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n )\n {\n IMaverickV2Pool.AddLiquidityParams[]\n memory addParams = new IMaverickV2Pool.AddLiquidityParams[](1);\n int32[] memory ticks = new int32[](1);\n uint128[] memory amounts = new uint128[](1);\n ticks[0] = TICK_NUMBER;\n // arbitrary LP amount\n amounts[0] = 1e24;\n\n // construct value for Quoter with arbitrary LP amount\n IMaverickV2Pool.AddLiquidityParams memory addParam = IMaverickV2Pool\n .AddLiquidityParams({\n kind: MAV_STATIC_BIN_KIND,\n ticks: ticks,\n amounts: amounts\n });\n\n // get the WETH and OETH required to get the proportion of tokens required\n // given the arbitrary liquidity\n (WETHRequired, OETHRequired, ) = quoter.calculateAddLiquidity(\n mPool,\n addParam\n );\n\n /**\n * If either token required is 0 then the tick consists only of the other token. In that\n * case the liquidity calculations need to be done using the non 0 token. By setting the\n * tokenRequired from 0 to 1 the `min` in next step will ignore that (the bigger) value.\n */\n WETHRequired = WETHRequired == 0 ? 1 : WETHRequired;\n OETHRequired = OETHRequired == 0 ? 1 : OETHRequired;\n\n addParam.amounts[0] = Math_v5\n .min(\n ((_maxWETH - 1) * 1e24) / WETHRequired,\n ((_maxOETH - 1) * 1e24) / OETHRequired\n )\n .toUint128();\n\n // update the quotes with the actual amounts\n (WETHRequired, OETHRequired, ) = quoter.calculateAddLiquidity(\n mPool,\n addParam\n );\n\n require(_maxWETH >= WETHRequired, \"More WETH required than specified\");\n require(_maxOETH >= OETHRequired, \"More OETH required than specified\");\n\n // organize values to be used by manager\n addParams[0] = addParam;\n packedArgs = liquidityManager.packAddLiquidityArgsArray(addParams);\n // price can stay 0 if array only has one element\n packedSqrtPriceBreaks = liquidityManager.packUint88Array(\n new uint88[](1)\n );\n }\n\n /**\n * @dev Check that the Rooster pool price is within the expected\n * parameters.\n * This function works whether the strategy contract has liquidity\n * position in the pool or not. The function returns _wethSharePct\n * as a gas optimization measure.\n * @param _throwException when set to true the function throws an exception\n * when pool's price is not within expected range.\n * @return _isExpectedRange Bool expressing price is within expected range\n * @return _wethSharePct Share of WETH owned by this strategy contract in the\n * configured ticker.\n */\n function _checkForExpectedPoolPrice(bool _throwException)\n internal\n view\n returns (bool _isExpectedRange, uint256 _wethSharePct)\n {\n require(\n allowedWethShareStart != 0 && allowedWethShareEnd != 0,\n \"Weth share interval not set\"\n );\n\n uint256 _currentPrice = getPoolSqrtPrice();\n\n /**\n * First check pool price is in expected tick range\n *\n * A revert is issued even though price being equal to the lower bound as that can not\n * be within the approved tick range.\n */\n if (\n _currentPrice <= sqrtPriceTickLower ||\n _currentPrice >= sqrtPriceTickHigher\n ) {\n if (_throwException) {\n revert OutsideExpectedTickRange();\n }\n\n return (false, _currentPrice <= sqrtPriceTickLower ? 0 : 1e18);\n }\n\n // 18 decimal number expressed WETH tick share\n _wethSharePct = _getWethShare(_currentPrice);\n\n if (\n _wethSharePct < allowedWethShareStart ||\n _wethSharePct > allowedWethShareEnd\n ) {\n if (_throwException) {\n revert PoolRebalanceOutOfBounds(\n _wethSharePct,\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n return (false, _wethSharePct);\n }\n\n return (true, _wethSharePct);\n }\n\n /**\n * @notice Rebalance the pool to the desired token split and Deposit any WETH on the contract to the\n * underlying rooster pool. Print the required amount of corresponding OETH. After the rebalancing is\n * done burn any potentially remaining OETH tokens still on the strategy contract.\n *\n * This function has a slightly different behaviour depending on the status of the underlying Rooster\n * pool. The function consists of the following 3 steps:\n * 1. withdrawLiquidityOption -> this is a configurable option where either only part of the liquidity\n * necessary for the swap is removed, or all of it. This way the rebalance\n * is able to optimize for volume, for efficiency or anything in between\n * 2. swapToDesiredPosition -> move active trading price in the pool to be able to deposit WETH & OETH\n * tokens with the desired pre-configured ratios\n * 3. addLiquidity -> add liquidity into the pool respecting ratio split configuration\n *\n *\n * Exact _amountToSwap, _swapWeth & _minTokenReceived parameters shall be determined by simulating the\n * transaction off-chain. The strategy checks that after the swap the share of the tokens is in the\n * expected ranges.\n *\n * @param _amountToSwap The amount of the token to swap\n * @param _swapWeth Swap using WETH when true, use OETH when false\n * @param _minTokenReceived Slippage check -> minimum amount of token expected in return\n * @param _liquidityToRemovePct Percentage of liquidity to remove -> the percentage amount of liquidity to\n * remove before performing the swap. 1e18 denominated\n */\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived,\n uint256 _liquidityToRemovePct\n ) external nonReentrant onlyGovernorOrStrategist {\n _rebalance(\n _amountToSwap,\n _swapWeth,\n _minTokenReceived,\n _liquidityToRemovePct\n );\n }\n\n // slither-disable-start reentrancy-no-eth\n function _rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived,\n uint256 _liquidityToRemovePct\n ) internal {\n // Remove the required amount of liquidity\n if (_liquidityToRemovePct > 0) {\n _removeLiquidity(_liquidityToRemovePct);\n }\n\n // in some cases (e.g. deposits) we will just want to add liquidity and not\n // issue a swap to move the active trading position within the pool. Before or after a\n // deposit or as a standalone call the strategist might issue a rebalance to move the\n // active trading price to a more desired position.\n if (_amountToSwap > 0) {\n // In case liquidity has been removed and there is still not enough WETH owned by the\n // strategy contract remove additional required amount of WETH.\n if (_swapWeth) _ensureWETHBalance(_amountToSwap);\n\n _swapToDesiredPosition(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n\n // calling check liquidity early so we don't get unexpected errors when adding liquidity\n // in the later stages of this function\n _checkForExpectedPoolPrice(true);\n\n _addLiquidity();\n\n // this call shouldn't be necessary, since adding liquidity shouldn't affect the active\n // trading price. It is a defensive programming measure.\n (, uint256 _wethSharePct) = _checkForExpectedPoolPrice(true);\n\n // revert if protocol insolvent\n _solvencyAssert();\n\n emit PoolRebalanced(_wethSharePct);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @dev Perform a swap so that after the swap the tick has the desired WETH to OETH token share.\n */\n function _swapToDesiredPosition(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n IERC20 _tokenToSwap = IERC20(_swapWeth ? WETH : OETH);\n uint256 _balance = _tokenToSwap.balanceOf(address(this));\n\n if (_balance < _amountToSwap) {\n // This should never trigger since _ensureWETHBalance will already\n // throw an error if there is not enough WETH\n if (_swapWeth) {\n revert NotEnoughWethForSwap(_balance, _amountToSwap);\n }\n // if swapping OETH\n uint256 mintForSwap = _amountToSwap - _balance;\n IVault(vaultAddress).mintForStrategy(mintForSwap);\n }\n\n // SafeERC20 is used for IERC20 transfers. Not sure why slither complains\n // slither-disable-next-line unchecked-transfer\n _tokenToSwap.transfer(address(mPool), _amountToSwap);\n\n // tickLimit: the furthest tick a swap will execute in. If no limit is desired,\n // value should be set to type(int32).max for a tokenAIn (WETH) swap\n // and type(int32).min for a swap where tokenB (OETH) is the input\n\n IMaverickV2Pool.SwapParams memory swapParams = IMaverickV2Pool\n // exactOutput defines whether the amount specified is the output\n // or the input amount of the swap\n .SwapParams({\n amount: _amountToSwap,\n tokenAIn: _swapWeth,\n exactOutput: false,\n tickLimit: TICK_NUMBER\n });\n\n // swaps without a callback as the assets are already sent to the pool\n (, uint256 amountOut) = mPool.swap(\n address(this),\n swapParams,\n bytes(\"\")\n );\n\n /**\n * There could be additional checks here for validating minTokenReceived is within the\n * expected range (e.g. 99% - 101% of the token sent in). Though that doesn't provide\n * any additional security. After the swap the `_checkForExpectedPoolPrice` validates\n * that the swap has moved the price into the expected tick (# -1).\n *\n * If the guardian forgets to set a `_minTokenReceived` and a sandwich attack bends\n * the pool before the swap the `_checkForExpectedPoolPrice` will fail the transaction.\n *\n * A check would not prevent a compromised guardian from stealing funds as multiple\n * transactions each loosing smaller amount of funds are still possible.\n */\n if (amountOut < _minTokenReceived) {\n revert SlippageCheck(amountOut);\n }\n\n /**\n * In the interest of each function in `_rebalance` to leave the contract state as\n * clean as possible the OETH tokens here are burned. This decreases the\n * dependence where `_swapToDesiredPosition` function relies on later functions\n * (`addLiquidity`) to burn the OETH. Reducing the risk of error introduction.\n */\n _burnOethOnTheContract();\n }\n\n /**\n * @dev This function removes the appropriate amount of liquidity to ensure that the required\n * amount of WETH is available on the contract\n *\n * @param _amount WETH balance required on the contract\n */\n function _ensureWETHBalance(uint256 _amount) internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance >= _amount) {\n return;\n }\n\n require(tokenId != 0, \"No liquidity available\");\n uint256 _additionalWethRequired = _amount - _wethBalance;\n (uint256 _wethInThePool, ) = getPositionPrincipal();\n\n if (_wethInThePool < _additionalWethRequired) {\n revert NotEnoughWethLiquidity(\n _wethInThePool,\n _additionalWethRequired\n );\n }\n\n uint256 shareOfWethToRemove = _wethInThePool <= 1\n ? 1e18\n : Math_v5.min(\n /**\n * When dealing with shares of liquidity to remove there is always some\n * rounding involved. After extensive fuzz testing the below approach\n * yielded the best results where the strategy overdraws the least and\n * never removes insufficient amount of WETH.\n */\n (_additionalWethRequired + 2).divPrecisely(_wethInThePool - 1) +\n 2,\n 1e18\n );\n\n _removeLiquidity(shareOfWethToRemove);\n }\n\n /**\n * @dev Decrease partial or all liquidity from the pool.\n * @param _liquidityToDecrease The amount of liquidity to remove denominated in 1e18\n */\n function _removeLiquidity(uint256 _liquidityToDecrease) internal {\n require(_liquidityToDecrease > 0, \"Must remove some liquidity\");\n require(\n _liquidityToDecrease <= 1e18,\n \"Can not remove more than 100% of liquidity\"\n );\n\n // 0 indicates the first (and only) bin in the NFT LP position.\n IMaverickV2Pool.RemoveLiquidityParams memory params = maverickPosition\n .getRemoveParams(tokenId, 0, _liquidityToDecrease);\n (uint256 _amountWeth, uint256 _amountOeth) = maverickPosition\n .removeLiquidityToSender(tokenId, mPool, params);\n\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n\n // needs to be called after the _updateUnderlyingAssets so the updated amount is reflected\n // in the event\n emit LiquidityRemoved(\n _liquidityToDecrease,\n _amountWeth,\n _amountOeth,\n underlyingAssets\n );\n }\n\n /**\n * @dev Burns any OETH tokens remaining on the strategy contract if the balance is\n * above the action threshold.\n */\n function _burnOethOnTheContract() internal {\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(_oethBalance);\n }\n\n /**\n * @notice Returns the percentage of WETH liquidity in the configured ticker\n * owned by this strategy contract.\n * @return uint256 1e18 denominated percentage expressing the share\n */\n function getWETHShare() external view returns (uint256) {\n uint256 _currentPrice = getPoolSqrtPrice();\n return _getWethShare(_currentPrice);\n }\n\n /**\n * @dev Returns the share of WETH in tick denominated in 1e18\n */\n function _getWethShare(uint256 _currentPrice)\n internal\n view\n returns (uint256)\n {\n (\n uint256 wethAmount,\n uint256 oethAmount\n ) = _reservesInTickForGivenPriceAndLiquidity(\n sqrtPriceTickLower,\n sqrtPriceTickHigher,\n _currentPrice,\n 1e24\n );\n\n return wethAmount.divPrecisely(wethAmount + oethAmount);\n }\n\n /**\n * @notice Returns the current pool price in square root\n * @return Square root of the pool price\n */\n function getPoolSqrtPrice() public view returns (uint256) {\n return poolLens.getPoolSqrtPrice(mPool);\n }\n\n /**\n * @notice Returns the current active trading tick of the underlying pool\n * @return _currentTick Current pool trading tick\n */\n function getCurrentTradingTick() public view returns (int32 _currentTick) {\n _currentTick = mPool.getState().activeTick;\n }\n\n /**\n * @notice Mint the initial NFT position\n *\n * @dev This amount is \"gifted\" to the strategy contract and will count as a yield\n * surplus.\n */\n // slither-disable-start reentrancy-no-eth\n function mintInitialPosition() external onlyGovernor nonReentrant {\n require(tokenId == 0, \"Initial position already minted\");\n (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n ) = _getAddLiquidityParams(1e16, 1e16);\n\n // Mint rounded up OETH amount\n if (OETHRequired > 0) {\n IVault(vaultAddress).mintForStrategy(OETHRequired);\n }\n\n _approveTokenAmounts(WETHRequired, OETHRequired);\n\n // Store the tokenId before calling updateUnderlyingAssets as it relies on the tokenId\n // not being 0\n (, , , tokenId) = liquidityManager.mintPositionNftToSender(\n mPool,\n packedSqrtPriceBreaks,\n packedArgs\n );\n\n // burn remaining OETH\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @notice Returns the balance of tokens the strategy holds in the LP position\n * @return _amountWeth Amount of WETH in position\n * @return _amountOeth Amount of OETH in position\n */\n function getPositionPrincipal()\n public\n view\n returns (uint256 _amountWeth, uint256 _amountOeth)\n {\n if (tokenId == 0) {\n return (0, 0);\n }\n\n (_amountWeth, _amountOeth, ) = _getPositionInformation();\n }\n\n /**\n * @dev Returns the balance of tokens the strategy holds in the LP position\n * @return _amountWeth Amount of WETH in position\n * @return _amountOeth Amount of OETH in position\n * @return liquidity Amount of liquidity in the position\n */\n function _getPositionInformation()\n internal\n view\n returns (\n uint256 _amountWeth,\n uint256 _amountOeth,\n uint256 liquidity\n )\n {\n IMaverickV2Position.PositionFullInformation\n memory positionInfo = maverickPosition.tokenIdPositionInformation(\n tokenId,\n 0\n );\n\n require(\n positionInfo.liquidities.length == 1,\n \"Unexpected liquidities length\"\n );\n require(positionInfo.ticks.length == 1, \"Unexpected ticks length\");\n\n _amountWeth = positionInfo.amountA;\n _amountOeth = positionInfo.amountB;\n liquidity = positionInfo.liquidities[0];\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99.8%) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethSupply = IERC20(OETH).totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethSupply) < SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /**\n * @dev Collect Rooster reward token, and send it to the harvesterAddress\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Do nothing if there's no position minted\n if (tokenId > 0) {\n uint32[] memory binIds = new uint32[](1);\n IMaverickV2Pool.TickState memory tickState = mPool.getTick(\n TICK_NUMBER\n );\n // get the binId for the MAV_STATIC_BIN_KIND in tick TICK_NUMBER (-1)\n binIds[0] = tickState.binIdsByTick[0];\n\n uint256 lastEpoch = votingDistributor.lastEpoch();\n\n poolDistributor.claimLp(\n address(this),\n tokenId,\n mPool,\n binIds,\n lastEpoch\n );\n }\n\n // Run the internal inherited function\n _collectRewardTokens();\n }\n\n /***************************************\n Balances and Fees\n ****************************************/\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256)\n {\n require(_asset == WETH, \"Only WETH supported\");\n\n // because of PoolLens inaccuracy there is usually some dust WETH left on the contract\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n // just paranoia check, in case there is OETH in the strategy that for some reason hasn't\n // been burned yet. This should always be 0.\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n return underlyingAssets + _wethBalance + _oethBalance;\n }\n\n /// @dev This function updates the amount of underlying assets with the approach of the least possible\n /// total tokens extracted for the current liquidity in the pool.\n function _updateUnderlyingAssets() internal {\n /**\n * Our net value represent the smallest amount of tokens we are able to extract from the position\n * given our liquidity.\n *\n * The least amount of tokens ex-tractable from the position is where the active trading price is\n * at the edge between tick -1 & tick 0. There the pool is offering 1:1 trades between WETH & OETH.\n * At that moment the pool consists completely of WETH and no OETH.\n *\n * The more swaps from OETH -> WETH happen on the pool the more the price starts to move away from the tick 0\n * towards the middle of tick -1 making OETH (priced in WETH) cheaper.\n */\n\n uint256 _wethAmount = tokenId == 0 ? 0 : _balanceInPosition();\n\n underlyingAssets = _wethAmount;\n emit UnderlyingAssetsUpdated(_wethAmount);\n }\n\n /**\n * @dev Strategy reserves (which consist only of WETH in case of Rooster - Plume pool)\n * when the tick price is closest to parity - assuring the lowest amount of tokens\n * returned for the current position liquidity.\n */\n function _balanceInPosition() internal view returns (uint256 _wethBalance) {\n (, , uint256 liquidity) = _getPositionInformation();\n\n uint256 _oethBalance;\n\n (_wethBalance, _oethBalance) = _reservesInTickForGivenPriceAndLiquidity(\n sqrtPriceTickLower,\n sqrtPriceTickHigher,\n sqrtPriceAtParity,\n liquidity\n );\n\n require(_oethBalance == 0, \"Non zero oethBalance\");\n }\n\n /**\n * @notice Tick dominance denominated in 1e18\n * @return _tickDominance The share of liquidity in TICK_NUMBER tick owned\n * by the strategy contract denominated in 1e18\n */\n function tickDominance() public view returns (uint256 _tickDominance) {\n IMaverickV2Pool.TickState memory tickState = mPool.getTick(TICK_NUMBER);\n\n uint256 wethReserve = tickState.reserveA;\n uint256 oethReserve = tickState.reserveB;\n\n // prettier-ignore\n (uint256 _amountWeth, uint256 _amountOeth, ) = _getPositionInformation();\n\n if (wethReserve + oethReserve == 0) {\n return 0;\n }\n\n _tickDominance = (_amountWeth + _amountOeth).divPrecisely(\n wethReserve + oethReserve\n );\n }\n\n /***************************************\n Hidden functions\n ****************************************/\n /**\n * @dev Unsupported\n */\n function setPTokenAddress(address, address) external pure override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function removePToken(uint256) external pure override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function _abstractSetPToken(address, address) internal pure override {\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function safeApproveAllTokens() external pure override {\n // all the amounts are approved at the time required\n revert(\"Unsupported method\");\n }\n\n /***************************************\n Maverick liquidity utilities\n ****************************************/\n\n /// @notice Calculates deltaA = liquidity * (sqrt(upper) - sqrt(lower))\n /// Calculates deltaB = liquidity / sqrt(lower) - liquidity / sqrt(upper),\n /// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower))\n ///\n /// @dev refactored from here:\n // solhint-disable-next-line max-line-length\n /// https://github.com/rooster-protocol/rooster-contracts/blob/main/v2-supplemental/contracts/libraries/LiquidityUtilities.sol#L665-L695\n function _reservesInTickForGivenPriceAndLiquidity(\n uint256 _lowerSqrtPrice,\n uint256 _upperSqrtPrice,\n uint256 _newSqrtPrice,\n uint256 _liquidity\n ) internal pure returns (uint128 reserveA, uint128 reserveB) {\n if (_liquidity == 0) {\n (reserveA, reserveB) = (0, 0);\n } else {\n uint256 lowerEdge = MathRooster.max(_lowerSqrtPrice, _newSqrtPrice);\n\n reserveA = MathRooster\n .mulCeil(\n _liquidity,\n MathRooster.clip(\n MathRooster.min(_upperSqrtPrice, _newSqrtPrice),\n _lowerSqrtPrice\n )\n )\n .toUint128();\n reserveB = MathRooster\n .mulDivCeil(\n _liquidity,\n 1e18 * MathRooster.clip(_upperSqrtPrice, lowerEdge),\n _upperSqrtPrice * lowerEdge\n )\n .toUint128();\n }\n }\n}\n" + }, + "contracts/strategies/sonic/SonicStakingStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SonicValidatorDelegator } from \"./SonicValidatorDelegator.sol\";\nimport { IWrappedSonic } from \"../../interfaces/sonic/IWrappedSonic.sol\";\n\n/**\n * @title Staking Strategy for Sonic's native S currency\n * @author Origin Protocol Inc\n */\ncontract SonicStakingStrategy is SonicValidatorDelegator {\n // For future use\n uint256[50] private __gap;\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wrappedSonic,\n address _sfc\n ) SonicValidatorDelegator(_baseConfig, _wrappedSonic, _sfc) {}\n\n /// @notice Deposit wrapped S asset into the underlying platform.\n /// @param _asset Address of asset to deposit. Has to be Wrapped Sonic (wS).\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposit Wrapped Sonic (wS) to this strategy and delegate to a validator.\n * @param _asset Address of Wrapped Sonic (wS) token\n * @param _amount Amount of Wrapped Sonic (wS) to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal virtual {\n require(_amount > 0, \"Must deposit something\");\n\n _delegate(_amount);\n emit Deposit(_asset, address(0), _amount);\n }\n\n /**\n * @notice Deposit the entire balance of wrapped S in this strategy contract into\n * the underlying platform.\n */\n function depositAll() external virtual override onlyVault nonReentrant {\n uint256 wSBalance = IERC20(wrappedSonic).balanceOf(address(this));\n\n if (wSBalance > 0) {\n _deposit(wrappedSonic, wSBalance);\n }\n }\n\n /// @notice Withdraw Wrapped Sonic (wS) from this strategy contract.\n /// Used only if some wS is lingering on the contract.\n /// That can happen only when someone sends wS directly to this contract\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset Address of the Wrapped Sonic (wS) token\n /// @param _amount Amount of Wrapped Sonic (wS) to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal override {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(_asset).transfer(_recipient, _amount);\n\n emit Withdrawal(wrappedSonic, address(0), _amount);\n }\n\n /// @notice Transfer all Wrapped Sonic (wS) deposits back to the vault.\n /// This does not withdraw from delegated validators. That has to be done separately with `undelegate`.\n /// Any native S in this strategy will be withdrawn.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 balance = address(this).balance;\n if (balance > 0) {\n IWrappedSonic(wrappedSonic).deposit{ value: balance }();\n }\n uint256 wSBalance = IERC20(wrappedSonic).balanceOf(address(this));\n if (wSBalance > 0) {\n _withdraw(vaultAddress, wrappedSonic, wSBalance);\n }\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset token\n */\n function supportsAsset(address _asset)\n public\n view\n virtual\n override\n returns (bool)\n {\n return _asset == wrappedSonic;\n }\n\n /**\n * @notice is not supported for this strategy as the\n * Wrapped Sonic (wS) token is set at deploy time.\n */\n function setPTokenAddress(address, address)\n external\n view\n override\n onlyGovernor\n {\n revert(\"unsupported function\");\n }\n\n /// @notice is not used by this strategy as all staking rewards are restaked\n function collectRewardTokens() external override nonReentrant {\n revert(\"unsupported function\");\n }\n\n /**\n * @notice is not supported for this strategy as the\n * Wrapped Sonic (wS) token is set at deploy time.\n */\n function removePToken(uint256) external view override onlyGovernor {\n revert(\"unsupported function\");\n }\n\n /// @dev is not used by this strategy but must be implemented as it's abstract\n /// in the inherited `InitializableAbstractStrategy` contract.\n function _abstractSetPToken(address, address) internal virtual override {}\n\n /// @notice is not used by this strategy\n function safeApproveAllTokens() external override onlyGovernor {}\n}\n" + }, + "contracts/strategies/sonic/SonicSwapXAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title SwapX Algorithmic Market Maker (AMO) Strategy\n * @notice AMO strategy for the SwapX OS/wS stable pool\n * @author Origin Protocol Inc\n */\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport { sqrt } from \"../../utils/PRBMath.sol\";\nimport { IBasicToken } from \"../../interfaces/IBasicToken.sol\";\nimport { IPair } from \"../../interfaces/sonic/ISwapXPair.sol\";\nimport { IGauge } from \"../../interfaces/sonic/ISwapXGauge.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\n\ncontract SonicSwapXAMOStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @notice a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n /// @notice Precision for the SwapX Stable AMM (sAMM) invariant k.\n uint256 public constant PRECISION = 1e18;\n\n /// @notice Address of the Wrapped S (wS) token.\n address public immutable ws;\n\n /// @notice Address of the OS token contract.\n address public immutable os;\n\n /// @notice Address of the SwapX Stable pool contract.\n address public immutable pool;\n\n /// @notice Address of the SwapX Gauge contract.\n address public immutable gauge;\n\n /// @notice The max amount the OS/wS price can deviate from peg (1e18)\n /// before deposits are reverted scaled to 18 decimals.\n /// eg 0.01e18 or 1e16 is 1% which is 100 basis points.\n /// This is the amount below and above peg so a 50 basis point deviation (0.005e18)\n /// allows a price range from 0.995 to 1.005.\n uint256 public maxDepeg;\n\n event SwapOTokensToPool(\n uint256 osMinted,\n uint256 wsDepositAmount,\n uint256 osDepositAmount,\n uint256 lpTokens\n );\n event SwapAssetsToPool(\n uint256 wsSwapped,\n uint256 lpTokens,\n uint256 osBurnt\n );\n event MaxDepegUpdated(uint256 maxDepeg);\n\n /**\n * @dev Verifies that the caller is the Strategist of the Vault.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Skim the SwapX pool in case any extra wS or OS tokens were added\n */\n modifier skimPool() {\n IPair(pool).skim(address(this));\n _;\n }\n\n /**\n * @dev Checks the pool is balanced enough to allow deposits.\n */\n modifier nearBalancedPool() {\n // OS/wS price = wS / OS\n // Get the OS/wS price for selling 1 OS for wS\n // As OS is 1, the wS amount is the OS/wS price\n uint256 sellPrice = IPair(pool).getAmountOut(1e18, os);\n\n // Get the amount of OS received from selling 1 wS. This is buying OS.\n uint256 osAmount = IPair(pool).getAmountOut(1e18, ws);\n // Convert to a OS/wS price = wS / OS\n uint256 buyPrice = 1e36 / osAmount;\n\n uint256 pegPrice = 1e18;\n\n require(\n sellPrice >= pegPrice - maxDepeg && buyPrice <= pegPrice + maxDepeg,\n \"price out of range\"\n );\n _;\n }\n\n /**\n * @dev Checks the pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do swaps against the pool.\n * Deposits and withdrawals are proportional to the pool's balances hence don't need this check.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the pool\n (uint256 wsReservesBefore, uint256 osReservesBefore, ) = IPair(pool)\n .getReserves();\n // diff = wS balance - OS balance\n int256 diffBefore = wsReservesBefore.toInt256() -\n osReservesBefore.toInt256();\n\n _;\n\n // Get the asset and OToken balances in the pool\n (uint256 wsReservesAfter, uint256 osReservesAfter, ) = IPair(pool)\n .getReserves();\n // diff = wS balance - OS balance\n int256 diffAfter = wsReservesAfter.toInt256() -\n osReservesAfter.toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OS, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"Assets overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of wS, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"OTokens overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n /**\n * @param _baseConfig The `platformAddress` is the address of the SwapX pool.\n * The `vaultAddress` is the address of the Origin Sonic Vault.\n * @param _os Address of the OS token.\n * @param _ws Address of the Wrapped S (wS) token.\n * @param _gauge Address of the SwapX gauge for the pool.\n */\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _os,\n address _ws,\n address _gauge\n ) InitializableAbstractStrategy(_baseConfig) {\n // Check the pool tokens are correct\n require(\n IPair(_baseConfig.platformAddress).token0() == _ws &&\n IPair(_baseConfig.platformAddress).token1() == _os,\n \"Incorrect pool tokens\"\n );\n // Checked both tokens are to 18 decimals\n require(\n IBasicToken(_ws).decimals() == 18 &&\n IBasicToken(_os).decimals() == 18,\n \"Incorrect token decimals\"\n );\n // Check the SwapX pool is a Stable AMM (sAMM)\n require(\n IPair(_baseConfig.platformAddress).isStable() == true,\n \"Pool not stable\"\n );\n // Check the gauge is for the pool\n require(\n IGauge(_gauge).TOKEN() == _baseConfig.platformAddress,\n \"Incorrect gauge\"\n );\n\n // Set the immutable variables\n os = _os;\n ws = _ws;\n pool = _baseConfig.platformAddress;\n gauge = _gauge;\n\n // This is an implementation contract. The governor is set in the proxy contract.\n _setGovernor(address(0));\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as SwapX strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Array containing SWPx token address\n * @param _maxDepeg The max amount the OS/wS price can deviate from peg (1e18) before deposits are reverted.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n uint256 _maxDepeg\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = pool;\n\n address[] memory _assets = new address[](1);\n _assets[0] = ws;\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n maxDepeg = _maxDepeg;\n\n _approveBase();\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit an amount of Wrapped S (wS) into the SwapX pool.\n * Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @dev This tx must be wrapped by the VaultValueChecker.\n * To minimize loses, the pool should be rebalanced before depositing.\n * The pool's OS/wS price must be within the maxDepeg range.\n * @param _asset Address of Wrapped S (wS) token.\n * @param _wsAmount Amount of Wrapped S (wS) tokens to deposit.\n */\n function deposit(address _asset, uint256 _wsAmount)\n external\n override\n onlyVault\n nonReentrant\n skimPool\n nearBalancedPool\n {\n require(_asset == ws, \"Unsupported asset\");\n require(_wsAmount > 0, \"Must deposit something\");\n\n (uint256 osDepositAmount, ) = _deposit(_wsAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the deposited wS tokens\n emit Deposit(ws, pool, _wsAmount);\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osDepositAmount);\n }\n\n /**\n * @notice Deposit all the strategy's Wrapped S (wS) tokens into the SwapX pool.\n * Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @dev This tx must be wrapped by the VaultValueChecker.\n * To minimize loses, the pool should be rebalanced before depositing.\n * The pool's OS/wS price must be within the maxDepeg range.\n */\n function depositAll()\n external\n override\n onlyVault\n nonReentrant\n skimPool\n nearBalancedPool\n {\n uint256 wsBalance = IERC20(ws).balanceOf(address(this));\n if (wsBalance > 0) {\n (uint256 osDepositAmount, ) = _deposit(wsBalance);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the deposited wS tokens\n emit Deposit(ws, pool, wsBalance);\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osDepositAmount);\n }\n }\n\n /**\n * @dev Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @param _wsAmount Amount of Wrapped S (wS) tokens to deposit.\n * @return osDepositAmount Amount of OS tokens minted and deposited into the pool.\n * @return lpTokens Amount of SwapX pool LP tokens minted and deposited into the gauge.\n */\n function _deposit(uint256 _wsAmount)\n internal\n returns (uint256 osDepositAmount, uint256 lpTokens)\n {\n // Calculate the required amount of OS to mint based on the wS amount.\n osDepositAmount = _calcTokensToMint(_wsAmount);\n\n // Mint the required OS tokens to this strategy\n IVault(vaultAddress).mintForStrategy(osDepositAmount);\n\n // Add wS and OS liquidity to the pool and stake in gauge\n lpTokens = _depositToPoolAndGauge(_wsAmount, osDepositAmount);\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw wS and OS from the SwapX pool, burn the OS,\n * and transfer the wS to the recipient.\n * @param _recipient Address of the Vault.\n * @param _asset Address of the Wrapped S (wS) contract.\n * @param _wsAmount Amount of Wrapped S (wS) to withdraw.\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _wsAmount\n ) external override onlyVault nonReentrant skimPool {\n require(_wsAmount > 0, \"Must withdraw something\");\n require(_asset == ws, \"Unsupported asset\");\n // This strategy can't be set as a default strategy for wS in the Vault.\n // This means the recipient must always be the Vault.\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n // Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n uint256 lpTokens = _calcTokensToBurn(_wsAmount);\n\n // Withdraw pool LP tokens from the gauge and remove assets from from the pool\n _withdrawFromGaugeAndPool(lpTokens);\n\n // Burn all the removed OS and any that was left in the strategy\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Transfer wS to the recipient\n // Note there can be a dust amount of wS left in the strategy as\n // the burn of the pool's LP tokens is rounded up\n require(\n IERC20(ws).balanceOf(address(this)) >= _wsAmount,\n \"Not enough wS removed from pool\"\n );\n IERC20(ws).safeTransfer(_recipient, _wsAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the withdrawn wS tokens\n emit Withdrawal(ws, pool, _wsAmount);\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n }\n\n /**\n * @notice Withdraw all pool LP tokens from the gauge,\n * remove all wS and OS from the SwapX pool,\n * burn all the OS tokens,\n * and transfer all the wS to the Vault contract.\n * @dev There is no solvency check here as withdrawAll can be called to\n * quickly secure assets to the Vault in emergencies.\n */\n function withdrawAll()\n external\n override\n onlyVaultOrGovernor\n nonReentrant\n skimPool\n {\n // Get all the pool LP tokens the strategy has staked in the gauge\n uint256 lpTokens = IGauge(gauge).balanceOf(address(this));\n // Can not withdraw zero LP tokens from the gauge\n if (lpTokens == 0) return;\n\n if (IGauge(gauge).emergency()) {\n // The gauge is in emergency mode\n _emergencyWithdrawFromGaugeAndPool();\n } else {\n // Withdraw pool LP tokens from the gauge and remove assets from from the pool\n _withdrawFromGaugeAndPool(lpTokens);\n }\n\n // Burn all OS in this strategy contract\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Get the strategy contract's wS balance.\n // This includes all that was removed from the SwapX pool and\n // any that was sitting in the strategy contract before the removal.\n uint256 wsBalance = IERC20(ws).balanceOf(address(this));\n IERC20(ws).safeTransfer(vaultAddress, wsBalance);\n\n // Emit event for the withdrawn wS tokens\n emit Withdrawal(ws, pool, wsBalance);\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n }\n\n /***************************************\n Pool Rebalancing\n ****************************************/\n\n /** @notice Used when there is more OS than wS in the pool.\n * wS and OS is removed from the pool, the received wS is swapped for OS\n * and the left over OS in the strategy is burnt.\n * The OS/wS price is < 1.0 so OS is being bought at a discount.\n * @param _wsAmount Amount of Wrapped S (wS) to swap into the pool.\n */\n function swapAssetsToPool(uint256 _wsAmount)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n skimPool\n {\n require(_wsAmount > 0, \"Must swap something\");\n\n // 1. Partially remove liquidity so there’s enough wS for the swap\n\n // Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n uint256 lpTokens = _calcTokensToBurn(_wsAmount);\n require(lpTokens > 0, \"No LP tokens to burn\");\n\n _withdrawFromGaugeAndPool(lpTokens);\n\n // 2. Swap wS for OS against the pool\n // Swap exact amount of wS for OS against the pool\n // There can be a dust amount of wS left in the strategy as the burn of the pool's LP tokens is rounded up\n _swapExactTokensForTokens(_wsAmount, ws, os);\n\n // 3. Burn all the OS left in the strategy from the remove liquidity and swap\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n // Emit event for the swap\n emit SwapAssetsToPool(_wsAmount, lpTokens, osToBurn);\n }\n\n /**\n * @notice Used when there is more wS than OS in the pool.\n * OS is minted and swapped for wS against the pool,\n * more OS is minted and added back into the pool with the swapped out wS.\n * The OS/wS price is > 1.0 so OS is being sold at a premium.\n * @param _osAmount Amount of OS to swap into the pool.\n */\n function swapOTokensToPool(uint256 _osAmount)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n skimPool\n {\n require(_osAmount > 0, \"Must swap something\");\n\n // 1. Mint OS so it can be swapped into the pool\n\n // There can be OS in the strategy from skimming the pool\n uint256 osInStrategy = IERC20(os).balanceOf(address(this));\n require(_osAmount >= osInStrategy, \"Too much OS in strategy\");\n uint256 osToMint = _osAmount - osInStrategy;\n\n // Mint the required OS tokens to this strategy\n IVault(vaultAddress).mintForStrategy(osToMint);\n\n // 2. Swap OS for wS against the pool\n _swapExactTokensForTokens(_osAmount, os, ws);\n\n // The wS is from the swap and any wS that was sitting in the strategy\n uint256 wsDepositAmount = IERC20(ws).balanceOf(address(this));\n\n // 3. Add wS and OS back to the pool in proportion to the pool's reserves\n (uint256 osDepositAmount, uint256 lpTokens) = _deposit(wsDepositAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osToMint + osDepositAmount);\n // Emit event for the swap\n emit SwapOTokensToPool(\n osToMint,\n wsDepositAmount,\n osDepositAmount,\n lpTokens\n );\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Get the wS value of assets in the strategy and SwapX pool.\n * The value of the assets in the pool is calculated assuming the pool is balanced.\n * This way the value can not be manipulated by changing the pool's token balances.\n * @param _asset Address of the Wrapped S (wS) token\n * @return balance Total value in wS.\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == ws, \"Unsupported asset\");\n\n // wS balance needed here for the balance check that happens from vault during depositing.\n balance = IERC20(ws).balanceOf(address(this));\n\n // This assumes 1 gauge LP token = 1 pool LP token\n uint256 lpTokens = IGauge(gauge).balanceOf(address(this));\n if (lpTokens == 0) return balance;\n\n // Add the strategy’s share of the wS and OS tokens in the SwapX pool if the pool was balanced.\n balance += _lpValue(lpTokens);\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == ws;\n }\n\n /**\n * @notice Collect accumulated SWPx (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect SWPx rewards from the gauge\n IGauge(gauge).getReward();\n\n _collectRewardTokens();\n }\n\n /***************************************\n Internal SwapX Pool and Gauge Functions\n ****************************************/\n\n /**\n * @dev Calculate the required amount of OS to mint based on the wS amount.\n * This ensures the proportion of OS tokens being added to the pool matches the proportion of wS tokens.\n * For example, if the added wS tokens is 10% of existing wS tokens in the pool,\n * then the OS tokens being added should also be 10% of the OS tokens in the pool.\n * @param _wsAmount Amount of Wrapped S (wS) to be added to the pool.\n * @return osAmount Amount of OS to be minted and added to the pool.\n */\n function _calcTokensToMint(uint256 _wsAmount)\n internal\n view\n returns (uint256 osAmount)\n {\n (uint256 wsReserves, uint256 osReserves, ) = IPair(pool).getReserves();\n require(wsReserves > 0, \"Empty pool\");\n\n // OS to add = (wS being added * OS in pool) / wS in pool\n osAmount = (_wsAmount * osReserves) / wsReserves;\n }\n\n /**\n * @dev Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n * from the pool.\n * @param _wsAmount Amount of Wrapped S (wS) to be removed from the pool.\n * @return lpTokens Amount of SwapX pool LP tokens to burn.\n */\n function _calcTokensToBurn(uint256 _wsAmount)\n internal\n view\n returns (uint256 lpTokens)\n {\n /* The SwapX pool proportionally returns the reserve tokens when removing liquidity.\n * First, calculate the proportion of required wS tokens against the pools wS reserves.\n * That same proportion is used to calculate the required amount of pool LP tokens.\n * For example, if the required wS tokens is 10% of the pool's wS reserves,\n * then 10% of the pool's LP supply needs to be burned.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognizant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on, the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n (uint256 wsReserves, , ) = IPair(pool).getReserves();\n require(wsReserves > 0, \"Empty pool\");\n\n lpTokens = (_wsAmount * IPair(pool).totalSupply()) / wsReserves;\n lpTokens += 1; // Add 1 to ensure we get enough LP tokens with rounding\n }\n\n /**\n * @dev Deposit Wrapped S (wS) and OS liquidity to the SwapX pool\n * and stake the pool's LP token in the gauge.\n * @param _wsAmount Amount of Wrapped S (wS) to deposit.\n * @param _osAmount Amount of OS to deposit.\n * @return lpTokens Amount of SwapX pool LP tokens minted.\n */\n function _depositToPoolAndGauge(uint256 _wsAmount, uint256 _osAmount)\n internal\n returns (uint256 lpTokens)\n {\n // Transfer wS to the pool\n IERC20(ws).safeTransfer(pool, _wsAmount);\n // Transfer OS to the pool\n IERC20(os).safeTransfer(pool, _osAmount);\n\n // Mint LP tokens from the pool\n lpTokens = IPair(pool).mint(address(this));\n\n // Deposit the pool's LP tokens into the gauge\n IGauge(gauge).deposit(lpTokens);\n }\n\n /**\n * @dev Withdraw pool LP tokens from the gauge and remove wS and OS from the pool.\n * @param _lpTokens Amount of SwapX pool LP tokens to withdraw from the gauge\n */\n function _withdrawFromGaugeAndPool(uint256 _lpTokens) internal {\n require(\n IGauge(gauge).balanceOf(address(this)) >= _lpTokens,\n \"Not enough LP tokens in gauge\"\n );\n\n // Withdraw pool LP tokens from the gauge\n IGauge(gauge).withdraw(_lpTokens);\n\n // Transfer the pool LP tokens to the pool\n IERC20(pool).safeTransfer(pool, _lpTokens);\n\n // Burn the LP tokens and transfer the wS and OS back to the strategy\n IPair(pool).burn(address(this));\n }\n\n /**\n * @dev Withdraw all pool LP tokens from the gauge when it's in emergency mode\n * and remove wS and OS from the pool.\n */\n function _emergencyWithdrawFromGaugeAndPool() internal {\n // Withdraw all pool LP tokens from the gauge\n IGauge(gauge).emergencyWithdraw();\n\n // Get the pool LP tokens in strategy\n uint256 _lpTokens = IERC20(pool).balanceOf(address(this));\n\n // Transfer the pool LP tokens to the pool\n IERC20(pool).safeTransfer(pool, _lpTokens);\n\n // Burn the LP tokens and transfer the wS and OS back to the strategy\n IPair(pool).burn(address(this));\n }\n\n /**\n * @dev Swap exact amount of tokens for another token against the pool.\n * @param _amountIn Amount of tokens to swap into the pool.\n * @param _tokenIn Address of the token going into the pool.\n * @param _tokenOut Address of the token being swapped out of the pool.\n */\n function _swapExactTokensForTokens(\n uint256 _amountIn,\n address _tokenIn,\n address _tokenOut\n ) internal {\n // Transfer in tokens to the pool\n IERC20(_tokenIn).safeTransfer(pool, _amountIn);\n\n // Calculate how much out tokens we get from the swap\n uint256 amountOut = IPair(pool).getAmountOut(_amountIn, _tokenIn);\n\n // Safety check that we are dealing with the correct pool tokens\n require(\n (_tokenIn == ws && _tokenOut == os) ||\n (_tokenIn == os && _tokenOut == ws),\n \"Unsupported swap\"\n );\n\n // Work out the correct order of the amounts for the pool\n (uint256 amount0, uint256 amount1) = _tokenIn == ws\n ? (uint256(0), amountOut)\n : (amountOut, 0);\n\n // Perform the swap on the pool\n IPair(pool).swap(amount0, amount1, address(this), new bytes(0));\n\n // The slippage protection against the amount out is indirectly done\n // via the improvePoolBalance\n }\n\n /// @dev Calculate the value of a LP position in a SwapX stable pool\n /// if the pool was balanced.\n /// @param _lpTokens Amount of LP tokens in the SwapX pool\n /// @return value The wS value of the LP tokens when the pool is balanced\n function _lpValue(uint256 _lpTokens) internal view returns (uint256 value) {\n // Get total supply of LP tokens\n uint256 totalSupply = IPair(pool).totalSupply();\n if (totalSupply == 0) return 0;\n\n // Get the current reserves of the pool\n (uint256 wsReserves, uint256 osReserves, ) = IPair(pool).getReserves();\n\n // Calculate the invariant of the pool assuming both tokens have 18 decimals.\n // k is scaled to 18 decimals.\n uint256 k = _invariant(wsReserves, osReserves);\n\n // If x = y, let’s denote x = y = z (where z is the common reserve value)\n // Substitute z into the invariant:\n // k = z^3 * z + z * z^3\n // k = 2 * z^4\n // Going back the other way to calculate the common reserve value z\n // z = (k / 2) ^ (1/4)\n // the total value of the pool when x = y is 2 * z, which is 2 * (k / 2) ^ (1/4)\n uint256 zSquared = sqrt((k * 1e18) / 2); // 18 + 18 = 36 decimals becomes 18 decimals after sqrt\n uint256 z = sqrt(zSquared * 1e18); // 18 + 18 = 36 decimals becomes 18 decimals after sqrt\n uint256 totalValueOfPool = 2 * z;\n\n // lp value = lp tokens * value of pool / total supply\n value = (_lpTokens * totalValueOfPool) / totalSupply;\n }\n\n /**\n * @dev Compute the invariant for a SwapX stable pool.\n * This assumed both x and y tokens are to 18 decimals which is checked in the constructor.\n * invariant: k = x^3 * y + x * y^3\n * @dev This implementation is copied from SwapX's Pair contract.\n * @param _x The amount of Wrapped S (wS) tokens in the pool\n * @param _y The amount of the OS tokens in the pool\n * @return k The invariant of the SwapX stable pool\n */\n function _invariant(uint256 _x, uint256 _y)\n internal\n pure\n returns (uint256 k)\n {\n uint256 _a = (_x * _y) / PRECISION;\n uint256 _b = ((_x * _x) / PRECISION + (_y * _y) / PRECISION);\n // slither-disable-next-line divide-before-multiply\n k = (_a * _b) / PRECISION;\n }\n\n /**\n * @dev Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalSupply = IERC20(os).totalSupply();\n\n if (\n _totalSupply > 0 &&\n _totalVaultValue.divPrecisely(_totalSupply) < SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Setters\n ****************************************/\n\n /**\n * @notice Set the maximum deviation from the OS/wS peg (1e18) before deposits are reverted.\n * @param _maxDepeg the OS/wS price from peg (1e18) in 18 decimals.\n * eg 0.01e18 or 1e16 is 1% which is 100 basis points.\n */\n function setMaxDepeg(uint256 _maxDepeg) external onlyGovernor {\n maxDepeg = _maxDepeg;\n\n emit MaxDepegUpdated(_maxDepeg);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve SwapX gauge contract to transfer SwapX pool LP tokens\n // This is needed for deposits of SwapX pool LP tokens into the gauge.\n // slither-disable-next-line unused-return\n IPair(pool).approve(address(gauge), type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/sonic/SonicValidatorDelegator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { ISFC } from \"../../interfaces/sonic/ISFC.sol\";\nimport { IWrappedSonic } from \"../../interfaces/sonic/IWrappedSonic.sol\";\n\n/**\n * @title Manages delegation to Sonic validators\n * @notice This contract implements all the required functionality to delegate to,\n undelegate from and withdraw from validators.\n * @author Origin Protocol Inc\n */\nabstract contract SonicValidatorDelegator is InitializableAbstractStrategy {\n /// @notice Address of Sonic's wrapped S token\n address public immutable wrappedSonic;\n /// @notice Sonic's Special Fee Contract (SFC)\n ISFC public immutable sfc;\n\n /// @notice a unique ID for each withdrawal request\n uint256 public nextWithdrawId;\n /// @notice Sonic (S) that is pending withdrawal after undelegating\n uint256 public pendingWithdrawals;\n\n /// @notice List of supported validator IDs that can be delegated to\n uint256[] public supportedValidators;\n\n /// @notice Default validator id to deposit to\n uint256 public defaultValidatorId;\n\n struct WithdrawRequest {\n uint256 validatorId;\n uint256 undelegatedAmount;\n uint256 timestamp;\n }\n /// @notice Mapping of withdrawIds to validatorIds and undelegatedAmounts\n mapping(uint256 => WithdrawRequest) public withdrawals;\n\n /// @notice Address of the registrator - allowed to register, exit and remove validators\n address public validatorRegistrator;\n\n // For future use\n uint256[44] private __gap;\n\n event Delegated(uint256 indexed validatorId, uint256 delegatedAmount);\n event Undelegated(\n uint256 indexed withdrawId,\n uint256 indexed validatorId,\n uint256 undelegatedAmount\n );\n event Withdrawn(\n uint256 indexed withdrawId,\n uint256 indexed validatorId,\n uint256 undelegatedAmount,\n uint256 withdrawnAmount\n );\n event RegistratorChanged(address indexed newAddress);\n event SupportedValidator(uint256 indexed validatorId);\n event UnsupportedValidator(uint256 indexed validatorId);\n event DefaultValidatorIdChanged(uint256 indexed validatorId);\n\n /// @dev Throws if called by any account other than the Registrator or Strategist\n modifier onlyRegistratorOrStrategist() {\n require(\n msg.sender == validatorRegistrator ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Registrator or Strategist\"\n );\n _;\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wrappedSonic,\n address _sfc\n ) InitializableAbstractStrategy(_baseConfig) {\n wrappedSonic = _wrappedSonic;\n sfc = ISFC(_sfc);\n }\n\n function initialize() external virtual onlyGovernor initializer {\n address[] memory rewardTokens = new address[](0);\n address[] memory assets = new address[](1);\n address[] memory pTokens = new address[](1);\n\n assets[0] = address(wrappedSonic);\n pTokens[0] = address(platformAddress);\n\n InitializableAbstractStrategy._initialize(\n rewardTokens,\n assets,\n pTokens\n );\n }\n\n /// @notice Returns the total value of Sonic (S) that is delegated validators.\n /// Wrapped Sonic (wS) deposits that are still to be delegated and any undelegated amounts\n /// still pending a withdrawal.\n /// @param _asset Address of Wrapped Sonic (wS) token\n /// @return balance Total value managed by the strategy\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n\n // add the Wrapped Sonic (wS) in the strategy from deposits that are still to be delegated\n // and any undelegated amounts still pending a withdrawal\n balance =\n IERC20(wrappedSonic).balanceOf(address(this)) +\n pendingWithdrawals;\n\n // For each supported validator, get the staked amount and pending rewards\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; i++) {\n uint256 validator = supportedValidators[i];\n balance += sfc.getStake(address(this), validator);\n balance += sfc.pendingRewards(address(this), validator);\n }\n }\n\n /**\n * @dev Delegate from this strategy to a specific Sonic validator. Called\n * automatically on asset deposit\n * @param _amount the amount of Sonic (S) to delegate.\n */\n function _delegate(uint256 _amount) internal {\n require(\n isSupportedValidator(defaultValidatorId),\n \"Validator not supported\"\n );\n\n // unwrap Wrapped Sonic (wS) to native Sonic (S)\n IWrappedSonic(wrappedSonic).withdraw(_amount);\n\n //slither-disable-next-line arbitrary-send-eth\n sfc.delegate{ value: _amount }(defaultValidatorId);\n\n emit Delegated(defaultValidatorId, _amount);\n }\n\n /**\n * @notice Undelegate from a specific Sonic validator.\n * This needs to be followed by a `withdrawFromSFC` two weeks later.\n * @param _validatorId The Sonic validator ID to undelegate from.\n * @param _undelegateAmount the amount of Sonic (S) to undelegate.\n * @return withdrawId The unique ID of the withdrawal request.\n */\n function undelegate(uint256 _validatorId, uint256 _undelegateAmount)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n returns (uint256 withdrawId)\n {\n withdrawId = _undelegate(_validatorId, _undelegateAmount);\n }\n\n function _undelegate(uint256 _validatorId, uint256 _undelegateAmount)\n internal\n returns (uint256 withdrawId)\n {\n // Can still undelegate even if the validator is no longer supported\n require(_undelegateAmount > 0, \"Must undelegate something\");\n\n uint256 amountDelegated = sfc.getStake(address(this), _validatorId);\n require(\n _undelegateAmount <= amountDelegated,\n \"Insufficient delegation\"\n );\n\n withdrawId = nextWithdrawId++;\n\n withdrawals[withdrawId] = WithdrawRequest(\n _validatorId,\n _undelegateAmount,\n block.timestamp\n );\n pendingWithdrawals += _undelegateAmount;\n\n sfc.undelegate(_validatorId, withdrawId, _undelegateAmount);\n\n emit Undelegated(withdrawId, _validatorId, _undelegateAmount);\n }\n\n /**\n * @notice Withdraw native S from a previously undelegated validator.\n * The native S is wrapped wS and transferred to the Vault.\n * @param _withdrawId The unique withdraw ID used to `undelegate`\n * @return withdrawnAmount The amount of Sonic (S) withdrawn.\n * This can be less than the undelegated amount in the event of slashing.\n */\n function withdrawFromSFC(uint256 _withdrawId)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n returns (uint256 withdrawnAmount)\n {\n require(_withdrawId < nextWithdrawId, \"Invalid withdrawId\");\n\n // Can still withdraw even if the validator is no longer supported\n // Load the withdrawal from storage into memory\n WithdrawRequest memory withdrawal = withdrawals[_withdrawId];\n require(!isWithdrawnFromSFC(_withdrawId), \"Already withdrawn\");\n\n withdrawals[_withdrawId].undelegatedAmount = 0;\n pendingWithdrawals -= withdrawal.undelegatedAmount;\n\n uint256 sBalanceBefore = address(this).balance;\n\n // Try to withdraw from SFC\n try sfc.withdraw(withdrawal.validatorId, _withdrawId) {\n // continue below\n } catch (bytes memory err) {\n bytes4 errorSelector = bytes4(err);\n\n // If the validator has been fully slashed, SFC's withdraw function will\n // revert with a StakeIsFullySlashed custom error.\n if (errorSelector == ISFC.StakeIsFullySlashed.selector) {\n // The validator was fully slashed, so all the delegated amounts were lost.\n // Will swallow the error as we still want to update the\n // withdrawals and pendingWithdrawals storage variables.\n\n // The return param defaults to zero but lets set it explicitly so it's clear\n withdrawnAmount = 0;\n\n emit Withdrawn(\n _withdrawId,\n withdrawal.validatorId,\n withdrawal.undelegatedAmount,\n withdrawnAmount\n );\n\n // Exit here as there is nothing to transfer to the Vault\n return withdrawnAmount;\n } else {\n // Bubble up any other SFC custom errors.\n // Inline assembly is currently the only way to generically rethrow the exact same custom error\n // from the raw bytes err in a catch block while preserving its original selector and parameters.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n revert(add(32, err), mload(err))\n }\n }\n }\n\n // Set return parameter\n withdrawnAmount = address(this).balance - sBalanceBefore;\n\n // Wrap Sonic (S) to Wrapped Sonic (wS)\n IWrappedSonic(wrappedSonic).deposit{ value: withdrawnAmount }();\n\n // Transfer the Wrapped Sonic (wS) to the Vault\n _withdraw(vaultAddress, wrappedSonic, withdrawnAmount);\n\n // withdrawal.undelegatedAmount & withdrawnAmount can differ in case of slashing\n emit Withdrawn(\n _withdrawId,\n withdrawal.validatorId,\n withdrawal.undelegatedAmount,\n withdrawnAmount\n );\n }\n\n /// @notice returns a bool whether a withdrawalId has already been withdrawn or not\n /// @param _withdrawId The unique withdraw ID used to `undelegate`\n function isWithdrawnFromSFC(uint256 _withdrawId)\n public\n view\n returns (bool)\n {\n WithdrawRequest memory withdrawal = withdrawals[_withdrawId];\n require(withdrawal.validatorId > 0, \"Invalid withdrawId\");\n return withdrawal.undelegatedAmount == 0;\n }\n\n /**\n * @notice Restake any pending validator rewards for all supported validators\n * @param _validatorIds List of Sonic validator IDs to restake rewards\n */\n function restakeRewards(uint256[] calldata _validatorIds)\n external\n nonReentrant\n {\n for (uint256 i = 0; i < _validatorIds.length; ++i) {\n require(\n isSupportedValidator(_validatorIds[i]),\n \"Validator not supported\"\n );\n\n uint256 rewards = sfc.pendingRewards(\n address(this),\n _validatorIds[i]\n );\n\n if (rewards > 0) {\n sfc.restakeRewards(_validatorIds[i]);\n }\n }\n\n // The SFC contract will emit Delegated and RestakedRewards events.\n // The checkBalance function should not change as the pending rewards will moved to the staked amount.\n }\n\n /**\n * @notice Claim any pending rewards from validators\n * @param _validatorIds List of Sonic validator IDs to claim rewards\n */\n function collectRewards(uint256[] calldata _validatorIds)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n {\n uint256 sBalanceBefore = address(this).balance;\n\n for (uint256 i = 0; i < _validatorIds.length; ++i) {\n uint256 rewards = sfc.pendingRewards(\n address(this),\n _validatorIds[i]\n );\n\n if (rewards > 0) {\n // The SFC contract will emit ClaimedRewards(delegator (this), validatorId, rewards)\n sfc.claimRewards(_validatorIds[i]);\n }\n }\n\n uint256 rewardsAmount = address(this).balance - sBalanceBefore;\n\n // Wrap Sonic (S) to Wrapped Sonic (wS)\n IWrappedSonic(wrappedSonic).deposit{ value: rewardsAmount }();\n\n // Transfer the Wrapped Sonic (wS) to the Vault\n _withdraw(vaultAddress, wrappedSonic, rewardsAmount);\n }\n\n /**\n * @notice To receive native S from SFC and Wrapped Sonic (wS)\n *\n * @dev This does not prevent donating S tokens to the contract\n * as wrappedSonic has a `withdrawTo` function where a third party\n * owner of wrappedSonic can withdraw to this contract.\n */\n receive() external payable {\n require(\n msg.sender == address(sfc) || msg.sender == wrappedSonic,\n \"S not from allowed contracts\"\n );\n }\n\n /***************************************\n Admin functions\n ****************************************/\n\n /// @notice Set the address of the Registrator which can undelegate, withdraw and collect rewards\n /// @param _validatorRegistrator The address of the Registrator\n function setRegistrator(address _validatorRegistrator)\n external\n onlyGovernor\n {\n validatorRegistrator = _validatorRegistrator;\n emit RegistratorChanged(_validatorRegistrator);\n }\n\n /// @notice Set the default validatorId to delegate to on deposit\n /// @param _validatorId The validator identifier. eg 18\n function setDefaultValidatorId(uint256 _validatorId)\n external\n onlyRegistratorOrStrategist\n {\n require(isSupportedValidator(_validatorId), \"Validator not supported\");\n defaultValidatorId = _validatorId;\n emit DefaultValidatorIdChanged(_validatorId);\n }\n\n /// @notice Allows a validator to be delegated to by the Registrator\n /// @param _validatorId The validator identifier. eg 18\n function supportValidator(uint256 _validatorId) external onlyGovernor {\n require(\n !isSupportedValidator(_validatorId),\n \"Validator already supported\"\n );\n\n supportedValidators.push(_validatorId);\n\n emit SupportedValidator(_validatorId);\n }\n\n /// @notice Removes a validator from the supported list.\n /// Unsupported validators can still be undelegated from, withdrawn from and rewards collected.\n /// @param _validatorId The validator identifier. eg 18\n function unsupportValidator(uint256 _validatorId) external onlyGovernor {\n require(isSupportedValidator(_validatorId), \"Validator not supported\");\n\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; ++i) {\n if (supportedValidators[i] == _validatorId) {\n supportedValidators[i] = supportedValidators[validatorLen - 1];\n supportedValidators.pop();\n break;\n }\n }\n\n uint256 stake = sfc.getStake(address(this), _validatorId);\n\n // undelegate if validator still has funds staked\n if (stake > 0) {\n _undelegate(_validatorId, stake);\n }\n emit UnsupportedValidator(_validatorId);\n }\n\n /// @notice Returns the length of the supportedValidators array\n function supportedValidatorsLength() external view returns (uint256) {\n return supportedValidators.length;\n }\n\n /// @notice Returns whether a validator is supported by this strategy\n /// @param _validatorId The validator identifier\n function isSupportedValidator(uint256 _validatorId)\n public\n view\n returns (bool)\n {\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; ++i) {\n if (supportedValidators[i] == _validatorId) {\n return true;\n }\n }\n return false;\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal virtual;\n}\n" + }, + "contracts/strategies/VaultValueChecker.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IOUSD } from \"../interfaces/IOUSD.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract VaultValueChecker {\n IVault public immutable vault;\n IOUSD public immutable ousd;\n // Snapshot expiration time in seconds.\n // Used to prevent accidental use of an old snapshot, but\n // is not zero to allow easy testing of strategist actions in fork testing\n uint256 constant SNAPSHOT_EXPIRES = 5 * 60;\n\n struct Snapshot {\n uint256 vaultValue;\n uint256 totalSupply;\n uint256 time;\n }\n // By doing per user snapshots, we prevent a reentrancy attack\n // from a third party that updates the snapshot in the middle\n // of an allocation process\n\n mapping(address => Snapshot) public snapshots;\n\n constructor(address _vault, address _ousd) {\n vault = IVault(_vault);\n ousd = IOUSD(_ousd);\n }\n\n function takeSnapshot() external {\n snapshots[msg.sender] = Snapshot({\n vaultValue: vault.totalValue(),\n totalSupply: ousd.totalSupply(),\n time: block.timestamp\n });\n }\n\n function checkDelta(\n int256 expectedProfit,\n int256 profitVariance,\n int256 expectedVaultChange,\n int256 vaultChangeVariance\n ) external {\n // Intentionaly not view so that this method shows up in TX builders\n Snapshot memory snapshot = snapshots[msg.sender];\n int256 vaultChange = toInt256(vault.totalValue()) -\n toInt256(snapshot.vaultValue);\n int256 supplyChange = toInt256(ousd.totalSupply()) -\n toInt256(snapshot.totalSupply);\n int256 profit = vaultChange - supplyChange;\n\n require(\n snapshot.time >= block.timestamp - SNAPSHOT_EXPIRES,\n \"Snapshot too old\"\n );\n require(snapshot.time <= block.timestamp, \"Snapshot too new\");\n require(profit >= expectedProfit - profitVariance, \"Profit too low\");\n require(profit <= expectedProfit + profitVariance, \"Profit too high\");\n require(\n vaultChange >= expectedVaultChange - vaultChangeVariance,\n \"Vault value change too low\"\n );\n require(\n vaultChange <= expectedVaultChange + vaultChangeVariance,\n \"Vault value change too high\"\n );\n }\n\n function toInt256(uint256 value) internal pure returns (int256) {\n // From openzeppelin math/SafeCast.sol\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(\n value <= uint256(type(int256).max),\n \"SafeCast: value doesn't fit in an int256\"\n );\n return int256(value);\n }\n}\n\ncontract OETHVaultValueChecker is VaultValueChecker {\n constructor(address _vault, address _ousd)\n VaultValueChecker(_vault, _ousd)\n {}\n}\n" + }, + "contracts/swapper/Swapper1InchV5.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @notice 1Inch Pathfinder V5 implementation of the general ISwapper interface.\n * @author Origin Protocol Inc\n * @dev It is possible that dust token amounts are left in this contract after a swap.\n * This can happen with some tokens that don't send the full transfer amount.\n * These dust amounts can build up over time and be used by anyone who calls the `swap` function.\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IAggregationExecutor, IOneInchRouter, SwapDescription } from \"../interfaces/IOneInch.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\n\ncontract Swapper1InchV5 is ISwapper {\n using SafeERC20 for IERC20;\n\n /// @notice 1Inch router contract to give allowance to perform swaps\n address public constant SWAP_ROUTER =\n 0x1111111254EEB25477B68fb85Ed929f73A960582;\n\n // swap(address,(address,address,address,address,uint256,uint256,uint256),bytes,bytes)\n bytes4 internal constant SWAP_SELECTOR = 0x12aa3caf;\n // unoswapTo(address,address,uint256,uint256,uint256[])\n bytes4 internal constant UNISWAP_SELECTOR = 0xf78dc253;\n // uniswapV3SwapTo(address,uint256,uint256,uint256[])\n bytes4 internal constant UNISWAPV3_SELECTOR = 0xbc80f1a8;\n\n /**\n * @notice Strategist swaps assets sitting in the contract of the `assetHolder`.\n * @param _fromAsset The token address of the asset being sold by the vault.\n * @param _toAsset The token address of the asset being purchased by the vault.\n * @param _fromAssetAmount The amount of assets being sold by the vault.\n * @param _minToAssetAmount The minimum amount of assets to be purchased.\n * @param _data RLP encoded executer address and bytes data. This is re-encoded tx.data from 1Inch swap API\n */\n function swap(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n ) external override returns (uint256 toAssetAmount) {\n // Decode the function selector from the RLP encoded _data param\n bytes4 swapSelector = bytes4(_data[:4]);\n\n if (swapSelector == SWAP_SELECTOR) {\n // Decode the executer address and data from the RLP encoded _data param\n (, address executer, bytes memory executerData) = abi.decode(\n _data,\n (bytes4, address, bytes)\n );\n SwapDescription memory swapDesc = SwapDescription({\n srcToken: IERC20(_fromAsset),\n dstToken: IERC20(_toAsset),\n srcReceiver: payable(executer),\n dstReceiver: payable(msg.sender),\n amount: _fromAssetAmount,\n minReturnAmount: _minToAssetAmount,\n flags: 4 // 1st bit _PARTIAL_FILL, 2nd bit _REQUIRES_EXTRA_ETH, 3rd bit _SHOULD_CLAIM\n });\n (toAssetAmount, ) = IOneInchRouter(SWAP_ROUTER).swap(\n IAggregationExecutor(executer),\n swapDesc,\n hex\"\",\n executerData\n );\n } else if (swapSelector == UNISWAP_SELECTOR) {\n // Need to get the Uniswap pools data from the _data param\n (, uint256[] memory pools) = abi.decode(_data, (bytes4, uint256[]));\n toAssetAmount = IOneInchRouter(SWAP_ROUTER).unoswapTo(\n payable(msg.sender),\n IERC20(_fromAsset),\n _fromAssetAmount,\n _minToAssetAmount,\n pools\n );\n } else if (swapSelector == UNISWAPV3_SELECTOR) {\n // Need to get the Uniswap pools data from the _data param\n // slither-disable-next-line uninitialized-storage\n (, uint256[] memory pools) = abi.decode(_data, (bytes4, uint256[]));\n toAssetAmount = IOneInchRouter(SWAP_ROUTER).uniswapV3SwapTo(\n payable(msg.sender),\n _fromAssetAmount,\n _minToAssetAmount,\n pools\n );\n } else {\n revert(\"Unsupported swap function\");\n }\n }\n\n /**\n * @notice Approve assets for swapping.\n * @param _assets Array of token addresses to approve.\n * @dev unlimited approval is used as no tokens sit in this contract outside a transaction.\n */\n function approveAssets(address[] memory _assets) external {\n for (uint256 i = 0; i < _assets.length; ++i) {\n // Give the 1Inch router approval to transfer unlimited assets\n IERC20(_assets[i]).safeApprove(SWAP_ROUTER, type(uint256).max);\n }\n }\n}\n" + }, + "contracts/token/BridgedWOETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\n\ncontract BridgedWOETH is\n Governable,\n AccessControlEnumerable,\n Initializable,\n ERC20\n{\n bytes32 public constant MINTER_ROLE = keccak256(\"MINTER_ROLE\");\n bytes32 public constant BURNER_ROLE = keccak256(\"BURNER_ROLE\");\n\n constructor() ERC20(\"Wrapped OETH\", \"wOETH\") {\n // Nobody owns the implementation\n _setGovernor(address(0));\n }\n\n /**\n * @dev Initialize the proxy and set the Governor\n */\n function initialize() external initializer {\n // Governor can grant Minter/Burner roles\n _setupRole(DEFAULT_ADMIN_ROLE, _governor());\n }\n\n /**\n * @dev Mint tokens for `account`\n * @param account Address to mint tokens for\n * @param amount Amount of tokens to mint\n */\n function mint(address account, uint256 amount)\n external\n onlyRole(MINTER_ROLE)\n nonReentrant\n {\n _mint(account, amount);\n }\n\n /**\n * @dev Burns tokens from `account`\n * @param account Address to burn tokens from\n * @param amount Amount of tokens to burn\n */\n function burn(address account, uint256 amount)\n external\n onlyRole(BURNER_ROLE)\n nonReentrant\n {\n _burn(account, amount);\n }\n\n /**\n * @dev Burns tokens from `msg.sender`\n * @param amount Amount of tokens to burn\n */\n function burn(uint256 amount) external onlyRole(BURNER_ROLE) nonReentrant {\n _burn(msg.sender, amount);\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return \"Wrapped OETH\";\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return \"wOETH\";\n }\n\n /**\n * @dev Returns the decimals of the token\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETH is OUSD {\n function symbol() external pure override returns (string memory) {\n return \"OETH\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Origin Ether\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETHBase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBase is OUSD {\n constructor() {\n // Nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n function symbol() external pure override returns (string memory) {\n return \"superOETHb\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Super OETH\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETHPlume.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title Super OETH (Plume) Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETHPlume is OUSD {\n constructor() {\n // Nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n function symbol() external pure override returns (string memory) {\n return \"superOETHp\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Super OETH\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OSonic.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title Origin Sonic (OS) token on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonic is OUSD {\n function symbol() external pure override returns (string memory) {\n return \"OS\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Origin Sonic\";\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/token/OUSDResolutionUpgrade.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract OUSDResolutionUpgrade {\n enum RebaseOptions {\n NotSet,\n OptOut,\n OptIn\n }\n\n // From Initializable\n bool private initialized;\n bool private initializing;\n uint256[50] private ______igap;\n\n // From InitializableERC20Detailed\n uint256[100] private _____ugap;\n string private _name;\n string private _symbol;\n uint8 private _decimals;\n\n // From OUSD\n uint256 private constant MAX_SUPPLY = ~uint128(0); // (2^128) - 1\n uint256 public _totalSupply;\n mapping(address => mapping(address => uint256)) private _allowances;\n address public vaultAddress = address(0);\n mapping(address => uint256) private _creditBalances;\n uint256 private _rebasingCredits;\n uint256 private _rebasingCreditsPerToken;\n uint256 public nonRebasingSupply;\n mapping(address => uint256) public nonRebasingCreditsPerToken;\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) public isUpgraded;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() public view returns (uint256) {\n return _rebasingCreditsPerToken / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCredits() public view returns (uint256) {\n return _rebasingCredits / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() public view returns (uint256) {\n return _rebasingCreditsPerToken;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() public view returns (uint256) {\n return _rebasingCredits;\n }\n\n function upgradeGlobals() external {\n require(isUpgraded[address(0)] == 0, \"Globals already upgraded\");\n require(_rebasingCredits > 0, \"Sanity _rebasingCredits\");\n require(\n _rebasingCreditsPerToken > 0,\n \"Sanity _rebasingCreditsPerToken\"\n );\n isUpgraded[address(0)] = 1;\n _rebasingCredits = _rebasingCredits * RESOLUTION_INCREASE;\n _rebasingCreditsPerToken =\n _rebasingCreditsPerToken *\n RESOLUTION_INCREASE;\n }\n\n function upgradeAccounts(address[] calldata accounts) external {\n for (uint256 i = 0; i < accounts.length; i++) {\n address account = accounts[i];\n require(account != address(0), \"Reserved\");\n require(isUpgraded[account] == 0, \"Account already upgraded\");\n isUpgraded[account] = 1;\n\n // Handle special for non-rebasing accounts\n uint256 nrc = nonRebasingCreditsPerToken[account];\n if (nrc > 0) {\n require(nrc < 1e18, \"Account already highres\");\n nonRebasingCreditsPerToken[account] = nrc * RESOLUTION_INCREASE;\n }\n // Upgrade balance\n uint256 balance = _creditBalances[account];\n require(balance > 0, \"Will not upgrade zero balance\");\n _creditBalances[account] = balance * RESOLUTION_INCREASE;\n }\n }\n\n function creditsBalanceOfHighres(address _account)\n public\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n _creditBalances[_account],\n _creditsPerToken(_account),\n isUpgraded[_account] == 1\n );\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n if (nonRebasingCreditsPerToken[_account] != 0) {\n return nonRebasingCreditsPerToken[_account];\n } else {\n return _rebasingCreditsPerToken;\n }\n }\n}\n" + }, + "contracts/token/WOETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC4626 } from \"../../lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { OETH } from \"./OETH.sol\";\n\n/**\n * @title Wrapped OETH Token Contract\n * @author Origin Protocol Inc\n *\n * @dev An important capability of this contract is that it isn't susceptible to changes of the\n * exchange rate of WOETH/OETH if/when someone sends the underlying asset (OETH) to the contract.\n * If OETH weren't rebasing this could be achieved by solely tracking the ERC20 transfers of the OETH\n * token on mint, deposit, redeem, withdraw. The issue is that OETH is rebasing and OETH balances\n * will change when the token rebases.\n * For that reason the contract logic checks the actual underlying OETH token balance only once\n * (either on a fresh contract creation or upgrade) and considering the WOETH supply and\n * rebasingCreditsPerToken calculates the _adjuster. Once the adjuster is calculated any donations\n * to the contract are ignored. The totalSupply (instead of querying OETH balance) works off of\n * adjuster the current WOETH supply and rebasingCreditsPerToken. This makes WOETH value accrual\n * completely follow OETH's value accrual.\n * WOETH is safe to use in lending markets as the VualtCore's _rebase contains safeguards preventing\n * any sudden large rebases.\n */\n\ncontract WOETH is ERC4626, Governable, Initializable {\n using SafeERC20 for IERC20;\n /* This is a 1e27 adjustment constant that expresses the difference in exchange rate between\n * OETH's rebase since inception (expressed with rebasingCreditsPerToken) and WOETH to OETH\n * conversion.\n *\n * If WOETH and OETH are deployed at the same time, the value of adjuster is a neutral 1e27\n */\n uint256 public adjuster;\n uint256[49] private __gap;\n\n // no need to set ERC20 name and symbol since they are overridden in WOETH & WOETHBase\n constructor(ERC20 underlying_) ERC20(\"\", \"\") ERC4626(underlying_) {}\n\n /**\n * @notice Enable OETH rebasing for this contract\n */\n function initialize() external onlyGovernor initializer {\n OETH(address(asset())).rebaseOptIn();\n\n initialize2();\n }\n\n /**\n * @notice secondary initializer that newly deployed contracts will execute as part\n * of primary initialize function and the existing contracts will have it called\n * as a governance operation.\n */\n function initialize2() public onlyGovernor {\n require(adjuster == 0, \"Initialize2 already called\");\n\n if (totalSupply() == 0) {\n adjuster = 1e27;\n } else {\n adjuster =\n (rebasingCreditsPerTokenHighres() *\n ERC20(asset()).balanceOf(address(this))) /\n totalSupply();\n }\n }\n\n function name()\n public\n view\n virtual\n override(ERC20, IERC20Metadata)\n returns (string memory)\n {\n return \"Wrapped OETH\";\n }\n\n function symbol()\n public\n view\n virtual\n override(ERC20, IERC20Metadata)\n returns (string memory)\n {\n return \"wOETH\";\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends. Cannot transfer OETH\n * @param asset_ Address for the asset\n * @param amount_ Amount of the asset to transfer\n */\n function transferToken(address asset_, uint256 amount_)\n external\n onlyGovernor\n {\n require(asset_ != address(asset()), \"Cannot collect core asset\");\n IERC20(asset_).safeTransfer(governor(), amount_);\n }\n\n /// @inheritdoc ERC4626\n function convertToShares(uint256 assets)\n public\n view\n virtual\n override\n returns (uint256 shares)\n {\n return (assets * rebasingCreditsPerTokenHighres()) / adjuster;\n }\n\n /// @inheritdoc ERC4626\n function convertToAssets(uint256 shares)\n public\n view\n virtual\n override\n returns (uint256 assets)\n {\n return (shares * adjuster) / rebasingCreditsPerTokenHighres();\n }\n\n /// @inheritdoc ERC4626\n function totalAssets() public view override returns (uint256) {\n return (totalSupply() * adjuster) / rebasingCreditsPerTokenHighres();\n }\n\n function rebasingCreditsPerTokenHighres() internal view returns (uint256) {\n return OETH(asset()).rebasingCreditsPerTokenHighres();\n }\n}\n" + }, + "contracts/token/WOETHBase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WOETH } from \"./WOETH.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\n\ncontract WOETHBase is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name() public view virtual override returns (string memory) {\n return \"Wrapped Super OETH\";\n }\n\n function symbol() public view virtual override returns (string memory) {\n return \"wsuperOETHb\";\n }\n}\n" + }, + "contracts/token/WOETHPlume.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WOETH } from \"./WOETH.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @title wOETH (Plume) Token Contract\n * @author Origin Protocol Inc\n */\n\ncontract WOETHPlume is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name() public view virtual override returns (string memory) {\n return \"Wrapped Super OETH\";\n }\n\n function symbol() public view virtual override returns (string memory) {\n return \"wsuperOETHp\";\n }\n}\n" + }, + "contracts/token/WOSonic.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { WOETH } from \"./WOETH.sol\";\n\n/**\n * @title Wrapped Origin Sonic (wOS) token on Sonic\n * @author Origin Protocol Inc\n */\ncontract WOSonic is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"Wrapped OS\";\n }\n\n function symbol()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"wOS\";\n }\n}\n" + }, + "contracts/token/WrappedOusd.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { WOETH } from \"./WOETH.sol\";\n\n/**\n * @title Wrapped OUSD Token Contract\n * @author Origin Protocol Inc\n */\ncontract WrappedOusd is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"Wrapped OUSD\";\n }\n\n function symbol()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"WOUSD\";\n }\n}\n" + }, + "contracts/utils/AerodromeAMOQuoter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.7;\n\nimport { ICLPool } from \"../interfaces/aerodrome/ICLPool.sol\";\nimport { IQuoterV2 } from \"../interfaces/aerodrome/IQuoterV2.sol\";\nimport { IAMOStrategy } from \"../interfaces/aerodrome/IAMOStrategy.sol\";\n\n/// @title QuoterHelper\n/// @author Origin Protocol\n/// @notice Helper for Aerodrome AMO Quoter, as `_quoteAmountToSwapBeforeRebalance` use try/catch method and\n/// this can only be used when calling external contracts.\ncontract QuoterHelper {\n ////////////////////////////////////////////////////////////////\n /// --- STRUCTS & ENUMS\n ////////////////////////////////////////////////////////////////\n enum RevertReasons {\n DefaultStatus,\n RebalanceOutOfBounds,\n NotInExpectedTickRange,\n NotEnoughWethForSwap,\n NotEnoughWethLiquidity,\n UnexpectedError,\n Found,\n NotFound\n }\n\n struct RebalanceStatus {\n RevertReasons reason;\n uint256 currentPoolWETHShare; // Case 1\n uint256 allowedWETHShareStart; // Case 1\n uint256 allowedWETHShareEnd; // Case 1\n int24 currentTick; // Case 2\n uint256 balanceWETH; // Case 3\n uint256 amountWETH; // Case 3\n string revertMessage;\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTANT & IMMUTABLE\n ////////////////////////////////////////////////////////////////\n uint256 public constant BINARY_MIN_AMOUNT = 1 wei;\n\n uint256 public constant BINARY_MAX_ITERATIONS = 40;\n uint256 public constant PERCENTAGE_BASE = 1e18; // 100%\n uint256 public constant ALLOWED_VARIANCE_PERCENTAGE = 1e15; // 0.1%\n\n ////////////////////////////////////////////////////////////////\n /// --- VARIABLES STORAGE\n ////////////////////////////////////////////////////////////////\n ICLPool public immutable clPool;\n IQuoterV2 public immutable quoterV2;\n IAMOStrategy public immutable strategy;\n\n address public originalGovernor;\n\n ////////////////////////////////////////////////////////////////\n /// --- ERRORS & EVENTS\n ////////////////////////////////////////////////////////////////\n error UnexpectedError(string message);\n error OutOfIterations(uint256 iterations);\n error ValidAmount(\n uint256 amount,\n uint256 iterations,\n bool swapWETHForOETHB\n );\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////////////////\n constructor(IAMOStrategy _strategy, IQuoterV2 _quoterV2) {\n strategy = _strategy;\n quoterV2 = _quoterV2;\n clPool = _strategy.clPool();\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- FUNCTIONS\n ////////////////////////////////////////////////////////////////\n /// @notice This call can only end with a revert.\n function getAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) public {\n if (\n overrideBottomWethShare != type(uint256).max ||\n overrideTopWethShare != type(uint256).max\n ) {\n // Current values\n uint256 shareStart = strategy.allowedWethShareStart();\n uint256 shareEnd = strategy.allowedWethShareEnd();\n\n // Override values\n if (overrideBottomWethShare != type(uint256).max) {\n shareStart = overrideBottomWethShare;\n }\n if (overrideTopWethShare != type(uint256).max) {\n shareEnd = overrideTopWethShare;\n }\n\n strategy.setAllowedPoolWethShareInterval(shareStart, shareEnd);\n }\n\n uint256 iterations = 0;\n uint256 low = BINARY_MIN_AMOUNT;\n uint256 high;\n (high, ) = strategy.getPositionPrincipal();\n int24 lowerTick = strategy.lowerTick();\n int24 upperTick = strategy.upperTick();\n bool swapWETHForOETHB = getSwapDirectionForRebalance();\n\n while (low <= high && iterations < BINARY_MAX_ITERATIONS) {\n uint256 mid = (low + high) / 2;\n\n RebalanceStatus memory status = getRebalanceStatus(\n mid,\n swapWETHForOETHB\n );\n\n // Best case, we found the `amount` that will reach the target pool share!\n // We can revert with the amount and the number of iterations\n if (status.reason == RevertReasons.Found) {\n revert ValidAmount(mid, iterations, swapWETHForOETHB);\n }\n\n // If the rebalance failed then we should try to change the amount.\n // We will handle all possible revert reasons here.\n\n // Case 1: Rebalance out of bounds\n // If the pool is out of bounds, we need to adjust the amount to reach the target pool share\n if (status.reason == RevertReasons.RebalanceOutOfBounds) {\n // If the current pool share is less than the target pool share, we need to increase the amount\n if (\n swapWETHForOETHB\n ? status.currentPoolWETHShare <\n status.allowedWETHShareStart\n : status.currentPoolWETHShare >\n status.allowedWETHShareEnd\n ) {\n low = mid + 1;\n }\n // Else we need to decrease the amount\n else {\n high = mid;\n }\n }\n\n // Case 2: Not in expected tick range\n // If the pool is not in the expected tick range, we need to adjust the amount\n // to reach the target pool share\n if (status.reason == RevertReasons.NotInExpectedTickRange) {\n // If we are buying OETHb and the current tick is greater than the lower tick,\n //we need to increase the amount in order to continue to push price down.\n // If we are selling OETHb and the current tick is less than the upper tick,\n // we need to increase the amount in order to continue to push price up.\n if (\n swapWETHForOETHB\n ? status.currentTick > lowerTick\n : status.currentTick < upperTick\n ) {\n low = mid + 1;\n }\n // Else we need to decrease the amount\n else {\n high = mid;\n }\n }\n\n // Case 3: Not enough WETH for swap\n // If we don't have enough WETH to swap, we need to decrease the amount\n // This error can happen, when initial value of mid is too high, so we need to decrease it\n if (status.reason == RevertReasons.NotEnoughWethForSwap) {\n high = mid;\n }\n\n // Case 4: Not enough WETH liquidity\n // If we don't have enough WETH liquidity\n // Revert for the moment, we need to improve this\n if (status.reason == RevertReasons.NotEnoughWethLiquidity) {\n revert(\"Quoter: Not enough WETH liquidity\");\n }\n\n // Case 5: Unexpected error\n // Worst case, it reverted with an unexpected error.\n if (status.reason == RevertReasons.UnexpectedError) {\n revert UnexpectedError(status.revertMessage);\n }\n\n iterations++;\n }\n\n // Case 6: Out of iterations\n // If we didn't find the amount after the max iterations, we need to revert.\n revert OutOfIterations(iterations);\n }\n\n /// @notice Get the status of the rebalance\n /// @param amount The amount of token to swap\n /// @param swapWETH True if we need to swap WETH for OETHb, false otherwise\n /// @return status The status of the rebalance\n function getRebalanceStatus(uint256 amount, bool swapWETH)\n public\n returns (RebalanceStatus memory status)\n {\n try strategy.rebalance(amount, swapWETH, 0) {\n status.reason = RevertReasons.Found;\n return status;\n } catch Error(string memory reason) {\n status.reason = RevertReasons.UnexpectedError;\n status.revertMessage = reason;\n } catch (bytes memory reason) {\n bytes4 receivedSelector = bytes4(reason);\n\n // Case 1: Rebalance out of bounds\n if (\n receivedSelector ==\n IAMOStrategy.PoolRebalanceOutOfBounds.selector\n ) {\n uint256 currentPoolWETHShare;\n uint256 allowedWETHShareStart;\n uint256 allowedWETHShareEnd;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n currentPoolWETHShare := mload(add(reason, 0x24))\n allowedWETHShareStart := mload(add(reason, 0x44))\n allowedWETHShareEnd := mload(add(reason, 0x64))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.RebalanceOutOfBounds,\n currentPoolWETHShare: currentPoolWETHShare,\n allowedWETHShareStart: allowedWETHShareStart,\n allowedWETHShareEnd: allowedWETHShareEnd,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 2: Not in expected tick range\n if (\n receivedSelector ==\n IAMOStrategy.OutsideExpectedTickRange.selector\n ) {\n int24 currentTick;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n currentTick := mload(add(reason, 0x24))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.NotInExpectedTickRange,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: currentTick,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 3: Not enough WETH for swap\n if (\n receivedSelector == IAMOStrategy.NotEnoughWethForSwap.selector\n ) {\n uint256 balanceWETH;\n uint256 amountWETH;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n balanceWETH := mload(add(reason, 0x24))\n amountWETH := mload(add(reason, 0x44))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.NotEnoughWethForSwap,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: balanceWETH,\n amountWETH: amountWETH,\n revertMessage: \"\"\n });\n }\n\n // Case 4: Not enough WETH liquidity\n if (\n receivedSelector == IAMOStrategy.NotEnoughWethLiquidity.selector\n ) {\n return\n RebalanceStatus({\n reason: RevertReasons.NotEnoughWethLiquidity,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 5: Unexpected error\n return\n RebalanceStatus({\n reason: RevertReasons.UnexpectedError,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: abi.decode(reason, (string))\n });\n }\n }\n\n /// @notice Get the swap direction to reach the target price before rebalance.\n /// @return bool True if we need to swap WETH for OETHb, false otherwise.\n function getSwapDirectionForRebalance() public view returns (bool) {\n uint160 currentPrice = strategy.getPoolX96Price();\n uint160 ticker0Price = strategy.sqrtRatioX96TickLower();\n uint160 ticker1Price = strategy.sqrtRatioX96TickHigher();\n uint256 allowedWethShareStart = strategy.allowedWethShareStart();\n uint256 allowedWethShareEnd = strategy.allowedWethShareEnd();\n uint160 mid = uint160(allowedWethShareStart + allowedWethShareEnd) / 2;\n // slither-disable-start divide-before-multiply\n uint160 targetPrice = (ticker0Price *\n mid +\n ticker1Price *\n (1 ether - mid)) / 1 ether;\n // slither-disable-end divide-before-multiply\n\n return currentPrice > targetPrice;\n }\n\n // returns total amount in the position principal of the Aerodrome AMO strategy. Needed as a\n // separate function because of the limitation in local variable count in getAmountToSwapToReachPrice\n function getTotalStrategyPosition() internal returns (uint256) {\n (uint256 wethAmount, uint256 oethBalance) = strategy\n .getPositionPrincipal();\n return wethAmount + oethBalance;\n }\n\n /// @notice Get the amount of tokens to swap to reach the target price.\n /// @dev This act like a quoter, i.e. the transaction is not performed.\n /// @dev Because the amount to swap can be largely overestimated, because CLAMM alow partial orders,\n /// i.e. when we ask to swap a very large amount, with a close priceLimite, it will swap only a part of it,\n /// and not revert. So if overestimated amount is to high, use a custom maxAmount to avoid this issue.\n /// @param sqrtPriceTargetX96 The target price to reach.\n /// @return amount The amount of tokens to swap.\n /// @return iterations The number of iterations to find the amount.\n /// @return swapWETHForOETHB True if we need to swap WETH for OETHb, false otherwise.\n /// @return sqrtPriceX96After The price after the swap.\n function getAmountToSwapToReachPrice(uint160 sqrtPriceTargetX96)\n public\n returns (\n uint256,\n uint256,\n bool,\n uint160\n )\n {\n uint256 iterations = 0;\n uint256 low = BINARY_MIN_AMOUNT;\n // high search start is twice the position principle of Aerodrome AMO strategy.\n // should be more than enough\n uint256 high = getTotalStrategyPosition() * 2;\n bool swapWETHForOETHB = getSwapDirection(sqrtPriceTargetX96);\n\n while (low <= high && iterations < BINARY_MAX_ITERATIONS) {\n uint256 mid = (low + high) / 2;\n\n // Call QuoterV2 from SugarHelper\n (uint256 amountOut, uint160 sqrtPriceX96After, , ) = quoterV2\n .quoteExactInputSingle(\n IQuoterV2.QuoteExactInputSingleParams({\n tokenIn: swapWETHForOETHB\n ? clPool.token0()\n : clPool.token1(),\n tokenOut: swapWETHForOETHB\n ? clPool.token1()\n : clPool.token0(),\n amountIn: mid,\n tickSpacing: strategy.tickSpacing(),\n sqrtPriceLimitX96: sqrtPriceTargetX96\n })\n );\n\n if (\n isWithinAllowedVariance(sqrtPriceX96After, sqrtPriceTargetX96)\n ) {\n /** Very important to return `amountOut` instead of `mid` as the first return parameter.\n * The issues was that when quoting we impose a swap price limit (sqrtPriceLimitX96: sqrtPriceTargetX96)\n * and in that case the `amountIn` acts like a maximum amount to swap. And we don't know how much\n * of that amount was actually consumed. For that reason we \"estimate\" it by returning the\n * amountOut since that is only going to be a couple of basis point away from amountIn in the\n * worst cases.\n *\n * Note: we could be returning mid instead of amountOut in cases when those values are only basis\n * points apart (assuming that complete balance of amountIn has been consumed) but that might increase\n * complexity too much in an already complex contract.\n */\n return (\n amountOut,\n iterations,\n swapWETHForOETHB,\n sqrtPriceX96After\n );\n } else if (low == high) {\n // target swap amount not found.\n // might be that \"high\" amount is too low on start\n revert(\"SwapAmountNotFound\");\n } else if (\n swapWETHForOETHB\n ? sqrtPriceX96After > sqrtPriceTargetX96\n : sqrtPriceX96After < sqrtPriceTargetX96\n ) {\n low = mid + 1;\n } else {\n high = mid;\n }\n iterations++;\n }\n\n revert OutOfIterations(iterations);\n }\n\n /// @notice Check if the current price is within the allowed variance in comparison to the target price\n /// @return bool True if the current price is within the allowed variance, false otherwise\n function isWithinAllowedVariance(\n uint160 sqrtPriceCurrentX96,\n uint160 sqrtPriceTargetX96\n ) public view returns (bool) {\n uint160 range = strategy.sqrtRatioX96TickHigher() -\n strategy.sqrtRatioX96TickLower();\n if (sqrtPriceCurrentX96 > sqrtPriceTargetX96) {\n return\n (sqrtPriceCurrentX96 - sqrtPriceTargetX96) <=\n (ALLOWED_VARIANCE_PERCENTAGE * range) / PERCENTAGE_BASE;\n } else {\n return\n (sqrtPriceTargetX96 - sqrtPriceCurrentX96) <=\n (ALLOWED_VARIANCE_PERCENTAGE * range) / PERCENTAGE_BASE;\n }\n }\n\n /// @notice Get the swap direction to reach the target price.\n /// @param sqrtPriceTargetX96 The target price to reach.\n /// @return bool True if we need to swap WETH for OETHb, false otherwise.\n function getSwapDirection(uint160 sqrtPriceTargetX96)\n public\n view\n returns (bool)\n {\n uint160 currentPrice = strategy.getPoolX96Price();\n return currentPrice > sqrtPriceTargetX96;\n }\n\n function claimGovernanceOnAMO() public {\n originalGovernor = strategy.governor();\n strategy.claimGovernance();\n }\n\n function giveBackGovernanceOnAMO() public {\n require(\n originalGovernor != address(0),\n \"Quoter: Original governor not set\"\n );\n strategy.transferGovernance(originalGovernor);\n }\n}\n\n/// @title AerodromeAMOQuoter\n/// @author Origin Protocol\n/// @notice Quoter for Aerodrome AMO\ncontract AerodromeAMOQuoter {\n ////////////////////////////////////////////////////////////////\n /// --- STRUCTS & ENUMS\n ///////////////////////////////////////////////////////////////\n struct Data {\n uint256 amount;\n uint256 iterations;\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- VARIABLES STORAGE\n ////////////////////////////////////////////////////////////////\n QuoterHelper public immutable quoterHelper;\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////////////////\n constructor(address _strategy, address _quoterV2) {\n quoterHelper = new QuoterHelper(\n IAMOStrategy(_strategy),\n IQuoterV2(_quoterV2)\n );\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- ERRORS & EVENTS\n ////////////////////////////////////////////////////////////////\n event ValueFound(uint256 value, uint256 iterations, bool swapWETHForOETHB);\n event ValueFoundBis(\n uint256 value,\n uint256 iterations,\n bool swapWETHForOETHB,\n uint160 sqrtPriceAfterX96\n );\n event ValueNotFound(string message);\n\n ////////////////////////////////////////////////////////////////\n /// --- FUNCTIONS\n ////////////////////////////////////////////////////////////////\n /// @notice Use this to get the amount to swap before rebalance\n /// @dev This call will only revert, check the logs to get returned values.\n /// @dev Need to perform this call while impersonating the governor or strategist of AMO.\n /// @return data Data struct with the amount and the number of iterations\n function quoteAmountToSwapBeforeRebalance()\n public\n returns (Data memory data)\n {\n return\n _quoteAmountToSwapBeforeRebalance(\n type(uint256).max,\n type(uint256).max\n );\n }\n\n /// @notice Use this to get the amount to swap before rebalance and\n /// update allowedWethShareStart and allowedWethShareEnd on AMO.\n /// @dev This call will only revert, check the logs to get returned values.\n /// @dev Need to perform this call while impersonating the governor of AMO.\n /// @param overrideBottomWethShare New value for the allowedWethShareStart on AMO.\n /// Use type(uint256).max to keep same value.\n /// @param overrideTopWethShare New value for the allowedWethShareEnd on AMO.\n /// Use type(uint256).max to keep same value.\n /// @return data Data struct with the amount and the number of iterations\n function quoteAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) public returns (Data memory data) {\n return\n _quoteAmountToSwapBeforeRebalance(\n overrideBottomWethShare,\n overrideTopWethShare\n );\n }\n\n /// @notice Internal logic for quoteAmountToSwapBeforeRebalance.\n function _quoteAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) internal returns (Data memory data) {\n try\n quoterHelper.getAmountToSwapBeforeRebalance(\n overrideBottomWethShare,\n overrideTopWethShare\n )\n {\n revert(\"Previous call should only revert, it cannot succeed\");\n } catch (bytes memory reason) {\n bytes4 receivedSelector = bytes4(reason);\n if (receivedSelector == QuoterHelper.ValidAmount.selector) {\n uint256 value;\n uint256 iterations;\n bool swapWETHForOETHB;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n value := mload(add(reason, 0x24))\n iterations := mload(add(reason, 0x44))\n swapWETHForOETHB := mload(add(reason, 0x64))\n }\n emit ValueFound(value, iterations, swapWETHForOETHB);\n return Data({ amount: value, iterations: iterations });\n }\n\n if (receivedSelector == QuoterHelper.OutOfIterations.selector) {\n emit ValueNotFound(\"Out of iterations\");\n revert(\"Out of iterations\");\n }\n\n emit ValueNotFound(\"Unexpected error\");\n revert(abi.decode(reason, (string)));\n }\n }\n\n /// @notice Use this to get the amount to swap to reach the target price after swap.\n /// @dev This call will only revert, check the logs to get returned values.\n /// @param sqrtPriceTargetX96 The target price to reach.\n function quoteAmountToSwapToReachPrice(uint160 sqrtPriceTargetX96) public {\n (\n uint256 amount,\n uint256 iterations,\n bool swapWETHForOETHB,\n uint160 sqrtPriceAfterX96\n ) = quoterHelper.getAmountToSwapToReachPrice(sqrtPriceTargetX96);\n\n emit ValueFoundBis(\n amount,\n iterations,\n swapWETHForOETHB,\n sqrtPriceAfterX96\n );\n }\n\n function claimGovernance() public {\n quoterHelper.claimGovernanceOnAMO();\n }\n\n function giveBackGovernance() public {\n quoterHelper.giveBackGovernanceOnAMO();\n }\n}\n" + }, + "contracts/utils/BalancerErrors.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity >=0.7.4 <0.9.0;\n\n// solhint-disable\n\n/**\n * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are\n * supported.\n * Uses the default 'BAL' prefix for the error code\n */\nfunction _require(bool condition, uint256 errorCode) pure {\n if (!condition) _revert(errorCode);\n}\n\n/**\n * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are\n * supported.\n */\nfunction _require(\n bool condition,\n uint256 errorCode,\n bytes3 prefix\n) pure {\n if (!condition) _revert(errorCode, prefix);\n}\n\n/**\n * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported.\n * Uses the default 'BAL' prefix for the error code\n */\nfunction _revert(uint256 errorCode) pure {\n _revert(errorCode, 0x42414c); // This is the raw byte representation of \"BAL\"\n}\n\n/**\n * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported.\n */\nfunction _revert(uint256 errorCode, bytes3 prefix) pure {\n uint256 prefixUint = uint256(uint24(prefix));\n // We're going to dynamically create a revert string based on the error code, with the following format:\n // 'BAL#{errorCode}'\n // where the code is left-padded with zeroes to three digits (so they range from 000 to 999).\n //\n // We don't have revert strings embedded in the contract to save bytecode size: it takes much less space to store a\n // number (8 to 16 bits) than the individual string characters.\n //\n // The dynamic string creation algorithm that follows could be implemented in Solidity, but assembly allows for a\n // much denser implementation, again saving bytecode size. Given this function unconditionally reverts, this is a\n // safe place to rely on it without worrying about how its usage might affect e.g. memory contents.\n assembly {\n // First, we need to compute the ASCII representation of the error code. We assume that it is in the 0-999\n // range, so we only need to convert three digits. To convert the digits to ASCII, we add 0x30, the value for\n // the '0' character.\n\n let units := add(mod(errorCode, 10), 0x30)\n\n errorCode := div(errorCode, 10)\n let tenths := add(mod(errorCode, 10), 0x30)\n\n errorCode := div(errorCode, 10)\n let hundreds := add(mod(errorCode, 10), 0x30)\n\n // With the individual characters, we can now construct the full string.\n // We first append the '#' character (0x23) to the prefix. In the case of 'BAL', it results in 0x42414c23 ('BAL#')\n // Then, we shift this by 24 (to provide space for the 3 bytes of the error code), and add the\n // characters to it, each shifted by a multiple of 8.\n // The revert reason is then shifted left by 200 bits (256 minus the length of the string, 7 characters * 8 bits\n // per character = 56) to locate it in the most significant part of the 256 slot (the beginning of a byte\n // array).\n let formattedPrefix := shl(24, add(0x23, shl(8, prefixUint)))\n\n let revertReason := shl(\n 200,\n add(\n formattedPrefix,\n add(add(units, shl(8, tenths)), shl(16, hundreds))\n )\n )\n\n // We can now encode the reason in memory, which can be safely overwritten as we're about to revert. The encoded\n // message will have the following layout:\n // [ revert reason identifier ] [ string location offset ] [ string length ] [ string contents ]\n\n // The Solidity revert reason identifier is 0x08c739a0, the function selector of the Error(string) function. We\n // also write zeroes to the next 28 bytes of memory, but those are about to be overwritten.\n mstore(\n 0x0,\n 0x08c379a000000000000000000000000000000000000000000000000000000000\n )\n // Next is the offset to the location of the string, which will be placed immediately after (20 bytes away).\n mstore(\n 0x04,\n 0x0000000000000000000000000000000000000000000000000000000000000020\n )\n // The string length is fixed: 7 characters.\n mstore(0x24, 7)\n // Finally, the string itself is stored.\n mstore(0x44, revertReason)\n\n // Even if the string is only 7 bytes long, we need to return a full 32 byte slot containing it. The length of\n // the encoded message is therefore 4 + 32 + 32 + 32 = 100.\n revert(0, 100)\n }\n}\n\nlibrary Errors {\n // Math\n uint256 internal constant ADD_OVERFLOW = 0;\n uint256 internal constant SUB_OVERFLOW = 1;\n uint256 internal constant SUB_UNDERFLOW = 2;\n uint256 internal constant MUL_OVERFLOW = 3;\n uint256 internal constant ZERO_DIVISION = 4;\n uint256 internal constant DIV_INTERNAL = 5;\n uint256 internal constant X_OUT_OF_BOUNDS = 6;\n uint256 internal constant Y_OUT_OF_BOUNDS = 7;\n uint256 internal constant PRODUCT_OUT_OF_BOUNDS = 8;\n uint256 internal constant INVALID_EXPONENT = 9;\n\n // Input\n uint256 internal constant OUT_OF_BOUNDS = 100;\n uint256 internal constant UNSORTED_ARRAY = 101;\n uint256 internal constant UNSORTED_TOKENS = 102;\n uint256 internal constant INPUT_LENGTH_MISMATCH = 103;\n uint256 internal constant ZERO_TOKEN = 104;\n uint256 internal constant INSUFFICIENT_DATA = 105;\n\n // Shared pools\n uint256 internal constant MIN_TOKENS = 200;\n uint256 internal constant MAX_TOKENS = 201;\n uint256 internal constant MAX_SWAP_FEE_PERCENTAGE = 202;\n uint256 internal constant MIN_SWAP_FEE_PERCENTAGE = 203;\n uint256 internal constant MINIMUM_BPT = 204;\n uint256 internal constant CALLER_NOT_VAULT = 205;\n uint256 internal constant UNINITIALIZED = 206;\n uint256 internal constant BPT_IN_MAX_AMOUNT = 207;\n uint256 internal constant BPT_OUT_MIN_AMOUNT = 208;\n uint256 internal constant EXPIRED_PERMIT = 209;\n uint256 internal constant NOT_TWO_TOKENS = 210;\n uint256 internal constant DISABLED = 211;\n\n // Pools\n uint256 internal constant MIN_AMP = 300;\n uint256 internal constant MAX_AMP = 301;\n uint256 internal constant MIN_WEIGHT = 302;\n uint256 internal constant MAX_STABLE_TOKENS = 303;\n uint256 internal constant MAX_IN_RATIO = 304;\n uint256 internal constant MAX_OUT_RATIO = 305;\n uint256 internal constant MIN_BPT_IN_FOR_TOKEN_OUT = 306;\n uint256 internal constant MAX_OUT_BPT_FOR_TOKEN_IN = 307;\n uint256 internal constant NORMALIZED_WEIGHT_INVARIANT = 308;\n uint256 internal constant INVALID_TOKEN = 309;\n uint256 internal constant UNHANDLED_JOIN_KIND = 310;\n uint256 internal constant ZERO_INVARIANT = 311;\n uint256 internal constant ORACLE_INVALID_SECONDS_QUERY = 312;\n uint256 internal constant ORACLE_NOT_INITIALIZED = 313;\n uint256 internal constant ORACLE_QUERY_TOO_OLD = 314;\n uint256 internal constant ORACLE_INVALID_INDEX = 315;\n uint256 internal constant ORACLE_BAD_SECS = 316;\n uint256 internal constant AMP_END_TIME_TOO_CLOSE = 317;\n uint256 internal constant AMP_ONGOING_UPDATE = 318;\n uint256 internal constant AMP_RATE_TOO_HIGH = 319;\n uint256 internal constant AMP_NO_ONGOING_UPDATE = 320;\n uint256 internal constant STABLE_INVARIANT_DIDNT_CONVERGE = 321;\n uint256 internal constant STABLE_GET_BALANCE_DIDNT_CONVERGE = 322;\n uint256 internal constant RELAYER_NOT_CONTRACT = 323;\n uint256 internal constant BASE_POOL_RELAYER_NOT_CALLED = 324;\n uint256 internal constant REBALANCING_RELAYER_REENTERED = 325;\n uint256 internal constant GRADUAL_UPDATE_TIME_TRAVEL = 326;\n uint256 internal constant SWAPS_DISABLED = 327;\n uint256 internal constant CALLER_IS_NOT_LBP_OWNER = 328;\n uint256 internal constant PRICE_RATE_OVERFLOW = 329;\n uint256 internal constant INVALID_JOIN_EXIT_KIND_WHILE_SWAPS_DISABLED = 330;\n uint256 internal constant WEIGHT_CHANGE_TOO_FAST = 331;\n uint256 internal constant LOWER_GREATER_THAN_UPPER_TARGET = 332;\n uint256 internal constant UPPER_TARGET_TOO_HIGH = 333;\n uint256 internal constant UNHANDLED_BY_LINEAR_POOL = 334;\n uint256 internal constant OUT_OF_TARGET_RANGE = 335;\n uint256 internal constant UNHANDLED_EXIT_KIND = 336;\n uint256 internal constant UNAUTHORIZED_EXIT = 337;\n uint256 internal constant MAX_MANAGEMENT_SWAP_FEE_PERCENTAGE = 338;\n uint256 internal constant UNHANDLED_BY_MANAGED_POOL = 339;\n uint256 internal constant UNHANDLED_BY_PHANTOM_POOL = 340;\n uint256 internal constant TOKEN_DOES_NOT_HAVE_RATE_PROVIDER = 341;\n uint256 internal constant INVALID_INITIALIZATION = 342;\n uint256 internal constant OUT_OF_NEW_TARGET_RANGE = 343;\n uint256 internal constant FEATURE_DISABLED = 344;\n uint256 internal constant UNINITIALIZED_POOL_CONTROLLER = 345;\n uint256 internal constant SET_SWAP_FEE_DURING_FEE_CHANGE = 346;\n uint256 internal constant SET_SWAP_FEE_PENDING_FEE_CHANGE = 347;\n uint256 internal constant CHANGE_TOKENS_DURING_WEIGHT_CHANGE = 348;\n uint256 internal constant CHANGE_TOKENS_PENDING_WEIGHT_CHANGE = 349;\n uint256 internal constant MAX_WEIGHT = 350;\n uint256 internal constant UNAUTHORIZED_JOIN = 351;\n uint256 internal constant MAX_MANAGEMENT_AUM_FEE_PERCENTAGE = 352;\n uint256 internal constant FRACTIONAL_TARGET = 353;\n uint256 internal constant ADD_OR_REMOVE_BPT = 354;\n uint256 internal constant INVALID_CIRCUIT_BREAKER_BOUNDS = 355;\n uint256 internal constant CIRCUIT_BREAKER_TRIPPED = 356;\n uint256 internal constant MALICIOUS_QUERY_REVERT = 357;\n uint256 internal constant JOINS_EXITS_DISABLED = 358;\n\n // Lib\n uint256 internal constant REENTRANCY = 400;\n uint256 internal constant SENDER_NOT_ALLOWED = 401;\n uint256 internal constant PAUSED = 402;\n uint256 internal constant PAUSE_WINDOW_EXPIRED = 403;\n uint256 internal constant MAX_PAUSE_WINDOW_DURATION = 404;\n uint256 internal constant MAX_BUFFER_PERIOD_DURATION = 405;\n uint256 internal constant INSUFFICIENT_BALANCE = 406;\n uint256 internal constant INSUFFICIENT_ALLOWANCE = 407;\n uint256 internal constant ERC20_TRANSFER_FROM_ZERO_ADDRESS = 408;\n uint256 internal constant ERC20_TRANSFER_TO_ZERO_ADDRESS = 409;\n uint256 internal constant ERC20_MINT_TO_ZERO_ADDRESS = 410;\n uint256 internal constant ERC20_BURN_FROM_ZERO_ADDRESS = 411;\n uint256 internal constant ERC20_APPROVE_FROM_ZERO_ADDRESS = 412;\n uint256 internal constant ERC20_APPROVE_TO_ZERO_ADDRESS = 413;\n uint256 internal constant ERC20_TRANSFER_EXCEEDS_ALLOWANCE = 414;\n uint256 internal constant ERC20_DECREASED_ALLOWANCE_BELOW_ZERO = 415;\n uint256 internal constant ERC20_TRANSFER_EXCEEDS_BALANCE = 416;\n uint256 internal constant ERC20_BURN_EXCEEDS_ALLOWANCE = 417;\n uint256 internal constant SAFE_ERC20_CALL_FAILED = 418;\n uint256 internal constant ADDRESS_INSUFFICIENT_BALANCE = 419;\n uint256 internal constant ADDRESS_CANNOT_SEND_VALUE = 420;\n uint256 internal constant SAFE_CAST_VALUE_CANT_FIT_INT256 = 421;\n uint256 internal constant GRANT_SENDER_NOT_ADMIN = 422;\n uint256 internal constant REVOKE_SENDER_NOT_ADMIN = 423;\n uint256 internal constant RENOUNCE_SENDER_NOT_ALLOWED = 424;\n uint256 internal constant BUFFER_PERIOD_EXPIRED = 425;\n uint256 internal constant CALLER_IS_NOT_OWNER = 426;\n uint256 internal constant NEW_OWNER_IS_ZERO = 427;\n uint256 internal constant CODE_DEPLOYMENT_FAILED = 428;\n uint256 internal constant CALL_TO_NON_CONTRACT = 429;\n uint256 internal constant LOW_LEVEL_CALL_FAILED = 430;\n uint256 internal constant NOT_PAUSED = 431;\n uint256 internal constant ADDRESS_ALREADY_ALLOWLISTED = 432;\n uint256 internal constant ADDRESS_NOT_ALLOWLISTED = 433;\n uint256 internal constant ERC20_BURN_EXCEEDS_BALANCE = 434;\n uint256 internal constant INVALID_OPERATION = 435;\n uint256 internal constant CODEC_OVERFLOW = 436;\n uint256 internal constant IN_RECOVERY_MODE = 437;\n uint256 internal constant NOT_IN_RECOVERY_MODE = 438;\n uint256 internal constant INDUCED_FAILURE = 439;\n uint256 internal constant EXPIRED_SIGNATURE = 440;\n uint256 internal constant MALFORMED_SIGNATURE = 441;\n uint256 internal constant SAFE_CAST_VALUE_CANT_FIT_UINT64 = 442;\n uint256 internal constant UNHANDLED_FEE_TYPE = 443;\n uint256 internal constant BURN_FROM_ZERO = 444;\n\n // Vault\n uint256 internal constant INVALID_POOL_ID = 500;\n uint256 internal constant CALLER_NOT_POOL = 501;\n uint256 internal constant SENDER_NOT_ASSET_MANAGER = 502;\n uint256 internal constant USER_DOESNT_ALLOW_RELAYER = 503;\n uint256 internal constant INVALID_SIGNATURE = 504;\n uint256 internal constant EXIT_BELOW_MIN = 505;\n uint256 internal constant JOIN_ABOVE_MAX = 506;\n uint256 internal constant SWAP_LIMIT = 507;\n uint256 internal constant SWAP_DEADLINE = 508;\n uint256 internal constant CANNOT_SWAP_SAME_TOKEN = 509;\n uint256 internal constant UNKNOWN_AMOUNT_IN_FIRST_SWAP = 510;\n uint256 internal constant MALCONSTRUCTED_MULTIHOP_SWAP = 511;\n uint256 internal constant INTERNAL_BALANCE_OVERFLOW = 512;\n uint256 internal constant INSUFFICIENT_INTERNAL_BALANCE = 513;\n uint256 internal constant INVALID_ETH_INTERNAL_BALANCE = 514;\n uint256 internal constant INVALID_POST_LOAN_BALANCE = 515;\n uint256 internal constant INSUFFICIENT_ETH = 516;\n uint256 internal constant UNALLOCATED_ETH = 517;\n uint256 internal constant ETH_TRANSFER = 518;\n uint256 internal constant CANNOT_USE_ETH_SENTINEL = 519;\n uint256 internal constant TOKENS_MISMATCH = 520;\n uint256 internal constant TOKEN_NOT_REGISTERED = 521;\n uint256 internal constant TOKEN_ALREADY_REGISTERED = 522;\n uint256 internal constant TOKENS_ALREADY_SET = 523;\n uint256 internal constant TOKENS_LENGTH_MUST_BE_2 = 524;\n uint256 internal constant NONZERO_TOKEN_BALANCE = 525;\n uint256 internal constant BALANCE_TOTAL_OVERFLOW = 526;\n uint256 internal constant POOL_NO_TOKENS = 527;\n uint256 internal constant INSUFFICIENT_FLASH_LOAN_BALANCE = 528;\n\n // Fees\n uint256 internal constant SWAP_FEE_PERCENTAGE_TOO_HIGH = 600;\n uint256 internal constant FLASH_LOAN_FEE_PERCENTAGE_TOO_HIGH = 601;\n uint256 internal constant INSUFFICIENT_FLASH_LOAN_FEE_AMOUNT = 602;\n uint256 internal constant AUM_FEE_PERCENTAGE_TOO_HIGH = 603;\n\n // FeeSplitter\n uint256 internal constant SPLITTER_FEE_PERCENTAGE_TOO_HIGH = 700;\n\n // Misc\n uint256 internal constant UNIMPLEMENTED = 998;\n uint256 internal constant SHOULD_NOT_HAPPEN = 999;\n}\n" + }, + "contracts/utils/DepositContractUtils.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract DepositContractUtils {\n function calculateDepositDataRoot(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature\n ) public pure returns (bytes32 node) {\n uint256 deposit_amount = 32 ether / 1 gwei;\n bytes memory amount = to_little_endian_64(uint64(deposit_amount));\n\n // Compute deposit data root (`DepositData` hash tree root)\n bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, bytes16(0)));\n bytes32 signature_root = sha256(\n abi.encodePacked(\n sha256(abi.encodePacked(signature[:64])),\n sha256(abi.encodePacked(signature[64:], bytes32(0)))\n )\n );\n node = sha256(\n abi.encodePacked(\n sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)),\n sha256(abi.encodePacked(amount, bytes24(0), signature_root))\n )\n );\n }\n\n function to_little_endian_64(uint64 value)\n internal\n pure\n returns (bytes memory ret)\n {\n ret = new bytes(8);\n bytes8 bytesValue = bytes8(value);\n // Byteswapping during copying to bytes.\n ret[0] = bytesValue[7];\n ret[1] = bytesValue[6];\n ret[2] = bytesValue[5];\n ret[3] = bytesValue[4];\n ret[4] = bytesValue[3];\n ret[5] = bytesValue[2];\n ret[6] = bytesValue[1];\n ret[7] = bytesValue[0];\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/utils/InitializableERC20Detailed.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @dev Optional functions from the ERC20 standard.\n * Converted from openzeppelin/contracts/token/ERC20/ERC20Detailed.sol\n * @author Origin Protocol Inc\n */\nabstract contract InitializableERC20Detailed is IERC20 {\n // Storage gap to skip storage from prior to OUSD reset\n uint256[100] private _____gap;\n\n string private _name;\n string private _symbol;\n uint8 private _decimals;\n\n /**\n * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of\n * these values are immutable: they can only be set once during\n * construction.\n * @notice To avoid variable shadowing appended `Arg` after arguments name.\n */\n function _initialize(\n string memory nameArg,\n string memory symbolArg,\n uint8 decimalsArg\n ) internal {\n _name = nameArg;\n _symbol = symbolArg;\n _decimals = decimalsArg;\n }\n\n /**\n * @notice Returns the name of the token.\n */\n function name() public view returns (string memory) {\n return _name;\n }\n\n /**\n * @notice Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view returns (string memory) {\n return _symbol;\n }\n\n /**\n * @notice Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5,05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei.\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view returns (uint8) {\n return _decimals;\n }\n}\n" + }, + "contracts/utils/PRBMath.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// Copied from the PRBMath library\n// https://github.com/PaulRBerg/prb-math/blob/main/src/Common.sol\n\n/// @notice Calculates the square root of x using the Babylonian method.\n///\n/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.\n///\n/// Notes:\n/// - If x is not a perfect square, the result is rounded down.\n/// - Credits to OpenZeppelin for the explanations in comments below.\n///\n/// @param x The uint256 number for which to calculate the square root.\n/// @return result The result as a uint256.\n/// @custom:smtchecker abstract-function-nondet\nfunction sqrt(uint256 x) pure returns (uint256 result) {\n if (x == 0) {\n return 0;\n }\n\n // For our first guess, we calculate the biggest power of 2 which is smaller than the square root of x.\n //\n // We know that the \"msb\" (most significant bit) of x is a power of 2 such that we have:\n //\n // $$\n // msb(x) <= x <= 2*msb(x)$\n // $$\n //\n // We write $msb(x)$ as $2^k$, and we get:\n //\n // $$\n // k = log_2(x)\n // $$\n //\n // Thus, we can write the initial inequality as:\n //\n // $$\n // 2^{log_2(x)} <= x <= 2*2^{log_2(x)+1} \\\\\n // sqrt(2^k) <= sqrt(x) < sqrt(2^{k+1}) \\\\\n // 2^{k/2} <= sqrt(x) < 2^{(k+1)/2} <= 2^{(k/2)+1}\n // $$\n //\n // Consequently, $2^{log_2(x) /2} is a good first approximation of sqrt(x) with at least one correct bit.\n uint256 xAux = uint256(x);\n result = 1;\n if (xAux >= 2**128) {\n xAux >>= 128;\n result <<= 64;\n }\n if (xAux >= 2**64) {\n xAux >>= 64;\n result <<= 32;\n }\n if (xAux >= 2**32) {\n xAux >>= 32;\n result <<= 16;\n }\n if (xAux >= 2**16) {\n xAux >>= 16;\n result <<= 8;\n }\n if (xAux >= 2**8) {\n xAux >>= 8;\n result <<= 4;\n }\n if (xAux >= 2**4) {\n xAux >>= 4;\n result <<= 2;\n }\n if (xAux >= 2**2) {\n result <<= 1;\n }\n\n // At this point, `result` is an estimation with at least one bit of precision. We know the true value has at\n // most 128 bits, since it is the square root of a uint256. Newton's method converges quadratically (precision\n // doubles at every iteration). We thus need at most 7 iteration to turn our partial result with one bit of\n // precision into the expected uint128 result.\n unchecked {\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n\n // If x is not a perfect square, round the result toward zero.\n uint256 roundedResult = x / result;\n if (result >= roundedResult) {\n result = roundedResult;\n }\n }\n}\n" + }, + "contracts/utils/StableMath.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\n// Based on StableMath from Stability Labs Pty. Ltd.\n// https://github.com/mstable/mStable-contracts/blob/master/contracts/shared/StableMath.sol\n\nlibrary StableMath {\n using SafeMath for uint256;\n\n /**\n * @dev Scaling unit for use in specific calculations,\n * where 1 * 10**18, or 1e18 represents a unit '1'\n */\n uint256 private constant FULL_SCALE = 1e18;\n\n /***************************************\n Helpers\n ****************************************/\n\n /**\n * @dev Adjust the scale of an integer\n * @param to Decimals to scale to\n * @param from Decimals to scale from\n */\n function scaleBy(\n uint256 x,\n uint256 to,\n uint256 from\n ) internal pure returns (uint256) {\n if (to > from) {\n x = x.mul(10**(to - from));\n } else if (to < from) {\n // slither-disable-next-line divide-before-multiply\n x = x.div(10**(from - to));\n }\n return x;\n }\n\n /***************************************\n Precise Arithmetic\n ****************************************/\n\n /**\n * @dev Multiplies two precise units, and then truncates by the full scale\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit\n */\n function mulTruncate(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulTruncateScale(x, y, FULL_SCALE);\n }\n\n /**\n * @dev Multiplies two precise units, and then truncates by the given scale. For example,\n * when calculating 90% of 10e18, (10e18 * 9e17) / 1e18 = (9e36) / 1e18 = 9e18\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @param scale Scale unit\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit\n */\n function mulTruncateScale(\n uint256 x,\n uint256 y,\n uint256 scale\n ) internal pure returns (uint256) {\n // e.g. assume scale = fullScale\n // z = 10e18 * 9e17 = 9e36\n uint256 z = x.mul(y);\n // return 9e36 / 1e18 = 9e18\n return z.div(scale);\n }\n\n /**\n * @dev Multiplies two precise units, and then truncates by the full scale, rounding up the result\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit, rounded up to the closest base unit.\n */\n function mulTruncateCeil(uint256 x, uint256 y)\n internal\n pure\n returns (uint256)\n {\n // e.g. 8e17 * 17268172638 = 138145381104e17\n uint256 scaled = x.mul(y);\n // e.g. 138145381104e17 + 9.99...e17 = 138145381113.99...e17\n uint256 ceil = scaled.add(FULL_SCALE.sub(1));\n // e.g. 13814538111.399...e18 / 1e18 = 13814538111\n return ceil.div(FULL_SCALE);\n }\n\n /**\n * @dev Precisely divides two units, by first scaling the left hand operand. Useful\n * for finding percentage weightings, i.e. 8e18/10e18 = 80% (or 8e17)\n * @param x Left hand input to division\n * @param y Right hand input to division\n * @return Result after multiplying the left operand by the scale, and\n * executing the division on the right hand input.\n */\n function divPrecisely(uint256 x, uint256 y)\n internal\n pure\n returns (uint256)\n {\n // e.g. 8e18 * 1e18 = 8e36\n uint256 z = x.mul(FULL_SCALE);\n // e.g. 8e36 / 10e18 = 8e17\n return z.div(y);\n }\n}\n" + }, + "contracts/vault/OETHBaseVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"./OETHVaultAdmin.sol\";\n\n/**\n * @title OETH Base VaultAdmin Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBaseVaultAdmin is OETHVaultAdmin {\n constructor(address _weth) OETHVaultAdmin(_weth) {}\n}\n" + }, + "contracts/vault/OETHBaseVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { OETHVaultCore } from \"./OETHVaultCore.sol\";\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\n\n/**\n * @title OETH Base VaultCore Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBaseVaultCore is OETHVaultCore {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(address _weth) OETHVaultCore(_weth) {}\n\n // @inheritdoc OETHVaultCore\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n override\n {\n // Only Strategist or Governor can redeem using the Vault for now.\n // We don't have the onlyGovernorOrStrategist modifier on VaultCore.\n // Since we won't be using that modifier anywhere in the VaultCore as well,\n // the check has been added inline instead of moving it to VaultStorage.\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n\n super._redeem(_amount, _minimumUnitAmount);\n }\n}\n" + }, + "contracts/vault/OETHBaseZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\n\ncontract OETHBaseZapper {\n IERC20 public immutable oethb;\n IERC4626 public immutable woethb;\n IVault public immutable vault;\n\n IWETH9 public constant weth =\n IWETH9(0x4200000000000000000000000000000000000006);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(\n address _oethb,\n address _woethb,\n address _vault\n ) {\n oethb = IERC20(_oethb);\n woethb = IERC4626(_woethb);\n vault = IVault(_vault);\n\n weth.approve(address(_vault), type(uint256).max);\n IERC20(_oethb).approve(_woethb, type(uint256).max);\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return.\n * Will verify that the user is sent 1:1 for ETH.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit ETH and receive superOETHb in return\n * Will verify that the user is sent 1:1 for ETH.\n * @return Amount of OETH sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap ETH\n weth.deposit{ value: balance }();\n\n // Mint with WETH\n return _mint(balance, msg.sender);\n }\n\n /**\n * @dev Deposit ETH and receive superOETHb in return\n * @param minReceived min amount of wsuperOETHb to receive\n * @return Amount of wsuperOETHb sent to user\n */\n function depositETHForWrappedTokens(uint256 minReceived)\n external\n payable\n returns (uint256)\n {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap ETH\n weth.deposit{ value: balance }();\n\n // Mint with WETH\n uint256 mintedOethb = _mint(balance, address(this));\n\n // Wrap superOETHb into wsuperOETHb\n uint256 mintedWoethb = woethb.deposit(mintedOethb, msg.sender);\n\n require(mintedWoethb >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWoethb;\n }\n\n /**\n * @dev Deposit WETH and receive superOETHb in return\n * @param wethAmount Amount of WETH to deposit\n * @param minReceived min amount of wsuperOETHb to receive\n * @return Amount of wsuperOETHb sent to user\n */\n function depositWETHForWrappedTokens(\n uint256 wethAmount,\n uint256 minReceived\n ) external returns (uint256) {\n // slither-disable-next-line unchecked-transfer unused-return\n weth.transferFrom(msg.sender, address(this), wethAmount);\n\n emit Zap(msg.sender, address(weth), wethAmount);\n\n // Mint with WETH\n uint256 mintedOethb = _mint(wethAmount, address(this));\n\n // Wrap superOETHb into wsuperOETHb\n uint256 mintedWoethb = woethb.deposit(mintedOethb, msg.sender);\n\n require(mintedWoethb >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWoethb;\n }\n\n /**\n * @dev Internal function to mint superOETHb with WETH\n * @param minOETH Minimum amount of OETH to for user to receive\n * @param recipient Address that receives the tokens\n * @return Amount of OETH sent to user\n */\n function _mint(uint256 minOETH, address recipient)\n internal\n returns (uint256)\n {\n uint256 toMint = weth.balanceOf(address(this));\n vault.mint(address(weth), toMint, minOETH);\n uint256 mintedAmount = oethb.balanceOf(address(this));\n require(mintedAmount >= minOETH, \"Zapper: not enough minted\");\n\n if (recipient != address(this)) {\n require(oethb.transfer(recipient, mintedAmount));\n }\n\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/OETHVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Vault } from \"./Vault.sol\";\n\n/**\n * @title OETH Vault Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVault is Vault {\n\n}\n" + }, + "contracts/vault/OETHVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { VaultAdmin } from \"./VaultAdmin.sol\";\n\n/**\n * @title OETH VaultAdmin Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVaultAdmin is VaultAdmin {\n using SafeERC20 for IERC20;\n\n address public immutable weth;\n\n constructor(address _weth) {\n weth = _weth;\n }\n\n /**\n * @notice Adds a strategy to the mint whitelist.\n * Reverts if strategy isn't approved on Vault.\n * @param strategyAddr Strategy address\n */\n function addStrategyToMintWhitelist(address strategyAddr)\n external\n onlyGovernor\n {\n require(strategies[strategyAddr].isSupported, \"Strategy not approved\");\n\n require(\n !isMintWhitelistedStrategy[strategyAddr],\n \"Already whitelisted\"\n );\n\n isMintWhitelistedStrategy[strategyAddr] = true;\n\n emit StrategyAddedToMintWhitelist(strategyAddr);\n }\n\n /**\n * @notice Removes a strategy from the mint whitelist.\n * @param strategyAddr Strategy address\n */\n function removeStrategyFromMintWhitelist(address strategyAddr)\n external\n onlyGovernor\n {\n // Intentionally skipping `strategies.isSupported` check since\n // we may wanna remove an address even after removing the strategy\n\n require(isMintWhitelistedStrategy[strategyAddr], \"Not whitelisted\");\n\n isMintWhitelistedStrategy[strategyAddr] = false;\n\n emit StrategyRemovedFromMintWhitelist(strategyAddr);\n }\n\n /// @dev Simplified version of the deposit function as WETH is the only supported asset.\n function _depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal override {\n require(\n strategies[_strategyToAddress].isSupported,\n \"Invalid to Strategy\"\n );\n require(\n _assets.length == 1 && _amounts.length == 1 && _assets[0] == weth,\n \"Only WETH is supported\"\n );\n\n // Check the there is enough WETH to transfer once the WETH reserved for the withdrawal queue is accounted for\n require(_amounts[0] <= _wethAvailable(), \"Not enough WETH available\");\n\n // Send required amount of funds to the strategy\n IERC20(weth).safeTransfer(_strategyToAddress, _amounts[0]);\n\n // Deposit all the funds that have been sent to the strategy\n IStrategy(_strategyToAddress).depositAll();\n }\n\n function _withdrawFromStrategy(\n address _recipient,\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal override {\n super._withdrawFromStrategy(\n _recipient,\n _strategyFromAddress,\n _assets,\n _amounts\n );\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n function _withdrawAllFromStrategy(address _strategyAddr) internal override {\n super._withdrawAllFromStrategy(_strategyAddr);\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n function _withdrawAllFromStrategies() internal override {\n super._withdrawAllFromStrategies();\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n /// @dev Calculate how much WETH in the vault is not reserved for the withdrawal queue.\n // That is, it is available to be redeemed or deposited into a strategy.\n function _wethAvailable() internal view returns (uint256 wethAvailable) {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n uint256 outstandingWithdrawals = queue.queued - queue.claimed;\n\n // The amount of sitting in WETH in the vault\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // If there is not enough WETH in the vault to cover the outstanding withdrawals\n if (wethBalance <= outstandingWithdrawals) {\n return 0;\n }\n\n return wethBalance - outstandingWithdrawals;\n }\n\n function _swapCollateral(\n address,\n address,\n uint256,\n uint256,\n bytes calldata\n ) internal pure override returns (uint256) {\n revert(\"Collateral swap not supported\");\n }\n}\n" + }, + "contracts/vault/OETHVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { VaultCore } from \"./VaultCore.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\n\n/**\n * @title OETH VaultCore Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVaultCore is VaultCore {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n address public immutable weth;\n uint256 public wethAssetIndex;\n\n // For future use (because OETHBaseVaultCore inherits from this)\n uint256[50] private __gap;\n\n constructor(address _weth) {\n weth = _weth;\n }\n\n /**\n * @dev Caches WETH's index in `allAssets` variable.\n * Reduces gas usage by redeem by caching that.\n */\n function cacheWETHAssetIndex() external onlyGovernor {\n uint256 assetCount = allAssets.length;\n for (uint256 i; i < assetCount; ++i) {\n if (allAssets[i] == weth) {\n wethAssetIndex = i;\n break;\n }\n }\n\n require(allAssets[wethAssetIndex] == weth, \"Invalid WETH Asset Index\");\n }\n\n // @inheritdoc VaultCore\n function mintForStrategy(uint256 amount)\n external\n override\n whenNotCapitalPaused\n {\n require(\n strategies[msg.sender].isSupported == true,\n \"Unsupported strategy\"\n );\n require(\n isMintWhitelistedStrategy[msg.sender] == true,\n \"Not whitelisted strategy\"\n );\n\n emit Mint(msg.sender, amount);\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, amount);\n }\n\n // @inheritdoc VaultCore\n function burnForStrategy(uint256 amount)\n external\n override\n whenNotCapitalPaused\n {\n require(\n strategies[msg.sender].isSupported == true,\n \"Unsupported strategy\"\n );\n require(\n isMintWhitelistedStrategy[msg.sender] == true,\n \"Not whitelisted strategy\"\n );\n\n emit Redeem(msg.sender, amount);\n\n // Burn OTokens\n oUSD.burn(msg.sender, amount);\n }\n\n // @inheritdoc VaultCore\n // slither-disable-start reentrancy-no-eth\n function _mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) internal virtual override {\n require(_asset == weth, \"Unsupported asset for minting\");\n require(_amount > 0, \"Amount must be greater than 0\");\n require(\n _amount >= _minimumOusdAmount,\n \"Mint amount lower than minimum\"\n );\n\n emit Mint(msg.sender, _amount);\n\n // Rebase must happen before any transfers occur.\n if (!rebasePaused && _amount >= rebaseThreshold) {\n _rebase();\n }\n\n // Mint oTokens\n oUSD.mint(msg.sender, _amount);\n\n // Transfer the deposited coins to the vault\n IERC20(_asset).safeTransferFrom(msg.sender, address(this), _amount);\n\n // Give priority to the withdrawal queue for the new WETH liquidity\n _addWithdrawalQueueLiquidity();\n\n // Auto-allocate if necessary\n if (_amount >= autoAllocateThreshold) {\n _allocate();\n }\n }\n\n // slither-disable-end reentrancy-no-eth\n\n // @inheritdoc VaultCore\n function _calculateRedeemOutputs(uint256 _amount)\n internal\n view\n virtual\n override\n returns (uint256[] memory outputs)\n {\n // Overrides `VaultCore._calculateRedeemOutputs` to redeem with only\n // WETH instead of LST-mix. Doesn't change the function signature\n // for backward compatibility\n\n // Calculate redeem fee\n if (redeemFeeBps > 0) {\n uint256 redeemFee = _amount.mulTruncateScale(redeemFeeBps, 1e4);\n _amount = _amount - redeemFee;\n }\n\n // Ensure that the WETH index is cached\n uint256 _wethAssetIndex = wethAssetIndex;\n require(\n allAssets[_wethAssetIndex] == weth,\n \"WETH Asset index not cached\"\n );\n\n outputs = new uint256[](allAssets.length);\n outputs[_wethAssetIndex] = _amount;\n }\n\n // @inheritdoc VaultCore\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n override\n {\n // Override `VaultCore._redeem` to simplify it. Gets rid of oracle\n // usage and looping through all assets for LST-mix redeem. Instead\n // does a simple WETH-only redeem.\n emit Redeem(msg.sender, _amount);\n\n if (_amount == 0) {\n return;\n }\n\n // Amount excluding fees\n // No fee for the strategist or the governor, makes it easier to do operations\n uint256 amountMinusFee = (msg.sender == strategistAddr || isGovernor())\n ? _amount\n : _calculateRedeemOutputs(_amount)[wethAssetIndex];\n\n require(\n amountMinusFee >= _minimumUnitAmount,\n \"Redeem amount lower than minimum\"\n );\n\n // Is there enough WETH in the Vault available after accounting for the withdrawal queue\n require(_wethAvailable() >= amountMinusFee, \"Liquidity error\");\n\n // Transfer WETH minus the fee to the redeemer\n IERC20(weth).safeTransfer(msg.sender, amountMinusFee);\n\n // Burn OETH from user (including fees)\n oUSD.burn(msg.sender, _amount);\n\n // Prevent insolvency\n _postRedeem(_amount);\n }\n\n /**\n * @notice Request an asynchronous withdrawal of WETH in exchange for OETH.\n * The OETH is burned on request and the WETH is transferred to the withdrawer on claim.\n * This request can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal this request's `queued` amount.\n * There is a minimum of 10 minutes before a request can be claimed. After that, the request just needs\n * enough WETH liquidity in the Vault to satisfy all the outstanding requests to that point in the queue.\n * OETH is converted to WETH at 1:1.\n * @param _amount Amount of OETH to burn.\n * @return requestId Unique ID for the withdrawal request\n * @return queued Cumulative total of all WETH queued including already claimed requests.\n */\n function requestWithdrawal(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256 requestId, uint256 queued)\n {\n require(withdrawalClaimDelay > 0, \"Async withdrawals not enabled\");\n\n // The check that the requester has enough OETH is done in to later burn call\n\n requestId = withdrawalQueueMetadata.nextWithdrawalIndex;\n queued = withdrawalQueueMetadata.queued + _amount;\n\n // Store the next withdrawal request\n withdrawalQueueMetadata.nextWithdrawalIndex = SafeCast.toUint128(\n requestId + 1\n );\n // Store the updated queued amount which reserves WETH in the withdrawal queue\n // and reduces the vault's total assets\n withdrawalQueueMetadata.queued = SafeCast.toUint128(queued);\n // Store the user's withdrawal request\n withdrawalRequests[requestId] = WithdrawalRequest({\n withdrawer: msg.sender,\n claimed: false,\n timestamp: uint40(block.timestamp),\n amount: SafeCast.toUint128(_amount),\n queued: SafeCast.toUint128(queued)\n });\n\n // Burn the user's OETH\n oUSD.burn(msg.sender, _amount);\n\n // Prevent withdrawal if the vault is solvent by more than the allowed percentage\n _postRedeem(_amount);\n\n emit WithdrawalRequested(msg.sender, requestId, _amount, queued);\n }\n\n // slither-disable-start reentrancy-no-eth\n /**\n * @notice Claim a previously requested withdrawal once it is claimable.\n * This request can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal this request's `queued` amount and 10 minutes has passed.\n * If the requests is not claimable, the transaction will revert with `Queue pending liquidity`.\n * If the request is not older than 10 minutes, the transaction will revert with `Claim delay not met`.\n * OETH is converted to WETH at 1:1.\n * @param _requestId Unique ID for the withdrawal request\n * @return amount Amount of WETH transferred to the withdrawer\n */\n function claimWithdrawal(uint256 _requestId)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256 amount)\n {\n // Try and get more liquidity if there is not enough available\n if (\n withdrawalRequests[_requestId].queued >\n withdrawalQueueMetadata.claimable\n ) {\n // Add any WETH to the withdrawal queue\n // this needs to remain here as:\n // - Vault can be funded and `addWithdrawalQueueLiquidity` is not externally called\n // - funds can be withdrawn from a strategy\n //\n // Those funds need to be added to withdrawal queue liquidity\n _addWithdrawalQueueLiquidity();\n }\n\n amount = _claimWithdrawal(_requestId);\n\n // transfer WETH from the vault to the withdrawer\n IERC20(weth).safeTransfer(msg.sender, amount);\n\n // Prevent insolvency\n _postRedeem(amount);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @notice Claim a previously requested withdrawals once they are claimable.\n * This requests can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal each request's `queued` amount and 10 minutes has passed.\n * If one of the requests is not claimable, the whole transaction will revert with `Queue pending liquidity`.\n * If one of the requests is not older than 10 minutes,\n * the whole transaction will revert with `Claim delay not met`.\n * @param _requestIds Unique ID of each withdrawal request\n * @return amounts Amount of WETH received for each request\n * @return totalAmount Total amount of WETH transferred to the withdrawer\n */\n function claimWithdrawals(uint256[] calldata _requestIds)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256[] memory amounts, uint256 totalAmount)\n {\n // Add any WETH to the withdrawal queue\n // this needs to remain here as:\n // - Vault can be funded and `addWithdrawalQueueLiquidity` is not externally called\n // - funds can be withdrawn from a strategy\n //\n // Those funds need to be added to withdrawal queue liquidity\n _addWithdrawalQueueLiquidity();\n\n amounts = new uint256[](_requestIds.length);\n for (uint256 i; i < _requestIds.length; ++i) {\n amounts[i] = _claimWithdrawal(_requestIds[i]);\n totalAmount += amounts[i];\n }\n\n // transfer all the claimed WETH from the vault to the withdrawer\n IERC20(weth).safeTransfer(msg.sender, totalAmount);\n\n // Prevent insolvency\n _postRedeem(totalAmount);\n }\n\n function _claimWithdrawal(uint256 requestId)\n internal\n returns (uint256 amount)\n {\n require(withdrawalClaimDelay > 0, \"Async withdrawals not enabled\");\n\n // Load the structs from storage into memory\n WithdrawalRequest memory request = withdrawalRequests[requestId];\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n require(\n request.timestamp + withdrawalClaimDelay <= block.timestamp,\n \"Claim delay not met\"\n );\n // If there isn't enough reserved liquidity in the queue to claim\n require(request.queued <= queue.claimable, \"Queue pending liquidity\");\n require(request.withdrawer == msg.sender, \"Not requester\");\n require(request.claimed == false, \"Already claimed\");\n\n // Store the request as claimed\n withdrawalRequests[requestId].claimed = true;\n // Store the updated claimed amount\n withdrawalQueueMetadata.claimed = queue.claimed + request.amount;\n\n emit WithdrawalClaimed(msg.sender, requestId, request.amount);\n\n return request.amount;\n }\n\n /// @notice Adds WETH to the withdrawal queue if there is a funding shortfall.\n /// @dev is called from the Native Staking strategy when validator withdrawals are processed.\n /// It also called before any WETH is allocated to a strategy.\n function addWithdrawalQueueLiquidity() external {\n _addWithdrawalQueueLiquidity();\n }\n\n /// @dev Adds WETH to the withdrawal queue if there is a funding shortfall.\n /// This assumes 1 WETH equal 1 OETH.\n function _addWithdrawalQueueLiquidity()\n internal\n returns (uint256 addedClaimable)\n {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // Check if the claimable WETH is less than the queued amount\n uint256 queueShortfall = queue.queued - queue.claimable;\n\n // No need to do anything is the withdrawal queue is full funded\n if (queueShortfall == 0) {\n return 0;\n }\n\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // Of the claimable withdrawal requests, how much is unclaimed?\n // That is, the amount of WETH that is currently allocated for the withdrawal queue\n uint256 allocatedWeth = queue.claimable - queue.claimed;\n\n // If there is no unallocated WETH then there is nothing to add to the queue\n if (wethBalance <= allocatedWeth) {\n return 0;\n }\n\n uint256 unallocatedWeth = wethBalance - allocatedWeth;\n\n // the new claimable amount is the smaller of the queue shortfall or unallocated weth\n addedClaimable = queueShortfall < unallocatedWeth\n ? queueShortfall\n : unallocatedWeth;\n uint256 newClaimable = queue.claimable + addedClaimable;\n\n // Store the new claimable amount back to storage\n withdrawalQueueMetadata.claimable = SafeCast.toUint128(newClaimable);\n\n // emit a WithdrawalClaimable event\n emit WithdrawalClaimable(newClaimable, addedClaimable);\n }\n\n /***************************************\n View Functions\n ****************************************/\n\n /// @dev Calculate how much WETH in the vault is not reserved for the withdrawal queue.\n // That is, it is available to be redeemed or deposited into a strategy.\n function _wethAvailable() internal view returns (uint256 wethAvailable) {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n uint256 outstandingWithdrawals = queue.queued - queue.claimed;\n\n // The amount of sitting in WETH in the vault\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // If there is not enough WETH in the vault to cover the outstanding withdrawals\n if (wethBalance <= outstandingWithdrawals) {\n return 0;\n }\n\n return wethBalance - outstandingWithdrawals;\n }\n\n /// @dev Get the balance of an asset held in Vault and all strategies\n /// less any WETH that is reserved for the withdrawal queue.\n /// WETH is the only asset that can return a non-zero balance.\n /// All other assets will return 0 even if there is some dust amounts left in the Vault.\n /// For example, there is 1 wei left of stETH in the OETH Vault but will return 0 in this function.\n ///\n /// If there is not enough WETH in the vault and all strategies to cover all outstanding\n /// withdrawal requests then return a WETH balance of 0\n function _checkBalance(address _asset)\n internal\n view\n override\n returns (uint256 balance)\n {\n if (_asset != weth) {\n return 0;\n }\n\n // Get the WETH in the vault and the strategies\n balance = super._checkBalance(_asset);\n\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // If the vault becomes insolvent enough that the total value in the vault and all strategies\n // is less than the outstanding withdrawals.\n // For example, there was a mass slashing event and most users request a withdrawal.\n if (balance + queue.claimed < queue.queued) {\n return 0;\n }\n\n // Need to remove WETH that is reserved for the withdrawal queue\n return balance + queue.claimed - queue.queued;\n }\n\n /**\n * @notice Allocate unallocated funds on Vault to strategies.\n **/\n function allocate() external override whenNotCapitalPaused nonReentrant {\n // Add any unallocated WETH to the withdrawal queue first\n _addWithdrawalQueueLiquidity();\n\n _allocate();\n }\n\n /// @dev Allocate WETH to the default WETH strategy if there is excess to the Vault buffer.\n /// This is called from either `mint` or `allocate` and assumes `_addWithdrawalQueueLiquidity`\n /// has been called before this function.\n function _allocate() internal override {\n // No need to do anything if no default strategy for WETH\n address depositStrategyAddr = assetDefaultStrategies[weth];\n if (depositStrategyAddr == address(0)) return;\n\n uint256 wethAvailableInVault = _wethAvailable();\n // No need to do anything if there isn't any WETH in the vault to allocate\n if (wethAvailableInVault == 0) return;\n\n // Calculate the target buffer for the vault using the total supply\n uint256 totalSupply = oUSD.totalSupply();\n uint256 targetBuffer = totalSupply.mulTruncate(vaultBuffer);\n\n // If available WETH in the Vault is below or equal the target buffer then there's nothing to allocate\n if (wethAvailableInVault <= targetBuffer) return;\n\n // The amount of assets to allocate to the default strategy\n uint256 allocateAmount = wethAvailableInVault - targetBuffer;\n\n IStrategy strategy = IStrategy(depositStrategyAddr);\n // Transfer WETH to the strategy and call the strategy's deposit function\n IERC20(weth).safeTransfer(address(strategy), allocateAmount);\n strategy.deposit(weth, allocateAmount);\n\n emit AssetAllocated(weth, depositStrategyAddr, allocateAmount);\n }\n\n /// @dev The total value of all WETH held by the vault and all its strategies\n /// less any WETH that is reserved for the withdrawal queue.\n ///\n // If there is not enough WETH in the vault and all strategies to cover all outstanding\n // withdrawal requests then return a total value of 0.\n function _totalValue() internal view override returns (uint256 value) {\n // As WETH is the only asset, just return the WETH balance\n return _checkBalance(weth);\n }\n\n /// @dev Only WETH is supported in the OETH Vault so return the WETH balance only\n /// Any ETH balances in the Vault will be ignored.\n /// Amounts from previously supported vault assets will also be ignored.\n /// For example, there is 1 wei left of stETH in the OETH Vault but is will be ignored.\n function _totalValueInVault()\n internal\n view\n override\n returns (uint256 value)\n {\n value = IERC20(weth).balanceOf(address(this));\n }\n}\n" + }, + "contracts/vault/OETHZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { ISfrxETH } from \"../interfaces/ISfrxETH.sol\";\n\ncontract OETHZapper {\n IERC20 public immutable oeth;\n IVault public immutable vault;\n\n IWETH9 public constant weth =\n IWETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);\n IERC20 public constant frxeth =\n IERC20(0x5E8422345238F34275888049021821E8E08CAa1f);\n ISfrxETH public constant sfrxeth =\n ISfrxETH(0xac3E018457B222d93114458476f3E3416Abbe38F);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(address _oeth, address _vault) {\n oeth = IERC20(_oeth);\n vault = IVault(_vault);\n\n weth.approve(address(_vault), type(uint256).max);\n frxeth.approve(address(_vault), type(uint256).max);\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return.\n * Will verify that the user is sent 1:1 for ETH.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return\n * Will verify that the user is sent 1:1 for ETH.\n * @return Amount of OETH sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n weth.deposit{ value: balance }();\n emit Zap(msg.sender, ETH_MARKER, balance);\n return _mint(address(weth), balance);\n }\n\n /**\n * @dev Deposit SFRXETH to the vault and receive OETH in return\n * @param amount Amount of SFRXETH to deposit\n * @param minOETH Minimum amount of OETH to receive\n * @return Amount of OETH sent to user\n */\n function depositSFRXETH(uint256 amount, uint256 minOETH)\n external\n returns (uint256)\n {\n sfrxeth.redeem(amount, address(this), msg.sender);\n emit Zap(msg.sender, address(sfrxeth), amount);\n return _mint(address(frxeth), minOETH);\n }\n\n /**\n * @dev Internal function to mint OETH from an asset\n * @param asset Address of asset for the vault to mint from\n * @param minOETH Minimum amount of OETH to for user to receive\n * @return Amount of OETH sent to user\n */\n function _mint(address asset, uint256 minOETH) internal returns (uint256) {\n uint256 toMint = IERC20(asset).balanceOf(address(this));\n vault.mint(asset, toMint, minOETH);\n uint256 mintedAmount = oeth.balanceOf(address(this));\n require(mintedAmount >= minOETH, \"Zapper: not enough minted\");\n require(oeth.transfer(msg.sender, mintedAmount));\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/OSonicVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"./OETHVaultAdmin.sol\";\n\n/**\n * @title Origin Sonic VaultAdmin contract on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonicVaultAdmin is OETHVaultAdmin {\n /// @param _wS Sonic's Wrapped S token\n constructor(address _wS) OETHVaultAdmin(_wS) {}\n\n /***************************************\n Asset Config\n ****************************************/\n\n /**\n * @notice Add a supported asset to the contract, i.e. one that can be to mint OTokens.\n * @dev Overridden to remove price provider integration\n * @param _asset Address of asset\n * @param _unitConversion 0 decimals, 1 exchange rate\n */\n function supportAsset(address _asset, uint8 _unitConversion)\n external\n override\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Asset already supported\");\n\n assets[_asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(_unitConversion),\n decimals: 0, // will be overridden in _cacheDecimals\n allowedOracleSlippageBps: 0 // 0% by default\n });\n\n _cacheDecimals(_asset);\n allAssets.push(_asset);\n\n emit AssetSupported(_asset);\n }\n}\n" + }, + "contracts/vault/OSonicVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultCore } from \"./OETHVaultCore.sol\";\n\n/**\n * @title Origin Sonic VaultCore contract on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonicVaultCore is OETHVaultCore {\n /// @param _wS Sonic's Wrapped S token\n constructor(address _wS) OETHVaultCore(_wS) {}\n\n /**\n * @notice Instant redeem is not supported on Sonic.\n * Use the asynchronous `requestWithdrawal` a `claimWithdrawal` instead.\n */\n function _redeem(uint256, uint256) internal override {\n revert(\"unsupported function\");\n }\n}\n" + }, + "contracts/vault/OSonicZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWrappedSonic } from \"../interfaces/sonic/IWrappedSonic.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\n\n/**\n * @title Zapper for Origin Sonic (OS) tokens\n * @author Origin Protocol Inc\n */\ncontract OSonicZapper {\n IERC20 public immutable OS;\n IERC4626 public immutable wOS;\n IVault public immutable vault;\n\n IWrappedSonic public constant wS =\n IWrappedSonic(0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(\n address _OS,\n address _wOS,\n address _vault\n ) {\n OS = IERC20(_OS);\n wOS = IERC4626(_wOS);\n vault = IVault(_vault);\n\n wS.approve(address(_vault), type(uint256).max);\n IERC20(_OS).approve(_wOS, type(uint256).max);\n }\n\n /**\n * @dev Deposit native S currency and receive Origin Sonic (OS) tokens in return.\n * Will verify that the user is sent 1:1 for S.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit native S currency and receive Origin Sonic (OS) tokens in return.\n * Will verify that the user is sent 1:1 for S.\n * @return Amount of Origin Sonic (OS) tokens sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap native S\n wS.deposit{ value: balance }();\n\n // Mint Origin Sonic (OS) with Wrapped Sonic (wS)\n return _mint(balance, msg.sender);\n }\n\n /**\n * @dev Deposit S and receive Wrapped Origin Sonic (wOS) in return\n * @param minReceived min amount of Wrapped Origin Sonic (wOS) to receive\n * @return Amount of Wrapped Origin Sonic (wOS) tokens sent to user\n */\n function depositSForWrappedTokens(uint256 minReceived)\n external\n payable\n returns (uint256)\n {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap S\n wS.deposit{ value: balance }();\n\n // Mint with Wrapped Sonic\n uint256 mintOS = _mint(balance, address(this));\n\n // Wrap Origin Sonic (OS) into Wrapped Origin Sonic (wOS)\n uint256 mintedWOS = wOS.deposit(mintOS, msg.sender);\n\n require(mintedWOS >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWOS;\n }\n\n /**\n * @dev Deposit Wrapped Sonic (wS) tokens and receive Wrapped Origin Sonic (wOS) tokens in return\n * @param wSAmount Amount of Wrapped Sonic (wS) to deposit\n * @param minReceived min amount of Wrapped Origin Sonic (wOS) token to receive\n * @return Amount of Wrapped Origin Sonic (wOS) tokens sent to user\n */\n function depositWSForWrappedTokens(uint256 wSAmount, uint256 minReceived)\n external\n returns (uint256)\n {\n // slither-disable-next-line unchecked-transfer unused-return\n wS.transferFrom(msg.sender, address(this), wSAmount);\n\n emit Zap(msg.sender, address(wS), wSAmount);\n\n // Mint with Wrapped Sonic (wS)\n uint256 mintedOS = _mint(wSAmount, address(this));\n\n // Wrap Origin Sonic (OS) tokens into Wrapped Origin Sonic (wOS) tokens\n uint256 mintedWOS = wOS.deposit(mintedOS, msg.sender);\n\n require(mintedWOS >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWOS;\n }\n\n /**\n * @dev Internal function to mint Origin Sonic (OS) with Wrapped S (wS)\n * @param minOS Minimum amount of Origin Sonic (OS) tokens the user can receive\n * @param recipient Address that receives the tokens\n * @return Amount of Origin Sonic (OS) tokens sent to the recipient\n */\n function _mint(uint256 minOS, address recipient)\n internal\n returns (uint256)\n {\n uint256 toMint = wS.balanceOf(address(this));\n vault.mint(address(wS), toMint, minOS);\n uint256 mintedAmount = OS.balanceOf(address(this));\n require(mintedAmount >= minOS, \"Zapper: not enough minted\");\n\n if (recipient != address(this)) {\n require(OS.transfer(recipient, mintedAmount));\n }\n\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/Vault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD VaultInitializer Contract\n * @notice The VaultInitializer sets up the initial contract.\n * @author Origin Protocol Inc\n */\nimport { VaultInitializer } from \"./VaultInitializer.sol\";\nimport { VaultAdmin } from \"./VaultAdmin.sol\";\n\ncontract Vault is VaultInitializer, VaultAdmin {}\n" + }, + "contracts/vault/VaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultAdmin contract\n * @notice The VaultAdmin contract makes configuration and admin calls on the vault.\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport \"./VaultStorage.sol\";\n\ncontract VaultAdmin is VaultStorage {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /***************************************\n Configuration\n ****************************************/\n\n /**\n * @notice Set address of price provider.\n * @param _priceProvider Address of price provider\n */\n function setPriceProvider(address _priceProvider) external onlyGovernor {\n priceProvider = _priceProvider;\n emit PriceProviderUpdated(_priceProvider);\n }\n\n /**\n * @notice Set a fee in basis points to be charged for a redeem.\n * @param _redeemFeeBps Basis point fee to be charged\n */\n function setRedeemFeeBps(uint256 _redeemFeeBps) external onlyGovernor {\n require(_redeemFeeBps <= 1000, \"Redeem fee should not be over 10%\");\n redeemFeeBps = _redeemFeeBps;\n emit RedeemFeeUpdated(_redeemFeeBps);\n }\n\n /**\n * @notice Set a buffer of assets to keep in the Vault to handle most\n * redemptions without needing to spend gas unwinding assets from a Strategy.\n * @param _vaultBuffer Percentage using 18 decimals. 100% = 1e18.\n */\n function setVaultBuffer(uint256 _vaultBuffer)\n external\n onlyGovernorOrStrategist\n {\n require(_vaultBuffer <= 1e18, \"Invalid value\");\n vaultBuffer = _vaultBuffer;\n emit VaultBufferUpdated(_vaultBuffer);\n }\n\n /**\n * @notice Sets the minimum amount of OTokens in a mint to trigger an\n * automatic allocation of funds afterwords.\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setAutoAllocateThreshold(uint256 _threshold)\n external\n onlyGovernor\n {\n autoAllocateThreshold = _threshold;\n emit AllocateThresholdUpdated(_threshold);\n }\n\n /**\n * @notice Set a minimum amount of OTokens in a mint or redeem that triggers a\n * rebase\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setRebaseThreshold(uint256 _threshold) external onlyGovernor {\n rebaseThreshold = _threshold;\n emit RebaseThresholdUpdated(_threshold);\n }\n\n /**\n * @notice Set address of Strategist\n * @param _address Address of Strategist\n */\n function setStrategistAddr(address _address) external onlyGovernor {\n strategistAddr = _address;\n emit StrategistUpdated(_address);\n }\n\n /**\n * @notice Set the default Strategy for an asset, i.e. the one which the asset\n will be automatically allocated to and withdrawn from\n * @param _asset Address of the asset\n * @param _strategy Address of the Strategy\n */\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external\n onlyGovernorOrStrategist\n {\n emit AssetDefaultStrategyUpdated(_asset, _strategy);\n // If its a zero address being passed for the strategy we are removing\n // the default strategy\n if (_strategy != address(0)) {\n // Make sure the strategy meets some criteria\n require(strategies[_strategy].isSupported, \"Strategy not approved\");\n IStrategy strategy = IStrategy(_strategy);\n require(assets[_asset].isSupported, \"Asset is not supported\");\n require(\n strategy.supportsAsset(_asset),\n \"Asset not supported by Strategy\"\n );\n }\n assetDefaultStrategies[_asset] = _strategy;\n }\n\n /**\n * @notice Set maximum amount of OTokens that can at any point be minted and deployed\n * to strategy (used only by ConvexOUSDMetaStrategy for now).\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setNetOusdMintForStrategyThreshold(uint256 _threshold)\n external\n onlyGovernor\n {\n /**\n * Because `netOusdMintedForStrategy` check in vault core works both ways\n * (positive and negative) the actual impact of the amount of OToken minted\n * could be double the threshold. E.g.:\n * - contract has threshold set to 100\n * - state of netOusdMinted is -90\n * - in effect it can mint 190 OToken and still be within limits\n *\n * We are somewhat mitigating this behaviour by resetting the netOusdMinted\n * counter whenever new threshold is set. So it can only move one threshold\n * amount in each direction. This also enables us to reduce the threshold\n * amount and not have problems with current netOusdMinted being near\n * limits on either side.\n */\n netOusdMintedForStrategy = 0;\n netOusdMintForStrategyThreshold = _threshold;\n emit NetOusdMintForStrategyThresholdChanged(_threshold);\n }\n\n /**\n * @notice Changes the async withdrawal claim period for OETH & superOETHb\n * @param _delay Delay period (should be between 10 mins to 7 days).\n * Set to 0 to disable async withdrawals\n */\n function setWithdrawalClaimDelay(uint256 _delay) external onlyGovernor {\n require(\n _delay == 0 || (_delay >= 10 minutes && _delay <= 15 days),\n \"Invalid claim delay period\"\n );\n withdrawalClaimDelay = _delay;\n emit WithdrawalClaimDelayUpdated(_delay);\n }\n\n /**\n * @notice Set a yield streaming max rate. This spreads yield over\n * time if it is above the max rate.\n * @param yearlyApr in 1e18 notation. 3 * 1e18 = 3% APR\n */\n function setRebaseRateMax(uint256 yearlyApr)\n external\n onlyGovernorOrStrategist\n {\n // The old yield will be at the old rate\n IVault(address(this)).rebase();\n // Change the rate\n uint256 newPerSecond = yearlyApr / 100 / 365 days;\n require(newPerSecond <= MAX_REBASE_PER_SECOND, \"Rate too high\");\n rebasePerSecondMax = newPerSecond.toUint64();\n emit RebasePerSecondMaxChanged(newPerSecond);\n }\n\n /**\n * @notice Set the drip duration period\n * @param _dripDuration Time in seconds to target a constant yield rate\n */\n function setDripDuration(uint256 _dripDuration)\n external\n onlyGovernorOrStrategist\n {\n // The old yield will be at the old rate\n IVault(address(this)).rebase();\n dripDuration = _dripDuration.toUint64();\n emit DripDurationChanged(_dripDuration);\n }\n\n /***************************************\n Swaps\n ****************************************/\n\n /**\n * @notice Strategist swaps collateral assets sitting in the vault.\n * @param _fromAsset The token address of the asset being sold by the vault.\n * @param _toAsset The token address of the asset being purchased by the vault.\n * @param _fromAssetAmount The amount of assets being sold by the vault.\n * @param _minToAssetAmount The minimum amount of assets to be purchased.\n * @param _data implementation specific data. eg 1Inch swap data\n * @return toAssetAmount The amount of toAssets that was received from the swap\n */\n function swapCollateral(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n )\n external\n nonReentrant\n onlyGovernorOrStrategist\n returns (uint256 toAssetAmount)\n {\n toAssetAmount = _swapCollateral(\n _fromAsset,\n _toAsset,\n _fromAssetAmount,\n _minToAssetAmount,\n _data\n );\n }\n\n function _swapCollateral(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n ) internal virtual returns (uint256 toAssetAmount) {\n // Check fromAsset and toAsset are valid\n Asset memory fromAssetConfig = assets[_fromAsset];\n Asset memory toAssetConfig = assets[_toAsset];\n require(fromAssetConfig.isSupported, \"From asset is not supported\");\n require(toAssetConfig.isSupported, \"To asset is not supported\");\n\n // Load swap config into memory to avoid separate SLOADs\n SwapConfig memory config = swapConfig;\n\n // Scope a new block to remove toAssetBalBefore from the scope of swapCollateral.\n // This avoids a stack too deep error.\n {\n uint256 toAssetBalBefore = IERC20(_toAsset).balanceOf(\n address(this)\n );\n\n // Transfer from assets to the swapper contract\n IERC20(_fromAsset).safeTransfer(config.swapper, _fromAssetAmount);\n\n // Call to the Swapper contract to do the actual swap\n // The -1 is required for stETH which sometimes transfers 1 wei less than what was specified.\n // slither-disable-next-line unused-return\n ISwapper(config.swapper).swap(\n _fromAsset,\n _toAsset,\n _fromAssetAmount - 1,\n _minToAssetAmount,\n _data\n );\n\n // Compute the change in asset balance held by the Vault\n toAssetAmount =\n IERC20(_toAsset).balanceOf(address(this)) -\n toAssetBalBefore;\n }\n\n // Check the to assets returned is above slippage amount specified by the strategist\n require(\n toAssetAmount >= _minToAssetAmount,\n \"Strategist slippage limit\"\n );\n\n // Scope a new block to remove minOracleToAssetAmount from the scope of swapCollateral.\n // This avoids a stack too deep error.\n {\n // Check the slippage against the Oracle in case the strategist made a mistake or has become malicious.\n // to asset amount = from asset amount * from asset price / to asset price\n uint256 minOracleToAssetAmount = (_fromAssetAmount *\n (1e4 - fromAssetConfig.allowedOracleSlippageBps) *\n IOracle(priceProvider).price(_fromAsset)) /\n (IOracle(priceProvider).price(_toAsset) *\n (1e4 + toAssetConfig.allowedOracleSlippageBps));\n\n // Scale both sides up to 18 decimals to compare\n require(\n toAssetAmount.scaleBy(18, toAssetConfig.decimals) >=\n minOracleToAssetAmount.scaleBy(\n 18,\n fromAssetConfig.decimals\n ),\n \"Oracle slippage limit exceeded\"\n );\n }\n\n // Check the vault's total value hasn't gone below the OToken total supply\n // by more than the allowed percentage.\n require(\n IVault(address(this)).totalValue() >=\n (oUSD.totalSupply() * ((1e4 - config.allowedUndervalueBps))) /\n 1e4,\n \"Allowed value < supply\"\n );\n\n emit Swapped(_fromAsset, _toAsset, _fromAssetAmount, toAssetAmount);\n }\n\n /***************************************\n Swap Config\n ****************************************/\n\n /**\n * @notice Set the contract the performs swaps of collateral assets.\n * @param _swapperAddr Address of the Swapper contract that implements the ISwapper interface.\n */\n function setSwapper(address _swapperAddr) external onlyGovernor {\n swapConfig.swapper = _swapperAddr;\n emit SwapperChanged(_swapperAddr);\n }\n\n /// @notice Contract that swaps the vault's collateral assets\n function swapper() external view returns (address swapper_) {\n swapper_ = swapConfig.swapper;\n }\n\n /**\n * @notice Set max allowed percentage the vault total value can drop below the OToken total supply in basis points\n * when executing collateral swaps.\n * @param _basis Percentage in basis points. eg 100 == 1%\n */\n function setSwapAllowedUndervalue(uint16 _basis) external onlyGovernor {\n require(_basis < 10001, \"Invalid basis points\");\n swapConfig.allowedUndervalueBps = _basis;\n emit SwapAllowedUndervalueChanged(_basis);\n }\n\n /**\n * @notice Max allowed percentage the vault total value can drop below the OToken total supply in basis points\n * when executing a collateral swap.\n * For example 100 == 1%\n * @return value Percentage in basis points.\n */\n function allowedSwapUndervalue() external view returns (uint256 value) {\n value = swapConfig.allowedUndervalueBps;\n }\n\n /**\n * @notice Set the allowed slippage from the Oracle price for collateral asset swaps.\n * @param _asset Address of the asset token.\n * @param _allowedOracleSlippageBps allowed slippage from Oracle in basis points. eg 20 = 0.2%. Max 10%.\n */\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external\n onlyGovernor\n {\n require(assets[_asset].isSupported, \"Asset not supported\");\n require(_allowedOracleSlippageBps < 1000, \"Slippage too high\");\n\n assets[_asset].allowedOracleSlippageBps = _allowedOracleSlippageBps;\n\n emit SwapSlippageChanged(_asset, _allowedOracleSlippageBps);\n }\n\n /***************************************\n Asset Config\n ****************************************/\n\n /**\n * @notice Add a supported asset to the contract, i.e. one that can be\n * to mint OTokens.\n * @param _asset Address of asset\n */\n function supportAsset(address _asset, uint8 _unitConversion)\n external\n virtual\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Asset already supported\");\n\n assets[_asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(_unitConversion),\n decimals: 0, // will be overridden in _cacheDecimals\n allowedOracleSlippageBps: 0 // 0% by default\n });\n\n _cacheDecimals(_asset);\n allAssets.push(_asset);\n\n // Verify that our oracle supports the asset\n // slither-disable-next-line unused-return\n IOracle(priceProvider).price(_asset);\n\n emit AssetSupported(_asset);\n }\n\n /**\n * @notice Remove a supported asset from the Vault\n * @param _asset Address of asset\n */\n function removeAsset(address _asset) external onlyGovernor {\n require(assets[_asset].isSupported, \"Asset not supported\");\n\n // 1e13 for 18 decimals. And 10 for 6 decimals\n uint256 maxDustBalance = uint256(1e13).scaleBy(\n assets[_asset].decimals,\n 18\n );\n\n require(\n IVault(address(this)).checkBalance(_asset) <= maxDustBalance,\n \"Vault still holds asset\"\n );\n\n uint256 assetsCount = allAssets.length;\n uint256 assetIndex = assetsCount; // initialize at invalid index\n for (uint256 i = 0; i < assetsCount; ++i) {\n if (allAssets[i] == _asset) {\n assetIndex = i;\n break;\n }\n }\n\n // Note: If asset is not found in `allAssets`, the following line\n // will revert with an out-of-bound error. However, there's no\n // reason why an asset would have `Asset.isSupported = true` but\n // not exist in `allAssets`.\n\n // Update allAssets array\n allAssets[assetIndex] = allAssets[assetsCount - 1];\n allAssets.pop();\n\n // Reset default strategy\n assetDefaultStrategies[_asset] = address(0);\n emit AssetDefaultStrategyUpdated(_asset, address(0));\n\n // Remove asset from storage\n delete assets[_asset];\n\n emit AssetRemoved(_asset);\n }\n\n /**\n * @notice Cache decimals on OracleRouter for a particular asset. This action\n * is required before that asset's price can be accessed.\n * @param _asset Address of asset token\n */\n function cacheDecimals(address _asset) external onlyGovernor {\n _cacheDecimals(_asset);\n }\n\n /***************************************\n Strategy Config\n ****************************************/\n\n /**\n * @notice Add a strategy to the Vault.\n * @param _addr Address of the strategy to add\n */\n function approveStrategy(address _addr) external onlyGovernor {\n require(!strategies[_addr].isSupported, \"Strategy already approved\");\n strategies[_addr] = Strategy({ isSupported: true, _deprecated: 0 });\n allStrategies.push(_addr);\n emit StrategyApproved(_addr);\n }\n\n /**\n * @notice Remove a strategy from the Vault.\n * @param _addr Address of the strategy to remove\n */\n\n function removeStrategy(address _addr) external onlyGovernor {\n require(strategies[_addr].isSupported, \"Strategy not approved\");\n\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n require(\n assetDefaultStrategies[allAssets[i]] != _addr,\n \"Strategy is default for an asset\"\n );\n }\n\n // Initialize strategyIndex with out of bounds result so function will\n // revert if no valid index found\n uint256 stratCount = allStrategies.length;\n uint256 strategyIndex = stratCount;\n for (uint256 i = 0; i < stratCount; ++i) {\n if (allStrategies[i] == _addr) {\n strategyIndex = i;\n break;\n }\n }\n\n if (strategyIndex < stratCount) {\n allStrategies[strategyIndex] = allStrategies[stratCount - 1];\n allStrategies.pop();\n\n // Mark the strategy as not supported\n strategies[_addr].isSupported = false;\n\n // Withdraw all assets\n IStrategy strategy = IStrategy(_addr);\n strategy.withdrawAll();\n\n emit StrategyRemoved(_addr);\n }\n }\n\n /***************************************\n Strategies\n ****************************************/\n\n /**\n * @notice Deposit multiple assets from the vault into the strategy.\n * @param _strategyToAddress Address of the Strategy to deposit assets into.\n * @param _assets Array of asset address that will be deposited into the strategy.\n * @param _amounts Array of amounts of each corresponding asset to deposit.\n */\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external onlyGovernorOrStrategist nonReentrant {\n _depositToStrategy(_strategyToAddress, _assets, _amounts);\n }\n\n function _depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal virtual {\n require(\n strategies[_strategyToAddress].isSupported,\n \"Invalid to Strategy\"\n );\n require(_assets.length == _amounts.length, \"Parameter length mismatch\");\n\n uint256 assetCount = _assets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n address assetAddr = _assets[i];\n require(\n IStrategy(_strategyToAddress).supportsAsset(assetAddr),\n \"Asset unsupported\"\n );\n // Send required amount of funds to the strategy\n IERC20(assetAddr).safeTransfer(_strategyToAddress, _amounts[i]);\n }\n\n // Deposit all the funds that have been sent to the strategy\n IStrategy(_strategyToAddress).depositAll();\n }\n\n /**\n * @notice Withdraw multiple assets from the strategy to the vault.\n * @param _strategyFromAddress Address of the Strategy to withdraw assets from.\n * @param _assets Array of asset address that will be withdrawn from the strategy.\n * @param _amounts Array of amounts of each corresponding asset to withdraw.\n */\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external onlyGovernorOrStrategist nonReentrant {\n _withdrawFromStrategy(\n address(this),\n _strategyFromAddress,\n _assets,\n _amounts\n );\n }\n\n /**\n * @param _recipient can either be a strategy or the Vault\n */\n function _withdrawFromStrategy(\n address _recipient,\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal virtual {\n require(\n strategies[_strategyFromAddress].isSupported,\n \"Invalid from Strategy\"\n );\n require(_assets.length == _amounts.length, \"Parameter length mismatch\");\n\n uint256 assetCount = _assets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n // Withdraw from Strategy to the recipient\n IStrategy(_strategyFromAddress).withdraw(\n _recipient,\n _assets[i],\n _amounts[i]\n );\n }\n }\n\n /**\n * @notice Sets the maximum allowable difference between\n * total supply and backing assets' value.\n */\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external onlyGovernor {\n maxSupplyDiff = _maxSupplyDiff;\n emit MaxSupplyDiffChanged(_maxSupplyDiff);\n }\n\n /**\n * @notice Sets the trusteeAddress that can receive a portion of yield.\n * Setting to the zero address disables this feature.\n */\n function setTrusteeAddress(address _address) external onlyGovernor {\n trusteeAddress = _address;\n emit TrusteeAddressChanged(_address);\n }\n\n /**\n * @notice Sets the TrusteeFeeBps to the percentage of yield that should be\n * received in basis points.\n */\n function setTrusteeFeeBps(uint256 _basis) external onlyGovernor {\n require(_basis <= 5000, \"basis cannot exceed 50%\");\n trusteeFeeBps = _basis;\n emit TrusteeFeeBpsChanged(_basis);\n }\n\n /**\n * @notice Set OToken Metapool strategy\n * @param _ousdMetaStrategy Address of OToken metapool strategy\n */\n function setOusdMetaStrategy(address _ousdMetaStrategy)\n external\n onlyGovernor\n {\n ousdMetaStrategy = _ousdMetaStrategy;\n emit OusdMetaStrategyUpdated(_ousdMetaStrategy);\n }\n\n /***************************************\n Pause\n ****************************************/\n\n /**\n * @notice Set the deposit paused flag to true to prevent rebasing.\n */\n function pauseRebase() external onlyGovernorOrStrategist {\n rebasePaused = true;\n emit RebasePaused();\n }\n\n /**\n * @notice Set the deposit paused flag to true to allow rebasing.\n */\n function unpauseRebase() external onlyGovernorOrStrategist {\n rebasePaused = false;\n emit RebaseUnpaused();\n }\n\n /**\n * @notice Set the deposit paused flag to true to prevent capital movement.\n */\n function pauseCapital() external onlyGovernorOrStrategist {\n capitalPaused = true;\n emit CapitalPaused();\n }\n\n /**\n * @notice Set the deposit paused flag to false to enable capital movement.\n */\n function unpauseCapital() external onlyGovernorOrStrategist {\n capitalPaused = false;\n emit CapitalUnpaused();\n }\n\n /***************************************\n Utils\n ****************************************/\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Only unsupported assets\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /***************************************\n Strategies Admin\n ****************************************/\n\n /**\n * @notice Withdraws all assets from the strategy and sends assets to the Vault.\n * @param _strategyAddr Strategy address.\n */\n function withdrawAllFromStrategy(address _strategyAddr)\n external\n onlyGovernorOrStrategist\n {\n _withdrawAllFromStrategy(_strategyAddr);\n }\n\n function _withdrawAllFromStrategy(address _strategyAddr) internal virtual {\n require(\n strategies[_strategyAddr].isSupported,\n \"Strategy is not supported\"\n );\n IStrategy strategy = IStrategy(_strategyAddr);\n strategy.withdrawAll();\n }\n\n /**\n * @notice Withdraws all assets from all the strategies and sends assets to the Vault.\n */\n function withdrawAllFromStrategies() external onlyGovernorOrStrategist {\n _withdrawAllFromStrategies();\n }\n\n function _withdrawAllFromStrategies() internal virtual {\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n IStrategy(allStrategies[i]).withdrawAll();\n }\n }\n\n /***************************************\n Utils\n ****************************************/\n\n function _cacheDecimals(address token) internal {\n Asset storage tokenAsset = assets[token];\n if (tokenAsset.decimals != 0) {\n return;\n }\n uint8 decimals = IBasicToken(token).decimals();\n require(decimals >= 6 && decimals <= 18, \"Unexpected precision\");\n tokenAsset.decimals = decimals;\n }\n}\n" + }, + "contracts/vault/VaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultCore contract\n * @notice The Vault contract stores assets. On a deposit, OTokens will be minted\n and sent to the depositor. On a withdrawal, OTokens will be burned and\n assets will be sent to the withdrawer. The Vault accepts deposits of\n interest from yield bearing strategies which will modify the supply\n of OTokens.\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IGetExchangeRateToken } from \"../interfaces/IGetExchangeRateToken.sol\";\n\nimport \"./VaultInitializer.sol\";\n\ncontract VaultCore is VaultInitializer {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n /// @dev max signed int\n uint256 internal constant MAX_INT = uint256(type(int256).max);\n\n /**\n * @dev Verifies that the rebasing is not paused.\n */\n modifier whenNotRebasePaused() {\n require(!rebasePaused, \"Rebasing paused\");\n _;\n }\n\n /**\n * @dev Verifies that the deposits are not paused.\n */\n modifier whenNotCapitalPaused() {\n require(!capitalPaused, \"Capital paused\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the AMO strategy.\n */\n modifier onlyOusdMetaStrategy() {\n require(\n msg.sender == ousdMetaStrategy,\n \"Caller is not the OUSD meta strategy\"\n );\n _;\n }\n\n /**\n * @notice Deposit a supported asset and mint OTokens.\n * @param _asset Address of the asset being deposited\n * @param _amount Amount of the asset being deposited\n * @param _minimumOusdAmount Minimum OTokens to mint\n */\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external whenNotCapitalPaused nonReentrant {\n _mint(_asset, _amount, _minimumOusdAmount);\n }\n\n /**\n * @dev Deposit a supported asset and mint OTokens.\n * @param _asset Address of the asset being deposited\n * @param _amount Amount of the asset being deposited\n * @param _minimumOusdAmount Minimum OTokens to mint\n */\n function _mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) internal virtual {\n require(assets[_asset].isSupported, \"Asset is not supported\");\n require(_amount > 0, \"Amount must be greater than 0\");\n\n uint256 units = _toUnits(_amount, _asset);\n uint256 unitPrice = _toUnitPrice(_asset, true);\n uint256 priceAdjustedDeposit = (units * unitPrice) / 1e18;\n\n if (_minimumOusdAmount > 0) {\n require(\n priceAdjustedDeposit >= _minimumOusdAmount,\n \"Mint amount lower than minimum\"\n );\n }\n\n emit Mint(msg.sender, priceAdjustedDeposit);\n\n // Rebase must happen before any transfers occur.\n if (priceAdjustedDeposit >= rebaseThreshold && !rebasePaused) {\n _rebase();\n }\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, priceAdjustedDeposit);\n\n // Transfer the deposited coins to the vault\n IERC20 asset = IERC20(_asset);\n asset.safeTransferFrom(msg.sender, address(this), _amount);\n\n if (priceAdjustedDeposit >= autoAllocateThreshold) {\n _allocate();\n }\n }\n\n /**\n * @notice Mint OTokens for a Metapool Strategy\n * @param _amount Amount of the asset being deposited\n *\n * Notice: can't use `nonReentrant` modifier since the `mint` function can\n * call `allocate`, and that can trigger `ConvexOUSDMetaStrategy` to call this function\n * while the execution of the `mint` has not yet completed -> causing a `nonReentrant` collision.\n *\n * Also important to understand is that this is a limitation imposed by the test suite.\n * Production / mainnet contracts should never be configured in a way where mint/redeem functions\n * that are moving funds between the Vault and end user wallets can influence strategies\n * utilizing this function.\n */\n function mintForStrategy(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n onlyOusdMetaStrategy\n {\n require(_amount < MAX_INT, \"Amount too high\");\n\n emit Mint(msg.sender, _amount);\n\n // safe to cast because of the require check at the beginning of the function\n netOusdMintedForStrategy += int256(_amount);\n\n require(\n abs(netOusdMintedForStrategy) < netOusdMintForStrategyThreshold,\n \"Minted ousd surpassed netOusdMintForStrategyThreshold.\"\n );\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, _amount);\n }\n\n // In memoriam\n\n /**\n * @notice Withdraw a supported asset and burn OTokens.\n * @param _amount Amount of OTokens to burn\n * @param _minimumUnitAmount Minimum stablecoin units to receive in return\n */\n function redeem(uint256 _amount, uint256 _minimumUnitAmount)\n external\n whenNotCapitalPaused\n nonReentrant\n {\n _redeem(_amount, _minimumUnitAmount);\n }\n\n /**\n * @notice Withdraw a supported asset and burn OTokens.\n * @param _amount Amount of OTokens to burn\n * @param _minimumUnitAmount Minimum stablecoin units to receive in return\n */\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n {\n // Calculate redemption outputs\n uint256[] memory outputs = _calculateRedeemOutputs(_amount);\n\n emit Redeem(msg.sender, _amount);\n\n // Send outputs\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n if (outputs[i] == 0) continue;\n\n address assetAddr = allAssets[i];\n\n if (IERC20(assetAddr).balanceOf(address(this)) >= outputs[i]) {\n // Use Vault funds first if sufficient\n IERC20(assetAddr).safeTransfer(msg.sender, outputs[i]);\n } else {\n address strategyAddr = assetDefaultStrategies[assetAddr];\n if (strategyAddr != address(0)) {\n // Nothing in Vault, but something in Strategy, send from there\n IStrategy strategy = IStrategy(strategyAddr);\n strategy.withdraw(msg.sender, assetAddr, outputs[i]);\n } else {\n // Cant find funds anywhere\n revert(\"Liquidity error\");\n }\n }\n }\n\n if (_minimumUnitAmount > 0) {\n uint256 unitTotal = 0;\n for (uint256 i = 0; i < outputs.length; ++i) {\n unitTotal += _toUnits(outputs[i], allAssets[i]);\n }\n require(\n unitTotal >= _minimumUnitAmount,\n \"Redeem amount lower than minimum\"\n );\n }\n\n oUSD.burn(msg.sender, _amount);\n\n _postRedeem(_amount);\n }\n\n function _postRedeem(uint256 _amount) internal {\n // Until we can prove that we won't affect the prices of our assets\n // by withdrawing them, this should be here.\n // It's possible that a strategy was off on its asset total, perhaps\n // a reward token sold for more or for less than anticipated.\n uint256 totalUnits = 0;\n if (_amount >= rebaseThreshold && !rebasePaused) {\n totalUnits = _rebase();\n } else {\n totalUnits = _totalValue();\n }\n\n // Check that the OTokens are backed by enough assets\n if (maxSupplyDiff > 0) {\n // If there are more outstanding withdrawal requests than assets in the vault and strategies\n // then the available assets will be negative and totalUnits will be rounded up to zero.\n // As we don't know the exact shortfall amount, we will reject all redeem and withdrawals\n require(totalUnits > 0, \"Too many outstanding requests\");\n\n // Allow a max difference of maxSupplyDiff% between\n // backing assets value and OUSD total supply\n uint256 diff = oUSD.totalSupply().divPrecisely(totalUnits);\n require(\n (diff > 1e18 ? diff - 1e18 : 1e18 - diff) <= maxSupplyDiff,\n \"Backing supply liquidity error\"\n );\n }\n }\n\n /**\n * @notice Burn OTokens for Metapool Strategy\n * @param _amount Amount of OUSD to burn\n *\n * @dev Notice: can't use `nonReentrant` modifier since the `redeem` function could\n * require withdrawal on `ConvexOUSDMetaStrategy` and that one can call `burnForStrategy`\n * while the execution of the `redeem` has not yet completed -> causing a `nonReentrant` collision.\n *\n * Also important to understand is that this is a limitation imposed by the test suite.\n * Production / mainnet contracts should never be configured in a way where mint/redeem functions\n * that are moving funds between the Vault and end user wallets can influence strategies\n * utilizing this function.\n */\n function burnForStrategy(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n onlyOusdMetaStrategy\n {\n require(_amount < MAX_INT, \"Amount too high\");\n\n emit Redeem(msg.sender, _amount);\n\n // safe to cast because of the require check at the beginning of the function\n netOusdMintedForStrategy -= int256(_amount);\n\n require(\n abs(netOusdMintedForStrategy) < netOusdMintForStrategyThreshold,\n \"Attempting to burn too much OUSD.\"\n );\n\n // Burn OTokens\n oUSD.burn(msg.sender, _amount);\n }\n\n /**\n * @notice Allocate unallocated funds on Vault to strategies.\n **/\n function allocate() external virtual whenNotCapitalPaused nonReentrant {\n _allocate();\n }\n\n /**\n * @dev Allocate unallocated funds on Vault to strategies.\n **/\n function _allocate() internal virtual {\n uint256 vaultValue = _totalValueInVault();\n // Nothing in vault to allocate\n if (vaultValue == 0) return;\n uint256 strategiesValue = _totalValueInStrategies();\n // We have a method that does the same as this, gas optimisation\n uint256 calculatedTotalValue = vaultValue + strategiesValue;\n\n // We want to maintain a buffer on the Vault so calculate a percentage\n // modifier to multiply each amount being allocated by to enforce the\n // vault buffer\n uint256 vaultBufferModifier;\n if (strategiesValue == 0) {\n // Nothing in Strategies, allocate 100% minus the vault buffer to\n // strategies\n vaultBufferModifier = uint256(1e18) - vaultBuffer;\n } else {\n vaultBufferModifier =\n (vaultBuffer * calculatedTotalValue) /\n vaultValue;\n if (1e18 > vaultBufferModifier) {\n // E.g. 1e18 - (1e17 * 10e18)/5e18 = 8e17\n // (5e18 * 8e17) / 1e18 = 4e18 allocated from Vault\n vaultBufferModifier = uint256(1e18) - vaultBufferModifier;\n } else {\n // We need to let the buffer fill\n return;\n }\n }\n if (vaultBufferModifier == 0) return;\n\n // Iterate over all assets in the Vault and allocate to the appropriate\n // strategy\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(allAssets[i]);\n uint256 assetBalance = asset.balanceOf(address(this));\n // No balance, nothing to do here\n if (assetBalance == 0) continue;\n\n // Multiply the balance by the vault buffer modifier and truncate\n // to the scale of the asset decimals\n uint256 allocateAmount = assetBalance.mulTruncate(\n vaultBufferModifier\n );\n\n address depositStrategyAddr = assetDefaultStrategies[\n address(asset)\n ];\n\n if (depositStrategyAddr != address(0) && allocateAmount > 0) {\n IStrategy strategy = IStrategy(depositStrategyAddr);\n // Transfer asset to Strategy and call deposit method to\n // mint or take required action\n asset.safeTransfer(address(strategy), allocateAmount);\n strategy.deposit(address(asset), allocateAmount);\n emit AssetAllocated(\n address(asset),\n depositStrategyAddr,\n allocateAmount\n );\n }\n }\n }\n\n /**\n * @notice Calculate the total value of assets held by the Vault and all\n * strategies and update the supply of OTokens.\n */\n function rebase() external virtual nonReentrant {\n _rebase();\n }\n\n /**\n * @dev Calculate the total value of assets held by the Vault and all\n * strategies and update the supply of OTokens, optionally sending a\n * portion of the yield to the trustee.\n * @return totalUnits Total balance of Vault in units\n */\n function _rebase() internal whenNotRebasePaused returns (uint256) {\n uint256 supply = oUSD.totalSupply();\n uint256 vaultValue = _totalValue();\n // If no supply yet, do not rebase\n if (supply == 0) {\n return vaultValue;\n }\n\n // Calculate yield and new supply\n (uint256 yield, uint256 targetRate) = _nextYield(supply, vaultValue);\n uint256 newSupply = supply + yield;\n // Only rebase upwards and if we have enough backing funds\n if (newSupply <= supply || newSupply > vaultValue) {\n return vaultValue;\n }\n\n rebasePerSecondTarget = uint64(_min(targetRate, type(uint64).max));\n lastRebase = uint64(block.timestamp); // Intentional cast\n\n // Fee collection on yield\n address _trusteeAddress = trusteeAddress; // gas savings\n uint256 fee = 0;\n if (_trusteeAddress != address(0)) {\n fee = (yield * trusteeFeeBps) / 1e4;\n if (fee > 0) {\n require(fee < yield, \"Fee must not be greater than yield\");\n oUSD.mint(_trusteeAddress, fee);\n }\n }\n emit YieldDistribution(_trusteeAddress, yield, fee);\n\n // Only ratchet OToken supply upwards\n // Final check uses latest totalSupply\n if (newSupply > oUSD.totalSupply()) {\n oUSD.changeSupply(newSupply);\n }\n return vaultValue;\n }\n\n /**\n * @notice Calculates the amount that would rebase at next rebase.\n * This is before any fees.\n * @return yield amount of expected yield\n */\n function previewYield() external view returns (uint256 yield) {\n (yield, ) = _nextYield(oUSD.totalSupply(), _totalValue());\n return yield;\n }\n\n function _nextYield(uint256 supply, uint256 vaultValue)\n internal\n view\n virtual\n returns (uint256 yield, uint256 targetRate)\n {\n uint256 nonRebasing = oUSD.nonRebasingSupply();\n uint256 rebasing = supply - nonRebasing;\n uint256 elapsed = block.timestamp - lastRebase;\n targetRate = rebasePerSecondTarget;\n\n if (\n elapsed == 0 || // Yield only once per block.\n rebasing == 0 || // No yield if there are no rebasing tokens to give it to.\n supply > vaultValue || // No yield if we do not have yield to give.\n block.timestamp >= type(uint64).max // No yield if we are too far in the future to calculate it correctly.\n ) {\n return (0, targetRate);\n }\n\n // Start with the full difference available\n yield = vaultValue - supply;\n\n // Cap via optional automatic duration smoothing\n uint256 _dripDuration = dripDuration;\n if (_dripDuration > 1) {\n // If we are able to sustain an increased drip rate for\n // double the duration, then increase the target drip rate\n targetRate = _max(targetRate, yield / (_dripDuration * 2));\n // If we cannot sustain the target rate any more,\n // then rebase what we can, and reduce the target\n targetRate = _min(targetRate, yield / _dripDuration);\n // drip at the new target rate\n yield = _min(yield, targetRate * elapsed);\n }\n\n // Cap per second. elapsed is not 1e18 denominated\n yield = _min(yield, (rebasing * elapsed * rebasePerSecondMax) / 1e18);\n\n // Cap at a hard max per rebase, to avoid long durations resulting in huge rebases\n yield = _min(yield, (rebasing * MAX_REBASE) / 1e18);\n\n return (yield, targetRate);\n }\n\n /**\n * @notice Determine the total value of assets held by the vault and its\n * strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function totalValue() external view virtual returns (uint256 value) {\n value = _totalValue();\n }\n\n /**\n * @dev Internal Calculate the total value of the assets held by the\n * vault and its strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValue() internal view virtual returns (uint256 value) {\n return _totalValueInVault() + _totalValueInStrategies();\n }\n\n /**\n * @dev Internal to calculate total value of all assets held in Vault.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInVault()\n internal\n view\n virtual\n returns (uint256 value)\n {\n uint256 assetCount = allAssets.length;\n for (uint256 y; y < assetCount; ++y) {\n address assetAddr = allAssets[y];\n uint256 balance = IERC20(assetAddr).balanceOf(address(this));\n if (balance > 0) {\n value += _toUnits(balance, assetAddr);\n }\n }\n }\n\n /**\n * @dev Internal to calculate total value of all assets held in Strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInStrategies() internal view returns (uint256 value) {\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n value = value + _totalValueInStrategy(allStrategies[i]);\n }\n }\n\n /**\n * @dev Internal to calculate total value of all assets held by strategy.\n * @param _strategyAddr Address of the strategy\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInStrategy(address _strategyAddr)\n internal\n view\n returns (uint256 value)\n {\n IStrategy strategy = IStrategy(_strategyAddr);\n uint256 assetCount = allAssets.length;\n for (uint256 y; y < assetCount; ++y) {\n address assetAddr = allAssets[y];\n if (strategy.supportsAsset(assetAddr)) {\n uint256 balance = strategy.checkBalance(assetAddr);\n if (balance > 0) {\n value += _toUnits(balance, assetAddr);\n }\n }\n }\n }\n\n /**\n * @notice Get the balance of an asset held in Vault and all strategies.\n * @param _asset Address of asset\n * @return uint256 Balance of asset in decimals of asset\n */\n function checkBalance(address _asset) external view returns (uint256) {\n return _checkBalance(_asset);\n }\n\n /**\n * @notice Get the balance of an asset held in Vault and all strategies.\n * @param _asset Address of asset\n * @return balance Balance of asset in decimals of asset\n */\n function _checkBalance(address _asset)\n internal\n view\n virtual\n returns (uint256 balance)\n {\n IERC20 asset = IERC20(_asset);\n balance = asset.balanceOf(address(this));\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n IStrategy strategy = IStrategy(allStrategies[i]);\n if (strategy.supportsAsset(_asset)) {\n balance = balance + strategy.checkBalance(_asset);\n }\n }\n }\n\n /**\n * @notice Calculate the outputs for a redeem function, i.e. the mix of\n * coins that will be returned\n */\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory)\n {\n return _calculateRedeemOutputs(_amount);\n }\n\n /**\n * @dev Calculate the outputs for a redeem function, i.e. the mix of\n * coins that will be returned.\n * @return outputs Array of amounts respective to the supported assets\n */\n function _calculateRedeemOutputs(uint256 _amount)\n internal\n view\n virtual\n returns (uint256[] memory outputs)\n {\n // We always give out coins in proportion to how many we have,\n // Now if all coins were the same value, this math would easy,\n // just take the percentage of each coin, and multiply by the\n // value to be given out. But if coins are worth more than $1,\n // then we would end up handing out too many coins. We need to\n // adjust by the total value of coins.\n //\n // To do this, we total up the value of our coins, by their\n // percentages. Then divide what we would otherwise give out by\n // this number.\n //\n // Let say we have 100 DAI at $1.06 and 200 USDT at $1.00.\n // So for every 1 DAI we give out, we'll be handing out 2 USDT\n // Our total output ratio is: 33% * 1.06 + 66% * 1.00 = 1.02\n //\n // So when calculating the output, we take the percentage of\n // each coin, times the desired output value, divided by the\n // totalOutputRatio.\n //\n // For example, withdrawing: 30 OUSD:\n // DAI 33% * 30 / 1.02 = 9.80 DAI\n // USDT = 66 % * 30 / 1.02 = 19.60 USDT\n //\n // Checking these numbers:\n // 9.80 DAI * 1.06 = $10.40\n // 19.60 USDT * 1.00 = $19.60\n //\n // And so the user gets $10.40 + $19.60 = $30 worth of value.\n\n uint256 assetCount = allAssets.length;\n uint256[] memory assetUnits = new uint256[](assetCount);\n uint256[] memory assetBalances = new uint256[](assetCount);\n outputs = new uint256[](assetCount);\n\n // Calculate redeem fee\n if (redeemFeeBps > 0) {\n uint256 redeemFee = _amount.mulTruncateScale(redeemFeeBps, 1e4);\n _amount = _amount - redeemFee;\n }\n\n // Calculate assets balances and decimals once,\n // for a large gas savings.\n uint256 totalUnits = 0;\n for (uint256 i = 0; i < assetCount; ++i) {\n address assetAddr = allAssets[i];\n uint256 balance = _checkBalance(assetAddr);\n assetBalances[i] = balance;\n assetUnits[i] = _toUnits(balance, assetAddr);\n totalUnits = totalUnits + assetUnits[i];\n }\n // Calculate totalOutputRatio\n uint256 totalOutputRatio = 0;\n for (uint256 i = 0; i < assetCount; ++i) {\n uint256 unitPrice = _toUnitPrice(allAssets[i], false);\n uint256 ratio = (assetUnits[i] * unitPrice) / totalUnits;\n totalOutputRatio = totalOutputRatio + ratio;\n }\n // Calculate final outputs\n uint256 factor = _amount.divPrecisely(totalOutputRatio);\n for (uint256 i = 0; i < assetCount; ++i) {\n outputs[i] = (assetBalances[i] * factor) / totalUnits;\n }\n }\n\n /***************************************\n Pricing\n ****************************************/\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * Never goes above 1, since that is how we price mints.\n * @param asset address of the asset\n * @return price uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed\n */\n function priceUnitMint(address asset)\n external\n view\n returns (uint256 price)\n {\n /* need to supply 1 asset unit in asset's decimals and can not just hard-code\n * to 1e18 and ignore calling `_toUnits` since we need to consider assets\n * with the exchange rate\n */\n uint256 units = _toUnits(\n uint256(1e18).scaleBy(_getDecimals(asset), 18),\n asset\n );\n price = (_toUnitPrice(asset, true) * units) / 1e18;\n }\n\n /**\n * @notice Returns the total price in 18 digit unit for a given asset.\n * Never goes below 1, since that is how we price redeems\n * @param asset Address of the asset\n * @return price uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed\n */\n function priceUnitRedeem(address asset)\n external\n view\n returns (uint256 price)\n {\n /* need to supply 1 asset unit in asset's decimals and can not just hard-code\n * to 1e18 and ignore calling `_toUnits` since we need to consider assets\n * with the exchange rate\n */\n uint256 units = _toUnits(\n uint256(1e18).scaleBy(_getDecimals(asset), 18),\n asset\n );\n price = (_toUnitPrice(asset, false) * units) / 1e18;\n }\n\n /***************************************\n Utils\n ****************************************/\n\n /**\n * @dev Convert a quantity of a token into 1e18 fixed decimal \"units\"\n * in the underlying base (USD/ETH) used by the vault.\n * Price is not taken into account, only quantity.\n *\n * Examples of this conversion:\n *\n * - 1e18 DAI becomes 1e18 units (same decimals)\n * - 1e6 USDC becomes 1e18 units (decimal conversion)\n * - 1e18 rETH becomes 1.2e18 units (exchange rate conversion)\n *\n * @param _raw Quantity of asset\n * @param _asset Core Asset address\n * @return value 1e18 normalized quantity of units\n */\n function _toUnits(uint256 _raw, address _asset)\n internal\n view\n returns (uint256)\n {\n UnitConversion conversion = assets[_asset].unitConversion;\n if (conversion == UnitConversion.DECIMALS) {\n return _raw.scaleBy(18, _getDecimals(_asset));\n } else if (conversion == UnitConversion.GETEXCHANGERATE) {\n uint256 exchangeRate = IGetExchangeRateToken(_asset)\n .getExchangeRate();\n return (_raw * exchangeRate) / 1e18;\n } else {\n revert(\"Unsupported conversion type\");\n }\n }\n\n /**\n * @dev Returns asset's unit price accounting for different asset types\n * and takes into account the context in which that price exists -\n * - mint or redeem.\n *\n * Note: since we are returning the price of the unit and not the one of the\n * asset (see comment above how 1 rETH exchanges for 1.2 units) we need\n * to make the Oracle price adjustment as well since we are pricing the\n * units and not the assets.\n *\n * The price also snaps to a \"full unit price\" in case a mint or redeem\n * action would be unfavourable to the protocol.\n *\n */\n function _toUnitPrice(address _asset, bool isMint)\n internal\n view\n returns (uint256 price)\n {\n UnitConversion conversion = assets[_asset].unitConversion;\n price = IOracle(priceProvider).price(_asset);\n\n if (conversion == UnitConversion.GETEXCHANGERATE) {\n uint256 exchangeRate = IGetExchangeRateToken(_asset)\n .getExchangeRate();\n price = (price * 1e18) / exchangeRate;\n } else if (conversion != UnitConversion.DECIMALS) {\n revert(\"Unsupported conversion type\");\n }\n\n /* At this stage the price is already adjusted to the unit\n * so the price checks are agnostic to underlying asset being\n * pegged to a USD or to an ETH or having a custom exchange rate.\n */\n require(price <= MAX_UNIT_PRICE_DRIFT, \"Vault: Price exceeds max\");\n require(price >= MIN_UNIT_PRICE_DRIFT, \"Vault: Price under min\");\n\n if (isMint) {\n /* Never price a normalized unit price for more than one\n * unit of OETH/OUSD when minting.\n */\n if (price > 1e18) {\n price = 1e18;\n }\n require(price >= MINT_MINIMUM_UNIT_PRICE, \"Asset price below peg\");\n } else {\n /* Never give out more than 1 normalized unit amount of assets\n * for one unit of OETH/OUSD when redeeming.\n */\n if (price < 1e18) {\n price = 1e18;\n }\n }\n }\n\n /**\n * @dev Get the number of decimals of a token asset\n * @param _asset Address of the asset\n * @return decimals number of decimals\n */\n function _getDecimals(address _asset)\n internal\n view\n returns (uint256 decimals)\n {\n decimals = assets[_asset].decimals;\n require(decimals > 0, \"Decimals not cached\");\n }\n\n /**\n * @notice Return the number of assets supported by the Vault.\n */\n function getAssetCount() public view returns (uint256) {\n return allAssets.length;\n }\n\n /**\n * @notice Gets the vault configuration of a supported asset.\n * @param _asset Address of the token asset\n */\n function getAssetConfig(address _asset)\n public\n view\n returns (Asset memory config)\n {\n config = assets[_asset];\n }\n\n /**\n * @notice Return all vault asset addresses in order\n */\n function getAllAssets() external view returns (address[] memory) {\n return allAssets;\n }\n\n /**\n * @notice Return the number of strategies active on the Vault.\n */\n function getStrategyCount() external view returns (uint256) {\n return allStrategies.length;\n }\n\n /**\n * @notice Return the array of all strategies\n */\n function getAllStrategies() external view returns (address[] memory) {\n return allStrategies;\n }\n\n /**\n * @notice Returns whether the vault supports the asset\n * @param _asset address of the asset\n * @return true if supported\n */\n function isSupportedAsset(address _asset) external view returns (bool) {\n return assets[_asset].isSupported;\n }\n\n function ADMIN_IMPLEMENTATION() external view returns (address adminImpl) {\n bytes32 slot = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n adminImpl := sload(slot)\n }\n }\n\n /**\n * @dev Falldown to the admin implementation\n * @notice This is a catch all for all functions not declared in core\n */\n // solhint-disable-next-line no-complex-fallback\n fallback() external {\n bytes32 slot = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(\n gas(),\n sload(slot),\n 0,\n calldatasize(),\n 0,\n 0\n )\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n function abs(int256 x) private pure returns (uint256) {\n require(x < int256(MAX_INT), \"Amount too high\");\n return x >= 0 ? uint256(x) : uint256(-x);\n }\n\n function _min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n function _max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a > b ? a : b;\n }\n}\n" + }, + "contracts/vault/VaultInitializer.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultInitializer contract\n * @notice The Vault contract initializes the vault.\n * @author Origin Protocol Inc\n */\n\nimport \"./VaultStorage.sol\";\n\ncontract VaultInitializer is VaultStorage {\n function initialize(address _priceProvider, address _oToken)\n external\n onlyGovernor\n initializer\n {\n require(_priceProvider != address(0), \"PriceProvider address is zero\");\n require(_oToken != address(0), \"oToken address is zero\");\n\n oUSD = OUSD(_oToken);\n\n priceProvider = _priceProvider;\n\n rebasePaused = false;\n capitalPaused = true;\n\n // Initial redeem fee of 0 basis points\n redeemFeeBps = 0;\n // Initial Vault buffer of 0%\n vaultBuffer = 0;\n // Initial allocate threshold of 25,000 OUSD\n autoAllocateThreshold = 25000e18;\n // Threshold for rebasing\n rebaseThreshold = 1000e18;\n // Initialize all strategies\n allStrategies = new address[](0);\n // Start with drip duration disabled\n dripDuration = 1;\n }\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + }, + "contracts/zapper/WOETHCCIPZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IRouterClient } from \"@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol\";\nimport { Client } from \"@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol\";\n// solhint-disable-next-line max-line-length\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"./../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IOETHZapper } from \"./../interfaces/IOETHZapper.sol\";\n\n/**\n * @title WOETH CCIP Zapper Contract\n * @notice Helps to directly convert ETH on mainnet into WOETH on L2s.\n * @author Origin Protocol Inc\n */\n\ncontract WOETHCCIPZapper {\n /**\n * @dev Event emitted when a zap occurs\n * @param messageId Unique message identifier for each zap\n * @param sender Address initiating the zap\n * @param recipient Recipient address at destination chain\n * @param amount Amount of ETH zapped\n */\n event Zap(\n bytes32 indexed messageId,\n address sender,\n address recipient,\n uint256 amount\n );\n\n // @dev Thrown when Zap amount is less than fee.\n error AmountLessThanFee();\n\n /**\n * @dev The destination chain selector\n */\n uint64 public immutable destinationChainSelector;\n\n /**\n * @dev The WOETH source chain (Mainnet)\n */\n IERC4626 public immutable woethOnSourceChain;\n\n /**\n * @dev The WOETH destination chain (Arbitrum)\n */\n IERC20 public immutable woethOnDestinationChain;\n\n /**\n * @dev The OETH zapper contract address\n */\n IOETHZapper public immutable oethZapper;\n\n /**\n * @dev The CCIP router contract address\n */\n IRouterClient public immutable ccipRouter;\n\n /**\n * @dev The OETH token contract address\n */\n IERC20 public immutable oeth;\n\n constructor(\n address _ccipRouter,\n uint64 _destinationChainSelector,\n IERC4626 _woethOnSourceChain,\n IERC20 _woethOnDestinationChain,\n IOETHZapper _oethZapper,\n IERC20 _oeth\n ) {\n ccipRouter = IRouterClient(_ccipRouter);\n destinationChainSelector = _destinationChainSelector;\n woethOnSourceChain = _woethOnSourceChain;\n woethOnDestinationChain = _woethOnDestinationChain;\n oethZapper = _oethZapper;\n oeth = _oeth;\n\n // Max allowance for Router and WOETH contracts\n _oeth.approve(address(_woethOnSourceChain), type(uint256).max); // for wrapping\n _woethOnSourceChain.approve(address(_ccipRouter), type(uint256).max); // for zapping\n }\n\n /**\n * @notice Accepts ETH, zaps for OETH, wraps it for WOETH and sends it to the destination chain (arbitrum)\n * @param receiver The address of the EOA on the destination chain\n * @return messageId The ID of the message that was sent\n */\n function zap(address receiver)\n external\n payable\n returns (bytes32 messageId)\n {\n return _zap(receiver, msg.value);\n }\n\n /**\n * @notice Used to estimate fee for CCIP transaction\n * @param amount The amount of ETH to be zapped\n * @param receiver The address of the EOA on the destination chain\n * @return feeAmount The CCIP tx fee in ETH.\n */\n\n function getFee(uint256 amount, address receiver)\n public\n view\n returns (uint256 feeAmount)\n {\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(woethOnSourceChain),\n amount: amount\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({\n receiver: abi.encode(receiver), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n feeAmount = ccipRouter.getFee(destinationChainSelector, message);\n }\n\n /**\n * @dev Deposit ETH and receive WOETH in L2.\n * @dev Note that the WOETH will be sent to the msg.sender at destination chain as well.\n */\n receive() external payable {\n _zap(msg.sender, msg.value);\n }\n\n function _zap(address receiver, uint256 amount)\n internal\n returns (bytes32 messageId)\n {\n // Estimate fee for zapping.\n uint256 feeAmount = getFee(amount, receiver);\n if (amount < feeAmount) {\n revert AmountLessThanFee();\n }\n\n // Convert only the msg.value - fees amount to WOETH.\n amount -= feeAmount;\n\n // 1.) Zap for OETH\n uint256 oethReceived = oethZapper.deposit{ value: amount }();\n\n // 2.) Wrap the received woeth\n uint256 woethReceived = woethOnSourceChain.deposit(\n oethReceived,\n address(this)\n );\n\n // 3.) Setup params for CCIP transfer\n\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(woethOnSourceChain),\n amount: woethReceived\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({\n receiver: abi.encode(receiver), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n // See: https://docs.chain.link/ccip/best-practices#setting-gaslimit\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n // ZAP ϟ\n //slither-disable-next-line arbitrary-send-eth\n messageId = ccipRouter.ccipSend{ value: feeAmount }(\n destinationChainSelector,\n message\n );\n\n // Emit Zap event with message details\n emit Zap(messageId, msg.sender, receiver, amount);\n\n // Return the message ID\n return messageId;\n }\n}\n" + }, + "hardhat/console.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.4.22 <0.9.0;\n\nlibrary console {\n address constant CONSOLE_ADDRESS =\n 0x000000000000000000636F6e736F6c652e6c6f67;\n\n function _sendLogPayloadImplementation(bytes memory payload) internal view {\n address consoleAddress = CONSOLE_ADDRESS;\n /// @solidity memory-safe-assembly\n assembly {\n pop(\n staticcall(\n gas(),\n consoleAddress,\n add(payload, 32),\n mload(payload),\n 0,\n 0\n )\n )\n }\n }\n\n function _castToPure(\n function(bytes memory) internal view fnIn\n ) internal pure returns (function(bytes memory) pure fnOut) {\n assembly {\n fnOut := fnIn\n }\n }\n\n function _sendLogPayload(bytes memory payload) internal pure {\n _castToPure(_sendLogPayloadImplementation)(payload);\n }\n\n function log() internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log()\"));\n }\n\n function logInt(int256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(int256)\", p0));\n }\n\n function logUint(uint256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256)\", p0));\n }\n\n function logString(string memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n }\n\n function logBool(bool p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n }\n\n function logAddress(address p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n }\n\n function logBytes(bytes memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes)\", p0));\n }\n\n function logBytes1(bytes1 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes1)\", p0));\n }\n\n function logBytes2(bytes2 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes2)\", p0));\n }\n\n function logBytes3(bytes3 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes3)\", p0));\n }\n\n function logBytes4(bytes4 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes4)\", p0));\n }\n\n function logBytes5(bytes5 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes5)\", p0));\n }\n\n function logBytes6(bytes6 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes6)\", p0));\n }\n\n function logBytes7(bytes7 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes7)\", p0));\n }\n\n function logBytes8(bytes8 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes8)\", p0));\n }\n\n function logBytes9(bytes9 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes9)\", p0));\n }\n\n function logBytes10(bytes10 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes10)\", p0));\n }\n\n function logBytes11(bytes11 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes11)\", p0));\n }\n\n function logBytes12(bytes12 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes12)\", p0));\n }\n\n function logBytes13(bytes13 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes13)\", p0));\n }\n\n function logBytes14(bytes14 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes14)\", p0));\n }\n\n function logBytes15(bytes15 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes15)\", p0));\n }\n\n function logBytes16(bytes16 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes16)\", p0));\n }\n\n function logBytes17(bytes17 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes17)\", p0));\n }\n\n function logBytes18(bytes18 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes18)\", p0));\n }\n\n function logBytes19(bytes19 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes19)\", p0));\n }\n\n function logBytes20(bytes20 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes20)\", p0));\n }\n\n function logBytes21(bytes21 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes21)\", p0));\n }\n\n function logBytes22(bytes22 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes22)\", p0));\n }\n\n function logBytes23(bytes23 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes23)\", p0));\n }\n\n function logBytes24(bytes24 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes24)\", p0));\n }\n\n function logBytes25(bytes25 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes25)\", p0));\n }\n\n function logBytes26(bytes26 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes26)\", p0));\n }\n\n function logBytes27(bytes27 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes27)\", p0));\n }\n\n function logBytes28(bytes28 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes28)\", p0));\n }\n\n function logBytes29(bytes29 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes29)\", p0));\n }\n\n function logBytes30(bytes30 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes30)\", p0));\n }\n\n function logBytes31(bytes31 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes31)\", p0));\n }\n\n function logBytes32(bytes32 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes32)\", p0));\n }\n\n function log(uint256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256)\", p0));\n }\n\n function log(string memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n }\n\n function log(bool p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n }\n\n function log(address p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n }\n\n function log(uint256 p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256)\", p0, p1));\n }\n\n function log(uint256 p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string)\", p0, p1));\n }\n\n function log(uint256 p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool)\", p0, p1));\n }\n\n function log(uint256 p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address)\", p0, p1));\n }\n\n function log(string memory p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256)\", p0, p1));\n }\n\n function log(string memory p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string)\", p0, p1));\n }\n\n function log(string memory p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool)\", p0, p1));\n }\n\n function log(string memory p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address)\", p0, p1));\n }\n\n function log(bool p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256)\", p0, p1));\n }\n\n function log(bool p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string)\", p0, p1));\n }\n\n function log(bool p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool)\", p0, p1));\n }\n\n function log(bool p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address)\", p0, p1));\n }\n\n function log(address p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256)\", p0, p1));\n }\n\n function log(address p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string)\", p0, p1));\n }\n\n function log(address p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool)\", p0, p1));\n }\n\n function log(address p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address)\", p0, p1));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,address)\", p0, p1, p2, p3));\n }\n}\n" + }, + "lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC4626 } from \"../../../../interfaces/IERC4626.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\n// From Open Zeppelin draft PR commit:\n// fac43034dca85ff539db3fc8aa2a7084b843d454\n// https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3171\n\nabstract contract ERC4626 is ERC20, IERC4626 {\n IERC20Metadata private immutable _asset;\n\n constructor(IERC20Metadata __asset) {\n _asset = __asset;\n }\n\n /** @dev See {IERC4262-asset} */\n function asset() public view virtual override returns (address) {\n return address(_asset);\n }\n\n /** @dev See {IERC4262-totalAssets} */\n function totalAssets() public view virtual override returns (uint256) {\n return _asset.balanceOf(address(this));\n }\n\n /**\n * @dev See {IERC4262-convertToShares}\n *\n * Will revert if asserts > 0, totalSupply > 0 and totalAssets = 0. That corresponds to a case where any asset\n * would represent an infinite amout of shares.\n */\n function convertToShares(uint256 assets) public view virtual override returns (uint256 shares) {\n uint256 supply = totalSupply();\n\n return\n (assets == 0 || supply == 0)\n ? (assets * 10**decimals()) / 10**_asset.decimals()\n : (assets * supply) / totalAssets();\n }\n\n /** @dev See {IERC4262-convertToAssets} */\n function convertToAssets(uint256 shares) public view virtual override returns (uint256 assets) {\n uint256 supply = totalSupply();\n\n return (supply == 0) ? (shares * 10**_asset.decimals()) / 10**decimals() : (shares * totalAssets()) / supply;\n }\n\n /** @dev See {IERC4262-maxDeposit} */\n function maxDeposit(address) public view virtual override returns (uint256) {\n return type(uint256).max;\n }\n\n /** @dev See {IERC4262-maxMint} */\n function maxMint(address) public view virtual override returns (uint256) {\n return type(uint256).max;\n }\n\n /** @dev See {IERC4262-maxWithdraw} */\n function maxWithdraw(address owner) public view virtual override returns (uint256) {\n return convertToAssets(balanceOf(owner));\n }\n\n /** @dev See {IERC4262-maxRedeem} */\n function maxRedeem(address owner) public view virtual override returns (uint256) {\n return balanceOf(owner);\n }\n\n /** @dev See {IERC4262-previewDeposit} */\n function previewDeposit(uint256 assets) public view virtual override returns (uint256) {\n return convertToShares(assets);\n }\n\n /** @dev See {IERC4262-previewMint} */\n function previewMint(uint256 shares) public view virtual override returns (uint256) {\n uint256 assets = convertToAssets(shares);\n return assets + (convertToShares(assets) < shares ? 1 : 0);\n }\n\n /** @dev See {IERC4262-previewWithdraw} */\n function previewWithdraw(uint256 assets) public view virtual override returns (uint256) {\n uint256 shares = convertToShares(assets);\n return shares + (convertToAssets(shares) < assets ? 1 : 0);\n }\n\n /** @dev See {IERC4262-previewRedeem} */\n function previewRedeem(uint256 shares) public view virtual override returns (uint256) {\n return convertToAssets(shares);\n }\n\n /** @dev See {IERC4262-deposit} */\n function deposit(uint256 assets, address receiver) public virtual override returns (uint256) {\n require(assets <= maxDeposit(receiver), \"ERC4626: deposit more then max\");\n\n address caller = _msgSender();\n uint256 shares = previewDeposit(assets);\n\n // if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through\n // the tokensToSend hook, so we need to transfer before we mint to keep the invariants.\n SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);\n _mint(receiver, shares);\n\n emit Deposit(caller, receiver, assets, shares);\n\n return shares;\n }\n\n /** @dev See {IERC4262-mint} */\n function mint(uint256 shares, address receiver) public virtual override returns (uint256) {\n require(shares <= maxMint(receiver), \"ERC4626: mint more then max\");\n\n address caller = _msgSender();\n uint256 assets = previewMint(shares);\n\n // if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through\n // the tokensToSend hook, so we need to transfer before we mint to keep the invariants.\n SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);\n _mint(receiver, shares);\n\n emit Deposit(caller, receiver, assets, shares);\n\n return assets;\n }\n\n /** @dev See {IERC4262-withdraw} */\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) public virtual override returns (uint256) {\n require(assets <= maxWithdraw(owner), \"ERC4626: withdraw more then max\");\n\n address caller = _msgSender();\n uint256 shares = previewWithdraw(assets);\n\n if (caller != owner) {\n _spendAllowance(owner, caller, shares);\n }\n\n // if _asset is ERC777, transfer can call reenter AFTER the transfer happens through\n // the tokensReceived hook, so we need to transfer after we burn to keep the invariants.\n _burn(owner, shares);\n SafeERC20.safeTransfer(_asset, receiver, assets);\n\n emit Withdraw(caller, receiver, owner, assets, shares);\n\n return shares;\n }\n\n /** @dev See {IERC4262-redeem} */\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) public virtual override returns (uint256) {\n require(shares <= maxRedeem(owner), \"ERC4626: redeem more then max\");\n\n address caller = _msgSender();\n uint256 assets = previewRedeem(shares);\n\n if (caller != owner) {\n _spendAllowance(owner, caller, shares);\n }\n\n // if _asset is ERC777, transfer can call reenter AFTER the transfer happens through\n // the tokensReceived hook, so we need to transfer after we burn to keep the invariants.\n _burn(owner, shares);\n SafeERC20.safeTransfer(_asset, receiver, assets);\n\n emit Withdraw(caller, receiver, owner, assets, shares);\n\n return assets;\n }\n\n // Included here, since this method was not yet present in\n // the version of Open Zeppelin ERC20 code we use.\n function _spendAllowance(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n uint256 currentAllowance = allowance(owner, spender);\n if (currentAllowance != type(uint256).max) {\n require(currentAllowance >= amount, \"ERC20: insufficient allowance\");\n unchecked {\n _approve(owner, spender, currentAllowance - amount);\n }\n }\n }\n}" + }, + "lib/openzeppelin/interfaces/IERC4626.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IERC4626 is IERC20, IERC20Metadata {\n event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);\n\n event Withdraw(\n address indexed caller,\n address indexed receiver,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n\n /**\n * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.\n *\n * - MUST be an ERC-20 token contract.\n * - MUST NOT revert.\n */\n function asset() external view returns (address assetTokenAddress);\n\n /**\n * @dev Returns the total amount of the underlying asset that is “managed” by Vault.\n *\n * - SHOULD include any compounding that occurs from yield.\n * - MUST be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT revert.\n */\n function totalAssets() external view returns (uint256 totalManagedAssets);\n\n /**\n * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal\n * scenario where all the conditions are met.\n *\n * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT show any variations depending on the caller.\n * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n * - MUST NOT revert.\n *\n * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n * from.\n */\n function convertToShares(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal\n * scenario where all the conditions are met.\n *\n * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT show any variations depending on the caller.\n * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n * - MUST NOT revert.\n *\n * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n * from.\n */\n function convertToAssets(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,\n * through a deposit call.\n *\n * - MUST return a limited value if receiver is subject to some deposit limit.\n * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.\n * - MUST NOT revert.\n */\n function maxDeposit(address receiver) external view returns (uint256 maxAssets);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given\n * current on-chain conditions.\n *\n * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit\n * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called\n * in the same transaction.\n * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the\n * deposit would be accepted, regardless if the user has enough tokens approved, etc.\n * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by depositing.\n */\n function previewDeposit(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.\n *\n * - MUST emit the Deposit event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * deposit execution, and are accounted for during deposit.\n * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not\n * approving enough underlying tokens to the Vault contract, etc).\n *\n * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n */\n function deposit(uint256 assets, address receiver) external returns (uint256 shares);\n\n /**\n * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.\n * - MUST return a limited value if receiver is subject to some mint limit.\n * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.\n * - MUST NOT revert.\n */\n function maxMint(address receiver) external view returns (uint256 maxShares);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given\n * current on-chain conditions.\n *\n * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call\n * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the\n * same transaction.\n * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint\n * would be accepted, regardless if the user has enough tokens approved, etc.\n * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by minting.\n */\n function previewMint(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.\n *\n * - MUST emit the Deposit event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint\n * execution, and are accounted for during mint.\n * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not\n * approving enough underlying tokens to the Vault contract, etc).\n *\n * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n */\n function mint(uint256 shares, address receiver) external returns (uint256 assets);\n\n /**\n * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the\n * Vault, through a withdraw call.\n *\n * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n * - MUST NOT revert.\n */\n function maxWithdraw(address owner) external view returns (uint256 maxAssets);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,\n * given current on-chain conditions.\n *\n * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw\n * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if\n * called\n * in the same transaction.\n * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though\n * the withdrawal would be accepted, regardless if the user has enough shares, etc.\n * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by depositing.\n */\n function previewWithdraw(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.\n *\n * - MUST emit the Withdraw event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * withdraw execution, and are accounted for during withdraw.\n * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner\n * not having enough shares, etc).\n *\n * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n * Those methods should be performed separately.\n */\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares);\n\n /**\n * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,\n * through a redeem call.\n *\n * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.\n * - MUST NOT revert.\n */\n function maxRedeem(address owner) external view returns (uint256 maxShares);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,\n * given current on-chain conditions.\n *\n * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call\n * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the\n * same transaction.\n * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the\n * redemption would be accepted, regardless if the user has enough shares, etc.\n * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by redeeming.\n */\n function previewRedeem(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.\n *\n * - MUST emit the Withdraw event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * redeem execution, and are accounted for during redeem.\n * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner\n * not having enough shares, etc).\n *\n * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n * Those methods should be performed separately.\n */\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets);\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.20;\n\nimport {Panic} from \"../Panic.sol\";\nimport {SafeCast} from \"./SafeCast.sol\";\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n enum Rounding {\n Floor, // Toward negative infinity\n Ceil, // Toward positive infinity\n Trunc, // Toward zero\n Expand // Away from zero\n }\n\n /**\n * @dev Return the 512-bit addition of two uint256.\n *\n * The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low.\n */\n function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {\n assembly (\"memory-safe\") {\n low := add(a, b)\n high := lt(low, a)\n }\n }\n\n /**\n * @dev Return the 512-bit multiplication of two uint256.\n *\n * The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low.\n */\n function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {\n // 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use\n // the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n // variables such that product = high * 2²⁵⁶ + low.\n assembly (\"memory-safe\") {\n let mm := mulmod(a, b, not(0))\n low := mul(a, b)\n high := sub(sub(mm, low), lt(mm, low))\n }\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, with a success flag (no overflow).\n */\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a + b;\n success = c >= a;\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).\n */\n function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a - b;\n success = c <= a;\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).\n */\n function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a * b;\n assembly (\"memory-safe\") {\n // Only true when the multiplication doesn't overflow\n // (c / a == b) || (a == 0)\n success := or(eq(div(c, a), b), iszero(a))\n }\n // equivalent to: success ? c : 0\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the division of two unsigned integers, with a success flag (no division by zero).\n */\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n success = b > 0;\n assembly (\"memory-safe\") {\n // The `DIV` opcode returns zero when the denominator is 0.\n result := div(a, b)\n }\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).\n */\n function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n success = b > 0;\n assembly (\"memory-safe\") {\n // The `MOD` opcode returns zero when the denominator is 0.\n result := mod(a, b)\n }\n }\n }\n\n /**\n * @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing.\n */\n function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {\n (bool success, uint256 result) = tryAdd(a, b);\n return ternary(success, result, type(uint256).max);\n }\n\n /**\n * @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.\n */\n function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {\n (, uint256 result) = trySub(a, b);\n return result;\n }\n\n /**\n * @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing.\n */\n function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {\n (bool success, uint256 result) = tryMul(a, b);\n return ternary(success, result, type(uint256).max);\n }\n\n /**\n * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.\n *\n * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.\n * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute\n * one branch when needed, making this function more expensive.\n */\n function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {\n unchecked {\n // branchless ternary works because:\n // b ^ (a ^ b) == a\n // b ^ 0 == b\n return b ^ ((a ^ b) * SafeCast.toUint(condition));\n }\n }\n\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return ternary(a > b, a, b);\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return ternary(a < b, a, b);\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds towards infinity instead\n * of rounding towards zero.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n if (b == 0) {\n // Guarantee the same behavior as in a regular Solidity division.\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n\n // The following calculation ensures accurate ceiling division without overflow.\n // Since a is non-zero, (a - 1) / b will not overflow.\n // The largest possible result occurs when (a - 1) / b is type(uint256).max,\n // but the largest value we can obtain is type(uint256).max - 1, which happens\n // when a = type(uint256).max and b = 1.\n unchecked {\n return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);\n }\n }\n\n /**\n * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or\n * denominator == 0.\n *\n * Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by\n * Uniswap Labs also under MIT license.\n */\n function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {\n unchecked {\n (uint256 high, uint256 low) = mul512(x, y);\n\n // Handle non-overflow cases, 256 by 256 division.\n if (high == 0) {\n // Solidity will revert if denominator == 0, unlike the div opcode on its own.\n // The surrounding unchecked block does not change this fact.\n // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.\n return low / denominator;\n }\n\n // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.\n if (denominator <= high) {\n Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));\n }\n\n ///////////////////////////////////////////////\n // 512 by 256 division.\n ///////////////////////////////////////////////\n\n // Make division exact by subtracting the remainder from [high low].\n uint256 remainder;\n assembly (\"memory-safe\") {\n // Compute remainder using mulmod.\n remainder := mulmod(x, y, denominator)\n\n // Subtract 256 bit number from 512 bit number.\n high := sub(high, gt(remainder, low))\n low := sub(low, remainder)\n }\n\n // Factor powers of two out of denominator and compute largest power of two divisor of denominator.\n // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.\n\n uint256 twos = denominator & (0 - denominator);\n assembly (\"memory-safe\") {\n // Divide denominator by twos.\n denominator := div(denominator, twos)\n\n // Divide [high low] by twos.\n low := div(low, twos)\n\n // Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.\n twos := add(div(sub(0, twos), twos), 1)\n }\n\n // Shift in bits from high into low.\n low |= high * twos;\n\n // Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such\n // that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for\n // four bits. That is, denominator * inv ≡ 1 mod 2⁴.\n uint256 inverse = (3 * denominator) ^ 2;\n\n // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also\n // works in modular arithmetic, doubling the correct bits in each step.\n inverse *= 2 - denominator * inverse; // inverse mod 2⁸\n inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶\n inverse *= 2 - denominator * inverse; // inverse mod 2³²\n inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴\n inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸\n inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶\n\n // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n // This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is\n // less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and high\n // is no longer required.\n result = low * inverse;\n return result;\n }\n }\n\n /**\n * @dev Calculates x * y / denominator with full precision, following the selected rounding direction.\n */\n function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {\n return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);\n }\n\n /**\n * @dev Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256.\n */\n function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) {\n unchecked {\n (uint256 high, uint256 low) = mul512(x, y);\n if (high >= 1 << n) {\n Panic.panic(Panic.UNDER_OVERFLOW);\n }\n return (high << (256 - n)) | (low >> n);\n }\n }\n\n /**\n * @dev Calculates x * y >> n with full precision, following the selected rounding direction.\n */\n function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) {\n return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 0);\n }\n\n /**\n * @dev Calculate the modular multiplicative inverse of a number in Z/nZ.\n *\n * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.\n * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.\n *\n * If the input value is not inversible, 0 is returned.\n *\n * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the\n * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.\n */\n function invMod(uint256 a, uint256 n) internal pure returns (uint256) {\n unchecked {\n if (n == 0) return 0;\n\n // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)\n // Used to compute integers x and y such that: ax + ny = gcd(a, n).\n // When the gcd is 1, then the inverse of a modulo n exists and it's x.\n // ax + ny = 1\n // ax = 1 + (-y)n\n // ax ≡ 1 (mod n) # x is the inverse of a modulo n\n\n // If the remainder is 0 the gcd is n right away.\n uint256 remainder = a % n;\n uint256 gcd = n;\n\n // Therefore the initial coefficients are:\n // ax + ny = gcd(a, n) = n\n // 0a + 1n = n\n int256 x = 0;\n int256 y = 1;\n\n while (remainder != 0) {\n uint256 quotient = gcd / remainder;\n\n (gcd, remainder) = (\n // The old remainder is the next gcd to try.\n remainder,\n // Compute the next remainder.\n // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd\n // where gcd is at most n (capped to type(uint256).max)\n gcd - remainder * quotient\n );\n\n (x, y) = (\n // Increment the coefficient of a.\n y,\n // Decrement the coefficient of n.\n // Can overflow, but the result is casted to uint256 so that the\n // next value of y is \"wrapped around\" to a value between 0 and n - 1.\n x - y * int256(quotient)\n );\n }\n\n if (gcd != 1) return 0; // No inverse exists.\n return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.\n }\n }\n\n /**\n * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.\n *\n * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is\n * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that\n * `a**(p-2)` is the modular multiplicative inverse of a in Fp.\n *\n * NOTE: this function does NOT check that `p` is a prime greater than `2`.\n */\n function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {\n unchecked {\n return Math.modExp(a, p - 2, p);\n }\n }\n\n /**\n * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)\n *\n * Requirements:\n * - modulus can't be zero\n * - underlying staticcall to precompile must succeed\n *\n * IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make\n * sure the chain you're using it on supports the precompiled contract for modular exponentiation\n * at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,\n * the underlying function will succeed given the lack of a revert, but the result may be incorrectly\n * interpreted as 0.\n */\n function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {\n (bool success, uint256 result) = tryModExp(b, e, m);\n if (!success) {\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n return result;\n }\n\n /**\n * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).\n * It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying\n * to operate modulo 0 or if the underlying precompile reverted.\n *\n * IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain\n * you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in\n * https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack\n * of a revert, but the result may be incorrectly interpreted as 0.\n */\n function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {\n if (m == 0) return (false, 0);\n assembly (\"memory-safe\") {\n let ptr := mload(0x40)\n // | Offset | Content | Content (Hex) |\n // |-----------|------------|--------------------------------------------------------------------|\n // | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x60:0x7f | value of b | 0x<.............................................................b> |\n // | 0x80:0x9f | value of e | 0x<.............................................................e> |\n // | 0xa0:0xbf | value of m | 0x<.............................................................m> |\n mstore(ptr, 0x20)\n mstore(add(ptr, 0x20), 0x20)\n mstore(add(ptr, 0x40), 0x20)\n mstore(add(ptr, 0x60), b)\n mstore(add(ptr, 0x80), e)\n mstore(add(ptr, 0xa0), m)\n\n // Given the result < m, it's guaranteed to fit in 32 bytes,\n // so we can use the memory scratch space located at offset 0.\n success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)\n result := mload(0x00)\n }\n }\n\n /**\n * @dev Variant of {modExp} that supports inputs of arbitrary length.\n */\n function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {\n (bool success, bytes memory result) = tryModExp(b, e, m);\n if (!success) {\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n return result;\n }\n\n /**\n * @dev Variant of {tryModExp} that supports inputs of arbitrary length.\n */\n function tryModExp(\n bytes memory b,\n bytes memory e,\n bytes memory m\n ) internal view returns (bool success, bytes memory result) {\n if (_zeroBytes(m)) return (false, new bytes(0));\n\n uint256 mLen = m.length;\n\n // Encode call args in result and move the free memory pointer\n result = abi.encodePacked(b.length, e.length, mLen, b, e, m);\n\n assembly (\"memory-safe\") {\n let dataPtr := add(result, 0x20)\n // Write result on top of args to avoid allocating extra memory.\n success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)\n // Overwrite the length.\n // result.length > returndatasize() is guaranteed because returndatasize() == m.length\n mstore(result, mLen)\n // Set the memory pointer after the returned data.\n mstore(0x40, add(dataPtr, mLen))\n }\n }\n\n /**\n * @dev Returns whether the provided byte array is zero.\n */\n function _zeroBytes(bytes memory byteArray) private pure returns (bool) {\n for (uint256 i = 0; i < byteArray.length; ++i) {\n if (byteArray[i] != 0) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded\n * towards zero.\n *\n * This method is based on Newton's method for computing square roots; the algorithm is restricted to only\n * using integer operations.\n */\n function sqrt(uint256 a) internal pure returns (uint256) {\n unchecked {\n // Take care of easy edge cases when a == 0 or a == 1\n if (a <= 1) {\n return a;\n }\n\n // In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a\n // sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between\n // the current value as `ε_n = | x_n - sqrt(a) |`.\n //\n // For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root\n // of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is\n // bigger than any uint256.\n //\n // By noticing that\n // `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`\n // we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar\n // to the msb function.\n uint256 aa = a;\n uint256 xn = 1;\n\n if (aa >= (1 << 128)) {\n aa >>= 128;\n xn <<= 64;\n }\n if (aa >= (1 << 64)) {\n aa >>= 64;\n xn <<= 32;\n }\n if (aa >= (1 << 32)) {\n aa >>= 32;\n xn <<= 16;\n }\n if (aa >= (1 << 16)) {\n aa >>= 16;\n xn <<= 8;\n }\n if (aa >= (1 << 8)) {\n aa >>= 8;\n xn <<= 4;\n }\n if (aa >= (1 << 4)) {\n aa >>= 4;\n xn <<= 2;\n }\n if (aa >= (1 << 2)) {\n xn <<= 1;\n }\n\n // We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).\n //\n // We can refine our estimation by noticing that the middle of that interval minimizes the error.\n // If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).\n // This is going to be our x_0 (and ε_0)\n xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)\n\n // From here, Newton's method give us:\n // x_{n+1} = (x_n + a / x_n) / 2\n //\n // One should note that:\n // x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a\n // = ((x_n² + a) / (2 * x_n))² - a\n // = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a\n // = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)\n // = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)\n // = (x_n² - a)² / (2 * x_n)²\n // = ((x_n² - a) / (2 * x_n))²\n // ≥ 0\n // Which proves that for all n ≥ 1, sqrt(a) ≤ x_n\n //\n // This gives us the proof of quadratic convergence of the sequence:\n // ε_{n+1} = | x_{n+1} - sqrt(a) |\n // = | (x_n + a / x_n) / 2 - sqrt(a) |\n // = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |\n // = | (x_n - sqrt(a))² / (2 * x_n) |\n // = | ε_n² / (2 * x_n) |\n // = ε_n² / | (2 * x_n) |\n //\n // For the first iteration, we have a special case where x_0 is known:\n // ε_1 = ε_0² / | (2 * x_0) |\n // ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))\n // ≤ 2**(2*e-4) / (3 * 2**(e-1))\n // ≤ 2**(e-3) / 3\n // ≤ 2**(e-3-log2(3))\n // ≤ 2**(e-4.5)\n //\n // For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:\n // ε_{n+1} = ε_n² / | (2 * x_n) |\n // ≤ (2**(e-k))² / (2 * 2**(e-1))\n // ≤ 2**(2*e-2*k) / 2**e\n // ≤ 2**(e-2*k)\n xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above\n xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5\n xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9\n xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18\n xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36\n xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72\n\n // Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision\n // ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either\n // sqrt(a) or sqrt(a) + 1.\n return xn - SafeCast.toUint(xn > a / xn);\n }\n }\n\n /**\n * @dev Calculates sqrt(a), following the selected rounding direction.\n */\n function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = sqrt(a);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);\n }\n }\n\n /**\n * @dev Return the log in base 2 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n */\n function log2(uint256 x) internal pure returns (uint256 r) {\n // If value has upper 128 bits set, log2 result is at least 128\n r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;\n // If upper 64 bits of 128-bit half set, add 64 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;\n // If upper 32 bits of 64-bit half set, add 32 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;\n // If upper 16 bits of 32-bit half set, add 16 to result\n r |= SafeCast.toUint((x >> r) > 0xffff) << 4;\n // If upper 8 bits of 16-bit half set, add 8 to result\n r |= SafeCast.toUint((x >> r) > 0xff) << 3;\n // If upper 4 bits of 8-bit half set, add 4 to result\n r |= SafeCast.toUint((x >> r) > 0xf) << 2;\n\n // Shifts value right by the current result and use it as an index into this lookup table:\n //\n // | x (4 bits) | index | table[index] = MSB position |\n // |------------|---------|-----------------------------|\n // | 0000 | 0 | table[0] = 0 |\n // | 0001 | 1 | table[1] = 0 |\n // | 0010 | 2 | table[2] = 1 |\n // | 0011 | 3 | table[3] = 1 |\n // | 0100 | 4 | table[4] = 2 |\n // | 0101 | 5 | table[5] = 2 |\n // | 0110 | 6 | table[6] = 2 |\n // | 0111 | 7 | table[7] = 2 |\n // | 1000 | 8 | table[8] = 3 |\n // | 1001 | 9 | table[9] = 3 |\n // | 1010 | 10 | table[10] = 3 |\n // | 1011 | 11 | table[11] = 3 |\n // | 1100 | 12 | table[12] = 3 |\n // | 1101 | 13 | table[13] = 3 |\n // | 1110 | 14 | table[14] = 3 |\n // | 1111 | 15 | table[15] = 3 |\n //\n // The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.\n assembly (\"memory-safe\") {\n r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))\n }\n }\n\n /**\n * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log2(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);\n }\n }\n\n /**\n * @dev Return the log in base 10 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n */\n function log10(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >= 10 ** 64) {\n value /= 10 ** 64;\n result += 64;\n }\n if (value >= 10 ** 32) {\n value /= 10 ** 32;\n result += 32;\n }\n if (value >= 10 ** 16) {\n value /= 10 ** 16;\n result += 16;\n }\n if (value >= 10 ** 8) {\n value /= 10 ** 8;\n result += 8;\n }\n if (value >= 10 ** 4) {\n value /= 10 ** 4;\n result += 4;\n }\n if (value >= 10 ** 2) {\n value /= 10 ** 2;\n result += 2;\n }\n if (value >= 10 ** 1) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log10(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);\n }\n }\n\n /**\n * @dev Return the log in base 256 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n *\n * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n */\n function log256(uint256 x) internal pure returns (uint256 r) {\n // If value has upper 128 bits set, log2 result is at least 128\n r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;\n // If upper 64 bits of 128-bit half set, add 64 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;\n // If upper 32 bits of 64-bit half set, add 32 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;\n // If upper 16 bits of 32-bit half set, add 16 to result\n r |= SafeCast.toUint((x >> r) > 0xffff) << 4;\n // Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8\n return (r >> 3) | SafeCast.toUint((x >> r) > 0xff);\n }\n\n /**\n * @dev Return the log in base 256, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log256(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);\n }\n }\n\n /**\n * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.\n */\n function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {\n return uint8(rounding) % 2 == 1;\n }\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)\n// This file was procedurally generated from scripts/generate/templates/SafeCast.js.\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n */\nlibrary SafeCast {\n /**\n * @dev Value doesn't fit in an uint of `bits` size.\n */\n error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);\n\n /**\n * @dev An int value doesn't fit in an uint of `bits` size.\n */\n error SafeCastOverflowedIntToUint(int256 value);\n\n /**\n * @dev Value doesn't fit in an int of `bits` size.\n */\n error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);\n\n /**\n * @dev An uint value doesn't fit in an int of `bits` size.\n */\n error SafeCastOverflowedUintToInt(uint256 value);\n\n /**\n * @dev Returns the downcasted uint248 from uint256, reverting on\n * overflow (when the input is greater than largest uint248).\n *\n * Counterpart to Solidity's `uint248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n */\n function toUint248(uint256 value) internal pure returns (uint248) {\n if (value > type(uint248).max) {\n revert SafeCastOverflowedUintDowncast(248, value);\n }\n return uint248(value);\n }\n\n /**\n * @dev Returns the downcasted uint240 from uint256, reverting on\n * overflow (when the input is greater than largest uint240).\n *\n * Counterpart to Solidity's `uint240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n */\n function toUint240(uint256 value) internal pure returns (uint240) {\n if (value > type(uint240).max) {\n revert SafeCastOverflowedUintDowncast(240, value);\n }\n return uint240(value);\n }\n\n /**\n * @dev Returns the downcasted uint232 from uint256, reverting on\n * overflow (when the input is greater than largest uint232).\n *\n * Counterpart to Solidity's `uint232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n */\n function toUint232(uint256 value) internal pure returns (uint232) {\n if (value > type(uint232).max) {\n revert SafeCastOverflowedUintDowncast(232, value);\n }\n return uint232(value);\n }\n\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n if (value > type(uint224).max) {\n revert SafeCastOverflowedUintDowncast(224, value);\n }\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint216 from uint256, reverting on\n * overflow (when the input is greater than largest uint216).\n *\n * Counterpart to Solidity's `uint216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n */\n function toUint216(uint256 value) internal pure returns (uint216) {\n if (value > type(uint216).max) {\n revert SafeCastOverflowedUintDowncast(216, value);\n }\n return uint216(value);\n }\n\n /**\n * @dev Returns the downcasted uint208 from uint256, reverting on\n * overflow (when the input is greater than largest uint208).\n *\n * Counterpart to Solidity's `uint208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n */\n function toUint208(uint256 value) internal pure returns (uint208) {\n if (value > type(uint208).max) {\n revert SafeCastOverflowedUintDowncast(208, value);\n }\n return uint208(value);\n }\n\n /**\n * @dev Returns the downcasted uint200 from uint256, reverting on\n * overflow (when the input is greater than largest uint200).\n *\n * Counterpart to Solidity's `uint200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n */\n function toUint200(uint256 value) internal pure returns (uint200) {\n if (value > type(uint200).max) {\n revert SafeCastOverflowedUintDowncast(200, value);\n }\n return uint200(value);\n }\n\n /**\n * @dev Returns the downcasted uint192 from uint256, reverting on\n * overflow (when the input is greater than largest uint192).\n *\n * Counterpart to Solidity's `uint192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n */\n function toUint192(uint256 value) internal pure returns (uint192) {\n if (value > type(uint192).max) {\n revert SafeCastOverflowedUintDowncast(192, value);\n }\n return uint192(value);\n }\n\n /**\n * @dev Returns the downcasted uint184 from uint256, reverting on\n * overflow (when the input is greater than largest uint184).\n *\n * Counterpart to Solidity's `uint184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n */\n function toUint184(uint256 value) internal pure returns (uint184) {\n if (value > type(uint184).max) {\n revert SafeCastOverflowedUintDowncast(184, value);\n }\n return uint184(value);\n }\n\n /**\n * @dev Returns the downcasted uint176 from uint256, reverting on\n * overflow (when the input is greater than largest uint176).\n *\n * Counterpart to Solidity's `uint176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n */\n function toUint176(uint256 value) internal pure returns (uint176) {\n if (value > type(uint176).max) {\n revert SafeCastOverflowedUintDowncast(176, value);\n }\n return uint176(value);\n }\n\n /**\n * @dev Returns the downcasted uint168 from uint256, reverting on\n * overflow (when the input is greater than largest uint168).\n *\n * Counterpart to Solidity's `uint168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n */\n function toUint168(uint256 value) internal pure returns (uint168) {\n if (value > type(uint168).max) {\n revert SafeCastOverflowedUintDowncast(168, value);\n }\n return uint168(value);\n }\n\n /**\n * @dev Returns the downcasted uint160 from uint256, reverting on\n * overflow (when the input is greater than largest uint160).\n *\n * Counterpart to Solidity's `uint160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n */\n function toUint160(uint256 value) internal pure returns (uint160) {\n if (value > type(uint160).max) {\n revert SafeCastOverflowedUintDowncast(160, value);\n }\n return uint160(value);\n }\n\n /**\n * @dev Returns the downcasted uint152 from uint256, reverting on\n * overflow (when the input is greater than largest uint152).\n *\n * Counterpart to Solidity's `uint152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n */\n function toUint152(uint256 value) internal pure returns (uint152) {\n if (value > type(uint152).max) {\n revert SafeCastOverflowedUintDowncast(152, value);\n }\n return uint152(value);\n }\n\n /**\n * @dev Returns the downcasted uint144 from uint256, reverting on\n * overflow (when the input is greater than largest uint144).\n *\n * Counterpart to Solidity's `uint144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n */\n function toUint144(uint256 value) internal pure returns (uint144) {\n if (value > type(uint144).max) {\n revert SafeCastOverflowedUintDowncast(144, value);\n }\n return uint144(value);\n }\n\n /**\n * @dev Returns the downcasted uint136 from uint256, reverting on\n * overflow (when the input is greater than largest uint136).\n *\n * Counterpart to Solidity's `uint136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n */\n function toUint136(uint256 value) internal pure returns (uint136) {\n if (value > type(uint136).max) {\n revert SafeCastOverflowedUintDowncast(136, value);\n }\n return uint136(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n if (value > type(uint128).max) {\n revert SafeCastOverflowedUintDowncast(128, value);\n }\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint120 from uint256, reverting on\n * overflow (when the input is greater than largest uint120).\n *\n * Counterpart to Solidity's `uint120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n */\n function toUint120(uint256 value) internal pure returns (uint120) {\n if (value > type(uint120).max) {\n revert SafeCastOverflowedUintDowncast(120, value);\n }\n return uint120(value);\n }\n\n /**\n * @dev Returns the downcasted uint112 from uint256, reverting on\n * overflow (when the input is greater than largest uint112).\n *\n * Counterpart to Solidity's `uint112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n */\n function toUint112(uint256 value) internal pure returns (uint112) {\n if (value > type(uint112).max) {\n revert SafeCastOverflowedUintDowncast(112, value);\n }\n return uint112(value);\n }\n\n /**\n * @dev Returns the downcasted uint104 from uint256, reverting on\n * overflow (when the input is greater than largest uint104).\n *\n * Counterpart to Solidity's `uint104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n */\n function toUint104(uint256 value) internal pure returns (uint104) {\n if (value > type(uint104).max) {\n revert SafeCastOverflowedUintDowncast(104, value);\n }\n return uint104(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n if (value > type(uint96).max) {\n revert SafeCastOverflowedUintDowncast(96, value);\n }\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint88 from uint256, reverting on\n * overflow (when the input is greater than largest uint88).\n *\n * Counterpart to Solidity's `uint88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n */\n function toUint88(uint256 value) internal pure returns (uint88) {\n if (value > type(uint88).max) {\n revert SafeCastOverflowedUintDowncast(88, value);\n }\n return uint88(value);\n }\n\n /**\n * @dev Returns the downcasted uint80 from uint256, reverting on\n * overflow (when the input is greater than largest uint80).\n *\n * Counterpart to Solidity's `uint80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n */\n function toUint80(uint256 value) internal pure returns (uint80) {\n if (value > type(uint80).max) {\n revert SafeCastOverflowedUintDowncast(80, value);\n }\n return uint80(value);\n }\n\n /**\n * @dev Returns the downcasted uint72 from uint256, reverting on\n * overflow (when the input is greater than largest uint72).\n *\n * Counterpart to Solidity's `uint72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n */\n function toUint72(uint256 value) internal pure returns (uint72) {\n if (value > type(uint72).max) {\n revert SafeCastOverflowedUintDowncast(72, value);\n }\n return uint72(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n if (value > type(uint64).max) {\n revert SafeCastOverflowedUintDowncast(64, value);\n }\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint56 from uint256, reverting on\n * overflow (when the input is greater than largest uint56).\n *\n * Counterpart to Solidity's `uint56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n */\n function toUint56(uint256 value) internal pure returns (uint56) {\n if (value > type(uint56).max) {\n revert SafeCastOverflowedUintDowncast(56, value);\n }\n return uint56(value);\n }\n\n /**\n * @dev Returns the downcasted uint48 from uint256, reverting on\n * overflow (when the input is greater than largest uint48).\n *\n * Counterpart to Solidity's `uint48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n */\n function toUint48(uint256 value) internal pure returns (uint48) {\n if (value > type(uint48).max) {\n revert SafeCastOverflowedUintDowncast(48, value);\n }\n return uint48(value);\n }\n\n /**\n * @dev Returns the downcasted uint40 from uint256, reverting on\n * overflow (when the input is greater than largest uint40).\n *\n * Counterpart to Solidity's `uint40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n */\n function toUint40(uint256 value) internal pure returns (uint40) {\n if (value > type(uint40).max) {\n revert SafeCastOverflowedUintDowncast(40, value);\n }\n return uint40(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n if (value > type(uint32).max) {\n revert SafeCastOverflowedUintDowncast(32, value);\n }\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint24 from uint256, reverting on\n * overflow (when the input is greater than largest uint24).\n *\n * Counterpart to Solidity's `uint24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n */\n function toUint24(uint256 value) internal pure returns (uint24) {\n if (value > type(uint24).max) {\n revert SafeCastOverflowedUintDowncast(24, value);\n }\n return uint24(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n if (value > type(uint16).max) {\n revert SafeCastOverflowedUintDowncast(16, value);\n }\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n if (value > type(uint8).max) {\n revert SafeCastOverflowedUintDowncast(8, value);\n }\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n if (value < 0) {\n revert SafeCastOverflowedIntToUint(value);\n }\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int248 from int256, reverting on\n * overflow (when the input is less than smallest int248 or\n * greater than largest int248).\n *\n * Counterpart to Solidity's `int248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n */\n function toInt248(int256 value) internal pure returns (int248 downcasted) {\n downcasted = int248(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(248, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int240 from int256, reverting on\n * overflow (when the input is less than smallest int240 or\n * greater than largest int240).\n *\n * Counterpart to Solidity's `int240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n */\n function toInt240(int256 value) internal pure returns (int240 downcasted) {\n downcasted = int240(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(240, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int232 from int256, reverting on\n * overflow (when the input is less than smallest int232 or\n * greater than largest int232).\n *\n * Counterpart to Solidity's `int232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n */\n function toInt232(int256 value) internal pure returns (int232 downcasted) {\n downcasted = int232(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(232, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int224 from int256, reverting on\n * overflow (when the input is less than smallest int224 or\n * greater than largest int224).\n *\n * Counterpart to Solidity's `int224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toInt224(int256 value) internal pure returns (int224 downcasted) {\n downcasted = int224(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(224, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int216 from int256, reverting on\n * overflow (when the input is less than smallest int216 or\n * greater than largest int216).\n *\n * Counterpart to Solidity's `int216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n */\n function toInt216(int256 value) internal pure returns (int216 downcasted) {\n downcasted = int216(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(216, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int208 from int256, reverting on\n * overflow (when the input is less than smallest int208 or\n * greater than largest int208).\n *\n * Counterpart to Solidity's `int208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n */\n function toInt208(int256 value) internal pure returns (int208 downcasted) {\n downcasted = int208(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(208, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int200 from int256, reverting on\n * overflow (when the input is less than smallest int200 or\n * greater than largest int200).\n *\n * Counterpart to Solidity's `int200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n */\n function toInt200(int256 value) internal pure returns (int200 downcasted) {\n downcasted = int200(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(200, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int192 from int256, reverting on\n * overflow (when the input is less than smallest int192 or\n * greater than largest int192).\n *\n * Counterpart to Solidity's `int192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n */\n function toInt192(int256 value) internal pure returns (int192 downcasted) {\n downcasted = int192(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(192, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int184 from int256, reverting on\n * overflow (when the input is less than smallest int184 or\n * greater than largest int184).\n *\n * Counterpart to Solidity's `int184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n */\n function toInt184(int256 value) internal pure returns (int184 downcasted) {\n downcasted = int184(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(184, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int176 from int256, reverting on\n * overflow (when the input is less than smallest int176 or\n * greater than largest int176).\n *\n * Counterpart to Solidity's `int176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n */\n function toInt176(int256 value) internal pure returns (int176 downcasted) {\n downcasted = int176(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(176, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int168 from int256, reverting on\n * overflow (when the input is less than smallest int168 or\n * greater than largest int168).\n *\n * Counterpart to Solidity's `int168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n */\n function toInt168(int256 value) internal pure returns (int168 downcasted) {\n downcasted = int168(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(168, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int160 from int256, reverting on\n * overflow (when the input is less than smallest int160 or\n * greater than largest int160).\n *\n * Counterpart to Solidity's `int160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n */\n function toInt160(int256 value) internal pure returns (int160 downcasted) {\n downcasted = int160(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(160, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int152 from int256, reverting on\n * overflow (when the input is less than smallest int152 or\n * greater than largest int152).\n *\n * Counterpart to Solidity's `int152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n */\n function toInt152(int256 value) internal pure returns (int152 downcasted) {\n downcasted = int152(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(152, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int144 from int256, reverting on\n * overflow (when the input is less than smallest int144 or\n * greater than largest int144).\n *\n * Counterpart to Solidity's `int144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n */\n function toInt144(int256 value) internal pure returns (int144 downcasted) {\n downcasted = int144(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(144, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int136 from int256, reverting on\n * overflow (when the input is less than smallest int136 or\n * greater than largest int136).\n *\n * Counterpart to Solidity's `int136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n */\n function toInt136(int256 value) internal pure returns (int136 downcasted) {\n downcasted = int136(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(136, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toInt128(int256 value) internal pure returns (int128 downcasted) {\n downcasted = int128(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(128, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int120 from int256, reverting on\n * overflow (when the input is less than smallest int120 or\n * greater than largest int120).\n *\n * Counterpart to Solidity's `int120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n */\n function toInt120(int256 value) internal pure returns (int120 downcasted) {\n downcasted = int120(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(120, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int112 from int256, reverting on\n * overflow (when the input is less than smallest int112 or\n * greater than largest int112).\n *\n * Counterpart to Solidity's `int112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n */\n function toInt112(int256 value) internal pure returns (int112 downcasted) {\n downcasted = int112(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(112, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int104 from int256, reverting on\n * overflow (when the input is less than smallest int104 or\n * greater than largest int104).\n *\n * Counterpart to Solidity's `int104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n */\n function toInt104(int256 value) internal pure returns (int104 downcasted) {\n downcasted = int104(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(104, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int96 from int256, reverting on\n * overflow (when the input is less than smallest int96 or\n * greater than largest int96).\n *\n * Counterpart to Solidity's `int96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toInt96(int256 value) internal pure returns (int96 downcasted) {\n downcasted = int96(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(96, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int88 from int256, reverting on\n * overflow (when the input is less than smallest int88 or\n * greater than largest int88).\n *\n * Counterpart to Solidity's `int88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n */\n function toInt88(int256 value) internal pure returns (int88 downcasted) {\n downcasted = int88(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(88, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int80 from int256, reverting on\n * overflow (when the input is less than smallest int80 or\n * greater than largest int80).\n *\n * Counterpart to Solidity's `int80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n */\n function toInt80(int256 value) internal pure returns (int80 downcasted) {\n downcasted = int80(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(80, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int72 from int256, reverting on\n * overflow (when the input is less than smallest int72 or\n * greater than largest int72).\n *\n * Counterpart to Solidity's `int72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n */\n function toInt72(int256 value) internal pure returns (int72 downcasted) {\n downcasted = int72(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(72, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toInt64(int256 value) internal pure returns (int64 downcasted) {\n downcasted = int64(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(64, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int56 from int256, reverting on\n * overflow (when the input is less than smallest int56 or\n * greater than largest int56).\n *\n * Counterpart to Solidity's `int56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n */\n function toInt56(int256 value) internal pure returns (int56 downcasted) {\n downcasted = int56(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(56, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int48 from int256, reverting on\n * overflow (when the input is less than smallest int48 or\n * greater than largest int48).\n *\n * Counterpart to Solidity's `int48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n */\n function toInt48(int256 value) internal pure returns (int48 downcasted) {\n downcasted = int48(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(48, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int40 from int256, reverting on\n * overflow (when the input is less than smallest int40 or\n * greater than largest int40).\n *\n * Counterpart to Solidity's `int40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n */\n function toInt40(int256 value) internal pure returns (int40 downcasted) {\n downcasted = int40(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(40, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toInt32(int256 value) internal pure returns (int32 downcasted) {\n downcasted = int32(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(32, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int24 from int256, reverting on\n * overflow (when the input is less than smallest int24 or\n * greater than largest int24).\n *\n * Counterpart to Solidity's `int24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n */\n function toInt24(int256 value) internal pure returns (int24 downcasted) {\n downcasted = int24(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(24, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toInt16(int256 value) internal pure returns (int16 downcasted) {\n downcasted = int16(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(16, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n */\n function toInt8(int256 value) internal pure returns (int8 downcasted) {\n downcasted = int8(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(8, value);\n }\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n if (value > uint256(type(int256).max)) {\n revert SafeCastOverflowedUintToInt(value);\n }\n return int256(value);\n }\n\n /**\n * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.\n */\n function toUint(bool b) internal pure returns (uint256 u) {\n assembly (\"memory-safe\") {\n u := iszero(iszero(b))\n }\n }\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/Panic.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Helper library for emitting standardized panic codes.\n *\n * ```solidity\n * contract Example {\n * using Panic for uint256;\n *\n * // Use any of the declared internal constants\n * function foo() { Panic.GENERIC.panic(); }\n *\n * // Alternatively\n * function foo() { Panic.panic(Panic.GENERIC); }\n * }\n * ```\n *\n * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].\n *\n * _Available since v5.1._\n */\n// slither-disable-next-line unused-state\nlibrary Panic {\n /// @dev generic / unspecified error\n uint256 internal constant GENERIC = 0x00;\n /// @dev used by the assert() builtin\n uint256 internal constant ASSERT = 0x01;\n /// @dev arithmetic underflow or overflow\n uint256 internal constant UNDER_OVERFLOW = 0x11;\n /// @dev division or modulo by zero\n uint256 internal constant DIVISION_BY_ZERO = 0x12;\n /// @dev enum conversion error\n uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;\n /// @dev invalid encoding in storage\n uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;\n /// @dev empty array pop\n uint256 internal constant EMPTY_ARRAY_POP = 0x31;\n /// @dev array out of bounds access\n uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;\n /// @dev resource error (too large allocation or too large array)\n uint256 internal constant RESOURCE_ERROR = 0x41;\n /// @dev calling invalid internal function\n uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;\n\n /// @dev Reverts with a panic code. Recommended to use with\n /// the internal constants with predefined codes.\n function panic(uint256 code) internal pure {\n assembly (\"memory-safe\") {\n mstore(0x00, 0x4e487b71)\n mstore(0x20, code)\n revert(0x1c, 0x24)\n }\n }\n}" + }, + "lib/rooster/v2-common/libraries/Constants.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\n// factory contraints on pools\nuint8 constant MAX_PROTOCOL_FEE_RATIO_D3 = 0.25e3; // 25%\nuint256 constant MAX_PROTOCOL_LENDING_FEE_RATE_D18 = 0.02e18; // 2%\nuint64 constant MAX_POOL_FEE_D18 = 0.9e18; // 90%\nuint64 constant MIN_LOOKBACK = 1 seconds;\n\n// pool constraints\nuint8 constant NUMBER_OF_KINDS = 4;\nint32 constant NUMBER_OF_KINDS_32 = int32(int8(NUMBER_OF_KINDS));\nuint256 constant MAX_TICK = 322_378; // max price 1e14 in D18 scale\nint32 constant MAX_TICK_32 = int32(int256(MAX_TICK));\nint32 constant MIN_TICK_32 = int32(-int256(MAX_TICK));\nuint256 constant MAX_BINS_TO_MERGE = 3;\nuint128 constant MINIMUM_LIQUIDITY = 1e8;\n\n// accessor named constants\nuint8 constant ALL_KINDS_MASK = 0xF; // 0b1111\nuint8 constant PERMISSIONED_LIQUIDITY_MASK = 0x10; // 0b010000\nuint8 constant PERMISSIONED_SWAP_MASK = 0x20; // 0b100000\nuint8 constant OPTIONS_MASK = ALL_KINDS_MASK | PERMISSIONED_LIQUIDITY_MASK | PERMISSIONED_SWAP_MASK; // 0b111111\n\n// named values\naddress constant MERGED_LP_BALANCE_ADDRESS = address(0);\nuint256 constant MERGED_LP_BALANCE_SUBACCOUNT = 0;\nuint128 constant ONE = 1e18;\nuint128 constant ONE_SQUARED = 1e36;\nint256 constant INT256_ONE = 1e18;\nuint256 constant ONE_D8 = 1e8;\nuint256 constant ONE_D3 = 1e3;\nint40 constant INT_ONE_D8 = 1e8;\nint40 constant HALF_TICK_D8 = 0.5e8;\nuint8 constant DEFAULT_DECIMALS = 18;\nuint256 constant DEFAULT_SCALE = 1;\nbytes constant EMPTY_PRICE_BREAKS = hex\"010000000000000000000000\";" + }, + "lib/rooster/v2-common/libraries/Math.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport {Math as OzMath} from \"../../openzeppelin-custom/contracts/utils/math/Math.sol\";\n\nimport {ONE, DEFAULT_SCALE, DEFAULT_DECIMALS, INT_ONE_D8, ONE_SQUARED} from \"./Constants.sol\";\n\n/**\n * @notice Math functions.\n */\nlibrary Math {\n /**\n * @notice Returns the lesser of two values.\n * @param x First uint256 value.\n * @param y Second uint256 value.\n */\n function min(uint256 x, uint256 y) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), lt(y, x)))\n }\n }\n\n /**\n * @notice Returns the lesser of two uint128 values.\n * @param x First uint128 value.\n * @param y Second uint128 value.\n */\n function min128(uint128 x, uint128 y) internal pure returns (uint128 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), lt(y, x)))\n }\n }\n\n /**\n * @notice Returns the lesser of two int256 values.\n * @param x First int256 value.\n * @param y Second int256 value.\n */\n function min(int256 x, int256 y) internal pure returns (int256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), slt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two uint256 values.\n * @param x First uint256 value.\n * @param y Second uint256 value.\n */\n function max(uint256 x, uint256 y) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), gt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two int256 values.\n * @param x First int256 value.\n * @param y Second int256 value.\n */\n function max(int256 x, int256 y) internal pure returns (int256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), sgt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two uint128 values.\n * @param x First uint128 value.\n * @param y Second uint128 value.\n */\n function max128(uint128 x, uint128 y) internal pure returns (uint128 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), gt(y, x)))\n }\n }\n\n /**\n * @notice Thresholds a value to be within the specified bounds.\n * @param value The value to bound.\n * @param lowerLimit The minimum allowable value.\n * @param upperLimit The maximum allowable value.\n */\n function boundValue(\n uint256 value,\n uint256 lowerLimit,\n uint256 upperLimit\n ) internal pure returns (uint256 outputValue) {\n outputValue = min(max(value, lowerLimit), upperLimit);\n }\n\n /**\n * @notice Returns the difference between two uint128 values or zero if the result would be negative.\n * @param x The minuend.\n * @param y The subtrahend.\n */\n function clip128(uint128 x, uint128 y) internal pure returns (uint128) {\n unchecked {\n return x < y ? 0 : x - y;\n }\n }\n\n /**\n * @notice Returns the difference between two uint256 values or zero if the result would be negative.\n * @param x The minuend.\n * @param y The subtrahend.\n */\n function clip(uint256 x, uint256 y) internal pure returns (uint256) {\n unchecked {\n return x < y ? 0 : x - y;\n }\n }\n\n /**\n * @notice Divides one uint256 by another, rounding down to the nearest\n * integer.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divFloor(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivFloor(x, ONE, y);\n }\n\n /**\n * @notice Divides one uint256 by another, rounding up to the nearest integer.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divCeil(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivCeil(x, ONE, y);\n }\n\n /**\n * @notice Multiplies two uint256 values and then divides by ONE, rounding down.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulFloor(uint256 x, uint256 y) internal pure returns (uint256) {\n return OzMath.mulDiv(x, y, ONE);\n }\n\n /**\n * @notice Multiplies two uint256 values and then divides by ONE, rounding up.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulCeil(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivCeil(x, y, ONE);\n }\n\n /**\n * @notice Calculates the multiplicative inverse of a uint256, rounding down.\n * @param x The value to invert.\n */\n function invFloor(uint256 x) internal pure returns (uint256) {\n unchecked {\n return ONE_SQUARED / x;\n }\n }\n\n /**\n * @notice Calculates the multiplicative inverse of a uint256, rounding up.\n * @param denominator The value to invert.\n */\n function invCeil(uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // divide z - 1 by the denominator and add 1.\n z := add(div(sub(ONE_SQUARED, 1), denominator), 1)\n }\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding down.\n * @param x The multiplicand.\n * @param y The multiplier.\n * @param k The divisor.\n */\n function mulDivFloor(uint256 x, uint256 y, uint256 k) internal pure returns (uint256 result) {\n result = OzMath.mulDiv(x, y, max(1, k));\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding up if there's a remainder.\n * @param x The multiplicand.\n * @param y The multiplier.\n * @param k The divisor.\n */\n function mulDivCeil(uint256 x, uint256 y, uint256 k) internal pure returns (uint256 result) {\n result = mulDivFloor(x, y, k);\n if (mulmod(x, y, max(1, k)) != 0) result = result + 1;\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding\n * down. Will revert if `x * y` is larger than `type(uint256).max`.\n * @param x The first operand for multiplication.\n * @param y The second operand for multiplication.\n * @param denominator The divisor after multiplication.\n */\n function mulDivDown(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // Store x * y in z for now.\n z := mul(x, y)\n if iszero(denominator) {\n denominator := 1\n }\n\n if iszero(or(iszero(x), eq(div(z, x), y))) {\n revert(0, 0)\n }\n\n // Divide z by the denominator.\n z := div(z, denominator)\n }\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding\n * up. Will revert if `x * y` is larger than `type(uint256).max`.\n * @param x The first operand for multiplication.\n * @param y The second operand for multiplication.\n * @param denominator The divisor after multiplication.\n */\n function mulDivUp(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // Store x * y in z for now.\n z := mul(x, y)\n if iszero(denominator) {\n denominator := 1\n }\n\n if iszero(or(iszero(x), eq(div(z, x), y))) {\n revert(0, 0)\n }\n\n // First, divide z - 1 by the denominator and add 1.\n // We allow z - 1 to underflow if z is 0, because we multiply the\n // end result by 0 if z is zero, ensuring we return 0 if z is zero.\n z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))\n }\n }\n\n /**\n * @notice Multiplies a uint256 by another and divides by a constant,\n * rounding down. Will revert if `x * y` is larger than\n * `type(uint256).max`.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulDown(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivDown(x, y, ONE);\n }\n\n /**\n * @notice Divides a uint256 by another, rounding down the result. Will\n * revert if `x * 1e18` is larger than `type(uint256).max`.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divDown(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivDown(x, ONE, y);\n }\n\n /**\n * @notice Divides a uint256 by another, rounding up the result. Will\n * revert if `x * 1e18` is larger than `type(uint256).max`.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divUp(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivUp(x, ONE, y);\n }\n\n /**\n * @notice Scales a number based on a difference in decimals from a default.\n * @param decimals The new decimal precision.\n */\n function scale(uint8 decimals) internal pure returns (uint256) {\n unchecked {\n if (decimals == DEFAULT_DECIMALS) {\n return DEFAULT_SCALE;\n } else {\n return 10 ** (DEFAULT_DECIMALS - decimals);\n }\n }\n }\n\n /**\n * @notice Adjusts a scaled amount to the token decimal scale.\n * @param amount The scaled amount.\n * @param scaleFactor The scaling factor to adjust by.\n * @param ceil Whether to round up (true) or down (false).\n */\n function ammScaleToTokenScale(uint256 amount, uint256 scaleFactor, bool ceil) internal pure returns (uint256 z) {\n unchecked {\n if (scaleFactor == DEFAULT_SCALE || amount == 0) {\n return amount;\n } else {\n if (!ceil) return amount / scaleFactor;\n assembly (\"memory-safe\") {\n z := add(div(sub(amount, 1), scaleFactor), 1)\n }\n }\n }\n }\n\n /**\n * @notice Adjusts a token amount to the D18 AMM scale.\n * @param amount The amount in token scale.\n * @param scaleFactor The scale factor for adjustment.\n */\n function tokenScaleToAmmScale(uint256 amount, uint256 scaleFactor) internal pure returns (uint256) {\n if (scaleFactor == DEFAULT_SCALE) {\n return amount;\n } else {\n return amount * scaleFactor;\n }\n }\n\n /**\n * @notice Returns the absolute value of a signed 32-bit integer.\n * @param x The integer to take the absolute value of.\n */\n function abs32(int32 x) internal pure returns (uint32) {\n unchecked {\n return uint32(x < 0 ? -x : x);\n }\n }\n\n /**\n * @notice Returns the absolute value of a signed 256-bit integer.\n * @param x The integer to take the absolute value of.\n */\n function abs(int256 x) internal pure returns (uint256) {\n unchecked {\n return uint256(x < 0 ? -x : x);\n }\n }\n\n /**\n * @notice Calculates the integer square root of a uint256 rounded down.\n * @param x The number to take the square root of.\n */\n function sqrt(uint256 x) internal pure returns (uint256 z) {\n // from https://github.com/transmissions11/solmate/blob/e8f96f25d48fe702117ce76c79228ca4f20206cb/src/utils/FixedPointMathLib.sol\n assembly (\"memory-safe\") {\n let y := x\n z := 181\n\n if iszero(lt(y, 0x10000000000000000000000000000000000)) {\n y := shr(128, y)\n z := shl(64, z)\n }\n if iszero(lt(y, 0x1000000000000000000)) {\n y := shr(64, y)\n z := shl(32, z)\n }\n if iszero(lt(y, 0x10000000000)) {\n y := shr(32, y)\n z := shl(16, z)\n }\n if iszero(lt(y, 0x1000000)) {\n y := shr(16, y)\n z := shl(8, z)\n }\n\n z := shr(18, mul(z, add(y, 65536)))\n\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n\n z := sub(z, lt(div(x, z), z))\n }\n }\n\n /**\n * @notice Computes the floor of a D8-scaled number as an int32, ignoring\n * potential overflow in the cast.\n * @param val The D8-scaled number.\n */\n function floorD8Unchecked(int256 val) internal pure returns (int32) {\n int32 val32;\n bool check;\n unchecked {\n val32 = int32(val / INT_ONE_D8);\n check = (val < 0 && val % INT_ONE_D8 != 0);\n }\n return check ? val32 - 1 : val32;\n }\n}" + }, + "lib/rooster/v2-common/libraries/TickMath.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport {Math as OzMath} from \"../../openzeppelin-custom/contracts/utils/math/Math.sol\";\nimport {Math} from \"./Math.sol\";\nimport {MAX_TICK, ONE} from \"./Constants.sol\";\n\n/**\n * @notice Math functions related to tick operations.\n */\n// slither-disable-start divide-before-multiply\nlibrary TickMath {\n using Math for uint256;\n\n error TickMaxExceeded(int256 tick);\n\n /**\n * @notice Compute the lower and upper sqrtPrice of a tick.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n */\n function tickSqrtPrices(\n uint256 tickSpacing,\n int32 _tick\n ) internal pure returns (uint256 sqrtLowerPrice, uint256 sqrtUpperPrice) {\n unchecked {\n sqrtLowerPrice = tickSqrtPrice(tickSpacing, _tick);\n sqrtUpperPrice = tickSqrtPrice(tickSpacing, _tick + 1);\n }\n }\n\n /**\n * @notice Compute the base tick value from the pool tick and the\n * tickSpacing. Revert if base tick is beyond the max tick boundary.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n */\n function subTickIndex(uint256 tickSpacing, int32 _tick) internal pure returns (uint32 subTick) {\n subTick = Math.abs32(_tick);\n subTick *= uint32(tickSpacing);\n if (subTick > MAX_TICK) {\n revert TickMaxExceeded(_tick);\n }\n }\n\n /**\n * @notice Calculate the square root price for a given tick and tick spacing.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n * @return _result The square root price.\n */\n function tickSqrtPrice(uint256 tickSpacing, int32 _tick) internal pure returns (uint256 _result) {\n unchecked {\n uint256 tick = subTickIndex(tickSpacing, _tick);\n\n uint256 ratio = tick & 0x1 != 0 ? 0xfffcb933bd6fad9d3af5f0b9f25db4d6 : 0x100000000000000000000000000000000;\n if (tick & 0x2 != 0) ratio = (ratio * 0xfff97272373d41fd789c8cb37ffcaa1c) >> 128;\n if (tick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656ac9229c67059486f389) >> 128;\n if (tick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e81259b3cddc7a064941) >> 128;\n if (tick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f67b19e8887e0bd251eb7) >> 128;\n if (tick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98cd2e57b660be99eb2c4a) >> 128;\n if (tick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c9838804e327cb417cafcb) >> 128;\n if (tick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99d51e2cc356c2f617dbe0) >> 128;\n if (tick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900aecf64236ab31f1f9dcb5) >> 128;\n if (tick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac4d9194200696907cf2e37) >> 128;\n if (tick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b88206f8abe8a3b44dd9be) >> 128;\n if (tick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c578ef4f1d17b2b235d480) >> 128;\n if (tick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd254ee83bdd3f248e7e785e) >> 128;\n if (tick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d8f7dd10e744d913d033333) >> 128;\n if (tick & 0x4000 != 0) ratio = (ratio * 0x70d869a156ddd32a39e257bc3f50aa9b) >> 128;\n if (tick & 0x8000 != 0) ratio = (ratio * 0x31be135f97da6e09a19dc367e3b6da40) >> 128;\n if (tick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7e5a9780b0cc4e25d61a56) >> 128;\n if (tick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedbcb3a6ccb7ce618d14225) >> 128;\n if (tick & 0x40000 != 0) ratio = (ratio * 0x2216e584f630389b2052b8db590e) >> 128;\n if (_tick > 0) ratio = type(uint256).max / ratio;\n _result = (ratio * ONE) >> 128;\n }\n }\n\n /**\n * @notice Calculate liquidity of a tick.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n */\n function getTickL(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n ) internal pure returns (uint256 liquidity) {\n // known:\n // - sqrt price values are different\n // - reserveA and reserveB fit in 128 bit\n // - sqrt price is in (1e-7, 1e7)\n // - D18 max for uint256 is 1.15e59\n // - D18 min is 1e-18\n\n unchecked {\n // diff is in (5e-12, 4e6); max tick spacing is 10_000\n uint256 diff = sqrtUpperTickPrice - sqrtLowerTickPrice;\n\n // Need to maximize precision by shifting small values A and B up so\n // that they use more of the available bit range. Two constraints to\n // consider: we need A * B * diff / sqrtPrice to be bigger than 1e-18\n // when the bump is not in play. This constrains the threshold for\n // bumping to be at least 77 bit; ie, either a or b needs 2^77 which\n // means that term A * B * diff / sqrtPrice > 1e-18.\n //\n // At the other end, the second constraint is that b^2 needs to fit in\n // a 256-bit number, so, post bump, the max reserve value needs to be\n // less than 6e22. With a 78-bit threshold and a 57-bit bump, we have A\n // and B are in (1.4e-1, 4.4e22 (2^(78+57))) with bump, and one of A or\n // B is at least 2^78 without the bump, but the other reserve value may\n // be as small as 1 wei.\n uint256 precisionBump = 0;\n if ((reserveA >> 78) == 0 && (reserveB >> 78) == 0) {\n precisionBump = 57;\n reserveA <<= precisionBump;\n reserveB <<= precisionBump;\n }\n\n if (reserveB == 0) return Math.divDown(reserveA, diff) >> precisionBump;\n if (reserveA == 0)\n return Math.mulDivDown(reserveB.mulDown(sqrtLowerTickPrice), sqrtUpperTickPrice, diff) >> precisionBump;\n\n // b is in (7.2e-9 (2^57 / 1e7 / 2), 2.8e29 (2^(78+57) * 1e7 / 2)) with bump\n // b is in a subset of the same range without bump\n uint256 b = (reserveA.divDown(sqrtUpperTickPrice) + reserveB.mulDown(sqrtLowerTickPrice)) >> 1;\n\n // b^2 is in (5.1e-17, 4.8e58); and will not overflow on either end;\n // A*B is in (3e-13 (2^78 / 1e18 * 1e-18), 1.9e45) without bump and is in a subset range with bump\n // A*B*diff/sqrtUpper is in (1.5e-17 (3e-13 * 5e-12 * 1e7), 7.6e58);\n\n // Since b^2 is at the upper edge of the precision range, we are not\n // able to multiply the argument of the sqrt by 1e18, instead, we move\n // this factor outside of the sqrt. The resulting loss of precision\n // means that this liquidity value is a lower bound on the tick\n // liquidity\n return\n OzMath.mulDiv(\n b +\n Math.sqrt(\n (OzMath.mulDiv(b, b, ONE) +\n OzMath.mulDiv(reserveB.mulFloor(reserveA), diff, sqrtUpperTickPrice))\n ) *\n 1e9,\n sqrtUpperTickPrice,\n diff\n ) >> precisionBump;\n }\n }\n\n /**\n * @notice Calculate square root price of a tick. Returns left edge of the\n * tick if the tick has no reserves.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n * @return sqrtPrice The calculated square root price.\n */\n function getSqrtPrice(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice,\n uint256 liquidity\n ) internal pure returns (uint256 sqrtPrice) {\n unchecked {\n if (reserveA == 0) {\n return sqrtLowerTickPrice;\n }\n if (reserveB == 0) {\n return sqrtUpperTickPrice;\n }\n sqrtPrice = Math.sqrt(\n ONE *\n (reserveA + liquidity.mulDown(sqrtLowerTickPrice)).divDown(\n reserveB + liquidity.divDown(sqrtUpperTickPrice)\n )\n );\n sqrtPrice = Math.boundValue(sqrtPrice, sqrtLowerTickPrice, sqrtUpperTickPrice);\n }\n }\n\n /**\n * @notice Calculate square root price of a tick. Returns left edge of the\n * tick if the tick has no reserves.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n * @return sqrtPrice The calculated square root price.\n * @return liquidity The calculated liquidity.\n */\n function getTickSqrtPriceAndL(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n ) internal pure returns (uint256 sqrtPrice, uint256 liquidity) {\n liquidity = getTickL(reserveA, reserveB, sqrtLowerTickPrice, sqrtUpperTickPrice);\n sqrtPrice = getSqrtPrice(reserveA, reserveB, sqrtLowerTickPrice, sqrtUpperTickPrice, liquidity);\n }\n}\n// slither-disable-end divide-before-multiply" + }, + "solidity-bytes-utils/contracts/BytesLib.sol": { + "content": "// SPDX-License-Identifier: Unlicense\n/*\n * @title Solidity Bytes Arrays Utils\n * @author Gonçalo Sá \n *\n * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.\n * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.\n */\npragma solidity >=0.8.0 <0.9.0;\n\n\nlibrary BytesLib {\n function concat(\n bytes memory _preBytes,\n bytes memory _postBytes\n )\n internal\n pure\n returns (bytes memory)\n {\n bytes memory tempBytes;\n\n assembly {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // Store the length of the first bytes array at the beginning of\n // the memory for tempBytes.\n let length := mload(_preBytes)\n mstore(tempBytes, length)\n\n // Maintain a memory counter for the current write location in the\n // temp bytes array by adding the 32 bytes for the array length to\n // the starting location.\n let mc := add(tempBytes, 0x20)\n // Stop copying when the memory counter reaches the length of the\n // first bytes array.\n let end := add(mc, length)\n\n for {\n // Initialize a copy counter to the start of the _preBytes data,\n // 32 bytes into its memory.\n let cc := add(_preBytes, 0x20)\n } lt(mc, end) {\n // Increase both counters by 32 bytes each iteration.\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // Write the _preBytes data into the tempBytes memory 32 bytes\n // at a time.\n mstore(mc, mload(cc))\n }\n\n // Add the length of _postBytes to the current length of tempBytes\n // and store it as the new length in the first 32 bytes of the\n // tempBytes memory.\n length := mload(_postBytes)\n mstore(tempBytes, add(length, mload(tempBytes)))\n\n // Move the memory counter back from a multiple of 0x20 to the\n // actual end of the _preBytes data.\n mc := end\n // Stop copying when the memory counter reaches the new combined\n // length of the arrays.\n end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n // Update the free-memory pointer by padding our last write location\n // to 32 bytes: add 31 bytes to the end of tempBytes to move to the\n // next 32 byte block, then round down to the nearest multiple of\n // 32. If the sum of the length of the two arrays is zero then add\n // one before rounding down to leave a blank 32 bytes (the length block with 0).\n mstore(0x40, and(\n add(add(end, iszero(add(length, mload(_preBytes)))), 31),\n not(31) // Round down to the nearest 32 bytes.\n ))\n }\n\n return tempBytes;\n }\n\n function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {\n assembly {\n // Read the first 32 bytes of _preBytes storage, which is the length\n // of the array. (We don't need to use the offset into the slot\n // because arrays use the entire slot.)\n let fslot := sload(_preBytes.slot)\n // Arrays of 31 bytes or less have an even value in their slot,\n // while longer arrays have an odd value. The actual length is\n // the slot divided by two for odd values, and the lowest order\n // byte divided by two for even values.\n // If the slot is even, bitwise and the slot with 255 and divide by\n // two to get the length. If the slot is odd, bitwise and the slot\n // with -1 and divide by two.\n let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)\n let mlength := mload(_postBytes)\n let newlength := add(slength, mlength)\n // slength can contain both the length and contents of the array\n // if length < 32 bytes so let's prepare for that\n // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage\n switch add(lt(slength, 32), lt(newlength, 32))\n case 2 {\n // Since the new array still fits in the slot, we just need to\n // update the contents of the slot.\n // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length\n sstore(\n _preBytes.slot,\n // all the modifications to the slot are inside this\n // next block\n add(\n // we can just add to the slot contents because the\n // bytes we want to change are the LSBs\n fslot,\n add(\n mul(\n div(\n // load the bytes from memory\n mload(add(_postBytes, 0x20)),\n // zero all bytes to the right\n exp(0x100, sub(32, mlength))\n ),\n // and now shift left the number of bytes to\n // leave space for the length in the slot\n exp(0x100, sub(32, newlength))\n ),\n // increase length by the double of the memory\n // bytes length\n mul(mlength, 2)\n )\n )\n )\n }\n case 1 {\n // The stored value fits in the slot, but the combined value\n // will exceed it.\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n let sc := add(keccak256(0x0, 0x20), div(slength, 32))\n\n // save new length\n sstore(_preBytes.slot, add(mul(newlength, 2), 1))\n\n // The contents of the _postBytes array start 32 bytes into\n // the structure. Our first read should obtain the `submod`\n // bytes that can fit into the unused space in the last word\n // of the stored array. To get this, we read 32 bytes starting\n // from `submod`, so the data we read overlaps with the array\n // contents by `submod` bytes. Masking the lowest-order\n // `submod` bytes allows us to add that value directly to the\n // stored value.\n\n let submod := sub(32, slength)\n let mc := add(_postBytes, submod)\n let end := add(_postBytes, mlength)\n let mask := sub(exp(0x100, submod), 1)\n\n sstore(\n sc,\n add(\n and(\n fslot,\n 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00\n ),\n and(mload(mc), mask)\n )\n )\n\n for {\n mc := add(mc, 0x20)\n sc := add(sc, 1)\n } lt(mc, end) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n sstore(sc, mload(mc))\n }\n\n mask := exp(0x100, sub(mc, end))\n\n sstore(sc, mul(div(mload(mc), mask), mask))\n }\n default {\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n // Start copying to the last used word of the stored array.\n let sc := add(keccak256(0x0, 0x20), div(slength, 32))\n\n // save new length\n sstore(_preBytes.slot, add(mul(newlength, 2), 1))\n\n // Copy over the first `submod` bytes of the new data as in\n // case 1 above.\n let slengthmod := mod(slength, 32)\n let mlengthmod := mod(mlength, 32)\n let submod := sub(32, slengthmod)\n let mc := add(_postBytes, submod)\n let end := add(_postBytes, mlength)\n let mask := sub(exp(0x100, submod), 1)\n\n sstore(sc, add(sload(sc), and(mload(mc), mask)))\n\n for {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } lt(mc, end) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n sstore(sc, mload(mc))\n }\n\n mask := exp(0x100, sub(mc, end))\n\n sstore(sc, mul(div(mload(mc), mask), mask))\n }\n }\n }\n\n function slice(\n bytes memory _bytes,\n uint256 _start,\n uint256 _length\n )\n internal\n pure\n returns (bytes memory)\n {\n // We're using the unchecked block below because otherwise execution ends \n // with the native overflow error code.\n unchecked {\n require(_length + 31 >= _length, \"slice_overflow\");\n }\n require(_bytes.length >= _start + _length, \"slice_outOfBounds\");\n\n bytes memory tempBytes;\n\n assembly {\n switch iszero(_length)\n case 0 {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // The first word of the slice result is potentially a partial\n // word read from the original array. To read it, we calculate\n // the length of that partial word and start copying that many\n // bytes into the array. The first word we copy will start with\n // data we don't care about, but the last `lengthmod` bytes will\n // land at the beginning of the contents of the new array. When\n // we're done copying, we overwrite the full first word with\n // the actual length of the slice.\n let lengthmod := and(_length, 31)\n\n // The multiplication in the next line is necessary\n // because when slicing multiples of 32 bytes (lengthmod == 0)\n // the following copy loop was copying the origin's length\n // and then ending prematurely not copying everything it should.\n let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))\n let end := add(mc, _length)\n\n for {\n // The multiplication in the next line has the same exact purpose\n // as the one above.\n let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n mstore(tempBytes, _length)\n\n //update free-memory pointer\n //allocating the array padded to 32 bytes like the compiler does now\n mstore(0x40, and(add(mc, 31), not(31)))\n }\n //if we want a zero-length slice let's just return a zero-length array\n default {\n tempBytes := mload(0x40)\n //zero out the 32 bytes slice we are about to return\n //we need to do it because Solidity does not garbage collect\n mstore(tempBytes, 0)\n\n mstore(0x40, add(tempBytes, 0x20))\n }\n }\n\n return tempBytes;\n }\n\n function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {\n require(_bytes.length >= _start + 20, \"toAddress_outOfBounds\");\n address tempAddress;\n\n assembly {\n tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)\n }\n\n return tempAddress;\n }\n\n function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {\n require(_bytes.length >= _start + 1 , \"toUint8_outOfBounds\");\n uint8 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x1), _start))\n }\n\n return tempUint;\n }\n\n function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {\n require(_bytes.length >= _start + 2, \"toUint16_outOfBounds\");\n uint16 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x2), _start))\n }\n\n return tempUint;\n }\n\n function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {\n require(_bytes.length >= _start + 4, \"toUint32_outOfBounds\");\n uint32 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x4), _start))\n }\n\n return tempUint;\n }\n\n function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {\n require(_bytes.length >= _start + 8, \"toUint64_outOfBounds\");\n uint64 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x8), _start))\n }\n\n return tempUint;\n }\n\n function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {\n require(_bytes.length >= _start + 12, \"toUint96_outOfBounds\");\n uint96 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0xc), _start))\n }\n\n return tempUint;\n }\n\n function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {\n require(_bytes.length >= _start + 16, \"toUint128_outOfBounds\");\n uint128 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x10), _start))\n }\n\n return tempUint;\n }\n\n function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {\n require(_bytes.length >= _start + 32, \"toUint256_outOfBounds\");\n uint256 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x20), _start))\n }\n\n return tempUint;\n }\n\n function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {\n require(_bytes.length >= _start + 32, \"toBytes32_outOfBounds\");\n bytes32 tempBytes32;\n\n assembly {\n tempBytes32 := mload(add(add(_bytes, 0x20), _start))\n }\n\n return tempBytes32;\n }\n\n function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {\n bool success = true;\n\n assembly {\n let length := mload(_preBytes)\n\n // if lengths don't match the arrays are not equal\n switch eq(length, mload(_postBytes))\n case 1 {\n // cb is a circuit breaker in the for loop since there's\n // no said feature for inline assembly loops\n // cb = 1 - don't breaker\n // cb = 0 - break\n let cb := 1\n\n let mc := add(_preBytes, 0x20)\n let end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n // the next line is the loop condition:\n // while(uint256(mc < end) + cb == 2)\n } eq(add(lt(mc, end), cb), 2) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // if any of these checks fails then arrays are not equal\n if iszero(eq(mload(mc), mload(cc))) {\n // unsuccess:\n success := 0\n cb := 0\n }\n }\n }\n default {\n // unsuccess:\n success := 0\n }\n }\n\n return success;\n }\n\n function equalStorage(\n bytes storage _preBytes,\n bytes memory _postBytes\n )\n internal\n view\n returns (bool)\n {\n bool success = true;\n\n assembly {\n // we know _preBytes_offset is 0\n let fslot := sload(_preBytes.slot)\n // Decode the length of the stored array like in concatStorage().\n let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)\n let mlength := mload(_postBytes)\n\n // if lengths don't match the arrays are not equal\n switch eq(slength, mlength)\n case 1 {\n // slength can contain both the length and contents of the array\n // if length < 32 bytes so let's prepare for that\n // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage\n if iszero(iszero(slength)) {\n switch lt(slength, 32)\n case 1 {\n // blank the last byte which is the length\n fslot := mul(div(fslot, 0x100), 0x100)\n\n if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {\n // unsuccess:\n success := 0\n }\n }\n default {\n // cb is a circuit breaker in the for loop since there's\n // no said feature for inline assembly loops\n // cb = 1 - don't breaker\n // cb = 0 - break\n let cb := 1\n\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n let sc := keccak256(0x0, 0x20)\n\n let mc := add(_postBytes, 0x20)\n let end := add(mc, mlength)\n\n // the next line is the loop condition:\n // while(uint256(mc < end) + cb == 2)\n for {} eq(add(lt(mc, end), cb), 2) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n if iszero(eq(sload(sc), mload(mc))) {\n // unsuccess:\n success := 0\n cb := 0\n }\n }\n }\n }\n }\n default {\n // unsuccess:\n success := 0\n }\n }\n\n return success;\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/ab900e39719d5c31fd5099bb0b06bb5a.json b/contracts/deployments/hoodi/solcInputs/ab900e39719d5c31fd5099bb0b06bb5a.json new file mode 100644 index 0000000000..7ddf98285a --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/ab900e39719d5c31fd5099bb0b06bb5a.json @@ -0,0 +1,102 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to retrieve beacon block roots.\n * @author Origin Protocol Inc\n */\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the beacon block root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Roots contract to get the parent block root.\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to request full or partial withdrawals from validators on the beacon chain.\n * @author Origin Protocol Inc\n */\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n /// @notice Requests a partial withdrawal for a given validator public key and amount.\n /// @param validatorPubKey The public key of the validator to withdraw from\n /// @param amount The amount of ETH to withdraw\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n /// @notice Gets fee for withdrawal requests contract on Beacon chain\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof,\n bytes calldata validatorPubKeyProof\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint64 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes32 pubKeyHash,\n bytes calldata firstPendingDepositProof\n ) external view returns (bool isEmptyDepositQueue);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with\n /// `platformAddress` not used so empty address\n /// `vaultAddress` the address of the OETH Vault contract\n /// @param _wethAddress Address of the WETH Token contract\n /// @param _ssvToken Address of the SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconProofs,\n _beaconGenesisTimestamp\n )\n {\n SSV_TOKEN = _ssvToken;\n\n // Make sure nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// @param _rewardTokenAddresses Not used so empty array\n /// @param _assets Not used so empty array\n /// @param _pTokens Not used so empty array\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n safeApproveAllTokens();\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH that was transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// @param _recipient Address to receive withdrawn assets.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH to withdraw.\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(WETH, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Accounts for all the assets managed by this strategy which includes:\n /// 1. The current WETH in this strategy contract\n /// 2. The last verified ETH balance, total deposits and total validator balances\n /// @param _asset Address of WETH asset.\n /// @return balance Total value in ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by the strategy.\n /// @param _asset The address of the WETH token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for validator registration.\n function safeApproveAllTokens() public override {\n // Approves the SSV Network contract to transfer SSV tokens when validators are registered\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n * The new ETH will be accounted for in `checkBalance` after the next snapBalances and verifyBalances txs.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n /// @notice is not supported for this strategy as there is no platform token.\n function setPTokenAddress(address, address) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @notice is not supported for this strategy as there is no platform token.\n function removePToken(uint256) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @dev This strategy does not use a platform token like the old Aave and Compound strategies.\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instead of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract.\n /// Withdrawals from validators also accumulate as ETH in this strategy contract.\n /// It's too complex to separate the rewards from withdrawals so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can now regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingView.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/**\n * @title Viewing contract for the Compounding Staking Strategy.\n * @author Origin Protocol Inc\n */\ncontract CompoundingStakingStrategyView {\n /// @notice The address of the Compounding Staking Strategy contract\n CompoundingValidatorManager public immutable stakingStrategy;\n\n constructor(address _stakingStrategy) {\n stakingStrategy = CompoundingValidatorManager(_stakingStrategy);\n }\n\n struct ValidatorView {\n bytes32 pubKeyHash;\n uint64 index;\n CompoundingValidatorManager.ValidatorState state;\n }\n\n struct DepositView {\n uint256 depositID;\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint256 withdrawableEpoch;\n }\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return validators An array of `ValidatorView` containing the public key hash, validator index and state.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorView[] memory validators)\n {\n uint256 validatorCount = stakingStrategy.verifiedValidatorsLength();\n validators = new ValidatorView[](validatorCount);\n for (uint256 i = 0; i < validatorCount; ++i) {\n bytes32 pubKeyHash = stakingStrategy.verifiedValidators(i);\n (\n CompoundingValidatorManager.ValidatorState state,\n uint64 index\n ) = stakingStrategy.validator(pubKeyHash);\n validators[i] = ValidatorView({\n pubKeyHash: pubKeyHash,\n index: index,\n state: state\n });\n }\n }\n\n /// @notice Returns the deposits that are still to be verified.\n /// These may or may not have been processed by the beacon chain.\n /// @return pendingDeposits An array of `DepositView` containing the deposit ID, public key hash,\n /// amount in Gwei and the slot of the deposit.\n function getPendingDeposits()\n external\n view\n returns (DepositView[] memory pendingDeposits)\n {\n uint256 depositsCount = stakingStrategy.depositListLength();\n pendingDeposits = new DepositView[](depositsCount);\n for (uint256 i = 0; i < depositsCount; ++i) {\n (\n bytes32 pubKeyHash,\n uint64 amountGwei,\n uint64 slot,\n ,\n ,\n uint256 withdrawableEpoch\n ) = stakingStrategy.deposits(stakingStrategy.depositList(i));\n pendingDeposits[i] = DepositView({\n depositID: stakingStrategy.depositList(i),\n pubKeyHash: pubKeyHash,\n amountGwei: amountGwei,\n slot: slot,\n withdrawableEpoch: withdrawableEpoch\n });\n }\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable {\n using SafeERC20 for IERC20;\n\n /// @dev The amount of ETH in wei that is required for a deposit to a new validator.\n /// Initially this is 32 ETH, but will be reduced to 1 ETH after P2P's APIs have been updated\n /// to support deposits of 1 ETH.\n uint256 internal constant DEPOSIT_AMOUNT_WEI = 32 ether;\n /// @dev The maximum number of deposits that are waiting to be verified as processed on the beacon chain.\n uint256 internal constant MAX_DEPOSITS = 12;\n /// @dev The maximum number of validators that can be verified.\n uint256 internal constant MAX_VERIFIED_VALIDATORS = 48;\n /// @dev The default withdrawable epoch value on the Beacon chain.\n /// A value in the far future means the validator is not exiting.\n uint64 internal constant FAR_FUTURE_EPOCH = type(uint64).max;\n /// @dev The number of seconds between each beacon chain slot.\n uint64 internal constant SLOT_DURATION = 12;\n /// @dev The number of slots in each beacon chain epoch.\n uint64 internal constant SLOTS_PER_EPOCH = 32;\n /// @dev Minimum time in seconds to allow snapped balances to be verified.\n /// Set to 1 epoch as the pending deposits only changes every epoch.\n /// That's also enough time to generate the proofs and call `verifyBalances`.\n uint64 internal constant SNAP_BALANCES_DELAY =\n SLOTS_PER_EPOCH * SLOT_DURATION;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n /// @notice The timestamp of the Beacon chain genesis.\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\n uint64 public immutable BEACON_GENESIS_TIMESTAMP;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// Deposit data for new compounding validators.\n\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified and is ready to be staked\n }\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountWei Amount of ETH in wei that has been deposited to the beacon chain deposit contract\n /// @param blockNumber Block number when the deposit was made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either PENDING or PROVEN\n\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint32 depositIndex;\n DepositStatus status;\n uint64 withdrawableEpoch;\n }\n /// @notice Restricts to only one deposit to an unverified validator at a time.\n /// This is to limit front-running attacks of deposits to the beacon chain contract.\n\n bool public firstDeposit;\n /// @notice Unique identifier of the next validator deposit.\n uint128 public nextDepositID;\n /// @notice Mapping of the deposit ID to the deposit data\n mapping(uint256 => DepositData) public deposits;\n /// @notice List of strategy deposit IDs to a validator.\n /// The list can be for deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n /// The list may not be ordered by time of deposit.\n /// Removed deposits will move the last deposit to the removed index.\n uint256[] public depositList;\n\n // Validator data\n\n enum ValidatorState {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITING, // The validator has been requested to exit or has been verified as forced exit\n EXITED, // The validator has been verified to have a zero balance\n REMOVED, // validator has funds withdrawn to the EigenPod and is removed from the SSV\n INVALID // The validator has been front-run and the withdrawal address is not this strategy\n }\n\n struct ValidatorData {\n ValidatorState state;\n uint64 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n\n bytes32[] public verifiedValidators;\n /// @notice Mapping of the hash of the validator's public key to the validator state and index.\n /// Uses the Beacon chain hashing for BLSPubkey which is sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => ValidatorData) public validator;\n\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n uint64 timestamp;\n uint128 ethBalance;\n }\n /// @notice Mapping of the block root to the balances at that slot\n\n mapping(bytes32 => Balances) public snappedBalances;\n /// @notice The timestamp of the last snapshot taken\n uint64 public lastSnapTimestamp;\n /// @notice The last verified ETH balance of the strategy\n uint128 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[50] private __gap;\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event FirstDepositReset();\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n uint256 indexed depositID,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint64 indexed validatorIndex\n );\n event ValidatorInvalid(bytes32 indexed pubKeyHash);\n event DepositVerified(uint256 indexed depositID, uint256 amountWei);\n event DepositToValidatorExiting(\n uint256 indexed depositID,\n uint256 amountWei,\n uint64 withdrawableEpoch\n );\n event DepositValidatorExited(uint256 indexed depositID, uint256 amountWei);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(bytes32 indexed blockRoot, uint256 ethBalance);\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 ethBalance\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_PROOFS = _beaconProofs;\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\n\n require(\n block.timestamp > _beaconGenesisTimestamp,\n \"Invalid genesis timestamp\"\n );\n }\n\n /**\n *\n * Admin Functions\n *\n */\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Reset the `firstDeposit` flag to false so deposits to unverified validators can be made again.\n function resetFirstDeposit() external onlyGovernor {\n require(firstDeposit, \"No first deposit\");\n\n firstDeposit = false;\n\n emit FirstDepositReset();\n }\n\n /**\n *\n * Validator Management\n *\n */\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validator[pubKeyHash].state == ValidatorState.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validator[pubKeyHash].state = ValidatorState.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n struct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n }\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validatorStakeData validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validatorStakeData,\n uint64 depositAmountGwei\n ) external onlyRegistrator {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(depositList.length < MAX_DEPOSITS, \"Max deposits\");\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validatorStakeData.pubkey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.VERIFIED),\n \"Not registered or verified\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n if (currentState == ValidatorState.REGISTERED) {\n // Can only have one pending deposit to an unverified validator at a time.\n // This is to limit front-running deposit attacks to a single deposit.\n // The exiting deposit needs to be verified before another deposit can be made.\n // If there was a front-running attack, the validator needs to be verified as invalid\n // and the Governor calls `resetFirstDeposit` to set `firstDeposit` to false.\n require(!firstDeposit, \"Existing first deposit\");\n // Limits the amount of ETH that can be at risk from a front-running deposit attack.\n require(\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n // Limits the number of validator balance proofs to verifyBalances\n require(\n verifiedValidators.length + 1 < MAX_VERIFIED_VALIDATORS,\n \"Max validators\"\n );\n\n // Flag a deposit to an unverified validator so only no other deposits can be made\n // to an unverified validator.\n firstDeposit = true;\n }\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validatorStakeData.pubkey,\n withdrawalCredentials,\n validatorStakeData.signature,\n validatorStakeData.depositDataRoot\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == ValidatorState.REGISTERED) {\n validator[pubKeyHash].state = ValidatorState.STAKED;\n }\n\n /// After the Pectra upgrade the validators have a new restriction when proposing\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\n /// forward. Each slot is created at strict 12 second intervals and those slots can\n /// either have blocks attached to them or not. This way using the block.timestamp\n /// the slot number can easily be calculated.\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\n BEACON_GENESIS_TIMESTAMP) / SLOT_DURATION;\n\n // Store the deposit data for verifyDeposit and verifyBalances\n uint256 depositID = nextDepositID++;\n deposits[depositID] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n slot: depositSlot,\n depositIndex: SafeCast.toUint32(depositList.length),\n status: DepositStatus.PENDING,\n withdrawableEpoch: FAR_FUTURE_EPOCH\n });\n depositList.push(depositID);\n\n emit ETHStaked(\n pubKeyHash,\n depositID,\n validatorStakeData.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then only the amount in excess of 32 ETH will be withdrawn.\n /// Only the Registrator can call this function.\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\n /// If no ETH balance, the tx will revert.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n payable\n onlyRegistrator\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n require(\n currentState == ValidatorState.VERIFIED,\n \"Validator not verified\"\n );\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n // If a full withdrawal (validator exit)\n if (amountGwei == 0) {\n // Store the validator state as exiting so no more deposits can be made to it.\n validator[pubKeyHash].state = ValidatorState.EXITING;\n }\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove the validator from the SSV Cluster after:\n /// - the validator has been exited from `validatorWithdrawal` or slashed\n /// - the validator has incorrectly registered and can not be staked to\n /// - the initial deposit was front-run and the withdrawal address is not this strategy's address.\n /// Make sure `validatorWithdrawal` is called with a zero amount and the validator has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.EXITED ||\n currentState == ValidatorState.INVALID,\n \"Validator not regd or exited\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validator[pubKeyHash].state = ValidatorState.REMOVED;\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /**\n *\n * SSV Management\n *\n */\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be withdrawn from the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /**\n *\n * Beacon Chain Proofs\n *\n */\n\n /// @notice Verifies a validator's index to its public key.\n /// Adds to the list of verified validators if the validator's withdrawal address is this strategy's address.\n /// Marks the validator as invalid and removes the deposit if the withdrawal address is not this strategy's address.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param withdrawalAddress The withdrawal address of the validator which should be this strategy's address.\n /// If the withdrawal address is not this strategy's address, the initial deposit was front-run\n /// and the validator is marked as invalid.\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n address withdrawalAddress,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validator[pubKeyHash].state == ValidatorState.STAKED,\n \"Validator not staked\"\n );\n\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key.\n // Also verify the validator's withdrawal credential points to the `withdrawalAddress`.\n IBeaconProofs(BEACON_PROOFS).verifyValidator(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n withdrawalAddress\n );\n\n // If the initial deposit was front-run and the withdrawal address is not this strategy\n if (withdrawalAddress != address(this)) {\n validator[pubKeyHash] = ValidatorData({\n state: ValidatorState.INVALID,\n index: validatorIndex\n });\n\n // Find and remove the deposit as the funds can not be recovered\n uint256 depositCount = depositList.length;\n for (uint256 i = 0; i < depositCount; i++) {\n DepositData memory deposit = deposits[depositList[i]];\n if (deposit.pubKeyHash == pubKeyHash) {\n _removeDeposit(depositList[i], deposit);\n break;\n }\n }\n\n // Leave the `firstDeposit` flag as true so no more deposits to unverified validators can be made.\n // The Governor has to reset the `firstDeposit` to false before another deposit to\n // an unverified validator can be made.\n // The Governor can set a new `validatorRegistrator` if they suspect it has been compromised.\n\n emit ValidatorInvalid(pubKeyHash);\n return;\n }\n\n // Store the validator state as verified\n validator[pubKeyHash] = ValidatorData({\n state: ValidatorState.VERIFIED,\n index: validatorIndex\n });\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(pubKeyHash);\n\n // Reset the firstDeposit flag as the first deposit to an unverified validator has been verified.\n firstDeposit = false;\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n struct FirstPendingDepositProofData {\n uint64 slot;\n uint64 validatorIndex;\n bytes32 pubKeyHash;\n bytes pendingDepositPubKeyProof;\n bytes withdrawableEpochProof;\n bytes validatorPubKeyProof;\n }\n\n struct DepositValidatorProofData {\n uint64 withdrawableEpoch;\n bytes withdrawableEpochProof;\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n ///\n /// Important: this function has a limitation where the `verificationSlot` that is passed by the off-chain\n /// verifier requires a slot immediately after it to propose a block otherwise the `BeaconRoots.parentBlockRoot`\n /// will fail. This shouldn't be a problem, since by the current behaviour of beacon chain only 1%-3% slots\n /// don't propose a block.\n /// @param depositID The deposit ID emitted in `ETHStaked` from the `stakeEth` function.\n /// @param depositProcessedSlot Any slot on or after the strategy's deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\n /// set for the next block timestamp in 12 seconds time.\n /// @param firstDepositValidatorCreatedSlot The slot on or after when the validator of the first pending deposit\n /// was created on the beacon chain. This is used to verify the validator has not exited.\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n uint256 depositID,\n uint64 depositProcessedSlot,\n uint64 firstDepositValidatorCreatedSlot,\n FirstPendingDepositProofData calldata firstPendingDeposit,\n DepositValidatorProofData calldata strategyValidatorData\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositID];\n ValidatorData memory strategyValidator = validator[deposit.pubKeyHash];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n strategyValidator.state == ValidatorState.VERIFIED,\n \"Validator not verified\"\n );\n // The verification slot must be after the deposit's slot.\n // This is needed for when the deposit queue is empty.\n require(deposit.slot < depositProcessedSlot, \"Slot not after deposit\");\n require(\n depositProcessedSlot <= firstDepositValidatorCreatedSlot,\n \"Invalid verification slots\"\n );\n\n // Get the parent beacon block root of the next block which is the block root of the deposit verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 depositBlockRoot = BeaconRoots.parentBlockRoot(\n _calcNextBlockTimestamp(depositProcessedSlot)\n );\n\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n depositBlockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.pubKeyHash,\n firstPendingDeposit.pendingDepositPubKeyProof\n );\n\n // If the deposit queue is not empty\n if (!isDepositQueueEmpty) {\n // Get the parent beacon block root of the next block which is\n // the block root of the validator verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 validatorBlockRoot = BeaconRoots.parentBlockRoot(\n _calcNextBlockTimestamp(firstDepositValidatorCreatedSlot)\n );\n\n // Verify the validator of the first pending deposit is not exiting.\n // If it is exiting we can't be sure this deposit has not been postponed in the deposit queue.\n // Hence we can not verify if the strategy's deposit has been processed or not.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n validatorBlockRoot,\n firstPendingDeposit.validatorIndex,\n firstPendingDeposit.pubKeyHash,\n FAR_FUTURE_EPOCH,\n firstPendingDeposit.withdrawableEpochProof,\n firstPendingDeposit.validatorPubKeyProof\n );\n }\n\n // Verify the withdrawableEpoch on the validator of the strategy's deposit\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n depositBlockRoot,\n strategyValidator.index,\n strategyValidatorData.withdrawableEpoch,\n strategyValidatorData.withdrawableEpochProof\n );\n\n // If the validator is exiting because it has been slashed\n if (strategyValidatorData.withdrawableEpoch != FAR_FUTURE_EPOCH) {\n // Store the exit epoch in the deposit data\n deposit.withdrawableEpoch = strategyValidatorData.withdrawableEpoch;\n\n emit DepositToValidatorExiting(\n depositID,\n uint256(deposit.amountGwei) * 1 gwei,\n strategyValidatorData.withdrawableEpoch\n );\n\n validator[deposit.pubKeyHash].state = ValidatorState.EXITING;\n\n // Leave the deposit status as PENDING\n return;\n }\n\n // solhint-disable max-line-length\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the deposit queue is empty then our deposit must have been processed on the beacon chain.\n // The deposit slot can be zero for validators consolidating to a compounding validator or 0x01 validator\n // being promoted to a compounding one. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // We can not guarantee that the deposit has been processed in that case.\n // solhint-enable max-line-length\n require(\n deposit.slot < firstPendingDeposit.slot || isDepositQueueEmpty,\n \"Deposit likely not processed\"\n );\n\n // Remove the deposit now it has been verified as processed on the beacon chain.\n _removeDeposit(depositID, deposit);\n\n emit DepositVerified(depositID, uint256(deposit.amountGwei) * 1 gwei);\n }\n\n function _removeDeposit(uint256 depositID, DepositData memory deposit)\n internal\n {\n // After verifying the proof, update the contract storage\n deposits[depositID].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n uint256 lastDeposit = depositList[depositList.length - 1];\n depositList[deposit.depositIndex] = lastDeposit;\n deposits[lastDeposit].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositList.pop();\n }\n\n /// @dev Calculates the timestamp of the next execution block from the given slot.\n /// @param slot The beacon chain slot number used for merkle proof verification.\n function _calcNextBlockTimestamp(uint64 slot)\n internal\n view\n returns (uint64)\n {\n // Calculate the next block timestamp from the slot.\n return SLOT_DURATION * slot + BEACON_GENESIS_TIMESTAMP + SLOT_DURATION;\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block and beacon block root\n /// of the slot that is associated with the previous block.\n ///\n /// When snapping / verifying balance it is of a high importance that there is no\n /// miss-match in respect to ETH that is held by the contract and balances that are\n /// verified on the validators.\n ///\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\n /// constructing a block on the beacon chain consist of:\n /// - process_withdrawals: ETH is deducted from the validator's balance\n /// - process_execution_payload: immediately after the previous step executing all the\n /// transactions\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\n /// contained in the withdrawal credentials of the exited validators\n ///\n /// That means that balance increases which are part of the post-block execution state are\n /// done within the block, but the transaction that are contained within that block can not\n /// see / interact with the balance from the exited validators. Only transactions in the\n /// next block can do that.\n ///\n /// When snap balances is performed the state of the chain is snapped across 2 separate\n /// chain states:\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\n /// will be referred to as Y - 1 as it makes no difference in the argument\n ///\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\n /// miss-counting ETH or much more dangerous double counting of the ETH.\n ///\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\n /// - ETH balance in the contract on block X\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\n /// - ETH balance in validators that are active in slot Y - 1\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\n /// and have their balance visible to transactions in slot Y and corresponding block X\n /// (or sooner)\n ///\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\n ///\n /// *ETH balance in the contract on block X*\n ///\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\n ///\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\n /// would result in double counting the `stakedEth` since it would be present once in the\n /// snapped contract balance and the second time in deposit storage variables.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\n ///\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\n /// contract at block Z. The execution layer doesn't have direct access to the state of\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\n /// and could also be part of the validator balances. It does that by verifying that at\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\n /// the last snap till now all are still in queue. Which ensures they can not be part of\n /// the validator balances in later steps.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in validators that are active in slot Y - 1*\n ///\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\n /// correctness.\n ///\n /// This behaviour is correct.\n ///\n /// *The withdrawn validators*\n ///\n /// The withdrawn validators could have their balances deducted in any slot before slot\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\n /// look at the \"worst case scenario\" where the validator withdrawal is processed in the\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\n /// even if the validator exits at the latest possible time it is paramount that the ETH\n /// balance on the execution layer is recorded in the next block. Correctly accounting\n /// for the withdrawn ETH.\n ///\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\n /// validator balance verification as well as execution layer contract balance snap.\n ///\n /// This behaviour is correct.\n ///\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n function snapBalances() external {\n uint64 currentTimestamp = SafeCast.toUint64(block.timestamp);\n require(\n lastSnapTimestamp + SNAP_BALANCES_DELAY < currentTimestamp,\n \"Snap too soon\"\n );\n\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(currentTimestamp);\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the balances in the mapping\n snappedBalances[blockRoot] = Balances({\n timestamp: currentTimestamp,\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n // Store the snapped timestamp\n lastSnapTimestamp = currentTimestamp;\n\n emit BalancesSnapped(blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct BalanceProofs {\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n /// @param snapBlockRoot The beacon block root emitted from `snapBalance` in `BalancesSnapped`.\n /// @param balanceProofs a `BalanceProofs` struct containing the following:\n /// balancesContainerRoot - the merkle root of the balances container\n /// balancesContainerProof - The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// validatorBalanceLeaves - Array of leaf nodes containing the validator balance with three other balances.\n /// validatorBalanceProofs - Array of merkle proofs for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(\n bytes32 snapBlockRoot,\n uint64 validatorVerificationBlockTimestamp,\n FirstPendingDepositProofData calldata firstPendingDeposit,\n BalanceProofs calldata balanceProofs\n ) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalances[snapBlockRoot];\n // Check the balances are the latest\n require(lastSnapTimestamp > 0, \"No snapped balances\");\n require(balancesMem.timestamp == lastSnapTimestamp, \"Stale snap\");\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n balanceProofs.validatorBalanceProofs.length ==\n verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n balanceProofs.validatorBalanceLeaves.length ==\n verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n snapBlockRoot,\n balanceProofs.balancesContainerRoot,\n balanceProofs.balancesContainerProof\n );\n\n // for each validator in reserve order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n balanceProofs.balancesContainerRoot,\n balanceProofs.validatorBalanceLeaves[i],\n balanceProofs.validatorBalanceProofs[i],\n validator[verifiedValidators[i]].index\n );\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n // This could have been in VERIFIED or EXITING state\n validator[verifiedValidators[i]].state = ValidatorState\n .EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += validatorBalanceGwei * 1 gwei;\n }\n }\n\n uint256 depositsCount = depositList.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification.\n // This section is after the validator balance verifications so an exited validator will be marked\n // as EXITED before the deposits are verified. If there was a deposit to an exited validator\n // then the deposit can only be removed once the validator is fully exited.\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n snapBlockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.pubKeyHash,\n firstPendingDeposit.pendingDepositPubKeyProof\n );\n\n // If there are no deposits in the beacon chain queue then our deposits must have been processed.\n // If the deposits have been processed, each deposit will need to be verified with `verifyDeposit`\n require(!isDepositQueueEmpty, \"Deposits have been processed\");\n\n // The verification of the validator the first pending deposit is for must be on or after when\n // `snapBalances` was called.\n require(\n balancesMem.timestamp <= validatorVerificationBlockTimestamp,\n \"Invalid validator timestamp\"\n );\n\n // Verify the validator of the first pending deposit is not exiting by checking\n // the withdrawable epoch is far into the future.\n // If it is exiting we can't be sure this deposit has not been postponed in the deposit queue.\n // Hence we can not verify if the strategy's deposit has been processed or not.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n // Get the parent beacon block root of the next block which is\n // the block root of the validator verification slot.\n // This will revert if the slot after the verification slot was missed.\n BeaconRoots.parentBlockRoot(\n validatorVerificationBlockTimestamp\n ),\n firstPendingDeposit.validatorIndex,\n firstPendingDeposit.pubKeyHash,\n // Validator is not exiting\n FAR_FUTURE_EPOCH,\n firstPendingDeposit.withdrawableEpochProof,\n firstPendingDeposit.validatorPubKeyProof\n );\n\n // solhint-disable max-line-length\n // If a validator is converted from a sweeping validator to a compounding validator, any balance in excess\n // of the min 32 ETH is put in the pending deposit queue. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // This will have a slot value of zero unfortunately.\n // We can not prove the strategy's deposits are still pending with a zero slot value so revert the tx.\n // Another snapBalances will need to be taken that does not have consolidation deposits at the front of the\n // beacon chain deposit queue.\n // solhint-enable max-line-length\n require(\n firstPendingDeposit.slot > 0,\n \"Invalid first pending deposit\"\n );\n\n // Calculate the epoch at the time of the snapBalances\n uint64 verificationEpoch = (SafeCast.toUint64(\n balancesMem.timestamp\n ) - BEACON_GENESIS_TIMESTAMP) / (SLOT_DURATION * SLOTS_PER_EPOCH);\n\n // For each staking strategy's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n uint256 depositID = depositList[i];\n DepositData memory depositData = deposits[depositID];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit slot is before the slot of the staking strategy's deposit.\n // If the deposit has been processed, it will need to be verified with `verifyDeposit`.\n // OR the deposit is to an exiting validator so check it is still not withdrawable.\n // If the validator is not withdrawable, then the deposit can not have been processed yet.\n // If the validator is now withdrawable, then the deposit may have been processed. The strategy\n // now has to wait until the validator's balance is verified to be zero.\n // OR the validator has exited and the deposit is now verified as processed.\n require(\n firstPendingDeposit.slot < depositData.slot ||\n (verificationEpoch < depositData.withdrawableEpoch &&\n depositData.withdrawableEpoch !=\n FAR_FUTURE_EPOCH) ||\n validator[depositData.pubKeyHash].state ==\n ValidatorState.EXITED,\n \"Deposit likely processed\"\n );\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei += uint256(depositData.amountGwei) * 1 gwei;\n\n // Remove the deposit if the validator has exited.\n if (\n validator[depositData.pubKeyHash].state ==\n ValidatorState.EXITED\n ) {\n _removeDeposit(depositID, depositData);\n\n emit DepositValidatorExited(\n depositID,\n uint256(depositData.amountGwei) * 1 gwei\n );\n }\n }\n }\n\n // Store the verified balance in storage\n lastVerifiedEthBalance = SafeCast.toUint128(\n totalDepositsWei + totalValidatorBalance + balancesMem.ethBalance\n );\n // Reset the last snap timestamp so a new snapBalances has to be made\n lastSnapTimestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n require(pubKey.length == 48, \"Invalid public key length\");\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /**\n *\n * WETH and ETH Accounting\n *\n */\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n // The min is required as more WETH can be withdrawn than deposited\n // as the strategy earns consensus and execution rewards.\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance.\n // The ETH balance in this strategy contract can be more than the last verified ETH balance\n // due to partial withdrawals or full exits being processed by the beacon chain since the last snapBalances.\n // It can also happen from execution rewards (MEV) or ETH donations.\n lastVerifiedEthBalance -= SafeCast.toUint128(\n Math.min(uint256(lastVerifiedEthBalance), _ethAmount)\n );\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += SafeCast.toUint128(_wethAmount);\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /**\n *\n * View Functions\n *\n */\n\n /// @notice Returns the number of deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n function depositListLength() external view returns (uint256) {\n return depositList.length;\n }\n\n /// @notice Returns the number of verified validators.\n function verifiedValidatorsLength() external view returns (uint256) {\n return verifiedValidators.length;\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/b41880d69e00fb92c855f9f3ac56ef33.json b/contracts/deployments/hoodi/solcInputs/b41880d69e00fb92c855f9f3ac56ef33.json new file mode 100644 index 0000000000..27b65d4def --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/b41880d69e00fb92c855f9f3ac56ef33.json @@ -0,0 +1,102 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to retrieve beacon block roots.\n * @author Origin Protocol Inc\n */\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the beacon block root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Roots contract to get the parent block root.\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to request full or partial withdrawals from validators on the beacon chain.\n * @author Origin Protocol Inc\n */\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n /// @notice Requests a partial withdrawal for a given validator public key and amount.\n /// @param validatorPubKey The public key of the validator to withdraw from\n /// @param amount The amount of ETH to withdraw\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n /// @notice Gets fee for withdrawal requests contract on Beacon chain\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint40 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n bytes32 pubKeyHash,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof,\n bytes calldata validatorPubKeyProof\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint40 validatorIndex\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes32 pubKeyHash,\n bytes calldata firstPendingDepositPubKeyProof\n ) external view returns (bool isEmptyDepositQueue);\n\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view returns (bool isEmptyDepositQueue);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with\n /// `platformAddress` not used so empty address\n /// `vaultAddress` the address of the OETH Vault contract\n /// @param _wethAddress Address of the WETH Token contract\n /// @param _ssvToken Address of the SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconProofs,\n _beaconGenesisTimestamp\n )\n {\n SSV_TOKEN = _ssvToken;\n\n // Make sure nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// @param _rewardTokenAddresses Not used so empty array\n /// @param _assets Not used so empty array\n /// @param _pTokens Not used so empty array\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n safeApproveAllTokens();\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH that was transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// @param _recipient Address to receive withdrawn assets.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH to withdraw.\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(WETH, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Accounts for all the assets managed by this strategy which includes:\n /// 1. The current WETH in this strategy contract\n /// 2. The last verified ETH balance, total deposits and total validator balances\n /// @param _asset Address of WETH asset.\n /// @return balance Total value in ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by the strategy.\n /// @param _asset The address of the WETH token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for validator registration.\n function safeApproveAllTokens() public override {\n // Approves the SSV Network contract to transfer SSV tokens when validators are registered\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n * The new ETH will be accounted for in `checkBalance` after the next snapBalances and verifyBalances txs.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n /// @notice is not supported for this strategy as there is no platform token.\n function setPTokenAddress(address, address) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @notice is not supported for this strategy as there is no platform token.\n function removePToken(uint256) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @dev This strategy does not use a platform token like the old Aave and Compound strategies.\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instead of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract.\n /// Withdrawals from validators also accumulate as ETH in this strategy contract.\n /// It's too complex to separate the rewards from withdrawals so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can now regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingView.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/**\n * @title Viewing contract for the Compounding Staking Strategy.\n * @author Origin Protocol Inc\n */\ncontract CompoundingStakingStrategyView {\n /// @notice The address of the Compounding Staking Strategy contract\n CompoundingValidatorManager public immutable stakingStrategy;\n\n constructor(address _stakingStrategy) {\n stakingStrategy = CompoundingValidatorManager(_stakingStrategy);\n }\n\n struct ValidatorView {\n bytes32 pubKeyHash;\n uint64 index;\n CompoundingValidatorManager.ValidatorState state;\n }\n\n struct DepositView {\n uint256 depositID;\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint256 withdrawableEpoch;\n }\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return validators An array of `ValidatorView` containing the public key hash, validator index and state.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorView[] memory validators)\n {\n uint256 validatorCount = stakingStrategy.verifiedValidatorsLength();\n validators = new ValidatorView[](validatorCount);\n for (uint256 i = 0; i < validatorCount; ++i) {\n bytes32 pubKeyHash = stakingStrategy.verifiedValidators(i);\n (\n CompoundingValidatorManager.ValidatorState state,\n uint64 index\n ) = stakingStrategy.validator(pubKeyHash);\n validators[i] = ValidatorView({\n pubKeyHash: pubKeyHash,\n index: index,\n state: state\n });\n }\n }\n\n /// @notice Returns the deposits that are still to be verified.\n /// These may or may not have been processed by the beacon chain.\n /// @return pendingDeposits An array of `DepositView` containing the deposit ID, public key hash,\n /// amount in Gwei and the slot of the deposit.\n function getPendingDeposits()\n external\n view\n returns (DepositView[] memory pendingDeposits)\n {\n uint256 depositsCount = stakingStrategy.depositListLength();\n pendingDeposits = new DepositView[](depositsCount);\n for (uint256 i = 0; i < depositsCount; ++i) {\n (\n bytes32 pubKeyHash,\n uint64 amountGwei,\n uint64 slot,\n ,\n ,\n uint256 withdrawableEpoch\n ) = stakingStrategy.deposits(stakingStrategy.depositList(i));\n pendingDeposits[i] = DepositView({\n depositID: stakingStrategy.depositList(i),\n pubKeyHash: pubKeyHash,\n amountGwei: amountGwei,\n slot: slot,\n withdrawableEpoch: withdrawableEpoch\n });\n }\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable {\n using SafeERC20 for IERC20;\n\n /// @dev The amount of ETH in wei that is required for a deposit to a new validator.\n uint256 internal constant DEPOSIT_AMOUNT_WEI = 1 ether;\n /// @dev The maximum number of deposits that are waiting to be verified as processed on the beacon chain.\n uint256 internal constant MAX_DEPOSITS = 12;\n /// @dev The maximum number of validators that can be verified.\n uint256 internal constant MAX_VERIFIED_VALIDATORS = 48;\n /// @dev The default withdrawable epoch value on the Beacon chain.\n /// A value in the far future means the validator is not exiting.\n uint64 internal constant FAR_FUTURE_EPOCH = type(uint64).max;\n /// @dev The number of seconds between each beacon chain slot.\n uint64 internal constant SLOT_DURATION = 12;\n /// @dev The number of slots in each beacon chain epoch.\n uint64 internal constant SLOTS_PER_EPOCH = 32;\n /// @dev Minimum time in seconds to allow snapped balances to be verified.\n /// Set to 35 slots which is 3 slots more than 1 epoch (32 slots). Deposits get processed\n /// once per epoch. This larger than 1 epoch delay should achieve that `snapBalances` sometimes\n /// get called in the middle (or towards the end) of the epoch. Giving the off-chain script\n /// sufficient time after the end of the epoch to prepare the proofs and call `verifyBalances`.\n /// This is considering a malicious actor would keep calling `snapBalances` as frequent as possible\n /// to disturb our operations.\n uint64 internal constant SNAP_BALANCES_DELAY = 35 * SLOT_DURATION;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n /// @notice The timestamp of the Beacon chain genesis.\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\n uint64 public immutable BEACON_GENESIS_TIMESTAMP;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// @notice Deposit data for new compounding validators.\n /// @dev A `VERIFIED` deposit can mean 3 separate things:\n /// - a deposit has been processed by the beacon chain and shall be included in the\n /// balance of the next verifyBalances call\n /// - a deposit has been done to a slashed validator and has probably been recovered\n /// back to this strategy. Probably because we can not know for certain. This contract\n /// only detects when the validator has passed its withdrawal epoch. It is close to impossible\n /// to prove with Merkle Proofs that the postponed deposit this contract is responsible for\n /// creating is not present anymore in BeaconChain.state.pending_deposits. This in effect\n /// means that there might be a period where this contract thinks the deposit has been already\n /// returned as ETH balance before it happens. This will result in some days (or weeks)\n /// -> depending on the size of deposit queue of showing a deficit when calling `checkBalance`.\n /// As this only offsets the yield and doesn't cause a critical double-counting we are not addressing\n /// this issue.\n /// - A deposit has been done to the validator, but our deposit has been front run by a malicious\n /// actor. Funds in the deposit this contract makes are not recoverable.\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified\n }\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountGwei Amount of ETH in gwei that has been deposited to the beacon chain deposit contract\n /// @param slot The beacon chain slot number when the deposit has been made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either UNKNOWN, PENDING or VERIFIED\n /// @param withdrawableEpoch The withdrawableEpoch of the validator which is being deposited to.\n /// At deposit time this is set to max default value (FAR_FUTURE_EPOCH). If a deposit has\n /// made to a slashed validator the `withdrawableEpoch` will be set to the epoch of that\n /// validator.\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint32 depositIndex;\n DepositStatus status;\n uint64 withdrawableEpoch;\n }\n /// @notice Restricts to only one deposit to an unverified validator at a time.\n /// This is to limit front-running attacks of deposits to the beacon chain contract.\n ///\n /// @dev The value is set to true when a deposit to a new validator has been done that has\n /// not yet be verified.\n bool public firstDeposit;\n /// @notice Unique identifier of the next validator deposit.\n uint128 public nextDepositID;\n /// @notice Mapping of the deposit ID to the deposit data\n mapping(uint256 => DepositData) public deposits;\n /// @notice List of strategy deposit IDs to a validator.\n /// The list can be for deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n /// The list may not be ordered by time of deposit.\n /// Removed deposits will move the last deposit to the removed index.\n uint256[] public depositList;\n\n enum ValidatorState {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITING, // The validator has been requested to exit or has been verified as forced exit\n EXITED, // The validator has been verified to have a zero balance\n REMOVED, // validator has funds withdrawn to this strategy contract and is removed from the SSV\n INVALID // The validator has been front-run and the withdrawal address is not this strategy\n }\n\n // Validator data\n struct ValidatorData {\n ValidatorState state; // The state of the validator known to this contract\n uint40 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n bytes32[] public verifiedValidators;\n /// @notice Mapping of the hash of the validator's public key to the validator state and index.\n /// Uses the Beacon chain hashing for BLSPubkey which is sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => ValidatorData) public validator;\n\n /// @param blockRoot Beacon chain block root of the snapshot\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n bytes32 blockRoot;\n uint64 timestamp;\n uint128 ethBalance;\n }\n /// @notice Mapping of the block root to the balances at that slot\n Balances public snappedBalance;\n /// @notice The last verified ETH balance of the strategy\n uint256 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[50] private __gap;\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event FirstDepositReset();\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n uint256 indexed depositID,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint40 indexed validatorIndex\n );\n event ValidatorInvalid(bytes32 indexed pubKeyHash);\n event DepositVerified(uint256 indexed depositID, uint256 amountWei);\n event DepositToValidatorExiting(\n uint256 indexed depositID,\n uint256 amountWei,\n uint64 withdrawableEpoch\n );\n event DepositValidatorExited(uint256 indexed depositID, uint256 amountWei);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(bytes32 indexed blockRoot, uint256 ethBalance);\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 ethBalance\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_PROOFS = _beaconProofs;\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\n\n require(\n block.timestamp > _beaconGenesisTimestamp,\n \"Invalid genesis timestamp\"\n );\n }\n\n /**\n *\n * Admin Functions\n *\n */\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Reset the `firstDeposit` flag to false so deposits to unverified validators can be made again.\n function resetFirstDeposit() external onlyGovernor {\n require(firstDeposit, \"No first deposit\");\n\n firstDeposit = false;\n\n emit FirstDepositReset();\n }\n\n /**\n *\n * Validator Management\n *\n */\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validator[pubKeyHash].state == ValidatorState.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validator[pubKeyHash].state = ValidatorState.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n struct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n }\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// The the first deposit to a new validator, the amount must be 1 ETH.\n /// Another deposit of at least 31 ETH is required for the validator to be activated.\n /// This second deposit has to be done after the validator has been verified.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validatorStakeData validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validatorStakeData,\n uint64 depositAmountGwei\n ) external onlyRegistrator {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(depositList.length < MAX_DEPOSITS, \"Max deposits\");\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validatorStakeData.pubkey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.VERIFIED),\n \"Not registered or verified\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n if (currentState == ValidatorState.REGISTERED) {\n // Can only have one pending deposit to an unverified validator at a time.\n // This is to limit front-running deposit attacks to a single deposit.\n // The exiting deposit needs to be verified before another deposit can be made.\n // If there was a front-running attack, the validator needs to be verified as invalid\n // and the Governor calls `resetFirstDeposit` to set `firstDeposit` to false.\n require(!firstDeposit, \"Existing first deposit\");\n // Limits the amount of ETH that can be at risk from a front-running deposit attack.\n require(\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n // Limits the number of validator balance proofs to verifyBalances\n require(\n verifiedValidators.length + 1 < MAX_VERIFIED_VALIDATORS,\n \"Max validators\"\n );\n\n // Flag a deposit to an unverified validator so only no other deposits can be made\n // to an unverified validator.\n firstDeposit = true;\n }\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == ValidatorState.REGISTERED) {\n validator[pubKeyHash].state = ValidatorState.STAKED;\n }\n\n /// After the Pectra upgrade the validators have a new restriction when proposing\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\n /// forward. Each slot is created at strict 12 second intervals and those slots can\n /// either have blocks attached to them or not. This way using the block.timestamp\n /// the slot number can easily be calculated.\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\n BEACON_GENESIS_TIMESTAMP) / SLOT_DURATION;\n\n // Store the deposit data for verifyDeposit and verifyBalances\n uint256 depositID = nextDepositID++;\n deposits[depositID] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n slot: depositSlot,\n depositIndex: SafeCast.toUint32(depositList.length),\n status: DepositStatus.PENDING,\n withdrawableEpoch: FAR_FUTURE_EPOCH\n });\n depositList.push(depositID);\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validatorStakeData.pubkey,\n withdrawalCredentials,\n validatorStakeData.signature,\n validatorStakeData.depositDataRoot\n );\n\n emit ETHStaked(\n pubKeyHash,\n depositID,\n validatorStakeData.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then only the amount in excess of 32 ETH will be withdrawn.\n /// Only the Registrator can call this function.\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\n /// If no ETH balance, the tx will revert.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n payable\n onlyRegistrator\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Validator full withdrawal could be denied due to multiple reasons:\n // - the validator has not been activated or active long enough\n // (current_epoch < activation_epoch + SHARD_COMMITTEE_PERIOD)\n // - the validator has pending balance to withdraw from a previous partial withdrawal request\n //\n // Meaning that the on-chain to beacon chain full withdrawal request could fail. Instead\n // of adding complexity of verifying if a validator is eligible for a full exit, we allow\n // multiple full withdrawal requests per validator.\n require(\n currentState == ValidatorState.VERIFIED ||\n currentState == ValidatorState.EXITING,\n \"Validator not verified/exiting\"\n );\n\n // If a full withdrawal (validator exit)\n if (amountGwei == 0) {\n // Store the validator state as exiting so no more deposits can be made to it.\n validator[pubKeyHash].state = ValidatorState.EXITING;\n }\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove the validator from the SSV Cluster after:\n /// - the validator has been exited from `validatorWithdrawal` or slashed\n /// - the validator has incorrectly registered and can not be staked to\n /// - the initial deposit was front-run and the withdrawal address is not this strategy's address.\n /// Make sure `validatorWithdrawal` is called with a zero amount and the validator has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.EXITED ||\n currentState == ValidatorState.INVALID,\n \"Validator not regd or exited\"\n );\n\n validator[pubKeyHash].state = ValidatorState.REMOVED;\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /**\n *\n * SSV Management\n *\n */\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be withdrawn from the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /**\n *\n * Beacon Chain Proofs\n *\n */\n\n /// @notice Verifies a validator's index to its public key.\n /// Adds to the list of verified validators if the validator's withdrawal address is this strategy's address.\n /// Marks the validator as invalid and removes the deposit if the withdrawal address is not this strategy's address.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param withdrawalAddress The withdrawal address of the validator which should be this strategy's address.\n /// If the withdrawal address is not this strategy's address, the initial deposit was front-run\n /// and the validator is marked as invalid.\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint40 validatorIndex,\n bytes32 pubKeyHash,\n address withdrawalAddress,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validator[pubKeyHash].state == ValidatorState.STAKED,\n \"Validator not staked\"\n );\n\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key.\n // Also verify the validator's withdrawal credential points to the `withdrawalAddress`.\n IBeaconProofs(BEACON_PROOFS).verifyValidator(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n withdrawalAddress\n );\n\n // Store the validator state as verified\n validator[pubKeyHash] = ValidatorData({\n state: ValidatorState.VERIFIED,\n index: validatorIndex\n });\n\n // If the initial deposit was front-run and the withdrawal address is not this strategy\n if (withdrawalAddress != address(this)) {\n // override the validator state\n validator[pubKeyHash].state = ValidatorState.INVALID;\n\n // Find and remove the deposit as the funds can not be recovered\n uint256 depositCount = depositList.length;\n for (uint256 i = 0; i < depositCount; i++) {\n DepositData memory deposit = deposits[depositList[i]];\n if (deposit.pubKeyHash == pubKeyHash) {\n // next verifyBalances will correctly account for the loss of a front-run\n // deposit. Doing it here accounts for the loss as soon as possible\n lastVerifiedEthBalance -= Math.min(\n lastVerifiedEthBalance,\n uint256(deposit.amountGwei) * 1 gwei\n );\n _removeDeposit(depositList[i], deposit);\n break;\n }\n }\n\n // Leave the `firstDeposit` flag as true so no more deposits to unverified validators can be made.\n // The Governor has to reset the `firstDeposit` to false before another deposit to\n // an unverified validator can be made.\n // The Governor can set a new `validatorRegistrator` if they suspect it has been compromised.\n\n emit ValidatorInvalid(pubKeyHash);\n return;\n }\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(pubKeyHash);\n\n // Reset the firstDeposit flag as the first deposit to an unverified validator has been verified.\n firstDeposit = false;\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n struct FirstPendingDepositSlotProofData {\n uint64 slot;\n bytes proof;\n }\n struct FirstPendingDepositWithdrawableProofData {\n uint64 slot;\n uint40 validatorIndex;\n bytes32 pubKeyHash;\n bytes pendingDepositPubKeyProof;\n bytes withdrawableEpochProof;\n bytes validatorPubKeyProof;\n }\n\n struct StrategyValidatorProofData {\n uint64 withdrawableEpoch;\n bytes withdrawableEpochProof;\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n ///\n /// Important: this function has a limitation where `depositProcessedSlot` and `firstDepositValidatorCreatedSlot`\n /// that is passed by the off-chain verifier requires a slot immediately after them to propose a block otherwise\n /// the `BeaconRoots.parentBlockRoot` will fail. This shouldn't be a problem, since by the current behaviour of\n /// beacon chain only 1%-3% slots don't propose a block.\n /// @param depositID The deposit ID emitted in `ETHStaked` from the `stakeEth` function.\n /// @param depositProcessedSlot Any slot on or after the strategy's deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\n /// set for the next block timestamp in 12 seconds time.\n /// @param firstPendingDeposit a `FirstPendingDepositSlotProofData` struct containing:\n /// - slot: The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero is a good choice.\n /// - proof: The merkle proof of the first pending deposit's slot to the beacon block root.\n /// Can be either:\n /// * 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// * 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @param strategyValidatorData a `StrategyValidatorProofData` struct containing:\n /// - withdrawableEpoch: The withdrawable epoch of the validator the strategy is depositing to.\n /// - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the validator the strategy\n /// is depositing to, to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n uint256 depositID,\n uint64 depositProcessedSlot,\n FirstPendingDepositSlotProofData calldata firstPendingDeposit,\n StrategyValidatorProofData calldata strategyValidatorData\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositID];\n ValidatorData memory strategyValidator = validator[deposit.pubKeyHash];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n strategyValidator.state == ValidatorState.VERIFIED,\n \"Validator not verified\"\n );\n // The verification slot must be after the deposit's slot.\n // This is needed for when the deposit queue is empty.\n require(deposit.slot < depositProcessedSlot, \"Slot not after deposit\");\n\n // Get the parent beacon block root of the next block which is the block root of the deposit verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 depositBlockRoot = BeaconRoots.parentBlockRoot(\n _calcNextBlockTimestamp(depositProcessedSlot)\n );\n\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n depositBlockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.proof\n );\n\n // Verify the withdrawableEpoch on the validator of the strategy's deposit\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n depositBlockRoot,\n strategyValidator.index,\n strategyValidatorData.withdrawableEpoch,\n strategyValidatorData.withdrawableEpochProof\n );\n\n // If the validator is exiting because it has been slashed\n if (strategyValidatorData.withdrawableEpoch != FAR_FUTURE_EPOCH) {\n // Store the exit epoch in the deposit data\n deposit.withdrawableEpoch = strategyValidatorData.withdrawableEpoch;\n\n emit DepositToValidatorExiting(\n depositID,\n uint256(deposit.amountGwei) * 1 gwei,\n strategyValidatorData.withdrawableEpoch\n );\n\n validator[deposit.pubKeyHash].state = ValidatorState.EXITING;\n\n // Leave the deposit status as PENDING\n return;\n }\n\n // solhint-disable max-line-length\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the deposit queue is empty then our deposit must have been processed on the beacon chain.\n // The deposit slot can be zero for validators consolidating to a compounding validator or 0x01 validator\n // being promoted to a compounding one. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // We can not guarantee that the deposit has been processed in that case.\n // solhint-enable max-line-length\n require(\n deposit.slot < firstPendingDeposit.slot || isDepositQueueEmpty,\n \"Deposit likely not processed\"\n );\n\n // Remove the deposit now it has been verified as processed on the beacon chain.\n _removeDeposit(depositID, deposit);\n\n emit DepositVerified(depositID, uint256(deposit.amountGwei) * 1 gwei);\n }\n\n function _removeDeposit(uint256 depositID, DepositData memory deposit)\n internal\n {\n // After verifying the proof, update the contract storage\n deposits[depositID].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n uint256 lastDeposit = depositList[depositList.length - 1];\n depositList[deposit.depositIndex] = lastDeposit;\n deposits[lastDeposit].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositList.pop();\n }\n\n /// @dev Calculates the timestamp of the next execution block from the given slot.\n /// @param slot The beacon chain slot number used for merkle proof verification.\n function _calcNextBlockTimestamp(uint64 slot)\n internal\n view\n returns (uint64)\n {\n // Calculate the next block timestamp from the slot.\n return SLOT_DURATION * slot + BEACON_GENESIS_TIMESTAMP + SLOT_DURATION;\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block and beacon block root\n /// of the slot that is associated with the previous block.\n ///\n /// When snapping / verifying balance it is of a high importance that there is no\n /// miss-match in respect to ETH that is held by the contract and balances that are\n /// verified on the validators.\n ///\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\n /// constructing a block on the beacon chain consist of:\n /// - process_withdrawals: ETH is deducted from the validator's balance\n /// - process_execution_payload: immediately after the previous step executing all the\n /// transactions\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\n /// contained in the withdrawal credentials of the exited validators\n ///\n /// That means that balance increases which are part of the post-block execution state are\n /// done within the block, but the transaction that are contained within that block can not\n /// see / interact with the balance from the exited validators. Only transactions in the\n /// next block can do that.\n ///\n /// When snap balances is performed the state of the chain is snapped across 2 separate\n /// chain states:\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\n /// will be referred to as Y - 1 as it makes no difference in the argument\n ///\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\n /// miss-counting ETH or much more dangerous double counting of the ETH.\n ///\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\n /// - ETH balance in the contract on block X\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\n /// - ETH balance in validators that are active in slot Y - 1\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\n /// and have their balance visible to transactions in slot Y and corresponding block X\n /// (or sooner)\n ///\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\n ///\n /// *ETH balance in the contract on block X*\n ///\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\n ///\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\n /// would result in double counting the `stakedEth` since it would be present once in the\n /// snapped contract balance and the second time in deposit storage variables.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\n ///\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\n /// contract at block Z. The execution layer doesn't have direct access to the state of\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\n /// and could also be part of the validator balances. It does that by verifying that at\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\n /// the last snap till now all are still in queue. Which ensures they can not be part of\n /// the validator balances in later steps.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in validators that are active in slot Y - 1*\n ///\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\n /// correctness.\n ///\n /// This behaviour is correct.\n ///\n /// *The withdrawn validators*\n ///\n /// The withdrawn validators could have their balances deducted in any slot before slot\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\n /// look at the \"worst case scenario\" where the validator withdrawal is processed in the\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\n /// even if the validator exits at the latest possible time it is paramount that the ETH\n /// balance on the execution layer is recorded in the next block. Correctly accounting\n /// for the withdrawn ETH.\n ///\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\n /// validator balance verification as well as execution layer contract balance snap.\n ///\n /// This behaviour is correct.\n ///\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n function snapBalances() external {\n uint64 currentTimestamp = SafeCast.toUint64(block.timestamp);\n require(\n snappedBalance.timestamp + SNAP_BALANCES_DELAY < currentTimestamp,\n \"Snap too soon\"\n );\n\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(currentTimestamp);\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the snapped balance\n snappedBalance = Balances({\n blockRoot: blockRoot,\n timestamp: currentTimestamp,\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n emit BalancesSnapped(blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct BalanceProofs {\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n /// @param validatorVerificationBlockTimestamp next block's timestamp of a slot that has the first pending\n /// deposit already applied to the validator.\n /// @param firstPendingDeposit a `FirstPendingDepositWithdrawableProofData` struct containing:\n /// - slot: The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero is a good choice.\n /// - validatorIndex: The index of the validator of the first pending deposit.\n /// Can be anything if the deposit queue is empty, but zero is a good choice.\n /// - pubKeyHash: The hash of the public key of the validator of the first pending deposit.\n /// Can be anything if the deposit queue is empty, but 32 zero bytes is a good choice\n /// - pendingDepositPubKeyProof: The merkle proof of the first pending deposit to the beacon block root.\n /// Can be either:\n /// * 40 witness hashes for BeaconBlock.state.PendingDeposits[0].pubKey when the deposit queue is not empty.\n /// * 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the first pending deposit's validator\n /// to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// - validatorPubKeyProof The merkle proof for the public key of the first pending deposit's validator\n /// to the this witness hash of withdrawableEpochProof.\n /// This is 2 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param balanceProofs a `BalanceProofs` struct containing the following:\n /// - balancesContainerRoot: The merkle root of the balances container\n /// - balancesContainerProof: The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// - validatorBalanceLeaves: Array of leaf nodes containing the validator balance with three other balances.\n /// - validatorBalanceProofs: Array of merkle proofs for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(\n uint64 validatorVerificationBlockTimestamp,\n FirstPendingDepositWithdrawableProofData calldata firstPendingDeposit,\n BalanceProofs calldata balanceProofs\n ) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalance;\n // Check the balances are the latest\n require(balancesMem.timestamp > 0, \"No snapped balances\");\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n balanceProofs.validatorBalanceProofs.length ==\n verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n balanceProofs.validatorBalanceLeaves.length ==\n verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n balancesMem.blockRoot,\n balanceProofs.balancesContainerRoot,\n balanceProofs.balancesContainerProof\n );\n\n // for each validator in reverse order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n balanceProofs.balancesContainerRoot,\n balanceProofs.validatorBalanceLeaves[i],\n balanceProofs.validatorBalanceProofs[i],\n validator[verifiedValidators[i]].index\n );\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n // This could have been in VERIFIED or EXITING state\n validator[verifiedValidators[i]].state = ValidatorState\n .EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += validatorBalanceGwei * 1 gwei;\n }\n }\n\n uint256 depositsCount = depositList.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification.\n // This section is after the validator balance verifications so an exited validator will be marked\n // as EXITED before the deposits are verified. If there was a deposit to an exited validator\n // then the deposit can only be removed once the validator is fully exited.\n // It is possible that validator fully exits and a postponed deposit to an exited validator increases\n // its balance again. In such case the contract will erroneously consider a deposit applied before it\n // has been applied on the beacon chain showing a smaller than real `totalValidatorBalance`.\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n balancesMem.blockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.pubKeyHash,\n firstPendingDeposit.pendingDepositPubKeyProof\n );\n\n // If there are no deposits in the beacon chain queue then our deposits must have been processed.\n // If the deposits have been processed, each deposit will need to be verified with `verifyDeposit`\n require(!isDepositQueueEmpty, \"Deposits have been processed\");\n\n // The verification of the validator the first pending deposit is for must be on or after when\n // `snapBalances` was called.\n require(\n balancesMem.timestamp <= validatorVerificationBlockTimestamp,\n \"Invalid validator timestamp\"\n );\n\n // Verify the validator of the first pending deposit is not exiting by checking\n // the withdrawable epoch is far into the future.\n // If it is exiting we can't be sure this deposit has not been postponed in the deposit queue.\n // Hence we can not verify if the strategy's deposit has been processed or not.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n // Get the parent beacon block root of the next block which is\n // the block root of the validator verification slot.\n // This will revert if the slot after the verification slot was missed.\n BeaconRoots.parentBlockRoot(\n validatorVerificationBlockTimestamp\n ),\n firstPendingDeposit.validatorIndex,\n firstPendingDeposit.pubKeyHash,\n // Validator is not exiting\n FAR_FUTURE_EPOCH,\n firstPendingDeposit.withdrawableEpochProof,\n firstPendingDeposit.validatorPubKeyProof\n );\n\n // solhint-disable max-line-length\n // If a validator is converted from a sweeping validator to a compounding validator, any balance in excess\n // of the min 32 ETH is put in the pending deposit queue. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // This will have a slot value of zero unfortunately.\n // We can not prove the strategy's deposits are still pending with a zero slot value so revert the tx.\n // Another snapBalances will need to be taken that does not have consolidation deposits at the front of the\n // beacon chain deposit queue.\n // solhint-enable max-line-length\n require(\n firstPendingDeposit.slot > 0,\n \"Invalid first pending deposit\"\n );\n\n // Calculate the epoch at the time of the snapBalances\n uint64 verificationEpoch = (SafeCast.toUint64(\n balancesMem.timestamp\n ) - BEACON_GENESIS_TIMESTAMP) / (SLOT_DURATION * SLOTS_PER_EPOCH);\n\n // For each staking strategy's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n uint256 depositID = depositList[i];\n DepositData memory depositData = deposits[depositID];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit slot is before the slot of the staking strategy's deposit.\n // If the deposit has been processed, it will need to be verified with `verifyDeposit`.\n // OR the deposit is to an exiting validator so check it is still not withdrawable.\n // If the validator is not withdrawable, then the deposit can not have been processed yet.\n // If the validator is now withdrawable, then the deposit may have been processed. The strategy\n // now has to wait until the validator's balance is verified to be zero.\n // OR the validator has exited and the deposit is now verified as processed.\n require(\n firstPendingDeposit.slot < depositData.slot ||\n (verificationEpoch < depositData.withdrawableEpoch &&\n depositData.withdrawableEpoch !=\n FAR_FUTURE_EPOCH) ||\n validator[depositData.pubKeyHash].state ==\n ValidatorState.EXITED,\n \"Deposit likely processed\"\n );\n\n // Remove the deposit if the validator has exited.\n if (\n validator[depositData.pubKeyHash].state ==\n ValidatorState.EXITED\n ) {\n _removeDeposit(depositID, depositData);\n\n emit DepositValidatorExited(\n depositID,\n uint256(depositData.amountGwei) * 1 gwei\n );\n\n // Skip to the next deposit as the deposit amount is now in the strategy's ETH balance\n continue;\n }\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei += uint256(depositData.amountGwei) * 1 gwei;\n }\n }\n\n // Store the verified balance in storage\n lastVerifiedEthBalance =\n totalDepositsWei +\n totalValidatorBalance +\n balancesMem.ethBalance;\n // Reset the last snap timestamp so a new snapBalances has to be made\n snappedBalance.timestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n require(pubKey.length == 48, \"Invalid public key length\");\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /**\n *\n * WETH and ETH Accounting\n *\n */\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n // The min is required as more WETH can be withdrawn than deposited\n // as the strategy earns consensus and execution rewards.\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance.\n // The ETH balance in this strategy contract can be more than the last verified ETH balance\n // due to partial withdrawals or full exits being processed by the beacon chain since the last snapBalances.\n // It can also happen from execution rewards (MEV) or ETH donations.\n lastVerifiedEthBalance -= Math.min(lastVerifiedEthBalance, _ethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n snappedBalance.timestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += _wethAmount;\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n snappedBalance.timestamp = 0;\n }\n\n /**\n *\n * View Functions\n *\n */\n\n /// @notice Returns the number of deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n function depositListLength() external view returns (uint256) {\n return depositList.length;\n }\n\n /// @notice Returns the number of verified validators.\n function verifiedValidatorsLength() external view returns (uint256) {\n return verifiedValidators.length;\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/bfe75117c1b1f2eb9af8e3e8503419ba.json b/contracts/deployments/hoodi/solcInputs/bfe75117c1b1f2eb9af8e3e8503419ba.json new file mode 100644 index 0000000000..859feb340c --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/bfe75117c1b1f2eb9af8e3e8503419ba.json @@ -0,0 +1,111 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/security/Pausable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the Beacon Block Root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Block Root Oracle to get the parent block root\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconOracle {\n function blockToSlot(uint64 blockNumber) external returns (uint64 slot);\n\n function slotToBlock(uint64 slot) external returns (uint64 blockNumber);\n\n function slotToRoot(uint64 slot) external returns (bytes32 blockRoot);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n enum BalanceProofLevel {\n Container,\n BeaconBlock\n }\n\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 root,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex,\n BalanceProofLevel level\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view;\n\n function verifyBlockNumber(\n bytes32 beaconBlockRoot,\n uint256 blockNumber,\n bytes calldata blockNumberProof\n ) external view;\n\n function verifySlot(\n bytes32 beaconBlockRoot,\n uint256 slot,\n bytes calldata slotProof\n ) external view;\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n external\n pure\n returns (uint256);\n\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) external pure returns (uint256);\n}\n" + }, + "contracts/interfaces/IConsolidation.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IConsolidationSource {\n function confirmConsolidation()\n external\n returns (uint256 consolidationCount);\n}\n\ninterface IConsolidationTarget {\n function requestConsolidation(\n bytes32 lastSourcePubKeyHash,\n bytes32 targetPubKeyHash\n ) external;\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconOracle Address of the Beacon Oracle contract that maps block numbers to slots\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconOracle,\n address _beaconProofs\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconOracle,\n _beaconProofs\n )\n {\n SSV_TOKEN = _ssvToken;\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract.\n /// @param _rewardTokenAddresses Address of reward token for platform\n /// @param _assets Addresses of initial supported assets\n /// @param _pTokens Platform Token corresponding addresses\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused for validator consolidation.\n /// @param _asset Address of asset to deposit. Has to be WETH.\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused for validator consolidation.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// Will revert if the strategy is paused for validator consolidation.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant whenNotPaused {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _asset, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(_asset, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n /// Will revert if the strategy is paused for validator consolidation.\n function withdrawAll()\n external\n override\n onlyVaultOrGovernor\n nonReentrant\n whenNotPaused\n {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, WETH, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Returns the last verified balance of validator deposits, validator balance,\n /// WETH and ETH in the strategy contract.\n /// @param _asset Address of weth asset\n /// @return balance Total value of (W)ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by strategy.\n /// @param _asset The address of the asset token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for deposits\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instread of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract,\n /// but so does withdrawals from validators. It's too complex to separate the two\n /// so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IConsolidationSource } from \"../../interfaces/IConsolidation.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\nimport { IBeaconOracle } from \"../../interfaces/IBeaconOracle.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit, remove and consolidate validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable, Pausable {\n using SafeERC20 for IERC20;\n\n /// @notice The amount of ETH in wei that is required for a deposit to a new validator.\n /// Initially this is 32 ETH, but will be increased to 1 ETH after P2P's APIs have been updated\n /// to support deposits of 1 ETH.\n uint256 public constant DEPOSIT_AMOUNT_WEI = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Oracle contract that maps block numbers to slots\n address public immutable BEACON_ORACLE;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// Deposit data for new compounding validators.\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountWei Amount of ETH in wei that has been deposited to the beacon chain deposit contract\n /// @param blockNumber Block number when the deposit was made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either PENDING or PROVEN\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 blockNumber;\n uint32 depositIndex;\n DepositStatus status;\n }\n /// @notice Mapping of the root of a deposit (depositDataRoot) to its data\n mapping(bytes32 => DepositData) public deposits;\n /// @notice List of deposit roots that are still to be verified as processed on the beacon chain\n bytes32[] public depositsRoots;\n\n // Validator data\n\n struct ValidatorData {\n bytes32 pubKeyHash; // Hash of the validator's public key using the Beacon Chain's format\n uint64 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes and indexes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n ValidatorData[] internal verifiedValidators;\n /// @notice State of the new compounding validators with a 0x02 withdrawal credential prefix.\n /// Uses the Beacon chain hashing for BLSPubkey which is\n /// sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => VALIDATOR_STATE) public validatorState;\n\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n uint64 timestamp;\n uint128 ethBalance;\n }\n // TODO is it more efficient to use the block root rather than hashing it?\n /// @notice Mapping of the block root to the balances at that slot\n mapping(bytes32 => Balances) public snappedBalances;\n uint64 public lastSnapTimestamp;\n uint128 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n bytes32 public consolidationLastPubKeyHash;\n address public consolidationSourceStrategy;\n mapping(address => bool) public consolidationSourceStrategies;\n\n // For future use\n uint256[50] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITED, // The validator has been verified to have a zero balance\n REMOVED // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified and is ready to be staked\n }\n\n event RegistratorChanged(address indexed newAddress);\n event SourceStrategyAdded(address indexed strategy);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n bytes32 indexed depositDataRoot,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint64 indexed validatorIndex\n );\n event DepositVerified(bytes32 indexed depositDataRoot, uint256 amountWei);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(\n uint256 indexed timestamp,\n bytes32 indexed blockRoot,\n uint256 ethBalance\n );\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 wethBalance,\n uint256 ethBalance\n );\n event ConsolidationRequested(\n bytes32 indexed lastSourcePubKeyHash,\n bytes32 indexed targetPubKeyHash,\n address indexed sourceStrategy\n );\n event ConsolidationVerified(\n bytes32 indexed lastSourcePubKeyHash,\n uint64 indexed lastValidatorIndex,\n uint256 consolidationCount\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconOracle Address of the Beacon Oracle contract that maps block numbers to slots\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconOracle,\n address _beaconProofs\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_ORACLE = _beaconOracle;\n BEACON_PROOFS = _beaconProofs;\n }\n\n /***************************************\n Admin Functions\n ****************************************/\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Adds support for a legacy staking strategy that can be used for consolidation.\n function addSourceStrategy(address _strategy) external onlyGovernor {\n consolidationSourceStrategies[_strategy] = true;\n\n emit SourceStrategyAdded(_strategy);\n }\n\n /***************************************`\n Validator Management\n ****************************************/\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validatorState[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validator validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validator,\n uint64 depositAmountGwei\n ) external onlyRegistrator whenNotPaused {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validator.pubkey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == VALIDATOR_STATE.REGISTERED ||\n currentState == VALIDATOR_STATE.VERIFIED),\n \"Not registered or verified\"\n );\n require(\n currentState == VALIDATOR_STATE.VERIFIED ||\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validator.pubkey,\n withdrawalCredentials,\n validator.signature,\n validator.depositDataRoot\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == VALIDATOR_STATE.REGISTERED) {\n validatorState[pubKeyHash] = VALIDATOR_STATE.STAKED;\n }\n // Store the deposit data for verifyDeposit and verifyBalances\n deposits[validator.depositDataRoot] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n blockNumber: SafeCast.toUint64(block.number),\n depositIndex: SafeCast.toUint32(depositsRoots.length),\n status: DepositStatus.PENDING\n });\n depositsRoots.push(validator.depositDataRoot);\n\n emit ETHStaked(\n pubKeyHash,\n validator.depositDataRoot,\n validator.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then the request will fail.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n onlyRegistrator\n whenNotPaused\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove a sweeping validator from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == VALIDATOR_STATE.EXITED ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exited\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validatorState[pubKeyHash] = VALIDATOR_STATE.REMOVED;\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /// @notice Receives requests from supported legacy strategies to consolidate sweeping validators to\n /// a new compounding validator on this new strategy.\n /// @param lastSourcePubKeyHash The last source validator to be consolidated hashed using the Beacon Chain's format.\n /// @param targetPubKeyHash The target validator's public key hash using the Beacon Chain's format.\n function requestConsolidation(\n bytes32 lastSourcePubKeyHash,\n bytes32 targetPubKeyHash\n ) external whenNotPaused {\n require(\n consolidationSourceStrategies[msg.sender],\n \"Not a source strategy\"\n );\n\n // The target validator must be a compounding validator that has been verified\n require(\n validatorState[targetPubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Target validator not verified\"\n );\n\n // Ensure balances can not be verified until after the consolidation has been verified.\n lastSnapTimestamp = 0;\n\n // Store consolidation state\n consolidationLastPubKeyHash = lastSourcePubKeyHash;\n consolidationSourceStrategy = msg.sender;\n\n // Pause the strategy while the consolidation is in progress\n _pause();\n\n emit ConsolidationRequested(\n lastSourcePubKeyHash,\n targetPubKeyHash,\n msg.sender\n );\n }\n\n /***************************************\n SSV Management\n ****************************************/\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n Beacon Chain Proofs\n ****************************************/\n\n /// @notice Verifies a validator's index to its public key.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param validatorPubKeyProof The merkle proof that the validator's index matches its public key\n /// on the beacon chain.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.STAKED,\n \"Validator not staked\"\n );\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key\n IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n address(this) // Withdrawal address is this strategy\n );\n\n // TODO verify the validator's withdrawal credential points to this strategy\n\n // Store the validator state as verified\n validatorState[pubKeyHash] = VALIDATOR_STATE.VERIFIED;\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(\n ValidatorData({ pubKeyHash: pubKeyHash, index: validatorIndex })\n );\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n /// @param depositDataRoot The root of the deposit data that was stored when\n /// the deposit was made on the execution layer.\n /// @param depositBlockNumber A block number that is on or after the block the deposit\n /// was made on the execution layer.\n /// @param processedSlot Any slot on or after the deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// @param firstPendingDepositSlot The slot of the first pending deposit in the beacon chain\n /// @param firstPendingDepositSlotProof The merkle proof that the slot of the first pending deposit\n /// matches the beacon chain.\n /// BeaconBlock.BeaconBlockBody.deposits[0].slot\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n bytes32 depositDataRoot,\n uint64 depositBlockNumber,\n uint64 processedSlot,\n uint64 firstPendingDepositSlot,\n bytes calldata firstPendingDepositSlotProof\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositDataRoot];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n validatorState[deposit.pubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n // The deposit block number mapped to a slot needs to be the same block or after\n // the deposit in `stakeETH` was created on the execution layer.\n require(\n deposit.blockNumber <= depositBlockNumber,\n \"Deposit block before deposit\"\n );\n\n // Get the slot that is on or after the deposit was made on the execution layer.\n uint64 depositSlot = IBeaconOracle(BEACON_ORACLE).blockToSlot(\n depositBlockNumber\n );\n\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the first pending deposit's slot is 0 then there are no pending deposits so\n // our deposit must have been processed on the beacon chain.\n require(\n depositSlot < firstPendingDepositSlot ||\n firstPendingDepositSlot == 0,\n \"Deposit not processed\"\n );\n\n // The slot of the execution layer deposit must be before\n // the slot that the deposit was processed on the beacon chain.\n require(depositSlot < processedSlot, \"Slot not after deposit\");\n\n // Get the beacon block root for the slot that is on or after the deposit was processed on the beacon chain.\n bytes32 blockRoot = IBeaconOracle(BEACON_ORACLE).slotToRoot(\n processedSlot\n );\n\n // Verify the slot of the first pending deposit matches the beacon chain\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n blockRoot,\n firstPendingDepositSlot,\n firstPendingDepositSlotProof\n );\n\n // After verifying the proof, update the contract storage\n deposits[depositDataRoot].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n bytes32 lastDepositDataRoot = depositsRoots[depositsRoots.length - 1];\n depositsRoots[deposit.depositIndex] = lastDepositDataRoot;\n deposits[lastDepositDataRoot].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositsRoots.pop();\n\n emit DepositVerified(\n depositDataRoot,\n uint256(deposit.amountGwei) * 1 gwei\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n // TODO what if the last validator was exited rather than consolidated?\n // slither-disable-start reentrancy-no-eth\n // function verifyConsolidation(\n // uint64 parentBlockTimestamp,\n // uint64 lastValidatorIndex,\n // bytes calldata validatorPubKeyProof,\n // bytes32 balancesLeaf,\n // bytes calldata validatorBalanceProof\n // ) external onlyRegistrator {\n // bytes32 consolidationLastPubKeyHashMem = consolidationLastPubKeyHash;\n // require(\n // consolidationLastPubKeyHashMem != bytes32(0),\n // \"No consolidations\"\n // );\n\n // bytes32 blockRoot = BeaconRoots.parentBlockRoot(parentBlockTimestamp);\n // // Verify the validator index has the same public key as the last source validator\n // IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n // blockRoot,\n // consolidationLastPubKeyHashMem,\n // validatorPubKeyProof,\n // lastValidatorIndex,\n // address(this) // Withdrawal address is this strategy\n // );\n\n // // Verify the balance of the last validator in the consolidation batch\n // // is zero. If its not then the consolidation has not been completed.\n // // This proof is to the beacon block root, not the balances container root.\n // uint256 validatorBalance = IBeaconProofs(BEACON_PROOFS)\n // .verifyValidatorBalance(\n // blockRoot,\n // balancesLeaf,\n // validatorBalanceProof,\n // lastValidatorIndex,\n // IBeaconProofs.BalanceProofLevel.BeaconBlock\n // );\n // require(validatorBalance == 0, \"Last validator balance not zero\");\n\n // // Call the old sweeping strategy to confirm the consolidation has been completed.\n // // This will decrease the balance of the source strategy by 32 ETH for each validator being consolidated.\n // uint256 consolidationCount = IConsolidationSource(\n // consolidationSourceStrategy\n // ).confirmConsolidation();\n\n // // Increase the balance of this strategy by 32 ETH for each validator being consolidated.\n // // This nets out the decrease in the source strategy's balance.\n // lastVerifiedEthBalance += SafeCast.toUint128(\n // consolidationCount * 32 ether\n // );\n\n // // Reset the stored consolidation state\n // consolidationLastPubKeyHash = bytes32(0);\n // consolidationSourceStrategy = address(0);\n\n // emit ConsolidationVerified(\n // consolidationLastPubKeyHashMem,\n // lastValidatorIndex,\n // consolidationCount\n // );\n\n // // Unpause now the balance of the target validator has been verified\n // _unpause();\n\n // // Take a snap of the balances so the actual balances of the new validator balances can be verified\n // _snapBalances();\n // }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block.\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n /// Can only be called by the registrator.\n /// Can not be called while a consolidation is in progress.\n function snapBalances() external whenNotPaused onlyRegistrator {\n _snapBalances();\n }\n\n function _snapBalances() internal {\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(\n SafeCast.toUint64(block.timestamp)\n );\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the balances in the mapping\n snappedBalances[blockRoot] = Balances({\n timestamp: SafeCast.toUint64(block.timestamp),\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n // Store the snapped timestamp\n lastSnapTimestamp = SafeCast.toUint64(block.timestamp);\n\n emit BalancesSnapped(block.timestamp, blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct VerifyBalancesParams {\n bytes32 blockRoot;\n uint64 mappedDepositSlot;\n uint64 firstPendingDepositSlot;\n // BeaconBlock.BeaconBlockBody.deposits[0].slot\n bytes firstPendingDepositSlotProof;\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(VerifyBalancesParams calldata params) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalances[params.blockRoot];\n // Check the balances are the latest\n require(lastSnapTimestamp > 0, \"No snapped balances\");\n require(balancesMem.timestamp == lastSnapTimestamp, \"Stale snap\");\n\n uint256 depositsCount = depositsRoots.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit to the beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n params.blockRoot,\n params.firstPendingDepositSlot,\n params.firstPendingDepositSlotProof\n );\n\n // As the BeaconOracle probably doesn't have the slot of the first pending deposit mapped to a block,\n // use any slot that is on or after the slot of the first pending deposit. That is,\n // firstPendingDepositSlot <= mappedDepositSlot < deposits[depositDataRoot].blockNumber\n require(\n params.firstPendingDepositSlot <= params.mappedDepositSlot,\n \"Invalid deposit slot\"\n );\n uint64 firstPendingDepositBlockNumber = IBeaconOracle(BEACON_ORACLE)\n .slotToBlock(params.mappedDepositSlot);\n\n // For each native staking contract's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n bytes32 depositDataRoot = depositsRoots[i];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit block number is before the\n // block number of the staking strategy's deposit.\n // If it has it will need to be verified with `verifyDeposit`\n require(\n firstPendingDepositBlockNumber <\n deposits[depositDataRoot].blockNumber,\n \"Deposit has been processed\"\n );\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei +=\n uint256(deposits[depositDataRoot].amountGwei) *\n 1 gwei;\n }\n }\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n params.validatorBalanceProofs.length == verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n params.validatorBalanceLeaves.length == verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n params.blockRoot,\n params.balancesContainerRoot,\n params.balancesContainerProof\n );\n\n // for each validator in reserve order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n params.balancesContainerRoot,\n params.validatorBalanceLeaves[i],\n params.validatorBalanceProofs[i],\n verifiedValidators[i].index,\n IBeaconProofs.BalanceProofLevel.Container\n );\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n validatorState[\n verifiedValidators[i].pubKeyHash\n ] = VALIDATOR_STATE.EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += uint256(validatorBalanceGwei) * 1 gwei;\n }\n }\n\n uint256 wethBalance = IWETH9(WETH).balanceOf(address(this));\n\n // Store the verified balance in storage\n lastVerifiedEthBalance = SafeCast.toUint128(\n totalDepositsWei + totalValidatorBalance + balancesMem.ethBalance\n );\n // Reset the last snap timestamp so a new snapBalances has to be made\n lastSnapTimestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n wethBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /***************************************\n WETH and ETH Accounting\n ****************************************/\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance\n if (lastVerifiedEthBalance > _ethAmount) {\n lastVerifiedEthBalance -= SafeCast.toUint128(_ethAmount);\n } else {\n // This can happen if all ETH in the validators was withdrawn\n // and there was more consensus rewards since the last balance verification.\n // Or it can happen if ETH is donated to this strategy.\n lastVerifiedEthBalance = 0;\n }\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += SafeCast.toUint128(_wethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /***************************************\n View Functions\n ****************************************/\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return An array of `ValidatorData` containing the public key hash and validator index.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorData[] memory)\n {\n return verifiedValidators;\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/c9a55245f58dfd5701f7897e7065f9b5.json b/contracts/deployments/hoodi/solcInputs/c9a55245f58dfd5701f7897e7065f9b5.json new file mode 100644 index 0000000000..abb4cf2751 --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/c9a55245f58dfd5701f7897e7065f9b5.json @@ -0,0 +1,102 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to retrieve beacon block roots.\n * @author Origin Protocol Inc\n */\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the beacon block root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Roots contract to get the parent block root.\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to request full or partial withdrawals from validators on the beacon chain.\n * @author Origin Protocol Inc\n */\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n /// @notice Requests a partial withdrawal for a given validator public key and amount.\n /// @param validatorPubKey The public key of the validator to withdraw from\n /// @param amount The amount of ETH to withdraw\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n /// @notice Gets fee for withdrawal requests contract on Beacon chain\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof,\n bytes calldata validatorPubKeyProof\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint64 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes32 pubKeyHash,\n bytes calldata firstPendingDepositProof\n ) external view returns (bool isEmptyDepositQueue);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with\n /// `platformAddress` not used so empty address\n /// `vaultAddress` the address of the OETH Vault contract\n /// @param _wethAddress Address of the WETH Token contract\n /// @param _ssvToken Address of the SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconProofs,\n _beaconGenesisTimestamp\n )\n {\n SSV_TOKEN = _ssvToken;\n\n // Make sure nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// @param _rewardTokenAddresses Not used so empty array\n /// @param _assets Not used so empty array\n /// @param _pTokens Not used so empty array\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n safeApproveAllTokens();\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH that was transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// @param _recipient Address to receive withdrawn assets.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH to withdraw.\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(WETH, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Accounts for all the assets managed by this strategy which includes:\n /// 1. The current WETH in this strategy contract\n /// 2. The last verified ETH balance, total deposits and total validator balances\n /// @param _asset Address of WETH asset.\n /// @return balance Total value in ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by the strategy.\n /// @param _asset The address of the WETH token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for validator registration.\n function safeApproveAllTokens() public override {\n // Approves the SSV Network contract to transfer SSV tokens when validators are registered\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n * The new ETH will be accounted for in `checkBalance` after the next snapBalances and verifyBalances txs.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n /// @notice is not supported for this strategy as there is no platform token.\n function setPTokenAddress(address, address) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @notice is not supported for this strategy as there is no platform token.\n function removePToken(uint256) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @dev This strategy does not use a platform token like the old Aave and Compound strategies.\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instead of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract.\n /// Withdrawals from validators also accumulate as ETH in this strategy contract.\n /// It's too complex to separate the rewards from withdrawals so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can now regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingView.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/**\n * @title Viewing contract for the Compounding Staking Strategy.\n * @author Origin Protocol Inc\n */\ncontract CompoundingStakingStrategyView {\n /// @notice The address of the Compounding Staking Strategy contract\n CompoundingValidatorManager public immutable stakingStrategy;\n\n constructor(address _stakingStrategy) {\n stakingStrategy = CompoundingValidatorManager(_stakingStrategy);\n }\n\n struct ValidatorView {\n bytes32 pubKeyHash;\n uint64 index;\n CompoundingValidatorManager.ValidatorState state;\n }\n\n struct DepositView {\n uint256 depositID;\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint256 withdrawableEpoch;\n }\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return validators An array of `ValidatorView` containing the public key hash, validator index and state.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorView[] memory validators)\n {\n uint256 validatorCount = stakingStrategy.verifiedValidatorsLength();\n validators = new ValidatorView[](validatorCount);\n for (uint256 i = 0; i < validatorCount; ++i) {\n bytes32 pubKeyHash = stakingStrategy.verifiedValidators(i);\n (\n CompoundingValidatorManager.ValidatorState state,\n uint64 index\n ) = stakingStrategy.validator(pubKeyHash);\n validators[i] = ValidatorView({\n pubKeyHash: pubKeyHash,\n index: index,\n state: state\n });\n }\n }\n\n /// @notice Returns the deposits that are still to be verified.\n /// These may or may not have been processed by the beacon chain.\n /// @return pendingDeposits An array of `DepositView` containing the deposit ID, public key hash,\n /// amount in Gwei and the slot of the deposit.\n function getPendingDeposits()\n external\n view\n returns (DepositView[] memory pendingDeposits)\n {\n uint256 depositsCount = stakingStrategy.depositListLength();\n pendingDeposits = new DepositView[](depositsCount);\n for (uint256 i = 0; i < depositsCount; ++i) {\n (\n bytes32 pubKeyHash,\n uint64 amountGwei,\n uint64 slot,\n ,\n ,\n uint256 withdrawableEpoch\n ) = stakingStrategy.deposits(stakingStrategy.depositList(i));\n pendingDeposits[i] = DepositView({\n depositID: stakingStrategy.depositList(i),\n pubKeyHash: pubKeyHash,\n amountGwei: amountGwei,\n slot: slot,\n withdrawableEpoch: withdrawableEpoch\n });\n }\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable {\n using SafeERC20 for IERC20;\n\n /// @dev The amount of ETH in wei that is required for a deposit to a new validator.\n /// Initially this is 32 ETH, but will be reduced to 1 ETH after P2P's APIs have been updated\n /// to support deposits of 1 ETH.\n uint256 internal constant DEPOSIT_AMOUNT_WEI = 32 ether;\n /// @dev The maximum number of deposits that are waiting to be verified as processed on the beacon chain.\n uint256 internal constant MAX_DEPOSITS = 12;\n /// @dev The maximum number of validators that can be verified.\n uint256 internal constant MAX_VERIFIED_VALIDATORS = 48;\n /// @dev The default withdrawable epoch value on the Beacon chain.\n /// A value in the far future means the validator is not exiting.\n uint64 internal constant FAR_FUTURE_EPOCH = type(uint64).max;\n /// @dev The number of seconds between each beacon chain slot.\n uint64 internal constant SLOT_DURATION = 12;\n /// @dev The number of slots in each beacon chain epoch.\n uint64 internal constant SLOTS_PER_EPOCH = 32;\n /// @dev Minimum time in seconds to allow snapped balances to be verified.\n /// Set to 1 epoch as the pending deposits only changes every epoch.\n /// That's also enough time to generate the proofs and call `verifyBalances`.\n uint64 internal constant SNAP_BALANCES_DELAY =\n SLOTS_PER_EPOCH * SLOT_DURATION;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n /// @notice The timestamp of the Beacon chain genesis.\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\n uint64 public immutable BEACON_GENESIS_TIMESTAMP;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// @notice Deposit data for new compounding validators.\n /// @dev A `VERIFIED` deposit can mean 3 separate things:\n /// - a deposit has been processed by the beacon chain and shall be included in the\n /// balance of the next verifyBalances call\n /// - a deposit has been done to a slashed validator and has probably been recovered\n /// back to this strategy. Probably because we can not know for certain. This contract\n /// only detects when the validator has passed its withdrawal epoch. It is close to impossible\n /// to prove with Merkle Proofs that the postponed deposit this contract is responsible for\n /// creating is not present anymore in BeaconChain.state.pending_deposits. This in effect\n /// means that there might be a period where this contract thinks the deposit has been already\n /// returned as ETH balance before it happens. This will result in some days (or weeks)\n /// -> depending on the size of deposit queue of showing a deficit when calling `checkBalance`.\n /// As this only offsets the yield and doesn't cause a critical double-counting we are not addressing\n /// this issue.\n /// - A deposit has been done to the validator, but our deposit has been front run by a malicious\n /// actor. Funds in the deposit this contract makes are not recoverable.\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified\n }\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountGwei Amount of ETH in gwei that has been deposited to the beacon chain deposit contract\n /// @param slot The beacon chain slot number when the deposit has been made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either UNKNOWN, PENDING or VERIFIED\n /// @param withdrawableEpoch The withdrawableEpoch of the validator which is being deposited to.\n /// At deposit time this is set to max default value (FAR_FUTURE_EPOCH). If a deposit has\n /// made to a slashed validator the `withdrawableEpoch` will be set to the epoch of that\n /// validator.\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint32 depositIndex;\n DepositStatus status;\n uint64 withdrawableEpoch;\n }\n /// @notice Restricts to only one deposit to an unverified validator at a time.\n /// This is to limit front-running attacks of deposits to the beacon chain contract.\n ///\n /// @dev The value is set to true when a deposit to a new validator has been done that has\n /// not yet be verified.\n bool public firstDeposit;\n /// @notice Unique identifier of the next validator deposit.\n uint128 public nextDepositID;\n /// @notice Mapping of the deposit ID to the deposit data\n mapping(uint256 => DepositData) public deposits;\n /// @notice List of strategy deposit IDs to a validator.\n /// The list can be for deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n /// The list may not be ordered by time of deposit.\n /// Removed deposits will move the last deposit to the removed index.\n uint256[] public depositList;\n\n enum ValidatorState {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITING, // The validator has been requested to exit or has been verified as forced exit\n EXITED, // The validator has been verified to have a zero balance\n REMOVED, // validator has funds withdrawn to this strategy contract and is removed from the SSV\n INVALID // The validator has been front-run and the withdrawal address is not this strategy\n }\n\n // Validator data\n struct ValidatorData {\n ValidatorState state; // The state of the validator known to this contract\n uint64 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n bytes32[] public verifiedValidators;\n /// @notice Mapping of the hash of the validator's public key to the validator state and index.\n /// Uses the Beacon chain hashing for BLSPubkey which is sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => ValidatorData) public validator;\n\n /// @param blockRoot Beacon chain block root of the snapshot\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n bytes32 blockRoot;\n uint64 timestamp;\n uint128 ethBalance;\n }\n /// @notice Mapping of the block root to the balances at that slot\n Balances public snappedBalance;\n /// @notice The last verified ETH balance of the strategy\n uint256 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[50] private __gap;\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event FirstDepositReset();\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n uint256 indexed depositID,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint64 indexed validatorIndex\n );\n event ValidatorInvalid(bytes32 indexed pubKeyHash);\n event DepositVerified(uint256 indexed depositID, uint256 amountWei);\n event DepositToValidatorExiting(\n uint256 indexed depositID,\n uint256 amountWei,\n uint64 withdrawableEpoch\n );\n event DepositValidatorExited(uint256 indexed depositID, uint256 amountWei);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(bytes32 indexed blockRoot, uint256 ethBalance);\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 ethBalance\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_PROOFS = _beaconProofs;\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\n\n require(\n block.timestamp > _beaconGenesisTimestamp,\n \"Invalid genesis timestamp\"\n );\n }\n\n /**\n *\n * Admin Functions\n *\n */\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Reset the `firstDeposit` flag to false so deposits to unverified validators can be made again.\n function resetFirstDeposit() external onlyGovernor {\n require(firstDeposit, \"No first deposit\");\n\n firstDeposit = false;\n\n emit FirstDepositReset();\n }\n\n /**\n *\n * Validator Management\n *\n */\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validator[pubKeyHash].state == ValidatorState.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validator[pubKeyHash].state = ValidatorState.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n struct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n }\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validatorStakeData validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validatorStakeData,\n uint64 depositAmountGwei\n ) external onlyRegistrator {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(depositList.length < MAX_DEPOSITS, \"Max deposits\");\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validatorStakeData.pubkey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.VERIFIED),\n \"Not registered or verified\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n if (currentState == ValidatorState.REGISTERED) {\n // Can only have one pending deposit to an unverified validator at a time.\n // This is to limit front-running deposit attacks to a single deposit.\n // The exiting deposit needs to be verified before another deposit can be made.\n // If there was a front-running attack, the validator needs to be verified as invalid\n // and the Governor calls `resetFirstDeposit` to set `firstDeposit` to false.\n require(!firstDeposit, \"Existing first deposit\");\n // Limits the amount of ETH that can be at risk from a front-running deposit attack.\n require(\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n // Limits the number of validator balance proofs to verifyBalances\n require(\n verifiedValidators.length + 1 < MAX_VERIFIED_VALIDATORS,\n \"Max validators\"\n );\n\n // Flag a deposit to an unverified validator so only no other deposits can be made\n // to an unverified validator.\n firstDeposit = true;\n }\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == ValidatorState.REGISTERED) {\n validator[pubKeyHash].state = ValidatorState.STAKED;\n }\n\n /// After the Pectra upgrade the validators have a new restriction when proposing\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\n /// forward. Each slot is created at strict 12 second intervals and those slots can\n /// either have blocks attached to them or not. This way using the block.timestamp\n /// the slot number can easily be calculated.\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\n BEACON_GENESIS_TIMESTAMP) / SLOT_DURATION;\n\n // Store the deposit data for verifyDeposit and verifyBalances\n uint256 depositID = nextDepositID++;\n deposits[depositID] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n slot: depositSlot,\n depositIndex: SafeCast.toUint32(depositList.length),\n status: DepositStatus.PENDING,\n withdrawableEpoch: FAR_FUTURE_EPOCH\n });\n depositList.push(depositID);\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validatorStakeData.pubkey,\n withdrawalCredentials,\n validatorStakeData.signature,\n validatorStakeData.depositDataRoot\n );\n\n emit ETHStaked(\n pubKeyHash,\n depositID,\n validatorStakeData.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then only the amount in excess of 32 ETH will be withdrawn.\n /// Only the Registrator can call this function.\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\n /// If no ETH balance, the tx will revert.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n payable\n onlyRegistrator\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n require(\n currentState == ValidatorState.VERIFIED,\n \"Validator not verified\"\n );\n\n // If a full withdrawal (validator exit)\n if (amountGwei == 0) {\n // Store the validator state as exiting so no more deposits can be made to it.\n validator[pubKeyHash].state = ValidatorState.EXITING;\n }\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove the validator from the SSV Cluster after:\n /// - the validator has been exited from `validatorWithdrawal` or slashed\n /// - the validator has incorrectly registered and can not be staked to\n /// - the initial deposit was front-run and the withdrawal address is not this strategy's address.\n /// Make sure `validatorWithdrawal` is called with a zero amount and the validator has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.EXITED ||\n currentState == ValidatorState.INVALID,\n \"Validator not regd or exited\"\n );\n\n validator[pubKeyHash].state = ValidatorState.REMOVED;\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /**\n *\n * SSV Management\n *\n */\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be withdrawn from the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /**\n *\n * Beacon Chain Proofs\n *\n */\n\n /// @notice Verifies a validator's index to its public key.\n /// Adds to the list of verified validators if the validator's withdrawal address is this strategy's address.\n /// Marks the validator as invalid and removes the deposit if the withdrawal address is not this strategy's address.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param withdrawalAddress The withdrawal address of the validator which should be this strategy's address.\n /// If the withdrawal address is not this strategy's address, the initial deposit was front-run\n /// and the validator is marked as invalid.\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n address withdrawalAddress,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validator[pubKeyHash].state == ValidatorState.STAKED,\n \"Validator not staked\"\n );\n\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key.\n // Also verify the validator's withdrawal credential points to the `withdrawalAddress`.\n IBeaconProofs(BEACON_PROOFS).verifyValidator(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n withdrawalAddress\n );\n\n // Store the validator state as verified\n validator[pubKeyHash] = ValidatorData({\n state: ValidatorState.VERIFIED,\n index: validatorIndex\n });\n\n // If the initial deposit was front-run and the withdrawal address is not this strategy\n if (withdrawalAddress != address(this)) {\n // override the validator state\n validator[pubKeyHash].state = ValidatorState.INVALID;\n\n // Find and remove the deposit as the funds can not be recovered\n uint256 depositCount = depositList.length;\n for (uint256 i = 0; i < depositCount; i++) {\n DepositData memory deposit = deposits[depositList[i]];\n if (deposit.pubKeyHash == pubKeyHash) {\n _removeDeposit(depositList[i], deposit);\n break;\n }\n }\n\n // Leave the `firstDeposit` flag as true so no more deposits to unverified validators can be made.\n // The Governor has to reset the `firstDeposit` to false before another deposit to\n // an unverified validator can be made.\n // The Governor can set a new `validatorRegistrator` if they suspect it has been compromised.\n\n emit ValidatorInvalid(pubKeyHash);\n return;\n }\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(pubKeyHash);\n\n // Reset the firstDeposit flag as the first deposit to an unverified validator has been verified.\n firstDeposit = false;\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n struct FirstPendingDepositProofData {\n uint64 slot;\n uint64 validatorIndex;\n bytes32 pubKeyHash;\n bytes pendingDepositPubKeyProof;\n bytes withdrawableEpochProof;\n bytes validatorPubKeyProof;\n }\n\n struct StrategyValidatorProofData {\n uint64 withdrawableEpoch;\n bytes withdrawableEpochProof;\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n ///\n /// Important: this function has a limitation where `depositProcessedSlot` and `firstDepositValidatorCreatedSlot`\n /// that is passed by the off-chain verifier requires a slot immediately after them to propose a block otherwise\n /// the `BeaconRoots.parentBlockRoot` will fail. This shouldn't be a problem, since by the current behaviour of\n /// beacon chain only 1%-3% slots don't propose a block.\n /// @param depositID The deposit ID emitted in `ETHStaked` from the `stakeEth` function.\n /// @param depositProcessedSlot Any slot on or after the strategy's deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\n /// set for the next block timestamp in 12 seconds time.\n /// @param firstDepositValidatorCreatedSlot The slot on or after when the validator of the first pending deposit\n /// was created on the beacon chain. This is used to verify the validator has not exited.\n /// If the first pending deposit is to an exiting validator, this can be the same slot as `depositProcessedSlot`.\n /// If the first pending deposit is to a new validator, the slot will have to be in the next epoch as the\n /// pending deposits are only processed at the start of each epoch.\n /// @param firstPendingDeposit a `FirstPendingDepositProofData` struct containing:\n /// - slot: The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero is a good choice.\n /// - validatorIndex: The index of the validator of the first pending deposit.\n /// Can be anything if the deposit queue is empty, but zero is a good choice.\n /// - pubKeyHash: The hash of the public key of the validator of the first pending deposit.\n /// Can be anything if the deposit queue is empty, but 32 zero bytes is a good choice\n /// - pendingDepositPubKeyProof: The merkle proof of the first pending deposit to the beacon block root.\n /// Can be either:\n /// * 40 witness hashes for BeaconBlock.state.PendingDeposits[0].pubKey when the deposit queue is not empty.\n /// * 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the first pending deposit's validator\n /// to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// - validatorPubKeyProof The merkle proof for the public key of the first pending deposit's validator\n /// to the this witness hash of withdrawableEpochProof.\n /// This is 2 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param strategyValidatorData a `StrategyValidatorProofData` struct containing:\n /// - withdrawableEpoch: The withdrawable epoch of the validator the strategy is depositing to.\n /// - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the validator the strategy\n /// is depositing to, to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n uint256 depositID,\n uint64 depositProcessedSlot,\n uint64 firstDepositValidatorCreatedSlot,\n FirstPendingDepositProofData calldata firstPendingDeposit,\n StrategyValidatorProofData calldata strategyValidatorData\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositID];\n ValidatorData memory strategyValidator = validator[deposit.pubKeyHash];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n strategyValidator.state == ValidatorState.VERIFIED,\n \"Validator not verified\"\n );\n // The verification slot must be after the deposit's slot.\n // This is needed for when the deposit queue is empty.\n require(deposit.slot < depositProcessedSlot, \"Slot not after deposit\");\n require(\n depositProcessedSlot <= firstDepositValidatorCreatedSlot,\n \"Invalid verification slots\"\n );\n\n // Get the parent beacon block root of the next block which is the block root of the deposit verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 depositBlockRoot = BeaconRoots.parentBlockRoot(\n _calcNextBlockTimestamp(depositProcessedSlot)\n );\n\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n depositBlockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.pubKeyHash,\n firstPendingDeposit.pendingDepositPubKeyProof\n );\n\n // If the deposit queue is not empty\n if (!isDepositQueueEmpty) {\n // Get the parent beacon block root of the next block which is\n // the block root of the validator verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 validatorBlockRoot = BeaconRoots.parentBlockRoot(\n _calcNextBlockTimestamp(firstDepositValidatorCreatedSlot)\n );\n\n // Verify the validator of the first pending deposit is not exiting.\n // If it is exiting we can't be sure this deposit has not been postponed in the deposit queue.\n // Hence we can not verify if the strategy's deposit has been processed or not.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n validatorBlockRoot,\n firstPendingDeposit.validatorIndex,\n firstPendingDeposit.pubKeyHash,\n FAR_FUTURE_EPOCH,\n firstPendingDeposit.withdrawableEpochProof,\n firstPendingDeposit.validatorPubKeyProof\n );\n }\n\n // Verify the withdrawableEpoch on the validator of the strategy's deposit\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n depositBlockRoot,\n strategyValidator.index,\n strategyValidatorData.withdrawableEpoch,\n strategyValidatorData.withdrawableEpochProof\n );\n\n // If the validator is exiting because it has been slashed\n if (strategyValidatorData.withdrawableEpoch != FAR_FUTURE_EPOCH) {\n // Store the exit epoch in the deposit data\n deposit.withdrawableEpoch = strategyValidatorData.withdrawableEpoch;\n\n emit DepositToValidatorExiting(\n depositID,\n uint256(deposit.amountGwei) * 1 gwei,\n strategyValidatorData.withdrawableEpoch\n );\n\n validator[deposit.pubKeyHash].state = ValidatorState.EXITING;\n\n // Leave the deposit status as PENDING\n return;\n }\n\n // solhint-disable max-line-length\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the deposit queue is empty then our deposit must have been processed on the beacon chain.\n // The deposit slot can be zero for validators consolidating to a compounding validator or 0x01 validator\n // being promoted to a compounding one. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // We can not guarantee that the deposit has been processed in that case.\n // solhint-enable max-line-length\n require(\n deposit.slot < firstPendingDeposit.slot || isDepositQueueEmpty,\n \"Deposit likely not processed\"\n );\n\n // Remove the deposit now it has been verified as processed on the beacon chain.\n _removeDeposit(depositID, deposit);\n\n emit DepositVerified(depositID, uint256(deposit.amountGwei) * 1 gwei);\n }\n\n function _removeDeposit(uint256 depositID, DepositData memory deposit)\n internal\n {\n // After verifying the proof, update the contract storage\n deposits[depositID].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n uint256 lastDeposit = depositList[depositList.length - 1];\n depositList[deposit.depositIndex] = lastDeposit;\n deposits[lastDeposit].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositList.pop();\n }\n\n /// @dev Calculates the timestamp of the next execution block from the given slot.\n /// @param slot The beacon chain slot number used for merkle proof verification.\n function _calcNextBlockTimestamp(uint64 slot)\n internal\n view\n returns (uint64)\n {\n // Calculate the next block timestamp from the slot.\n return SLOT_DURATION * slot + BEACON_GENESIS_TIMESTAMP + SLOT_DURATION;\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block and beacon block root\n /// of the slot that is associated with the previous block.\n ///\n /// When snapping / verifying balance it is of a high importance that there is no\n /// miss-match in respect to ETH that is held by the contract and balances that are\n /// verified on the validators.\n ///\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\n /// constructing a block on the beacon chain consist of:\n /// - process_withdrawals: ETH is deducted from the validator's balance\n /// - process_execution_payload: immediately after the previous step executing all the\n /// transactions\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\n /// contained in the withdrawal credentials of the exited validators\n ///\n /// That means that balance increases which are part of the post-block execution state are\n /// done within the block, but the transaction that are contained within that block can not\n /// see / interact with the balance from the exited validators. Only transactions in the\n /// next block can do that.\n ///\n /// When snap balances is performed the state of the chain is snapped across 2 separate\n /// chain states:\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\n /// will be referred to as Y - 1 as it makes no difference in the argument\n ///\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\n /// miss-counting ETH or much more dangerous double counting of the ETH.\n ///\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\n /// - ETH balance in the contract on block X\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\n /// - ETH balance in validators that are active in slot Y - 1\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\n /// and have their balance visible to transactions in slot Y and corresponding block X\n /// (or sooner)\n ///\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\n ///\n /// *ETH balance in the contract on block X*\n ///\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\n ///\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\n /// would result in double counting the `stakedEth` since it would be present once in the\n /// snapped contract balance and the second time in deposit storage variables.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\n ///\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\n /// contract at block Z. The execution layer doesn't have direct access to the state of\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\n /// and could also be part of the validator balances. It does that by verifying that at\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\n /// the last snap till now all are still in queue. Which ensures they can not be part of\n /// the validator balances in later steps.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in validators that are active in slot Y - 1*\n ///\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\n /// correctness.\n ///\n /// This behaviour is correct.\n ///\n /// *The withdrawn validators*\n ///\n /// The withdrawn validators could have their balances deducted in any slot before slot\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\n /// look at the \"worst case scenario\" where the validator withdrawal is processed in the\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\n /// even if the validator exits at the latest possible time it is paramount that the ETH\n /// balance on the execution layer is recorded in the next block. Correctly accounting\n /// for the withdrawn ETH.\n ///\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\n /// validator balance verification as well as execution layer contract balance snap.\n ///\n /// This behaviour is correct.\n ///\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n function snapBalances() external {\n uint64 currentTimestamp = SafeCast.toUint64(block.timestamp);\n require(\n snappedBalance.timestamp + SNAP_BALANCES_DELAY < currentTimestamp,\n \"Snap too soon\"\n );\n\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(currentTimestamp);\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the snapped balance\n snappedBalance = Balances({\n blockRoot: blockRoot,\n timestamp: currentTimestamp,\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n emit BalancesSnapped(blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct BalanceProofs {\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n /// @param balanceProofs a `BalanceProofs` struct containing the following:\n /// balancesContainerRoot - the merkle root of the balances container\n /// balancesContainerProof - The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// validatorBalanceLeaves - Array of leaf nodes containing the validator balance with three other balances.\n /// validatorBalanceProofs - Array of merkle proofs for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(\n uint64 validatorVerificationBlockTimestamp,\n FirstPendingDepositProofData calldata firstPendingDeposit,\n BalanceProofs calldata balanceProofs\n ) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalance;\n // Check the balances are the latest\n require(balancesMem.timestamp > 0, \"No snapped balances\");\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n balanceProofs.validatorBalanceProofs.length ==\n verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n balanceProofs.validatorBalanceLeaves.length ==\n verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n balancesMem.blockRoot,\n balanceProofs.balancesContainerRoot,\n balanceProofs.balancesContainerProof\n );\n\n // for each validator in reserve order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n balanceProofs.balancesContainerRoot,\n balanceProofs.validatorBalanceLeaves[i],\n balanceProofs.validatorBalanceProofs[i],\n validator[verifiedValidators[i]].index\n );\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n // This could have been in VERIFIED or EXITING state\n validator[verifiedValidators[i]].state = ValidatorState\n .EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += validatorBalanceGwei * 1 gwei;\n }\n }\n\n uint256 depositsCount = depositList.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification.\n // This section is after the validator balance verifications so an exited validator will be marked\n // as EXITED before the deposits are verified. If there was a deposit to an exited validator\n // then the deposit can only be removed once the validator is fully exited.\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n balancesMem.blockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.pubKeyHash,\n firstPendingDeposit.pendingDepositPubKeyProof\n );\n\n // If there are no deposits in the beacon chain queue then our deposits must have been processed.\n // If the deposits have been processed, each deposit will need to be verified with `verifyDeposit`\n require(!isDepositQueueEmpty, \"Deposits have been processed\");\n\n // The verification of the validator the first pending deposit is for must be on or after when\n // `snapBalances` was called.\n require(\n balancesMem.timestamp <= validatorVerificationBlockTimestamp,\n \"Invalid validator timestamp\"\n );\n\n // Verify the validator of the first pending deposit is not exiting by checking\n // the withdrawable epoch is far into the future.\n // If it is exiting we can't be sure this deposit has not been postponed in the deposit queue.\n // Hence we can not verify if the strategy's deposit has been processed or not.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n // Get the parent beacon block root of the next block which is\n // the block root of the validator verification slot.\n // This will revert if the slot after the verification slot was missed.\n BeaconRoots.parentBlockRoot(\n validatorVerificationBlockTimestamp\n ),\n firstPendingDeposit.validatorIndex,\n firstPendingDeposit.pubKeyHash,\n // Validator is not exiting\n FAR_FUTURE_EPOCH,\n firstPendingDeposit.withdrawableEpochProof,\n firstPendingDeposit.validatorPubKeyProof\n );\n\n // solhint-disable max-line-length\n // If a validator is converted from a sweeping validator to a compounding validator, any balance in excess\n // of the min 32 ETH is put in the pending deposit queue. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // This will have a slot value of zero unfortunately.\n // We can not prove the strategy's deposits are still pending with a zero slot value so revert the tx.\n // Another snapBalances will need to be taken that does not have consolidation deposits at the front of the\n // beacon chain deposit queue.\n // solhint-enable max-line-length\n require(\n firstPendingDeposit.slot > 0,\n \"Invalid first pending deposit\"\n );\n\n // Calculate the epoch at the time of the snapBalances\n uint64 verificationEpoch = (SafeCast.toUint64(\n balancesMem.timestamp\n ) - BEACON_GENESIS_TIMESTAMP) / (SLOT_DURATION * SLOTS_PER_EPOCH);\n\n // For each staking strategy's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n uint256 depositID = depositList[i];\n DepositData memory depositData = deposits[depositID];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit slot is before the slot of the staking strategy's deposit.\n // If the deposit has been processed, it will need to be verified with `verifyDeposit`.\n // OR the deposit is to an exiting validator so check it is still not withdrawable.\n // If the validator is not withdrawable, then the deposit can not have been processed yet.\n // If the validator is now withdrawable, then the deposit may have been processed. The strategy\n // now has to wait until the validator's balance is verified to be zero.\n // OR the validator has exited and the deposit is now verified as processed.\n require(\n firstPendingDeposit.slot < depositData.slot ||\n (verificationEpoch < depositData.withdrawableEpoch &&\n depositData.withdrawableEpoch !=\n FAR_FUTURE_EPOCH) ||\n validator[depositData.pubKeyHash].state ==\n ValidatorState.EXITED,\n \"Deposit likely processed\"\n );\n\n // Remove the deposit if the validator has exited.\n if (\n validator[depositData.pubKeyHash].state ==\n ValidatorState.EXITED\n ) {\n _removeDeposit(depositID, depositData);\n\n emit DepositValidatorExited(\n depositID,\n uint256(depositData.amountGwei) * 1 gwei\n );\n\n // Skip to the next deposit as the deposit amount is now in the strategy's ETH balance\n continue;\n }\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei += uint256(depositData.amountGwei) * 1 gwei;\n }\n }\n\n // Store the verified balance in storage\n lastVerifiedEthBalance =\n totalDepositsWei +\n totalValidatorBalance +\n balancesMem.ethBalance;\n // Reset the last snap timestamp so a new snapBalances has to be made\n snappedBalance.timestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n require(pubKey.length == 48, \"Invalid public key length\");\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /**\n *\n * WETH and ETH Accounting\n *\n */\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n // The min is required as more WETH can be withdrawn than deposited\n // as the strategy earns consensus and execution rewards.\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance.\n // The ETH balance in this strategy contract can be more than the last verified ETH balance\n // due to partial withdrawals or full exits being processed by the beacon chain since the last snapBalances.\n // It can also happen from execution rewards (MEV) or ETH donations.\n lastVerifiedEthBalance -= Math.min(lastVerifiedEthBalance, _ethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n snappedBalance.timestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += _wethAmount;\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n snappedBalance.timestamp = 0;\n }\n\n /**\n *\n * View Functions\n *\n */\n\n /// @notice Returns the number of deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n function depositListLength() external view returns (uint256) {\n return depositList.length;\n }\n\n /// @notice Returns the number of verified validators.\n function verifiedValidatorsLength() external view returns (uint256) {\n return verifiedValidators.length;\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/cfd2e1e193f6a929480fb019cc024c27.json b/contracts/deployments/hoodi/solcInputs/cfd2e1e193f6a929480fb019cc024c27.json new file mode 100644 index 0000000000..1dc2fa9075 --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/cfd2e1e193f6a929480fb019cc024c27.json @@ -0,0 +1,126 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/beacon/BeaconConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to request validator consolidation on the beacon chain.\n * @author Origin Protocol Inc\n */\nlibrary BeaconConsolidation {\n /// @notice The address the validator consolidation requests are sent\n /// See https://eips.ethereum.org/EIPS/eip-7251\n address internal constant CONSOLIDATION_REQUEST_ADDRESS =\n 0x0000BBdDc7CE488642fb579F8B00f3a590007251;\n\n function request(bytes calldata source, bytes calldata target)\n internal\n returns (uint256 fee_)\n {\n require(source.length == 48, \"Invalid source byte length\");\n require(target.length == 48, \"Invalid target byte length\");\n\n fee_ = fee();\n\n // Call the Consolidation Request contract with the public keys of the source and target\n // validators packed together.\n // This does not have a function signature, so we use a call\n (bool success, ) = CONSOLIDATION_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(source, target)\n );\n\n require(success, \"Consolidation request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the consolidation request contract\n (bool success, bytes memory result) = CONSOLIDATION_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/beacon/BeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"./BeaconProofsLib.sol\";\n\n/**\n * @title Verifies merkle proofs of beacon chain data.\n * @author Origin Protocol Inc\n */\ncontract BeaconProofs {\n /// @notice Verifies the validator public key to the beacon block root\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view {\n BeaconProofsLib.verifyValidatorPubkey(\n beaconBlockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n withdrawalAddress\n );\n }\n\n /// @notice Verifies the balances container to the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerRoot The merkle root of the the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata balancesContainerProof\n ) external view {\n BeaconProofsLib.verifyBalancesContainer(\n beaconBlockRoot,\n balancesContainerRoot,\n balancesContainerProof\n );\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param balanceProof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalanceGwei) {\n validatorBalanceGwei = BeaconProofsLib.verifyValidatorBalance(\n balancesContainerRoot,\n validatorBalanceLeaf,\n balanceProof,\n validatorIndex\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the slot of the first pending deposit to the beacon block root\n /// BeaconBlock.state.PendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero would be a good choice.\n /// @param firstPendingDepositSlotProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view returns (bool isEmptyDepositQueue) {\n isEmptyDepositQueue = BeaconProofsLib.verifyFirstPendingDepositSlot(\n beaconBlockRoot,\n slot,\n firstPendingDepositSlotProof\n );\n }\n}\n" + }, + "contracts/beacon/BeaconProofsLib.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Merkle } from \"./Merkle.sol\";\nimport { Endian } from \"./Endian.sol\";\n\n/**\n * @title Library to verify merkle proofs of beacon chain data.\n * @author Origin Protocol Inc\n */\nlibrary BeaconProofsLib {\n // Known generalized indices in the beacon block\n /// @dev BeaconBlock.slot\n uint256 internal constant SLOT_GENERALIZED_INDEX = 8;\n /// @dev BeaconBlock.state.PendingDeposits[0]\n uint256 internal constant FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX =\n 198105366528;\n /// @dev BeaconBlock.state.PendingDeposits[0].slot\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX =\n 1584842932228;\n /// @dev BeaconBlock.body.executionPayload.blockNumber\n uint256 internal constant BLOCK_NUMBER_GENERALIZED_INDEX = 6438;\n /// @dev BeaconBlock.state.validators\n uint256 internal constant VALIDATORS_CONTAINER_GENERALIZED_INDEX = 715;\n /// @dev BeaconBlock.state.balances\n uint256 internal constant BALANCES_CONTAINER_GENERALIZED_INDEX = 716;\n\n /// @dev Number of bytes in the proof to the first pending deposit.\n /// 37 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0]\n uint256 internal constant FIRST_PENDING_DEPOSIT_PROOF_LENGTH = 37 * 32;\n /// @dev Number of bytes in the proof to the slot of the first pending deposit.\n /// 40 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0].slot\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_PROOF_LENGTH = 40 * 32;\n\n /// @dev Merkle height of the Balances container\n /// BeaconBlock.state.balances\n uint256 internal constant BALANCES_HEIGHT = 39;\n /// @dev Merkle height of the Validators container\n /// BeaconBlock.state.validators\n uint256 internal constant VALIDATORS_HEIGHT = 41;\n /// @dev Merkle height of the Validator container\n /// BeaconBlock.state.validators[validatorIndex]\n uint256 internal constant VALIDATOR_HEIGHT = 3;\n\n /// @dev Position of the pubkey field in the Validator container.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;\n\n /// @notice Verifies the validator public key to the beacon block root\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) internal view {\n // BeaconBlock.state.validators[validatorIndex].pubkey\n uint256 generalizedIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_HEIGHT,\n validatorIndex\n );\n generalizedIndex = concatGenIndices(\n generalizedIndex,\n VALIDATOR_HEIGHT,\n VALIDATOR_PUBKEY_INDEX\n );\n\n // Get the withdrawal address from the first witness in the pubkey merkle proof.\n address withdrawalAddressFromProof;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // The first 32 bytes of the proof is the withdrawal credential so load it into memory.\n calldatacopy(0, validatorPubKeyProof.offset, 32)\n // Cast the 32 bytes in memory to an address which is the last 20 bytes.\n withdrawalAddressFromProof := mload(0)\n }\n require(\n withdrawalAddressFromProof == withdrawalAddress,\n \"Invalid withdrawal address\"\n );\n\n require(\n Merkle.verifyInclusionSha256({\n proof: validatorPubKeyProof,\n root: beaconBlockRoot,\n leaf: pubKeyHash,\n index: generalizedIndex\n }),\n \"Invalid validator pubkey proof\"\n );\n }\n\n /// @notice Verifies the balances container to the beacon block root.\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param balancesContainerRoot The merkle root of the the balances container.\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata balancesContainerProof\n ) internal view {\n // BeaconBlock.state.balances\n require(\n Merkle.verifyInclusionSha256({\n proof: balancesContainerProof,\n root: beaconBlockRoot,\n leaf: balancesContainerRoot,\n index: BALANCES_CONTAINER_GENERALIZED_INDEX\n }),\n \"Invalid balance container proof\"\n );\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param balanceProof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for.\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index.\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) internal view returns (uint256 validatorBalanceGwei) {\n // Four balances are stored in each leaf so the validator index is divided by 4\n uint64 balanceIndex = validatorIndex / 4;\n\n // Get the index within the balances container, not the Beacon Block\n // BeaconBlock.state.balances[balanceIndex]\n uint256 generalizedIndex = concatGenIndices(\n 1,\n BALANCES_HEIGHT,\n balanceIndex\n );\n\n validatorBalanceGwei = balanceAtIndex(\n validatorBalanceLeaf,\n validatorIndex\n );\n\n require(\n Merkle.verifyInclusionSha256({\n proof: balanceProof,\n root: balancesContainerRoot,\n leaf: validatorBalanceLeaf,\n index: generalizedIndex\n }),\n \"Invalid balance proof\"\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the slot of the first pending deposit to the beacon block root\n /// BeaconBlock.state.PendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero would be a good choice.\n /// @param firstPendingDepositSlotProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) internal view returns (bool isEmptyDepositQueue) {\n uint256 generalizedIndex;\n bytes32 leaf;\n // If the deposit queue is empty\n if (\n firstPendingDepositSlotProof.length ==\n FIRST_PENDING_DEPOSIT_PROOF_LENGTH\n ) {\n isEmptyDepositQueue = true;\n // use an empty leaf node as the root of the first pending deposit\n // when the deposit queue is empty\n leaf = bytes32(0);\n // BeaconBlock.state.PendingDeposits[0]\n generalizedIndex = FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX;\n } else if (\n firstPendingDepositSlotProof.length ==\n FIRST_PENDING_DEPOSIT_SLOT_PROOF_LENGTH\n ) {\n // Convert uint64 slot number to a little endian bytes32\n leaf = Endian.toLittleEndianUint64(slot);\n // BeaconBlock.state.PendingDeposits[0].slot\n generalizedIndex = FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX;\n } else {\n revert(\"Invalid proof length\");\n }\n\n require(\n Merkle.verifyInclusionSha256({\n proof: firstPendingDepositSlotProof,\n root: beaconBlockRoot,\n leaf: leaf,\n index: generalizedIndex\n }),\n \"Invalid pending deposit proof\"\n );\n }\n\n ////////////////////////////////////////////////////\n /// Internal Helper Functions\n ////////////////////////////////////////////////////\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n internal\n pure\n returns (uint256)\n {\n uint256 bitShiftAmount = (validatorIndex % 4) * 64;\n return\n Endian.fromLittleEndianUint64(\n bytes32((uint256(validatorBalanceLeaf) << bitShiftAmount))\n );\n }\n\n /// @notice Concatenates two beacon chain generalized indices into one.\n /// @param genIndex The first generalized index or 1 if calculating for a single container.\n /// @param height The merkle tree height of the second container. eg 39 for balances, 41 for validators.\n /// @param index The index within the second container. eg the validator index.\n /// @return genIndex The concatenated generalized index.\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) internal pure returns (uint256) {\n return (genIndex << height) | index;\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to retrieve beacon block roots.\n * @author Origin Protocol Inc\n */\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the beacon block root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Roots contract to get the parent block root.\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/Endian.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n/**\n * @title Library to handle conversion between little-endian and big-endian formats.\n * @author Origin Protocol Inc\n */\nlibrary Endian {\n /**\n * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64\n * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type\n * @return n The big endian-formatted uint64\n * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits),\n * but it is immediately truncated to a uint64 (i.e. 64 bits)\n * through a right-shift/shr operation.\n */\n function fromLittleEndianUint64(bytes32 lenum)\n internal\n pure\n returns (uint64 n)\n {\n // the number needs to be stored in little-endian encoding (ie in bytes 0-8)\n n = uint64(uint256(lenum >> 192));\n // forgefmt: disable-next-item\n return\n (n >> 56) |\n ((0x00FF000000000000 & n) >> 40) |\n ((0x0000FF0000000000 & n) >> 24) |\n ((0x000000FF00000000 & n) >> 8) |\n ((0x00000000FF000000 & n) << 8) |\n ((0x0000000000FF0000 & n) << 24) |\n ((0x000000000000FF00 & n) << 40) |\n ((0x00000000000000FF & n) << 56);\n }\n\n function toLittleEndianUint64(uint64 benum)\n internal\n pure\n returns (bytes32 n)\n {\n // Convert to little-endian by reversing byte order\n uint64 reversed = (benum >> 56) |\n ((0x00FF000000000000 & benum) >> 40) |\n ((0x0000FF0000000000 & benum) >> 24) |\n ((0x000000FF00000000 & benum) >> 8) |\n ((0x00000000FF000000 & benum) << 8) |\n ((0x0000000000FF0000 & benum) << 24) |\n ((0x000000000000FF00 & benum) << 40) |\n ((0x00000000000000FF & benum) << 56);\n\n // Store the little-endian uint64 in the least significant 64 bits of bytes32\n n = bytes32(uint256(reversed));\n // Shift to most significant bits\n n = n << 192;\n }\n}\n" + }, + "contracts/beacon/Merkle.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev These functions deal with verification of Merkle Tree proofs.\n *\n * The tree and the proofs can be generated using our\n * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].\n * You will find a quickstart guide in the readme.\n *\n * WARNING: You should avoid using leaf values that are 64 bytes long prior to\n * hashing, or use a hash function other than keccak256 for hashing leaves.\n * This is because the concatenation of a sorted pair of internal nodes in\n * the merkle tree could be reinterpreted as a leaf value.\n * OpenZeppelin's JavaScript library generates merkle trees that are safe\n * against this attack out of the box.\n */\nlibrary Merkle {\n error InvalidProofLength();\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function verifyInclusionSha256(\n bytes memory proof,\n bytes32 root,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bool) {\n return processInclusionProofSha256(proof, leaf, index) == root;\n }\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * _Available since v4.4._\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function processInclusionProofSha256(\n bytes memory proof,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bytes32) {\n require(\n proof.length != 0 && proof.length % 32 == 0,\n InvalidProofLength()\n );\n bytes32[1] memory computedHash = [leaf];\n for (uint256 i = 32; i <= proof.length; i += 32) {\n if (index % 2 == 0) {\n // if ith bit of index is 0, then computedHash is a left sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(computedHash))\n mstore(0x20, mload(add(proof, i)))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n index := div(index, 2)\n }\n } else {\n // if ith bit of index is 1, then computedHash is a right sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(add(proof, i)))\n mstore(0x20, mload(computedHash))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n index := div(index, 2)\n }\n }\n }\n return computedHash[0];\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to request full or partial withdrawals from validators on the beacon chain.\n * @author Origin Protocol Inc\n */\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n enum BalanceProofLevel {\n Container,\n BeaconBlock\n }\n\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view returns (bool isEmptyDepositQueue);\n\n function verifyBlockNumber(\n bytes32 beaconBlockRoot,\n uint256 blockNumber,\n bytes calldata blockNumberProof\n ) external view;\n\n function verifySlot(\n bytes32 beaconBlockRoot,\n uint256 slot,\n bytes calldata slotProof\n ) external view;\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n external\n pure\n returns (uint256);\n\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) external pure returns (uint256);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/mocks/MockBeaconConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconConsolidation } from \"../beacon/BeaconConsolidation.sol\";\n\ncontract MockBeaconConsolidation {\n function fee() external view returns (uint256) {\n return BeaconConsolidation.fee();\n }\n\n function request(bytes calldata source, bytes calldata target)\n external\n returns (uint256 fee_)\n {\n return BeaconConsolidation.request(source, target);\n }\n}\n" + }, + "contracts/mocks/MockBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"../beacon/BeaconProofsLib.sol\";\nimport { BeaconProofs } from \"../beacon/BeaconProofs.sol\";\n\ncontract MockBeaconProofs is BeaconProofs {\n function concatGenIndices(\n uint256 index1,\n uint256 height2,\n uint256 index2\n ) external pure returns (uint256 genIndex) {\n return BeaconProofsLib.concatGenIndices(index1, height2, index2);\n }\n}\n" + }, + "contracts/mocks/MockBeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconRoots } from \"../beacon/BeaconRoots.sol\";\n\ncontract MockBeaconRoots {\n // Mapping to simulate the ring buffer: timestamp => beacon block root\n mapping(uint256 => bytes32) internal _beaconRoots;\n\n // Event to log when a new root is set (for testing)\n event RootSet(uint256 indexed timestamp, bytes32 root);\n\n // Fallback function to handle raw 32-byte timestamp input\n // solhint-disable no-complex-fallback\n fallback() external {\n // Ensure input is exactly 32 bytes (big-endian encoded timestamp)\n require(msg.data.length == 32, \"Input must be 32 bytes\");\n\n // Decode the 32-byte input as a uint256 timestamp (big-endian)\n uint256 timestamp = abi.decode(msg.data, (uint256));\n\n // Don't do any validation of timestamp so we can test any block\n\n // Retrieve the root. Will return bytes32(0) if not set.\n bytes32 root = _beaconRoots[timestamp];\n\n // Return the 32-byte root directly\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0, root)\n return(0, 32)\n }\n }\n\n // Mock function to set a beacon block root (for testing)\n function setBeaconRoot(uint256 timestamp, bytes32 root) external {\n require(timestamp > 0, \"Invalid timestamp\");\n require(root != bytes32(0), \"Invalid root\");\n\n // Store the root at the given timestamp\n _beaconRoots[timestamp] = root;\n\n emit RootSet(timestamp, root);\n }\n\n function setBeaconRoot(bytes32 root) external {\n require(root != bytes32(0), \"Invalid root\");\n\n // Store the root at the given timestamp\n _beaconRoots[block.timestamp] = root;\n\n emit RootSet(block.timestamp, root);\n }\n\n function parentBlockRoot(uint64 timestamp)\n external\n view\n returns (bytes32 parentRoot)\n {\n return BeaconRoots.parentBlockRoot(timestamp);\n }\n\n function latestBlockRoot()\n external\n view\n returns (bytes32 parentRoot, uint64 timestamp)\n {\n timestamp = uint64(block.timestamp);\n parentRoot = BeaconRoots.parentBlockRoot(timestamp);\n }\n}\n" + }, + "contracts/mocks/MockPartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PartialWithdrawal } from \"../beacon/PartialWithdrawal.sol\";\n\ncontract MockPartialWithdrawal {\n function fee() external view returns (uint256) {\n return PartialWithdrawal.fee();\n }\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n external\n returns (uint256 fee_)\n {\n return PartialWithdrawal.request(validatorPubKey, amount);\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconProofs,\n _beaconGenesisTimestamp\n )\n {\n SSV_TOKEN = _ssvToken;\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// @param _rewardTokenAddresses Not used so empty array\n /// @param _assets Not used so empty array\n /// @param _pTokens Not used so empty array\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH that was transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _asset, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(_asset, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, WETH, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Accounts for all the assets managed by this strategy which includes:\n /// 1. The current WETH in this strategy contract\n /// 2. The last verified ETH balance, total deposits and total validator balances\n /// @param _asset Address of WETH asset.\n /// @return balance Total value in ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by the strategy.\n /// @param _asset The address of the WETH token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for validator registration.\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens when validators are registered\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n * The new ETH will be accounted for in `checkBalance` after the next snapBalances and verifyBalances txs.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n /// @dev This strategy does not use a platform token like the old Aave and Compound strategies.\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instead of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract.\n /// Withdrawals from validators also accumulate as ETH in this strategy contract.\n /// It's too complex to separate the rewards from withdrawals so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can now regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable {\n using SafeERC20 for IERC20;\n\n /// @notice The amount of ETH in wei that is required for a deposit to a new validator.\n /// Initially this is 32 ETH, but will be increased to 1 ETH after P2P's APIs have been updated\n /// to support deposits of 1 ETH.\n uint256 public constant DEPOSIT_AMOUNT_WEI = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n /// @notice The timestamp of the Beacon chain genesis.\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\n uint64 public immutable BEACON_GENESIS_TIMESTAMP;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// Deposit data for new compounding validators.\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountWei Amount of ETH in wei that has been deposited to the beacon chain deposit contract\n /// @param blockNumber Block number when the deposit was made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either PENDING or PROVEN\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint32 depositIndex;\n DepositStatus status;\n }\n /// @notice Mapping of the root of a deposit (depositDataRoot) to its data\n mapping(bytes32 => DepositData) public deposits;\n /// @notice List of deposit roots that are still to be verified as processed on the beacon chain\n bytes32[] public depositsRoots;\n\n // Validator data\n\n struct ValidatorData {\n bytes32 pubKeyHash; // Hash of the validator's public key using the Beacon Chain's format\n uint64 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes and indexes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n ValidatorData[] internal verifiedValidators;\n /// @notice State of the new compounding validators with a 0x02 withdrawal credential prefix.\n /// Uses the Beacon chain hashing for BLSPubkey which is\n /// sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => VALIDATOR_STATE) public validatorState;\n\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n uint64 timestamp;\n uint128 ethBalance;\n }\n // TODO is it more efficient to use the block root rather than hashing it?\n /// @notice Mapping of the block root to the balances at that slot\n mapping(bytes32 => Balances) public snappedBalances;\n uint64 public lastSnapTimestamp;\n uint128 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[50] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITED, // The validator has been verified to have a zero balance\n REMOVED // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified and is ready to be staked\n }\n\n event RegistratorChanged(address indexed newAddress);\n event SourceStrategyAdded(address indexed strategy);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n bytes32 indexed depositDataRoot,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint64 indexed validatorIndex\n );\n event DepositVerified(bytes32 indexed depositDataRoot, uint256 amountWei);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(\n uint256 indexed timestamp,\n bytes32 indexed blockRoot,\n uint256 ethBalance\n );\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 wethBalance,\n uint256 ethBalance\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_PROOFS = _beaconProofs;\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\n\n require(\n block.timestamp > _beaconGenesisTimestamp,\n \"Invalid genesis timestamp\"\n );\n }\n\n /***************************************\n Admin Functions\n ****************************************/\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /***************************************`\n Validator Management\n ****************************************/\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validatorState[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validator validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validator,\n uint64 depositAmountGwei\n ) external onlyRegistrator {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validator.pubkey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == VALIDATOR_STATE.REGISTERED ||\n currentState == VALIDATOR_STATE.VERIFIED),\n \"Not registered or verified\"\n );\n require(\n currentState == VALIDATOR_STATE.VERIFIED ||\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validator.pubkey,\n withdrawalCredentials,\n validator.signature,\n validator.depositDataRoot\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == VALIDATOR_STATE.REGISTERED) {\n validatorState[pubKeyHash] = VALIDATOR_STATE.STAKED;\n }\n\n /// After the Pectra upgrade the validators have a new restriction when proposing\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\n /// forward. Each slot is created at strict 12 second intervals and those slots can\n /// either have blocks attached to them or not. This way using the block.timestamp\n /// the slot number can easily be calculated.\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\n BEACON_GENESIS_TIMESTAMP) / 12;\n\n // Store the deposit data for verifyDeposit and verifyBalances\n deposits[validator.depositDataRoot] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n slot: depositSlot,\n depositIndex: SafeCast.toUint32(depositsRoots.length),\n status: DepositStatus.PENDING\n });\n depositsRoots.push(validator.depositDataRoot);\n\n emit ETHStaked(\n pubKeyHash,\n validator.depositDataRoot,\n validator.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then the request will fail.\n /// Only the Registrator can call this function.\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\n /// If no ETH balance, the tx will revert.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n payable\n onlyRegistrator\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove a sweeping validator from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == VALIDATOR_STATE.EXITED ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exited\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validatorState[pubKeyHash] = VALIDATOR_STATE.REMOVED;\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /***************************************\n SSV Management\n ****************************************/\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n Beacon Chain Proofs\n ****************************************/\n\n /// @notice Verifies a validator's index to its public key.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.STAKED,\n \"Validator not staked\"\n );\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key.\n // Also verify the validator's withdrawal credential points to this strategy.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n address(this) // Withdrawal address is this strategy\n );\n\n // Store the validator state as verified\n validatorState[pubKeyHash] = VALIDATOR_STATE.VERIFIED;\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(\n ValidatorData({ pubKeyHash: pubKeyHash, index: validatorIndex })\n );\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n ///\n /// Important: this function has a limitation where the `verificationSlot` that is passed by the off-chain\n /// verifier requires a slot immediately after it to propose a block otherwise the `BeaconRoots.parentBlockRoot`\n /// will fail. This shouldn't be a problem, since by the current behaviour of beacon chain only 1%-3% slots\n /// don't propose a block.\n /// @param depositDataRoot The root of the deposit data that was stored when\n /// the deposit was made on the execution layer.\n /// @param verificationSlot Any slot on or after the deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\n /// set for the next block timestamp in 12 seconds time.\n /// @param firstPendingDepositSlot The slot of the first pending deposit in the beacon chain.\n /// Can be anything if the deposit queue is empty, but zero is a good choice.\n /// @param firstPendingDepositSlotProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n bytes32 depositDataRoot,\n uint64 verificationSlot,\n uint64 firstPendingDepositSlot,\n bytes calldata firstPendingDepositSlotProof\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositDataRoot];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n validatorState[deposit.pubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n // The verification slot must be after the deposit's slot.\n // This is needed for when the deposit queue is empty.\n require(deposit.slot < verificationSlot, \"Slot not after deposit\");\n\n // Calculate the next block timestamp from the verification slot.\n uint64 nextBlockTimestamp = 12 *\n verificationSlot +\n BEACON_GENESIS_TIMESTAMP +\n // Add 12 seconds for the next block\n 12;\n // Get the parent beacon block root of the next block which is the block root of the verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDepositSlot(\n blockRoot,\n firstPendingDepositSlot,\n firstPendingDepositSlotProof\n );\n\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the deposit queue is empty then our deposit must have been processed on the beacon chain.\n require(\n deposit.slot < firstPendingDepositSlot || isDepositQueueEmpty,\n \"Deposit not processed\"\n );\n\n // After verifying the proof, update the contract storage\n deposits[depositDataRoot].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n bytes32 lastDepositDataRoot = depositsRoots[depositsRoots.length - 1];\n depositsRoots[deposit.depositIndex] = lastDepositDataRoot;\n deposits[lastDepositDataRoot].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositsRoots.pop();\n\n emit DepositVerified(\n depositDataRoot,\n uint256(deposit.amountGwei) * 1 gwei\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block and beacon block root\n /// of the slot that is associated with the previous block.\n ///\n /// When snapping / verifying balance it is of a high importance that there is no\n /// miss-match in respect to ETH that is held by the contract and balances that are\n /// verified on the validators.\n ///\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\n /// constructing a block on the beacon chain consist of:\n /// - process_withdrawals: ETH is deducted from the validator's balance\n /// - process_execution_payload: immediately after the previous step executing all the\n /// transactions\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\n /// contained in the withdrawal credentials of the exited validators\n ///\n /// That means that balance increases which are part of the post-block execution state are\n /// done within the block, but the transaction that are contained within that block can not\n /// see / interact with the balance from the exited validators. Only transactions in the\n /// next block can do that.\n ///\n /// When snap balances is performed the state of the chain is snapped across 2 separate\n /// chain states:\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\n /// will be referred to as Y - 1 as it makes no difference in the argument\n ///\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\n /// miss-counting ETH or much more dangerous double counting of the ETH.\n ///\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\n /// - ETH balance in the contract on block X\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\n /// - ETH balance in validators that are active in slot Y - 1\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\n /// and have their balance visible to transactions in slot Y and corresponding block X\n /// (or sooner)\n ///\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\n ///\n /// *ETH balance in the contract on block X*\n ///\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\n ///\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\n /// would result in double counting the `stakedEth` since it would be present once in the\n /// snapped contract balance and the second time in deposit storage variables.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\n ///\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\n /// contract at block Z. The execution layer doesn't have direct access to the state of\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\n /// and could also be part of the validator balances. It does that by verifying that at\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\n /// the last snap till now all are still in queue. Which ensures they can not be part of\n /// the validator balances in later steps.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in validators that are active in slot Y - 1*\n ///\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\n /// correctness.\n ///\n /// This behaviour is correct.\n ///\n /// *The withdrawn validators*\n ///\n /// The withdrawn validators could have their balances deducted in any slot before slot\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\n /// look at the \"worst case scenario\" where the validator withdrawal is processed in the\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\n /// even if the validator exits at the latest possible time it is paramount that the ETH\n /// balance on the execution layer is recorded in the next block. Correctly accounting\n /// for the withdrawn ETH.\n ///\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\n /// validator balance verification as well as execution layer contract balance snap.\n ///\n /// This behaviour is correct.\n ///\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n /// Can only be called by the registrator.\n function snapBalances() external onlyRegistrator {\n _snapBalances();\n }\n\n function _snapBalances() internal {\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(\n SafeCast.toUint64(block.timestamp)\n );\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the balances in the mapping\n snappedBalances[blockRoot] = Balances({\n timestamp: SafeCast.toUint64(block.timestamp),\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n // Store the snapped timestamp\n lastSnapTimestamp = SafeCast.toUint64(block.timestamp);\n\n emit BalancesSnapped(block.timestamp, blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct VerifyBalancesParams {\n bytes32 blockRoot;\n uint64 firstPendingDepositSlot;\n // BeaconBlock.BeaconBlockBody.deposits[0].slot\n bytes firstPendingDepositSlotProof;\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n /// @param params a `VerifyBalancesParams` struct containing the following:\n /// blockRoot - the beacon block root emitted from `snapBalance` in `BalancesSnapped`\n /// firstPendingDepositSlot - The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero would be a good choice.\n /// firstPendingDepositSlotProof - The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// balancesContainerRoot - the merkle root of the balances container\n /// balancesContainerProof - The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// validatorBalanceLeaves - Array of leaf nodes containing the validator balance with three other balances.\n /// validatorBalanceProofs - Array of merkle proofs for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(VerifyBalancesParams calldata params) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalances[params.blockRoot];\n // Check the balances are the latest\n require(lastSnapTimestamp > 0, \"No snapped balances\");\n require(balancesMem.timestamp == lastSnapTimestamp, \"Stale snap\");\n\n uint256 depositsCount = depositsRoots.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit to the beacon block root\n bool isEmptyDepositQueue = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDepositSlot(\n params.blockRoot,\n params.firstPendingDepositSlot,\n params.firstPendingDepositSlotProof\n );\n\n // If there are no deposits in the beacon chain queue then our deposits must have been processed.\n // If the deposits have been processed, each deposit will need to be verified with `verifyDeposit`\n require(!isEmptyDepositQueue, \"Deposits have been processed\");\n\n // For each staking strategy's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n bytes32 depositDataRoot = depositsRoots[i];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit slot is before the\n // slot of the staking strategy's deposit.\n // If the deposit has been processed, it will need to be verified with `verifyDeposit`\n require(\n params.firstPendingDepositSlot <\n deposits[depositDataRoot].slot,\n \"Deposit has been processed\"\n );\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei +=\n uint256(deposits[depositDataRoot].amountGwei) *\n 1 gwei;\n }\n }\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n params.validatorBalanceProofs.length == verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n params.validatorBalanceLeaves.length == verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n params.blockRoot,\n params.balancesContainerRoot,\n params.balancesContainerProof\n );\n\n // for each validator in reserve order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n params.balancesContainerRoot,\n params.validatorBalanceLeaves[i],\n params.validatorBalanceProofs[i],\n verifiedValidators[i].index\n );\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n validatorState[\n verifiedValidators[i].pubKeyHash\n ] = VALIDATOR_STATE.EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += uint256(validatorBalanceGwei) * 1 gwei;\n }\n }\n\n uint256 wethBalance = IWETH9(WETH).balanceOf(address(this));\n\n // Store the verified balance in storage\n lastVerifiedEthBalance = SafeCast.toUint128(\n totalDepositsWei + totalValidatorBalance + balancesMem.ethBalance\n );\n // Reset the last snap timestamp so a new snapBalances has to be made\n lastSnapTimestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n wethBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /***************************************\n WETH and ETH Accounting\n ****************************************/\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance\n if (lastVerifiedEthBalance > _ethAmount) {\n lastVerifiedEthBalance -= SafeCast.toUint128(_ethAmount);\n } else {\n // This can happen if all ETH in the validators was withdrawn\n // and there was more consensus rewards since the last balance verification.\n // Or it can happen if ETH is donated to this strategy.\n lastVerifiedEthBalance = 0;\n }\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += SafeCast.toUint128(_wethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /***************************************\n View Functions\n ****************************************/\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return An array of `ValidatorData` containing the public key hash and validator index.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorData[] memory)\n {\n return verifiedValidators;\n }\n\n struct DepositView {\n bytes32 root;\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n }\n\n /// @notice Returns the deposits that are still to be verified.\n /// These may or may not have been processed by the beacon chain.\n /// @return pendingDeposits An array of `DepositView` containing the deposit root, public key hash,\n /// amount in Gwei and the slot of the deposit.\n function getPendingDeposits()\n external\n view\n returns (DepositView[] memory pendingDeposits)\n {\n pendingDeposits = new DepositView[](depositsRoots.length);\n for (uint256 i = 0; i < depositsRoots.length; ++i) {\n DepositData memory deposit = deposits[depositsRoots[i]];\n pendingDeposits[i] = DepositView({\n root: depositsRoots[i],\n pubKeyHash: deposit.pubKeyHash,\n amountGwei: deposit.amountGwei,\n slot: deposit.slot\n });\n }\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/d6153015beb2dbf35534a30be4430611.json b/contracts/deployments/hoodi/solcInputs/d6153015beb2dbf35534a30be4430611.json new file mode 100644 index 0000000000..035b8c3429 --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/d6153015beb2dbf35534a30be4430611.json @@ -0,0 +1,1152 @@ +{ + "language": "Solidity", + "sources": { + "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {Client} from \"../libraries/Client.sol\";\n\ninterface IRouterClient {\n error UnsupportedDestinationChain(uint64 destChainSelector);\n error InsufficientFeeTokenAmount();\n error InvalidMsgValue();\n\n /// @notice Checks if the given chain ID is supported for sending/receiving.\n /// @param chainSelector The chain to check.\n /// @return supported is true if it is supported, false if not.\n function isChainSupported(uint64 chainSelector) external view returns (bool supported);\n\n /// @notice Gets a list of all supported tokens which can be sent or received\n /// to/from a given chain id.\n /// @param chainSelector The chainSelector.\n /// @return tokens The addresses of all tokens that are supported.\n function getSupportedTokens(uint64 chainSelector) external view returns (address[] memory tokens);\n\n /// @param destinationChainSelector The destination chainSelector\n /// @param message The cross-chain CCIP message including data and/or tokens\n /// @return fee returns execution fee for the message\n /// delivery to destination chain, denominated in the feeToken specified in the message.\n /// @dev Reverts with appropriate reason upon invalid message.\n function getFee(\n uint64 destinationChainSelector,\n Client.EVM2AnyMessage memory message\n ) external view returns (uint256 fee);\n\n /// @notice Request a message to be sent to the destination chain\n /// @param destinationChainSelector The destination chain ID\n /// @param message The cross-chain CCIP message including data and/or tokens\n /// @return messageId The message ID\n /// @dev Note if msg.value is larger than the required fee (from getFee) we accept\n /// the overpayment with no refund.\n /// @dev Reverts with appropriate reason upon invalid message.\n function ccipSend(\n uint64 destinationChainSelector,\n Client.EVM2AnyMessage calldata message\n ) external payable returns (bytes32);\n}\n" + }, + "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// End consumer library.\nlibrary Client {\n /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.\n struct EVMTokenAmount {\n address token; // token address on the local chain.\n uint256 amount; // Amount of tokens.\n }\n\n struct Any2EVMMessage {\n bytes32 messageId; // MessageId corresponding to ccipSend on source.\n uint64 sourceChainSelector; // Source chain selector.\n bytes sender; // abi.decode(sender) if coming from an EVM chain.\n bytes data; // payload sent in original message.\n EVMTokenAmount[] destTokenAmounts; // Tokens and their amounts in their destination chain representation.\n }\n\n // If extraArgs is empty bytes, the default is 200k gas limit.\n struct EVM2AnyMessage {\n bytes receiver; // abi.encode(receiver address) for dest EVM chains\n bytes data; // Data payload\n EVMTokenAmount[] tokenAmounts; // Token transfers\n address feeToken; // Address of feeToken. address(0) means you will send msg.value.\n bytes extraArgs; // Populate this with _argsToBytes(EVMExtraArgsV1)\n }\n\n // bytes4(keccak256(\"CCIP EVMExtraArgsV1\"));\n bytes4 public constant EVM_EXTRA_ARGS_V1_TAG = 0x97a657c9;\n struct EVMExtraArgsV1 {\n uint256 gasLimit;\n }\n\n function _argsToBytes(EVMExtraArgsV1 memory extraArgs) internal pure returns (bytes memory bts) {\n return abi.encodeWithSelector(EVM_EXTRA_ARGS_V1_TAG, extraArgs);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport \"@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol\";\n\nlibrary ExecutorOptions {\n using CalldataBytesLib for bytes;\n\n uint8 internal constant WORKER_ID = 1;\n\n uint8 internal constant OPTION_TYPE_LZRECEIVE = 1;\n uint8 internal constant OPTION_TYPE_NATIVE_DROP = 2;\n uint8 internal constant OPTION_TYPE_LZCOMPOSE = 3;\n uint8 internal constant OPTION_TYPE_ORDERED_EXECUTION = 4;\n uint8 internal constant OPTION_TYPE_LZREAD = 5;\n\n error Executor_InvalidLzReceiveOption();\n error Executor_InvalidNativeDropOption();\n error Executor_InvalidLzComposeOption();\n error Executor_InvalidLzReadOption();\n\n /// @dev decode the next executor option from the options starting from the specified cursor\n /// @param _options [executor_id][executor_option][executor_id][executor_option]...\n /// executor_option = [option_size][option_type][option]\n /// option_size = len(option_type) + len(option)\n /// executor_id: uint8, option_size: uint16, option_type: uint8, option: bytes\n /// @param _cursor the cursor to start decoding from\n /// @return optionType the type of the option\n /// @return option the option of the executor\n /// @return cursor the cursor to start decoding the next executor option\n function nextExecutorOption(\n bytes calldata _options,\n uint256 _cursor\n ) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) {\n unchecked {\n // skip worker id\n cursor = _cursor + 1;\n\n // read option size\n uint16 size = _options.toU16(cursor);\n cursor += 2;\n\n // read option type\n optionType = _options.toU8(cursor);\n\n // startCursor and endCursor are used to slice the option from _options\n uint256 startCursor = cursor + 1; // skip option type\n uint256 endCursor = cursor + size;\n option = _options[startCursor:endCursor];\n cursor += size;\n }\n }\n\n function decodeLzReceiveOption(bytes calldata _option) internal pure returns (uint128 gas, uint128 value) {\n if (_option.length != 16 && _option.length != 32) revert Executor_InvalidLzReceiveOption();\n gas = _option.toU128(0);\n value = _option.length == 32 ? _option.toU128(16) : 0;\n }\n\n function decodeNativeDropOption(bytes calldata _option) internal pure returns (uint128 amount, bytes32 receiver) {\n if (_option.length != 48) revert Executor_InvalidNativeDropOption();\n amount = _option.toU128(0);\n receiver = _option.toB32(16);\n }\n\n function decodeLzComposeOption(\n bytes calldata _option\n ) internal pure returns (uint16 index, uint128 gas, uint128 value) {\n if (_option.length != 18 && _option.length != 34) revert Executor_InvalidLzComposeOption();\n index = _option.toU16(0);\n gas = _option.toU128(2);\n value = _option.length == 34 ? _option.toU128(18) : 0;\n }\n\n function decodeLzReadOption(\n bytes calldata _option\n ) internal pure returns (uint128 gas, uint32 calldataSize, uint128 value) {\n if (_option.length != 20 && _option.length != 36) revert Executor_InvalidLzReadOption();\n gas = _option.toU128(0);\n calldataSize = _option.toU32(16);\n value = _option.length == 36 ? _option.toU128(20) : 0;\n }\n\n function encodeLzReceiveOption(uint128 _gas, uint128 _value) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_gas) : abi.encodePacked(_gas, _value);\n }\n\n function encodeNativeDropOption(uint128 _amount, bytes32 _receiver) internal pure returns (bytes memory) {\n return abi.encodePacked(_amount, _receiver);\n }\n\n function encodeLzComposeOption(uint16 _index, uint128 _gas, uint128 _value) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_index, _gas) : abi.encodePacked(_index, _gas, _value);\n }\n\n function encodeLzReadOption(\n uint128 _gas,\n uint32 _calldataSize,\n uint128 _value\n ) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_gas, _calldataSize) : abi.encodePacked(_gas, _calldataSize, _value);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/DVNOptions.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport { BytesLib } from \"solidity-bytes-utils/contracts/BytesLib.sol\";\n\nimport { BitMap256 } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/BitMaps.sol\";\nimport { CalldataBytesLib } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol\";\n\nlibrary DVNOptions {\n using CalldataBytesLib for bytes;\n using BytesLib for bytes;\n\n uint8 internal constant WORKER_ID = 2;\n uint8 internal constant OPTION_TYPE_PRECRIME = 1;\n\n error DVN_InvalidDVNIdx();\n error DVN_InvalidDVNOptions(uint256 cursor);\n\n /// @dev group dvn options by its idx\n /// @param _options [dvn_id][dvn_option][dvn_id][dvn_option]...\n /// dvn_option = [option_size][dvn_idx][option_type][option]\n /// option_size = len(dvn_idx) + len(option_type) + len(option)\n /// dvn_id: uint8, dvn_idx: uint8, option_size: uint16, option_type: uint8, option: bytes\n /// @return dvnOptions the grouped options, still share the same format of _options\n /// @return dvnIndices the dvn indices\n function groupDVNOptionsByIdx(\n bytes memory _options\n ) internal pure returns (bytes[] memory dvnOptions, uint8[] memory dvnIndices) {\n if (_options.length == 0) return (dvnOptions, dvnIndices);\n\n uint8 numDVNs = getNumDVNs(_options);\n\n // if there is only 1 dvn, we can just return the whole options\n if (numDVNs == 1) {\n dvnOptions = new bytes[](1);\n dvnOptions[0] = _options;\n\n dvnIndices = new uint8[](1);\n dvnIndices[0] = _options.toUint8(3); // dvn idx\n return (dvnOptions, dvnIndices);\n }\n\n // otherwise, we need to group the options by dvn_idx\n dvnIndices = new uint8[](numDVNs);\n dvnOptions = new bytes[](numDVNs);\n unchecked {\n uint256 cursor = 0;\n uint256 start = 0;\n uint8 lastDVNIdx = 255; // 255 is an invalid dvn_idx\n\n while (cursor < _options.length) {\n ++cursor; // skip worker_id\n\n // optionLength asserted in getNumDVNs (skip check)\n uint16 optionLength = _options.toUint16(cursor);\n cursor += 2;\n\n // dvnIdx asserted in getNumDVNs (skip check)\n uint8 dvnIdx = _options.toUint8(cursor);\n\n // dvnIdx must equal to the lastDVNIdx for the first option\n // so it is always skipped in the first option\n // this operation slices out options whenever the scan finds a different lastDVNIdx\n if (lastDVNIdx == 255) {\n lastDVNIdx = dvnIdx;\n } else if (dvnIdx != lastDVNIdx) {\n uint256 len = cursor - start - 3; // 3 is for worker_id and option_length\n bytes memory opt = _options.slice(start, len);\n _insertDVNOptions(dvnOptions, dvnIndices, lastDVNIdx, opt);\n\n // reset the start and lastDVNIdx\n start += len;\n lastDVNIdx = dvnIdx;\n }\n\n cursor += optionLength;\n }\n\n // skip check the cursor here because the cursor is asserted in getNumDVNs\n // if we have reached the end of the options, we need to process the last dvn\n uint256 size = cursor - start;\n bytes memory op = _options.slice(start, size);\n _insertDVNOptions(dvnOptions, dvnIndices, lastDVNIdx, op);\n\n // revert dvnIndices to start from 0\n for (uint8 i = 0; i < numDVNs; ++i) {\n --dvnIndices[i];\n }\n }\n }\n\n function _insertDVNOptions(\n bytes[] memory _dvnOptions,\n uint8[] memory _dvnIndices,\n uint8 _dvnIdx,\n bytes memory _newOptions\n ) internal pure {\n // dvnIdx starts from 0 but default value of dvnIndices is 0,\n // so we tell if the slot is empty by adding 1 to dvnIdx\n if (_dvnIdx == 255) revert DVN_InvalidDVNIdx();\n uint8 dvnIdxAdj = _dvnIdx + 1;\n\n for (uint256 j = 0; j < _dvnIndices.length; ++j) {\n uint8 index = _dvnIndices[j];\n if (dvnIdxAdj == index) {\n _dvnOptions[j] = abi.encodePacked(_dvnOptions[j], _newOptions);\n break;\n } else if (index == 0) {\n // empty slot, that means it is the first time we see this dvn\n _dvnIndices[j] = dvnIdxAdj;\n _dvnOptions[j] = _newOptions;\n break;\n }\n }\n }\n\n /// @dev get the number of unique dvns\n /// @param _options the format is the same as groupDVNOptionsByIdx\n function getNumDVNs(bytes memory _options) internal pure returns (uint8 numDVNs) {\n uint256 cursor = 0;\n BitMap256 bitmap;\n\n // find number of unique dvn_idx\n unchecked {\n while (cursor < _options.length) {\n ++cursor; // skip worker_id\n\n uint16 optionLength = _options.toUint16(cursor);\n cursor += 2;\n if (optionLength < 2) revert DVN_InvalidDVNOptions(cursor); // at least 1 byte for dvn_idx and 1 byte for option_type\n\n uint8 dvnIdx = _options.toUint8(cursor);\n\n // if dvnIdx is not set, increment numDVNs\n // max num of dvns is 255, 255 is an invalid dvn_idx\n // The order of the dvnIdx is not required to be sequential, as enforcing the order may weaken\n // the composability of the options. e.g. if we refrain from enforcing the order, an OApp that has\n // already enforced certain options can append additional options to the end of the enforced\n // ones without restrictions.\n if (dvnIdx == 255) revert DVN_InvalidDVNIdx();\n if (!bitmap.get(dvnIdx)) {\n ++numDVNs;\n bitmap = bitmap.set(dvnIdx);\n }\n\n cursor += optionLength;\n }\n }\n if (cursor != _options.length) revert DVN_InvalidDVNOptions(cursor);\n }\n\n /// @dev decode the next dvn option from _options starting from the specified cursor\n /// @param _options the format is the same as groupDVNOptionsByIdx\n /// @param _cursor the cursor to start decoding\n /// @return optionType the type of the option\n /// @return option the option\n /// @return cursor the cursor to start decoding the next option\n function nextDVNOption(\n bytes calldata _options,\n uint256 _cursor\n ) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) {\n unchecked {\n // skip worker id\n cursor = _cursor + 1;\n\n // read option size\n uint16 size = _options.toU16(cursor);\n cursor += 2;\n\n // read option type\n optionType = _options.toU8(cursor + 1); // skip dvn_idx\n\n // startCursor and endCursor are used to slice the option from _options\n uint256 startCursor = cursor + 2; // skip option type and dvn_idx\n uint256 endCursor = cursor + size;\n option = _options[startCursor:endCursor];\n cursor += size;\n }\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { IMessageLibManager } from \"./IMessageLibManager.sol\";\nimport { IMessagingComposer } from \"./IMessagingComposer.sol\";\nimport { IMessagingChannel } from \"./IMessagingChannel.sol\";\nimport { IMessagingContext } from \"./IMessagingContext.sol\";\n\nstruct MessagingParams {\n uint32 dstEid;\n bytes32 receiver;\n bytes message;\n bytes options;\n bool payInLzToken;\n}\n\nstruct MessagingReceipt {\n bytes32 guid;\n uint64 nonce;\n MessagingFee fee;\n}\n\nstruct MessagingFee {\n uint256 nativeFee;\n uint256 lzTokenFee;\n}\n\nstruct Origin {\n uint32 srcEid;\n bytes32 sender;\n uint64 nonce;\n}\n\ninterface ILayerZeroEndpointV2 is IMessageLibManager, IMessagingComposer, IMessagingChannel, IMessagingContext {\n event PacketSent(bytes encodedPayload, bytes options, address sendLibrary);\n\n event PacketVerified(Origin origin, address receiver, bytes32 payloadHash);\n\n event PacketDelivered(Origin origin, address receiver);\n\n event LzReceiveAlert(\n address indexed receiver,\n address indexed executor,\n Origin origin,\n bytes32 guid,\n uint256 gas,\n uint256 value,\n bytes message,\n bytes extraData,\n bytes reason\n );\n\n event LzTokenSet(address token);\n\n event DelegateSet(address sender, address delegate);\n\n function quote(MessagingParams calldata _params, address _sender) external view returns (MessagingFee memory);\n\n function send(\n MessagingParams calldata _params,\n address _refundAddress\n ) external payable returns (MessagingReceipt memory);\n\n function verify(Origin calldata _origin, address _receiver, bytes32 _payloadHash) external;\n\n function verifiable(Origin calldata _origin, address _receiver) external view returns (bool);\n\n function initializable(Origin calldata _origin, address _receiver) external view returns (bool);\n\n function lzReceive(\n Origin calldata _origin,\n address _receiver,\n bytes32 _guid,\n bytes calldata _message,\n bytes calldata _extraData\n ) external payable;\n\n // oapp can burn messages partially by calling this function with its own business logic if messages are verified in order\n function clear(address _oapp, Origin calldata _origin, bytes32 _guid, bytes calldata _message) external;\n\n function setLzToken(address _lzToken) external;\n\n function lzToken() external view returns (address);\n\n function nativeToken() external view returns (address);\n\n function setDelegate(address _delegate) external;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { Origin } from \"./ILayerZeroEndpointV2.sol\";\n\ninterface ILayerZeroReceiver {\n function allowInitializePath(Origin calldata _origin) external view returns (bool);\n\n function nextNonce(uint32 _eid, bytes32 _sender) external view returns (uint64);\n\n function lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) external payable;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessageLib.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { IERC165 } from \"@openzeppelin/contracts/utils/introspection/IERC165.sol\";\n\nimport { SetConfigParam } from \"./IMessageLibManager.sol\";\n\nenum MessageLibType {\n Send,\n Receive,\n SendAndReceive\n}\n\ninterface IMessageLib is IERC165 {\n function setConfig(address _oapp, SetConfigParam[] calldata _config) external;\n\n function getConfig(uint32 _eid, address _oapp, uint32 _configType) external view returns (bytes memory config);\n\n function isSupportedEid(uint32 _eid) external view returns (bool);\n\n // message libs of same major version are compatible\n function version() external view returns (uint64 major, uint8 minor, uint8 endpointVersion);\n\n function messageLibType() external view returns (MessageLibType);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessageLibManager.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nstruct SetConfigParam {\n uint32 eid;\n uint32 configType;\n bytes config;\n}\n\ninterface IMessageLibManager {\n struct Timeout {\n address lib;\n uint256 expiry;\n }\n\n event LibraryRegistered(address newLib);\n event DefaultSendLibrarySet(uint32 eid, address newLib);\n event DefaultReceiveLibrarySet(uint32 eid, address newLib);\n event DefaultReceiveLibraryTimeoutSet(uint32 eid, address oldLib, uint256 expiry);\n event SendLibrarySet(address sender, uint32 eid, address newLib);\n event ReceiveLibrarySet(address receiver, uint32 eid, address newLib);\n event ReceiveLibraryTimeoutSet(address receiver, uint32 eid, address oldLib, uint256 timeout);\n\n function registerLibrary(address _lib) external;\n\n function isRegisteredLibrary(address _lib) external view returns (bool);\n\n function getRegisteredLibraries() external view returns (address[] memory);\n\n function setDefaultSendLibrary(uint32 _eid, address _newLib) external;\n\n function defaultSendLibrary(uint32 _eid) external view returns (address);\n\n function setDefaultReceiveLibrary(uint32 _eid, address _newLib, uint256 _gracePeriod) external;\n\n function defaultReceiveLibrary(uint32 _eid) external view returns (address);\n\n function setDefaultReceiveLibraryTimeout(uint32 _eid, address _lib, uint256 _expiry) external;\n\n function defaultReceiveLibraryTimeout(uint32 _eid) external view returns (address lib, uint256 expiry);\n\n function isSupportedEid(uint32 _eid) external view returns (bool);\n\n function isValidReceiveLibrary(address _receiver, uint32 _eid, address _lib) external view returns (bool);\n\n /// ------------------- OApp interfaces -------------------\n function setSendLibrary(address _oapp, uint32 _eid, address _newLib) external;\n\n function getSendLibrary(address _sender, uint32 _eid) external view returns (address lib);\n\n function isDefaultSendLibrary(address _sender, uint32 _eid) external view returns (bool);\n\n function setReceiveLibrary(address _oapp, uint32 _eid, address _newLib, uint256 _gracePeriod) external;\n\n function getReceiveLibrary(address _receiver, uint32 _eid) external view returns (address lib, bool isDefault);\n\n function setReceiveLibraryTimeout(address _oapp, uint32 _eid, address _lib, uint256 _expiry) external;\n\n function receiveLibraryTimeout(address _receiver, uint32 _eid) external view returns (address lib, uint256 expiry);\n\n function setConfig(address _oapp, address _lib, SetConfigParam[] calldata _params) external;\n\n function getConfig(\n address _oapp,\n address _lib,\n uint32 _eid,\n uint32 _configType\n ) external view returns (bytes memory config);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingChannel.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingChannel {\n event InboundNonceSkipped(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce);\n event PacketNilified(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);\n event PacketBurnt(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);\n\n function eid() external view returns (uint32);\n\n // this is an emergency function if a message cannot be verified for some reasons\n // required to provide _nextNonce to avoid race condition\n function skip(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce) external;\n\n function nilify(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;\n\n function burn(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;\n\n function nextGuid(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (bytes32);\n\n function inboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);\n\n function outboundNonce(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (uint64);\n\n function inboundPayloadHash(\n address _receiver,\n uint32 _srcEid,\n bytes32 _sender,\n uint64 _nonce\n ) external view returns (bytes32);\n\n function lazyInboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingComposer.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingComposer {\n event ComposeSent(address from, address to, bytes32 guid, uint16 index, bytes message);\n event ComposeDelivered(address from, address to, bytes32 guid, uint16 index);\n event LzComposeAlert(\n address indexed from,\n address indexed to,\n address indexed executor,\n bytes32 guid,\n uint16 index,\n uint256 gas,\n uint256 value,\n bytes message,\n bytes extraData,\n bytes reason\n );\n\n function composeQueue(\n address _from,\n address _to,\n bytes32 _guid,\n uint16 _index\n ) external view returns (bytes32 messageHash);\n\n function sendCompose(address _to, bytes32 _guid, uint16 _index, bytes calldata _message) external;\n\n function lzCompose(\n address _from,\n address _to,\n bytes32 _guid,\n uint16 _index,\n bytes calldata _message,\n bytes calldata _extraData\n ) external payable;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingContext.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingContext {\n function isSendingMessage() external view returns (bool);\n\n function getSendContext() external view returns (uint32 dstEid, address sender);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ISendLib.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { MessagingFee } from \"./ILayerZeroEndpointV2.sol\";\nimport { IMessageLib } from \"./IMessageLib.sol\";\n\nstruct Packet {\n uint64 nonce;\n uint32 srcEid;\n address sender;\n uint32 dstEid;\n bytes32 receiver;\n bytes32 guid;\n bytes message;\n}\n\ninterface ISendLib is IMessageLib {\n function send(\n Packet calldata _packet,\n bytes calldata _options,\n bool _payInLzToken\n ) external returns (MessagingFee memory, bytes memory encodedPacket);\n\n function quote(\n Packet calldata _packet,\n bytes calldata _options,\n bool _payInLzToken\n ) external view returns (MessagingFee memory);\n\n function setTreasury(address _treasury) external;\n\n function withdrawFee(address _to, uint256 _amount) external;\n\n function withdrawLzTokenFee(address _lzToken, address _to, uint256 _amount) external;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/AddressCast.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nlibrary AddressCast {\n error AddressCast_InvalidSizeForAddress();\n error AddressCast_InvalidAddress();\n\n function toBytes32(bytes calldata _addressBytes) internal pure returns (bytes32 result) {\n if (_addressBytes.length > 32) revert AddressCast_InvalidAddress();\n result = bytes32(_addressBytes);\n unchecked {\n uint256 offset = 32 - _addressBytes.length;\n result = result >> (offset * 8);\n }\n }\n\n function toBytes32(address _address) internal pure returns (bytes32 result) {\n result = bytes32(uint256(uint160(_address)));\n }\n\n function toBytes(bytes32 _addressBytes32, uint256 _size) internal pure returns (bytes memory result) {\n if (_size == 0 || _size > 32) revert AddressCast_InvalidSizeForAddress();\n result = new bytes(_size);\n unchecked {\n uint256 offset = 256 - _size * 8;\n assembly {\n mstore(add(result, 32), shl(offset, _addressBytes32))\n }\n }\n }\n\n function toAddress(bytes32 _addressBytes32) internal pure returns (address result) {\n result = address(uint160(uint256(_addressBytes32)));\n }\n\n function toAddress(bytes calldata _addressBytes) internal pure returns (address result) {\n if (_addressBytes.length != 20) revert AddressCast_InvalidAddress();\n result = address(bytes20(_addressBytes));\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nlibrary CalldataBytesLib {\n function toU8(bytes calldata _bytes, uint256 _start) internal pure returns (uint8) {\n return uint8(_bytes[_start]);\n }\n\n function toU16(bytes calldata _bytes, uint256 _start) internal pure returns (uint16) {\n unchecked {\n uint256 end = _start + 2;\n return uint16(bytes2(_bytes[_start:end]));\n }\n }\n\n function toU32(bytes calldata _bytes, uint256 _start) internal pure returns (uint32) {\n unchecked {\n uint256 end = _start + 4;\n return uint32(bytes4(_bytes[_start:end]));\n }\n }\n\n function toU64(bytes calldata _bytes, uint256 _start) internal pure returns (uint64) {\n unchecked {\n uint256 end = _start + 8;\n return uint64(bytes8(_bytes[_start:end]));\n }\n }\n\n function toU128(bytes calldata _bytes, uint256 _start) internal pure returns (uint128) {\n unchecked {\n uint256 end = _start + 16;\n return uint128(bytes16(_bytes[_start:end]));\n }\n }\n\n function toU256(bytes calldata _bytes, uint256 _start) internal pure returns (uint256) {\n unchecked {\n uint256 end = _start + 32;\n return uint256(bytes32(_bytes[_start:end]));\n }\n }\n\n function toAddr(bytes calldata _bytes, uint256 _start) internal pure returns (address) {\n unchecked {\n uint256 end = _start + 20;\n return address(bytes20(_bytes[_start:end]));\n }\n }\n\n function toB32(bytes calldata _bytes, uint256 _start) internal pure returns (bytes32) {\n unchecked {\n uint256 end = _start + 32;\n return bytes32(_bytes[_start:end]);\n }\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/BitMaps.sol": { + "content": "// SPDX-License-Identifier: MIT\n\n// modified from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/structs/BitMaps.sol\npragma solidity ^0.8.20;\n\ntype BitMap256 is uint256;\n\nusing BitMaps for BitMap256 global;\n\nlibrary BitMaps {\n /**\n * @dev Returns whether the bit at `index` is set.\n */\n function get(BitMap256 bitmap, uint8 index) internal pure returns (bool) {\n uint256 mask = 1 << index;\n return BitMap256.unwrap(bitmap) & mask != 0;\n }\n\n /**\n * @dev Sets the bit at `index`.\n */\n function set(BitMap256 bitmap, uint8 index) internal pure returns (BitMap256) {\n uint256 mask = 1 << index;\n return BitMap256.wrap(BitMap256.unwrap(bitmap) | mask);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport { Packet } from \"../../interfaces/ISendLib.sol\";\nimport { AddressCast } from \"../../libs/AddressCast.sol\";\n\nlibrary PacketV1Codec {\n using AddressCast for address;\n using AddressCast for bytes32;\n\n uint8 internal constant PACKET_VERSION = 1;\n\n // header (version + nonce + path)\n // version\n uint256 private constant PACKET_VERSION_OFFSET = 0;\n // nonce\n uint256 private constant NONCE_OFFSET = 1;\n // path\n uint256 private constant SRC_EID_OFFSET = 9;\n uint256 private constant SENDER_OFFSET = 13;\n uint256 private constant DST_EID_OFFSET = 45;\n uint256 private constant RECEIVER_OFFSET = 49;\n // payload (guid + message)\n uint256 private constant GUID_OFFSET = 81; // keccak256(nonce + path)\n uint256 private constant MESSAGE_OFFSET = 113;\n\n function encode(Packet memory _packet) internal pure returns (bytes memory encodedPacket) {\n encodedPacket = abi.encodePacked(\n PACKET_VERSION,\n _packet.nonce,\n _packet.srcEid,\n _packet.sender.toBytes32(),\n _packet.dstEid,\n _packet.receiver,\n _packet.guid,\n _packet.message\n );\n }\n\n function encodePacketHeader(Packet memory _packet) internal pure returns (bytes memory) {\n return\n abi.encodePacked(\n PACKET_VERSION,\n _packet.nonce,\n _packet.srcEid,\n _packet.sender.toBytes32(),\n _packet.dstEid,\n _packet.receiver\n );\n }\n\n function encodePayload(Packet memory _packet) internal pure returns (bytes memory) {\n return abi.encodePacked(_packet.guid, _packet.message);\n }\n\n function header(bytes calldata _packet) internal pure returns (bytes calldata) {\n return _packet[0:GUID_OFFSET];\n }\n\n function version(bytes calldata _packet) internal pure returns (uint8) {\n return uint8(bytes1(_packet[PACKET_VERSION_OFFSET:NONCE_OFFSET]));\n }\n\n function nonce(bytes calldata _packet) internal pure returns (uint64) {\n return uint64(bytes8(_packet[NONCE_OFFSET:SRC_EID_OFFSET]));\n }\n\n function srcEid(bytes calldata _packet) internal pure returns (uint32) {\n return uint32(bytes4(_packet[SRC_EID_OFFSET:SENDER_OFFSET]));\n }\n\n function sender(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[SENDER_OFFSET:DST_EID_OFFSET]);\n }\n\n function senderAddressB20(bytes calldata _packet) internal pure returns (address) {\n return sender(_packet).toAddress();\n }\n\n function dstEid(bytes calldata _packet) internal pure returns (uint32) {\n return uint32(bytes4(_packet[DST_EID_OFFSET:RECEIVER_OFFSET]));\n }\n\n function receiver(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[RECEIVER_OFFSET:GUID_OFFSET]);\n }\n\n function receiverB20(bytes calldata _packet) internal pure returns (address) {\n return receiver(_packet).toAddress();\n }\n\n function guid(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[GUID_OFFSET:MESSAGE_OFFSET]);\n }\n\n function message(bytes calldata _packet) internal pure returns (bytes calldata) {\n return bytes(_packet[MESSAGE_OFFSET:]);\n }\n\n function payload(bytes calldata _packet) internal pure returns (bytes calldata) {\n return bytes(_packet[GUID_OFFSET:]);\n }\n\n function payloadHash(bytes calldata _packet) internal pure returns (bytes32) {\n return keccak256(payload(_packet));\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { ILayerZeroEndpointV2 } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\n\n/**\n * @title IOAppCore\n */\ninterface IOAppCore {\n // Custom error messages\n error OnlyPeer(uint32 eid, bytes32 sender);\n error NoPeer(uint32 eid);\n error InvalidEndpointCall();\n error InvalidDelegate();\n\n // Event emitted when a peer (OApp) is set for a corresponding endpoint\n event PeerSet(uint32 eid, bytes32 peer);\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n */\n function oAppVersion() external view returns (uint64 senderVersion, uint64 receiverVersion);\n\n /**\n * @notice Retrieves the LayerZero endpoint associated with the OApp.\n * @return iEndpoint The LayerZero endpoint as an interface.\n */\n function endpoint() external view returns (ILayerZeroEndpointV2 iEndpoint);\n\n /**\n * @notice Retrieves the peer (OApp) associated with a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @return peer The peer address (OApp instance) associated with the corresponding endpoint.\n */\n function peers(uint32 _eid) external view returns (bytes32 peer);\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n */\n function setPeer(uint32 _eid, bytes32 _peer) external;\n\n /**\n * @notice Sets the delegate address for the OApp Core.\n * @param _delegate The address of the delegate to be set.\n */\n function setDelegate(address _delegate) external;\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppMsgInspector.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n/**\n * @title IOAppMsgInspector\n * @dev Interface for the OApp Message Inspector, allowing examination of message and options contents.\n */\ninterface IOAppMsgInspector {\n // Custom error message for inspection failure\n error InspectionFailed(bytes message, bytes options);\n\n /**\n * @notice Allows the inspector to examine LayerZero message contents and optionally throw a revert if invalid.\n * @param _message The message payload to be inspected.\n * @param _options Additional options or parameters for inspection.\n * @return valid A boolean indicating whether the inspection passed (true) or failed (false).\n *\n * @dev Optionally done as a revert, OR use the boolean provided to handle the failure.\n */\n function inspect(bytes calldata _message, bytes calldata _options) external view returns (bool valid);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppOptionsType3.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Struct representing enforced option parameters.\n */\nstruct EnforcedOptionParam {\n uint32 eid; // Endpoint ID\n uint16 msgType; // Message Type\n bytes options; // Additional options\n}\n\n/**\n * @title IOAppOptionsType3\n * @dev Interface for the OApp with Type 3 Options, allowing the setting and combining of enforced options.\n */\ninterface IOAppOptionsType3 {\n // Custom error message for invalid options\n error InvalidOptions(bytes options);\n\n // Event emitted when enforced options are set\n event EnforcedOptionSet(EnforcedOptionParam[] _enforcedOptions);\n\n /**\n * @notice Sets enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n */\n function setEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) external;\n\n /**\n * @notice Combines options for a given endpoint and message type.\n * @param _eid The endpoint ID.\n * @param _msgType The OApp message type.\n * @param _extraOptions Additional options passed by the caller.\n * @return options The combination of caller specified options AND enforced options.\n */\n function combineOptions(\n uint32 _eid,\n uint16 _msgType,\n bytes calldata _extraOptions\n ) external view returns (bytes memory options);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\nimport { ILayerZeroReceiver, Origin } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroReceiver.sol\";\n\ninterface IOAppReceiver is ILayerZeroReceiver {\n /**\n * @notice Indicates whether an address is an approved composeMsg sender to the Endpoint.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _message The lzReceive payload.\n * @param _sender The sender address.\n * @return isSender Is a valid sender.\n *\n * @dev Applications can optionally choose to implement a separate composeMsg sender that is NOT the bridging layer.\n * @dev The default sender IS the OAppReceiver implementer.\n */\n function isComposeMsgSender(\n Origin calldata _origin,\n bytes calldata _message,\n address _sender\n ) external view returns (bool isSender);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IOAppOptionsType3, EnforcedOptionParam } from \"../interfaces/IOAppOptionsType3.sol\";\n\n/**\n * @title OAppOptionsType3\n * @dev Abstract contract implementing the IOAppOptionsType3 interface with type 3 options.\n */\nabstract contract OAppOptionsType3 is IOAppOptionsType3, Ownable {\n uint16 internal constant OPTION_TYPE_3 = 3;\n\n // @dev The \"msgType\" should be defined in the child contract.\n mapping(uint32 eid => mapping(uint16 msgType => bytes enforcedOption)) public enforcedOptions;\n\n /**\n * @dev Sets the enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Provides a way for the OApp to enforce things like paying for PreCrime, AND/OR minimum dst lzReceive gas amounts etc.\n * @dev These enforced options can vary as the potential options/execution on the remote may differ as per the msgType.\n * eg. Amount of lzReceive() gas necessary to deliver a lzCompose() message adds overhead you dont want to pay\n * if you are only making a standard LayerZero message ie. lzReceive() WITHOUT sendCompose().\n */\n function setEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) public virtual onlyOwner {\n _setEnforcedOptions(_enforcedOptions);\n }\n\n /**\n * @dev Sets the enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n *\n * @dev Provides a way for the OApp to enforce things like paying for PreCrime, AND/OR minimum dst lzReceive gas amounts etc.\n * @dev These enforced options can vary as the potential options/execution on the remote may differ as per the msgType.\n * eg. Amount of lzReceive() gas necessary to deliver a lzCompose() message adds overhead you dont want to pay\n * if you are only making a standard LayerZero message ie. lzReceive() WITHOUT sendCompose().\n */\n function _setEnforcedOptions(EnforcedOptionParam[] memory _enforcedOptions) internal virtual {\n for (uint256 i = 0; i < _enforcedOptions.length; i++) {\n // @dev Enforced options are only available for optionType 3, as type 1 and 2 dont support combining.\n _assertOptionsType3(_enforcedOptions[i].options);\n enforcedOptions[_enforcedOptions[i].eid][_enforcedOptions[i].msgType] = _enforcedOptions[i].options;\n }\n\n emit EnforcedOptionSet(_enforcedOptions);\n }\n\n /**\n * @notice Combines options for a given endpoint and message type.\n * @param _eid The endpoint ID.\n * @param _msgType The OAPP message type.\n * @param _extraOptions Additional options passed by the caller.\n * @return options The combination of caller specified options AND enforced options.\n *\n * @dev If there is an enforced lzReceive option:\n * - {gasLimit: 200k, msg.value: 1 ether} AND a caller supplies a lzReceive option: {gasLimit: 100k, msg.value: 0.5 ether}\n * - The resulting options will be {gasLimit: 300k, msg.value: 1.5 ether} when the message is executed on the remote lzReceive() function.\n * @dev This presence of duplicated options is handled off-chain in the verifier/executor.\n */\n function combineOptions(\n uint32 _eid,\n uint16 _msgType,\n bytes calldata _extraOptions\n ) public view virtual returns (bytes memory) {\n bytes memory enforced = enforcedOptions[_eid][_msgType];\n\n // No enforced options, pass whatever the caller supplied, even if it's empty or legacy type 1/2 options.\n if (enforced.length == 0) return _extraOptions;\n\n // No caller options, return enforced\n if (_extraOptions.length == 0) return enforced;\n\n // @dev If caller provided _extraOptions, must be type 3 as its the ONLY type that can be combined.\n if (_extraOptions.length >= 2) {\n _assertOptionsType3(_extraOptions);\n // @dev Remove the first 2 bytes containing the type from the _extraOptions and combine with enforced.\n return bytes.concat(enforced, _extraOptions[2:]);\n }\n\n // No valid set of options was found.\n revert InvalidOptions(_extraOptions);\n }\n\n /**\n * @dev Internal function to assert that options are of type 3.\n * @param _options The options to be checked.\n */\n function _assertOptionsType3(bytes memory _options) internal pure virtual {\n uint16 optionsType;\n assembly {\n optionsType := mload(add(_options, 2))\n }\n if (optionsType != OPTION_TYPE_3) revert InvalidOptions(_options);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { BytesLib } from \"solidity-bytes-utils/contracts/BytesLib.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { ExecutorOptions } from \"@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol\";\nimport { DVNOptions } from \"@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/DVNOptions.sol\";\n\n/**\n * @title OptionsBuilder\n * @dev Library for building and encoding various message options.\n */\nlibrary OptionsBuilder {\n using SafeCast for uint256;\n using BytesLib for bytes;\n\n // Constants for options types\n uint16 internal constant TYPE_1 = 1; // legacy options type 1\n uint16 internal constant TYPE_2 = 2; // legacy options type 2\n uint16 internal constant TYPE_3 = 3;\n\n // Custom error message\n error InvalidSize(uint256 max, uint256 actual);\n error InvalidOptionType(uint16 optionType);\n\n // Modifier to ensure only options of type 3 are used\n modifier onlyType3(bytes memory _options) {\n if (_options.toUint16(0) != TYPE_3) revert InvalidOptionType(_options.toUint16(0));\n _;\n }\n\n /**\n * @dev Creates a new options container with type 3.\n * @return options The newly created options container.\n */\n function newOptions() internal pure returns (bytes memory) {\n return abi.encodePacked(TYPE_3);\n }\n\n /**\n * @dev Adds an executor LZ receive option to the existing options.\n * @param _options The existing options container.\n * @param _gas The gasLimit used on the lzReceive() function in the OApp.\n * @param _value The msg.value passed to the lzReceive() function in the OApp.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed by the executor\n * eg. if (_gas: 200k, and _value: 1 ether) AND (_gas: 100k, _value: 0.5 ether) are sent in an option to the LayerZeroEndpoint,\n * that becomes (300k, 1.5 ether) when the message is executed on the remote lzReceive() function.\n */\n function addExecutorLzReceiveOption(\n bytes memory _options,\n uint128 _gas,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzReceiveOption(_gas, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZRECEIVE, option);\n }\n\n /**\n * @dev Adds an executor native drop option to the existing options.\n * @param _options The existing options container.\n * @param _amount The amount for the native value that is airdropped to the 'receiver'.\n * @param _receiver The receiver address for the native drop option.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed by the executor on the remote chain.\n */\n function addExecutorNativeDropOption(\n bytes memory _options,\n uint128 _amount,\n bytes32 _receiver\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeNativeDropOption(_amount, _receiver);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_NATIVE_DROP, option);\n }\n\n // /**\n // * @dev Adds an executor native drop option to the existing options.\n // * @param _options The existing options container.\n // * @param _amount The amount for the native value that is airdropped to the 'receiver'.\n // * @param _receiver The receiver address for the native drop option.\n // * @return options The updated options container.\n // *\n // * @dev When multiples of this option are added, they are summed by the executor on the remote chain.\n // */\n function addExecutorLzReadOption(\n bytes memory _options,\n uint128 _gas,\n uint32 _size,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzReadOption(_gas, _size, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZREAD, option);\n }\n\n /**\n * @dev Adds an executor LZ compose option to the existing options.\n * @param _options The existing options container.\n * @param _index The index for the lzCompose() function call.\n * @param _gas The gasLimit for the lzCompose() function call.\n * @param _value The msg.value for the lzCompose() function call.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed PER index by the executor on the remote chain.\n * @dev If the OApp sends N lzCompose calls on the remote, you must provide N incremented indexes starting with 0.\n * ie. When your remote OApp composes (N = 3) messages, you must set this option for index 0,1,2\n */\n function addExecutorLzComposeOption(\n bytes memory _options,\n uint16 _index,\n uint128 _gas,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzComposeOption(_index, _gas, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZCOMPOSE, option);\n }\n\n /**\n * @dev Adds an executor ordered execution option to the existing options.\n * @param _options The existing options container.\n * @return options The updated options container.\n */\n function addExecutorOrderedExecutionOption(\n bytes memory _options\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_ORDERED_EXECUTION, bytes(\"\"));\n }\n\n /**\n * @dev Adds a DVN pre-crime option to the existing options.\n * @param _options The existing options container.\n * @param _dvnIdx The DVN index for the pre-crime option.\n * @return options The updated options container.\n */\n function addDVNPreCrimeOption(\n bytes memory _options,\n uint8 _dvnIdx\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return addDVNOption(_options, _dvnIdx, DVNOptions.OPTION_TYPE_PRECRIME, bytes(\"\"));\n }\n\n /**\n * @dev Adds an executor option to the existing options.\n * @param _options The existing options container.\n * @param _optionType The type of the executor option.\n * @param _option The encoded data for the executor option.\n * @return options The updated options container.\n */\n function addExecutorOption(\n bytes memory _options,\n uint8 _optionType,\n bytes memory _option\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return\n abi.encodePacked(\n _options,\n ExecutorOptions.WORKER_ID,\n _option.length.toUint16() + 1, // +1 for optionType\n _optionType,\n _option\n );\n }\n\n /**\n * @dev Adds a DVN option to the existing options.\n * @param _options The existing options container.\n * @param _dvnIdx The DVN index for the DVN option.\n * @param _optionType The type of the DVN option.\n * @param _option The encoded data for the DVN option.\n * @return options The updated options container.\n */\n function addDVNOption(\n bytes memory _options,\n uint8 _dvnIdx,\n uint8 _optionType,\n bytes memory _option\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return\n abi.encodePacked(\n _options,\n DVNOptions.WORKER_ID,\n _option.length.toUint16() + 2, // +2 for optionType and dvnIdx\n _dvnIdx,\n _optionType,\n _option\n );\n }\n\n /**\n * @dev Encodes legacy options of type 1.\n * @param _executionGas The gasLimit value passed to lzReceive().\n * @return legacyOptions The encoded legacy options.\n */\n function encodeLegacyOptionsType1(uint256 _executionGas) internal pure returns (bytes memory) {\n if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);\n return abi.encodePacked(TYPE_1, _executionGas);\n }\n\n /**\n * @dev Encodes legacy options of type 2.\n * @param _executionGas The gasLimit value passed to lzReceive().\n * @param _nativeForDst The amount of native air dropped to the receiver.\n * @param _receiver The _nativeForDst receiver address.\n * @return legacyOptions The encoded legacy options of type 2.\n */\n function encodeLegacyOptionsType2(\n uint256 _executionGas,\n uint256 _nativeForDst,\n bytes memory _receiver // @dev Use bytes instead of bytes32 in legacy type 2 for _receiver.\n ) internal pure returns (bytes memory) {\n if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);\n if (_nativeForDst > type(uint128).max) revert InvalidSize(type(uint128).max, _nativeForDst);\n if (_receiver.length > 32) revert InvalidSize(32, _receiver.length);\n return abi.encodePacked(TYPE_2, _executionGas, _nativeForDst, _receiver);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n// @dev Import the 'MessagingFee' and 'MessagingReceipt' so it's exposed to OApp implementers\n// solhint-disable-next-line no-unused-import\nimport { OAppSender, MessagingFee, MessagingReceipt } from \"./OAppSender.sol\";\n// @dev Import the 'Origin' so it's exposed to OApp implementers\n// solhint-disable-next-line no-unused-import\nimport { OAppReceiver, Origin } from \"./OAppReceiver.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OApp\n * @dev Abstract contract serving as the base for OApp implementation, combining OAppSender and OAppReceiver functionality.\n */\nabstract contract OApp is OAppSender, OAppReceiver {\n /**\n * @dev Constructor to initialize the OApp with the provided endpoint and owner.\n * @param _endpoint The address of the LOCAL LayerZero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(address _endpoint, address _delegate) OAppCore(_endpoint, _delegate) {}\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol implementation.\n * @return receiverVersion The version of the OAppReceiver.sol implementation.\n */\n function oAppVersion()\n public\n pure\n virtual\n override(OAppSender, OAppReceiver)\n returns (uint64 senderVersion, uint64 receiverVersion)\n {\n return (SENDER_VERSION, RECEIVER_VERSION);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IOAppCore, ILayerZeroEndpointV2 } from \"./interfaces/IOAppCore.sol\";\n\n/**\n * @title OAppCore\n * @dev Abstract contract implementing the IOAppCore interface with basic OApp configurations.\n */\nabstract contract OAppCore is IOAppCore, Ownable {\n // The LayerZero endpoint associated with the given OApp\n ILayerZeroEndpointV2 public immutable endpoint;\n\n // Mapping to store peers associated with corresponding endpoints\n mapping(uint32 eid => bytes32 peer) public peers;\n\n /**\n * @dev Constructor to initialize the OAppCore with the provided endpoint and delegate.\n * @param _endpoint The address of the LOCAL Layer Zero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n *\n * @dev The delegate typically should be set as the owner of the contract.\n */\n constructor(address _endpoint, address _delegate) {\n endpoint = ILayerZeroEndpointV2(_endpoint);\n\n if (_delegate == address(0)) revert InvalidDelegate();\n endpoint.setDelegate(_delegate);\n }\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.\n * @dev Set this to bytes32(0) to remove the peer address.\n * @dev Peer is a bytes32 to accommodate non-evm chains.\n */\n function setPeer(uint32 _eid, bytes32 _peer) public virtual onlyOwner {\n _setPeer(_eid, _peer);\n }\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n *\n * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.\n * @dev Set this to bytes32(0) to remove the peer address.\n * @dev Peer is a bytes32 to accommodate non-evm chains.\n */\n function _setPeer(uint32 _eid, bytes32 _peer) internal virtual {\n peers[_eid] = _peer;\n emit PeerSet(_eid, _peer);\n }\n\n /**\n * @notice Internal function to get the peer address associated with a specific endpoint; reverts if NOT set.\n * ie. the peer is set to bytes32(0).\n * @param _eid The endpoint ID.\n * @return peer The address of the peer associated with the specified endpoint.\n */\n function _getPeerOrRevert(uint32 _eid) internal view virtual returns (bytes32) {\n bytes32 peer = peers[_eid];\n if (peer == bytes32(0)) revert NoPeer(_eid);\n return peer;\n }\n\n /**\n * @notice Sets the delegate address for the OApp.\n * @param _delegate The address of the delegate to be set.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Provides the ability for a delegate to set configs, on behalf of the OApp, directly on the Endpoint contract.\n */\n function setDelegate(address _delegate) public onlyOwner {\n endpoint.setDelegate(_delegate);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IOAppReceiver, Origin } from \"./interfaces/IOAppReceiver.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OAppReceiver\n * @dev Abstract contract implementing the ILayerZeroReceiver interface and extending OAppCore for OApp receivers.\n */\nabstract contract OAppReceiver is IOAppReceiver, OAppCore {\n // Custom error message for when the caller is not the registered endpoint/\n error OnlyEndpoint(address addr);\n\n // @dev The version of the OAppReceiver implementation.\n // @dev Version is bumped when changes are made to this contract.\n uint64 internal constant RECEIVER_VERSION = 2;\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n *\n * @dev Providing 0 as the default for OAppSender version. Indicates that the OAppSender is not implemented.\n * ie. this is a RECEIVE only OApp.\n * @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions.\n */\n function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {\n return (0, RECEIVER_VERSION);\n }\n\n /**\n * @notice Indicates whether an address is an approved composeMsg sender to the Endpoint.\n * @dev _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @dev _message The lzReceive payload.\n * @param _sender The sender address.\n * @return isSender Is a valid sender.\n *\n * @dev Applications can optionally choose to implement separate composeMsg senders that are NOT the bridging layer.\n * @dev The default sender IS the OAppReceiver implementer.\n */\n function isComposeMsgSender(\n Origin calldata /*_origin*/,\n bytes calldata /*_message*/,\n address _sender\n ) public view virtual returns (bool) {\n return _sender == address(this);\n }\n\n /**\n * @notice Checks if the path initialization is allowed based on the provided origin.\n * @param origin The origin information containing the source endpoint and sender address.\n * @return Whether the path has been initialized.\n *\n * @dev This indicates to the endpoint that the OApp has enabled msgs for this particular path to be received.\n * @dev This defaults to assuming if a peer has been set, its initialized.\n * Can be overridden by the OApp if there is other logic to determine this.\n */\n function allowInitializePath(Origin calldata origin) public view virtual returns (bool) {\n return peers[origin.srcEid] == origin.sender;\n }\n\n /**\n * @notice Retrieves the next nonce for a given source endpoint and sender address.\n * @dev _srcEid The source endpoint ID.\n * @dev _sender The sender address.\n * @return nonce The next nonce.\n *\n * @dev The path nonce starts from 1. If 0 is returned it means that there is NO nonce ordered enforcement.\n * @dev Is required by the off-chain executor to determine the OApp expects msg execution is ordered.\n * @dev This is also enforced by the OApp.\n * @dev By default this is NOT enabled. ie. nextNonce is hardcoded to return 0.\n */\n function nextNonce(uint32 /*_srcEid*/, bytes32 /*_sender*/) public view virtual returns (uint64 nonce) {\n return 0;\n }\n\n /**\n * @dev Entry point for receiving messages or packets from the endpoint.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The payload of the received message.\n * @param _executor The address of the executor for the received message.\n * @param _extraData Additional arbitrary data provided by the corresponding executor.\n *\n * @dev Entry point for receiving msg/packet from the LayerZero endpoint.\n */\n function lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) public payable virtual {\n // Ensures that only the endpoint can attempt to lzReceive() messages to this OApp.\n if (address(endpoint) != msg.sender) revert OnlyEndpoint(msg.sender);\n\n // Ensure that the sender matches the expected peer for the source endpoint.\n if (_getPeerOrRevert(_origin.srcEid) != _origin.sender) revert OnlyPeer(_origin.srcEid, _origin.sender);\n\n // Call the internal OApp implementation of lzReceive.\n _lzReceive(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Internal function to implement lzReceive logic without needing to copy the basic parameter validation.\n */\n function _lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual;\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { SafeERC20, IERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { MessagingParams, MessagingFee, MessagingReceipt } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OAppSender\n * @dev Abstract contract implementing the OAppSender functionality for sending messages to a LayerZero endpoint.\n */\nabstract contract OAppSender is OAppCore {\n using SafeERC20 for IERC20;\n\n // Custom error messages\n error NotEnoughNative(uint256 msgValue);\n error LzTokenUnavailable();\n\n // @dev The version of the OAppSender implementation.\n // @dev Version is bumped when changes are made to this contract.\n uint64 internal constant SENDER_VERSION = 1;\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n *\n * @dev Providing 0 as the default for OAppReceiver version. Indicates that the OAppReceiver is not implemented.\n * ie. this is a SEND only OApp.\n * @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions\n */\n function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {\n return (SENDER_VERSION, 0);\n }\n\n /**\n * @dev Internal function to interact with the LayerZero EndpointV2.quote() for fee calculation.\n * @param _dstEid The destination endpoint ID.\n * @param _message The message payload.\n * @param _options Additional options for the message.\n * @param _payInLzToken Flag indicating whether to pay the fee in LZ tokens.\n * @return fee The calculated MessagingFee for the message.\n * - nativeFee: The native fee for the message.\n * - lzTokenFee: The LZ token fee for the message.\n */\n function _quote(\n uint32 _dstEid,\n bytes memory _message,\n bytes memory _options,\n bool _payInLzToken\n ) internal view virtual returns (MessagingFee memory fee) {\n return\n endpoint.quote(\n MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _payInLzToken),\n address(this)\n );\n }\n\n /**\n * @dev Internal function to interact with the LayerZero EndpointV2.send() for sending a message.\n * @param _dstEid The destination endpoint ID.\n * @param _message The message payload.\n * @param _options Additional options for the message.\n * @param _fee The calculated LayerZero fee for the message.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess fee values sent to the endpoint.\n * @return receipt The receipt for the sent message.\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function _lzSend(\n uint32 _dstEid,\n bytes memory _message,\n bytes memory _options,\n MessagingFee memory _fee,\n address _refundAddress\n ) internal virtual returns (MessagingReceipt memory receipt) {\n // @dev Push corresponding fees to the endpoint, any excess is sent back to the _refundAddress from the endpoint.\n uint256 messageValue = _payNative(_fee.nativeFee);\n if (_fee.lzTokenFee > 0) _payLzToken(_fee.lzTokenFee);\n\n return\n // solhint-disable-next-line check-send-result\n endpoint.send{ value: messageValue }(\n MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _fee.lzTokenFee > 0),\n _refundAddress\n );\n }\n\n /**\n * @dev Internal function to pay the native fee associated with the message.\n * @param _nativeFee The native fee to be paid.\n * @return nativeFee The amount of native currency paid.\n *\n * @dev If the OApp needs to initiate MULTIPLE LayerZero messages in a single transaction,\n * this will need to be overridden because msg.value would contain multiple lzFees.\n * @dev Should be overridden in the event the LayerZero endpoint requires a different native currency.\n * @dev Some EVMs use an ERC20 as a method for paying transactions/gasFees.\n * @dev The endpoint is EITHER/OR, ie. it will NOT support both types of native payment at a time.\n */\n function _payNative(uint256 _nativeFee) internal virtual returns (uint256 nativeFee) {\n if (msg.value != _nativeFee) revert NotEnoughNative(msg.value);\n return _nativeFee;\n }\n\n /**\n * @dev Internal function to pay the LZ token fee associated with the message.\n * @param _lzTokenFee The LZ token fee to be paid.\n *\n * @dev If the caller is trying to pay in the specified lzToken, then the lzTokenFee is passed to the endpoint.\n * @dev Any excess sent, is passed back to the specified _refundAddress in the _lzSend().\n */\n function _payLzToken(uint256 _lzTokenFee) internal virtual {\n // @dev Cannot cache the token because it is not immutable in the endpoint.\n address lzToken = endpoint.lzToken();\n if (lzToken == address(0)) revert LzTokenUnavailable();\n\n // Pay LZ token fee by sending tokens to the endpoint.\n IERC20(lzToken).safeTransferFrom(msg.sender, address(endpoint), _lzTokenFee);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/interfaces/IOAppPreCrimeSimulator.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n// @dev Import the Origin so it's exposed to OAppPreCrimeSimulator implementers.\n// solhint-disable-next-line no-unused-import\nimport { InboundPacket, Origin } from \"../libs/Packet.sol\";\n\n/**\n * @title IOAppPreCrimeSimulator Interface\n * @dev Interface for the preCrime simulation functionality in an OApp.\n */\ninterface IOAppPreCrimeSimulator {\n // @dev simulation result used in PreCrime implementation\n error SimulationResult(bytes result);\n error OnlySelf();\n\n /**\n * @dev Emitted when the preCrime contract address is set.\n * @param preCrimeAddress The address of the preCrime contract.\n */\n event PreCrimeSet(address preCrimeAddress);\n\n /**\n * @dev Retrieves the address of the preCrime contract implementation.\n * @return The address of the preCrime contract.\n */\n function preCrime() external view returns (address);\n\n /**\n * @dev Retrieves the address of the OApp contract.\n * @return The address of the OApp contract.\n */\n function oApp() external view returns (address);\n\n /**\n * @dev Sets the preCrime contract address.\n * @param _preCrime The address of the preCrime contract.\n */\n function setPreCrime(address _preCrime) external;\n\n /**\n * @dev Mocks receiving a packet, then reverts with a series of data to infer the state/result.\n * @param _packets An array of LayerZero InboundPacket objects representing received packets.\n */\n function lzReceiveAndRevert(InboundPacket[] calldata _packets) external payable;\n\n /**\n * @dev checks if the specified peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint Id to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n */\n function isPeer(uint32 _eid, bytes32 _peer) external view returns (bool);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/interfaces/IPreCrime.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\nstruct PreCrimePeer {\n uint32 eid;\n bytes32 preCrime;\n bytes32 oApp;\n}\n\n// TODO not done yet\ninterface IPreCrime {\n error OnlyOffChain();\n\n // for simulate()\n error PacketOversize(uint256 max, uint256 actual);\n error PacketUnsorted();\n error SimulationFailed(bytes reason);\n\n // for preCrime()\n error SimulationResultNotFound(uint32 eid);\n error InvalidSimulationResult(uint32 eid, bytes reason);\n error CrimeFound(bytes crime);\n\n function getConfig(bytes[] calldata _packets, uint256[] calldata _packetMsgValues) external returns (bytes memory);\n\n function simulate(\n bytes[] calldata _packets,\n uint256[] calldata _packetMsgValues\n ) external payable returns (bytes memory);\n\n function buildSimulationResult() external view returns (bytes memory);\n\n function preCrime(\n bytes[] calldata _packets,\n uint256[] calldata _packetMsgValues,\n bytes[] calldata _simulations\n ) external;\n\n function version() external view returns (uint64 major, uint8 minor);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/libs/Packet.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Origin } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\nimport { PacketV1Codec } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol\";\n\n/**\n * @title InboundPacket\n * @dev Structure representing an inbound packet received by the contract.\n */\nstruct InboundPacket {\n Origin origin; // Origin information of the packet.\n uint32 dstEid; // Destination endpointId of the packet.\n address receiver; // Receiver address for the packet.\n bytes32 guid; // Unique identifier of the packet.\n uint256 value; // msg.value of the packet.\n address executor; // Executor address for the packet.\n bytes message; // Message payload of the packet.\n bytes extraData; // Additional arbitrary data for the packet.\n}\n\n/**\n * @title PacketDecoder\n * @dev Library for decoding LayerZero packets.\n */\nlibrary PacketDecoder {\n using PacketV1Codec for bytes;\n\n /**\n * @dev Decode an inbound packet from the given packet data.\n * @param _packet The packet data to decode.\n * @return packet An InboundPacket struct representing the decoded packet.\n */\n function decode(bytes calldata _packet) internal pure returns (InboundPacket memory packet) {\n packet.origin = Origin(_packet.srcEid(), _packet.sender(), _packet.nonce());\n packet.dstEid = _packet.dstEid();\n packet.receiver = _packet.receiverB20();\n packet.guid = _packet.guid();\n packet.message = _packet.message();\n }\n\n /**\n * @dev Decode multiple inbound packets from the given packet data and associated message values.\n * @param _packets An array of packet data to decode.\n * @param _packetMsgValues An array of associated message values for each packet.\n * @return packets An array of InboundPacket structs representing the decoded packets.\n */\n function decode(\n bytes[] calldata _packets,\n uint256[] memory _packetMsgValues\n ) internal pure returns (InboundPacket[] memory packets) {\n packets = new InboundPacket[](_packets.length);\n for (uint256 i = 0; i < _packets.length; i++) {\n bytes calldata packet = _packets[i];\n packets[i] = PacketDecoder.decode(packet);\n // @dev Allows the verifier to specify the msg.value that gets passed in lzReceive.\n packets[i].value = _packetMsgValues[i];\n }\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/OAppPreCrimeSimulator.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IPreCrime } from \"./interfaces/IPreCrime.sol\";\nimport { IOAppPreCrimeSimulator, InboundPacket, Origin } from \"./interfaces/IOAppPreCrimeSimulator.sol\";\n\n/**\n * @title OAppPreCrimeSimulator\n * @dev Abstract contract serving as the base for preCrime simulation functionality in an OApp.\n */\nabstract contract OAppPreCrimeSimulator is IOAppPreCrimeSimulator, Ownable {\n // The address of the preCrime implementation.\n address public preCrime;\n\n /**\n * @dev Retrieves the address of the OApp contract.\n * @return The address of the OApp contract.\n *\n * @dev The simulator contract is the base contract for the OApp by default.\n * @dev If the simulator is a separate contract, override this function.\n */\n function oApp() external view virtual returns (address) {\n return address(this);\n }\n\n /**\n * @dev Sets the preCrime contract address.\n * @param _preCrime The address of the preCrime contract.\n */\n function setPreCrime(address _preCrime) public virtual onlyOwner {\n preCrime = _preCrime;\n emit PreCrimeSet(_preCrime);\n }\n\n /**\n * @dev Interface for pre-crime simulations. Always reverts at the end with the simulation results.\n * @param _packets An array of InboundPacket objects representing received packets to be delivered.\n *\n * @dev WARNING: MUST revert at the end with the simulation results.\n * @dev Gives the preCrime implementation the ability to mock sending packets to the lzReceive function,\n * WITHOUT actually executing them.\n */\n function lzReceiveAndRevert(InboundPacket[] calldata _packets) public payable virtual {\n for (uint256 i = 0; i < _packets.length; i++) {\n InboundPacket calldata packet = _packets[i];\n\n // Ignore packets that are not from trusted peers.\n if (!isPeer(packet.origin.srcEid, packet.origin.sender)) continue;\n\n // @dev Because a verifier is calling this function, it doesnt have access to executor params:\n // - address _executor\n // - bytes calldata _extraData\n // preCrime will NOT work for OApps that rely on these two parameters inside of their _lzReceive().\n // They are instead stubbed to default values, address(0) and bytes(\"\")\n // @dev Calling this.lzReceiveSimulate removes ability for assembly return 0 callstack exit,\n // which would cause the revert to be ignored.\n this.lzReceiveSimulate{ value: packet.value }(\n packet.origin,\n packet.guid,\n packet.message,\n packet.executor,\n packet.extraData\n );\n }\n\n // @dev Revert with the simulation results. msg.sender must implement IPreCrime.buildSimulationResult().\n revert SimulationResult(IPreCrime(msg.sender).buildSimulationResult());\n }\n\n /**\n * @dev Is effectively an internal function because msg.sender must be address(this).\n * Allows resetting the call stack for 'internal' calls.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _guid The unique identifier of the packet.\n * @param _message The message payload of the packet.\n * @param _executor The executor address for the packet.\n * @param _extraData Additional data for the packet.\n */\n function lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) external payable virtual {\n // @dev Ensure ONLY can be called 'internally'.\n if (msg.sender != address(this)) revert OnlySelf();\n _lzReceiveSimulate(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Internal function to handle the OAppPreCrimeSimulator simulated receive.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The GUID of the LayerZero message.\n * @param _message The LayerZero message.\n * @param _executor The address of the off-chain executor.\n * @param _extraData Arbitrary data passed by the msg executor.\n *\n * @dev Enables the preCrime simulator to mock sending lzReceive() messages,\n * routes the msg down from the OAppPreCrimeSimulator, and back up to the OAppReceiver.\n */\n function _lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual;\n\n /**\n * @dev checks if the specified peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint Id to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n */\n function isPeer(uint32 _eid, bytes32 _peer) public view virtual returns (bool);\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/interfaces/IMintableBurnable.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\n/// @title Interface for mintable and burnable tokens\ninterface IMintableBurnable {\n \n /**\n * @notice Burns tokens from a specified account\n * @param _from Address from which tokens will be burned\n * @param _amount Amount of tokens to be burned\n * @return success Indicates whether the operation was successful\n */\n function burn(address _from, uint256 _amount) external returns (bool success);\n\n /**\n * @notice Mints tokens to a specified account\n * @param _to Address to which tokens will be minted\n * @param _amount Amount of tokens to be minted\n * @return success Indicates whether the operation was successful\n */\n function mint(address _to, uint256 _amount) external returns (bool success);\n}" + }, + "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { MessagingReceipt, MessagingFee } from \"@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol\";\n\n/**\n * @dev Struct representing token parameters for the OFT send() operation.\n */\nstruct SendParam {\n uint32 dstEid; // Destination endpoint ID.\n bytes32 to; // Recipient address.\n uint256 amountLD; // Amount to send in local decimals.\n uint256 minAmountLD; // Minimum amount to send in local decimals.\n bytes extraOptions; // Additional options supplied by the caller to be used in the LayerZero message.\n bytes composeMsg; // The composed message for the send() operation.\n bytes oftCmd; // The OFT command to be executed, unused in default OFT implementations.\n}\n\n/**\n * @dev Struct representing OFT limit information.\n * @dev These amounts can change dynamically and are up the specific oft implementation.\n */\nstruct OFTLimit {\n uint256 minAmountLD; // Minimum amount in local decimals that can be sent to the recipient.\n uint256 maxAmountLD; // Maximum amount in local decimals that can be sent to the recipient.\n}\n\n/**\n * @dev Struct representing OFT receipt information.\n */\nstruct OFTReceipt {\n uint256 amountSentLD; // Amount of tokens ACTUALLY debited from the sender in local decimals.\n // @dev In non-default implementations, the amountReceivedLD COULD differ from this value.\n uint256 amountReceivedLD; // Amount of tokens to be received on the remote side.\n}\n\n/**\n * @dev Struct representing OFT fee details.\n * @dev Future proof mechanism to provide a standardized way to communicate fees to things like a UI.\n */\nstruct OFTFeeDetail {\n int256 feeAmountLD; // Amount of the fee in local decimals.\n string description; // Description of the fee.\n}\n\n/**\n * @title IOFT\n * @dev Interface for the OftChain (OFT) token.\n * @dev Does not inherit ERC20 to accommodate usage by OFTAdapter as well.\n * @dev This specific interface ID is '0x02e49c2c'.\n */\ninterface IOFT {\n // Custom error messages\n error InvalidLocalDecimals();\n error SlippageExceeded(uint256 amountLD, uint256 minAmountLD);\n\n // Events\n event OFTSent(\n bytes32 indexed guid, // GUID of the OFT message.\n uint32 dstEid, // Destination Endpoint ID.\n address indexed fromAddress, // Address of the sender on the src chain.\n uint256 amountSentLD, // Amount of tokens sent in local decimals.\n uint256 amountReceivedLD // Amount of tokens received in local decimals.\n );\n event OFTReceived(\n bytes32 indexed guid, // GUID of the OFT message.\n uint32 srcEid, // Source Endpoint ID.\n address indexed toAddress, // Address of the recipient on the dst chain.\n uint256 amountReceivedLD // Amount of tokens received in local decimals.\n );\n\n /**\n * @notice Retrieves interfaceID and the version of the OFT.\n * @return interfaceId The interface ID.\n * @return version The version.\n *\n * @dev interfaceId: This specific interface ID is '0x02e49c2c'.\n * @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.\n * @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.\n * ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)\n */\n function oftVersion() external view returns (bytes4 interfaceId, uint64 version);\n\n /**\n * @notice Retrieves the address of the token associated with the OFT.\n * @return token The address of the ERC20 token implementation.\n */\n function token() external view returns (address);\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the 'token()' to send.\n * @return requiresApproval Needs approval of the underlying token implementation.\n *\n * @dev Allows things like wallet implementers to determine integration requirements,\n * without understanding the underlying token implementation.\n */\n function approvalRequired() external view returns (bool);\n\n /**\n * @notice Retrieves the shared decimals of the OFT.\n * @return sharedDecimals The shared decimals of the OFT.\n */\n function sharedDecimals() external view returns (uint8);\n\n /**\n * @notice Provides the fee breakdown and settings data for an OFT. Unused in the default implementation.\n * @param _sendParam The parameters for the send operation.\n * @return limit The OFT limit information.\n * @return oftFeeDetails The details of OFT fees.\n * @return receipt The OFT receipt information.\n */\n function quoteOFT(\n SendParam calldata _sendParam\n ) external view returns (OFTLimit memory, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory);\n\n /**\n * @notice Provides a quote for the send() operation.\n * @param _sendParam The parameters for the send() operation.\n * @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.\n * @return fee The calculated LayerZero messaging fee from the send() operation.\n *\n * @dev MessagingFee: LayerZero msg fee\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n */\n function quoteSend(SendParam calldata _sendParam, bool _payInLzToken) external view returns (MessagingFee memory);\n\n /**\n * @notice Executes the send() operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The fee information supplied by the caller.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds from fees etc. on the src.\n * @return receipt The LayerZero messaging receipt from the send() operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) external payable returns (MessagingReceipt memory, OFTReceipt memory);\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/libs/OFTComposeMsgCodec.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nlibrary OFTComposeMsgCodec {\n // Offset constants for decoding composed messages\n uint8 private constant NONCE_OFFSET = 8;\n uint8 private constant SRC_EID_OFFSET = 12;\n uint8 private constant AMOUNT_LD_OFFSET = 44;\n uint8 private constant COMPOSE_FROM_OFFSET = 76;\n\n /**\n * @dev Encodes a OFT composed message.\n * @param _nonce The nonce value.\n * @param _srcEid The source endpoint ID.\n * @param _amountLD The amount in local decimals.\n * @param _composeMsg The composed message.\n * @return _msg The encoded Composed message.\n */\n function encode(\n uint64 _nonce,\n uint32 _srcEid,\n uint256 _amountLD,\n bytes memory _composeMsg // 0x[composeFrom][composeMsg]\n ) internal pure returns (bytes memory _msg) {\n _msg = abi.encodePacked(_nonce, _srcEid, _amountLD, _composeMsg);\n }\n\n /**\n * @dev Retrieves the nonce for the composed message.\n * @param _msg The message.\n * @return The nonce value.\n */\n function nonce(bytes calldata _msg) internal pure returns (uint64) {\n return uint64(bytes8(_msg[:NONCE_OFFSET]));\n }\n\n /**\n * @dev Retrieves the source endpoint ID for the composed message.\n * @param _msg The message.\n * @return The source endpoint ID.\n */\n function srcEid(bytes calldata _msg) internal pure returns (uint32) {\n return uint32(bytes4(_msg[NONCE_OFFSET:SRC_EID_OFFSET]));\n }\n\n /**\n * @dev Retrieves the amount in local decimals from the composed message.\n * @param _msg The message.\n * @return The amount in local decimals.\n */\n function amountLD(bytes calldata _msg) internal pure returns (uint256) {\n return uint256(bytes32(_msg[SRC_EID_OFFSET:AMOUNT_LD_OFFSET]));\n }\n\n /**\n * @dev Retrieves the composeFrom value from the composed message.\n * @param _msg The message.\n * @return The composeFrom value.\n */\n function composeFrom(bytes calldata _msg) internal pure returns (bytes32) {\n return bytes32(_msg[AMOUNT_LD_OFFSET:COMPOSE_FROM_OFFSET]);\n }\n\n /**\n * @dev Retrieves the composed message.\n * @param _msg The message.\n * @return The composed message.\n */\n function composeMsg(bytes calldata _msg) internal pure returns (bytes memory) {\n return _msg[COMPOSE_FROM_OFFSET:];\n }\n\n /**\n * @dev Converts an address to bytes32.\n * @param _addr The address to convert.\n * @return The bytes32 representation of the address.\n */\n function addressToBytes32(address _addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(_addr)));\n }\n\n /**\n * @dev Converts bytes32 to an address.\n * @param _b The bytes32 value to convert.\n * @return The address representation of bytes32.\n */\n function bytes32ToAddress(bytes32 _b) internal pure returns (address) {\n return address(uint160(uint256(_b)));\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/libs/OFTMsgCodec.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nlibrary OFTMsgCodec {\n // Offset constants for encoding and decoding OFT messages\n uint8 private constant SEND_TO_OFFSET = 32;\n uint8 private constant SEND_AMOUNT_SD_OFFSET = 40;\n\n /**\n * @dev Encodes an OFT LayerZero message.\n * @param _sendTo The recipient address.\n * @param _amountShared The amount in shared decimals.\n * @param _composeMsg The composed message.\n * @return _msg The encoded message.\n * @return hasCompose A boolean indicating whether the message has a composed payload.\n */\n function encode(\n bytes32 _sendTo,\n uint64 _amountShared,\n bytes memory _composeMsg\n ) internal view returns (bytes memory _msg, bool hasCompose) {\n hasCompose = _composeMsg.length > 0;\n // @dev Remote chains will want to know the composed function caller ie. msg.sender on the src.\n _msg = hasCompose\n ? abi.encodePacked(_sendTo, _amountShared, addressToBytes32(msg.sender), _composeMsg)\n : abi.encodePacked(_sendTo, _amountShared);\n }\n\n /**\n * @dev Checks if the OFT message is composed.\n * @param _msg The OFT message.\n * @return A boolean indicating whether the message is composed.\n */\n function isComposed(bytes calldata _msg) internal pure returns (bool) {\n return _msg.length > SEND_AMOUNT_SD_OFFSET;\n }\n\n /**\n * @dev Retrieves the recipient address from the OFT message.\n * @param _msg The OFT message.\n * @return The recipient address.\n */\n function sendTo(bytes calldata _msg) internal pure returns (bytes32) {\n return bytes32(_msg[:SEND_TO_OFFSET]);\n }\n\n /**\n * @dev Retrieves the amount in shared decimals from the OFT message.\n * @param _msg The OFT message.\n * @return The amount in shared decimals.\n */\n function amountSD(bytes calldata _msg) internal pure returns (uint64) {\n return uint64(bytes8(_msg[SEND_TO_OFFSET:SEND_AMOUNT_SD_OFFSET]));\n }\n\n /**\n * @dev Retrieves the composed message from the OFT message.\n * @param _msg The OFT message.\n * @return The composed message.\n */\n function composeMsg(bytes calldata _msg) internal pure returns (bytes memory) {\n return _msg[SEND_AMOUNT_SD_OFFSET:];\n }\n\n /**\n * @dev Converts an address to bytes32.\n * @param _addr The address to convert.\n * @return The bytes32 representation of the address.\n */\n function addressToBytes32(address _addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(_addr)));\n }\n\n /**\n * @dev Converts bytes32 to an address.\n * @param _b The bytes32 value to convert.\n * @return The address representation of bytes32.\n */\n function bytes32ToAddress(bytes32 _b) internal pure returns (address) {\n return address(uint160(uint256(_b)));\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/MintBurnOFTAdapter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\n// External imports\nimport { IERC20, IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\n// Local imports\nimport { IMintableBurnable } from \"./interfaces/IMintableBurnable.sol\";\nimport { OFTCore } from \"./OFTCore.sol\";\n\n/**\n * @title MintBurnOFTAdapter\n * @notice A variant of the standard OFT Adapter that uses an existing ERC20's mint and burn mechanisms for cross-chain transfers.\n *\n * @dev Inherits from OFTCore and provides implementations for _debit and _credit functions using a mintable and burnable token.\n */\nabstract contract MintBurnOFTAdapter is OFTCore {\n /// @dev The underlying ERC20 token.\n IERC20 internal immutable innerToken;\n\n /// @notice The contract responsible for minting and burning tokens.\n IMintableBurnable public immutable minterBurner;\n\n /**\n * @notice Initializes the MintBurnOFTAdapter contract.\n *\n * @param _token The address of the underlying ERC20 token.\n * @param _minterBurner The contract responsible for minting and burning tokens.\n * @param _lzEndpoint The LayerZero endpoint address.\n * @param _delegate The address of the delegate.\n *\n * @dev Calls the OFTCore constructor with the token's decimals, the endpoint, and the delegate.\n */\n constructor(\n address _token,\n IMintableBurnable _minterBurner,\n address _lzEndpoint,\n address _delegate\n ) OFTCore(IERC20Metadata(_token).decimals(), _lzEndpoint, _delegate) {\n innerToken = IERC20(_token);\n minterBurner = _minterBurner;\n }\n\n /**\n * @notice Retrieves the address of the underlying ERC20 token.\n *\n * @return The address of the adapted ERC20 token.\n *\n * @dev In the case of MintBurnOFTAdapter, address(this) and erc20 are NOT the same contract.\n */\n function token() public view returns (address) {\n return address(innerToken);\n }\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the underlying token to send.\n *\n * @return requiresApproval True if approval is required, false otherwise.\n *\n * @dev In this MintBurnOFTAdapter, approval is NOT required because it uses mint and burn privileges.\n */\n function approvalRequired() external pure virtual returns (bool) {\n return false;\n }\n\n /**\n * @notice Burns tokens from the sender's balance to prepare for sending.\n *\n * @param _from The address to debit the tokens from.\n * @param _amountLD The amount of tokens to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination chain ID.\n *\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, i.e., 1 token in, 1 token out.\n * If the 'innerToken' applies something like a transfer fee, the default will NOT work.\n * A pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual override returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n (amountSentLD, amountReceivedLD) = _debitView(_amountLD, _minAmountLD, _dstEid);\n // Burns tokens from the caller.\n minterBurner.burn(_from, amountSentLD);\n }\n\n /**\n * @notice Mints tokens to the specified address upon receiving them.\n *\n * @param _to The address to credit the tokens to.\n * @param _amountLD The amount of tokens to credit in local decimals.\n *\n * @return amountReceivedLD The amount of tokens actually received in local decimals.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, i.e., 1 token in, 1 token out.\n * If the 'innerToken' applies something like a transfer fee, the default will NOT work.\n * A pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /* _srcEid */\n ) internal virtual override returns (uint256 amountReceivedLD) {\n if (_to == address(0x0)) _to = address(0xdead); // _mint(...) does not support address(0x0)\n // Mints the tokens and transfers to the recipient.\n minterBurner.mint(_to, _amountLD);\n // In the case of NON-default OFTAdapter, the amountLD MIGHT not be equal to amountReceivedLD.\n return _amountLD;\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/OFTAdapter.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IERC20Metadata, IERC20 } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IOFT, OFTCore } from \"./OFTCore.sol\";\n\n/**\n * @title OFTAdapter Contract\n * @dev OFTAdapter is a contract that adapts an ERC-20 token to the OFT functionality.\n *\n * @dev For existing ERC20 tokens, this can be used to convert the token to crosschain compatibility.\n * @dev WARNING: ONLY 1 of these should exist for a given global mesh,\n * unless you make a NON-default implementation of OFT and needs to be done very carefully.\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountSentLD/amountReceivedLD.\n */\nabstract contract OFTAdapter is OFTCore {\n using SafeERC20 for IERC20;\n\n IERC20 internal immutable innerToken;\n\n /**\n * @dev Constructor for the OFTAdapter contract.\n * @param _token The address of the ERC-20 token to be adapted.\n * @param _lzEndpoint The LayerZero endpoint address.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(\n address _token,\n address _lzEndpoint,\n address _delegate\n ) OFTCore(IERC20Metadata(_token).decimals(), _lzEndpoint, _delegate) {\n innerToken = IERC20(_token);\n }\n\n /**\n * @dev Retrieves the address of the underlying ERC20 implementation.\n * @return The address of the adapted ERC-20 token.\n *\n * @dev In the case of OFTAdapter, address(this) and erc20 are NOT the same contract.\n */\n function token() public view returns (address) {\n return address(innerToken);\n }\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the 'token()' to send.\n * @return requiresApproval Needs approval of the underlying token implementation.\n *\n * @dev In the case of default OFTAdapter, approval is required.\n * @dev In non-default OFTAdapter contracts with something like mint and burn privileges, it would NOT need approval.\n */\n function approvalRequired() external pure virtual returns (bool) {\n return true;\n }\n\n /**\n * @dev Locks tokens from the sender's specified balance in this contract.\n * @param _from The address to debit from.\n * @param _amountLD The amount of tokens to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination chain ID.\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev msg.sender will need to approve this _amountLD of tokens to be locked inside of the contract.\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual override returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n (amountSentLD, amountReceivedLD) = _debitView(_amountLD, _minAmountLD, _dstEid);\n // @dev Lock tokens by moving them into this contract from the caller.\n innerToken.safeTransferFrom(_from, address(this), amountSentLD);\n }\n\n /**\n * @dev Credits tokens to the specified address.\n * @param _to The address to credit the tokens to.\n * @param _amountLD The amount of tokens to credit in local decimals.\n * @dev _srcEid The source chain ID.\n * @return amountReceivedLD The amount of tokens ACTUALLY received in local decimals.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /*_srcEid*/\n ) internal virtual override returns (uint256 amountReceivedLD) {\n // @dev Unlock the tokens and transfer to the recipient.\n innerToken.safeTransfer(_to, _amountLD);\n // @dev In the case of NON-default OFTAdapter, the amountLD MIGHT not be == amountReceivedLD.\n return _amountLD;\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/OFTCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { OApp, Origin } from \"@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol\";\nimport { OAppOptionsType3 } from \"@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol\";\nimport { IOAppMsgInspector } from \"@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppMsgInspector.sol\";\n\nimport { OAppPreCrimeSimulator } from \"@layerzerolabs/oapp-evm/contracts/precrime/OAppPreCrimeSimulator.sol\";\n\nimport { IOFT, SendParam, OFTLimit, OFTReceipt, OFTFeeDetail, MessagingReceipt, MessagingFee } from \"./interfaces/IOFT.sol\";\nimport { OFTMsgCodec } from \"./libs/OFTMsgCodec.sol\";\nimport { OFTComposeMsgCodec } from \"./libs/OFTComposeMsgCodec.sol\";\n\n/**\n * @title OFTCore\n * @dev Abstract contract for the OftChain (OFT) token.\n */\nabstract contract OFTCore is IOFT, OApp, OAppPreCrimeSimulator, OAppOptionsType3 {\n using OFTMsgCodec for bytes;\n using OFTMsgCodec for bytes32;\n\n // @notice Provides a conversion rate when swapping between denominations of SD and LD\n // - shareDecimals == SD == shared Decimals\n // - localDecimals == LD == local decimals\n // @dev Considers that tokens have different decimal amounts on various chains.\n // @dev eg.\n // For a token\n // - locally with 4 decimals --> 1.2345 => uint(12345)\n // - remotely with 2 decimals --> 1.23 => uint(123)\n // - The conversion rate would be 10 ** (4 - 2) = 100\n // @dev If you want to send 1.2345 -> (uint 12345), you CANNOT represent that value on the remote,\n // you can only display 1.23 -> uint(123).\n // @dev To preserve the dust that would otherwise be lost on that conversion,\n // we need to unify a denomination that can be represented on ALL chains inside of the OFT mesh\n uint256 public immutable decimalConversionRate;\n\n // @notice Msg types that are used to identify the various OFT operations.\n // @dev This can be extended in child contracts for non-default oft operations\n // @dev These values are used in things like combineOptions() in OAppOptionsType3.sol.\n uint16 public constant SEND = 1;\n uint16 public constant SEND_AND_CALL = 2;\n\n // Address of an optional contract to inspect both 'message' and 'options'\n address public msgInspector;\n event MsgInspectorSet(address inspector);\n\n /**\n * @dev Constructor.\n * @param _localDecimals The decimals of the token on the local chain (this chain).\n * @param _endpoint The address of the LayerZero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(uint8 _localDecimals, address _endpoint, address _delegate) OApp(_endpoint, _delegate) {\n if (_localDecimals < sharedDecimals()) revert InvalidLocalDecimals();\n decimalConversionRate = 10 ** (_localDecimals - sharedDecimals());\n }\n\n /**\n * @notice Retrieves interfaceID and the version of the OFT.\n * @return interfaceId The interface ID.\n * @return version The version.\n *\n * @dev interfaceId: This specific interface ID is '0x02e49c2c'.\n * @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.\n * @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.\n * ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)\n */\n function oftVersion() external pure virtual returns (bytes4 interfaceId, uint64 version) {\n return (type(IOFT).interfaceId, 1);\n }\n\n /**\n * @dev Retrieves the shared decimals of the OFT.\n * @return The shared decimals of the OFT.\n *\n * @dev Sets an implicit cap on the amount of tokens, over uint64.max() will need some sort of outbound cap / totalSupply cap\n * Lowest common decimal denominator between chains.\n * Defaults to 6 decimal places to provide up to 18,446,744,073,709.551615 units (max uint64).\n * For tokens exceeding this totalSupply(), they will need to override the sharedDecimals function with something smaller.\n * ie. 4 sharedDecimals would be 1,844,674,407,370,955.1615\n */\n function sharedDecimals() public view virtual returns (uint8) {\n return 6;\n }\n\n /**\n * @dev Sets the message inspector address for the OFT.\n * @param _msgInspector The address of the message inspector.\n *\n * @dev This is an optional contract that can be used to inspect both 'message' and 'options'.\n * @dev Set it to address(0) to disable it, or set it to a contract address to enable it.\n */\n function setMsgInspector(address _msgInspector) public virtual onlyOwner {\n msgInspector = _msgInspector;\n emit MsgInspectorSet(_msgInspector);\n }\n\n /**\n * @notice Provides the fee breakdown and settings data for an OFT. Unused in the default implementation.\n * @param _sendParam The parameters for the send operation.\n * @return oftLimit The OFT limit information.\n * @return oftFeeDetails The details of OFT fees.\n * @return oftReceipt The OFT receipt information.\n */\n function quoteOFT(\n SendParam calldata _sendParam\n )\n external\n view\n virtual\n returns (OFTLimit memory oftLimit, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory oftReceipt)\n {\n uint256 minAmountLD = 0; // Unused in the default implementation.\n uint256 maxAmountLD = IERC20(this.token()).totalSupply(); // Unused in the default implementation.\n oftLimit = OFTLimit(minAmountLD, maxAmountLD);\n\n // Unused in the default implementation; reserved for future complex fee details.\n oftFeeDetails = new OFTFeeDetail[](0);\n\n // @dev This is the same as the send() operation, but without the actual send.\n // - amountSentLD is the amount in local decimals that would be sent from the sender.\n // - amountReceivedLD is the amount in local decimals that will be credited to the recipient on the remote OFT instance.\n // @dev The amountSentLD MIGHT not equal the amount the user actually receives. HOWEVER, the default does.\n (uint256 amountSentLD, uint256 amountReceivedLD) = _debitView(\n _sendParam.amountLD,\n _sendParam.minAmountLD,\n _sendParam.dstEid\n );\n oftReceipt = OFTReceipt(amountSentLD, amountReceivedLD);\n }\n\n /**\n * @notice Provides a quote for the send() operation.\n * @param _sendParam The parameters for the send() operation.\n * @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.\n * @return msgFee The calculated LayerZero messaging fee from the send() operation.\n *\n * @dev MessagingFee: LayerZero msg fee\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n */\n function quoteSend(\n SendParam calldata _sendParam,\n bool _payInLzToken\n ) external view virtual returns (MessagingFee memory msgFee) {\n // @dev mock the amount to receive, this is the same operation used in the send().\n // The quote is as similar as possible to the actual send() operation.\n (, uint256 amountReceivedLD) = _debitView(_sendParam.amountLD, _sendParam.minAmountLD, _sendParam.dstEid);\n\n // @dev Builds the options and OFT message to quote in the endpoint.\n (bytes memory message, bytes memory options) = _buildMsgAndOptions(_sendParam, amountReceivedLD);\n\n // @dev Calculates the LayerZero fee for the send() operation.\n return _quote(_sendParam.dstEid, message, options, _payInLzToken);\n }\n\n /**\n * @dev Executes the send operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The calculated fee for the send() operation.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds.\n * @return msgReceipt The receipt for the send operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) external payable virtual returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) {\n return _send(_sendParam, _fee, _refundAddress);\n }\n\n /**\n * @dev Internal function to execute the send operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The calculated fee for the send() operation.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds.\n * @return msgReceipt The receipt for the send operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function _send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) internal virtual returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) {\n // @dev Applies the token transfers regarding this send() operation.\n // - amountSentLD is the amount in local decimals that was ACTUALLY sent/debited from the sender.\n // - amountReceivedLD is the amount in local decimals that will be received/credited to the recipient on the remote OFT instance.\n (uint256 amountSentLD, uint256 amountReceivedLD) = _debit(\n msg.sender,\n _sendParam.amountLD,\n _sendParam.minAmountLD,\n _sendParam.dstEid\n );\n\n // @dev Builds the options and OFT message to quote in the endpoint.\n (bytes memory message, bytes memory options) = _buildMsgAndOptions(_sendParam, amountReceivedLD);\n\n // @dev Sends the message to the LayerZero endpoint and returns the LayerZero msg receipt.\n msgReceipt = _lzSend(_sendParam.dstEid, message, options, _fee, _refundAddress);\n // @dev Formulate the OFT receipt.\n oftReceipt = OFTReceipt(amountSentLD, amountReceivedLD);\n\n emit OFTSent(msgReceipt.guid, _sendParam.dstEid, msg.sender, amountSentLD, amountReceivedLD);\n }\n\n /**\n * @dev Internal function to build the message and options.\n * @param _sendParam The parameters for the send() operation.\n * @param _amountLD The amount in local decimals.\n * @return message The encoded message.\n * @return options The encoded options.\n */\n function _buildMsgAndOptions(\n SendParam calldata _sendParam,\n uint256 _amountLD\n ) internal view virtual returns (bytes memory message, bytes memory options) {\n bool hasCompose;\n // @dev This generated message has the msg.sender encoded into the payload so the remote knows who the caller is.\n (message, hasCompose) = OFTMsgCodec.encode(\n _sendParam.to,\n _toSD(_amountLD),\n // @dev Must be include a non empty bytes if you want to compose, EVEN if you dont need it on the remote.\n // EVEN if you dont require an arbitrary payload to be sent... eg. '0x01'\n _sendParam.composeMsg\n );\n // @dev Change the msg type depending if its composed or not.\n uint16 msgType = hasCompose ? SEND_AND_CALL : SEND;\n // @dev Combine the callers _extraOptions with the enforced options via the OAppOptionsType3.\n options = combineOptions(_sendParam.dstEid, msgType, _sendParam.extraOptions);\n\n // @dev Optionally inspect the message and options depending if the OApp owner has set a msg inspector.\n // @dev If it fails inspection, needs to revert in the implementation. ie. does not rely on return boolean\n address inspector = msgInspector; // caches the msgInspector to avoid potential double storage read\n if (inspector != address(0)) IOAppMsgInspector(inspector).inspect(message, options);\n }\n\n /**\n * @dev Internal function to handle the receive on the LayerZero endpoint.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The encoded message.\n * @dev _executor The address of the executor.\n * @dev _extraData Additional data.\n */\n function _lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address /*_executor*/, // @dev unused in the default implementation.\n bytes calldata /*_extraData*/ // @dev unused in the default implementation.\n ) internal virtual override {\n // @dev The src sending chain doesnt know the address length on this chain (potentially non-evm)\n // Thus everything is bytes32() encoded in flight.\n address toAddress = _message.sendTo().bytes32ToAddress();\n // @dev Credit the amountLD to the recipient and return the ACTUAL amount the recipient received in local decimals\n uint256 amountReceivedLD = _credit(toAddress, _toLD(_message.amountSD()), _origin.srcEid);\n\n if (_message.isComposed()) {\n // @dev Proprietary composeMsg format for the OFT.\n bytes memory composeMsg = OFTComposeMsgCodec.encode(\n _origin.nonce,\n _origin.srcEid,\n amountReceivedLD,\n _message.composeMsg()\n );\n\n // @dev Stores the lzCompose payload that will be executed in a separate tx.\n // Standardizes functionality for executing arbitrary contract invocation on some non-evm chains.\n // @dev The off-chain executor will listen and process the msg based on the src-chain-callers compose options passed.\n // @dev The index is used when a OApp needs to compose multiple msgs on lzReceive.\n // For default OFT implementation there is only 1 compose msg per lzReceive, thus its always 0.\n endpoint.sendCompose(toAddress, _guid, 0 /* the index of the composed message*/, composeMsg);\n }\n\n emit OFTReceived(_guid, _origin.srcEid, toAddress, amountReceivedLD);\n }\n\n /**\n * @dev Internal function to handle the OAppPreCrimeSimulator simulated receive.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The LayerZero message.\n * @param _executor The address of the off-chain executor.\n * @param _extraData Arbitrary data passed by the msg executor.\n *\n * @dev Enables the preCrime simulator to mock sending lzReceive() messages,\n * routes the msg down from the OAppPreCrimeSimulator, and back up to the OAppReceiver.\n */\n function _lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual override {\n _lzReceive(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Check if the peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint ID to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n *\n * @dev Enables OAppPreCrimeSimulator to check whether a potential Inbound Packet is from a trusted source.\n */\n function isPeer(uint32 _eid, bytes32 _peer) public view virtual override returns (bool) {\n return peers[_eid] == _peer;\n }\n\n /**\n * @dev Internal function to remove dust from the given local decimal amount.\n * @param _amountLD The amount in local decimals.\n * @return amountLD The amount after removing dust.\n *\n * @dev Prevents the loss of dust when moving amounts between chains with different decimals.\n * @dev eg. uint(123) with a conversion rate of 100 becomes uint(100).\n */\n function _removeDust(uint256 _amountLD) internal view virtual returns (uint256 amountLD) {\n return (_amountLD / decimalConversionRate) * decimalConversionRate;\n }\n\n /**\n * @dev Internal function to convert an amount from shared decimals into local decimals.\n * @param _amountSD The amount in shared decimals.\n * @return amountLD The amount in local decimals.\n */\n function _toLD(uint64 _amountSD) internal view virtual returns (uint256 amountLD) {\n return _amountSD * decimalConversionRate;\n }\n\n /**\n * @dev Internal function to convert an amount from local decimals into shared decimals.\n * @param _amountLD The amount in local decimals.\n * @return amountSD The amount in shared decimals.\n */\n function _toSD(uint256 _amountLD) internal view virtual returns (uint64 amountSD) {\n return uint64(_amountLD / decimalConversionRate);\n }\n\n /**\n * @dev Internal function to mock the amount mutation from a OFT debit() operation.\n * @param _amountLD The amount to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @dev _dstEid The destination endpoint ID.\n * @return amountSentLD The amount sent, in local decimals.\n * @return amountReceivedLD The amount to be received on the remote chain, in local decimals.\n *\n * @dev This is where things like fees would be calculated and deducted from the amount to be received on the remote.\n */\n function _debitView(\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 /*_dstEid*/\n ) internal view virtual returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n // @dev Remove the dust so nothing is lost on the conversion between chains with different decimals for the token.\n amountSentLD = _removeDust(_amountLD);\n // @dev The amount to send is the same as amount received in the default implementation.\n amountReceivedLD = amountSentLD;\n\n // @dev Check for slippage.\n if (amountReceivedLD < _minAmountLD) {\n revert SlippageExceeded(amountReceivedLD, _minAmountLD);\n }\n }\n\n /**\n * @dev Internal function to perform a debit operation.\n * @param _from The address to debit.\n * @param _amountLD The amount to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination endpoint ID.\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev Defined here but are intended to be overriden depending on the OFT implementation.\n * @dev Depending on OFT implementation the _amountLD could differ from the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual returns (uint256 amountSentLD, uint256 amountReceivedLD);\n\n /**\n * @dev Internal function to perform a credit operation.\n * @param _to The address to credit.\n * @param _amountLD The amount to credit in local decimals.\n * @param _srcEid The source endpoint ID.\n * @return amountReceivedLD The amount ACTUALLY received in local decimals.\n *\n * @dev Defined here but are intended to be overriden depending on the OFT implementation.\n * @dev Depending on OFT implementation the _amountLD could differ from the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 _srcEid\n ) internal virtual returns (uint256 amountReceivedLD);\n}\n" + }, + "@openzeppelin/contracts/access/AccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/AccessControl.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\nimport \"../utils/Context.sol\";\nimport \"../utils/Strings.sol\";\nimport \"../utils/introspection/ERC165.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\n * members except through off-chain means by accessing the contract event logs. Some\n * applications may benefit from on-chain enumerability, for those cases see\n * {AccessControlEnumerable}.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```\n * function foo() public {\n * require(hasRole(MY_ROLE, msg.sender));\n * ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it.\n */\nabstract contract AccessControl is Context, IAccessControl, ERC165 {\n struct RoleData {\n mapping(address => bool) members;\n bytes32 adminRole;\n }\n\n mapping(bytes32 => RoleData) private _roles;\n\n bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n /**\n * @dev Modifier that checks that an account has a specific role. Reverts\n * with a standardized message including the required role.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n *\n * _Available since v4.1._\n */\n modifier onlyRole(bytes32 role) {\n _checkRole(role, _msgSender());\n _;\n }\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) public view override returns (bool) {\n return _roles[role].members[account];\n }\n\n /**\n * @dev Revert with a standard message if `account` is missing `role`.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n */\n function _checkRole(bytes32 role, address account) internal view {\n if (!hasRole(role, account)) {\n revert(\n string(\n abi.encodePacked(\n \"AccessControl: account \",\n Strings.toHexString(uint160(account), 20),\n \" is missing role \",\n Strings.toHexString(uint256(role), 32)\n )\n )\n );\n }\n }\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) public view override returns (bytes32) {\n return _roles[role].adminRole;\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _grantRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _revokeRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been revoked `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) public virtual override {\n require(account == _msgSender(), \"AccessControl: can only renounce roles for self\");\n\n _revokeRole(role, account);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event. Note that unlike {grantRole}, this function doesn't perform any\n * checks on the calling account.\n *\n * [WARNING]\n * ====\n * This function should only be called from the constructor when setting\n * up the initial roles for the system.\n *\n * Using this function in any other way is effectively circumventing the admin\n * system imposed by {AccessControl}.\n * ====\n *\n * NOTE: This function is deprecated in favor of {_grantRole}.\n */\n function _setupRole(bytes32 role, address account) internal virtual {\n _grantRole(role, account);\n }\n\n /**\n * @dev Sets `adminRole` as ``role``'s admin role.\n *\n * Emits a {RoleAdminChanged} event.\n */\n function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n bytes32 previousAdminRole = getRoleAdmin(role);\n _roles[role].adminRole = adminRole;\n emit RoleAdminChanged(role, previousAdminRole, adminRole);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * Internal function without access restriction.\n */\n function _grantRole(bytes32 role, address account) internal virtual {\n if (!hasRole(role, account)) {\n _roles[role].members[account] = true;\n emit RoleGranted(role, account, _msgSender());\n }\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * Internal function without access restriction.\n */\n function _revokeRole(bytes32 role, address account) internal virtual {\n if (hasRole(role, account)) {\n _roles[role].members[account] = false;\n emit RoleRevoked(role, account, _msgSender());\n }\n }\n}\n" + }, + "@openzeppelin/contracts/access/AccessControlEnumerable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/AccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControlEnumerable.sol\";\nimport \"./AccessControl.sol\";\nimport \"../utils/structs/EnumerableSet.sol\";\n\n/**\n * @dev Extension of {AccessControl} that allows enumerating the members of each role.\n */\nabstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {\n using EnumerableSet for EnumerableSet.AddressSet;\n\n mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) public view override returns (address) {\n return _roleMembers[role].at(index);\n }\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) public view override returns (uint256) {\n return _roleMembers[role].length();\n }\n\n /**\n * @dev Overload {_grantRole} to track enumerable memberships\n */\n function _grantRole(bytes32 role, address account) internal virtual override {\n super._grantRole(role, account);\n _roleMembers[role].add(account);\n }\n\n /**\n * @dev Overload {_revokeRole} to track enumerable memberships\n */\n function _revokeRole(bytes32 role, address account) internal virtual override {\n super._revokeRole(role, account);\n _roleMembers[role].remove(account);\n }\n}\n" + }, + "@openzeppelin/contracts/access/IAccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n /**\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n *\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n * {RoleAdminChanged} not being emitted signaling this.\n *\n * _Available since v3.1._\n */\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n /**\n * @dev Emitted when `account` is granted `role`.\n *\n * `sender` is the account that originated the contract call, an admin role\n * bearer except when using {AccessControl-_setupRole}.\n */\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Emitted when `account` is revoked `role`.\n *\n * `sender` is the account that originated the contract call:\n * - if using `revokeRole`, it is the admin role bearer\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\n */\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) external view returns (bool);\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been granted `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) external;\n}\n" + }, + "@openzeppelin/contracts/access/IAccessControlEnumerable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\n\n/**\n * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.\n */\ninterface IAccessControlEnumerable is IAccessControl {\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) external view returns (address);\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) external view returns (uint256);\n}\n" + }, + "@openzeppelin/contracts/access/Ownable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n" + }, + "@openzeppelin/contracts/security/Pausable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/ERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/ERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"./extensions/IERC20Metadata.sol\";\nimport \"../../utils/Context.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20, IERC20Metadata {\n mapping(address => uint256) private _balances;\n\n mapping(address => mapping(address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n\n /**\n * @dev Sets the values for {name} and {symbol}.\n *\n * The default value of {decimals} is 18. To select a different value for\n * {decimals} you should overload it.\n *\n * All two of these values are immutable: they can only be set once during\n * construction.\n */\n constructor(string memory name_, string memory symbol_) {\n _name = name_;\n _symbol = symbol_;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless this function is\n * overridden;\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `recipient` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n _approve(_msgSender(), spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * Requirements:\n *\n * - `sender` and `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n * - the caller must have allowance for ``sender``'s tokens of at least\n * `amount`.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public virtual override returns (bool) {\n _transfer(sender, recipient, amount);\n\n uint256 currentAllowance = _allowances[sender][_msgSender()];\n require(currentAllowance >= amount, \"ERC20: transfer amount exceeds allowance\");\n unchecked {\n _approve(sender, _msgSender(), currentAllowance - amount);\n }\n\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n uint256 currentAllowance = _allowances[_msgSender()][spender];\n require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n unchecked {\n _approve(_msgSender(), spender, currentAllowance - subtractedValue);\n }\n\n return true;\n }\n\n /**\n * @dev Moves `amount` of tokens from `sender` to `recipient`.\n *\n * This internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `sender` cannot be the zero address.\n * - `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n */\n function _transfer(\n address sender,\n address recipient,\n uint256 amount\n ) internal virtual {\n require(sender != address(0), \"ERC20: transfer from the zero address\");\n require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(sender, recipient, amount);\n\n uint256 senderBalance = _balances[sender];\n require(senderBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n unchecked {\n _balances[sender] = senderBalance - amount;\n }\n _balances[recipient] += amount;\n\n emit Transfer(sender, recipient, amount);\n\n _afterTokenTransfer(sender, recipient, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply += amount;\n _balances[account] += amount;\n emit Transfer(address(0), account, amount);\n\n _afterTokenTransfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n uint256 accountBalance = _balances[account];\n require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n unchecked {\n _balances[account] = accountBalance - amount;\n }\n _totalSupply -= amount;\n\n emit Transfer(account, address(0), amount);\n\n _afterTokenTransfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n\n /**\n * @dev Hook that is called after any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * has been transferred to `to`.\n * - when `from` is zero, `amount` tokens have been minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _afterTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC721 token receiver interface\n * @dev Interface for any contract that wants to support safeTransfers\n * from ERC721 asset contracts.\n */\ninterface IERC721Receiver {\n /**\n * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}\n * by `operator` from `from`, this function is called.\n *\n * It must return its Solidity selector to confirm the token transfer.\n * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.\n *\n * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.\n */\n function onERC721Received(\n address operator,\n address from,\n uint256 tokenId,\n bytes calldata data\n ) external returns (bytes4);\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/ERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC165.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n *\n * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.\n */\nabstract contract ERC165 is IERC165 {\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IERC165).interfaceId;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/IERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeMath.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)\n\npragma solidity ^0.8.0;\n\n// CAUTION\n// This version of SafeMath should only be used with Solidity 0.8 or later,\n// because it relies on the compiler's built in overflow checks.\n\n/**\n * @dev Wrappers over Solidity's arithmetic operations.\n *\n * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler\n * now has built in overflow checking.\n */\nlibrary SafeMath {\n /**\n * @dev Returns the addition of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n uint256 c = a + b;\n if (c < a) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the substraction of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b > a) return (false, 0);\n return (true, a - b);\n }\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n // benefit is lost if 'b' is also tested.\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n if (a == 0) return (true, 0);\n uint256 c = a * b;\n if (c / a != b) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the division of two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a / b);\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a % b);\n }\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `+` operator.\n *\n * Requirements:\n *\n * - Addition cannot overflow.\n */\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\n return a + b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting on\n * overflow (when the result is negative).\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\n return a - b;\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `*` operator.\n *\n * Requirements:\n *\n * - Multiplication cannot overflow.\n */\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\n return a * b;\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator.\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\n return a / b;\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting when dividing by zero.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\n return a % b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\n * overflow (when the result is negative).\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {trySub}.\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b <= a, errorMessage);\n return a - b;\n }\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting with custom message on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator. Note: this function uses a\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\n * uses an invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a / b;\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting with custom message when dividing by zero.\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {tryMod}.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a % b;\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Strings.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n bytes16 private constant _HEX_SYMBOLS = \"0123456789abcdef\";\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n // Inspired by OraclizeAPI's implementation - MIT licence\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\n\n if (value == 0) {\n return \"0\";\n }\n uint256 temp = value;\n uint256 digits;\n while (temp != 0) {\n digits++;\n temp /= 10;\n }\n bytes memory buffer = new bytes(digits);\n while (value != 0) {\n digits -= 1;\n buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\n value /= 10;\n }\n return string(buffer);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n if (value == 0) {\n return \"0x00\";\n }\n uint256 temp = value;\n uint256 length = 0;\n while (temp != 0) {\n length++;\n temp >>= 8;\n }\n return toHexString(value, length);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _HEX_SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/structs/EnumerableSet.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 => uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n" + }, + "contracts/automation/AbstractCCIPBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IRouterClient } from \"@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol\";\nimport { Client } from \"@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol\";\n\nabstract contract AbstractCCIPBridgeHelperModule is AbstractSafeModule {\n /**\n * @notice Bridges a token from the source chain to the destination chain using CCIP\n * @param ccipRouter The CCIP router contract\n * @param destinationChainSelector The selector for the destination chain\n * @param token The token to bridge\n * @param amount The amount of token to bridge\n */\n function _bridgeTokenWithCCIP(\n IRouterClient ccipRouter,\n uint64 destinationChainSelector,\n IERC20 token,\n uint256 amount\n ) internal {\n bool success;\n\n // Approve CCIP Router to move the token\n success = safeContract.execTransactionFromModule(\n address(token),\n 0, // Value\n abi.encodeWithSelector(token.approve.selector, ccipRouter, amount),\n 0 // Call\n );\n require(success, \"Failed to approve token\");\n\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(token),\n amount: amount\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory ccipMessage = Client.EVM2AnyMessage({\n receiver: abi.encode(address(safeContract)), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n // Get CCIP fee\n uint256 ccipFee = ccipRouter.getFee(\n destinationChainSelector,\n ccipMessage\n );\n\n // Send CCIP message\n success = safeContract.execTransactionFromModule(\n address(ccipRouter),\n ccipFee, // Value\n abi.encodeWithSelector(\n ccipRouter.ccipSend.selector,\n destinationChainSelector,\n ccipMessage\n ),\n 0 // Call\n );\n require(success, \"Failed to send CCIP message\");\n }\n}\n" + }, + "contracts/automation/AbstractLZBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\n\nimport { IOFT, SendParam } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\nimport { MessagingFee } from \"@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol\";\nimport { OptionsBuilder } from \"@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nabstract contract AbstractLZBridgeHelperModule is AbstractSafeModule {\n using OptionsBuilder for bytes;\n\n /**\n * @dev Bridges token using LayerZero.\n * @param lzEndpointId LayerZero endpoint id.\n * @param token Token to bridge.\n * @param lzAdapter LZ Adapter to use.\n * @param amount Amount of token to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n * @param isNativeToken Whether the token is native token.\n */\n function _bridgeTokenWithLz(\n uint32 lzEndpointId,\n IERC20 token,\n IOFT lzAdapter,\n uint256 amount,\n uint256 slippageBps,\n bool isNativeToken\n ) internal {\n bool success;\n\n if (!isNativeToken) {\n // Approve LZ Adapter to move the token\n success = safeContract.execTransactionFromModule(\n address(token),\n 0, // Value\n abi.encodeWithSelector(\n token.approve.selector,\n address(lzAdapter),\n amount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve token\");\n }\n\n // Calculate minimum amount to receive\n uint256 minAmount = (amount * (10000 - slippageBps)) / 10000;\n\n // Hardcoded gaslimit of 400k\n bytes memory options = OptionsBuilder\n .newOptions()\n .addExecutorLzReceiveOption(400000, 0);\n\n // Build send param\n SendParam memory sendParam = SendParam({\n dstEid: lzEndpointId,\n to: bytes32(uint256(uint160(address(safeContract)))),\n amountLD: amount,\n minAmountLD: minAmount,\n extraOptions: options,\n composeMsg: bytes(\"\"),\n oftCmd: bytes(\"\")\n });\n\n // Compute fees\n MessagingFee memory msgFee = lzAdapter.quoteSend(sendParam, false);\n\n uint256 value = isNativeToken\n ? amount + msgFee.nativeFee\n : msgFee.nativeFee;\n\n // Execute transaction\n success = safeContract.execTransactionFromModule(\n address(lzAdapter),\n value,\n abi.encodeWithSelector(\n lzAdapter.send.selector,\n sendParam,\n msgFee,\n address(safeContract)\n ),\n 0\n );\n require(success, \"Failed to bridge token\");\n }\n}\n" + }, + "contracts/automation/AbstractSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { ISafe } from \"../interfaces/ISafe.sol\";\n\nabstract contract AbstractSafeModule is AccessControlEnumerable {\n ISafe public immutable safeContract;\n\n bytes32 public constant OPERATOR_ROLE = keccak256(\"OPERATOR_ROLE\");\n\n modifier onlySafe() {\n require(\n msg.sender == address(safeContract),\n \"Caller is not the safe contract\"\n );\n _;\n }\n\n modifier onlyOperator() {\n require(\n hasRole(OPERATOR_ROLE, msg.sender),\n \"Caller is not an operator\"\n );\n _;\n }\n\n constructor(address _safeContract) {\n safeContract = ISafe(_safeContract);\n _grantRole(DEFAULT_ADMIN_ROLE, address(safeContract));\n _grantRole(OPERATOR_ROLE, address(safeContract));\n }\n\n /**\n * @dev Helps recovering any tokens accidentally sent to this module.\n * @param token Token to transfer. 0x0 to transfer Native token.\n * @param amount Amount to transfer. 0 to transfer all balance.\n */\n function transferTokens(address token, uint256 amount) external onlySafe {\n if (address(token) == address(0)) {\n // Move ETH\n amount = amount > 0 ? amount : address(this).balance;\n payable(address(safeContract)).transfer(amount);\n return;\n }\n\n // Move all balance if amount set to 0\n amount = amount > 0 ? amount : IERC20(token).balanceOf(address(this));\n\n // Transfer to Safe contract\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(token).transfer(address(safeContract), amount);\n }\n\n receive() external payable {\n // Accept ETH to pay for bridge fees\n }\n}\n" + }, + "contracts/automation/BaseBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// solhint-disable-next-line max-line-length\nimport { AbstractCCIPBridgeHelperModule, AbstractSafeModule, IRouterClient } from \"./AbstractCCIPBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { BridgedWOETHStrategy } from \"../strategies/BridgedWOETHStrategy.sol\";\n\ncontract BaseBridgeHelperModule is\n AccessControlEnumerable,\n AbstractCCIPBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0x98a0CbeF61bD2D21435f433bE4CD42B56B38CC93);\n IWETH9 public constant weth =\n IWETH9(0x4200000000000000000000000000000000000006);\n IERC20 public constant oethb =\n IERC20(0xDBFeFD2e8460a6Ee4955A68582F85708BAEA60A3);\n IERC4626 public constant bridgedWOETH =\n IERC4626(0xD8724322f44E5c58D7A815F542036fb17DbbF839);\n\n BridgedWOETHStrategy public constant bridgedWOETHStrategy =\n BridgedWOETHStrategy(0x80c864704DD06C3693ed5179190786EE38ACf835);\n\n IRouterClient public constant CCIP_ROUTER =\n IRouterClient(0x881e3A65B4d4a04dD529061dd0071cf975F58bCD);\n\n uint64 public constant CCIP_ETHEREUM_CHAIN_SELECTOR = 5009297550715157269;\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Ethereum.\n * @param woethAmount Amount of wOETH to bridge.\n */\n function bridgeWOETHToEthereum(uint256 woethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_ETHEREUM_CHAIN_SELECTOR,\n IERC20(address(bridgedWOETH)),\n woethAmount\n );\n }\n\n /**\n * @dev Bridges WETH to Ethereum.\n * @param wethAmount Amount of WETH to bridge.\n */\n function bridgeWETHToEthereum(uint256 wethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_ETHEREUM_CHAIN_SELECTOR,\n IERC20(address(weth)),\n wethAmount\n );\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem the wOETH for WETH using the Vault.\n * @return Amount of WETH received.\n */\n function depositWOETH(uint256 woethAmount, bool redeemWithVault)\n external\n onlyOperator\n returns (uint256)\n {\n return _depositWOETH(woethAmount, redeemWithVault);\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy and bridges it to Ethereum.\n * @param woethAmount Amount of wOETH to deposit.\n * @return Amount of WETH received.\n */\n function depositWOETHAndBridgeWETH(uint256 woethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n uint256 wethAmount = _depositWOETH(woethAmount, true);\n bridgeWETHToEthereum(wethAmount);\n return wethAmount;\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem the wOETH for WETH using the Vault.\n * @return Amount of WETH received.\n */\n function _depositWOETH(uint256 woethAmount, bool redeemWithVault)\n internal\n returns (uint256)\n {\n // Update oracle price\n bridgedWOETHStrategy.updateWOETHOraclePrice();\n\n // Rebase to account for any yields from price update\n vault.rebase();\n\n uint256 oethbAmount = oethb.balanceOf(address(safeContract));\n\n // Approve bridgedWOETH strategy to move wOETH\n bool success = safeContract.execTransactionFromModule(\n address(bridgedWOETH),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETH.approve.selector,\n address(bridgedWOETHStrategy),\n woethAmount\n ),\n 0 // Call\n );\n\n // Deposit to bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.depositBridgedWOETH.selector,\n woethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to deposit bridged WOETH\");\n\n oethbAmount = oethb.balanceOf(address(safeContract)) - oethbAmount;\n\n // Rebase to account for any yields from price update\n // and backing asset change from deposit\n vault.rebase();\n\n if (!redeemWithVault) {\n return oethbAmount;\n }\n\n // Redeem for WETH using Vault\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethbAmount,\n oethbAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETHb\");\n\n return oethbAmount;\n }\n\n /**\n * @dev Deposits WETH into the Vault and redeems wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to deposit.\n * @return Amount of wOETH received.\n */\n function depositWETHAndRedeemWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _withdrawWOETH(wethAmount);\n }\n\n function depositWETHAndBridgeWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n uint256 woethAmount = _withdrawWOETH(wethAmount);\n bridgeWOETHToEthereum(woethAmount);\n return woethAmount;\n }\n\n /**\n * @dev Withdraws wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to use to withdraw.\n * @return Amount of wOETH received.\n */\n function _withdrawWOETH(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETHb with WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETHb\");\n\n // Approve bridgedWOETH strategy to move OETHb\n success = safeContract.execTransactionFromModule(\n address(oethb),\n 0, // Value\n abi.encodeWithSelector(\n oethb.approve.selector,\n address(bridgedWOETHStrategy),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETHb\");\n\n uint256 woethAmount = bridgedWOETH.balanceOf(address(safeContract));\n\n // Withdraw from bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.withdrawBridgedWOETH.selector,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to withdraw bridged WOETH\");\n\n woethAmount =\n bridgedWOETH.balanceOf(address(safeContract)) -\n woethAmount;\n\n return woethAmount;\n }\n}\n" + }, + "contracts/automation/ClaimBribesSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { ICLGauge } from \"../interfaces/aerodrome/ICLGauge.sol\";\nimport { ICLPool } from \"../interfaces/aerodrome/ICLPool.sol\";\n\nstruct BribePoolInfo {\n address poolAddress;\n address rewardContractAddress;\n address[] rewardTokens;\n}\n\ninterface IAerodromeVoter {\n function claimBribes(\n address[] memory _bribes,\n address[][] memory _tokens,\n uint256 _tokenId\n ) external;\n}\n\ninterface IVeNFT {\n function ownerOf(uint256 tokenId) external view returns (address);\n\n function ownerToNFTokenIdList(address owner, uint256 index)\n external\n view\n returns (uint256);\n}\n\ninterface ICLRewardContract {\n function rewards(uint256 index) external view returns (address);\n\n function rewardsListLength() external view returns (uint256);\n}\n\ncontract ClaimBribesSafeModule is AbstractSafeModule {\n IAerodromeVoter public immutable voter;\n address public immutable veNFT;\n\n uint256[] nftIds;\n mapping(uint256 => uint256) nftIdIndex;\n\n BribePoolInfo[] bribePools;\n mapping(address => uint256) bribePoolIndex;\n\n event NFTIdAdded(uint256 nftId);\n event NFTIdRemoved(uint256 nftId);\n\n event BribePoolAdded(address bribePool);\n event BribePoolRemoved(address bribePool);\n\n constructor(\n address _safeContract,\n address _voter,\n address _veNFT\n ) AbstractSafeModule(_safeContract) {\n voter = IAerodromeVoter(_voter);\n veNFT = _veNFT;\n }\n\n /**\n * @dev Claim bribes for a range of NFTs\n * @param nftIndexStart The start index of the NFTs\n * @param nftIndexEnd The end index of the NFTs\n * @param silent Doesn't revert if the claim fails when true\n */\n function claimBribes(\n uint256 nftIndexStart,\n uint256 nftIndexEnd,\n bool silent\n ) external onlyOperator {\n if (nftIndexEnd < nftIndexStart) {\n (nftIndexStart, nftIndexEnd) = (nftIndexEnd, nftIndexStart);\n }\n uint256 nftCount = nftIds.length;\n nftIndexEnd = nftCount < nftIndexEnd ? nftCount : nftIndexEnd;\n\n (\n address[] memory rewardContractAddresses,\n address[][] memory rewardTokens\n ) = _getRewardsInfoArray();\n\n for (uint256 i = nftIndexStart; i < nftIndexEnd; i++) {\n uint256 nftId = nftIds[i];\n bool success = safeContract.execTransactionFromModule(\n address(voter),\n 0, // Value\n abi.encodeWithSelector(\n IAerodromeVoter.claimBribes.selector,\n rewardContractAddresses,\n rewardTokens,\n nftId\n ),\n 0 // Call\n );\n\n require(success || silent, \"ClaimBribes failed\");\n }\n }\n\n /**\n * @dev Get the reward contract address and reward tokens for all pools\n * @return rewardContractAddresses The reward contract addresses\n * @return rewardTokens The reward tokens\n */\n function _getRewardsInfoArray()\n internal\n view\n returns (\n address[] memory rewardContractAddresses,\n address[][] memory rewardTokens\n )\n {\n BribePoolInfo[] memory _bribePools = bribePools;\n uint256 bribePoolCount = _bribePools.length;\n rewardContractAddresses = new address[](bribePoolCount);\n rewardTokens = new address[][](bribePoolCount);\n\n for (uint256 i = 0; i < bribePoolCount; i++) {\n rewardContractAddresses[i] = _bribePools[i].rewardContractAddress;\n rewardTokens[i] = _bribePools[i].rewardTokens;\n }\n }\n\n /***************************************\n NFT Management\n ****************************************/\n /**\n * @dev Add NFT IDs to the list\n * @param _nftIds The NFT IDs to add\n */\n function addNFTIds(uint256[] memory _nftIds) external onlyOperator {\n for (uint256 i = 0; i < _nftIds.length; i++) {\n uint256 nftId = _nftIds[i];\n if (nftIdExists(nftId)) {\n // If it already exists, skip\n continue;\n }\n\n // Make sure the NFT is owned by the Safe\n require(\n IVeNFT(veNFT).ownerOf(nftId) == address(safeContract),\n \"NFT not owned by safe\"\n );\n\n nftIdIndex[nftId] = nftIds.length;\n nftIds.push(nftId);\n\n emit NFTIdAdded(nftId);\n }\n }\n\n /**\n * @dev Remove NFT IDs from the list\n * @param _nftIds The NFT IDs to remove\n */\n function removeNFTIds(uint256[] memory _nftIds) external onlyOperator {\n for (uint256 i = 0; i < _nftIds.length; i++) {\n uint256 nftId = _nftIds[i];\n if (!nftIdExists(nftId)) {\n // If it doesn't exist, skip\n continue;\n }\n\n uint256 index = nftIdIndex[nftId];\n uint256 lastNftId = nftIds[nftIds.length - 1];\n nftIds[index] = lastNftId;\n nftIdIndex[lastNftId] = index;\n nftIds.pop();\n\n emit NFTIdRemoved(nftId);\n }\n }\n\n /**\n * @dev Check if a NFT exists on the list\n * @param nftId The NFT ID to check\n * @return true if the NFT ID exists, false otherwise\n */\n function nftIdExists(uint256 nftId) public view returns (bool) {\n uint256 index = nftIdIndex[nftId];\n uint256[] memory _nftIds = nftIds;\n return (index < _nftIds.length) && _nftIds[index] == nftId;\n }\n\n /**\n * @dev Get the length of the nftIds list\n * @return The length of the nftIds list\n */\n function getNFTIdsLength() external view returns (uint256) {\n return nftIds.length;\n }\n\n /**\n * @dev Get all NFT IDs\n * @return The NFT IDs\n */\n function getAllNFTIds() external view returns (uint256[] memory) {\n return nftIds;\n }\n\n /**\n * @dev Fetch all NFT IDs from the veNFT contract\n * @notice This can revert if Safe owns too many NFTs since tx will be huge.\n * This function is public, anyone can call it, since it only fetches\n * the NFT IDs owned by the Safe. It shouldn't cause us any issues.\n */\n function fetchNFTIds() external {\n // Purge the array\n delete nftIds;\n\n uint256 i = 0;\n while (true) {\n uint256 nftId = IVeNFT(veNFT).ownerToNFTokenIdList(\n address(safeContract),\n i\n );\n if (nftId == 0) {\n break;\n }\n\n nftIdIndex[nftId] = nftIds.length;\n nftIds.push(nftId);\n i++;\n }\n }\n\n /**\n * @dev Remove all NFT IDs from the list\n */\n function removeAllNFTIds() external onlyOperator {\n uint256 length = nftIds.length;\n for (uint256 i = 0; i < length; i++) {\n uint256 nftId = nftIds[i];\n delete nftIdIndex[nftId];\n emit NFTIdRemoved(nftId);\n }\n\n delete nftIds;\n }\n\n /***************************************\n Bribe Pool Management\n ****************************************/\n // @dev Whitelist a pool to claim bribes from\n // @param _poolAddress The address of the pool to whitelist\n function addBribePool(address _poolAddress, bool _isVotingContract)\n external\n onlySafe\n {\n BribePoolInfo memory bribePool;\n\n if (_isVotingContract) {\n bribePool = BribePoolInfo({\n poolAddress: _poolAddress,\n rewardContractAddress: _poolAddress,\n rewardTokens: _getRewardTokenAddresses(_poolAddress)\n });\n } else {\n // Find the gauge address\n address _gaugeAddress = ICLPool(_poolAddress).gauge();\n // And the reward contract address\n address _rewardContractAddress = ICLGauge(_gaugeAddress)\n .feesVotingReward();\n\n bribePool = BribePoolInfo({\n poolAddress: _poolAddress,\n rewardContractAddress: _rewardContractAddress,\n rewardTokens: _getRewardTokenAddresses(_rewardContractAddress)\n });\n }\n\n if (bribePoolExists(_poolAddress)) {\n // Update if it already exists\n bribePools[bribePoolIndex[_poolAddress]] = bribePool;\n } else {\n // If not, Append to the list\n bribePoolIndex[_poolAddress] = bribePools.length;\n bribePools.push(bribePool);\n }\n\n emit BribePoolAdded(_poolAddress);\n }\n\n /**\n * @dev Update the reward token addresses for all pools\n */\n function updateRewardTokenAddresses() external onlyOperator {\n BribePoolInfo[] storage _bribePools = bribePools;\n for (uint256 i = 0; i < _bribePools.length; i++) {\n BribePoolInfo storage bribePool = _bribePools[i];\n bribePool.rewardTokens = _getRewardTokenAddresses(\n bribePool.rewardContractAddress == bribePool.poolAddress\n ? bribePool.poolAddress\n : bribePool.rewardContractAddress\n );\n }\n }\n\n /**\n * @dev Get the reward token addresses for a given reward contract address\n * @param _rewardContractAddress The address of the reward contract\n * @return _rewardTokens The reward token addresses\n */\n function _getRewardTokenAddresses(address _rewardContractAddress)\n internal\n view\n returns (address[] memory)\n {\n address[] memory _rewardTokens = new address[](\n ICLRewardContract(_rewardContractAddress).rewardsListLength()\n );\n for (uint256 i = 0; i < _rewardTokens.length; i++) {\n _rewardTokens[i] = ICLRewardContract(_rewardContractAddress)\n .rewards(i);\n }\n\n return _rewardTokens;\n }\n\n /**\n * @dev Remove a bribe pool from the list\n * @param _poolAddress The address of the pool to remove\n */\n function removeBribePool(address _poolAddress) external onlySafe {\n if (!bribePoolExists(_poolAddress)) {\n // If it doesn't exist, skip\n return;\n }\n\n uint256 index = bribePoolIndex[_poolAddress];\n BribePoolInfo memory lastBribePool = bribePools[bribePools.length - 1];\n bribePools[index] = lastBribePool;\n bribePoolIndex[lastBribePool.poolAddress] = index;\n bribePools.pop();\n\n emit BribePoolRemoved(_poolAddress);\n }\n\n /**\n * @dev Check if a bribe pool exists\n * @param bribePool The address of the pool to check\n * @return true if the pool exists, false otherwise\n */\n function bribePoolExists(address bribePool) public view returns (bool) {\n BribePoolInfo[] memory _bribePools = bribePools;\n uint256 poolIndex = bribePoolIndex[bribePool];\n return\n poolIndex < _bribePools.length &&\n _bribePools[poolIndex].poolAddress == bribePool;\n }\n\n /**\n * @dev Get the length of the bribe pools list\n * @return The length of the bribe pools list\n */\n function getBribePoolsLength() external view returns (uint256) {\n return bribePools.length;\n }\n}\n" + }, + "contracts/automation/ClaimStrategyRewardsSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\n\nimport { ISafe } from \"../interfaces/ISafe.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract ClaimStrategyRewardsSafeModule is AbstractSafeModule {\n using SafeERC20 for IERC20;\n\n mapping(address => bool) public isStrategyWhitelisted;\n address[] public strategies;\n\n event StrategyAdded(address strategy);\n event StrategyRemoved(address strategy);\n\n event ClaimRewardsFailed(address strategy);\n\n constructor(\n address _safeAddress,\n address operator,\n address[] memory _strategies\n ) AbstractSafeModule(_safeAddress) {\n _grantRole(OPERATOR_ROLE, operator);\n\n // Whitelist all strategies\n for (uint256 i = 0; i < _strategies.length; i++) {\n _addStrategy(_strategies[i]);\n }\n }\n\n /**\n * @dev Claim rewards from all whitelisted strategies\n * @param silent Doesn't revert on error if set to true\n */\n function claimRewards(bool silent) external onlyRole(OPERATOR_ROLE) {\n uint256 strategiesLength = strategies.length;\n for (uint256 i = 0; i < strategiesLength; i++) {\n address strategy = strategies[i];\n\n // Execute `collectRewardTokens` for all strategies\n bool success = safeContract.execTransactionFromModule(\n strategy, // To\n 0, // Value\n abi.encodeWithSelector(IStrategy.collectRewardTokens.selector),\n 0 // Call\n );\n\n if (!success) {\n emit ClaimRewardsFailed(strategy);\n }\n\n require(success || silent, \"Failed to claim rewards\");\n }\n }\n\n /**\n * @dev Add a strategy to the whitelist\n * @param _strategy The address of the strategy to add\n */\n function addStrategy(address _strategy)\n external\n onlyRole(DEFAULT_ADMIN_ROLE)\n {\n _addStrategy(_strategy);\n }\n\n function _addStrategy(address _strategy) internal {\n require(\n !isStrategyWhitelisted[_strategy],\n \"Strategy already whitelisted\"\n );\n isStrategyWhitelisted[_strategy] = true;\n strategies.push(_strategy);\n emit StrategyAdded(_strategy);\n }\n\n /**\n * @dev Remove a strategy from the whitelist\n * @param _strategy The address of the strategy to remove\n */\n function removeStrategy(address _strategy)\n external\n onlyRole(DEFAULT_ADMIN_ROLE)\n {\n require(isStrategyWhitelisted[_strategy], \"Strategy not whitelisted\");\n isStrategyWhitelisted[_strategy] = false;\n\n for (uint256 i = 0; i < strategies.length; i++) {\n if (strategies[i] == _strategy) {\n strategies[i] = strategies[strategies.length - 1];\n strategies.pop();\n break;\n }\n }\n\n emit StrategyRemoved(_strategy);\n }\n}\n" + }, + "contracts/automation/CollectXOGNRewardsModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IXOGN {\n function collectRewards() external;\n}\n\ncontract CollectXOGNRewardsModule is AbstractSafeModule {\n IXOGN public constant xogn =\n IXOGN(0x63898b3b6Ef3d39332082178656E9862bee45C57);\n address public constant rewardsSource =\n 0x7609c88E5880e934dd3A75bCFef44E31b1Badb8b;\n IERC20 public constant ogn =\n IERC20(0x8207c1FfC5B6804F6024322CcF34F29c3541Ae26);\n\n constructor(address _safeContract, address operator)\n AbstractSafeModule(_safeContract)\n {\n _grantRole(OPERATOR_ROLE, operator);\n }\n\n function collectRewards() external onlyOperator {\n uint256 balance = ogn.balanceOf(address(safeContract));\n\n bool success = safeContract.execTransactionFromModule(\n address(xogn),\n 0, // Value\n abi.encodeWithSelector(IXOGN.collectRewards.selector),\n 0 // Call\n );\n\n require(success, \"Failed to collect rewards\");\n\n balance = ogn.balanceOf(address(safeContract)) - balance;\n\n if (balance == 0) {\n return;\n }\n\n success = safeContract.execTransactionFromModule(\n address(ogn),\n 0, // Value\n abi.encodeWithSelector(\n IERC20.transfer.selector,\n rewardsSource,\n balance\n ),\n 0 // Call\n );\n\n require(success, \"Failed to collect rewards\");\n }\n}\n" + }, + "contracts/automation/CurvePoolBoosterBribesModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\n\ninterface ICurvePoolBooster {\n function manageTotalRewardAmount(\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external;\n\n function manageNumberOfPeriods(\n uint8 extraNumberOfPeriods,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external;\n}\n\ncontract CurvePoolBoosterBribesModule is AbstractSafeModule {\n address[] public POOLS;\n\n event PoolBoosterAddressAdded(address pool);\n event PoolBoosterAddressRemoved(address pool);\n\n constructor(\n address _safeContract,\n address _operator,\n address[] memory _pools\n ) AbstractSafeModule(_safeContract) {\n _grantRole(OPERATOR_ROLE, _operator);\n _addPoolBoosterAddress(_pools);\n }\n\n function addPoolBoosterAddress(address[] memory pools)\n external\n onlyOperator\n {\n _addPoolBoosterAddress(pools);\n }\n\n function _addPoolBoosterAddress(address[] memory pools) internal {\n for (uint256 i = 0; i < pools.length; i++) {\n POOLS.push(pools[i]);\n emit PoolBoosterAddressAdded(pools[i]);\n }\n }\n\n function removePoolBoosterAddress(address[] calldata pools)\n external\n onlyOperator\n {\n for (uint256 i = 0; i < pools.length; i++) {\n _removePoolBoosterAddress(pools[i]);\n }\n }\n\n function _removePoolBoosterAddress(address pool) internal {\n uint256 length = POOLS.length;\n for (uint256 i = 0; i < length; i++) {\n if (POOLS[i] == pool) {\n POOLS[i] = POOLS[length - 1];\n POOLS.pop();\n emit PoolBoosterAddressRemoved(pool);\n }\n }\n }\n\n function manageBribes() external onlyOperator {\n uint256 length = POOLS.length;\n for (uint256 i = 0; i < length; i++) {\n address poolBoosterAddress = POOLS[i];\n\n // PoolBooster need to have a balance of at least 0.002 ether to operate\n // 0.001 ether are used for the bridge fee\n require(\n poolBoosterAddress.balance > 0.002 ether,\n \"Insufficient balance for bribes\"\n );\n\n require(\n safeContract.execTransactionFromModule(\n poolBoosterAddress,\n 0, // Value\n abi.encodeWithSelector(\n ICurvePoolBooster.manageNumberOfPeriods.selector,\n 1, // extraNumberOfPeriods\n 0.001 ether, // bridgeFee\n 1000000 // additionalGasLimit\n ),\n 0\n ),\n \"Manage number of periods failed\"\n );\n\n require(\n safeContract.execTransactionFromModule(\n poolBoosterAddress,\n 0, // Value\n abi.encodeWithSelector(\n ICurvePoolBooster.manageTotalRewardAmount.selector,\n 0.001 ether, // bridgeFee\n 1000000 // additionalGasLimit\n ),\n 0\n ),\n \"Manage total reward failed\"\n );\n }\n }\n}\n" + }, + "contracts/automation/EthereumBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractLZBridgeHelperModule, AbstractSafeModule } from \"./AbstractLZBridgeHelperModule.sol\";\nimport { AbstractCCIPBridgeHelperModule, IRouterClient } from \"./AbstractCCIPBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IOFT } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract EthereumBridgeHelperModule is\n AccessControlEnumerable,\n AbstractLZBridgeHelperModule,\n AbstractCCIPBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0x39254033945AA2E4809Cc2977E7087BEE48bd7Ab);\n IWETH9 public constant weth =\n IWETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);\n IERC20 public constant oeth =\n IERC20(0x856c4Efb76C1D1AE02e20CEB03A2A6a08b0b8dC3);\n IERC4626 public constant woeth =\n IERC4626(0xDcEe70654261AF21C44c093C300eD3Bb97b78192);\n\n uint32 public constant LZ_PLUME_ENDPOINT_ID = 30370;\n IOFT public constant LZ_WOETH_OMNICHAIN_ADAPTER =\n IOFT(0x7d1bEa5807e6af125826d56ff477745BB89972b8);\n IOFT public constant LZ_ETH_OMNICHAIN_ADAPTER =\n IOFT(0x77b2043768d28E9C9aB44E1aBfC95944bcE57931);\n\n IRouterClient public constant CCIP_ROUTER =\n IRouterClient(0x80226fc0Ee2b096224EeAc085Bb9a8cba1146f7D);\n\n uint64 public constant CCIP_BASE_CHAIN_SELECTOR = 15971525489660198786;\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Plume.\n * @param woethAmount Amount of wOETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWOETHToPlume(uint256 woethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_PLUME_ENDPOINT_ID,\n woeth,\n LZ_WOETH_OMNICHAIN_ADAPTER,\n woethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Bridges wOETH to Base using CCIP.\n * @param woethAmount Amount of wOETH to bridge.\n */\n function bridgeWOETHToBase(uint256 woethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_BASE_CHAIN_SELECTOR,\n woeth,\n woethAmount\n );\n }\n\n /**\n * @dev Bridges wETH to Plume.\n * @param wethAmount Amount of wETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWETHToPlume(uint256 wethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n // Unwrap into ETH\n safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(weth.withdraw.selector, wethAmount),\n 0 // Call\n );\n\n _bridgeTokenWithLz(\n LZ_PLUME_ENDPOINT_ID,\n IERC20(address(weth)),\n LZ_ETH_OMNICHAIN_ADAPTER,\n wethAmount,\n slippageBps,\n true\n );\n }\n\n /**\n * @dev Bridges wETH to Base using CCIP.\n * @param wethAmount Amount of wETH to bridge.\n */\n function bridgeWETHToBase(uint256 wethAmount) public payable onlyOperator {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_BASE_CHAIN_SELECTOR,\n IERC20(address(weth)),\n wethAmount\n );\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH.\n * @param wethAmount Amount of WETH to mint.\n * @param useNativeToken Whether to use native token to mint.\n * @return Amount of wOETH minted.\n */\n function mintAndWrap(uint256 wethAmount, bool useNativeToken)\n external\n onlyOperator\n returns (uint256)\n {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n return _mintAndWrap(wethAmount);\n }\n\n function wrapETH(uint256 ethAmount) public payable onlyOperator {\n // Deposit ETH into WETH\n safeContract.execTransactionFromModule(\n address(weth),\n ethAmount, // Value\n abi.encodeWithSelector(weth.deposit.selector),\n 0 // Call\n );\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH.\n * @param wethAmount Amount of WETH to mint.\n * @return Amount of wOETH minted.\n */\n function _mintAndWrap(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETH\");\n\n // Approve wOETH to move OETH\n success = safeContract.execTransactionFromModule(\n address(oeth),\n 0, // Value\n abi.encodeWithSelector(\n oeth.approve.selector,\n address(woeth),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETH\");\n\n uint256 woethAmount = woeth.balanceOf(address(safeContract));\n\n // Wrap OETH into wOETH\n success = safeContract.execTransactionFromModule(\n address(woeth),\n 0, // Value\n abi.encodeWithSelector(\n woeth.deposit.selector,\n wethAmount,\n address(safeContract)\n ),\n 0 // Call\n );\n require(success, \"Failed to wrap OETH\");\n\n // Compute amount of wOETH minted\n return woeth.balanceOf(address(safeContract)) - woethAmount;\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH, then bridges it to Plume.\n * @param wethAmount Amount of WETH to mint.\n * @param slippageBps Bridge slippage in 10^4 basis points.\n * @param useNativeToken Whether to use native token to mint.\n */\n function mintWrapAndBridgeToPlume(\n uint256 wethAmount,\n uint256 slippageBps,\n bool useNativeToken\n ) external payable onlyOperator {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n uint256 woethAmount = _mintAndWrap(wethAmount);\n bridgeWOETHToPlume(woethAmount, slippageBps);\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH, then bridges it to Base using CCIP.\n * @param wethAmount Amount of WETH to mint.\n * @param useNativeToken Whether to use native token to mint.\n */\n function mintWrapAndBridgeToBase(uint256 wethAmount, bool useNativeToken)\n external\n payable\n onlyOperator\n {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n uint256 woethAmount = _mintAndWrap(wethAmount);\n bridgeWOETHToBase(woethAmount);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH.\n * @param woethAmount Amount of wOETH to unwrap.\n * @return Amount of WETH received.\n */\n function unwrapAndRedeem(uint256 woethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _unwrapAndRedeem(woethAmount);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH.\n * @param woethAmount Amount of wOETH to unwrap.\n * @return Amount of WETH received.\n */\n function _unwrapAndRedeem(uint256 woethAmount) internal returns (uint256) {\n uint256 oethAmount = oeth.balanceOf(address(safeContract));\n\n // Unwrap wOETH\n bool success = safeContract.execTransactionFromModule(\n address(woeth),\n 0, // Value\n abi.encodeWithSelector(\n woeth.redeem.selector,\n woethAmount,\n address(safeContract),\n address(safeContract)\n ),\n 0 // Call\n );\n require(success, \"Failed to unwrap wOETH\");\n\n oethAmount = oeth.balanceOf(address(safeContract)) - oethAmount;\n\n // Redeem OETH using Vault to get WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethAmount,\n oethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETH\");\n\n return oethAmount;\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH, then bridges it to Plume.\n * @param woethAmount Amount of wOETH to unwrap.\n * @param slippageBps Bridge slippage in 10^4 basis points.\n */\n function unwrapRedeemAndBridgeToPlume(\n uint256 woethAmount,\n uint256 slippageBps\n ) external payable onlyOperator {\n uint256 wethAmount = _unwrapAndRedeem(woethAmount);\n // Unwrap into ETH\n safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(weth.withdraw.selector, wethAmount),\n 0 // Call\n );\n\n bridgeWETHToPlume(wethAmount, slippageBps);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH, then bridges it to Base using CCIP.\n * @param woethAmount Amount of wOETH to unwrap.\n */\n function unwrapRedeemAndBridgeToBase(uint256 woethAmount)\n external\n payable\n onlyOperator\n {\n uint256 wethAmount = _unwrapAndRedeem(woethAmount);\n bridgeWETHToBase(wethAmount);\n }\n}\n" + }, + "contracts/automation/PlumeBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { AbstractLZBridgeHelperModule } from \"./AbstractLZBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IOFT } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { BridgedWOETHStrategy } from \"../strategies/BridgedWOETHStrategy.sol\";\n\ncontract PlumeBridgeHelperModule is\n AccessControlEnumerable,\n AbstractLZBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0xc8c8F8bEA5631A8AF26440AF32a55002138cB76a);\n IWETH9 public constant weth =\n IWETH9(0xca59cA09E5602fAe8B629DeE83FfA819741f14be);\n IERC20 public constant oethp =\n IERC20(0xFCbe50DbE43bF7E5C88C6F6Fb9ef432D4165406E);\n IERC4626 public constant bridgedWOETH =\n IERC4626(0xD8724322f44E5c58D7A815F542036fb17DbbF839);\n\n uint32 public constant LZ_ETHEREUM_ENDPOINT_ID = 30101;\n IOFT public constant LZ_WOETH_OMNICHAIN_ADAPTER =\n IOFT(0x592CB6A596E7919930bF49a27AdAeCA7C055e4DB);\n IOFT public constant LZ_ETH_OMNICHAIN_ADAPTER =\n IOFT(0x4683CE822272CD66CEa73F5F1f9f5cBcaEF4F066);\n\n BridgedWOETHStrategy public constant bridgedWOETHStrategy =\n BridgedWOETHStrategy(0x1E3EdD5e019207D6355Ea77F724b1F1BF639B569);\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Ethereum.\n * @param woethAmount Amount of wOETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWOETHToEthereum(uint256 woethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_ETHEREUM_ENDPOINT_ID,\n IERC20(address(bridgedWOETH)),\n LZ_WOETH_OMNICHAIN_ADAPTER,\n woethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Bridges wETH to Ethereum.\n * @param wethAmount Amount of wETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWETHToEthereum(uint256 wethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_ETHEREUM_ENDPOINT_ID,\n IERC20(address(weth)),\n LZ_ETH_OMNICHAIN_ADAPTER,\n wethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem with Vault.\n * @return Amount of OETHp received.\n */\n function depositWOETH(uint256 woethAmount, bool redeemWithVault)\n external\n onlyOperator\n returns (uint256)\n {\n return _depositWOETH(woethAmount, redeemWithVault);\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy and bridges it to Ethereum.\n * @param woethAmount Amount of wOETH to deposit.\n * @param slippageBps Slippage in 10^4 basis points.\n * @return Amount of WETH received.\n */\n function depositWOETHAndBridgeWETH(uint256 woethAmount, uint256 slippageBps)\n external\n payable\n onlyOperator\n returns (uint256)\n {\n uint256 wethAmount = _depositWOETH(woethAmount, true);\n bridgeWETHToEthereum(wethAmount, slippageBps);\n return wethAmount;\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem with Vault.\n * @return Amount of OETHp received.\n */\n function _depositWOETH(uint256 woethAmount, bool redeemWithVault)\n internal\n returns (uint256)\n {\n // Update oracle price\n bridgedWOETHStrategy.updateWOETHOraclePrice();\n\n // Rebase to account for any yields from price update\n vault.rebase();\n\n uint256 oethpAmount = oethp.balanceOf(address(safeContract));\n\n // Approve bridgedWOETH strategy to move wOETH\n bool success = safeContract.execTransactionFromModule(\n address(bridgedWOETH),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETH.approve.selector,\n address(bridgedWOETHStrategy),\n woethAmount\n ),\n 0 // Call\n );\n\n // Deposit to bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.depositBridgedWOETH.selector,\n woethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to deposit bridged WOETH\");\n\n oethpAmount = oethp.balanceOf(address(safeContract)) - oethpAmount;\n\n // Rebase to account for any yields from price update\n // and backing asset change from deposit\n vault.rebase();\n\n if (!redeemWithVault) {\n return oethpAmount;\n }\n\n // Redeem for WETH using Vault\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethpAmount,\n oethpAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETHp\");\n\n return oethpAmount;\n }\n\n /**\n * @dev Deposits wETH into the vault.\n * @param wethAmount Amount of wETH to deposit.\n * @return Amount of OETHp received.\n */\n function depositWETHAndRedeemWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _withdrawWOETH(wethAmount);\n }\n\n /**\n * @dev Deposits wETH into the vault and bridges it to Ethereum.\n * @param wethAmount Amount of wETH to deposit.\n * @param slippageBps Slippage in 10^4 basis points.\n * @return Amount of WOETH received.\n */\n function depositWETHAndBridgeWOETH(uint256 wethAmount, uint256 slippageBps)\n external\n payable\n onlyOperator\n returns (uint256)\n {\n uint256 woethAmount = _withdrawWOETH(wethAmount);\n bridgeWOETHToEthereum(woethAmount, slippageBps);\n return woethAmount;\n }\n\n /**\n * @dev Withdraws wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to use to withdraw.\n * @return Amount of wOETH received.\n */\n function _withdrawWOETH(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETHp with WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETHp\");\n\n // Approve bridgedWOETH strategy to move OETHp\n success = safeContract.execTransactionFromModule(\n address(oethp),\n 0, // Value\n abi.encodeWithSelector(\n oethp.approve.selector,\n address(bridgedWOETHStrategy),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETHp\");\n\n uint256 woethAmount = bridgedWOETH.balanceOf(address(safeContract));\n\n // Withdraw from bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.withdrawBridgedWOETH.selector,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to withdraw bridged WOETH\");\n\n woethAmount =\n bridgedWOETH.balanceOf(address(safeContract)) -\n woethAmount;\n\n return woethAmount;\n }\n}\n" + }, + "contracts/beacon/BeaconConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to request validator consolidation on the beacon chain.\n * @author Origin Protocol Inc\n */\nlibrary BeaconConsolidation {\n /// @notice The address the validator consolidation requests are sent\n /// See https://eips.ethereum.org/EIPS/eip-7251\n address internal constant CONSOLIDATION_REQUEST_ADDRESS =\n 0x0000BBdDc7CE488642fb579F8B00f3a590007251;\n\n function request(bytes calldata source, bytes calldata target)\n internal\n returns (uint256 fee_)\n {\n require(source.length == 48, \"Invalid source byte length\");\n require(target.length == 48, \"Invalid target byte length\");\n\n fee_ = fee();\n\n // Call the Consolidation Request contract with the public keys of the source and target\n // validators packed together.\n // This does not have a function signature, so we use a call\n (bool success, ) = CONSOLIDATION_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(source, target)\n );\n\n require(success, \"Consolidation request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the consolidation request contract\n (bool success, bytes memory result) = CONSOLIDATION_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/beacon/BeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"./BeaconProofsLib.sol\";\nimport { IBeaconProofs } from \"../interfaces/IBeaconProofs.sol\";\n\n/**\n * @title Verifies merkle proofs of beacon chain data.\n * @author Origin Protocol Inc\n */\ncontract BeaconProofs is IBeaconProofs {\n /// @notice Verifies the validator index is for the given validator public key.\n /// Also verify the validator's withdrawal credential points to the withdrawal address.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param proof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata proof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view {\n BeaconProofsLib.verifyValidator(\n beaconBlockRoot,\n pubKeyHash,\n proof,\n validatorIndex,\n withdrawalAddress\n );\n }\n\n /// @notice Verifies a validator's withdrawable epoch to the beacon block root\n /// for a given validator index.\n /// Also verifies the validator's public key for the given validator index.\n /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n /// @param beaconBlockRoot The root of the beacon block\n /// @param validatorIndex The validator index to verify the withdrawable epoch for.\n /// @param withdrawableEpoch The withdrawable epoch to verify in big endian uint64 format\n /// @param withdrawableEpochProof The merkle proof for the validator's withdrawable epoch to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorPubKeyProof The merkle proof for the validator public key in a sub tree of height two.\n /// This is 2 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof,\n bytes calldata validatorPubKeyProof\n ) external view {\n BeaconProofsLib.verifyValidatorWithdrawableEpoch(\n beaconBlockRoot,\n validatorIndex,\n withdrawableEpoch,\n withdrawableEpochProof\n );\n\n // Get the third 32 byte witness from the withdrawable epoch proof\n // 2 * 32 bytes = 64 bytes offset\n bytes32 subTreeRoot = bytes32(withdrawableEpochProof[64:96]);\n\n BeaconProofsLib.verifyValidatorPubKeySubTree(\n subTreeRoot,\n pubKeyHash,\n validatorPubKeyProof\n );\n }\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint64 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view {\n BeaconProofsLib.verifyValidatorWithdrawableEpoch(\n beaconBlockRoot,\n validatorIndex,\n withdrawableEpoch,\n withdrawableEpochProof\n );\n }\n\n /// @notice Verifies the balances container to the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerRoot The merkle root of the the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata balancesContainerProof\n ) external view {\n BeaconProofsLib.verifyBalancesContainer(\n beaconBlockRoot,\n balancesContainerRoot,\n balancesContainerProof\n );\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param balanceProof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalanceGwei) {\n validatorBalanceGwei = BeaconProofsLib.verifyValidatorBalance(\n balancesContainerRoot,\n validatorBalanceLeaf,\n balanceProof,\n validatorIndex\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the pubKey and slot of the first pending deposit to the beacon block root.\n /// BeaconBlock.state.PendingDeposits[0].pubKey\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero would be a good choice.\n /// @param pubKeyHash The hash of the validator public key for the first pending deposit.\n /// Use zero bytes if the deposit queue is empty.\n /// @param firstPendingDepositProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].pubKey when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes32 pubKeyHash,\n bytes calldata firstPendingDepositProof\n ) external view returns (bool isEmptyDepositQueue) {\n isEmptyDepositQueue = BeaconProofsLib.verifyFirstPendingDeposit(\n beaconBlockRoot,\n slot,\n pubKeyHash,\n firstPendingDepositProof\n );\n }\n}\n" + }, + "contracts/beacon/BeaconProofsLib.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Merkle } from \"./Merkle.sol\";\nimport { Endian } from \"./Endian.sol\";\n\n/**\n * @title Library to verify merkle proofs of beacon chain data.\n * @author Origin Protocol Inc\n */\nlibrary BeaconProofsLib {\n // Known generalized indices in the beacon block\n /// @dev BeaconBlock.state.PendingDeposits[0]\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, pending deposits at index 34\n /// Pending deposits container: height 28, first deposit at index 0\n /// ((2 ^ 3 + 3) * 2 ^ 6 + 34) * 2 ^ 28 + 0 = 198105366528\n uint256 internal constant FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX =\n 198105366528;\n /// @dev BeaconBlock.state.PendingDeposits[0].pubkey\n /// Pending Deposit container: height 3, pubkey at index 0\n /// (((2 ^ 3 + 3) * 2 ^ 6 + 34) * 2 ^ 28 + 0) * 2 ^ 3 + 0 = 1584842932224\n uint256 internal constant FIRST_PENDING_DEPOSIT_PUBKEY_GENERALIZED_INDEX =\n 1584842932224;\n /// @dev BeaconBlock.state.validators\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, validators at index 11\n /// (2 ^ 3 + 3) * 2 ^ 6 + 11 = 715\n uint256 internal constant VALIDATORS_CONTAINER_GENERALIZED_INDEX = 715;\n /// @dev BeaconBlock.state.balances\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, balances at index 13\n /// (2 ^ 3 + 3) * 2 ^ 6 + 13 = 716\n uint256 internal constant BALANCES_CONTAINER_GENERALIZED_INDEX = 716;\n\n /// @dev Number of bytes in the proof to the first pending deposit.\n /// 37 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0]\n /// 37 * 32 bytes = 1184 bytes\n uint256 internal constant FIRST_PENDING_DEPOSIT_PROOF_LENGTH = 1184;\n /// @dev Number of bytes in the proof from the pubKey of the first pending deposit to the beacon block root.\n /// 40 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0].pubKey\n /// 40 * 32 bytes = 1280 bytes\n uint256 internal constant FIRST_PENDING_DEPOSIT_PUBKEY_PROOF_LENGTH = 1280;\n /// The slot is at index 4 in the Pending Deposits container.\n /// The sub tree from the right node from the root is a tree of height 2.\n /// The first 32 bytes witness is an empty bytes32 as there are\n /// no items after the slot in the Pending Deposits container.\n /// The second 32 bytes witness is a hash or two empty bytes32.\n bytes internal constant PENDING_DEPOSIT_SLOT_PROOF =\n // solhint-disable-next-line max-line-length\n hex\"0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b\";\n\n /// @dev Merkle height of the Balances container\n /// BeaconBlock.state.balances\n uint256 internal constant BALANCES_HEIGHT = 39;\n /// @dev Merkle height of the Validators container list\n /// BeaconBlock.state.validators\n uint256 internal constant VALIDATORS_LIST_HEIGHT = 41;\n /// @dev Merkle height of the Validator container\n /// BeaconBlock.state.validators[validatorIndex]\n uint256 internal constant VALIDATOR_CONTAINER_HEIGHT = 3;\n\n /// @dev Position of the pubkey field in the Validator container.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;\n /// @dev Position of the withdrawable epoch field in the Validator container.\n /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n uint256 internal constant VALIDATOR_WITHDRAWABLE_EPOCH_INDEX = 7;\n\n /// @notice Verifies the validator index is for the given validator public key.\n /// Also verify the validator's withdrawal credential points to the withdrawal address.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param proof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata proof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.validators[validatorIndex]\n uint256 generalizedIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_LIST_HEIGHT,\n validatorIndex\n );\n // BeaconBlock.state.validators[validatorIndex].pubkey\n generalizedIndex = concatGenIndices(\n generalizedIndex,\n VALIDATOR_CONTAINER_HEIGHT,\n VALIDATOR_PUBKEY_INDEX\n );\n\n // Get the withdrawal address from the first witness in the pubkey merkle proof.\n address withdrawalAddressFromProof;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // The first 32 bytes of the proof is the withdrawal credential so load it into memory.\n calldatacopy(0, proof.offset, 32)\n // Cast the 32 bytes in memory to an address which is the last 20 bytes.\n withdrawalAddressFromProof := mload(0)\n }\n require(\n withdrawalAddressFromProof == withdrawalAddress,\n \"Invalid withdrawal address\"\n );\n\n require(\n // 53 * 32 bytes = 1696 bytes\n proof.length == 1696 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: pubKeyHash,\n index: generalizedIndex\n }),\n \"Invalid validator proof\"\n );\n }\n\n /// @notice Verifies a validator's withdrawable epoch to the beacon block root\n /// for a given validator index.\n /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n /// @param beaconBlockRoot The root of the beacon block\n /// @param validatorIndex The validator index to verify the withdrawable epoch for.\n /// @param withdrawableEpoch The withdrawable epoch to verify in big endian uint64 format\n /// @param proof The merkle proof for the validator's withdrawable epoch to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyValidatorWithdrawableEpoch(\n bytes32 beaconBlockRoot,\n uint64 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata proof\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.validators[validatorIndex]\n uint256 exitEpochGenIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_LIST_HEIGHT,\n validatorIndex\n );\n // BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n exitEpochGenIndex = concatGenIndices(\n exitEpochGenIndex,\n VALIDATOR_CONTAINER_HEIGHT,\n VALIDATOR_WITHDRAWABLE_EPOCH_INDEX\n );\n\n require(\n // 53 * 32 bytes = 1696 bytes\n proof.length == 1696 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: Endian.toLittleEndianUint64(withdrawableEpoch),\n index: exitEpochGenIndex\n }),\n \"Invalid withdrawable proof\"\n );\n }\n\n /// @param subTreeRoot The third 32 byte witness from the withdrawable epoch proof\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param proof The merkle proof for the validator public key in a sub tree of height two.\n /// This is 2 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyValidatorPubKeySubTree(\n bytes32 subTreeRoot,\n bytes32 pubKeyHash,\n bytes calldata proof\n ) internal view {\n // Tree height 2 and pub key is at index 0\n // index = 2 ^ 2 + 0 = 4\n require(\n // 2 * 32 bytes = 64 bytes\n proof.length == 64 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: subTreeRoot,\n leaf: pubKeyHash,\n index: 4\n }),\n \"Invalid pub key proof\"\n );\n }\n\n /// @notice Verifies the balances container to the beacon block root.\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param balancesContainerRoot The merkle root of the the balances container.\n /// @param proof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata proof\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.balances\n require(\n // 9 * 32 bytes = 288 bytes\n proof.length == 288 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: balancesContainerRoot,\n index: BALANCES_CONTAINER_GENERALIZED_INDEX\n }),\n \"Invalid balance container proof\"\n );\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param proof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for.\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index.\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata proof,\n uint64 validatorIndex\n ) internal view returns (uint256 validatorBalanceGwei) {\n require(balancesContainerRoot != bytes32(0), \"Invalid container root\");\n\n // Four balances are stored in each leaf so the validator index is divided by 4\n uint64 balanceIndex = validatorIndex / 4;\n\n // Get the index within the balances container, not the Beacon Block\n // BeaconBlock.state.balances[balanceIndex]\n uint256 generalizedIndex = concatGenIndices(\n 1,\n BALANCES_HEIGHT,\n balanceIndex\n );\n\n validatorBalanceGwei = balanceAtIndex(\n validatorBalanceLeaf,\n validatorIndex\n );\n\n require(\n // 39 * 32 bytes = 1248 bytes\n proof.length == 1248 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: balancesContainerRoot,\n leaf: validatorBalanceLeaf,\n index: generalizedIndex\n }),\n \"Invalid balance proof\"\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the pubKey and slot of the first pending deposit to the beacon block root.\n /// BeaconBlock.state.pendingDeposits[0].pubKey\n /// BeaconBlock.state.pendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero would be a good choice.\n /// @param pubKeyHash The hash of the validator public key for the first pending deposit.\n /// Use zero bytes if the deposit queue is empty.\n /// @param proof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].pubKey when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes32 pubKeyHash,\n bytes calldata proof\n ) internal view returns (bool isEmptyDepositQueue) {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // If the deposit queue is empty\n if (proof.length == FIRST_PENDING_DEPOSIT_PROOF_LENGTH) {\n require(\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: bytes32(0),\n index: FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX\n }),\n \"Invalid empty deposits proof\"\n );\n return true;\n }\n\n // Verify the public key of the first pending deposit\n // BeaconBlock.state.PendingDeposits[0].pubKey\n require(\n proof.length == FIRST_PENDING_DEPOSIT_PUBKEY_PROOF_LENGTH &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: pubKeyHash,\n index: FIRST_PENDING_DEPOSIT_PUBKEY_GENERALIZED_INDEX\n }),\n \"Invalid deposit pub key proof\"\n );\n\n // Now verify the slot of the first pending deposit\n\n // Get the third 32 bytes witness from the first pending deposit pubKey proof\n // 2 * 32 bytes = 64 bytes offset\n bytes32 slotRoot = bytes32(proof[64:96]);\n\n // Sub tree height 2 and slot is at index 0 in the sub tree\n // index = 2 ^ 2 + 0 = 4\n require(\n Merkle.verifyInclusionSha256({\n proof: PENDING_DEPOSIT_SLOT_PROOF,\n root: slotRoot,\n leaf: Endian.toLittleEndianUint64(slot),\n index: 4\n }),\n \"Invalid deposit slot\"\n );\n }\n\n ////////////////////////////////////////////////////\n /// Internal Helper Functions\n ////////////////////////////////////////////////////\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n internal\n pure\n returns (uint256)\n {\n uint256 bitShiftAmount = (validatorIndex % 4) * 64;\n return\n Endian.fromLittleEndianUint64(\n bytes32((uint256(validatorBalanceLeaf) << bitShiftAmount))\n );\n }\n\n /// @notice Concatenates two beacon chain generalized indices into one.\n /// @param genIndex The first generalized index or 1 if calculating for a single container.\n /// @param height The merkle tree height of the second container. eg 39 for balances, 41 for validators.\n /// @param index The index within the second container. eg the validator index.\n /// @return genIndex The concatenated generalized index.\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) internal pure returns (uint256) {\n return (genIndex << height) | index;\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to retrieve beacon block roots.\n * @author Origin Protocol Inc\n */\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the beacon block root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Roots contract to get the parent block root.\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/Endian.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n/**\n * @title Library to handle conversion between little-endian and big-endian formats.\n * @author Origin Protocol Inc\n */\nlibrary Endian {\n /**\n * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64\n * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type\n * @return n The big endian-formatted uint64\n * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits),\n * but it is immediately truncated to a uint64 (i.e. 64 bits)\n * through a right-shift/shr operation.\n */\n function fromLittleEndianUint64(bytes32 lenum)\n internal\n pure\n returns (uint64 n)\n {\n // the number needs to be stored in little-endian encoding (ie in bytes 0-8)\n n = uint64(uint256(lenum >> 192));\n // forgefmt: disable-next-item\n return\n (n >> 56) |\n ((0x00FF000000000000 & n) >> 40) |\n ((0x0000FF0000000000 & n) >> 24) |\n ((0x000000FF00000000 & n) >> 8) |\n ((0x00000000FF000000 & n) << 8) |\n ((0x0000000000FF0000 & n) << 24) |\n ((0x000000000000FF00 & n) << 40) |\n ((0x00000000000000FF & n) << 56);\n }\n\n function toLittleEndianUint64(uint64 benum)\n internal\n pure\n returns (bytes32 n)\n {\n // Convert to little-endian by reversing byte order\n uint64 reversed = (benum >> 56) |\n ((0x00FF000000000000 & benum) >> 40) |\n ((0x0000FF0000000000 & benum) >> 24) |\n ((0x000000FF00000000 & benum) >> 8) |\n ((0x00000000FF000000 & benum) << 8) |\n ((0x0000000000FF0000 & benum) << 24) |\n ((0x000000000000FF00 & benum) << 40) |\n ((0x00000000000000FF & benum) << 56);\n\n // Store the little-endian uint64 in the least significant 64 bits of bytes32\n n = bytes32(uint256(reversed));\n // Shift to most significant bits\n n = n << 192;\n }\n}\n" + }, + "contracts/beacon/Merkle.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev These functions deal with verification of Merkle Tree proofs.\n *\n * The tree and the proofs can be generated using our\n * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].\n * You will find a quickstart guide in the readme.\n *\n * WARNING: You should avoid using leaf values that are 64 bytes long prior to\n * hashing, or use a hash function other than keccak256 for hashing leaves.\n * This is because the concatenation of a sorted pair of internal nodes in\n * the merkle tree could be reinterpreted as a leaf value.\n * OpenZeppelin's JavaScript library generates merkle trees that are safe\n * against this attack out of the box.\n */\nlibrary Merkle {\n error InvalidProofLength();\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function verifyInclusionSha256(\n bytes memory proof,\n bytes32 root,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bool) {\n return processInclusionProofSha256(proof, leaf, index) == root;\n }\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * _Available since v4.4._\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function processInclusionProofSha256(\n bytes memory proof,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bytes32) {\n require(\n proof.length != 0 && proof.length % 32 == 0,\n InvalidProofLength()\n );\n bytes32[1] memory computedHash = [leaf];\n for (uint256 i = 32; i <= proof.length; i += 32) {\n if (index % 2 == 0) {\n // if ith bit of index is 0, then computedHash is a left sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(computedHash))\n mstore(0x20, mload(add(proof, i)))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n }\n } else {\n // if ith bit of index is 1, then computedHash is a right sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(add(proof, i)))\n mstore(0x20, mload(computedHash))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n }\n }\n index = index / 2;\n }\n return computedHash[0];\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to request full or partial withdrawals from validators on the beacon chain.\n * @author Origin Protocol Inc\n */\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n /// @notice Requests a partial withdrawal for a given validator public key and amount.\n /// @param validatorPubKey The public key of the validator to withdraw from\n /// @param amount The amount of ETH to withdraw\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n /// @notice Gets fee for withdrawal requests contract on Beacon chain\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/bridges/OmnichainL2Adapter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.0;\n\nimport { MintBurnOFTAdapter } from \"@layerzerolabs/oft-evm/contracts/MintBurnOFTAdapter.sol\";\nimport { IMintableBurnable } from \"@layerzerolabs/oft-evm/contracts/interfaces/IMintableBurnable.sol\";\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\n\n/// NOTE: It's necessary to inherit from Ownable instead of Governable\n/// because OFTCore uses Ownable to manage the governor.\n/// Ownable uses slot 0 for storing the address, whereas Goveranble\n/// stores it in a computed slot.\n\n/// @notice Omnichain uses a deployed ERC-20 token and safeERC20\n/// to interact with the OFTCore contract.\n/// On L2, we follow the mint and burn mechanism.\n/// The adapter should have minter and burner roles.\n\n/// @title Omnichain L2 Adapter\ncontract OmnichainL2Adapter is MintBurnOFTAdapter {\n constructor(\n address _token,\n address _lzEndpoint,\n address _governor\n )\n MintBurnOFTAdapter(\n _token,\n IMintableBurnable(_token),\n _lzEndpoint,\n _governor\n )\n Ownable()\n {\n _transferOwnership(_governor);\n }\n\n /// @inheritdoc MintBurnOFTAdapter\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n )\n internal\n virtual\n override\n returns (uint256 amountSentLD, uint256 amountReceivedLD)\n {\n (amountSentLD, amountReceivedLD) = _debitView(\n _amountLD,\n _minAmountLD,\n _dstEid\n );\n // Burns tokens from the caller.\n IMintableERC20(address(minterBurner)).burn(_from, amountSentLD);\n }\n\n /// @inheritdoc MintBurnOFTAdapter\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /* _srcEid */\n ) internal virtual override returns (uint256 amountReceivedLD) {\n if (_to == address(0x0)) _to = address(0xdead); // _mint(...) does not support address(0x0)\n // Mints the tokens and transfers to the recipient.\n IMintableERC20(address(minterBurner)).mint(_to, _amountLD);\n // In the case of NON-default OFTAdapter, the amountLD MIGHT not be equal to amountReceivedLD.\n return _amountLD;\n }\n}\n\ninterface IMintableERC20 {\n function mint(address to, uint256 value) external;\n\n function burn(address to, uint256 value) external;\n}\n" + }, + "contracts/bridges/OmnichainMainnetAdapter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.0;\n\nimport { OFTAdapter } from \"@layerzerolabs/oft-evm/contracts/OFTAdapter.sol\";\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\n\n/// NOTE: It's necessary to inherit from Ownable instead of Governable\n/// because OFTCore uses Ownable to manage the governor.\n/// Ownable uses slot 0 for storing the address, whereas Goveranble\n/// stores it in a computed slot.\n\n/// @notice Omnichain uses a deployed ERC-20 token and safeERC20\n/// to interact with the OFTCore contract.\n/// On Ethereum, we follow the lock and unlock mechanism.\n\n/// @title Omnichain Mainnet Adapter\ncontract OmnichainMainnetAdapter is OFTAdapter {\n constructor(\n address _token,\n address _lzEndpoint,\n address _governor\n ) OFTAdapter(_token, _lzEndpoint, _governor) Ownable() {\n _transferOwnership(_governor);\n }\n}\n" + }, + "contracts/buyback/AbstractBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { ICVXLocker } from \"../interfaces/ICVXLocker.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\n\nabstract contract AbstractBuyback is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n event SwapRouterUpdated(address indexed _address);\n\n event RewardsSourceUpdated(address indexed _address);\n event TreasuryManagerUpdated(address indexed _address);\n event CVXShareBpsUpdated(uint256 bps);\n\n // Emitted whenever OUSD/OETH is swapped for OGN/CVX or any other token\n event OTokenBuyback(\n address indexed oToken,\n address indexed swappedFor,\n uint256 swapAmountIn,\n uint256 amountOut\n );\n\n // Address of 1-inch Swap Router\n address public swapRouter;\n\n // slither-disable-next-line constable-states\n address private __deprecated_ousd;\n // slither-disable-next-line constable-states\n address private __deprecated_ogv;\n // slither-disable-next-line constable-states\n address private __deprecated_usdt;\n // slither-disable-next-line constable-states\n address private __deprecated_weth9;\n\n // Address that receives OGN after swaps\n address public rewardsSource;\n\n // Address that receives all other tokens after swaps\n address public treasuryManager;\n\n // slither-disable-next-line constable-states\n uint256 private __deprecated_treasuryBps;\n\n address public immutable oToken;\n address public immutable ogn;\n address public immutable cvx;\n address public immutable cvxLocker;\n\n // Amount of `oToken` balance to use for OGN buyback\n uint256 public balanceForOGN;\n\n // Amount of `oToken` balance to use for CVX buyback\n uint256 public balanceForCVX;\n\n // Percentage of `oToken` balance to be used for CVX\n uint256 public cvxShareBps; // 10000 = 100%\n\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) {\n // Make sure nobody owns the implementation contract\n _setGovernor(address(0));\n\n oToken = _oToken;\n ogn = _ogn;\n cvx = _cvx;\n cvxLocker = _cvxLocker;\n }\n\n /**\n * @param _swapRouter Address of Uniswap V3 Router\n * @param _strategistAddr Address of Strategist multi-sig wallet\n * @param _treasuryManagerAddr Address that receives the treasury's share of OUSD\n * @param _rewardsSource Address of RewardsSource contract\n * @param _cvxShareBps Percentage of balance to use for CVX\n */\n function initialize(\n address _swapRouter,\n address _strategistAddr,\n address _treasuryManagerAddr,\n address _rewardsSource,\n uint256 _cvxShareBps\n ) external onlyGovernor initializer {\n _setStrategistAddr(_strategistAddr);\n\n _setSwapRouter(_swapRouter);\n _setRewardsSource(_rewardsSource);\n\n _setTreasuryManager(_treasuryManagerAddr);\n\n _setCVXShareBps(_cvxShareBps);\n }\n\n /**\n * @dev Set address of Uniswap Universal Router for performing liquidation\n * of platform fee tokens. Setting to 0x0 will pause swaps.\n *\n * @param _router Address of the Uniswap Universal router\n */\n function setSwapRouter(address _router) external onlyGovernor {\n _setSwapRouter(_router);\n }\n\n function _setSwapRouter(address _router) internal {\n address oldRouter = swapRouter;\n swapRouter = _router;\n\n if (oldRouter != address(0)) {\n // Remove allowance of old router, if any\n\n if (IERC20(ogn).allowance(address(this), oldRouter) != 0) {\n // slither-disable-next-line unused-return\n IERC20(ogn).safeApprove(oldRouter, 0);\n }\n\n if (IERC20(cvx).allowance(address(this), oldRouter) != 0) {\n // slither-disable-next-line unused-return\n IERC20(cvx).safeApprove(oldRouter, 0);\n }\n }\n\n emit SwapRouterUpdated(_router);\n }\n\n /**\n * @dev Sets the address that receives the OGN buyback rewards\n * @param _address Address\n */\n function setRewardsSource(address _address) external onlyGovernor {\n _setRewardsSource(_address);\n }\n\n function _setRewardsSource(address _address) internal {\n require(_address != address(0), \"Address not set\");\n rewardsSource = _address;\n emit RewardsSourceUpdated(_address);\n }\n\n /**\n * @dev Sets the address that can receive and manage the funds for Treasury\n * @param _address Address\n */\n function setTreasuryManager(address _address) external onlyGovernor {\n _setTreasuryManager(_address);\n }\n\n function _setTreasuryManager(address _address) internal {\n require(_address != address(0), \"Address not set\");\n treasuryManager = _address;\n emit TreasuryManagerUpdated(_address);\n }\n\n /**\n * @dev Sets the percentage of oToken to use for Flywheel tokens\n * @param _bps BPS, 10000 to 100%\n */\n function setCVXShareBps(uint256 _bps) external onlyGovernor {\n _setCVXShareBps(_bps);\n }\n\n function _setCVXShareBps(uint256 _bps) internal {\n require(_bps <= 10000, \"Invalid bps value\");\n cvxShareBps = _bps;\n emit CVXShareBpsUpdated(_bps);\n }\n\n /**\n * @dev Computes the split of oToken balance that can be\n * used for OGN and CVX buybacks.\n */\n function _updateBuybackSplits()\n internal\n returns (uint256 _balanceForOGN, uint256 _balanceForCVX)\n {\n _balanceForOGN = balanceForOGN;\n _balanceForCVX = balanceForCVX;\n\n uint256 totalBalance = IERC20(oToken).balanceOf(address(this));\n uint256 unsplitBalance = totalBalance - _balanceForOGN - _balanceForCVX;\n\n // Check if all balance is accounted for\n if (unsplitBalance != 0) {\n // If not, split unaccounted balance based on `cvxShareBps`\n uint256 addToCVX = (unsplitBalance * cvxShareBps) / 10000;\n _balanceForCVX = _balanceForCVX + addToCVX;\n _balanceForOGN = _balanceForOGN + unsplitBalance - addToCVX;\n\n // Update storage\n balanceForOGN = _balanceForOGN;\n balanceForCVX = _balanceForCVX;\n }\n }\n\n function updateBuybackSplits() external onlyGovernor {\n // slither-disable-next-line unused-return\n _updateBuybackSplits();\n }\n\n function _swapToken(\n address tokenOut,\n uint256 oTokenAmount,\n uint256 minAmountOut,\n bytes calldata swapData\n ) internal returns (uint256 amountOut) {\n require(oTokenAmount > 0, \"Invalid Swap Amount\");\n require(swapRouter != address(0), \"Swap Router not set\");\n require(minAmountOut > 0, \"Invalid minAmount\");\n\n // Transfer OToken to Swapper for swapping\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(oToken).transfer(swapRouter, oTokenAmount);\n\n // Swap\n amountOut = ISwapper(swapRouter).swap(\n oToken,\n tokenOut,\n oTokenAmount,\n minAmountOut,\n swapData\n );\n\n require(amountOut >= minAmountOut, \"Higher Slippage\");\n\n emit OTokenBuyback(oToken, tokenOut, oTokenAmount, amountOut);\n }\n\n /**\n * @dev Swaps `oTokenAmount` to OGN\n * @param oTokenAmount Amount of OUSD/OETH to swap\n * @param minOGN Minimum OGN to receive for oTokenAmount\n * @param swapData 1inch Swap Data\n */\n function swapForOGN(\n uint256 oTokenAmount,\n uint256 minOGN,\n bytes calldata swapData\n ) external onlyGovernorOrStrategist nonReentrant {\n (uint256 _amountForOGN, ) = _updateBuybackSplits();\n require(_amountForOGN >= oTokenAmount, \"Balance underflow\");\n require(rewardsSource != address(0), \"RewardsSource contract not set\");\n\n unchecked {\n // Subtract the amount to swap from net balance\n balanceForOGN = _amountForOGN - oTokenAmount;\n }\n\n uint256 ognReceived = _swapToken(ogn, oTokenAmount, minOGN, swapData);\n\n // Transfer OGN received to RewardsSource contract\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(ogn).transfer(rewardsSource, ognReceived);\n }\n\n /**\n * @dev Swaps `oTokenAmount` to CVX\n * @param oTokenAmount Amount of OUSD/OETH to swap\n * @param minCVX Minimum CVX to receive for oTokenAmount\n * @param swapData 1inch Swap Data\n */\n function swapForCVX(\n uint256 oTokenAmount,\n uint256 minCVX,\n bytes calldata swapData\n ) external onlyGovernorOrStrategist nonReentrant {\n (, uint256 _amountForCVX) = _updateBuybackSplits();\n require(_amountForCVX >= oTokenAmount, \"Balance underflow\");\n\n unchecked {\n // Subtract the amount to swap from net balance\n balanceForCVX = _amountForCVX - oTokenAmount;\n }\n\n uint256 cvxReceived = _swapToken(cvx, oTokenAmount, minCVX, swapData);\n\n // Lock all CVX\n _lockAllCVX(cvxReceived);\n }\n\n /**\n * @dev Locks all CVX held by the contract on behalf of the Treasury Manager\n */\n function lockAllCVX() external onlyGovernorOrStrategist {\n _lockAllCVX(IERC20(cvx).balanceOf(address(this)));\n }\n\n function _lockAllCVX(uint256 cvxAmount) internal {\n require(\n treasuryManager != address(0),\n \"Treasury manager address not set\"\n );\n\n // Lock all available CVX on behalf of `treasuryManager`\n ICVXLocker(cvxLocker).lock(treasuryManager, cvxAmount, 0);\n }\n\n /**\n * @dev Approve CVX Locker to move CVX held by this contract\n */\n function safeApproveAllTokens() external onlyGovernorOrStrategist {\n IERC20(cvx).safeApprove(cvxLocker, type(uint256).max);\n }\n\n /**\n * @notice Owner function to withdraw a specific amount of a token\n * @param token token to be transferered\n * @param amount amount of the token to be transferred\n */\n function transferToken(address token, uint256 amount)\n external\n onlyGovernor\n nonReentrant\n {\n IERC20(token).safeTransfer(_governor(), amount);\n }\n}\n" + }, + "contracts/buyback/ARMBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract ARMBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/buyback/OETHBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract OETHBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/buyback/OUSDBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract OUSDBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/echidna/Debugger.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary Debugger {\n event Debug(string debugString);\n event Debug(string description, string data);\n event Debug(string prefix, string description, string data);\n event Debug(string description, bytes32 data);\n event Debug(string prefix, string description, bytes32 data);\n event Debug(string description, uint256 data);\n event Debug(string prefix, string description, uint256 data);\n event Debug(string description, int256 data);\n event Debug(string prefix, string description, int256 data);\n event Debug(string description, address data);\n event Debug(string prefix, string description, address data);\n event Debug(string description, bool data);\n event Debug(string prefix, string description, bool data);\n\n function log(string memory debugString) internal {\n emit Debug(debugString);\n }\n\n function log(string memory description, string memory data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n string memory data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, bytes32 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n bytes32 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, uint256 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n uint256 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, int256 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n int256 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, address data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n address data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, bool data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n bool data\n ) internal {\n emit Debug(prefix, description, data);\n }\n}\n" + }, + "contracts/echidna/Echidna.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaTestApproval.sol\";\n\n/**\n * @title Echidna test contract for OUSD\n * @notice Target contract to be tested, containing all mixins\n * @author Rappie\n */\ncontract Echidna is EchidnaTestApproval {\n\n}\n" + }, + "contracts/echidna/EchidnaConfig.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Top-level mixin for configuring the desired fuzzing setup\n * @author Rappie\n */\ncontract EchidnaConfig {\n address internal constant ADDRESS_VAULT = address(0x10000);\n address internal constant ADDRESS_OUTSIDER_USER = address(0x20000);\n\n address internal constant ADDRESS_USER0 = address(0x30000);\n address internal constant ADDRESS_USER1 = address(0x40000);\n\n // Will be set in EchidnaSetup constructor\n address internal ADDRESS_OUTSIDER_CONTRACT;\n address internal ADDRESS_CONTRACT0;\n address internal ADDRESS_CONTRACT1;\n\n // Toggle known issues\n //\n // This can be used to skip tests that are known to fail. This is useful\n // when debugging a specific issue, but should be disabled when running\n // the full test suite.\n //\n // True => skip tests that are known to fail\n // False => run all tests\n //\n bool internal constant TOGGLE_KNOWN_ISSUES = false;\n\n // Toggle known issues within limits\n //\n // Same as TOGGLE_KNOWN_ISSUES, but also skip tests that are known to fail\n // within limits set by the variables below.\n //\n bool internal constant TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS = true;\n\n // Starting balance\n //\n // Gives OUSD a non-zero starting supply, which can be useful to ignore\n // certain edge cases.\n //\n // The starting balance is given to outsider accounts that are not used as\n // accounts while fuzzing.\n //\n bool internal constant TOGGLE_STARTING_BALANCE = true;\n uint256 internal constant STARTING_BALANCE = 1_000_000e18;\n\n // Change supply\n //\n // Set a limit to the amount of change per rebase, which can be useful to\n // ignore certain edge cases.\n //\n // True => limit the amount of change to a percentage of total supply\n // False => no limit\n //\n bool internal constant TOGGLE_CHANGESUPPLY_LIMIT = true;\n uint256 internal constant CHANGESUPPLY_DIVISOR = 10; // 10% of total supply\n\n // Mint limit\n //\n // Set a limit the amount minted per mint, which can be useful to\n // ignore certain edge cases.\n //\n // True => limit the amount of minted tokens\n // False => no limit\n //\n bool internal constant TOGGLE_MINT_LIMIT = true;\n uint256 internal constant MINT_MODULO = 1_000_000_000_000e18;\n\n // Known rounding errors\n uint256 internal constant TRANSFER_ROUNDING_ERROR = 1e18 - 1;\n uint256 internal constant OPT_IN_ROUNDING_ERROR = 1e18 - 1;\n uint256 internal constant MINT_ROUNDING_ERROR = 1e18 - 1;\n\n /**\n * @notice Modifier to skip tests that are known to fail\n * @dev see TOGGLE_KNOWN_ISSUES for more information\n */\n modifier hasKnownIssue() {\n if (TOGGLE_KNOWN_ISSUES) return;\n _;\n }\n\n /**\n * @notice Modifier to skip tests that are known to fail within limits\n * @dev see TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS for more information\n */\n modifier hasKnownIssueWithinLimits() {\n if (TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS) return;\n _;\n }\n\n /**\n * @notice Translate an account ID to an address\n * @param accountId The ID of the account\n * @return account The address of the account\n */\n function getAccount(uint8 accountId)\n internal\n view\n returns (address account)\n {\n accountId = accountId / 64;\n if (accountId == 0) return account = ADDRESS_USER0;\n if (accountId == 1) return account = ADDRESS_USER1;\n if (accountId == 2) return account = ADDRESS_CONTRACT0;\n if (accountId == 3) return account = ADDRESS_CONTRACT1;\n require(false, \"Unknown account ID\");\n }\n}\n" + }, + "contracts/echidna/EchidnaDebug.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport \"./EchidnaHelper.sol\";\nimport \"./Debugger.sol\";\n\nimport \"../token/OUSD.sol\";\n\n/**\n * @title Room for random debugging functions\n * @author Rappie\n */\ncontract EchidnaDebug is EchidnaHelper {\n function debugOUSD() public pure {\n // assert(ousd.balanceOf(ADDRESS_USER0) == 1000);\n // assert(ousd.rebaseState(ADDRESS_USER0) != OUSD.RebaseOptions.OptIn);\n // assert(Address.isContract(ADDRESS_CONTRACT0));\n // Debugger.log(\"nonRebasingSupply\", ousd.nonRebasingSupply());\n // assert(false);\n }\n}\n" + }, + "contracts/echidna/EchidnaHelper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaSetup.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin containing helper functions\n * @author Rappie\n */\ncontract EchidnaHelper is EchidnaSetup {\n /**\n * @notice Mint tokens to an account\n * @param toAcc Account to mint to\n * @param amount Amount to mint\n * @return Amount minted (in case of capped mint with modulo)\n */\n function mint(uint8 toAcc, uint256 amount) public returns (uint256) {\n address to = getAccount(toAcc);\n\n if (TOGGLE_MINT_LIMIT) {\n amount = amount % MINT_MODULO;\n }\n\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(to, amount);\n\n return amount;\n }\n\n /**\n * @notice Burn tokens from an account\n * @param fromAcc Account to burn from\n * @param amount Amount to burn\n */\n function burn(uint8 fromAcc, uint256 amount) public {\n address from = getAccount(fromAcc);\n hevm.prank(ADDRESS_VAULT);\n ousd.burn(from, amount);\n }\n\n /**\n * @notice Change the total supply of OUSD (rebase)\n * @param amount New total supply\n */\n function changeSupply(uint256 amount) public {\n if (TOGGLE_CHANGESUPPLY_LIMIT) {\n amount =\n ousd.totalSupply() +\n (amount % (ousd.totalSupply() / CHANGESUPPLY_DIVISOR));\n }\n\n hevm.prank(ADDRESS_VAULT);\n ousd.changeSupply(amount);\n }\n\n /**\n * @notice Transfer tokens between accounts\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function transfer(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n ousd.transfer(to, amount);\n }\n\n /**\n * @notice Transfer approved tokens between accounts\n * @param authorizedAcc Account that is authorized to transfer\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function transferFrom(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n ousd.transferFrom(from, to, amount);\n }\n\n /**\n * @notice Opt in to rebasing\n * @param targetAcc Account to opt in\n */\n function optIn(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n hevm.prank(target);\n ousd.rebaseOptIn();\n }\n\n /**\n * @notice Opt out of rebasing\n * @param targetAcc Account to opt out\n */\n function optOut(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n hevm.prank(target);\n ousd.rebaseOptOut();\n }\n\n /**\n * @notice Approve an account to spend OUSD\n * @param ownerAcc Account that owns the OUSD\n * @param spenderAcc Account that is approved to spend the OUSD\n * @param amount Amount to approve\n */\n function approve(\n uint8 ownerAcc,\n uint8 spenderAcc,\n uint256 amount\n ) public {\n address owner = getAccount(ownerAcc);\n address spender = getAccount(spenderAcc);\n hevm.prank(owner);\n // slither-disable-next-line unused-return\n ousd.approve(spender, amount);\n }\n\n /**\n * @notice Get the sum of all OUSD balances\n * @return total Total balance\n */\n function getTotalBalance() public view returns (uint256 total) {\n total += ousd.balanceOf(ADDRESS_VAULT);\n total += ousd.balanceOf(ADDRESS_OUTSIDER_USER);\n total += ousd.balanceOf(ADDRESS_OUTSIDER_CONTRACT);\n total += ousd.balanceOf(ADDRESS_USER0);\n total += ousd.balanceOf(ADDRESS_USER1);\n total += ousd.balanceOf(ADDRESS_CONTRACT0);\n total += ousd.balanceOf(ADDRESS_CONTRACT1);\n }\n\n /**\n * @notice Get the sum of all non-rebasing OUSD balances\n * @return total Total balance\n */\n function getTotalNonRebasingBalance() public returns (uint256 total) {\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_VAULT)\n ? ousd.balanceOf(ADDRESS_VAULT)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_OUTSIDER_USER)\n ? ousd.balanceOf(ADDRESS_OUTSIDER_USER)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_OUTSIDER_CONTRACT)\n ? ousd.balanceOf(ADDRESS_OUTSIDER_CONTRACT)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_USER0)\n ? ousd.balanceOf(ADDRESS_USER0)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_USER1)\n ? ousd.balanceOf(ADDRESS_USER1)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_CONTRACT0)\n ? ousd.balanceOf(ADDRESS_CONTRACT0)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_CONTRACT1)\n ? ousd.balanceOf(ADDRESS_CONTRACT1)\n : 0;\n }\n}\n" + }, + "contracts/echidna/EchidnaSetup.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./IHevm.sol\";\nimport \"./EchidnaConfig.sol\";\nimport \"./OUSDEchidna.sol\";\n\ncontract Dummy {}\n\n/**\n * @title Mixin for setup and deployment\n * @author Rappie\n */\ncontract EchidnaSetup is EchidnaConfig {\n IHevm hevm = IHevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);\n OUSDEchidna ousd = new OUSDEchidna();\n\n /**\n * @notice Deploy the OUSD contract and set up initial state\n */\n constructor() {\n ousd.initialize(ADDRESS_VAULT, 1e18);\n\n // Deploy dummny contracts as users\n Dummy outsider = new Dummy();\n ADDRESS_OUTSIDER_CONTRACT = address(outsider);\n Dummy dummy0 = new Dummy();\n ADDRESS_CONTRACT0 = address(dummy0);\n Dummy dummy1 = new Dummy();\n ADDRESS_CONTRACT1 = address(dummy1);\n\n // Start out with a reasonable amount of OUSD\n if (TOGGLE_STARTING_BALANCE) {\n // Rebasing tokens\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(ADDRESS_OUTSIDER_USER, STARTING_BALANCE / 2);\n\n // Non-rebasing tokens\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(ADDRESS_OUTSIDER_CONTRACT, STARTING_BALANCE / 2);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestAccounting.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestSupply.sol\";\n\n/**\n * @title Mixin for testing accounting functions\n * @author Rappie\n */\ncontract EchidnaTestAccounting is EchidnaTestSupply {\n /**\n * @notice After opting in, balance should not increase. (Ok to lose rounding funds doing this)\n * @param targetAcc Account to opt in\n */\n function testOptInBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optIn(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter <= balanceBefore);\n }\n\n /**\n * @notice After opting out, balance should remain the same\n * @param targetAcc Account to opt out\n */\n function testOptOutBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optOut(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Account balance should remain the same after opting in minus rounding error\n * @param targetAcc Account to opt in\n */\n function testOptInBalanceRounding(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optIn(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n int256 delta = int256(balanceAfter) - int256(balanceBefore);\n Debugger.log(\"delta\", delta);\n\n // slither-disable-next-line tautology\n assert(-1 * delta >= 0);\n assert(-1 * delta <= int256(OPT_IN_ROUNDING_ERROR));\n }\n\n /**\n * @notice After opting in, total supply should remain the same\n * @param targetAcc Account to opt in\n */\n function testOptInTotalSupply(uint8 targetAcc) public {\n uint256 totalSupplyBefore = ousd.totalSupply();\n optIn(targetAcc);\n uint256 totalSupplyAfter = ousd.totalSupply();\n\n assert(totalSupplyAfter == totalSupplyBefore);\n }\n\n /**\n * @notice After opting out, total supply should remain the same\n * @param targetAcc Account to opt out\n */\n function testOptOutTotalSupply(uint8 targetAcc) public {\n uint256 totalSupplyBefore = ousd.totalSupply();\n optOut(targetAcc);\n uint256 totalSupplyAfter = ousd.totalSupply();\n\n assert(totalSupplyAfter == totalSupplyBefore);\n }\n\n /**\n * @notice Account balance should remain the same when a smart contract auto converts\n * @param targetAcc Account to auto convert\n */\n function testAutoConvertBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n // slither-disable-next-line unused-return\n ousd._isNonRebasingAccountEchidna(target);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice The `balanceOf` function should never revert\n * @param targetAcc Account to check balance of\n */\n function testBalanceOfShouldNotRevert(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n // slither-disable-next-line unused-return\n try ousd.balanceOf(target) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestApproval.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaTestMintBurn.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin for testing approval related functions\n * @author Rappie\n */\ncontract EchidnaTestApproval is EchidnaTestMintBurn {\n /**\n * @notice Performing `transferFrom` with an amount inside the allowance should not revert\n * @param authorizedAcc The account that is authorized to transfer\n * @param fromAcc The account that is transferring\n * @param toAcc The account that is receiving\n * @param amount The amount to transfer\n */\n function testTransferFromShouldNotRevert(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount <= ousd.balanceOf(from));\n require(amount <= ousd.allowance(from, authorized));\n\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n try ousd.transferFrom(from, to, amount) {\n // pass\n } catch {\n assert(false);\n }\n }\n\n /**\n * @notice Performing `transferFrom` with an amount outside the allowance should revert\n * @param authorizedAcc The account that is authorized to transfer\n * @param fromAcc The account that is transferring\n * @param toAcc The account that is receiving\n * @param amount The amount to transfer\n */\n function testTransferFromShouldRevert(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount > 0);\n require(\n !(amount <= ousd.balanceOf(from) &&\n amount <= ousd.allowance(from, authorized))\n );\n\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n try ousd.transferFrom(from, to, amount) {\n assert(false);\n } catch {\n // pass\n }\n }\n\n /**\n * @notice Approving an amount should update the allowance and overwrite any previous allowance\n * @param ownerAcc The account that is approving\n * @param spenderAcc The account that is being approved\n * @param amount The amount to approve\n */\n function testApprove(\n uint8 ownerAcc,\n uint8 spenderAcc,\n uint256 amount\n ) public {\n address owner = getAccount(ownerAcc);\n address spender = getAccount(spenderAcc);\n\n approve(ownerAcc, spenderAcc, amount);\n uint256 allowanceAfter1 = ousd.allowance(owner, spender);\n\n assert(allowanceAfter1 == amount);\n\n approve(ownerAcc, spenderAcc, amount / 2);\n uint256 allowanceAfter2 = ousd.allowance(owner, spender);\n\n assert(allowanceAfter2 == amount / 2);\n }\n}\n" + }, + "contracts/echidna/EchidnaTestMintBurn.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestAccounting.sol\";\n\n/**\n * @title Mixin for testing Mint and Burn functions\n * @author Rappie\n */\ncontract EchidnaTestMintBurn is EchidnaTestAccounting {\n /**\n * @notice Minting 0 tokens should not affect account balance\n * @param targetAcc Account to mint to\n */\n function testMintZeroBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n mint(targetAcc, 0);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Burning 0 tokens should not affect account balance\n * @param targetAcc Account to burn from\n */\n function testBurnZeroBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n burn(targetAcc, 0);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Minting tokens must increase the account balance by at least amount\n * @param targetAcc Account to mint to\n * @param amount Amount to mint\n * @custom:error testMintBalance(uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * testMintBalance(0,1)\n * Event sequence:\n * Debug(«balanceBefore», 0)\n * Debug(«balanceAfter», 0)\n */\n function testMintBalance(uint8 targetAcc, uint256 amount)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n uint256 amountMinted = mint(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n Debugger.log(\"amountMinted\", amountMinted);\n Debugger.log(\"balanceBefore\", balanceBefore);\n Debugger.log(\"balanceAfter\", balanceAfter);\n\n assert(balanceAfter >= balanceBefore + amountMinted);\n }\n\n /**\n * @notice Burning tokens must decrease the account balance by at least amount\n * @param targetAcc Account to burn from\n * @param amount Amount to burn\n * @custom:error testBurnBalance(uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * mint(0,3)\n * testBurnBalance(0,1)\n * Event sequence:\n * Debug(«balanceBefore», 2)\n * Debug(«balanceAfter», 2)\n */\n function testBurnBalance(uint8 targetAcc, uint256 amount)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n burn(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n Debugger.log(\"balanceBefore\", balanceBefore);\n Debugger.log(\"balanceAfter\", balanceAfter);\n\n assert(balanceAfter <= balanceBefore - amount);\n }\n\n /**\n * @notice Minting tokens should not increase the account balance by less than rounding error above amount\n * @param targetAcc Account to mint to\n * @param amount Amount to mint\n */\n function testMintBalanceRounding(uint8 targetAcc, uint256 amount) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n uint256 amountMinted = mint(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n int256 delta = int256(balanceAfter) - int256(balanceBefore);\n\n // delta == amount, if no error\n // delta < amount, if too little is minted\n // delta > amount, if too much is minted\n int256 error = int256(amountMinted) - delta;\n\n assert(error >= 0);\n assert(error <= int256(MINT_ROUNDING_ERROR));\n }\n\n /**\n * @notice A burn of an account balance must result in a zero balance\n * @param targetAcc Account to burn from\n */\n function testBurnAllBalanceToZero(uint8 targetAcc) public hasKnownIssue {\n address target = getAccount(targetAcc);\n\n burn(targetAcc, ousd.balanceOf(target));\n assert(ousd.balanceOf(target) == 0);\n }\n\n /**\n * @notice You should always be able to burn an account's balance\n * @param targetAcc Account to burn from\n */\n function testBurnAllBalanceShouldNotRevert(uint8 targetAcc)\n public\n hasKnownIssue\n {\n address target = getAccount(targetAcc);\n uint256 balance = ousd.balanceOf(target);\n\n hevm.prank(ADDRESS_VAULT);\n try ousd.burn(target, balance) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestSupply.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestTransfer.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n/**\n * @title Mixin for testing supply related functions\n * @author Rappie\n */\ncontract EchidnaTestSupply is EchidnaTestTransfer {\n using StableMath for uint256;\n\n uint256 prevRebasingCreditsPerToken = type(uint256).max;\n\n /**\n * @notice After a `changeSupply`, the total supply should exactly\n * match the target total supply. (This is needed to ensure successive\n * rebases are correct).\n * @param supply New total supply\n * @custom:error testChangeSupply(uint256): failed!💥\n * Call sequence:\n * testChangeSupply(1044505275072865171609)\n * Event sequence:\n * TotalSupplyUpdatedHighres(1044505275072865171610, 1000000000000000000000000, 957391048054055578595)\n */\n function testChangeSupply(uint256 supply)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n hevm.prank(ADDRESS_VAULT);\n ousd.changeSupply(supply);\n\n assert(ousd.totalSupply() == supply);\n }\n\n /**\n * @notice The total supply must not be less than the sum of account balances.\n * (The difference will go into future rebases)\n * @custom:error testTotalSupplyLessThanTotalBalance(): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(1)\n * optOut(64)\n * transfer(0,64,1)\n * testTotalSupplyLessThanTotalBalance()\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000001000001)\n * Debug(«totalBalance», 1000000000000000001000002)\n */\n function testTotalSupplyLessThanTotalBalance()\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n uint256 totalSupply = ousd.totalSupply();\n uint256 totalBalance = getTotalBalance();\n\n Debugger.log(\"totalSupply\", totalSupply);\n Debugger.log(\"totalBalance\", totalBalance);\n\n assert(totalSupply >= totalBalance);\n }\n\n /**\n * @notice Non-rebasing supply should not be larger than total supply\n * @custom:error testNonRebasingSupplyVsTotalSupply(): failed!💥\n * Call sequence:\n * mint(0,2)\n * changeSupply(3)\n * burn(0,1)\n * optOut(0)\n * testNonRebasingSupplyVsTotalSupply()\n */\n function testNonRebasingSupplyVsTotalSupply() public hasKnownIssue {\n uint256 nonRebasingSupply = ousd.nonRebasingSupply();\n uint256 totalSupply = ousd.totalSupply();\n\n assert(nonRebasingSupply <= totalSupply);\n }\n\n /**\n * @notice Global `rebasingCreditsPerToken` should never increase\n * @custom:error testRebasingCreditsPerTokenNotIncreased(): failed!💥\n * Call sequence:\n * testRebasingCreditsPerTokenNotIncreased()\n * changeSupply(1)\n * testRebasingCreditsPerTokenNotIncreased()\n */\n function testRebasingCreditsPerTokenNotIncreased() public hasKnownIssue {\n uint256 curRebasingCreditsPerToken = ousd\n .rebasingCreditsPerTokenHighres();\n\n Debugger.log(\n \"prevRebasingCreditsPerToken\",\n prevRebasingCreditsPerToken\n );\n Debugger.log(\"curRebasingCreditsPerToken\", curRebasingCreditsPerToken);\n\n assert(curRebasingCreditsPerToken <= prevRebasingCreditsPerToken);\n\n prevRebasingCreditsPerToken = curRebasingCreditsPerToken;\n }\n\n /**\n * @notice The rebasing credits per token ratio must greater than zero\n */\n function testRebasingCreditsPerTokenAboveZero() public {\n assert(ousd.rebasingCreditsPerTokenHighres() > 0);\n }\n\n /**\n * @notice The sum of all non-rebasing balances should not be larger than\n * non-rebasing supply\n * @custom:error testTotalNonRebasingSupplyLessThanTotalBalance(): failed!💥\n * Call sequence\n * mint(0,2)\n * changeSupply(1)\n * optOut(0)\n * burn(0,1)\n * testTotalNonRebasingSupplyLessThanTotalBalance()\n * Event sequence:\n * Debug(«totalNonRebasingSupply», 500000000000000000000001)\n * Debug(«totalNonRebasingBalance», 500000000000000000000002)\n */\n function testTotalNonRebasingSupplyLessThanTotalBalance()\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n uint256 totalNonRebasingSupply = ousd.nonRebasingSupply();\n uint256 totalNonRebasingBalance = getTotalNonRebasingBalance();\n\n Debugger.log(\"totalNonRebasingSupply\", totalNonRebasingSupply);\n Debugger.log(\"totalNonRebasingBalance\", totalNonRebasingBalance);\n\n assert(totalNonRebasingSupply >= totalNonRebasingBalance);\n }\n\n /**\n * @notice An accounts credits / credits per token should not be larger it's balance\n * @param targetAcc The account to check\n */\n function testCreditsPerTokenVsBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n (uint256 credits, uint256 creditsPerToken, ) = ousd\n .creditsBalanceOfHighres(target);\n uint256 expectedBalance = credits.divPrecisely(creditsPerToken);\n\n uint256 balance = ousd.balanceOf(target);\n\n Debugger.log(\"credits\", credits);\n Debugger.log(\"creditsPerToken\", creditsPerToken);\n Debugger.log(\"expectedBalance\", expectedBalance);\n Debugger.log(\"balance\", balance);\n\n assert(expectedBalance == balance);\n }\n}\n" + }, + "contracts/echidna/EchidnaTestTransfer.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin for testing transfer related functions\n * @author Rappie\n */\ncontract EchidnaTestTransfer is EchidnaDebug {\n /**\n * @notice The receiving account's balance after a transfer must not increase by\n * less than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferBalanceReceivedLess(uint8,uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * mint(64,2)\n * testTransferBalanceReceivedLess(64,0,1)\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000000500002)\n * Debug(«toBalBefore», 0)\n * Debug(«toBalAfter», 0)\n */\n function testTransferBalanceReceivedLess(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue hasKnownIssueWithinLimits {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n\n assert(toBalAfter >= toBalBefore + amount);\n }\n\n /**\n * @notice The receiving account's balance after a transfer must not\n * increase by more than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceReceivedMore(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n\n assert(toBalAfter <= toBalBefore + amount);\n }\n\n /**\n * @notice The sending account's balance after a transfer must not\n * decrease by less than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferBalanceSentLess(uint8,uint8,uint256): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(1)\n * testTransferBalanceSentLess(0,64,1)\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000000500001)\n * Debug(«fromBalBefore», 1)\n * Debug(«fromBalAfter», 1)\n */\n function testTransferBalanceSentLess(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue hasKnownIssueWithinLimits {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n\n assert(fromBalAfter <= fromBalBefore - amount);\n }\n\n /**\n * @notice The sending account's balance after a transfer must not\n * decrease by more than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceSentMore(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n\n assert(fromBalAfter >= fromBalBefore - amount);\n }\n\n /**\n * @notice The receiving account's balance after a transfer must not\n * increase by less than the amount transferred (minus rounding error)\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceReceivedLessRounding(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n int256 toDelta = int256(toBalAfter) - int256(toBalBefore);\n\n // delta == amount, if no error\n // delta < amount, if too little is sent\n // delta > amount, if too much is sent\n int256 error = int256(amount) - toDelta;\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n Debugger.log(\"toDelta\", toDelta);\n Debugger.log(\"error\", error);\n\n assert(error >= 0);\n assert(error <= int256(TRANSFER_ROUNDING_ERROR));\n }\n\n /**\n * @notice The sending account's balance after a transfer must\n * not decrease by less than the amount transferred (minus rounding error)\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceSentLessRounding(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n int256 fromDelta = int256(fromBalAfter) - int256(fromBalBefore);\n\n // delta == -amount, if no error\n // delta < -amount, if too much is sent\n // delta > -amount, if too little is sent\n int256 error = int256(amount) + fromDelta;\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n Debugger.log(\"fromDelta\", fromDelta);\n Debugger.log(\"error\", error);\n\n assert(error >= 0);\n assert(error <= int256(TRANSFER_ROUNDING_ERROR));\n }\n\n /**\n * @notice An account should always be able to successfully transfer\n * an amount within its balance.\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferWithinBalanceDoesNotRevert(uint8,uint8,uint8): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(3)\n * optOut(0)\n * testTransferWithinBalanceDoesNotRevert(0,128,2)\n * optIn(0)\n * testTransferWithinBalanceDoesNotRevert(128,0,1)\n * Event sequence:\n * error Revert Panic(17): SafeMath over-/under-flows\n */\n function testTransferWithinBalanceDoesNotRevert(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount > 0);\n amount = amount % ousd.balanceOf(from);\n\n Debugger.log(\"Total supply\", ousd.totalSupply());\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(to, amount) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n\n /**\n * @notice An account should never be able to successfully transfer\n * an amount greater than their balance.\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferExceedingBalanceReverts(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n amount = ousd.balanceOf(from) + 1 + amount;\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(to, amount) {\n assert(false);\n } catch {\n assert(true);\n }\n }\n\n /**\n * @notice A transfer to the same account should not change that account's balance\n * @param targetAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferSelf(uint8 targetAcc, uint256 amount) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n transfer(targetAcc, targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceBefore == balanceAfter);\n }\n\n /**\n * @notice Transfers to the zero account revert\n * @param fromAcc Account to transfer from\n * @param amount Amount to transfer\n */\n function testTransferToZeroAddress(uint8 fromAcc, uint256 amount) public {\n address from = getAccount(fromAcc);\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(address(0), amount) {\n assert(false);\n } catch {\n assert(true);\n }\n }\n}\n" + }, + "contracts/echidna/IHevm.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// https://github.com/ethereum/hevm/blob/main/doc/src/controlling-the-unit-testing-environment.md#cheat-codes\n\ninterface IHevm {\n function warp(uint256 x) external;\n\n function roll(uint256 x) external;\n\n function store(\n address c,\n bytes32 loc,\n bytes32 val\n ) external;\n\n function load(address c, bytes32 loc) external returns (bytes32 val);\n\n function sign(uint256 sk, bytes32 digest)\n external\n returns (\n uint8 v,\n bytes32 r,\n bytes32 s\n );\n\n function addr(uint256 sk) external returns (address addr);\n\n function ffi(string[] calldata) external returns (bytes memory);\n\n function prank(address sender) external;\n}\n" + }, + "contracts/echidna/OUSDEchidna.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../token/OUSD.sol\";\n\ncontract OUSDEchidna is OUSD {\n constructor() OUSD() {}\n\n function _isNonRebasingAccountEchidna(address _account)\n public\n returns (bool)\n {\n _autoMigrate(_account);\n return alternativeCreditsPerToken[_account] > 0;\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/governance/InitializableGovernable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD InitializableGovernable Contract\n * @author Origin Protocol Inc\n */\nimport { Initializable } from \"../utils/Initializable.sol\";\n\nimport { Governable } from \"./Governable.sol\";\n\ncontract InitializableGovernable is Governable, Initializable {\n function _initialize(address _newGovernor) internal {\n _changeGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/governance/Strategizable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"./Governable.sol\";\n\ncontract Strategizable is Governable {\n event StrategistUpdated(address _address);\n\n // Address of strategist\n address public strategistAddr;\n\n // For future use\n uint256[50] private __gap;\n\n /**\n * @dev Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @dev Set address of Strategist\n * @param _address Address of Strategist\n */\n function setStrategistAddr(address _address) external onlyGovernor {\n _setStrategistAddr(_address);\n }\n\n /**\n * @dev Set address of Strategist\n * @param _address Address of Strategist\n */\n function _setStrategistAddr(address _address) internal {\n strategistAddr = _address;\n emit StrategistUpdated(_address);\n }\n}\n" + }, + "contracts/harvest/AbstractHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IUniswapV2Router } from \"../interfaces/uniswap/IUniswapV2Router02.sol\";\nimport { IUniswapV3Router } from \"../interfaces/uniswap/IUniswapV3Router.sol\";\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { ICurvePool } from \"../strategies/ICurvePool.sol\";\nimport \"../utils/Helpers.sol\";\n\nabstract contract AbstractHarvester is Governable {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using StableMath for uint256;\n\n enum SwapPlatform {\n UniswapV2Compatible,\n UniswapV3,\n Balancer,\n Curve\n }\n\n event SupportedStrategyUpdate(address strategyAddress, bool isSupported);\n event RewardTokenConfigUpdated(\n address tokenAddress,\n uint16 allowedSlippageBps,\n uint16 harvestRewardBps,\n SwapPlatform swapPlatform,\n address swapPlatformAddr,\n bytes swapData,\n uint256 liquidationLimit,\n bool doSwapRewardToken\n );\n event RewardTokenSwapped(\n address indexed rewardToken,\n address indexed swappedInto,\n SwapPlatform swapPlatform,\n uint256 amountIn,\n uint256 amountOut\n );\n event RewardProceedsTransferred(\n address indexed token,\n address farmer,\n uint256 protcolYield,\n uint256 farmerFee\n );\n event RewardProceedsAddressChanged(address newProceedsAddress);\n\n error EmptyAddress();\n error InvalidSlippageBps();\n error InvalidHarvestRewardBps();\n\n error InvalidSwapPlatform(SwapPlatform swapPlatform);\n\n error InvalidUniswapV2PathLength();\n error InvalidTokenInSwapPath(address token);\n error EmptyBalancerPoolId();\n error InvalidCurvePoolAssetIndex(address token);\n\n error UnsupportedStrategy(address strategyAddress);\n\n error SlippageError(uint256 actualBalance, uint256 minExpected);\n error BalanceMismatchAfterSwap(uint256 actualBalance, uint256 minExpected);\n\n // Configuration properties for harvesting logic of reward tokens\n struct RewardTokenConfig {\n // Max allowed slippage when swapping reward token for a stablecoin denominated in basis points.\n uint16 allowedSlippageBps;\n // Reward when calling a harvest function denominated in basis points.\n uint16 harvestRewardBps;\n // Address of compatible exchange protocol (Uniswap V2/V3, SushiSwap, Balancer and Curve).\n address swapPlatformAddr;\n /* When true the reward token is being swapped. In a need of (temporarily) disabling the swapping of\n * a reward token this needs to be set to false.\n */\n bool doSwapRewardToken;\n // Platform to use for Swapping\n SwapPlatform swapPlatform;\n /* How much token can be sold per one harvest call. If the balance of rewards tokens\n * exceeds that limit multiple harvest calls are required to harvest all of the tokens.\n * Set it to MAX_INT to effectively disable the limit.\n */\n uint256 liquidationLimit;\n }\n\n mapping(address => RewardTokenConfig) public rewardTokenConfigs;\n mapping(address => bool) public supportedStrategies;\n\n address public immutable vaultAddress;\n\n /**\n * Address receiving rewards proceeds. Initially the Vault contract later will possibly\n * be replaced by another contract that eases out rewards distribution.\n **/\n address public rewardProceedsAddress;\n\n /**\n * All tokens are swapped to this token before it gets transferred\n * to the `rewardProceedsAddress`. USDT for OUSD and WETH for OETH.\n **/\n address public immutable baseTokenAddress;\n // Cached decimals for `baseTokenAddress`\n uint256 public immutable baseTokenDecimals;\n\n // Uniswap V2 path for reward tokens using Uniswap V2 Router\n mapping(address => address[]) public uniswapV2Path;\n // Uniswap V3 path for reward tokens using Uniswap V3 Router\n mapping(address => bytes) public uniswapV3Path;\n // Pool ID to use for reward tokens on Balancer\n mapping(address => bytes32) public balancerPoolId;\n\n struct CurvePoolIndices {\n // Casted into uint128 and stored in a struct to save gas\n uint128 rewardTokenIndex;\n uint128 baseTokenIndex;\n }\n // Packed indices of assets on the Curve pool\n mapping(address => CurvePoolIndices) public curvePoolIndices;\n\n constructor(address _vaultAddress, address _baseTokenAddress) {\n require(_vaultAddress != address(0));\n require(_baseTokenAddress != address(0));\n\n vaultAddress = _vaultAddress;\n baseTokenAddress = _baseTokenAddress;\n\n // Cache decimals as well\n baseTokenDecimals = Helpers.getDecimals(_baseTokenAddress);\n }\n\n /***************************************\n Configuration\n ****************************************/\n\n /**\n * Set the Address receiving rewards proceeds.\n * @param _rewardProceedsAddress Address of the reward token\n */\n function setRewardProceedsAddress(address _rewardProceedsAddress)\n external\n onlyGovernor\n {\n if (_rewardProceedsAddress == address(0)) {\n revert EmptyAddress();\n }\n\n rewardProceedsAddress = _rewardProceedsAddress;\n emit RewardProceedsAddressChanged(_rewardProceedsAddress);\n }\n\n /**\n * @dev Add/update a reward token configuration that holds harvesting config variables\n * @param _tokenAddress Address of the reward token\n * @param tokenConfig.allowedSlippageBps uint16 maximum allowed slippage denominated in basis points.\n * Example: 300 == 3% slippage\n * @param tokenConfig.harvestRewardBps uint16 amount of reward tokens the caller of the function is rewarded.\n * Example: 100 == 1%\n * @param tokenConfig.swapPlatformAddr Address Address of a UniswapV2 compatible contract to perform\n * the exchange from reward tokens to stablecoin (currently hard-coded to USDT)\n * @param tokenConfig.liquidationLimit uint256 Maximum amount of token to be sold per one swap function call.\n * When value is 0 there is no limit.\n * @param tokenConfig.doSwapRewardToken bool Disables swapping of the token when set to true,\n * does not cause it to revert though.\n * @param tokenConfig.swapPlatform SwapPlatform to use for Swapping\n * @param swapData Additional data required for swapping\n */\n function setRewardTokenConfig(\n address _tokenAddress,\n RewardTokenConfig calldata tokenConfig,\n bytes calldata swapData\n ) external onlyGovernor {\n if (tokenConfig.allowedSlippageBps > 1000) {\n revert InvalidSlippageBps();\n }\n\n if (tokenConfig.harvestRewardBps > 1000) {\n revert InvalidHarvestRewardBps();\n }\n\n address newRouterAddress = tokenConfig.swapPlatformAddr;\n if (newRouterAddress == address(0)) {\n // Swap router address should be non zero address\n revert EmptyAddress();\n }\n\n address oldRouterAddress = rewardTokenConfigs[_tokenAddress]\n .swapPlatformAddr;\n rewardTokenConfigs[_tokenAddress] = tokenConfig;\n\n // Revert if feed does not exist\n // slither-disable-next-line unused-return\n IOracle(IVault(vaultAddress).priceProvider()).price(_tokenAddress);\n\n IERC20 token = IERC20(_tokenAddress);\n // if changing token swap provider cancel existing allowance\n if (\n /* oldRouterAddress == address(0) when there is no pre-existing\n * configuration for said rewards token\n */\n oldRouterAddress != address(0) &&\n oldRouterAddress != newRouterAddress\n ) {\n token.safeApprove(oldRouterAddress, 0);\n }\n\n // Give SwapRouter infinite approval when needed\n if (oldRouterAddress != newRouterAddress) {\n token.safeApprove(newRouterAddress, 0);\n token.safeApprove(newRouterAddress, type(uint256).max);\n }\n\n SwapPlatform _platform = tokenConfig.swapPlatform;\n if (_platform == SwapPlatform.UniswapV2Compatible) {\n uniswapV2Path[_tokenAddress] = _decodeUniswapV2Path(\n swapData,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.UniswapV3) {\n uniswapV3Path[_tokenAddress] = _decodeUniswapV3Path(\n swapData,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.Balancer) {\n balancerPoolId[_tokenAddress] = _decodeBalancerPoolId(\n swapData,\n newRouterAddress,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.Curve) {\n curvePoolIndices[_tokenAddress] = _decodeCurvePoolIndices(\n swapData,\n newRouterAddress,\n _tokenAddress\n );\n } else {\n // Note: This code is unreachable since Solidity reverts when\n // the value is outside the range of defined values of the enum\n // (even if it's under the max length of the base type)\n revert InvalidSwapPlatform(_platform);\n }\n\n emit RewardTokenConfigUpdated(\n _tokenAddress,\n tokenConfig.allowedSlippageBps,\n tokenConfig.harvestRewardBps,\n _platform,\n newRouterAddress,\n swapData,\n tokenConfig.liquidationLimit,\n tokenConfig.doSwapRewardToken\n );\n }\n\n /**\n * @dev Decodes the data passed into Uniswap V2 path and validates\n * it to make sure the path is for `token` to `baseToken`\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param token The address of the reward token\n * @return path The validated Uniswap V2 path\n */\n function _decodeUniswapV2Path(bytes calldata data, address token)\n internal\n view\n returns (address[] memory path)\n {\n (path) = abi.decode(data, (address[]));\n uint256 len = path.length;\n\n if (len < 2) {\n // Path should have at least two tokens\n revert InvalidUniswapV2PathLength();\n }\n\n // Do some validation\n if (path[0] != token) {\n revert InvalidTokenInSwapPath(path[0]);\n }\n\n if (path[len - 1] != baseTokenAddress) {\n revert InvalidTokenInSwapPath(path[len - 1]);\n }\n }\n\n /**\n * @dev Decodes the data passed into Uniswap V3 path and validates\n * it to make sure the path is for `token` to `baseToken`\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param token The address of the reward token\n * @return path The validated Uniswap V3 path\n */\n function _decodeUniswapV3Path(bytes calldata data, address token)\n internal\n view\n returns (bytes calldata path)\n {\n path = data;\n\n address decodedAddress = address(uint160(bytes20(data[0:20])));\n\n if (decodedAddress != token) {\n // Invalid Reward Token in swap path\n revert InvalidTokenInSwapPath(decodedAddress);\n }\n\n decodedAddress = address(uint160(bytes20(data[path.length - 20:])));\n if (decodedAddress != baseTokenAddress) {\n // Invalid Base Token in swap path\n revert InvalidTokenInSwapPath(decodedAddress);\n }\n }\n\n /**\n * @dev Decodes the data passed to Balancer Pool ID\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @return poolId The pool ID\n */\n function _decodeBalancerPoolId(\n bytes calldata data,\n address balancerVault,\n address token\n ) internal view returns (bytes32 poolId) {\n (poolId) = abi.decode(data, (bytes32));\n\n if (poolId == bytes32(0)) {\n revert EmptyBalancerPoolId();\n }\n\n IBalancerVault bVault = IBalancerVault(balancerVault);\n\n // Note: this reverts if token is not a pool asset\n // slither-disable-next-line unused-return\n bVault.getPoolTokenInfo(poolId, token);\n\n // slither-disable-next-line unused-return\n bVault.getPoolTokenInfo(poolId, baseTokenAddress);\n }\n\n /**\n * @dev Decodes the data passed to get the pool indices and\n * checks it against the Curve Pool to make sure it's\n * not misconfigured. The indices are packed into a single\n * uint256 for gas savings\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param poolAddress Curve pool address\n * @param token The address of the reward token\n * @return indices Packed pool asset indices\n */\n function _decodeCurvePoolIndices(\n bytes calldata data,\n address poolAddress,\n address token\n ) internal view returns (CurvePoolIndices memory indices) {\n indices = abi.decode(data, (CurvePoolIndices));\n\n ICurvePool pool = ICurvePool(poolAddress);\n if (token != pool.coins(indices.rewardTokenIndex)) {\n revert InvalidCurvePoolAssetIndex(token);\n }\n if (baseTokenAddress != pool.coins(indices.baseTokenIndex)) {\n revert InvalidCurvePoolAssetIndex(baseTokenAddress);\n }\n }\n\n /**\n * @dev Flags a strategy as supported or not supported one\n * @param _strategyAddress Address of the strategy\n * @param _isSupported Bool marking strategy as supported or not supported\n */\n function setSupportedStrategy(address _strategyAddress, bool _isSupported)\n external\n onlyGovernor\n {\n supportedStrategies[_strategyAddress] = _isSupported;\n emit SupportedStrategyUpdate(_strategyAddress, _isSupported);\n }\n\n /***************************************\n Rewards\n ****************************************/\n\n /**\n * @dev Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform. Can be called by anyone.\n * Rewards incentivizing the caller are sent to the caller of this function.\n * @param _strategyAddr Address of the strategy to collect rewards from\n */\n function harvestAndSwap(address _strategyAddr) external nonReentrant {\n // Remember _harvest function checks for the validity of _strategyAddr\n _harvestAndSwap(_strategyAddr, msg.sender);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform. Can be called by anyone\n * @param _strategyAddr Address of the strategy to collect rewards from\n * @param _rewardTo Address where to send a share of harvest rewards to as an incentive\n * for executing this function\n */\n function harvestAndSwap(address _strategyAddr, address _rewardTo)\n external\n nonReentrant\n {\n // Remember _harvest function checks for the validity of _strategyAddr\n _harvestAndSwap(_strategyAddr, _rewardTo);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform\n * @param _strategyAddr Address of the strategy to collect rewards from\n * @param _rewardTo Address where to send a share of harvest rewards to as an incentive\n * for executing this function\n */\n function _harvestAndSwap(address _strategyAddr, address _rewardTo)\n internal\n {\n _harvest(_strategyAddr);\n IStrategy strategy = IStrategy(_strategyAddr);\n address[] memory rewardTokens = strategy.getRewardTokenAddresses();\n IOracle priceProvider = IOracle(IVault(vaultAddress).priceProvider());\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; ++i) {\n _swap(rewardTokens[i], _rewardTo, priceProvider);\n }\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform\n * @param _strategyAddr Address of the strategy to collect rewards from.\n */\n function _harvest(address _strategyAddr) internal virtual {\n if (!supportedStrategies[_strategyAddr]) {\n revert UnsupportedStrategy(_strategyAddr);\n }\n\n IStrategy strategy = IStrategy(_strategyAddr);\n strategy.collectRewardTokens();\n }\n\n /**\n * @dev Swap a reward token for the base token on the configured\n * swap platform. The token must have a registered price feed\n * with the price provider\n * @param _swapToken Address of the token to swap\n * @param _rewardTo Address where to send the share of harvest rewards to\n * @param _priceProvider Oracle to get prices of the swap token\n */\n function _swap(\n address _swapToken,\n address _rewardTo,\n IOracle _priceProvider\n ) internal virtual {\n uint256 balance = IERC20(_swapToken).balanceOf(address(this));\n\n // No need to swap if the reward token is the base token. eg USDT or WETH.\n // There is also no limit on the transfer. Everything in the harvester will be transferred\n // to the Dripper regardless of the liquidationLimit config.\n if (_swapToken == baseTokenAddress) {\n IERC20(_swapToken).safeTransfer(rewardProceedsAddress, balance);\n // currently not paying the farmer any rewards as there is no swap\n emit RewardProceedsTransferred(\n baseTokenAddress,\n address(0),\n balance,\n 0\n );\n return;\n }\n\n RewardTokenConfig memory tokenConfig = rewardTokenConfigs[_swapToken];\n\n /* This will trigger a return when reward token configuration has not yet been set\n * or we have temporarily disabled swapping of specific reward token via setting\n * doSwapRewardToken to false.\n */\n if (!tokenConfig.doSwapRewardToken) {\n return;\n }\n\n if (balance == 0) {\n return;\n }\n\n if (tokenConfig.liquidationLimit > 0) {\n balance = Math.min(balance, tokenConfig.liquidationLimit);\n }\n\n // This'll revert if there is no price feed\n uint256 oraclePrice = _priceProvider.price(_swapToken);\n\n // Oracle price is 1e18\n uint256 minExpected = (balance *\n (1e4 - tokenConfig.allowedSlippageBps) * // max allowed slippage\n oraclePrice).scaleBy(\n baseTokenDecimals,\n Helpers.getDecimals(_swapToken)\n ) /\n 1e4 / // fix the max slippage decimal position\n 1e18; // and oracle price decimals position\n\n // Do the swap\n uint256 amountReceived = _doSwap(\n tokenConfig.swapPlatform,\n tokenConfig.swapPlatformAddr,\n _swapToken,\n balance,\n minExpected\n );\n\n if (amountReceived < minExpected) {\n revert SlippageError(amountReceived, minExpected);\n }\n\n emit RewardTokenSwapped(\n _swapToken,\n baseTokenAddress,\n tokenConfig.swapPlatform,\n balance,\n amountReceived\n );\n\n IERC20 baseToken = IERC20(baseTokenAddress);\n uint256 baseTokenBalance = baseToken.balanceOf(address(this));\n if (baseTokenBalance < amountReceived) {\n // Note: It's possible to bypass this check by transferring `baseToken`\n // directly to Harvester before calling the `harvestAndSwap`. However,\n // there's no incentive for an attacker to do that. Doing a balance diff\n // will increase the gas cost significantly\n revert BalanceMismatchAfterSwap(baseTokenBalance, amountReceived);\n }\n\n // Farmer only gets fee from the base amount they helped farm,\n // They do not get anything from anything that already was there\n // on the Harvester\n uint256 farmerFee = amountReceived.mulTruncateScale(\n tokenConfig.harvestRewardBps,\n 1e4\n );\n uint256 protocolYield = baseTokenBalance - farmerFee;\n\n baseToken.safeTransfer(rewardProceedsAddress, protocolYield);\n baseToken.safeTransfer(_rewardTo, farmerFee);\n emit RewardProceedsTransferred(\n baseTokenAddress,\n _rewardTo,\n protocolYield,\n farmerFee\n );\n }\n\n function _doSwap(\n SwapPlatform swapPlatform,\n address routerAddress,\n address rewardTokenAddress,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n if (swapPlatform == SwapPlatform.UniswapV2Compatible) {\n return\n _swapWithUniswapV2(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.UniswapV3) {\n return\n _swapWithUniswapV3(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.Balancer) {\n return\n _swapWithBalancer(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.Curve) {\n return\n _swapWithCurve(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else {\n // Should never be invoked since we catch invalid values\n // in the `setRewardTokenConfig` function before it's set\n revert InvalidSwapPlatform(swapPlatform);\n }\n }\n\n /**\n * @dev Swaps the token to `baseToken` with Uniswap V2\n *\n * @param routerAddress Uniswap V2 Router address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithUniswapV2(\n address routerAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n address[] memory path = uniswapV2Path[swapToken];\n\n uint256[] memory amounts = IUniswapV2Router(routerAddress)\n .swapExactTokensForTokens(\n amountIn,\n minAmountOut,\n path,\n address(this),\n block.timestamp\n );\n\n amountOut = amounts[amounts.length - 1];\n }\n\n /**\n * @dev Swaps the token to `baseToken` with Uniswap V3\n *\n * @param routerAddress Uniswap V3 Router address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithUniswapV3(\n address routerAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n bytes memory path = uniswapV3Path[swapToken];\n\n IUniswapV3Router.ExactInputParams memory params = IUniswapV3Router\n .ExactInputParams({\n path: path,\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: amountIn,\n amountOutMinimum: minAmountOut\n });\n amountOut = IUniswapV3Router(routerAddress).exactInput(params);\n }\n\n /**\n * @dev Swaps the token to `baseToken` on Balancer\n *\n * @param balancerVaultAddress BalancerVaultAddress\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithBalancer(\n address balancerVaultAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n bytes32 poolId = balancerPoolId[swapToken];\n\n IBalancerVault.SingleSwap memory singleSwap = IBalancerVault\n .SingleSwap({\n poolId: poolId,\n kind: IBalancerVault.SwapKind.GIVEN_IN,\n assetIn: swapToken,\n assetOut: baseTokenAddress,\n amount: amountIn,\n userData: hex\"\"\n });\n\n IBalancerVault.FundManagement memory fundMgmt = IBalancerVault\n .FundManagement({\n sender: address(this),\n fromInternalBalance: false,\n recipient: payable(address(this)),\n toInternalBalance: false\n });\n\n amountOut = IBalancerVault(balancerVaultAddress).swap(\n singleSwap,\n fundMgmt,\n minAmountOut,\n block.timestamp\n );\n }\n\n /**\n * @dev Swaps the token to `baseToken` on Curve\n *\n * @param poolAddress Curve Pool Address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithCurve(\n address poolAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n CurvePoolIndices memory indices = curvePoolIndices[swapToken];\n\n // Note: Not all CurvePools return the `amountOut`, make sure\n // to use only pool that do. Otherwise the swap would revert\n // always\n amountOut = ICurvePool(poolAddress).exchange(\n uint256(indices.rewardTokenIndex),\n uint256(indices.baseTokenIndex),\n amountIn,\n minAmountOut\n );\n }\n}\n" + }, + "contracts/harvest/Dripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\n/**\n * @title OUSD Dripper\n *\n * The dripper contract smooths out the yield from point-in-time yield events\n * and spreads the yield out over a configurable time period. This ensures a\n * continuous per block yield to makes users happy as their next rebase\n * amount is always moving up. Also, this makes historical day to day yields\n * smooth, rather than going from a near zero day, to a large APY day, then\n * back to a near zero day again.\n *\n *\n * Design notes\n * - USDT has a smaller resolution than the number of seconds\n * in a week, which can make per second payouts have a rounding error. However\n * the total effect is not large - cents per day, and this money is\n * not lost, just distributed in the future. While we could use a higher\n * decimal precision for the drip perSecond, we chose simpler code.\n * - By calculating the changing drip rates on collects only, harvests and yield\n * events don't have to call anything on this contract or pay any extra gas.\n * Collect() is already be paying for a single write, since it has to reset\n * the lastCollect time.\n * - By having a collectAndRebase method, and having our external systems call\n * that, the OUSD vault does not need any changes, not even to know the address\n * of the dripper.\n * - A rejected design was to retro-calculate the drip rate on each collect,\n * based on the balance at the time of the collect. While this would have\n * required less state, and would also have made the contract respond more quickly\n * to new income, it would break the predictability that is this contract's entire\n * purpose. If we did this, the amount of fundsAvailable() would make sharp increases\n * when funds were deposited.\n * - When the dripper recalculates the rate, it targets spending the balance over\n * the duration. This means that every time that collect is called, if no\n * new funds have been deposited the duration is being pushed back and the\n * rate decreases. This is expected, and ends up following a smoother but\n * longer curve the more collect() is called without incoming yield.\n *\n */\n\ncontract Dripper is Governable {\n using SafeERC20 for IERC20;\n\n struct Drip {\n uint64 lastCollect; // overflows 262 billion years after the sun dies\n uint192 perSecond; // drip rate per second\n }\n\n address immutable vault; // OUSD vault\n address immutable token; // token to drip out\n uint256 public dripDuration; // in seconds\n Drip public drip; // active drip parameters\n\n constructor(address _vault, address _token) {\n vault = _vault;\n token = _token;\n }\n\n /// @notice How much funds have dripped out already and are currently\n // available to be sent to the vault.\n /// @return The amount that would be sent if a collect was called\n function availableFunds() external view returns (uint256) {\n uint256 balance = IERC20(token).balanceOf(address(this));\n return _availableFunds(balance, drip);\n }\n\n /// @notice Collect all dripped funds and send to vault.\n /// Recalculate new drip rate.\n function collect() external {\n _collect();\n }\n\n /// @notice Collect all dripped funds, send to vault, recalculate new drip\n /// rate, and rebase OUSD.\n function collectAndRebase() external {\n _collect();\n IVault(vault).rebase();\n }\n\n /// @dev Change the drip duration. Governor only.\n /// @param _durationSeconds the number of seconds to drip out the entire\n /// balance over if no collects were called during that time.\n function setDripDuration(uint256 _durationSeconds)\n external\n virtual\n onlyGovernor\n {\n require(_durationSeconds > 0, \"duration must be non-zero\");\n dripDuration = _durationSeconds;\n _collect(); // duration change take immediate effect\n }\n\n /// @dev Transfer out ERC20 tokens held by the contract. Governor only.\n /// @param _asset ERC20 token address\n /// @param _amount amount to transfer\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /// @dev Calculate available funds by taking the lower of either the\n /// currently dripped out funds or the balance available.\n /// Uses passed in parameters to calculate with for gas savings.\n /// @param _balance current balance in contract\n /// @param _drip current drip parameters\n function _availableFunds(uint256 _balance, Drip memory _drip)\n internal\n view\n returns (uint256)\n {\n uint256 elapsed = block.timestamp - _drip.lastCollect;\n uint256 allowed = (elapsed * _drip.perSecond);\n return (allowed > _balance) ? _balance : allowed;\n }\n\n /// @dev Sends the currently dripped funds to be vault, and sets\n /// the new drip rate based on the new balance.\n function _collect() internal virtual {\n // Calculate send\n uint256 balance = IERC20(token).balanceOf(address(this));\n uint256 amountToSend = _availableFunds(balance, drip);\n uint256 remaining = balance - amountToSend;\n // Calculate new drip perSecond\n // Gas savings by setting entire struct at one time\n drip = Drip({\n perSecond: uint192(remaining / dripDuration),\n lastCollect: uint64(block.timestamp)\n });\n // Send funds\n IERC20(token).safeTransfer(vault, amountToSend);\n }\n\n /// @dev Transfer out all ERC20 held by the contract. Governor only.\n /// @param _asset ERC20 token address\n function transferAllToken(address _asset, address _receiver)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(\n _receiver,\n IERC20(_asset).balanceOf(address(this))\n );\n }\n}\n" + }, + "contracts/harvest/FixedRateDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Dripper } from \"./Dripper.sol\";\n\n/**\n * @title Fixed Rate Dripper\n *\n * Similar to the Dripper, Fixed Rate Dripper drips out yield per second.\n * However the Strategist decides the rate and it doesn't change after\n * a drip.\n *\n */\n\ncontract FixedRateDripper is Dripper {\n using SafeERC20 for IERC20;\n\n event DripRateUpdated(uint192 oldDripRate, uint192 newDripRate);\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vault).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n constructor(address _vault, address _token) Dripper(_vault, _token) {}\n\n /// @inheritdoc Dripper\n function setDripDuration(uint256) external virtual override {\n // Not used in FixedRateDripper\n revert(\"Drip duration disabled\");\n }\n\n /// @inheritdoc Dripper\n function _collect() internal virtual override {\n // Calculate amount to send\n uint256 balance = IERC20(token).balanceOf(address(this));\n uint256 amountToSend = _availableFunds(balance, drip);\n\n // Update timestamp\n drip.lastCollect = uint64(block.timestamp);\n\n // Send funds\n IERC20(token).safeTransfer(vault, amountToSend);\n }\n\n /**\n * @dev Sets the drip rate. Callable by Strategist or Governor.\n * Can be set to zero to stop dripper.\n * @param _perSecond Rate of WETH to drip per second\n */\n function setDripRate(uint192 _perSecond) external onlyGovernorOrStrategist {\n emit DripRateUpdated(_perSecond, drip.perSecond);\n\n /**\n * Note: It's important to call `_collect` before updating\n * the drip rate especially on a new proxy contract.\n * When `lastCollect` is not set/initialized, the elapsed\n * time would be calculated as `block.number` seconds,\n * resulting in a huge yield, if `collect` isn't called first.\n */\n // Collect at existing rate\n _collect();\n\n // Update rate\n drip.perSecond = _perSecond;\n }\n}\n" + }, + "contracts/harvest/Harvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractHarvester } from \"./AbstractHarvester.sol\";\n\ncontract Harvester is AbstractHarvester {\n constructor(address _vault, address _usdtAddress)\n AbstractHarvester(_vault, _usdtAddress)\n {}\n}\n" + }, + "contracts/harvest/OETHBaseHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { ISwapRouter } from \"../interfaces/aerodrome/ISwapRouter.sol\";\n\ncontract OETHBaseHarvester is Governable {\n using SafeERC20 for IERC20;\n\n IVault public immutable vault;\n IStrategy public immutable amoStrategy;\n IERC20 public immutable aero;\n IERC20 public immutable weth;\n ISwapRouter public immutable swapRouter;\n\n address public operatorAddr;\n\n // Similar sig to `AbstractHarvester.RewardTokenSwapped` for\n // future compatibility with monitoring\n event RewardTokenSwapped(\n address indexed rewardToken,\n address indexed swappedInto,\n uint8 swapPlatform,\n uint256 amountIn,\n uint256 amountOut\n );\n\n event OperatorChanged(address oldOperator, address newOperator);\n event YieldSent(address recipient, uint256 yield, uint256 fee);\n\n /**\n * @notice Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == vault.strategistAddr() || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @notice Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategistOrOperator() {\n require(\n msg.sender == operatorAddr ||\n msg.sender == vault.strategistAddr() ||\n isGovernor(),\n \"Caller is not the Operator or Strategist or Governor\"\n );\n _;\n }\n\n constructor(\n address _vault,\n address _amoStrategy,\n address _aero,\n address _weth,\n address _swapRouter\n ) {\n vault = IVault(_vault);\n amoStrategy = IStrategy(_amoStrategy);\n aero = IERC20(_aero);\n weth = IERC20(_weth);\n swapRouter = ISwapRouter(_swapRouter);\n }\n\n /**\n * @dev Changes the operator address which can call `harvest`\n * @param _operatorAddr New operator address\n */\n function setOperatorAddr(address _operatorAddr) external onlyGovernor {\n emit OperatorChanged(operatorAddr, _operatorAddr);\n operatorAddr = _operatorAddr;\n }\n\n /**\n * @notice Collects AERO from AMO strategy and\n * sends it to the Strategist multisig.\n * Anyone can call it.\n */\n function harvest() external onlyGovernorOrStrategistOrOperator {\n address strategistAddr = vault.strategistAddr();\n require(strategistAddr != address(0), \"Guardian address not set\");\n\n // Collect all AERO\n amoStrategy.collectRewardTokens();\n\n uint256 aeroBalance = aero.balanceOf(address(this));\n if (aeroBalance == 0) {\n // Do nothing if there's no AERO to transfer\n return;\n }\n\n // Transfer everything to Strategist\n aero.safeTransfer(strategistAddr, aeroBalance);\n }\n\n /**\n * @notice Harvests AERO from AMO strategy and then swaps some (or all)\n * of it into WETH to distribute yield and fee.\n * When `feeBps` is set to 10000 (100%), all WETH received is\n * sent to strategist.\n *\n * @param aeroToSwap Amount of AERO to swap\n * @param minWETHExpected Min. amount of WETH to expect\n * @param feeBps Performance fee bps (Sent to strategist)\n * @param sendYieldToDripper Sends yield to Dripper, if set to true.\n * Otherwise, to the Guardian\n */\n function harvestAndSwap(\n uint256 aeroToSwap,\n uint256 minWETHExpected,\n uint256 feeBps,\n bool sendYieldToDripper\n ) external onlyGovernorOrStrategist {\n address strategistAddr = vault.strategistAddr();\n require(strategistAddr != address(0), \"Guardian address not set\");\n\n // Yields can either be sent to the Dripper or Strategist\n address yieldRecipient = sendYieldToDripper\n ? vault.dripper()\n : strategistAddr;\n require(yieldRecipient != address(0), \"Yield recipient not set\");\n\n require(feeBps <= 10000, \"Invalid Fee Bps\");\n\n // Collect all AERO\n amoStrategy.collectRewardTokens();\n\n uint256 aeroBalance = aero.balanceOf(address(this));\n if (aeroBalance == 0) {\n // Do nothing if there's no AERO to transfer/swap\n return;\n }\n\n if (aeroToSwap > 0) {\n if (aeroBalance < aeroToSwap) {\n // Transfer in balance from the multisig as needed\n // slither-disable-next-line unchecked-transfer arbitrary-send-erc20\n aero.safeTransferFrom(\n strategistAddr,\n address(this),\n aeroToSwap - aeroBalance\n );\n }\n\n _doSwap(aeroToSwap, minWETHExpected);\n\n // Figure out AERO left in contract after swap\n aeroBalance = aero.balanceOf(address(this));\n }\n\n // Transfer out any leftover AERO after swap\n if (aeroBalance > 0) {\n aero.safeTransfer(strategistAddr, aeroBalance);\n }\n\n // Computes using all balance the contract holds,\n // not just the WETH received from swap. Use `transferToken`\n // if there's any WETH left that needs to be taken out\n uint256 availableWETHBalance = weth.balanceOf(address(this));\n // Computation rounds in favor of protocol\n uint256 fee = (availableWETHBalance * feeBps) / 10000;\n uint256 yield = availableWETHBalance - fee;\n\n // Transfer yield, if any\n if (yield > 0) {\n weth.safeTransfer(yieldRecipient, yield);\n }\n\n // Transfer fee to the Guardian, if any\n if (fee > 0) {\n weth.safeTransfer(strategistAddr, fee);\n }\n\n emit YieldSent(yieldRecipient, yield, fee);\n }\n\n /**\n * @notice Swaps AERO to WETH on Aerodrome\n * @param aeroToSwap Amount of AERO to swap\n * @param minWETHExpected Min. amount of WETH to expect\n */\n function _doSwap(uint256 aeroToSwap, uint256 minWETHExpected) internal {\n // Let the swap router move funds\n aero.approve(address(swapRouter), aeroToSwap);\n\n // Do the swap\n uint256 wethReceived = swapRouter.exactInputSingle(\n ISwapRouter.ExactInputSingleParams({\n tokenIn: address(aero),\n tokenOut: address(weth),\n tickSpacing: 200, // From AERO/WETH pool contract\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: aeroToSwap,\n amountOutMinimum: minWETHExpected,\n sqrtPriceLimitX96: 0\n })\n );\n\n emit RewardTokenSwapped(\n address(aero),\n address(weth),\n 0,\n aeroToSwap,\n wethReceived\n );\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * the contract, i.e. mistaken sends.\n * Also, allows to transfer any AERO left in the contract.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n virtual\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n}\n" + }, + "contracts/harvest/OETHDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Dripper } from \"./Dripper.sol\";\n\n/**\n * @title OETH Dripper Contract\n * @author Origin Protocol Inc\n */\ncontract OETHDripper is Dripper {\n constructor(address _vault, address _token) Dripper(_vault, _token) {}\n}\n" + }, + "contracts/harvest/OETHFixedRateDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { FixedRateDripper } from \"./FixedRateDripper.sol\";\n\n/**\n * @title OETH FixedRateDripper Contract\n * @author Origin Protocol Inc\n */\ncontract OETHFixedRateDripper is FixedRateDripper {\n constructor(address _vault, address _token)\n FixedRateDripper(_vault, _token)\n {}\n}\n" + }, + "contracts/harvest/OETHHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractHarvester } from \"./AbstractHarvester.sol\";\n\ncontract OETHHarvester is AbstractHarvester {\n constructor(address _vault, address _wethAddress)\n AbstractHarvester(_vault, _wethAddress)\n {}\n}\n" + }, + "contracts/harvest/OETHHarvesterSimple.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\n\n/// @title OETH Harvester Simple Contract\n/// @notice Contract to harvest rewards from strategies\n/// @author Origin Protocol Inc\ncontract OETHHarvesterSimple is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n ////////////////////////////////////////////////////\n /// --- CONSTANTS & IMMUTABLES\n ////////////////////////////////////////////////////\n /// @notice wrapped native token address (WETH or wS)\n address public immutable wrappedNativeToken;\n\n ////////////////////////////////////////////////////\n /// --- STORAGE\n ////////////////////////////////////////////////////\n /// @notice Dripper address\n address public dripper;\n\n /// @notice Mapping of supported strategies\n mapping(address => bool) public supportedStrategies;\n\n /// @notice Gap for upgrade safety\n uint256[48] private ___gap;\n\n ////////////////////////////////////////////////////\n /// --- EVENTS\n ////////////////////////////////////////////////////\n event Harvested(\n address indexed strategy,\n address token,\n uint256 amount,\n address indexed receiver\n );\n event SupportedStrategyUpdated(address strategy, bool status);\n event DripperUpdated(address dripper);\n\n ////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////\n constructor(address _wrappedNativeToken) {\n wrappedNativeToken = _wrappedNativeToken;\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /// @notice Initialize the contract\n function initialize() external onlyGovernor initializer {\n // Call it to set `initialized` to true and to prevent the implementation\n // from getting initialized in future through the proxy\n }\n\n ////////////////////////////////////////////////////\n /// --- MUTATIVE FUNCTIONS\n ////////////////////////////////////////////////////\n /// @notice Harvest rewards from a strategy and transfer to strategist or dripper\n /// @param _strategy Address of the strategy to harvest\n function harvestAndTransfer(address _strategy) external {\n _harvestAndTransfer(_strategy);\n }\n\n /// @notice Harvest rewards from multiple strategies and transfer to strategist or dripper\n /// @param _strategies Array of strategy addresses to harvest\n function harvestAndTransfer(address[] calldata _strategies) external {\n for (uint256 i = 0; i < _strategies.length; i++) {\n _harvestAndTransfer(_strategies[i]);\n }\n }\n\n /// @notice Internal logic to harvest rewards from a strategy\n function _harvestAndTransfer(address _strategy) internal virtual {\n // Ensure strategy is supported\n require(supportedStrategies[_strategy], \"Strategy not supported\");\n\n // Store locally for some gas savings\n address _strategist = strategistAddr;\n address _dripper = dripper;\n\n // Harvest rewards\n IStrategy(_strategy).collectRewardTokens();\n\n // Cache reward tokens\n address[] memory rewardTokens = IStrategy(_strategy)\n .getRewardTokenAddresses();\n\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; i++) {\n // Cache balance\n address token = rewardTokens[i];\n uint256 balance = IERC20(token).balanceOf(address(this));\n if (balance > 0) {\n // Determine receiver\n address receiver = token == wrappedNativeToken\n ? _dripper\n : _strategist;\n require(receiver != address(0), \"Invalid receiver\");\n\n // Transfer to the Strategist or the Dripper\n IERC20(token).safeTransfer(receiver, balance);\n emit Harvested(_strategy, token, balance, receiver);\n }\n }\n }\n\n ////////////////////////////////////////////////////\n /// --- GOVERNANCE\n ////////////////////////////////////////////////////\n /// @notice Set supported strategy\n /// @param _strategy Address of the strategy\n /// @param _isSupported Boolean indicating if strategy is supported\n function setSupportedStrategy(address _strategy, bool _isSupported)\n external\n onlyGovernorOrStrategist\n {\n require(_strategy != address(0), \"Invalid strategy\");\n supportedStrategies[_strategy] = _isSupported;\n emit SupportedStrategyUpdated(_strategy, _isSupported);\n }\n\n /// @notice Transfer tokens to strategist\n /// @param _asset Address of the token\n /// @param _amount Amount of tokens to transfer\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernorOrStrategist\n {\n IERC20(_asset).safeTransfer(strategistAddr, _amount);\n }\n\n /// @notice Set the dripper address\n /// @param _dripper Address of the dripper\n function setDripper(address _dripper) external onlyGovernor {\n _setDripper(_dripper);\n }\n\n /// @notice Internal logic to set the dripper address\n function _setDripper(address _dripper) internal {\n require(_dripper != address(0), \"Invalid dripper\");\n dripper = _dripper;\n emit DripperUpdated(_dripper);\n }\n}\n" + }, + "contracts/harvest/OSonicHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SuperOETHHarvester } from \"./SuperOETHHarvester.sol\";\n\ncontract OSonicHarvester is SuperOETHHarvester {\n /// @param _wrappedNativeToken Address of the native Wrapped S (wS) token\n constructor(address _wrappedNativeToken)\n SuperOETHHarvester(_wrappedNativeToken)\n {}\n}\n" + }, + "contracts/harvest/SuperOETHHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHHarvesterSimple, IERC20, IStrategy, SafeERC20 } from \"./OETHHarvesterSimple.sol\";\n\ncontract SuperOETHHarvester is OETHHarvesterSimple {\n using SafeERC20 for IERC20;\n\n constructor(address _wrappedNativeToken)\n OETHHarvesterSimple(_wrappedNativeToken)\n {}\n\n /// @inheritdoc OETHHarvesterSimple\n function _harvestAndTransfer(address _strategy) internal virtual override {\n // Ensure strategy is supported\n require(supportedStrategies[_strategy], \"Strategy not supported\");\n\n address receiver = strategistAddr;\n require(receiver != address(0), \"Invalid receiver\");\n\n // Harvest rewards\n IStrategy(_strategy).collectRewardTokens();\n\n // Cache reward tokens\n address[] memory rewardTokens = IStrategy(_strategy)\n .getRewardTokenAddresses();\n\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; i++) {\n // Cache balance\n address token = rewardTokens[i];\n uint256 balance = IERC20(token).balanceOf(address(this));\n if (balance > 0) {\n // Transfer everything to the strategist\n IERC20(token).safeTransfer(receiver, balance);\n emit Harvested(_strategy, token, balance, receiver);\n }\n }\n }\n}\n" + }, + "contracts/interfaces/aerodrome/IAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ICLPool } from \"./ICLPool.sol\";\n\ninterface IAMOStrategy {\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth);\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth);\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n error OutsideExpectedTickRange(int24 currentTick);\n\n function governor() external view returns (address);\n\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) external;\n\n function clPool() external view returns (ICLPool);\n\n function vaultAddress() external view returns (address);\n\n function poolWethShareVarianceAllowed() external view returns (uint256);\n\n function poolWethShare() external view returns (uint256);\n\n function tokenId() external view returns (uint256);\n\n function withdrawAll() external;\n\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external;\n\n function setWithdrawLiquidityShare(uint128 share) external;\n\n function lowerTick() external view returns (int24);\n\n function upperTick() external view returns (int24);\n\n function getPoolX96Price() external view returns (uint160 _sqrtRatioX96);\n\n function sqrtRatioX96TickLower() external view returns (uint160);\n\n function sqrtRatioX96TickHigher() external view returns (uint160);\n\n function tickSpacing() external view returns (int24);\n\n function allowedWethShareStart() external view returns (uint256);\n\n function allowedWethShareEnd() external view returns (uint256);\n\n function claimGovernance() external;\n\n function transferGovernance(address _governor) external;\n\n function getPositionPrincipal()\n external\n view\n returns (uint256 _amountWeth, uint256 _amountOethb);\n}\n" + }, + "contracts/interfaces/aerodrome/ICLGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\ninterface ICLGauge {\n /// @notice Returns the claimable rewards for a given account and tokenId\n /// @dev Throws if account is not the position owner\n /// @dev pool.updateRewardsGrowthGlobal() needs to be called first, to return the correct claimable rewards\n /// @param account The address of the user\n /// @param tokenId The tokenId of the position\n /// @return The amount of claimable reward\n function earned(address account, uint256 tokenId)\n external\n view\n returns (uint256);\n\n /// @notice Retrieve rewards for all tokens owned by an account\n /// @dev Throws if not called by the voter\n /// @param account The account of the user\n function getReward(address account) external;\n\n /// @notice Retrieve rewards for a tokenId\n /// @dev Throws if not called by the position owner\n /// @param tokenId The tokenId of the position\n function getReward(uint256 tokenId) external;\n\n /// @notice Notifies gauge of gauge rewards.\n /// @param amount Amount of gauge rewards (emissions) to notify. Must be greater than 604_800.\n function notifyRewardAmount(uint256 amount) external;\n\n /// @dev Notifies gauge of gauge rewards without distributing its fees.\n /// Assumes gauge reward tokens is 18 decimals.\n /// If not 18 decimals, rewardRate may have rounding issues.\n /// @param amount Amount of gauge rewards (emissions) to notify. Must be greater than 604_800.\n function notifyRewardWithoutClaim(uint256 amount) external;\n\n /// @notice Used to deposit a CL position into the gauge\n /// @notice Allows the user to receive emissions instead of fees\n /// @param tokenId The tokenId of the position\n function deposit(uint256 tokenId) external;\n\n /// @notice Used to withdraw a CL position from the gauge\n /// @notice Allows the user to receive fees instead of emissions\n /// @notice Outstanding emissions will be collected on withdrawal\n /// @param tokenId The tokenId of the position\n function withdraw(uint256 tokenId) external;\n\n // /// @notice Fetch all tokenIds staked by a given account\n // /// @param depositor The address of the user\n // /// @return The tokenIds of the staked positions\n // function stakedValues(address depositor) external view returns (uint256[] memory);\n\n // /// @notice Fetch a staked tokenId by index\n // /// @param depositor The address of the user\n // /// @param index The index of the staked tokenId\n // /// @return The tokenId of the staked position\n // function stakedByIndex(address depositor, uint256 index) external view returns (uint256);\n\n // /// @notice Check whether a position is staked in the gauge by a certain user\n // /// @param depositor The address of the user\n // /// @param tokenId The tokenId of the position\n // /// @return Whether the position is staked in the gauge\n // function stakedContains(address depositor, uint256 tokenId) external view returns (bool);\n\n // /// @notice The amount of positions staked in the gauge by a certain user\n // /// @param depositor The address of the user\n // /// @return The amount of positions staked in the gauge\n // function stakedLength(address depositor) external view returns (uint256);\n\n function feesVotingReward() external view returns (address);\n}\n" + }, + "contracts/interfaces/aerodrome/ICLPool.sol": { + "content": "pragma solidity >=0.5.0;\n\n/// @title The interface for a CL Pool\n/// @notice A CL pool facilitates swapping and automated market making between any two assets that strictly conform\n/// to the ERC20 specification\n/// @dev The pool interface is broken up into many smaller pieces\ninterface ICLPool {\n function slot0()\n external\n view\n returns (\n uint160 sqrtPriceX96,\n int24 tick,\n uint16 observationIndex,\n uint16 observationCardinality,\n uint16 observationCardinalityNext,\n bool unlocked\n );\n\n /// @notice The first of the two tokens of the pool, sorted by address\n /// @return The token contract address\n function token0() external view returns (address);\n\n /// @notice The second of the two tokens of the pool, sorted by address\n /// @return The token contract address\n function token1() external view returns (address);\n\n function tickSpacing() external view returns (int24);\n\n /// @notice The gauge corresponding to this pool\n /// @return The gauge contract address\n function gauge() external view returns (address);\n\n /// @notice The currently in range liquidity available to the pool\n /// @dev This value has no relationship to the total liquidity across all ticks\n /// @dev This value includes staked liquidity\n function liquidity() external view returns (uint128);\n\n /// @notice Look up information about a specific tick in the pool\n /// @param tick The tick to look up\n /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or\n /// tick upper,\n /// liquidityNet how much liquidity changes when the pool price crosses the tick,\n /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,\n /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,\n /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick\n /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from\n /// the current tick,\n /// secondsOutside the seconds spent on the other side of the tick from the current tick,\n /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise\n /// equal to false.\n /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.\n /// In addition, these values are only relative and must be used only in comparison to previous snapshots for\n /// a specific position.\n function ticks(int24 tick)\n external\n view\n returns (\n uint128 liquidityGross,\n int128 liquidityNet,\n uint256 feeGrowthOutside0X128,\n uint256 feeGrowthOutside1X128,\n int56 tickCumulativeOutside,\n uint160 secondsPerLiquidityOutsideX128,\n uint32 secondsOutside,\n bool initialized\n );\n}\n" + }, + "contracts/interfaces/aerodrome/INonfungiblePositionManager.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title Non-fungible token for positions\n/// @notice Wraps CL positions in a non-fungible token interface which allows for them to be transferred\n/// and authorized.\n// slither-disable-start erc20-interface\ninterface INonfungiblePositionManager {\n /**\n * @dev See {IERC721-approve}.\n */\n function approve(address to, uint256 tokenId) external;\n\n /**\n * @dev See {IERC721-getApproved}.\n */\n function getApproved(uint256 tokenId) external returns (address);\n\n /**\n * @dev See {IERC721-ownerOf}.\n */\n function ownerOf(uint256 tokenId) external view returns (address);\n\n /// @notice Returns the position information associated with a given token ID.\n /// @dev Throws if the token ID is not valid.\n /// @param tokenId The ID of the token that represents the position\n /// @return nonce The nonce for permits\n /// @return operator The address that is approved for spending\n /// @return token0 The address of the token0 for a specific pool\n /// @return token1 The address of the token1 for a specific pool\n /// @return tickSpacing The tick spacing associated with the pool\n /// @return tickLower The lower end of the tick range for the position\n /// @return tickUpper The higher end of the tick range for the position\n /// @return liquidity The liquidity of the position\n /// @return feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position\n /// @return feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position\n /// @return tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation\n /// @return tokensOwed1 The uncollected amount of token1 owed to the position as of the last computation\n function positions(uint256 tokenId)\n external\n view\n returns (\n uint96 nonce,\n address operator,\n address token0,\n address token1,\n int24 tickSpacing,\n int24 tickLower,\n int24 tickUpper,\n uint128 liquidity,\n uint256 feeGrowthInside0LastX128,\n uint256 feeGrowthInside1LastX128,\n uint128 tokensOwed0,\n uint128 tokensOwed1\n );\n\n struct MintParams {\n address token0;\n address token1;\n int24 tickSpacing;\n int24 tickLower;\n int24 tickUpper;\n uint256 amount0Desired;\n uint256 amount1Desired;\n uint256 amount0Min;\n uint256 amount1Min;\n address recipient;\n uint256 deadline;\n uint160 sqrtPriceX96;\n }\n\n /// @notice Creates a new position wrapped in a NFT\n /// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized\n /// a method does not exist, i.e. the pool is assumed to be initialized.\n /// @param params The params necessary to mint a position, encoded as `MintParams` in calldata\n /// @return tokenId The ID of the token that represents the minted position\n /// @return liquidity The amount of liquidity for this position\n /// @return amount0 The amount of token0\n /// @return amount1 The amount of token1\n function mint(MintParams calldata params)\n external\n payable\n returns (\n uint256 tokenId,\n uint128 liquidity,\n uint256 amount0,\n uint256 amount1\n );\n\n struct IncreaseLiquidityParams {\n uint256 tokenId;\n uint256 amount0Desired;\n uint256 amount1Desired;\n uint256 amount0Min;\n uint256 amount1Min;\n uint256 deadline;\n }\n\n /// @notice Increases the amount of liquidity in a position, with tokens paid by the `msg.sender`\n /// @param params tokenId The ID of the token for which liquidity is being increased,\n /// amount0Desired The desired amount of token0 to be spent,\n /// amount1Desired The desired amount of token1 to be spent,\n /// amount0Min The minimum amount of token0 to spend, which serves as a slippage check,\n /// amount1Min The minimum amount of token1 to spend, which serves as a slippage check,\n /// deadline The time by which the transaction must be included to effect the change\n /// @return liquidity The new liquidity amount as a result of the increase\n /// @return amount0 The amount of token0 to acheive resulting liquidity\n /// @return amount1 The amount of token1 to acheive resulting liquidity\n function increaseLiquidity(IncreaseLiquidityParams calldata params)\n external\n payable\n returns (\n uint128 liquidity,\n uint256 amount0,\n uint256 amount1\n );\n\n struct DecreaseLiquidityParams {\n uint256 tokenId;\n uint128 liquidity;\n uint256 amount0Min;\n uint256 amount1Min;\n uint256 deadline;\n }\n\n /// @notice Decreases the amount of liquidity in a position and accounts it to the position\n /// @param params tokenId The ID of the token for which liquidity is being decreased,\n /// amount The amount by which liquidity will be decreased,\n /// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity,\n /// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity,\n /// deadline The time by which the transaction must be included to effect the change\n /// @return amount0 The amount of token0 accounted to the position's tokens owed\n /// @return amount1 The amount of token1 accounted to the position's tokens owed\n /// @dev The use of this function can cause a loss to users of the NonfungiblePositionManager\n /// @dev for tokens that have very high decimals.\n /// @dev The amount of tokens necessary for the loss is: 3.4028237e+38.\n /// @dev This is equivalent to 1e20 value with 18 decimals.\n function decreaseLiquidity(DecreaseLiquidityParams calldata params)\n external\n payable\n returns (uint256 amount0, uint256 amount1);\n\n struct CollectParams {\n uint256 tokenId;\n address recipient;\n uint128 amount0Max;\n uint128 amount1Max;\n }\n\n /// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient\n /// @notice Used to update staked positions before deposit and withdraw\n /// @param params tokenId The ID of the NFT for which tokens are being collected,\n /// recipient The account that should receive the tokens,\n /// amount0Max The maximum amount of token0 to collect,\n /// amount1Max The maximum amount of token1 to collect\n /// @return amount0 The amount of fees collected in token0\n /// @return amount1 The amount of fees collected in token1\n function collect(CollectParams calldata params)\n external\n payable\n returns (uint256 amount0, uint256 amount1);\n\n /// @notice Burns a token ID, which deletes it from the NFT contract. The token must have 0 liquidity and all tokens\n /// must be collected first.\n /// @param tokenId The ID of the token that is being burned\n function burn(uint256 tokenId) external payable;\n\n /// @notice Sets a new Token Descriptor\n /// @param _tokenDescriptor Address of the new Token Descriptor to be chosen\n function setTokenDescriptor(address _tokenDescriptor) external;\n\n /// @notice Sets a new Owner address\n /// @param _owner Address of the new Owner to be chosen\n function setOwner(address _owner) external;\n}\n// slither-disable-end erc20-interface\n" + }, + "contracts/interfaces/aerodrome/IQuoterV2.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title QuoterV2 Interface\n/// @notice Supports quoting the calculated amounts from exact input or exact output swaps.\n/// @notice For each pool also tells you the number of initialized ticks crossed and the sqrt price of the\n/// pool after the swap.\n/// @dev These functions are not marked view because they rely on calling non-view functions and reverting\n/// to compute the result. They are also not gas efficient and should not be called on-chain.\ninterface IQuoterV2 {\n /// @notice Returns the amount out received for a given exact input swap without executing the swap\n /// @param path The path of the swap, i.e. each token pair and the pool tick spacing\n /// @param amountIn The amount of the first token to swap\n /// @return amountOut The amount of the last token that would be received\n /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path\n /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for\n /// each pool in the path\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactInput(bytes memory path, uint256 amountIn)\n external\n returns (\n uint256 amountOut,\n uint160[] memory sqrtPriceX96AfterList,\n uint32[] memory initializedTicksCrossedList,\n uint256 gasEstimate\n );\n\n struct QuoteExactInputSingleParams {\n address tokenIn;\n address tokenOut;\n uint256 amountIn;\n int24 tickSpacing;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Returns the amount out received for a given exact input but for a swap of a single pool\n /// @param params The params for the quote, encoded as `QuoteExactInputSingleParams`\n /// tokenIn The token being swapped in\n /// tokenOut The token being swapped out\n /// tickSpacing The tick spacing of the token pool to consider for the pair\n /// amountIn The desired input amount\n /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap\n /// @return amountOut The amount of `tokenOut` that would be received\n /// @return sqrtPriceX96After The sqrt price of the pool after the swap\n /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactInputSingle(QuoteExactInputSingleParams memory params)\n external\n returns (\n uint256 amountOut,\n uint160 sqrtPriceX96After,\n uint32 initializedTicksCrossed,\n uint256 gasEstimate\n );\n\n /// @notice Returns the amount in required for a given exact output swap without executing the swap\n /// @param path The path of the swap, i.e. each token pair and the pool tick spacing.\n /// Path must be provided in reverse order\n /// @param amountOut The amount of the last token to receive\n /// @return amountIn The amount of first token required to be paid\n /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path\n /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for\n /// each pool in the path\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactOutput(bytes memory path, uint256 amountOut)\n external\n returns (\n uint256 amountIn,\n uint160[] memory sqrtPriceX96AfterList,\n uint32[] memory initializedTicksCrossedList,\n uint256 gasEstimate\n );\n\n struct QuoteExactOutputSingleParams {\n address tokenIn;\n address tokenOut;\n uint256 amount;\n int24 tickSpacing;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Returns the amount in required to receive the given exact output amount but for a swap of a single pool\n /// @param params The params for the quote, encoded as `QuoteExactOutputSingleParams`\n /// tokenIn The token being swapped in\n /// tokenOut The token being swapped out\n /// tickSpacing The tick spacing of the token pool to consider for the pair\n /// amountOut The desired output amount\n /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap\n /// @return amountIn The amount required as the input for the swap in order to receive `amountOut`\n /// @return sqrtPriceX96After The sqrt price of the pool after the swap\n /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactOutputSingle(QuoteExactOutputSingleParams memory params)\n external\n returns (\n uint256 amountIn,\n uint160 sqrtPriceX96After,\n uint32 initializedTicksCrossed,\n uint256 gasEstimate\n );\n}\n" + }, + "contracts/interfaces/aerodrome/ISugarHelper.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\npragma abicoder v2;\n\nimport { INonfungiblePositionManager } from \"./INonfungiblePositionManager.sol\";\n\ninterface ISugarHelper {\n struct PopulatedTick {\n int24 tick;\n uint160 sqrtRatioX96;\n int128 liquidityNet;\n uint128 liquidityGross;\n }\n\n ///\n /// Wrappers for LiquidityAmounts\n ///\n\n function getAmountsForLiquidity(\n uint160 sqrtRatioX96,\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96,\n uint128 liquidity\n ) external pure returns (uint256 amount0, uint256 amount1);\n\n function getLiquidityForAmounts(\n uint256 amount0,\n uint256 amount1,\n uint160 sqrtRatioX96,\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96\n ) external pure returns (uint128 liquidity);\n\n /// @notice Computes the amount of token0 for a given amount of token1 and price range\n /// @param amount1 Amount of token1 to estimate liquidity\n /// @param pool Address of the pool to be used\n /// @param sqrtRatioX96 A sqrt price representing the current pool prices\n /// @param tickLow Lower tick boundary\n /// @param tickLow Upper tick boundary\n /// @dev If the given pool address is not the zero address, will fetch `sqrtRatioX96` from pool\n /// @return amount0 Estimated amount of token0\n function estimateAmount0(\n uint256 amount1,\n address pool,\n uint160 sqrtRatioX96,\n int24 tickLow,\n int24 tickHigh\n ) external view returns (uint256 amount0);\n\n /// @notice Computes the amount of token1 for a given amount of token0 and price range\n /// @param amount0 Amount of token0 to estimate liquidity\n /// @param pool Address of the pool to be used\n /// @param sqrtRatioX96 A sqrt price representing the current pool prices\n /// @param tickLow Lower tick boundary\n /// @param tickLow Upper tick boundary\n /// @dev If the given pool address is not the zero address, will fetch `sqrtRatioX96` from pool\n /// @return amount1 Estimated amount of token1\n function estimateAmount1(\n uint256 amount0,\n address pool,\n uint160 sqrtRatioX96,\n int24 tickLow,\n int24 tickHigh\n ) external view returns (uint256 amount1);\n\n ///\n /// Wrappers for PositionValue\n ///\n\n function principal(\n INonfungiblePositionManager positionManager,\n uint256 tokenId,\n uint160 sqrtRatioX96\n ) external view returns (uint256 amount0, uint256 amount1);\n\n function fees(INonfungiblePositionManager positionManager, uint256 tokenId)\n external\n view\n returns (uint256 amount0, uint256 amount1);\n\n ///\n /// Wrappers for TickMath\n ///\n\n function getSqrtRatioAtTick(int24 tick)\n external\n pure\n returns (uint160 sqrtRatioX96);\n\n function getTickAtSqrtRatio(uint160 sqrtRatioX96)\n external\n pure\n returns (int24 tick);\n\n /// @notice Fetches Tick Data for all populated Ticks in given bitmaps\n /// @param pool Address of the pool from which to fetch data\n /// @param startTick Tick from which the first bitmap will be fetched\n /// @dev The number of bitmaps fetched by this function should always be `MAX_BITMAPS`,\n /// unless there are less than `MAX_BITMAPS` left to iterate through\n /// @return populatedTicks Array of all Populated Ticks in the provided bitmaps\n function getPopulatedTicks(address pool, int24 startTick)\n external\n view\n returns (PopulatedTick[] memory populatedTicks);\n}\n" + }, + "contracts/interfaces/aerodrome/ISwapRouter.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title Router token swapping functionality\n/// @notice Functions for swapping tokens via CL\ninterface ISwapRouter {\n struct ExactInputSingleParams {\n address tokenIn;\n address tokenOut;\n int24 tickSpacing;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInputSingle(ExactInputSingleParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactOutputSingleParams {\n address tokenIn;\n address tokenOut;\n int24 tickSpacing;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutputSingle(ExactOutputSingleParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n\n struct ExactOutputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutput(ExactOutputParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n}\n" + }, + "contracts/interfaces/balancer/IBalancerVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"../../utils/InitializableAbstractStrategy.sol\";\n\ninterface IBalancerVault {\n enum WeightedPoolJoinKind {\n INIT,\n EXACT_TOKENS_IN_FOR_BPT_OUT,\n TOKEN_IN_FOR_EXACT_BPT_OUT,\n ALL_TOKENS_IN_FOR_EXACT_BPT_OUT,\n ADD_TOKEN\n }\n\n enum WeightedPoolExitKind {\n EXACT_BPT_IN_FOR_ONE_TOKEN_OUT,\n EXACT_BPT_IN_FOR_TOKENS_OUT,\n BPT_IN_FOR_EXACT_TOKENS_OUT,\n REMOVE_TOKEN\n }\n\n /**\n * @dev Called by users to join a Pool, which transfers tokens from `sender` into the Pool's balance. This will\n * trigger custom Pool behavior, which will typically grant something in return to `recipient` - often tokenized\n * Pool shares.\n *\n * If the caller is not `sender`, it must be an authorized relayer for them.\n *\n * The `assets` and `maxAmountsIn` arrays must have the same length, and each entry indicates the maximum amount\n * to send for each asset. The amounts to send are decided by the Pool and not the Vault: it just enforces\n * these maximums.\n *\n * If joining a Pool that holds WETH, it is possible to send ETH directly: the Vault will do the wrapping. To enable\n * this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead of the\n * WETH address. Note that it is not possible to combine ETH and WETH in the same join. Any excess ETH will be sent\n * back to the caller (not the sender, which is important for relayers).\n *\n * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when\n * interacting with Pools that register and deregister tokens frequently. If sending ETH however, the array must be\n * sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the final\n * `assets` array might not be sorted. Pools with no registered tokens cannot be joined.\n *\n * If `fromInternalBalance` is true, the caller's Internal Balance will be preferred: ERC20 transfers will only\n * be made for the difference between the requested amount and Internal Balance (if any). Note that ETH cannot be\n * withdrawn from Internal Balance: attempting to do so will trigger a revert.\n *\n * This causes the Vault to call the `IBasePool.onJoinPool` hook on the Pool's contract, where Pools implement\n * their own custom logic. This typically requires additional information from the user (such as the expected number\n * of Pool shares). This can be encoded in the `userData` argument, which is ignored by the Vault and passed\n * directly to the Pool's contract, as is `recipient`.\n *\n * Emits a `PoolBalanceChanged` event.\n */\n function joinPool(\n bytes32 poolId,\n address sender,\n address recipient,\n JoinPoolRequest memory request\n ) external payable;\n\n struct JoinPoolRequest {\n address[] assets;\n uint256[] maxAmountsIn;\n bytes userData;\n bool fromInternalBalance;\n }\n\n /**\n * @dev Called by users to exit a Pool, which transfers tokens from the Pool's balance to `recipient`. This will\n * trigger custom Pool behavior, which will typically ask for something in return from `sender` - often tokenized\n * Pool shares. The amount of tokens that can be withdrawn is limited by the Pool's `cash` balance (see\n * `getPoolTokenInfo`).\n *\n * If the caller is not `sender`, it must be an authorized relayer for them.\n *\n * The `tokens` and `minAmountsOut` arrays must have the same length, and each entry in these indicates the minimum\n * token amount to receive for each token contract. The amounts to send are decided by the Pool and not the Vault:\n * it just enforces these minimums.\n *\n * If exiting a Pool that holds WETH, it is possible to receive ETH directly: the Vault will do the unwrapping. To\n * enable this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead\n * of the WETH address. Note that it is not possible to combine ETH and WETH in the same exit.\n *\n * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when\n * interacting with Pools that register and deregister tokens frequently. If receiving ETH however, the array must\n * be sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the\n * final `assets` array might not be sorted. Pools with no registered tokens cannot be exited.\n *\n * If `toInternalBalance` is true, the tokens will be deposited to `recipient`'s Internal Balance. Otherwise,\n * an ERC20 transfer will be performed. Note that ETH cannot be deposited to Internal Balance: attempting to\n * do so will trigger a revert.\n *\n * `minAmountsOut` is the minimum amount of tokens the user expects to get out of the Pool, for each token in the\n * `tokens` array. This array must match the Pool's registered tokens.\n *\n * This causes the Vault to call the `IBasePool.onExitPool` hook on the Pool's contract, where Pools implement\n * their own custom logic. This typically requires additional information from the user (such as the expected number\n * of Pool shares to return). This can be encoded in the `userData` argument, which is ignored by the Vault and\n * passed directly to the Pool's contract.\n *\n * Emits a `PoolBalanceChanged` event.\n */\n function exitPool(\n bytes32 poolId,\n address sender,\n address payable recipient,\n ExitPoolRequest memory request\n ) external;\n\n struct ExitPoolRequest {\n address[] assets;\n uint256[] minAmountsOut;\n bytes userData;\n bool toInternalBalance;\n }\n\n /**\n * @dev Returns a Pool's registered tokens, the total balance for each, and the latest block when *any* of\n * the tokens' `balances` changed.\n *\n * The order of the `tokens` array is the same order that will be used in `joinPool`, `exitPool`, as well as in all\n * Pool hooks (where applicable). Calls to `registerTokens` and `deregisterTokens` may change this order.\n *\n * If a Pool only registers tokens once, and these are sorted in ascending order, they will be stored in the same\n * order as passed to `registerTokens`.\n *\n * Total balances include both tokens held by the Vault and those withdrawn by the Pool's Asset Managers. These are\n * the amounts used by joins, exits and swaps. For a detailed breakdown of token balances, use `getPoolTokenInfo`\n * instead.\n */\n function getPoolTokens(bytes32 poolId)\n external\n view\n returns (\n IERC20[] memory tokens,\n uint256[] memory balances,\n uint256 lastChangeBlock\n );\n\n /**\n * @dev Performs a set of user balance operations, which involve Internal Balance (deposit, withdraw or transfer)\n * and plain ERC20 transfers using the Vault's allowance. This last feature is particularly useful for relayers, as\n * it lets integrators reuse a user's Vault allowance.\n *\n * For each operation, if the caller is not `sender`, it must be an authorized relayer for them.\n */\n function manageUserBalance(UserBalanceOp[] memory ops) external payable;\n\n struct UserBalanceOp {\n UserBalanceOpKind kind;\n address asset;\n uint256 amount;\n address sender;\n address payable recipient;\n }\n\n enum UserBalanceOpKind {\n DEPOSIT_INTERNAL,\n WITHDRAW_INTERNAL,\n TRANSFER_INTERNAL,\n TRANSFER_EXTERNAL\n }\n\n enum SwapKind {\n GIVEN_IN,\n GIVEN_OUT\n }\n\n struct SingleSwap {\n bytes32 poolId;\n SwapKind kind;\n address assetIn;\n address assetOut;\n uint256 amount;\n bytes userData;\n }\n\n struct FundManagement {\n address sender;\n bool fromInternalBalance;\n address payable recipient;\n bool toInternalBalance;\n }\n\n function swap(\n SingleSwap calldata singleSwap,\n FundManagement calldata funds,\n uint256 limit,\n uint256 deadline\n ) external returns (uint256 amountCalculated);\n\n function getPoolTokenInfo(bytes32 poolId, address token)\n external\n view\n returns (\n uint256 cash,\n uint256 managed,\n uint256 lastChangeBlock,\n address assetManager\n );\n}\n" + }, + "contracts/interfaces/balancer/IMetaStablePool.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IRateProvider } from \"./IRateProvider.sol\";\n\ninterface IMetaStablePool {\n function getRateProviders()\n external\n view\n returns (IRateProvider[] memory providers);\n}\n" + }, + "contracts/interfaces/balancer/IOracleWeightedPool.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// The three values that can be queried:\n//\n// - PAIR_PRICE: the price of the tokens in the Pool, expressed as the price of the second token in units of the\n// first token. For example, if token A is worth $2, and token B is worth $4, the pair price will be 2.0.\n// Note that the price is computed *including* the tokens decimals. This means that the pair price of a Pool with\n// DAI and USDC will be close to 1.0, despite DAI having 18 decimals and USDC 6.\n//\n// - BPT_PRICE: the price of the Pool share token (BPT), in units of the first token.\n// Note that the price is computed *including* the tokens decimals. This means that the BPT price of a Pool with\n// USDC in which BPT is worth $5 will be 5.0, despite the BPT having 18 decimals and USDC 6.\n//\n// - INVARIANT: the value of the Pool's invariant, which serves as a measure of its liquidity.\nenum Variable {\n PAIR_PRICE,\n BPT_PRICE,\n INVARIANT\n}\n\n/**\n * @dev Information for a Time Weighted Average query.\n *\n * Each query computes the average over a window of duration `secs` seconds that ended `ago` seconds ago. For\n * example, the average over the past 30 minutes is computed by settings secs to 1800 and ago to 0. If secs is 1800\n * and ago is 1800 as well, the average between 60 and 30 minutes ago is computed instead.\n */\nstruct OracleAverageQuery {\n Variable variable;\n uint256 secs;\n uint256 ago;\n}\n\ninterface IOracleWeightedPool {\n /**\n * @dev Returns the time average weighted price corresponding to each of `queries`. Prices are represented as 18\n * decimal fixed point values.\n */\n function getTimeWeightedAverage(OracleAverageQuery[] memory queries)\n external\n view\n returns (uint256[] memory results);\n}\n" + }, + "contracts/interfaces/balancer/IRateProvider.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity ^0.8.0;\n\ninterface IRateProvider {\n function getRate() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/chainlink/AggregatorV3Interface.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorV3Interface {\n function decimals() external view returns (uint8);\n\n function description() external view returns (string memory);\n\n function version() external view returns (uint256);\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(uint80 _roundId)\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n\n function latestRoundData()\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof,\n bytes calldata validatorPubKeyProof\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint64 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes32 pubKeyHash,\n bytes calldata firstPendingDepositProof\n ) external view returns (bool isEmptyDepositQueue);\n}\n" + }, + "contracts/interfaces/IBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBuyback {\n function swap() external;\n}\n" + }, + "contracts/interfaces/ICampaignRemoteManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICampaignRemoteManager {\n function createCampaign(\n CampaignCreationParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n function manageCampaign(\n CampaignManagementParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n function closeCampaign(\n CampaignClosingParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n struct CampaignCreationParams {\n uint256 chainId;\n address gauge;\n address manager;\n address rewardToken;\n uint8 numberOfPeriods;\n uint256 maxRewardPerVote;\n uint256 totalRewardAmount;\n address[] addresses;\n address hook;\n bool isWhitelist;\n }\n\n struct CampaignManagementParams {\n uint256 campaignId;\n address rewardToken;\n uint8 numberOfPeriods;\n uint256 totalRewardAmount;\n uint256 maxRewardPerVote;\n }\n\n struct CampaignClosingParams {\n uint256 campaignId;\n }\n}\n" + }, + "contracts/interfaces/IChildLiquidityGaugeFactory.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IChildLiquidityGaugeFactory {\n event DeployedGauge(\n address indexed _implementation,\n address indexed _lp_token,\n address indexed _deployer,\n bytes32 _salt,\n address _gauge\n );\n event Minted(\n address indexed _user,\n address indexed _gauge,\n uint256 _new_total\n );\n event TransferOwnership(address _old_owner, address _new_owner);\n event UpdateCallProxy(address _old_call_proxy, address _new_call_proxy);\n event UpdateImplementation(\n address _old_implementation,\n address _new_implementation\n );\n event UpdateManager(address _manager);\n event UpdateMirrored(address indexed _gauge, bool _mirrored);\n event UpdateRoot(address _factory, address _implementation);\n event UpdateVotingEscrow(\n address _old_voting_escrow,\n address _new_voting_escrow\n );\n\n function accept_transfer_ownership() external;\n\n function call_proxy() external view returns (address);\n\n function commit_transfer_ownership(address _future_owner) external;\n\n function crv() external view returns (address);\n\n function deploy_gauge(address _lp_token, bytes32 _salt)\n external\n returns (address);\n\n function deploy_gauge(\n address _lp_token,\n bytes32 _salt,\n address _manager\n ) external returns (address);\n\n function future_owner() external view returns (address);\n\n function gauge_data(address arg0) external view returns (uint256);\n\n function get_gauge(uint256 arg0) external view returns (address);\n\n function get_gauge_count() external view returns (uint256);\n\n function get_gauge_from_lp_token(address arg0)\n external\n view\n returns (address);\n\n function get_implementation() external view returns (address);\n\n function is_mirrored(address _gauge) external view returns (bool);\n\n function is_valid_gauge(address _gauge) external view returns (bool);\n\n function last_request(address _gauge) external view returns (uint256);\n\n function manager() external view returns (address);\n\n function mint(address _gauge) external;\n\n function mint_many(address[32] memory _gauges) external;\n\n function minted(address arg0, address arg1) external view returns (uint256);\n\n function owner() external view returns (address);\n\n function root_factory() external view returns (address);\n\n function root_implementation() external view returns (address);\n\n function set_call_proxy(address _new_call_proxy) external;\n\n function set_crv(address _crv) external;\n\n function set_implementation(address _implementation) external;\n\n function set_manager(address _new_manager) external;\n\n function set_mirrored(address _gauge, bool _mirrored) external;\n\n function set_root(address _factory, address _implementation) external;\n\n function set_voting_escrow(address _voting_escrow) external;\n\n function version() external view returns (string memory);\n\n function voting_escrow() external view returns (address);\n}\n" + }, + "contracts/interfaces/IComptroller.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IComptroller {\n // Claim all the COMP accrued by specific holders in specific markets for their supplies and/or borrows\n function claimComp(\n address[] memory holders,\n address[] memory cTokens,\n bool borrowers,\n bool suppliers\n ) external;\n\n function oracle() external view returns (address);\n}\n" + }, + "contracts/interfaces/IConsolidation.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IConsolidationSource {\n function confirmConsolidation()\n external\n returns (uint256 consolidationCount);\n}\n\ninterface IConsolidationTarget {\n function requestConsolidation(\n bytes32 lastSourcePubKeyHash,\n bytes32 targetPubKeyHash\n ) external;\n}\n" + }, + "contracts/interfaces/ICurveLiquidityGaugeV6.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveLiquidityGaugeV6 {\n event ApplyOwnership(address admin);\n event Approval(\n address indexed _owner,\n address indexed _spender,\n uint256 _value\n );\n event CommitOwnership(address admin);\n event Deposit(address indexed provider, uint256 value);\n event SetGaugeManager(address _gauge_manager);\n event Transfer(address indexed _from, address indexed _to, uint256 _value);\n event UpdateLiquidityLimit(\n address indexed user,\n uint256 original_balance,\n uint256 original_supply,\n uint256 working_balance,\n uint256 working_supply\n );\n event Withdraw(address indexed provider, uint256 value);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_reward(address _reward_token, address _distributor) external;\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function claim_rewards() external;\n\n function claim_rewards(address _addr) external;\n\n function claim_rewards(address _addr, address _receiver) external;\n\n function claimable_reward(address _user, address _reward_token)\n external\n view\n returns (uint256);\n\n function claimable_tokens(address addr) external returns (uint256);\n\n function claimed_reward(address _addr, address _token)\n external\n view\n returns (uint256);\n\n function decimals() external view returns (uint256);\n\n function decreaseAllowance(address _spender, uint256 _subtracted_value)\n external\n returns (bool);\n\n function deposit(uint256 _value) external;\n\n function deposit(uint256 _value, address _addr) external;\n\n function deposit(\n uint256 _value,\n address _addr,\n bool _claim_rewards\n ) external;\n\n function deposit_reward_token(address _reward_token, uint256 _amount)\n external;\n\n function deposit_reward_token(\n address _reward_token,\n uint256 _amount,\n uint256 _epoch\n ) external;\n\n function factory() external view returns (address);\n\n function future_epoch_time() external view returns (uint256);\n\n function increaseAllowance(address _spender, uint256 _added_value)\n external\n returns (bool);\n\n function inflation_rate() external view returns (uint256);\n\n function integrate_checkpoint() external view returns (uint256);\n\n function integrate_checkpoint_of(address arg0)\n external\n view\n returns (uint256);\n\n function integrate_fraction(address arg0) external view returns (uint256);\n\n function integrate_inv_supply(uint256 arg0) external view returns (uint256);\n\n function integrate_inv_supply_of(address arg0)\n external\n view\n returns (uint256);\n\n function is_killed() external view returns (bool);\n\n function kick(address addr) external;\n\n function lp_token() external view returns (address);\n\n function manager() external view returns (address);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function period() external view returns (int128);\n\n function period_timestamp(uint256 arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function reward_count() external view returns (uint256);\n\n function reward_integral_for(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function reward_tokens(uint256 arg0) external view returns (address);\n\n function rewards_receiver(address arg0) external view returns (address);\n\n function salt() external view returns (bytes32);\n\n function set_gauge_manager(address _gauge_manager) external;\n\n function set_killed(bool _is_killed) external;\n\n function set_reward_distributor(address _reward_token, address _distributor)\n external;\n\n function set_rewards_receiver(address _receiver) external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function user_checkpoint(address addr) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw(uint256 _value) external;\n\n function withdraw(uint256 _value, bool _claim_rewards) external;\n\n function working_balances(address arg0) external view returns (uint256);\n\n function working_supply() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/ICurveMinter.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveMinter {\n event Minted(address indexed recipient, address gauge, uint256 minted);\n\n function allowed_to_mint_for(address arg0, address arg1)\n external\n view\n returns (bool);\n\n function controller() external view returns (address);\n\n function mint(address gauge_addr) external;\n\n function mint_for(address gauge_addr, address _for) external;\n\n function mint_many(address[8] memory gauge_addrs) external;\n\n function minted(address arg0, address arg1) external view returns (uint256);\n\n function toggle_approve_mint(address minting_user) external;\n\n function token() external view returns (address);\n}\n" + }, + "contracts/interfaces/ICurveStableSwapNG.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveStableSwapNG {\n event AddLiquidity(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event ApplyNewFee(uint256 fee, uint256 offpeg_fee_multiplier);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event RampA(\n uint256 old_A,\n uint256 new_A,\n uint256 initial_time,\n uint256 future_time\n );\n event RemoveLiquidity(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 token_supply\n );\n event RemoveLiquidityImbalance(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event RemoveLiquidityOne(\n address indexed provider,\n int128 token_id,\n uint256 token_amount,\n uint256 coin_amount,\n uint256 token_supply\n );\n event SetNewMATime(uint256 ma_exp_time, uint256 D_ma_time);\n event StopRampA(uint256 A, uint256 t);\n event TokenExchange(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event TokenExchangeUnderlying(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event Transfer(\n address indexed sender,\n address indexed receiver,\n uint256 value\n );\n\n function A() external view returns (uint256);\n\n function A_precise() external view returns (uint256);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function D_ma_time() external view returns (uint256);\n\n function D_oracle() external view returns (uint256);\n\n function N_COINS() external view returns (uint256);\n\n function add_liquidity(uint256[] memory _amounts, uint256 _min_mint_amount)\n external\n returns (uint256);\n\n function add_liquidity(\n uint256[] memory _amounts,\n uint256 _min_mint_amount,\n address _receiver\n ) external returns (uint256);\n\n function admin_balances(uint256 arg0) external view returns (uint256);\n\n function admin_fee() external view returns (uint256);\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function balances(uint256 i) external view returns (uint256);\n\n function calc_token_amount(uint256[] memory _amounts, bool _is_deposit)\n external\n view\n returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _burn_amount, int128 i)\n external\n view\n returns (uint256);\n\n function coins(uint256 arg0) external view returns (address);\n\n function decimals() external view returns (uint8);\n\n function dynamic_fee(int128 i, int128 j) external view returns (uint256);\n\n function ema_price(uint256 i) external view returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external returns (uint256);\n\n function exchange_received(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external returns (uint256);\n\n function exchange_received(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external returns (uint256);\n\n function fee() external view returns (uint256);\n\n function future_A() external view returns (uint256);\n\n function future_A_time() external view returns (uint256);\n\n function get_balances() external view returns (uint256[] memory);\n\n function get_dx(\n int128 i,\n int128 j,\n uint256 dy\n ) external view returns (uint256);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n\n function get_p(uint256 i) external view returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function initial_A() external view returns (uint256);\n\n function initial_A_time() external view returns (uint256);\n\n function last_price(uint256 i) external view returns (uint256);\n\n function ma_exp_time() external view returns (uint256);\n\n function ma_last_time() external view returns (uint256);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function offpeg_fee_multiplier() external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function price_oracle(uint256 i) external view returns (uint256);\n\n function ramp_A(uint256 _future_A, uint256 _future_time) external;\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts\n ) external returns (uint256[] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts,\n address _receiver\n ) external returns (uint256[] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts,\n address _receiver,\n bool _claim_admin_fees\n ) external returns (uint256[] memory);\n\n function remove_liquidity_imbalance(\n uint256[] memory _amounts,\n uint256 _max_burn_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[] memory _amounts,\n uint256 _max_burn_amount,\n address _receiver\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received,\n address _receiver\n ) external returns (uint256);\n\n function salt() external view returns (bytes32);\n\n function set_ma_exp_time(uint256 _ma_exp_time, uint256 _D_ma_time) external;\n\n function set_new_fee(uint256 _new_fee, uint256 _new_offpeg_fee_multiplier)\n external;\n\n function stop_ramp_A() external;\n\n function stored_rates() external view returns (uint256[] memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw_admin_fees() external;\n}\n" + }, + "contracts/interfaces/ICurveXChainLiquidityGauge.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveXChainLiquidityGauge {\n event Approval(\n address indexed _owner,\n address indexed _spender,\n uint256 _value\n );\n event Deposit(address indexed provider, uint256 value);\n event SetGaugeManager(address _gauge_manager);\n event Transfer(address indexed _from, address indexed _to, uint256 _value);\n event UpdateLiquidityLimit(\n address indexed user,\n uint256 original_balance,\n uint256 original_supply,\n uint256 working_balance,\n uint256 working_supply\n );\n event Withdraw(address indexed provider, uint256 value);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_reward(address _reward_token, address _distributor) external;\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function claim_rewards() external;\n\n function claim_rewards(address _addr) external;\n\n function claim_rewards(address _addr, address _receiver) external;\n\n function claimable_reward(address _user, address _reward_token)\n external\n view\n returns (uint256);\n\n function claimable_tokens(address addr) external returns (uint256);\n\n function claimed_reward(address _addr, address _token)\n external\n view\n returns (uint256);\n\n function decimals() external view returns (uint256);\n\n function decreaseAllowance(address _spender, uint256 _subtracted_value)\n external\n returns (bool);\n\n function deposit(uint256 _value) external;\n\n function deposit(uint256 _value, address _addr) external;\n\n function deposit(\n uint256 _value,\n address _addr,\n bool _claim_rewards\n ) external;\n\n function deposit_reward_token(address _reward_token, uint256 _amount)\n external;\n\n function deposit_reward_token(\n address _reward_token,\n uint256 _amount,\n uint256 _epoch\n ) external;\n\n function factory() external view returns (address);\n\n function increaseAllowance(address _spender, uint256 _added_value)\n external\n returns (bool);\n\n function inflation_rate(uint256 arg0) external view returns (uint256);\n\n function initialize(\n address _lp_token,\n address _root,\n address _manager\n ) external;\n\n function integrate_checkpoint() external view returns (uint256);\n\n function integrate_checkpoint_of(address arg0)\n external\n view\n returns (uint256);\n\n function integrate_fraction(address arg0) external view returns (uint256);\n\n function integrate_inv_supply(int128 arg0) external view returns (uint256);\n\n function integrate_inv_supply_of(address arg0)\n external\n view\n returns (uint256);\n\n function is_killed() external view returns (bool);\n\n function lp_token() external view returns (address);\n\n function manager() external view returns (address);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function period() external view returns (int128);\n\n function period_timestamp(int128 arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function recover_remaining(address _reward_token) external;\n\n function reward_count() external view returns (uint256);\n\n function reward_integral_for(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function reward_remaining(address arg0) external view returns (uint256);\n\n function reward_tokens(uint256 arg0) external view returns (address);\n\n function rewards_receiver(address arg0) external view returns (address);\n\n function root_gauge() external view returns (address);\n\n function set_gauge_manager(address _gauge_manager) external;\n\n function set_killed(bool _is_killed) external;\n\n function set_manager(address _gauge_manager) external;\n\n function set_reward_distributor(address _reward_token, address _distributor)\n external;\n\n function set_rewards_receiver(address _receiver) external;\n\n function set_root_gauge(address _root) external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function update_voting_escrow() external;\n\n function user_checkpoint(address addr) external returns (bool);\n\n function version() external view returns (string memory);\n\n function voting_escrow() external view returns (address);\n\n function withdraw(uint256 _value) external;\n\n function withdraw(uint256 _value, bool _claim_rewards) external;\n\n function withdraw(\n uint256 _value,\n bool _claim_rewards,\n address _receiver\n ) external;\n\n function working_balances(address arg0) external view returns (uint256);\n\n function working_supply() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/ICVXLocker.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface ICVXLocker {\n function lock(\n address _account,\n uint256 _amount,\n uint256 _spendRatio\n ) external;\n\n function lockedBalanceOf(address _account) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/IDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IDripper {\n /// @notice How much funds have dripped out already and are currently\n // available to be sent to the vault.\n /// @return The amount that would be sent if a collect was called\n function availableFunds() external view returns (uint256);\n\n /// @notice Collect all dripped funds and send to vault.\n /// Recalculate new drip rate.\n function collect() external;\n\n /// @notice Collect all dripped funds, send to vault, recalculate new drip\n /// rate, and rebase mToken.\n function collectAndRebase() external;\n\n /// @notice Change the drip duration. Governor only.\n /// @param _durationSeconds the number of seconds to drip out the entire\n /// balance over if no collects were called during that time.\n function setDripDuration(uint256 _durationSeconds) external;\n\n /// @dev Transfer out ERC20 tokens held by the contract. Governor only.\n /// @param _asset ERC20 token address\n /// @param _amount amount to transfer\n function transferToken(address _asset, uint256 _amount) external;\n}\n" + }, + "contracts/interfaces/IEthUsdOracle.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IEthUsdOracle {\n /**\n * @notice Returns ETH price in USD.\n * @return Price in USD with 6 decimal digits.\n */\n function ethUsdPrice() external view returns (uint256);\n\n /**\n * @notice Returns token price in USD.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in USD with 6 decimal digits.\n */\n function tokUsdPrice(string calldata symbol)\n external\n view\n returns (uint256);\n\n /**\n * @notice Returns the asset price in ETH.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in ETH with 8 decimal digits.\n */\n function tokEthPrice(string calldata symbol)\n external\n view\n returns (uint256);\n}\n\ninterface IViewEthUsdOracle {\n /**\n * @notice Returns ETH price in USD.\n * @return Price in USD with 6 decimal digits.\n */\n function ethUsdPrice() external view returns (uint256);\n\n /**\n * @notice Returns token price in USD.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in USD with 6 decimal digits.\n */\n function tokUsdPrice(string calldata symbol)\n external\n view\n returns (uint256);\n\n /**\n * @notice Returns the asset price in ETH.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in ETH with 8 decimal digits.\n */\n function tokEthPrice(string calldata symbol)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/IFraxETHMinter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IFraxETHMinter {\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares);\n}\n" + }, + "contracts/interfaces/IGetExchangeRateToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IGetExchangeRateToken {\n function getExchangeRate() external view returns (uint256 _exchangeRate);\n}\n" + }, + "contracts/interfaces/IMinMaxOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IMinMaxOracle {\n //Assuming 8 decimals\n function priceMin(string calldata symbol) external view returns (uint256);\n\n function priceMax(string calldata symbol) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IMockVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"./IVault.sol\";\n\ninterface IMockVault is IVault {\n function outstandingWithdrawalsAmount() external view returns (uint256);\n\n function wethAvailable() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IOETHZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOETHZapper {\n function deposit() external payable returns (uint256);\n}\n" + }, + "contracts/interfaces/IOneInch.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/// 1Inch swap data\nstruct SwapDescription {\n IERC20 srcToken; // contract address of a token to sell\n IERC20 dstToken; // contract address of a token to buy\n address payable srcReceiver;\n address payable dstReceiver; // Receiver of destination currency. default: fromAddress\n uint256 amount;\n uint256 minReturnAmount;\n uint256 flags;\n}\n\n/// @title Interface for making arbitrary calls during swap\ninterface IAggregationExecutor {\n /// @notice propagates information about original msg.sender and executes arbitrary data\n function execute(address msgSender) external payable; // 0x4b64e492\n}\n\ninterface IOneInchRouter {\n /// @notice Performs a swap, delegating all calls encoded in `data` to `executor`.\n function swap(\n IAggregationExecutor executor,\n SwapDescription calldata desc,\n bytes calldata permit,\n bytes calldata data\n ) external returns (uint256 returnAmount, uint256 spentAmount);\n\n /// @notice Performs swap using Uniswap exchange. Wraps and unwraps ETH if required.\n function unoswapTo(\n address payable recipient,\n IERC20 srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) external payable returns (uint256 returnAmount);\n\n /// @notice Performs swap using Uniswap V3 exchange. Wraps and unwraps ETH if required.\n function uniswapV3SwapTo(\n address payable recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) external payable returns (uint256 returnAmount);\n}\n" + }, + "contracts/interfaces/IOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOracle {\n /**\n * @dev returns the asset price in USD, in 8 decimal digits.\n *\n * The version of priceProvider deployed for OETH has 18 decimal digits\n */\n function price(address asset) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IOUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOUSD {\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n function _totalSupply() external view returns (uint256);\n\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address _account) external view returns (uint256);\n\n function burn(address account, uint256 amount) external;\n\n function changeSupply(uint256 _newTotalSupply) external;\n\n function claimGovernance() external;\n\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256);\n\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n );\n\n function decimals() external view returns (uint8);\n\n function decreaseAllowance(address _spender, uint256 _subtractedValue)\n external\n returns (bool);\n\n function governor() external view returns (address);\n\n function increaseAllowance(address _spender, uint256 _addedValue)\n external\n returns (bool);\n\n function initialize(\n string memory _nameArg,\n string memory _symbolArg,\n address _vaultAddress\n ) external;\n\n function isGovernor() external view returns (bool);\n\n function isUpgraded(address) external view returns (uint256);\n\n function mint(address _account, uint256 _amount) external;\n\n function name() external view returns (string memory);\n\n function nonRebasingCreditsPerToken(address)\n external\n view\n returns (uint256);\n\n function nonRebasingSupply() external view returns (uint256);\n\n function rebaseOptIn() external;\n\n function rebaseOptOut() external;\n\n function rebaseState(address) external view returns (uint8);\n\n function rebasingCredits() external view returns (uint256);\n\n function rebasingCreditsHighres() external view returns (uint256);\n\n function rebasingCreditsPerToken() external view returns (uint256);\n\n function rebasingCreditsPerTokenHighres() external view returns (uint256);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function transferGovernance(address _newGovernor) external;\n\n function vaultAddress() external view returns (address);\n}\n" + }, + "contracts/interfaces/IPriceOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPriceOracle {\n /**\n * @dev returns the asset price in USD, 6 decimal digits.\n * Compatible with the Open Price Feed.\n */\n function price(string calldata symbol) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IRETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IRETH {\n function getEthValue(uint256 _rethAmount) external view returns (uint256);\n\n function getRethValue(uint256 _ethAmount) external view returns (uint256);\n\n function getExchangeRate() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function balanceOf(address account) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 amount) external returns (bool);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/ISafe.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ISafe {\n function execTransactionFromModule(\n address,\n uint256,\n bytes memory,\n uint8\n ) external returns (bool);\n}\n" + }, + "contracts/interfaces/ISfrxETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface ISfrxETH {\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 amount\n );\n event Deposit(\n address indexed caller,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n event NewRewardsCycle(uint32 indexed cycleEnd, uint256 rewardAmount);\n event Transfer(address indexed from, address indexed to, uint256 amount);\n event Withdraw(\n address indexed caller,\n address indexed receiver,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address spender, uint256 amount) external returns (bool);\n\n function asset() external view returns (address);\n\n function balanceOf(address) external view returns (uint256);\n\n function convertToAssets(uint256 shares) external view returns (uint256);\n\n function convertToShares(uint256 assets) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit(uint256 assets, address receiver)\n external\n returns (uint256 shares);\n\n function depositWithSignature(\n uint256 assets,\n address receiver,\n uint256 deadline,\n bool approveMax,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external returns (uint256 shares);\n\n function lastRewardAmount() external view returns (uint192);\n\n function lastSync() external view returns (uint32);\n\n function maxDeposit(address) external view returns (uint256);\n\n function maxMint(address) external view returns (uint256);\n\n function maxRedeem(address owner) external view returns (uint256);\n\n function maxWithdraw(address owner) external view returns (uint256);\n\n function mint(uint256 shares, address receiver)\n external\n returns (uint256 assets);\n\n function name() external view returns (string memory);\n\n function nonces(address) external view returns (uint256);\n\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function previewDeposit(uint256 assets) external view returns (uint256);\n\n function previewMint(uint256 shares) external view returns (uint256);\n\n function previewRedeem(uint256 shares) external view returns (uint256);\n\n function previewWithdraw(uint256 assets) external view returns (uint256);\n\n function pricePerShare() external view returns (uint256);\n\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets);\n\n function rewardsCycleEnd() external view returns (uint32);\n\n function rewardsCycleLength() external view returns (uint32);\n\n function symbol() external view returns (string memory);\n\n function syncRewards() external;\n\n function totalAssets() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address to, uint256 amount) external returns (bool);\n\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/ISwapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ISwapper {\n /**\n * @param fromAsset The token address of the asset being sold.\n * @param toAsset The token address of the asset being purchased.\n * @param fromAssetAmount The amount of assets being sold.\n * @param minToAssetAmmount The minimum amount of assets to be purchased.\n * @param data tx.data returned from 1Inch's /v5.0/1/swap API\n */\n function swap(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n}\n" + }, + "contracts/interfaces/ITimelock.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ITimelock {\n function delay() external view returns (uint256);\n\n function GRACE_PERIOD() external view returns (uint256);\n\n function acceptAdmin() external;\n\n function queuedTransactions(bytes32 hash) external view returns (bool);\n\n function queueTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external returns (bytes32);\n\n function cancelTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external;\n\n function executeTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external payable returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ITimelockController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ITimelockController {\n function grantRole(bytes32 role, address account) external;\n\n function revokeRole(bytes32 role, address account) external;\n\n function renounceRole(bytes32 role, address account) external;\n\n function hasRole(bytes32 role, address account)\n external\n view\n returns (bool);\n\n function executeBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt\n ) external payable;\n\n function scheduleBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt,\n uint256 delay\n ) external;\n\n function hashOperationBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt\n ) external view returns (bytes32);\n\n function isOperationDone(bytes32 opHash) external view returns (bool);\n\n function isOperationReady(bytes32 opHash) external view returns (bool);\n\n function isOperation(bytes32 opHash) external view returns (bool);\n\n function getMinDelay() external view returns (uint256);\n\n function updateDelay(uint256 newDelay) external;\n\n function CANCELLER_ROLE() external view returns (bytes32);\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/interfaces/IWstETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWstETH {\n /**\n * @notice Get amount of wstETH for a given amount of stETH\n * @param _stETHAmount amount of stETH\n * @return Amount of wstETH for a given stETH amount\n */\n function getWstETHByStETH(uint256 _stETHAmount)\n external\n view\n returns (uint256);\n\n /**\n * @notice Get amount of stETH for a given amount of wstETH\n * @param _wstETHAmount amount of wstETH\n * @return Amount of stETH for a given wstETH amount\n */\n function getStETHByWstETH(uint256 _wstETHAmount)\n external\n view\n returns (uint256);\n\n /**\n * @notice Get amount of stETH for a one wstETH\n * @return Amount of stETH for 1 wstETH\n */\n function stEthPerToken() external view returns (uint256);\n\n /**\n * @notice Get amount of wstETH for a one stETH\n * @return Amount of wstETH for a 1 stETH\n */\n function tokensPerStEth() external view returns (uint256);\n\n /**\n * @notice Exchanges stETH to wstETH\n * @param _stETHAmount amount of stETH to wrap in exchange for wstETH\n * @dev Requirements:\n * - `_stETHAmount` must be non-zero\n * - msg.sender must approve at least `_stETHAmount` stETH to this\n * contract.\n * - msg.sender must have at least `_stETHAmount` of stETH.\n * User should first approve _stETHAmount to the WstETH contract\n * @return Amount of wstETH user receives after wrap\n */\n function wrap(uint256 _stETHAmount) external returns (uint256);\n\n /**\n * @notice Exchanges wstETH to stETH\n * @param _wstETHAmount amount of wstETH to uwrap in exchange for stETH\n * @dev Requirements:\n * - `_wstETHAmount` must be non-zero\n * - msg.sender must have at least `_wstETHAmount` wstETH.\n * @return Amount of stETH user receives after unwrap\n */\n function unwrap(uint256 _wstETHAmount) external returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/compound/ICompoundOracle.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\ninterface ICompoundOracle {\n function getUnderlyingPrice(address) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/ILens.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\nimport \"./compound/ICompoundOracle.sol\";\nimport \"./IMorpho.sol\";\n\ninterface ILens {\n /// STORAGE ///\n\n function MAX_BASIS_POINTS() external view returns (uint256);\n\n function WAD() external view returns (uint256);\n\n function morpho() external view returns (IMorpho);\n\n function comptroller() external view returns (IComptroller);\n\n /// GENERAL ///\n\n function getTotalSupply()\n external\n view\n returns (\n uint256 p2pSupplyAmount,\n uint256 poolSupplyAmount,\n uint256 totalSupplyAmount\n );\n\n function getTotalBorrow()\n external\n view\n returns (\n uint256 p2pBorrowAmount,\n uint256 poolBorrowAmount,\n uint256 totalBorrowAmount\n );\n\n /// MARKETS ///\n\n function isMarketCreated(address _poolToken) external view returns (bool);\n\n function isMarketCreatedAndNotPaused(address _poolToken)\n external\n view\n returns (bool);\n\n function isMarketCreatedAndNotPausedNorPartiallyPaused(address _poolToken)\n external\n view\n returns (bool);\n\n function getAllMarkets()\n external\n view\n returns (address[] memory marketsCreated_);\n\n function getMainMarketData(address _poolToken)\n external\n view\n returns (\n uint256 avgSupplyRatePerBlock,\n uint256 avgBorrowRatePerBlock,\n uint256 p2pSupplyAmount,\n uint256 p2pBorrowAmount,\n uint256 poolSupplyAmount,\n uint256 poolBorrowAmount\n );\n\n function getAdvancedMarketData(address _poolToken)\n external\n view\n returns (\n uint256 p2pSupplyIndex,\n uint256 p2pBorrowIndex,\n uint256 poolSupplyIndex,\n uint256 poolBorrowIndex,\n uint32 lastUpdateBlockNumber,\n uint256 p2pSupplyDelta,\n uint256 p2pBorrowDelta\n );\n\n function getMarketConfiguration(address _poolToken)\n external\n view\n returns (\n address underlying,\n bool isCreated,\n bool p2pDisabled,\n bool isPaused,\n bool isPartiallyPaused,\n uint16 reserveFactor,\n uint16 p2pIndexCursor,\n uint256 collateralFactor\n );\n\n function getTotalMarketSupply(address _poolToken)\n external\n view\n returns (uint256 p2pSupplyAmount, uint256 poolSupplyAmount);\n\n function getTotalMarketBorrow(address _poolToken)\n external\n view\n returns (uint256 p2pBorrowAmount, uint256 poolBorrowAmount);\n\n /// INDEXES ///\n\n function getCurrentP2PSupplyIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentP2PBorrowIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentPoolIndexes(address _poolToken)\n external\n view\n returns (\n uint256 currentPoolSupplyIndex,\n uint256 currentPoolBorrowIndex\n );\n\n function getIndexes(address _poolToken, bool _computeUpdatedIndexes)\n external\n view\n returns (\n uint256 p2pSupplyIndex,\n uint256 p2pBorrowIndex,\n uint256 poolSupplyIndex,\n uint256 poolBorrowIndex\n );\n\n /// USERS ///\n\n function getEnteredMarkets(address _user)\n external\n view\n returns (address[] memory enteredMarkets);\n\n function getUserHealthFactor(\n address _user,\n address[] calldata _updatedMarkets\n ) external view returns (uint256);\n\n function getUserBalanceStates(\n address _user,\n address[] calldata _updatedMarkets\n )\n external\n view\n returns (\n uint256 collateralValue,\n uint256 debtValue,\n uint256 maxDebtValue\n );\n\n function getCurrentSupplyBalanceInOf(address _poolToken, address _user)\n external\n view\n returns (\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getCurrentBorrowBalanceInOf(address _poolToken, address _user)\n external\n view\n returns (\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getUserMaxCapacitiesForAsset(address _user, address _poolToken)\n external\n view\n returns (uint256 withdrawable, uint256 borrowable);\n\n function getUserHypotheticalBalanceStates(\n address _user,\n address _poolToken,\n uint256 _withdrawnAmount,\n uint256 _borrowedAmount\n ) external view returns (uint256 debtValue, uint256 maxDebtValue);\n\n function getUserLiquidityDataForAsset(\n address _user,\n address _poolToken,\n bool _computeUpdatedIndexes,\n ICompoundOracle _oracle\n ) external view returns (Types.AssetLiquidityData memory assetData);\n\n function isLiquidatable(address _user, address[] memory _updatedMarkets)\n external\n view\n returns (bool);\n\n function computeLiquidationRepayAmount(\n address _user,\n address _poolTokenBorrowed,\n address _poolTokenCollateral,\n address[] calldata _updatedMarkets\n ) external view returns (uint256 toRepay);\n\n /// RATES ///\n\n function getAverageSupplyRatePerBlock(address _poolToken)\n external\n view\n returns (\n uint256 avgSupplyRatePerBlock,\n uint256 p2pSupplyAmount,\n uint256 poolSupplyAmount\n );\n\n function getAverageBorrowRatePerBlock(address _poolToken)\n external\n view\n returns (\n uint256 avgBorrowRatePerBlock,\n uint256 p2pBorrowAmount,\n uint256 poolBorrowAmount\n );\n\n function getNextUserSupplyRatePerBlock(\n address _poolToken,\n address _user,\n uint256 _amount\n )\n external\n view\n returns (\n uint256 nextSupplyRatePerBlock,\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getNextUserBorrowRatePerBlock(\n address _poolToken,\n address _user,\n uint256 _amount\n )\n external\n view\n returns (\n uint256 nextBorrowRatePerBlock,\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getCurrentUserSupplyRatePerBlock(address _poolToken, address _user)\n external\n view\n returns (uint256);\n\n function getCurrentUserBorrowRatePerBlock(address _poolToken, address _user)\n external\n view\n returns (uint256);\n\n function getRatesPerBlock(address _poolToken)\n external\n view\n returns (\n uint256 p2pSupplyRate,\n uint256 p2pBorrowRate,\n uint256 poolSupplyRate,\n uint256 poolBorrowRate\n );\n\n /// REWARDS ///\n\n function getUserUnclaimedRewards(\n address[] calldata _poolTokens,\n address _user\n ) external view returns (uint256 unclaimedRewards);\n\n function getAccruedSupplierComp(\n address _supplier,\n address _poolToken,\n uint256 _balance\n ) external view returns (uint256);\n\n function getAccruedBorrowerComp(\n address _borrower,\n address _poolToken,\n uint256 _balance\n ) external view returns (uint256);\n\n function getCurrentCompSupplyIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentCompBorrowIndex(address _poolToken)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/IMorpho.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\nimport \"./Types.sol\";\nimport \"../IComptroller.sol\";\nimport \"./compound/ICompoundOracle.sol\";\n\n// prettier-ignore\ninterface IMorpho {\n function comptroller() external view returns (IComptroller);\n function supply(address _poolTokenAddress, address _onBehalf, uint256 _amount) external;\n function supply(address _poolTokenAddress, address _onBehalf, uint256 _amount, uint256 _maxGasForMatching) external;\n function withdraw(address _poolTokenAddress, uint256 _amount) external;\n function claimRewards(\n address[] calldata _cTokenAddresses,\n bool _tradeForMorphoToken\n ) external returns (uint256 claimedAmount);\n}\n" + }, + "contracts/interfaces/morpho/Types.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\n/// @title Types.\n/// @author Morpho Labs.\n/// @custom:contact security@morpho.xyz\n/// @dev Common types and structs used in Moprho contracts.\nlibrary Types {\n /// ENUMS ///\n\n enum PositionType {\n SUPPLIERS_IN_P2P,\n SUPPLIERS_ON_POOL,\n BORROWERS_IN_P2P,\n BORROWERS_ON_POOL\n }\n\n /// STRUCTS ///\n\n struct SupplyBalance {\n uint256 inP2P; // In supplier's peer-to-peer unit, a unit that grows in underlying value, to keep track of the interests earned by suppliers in peer-to-peer. Multiply by the peer-to-peer supply index to get the underlying amount.\n uint256 onPool; // In cToken. Multiply by the pool supply index to get the underlying amount.\n }\n\n struct BorrowBalance {\n uint256 inP2P; // In borrower's peer-to-peer unit, a unit that grows in underlying value, to keep track of the interests paid by borrowers in peer-to-peer. Multiply by the peer-to-peer borrow index to get the underlying amount.\n uint256 onPool; // In cdUnit, a unit that grows in value, to keep track of the debt increase when borrowers are on Compound. Multiply by the pool borrow index to get the underlying amount.\n }\n\n // Max gas to consume during the matching process for supply, borrow, withdraw and repay functions.\n struct MaxGasForMatching {\n uint64 supply;\n uint64 borrow;\n uint64 withdraw;\n uint64 repay;\n }\n\n struct Delta {\n uint256 p2pSupplyDelta; // Difference between the stored peer-to-peer supply amount and the real peer-to-peer supply amount (in pool supply unit).\n uint256 p2pBorrowDelta; // Difference between the stored peer-to-peer borrow amount and the real peer-to-peer borrow amount (in pool borrow unit).\n uint256 p2pSupplyAmount; // Sum of all stored peer-to-peer supply (in peer-to-peer supply unit).\n uint256 p2pBorrowAmount; // Sum of all stored peer-to-peer borrow (in peer-to-peer borrow unit).\n }\n\n struct AssetLiquidityData {\n uint256 collateralValue; // The collateral value of the asset.\n uint256 maxDebtValue; // The maximum possible debt value of the asset.\n uint256 debtValue; // The debt value of the asset.\n uint256 underlyingPrice; // The price of the token.\n uint256 collateralFactor; // The liquidation threshold applied on this token.\n }\n\n struct LiquidityData {\n uint256 collateralValue; // The collateral value.\n uint256 maxDebtValue; // The maximum debt value possible.\n uint256 debtValue; // The debt value.\n }\n\n // Variables are packed together to save gas (will not exceed their limit during Morpho's lifetime).\n struct LastPoolIndexes {\n uint32 lastUpdateBlockNumber; // The last time the peer-to-peer indexes were updated.\n uint112 lastSupplyPoolIndex; // Last pool supply index.\n uint112 lastBorrowPoolIndex; // Last pool borrow index.\n }\n\n struct MarketParameters {\n uint16 reserveFactor; // Proportion of the interest earned by users sent to the DAO for each market, in basis point (100% = 10 000). The value is set at market creation.\n uint16 p2pIndexCursor; // Position of the peer-to-peer rate in the pool's spread. Determine the weights of the weighted arithmetic average in the indexes computations ((1 - p2pIndexCursor) * r^S + p2pIndexCursor * r^B) (in basis point).\n }\n\n struct MarketStatus {\n bool isCreated; // Whether or not this market is created.\n bool isPaused; // Whether the market is paused or not (all entry points on Morpho are frozen; supply, borrow, withdraw, repay and liquidate).\n bool isPartiallyPaused; // Whether the market is partially paused or not (only supply and borrow are frozen).\n }\n}\n" + }, + "contracts/interfaces/plume/IFeeRegistry.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\ninterface IFeeRegistry {\n function registerFee(\n bool isTokenA,\n uint32 binId,\n uint256 binFeeInQuote\n ) external;\n}\n" + }, + "contracts/interfaces/plume/ILiquidityRegistry.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface ILiquidityRegistry {\n function notifyBinLiquidity(\n IMaverickV2Pool pool,\n uint256 tokenId,\n uint32 binId,\n uint256 currentBinLpBalance\n ) external;\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Factory.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IFeeRegistry } from \"./IFeeRegistry.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2Factory {\n error FactorAlreadyInitialized();\n error FactorNotInitialized();\n error FactoryInvalidTokenOrder(IERC20 _tokenA, IERC20 _tokenB);\n error FactoryInvalidFee();\n error FactoryInvalidKinds(uint8 kinds);\n error FactoryInvalidTickSpacing(uint256 tickSpacing);\n error FactoryInvalidLookback(uint256 lookback);\n error FactoryInvalidTokenDecimals(uint8 decimalsA, uint8 decimalsB);\n error FactoryPoolAlreadyExists(\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds,\n address accessor\n );\n error FactoryAccessorMustBeNonZero();\n error NotImplemented();\n\n event PoolCreated(\n IMaverickV2Pool poolAddress,\n uint8 protocolFeeRatio,\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n int32 activeTick,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds,\n address accessor\n );\n event SetFactoryProtocolFeeReceiver(address receiver);\n event SetFactoryProtocolFeeRegistry(IFeeRegistry registry);\n\n struct DeployParameters {\n uint64 feeAIn;\n uint64 feeBIn;\n uint32 lookback;\n int32 activeTick;\n uint64 tokenAScale;\n uint64 tokenBScale;\n // slot\n IERC20 tokenA;\n // slot\n IERC20 tokenB;\n // slot\n uint16 tickSpacing;\n uint8 options;\n address accessor;\n }\n\n /**\n * @notice Called by deployer library to initialize a pool.\n */\n function deployParameters()\n external\n view\n returns (\n uint64 feeAIn,\n uint64 feeBIn,\n uint32 lookback,\n int32 activeTick,\n uint64 tokenAScale,\n uint64 tokenBScale,\n // slot\n IERC20 tokenA,\n // slot\n IERC20 tokenB,\n // slot\n uint16 tickSpacing,\n uint8 options,\n address accessor\n );\n\n /**\n * @notice Create a new MaverickV2Pool with symmetric swap fees.\n * @param fee Fraction of the pool swap amount that is retained as an LP in\n * D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * E.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function create(\n uint64 fee,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external returns (IMaverickV2Pool);\n\n /**\n * @notice Create a new MaverickV2Pool.\n * @param feeAIn Fraction of the pool swap amount for tokenA-input swaps\n * that is retained as an LP in D18 scale.\n * @param feeBIn Fraction of the pool swap amount for tokenB-input swaps\n * that is retained as an LP in D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * e.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function create(\n uint64 feeAIn,\n uint64 feeBIn,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external returns (IMaverickV2Pool);\n\n /**\n * @notice Bool indicating whether the pool was deployed from this factory.\n */\n function isFactoryPool(IMaverickV2Pool pool) external view returns (bool);\n\n /**\n * @notice Address that receives the protocol fee\n */\n function protocolFeeReceiver() external view returns (address);\n\n /**\n * @notice Address notified on swaps of the protocol fee\n */\n function protocolFeeRegistry() external view returns (IFeeRegistry);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds\n ) external view returns (IMaverickV2Pool);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(\n IERC20 _tokenA,\n IERC20 _tokenB,\n uint256 startIndex,\n uint256 endIndex\n ) external view returns (IMaverickV2Pool[] memory pools);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(uint256 startIndex, uint256 endIndex)\n external\n view\n returns (IMaverickV2Pool[] memory pools);\n\n /**\n * @notice Count of permissionless pools.\n */\n function poolCount() external view returns (uint256 _poolCount);\n\n /**\n * @notice Count of pools for a given accessor and token pair. For\n * permissionless pools, pass `accessor = address(0)`.\n */\n function poolByTokenCount(\n IERC20 _tokenA,\n IERC20 _tokenB,\n address accessor\n ) external view returns (uint256 _poolCount);\n\n /**\n * @notice Get the current factory owner.\n */\n function owner() external view returns (address);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2LiquidityManager.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\nimport { IMaverickV2Position } from \"./IMaverickV2Position.sol\";\nimport { IMaverickV2PoolLens } from \"./IMaverickV2PoolLens.sol\";\n\ninterface IMaverickV2LiquidityManager {\n error LiquidityManagerNotFactoryPool();\n error LiquidityManagerNotTokenIdOwner();\n\n /**\n * @notice Maverick V2 NFT position contract that tracks NFT-based\n * liquditiy positions.\n */\n function position() external view returns (IMaverickV2Position);\n\n /**\n * @notice Create Maverick V2 pool. Function is a pass through to the pool\n * factory and is provided here so that is can be assembled as part of a\n * multicall transaction.\n */\n function createPool(\n uint64 fee,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external payable returns (IMaverickV2Pool pool);\n\n /**\n * @notice Add Liquidity position NFT for msg.sender by specifying\n * msg.sender's token index.\n * @dev Token index is different from tokenId.\n * On the Position NFT contract a user can own multiple NFT tokenIds and\n * these are indexes by an enumeration index which is the `index` input\n * here.\n *\n * See addLiquidity for a description of the add params.\n */\n function addPositionLiquidityToSenderByTokenIndex(\n IMaverickV2Pool pool,\n uint256 index,\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs\n )\n external\n payable\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds\n );\n\n /**\n * @notice Mint new tokenId in the Position NFt contract to msg.sender.\n * Both mints an NFT and adds liquidity to the pool that is held by the\n * NFT.\n */\n function mintPositionNftToSender(\n IMaverickV2Pool pool,\n bytes calldata packedSqrtPriceBreaks,\n bytes[] calldata packedArgs\n )\n external\n payable\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds,\n uint256 tokenId\n );\n\n /**\n * @notice Donates liqudity to a pool that is held by the position contract\n * and will never be retrievable. Can be used to start a pool and ensure\n * there will always be a base level of liquditiy in the pool.\n */\n function donateLiquidity(\n IMaverickV2Pool pool,\n IMaverickV2Pool.AddLiquidityParams memory args\n ) external payable;\n\n /**\n * @notice Packs sqrtPrice breaks array with this format: [length,\n * array[0], array[1],..., array[length-1]] where length is 1 byte.\n */\n function packUint88Array(uint88[] memory fullArray)\n external\n pure\n returns (bytes memory packedArray);\n\n /**\n * @notice Packs addLiquidity paramters array element-wise.\n */\n function packAddLiquidityArgsArray(\n IMaverickV2Pool.AddLiquidityParams[] memory args\n ) external pure returns (bytes[] memory argsPacked);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Pool.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\n\ninterface IMaverickV2Pool {\n error PoolZeroLiquidityAdded();\n error PoolMinimumLiquidityNotMet();\n error PoolLocked();\n error PoolInvalidFee();\n error PoolTicksNotSorted(uint256 index, int256 previousTick, int256 tick);\n error PoolTicksAmountsLengthMismatch(\n uint256 ticksLength,\n uint256 amountsLength\n );\n error PoolBinIdsAmountsLengthMismatch(\n uint256 binIdsLength,\n uint256 amountsLength\n );\n error PoolKindNotSupported(uint256 kinds, uint256 kind);\n error PoolInsufficientBalance(\n uint256 deltaLpAmount,\n uint256 accountBalance\n );\n error PoolReservesExceedMaximum(uint256 amount);\n error PoolValueExceedsBits(uint256 amount, uint256 bits);\n error PoolTickMaxExceeded(uint256 tick);\n error PoolMigrateBinFirst();\n error PoolCurrentTickBeyondSwapLimit(int32 startingTick);\n error PoolSenderNotAccessor(address sender_, address accessor);\n error PoolSenderNotFactory(address sender_, address accessor);\n error PoolFunctionNotImplemented();\n error PoolTokenNotSolvent(\n uint256 internalReserve,\n uint256 tokenBalance,\n IERC20 token\n );\n error PoolNoProtocolFeeReceiverSet();\n\n event PoolSwap(\n address sender,\n address recipient,\n SwapParams params,\n uint256 amountIn,\n uint256 amountOut\n );\n event PoolFlashLoan(\n address sender,\n address recipient,\n uint256 amountA,\n uint256 amountB\n );\n event PoolProtocolFeeCollected(IERC20 token, uint256 protocolFee);\n\n event PoolAddLiquidity(\n address sender,\n address recipient,\n uint256 subaccount,\n AddLiquidityParams params,\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] binIds\n );\n\n event PoolMigrateBinsUpStack(\n address sender,\n uint32 binId,\n uint32 maxRecursion\n );\n\n event PoolRemoveLiquidity(\n address sender,\n address recipient,\n uint256 subaccount,\n RemoveLiquidityParams params,\n uint256 tokenAOut,\n uint256 tokenBOut\n );\n\n event PoolTickState(int32 tick, uint256 reserveA, uint256 reserveB);\n event PoolTickBinUpdate(int32 tick, uint8 kind, uint32 binId);\n event PoolSqrtPrice(uint256 sqrtPrice);\n\n /**\n * @notice Tick state parameters.\n */\n struct TickState {\n uint128 reserveA;\n uint128 reserveB;\n uint128 totalSupply;\n uint32[4] binIdsByTick;\n }\n\n /**\n * @notice Tick data parameters.\n * @param currentReserveA Current reserve of token A.\n * @param currentReserveB Current reserve of token B.\n * @param currentLiquidity Current liquidity amount.\n */\n struct TickData {\n uint256 currentReserveA;\n uint256 currentReserveB;\n uint256 currentLiquidity;\n }\n\n /**\n * @notice Bin state parameters.\n * @param mergeBinBalance LP token balance that this bin possesses of the merge bin.\n * @param mergeId Bin ID of the bin that this bin has merged into.\n * @param totalSupply Total amount of LP tokens in this bin.\n * @param kind One of the 4 kinds (0=static, 1=right, 2=left, 3=both).\n * @param tick The lower price tick of the bin in its current state.\n * @param tickBalance Balance of the tick.\n */\n struct BinState {\n uint128 mergeBinBalance;\n uint128 tickBalance;\n uint128 totalSupply;\n uint8 kind;\n int32 tick;\n uint32 mergeId;\n }\n\n /**\n * @notice Parameters for swap.\n * @param amount Amount of the token that is either the input if exactOutput is false\n * or the output if exactOutput is true.\n * @param tokenAIn Boolean indicating whether tokenA is the input.\n * @param exactOutput Boolean indicating whether the amount specified is\n * the exact output amount (true).\n * @param tickLimit The furthest tick a swap will execute in. If no limit\n * is desired, value should be set to type(int32).max for a tokenAIn swap\n * and type(int32).min for a swap where tokenB is the input.\n */\n struct SwapParams {\n uint256 amount;\n bool tokenAIn;\n bool exactOutput;\n int32 tickLimit;\n }\n\n /**\n * @notice Parameters associated with adding liquidity.\n * @param kind One of the 4 kinds (0=static, 1=right, 2=left, 3=both).\n * @param ticks Array of ticks to add liquidity to.\n * @param amounts Array of bin LP amounts to add.\n */\n struct AddLiquidityParams {\n uint8 kind;\n int32[] ticks;\n uint128[] amounts;\n }\n\n /**\n * @notice Parameters for each bin that will have liquidity removed.\n * @param binIds Index array of the bins losing liquidity.\n * @param amounts Array of bin LP amounts to remove.\n */\n struct RemoveLiquidityParams {\n uint32[] binIds;\n uint128[] amounts;\n }\n\n /**\n * @notice State of the pool.\n * @param reserveA Pool tokenA balanceOf at end of last operation\n * @param reserveB Pool tokenB balanceOf at end of last operation\n * @param lastTwaD8 Value of log time weighted average price at last block.\n * Value is 8-decimal scale and is in the fractional tick domain. E.g. a\n * value of 12.3e8 indicates the TWAP was 3/10ths of the way into the 12th\n * tick.\n * @param lastLogPriceD8 Value of log price at last block. Value is\n * 8-decimal scale and is in the fractional tick domain. E.g. a value of\n * 12.3e8 indicates the price was 3/10ths of the way into the 12th tick.\n * @param lastTimestamp Last block.timestamp value in seconds for latest\n * swap transaction.\n * @param activeTick Current tick position that contains the active bins.\n * @param isLocked Pool isLocked, E.g., locked or unlocked; isLocked values\n * defined in Pool.sol.\n * @param binCounter Index of the last bin created.\n * @param protocolFeeRatioD3 Ratio of the swap fee that is kept for the\n * protocol.\n */\n struct State {\n uint128 reserveA;\n uint128 reserveB;\n int64 lastTwaD8;\n int64 lastLogPriceD8;\n uint40 lastTimestamp;\n int32 activeTick;\n bool isLocked;\n uint32 binCounter;\n uint8 protocolFeeRatioD3;\n }\n\n /**\n * @notice Internal data used for data passing between Pool and Bin code.\n */\n struct BinDelta {\n uint128 deltaA;\n uint128 deltaB;\n }\n\n /**\n * @notice 1-15 number to represent the active kinds.\n * @notice 0b0001 = static;\n * @notice 0b0010 = right;\n * @notice 0b0100 = left;\n * @notice 0b1000 = both;\n *\n * E.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function kinds() external view returns (uint8 _kinds);\n\n /**\n * @notice Pool swap fee for the given direction (A-in or B-in swap) in\n * 18-decimal format. E.g. 0.01e18 is a 1% swap fee.\n */\n function fee(bool tokenAIn) external view returns (uint256);\n\n /**\n * @notice TickSpacing of pool where 1.0001^tickSpacing is the bin width.\n */\n function tickSpacing() external view returns (uint256);\n\n /**\n * @notice Lookback period of pool in seconds.\n */\n function lookback() external view returns (uint256);\n\n /**\n * @notice Address of Pool accessor. This is Zero address for\n * permissionless pools.\n */\n function accessor() external view returns (address);\n\n /**\n * @notice Pool tokenA. Address of tokenA is such that tokenA < tokenB.\n */\n function tokenA() external view returns (IERC20);\n\n /**\n * @notice Pool tokenB.\n */\n function tokenB() external view returns (IERC20);\n\n /**\n * @notice Deploying factory of the pool and also contract that has ability\n * to set and collect protocol fees for the pool.\n */\n function factory() external view returns (IMaverickV2Factory);\n\n /**\n * @notice Most significant bit of scale value is a flag to indicate whether\n * tokenA has more or less than 18 decimals. Scale is used in conjuction\n * with Math.toScale/Math.fromScale functions to convert from token amounts\n * to D18 scale internal pool accounting.\n */\n function tokenAScale() external view returns (uint256);\n\n /**\n * @notice Most significant bit of scale value is a flag to indicate whether\n * tokenA has more or less than 18 decimals. Scale is used in conjuction\n * with Math.toScale/Math.fromScale functions to convert from token amounts\n * to D18 scale internal pool accounting.\n */\n function tokenBScale() external view returns (uint256);\n\n /**\n * @notice ID of bin at input tick position and kind.\n */\n function binIdByTickKind(int32 tick, uint256 kind)\n external\n view\n returns (uint32);\n\n /**\n * @notice Accumulated tokenA protocol fee.\n */\n function protocolFeeA() external view returns (uint128);\n\n /**\n * @notice Accumulated tokenB protocol fee.\n */\n function protocolFeeB() external view returns (uint128);\n\n /**\n * @notice Lending fee rate on flash loans.\n */\n function lendingFeeRateD18() external view returns (uint256);\n\n /**\n * @notice External function to get the current time-weighted average price.\n */\n function getCurrentTwa() external view returns (int256);\n\n /**\n * @notice External function to get the state of the pool.\n */\n function getState() external view returns (State memory);\n\n /**\n * @notice Return state of Bin at input binId.\n */\n function getBin(uint32 binId) external view returns (BinState memory bin);\n\n /**\n * @notice Return state of Tick at input tick position.\n */\n function getTick(int32 tick)\n external\n view\n returns (TickState memory tickState);\n\n /**\n * @notice Retrieves the balance of a user within a bin.\n * @param user The user's address.\n * @param subaccount The subaccount for the user.\n * @param binId The ID of the bin.\n */\n function balanceOf(\n address user,\n uint256 subaccount,\n uint32 binId\n ) external view returns (uint128 lpToken);\n\n /**\n * @notice Add liquidity to a pool. This function allows users to deposit\n * tokens into a liquidity pool.\n * @dev This function will call `maverickV2AddLiquidityCallback` on the\n * calling contract to collect the tokenA/tokenB payment.\n * @param recipient The account that will receive credit for the added liquidity.\n * @param subaccount The account that will receive credit for the added liquidity.\n * @param params Parameters containing the details for adding liquidity,\n * such as token types and amounts.\n * @param data Bytes information that gets passed to the callback.\n * @return tokenAAmount The amount of token A added to the pool.\n * @return tokenBAmount The amount of token B added to the pool.\n * @return binIds An array of bin IDs where the liquidity is stored.\n */\n function addLiquidity(\n address recipient,\n uint256 subaccount,\n AddLiquidityParams calldata params,\n bytes calldata data\n )\n external\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds\n );\n\n /**\n * @notice Removes liquidity from the pool.\n * @dev Liquidy can only be removed from a bin that is either unmerged or\n * has a mergeId of an unmerged bin. If a bin is merged more than one\n * level deep, it must be migrated up the merge stack to the root bin\n * before liquidity removal.\n * @param recipient The address to receive the tokens.\n * @param subaccount The subaccount for the recipient.\n * @param params The parameters for removing liquidity.\n * @return tokenAOut The amount of token A received.\n * @return tokenBOut The amount of token B received.\n */\n function removeLiquidity(\n address recipient,\n uint256 subaccount,\n RemoveLiquidityParams calldata params\n ) external returns (uint256 tokenAOut, uint256 tokenBOut);\n\n /**\n * @notice Migrate bins up the linked list of merged bins so that its\n * mergeId is the currrent active bin.\n * @dev Liquidy can only be removed from a bin that is either unmerged or\n * has a mergeId of an unmerged bin. If a bin is merged more than one\n * level deep, it must be migrated up the merge stack to the root bin\n * before liquidity removal.\n * @param binId The ID of the bin to migrate.\n * @param maxRecursion The maximum recursion depth for the migration.\n */\n function migrateBinUpStack(uint32 binId, uint32 maxRecursion) external;\n\n /**\n * @notice Swap tokenA/tokenB assets in the pool. The swap user has two\n * options for funding their swap.\n * - The user can push the input token amount to the pool before calling\n * the swap function. In order to avoid having the pool call the callback,\n * the user should pass a zero-length `data` bytes object with the swap\n * call.\n * - The user can send the input token amount to the pool when the pool\n * calls the `maverickV2SwapCallback` function on the calling contract.\n * That callback has input parameters that specify the token address of the\n * input token, the input and output amounts, and the bytes data sent to\n * the swap function.\n * @dev If the users elects to do a callback-based swap, the output\n * assets will be sent before the callback is called, allowing the user to\n * execute flash swaps. However, the pool does have reentrancy protection,\n * so a swapper will not be able to interact with the same pool again\n * while they are in the callback function.\n * @param recipient The address to receive the output tokens.\n * @param params Parameters containing the details of the swap\n * @param data Bytes information that gets passed to the callback.\n */\n function swap(\n address recipient,\n SwapParams memory params,\n bytes calldata data\n ) external returns (uint256 amountIn, uint256 amountOut);\n\n /**\n * @notice Loan tokenA/tokenB assets from the pool to recipient. The fee\n * rate of a loan is determined by `lendingFeeRateD18`, which is set at the\n * protocol level by the factory. This function calls\n * `maverickV2FlashLoanCallback` on the calling contract. At the end of\n * the callback, the caller must pay back the loan with fee (if there is a\n * fee).\n * @param recipient The address to receive the loaned tokens.\n * @param amountB Loan amount of tokenA sent to recipient.\n * @param amountB Loan amount of tokenB sent to recipient.\n * @param data Bytes information that gets passed to the callback.\n */\n function flashLoan(\n address recipient,\n uint256 amountA,\n uint256 amountB,\n bytes calldata data\n ) external;\n\n /**\n * @notice Distributes accumulated protocol fee to factory protocolFeeReceiver\n */\n function distributeFees(bool isTokenA)\n external\n returns (uint256 protocolFee, IERC20 token);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2PoolLens.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2PoolLens {\n error LensTargetPriceOutOfBounds(\n uint256 targetSqrtPrice,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n );\n error LensTooLittleLiquidity(\n uint256 relativeLiquidityAmount,\n uint256 deltaA,\n uint256 deltaB\n );\n error LensTargetingTokenWithNoDelta(\n bool targetIsA,\n uint256 deltaA,\n uint256 deltaB\n );\n\n /**\n * @notice Add liquidity slippage parameters for a distribution of liquidity.\n * @param pool Pool where liquidity is being added.\n * @param kind Bin kind; all bins must have the same kind in a given call\n * to addLiquidity.\n * @param ticks Array of tick values to add liquidity to.\n * @param relativeLiquidityAmounts Relative liquidity amounts for the\n * specified ticks. Liquidity in this case is not bin LP balance, it is\n * the bin liquidity as defined by liquidity = deltaA / (sqrt(upper) -\n * sqrt(lower)) or deltaB = liquidity / sqrt(lower) - liquidity /\n * sqrt(upper).\n * @param addSpec Slippage specification.\n */\n struct AddParamsViewInputs {\n IMaverickV2Pool pool;\n uint8 kind;\n int32[] ticks;\n uint128[] relativeLiquidityAmounts;\n AddParamsSpecification addSpec;\n }\n\n /**\n * @notice Multi-price add param specification.\n * @param slippageFactorD18 Max slippage allowed as a percent in D18 scale. e.g. 1% slippage is 0.01e18\n * @param numberOfPriceBreaksPerSide Number of price break values on either\n * side of current price.\n * @param targetAmount Target token contribution amount in tokenA if\n * targetIsA is true, otherwise this is the target amount for tokenB.\n * @param targetIsA Indicates if the target amount is for tokenA or tokenB\n */\n struct AddParamsSpecification {\n uint256 slippageFactorD18;\n uint256 numberOfPriceBreaksPerSide;\n uint256 targetAmount;\n bool targetIsA;\n }\n\n /**\n * @notice Specification for deriving create pool parameters. Creating a\n * pool in the liquidity manager has several steps:\n *\n * - Deploy pool\n * - Donate a small amount of initial liquidity in the activeTick\n * - Execute a small swap to set the pool price to the desired value\n * - Add liquidity\n *\n * In order to execute these steps, the caller must specify the parameters\n * of each step. The PoolLens has helper function to derive the values\n * used by the LiquidityManager, but this struct is the input to that\n * helper function and represents the core intent of the pool creator.\n *\n * @param fee Fraction of the pool swap amount that is retained as an LP in\n * D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * e.g. a pool with all 4 modes will have kinds = b1111 = 15\n * @param initialTargetB Amount of B to be donated to the pool after pool\n * create. This amount needs to be big enough to meet the minimum bin\n * liquidity.\n * @param sqrtPrice Target sqrt price of the pool.\n * @param kind Bin kind; all bins must have the same kind in a given call\n * to addLiquidity.\n * @param ticks Array of tick values to add liquidity to.\n * @param relativeLiquidityAmounts Relative liquidity amounts for the\n * specified ticks. Liquidity in this case is not bin LP balance, it is\n * the bin liquidity as defined by liquidity = deltaA / (sqrt(upper) -\n * sqrt(lower)) or deltaB = liquidity / sqrt(lower) - liquidity /\n * sqrt(upper).\n * @param targetAmount Target token contribution amount in tokenA if\n * targetIsA is true, otherwise this is the target amount for tokenB.\n * @param targetIsA Indicates if the target amount is for tokenA or tokenB\n */\n struct CreateAndAddParamsViewInputs {\n uint64 feeAIn;\n uint64 feeBIn;\n uint16 tickSpacing;\n uint32 lookback;\n IERC20 tokenA;\n IERC20 tokenB;\n int32 activeTick;\n uint8 kinds;\n // donate params\n uint256 initialTargetB;\n uint256 sqrtPrice;\n // add target\n uint8 kind;\n int32[] ticks;\n uint128[] relativeLiquidityAmounts;\n uint256 targetAmount;\n bool targetIsA;\n }\n\n struct Output {\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256[] deltaAs;\n uint256[] deltaBs;\n uint128[] deltaLpBalances;\n }\n\n struct Reserves {\n uint256 amountA;\n uint256 amountB;\n }\n\n struct BinPositionKinds {\n uint128[4] values;\n }\n\n struct PoolState {\n IMaverickV2Pool.TickState[] tickStateMapping;\n IMaverickV2Pool.BinState[] binStateMapping;\n BinPositionKinds[] binIdByTickKindMapping;\n IMaverickV2Pool.State state;\n Reserves protocolFees;\n }\n\n struct BoostedPositionSpecification {\n IMaverickV2Pool pool;\n uint32[] binIds;\n uint128[] ratios;\n uint8 kind;\n }\n\n struct CreateAndAddParamsInputs {\n uint64 feeAIn;\n uint64 feeBIn;\n uint16 tickSpacing;\n uint32 lookback;\n IERC20 tokenA;\n IERC20 tokenB;\n int32 activeTick;\n uint8 kinds;\n // donate params\n IMaverickV2Pool.AddLiquidityParams donateParams;\n // swap params\n uint256 swapAmount;\n // add params\n IMaverickV2Pool.AddLiquidityParams addParams;\n bytes[] packedAddParams;\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256 preAddReserveA;\n uint256 preAddReserveB;\n }\n\n struct TickDeltas {\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256[] deltaAs;\n uint256[] deltaBs;\n }\n\n /**\n * @notice Converts add parameter slippage specification into add\n * parameters. The return values are given in both raw format and as packed\n * values that can be used in the LiquidityManager contract.\n */\n function getAddLiquidityParams(AddParamsViewInputs memory params)\n external\n view\n returns (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint88[] memory sqrtPriceBreaks,\n IMaverickV2Pool.AddLiquidityParams[] memory addParams,\n IMaverickV2PoolLens.TickDeltas[] memory tickDeltas\n );\n\n /**\n * @notice Converts add parameter slippage specification and new pool\n * specification into CreateAndAddParamsInputs parameters that can be used in the\n * LiquidityManager contract.\n */\n function getCreatePoolAtPriceAndAddLiquidityParams(\n CreateAndAddParamsViewInputs memory params\n ) external view returns (CreateAndAddParamsInputs memory output);\n\n /**\n * @notice View function that provides information about pool ticks within\n * a tick radius from the activeTick. Ticks with no reserves are not\n * included in part o f the return array.\n */\n function getTicksAroundActive(IMaverickV2Pool pool, int32 tickRadius)\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates\n );\n\n /**\n * @notice View function that provides information about pool ticks within\n * a range. Ticks with no reserves are not included in part o f the return\n * array.\n */\n function getTicks(\n IMaverickV2Pool pool,\n int32 tickStart,\n int32 tickEnd\n )\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates\n );\n\n /**\n * @notice View function that provides information about pool ticks within\n * a range. Information returned includes all pool state needed to emulate\n * a swap off chain. Ticks with no reserves are not included in part o f\n * the return array.\n */\n function getTicksAroundActiveWLiquidity(\n IMaverickV2Pool pool,\n int32 tickRadius\n )\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates,\n uint256[] memory liquidities,\n uint256[] memory sqrtLowerTickPrices,\n uint256[] memory sqrtUpperTickPrices,\n IMaverickV2Pool.State memory poolState,\n uint256 sqrtPrice,\n uint256 feeAIn,\n uint256 feeBIn\n );\n\n /**\n * @notice View function that provides pool state information.\n */\n function getFullPoolState(\n IMaverickV2Pool pool,\n uint32 binStart,\n uint32 binEnd\n ) external view returns (PoolState memory poolState);\n\n /**\n * @notice View function that provides price and liquidity of a given tick.\n */\n function getTickSqrtPriceAndL(IMaverickV2Pool pool, int32 tick)\n external\n view\n returns (uint256 sqrtPrice, uint256 liquidity);\n\n /**\n * @notice Pool sqrt price.\n */\n function getPoolSqrtPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 sqrtPrice);\n\n /**\n * @notice Pool price.\n */\n function getPoolPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 price);\n\n /**\n * @notice Token scale of two tokens in a pool.\n */\n function tokenScales(IMaverickV2Pool pool)\n external\n view\n returns (uint256 tokenAScale, uint256 tokenBScale);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Position.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\nimport { ILiquidityRegistry } from \"./ILiquidityRegistry.sol\";\n\ninterface IMaverickV2Position {\n event PositionClearData(uint256 indexed tokenId);\n event PositionSetData(\n uint256 indexed tokenId,\n uint256 index,\n PositionPoolBinIds newData\n );\n event SetLpReward(ILiquidityRegistry lpReward);\n\n error PositionDuplicatePool(uint256 index, IMaverickV2Pool pool);\n error PositionNotFactoryPool();\n error PositionPermissionedLiquidityPool();\n\n struct PositionPoolBinIds {\n IMaverickV2Pool pool;\n uint32[] binIds;\n }\n\n struct PositionFullInformation {\n PositionPoolBinIds poolBinIds;\n uint256 amountA;\n uint256 amountB;\n uint256[] binAAmounts;\n uint256[] binBAmounts;\n int32[] ticks;\n uint256[] liquidities;\n }\n\n /**\n * @notice Factory that tracks lp rewards.\n */\n function lpReward() external view returns (ILiquidityRegistry);\n\n /**\n * @notice Pool factory.\n */\n function factory() external view returns (IMaverickV2Factory);\n\n /**\n * @notice Mint NFT that holds liquidity in a Maverick V2 Pool. To mint\n * liquidity to an NFT, add liquidity to bins in a pool where the\n * add liquidity recipient is this contract and the subaccount is the\n * tokenId. LiquidityManager can be used to simplify minting Position NFTs.\n */\n function mint(\n address recipient,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external returns (uint256 tokenId);\n\n /**\n * @notice Overwrites tokenId pool/binId information for a given data index.\n */\n function setTokenIdData(\n uint256 tokenId,\n uint256 index,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n\n /**\n * @notice Overwrites entire pool/binId data set for a given tokenId.\n */\n function setTokenIdData(uint256 tokenId, PositionPoolBinIds[] memory data)\n external;\n\n /**\n * @notice Append new pool/binIds data array to tokenId.\n */\n function appendTokenIdData(\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n\n /**\n * @notice Get array pool/binIds data for a given tokenId.\n */\n function getTokenIdData(uint256 tokenId)\n external\n view\n returns (PositionPoolBinIds[] memory);\n\n /**\n * @notice Get value from array of pool/binIds data for a given tokenId.\n */\n function getTokenIdData(uint256 tokenId, uint256 index)\n external\n view\n returns (PositionPoolBinIds memory);\n\n /**\n * @notice Length of array of pool/binIds data for a given tokenId.\n */\n function tokenIdDataLength(uint256 tokenId)\n external\n view\n returns (uint256 length);\n\n /**\n * @notice Remove liquidity from tokenId for a given pool. User can\n * specify arbitrary bins to remove from for their subaccount in the pool\n * even if those bins are not in the tokenIdData set.\n */\n function removeLiquidity(\n uint256 tokenId,\n address recipient,\n IMaverickV2Pool pool,\n IMaverickV2Pool.RemoveLiquidityParams memory params\n ) external returns (uint256 tokenAAmount, uint256 tokenBAmount);\n\n /**\n * @notice Remove liquidity from tokenId for a given pool to sender. User\n * can specify arbitrary bins to remove from for their subaccount in the\n * pool even if those bins are not in the tokenIdData set.\n */\n function removeLiquidityToSender(\n uint256 tokenId,\n IMaverickV2Pool pool,\n IMaverickV2Pool.RemoveLiquidityParams memory params\n ) external returns (uint256 tokenAAmount, uint256 tokenBAmount);\n\n /**\n * @notice NFT asset information for a given range of pool/binIds indexes.\n * This function only returns the liquidity in the pools/binIds stored as\n * part of the tokenIdData, but it is possible that the NFT has additional\n * liquidity in pools/binIds that have not been recorded.\n */\n function tokenIdPositionInformation(\n uint256 tokenId,\n uint256 startIndex,\n uint256 stopIndex\n ) external view returns (PositionFullInformation[] memory output);\n\n /**\n * @notice NFT asset information for a given pool/binIds index. This\n * function only returns the liquidity in the pools/binIds stored as part\n * of the tokenIdData, but it is possible that the NFT has additional\n * liquidity in pools/binIds that have not been recorded.\n */\n function tokenIdPositionInformation(uint256 tokenId, uint256 index)\n external\n view\n returns (PositionFullInformation memory output);\n\n /**\n * @notice Get remove parameters for removing a fractional part of the\n * liquidity owned by a given tokenId. The fractional factor to remove is\n * given by proporationD18 in 18-decimal scale.\n */\n function getRemoveParams(\n uint256 tokenId,\n uint256 index,\n uint256 proportionD18\n )\n external\n view\n returns (IMaverickV2Pool.RemoveLiquidityParams memory params);\n\n /**\n * @notice Register the bin balances in the nft with the LpReward contract.\n */\n function checkpointBinLpBalance(\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Quoter.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2Quoter {\n error QuoterInvalidSwap();\n error QuoterInvalidAddLiquidity();\n\n /**\n * @notice Calculates a swap on a MaverickV2Pool and returns the resulting\n * amount and estimated gas. The gas estimate is only a rough estimate and\n * may not match a swap's gas.\n * @param pool The MaverickV2Pool to swap on.\n * @param amount The input amount.\n * @param tokenAIn Indicates if token A is the input token.\n * @param exactOutput Indicates if the amount is the output amount (true)\n * or input amount (false). If the tickLimit is reached, the full value of\n * the exactOutput may not be returned because the pool will stop swapping\n * before the whole order is filled.\n * @param tickLimit The tick limit for the swap. Once the swap lands in\n * this tick, it will stop and return the output amount swapped up to that\n * tick.\n */\n function calculateSwap(\n IMaverickV2Pool pool,\n uint128 amount,\n bool tokenAIn,\n bool exactOutput,\n int32 tickLimit\n )\n external\n returns (\n uint256 amountIn,\n uint256 amountOut,\n uint256 gasEstimate\n );\n\n /**\n * @notice Calculates a multihop swap and returns the resulting amount and\n * estimated gas. The gas estimate is only a rough estimate and\n * may not match a swap's gas.\n * @param path The path of pools to swap through. Path is given by an\n * packed array of (pool, tokenAIn) tuples. So each step in the path is 160\n * + 8 = 168 bits of data. e.g. path = abi.encodePacked(pool1, true, pool2, false);\n * @param amount The input amount.\n * @param exactOutput A boolean indicating if exact output is required.\n */\n function calculateMultiHopSwap(\n bytes memory path,\n uint256 amount,\n bool exactOutput\n ) external returns (uint256 returnAmount, uint256 gasEstimate);\n\n /**\n * @notice Computes the token amounts required for a given set of\n * addLiquidity parameters. The gas estimate is only a rough estimate and\n * may not match a add's gas.\n */\n function calculateAddLiquidity(\n IMaverickV2Pool pool,\n IMaverickV2Pool.AddLiquidityParams calldata params\n )\n external\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 gasEstimate\n );\n\n /**\n * @notice Pool's sqrt price.\n */\n function poolSqrtPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 sqrtPrice);\n}\n" + }, + "contracts/interfaces/plume/IPoolDistributor.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.0;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IPoolDistributor {\n function rewardToken() external view returns (address);\n\n function claimLp(\n address recipient,\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds,\n uint256 epoch\n ) external returns (uint256 amount);\n}\n" + }, + "contracts/interfaces/plume/IVotingDistributor.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.0;\n\ninterface IVotingDistributor {\n function lastEpoch() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/poolBooster/IMerklDistributor.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IMerklDistributor {\n struct CampaignParameters {\n // POPULATED ONCE CREATED\n\n // ID of the campaign. This can be left as a null bytes32 when creating campaigns\n // on Merkl.\n bytes32 campaignId;\n // CHOSEN BY CAMPAIGN CREATOR\n\n // Address of the campaign creator, if marked as address(0), it will be overriden with the\n // address of the `msg.sender` creating the campaign\n address creator;\n // Address of the token used as a reward\n address rewardToken;\n // Amount of `rewardToken` to distribute across all the epochs\n // Amount distributed per epoch is `amount/numEpoch`\n uint256 amount;\n // Type of campaign\n uint32 campaignType;\n // Timestamp at which the campaign should start\n uint32 startTimestamp;\n // Duration of the campaign in seconds. Has to be a multiple of EPOCH = 3600\n uint32 duration;\n // Extra data to pass to specify the campaign\n bytes campaignData;\n }\n\n function createCampaign(CampaignParameters memory newCampaign)\n external\n returns (bytes32);\n\n function signAndCreateCampaign(\n CampaignParameters memory newCampaign,\n bytes memory _signature\n ) external returns (bytes32);\n\n function sign(bytes memory _signature) external;\n\n function rewardTokenMinAmounts(address _rewardToken)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/poolBooster/IPoolBoostCentralRegistry.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPoolBoostCentralRegistry {\n /**\n * @dev all the supported pool booster types are listed here. It is possible\n * to have multiple versions of the factory that supports the same type of\n * pool booster. Factories are immutable and this can happen when a factory\n * or related pool booster required code update.\n * e.g. \"PoolBoosterSwapxDouble\" & \"PoolBoosterSwapxDouble_v2\"\n */\n enum PoolBoosterType {\n // Supports bribing 2 contracts per pool. Appropriate for Ichi vault concentrated\n // liquidity pools where (which is expected in most/all cases) both pool gauges\n // require bribing.\n SwapXDoubleBooster,\n // Supports bribing a single contract per pool. Appropriate for Classic Stable &\n // Classic Volatile pools and Ichi vaults where only 1 side (1 of the 2 gauges)\n // needs bribing\n SwapXSingleBooster,\n // Supports bribing a single contract per pool. Appropriate for Metropolis pools\n MetropolisBooster,\n // Supports creating a Merkl campaign.\n MerklBooster\n }\n\n struct PoolBoosterEntry {\n address boosterAddress;\n address ammPoolAddress;\n PoolBoosterType boosterType;\n }\n\n event PoolBoosterCreated(\n address poolBoosterAddress,\n address ammPoolAddress,\n PoolBoosterType poolBoosterType,\n address factoryAddress\n );\n event PoolBoosterRemoved(address poolBoosterAddress);\n\n function emitPoolBoosterCreated(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n PoolBoosterType _boosterType\n ) external;\n\n function emitPoolBoosterRemoved(address _poolBoosterAddress) external;\n}\n" + }, + "contracts/interfaces/poolBooster/IPoolBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPoolBooster {\n event BribeExecuted(uint256 amount);\n\n /// @notice Execute the bribe action\n function bribe() external;\n}\n" + }, + "contracts/interfaces/poolBooster/ISwapXAlgebraBribe.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBribe {\n /// @notice Notify a bribe amount\n /// @dev Rewards are saved into NEXT EPOCH mapping.\n function notifyRewardAmount(address _rewardsToken, uint256 reward) external;\n}\n" + }, + "contracts/interfaces/sonic/INodeDriver.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\n\ninterface INodeDriver {\n /// Seal epoch. Called BEFORE epoch sealing made by the client itself.\n function sealEpoch(\n uint256[] calldata offlineTimes,\n uint256[] calldata offlineBlocks,\n uint256[] calldata uptimes,\n uint256[] calldata originatedTxsFee\n ) external;\n}\n" + }, + "contracts/interfaces/sonic/ISFC.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\n\n/**\n * @title Special Fee Contract for Sonic network\n * @notice The SFC maintains a list of validators and delegators and distributes rewards to them.\n * @custom:security-contact security@fantom.foundation\n */\ninterface ISFC {\n error StakeIsFullySlashed();\n\n event CreatedValidator(\n uint256 indexed validatorID,\n address indexed auth,\n uint256 createdEpoch,\n uint256 createdTime\n );\n event Delegated(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 amount\n );\n event Undelegated(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 indexed wrID,\n uint256 amount\n );\n event Withdrawn(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 indexed wrID,\n uint256 amount,\n uint256 penalty\n );\n event ClaimedRewards(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 rewards\n );\n event RestakedRewards(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 rewards\n );\n event BurntFTM(uint256 amount);\n event UpdatedSlashingRefundRatio(\n uint256 indexed validatorID,\n uint256 refundRatio\n );\n event RefundedSlashedLegacyDelegation(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 amount\n );\n\n event DeactivatedValidator(\n uint256 indexed validatorID,\n uint256 deactivatedEpoch,\n uint256 deactivatedTime\n );\n event ChangedValidatorStatus(uint256 indexed validatorID, uint256 status);\n event AnnouncedRedirection(address indexed from, address indexed to);\n\n function currentSealedEpoch() external view returns (uint256);\n\n function getEpochSnapshot(uint256 epoch)\n external\n view\n returns (\n uint256 endTime,\n uint256 endBlock,\n uint256 epochFee,\n uint256 baseRewardPerSecond,\n uint256 totalStake,\n uint256 totalSupply\n );\n\n function getStake(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getValidator(uint256 validatorID)\n external\n view\n returns (\n uint256 status,\n uint256 receivedStake,\n address auth,\n uint256 createdEpoch,\n uint256 createdTime,\n uint256 deactivatedTime,\n uint256 deactivatedEpoch\n );\n\n function getValidatorID(address auth) external view returns (uint256);\n\n function getValidatorPubkey(uint256 validatorID)\n external\n view\n returns (bytes memory);\n\n function pubkeyAddressvalidatorID(address pubkeyAddress)\n external\n view\n returns (uint256);\n\n function getWithdrawalRequest(\n address delegator,\n uint256 validatorID,\n uint256 wrID\n )\n external\n view\n returns (\n uint256 epoch,\n uint256 time,\n uint256 amount\n );\n\n function isOwner() external view returns (bool);\n\n function lastValidatorID() external view returns (uint256);\n\n function minGasPrice() external view returns (uint256);\n\n function owner() external view returns (address);\n\n function renounceOwnership() external;\n\n function slashingRefundRatio(uint256 validatorID)\n external\n view\n returns (uint256);\n\n function stashedRewardsUntilEpoch(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function totalActiveStake() external view returns (uint256);\n\n function totalStake() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function transferOwnership(address newOwner) external;\n\n function treasuryAddress() external view returns (address);\n\n function version() external pure returns (bytes3);\n\n function currentEpoch() external view returns (uint256);\n\n function updateConstsAddress(address v) external;\n\n function constsAddress() external view returns (address);\n\n function getEpochValidatorIDs(uint256 epoch)\n external\n view\n returns (uint256[] memory);\n\n function getEpochReceivedStake(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochAccumulatedRewardPerToken(\n uint256 epoch,\n uint256 validatorID\n ) external view returns (uint256);\n\n function getEpochAccumulatedUptime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochAverageUptime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint32);\n\n function getEpochAccumulatedOriginatedTxsFee(\n uint256 epoch,\n uint256 validatorID\n ) external view returns (uint256);\n\n function getEpochOfflineTime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochOfflineBlocks(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochEndBlock(uint256 epoch) external view returns (uint256);\n\n function rewardsStash(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function createValidator(bytes calldata pubkey) external payable;\n\n function getSelfStake(uint256 validatorID) external view returns (uint256);\n\n function delegate(uint256 validatorID) external payable;\n\n function undelegate(\n uint256 validatorID,\n uint256 wrID,\n uint256 amount\n ) external;\n\n function isSlashed(uint256 validatorID) external view returns (bool);\n\n function withdraw(uint256 validatorID, uint256 wrID) external;\n\n function deactivateValidator(uint256 validatorID, uint256 status) external;\n\n function pendingRewards(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function stashRewards(address delegator, uint256 validatorID) external;\n\n function claimRewards(uint256 validatorID) external;\n\n function restakeRewards(uint256 validatorID) external;\n\n function updateSlashingRefundRatio(uint256 validatorID, uint256 refundRatio)\n external;\n\n function updateTreasuryAddress(address v) external;\n\n function burnFTM(uint256 amount) external;\n\n function sealEpoch(\n uint256[] calldata offlineTime,\n uint256[] calldata offlineBlocks,\n uint256[] calldata uptimes,\n uint256[] calldata originatedTxsFee\n ) external;\n\n function sealEpochValidators(uint256[] calldata nextValidatorIDs) external;\n\n function initialize(\n uint256 sealedEpoch,\n uint256 _totalSupply,\n address nodeDriver,\n address consts,\n address _owner\n ) external;\n\n function setGenesisValidator(\n address auth,\n uint256 validatorID,\n bytes calldata pubkey,\n uint256 createdTime\n ) external;\n\n function setGenesisDelegation(\n address delegator,\n uint256 validatorID,\n uint256 stake\n ) external;\n\n function updateStakeSubscriberAddress(address v) external;\n\n function stakeSubscriberAddress() external view returns (address);\n\n function setRedirectionAuthorizer(address v) external;\n\n function announceRedirection(address to) external;\n\n function initiateRedirection(address from, address to) external;\n\n function redirect(address to) external;\n}\n" + }, + "contracts/interfaces/sonic/ISwapXGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IGauge {\n function owner() external view returns (address);\n\n function TOKEN() external view returns (address);\n\n function DISTRIBUTION() external view returns (address);\n\n function balanceOf(address account) external view returns (uint256);\n\n function claimFees() external returns (uint256 claimed0, uint256 claimed1);\n\n function deposit(uint256 amount) external;\n\n function depositAll() external;\n\n function earned(address account) external view returns (uint256);\n\n function getReward() external;\n\n function getReward(address _user) external;\n\n function isForPair() external view returns (bool);\n\n function lastTimeRewardApplicable() external view returns (uint256);\n\n function lastUpdateTime() external view returns (uint256);\n\n function notifyRewardAmount(address token, uint256 reward) external;\n\n function periodFinish() external view returns (uint256);\n\n function rewardForDuration() external view returns (uint256);\n\n function rewardPerToken() external view returns (uint256);\n\n function rewardPerTokenStored() external view returns (uint256);\n\n function rewardRate() external view returns (uint256);\n\n function rewardToken() external view returns (address);\n\n function rewards(address) external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function userRewardPerTokenPaid(address) external view returns (uint256);\n\n function withdraw(uint256 amount) external;\n\n function withdrawAll() external;\n\n function withdrawAllAndHarvest() external;\n\n function withdrawExcess(address token, uint256 amount) external;\n\n function emergency() external returns (bool);\n\n function emergencyWithdraw() external;\n\n function activateEmergencyMode() external;\n\n function stopEmergencyMode() external;\n}\n" + }, + "contracts/interfaces/sonic/ISwapXPair.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IPair {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n\n event Mint(address indexed sender, uint256 amount0, uint256 amount1);\n event Burn(\n address indexed sender,\n uint256 amount0,\n uint256 amount1,\n address indexed to\n );\n event Swap(\n address indexed sender,\n uint256 amount0In,\n uint256 amount1In,\n uint256 amount0Out,\n uint256 amount1Out,\n address indexed to\n );\n event Claim(\n address indexed sender,\n address indexed recipient,\n uint256 amount0,\n uint256 amount1\n );\n\n function metadata()\n external\n view\n returns (\n uint256 dec0,\n uint256 dec1,\n uint256 r0,\n uint256 r1,\n bool st,\n address t0,\n address t1\n );\n\n function claimFees() external returns (uint256, uint256);\n\n function tokens() external view returns (address, address);\n\n function token0() external view returns (address);\n\n function token1() external view returns (address);\n\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function swap(\n uint256 amount0Out,\n uint256 amount1Out,\n address to,\n bytes calldata data\n ) external;\n\n function burn(address to)\n external\n returns (uint256 amount0, uint256 amount1);\n\n function mint(address to) external returns (uint256 liquidity);\n\n function getReserves()\n external\n view\n returns (\n uint256 _reserve0,\n uint256 _reserve1,\n uint256 _blockTimestampLast\n );\n\n function getAmountOut(uint256, address) external view returns (uint256);\n\n // ERC20 methods\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n\n function totalSupply() external view returns (uint256);\n\n function balanceOf(address) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 value) external returns (bool);\n\n function claimable0(address _user) external view returns (uint256);\n\n function claimable1(address _user) external view returns (uint256);\n\n function isStable() external view returns (bool);\n\n function skim(address to) external;\n\n function sync() external;\n}\n" + }, + "contracts/interfaces/sonic/IVoterV3.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IVoterV3 {\n /// @notice create a gauge\n function createGauge(address _pool, uint256 _gaugeType)\n external\n returns (\n address _gauge,\n address _internal_bribe,\n address _external_bribe\n );\n\n function gauges(address _pool) external view returns (address _gauge);\n}\n" + }, + "contracts/interfaces/sonic/IWrappedSonic.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWrappedSonic {\n event Deposit(address indexed account, uint256 value);\n event Withdrawal(address indexed account, uint256 value);\n event Transfer(address indexed from, address indexed to, uint256 value);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 value) external returns (bool);\n\n function balanceOf(address account) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function depositFor(address account) external payable returns (bool);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address to, uint256 value) external returns (bool);\n\n function transferFrom(\n address from,\n address to,\n uint256 value\n ) external returns (bool);\n\n function withdraw(uint256 value) external;\n\n function withdrawTo(address account, uint256 value) external returns (bool);\n}\n" + }, + "contracts/interfaces/Tether.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// slither-disable-start erc20-interface\ninterface Tether {\n function transfer(address to, uint256 value) external;\n\n function transferFrom(\n address from,\n address to,\n uint256 value\n ) external;\n\n function balanceOf(address) external view returns (uint256);\n\n function approve(address _spender, uint256 _value) external;\n}\n// slither-disable-end erc20-interface\n" + }, + "contracts/interfaces/uniswap/IUniswapUniversalRouter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapUniversalRouter {\n /// @notice Executes encoded commands along with provided inputs. Reverts if deadline has expired.\n /// @param commands A set of concatenated commands, each 1 byte in length\n /// @param inputs An array of byte strings containing abi encoded inputs for each command\n /// @param deadline The deadline by which the transaction must be executed\n function execute(\n bytes calldata commands,\n bytes[] calldata inputs,\n uint256 deadline\n ) external payable;\n\n function execute(bytes calldata commands, bytes[] calldata inputs)\n external\n payable;\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV2Pair.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapV2Pair {\n function token0() external view returns (address);\n\n function token1() external view returns (address);\n\n function getReserves()\n external\n view\n returns (\n uint112 reserve0,\n uint112 reserve1,\n uint32 blockTimestampLast\n );\n\n function price0CumulativeLast() external view returns (uint256);\n\n function price1CumulativeLast() external view returns (uint256);\n\n function sync() external;\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV2Router02.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapV2Router {\n function WETH() external pure returns (address);\n\n function swapExactTokensForTokens(\n uint256 amountIn,\n uint256 amountOutMin,\n address[] calldata path,\n address to,\n uint256 deadline\n ) external returns (uint256[] memory amounts);\n\n function addLiquidity(\n address tokenA,\n address tokenB,\n uint256 amountADesired,\n uint256 amountBDesired,\n uint256 amountAMin,\n uint256 amountBMin,\n address to,\n uint256 deadline\n )\n external\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 liquidity\n );\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV3Router.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// -- Solididy v0.5.x compatible interface\ninterface IUniswapV3Router {\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n}\n" + }, + "contracts/mocks/beacon/ExecutionLayerConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { GeneralPurposeToConsensusLayerRequest } from \"./GeneralPurposeToConsensusLayerRequest.sol\";\n\ncontract ExecutionLayerConsolidation is GeneralPurposeToConsensusLayerRequest {\n event ConsolidationRequestIssued(bytes sourceKey, bytes targetKey);\n bytes public lastSource;\n bytes public lastTarget;\n\n function handleRequest(bytes calldata data) internal override {\n // parameters should consist of twice the 48 bytes for 2 public keys\n require(data.length == 96, \"Invalid Consolidation data\");\n lastSource = data[:48];\n lastTarget = data[48:];\n\n emit ConsolidationRequestIssued(lastSource, lastTarget);\n }\n}\n" + }, + "contracts/mocks/beacon/ExecutionLayerWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { GeneralPurposeToConsensusLayerRequest } from \"./GeneralPurposeToConsensusLayerRequest.sol\";\n\ncontract ExecutionLayerWithdrawal is GeneralPurposeToConsensusLayerRequest {\n event WithdrawalRequestIssued(bytes publicKey, uint64 amount);\n\n bytes public lastPublicKey;\n uint64 public lastAmount;\n\n function handleRequest(bytes calldata data) internal override {\n // parameters should consist of 48 bytes for public key and 8 bytes for uint64 amount\n require(data.length == 56, \"Invalid Withdrawal data\");\n lastPublicKey = data[:48];\n lastAmount = uint64(bytes8(data[48:]));\n emit WithdrawalRequestIssued(lastPublicKey, lastAmount);\n }\n}\n" + }, + "contracts/mocks/beacon/GeneralPurposeToConsensusLayerRequest.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nabstract contract GeneralPurposeToConsensusLayerRequest {\n // solhint-disable no-complex-fallback\n fallback() external payable {\n // fee requested\n if (msg.data.length == 0) {\n uint256 fee = _fee();\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Return a uint256 value\n mstore(0, fee)\n return(0, 32) // Return 32 bytes from memory\n }\n }\n\n // else handle request\n handleRequest(msg.data);\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _fee() internal virtual returns (uint256) {\n return 1;\n }\n\n function handleRequest(bytes calldata data) internal virtual;\n}\n" + }, + "contracts/mocks/BurnableERC20.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ninterface IBurnableERC20 {\n function burn(uint256 value) external returns (bool);\n\n function burnFrom(address account, uint256 value) external returns (bool);\n}\n\n/**\n * @title BurnableERC20\n * @dev Exposes the burn function of ERC20 for tests\n */\nabstract contract BurnableERC20 is IBurnableERC20, ERC20 {\n /**\n * @dev Function to burn tokens\n * @param value The amount of tokens to burn.\n * @return A boolean that indicates if the operation was successful.\n */\n function burn(uint256 value) public virtual override returns (bool) {\n _burn(msg.sender, value);\n return true;\n }\n\n /**\n * @dev Function to burn tokens from a specific account\n * @param account The address with the tokens to burn.\n * @param value The amount of tokens to burn.\n * @return A boolean that indicates if the operation was successful.\n */\n function burnFrom(address account, uint256 value)\n public\n override\n returns (bool)\n {\n _burn(account, value);\n return true;\n }\n}\n" + }, + "contracts/mocks/curve/Mock3CRV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20, MintableERC20 } from \"../MintableERC20.sol\";\nimport { BurnableERC20 } from \"../BurnableERC20.sol\";\n\ncontract Mock3CRV is MintableERC20, BurnableERC20 {\n constructor() ERC20(\"Curve.fi DAI/USDC/USDT\", \"3Crv\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/curve/MockBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { MockRewardPool } from \"./MockRewardPool.sol\";\n\nimport { IRewardStaking } from \"../../strategies/IRewardStaking.sol\";\nimport { IMintableERC20, MintableERC20, ERC20 } from \"../MintableERC20.sol\";\nimport { IBurnableERC20, BurnableERC20 } from \"../BurnableERC20.sol\";\n\ncontract MockDepositToken is MintableERC20, BurnableERC20 {\n constructor() ERC20(\"DCVX\", \"CVX Deposit Token\") {}\n}\n\ncontract MockBooster {\n using SafeERC20 for IERC20;\n\n struct PoolInfo {\n address lptoken;\n address token;\n address crvRewards;\n }\n\n address public minter; // this is CVx for the booster on live\n address public crv; // Curve rewards token\n address public cvx; // Convex rewards token\n mapping(uint256 => PoolInfo) public poolInfo;\n\n constructor(\n address _rewardsMinter,\n address _crv,\n address _cvx\n ) public {\n minter = _rewardsMinter;\n crv = _crv;\n cvx = _cvx;\n }\n\n function setPool(uint256 pid, address _lpToken)\n external\n returns (address rewards)\n {\n address token = address(new MockDepositToken());\n // Deploy a new Convex Rewards Pool\n rewards = address(\n new MockRewardPool(pid, token, crv, cvx, address(this))\n );\n\n poolInfo[pid] = PoolInfo({\n lptoken: _lpToken,\n token: token,\n crvRewards: rewards\n });\n }\n\n function deposit(\n uint256 _pid,\n uint256 _amount,\n bool _stake\n ) public returns (bool) {\n PoolInfo storage pool = poolInfo[_pid];\n\n address lptoken = pool.lptoken;\n\n // hold on to the Curve LP tokens\n IERC20(lptoken).safeTransferFrom(msg.sender, address(this), _amount);\n\n address token = pool.token;\n if (_stake) {\n // mint Convex pool LP tokens and stake in rewards contract on user behalf\n IMintableERC20(token).mint(_amount);\n address rewardContract = pool.crvRewards;\n IERC20(token).safeApprove(rewardContract, 0);\n IERC20(token).safeApprove(rewardContract, _amount);\n IRewardStaking(rewardContract).stakeFor(msg.sender, _amount);\n } else {\n // mint Convex pool LP tokens and send to user\n IMintableERC20(token).mint(_amount);\n IERC20(token).transfer(msg.sender, _amount);\n }\n return true;\n }\n\n // Deposit all Curve LP tokens and stake\n function depositAll(uint256 _pid, bool _stake) external returns (bool) {\n address lptoken = poolInfo[_pid].lptoken;\n uint256 balance = IERC20(lptoken).balanceOf(msg.sender);\n deposit(_pid, balance, _stake);\n return true;\n }\n\n // withdraw Curve LP tokens\n function _withdraw(\n uint256 _pid,\n uint256 _amount,\n address _from,\n address _to\n ) internal {\n PoolInfo storage pool = poolInfo[_pid];\n\n // burn the Convex pool LP tokens\n IBurnableERC20(pool.token).burnFrom(_from, _amount);\n\n // return the Curve LP tokens\n IERC20(pool.lptoken).safeTransfer(_to, _amount);\n }\n\n // withdraw Curve LP tokens\n function withdraw(uint256 _pid, uint256 _amount) public returns (bool) {\n _withdraw(_pid, _amount, msg.sender, msg.sender);\n return true;\n }\n\n // withdraw all Curve LP tokens\n function withdrawAll(uint256 _pid) public returns (bool) {\n address token = poolInfo[_pid].token;\n uint256 userBal = IERC20(token).balanceOf(msg.sender);\n withdraw(_pid, userBal);\n return true;\n }\n\n // allow reward contracts to send here and withdraw to user\n function withdrawTo(\n uint256 _pid,\n uint256 _amount,\n address _to\n ) external returns (bool) {\n address rewardContract = poolInfo[_pid].crvRewards;\n require(msg.sender == rewardContract, \"!auth\");\n\n _withdraw(_pid, _amount, msg.sender, _to);\n return true;\n }\n\n // callback from reward contract when crv is received.\n function rewardClaimed(\n uint256 _pid,\n // solhint-disable-next-line no-unused-vars\n address _address,\n uint256 _amount\n ) external returns (bool) {\n address rewardContract = poolInfo[_pid].crvRewards;\n require(msg.sender == rewardContract, \"!auth\");\n\n //mint reward tokens\n // and transfer it\n IMintableERC20(minter).mint(_amount);\n IERC20(minter).transfer(msg.sender, _amount);\n return true;\n }\n}\n" + }, + "contracts/mocks/curve/MockCRV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../MintableERC20.sol\";\n\ncontract MockCRV is MintableERC20 {\n constructor() ERC20(\"Curve DAO Token\", \"CRV\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/curve/MockCRVMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\n\ncontract MockCRVMinter {\n address crv;\n\n constructor(address _crv) {\n crv = _crv;\n }\n\n function mint(address _address) external {\n uint256 amount = 2e18;\n IMintableERC20(crv).mint(amount);\n IERC20(crv).transfer(_address, amount);\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveAbstractMetapool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { MintableERC20, IMintableERC20 } from \"../MintableERC20.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport \"../../utils/Helpers.sol\";\n\nabstract contract MockCurveAbstractMetapool is MintableERC20 {\n using StableMath for uint256;\n\n address[] public coins;\n uint256[2] public balances;\n\n // Returns the same amount of LP tokens in 1e18 decimals\n function add_liquidity(uint256[2] calldata _amounts, uint256 _minAmount)\n external\n returns (uint256 lpAmount)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transferFrom(\n msg.sender,\n address(this),\n _amounts[i]\n );\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to sum\n lpAmount += _amounts[i].scaleBy(18, assetDecimals);\n balances[i] = balances[i] + _amounts[i];\n }\n }\n // Hacky way of simulating slippage to check _minAmount\n if (lpAmount == 29000e18) lpAmount = 14500e18;\n require(lpAmount >= _minAmount, \"Slippage ruined your day\");\n // Send LP token to sender, e.g. 3CRV\n _mint(msg.sender, lpAmount);\n }\n\n // Dumb implementation that returns the same amount\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n public\n view\n returns (uint256 lpAmount)\n {\n uint256 assetDecimals = Helpers.getDecimals(coins[uint128(_index)]);\n lpAmount = _amount.scaleBy(assetDecimals, 18);\n }\n\n function remove_liquidity_one_coin(\n uint256 _lpAmount,\n int128 _index,\n // solhint-disable-next-line no-unused-vars\n uint256 _minAmount\n ) external returns (uint256 amount) {\n _burn(msg.sender, _lpAmount);\n uint256[] memory amounts = new uint256[](coins.length);\n amounts[uint128(_index)] = _lpAmount;\n amount = calc_withdraw_one_coin(_lpAmount, _index);\n balances[uint128(_index)] -= amount;\n IERC20(coins[uint128(_index)]).transfer(msg.sender, amount);\n }\n\n function get_virtual_price() external pure returns (uint256) {\n return 1e18;\n }\n\n // solhint-disable-next-line no-unused-vars\n function remove_liquidity(uint256 _amount, uint256[2] memory _min_amounts)\n public\n returns (uint256[2] memory amounts)\n {\n _burn(msg.sender, _amount);\n uint256 totalSupply = totalSupply();\n for (uint256 i = 0; i < 2; i++) {\n amounts[i] = totalSupply > 0\n ? (_amount * IERC20(coins[i]).balanceOf(address(this))) /\n totalSupply\n : IERC20(coins[i]).balanceOf(address(this));\n balances[i] -= amounts[i];\n IERC20(coins[i]).transfer(msg.sender, amounts[i]);\n }\n }\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens\n ) public returns (uint256) {\n return\n _remove_liquidity_imbalance(\n _amounts,\n _max_burned_tokens,\n msg.sender\n );\n }\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens,\n address _reveiver\n ) public returns (uint256) {\n return\n _remove_liquidity_imbalance(\n _amounts,\n _max_burned_tokens,\n _reveiver\n );\n }\n\n function _remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens,\n address _reveiver\n ) internal returns (uint256 lpTokens) {\n lpTokens = _max_burned_tokens;\n _burn(msg.sender, lpTokens);\n for (uint256 i = 0; i < _amounts.length; i++) {\n balances[i] -= _amounts[i];\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transfer(_reveiver, _amounts[i]);\n }\n }\n }\n\n // Dumb implementation that sums the scaled amounts\n function calc_token_amount(uint256[2] memory _amounts, bool)\n public\n view\n returns (uint256 lpTokens)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to lpTokens\n lpTokens += _amounts[i].scaleBy(18, assetDecimals);\n }\n }\n\n /// @notice 0.02% fee\n function fee() external pure returns (uint256) {\n return 2000000;\n }\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n\n function burnFrom(address from, uint256 value) public {\n _burn(from, value);\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveGauge.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { ICurveGauge } from \"../../strategies/ICurveGauge.sol\";\n\ncontract MockCurveGauge is ICurveGauge {\n mapping(address => uint256) private _balances;\n address lpToken;\n\n constructor(address _lpToken) {\n lpToken = _lpToken;\n }\n\n function balanceOf(address account) public view override returns (uint256) {\n return _balances[account];\n }\n\n function deposit(uint256 _value, address _account) external override {\n IERC20(lpToken).transferFrom(msg.sender, address(this), _value);\n _balances[_account] += _value;\n }\n\n function withdraw(uint256 _value) external override {\n IERC20(lpToken).transfer(msg.sender, _value);\n // solhint-disable-next-line reentrancy\n _balances[msg.sender] -= _value;\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveMetapool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { MockCurveAbstractMetapool } from \"./MockCurveAbstractMetapool.sol\";\nimport \"../MintableERC20.sol\";\n\ncontract MockCurveMetapool is MockCurveAbstractMetapool {\n constructor(address[2] memory _coins)\n ERC20(\"Curve.fi 3pool/OUSD metapool\", \"3crv_OUSD\")\n {\n coins = _coins;\n }\n}\n" + }, + "contracts/mocks/curve/MockCurvePool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IBurnableERC20 } from \"../BurnableERC20.sol\";\n\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\nimport { ICurvePool } from \"../../strategies/ICurvePool.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport \"../../utils/Helpers.sol\";\n\ncontract MockCurvePool {\n using StableMath for uint256;\n\n address[] public coins;\n uint256[3] public balances;\n address lpToken;\n uint256 public slippage = 1 ether;\n\n constructor(address[3] memory _coins, address _lpToken) {\n coins = _coins;\n lpToken = _lpToken;\n }\n\n function setCoins(address[] memory _coins) external {\n coins = _coins;\n }\n\n // Returns the same amount of LP tokens in 1e18 decimals\n function add_liquidity(uint256[3] calldata _amounts, uint256 _minAmount)\n external\n {\n uint256 sum = 0;\n for (uint256 i = 0; i < _amounts.length; i++) {\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transferFrom(\n msg.sender,\n address(this),\n _amounts[i]\n );\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to sum\n sum += _amounts[i].scaleBy(18, assetDecimals);\n balances[i] = balances[i] + _amounts[i];\n }\n }\n // Hacky way of simulating slippage to check _minAmount\n if (sum == 29000e18) sum = 14500e18;\n require(sum >= _minAmount, \"Slippage ruined your day\");\n // Send LP token to sender, e.g. 3CRV\n IMintableERC20(lpToken).mint(sum);\n IERC20(lpToken).transfer(msg.sender, sum);\n }\n\n // Dumb implementation that returns the same amount\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n public\n view\n returns (uint256)\n {\n uint256 assetDecimals = Helpers.getDecimals(coins[uint128(_index)]);\n return _amount.scaleBy(assetDecimals, 18);\n }\n\n function remove_liquidity_one_coin(\n uint256 _amount,\n int128 _index,\n // solhint-disable-next-line no-unused-vars\n uint256 _minAmount\n ) external {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _amount);\n uint256[] memory amounts = new uint256[](coins.length);\n amounts[uint128(_index)] = _amount;\n uint256 coinAmount = calc_withdraw_one_coin(_amount, _index);\n balances[uint128(_index)] -= coinAmount;\n IERC20(coins[uint128(_index)]).transfer(msg.sender, coinAmount);\n }\n\n function get_virtual_price() external pure returns (uint256) {\n return 1e18;\n }\n\n // solhint-disable-next-line no-unused-vars\n function remove_liquidity(uint256 _lpAmount, uint256[3] memory _min_amounts)\n public\n {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _lpAmount);\n uint256 totalSupply = IERC20(lpToken).totalSupply();\n for (uint256 i = 0; i < 3; i++) {\n uint256 coinAmount = totalSupply > 0\n ? (_lpAmount * IERC20(coins[i]).balanceOf(address(this))) /\n totalSupply\n : IERC20(coins[i]).balanceOf(address(this));\n balances[i] -= coinAmount;\n IERC20(coins[i]).transfer(msg.sender, coinAmount);\n }\n }\n\n function remove_liquidity_imbalance(\n uint256[3] memory _amounts,\n uint256 _max_burned_tokens\n ) public {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _max_burned_tokens);\n // For each coin, transfer to the caller\n for (uint256 i = 0; i < _amounts.length; i++) {\n balances[i] -= _amounts[i];\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transfer(msg.sender, _amounts[i]);\n }\n }\n }\n\n // Dumb implementation that sums the scaled amounts\n function calc_token_amount(uint256[3] memory _amounts, bool)\n public\n view\n returns (uint256 lpTokens)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to lpTokens\n lpTokens += _amounts[i].scaleBy(18, assetDecimals);\n }\n }\n\n function fee() external pure returns (uint256) {\n return 1000000;\n }\n\n function exchange(\n uint256 coin0,\n uint256 coin1,\n uint256 amountIn,\n uint256 minAmountOut\n ) external returns (uint256 amountOut) {\n IERC20(coins[coin0]).transferFrom(msg.sender, address(this), amountIn);\n amountOut = (minAmountOut * slippage) / 1 ether;\n require(amountOut >= minAmountOut, \"Slippage error\");\n IMintableERC20(coins[coin1]).mintTo(msg.sender, amountOut);\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n}\n" + }, + "contracts/mocks/curve/MockCVX.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../MintableERC20.sol\";\n\ncontract MockCVX is MintableERC20 {\n constructor() ERC20(\"CVX\", \"CVX DAO Token\") {}\n}\n" + }, + "contracts/mocks/curve/MockRewardPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\ninterface IDeposit {\n function poolInfo(uint256)\n external\n view\n returns (\n address,\n address,\n address,\n address,\n address,\n bool\n );\n\n function rewardClaimed(\n uint256,\n address,\n uint256\n ) external;\n\n function withdrawTo(\n uint256,\n uint256,\n address\n ) external;\n}\n\ncontract MockRewardPool {\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 public pid;\n address public stakingToken;\n address public rewardTokenA;\n address public rewardTokenB;\n address public operator;\n\n uint256 private _totalSupply;\n\n mapping(address => uint256) private _balances;\n mapping(address => uint256) public rewards;\n\n constructor(\n uint256 _pid,\n address _stakingToken,\n address _rewardTokenA,\n address _rewardTokenB,\n address _operator\n ) public {\n pid = _pid;\n stakingToken = _stakingToken;\n rewardTokenA = _rewardTokenA;\n rewardTokenB = _rewardTokenB;\n operator = _operator;\n }\n\n function totalSupply() public view returns (uint256) {\n return _totalSupply;\n }\n\n function balanceOf(address account) public view returns (uint256) {\n return _balances[account];\n }\n\n function stakeFor(address _for, uint256 _amount) public returns (bool) {\n require(_amount > 0, \"RewardPool : Cannot stake 0\");\n\n //give to _for\n _totalSupply = _totalSupply.add(_amount);\n _balances[_for] = _balances[_for].add(_amount);\n\n //take away from sender\n IERC20(stakingToken).safeTransferFrom(\n msg.sender,\n address(this),\n _amount\n );\n\n return true;\n }\n\n function withdrawAndUnwrap(uint256 amount, bool claim)\n public\n returns (bool)\n {\n _totalSupply = _totalSupply.sub(amount);\n _balances[msg.sender] = _balances[msg.sender].sub(amount);\n\n //tell operator to withdraw from here directly to user\n IDeposit(operator).withdrawTo(pid, amount, msg.sender);\n\n //get rewards too\n if (claim) {\n getReward(msg.sender, true);\n }\n return true;\n }\n\n function withdrawAllAndUnwrap(bool claim) external {\n withdrawAndUnwrap(_balances[msg.sender], claim);\n }\n\n // solhint-disable-next-line no-unused-vars\n function getReward(address _account, bool _claimExtras)\n public\n returns (bool)\n {\n IMintableERC20(rewardTokenA).mint(2 * 1e18);\n IERC20(rewardTokenA).transfer(_account, 2 * 1e18);\n\n IMintableERC20(rewardTokenB).mint(3 * 1e18);\n IERC20(rewardTokenB).transfer(_account, 3 * 1e18);\n\n return true;\n }\n\n function getReward() public returns (bool) {\n getReward(msg.sender, true);\n }\n}\n" + }, + "contracts/mocks/ForceEtherSender.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract ForceEtherSender {\n // Constructor to optionally receive Ether upon deployment\n constructor() payable {}\n\n // Function to allow the contract to receive Ether\n receive() external payable {}\n\n // Function to self-destruct and force-send Ether to an address\n function forceSend(address payable recipient) external {\n // Requires that the contract has a balance greater than 0\n require(address(this).balance > 0, \"No Ether to send\");\n\n // selfdestruct sends all Ether held by the contract to the recipient\n selfdestruct(recipient);\n }\n}\n" + }, + "contracts/mocks/MintableERC20.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ninterface IMintableERC20 {\n function mint(uint256 value) external;\n\n function mintTo(address to, uint256 value) external;\n}\n\n/**\n * @title MintableERC20\n * @dev Exposes the mint function of ERC20 for tests\n */\nabstract contract MintableERC20 is IMintableERC20, ERC20 {\n /**\n * @dev Function to mint tokens\n * @param _value The amount of tokens to mint.\n */\n function mint(uint256 _value) public virtual override {\n _mint(msg.sender, _value);\n }\n\n /**\n * @dev Function to mint tokens\n * @param _to Address to mint to.\n * @param _value The amount of tokens to mint.\n */\n function mintTo(address _to, uint256 _value) public virtual override {\n _mint(_to, _value);\n }\n}\n" + }, + "contracts/mocks/Mock1InchSwapRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { SwapDescription } from \"../interfaces/IOneInch.sol\";\n\ncontract Mock1InchSwapRouter {\n using SafeERC20 for IERC20;\n\n event MockSwap(address executor, bytes permitData, bytes executorData);\n\n event MockSwapDesc(\n address srcToken,\n address dstToken,\n address srcReceiver,\n address dstReceiver,\n uint256 amount,\n uint256 minReturnAmount,\n uint256 flags\n );\n\n event MockUnoswapTo(\n address recipient,\n address srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] pools\n );\n\n event MockUniswapV3SwapTo(\n address recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] pools\n );\n\n /**\n * @dev transfers the shource asset and returns the minReturnAmount of the destination asset.\n */\n function swap(\n address executor,\n SwapDescription calldata desc,\n bytes calldata permitData,\n bytes calldata executorData\n ) public returns (uint256 returnAmount, uint256 spentAmount) {\n // Transfer the source tokens to the receiver contract\n IERC20(desc.srcToken).safeTransferFrom(\n msg.sender,\n desc.srcReceiver,\n desc.amount\n );\n\n // Transfer the destination tokens to the recipient\n IERC20(desc.dstToken).safeTransfer(\n desc.dstReceiver,\n desc.minReturnAmount\n );\n\n emit MockSwap(executor, permitData, executorData);\n _swapDesc(desc);\n returnAmount = 0;\n spentAmount = 0;\n }\n\n function _swapDesc(SwapDescription calldata desc) public {\n emit MockSwapDesc(\n address(desc.srcToken),\n address(desc.dstToken),\n desc.srcReceiver,\n desc.dstReceiver,\n desc.amount,\n desc.minReturnAmount,\n desc.flags\n );\n }\n\n /**\n * @dev only transfers the source asset to this contract.\n * Ideally it would return the destination asset but that's encoded in the pools array.\n */\n function unoswapTo(\n address payable recipient,\n address srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) public returns (uint256 returnAmount) {\n // transfer the from asset from the caller\n IERC20(srcToken).safeTransferFrom(msg.sender, address(this), amount);\n\n emit MockUnoswapTo(recipient, srcToken, amount, minReturn, pools);\n returnAmount = 0;\n }\n\n /**\n * @dev does not do any transfers. Just emits MockUniswapV3SwapTo.\n */\n function uniswapV3SwapTo(\n address payable recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) public returns (uint256 returnAmount) {\n emit MockUniswapV3SwapTo(recipient, amount, minReturn, pools);\n returnAmount = 0;\n }\n}\n" + }, + "contracts/mocks/MockAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { IAaveLendingPool, ILendingPoolAddressesProvider } from \"../strategies/IAave.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// 1. User calls 'getLendingPool'\n// 2. User calls 'deposit' (Aave)\n// - Deposit their underlying\n// - Mint aToken to them\n// 3. User calls redeem (aToken)\n// - Retrieve their aToken\n// - Return equal amount of underlying\n\ncontract MockAToken is MintableERC20 {\n address public lendingPool;\n IERC20 public underlyingToken;\n using SafeERC20 for IERC20;\n\n constructor(\n address _lendingPool,\n string memory _name,\n string memory _symbol,\n IERC20 _underlyingToken\n ) ERC20(_name, _symbol) {\n lendingPool = _lendingPool;\n underlyingToken = _underlyingToken;\n // addMinter(_lendingPool);\n }\n\n function decimals() public view override returns (uint8) {\n return ERC20(address(underlyingToken)).decimals();\n }\n\n function poolRedeem(uint256 _amount, address _to) external {\n require(msg.sender == lendingPool, \"pool only\");\n // Redeem these a Tokens\n _burn(_to, _amount);\n // For the underlying\n underlyingToken.safeTransferFrom(lendingPool, _to, _amount);\n }\n}\n\ncontract MockAave is IAaveLendingPool, ILendingPoolAddressesProvider {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n mapping(address => address) reserveToAToken;\n address pool = address(this);\n address payable core = payable(address(this));\n uint256 factor;\n\n function addAToken(address _aToken, address _underlying) public {\n IERC20(_underlying).safeApprove(_aToken, 0);\n IERC20(_underlying).safeApprove(_aToken, type(uint256).max);\n reserveToAToken[_underlying] = _aToken;\n }\n\n // set the reserve factor / basically the interest on deposit\n // in 18 precision\n // so 0.5% would be 5 * 10 ^ 15\n function setFactor(uint256 factor_) public {\n factor = factor_;\n }\n\n function deposit(\n address _reserve,\n uint256 _amount,\n address _to,\n uint16 /*_referralCode*/\n ) external override {\n uint256 previousBal = IERC20(reserveToAToken[_reserve]).balanceOf(\n msg.sender\n );\n uint256 interest = previousBal.mulTruncate(factor);\n MintableERC20(reserveToAToken[_reserve]).mintTo(msg.sender, interest);\n // Take their reserve\n IERC20(_reserve).safeTransferFrom(msg.sender, address(this), _amount);\n // Credit them with aToken\n MintableERC20(reserveToAToken[_reserve]).mintTo(_to, _amount);\n }\n\n function withdraw(\n address asset,\n uint256 amount,\n address to\n ) external override returns (uint256) {\n MockAToken atoken = MockAToken(reserveToAToken[asset]);\n atoken.poolRedeem(amount, to);\n return amount;\n }\n\n function getLendingPool() external view override returns (address) {\n return pool;\n }\n}\n" + }, + "contracts/mocks/MockAaveIncentivesController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { MockStkAave } from \"./MockStkAave.sol\";\n\ncontract MockAaveIncentivesController {\n mapping(address => uint256) private rewards;\n MockStkAave public REWARD_TOKEN;\n\n constructor(address _reward_token) {\n REWARD_TOKEN = MockStkAave(_reward_token);\n }\n\n function setRewardsBalance(address user, uint256 amount) external {\n rewards[user] = amount;\n }\n\n /**\n * @dev Returns the total of rewards of an user, already accrued + not yet accrued\n * @param user The address of the user\n * @return The rewards\n **/\n // solhint-disable-next-line no-unused-vars\n function getRewardsBalance(address[] calldata assets, address user)\n external\n view\n returns (uint256)\n {\n return rewards[user];\n }\n\n /**\n * @dev Claims reward for an user, on all the assets of the lending pool, accumulating the pending rewards\n * @param amount Amount of rewards to claim\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewards(\n // solhint-disable-next-line no-unused-vars\n address[] calldata assets,\n uint256 amount,\n address to\n ) external returns (uint256) {\n require(amount > 0);\n require(rewards[to] == amount);\n REWARD_TOKEN.mint(amount);\n require(REWARD_TOKEN.transfer(to, amount));\n // solhint-disable-next-line reentrancy\n rewards[to] = 0;\n return amount;\n }\n}\n" + }, + "contracts/mocks/MockAAVEToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAAVEToken is MintableERC20 {\n constructor() ERC20(\"AAVE\", \"AAVE\") {}\n}\n" + }, + "contracts/mocks/MockAero.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAero is MintableERC20 {\n constructor() ERC20(\"Aerodrome\", \"AERO\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/MockAura.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAura is MintableERC20 {\n constructor() ERC20(\"Aura\", \"AURA\") {}\n}\n" + }, + "contracts/mocks/MockBAL.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockBAL is MintableERC20 {\n constructor() ERC20(\"Balancer\", \"BAL\") {}\n}\n" + }, + "contracts/mocks/MockBalancerVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// import \"hardhat/console.sol\";\n\ncontract MockBalancerVault {\n using StableMath for uint256;\n uint256 public slippage = 1 ether;\n bool public transferDisabled = false;\n bool public slippageErrorDisabled = false;\n\n function swap(\n IBalancerVault.SingleSwap calldata singleSwap,\n IBalancerVault.FundManagement calldata funds,\n uint256 minAmountOut,\n uint256\n ) external returns (uint256 amountCalculated) {\n amountCalculated = (minAmountOut * slippage) / 1 ether;\n if (!slippageErrorDisabled) {\n require(amountCalculated >= minAmountOut, \"Slippage error\");\n }\n IERC20(singleSwap.assetIn).transferFrom(\n funds.sender,\n address(this),\n singleSwap.amount\n );\n if (!transferDisabled) {\n MintableERC20(singleSwap.assetOut).mintTo(\n funds.recipient,\n amountCalculated\n );\n }\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n\n function getPoolTokenInfo(bytes32 poolId, address token)\n external\n view\n returns (\n uint256,\n uint256,\n uint256,\n address\n )\n {}\n\n function disableTransfer() external {\n transferDisabled = true;\n }\n\n function disableSlippageError() external {\n slippageErrorDisabled = true;\n }\n}\n" + }, + "contracts/mocks/MockBeaconConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconConsolidation } from \"../beacon/BeaconConsolidation.sol\";\n\ncontract MockBeaconConsolidation {\n function fee() external view returns (uint256) {\n return BeaconConsolidation.fee();\n }\n\n function request(bytes calldata source, bytes calldata target)\n external\n returns (uint256 fee_)\n {\n return BeaconConsolidation.request(source, target);\n }\n}\n" + }, + "contracts/mocks/MockBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"../beacon/BeaconProofsLib.sol\";\nimport { BeaconProofs } from \"../beacon/BeaconProofs.sol\";\n\ncontract MockBeaconProofs is BeaconProofs {\n function concatGenIndices(\n uint256 index1,\n uint256 height2,\n uint256 index2\n ) external pure returns (uint256 genIndex) {\n return BeaconProofsLib.concatGenIndices(index1, height2, index2);\n }\n}\n" + }, + "contracts/mocks/MockBeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconRoots } from \"../beacon/BeaconRoots.sol\";\n\ncontract MockBeaconRoots {\n // Mapping to simulate the ring buffer: timestamp => beacon block root\n mapping(uint256 => bytes32) internal _beaconRoots;\n\n // Event to log when a new root is set (for testing)\n event RootSet(uint256 indexed timestamp, bytes32 root);\n\n // Fallback function to handle raw 32-byte timestamp input\n // solhint-disable no-complex-fallback\n fallback() external {\n // Ensure input is exactly 32 bytes (big-endian encoded timestamp)\n require(msg.data.length == 32, \"Input must be 32 bytes\");\n\n // Decode the 32-byte input as a uint256 timestamp (big-endian)\n uint256 timestamp = abi.decode(msg.data, (uint256));\n\n // Don't do any validation of timestamp so we can test any block\n\n // Retrieve the root. Will return bytes32(0) if not set.\n bytes32 root = _beaconRoots[timestamp];\n\n // Return the 32-byte root directly\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0, root)\n return(0, 32)\n }\n }\n\n // Mock function to set a beacon block root (for testing)\n function setBeaconRoot(uint256 timestamp, bytes32 root) external {\n require(timestamp > 0, \"Invalid timestamp\");\n require(root != bytes32(0), \"Invalid root\");\n\n // Store the root at the given timestamp\n _beaconRoots[timestamp] = root;\n\n emit RootSet(timestamp, root);\n }\n\n function setBeaconRoot(bytes32 root) external {\n require(root != bytes32(0), \"Invalid root\");\n\n // Store the root at the given timestamp\n _beaconRoots[block.timestamp] = root;\n\n emit RootSet(block.timestamp, root);\n }\n\n function parentBlockRoot(uint64 timestamp)\n external\n view\n returns (bytes32 parentRoot)\n {\n return BeaconRoots.parentBlockRoot(timestamp);\n }\n\n function latestBlockRoot()\n external\n view\n returns (bytes32 parentRoot, uint64 timestamp)\n {\n timestamp = uint64(block.timestamp);\n parentRoot = BeaconRoots.parentBlockRoot(timestamp);\n }\n}\n" + }, + "contracts/mocks/MockChainlinkOracleFeed.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\ncontract MockChainlinkOracleFeed is AggregatorV3Interface {\n int256 price;\n uint8 numDecimals;\n\n constructor(int256 _price, uint8 _decimals) {\n price = _price;\n numDecimals = _decimals;\n }\n\n function decimals() external view override returns (uint8) {\n return numDecimals;\n }\n\n function description() external pure override returns (string memory) {\n return \"MockOracleEthFeed\";\n }\n\n function version() external pure override returns (uint256) {\n return 1;\n }\n\n function setPrice(int256 _price) public {\n price = _price;\n }\n\n function setDecimals(uint8 _decimals) public {\n numDecimals = _decimals;\n }\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(uint80 _roundId)\n external\n view\n override\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n )\n {\n roundId = _roundId;\n answer = price;\n startedAt = 0;\n updatedAt = 0;\n answeredInRound = 0;\n }\n\n function latestRoundData()\n external\n view\n override\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n )\n {\n roundId = 0;\n answer = price;\n startedAt = 0;\n updatedAt = block.timestamp;\n answeredInRound = 0;\n }\n}\n" + }, + "contracts/mocks/MockCOMP.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockCOMP is MintableERC20 {\n constructor() ERC20(\"COMP\", \"COMP\") {}\n}\n" + }, + "contracts/mocks/MockComptroller.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract MockComptroller {\n // Claim all the COMP accrued by specific holders in specific markets for their supplies and/or borrows\n function claimComp(\n address[] memory holders,\n address[] memory cTokens,\n bool borrowers,\n bool suppliers\n ) external {}\n}\n" + }, + "contracts/mocks/MockCToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { ICERC20 } from \"../strategies/ICompound.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockCToken is ICERC20, ERC20 {\n using StableMath for uint256;\n\n IERC20 public underlyingToken;\n // underlying = cToken * exchangeRate\n // cToken = underlying / exchangeRate\n uint256 exchangeRate;\n address public override comptroller;\n\n constructor(ERC20 _underlyingToken, address _comptroller)\n ERC20(\"cMock\", \"cMK\")\n {\n uint8 underlyingDecimals = _underlyingToken.decimals();\n // if has 18 dp, exchange rate should be 1e26\n // if has 8 dp, exchange rate should be 1e18\n if (underlyingDecimals > 8) {\n exchangeRate = 10**uint256(18 + underlyingDecimals - 10);\n } else if (underlyingDecimals < 8) {\n // e.g. 18-8+6 = 16\n exchangeRate = 10**uint256(18 - 8 + underlyingDecimals);\n } else {\n exchangeRate = 1e18;\n }\n underlyingToken = _underlyingToken;\n comptroller = _comptroller;\n }\n\n function decimals() public pure override returns (uint8) {\n return 8;\n }\n\n function mint(uint256 mintAmount) public override returns (uint256) {\n // Credit them with cToken\n _mint(msg.sender, mintAmount.divPrecisely(exchangeRate));\n // Take their reserve\n underlyingToken.transferFrom(msg.sender, address(this), mintAmount);\n return 0;\n }\n\n function redeem(uint256 redeemAmount) external override returns (uint256) {\n uint256 tokenAmount = redeemAmount.mulTruncate(exchangeRate);\n // Burn the cToken\n _burn(msg.sender, redeemAmount);\n // Transfer underlying to caller\n underlyingToken.transfer(msg.sender, tokenAmount);\n return 0;\n }\n\n function redeemUnderlying(uint256 redeemAmount)\n external\n override\n returns (uint256)\n {\n uint256 cTokens = redeemAmount.divPrecisely(exchangeRate);\n // Burn the cToken\n _burn(msg.sender, cTokens);\n // Transfer underlying to caller\n underlyingToken.transfer(msg.sender, redeemAmount);\n return 0;\n }\n\n function balanceOfUnderlying(address owner)\n external\n view\n override\n returns (uint256)\n {\n uint256 cTokenBal = this.balanceOf(owner);\n return cTokenBal.mulTruncate(exchangeRate);\n }\n\n function balanceOf(address owner)\n public\n view\n override(ICERC20, ERC20)\n returns (uint256)\n {\n return ERC20.balanceOf(owner);\n }\n\n function updateExchangeRate()\n internal\n view\n returns (uint256 newExchangeRate)\n {\n uint256 factor = 100002 * (10**13); // 0.002%\n newExchangeRate = exchangeRate.mulTruncate(factor);\n }\n\n function exchangeRateStored() external view override returns (uint256) {\n return exchangeRate;\n }\n\n function supplyRatePerBlock() external pure override returns (uint256) {\n return 141 * (10**8);\n }\n}\n" + }, + "contracts/mocks/MockCVXLocker.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MockCVXLocker {\n address public immutable cvx;\n mapping(address => uint256) public lockedBalanceOf;\n\n constructor(address _cvx) {\n cvx = _cvx;\n }\n\n function lock(\n address _account,\n uint256 _amount,\n uint256\n ) external {\n lockedBalanceOf[_account] += _amount;\n ERC20(cvx).transferFrom(msg.sender, address(this), _amount);\n }\n\n function unlockAllTokens(address _account) external {\n lockedBalanceOf[_account] = 0;\n ERC20(cvx).transfer(_account, lockedBalanceOf[_account]);\n }\n}\n" + }, + "contracts/mocks/MockDAI.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockDAI is MintableERC20 {\n constructor() ERC20(\"DAI\", \"DAI\") {}\n}\n" + }, + "contracts/mocks/MockDepositContract.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IDepositContract } from \"./../interfaces/IDepositContract.sol\";\n\ncontract MockDepositContract is IDepositContract {\n uint256 deposit_count;\n\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable override {\n require(pubkey.length == 48, \"DepositContract: invalid pubkey length\");\n require(\n withdrawal_credentials.length == 32,\n \"DepositContract: invalid withdrawal_credentials length\"\n );\n require(\n signature.length == 96,\n \"DepositContract: invalid signature length\"\n );\n\n // Check deposit amount\n require(msg.value >= 1 ether, \"DepositContract: deposit value too low\");\n require(\n msg.value % 1 gwei == 0,\n \"DepositContract: deposit value not multiple of gwei\"\n );\n uint256 deposit_amount = msg.value / 1 gwei;\n require(\n deposit_amount <= type(uint64).max,\n \"DepositContract: deposit value too high\"\n );\n\n // Emit `DepositEvent` log\n bytes memory amount = to_little_endian_64(uint64(deposit_amount));\n emit DepositEvent(\n pubkey,\n withdrawal_credentials,\n amount,\n signature,\n to_little_endian_64(uint64(deposit_count))\n );\n require(\n deposit_data_root != 0,\n \"DepositContract: invalid deposit_data_root\"\n );\n }\n\n function get_deposit_root() external view override returns (bytes32) {\n // just return some bytes32\n return sha256(abi.encodePacked(deposit_count, bytes16(0)));\n }\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view override returns (bytes memory) {\n return to_little_endian_64(uint64(deposit_count));\n }\n\n function to_little_endian_64(uint64 value)\n internal\n pure\n returns (bytes memory ret)\n {\n ret = new bytes(8);\n bytes8 bytesValue = bytes8(value);\n // Byteswapping during copying to bytes.\n ret[0] = bytesValue[7];\n ret[1] = bytesValue[6];\n ret[2] = bytesValue[5];\n ret[3] = bytesValue[4];\n ret[4] = bytesValue[3];\n ret[5] = bytesValue[2];\n ret[6] = bytesValue[1];\n ret[7] = bytesValue[0];\n }\n}\n" + }, + "contracts/mocks/MockEvilDAI.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract MockEvilDAI is MintableERC20 {\n address host;\n address realCoin;\n\n constructor(address _host, address _realCoin) ERC20(\"DAI\", \"DAI\") {\n host = _host;\n realCoin = _realCoin;\n }\n\n function transferFrom(\n // solhint-disable-next-line no-unused-vars\n address _from,\n // solhint-disable-next-line no-unused-vars\n address _to,\n uint256 _amount\n ) public override returns (bool) {\n // call mint again!\n if (_amount != 69) {\n IVault(host).mint(address(this), 69, 0);\n }\n return true;\n }\n}\n" + }, + "contracts/mocks/MockEvilReentrantContract.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IRateProvider } from \"../interfaces/balancer/IRateProvider.sol\";\n\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { IERC20 } from \"../utils/InitializableAbstractStrategy.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockEvilReentrantContract {\n using StableMath for uint256;\n\n IBalancerVault public immutable balancerVault;\n IERC20 public immutable reth;\n IERC20 public immutable weth;\n IVault public immutable oethVault;\n address public immutable poolAddress;\n bytes32 public immutable balancerPoolId;\n\n constructor(\n address _balancerVault,\n address _oethVault,\n address _reth,\n address _weth,\n address _poolAddress,\n bytes32 _poolId\n ) {\n balancerVault = IBalancerVault(_balancerVault);\n oethVault = IVault(_oethVault);\n reth = IERC20(_reth);\n weth = IERC20(_weth);\n poolAddress = _poolAddress;\n balancerPoolId = _poolId;\n }\n\n function doEvilStuff() public {\n address priceProvider = oethVault.priceProvider();\n uint256 rethPrice = IOracle(priceProvider).price(address(reth));\n\n // 1. Join pool\n uint256[] memory amounts = new uint256[](2);\n amounts[0] = uint256(10 ether);\n amounts[1] = rethPrice * 10;\n\n address[] memory assets = new address[](2);\n assets[0] = address(reth);\n assets[1] = address(weth);\n\n uint256 minBPT = getBPTExpected(assets, amounts).mulTruncate(\n 0.99 ether\n );\n\n bytes memory joinUserData = abi.encode(\n IBalancerVault.WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,\n amounts,\n minBPT\n );\n\n IBalancerVault.JoinPoolRequest memory joinRequest = IBalancerVault\n .JoinPoolRequest(assets, amounts, joinUserData, false);\n\n balancerVault.joinPool(\n balancerPoolId,\n address(this),\n address(this),\n joinRequest\n );\n\n uint256 bptTokenBalance = IERC20(poolAddress).balanceOf(address(this));\n\n // 2. Redeem as ETH\n bytes memory exitUserData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT,\n bptTokenBalance,\n 1\n );\n\n assets[1] = address(0); // Receive ETH instead of WETH\n uint256[] memory exitAmounts = new uint256[](2);\n exitAmounts[1] = 15 ether;\n IBalancerVault.ExitPoolRequest memory exitRequest = IBalancerVault\n .ExitPoolRequest(assets, exitAmounts, exitUserData, false);\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n payable(address(this)),\n exitRequest\n );\n bptTokenBalance = IERC20(poolAddress).balanceOf(address(this));\n }\n\n function getBPTExpected(address[] memory _assets, uint256[] memory _amounts)\n internal\n view\n virtual\n returns (uint256 bptExpected)\n {\n // Get the oracle from the OETH Vault\n address priceProvider = oethVault.priceProvider();\n\n for (uint256 i = 0; i < _assets.length; ++i) {\n uint256 strategyAssetMarketPrice = IOracle(priceProvider).price(\n _assets[i]\n );\n // convert asset amount to ETH amount\n bptExpected =\n bptExpected +\n _amounts[i].mulTruncate(strategyAssetMarketPrice);\n }\n\n uint256 bptRate = IRateProvider(poolAddress).getRate();\n // Convert ETH amount to BPT amount\n bptExpected = bptExpected.divPrecisely(bptRate);\n }\n\n function approveAllTokens() public {\n // Approve all tokens\n weth.approve(address(oethVault), type(uint256).max);\n reth.approve(poolAddress, type(uint256).max);\n weth.approve(poolAddress, type(uint256).max);\n reth.approve(address(balancerVault), type(uint256).max);\n weth.approve(address(balancerVault), type(uint256).max);\n }\n\n receive() external payable {\n // 3. Try to mint OETH\n oethVault.mint(address(weth), 1 ether, 0.9 ether);\n }\n}\n" + }, + "contracts/mocks/MockfrxETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockfrxETH is MintableERC20 {\n constructor() ERC20(\"frxETH\", \"frxETH\") {}\n}\n" + }, + "contracts/mocks/MockFrxETHMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockFrxETHMinter {\n address public immutable frxETH;\n address public immutable sfrxETH;\n\n constructor(address _frxETH, address _sfrxETH) {\n frxETH = _frxETH;\n sfrxETH = _sfrxETH;\n }\n\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares)\n {\n IMintableERC20(frxETH).mintTo(sfrxETH, msg.value);\n IMintableERC20(sfrxETH).mintTo(recipient, msg.value);\n shares = msg.value;\n }\n}\n" + }, + "contracts/mocks/MockLimitedWrappedOusd.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WrappedOusd } from \"../token/WrappedOusd.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MockLimitedWrappedOusd is WrappedOusd {\n constructor(ERC20 underlying_) WrappedOusd(underlying_) {}\n\n function maxDeposit(address)\n public\n view\n virtual\n override\n returns (uint256)\n {\n return 1e18;\n }\n}\n" + }, + "contracts/mocks/MockMaverickDistributor.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Pool } from \"../interfaces/plume/IMaverickV2Pool.sol\";\n\ncontract MockMaverickDistributor {\n IERC20 public immutable rewardToken;\n\n uint256 public lastEpoch;\n uint256 public rewardAmount;\n\n constructor(address _rewardToken) {\n rewardToken = IERC20(_rewardToken);\n }\n\n function setLastEpoch(uint256 _epoch) external {\n lastEpoch = _epoch;\n }\n\n function setRewardTokenAmount(uint256 _amount) external {\n rewardAmount = _amount;\n }\n\n function claimLp(\n address recipient,\n uint256,\n IMaverickV2Pool,\n uint32[] memory,\n uint256\n ) external returns (uint256 amount) {\n rewardToken.transfer(recipient, rewardAmount);\n return rewardAmount;\n }\n}\n" + }, + "contracts/mocks/MockMetadataToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// IERC20Metadata is used in the resolveAsset function in contracts/utils/assets.js\n// We just need to import it here to make its ABI available to Hardhat\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n" + }, + "contracts/mocks/MockMintableUniswapPair.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport \"./MockUniswapPair.sol\";\n\ncontract MockMintableUniswapPair is MockUniswapPair, MintableERC20 {\n constructor(\n address _token0,\n address _token1,\n uint112 _reserve0,\n uint112 _reserve1\n )\n MockUniswapPair(_token0, _token1, _reserve0, _reserve1)\n ERC20(\"Uniswap V2\", \"UNI-v2\")\n {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/MockNonRebasing.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { OUSD } from \"../token/OUSD.sol\";\n\ncontract MockNonRebasing {\n OUSD oUSD;\n\n function setOUSD(address _oUSDAddress) public {\n oUSD = OUSD(_oUSDAddress);\n }\n\n function rebaseOptIn() public {\n oUSD.rebaseOptIn();\n }\n\n function rebaseOptOut() public {\n oUSD.rebaseOptOut();\n }\n\n function transfer(address _to, uint256 _value) public {\n oUSD.transfer(_to, _value);\n }\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) public {\n oUSD.transferFrom(_from, _to, _value);\n }\n\n function approve(address _spender, uint256 _addedValue) public {\n oUSD.approve(_spender, _addedValue);\n }\n\n function mintOusd(\n address _vaultContract,\n address _asset,\n uint256 _amount\n ) public {\n IVault(_vaultContract).mint(_asset, _amount, 0);\n }\n\n function redeemOusd(address _vaultContract, uint256 _amount) public {\n IVault(_vaultContract).redeem(_amount, 0);\n }\n\n function approveFor(\n address _contract,\n address _spender,\n uint256 _addedValue\n ) public {\n IERC20(_contract).approve(_spender, _addedValue);\n }\n}\n" + }, + "contracts/mocks/MockNonStandardToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\nimport \"./MintableERC20.sol\";\n\n/**\n * Mock token contract to simulate tokens that don't\n * throw/revert when a transfer/transferFrom call fails\n */\ncontract MockNonStandardToken is MintableERC20 {\n using SafeMath for uint256;\n\n constructor() ERC20(\"NonStandardToken\", \"NonStandardToken\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n\n function transfer(address recipient, uint256 amount)\n public\n override\n returns (bool)\n {\n if (balanceOf(msg.sender) < amount) {\n // Fail silently\n return false;\n }\n\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public override returns (bool) {\n if (balanceOf(sender) < amount) {\n // Fail silently\n return false;\n }\n\n _transfer(sender, recipient, amount);\n _approve(\n sender,\n _msgSender(),\n allowance(sender, _msgSender()).sub(\n amount,\n \"ERC20: transfer amount exceeds allowance\"\n )\n );\n return true;\n }\n}\n" + }, + "contracts/mocks/MockOETHVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultCore } from \"../vault/OETHVaultCore.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MockOETHVault is OETHVaultCore {\n using StableMath for uint256;\n\n constructor(address _weth) OETHVaultCore(_weth) {\n _setGovernor(msg.sender);\n }\n\n function supportAsset(address asset) external {\n assets[asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(0),\n decimals: 18,\n allowedOracleSlippageBps: 0\n });\n\n allAssets.push(asset);\n }\n}\n" + }, + "contracts/mocks/MockOETHVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"../vault/OETHVaultAdmin.sol\";\n\ncontract MockOETHVaultAdmin is OETHVaultAdmin {\n constructor(address _weth) OETHVaultAdmin(_weth) {}\n\n // fetches the WETH amount in outstanding withdrawals\n function outstandingWithdrawalsAmount()\n external\n view\n returns (uint256 wethAmount)\n {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n wethAmount = queue.queued - queue.claimed;\n }\n\n function wethAvailable() external view returns (uint256) {\n return _wethAvailable();\n }\n}\n" + }, + "contracts/mocks/MockOGN.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./BurnableERC20.sol\";\nimport \"./MintableERC20.sol\";\n\n/**\n * @title Origin token (OGN).\n *\n * @dev Token that allows minting and burning.\n * @dev Important note:\n * @dev There is a known race condition in the ERC20 standard on the approve() method.\n * @dev See details: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n * @dev The Origin token contract implements the increaseApproval() and decreaseApproval() methods.\n * @dev It is strongly recommended to use those methods rather than approve()\n * @dev when updating the token allowance.\n */\ncontract MockOGN is MintableERC20, BurnableERC20 {\n event SetWhitelistExpiration(uint256 expiration);\n event AllowedTransactorAdded(address sender);\n event AllowedTransactorRemoved(address sender);\n event AddCallSpenderWhitelist(address enabler, address spender);\n event RemoveCallSpenderWhitelist(address disabler, address spender);\n\n mapping(address => bool) public callSpenderWhitelist;\n address public owner = msg.sender;\n // UNIX timestamp (in seconds) after which this whitelist no longer applies\n uint256 public whitelistExpiration;\n // While the whitelist is active, either the sender or recipient must be\n // in allowedTransactors.\n mapping(address => bool) public allowedTransactors;\n\n // @dev Constructor that gives msg.sender all initial tokens.\n constructor(uint256 _initialSupply) ERC20(\"OriginToken\", \"OGN\") {\n owner = msg.sender;\n _mint(owner, _initialSupply);\n }\n\n //\n // approveAndCall methods\n //\n\n // @dev Add spender to whitelist of spenders for approveAndCall\n // @param _spender Address to add\n function addCallSpenderWhitelist(address _spender) public onlyOwner {\n callSpenderWhitelist[_spender] = true;\n emit AddCallSpenderWhitelist(msg.sender, _spender);\n }\n\n // @dev Remove spender from whitelist of spenders for approveAndCall\n // @param _spender Address to remove\n function removeCallSpenderWhitelist(address _spender) public onlyOwner {\n delete callSpenderWhitelist[_spender];\n emit RemoveCallSpenderWhitelist(msg.sender, _spender);\n }\n\n // @dev Approve transfer of tokens and make a contract call in a single\n // @dev transaction. This allows a DApp to avoid requiring two MetaMask\n // @dev approvals for a single logical action, such as creating a listing,\n // @dev which requires the seller to approve a token transfer and the\n // @dev marketplace contract to transfer tokens from the seller.\n //\n // @dev This is based on the ERC827 function approveAndCall and avoids\n // @dev security issues by only working with a whitelisted set of _spender\n // @dev addresses. The other difference is that the combination of this\n // @dev function ensures that the proxied function call receives the\n // @dev msg.sender for this function as its first parameter.\n //\n // @param _spender The address that will spend the funds.\n // @param _value The amount of tokens to be spent.\n // @param _selector Function selector for function to be called.\n // @param _callParams Packed, encoded parameters, omitting the first parameter which is always msg.sender\n function approveAndCallWithSender(\n address _spender,\n uint256 _value,\n bytes4 _selector,\n bytes memory _callParams\n ) public payable returns (bool) {\n require(_spender != address(this), \"token contract can't be approved\");\n require(callSpenderWhitelist[_spender], \"spender not in whitelist\");\n\n require(super.approve(_spender, _value), \"approve failed\");\n\n bytes memory callData = abi.encodePacked(\n _selector,\n uint256(uint160(msg.sender)),\n _callParams\n );\n // solium-disable-next-line security/no-call-value\n (bool success, ) = _spender.call{ value: msg.value }(callData);\n require(success, \"proxied call failed\");\n return true;\n }\n\n //\n // Functions for maintaining whitelist\n //\n\n modifier onlyOwner() {\n require(msg.sender == owner);\n _;\n }\n modifier allowedTransfer(address _from, address _to) {\n require(\n // solium-disable-next-line operator-whitespace\n !whitelistActive() ||\n allowedTransactors[_from] ||\n allowedTransactors[_to],\n \"neither sender nor recipient are allowed\"\n );\n _;\n }\n\n function whitelistActive() public view returns (bool) {\n return block.timestamp < whitelistExpiration;\n }\n\n function addAllowedTransactor(address _transactor) public onlyOwner {\n emit AllowedTransactorAdded(_transactor);\n allowedTransactors[_transactor] = true;\n }\n\n function removeAllowedTransactor(address _transactor) public onlyOwner {\n emit AllowedTransactorRemoved(_transactor);\n delete allowedTransactors[_transactor];\n }\n\n /**\n * @dev Set the whitelist expiration, after which the whitelist no longer\n * applies.\n */\n function setWhitelistExpiration(uint256 _expiration) public onlyOwner {\n // allow only if whitelist expiration hasn't yet been set, or if the\n // whitelist expiration hasn't passed yet\n require(\n whitelistExpiration == 0 || whitelistActive(),\n \"an expired whitelist cannot be extended\"\n );\n // prevent possible mistakes in calling this function\n require(\n _expiration >= block.timestamp + 1 days,\n \"whitelist expiration not far enough into the future\"\n );\n emit SetWhitelistExpiration(_expiration);\n whitelistExpiration = _expiration;\n }\n\n //\n // ERC20 transfer functions that have been overridden to enforce the\n // whitelist.\n //\n\n function transfer(address _to, uint256 _value)\n public\n override\n allowedTransfer(msg.sender, _to)\n returns (bool)\n {\n return super.transfer(_to, _value);\n }\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) public override allowedTransfer(_from, _to) returns (bool) {\n return super.transferFrom(_from, _to, _value);\n }\n}\n" + }, + "contracts/mocks/MockOGV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockOGV is MintableERC20 {\n constructor() ERC20(\"OGV\", \"OGV\") {}\n}\n" + }, + "contracts/mocks/MockOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/IPriceOracle.sol\";\nimport \"../interfaces/IMinMaxOracle.sol\";\n\n/**\n * Mock of both price Oracle and min max oracles\n */\ncontract MockOracle is IPriceOracle, IMinMaxOracle {\n mapping(bytes32 => uint256) prices;\n mapping(bytes32 => uint256[]) pricesMinMax;\n uint256 ethMin;\n uint256 ethMax;\n\n /**\n * @dev returns the asset price in USD, 6 decimal digits.\n * Compatible with the Open Price Feed.\n */\n function price(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n return prices[keccak256(abi.encodePacked(symbol))];\n }\n\n /**\n * @dev sets the price of the asset in USD, 6 decimal digits\n *\n */\n function setPrice(string calldata symbol, uint256 _price) external {\n prices[keccak256(abi.encodePacked(symbol))] = _price;\n }\n\n /**\n * @dev sets the min and max price of ETH in USD, 6 decimal digits\n *\n */\n function setEthPriceMinMax(uint256 _min, uint256 _max) external {\n ethMin = _min;\n ethMax = _max;\n }\n\n /**\n * @dev sets the prices Min Max for a specific symbol in ETH, 8 decimal digits\n *\n */\n function setTokPriceMinMax(\n string calldata symbol,\n uint256 _min,\n uint256 _max\n ) external {\n pricesMinMax[keccak256(abi.encodePacked(symbol))] = [_min, _max];\n }\n\n /**\n * @dev get the price of asset in ETH, 8 decimal digits.\n */\n function priceMin(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n uint256[] storage pMinMax = pricesMinMax[\n keccak256(abi.encodePacked(symbol))\n ];\n return (pMinMax[0] * ethMin) / 1e6;\n }\n\n /**\n * @dev get the price of asset in USD, 8 decimal digits.\n * Not needed for now\n */\n function priceMax(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n uint256[] storage pMinMax = pricesMinMax[\n keccak256(abi.encodePacked(symbol))\n ];\n return (pMinMax[1] * ethMax) / 1e6;\n }\n}\n" + }, + "contracts/mocks/MockOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"../oracle/AbstractOracleRouter.sol\";\n\n// @notice Oracle Router required for testing environment\ncontract MockOracleRouter is AbstractOracleRouter {\n struct FeedMetadata {\n address feedAddress;\n uint256 maxStaleness;\n }\n\n mapping(address => FeedMetadata) public assetToFeedMetadata;\n\n /* @dev Override feed and maxStaleness information for a particular asset\n * @param _asset the asset to override feed for\n * @param _feed new feed\n * @param _maxStaleness new maximum time allowed for feed data to be stale\n */\n function setFeed(\n address _asset,\n address _feed,\n uint256 _maxStaleness\n ) external {\n assetToFeedMetadata[_asset] = FeedMetadata(_feed, _maxStaleness);\n }\n\n /*\n * The dev version of the Oracle doesn't need to gas optimize and cache the decimals\n */\n function getDecimals(address _feed) internal view override returns (uint8) {\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n return AggregatorV3Interface(_feed).decimals();\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n FeedMetadata storage fm = assetToFeedMetadata[asset];\n feedAddress = fm.feedAddress;\n maxStaleness = fm.maxStaleness;\n }\n}\n" + }, + "contracts/mocks/MockOracleRouterNoStale.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { OracleRouter } from \"../oracle/OracleRouter.sol\";\nimport { OETHOracleRouter } from \"../oracle/OETHOracleRouter.sol\";\n\n// @notice Oracle Router used to bypass staleness\ncontract MockOracleRouterNoStale is OracleRouter {\n function feedMetadata(address asset)\n internal\n pure\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n (feedAddress, ) = super.feedMetadata(asset);\n maxStaleness = 365 days;\n }\n}\n\n// @notice Oracle Router used to bypass staleness\ncontract MockOETHOracleRouterNoStale is OETHOracleRouter {\n constructor() OETHOracleRouter() {}\n\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n (feedAddress, ) = super.feedMetadata(asset);\n maxStaleness = 365 days;\n }\n}\n" + }, + "contracts/mocks/MockOracleWeightedPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Variable, OracleAverageQuery, IOracleWeightedPool } from \"../interfaces/balancer/IOracleWeightedPool.sol\";\n\ncontract MockOracleWeightedPool is IOracleWeightedPool {\n uint256[] public nextResults;\n\n constructor() {\n nextResults = [1 ether, 1 ether];\n }\n\n function getTimeWeightedAverage(OracleAverageQuery[] memory)\n external\n view\n override\n returns (uint256[] memory results)\n {\n return nextResults;\n }\n\n function setNextResults(uint256[] calldata results) external {\n nextResults = results;\n }\n}\n" + }, + "contracts/mocks/MockPartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PartialWithdrawal } from \"../beacon/PartialWithdrawal.sol\";\n\ncontract MockPartialWithdrawal {\n function fee() external view returns (uint256) {\n return PartialWithdrawal.fee();\n }\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n external\n returns (uint256 fee_)\n {\n return PartialWithdrawal.request(validatorPubKey, amount);\n }\n}\n" + }, + "contracts/mocks/MockRebornMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n// solhint-disable-next-line no-console\nimport \"hardhat/console.sol\";\n\ncontract Sanctum {\n address public asset;\n address public vault;\n address public reborner;\n bool public shouldAttack = false;\n // should selfdestruct in the constructor\n bool public shouldDestruct = false;\n uint256 public targetMethod;\n address public ousdContract;\n\n constructor(address _asset, address _vault) {\n asset = _asset;\n vault = _vault;\n }\n\n function deploy(uint256 salt, bytes memory bytecode)\n public\n returns (address addr)\n {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt)\n }\n require(addr != address(0), \"Create2: Failed on deploy\");\n }\n\n function computeAddress(uint256 salt, bytes memory bytecode)\n public\n view\n returns (address)\n {\n bytes32 bytecodeHashHash = keccak256(bytecode);\n bytes32 _data = keccak256(\n abi.encodePacked(\n bytes1(0xff),\n address(this),\n salt,\n bytecodeHashHash\n )\n );\n return address(bytes20(_data << 96));\n }\n\n function setShouldAttack(bool _shouldAttack) public {\n shouldAttack = _shouldAttack;\n }\n\n // should call selfdestruct in the constructor\n function setShouldDesctruct(bool _shouldDestruct) public {\n shouldDestruct = _shouldDestruct;\n }\n\n function setTargetMethod(uint256 target) public {\n targetMethod = target;\n }\n\n function setOUSDAddress(address _ousdContract) public {\n ousdContract = _ousdContract;\n }\n}\n\ncontract Reborner {\n Sanctum sanctum;\n bool logging = false;\n\n constructor(address _sanctum) {\n log(\"We are created...\");\n sanctum = Sanctum(_sanctum);\n if (sanctum.shouldAttack()) {\n log(\"We are attacking now...\");\n\n uint256 target = sanctum.targetMethod();\n\n if (target == 1) {\n redeem();\n } else if (target == 2) {\n transfer();\n } else {\n mint();\n }\n }\n\n if (sanctum.shouldDestruct()) {\n bye();\n }\n }\n\n function mint() public {\n log(\"We are attempting to mint..\");\n address asset = sanctum.asset();\n address vault = sanctum.vault();\n IERC20(asset).approve(vault, 1e18);\n IVault(vault).mint(asset, 1e18, 0);\n log(\"We are now minting..\");\n }\n\n function redeem() public {\n log(\"We are attempting to redeem..\");\n address vault = sanctum.vault();\n IVault(vault).redeem(1e18, 1e18);\n log(\"We are now redeeming..\");\n }\n\n function transfer() public {\n log(\"We are attempting to transfer..\");\n address ousd = sanctum.ousdContract();\n require(IERC20(ousd).transfer(address(1), 1e18), \"transfer failed\");\n log(\"We are now transfering..\");\n }\n\n function bye() public {\n log(\"We are now destructing..\");\n selfdestruct(payable(msg.sender));\n }\n\n function log(string memory message) internal view {\n if (logging) {\n // solhint-disable-next-line no-console\n console.log(message);\n }\n }\n}\n" + }, + "contracts/mocks/MockRETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport \"../interfaces/IGetExchangeRateToken.sol\";\n\ncontract MockRETH is MintableERC20, IGetExchangeRateToken {\n uint256 private exchangeRate = 12e17;\n\n constructor() ERC20(\"Rocket Pool ETH\", \"rETH\") {}\n\n function getExchangeRate() external view override returns (uint256) {\n return exchangeRate;\n }\n\n function setExchangeRate(uint256 _rate) external {\n exchangeRate = _rate;\n }\n}\n" + }, + "contracts/mocks/MockRoosterAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Rooster AMO strategy exposing extra functionality\n * @author Origin Protocol Inc\n */\n\nimport { RoosterAMOStrategy } from \"../strategies/plume/RoosterAMOStrategy.sol\";\nimport { IMaverickV2Pool } from \"../interfaces/plume/IMaverickV2Pool.sol\";\n\ncontract MockRoosterAMOStrategy is RoosterAMOStrategy {\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethpAddress,\n address _liquidityManager,\n address _poolLens,\n address _maverickPosition,\n address _maverickQuoter,\n address _mPool,\n bool _upperTickAtParity,\n address _votingDistributor,\n address _poolDistributor\n )\n RoosterAMOStrategy(\n _stratConfig,\n _wethAddress,\n _oethpAddress,\n _liquidityManager,\n _poolLens,\n _maverickPosition,\n _maverickQuoter,\n _mPool,\n _upperTickAtParity,\n _votingDistributor,\n _poolDistributor\n )\n {}\n\n function getCurrentWethShare() external view returns (uint256) {\n uint256 _currentPrice = getPoolSqrtPrice();\n\n return _getWethShare(_currentPrice);\n }\n}\n" + }, + "contracts/mocks/MockSFC.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockSFC {\n error ZeroAmount();\n error TransferFailed();\n error StakeIsFullySlashed();\n\n // Mapping of delegator address to validator ID to amount delegated\n mapping(address => mapping(uint256 => uint256)) public delegations;\n // Mapping of delegator address to validator ID to withdrawal request ID to amount\n mapping(address => mapping(uint256 => mapping(uint256 => uint256)))\n public withdraws;\n // validator ID -> slashing refund ratio (allows to withdraw slashed stake)\n mapping(uint256 => uint256) public slashingRefundRatio;\n\n function getStake(address delegator, uint256 validatorID)\n external\n view\n returns (uint256)\n {\n return delegations[delegator][validatorID];\n }\n\n function delegate(uint256 validatorID) external payable {\n if (msg.value == 0) {\n revert ZeroAmount();\n }\n delegations[msg.sender][validatorID] += msg.value;\n }\n\n function undelegate(\n uint256 validatorID,\n uint256 wrID,\n uint256 amount\n ) external {\n require(\n delegations[msg.sender][validatorID] >= amount,\n \"insufficient stake\"\n );\n require(\n withdraws[msg.sender][validatorID][wrID] == 0,\n \"withdrawal request already exists\"\n );\n\n delegations[msg.sender][validatorID] -= amount;\n withdraws[msg.sender][validatorID][wrID] = amount;\n }\n\n function withdraw(uint256 validatorID, uint256 wrID) external {\n require(withdraws[msg.sender][validatorID][wrID] > 0, \"no withdrawal\");\n\n uint256 withdrawAmount = withdraws[msg.sender][validatorID][wrID];\n uint256 penalty = (withdrawAmount *\n (1e18 - slashingRefundRatio[validatorID])) / 1e18;\n\n if (penalty >= withdrawAmount) {\n revert StakeIsFullySlashed();\n }\n\n (bool sent, ) = msg.sender.call{ value: withdrawAmount - penalty }(\"\");\n if (!sent) {\n revert TransferFailed();\n }\n }\n\n function pendingRewards(address delegator, uint256 validatorID)\n external\n view\n returns (uint256)\n {}\n\n function claimRewards(uint256 validatorID) external {}\n\n function restakeRewards(uint256 validatorID) external {}\n\n /// @param refundRatio the percentage of the staked amount that can be refunded. 0.1e18 = 10%, 1e18 = 100%\n function slashValidator(uint256 validatorID, uint256 refundRatio) external {\n require(refundRatio <= 1e18, \"invalid refund ratio\");\n slashingRefundRatio[validatorID] = refundRatio;\n }\n}\n" + }, + "contracts/mocks/MocksfrxETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MocksfrxETH is MintableERC20 {\n address public frxETH;\n\n constructor(address _frxETH) ERC20(\"sfrxETH\", \"sfrxETH\") {\n frxETH = _frxETH;\n }\n\n function setMockfrxETHAddress(address _frxETH) external {\n frxETH = _frxETH;\n }\n\n function deposit(uint256 assets, address receiver)\n external\n returns (uint256 shares)\n {\n ERC20(frxETH).transferFrom(msg.sender, address(this), assets);\n\n _mint(receiver, assets);\n\n return assets;\n }\n\n function maxWithdraw(address owner) external view returns (uint256) {\n return balanceOf(owner);\n }\n\n function setMaxWithdrawableBalance(address owner, uint256 balance)\n external\n {\n uint256 currentBalance = balanceOf(owner);\n if (currentBalance > balance) {\n _burn(owner, currentBalance - balance);\n } else if (balance > currentBalance) {\n _mint(owner, balance - currentBalance);\n }\n }\n\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets) {\n _burn(owner, shares);\n\n ERC20(frxETH).transfer(receiver, shares);\n\n assets = shares;\n }\n\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares) {\n _burn(owner, assets);\n\n ERC20(frxETH).transfer(receiver, assets);\n\n shares = assets;\n }\n\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares)\n {\n _mint(recipient, msg.value);\n shares = msg.value;\n }\n}\n" + }, + "contracts/mocks/MockSSV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockSSV is MintableERC20 {\n constructor() ERC20(\"SSV Token\", \"SSV\") {}\n}\n" + }, + "contracts/mocks/MockSSVNetwork.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Cluster } from \"./../interfaces/ISSVNetwork.sol\";\n\ncontract MockSSVNetwork {\n function registerValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function exitValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds\n ) external {}\n\n function removeValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external {}\n\n function deposit(\n address clusterOwner,\n uint64[] calldata operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function setFeeRecipientAddress(address recipient) external {}\n}\n" + }, + "contracts/mocks/MockstETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockstETH is MintableERC20 {\n constructor() ERC20(\"stETH\", \"stETH\") {}\n}\n" + }, + "contracts/mocks/MockStkAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"./MintableERC20.sol\";\n\ncontract MockStkAave is MintableERC20 {\n uint256 public COOLDOWN_SECONDS = 864000;\n uint256 public UNSTAKE_WINDOW = 172800;\n address public STAKED_TOKEN;\n\n mapping(address => uint256) public stakerRewardsToClaim;\n mapping(address => uint256) public stakersCooldowns;\n\n using SafeERC20 for IERC20;\n\n constructor(address _stakedToken) ERC20(\"Staked Aave\", \"stkAAVE\") {\n STAKED_TOKEN = _stakedToken;\n }\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n\n function setStakedToken(address _stakedToken) external {\n STAKED_TOKEN = _stakedToken;\n }\n\n /**\n * @dev Redeems staked tokens, and stop earning rewards\n * @param to Address to redeem to\n * @param amount Amount to redeem\n **/\n function redeem(address to, uint256 amount) external {\n uint256 cooldownStartTimestamp = stakersCooldowns[msg.sender];\n uint256 windowStart = cooldownStartTimestamp + COOLDOWN_SECONDS;\n require(amount != 0, \"INVALID_ZERO_AMOUNT\");\n require(block.timestamp > windowStart, \"INSUFFICIENT_COOLDOWN\");\n require(\n block.timestamp - windowStart <= UNSTAKE_WINDOW,\n \"UNSTAKE_WINDOW_FINISHED\"\n );\n uint256 balanceOfMessageSender = balanceOf(msg.sender);\n uint256 amountToRedeem = (amount > balanceOfMessageSender)\n ? balanceOfMessageSender\n : amount;\n\n stakersCooldowns[msg.sender] = 0;\n _burn(msg.sender, amountToRedeem);\n IERC20(STAKED_TOKEN).safeTransfer(to, amountToRedeem);\n }\n\n /**\n * @dev Activates the cooldown period to unstake\n * - It can't be called if the user is not staking\n **/\n function cooldown() external {\n require(balanceOf(msg.sender) != 0, \"INVALID_BALANCE_ON_COOLDOWN\");\n stakersCooldowns[msg.sender] = block.timestamp;\n }\n\n /**\n * @dev Test helper function to allow changing the cooldown\n **/\n function setCooldown(address account, uint256 _cooldown) external {\n stakersCooldowns[account] = _cooldown;\n }\n}\n" + }, + "contracts/mocks/MockStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract MockStrategy {\n address[] public assets;\n\n address public withdrawAllAsset;\n address public withdrawAllRecipient;\n\n constructor() {}\n\n function deposit(address asset, uint256 amount) external {}\n\n function depositAll() external {}\n\n function withdraw(\n address recipient,\n address asset,\n uint256 amount\n ) external {\n IERC20(asset).transfer(recipient, amount);\n }\n\n function withdrawAll() external {\n IERC20(withdrawAllAsset).transfer(\n withdrawAllRecipient,\n IERC20(withdrawAllAsset).balanceOf(address(this))\n );\n }\n\n function checkBalance(address asset)\n external\n view\n returns (uint256 balance)\n {\n balance = IERC20(asset).balanceOf(address(this));\n }\n\n function supportsAsset(address) external view returns (bool) {\n return true;\n }\n\n function collectRewardTokens() external {}\n\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return new address[](0);\n }\n\n function setWithdrawAll(address asset, address recipient) external {\n withdrawAllAsset = asset;\n withdrawAllRecipient = recipient;\n }\n}\n" + }, + "contracts/mocks/MockSwapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IMintableERC20 } from \"./MintableERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract MockSwapper {\n uint256 public nextOutAmount;\n\n function swap(\n // solhint-disable-next-line no-unused-vars\n address _fromAsset,\n address _toAsset,\n // solhint-disable-next-line no-unused-vars\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n // solhint-disable-next-line no-unused-vars\n bytes calldata _data\n ) external returns (uint256 toAssetAmount) {\n toAssetAmount = (nextOutAmount > 0) ? nextOutAmount : _minToAssetAmount;\n nextOutAmount = 0;\n IMintableERC20(_toAsset).mint(toAssetAmount);\n IERC20(_toAsset).transfer(msg.sender, toAssetAmount);\n }\n\n function setNextOutAmount(uint256 _nextOutAmount) public {\n nextOutAmount = _nextOutAmount;\n }\n}\n" + }, + "contracts/mocks/MockTUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockTUSD is MintableERC20 {\n constructor() ERC20(\"TrueUSD\", \"TUSD\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/MockUniswapPair.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IUniswapV2Pair } from \"../interfaces/uniswap/IUniswapV2Pair.sol\";\n\ncontract MockUniswapPair is IUniswapV2Pair {\n address tok0;\n address tok1;\n uint112 reserve0;\n uint112 reserve1;\n uint256 blockTimestampLast;\n\n bool public hasSynced = false;\n\n constructor(\n address _token0,\n address _token1,\n uint112 _reserve0,\n uint112 _reserve1\n ) {\n tok0 = _token0;\n tok1 = _token1;\n reserve0 = _reserve0;\n reserve1 = _reserve1;\n blockTimestampLast = block.timestamp;\n }\n\n function token0() external view override returns (address) {\n return tok0;\n }\n\n function token1() external view override returns (address) {\n return tok1;\n }\n\n function getReserves()\n external\n view\n override\n returns (\n uint112,\n uint112,\n uint32\n )\n {\n return (reserve0, reserve1, uint32(blockTimestampLast));\n }\n\n function setReserves(uint112 _reserve0, uint112 _reserve1) public {\n reserve0 = _reserve0;\n reserve1 = _reserve1;\n blockTimestampLast = block.timestamp;\n }\n\n // CAUTION This will not work if you setReserves multiple times over\n // multiple different blocks because then it wouldn't be a continuous\n // reserve factor over that blockTimestamp, this assumes an even reserve\n // ratio all the way through\n function price0CumulativeLast() external view override returns (uint256) {\n return\n uint256(FixedPoint.fraction(reserve1, reserve0)._x) *\n blockTimestampLast;\n }\n\n function price1CumulativeLast() external view override returns (uint256) {\n return\n uint256(FixedPoint.fraction(reserve0, reserve1)._x) *\n blockTimestampLast;\n }\n\n function sync() external override {\n hasSynced = true;\n }\n\n function checkHasSynced() external view {\n require(hasSynced, \"Not synced\");\n }\n}\n\n// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))\nlibrary FixedPoint {\n // range: [0, 2**112 - 1]\n // resolution: 1 / 2**112\n struct uq112x112 {\n uint224 _x;\n }\n\n // returns a uq112x112 which represents the ratio of the numerator to the denominator\n // equivalent to encode(numerator).div(denominator)\n function fraction(uint112 numerator, uint112 denominator)\n internal\n pure\n returns (uq112x112 memory)\n {\n require(denominator > 0, \"FixedPoint: DIV_BY_ZERO\");\n return uq112x112((uint224(numerator) << 112) / denominator);\n }\n}\n" + }, + "contracts/mocks/MockUniswapRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { IUniswapV2Router } from \"../interfaces/uniswap/IUniswapV2Router02.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockUniswapRouter is IUniswapV2Router {\n using StableMath for uint256;\n\n mapping(address => address) public pairMaps;\n uint256 public slippage = 1 ether;\n\n function initialize(\n address[] calldata _0tokens,\n address[] calldata _1tokens\n ) public {\n require(\n _0tokens.length == _1tokens.length,\n \"Mock token pairs should be of the same length\"\n );\n for (uint256 i = 0; i < _0tokens.length; i++) {\n pairMaps[_0tokens[i]] = _1tokens[i];\n }\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n\n function swapExactTokensForTokens(\n uint256 amountIn,\n uint256 amountOutMin,\n address[] calldata path,\n address to,\n // solhint-disable-next-line no-unused-vars\n uint256\n ) external override returns (uint256[] memory amountsOut) {\n address tok0 = path[0];\n address tok1 = path[path.length - 1];\n\n uint256 amountOut = (amountOutMin * slippage) / 1 ether;\n require(amountOut >= amountOutMin, \"Slippage error\");\n\n IERC20(tok0).transferFrom(msg.sender, address(this), amountIn);\n MintableERC20(tok1).mintTo(to, amountOut);\n\n amountsOut = new uint256[](path.length);\n amountsOut[path.length - 1] = amountOut;\n }\n\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut)\n {\n (address tok0, address tok1) = _getFirstAndLastToken(params.path);\n\n amountOut = (params.amountOutMinimum * slippage) / 1 ether;\n\n IERC20(tok0).transferFrom(msg.sender, address(this), params.amountIn);\n MintableERC20(tok1).mintTo(params.recipient, amountOut);\n\n require(\n amountOut >= params.amountOutMinimum,\n \"UniswapMock: amountOut less than amountOutMinimum\"\n );\n return amountOut;\n }\n\n function addLiquidity(\n address tokenA,\n address tokenB,\n uint256 amountADesired,\n uint256 amountBDesired,\n uint256 amountAMin,\n uint256 amountBMin,\n address to,\n uint256 deadline\n )\n external\n override\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 liquidity\n )\n {\n // this is needed to make this contract whole else it'd be just virtual\n }\n\n function WETH() external pure override returns (address) {\n return address(0);\n }\n\n // Universal router mock\n function execute(\n bytes calldata,\n bytes[] calldata inputs,\n uint256\n ) external payable {\n uint256 inLen = inputs.length;\n for (uint256 i = 0; i < inLen; ++i) {\n (\n address recipient,\n ,\n uint256 amountOutMinimum,\n bytes memory path,\n\n ) = abi.decode(inputs[i], (address, uint256, uint256, bytes, bool));\n\n (address token0, address token1) = _getFirstAndLastToken(path);\n\n amountOutMinimum = amountOutMinimum.scaleBy(\n Helpers.getDecimals(token0),\n Helpers.getDecimals(token1)\n );\n\n MintableERC20(token1).mintTo(recipient, amountOutMinimum);\n }\n }\n\n function _getFirstAndLastToken(bytes memory path)\n internal\n view\n returns (address token0, address token1)\n {\n bytes memory tok0Bytes = new bytes(20);\n for (uint256 j = 0; j < 20; ++j) {\n tok0Bytes[j] = path[j];\n }\n token0 = address(bytes20(tok0Bytes));\n\n if (pairMaps[token0] != address(0)) {\n token0 = pairMaps[token0];\n }\n\n bytes memory tok1Bytes = new bytes(20);\n uint256 tok1Offset = path.length - 20;\n for (uint256 j = 0; j < 20; ++j) {\n tok1Bytes[j] = path[j + tok1Offset];\n }\n token1 = address(bytes20(tok1Bytes));\n\n if (pairMaps[token1] != address(0)) {\n token1 = pairMaps[token1];\n }\n }\n}\n" + }, + "contracts/mocks/MockUSDC.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDC is MintableERC20 {\n constructor() ERC20(\"USDC Coin\", \"USDC\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n}\n" + }, + "contracts/mocks/MockUSDS.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDS is MintableERC20 {\n constructor() ERC20(\"USDS\", \"USDS\") {}\n}\n" + }, + "contracts/mocks/MockUSDT.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDT is MintableERC20 {\n constructor() ERC20(\"USDT Coin\", \"USDT\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n}\n" + }, + "contracts/mocks/MockVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultCore } from \"../vault/VaultCore.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { VaultInitializer } from \"../vault/VaultInitializer.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MockVault is VaultCore {\n using StableMath for uint256;\n\n uint256 storedTotalValue;\n\n function setTotalValue(uint256 _value) public {\n storedTotalValue = _value;\n }\n\n function totalValue() external view override returns (uint256) {\n return storedTotalValue;\n }\n\n function _totalValue() internal view override returns (uint256) {\n return storedTotalValue;\n }\n\n function _checkBalance(address _asset)\n internal\n view\n override\n returns (uint256 balance)\n {\n // Avoids rounding errors by returning the total value\n // in a single currency\n if (allAssets[0] == _asset) {\n uint256 decimals = Helpers.getDecimals(_asset);\n return storedTotalValue.scaleBy(decimals, 18);\n } else {\n return 0;\n }\n }\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external onlyGovernor {\n maxSupplyDiff = _maxSupplyDiff;\n }\n}\n" + }, + "contracts/mocks/MockVaultCoreInstantRebase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultCore } from \"../vault/VaultCore.sol\";\n\ncontract MockVaultCoreInstantRebase is VaultCore {\n function _nextYield(uint256 supply, uint256 vaultValue)\n internal\n view\n override\n returns (uint256 yield, uint256 targetRate)\n {\n if (vaultValue <= supply) {\n return (0, 0);\n }\n yield = vaultValue - supply;\n return (yield, 0);\n }\n}\n" + }, + "contracts/mocks/MockWETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\n// Just importing to \"unbreak\" coverage tests\nimport { IERC721Receiver } from \"@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol\";\n\ncontract MockWETH is MintableERC20 {\n constructor() ERC20(\"WETH\", \"WETH\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/MockWS.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockWS is MintableERC20 {\n constructor() ERC20(\"Wrapped Sonic\", \"wS\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/TestUpgradedOUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../token/OUSD.sol\";\n\n// used to alter internal state of OUSD contract\ncontract TestUpgradedOUSD is OUSD {\n constructor() OUSD() {}\n\n function overwriteCreditBalances(address _account, uint256 _creditBalance)\n public\n {\n creditBalances[_account] = _creditBalance;\n }\n\n function overwriteAlternativeCPT(address _account, uint256 _acpt) public {\n alternativeCreditsPerToken[_account] = _acpt;\n }\n\n function overwriteRebaseState(address _account, RebaseOptions _rebaseOption)\n public\n {\n rebaseState[_account] = _rebaseOption;\n }\n}\n" + }, + "contracts/oracle/AbstractOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\n// @notice Abstract functionality that is shared between various Oracle Routers\nabstract contract AbstractOracleRouter is IOracle {\n using StableMath for uint256;\n using SafeCast for int256;\n\n uint256 internal constant MIN_DRIFT = 0.7e18;\n uint256 internal constant MAX_DRIFT = 1.3e18;\n address internal constant FIXED_PRICE =\n 0x0000000000000000000000000000000000000001;\n // Maximum allowed staleness buffer above normal Oracle maximum staleness\n uint256 internal constant STALENESS_BUFFER = 1 days;\n mapping(address => uint8) internal decimalsCache;\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n returns (address feedAddress, uint256 maxStaleness);\n\n /**\n * @notice Returns the total price in 18 digit unit for a given asset.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n if (shouldBePegged(asset)) {\n require(_price <= MAX_DRIFT, \"Oracle: Price exceeds max\");\n require(_price >= MIN_DRIFT, \"Oracle: Price under min\");\n }\n return _price;\n }\n\n function getDecimals(address _feed) internal view virtual returns (uint8) {\n uint8 decimals = decimalsCache[_feed];\n require(decimals > 0, \"Oracle: Decimals not cached\");\n return decimals;\n }\n\n /**\n * @notice Before an asset/feed price is fetches for the first time the\n * decimals need to be cached. This is a gas optimization\n * @param asset address of the asset\n * @return uint8 corresponding asset decimals\n */\n function cacheDecimals(address asset) external returns (uint8) {\n (address _feed, ) = feedMetadata(asset);\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n uint8 decimals = AggregatorV3Interface(_feed).decimals();\n decimalsCache[_feed] = decimals;\n return decimals;\n }\n\n function shouldBePegged(address _asset) internal view returns (bool) {\n string memory symbol = Helpers.getSymbol(_asset);\n bytes32 symbolHash = keccak256(abi.encodePacked(symbol));\n return\n symbolHash == keccak256(abi.encodePacked(\"DAI\")) ||\n symbolHash == keccak256(abi.encodePacked(\"USDC\")) ||\n symbolHash == keccak256(abi.encodePacked(\"USDT\"));\n }\n}\n" + }, + "contracts/oracle/OETHBaseOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\n// @notice Oracle Router (for OETH on Base) that denominates all prices in ETH\ncontract OETHBaseOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n using SafeCast for int256;\n\n address constant WETH = 0x4200000000000000000000000000000000000006;\n address constant WOETH = 0xD8724322f44E5c58D7A815F542036fb17DbbF839;\n address constant WOETH_CHAINLINK_FEED =\n 0xe96EB1EDa83d18cbac224233319FA5071464e1b9;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == WETH) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == WOETH) {\n // Chainlink: https://data.chain.link/feeds/base/base/woeth-oeth-exchange-rate\n // Bridged wOETH/OETH\n feedAddress = WOETH_CHAINLINK_FEED;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OETHFixedOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHOracleRouter } from \"./OETHOracleRouter.sol\";\n\n// @notice Oracle Router that returns 1e18 for all prices\n// used solely for deployment to testnets\ncontract OETHFixedOracle is OETHOracleRouter {\n constructor() OETHOracleRouter() {}\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n // solhint-disable-next-line no-unused-vars\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n // fixes price for all of the assets\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n }\n}\n" + }, + "contracts/oracle/OETHOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// @notice Oracle Router that denominates all prices in ETH\ncontract OETHOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = uint256(_iprice).scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == 0x5E8422345238F34275888049021821E8E08CAa1f) {\n // frxETH/ETH\n feedAddress = 0xC58F3385FBc1C8AD2c0C9a061D7c13b141D7A5Df;\n maxStaleness = 18 hours + STALENESS_BUFFER;\n } else if (asset == 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/steth-eth\n // Chainlink: stETH/ETH\n feedAddress = 0x86392dC19c0b719886221c78AB11eb8Cf5c52812;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xae78736Cd615f374D3085123A210448E74Fc6393) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/reth-eth\n // Chainlink: rETH/ETH\n feedAddress = 0x536218f9E9Eb48863970252233c8F271f554C2d0;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xD533a949740bb3306d119CC777fa900bA034cd52) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/crv-eth\n // Chainlink: CRV/ETH\n feedAddress = 0x8a12Be339B0cD1829b91Adc01977caa5E9ac121e;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/cvx-eth\n // Chainlink: CVX/ETH\n feedAddress = 0xC9CbF687f43176B302F03f5e58470b77D07c61c6;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xBe9895146f7AF43049ca1c1AE358B0541Ea49704) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/cbeth-eth\n // Chainlink: cbETH/ETH\n feedAddress = 0xF017fcB346A1885194689bA23Eff2fE6fA5C483b;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xba100000625a3754423978a60c9317c58a424e3D) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/bal-eth\n // Chainlink: BAL/ETH\n feedAddress = 0xC1438AA3823A6Ba0C159CfA8D98dF5A994bA120b;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OETHPlumeOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\n// @notice Oracle Router (for OETH on Plume) that denominates all prices in ETH\ncontract OETHPlumeOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n using SafeCast for int256;\n\n address constant WETH = 0xca59cA09E5602fAe8B629DeE83FfA819741f14be;\n address constant WOETH = 0xD8724322f44E5c58D7A815F542036fb17DbbF839;\n // Ref: https://docs.eo.app/docs/eprice/feed-addresses/plume\n address constant WOETH_ORACLE_FEED =\n 0x4915600Ed7d85De62011433eEf0BD5399f677e9b;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == WETH) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == WOETH) {\n // Chainlink: https://data.chain.link/feeds/base/base/woeth-oeth-exchange-rate\n // Bridged wOETH/OETH\n feedAddress = WOETH_ORACLE_FEED;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\n\n// @notice Oracle Router that denominates all prices in USD\ncontract OracleRouter is AbstractOracleRouter {\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n pure\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n /* + STALENESS_BUFFER is added in case Oracle for some reason doesn't\n * update on heartbeat and we add a generous buffer amount.\n */\n if (asset == 0x6B175474E89094C44Da98b954EedeAC495271d0F) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/dai-usd\n // Chainlink: DAI/USD\n feedAddress = 0xAed0c38402a5d19df6E4c03F4E2DceD6e29c1ee9;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xdC035D45d973E3EC169d2276DDab16f1e407384F) {\n // https://data.chain.link/feeds/ethereum/mainnet/usds-usd\n // Chainlink: USDS/USD\n feedAddress = 0xfF30586cD0F29eD462364C7e81375FC0C71219b1;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/usdc-usd\n // Chainlink: USDC/USD\n feedAddress = 0x8fFfFfd4AfB6115b954Bd326cbe7B4BA576818f6;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xdAC17F958D2ee523a2206206994597C13D831ec7) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/usdt-usd\n // Chainlink: USDT/USD\n feedAddress = 0x3E7d1eAB13ad0104d2750B8863b489D65364e32D;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xc00e94Cb662C3520282E6f5717214004A7f26888) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/comp-usd\n // Chainlink: COMP/USD\n feedAddress = 0xdbd020CAeF83eFd542f4De03e3cF0C28A4428bd5;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/aave-usd\n // Chainlink: AAVE/USD\n feedAddress = 0x547a514d5e3769680Ce22B2361c10Ea13619e8a9;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xD533a949740bb3306d119CC777fa900bA034cd52) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/crv-usd\n // Chainlink: CRV/USD\n feedAddress = 0xCd627aA160A6fA45Eb793D19Ef54f5062F20f33f;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B) {\n // Chainlink: CVX/USD\n feedAddress = 0xd962fC30A72A84cE50161031391756Bf2876Af5D;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OSonicOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHFixedOracle } from \"./OETHFixedOracle.sol\";\n\n// @notice Oracle Router that returns 1e18 for all prices\n// used solely for deployment to testnets\ncontract OSonicOracleRouter is OETHFixedOracle {\n constructor() OETHFixedOracle() {}\n}\n" + }, + "contracts/poolBooster/AbstractPoolBoosterFactory.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IPoolBoostCentralRegistry } from \"../interfaces/poolBooster/IPoolBoostCentralRegistry.sol\";\n\n/**\n * @title Abstract Pool booster factory\n * @author Origin Protocol Inc\n */\ncontract AbstractPoolBoosterFactory is Governable {\n struct PoolBoosterEntry {\n address boosterAddress;\n address ammPoolAddress;\n IPoolBoostCentralRegistry.PoolBoosterType boosterType;\n }\n\n // @notice address of Origin Sonic\n address public immutable oSonic;\n // @notice Central registry contract\n IPoolBoostCentralRegistry public immutable centralRegistry;\n\n // @notice list of all the pool boosters created by this factory\n PoolBoosterEntry[] public poolBoosters;\n // @notice mapping of AMM pool to pool booster\n mapping(address => PoolBoosterEntry) public poolBoosterFromPool;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) {\n require(_oSonic != address(0), \"Invalid oSonic address\");\n require(_governor != address(0), \"Invalid governor address\");\n require(\n _centralRegistry != address(0),\n \"Invalid central registry address\"\n );\n\n oSonic = _oSonic;\n centralRegistry = IPoolBoostCentralRegistry(_centralRegistry);\n _setGovernor(_governor);\n }\n\n /**\n * @notice Goes over all the pool boosters created by this factory and\n * calls bribe() on them.\n * @param _exclusionList A list of pool booster addresses to skip when\n * calling this function.\n */\n function bribeAll(address[] memory _exclusionList) external {\n uint256 lengthI = poolBoosters.length;\n for (uint256 i = 0; i < lengthI; i++) {\n address poolBoosterAddress = poolBoosters[i].boosterAddress;\n bool skipBribeCall = false;\n uint256 lengthJ = _exclusionList.length;\n for (uint256 j = 0; j < lengthJ; j++) {\n // pool booster in exclusion list\n if (_exclusionList[j] == poolBoosterAddress) {\n skipBribeCall = true;\n break;\n }\n }\n\n if (!skipBribeCall) {\n IPoolBooster(poolBoosterAddress).bribe();\n }\n }\n }\n\n /**\n * @notice Removes the pool booster from the internal list of pool boosters.\n * @dev This action does not destroy the pool booster contract nor does it\n * stop the yield delegation to it.\n * @param _poolBoosterAddress address of the pool booster\n */\n function removePoolBooster(address _poolBoosterAddress)\n external\n onlyGovernor\n {\n uint256 boostersLen = poolBoosters.length;\n for (uint256 i = 0; i < boostersLen; ++i) {\n if (poolBoosters[i].boosterAddress == _poolBoosterAddress) {\n // erase mapping\n delete poolBoosterFromPool[poolBoosters[i].ammPoolAddress];\n\n // overwrite current pool booster with the last entry in the list\n poolBoosters[i] = poolBoosters[boostersLen - 1];\n // drop the last entry\n poolBoosters.pop();\n\n centralRegistry.emitPoolBoosterRemoved(_poolBoosterAddress);\n break;\n }\n }\n }\n\n function _storePoolBoosterEntry(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType _boosterType\n ) internal {\n PoolBoosterEntry memory entry = PoolBoosterEntry(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType\n );\n\n poolBoosters.push(entry);\n poolBoosterFromPool[_ammPoolAddress] = entry;\n\n // emit the events of the pool booster created\n centralRegistry.emitPoolBoosterCreated(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType\n );\n }\n\n function _deployContract(bytes memory _bytecode, uint256 _salt)\n internal\n returns (address _address)\n {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _address := create2(\n 0,\n add(_bytecode, 0x20),\n mload(_bytecode),\n _salt\n )\n }\n\n require(\n _address.code.length > 0 && _address != address(0),\n \"Failed creating a pool booster\"\n );\n }\n\n // pre-compute the address of the deployed contract that will be\n // created when create2 is called\n function _computeAddress(bytes memory _bytecode, uint256 _salt)\n internal\n view\n returns (address)\n {\n bytes32 hash = keccak256(\n abi.encodePacked(\n bytes1(0xff),\n address(this),\n _salt,\n keccak256(_bytecode)\n )\n );\n\n // cast last 20 bytes of hash to address\n return address(uint160(uint256(hash)));\n }\n\n function poolBoosterLength() external view returns (uint256) {\n return poolBoosters.length;\n }\n}\n" + }, + "contracts/poolBooster/PoolBoostCentralRegistry.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IPoolBoostCentralRegistry } from \"../interfaces/poolBooster/IPoolBoostCentralRegistry.sol\";\n\n/**\n * @title Contract that holds all governance approved pool booster Factory\n * implementation deployments\n * @author Origin Protocol Inc\n */\ncontract PoolBoostCentralRegistry is Governable, IPoolBoostCentralRegistry {\n event FactoryApproved(address factoryAddress);\n event FactoryRemoved(address factoryAddress);\n\n // @notice List of approved factories\n address[] public factories;\n\n modifier onlyApprovedFactories() {\n require(isApprovedFactory(msg.sender), \"Not an approved factory\");\n _;\n }\n\n constructor() {\n // set the governor of the implementation contract to zero address\n _setGovernor(address(0));\n }\n\n /**\n * @notice Adds a factory address to the approved factory addresses\n * @param _factoryAddress address of the factory\n */\n function approveFactory(address _factoryAddress) external onlyGovernor {\n require(_factoryAddress != address(0), \"Invalid address\");\n require(\n !isApprovedFactory(_factoryAddress),\n \"Factory already approved\"\n );\n\n factories.push(_factoryAddress);\n emit FactoryApproved(_factoryAddress);\n }\n\n /**\n * @notice Removes the factory from approved factory addresses\n * @param _factoryAddress address of the factory\n */\n function removeFactory(address _factoryAddress) external onlyGovernor {\n require(_factoryAddress != address(0), \"Invalid address\");\n\n uint256 length = factories.length;\n bool factoryRemoved = false;\n for (uint256 i = 0; i < length; i++) {\n if (factories[i] != _factoryAddress) {\n continue;\n }\n\n factories[i] = factories[length - 1];\n factories.pop();\n emit FactoryRemoved(_factoryAddress);\n factoryRemoved = true;\n break;\n }\n require(factoryRemoved, \"Not an approved factory\");\n\n emit FactoryRemoved(_factoryAddress);\n }\n\n /**\n * @notice Emits a pool booster created event\n * @dev This has been created as a convenience method for the monitoring to have\n * an index of all of the created pool boosters by only listening to the\n * events of this contract.\n * @param _poolBoosterAddress address of the pool booster created\n * @param _ammPoolAddress address of the AMM pool forwarding yield to the pool booster\n * @param _boosterType PoolBoosterType the type of the pool booster\n */\n function emitPoolBoosterCreated(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n PoolBoosterType _boosterType\n ) external onlyApprovedFactories {\n emit PoolBoosterCreated(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType,\n msg.sender // address of the factory\n );\n }\n\n /**\n * @notice Emits a pool booster removed event\n * @dev This has been created as a convenience method for the monitoring to have\n * an index of all of the removed pool boosters by only listening to the\n * events of this contract.\n * @param _poolBoosterAddress address of the pool booster to be removed\n */\n function emitPoolBoosterRemoved(address _poolBoosterAddress)\n external\n onlyApprovedFactories\n {\n emit PoolBoosterRemoved(_poolBoosterAddress);\n }\n\n /**\n * @notice Returns true if the factory is approved\n * @param _factoryAddress address of the factory\n */\n function isApprovedFactory(address _factoryAddress)\n public\n view\n returns (bool)\n {\n uint256 length = factories.length;\n for (uint256 i = 0; i < length; i++) {\n if (factories[i] == _factoryAddress) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * @notice Returns all supported factories\n */\n function getAllFactories() external view returns (address[] memory) {\n return factories;\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactoryMerkl.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterMerkl } from \"./PoolBoosterMerkl.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Merkl pool boosters.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactoryMerkl is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n /// @notice address of the Merkl distributor\n address public merklDistributor;\n\n /// @notice event emitted when the Merkl distributor is updated\n event MerklDistributorUpdated(address newDistributor);\n\n /**\n * @param _oSonic address of the OSonic token\n * @param _governor address governor\n * @param _centralRegistry address of the central registry\n * @param _merklDistributor address of the Merkl distributor\n */\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry,\n address _merklDistributor\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {\n _setMerklDistributor(_merklDistributor);\n }\n\n /**\n * @dev Create a Pool Booster for Merkl.\n * @param _campaignType The type of campaign to create. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _campaignDuration The duration of the campaign in seconds\n * @param campaignData The data to be used for the campaign. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * This should be fetched from the Merkl UI.\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterMerkl(\n uint32 _campaignType,\n address _ammPoolAddress,\n uint32 _campaignDuration,\n bytes calldata campaignData,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n require(_campaignDuration > 1 hours, \"Invalid campaign duration\");\n require(campaignData.length > 0, \"Invalid campaign data\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterMerkl).creationCode,\n abi.encode(\n oSonic,\n merklDistributor,\n _campaignDuration,\n _campaignType,\n governor(),\n campaignData\n )\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.MerklBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for Merkl.\n * @param _campaignType The type of campaign to create. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterMerkl` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n uint32 _campaignType,\n address _ammPoolAddress,\n uint32 _campaignDuration,\n bytes calldata campaignData,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n require(_campaignDuration > 1 hours, \"Invalid campaign duration\");\n require(campaignData.length > 0, \"Invalid campaign data\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterMerkl).creationCode,\n abi.encode(\n oSonic,\n merklDistributor,\n _campaignDuration,\n _campaignType,\n governor(),\n campaignData\n )\n ),\n _salt\n );\n }\n\n /**\n * @dev Set the address of the Merkl distributor\n * @param _merklDistributor The address of the Merkl distributor\n */\n function setMerklDistributor(address _merklDistributor)\n external\n onlyGovernor\n {\n _setMerklDistributor(_merklDistributor);\n }\n\n function _setMerklDistributor(address _merklDistributor) internal {\n require(\n _merklDistributor != address(0),\n \"Invalid merklDistributor address\"\n );\n merklDistributor = _merklDistributor;\n emit MerklDistributorUpdated(_merklDistributor);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactoryMetropolis.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterMetropolis } from \"./PoolBoosterMetropolis.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Metropolis pool boosters.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactoryMetropolis is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n address public immutable rewardFactory;\n address public immutable voter;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n // @param address _rewardFactory address of the Metropolis reward factory\n // @param address _voter address of the Metropolis voter\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry,\n address _rewardFactory,\n address _voter\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {\n rewardFactory = _rewardFactory;\n voter = _voter;\n }\n\n /**\n * @dev Create a Pool Booster for Metropolis pool.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterMetropolis(address _ammPoolAddress, uint256 _salt)\n external\n onlyGovernor\n {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterMetropolis).creationCode,\n abi.encode(oSonic, rewardFactory, _ammPoolAddress, voter)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.MetropolisBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for Metropolis pool.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterMetropolis` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(address _ammPoolAddress, uint256 _salt)\n external\n view\n returns (address)\n {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterMetropolis).creationCode,\n abi.encode(oSonic, rewardFactory, _ammPoolAddress, voter)\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactorySwapxDouble.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterSwapxDouble } from \"./PoolBoosterSwapxDouble.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Swapx Ichi pool boosters where both of the\n * gauges need incentivizing.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactorySwapxDouble is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {}\n\n /**\n * @dev Create a Pool Booster for SwapX Ichi vault based pool where 2 Bribe contracts need to be\n * bribed\n * @param _bribeAddressOS address of the Bribes.sol(Bribe) contract for the OS token side\n * @param _bribeAddressOther address of the Bribes.sol(Bribe) contract for the other token in the pool\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _split 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n * bribe contract and 60% to other bribe contract\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterSwapxDouble(\n address _bribeAddressOS,\n address _bribeAddressOther,\n address _ammPoolAddress,\n uint256 _split,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterSwapxDouble).creationCode,\n abi.encode(_bribeAddressOS, _bribeAddressOther, oSonic, _split)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.SwapXDoubleBooster\n );\n }\n\n /**\n * @dev Compute the address of the pool booster to be deployed.\n * @param _bribeAddressOS address of the Bribes.sol(Bribe) contract for the OS token side\n * @param _bribeAddressOther address of the Bribes.sol(Bribe) contract for the other token in the pool\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _split 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n * bribe contract and 60% to other bribe contract\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterSwapxDouble` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n address _bribeAddressOS,\n address _bribeAddressOther,\n address _ammPoolAddress,\n uint256 _split,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterSwapxDouble).creationCode,\n abi.encode(\n _bribeAddressOS,\n _bribeAddressOther,\n oSonic,\n _split\n )\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactorySwapxSingle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterSwapxSingle } from \"./PoolBoosterSwapxSingle.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Swapx Single pool boosters - where a single\n * gauge is created for a pool. this is appropriate for Classic Stable & Classic\n * Volatile SwapX pools.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactorySwapxSingle is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {}\n\n /**\n * @dev Create a Pool Booster for SwapX classic volatile or classic stable pools where\n * a single Bribe contract is incentivized.\n * @param _bribeAddress address of the Bribes.sol contract\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterSwapxSingle(\n address _bribeAddress,\n address _ammPoolAddress,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterSwapxSingle).creationCode,\n abi.encode(_bribeAddress, oSonic)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.SwapXSingleBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for SwapX classic volatile or classic stable pools where\n * a single Bribe contract is incentivized.\n * @param _bribeAddress address of the Bribes.sol contract\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterSwapxSingle` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n address _bribeAddress,\n address _ammPoolAddress,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterSwapxSingle).creationCode,\n abi.encode(_bribeAddress, oSonic)\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterMerkl.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IMerklDistributor } from \"../interfaces/poolBooster/IMerklDistributor.sol\";\n\ninterface IERC1271 {\n /**\n * @dev Should return whether the signature provided is valid for the provided data\n * @param hash Hash of the data to be signed\n * @param signature Signature byte array associated with _data\n */\n function isValidSignature(bytes32 hash, bytes memory signature)\n external\n view\n returns (bytes4 magicValue);\n}\n\n/**\n * @title Pool booster for Merkl distributor\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterMerkl is IPoolBooster, IERC1271 {\n /// @notice address of merkl distributor\n IMerklDistributor public immutable merklDistributor;\n /// @notice address of the OS token\n IERC20 public immutable rewardToken;\n /// @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n /// @notice Campaign duration in seconds\n uint32 public immutable duration; // -> should be immutable\n /// @notice Campaign type\n uint32 public immutable campaignType;\n /// @notice Owner of the campaign\n address public immutable creator;\n /// @notice Campaign data\n bytes public campaignData;\n\n constructor(\n address _rewardToken,\n address _merklDistributor,\n uint32 _duration,\n uint32 _campaignType,\n address _creator,\n bytes memory _campaignData\n ) {\n require(_rewardToken != address(0), \"Invalid rewardToken address\");\n require(\n _merklDistributor != address(0),\n \"Invalid merklDistributor address\"\n );\n require(_campaignData.length > 0, \"Invalid campaignData\");\n require(_duration > 1 hours, \"Invalid duration\");\n\n campaignType = _campaignType;\n duration = _duration;\n creator = _creator;\n\n merklDistributor = IMerklDistributor(_merklDistributor);\n rewardToken = IERC20(_rewardToken);\n campaignData = _campaignData;\n }\n\n /// @notice Create a campaign on the Merkl distributor\n function bribe() external override {\n // Ensure token is approved for the Merkl distributor\n uint256 minAmount = merklDistributor.rewardTokenMinAmounts(\n address(rewardToken)\n );\n require(minAmount > 0, \"Min reward amount must be > 0\");\n\n // if balance too small or below threshold, do no bribes\n uint256 balance = rewardToken.balanceOf(address(this));\n if (\n balance < MIN_BRIBE_AMOUNT ||\n (balance * 1 hours < minAmount * duration)\n ) {\n return;\n }\n\n // Approve the bribe contract to spend the reward token\n rewardToken.approve(address(merklDistributor), balance);\n\n // Notify the bribe contract of the reward amount\n merklDistributor.signAndCreateCampaign(\n IMerklDistributor.CampaignParameters({\n campaignId: bytes32(0),\n creator: creator,\n rewardToken: address(rewardToken),\n amount: balance,\n campaignType: campaignType,\n startTimestamp: getNextPeriodStartTime(),\n duration: duration,\n campaignData: campaignData\n }),\n bytes(\"\")\n );\n emit BribeExecuted(balance);\n }\n\n /// @notice Used to sign a campaign on the Merkl distributor\n function isValidSignature(bytes32, bytes memory)\n external\n view\n override\n returns (bytes4 magicValue)\n {\n require(msg.sender == address(merklDistributor), \"Invalid sender\");\n // bytes4(keccak256(\"isValidSignature(bytes32,bytes)\")) == 0x1626ba7e\n return bytes4(0x1626ba7e);\n }\n\n /// @notice Returns the timestamp for the start of the next period based on the configured duration\n function getNextPeriodStartTime() public view returns (uint32) {\n // Calculate the timestamp for the next period boundary\n return uint32((block.timestamp / duration + 1) * duration);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterMetropolis.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @title Pool booster for Metropolis pools\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterMetropolis is IPoolBooster {\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice address of the pool\n address public immutable pool;\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n IRewarderFactory public immutable rewardFactory;\n\n IVoter public immutable voter;\n\n constructor(\n address _osToken,\n address _rewardFactory,\n address _pool,\n address _voter\n ) {\n require(_pool != address(0), \"Invalid pool address\");\n pool = _pool;\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n\n rewardFactory = IRewarderFactory(_rewardFactory);\n\n voter = IVoter(_voter);\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n (, uint256 minBribeAmount) = rewardFactory.getWhitelistedTokenInfo(\n address(osToken)\n );\n if (balance < MIN_BRIBE_AMOUNT || balance < minBribeAmount) {\n return;\n }\n\n uint256 id = voter.getCurrentVotingPeriod() + 1;\n\n // Deploy a rewarder\n IRewarder rewarder = IRewarder(\n rewardFactory.createBribeRewarder(address(osToken), pool)\n );\n\n // Approve the rewarder to spend the balance\n osToken.approve(address(rewarder), balance);\n\n // Fund and bribe the rewarder\n rewarder.fundAndBribe(id, id, balance);\n\n emit BribeExecuted(balance);\n }\n}\n\ninterface IRewarderFactory {\n function createBribeRewarder(address token, address pool)\n external\n returns (address rewarder);\n\n function getWhitelistedTokenInfo(address token)\n external\n view\n returns (bool isWhitelisted, uint256 minBribeAmount);\n}\n\ninterface IRewarder {\n function fundAndBribe(\n uint256 startId,\n uint256 lastId,\n uint256 amountPerPeriod\n ) external payable;\n}\n\ninterface IVoter {\n function getCurrentVotingPeriod() external view returns (uint256);\n}\n" + }, + "contracts/poolBooster/PoolBoosterSwapxDouble.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBribe } from \"../interfaces/poolBooster/ISwapXAlgebraBribe.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n/**\n * @title Pool booster for SwapX concentrated liquidity where 2 gauges are created for\n * every pool. Ichi vaults currently have such setup.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterSwapxDouble is IPoolBooster {\n using StableMath for uint256;\n\n // @notice address of the Bribes.sol(Bribe) contract for the OS token side\n IBribe public immutable bribeContractOS;\n // @notice address of the Bribes.sol(Bribe) contract for the other token in the pool\n IBribe public immutable bribeContractOther;\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n // bribe contract and 60% to other bribe contract\n uint256 public immutable split;\n\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n constructor(\n address _bribeContractOS,\n address _bribeContractOther,\n address _osToken,\n uint256 _split\n ) {\n require(\n _bribeContractOS != address(0),\n \"Invalid bribeContractOS address\"\n );\n require(\n _bribeContractOther != address(0),\n \"Invalid bribeContractOther address\"\n );\n // expect it to be between 1% & 99%\n require(_split > 1e16 && _split < 99e16, \"Unexpected split amount\");\n\n bribeContractOS = IBribe(_bribeContractOS);\n bribeContractOther = IBribe(_bribeContractOther);\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n split = _split;\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n if (balance < MIN_BRIBE_AMOUNT) {\n return;\n }\n\n uint256 osBribeAmount = balance.mulTruncate(split);\n uint256 otherBribeAmount = balance - osBribeAmount;\n\n osToken.approve(address(bribeContractOS), osBribeAmount);\n osToken.approve(address(bribeContractOther), otherBribeAmount);\n\n bribeContractOS.notifyRewardAmount(address(osToken), osBribeAmount);\n bribeContractOther.notifyRewardAmount(\n address(osToken),\n otherBribeAmount\n );\n\n emit BribeExecuted(balance);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterSwapxSingle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBribe } from \"../interfaces/poolBooster/ISwapXAlgebraBribe.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @title Pool booster for SwapX for Classic Stable Pools and Classic Volatile Pools\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterSwapxSingle is IPoolBooster {\n // @notice address of the Bribes.sol(Bribe) contract\n IBribe public immutable bribeContract;\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n constructor(address _bribeContract, address _osToken) {\n require(_bribeContract != address(0), \"Invalid bribeContract address\");\n bribeContract = IBribe(_bribeContract);\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n if (balance < MIN_BRIBE_AMOUNT) {\n return;\n }\n\n osToken.approve(address(bribeContract), balance);\n\n bribeContract.notifyRewardAmount(address(osToken), balance);\n emit BribeExecuted(balance);\n }\n}\n" + }, + "contracts/proxies/BaseProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice BridgedBaseWOETHProxy delegates calls to BridgedWOETH implementation\n */\ncontract BridgedBaseWOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseVaultProxy delegates calls to OETHBaseVault implementation\n */\ncontract OETHBaseVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseProxy delegates calls to OETH implementation\n */\ncontract OETHBaseProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHBaseProxy delegates calls to WOETH implementation\n */\ncontract WOETHBaseProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseDripperProxy delegates calls to a FixedRateDripper implementation\n */\ncontract OETHBaseDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice AerodromeAMOStrategyProxy delegates calls to AerodromeAMOStrategy implementation\n */\ncontract AerodromeAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BridgedWOETHStrategyProxy delegates calls to BridgedWOETHStrategy implementation\n */\ncontract BridgedWOETHStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseHarvesterProxy delegates calls to a SuperOETHHarvester implementation\n */\ncontract OETHBaseHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseCurveAMOProxy delegates calls to a OETHBaseCurveAMO implementation\n */\ncontract OETHBaseCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/proxies/InitializeGovernedUpgradeabilityProxy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { Governable } from \"../governance/Governable.sol\";\n\n/**\n * @title BaseGovernedUpgradeabilityProxy\n * @dev This contract combines an upgradeability proxy with our governor system.\n * It is based on an older version of OpenZeppelins BaseUpgradeabilityProxy\n * with Solidity ^0.8.0.\n * @author Origin Protocol Inc\n */\ncontract InitializeGovernedUpgradeabilityProxy is Governable {\n /**\n * @dev Emitted when the implementation is upgraded.\n * @param implementation Address of the new implementation.\n */\n event Upgraded(address indexed implementation);\n\n constructor() {\n _setGovernor(msg.sender);\n }\n\n /**\n * @dev Contract initializer with Governor enforcement\n * @param _logic Address of the initial implementation.\n * @param _initGovernor Address of the initial Governor.\n * @param _data Data to send as msg.data to the implementation to initialize\n * the proxied contract.\n * It should include the signature and the parameters of the function to be\n * called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n * This parameter is optional, if no data is given the initialization call\n * to proxied contract will be skipped.\n */\n function initialize(\n address _logic,\n address _initGovernor,\n bytes calldata _data\n ) public payable onlyGovernor {\n require(_implementation() == address(0));\n require(_logic != address(0), \"Implementation not set\");\n assert(\n IMPLEMENTATION_SLOT ==\n bytes32(uint256(keccak256(\"eip1967.proxy.implementation\")) - 1)\n );\n _setImplementation(_logic);\n if (_data.length > 0) {\n (bool success, ) = _logic.delegatecall(_data);\n require(success);\n }\n _changeGovernor(_initGovernor);\n }\n\n /**\n * @return The address of the proxy admin/it's also the governor.\n */\n function admin() external view returns (address) {\n return _governor();\n }\n\n /**\n * @return The address of the implementation.\n */\n function implementation() external view returns (address) {\n return _implementation();\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy.\n * Only the admin can call this function.\n * @param _newImplementation Address of the new implementation.\n */\n function upgradeTo(address _newImplementation) external onlyGovernor {\n _upgradeTo(_newImplementation);\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy and call a function\n * on the new implementation.\n * This is useful to initialize the proxied contract.\n * @param newImplementation Address of the new implementation.\n * @param data Data to send as msg.data in the low level call.\n * It should include the signature and the parameters of the function to be called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n */\n function upgradeToAndCall(address newImplementation, bytes calldata data)\n external\n payable\n onlyGovernor\n {\n _upgradeTo(newImplementation);\n (bool success, ) = newImplementation.delegatecall(data);\n require(success);\n }\n\n /**\n * @dev Fallback function.\n * Implemented entirely in `_fallback`.\n */\n fallback() external payable {\n _fallback();\n }\n\n /**\n * @dev Delegates execution to an implementation contract.\n * This is a low level function that doesn't return to its internal call site.\n * It will return to the external caller whatever the implementation returns.\n * @param _impl Address to delegate.\n */\n function _delegate(address _impl) internal {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(gas(), _impl, 0, calldatasize(), 0, 0)\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n /**\n * @dev Function that is run as the first thing in the fallback function.\n * Can be redefined in derived contracts to add functionality.\n * Redefinitions must call super._willFallback().\n */\n function _willFallback() internal {}\n\n /**\n * @dev fallback implementation.\n * Extracted to enable manual triggering.\n */\n function _fallback() internal {\n _willFallback();\n _delegate(_implementation());\n }\n\n /**\n * @dev Storage slot with the address of the current implementation.\n * This is the keccak-256 hash of \"eip1967.proxy.implementation\" subtracted by 1, and is\n * validated in the constructor.\n */\n bytes32 internal constant IMPLEMENTATION_SLOT =\n 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n /**\n * @dev Returns the current implementation.\n * @return impl Address of the current implementation\n */\n function _implementation() internal view returns (address impl) {\n bytes32 slot = IMPLEMENTATION_SLOT;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n impl := sload(slot)\n }\n }\n\n /**\n * @dev Upgrades the proxy to a new implementation.\n * @param newImplementation Address of the new implementation.\n */\n function _upgradeTo(address newImplementation) internal {\n _setImplementation(newImplementation);\n emit Upgraded(newImplementation);\n }\n\n /**\n * @dev Sets the implementation address of the proxy.\n * @param newImplementation Address of the new implementation.\n */\n function _setImplementation(address newImplementation) internal {\n require(\n Address.isContract(newImplementation),\n \"Cannot set a proxy implementation to a non-contract address\"\n );\n\n bytes32 slot = IMPLEMENTATION_SLOT;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(slot, newImplementation)\n }\n }\n}\n" + }, + "contracts/proxies/PlumeProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OETHPlumeVaultProxy delegates calls to OETHPlumeVault implementation\n */\ncontract OETHPlumeVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHPlumeProxy delegates calls to OETH implementation\n */\ncontract OETHPlumeProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHPlumeProxy delegates calls to WOETH implementation\n */\ncontract WOETHPlumeProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice RoosterAMOStrategyProxy delegates calls to a RoosterAMOStrategy implementation\n */\ncontract RoosterAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/proxies/Proxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OUSDProxy delegates calls to an OUSD implementation\n */\ncontract OUSDProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WrappedOUSDProxy delegates calls to a WrappedOUSD implementation\n */\ncontract WrappedOUSDProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice VaultProxy delegates calls to a Vault implementation\n */\ncontract VaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CompoundStrategyProxy delegates calls to a CompoundStrategy implementation\n */\ncontract CompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice AaveStrategyProxy delegates calls to a AaveStrategy implementation\n */\ncontract AaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ConvexStrategyProxy delegates calls to a ConvexStrategy implementation\n */\ncontract ConvexStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice HarvesterProxy delegates calls to a Harvester implementation\n */\ncontract HarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice DripperProxy delegates calls to a Dripper implementation\n */\ncontract DripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoCompoundStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\n */\ncontract MorphoCompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ConvexOUSDMetaStrategyProxy delegates calls to a ConvexOUSDMetaStrategy implementation\n */\ncontract ConvexOUSDMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoAaveStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\n */\ncontract MorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHProxy delegates calls to nowhere for now\n */\ncontract OETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHProxy delegates calls to nowhere for now\n */\ncontract WOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHVaultProxy delegates calls to a Vault implementation\n */\ncontract OETHVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHDripperProxy delegates calls to a OETHDripper implementation\n */\ncontract OETHDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHHarvesterProxy delegates calls to a Harvester implementation\n */\ncontract OETHHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CurveEthStrategyProxy delegates calls to a CurveEthStrategy implementation\n */\ncontract ConvexEthMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BuybackProxy delegates calls to Buyback implementation\n */\ncontract BuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHMorphoAaveStrategyProxy delegates calls to a MorphoAaveStrategy implementation\n */\ncontract OETHMorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBalancerMetaPoolrEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\n */\ncontract OETHBalancerMetaPoolrEthStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice OETHBalancerMetaPoolwstEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\n */\ncontract OETHBalancerMetaPoolwstEthStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MakerDsrStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MakerDsrStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBuybackProxy delegates calls to Buyback implementation\n */\ncontract OETHBuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BridgedWOETHProxy delegates calls to BridgedWOETH implementation\n */\ncontract BridgedWOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice NativeStakingSSVStrategyProxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulatorProxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulatorProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingSSVStrategy2Proxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategy2Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulator2Proxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulator2Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingSSVStrategy3Proxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategy3Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulator3Proxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulator3Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MetaMorphoStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MetaMorphoStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ARMBuybackProxy delegates calls to Buyback implementation\n */\ncontract ARMBuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoGauntletPrimeUSDCStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MorphoGauntletPrimeUSDCStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MorphoGauntletPrimeUSDTStrategyProxy delegates calls to a Generalized4626USDTStrategy implementation\n */\ncontract MorphoGauntletPrimeUSDTStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice CurvePoolBoosterProxy delegates calls to a CurvePoolBooster implementation\n */\ncontract CurvePoolBoosterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHFixedRateDripperProxy delegates calls to a OETHFixedRateDripper implementation\n */\ncontract OETHFixedRateDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHSimpleHarvesterProxy delegates calls to a OETHSimpleHarvester implementation\n */\ncontract OETHSimpleHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice PoolBoostCentralRegistryProxy delegates calls to the PoolBoostCentralRegistry implementation\n */\ncontract PoolBoostCentralRegistryProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MakerSSRStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MakerSSRStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OUSDCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\n */\ncontract OUSDCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\n */\ncontract OETHCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CompoundingStakingSSVStrategyProxy delegates calls to a CompoundingStakingSSVStrategy implementation\n */\ncontract CompoundingStakingSSVStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n" + }, + "contracts/proxies/SonicProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OSonicVaultProxy delegates calls to OSonicVault implementation\n */\ncontract OSonicVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicProxy delegates calls to OSonic implementation\n */\ncontract OSonicProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOSonicProxy delegates calls to WOSonic implementation\n */\ncontract WOSonicProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicDripperProxy delegates calls to a FixedRateDripper implementation\n */\ncontract OSonicDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice SonicStakingStrategyProxy delegates calls to SonicStakingStrategy implementation\n */\ncontract SonicStakingStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicHarvesterProxy delegates calls to a OSonicHarvester implementation\n */\ncontract OSonicHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice SonicSwapXAMOStrategyProxy delegates calls to a SonicSwapXAMOStrategy implementation\n */\ncontract SonicSwapXAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/strategies/AaveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Aave Strategy\n * @notice Investment strategy for investing stablecoins via Aave\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport \"./IAave.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\nimport { IAaveStakedToken } from \"./IAaveStakeToken.sol\";\nimport { IAaveIncentivesController } from \"./IAaveIncentivesController.sol\";\n\ncontract AaveStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n\n uint16 constant referralCode = 92;\n\n IAaveIncentivesController public incentivesController;\n IAaveStakedToken public stkAave;\n\n /**\n * @param _stratConfig The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as AAVE needs several extra\n * addresses for the rewards program.\n * @param _rewardTokenAddresses Address of the AAVE token\n * @param _assets Addresses of supported assets\n * @param _pTokens Platform Token corresponding addresses\n * @param _incentivesAddress Address of the AAVE incentives controller\n * @param _stkAaveAddress Address of the stkAave contract\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // AAVE\n address[] calldata _assets,\n address[] calldata _pTokens,\n address _incentivesAddress,\n address _stkAaveAddress\n ) external onlyGovernor initializer {\n incentivesController = IAaveIncentivesController(_incentivesAddress);\n stkAave = IAaveStakedToken(_stkAaveAddress);\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Deposit asset into Aave\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Aave\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n // Following line also doubles as a check that we are depositing\n // an asset that we support.\n emit Deposit(_asset, _getATokenFor(_asset), _amount);\n _getLendingPool().deposit(_asset, _amount, address(this), referralCode);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Aave\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Aave\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n emit Withdrawal(_asset, _getATokenFor(_asset), _amount);\n uint256 actual = _getLendingPool().withdraw(\n _asset,\n _amount,\n address(this)\n );\n require(actual == _amount, \"Did not withdraw enough\");\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n // Redeem entire balance of aToken\n IERC20 asset = IERC20(assetsMapped[i]);\n address aToken = _getATokenFor(assetsMapped[i]);\n uint256 balance = IERC20(aToken).balanceOf(address(this));\n if (balance > 0) {\n uint256 actual = _getLendingPool().withdraw(\n address(asset),\n balance,\n address(this)\n );\n require(actual == balance, \"Did not withdraw enough\");\n\n uint256 assetBalance = asset.balanceOf(address(this));\n // Transfer entire balance to Vault\n asset.safeTransfer(vaultAddress, assetBalance);\n\n emit Withdrawal(address(asset), aToken, assetBalance);\n }\n }\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n // Balance is always with token aToken decimals\n address aToken = _getATokenFor(_asset);\n balance = IERC20(aToken).balanceOf(address(this));\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Approve the spending of all assets by their corresponding aToken,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n address lendingPool = address(_getLendingPool());\n // approve the pool to spend the Asset\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n address asset = assetsMapped[i];\n // Safe approval\n IERC20(asset).safeApprove(lendingPool, 0);\n IERC20(asset).safeApprove(lendingPool, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / aTokens\n We need to give the AAVE lending pool approval to transfer the\n asset.\n * @param _asset Address of the asset to approve\n * @param _aToken Address of the aToken\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _aToken)\n internal\n override\n {\n address lendingPool = address(_getLendingPool());\n IERC20(_asset).safeApprove(lendingPool, 0);\n IERC20(_asset).safeApprove(lendingPool, type(uint256).max);\n }\n\n /**\n * @dev Get the aToken wrapped in the IERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return Corresponding aToken to this asset\n */\n function _getATokenFor(address _asset) internal view returns (address) {\n address aToken = assetToPToken[_asset];\n require(aToken != address(0), \"aToken does not exist\");\n return aToken;\n }\n\n /**\n * @dev Get the current address of the Aave lending pool, which is the gateway to\n * depositing.\n * @return Current lending pool implementation\n */\n function _getLendingPool() internal view returns (IAaveLendingPool) {\n address lendingPool = ILendingPoolAddressesProvider(platformAddress)\n .getLendingPool();\n require(lendingPool != address(0), \"Lending pool does not exist\");\n return IAaveLendingPool(lendingPool);\n }\n\n /**\n * @dev Collect stkAave, convert it to AAVE send to Vault.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n if (address(stkAave) == address(0)) {\n return;\n }\n\n // Check staked AAVE cooldown timer\n uint256 cooldown = stkAave.stakersCooldowns(address(this));\n uint256 windowStart = cooldown + stkAave.COOLDOWN_SECONDS();\n uint256 windowEnd = windowStart + stkAave.UNSTAKE_WINDOW();\n\n // If inside the unlock window, then we can redeem stkAave\n // for AAVE and send it to the vault.\n if (block.timestamp > windowStart && block.timestamp <= windowEnd) {\n // Redeem to AAVE\n uint256 stkAaveBalance = stkAave.balanceOf(address(this));\n stkAave.redeem(address(this), stkAaveBalance);\n\n // Transfer AAVE to harvesterAddress\n uint256 aaveBalance = IERC20(rewardTokenAddresses[0]).balanceOf(\n address(this)\n );\n if (aaveBalance > 0) {\n IERC20(rewardTokenAddresses[0]).safeTransfer(\n harvesterAddress,\n aaveBalance\n );\n }\n }\n\n // Collect available rewards and restart the cooldown timer, if either of\n // those should be run.\n if (block.timestamp > windowStart || cooldown == 0) {\n uint256 assetsLen = assetsMapped.length;\n // aToken addresses for incentives controller\n address[] memory aTokens = new address[](assetsLen);\n for (uint256 i = 0; i < assetsLen; ++i) {\n aTokens[i] = _getATokenFor(assetsMapped[i]);\n }\n\n // 1. If we have rewards availabile, collect them\n uint256 pendingRewards = incentivesController.getRewardsBalance(\n aTokens,\n address(this)\n );\n if (pendingRewards > 0) {\n // Because getting more stkAAVE from the incentives controller\n // with claimRewards() may push the stkAAVE cooldown time\n // forward, it is called after stakedAAVE has been turned into\n // AAVE.\n uint256 collected = incentivesController.claimRewards(\n aTokens,\n pendingRewards,\n address(this)\n );\n require(collected == pendingRewards, \"AAVE reward difference\");\n }\n\n // 2. Start cooldown counting down.\n if (stkAave.balanceOf(address(this)) > 0) {\n // Protected with if since cooldown call would revert\n // if no stkAave balance.\n stkAave.cooldown();\n }\n }\n }\n}\n" + }, + "contracts/strategies/AbstractCompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base Compound Abstract Strategy\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { ICERC20 } from \"./ICompound.sol\";\nimport { IComptroller } from \"../interfaces/IComptroller.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\nabstract contract AbstractCompoundStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n\n int256[50] private __reserved;\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Get the cToken wrapped in the ICERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return Corresponding cToken to this asset\n */\n function _getCTokenFor(address _asset) internal view returns (ICERC20) {\n address cToken = assetToPToken[_asset];\n require(cToken != address(0), \"cToken does not exist\");\n return ICERC20(cToken);\n }\n\n /**\n * @dev Converts an underlying amount into cToken amount\n * cTokenAmt = (underlying * 1e18) / exchangeRate\n * @param _cToken cToken for which to change\n * @param _underlying Amount of underlying to convert\n * @return amount Equivalent amount of cTokens\n */\n function _convertUnderlyingToCToken(ICERC20 _cToken, uint256 _underlying)\n internal\n view\n returns (uint256 amount)\n {\n // e.g. 1e18*1e18 / 205316390724364402565641705 = 50e8\n // e.g. 1e8*1e18 / 205316390724364402565641705 = 0.45 or 0\n amount = (_underlying * 1e18) / _cToken.exchangeRateStored();\n }\n}\n" + }, + "contracts/strategies/AbstractConvexMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { ICurveMetaPool } from \"./ICurveMetaPool.sol\";\nimport { IERC20, AbstractCurveStrategy, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\nabstract contract AbstractConvexMetaStrategy is AbstractCurveStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n event MaxWithdrawalSlippageUpdated(\n uint256 _prevMaxSlippagePercentage,\n uint256 _newMaxSlippagePercentage\n );\n\n // used to circumvent the stack too deep issue\n struct InitConfig {\n address cvxDepositorAddress; //Address of the Convex depositor(AKA booster) for this pool\n address metapoolAddress; //Address of the Curve MetaPool\n address metapoolMainToken; //Address of Main metapool token\n address cvxRewardStakerAddress; //Address of the CVX rewards staker\n address metapoolLPToken; //Address of metapool LP token\n uint256 cvxDepositorPTokenId; //Pid of the pool referred to by Depositor and staker\n }\n\n address internal cvxDepositorAddress;\n address internal cvxRewardStakerAddress;\n uint256 internal cvxDepositorPTokenId;\n ICurveMetaPool internal metapool;\n IERC20 internal metapoolMainToken;\n IERC20 internal metapoolLPToken;\n // Ordered list of metapool assets\n address[] internal metapoolAssets;\n // Max withdrawal slippage denominated in 1e18 (1e18 == 100%)\n uint256 public maxWithdrawalSlippage;\n uint128 internal crvCoinIndex;\n uint128 internal mainCoinIndex;\n\n int256[41] private ___reserved;\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * DAI, USDC, USDT\n * @param _pTokens Platform Token corresponding addresses\n * @param initConfig Various addresses and info for initialization state\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets,\n address[] calldata _pTokens,\n InitConfig calldata initConfig\n ) external onlyGovernor initializer {\n require(_assets.length == 3, \"Must have exactly three assets\");\n // Should be set prior to abstract initialize call otherwise\n // abstractSetPToken calls will fail\n cvxDepositorAddress = initConfig.cvxDepositorAddress;\n pTokenAddress = _pTokens[0];\n metapool = ICurveMetaPool(initConfig.metapoolAddress);\n metapoolMainToken = IERC20(initConfig.metapoolMainToken);\n cvxRewardStakerAddress = initConfig.cvxRewardStakerAddress;\n metapoolLPToken = IERC20(initConfig.metapoolLPToken);\n cvxDepositorPTokenId = initConfig.cvxDepositorPTokenId;\n maxWithdrawalSlippage = 1e16;\n\n metapoolAssets = [metapool.coins(0), metapool.coins(1)];\n crvCoinIndex = _getMetapoolCoinIndex(pTokenAddress);\n mainCoinIndex = _getMetapoolCoinIndex(initConfig.metapoolMainToken);\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n balance = 0;\n\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 contractPTokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (contractPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = contractPTokens.mulTruncate(virtual_price);\n balance += value;\n }\n\n /* We intentionally omit the metapoolLp tokens held by the metastrategyContract\n * since the contract should never (except in the middle of deposit/withdrawal\n * transaction) hold any amount of those tokens in normal operation. There\n * could be tokens sent to it by a 3rd party and we decide to actively ignore\n * those.\n */\n uint256 metapoolGaugePTokens = IRewardStaking(cvxRewardStakerAddress)\n .balanceOf(address(this));\n\n if (metapoolGaugePTokens > 0) {\n uint256 value = metapoolGaugePTokens.mulTruncate(\n metapool.get_virtual_price()\n );\n balance += value;\n }\n\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = balance.scaleBy(assetDecimals, 18) / THREEPOOL_ASSET_COUNT;\n }\n\n /**\n * @dev This function is completely analogous to _calcCurveTokenAmount[AbstractCurveStrategy]\n * and just utilizes different Curve (meta)pool API\n */\n function _calcCurveMetaTokenAmount(uint128 _coinIndex, uint256 _amount)\n internal\n returns (uint256 requiredMetapoolLP)\n {\n uint256[2] memory _amounts = [uint256(0), uint256(0)];\n _amounts[uint256(_coinIndex)] = _amount;\n\n // LP required when removing required asset ignoring fees\n uint256 lpRequiredNoFees = metapool.calc_token_amount(_amounts, false);\n /* LP required if fees would apply to entirety of removed amount\n *\n * fee is 1e10 denominated number: https://curve.readthedocs.io/exchange-pools.html#StableSwap.fee\n */\n uint256 lpRequiredFullFees = lpRequiredNoFees.mulTruncateScale(\n 1e10 + metapool.fee(),\n 1e10\n );\n\n /* asset received when withdrawing full fee applicable LP accounting for\n * slippage and fees\n */\n uint256 assetReceivedForFullLPFees = metapool.calc_withdraw_one_coin(\n lpRequiredFullFees,\n int128(_coinIndex)\n );\n\n // exact amount of LP required\n requiredMetapoolLP =\n (lpRequiredFullFees * _amount) /\n assetReceivedForFullLPFees;\n }\n\n function _approveBase() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // 3Pool for LP token (required for removing liquidity)\n pToken.safeApprove(platformAddress, 0);\n pToken.safeApprove(platformAddress, type(uint256).max);\n // Gauge for LP token\n metapoolLPToken.safeApprove(cvxDepositorAddress, 0);\n metapoolLPToken.safeApprove(cvxDepositorAddress, type(uint256).max);\n // Metapool for LP token\n pToken.safeApprove(address(metapool), 0);\n pToken.safeApprove(address(metapool), type(uint256).max);\n // Metapool for Metapool main token\n metapoolMainToken.safeApprove(address(metapool), 0);\n metapoolMainToken.safeApprove(address(metapool), type(uint256).max);\n }\n\n /**\n * @dev Get the index of the coin\n */\n function _getMetapoolCoinIndex(address _asset)\n internal\n view\n returns (uint128)\n {\n for (uint128 i = 0; i < 2; i++) {\n if (metapoolAssets[i] == _asset) return i;\n }\n revert(\"Invalid Metapool asset\");\n }\n\n /**\n * @dev Sets max withdrawal slippage that is considered when removing\n * liquidity from Metapools.\n * @param _maxWithdrawalSlippage Max withdrawal slippage denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxWithdrawalSlippage should actually be 0.1% (1e15)\n * for production usage. Contract allows as low value as 0% for confirming\n * correct behavior in test suite.\n */\n function setMaxWithdrawalSlippage(uint256 _maxWithdrawalSlippage)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(\n _maxWithdrawalSlippage <= 1e18,\n \"Max withdrawal slippage needs to be between 0% - 100%\"\n );\n emit MaxWithdrawalSlippageUpdated(\n maxWithdrawalSlippage,\n _maxWithdrawalSlippage\n );\n maxWithdrawalSlippage = _maxWithdrawalSlippage;\n }\n\n /**\n * @dev Collect accumulated CRV and CVX and send to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n IRewardStaking(cvxRewardStakerAddress).getReward();\n _collectRewardTokens();\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/AbstractCurveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve 3Pool Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\nabstract contract AbstractCurveStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 internal constant MAX_SLIPPAGE = 1e16; // 1%, same as the Curve UI\n // number of assets in Curve 3Pool (USDC, DAI, USDT)\n uint256 internal constant THREEPOOL_ASSET_COUNT = 3;\n address internal pTokenAddress;\n\n int256[49] private __reserved;\n\n /**\n * @dev Deposit asset into the Curve 3Pool\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_amount > 0, \"Must deposit something\");\n emit Deposit(_asset, pTokenAddress, _amount);\n\n // 3Pool requires passing deposit amounts for all 3 assets, set to 0 for\n // all\n uint256[3] memory _amounts;\n uint256 poolCoinIndex = _getCoinIndex(_asset);\n // Set the amount on the asset we want to deposit\n _amounts[poolCoinIndex] = _amount;\n ICurvePool curvePool = ICurvePool(platformAddress);\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n uint256 depositValue = _amount.scaleBy(18, assetDecimals).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = depositValue.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n // Do the deposit to 3pool\n curvePool.add_liquidity(_amounts, minMintAmount);\n _lpDepositAll();\n }\n\n function _lpDepositAll() internal virtual;\n\n /**\n * @dev Deposit the entire balance of any supported asset into the Curve 3pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n uint256 depositValue = 0;\n ICurvePool curvePool = ICurvePool(platformAddress);\n uint256 curveVirtualPrice = curvePool.get_virtual_price();\n\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address assetAddress = assetsMapped[i];\n uint256 balance = IERC20(assetAddress).balanceOf(address(this));\n if (balance > 0) {\n uint256 poolCoinIndex = _getCoinIndex(assetAddress);\n // Set the amount on the asset we want to deposit\n _amounts[poolCoinIndex] = balance;\n uint256 assetDecimals = Helpers.getDecimals(assetAddress);\n // Get value of deposit in Curve LP token to later determine\n // the minMintAmount argument for add_liquidity\n depositValue =\n depositValue +\n balance.scaleBy(18, assetDecimals).divPrecisely(\n curveVirtualPrice\n );\n emit Deposit(assetAddress, pTokenAddress, balance);\n }\n }\n\n uint256 minMintAmount = depositValue.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n // Do the deposit to 3pool\n curvePool.add_liquidity(_amounts, minMintAmount);\n\n /* In case of Curve Strategy all assets are mapped to the same pToken (3CrvLP). Let\n * descendants further handle the pToken. By either deploying it to the metapool and\n * resulting tokens in Gauge. Or deploying pTokens directly to the Gauge.\n */\n _lpDepositAll();\n }\n\n function _lpWithdraw(uint256 numCrvTokens) internal virtual;\n\n function _lpWithdrawAll() internal virtual;\n\n /**\n * @dev Withdraw asset from Curve 3Pool\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Invalid amount\");\n\n emit Withdrawal(_asset, pTokenAddress, _amount);\n\n uint256 contractCrv3Tokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n\n uint256 coinIndex = _getCoinIndex(_asset);\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 requiredCrv3Tokens = _calcCurveTokenAmount(coinIndex, _amount);\n\n // We have enough LP tokens, make sure they are all on this contract\n if (contractCrv3Tokens < requiredCrv3Tokens) {\n _lpWithdraw(requiredCrv3Tokens - contractCrv3Tokens);\n }\n\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n _amounts[coinIndex] = _amount;\n\n curvePool.remove_liquidity_imbalance(_amounts, requiredCrv3Tokens);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Calculate amount of LP required when withdrawing specific amount of one\n * of the underlying assets accounting for fees and slippage.\n *\n * Curve pools unfortunately do not contain a calculation function for\n * amount of LP required when withdrawing a specific amount of one of the\n * underlying tokens and also accounting for fees (Curve's calc_token_amount\n * does account for slippage but not fees).\n *\n * Steps taken to calculate the metric:\n * - get amount of LP required if fees wouldn't apply\n * - increase the LP amount as if fees would apply to the entirety of the underlying\n * asset withdrawal. (when withdrawing only one coin fees apply only to amounts\n * of other assets pool would return in case of balanced removal - since those need\n * to be swapped for the single underlying asset being withdrawn)\n * - get amount of underlying asset withdrawn (this Curve function does consider slippage\n * and fees) when using the increased LP amount. As LP amount is slightly over-increased\n * so is amount of underlying assets returned.\n * - since we know exactly how much asset we require take the rate of LP required for asset\n * withdrawn to get the exact amount of LP.\n */\n function _calcCurveTokenAmount(uint256 _coinIndex, uint256 _amount)\n internal\n returns (uint256 required3Crv)\n {\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n _amounts[_coinIndex] = _amount;\n\n // LP required when removing required asset ignoring fees\n uint256 lpRequiredNoFees = curvePool.calc_token_amount(_amounts, false);\n /* LP required if fees would apply to entirety of removed amount\n *\n * fee is 1e10 denominated number: https://curve.readthedocs.io/exchange-pools.html#StableSwap.fee\n */\n uint256 lpRequiredFullFees = lpRequiredNoFees.mulTruncateScale(\n 1e10 + curvePool.fee(),\n 1e10\n );\n\n /* asset received when withdrawing full fee applicable LP accounting for\n * slippage and fees\n */\n uint256 assetReceivedForFullLPFees = curvePool.calc_withdraw_one_coin(\n lpRequiredFullFees,\n int128(uint128(_coinIndex))\n );\n\n // exact amount of LP required\n required3Crv =\n (lpRequiredFullFees * _amount) /\n assetReceivedForFullLPFees;\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n _lpWithdrawAll();\n // Withdraws are proportional to assets held by 3Pool\n uint256[3] memory minWithdrawAmounts = [\n uint256(0),\n uint256(0),\n uint256(0)\n ];\n\n // Remove liquidity\n ICurvePool threePool = ICurvePool(platformAddress);\n threePool.remove_liquidity(\n IERC20(pTokenAddress).balanceOf(address(this)),\n minWithdrawAmounts\n );\n // Transfer assets out of Vault\n // Note that Curve will provide all 3 of the assets in 3pool even if\n // we have not set PToken addresses for all of them in this strategy\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n IERC20 asset = IERC20(threePool.coins(i));\n asset.safeTransfer(vaultAddress, asset.balanceOf(address(this)));\n }\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 totalPTokens = IERC20(pTokenAddress).balanceOf(address(this));\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (totalPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = (totalPTokens * virtual_price) / 1e18;\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = value.scaleBy(assetDecimals, 18) / THREEPOOL_ASSET_COUNT;\n }\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n // This strategy is a special case since it only supports one asset\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n _approveAsset(assetsMapped[i]);\n }\n }\n\n /**\n * @dev Call the necessary approvals for the Curve pool and gauge\n * @param _asset Address of the asset\n */\n function _abstractSetPToken(address _asset, address) internal override {\n _approveAsset(_asset);\n }\n\n function _approveAsset(address _asset) internal {\n IERC20 asset = IERC20(_asset);\n // 3Pool for asset (required for adding liquidity)\n asset.safeApprove(platformAddress, 0);\n asset.safeApprove(platformAddress, type(uint256).max);\n }\n\n function _approveBase() internal virtual;\n\n /**\n * @dev Get the index of the coin\n */\n function _getCoinIndex(address _asset) internal view returns (uint256) {\n for (uint256 i = 0; i < 3; i++) {\n if (assetsMapped[i] == _asset) return i;\n }\n revert(\"Invalid 3pool asset\");\n }\n}\n" + }, + "contracts/strategies/aerodrome/AerodromeAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Aerodrome AMO strategy\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nimport { ISugarHelper } from \"../../interfaces/aerodrome/ISugarHelper.sol\";\nimport { INonfungiblePositionManager } from \"../../interfaces/aerodrome/INonfungiblePositionManager.sol\";\nimport { ISwapRouter } from \"../../interfaces/aerodrome/ISwapRouter.sol\";\nimport { ICLPool } from \"../../interfaces/aerodrome/ICLPool.sol\";\nimport { ICLGauge } from \"../../interfaces/aerodrome/ICLGauge.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\n\ncontract AerodromeAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n using SafeCast for uint256;\n\n /************************************************\n Important (!) setup configuration\n *************************************************/\n\n /**\n * In order to be able to remove a reasonable amount of complexity from the contract one of the\n * preconditions for this contract to function correctly is to have an outside account mint a small\n * amount of liquidity in the tick space where the contract will deploy's its liquidity and then send\n * that NFT LP position to a dead address (transfer to zero address not allowed.) See example of such\n * NFT LP token:\n * https://basescan.org/token/0x827922686190790b37229fd06084350e74485b72?a=413296#inventory\n */\n\n /***************************************\n Storage slot members\n ****************************************/\n\n /// @notice tokenId of the liquidity position\n uint256 public tokenId;\n /// @dev Minimum amount of tokens the strategy would be able to withdraw from the pool.\n /// minimum amount of tokens are withdrawn at a 1:1 price\n uint256 public underlyingAssets;\n /// @notice Marks the start of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareStart;\n /// @notice Marks the end of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareEnd;\n /// @dev reserved for inheritance\n int256[46] private __reserved;\n\n /***************************************\n Constants, structs and events\n ****************************************/\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the OETHb token contract\n address public immutable OETHb;\n /// @notice lower tick set to -1 representing the price of 1.0001 of WETH for 1 OETHb.\n int24 public immutable lowerTick;\n /// @notice lower tick set to 0 representing the price of 1.0000 of WETH for 1 OETHb.\n int24 public immutable upperTick;\n /// @notice tick spacing of the pool (set to 1)\n int24 public immutable tickSpacing;\n /// @notice the swapRouter for performing swaps\n ISwapRouter public immutable swapRouter;\n /// @notice the underlying AMO Slipstream pool\n ICLPool public immutable clPool;\n /// @notice the gauge for the corresponding Slipstream pool (clPool)\n /// @dev can become an immutable once the gauge is created on the base main-net\n ICLGauge public immutable clGauge;\n /// @notice the Position manager contract that is used to manage the pool's position\n INonfungiblePositionManager public immutable positionManager;\n /// @notice helper contract for liquidity and ticker math\n ISugarHelper public immutable helper;\n /// @notice sqrtRatioX96TickLower\n /// @dev tick lower has value -1 and represents the lowest price of WETH priced in OETHb. Meaning the pool\n /// offers less than 1 OETHb for 1 WETH. In other terms to get 1 OETHB the swap needs to offer 1.0001 WETH\n /// this is where purchasing OETHb with WETH within the liquidity position is most expensive\n uint160 public immutable sqrtRatioX96TickLower;\n /// @notice sqrtRatioX96TickHigher\n /// @dev tick higher has value 0 and represents 1:1 price parity of WETH to OETHb\n uint160 public immutable sqrtRatioX96TickHigher;\n /// @dev tick closest to 1:1 price parity\n /// Correctly assessing which tick is closer to 1:1 price parity is important since it affects\n /// the way we calculate the underlying assets in check Balance. The underlying aerodrome pool\n /// orders the tokens depending on the values of their addresses. If OETH token is token0 in the pool\n /// then sqrtRatioX96TickClosestToParity=sqrtRatioX96TickLower. If it is token1 in the pool then\n /// sqrtRatioX96TickClosestToParity=sqrtRatioX96TickHigher\n uint160 public immutable sqrtRatioX96TickClosestToParity;\n\n /// @dev a threshold under which the contract no longer allows for the protocol to rebalance. Guarding\n /// against a strategist / guardian being taken over and with multiple transactions draining the\n /// protocol funds.\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth); // 0x989e5ca8\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth); // 0xa6737d87\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n ); // 0x3681e8e0\n error OutsideExpectedTickRange(int24 currentTick); // 0x5a2eba75\n\n event PoolRebalanced(uint256 currentPoolWethShare);\n\n event PoolWethShareIntervalUpdated(\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n\n event LiquidityRemoved(\n uint256 withdrawLiquidityShare,\n uint256 removedWETHAmount,\n uint256 removedOETHbAmount,\n uint256 wethAmountCollected,\n uint256 oethbAmountCollected,\n uint256 underlyingAssets\n );\n\n event LiquidityAdded(\n uint256 wethAmountDesired,\n uint256 oethbAmountDesired,\n uint256 wethAmountSupplied,\n uint256 oethbAmountSupplied,\n uint256 tokenId,\n uint256 underlyingAssets\n );\n\n event UnderlyingAssetsUpdated(uint256 underlyingAssets);\n\n /**\n * @dev Un-stakes the token from the gauge for the execution duration of\n * the function and after that re-stakes it back in.\n *\n * It is important that the token is unstaked and owned by the strategy contract\n * during any liquidity altering operations and that it is re-staked back into the\n * gauge after liquidity changes. If the token fails to re-stake back to the\n * gauge it is not earning incentives.\n */\n // all functions using this modifier are used by functions with reentrancy check\n // slither-disable-start reentrancy-no-eth\n modifier gaugeUnstakeAndRestake() {\n // because of solidity short-circuit _isLpTokenStakedInGauge doesn't get called\n // when tokenId == 0\n if (tokenId != 0 && _isLpTokenStakedInGauge()) {\n clGauge.withdraw(tokenId);\n }\n _;\n // because of solidity short-circuit _isLpTokenStakedInGauge doesn't get called\n // when tokenId == 0\n if (tokenId != 0 && !_isLpTokenStakedInGauge()) {\n /**\n * It can happen that a withdrawal (or a full withdrawal) transactions would\n * remove all of the liquidity from the token with a NFT token still existing.\n * In that case the token can not be staked into the gauge, as some liquidity\n * needs to be added to it first.\n */\n if (_getLiquidity() > 0) {\n // if token liquidity changes the positionManager requires re-approval.\n // to any contract pre-approved to handle the token.\n positionManager.approve(address(clGauge), tokenId);\n clGauge.deposit(tokenId);\n }\n }\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice the constructor\n /// @dev This contract is intended to be used as a proxy. To prevent the\n /// potential confusion of having a functional implementation contract\n /// the constructor has the `initializer` modifier. This way the\n /// `initialize` function can not be called on the implementation contract.\n /// For the same reason the implementation contract also has the governor\n /// set to a zero address.\n /// @param _stratConfig the basic strategy configuration\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _oethbAddress Address of the Erc20 OETHb Token contract\n /// @param _swapRouter Address of the Aerodrome Universal Swap Router\n /// @param _nonfungiblePositionManager Address of position manager to add/remove\n /// the liquidity\n /// @param _clPool Address of the Aerodrome concentrated liquidity pool\n /// @param _clGauge Address of the Aerodrome slipstream pool gauge\n /// @param _sugarHelper Address of the Aerodrome Sugar helper contract\n /// @param _lowerBoundingTick Smaller bounding tick of our liquidity position\n /// @param _upperBoundingTick Larger bounding tick of our liquidity position\n /// @param _tickClosestToParity Tick that is closer to 1:1 price parity\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethbAddress,\n address _swapRouter,\n address _nonfungiblePositionManager,\n address _clPool,\n address _clGauge,\n address _sugarHelper,\n int24 _lowerBoundingTick,\n int24 _upperBoundingTick,\n int24 _tickClosestToParity\n ) initializer InitializableAbstractStrategy(_stratConfig) {\n require(\n _lowerBoundingTick == _tickClosestToParity ||\n _upperBoundingTick == _tickClosestToParity,\n \"Misconfigured tickClosestToParity\"\n );\n require(\n ICLPool(_clPool).token0() == _wethAddress,\n \"Only WETH supported as token0\"\n );\n require(\n ICLPool(_clPool).token1() == _oethbAddress,\n \"Only OETHb supported as token1\"\n );\n int24 _tickSpacing = ICLPool(_clPool).tickSpacing();\n // when we generalize AMO we might support other tick spacings\n require(_tickSpacing == 1, \"Unsupported tickSpacing\");\n\n WETH = _wethAddress;\n OETHb = _oethbAddress;\n swapRouter = ISwapRouter(_swapRouter);\n positionManager = INonfungiblePositionManager(\n _nonfungiblePositionManager\n );\n clPool = ICLPool(_clPool);\n clGauge = ICLGauge(_clGauge);\n helper = ISugarHelper(_sugarHelper);\n sqrtRatioX96TickLower = ISugarHelper(_sugarHelper).getSqrtRatioAtTick(\n _lowerBoundingTick\n );\n sqrtRatioX96TickHigher = ISugarHelper(_sugarHelper).getSqrtRatioAtTick(\n _upperBoundingTick\n );\n sqrtRatioX96TickClosestToParity = ISugarHelper(_sugarHelper)\n .getSqrtRatioAtTick(_tickClosestToParity);\n\n lowerTick = _lowerBoundingTick;\n upperTick = _upperBoundingTick;\n tickSpacing = _tickSpacing;\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /**\n * @notice initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n */\n function initialize(address[] memory _rewardTokenAddresses)\n external\n onlyGovernor\n initializer\n {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n new address[](0),\n new address[](0)\n );\n }\n\n /***************************************\n Configuration \n ****************************************/\n\n /**\n * @notice Set allowed pool weth share interval. After the rebalance happens\n * the share of WETH token in the ticker needs to be withing the specifications\n * of the interval.\n *\n * @param _allowedWethShareStart Start of WETH share interval expressed as 18 decimal amount\n * @param _allowedWethShareEnd End of WETH share interval expressed as 18 decimal amount\n */\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external onlyGovernor {\n require(\n _allowedWethShareStart < _allowedWethShareEnd,\n \"Invalid interval\"\n );\n // can not go below 1% weth share\n require(_allowedWethShareStart > 0.01 ether, \"Invalid interval start\");\n // can not go above 95% weth share\n require(_allowedWethShareEnd < 0.95 ether, \"Invalid interval end\");\n\n allowedWethShareStart = _allowedWethShareStart;\n allowedWethShareEnd = _allowedWethShareEnd;\n emit PoolWethShareIntervalUpdated(\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n\n /***************************************\n Periphery utils\n ****************************************/\n\n function _isLpTokenStakedInGauge() internal view returns (bool) {\n require(tokenId != 0, \"Missing NFT LP token\");\n\n address owner = positionManager.ownerOf(tokenId);\n require(\n owner == address(clGauge) || owner == address(this),\n \"Unexpected token owner\"\n );\n return owner == address(clGauge);\n }\n\n /***************************************\n Strategy overrides \n ****************************************/\n\n /**\n * @notice Deposit an amount of assets into the strategy contract. Calling deposit doesn't\n * automatically deposit funds into the underlying Aerodrome pool\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposit WETH to the strategy contract. This function does not add liquidity to the\n * underlying Aerodrome pool.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance > 1e12) {\n _deposit(WETH, _wethBalance);\n }\n }\n\n /**\n * @dev Deposit WETH to the contract. This function doesn't deposit the liquidity to the\n * pool, that is done via the rebalance call.\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n emit Deposit(_asset, address(0), _amount);\n\n // if the pool price is not within the expected interval leave the WETH on the contract\n // as to not break the mints\n (bool _isExpectedRange, ) = _checkForExpectedPoolPrice(false);\n if (_isExpectedRange) {\n // deposit funds into the underlying pool\n _rebalance(0, false, 0);\n }\n }\n\n /**\n * @notice Rebalance the pool to the desired token split and Deposit any WETH on the contract to the\n * underlying aerodrome pool. Print the required amount of corresponding OETHb. After the rebalancing is\n * done burn any potentially remaining OETHb tokens still on the strategy contract.\n *\n * This function has a slightly different behaviour depending on the status of the underlying Aerodrome\n * slipstream pool. The function consists of the following 3 steps:\n * 1. withdrawPartialLiquidity -> so that moving the activeTrading price via a swap is cheaper\n * 2. swapToDesiredPosition -> move active trading price in the pool to be able to deposit WETH & OETHb\n * tokens with the desired pre-configured shares\n * 3. addLiquidity -> add liquidity into the pool respecting share split configuration\n *\n * Scenario 1: When there is no liquidity in the pool from the strategy but there is from other LPs then\n * only step 1 is skipped. (It is important to note that liquidity needs to exist in the configured\n * strategy tick ranges in order for the swap to be possible) Step 3 mints new liquidity position\n * instead of adding to an existing one.\n * Scenario 2: When there is strategy's liquidity in the pool all 3 steps are taken\n *\n *\n * Exact _amountToSwap, _swapWeth & _minTokenReceived parameters shall be determined by simulating the\n * transaction off-chain. The strategy checks that after the swap the share of the tokens is in the\n * expected ranges.\n *\n * @param _amountToSwap The amount of the token to swap\n * @param _swapWeth Swap using WETH when true, use OETHb when false\n * @param _minTokenReceived Slippage check -> minimum amount of token expected in return\n */\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) external nonReentrant onlyGovernorOrStrategist {\n _rebalance(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n\n function _rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n /**\n * Would be nice to check if there is any total liquidity in the pool before performing this swap\n * but there is no easy way to do that in UniswapV3:\n * - clPool.liquidity() -> only liquidity in the active tick\n * - asset[1&2].balanceOf(address(clPool)) -> will include uncollected tokens of LP providers\n * after their liquidity position has been decreased\n */\n\n /**\n * When rebalance is called for the first time there is no strategy\n * liquidity in the pool yet. The liquidity removal is thus skipped.\n * Also execute this function when WETH is required for the swap.\n */\n if (tokenId != 0 && _swapWeth && _amountToSwap > 0) {\n _ensureWETHBalance(_amountToSwap);\n }\n\n // in some cases we will just want to add liquidity and not issue a swap to move the\n // active trading position within the pool\n if (_amountToSwap > 0) {\n _swapToDesiredPosition(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n // calling check liquidity early so we don't get unexpected errors when adding liquidity\n // in the later stages of this function\n _checkForExpectedPoolPrice(true);\n\n _addLiquidity();\n\n // this call shouldn't be necessary, since adding liquidity shouldn't affect the active\n // trading price. It is a defensive programming measure.\n (, uint256 _wethSharePct) = _checkForExpectedPoolPrice(true);\n\n // revert if protocol insolvent\n _solvencyAssert();\n\n emit PoolRebalanced(_wethSharePct);\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethbSupply = IERC20(OETHb).totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethbSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /**\n * @dev Decrease partial or all liquidity from the pool.\n * @param _liquidityToDecrease The amount of liquidity to remove expressed in 18 decimal point\n */\n function _removeLiquidity(uint256 _liquidityToDecrease)\n internal\n gaugeUnstakeAndRestake\n {\n require(_liquidityToDecrease > 0, \"Must remove some liquidity\");\n\n uint128 _liquidity = _getLiquidity();\n // need to convert to uint256 since intermittent result is to big for uint128 to handle\n uint128 _liquidityToRemove = uint256(_liquidity)\n .mulTruncate(_liquidityToDecrease)\n .toUint128();\n\n /**\n * There is no liquidity to remove -> exit function early. This can happen after a\n * withdraw/withdrawAll removes all of the liquidity while retaining the NFT token.\n */\n if (_liquidity == 0 || _liquidityToRemove == 0) {\n return;\n }\n\n (uint256 _amountWeth, uint256 _amountOethb) = positionManager\n .decreaseLiquidity(\n // Both expected amounts can be 0 since we don't really care if any swaps\n // happen just before the liquidity removal.\n INonfungiblePositionManager.DecreaseLiquidityParams({\n tokenId: tokenId,\n liquidity: _liquidityToRemove,\n amount0Min: 0,\n amount1Min: 0,\n deadline: block.timestamp\n })\n );\n\n (\n uint256 _amountWethCollected,\n uint256 _amountOethbCollected\n ) = positionManager.collect(\n INonfungiblePositionManager.CollectParams({\n tokenId: tokenId,\n recipient: address(this),\n amount0Max: type(uint128).max, // defaults to all tokens owed\n amount1Max: type(uint128).max // defaults to all tokens owed\n })\n );\n\n _updateUnderlyingAssets();\n\n emit LiquidityRemoved(\n _liquidityToDecrease,\n _amountWeth, //removedWethAmount\n _amountOethb, //removedOethbAmount\n _amountWethCollected,\n _amountOethbCollected,\n underlyingAssets\n );\n\n _burnOethbOnTheContract();\n }\n\n /**\n * @dev Perform a swap so that after the swap the ticker has the desired WETH to OETHb token share.\n */\n function _swapToDesiredPosition(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n IERC20 _tokenToSwap = IERC20(_swapWeth ? WETH : OETHb);\n uint256 _balance = _tokenToSwap.balanceOf(address(this));\n\n if (_balance < _amountToSwap) {\n // This should never trigger since _ensureWETHBalance will already\n // throw an error if there is not enough WETH\n if (_swapWeth) {\n revert NotEnoughWethForSwap(_balance, _amountToSwap);\n }\n // if swapping OETHb\n uint256 mintForSwap = _amountToSwap - _balance;\n IVault(vaultAddress).mintForStrategy(mintForSwap);\n }\n\n // approve the specific amount of WETH required\n if (_swapWeth) {\n IERC20(WETH).approve(address(swapRouter), _amountToSwap);\n }\n\n // Swap it\n swapRouter.exactInputSingle(\n // sqrtPriceLimitX96 is just a rough sanity check that we are within 0 -> 1 tick\n // a more fine check is performed in _checkForExpectedPoolPrice\n // Note: this needs further work if we want to generalize this approach\n ISwapRouter.ExactInputSingleParams({\n tokenIn: address(_tokenToSwap),\n tokenOut: _swapWeth ? OETHb : WETH,\n tickSpacing: tickSpacing, // set to 1\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: _amountToSwap,\n amountOutMinimum: _minTokenReceived, // slippage check\n sqrtPriceLimitX96: _swapWeth\n ? sqrtRatioX96TickLower\n : sqrtRatioX96TickHigher\n })\n );\n\n /**\n * In the interest of each function in _rebalance to leave the contract state as\n * clean as possible the OETHb tokens here are burned. This decreases the\n * dependence where `_swapToDesiredPosition` function relies on later functions\n * (`addLiquidity`) to burn the OETHb. Reducing the risk of error introduction.\n */\n _burnOethbOnTheContract();\n }\n\n /**\n * @dev Add liquidity into the pool in the pre-configured WETH to OETHb share ratios\n * defined by the allowedPoolWethShareStart|End interval. This function will respect\n * liquidity ratios when there is no liquidity yet in the pool. If liquidity is already\n * present then it relies on the `_swapToDesiredPosition` function in a step before\n * to already move the trading price to desired position (with some tolerance).\n */\n // rebalance already has re-entrency checks\n // slither-disable-start reentrancy-no-eth\n function _addLiquidity() internal gaugeUnstakeAndRestake {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n // don't deposit small liquidity amounts\n if (_wethBalance <= 1e12) {\n return;\n }\n\n uint160 _currentPrice = getPoolX96Price();\n /**\n * Sanity check active trading price is positioned within our desired tick.\n *\n * We revert when price is equal to the lower tick even though that is still\n * a valid amount in regards to ticker position by Sugar.estimateAmount call.\n * Current price equaling tick bound at the 1:1 price parity results in\n * uint overfow when calculating the OETHb balance to deposit.\n */\n if (\n _currentPrice <= sqrtRatioX96TickLower ||\n _currentPrice >= sqrtRatioX96TickHigher\n ) {\n revert OutsideExpectedTickRange(getCurrentTradingTick());\n }\n\n /**\n * If estimateAmount1 call fails it could be due to _currentPrice being really\n * close to a tick and amount1 is a larger number than the sugar helper is able\n * to compute.\n *\n * If token addresses were reversed estimateAmount0 would be required here\n */\n uint256 _oethbRequired = helper.estimateAmount1(\n _wethBalance,\n address(0), // no need to pass pool address when current price is specified\n _currentPrice,\n lowerTick,\n upperTick\n );\n\n if (_oethbRequired > _oethbBalance) {\n IVault(vaultAddress).mintForStrategy(\n _oethbRequired - _oethbBalance\n );\n }\n\n // approve the specific amount of WETH required\n IERC20(WETH).approve(address(positionManager), _wethBalance);\n\n uint256 _wethAmountSupplied;\n uint256 _oethbAmountSupplied;\n if (tokenId == 0) {\n (\n tokenId,\n ,\n _wethAmountSupplied,\n _oethbAmountSupplied\n ) = positionManager.mint(\n /** amount0Min & amount1Min are left at 0 because slippage protection is ensured by the\n * _checkForExpectedPoolPrice\n *›\n * Also sqrtPriceX96 is 0 because the pool is already created\n * non zero amount attempts to create a new instance of the pool\n */\n INonfungiblePositionManager.MintParams({\n token0: WETH,\n token1: OETHb,\n tickSpacing: tickSpacing,\n tickLower: lowerTick,\n tickUpper: upperTick,\n amount0Desired: _wethBalance,\n amount1Desired: _oethbRequired,\n amount0Min: 0,\n amount1Min: 0,\n recipient: address(this),\n deadline: block.timestamp,\n sqrtPriceX96: 0\n })\n );\n } else {\n (, _wethAmountSupplied, _oethbAmountSupplied) = positionManager\n .increaseLiquidity(\n /** amount0Min & amount1Min are left at 0 because slippage protection is ensured by the\n * _checkForExpectedPoolPrice\n */\n INonfungiblePositionManager.IncreaseLiquidityParams({\n tokenId: tokenId,\n amount0Desired: _wethBalance,\n amount1Desired: _oethbRequired,\n amount0Min: 0,\n amount1Min: 0,\n deadline: block.timestamp\n })\n );\n }\n\n _updateUnderlyingAssets();\n emit LiquidityAdded(\n _wethBalance, // wethAmountDesired\n _oethbRequired, // oethbAmountDesired\n _wethAmountSupplied, // wethAmountSupplied\n _oethbAmountSupplied, // oethbAmountSupplied\n tokenId, // tokenId\n underlyingAssets\n );\n\n // burn remaining OETHb\n _burnOethbOnTheContract();\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @dev Check that the Aerodrome pool price is within the expected\n * parameters.\n * This function works whether the strategy contract has liquidity\n * position in the pool or not. The function returns _wethSharePct\n * as a gas optimization measure.\n * @param throwException when set to true the function throws an exception\n * when pool's price is not within expected range.\n * @return _isExpectedRange Bool expressing price is within expected range\n * @return _wethSharePct Share of WETH owned by this strategy contract in the\n * configured ticker.\n */\n function _checkForExpectedPoolPrice(bool throwException)\n internal\n view\n returns (bool _isExpectedRange, uint256 _wethSharePct)\n {\n require(\n allowedWethShareStart != 0 && allowedWethShareEnd != 0,\n \"Weth share interval not set\"\n );\n\n uint160 _currentPrice = getPoolX96Price();\n\n /**\n * First check we are in expected tick range\n *\n * We revert even though price being equal to the lower tick would still\n * count being within lower tick for the purpose of Sugar.estimateAmount calls\n */\n if (\n _currentPrice <= sqrtRatioX96TickLower ||\n _currentPrice >= sqrtRatioX96TickHigher\n ) {\n if (throwException) {\n revert OutsideExpectedTickRange(getCurrentTradingTick());\n }\n return (false, 0);\n }\n\n // 18 decimal number expressed WETH tick share\n _wethSharePct = _getWethShare(_currentPrice);\n\n if (\n _wethSharePct < allowedWethShareStart ||\n _wethSharePct > allowedWethShareEnd\n ) {\n if (throwException) {\n revert PoolRebalanceOutOfBounds(\n _wethSharePct,\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n return (false, _wethSharePct);\n }\n\n return (true, _wethSharePct);\n }\n\n /**\n * Burns any OETHb tokens remaining on the strategy contract\n */\n function _burnOethbOnTheContract() internal {\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n if (_oethbBalance > 1e12) {\n IVault(vaultAddress).burnForStrategy(_oethbBalance);\n }\n }\n\n /// @dev This function assumes there are no uncollected tokens in the clPool owned by the strategy contract.\n /// For that reason any liquidity withdrawals must also collect the tokens.\n function _updateUnderlyingAssets() internal {\n if (tokenId == 0) {\n underlyingAssets = 0;\n emit UnderlyingAssetsUpdated(underlyingAssets);\n return;\n }\n\n uint128 _liquidity = _getLiquidity();\n\n /**\n * Our net value represent the smallest amount of tokens we are able to extract from the position\n * given our liquidity.\n *\n * The least amount of tokens extraditable from the position is where the active trading price is\n * at the ticker 0 meaning the pool is offering 1:1 trades between WETH & OETHb. At that moment the pool\n * consists completely of OETHb and no WETH.\n *\n * The more swaps from WETH -> OETHb happen on the pool the more the price starts to move towards the -1\n * ticker making OETHb (priced in WETH) more expensive.\n *\n * An additional note: when liquidity is 0 then the helper returns 0 for both token amounts. And the\n * function set underlying assets to 0.\n */\n (uint256 _wethAmount, uint256 _oethbAmount) = helper\n .getAmountsForLiquidity(\n sqrtRatioX96TickClosestToParity, // sqrtRatioX96\n sqrtRatioX96TickLower, // sqrtRatioAX96\n sqrtRatioX96TickHigher, // sqrtRatioBX96\n _liquidity\n );\n\n require(_wethAmount == 0, \"Non zero wethAmount\");\n underlyingAssets = _oethbAmount;\n emit UnderlyingAssetsUpdated(underlyingAssets);\n }\n\n /**\n * @dev This function removes the appropriate amount of liquidity to assure that the required\n * amount of WETH is available on the contract\n *\n * @param _amount WETH balance required on the contract\n */\n function _ensureWETHBalance(uint256 _amount) internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance >= _amount) {\n return;\n }\n\n require(tokenId != 0, \"No liquidity available\");\n uint256 _additionalWethRequired = _amount - _wethBalance;\n (uint256 _wethInThePool, ) = getPositionPrincipal();\n\n if (_wethInThePool < _additionalWethRequired) {\n revert NotEnoughWethLiquidity(\n _wethInThePool,\n _additionalWethRequired\n );\n }\n\n uint256 shareOfWethToRemove = Math.min(\n _additionalWethRequired.divPrecisely(_wethInThePool) + 1,\n 1e18\n );\n _removeLiquidity(shareOfWethToRemove);\n }\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset WETH address\n * @param _amount Amount of WETH to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n _ensureWETHBalance(_amount);\n\n _withdraw(_recipient, _amount);\n }\n\n /**\n * @notice Withdraw WETH and sends it to the Vault.\n */\n function withdrawAll() external override onlyVault nonReentrant {\n if (tokenId != 0) {\n _removeLiquidity(1e18);\n }\n\n uint256 _balance = IERC20(WETH).balanceOf(address(this));\n if (_balance > 0) {\n _withdraw(vaultAddress, _balance);\n }\n }\n\n function _withdraw(address _recipient, uint256 _amount) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n IERC20(WETH).safeTransfer(_recipient, _amount);\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /**\n * @dev Collect the AERO token from the gauge\n */\n function _collectRewardTokens() internal override {\n if (tokenId != 0 && _isLpTokenStakedInGauge()) {\n clGauge.getReward(tokenId);\n }\n super._collectRewardTokens();\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /**\n * @dev Approve the spending of all assets\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n // to add liquidity to the clPool\n IERC20(OETHb).approve(address(positionManager), type(uint256).max);\n // to be able to rebalance using the swapRouter\n IERC20(OETHb).approve(address(swapRouter), type(uint256).max);\n\n /* the behaviour of this strategy has slightly changed and WETH could be\n * present on the contract between the transactions. For that reason we are\n * un-approving WETH to the swapRouter & positionManager and only approving\n * the required amount before a transaction\n */\n IERC20(WETH).approve(address(swapRouter), 0);\n IERC20(WETH).approve(address(positionManager), 0);\n }\n\n /***************************************\n Balances and Fees\n ****************************************/\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256)\n {\n require(_asset == WETH, \"Only WETH supported\");\n\n // we could in theory deposit to the strategy and forget to call rebalance in the same\n // governance transaction batch. In that case the WETH that is on the strategy contract\n // also needs to be accounted for.\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n // just paranoia check, in case there is OETHb in the strategy that for some reason hasn't\n // been burned yet.\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n return underlyingAssets + _wethBalance + _oethbBalance;\n }\n\n /**\n * @dev Returns the balance of both tokens in a given position (excluding fees)\n * @return _amountWeth Amount of WETH in position\n * @return _amountOethb Amount of OETHb in position\n */\n function getPositionPrincipal()\n public\n view\n returns (uint256 _amountWeth, uint256 _amountOethb)\n {\n if (tokenId == 0) {\n return (0, 0);\n }\n\n uint160 _sqrtRatioX96 = getPoolX96Price();\n (_amountWeth, _amountOethb) = helper.principal(\n positionManager,\n tokenId,\n _sqrtRatioX96\n );\n }\n\n /**\n * @notice Returns the current pool price in X96 format\n * @return _sqrtRatioX96 Pool price\n */\n function getPoolX96Price() public view returns (uint160 _sqrtRatioX96) {\n (_sqrtRatioX96, , , , , ) = clPool.slot0();\n }\n\n /**\n * @notice Returns the current active trading tick of the underlying pool\n * @return _currentTick Current pool trading tick\n */\n function getCurrentTradingTick() public view returns (int24 _currentTick) {\n (, _currentTick, , , , ) = clPool.slot0();\n }\n\n /**\n * @notice Returns the percentage of WETH liquidity in the configured ticker\n * owned by this strategy contract.\n * @return uint256 1e18 denominated percentage expressing the share\n */\n function getWETHShare() external view returns (uint256) {\n uint160 _currentPrice = getPoolX96Price();\n return _getWethShare(_currentPrice);\n }\n\n /**\n * @notice Returns the amount of liquidity in the contract's LP position\n * @return _liquidity Amount of liquidity in the position\n */\n function _getLiquidity() internal view returns (uint128 _liquidity) {\n if (tokenId == 0) {\n revert(\"No LP position\");\n }\n\n (, , , , , , , _liquidity, , , , ) = positionManager.positions(tokenId);\n }\n\n function _getWethShare(uint160 _currentPrice)\n internal\n view\n returns (uint256)\n {\n /**\n * If estimateAmount1 call fails it could be due to _currentPrice being really\n * close to a tick and amount1 too big to compute.\n *\n * If token addresses were reversed estimateAmount0 would be required here\n */\n uint256 _normalizedWethAmount = 1 ether;\n uint256 _correspondingOethAmount = helper.estimateAmount1(\n _normalizedWethAmount,\n address(0), // no need to pass pool address when current price is specified\n _currentPrice,\n lowerTick,\n upperTick\n );\n\n // 18 decimal number expressed weth tick share\n return\n _normalizedWethAmount.divPrecisely(\n _normalizedWethAmount + _correspondingOethAmount\n );\n }\n\n /***************************************\n Hidden functions\n ****************************************/\n /// @inheritdoc InitializableAbstractStrategy\n function setPTokenAddress(address, address) external override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /// @inheritdoc InitializableAbstractStrategy\n function removePToken(uint256) external override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Not supported\n */\n function _abstractSetPToken(address, address) internal override {\n // the deployer shall call safeApproveAllTokens() to set necessary approvals\n revert(\"Unsupported method\");\n }\n\n /***************************************\n ERC721 management\n ****************************************/\n\n /// @notice Callback function for whenever a NFT is transferred to this contract\n // solhint-disable-next-line max-line-length\n /// Ref: https://docs.openzeppelin.com/contracts/3.x/api/token/erc721#IERC721Receiver-onERC721Received-address-address-uint256-bytes-\n function onERC721Received(\n address,\n address,\n uint256,\n bytes calldata\n ) external returns (bytes4) {\n return this.onERC721Received.selector;\n }\n}\n" + }, + "contracts/strategies/balancer/AbstractAuraStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Base Balancer Abstract Strategy\n * @author Origin Protocol Inc\n */\n\nimport { AbstractBalancerStrategy } from \"./AbstractBalancerStrategy.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IERC4626 } from \"../../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport { IRewardStaking } from \"../IRewardStaking.sol\";\n\nabstract contract AbstractAuraStrategy is AbstractBalancerStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n /// @notice Address of the Aura rewards pool\n address public immutable auraRewardPoolAddress;\n\n // renamed from __reserved to not shadow AbstractBalancerStrategy.__reserved,\n int256[50] private __reserved_baseAuraStrategy;\n\n constructor(address _auraRewardPoolAddress) {\n auraRewardPoolAddress = _auraRewardPoolAddress;\n }\n\n /**\n * @dev Deposit all Balancer Pool Tokens (BPT) in this strategy contract\n * to the Aura rewards pool.\n */\n function _lpDepositAll() internal virtual override {\n uint256 bptBalance = IERC20(platformAddress).balanceOf(address(this));\n uint256 auraLp = IERC4626(auraRewardPoolAddress).deposit(\n bptBalance,\n address(this)\n );\n require(bptBalance == auraLp, \"Aura LP != BPT\");\n }\n\n /**\n * @dev Withdraw `numBPTTokens` Balancer Pool Tokens (BPT) from\n * the Aura rewards pool to this strategy contract.\n * @param numBPTTokens Number of Balancer Pool Tokens (BPT) to withdraw\n */\n function _lpWithdraw(uint256 numBPTTokens) internal virtual override {\n IRewardStaking(auraRewardPoolAddress).withdrawAndUnwrap(\n numBPTTokens,\n true // also claim reward tokens\n );\n }\n\n /**\n * @dev Withdraw all Balancer Pool Tokens (BPT) from\n * the Aura rewards pool to this strategy contract.\n */\n function _lpWithdrawAll() internal virtual override {\n // Get all the strategy's BPTs in Aura\n // maxRedeem is implemented as balanceOf(address) in Aura\n uint256 bptBalance = IERC4626(auraRewardPoolAddress).maxRedeem(\n address(this)\n );\n\n IRewardStaking(auraRewardPoolAddress).withdrawAndUnwrap(\n bptBalance,\n true // also claim reward tokens\n );\n }\n\n /**\n * @notice Collects BAL and AURA tokens from the rewards pool.\n */\n function collectRewardTokens()\n external\n virtual\n override\n onlyHarvester\n nonReentrant\n {\n /* Similar to Convex, calling this function collects both of the\n * accrued BAL and AURA tokens.\n */\n IRewardStaking(auraRewardPoolAddress).getReward();\n _collectRewardTokens();\n }\n\n /// @notice Balancer Pool Tokens (BPT) in the Balancer pool and the Aura rewards pool.\n function _getBalancerPoolTokens()\n internal\n view\n override\n returns (uint256 balancerPoolTokens)\n {\n balancerPoolTokens =\n IERC20(platformAddress).balanceOf(address(this)) +\n // maxRedeem is implemented as balanceOf(address) in Aura\n IERC4626(auraRewardPoolAddress).maxRedeem(address(this));\n }\n\n function _approveBase() internal virtual override {\n super._approveBase();\n\n IERC20 pToken = IERC20(platformAddress);\n pToken.safeApprove(auraRewardPoolAddress, type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/balancer/AbstractBalancerStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Balancer Abstract Strategy\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\nimport { IRateProvider } from \"../../interfaces/balancer/IRateProvider.sol\";\nimport { VaultReentrancyLib } from \"./VaultReentrancyLib.sol\";\nimport { IOracle } from \"../../interfaces/IOracle.sol\";\nimport { IWstETH } from \"../../interfaces/IWstETH.sol\";\nimport { IERC4626 } from \"../../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nabstract contract AbstractBalancerStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n address public immutable rETH;\n address public immutable stETH;\n address public immutable wstETH;\n address public immutable frxETH;\n address public immutable sfrxETH;\n\n /// @notice Address of the Balancer vault\n IBalancerVault public immutable balancerVault;\n /// @notice Balancer pool identifier\n bytes32 public immutable balancerPoolId;\n\n // Max withdrawal deviation denominated in 1e18 (1e18 == 100%)\n uint256 public maxWithdrawalDeviation;\n // Max deposit deviation denominated in 1e18 (1e18 == 100%)\n uint256 public maxDepositDeviation;\n\n int256[48] private __reserved;\n\n struct BaseBalancerConfig {\n address rEthAddress; // Address of the rETH token\n address stEthAddress; // Address of the stETH token\n address wstEthAddress; // Address of the wstETH token\n address frxEthAddress; // Address of the frxEth token\n address sfrxEthAddress; // Address of the sfrxEth token\n address balancerVaultAddress; // Address of the Balancer vault\n bytes32 balancerPoolId; // Balancer pool identifier\n }\n\n event MaxWithdrawalDeviationUpdated(\n uint256 _prevMaxDeviationPercentage,\n uint256 _newMaxDeviationPercentage\n );\n event MaxDepositDeviationUpdated(\n uint256 _prevMaxDeviationPercentage,\n uint256 _newMaxDeviationPercentage\n );\n\n /**\n * @dev Ensure we are not in a Vault context when this function is called, by attempting a no-op internal\n * balance operation. If we are already in a Vault transaction (e.g., a swap, join, or exit), the Vault's\n * reentrancy protection will cause this function to revert.\n *\n * Use this modifier with any function that can cause a state change in a pool and is either public itself,\n * or called by a public function *outside* a Vault operation (e.g., join, exit, or swap).\n *\n * This is to protect against Balancer's read-only re-entrancy vulnerability:\n * https://www.notion.so/originprotocol/Balancer-read-only-reentrancy-c686e72c82414ef18fa34312bb02e11b\n */\n modifier whenNotInBalancerVaultContext() {\n VaultReentrancyLib.ensureNotInVaultContext(balancerVault);\n _;\n }\n\n constructor(BaseBalancerConfig memory _balancerConfig) {\n rETH = _balancerConfig.rEthAddress;\n stETH = _balancerConfig.stEthAddress;\n wstETH = _balancerConfig.wstEthAddress;\n frxETH = _balancerConfig.frxEthAddress;\n sfrxETH = _balancerConfig.sfrxEthAddress;\n\n balancerVault = IBalancerVault(_balancerConfig.balancerVaultAddress);\n balancerPoolId = _balancerConfig.balancerPoolId;\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Balancer's strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of BAL & AURA\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * WETH, stETH\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // BAL & AURA\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n maxWithdrawalDeviation = 1e16;\n maxDepositDeviation = 1e16;\n\n emit MaxWithdrawalDeviationUpdated(0, maxWithdrawalDeviation);\n emit MaxDepositDeviationUpdated(0, maxDepositDeviation);\n\n IERC20[] memory poolAssets = _getPoolAssets();\n require(\n poolAssets.length == _assets.length,\n \"Pool assets length mismatch\"\n );\n for (uint256 i = 0; i < _assets.length; ++i) {\n address asset = _fromPoolAsset(address(poolAssets[i]));\n require(_assets[i] == asset, \"Pool assets mismatch\");\n }\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @notice Get strategy's share of an assets in the Balancer pool.\n * This is not denominated in OUSD/ETH value of the assets in the Balancer pool.\n * @param _asset Address of the Vault collateral asset\n * @return amount the amount of vault collateral assets\n *\n * IMPORTANT if this function is overridden it needs to have a whenNotInBalancerVaultContext\n * modifier on it or it is susceptible to read-only re-entrancy attack\n *\n * @dev it is important that this function is not affected by reporting inflated\n * values of assets in case of any pool manipulation. Such a manipulation could easily\n * exploit the protocol by:\n * - minting OETH\n * - tilting Balancer pool to report higher balances of assets\n * - rebasing() -> all that extra token balances get distributed to OETH holders\n * - tilting pool back\n * - redeeming OETH\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n whenNotInBalancerVaultContext\n returns (uint256 amount)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n\n uint256 bptBalance = _getBalancerPoolTokens();\n\n /* To calculate the worth of queried asset:\n * - assume that all tokens normalized to their ETH value have an equal split balance\n * in the pool when it is balanced\n * - multiply the BPT amount with the bpt rate to get the ETH denominated amount\n * of strategy's holdings\n * - divide that by the number of tokens we support in the pool to get ETH denominated\n * amount that is applicable to each supported token in the pool.\n *\n * It would be possible to support only 1 asset in the pool (and be exposed to all\n * the assets while holding BPT tokens) and deposit/withdraw/checkBalance using only\n * that asset. TBD: changes to other functions still required if we ever decide to\n * go with such configuration.\n */\n amount = (bptBalance.mulTruncate(\n IRateProvider(platformAddress).getRate()\n ) / assetsMapped.length);\n\n /* If the pool asset is equal to (strategy )_asset it means that a rate\n * provider for that asset exists and that asset is not necessarily\n * pegged to a unit (ETH).\n *\n * Because this function returns the balance of the asset and is not denominated in\n * ETH units we need to convert the ETH denominated amount to asset amount.\n */\n if (_toPoolAsset(_asset) == _asset) {\n amount = amount.divPrecisely(_getRateProviderRate(_asset));\n }\n }\n\n /**\n * @notice Returns the value of all assets managed by this strategy.\n * Uses the Balancer pool's rate (virtual price) to convert the strategy's\n * Balancer Pool Tokens (BPT) to ETH value.\n * @return value The ETH value\n *\n * IMPORTANT if this function is overridden it needs to have a whenNotInBalancerVaultContext\n * modifier on it or it is susceptible to read-only re-entrancy attack\n */\n function checkBalance()\n external\n view\n virtual\n whenNotInBalancerVaultContext\n returns (uint256 value)\n {\n uint256 bptBalance = _getBalancerPoolTokens();\n\n // Convert BPT to ETH value\n value = bptBalance.mulTruncate(\n IRateProvider(platformAddress).getRate()\n );\n }\n\n /// @notice Balancer Pool Tokens (BPT) in the Balancer pool.\n function _getBalancerPoolTokens()\n internal\n view\n virtual\n returns (uint256 balancerPoolTokens)\n {\n balancerPoolTokens = IERC20(platformAddress).balanceOf(address(this));\n }\n\n /* solhint-disable max-line-length */\n /**\n * @notice BPT price is calculated by taking the rate from the rateProvider of the asset in\n * question. If one does not exist it defaults to 1e18. To get the final BPT expected that\n * is multiplied by the underlying asset amount divided by BPT token rate. BPT token rate is\n * similar to Curve's virtual_price and expresses how much has the price of BPT appreciated\n * (e.g. due to swap fees) in relation to the underlying assets\n *\n * Using the above approach makes the strategy vulnerable to a possible MEV attack using\n * flash loan to manipulate the pool before a deposit/withdrawal since the function ignores\n * market values of the assets being priced in BPT.\n *\n * At the time of writing there is no safe on-chain approach to pricing BPT in a way that it\n * would make it invulnerable to MEV pool manipulation. See recent Balancer exploit:\n * https://www.notion.so/originprotocol/Balancer-OETH-strategy-9becdea132704e588782a919d7d471eb?pvs=4#1cf07de12fc64f1888072321e0644348\n *\n * To mitigate MEV possibilities during deposits and withdraws, the VaultValueChecker will use checkBalance before and after the move\n * to ensure the expected changes took place.\n *\n * @param _asset Address of the Balancer pool asset\n * @param _amount Amount of the Balancer pool asset\n * @return bptExpected of BPT expected in exchange for the asset\n *\n * @dev\n * bptAssetPrice = 1e18 (asset peg) * pool_asset_rate\n *\n * bptExpected = bptAssetPrice * asset_amount / BPT_token_rate\n *\n * bptExpected = 1e18 (asset peg) * pool_asset_rate * asset_amount / BPT_token_rate\n * bptExpected = asset_amount * pool_asset_rate / BPT_token_rate\n *\n * further information available here:\n * https://www.notion.so/originprotocol/Balancer-OETH-strategy-9becdea132704e588782a919d7d471eb?pvs=4#ce01495ae70346d8971f5dced809fb83\n */\n /* solhint-enable max-line-length */\n function _getBPTExpected(address _asset, uint256 _amount)\n internal\n view\n virtual\n returns (uint256 bptExpected)\n {\n uint256 bptRate = IRateProvider(platformAddress).getRate();\n uint256 poolAssetRate = _getRateProviderRate(_asset);\n bptExpected = _amount.mulTruncate(poolAssetRate).divPrecisely(bptRate);\n }\n\n function _getBPTExpected(\n address[] memory _assets,\n uint256[] memory _amounts\n ) internal view virtual returns (uint256 bptExpected) {\n require(_assets.length == _amounts.length, \"Assets & amounts mismatch\");\n\n for (uint256 i = 0; i < _assets.length; ++i) {\n uint256 poolAssetRate = _getRateProviderRate(_assets[i]);\n // convert asset amount to ETH amount\n bptExpected += _amounts[i].mulTruncate(poolAssetRate);\n }\n\n uint256 bptRate = IRateProvider(platformAddress).getRate();\n // Convert ETH amount to BPT amount\n bptExpected = bptExpected.divPrecisely(bptRate);\n }\n\n function _lpDepositAll() internal virtual;\n\n function _lpWithdraw(uint256 numBPTTokens) internal virtual;\n\n function _lpWithdrawAll() internal virtual;\n\n /**\n * @notice Balancer returns assets and rateProviders for corresponding assets ordered\n * by numerical order.\n */\n function _getPoolAssets() internal view returns (IERC20[] memory assets) {\n // slither-disable-next-line unused-return\n (assets, , ) = balancerVault.getPoolTokens(balancerPoolId);\n }\n\n /**\n * @dev If an asset is rebasing the Balancer pools have a wrapped versions of assets\n * that the strategy supports. This function converts the pool(wrapped) asset\n * and corresponding amount to strategy asset.\n */\n function _toPoolAsset(address asset, uint256 amount)\n internal\n view\n returns (address poolAsset, uint256 poolAmount)\n {\n if (asset == stETH) {\n poolAsset = wstETH;\n if (amount > 0) {\n poolAmount = IWstETH(wstETH).getWstETHByStETH(amount);\n }\n } else if (asset == frxETH) {\n poolAsset = sfrxETH;\n if (amount > 0) {\n poolAmount = IERC4626(sfrxETH).convertToShares(amount);\n }\n } else {\n poolAsset = asset;\n poolAmount = amount;\n }\n }\n\n /**\n * @dev Converts a Vault collateral asset to a Balancer pool asset.\n * stETH becomes wstETH, frxETH becomes sfrxETH and everything else stays the same.\n * @param asset Address of the Vault collateral asset.\n * @return Address of the Balancer pool asset.\n */\n function _toPoolAsset(address asset) internal view returns (address) {\n if (asset == stETH) {\n return wstETH;\n } else if (asset == frxETH) {\n return sfrxETH;\n }\n return asset;\n }\n\n /**\n * @dev Converts rebasing asset to its wrapped counterpart.\n */\n function _wrapPoolAsset(address asset, uint256 amount)\n internal\n returns (address wrappedAsset, uint256 wrappedAmount)\n {\n if (asset == stETH) {\n wrappedAsset = wstETH;\n if (amount > 0) {\n wrappedAmount = IWstETH(wstETH).wrap(amount);\n }\n } else if (asset == frxETH) {\n wrappedAsset = sfrxETH;\n if (amount > 0) {\n wrappedAmount = IERC4626(sfrxETH).deposit(\n amount,\n address(this)\n );\n }\n } else {\n wrappedAsset = asset;\n wrappedAmount = amount;\n }\n }\n\n /**\n * @dev Converts wrapped asset to its rebasing counterpart.\n */\n function _unwrapPoolAsset(address asset, uint256 amount)\n internal\n returns (uint256 unwrappedAmount)\n {\n if (asset == stETH) {\n unwrappedAmount = IWstETH(wstETH).unwrap(amount);\n } else if (asset == frxETH) {\n unwrappedAmount = IERC4626(sfrxETH).withdraw(\n amount,\n address(this),\n address(this)\n );\n } else {\n unwrappedAmount = amount;\n }\n }\n\n /**\n * @dev If an asset is rebasing the Balancer pools have a wrapped versions of assets\n * that the strategy supports. This function converts the rebasing strategy asset\n * and corresponding amount to wrapped(pool) asset.\n */\n function _fromPoolAsset(address poolAsset, uint256 poolAmount)\n internal\n view\n returns (address asset, uint256 amount)\n {\n if (poolAsset == wstETH) {\n asset = stETH;\n if (poolAmount > 0) {\n amount = IWstETH(wstETH).getStETHByWstETH(poolAmount);\n }\n } else if (poolAsset == sfrxETH) {\n asset = frxETH;\n if (poolAmount > 0) {\n amount = IERC4626(sfrxETH).convertToAssets(poolAmount);\n }\n } else {\n asset = poolAsset;\n amount = poolAmount;\n }\n }\n\n function _fromPoolAsset(address poolAsset)\n internal\n view\n returns (address asset)\n {\n if (poolAsset == wstETH) {\n asset = stETH;\n } else if (poolAsset == sfrxETH) {\n asset = frxETH;\n } else {\n asset = poolAsset;\n }\n }\n\n /**\n * @notice Sets max withdrawal deviation that is considered when removing\n * liquidity from Balancer pools.\n * @param _maxWithdrawalDeviation Max withdrawal deviation denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxWithdrawalDeviation will be 1% (1e16) for production\n * usage. Vault value checker in combination with checkBalance will\n * catch any unexpected manipulation.\n */\n function setMaxWithdrawalDeviation(uint256 _maxWithdrawalDeviation)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(\n _maxWithdrawalDeviation <= 1e18,\n \"Withdrawal dev. out of bounds\"\n );\n emit MaxWithdrawalDeviationUpdated(\n maxWithdrawalDeviation,\n _maxWithdrawalDeviation\n );\n maxWithdrawalDeviation = _maxWithdrawalDeviation;\n }\n\n /**\n * @notice Sets max deposit deviation that is considered when adding\n * liquidity to Balancer pools.\n * @param _maxDepositDeviation Max deposit deviation denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxDepositDeviation will default to 1% (1e16)\n * for production usage. Vault value checker in combination with\n * checkBalance will catch any unexpected manipulation.\n */\n function setMaxDepositDeviation(uint256 _maxDepositDeviation)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(_maxDepositDeviation <= 1e18, \"Deposit dev. out of bounds\");\n emit MaxDepositDeviationUpdated(\n maxDepositDeviation,\n _maxDepositDeviation\n );\n maxDepositDeviation = _maxDepositDeviation;\n }\n\n function _approveBase() internal virtual {\n IERC20 pToken = IERC20(platformAddress);\n // Balancer vault for BPT token (required for removing liquidity)\n pToken.safeApprove(address(balancerVault), type(uint256).max);\n }\n\n function _getRateProviderRate(address _asset)\n internal\n view\n virtual\n returns (uint256);\n}\n" + }, + "contracts/strategies/balancer/BalancerMetaPoolStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Balancer MetaStablePool Strategy\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { AbstractAuraStrategy, AbstractBalancerStrategy } from \"./AbstractAuraStrategy.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\nimport { IRateProvider } from \"../../interfaces/balancer/IRateProvider.sol\";\nimport { IMetaStablePool } from \"../../interfaces/balancer/IMetaStablePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\ncontract BalancerMetaPoolStrategy is AbstractAuraStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(\n BaseStrategyConfig memory _stratConfig,\n BaseBalancerConfig memory _balancerConfig,\n address _auraRewardPoolAddress\n )\n InitializableAbstractStrategy(_stratConfig)\n AbstractBalancerStrategy(_balancerConfig)\n AbstractAuraStrategy(_auraRewardPoolAddress)\n {}\n\n /**\n * @notice There are no plans to configure BalancerMetaPool as a default\n * asset strategy. For that reason there is no need to support this\n * functionality.\n */\n function deposit(address, uint256)\n external\n override\n onlyVault\n nonReentrant\n {\n revert(\"Not supported\");\n }\n\n /**\n * @notice There are no plans to configure BalancerMetaPool as a default\n * asset strategy. For that reason there is no need to support this\n * functionality.\n */\n function deposit(address[] calldata, uint256[] calldata)\n external\n onlyVault\n nonReentrant\n {\n revert(\"Not supported\");\n }\n\n /**\n * @notice Deposits all supported assets in this strategy contract to the Balancer pool.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 assetsLength = assetsMapped.length;\n address[] memory strategyAssets = new address[](assetsLength);\n uint256[] memory strategyAmounts = new uint256[](assetsLength);\n\n // For each vault collateral asset\n for (uint256 i = 0; i < assetsLength; ++i) {\n strategyAssets[i] = assetsMapped[i];\n // Get the asset balance in this strategy contract\n strategyAmounts[i] = IERC20(strategyAssets[i]).balanceOf(\n address(this)\n );\n }\n _deposit(strategyAssets, strategyAmounts);\n }\n\n /*\n * _deposit doesn't require a read-only re-entrancy protection since during the deposit\n * the function enters the Balancer Vault Context. If this function were called as part of\n * the attacking contract (while intercepting execution flow upon receiving ETH) the read-only\n * protection of the Balancer Vault would be triggered. Since the attacking contract would\n * already be in the Balancer Vault context and wouldn't be able to enter it again.\n */\n function _deposit(\n address[] memory _strategyAssets,\n uint256[] memory _strategyAmounts\n ) internal {\n require(\n _strategyAssets.length == _strategyAmounts.length,\n \"Array length missmatch\"\n );\n\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n\n uint256[] memory strategyAssetAmountsToPoolAssetAmounts = new uint256[](\n _strategyAssets.length\n );\n address[] memory strategyAssetsToPoolAssets = new address[](\n _strategyAssets.length\n );\n\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n address strategyAsset = _strategyAssets[i];\n uint256 strategyAmount = _strategyAmounts[i];\n\n require(\n assetToPToken[strategyAsset] != address(0),\n \"Unsupported asset\"\n );\n strategyAssetsToPoolAssets[i] = _toPoolAsset(strategyAsset);\n\n if (strategyAmount > 0) {\n emit Deposit(strategyAsset, platformAddress, strategyAmount);\n\n // wrap rebasing assets like stETH and frxETH to wstETH and sfrxETH\n (, strategyAssetAmountsToPoolAssetAmounts[i]) = _wrapPoolAsset(\n strategyAsset,\n strategyAmount\n );\n }\n }\n\n uint256[] memory amountsIn = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n for (uint256 i = 0; i < tokens.length; ++i) {\n // Convert IERC20 type to address\n poolAssets[i] = address(tokens[i]);\n\n // For each of the mapped assets\n for (uint256 j = 0; j < strategyAssetsToPoolAssets.length; ++j) {\n // If the pool asset is the same as the mapped asset\n if (poolAssets[i] == strategyAssetsToPoolAssets[j]) {\n amountsIn[i] = strategyAssetAmountsToPoolAssetAmounts[j];\n }\n }\n }\n\n uint256 minBPT = _getBPTExpected(\n strategyAssetsToPoolAssets,\n strategyAssetAmountsToPoolAssetAmounts\n );\n uint256 minBPTwDeviation = minBPT.mulTruncate(\n 1e18 - maxDepositDeviation\n );\n\n /* EXACT_TOKENS_IN_FOR_BPT_OUT:\n * User sends precise quantities of tokens, and receives an\n * estimated but unknown (computed at run time) quantity of BPT.\n *\n * ['uint256', 'uint256[]', 'uint256']\n * [EXACT_TOKENS_IN_FOR_BPT_OUT, amountsIn, minimumBPT]\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,\n amountsIn,\n minBPTwDeviation\n );\n\n IBalancerVault.JoinPoolRequest memory request = IBalancerVault\n .JoinPoolRequest(poolAssets, amountsIn, userData, false);\n\n // Add the pool assets in this strategy to the balancer pool\n balancerVault.joinPool(\n balancerPoolId,\n address(this),\n address(this),\n request\n );\n\n // Deposit the Balancer Pool Tokens (BPT) into Aura\n _lpDepositAll();\n }\n\n /**\n * @notice Withdraw a Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAsset Address of the Vault collateral asset\n * @param _strategyAmount The amount of Vault collateral assets to withdraw\n */\n function withdraw(\n address _recipient,\n address _strategyAsset,\n uint256 _strategyAmount\n ) external override onlyVault nonReentrant {\n address[] memory strategyAssets = new address[](1);\n uint256[] memory strategyAmounts = new uint256[](1);\n strategyAssets[0] = _strategyAsset;\n strategyAmounts[0] = _strategyAmount;\n\n _withdraw(_recipient, strategyAssets, strategyAmounts);\n }\n\n /**\n * @notice Withdraw multiple Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAssets Addresses of the Vault collateral assets\n * @param _strategyAmounts The amounts of Vault collateral assets to withdraw\n */\n function withdraw(\n address _recipient,\n address[] calldata _strategyAssets,\n uint256[] calldata _strategyAmounts\n ) external onlyVault nonReentrant {\n _withdraw(_recipient, _strategyAssets, _strategyAmounts);\n }\n\n /**\n * @dev Withdraw multiple Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAssets Addresses of the Vault collateral assets\n * @param _strategyAmounts The amounts of Vault collateral assets to withdraw\n *\n * _withdrawal doesn't require a read-only re-entrancy protection since during the withdrawal\n * the function enters the Balancer Vault Context. If this function were called as part of\n * the attacking contract (while intercepting execution flow upon receiving ETH) the read-only\n * protection of the Balancer Vault would be triggered. Since the attacking contract would\n * already be in the Balancer Vault context and wouldn't be able to enter it again.\n */\n function _withdraw(\n address _recipient,\n address[] memory _strategyAssets,\n uint256[] memory _strategyAmounts\n ) internal {\n require(\n _strategyAssets.length == _strategyAmounts.length,\n \"Invalid input arrays\"\n );\n\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n require(\n assetToPToken[_strategyAssets[i]] != address(0),\n \"Unsupported asset\"\n );\n }\n\n // STEP 1 - Calculate the Balancer pool assets and amounts from the vault collateral assets\n\n // Get all the supported balancer pool assets\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n // Calculate the balancer pool assets and amounts to withdraw\n uint256[] memory poolAssetsAmountsOut = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n // Is the wrapped asset amount indexed by the assets array, not the order of the Balancer pool tokens\n // eg wstETH and sfrxETH amounts, not the stETH and frxETH amounts\n uint256[] memory strategyAssetsToPoolAssetsAmounts = new uint256[](\n _strategyAssets.length\n );\n\n // For each of the Balancer pool assets\n for (uint256 i = 0; i < tokens.length; ++i) {\n poolAssets[i] = address(tokens[i]);\n\n // Convert the Balancer pool asset back to a vault collateral asset\n address strategyAsset = _fromPoolAsset(poolAssets[i]);\n\n // for each of the vault assets\n for (uint256 j = 0; j < _strategyAssets.length; ++j) {\n // If the vault asset equals the vault asset mapped from the Balancer pool asset\n if (_strategyAssets[j] == strategyAsset) {\n (, poolAssetsAmountsOut[i]) = _toPoolAsset(\n strategyAsset,\n _strategyAmounts[j]\n );\n strategyAssetsToPoolAssetsAmounts[j] = poolAssetsAmountsOut[\n i\n ];\n\n /* Because of the potential Balancer rounding error mentioned below\n * the contract might receive 1-2 WEI smaller amount than required\n * in the withdraw user data encoding. If slightly lesser token amount\n * is received the strategy can not unwrap the pool asset as it is\n * smaller than expected.\n *\n * For that reason we `overshoot` the required tokens expected to\n * circumvent the error\n */\n if (poolAssetsAmountsOut[i] > 0) {\n poolAssetsAmountsOut[i] += 2;\n }\n }\n }\n }\n\n // STEP 2 - Calculate the max about of Balancer Pool Tokens (BPT) to withdraw\n\n // Estimate the required amount of Balancer Pool Tokens (BPT) for the assets\n uint256 maxBPTtoWithdraw = _getBPTExpected(\n poolAssets,\n /* all non 0 values are overshot by 2 WEI and with the expected mainnet\n * ~1% withdrawal deviation, the 2 WEI aren't important\n */\n poolAssetsAmountsOut\n );\n // Increase BPTs by the max allowed deviation\n // Any excess BPTs will be left in this strategy contract\n maxBPTtoWithdraw = maxBPTtoWithdraw.mulTruncate(\n 1e18 + maxWithdrawalDeviation\n );\n\n // STEP 3 - Withdraw the Balancer Pool Tokens (BPT) from Aura to this strategy contract\n\n // Withdraw BPT from Aura allowing for BPTs left in this strategy contract from previous withdrawals\n _lpWithdraw(\n maxBPTtoWithdraw - IERC20(platformAddress).balanceOf(address(this))\n );\n\n // STEP 4 - Withdraw the balancer pool assets from the pool\n\n /* Custom asset exit: BPT_IN_FOR_EXACT_TOKENS_OUT:\n * User sends an estimated but unknown (computed at run time) quantity of BPT,\n * and receives precise quantities of specified tokens.\n *\n * ['uint256', 'uint256[]', 'uint256']\n * [BPT_IN_FOR_EXACT_TOKENS_OUT, amountsOut, maxBPTAmountIn]\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.BPT_IN_FOR_EXACT_TOKENS_OUT,\n poolAssetsAmountsOut,\n maxBPTtoWithdraw\n );\n\n IBalancerVault.ExitPoolRequest memory request = IBalancerVault\n .ExitPoolRequest(\n poolAssets,\n /* We specify the exact amount of a tokens we are expecting in the encoded\n * userData, for that reason we don't need to specify the amountsOut here.\n *\n * Also Balancer has a rounding issue that can make a transaction fail:\n * https://github.com/balancer/balancer-v2-monorepo/issues/2541\n * which is an extra reason why this field is empty.\n */\n new uint256[](tokens.length),\n userData,\n false\n );\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n /* Payable keyword is required because of the IBalancerVault interface even though\n * this strategy shall never be receiving native ETH\n */\n payable(address(this)),\n request\n );\n\n // STEP 5 - Re-deposit any left over BPT tokens back into Aura\n /* When concluding how much of BPT we need to withdraw from Aura we overshoot by\n * roughly around 1% (initial mainnet setting of maxWithdrawalDeviation). After exiting\n * the pool strategy could have left over BPT tokens that are not earning boosted yield.\n * We re-deploy those back in.\n */\n _lpDepositAll();\n\n // STEP 6 - Unswap balancer pool assets to vault collateral assets and send to the vault.\n\n // For each of the specified assets\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n // Unwrap assets like wstETH and sfrxETH to rebasing assets stETH and frxETH\n if (strategyAssetsToPoolAssetsAmounts[i] > 0) {\n _unwrapPoolAsset(\n _strategyAssets[i],\n strategyAssetsToPoolAssetsAmounts[i]\n );\n }\n\n // Transfer the vault collateral assets to the recipient, which is typically the vault\n if (_strategyAmounts[i] > 0) {\n IERC20(_strategyAssets[i]).safeTransfer(\n _recipient,\n _strategyAmounts[i]\n );\n\n emit Withdrawal(\n _strategyAssets[i],\n platformAddress,\n _strategyAmounts[i]\n );\n }\n }\n }\n\n /**\n * @notice Withdraws all supported Vault collateral assets from the Balancer pool\n * and send to the OToken's Vault.\n *\n * Is only executable by the OToken's Vault or the Governor.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n // STEP 1 - Withdraw all Balancer Pool Tokens (BPT) from Aura to this strategy contract\n\n _lpWithdrawAll();\n // Get the BPTs withdrawn from Aura plus any that were already in this strategy contract\n uint256 BPTtoWithdraw = IERC20(platformAddress).balanceOf(\n address(this)\n );\n // Get the balancer pool assets and their total balances\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n uint256[] memory minAmountsOut = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n for (uint256 i = 0; i < tokens.length; ++i) {\n poolAssets[i] = address(tokens[i]);\n }\n\n // STEP 2 - Withdraw the Balancer pool assets from the pool\n /* Proportional exit: EXACT_BPT_IN_FOR_TOKENS_OUT:\n * User sends a precise quantity of BPT, and receives an estimated but unknown\n * (computed at run time) quantity of a single token\n *\n * ['uint256', 'uint256']\n * [EXACT_BPT_IN_FOR_TOKENS_OUT, bptAmountIn]\n *\n * It is ok to pass an empty minAmountsOut since tilting the pool in any direction\n * when doing a proportional exit can only be beneficial to the strategy. Since\n * it will receive more of the underlying tokens for the BPT traded in.\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.EXACT_BPT_IN_FOR_TOKENS_OUT,\n BPTtoWithdraw\n );\n\n IBalancerVault.ExitPoolRequest memory request = IBalancerVault\n .ExitPoolRequest(poolAssets, minAmountsOut, userData, false);\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n /* Payable keyword is required because of the IBalancerVault interface even though\n * this strategy shall never be receiving native ETH\n */\n payable(address(this)),\n request\n );\n\n // STEP 3 - Convert the balancer pool assets to the vault collateral assets and send to the vault\n // For each of the Balancer pool assets\n for (uint256 i = 0; i < tokens.length; ++i) {\n address poolAsset = address(tokens[i]);\n // Convert the balancer pool asset to the strategy asset\n address strategyAsset = _fromPoolAsset(poolAsset);\n // Get the balancer pool assets withdraw from the pool plus any that were already in this strategy contract\n uint256 poolAssetAmount = IERC20(poolAsset).balanceOf(\n address(this)\n );\n\n // Unwrap assets like wstETH and sfrxETH to rebasing assets stETH and frxETH\n uint256 unwrappedAmount = 0;\n if (poolAssetAmount > 0) {\n unwrappedAmount = _unwrapPoolAsset(\n strategyAsset,\n poolAssetAmount\n );\n }\n\n // Transfer the vault collateral assets to the vault\n if (unwrappedAmount > 0) {\n IERC20(strategyAsset).safeTransfer(\n vaultAddress,\n unwrappedAmount\n );\n emit Withdrawal(\n strategyAsset,\n platformAddress,\n unwrappedAmount\n );\n }\n }\n }\n\n /**\n * @notice Approves the Balancer Vault to transfer poolAsset counterparts\n * of all of the supported assets from this strategy. E.g. stETH is a supported\n * strategy and Balancer Vault gets unlimited approval to transfer wstETH.\n *\n * If Balancer pool uses a wrapped version of a supported asset then also approve\n * unlimited usage of an asset to the contract responsible for wrapping.\n *\n * Approve unlimited spending by Balancer Vault and Aura reward pool of the\n * pool BPT tokens.\n *\n * Is only executable by the Governor.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n _abstractSetPToken(assetsMapped[i], platformAddress);\n }\n _approveBase();\n }\n\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address) internal override {\n address poolAsset = _toPoolAsset(_asset);\n if (_asset == stETH) {\n // slither-disable-next-line unused-return\n IERC20(stETH).approve(wstETH, type(uint256).max);\n } else if (_asset == frxETH) {\n // slither-disable-next-line unused-return\n IERC20(frxETH).approve(sfrxETH, type(uint256).max);\n }\n _approveAsset(poolAsset);\n }\n\n /**\n * @dev Approves the Balancer Vault to transfer an asset from\n * this strategy. The assets could be a Vault collateral asset\n * like WETH or rETH; or a Balancer pool asset that wraps the vault asset\n * like wstETH or sfrxETH.\n */\n function _approveAsset(address _asset) internal {\n IERC20 asset = IERC20(_asset);\n // slither-disable-next-line unused-return\n asset.approve(address(balancerVault), type(uint256).max);\n }\n\n /**\n * @notice Returns the rate supplied by the Balancer configured rate\n * provider. Rate is used to normalize the token to common underlying\n * pool denominator. (ETH for ETH Liquid staking derivatives)\n *\n * @param _asset Address of the Balancer pool asset\n * @return rate of the corresponding asset\n */\n function _getRateProviderRate(address _asset)\n internal\n view\n override\n returns (uint256)\n {\n IMetaStablePool pool = IMetaStablePool(platformAddress);\n IRateProvider[] memory providers = pool.getRateProviders();\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n\n uint256 providersLength = providers.length;\n for (uint256 i = 0; i < providersLength; ++i) {\n // _assets and corresponding rate providers are all in the same order\n if (address(tokens[i]) == _asset) {\n // rate provider doesn't exist, defaults to 1e18\n if (address(providers[i]) == address(0)) {\n return 1e18;\n }\n return providers[i].getRate();\n }\n }\n\n // should never happen\n assert(false);\n }\n}\n" + }, + "contracts/strategies/balancer/VaultReentrancyLib.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity >=0.7.0 <0.9.0;\n\nimport \"../../utils/BalancerErrors.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\n\nlibrary VaultReentrancyLib {\n /**\n * @dev Ensure we are not in a Vault context when this function is called, by attempting a no-op internal\n * balance operation. If we are already in a Vault transaction (e.g., a swap, join, or exit), the Vault's\n * reentrancy protection will cause this function to revert.\n *\n * The exact function call doesn't really matter: we're just trying to trigger the Vault reentrancy check\n * (and not hurt anything in case it works). An empty operation array with no specific operation at all works\n * for that purpose, and is also the least expensive in terms of gas and bytecode size.\n *\n * Call this at the top of any function that can cause a state change in a pool and is either public itself,\n * or called by a public function *outside* a Vault operation (e.g., join, exit, or swap).\n *\n * If this is *not* called in functions that are vulnerable to the read-only reentrancy issue described\n * here (https://forum.balancer.fi/t/reentrancy-vulnerability-scope-expanded/4345), those functions are unsafe,\n * and subject to manipulation that may result in loss of funds.\n */\n function ensureNotInVaultContext(IBalancerVault vault) internal view {\n // Perform the following operation to trigger the Vault's reentrancy guard:\n //\n // IBalancerVault.UserBalanceOp[] memory noop = new IBalancerVault.UserBalanceOp[](0);\n // _vault.manageUserBalance(noop);\n //\n // However, use a static call so that it can be a view function (even though the function is non-view).\n // This allows the library to be used more widely, as some functions that need to be protected might be\n // view.\n //\n // This staticcall always reverts, but we need to make sure it doesn't fail due to a re-entrancy attack.\n // Staticcalls consume all gas forwarded to them on a revert caused by storage modification.\n // By default, almost the entire available gas is forwarded to the staticcall,\n // causing the entire call to revert with an 'out of gas' error.\n //\n // We set the gas limit to 10k for the staticcall to\n // avoid wasting gas when it reverts due to storage modification.\n // `manageUserBalance` is a non-reentrant function in the Vault, so calling it invokes `_enterNonReentrant`\n // in the `ReentrancyGuard` contract, reproduced here:\n //\n // function _enterNonReentrant() private {\n // // If the Vault is actually being reentered, it will revert in the first line, at the `_require` that\n // // checks the reentrancy flag, with \"BAL#400\" (corresponding to Errors.REENTRANCY) in the revertData.\n // // The full revertData will be: `abi.encodeWithSignature(\"Error(string)\", \"BAL#400\")`.\n // _require(_status != _ENTERED, Errors.REENTRANCY);\n //\n // // If the Vault is not being reentered, the check above will pass: but it will *still* revert,\n // // because the next line attempts to modify storage during a staticcall. However, this type of\n // // failure results in empty revertData.\n // _status = _ENTERED;\n // }\n //\n // So based on this analysis, there are only two possible revertData values: empty, or abi.encoded BAL#400.\n //\n // It is of course much more bytecode and gas efficient to check for zero-length revertData than to compare it\n // to the encoded REENTRANCY revertData.\n //\n // While it should be impossible for the call to fail in any other way (especially since it reverts before\n // `manageUserBalance` even gets called), any other error would generate non-zero revertData, so checking for\n // empty data guards against this case too.\n\n (, bytes memory revertData) = address(vault).staticcall{ gas: 10_000 }(\n abi.encodeWithSelector(vault.manageUserBalance.selector, 0)\n );\n\n _require(revertData.length == 0, Errors.REENTRANCY);\n }\n}\n" + }, + "contracts/strategies/BaseCurveAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for the Curve OETH/WETH pool\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { ICurveStableSwapNG } from \"../interfaces/ICurveStableSwapNG.sol\";\nimport { ICurveXChainLiquidityGauge } from \"../interfaces/ICurveXChainLiquidityGauge.sol\";\nimport { IChildLiquidityGaugeFactory } from \"../interfaces/IChildLiquidityGaugeFactory.sol\";\n\ncontract BaseCurveAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @dev a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n // New immutable variables that must be set in the constructor\n /**\n * @notice Address of the Wrapped ETH (WETH) contract.\n */\n IWETH9 public immutable weth;\n\n /**\n * @notice Address of the OETH token contract.\n */\n IERC20 public immutable oeth;\n\n /**\n * @notice Address of the LP (Liquidity Provider) token contract.\n */\n IERC20 public immutable lpToken;\n\n /**\n * @notice Address of the Curve StableSwap NG pool contract.\n */\n ICurveStableSwapNG public immutable curvePool;\n\n /**\n * @notice Address of the Curve X-Chain Liquidity Gauge contract.\n */\n ICurveXChainLiquidityGauge public immutable gauge;\n\n /**\n * @notice Address of the Child Liquidity Gauge Factory contract.\n */\n IChildLiquidityGaugeFactory public immutable gaugeFactory;\n\n // Ordered list of pool assets\n uint128 public immutable oethCoinIndex;\n uint128 public immutable wethCoinIndex;\n\n /**\n * @notice Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n uint256 public maxSlippage;\n\n event MaxSlippageUpdated(uint256 newMaxSlippage);\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balancesBefore = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffBefore = balancesBefore[wethCoinIndex].toInt256() -\n balancesBefore[oethCoinIndex].toInt256();\n\n _;\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balancesAfter = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffAfter = balancesAfter[wethCoinIndex].toInt256() -\n balancesAfter[oethCoinIndex].toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of ETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _oeth,\n address _weth,\n address _gauge,\n address _gaugeFactory,\n uint128 _oethCoinIndex,\n uint128 _wethCoinIndex\n ) InitializableAbstractStrategy(_baseConfig) {\n oethCoinIndex = _oethCoinIndex;\n wethCoinIndex = _wethCoinIndex;\n\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveStableSwapNG(_baseConfig.platformAddress);\n\n oeth = IERC20(_oeth);\n weth = IWETH9(_weth);\n gauge = ICurveXChainLiquidityGauge(_gauge);\n gaugeFactory = IChildLiquidityGaugeFactory(_gaugeFactory);\n\n _setGovernor(address(0));\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV\n * @param _maxSlippage Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV\n uint256 _maxSlippage\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n address[] memory _assets = new address[](1);\n _assets[0] = address(weth);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n _setMaxSlippage(_maxSlippage);\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit WETH into the Curve pool\n * @param _weth Address of Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to deposit.\n */\n function deposit(address _weth, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_weth, _amount);\n }\n\n function _deposit(address _weth, uint256 _wethAmount) internal {\n require(_wethAmount > 0, \"Must deposit something\");\n require(_weth == address(weth), \"Can only deposit WETH\");\n\n emit Deposit(_weth, address(lpToken), _wethAmount);\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 oethToAdd = uint256(\n _max(\n 0,\n balances[wethCoinIndex].toInt256() +\n _wethAmount.toInt256() -\n balances[oethCoinIndex].toInt256()\n )\n );\n\n /* Add so much OETH so that the pool ends up being balanced. And at minimum\n * add as much OETH as WETH and at maximum twice as much OETH.\n */\n oethToAdd = Math.max(oethToAdd, _wethAmount);\n oethToAdd = Math.min(oethToAdd, _wethAmount * 2);\n\n /* Mint OETH with a strategy that attempts to contribute to stability of OETH/WETH pool. Try\n * to mint so much OETH that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OETH minted will always be at least equal or greater\n * to WETH amount deployed. And never larger than twice the WETH amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(oethToAdd);\n\n emit Deposit(address(oeth), address(lpToken), oethToAdd);\n\n uint256[] memory _amounts = new uint256[](2);\n _amounts[wethCoinIndex] = _wethAmount;\n _amounts[oethCoinIndex] = oethToAdd;\n\n uint256 valueInLpTokens = (_wethAmount + oethToAdd).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Do the deposit to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(_amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool's LP tokens into the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n /**\n * @notice Deposit the strategy's entire balance of WETH into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = weth.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(weth), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _weth Address of the Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to withdraw.\n */\n function withdraw(\n address _recipient,\n address _weth,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_weth == address(weth), \"Can only withdraw WETH\");\n\n emit Withdrawal(_weth, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(_amount);\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough WETH on balanced removal\n */\n uint256[] memory _minWithdrawalAmounts = new uint256[](2);\n _minWithdrawalAmounts[wethCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OETH and any that was left in the strategy\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n\n // Transfer WETH to the recipient\n require(\n weth.transfer(_recipient, _amount),\n \"Transfer of WETH not successful\"\n );\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n function calcTokenToBurn(uint256 _wethAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much WETH\n * we want we can determine how much of OETH we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolWETHBalance = curvePool.balances(wethCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolWETHBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_wethAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = gauge.balanceOf(address(this));\n // Can not withdraw zero LP tokens from the gauge\n if (gaugeTokens == 0) return;\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[] memory minWithdrawAmounts = new uint256[](2);\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(\n lpToken.balanceOf(address(this)),\n minWithdrawAmounts\n );\n\n // Burn all OETH\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Get the strategy contract's WETH balance.\n // This includes all that was removed from the Curve pool and\n // any ether that was sitting in the strategy contract before the removal.\n uint256 ethBalance = weth.balanceOf(address(this));\n require(\n weth.transfer(vaultAddress, ethBalance),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethBalance);\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[] memory amounts = new uint256[](2);\n amounts[oethCoinIndex] = _oTokens;\n\n // Convert OETH to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool LP tokens to the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Deposit(address(oeth), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough ETH.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove OTokens from the Curve pool\n uint256 oethToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n oethCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /**\n * @notice One-sided remove of ETH from the Curve pool, convert to WETH\n * and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for ETH.\n * @dev Curve pool LP tokens is used rather than WETH assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of ETH. Curve's `calc_token_amount` functioun does not include fees.\n * A 3rd party libary can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * caclulate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Curve gauge and remove ETH from the Curve pool\n uint256 ethAmount = _withdrawAndRemoveFromPool(\n _lpTokens,\n wethCoinIndex\n );\n\n // Transfer WETH to the vault\n require(\n weth.transfer(vaultAddress, ethAmount),\n \"Transfer of WETH not successful\"\n );\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(weth), address(lpToken), ethAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the Convex pool and\n * do a one-sided remove of ETH or OETH from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the Convex pool.\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = ETH, 1 = OETH.\n * @return coinsRemoved The amount of ETH or OETH removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Curve gauge\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to ETH value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n // Apply slippage to ETH value\n uint256 minAmount = valueInEth.mulTruncate(uint256(1e18) - maxSlippage);\n\n // Remove just the ETH from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethbSupply = oeth.totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethbSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // CRV rewards flow.\n //---\n // CRV inflation:\n // Gauge receive CRV rewards from inflation.\n // Each checkpoint on the gauge send this CRV inflation to gauge factory.\n // This strategy should call mint on the gauge factory to collect the CRV rewards.\n // ---\n // Extra rewards:\n // Calling claim_rewards on the gauge will only claim extra rewards (outside of CRV).\n // ---\n\n // Mint CRV on Child Liquidity gauge factory\n gaugeFactory.mint(address(gauge));\n // Collect extra gauge rewards (outside of CRV)\n gauge.claim_rewards();\n\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _lpAmount) internal {\n // withdraw lp tokens from the gauge without claiming rewards\n gauge.withdraw(_lpAmount);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // WETH balance needed here for the balance check that happens from vault during depositing.\n balance = weth.balanceOf(address(this));\n uint256 lpTokens = gauge.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += (lpTokens * curvePool.get_virtual_price()) / 1e18;\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(weth);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Sets the maximum slippage allowed for any swap/liquidity operation\n * @param _maxSlippage Maximum slippage allowed, 1e18 = 100%.\n */\n function setMaxSlippage(uint256 _maxSlippage) external onlyGovernor {\n _setMaxSlippage(_maxSlippage);\n }\n\n function _setMaxSlippage(uint256 _maxSlippage) internal {\n require(_maxSlippage <= 5e16, \"Slippage must be less than 100%\");\n maxSlippage = _maxSlippage;\n emit MaxSlippageUpdated(_maxSlippage);\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OETH (required for adding liquidity)\n // slither-disable-next-line unused-return\n oeth.approve(platformAddress, type(uint256).max);\n\n // Approve Curve pool for WETH (required for adding liquidity)\n // slither-disable-next-line unused-return\n weth.approve(platformAddress, type(uint256).max);\n\n // Approve Curve gauge contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Curve gauge.\n // slither-disable-next-line unused-return\n lpToken.approve(address(gauge), type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/BridgedWOETHStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, SafeERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { AggregatorV3Interface } from \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\n\ncontract BridgedWOETHStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using StableMath for uint128;\n using SafeCast for uint256;\n using SafeERC20 for IERC20;\n\n event MaxPriceDiffBpsUpdated(uint128 oldValue, uint128 newValue);\n event WOETHPriceUpdated(uint128 oldValue, uint128 newValue);\n\n IWETH9 public immutable weth;\n IERC20 public immutable bridgedWOETH;\n IERC20 public immutable oethb;\n\n uint256 public constant MAX_PRICE_STALENESS = 2 days;\n\n uint128 public lastOraclePrice;\n uint128 public maxPriceDiffBps;\n\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _weth,\n address _bridgedWOETH,\n address _oethb\n ) InitializableAbstractStrategy(_stratConfig) {\n weth = IWETH9(_weth);\n bridgedWOETH = IERC20(_bridgedWOETH);\n oethb = IERC20(_oethb);\n }\n\n function initialize(uint128 _maxPriceDiffBps)\n external\n onlyGovernor\n initializer\n {\n InitializableAbstractStrategy._initialize(\n new address[](0), // No reward tokens\n new address[](0), // No assets\n new address[](0) // No pTokens\n );\n\n _setMaxPriceDiffBps(_maxPriceDiffBps);\n }\n\n /**\n * @dev Sets the max price diff bps for the wOETH value appreciation\n * @param _maxPriceDiffBps Bps value, 10k == 100%\n */\n function setMaxPriceDiffBps(uint128 _maxPriceDiffBps)\n external\n onlyGovernor\n {\n _setMaxPriceDiffBps(_maxPriceDiffBps);\n }\n\n /**\n * @dev Sets the max price diff bps for the wOETH value appreciation\n * @param _maxPriceDiffBps Bps value, 10k == 100%\n */\n function _setMaxPriceDiffBps(uint128 _maxPriceDiffBps) internal {\n require(\n _maxPriceDiffBps > 0 && _maxPriceDiffBps <= 10000,\n \"Invalid bps value\"\n );\n\n emit MaxPriceDiffBpsUpdated(maxPriceDiffBps, _maxPriceDiffBps);\n\n maxPriceDiffBps = _maxPriceDiffBps;\n }\n\n /**\n * @dev Wrapper for _updateWOETHOraclePrice with nonReentrant flag\n * @return The latest price of wOETH from Oracle\n */\n function updateWOETHOraclePrice() external nonReentrant returns (uint256) {\n return _updateWOETHOraclePrice();\n }\n\n /**\n * @dev Finds the value of bridged wOETH from the Oracle.\n * Ensures that it's within the bounds and reasonable.\n * And stores it.\n *\n * NOTE: Intentionally not caching `Vault.priceProvider` here,\n * since doing so would mean that we also have to update this\n * strategy every time there's a change in oracle router.\n * Besides on L2, the gas is considerably cheaper than mainnet.\n *\n * @return Latest price from oracle\n */\n function _updateWOETHOraclePrice() internal returns (uint256) {\n // WETH price per unit of bridged wOETH\n uint256 oraclePrice = IOracle(IVault(vaultAddress).priceProvider())\n .price(address(bridgedWOETH));\n\n // 1 wOETH > 1 WETH, always\n require(oraclePrice > 1 ether, \"Invalid wOETH value\");\n\n uint128 oraclePrice128 = oraclePrice.toUint128();\n\n // Do some checks\n if (lastOraclePrice > 0) {\n // Make sure the value only goes up\n require(oraclePrice128 >= lastOraclePrice, \"Negative wOETH yield\");\n\n // lastOraclePrice * (1 + maxPriceDiffBps)\n uint256 maxPrice = (lastOraclePrice * (1e4 + maxPriceDiffBps)) /\n 1e4;\n\n // And that it's within the bounds.\n require(oraclePrice128 <= maxPrice, \"Price diff beyond threshold\");\n }\n\n emit WOETHPriceUpdated(lastOraclePrice, oraclePrice128);\n\n // Store the price\n lastOraclePrice = oraclePrice128;\n\n return oraclePrice;\n }\n\n /**\n * @dev Computes & returns the value of given wOETH in WETH\n * @param woethAmount Amount of wOETH\n * @return Value of wOETH in WETH (using the last stored oracle price)\n */\n function getBridgedWOETHValue(uint256 woethAmount)\n public\n view\n returns (uint256)\n {\n return (woethAmount * lastOraclePrice) / 1 ether;\n }\n\n /**\n * @dev Takes in bridged wOETH and mints & returns\n * equivalent amount of OETHb.\n * @param woethAmount Amount of bridged wOETH to transfer in\n */\n function depositBridgedWOETH(uint256 woethAmount)\n external\n onlyGovernorOrStrategist\n nonReentrant\n {\n // Update wOETH price\n uint256 oraclePrice = _updateWOETHOraclePrice();\n\n // Figure out how much they are worth\n uint256 oethToMint = (woethAmount * oraclePrice) / 1 ether;\n\n require(oethToMint > 0, \"Invalid deposit amount\");\n\n // There's no pToken, however, it just uses WOETH address in the event\n emit Deposit(address(weth), address(bridgedWOETH), oethToMint);\n\n // Mint OETHb tokens and transfer it to the caller\n IVault(vaultAddress).mintForStrategy(oethToMint);\n\n // Transfer out minted OETHb\n // slither-disable-next-line unchecked-transfer unused-return\n oethb.transfer(msg.sender, oethToMint);\n\n // Transfer in all bridged wOETH tokens\n // slither-disable-next-line unchecked-transfer unused-return\n bridgedWOETH.transferFrom(msg.sender, address(this), woethAmount);\n }\n\n /**\n * @dev Takes in OETHb and burns it and returns\n * equivalent amount of bridged wOETH.\n * @param oethToBurn Amount of OETHb to burn\n */\n function withdrawBridgedWOETH(uint256 oethToBurn)\n external\n onlyGovernorOrStrategist\n nonReentrant\n {\n // Update wOETH price\n uint256 oraclePrice = _updateWOETHOraclePrice();\n\n // Figure out how much they are worth\n uint256 woethAmount = (oethToBurn * 1 ether) / oraclePrice;\n\n require(woethAmount > 0, \"Invalid withdraw amount\");\n\n // There's no pToken, however, it just uses WOETH address in the event\n emit Withdrawal(address(weth), address(bridgedWOETH), oethToBurn);\n\n // Transfer WOETH back\n // slither-disable-next-line unchecked-transfer unused-return\n bridgedWOETH.transfer(msg.sender, woethAmount);\n\n // Transfer in OETHb\n // slither-disable-next-line unchecked-transfer unused-return\n oethb.transferFrom(msg.sender, address(this), oethToBurn);\n\n // Burn OETHb\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n }\n\n /**\n * @notice Returns the amount of backing WETH the strategy holds\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // Figure out how much wOETH is worth at the time.\n // Always uses the last stored oracle price.\n // Call updateWOETHOraclePrice manually to pull in latest yields.\n\n // NOTE: If the contract has been deployed but the call to\n // `updateWOETHOraclePrice()` has never been made, then this\n // will return zero. It should be fine because the strategy\n // should update the price whenever a deposit/withdraw happens.\n\n // If `updateWOETHOraclePrice()` hasn't been called in a while,\n // the strategy will underreport its holdings but never overreport it.\n\n balance =\n (bridgedWOETH.balanceOf(address(this)) * lastOraclePrice) /\n 1 ether;\n }\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n // Strategist deposits bridged wOETH but the contract only\n // reports the balance in WETH. As far as Vault is concerned,\n // it isn't aware of bridged wOETH token\n return _asset == address(weth);\n }\n\n /***************************************\n Overridden methods\n ****************************************/\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function transferToken(address _asset, uint256 _amount)\n public\n override\n onlyGovernor\n {\n require(\n _asset != address(bridgedWOETH) && _asset != address(weth),\n \"Cannot transfer supported asset\"\n );\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice deposit() function not used for this strategy\n */\n function deposit(address, uint256)\n external\n override\n onlyVault\n nonReentrant\n {\n // Use depositBridgedWOETH() instead\n require(false, \"Deposit disabled\");\n }\n\n /**\n * @notice depositAll() function not used for this strategy\n */\n function depositAll() external override onlyVault nonReentrant {\n // Use depositBridgedWOETH() instead\n require(false, \"Deposit disabled\");\n }\n\n /**\n * @notice withdraw() function not used for this strategy\n */\n function withdraw(\n // solhint-disable-next-line no-unused-vars\n address _recipient,\n // solhint-disable-next-line no-unused-vars\n address _asset,\n // solhint-disable-next-line no-unused-vars\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(false, \"Withdrawal disabled\");\n }\n\n /**\n * @notice withdrawAll() function not used for this strategy\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n // Withdrawal disabled\n }\n\n function _abstractSetPToken(address, address) internal override {\n revert(\"No pTokens are used\");\n }\n\n function safeApproveAllTokens() external override {}\n\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function removePToken(uint256) external override {\n revert(\"No pTokens are used\");\n }\n\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function collectRewardTokens() external override {}\n}\n" + }, + "contracts/strategies/CompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Compound Strategy\n * @notice Investment strategy for Compound like lending platforms. eg Compound and Flux\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICERC20 } from \"./ICompound.sol\";\nimport { AbstractCompoundStrategy, InitializableAbstractStrategy } from \"./AbstractCompoundStrategy.sol\";\nimport { IComptroller } from \"../interfaces/IComptroller.sol\";\nimport { IERC20 } from \"../utils/InitializableAbstractStrategy.sol\";\n\ncontract CompoundStrategy is AbstractCompoundStrategy {\n using SafeERC20 for IERC20;\n event SkippedWithdrawal(address asset, uint256 amount);\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @notice initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @notice Collect accumulated COMP and send to Harvester.\n */\n function collectRewardTokens()\n external\n virtual\n override\n onlyHarvester\n nonReentrant\n {\n // Claim COMP from Comptroller\n ICERC20 cToken = _getCTokenFor(assetsMapped[0]);\n IComptroller comptroller = IComptroller(cToken.comptroller());\n // Only collect from active cTokens, saves gas\n address[] memory ctokensToCollect = new address[](assetsMapped.length);\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n ctokensToCollect[i] = address(_getCTokenFor(assetsMapped[i]));\n }\n // Claim only for this strategy\n address[] memory claimers = new address[](1);\n claimers[0] = address(this);\n // Claim COMP from Comptroller. Only collect for supply, saves gas\n comptroller.claimComp(claimers, ctokensToCollect, false, true);\n // Transfer COMP to Harvester\n IERC20 rewardToken = IERC20(rewardTokenAddresses[0]);\n uint256 balance = rewardToken.balanceOf(address(this));\n emit RewardTokenCollected(\n harvesterAddress,\n rewardTokenAddresses[0],\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n\n /**\n * @notice Deposit asset into the underlying platform\n * @param _asset Address of asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit an asset into the underlying platform\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n ICERC20 cToken = _getCTokenFor(_asset);\n emit Deposit(_asset, address(cToken), _amount);\n require(cToken.mint(_amount) == 0, \"cToken mint failed\");\n }\n\n /**\n * @notice Deposit the entire balance of any supported asset in the strategy into the underlying platform\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n uint256 assetBalance = asset.balanceOf(address(this));\n if (assetBalance > 0) {\n _deposit(address(asset), assetBalance);\n }\n }\n }\n\n /**\n * @notice Withdraw an asset from the underlying platform\n * @param _recipient Address to receive withdrawn assets\n * @param _asset Address of the asset to withdraw\n * @param _amount Amount of assets to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n ICERC20 cToken = _getCTokenFor(_asset);\n // If redeeming 0 cTokens, just skip, else COMP will revert\n uint256 cTokensToRedeem = _convertUnderlyingToCToken(cToken, _amount);\n if (cTokensToRedeem == 0) {\n emit SkippedWithdrawal(_asset, _amount);\n return;\n }\n\n emit Withdrawal(_asset, address(cToken), _amount);\n require(cToken.redeemUnderlying(_amount) == 0, \"Redeem failed\");\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / cTokens\n * We need to approve the cToken and give it permission to spend the asset\n * @param _asset Address of the asset to approve. eg DAI\n * @param _pToken The pToken for the approval. eg cDAI or fDAI\n */\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n // Safe approval\n IERC20(_asset).safeApprove(_pToken, 0);\n IERC20(_asset).safeApprove(_pToken, type(uint256).max);\n }\n\n /**\n * @notice Remove all supported assets from the underlying platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n // Redeem entire balance of cToken\n ICERC20 cToken = _getCTokenFor(address(asset));\n uint256 cTokenBalance = cToken.balanceOf(address(this));\n if (cTokenBalance > 0) {\n require(cToken.redeem(cTokenBalance) == 0, \"Redeem failed\");\n uint256 assetBalance = asset.balanceOf(address(this));\n // Transfer entire balance to Vault\n asset.safeTransfer(vaultAddress, assetBalance);\n\n emit Withdrawal(address(asset), address(cToken), assetBalance);\n }\n }\n }\n\n /**\n * @notice Get the total asset value held in the underlying platform\n * This includes any interest that was generated since depositing.\n * The exchange rate between the cToken and asset gradually increases,\n * causing the cToken to be worth more corresponding asset.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n // Balance is always with token cToken decimals\n ICERC20 cToken = _getCTokenFor(_asset);\n balance = _checkBalance(cToken);\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * underlying = (cTokenAmt * exchangeRate) / 1e18\n * @param _cToken cToken for which to check balance\n * @return balance Total value of the asset in the platform\n */\n function _checkBalance(ICERC20 _cToken)\n internal\n view\n returns (uint256 balance)\n {\n // e.g. 50e8*205316390724364402565641705 / 1e18 = 1.0265..e18\n balance =\n (_cToken.balanceOf(address(this)) * _cToken.exchangeRateStored()) /\n 1e18;\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding cToken,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens() external override {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n address cToken = assetToPToken[address(asset)];\n // Safe approval\n asset.safeApprove(cToken, 0);\n asset.safeApprove(cToken, type(uint256).max);\n }\n }\n}\n" + }, + "contracts/strategies/ConvexEthMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Convex Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for the Curve OETH/ETH pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { ICurveETHPoolV1 } from \"./ICurveETHPoolV1.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\n\ncontract ConvexEthMetaStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 public constant MAX_SLIPPAGE = 1e16; // 1%, same as the Curve UI\n address public constant ETH_ADDRESS =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n // The following slots have been deprecated with immutable variables\n // slither-disable-next-line constable-states\n address private _deprecated_cvxDepositorAddress;\n // slither-disable-next-line constable-states\n address private _deprecated_cvxRewardStaker;\n // slither-disable-next-line constable-states\n uint256 private _deprecated_cvxDepositorPTokenId;\n // slither-disable-next-line constable-states\n address private _deprecated_curvePool;\n // slither-disable-next-line constable-states\n address private _deprecated_lpToken;\n // slither-disable-next-line constable-states\n address private _deprecated_oeth;\n // slither-disable-next-line constable-states\n address private _deprecated_weth;\n\n // Ordered list of pool assets\n // slither-disable-next-line constable-states\n uint128 private _deprecated_oethCoinIndex;\n // slither-disable-next-line constable-states\n uint128 private _deprecated_ethCoinIndex;\n\n // New immutable variables that must be set in the constructor\n address public immutable cvxDepositorAddress;\n IRewardStaking public immutable cvxRewardStaker;\n uint256 public immutable cvxDepositorPTokenId;\n ICurveETHPoolV1 public immutable curvePool;\n IERC20 public immutable lpToken;\n IERC20 public immutable oeth;\n IWETH9 public immutable weth;\n\n // Ordered list of pool assets\n uint128 public constant oethCoinIndex = 1;\n uint128 public constant ethCoinIndex = 0;\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier only works on functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balancesBefore = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffBefore = int256(balancesBefore[ethCoinIndex]) -\n int256(balancesBefore[oethCoinIndex]);\n\n _;\n\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balancesAfter = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffAfter = int256(balancesAfter[ethCoinIndex]) -\n int256(balancesAfter[oethCoinIndex]);\n\n if (diffBefore <= 0) {\n // If the pool was originally imbalanced in favor of OETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n }\n if (diffBefore >= 0) {\n // If the pool was originally imbalanced in favor of ETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n // Used to circumvent the stack too deep issue\n struct ConvexEthMetaConfig {\n address cvxDepositorAddress; //Address of the Convex depositor(AKA booster) for this pool\n address cvxRewardStakerAddress; //Address of the CVX rewards staker\n uint256 cvxDepositorPTokenId; //Pid of the pool referred to by Depositor and staker\n address oethAddress; //Address of OETH token\n address wethAddress; //Address of WETH\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n ConvexEthMetaConfig memory _convexConfig\n ) InitializableAbstractStrategy(_baseConfig) {\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveETHPoolV1(_baseConfig.platformAddress);\n\n cvxDepositorAddress = _convexConfig.cvxDepositorAddress;\n cvxRewardStaker = IRewardStaking(_convexConfig.cvxRewardStakerAddress);\n cvxDepositorPTokenId = _convexConfig.cvxDepositorPTokenId;\n oeth = IERC20(_convexConfig.oethAddress);\n weth = IWETH9(_convexConfig.wethAddress);\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. eg WETH\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets // WETH\n ) external onlyGovernor initializer {\n require(_assets.length == 1, \"Must have exactly one asset\");\n require(_assets[0] == address(weth), \"Asset not WETH\");\n\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit WETH into the Curve pool\n * @param _weth Address of Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to deposit.\n */\n function deposit(address _weth, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_weth, _amount);\n }\n\n function _deposit(address _weth, uint256 _wethAmount) internal {\n require(_wethAmount > 0, \"Must deposit something\");\n require(_weth == address(weth), \"Can only deposit WETH\");\n weth.withdraw(_wethAmount);\n\n emit Deposit(_weth, address(lpToken), _wethAmount);\n\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 oethToAdd = uint256(\n _max(\n 0,\n int256(balances[ethCoinIndex]) +\n int256(_wethAmount) -\n int256(balances[oethCoinIndex])\n )\n );\n\n /* Add so much OETH so that the pool ends up being balanced. And at minimum\n * add as much OETH as WETH and at maximum twice as much OETH.\n */\n oethToAdd = Math.max(oethToAdd, _wethAmount);\n oethToAdd = Math.min(oethToAdd, _wethAmount * 2);\n\n /* Mint OETH with a strategy that attempts to contribute to stability of OETH/WETH pool. Try\n * to mint so much OETH that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OETH minted will always be at least equal or greater\n * to WETH amount deployed. And never larger than twice the WETH amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(oethToAdd);\n\n emit Deposit(address(oeth), address(lpToken), oethToAdd);\n\n uint256[2] memory _amounts;\n _amounts[ethCoinIndex] = _wethAmount;\n _amounts[oethCoinIndex] = oethToAdd;\n\n uint256 valueInLpTokens = (_wethAmount + oethToAdd).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Do the deposit to the Curve pool\n // slither-disable-next-line arbitrary-send\n uint256 lpDeposited = curvePool.add_liquidity{ value: _wethAmount }(\n _amounts,\n minMintAmount\n );\n\n // Deposit the Curve pool's LP tokens into the Convex rewards pool\n require(\n IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n lpDeposited,\n true // Deposit with staking\n ),\n \"Depositing LP to Convex not successful\"\n );\n }\n\n /**\n * @notice Deposit the strategy's entire balance of WETH into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = weth.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(weth), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _weth Address of the Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to withdraw.\n */\n function withdraw(\n address _recipient,\n address _weth,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Invalid amount\");\n require(_weth == address(weth), \"Can only withdraw WETH\");\n\n emit Withdrawal(_weth, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(_amount);\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough WETH on balanced removal\n */\n uint256[2] memory _minWithdrawalAmounts = [uint256(0), uint256(0)];\n _minWithdrawalAmounts[ethCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OETH and any that was left in the strategy\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n\n // Transfer WETH to the recipient\n weth.deposit{ value: _amount }();\n require(\n weth.transfer(_recipient, _amount),\n \"Transfer of WETH not successful\"\n );\n }\n\n function calcTokenToBurn(uint256 _wethAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much WETH\n * we want we can determine how much of OETH we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolWETHBalance = curvePool.balances(ethCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolWETHBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_wethAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = cvxRewardStaker.balanceOf(address(this));\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[2] memory minWithdrawAmounts = [uint256(0), uint256(0)];\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(\n lpToken.balanceOf(address(this)),\n minWithdrawAmounts\n );\n\n // Burn all OETH\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Get the strategy contract's ether balance.\n // This includes all that was removed from the Curve pool and\n // any ether that was sitting in the strategy contract before the removal.\n uint256 ethBalance = address(this).balance;\n // Convert all the strategy contract's ether to WETH and transfer to the vault.\n weth.deposit{ value: ethBalance }();\n require(\n weth.transfer(vaultAddress, ethBalance),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethBalance);\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[2] memory amounts = [uint256(0), uint256(0)];\n amounts[oethCoinIndex] = _oTokens;\n\n // Convert OETH to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n\n // Deposit the Curve pool LP tokens to the Convex rewards pool\n require(\n IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n lpDeposited,\n true // Deposit with staking\n ),\n \"Failed to Deposit LP to Convex\"\n );\n\n emit Deposit(address(oeth), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough ETH.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove OTokens from the Curve pool\n uint256 oethToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n oethCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /**\n * @notice One-sided remove of ETH from the Curve pool, convert to WETH\n * and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for ETH.\n * @dev Curve pool LP tokens is used rather than WETH assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of ETH. Curve's `calc_token_amount` functioun does not include fees.\n * A 3rd party libary can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * caclulate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove ETH from the Curve pool\n uint256 ethAmount = _withdrawAndRemoveFromPool(_lpTokens, ethCoinIndex);\n\n // Convert ETH to WETH and transfer to the vault\n weth.deposit{ value: ethAmount }();\n require(\n weth.transfer(vaultAddress, ethAmount),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the Convex pool and\n * do a one-sided remove of ETH or OETH from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the Convex pool.\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = ETH, 1 = OETH.\n * @return coinsRemoved The amount of ETH or OETH removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Convex pool\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to ETH value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n // Apply slippage to ETH value\n uint256 minAmount = valueInEth.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Remove just the ETH from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV and CVX rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n cvxRewardStaker.getReward();\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _wethAmount) internal {\n // withdraw and unwrap with claim takes back the lpTokens\n // and also collects the rewards for deposit\n cvxRewardStaker.withdrawAndUnwrap(_wethAmount, true);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // Eth balance needed here for the balance check that happens from vault during depositing.\n balance = address(this).balance;\n uint256 lpTokens = cvxRewardStaker.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += (lpTokens * curvePool.get_virtual_price()) / 1e18;\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(weth);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @notice Accept unwrapped WETH\n */\n receive() external payable {}\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OETH (required for adding liquidity)\n // No approval is needed for ETH\n // slither-disable-next-line unused-return\n oeth.approve(platformAddress, type(uint256).max);\n\n // Approve Convex deposit contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Convex rewards pool\n // slither-disable-next-line unused-return\n lpToken.approve(cvxDepositorAddress, type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/ConvexGeneralizedMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { AbstractConvexMetaStrategy } from \"./AbstractConvexMetaStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract ConvexGeneralizedMetaStrategy is AbstractConvexMetaStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /* Take 3pool LP and deposit it to metapool. Take the LP from metapool\n * and deposit them to Convex.\n */\n function _lpDepositAll() internal override {\n IERC20 threePoolLp = IERC20(pTokenAddress);\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 threePoolLpBalance = threePoolLp.balanceOf(address(this));\n uint256 curve3PoolVirtualPrice = curvePool.get_virtual_price();\n uint256 threePoolLpDollarValue = threePoolLpBalance.mulTruncate(\n curve3PoolVirtualPrice\n );\n\n uint256[2] memory _amounts = [0, threePoolLpBalance];\n\n uint256 metapoolVirtualPrice = metapool.get_virtual_price();\n /**\n * First convert all the deposited tokens to dollar values,\n * then divide by virtual price to convert to metapool LP tokens\n * and apply the max slippage\n */\n uint256 minReceived = threePoolLpDollarValue\n .divPrecisely(metapoolVirtualPrice)\n .mulTruncate(uint256(1e18) - MAX_SLIPPAGE);\n\n uint256 metapoolLp = metapool.add_liquidity(_amounts, minReceived);\n\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n metapoolLp,\n true // Deposit with staking\n );\n\n require(success, \"Failed to deposit to Convex\");\n }\n\n /**\n * Withdraw the specified amount of tokens from the gauge. And use all the resulting tokens\n * to remove liquidity from metapool\n * @param num3CrvTokens Number of Convex 3pool LP tokens to withdraw from metapool\n */\n function _lpWithdraw(uint256 num3CrvTokens) internal override {\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n uint256 requiredMetapoolLpTokens = _calcCurveMetaTokenAmount(\n crvCoinIndex,\n num3CrvTokens\n );\n\n require(\n requiredMetapoolLpTokens <= gaugeTokens,\n string(\n bytes.concat(\n bytes(\"Attempting to withdraw \"),\n bytes(Strings.toString(requiredMetapoolLpTokens)),\n bytes(\", metapoolLP but only \"),\n bytes(Strings.toString(gaugeTokens)),\n bytes(\" available.\")\n )\n )\n );\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards for deposit\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n requiredMetapoolLpTokens,\n true\n );\n\n if (requiredMetapoolLpTokens > 0) {\n // slither-disable-next-line unused-return\n metapool.remove_liquidity_one_coin(\n requiredMetapoolLpTokens,\n int128(crvCoinIndex),\n num3CrvTokens\n );\n }\n }\n\n function _lpWithdrawAll() internal override {\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n gaugeTokens,\n true\n );\n\n if (gaugeTokens > 0) {\n uint256 burnDollarAmount = gaugeTokens.mulTruncate(\n metapool.get_virtual_price()\n );\n uint256 curve3PoolExpected = burnDollarAmount.divPrecisely(\n ICurvePool(platformAddress).get_virtual_price()\n );\n\n // Always withdraw all of the available metapool LP tokens (similar to how we always deposit all)\n // slither-disable-next-line unused-return\n metapool.remove_liquidity_one_coin(\n gaugeTokens,\n int128(crvCoinIndex),\n curve3PoolExpected -\n curve3PoolExpected.mulTruncate(maxWithdrawalSlippage)\n );\n }\n }\n}\n" + }, + "contracts/strategies/ConvexOUSDMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { AbstractConvexMetaStrategy } from \"./AbstractConvexMetaStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract ConvexOUSDMetaStrategy is AbstractConvexMetaStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /* Take 3pool LP and mint the corresponding amount of ousd. Deposit and stake that to\n * ousd Curve Metapool. Take the LP from metapool and deposit them to Convex.\n */\n function _lpDepositAll() internal override {\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 threePoolLpBalance = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n uint256 curve3PoolVirtualPrice = curvePool.get_virtual_price();\n uint256 threePoolLpDollarValue = threePoolLpBalance.mulTruncate(\n curve3PoolVirtualPrice\n );\n\n // safe to cast since min value is at least 0\n uint256 ousdToAdd = uint256(\n _max(\n 0,\n int256(\n metapool.balances(crvCoinIndex).mulTruncate(\n curve3PoolVirtualPrice\n )\n ) -\n int256(metapool.balances(mainCoinIndex)) +\n int256(threePoolLpDollarValue)\n )\n );\n\n /* Add so much OUSD so that the pool ends up being balanced. And at minimum\n * add twice as much OUSD as 3poolLP and at maximum at twice as\n * much OUSD.\n */\n ousdToAdd = Math.max(ousdToAdd, threePoolLpDollarValue);\n ousdToAdd = Math.min(ousdToAdd, threePoolLpDollarValue * 2);\n\n /* Mint OUSD with a strategy that attempts to contribute to stability of OUSD metapool. Try\n * to mint so much OUSD that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OUSD minted will always be at least equal or greater\n * to stablecoin(DAI, USDC, USDT) amount of 3CRVLP deployed. And never larger than twice the\n * stablecoin amount of 3CRVLP deployed even if it would have a further beneficial effect\n * on pool stability.\n */\n if (ousdToAdd > 0) {\n IVault(vaultAddress).mintForStrategy(ousdToAdd);\n }\n\n uint256[2] memory _amounts = [ousdToAdd, threePoolLpBalance];\n\n uint256 metapoolVirtualPrice = metapool.get_virtual_price();\n /**\n * First convert all the deposited tokens to dollar values,\n * then divide by virtual price to convert to metapool LP tokens\n * and apply the max slippage\n */\n uint256 minReceived = (ousdToAdd + threePoolLpDollarValue)\n .divPrecisely(metapoolVirtualPrice)\n .mulTruncate(uint256(1e18) - MAX_SLIPPAGE);\n\n uint256 metapoolLp = metapool.add_liquidity(_amounts, minReceived);\n\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n metapoolLp,\n true // Deposit with staking\n );\n\n require(success, \"Failed to deposit to Convex\");\n }\n\n /**\n * Withdraw the specified amount of tokens from the gauge. And use all the resulting tokens\n * to remove liquidity from metapool\n * @param num3CrvTokens Number of 3CRV tokens to withdraw from metapool\n */\n function _lpWithdraw(uint256 num3CrvTokens) internal override {\n ICurvePool curvePool = ICurvePool(platformAddress);\n /* The rate between coins in the metapool determines the rate at which metapool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much 3crvLp\n * we want we can determine how much of OUSD we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 crvPoolBalance = metapool.balances(crvCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * metapool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * metapoolLPToken.totalSupply()) / crvPoolBalance;\n // simplifying below to: `uint256 diff = (num3CrvTokens - 1) * k` causes loss of precision\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = crvPoolBalance * k -\n (crvPoolBalance - num3CrvTokens - 1) * k;\n uint256 lpToBurn = diff / 1e36;\n\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n require(\n lpToBurn <= gaugeTokens,\n string(\n bytes.concat(\n bytes(\"Attempting to withdraw \"),\n bytes(Strings.toString(lpToBurn)),\n bytes(\", metapoolLP but only \"),\n bytes(Strings.toString(gaugeTokens)),\n bytes(\" available.\")\n )\n )\n );\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards for deposit\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n lpToBurn,\n true\n );\n\n // calculate the min amount of OUSD expected for the specified amount of LP tokens\n uint256 minOUSDAmount = lpToBurn.mulTruncate(\n metapool.get_virtual_price()\n ) -\n num3CrvTokens.mulTruncate(curvePool.get_virtual_price()) -\n 1;\n\n // withdraw the liquidity from metapool\n uint256[2] memory _removedAmounts = metapool.remove_liquidity(\n lpToBurn,\n [minOUSDAmount, num3CrvTokens]\n );\n\n IVault(vaultAddress).burnForStrategy(_removedAmounts[mainCoinIndex]);\n }\n\n function _lpWithdrawAll() internal override {\n IERC20 metapoolErc20 = IERC20(address(metapool));\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n gaugeTokens,\n true\n );\n\n uint256[2] memory _minAmounts = [uint256(0), uint256(0)];\n uint256[2] memory _removedAmounts = metapool.remove_liquidity(\n metapoolErc20.balanceOf(address(this)),\n _minAmounts\n );\n\n IVault(vaultAddress).burnForStrategy(_removedAmounts[mainCoinIndex]);\n }\n}\n" + }, + "contracts/strategies/ConvexStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { IERC20, AbstractCurveStrategy, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\n/*\n * IMPORTANT(!) If ConvexStrategy needs to be re-deployed, it requires new\n * proxy contract with fresh storage slots. Changes in `AbstractCurveStrategy`\n * storage slots would break existing implementation.\n *\n * Remove this notice if ConvexStrategy is re-deployed\n */\ncontract ConvexStrategy is AbstractCurveStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n address internal cvxDepositorAddress;\n address internal cvxRewardStakerAddress;\n // slither-disable-next-line constable-states\n address private _deprecated_cvxRewardTokenAddress;\n uint256 internal cvxDepositorPTokenId;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * DAI, USDC, USDT\n * @param _pTokens Platform Token corresponding addresses\n * @param _cvxDepositorAddress Address of the Convex depositor(AKA booster) for this pool\n * @param _cvxRewardStakerAddress Address of the CVX rewards staker\n * @param _cvxDepositorPTokenId Pid of the pool referred to by Depositor and staker\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets,\n address[] calldata _pTokens,\n address _cvxDepositorAddress,\n address _cvxRewardStakerAddress,\n uint256 _cvxDepositorPTokenId\n ) external onlyGovernor initializer {\n require(_assets.length == 3, \"Must have exactly three assets\");\n // Should be set prior to abstract initialize call otherwise\n // abstractSetPToken calls will fail\n cvxDepositorAddress = _cvxDepositorAddress;\n cvxRewardStakerAddress = _cvxRewardStakerAddress;\n cvxDepositorPTokenId = _cvxDepositorPTokenId;\n pTokenAddress = _pTokens[0];\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n function _lpDepositAll() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // Deposit with staking\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n pToken.balanceOf(address(this)),\n true\n );\n require(success, \"Failed to deposit to Convex\");\n }\n\n function _lpWithdraw(uint256 numCrvTokens) internal override {\n uint256 gaugePTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n // Not enough in this contract or in the Gauge, can't proceed\n require(numCrvTokens > gaugePTokens, \"Insufficient 3CRV balance\");\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards to this\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n numCrvTokens,\n true\n );\n }\n\n function _lpWithdrawAll() internal override {\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards to this\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n IRewardStaking(cvxRewardStakerAddress).balanceOf(address(this)),\n true\n );\n }\n\n function _approveBase() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // 3Pool for LP token (required for removing liquidity)\n pToken.safeApprove(platformAddress, 0);\n pToken.safeApprove(platformAddress, type(uint256).max);\n // Gauge for LP token\n pToken.safeApprove(cvxDepositorAddress, 0);\n pToken.safeApprove(cvxDepositorAddress, type(uint256).max);\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 contractPTokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n uint256 gaugePTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n uint256 totalPTokens = contractPTokens + gaugePTokens;\n\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (totalPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = (totalPTokens * virtual_price) / 1e18;\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = value.scaleBy(assetDecimals, 18) / 3;\n }\n }\n\n /**\n * @dev Collect accumulated CRV and CVX and send to Vault.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n IRewardStaking(cvxRewardStakerAddress).getReward();\n _collectRewardTokens();\n }\n}\n" + }, + "contracts/strategies/CurveAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for a Curve pool using an OToken.\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { ICurveStableSwapNG } from \"../interfaces/ICurveStableSwapNG.sol\";\nimport { ICurveLiquidityGaugeV6 } from \"../interfaces/ICurveLiquidityGaugeV6.sol\";\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\nimport { ICurveMinter } from \"../interfaces/ICurveMinter.sol\";\n\ncontract CurveAMOStrategy is InitializableAbstractStrategy {\n using SafeCast for uint256;\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n /**\n * @dev a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n // New immutable variables that must be set in the constructor\n /**\n * @notice Address of the hard asset (weth, usdt, usdc).\n */\n IERC20 public immutable hardAsset;\n\n /**\n * @notice Address of the OTOKEN token contract.\n */\n IERC20 public immutable oToken;\n\n /**\n * @notice Address of the LP (Liquidity Provider) token contract.\n */\n IERC20 public immutable lpToken;\n\n /**\n * @notice Address of the Curve StableSwap NG pool contract.\n */\n ICurveStableSwapNG public immutable curvePool;\n\n /**\n * @notice Address of the Curve X-Chain Liquidity Gauge contract.\n */\n ICurveLiquidityGaugeV6 public immutable gauge;\n\n /**\n * @notice Address of the Curve Minter contract.\n */\n ICurveMinter public immutable minter;\n\n /**\n * @notice Index of the OTOKEN and hardAsset in the Curve pool.\n */\n uint128 public immutable otokenCoinIndex;\n uint128 public immutable hardAssetCoinIndex;\n\n /**\n * @notice Decimals of the OTOKEN and hardAsset.\n */\n uint8 public immutable decimalsOToken;\n uint8 public immutable decimalsHardAsset;\n\n /**\n * @notice Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n uint256 public maxSlippage;\n\n event MaxSlippageUpdated(uint256 newMaxSlippage);\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the hard asset and OToken balances in the Curve pool\n uint256[] memory balancesBefore = curvePool.get_balances();\n // diff = hardAsset balance - OTOKEN balance\n int256 diffBefore = (\n balancesBefore[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() - balancesBefore[otokenCoinIndex].toInt256();\n\n _;\n\n // Get the hard asset and OToken balances in the Curve pool\n uint256[] memory balancesAfter = curvePool.get_balances();\n // diff = hardAsset balance - OTOKEN balance\n int256 diffAfter = (\n balancesAfter[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() - balancesAfter[otokenCoinIndex].toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OTOKEN, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of hardAsset, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _otoken,\n address _hardAsset,\n address _gauge,\n address _minter\n ) InitializableAbstractStrategy(_baseConfig) {\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveStableSwapNG(_baseConfig.platformAddress);\n minter = ICurveMinter(_minter);\n\n oToken = IERC20(_otoken);\n hardAsset = IERC20(_hardAsset);\n gauge = ICurveLiquidityGaugeV6(_gauge);\n decimalsHardAsset = IBasicToken(_hardAsset).decimals();\n decimalsOToken = IBasicToken(_otoken).decimals();\n\n (hardAssetCoinIndex, otokenCoinIndex) = curvePool.coins(0) == _hardAsset\n ? (0, 1)\n : (1, 0);\n require(\n curvePool.coins(otokenCoinIndex) == _otoken &&\n curvePool.coins(hardAssetCoinIndex) == _hardAsset,\n \"Invalid coin indexes\"\n );\n require(gauge.lp_token() == address(curvePool), \"Invalid pool\");\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV\n * @param _maxSlippage Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV\n uint256 _maxSlippage\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n address[] memory _assets = new address[](1);\n _assets[0] = address(hardAsset);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n _setMaxSlippage(_maxSlippage);\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit hard asset into the Curve pool\n * @param _hardAsset Address of hard asset contract.\n * @param _amount Amount of hard asset to deposit.\n */\n function deposit(address _hardAsset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_hardAsset, _amount);\n }\n\n function _deposit(address _hardAsset, uint256 _hardAssetAmount) internal {\n require(_hardAssetAmount > 0, \"Must deposit something\");\n require(_hardAsset == address(hardAsset), \"Unsupported asset\");\n\n emit Deposit(_hardAsset, address(lpToken), _hardAssetAmount);\n uint256 scaledHardAssetAmount = _hardAssetAmount.scaleBy(\n decimalsOToken,\n decimalsHardAsset\n );\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 otokenToAdd = uint256(\n _max(\n 0,\n (\n balances[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() +\n scaledHardAssetAmount.toInt256() -\n balances[otokenCoinIndex].toInt256()\n )\n );\n\n /* Add so much OTOKEN so that the pool ends up being balanced. And at minimum\n * add as much OTOKEN as hard asset and at maximum twice as much OTOKEN.\n */\n otokenToAdd = Math.max(otokenToAdd, scaledHardAssetAmount);\n otokenToAdd = Math.min(otokenToAdd, scaledHardAssetAmount * 2);\n\n /* Mint OTOKEN with a strategy that attempts to contribute to stability of OTOKEN/hardAsset pool. Try\n * to mint so much OTOKEN that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OTOKEN minted will always be at least equal or greater\n * to hardAsset amount deployed. And never larger than twice the hardAsset amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(otokenToAdd);\n\n emit Deposit(address(oToken), address(lpToken), otokenToAdd);\n\n uint256[] memory _amounts = new uint256[](2);\n _amounts[hardAssetCoinIndex] = _hardAssetAmount;\n _amounts[otokenCoinIndex] = otokenToAdd;\n\n uint256 valueInLpTokens = (scaledHardAssetAmount + otokenToAdd)\n .divPrecisely(curvePool.get_virtual_price());\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Do the deposit to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(_amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool's LP tokens into the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n /**\n * @notice Deposit the strategy's entire balance of hardAsset into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = hardAsset.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(hardAsset), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw hardAsset and OTOKEN from the Curve pool, burn the OTOKEN,\n * transfer hardAsset to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _hardAsset Address of the hardAsset contract.\n * @param _amount Amount of hardAsset to withdraw.\n */\n function withdraw(\n address _recipient,\n address _hardAsset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(\n _hardAsset == address(hardAsset),\n \"Can only withdraw hard asset\"\n );\n\n emit Withdrawal(_hardAsset, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(\n _amount.scaleBy(decimalsOToken, decimalsHardAsset)\n );\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough hardAsset on balanced removal\n */\n uint256[] memory _minWithdrawalAmounts = new uint256[](2);\n _minWithdrawalAmounts[hardAssetCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OTOKEN and any that was left in the strategy\n uint256 otokenToBurn = oToken.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n\n // Transfer hardAsset to the recipient\n hardAsset.safeTransfer(_recipient, _amount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n function calcTokenToBurn(uint256 _hardAssetAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much hardAsset\n * we want we can determine how much of OTOKEN we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolHardAssetBalance = curvePool\n .balances(hardAssetCoinIndex)\n .scaleBy(decimalsOToken, decimalsHardAsset);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolHardAssetBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_hardAssetAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all hardAsset and OTOKEN from the Curve pool, burn the OTOKEN,\n * transfer hardAsset to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = gauge.balanceOf(address(this));\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[] memory minWithdrawAmounts = new uint256[](2);\n\n // Check balance of LP tokens in the strategy, if 0 return\n uint256 lpBalance = lpToken.balanceOf(address(this));\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n if (lpBalance > 0) {\n curvePool.remove_liquidity(lpBalance, minWithdrawAmounts);\n }\n\n // Burn all OTOKEN\n uint256 otokenToBurn = oToken.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n // Get the strategy contract's hardAsset balance.\n // This includes all that was removed from the Curve pool and\n // any hardAsset that was sitting in the strategy contract before the removal.\n uint256 hardAssetBalance = hardAsset.balanceOf(address(this));\n hardAsset.safeTransfer(vaultAddress, hardAssetBalance);\n\n if (hardAssetBalance > 0)\n emit Withdrawal(\n address(hardAsset),\n address(lpToken),\n hardAssetBalance\n );\n if (otokenToBurn > 0)\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many hardAsset.\n * The OToken/Asset, eg OTOKEN/hardAsset, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[] memory amounts = new uint256[](2);\n amounts[otokenCoinIndex] = _oTokens;\n\n // Convert OTOKEN to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool LP tokens to the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Deposit(address(oToken), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough hardAsset.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from gauge and remove OTokens from the Curve pool\n uint256 otokenToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n otokenCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n }\n\n /**\n * @notice One-sided remove of hardAsset from the Curve pool and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many hardAsset.\n * The OToken/Asset, eg OTOKEN/hardAsset, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for hardAsset.\n * @dev Curve pool LP tokens is used rather than hardAsset assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of hardAsset. Curve's `calc_token_amount` function does not include fees.\n * A 3rd party library can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * calculate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Curve gauge and remove hardAsset from the Curve pool\n uint256 hardAssetAmount = _withdrawAndRemoveFromPool(\n _lpTokens,\n hardAssetCoinIndex\n );\n\n // Transfer hardAsset to the vault\n hardAsset.safeTransfer(vaultAddress, hardAssetAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(hardAsset), address(lpToken), hardAssetAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the gauge and\n * do a one-sided remove of hardAsset or OTOKEN from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the gauge\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = hardAsset, 1 = OTOKEN.\n * @return coinsRemoved The amount of hardAsset or OTOKEN removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Curve gauge\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to hardAsset value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n\n if (coinIndex == hardAssetCoinIndex) {\n valueInEth = valueInEth.scaleBy(decimalsHardAsset, decimalsOToken);\n }\n\n // Apply slippage to hardAsset value\n uint256 minAmount = valueInEth.mulTruncate(uint256(1e18) - maxSlippage);\n\n // Remove just the hardAsset from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOtokenSupply = oToken.totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOtokenSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV rewards from inflation\n minter.mint(address(gauge));\n\n // Collect extra gauge rewards (outside of CRV)\n gauge.claim_rewards();\n\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _lpAmount) internal {\n require(\n gauge.balanceOf(address(this)) >= _lpAmount,\n \"Insufficient LP tokens\"\n );\n // withdraw lp tokens from the gauge without claiming rewards\n gauge.withdraw(_lpAmount);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(hardAsset), \"Unsupported asset\");\n\n // hardAsset balance needed here for the balance check that happens from vault during depositing.\n balance = hardAsset.balanceOf(address(this));\n uint256 lpTokens = gauge.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += ((lpTokens * curvePool.get_virtual_price()) / 1e18)\n .scaleBy(decimalsHardAsset, decimalsOToken);\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(hardAsset);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Sets the maximum slippage allowed for any swap/liquidity operation\n * @param _maxSlippage Maximum slippage allowed, 1e18 = 100%.\n */\n function setMaxSlippage(uint256 _maxSlippage) external onlyGovernor {\n _setMaxSlippage(_maxSlippage);\n }\n\n function _setMaxSlippage(uint256 _maxSlippage) internal {\n require(_maxSlippage <= 5e16, \"Slippage must be less than 100%\");\n maxSlippage = _maxSlippage;\n emit MaxSlippageUpdated(_maxSlippage);\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OTOKEN (required for adding liquidity)\n // slither-disable-next-line unused-return\n oToken.approve(platformAddress, type(uint256).max);\n\n // Approve Curve pool for hardAsset (required for adding liquidity)\n // slither-disable-next-line unused-return\n hardAsset.safeApprove(platformAddress, type(uint256).max);\n\n // Approve Curve gauge contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Curve gauge.\n // slither-disable-next-line unused-return\n lpToken.approve(address(gauge), type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/CurvePoolBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport { ICampaignRemoteManager } from \"../interfaces/ICampaignRemoteManager.sol\";\n\n/// @title CurvePoolBooster\n/// @author Origin Protocol\n/// @notice Contract to manage interactions with VotemarketV2 for a dedicated Curve pool/gauge.\ncontract CurvePoolBooster is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n ////////////////////////////////////////////////////\n /// --- CONSTANTS && IMMUTABLES\n ////////////////////////////////////////////////////\n /// @notice Base fee for the contract, 100%\n uint16 public constant FEE_BASE = 10_000;\n\n /// @notice Address of the gauge to manage\n address public immutable gauge;\n\n /// @notice Address of the reward token\n address public immutable rewardToken;\n\n /// @notice Chain id of the target chain\n uint256 public immutable targetChainId;\n\n ////////////////////////////////////////////////////\n /// --- STORAGE\n ////////////////////////////////////////////////////\n /// @notice Fee in FEE_BASE unit payed when managing campaign.\n uint16 public fee;\n\n /// @notice Address of the fee collector\n address public feeCollector;\n\n /// @notice Address of the campaignRemoteManager linked to VotemarketV2\n address public campaignRemoteManager;\n\n /// @notice Address of votemarket in L2\n address public votemarket;\n\n /// @notice Id of the campaign created\n uint256 public campaignId;\n\n ////////////////////////////////////////////////////\n /// --- EVENTS\n ////////////////////////////////////////////////////\n event FeeUpdated(uint16 newFee);\n event FeeCollected(address feeCollector, uint256 feeAmount);\n event FeeCollectorUpdated(address newFeeCollector);\n event VotemarketUpdated(address newVotemarket);\n event CampaignRemoteManagerUpdated(address newCampaignRemoteManager);\n event CampaignCreated(\n address gauge,\n address rewardToken,\n uint256 maxRewardPerVote,\n uint256 totalRewardAmount\n );\n event CampaignIdUpdated(uint256 newId);\n event CampaignClosed(uint256 campaignId);\n event TotalRewardAmountUpdated(uint256 extraTotalRewardAmount);\n event NumberOfPeriodsUpdated(uint8 extraNumberOfPeriods);\n event RewardPerVoteUpdated(uint256 newMaxRewardPerVote);\n event TokensRescued(address token, uint256 amount, address receiver);\n\n ////////////////////////////////////////////////////\n /// --- CONSTRUCTOR && INITIALIZATION\n ////////////////////////////////////////////////////\n constructor(\n uint256 _targetChainId,\n address _rewardToken,\n address _gauge\n ) {\n targetChainId = _targetChainId;\n rewardToken = _rewardToken;\n gauge = _gauge;\n\n // Prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /// @notice initialize function, to set up initial internal state\n /// @param _strategist Address of the strategist\n /// @param _fee Fee in FEE_BASE unit payed when managing campaign\n /// @param _feeCollector Address of the fee collector\n function initialize(\n address _strategist,\n uint16 _fee,\n address _feeCollector,\n address _campaignRemoteManager,\n address _votemarket\n ) external onlyGovernor initializer {\n _setStrategistAddr(_strategist);\n _setFee(_fee);\n _setFeeCollector(_feeCollector);\n _setCampaignRemoteManager(_campaignRemoteManager);\n _setVotemarket(_votemarket);\n }\n\n ////////////////////////////////////////////////////\n /// --- MUTATIVE FUNCTIONS\n ////////////////////////////////////////////////////\n /// @notice Create a new campaign on VotemarketV2\n /// @dev This will use all token available in this contract\n /// @param numberOfPeriods Duration of the campaign in weeks\n /// @param maxRewardPerVote Maximum reward per vote to distribute, to avoid overspending\n /// @param blacklist List of addresses to exclude from the campaign\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function createCampaign(\n uint8 numberOfPeriods,\n uint256 maxRewardPerVote,\n address[] calldata blacklist,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId == 0, \"Campaign already created\");\n require(numberOfPeriods > 1, \"Invalid number of periods\");\n require(maxRewardPerVote > 0, \"Invalid reward per vote\");\n\n // Handle fee (if any)\n uint256 balanceSubFee = _handleFee();\n\n // Approve the balanceSubFee to the campaign manager\n IERC20(rewardToken).safeApprove(campaignRemoteManager, 0);\n IERC20(rewardToken).safeApprove(campaignRemoteManager, balanceSubFee);\n\n // Create a new campaign\n ICampaignRemoteManager(campaignRemoteManager).createCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignCreationParams({\n chainId: targetChainId,\n gauge: gauge,\n manager: address(this),\n rewardToken: rewardToken,\n numberOfPeriods: numberOfPeriods,\n maxRewardPerVote: maxRewardPerVote,\n totalRewardAmount: balanceSubFee,\n addresses: blacklist,\n hook: address(0),\n isWhitelist: false\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit CampaignCreated(\n gauge,\n rewardToken,\n maxRewardPerVote,\n balanceSubFee\n );\n }\n\n /// @notice Manage the total reward amount of the campaign\n /// @dev This function should be called after the campaign is created\n /// @dev This will use all the token available in this contract\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageTotalRewardAmount(\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n\n // Handle fee (if any)\n uint256 balanceSubFee = _handleFee();\n\n // Approve the total reward amount to the campaign manager\n IERC20(rewardToken).safeApprove(campaignRemoteManager, 0);\n IERC20(rewardToken).safeApprove(campaignRemoteManager, balanceSubFee);\n\n // Manage the campaign\n // https://github.com/stake-dao/votemarket-v2/blob/main/packages/votemarket/src/Votemarket.sol#L668\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: 0,\n totalRewardAmount: balanceSubFee,\n maxRewardPerVote: 0\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit TotalRewardAmountUpdated(balanceSubFee);\n }\n\n /// @notice Manage the number of periods of the campaign\n /// @dev This function should be called after the campaign is created\n /// @param extraNumberOfPeriods Number of additional periods (cannot be 0)\n /// that will be added to already existing amount of periods.\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageNumberOfPeriods(\n uint8 extraNumberOfPeriods,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n require(extraNumberOfPeriods > 0, \"Invalid number of periods\");\n\n // Manage the campaign\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: extraNumberOfPeriods,\n totalRewardAmount: 0,\n maxRewardPerVote: 0\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit NumberOfPeriodsUpdated(extraNumberOfPeriods);\n }\n\n /// @notice Manage the reward per vote of the campaign\n /// @dev This function should be called after the campaign is created\n /// @param newMaxRewardPerVote New maximum reward per vote\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageRewardPerVote(\n uint256 newMaxRewardPerVote,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n require(newMaxRewardPerVote > 0, \"Invalid reward per vote\");\n\n // Manage the campaign\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: 0,\n totalRewardAmount: 0,\n maxRewardPerVote: newMaxRewardPerVote\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit RewardPerVoteUpdated(newMaxRewardPerVote);\n }\n\n /// @notice Close the campaign.\n /// @dev This function only work on the L2 chain. Not on mainnet.\n /// @dev The _campaignId parameter is not related to the campaignId of this contract, allowing greater flexibility.\n /// @param _campaignId Id of the campaign to close\n function closeCampaign(\n uint256 _campaignId,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n ICampaignRemoteManager(campaignRemoteManager).closeCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignClosingParams({\n campaignId: campaignId\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n emit CampaignClosed(_campaignId);\n }\n\n /// @notice calculate the fee amount and transfer it to the feeCollector\n /// @return Balance after fee\n function _handleFee() internal returns (uint256) {\n // Cache current rewardToken balance\n uint256 balance = IERC20(rewardToken).balanceOf(address(this));\n require(balance > 0, \"No reward to manage\");\n\n uint256 feeAmount = (balance * fee) / FEE_BASE;\n\n // If there is a fee, transfer it to the feeCollector\n if (feeAmount > 0) {\n // Transfer the fee to the feeCollector\n IERC20(rewardToken).safeTransfer(feeCollector, feeAmount);\n emit FeeCollected(feeCollector, feeAmount);\n\n return IERC20(rewardToken).balanceOf(address(this));\n }\n\n // Return remaining balance\n return balance;\n }\n\n ////////////////////////////////////////////////////\n /// --- GOVERNANCE && OPERATION\n ////////////////////////////////////////////////////\n /// @notice Set the campaign id\n /// @dev Only callable by the governor or strategist\n /// @param _campaignId New campaign id\n function setCampaignId(uint256 _campaignId)\n external\n onlyGovernorOrStrategist\n {\n campaignId = _campaignId;\n emit CampaignIdUpdated(_campaignId);\n }\n\n /// @notice Rescue ETH from the contract\n /// @dev Only callable by the governor or strategist\n /// @param receiver Address to receive the ETH\n function rescueETH(address receiver)\n external\n nonReentrant\n onlyGovernorOrStrategist\n {\n require(receiver != address(0), \"Invalid receiver\");\n uint256 balance = address(this).balance;\n (bool success, ) = receiver.call{ value: balance }(\"\");\n require(success, \"Transfer failed\");\n emit TokensRescued(address(0), balance, receiver);\n }\n\n /// @notice Rescue ERC20 tokens from the contract\n /// @dev Only callable by the governor or strategist\n /// @param token Address of the token to rescue\n function rescueToken(address token, address receiver)\n external\n nonReentrant\n onlyGovernor\n {\n require(receiver != address(0), \"Invalid receiver\");\n uint256 balance = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransfer(receiver, balance);\n emit TokensRescued(token, balance, receiver);\n }\n\n /// @notice Set the fee\n /// @dev Only callable by the governor\n /// @param _fee New fee\n function setFee(uint16 _fee) external onlyGovernor {\n _setFee(_fee);\n }\n\n /// @notice Internal logic to set the fee\n function _setFee(uint16 _fee) internal {\n require(_fee <= FEE_BASE / 2, \"Fee too high\");\n fee = _fee;\n emit FeeUpdated(_fee);\n }\n\n /// @notice Set the fee collector\n /// @dev Only callable by the governor\n /// @param _feeCollector New fee collector\n function setFeeCollector(address _feeCollector) external onlyGovernor {\n _setFeeCollector(_feeCollector);\n }\n\n /// @notice Internal logic to set the fee collector\n function _setFeeCollector(address _feeCollector) internal {\n require(_feeCollector != address(0), \"Invalid fee collector\");\n feeCollector = _feeCollector;\n emit FeeCollectorUpdated(_feeCollector);\n }\n\n /// @notice Set the campaignRemoteManager\n /// @param _campaignRemoteManager New campaignRemoteManager address\n function setCampaignRemoteManager(address _campaignRemoteManager)\n external\n onlyGovernor\n {\n _setCampaignRemoteManager(_campaignRemoteManager);\n }\n\n /// @notice Internal logic to set the campaignRemoteManager\n /// @param _campaignRemoteManager New campaignRemoteManager address\n function _setCampaignRemoteManager(address _campaignRemoteManager)\n internal\n {\n require(\n _campaignRemoteManager != address(0),\n \"Invalid campaignRemoteManager\"\n );\n campaignRemoteManager = _campaignRemoteManager;\n emit CampaignRemoteManagerUpdated(_campaignRemoteManager);\n }\n\n /// @notice Set the votemarket address\n /// @param _votemarket New votemarket address\n function setVotemarket(address _votemarket) external onlyGovernor {\n _setVotemarket(_votemarket);\n }\n\n /// @notice Internal logic to set the votemarket address\n function _setVotemarket(address _votemarket) internal onlyGovernor {\n require(_votemarket != address(0), \"Invalid votemarket\");\n votemarket = _votemarket;\n emit VotemarketUpdated(_votemarket);\n }\n\n receive() external payable {}\n}\n" + }, + "contracts/strategies/Generalized4626Strategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Generalized 4626 Strategy\n * @notice Investment strategy for ERC-4626 Tokenized Vaults\n * @author Origin Protocol Inc\n */\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\ncontract Generalized4626Strategy is InitializableAbstractStrategy {\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecate_shareToken;\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecate_assetToken;\n\n IERC20 public immutable shareToken;\n IERC20 public immutable assetToken;\n\n // For future use\n uint256[50] private __gap;\n\n /**\n * @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n * and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n * @param _assetToken Address of the ERC-4626 asset token. eg frxETH or DAI\n */\n constructor(BaseStrategyConfig memory _baseConfig, address _assetToken)\n InitializableAbstractStrategy(_baseConfig)\n {\n shareToken = IERC20(_baseConfig.platformAddress);\n assetToken = IERC20(_assetToken);\n }\n\n function initialize() external virtual onlyGovernor initializer {\n address[] memory rewardTokens = new address[](0);\n address[] memory assets = new address[](1);\n address[] memory pTokens = new address[](1);\n\n assets[0] = address(assetToken);\n pTokens[0] = address(platformAddress);\n\n InitializableAbstractStrategy._initialize(\n rewardTokens,\n assets,\n pTokens\n );\n }\n\n /**\n * @dev Deposit assets by converting them to shares\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit assets by converting them to shares\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal virtual {\n require(_amount > 0, \"Must deposit something\");\n require(_asset == address(assetToken), \"Unexpected asset address\");\n\n // slither-disable-next-line unused-return\n IERC4626(platformAddress).deposit(_amount, address(this));\n emit Deposit(_asset, address(shareToken), _amount);\n }\n\n /**\n * @dev Deposit the entire balance of assetToken to gain shareToken\n */\n function depositAll() external virtual override onlyVault nonReentrant {\n uint256 balance = assetToken.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(assetToken), balance);\n }\n }\n\n /**\n * @dev Withdraw asset by burning shares\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n require(_asset == address(assetToken), \"Unexpected asset address\");\n\n // slither-disable-next-line unused-return\n IERC4626(platformAddress).withdraw(_amount, _recipient, address(this));\n emit Withdrawal(_asset, address(shareToken), _amount);\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / share tokens\n */\n function _abstractSetPToken(address, address) internal virtual override {\n _approveBase();\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll()\n external\n virtual\n override\n onlyVaultOrGovernor\n nonReentrant\n {\n uint256 shareBalance = shareToken.balanceOf(address(this));\n uint256 assetAmount = 0;\n if (shareBalance > 0) {\n assetAmount = IERC4626(platformAddress).redeem(\n shareBalance,\n vaultAddress,\n address(this)\n );\n emit Withdrawal(\n address(assetToken),\n address(shareToken),\n assetAmount\n );\n }\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(_asset == address(assetToken), \"Unexpected asset address\");\n /* We are intentionally not counting the amount of assetToken parked on the\n * contract toward the checkBalance. The deposit and withdraw functions\n * should not result in assetToken being unused and owned by this strategy\n * contract.\n */\n IERC4626 platform = IERC4626(platformAddress);\n return platform.previewRedeem(platform.balanceOf(address(this)));\n }\n\n /**\n * @notice Governor approves the ERC-4626 Tokenized Vault to spend the asset.\n */\n function safeApproveAllTokens() external override onlyGovernor {\n _approveBase();\n }\n\n function _approveBase() internal virtual {\n // Approval the asset to be transferred to the ERC-4626 Tokenized Vault.\n // Used by the ERC-4626 deposit() and mint() functions\n // slither-disable-next-line unused-return\n assetToken.approve(platformAddress, type(uint256).max);\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset)\n public\n view\n virtual\n override\n returns (bool)\n {\n return _asset == address(assetToken);\n }\n\n /**\n * @notice is not supported for this strategy as the asset and\n * ERC-4626 Tokenized Vault are set at deploy time.\n * @dev If the ERC-4626 Tokenized Vault needed to be changed, a new\n * contract would need to be deployed and the proxy updated.\n */\n function setPTokenAddress(address, address) external override onlyGovernor {\n revert(\"unsupported function\");\n }\n\n /**\n * @notice is not supported for this strategy as the asset and\n * ERC-4626 Tokenized Vault are set at deploy time.\n * @dev If the ERC-4626 Tokenized Vault needed to be changed, a new\n * contract would need to be deployed and the proxy updated.\n */\n function removePToken(uint256) external override onlyGovernor {\n revert(\"unsupported function\");\n }\n}\n" + }, + "contracts/strategies/Generalized4626USDTStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IUSDT {\n // Tether's approve does not return a bool like standard IERC20 contracts\n // slither-disable-next-line erc20-interface\n function approve(address _spender, uint256 _value) external;\n}\n\n/**\n * @title Generalized 4626 Strategy when asset is Tether USD (USDT)\n * @notice Investment strategy for ERC-4626 Tokenized Vaults for the USDT asset.\n * @author Origin Protocol Inc\n */\nimport { Generalized4626Strategy } from \"./Generalized4626Strategy.sol\";\n\ncontract Generalized4626USDTStrategy is Generalized4626Strategy {\n /**\n * @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n * and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n * @param _assetToken Address of the ERC-4626 asset token. eg frxETH or DAI\n */\n constructor(BaseStrategyConfig memory _baseConfig, address _assetToken)\n Generalized4626Strategy(_baseConfig, _assetToken)\n {}\n\n /// @dev Override for Tether as USDT does not return a bool on approve.\n /// Using assetToken.approve will fail as it expects a bool return value\n function _approveBase() internal virtual override {\n // Approval the asset to be transferred to the ERC-4626 Tokenized Vault.\n // Used by the ERC-4626 deposit() and mint() functions\n // slither-disable-next-line unused-return\n IUSDT(address(assetToken)).approve(platformAddress, type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/IAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface for Aaves Lending Pool\n * Documentation: https://developers.aave.com/#lendingpool\n */\ninterface IAaveLendingPool {\n /**\n * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.\n * - E.g. User deposits 100 USDC and gets in return 100 aUSDC\n * @param asset The address of the underlying asset to deposit\n * @param amount The amount to be deposited\n * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user\n * wants to receive them on his own wallet, or a different address if the beneficiary of aTokens\n * is a different wallet\n * @param referralCode Code used to register the integrator originating the operation, for potential rewards.\n * 0 if the action is executed directly by the user, without any middle-man\n **/\n function deposit(\n address asset,\n uint256 amount,\n address onBehalfOf,\n uint16 referralCode\n ) external;\n\n /**\n * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned\n * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC\n * @param asset The address of the underlying asset to withdraw\n * @param amount The underlying amount to be withdrawn\n * - Send the value type(uint256).max in order to withdraw the whole aToken balance\n * @param to Address that will receive the underlying, same as msg.sender if the user\n * wants to receive it on his own wallet, or a different address if the beneficiary is a\n * different wallet\n * @return The final amount withdrawn\n **/\n function withdraw(\n address asset,\n uint256 amount,\n address to\n ) external returns (uint256);\n}\n\n/**\n * @dev Interface for Aaves Lending Pool\n * Documentation: https://developers.aave.com/#lendingpooladdressesprovider\n */\ninterface ILendingPoolAddressesProvider {\n /**\n * @notice Get the current address for Aave LendingPool\n * @dev Lending pool is the core contract on which to call deposit\n */\n function getLendingPool() external view returns (address);\n}\n" + }, + "contracts/strategies/IAaveIncentivesController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IAaveIncentivesController {\n event RewardsAccrued(address indexed user, uint256 amount);\n\n event RewardsClaimed(\n address indexed user,\n address indexed to,\n uint256 amount\n );\n\n event RewardsClaimed(\n address indexed user,\n address indexed to,\n address indexed claimer,\n uint256 amount\n );\n\n event ClaimerSet(address indexed user, address indexed claimer);\n\n /*\n * @dev Returns the configuration of the distribution for a certain asset\n * @param asset The address of the reference asset of the distribution\n * @return The asset index, the emission per second and the last updated timestamp\n **/\n function getAssetData(address asset)\n external\n view\n returns (\n uint256,\n uint256,\n uint256\n );\n\n /**\n * @dev Whitelists an address to claim the rewards on behalf of another address\n * @param user The address of the user\n * @param claimer The address of the claimer\n */\n function setClaimer(address user, address claimer) external;\n\n /**\n * @dev Returns the whitelisted claimer for a certain address (0x0 if not set)\n * @param user The address of the user\n * @return The claimer address\n */\n function getClaimer(address user) external view returns (address);\n\n /**\n * @dev Configure assets for a certain rewards emission\n * @param assets The assets to incentivize\n * @param emissionsPerSecond The emission for each asset\n */\n function configureAssets(\n address[] calldata assets,\n uint256[] calldata emissionsPerSecond\n ) external;\n\n /**\n * @dev Called by the corresponding asset on any update that affects the rewards distribution\n * @param asset The address of the user\n * @param userBalance The balance of the user of the asset in the lending pool\n * @param totalSupply The total supply of the asset in the lending pool\n **/\n function handleAction(\n address asset,\n uint256 userBalance,\n uint256 totalSupply\n ) external;\n\n /**\n * @dev Returns the total of rewards of an user, already accrued + not yet accrued\n * @param user The address of the user\n * @return The rewards\n **/\n function getRewardsBalance(address[] calldata assets, address user)\n external\n view\n returns (uint256);\n\n /**\n * @dev Claims reward for an user, on all the assets of the lending pool,\n * accumulating the pending rewards\n * @param amount Amount of rewards to claim\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewards(\n address[] calldata assets,\n uint256 amount,\n address to\n ) external returns (uint256);\n\n /**\n * @dev Claims reward for an user on behalf, on all the assets of the\n * lending pool, accumulating the pending rewards. The caller must\n * be whitelisted via \"allowClaimOnBehalf\" function by the RewardsAdmin role manager\n * @param amount Amount of rewards to claim\n * @param user Address to check and claim rewards\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewardsOnBehalf(\n address[] calldata assets,\n uint256 amount,\n address user,\n address to\n ) external returns (uint256);\n\n /**\n * @dev returns the unclaimed rewards of the user\n * @param user the address of the user\n * @return the unclaimed user rewards\n */\n function getUserUnclaimedRewards(address user)\n external\n view\n returns (uint256);\n\n /**\n * @dev returns the unclaimed rewards of the user\n * @param user the address of the user\n * @param asset The asset to incentivize\n * @return the user index for the asset\n */\n function getUserAssetData(address user, address asset)\n external\n view\n returns (uint256);\n\n /**\n * @dev for backward compatibility with previous implementation of the Incentives controller\n */\n function REWARD_TOKEN() external view returns (address);\n\n /**\n * @dev for backward compatibility with previous implementation of the Incentives controller\n */\n function PRECISION() external view returns (uint8);\n\n /**\n * @dev Gets the distribution end timestamp of the emissions\n */\n function DISTRIBUTION_END() external view returns (uint256);\n}\n" + }, + "contracts/strategies/IAaveStakeToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IAaveStakedToken {\n function COOLDOWN_SECONDS() external returns (uint256);\n\n function UNSTAKE_WINDOW() external returns (uint256);\n\n function balanceOf(address addr) external returns (uint256);\n\n function redeem(address to, uint256 amount) external;\n\n function stakersCooldowns(address addr) external returns (uint256);\n\n function cooldown() external;\n}\n" + }, + "contracts/strategies/ICompound.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @dev Compound C Token interface\n * Documentation: https://compound.finance/developers/ctokens\n */\ninterface ICERC20 {\n /**\n * @notice The mint function transfers an asset into the protocol, which begins accumulating\n * interest based on the current Supply Rate for the asset. The user receives a quantity of\n * cTokens equal to the underlying tokens supplied, divided by the current Exchange Rate.\n * @param mintAmount The amount of the asset to be supplied, in units of the underlying asset.\n * @return 0 on success, otherwise an Error codes\n */\n function mint(uint256 mintAmount) external returns (uint256);\n\n /**\n * @notice Sender redeems cTokens in exchange for the underlying asset\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param redeemTokens The number of cTokens to redeem into underlying\n * @return uint 0=success, otherwise an error code.\n */\n function redeem(uint256 redeemTokens) external returns (uint256);\n\n /**\n * @notice The redeem underlying function converts cTokens into a specified quantity of the underlying\n * asset, and returns them to the user. The amount of cTokens redeemed is equal to the quantity of\n * underlying tokens received, divided by the current Exchange Rate. The amount redeemed must be less\n * than the user's Account Liquidity and the market's available liquidity.\n * @param redeemAmount The amount of underlying to be redeemed.\n * @return 0 on success, otherwise an error code.\n */\n function redeemUnderlying(uint256 redeemAmount) external returns (uint256);\n\n /**\n * @notice The user's underlying balance, representing their assets in the protocol, is equal to\n * the user's cToken balance multiplied by the Exchange Rate.\n * @param owner The account to get the underlying balance of.\n * @return The amount of underlying currently owned by the account.\n */\n function balanceOfUnderlying(address owner) external returns (uint256);\n\n /**\n * @notice Calculates the exchange rate from the underlying to the CToken\n * @dev This function does not accrue interest before calculating the exchange rate\n * @return Calculated exchange rate scaled by 1e18\n */\n function exchangeRateStored() external view returns (uint256);\n\n /**\n * @notice Get the token balance of the `owner`\n * @param owner The address of the account to query\n * @return The number of tokens owned by `owner`\n */\n function balanceOf(address owner) external view returns (uint256);\n\n /**\n * @notice Get the supply rate per block for supplying the token to Compound.\n */\n function supplyRatePerBlock() external view returns (uint256);\n\n /**\n * @notice Address of the Compound Comptroller.\n */\n function comptroller() external view returns (address);\n}\n" + }, + "contracts/strategies/IConvexDeposits.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IConvexDeposits {\n function deposit(\n uint256 _pid,\n uint256 _amount,\n bool _stake\n ) external returns (bool);\n\n function deposit(\n uint256 _amount,\n bool _lock,\n address _stakeAddress\n ) external;\n}\n" + }, + "contracts/strategies/ICRVMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICRVMinter {\n function mint(address gaugeAddress) external;\n}\n" + }, + "contracts/strategies/ICurveETHPoolV1.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurveETHPoolV1 {\n event AddLiquidity(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event ApplyNewFee(uint256 fee);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event CommitNewFee(uint256 new_fee);\n event RampA(\n uint256 old_A,\n uint256 new_A,\n uint256 initial_time,\n uint256 future_time\n );\n event RemoveLiquidity(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 token_supply\n );\n event RemoveLiquidityImbalance(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event RemoveLiquidityOne(\n address indexed provider,\n uint256 token_amount,\n uint256 coin_amount,\n uint256 token_supply\n );\n event StopRampA(uint256 A, uint256 t);\n event TokenExchange(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event Transfer(\n address indexed sender,\n address indexed receiver,\n uint256 value\n );\n\n function A() external view returns (uint256);\n\n function A_precise() external view returns (uint256);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_liquidity(uint256[2] memory _amounts, uint256 _min_mint_amount)\n external\n payable\n returns (uint256);\n\n function add_liquidity(\n uint256[2] memory _amounts,\n uint256 _min_mint_amount,\n address _receiver\n ) external payable returns (uint256);\n\n function admin_action_deadline() external view returns (uint256);\n\n function admin_balances(uint256 i) external view returns (uint256);\n\n function admin_fee() external view returns (uint256);\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function apply_new_fee() external;\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function balances(uint256 arg0) external view returns (uint256);\n\n function calc_token_amount(uint256[2] memory _amounts, bool _is_deposit)\n external\n view\n returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _burn_amount, int128 i)\n external\n view\n returns (uint256);\n\n function coins(uint256 arg0) external view returns (address);\n\n function commit_new_fee(uint256 _new_fee) external;\n\n function decimals() external view returns (uint256);\n\n function ema_price() external view returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external payable returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external payable returns (uint256);\n\n function fee() external view returns (uint256);\n\n function future_A() external view returns (uint256);\n\n function future_A_time() external view returns (uint256);\n\n function future_fee() external view returns (uint256);\n\n function get_balances() external view returns (uint256[2] memory);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n\n function get_p() external view returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function initial_A() external view returns (uint256);\n\n function initial_A_time() external view returns (uint256);\n\n function initialize(\n string memory _name,\n string memory _symbol,\n address[4] memory _coins,\n uint256[4] memory _rate_multipliers,\n uint256 _A,\n uint256 _fee\n ) external;\n\n function last_price() external view returns (uint256);\n\n function ma_exp_time() external view returns (uint256);\n\n function ma_last_time() external view returns (uint256);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function price_oracle() external view returns (uint256);\n\n function ramp_A(uint256 _future_A, uint256 _future_time) external;\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[2] memory _min_amounts\n ) external returns (uint256[2] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[2] memory _min_amounts,\n address _receiver\n ) external returns (uint256[2] memory);\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burn_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burn_amount,\n address _receiver\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received,\n address _receiver\n ) external returns (uint256);\n\n function set_ma_exp_time(uint256 _ma_exp_time) external;\n\n function stop_ramp_A() external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw_admin_fees() external;\n}\n" + }, + "contracts/strategies/ICurveGauge.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurveGauge {\n function balanceOf(address account) external view returns (uint256);\n\n function deposit(uint256 value, address account) external;\n\n function withdraw(uint256 value) external;\n}\n" + }, + "contracts/strategies/ICurveMetaPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface ICurveMetaPool {\n function add_liquidity(uint256[2] calldata amounts, uint256 min_mint_amount)\n external\n returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function remove_liquidity(uint256 _amount, uint256[2] calldata min_amounts)\n external\n returns (uint256[2] calldata);\n\n function remove_liquidity_one_coin(\n uint256 _token_amount,\n int128 i,\n uint256 min_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[2] calldata amounts,\n uint256 max_burn_amount\n ) external returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _token_amount, int128 i)\n external\n view\n returns (uint256);\n\n function balances(uint256 i) external view returns (uint256);\n\n function calc_token_amount(uint256[2] calldata amounts, bool deposit)\n external\n view\n returns (uint256);\n\n function base_pool() external view returns (address);\n\n function fee() external view returns (uint256);\n\n function coins(uint256 i) external view returns (address);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 dx,\n uint256 min_dy\n ) external returns (uint256);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n}\n" + }, + "contracts/strategies/ICurvePool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurvePool {\n function get_virtual_price() external view returns (uint256);\n\n function add_liquidity(uint256[3] calldata _amounts, uint256 _min) external;\n\n function balances(uint256) external view returns (uint256);\n\n function calc_token_amount(uint256[3] calldata _amounts, bool _deposit)\n external\n returns (uint256);\n\n function fee() external view returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _amount,\n int128 _index,\n uint256 _minAmount\n ) external;\n\n function remove_liquidity(\n uint256 _amount,\n uint256[3] calldata _minWithdrawAmounts\n ) external;\n\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n external\n view\n returns (uint256);\n\n function exchange(\n uint256 i,\n uint256 j,\n uint256 dx,\n uint256 min_dy\n ) external returns (uint256);\n\n function coins(uint256 _index) external view returns (address);\n\n function remove_liquidity_imbalance(\n uint256[3] calldata _amounts,\n uint256 maxBurnAmount\n ) external;\n}\n" + }, + "contracts/strategies/IRewardStaking.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IRewardStaking {\n function stakeFor(address, uint256) external;\n\n function stake(uint256) external;\n\n function withdraw(uint256 amount, bool claim) external;\n\n function withdrawAndUnwrap(uint256 amount, bool claim) external;\n\n function earned(address account) external view returns (uint256);\n\n function getReward() external;\n\n function getReward(address _account, bool _claimExtras) external;\n\n function extraRewardsLength() external returns (uint256);\n\n function extraRewards(uint256 _pid) external returns (address);\n\n function rewardToken() external returns (address);\n\n function balanceOf(address account) external view returns (uint256);\n}\n" + }, + "contracts/strategies/MorphoAaveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Morpho Aave Strategy\n * @notice Investment strategy for investing stablecoins via Morpho (Aave)\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { IMorpho } from \"../interfaces/morpho/IMorpho.sol\";\nimport { ILens } from \"../interfaces/morpho/ILens.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MorphoAaveStrategy is InitializableAbstractStrategy {\n address public constant MORPHO = 0x777777c9898D384F785Ee44Acfe945efDFf5f3E0;\n address public constant LENS = 0x507fA343d0A90786d86C7cd885f5C49263A91FF4;\n\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @dev Initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Approve the spending of all assets by main Morpho contract,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address asset = assetsMapped[i];\n\n // Safe approval\n IERC20(asset).safeApprove(MORPHO, 0);\n IERC20(asset).safeApprove(MORPHO, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset\n * We need to approve and allow Morpho to move them\n * @param _asset Address of the asset to approve\n * @param _pToken The pToken for the approval\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n IERC20(_asset).safeApprove(MORPHO, 0);\n IERC20(_asset).safeApprove(MORPHO, type(uint256).max);\n }\n\n /**\n * @dev Collect accumulated rewards and send them to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Morpho Aave-v2 doesn't distribute reward tokens\n // solhint-disable-next-line max-line-length\n // Ref: https://developers.morpho.xyz/interact-with-morpho/get-started/interact-with-morpho/claim-rewards#morpho-aave-v2\n }\n\n /**\n * @dev Get the amount of rewards pending to be collected from the protocol\n */\n function getPendingRewards() external view returns (uint256 balance) {\n // Morpho Aave-v2 doesn't distribute reward tokens\n // solhint-disable-next-line max-line-length\n // Ref: https://developers.morpho.xyz/interact-with-morpho/get-started/interact-with-morpho/claim-rewards#morpho-aave-v2\n return 0;\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n\n address pToken = address(_getPTokenFor(_asset));\n\n IMorpho(MORPHO).supply(\n pToken,\n address(this), // the address of the user you want to supply on behalf of\n _amount\n );\n emit Deposit(_asset, pToken, _amount);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Morpho\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Morpho\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n address pToken = address(_getPTokenFor(_asset));\n\n IMorpho(MORPHO).withdraw(pToken, _amount);\n emit Withdrawal(_asset, pToken, _amount);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = _checkBalance(assetsMapped[i]);\n if (balance > 0) {\n _withdraw(vaultAddress, assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Return total value of an asset held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n return _checkBalance(_asset);\n }\n\n function _checkBalance(address _asset)\n internal\n view\n returns (uint256 balance)\n {\n address pToken = address(_getPTokenFor(_asset));\n\n // Total value represented by decimal position of underlying token\n (, , balance) = ILens(LENS).getCurrentSupplyBalanceInOf(\n pToken,\n address(this)\n );\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Get the pToken wrapped in the IERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return pToken Corresponding pToken to this asset\n */\n function _getPTokenFor(address _asset) internal view returns (IERC20) {\n address pToken = assetToPToken[_asset];\n require(pToken != address(0), \"pToken does not exist\");\n return IERC20(pToken);\n }\n}\n" + }, + "contracts/strategies/MorphoCompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Morpho Compound Strategy\n * @notice Investment strategy for investing stablecoins via Morpho (Compound)\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, AbstractCompoundStrategy, InitializableAbstractStrategy } from \"./AbstractCompoundStrategy.sol\";\nimport { IMorpho } from \"../interfaces/morpho/IMorpho.sol\";\nimport { ILens } from \"../interfaces/morpho/ILens.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MorphoCompoundStrategy is AbstractCompoundStrategy {\n address public constant MORPHO = 0x8888882f8f843896699869179fB6E4f7e3B58888;\n address public constant LENS = 0x930f1b46e1D081Ec1524efD95752bE3eCe51EF67;\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @dev Initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Approve the spending of all assets by main Morpho contract,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address asset = assetsMapped[i];\n\n // Safe approval\n IERC20(asset).safeApprove(MORPHO, 0);\n IERC20(asset).safeApprove(MORPHO, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset\n * We need to approve and allow Morpho to move them\n * @param _asset Address of the asset to approve\n * @param _pToken The pToken for the approval\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n IERC20(_asset).safeApprove(MORPHO, 0);\n IERC20(_asset).safeApprove(MORPHO, type(uint256).max);\n }\n\n /**\n * @dev Collect accumulated rewards and send them to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n /**\n * Gas considerations. We could query Morpho LENS's `getUserUnclaimedRewards` for each\n * cToken separately and only claimRewards where it is economically feasible. Each call\n * (out of 3) costs ~60k gas extra.\n *\n * Each extra cToken in the `poolTokens` of `claimRewards` function makes that call\n * 89-120k more expensive gas wise.\n *\n * With Lens query in case where:\n * - there is only 1 reward token to collect. Net gas usage in best case would be\n * 3*60 - 2*120 = -60k -> saving 60k gas\n * - there are 2 reward tokens to collect. Net gas usage in best case would be\n * 3*60 - 120 = 60k -> more expensive for 60k gas\n * - there are 3 reward tokens to collect. Net gas usage in best case would be\n * 3*60 = 180k -> more expensive for 180k gas\n *\n * For the above reasoning such \"optimization\" is not implemented\n */\n\n address[] memory poolTokens = new address[](assetsMapped.length);\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n poolTokens[i] = assetToPToken[assetsMapped[i]];\n }\n\n // slither-disable-next-line unused-return\n IMorpho(MORPHO).claimRewards(\n poolTokens, // The addresses of the underlying protocol's pools to claim rewards from\n false // Whether to trade the accrued rewards for MORPHO token, with a premium\n );\n\n // Transfer COMP to Harvester\n IERC20 rewardToken = IERC20(rewardTokenAddresses[0]);\n uint256 balance = rewardToken.balanceOf(address(this));\n emit RewardTokenCollected(\n harvesterAddress,\n rewardTokenAddresses[0],\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n\n /**\n * @dev Get the amount of rewards pending to be collected from the protocol\n */\n function getPendingRewards() external view returns (uint256 balance) {\n address[] memory poolTokens = new address[](assetsMapped.length);\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n poolTokens[i] = assetToPToken[assetsMapped[i]];\n }\n\n return ILens(LENS).getUserUnclaimedRewards(poolTokens, address(this));\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n\n IMorpho(MORPHO).supply(\n address(_getCTokenFor(_asset)),\n address(this), // the address of the user you want to supply on behalf of\n _amount\n );\n emit Deposit(_asset, address(_getCTokenFor(_asset)), _amount);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Morpho\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Morpho\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n address pToken = assetToPToken[_asset];\n\n IMorpho(MORPHO).withdraw(pToken, _amount);\n emit Withdrawal(_asset, address(_getCTokenFor(_asset)), _amount);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = _checkBalance(assetsMapped[i]);\n if (balance > 0) {\n _withdraw(vaultAddress, assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Return total value of an asset held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n return _checkBalance(_asset);\n }\n\n function _checkBalance(address _asset)\n internal\n view\n returns (uint256 balance)\n {\n address pToken = assetToPToken[_asset];\n\n // Total value represented by decimal position of underlying token\n (, , balance) = ILens(LENS).getCurrentSupplyBalanceInOf(\n pToken,\n address(this)\n );\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with\n /// `platformAddress` not used so empty address\n /// `vaultAddress` the address of the OETH Vault contract\n /// @param _wethAddress Address of the WETH Token contract\n /// @param _ssvToken Address of the SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconProofs,\n _beaconGenesisTimestamp\n )\n {\n SSV_TOKEN = _ssvToken;\n\n // Make sure nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// @param _rewardTokenAddresses Not used so empty array\n /// @param _assets Not used so empty array\n /// @param _pTokens Not used so empty array\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n safeApproveAllTokens();\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH that was transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// @param _recipient Address to receive withdrawn assets.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH to withdraw.\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(WETH, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Accounts for all the assets managed by this strategy which includes:\n /// 1. The current WETH in this strategy contract\n /// 2. The last verified ETH balance, total deposits and total validator balances\n /// @param _asset Address of WETH asset.\n /// @return balance Total value in ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by the strategy.\n /// @param _asset The address of the WETH token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for validator registration.\n function safeApproveAllTokens() public override {\n // Approves the SSV Network contract to transfer SSV tokens when validators are registered\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n * The new ETH will be accounted for in `checkBalance` after the next snapBalances and verifyBalances txs.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n /// @notice is not supported for this strategy as there is no platform token.\n function setPTokenAddress(address, address) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @notice is not supported for this strategy as there is no platform token.\n function removePToken(uint256) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @dev This strategy does not use a platform token like the old Aave and Compound strategies.\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instead of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract.\n /// Withdrawals from validators also accumulate as ETH in this strategy contract.\n /// It's too complex to separate the rewards from withdrawals so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can now regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingView.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/**\n * @title Viewing contract for the Compounding Staking Strategy.\n * @author Origin Protocol Inc\n */\ncontract CompoundingStakingStrategyView {\n /// @notice The address of the Compounding Staking Strategy contract\n CompoundingValidatorManager public immutable stakingStrategy;\n\n constructor(address _stakingStrategy) {\n stakingStrategy = CompoundingValidatorManager(_stakingStrategy);\n }\n\n struct ValidatorView {\n bytes32 pubKeyHash;\n uint64 index;\n CompoundingValidatorManager.ValidatorState state;\n }\n\n struct DepositView {\n uint256 depositID;\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint256 withdrawableEpoch;\n }\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return validators An array of `ValidatorView` containing the public key hash, validator index and state.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorView[] memory validators)\n {\n uint256 validatorCount = stakingStrategy.verifiedValidatorsLength();\n validators = new ValidatorView[](validatorCount);\n for (uint256 i = 0; i < validatorCount; ++i) {\n bytes32 pubKeyHash = stakingStrategy.verifiedValidators(i);\n (\n CompoundingValidatorManager.ValidatorState state,\n uint64 index\n ) = stakingStrategy.validator(pubKeyHash);\n validators[i] = ValidatorView({\n pubKeyHash: pubKeyHash,\n index: index,\n state: state\n });\n }\n }\n\n /// @notice Returns the deposits that are still to be verified.\n /// These may or may not have been processed by the beacon chain.\n /// @return pendingDeposits An array of `DepositView` containing the deposit ID, public key hash,\n /// amount in Gwei and the slot of the deposit.\n function getPendingDeposits()\n external\n view\n returns (DepositView[] memory pendingDeposits)\n {\n uint256 depositsCount = stakingStrategy.depositListLength();\n pendingDeposits = new DepositView[](depositsCount);\n for (uint256 i = 0; i < depositsCount; ++i) {\n (\n bytes32 pubKeyHash,\n uint64 amountGwei,\n uint64 slot,\n ,\n ,\n uint256 withdrawableEpoch\n ) = stakingStrategy.deposits(stakingStrategy.depositList(i));\n pendingDeposits[i] = DepositView({\n depositID: stakingStrategy.depositList(i),\n pubKeyHash: pubKeyHash,\n amountGwei: amountGwei,\n slot: slot,\n withdrawableEpoch: withdrawableEpoch\n });\n }\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable {\n using SafeERC20 for IERC20;\n\n /// @dev The amount of ETH in wei that is required for a deposit to a new validator.\n /// Initially this is 32 ETH, but will be reduced to 1 ETH after P2P's APIs have been updated\n /// to support deposits of 1 ETH.\n uint256 internal constant DEPOSIT_AMOUNT_WEI = 32 ether;\n /// @dev The maximum number of deposits that are waiting to be verified as processed on the beacon chain.\n uint256 internal constant MAX_DEPOSITS = 12;\n /// @dev The maximum number of validators that can be verified.\n uint256 internal constant MAX_VERIFIED_VALIDATORS = 48;\n /// @dev The default withdrawable epoch value on the Beacon chain.\n /// A value in the far future means the validator is not exiting.\n uint64 internal constant FAR_FUTURE_EPOCH = type(uint64).max;\n /// @dev The number of seconds between each beacon chain slot.\n uint64 internal constant SLOT_DURATION = 12;\n /// @dev The number of slots in each beacon chain epoch.\n uint64 internal constant SLOTS_PER_EPOCH = 32;\n /// @dev Minimum time in seconds to allow snapped balances to be verified.\n /// Set to 1 epoch as the pending deposits only changes every epoch.\n /// That's also enough time to generate the proofs and call `verifyBalances`.\n uint64 internal constant SNAP_BALANCES_DELAY =\n SLOTS_PER_EPOCH * SLOT_DURATION;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n /// @notice The timestamp of the Beacon chain genesis.\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\n uint64 public immutable BEACON_GENESIS_TIMESTAMP;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// @notice Deposit data for new compounding validators.\n /// @dev A `VERIFIED` deposit can mean 3 separate things:\n /// - a deposit has been processed by the beacon chain and shall be included in the\n /// balance of the next verifyBalances call\n /// - a deposit has been done to a slashed validator and has probably been recovered\n /// back to this strategy. Probably because we can not know for certain. This contract\n /// only detects when the validator has passed its withdrawal epoch. It is close to impossible\n /// to prove with Merkle Proofs that the postponed deposit this contract is responsible for\n /// creating is not present anymore in BeaconChain.state.pending_deposits. This in effect\n /// means that there might be a period where this contract thinks the deposit has been already\n /// returned as ETH balance before it happens. This will result in some days (or weeks)\n /// -> depending on the size of deposit queue of showing a deficit when calling `checkBalance`.\n /// As this only offsets the yield and doesn't cause a critical double-counting we are not addressing\n /// this issue.\n /// - A deposit has been done to the validator, but our deposit has been front run by a malicious\n /// actor. Funds in the deposit this contract makes are not recoverable.\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified\n }\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountGwei Amount of ETH in gwei that has been deposited to the beacon chain deposit contract\n /// @param slot The beacon chain slot number when the deposit has been made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either UNKNOWN, PENDING or VERIFIED\n /// @param withdrawableEpoch The withdrawableEpoch of the validator which is being deposited to.\n /// At deposit time this is set to max default value (FAR_FUTURE_EPOCH). If a deposit has\n /// made to a slashed validator the `withdrawableEpoch` will be set to the epoch of that\n /// validator.\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint32 depositIndex;\n DepositStatus status;\n uint64 withdrawableEpoch;\n }\n /// @notice Restricts to only one deposit to an unverified validator at a time.\n /// This is to limit front-running attacks of deposits to the beacon chain contract.\n ///\n /// @dev The value is set to true when a deposit to a new validator has been done that has\n /// not yet be verified.\n bool public firstDeposit;\n /// @notice Unique identifier of the next validator deposit.\n uint128 public nextDepositID;\n /// @notice Mapping of the deposit ID to the deposit data\n mapping(uint256 => DepositData) public deposits;\n /// @notice List of strategy deposit IDs to a validator.\n /// The list can be for deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n /// The list may not be ordered by time of deposit.\n /// Removed deposits will move the last deposit to the removed index.\n uint256[] public depositList;\n\n enum ValidatorState {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITING, // The validator has been requested to exit or has been verified as forced exit\n EXITED, // The validator has been verified to have a zero balance\n REMOVED, // validator has funds withdrawn to this strategy contract and is removed from the SSV\n INVALID // The validator has been front-run and the withdrawal address is not this strategy\n }\n\n // Validator data\n struct ValidatorData {\n ValidatorState state; // The state of the validator known to this contract\n uint64 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n bytes32[] public verifiedValidators;\n /// @notice Mapping of the hash of the validator's public key to the validator state and index.\n /// Uses the Beacon chain hashing for BLSPubkey which is sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => ValidatorData) public validator;\n\n /// @param blockRoot Beacon chain block root of the snapshot\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n bytes32 blockRoot;\n uint64 timestamp;\n uint128 ethBalance;\n }\n /// @notice Mapping of the block root to the balances at that slot\n Balances public snappedBalance;\n /// @notice The last verified ETH balance of the strategy\n uint256 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[50] private __gap;\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event FirstDepositReset();\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n uint256 indexed depositID,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint64 indexed validatorIndex\n );\n event ValidatorInvalid(bytes32 indexed pubKeyHash);\n event DepositVerified(uint256 indexed depositID, uint256 amountWei);\n event DepositToValidatorExiting(\n uint256 indexed depositID,\n uint256 amountWei,\n uint64 withdrawableEpoch\n );\n event DepositValidatorExited(uint256 indexed depositID, uint256 amountWei);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(bytes32 indexed blockRoot, uint256 ethBalance);\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 ethBalance\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_PROOFS = _beaconProofs;\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\n\n require(\n block.timestamp > _beaconGenesisTimestamp,\n \"Invalid genesis timestamp\"\n );\n }\n\n /**\n *\n * Admin Functions\n *\n */\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Reset the `firstDeposit` flag to false so deposits to unverified validators can be made again.\n function resetFirstDeposit() external onlyGovernor {\n require(firstDeposit, \"No first deposit\");\n\n firstDeposit = false;\n\n emit FirstDepositReset();\n }\n\n /**\n *\n * Validator Management\n *\n */\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validator[pubKeyHash].state == ValidatorState.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validator[pubKeyHash].state = ValidatorState.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n struct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n }\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validatorStakeData validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validatorStakeData,\n uint64 depositAmountGwei\n ) external onlyRegistrator {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(depositList.length < MAX_DEPOSITS, \"Max deposits\");\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validatorStakeData.pubkey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.VERIFIED),\n \"Not registered or verified\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n if (currentState == ValidatorState.REGISTERED) {\n // Can only have one pending deposit to an unverified validator at a time.\n // This is to limit front-running deposit attacks to a single deposit.\n // The exiting deposit needs to be verified before another deposit can be made.\n // If there was a front-running attack, the validator needs to be verified as invalid\n // and the Governor calls `resetFirstDeposit` to set `firstDeposit` to false.\n require(!firstDeposit, \"Existing first deposit\");\n // Limits the amount of ETH that can be at risk from a front-running deposit attack.\n require(\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n // Limits the number of validator balance proofs to verifyBalances\n require(\n verifiedValidators.length + 1 < MAX_VERIFIED_VALIDATORS,\n \"Max validators\"\n );\n\n // Flag a deposit to an unverified validator so only no other deposits can be made\n // to an unverified validator.\n firstDeposit = true;\n }\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == ValidatorState.REGISTERED) {\n validator[pubKeyHash].state = ValidatorState.STAKED;\n }\n\n /// After the Pectra upgrade the validators have a new restriction when proposing\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\n /// forward. Each slot is created at strict 12 second intervals and those slots can\n /// either have blocks attached to them or not. This way using the block.timestamp\n /// the slot number can easily be calculated.\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\n BEACON_GENESIS_TIMESTAMP) / SLOT_DURATION;\n\n // Store the deposit data for verifyDeposit and verifyBalances\n uint256 depositID = nextDepositID++;\n deposits[depositID] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n slot: depositSlot,\n depositIndex: SafeCast.toUint32(depositList.length),\n status: DepositStatus.PENDING,\n withdrawableEpoch: FAR_FUTURE_EPOCH\n });\n depositList.push(depositID);\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validatorStakeData.pubkey,\n withdrawalCredentials,\n validatorStakeData.signature,\n validatorStakeData.depositDataRoot\n );\n\n emit ETHStaked(\n pubKeyHash,\n depositID,\n validatorStakeData.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then only the amount in excess of 32 ETH will be withdrawn.\n /// Only the Registrator can call this function.\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\n /// If no ETH balance, the tx will revert.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n payable\n onlyRegistrator\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n require(\n currentState == ValidatorState.VERIFIED,\n \"Validator not verified\"\n );\n\n // If a full withdrawal (validator exit)\n if (amountGwei == 0) {\n // Store the validator state as exiting so no more deposits can be made to it.\n validator[pubKeyHash].state = ValidatorState.EXITING;\n }\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove the validator from the SSV Cluster after:\n /// - the validator has been exited from `validatorWithdrawal` or slashed\n /// - the validator has incorrectly registered and can not be staked to\n /// - the initial deposit was front-run and the withdrawal address is not this strategy's address.\n /// Make sure `validatorWithdrawal` is called with a zero amount and the validator has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.EXITED ||\n currentState == ValidatorState.INVALID,\n \"Validator not regd or exited\"\n );\n\n validator[pubKeyHash].state = ValidatorState.REMOVED;\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /**\n *\n * SSV Management\n *\n */\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be withdrawn from the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /**\n *\n * Beacon Chain Proofs\n *\n */\n\n /// @notice Verifies a validator's index to its public key.\n /// Adds to the list of verified validators if the validator's withdrawal address is this strategy's address.\n /// Marks the validator as invalid and removes the deposit if the withdrawal address is not this strategy's address.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param withdrawalAddress The withdrawal address of the validator which should be this strategy's address.\n /// If the withdrawal address is not this strategy's address, the initial deposit was front-run\n /// and the validator is marked as invalid.\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n address withdrawalAddress,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validator[pubKeyHash].state == ValidatorState.STAKED,\n \"Validator not staked\"\n );\n\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key.\n // Also verify the validator's withdrawal credential points to the `withdrawalAddress`.\n IBeaconProofs(BEACON_PROOFS).verifyValidator(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n withdrawalAddress\n );\n\n // Store the validator state as verified\n validator[pubKeyHash] = ValidatorData({\n state: ValidatorState.VERIFIED,\n index: validatorIndex\n });\n\n // If the initial deposit was front-run and the withdrawal address is not this strategy\n if (withdrawalAddress != address(this)) {\n // override the validator state\n validator[pubKeyHash].state = ValidatorState.INVALID;\n\n // Find and remove the deposit as the funds can not be recovered\n uint256 depositCount = depositList.length;\n for (uint256 i = 0; i < depositCount; i++) {\n DepositData memory deposit = deposits[depositList[i]];\n if (deposit.pubKeyHash == pubKeyHash) {\n _removeDeposit(depositList[i], deposit);\n break;\n }\n }\n\n // Leave the `firstDeposit` flag as true so no more deposits to unverified validators can be made.\n // The Governor has to reset the `firstDeposit` to false before another deposit to\n // an unverified validator can be made.\n // The Governor can set a new `validatorRegistrator` if they suspect it has been compromised.\n\n emit ValidatorInvalid(pubKeyHash);\n return;\n }\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(pubKeyHash);\n\n // Reset the firstDeposit flag as the first deposit to an unverified validator has been verified.\n firstDeposit = false;\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n struct FirstPendingDepositProofData {\n uint64 slot;\n uint64 validatorIndex;\n bytes32 pubKeyHash;\n bytes pendingDepositPubKeyProof;\n bytes withdrawableEpochProof;\n bytes validatorPubKeyProof;\n }\n\n struct StrategyValidatorProofData {\n uint64 withdrawableEpoch;\n bytes withdrawableEpochProof;\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n ///\n /// Important: this function has a limitation where `depositProcessedSlot` and `firstDepositValidatorCreatedSlot`\n /// that is passed by the off-chain verifier requires a slot immediately after them to propose a block otherwise\n /// the `BeaconRoots.parentBlockRoot` will fail. This shouldn't be a problem, since by the current behaviour of\n /// beacon chain only 1%-3% slots don't propose a block.\n /// @param depositID The deposit ID emitted in `ETHStaked` from the `stakeEth` function.\n /// @param depositProcessedSlot Any slot on or after the strategy's deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\n /// set for the next block timestamp in 12 seconds time.\n /// @param firstDepositValidatorCreatedSlot The slot on or after when the validator of the first pending deposit\n /// was created on the beacon chain. This is used to verify the validator has not exited.\n /// If the first pending deposit is to an exiting validator, this can be the same slot as `depositProcessedSlot`.\n /// If the first pending deposit is to a new validator, the slot will have to be in the next epoch as the\n /// pending deposits are only processed at the start of each epoch.\n /// @param firstPendingDeposit a `FirstPendingDepositProofData` struct containing:\n /// - slot: The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero is a good choice.\n /// - validatorIndex: The index of the validator of the first pending deposit.\n /// Can be anything if the deposit queue is empty, but zero is a good choice.\n /// - pubKeyHash: The hash of the public key of the validator of the first pending deposit.\n /// Can be anything if the deposit queue is empty, but 32 zero bytes is a good choice\n /// - pendingDepositPubKeyProof: The merkle proof of the first pending deposit to the beacon block root.\n /// Can be either:\n /// * 40 witness hashes for BeaconBlock.state.PendingDeposits[0].pubKey when the deposit queue is not empty.\n /// * 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the first pending deposit's validator\n /// to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// - validatorPubKeyProof The merkle proof for the public key of the first pending deposit's validator\n /// to the this witness hash of withdrawableEpochProof.\n /// This is 2 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param strategyValidatorData a `StrategyValidatorProofData` struct containing:\n /// - withdrawableEpoch: The withdrawable epoch of the validator the strategy is depositing to.\n /// - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the validator the strategy\n /// is depositing to, to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n uint256 depositID,\n uint64 depositProcessedSlot,\n uint64 firstDepositValidatorCreatedSlot,\n FirstPendingDepositProofData calldata firstPendingDeposit,\n StrategyValidatorProofData calldata strategyValidatorData\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositID];\n ValidatorData memory strategyValidator = validator[deposit.pubKeyHash];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n strategyValidator.state == ValidatorState.VERIFIED,\n \"Validator not verified\"\n );\n // The verification slot must be after the deposit's slot.\n // This is needed for when the deposit queue is empty.\n require(deposit.slot < depositProcessedSlot, \"Slot not after deposit\");\n require(\n depositProcessedSlot <= firstDepositValidatorCreatedSlot,\n \"Invalid verification slots\"\n );\n\n // Get the parent beacon block root of the next block which is the block root of the deposit verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 depositBlockRoot = BeaconRoots.parentBlockRoot(\n _calcNextBlockTimestamp(depositProcessedSlot)\n );\n\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n depositBlockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.pubKeyHash,\n firstPendingDeposit.pendingDepositPubKeyProof\n );\n\n // If the deposit queue is not empty\n if (!isDepositQueueEmpty) {\n // Get the parent beacon block root of the next block which is\n // the block root of the validator verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 validatorBlockRoot = BeaconRoots.parentBlockRoot(\n _calcNextBlockTimestamp(firstDepositValidatorCreatedSlot)\n );\n\n // Verify the validator of the first pending deposit is not exiting.\n // If it is exiting we can't be sure this deposit has not been postponed in the deposit queue.\n // Hence we can not verify if the strategy's deposit has been processed or not.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n validatorBlockRoot,\n firstPendingDeposit.validatorIndex,\n firstPendingDeposit.pubKeyHash,\n FAR_FUTURE_EPOCH,\n firstPendingDeposit.withdrawableEpochProof,\n firstPendingDeposit.validatorPubKeyProof\n );\n }\n\n // Verify the withdrawableEpoch on the validator of the strategy's deposit\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n depositBlockRoot,\n strategyValidator.index,\n strategyValidatorData.withdrawableEpoch,\n strategyValidatorData.withdrawableEpochProof\n );\n\n // If the validator is exiting because it has been slashed\n if (strategyValidatorData.withdrawableEpoch != FAR_FUTURE_EPOCH) {\n // Store the exit epoch in the deposit data\n deposit.withdrawableEpoch = strategyValidatorData.withdrawableEpoch;\n\n emit DepositToValidatorExiting(\n depositID,\n uint256(deposit.amountGwei) * 1 gwei,\n strategyValidatorData.withdrawableEpoch\n );\n\n validator[deposit.pubKeyHash].state = ValidatorState.EXITING;\n\n // Leave the deposit status as PENDING\n return;\n }\n\n // solhint-disable max-line-length\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the deposit queue is empty then our deposit must have been processed on the beacon chain.\n // The deposit slot can be zero for validators consolidating to a compounding validator or 0x01 validator\n // being promoted to a compounding one. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // We can not guarantee that the deposit has been processed in that case.\n // solhint-enable max-line-length\n require(\n deposit.slot < firstPendingDeposit.slot || isDepositQueueEmpty,\n \"Deposit likely not processed\"\n );\n\n // Remove the deposit now it has been verified as processed on the beacon chain.\n _removeDeposit(depositID, deposit);\n\n emit DepositVerified(depositID, uint256(deposit.amountGwei) * 1 gwei);\n }\n\n function _removeDeposit(uint256 depositID, DepositData memory deposit)\n internal\n {\n // After verifying the proof, update the contract storage\n deposits[depositID].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n uint256 lastDeposit = depositList[depositList.length - 1];\n depositList[deposit.depositIndex] = lastDeposit;\n deposits[lastDeposit].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositList.pop();\n }\n\n /// @dev Calculates the timestamp of the next execution block from the given slot.\n /// @param slot The beacon chain slot number used for merkle proof verification.\n function _calcNextBlockTimestamp(uint64 slot)\n internal\n view\n returns (uint64)\n {\n // Calculate the next block timestamp from the slot.\n return SLOT_DURATION * slot + BEACON_GENESIS_TIMESTAMP + SLOT_DURATION;\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block and beacon block root\n /// of the slot that is associated with the previous block.\n ///\n /// When snapping / verifying balance it is of a high importance that there is no\n /// miss-match in respect to ETH that is held by the contract and balances that are\n /// verified on the validators.\n ///\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\n /// constructing a block on the beacon chain consist of:\n /// - process_withdrawals: ETH is deducted from the validator's balance\n /// - process_execution_payload: immediately after the previous step executing all the\n /// transactions\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\n /// contained in the withdrawal credentials of the exited validators\n ///\n /// That means that balance increases which are part of the post-block execution state are\n /// done within the block, but the transaction that are contained within that block can not\n /// see / interact with the balance from the exited validators. Only transactions in the\n /// next block can do that.\n ///\n /// When snap balances is performed the state of the chain is snapped across 2 separate\n /// chain states:\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\n /// will be referred to as Y - 1 as it makes no difference in the argument\n ///\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\n /// miss-counting ETH or much more dangerous double counting of the ETH.\n ///\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\n /// - ETH balance in the contract on block X\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\n /// - ETH balance in validators that are active in slot Y - 1\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\n /// and have their balance visible to transactions in slot Y and corresponding block X\n /// (or sooner)\n ///\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\n ///\n /// *ETH balance in the contract on block X*\n ///\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\n ///\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\n /// would result in double counting the `stakedEth` since it would be present once in the\n /// snapped contract balance and the second time in deposit storage variables.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\n ///\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\n /// contract at block Z. The execution layer doesn't have direct access to the state of\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\n /// and could also be part of the validator balances. It does that by verifying that at\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\n /// the last snap till now all are still in queue. Which ensures they can not be part of\n /// the validator balances in later steps.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in validators that are active in slot Y - 1*\n ///\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\n /// correctness.\n ///\n /// This behaviour is correct.\n ///\n /// *The withdrawn validators*\n ///\n /// The withdrawn validators could have their balances deducted in any slot before slot\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\n /// look at the \"worst case scenario\" where the validator withdrawal is processed in the\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\n /// even if the validator exits at the latest possible time it is paramount that the ETH\n /// balance on the execution layer is recorded in the next block. Correctly accounting\n /// for the withdrawn ETH.\n ///\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\n /// validator balance verification as well as execution layer contract balance snap.\n ///\n /// This behaviour is correct.\n ///\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n function snapBalances() external {\n uint64 currentTimestamp = SafeCast.toUint64(block.timestamp);\n require(\n snappedBalance.timestamp + SNAP_BALANCES_DELAY < currentTimestamp,\n \"Snap too soon\"\n );\n\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(currentTimestamp);\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the snapped balance\n snappedBalance = Balances({\n blockRoot: blockRoot,\n timestamp: currentTimestamp,\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n emit BalancesSnapped(blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct BalanceProofs {\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n /// @param balanceProofs a `BalanceProofs` struct containing the following:\n /// balancesContainerRoot - the merkle root of the balances container\n /// balancesContainerProof - The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// validatorBalanceLeaves - Array of leaf nodes containing the validator balance with three other balances.\n /// validatorBalanceProofs - Array of merkle proofs for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(\n uint64 validatorVerificationBlockTimestamp,\n FirstPendingDepositProofData calldata firstPendingDeposit,\n BalanceProofs calldata balanceProofs\n ) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalance;\n // Check the balances are the latest\n require(balancesMem.timestamp > 0, \"No snapped balances\");\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n balanceProofs.validatorBalanceProofs.length ==\n verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n balanceProofs.validatorBalanceLeaves.length ==\n verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n balancesMem.blockRoot,\n balanceProofs.balancesContainerRoot,\n balanceProofs.balancesContainerProof\n );\n\n // for each validator in reserve order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n balanceProofs.balancesContainerRoot,\n balanceProofs.validatorBalanceLeaves[i],\n balanceProofs.validatorBalanceProofs[i],\n validator[verifiedValidators[i]].index\n );\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n // This could have been in VERIFIED or EXITING state\n validator[verifiedValidators[i]].state = ValidatorState\n .EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += validatorBalanceGwei * 1 gwei;\n }\n }\n\n uint256 depositsCount = depositList.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification.\n // This section is after the validator balance verifications so an exited validator will be marked\n // as EXITED before the deposits are verified. If there was a deposit to an exited validator\n // then the deposit can only be removed once the validator is fully exited.\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n balancesMem.blockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.pubKeyHash,\n firstPendingDeposit.pendingDepositPubKeyProof\n );\n\n // If there are no deposits in the beacon chain queue then our deposits must have been processed.\n // If the deposits have been processed, each deposit will need to be verified with `verifyDeposit`\n require(!isDepositQueueEmpty, \"Deposits have been processed\");\n\n // The verification of the validator the first pending deposit is for must be on or after when\n // `snapBalances` was called.\n require(\n balancesMem.timestamp <= validatorVerificationBlockTimestamp,\n \"Invalid validator timestamp\"\n );\n\n // Verify the validator of the first pending deposit is not exiting by checking\n // the withdrawable epoch is far into the future.\n // If it is exiting we can't be sure this deposit has not been postponed in the deposit queue.\n // Hence we can not verify if the strategy's deposit has been processed or not.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n // Get the parent beacon block root of the next block which is\n // the block root of the validator verification slot.\n // This will revert if the slot after the verification slot was missed.\n BeaconRoots.parentBlockRoot(\n validatorVerificationBlockTimestamp\n ),\n firstPendingDeposit.validatorIndex,\n firstPendingDeposit.pubKeyHash,\n // Validator is not exiting\n FAR_FUTURE_EPOCH,\n firstPendingDeposit.withdrawableEpochProof,\n firstPendingDeposit.validatorPubKeyProof\n );\n\n // solhint-disable max-line-length\n // If a validator is converted from a sweeping validator to a compounding validator, any balance in excess\n // of the min 32 ETH is put in the pending deposit queue. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // This will have a slot value of zero unfortunately.\n // We can not prove the strategy's deposits are still pending with a zero slot value so revert the tx.\n // Another snapBalances will need to be taken that does not have consolidation deposits at the front of the\n // beacon chain deposit queue.\n // solhint-enable max-line-length\n require(\n firstPendingDeposit.slot > 0,\n \"Invalid first pending deposit\"\n );\n\n // Calculate the epoch at the time of the snapBalances\n uint64 verificationEpoch = (SafeCast.toUint64(\n balancesMem.timestamp\n ) - BEACON_GENESIS_TIMESTAMP) / (SLOT_DURATION * SLOTS_PER_EPOCH);\n\n // For each staking strategy's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n uint256 depositID = depositList[i];\n DepositData memory depositData = deposits[depositID];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit slot is before the slot of the staking strategy's deposit.\n // If the deposit has been processed, it will need to be verified with `verifyDeposit`.\n // OR the deposit is to an exiting validator so check it is still not withdrawable.\n // If the validator is not withdrawable, then the deposit can not have been processed yet.\n // If the validator is now withdrawable, then the deposit may have been processed. The strategy\n // now has to wait until the validator's balance is verified to be zero.\n // OR the validator has exited and the deposit is now verified as processed.\n require(\n firstPendingDeposit.slot < depositData.slot ||\n (verificationEpoch < depositData.withdrawableEpoch &&\n depositData.withdrawableEpoch !=\n FAR_FUTURE_EPOCH) ||\n validator[depositData.pubKeyHash].state ==\n ValidatorState.EXITED,\n \"Deposit likely processed\"\n );\n\n // Remove the deposit if the validator has exited.\n if (\n validator[depositData.pubKeyHash].state ==\n ValidatorState.EXITED\n ) {\n _removeDeposit(depositID, depositData);\n\n emit DepositValidatorExited(\n depositID,\n uint256(depositData.amountGwei) * 1 gwei\n );\n\n // Skip to the next deposit as the deposit amount is now in the strategy's ETH balance\n continue;\n }\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei += uint256(depositData.amountGwei) * 1 gwei;\n }\n }\n\n // Store the verified balance in storage\n lastVerifiedEthBalance =\n totalDepositsWei +\n totalValidatorBalance +\n balancesMem.ethBalance;\n // Reset the last snap timestamp so a new snapBalances has to be made\n snappedBalance.timestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n require(pubKey.length == 48, \"Invalid public key length\");\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /**\n *\n * WETH and ETH Accounting\n *\n */\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n // The min is required as more WETH can be withdrawn than deposited\n // as the strategy earns consensus and execution rewards.\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance.\n // The ETH balance in this strategy contract can be more than the last verified ETH balance\n // due to partial withdrawals or full exits being processed by the beacon chain since the last snapBalances.\n // It can also happen from execution rewards (MEV) or ETH donations.\n lastVerifiedEthBalance -= Math.min(lastVerifiedEthBalance, _ethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n snappedBalance.timestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += _wethAmount;\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n snappedBalance.timestamp = 0;\n }\n\n /**\n *\n * View Functions\n *\n */\n\n /// @notice Returns the number of deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n function depositListLength() external view returns (uint256) {\n return depositList.length;\n }\n\n /// @notice Returns the number of verified validators.\n function verifiedValidatorsLength() external view returns (uint256) {\n return verifiedValidators.length;\n }\n}\n" + }, + "contracts/strategies/NativeStaking/FeeAccumulator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\n/**\n * @title Fee Accumulator for Native Staking SSV Strategy\n * @notice Receives execution rewards which includes tx fees and\n * MEV rewards like tx priority and tx ordering.\n * It does NOT include swept ETH from beacon chain consensus rewards or full validator withdrawals.\n * @author Origin Protocol Inc\n */\ncontract FeeAccumulator {\n /// @notice The address of the Native Staking Strategy\n address public immutable STRATEGY;\n\n event ExecutionRewardsCollected(address indexed strategy, uint256 amount);\n\n /**\n * @param _strategy Address of the Native Staking Strategy\n */\n constructor(address _strategy) {\n STRATEGY = _strategy;\n }\n\n /**\n * @notice sends all ETH in this FeeAccumulator contract to the Native Staking Strategy.\n * @return eth The amount of execution rewards that were sent to the Native Staking Strategy\n */\n function collect() external returns (uint256 eth) {\n require(msg.sender == STRATEGY, \"Caller is not the Strategy\");\n\n eth = address(this).balance;\n if (eth > 0) {\n // Send the ETH to the Native Staking Strategy\n Address.sendValue(payable(STRATEGY), eth);\n\n emit ExecutionRewardsCollected(STRATEGY, eth);\n }\n }\n\n /**\n * @dev Accept ETH\n */\n receive() external payable {}\n}\n" + }, + "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { FeeAccumulator } from \"./FeeAccumulator.sol\";\nimport { ValidatorAccountant } from \"./ValidatorAccountant.sol\";\nimport { ISSVNetwork } from \"../../interfaces/ISSVNetwork.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/// @title Native Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract NativeStakingSSVStrategy is\n ValidatorAccountant,\n InitializableAbstractStrategy\n{\n using SafeERC20 for IERC20;\n\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n /// @notice Fee collector address\n /// @dev this address will receive maximal extractable value (MEV) rewards. These are\n /// rewards for arranging transactions in a way that benefits the validator.\n address payable public immutable FEE_ACCUMULATOR_ADDRESS;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[49] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n /// @param _feeAccumulator Address of the fee accumulator receiving execution layer validator rewards\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n uint256 _maxValidators,\n address _feeAccumulator,\n address _beaconChainDepositContract\n )\n InitializableAbstractStrategy(_baseConfig)\n ValidatorAccountant(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _maxValidators\n )\n {\n SSV_TOKEN = _ssvToken;\n FEE_ACCUMULATOR_ADDRESS = payable(_feeAccumulator);\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract.\n /// @param _rewardTokenAddresses Address of reward token for platform\n /// @param _assets Addresses of initial supported assets\n /// @param _pTokens Platform Token corresponding addresses\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n\n // Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\n FEE_ACCUMULATOR_ADDRESS\n );\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n /// @param _asset Address of asset to deposit. Has to be WETH.\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n depositedWethAccountedFor += _amount;\n _deposit(_asset, _amount);\n }\n\n /// @dev Deposit WETH to this strategy so it can later be staked into a validator.\n /// @param _asset Address of WETH\n /// @param _amount Amount of WETH to deposit\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n /*\n * We could do a check here that would revert when \"_amount % 32 ether != 0\". With the idea of\n * not allowing deposits that will result in WETH sitting on the strategy after all the possible batches\n * of 32ETH have been staked.\n * But someone could mess with our strategy by sending some WETH to it. And we might want to deposit just\n * enough WETH to add it up to 32 so it can be staked. For that reason the check is left out.\n *\n * WETH sitting on the strategy won't interfere with the accounting since accounting only operates on ETH.\n */\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n depositedWethAccountedFor = wethBalance;\n\n _deposit(WETH, newWeth);\n }\n }\n\n /// @notice Withdraw WETH from this contract. Used only if some WETH for is lingering on the contract.\n /// That can happen when:\n /// - after mints if the strategy is the default\n /// - time between depositToStrategy and stakeEth\n /// - the deposit was not a multiple of 32 WETH\n /// - someone sent WETH directly to this contract\n /// Will NOT revert if the strategy is paused from an accounting failure.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n _wethWithdrawn(_amount);\n\n IERC20(_asset).safeTransfer(_recipient, _amount);\n emit Withdrawal(_asset, address(0), _amount);\n }\n\n /// @notice transfer all WETH deposits back to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `exitSsvValidator` and `removeSsvValidator` operations.\n /// This does not withdraw any execution rewards from the FeeAccumulator or\n /// consensus rewards in this strategy.\n /// Any ETH in this strategy that was swept from a full validator withdrawal will not be withdrawn.\n /// ETH from full validator withdrawals is sent to the Vault using `doAccounting`.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n if (wethBalance > 0) {\n _withdraw(vaultAddress, WETH, wethBalance);\n }\n }\n\n /// @notice Returns the total value of (W)ETH that is staked to the validators\n /// and WETH deposits that are still to be staked.\n /// This does not include ETH from consensus rewards sitting in this strategy\n /// or ETH from MEV rewards in the FeeAccumulator. These rewards are harvested\n /// and sent to the Dripper so will eventually be sent to the Vault as WETH.\n /// @param _asset Address of weth asset\n /// @return balance Total value of (W)ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n balance =\n // add the ETH that has been staked in validators\n activeDepositedValidators *\n FULL_STAKE +\n // add the WETH in the strategy from deposits that are still to be staked\n IERC20(WETH).balanceOf(address(this));\n }\n\n function pause() external onlyStrategist {\n _pause();\n }\n\n /// @notice Returns bool indicating whether asset is supported by strategy.\n /// @param _asset The address of the asset token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for deposits\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\n function setFeeRecipient() external {\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\n FEE_ACCUMULATOR_ADDRESS\n );\n }\n\n /**\n * @notice Only accept ETH from the FeeAccumulator and the WETH contract - required when\n * unwrapping WETH just before staking it to the validator.\n * The strategy will also receive ETH from the priority fees of transactions when producing blocks\n * as defined in EIP-1559.\n * The tx fees come from the Beacon chain so do not need any EVM level permissions to receive ETH.\n * The tx fees are paid with each block produced. They are not included in the consensus rewards\n * which are periodically swept from the validators to this strategy.\n * For accounting purposes, the priority fees of transactions will be considered consensus rewards\n * and will be included in the AccountingConsensusRewards event.\n * @dev don't want to receive donations from anyone else as donations over the fuse limits will\n * mess with the accounting of the consensus rewards and validator full withdrawals.\n */\n receive() external payable {\n require(\n msg.sender == FEE_ACCUMULATOR_ADDRESS || msg.sender == WETH,\n \"Eth not from allowed contracts\"\n );\n }\n\n /***************************************\n Internal functions\n ****************************************/\n\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Convert accumulated ETH to WETH and send to the Harvester.\n /// Will revert if the strategy is paused for accounting.\n function _collectRewardTokens() internal override whenNotPaused {\n // collect ETH from execution rewards from the fee accumulator\n uint256 executionRewards = FeeAccumulator(FEE_ACCUMULATOR_ADDRESS)\n .collect();\n\n // total ETH rewards to be harvested = execution rewards + consensus rewards\n uint256 ethRewards = executionRewards + consensusRewards;\n\n require(\n address(this).balance >= ethRewards,\n \"Insufficient eth balance\"\n );\n\n if (ethRewards > 0) {\n // reset the counter keeping track of beacon chain consensus rewards\n consensusRewards = 0;\n\n // Convert ETH rewards to WETH\n IWETH9(WETH).deposit{ value: ethRewards }();\n\n IERC20(WETH).safeTransfer(harvesterAddress, ethRewards);\n emit RewardTokenCollected(harvesterAddress, WETH, ethRewards);\n }\n }\n\n /// @dev emits Withdrawal event from NativeStakingSSVStrategy\n function _wethWithdrawnToVault(uint256 _amount) internal override {\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _wethWithdrawn(uint256 _amount) internal override {\n /* In an ideal world we wouldn't need to reduce the deduction amount when the\n * depositedWethAccountedFor is smaller than the _amount.\n *\n * The reason this is required is that a malicious actor could sent WETH directly\n * to this contract and that would circumvent the increase of depositedWethAccountedFor\n * property. When the ETH would be staked the depositedWethAccountedFor amount could\n * be deducted so much that it would be negative.\n */\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n }\n}\n" + }, + "contracts/strategies/NativeStaking/ValidatorAccountant.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ValidatorRegistrator } from \"./ValidatorRegistrator.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\n\n/// @title Validator Accountant\n/// @notice Attributes the ETH swept from beacon chain validators to this strategy contract\n/// as either full or partial withdrawals. Partial withdrawals being consensus rewards.\n/// Full withdrawals are from exited validators.\n/// @author Origin Protocol Inc\nabstract contract ValidatorAccountant is ValidatorRegistrator {\n /// @notice The minimum amount of blocks that need to pass between two calls to manuallyFixAccounting\n uint256 public constant MIN_FIX_ACCOUNTING_CADENCE = 7200; // 1 day\n\n /// @notice Keeps track of the total consensus rewards swept from the beacon chain\n uint256 public consensusRewards;\n\n /// @notice start of fuse interval\n uint256 public fuseIntervalStart;\n /// @notice end of fuse interval\n uint256 public fuseIntervalEnd;\n /// @notice last block number manuallyFixAccounting has been called\n uint256 public lastFixAccountingBlockNumber;\n\n uint256[49] private __gap;\n\n event FuseIntervalUpdated(uint256 start, uint256 end);\n event AccountingFullyWithdrawnValidator(\n uint256 noOfValidators,\n uint256 remainingValidators,\n uint256 wethSentToVault\n );\n event AccountingValidatorSlashed(\n uint256 remainingValidators,\n uint256 wethSentToVault\n );\n event AccountingConsensusRewards(uint256 amount);\n\n event AccountingManuallyFixed(\n int256 validatorsDelta,\n int256 consensusRewardsDelta,\n uint256 wethToVault\n );\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n uint256 _maxValidators\n )\n ValidatorRegistrator(\n _wethAddress,\n _vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _maxValidators\n )\n {}\n\n /// @notice set fuse interval values\n function setFuseInterval(\n uint256 _fuseIntervalStart,\n uint256 _fuseIntervalEnd\n ) external onlyGovernor {\n require(\n _fuseIntervalStart < _fuseIntervalEnd &&\n _fuseIntervalEnd < 32 ether &&\n _fuseIntervalEnd - _fuseIntervalStart >= 4 ether,\n \"Incorrect fuse interval\"\n );\n\n fuseIntervalStart = _fuseIntervalStart;\n fuseIntervalEnd = _fuseIntervalEnd;\n\n emit FuseIntervalUpdated(_fuseIntervalStart, _fuseIntervalEnd);\n }\n\n /* solhint-disable max-line-length */\n /// This notion page offers a good explanation of how the accounting functions\n /// https://www.notion.so/originprotocol/Limited-simplified-native-staking-accounting-67a217c8420d40678eb943b9da0ee77d\n /// In short, after dividing by 32, if the ETH remaining on the contract falls between 0 and fuseIntervalStart,\n /// the accounting function will treat that ETH as Beacon chain consensus rewards.\n /// On the contrary, if after dividing by 32, the ETH remaining on the contract falls between fuseIntervalEnd and 32,\n /// the accounting function will treat that as a validator slashing.\n /// @notice Perform the accounting attributing beacon chain ETH to either full or partial withdrawals. Returns true when\n /// accounting is valid and fuse isn't \"blown\". Returns false when fuse is blown.\n /// @dev This function could in theory be permission-less but lets allow only the Registrator (Defender Action) to call it\n /// for now.\n /// @return accountingValid true if accounting was successful, false if fuse is blown\n /* solhint-enable max-line-length */\n function doAccounting()\n external\n onlyRegistrator\n whenNotPaused\n nonReentrant\n returns (bool accountingValid)\n {\n // pause the accounting on failure\n accountingValid = _doAccounting(true);\n }\n\n // slither-disable-start reentrancy-eth\n function _doAccounting(bool pauseOnFail)\n internal\n returns (bool accountingValid)\n {\n if (address(this).balance < consensusRewards) {\n return _failAccounting(pauseOnFail);\n }\n\n // Calculate all the new ETH that has been swept to the contract since the last accounting\n uint256 newSweptETH = address(this).balance - consensusRewards;\n accountingValid = true;\n\n // send the ETH that is from fully withdrawn validators to the Vault\n if (newSweptETH >= FULL_STAKE) {\n uint256 fullyWithdrawnValidators;\n // explicitly cast to uint256 as we want to round to a whole number of validators\n fullyWithdrawnValidators = uint256(newSweptETH / FULL_STAKE);\n activeDepositedValidators -= fullyWithdrawnValidators;\n\n uint256 wethToVault = FULL_STAKE * fullyWithdrawnValidators;\n IWETH9(WETH).deposit{ value: wethToVault }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, wethToVault);\n _wethWithdrawnToVault(wethToVault);\n\n emit AccountingFullyWithdrawnValidator(\n fullyWithdrawnValidators,\n activeDepositedValidators,\n wethToVault\n );\n }\n\n uint256 ethRemaining = address(this).balance - consensusRewards;\n // should be less than a whole validator stake\n require(ethRemaining < FULL_STAKE, \"Unexpected accounting\");\n\n // If no Beacon chain consensus rewards swept\n if (ethRemaining == 0) {\n // do nothing\n return accountingValid;\n } else if (ethRemaining < fuseIntervalStart) {\n // Beacon chain consensus rewards swept (partial validator withdrawals)\n // solhint-disable-next-line reentrancy\n consensusRewards += ethRemaining;\n emit AccountingConsensusRewards(ethRemaining);\n } else if (ethRemaining > fuseIntervalEnd) {\n // Beacon chain consensus rewards swept but also a slashed validator fully exited\n IWETH9(WETH).deposit{ value: ethRemaining }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, ethRemaining);\n activeDepositedValidators -= 1;\n\n _wethWithdrawnToVault(ethRemaining);\n\n emit AccountingValidatorSlashed(\n activeDepositedValidators,\n ethRemaining\n );\n }\n // Oh no... Fuse is blown. The Strategist needs to adjust the accounting values.\n else {\n return _failAccounting(pauseOnFail);\n }\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @dev pause any further accounting if required and return false\n function _failAccounting(bool pauseOnFail)\n internal\n returns (bool accountingValid)\n {\n // pause if not already\n if (pauseOnFail) {\n _pause();\n }\n // fail the accounting\n accountingValid = false;\n }\n\n /// @notice Allow the Strategist to fix the accounting of this strategy and unpause.\n /// @param _validatorsDelta adjust the active validators by up to plus three or minus three\n /// @param _consensusRewardsDelta adjust the accounted for consensus rewards up or down\n /// @param _ethToVaultAmount the amount of ETH that gets wrapped into WETH and sent to the Vault\n /// @dev There is a case when a validator(s) gets slashed so much that the eth swept from\n /// the beacon chain enters the fuse area and there are no consensus rewards on the contract\n /// to \"dip into\"/use. To increase the amount of unaccounted ETH over the fuse end interval\n /// we need to reduce the amount of active deposited validators and immediately send WETH\n /// to the vault, so it doesn't interfere with further accounting.\n function manuallyFixAccounting(\n int256 _validatorsDelta,\n int256 _consensusRewardsDelta,\n uint256 _ethToVaultAmount\n ) external onlyStrategist whenPaused nonReentrant {\n require(\n lastFixAccountingBlockNumber + MIN_FIX_ACCOUNTING_CADENCE <\n block.number,\n \"Fix accounting called too soon\"\n );\n require(\n _validatorsDelta >= -3 &&\n _validatorsDelta <= 3 &&\n // new value must be positive\n int256(activeDepositedValidators) + _validatorsDelta >= 0,\n \"Invalid validatorsDelta\"\n );\n require(\n _consensusRewardsDelta >= -332 ether &&\n _consensusRewardsDelta <= 332 ether &&\n // new value must be positive\n int256(consensusRewards) + _consensusRewardsDelta >= 0,\n \"Invalid consensusRewardsDelta\"\n );\n require(_ethToVaultAmount <= 32 ether * 3, \"Invalid wethToVaultAmount\");\n\n activeDepositedValidators = uint256(\n int256(activeDepositedValidators) + _validatorsDelta\n );\n consensusRewards = uint256(\n int256(consensusRewards) + _consensusRewardsDelta\n );\n lastFixAccountingBlockNumber = block.number;\n if (_ethToVaultAmount > 0) {\n IWETH9(WETH).deposit{ value: _ethToVaultAmount }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, _ethToVaultAmount);\n _wethWithdrawnToVault(_ethToVaultAmount);\n }\n\n emit AccountingManuallyFixed(\n _validatorsDelta,\n _consensusRewardsDelta,\n _ethToVaultAmount\n );\n\n // rerun the accounting to see if it has now been fixed.\n // Do not pause the accounting on failure as it is already paused\n require(_doAccounting(false), \"Fuse still blown\");\n\n // unpause since doAccounting was successful\n _unpause();\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n /// @dev allows for NativeStakingSSVStrategy contract to emit the Withdrawal event\n function _wethWithdrawnToVault(uint256 _amount) internal virtual;\n}\n" + }, + "contracts/strategies/NativeStaking/ValidatorRegistrator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Registrator of the validators\n * @notice This contract implements all the required functionality to register, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract ValidatorRegistrator is Governable, Pausable {\n /// @notice The maximum amount of ETH that can be staked by a validator\n /// @dev this can change in the future with EIP-7251, Increase the MAX_EFFECTIVE_BALANCE\n uint256 public constant FULL_STAKE = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Maximum number of validators that can be registered in this strategy\n uint256 public immutable MAX_VALIDATORS;\n\n /// @notice Address of the registrator - allowed to register, exit and remove validators\n address public validatorRegistrator;\n /// @notice The number of validators that have 32 (!) ETH actively deposited. When a new deposit\n /// to a validator happens this number increases, when a validator exit is detected this number\n /// decreases.\n uint256 public activeDepositedValidators;\n /// @notice State of the validators keccak256(pubKey) => state\n mapping(bytes32 => VALIDATOR_STATE) public validatorsStates;\n /// @notice The account that is allowed to modify stakeETHThreshold and reset stakeETHTally\n address public stakingMonitor;\n /// @notice Amount of ETH that can be staked before staking on the contract is suspended\n /// and the `stakingMonitor` needs to approve further staking by calling `resetStakeETHTally`\n uint256 public stakeETHThreshold;\n /// @notice Amount of ETH that has been staked since the `stakingMonitor` last called `resetStakeETHTally`.\n /// This can not go above `stakeETHThreshold`.\n uint256 public stakeETHTally;\n\n // For future use\n uint256[47] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n EXITING, // exit message has been posted and validator is in the process of exiting\n EXIT_COMPLETE // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event ETHStaked(bytes32 indexed pubKeyHash, bytes pubKey, uint256 amount);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event SSVValidatorExitInitiated(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event SSVValidatorExitCompleted(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event StakeETHThresholdChanged(uint256 amount);\n event StakeETHTallyReset();\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(\n msg.sender == validatorRegistrator,\n \"Caller is not the Registrator\"\n );\n _;\n }\n\n /// @dev Throws if called by any account other than the Staking monitor\n modifier onlyStakingMonitor() {\n require(msg.sender == stakingMonitor, \"Caller is not the Monitor\");\n _;\n }\n\n /// @dev Throws if called by any account other than the Strategist\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(VAULT_ADDRESS).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n uint256 _maxValidators\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n MAX_VALIDATORS = _maxValidators;\n }\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Set the address of the staking monitor that is allowed to reset stakeETHTally\n function setStakingMonitor(address _address) external onlyGovernor {\n stakingMonitor = _address;\n emit StakingMonitorChanged(_address);\n }\n\n /// @notice Set the amount of ETH that can be staked before staking monitor\n // needs to a approve further staking by resetting the stake ETH tally\n function setStakeETHThreshold(uint256 _amount) external onlyGovernor {\n stakeETHThreshold = _amount;\n emit StakeETHThresholdChanged(_amount);\n }\n\n /// @notice Reset the stakeETHTally\n function resetStakeETHTally() external onlyStakingMonitor {\n stakeETHTally = 0;\n emit StakeETHTallyReset();\n }\n\n /// @notice Stakes WETH to the node validators\n /// @param validators A list of validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n // slither-disable-start reentrancy-eth\n function stakeEth(ValidatorStakeData[] calldata validators)\n external\n onlyRegistrator\n whenNotPaused\n nonReentrant\n {\n uint256 requiredETH = validators.length * FULL_STAKE;\n\n // Check there is enough WETH from the deposits sitting in this strategy contract\n require(\n requiredETH <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(\n activeDepositedValidators + validators.length <= MAX_VALIDATORS,\n \"Max validators reached\"\n );\n\n require(\n stakeETHTally + requiredETH <= stakeETHThreshold,\n \"Staking ETH over threshold\"\n );\n stakeETHTally += requiredETH;\n\n // Convert required ETH from WETH\n IWETH9(WETH).withdraw(requiredETH);\n _wethWithdrawn(requiredETH);\n\n /* 0x01 to indicate that withdrawal credentials will contain an EOA address that the sweeping function\n * can sweep funds to.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x01),\n bytes11(0),\n address(this)\n );\n\n // For each validator\n for (uint256 i = 0; i < validators.length; ++i) {\n bytes32 pubKeyHash = keccak256(validators[i].pubkey);\n\n require(\n validatorsStates[pubKeyHash] == VALIDATOR_STATE.REGISTERED,\n \"Validator not registered\"\n );\n\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: FULL_STAKE\n }(\n validators[i].pubkey,\n withdrawalCredentials,\n validators[i].signature,\n validators[i].depositDataRoot\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.STAKED;\n\n emit ETHStaked(pubKeyHash, validators[i].pubkey, FULL_STAKE);\n }\n // save gas by changing this storage variable only once rather each time in the loop.\n activeDepositedValidators += validators.length;\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Registers a new validator in the SSV Cluster.\n /// Only the registrator can call this function.\n /// @param publicKeys The public keys of the validators\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for each validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function registerSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n require(\n publicKeys.length == sharesData.length,\n \"Pubkey sharesData mismatch\"\n );\n // Check each public key has not already been used\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n emit SSVValidatorRegistered(pubKeyHash, publicKeys[i], operatorIds);\n }\n\n ISSVNetwork(SSV_NETWORK).bulkRegisterValidator(\n publicKeys,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n }\n\n /// @notice Exit validators from the Beacon chain.\n /// The staked ETH will eventually swept to this native staking strategy.\n /// Only the registrator can call this function.\n /// @param publicKeys List of SSV validator public keys\n /// @param operatorIds The operator IDs of the SSV Cluster\n function exitSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external onlyRegistrator whenNotPaused nonReentrant {\n ISSVNetwork(SSV_NETWORK).bulkExitValidator(publicKeys, operatorIds);\n\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n\n // Check each validator has not already been staked.\n // This would normally be done before the external call but is after\n // so only one for loop of the validators is needed.\n require(\n currentState == VALIDATOR_STATE.STAKED,\n \"Validator not staked\"\n );\n\n // Store the new validator state\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXITING;\n\n emit SSVValidatorExitInitiated(\n pubKeyHash,\n publicKeys[i],\n operatorIds\n );\n }\n }\n\n /// @notice Remove validators from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKeys List of SSV validator public keys\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function removeSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused nonReentrant {\n ISSVNetwork(SSV_NETWORK).bulkRemoveValidator(\n publicKeys,\n operatorIds,\n cluster\n );\n\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n\n // Check each validator is either registered or exited.\n // This would normally be done before the external call but is after\n // so only one for loop of the validators is needed.\n require(\n currentState == VALIDATOR_STATE.EXITING ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exiting\"\n );\n\n // Store the new validator state\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXIT_COMPLETE;\n\n emit SSVValidatorExitCompleted(\n pubKeyHash,\n publicKeys[i],\n operatorIds\n );\n }\n }\n\n /// @notice Deposits more SSV Tokens to the SSV Network contract which is used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// uses \"onlyStrategist\" modifier so continuous front-running can't DOS our maintenance service\n /// that tries to top up SSV tokens.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function depositSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyStrategist {\n ISSVNetwork(SSV_NETWORK).deposit(\n address(this),\n operatorIds,\n ssvAmount,\n cluster\n );\n }\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _wethWithdrawn(uint256 _amount) internal virtual;\n}\n" + }, + "contracts/strategies/plume/RoosterAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Rooster AMO strategy\n * @author Origin Protocol Inc\n * @custom:security-contact security@originprotocol.com\n */\nimport { Math as MathRooster } from \"../../../lib/rooster/v2-common/libraries/Math.sol\";\nimport { Math as Math_v5 } from \"../../../lib/rooster/openzeppelin-custom/contracts/utils/math/Math.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { IMaverickV2Pool } from \"../../interfaces/plume/IMaverickV2Pool.sol\";\nimport { IMaverickV2Quoter } from \"../../interfaces/plume/IMaverickV2Quoter.sol\";\nimport { IMaverickV2LiquidityManager } from \"../../interfaces/plume/IMaverickV2LiquidityManager.sol\";\nimport { IMaverickV2PoolLens } from \"../../interfaces/plume/IMaverickV2PoolLens.sol\";\nimport { IMaverickV2Position } from \"../../interfaces/plume/IMaverickV2Position.sol\";\nimport { IVotingDistributor } from \"../../interfaces/plume/IVotingDistributor.sol\";\nimport { IPoolDistributor } from \"../../interfaces/plume/IPoolDistributor.sol\";\n// importing custom version of rooster TickMath because of dependency collision. Maverick uses\n// a newer OpenZepplin Math library with functionality that is not present in 4.4.2 (the one we use)\nimport { TickMath } from \"../../../lib/rooster/v2-common/libraries/TickMath.sol\";\n\ncontract RoosterAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n using SafeCast for uint256;\n\n /***************************************\n Storage slot members\n ****************************************/\n\n /// @notice NFT tokenId of the liquidity position\n ///\n /// @dev starts with value of 1 and can not be 0\n // solhint-disable-next-line max-line-length\n /// https://github.com/rooster-protocol/rooster-contracts/blob/fbfecbc519e4495b12598024a42630b4a8ea4489/v2-common/contracts/base/Nft.sol#L14\n uint256 public tokenId;\n /// @dev Minimum amount of tokens the strategy would be able to withdraw from the pool.\n /// minimum amount of tokens are withdrawn at a 1:1 price\n /// Important: Underlying assets contains only assets that are deposited in the underlying Rooster pool.\n /// WETH or OETH held by this contract is not accounted for in underlying assets\n uint256 public underlyingAssets;\n /// @notice Marks the start of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareStart;\n /// @notice Marks the end of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareEnd;\n /// @dev reserved for inheritance\n int256[46] private __reserved;\n\n /***************************************\n Constants, structs and events\n ****************************************/\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the OETH token contract\n address public immutable OETH;\n /// @notice the underlying AMO Maverick (Rooster) pool\n IMaverickV2Pool public immutable mPool;\n /// @notice the Liquidity manager used to add liquidity to the pool\n IMaverickV2LiquidityManager public immutable liquidityManager;\n /// @notice the Maverick V2 poolLens\n ///\n /// @dev only used to provide the pool's current sqrtPrice\n IMaverickV2PoolLens public immutable poolLens;\n /// @notice the Maverick V2 position\n ///\n /// @dev provides details of the NFT LP position and offers functions to\n /// remove the liquidity.\n IMaverickV2Position public immutable maverickPosition;\n /// @notice the Maverick Quoter\n IMaverickV2Quoter public immutable quoter;\n /// @notice the Maverick Voting Distributor\n IVotingDistributor public immutable votingDistributor;\n /// @notice the Maverick Pool Distributor\n IPoolDistributor public immutable poolDistributor;\n\n /// @notice sqrtPriceTickLower\n /// @dev tick lower represents the lower price of OETH priced in WETH. Meaning the pool\n /// offers more than 1 OETH for 1 WETH. In other terms to get 1 OETH the swap needs to offer 0.9999 WETH\n /// this is where purchasing OETH with WETH within the liquidity position is the cheapest.\n ///\n /// _____________________\n /// | | |\n /// | WETH | OETH |\n /// | | |\n /// | | |\n /// --------- * ---- * ---------- * ---------\n /// currentPrice\n /// sqrtPriceHigher-(1:1 parity)\n /// sqrtPriceLower\n ///\n ///\n /// Price is defined as price of token1 in terms of token0. (token1 / token0)\n /// @notice sqrtPriceTickLower - OETH is priced 0.9999 WETH\n uint256 public immutable sqrtPriceTickLower;\n /// @notice sqrtPriceTickHigher\n /// @dev tick higher represents 1:1 price parity of WETH to OETH\n uint256 public immutable sqrtPriceTickHigher;\n /// @dev price at parity (in OETH this is equal to sqrtPriceTickHigher)\n uint256 public immutable sqrtPriceAtParity;\n /// @notice The tick where the strategy deploys the liquidity to\n int32 public constant TICK_NUMBER = -1;\n /// @notice Minimum liquidity that must be exceeded to continue with the action\n /// e.g. deposit, add liquidity\n uint256 public constant ACTION_THRESHOLD = 1e12;\n /// @notice Maverick pool static liquidity bin type\n uint8 public constant MAV_STATIC_BIN_KIND = 0;\n /// @dev a threshold under which the contract no longer allows for the protocol to rebalance. Guarding\n /// against a strategist / guardian being taken over and with multiple transactions draining the\n /// protocol funds.\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n /// @notice Emitted when the allowed interval within which the strategy contract is allowed to deposit\n /// liquidity to the underlying pool is updated.\n /// @param allowedWethShareStart The start of the interval\n /// @param allowedWethShareEnd The end of the interval\n event PoolWethShareIntervalUpdated(\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n /// @notice Emitted when liquidity is removed from the underlying pool\n /// @param withdrawLiquidityShare Share of strategy's liquidity that has been removed\n /// @param removedWETHAmount The amount of WETH removed\n /// @param removedOETHAmount The amount of OETH removed\n /// @param underlyingAssets Updated amount of strategy's underlying assets\n event LiquidityRemoved(\n uint256 withdrawLiquidityShare,\n uint256 removedWETHAmount,\n uint256 removedOETHAmount,\n uint256 underlyingAssets\n );\n\n /// @notice Emitted when the underlying pool is rebalanced\n /// @param currentPoolWethShare The resulting share of strategy's liquidity\n /// in the TICK_NUMBER\n event PoolRebalanced(uint256 currentPoolWethShare);\n\n /// @notice Emitted when the amount of underlying assets the strategy hold as\n /// liquidity in the pool is updated.\n /// @param underlyingAssets Updated amount of strategy's underlying assets\n event UnderlyingAssetsUpdated(uint256 underlyingAssets);\n\n /// @notice Emitted when liquidity is added to the underlying pool\n /// @param wethAmountDesired Amount of WETH desired to be deposited\n /// @param oethAmountDesired Amount of OETH desired to be deposited\n /// @param wethAmountSupplied Amount of WETH deposited\n /// @param oethAmountSupplied Amount of OETH deposited\n /// @param tokenId NFT liquidity token id\n /// @param underlyingAssets Updated amount of underlying assets\n event LiquidityAdded(\n uint256 wethAmountDesired,\n uint256 oethAmountDesired,\n uint256 wethAmountSupplied,\n uint256 oethAmountSupplied,\n uint256 tokenId,\n uint256 underlyingAssets\n ); // 0x1530ec74\n\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n ); // 0x3681e8e0\n\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth); // 0x989e5ca8\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth); // 0xa6737d87\n error OutsideExpectedTickRange(); // 0xa6e1bad2\n error SlippageCheck(uint256 tokenReceived); // 0x355cdb78\n\n /// @notice the constructor\n /// @dev This contract is intended to be used as a proxy. To prevent the\n /// potential confusion of having a functional implementation contract\n /// the constructor has the `initializer` modifier. This way the\n /// `initialize` function can not be called on the implementation contract.\n /// For the same reason the implementation contract also has the governor\n /// set to a zero address.\n /// @param _stratConfig the basic strategy configuration\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _oethAddress Address of the Erc20 OETH Token contract\n /// @param _liquidityManager Address of liquidity manager to add\n /// the liquidity\n /// @param _poolLens Address of the pool lens contract\n /// @param _maverickPosition Address of the Maverick's position contract\n /// @param _maverickQuoter Address of the Maverick's Quoter contract\n /// @param _mPool Address of the Rooster concentrated liquidity pool\n /// @param _upperTickAtParity Bool when true upperTick is the one where the\n /// price of OETH and WETH are at parity\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethAddress,\n address _liquidityManager,\n address _poolLens,\n address _maverickPosition,\n address _maverickQuoter,\n address _mPool,\n bool _upperTickAtParity,\n address _votingDistributor,\n address _poolDistributor\n ) initializer InitializableAbstractStrategy(_stratConfig) {\n require(\n address(IMaverickV2Pool(_mPool).tokenA()) == _wethAddress,\n \"WETH not TokenA\"\n );\n require(\n address(IMaverickV2Pool(_mPool).tokenB()) == _oethAddress,\n \"OETH not TokenB\"\n );\n require(\n _liquidityManager != address(0),\n \"LiquidityManager zero address not allowed\"\n );\n require(\n _maverickQuoter != address(0),\n \"Quoter zero address not allowed\"\n );\n require(_poolLens != address(0), \"PoolLens zero address not allowed\");\n require(\n _maverickPosition != address(0),\n \"Position zero address not allowed\"\n );\n require(\n _votingDistributor != address(0),\n \"Voting distributor zero address not allowed\"\n );\n require(\n _poolDistributor != address(0),\n \"Pool distributor zero address not allowed\"\n );\n\n uint256 _tickSpacing = IMaverickV2Pool(_mPool).tickSpacing();\n require(_tickSpacing == 1, \"Unsupported tickSpacing\");\n\n // tickSpacing == 1\n (sqrtPriceTickLower, sqrtPriceTickHigher) = TickMath.tickSqrtPrices(\n _tickSpacing,\n TICK_NUMBER\n );\n sqrtPriceAtParity = _upperTickAtParity\n ? sqrtPriceTickHigher\n : sqrtPriceTickLower;\n\n WETH = _wethAddress;\n OETH = _oethAddress;\n liquidityManager = IMaverickV2LiquidityManager(_liquidityManager);\n poolLens = IMaverickV2PoolLens(_poolLens);\n maverickPosition = IMaverickV2Position(_maverickPosition);\n quoter = IMaverickV2Quoter(_maverickQuoter);\n mPool = IMaverickV2Pool(_mPool);\n votingDistributor = IVotingDistributor(_votingDistributor);\n poolDistributor = IPoolDistributor(_poolDistributor);\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /**\n * @notice initialize function, to set up initial internal state\n */\n function initialize() external onlyGovernor initializer {\n // Read reward\n address[] memory _rewardTokens = new address[](1);\n _rewardTokens[0] = poolDistributor.rewardToken();\n\n require(_rewardTokens[0] != address(0), \"No reward token configured\");\n\n InitializableAbstractStrategy._initialize(\n _rewardTokens,\n new address[](0),\n new address[](0)\n );\n }\n\n /***************************************\n Configuration \n ****************************************/\n\n /**\n * @notice Set allowed pool weth share interval. After the rebalance happens\n * the share of WETH token in the ticker needs to be within the specifications\n * of the interval.\n *\n * @param _allowedWethShareStart Start of WETH share interval expressed as 18 decimal amount\n * @param _allowedWethShareEnd End of WETH share interval expressed as 18 decimal amount\n */\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external onlyGovernor {\n require(\n _allowedWethShareStart < _allowedWethShareEnd,\n \"Invalid interval\"\n );\n // can not go below 1% weth share\n require(_allowedWethShareStart > 0.01 ether, \"Invalid interval start\");\n // can not go above 95% weth share\n require(_allowedWethShareEnd < 0.95 ether, \"Invalid interval end\");\n\n allowedWethShareStart = _allowedWethShareStart;\n allowedWethShareEnd = _allowedWethShareEnd;\n emit PoolWethShareIntervalUpdated(\n _allowedWethShareStart,\n _allowedWethShareEnd\n );\n }\n\n /***************************************\n Strategy overrides \n ****************************************/\n\n /**\n * @notice Deposits funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposits all the funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n _deposit(WETH, _wethBalance);\n }\n\n /**\n * @dev Deposits funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n * Before this function can be called the initial pool position needs to already\n * be minted.\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n require(tokenId > 0, \"Initial position not minted\");\n emit Deposit(_asset, address(0), _amount);\n\n // if the pool price is not within the expected interval leave the WETH on the contract\n // as to not break the mints - in case it would be configured as a default asset strategy\n (bool _isExpectedRange, ) = _checkForExpectedPoolPrice(false);\n if (_isExpectedRange) {\n // deposit funds into the underlying pool. Because no swap is performed there is no\n // need to remove any of the liquidity beforehand.\n _rebalance(0, false, 0, 0);\n }\n }\n\n /**\n * @notice Withdraw an `amount` of WETH from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset WETH address\n * @param _amount Amount of WETH to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n _ensureWETHBalance(_amount);\n\n _withdraw(_recipient, _amount);\n }\n\n /**\n * @notice Withdraw WETH and sends it to the Vault.\n */\n function withdrawAll() external override onlyVault nonReentrant {\n if (tokenId != 0) {\n _removeLiquidity(1e18);\n }\n\n uint256 _balance = IERC20(WETH).balanceOf(address(this));\n if (_balance > 0) {\n _withdraw(vaultAddress, _balance);\n }\n }\n\n function _withdraw(address _recipient, uint256 _amount) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n * @return bool True when the _asset is WETH\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /**\n * @dev Approve the spending amounts for the assets\n */\n function _approveTokenAmounts(\n uint256 _wethAllowance,\n uint256 _oethAllowance\n ) internal {\n IERC20(WETH).approve(address(liquidityManager), _wethAllowance);\n IERC20(OETH).approve(address(liquidityManager), _oethAllowance);\n }\n\n /***************************************\n Liquidity management\n ****************************************/\n /**\n * @dev Add liquidity into the pool in the pre-configured WETH to OETH share ratios\n * defined by the allowedPoolWethShareStart|End interval.\n *\n * Normally a PoolLens contract is used to prepare the parameters to add liquidity to the\n * Rooster pools. It has some errors when doing those calculation and for that reason a\n * much more accurate Quoter contract is used. This is possible due to our requirement of\n * adding liquidity only to one tick - PoolLens supports adding liquidity into multiple ticks\n * using different distribution ratios.\n */\n function _addLiquidity() internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n // don't deposit small liquidity amounts\n if (_wethBalance <= ACTION_THRESHOLD) {\n return;\n }\n\n (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n ) = _getAddLiquidityParams(_wethBalance, 1e30);\n\n if (OETHRequired > _oethBalance) {\n IVault(vaultAddress).mintForStrategy(OETHRequired - _oethBalance);\n }\n\n _approveTokenAmounts(WETHRequired, OETHRequired);\n\n (\n uint256 _wethAmount,\n uint256 _oethAmount,\n uint32[] memory binIds\n ) = liquidityManager.addPositionLiquidityToSenderByTokenIndex(\n mPool,\n 0, // NFT token index\n packedSqrtPriceBreaks,\n packedArgs\n );\n\n require(binIds.length == 1, \"Unexpected binIds length\");\n\n // burn remaining OETH\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n\n // needs to be called after _updateUnderlyingAssets so the updated amount\n // is reflected in the event\n emit LiquidityAdded(\n _wethBalance, // wethAmountDesired\n OETHRequired, // oethAmountDesired\n _wethAmount, // wethAmountSupplied\n _oethAmount, // oethAmountSupplied\n tokenId, // tokenId\n underlyingAssets\n );\n }\n\n /**\n * @dev The function creates liquidity parameters required to be able to add liquidity to the pool.\n * The function needs to handle the 3 different cases of the way liquidity is added:\n * - only WETH present in the tick\n * - only OETH present in the tick\n * - both tokens present in the tick\n *\n */\n function _getAddLiquidityParams(uint256 _maxWETH, uint256 _maxOETH)\n internal\n returns (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n )\n {\n IMaverickV2Pool.AddLiquidityParams[]\n memory addParams = new IMaverickV2Pool.AddLiquidityParams[](1);\n int32[] memory ticks = new int32[](1);\n uint128[] memory amounts = new uint128[](1);\n ticks[0] = TICK_NUMBER;\n // arbitrary LP amount\n amounts[0] = 1e24;\n\n // construct value for Quoter with arbitrary LP amount\n IMaverickV2Pool.AddLiquidityParams memory addParam = IMaverickV2Pool\n .AddLiquidityParams({\n kind: MAV_STATIC_BIN_KIND,\n ticks: ticks,\n amounts: amounts\n });\n\n // get the WETH and OETH required to get the proportion of tokens required\n // given the arbitrary liquidity\n (WETHRequired, OETHRequired, ) = quoter.calculateAddLiquidity(\n mPool,\n addParam\n );\n\n /**\n * If either token required is 0 then the tick consists only of the other token. In that\n * case the liquidity calculations need to be done using the non 0 token. By setting the\n * tokenRequired from 0 to 1 the `min` in next step will ignore that (the bigger) value.\n */\n WETHRequired = WETHRequired == 0 ? 1 : WETHRequired;\n OETHRequired = OETHRequired == 0 ? 1 : OETHRequired;\n\n addParam.amounts[0] = Math_v5\n .min(\n ((_maxWETH - 1) * 1e24) / WETHRequired,\n ((_maxOETH - 1) * 1e24) / OETHRequired\n )\n .toUint128();\n\n // update the quotes with the actual amounts\n (WETHRequired, OETHRequired, ) = quoter.calculateAddLiquidity(\n mPool,\n addParam\n );\n\n require(_maxWETH >= WETHRequired, \"More WETH required than specified\");\n require(_maxOETH >= OETHRequired, \"More OETH required than specified\");\n\n // organize values to be used by manager\n addParams[0] = addParam;\n packedArgs = liquidityManager.packAddLiquidityArgsArray(addParams);\n // price can stay 0 if array only has one element\n packedSqrtPriceBreaks = liquidityManager.packUint88Array(\n new uint88[](1)\n );\n }\n\n /**\n * @dev Check that the Rooster pool price is within the expected\n * parameters.\n * This function works whether the strategy contract has liquidity\n * position in the pool or not. The function returns _wethSharePct\n * as a gas optimization measure.\n * @param _throwException when set to true the function throws an exception\n * when pool's price is not within expected range.\n * @return _isExpectedRange Bool expressing price is within expected range\n * @return _wethSharePct Share of WETH owned by this strategy contract in the\n * configured ticker.\n */\n function _checkForExpectedPoolPrice(bool _throwException)\n internal\n view\n returns (bool _isExpectedRange, uint256 _wethSharePct)\n {\n require(\n allowedWethShareStart != 0 && allowedWethShareEnd != 0,\n \"Weth share interval not set\"\n );\n\n uint256 _currentPrice = getPoolSqrtPrice();\n\n /**\n * First check pool price is in expected tick range\n *\n * A revert is issued even though price being equal to the lower bound as that can not\n * be within the approved tick range.\n */\n if (\n _currentPrice <= sqrtPriceTickLower ||\n _currentPrice >= sqrtPriceTickHigher\n ) {\n if (_throwException) {\n revert OutsideExpectedTickRange();\n }\n\n return (false, _currentPrice <= sqrtPriceTickLower ? 0 : 1e18);\n }\n\n // 18 decimal number expressed WETH tick share\n _wethSharePct = _getWethShare(_currentPrice);\n\n if (\n _wethSharePct < allowedWethShareStart ||\n _wethSharePct > allowedWethShareEnd\n ) {\n if (_throwException) {\n revert PoolRebalanceOutOfBounds(\n _wethSharePct,\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n return (false, _wethSharePct);\n }\n\n return (true, _wethSharePct);\n }\n\n /**\n * @notice Rebalance the pool to the desired token split and Deposit any WETH on the contract to the\n * underlying rooster pool. Print the required amount of corresponding OETH. After the rebalancing is\n * done burn any potentially remaining OETH tokens still on the strategy contract.\n *\n * This function has a slightly different behaviour depending on the status of the underlying Rooster\n * pool. The function consists of the following 3 steps:\n * 1. withdrawLiquidityOption -> this is a configurable option where either only part of the liquidity\n * necessary for the swap is removed, or all of it. This way the rebalance\n * is able to optimize for volume, for efficiency or anything in between\n * 2. swapToDesiredPosition -> move active trading price in the pool to be able to deposit WETH & OETH\n * tokens with the desired pre-configured ratios\n * 3. addLiquidity -> add liquidity into the pool respecting ratio split configuration\n *\n *\n * Exact _amountToSwap, _swapWeth & _minTokenReceived parameters shall be determined by simulating the\n * transaction off-chain. The strategy checks that after the swap the share of the tokens is in the\n * expected ranges.\n *\n * @param _amountToSwap The amount of the token to swap\n * @param _swapWeth Swap using WETH when true, use OETH when false\n * @param _minTokenReceived Slippage check -> minimum amount of token expected in return\n * @param _liquidityToRemovePct Percentage of liquidity to remove -> the percentage amount of liquidity to\n * remove before performing the swap. 1e18 denominated\n */\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived,\n uint256 _liquidityToRemovePct\n ) external nonReentrant onlyGovernorOrStrategist {\n _rebalance(\n _amountToSwap,\n _swapWeth,\n _minTokenReceived,\n _liquidityToRemovePct\n );\n }\n\n // slither-disable-start reentrancy-no-eth\n function _rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived,\n uint256 _liquidityToRemovePct\n ) internal {\n // Remove the required amount of liquidity\n if (_liquidityToRemovePct > 0) {\n _removeLiquidity(_liquidityToRemovePct);\n }\n\n // in some cases (e.g. deposits) we will just want to add liquidity and not\n // issue a swap to move the active trading position within the pool. Before or after a\n // deposit or as a standalone call the strategist might issue a rebalance to move the\n // active trading price to a more desired position.\n if (_amountToSwap > 0) {\n // In case liquidity has been removed and there is still not enough WETH owned by the\n // strategy contract remove additional required amount of WETH.\n if (_swapWeth) _ensureWETHBalance(_amountToSwap);\n\n _swapToDesiredPosition(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n\n // calling check liquidity early so we don't get unexpected errors when adding liquidity\n // in the later stages of this function\n _checkForExpectedPoolPrice(true);\n\n _addLiquidity();\n\n // this call shouldn't be necessary, since adding liquidity shouldn't affect the active\n // trading price. It is a defensive programming measure.\n (, uint256 _wethSharePct) = _checkForExpectedPoolPrice(true);\n\n // revert if protocol insolvent\n _solvencyAssert();\n\n emit PoolRebalanced(_wethSharePct);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @dev Perform a swap so that after the swap the tick has the desired WETH to OETH token share.\n */\n function _swapToDesiredPosition(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n IERC20 _tokenToSwap = IERC20(_swapWeth ? WETH : OETH);\n uint256 _balance = _tokenToSwap.balanceOf(address(this));\n\n if (_balance < _amountToSwap) {\n // This should never trigger since _ensureWETHBalance will already\n // throw an error if there is not enough WETH\n if (_swapWeth) {\n revert NotEnoughWethForSwap(_balance, _amountToSwap);\n }\n // if swapping OETH\n uint256 mintForSwap = _amountToSwap - _balance;\n IVault(vaultAddress).mintForStrategy(mintForSwap);\n }\n\n // SafeERC20 is used for IERC20 transfers. Not sure why slither complains\n // slither-disable-next-line unchecked-transfer\n _tokenToSwap.transfer(address(mPool), _amountToSwap);\n\n // tickLimit: the furthest tick a swap will execute in. If no limit is desired,\n // value should be set to type(int32).max for a tokenAIn (WETH) swap\n // and type(int32).min for a swap where tokenB (OETH) is the input\n\n IMaverickV2Pool.SwapParams memory swapParams = IMaverickV2Pool\n // exactOutput defines whether the amount specified is the output\n // or the input amount of the swap\n .SwapParams({\n amount: _amountToSwap,\n tokenAIn: _swapWeth,\n exactOutput: false,\n tickLimit: TICK_NUMBER\n });\n\n // swaps without a callback as the assets are already sent to the pool\n (, uint256 amountOut) = mPool.swap(\n address(this),\n swapParams,\n bytes(\"\")\n );\n\n /**\n * There could be additional checks here for validating minTokenReceived is within the\n * expected range (e.g. 99% - 101% of the token sent in). Though that doesn't provide\n * any additional security. After the swap the `_checkForExpectedPoolPrice` validates\n * that the swap has moved the price into the expected tick (# -1).\n *\n * If the guardian forgets to set a `_minTokenReceived` and a sandwich attack bends\n * the pool before the swap the `_checkForExpectedPoolPrice` will fail the transaction.\n *\n * A check would not prevent a compromised guardian from stealing funds as multiple\n * transactions each loosing smaller amount of funds are still possible.\n */\n if (amountOut < _minTokenReceived) {\n revert SlippageCheck(amountOut);\n }\n\n /**\n * In the interest of each function in `_rebalance` to leave the contract state as\n * clean as possible the OETH tokens here are burned. This decreases the\n * dependence where `_swapToDesiredPosition` function relies on later functions\n * (`addLiquidity`) to burn the OETH. Reducing the risk of error introduction.\n */\n _burnOethOnTheContract();\n }\n\n /**\n * @dev This function removes the appropriate amount of liquidity to ensure that the required\n * amount of WETH is available on the contract\n *\n * @param _amount WETH balance required on the contract\n */\n function _ensureWETHBalance(uint256 _amount) internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance >= _amount) {\n return;\n }\n\n require(tokenId != 0, \"No liquidity available\");\n uint256 _additionalWethRequired = _amount - _wethBalance;\n (uint256 _wethInThePool, ) = getPositionPrincipal();\n\n if (_wethInThePool < _additionalWethRequired) {\n revert NotEnoughWethLiquidity(\n _wethInThePool,\n _additionalWethRequired\n );\n }\n\n uint256 shareOfWethToRemove = _wethInThePool <= 1\n ? 1e18\n : Math_v5.min(\n /**\n * When dealing with shares of liquidity to remove there is always some\n * rounding involved. After extensive fuzz testing the below approach\n * yielded the best results where the strategy overdraws the least and\n * never removes insufficient amount of WETH.\n */\n (_additionalWethRequired + 2).divPrecisely(_wethInThePool - 1) +\n 2,\n 1e18\n );\n\n _removeLiquidity(shareOfWethToRemove);\n }\n\n /**\n * @dev Decrease partial or all liquidity from the pool.\n * @param _liquidityToDecrease The amount of liquidity to remove denominated in 1e18\n */\n function _removeLiquidity(uint256 _liquidityToDecrease) internal {\n require(_liquidityToDecrease > 0, \"Must remove some liquidity\");\n require(\n _liquidityToDecrease <= 1e18,\n \"Can not remove more than 100% of liquidity\"\n );\n\n // 0 indicates the first (and only) bin in the NFT LP position.\n IMaverickV2Pool.RemoveLiquidityParams memory params = maverickPosition\n .getRemoveParams(tokenId, 0, _liquidityToDecrease);\n (uint256 _amountWeth, uint256 _amountOeth) = maverickPosition\n .removeLiquidityToSender(tokenId, mPool, params);\n\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n\n // needs to be called after the _updateUnderlyingAssets so the updated amount is reflected\n // in the event\n emit LiquidityRemoved(\n _liquidityToDecrease,\n _amountWeth,\n _amountOeth,\n underlyingAssets\n );\n }\n\n /**\n * @dev Burns any OETH tokens remaining on the strategy contract if the balance is\n * above the action threshold.\n */\n function _burnOethOnTheContract() internal {\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(_oethBalance);\n }\n\n /**\n * @notice Returns the percentage of WETH liquidity in the configured ticker\n * owned by this strategy contract.\n * @return uint256 1e18 denominated percentage expressing the share\n */\n function getWETHShare() external view returns (uint256) {\n uint256 _currentPrice = getPoolSqrtPrice();\n return _getWethShare(_currentPrice);\n }\n\n /**\n * @dev Returns the share of WETH in tick denominated in 1e18\n */\n function _getWethShare(uint256 _currentPrice)\n internal\n view\n returns (uint256)\n {\n (\n uint256 wethAmount,\n uint256 oethAmount\n ) = _reservesInTickForGivenPriceAndLiquidity(\n sqrtPriceTickLower,\n sqrtPriceTickHigher,\n _currentPrice,\n 1e24\n );\n\n return wethAmount.divPrecisely(wethAmount + oethAmount);\n }\n\n /**\n * @notice Returns the current pool price in square root\n * @return Square root of the pool price\n */\n function getPoolSqrtPrice() public view returns (uint256) {\n return poolLens.getPoolSqrtPrice(mPool);\n }\n\n /**\n * @notice Returns the current active trading tick of the underlying pool\n * @return _currentTick Current pool trading tick\n */\n function getCurrentTradingTick() public view returns (int32 _currentTick) {\n _currentTick = mPool.getState().activeTick;\n }\n\n /**\n * @notice Mint the initial NFT position\n *\n * @dev This amount is \"gifted\" to the strategy contract and will count as a yield\n * surplus.\n */\n // slither-disable-start reentrancy-no-eth\n function mintInitialPosition() external onlyGovernor nonReentrant {\n require(tokenId == 0, \"Initial position already minted\");\n (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n ) = _getAddLiquidityParams(1e16, 1e16);\n\n // Mint rounded up OETH amount\n if (OETHRequired > 0) {\n IVault(vaultAddress).mintForStrategy(OETHRequired);\n }\n\n _approveTokenAmounts(WETHRequired, OETHRequired);\n\n // Store the tokenId before calling updateUnderlyingAssets as it relies on the tokenId\n // not being 0\n (, , , tokenId) = liquidityManager.mintPositionNftToSender(\n mPool,\n packedSqrtPriceBreaks,\n packedArgs\n );\n\n // burn remaining OETH\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @notice Returns the balance of tokens the strategy holds in the LP position\n * @return _amountWeth Amount of WETH in position\n * @return _amountOeth Amount of OETH in position\n */\n function getPositionPrincipal()\n public\n view\n returns (uint256 _amountWeth, uint256 _amountOeth)\n {\n if (tokenId == 0) {\n return (0, 0);\n }\n\n (_amountWeth, _amountOeth, ) = _getPositionInformation();\n }\n\n /**\n * @dev Returns the balance of tokens the strategy holds in the LP position\n * @return _amountWeth Amount of WETH in position\n * @return _amountOeth Amount of OETH in position\n * @return liquidity Amount of liquidity in the position\n */\n function _getPositionInformation()\n internal\n view\n returns (\n uint256 _amountWeth,\n uint256 _amountOeth,\n uint256 liquidity\n )\n {\n IMaverickV2Position.PositionFullInformation\n memory positionInfo = maverickPosition.tokenIdPositionInformation(\n tokenId,\n 0\n );\n\n require(\n positionInfo.liquidities.length == 1,\n \"Unexpected liquidities length\"\n );\n require(positionInfo.ticks.length == 1, \"Unexpected ticks length\");\n\n _amountWeth = positionInfo.amountA;\n _amountOeth = positionInfo.amountB;\n liquidity = positionInfo.liquidities[0];\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99.8%) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethSupply = IERC20(OETH).totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethSupply) < SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /**\n * @dev Collect Rooster reward token, and send it to the harvesterAddress\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Do nothing if there's no position minted\n if (tokenId > 0) {\n uint32[] memory binIds = new uint32[](1);\n IMaverickV2Pool.TickState memory tickState = mPool.getTick(\n TICK_NUMBER\n );\n // get the binId for the MAV_STATIC_BIN_KIND in tick TICK_NUMBER (-1)\n binIds[0] = tickState.binIdsByTick[0];\n\n uint256 lastEpoch = votingDistributor.lastEpoch();\n\n poolDistributor.claimLp(\n address(this),\n tokenId,\n mPool,\n binIds,\n lastEpoch\n );\n }\n\n // Run the internal inherited function\n _collectRewardTokens();\n }\n\n /***************************************\n Balances and Fees\n ****************************************/\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256)\n {\n require(_asset == WETH, \"Only WETH supported\");\n\n // because of PoolLens inaccuracy there is usually some dust WETH left on the contract\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n // just paranoia check, in case there is OETH in the strategy that for some reason hasn't\n // been burned yet. This should always be 0.\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n return underlyingAssets + _wethBalance + _oethBalance;\n }\n\n /// @dev This function updates the amount of underlying assets with the approach of the least possible\n /// total tokens extracted for the current liquidity in the pool.\n function _updateUnderlyingAssets() internal {\n /**\n * Our net value represent the smallest amount of tokens we are able to extract from the position\n * given our liquidity.\n *\n * The least amount of tokens ex-tractable from the position is where the active trading price is\n * at the edge between tick -1 & tick 0. There the pool is offering 1:1 trades between WETH & OETH.\n * At that moment the pool consists completely of WETH and no OETH.\n *\n * The more swaps from OETH -> WETH happen on the pool the more the price starts to move away from the tick 0\n * towards the middle of tick -1 making OETH (priced in WETH) cheaper.\n */\n\n uint256 _wethAmount = tokenId == 0 ? 0 : _balanceInPosition();\n\n underlyingAssets = _wethAmount;\n emit UnderlyingAssetsUpdated(_wethAmount);\n }\n\n /**\n * @dev Strategy reserves (which consist only of WETH in case of Rooster - Plume pool)\n * when the tick price is closest to parity - assuring the lowest amount of tokens\n * returned for the current position liquidity.\n */\n function _balanceInPosition() internal view returns (uint256 _wethBalance) {\n (, , uint256 liquidity) = _getPositionInformation();\n\n uint256 _oethBalance;\n\n (_wethBalance, _oethBalance) = _reservesInTickForGivenPriceAndLiquidity(\n sqrtPriceTickLower,\n sqrtPriceTickHigher,\n sqrtPriceAtParity,\n liquidity\n );\n\n require(_oethBalance == 0, \"Non zero oethBalance\");\n }\n\n /**\n * @notice Tick dominance denominated in 1e18\n * @return _tickDominance The share of liquidity in TICK_NUMBER tick owned\n * by the strategy contract denominated in 1e18\n */\n function tickDominance() public view returns (uint256 _tickDominance) {\n IMaverickV2Pool.TickState memory tickState = mPool.getTick(TICK_NUMBER);\n\n uint256 wethReserve = tickState.reserveA;\n uint256 oethReserve = tickState.reserveB;\n\n // prettier-ignore\n (uint256 _amountWeth, uint256 _amountOeth, ) = _getPositionInformation();\n\n if (wethReserve + oethReserve == 0) {\n return 0;\n }\n\n _tickDominance = (_amountWeth + _amountOeth).divPrecisely(\n wethReserve + oethReserve\n );\n }\n\n /***************************************\n Hidden functions\n ****************************************/\n /**\n * @dev Unsupported\n */\n function setPTokenAddress(address, address) external pure override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function removePToken(uint256) external pure override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function _abstractSetPToken(address, address) internal pure override {\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function safeApproveAllTokens() external pure override {\n // all the amounts are approved at the time required\n revert(\"Unsupported method\");\n }\n\n /***************************************\n Maverick liquidity utilities\n ****************************************/\n\n /// @notice Calculates deltaA = liquidity * (sqrt(upper) - sqrt(lower))\n /// Calculates deltaB = liquidity / sqrt(lower) - liquidity / sqrt(upper),\n /// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower))\n ///\n /// @dev refactored from here:\n // solhint-disable-next-line max-line-length\n /// https://github.com/rooster-protocol/rooster-contracts/blob/main/v2-supplemental/contracts/libraries/LiquidityUtilities.sol#L665-L695\n function _reservesInTickForGivenPriceAndLiquidity(\n uint256 _lowerSqrtPrice,\n uint256 _upperSqrtPrice,\n uint256 _newSqrtPrice,\n uint256 _liquidity\n ) internal pure returns (uint128 reserveA, uint128 reserveB) {\n if (_liquidity == 0) {\n (reserveA, reserveB) = (0, 0);\n } else {\n uint256 lowerEdge = MathRooster.max(_lowerSqrtPrice, _newSqrtPrice);\n\n reserveA = MathRooster\n .mulCeil(\n _liquidity,\n MathRooster.clip(\n MathRooster.min(_upperSqrtPrice, _newSqrtPrice),\n _lowerSqrtPrice\n )\n )\n .toUint128();\n reserveB = MathRooster\n .mulDivCeil(\n _liquidity,\n 1e18 * MathRooster.clip(_upperSqrtPrice, lowerEdge),\n _upperSqrtPrice * lowerEdge\n )\n .toUint128();\n }\n }\n}\n" + }, + "contracts/strategies/sonic/SonicStakingStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SonicValidatorDelegator } from \"./SonicValidatorDelegator.sol\";\nimport { IWrappedSonic } from \"../../interfaces/sonic/IWrappedSonic.sol\";\n\n/**\n * @title Staking Strategy for Sonic's native S currency\n * @author Origin Protocol Inc\n */\ncontract SonicStakingStrategy is SonicValidatorDelegator {\n // For future use\n uint256[50] private __gap;\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wrappedSonic,\n address _sfc\n ) SonicValidatorDelegator(_baseConfig, _wrappedSonic, _sfc) {}\n\n /// @notice Deposit wrapped S asset into the underlying platform.\n /// @param _asset Address of asset to deposit. Has to be Wrapped Sonic (wS).\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposit Wrapped Sonic (wS) to this strategy and delegate to a validator.\n * @param _asset Address of Wrapped Sonic (wS) token\n * @param _amount Amount of Wrapped Sonic (wS) to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal virtual {\n require(_amount > 0, \"Must deposit something\");\n\n _delegate(_amount);\n emit Deposit(_asset, address(0), _amount);\n }\n\n /**\n * @notice Deposit the entire balance of wrapped S in this strategy contract into\n * the underlying platform.\n */\n function depositAll() external virtual override onlyVault nonReentrant {\n uint256 wSBalance = IERC20(wrappedSonic).balanceOf(address(this));\n\n if (wSBalance > 0) {\n _deposit(wrappedSonic, wSBalance);\n }\n }\n\n /// @notice Withdraw Wrapped Sonic (wS) from this strategy contract.\n /// Used only if some wS is lingering on the contract.\n /// That can happen only when someone sends wS directly to this contract\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset Address of the Wrapped Sonic (wS) token\n /// @param _amount Amount of Wrapped Sonic (wS) to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal override {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(_asset).transfer(_recipient, _amount);\n\n emit Withdrawal(wrappedSonic, address(0), _amount);\n }\n\n /// @notice Transfer all Wrapped Sonic (wS) deposits back to the vault.\n /// This does not withdraw from delegated validators. That has to be done separately with `undelegate`.\n /// Any native S in this strategy will be withdrawn.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 balance = address(this).balance;\n if (balance > 0) {\n IWrappedSonic(wrappedSonic).deposit{ value: balance }();\n }\n uint256 wSBalance = IERC20(wrappedSonic).balanceOf(address(this));\n if (wSBalance > 0) {\n _withdraw(vaultAddress, wrappedSonic, wSBalance);\n }\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset token\n */\n function supportsAsset(address _asset)\n public\n view\n virtual\n override\n returns (bool)\n {\n return _asset == wrappedSonic;\n }\n\n /**\n * @notice is not supported for this strategy as the\n * Wrapped Sonic (wS) token is set at deploy time.\n */\n function setPTokenAddress(address, address)\n external\n view\n override\n onlyGovernor\n {\n revert(\"unsupported function\");\n }\n\n /// @notice is not used by this strategy as all staking rewards are restaked\n function collectRewardTokens() external override nonReentrant {\n revert(\"unsupported function\");\n }\n\n /**\n * @notice is not supported for this strategy as the\n * Wrapped Sonic (wS) token is set at deploy time.\n */\n function removePToken(uint256) external view override onlyGovernor {\n revert(\"unsupported function\");\n }\n\n /// @dev is not used by this strategy but must be implemented as it's abstract\n /// in the inherited `InitializableAbstractStrategy` contract.\n function _abstractSetPToken(address, address) internal virtual override {}\n\n /// @notice is not used by this strategy\n function safeApproveAllTokens() external override onlyGovernor {}\n}\n" + }, + "contracts/strategies/sonic/SonicSwapXAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title SwapX Algorithmic Market Maker (AMO) Strategy\n * @notice AMO strategy for the SwapX OS/wS stable pool\n * @author Origin Protocol Inc\n */\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport { sqrt } from \"../../utils/PRBMath.sol\";\nimport { IBasicToken } from \"../../interfaces/IBasicToken.sol\";\nimport { IPair } from \"../../interfaces/sonic/ISwapXPair.sol\";\nimport { IGauge } from \"../../interfaces/sonic/ISwapXGauge.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\n\ncontract SonicSwapXAMOStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @notice a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n /// @notice Precision for the SwapX Stable AMM (sAMM) invariant k.\n uint256 public constant PRECISION = 1e18;\n\n /// @notice Address of the Wrapped S (wS) token.\n address public immutable ws;\n\n /// @notice Address of the OS token contract.\n address public immutable os;\n\n /// @notice Address of the SwapX Stable pool contract.\n address public immutable pool;\n\n /// @notice Address of the SwapX Gauge contract.\n address public immutable gauge;\n\n /// @notice The max amount the OS/wS price can deviate from peg (1e18)\n /// before deposits are reverted scaled to 18 decimals.\n /// eg 0.01e18 or 1e16 is 1% which is 100 basis points.\n /// This is the amount below and above peg so a 50 basis point deviation (0.005e18)\n /// allows a price range from 0.995 to 1.005.\n uint256 public maxDepeg;\n\n event SwapOTokensToPool(\n uint256 osMinted,\n uint256 wsDepositAmount,\n uint256 osDepositAmount,\n uint256 lpTokens\n );\n event SwapAssetsToPool(\n uint256 wsSwapped,\n uint256 lpTokens,\n uint256 osBurnt\n );\n event MaxDepegUpdated(uint256 maxDepeg);\n\n /**\n * @dev Verifies that the caller is the Strategist of the Vault.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Skim the SwapX pool in case any extra wS or OS tokens were added\n */\n modifier skimPool() {\n IPair(pool).skim(address(this));\n _;\n }\n\n /**\n * @dev Checks the pool is balanced enough to allow deposits.\n */\n modifier nearBalancedPool() {\n // OS/wS price = wS / OS\n // Get the OS/wS price for selling 1 OS for wS\n // As OS is 1, the wS amount is the OS/wS price\n uint256 sellPrice = IPair(pool).getAmountOut(1e18, os);\n\n // Get the amount of OS received from selling 1 wS. This is buying OS.\n uint256 osAmount = IPair(pool).getAmountOut(1e18, ws);\n // Convert to a OS/wS price = wS / OS\n uint256 buyPrice = 1e36 / osAmount;\n\n uint256 pegPrice = 1e18;\n\n require(\n sellPrice >= pegPrice - maxDepeg && buyPrice <= pegPrice + maxDepeg,\n \"price out of range\"\n );\n _;\n }\n\n /**\n * @dev Checks the pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do swaps against the pool.\n * Deposits and withdrawals are proportional to the pool's balances hence don't need this check.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the pool\n (uint256 wsReservesBefore, uint256 osReservesBefore, ) = IPair(pool)\n .getReserves();\n // diff = wS balance - OS balance\n int256 diffBefore = wsReservesBefore.toInt256() -\n osReservesBefore.toInt256();\n\n _;\n\n // Get the asset and OToken balances in the pool\n (uint256 wsReservesAfter, uint256 osReservesAfter, ) = IPair(pool)\n .getReserves();\n // diff = wS balance - OS balance\n int256 diffAfter = wsReservesAfter.toInt256() -\n osReservesAfter.toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OS, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"Assets overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of wS, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"OTokens overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n /**\n * @param _baseConfig The `platformAddress` is the address of the SwapX pool.\n * The `vaultAddress` is the address of the Origin Sonic Vault.\n * @param _os Address of the OS token.\n * @param _ws Address of the Wrapped S (wS) token.\n * @param _gauge Address of the SwapX gauge for the pool.\n */\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _os,\n address _ws,\n address _gauge\n ) InitializableAbstractStrategy(_baseConfig) {\n // Check the pool tokens are correct\n require(\n IPair(_baseConfig.platformAddress).token0() == _ws &&\n IPair(_baseConfig.platformAddress).token1() == _os,\n \"Incorrect pool tokens\"\n );\n // Checked both tokens are to 18 decimals\n require(\n IBasicToken(_ws).decimals() == 18 &&\n IBasicToken(_os).decimals() == 18,\n \"Incorrect token decimals\"\n );\n // Check the SwapX pool is a Stable AMM (sAMM)\n require(\n IPair(_baseConfig.platformAddress).isStable() == true,\n \"Pool not stable\"\n );\n // Check the gauge is for the pool\n require(\n IGauge(_gauge).TOKEN() == _baseConfig.platformAddress,\n \"Incorrect gauge\"\n );\n\n // Set the immutable variables\n os = _os;\n ws = _ws;\n pool = _baseConfig.platformAddress;\n gauge = _gauge;\n\n // This is an implementation contract. The governor is set in the proxy contract.\n _setGovernor(address(0));\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as SwapX strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Array containing SWPx token address\n * @param _maxDepeg The max amount the OS/wS price can deviate from peg (1e18) before deposits are reverted.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n uint256 _maxDepeg\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = pool;\n\n address[] memory _assets = new address[](1);\n _assets[0] = ws;\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n maxDepeg = _maxDepeg;\n\n _approveBase();\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit an amount of Wrapped S (wS) into the SwapX pool.\n * Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @dev This tx must be wrapped by the VaultValueChecker.\n * To minimize loses, the pool should be rebalanced before depositing.\n * The pool's OS/wS price must be within the maxDepeg range.\n * @param _asset Address of Wrapped S (wS) token.\n * @param _wsAmount Amount of Wrapped S (wS) tokens to deposit.\n */\n function deposit(address _asset, uint256 _wsAmount)\n external\n override\n onlyVault\n nonReentrant\n skimPool\n nearBalancedPool\n {\n require(_asset == ws, \"Unsupported asset\");\n require(_wsAmount > 0, \"Must deposit something\");\n\n (uint256 osDepositAmount, ) = _deposit(_wsAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the deposited wS tokens\n emit Deposit(ws, pool, _wsAmount);\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osDepositAmount);\n }\n\n /**\n * @notice Deposit all the strategy's Wrapped S (wS) tokens into the SwapX pool.\n * Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @dev This tx must be wrapped by the VaultValueChecker.\n * To minimize loses, the pool should be rebalanced before depositing.\n * The pool's OS/wS price must be within the maxDepeg range.\n */\n function depositAll()\n external\n override\n onlyVault\n nonReentrant\n skimPool\n nearBalancedPool\n {\n uint256 wsBalance = IERC20(ws).balanceOf(address(this));\n if (wsBalance > 0) {\n (uint256 osDepositAmount, ) = _deposit(wsBalance);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the deposited wS tokens\n emit Deposit(ws, pool, wsBalance);\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osDepositAmount);\n }\n }\n\n /**\n * @dev Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @param _wsAmount Amount of Wrapped S (wS) tokens to deposit.\n * @return osDepositAmount Amount of OS tokens minted and deposited into the pool.\n * @return lpTokens Amount of SwapX pool LP tokens minted and deposited into the gauge.\n */\n function _deposit(uint256 _wsAmount)\n internal\n returns (uint256 osDepositAmount, uint256 lpTokens)\n {\n // Calculate the required amount of OS to mint based on the wS amount.\n osDepositAmount = _calcTokensToMint(_wsAmount);\n\n // Mint the required OS tokens to this strategy\n IVault(vaultAddress).mintForStrategy(osDepositAmount);\n\n // Add wS and OS liquidity to the pool and stake in gauge\n lpTokens = _depositToPoolAndGauge(_wsAmount, osDepositAmount);\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw wS and OS from the SwapX pool, burn the OS,\n * and transfer the wS to the recipient.\n * @param _recipient Address of the Vault.\n * @param _asset Address of the Wrapped S (wS) contract.\n * @param _wsAmount Amount of Wrapped S (wS) to withdraw.\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _wsAmount\n ) external override onlyVault nonReentrant skimPool {\n require(_wsAmount > 0, \"Must withdraw something\");\n require(_asset == ws, \"Unsupported asset\");\n // This strategy can't be set as a default strategy for wS in the Vault.\n // This means the recipient must always be the Vault.\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n // Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n uint256 lpTokens = _calcTokensToBurn(_wsAmount);\n\n // Withdraw pool LP tokens from the gauge and remove assets from from the pool\n _withdrawFromGaugeAndPool(lpTokens);\n\n // Burn all the removed OS and any that was left in the strategy\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Transfer wS to the recipient\n // Note there can be a dust amount of wS left in the strategy as\n // the burn of the pool's LP tokens is rounded up\n require(\n IERC20(ws).balanceOf(address(this)) >= _wsAmount,\n \"Not enough wS removed from pool\"\n );\n IERC20(ws).safeTransfer(_recipient, _wsAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the withdrawn wS tokens\n emit Withdrawal(ws, pool, _wsAmount);\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n }\n\n /**\n * @notice Withdraw all pool LP tokens from the gauge,\n * remove all wS and OS from the SwapX pool,\n * burn all the OS tokens,\n * and transfer all the wS to the Vault contract.\n * @dev There is no solvency check here as withdrawAll can be called to\n * quickly secure assets to the Vault in emergencies.\n */\n function withdrawAll()\n external\n override\n onlyVaultOrGovernor\n nonReentrant\n skimPool\n {\n // Get all the pool LP tokens the strategy has staked in the gauge\n uint256 lpTokens = IGauge(gauge).balanceOf(address(this));\n // Can not withdraw zero LP tokens from the gauge\n if (lpTokens == 0) return;\n\n if (IGauge(gauge).emergency()) {\n // The gauge is in emergency mode\n _emergencyWithdrawFromGaugeAndPool();\n } else {\n // Withdraw pool LP tokens from the gauge and remove assets from from the pool\n _withdrawFromGaugeAndPool(lpTokens);\n }\n\n // Burn all OS in this strategy contract\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Get the strategy contract's wS balance.\n // This includes all that was removed from the SwapX pool and\n // any that was sitting in the strategy contract before the removal.\n uint256 wsBalance = IERC20(ws).balanceOf(address(this));\n IERC20(ws).safeTransfer(vaultAddress, wsBalance);\n\n // Emit event for the withdrawn wS tokens\n emit Withdrawal(ws, pool, wsBalance);\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n }\n\n /***************************************\n Pool Rebalancing\n ****************************************/\n\n /** @notice Used when there is more OS than wS in the pool.\n * wS and OS is removed from the pool, the received wS is swapped for OS\n * and the left over OS in the strategy is burnt.\n * The OS/wS price is < 1.0 so OS is being bought at a discount.\n * @param _wsAmount Amount of Wrapped S (wS) to swap into the pool.\n */\n function swapAssetsToPool(uint256 _wsAmount)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n skimPool\n {\n require(_wsAmount > 0, \"Must swap something\");\n\n // 1. Partially remove liquidity so there’s enough wS for the swap\n\n // Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n uint256 lpTokens = _calcTokensToBurn(_wsAmount);\n require(lpTokens > 0, \"No LP tokens to burn\");\n\n _withdrawFromGaugeAndPool(lpTokens);\n\n // 2. Swap wS for OS against the pool\n // Swap exact amount of wS for OS against the pool\n // There can be a dust amount of wS left in the strategy as the burn of the pool's LP tokens is rounded up\n _swapExactTokensForTokens(_wsAmount, ws, os);\n\n // 3. Burn all the OS left in the strategy from the remove liquidity and swap\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n // Emit event for the swap\n emit SwapAssetsToPool(_wsAmount, lpTokens, osToBurn);\n }\n\n /**\n * @notice Used when there is more wS than OS in the pool.\n * OS is minted and swapped for wS against the pool,\n * more OS is minted and added back into the pool with the swapped out wS.\n * The OS/wS price is > 1.0 so OS is being sold at a premium.\n * @param _osAmount Amount of OS to swap into the pool.\n */\n function swapOTokensToPool(uint256 _osAmount)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n skimPool\n {\n require(_osAmount > 0, \"Must swap something\");\n\n // 1. Mint OS so it can be swapped into the pool\n\n // There can be OS in the strategy from skimming the pool\n uint256 osInStrategy = IERC20(os).balanceOf(address(this));\n require(_osAmount >= osInStrategy, \"Too much OS in strategy\");\n uint256 osToMint = _osAmount - osInStrategy;\n\n // Mint the required OS tokens to this strategy\n IVault(vaultAddress).mintForStrategy(osToMint);\n\n // 2. Swap OS for wS against the pool\n _swapExactTokensForTokens(_osAmount, os, ws);\n\n // The wS is from the swap and any wS that was sitting in the strategy\n uint256 wsDepositAmount = IERC20(ws).balanceOf(address(this));\n\n // 3. Add wS and OS back to the pool in proportion to the pool's reserves\n (uint256 osDepositAmount, uint256 lpTokens) = _deposit(wsDepositAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osToMint + osDepositAmount);\n // Emit event for the swap\n emit SwapOTokensToPool(\n osToMint,\n wsDepositAmount,\n osDepositAmount,\n lpTokens\n );\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Get the wS value of assets in the strategy and SwapX pool.\n * The value of the assets in the pool is calculated assuming the pool is balanced.\n * This way the value can not be manipulated by changing the pool's token balances.\n * @param _asset Address of the Wrapped S (wS) token\n * @return balance Total value in wS.\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == ws, \"Unsupported asset\");\n\n // wS balance needed here for the balance check that happens from vault during depositing.\n balance = IERC20(ws).balanceOf(address(this));\n\n // This assumes 1 gauge LP token = 1 pool LP token\n uint256 lpTokens = IGauge(gauge).balanceOf(address(this));\n if (lpTokens == 0) return balance;\n\n // Add the strategy’s share of the wS and OS tokens in the SwapX pool if the pool was balanced.\n balance += _lpValue(lpTokens);\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == ws;\n }\n\n /**\n * @notice Collect accumulated SWPx (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect SWPx rewards from the gauge\n IGauge(gauge).getReward();\n\n _collectRewardTokens();\n }\n\n /***************************************\n Internal SwapX Pool and Gauge Functions\n ****************************************/\n\n /**\n * @dev Calculate the required amount of OS to mint based on the wS amount.\n * This ensures the proportion of OS tokens being added to the pool matches the proportion of wS tokens.\n * For example, if the added wS tokens is 10% of existing wS tokens in the pool,\n * then the OS tokens being added should also be 10% of the OS tokens in the pool.\n * @param _wsAmount Amount of Wrapped S (wS) to be added to the pool.\n * @return osAmount Amount of OS to be minted and added to the pool.\n */\n function _calcTokensToMint(uint256 _wsAmount)\n internal\n view\n returns (uint256 osAmount)\n {\n (uint256 wsReserves, uint256 osReserves, ) = IPair(pool).getReserves();\n require(wsReserves > 0, \"Empty pool\");\n\n // OS to add = (wS being added * OS in pool) / wS in pool\n osAmount = (_wsAmount * osReserves) / wsReserves;\n }\n\n /**\n * @dev Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n * from the pool.\n * @param _wsAmount Amount of Wrapped S (wS) to be removed from the pool.\n * @return lpTokens Amount of SwapX pool LP tokens to burn.\n */\n function _calcTokensToBurn(uint256 _wsAmount)\n internal\n view\n returns (uint256 lpTokens)\n {\n /* The SwapX pool proportionally returns the reserve tokens when removing liquidity.\n * First, calculate the proportion of required wS tokens against the pools wS reserves.\n * That same proportion is used to calculate the required amount of pool LP tokens.\n * For example, if the required wS tokens is 10% of the pool's wS reserves,\n * then 10% of the pool's LP supply needs to be burned.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognizant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on, the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n (uint256 wsReserves, , ) = IPair(pool).getReserves();\n require(wsReserves > 0, \"Empty pool\");\n\n lpTokens = (_wsAmount * IPair(pool).totalSupply()) / wsReserves;\n lpTokens += 1; // Add 1 to ensure we get enough LP tokens with rounding\n }\n\n /**\n * @dev Deposit Wrapped S (wS) and OS liquidity to the SwapX pool\n * and stake the pool's LP token in the gauge.\n * @param _wsAmount Amount of Wrapped S (wS) to deposit.\n * @param _osAmount Amount of OS to deposit.\n * @return lpTokens Amount of SwapX pool LP tokens minted.\n */\n function _depositToPoolAndGauge(uint256 _wsAmount, uint256 _osAmount)\n internal\n returns (uint256 lpTokens)\n {\n // Transfer wS to the pool\n IERC20(ws).safeTransfer(pool, _wsAmount);\n // Transfer OS to the pool\n IERC20(os).safeTransfer(pool, _osAmount);\n\n // Mint LP tokens from the pool\n lpTokens = IPair(pool).mint(address(this));\n\n // Deposit the pool's LP tokens into the gauge\n IGauge(gauge).deposit(lpTokens);\n }\n\n /**\n * @dev Withdraw pool LP tokens from the gauge and remove wS and OS from the pool.\n * @param _lpTokens Amount of SwapX pool LP tokens to withdraw from the gauge\n */\n function _withdrawFromGaugeAndPool(uint256 _lpTokens) internal {\n require(\n IGauge(gauge).balanceOf(address(this)) >= _lpTokens,\n \"Not enough LP tokens in gauge\"\n );\n\n // Withdraw pool LP tokens from the gauge\n IGauge(gauge).withdraw(_lpTokens);\n\n // Transfer the pool LP tokens to the pool\n IERC20(pool).safeTransfer(pool, _lpTokens);\n\n // Burn the LP tokens and transfer the wS and OS back to the strategy\n IPair(pool).burn(address(this));\n }\n\n /**\n * @dev Withdraw all pool LP tokens from the gauge when it's in emergency mode\n * and remove wS and OS from the pool.\n */\n function _emergencyWithdrawFromGaugeAndPool() internal {\n // Withdraw all pool LP tokens from the gauge\n IGauge(gauge).emergencyWithdraw();\n\n // Get the pool LP tokens in strategy\n uint256 _lpTokens = IERC20(pool).balanceOf(address(this));\n\n // Transfer the pool LP tokens to the pool\n IERC20(pool).safeTransfer(pool, _lpTokens);\n\n // Burn the LP tokens and transfer the wS and OS back to the strategy\n IPair(pool).burn(address(this));\n }\n\n /**\n * @dev Swap exact amount of tokens for another token against the pool.\n * @param _amountIn Amount of tokens to swap into the pool.\n * @param _tokenIn Address of the token going into the pool.\n * @param _tokenOut Address of the token being swapped out of the pool.\n */\n function _swapExactTokensForTokens(\n uint256 _amountIn,\n address _tokenIn,\n address _tokenOut\n ) internal {\n // Transfer in tokens to the pool\n IERC20(_tokenIn).safeTransfer(pool, _amountIn);\n\n // Calculate how much out tokens we get from the swap\n uint256 amountOut = IPair(pool).getAmountOut(_amountIn, _tokenIn);\n\n // Safety check that we are dealing with the correct pool tokens\n require(\n (_tokenIn == ws && _tokenOut == os) ||\n (_tokenIn == os && _tokenOut == ws),\n \"Unsupported swap\"\n );\n\n // Work out the correct order of the amounts for the pool\n (uint256 amount0, uint256 amount1) = _tokenIn == ws\n ? (uint256(0), amountOut)\n : (amountOut, 0);\n\n // Perform the swap on the pool\n IPair(pool).swap(amount0, amount1, address(this), new bytes(0));\n\n // The slippage protection against the amount out is indirectly done\n // via the improvePoolBalance\n }\n\n /// @dev Calculate the value of a LP position in a SwapX stable pool\n /// if the pool was balanced.\n /// @param _lpTokens Amount of LP tokens in the SwapX pool\n /// @return value The wS value of the LP tokens when the pool is balanced\n function _lpValue(uint256 _lpTokens) internal view returns (uint256 value) {\n // Get total supply of LP tokens\n uint256 totalSupply = IPair(pool).totalSupply();\n if (totalSupply == 0) return 0;\n\n // Get the current reserves of the pool\n (uint256 wsReserves, uint256 osReserves, ) = IPair(pool).getReserves();\n\n // Calculate the invariant of the pool assuming both tokens have 18 decimals.\n // k is scaled to 18 decimals.\n uint256 k = _invariant(wsReserves, osReserves);\n\n // If x = y, let’s denote x = y = z (where z is the common reserve value)\n // Substitute z into the invariant:\n // k = z^3 * z + z * z^3\n // k = 2 * z^4\n // Going back the other way to calculate the common reserve value z\n // z = (k / 2) ^ (1/4)\n // the total value of the pool when x = y is 2 * z, which is 2 * (k / 2) ^ (1/4)\n uint256 zSquared = sqrt((k * 1e18) / 2); // 18 + 18 = 36 decimals becomes 18 decimals after sqrt\n uint256 z = sqrt(zSquared * 1e18); // 18 + 18 = 36 decimals becomes 18 decimals after sqrt\n uint256 totalValueOfPool = 2 * z;\n\n // lp value = lp tokens * value of pool / total supply\n value = (_lpTokens * totalValueOfPool) / totalSupply;\n }\n\n /**\n * @dev Compute the invariant for a SwapX stable pool.\n * This assumed both x and y tokens are to 18 decimals which is checked in the constructor.\n * invariant: k = x^3 * y + x * y^3\n * @dev This implementation is copied from SwapX's Pair contract.\n * @param _x The amount of Wrapped S (wS) tokens in the pool\n * @param _y The amount of the OS tokens in the pool\n * @return k The invariant of the SwapX stable pool\n */\n function _invariant(uint256 _x, uint256 _y)\n internal\n pure\n returns (uint256 k)\n {\n uint256 _a = (_x * _y) / PRECISION;\n uint256 _b = ((_x * _x) / PRECISION + (_y * _y) / PRECISION);\n // slither-disable-next-line divide-before-multiply\n k = (_a * _b) / PRECISION;\n }\n\n /**\n * @dev Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalSupply = IERC20(os).totalSupply();\n\n if (\n _totalSupply > 0 &&\n _totalVaultValue.divPrecisely(_totalSupply) < SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Setters\n ****************************************/\n\n /**\n * @notice Set the maximum deviation from the OS/wS peg (1e18) before deposits are reverted.\n * @param _maxDepeg the OS/wS price from peg (1e18) in 18 decimals.\n * eg 0.01e18 or 1e16 is 1% which is 100 basis points.\n */\n function setMaxDepeg(uint256 _maxDepeg) external onlyGovernor {\n maxDepeg = _maxDepeg;\n\n emit MaxDepegUpdated(_maxDepeg);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve SwapX gauge contract to transfer SwapX pool LP tokens\n // This is needed for deposits of SwapX pool LP tokens into the gauge.\n // slither-disable-next-line unused-return\n IPair(pool).approve(address(gauge), type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/sonic/SonicValidatorDelegator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { ISFC } from \"../../interfaces/sonic/ISFC.sol\";\nimport { IWrappedSonic } from \"../../interfaces/sonic/IWrappedSonic.sol\";\n\n/**\n * @title Manages delegation to Sonic validators\n * @notice This contract implements all the required functionality to delegate to,\n undelegate from and withdraw from validators.\n * @author Origin Protocol Inc\n */\nabstract contract SonicValidatorDelegator is InitializableAbstractStrategy {\n /// @notice Address of Sonic's wrapped S token\n address public immutable wrappedSonic;\n /// @notice Sonic's Special Fee Contract (SFC)\n ISFC public immutable sfc;\n\n /// @notice a unique ID for each withdrawal request\n uint256 public nextWithdrawId;\n /// @notice Sonic (S) that is pending withdrawal after undelegating\n uint256 public pendingWithdrawals;\n\n /// @notice List of supported validator IDs that can be delegated to\n uint256[] public supportedValidators;\n\n /// @notice Default validator id to deposit to\n uint256 public defaultValidatorId;\n\n struct WithdrawRequest {\n uint256 validatorId;\n uint256 undelegatedAmount;\n uint256 timestamp;\n }\n /// @notice Mapping of withdrawIds to validatorIds and undelegatedAmounts\n mapping(uint256 => WithdrawRequest) public withdrawals;\n\n /// @notice Address of the registrator - allowed to register, exit and remove validators\n address public validatorRegistrator;\n\n // For future use\n uint256[44] private __gap;\n\n event Delegated(uint256 indexed validatorId, uint256 delegatedAmount);\n event Undelegated(\n uint256 indexed withdrawId,\n uint256 indexed validatorId,\n uint256 undelegatedAmount\n );\n event Withdrawn(\n uint256 indexed withdrawId,\n uint256 indexed validatorId,\n uint256 undelegatedAmount,\n uint256 withdrawnAmount\n );\n event RegistratorChanged(address indexed newAddress);\n event SupportedValidator(uint256 indexed validatorId);\n event UnsupportedValidator(uint256 indexed validatorId);\n event DefaultValidatorIdChanged(uint256 indexed validatorId);\n\n /// @dev Throws if called by any account other than the Registrator or Strategist\n modifier onlyRegistratorOrStrategist() {\n require(\n msg.sender == validatorRegistrator ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Registrator or Strategist\"\n );\n _;\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wrappedSonic,\n address _sfc\n ) InitializableAbstractStrategy(_baseConfig) {\n wrappedSonic = _wrappedSonic;\n sfc = ISFC(_sfc);\n }\n\n function initialize() external virtual onlyGovernor initializer {\n address[] memory rewardTokens = new address[](0);\n address[] memory assets = new address[](1);\n address[] memory pTokens = new address[](1);\n\n assets[0] = address(wrappedSonic);\n pTokens[0] = address(platformAddress);\n\n InitializableAbstractStrategy._initialize(\n rewardTokens,\n assets,\n pTokens\n );\n }\n\n /// @notice Returns the total value of Sonic (S) that is delegated validators.\n /// Wrapped Sonic (wS) deposits that are still to be delegated and any undelegated amounts\n /// still pending a withdrawal.\n /// @param _asset Address of Wrapped Sonic (wS) token\n /// @return balance Total value managed by the strategy\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n\n // add the Wrapped Sonic (wS) in the strategy from deposits that are still to be delegated\n // and any undelegated amounts still pending a withdrawal\n balance =\n IERC20(wrappedSonic).balanceOf(address(this)) +\n pendingWithdrawals;\n\n // For each supported validator, get the staked amount and pending rewards\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; i++) {\n uint256 validator = supportedValidators[i];\n balance += sfc.getStake(address(this), validator);\n balance += sfc.pendingRewards(address(this), validator);\n }\n }\n\n /**\n * @dev Delegate from this strategy to a specific Sonic validator. Called\n * automatically on asset deposit\n * @param _amount the amount of Sonic (S) to delegate.\n */\n function _delegate(uint256 _amount) internal {\n require(\n isSupportedValidator(defaultValidatorId),\n \"Validator not supported\"\n );\n\n // unwrap Wrapped Sonic (wS) to native Sonic (S)\n IWrappedSonic(wrappedSonic).withdraw(_amount);\n\n //slither-disable-next-line arbitrary-send-eth\n sfc.delegate{ value: _amount }(defaultValidatorId);\n\n emit Delegated(defaultValidatorId, _amount);\n }\n\n /**\n * @notice Undelegate from a specific Sonic validator.\n * This needs to be followed by a `withdrawFromSFC` two weeks later.\n * @param _validatorId The Sonic validator ID to undelegate from.\n * @param _undelegateAmount the amount of Sonic (S) to undelegate.\n * @return withdrawId The unique ID of the withdrawal request.\n */\n function undelegate(uint256 _validatorId, uint256 _undelegateAmount)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n returns (uint256 withdrawId)\n {\n withdrawId = _undelegate(_validatorId, _undelegateAmount);\n }\n\n function _undelegate(uint256 _validatorId, uint256 _undelegateAmount)\n internal\n returns (uint256 withdrawId)\n {\n // Can still undelegate even if the validator is no longer supported\n require(_undelegateAmount > 0, \"Must undelegate something\");\n\n uint256 amountDelegated = sfc.getStake(address(this), _validatorId);\n require(\n _undelegateAmount <= amountDelegated,\n \"Insufficient delegation\"\n );\n\n withdrawId = nextWithdrawId++;\n\n withdrawals[withdrawId] = WithdrawRequest(\n _validatorId,\n _undelegateAmount,\n block.timestamp\n );\n pendingWithdrawals += _undelegateAmount;\n\n sfc.undelegate(_validatorId, withdrawId, _undelegateAmount);\n\n emit Undelegated(withdrawId, _validatorId, _undelegateAmount);\n }\n\n /**\n * @notice Withdraw native S from a previously undelegated validator.\n * The native S is wrapped wS and transferred to the Vault.\n * @param _withdrawId The unique withdraw ID used to `undelegate`\n * @return withdrawnAmount The amount of Sonic (S) withdrawn.\n * This can be less than the undelegated amount in the event of slashing.\n */\n function withdrawFromSFC(uint256 _withdrawId)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n returns (uint256 withdrawnAmount)\n {\n require(_withdrawId < nextWithdrawId, \"Invalid withdrawId\");\n\n // Can still withdraw even if the validator is no longer supported\n // Load the withdrawal from storage into memory\n WithdrawRequest memory withdrawal = withdrawals[_withdrawId];\n require(!isWithdrawnFromSFC(_withdrawId), \"Already withdrawn\");\n\n withdrawals[_withdrawId].undelegatedAmount = 0;\n pendingWithdrawals -= withdrawal.undelegatedAmount;\n\n uint256 sBalanceBefore = address(this).balance;\n\n // Try to withdraw from SFC\n try sfc.withdraw(withdrawal.validatorId, _withdrawId) {\n // continue below\n } catch (bytes memory err) {\n bytes4 errorSelector = bytes4(err);\n\n // If the validator has been fully slashed, SFC's withdraw function will\n // revert with a StakeIsFullySlashed custom error.\n if (errorSelector == ISFC.StakeIsFullySlashed.selector) {\n // The validator was fully slashed, so all the delegated amounts were lost.\n // Will swallow the error as we still want to update the\n // withdrawals and pendingWithdrawals storage variables.\n\n // The return param defaults to zero but lets set it explicitly so it's clear\n withdrawnAmount = 0;\n\n emit Withdrawn(\n _withdrawId,\n withdrawal.validatorId,\n withdrawal.undelegatedAmount,\n withdrawnAmount\n );\n\n // Exit here as there is nothing to transfer to the Vault\n return withdrawnAmount;\n } else {\n // Bubble up any other SFC custom errors.\n // Inline assembly is currently the only way to generically rethrow the exact same custom error\n // from the raw bytes err in a catch block while preserving its original selector and parameters.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n revert(add(32, err), mload(err))\n }\n }\n }\n\n // Set return parameter\n withdrawnAmount = address(this).balance - sBalanceBefore;\n\n // Wrap Sonic (S) to Wrapped Sonic (wS)\n IWrappedSonic(wrappedSonic).deposit{ value: withdrawnAmount }();\n\n // Transfer the Wrapped Sonic (wS) to the Vault\n _withdraw(vaultAddress, wrappedSonic, withdrawnAmount);\n\n // withdrawal.undelegatedAmount & withdrawnAmount can differ in case of slashing\n emit Withdrawn(\n _withdrawId,\n withdrawal.validatorId,\n withdrawal.undelegatedAmount,\n withdrawnAmount\n );\n }\n\n /// @notice returns a bool whether a withdrawalId has already been withdrawn or not\n /// @param _withdrawId The unique withdraw ID used to `undelegate`\n function isWithdrawnFromSFC(uint256 _withdrawId)\n public\n view\n returns (bool)\n {\n WithdrawRequest memory withdrawal = withdrawals[_withdrawId];\n require(withdrawal.validatorId > 0, \"Invalid withdrawId\");\n return withdrawal.undelegatedAmount == 0;\n }\n\n /**\n * @notice Restake any pending validator rewards for all supported validators\n * @param _validatorIds List of Sonic validator IDs to restake rewards\n */\n function restakeRewards(uint256[] calldata _validatorIds)\n external\n nonReentrant\n {\n for (uint256 i = 0; i < _validatorIds.length; ++i) {\n require(\n isSupportedValidator(_validatorIds[i]),\n \"Validator not supported\"\n );\n\n uint256 rewards = sfc.pendingRewards(\n address(this),\n _validatorIds[i]\n );\n\n if (rewards > 0) {\n sfc.restakeRewards(_validatorIds[i]);\n }\n }\n\n // The SFC contract will emit Delegated and RestakedRewards events.\n // The checkBalance function should not change as the pending rewards will moved to the staked amount.\n }\n\n /**\n * @notice Claim any pending rewards from validators\n * @param _validatorIds List of Sonic validator IDs to claim rewards\n */\n function collectRewards(uint256[] calldata _validatorIds)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n {\n uint256 sBalanceBefore = address(this).balance;\n\n for (uint256 i = 0; i < _validatorIds.length; ++i) {\n uint256 rewards = sfc.pendingRewards(\n address(this),\n _validatorIds[i]\n );\n\n if (rewards > 0) {\n // The SFC contract will emit ClaimedRewards(delegator (this), validatorId, rewards)\n sfc.claimRewards(_validatorIds[i]);\n }\n }\n\n uint256 rewardsAmount = address(this).balance - sBalanceBefore;\n\n // Wrap Sonic (S) to Wrapped Sonic (wS)\n IWrappedSonic(wrappedSonic).deposit{ value: rewardsAmount }();\n\n // Transfer the Wrapped Sonic (wS) to the Vault\n _withdraw(vaultAddress, wrappedSonic, rewardsAmount);\n }\n\n /**\n * @notice To receive native S from SFC and Wrapped Sonic (wS)\n *\n * @dev This does not prevent donating S tokens to the contract\n * as wrappedSonic has a `withdrawTo` function where a third party\n * owner of wrappedSonic can withdraw to this contract.\n */\n receive() external payable {\n require(\n msg.sender == address(sfc) || msg.sender == wrappedSonic,\n \"S not from allowed contracts\"\n );\n }\n\n /***************************************\n Admin functions\n ****************************************/\n\n /// @notice Set the address of the Registrator which can undelegate, withdraw and collect rewards\n /// @param _validatorRegistrator The address of the Registrator\n function setRegistrator(address _validatorRegistrator)\n external\n onlyGovernor\n {\n validatorRegistrator = _validatorRegistrator;\n emit RegistratorChanged(_validatorRegistrator);\n }\n\n /// @notice Set the default validatorId to delegate to on deposit\n /// @param _validatorId The validator identifier. eg 18\n function setDefaultValidatorId(uint256 _validatorId)\n external\n onlyRegistratorOrStrategist\n {\n require(isSupportedValidator(_validatorId), \"Validator not supported\");\n defaultValidatorId = _validatorId;\n emit DefaultValidatorIdChanged(_validatorId);\n }\n\n /// @notice Allows a validator to be delegated to by the Registrator\n /// @param _validatorId The validator identifier. eg 18\n function supportValidator(uint256 _validatorId) external onlyGovernor {\n require(\n !isSupportedValidator(_validatorId),\n \"Validator already supported\"\n );\n\n supportedValidators.push(_validatorId);\n\n emit SupportedValidator(_validatorId);\n }\n\n /// @notice Removes a validator from the supported list.\n /// Unsupported validators can still be undelegated from, withdrawn from and rewards collected.\n /// @param _validatorId The validator identifier. eg 18\n function unsupportValidator(uint256 _validatorId) external onlyGovernor {\n require(isSupportedValidator(_validatorId), \"Validator not supported\");\n\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; ++i) {\n if (supportedValidators[i] == _validatorId) {\n supportedValidators[i] = supportedValidators[validatorLen - 1];\n supportedValidators.pop();\n break;\n }\n }\n\n uint256 stake = sfc.getStake(address(this), _validatorId);\n\n // undelegate if validator still has funds staked\n if (stake > 0) {\n _undelegate(_validatorId, stake);\n }\n emit UnsupportedValidator(_validatorId);\n }\n\n /// @notice Returns the length of the supportedValidators array\n function supportedValidatorsLength() external view returns (uint256) {\n return supportedValidators.length;\n }\n\n /// @notice Returns whether a validator is supported by this strategy\n /// @param _validatorId The validator identifier\n function isSupportedValidator(uint256 _validatorId)\n public\n view\n returns (bool)\n {\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; ++i) {\n if (supportedValidators[i] == _validatorId) {\n return true;\n }\n }\n return false;\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal virtual;\n}\n" + }, + "contracts/strategies/VaultValueChecker.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IOUSD } from \"../interfaces/IOUSD.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract VaultValueChecker {\n IVault public immutable vault;\n IOUSD public immutable ousd;\n // Snapshot expiration time in seconds.\n // Used to prevent accidental use of an old snapshot, but\n // is not zero to allow easy testing of strategist actions in fork testing\n uint256 constant SNAPSHOT_EXPIRES = 5 * 60;\n\n struct Snapshot {\n uint256 vaultValue;\n uint256 totalSupply;\n uint256 time;\n }\n // By doing per user snapshots, we prevent a reentrancy attack\n // from a third party that updates the snapshot in the middle\n // of an allocation process\n\n mapping(address => Snapshot) public snapshots;\n\n constructor(address _vault, address _ousd) {\n vault = IVault(_vault);\n ousd = IOUSD(_ousd);\n }\n\n function takeSnapshot() external {\n snapshots[msg.sender] = Snapshot({\n vaultValue: vault.totalValue(),\n totalSupply: ousd.totalSupply(),\n time: block.timestamp\n });\n }\n\n function checkDelta(\n int256 expectedProfit,\n int256 profitVariance,\n int256 expectedVaultChange,\n int256 vaultChangeVariance\n ) external {\n // Intentionaly not view so that this method shows up in TX builders\n Snapshot memory snapshot = snapshots[msg.sender];\n int256 vaultChange = toInt256(vault.totalValue()) -\n toInt256(snapshot.vaultValue);\n int256 supplyChange = toInt256(ousd.totalSupply()) -\n toInt256(snapshot.totalSupply);\n int256 profit = vaultChange - supplyChange;\n\n require(\n snapshot.time >= block.timestamp - SNAPSHOT_EXPIRES,\n \"Snapshot too old\"\n );\n require(snapshot.time <= block.timestamp, \"Snapshot too new\");\n require(profit >= expectedProfit - profitVariance, \"Profit too low\");\n require(profit <= expectedProfit + profitVariance, \"Profit too high\");\n require(\n vaultChange >= expectedVaultChange - vaultChangeVariance,\n \"Vault value change too low\"\n );\n require(\n vaultChange <= expectedVaultChange + vaultChangeVariance,\n \"Vault value change too high\"\n );\n }\n\n function toInt256(uint256 value) internal pure returns (int256) {\n // From openzeppelin math/SafeCast.sol\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(\n value <= uint256(type(int256).max),\n \"SafeCast: value doesn't fit in an int256\"\n );\n return int256(value);\n }\n}\n\ncontract OETHVaultValueChecker is VaultValueChecker {\n constructor(address _vault, address _ousd)\n VaultValueChecker(_vault, _ousd)\n {}\n}\n" + }, + "contracts/swapper/Swapper1InchV5.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @notice 1Inch Pathfinder V5 implementation of the general ISwapper interface.\n * @author Origin Protocol Inc\n * @dev It is possible that dust token amounts are left in this contract after a swap.\n * This can happen with some tokens that don't send the full transfer amount.\n * These dust amounts can build up over time and be used by anyone who calls the `swap` function.\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IAggregationExecutor, IOneInchRouter, SwapDescription } from \"../interfaces/IOneInch.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\n\ncontract Swapper1InchV5 is ISwapper {\n using SafeERC20 for IERC20;\n\n /// @notice 1Inch router contract to give allowance to perform swaps\n address public constant SWAP_ROUTER =\n 0x1111111254EEB25477B68fb85Ed929f73A960582;\n\n // swap(address,(address,address,address,address,uint256,uint256,uint256),bytes,bytes)\n bytes4 internal constant SWAP_SELECTOR = 0x12aa3caf;\n // unoswapTo(address,address,uint256,uint256,uint256[])\n bytes4 internal constant UNISWAP_SELECTOR = 0xf78dc253;\n // uniswapV3SwapTo(address,uint256,uint256,uint256[])\n bytes4 internal constant UNISWAPV3_SELECTOR = 0xbc80f1a8;\n\n /**\n * @notice Strategist swaps assets sitting in the contract of the `assetHolder`.\n * @param _fromAsset The token address of the asset being sold by the vault.\n * @param _toAsset The token address of the asset being purchased by the vault.\n * @param _fromAssetAmount The amount of assets being sold by the vault.\n * @param _minToAssetAmount The minimum amount of assets to be purchased.\n * @param _data RLP encoded executer address and bytes data. This is re-encoded tx.data from 1Inch swap API\n */\n function swap(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n ) external override returns (uint256 toAssetAmount) {\n // Decode the function selector from the RLP encoded _data param\n bytes4 swapSelector = bytes4(_data[:4]);\n\n if (swapSelector == SWAP_SELECTOR) {\n // Decode the executer address and data from the RLP encoded _data param\n (, address executer, bytes memory executerData) = abi.decode(\n _data,\n (bytes4, address, bytes)\n );\n SwapDescription memory swapDesc = SwapDescription({\n srcToken: IERC20(_fromAsset),\n dstToken: IERC20(_toAsset),\n srcReceiver: payable(executer),\n dstReceiver: payable(msg.sender),\n amount: _fromAssetAmount,\n minReturnAmount: _minToAssetAmount,\n flags: 4 // 1st bit _PARTIAL_FILL, 2nd bit _REQUIRES_EXTRA_ETH, 3rd bit _SHOULD_CLAIM\n });\n (toAssetAmount, ) = IOneInchRouter(SWAP_ROUTER).swap(\n IAggregationExecutor(executer),\n swapDesc,\n hex\"\",\n executerData\n );\n } else if (swapSelector == UNISWAP_SELECTOR) {\n // Need to get the Uniswap pools data from the _data param\n (, uint256[] memory pools) = abi.decode(_data, (bytes4, uint256[]));\n toAssetAmount = IOneInchRouter(SWAP_ROUTER).unoswapTo(\n payable(msg.sender),\n IERC20(_fromAsset),\n _fromAssetAmount,\n _minToAssetAmount,\n pools\n );\n } else if (swapSelector == UNISWAPV3_SELECTOR) {\n // Need to get the Uniswap pools data from the _data param\n // slither-disable-next-line uninitialized-storage\n (, uint256[] memory pools) = abi.decode(_data, (bytes4, uint256[]));\n toAssetAmount = IOneInchRouter(SWAP_ROUTER).uniswapV3SwapTo(\n payable(msg.sender),\n _fromAssetAmount,\n _minToAssetAmount,\n pools\n );\n } else {\n revert(\"Unsupported swap function\");\n }\n }\n\n /**\n * @notice Approve assets for swapping.\n * @param _assets Array of token addresses to approve.\n * @dev unlimited approval is used as no tokens sit in this contract outside a transaction.\n */\n function approveAssets(address[] memory _assets) external {\n for (uint256 i = 0; i < _assets.length; ++i) {\n // Give the 1Inch router approval to transfer unlimited assets\n IERC20(_assets[i]).safeApprove(SWAP_ROUTER, type(uint256).max);\n }\n }\n}\n" + }, + "contracts/token/BridgedWOETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\n\ncontract BridgedWOETH is\n Governable,\n AccessControlEnumerable,\n Initializable,\n ERC20\n{\n bytes32 public constant MINTER_ROLE = keccak256(\"MINTER_ROLE\");\n bytes32 public constant BURNER_ROLE = keccak256(\"BURNER_ROLE\");\n\n constructor() ERC20(\"Wrapped OETH\", \"wOETH\") {\n // Nobody owns the implementation\n _setGovernor(address(0));\n }\n\n /**\n * @dev Initialize the proxy and set the Governor\n */\n function initialize() external initializer {\n // Governor can grant Minter/Burner roles\n _setupRole(DEFAULT_ADMIN_ROLE, _governor());\n }\n\n /**\n * @dev Mint tokens for `account`\n * @param account Address to mint tokens for\n * @param amount Amount of tokens to mint\n */\n function mint(address account, uint256 amount)\n external\n onlyRole(MINTER_ROLE)\n nonReentrant\n {\n _mint(account, amount);\n }\n\n /**\n * @dev Burns tokens from `account`\n * @param account Address to burn tokens from\n * @param amount Amount of tokens to burn\n */\n function burn(address account, uint256 amount)\n external\n onlyRole(BURNER_ROLE)\n nonReentrant\n {\n _burn(account, amount);\n }\n\n /**\n * @dev Burns tokens from `msg.sender`\n * @param amount Amount of tokens to burn\n */\n function burn(uint256 amount) external onlyRole(BURNER_ROLE) nonReentrant {\n _burn(msg.sender, amount);\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return \"Wrapped OETH\";\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return \"wOETH\";\n }\n\n /**\n * @dev Returns the decimals of the token\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETH is OUSD {\n function symbol() external pure override returns (string memory) {\n return \"OETH\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Origin Ether\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETHBase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBase is OUSD {\n constructor() {\n // Nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n function symbol() external pure override returns (string memory) {\n return \"superOETHb\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Super OETH\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETHPlume.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title Super OETH (Plume) Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETHPlume is OUSD {\n constructor() {\n // Nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n function symbol() external pure override returns (string memory) {\n return \"superOETHp\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Super OETH\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OSonic.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title Origin Sonic (OS) token on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonic is OUSD {\n function symbol() external pure override returns (string memory) {\n return \"OS\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Origin Sonic\";\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/token/OUSDResolutionUpgrade.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract OUSDResolutionUpgrade {\n enum RebaseOptions {\n NotSet,\n OptOut,\n OptIn\n }\n\n // From Initializable\n bool private initialized;\n bool private initializing;\n uint256[50] private ______igap;\n\n // From InitializableERC20Detailed\n uint256[100] private _____ugap;\n string private _name;\n string private _symbol;\n uint8 private _decimals;\n\n // From OUSD\n uint256 private constant MAX_SUPPLY = ~uint128(0); // (2^128) - 1\n uint256 public _totalSupply;\n mapping(address => mapping(address => uint256)) private _allowances;\n address public vaultAddress = address(0);\n mapping(address => uint256) private _creditBalances;\n uint256 private _rebasingCredits;\n uint256 private _rebasingCreditsPerToken;\n uint256 public nonRebasingSupply;\n mapping(address => uint256) public nonRebasingCreditsPerToken;\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) public isUpgraded;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() public view returns (uint256) {\n return _rebasingCreditsPerToken / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCredits() public view returns (uint256) {\n return _rebasingCredits / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() public view returns (uint256) {\n return _rebasingCreditsPerToken;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() public view returns (uint256) {\n return _rebasingCredits;\n }\n\n function upgradeGlobals() external {\n require(isUpgraded[address(0)] == 0, \"Globals already upgraded\");\n require(_rebasingCredits > 0, \"Sanity _rebasingCredits\");\n require(\n _rebasingCreditsPerToken > 0,\n \"Sanity _rebasingCreditsPerToken\"\n );\n isUpgraded[address(0)] = 1;\n _rebasingCredits = _rebasingCredits * RESOLUTION_INCREASE;\n _rebasingCreditsPerToken =\n _rebasingCreditsPerToken *\n RESOLUTION_INCREASE;\n }\n\n function upgradeAccounts(address[] calldata accounts) external {\n for (uint256 i = 0; i < accounts.length; i++) {\n address account = accounts[i];\n require(account != address(0), \"Reserved\");\n require(isUpgraded[account] == 0, \"Account already upgraded\");\n isUpgraded[account] = 1;\n\n // Handle special for non-rebasing accounts\n uint256 nrc = nonRebasingCreditsPerToken[account];\n if (nrc > 0) {\n require(nrc < 1e18, \"Account already highres\");\n nonRebasingCreditsPerToken[account] = nrc * RESOLUTION_INCREASE;\n }\n // Upgrade balance\n uint256 balance = _creditBalances[account];\n require(balance > 0, \"Will not upgrade zero balance\");\n _creditBalances[account] = balance * RESOLUTION_INCREASE;\n }\n }\n\n function creditsBalanceOfHighres(address _account)\n public\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n _creditBalances[_account],\n _creditsPerToken(_account),\n isUpgraded[_account] == 1\n );\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n if (nonRebasingCreditsPerToken[_account] != 0) {\n return nonRebasingCreditsPerToken[_account];\n } else {\n return _rebasingCreditsPerToken;\n }\n }\n}\n" + }, + "contracts/token/WOETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC4626 } from \"../../lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { OETH } from \"./OETH.sol\";\n\n/**\n * @title Wrapped OETH Token Contract\n * @author Origin Protocol Inc\n *\n * @dev An important capability of this contract is that it isn't susceptible to changes of the\n * exchange rate of WOETH/OETH if/when someone sends the underlying asset (OETH) to the contract.\n * If OETH weren't rebasing this could be achieved by solely tracking the ERC20 transfers of the OETH\n * token on mint, deposit, redeem, withdraw. The issue is that OETH is rebasing and OETH balances\n * will change when the token rebases.\n * For that reason the contract logic checks the actual underlying OETH token balance only once\n * (either on a fresh contract creation or upgrade) and considering the WOETH supply and\n * rebasingCreditsPerToken calculates the _adjuster. Once the adjuster is calculated any donations\n * to the contract are ignored. The totalSupply (instead of querying OETH balance) works off of\n * adjuster the current WOETH supply and rebasingCreditsPerToken. This makes WOETH value accrual\n * completely follow OETH's value accrual.\n * WOETH is safe to use in lending markets as the VualtCore's _rebase contains safeguards preventing\n * any sudden large rebases.\n */\n\ncontract WOETH is ERC4626, Governable, Initializable {\n using SafeERC20 for IERC20;\n /* This is a 1e27 adjustment constant that expresses the difference in exchange rate between\n * OETH's rebase since inception (expressed with rebasingCreditsPerToken) and WOETH to OETH\n * conversion.\n *\n * If WOETH and OETH are deployed at the same time, the value of adjuster is a neutral 1e27\n */\n uint256 public adjuster;\n uint256[49] private __gap;\n\n // no need to set ERC20 name and symbol since they are overridden in WOETH & WOETHBase\n constructor(ERC20 underlying_) ERC20(\"\", \"\") ERC4626(underlying_) {}\n\n /**\n * @notice Enable OETH rebasing for this contract\n */\n function initialize() external onlyGovernor initializer {\n OETH(address(asset())).rebaseOptIn();\n\n initialize2();\n }\n\n /**\n * @notice secondary initializer that newly deployed contracts will execute as part\n * of primary initialize function and the existing contracts will have it called\n * as a governance operation.\n */\n function initialize2() public onlyGovernor {\n require(adjuster == 0, \"Initialize2 already called\");\n\n if (totalSupply() == 0) {\n adjuster = 1e27;\n } else {\n adjuster =\n (rebasingCreditsPerTokenHighres() *\n ERC20(asset()).balanceOf(address(this))) /\n totalSupply();\n }\n }\n\n function name()\n public\n view\n virtual\n override(ERC20, IERC20Metadata)\n returns (string memory)\n {\n return \"Wrapped OETH\";\n }\n\n function symbol()\n public\n view\n virtual\n override(ERC20, IERC20Metadata)\n returns (string memory)\n {\n return \"wOETH\";\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends. Cannot transfer OETH\n * @param asset_ Address for the asset\n * @param amount_ Amount of the asset to transfer\n */\n function transferToken(address asset_, uint256 amount_)\n external\n onlyGovernor\n {\n require(asset_ != address(asset()), \"Cannot collect core asset\");\n IERC20(asset_).safeTransfer(governor(), amount_);\n }\n\n /// @inheritdoc ERC4626\n function convertToShares(uint256 assets)\n public\n view\n virtual\n override\n returns (uint256 shares)\n {\n return (assets * rebasingCreditsPerTokenHighres()) / adjuster;\n }\n\n /// @inheritdoc ERC4626\n function convertToAssets(uint256 shares)\n public\n view\n virtual\n override\n returns (uint256 assets)\n {\n return (shares * adjuster) / rebasingCreditsPerTokenHighres();\n }\n\n /// @inheritdoc ERC4626\n function totalAssets() public view override returns (uint256) {\n return (totalSupply() * adjuster) / rebasingCreditsPerTokenHighres();\n }\n\n function rebasingCreditsPerTokenHighres() internal view returns (uint256) {\n return OETH(asset()).rebasingCreditsPerTokenHighres();\n }\n}\n" + }, + "contracts/token/WOETHBase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WOETH } from \"./WOETH.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\n\ncontract WOETHBase is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name() public view virtual override returns (string memory) {\n return \"Wrapped Super OETH\";\n }\n\n function symbol() public view virtual override returns (string memory) {\n return \"wsuperOETHb\";\n }\n}\n" + }, + "contracts/token/WOETHPlume.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WOETH } from \"./WOETH.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @title wOETH (Plume) Token Contract\n * @author Origin Protocol Inc\n */\n\ncontract WOETHPlume is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name() public view virtual override returns (string memory) {\n return \"Wrapped Super OETH\";\n }\n\n function symbol() public view virtual override returns (string memory) {\n return \"wsuperOETHp\";\n }\n}\n" + }, + "contracts/token/WOSonic.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { WOETH } from \"./WOETH.sol\";\n\n/**\n * @title Wrapped Origin Sonic (wOS) token on Sonic\n * @author Origin Protocol Inc\n */\ncontract WOSonic is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"Wrapped OS\";\n }\n\n function symbol()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"wOS\";\n }\n}\n" + }, + "contracts/token/WrappedOusd.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { WOETH } from \"./WOETH.sol\";\n\n/**\n * @title Wrapped OUSD Token Contract\n * @author Origin Protocol Inc\n */\ncontract WrappedOusd is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"Wrapped OUSD\";\n }\n\n function symbol()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"WOUSD\";\n }\n}\n" + }, + "contracts/utils/AerodromeAMOQuoter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.7;\n\nimport { ICLPool } from \"../interfaces/aerodrome/ICLPool.sol\";\nimport { IQuoterV2 } from \"../interfaces/aerodrome/IQuoterV2.sol\";\nimport { IAMOStrategy } from \"../interfaces/aerodrome/IAMOStrategy.sol\";\n\n/// @title QuoterHelper\n/// @author Origin Protocol\n/// @notice Helper for Aerodrome AMO Quoter, as `_quoteAmountToSwapBeforeRebalance` use try/catch method and\n/// this can only be used when calling external contracts.\ncontract QuoterHelper {\n ////////////////////////////////////////////////////////////////\n /// --- STRUCTS & ENUMS\n ////////////////////////////////////////////////////////////////\n enum RevertReasons {\n DefaultStatus,\n RebalanceOutOfBounds,\n NotInExpectedTickRange,\n NotEnoughWethForSwap,\n NotEnoughWethLiquidity,\n UnexpectedError,\n Found,\n NotFound\n }\n\n struct RebalanceStatus {\n RevertReasons reason;\n uint256 currentPoolWETHShare; // Case 1\n uint256 allowedWETHShareStart; // Case 1\n uint256 allowedWETHShareEnd; // Case 1\n int24 currentTick; // Case 2\n uint256 balanceWETH; // Case 3\n uint256 amountWETH; // Case 3\n string revertMessage;\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTANT & IMMUTABLE\n ////////////////////////////////////////////////////////////////\n uint256 public constant BINARY_MIN_AMOUNT = 1 wei;\n\n uint256 public constant BINARY_MAX_ITERATIONS = 40;\n uint256 public constant PERCENTAGE_BASE = 1e18; // 100%\n uint256 public constant ALLOWED_VARIANCE_PERCENTAGE = 1e15; // 0.1%\n\n ////////////////////////////////////////////////////////////////\n /// --- VARIABLES STORAGE\n ////////////////////////////////////////////////////////////////\n ICLPool public immutable clPool;\n IQuoterV2 public immutable quoterV2;\n IAMOStrategy public immutable strategy;\n\n address public originalGovernor;\n\n ////////////////////////////////////////////////////////////////\n /// --- ERRORS & EVENTS\n ////////////////////////////////////////////////////////////////\n error UnexpectedError(string message);\n error OutOfIterations(uint256 iterations);\n error ValidAmount(\n uint256 amount,\n uint256 iterations,\n bool swapWETHForOETHB\n );\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////////////////\n constructor(IAMOStrategy _strategy, IQuoterV2 _quoterV2) {\n strategy = _strategy;\n quoterV2 = _quoterV2;\n clPool = _strategy.clPool();\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- FUNCTIONS\n ////////////////////////////////////////////////////////////////\n /// @notice This call can only end with a revert.\n function getAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) public {\n if (\n overrideBottomWethShare != type(uint256).max ||\n overrideTopWethShare != type(uint256).max\n ) {\n // Current values\n uint256 shareStart = strategy.allowedWethShareStart();\n uint256 shareEnd = strategy.allowedWethShareEnd();\n\n // Override values\n if (overrideBottomWethShare != type(uint256).max) {\n shareStart = overrideBottomWethShare;\n }\n if (overrideTopWethShare != type(uint256).max) {\n shareEnd = overrideTopWethShare;\n }\n\n strategy.setAllowedPoolWethShareInterval(shareStart, shareEnd);\n }\n\n uint256 iterations = 0;\n uint256 low = BINARY_MIN_AMOUNT;\n uint256 high;\n (high, ) = strategy.getPositionPrincipal();\n int24 lowerTick = strategy.lowerTick();\n int24 upperTick = strategy.upperTick();\n bool swapWETHForOETHB = getSwapDirectionForRebalance();\n\n while (low <= high && iterations < BINARY_MAX_ITERATIONS) {\n uint256 mid = (low + high) / 2;\n\n RebalanceStatus memory status = getRebalanceStatus(\n mid,\n swapWETHForOETHB\n );\n\n // Best case, we found the `amount` that will reach the target pool share!\n // We can revert with the amount and the number of iterations\n if (status.reason == RevertReasons.Found) {\n revert ValidAmount(mid, iterations, swapWETHForOETHB);\n }\n\n // If the rebalance failed then we should try to change the amount.\n // We will handle all possible revert reasons here.\n\n // Case 1: Rebalance out of bounds\n // If the pool is out of bounds, we need to adjust the amount to reach the target pool share\n if (status.reason == RevertReasons.RebalanceOutOfBounds) {\n // If the current pool share is less than the target pool share, we need to increase the amount\n if (\n swapWETHForOETHB\n ? status.currentPoolWETHShare <\n status.allowedWETHShareStart\n : status.currentPoolWETHShare >\n status.allowedWETHShareEnd\n ) {\n low = mid + 1;\n }\n // Else we need to decrease the amount\n else {\n high = mid;\n }\n }\n\n // Case 2: Not in expected tick range\n // If the pool is not in the expected tick range, we need to adjust the amount\n // to reach the target pool share\n if (status.reason == RevertReasons.NotInExpectedTickRange) {\n // If we are buying OETHb and the current tick is greater than the lower tick,\n //we need to increase the amount in order to continue to push price down.\n // If we are selling OETHb and the current tick is less than the upper tick,\n // we need to increase the amount in order to continue to push price up.\n if (\n swapWETHForOETHB\n ? status.currentTick > lowerTick\n : status.currentTick < upperTick\n ) {\n low = mid + 1;\n }\n // Else we need to decrease the amount\n else {\n high = mid;\n }\n }\n\n // Case 3: Not enough WETH for swap\n // If we don't have enough WETH to swap, we need to decrease the amount\n // This error can happen, when initial value of mid is too high, so we need to decrease it\n if (status.reason == RevertReasons.NotEnoughWethForSwap) {\n high = mid;\n }\n\n // Case 4: Not enough WETH liquidity\n // If we don't have enough WETH liquidity\n // Revert for the moment, we need to improve this\n if (status.reason == RevertReasons.NotEnoughWethLiquidity) {\n revert(\"Quoter: Not enough WETH liquidity\");\n }\n\n // Case 5: Unexpected error\n // Worst case, it reverted with an unexpected error.\n if (status.reason == RevertReasons.UnexpectedError) {\n revert UnexpectedError(status.revertMessage);\n }\n\n iterations++;\n }\n\n // Case 6: Out of iterations\n // If we didn't find the amount after the max iterations, we need to revert.\n revert OutOfIterations(iterations);\n }\n\n /// @notice Get the status of the rebalance\n /// @param amount The amount of token to swap\n /// @param swapWETH True if we need to swap WETH for OETHb, false otherwise\n /// @return status The status of the rebalance\n function getRebalanceStatus(uint256 amount, bool swapWETH)\n public\n returns (RebalanceStatus memory status)\n {\n try strategy.rebalance(amount, swapWETH, 0) {\n status.reason = RevertReasons.Found;\n return status;\n } catch Error(string memory reason) {\n status.reason = RevertReasons.UnexpectedError;\n status.revertMessage = reason;\n } catch (bytes memory reason) {\n bytes4 receivedSelector = bytes4(reason);\n\n // Case 1: Rebalance out of bounds\n if (\n receivedSelector ==\n IAMOStrategy.PoolRebalanceOutOfBounds.selector\n ) {\n uint256 currentPoolWETHShare;\n uint256 allowedWETHShareStart;\n uint256 allowedWETHShareEnd;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n currentPoolWETHShare := mload(add(reason, 0x24))\n allowedWETHShareStart := mload(add(reason, 0x44))\n allowedWETHShareEnd := mload(add(reason, 0x64))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.RebalanceOutOfBounds,\n currentPoolWETHShare: currentPoolWETHShare,\n allowedWETHShareStart: allowedWETHShareStart,\n allowedWETHShareEnd: allowedWETHShareEnd,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 2: Not in expected tick range\n if (\n receivedSelector ==\n IAMOStrategy.OutsideExpectedTickRange.selector\n ) {\n int24 currentTick;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n currentTick := mload(add(reason, 0x24))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.NotInExpectedTickRange,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: currentTick,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 3: Not enough WETH for swap\n if (\n receivedSelector == IAMOStrategy.NotEnoughWethForSwap.selector\n ) {\n uint256 balanceWETH;\n uint256 amountWETH;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n balanceWETH := mload(add(reason, 0x24))\n amountWETH := mload(add(reason, 0x44))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.NotEnoughWethForSwap,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: balanceWETH,\n amountWETH: amountWETH,\n revertMessage: \"\"\n });\n }\n\n // Case 4: Not enough WETH liquidity\n if (\n receivedSelector == IAMOStrategy.NotEnoughWethLiquidity.selector\n ) {\n return\n RebalanceStatus({\n reason: RevertReasons.NotEnoughWethLiquidity,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 5: Unexpected error\n return\n RebalanceStatus({\n reason: RevertReasons.UnexpectedError,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: abi.decode(reason, (string))\n });\n }\n }\n\n /// @notice Get the swap direction to reach the target price before rebalance.\n /// @return bool True if we need to swap WETH for OETHb, false otherwise.\n function getSwapDirectionForRebalance() public view returns (bool) {\n uint160 currentPrice = strategy.getPoolX96Price();\n uint160 ticker0Price = strategy.sqrtRatioX96TickLower();\n uint160 ticker1Price = strategy.sqrtRatioX96TickHigher();\n uint256 allowedWethShareStart = strategy.allowedWethShareStart();\n uint256 allowedWethShareEnd = strategy.allowedWethShareEnd();\n uint160 mid = uint160(allowedWethShareStart + allowedWethShareEnd) / 2;\n // slither-disable-start divide-before-multiply\n uint160 targetPrice = (ticker0Price *\n mid +\n ticker1Price *\n (1 ether - mid)) / 1 ether;\n // slither-disable-end divide-before-multiply\n\n return currentPrice > targetPrice;\n }\n\n // returns total amount in the position principal of the Aerodrome AMO strategy. Needed as a\n // separate function because of the limitation in local variable count in getAmountToSwapToReachPrice\n function getTotalStrategyPosition() internal returns (uint256) {\n (uint256 wethAmount, uint256 oethBalance) = strategy\n .getPositionPrincipal();\n return wethAmount + oethBalance;\n }\n\n /// @notice Get the amount of tokens to swap to reach the target price.\n /// @dev This act like a quoter, i.e. the transaction is not performed.\n /// @dev Because the amount to swap can be largely overestimated, because CLAMM alow partial orders,\n /// i.e. when we ask to swap a very large amount, with a close priceLimite, it will swap only a part of it,\n /// and not revert. So if overestimated amount is to high, use a custom maxAmount to avoid this issue.\n /// @param sqrtPriceTargetX96 The target price to reach.\n /// @return amount The amount of tokens to swap.\n /// @return iterations The number of iterations to find the amount.\n /// @return swapWETHForOETHB True if we need to swap WETH for OETHb, false otherwise.\n /// @return sqrtPriceX96After The price after the swap.\n function getAmountToSwapToReachPrice(uint160 sqrtPriceTargetX96)\n public\n returns (\n uint256,\n uint256,\n bool,\n uint160\n )\n {\n uint256 iterations = 0;\n uint256 low = BINARY_MIN_AMOUNT;\n // high search start is twice the position principle of Aerodrome AMO strategy.\n // should be more than enough\n uint256 high = getTotalStrategyPosition() * 2;\n bool swapWETHForOETHB = getSwapDirection(sqrtPriceTargetX96);\n\n while (low <= high && iterations < BINARY_MAX_ITERATIONS) {\n uint256 mid = (low + high) / 2;\n\n // Call QuoterV2 from SugarHelper\n (uint256 amountOut, uint160 sqrtPriceX96After, , ) = quoterV2\n .quoteExactInputSingle(\n IQuoterV2.QuoteExactInputSingleParams({\n tokenIn: swapWETHForOETHB\n ? clPool.token0()\n : clPool.token1(),\n tokenOut: swapWETHForOETHB\n ? clPool.token1()\n : clPool.token0(),\n amountIn: mid,\n tickSpacing: strategy.tickSpacing(),\n sqrtPriceLimitX96: sqrtPriceTargetX96\n })\n );\n\n if (\n isWithinAllowedVariance(sqrtPriceX96After, sqrtPriceTargetX96)\n ) {\n /** Very important to return `amountOut` instead of `mid` as the first return parameter.\n * The issues was that when quoting we impose a swap price limit (sqrtPriceLimitX96: sqrtPriceTargetX96)\n * and in that case the `amountIn` acts like a maximum amount to swap. And we don't know how much\n * of that amount was actually consumed. For that reason we \"estimate\" it by returning the\n * amountOut since that is only going to be a couple of basis point away from amountIn in the\n * worst cases.\n *\n * Note: we could be returning mid instead of amountOut in cases when those values are only basis\n * points apart (assuming that complete balance of amountIn has been consumed) but that might increase\n * complexity too much in an already complex contract.\n */\n return (\n amountOut,\n iterations,\n swapWETHForOETHB,\n sqrtPriceX96After\n );\n } else if (low == high) {\n // target swap amount not found.\n // might be that \"high\" amount is too low on start\n revert(\"SwapAmountNotFound\");\n } else if (\n swapWETHForOETHB\n ? sqrtPriceX96After > sqrtPriceTargetX96\n : sqrtPriceX96After < sqrtPriceTargetX96\n ) {\n low = mid + 1;\n } else {\n high = mid;\n }\n iterations++;\n }\n\n revert OutOfIterations(iterations);\n }\n\n /// @notice Check if the current price is within the allowed variance in comparison to the target price\n /// @return bool True if the current price is within the allowed variance, false otherwise\n function isWithinAllowedVariance(\n uint160 sqrtPriceCurrentX96,\n uint160 sqrtPriceTargetX96\n ) public view returns (bool) {\n uint160 range = strategy.sqrtRatioX96TickHigher() -\n strategy.sqrtRatioX96TickLower();\n if (sqrtPriceCurrentX96 > sqrtPriceTargetX96) {\n return\n (sqrtPriceCurrentX96 - sqrtPriceTargetX96) <=\n (ALLOWED_VARIANCE_PERCENTAGE * range) / PERCENTAGE_BASE;\n } else {\n return\n (sqrtPriceTargetX96 - sqrtPriceCurrentX96) <=\n (ALLOWED_VARIANCE_PERCENTAGE * range) / PERCENTAGE_BASE;\n }\n }\n\n /// @notice Get the swap direction to reach the target price.\n /// @param sqrtPriceTargetX96 The target price to reach.\n /// @return bool True if we need to swap WETH for OETHb, false otherwise.\n function getSwapDirection(uint160 sqrtPriceTargetX96)\n public\n view\n returns (bool)\n {\n uint160 currentPrice = strategy.getPoolX96Price();\n return currentPrice > sqrtPriceTargetX96;\n }\n\n function claimGovernanceOnAMO() public {\n originalGovernor = strategy.governor();\n strategy.claimGovernance();\n }\n\n function giveBackGovernanceOnAMO() public {\n require(\n originalGovernor != address(0),\n \"Quoter: Original governor not set\"\n );\n strategy.transferGovernance(originalGovernor);\n }\n}\n\n/// @title AerodromeAMOQuoter\n/// @author Origin Protocol\n/// @notice Quoter for Aerodrome AMO\ncontract AerodromeAMOQuoter {\n ////////////////////////////////////////////////////////////////\n /// --- STRUCTS & ENUMS\n ///////////////////////////////////////////////////////////////\n struct Data {\n uint256 amount;\n uint256 iterations;\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- VARIABLES STORAGE\n ////////////////////////////////////////////////////////////////\n QuoterHelper public immutable quoterHelper;\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////////////////\n constructor(address _strategy, address _quoterV2) {\n quoterHelper = new QuoterHelper(\n IAMOStrategy(_strategy),\n IQuoterV2(_quoterV2)\n );\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- ERRORS & EVENTS\n ////////////////////////////////////////////////////////////////\n event ValueFound(uint256 value, uint256 iterations, bool swapWETHForOETHB);\n event ValueFoundBis(\n uint256 value,\n uint256 iterations,\n bool swapWETHForOETHB,\n uint160 sqrtPriceAfterX96\n );\n event ValueNotFound(string message);\n\n ////////////////////////////////////////////////////////////////\n /// --- FUNCTIONS\n ////////////////////////////////////////////////////////////////\n /// @notice Use this to get the amount to swap before rebalance\n /// @dev This call will only revert, check the logs to get returned values.\n /// @dev Need to perform this call while impersonating the governor or strategist of AMO.\n /// @return data Data struct with the amount and the number of iterations\n function quoteAmountToSwapBeforeRebalance()\n public\n returns (Data memory data)\n {\n return\n _quoteAmountToSwapBeforeRebalance(\n type(uint256).max,\n type(uint256).max\n );\n }\n\n /// @notice Use this to get the amount to swap before rebalance and\n /// update allowedWethShareStart and allowedWethShareEnd on AMO.\n /// @dev This call will only revert, check the logs to get returned values.\n /// @dev Need to perform this call while impersonating the governor of AMO.\n /// @param overrideBottomWethShare New value for the allowedWethShareStart on AMO.\n /// Use type(uint256).max to keep same value.\n /// @param overrideTopWethShare New value for the allowedWethShareEnd on AMO.\n /// Use type(uint256).max to keep same value.\n /// @return data Data struct with the amount and the number of iterations\n function quoteAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) public returns (Data memory data) {\n return\n _quoteAmountToSwapBeforeRebalance(\n overrideBottomWethShare,\n overrideTopWethShare\n );\n }\n\n /// @notice Internal logic for quoteAmountToSwapBeforeRebalance.\n function _quoteAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) internal returns (Data memory data) {\n try\n quoterHelper.getAmountToSwapBeforeRebalance(\n overrideBottomWethShare,\n overrideTopWethShare\n )\n {\n revert(\"Previous call should only revert, it cannot succeed\");\n } catch (bytes memory reason) {\n bytes4 receivedSelector = bytes4(reason);\n if (receivedSelector == QuoterHelper.ValidAmount.selector) {\n uint256 value;\n uint256 iterations;\n bool swapWETHForOETHB;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n value := mload(add(reason, 0x24))\n iterations := mload(add(reason, 0x44))\n swapWETHForOETHB := mload(add(reason, 0x64))\n }\n emit ValueFound(value, iterations, swapWETHForOETHB);\n return Data({ amount: value, iterations: iterations });\n }\n\n if (receivedSelector == QuoterHelper.OutOfIterations.selector) {\n emit ValueNotFound(\"Out of iterations\");\n revert(\"Out of iterations\");\n }\n\n emit ValueNotFound(\"Unexpected error\");\n revert(abi.decode(reason, (string)));\n }\n }\n\n /// @notice Use this to get the amount to swap to reach the target price after swap.\n /// @dev This call will only revert, check the logs to get returned values.\n /// @param sqrtPriceTargetX96 The target price to reach.\n function quoteAmountToSwapToReachPrice(uint160 sqrtPriceTargetX96) public {\n (\n uint256 amount,\n uint256 iterations,\n bool swapWETHForOETHB,\n uint160 sqrtPriceAfterX96\n ) = quoterHelper.getAmountToSwapToReachPrice(sqrtPriceTargetX96);\n\n emit ValueFoundBis(\n amount,\n iterations,\n swapWETHForOETHB,\n sqrtPriceAfterX96\n );\n }\n\n function claimGovernance() public {\n quoterHelper.claimGovernanceOnAMO();\n }\n\n function giveBackGovernance() public {\n quoterHelper.giveBackGovernanceOnAMO();\n }\n}\n" + }, + "contracts/utils/BalancerErrors.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity >=0.7.4 <0.9.0;\n\n// solhint-disable\n\n/**\n * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are\n * supported.\n * Uses the default 'BAL' prefix for the error code\n */\nfunction _require(bool condition, uint256 errorCode) pure {\n if (!condition) _revert(errorCode);\n}\n\n/**\n * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are\n * supported.\n */\nfunction _require(\n bool condition,\n uint256 errorCode,\n bytes3 prefix\n) pure {\n if (!condition) _revert(errorCode, prefix);\n}\n\n/**\n * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported.\n * Uses the default 'BAL' prefix for the error code\n */\nfunction _revert(uint256 errorCode) pure {\n _revert(errorCode, 0x42414c); // This is the raw byte representation of \"BAL\"\n}\n\n/**\n * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported.\n */\nfunction _revert(uint256 errorCode, bytes3 prefix) pure {\n uint256 prefixUint = uint256(uint24(prefix));\n // We're going to dynamically create a revert string based on the error code, with the following format:\n // 'BAL#{errorCode}'\n // where the code is left-padded with zeroes to three digits (so they range from 000 to 999).\n //\n // We don't have revert strings embedded in the contract to save bytecode size: it takes much less space to store a\n // number (8 to 16 bits) than the individual string characters.\n //\n // The dynamic string creation algorithm that follows could be implemented in Solidity, but assembly allows for a\n // much denser implementation, again saving bytecode size. Given this function unconditionally reverts, this is a\n // safe place to rely on it without worrying about how its usage might affect e.g. memory contents.\n assembly {\n // First, we need to compute the ASCII representation of the error code. We assume that it is in the 0-999\n // range, so we only need to convert three digits. To convert the digits to ASCII, we add 0x30, the value for\n // the '0' character.\n\n let units := add(mod(errorCode, 10), 0x30)\n\n errorCode := div(errorCode, 10)\n let tenths := add(mod(errorCode, 10), 0x30)\n\n errorCode := div(errorCode, 10)\n let hundreds := add(mod(errorCode, 10), 0x30)\n\n // With the individual characters, we can now construct the full string.\n // We first append the '#' character (0x23) to the prefix. In the case of 'BAL', it results in 0x42414c23 ('BAL#')\n // Then, we shift this by 24 (to provide space for the 3 bytes of the error code), and add the\n // characters to it, each shifted by a multiple of 8.\n // The revert reason is then shifted left by 200 bits (256 minus the length of the string, 7 characters * 8 bits\n // per character = 56) to locate it in the most significant part of the 256 slot (the beginning of a byte\n // array).\n let formattedPrefix := shl(24, add(0x23, shl(8, prefixUint)))\n\n let revertReason := shl(\n 200,\n add(\n formattedPrefix,\n add(add(units, shl(8, tenths)), shl(16, hundreds))\n )\n )\n\n // We can now encode the reason in memory, which can be safely overwritten as we're about to revert. The encoded\n // message will have the following layout:\n // [ revert reason identifier ] [ string location offset ] [ string length ] [ string contents ]\n\n // The Solidity revert reason identifier is 0x08c739a0, the function selector of the Error(string) function. We\n // also write zeroes to the next 28 bytes of memory, but those are about to be overwritten.\n mstore(\n 0x0,\n 0x08c379a000000000000000000000000000000000000000000000000000000000\n )\n // Next is the offset to the location of the string, which will be placed immediately after (20 bytes away).\n mstore(\n 0x04,\n 0x0000000000000000000000000000000000000000000000000000000000000020\n )\n // The string length is fixed: 7 characters.\n mstore(0x24, 7)\n // Finally, the string itself is stored.\n mstore(0x44, revertReason)\n\n // Even if the string is only 7 bytes long, we need to return a full 32 byte slot containing it. The length of\n // the encoded message is therefore 4 + 32 + 32 + 32 = 100.\n revert(0, 100)\n }\n}\n\nlibrary Errors {\n // Math\n uint256 internal constant ADD_OVERFLOW = 0;\n uint256 internal constant SUB_OVERFLOW = 1;\n uint256 internal constant SUB_UNDERFLOW = 2;\n uint256 internal constant MUL_OVERFLOW = 3;\n uint256 internal constant ZERO_DIVISION = 4;\n uint256 internal constant DIV_INTERNAL = 5;\n uint256 internal constant X_OUT_OF_BOUNDS = 6;\n uint256 internal constant Y_OUT_OF_BOUNDS = 7;\n uint256 internal constant PRODUCT_OUT_OF_BOUNDS = 8;\n uint256 internal constant INVALID_EXPONENT = 9;\n\n // Input\n uint256 internal constant OUT_OF_BOUNDS = 100;\n uint256 internal constant UNSORTED_ARRAY = 101;\n uint256 internal constant UNSORTED_TOKENS = 102;\n uint256 internal constant INPUT_LENGTH_MISMATCH = 103;\n uint256 internal constant ZERO_TOKEN = 104;\n uint256 internal constant INSUFFICIENT_DATA = 105;\n\n // Shared pools\n uint256 internal constant MIN_TOKENS = 200;\n uint256 internal constant MAX_TOKENS = 201;\n uint256 internal constant MAX_SWAP_FEE_PERCENTAGE = 202;\n uint256 internal constant MIN_SWAP_FEE_PERCENTAGE = 203;\n uint256 internal constant MINIMUM_BPT = 204;\n uint256 internal constant CALLER_NOT_VAULT = 205;\n uint256 internal constant UNINITIALIZED = 206;\n uint256 internal constant BPT_IN_MAX_AMOUNT = 207;\n uint256 internal constant BPT_OUT_MIN_AMOUNT = 208;\n uint256 internal constant EXPIRED_PERMIT = 209;\n uint256 internal constant NOT_TWO_TOKENS = 210;\n uint256 internal constant DISABLED = 211;\n\n // Pools\n uint256 internal constant MIN_AMP = 300;\n uint256 internal constant MAX_AMP = 301;\n uint256 internal constant MIN_WEIGHT = 302;\n uint256 internal constant MAX_STABLE_TOKENS = 303;\n uint256 internal constant MAX_IN_RATIO = 304;\n uint256 internal constant MAX_OUT_RATIO = 305;\n uint256 internal constant MIN_BPT_IN_FOR_TOKEN_OUT = 306;\n uint256 internal constant MAX_OUT_BPT_FOR_TOKEN_IN = 307;\n uint256 internal constant NORMALIZED_WEIGHT_INVARIANT = 308;\n uint256 internal constant INVALID_TOKEN = 309;\n uint256 internal constant UNHANDLED_JOIN_KIND = 310;\n uint256 internal constant ZERO_INVARIANT = 311;\n uint256 internal constant ORACLE_INVALID_SECONDS_QUERY = 312;\n uint256 internal constant ORACLE_NOT_INITIALIZED = 313;\n uint256 internal constant ORACLE_QUERY_TOO_OLD = 314;\n uint256 internal constant ORACLE_INVALID_INDEX = 315;\n uint256 internal constant ORACLE_BAD_SECS = 316;\n uint256 internal constant AMP_END_TIME_TOO_CLOSE = 317;\n uint256 internal constant AMP_ONGOING_UPDATE = 318;\n uint256 internal constant AMP_RATE_TOO_HIGH = 319;\n uint256 internal constant AMP_NO_ONGOING_UPDATE = 320;\n uint256 internal constant STABLE_INVARIANT_DIDNT_CONVERGE = 321;\n uint256 internal constant STABLE_GET_BALANCE_DIDNT_CONVERGE = 322;\n uint256 internal constant RELAYER_NOT_CONTRACT = 323;\n uint256 internal constant BASE_POOL_RELAYER_NOT_CALLED = 324;\n uint256 internal constant REBALANCING_RELAYER_REENTERED = 325;\n uint256 internal constant GRADUAL_UPDATE_TIME_TRAVEL = 326;\n uint256 internal constant SWAPS_DISABLED = 327;\n uint256 internal constant CALLER_IS_NOT_LBP_OWNER = 328;\n uint256 internal constant PRICE_RATE_OVERFLOW = 329;\n uint256 internal constant INVALID_JOIN_EXIT_KIND_WHILE_SWAPS_DISABLED = 330;\n uint256 internal constant WEIGHT_CHANGE_TOO_FAST = 331;\n uint256 internal constant LOWER_GREATER_THAN_UPPER_TARGET = 332;\n uint256 internal constant UPPER_TARGET_TOO_HIGH = 333;\n uint256 internal constant UNHANDLED_BY_LINEAR_POOL = 334;\n uint256 internal constant OUT_OF_TARGET_RANGE = 335;\n uint256 internal constant UNHANDLED_EXIT_KIND = 336;\n uint256 internal constant UNAUTHORIZED_EXIT = 337;\n uint256 internal constant MAX_MANAGEMENT_SWAP_FEE_PERCENTAGE = 338;\n uint256 internal constant UNHANDLED_BY_MANAGED_POOL = 339;\n uint256 internal constant UNHANDLED_BY_PHANTOM_POOL = 340;\n uint256 internal constant TOKEN_DOES_NOT_HAVE_RATE_PROVIDER = 341;\n uint256 internal constant INVALID_INITIALIZATION = 342;\n uint256 internal constant OUT_OF_NEW_TARGET_RANGE = 343;\n uint256 internal constant FEATURE_DISABLED = 344;\n uint256 internal constant UNINITIALIZED_POOL_CONTROLLER = 345;\n uint256 internal constant SET_SWAP_FEE_DURING_FEE_CHANGE = 346;\n uint256 internal constant SET_SWAP_FEE_PENDING_FEE_CHANGE = 347;\n uint256 internal constant CHANGE_TOKENS_DURING_WEIGHT_CHANGE = 348;\n uint256 internal constant CHANGE_TOKENS_PENDING_WEIGHT_CHANGE = 349;\n uint256 internal constant MAX_WEIGHT = 350;\n uint256 internal constant UNAUTHORIZED_JOIN = 351;\n uint256 internal constant MAX_MANAGEMENT_AUM_FEE_PERCENTAGE = 352;\n uint256 internal constant FRACTIONAL_TARGET = 353;\n uint256 internal constant ADD_OR_REMOVE_BPT = 354;\n uint256 internal constant INVALID_CIRCUIT_BREAKER_BOUNDS = 355;\n uint256 internal constant CIRCUIT_BREAKER_TRIPPED = 356;\n uint256 internal constant MALICIOUS_QUERY_REVERT = 357;\n uint256 internal constant JOINS_EXITS_DISABLED = 358;\n\n // Lib\n uint256 internal constant REENTRANCY = 400;\n uint256 internal constant SENDER_NOT_ALLOWED = 401;\n uint256 internal constant PAUSED = 402;\n uint256 internal constant PAUSE_WINDOW_EXPIRED = 403;\n uint256 internal constant MAX_PAUSE_WINDOW_DURATION = 404;\n uint256 internal constant MAX_BUFFER_PERIOD_DURATION = 405;\n uint256 internal constant INSUFFICIENT_BALANCE = 406;\n uint256 internal constant INSUFFICIENT_ALLOWANCE = 407;\n uint256 internal constant ERC20_TRANSFER_FROM_ZERO_ADDRESS = 408;\n uint256 internal constant ERC20_TRANSFER_TO_ZERO_ADDRESS = 409;\n uint256 internal constant ERC20_MINT_TO_ZERO_ADDRESS = 410;\n uint256 internal constant ERC20_BURN_FROM_ZERO_ADDRESS = 411;\n uint256 internal constant ERC20_APPROVE_FROM_ZERO_ADDRESS = 412;\n uint256 internal constant ERC20_APPROVE_TO_ZERO_ADDRESS = 413;\n uint256 internal constant ERC20_TRANSFER_EXCEEDS_ALLOWANCE = 414;\n uint256 internal constant ERC20_DECREASED_ALLOWANCE_BELOW_ZERO = 415;\n uint256 internal constant ERC20_TRANSFER_EXCEEDS_BALANCE = 416;\n uint256 internal constant ERC20_BURN_EXCEEDS_ALLOWANCE = 417;\n uint256 internal constant SAFE_ERC20_CALL_FAILED = 418;\n uint256 internal constant ADDRESS_INSUFFICIENT_BALANCE = 419;\n uint256 internal constant ADDRESS_CANNOT_SEND_VALUE = 420;\n uint256 internal constant SAFE_CAST_VALUE_CANT_FIT_INT256 = 421;\n uint256 internal constant GRANT_SENDER_NOT_ADMIN = 422;\n uint256 internal constant REVOKE_SENDER_NOT_ADMIN = 423;\n uint256 internal constant RENOUNCE_SENDER_NOT_ALLOWED = 424;\n uint256 internal constant BUFFER_PERIOD_EXPIRED = 425;\n uint256 internal constant CALLER_IS_NOT_OWNER = 426;\n uint256 internal constant NEW_OWNER_IS_ZERO = 427;\n uint256 internal constant CODE_DEPLOYMENT_FAILED = 428;\n uint256 internal constant CALL_TO_NON_CONTRACT = 429;\n uint256 internal constant LOW_LEVEL_CALL_FAILED = 430;\n uint256 internal constant NOT_PAUSED = 431;\n uint256 internal constant ADDRESS_ALREADY_ALLOWLISTED = 432;\n uint256 internal constant ADDRESS_NOT_ALLOWLISTED = 433;\n uint256 internal constant ERC20_BURN_EXCEEDS_BALANCE = 434;\n uint256 internal constant INVALID_OPERATION = 435;\n uint256 internal constant CODEC_OVERFLOW = 436;\n uint256 internal constant IN_RECOVERY_MODE = 437;\n uint256 internal constant NOT_IN_RECOVERY_MODE = 438;\n uint256 internal constant INDUCED_FAILURE = 439;\n uint256 internal constant EXPIRED_SIGNATURE = 440;\n uint256 internal constant MALFORMED_SIGNATURE = 441;\n uint256 internal constant SAFE_CAST_VALUE_CANT_FIT_UINT64 = 442;\n uint256 internal constant UNHANDLED_FEE_TYPE = 443;\n uint256 internal constant BURN_FROM_ZERO = 444;\n\n // Vault\n uint256 internal constant INVALID_POOL_ID = 500;\n uint256 internal constant CALLER_NOT_POOL = 501;\n uint256 internal constant SENDER_NOT_ASSET_MANAGER = 502;\n uint256 internal constant USER_DOESNT_ALLOW_RELAYER = 503;\n uint256 internal constant INVALID_SIGNATURE = 504;\n uint256 internal constant EXIT_BELOW_MIN = 505;\n uint256 internal constant JOIN_ABOVE_MAX = 506;\n uint256 internal constant SWAP_LIMIT = 507;\n uint256 internal constant SWAP_DEADLINE = 508;\n uint256 internal constant CANNOT_SWAP_SAME_TOKEN = 509;\n uint256 internal constant UNKNOWN_AMOUNT_IN_FIRST_SWAP = 510;\n uint256 internal constant MALCONSTRUCTED_MULTIHOP_SWAP = 511;\n uint256 internal constant INTERNAL_BALANCE_OVERFLOW = 512;\n uint256 internal constant INSUFFICIENT_INTERNAL_BALANCE = 513;\n uint256 internal constant INVALID_ETH_INTERNAL_BALANCE = 514;\n uint256 internal constant INVALID_POST_LOAN_BALANCE = 515;\n uint256 internal constant INSUFFICIENT_ETH = 516;\n uint256 internal constant UNALLOCATED_ETH = 517;\n uint256 internal constant ETH_TRANSFER = 518;\n uint256 internal constant CANNOT_USE_ETH_SENTINEL = 519;\n uint256 internal constant TOKENS_MISMATCH = 520;\n uint256 internal constant TOKEN_NOT_REGISTERED = 521;\n uint256 internal constant TOKEN_ALREADY_REGISTERED = 522;\n uint256 internal constant TOKENS_ALREADY_SET = 523;\n uint256 internal constant TOKENS_LENGTH_MUST_BE_2 = 524;\n uint256 internal constant NONZERO_TOKEN_BALANCE = 525;\n uint256 internal constant BALANCE_TOTAL_OVERFLOW = 526;\n uint256 internal constant POOL_NO_TOKENS = 527;\n uint256 internal constant INSUFFICIENT_FLASH_LOAN_BALANCE = 528;\n\n // Fees\n uint256 internal constant SWAP_FEE_PERCENTAGE_TOO_HIGH = 600;\n uint256 internal constant FLASH_LOAN_FEE_PERCENTAGE_TOO_HIGH = 601;\n uint256 internal constant INSUFFICIENT_FLASH_LOAN_FEE_AMOUNT = 602;\n uint256 internal constant AUM_FEE_PERCENTAGE_TOO_HIGH = 603;\n\n // FeeSplitter\n uint256 internal constant SPLITTER_FEE_PERCENTAGE_TOO_HIGH = 700;\n\n // Misc\n uint256 internal constant UNIMPLEMENTED = 998;\n uint256 internal constant SHOULD_NOT_HAPPEN = 999;\n}\n" + }, + "contracts/utils/DepositContractUtils.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract DepositContractUtils {\n function calculateDepositDataRoot(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature\n ) public pure returns (bytes32 node) {\n uint256 deposit_amount = 32 ether / 1 gwei;\n bytes memory amount = to_little_endian_64(uint64(deposit_amount));\n\n // Compute deposit data root (`DepositData` hash tree root)\n bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, bytes16(0)));\n bytes32 signature_root = sha256(\n abi.encodePacked(\n sha256(abi.encodePacked(signature[:64])),\n sha256(abi.encodePacked(signature[64:], bytes32(0)))\n )\n );\n node = sha256(\n abi.encodePacked(\n sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)),\n sha256(abi.encodePacked(amount, bytes24(0), signature_root))\n )\n );\n }\n\n function to_little_endian_64(uint64 value)\n internal\n pure\n returns (bytes memory ret)\n {\n ret = new bytes(8);\n bytes8 bytesValue = bytes8(value);\n // Byteswapping during copying to bytes.\n ret[0] = bytesValue[7];\n ret[1] = bytesValue[6];\n ret[2] = bytesValue[5];\n ret[3] = bytesValue[4];\n ret[4] = bytesValue[3];\n ret[5] = bytesValue[2];\n ret[6] = bytesValue[1];\n ret[7] = bytesValue[0];\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/utils/InitializableERC20Detailed.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @dev Optional functions from the ERC20 standard.\n * Converted from openzeppelin/contracts/token/ERC20/ERC20Detailed.sol\n * @author Origin Protocol Inc\n */\nabstract contract InitializableERC20Detailed is IERC20 {\n // Storage gap to skip storage from prior to OUSD reset\n uint256[100] private _____gap;\n\n string private _name;\n string private _symbol;\n uint8 private _decimals;\n\n /**\n * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of\n * these values are immutable: they can only be set once during\n * construction.\n * @notice To avoid variable shadowing appended `Arg` after arguments name.\n */\n function _initialize(\n string memory nameArg,\n string memory symbolArg,\n uint8 decimalsArg\n ) internal {\n _name = nameArg;\n _symbol = symbolArg;\n _decimals = decimalsArg;\n }\n\n /**\n * @notice Returns the name of the token.\n */\n function name() public view returns (string memory) {\n return _name;\n }\n\n /**\n * @notice Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view returns (string memory) {\n return _symbol;\n }\n\n /**\n * @notice Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5,05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei.\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view returns (uint8) {\n return _decimals;\n }\n}\n" + }, + "contracts/utils/PRBMath.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// Copied from the PRBMath library\n// https://github.com/PaulRBerg/prb-math/blob/main/src/Common.sol\n\n/// @notice Calculates the square root of x using the Babylonian method.\n///\n/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.\n///\n/// Notes:\n/// - If x is not a perfect square, the result is rounded down.\n/// - Credits to OpenZeppelin for the explanations in comments below.\n///\n/// @param x The uint256 number for which to calculate the square root.\n/// @return result The result as a uint256.\n/// @custom:smtchecker abstract-function-nondet\nfunction sqrt(uint256 x) pure returns (uint256 result) {\n if (x == 0) {\n return 0;\n }\n\n // For our first guess, we calculate the biggest power of 2 which is smaller than the square root of x.\n //\n // We know that the \"msb\" (most significant bit) of x is a power of 2 such that we have:\n //\n // $$\n // msb(x) <= x <= 2*msb(x)$\n // $$\n //\n // We write $msb(x)$ as $2^k$, and we get:\n //\n // $$\n // k = log_2(x)\n // $$\n //\n // Thus, we can write the initial inequality as:\n //\n // $$\n // 2^{log_2(x)} <= x <= 2*2^{log_2(x)+1} \\\\\n // sqrt(2^k) <= sqrt(x) < sqrt(2^{k+1}) \\\\\n // 2^{k/2} <= sqrt(x) < 2^{(k+1)/2} <= 2^{(k/2)+1}\n // $$\n //\n // Consequently, $2^{log_2(x) /2} is a good first approximation of sqrt(x) with at least one correct bit.\n uint256 xAux = uint256(x);\n result = 1;\n if (xAux >= 2**128) {\n xAux >>= 128;\n result <<= 64;\n }\n if (xAux >= 2**64) {\n xAux >>= 64;\n result <<= 32;\n }\n if (xAux >= 2**32) {\n xAux >>= 32;\n result <<= 16;\n }\n if (xAux >= 2**16) {\n xAux >>= 16;\n result <<= 8;\n }\n if (xAux >= 2**8) {\n xAux >>= 8;\n result <<= 4;\n }\n if (xAux >= 2**4) {\n xAux >>= 4;\n result <<= 2;\n }\n if (xAux >= 2**2) {\n result <<= 1;\n }\n\n // At this point, `result` is an estimation with at least one bit of precision. We know the true value has at\n // most 128 bits, since it is the square root of a uint256. Newton's method converges quadratically (precision\n // doubles at every iteration). We thus need at most 7 iteration to turn our partial result with one bit of\n // precision into the expected uint128 result.\n unchecked {\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n\n // If x is not a perfect square, round the result toward zero.\n uint256 roundedResult = x / result;\n if (result >= roundedResult) {\n result = roundedResult;\n }\n }\n}\n" + }, + "contracts/utils/StableMath.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\n// Based on StableMath from Stability Labs Pty. Ltd.\n// https://github.com/mstable/mStable-contracts/blob/master/contracts/shared/StableMath.sol\n\nlibrary StableMath {\n using SafeMath for uint256;\n\n /**\n * @dev Scaling unit for use in specific calculations,\n * where 1 * 10**18, or 1e18 represents a unit '1'\n */\n uint256 private constant FULL_SCALE = 1e18;\n\n /***************************************\n Helpers\n ****************************************/\n\n /**\n * @dev Adjust the scale of an integer\n * @param to Decimals to scale to\n * @param from Decimals to scale from\n */\n function scaleBy(\n uint256 x,\n uint256 to,\n uint256 from\n ) internal pure returns (uint256) {\n if (to > from) {\n x = x.mul(10**(to - from));\n } else if (to < from) {\n // slither-disable-next-line divide-before-multiply\n x = x.div(10**(from - to));\n }\n return x;\n }\n\n /***************************************\n Precise Arithmetic\n ****************************************/\n\n /**\n * @dev Multiplies two precise units, and then truncates by the full scale\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit\n */\n function mulTruncate(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulTruncateScale(x, y, FULL_SCALE);\n }\n\n /**\n * @dev Multiplies two precise units, and then truncates by the given scale. For example,\n * when calculating 90% of 10e18, (10e18 * 9e17) / 1e18 = (9e36) / 1e18 = 9e18\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @param scale Scale unit\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit\n */\n function mulTruncateScale(\n uint256 x,\n uint256 y,\n uint256 scale\n ) internal pure returns (uint256) {\n // e.g. assume scale = fullScale\n // z = 10e18 * 9e17 = 9e36\n uint256 z = x.mul(y);\n // return 9e36 / 1e18 = 9e18\n return z.div(scale);\n }\n\n /**\n * @dev Multiplies two precise units, and then truncates by the full scale, rounding up the result\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit, rounded up to the closest base unit.\n */\n function mulTruncateCeil(uint256 x, uint256 y)\n internal\n pure\n returns (uint256)\n {\n // e.g. 8e17 * 17268172638 = 138145381104e17\n uint256 scaled = x.mul(y);\n // e.g. 138145381104e17 + 9.99...e17 = 138145381113.99...e17\n uint256 ceil = scaled.add(FULL_SCALE.sub(1));\n // e.g. 13814538111.399...e18 / 1e18 = 13814538111\n return ceil.div(FULL_SCALE);\n }\n\n /**\n * @dev Precisely divides two units, by first scaling the left hand operand. Useful\n * for finding percentage weightings, i.e. 8e18/10e18 = 80% (or 8e17)\n * @param x Left hand input to division\n * @param y Right hand input to division\n * @return Result after multiplying the left operand by the scale, and\n * executing the division on the right hand input.\n */\n function divPrecisely(uint256 x, uint256 y)\n internal\n pure\n returns (uint256)\n {\n // e.g. 8e18 * 1e18 = 8e36\n uint256 z = x.mul(FULL_SCALE);\n // e.g. 8e36 / 10e18 = 8e17\n return z.div(y);\n }\n}\n" + }, + "contracts/vault/OETHBaseVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"./OETHVaultAdmin.sol\";\n\n/**\n * @title OETH Base VaultAdmin Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBaseVaultAdmin is OETHVaultAdmin {\n constructor(address _weth) OETHVaultAdmin(_weth) {}\n}\n" + }, + "contracts/vault/OETHBaseVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { OETHVaultCore } from \"./OETHVaultCore.sol\";\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\n\n/**\n * @title OETH Base VaultCore Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBaseVaultCore is OETHVaultCore {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(address _weth) OETHVaultCore(_weth) {}\n\n // @inheritdoc OETHVaultCore\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n override\n {\n // Only Strategist or Governor can redeem using the Vault for now.\n // We don't have the onlyGovernorOrStrategist modifier on VaultCore.\n // Since we won't be using that modifier anywhere in the VaultCore as well,\n // the check has been added inline instead of moving it to VaultStorage.\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n\n super._redeem(_amount, _minimumUnitAmount);\n }\n}\n" + }, + "contracts/vault/OETHBaseZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\n\ncontract OETHBaseZapper {\n IERC20 public immutable oethb;\n IERC4626 public immutable woethb;\n IVault public immutable vault;\n\n IWETH9 public constant weth =\n IWETH9(0x4200000000000000000000000000000000000006);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(\n address _oethb,\n address _woethb,\n address _vault\n ) {\n oethb = IERC20(_oethb);\n woethb = IERC4626(_woethb);\n vault = IVault(_vault);\n\n weth.approve(address(_vault), type(uint256).max);\n IERC20(_oethb).approve(_woethb, type(uint256).max);\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return.\n * Will verify that the user is sent 1:1 for ETH.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit ETH and receive superOETHb in return\n * Will verify that the user is sent 1:1 for ETH.\n * @return Amount of OETH sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap ETH\n weth.deposit{ value: balance }();\n\n // Mint with WETH\n return _mint(balance, msg.sender);\n }\n\n /**\n * @dev Deposit ETH and receive superOETHb in return\n * @param minReceived min amount of wsuperOETHb to receive\n * @return Amount of wsuperOETHb sent to user\n */\n function depositETHForWrappedTokens(uint256 minReceived)\n external\n payable\n returns (uint256)\n {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap ETH\n weth.deposit{ value: balance }();\n\n // Mint with WETH\n uint256 mintedOethb = _mint(balance, address(this));\n\n // Wrap superOETHb into wsuperOETHb\n uint256 mintedWoethb = woethb.deposit(mintedOethb, msg.sender);\n\n require(mintedWoethb >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWoethb;\n }\n\n /**\n * @dev Deposit WETH and receive superOETHb in return\n * @param wethAmount Amount of WETH to deposit\n * @param minReceived min amount of wsuperOETHb to receive\n * @return Amount of wsuperOETHb sent to user\n */\n function depositWETHForWrappedTokens(\n uint256 wethAmount,\n uint256 minReceived\n ) external returns (uint256) {\n // slither-disable-next-line unchecked-transfer unused-return\n weth.transferFrom(msg.sender, address(this), wethAmount);\n\n emit Zap(msg.sender, address(weth), wethAmount);\n\n // Mint with WETH\n uint256 mintedOethb = _mint(wethAmount, address(this));\n\n // Wrap superOETHb into wsuperOETHb\n uint256 mintedWoethb = woethb.deposit(mintedOethb, msg.sender);\n\n require(mintedWoethb >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWoethb;\n }\n\n /**\n * @dev Internal function to mint superOETHb with WETH\n * @param minOETH Minimum amount of OETH to for user to receive\n * @param recipient Address that receives the tokens\n * @return Amount of OETH sent to user\n */\n function _mint(uint256 minOETH, address recipient)\n internal\n returns (uint256)\n {\n uint256 toMint = weth.balanceOf(address(this));\n vault.mint(address(weth), toMint, minOETH);\n uint256 mintedAmount = oethb.balanceOf(address(this));\n require(mintedAmount >= minOETH, \"Zapper: not enough minted\");\n\n if (recipient != address(this)) {\n require(oethb.transfer(recipient, mintedAmount));\n }\n\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/OETHVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Vault } from \"./Vault.sol\";\n\n/**\n * @title OETH Vault Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVault is Vault {\n\n}\n" + }, + "contracts/vault/OETHVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { VaultAdmin } from \"./VaultAdmin.sol\";\n\n/**\n * @title OETH VaultAdmin Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVaultAdmin is VaultAdmin {\n using SafeERC20 for IERC20;\n\n address public immutable weth;\n\n constructor(address _weth) {\n weth = _weth;\n }\n\n /**\n * @notice Adds a strategy to the mint whitelist.\n * Reverts if strategy isn't approved on Vault.\n * @param strategyAddr Strategy address\n */\n function addStrategyToMintWhitelist(address strategyAddr)\n external\n onlyGovernor\n {\n require(strategies[strategyAddr].isSupported, \"Strategy not approved\");\n\n require(\n !isMintWhitelistedStrategy[strategyAddr],\n \"Already whitelisted\"\n );\n\n isMintWhitelistedStrategy[strategyAddr] = true;\n\n emit StrategyAddedToMintWhitelist(strategyAddr);\n }\n\n /**\n * @notice Removes a strategy from the mint whitelist.\n * @param strategyAddr Strategy address\n */\n function removeStrategyFromMintWhitelist(address strategyAddr)\n external\n onlyGovernor\n {\n // Intentionally skipping `strategies.isSupported` check since\n // we may wanna remove an address even after removing the strategy\n\n require(isMintWhitelistedStrategy[strategyAddr], \"Not whitelisted\");\n\n isMintWhitelistedStrategy[strategyAddr] = false;\n\n emit StrategyRemovedFromMintWhitelist(strategyAddr);\n }\n\n /// @dev Simplified version of the deposit function as WETH is the only supported asset.\n function _depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal override {\n require(\n strategies[_strategyToAddress].isSupported,\n \"Invalid to Strategy\"\n );\n require(\n _assets.length == 1 && _amounts.length == 1 && _assets[0] == weth,\n \"Only WETH is supported\"\n );\n\n // Check the there is enough WETH to transfer once the WETH reserved for the withdrawal queue is accounted for\n require(_amounts[0] <= _wethAvailable(), \"Not enough WETH available\");\n\n // Send required amount of funds to the strategy\n IERC20(weth).safeTransfer(_strategyToAddress, _amounts[0]);\n\n // Deposit all the funds that have been sent to the strategy\n IStrategy(_strategyToAddress).depositAll();\n }\n\n function _withdrawFromStrategy(\n address _recipient,\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal override {\n super._withdrawFromStrategy(\n _recipient,\n _strategyFromAddress,\n _assets,\n _amounts\n );\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n function _withdrawAllFromStrategy(address _strategyAddr) internal override {\n super._withdrawAllFromStrategy(_strategyAddr);\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n function _withdrawAllFromStrategies() internal override {\n super._withdrawAllFromStrategies();\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n /// @dev Calculate how much WETH in the vault is not reserved for the withdrawal queue.\n // That is, it is available to be redeemed or deposited into a strategy.\n function _wethAvailable() internal view returns (uint256 wethAvailable) {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n uint256 outstandingWithdrawals = queue.queued - queue.claimed;\n\n // The amount of sitting in WETH in the vault\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // If there is not enough WETH in the vault to cover the outstanding withdrawals\n if (wethBalance <= outstandingWithdrawals) {\n return 0;\n }\n\n return wethBalance - outstandingWithdrawals;\n }\n\n function _swapCollateral(\n address,\n address,\n uint256,\n uint256,\n bytes calldata\n ) internal pure override returns (uint256) {\n revert(\"Collateral swap not supported\");\n }\n}\n" + }, + "contracts/vault/OETHVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { VaultCore } from \"./VaultCore.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\n\n/**\n * @title OETH VaultCore Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVaultCore is VaultCore {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n address public immutable weth;\n uint256 public wethAssetIndex;\n\n // For future use (because OETHBaseVaultCore inherits from this)\n uint256[50] private __gap;\n\n constructor(address _weth) {\n weth = _weth;\n }\n\n /**\n * @dev Caches WETH's index in `allAssets` variable.\n * Reduces gas usage by redeem by caching that.\n */\n function cacheWETHAssetIndex() external onlyGovernor {\n uint256 assetCount = allAssets.length;\n for (uint256 i; i < assetCount; ++i) {\n if (allAssets[i] == weth) {\n wethAssetIndex = i;\n break;\n }\n }\n\n require(allAssets[wethAssetIndex] == weth, \"Invalid WETH Asset Index\");\n }\n\n // @inheritdoc VaultCore\n function mintForStrategy(uint256 amount)\n external\n override\n whenNotCapitalPaused\n {\n require(\n strategies[msg.sender].isSupported == true,\n \"Unsupported strategy\"\n );\n require(\n isMintWhitelistedStrategy[msg.sender] == true,\n \"Not whitelisted strategy\"\n );\n\n emit Mint(msg.sender, amount);\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, amount);\n }\n\n // @inheritdoc VaultCore\n function burnForStrategy(uint256 amount)\n external\n override\n whenNotCapitalPaused\n {\n require(\n strategies[msg.sender].isSupported == true,\n \"Unsupported strategy\"\n );\n require(\n isMintWhitelistedStrategy[msg.sender] == true,\n \"Not whitelisted strategy\"\n );\n\n emit Redeem(msg.sender, amount);\n\n // Burn OTokens\n oUSD.burn(msg.sender, amount);\n }\n\n // @inheritdoc VaultCore\n // slither-disable-start reentrancy-no-eth\n function _mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) internal virtual override {\n require(_asset == weth, \"Unsupported asset for minting\");\n require(_amount > 0, \"Amount must be greater than 0\");\n require(\n _amount >= _minimumOusdAmount,\n \"Mint amount lower than minimum\"\n );\n\n emit Mint(msg.sender, _amount);\n\n // Rebase must happen before any transfers occur.\n if (!rebasePaused && _amount >= rebaseThreshold) {\n _rebase();\n }\n\n // Mint oTokens\n oUSD.mint(msg.sender, _amount);\n\n // Transfer the deposited coins to the vault\n IERC20(_asset).safeTransferFrom(msg.sender, address(this), _amount);\n\n // Give priority to the withdrawal queue for the new WETH liquidity\n _addWithdrawalQueueLiquidity();\n\n // Auto-allocate if necessary\n if (_amount >= autoAllocateThreshold) {\n _allocate();\n }\n }\n\n // slither-disable-end reentrancy-no-eth\n\n // @inheritdoc VaultCore\n function _calculateRedeemOutputs(uint256 _amount)\n internal\n view\n virtual\n override\n returns (uint256[] memory outputs)\n {\n // Overrides `VaultCore._calculateRedeemOutputs` to redeem with only\n // WETH instead of LST-mix. Doesn't change the function signature\n // for backward compatibility\n\n // Calculate redeem fee\n if (redeemFeeBps > 0) {\n uint256 redeemFee = _amount.mulTruncateScale(redeemFeeBps, 1e4);\n _amount = _amount - redeemFee;\n }\n\n // Ensure that the WETH index is cached\n uint256 _wethAssetIndex = wethAssetIndex;\n require(\n allAssets[_wethAssetIndex] == weth,\n \"WETH Asset index not cached\"\n );\n\n outputs = new uint256[](allAssets.length);\n outputs[_wethAssetIndex] = _amount;\n }\n\n // @inheritdoc VaultCore\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n override\n {\n // Override `VaultCore._redeem` to simplify it. Gets rid of oracle\n // usage and looping through all assets for LST-mix redeem. Instead\n // does a simple WETH-only redeem.\n emit Redeem(msg.sender, _amount);\n\n if (_amount == 0) {\n return;\n }\n\n // Amount excluding fees\n // No fee for the strategist or the governor, makes it easier to do operations\n uint256 amountMinusFee = (msg.sender == strategistAddr || isGovernor())\n ? _amount\n : _calculateRedeemOutputs(_amount)[wethAssetIndex];\n\n require(\n amountMinusFee >= _minimumUnitAmount,\n \"Redeem amount lower than minimum\"\n );\n\n // Is there enough WETH in the Vault available after accounting for the withdrawal queue\n require(_wethAvailable() >= amountMinusFee, \"Liquidity error\");\n\n // Transfer WETH minus the fee to the redeemer\n IERC20(weth).safeTransfer(msg.sender, amountMinusFee);\n\n // Burn OETH from user (including fees)\n oUSD.burn(msg.sender, _amount);\n\n // Prevent insolvency\n _postRedeem(_amount);\n }\n\n /**\n * @notice Request an asynchronous withdrawal of WETH in exchange for OETH.\n * The OETH is burned on request and the WETH is transferred to the withdrawer on claim.\n * This request can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal this request's `queued` amount.\n * There is a minimum of 10 minutes before a request can be claimed. After that, the request just needs\n * enough WETH liquidity in the Vault to satisfy all the outstanding requests to that point in the queue.\n * OETH is converted to WETH at 1:1.\n * @param _amount Amount of OETH to burn.\n * @return requestId Unique ID for the withdrawal request\n * @return queued Cumulative total of all WETH queued including already claimed requests.\n */\n function requestWithdrawal(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256 requestId, uint256 queued)\n {\n require(withdrawalClaimDelay > 0, \"Async withdrawals not enabled\");\n\n // The check that the requester has enough OETH is done in to later burn call\n\n requestId = withdrawalQueueMetadata.nextWithdrawalIndex;\n queued = withdrawalQueueMetadata.queued + _amount;\n\n // Store the next withdrawal request\n withdrawalQueueMetadata.nextWithdrawalIndex = SafeCast.toUint128(\n requestId + 1\n );\n // Store the updated queued amount which reserves WETH in the withdrawal queue\n // and reduces the vault's total assets\n withdrawalQueueMetadata.queued = SafeCast.toUint128(queued);\n // Store the user's withdrawal request\n withdrawalRequests[requestId] = WithdrawalRequest({\n withdrawer: msg.sender,\n claimed: false,\n timestamp: uint40(block.timestamp),\n amount: SafeCast.toUint128(_amount),\n queued: SafeCast.toUint128(queued)\n });\n\n // Burn the user's OETH\n oUSD.burn(msg.sender, _amount);\n\n // Prevent withdrawal if the vault is solvent by more than the allowed percentage\n _postRedeem(_amount);\n\n emit WithdrawalRequested(msg.sender, requestId, _amount, queued);\n }\n\n // slither-disable-start reentrancy-no-eth\n /**\n * @notice Claim a previously requested withdrawal once it is claimable.\n * This request can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal this request's `queued` amount and 10 minutes has passed.\n * If the requests is not claimable, the transaction will revert with `Queue pending liquidity`.\n * If the request is not older than 10 minutes, the transaction will revert with `Claim delay not met`.\n * OETH is converted to WETH at 1:1.\n * @param _requestId Unique ID for the withdrawal request\n * @return amount Amount of WETH transferred to the withdrawer\n */\n function claimWithdrawal(uint256 _requestId)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256 amount)\n {\n // Try and get more liquidity if there is not enough available\n if (\n withdrawalRequests[_requestId].queued >\n withdrawalQueueMetadata.claimable\n ) {\n // Add any WETH to the withdrawal queue\n // this needs to remain here as:\n // - Vault can be funded and `addWithdrawalQueueLiquidity` is not externally called\n // - funds can be withdrawn from a strategy\n //\n // Those funds need to be added to withdrawal queue liquidity\n _addWithdrawalQueueLiquidity();\n }\n\n amount = _claimWithdrawal(_requestId);\n\n // transfer WETH from the vault to the withdrawer\n IERC20(weth).safeTransfer(msg.sender, amount);\n\n // Prevent insolvency\n _postRedeem(amount);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @notice Claim a previously requested withdrawals once they are claimable.\n * This requests can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal each request's `queued` amount and 10 minutes has passed.\n * If one of the requests is not claimable, the whole transaction will revert with `Queue pending liquidity`.\n * If one of the requests is not older than 10 minutes,\n * the whole transaction will revert with `Claim delay not met`.\n * @param _requestIds Unique ID of each withdrawal request\n * @return amounts Amount of WETH received for each request\n * @return totalAmount Total amount of WETH transferred to the withdrawer\n */\n function claimWithdrawals(uint256[] calldata _requestIds)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256[] memory amounts, uint256 totalAmount)\n {\n // Add any WETH to the withdrawal queue\n // this needs to remain here as:\n // - Vault can be funded and `addWithdrawalQueueLiquidity` is not externally called\n // - funds can be withdrawn from a strategy\n //\n // Those funds need to be added to withdrawal queue liquidity\n _addWithdrawalQueueLiquidity();\n\n amounts = new uint256[](_requestIds.length);\n for (uint256 i; i < _requestIds.length; ++i) {\n amounts[i] = _claimWithdrawal(_requestIds[i]);\n totalAmount += amounts[i];\n }\n\n // transfer all the claimed WETH from the vault to the withdrawer\n IERC20(weth).safeTransfer(msg.sender, totalAmount);\n\n // Prevent insolvency\n _postRedeem(totalAmount);\n }\n\n function _claimWithdrawal(uint256 requestId)\n internal\n returns (uint256 amount)\n {\n require(withdrawalClaimDelay > 0, \"Async withdrawals not enabled\");\n\n // Load the structs from storage into memory\n WithdrawalRequest memory request = withdrawalRequests[requestId];\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n require(\n request.timestamp + withdrawalClaimDelay <= block.timestamp,\n \"Claim delay not met\"\n );\n // If there isn't enough reserved liquidity in the queue to claim\n require(request.queued <= queue.claimable, \"Queue pending liquidity\");\n require(request.withdrawer == msg.sender, \"Not requester\");\n require(request.claimed == false, \"Already claimed\");\n\n // Store the request as claimed\n withdrawalRequests[requestId].claimed = true;\n // Store the updated claimed amount\n withdrawalQueueMetadata.claimed = queue.claimed + request.amount;\n\n emit WithdrawalClaimed(msg.sender, requestId, request.amount);\n\n return request.amount;\n }\n\n /// @notice Adds WETH to the withdrawal queue if there is a funding shortfall.\n /// @dev is called from the Native Staking strategy when validator withdrawals are processed.\n /// It also called before any WETH is allocated to a strategy.\n function addWithdrawalQueueLiquidity() external {\n _addWithdrawalQueueLiquidity();\n }\n\n /// @dev Adds WETH to the withdrawal queue if there is a funding shortfall.\n /// This assumes 1 WETH equal 1 OETH.\n function _addWithdrawalQueueLiquidity()\n internal\n returns (uint256 addedClaimable)\n {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // Check if the claimable WETH is less than the queued amount\n uint256 queueShortfall = queue.queued - queue.claimable;\n\n // No need to do anything is the withdrawal queue is full funded\n if (queueShortfall == 0) {\n return 0;\n }\n\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // Of the claimable withdrawal requests, how much is unclaimed?\n // That is, the amount of WETH that is currently allocated for the withdrawal queue\n uint256 allocatedWeth = queue.claimable - queue.claimed;\n\n // If there is no unallocated WETH then there is nothing to add to the queue\n if (wethBalance <= allocatedWeth) {\n return 0;\n }\n\n uint256 unallocatedWeth = wethBalance - allocatedWeth;\n\n // the new claimable amount is the smaller of the queue shortfall or unallocated weth\n addedClaimable = queueShortfall < unallocatedWeth\n ? queueShortfall\n : unallocatedWeth;\n uint256 newClaimable = queue.claimable + addedClaimable;\n\n // Store the new claimable amount back to storage\n withdrawalQueueMetadata.claimable = SafeCast.toUint128(newClaimable);\n\n // emit a WithdrawalClaimable event\n emit WithdrawalClaimable(newClaimable, addedClaimable);\n }\n\n /***************************************\n View Functions\n ****************************************/\n\n /// @dev Calculate how much WETH in the vault is not reserved for the withdrawal queue.\n // That is, it is available to be redeemed or deposited into a strategy.\n function _wethAvailable() internal view returns (uint256 wethAvailable) {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n uint256 outstandingWithdrawals = queue.queued - queue.claimed;\n\n // The amount of sitting in WETH in the vault\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // If there is not enough WETH in the vault to cover the outstanding withdrawals\n if (wethBalance <= outstandingWithdrawals) {\n return 0;\n }\n\n return wethBalance - outstandingWithdrawals;\n }\n\n /// @dev Get the balance of an asset held in Vault and all strategies\n /// less any WETH that is reserved for the withdrawal queue.\n /// WETH is the only asset that can return a non-zero balance.\n /// All other assets will return 0 even if there is some dust amounts left in the Vault.\n /// For example, there is 1 wei left of stETH in the OETH Vault but will return 0 in this function.\n ///\n /// If there is not enough WETH in the vault and all strategies to cover all outstanding\n /// withdrawal requests then return a WETH balance of 0\n function _checkBalance(address _asset)\n internal\n view\n override\n returns (uint256 balance)\n {\n if (_asset != weth) {\n return 0;\n }\n\n // Get the WETH in the vault and the strategies\n balance = super._checkBalance(_asset);\n\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // If the vault becomes insolvent enough that the total value in the vault and all strategies\n // is less than the outstanding withdrawals.\n // For example, there was a mass slashing event and most users request a withdrawal.\n if (balance + queue.claimed < queue.queued) {\n return 0;\n }\n\n // Need to remove WETH that is reserved for the withdrawal queue\n return balance + queue.claimed - queue.queued;\n }\n\n /**\n * @notice Allocate unallocated funds on Vault to strategies.\n **/\n function allocate() external override whenNotCapitalPaused nonReentrant {\n // Add any unallocated WETH to the withdrawal queue first\n _addWithdrawalQueueLiquidity();\n\n _allocate();\n }\n\n /// @dev Allocate WETH to the default WETH strategy if there is excess to the Vault buffer.\n /// This is called from either `mint` or `allocate` and assumes `_addWithdrawalQueueLiquidity`\n /// has been called before this function.\n function _allocate() internal override {\n // No need to do anything if no default strategy for WETH\n address depositStrategyAddr = assetDefaultStrategies[weth];\n if (depositStrategyAddr == address(0)) return;\n\n uint256 wethAvailableInVault = _wethAvailable();\n // No need to do anything if there isn't any WETH in the vault to allocate\n if (wethAvailableInVault == 0) return;\n\n // Calculate the target buffer for the vault using the total supply\n uint256 totalSupply = oUSD.totalSupply();\n uint256 targetBuffer = totalSupply.mulTruncate(vaultBuffer);\n\n // If available WETH in the Vault is below or equal the target buffer then there's nothing to allocate\n if (wethAvailableInVault <= targetBuffer) return;\n\n // The amount of assets to allocate to the default strategy\n uint256 allocateAmount = wethAvailableInVault - targetBuffer;\n\n IStrategy strategy = IStrategy(depositStrategyAddr);\n // Transfer WETH to the strategy and call the strategy's deposit function\n IERC20(weth).safeTransfer(address(strategy), allocateAmount);\n strategy.deposit(weth, allocateAmount);\n\n emit AssetAllocated(weth, depositStrategyAddr, allocateAmount);\n }\n\n /// @dev The total value of all WETH held by the vault and all its strategies\n /// less any WETH that is reserved for the withdrawal queue.\n ///\n // If there is not enough WETH in the vault and all strategies to cover all outstanding\n // withdrawal requests then return a total value of 0.\n function _totalValue() internal view override returns (uint256 value) {\n // As WETH is the only asset, just return the WETH balance\n return _checkBalance(weth);\n }\n\n /// @dev Only WETH is supported in the OETH Vault so return the WETH balance only\n /// Any ETH balances in the Vault will be ignored.\n /// Amounts from previously supported vault assets will also be ignored.\n /// For example, there is 1 wei left of stETH in the OETH Vault but is will be ignored.\n function _totalValueInVault()\n internal\n view\n override\n returns (uint256 value)\n {\n value = IERC20(weth).balanceOf(address(this));\n }\n}\n" + }, + "contracts/vault/OETHZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { ISfrxETH } from \"../interfaces/ISfrxETH.sol\";\n\ncontract OETHZapper {\n IERC20 public immutable oeth;\n IVault public immutable vault;\n\n IWETH9 public constant weth =\n IWETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);\n IERC20 public constant frxeth =\n IERC20(0x5E8422345238F34275888049021821E8E08CAa1f);\n ISfrxETH public constant sfrxeth =\n ISfrxETH(0xac3E018457B222d93114458476f3E3416Abbe38F);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(address _oeth, address _vault) {\n oeth = IERC20(_oeth);\n vault = IVault(_vault);\n\n weth.approve(address(_vault), type(uint256).max);\n frxeth.approve(address(_vault), type(uint256).max);\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return.\n * Will verify that the user is sent 1:1 for ETH.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return\n * Will verify that the user is sent 1:1 for ETH.\n * @return Amount of OETH sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n weth.deposit{ value: balance }();\n emit Zap(msg.sender, ETH_MARKER, balance);\n return _mint(address(weth), balance);\n }\n\n /**\n * @dev Deposit SFRXETH to the vault and receive OETH in return\n * @param amount Amount of SFRXETH to deposit\n * @param minOETH Minimum amount of OETH to receive\n * @return Amount of OETH sent to user\n */\n function depositSFRXETH(uint256 amount, uint256 minOETH)\n external\n returns (uint256)\n {\n sfrxeth.redeem(amount, address(this), msg.sender);\n emit Zap(msg.sender, address(sfrxeth), amount);\n return _mint(address(frxeth), minOETH);\n }\n\n /**\n * @dev Internal function to mint OETH from an asset\n * @param asset Address of asset for the vault to mint from\n * @param minOETH Minimum amount of OETH to for user to receive\n * @return Amount of OETH sent to user\n */\n function _mint(address asset, uint256 minOETH) internal returns (uint256) {\n uint256 toMint = IERC20(asset).balanceOf(address(this));\n vault.mint(asset, toMint, minOETH);\n uint256 mintedAmount = oeth.balanceOf(address(this));\n require(mintedAmount >= minOETH, \"Zapper: not enough minted\");\n require(oeth.transfer(msg.sender, mintedAmount));\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/OSonicVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"./OETHVaultAdmin.sol\";\n\n/**\n * @title Origin Sonic VaultAdmin contract on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonicVaultAdmin is OETHVaultAdmin {\n /// @param _wS Sonic's Wrapped S token\n constructor(address _wS) OETHVaultAdmin(_wS) {}\n\n /***************************************\n Asset Config\n ****************************************/\n\n /**\n * @notice Add a supported asset to the contract, i.e. one that can be to mint OTokens.\n * @dev Overridden to remove price provider integration\n * @param _asset Address of asset\n * @param _unitConversion 0 decimals, 1 exchange rate\n */\n function supportAsset(address _asset, uint8 _unitConversion)\n external\n override\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Asset already supported\");\n\n assets[_asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(_unitConversion),\n decimals: 0, // will be overridden in _cacheDecimals\n allowedOracleSlippageBps: 0 // 0% by default\n });\n\n _cacheDecimals(_asset);\n allAssets.push(_asset);\n\n emit AssetSupported(_asset);\n }\n}\n" + }, + "contracts/vault/OSonicVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultCore } from \"./OETHVaultCore.sol\";\n\n/**\n * @title Origin Sonic VaultCore contract on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonicVaultCore is OETHVaultCore {\n /// @param _wS Sonic's Wrapped S token\n constructor(address _wS) OETHVaultCore(_wS) {}\n\n /**\n * @notice Instant redeem is not supported on Sonic.\n * Use the asynchronous `requestWithdrawal` a `claimWithdrawal` instead.\n */\n function _redeem(uint256, uint256) internal override {\n revert(\"unsupported function\");\n }\n}\n" + }, + "contracts/vault/OSonicZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWrappedSonic } from \"../interfaces/sonic/IWrappedSonic.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\n\n/**\n * @title Zapper for Origin Sonic (OS) tokens\n * @author Origin Protocol Inc\n */\ncontract OSonicZapper {\n IERC20 public immutable OS;\n IERC4626 public immutable wOS;\n IVault public immutable vault;\n\n IWrappedSonic public constant wS =\n IWrappedSonic(0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(\n address _OS,\n address _wOS,\n address _vault\n ) {\n OS = IERC20(_OS);\n wOS = IERC4626(_wOS);\n vault = IVault(_vault);\n\n wS.approve(address(_vault), type(uint256).max);\n IERC20(_OS).approve(_wOS, type(uint256).max);\n }\n\n /**\n * @dev Deposit native S currency and receive Origin Sonic (OS) tokens in return.\n * Will verify that the user is sent 1:1 for S.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit native S currency and receive Origin Sonic (OS) tokens in return.\n * Will verify that the user is sent 1:1 for S.\n * @return Amount of Origin Sonic (OS) tokens sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap native S\n wS.deposit{ value: balance }();\n\n // Mint Origin Sonic (OS) with Wrapped Sonic (wS)\n return _mint(balance, msg.sender);\n }\n\n /**\n * @dev Deposit S and receive Wrapped Origin Sonic (wOS) in return\n * @param minReceived min amount of Wrapped Origin Sonic (wOS) to receive\n * @return Amount of Wrapped Origin Sonic (wOS) tokens sent to user\n */\n function depositSForWrappedTokens(uint256 minReceived)\n external\n payable\n returns (uint256)\n {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap S\n wS.deposit{ value: balance }();\n\n // Mint with Wrapped Sonic\n uint256 mintOS = _mint(balance, address(this));\n\n // Wrap Origin Sonic (OS) into Wrapped Origin Sonic (wOS)\n uint256 mintedWOS = wOS.deposit(mintOS, msg.sender);\n\n require(mintedWOS >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWOS;\n }\n\n /**\n * @dev Deposit Wrapped Sonic (wS) tokens and receive Wrapped Origin Sonic (wOS) tokens in return\n * @param wSAmount Amount of Wrapped Sonic (wS) to deposit\n * @param minReceived min amount of Wrapped Origin Sonic (wOS) token to receive\n * @return Amount of Wrapped Origin Sonic (wOS) tokens sent to user\n */\n function depositWSForWrappedTokens(uint256 wSAmount, uint256 minReceived)\n external\n returns (uint256)\n {\n // slither-disable-next-line unchecked-transfer unused-return\n wS.transferFrom(msg.sender, address(this), wSAmount);\n\n emit Zap(msg.sender, address(wS), wSAmount);\n\n // Mint with Wrapped Sonic (wS)\n uint256 mintedOS = _mint(wSAmount, address(this));\n\n // Wrap Origin Sonic (OS) tokens into Wrapped Origin Sonic (wOS) tokens\n uint256 mintedWOS = wOS.deposit(mintedOS, msg.sender);\n\n require(mintedWOS >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWOS;\n }\n\n /**\n * @dev Internal function to mint Origin Sonic (OS) with Wrapped S (wS)\n * @param minOS Minimum amount of Origin Sonic (OS) tokens the user can receive\n * @param recipient Address that receives the tokens\n * @return Amount of Origin Sonic (OS) tokens sent to the recipient\n */\n function _mint(uint256 minOS, address recipient)\n internal\n returns (uint256)\n {\n uint256 toMint = wS.balanceOf(address(this));\n vault.mint(address(wS), toMint, minOS);\n uint256 mintedAmount = OS.balanceOf(address(this));\n require(mintedAmount >= minOS, \"Zapper: not enough minted\");\n\n if (recipient != address(this)) {\n require(OS.transfer(recipient, mintedAmount));\n }\n\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/Vault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD VaultInitializer Contract\n * @notice The VaultInitializer sets up the initial contract.\n * @author Origin Protocol Inc\n */\nimport { VaultInitializer } from \"./VaultInitializer.sol\";\nimport { VaultAdmin } from \"./VaultAdmin.sol\";\n\ncontract Vault is VaultInitializer, VaultAdmin {}\n" + }, + "contracts/vault/VaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultAdmin contract\n * @notice The VaultAdmin contract makes configuration and admin calls on the vault.\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport \"./VaultStorage.sol\";\n\ncontract VaultAdmin is VaultStorage {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /***************************************\n Configuration\n ****************************************/\n\n /**\n * @notice Set address of price provider.\n * @param _priceProvider Address of price provider\n */\n function setPriceProvider(address _priceProvider) external onlyGovernor {\n priceProvider = _priceProvider;\n emit PriceProviderUpdated(_priceProvider);\n }\n\n /**\n * @notice Set a fee in basis points to be charged for a redeem.\n * @param _redeemFeeBps Basis point fee to be charged\n */\n function setRedeemFeeBps(uint256 _redeemFeeBps) external onlyGovernor {\n require(_redeemFeeBps <= 1000, \"Redeem fee should not be over 10%\");\n redeemFeeBps = _redeemFeeBps;\n emit RedeemFeeUpdated(_redeemFeeBps);\n }\n\n /**\n * @notice Set a buffer of assets to keep in the Vault to handle most\n * redemptions without needing to spend gas unwinding assets from a Strategy.\n * @param _vaultBuffer Percentage using 18 decimals. 100% = 1e18.\n */\n function setVaultBuffer(uint256 _vaultBuffer)\n external\n onlyGovernorOrStrategist\n {\n require(_vaultBuffer <= 1e18, \"Invalid value\");\n vaultBuffer = _vaultBuffer;\n emit VaultBufferUpdated(_vaultBuffer);\n }\n\n /**\n * @notice Sets the minimum amount of OTokens in a mint to trigger an\n * automatic allocation of funds afterwords.\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setAutoAllocateThreshold(uint256 _threshold)\n external\n onlyGovernor\n {\n autoAllocateThreshold = _threshold;\n emit AllocateThresholdUpdated(_threshold);\n }\n\n /**\n * @notice Set a minimum amount of OTokens in a mint or redeem that triggers a\n * rebase\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setRebaseThreshold(uint256 _threshold) external onlyGovernor {\n rebaseThreshold = _threshold;\n emit RebaseThresholdUpdated(_threshold);\n }\n\n /**\n * @notice Set address of Strategist\n * @param _address Address of Strategist\n */\n function setStrategistAddr(address _address) external onlyGovernor {\n strategistAddr = _address;\n emit StrategistUpdated(_address);\n }\n\n /**\n * @notice Set the default Strategy for an asset, i.e. the one which the asset\n will be automatically allocated to and withdrawn from\n * @param _asset Address of the asset\n * @param _strategy Address of the Strategy\n */\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external\n onlyGovernorOrStrategist\n {\n emit AssetDefaultStrategyUpdated(_asset, _strategy);\n // If its a zero address being passed for the strategy we are removing\n // the default strategy\n if (_strategy != address(0)) {\n // Make sure the strategy meets some criteria\n require(strategies[_strategy].isSupported, \"Strategy not approved\");\n IStrategy strategy = IStrategy(_strategy);\n require(assets[_asset].isSupported, \"Asset is not supported\");\n require(\n strategy.supportsAsset(_asset),\n \"Asset not supported by Strategy\"\n );\n }\n assetDefaultStrategies[_asset] = _strategy;\n }\n\n /**\n * @notice Set maximum amount of OTokens that can at any point be minted and deployed\n * to strategy (used only by ConvexOUSDMetaStrategy for now).\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setNetOusdMintForStrategyThreshold(uint256 _threshold)\n external\n onlyGovernor\n {\n /**\n * Because `netOusdMintedForStrategy` check in vault core works both ways\n * (positive and negative) the actual impact of the amount of OToken minted\n * could be double the threshold. E.g.:\n * - contract has threshold set to 100\n * - state of netOusdMinted is -90\n * - in effect it can mint 190 OToken and still be within limits\n *\n * We are somewhat mitigating this behaviour by resetting the netOusdMinted\n * counter whenever new threshold is set. So it can only move one threshold\n * amount in each direction. This also enables us to reduce the threshold\n * amount and not have problems with current netOusdMinted being near\n * limits on either side.\n */\n netOusdMintedForStrategy = 0;\n netOusdMintForStrategyThreshold = _threshold;\n emit NetOusdMintForStrategyThresholdChanged(_threshold);\n }\n\n /**\n * @notice Changes the async withdrawal claim period for OETH & superOETHb\n * @param _delay Delay period (should be between 10 mins to 7 days).\n * Set to 0 to disable async withdrawals\n */\n function setWithdrawalClaimDelay(uint256 _delay) external onlyGovernor {\n require(\n _delay == 0 || (_delay >= 10 minutes && _delay <= 15 days),\n \"Invalid claim delay period\"\n );\n withdrawalClaimDelay = _delay;\n emit WithdrawalClaimDelayUpdated(_delay);\n }\n\n /**\n * @notice Set a yield streaming max rate. This spreads yield over\n * time if it is above the max rate.\n * @param yearlyApr in 1e18 notation. 3 * 1e18 = 3% APR\n */\n function setRebaseRateMax(uint256 yearlyApr)\n external\n onlyGovernorOrStrategist\n {\n // The old yield will be at the old rate\n IVault(address(this)).rebase();\n // Change the rate\n uint256 newPerSecond = yearlyApr / 100 / 365 days;\n require(newPerSecond <= MAX_REBASE_PER_SECOND, \"Rate too high\");\n rebasePerSecondMax = newPerSecond.toUint64();\n emit RebasePerSecondMaxChanged(newPerSecond);\n }\n\n /**\n * @notice Set the drip duration period\n * @param _dripDuration Time in seconds to target a constant yield rate\n */\n function setDripDuration(uint256 _dripDuration)\n external\n onlyGovernorOrStrategist\n {\n // The old yield will be at the old rate\n IVault(address(this)).rebase();\n dripDuration = _dripDuration.toUint64();\n emit DripDurationChanged(_dripDuration);\n }\n\n /***************************************\n Swaps\n ****************************************/\n\n /**\n * @notice Strategist swaps collateral assets sitting in the vault.\n * @param _fromAsset The token address of the asset being sold by the vault.\n * @param _toAsset The token address of the asset being purchased by the vault.\n * @param _fromAssetAmount The amount of assets being sold by the vault.\n * @param _minToAssetAmount The minimum amount of assets to be purchased.\n * @param _data implementation specific data. eg 1Inch swap data\n * @return toAssetAmount The amount of toAssets that was received from the swap\n */\n function swapCollateral(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n )\n external\n nonReentrant\n onlyGovernorOrStrategist\n returns (uint256 toAssetAmount)\n {\n toAssetAmount = _swapCollateral(\n _fromAsset,\n _toAsset,\n _fromAssetAmount,\n _minToAssetAmount,\n _data\n );\n }\n\n function _swapCollateral(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n ) internal virtual returns (uint256 toAssetAmount) {\n // Check fromAsset and toAsset are valid\n Asset memory fromAssetConfig = assets[_fromAsset];\n Asset memory toAssetConfig = assets[_toAsset];\n require(fromAssetConfig.isSupported, \"From asset is not supported\");\n require(toAssetConfig.isSupported, \"To asset is not supported\");\n\n // Load swap config into memory to avoid separate SLOADs\n SwapConfig memory config = swapConfig;\n\n // Scope a new block to remove toAssetBalBefore from the scope of swapCollateral.\n // This avoids a stack too deep error.\n {\n uint256 toAssetBalBefore = IERC20(_toAsset).balanceOf(\n address(this)\n );\n\n // Transfer from assets to the swapper contract\n IERC20(_fromAsset).safeTransfer(config.swapper, _fromAssetAmount);\n\n // Call to the Swapper contract to do the actual swap\n // The -1 is required for stETH which sometimes transfers 1 wei less than what was specified.\n // slither-disable-next-line unused-return\n ISwapper(config.swapper).swap(\n _fromAsset,\n _toAsset,\n _fromAssetAmount - 1,\n _minToAssetAmount,\n _data\n );\n\n // Compute the change in asset balance held by the Vault\n toAssetAmount =\n IERC20(_toAsset).balanceOf(address(this)) -\n toAssetBalBefore;\n }\n\n // Check the to assets returned is above slippage amount specified by the strategist\n require(\n toAssetAmount >= _minToAssetAmount,\n \"Strategist slippage limit\"\n );\n\n // Scope a new block to remove minOracleToAssetAmount from the scope of swapCollateral.\n // This avoids a stack too deep error.\n {\n // Check the slippage against the Oracle in case the strategist made a mistake or has become malicious.\n // to asset amount = from asset amount * from asset price / to asset price\n uint256 minOracleToAssetAmount = (_fromAssetAmount *\n (1e4 - fromAssetConfig.allowedOracleSlippageBps) *\n IOracle(priceProvider).price(_fromAsset)) /\n (IOracle(priceProvider).price(_toAsset) *\n (1e4 + toAssetConfig.allowedOracleSlippageBps));\n\n // Scale both sides up to 18 decimals to compare\n require(\n toAssetAmount.scaleBy(18, toAssetConfig.decimals) >=\n minOracleToAssetAmount.scaleBy(\n 18,\n fromAssetConfig.decimals\n ),\n \"Oracle slippage limit exceeded\"\n );\n }\n\n // Check the vault's total value hasn't gone below the OToken total supply\n // by more than the allowed percentage.\n require(\n IVault(address(this)).totalValue() >=\n (oUSD.totalSupply() * ((1e4 - config.allowedUndervalueBps))) /\n 1e4,\n \"Allowed value < supply\"\n );\n\n emit Swapped(_fromAsset, _toAsset, _fromAssetAmount, toAssetAmount);\n }\n\n /***************************************\n Swap Config\n ****************************************/\n\n /**\n * @notice Set the contract the performs swaps of collateral assets.\n * @param _swapperAddr Address of the Swapper contract that implements the ISwapper interface.\n */\n function setSwapper(address _swapperAddr) external onlyGovernor {\n swapConfig.swapper = _swapperAddr;\n emit SwapperChanged(_swapperAddr);\n }\n\n /// @notice Contract that swaps the vault's collateral assets\n function swapper() external view returns (address swapper_) {\n swapper_ = swapConfig.swapper;\n }\n\n /**\n * @notice Set max allowed percentage the vault total value can drop below the OToken total supply in basis points\n * when executing collateral swaps.\n * @param _basis Percentage in basis points. eg 100 == 1%\n */\n function setSwapAllowedUndervalue(uint16 _basis) external onlyGovernor {\n require(_basis < 10001, \"Invalid basis points\");\n swapConfig.allowedUndervalueBps = _basis;\n emit SwapAllowedUndervalueChanged(_basis);\n }\n\n /**\n * @notice Max allowed percentage the vault total value can drop below the OToken total supply in basis points\n * when executing a collateral swap.\n * For example 100 == 1%\n * @return value Percentage in basis points.\n */\n function allowedSwapUndervalue() external view returns (uint256 value) {\n value = swapConfig.allowedUndervalueBps;\n }\n\n /**\n * @notice Set the allowed slippage from the Oracle price for collateral asset swaps.\n * @param _asset Address of the asset token.\n * @param _allowedOracleSlippageBps allowed slippage from Oracle in basis points. eg 20 = 0.2%. Max 10%.\n */\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external\n onlyGovernor\n {\n require(assets[_asset].isSupported, \"Asset not supported\");\n require(_allowedOracleSlippageBps < 1000, \"Slippage too high\");\n\n assets[_asset].allowedOracleSlippageBps = _allowedOracleSlippageBps;\n\n emit SwapSlippageChanged(_asset, _allowedOracleSlippageBps);\n }\n\n /***************************************\n Asset Config\n ****************************************/\n\n /**\n * @notice Add a supported asset to the contract, i.e. one that can be\n * to mint OTokens.\n * @param _asset Address of asset\n */\n function supportAsset(address _asset, uint8 _unitConversion)\n external\n virtual\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Asset already supported\");\n\n assets[_asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(_unitConversion),\n decimals: 0, // will be overridden in _cacheDecimals\n allowedOracleSlippageBps: 0 // 0% by default\n });\n\n _cacheDecimals(_asset);\n allAssets.push(_asset);\n\n // Verify that our oracle supports the asset\n // slither-disable-next-line unused-return\n IOracle(priceProvider).price(_asset);\n\n emit AssetSupported(_asset);\n }\n\n /**\n * @notice Remove a supported asset from the Vault\n * @param _asset Address of asset\n */\n function removeAsset(address _asset) external onlyGovernor {\n require(assets[_asset].isSupported, \"Asset not supported\");\n\n // 1e13 for 18 decimals. And 10 for 6 decimals\n uint256 maxDustBalance = uint256(1e13).scaleBy(\n assets[_asset].decimals,\n 18\n );\n\n require(\n IVault(address(this)).checkBalance(_asset) <= maxDustBalance,\n \"Vault still holds asset\"\n );\n\n uint256 assetsCount = allAssets.length;\n uint256 assetIndex = assetsCount; // initialize at invalid index\n for (uint256 i = 0; i < assetsCount; ++i) {\n if (allAssets[i] == _asset) {\n assetIndex = i;\n break;\n }\n }\n\n // Note: If asset is not found in `allAssets`, the following line\n // will revert with an out-of-bound error. However, there's no\n // reason why an asset would have `Asset.isSupported = true` but\n // not exist in `allAssets`.\n\n // Update allAssets array\n allAssets[assetIndex] = allAssets[assetsCount - 1];\n allAssets.pop();\n\n // Reset default strategy\n assetDefaultStrategies[_asset] = address(0);\n emit AssetDefaultStrategyUpdated(_asset, address(0));\n\n // Remove asset from storage\n delete assets[_asset];\n\n emit AssetRemoved(_asset);\n }\n\n /**\n * @notice Cache decimals on OracleRouter for a particular asset. This action\n * is required before that asset's price can be accessed.\n * @param _asset Address of asset token\n */\n function cacheDecimals(address _asset) external onlyGovernor {\n _cacheDecimals(_asset);\n }\n\n /***************************************\n Strategy Config\n ****************************************/\n\n /**\n * @notice Add a strategy to the Vault.\n * @param _addr Address of the strategy to add\n */\n function approveStrategy(address _addr) external onlyGovernor {\n require(!strategies[_addr].isSupported, \"Strategy already approved\");\n strategies[_addr] = Strategy({ isSupported: true, _deprecated: 0 });\n allStrategies.push(_addr);\n emit StrategyApproved(_addr);\n }\n\n /**\n * @notice Remove a strategy from the Vault.\n * @param _addr Address of the strategy to remove\n */\n\n function removeStrategy(address _addr) external onlyGovernor {\n require(strategies[_addr].isSupported, \"Strategy not approved\");\n\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n require(\n assetDefaultStrategies[allAssets[i]] != _addr,\n \"Strategy is default for an asset\"\n );\n }\n\n // Initialize strategyIndex with out of bounds result so function will\n // revert if no valid index found\n uint256 stratCount = allStrategies.length;\n uint256 strategyIndex = stratCount;\n for (uint256 i = 0; i < stratCount; ++i) {\n if (allStrategies[i] == _addr) {\n strategyIndex = i;\n break;\n }\n }\n\n if (strategyIndex < stratCount) {\n allStrategies[strategyIndex] = allStrategies[stratCount - 1];\n allStrategies.pop();\n\n // Mark the strategy as not supported\n strategies[_addr].isSupported = false;\n\n // Withdraw all assets\n IStrategy strategy = IStrategy(_addr);\n strategy.withdrawAll();\n\n emit StrategyRemoved(_addr);\n }\n }\n\n /***************************************\n Strategies\n ****************************************/\n\n /**\n * @notice Deposit multiple assets from the vault into the strategy.\n * @param _strategyToAddress Address of the Strategy to deposit assets into.\n * @param _assets Array of asset address that will be deposited into the strategy.\n * @param _amounts Array of amounts of each corresponding asset to deposit.\n */\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external onlyGovernorOrStrategist nonReentrant {\n _depositToStrategy(_strategyToAddress, _assets, _amounts);\n }\n\n function _depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal virtual {\n require(\n strategies[_strategyToAddress].isSupported,\n \"Invalid to Strategy\"\n );\n require(_assets.length == _amounts.length, \"Parameter length mismatch\");\n\n uint256 assetCount = _assets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n address assetAddr = _assets[i];\n require(\n IStrategy(_strategyToAddress).supportsAsset(assetAddr),\n \"Asset unsupported\"\n );\n // Send required amount of funds to the strategy\n IERC20(assetAddr).safeTransfer(_strategyToAddress, _amounts[i]);\n }\n\n // Deposit all the funds that have been sent to the strategy\n IStrategy(_strategyToAddress).depositAll();\n }\n\n /**\n * @notice Withdraw multiple assets from the strategy to the vault.\n * @param _strategyFromAddress Address of the Strategy to withdraw assets from.\n * @param _assets Array of asset address that will be withdrawn from the strategy.\n * @param _amounts Array of amounts of each corresponding asset to withdraw.\n */\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external onlyGovernorOrStrategist nonReentrant {\n _withdrawFromStrategy(\n address(this),\n _strategyFromAddress,\n _assets,\n _amounts\n );\n }\n\n /**\n * @param _recipient can either be a strategy or the Vault\n */\n function _withdrawFromStrategy(\n address _recipient,\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal virtual {\n require(\n strategies[_strategyFromAddress].isSupported,\n \"Invalid from Strategy\"\n );\n require(_assets.length == _amounts.length, \"Parameter length mismatch\");\n\n uint256 assetCount = _assets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n // Withdraw from Strategy to the recipient\n IStrategy(_strategyFromAddress).withdraw(\n _recipient,\n _assets[i],\n _amounts[i]\n );\n }\n }\n\n /**\n * @notice Sets the maximum allowable difference between\n * total supply and backing assets' value.\n */\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external onlyGovernor {\n maxSupplyDiff = _maxSupplyDiff;\n emit MaxSupplyDiffChanged(_maxSupplyDiff);\n }\n\n /**\n * @notice Sets the trusteeAddress that can receive a portion of yield.\n * Setting to the zero address disables this feature.\n */\n function setTrusteeAddress(address _address) external onlyGovernor {\n trusteeAddress = _address;\n emit TrusteeAddressChanged(_address);\n }\n\n /**\n * @notice Sets the TrusteeFeeBps to the percentage of yield that should be\n * received in basis points.\n */\n function setTrusteeFeeBps(uint256 _basis) external onlyGovernor {\n require(_basis <= 5000, \"basis cannot exceed 50%\");\n trusteeFeeBps = _basis;\n emit TrusteeFeeBpsChanged(_basis);\n }\n\n /**\n * @notice Set OToken Metapool strategy\n * @param _ousdMetaStrategy Address of OToken metapool strategy\n */\n function setOusdMetaStrategy(address _ousdMetaStrategy)\n external\n onlyGovernor\n {\n ousdMetaStrategy = _ousdMetaStrategy;\n emit OusdMetaStrategyUpdated(_ousdMetaStrategy);\n }\n\n /***************************************\n Pause\n ****************************************/\n\n /**\n * @notice Set the deposit paused flag to true to prevent rebasing.\n */\n function pauseRebase() external onlyGovernorOrStrategist {\n rebasePaused = true;\n emit RebasePaused();\n }\n\n /**\n * @notice Set the deposit paused flag to true to allow rebasing.\n */\n function unpauseRebase() external onlyGovernorOrStrategist {\n rebasePaused = false;\n emit RebaseUnpaused();\n }\n\n /**\n * @notice Set the deposit paused flag to true to prevent capital movement.\n */\n function pauseCapital() external onlyGovernorOrStrategist {\n capitalPaused = true;\n emit CapitalPaused();\n }\n\n /**\n * @notice Set the deposit paused flag to false to enable capital movement.\n */\n function unpauseCapital() external onlyGovernorOrStrategist {\n capitalPaused = false;\n emit CapitalUnpaused();\n }\n\n /***************************************\n Utils\n ****************************************/\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Only unsupported assets\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /***************************************\n Strategies Admin\n ****************************************/\n\n /**\n * @notice Withdraws all assets from the strategy and sends assets to the Vault.\n * @param _strategyAddr Strategy address.\n */\n function withdrawAllFromStrategy(address _strategyAddr)\n external\n onlyGovernorOrStrategist\n {\n _withdrawAllFromStrategy(_strategyAddr);\n }\n\n function _withdrawAllFromStrategy(address _strategyAddr) internal virtual {\n require(\n strategies[_strategyAddr].isSupported,\n \"Strategy is not supported\"\n );\n IStrategy strategy = IStrategy(_strategyAddr);\n strategy.withdrawAll();\n }\n\n /**\n * @notice Withdraws all assets from all the strategies and sends assets to the Vault.\n */\n function withdrawAllFromStrategies() external onlyGovernorOrStrategist {\n _withdrawAllFromStrategies();\n }\n\n function _withdrawAllFromStrategies() internal virtual {\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n IStrategy(allStrategies[i]).withdrawAll();\n }\n }\n\n /***************************************\n Utils\n ****************************************/\n\n function _cacheDecimals(address token) internal {\n Asset storage tokenAsset = assets[token];\n if (tokenAsset.decimals != 0) {\n return;\n }\n uint8 decimals = IBasicToken(token).decimals();\n require(decimals >= 6 && decimals <= 18, \"Unexpected precision\");\n tokenAsset.decimals = decimals;\n }\n}\n" + }, + "contracts/vault/VaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultCore contract\n * @notice The Vault contract stores assets. On a deposit, OTokens will be minted\n and sent to the depositor. On a withdrawal, OTokens will be burned and\n assets will be sent to the withdrawer. The Vault accepts deposits of\n interest from yield bearing strategies which will modify the supply\n of OTokens.\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IGetExchangeRateToken } from \"../interfaces/IGetExchangeRateToken.sol\";\n\nimport \"./VaultInitializer.sol\";\n\ncontract VaultCore is VaultInitializer {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n /// @dev max signed int\n uint256 internal constant MAX_INT = uint256(type(int256).max);\n\n /**\n * @dev Verifies that the rebasing is not paused.\n */\n modifier whenNotRebasePaused() {\n require(!rebasePaused, \"Rebasing paused\");\n _;\n }\n\n /**\n * @dev Verifies that the deposits are not paused.\n */\n modifier whenNotCapitalPaused() {\n require(!capitalPaused, \"Capital paused\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the AMO strategy.\n */\n modifier onlyOusdMetaStrategy() {\n require(\n msg.sender == ousdMetaStrategy,\n \"Caller is not the OUSD meta strategy\"\n );\n _;\n }\n\n /**\n * @notice Deposit a supported asset and mint OTokens.\n * @param _asset Address of the asset being deposited\n * @param _amount Amount of the asset being deposited\n * @param _minimumOusdAmount Minimum OTokens to mint\n */\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external whenNotCapitalPaused nonReentrant {\n _mint(_asset, _amount, _minimumOusdAmount);\n }\n\n /**\n * @dev Deposit a supported asset and mint OTokens.\n * @param _asset Address of the asset being deposited\n * @param _amount Amount of the asset being deposited\n * @param _minimumOusdAmount Minimum OTokens to mint\n */\n function _mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) internal virtual {\n require(assets[_asset].isSupported, \"Asset is not supported\");\n require(_amount > 0, \"Amount must be greater than 0\");\n\n uint256 units = _toUnits(_amount, _asset);\n uint256 unitPrice = _toUnitPrice(_asset, true);\n uint256 priceAdjustedDeposit = (units * unitPrice) / 1e18;\n\n if (_minimumOusdAmount > 0) {\n require(\n priceAdjustedDeposit >= _minimumOusdAmount,\n \"Mint amount lower than minimum\"\n );\n }\n\n emit Mint(msg.sender, priceAdjustedDeposit);\n\n // Rebase must happen before any transfers occur.\n if (priceAdjustedDeposit >= rebaseThreshold && !rebasePaused) {\n _rebase();\n }\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, priceAdjustedDeposit);\n\n // Transfer the deposited coins to the vault\n IERC20 asset = IERC20(_asset);\n asset.safeTransferFrom(msg.sender, address(this), _amount);\n\n if (priceAdjustedDeposit >= autoAllocateThreshold) {\n _allocate();\n }\n }\n\n /**\n * @notice Mint OTokens for a Metapool Strategy\n * @param _amount Amount of the asset being deposited\n *\n * Notice: can't use `nonReentrant` modifier since the `mint` function can\n * call `allocate`, and that can trigger `ConvexOUSDMetaStrategy` to call this function\n * while the execution of the `mint` has not yet completed -> causing a `nonReentrant` collision.\n *\n * Also important to understand is that this is a limitation imposed by the test suite.\n * Production / mainnet contracts should never be configured in a way where mint/redeem functions\n * that are moving funds between the Vault and end user wallets can influence strategies\n * utilizing this function.\n */\n function mintForStrategy(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n onlyOusdMetaStrategy\n {\n require(_amount < MAX_INT, \"Amount too high\");\n\n emit Mint(msg.sender, _amount);\n\n // safe to cast because of the require check at the beginning of the function\n netOusdMintedForStrategy += int256(_amount);\n\n require(\n abs(netOusdMintedForStrategy) < netOusdMintForStrategyThreshold,\n \"Minted ousd surpassed netOusdMintForStrategyThreshold.\"\n );\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, _amount);\n }\n\n // In memoriam\n\n /**\n * @notice Withdraw a supported asset and burn OTokens.\n * @param _amount Amount of OTokens to burn\n * @param _minimumUnitAmount Minimum stablecoin units to receive in return\n */\n function redeem(uint256 _amount, uint256 _minimumUnitAmount)\n external\n whenNotCapitalPaused\n nonReentrant\n {\n _redeem(_amount, _minimumUnitAmount);\n }\n\n /**\n * @notice Withdraw a supported asset and burn OTokens.\n * @param _amount Amount of OTokens to burn\n * @param _minimumUnitAmount Minimum stablecoin units to receive in return\n */\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n {\n // Calculate redemption outputs\n uint256[] memory outputs = _calculateRedeemOutputs(_amount);\n\n emit Redeem(msg.sender, _amount);\n\n // Send outputs\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n if (outputs[i] == 0) continue;\n\n address assetAddr = allAssets[i];\n\n if (IERC20(assetAddr).balanceOf(address(this)) >= outputs[i]) {\n // Use Vault funds first if sufficient\n IERC20(assetAddr).safeTransfer(msg.sender, outputs[i]);\n } else {\n address strategyAddr = assetDefaultStrategies[assetAddr];\n if (strategyAddr != address(0)) {\n // Nothing in Vault, but something in Strategy, send from there\n IStrategy strategy = IStrategy(strategyAddr);\n strategy.withdraw(msg.sender, assetAddr, outputs[i]);\n } else {\n // Cant find funds anywhere\n revert(\"Liquidity error\");\n }\n }\n }\n\n if (_minimumUnitAmount > 0) {\n uint256 unitTotal = 0;\n for (uint256 i = 0; i < outputs.length; ++i) {\n unitTotal += _toUnits(outputs[i], allAssets[i]);\n }\n require(\n unitTotal >= _minimumUnitAmount,\n \"Redeem amount lower than minimum\"\n );\n }\n\n oUSD.burn(msg.sender, _amount);\n\n _postRedeem(_amount);\n }\n\n function _postRedeem(uint256 _amount) internal {\n // Until we can prove that we won't affect the prices of our assets\n // by withdrawing them, this should be here.\n // It's possible that a strategy was off on its asset total, perhaps\n // a reward token sold for more or for less than anticipated.\n uint256 totalUnits = 0;\n if (_amount >= rebaseThreshold && !rebasePaused) {\n totalUnits = _rebase();\n } else {\n totalUnits = _totalValue();\n }\n\n // Check that the OTokens are backed by enough assets\n if (maxSupplyDiff > 0) {\n // If there are more outstanding withdrawal requests than assets in the vault and strategies\n // then the available assets will be negative and totalUnits will be rounded up to zero.\n // As we don't know the exact shortfall amount, we will reject all redeem and withdrawals\n require(totalUnits > 0, \"Too many outstanding requests\");\n\n // Allow a max difference of maxSupplyDiff% between\n // backing assets value and OUSD total supply\n uint256 diff = oUSD.totalSupply().divPrecisely(totalUnits);\n require(\n (diff > 1e18 ? diff - 1e18 : 1e18 - diff) <= maxSupplyDiff,\n \"Backing supply liquidity error\"\n );\n }\n }\n\n /**\n * @notice Burn OTokens for Metapool Strategy\n * @param _amount Amount of OUSD to burn\n *\n * @dev Notice: can't use `nonReentrant` modifier since the `redeem` function could\n * require withdrawal on `ConvexOUSDMetaStrategy` and that one can call `burnForStrategy`\n * while the execution of the `redeem` has not yet completed -> causing a `nonReentrant` collision.\n *\n * Also important to understand is that this is a limitation imposed by the test suite.\n * Production / mainnet contracts should never be configured in a way where mint/redeem functions\n * that are moving funds between the Vault and end user wallets can influence strategies\n * utilizing this function.\n */\n function burnForStrategy(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n onlyOusdMetaStrategy\n {\n require(_amount < MAX_INT, \"Amount too high\");\n\n emit Redeem(msg.sender, _amount);\n\n // safe to cast because of the require check at the beginning of the function\n netOusdMintedForStrategy -= int256(_amount);\n\n require(\n abs(netOusdMintedForStrategy) < netOusdMintForStrategyThreshold,\n \"Attempting to burn too much OUSD.\"\n );\n\n // Burn OTokens\n oUSD.burn(msg.sender, _amount);\n }\n\n /**\n * @notice Allocate unallocated funds on Vault to strategies.\n **/\n function allocate() external virtual whenNotCapitalPaused nonReentrant {\n _allocate();\n }\n\n /**\n * @dev Allocate unallocated funds on Vault to strategies.\n **/\n function _allocate() internal virtual {\n uint256 vaultValue = _totalValueInVault();\n // Nothing in vault to allocate\n if (vaultValue == 0) return;\n uint256 strategiesValue = _totalValueInStrategies();\n // We have a method that does the same as this, gas optimisation\n uint256 calculatedTotalValue = vaultValue + strategiesValue;\n\n // We want to maintain a buffer on the Vault so calculate a percentage\n // modifier to multiply each amount being allocated by to enforce the\n // vault buffer\n uint256 vaultBufferModifier;\n if (strategiesValue == 0) {\n // Nothing in Strategies, allocate 100% minus the vault buffer to\n // strategies\n vaultBufferModifier = uint256(1e18) - vaultBuffer;\n } else {\n vaultBufferModifier =\n (vaultBuffer * calculatedTotalValue) /\n vaultValue;\n if (1e18 > vaultBufferModifier) {\n // E.g. 1e18 - (1e17 * 10e18)/5e18 = 8e17\n // (5e18 * 8e17) / 1e18 = 4e18 allocated from Vault\n vaultBufferModifier = uint256(1e18) - vaultBufferModifier;\n } else {\n // We need to let the buffer fill\n return;\n }\n }\n if (vaultBufferModifier == 0) return;\n\n // Iterate over all assets in the Vault and allocate to the appropriate\n // strategy\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(allAssets[i]);\n uint256 assetBalance = asset.balanceOf(address(this));\n // No balance, nothing to do here\n if (assetBalance == 0) continue;\n\n // Multiply the balance by the vault buffer modifier and truncate\n // to the scale of the asset decimals\n uint256 allocateAmount = assetBalance.mulTruncate(\n vaultBufferModifier\n );\n\n address depositStrategyAddr = assetDefaultStrategies[\n address(asset)\n ];\n\n if (depositStrategyAddr != address(0) && allocateAmount > 0) {\n IStrategy strategy = IStrategy(depositStrategyAddr);\n // Transfer asset to Strategy and call deposit method to\n // mint or take required action\n asset.safeTransfer(address(strategy), allocateAmount);\n strategy.deposit(address(asset), allocateAmount);\n emit AssetAllocated(\n address(asset),\n depositStrategyAddr,\n allocateAmount\n );\n }\n }\n }\n\n /**\n * @notice Calculate the total value of assets held by the Vault and all\n * strategies and update the supply of OTokens.\n */\n function rebase() external virtual nonReentrant {\n _rebase();\n }\n\n /**\n * @dev Calculate the total value of assets held by the Vault and all\n * strategies and update the supply of OTokens, optionally sending a\n * portion of the yield to the trustee.\n * @return totalUnits Total balance of Vault in units\n */\n function _rebase() internal whenNotRebasePaused returns (uint256) {\n uint256 supply = oUSD.totalSupply();\n uint256 vaultValue = _totalValue();\n // If no supply yet, do not rebase\n if (supply == 0) {\n return vaultValue;\n }\n\n // Calculate yield and new supply\n (uint256 yield, uint256 targetRate) = _nextYield(supply, vaultValue);\n uint256 newSupply = supply + yield;\n // Only rebase upwards and if we have enough backing funds\n if (newSupply <= supply || newSupply > vaultValue) {\n return vaultValue;\n }\n\n rebasePerSecondTarget = uint64(_min(targetRate, type(uint64).max));\n lastRebase = uint64(block.timestamp); // Intentional cast\n\n // Fee collection on yield\n address _trusteeAddress = trusteeAddress; // gas savings\n uint256 fee = 0;\n if (_trusteeAddress != address(0)) {\n fee = (yield * trusteeFeeBps) / 1e4;\n if (fee > 0) {\n require(fee < yield, \"Fee must not be greater than yield\");\n oUSD.mint(_trusteeAddress, fee);\n }\n }\n emit YieldDistribution(_trusteeAddress, yield, fee);\n\n // Only ratchet OToken supply upwards\n // Final check uses latest totalSupply\n if (newSupply > oUSD.totalSupply()) {\n oUSD.changeSupply(newSupply);\n }\n return vaultValue;\n }\n\n /**\n * @notice Calculates the amount that would rebase at next rebase.\n * This is before any fees.\n * @return yield amount of expected yield\n */\n function previewYield() external view returns (uint256 yield) {\n (yield, ) = _nextYield(oUSD.totalSupply(), _totalValue());\n return yield;\n }\n\n function _nextYield(uint256 supply, uint256 vaultValue)\n internal\n view\n virtual\n returns (uint256 yield, uint256 targetRate)\n {\n uint256 nonRebasing = oUSD.nonRebasingSupply();\n uint256 rebasing = supply - nonRebasing;\n uint256 elapsed = block.timestamp - lastRebase;\n targetRate = rebasePerSecondTarget;\n\n if (\n elapsed == 0 || // Yield only once per block.\n rebasing == 0 || // No yield if there are no rebasing tokens to give it to.\n supply > vaultValue || // No yield if we do not have yield to give.\n block.timestamp >= type(uint64).max // No yield if we are too far in the future to calculate it correctly.\n ) {\n return (0, targetRate);\n }\n\n // Start with the full difference available\n yield = vaultValue - supply;\n\n // Cap via optional automatic duration smoothing\n uint256 _dripDuration = dripDuration;\n if (_dripDuration > 1) {\n // If we are able to sustain an increased drip rate for\n // double the duration, then increase the target drip rate\n targetRate = _max(targetRate, yield / (_dripDuration * 2));\n // If we cannot sustain the target rate any more,\n // then rebase what we can, and reduce the target\n targetRate = _min(targetRate, yield / _dripDuration);\n // drip at the new target rate\n yield = _min(yield, targetRate * elapsed);\n }\n\n // Cap per second. elapsed is not 1e18 denominated\n yield = _min(yield, (rebasing * elapsed * rebasePerSecondMax) / 1e18);\n\n // Cap at a hard max per rebase, to avoid long durations resulting in huge rebases\n yield = _min(yield, (rebasing * MAX_REBASE) / 1e18);\n\n return (yield, targetRate);\n }\n\n /**\n * @notice Determine the total value of assets held by the vault and its\n * strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function totalValue() external view virtual returns (uint256 value) {\n value = _totalValue();\n }\n\n /**\n * @dev Internal Calculate the total value of the assets held by the\n * vault and its strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValue() internal view virtual returns (uint256 value) {\n return _totalValueInVault() + _totalValueInStrategies();\n }\n\n /**\n * @dev Internal to calculate total value of all assets held in Vault.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInVault()\n internal\n view\n virtual\n returns (uint256 value)\n {\n uint256 assetCount = allAssets.length;\n for (uint256 y; y < assetCount; ++y) {\n address assetAddr = allAssets[y];\n uint256 balance = IERC20(assetAddr).balanceOf(address(this));\n if (balance > 0) {\n value += _toUnits(balance, assetAddr);\n }\n }\n }\n\n /**\n * @dev Internal to calculate total value of all assets held in Strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInStrategies() internal view returns (uint256 value) {\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n value = value + _totalValueInStrategy(allStrategies[i]);\n }\n }\n\n /**\n * @dev Internal to calculate total value of all assets held by strategy.\n * @param _strategyAddr Address of the strategy\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInStrategy(address _strategyAddr)\n internal\n view\n returns (uint256 value)\n {\n IStrategy strategy = IStrategy(_strategyAddr);\n uint256 assetCount = allAssets.length;\n for (uint256 y; y < assetCount; ++y) {\n address assetAddr = allAssets[y];\n if (strategy.supportsAsset(assetAddr)) {\n uint256 balance = strategy.checkBalance(assetAddr);\n if (balance > 0) {\n value += _toUnits(balance, assetAddr);\n }\n }\n }\n }\n\n /**\n * @notice Get the balance of an asset held in Vault and all strategies.\n * @param _asset Address of asset\n * @return uint256 Balance of asset in decimals of asset\n */\n function checkBalance(address _asset) external view returns (uint256) {\n return _checkBalance(_asset);\n }\n\n /**\n * @notice Get the balance of an asset held in Vault and all strategies.\n * @param _asset Address of asset\n * @return balance Balance of asset in decimals of asset\n */\n function _checkBalance(address _asset)\n internal\n view\n virtual\n returns (uint256 balance)\n {\n IERC20 asset = IERC20(_asset);\n balance = asset.balanceOf(address(this));\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n IStrategy strategy = IStrategy(allStrategies[i]);\n if (strategy.supportsAsset(_asset)) {\n balance = balance + strategy.checkBalance(_asset);\n }\n }\n }\n\n /**\n * @notice Calculate the outputs for a redeem function, i.e. the mix of\n * coins that will be returned\n */\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory)\n {\n return _calculateRedeemOutputs(_amount);\n }\n\n /**\n * @dev Calculate the outputs for a redeem function, i.e. the mix of\n * coins that will be returned.\n * @return outputs Array of amounts respective to the supported assets\n */\n function _calculateRedeemOutputs(uint256 _amount)\n internal\n view\n virtual\n returns (uint256[] memory outputs)\n {\n // We always give out coins in proportion to how many we have,\n // Now if all coins were the same value, this math would easy,\n // just take the percentage of each coin, and multiply by the\n // value to be given out. But if coins are worth more than $1,\n // then we would end up handing out too many coins. We need to\n // adjust by the total value of coins.\n //\n // To do this, we total up the value of our coins, by their\n // percentages. Then divide what we would otherwise give out by\n // this number.\n //\n // Let say we have 100 DAI at $1.06 and 200 USDT at $1.00.\n // So for every 1 DAI we give out, we'll be handing out 2 USDT\n // Our total output ratio is: 33% * 1.06 + 66% * 1.00 = 1.02\n //\n // So when calculating the output, we take the percentage of\n // each coin, times the desired output value, divided by the\n // totalOutputRatio.\n //\n // For example, withdrawing: 30 OUSD:\n // DAI 33% * 30 / 1.02 = 9.80 DAI\n // USDT = 66 % * 30 / 1.02 = 19.60 USDT\n //\n // Checking these numbers:\n // 9.80 DAI * 1.06 = $10.40\n // 19.60 USDT * 1.00 = $19.60\n //\n // And so the user gets $10.40 + $19.60 = $30 worth of value.\n\n uint256 assetCount = allAssets.length;\n uint256[] memory assetUnits = new uint256[](assetCount);\n uint256[] memory assetBalances = new uint256[](assetCount);\n outputs = new uint256[](assetCount);\n\n // Calculate redeem fee\n if (redeemFeeBps > 0) {\n uint256 redeemFee = _amount.mulTruncateScale(redeemFeeBps, 1e4);\n _amount = _amount - redeemFee;\n }\n\n // Calculate assets balances and decimals once,\n // for a large gas savings.\n uint256 totalUnits = 0;\n for (uint256 i = 0; i < assetCount; ++i) {\n address assetAddr = allAssets[i];\n uint256 balance = _checkBalance(assetAddr);\n assetBalances[i] = balance;\n assetUnits[i] = _toUnits(balance, assetAddr);\n totalUnits = totalUnits + assetUnits[i];\n }\n // Calculate totalOutputRatio\n uint256 totalOutputRatio = 0;\n for (uint256 i = 0; i < assetCount; ++i) {\n uint256 unitPrice = _toUnitPrice(allAssets[i], false);\n uint256 ratio = (assetUnits[i] * unitPrice) / totalUnits;\n totalOutputRatio = totalOutputRatio + ratio;\n }\n // Calculate final outputs\n uint256 factor = _amount.divPrecisely(totalOutputRatio);\n for (uint256 i = 0; i < assetCount; ++i) {\n outputs[i] = (assetBalances[i] * factor) / totalUnits;\n }\n }\n\n /***************************************\n Pricing\n ****************************************/\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * Never goes above 1, since that is how we price mints.\n * @param asset address of the asset\n * @return price uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed\n */\n function priceUnitMint(address asset)\n external\n view\n returns (uint256 price)\n {\n /* need to supply 1 asset unit in asset's decimals and can not just hard-code\n * to 1e18 and ignore calling `_toUnits` since we need to consider assets\n * with the exchange rate\n */\n uint256 units = _toUnits(\n uint256(1e18).scaleBy(_getDecimals(asset), 18),\n asset\n );\n price = (_toUnitPrice(asset, true) * units) / 1e18;\n }\n\n /**\n * @notice Returns the total price in 18 digit unit for a given asset.\n * Never goes below 1, since that is how we price redeems\n * @param asset Address of the asset\n * @return price uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed\n */\n function priceUnitRedeem(address asset)\n external\n view\n returns (uint256 price)\n {\n /* need to supply 1 asset unit in asset's decimals and can not just hard-code\n * to 1e18 and ignore calling `_toUnits` since we need to consider assets\n * with the exchange rate\n */\n uint256 units = _toUnits(\n uint256(1e18).scaleBy(_getDecimals(asset), 18),\n asset\n );\n price = (_toUnitPrice(asset, false) * units) / 1e18;\n }\n\n /***************************************\n Utils\n ****************************************/\n\n /**\n * @dev Convert a quantity of a token into 1e18 fixed decimal \"units\"\n * in the underlying base (USD/ETH) used by the vault.\n * Price is not taken into account, only quantity.\n *\n * Examples of this conversion:\n *\n * - 1e18 DAI becomes 1e18 units (same decimals)\n * - 1e6 USDC becomes 1e18 units (decimal conversion)\n * - 1e18 rETH becomes 1.2e18 units (exchange rate conversion)\n *\n * @param _raw Quantity of asset\n * @param _asset Core Asset address\n * @return value 1e18 normalized quantity of units\n */\n function _toUnits(uint256 _raw, address _asset)\n internal\n view\n returns (uint256)\n {\n UnitConversion conversion = assets[_asset].unitConversion;\n if (conversion == UnitConversion.DECIMALS) {\n return _raw.scaleBy(18, _getDecimals(_asset));\n } else if (conversion == UnitConversion.GETEXCHANGERATE) {\n uint256 exchangeRate = IGetExchangeRateToken(_asset)\n .getExchangeRate();\n return (_raw * exchangeRate) / 1e18;\n } else {\n revert(\"Unsupported conversion type\");\n }\n }\n\n /**\n * @dev Returns asset's unit price accounting for different asset types\n * and takes into account the context in which that price exists -\n * - mint or redeem.\n *\n * Note: since we are returning the price of the unit and not the one of the\n * asset (see comment above how 1 rETH exchanges for 1.2 units) we need\n * to make the Oracle price adjustment as well since we are pricing the\n * units and not the assets.\n *\n * The price also snaps to a \"full unit price\" in case a mint or redeem\n * action would be unfavourable to the protocol.\n *\n */\n function _toUnitPrice(address _asset, bool isMint)\n internal\n view\n returns (uint256 price)\n {\n UnitConversion conversion = assets[_asset].unitConversion;\n price = IOracle(priceProvider).price(_asset);\n\n if (conversion == UnitConversion.GETEXCHANGERATE) {\n uint256 exchangeRate = IGetExchangeRateToken(_asset)\n .getExchangeRate();\n price = (price * 1e18) / exchangeRate;\n } else if (conversion != UnitConversion.DECIMALS) {\n revert(\"Unsupported conversion type\");\n }\n\n /* At this stage the price is already adjusted to the unit\n * so the price checks are agnostic to underlying asset being\n * pegged to a USD or to an ETH or having a custom exchange rate.\n */\n require(price <= MAX_UNIT_PRICE_DRIFT, \"Vault: Price exceeds max\");\n require(price >= MIN_UNIT_PRICE_DRIFT, \"Vault: Price under min\");\n\n if (isMint) {\n /* Never price a normalized unit price for more than one\n * unit of OETH/OUSD when minting.\n */\n if (price > 1e18) {\n price = 1e18;\n }\n require(price >= MINT_MINIMUM_UNIT_PRICE, \"Asset price below peg\");\n } else {\n /* Never give out more than 1 normalized unit amount of assets\n * for one unit of OETH/OUSD when redeeming.\n */\n if (price < 1e18) {\n price = 1e18;\n }\n }\n }\n\n /**\n * @dev Get the number of decimals of a token asset\n * @param _asset Address of the asset\n * @return decimals number of decimals\n */\n function _getDecimals(address _asset)\n internal\n view\n returns (uint256 decimals)\n {\n decimals = assets[_asset].decimals;\n require(decimals > 0, \"Decimals not cached\");\n }\n\n /**\n * @notice Return the number of assets supported by the Vault.\n */\n function getAssetCount() public view returns (uint256) {\n return allAssets.length;\n }\n\n /**\n * @notice Gets the vault configuration of a supported asset.\n * @param _asset Address of the token asset\n */\n function getAssetConfig(address _asset)\n public\n view\n returns (Asset memory config)\n {\n config = assets[_asset];\n }\n\n /**\n * @notice Return all vault asset addresses in order\n */\n function getAllAssets() external view returns (address[] memory) {\n return allAssets;\n }\n\n /**\n * @notice Return the number of strategies active on the Vault.\n */\n function getStrategyCount() external view returns (uint256) {\n return allStrategies.length;\n }\n\n /**\n * @notice Return the array of all strategies\n */\n function getAllStrategies() external view returns (address[] memory) {\n return allStrategies;\n }\n\n /**\n * @notice Returns whether the vault supports the asset\n * @param _asset address of the asset\n * @return true if supported\n */\n function isSupportedAsset(address _asset) external view returns (bool) {\n return assets[_asset].isSupported;\n }\n\n function ADMIN_IMPLEMENTATION() external view returns (address adminImpl) {\n bytes32 slot = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n adminImpl := sload(slot)\n }\n }\n\n /**\n * @dev Falldown to the admin implementation\n * @notice This is a catch all for all functions not declared in core\n */\n // solhint-disable-next-line no-complex-fallback\n fallback() external {\n bytes32 slot = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(\n gas(),\n sload(slot),\n 0,\n calldatasize(),\n 0,\n 0\n )\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n function abs(int256 x) private pure returns (uint256) {\n require(x < int256(MAX_INT), \"Amount too high\");\n return x >= 0 ? uint256(x) : uint256(-x);\n }\n\n function _min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n function _max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a > b ? a : b;\n }\n}\n" + }, + "contracts/vault/VaultInitializer.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultInitializer contract\n * @notice The Vault contract initializes the vault.\n * @author Origin Protocol Inc\n */\n\nimport \"./VaultStorage.sol\";\n\ncontract VaultInitializer is VaultStorage {\n function initialize(address _priceProvider, address _oToken)\n external\n onlyGovernor\n initializer\n {\n require(_priceProvider != address(0), \"PriceProvider address is zero\");\n require(_oToken != address(0), \"oToken address is zero\");\n\n oUSD = OUSD(_oToken);\n\n priceProvider = _priceProvider;\n\n rebasePaused = false;\n capitalPaused = true;\n\n // Initial redeem fee of 0 basis points\n redeemFeeBps = 0;\n // Initial Vault buffer of 0%\n vaultBuffer = 0;\n // Initial allocate threshold of 25,000 OUSD\n autoAllocateThreshold = 25000e18;\n // Threshold for rebasing\n rebaseThreshold = 1000e18;\n // Initialize all strategies\n allStrategies = new address[](0);\n // Start with drip duration disabled\n dripDuration = 1;\n }\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + }, + "contracts/zapper/WOETHCCIPZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IRouterClient } from \"@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol\";\nimport { Client } from \"@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol\";\n// solhint-disable-next-line max-line-length\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"./../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IOETHZapper } from \"./../interfaces/IOETHZapper.sol\";\n\n/**\n * @title WOETH CCIP Zapper Contract\n * @notice Helps to directly convert ETH on mainnet into WOETH on L2s.\n * @author Origin Protocol Inc\n */\n\ncontract WOETHCCIPZapper {\n /**\n * @dev Event emitted when a zap occurs\n * @param messageId Unique message identifier for each zap\n * @param sender Address initiating the zap\n * @param recipient Recipient address at destination chain\n * @param amount Amount of ETH zapped\n */\n event Zap(\n bytes32 indexed messageId,\n address sender,\n address recipient,\n uint256 amount\n );\n\n // @dev Thrown when Zap amount is less than fee.\n error AmountLessThanFee();\n\n /**\n * @dev The destination chain selector\n */\n uint64 public immutable destinationChainSelector;\n\n /**\n * @dev The WOETH source chain (Mainnet)\n */\n IERC4626 public immutable woethOnSourceChain;\n\n /**\n * @dev The WOETH destination chain (Arbitrum)\n */\n IERC20 public immutable woethOnDestinationChain;\n\n /**\n * @dev The OETH zapper contract address\n */\n IOETHZapper public immutable oethZapper;\n\n /**\n * @dev The CCIP router contract address\n */\n IRouterClient public immutable ccipRouter;\n\n /**\n * @dev The OETH token contract address\n */\n IERC20 public immutable oeth;\n\n constructor(\n address _ccipRouter,\n uint64 _destinationChainSelector,\n IERC4626 _woethOnSourceChain,\n IERC20 _woethOnDestinationChain,\n IOETHZapper _oethZapper,\n IERC20 _oeth\n ) {\n ccipRouter = IRouterClient(_ccipRouter);\n destinationChainSelector = _destinationChainSelector;\n woethOnSourceChain = _woethOnSourceChain;\n woethOnDestinationChain = _woethOnDestinationChain;\n oethZapper = _oethZapper;\n oeth = _oeth;\n\n // Max allowance for Router and WOETH contracts\n _oeth.approve(address(_woethOnSourceChain), type(uint256).max); // for wrapping\n _woethOnSourceChain.approve(address(_ccipRouter), type(uint256).max); // for zapping\n }\n\n /**\n * @notice Accepts ETH, zaps for OETH, wraps it for WOETH and sends it to the destination chain (arbitrum)\n * @param receiver The address of the EOA on the destination chain\n * @return messageId The ID of the message that was sent\n */\n function zap(address receiver)\n external\n payable\n returns (bytes32 messageId)\n {\n return _zap(receiver, msg.value);\n }\n\n /**\n * @notice Used to estimate fee for CCIP transaction\n * @param amount The amount of ETH to be zapped\n * @param receiver The address of the EOA on the destination chain\n * @return feeAmount The CCIP tx fee in ETH.\n */\n\n function getFee(uint256 amount, address receiver)\n public\n view\n returns (uint256 feeAmount)\n {\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(woethOnSourceChain),\n amount: amount\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({\n receiver: abi.encode(receiver), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n feeAmount = ccipRouter.getFee(destinationChainSelector, message);\n }\n\n /**\n * @dev Deposit ETH and receive WOETH in L2.\n * @dev Note that the WOETH will be sent to the msg.sender at destination chain as well.\n */\n receive() external payable {\n _zap(msg.sender, msg.value);\n }\n\n function _zap(address receiver, uint256 amount)\n internal\n returns (bytes32 messageId)\n {\n // Estimate fee for zapping.\n uint256 feeAmount = getFee(amount, receiver);\n if (amount < feeAmount) {\n revert AmountLessThanFee();\n }\n\n // Convert only the msg.value - fees amount to WOETH.\n amount -= feeAmount;\n\n // 1.) Zap for OETH\n uint256 oethReceived = oethZapper.deposit{ value: amount }();\n\n // 2.) Wrap the received woeth\n uint256 woethReceived = woethOnSourceChain.deposit(\n oethReceived,\n address(this)\n );\n\n // 3.) Setup params for CCIP transfer\n\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(woethOnSourceChain),\n amount: woethReceived\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({\n receiver: abi.encode(receiver), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n // See: https://docs.chain.link/ccip/best-practices#setting-gaslimit\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n // ZAP ϟ\n //slither-disable-next-line arbitrary-send-eth\n messageId = ccipRouter.ccipSend{ value: feeAmount }(\n destinationChainSelector,\n message\n );\n\n // Emit Zap event with message details\n emit Zap(messageId, msg.sender, receiver, amount);\n\n // Return the message ID\n return messageId;\n }\n}\n" + }, + "hardhat/console.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.4.22 <0.9.0;\n\nlibrary console {\n address constant CONSOLE_ADDRESS =\n 0x000000000000000000636F6e736F6c652e6c6f67;\n\n function _sendLogPayloadImplementation(bytes memory payload) internal view {\n address consoleAddress = CONSOLE_ADDRESS;\n /// @solidity memory-safe-assembly\n assembly {\n pop(\n staticcall(\n gas(),\n consoleAddress,\n add(payload, 32),\n mload(payload),\n 0,\n 0\n )\n )\n }\n }\n\n function _castToPure(\n function(bytes memory) internal view fnIn\n ) internal pure returns (function(bytes memory) pure fnOut) {\n assembly {\n fnOut := fnIn\n }\n }\n\n function _sendLogPayload(bytes memory payload) internal pure {\n _castToPure(_sendLogPayloadImplementation)(payload);\n }\n\n function log() internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log()\"));\n }\n\n function logInt(int256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(int256)\", p0));\n }\n\n function logUint(uint256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256)\", p0));\n }\n\n function logString(string memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n }\n\n function logBool(bool p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n }\n\n function logAddress(address p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n }\n\n function logBytes(bytes memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes)\", p0));\n }\n\n function logBytes1(bytes1 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes1)\", p0));\n }\n\n function logBytes2(bytes2 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes2)\", p0));\n }\n\n function logBytes3(bytes3 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes3)\", p0));\n }\n\n function logBytes4(bytes4 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes4)\", p0));\n }\n\n function logBytes5(bytes5 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes5)\", p0));\n }\n\n function logBytes6(bytes6 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes6)\", p0));\n }\n\n function logBytes7(bytes7 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes7)\", p0));\n }\n\n function logBytes8(bytes8 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes8)\", p0));\n }\n\n function logBytes9(bytes9 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes9)\", p0));\n }\n\n function logBytes10(bytes10 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes10)\", p0));\n }\n\n function logBytes11(bytes11 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes11)\", p0));\n }\n\n function logBytes12(bytes12 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes12)\", p0));\n }\n\n function logBytes13(bytes13 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes13)\", p0));\n }\n\n function logBytes14(bytes14 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes14)\", p0));\n }\n\n function logBytes15(bytes15 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes15)\", p0));\n }\n\n function logBytes16(bytes16 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes16)\", p0));\n }\n\n function logBytes17(bytes17 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes17)\", p0));\n }\n\n function logBytes18(bytes18 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes18)\", p0));\n }\n\n function logBytes19(bytes19 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes19)\", p0));\n }\n\n function logBytes20(bytes20 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes20)\", p0));\n }\n\n function logBytes21(bytes21 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes21)\", p0));\n }\n\n function logBytes22(bytes22 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes22)\", p0));\n }\n\n function logBytes23(bytes23 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes23)\", p0));\n }\n\n function logBytes24(bytes24 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes24)\", p0));\n }\n\n function logBytes25(bytes25 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes25)\", p0));\n }\n\n function logBytes26(bytes26 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes26)\", p0));\n }\n\n function logBytes27(bytes27 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes27)\", p0));\n }\n\n function logBytes28(bytes28 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes28)\", p0));\n }\n\n function logBytes29(bytes29 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes29)\", p0));\n }\n\n function logBytes30(bytes30 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes30)\", p0));\n }\n\n function logBytes31(bytes31 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes31)\", p0));\n }\n\n function logBytes32(bytes32 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes32)\", p0));\n }\n\n function log(uint256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256)\", p0));\n }\n\n function log(string memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n }\n\n function log(bool p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n }\n\n function log(address p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n }\n\n function log(uint256 p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256)\", p0, p1));\n }\n\n function log(uint256 p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string)\", p0, p1));\n }\n\n function log(uint256 p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool)\", p0, p1));\n }\n\n function log(uint256 p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address)\", p0, p1));\n }\n\n function log(string memory p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256)\", p0, p1));\n }\n\n function log(string memory p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string)\", p0, p1));\n }\n\n function log(string memory p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool)\", p0, p1));\n }\n\n function log(string memory p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address)\", p0, p1));\n }\n\n function log(bool p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256)\", p0, p1));\n }\n\n function log(bool p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string)\", p0, p1));\n }\n\n function log(bool p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool)\", p0, p1));\n }\n\n function log(bool p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address)\", p0, p1));\n }\n\n function log(address p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256)\", p0, p1));\n }\n\n function log(address p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string)\", p0, p1));\n }\n\n function log(address p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool)\", p0, p1));\n }\n\n function log(address p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address)\", p0, p1));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,address)\", p0, p1, p2, p3));\n }\n}\n" + }, + "lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC4626 } from \"../../../../interfaces/IERC4626.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\n// From Open Zeppelin draft PR commit:\n// fac43034dca85ff539db3fc8aa2a7084b843d454\n// https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3171\n\nabstract contract ERC4626 is ERC20, IERC4626 {\n IERC20Metadata private immutable _asset;\n\n constructor(IERC20Metadata __asset) {\n _asset = __asset;\n }\n\n /** @dev See {IERC4262-asset} */\n function asset() public view virtual override returns (address) {\n return address(_asset);\n }\n\n /** @dev See {IERC4262-totalAssets} */\n function totalAssets() public view virtual override returns (uint256) {\n return _asset.balanceOf(address(this));\n }\n\n /**\n * @dev See {IERC4262-convertToShares}\n *\n * Will revert if asserts > 0, totalSupply > 0 and totalAssets = 0. That corresponds to a case where any asset\n * would represent an infinite amout of shares.\n */\n function convertToShares(uint256 assets) public view virtual override returns (uint256 shares) {\n uint256 supply = totalSupply();\n\n return\n (assets == 0 || supply == 0)\n ? (assets * 10**decimals()) / 10**_asset.decimals()\n : (assets * supply) / totalAssets();\n }\n\n /** @dev See {IERC4262-convertToAssets} */\n function convertToAssets(uint256 shares) public view virtual override returns (uint256 assets) {\n uint256 supply = totalSupply();\n\n return (supply == 0) ? (shares * 10**_asset.decimals()) / 10**decimals() : (shares * totalAssets()) / supply;\n }\n\n /** @dev See {IERC4262-maxDeposit} */\n function maxDeposit(address) public view virtual override returns (uint256) {\n return type(uint256).max;\n }\n\n /** @dev See {IERC4262-maxMint} */\n function maxMint(address) public view virtual override returns (uint256) {\n return type(uint256).max;\n }\n\n /** @dev See {IERC4262-maxWithdraw} */\n function maxWithdraw(address owner) public view virtual override returns (uint256) {\n return convertToAssets(balanceOf(owner));\n }\n\n /** @dev See {IERC4262-maxRedeem} */\n function maxRedeem(address owner) public view virtual override returns (uint256) {\n return balanceOf(owner);\n }\n\n /** @dev See {IERC4262-previewDeposit} */\n function previewDeposit(uint256 assets) public view virtual override returns (uint256) {\n return convertToShares(assets);\n }\n\n /** @dev See {IERC4262-previewMint} */\n function previewMint(uint256 shares) public view virtual override returns (uint256) {\n uint256 assets = convertToAssets(shares);\n return assets + (convertToShares(assets) < shares ? 1 : 0);\n }\n\n /** @dev See {IERC4262-previewWithdraw} */\n function previewWithdraw(uint256 assets) public view virtual override returns (uint256) {\n uint256 shares = convertToShares(assets);\n return shares + (convertToAssets(shares) < assets ? 1 : 0);\n }\n\n /** @dev See {IERC4262-previewRedeem} */\n function previewRedeem(uint256 shares) public view virtual override returns (uint256) {\n return convertToAssets(shares);\n }\n\n /** @dev See {IERC4262-deposit} */\n function deposit(uint256 assets, address receiver) public virtual override returns (uint256) {\n require(assets <= maxDeposit(receiver), \"ERC4626: deposit more then max\");\n\n address caller = _msgSender();\n uint256 shares = previewDeposit(assets);\n\n // if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through\n // the tokensToSend hook, so we need to transfer before we mint to keep the invariants.\n SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);\n _mint(receiver, shares);\n\n emit Deposit(caller, receiver, assets, shares);\n\n return shares;\n }\n\n /** @dev See {IERC4262-mint} */\n function mint(uint256 shares, address receiver) public virtual override returns (uint256) {\n require(shares <= maxMint(receiver), \"ERC4626: mint more then max\");\n\n address caller = _msgSender();\n uint256 assets = previewMint(shares);\n\n // if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through\n // the tokensToSend hook, so we need to transfer before we mint to keep the invariants.\n SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);\n _mint(receiver, shares);\n\n emit Deposit(caller, receiver, assets, shares);\n\n return assets;\n }\n\n /** @dev See {IERC4262-withdraw} */\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) public virtual override returns (uint256) {\n require(assets <= maxWithdraw(owner), \"ERC4626: withdraw more then max\");\n\n address caller = _msgSender();\n uint256 shares = previewWithdraw(assets);\n\n if (caller != owner) {\n _spendAllowance(owner, caller, shares);\n }\n\n // if _asset is ERC777, transfer can call reenter AFTER the transfer happens through\n // the tokensReceived hook, so we need to transfer after we burn to keep the invariants.\n _burn(owner, shares);\n SafeERC20.safeTransfer(_asset, receiver, assets);\n\n emit Withdraw(caller, receiver, owner, assets, shares);\n\n return shares;\n }\n\n /** @dev See {IERC4262-redeem} */\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) public virtual override returns (uint256) {\n require(shares <= maxRedeem(owner), \"ERC4626: redeem more then max\");\n\n address caller = _msgSender();\n uint256 assets = previewRedeem(shares);\n\n if (caller != owner) {\n _spendAllowance(owner, caller, shares);\n }\n\n // if _asset is ERC777, transfer can call reenter AFTER the transfer happens through\n // the tokensReceived hook, so we need to transfer after we burn to keep the invariants.\n _burn(owner, shares);\n SafeERC20.safeTransfer(_asset, receiver, assets);\n\n emit Withdraw(caller, receiver, owner, assets, shares);\n\n return assets;\n }\n\n // Included here, since this method was not yet present in\n // the version of Open Zeppelin ERC20 code we use.\n function _spendAllowance(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n uint256 currentAllowance = allowance(owner, spender);\n if (currentAllowance != type(uint256).max) {\n require(currentAllowance >= amount, \"ERC20: insufficient allowance\");\n unchecked {\n _approve(owner, spender, currentAllowance - amount);\n }\n }\n }\n}" + }, + "lib/openzeppelin/interfaces/IERC4626.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IERC4626 is IERC20, IERC20Metadata {\n event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);\n\n event Withdraw(\n address indexed caller,\n address indexed receiver,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n\n /**\n * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.\n *\n * - MUST be an ERC-20 token contract.\n * - MUST NOT revert.\n */\n function asset() external view returns (address assetTokenAddress);\n\n /**\n * @dev Returns the total amount of the underlying asset that is “managed” by Vault.\n *\n * - SHOULD include any compounding that occurs from yield.\n * - MUST be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT revert.\n */\n function totalAssets() external view returns (uint256 totalManagedAssets);\n\n /**\n * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal\n * scenario where all the conditions are met.\n *\n * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT show any variations depending on the caller.\n * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n * - MUST NOT revert.\n *\n * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n * from.\n */\n function convertToShares(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal\n * scenario where all the conditions are met.\n *\n * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT show any variations depending on the caller.\n * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n * - MUST NOT revert.\n *\n * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n * from.\n */\n function convertToAssets(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,\n * through a deposit call.\n *\n * - MUST return a limited value if receiver is subject to some deposit limit.\n * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.\n * - MUST NOT revert.\n */\n function maxDeposit(address receiver) external view returns (uint256 maxAssets);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given\n * current on-chain conditions.\n *\n * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit\n * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called\n * in the same transaction.\n * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the\n * deposit would be accepted, regardless if the user has enough tokens approved, etc.\n * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by depositing.\n */\n function previewDeposit(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.\n *\n * - MUST emit the Deposit event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * deposit execution, and are accounted for during deposit.\n * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not\n * approving enough underlying tokens to the Vault contract, etc).\n *\n * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n */\n function deposit(uint256 assets, address receiver) external returns (uint256 shares);\n\n /**\n * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.\n * - MUST return a limited value if receiver is subject to some mint limit.\n * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.\n * - MUST NOT revert.\n */\n function maxMint(address receiver) external view returns (uint256 maxShares);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given\n * current on-chain conditions.\n *\n * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call\n * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the\n * same transaction.\n * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint\n * would be accepted, regardless if the user has enough tokens approved, etc.\n * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by minting.\n */\n function previewMint(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.\n *\n * - MUST emit the Deposit event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint\n * execution, and are accounted for during mint.\n * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not\n * approving enough underlying tokens to the Vault contract, etc).\n *\n * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n */\n function mint(uint256 shares, address receiver) external returns (uint256 assets);\n\n /**\n * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the\n * Vault, through a withdraw call.\n *\n * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n * - MUST NOT revert.\n */\n function maxWithdraw(address owner) external view returns (uint256 maxAssets);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,\n * given current on-chain conditions.\n *\n * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw\n * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if\n * called\n * in the same transaction.\n * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though\n * the withdrawal would be accepted, regardless if the user has enough shares, etc.\n * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by depositing.\n */\n function previewWithdraw(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.\n *\n * - MUST emit the Withdraw event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * withdraw execution, and are accounted for during withdraw.\n * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner\n * not having enough shares, etc).\n *\n * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n * Those methods should be performed separately.\n */\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares);\n\n /**\n * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,\n * through a redeem call.\n *\n * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.\n * - MUST NOT revert.\n */\n function maxRedeem(address owner) external view returns (uint256 maxShares);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,\n * given current on-chain conditions.\n *\n * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call\n * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the\n * same transaction.\n * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the\n * redemption would be accepted, regardless if the user has enough shares, etc.\n * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by redeeming.\n */\n function previewRedeem(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.\n *\n * - MUST emit the Withdraw event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * redeem execution, and are accounted for during redeem.\n * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner\n * not having enough shares, etc).\n *\n * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n * Those methods should be performed separately.\n */\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets);\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.20;\n\nimport {Panic} from \"../Panic.sol\";\nimport {SafeCast} from \"./SafeCast.sol\";\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n enum Rounding {\n Floor, // Toward negative infinity\n Ceil, // Toward positive infinity\n Trunc, // Toward zero\n Expand // Away from zero\n }\n\n /**\n * @dev Return the 512-bit addition of two uint256.\n *\n * The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low.\n */\n function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {\n assembly (\"memory-safe\") {\n low := add(a, b)\n high := lt(low, a)\n }\n }\n\n /**\n * @dev Return the 512-bit multiplication of two uint256.\n *\n * The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low.\n */\n function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {\n // 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use\n // the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n // variables such that product = high * 2²⁵⁶ + low.\n assembly (\"memory-safe\") {\n let mm := mulmod(a, b, not(0))\n low := mul(a, b)\n high := sub(sub(mm, low), lt(mm, low))\n }\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, with a success flag (no overflow).\n */\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a + b;\n success = c >= a;\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).\n */\n function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a - b;\n success = c <= a;\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).\n */\n function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a * b;\n assembly (\"memory-safe\") {\n // Only true when the multiplication doesn't overflow\n // (c / a == b) || (a == 0)\n success := or(eq(div(c, a), b), iszero(a))\n }\n // equivalent to: success ? c : 0\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the division of two unsigned integers, with a success flag (no division by zero).\n */\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n success = b > 0;\n assembly (\"memory-safe\") {\n // The `DIV` opcode returns zero when the denominator is 0.\n result := div(a, b)\n }\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).\n */\n function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n success = b > 0;\n assembly (\"memory-safe\") {\n // The `MOD` opcode returns zero when the denominator is 0.\n result := mod(a, b)\n }\n }\n }\n\n /**\n * @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing.\n */\n function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {\n (bool success, uint256 result) = tryAdd(a, b);\n return ternary(success, result, type(uint256).max);\n }\n\n /**\n * @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.\n */\n function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {\n (, uint256 result) = trySub(a, b);\n return result;\n }\n\n /**\n * @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing.\n */\n function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {\n (bool success, uint256 result) = tryMul(a, b);\n return ternary(success, result, type(uint256).max);\n }\n\n /**\n * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.\n *\n * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.\n * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute\n * one branch when needed, making this function more expensive.\n */\n function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {\n unchecked {\n // branchless ternary works because:\n // b ^ (a ^ b) == a\n // b ^ 0 == b\n return b ^ ((a ^ b) * SafeCast.toUint(condition));\n }\n }\n\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return ternary(a > b, a, b);\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return ternary(a < b, a, b);\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds towards infinity instead\n * of rounding towards zero.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n if (b == 0) {\n // Guarantee the same behavior as in a regular Solidity division.\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n\n // The following calculation ensures accurate ceiling division without overflow.\n // Since a is non-zero, (a - 1) / b will not overflow.\n // The largest possible result occurs when (a - 1) / b is type(uint256).max,\n // but the largest value we can obtain is type(uint256).max - 1, which happens\n // when a = type(uint256).max and b = 1.\n unchecked {\n return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);\n }\n }\n\n /**\n * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or\n * denominator == 0.\n *\n * Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by\n * Uniswap Labs also under MIT license.\n */\n function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {\n unchecked {\n (uint256 high, uint256 low) = mul512(x, y);\n\n // Handle non-overflow cases, 256 by 256 division.\n if (high == 0) {\n // Solidity will revert if denominator == 0, unlike the div opcode on its own.\n // The surrounding unchecked block does not change this fact.\n // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.\n return low / denominator;\n }\n\n // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.\n if (denominator <= high) {\n Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));\n }\n\n ///////////////////////////////////////////////\n // 512 by 256 division.\n ///////////////////////////////////////////////\n\n // Make division exact by subtracting the remainder from [high low].\n uint256 remainder;\n assembly (\"memory-safe\") {\n // Compute remainder using mulmod.\n remainder := mulmod(x, y, denominator)\n\n // Subtract 256 bit number from 512 bit number.\n high := sub(high, gt(remainder, low))\n low := sub(low, remainder)\n }\n\n // Factor powers of two out of denominator and compute largest power of two divisor of denominator.\n // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.\n\n uint256 twos = denominator & (0 - denominator);\n assembly (\"memory-safe\") {\n // Divide denominator by twos.\n denominator := div(denominator, twos)\n\n // Divide [high low] by twos.\n low := div(low, twos)\n\n // Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.\n twos := add(div(sub(0, twos), twos), 1)\n }\n\n // Shift in bits from high into low.\n low |= high * twos;\n\n // Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such\n // that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for\n // four bits. That is, denominator * inv ≡ 1 mod 2⁴.\n uint256 inverse = (3 * denominator) ^ 2;\n\n // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also\n // works in modular arithmetic, doubling the correct bits in each step.\n inverse *= 2 - denominator * inverse; // inverse mod 2⁸\n inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶\n inverse *= 2 - denominator * inverse; // inverse mod 2³²\n inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴\n inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸\n inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶\n\n // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n // This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is\n // less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and high\n // is no longer required.\n result = low * inverse;\n return result;\n }\n }\n\n /**\n * @dev Calculates x * y / denominator with full precision, following the selected rounding direction.\n */\n function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {\n return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);\n }\n\n /**\n * @dev Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256.\n */\n function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) {\n unchecked {\n (uint256 high, uint256 low) = mul512(x, y);\n if (high >= 1 << n) {\n Panic.panic(Panic.UNDER_OVERFLOW);\n }\n return (high << (256 - n)) | (low >> n);\n }\n }\n\n /**\n * @dev Calculates x * y >> n with full precision, following the selected rounding direction.\n */\n function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) {\n return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 0);\n }\n\n /**\n * @dev Calculate the modular multiplicative inverse of a number in Z/nZ.\n *\n * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.\n * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.\n *\n * If the input value is not inversible, 0 is returned.\n *\n * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the\n * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.\n */\n function invMod(uint256 a, uint256 n) internal pure returns (uint256) {\n unchecked {\n if (n == 0) return 0;\n\n // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)\n // Used to compute integers x and y such that: ax + ny = gcd(a, n).\n // When the gcd is 1, then the inverse of a modulo n exists and it's x.\n // ax + ny = 1\n // ax = 1 + (-y)n\n // ax ≡ 1 (mod n) # x is the inverse of a modulo n\n\n // If the remainder is 0 the gcd is n right away.\n uint256 remainder = a % n;\n uint256 gcd = n;\n\n // Therefore the initial coefficients are:\n // ax + ny = gcd(a, n) = n\n // 0a + 1n = n\n int256 x = 0;\n int256 y = 1;\n\n while (remainder != 0) {\n uint256 quotient = gcd / remainder;\n\n (gcd, remainder) = (\n // The old remainder is the next gcd to try.\n remainder,\n // Compute the next remainder.\n // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd\n // where gcd is at most n (capped to type(uint256).max)\n gcd - remainder * quotient\n );\n\n (x, y) = (\n // Increment the coefficient of a.\n y,\n // Decrement the coefficient of n.\n // Can overflow, but the result is casted to uint256 so that the\n // next value of y is \"wrapped around\" to a value between 0 and n - 1.\n x - y * int256(quotient)\n );\n }\n\n if (gcd != 1) return 0; // No inverse exists.\n return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.\n }\n }\n\n /**\n * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.\n *\n * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is\n * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that\n * `a**(p-2)` is the modular multiplicative inverse of a in Fp.\n *\n * NOTE: this function does NOT check that `p` is a prime greater than `2`.\n */\n function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {\n unchecked {\n return Math.modExp(a, p - 2, p);\n }\n }\n\n /**\n * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)\n *\n * Requirements:\n * - modulus can't be zero\n * - underlying staticcall to precompile must succeed\n *\n * IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make\n * sure the chain you're using it on supports the precompiled contract for modular exponentiation\n * at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,\n * the underlying function will succeed given the lack of a revert, but the result may be incorrectly\n * interpreted as 0.\n */\n function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {\n (bool success, uint256 result) = tryModExp(b, e, m);\n if (!success) {\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n return result;\n }\n\n /**\n * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).\n * It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying\n * to operate modulo 0 or if the underlying precompile reverted.\n *\n * IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain\n * you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in\n * https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack\n * of a revert, but the result may be incorrectly interpreted as 0.\n */\n function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {\n if (m == 0) return (false, 0);\n assembly (\"memory-safe\") {\n let ptr := mload(0x40)\n // | Offset | Content | Content (Hex) |\n // |-----------|------------|--------------------------------------------------------------------|\n // | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x60:0x7f | value of b | 0x<.............................................................b> |\n // | 0x80:0x9f | value of e | 0x<.............................................................e> |\n // | 0xa0:0xbf | value of m | 0x<.............................................................m> |\n mstore(ptr, 0x20)\n mstore(add(ptr, 0x20), 0x20)\n mstore(add(ptr, 0x40), 0x20)\n mstore(add(ptr, 0x60), b)\n mstore(add(ptr, 0x80), e)\n mstore(add(ptr, 0xa0), m)\n\n // Given the result < m, it's guaranteed to fit in 32 bytes,\n // so we can use the memory scratch space located at offset 0.\n success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)\n result := mload(0x00)\n }\n }\n\n /**\n * @dev Variant of {modExp} that supports inputs of arbitrary length.\n */\n function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {\n (bool success, bytes memory result) = tryModExp(b, e, m);\n if (!success) {\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n return result;\n }\n\n /**\n * @dev Variant of {tryModExp} that supports inputs of arbitrary length.\n */\n function tryModExp(\n bytes memory b,\n bytes memory e,\n bytes memory m\n ) internal view returns (bool success, bytes memory result) {\n if (_zeroBytes(m)) return (false, new bytes(0));\n\n uint256 mLen = m.length;\n\n // Encode call args in result and move the free memory pointer\n result = abi.encodePacked(b.length, e.length, mLen, b, e, m);\n\n assembly (\"memory-safe\") {\n let dataPtr := add(result, 0x20)\n // Write result on top of args to avoid allocating extra memory.\n success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)\n // Overwrite the length.\n // result.length > returndatasize() is guaranteed because returndatasize() == m.length\n mstore(result, mLen)\n // Set the memory pointer after the returned data.\n mstore(0x40, add(dataPtr, mLen))\n }\n }\n\n /**\n * @dev Returns whether the provided byte array is zero.\n */\n function _zeroBytes(bytes memory byteArray) private pure returns (bool) {\n for (uint256 i = 0; i < byteArray.length; ++i) {\n if (byteArray[i] != 0) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded\n * towards zero.\n *\n * This method is based on Newton's method for computing square roots; the algorithm is restricted to only\n * using integer operations.\n */\n function sqrt(uint256 a) internal pure returns (uint256) {\n unchecked {\n // Take care of easy edge cases when a == 0 or a == 1\n if (a <= 1) {\n return a;\n }\n\n // In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a\n // sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between\n // the current value as `ε_n = | x_n - sqrt(a) |`.\n //\n // For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root\n // of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is\n // bigger than any uint256.\n //\n // By noticing that\n // `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`\n // we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar\n // to the msb function.\n uint256 aa = a;\n uint256 xn = 1;\n\n if (aa >= (1 << 128)) {\n aa >>= 128;\n xn <<= 64;\n }\n if (aa >= (1 << 64)) {\n aa >>= 64;\n xn <<= 32;\n }\n if (aa >= (1 << 32)) {\n aa >>= 32;\n xn <<= 16;\n }\n if (aa >= (1 << 16)) {\n aa >>= 16;\n xn <<= 8;\n }\n if (aa >= (1 << 8)) {\n aa >>= 8;\n xn <<= 4;\n }\n if (aa >= (1 << 4)) {\n aa >>= 4;\n xn <<= 2;\n }\n if (aa >= (1 << 2)) {\n xn <<= 1;\n }\n\n // We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).\n //\n // We can refine our estimation by noticing that the middle of that interval minimizes the error.\n // If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).\n // This is going to be our x_0 (and ε_0)\n xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)\n\n // From here, Newton's method give us:\n // x_{n+1} = (x_n + a / x_n) / 2\n //\n // One should note that:\n // x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a\n // = ((x_n² + a) / (2 * x_n))² - a\n // = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a\n // = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)\n // = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)\n // = (x_n² - a)² / (2 * x_n)²\n // = ((x_n² - a) / (2 * x_n))²\n // ≥ 0\n // Which proves that for all n ≥ 1, sqrt(a) ≤ x_n\n //\n // This gives us the proof of quadratic convergence of the sequence:\n // ε_{n+1} = | x_{n+1} - sqrt(a) |\n // = | (x_n + a / x_n) / 2 - sqrt(a) |\n // = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |\n // = | (x_n - sqrt(a))² / (2 * x_n) |\n // = | ε_n² / (2 * x_n) |\n // = ε_n² / | (2 * x_n) |\n //\n // For the first iteration, we have a special case where x_0 is known:\n // ε_1 = ε_0² / | (2 * x_0) |\n // ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))\n // ≤ 2**(2*e-4) / (3 * 2**(e-1))\n // ≤ 2**(e-3) / 3\n // ≤ 2**(e-3-log2(3))\n // ≤ 2**(e-4.5)\n //\n // For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:\n // ε_{n+1} = ε_n² / | (2 * x_n) |\n // ≤ (2**(e-k))² / (2 * 2**(e-1))\n // ≤ 2**(2*e-2*k) / 2**e\n // ≤ 2**(e-2*k)\n xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above\n xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5\n xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9\n xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18\n xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36\n xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72\n\n // Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision\n // ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either\n // sqrt(a) or sqrt(a) + 1.\n return xn - SafeCast.toUint(xn > a / xn);\n }\n }\n\n /**\n * @dev Calculates sqrt(a), following the selected rounding direction.\n */\n function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = sqrt(a);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);\n }\n }\n\n /**\n * @dev Return the log in base 2 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n */\n function log2(uint256 x) internal pure returns (uint256 r) {\n // If value has upper 128 bits set, log2 result is at least 128\n r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;\n // If upper 64 bits of 128-bit half set, add 64 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;\n // If upper 32 bits of 64-bit half set, add 32 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;\n // If upper 16 bits of 32-bit half set, add 16 to result\n r |= SafeCast.toUint((x >> r) > 0xffff) << 4;\n // If upper 8 bits of 16-bit half set, add 8 to result\n r |= SafeCast.toUint((x >> r) > 0xff) << 3;\n // If upper 4 bits of 8-bit half set, add 4 to result\n r |= SafeCast.toUint((x >> r) > 0xf) << 2;\n\n // Shifts value right by the current result and use it as an index into this lookup table:\n //\n // | x (4 bits) | index | table[index] = MSB position |\n // |------------|---------|-----------------------------|\n // | 0000 | 0 | table[0] = 0 |\n // | 0001 | 1 | table[1] = 0 |\n // | 0010 | 2 | table[2] = 1 |\n // | 0011 | 3 | table[3] = 1 |\n // | 0100 | 4 | table[4] = 2 |\n // | 0101 | 5 | table[5] = 2 |\n // | 0110 | 6 | table[6] = 2 |\n // | 0111 | 7 | table[7] = 2 |\n // | 1000 | 8 | table[8] = 3 |\n // | 1001 | 9 | table[9] = 3 |\n // | 1010 | 10 | table[10] = 3 |\n // | 1011 | 11 | table[11] = 3 |\n // | 1100 | 12 | table[12] = 3 |\n // | 1101 | 13 | table[13] = 3 |\n // | 1110 | 14 | table[14] = 3 |\n // | 1111 | 15 | table[15] = 3 |\n //\n // The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.\n assembly (\"memory-safe\") {\n r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))\n }\n }\n\n /**\n * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log2(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);\n }\n }\n\n /**\n * @dev Return the log in base 10 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n */\n function log10(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >= 10 ** 64) {\n value /= 10 ** 64;\n result += 64;\n }\n if (value >= 10 ** 32) {\n value /= 10 ** 32;\n result += 32;\n }\n if (value >= 10 ** 16) {\n value /= 10 ** 16;\n result += 16;\n }\n if (value >= 10 ** 8) {\n value /= 10 ** 8;\n result += 8;\n }\n if (value >= 10 ** 4) {\n value /= 10 ** 4;\n result += 4;\n }\n if (value >= 10 ** 2) {\n value /= 10 ** 2;\n result += 2;\n }\n if (value >= 10 ** 1) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log10(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);\n }\n }\n\n /**\n * @dev Return the log in base 256 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n *\n * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n */\n function log256(uint256 x) internal pure returns (uint256 r) {\n // If value has upper 128 bits set, log2 result is at least 128\n r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;\n // If upper 64 bits of 128-bit half set, add 64 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;\n // If upper 32 bits of 64-bit half set, add 32 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;\n // If upper 16 bits of 32-bit half set, add 16 to result\n r |= SafeCast.toUint((x >> r) > 0xffff) << 4;\n // Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8\n return (r >> 3) | SafeCast.toUint((x >> r) > 0xff);\n }\n\n /**\n * @dev Return the log in base 256, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log256(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);\n }\n }\n\n /**\n * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.\n */\n function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {\n return uint8(rounding) % 2 == 1;\n }\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)\n// This file was procedurally generated from scripts/generate/templates/SafeCast.js.\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n */\nlibrary SafeCast {\n /**\n * @dev Value doesn't fit in an uint of `bits` size.\n */\n error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);\n\n /**\n * @dev An int value doesn't fit in an uint of `bits` size.\n */\n error SafeCastOverflowedIntToUint(int256 value);\n\n /**\n * @dev Value doesn't fit in an int of `bits` size.\n */\n error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);\n\n /**\n * @dev An uint value doesn't fit in an int of `bits` size.\n */\n error SafeCastOverflowedUintToInt(uint256 value);\n\n /**\n * @dev Returns the downcasted uint248 from uint256, reverting on\n * overflow (when the input is greater than largest uint248).\n *\n * Counterpart to Solidity's `uint248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n */\n function toUint248(uint256 value) internal pure returns (uint248) {\n if (value > type(uint248).max) {\n revert SafeCastOverflowedUintDowncast(248, value);\n }\n return uint248(value);\n }\n\n /**\n * @dev Returns the downcasted uint240 from uint256, reverting on\n * overflow (when the input is greater than largest uint240).\n *\n * Counterpart to Solidity's `uint240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n */\n function toUint240(uint256 value) internal pure returns (uint240) {\n if (value > type(uint240).max) {\n revert SafeCastOverflowedUintDowncast(240, value);\n }\n return uint240(value);\n }\n\n /**\n * @dev Returns the downcasted uint232 from uint256, reverting on\n * overflow (when the input is greater than largest uint232).\n *\n * Counterpart to Solidity's `uint232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n */\n function toUint232(uint256 value) internal pure returns (uint232) {\n if (value > type(uint232).max) {\n revert SafeCastOverflowedUintDowncast(232, value);\n }\n return uint232(value);\n }\n\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n if (value > type(uint224).max) {\n revert SafeCastOverflowedUintDowncast(224, value);\n }\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint216 from uint256, reverting on\n * overflow (when the input is greater than largest uint216).\n *\n * Counterpart to Solidity's `uint216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n */\n function toUint216(uint256 value) internal pure returns (uint216) {\n if (value > type(uint216).max) {\n revert SafeCastOverflowedUintDowncast(216, value);\n }\n return uint216(value);\n }\n\n /**\n * @dev Returns the downcasted uint208 from uint256, reverting on\n * overflow (when the input is greater than largest uint208).\n *\n * Counterpart to Solidity's `uint208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n */\n function toUint208(uint256 value) internal pure returns (uint208) {\n if (value > type(uint208).max) {\n revert SafeCastOverflowedUintDowncast(208, value);\n }\n return uint208(value);\n }\n\n /**\n * @dev Returns the downcasted uint200 from uint256, reverting on\n * overflow (when the input is greater than largest uint200).\n *\n * Counterpart to Solidity's `uint200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n */\n function toUint200(uint256 value) internal pure returns (uint200) {\n if (value > type(uint200).max) {\n revert SafeCastOverflowedUintDowncast(200, value);\n }\n return uint200(value);\n }\n\n /**\n * @dev Returns the downcasted uint192 from uint256, reverting on\n * overflow (when the input is greater than largest uint192).\n *\n * Counterpart to Solidity's `uint192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n */\n function toUint192(uint256 value) internal pure returns (uint192) {\n if (value > type(uint192).max) {\n revert SafeCastOverflowedUintDowncast(192, value);\n }\n return uint192(value);\n }\n\n /**\n * @dev Returns the downcasted uint184 from uint256, reverting on\n * overflow (when the input is greater than largest uint184).\n *\n * Counterpart to Solidity's `uint184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n */\n function toUint184(uint256 value) internal pure returns (uint184) {\n if (value > type(uint184).max) {\n revert SafeCastOverflowedUintDowncast(184, value);\n }\n return uint184(value);\n }\n\n /**\n * @dev Returns the downcasted uint176 from uint256, reverting on\n * overflow (when the input is greater than largest uint176).\n *\n * Counterpart to Solidity's `uint176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n */\n function toUint176(uint256 value) internal pure returns (uint176) {\n if (value > type(uint176).max) {\n revert SafeCastOverflowedUintDowncast(176, value);\n }\n return uint176(value);\n }\n\n /**\n * @dev Returns the downcasted uint168 from uint256, reverting on\n * overflow (when the input is greater than largest uint168).\n *\n * Counterpart to Solidity's `uint168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n */\n function toUint168(uint256 value) internal pure returns (uint168) {\n if (value > type(uint168).max) {\n revert SafeCastOverflowedUintDowncast(168, value);\n }\n return uint168(value);\n }\n\n /**\n * @dev Returns the downcasted uint160 from uint256, reverting on\n * overflow (when the input is greater than largest uint160).\n *\n * Counterpart to Solidity's `uint160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n */\n function toUint160(uint256 value) internal pure returns (uint160) {\n if (value > type(uint160).max) {\n revert SafeCastOverflowedUintDowncast(160, value);\n }\n return uint160(value);\n }\n\n /**\n * @dev Returns the downcasted uint152 from uint256, reverting on\n * overflow (when the input is greater than largest uint152).\n *\n * Counterpart to Solidity's `uint152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n */\n function toUint152(uint256 value) internal pure returns (uint152) {\n if (value > type(uint152).max) {\n revert SafeCastOverflowedUintDowncast(152, value);\n }\n return uint152(value);\n }\n\n /**\n * @dev Returns the downcasted uint144 from uint256, reverting on\n * overflow (when the input is greater than largest uint144).\n *\n * Counterpart to Solidity's `uint144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n */\n function toUint144(uint256 value) internal pure returns (uint144) {\n if (value > type(uint144).max) {\n revert SafeCastOverflowedUintDowncast(144, value);\n }\n return uint144(value);\n }\n\n /**\n * @dev Returns the downcasted uint136 from uint256, reverting on\n * overflow (when the input is greater than largest uint136).\n *\n * Counterpart to Solidity's `uint136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n */\n function toUint136(uint256 value) internal pure returns (uint136) {\n if (value > type(uint136).max) {\n revert SafeCastOverflowedUintDowncast(136, value);\n }\n return uint136(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n if (value > type(uint128).max) {\n revert SafeCastOverflowedUintDowncast(128, value);\n }\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint120 from uint256, reverting on\n * overflow (when the input is greater than largest uint120).\n *\n * Counterpart to Solidity's `uint120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n */\n function toUint120(uint256 value) internal pure returns (uint120) {\n if (value > type(uint120).max) {\n revert SafeCastOverflowedUintDowncast(120, value);\n }\n return uint120(value);\n }\n\n /**\n * @dev Returns the downcasted uint112 from uint256, reverting on\n * overflow (when the input is greater than largest uint112).\n *\n * Counterpart to Solidity's `uint112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n */\n function toUint112(uint256 value) internal pure returns (uint112) {\n if (value > type(uint112).max) {\n revert SafeCastOverflowedUintDowncast(112, value);\n }\n return uint112(value);\n }\n\n /**\n * @dev Returns the downcasted uint104 from uint256, reverting on\n * overflow (when the input is greater than largest uint104).\n *\n * Counterpart to Solidity's `uint104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n */\n function toUint104(uint256 value) internal pure returns (uint104) {\n if (value > type(uint104).max) {\n revert SafeCastOverflowedUintDowncast(104, value);\n }\n return uint104(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n if (value > type(uint96).max) {\n revert SafeCastOverflowedUintDowncast(96, value);\n }\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint88 from uint256, reverting on\n * overflow (when the input is greater than largest uint88).\n *\n * Counterpart to Solidity's `uint88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n */\n function toUint88(uint256 value) internal pure returns (uint88) {\n if (value > type(uint88).max) {\n revert SafeCastOverflowedUintDowncast(88, value);\n }\n return uint88(value);\n }\n\n /**\n * @dev Returns the downcasted uint80 from uint256, reverting on\n * overflow (when the input is greater than largest uint80).\n *\n * Counterpart to Solidity's `uint80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n */\n function toUint80(uint256 value) internal pure returns (uint80) {\n if (value > type(uint80).max) {\n revert SafeCastOverflowedUintDowncast(80, value);\n }\n return uint80(value);\n }\n\n /**\n * @dev Returns the downcasted uint72 from uint256, reverting on\n * overflow (when the input is greater than largest uint72).\n *\n * Counterpart to Solidity's `uint72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n */\n function toUint72(uint256 value) internal pure returns (uint72) {\n if (value > type(uint72).max) {\n revert SafeCastOverflowedUintDowncast(72, value);\n }\n return uint72(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n if (value > type(uint64).max) {\n revert SafeCastOverflowedUintDowncast(64, value);\n }\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint56 from uint256, reverting on\n * overflow (when the input is greater than largest uint56).\n *\n * Counterpart to Solidity's `uint56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n */\n function toUint56(uint256 value) internal pure returns (uint56) {\n if (value > type(uint56).max) {\n revert SafeCastOverflowedUintDowncast(56, value);\n }\n return uint56(value);\n }\n\n /**\n * @dev Returns the downcasted uint48 from uint256, reverting on\n * overflow (when the input is greater than largest uint48).\n *\n * Counterpart to Solidity's `uint48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n */\n function toUint48(uint256 value) internal pure returns (uint48) {\n if (value > type(uint48).max) {\n revert SafeCastOverflowedUintDowncast(48, value);\n }\n return uint48(value);\n }\n\n /**\n * @dev Returns the downcasted uint40 from uint256, reverting on\n * overflow (when the input is greater than largest uint40).\n *\n * Counterpart to Solidity's `uint40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n */\n function toUint40(uint256 value) internal pure returns (uint40) {\n if (value > type(uint40).max) {\n revert SafeCastOverflowedUintDowncast(40, value);\n }\n return uint40(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n if (value > type(uint32).max) {\n revert SafeCastOverflowedUintDowncast(32, value);\n }\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint24 from uint256, reverting on\n * overflow (when the input is greater than largest uint24).\n *\n * Counterpart to Solidity's `uint24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n */\n function toUint24(uint256 value) internal pure returns (uint24) {\n if (value > type(uint24).max) {\n revert SafeCastOverflowedUintDowncast(24, value);\n }\n return uint24(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n if (value > type(uint16).max) {\n revert SafeCastOverflowedUintDowncast(16, value);\n }\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n if (value > type(uint8).max) {\n revert SafeCastOverflowedUintDowncast(8, value);\n }\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n if (value < 0) {\n revert SafeCastOverflowedIntToUint(value);\n }\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int248 from int256, reverting on\n * overflow (when the input is less than smallest int248 or\n * greater than largest int248).\n *\n * Counterpart to Solidity's `int248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n */\n function toInt248(int256 value) internal pure returns (int248 downcasted) {\n downcasted = int248(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(248, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int240 from int256, reverting on\n * overflow (when the input is less than smallest int240 or\n * greater than largest int240).\n *\n * Counterpart to Solidity's `int240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n */\n function toInt240(int256 value) internal pure returns (int240 downcasted) {\n downcasted = int240(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(240, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int232 from int256, reverting on\n * overflow (when the input is less than smallest int232 or\n * greater than largest int232).\n *\n * Counterpart to Solidity's `int232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n */\n function toInt232(int256 value) internal pure returns (int232 downcasted) {\n downcasted = int232(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(232, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int224 from int256, reverting on\n * overflow (when the input is less than smallest int224 or\n * greater than largest int224).\n *\n * Counterpart to Solidity's `int224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toInt224(int256 value) internal pure returns (int224 downcasted) {\n downcasted = int224(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(224, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int216 from int256, reverting on\n * overflow (when the input is less than smallest int216 or\n * greater than largest int216).\n *\n * Counterpart to Solidity's `int216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n */\n function toInt216(int256 value) internal pure returns (int216 downcasted) {\n downcasted = int216(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(216, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int208 from int256, reverting on\n * overflow (when the input is less than smallest int208 or\n * greater than largest int208).\n *\n * Counterpart to Solidity's `int208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n */\n function toInt208(int256 value) internal pure returns (int208 downcasted) {\n downcasted = int208(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(208, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int200 from int256, reverting on\n * overflow (when the input is less than smallest int200 or\n * greater than largest int200).\n *\n * Counterpart to Solidity's `int200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n */\n function toInt200(int256 value) internal pure returns (int200 downcasted) {\n downcasted = int200(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(200, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int192 from int256, reverting on\n * overflow (when the input is less than smallest int192 or\n * greater than largest int192).\n *\n * Counterpart to Solidity's `int192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n */\n function toInt192(int256 value) internal pure returns (int192 downcasted) {\n downcasted = int192(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(192, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int184 from int256, reverting on\n * overflow (when the input is less than smallest int184 or\n * greater than largest int184).\n *\n * Counterpart to Solidity's `int184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n */\n function toInt184(int256 value) internal pure returns (int184 downcasted) {\n downcasted = int184(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(184, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int176 from int256, reverting on\n * overflow (when the input is less than smallest int176 or\n * greater than largest int176).\n *\n * Counterpart to Solidity's `int176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n */\n function toInt176(int256 value) internal pure returns (int176 downcasted) {\n downcasted = int176(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(176, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int168 from int256, reverting on\n * overflow (when the input is less than smallest int168 or\n * greater than largest int168).\n *\n * Counterpart to Solidity's `int168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n */\n function toInt168(int256 value) internal pure returns (int168 downcasted) {\n downcasted = int168(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(168, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int160 from int256, reverting on\n * overflow (when the input is less than smallest int160 or\n * greater than largest int160).\n *\n * Counterpart to Solidity's `int160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n */\n function toInt160(int256 value) internal pure returns (int160 downcasted) {\n downcasted = int160(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(160, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int152 from int256, reverting on\n * overflow (when the input is less than smallest int152 or\n * greater than largest int152).\n *\n * Counterpart to Solidity's `int152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n */\n function toInt152(int256 value) internal pure returns (int152 downcasted) {\n downcasted = int152(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(152, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int144 from int256, reverting on\n * overflow (when the input is less than smallest int144 or\n * greater than largest int144).\n *\n * Counterpart to Solidity's `int144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n */\n function toInt144(int256 value) internal pure returns (int144 downcasted) {\n downcasted = int144(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(144, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int136 from int256, reverting on\n * overflow (when the input is less than smallest int136 or\n * greater than largest int136).\n *\n * Counterpart to Solidity's `int136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n */\n function toInt136(int256 value) internal pure returns (int136 downcasted) {\n downcasted = int136(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(136, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toInt128(int256 value) internal pure returns (int128 downcasted) {\n downcasted = int128(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(128, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int120 from int256, reverting on\n * overflow (when the input is less than smallest int120 or\n * greater than largest int120).\n *\n * Counterpart to Solidity's `int120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n */\n function toInt120(int256 value) internal pure returns (int120 downcasted) {\n downcasted = int120(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(120, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int112 from int256, reverting on\n * overflow (when the input is less than smallest int112 or\n * greater than largest int112).\n *\n * Counterpart to Solidity's `int112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n */\n function toInt112(int256 value) internal pure returns (int112 downcasted) {\n downcasted = int112(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(112, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int104 from int256, reverting on\n * overflow (when the input is less than smallest int104 or\n * greater than largest int104).\n *\n * Counterpart to Solidity's `int104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n */\n function toInt104(int256 value) internal pure returns (int104 downcasted) {\n downcasted = int104(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(104, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int96 from int256, reverting on\n * overflow (when the input is less than smallest int96 or\n * greater than largest int96).\n *\n * Counterpart to Solidity's `int96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toInt96(int256 value) internal pure returns (int96 downcasted) {\n downcasted = int96(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(96, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int88 from int256, reverting on\n * overflow (when the input is less than smallest int88 or\n * greater than largest int88).\n *\n * Counterpart to Solidity's `int88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n */\n function toInt88(int256 value) internal pure returns (int88 downcasted) {\n downcasted = int88(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(88, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int80 from int256, reverting on\n * overflow (when the input is less than smallest int80 or\n * greater than largest int80).\n *\n * Counterpart to Solidity's `int80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n */\n function toInt80(int256 value) internal pure returns (int80 downcasted) {\n downcasted = int80(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(80, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int72 from int256, reverting on\n * overflow (when the input is less than smallest int72 or\n * greater than largest int72).\n *\n * Counterpart to Solidity's `int72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n */\n function toInt72(int256 value) internal pure returns (int72 downcasted) {\n downcasted = int72(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(72, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toInt64(int256 value) internal pure returns (int64 downcasted) {\n downcasted = int64(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(64, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int56 from int256, reverting on\n * overflow (when the input is less than smallest int56 or\n * greater than largest int56).\n *\n * Counterpart to Solidity's `int56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n */\n function toInt56(int256 value) internal pure returns (int56 downcasted) {\n downcasted = int56(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(56, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int48 from int256, reverting on\n * overflow (when the input is less than smallest int48 or\n * greater than largest int48).\n *\n * Counterpart to Solidity's `int48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n */\n function toInt48(int256 value) internal pure returns (int48 downcasted) {\n downcasted = int48(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(48, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int40 from int256, reverting on\n * overflow (when the input is less than smallest int40 or\n * greater than largest int40).\n *\n * Counterpart to Solidity's `int40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n */\n function toInt40(int256 value) internal pure returns (int40 downcasted) {\n downcasted = int40(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(40, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toInt32(int256 value) internal pure returns (int32 downcasted) {\n downcasted = int32(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(32, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int24 from int256, reverting on\n * overflow (when the input is less than smallest int24 or\n * greater than largest int24).\n *\n * Counterpart to Solidity's `int24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n */\n function toInt24(int256 value) internal pure returns (int24 downcasted) {\n downcasted = int24(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(24, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toInt16(int256 value) internal pure returns (int16 downcasted) {\n downcasted = int16(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(16, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n */\n function toInt8(int256 value) internal pure returns (int8 downcasted) {\n downcasted = int8(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(8, value);\n }\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n if (value > uint256(type(int256).max)) {\n revert SafeCastOverflowedUintToInt(value);\n }\n return int256(value);\n }\n\n /**\n * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.\n */\n function toUint(bool b) internal pure returns (uint256 u) {\n assembly (\"memory-safe\") {\n u := iszero(iszero(b))\n }\n }\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/Panic.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Helper library for emitting standardized panic codes.\n *\n * ```solidity\n * contract Example {\n * using Panic for uint256;\n *\n * // Use any of the declared internal constants\n * function foo() { Panic.GENERIC.panic(); }\n *\n * // Alternatively\n * function foo() { Panic.panic(Panic.GENERIC); }\n * }\n * ```\n *\n * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].\n *\n * _Available since v5.1._\n */\n// slither-disable-next-line unused-state\nlibrary Panic {\n /// @dev generic / unspecified error\n uint256 internal constant GENERIC = 0x00;\n /// @dev used by the assert() builtin\n uint256 internal constant ASSERT = 0x01;\n /// @dev arithmetic underflow or overflow\n uint256 internal constant UNDER_OVERFLOW = 0x11;\n /// @dev division or modulo by zero\n uint256 internal constant DIVISION_BY_ZERO = 0x12;\n /// @dev enum conversion error\n uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;\n /// @dev invalid encoding in storage\n uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;\n /// @dev empty array pop\n uint256 internal constant EMPTY_ARRAY_POP = 0x31;\n /// @dev array out of bounds access\n uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;\n /// @dev resource error (too large allocation or too large array)\n uint256 internal constant RESOURCE_ERROR = 0x41;\n /// @dev calling invalid internal function\n uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;\n\n /// @dev Reverts with a panic code. Recommended to use with\n /// the internal constants with predefined codes.\n function panic(uint256 code) internal pure {\n assembly (\"memory-safe\") {\n mstore(0x00, 0x4e487b71)\n mstore(0x20, code)\n revert(0x1c, 0x24)\n }\n }\n}" + }, + "lib/rooster/v2-common/libraries/Constants.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\n// factory contraints on pools\nuint8 constant MAX_PROTOCOL_FEE_RATIO_D3 = 0.25e3; // 25%\nuint256 constant MAX_PROTOCOL_LENDING_FEE_RATE_D18 = 0.02e18; // 2%\nuint64 constant MAX_POOL_FEE_D18 = 0.9e18; // 90%\nuint64 constant MIN_LOOKBACK = 1 seconds;\n\n// pool constraints\nuint8 constant NUMBER_OF_KINDS = 4;\nint32 constant NUMBER_OF_KINDS_32 = int32(int8(NUMBER_OF_KINDS));\nuint256 constant MAX_TICK = 322_378; // max price 1e14 in D18 scale\nint32 constant MAX_TICK_32 = int32(int256(MAX_TICK));\nint32 constant MIN_TICK_32 = int32(-int256(MAX_TICK));\nuint256 constant MAX_BINS_TO_MERGE = 3;\nuint128 constant MINIMUM_LIQUIDITY = 1e8;\n\n// accessor named constants\nuint8 constant ALL_KINDS_MASK = 0xF; // 0b1111\nuint8 constant PERMISSIONED_LIQUIDITY_MASK = 0x10; // 0b010000\nuint8 constant PERMISSIONED_SWAP_MASK = 0x20; // 0b100000\nuint8 constant OPTIONS_MASK = ALL_KINDS_MASK | PERMISSIONED_LIQUIDITY_MASK | PERMISSIONED_SWAP_MASK; // 0b111111\n\n// named values\naddress constant MERGED_LP_BALANCE_ADDRESS = address(0);\nuint256 constant MERGED_LP_BALANCE_SUBACCOUNT = 0;\nuint128 constant ONE = 1e18;\nuint128 constant ONE_SQUARED = 1e36;\nint256 constant INT256_ONE = 1e18;\nuint256 constant ONE_D8 = 1e8;\nuint256 constant ONE_D3 = 1e3;\nint40 constant INT_ONE_D8 = 1e8;\nint40 constant HALF_TICK_D8 = 0.5e8;\nuint8 constant DEFAULT_DECIMALS = 18;\nuint256 constant DEFAULT_SCALE = 1;\nbytes constant EMPTY_PRICE_BREAKS = hex\"010000000000000000000000\";" + }, + "lib/rooster/v2-common/libraries/Math.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport {Math as OzMath} from \"../../openzeppelin-custom/contracts/utils/math/Math.sol\";\n\nimport {ONE, DEFAULT_SCALE, DEFAULT_DECIMALS, INT_ONE_D8, ONE_SQUARED} from \"./Constants.sol\";\n\n/**\n * @notice Math functions.\n */\nlibrary Math {\n /**\n * @notice Returns the lesser of two values.\n * @param x First uint256 value.\n * @param y Second uint256 value.\n */\n function min(uint256 x, uint256 y) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), lt(y, x)))\n }\n }\n\n /**\n * @notice Returns the lesser of two uint128 values.\n * @param x First uint128 value.\n * @param y Second uint128 value.\n */\n function min128(uint128 x, uint128 y) internal pure returns (uint128 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), lt(y, x)))\n }\n }\n\n /**\n * @notice Returns the lesser of two int256 values.\n * @param x First int256 value.\n * @param y Second int256 value.\n */\n function min(int256 x, int256 y) internal pure returns (int256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), slt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two uint256 values.\n * @param x First uint256 value.\n * @param y Second uint256 value.\n */\n function max(uint256 x, uint256 y) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), gt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two int256 values.\n * @param x First int256 value.\n * @param y Second int256 value.\n */\n function max(int256 x, int256 y) internal pure returns (int256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), sgt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two uint128 values.\n * @param x First uint128 value.\n * @param y Second uint128 value.\n */\n function max128(uint128 x, uint128 y) internal pure returns (uint128 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), gt(y, x)))\n }\n }\n\n /**\n * @notice Thresholds a value to be within the specified bounds.\n * @param value The value to bound.\n * @param lowerLimit The minimum allowable value.\n * @param upperLimit The maximum allowable value.\n */\n function boundValue(\n uint256 value,\n uint256 lowerLimit,\n uint256 upperLimit\n ) internal pure returns (uint256 outputValue) {\n outputValue = min(max(value, lowerLimit), upperLimit);\n }\n\n /**\n * @notice Returns the difference between two uint128 values or zero if the result would be negative.\n * @param x The minuend.\n * @param y The subtrahend.\n */\n function clip128(uint128 x, uint128 y) internal pure returns (uint128) {\n unchecked {\n return x < y ? 0 : x - y;\n }\n }\n\n /**\n * @notice Returns the difference between two uint256 values or zero if the result would be negative.\n * @param x The minuend.\n * @param y The subtrahend.\n */\n function clip(uint256 x, uint256 y) internal pure returns (uint256) {\n unchecked {\n return x < y ? 0 : x - y;\n }\n }\n\n /**\n * @notice Divides one uint256 by another, rounding down to the nearest\n * integer.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divFloor(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivFloor(x, ONE, y);\n }\n\n /**\n * @notice Divides one uint256 by another, rounding up to the nearest integer.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divCeil(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivCeil(x, ONE, y);\n }\n\n /**\n * @notice Multiplies two uint256 values and then divides by ONE, rounding down.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulFloor(uint256 x, uint256 y) internal pure returns (uint256) {\n return OzMath.mulDiv(x, y, ONE);\n }\n\n /**\n * @notice Multiplies two uint256 values and then divides by ONE, rounding up.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulCeil(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivCeil(x, y, ONE);\n }\n\n /**\n * @notice Calculates the multiplicative inverse of a uint256, rounding down.\n * @param x The value to invert.\n */\n function invFloor(uint256 x) internal pure returns (uint256) {\n unchecked {\n return ONE_SQUARED / x;\n }\n }\n\n /**\n * @notice Calculates the multiplicative inverse of a uint256, rounding up.\n * @param denominator The value to invert.\n */\n function invCeil(uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // divide z - 1 by the denominator and add 1.\n z := add(div(sub(ONE_SQUARED, 1), denominator), 1)\n }\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding down.\n * @param x The multiplicand.\n * @param y The multiplier.\n * @param k The divisor.\n */\n function mulDivFloor(uint256 x, uint256 y, uint256 k) internal pure returns (uint256 result) {\n result = OzMath.mulDiv(x, y, max(1, k));\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding up if there's a remainder.\n * @param x The multiplicand.\n * @param y The multiplier.\n * @param k The divisor.\n */\n function mulDivCeil(uint256 x, uint256 y, uint256 k) internal pure returns (uint256 result) {\n result = mulDivFloor(x, y, k);\n if (mulmod(x, y, max(1, k)) != 0) result = result + 1;\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding\n * down. Will revert if `x * y` is larger than `type(uint256).max`.\n * @param x The first operand for multiplication.\n * @param y The second operand for multiplication.\n * @param denominator The divisor after multiplication.\n */\n function mulDivDown(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // Store x * y in z for now.\n z := mul(x, y)\n if iszero(denominator) {\n denominator := 1\n }\n\n if iszero(or(iszero(x), eq(div(z, x), y))) {\n revert(0, 0)\n }\n\n // Divide z by the denominator.\n z := div(z, denominator)\n }\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding\n * up. Will revert if `x * y` is larger than `type(uint256).max`.\n * @param x The first operand for multiplication.\n * @param y The second operand for multiplication.\n * @param denominator The divisor after multiplication.\n */\n function mulDivUp(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // Store x * y in z for now.\n z := mul(x, y)\n if iszero(denominator) {\n denominator := 1\n }\n\n if iszero(or(iszero(x), eq(div(z, x), y))) {\n revert(0, 0)\n }\n\n // First, divide z - 1 by the denominator and add 1.\n // We allow z - 1 to underflow if z is 0, because we multiply the\n // end result by 0 if z is zero, ensuring we return 0 if z is zero.\n z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))\n }\n }\n\n /**\n * @notice Multiplies a uint256 by another and divides by a constant,\n * rounding down. Will revert if `x * y` is larger than\n * `type(uint256).max`.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulDown(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivDown(x, y, ONE);\n }\n\n /**\n * @notice Divides a uint256 by another, rounding down the result. Will\n * revert if `x * 1e18` is larger than `type(uint256).max`.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divDown(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivDown(x, ONE, y);\n }\n\n /**\n * @notice Divides a uint256 by another, rounding up the result. Will\n * revert if `x * 1e18` is larger than `type(uint256).max`.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divUp(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivUp(x, ONE, y);\n }\n\n /**\n * @notice Scales a number based on a difference in decimals from a default.\n * @param decimals The new decimal precision.\n */\n function scale(uint8 decimals) internal pure returns (uint256) {\n unchecked {\n if (decimals == DEFAULT_DECIMALS) {\n return DEFAULT_SCALE;\n } else {\n return 10 ** (DEFAULT_DECIMALS - decimals);\n }\n }\n }\n\n /**\n * @notice Adjusts a scaled amount to the token decimal scale.\n * @param amount The scaled amount.\n * @param scaleFactor The scaling factor to adjust by.\n * @param ceil Whether to round up (true) or down (false).\n */\n function ammScaleToTokenScale(uint256 amount, uint256 scaleFactor, bool ceil) internal pure returns (uint256 z) {\n unchecked {\n if (scaleFactor == DEFAULT_SCALE || amount == 0) {\n return amount;\n } else {\n if (!ceil) return amount / scaleFactor;\n assembly (\"memory-safe\") {\n z := add(div(sub(amount, 1), scaleFactor), 1)\n }\n }\n }\n }\n\n /**\n * @notice Adjusts a token amount to the D18 AMM scale.\n * @param amount The amount in token scale.\n * @param scaleFactor The scale factor for adjustment.\n */\n function tokenScaleToAmmScale(uint256 amount, uint256 scaleFactor) internal pure returns (uint256) {\n if (scaleFactor == DEFAULT_SCALE) {\n return amount;\n } else {\n return amount * scaleFactor;\n }\n }\n\n /**\n * @notice Returns the absolute value of a signed 32-bit integer.\n * @param x The integer to take the absolute value of.\n */\n function abs32(int32 x) internal pure returns (uint32) {\n unchecked {\n return uint32(x < 0 ? -x : x);\n }\n }\n\n /**\n * @notice Returns the absolute value of a signed 256-bit integer.\n * @param x The integer to take the absolute value of.\n */\n function abs(int256 x) internal pure returns (uint256) {\n unchecked {\n return uint256(x < 0 ? -x : x);\n }\n }\n\n /**\n * @notice Calculates the integer square root of a uint256 rounded down.\n * @param x The number to take the square root of.\n */\n function sqrt(uint256 x) internal pure returns (uint256 z) {\n // from https://github.com/transmissions11/solmate/blob/e8f96f25d48fe702117ce76c79228ca4f20206cb/src/utils/FixedPointMathLib.sol\n assembly (\"memory-safe\") {\n let y := x\n z := 181\n\n if iszero(lt(y, 0x10000000000000000000000000000000000)) {\n y := shr(128, y)\n z := shl(64, z)\n }\n if iszero(lt(y, 0x1000000000000000000)) {\n y := shr(64, y)\n z := shl(32, z)\n }\n if iszero(lt(y, 0x10000000000)) {\n y := shr(32, y)\n z := shl(16, z)\n }\n if iszero(lt(y, 0x1000000)) {\n y := shr(16, y)\n z := shl(8, z)\n }\n\n z := shr(18, mul(z, add(y, 65536)))\n\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n\n z := sub(z, lt(div(x, z), z))\n }\n }\n\n /**\n * @notice Computes the floor of a D8-scaled number as an int32, ignoring\n * potential overflow in the cast.\n * @param val The D8-scaled number.\n */\n function floorD8Unchecked(int256 val) internal pure returns (int32) {\n int32 val32;\n bool check;\n unchecked {\n val32 = int32(val / INT_ONE_D8);\n check = (val < 0 && val % INT_ONE_D8 != 0);\n }\n return check ? val32 - 1 : val32;\n }\n}" + }, + "lib/rooster/v2-common/libraries/TickMath.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport {Math as OzMath} from \"../../openzeppelin-custom/contracts/utils/math/Math.sol\";\nimport {Math} from \"./Math.sol\";\nimport {MAX_TICK, ONE} from \"./Constants.sol\";\n\n/**\n * @notice Math functions related to tick operations.\n */\n// slither-disable-start divide-before-multiply\nlibrary TickMath {\n using Math for uint256;\n\n error TickMaxExceeded(int256 tick);\n\n /**\n * @notice Compute the lower and upper sqrtPrice of a tick.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n */\n function tickSqrtPrices(\n uint256 tickSpacing,\n int32 _tick\n ) internal pure returns (uint256 sqrtLowerPrice, uint256 sqrtUpperPrice) {\n unchecked {\n sqrtLowerPrice = tickSqrtPrice(tickSpacing, _tick);\n sqrtUpperPrice = tickSqrtPrice(tickSpacing, _tick + 1);\n }\n }\n\n /**\n * @notice Compute the base tick value from the pool tick and the\n * tickSpacing. Revert if base tick is beyond the max tick boundary.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n */\n function subTickIndex(uint256 tickSpacing, int32 _tick) internal pure returns (uint32 subTick) {\n subTick = Math.abs32(_tick);\n subTick *= uint32(tickSpacing);\n if (subTick > MAX_TICK) {\n revert TickMaxExceeded(_tick);\n }\n }\n\n /**\n * @notice Calculate the square root price for a given tick and tick spacing.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n * @return _result The square root price.\n */\n function tickSqrtPrice(uint256 tickSpacing, int32 _tick) internal pure returns (uint256 _result) {\n unchecked {\n uint256 tick = subTickIndex(tickSpacing, _tick);\n\n uint256 ratio = tick & 0x1 != 0 ? 0xfffcb933bd6fad9d3af5f0b9f25db4d6 : 0x100000000000000000000000000000000;\n if (tick & 0x2 != 0) ratio = (ratio * 0xfff97272373d41fd789c8cb37ffcaa1c) >> 128;\n if (tick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656ac9229c67059486f389) >> 128;\n if (tick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e81259b3cddc7a064941) >> 128;\n if (tick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f67b19e8887e0bd251eb7) >> 128;\n if (tick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98cd2e57b660be99eb2c4a) >> 128;\n if (tick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c9838804e327cb417cafcb) >> 128;\n if (tick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99d51e2cc356c2f617dbe0) >> 128;\n if (tick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900aecf64236ab31f1f9dcb5) >> 128;\n if (tick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac4d9194200696907cf2e37) >> 128;\n if (tick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b88206f8abe8a3b44dd9be) >> 128;\n if (tick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c578ef4f1d17b2b235d480) >> 128;\n if (tick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd254ee83bdd3f248e7e785e) >> 128;\n if (tick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d8f7dd10e744d913d033333) >> 128;\n if (tick & 0x4000 != 0) ratio = (ratio * 0x70d869a156ddd32a39e257bc3f50aa9b) >> 128;\n if (tick & 0x8000 != 0) ratio = (ratio * 0x31be135f97da6e09a19dc367e3b6da40) >> 128;\n if (tick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7e5a9780b0cc4e25d61a56) >> 128;\n if (tick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedbcb3a6ccb7ce618d14225) >> 128;\n if (tick & 0x40000 != 0) ratio = (ratio * 0x2216e584f630389b2052b8db590e) >> 128;\n if (_tick > 0) ratio = type(uint256).max / ratio;\n _result = (ratio * ONE) >> 128;\n }\n }\n\n /**\n * @notice Calculate liquidity of a tick.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n */\n function getTickL(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n ) internal pure returns (uint256 liquidity) {\n // known:\n // - sqrt price values are different\n // - reserveA and reserveB fit in 128 bit\n // - sqrt price is in (1e-7, 1e7)\n // - D18 max for uint256 is 1.15e59\n // - D18 min is 1e-18\n\n unchecked {\n // diff is in (5e-12, 4e6); max tick spacing is 10_000\n uint256 diff = sqrtUpperTickPrice - sqrtLowerTickPrice;\n\n // Need to maximize precision by shifting small values A and B up so\n // that they use more of the available bit range. Two constraints to\n // consider: we need A * B * diff / sqrtPrice to be bigger than 1e-18\n // when the bump is not in play. This constrains the threshold for\n // bumping to be at least 77 bit; ie, either a or b needs 2^77 which\n // means that term A * B * diff / sqrtPrice > 1e-18.\n //\n // At the other end, the second constraint is that b^2 needs to fit in\n // a 256-bit number, so, post bump, the max reserve value needs to be\n // less than 6e22. With a 78-bit threshold and a 57-bit bump, we have A\n // and B are in (1.4e-1, 4.4e22 (2^(78+57))) with bump, and one of A or\n // B is at least 2^78 without the bump, but the other reserve value may\n // be as small as 1 wei.\n uint256 precisionBump = 0;\n if ((reserveA >> 78) == 0 && (reserveB >> 78) == 0) {\n precisionBump = 57;\n reserveA <<= precisionBump;\n reserveB <<= precisionBump;\n }\n\n if (reserveB == 0) return Math.divDown(reserveA, diff) >> precisionBump;\n if (reserveA == 0)\n return Math.mulDivDown(reserveB.mulDown(sqrtLowerTickPrice), sqrtUpperTickPrice, diff) >> precisionBump;\n\n // b is in (7.2e-9 (2^57 / 1e7 / 2), 2.8e29 (2^(78+57) * 1e7 / 2)) with bump\n // b is in a subset of the same range without bump\n uint256 b = (reserveA.divDown(sqrtUpperTickPrice) + reserveB.mulDown(sqrtLowerTickPrice)) >> 1;\n\n // b^2 is in (5.1e-17, 4.8e58); and will not overflow on either end;\n // A*B is in (3e-13 (2^78 / 1e18 * 1e-18), 1.9e45) without bump and is in a subset range with bump\n // A*B*diff/sqrtUpper is in (1.5e-17 (3e-13 * 5e-12 * 1e7), 7.6e58);\n\n // Since b^2 is at the upper edge of the precision range, we are not\n // able to multiply the argument of the sqrt by 1e18, instead, we move\n // this factor outside of the sqrt. The resulting loss of precision\n // means that this liquidity value is a lower bound on the tick\n // liquidity\n return\n OzMath.mulDiv(\n b +\n Math.sqrt(\n (OzMath.mulDiv(b, b, ONE) +\n OzMath.mulDiv(reserveB.mulFloor(reserveA), diff, sqrtUpperTickPrice))\n ) *\n 1e9,\n sqrtUpperTickPrice,\n diff\n ) >> precisionBump;\n }\n }\n\n /**\n * @notice Calculate square root price of a tick. Returns left edge of the\n * tick if the tick has no reserves.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n * @return sqrtPrice The calculated square root price.\n */\n function getSqrtPrice(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice,\n uint256 liquidity\n ) internal pure returns (uint256 sqrtPrice) {\n unchecked {\n if (reserveA == 0) {\n return sqrtLowerTickPrice;\n }\n if (reserveB == 0) {\n return sqrtUpperTickPrice;\n }\n sqrtPrice = Math.sqrt(\n ONE *\n (reserveA + liquidity.mulDown(sqrtLowerTickPrice)).divDown(\n reserveB + liquidity.divDown(sqrtUpperTickPrice)\n )\n );\n sqrtPrice = Math.boundValue(sqrtPrice, sqrtLowerTickPrice, sqrtUpperTickPrice);\n }\n }\n\n /**\n * @notice Calculate square root price of a tick. Returns left edge of the\n * tick if the tick has no reserves.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n * @return sqrtPrice The calculated square root price.\n * @return liquidity The calculated liquidity.\n */\n function getTickSqrtPriceAndL(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n ) internal pure returns (uint256 sqrtPrice, uint256 liquidity) {\n liquidity = getTickL(reserveA, reserveB, sqrtLowerTickPrice, sqrtUpperTickPrice);\n sqrtPrice = getSqrtPrice(reserveA, reserveB, sqrtLowerTickPrice, sqrtUpperTickPrice, liquidity);\n }\n}\n// slither-disable-end divide-before-multiply" + }, + "solidity-bytes-utils/contracts/BytesLib.sol": { + "content": "// SPDX-License-Identifier: Unlicense\n/*\n * @title Solidity Bytes Arrays Utils\n * @author Gonçalo Sá \n *\n * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.\n * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.\n */\npragma solidity >=0.8.0 <0.9.0;\n\n\nlibrary BytesLib {\n function concat(\n bytes memory _preBytes,\n bytes memory _postBytes\n )\n internal\n pure\n returns (bytes memory)\n {\n bytes memory tempBytes;\n\n assembly {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // Store the length of the first bytes array at the beginning of\n // the memory for tempBytes.\n let length := mload(_preBytes)\n mstore(tempBytes, length)\n\n // Maintain a memory counter for the current write location in the\n // temp bytes array by adding the 32 bytes for the array length to\n // the starting location.\n let mc := add(tempBytes, 0x20)\n // Stop copying when the memory counter reaches the length of the\n // first bytes array.\n let end := add(mc, length)\n\n for {\n // Initialize a copy counter to the start of the _preBytes data,\n // 32 bytes into its memory.\n let cc := add(_preBytes, 0x20)\n } lt(mc, end) {\n // Increase both counters by 32 bytes each iteration.\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // Write the _preBytes data into the tempBytes memory 32 bytes\n // at a time.\n mstore(mc, mload(cc))\n }\n\n // Add the length of _postBytes to the current length of tempBytes\n // and store it as the new length in the first 32 bytes of the\n // tempBytes memory.\n length := mload(_postBytes)\n mstore(tempBytes, add(length, mload(tempBytes)))\n\n // Move the memory counter back from a multiple of 0x20 to the\n // actual end of the _preBytes data.\n mc := end\n // Stop copying when the memory counter reaches the new combined\n // length of the arrays.\n end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n // Update the free-memory pointer by padding our last write location\n // to 32 bytes: add 31 bytes to the end of tempBytes to move to the\n // next 32 byte block, then round down to the nearest multiple of\n // 32. If the sum of the length of the two arrays is zero then add\n // one before rounding down to leave a blank 32 bytes (the length block with 0).\n mstore(0x40, and(\n add(add(end, iszero(add(length, mload(_preBytes)))), 31),\n not(31) // Round down to the nearest 32 bytes.\n ))\n }\n\n return tempBytes;\n }\n\n function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {\n assembly {\n // Read the first 32 bytes of _preBytes storage, which is the length\n // of the array. (We don't need to use the offset into the slot\n // because arrays use the entire slot.)\n let fslot := sload(_preBytes.slot)\n // Arrays of 31 bytes or less have an even value in their slot,\n // while longer arrays have an odd value. The actual length is\n // the slot divided by two for odd values, and the lowest order\n // byte divided by two for even values.\n // If the slot is even, bitwise and the slot with 255 and divide by\n // two to get the length. If the slot is odd, bitwise and the slot\n // with -1 and divide by two.\n let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)\n let mlength := mload(_postBytes)\n let newlength := add(slength, mlength)\n // slength can contain both the length and contents of the array\n // if length < 32 bytes so let's prepare for that\n // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage\n switch add(lt(slength, 32), lt(newlength, 32))\n case 2 {\n // Since the new array still fits in the slot, we just need to\n // update the contents of the slot.\n // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length\n sstore(\n _preBytes.slot,\n // all the modifications to the slot are inside this\n // next block\n add(\n // we can just add to the slot contents because the\n // bytes we want to change are the LSBs\n fslot,\n add(\n mul(\n div(\n // load the bytes from memory\n mload(add(_postBytes, 0x20)),\n // zero all bytes to the right\n exp(0x100, sub(32, mlength))\n ),\n // and now shift left the number of bytes to\n // leave space for the length in the slot\n exp(0x100, sub(32, newlength))\n ),\n // increase length by the double of the memory\n // bytes length\n mul(mlength, 2)\n )\n )\n )\n }\n case 1 {\n // The stored value fits in the slot, but the combined value\n // will exceed it.\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n let sc := add(keccak256(0x0, 0x20), div(slength, 32))\n\n // save new length\n sstore(_preBytes.slot, add(mul(newlength, 2), 1))\n\n // The contents of the _postBytes array start 32 bytes into\n // the structure. Our first read should obtain the `submod`\n // bytes that can fit into the unused space in the last word\n // of the stored array. To get this, we read 32 bytes starting\n // from `submod`, so the data we read overlaps with the array\n // contents by `submod` bytes. Masking the lowest-order\n // `submod` bytes allows us to add that value directly to the\n // stored value.\n\n let submod := sub(32, slength)\n let mc := add(_postBytes, submod)\n let end := add(_postBytes, mlength)\n let mask := sub(exp(0x100, submod), 1)\n\n sstore(\n sc,\n add(\n and(\n fslot,\n 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00\n ),\n and(mload(mc), mask)\n )\n )\n\n for {\n mc := add(mc, 0x20)\n sc := add(sc, 1)\n } lt(mc, end) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n sstore(sc, mload(mc))\n }\n\n mask := exp(0x100, sub(mc, end))\n\n sstore(sc, mul(div(mload(mc), mask), mask))\n }\n default {\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n // Start copying to the last used word of the stored array.\n let sc := add(keccak256(0x0, 0x20), div(slength, 32))\n\n // save new length\n sstore(_preBytes.slot, add(mul(newlength, 2), 1))\n\n // Copy over the first `submod` bytes of the new data as in\n // case 1 above.\n let slengthmod := mod(slength, 32)\n let mlengthmod := mod(mlength, 32)\n let submod := sub(32, slengthmod)\n let mc := add(_postBytes, submod)\n let end := add(_postBytes, mlength)\n let mask := sub(exp(0x100, submod), 1)\n\n sstore(sc, add(sload(sc), and(mload(mc), mask)))\n\n for {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } lt(mc, end) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n sstore(sc, mload(mc))\n }\n\n mask := exp(0x100, sub(mc, end))\n\n sstore(sc, mul(div(mload(mc), mask), mask))\n }\n }\n }\n\n function slice(\n bytes memory _bytes,\n uint256 _start,\n uint256 _length\n )\n internal\n pure\n returns (bytes memory)\n {\n // We're using the unchecked block below because otherwise execution ends \n // with the native overflow error code.\n unchecked {\n require(_length + 31 >= _length, \"slice_overflow\");\n }\n require(_bytes.length >= _start + _length, \"slice_outOfBounds\");\n\n bytes memory tempBytes;\n\n assembly {\n switch iszero(_length)\n case 0 {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // The first word of the slice result is potentially a partial\n // word read from the original array. To read it, we calculate\n // the length of that partial word and start copying that many\n // bytes into the array. The first word we copy will start with\n // data we don't care about, but the last `lengthmod` bytes will\n // land at the beginning of the contents of the new array. When\n // we're done copying, we overwrite the full first word with\n // the actual length of the slice.\n let lengthmod := and(_length, 31)\n\n // The multiplication in the next line is necessary\n // because when slicing multiples of 32 bytes (lengthmod == 0)\n // the following copy loop was copying the origin's length\n // and then ending prematurely not copying everything it should.\n let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))\n let end := add(mc, _length)\n\n for {\n // The multiplication in the next line has the same exact purpose\n // as the one above.\n let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n mstore(tempBytes, _length)\n\n //update free-memory pointer\n //allocating the array padded to 32 bytes like the compiler does now\n mstore(0x40, and(add(mc, 31), not(31)))\n }\n //if we want a zero-length slice let's just return a zero-length array\n default {\n tempBytes := mload(0x40)\n //zero out the 32 bytes slice we are about to return\n //we need to do it because Solidity does not garbage collect\n mstore(tempBytes, 0)\n\n mstore(0x40, add(tempBytes, 0x20))\n }\n }\n\n return tempBytes;\n }\n\n function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {\n require(_bytes.length >= _start + 20, \"toAddress_outOfBounds\");\n address tempAddress;\n\n assembly {\n tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)\n }\n\n return tempAddress;\n }\n\n function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {\n require(_bytes.length >= _start + 1 , \"toUint8_outOfBounds\");\n uint8 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x1), _start))\n }\n\n return tempUint;\n }\n\n function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {\n require(_bytes.length >= _start + 2, \"toUint16_outOfBounds\");\n uint16 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x2), _start))\n }\n\n return tempUint;\n }\n\n function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {\n require(_bytes.length >= _start + 4, \"toUint32_outOfBounds\");\n uint32 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x4), _start))\n }\n\n return tempUint;\n }\n\n function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {\n require(_bytes.length >= _start + 8, \"toUint64_outOfBounds\");\n uint64 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x8), _start))\n }\n\n return tempUint;\n }\n\n function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {\n require(_bytes.length >= _start + 12, \"toUint96_outOfBounds\");\n uint96 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0xc), _start))\n }\n\n return tempUint;\n }\n\n function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {\n require(_bytes.length >= _start + 16, \"toUint128_outOfBounds\");\n uint128 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x10), _start))\n }\n\n return tempUint;\n }\n\n function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {\n require(_bytes.length >= _start + 32, \"toUint256_outOfBounds\");\n uint256 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x20), _start))\n }\n\n return tempUint;\n }\n\n function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {\n require(_bytes.length >= _start + 32, \"toBytes32_outOfBounds\");\n bytes32 tempBytes32;\n\n assembly {\n tempBytes32 := mload(add(add(_bytes, 0x20), _start))\n }\n\n return tempBytes32;\n }\n\n function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {\n bool success = true;\n\n assembly {\n let length := mload(_preBytes)\n\n // if lengths don't match the arrays are not equal\n switch eq(length, mload(_postBytes))\n case 1 {\n // cb is a circuit breaker in the for loop since there's\n // no said feature for inline assembly loops\n // cb = 1 - don't breaker\n // cb = 0 - break\n let cb := 1\n\n let mc := add(_preBytes, 0x20)\n let end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n // the next line is the loop condition:\n // while(uint256(mc < end) + cb == 2)\n } eq(add(lt(mc, end), cb), 2) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // if any of these checks fails then arrays are not equal\n if iszero(eq(mload(mc), mload(cc))) {\n // unsuccess:\n success := 0\n cb := 0\n }\n }\n }\n default {\n // unsuccess:\n success := 0\n }\n }\n\n return success;\n }\n\n function equalStorage(\n bytes storage _preBytes,\n bytes memory _postBytes\n )\n internal\n view\n returns (bool)\n {\n bool success = true;\n\n assembly {\n // we know _preBytes_offset is 0\n let fslot := sload(_preBytes.slot)\n // Decode the length of the stored array like in concatStorage().\n let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)\n let mlength := mload(_postBytes)\n\n // if lengths don't match the arrays are not equal\n switch eq(slength, mlength)\n case 1 {\n // slength can contain both the length and contents of the array\n // if length < 32 bytes so let's prepare for that\n // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage\n if iszero(iszero(slength)) {\n switch lt(slength, 32)\n case 1 {\n // blank the last byte which is the length\n fslot := mul(div(fslot, 0x100), 0x100)\n\n if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {\n // unsuccess:\n success := 0\n }\n }\n default {\n // cb is a circuit breaker in the for loop since there's\n // no said feature for inline assembly loops\n // cb = 1 - don't breaker\n // cb = 0 - break\n let cb := 1\n\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n let sc := keccak256(0x0, 0x20)\n\n let mc := add(_postBytes, 0x20)\n let end := add(mc, mlength)\n\n // the next line is the loop condition:\n // while(uint256(mc < end) + cb == 2)\n for {} eq(add(lt(mc, end), cb), 2) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n if iszero(eq(sload(sc), mload(mc))) {\n // unsuccess:\n success := 0\n cb := 0\n }\n }\n }\n }\n }\n default {\n // unsuccess:\n success := 0\n }\n }\n\n return success;\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/d8e9ed882273e91bb791fcc0c3929abb.json b/contracts/deployments/hoodi/solcInputs/d8e9ed882273e91bb791fcc0c3929abb.json new file mode 100644 index 0000000000..8e9e9ee44c --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/d8e9ed882273e91bb791fcc0c3929abb.json @@ -0,0 +1,99 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to retrieve beacon block roots.\n * @author Origin Protocol Inc\n */\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the beacon block root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Roots contract to get the parent block root.\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to request full or partial withdrawals from validators on the beacon chain.\n * @author Origin Protocol Inc\n */\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n enum BalanceProofLevel {\n Container,\n BeaconBlock\n }\n\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view returns (bool isEmptyDepositQueue);\n\n function verifyBlockNumber(\n bytes32 beaconBlockRoot,\n uint256 blockNumber,\n bytes calldata blockNumberProof\n ) external view;\n\n function verifySlot(\n bytes32 beaconBlockRoot,\n uint256 slot,\n bytes calldata slotProof\n ) external view;\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n external\n pure\n returns (uint256);\n\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) external pure returns (uint256);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconProofs,\n _beaconGenesisTimestamp\n )\n {\n SSV_TOKEN = _ssvToken;\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// @param _rewardTokenAddresses Not used so empty array\n /// @param _assets Not used so empty array\n /// @param _pTokens Not used so empty array\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH that was transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _asset, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(_asset, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, WETH, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Accounts for all the assets managed by this strategy which includes:\n /// 1. The current WETH in this strategy contract\n /// 2. The last verified ETH balance, total deposits and total validator balances\n /// @param _asset Address of WETH asset.\n /// @return balance Total value in ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by the strategy.\n /// @param _asset The address of the WETH token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for validator registration.\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens when validators are registered\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n * The new ETH will be accounted for in `checkBalance` after the next snapBalances and verifyBalances txs.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n /// @dev This strategy does not use a platform token like the old Aave and Compound strategies.\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instead of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract.\n /// Withdrawals from validators also accumulate as ETH in this strategy contract.\n /// It's too complex to separate the rewards from withdrawals so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can now regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable {\n using SafeERC20 for IERC20;\n\n /// @notice The amount of ETH in wei that is required for a deposit to a new validator.\n /// Initially this is 32 ETH, but will be increased to 1 ETH after P2P's APIs have been updated\n /// to support deposits of 1 ETH.\n uint256 public constant DEPOSIT_AMOUNT_WEI = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n /// @notice The timestamp of the Beacon chain genesis.\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\n uint64 public immutable BEACON_GENESIS_TIMESTAMP;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// Deposit data for new compounding validators.\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountWei Amount of ETH in wei that has been deposited to the beacon chain deposit contract\n /// @param blockNumber Block number when the deposit was made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either PENDING or PROVEN\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint32 depositIndex;\n DepositStatus status;\n }\n /// @notice Mapping of the root of a deposit (depositDataRoot) to its data\n mapping(bytes32 => DepositData) public deposits;\n /// @notice List of deposit roots that are still to be verified as processed on the beacon chain\n bytes32[] public depositsRoots;\n\n // Validator data\n\n struct ValidatorData {\n bytes32 pubKeyHash; // Hash of the validator's public key using the Beacon Chain's format\n uint64 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes and indexes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n ValidatorData[] internal verifiedValidators;\n /// @notice State of the new compounding validators with a 0x02 withdrawal credential prefix.\n /// Uses the Beacon chain hashing for BLSPubkey which is\n /// sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => VALIDATOR_STATE) public validatorState;\n\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n uint64 timestamp;\n uint128 ethBalance;\n }\n // TODO is it more efficient to use the block root rather than hashing it?\n /// @notice Mapping of the block root to the balances at that slot\n mapping(bytes32 => Balances) public snappedBalances;\n uint64 public lastSnapTimestamp;\n uint128 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[50] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITED, // The validator has been verified to have a zero balance\n REMOVED // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified and is ready to be staked\n }\n\n event RegistratorChanged(address indexed newAddress);\n event SourceStrategyAdded(address indexed strategy);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n bytes32 indexed depositDataRoot,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint64 indexed validatorIndex\n );\n event DepositVerified(bytes32 indexed depositDataRoot, uint256 amountWei);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(\n uint256 indexed timestamp,\n bytes32 indexed blockRoot,\n uint256 ethBalance\n );\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 wethBalance,\n uint256 ethBalance\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_PROOFS = _beaconProofs;\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\n\n require(\n block.timestamp > _beaconGenesisTimestamp,\n \"Invalid genesis timestamp\"\n );\n }\n\n /***************************************\n Admin Functions\n ****************************************/\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /***************************************`\n Validator Management\n ****************************************/\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validatorState[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validator validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validator,\n uint64 depositAmountGwei\n ) external onlyRegistrator {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validator.pubkey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == VALIDATOR_STATE.REGISTERED ||\n currentState == VALIDATOR_STATE.VERIFIED),\n \"Not registered or verified\"\n );\n require(\n currentState == VALIDATOR_STATE.VERIFIED ||\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validator.pubkey,\n withdrawalCredentials,\n validator.signature,\n validator.depositDataRoot\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == VALIDATOR_STATE.REGISTERED) {\n validatorState[pubKeyHash] = VALIDATOR_STATE.STAKED;\n }\n\n /// After the Pectra upgrade the validators have a new restriction when proposing\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\n /// forward. Each slot is created at strict 12 second intervals and those slots can\n /// either have blocks attached to them or not. This way using the block.timestamp\n /// the slot number can easily be calculated.\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\n BEACON_GENESIS_TIMESTAMP) / 12;\n\n // Store the deposit data for verifyDeposit and verifyBalances\n deposits[validator.depositDataRoot] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n slot: depositSlot,\n depositIndex: SafeCast.toUint32(depositsRoots.length),\n status: DepositStatus.PENDING\n });\n depositsRoots.push(validator.depositDataRoot);\n\n emit ETHStaked(\n pubKeyHash,\n validator.depositDataRoot,\n validator.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then the request will fail.\n /// Only the Registrator can call this function.\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\n /// If no ETH balance, the tx will revert.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n payable\n onlyRegistrator\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove a sweeping validator from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == VALIDATOR_STATE.EXITED ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exited\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validatorState[pubKeyHash] = VALIDATOR_STATE.REMOVED;\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /***************************************\n SSV Management\n ****************************************/\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n Beacon Chain Proofs\n ****************************************/\n\n /// @notice Verifies a validator's index to its public key.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.STAKED,\n \"Validator not staked\"\n );\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key.\n // Also verify the validator's withdrawal credential points to this strategy.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n address(this) // Withdrawal address is this strategy\n );\n\n // Store the validator state as verified\n validatorState[pubKeyHash] = VALIDATOR_STATE.VERIFIED;\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(\n ValidatorData({ pubKeyHash: pubKeyHash, index: validatorIndex })\n );\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n ///\n /// Important: this function has a limitation where the `verificationSlot` that is passed by the off-chain\n /// verifier requires a slot immediately after it to propose a block otherwise the `BeaconRoots.parentBlockRoot`\n /// will fail. This shouldn't be a problem, since by the current behaviour of beacon chain only 1%-3% slots\n /// don't propose a block.\n /// @param depositDataRoot The root of the deposit data that was stored when\n /// the deposit was made on the execution layer.\n /// @param verificationSlot Any slot on or after the deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\n /// set for the next block timestamp in 12 seconds time.\n /// @param firstPendingDepositSlot The slot of the first pending deposit in the beacon chain.\n /// Can be anything if the deposit queue is empty, but zero is a good choice.\n /// @param firstPendingDepositSlotProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n bytes32 depositDataRoot,\n uint64 verificationSlot,\n uint64 firstPendingDepositSlot,\n bytes calldata firstPendingDepositSlotProof\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositDataRoot];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n validatorState[deposit.pubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n // The verification slot must be after the deposit's slot.\n // This is needed for when the deposit queue is empty.\n require(deposit.slot < verificationSlot, \"Slot not after deposit\");\n\n // Calculate the next block timestamp from the verification slot.\n uint64 nextBlockTimestamp = 12 *\n verificationSlot +\n BEACON_GENESIS_TIMESTAMP +\n // Add 12 seconds for the next block\n 12;\n // Get the parent beacon block root of the next block which is the block root of the verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDepositSlot(\n blockRoot,\n firstPendingDepositSlot,\n firstPendingDepositSlotProof\n );\n\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the deposit queue is empty then our deposit must have been processed on the beacon chain.\n // The deposit slot can be zero for validators consolidating to a compounding validator. We can\n // not guarantee that the deposit has been processed in that case.\n require(\n deposit.slot < firstPendingDepositSlot || isDepositQueueEmpty,\n \"Deposit likely not processed\"\n );\n\n // After verifying the proof, update the contract storage\n deposits[depositDataRoot].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n bytes32 lastDepositDataRoot = depositsRoots[depositsRoots.length - 1];\n depositsRoots[deposit.depositIndex] = lastDepositDataRoot;\n deposits[lastDepositDataRoot].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositsRoots.pop();\n\n emit DepositVerified(\n depositDataRoot,\n uint256(deposit.amountGwei) * 1 gwei\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block and beacon block root\n /// of the slot that is associated with the previous block.\n ///\n /// When snapping / verifying balance it is of a high importance that there is no\n /// miss-match in respect to ETH that is held by the contract and balances that are\n /// verified on the validators.\n ///\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\n /// constructing a block on the beacon chain consist of:\n /// - process_withdrawals: ETH is deducted from the validator's balance\n /// - process_execution_payload: immediately after the previous step executing all the\n /// transactions\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\n /// contained in the withdrawal credentials of the exited validators\n ///\n /// That means that balance increases which are part of the post-block execution state are\n /// done within the block, but the transaction that are contained within that block can not\n /// see / interact with the balance from the exited validators. Only transactions in the\n /// next block can do that.\n ///\n /// When snap balances is performed the state of the chain is snapped across 2 separate\n /// chain states:\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\n /// will be referred to as Y - 1 as it makes no difference in the argument\n ///\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\n /// miss-counting ETH or much more dangerous double counting of the ETH.\n ///\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\n /// - ETH balance in the contract on block X\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\n /// - ETH balance in validators that are active in slot Y - 1\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\n /// and have their balance visible to transactions in slot Y and corresponding block X\n /// (or sooner)\n ///\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\n ///\n /// *ETH balance in the contract on block X*\n ///\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\n ///\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\n /// would result in double counting the `stakedEth` since it would be present once in the\n /// snapped contract balance and the second time in deposit storage variables.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\n ///\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\n /// contract at block Z. The execution layer doesn't have direct access to the state of\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\n /// and could also be part of the validator balances. It does that by verifying that at\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\n /// the last snap till now all are still in queue. Which ensures they can not be part of\n /// the validator balances in later steps.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in validators that are active in slot Y - 1*\n ///\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\n /// correctness.\n ///\n /// This behaviour is correct.\n ///\n /// *The withdrawn validators*\n ///\n /// The withdrawn validators could have their balances deducted in any slot before slot\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\n /// look at the \"worst case scenario\" where the validator withdrawal is processed in the\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\n /// even if the validator exits at the latest possible time it is paramount that the ETH\n /// balance on the execution layer is recorded in the next block. Correctly accounting\n /// for the withdrawn ETH.\n ///\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\n /// validator balance verification as well as execution layer contract balance snap.\n ///\n /// This behaviour is correct.\n ///\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n /// Can only be called by the registrator.\n function snapBalances() external onlyRegistrator {\n _snapBalances();\n }\n\n function _snapBalances() internal {\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(\n SafeCast.toUint64(block.timestamp)\n );\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the balances in the mapping\n snappedBalances[blockRoot] = Balances({\n timestamp: SafeCast.toUint64(block.timestamp),\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n // Store the snapped timestamp\n lastSnapTimestamp = SafeCast.toUint64(block.timestamp);\n\n emit BalancesSnapped(block.timestamp, blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct VerifyBalancesParams {\n bytes32 blockRoot;\n uint64 firstPendingDepositSlot;\n // BeaconBlock.BeaconBlockBody.deposits[0].slot\n bytes firstPendingDepositSlotProof;\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n /// @param params a `VerifyBalancesParams` struct containing the following:\n /// blockRoot - the beacon block root emitted from `snapBalance` in `BalancesSnapped`\n /// firstPendingDepositSlot - The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero would be a good choice.\n /// firstPendingDepositSlotProof - The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// balancesContainerRoot - the merkle root of the balances container\n /// balancesContainerProof - The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// validatorBalanceLeaves - Array of leaf nodes containing the validator balance with three other balances.\n /// validatorBalanceProofs - Array of merkle proofs for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(VerifyBalancesParams calldata params) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalances[params.blockRoot];\n // Check the balances are the latest\n require(lastSnapTimestamp > 0, \"No snapped balances\");\n require(balancesMem.timestamp == lastSnapTimestamp, \"Stale snap\");\n\n uint256 depositsCount = depositsRoots.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit to the beacon block root\n bool isEmptyDepositQueue = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDepositSlot(\n params.blockRoot,\n params.firstPendingDepositSlot,\n params.firstPendingDepositSlotProof\n );\n\n // If there are no deposits in the beacon chain queue then our deposits must have been processed.\n // If the deposits have been processed, each deposit will need to be verified with `verifyDeposit`\n require(!isEmptyDepositQueue, \"Deposits have been processed\");\n // If a validator is converted from a sweeping validator to a compounding validator, any balance in excess\n // of the min 32 ETH is put in the pending deposit queue. This will have a slot value of zero unfortunately.\n // We can not prove the strategy's deposits are still pending with a zero slot value so revert the verification.\n // Another snapBalances will need to be taken that does not have consolidation deposits at the front of the\n // beacon chain deposit queue.\n require(\n params.firstPendingDepositSlot > 0,\n \"Invalid first pending deposit\"\n );\n\n // For each staking strategy's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n bytes32 depositDataRoot = depositsRoots[i];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit slot is before the\n // slot of the staking strategy's deposit.\n // If the deposit has been processed, it will need to be verified with `verifyDeposit`\n require(\n params.firstPendingDepositSlot <\n deposits[depositDataRoot].slot,\n \"Deposit has been processed\"\n );\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei +=\n uint256(deposits[depositDataRoot].amountGwei) *\n 1 gwei;\n }\n }\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n params.validatorBalanceProofs.length == verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n params.validatorBalanceLeaves.length == verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n params.blockRoot,\n params.balancesContainerRoot,\n params.balancesContainerProof\n );\n\n // for each validator in reserve order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n params.balancesContainerRoot,\n params.validatorBalanceLeaves[i],\n params.validatorBalanceProofs[i],\n verifiedValidators[i].index\n );\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n validatorState[\n verifiedValidators[i].pubKeyHash\n ] = VALIDATOR_STATE.EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += uint256(validatorBalanceGwei) * 1 gwei;\n }\n }\n\n uint256 wethBalance = IWETH9(WETH).balanceOf(address(this));\n\n // Store the verified balance in storage\n lastVerifiedEthBalance = SafeCast.toUint128(\n totalDepositsWei + totalValidatorBalance + balancesMem.ethBalance\n );\n // Reset the last snap timestamp so a new snapBalances has to be made\n lastSnapTimestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n wethBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /***************************************\n WETH and ETH Accounting\n ****************************************/\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance\n if (lastVerifiedEthBalance > _ethAmount) {\n lastVerifiedEthBalance -= SafeCast.toUint128(_ethAmount);\n } else {\n // This can happen if all ETH in the validators was withdrawn\n // and there was more consensus rewards since the last balance verification.\n // Or it can happen if ETH is donated to this strategy.\n lastVerifiedEthBalance = 0;\n }\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += SafeCast.toUint128(_wethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /***************************************\n View Functions\n ****************************************/\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return An array of `ValidatorData` containing the public key hash and validator index.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorData[] memory)\n {\n return verifiedValidators;\n }\n\n struct DepositView {\n bytes32 root;\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n }\n\n /// @notice Returns the deposits that are still to be verified.\n /// These may or may not have been processed by the beacon chain.\n /// @return pendingDeposits An array of `DepositView` containing the deposit root, public key hash,\n /// amount in Gwei and the slot of the deposit.\n function getPendingDeposits()\n external\n view\n returns (DepositView[] memory pendingDeposits)\n {\n pendingDeposits = new DepositView[](depositsRoots.length);\n for (uint256 i = 0; i < depositsRoots.length; ++i) {\n DepositData memory deposit = deposits[depositsRoots[i]];\n pendingDeposits[i] = DepositView({\n root: depositsRoots[i],\n pubKeyHash: deposit.pubKeyHash,\n amountGwei: deposit.amountGwei,\n slot: deposit.slot\n });\n }\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/de6d2f00431b04abbd2234fb96d2b847.json b/contracts/deployments/hoodi/solcInputs/de6d2f00431b04abbd2234fb96d2b847.json new file mode 100644 index 0000000000..469bb2a878 --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/de6d2f00431b04abbd2234fb96d2b847.json @@ -0,0 +1,1149 @@ +{ + "language": "Solidity", + "sources": { + "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {Client} from \"../libraries/Client.sol\";\n\ninterface IRouterClient {\n error UnsupportedDestinationChain(uint64 destChainSelector);\n error InsufficientFeeTokenAmount();\n error InvalidMsgValue();\n\n /// @notice Checks if the given chain ID is supported for sending/receiving.\n /// @param chainSelector The chain to check.\n /// @return supported is true if it is supported, false if not.\n function isChainSupported(uint64 chainSelector) external view returns (bool supported);\n\n /// @notice Gets a list of all supported tokens which can be sent or received\n /// to/from a given chain id.\n /// @param chainSelector The chainSelector.\n /// @return tokens The addresses of all tokens that are supported.\n function getSupportedTokens(uint64 chainSelector) external view returns (address[] memory tokens);\n\n /// @param destinationChainSelector The destination chainSelector\n /// @param message The cross-chain CCIP message including data and/or tokens\n /// @return fee returns execution fee for the message\n /// delivery to destination chain, denominated in the feeToken specified in the message.\n /// @dev Reverts with appropriate reason upon invalid message.\n function getFee(\n uint64 destinationChainSelector,\n Client.EVM2AnyMessage memory message\n ) external view returns (uint256 fee);\n\n /// @notice Request a message to be sent to the destination chain\n /// @param destinationChainSelector The destination chain ID\n /// @param message The cross-chain CCIP message including data and/or tokens\n /// @return messageId The message ID\n /// @dev Note if msg.value is larger than the required fee (from getFee) we accept\n /// the overpayment with no refund.\n /// @dev Reverts with appropriate reason upon invalid message.\n function ccipSend(\n uint64 destinationChainSelector,\n Client.EVM2AnyMessage calldata message\n ) external payable returns (bytes32);\n}\n" + }, + "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// End consumer library.\nlibrary Client {\n /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.\n struct EVMTokenAmount {\n address token; // token address on the local chain.\n uint256 amount; // Amount of tokens.\n }\n\n struct Any2EVMMessage {\n bytes32 messageId; // MessageId corresponding to ccipSend on source.\n uint64 sourceChainSelector; // Source chain selector.\n bytes sender; // abi.decode(sender) if coming from an EVM chain.\n bytes data; // payload sent in original message.\n EVMTokenAmount[] destTokenAmounts; // Tokens and their amounts in their destination chain representation.\n }\n\n // If extraArgs is empty bytes, the default is 200k gas limit.\n struct EVM2AnyMessage {\n bytes receiver; // abi.encode(receiver address) for dest EVM chains\n bytes data; // Data payload\n EVMTokenAmount[] tokenAmounts; // Token transfers\n address feeToken; // Address of feeToken. address(0) means you will send msg.value.\n bytes extraArgs; // Populate this with _argsToBytes(EVMExtraArgsV1)\n }\n\n // bytes4(keccak256(\"CCIP EVMExtraArgsV1\"));\n bytes4 public constant EVM_EXTRA_ARGS_V1_TAG = 0x97a657c9;\n struct EVMExtraArgsV1 {\n uint256 gasLimit;\n }\n\n function _argsToBytes(EVMExtraArgsV1 memory extraArgs) internal pure returns (bytes memory bts) {\n return abi.encodeWithSelector(EVM_EXTRA_ARGS_V1_TAG, extraArgs);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport \"@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol\";\n\nlibrary ExecutorOptions {\n using CalldataBytesLib for bytes;\n\n uint8 internal constant WORKER_ID = 1;\n\n uint8 internal constant OPTION_TYPE_LZRECEIVE = 1;\n uint8 internal constant OPTION_TYPE_NATIVE_DROP = 2;\n uint8 internal constant OPTION_TYPE_LZCOMPOSE = 3;\n uint8 internal constant OPTION_TYPE_ORDERED_EXECUTION = 4;\n uint8 internal constant OPTION_TYPE_LZREAD = 5;\n\n error Executor_InvalidLzReceiveOption();\n error Executor_InvalidNativeDropOption();\n error Executor_InvalidLzComposeOption();\n error Executor_InvalidLzReadOption();\n\n /// @dev decode the next executor option from the options starting from the specified cursor\n /// @param _options [executor_id][executor_option][executor_id][executor_option]...\n /// executor_option = [option_size][option_type][option]\n /// option_size = len(option_type) + len(option)\n /// executor_id: uint8, option_size: uint16, option_type: uint8, option: bytes\n /// @param _cursor the cursor to start decoding from\n /// @return optionType the type of the option\n /// @return option the option of the executor\n /// @return cursor the cursor to start decoding the next executor option\n function nextExecutorOption(\n bytes calldata _options,\n uint256 _cursor\n ) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) {\n unchecked {\n // skip worker id\n cursor = _cursor + 1;\n\n // read option size\n uint16 size = _options.toU16(cursor);\n cursor += 2;\n\n // read option type\n optionType = _options.toU8(cursor);\n\n // startCursor and endCursor are used to slice the option from _options\n uint256 startCursor = cursor + 1; // skip option type\n uint256 endCursor = cursor + size;\n option = _options[startCursor:endCursor];\n cursor += size;\n }\n }\n\n function decodeLzReceiveOption(bytes calldata _option) internal pure returns (uint128 gas, uint128 value) {\n if (_option.length != 16 && _option.length != 32) revert Executor_InvalidLzReceiveOption();\n gas = _option.toU128(0);\n value = _option.length == 32 ? _option.toU128(16) : 0;\n }\n\n function decodeNativeDropOption(bytes calldata _option) internal pure returns (uint128 amount, bytes32 receiver) {\n if (_option.length != 48) revert Executor_InvalidNativeDropOption();\n amount = _option.toU128(0);\n receiver = _option.toB32(16);\n }\n\n function decodeLzComposeOption(\n bytes calldata _option\n ) internal pure returns (uint16 index, uint128 gas, uint128 value) {\n if (_option.length != 18 && _option.length != 34) revert Executor_InvalidLzComposeOption();\n index = _option.toU16(0);\n gas = _option.toU128(2);\n value = _option.length == 34 ? _option.toU128(18) : 0;\n }\n\n function decodeLzReadOption(\n bytes calldata _option\n ) internal pure returns (uint128 gas, uint32 calldataSize, uint128 value) {\n if (_option.length != 20 && _option.length != 36) revert Executor_InvalidLzReadOption();\n gas = _option.toU128(0);\n calldataSize = _option.toU32(16);\n value = _option.length == 36 ? _option.toU128(20) : 0;\n }\n\n function encodeLzReceiveOption(uint128 _gas, uint128 _value) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_gas) : abi.encodePacked(_gas, _value);\n }\n\n function encodeNativeDropOption(uint128 _amount, bytes32 _receiver) internal pure returns (bytes memory) {\n return abi.encodePacked(_amount, _receiver);\n }\n\n function encodeLzComposeOption(uint16 _index, uint128 _gas, uint128 _value) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_index, _gas) : abi.encodePacked(_index, _gas, _value);\n }\n\n function encodeLzReadOption(\n uint128 _gas,\n uint32 _calldataSize,\n uint128 _value\n ) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_gas, _calldataSize) : abi.encodePacked(_gas, _calldataSize, _value);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/DVNOptions.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport { BytesLib } from \"solidity-bytes-utils/contracts/BytesLib.sol\";\n\nimport { BitMap256 } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/BitMaps.sol\";\nimport { CalldataBytesLib } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol\";\n\nlibrary DVNOptions {\n using CalldataBytesLib for bytes;\n using BytesLib for bytes;\n\n uint8 internal constant WORKER_ID = 2;\n uint8 internal constant OPTION_TYPE_PRECRIME = 1;\n\n error DVN_InvalidDVNIdx();\n error DVN_InvalidDVNOptions(uint256 cursor);\n\n /// @dev group dvn options by its idx\n /// @param _options [dvn_id][dvn_option][dvn_id][dvn_option]...\n /// dvn_option = [option_size][dvn_idx][option_type][option]\n /// option_size = len(dvn_idx) + len(option_type) + len(option)\n /// dvn_id: uint8, dvn_idx: uint8, option_size: uint16, option_type: uint8, option: bytes\n /// @return dvnOptions the grouped options, still share the same format of _options\n /// @return dvnIndices the dvn indices\n function groupDVNOptionsByIdx(\n bytes memory _options\n ) internal pure returns (bytes[] memory dvnOptions, uint8[] memory dvnIndices) {\n if (_options.length == 0) return (dvnOptions, dvnIndices);\n\n uint8 numDVNs = getNumDVNs(_options);\n\n // if there is only 1 dvn, we can just return the whole options\n if (numDVNs == 1) {\n dvnOptions = new bytes[](1);\n dvnOptions[0] = _options;\n\n dvnIndices = new uint8[](1);\n dvnIndices[0] = _options.toUint8(3); // dvn idx\n return (dvnOptions, dvnIndices);\n }\n\n // otherwise, we need to group the options by dvn_idx\n dvnIndices = new uint8[](numDVNs);\n dvnOptions = new bytes[](numDVNs);\n unchecked {\n uint256 cursor = 0;\n uint256 start = 0;\n uint8 lastDVNIdx = 255; // 255 is an invalid dvn_idx\n\n while (cursor < _options.length) {\n ++cursor; // skip worker_id\n\n // optionLength asserted in getNumDVNs (skip check)\n uint16 optionLength = _options.toUint16(cursor);\n cursor += 2;\n\n // dvnIdx asserted in getNumDVNs (skip check)\n uint8 dvnIdx = _options.toUint8(cursor);\n\n // dvnIdx must equal to the lastDVNIdx for the first option\n // so it is always skipped in the first option\n // this operation slices out options whenever the scan finds a different lastDVNIdx\n if (lastDVNIdx == 255) {\n lastDVNIdx = dvnIdx;\n } else if (dvnIdx != lastDVNIdx) {\n uint256 len = cursor - start - 3; // 3 is for worker_id and option_length\n bytes memory opt = _options.slice(start, len);\n _insertDVNOptions(dvnOptions, dvnIndices, lastDVNIdx, opt);\n\n // reset the start and lastDVNIdx\n start += len;\n lastDVNIdx = dvnIdx;\n }\n\n cursor += optionLength;\n }\n\n // skip check the cursor here because the cursor is asserted in getNumDVNs\n // if we have reached the end of the options, we need to process the last dvn\n uint256 size = cursor - start;\n bytes memory op = _options.slice(start, size);\n _insertDVNOptions(dvnOptions, dvnIndices, lastDVNIdx, op);\n\n // revert dvnIndices to start from 0\n for (uint8 i = 0; i < numDVNs; ++i) {\n --dvnIndices[i];\n }\n }\n }\n\n function _insertDVNOptions(\n bytes[] memory _dvnOptions,\n uint8[] memory _dvnIndices,\n uint8 _dvnIdx,\n bytes memory _newOptions\n ) internal pure {\n // dvnIdx starts from 0 but default value of dvnIndices is 0,\n // so we tell if the slot is empty by adding 1 to dvnIdx\n if (_dvnIdx == 255) revert DVN_InvalidDVNIdx();\n uint8 dvnIdxAdj = _dvnIdx + 1;\n\n for (uint256 j = 0; j < _dvnIndices.length; ++j) {\n uint8 index = _dvnIndices[j];\n if (dvnIdxAdj == index) {\n _dvnOptions[j] = abi.encodePacked(_dvnOptions[j], _newOptions);\n break;\n } else if (index == 0) {\n // empty slot, that means it is the first time we see this dvn\n _dvnIndices[j] = dvnIdxAdj;\n _dvnOptions[j] = _newOptions;\n break;\n }\n }\n }\n\n /// @dev get the number of unique dvns\n /// @param _options the format is the same as groupDVNOptionsByIdx\n function getNumDVNs(bytes memory _options) internal pure returns (uint8 numDVNs) {\n uint256 cursor = 0;\n BitMap256 bitmap;\n\n // find number of unique dvn_idx\n unchecked {\n while (cursor < _options.length) {\n ++cursor; // skip worker_id\n\n uint16 optionLength = _options.toUint16(cursor);\n cursor += 2;\n if (optionLength < 2) revert DVN_InvalidDVNOptions(cursor); // at least 1 byte for dvn_idx and 1 byte for option_type\n\n uint8 dvnIdx = _options.toUint8(cursor);\n\n // if dvnIdx is not set, increment numDVNs\n // max num of dvns is 255, 255 is an invalid dvn_idx\n // The order of the dvnIdx is not required to be sequential, as enforcing the order may weaken\n // the composability of the options. e.g. if we refrain from enforcing the order, an OApp that has\n // already enforced certain options can append additional options to the end of the enforced\n // ones without restrictions.\n if (dvnIdx == 255) revert DVN_InvalidDVNIdx();\n if (!bitmap.get(dvnIdx)) {\n ++numDVNs;\n bitmap = bitmap.set(dvnIdx);\n }\n\n cursor += optionLength;\n }\n }\n if (cursor != _options.length) revert DVN_InvalidDVNOptions(cursor);\n }\n\n /// @dev decode the next dvn option from _options starting from the specified cursor\n /// @param _options the format is the same as groupDVNOptionsByIdx\n /// @param _cursor the cursor to start decoding\n /// @return optionType the type of the option\n /// @return option the option\n /// @return cursor the cursor to start decoding the next option\n function nextDVNOption(\n bytes calldata _options,\n uint256 _cursor\n ) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) {\n unchecked {\n // skip worker id\n cursor = _cursor + 1;\n\n // read option size\n uint16 size = _options.toU16(cursor);\n cursor += 2;\n\n // read option type\n optionType = _options.toU8(cursor + 1); // skip dvn_idx\n\n // startCursor and endCursor are used to slice the option from _options\n uint256 startCursor = cursor + 2; // skip option type and dvn_idx\n uint256 endCursor = cursor + size;\n option = _options[startCursor:endCursor];\n cursor += size;\n }\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { IMessageLibManager } from \"./IMessageLibManager.sol\";\nimport { IMessagingComposer } from \"./IMessagingComposer.sol\";\nimport { IMessagingChannel } from \"./IMessagingChannel.sol\";\nimport { IMessagingContext } from \"./IMessagingContext.sol\";\n\nstruct MessagingParams {\n uint32 dstEid;\n bytes32 receiver;\n bytes message;\n bytes options;\n bool payInLzToken;\n}\n\nstruct MessagingReceipt {\n bytes32 guid;\n uint64 nonce;\n MessagingFee fee;\n}\n\nstruct MessagingFee {\n uint256 nativeFee;\n uint256 lzTokenFee;\n}\n\nstruct Origin {\n uint32 srcEid;\n bytes32 sender;\n uint64 nonce;\n}\n\ninterface ILayerZeroEndpointV2 is IMessageLibManager, IMessagingComposer, IMessagingChannel, IMessagingContext {\n event PacketSent(bytes encodedPayload, bytes options, address sendLibrary);\n\n event PacketVerified(Origin origin, address receiver, bytes32 payloadHash);\n\n event PacketDelivered(Origin origin, address receiver);\n\n event LzReceiveAlert(\n address indexed receiver,\n address indexed executor,\n Origin origin,\n bytes32 guid,\n uint256 gas,\n uint256 value,\n bytes message,\n bytes extraData,\n bytes reason\n );\n\n event LzTokenSet(address token);\n\n event DelegateSet(address sender, address delegate);\n\n function quote(MessagingParams calldata _params, address _sender) external view returns (MessagingFee memory);\n\n function send(\n MessagingParams calldata _params,\n address _refundAddress\n ) external payable returns (MessagingReceipt memory);\n\n function verify(Origin calldata _origin, address _receiver, bytes32 _payloadHash) external;\n\n function verifiable(Origin calldata _origin, address _receiver) external view returns (bool);\n\n function initializable(Origin calldata _origin, address _receiver) external view returns (bool);\n\n function lzReceive(\n Origin calldata _origin,\n address _receiver,\n bytes32 _guid,\n bytes calldata _message,\n bytes calldata _extraData\n ) external payable;\n\n // oapp can burn messages partially by calling this function with its own business logic if messages are verified in order\n function clear(address _oapp, Origin calldata _origin, bytes32 _guid, bytes calldata _message) external;\n\n function setLzToken(address _lzToken) external;\n\n function lzToken() external view returns (address);\n\n function nativeToken() external view returns (address);\n\n function setDelegate(address _delegate) external;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { Origin } from \"./ILayerZeroEndpointV2.sol\";\n\ninterface ILayerZeroReceiver {\n function allowInitializePath(Origin calldata _origin) external view returns (bool);\n\n function nextNonce(uint32 _eid, bytes32 _sender) external view returns (uint64);\n\n function lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) external payable;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessageLib.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { IERC165 } from \"@openzeppelin/contracts/utils/introspection/IERC165.sol\";\n\nimport { SetConfigParam } from \"./IMessageLibManager.sol\";\n\nenum MessageLibType {\n Send,\n Receive,\n SendAndReceive\n}\n\ninterface IMessageLib is IERC165 {\n function setConfig(address _oapp, SetConfigParam[] calldata _config) external;\n\n function getConfig(uint32 _eid, address _oapp, uint32 _configType) external view returns (bytes memory config);\n\n function isSupportedEid(uint32 _eid) external view returns (bool);\n\n // message libs of same major version are compatible\n function version() external view returns (uint64 major, uint8 minor, uint8 endpointVersion);\n\n function messageLibType() external view returns (MessageLibType);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessageLibManager.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nstruct SetConfigParam {\n uint32 eid;\n uint32 configType;\n bytes config;\n}\n\ninterface IMessageLibManager {\n struct Timeout {\n address lib;\n uint256 expiry;\n }\n\n event LibraryRegistered(address newLib);\n event DefaultSendLibrarySet(uint32 eid, address newLib);\n event DefaultReceiveLibrarySet(uint32 eid, address newLib);\n event DefaultReceiveLibraryTimeoutSet(uint32 eid, address oldLib, uint256 expiry);\n event SendLibrarySet(address sender, uint32 eid, address newLib);\n event ReceiveLibrarySet(address receiver, uint32 eid, address newLib);\n event ReceiveLibraryTimeoutSet(address receiver, uint32 eid, address oldLib, uint256 timeout);\n\n function registerLibrary(address _lib) external;\n\n function isRegisteredLibrary(address _lib) external view returns (bool);\n\n function getRegisteredLibraries() external view returns (address[] memory);\n\n function setDefaultSendLibrary(uint32 _eid, address _newLib) external;\n\n function defaultSendLibrary(uint32 _eid) external view returns (address);\n\n function setDefaultReceiveLibrary(uint32 _eid, address _newLib, uint256 _gracePeriod) external;\n\n function defaultReceiveLibrary(uint32 _eid) external view returns (address);\n\n function setDefaultReceiveLibraryTimeout(uint32 _eid, address _lib, uint256 _expiry) external;\n\n function defaultReceiveLibraryTimeout(uint32 _eid) external view returns (address lib, uint256 expiry);\n\n function isSupportedEid(uint32 _eid) external view returns (bool);\n\n function isValidReceiveLibrary(address _receiver, uint32 _eid, address _lib) external view returns (bool);\n\n /// ------------------- OApp interfaces -------------------\n function setSendLibrary(address _oapp, uint32 _eid, address _newLib) external;\n\n function getSendLibrary(address _sender, uint32 _eid) external view returns (address lib);\n\n function isDefaultSendLibrary(address _sender, uint32 _eid) external view returns (bool);\n\n function setReceiveLibrary(address _oapp, uint32 _eid, address _newLib, uint256 _gracePeriod) external;\n\n function getReceiveLibrary(address _receiver, uint32 _eid) external view returns (address lib, bool isDefault);\n\n function setReceiveLibraryTimeout(address _oapp, uint32 _eid, address _lib, uint256 _expiry) external;\n\n function receiveLibraryTimeout(address _receiver, uint32 _eid) external view returns (address lib, uint256 expiry);\n\n function setConfig(address _oapp, address _lib, SetConfigParam[] calldata _params) external;\n\n function getConfig(\n address _oapp,\n address _lib,\n uint32 _eid,\n uint32 _configType\n ) external view returns (bytes memory config);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingChannel.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingChannel {\n event InboundNonceSkipped(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce);\n event PacketNilified(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);\n event PacketBurnt(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);\n\n function eid() external view returns (uint32);\n\n // this is an emergency function if a message cannot be verified for some reasons\n // required to provide _nextNonce to avoid race condition\n function skip(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce) external;\n\n function nilify(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;\n\n function burn(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;\n\n function nextGuid(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (bytes32);\n\n function inboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);\n\n function outboundNonce(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (uint64);\n\n function inboundPayloadHash(\n address _receiver,\n uint32 _srcEid,\n bytes32 _sender,\n uint64 _nonce\n ) external view returns (bytes32);\n\n function lazyInboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingComposer.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingComposer {\n event ComposeSent(address from, address to, bytes32 guid, uint16 index, bytes message);\n event ComposeDelivered(address from, address to, bytes32 guid, uint16 index);\n event LzComposeAlert(\n address indexed from,\n address indexed to,\n address indexed executor,\n bytes32 guid,\n uint16 index,\n uint256 gas,\n uint256 value,\n bytes message,\n bytes extraData,\n bytes reason\n );\n\n function composeQueue(\n address _from,\n address _to,\n bytes32 _guid,\n uint16 _index\n ) external view returns (bytes32 messageHash);\n\n function sendCompose(address _to, bytes32 _guid, uint16 _index, bytes calldata _message) external;\n\n function lzCompose(\n address _from,\n address _to,\n bytes32 _guid,\n uint16 _index,\n bytes calldata _message,\n bytes calldata _extraData\n ) external payable;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingContext.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingContext {\n function isSendingMessage() external view returns (bool);\n\n function getSendContext() external view returns (uint32 dstEid, address sender);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ISendLib.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { MessagingFee } from \"./ILayerZeroEndpointV2.sol\";\nimport { IMessageLib } from \"./IMessageLib.sol\";\n\nstruct Packet {\n uint64 nonce;\n uint32 srcEid;\n address sender;\n uint32 dstEid;\n bytes32 receiver;\n bytes32 guid;\n bytes message;\n}\n\ninterface ISendLib is IMessageLib {\n function send(\n Packet calldata _packet,\n bytes calldata _options,\n bool _payInLzToken\n ) external returns (MessagingFee memory, bytes memory encodedPacket);\n\n function quote(\n Packet calldata _packet,\n bytes calldata _options,\n bool _payInLzToken\n ) external view returns (MessagingFee memory);\n\n function setTreasury(address _treasury) external;\n\n function withdrawFee(address _to, uint256 _amount) external;\n\n function withdrawLzTokenFee(address _lzToken, address _to, uint256 _amount) external;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/AddressCast.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nlibrary AddressCast {\n error AddressCast_InvalidSizeForAddress();\n error AddressCast_InvalidAddress();\n\n function toBytes32(bytes calldata _addressBytes) internal pure returns (bytes32 result) {\n if (_addressBytes.length > 32) revert AddressCast_InvalidAddress();\n result = bytes32(_addressBytes);\n unchecked {\n uint256 offset = 32 - _addressBytes.length;\n result = result >> (offset * 8);\n }\n }\n\n function toBytes32(address _address) internal pure returns (bytes32 result) {\n result = bytes32(uint256(uint160(_address)));\n }\n\n function toBytes(bytes32 _addressBytes32, uint256 _size) internal pure returns (bytes memory result) {\n if (_size == 0 || _size > 32) revert AddressCast_InvalidSizeForAddress();\n result = new bytes(_size);\n unchecked {\n uint256 offset = 256 - _size * 8;\n assembly {\n mstore(add(result, 32), shl(offset, _addressBytes32))\n }\n }\n }\n\n function toAddress(bytes32 _addressBytes32) internal pure returns (address result) {\n result = address(uint160(uint256(_addressBytes32)));\n }\n\n function toAddress(bytes calldata _addressBytes) internal pure returns (address result) {\n if (_addressBytes.length != 20) revert AddressCast_InvalidAddress();\n result = address(bytes20(_addressBytes));\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nlibrary CalldataBytesLib {\n function toU8(bytes calldata _bytes, uint256 _start) internal pure returns (uint8) {\n return uint8(_bytes[_start]);\n }\n\n function toU16(bytes calldata _bytes, uint256 _start) internal pure returns (uint16) {\n unchecked {\n uint256 end = _start + 2;\n return uint16(bytes2(_bytes[_start:end]));\n }\n }\n\n function toU32(bytes calldata _bytes, uint256 _start) internal pure returns (uint32) {\n unchecked {\n uint256 end = _start + 4;\n return uint32(bytes4(_bytes[_start:end]));\n }\n }\n\n function toU64(bytes calldata _bytes, uint256 _start) internal pure returns (uint64) {\n unchecked {\n uint256 end = _start + 8;\n return uint64(bytes8(_bytes[_start:end]));\n }\n }\n\n function toU128(bytes calldata _bytes, uint256 _start) internal pure returns (uint128) {\n unchecked {\n uint256 end = _start + 16;\n return uint128(bytes16(_bytes[_start:end]));\n }\n }\n\n function toU256(bytes calldata _bytes, uint256 _start) internal pure returns (uint256) {\n unchecked {\n uint256 end = _start + 32;\n return uint256(bytes32(_bytes[_start:end]));\n }\n }\n\n function toAddr(bytes calldata _bytes, uint256 _start) internal pure returns (address) {\n unchecked {\n uint256 end = _start + 20;\n return address(bytes20(_bytes[_start:end]));\n }\n }\n\n function toB32(bytes calldata _bytes, uint256 _start) internal pure returns (bytes32) {\n unchecked {\n uint256 end = _start + 32;\n return bytes32(_bytes[_start:end]);\n }\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/BitMaps.sol": { + "content": "// SPDX-License-Identifier: MIT\n\n// modified from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/structs/BitMaps.sol\npragma solidity ^0.8.20;\n\ntype BitMap256 is uint256;\n\nusing BitMaps for BitMap256 global;\n\nlibrary BitMaps {\n /**\n * @dev Returns whether the bit at `index` is set.\n */\n function get(BitMap256 bitmap, uint8 index) internal pure returns (bool) {\n uint256 mask = 1 << index;\n return BitMap256.unwrap(bitmap) & mask != 0;\n }\n\n /**\n * @dev Sets the bit at `index`.\n */\n function set(BitMap256 bitmap, uint8 index) internal pure returns (BitMap256) {\n uint256 mask = 1 << index;\n return BitMap256.wrap(BitMap256.unwrap(bitmap) | mask);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport { Packet } from \"../../interfaces/ISendLib.sol\";\nimport { AddressCast } from \"../../libs/AddressCast.sol\";\n\nlibrary PacketV1Codec {\n using AddressCast for address;\n using AddressCast for bytes32;\n\n uint8 internal constant PACKET_VERSION = 1;\n\n // header (version + nonce + path)\n // version\n uint256 private constant PACKET_VERSION_OFFSET = 0;\n // nonce\n uint256 private constant NONCE_OFFSET = 1;\n // path\n uint256 private constant SRC_EID_OFFSET = 9;\n uint256 private constant SENDER_OFFSET = 13;\n uint256 private constant DST_EID_OFFSET = 45;\n uint256 private constant RECEIVER_OFFSET = 49;\n // payload (guid + message)\n uint256 private constant GUID_OFFSET = 81; // keccak256(nonce + path)\n uint256 private constant MESSAGE_OFFSET = 113;\n\n function encode(Packet memory _packet) internal pure returns (bytes memory encodedPacket) {\n encodedPacket = abi.encodePacked(\n PACKET_VERSION,\n _packet.nonce,\n _packet.srcEid,\n _packet.sender.toBytes32(),\n _packet.dstEid,\n _packet.receiver,\n _packet.guid,\n _packet.message\n );\n }\n\n function encodePacketHeader(Packet memory _packet) internal pure returns (bytes memory) {\n return\n abi.encodePacked(\n PACKET_VERSION,\n _packet.nonce,\n _packet.srcEid,\n _packet.sender.toBytes32(),\n _packet.dstEid,\n _packet.receiver\n );\n }\n\n function encodePayload(Packet memory _packet) internal pure returns (bytes memory) {\n return abi.encodePacked(_packet.guid, _packet.message);\n }\n\n function header(bytes calldata _packet) internal pure returns (bytes calldata) {\n return _packet[0:GUID_OFFSET];\n }\n\n function version(bytes calldata _packet) internal pure returns (uint8) {\n return uint8(bytes1(_packet[PACKET_VERSION_OFFSET:NONCE_OFFSET]));\n }\n\n function nonce(bytes calldata _packet) internal pure returns (uint64) {\n return uint64(bytes8(_packet[NONCE_OFFSET:SRC_EID_OFFSET]));\n }\n\n function srcEid(bytes calldata _packet) internal pure returns (uint32) {\n return uint32(bytes4(_packet[SRC_EID_OFFSET:SENDER_OFFSET]));\n }\n\n function sender(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[SENDER_OFFSET:DST_EID_OFFSET]);\n }\n\n function senderAddressB20(bytes calldata _packet) internal pure returns (address) {\n return sender(_packet).toAddress();\n }\n\n function dstEid(bytes calldata _packet) internal pure returns (uint32) {\n return uint32(bytes4(_packet[DST_EID_OFFSET:RECEIVER_OFFSET]));\n }\n\n function receiver(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[RECEIVER_OFFSET:GUID_OFFSET]);\n }\n\n function receiverB20(bytes calldata _packet) internal pure returns (address) {\n return receiver(_packet).toAddress();\n }\n\n function guid(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[GUID_OFFSET:MESSAGE_OFFSET]);\n }\n\n function message(bytes calldata _packet) internal pure returns (bytes calldata) {\n return bytes(_packet[MESSAGE_OFFSET:]);\n }\n\n function payload(bytes calldata _packet) internal pure returns (bytes calldata) {\n return bytes(_packet[GUID_OFFSET:]);\n }\n\n function payloadHash(bytes calldata _packet) internal pure returns (bytes32) {\n return keccak256(payload(_packet));\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { ILayerZeroEndpointV2 } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\n\n/**\n * @title IOAppCore\n */\ninterface IOAppCore {\n // Custom error messages\n error OnlyPeer(uint32 eid, bytes32 sender);\n error NoPeer(uint32 eid);\n error InvalidEndpointCall();\n error InvalidDelegate();\n\n // Event emitted when a peer (OApp) is set for a corresponding endpoint\n event PeerSet(uint32 eid, bytes32 peer);\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n */\n function oAppVersion() external view returns (uint64 senderVersion, uint64 receiverVersion);\n\n /**\n * @notice Retrieves the LayerZero endpoint associated with the OApp.\n * @return iEndpoint The LayerZero endpoint as an interface.\n */\n function endpoint() external view returns (ILayerZeroEndpointV2 iEndpoint);\n\n /**\n * @notice Retrieves the peer (OApp) associated with a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @return peer The peer address (OApp instance) associated with the corresponding endpoint.\n */\n function peers(uint32 _eid) external view returns (bytes32 peer);\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n */\n function setPeer(uint32 _eid, bytes32 _peer) external;\n\n /**\n * @notice Sets the delegate address for the OApp Core.\n * @param _delegate The address of the delegate to be set.\n */\n function setDelegate(address _delegate) external;\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppMsgInspector.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n/**\n * @title IOAppMsgInspector\n * @dev Interface for the OApp Message Inspector, allowing examination of message and options contents.\n */\ninterface IOAppMsgInspector {\n // Custom error message for inspection failure\n error InspectionFailed(bytes message, bytes options);\n\n /**\n * @notice Allows the inspector to examine LayerZero message contents and optionally throw a revert if invalid.\n * @param _message The message payload to be inspected.\n * @param _options Additional options or parameters for inspection.\n * @return valid A boolean indicating whether the inspection passed (true) or failed (false).\n *\n * @dev Optionally done as a revert, OR use the boolean provided to handle the failure.\n */\n function inspect(bytes calldata _message, bytes calldata _options) external view returns (bool valid);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppOptionsType3.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Struct representing enforced option parameters.\n */\nstruct EnforcedOptionParam {\n uint32 eid; // Endpoint ID\n uint16 msgType; // Message Type\n bytes options; // Additional options\n}\n\n/**\n * @title IOAppOptionsType3\n * @dev Interface for the OApp with Type 3 Options, allowing the setting and combining of enforced options.\n */\ninterface IOAppOptionsType3 {\n // Custom error message for invalid options\n error InvalidOptions(bytes options);\n\n // Event emitted when enforced options are set\n event EnforcedOptionSet(EnforcedOptionParam[] _enforcedOptions);\n\n /**\n * @notice Sets enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n */\n function setEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) external;\n\n /**\n * @notice Combines options for a given endpoint and message type.\n * @param _eid The endpoint ID.\n * @param _msgType The OApp message type.\n * @param _extraOptions Additional options passed by the caller.\n * @return options The combination of caller specified options AND enforced options.\n */\n function combineOptions(\n uint32 _eid,\n uint16 _msgType,\n bytes calldata _extraOptions\n ) external view returns (bytes memory options);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\nimport { ILayerZeroReceiver, Origin } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroReceiver.sol\";\n\ninterface IOAppReceiver is ILayerZeroReceiver {\n /**\n * @notice Indicates whether an address is an approved composeMsg sender to the Endpoint.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _message The lzReceive payload.\n * @param _sender The sender address.\n * @return isSender Is a valid sender.\n *\n * @dev Applications can optionally choose to implement a separate composeMsg sender that is NOT the bridging layer.\n * @dev The default sender IS the OAppReceiver implementer.\n */\n function isComposeMsgSender(\n Origin calldata _origin,\n bytes calldata _message,\n address _sender\n ) external view returns (bool isSender);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IOAppOptionsType3, EnforcedOptionParam } from \"../interfaces/IOAppOptionsType3.sol\";\n\n/**\n * @title OAppOptionsType3\n * @dev Abstract contract implementing the IOAppOptionsType3 interface with type 3 options.\n */\nabstract contract OAppOptionsType3 is IOAppOptionsType3, Ownable {\n uint16 internal constant OPTION_TYPE_3 = 3;\n\n // @dev The \"msgType\" should be defined in the child contract.\n mapping(uint32 eid => mapping(uint16 msgType => bytes enforcedOption)) public enforcedOptions;\n\n /**\n * @dev Sets the enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Provides a way for the OApp to enforce things like paying for PreCrime, AND/OR minimum dst lzReceive gas amounts etc.\n * @dev These enforced options can vary as the potential options/execution on the remote may differ as per the msgType.\n * eg. Amount of lzReceive() gas necessary to deliver a lzCompose() message adds overhead you dont want to pay\n * if you are only making a standard LayerZero message ie. lzReceive() WITHOUT sendCompose().\n */\n function setEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) public virtual onlyOwner {\n _setEnforcedOptions(_enforcedOptions);\n }\n\n /**\n * @dev Sets the enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n *\n * @dev Provides a way for the OApp to enforce things like paying for PreCrime, AND/OR minimum dst lzReceive gas amounts etc.\n * @dev These enforced options can vary as the potential options/execution on the remote may differ as per the msgType.\n * eg. Amount of lzReceive() gas necessary to deliver a lzCompose() message adds overhead you dont want to pay\n * if you are only making a standard LayerZero message ie. lzReceive() WITHOUT sendCompose().\n */\n function _setEnforcedOptions(EnforcedOptionParam[] memory _enforcedOptions) internal virtual {\n for (uint256 i = 0; i < _enforcedOptions.length; i++) {\n // @dev Enforced options are only available for optionType 3, as type 1 and 2 dont support combining.\n _assertOptionsType3(_enforcedOptions[i].options);\n enforcedOptions[_enforcedOptions[i].eid][_enforcedOptions[i].msgType] = _enforcedOptions[i].options;\n }\n\n emit EnforcedOptionSet(_enforcedOptions);\n }\n\n /**\n * @notice Combines options for a given endpoint and message type.\n * @param _eid The endpoint ID.\n * @param _msgType The OAPP message type.\n * @param _extraOptions Additional options passed by the caller.\n * @return options The combination of caller specified options AND enforced options.\n *\n * @dev If there is an enforced lzReceive option:\n * - {gasLimit: 200k, msg.value: 1 ether} AND a caller supplies a lzReceive option: {gasLimit: 100k, msg.value: 0.5 ether}\n * - The resulting options will be {gasLimit: 300k, msg.value: 1.5 ether} when the message is executed on the remote lzReceive() function.\n * @dev This presence of duplicated options is handled off-chain in the verifier/executor.\n */\n function combineOptions(\n uint32 _eid,\n uint16 _msgType,\n bytes calldata _extraOptions\n ) public view virtual returns (bytes memory) {\n bytes memory enforced = enforcedOptions[_eid][_msgType];\n\n // No enforced options, pass whatever the caller supplied, even if it's empty or legacy type 1/2 options.\n if (enforced.length == 0) return _extraOptions;\n\n // No caller options, return enforced\n if (_extraOptions.length == 0) return enforced;\n\n // @dev If caller provided _extraOptions, must be type 3 as its the ONLY type that can be combined.\n if (_extraOptions.length >= 2) {\n _assertOptionsType3(_extraOptions);\n // @dev Remove the first 2 bytes containing the type from the _extraOptions and combine with enforced.\n return bytes.concat(enforced, _extraOptions[2:]);\n }\n\n // No valid set of options was found.\n revert InvalidOptions(_extraOptions);\n }\n\n /**\n * @dev Internal function to assert that options are of type 3.\n * @param _options The options to be checked.\n */\n function _assertOptionsType3(bytes memory _options) internal pure virtual {\n uint16 optionsType;\n assembly {\n optionsType := mload(add(_options, 2))\n }\n if (optionsType != OPTION_TYPE_3) revert InvalidOptions(_options);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { BytesLib } from \"solidity-bytes-utils/contracts/BytesLib.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { ExecutorOptions } from \"@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol\";\nimport { DVNOptions } from \"@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/DVNOptions.sol\";\n\n/**\n * @title OptionsBuilder\n * @dev Library for building and encoding various message options.\n */\nlibrary OptionsBuilder {\n using SafeCast for uint256;\n using BytesLib for bytes;\n\n // Constants for options types\n uint16 internal constant TYPE_1 = 1; // legacy options type 1\n uint16 internal constant TYPE_2 = 2; // legacy options type 2\n uint16 internal constant TYPE_3 = 3;\n\n // Custom error message\n error InvalidSize(uint256 max, uint256 actual);\n error InvalidOptionType(uint16 optionType);\n\n // Modifier to ensure only options of type 3 are used\n modifier onlyType3(bytes memory _options) {\n if (_options.toUint16(0) != TYPE_3) revert InvalidOptionType(_options.toUint16(0));\n _;\n }\n\n /**\n * @dev Creates a new options container with type 3.\n * @return options The newly created options container.\n */\n function newOptions() internal pure returns (bytes memory) {\n return abi.encodePacked(TYPE_3);\n }\n\n /**\n * @dev Adds an executor LZ receive option to the existing options.\n * @param _options The existing options container.\n * @param _gas The gasLimit used on the lzReceive() function in the OApp.\n * @param _value The msg.value passed to the lzReceive() function in the OApp.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed by the executor\n * eg. if (_gas: 200k, and _value: 1 ether) AND (_gas: 100k, _value: 0.5 ether) are sent in an option to the LayerZeroEndpoint,\n * that becomes (300k, 1.5 ether) when the message is executed on the remote lzReceive() function.\n */\n function addExecutorLzReceiveOption(\n bytes memory _options,\n uint128 _gas,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzReceiveOption(_gas, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZRECEIVE, option);\n }\n\n /**\n * @dev Adds an executor native drop option to the existing options.\n * @param _options The existing options container.\n * @param _amount The amount for the native value that is airdropped to the 'receiver'.\n * @param _receiver The receiver address for the native drop option.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed by the executor on the remote chain.\n */\n function addExecutorNativeDropOption(\n bytes memory _options,\n uint128 _amount,\n bytes32 _receiver\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeNativeDropOption(_amount, _receiver);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_NATIVE_DROP, option);\n }\n\n // /**\n // * @dev Adds an executor native drop option to the existing options.\n // * @param _options The existing options container.\n // * @param _amount The amount for the native value that is airdropped to the 'receiver'.\n // * @param _receiver The receiver address for the native drop option.\n // * @return options The updated options container.\n // *\n // * @dev When multiples of this option are added, they are summed by the executor on the remote chain.\n // */\n function addExecutorLzReadOption(\n bytes memory _options,\n uint128 _gas,\n uint32 _size,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzReadOption(_gas, _size, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZREAD, option);\n }\n\n /**\n * @dev Adds an executor LZ compose option to the existing options.\n * @param _options The existing options container.\n * @param _index The index for the lzCompose() function call.\n * @param _gas The gasLimit for the lzCompose() function call.\n * @param _value The msg.value for the lzCompose() function call.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed PER index by the executor on the remote chain.\n * @dev If the OApp sends N lzCompose calls on the remote, you must provide N incremented indexes starting with 0.\n * ie. When your remote OApp composes (N = 3) messages, you must set this option for index 0,1,2\n */\n function addExecutorLzComposeOption(\n bytes memory _options,\n uint16 _index,\n uint128 _gas,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzComposeOption(_index, _gas, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZCOMPOSE, option);\n }\n\n /**\n * @dev Adds an executor ordered execution option to the existing options.\n * @param _options The existing options container.\n * @return options The updated options container.\n */\n function addExecutorOrderedExecutionOption(\n bytes memory _options\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_ORDERED_EXECUTION, bytes(\"\"));\n }\n\n /**\n * @dev Adds a DVN pre-crime option to the existing options.\n * @param _options The existing options container.\n * @param _dvnIdx The DVN index for the pre-crime option.\n * @return options The updated options container.\n */\n function addDVNPreCrimeOption(\n bytes memory _options,\n uint8 _dvnIdx\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return addDVNOption(_options, _dvnIdx, DVNOptions.OPTION_TYPE_PRECRIME, bytes(\"\"));\n }\n\n /**\n * @dev Adds an executor option to the existing options.\n * @param _options The existing options container.\n * @param _optionType The type of the executor option.\n * @param _option The encoded data for the executor option.\n * @return options The updated options container.\n */\n function addExecutorOption(\n bytes memory _options,\n uint8 _optionType,\n bytes memory _option\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return\n abi.encodePacked(\n _options,\n ExecutorOptions.WORKER_ID,\n _option.length.toUint16() + 1, // +1 for optionType\n _optionType,\n _option\n );\n }\n\n /**\n * @dev Adds a DVN option to the existing options.\n * @param _options The existing options container.\n * @param _dvnIdx The DVN index for the DVN option.\n * @param _optionType The type of the DVN option.\n * @param _option The encoded data for the DVN option.\n * @return options The updated options container.\n */\n function addDVNOption(\n bytes memory _options,\n uint8 _dvnIdx,\n uint8 _optionType,\n bytes memory _option\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return\n abi.encodePacked(\n _options,\n DVNOptions.WORKER_ID,\n _option.length.toUint16() + 2, // +2 for optionType and dvnIdx\n _dvnIdx,\n _optionType,\n _option\n );\n }\n\n /**\n * @dev Encodes legacy options of type 1.\n * @param _executionGas The gasLimit value passed to lzReceive().\n * @return legacyOptions The encoded legacy options.\n */\n function encodeLegacyOptionsType1(uint256 _executionGas) internal pure returns (bytes memory) {\n if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);\n return abi.encodePacked(TYPE_1, _executionGas);\n }\n\n /**\n * @dev Encodes legacy options of type 2.\n * @param _executionGas The gasLimit value passed to lzReceive().\n * @param _nativeForDst The amount of native air dropped to the receiver.\n * @param _receiver The _nativeForDst receiver address.\n * @return legacyOptions The encoded legacy options of type 2.\n */\n function encodeLegacyOptionsType2(\n uint256 _executionGas,\n uint256 _nativeForDst,\n bytes memory _receiver // @dev Use bytes instead of bytes32 in legacy type 2 for _receiver.\n ) internal pure returns (bytes memory) {\n if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);\n if (_nativeForDst > type(uint128).max) revert InvalidSize(type(uint128).max, _nativeForDst);\n if (_receiver.length > 32) revert InvalidSize(32, _receiver.length);\n return abi.encodePacked(TYPE_2, _executionGas, _nativeForDst, _receiver);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n// @dev Import the 'MessagingFee' and 'MessagingReceipt' so it's exposed to OApp implementers\n// solhint-disable-next-line no-unused-import\nimport { OAppSender, MessagingFee, MessagingReceipt } from \"./OAppSender.sol\";\n// @dev Import the 'Origin' so it's exposed to OApp implementers\n// solhint-disable-next-line no-unused-import\nimport { OAppReceiver, Origin } from \"./OAppReceiver.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OApp\n * @dev Abstract contract serving as the base for OApp implementation, combining OAppSender and OAppReceiver functionality.\n */\nabstract contract OApp is OAppSender, OAppReceiver {\n /**\n * @dev Constructor to initialize the OApp with the provided endpoint and owner.\n * @param _endpoint The address of the LOCAL LayerZero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(address _endpoint, address _delegate) OAppCore(_endpoint, _delegate) {}\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol implementation.\n * @return receiverVersion The version of the OAppReceiver.sol implementation.\n */\n function oAppVersion()\n public\n pure\n virtual\n override(OAppSender, OAppReceiver)\n returns (uint64 senderVersion, uint64 receiverVersion)\n {\n return (SENDER_VERSION, RECEIVER_VERSION);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IOAppCore, ILayerZeroEndpointV2 } from \"./interfaces/IOAppCore.sol\";\n\n/**\n * @title OAppCore\n * @dev Abstract contract implementing the IOAppCore interface with basic OApp configurations.\n */\nabstract contract OAppCore is IOAppCore, Ownable {\n // The LayerZero endpoint associated with the given OApp\n ILayerZeroEndpointV2 public immutable endpoint;\n\n // Mapping to store peers associated with corresponding endpoints\n mapping(uint32 eid => bytes32 peer) public peers;\n\n /**\n * @dev Constructor to initialize the OAppCore with the provided endpoint and delegate.\n * @param _endpoint The address of the LOCAL Layer Zero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n *\n * @dev The delegate typically should be set as the owner of the contract.\n */\n constructor(address _endpoint, address _delegate) {\n endpoint = ILayerZeroEndpointV2(_endpoint);\n\n if (_delegate == address(0)) revert InvalidDelegate();\n endpoint.setDelegate(_delegate);\n }\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.\n * @dev Set this to bytes32(0) to remove the peer address.\n * @dev Peer is a bytes32 to accommodate non-evm chains.\n */\n function setPeer(uint32 _eid, bytes32 _peer) public virtual onlyOwner {\n _setPeer(_eid, _peer);\n }\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n *\n * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.\n * @dev Set this to bytes32(0) to remove the peer address.\n * @dev Peer is a bytes32 to accommodate non-evm chains.\n */\n function _setPeer(uint32 _eid, bytes32 _peer) internal virtual {\n peers[_eid] = _peer;\n emit PeerSet(_eid, _peer);\n }\n\n /**\n * @notice Internal function to get the peer address associated with a specific endpoint; reverts if NOT set.\n * ie. the peer is set to bytes32(0).\n * @param _eid The endpoint ID.\n * @return peer The address of the peer associated with the specified endpoint.\n */\n function _getPeerOrRevert(uint32 _eid) internal view virtual returns (bytes32) {\n bytes32 peer = peers[_eid];\n if (peer == bytes32(0)) revert NoPeer(_eid);\n return peer;\n }\n\n /**\n * @notice Sets the delegate address for the OApp.\n * @param _delegate The address of the delegate to be set.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Provides the ability for a delegate to set configs, on behalf of the OApp, directly on the Endpoint contract.\n */\n function setDelegate(address _delegate) public onlyOwner {\n endpoint.setDelegate(_delegate);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IOAppReceiver, Origin } from \"./interfaces/IOAppReceiver.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OAppReceiver\n * @dev Abstract contract implementing the ILayerZeroReceiver interface and extending OAppCore for OApp receivers.\n */\nabstract contract OAppReceiver is IOAppReceiver, OAppCore {\n // Custom error message for when the caller is not the registered endpoint/\n error OnlyEndpoint(address addr);\n\n // @dev The version of the OAppReceiver implementation.\n // @dev Version is bumped when changes are made to this contract.\n uint64 internal constant RECEIVER_VERSION = 2;\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n *\n * @dev Providing 0 as the default for OAppSender version. Indicates that the OAppSender is not implemented.\n * ie. this is a RECEIVE only OApp.\n * @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions.\n */\n function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {\n return (0, RECEIVER_VERSION);\n }\n\n /**\n * @notice Indicates whether an address is an approved composeMsg sender to the Endpoint.\n * @dev _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @dev _message The lzReceive payload.\n * @param _sender The sender address.\n * @return isSender Is a valid sender.\n *\n * @dev Applications can optionally choose to implement separate composeMsg senders that are NOT the bridging layer.\n * @dev The default sender IS the OAppReceiver implementer.\n */\n function isComposeMsgSender(\n Origin calldata /*_origin*/,\n bytes calldata /*_message*/,\n address _sender\n ) public view virtual returns (bool) {\n return _sender == address(this);\n }\n\n /**\n * @notice Checks if the path initialization is allowed based on the provided origin.\n * @param origin The origin information containing the source endpoint and sender address.\n * @return Whether the path has been initialized.\n *\n * @dev This indicates to the endpoint that the OApp has enabled msgs for this particular path to be received.\n * @dev This defaults to assuming if a peer has been set, its initialized.\n * Can be overridden by the OApp if there is other logic to determine this.\n */\n function allowInitializePath(Origin calldata origin) public view virtual returns (bool) {\n return peers[origin.srcEid] == origin.sender;\n }\n\n /**\n * @notice Retrieves the next nonce for a given source endpoint and sender address.\n * @dev _srcEid The source endpoint ID.\n * @dev _sender The sender address.\n * @return nonce The next nonce.\n *\n * @dev The path nonce starts from 1. If 0 is returned it means that there is NO nonce ordered enforcement.\n * @dev Is required by the off-chain executor to determine the OApp expects msg execution is ordered.\n * @dev This is also enforced by the OApp.\n * @dev By default this is NOT enabled. ie. nextNonce is hardcoded to return 0.\n */\n function nextNonce(uint32 /*_srcEid*/, bytes32 /*_sender*/) public view virtual returns (uint64 nonce) {\n return 0;\n }\n\n /**\n * @dev Entry point for receiving messages or packets from the endpoint.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The payload of the received message.\n * @param _executor The address of the executor for the received message.\n * @param _extraData Additional arbitrary data provided by the corresponding executor.\n *\n * @dev Entry point for receiving msg/packet from the LayerZero endpoint.\n */\n function lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) public payable virtual {\n // Ensures that only the endpoint can attempt to lzReceive() messages to this OApp.\n if (address(endpoint) != msg.sender) revert OnlyEndpoint(msg.sender);\n\n // Ensure that the sender matches the expected peer for the source endpoint.\n if (_getPeerOrRevert(_origin.srcEid) != _origin.sender) revert OnlyPeer(_origin.srcEid, _origin.sender);\n\n // Call the internal OApp implementation of lzReceive.\n _lzReceive(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Internal function to implement lzReceive logic without needing to copy the basic parameter validation.\n */\n function _lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual;\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { SafeERC20, IERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { MessagingParams, MessagingFee, MessagingReceipt } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OAppSender\n * @dev Abstract contract implementing the OAppSender functionality for sending messages to a LayerZero endpoint.\n */\nabstract contract OAppSender is OAppCore {\n using SafeERC20 for IERC20;\n\n // Custom error messages\n error NotEnoughNative(uint256 msgValue);\n error LzTokenUnavailable();\n\n // @dev The version of the OAppSender implementation.\n // @dev Version is bumped when changes are made to this contract.\n uint64 internal constant SENDER_VERSION = 1;\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n *\n * @dev Providing 0 as the default for OAppReceiver version. Indicates that the OAppReceiver is not implemented.\n * ie. this is a SEND only OApp.\n * @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions\n */\n function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {\n return (SENDER_VERSION, 0);\n }\n\n /**\n * @dev Internal function to interact with the LayerZero EndpointV2.quote() for fee calculation.\n * @param _dstEid The destination endpoint ID.\n * @param _message The message payload.\n * @param _options Additional options for the message.\n * @param _payInLzToken Flag indicating whether to pay the fee in LZ tokens.\n * @return fee The calculated MessagingFee for the message.\n * - nativeFee: The native fee for the message.\n * - lzTokenFee: The LZ token fee for the message.\n */\n function _quote(\n uint32 _dstEid,\n bytes memory _message,\n bytes memory _options,\n bool _payInLzToken\n ) internal view virtual returns (MessagingFee memory fee) {\n return\n endpoint.quote(\n MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _payInLzToken),\n address(this)\n );\n }\n\n /**\n * @dev Internal function to interact with the LayerZero EndpointV2.send() for sending a message.\n * @param _dstEid The destination endpoint ID.\n * @param _message The message payload.\n * @param _options Additional options for the message.\n * @param _fee The calculated LayerZero fee for the message.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess fee values sent to the endpoint.\n * @return receipt The receipt for the sent message.\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function _lzSend(\n uint32 _dstEid,\n bytes memory _message,\n bytes memory _options,\n MessagingFee memory _fee,\n address _refundAddress\n ) internal virtual returns (MessagingReceipt memory receipt) {\n // @dev Push corresponding fees to the endpoint, any excess is sent back to the _refundAddress from the endpoint.\n uint256 messageValue = _payNative(_fee.nativeFee);\n if (_fee.lzTokenFee > 0) _payLzToken(_fee.lzTokenFee);\n\n return\n // solhint-disable-next-line check-send-result\n endpoint.send{ value: messageValue }(\n MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _fee.lzTokenFee > 0),\n _refundAddress\n );\n }\n\n /**\n * @dev Internal function to pay the native fee associated with the message.\n * @param _nativeFee The native fee to be paid.\n * @return nativeFee The amount of native currency paid.\n *\n * @dev If the OApp needs to initiate MULTIPLE LayerZero messages in a single transaction,\n * this will need to be overridden because msg.value would contain multiple lzFees.\n * @dev Should be overridden in the event the LayerZero endpoint requires a different native currency.\n * @dev Some EVMs use an ERC20 as a method for paying transactions/gasFees.\n * @dev The endpoint is EITHER/OR, ie. it will NOT support both types of native payment at a time.\n */\n function _payNative(uint256 _nativeFee) internal virtual returns (uint256 nativeFee) {\n if (msg.value != _nativeFee) revert NotEnoughNative(msg.value);\n return _nativeFee;\n }\n\n /**\n * @dev Internal function to pay the LZ token fee associated with the message.\n * @param _lzTokenFee The LZ token fee to be paid.\n *\n * @dev If the caller is trying to pay in the specified lzToken, then the lzTokenFee is passed to the endpoint.\n * @dev Any excess sent, is passed back to the specified _refundAddress in the _lzSend().\n */\n function _payLzToken(uint256 _lzTokenFee) internal virtual {\n // @dev Cannot cache the token because it is not immutable in the endpoint.\n address lzToken = endpoint.lzToken();\n if (lzToken == address(0)) revert LzTokenUnavailable();\n\n // Pay LZ token fee by sending tokens to the endpoint.\n IERC20(lzToken).safeTransferFrom(msg.sender, address(endpoint), _lzTokenFee);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/interfaces/IOAppPreCrimeSimulator.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n// @dev Import the Origin so it's exposed to OAppPreCrimeSimulator implementers.\n// solhint-disable-next-line no-unused-import\nimport { InboundPacket, Origin } from \"../libs/Packet.sol\";\n\n/**\n * @title IOAppPreCrimeSimulator Interface\n * @dev Interface for the preCrime simulation functionality in an OApp.\n */\ninterface IOAppPreCrimeSimulator {\n // @dev simulation result used in PreCrime implementation\n error SimulationResult(bytes result);\n error OnlySelf();\n\n /**\n * @dev Emitted when the preCrime contract address is set.\n * @param preCrimeAddress The address of the preCrime contract.\n */\n event PreCrimeSet(address preCrimeAddress);\n\n /**\n * @dev Retrieves the address of the preCrime contract implementation.\n * @return The address of the preCrime contract.\n */\n function preCrime() external view returns (address);\n\n /**\n * @dev Retrieves the address of the OApp contract.\n * @return The address of the OApp contract.\n */\n function oApp() external view returns (address);\n\n /**\n * @dev Sets the preCrime contract address.\n * @param _preCrime The address of the preCrime contract.\n */\n function setPreCrime(address _preCrime) external;\n\n /**\n * @dev Mocks receiving a packet, then reverts with a series of data to infer the state/result.\n * @param _packets An array of LayerZero InboundPacket objects representing received packets.\n */\n function lzReceiveAndRevert(InboundPacket[] calldata _packets) external payable;\n\n /**\n * @dev checks if the specified peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint Id to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n */\n function isPeer(uint32 _eid, bytes32 _peer) external view returns (bool);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/interfaces/IPreCrime.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\nstruct PreCrimePeer {\n uint32 eid;\n bytes32 preCrime;\n bytes32 oApp;\n}\n\n// TODO not done yet\ninterface IPreCrime {\n error OnlyOffChain();\n\n // for simulate()\n error PacketOversize(uint256 max, uint256 actual);\n error PacketUnsorted();\n error SimulationFailed(bytes reason);\n\n // for preCrime()\n error SimulationResultNotFound(uint32 eid);\n error InvalidSimulationResult(uint32 eid, bytes reason);\n error CrimeFound(bytes crime);\n\n function getConfig(bytes[] calldata _packets, uint256[] calldata _packetMsgValues) external returns (bytes memory);\n\n function simulate(\n bytes[] calldata _packets,\n uint256[] calldata _packetMsgValues\n ) external payable returns (bytes memory);\n\n function buildSimulationResult() external view returns (bytes memory);\n\n function preCrime(\n bytes[] calldata _packets,\n uint256[] calldata _packetMsgValues,\n bytes[] calldata _simulations\n ) external;\n\n function version() external view returns (uint64 major, uint8 minor);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/libs/Packet.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Origin } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\nimport { PacketV1Codec } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol\";\n\n/**\n * @title InboundPacket\n * @dev Structure representing an inbound packet received by the contract.\n */\nstruct InboundPacket {\n Origin origin; // Origin information of the packet.\n uint32 dstEid; // Destination endpointId of the packet.\n address receiver; // Receiver address for the packet.\n bytes32 guid; // Unique identifier of the packet.\n uint256 value; // msg.value of the packet.\n address executor; // Executor address for the packet.\n bytes message; // Message payload of the packet.\n bytes extraData; // Additional arbitrary data for the packet.\n}\n\n/**\n * @title PacketDecoder\n * @dev Library for decoding LayerZero packets.\n */\nlibrary PacketDecoder {\n using PacketV1Codec for bytes;\n\n /**\n * @dev Decode an inbound packet from the given packet data.\n * @param _packet The packet data to decode.\n * @return packet An InboundPacket struct representing the decoded packet.\n */\n function decode(bytes calldata _packet) internal pure returns (InboundPacket memory packet) {\n packet.origin = Origin(_packet.srcEid(), _packet.sender(), _packet.nonce());\n packet.dstEid = _packet.dstEid();\n packet.receiver = _packet.receiverB20();\n packet.guid = _packet.guid();\n packet.message = _packet.message();\n }\n\n /**\n * @dev Decode multiple inbound packets from the given packet data and associated message values.\n * @param _packets An array of packet data to decode.\n * @param _packetMsgValues An array of associated message values for each packet.\n * @return packets An array of InboundPacket structs representing the decoded packets.\n */\n function decode(\n bytes[] calldata _packets,\n uint256[] memory _packetMsgValues\n ) internal pure returns (InboundPacket[] memory packets) {\n packets = new InboundPacket[](_packets.length);\n for (uint256 i = 0; i < _packets.length; i++) {\n bytes calldata packet = _packets[i];\n packets[i] = PacketDecoder.decode(packet);\n // @dev Allows the verifier to specify the msg.value that gets passed in lzReceive.\n packets[i].value = _packetMsgValues[i];\n }\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/OAppPreCrimeSimulator.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IPreCrime } from \"./interfaces/IPreCrime.sol\";\nimport { IOAppPreCrimeSimulator, InboundPacket, Origin } from \"./interfaces/IOAppPreCrimeSimulator.sol\";\n\n/**\n * @title OAppPreCrimeSimulator\n * @dev Abstract contract serving as the base for preCrime simulation functionality in an OApp.\n */\nabstract contract OAppPreCrimeSimulator is IOAppPreCrimeSimulator, Ownable {\n // The address of the preCrime implementation.\n address public preCrime;\n\n /**\n * @dev Retrieves the address of the OApp contract.\n * @return The address of the OApp contract.\n *\n * @dev The simulator contract is the base contract for the OApp by default.\n * @dev If the simulator is a separate contract, override this function.\n */\n function oApp() external view virtual returns (address) {\n return address(this);\n }\n\n /**\n * @dev Sets the preCrime contract address.\n * @param _preCrime The address of the preCrime contract.\n */\n function setPreCrime(address _preCrime) public virtual onlyOwner {\n preCrime = _preCrime;\n emit PreCrimeSet(_preCrime);\n }\n\n /**\n * @dev Interface for pre-crime simulations. Always reverts at the end with the simulation results.\n * @param _packets An array of InboundPacket objects representing received packets to be delivered.\n *\n * @dev WARNING: MUST revert at the end with the simulation results.\n * @dev Gives the preCrime implementation the ability to mock sending packets to the lzReceive function,\n * WITHOUT actually executing them.\n */\n function lzReceiveAndRevert(InboundPacket[] calldata _packets) public payable virtual {\n for (uint256 i = 0; i < _packets.length; i++) {\n InboundPacket calldata packet = _packets[i];\n\n // Ignore packets that are not from trusted peers.\n if (!isPeer(packet.origin.srcEid, packet.origin.sender)) continue;\n\n // @dev Because a verifier is calling this function, it doesnt have access to executor params:\n // - address _executor\n // - bytes calldata _extraData\n // preCrime will NOT work for OApps that rely on these two parameters inside of their _lzReceive().\n // They are instead stubbed to default values, address(0) and bytes(\"\")\n // @dev Calling this.lzReceiveSimulate removes ability for assembly return 0 callstack exit,\n // which would cause the revert to be ignored.\n this.lzReceiveSimulate{ value: packet.value }(\n packet.origin,\n packet.guid,\n packet.message,\n packet.executor,\n packet.extraData\n );\n }\n\n // @dev Revert with the simulation results. msg.sender must implement IPreCrime.buildSimulationResult().\n revert SimulationResult(IPreCrime(msg.sender).buildSimulationResult());\n }\n\n /**\n * @dev Is effectively an internal function because msg.sender must be address(this).\n * Allows resetting the call stack for 'internal' calls.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _guid The unique identifier of the packet.\n * @param _message The message payload of the packet.\n * @param _executor The executor address for the packet.\n * @param _extraData Additional data for the packet.\n */\n function lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) external payable virtual {\n // @dev Ensure ONLY can be called 'internally'.\n if (msg.sender != address(this)) revert OnlySelf();\n _lzReceiveSimulate(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Internal function to handle the OAppPreCrimeSimulator simulated receive.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The GUID of the LayerZero message.\n * @param _message The LayerZero message.\n * @param _executor The address of the off-chain executor.\n * @param _extraData Arbitrary data passed by the msg executor.\n *\n * @dev Enables the preCrime simulator to mock sending lzReceive() messages,\n * routes the msg down from the OAppPreCrimeSimulator, and back up to the OAppReceiver.\n */\n function _lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual;\n\n /**\n * @dev checks if the specified peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint Id to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n */\n function isPeer(uint32 _eid, bytes32 _peer) public view virtual returns (bool);\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/interfaces/IMintableBurnable.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\n/// @title Interface for mintable and burnable tokens\ninterface IMintableBurnable {\n \n /**\n * @notice Burns tokens from a specified account\n * @param _from Address from which tokens will be burned\n * @param _amount Amount of tokens to be burned\n * @return success Indicates whether the operation was successful\n */\n function burn(address _from, uint256 _amount) external returns (bool success);\n\n /**\n * @notice Mints tokens to a specified account\n * @param _to Address to which tokens will be minted\n * @param _amount Amount of tokens to be minted\n * @return success Indicates whether the operation was successful\n */\n function mint(address _to, uint256 _amount) external returns (bool success);\n}" + }, + "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { MessagingReceipt, MessagingFee } from \"@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol\";\n\n/**\n * @dev Struct representing token parameters for the OFT send() operation.\n */\nstruct SendParam {\n uint32 dstEid; // Destination endpoint ID.\n bytes32 to; // Recipient address.\n uint256 amountLD; // Amount to send in local decimals.\n uint256 minAmountLD; // Minimum amount to send in local decimals.\n bytes extraOptions; // Additional options supplied by the caller to be used in the LayerZero message.\n bytes composeMsg; // The composed message for the send() operation.\n bytes oftCmd; // The OFT command to be executed, unused in default OFT implementations.\n}\n\n/**\n * @dev Struct representing OFT limit information.\n * @dev These amounts can change dynamically and are up the specific oft implementation.\n */\nstruct OFTLimit {\n uint256 minAmountLD; // Minimum amount in local decimals that can be sent to the recipient.\n uint256 maxAmountLD; // Maximum amount in local decimals that can be sent to the recipient.\n}\n\n/**\n * @dev Struct representing OFT receipt information.\n */\nstruct OFTReceipt {\n uint256 amountSentLD; // Amount of tokens ACTUALLY debited from the sender in local decimals.\n // @dev In non-default implementations, the amountReceivedLD COULD differ from this value.\n uint256 amountReceivedLD; // Amount of tokens to be received on the remote side.\n}\n\n/**\n * @dev Struct representing OFT fee details.\n * @dev Future proof mechanism to provide a standardized way to communicate fees to things like a UI.\n */\nstruct OFTFeeDetail {\n int256 feeAmountLD; // Amount of the fee in local decimals.\n string description; // Description of the fee.\n}\n\n/**\n * @title IOFT\n * @dev Interface for the OftChain (OFT) token.\n * @dev Does not inherit ERC20 to accommodate usage by OFTAdapter as well.\n * @dev This specific interface ID is '0x02e49c2c'.\n */\ninterface IOFT {\n // Custom error messages\n error InvalidLocalDecimals();\n error SlippageExceeded(uint256 amountLD, uint256 minAmountLD);\n\n // Events\n event OFTSent(\n bytes32 indexed guid, // GUID of the OFT message.\n uint32 dstEid, // Destination Endpoint ID.\n address indexed fromAddress, // Address of the sender on the src chain.\n uint256 amountSentLD, // Amount of tokens sent in local decimals.\n uint256 amountReceivedLD // Amount of tokens received in local decimals.\n );\n event OFTReceived(\n bytes32 indexed guid, // GUID of the OFT message.\n uint32 srcEid, // Source Endpoint ID.\n address indexed toAddress, // Address of the recipient on the dst chain.\n uint256 amountReceivedLD // Amount of tokens received in local decimals.\n );\n\n /**\n * @notice Retrieves interfaceID and the version of the OFT.\n * @return interfaceId The interface ID.\n * @return version The version.\n *\n * @dev interfaceId: This specific interface ID is '0x02e49c2c'.\n * @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.\n * @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.\n * ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)\n */\n function oftVersion() external view returns (bytes4 interfaceId, uint64 version);\n\n /**\n * @notice Retrieves the address of the token associated with the OFT.\n * @return token The address of the ERC20 token implementation.\n */\n function token() external view returns (address);\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the 'token()' to send.\n * @return requiresApproval Needs approval of the underlying token implementation.\n *\n * @dev Allows things like wallet implementers to determine integration requirements,\n * without understanding the underlying token implementation.\n */\n function approvalRequired() external view returns (bool);\n\n /**\n * @notice Retrieves the shared decimals of the OFT.\n * @return sharedDecimals The shared decimals of the OFT.\n */\n function sharedDecimals() external view returns (uint8);\n\n /**\n * @notice Provides the fee breakdown and settings data for an OFT. Unused in the default implementation.\n * @param _sendParam The parameters for the send operation.\n * @return limit The OFT limit information.\n * @return oftFeeDetails The details of OFT fees.\n * @return receipt The OFT receipt information.\n */\n function quoteOFT(\n SendParam calldata _sendParam\n ) external view returns (OFTLimit memory, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory);\n\n /**\n * @notice Provides a quote for the send() operation.\n * @param _sendParam The parameters for the send() operation.\n * @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.\n * @return fee The calculated LayerZero messaging fee from the send() operation.\n *\n * @dev MessagingFee: LayerZero msg fee\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n */\n function quoteSend(SendParam calldata _sendParam, bool _payInLzToken) external view returns (MessagingFee memory);\n\n /**\n * @notice Executes the send() operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The fee information supplied by the caller.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds from fees etc. on the src.\n * @return receipt The LayerZero messaging receipt from the send() operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) external payable returns (MessagingReceipt memory, OFTReceipt memory);\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/libs/OFTComposeMsgCodec.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nlibrary OFTComposeMsgCodec {\n // Offset constants for decoding composed messages\n uint8 private constant NONCE_OFFSET = 8;\n uint8 private constant SRC_EID_OFFSET = 12;\n uint8 private constant AMOUNT_LD_OFFSET = 44;\n uint8 private constant COMPOSE_FROM_OFFSET = 76;\n\n /**\n * @dev Encodes a OFT composed message.\n * @param _nonce The nonce value.\n * @param _srcEid The source endpoint ID.\n * @param _amountLD The amount in local decimals.\n * @param _composeMsg The composed message.\n * @return _msg The encoded Composed message.\n */\n function encode(\n uint64 _nonce,\n uint32 _srcEid,\n uint256 _amountLD,\n bytes memory _composeMsg // 0x[composeFrom][composeMsg]\n ) internal pure returns (bytes memory _msg) {\n _msg = abi.encodePacked(_nonce, _srcEid, _amountLD, _composeMsg);\n }\n\n /**\n * @dev Retrieves the nonce for the composed message.\n * @param _msg The message.\n * @return The nonce value.\n */\n function nonce(bytes calldata _msg) internal pure returns (uint64) {\n return uint64(bytes8(_msg[:NONCE_OFFSET]));\n }\n\n /**\n * @dev Retrieves the source endpoint ID for the composed message.\n * @param _msg The message.\n * @return The source endpoint ID.\n */\n function srcEid(bytes calldata _msg) internal pure returns (uint32) {\n return uint32(bytes4(_msg[NONCE_OFFSET:SRC_EID_OFFSET]));\n }\n\n /**\n * @dev Retrieves the amount in local decimals from the composed message.\n * @param _msg The message.\n * @return The amount in local decimals.\n */\n function amountLD(bytes calldata _msg) internal pure returns (uint256) {\n return uint256(bytes32(_msg[SRC_EID_OFFSET:AMOUNT_LD_OFFSET]));\n }\n\n /**\n * @dev Retrieves the composeFrom value from the composed message.\n * @param _msg The message.\n * @return The composeFrom value.\n */\n function composeFrom(bytes calldata _msg) internal pure returns (bytes32) {\n return bytes32(_msg[AMOUNT_LD_OFFSET:COMPOSE_FROM_OFFSET]);\n }\n\n /**\n * @dev Retrieves the composed message.\n * @param _msg The message.\n * @return The composed message.\n */\n function composeMsg(bytes calldata _msg) internal pure returns (bytes memory) {\n return _msg[COMPOSE_FROM_OFFSET:];\n }\n\n /**\n * @dev Converts an address to bytes32.\n * @param _addr The address to convert.\n * @return The bytes32 representation of the address.\n */\n function addressToBytes32(address _addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(_addr)));\n }\n\n /**\n * @dev Converts bytes32 to an address.\n * @param _b The bytes32 value to convert.\n * @return The address representation of bytes32.\n */\n function bytes32ToAddress(bytes32 _b) internal pure returns (address) {\n return address(uint160(uint256(_b)));\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/libs/OFTMsgCodec.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nlibrary OFTMsgCodec {\n // Offset constants for encoding and decoding OFT messages\n uint8 private constant SEND_TO_OFFSET = 32;\n uint8 private constant SEND_AMOUNT_SD_OFFSET = 40;\n\n /**\n * @dev Encodes an OFT LayerZero message.\n * @param _sendTo The recipient address.\n * @param _amountShared The amount in shared decimals.\n * @param _composeMsg The composed message.\n * @return _msg The encoded message.\n * @return hasCompose A boolean indicating whether the message has a composed payload.\n */\n function encode(\n bytes32 _sendTo,\n uint64 _amountShared,\n bytes memory _composeMsg\n ) internal view returns (bytes memory _msg, bool hasCompose) {\n hasCompose = _composeMsg.length > 0;\n // @dev Remote chains will want to know the composed function caller ie. msg.sender on the src.\n _msg = hasCompose\n ? abi.encodePacked(_sendTo, _amountShared, addressToBytes32(msg.sender), _composeMsg)\n : abi.encodePacked(_sendTo, _amountShared);\n }\n\n /**\n * @dev Checks if the OFT message is composed.\n * @param _msg The OFT message.\n * @return A boolean indicating whether the message is composed.\n */\n function isComposed(bytes calldata _msg) internal pure returns (bool) {\n return _msg.length > SEND_AMOUNT_SD_OFFSET;\n }\n\n /**\n * @dev Retrieves the recipient address from the OFT message.\n * @param _msg The OFT message.\n * @return The recipient address.\n */\n function sendTo(bytes calldata _msg) internal pure returns (bytes32) {\n return bytes32(_msg[:SEND_TO_OFFSET]);\n }\n\n /**\n * @dev Retrieves the amount in shared decimals from the OFT message.\n * @param _msg The OFT message.\n * @return The amount in shared decimals.\n */\n function amountSD(bytes calldata _msg) internal pure returns (uint64) {\n return uint64(bytes8(_msg[SEND_TO_OFFSET:SEND_AMOUNT_SD_OFFSET]));\n }\n\n /**\n * @dev Retrieves the composed message from the OFT message.\n * @param _msg The OFT message.\n * @return The composed message.\n */\n function composeMsg(bytes calldata _msg) internal pure returns (bytes memory) {\n return _msg[SEND_AMOUNT_SD_OFFSET:];\n }\n\n /**\n * @dev Converts an address to bytes32.\n * @param _addr The address to convert.\n * @return The bytes32 representation of the address.\n */\n function addressToBytes32(address _addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(_addr)));\n }\n\n /**\n * @dev Converts bytes32 to an address.\n * @param _b The bytes32 value to convert.\n * @return The address representation of bytes32.\n */\n function bytes32ToAddress(bytes32 _b) internal pure returns (address) {\n return address(uint160(uint256(_b)));\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/MintBurnOFTAdapter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\n// External imports\nimport { IERC20, IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\n// Local imports\nimport { IMintableBurnable } from \"./interfaces/IMintableBurnable.sol\";\nimport { OFTCore } from \"./OFTCore.sol\";\n\n/**\n * @title MintBurnOFTAdapter\n * @notice A variant of the standard OFT Adapter that uses an existing ERC20's mint and burn mechanisms for cross-chain transfers.\n *\n * @dev Inherits from OFTCore and provides implementations for _debit and _credit functions using a mintable and burnable token.\n */\nabstract contract MintBurnOFTAdapter is OFTCore {\n /// @dev The underlying ERC20 token.\n IERC20 internal immutable innerToken;\n\n /// @notice The contract responsible for minting and burning tokens.\n IMintableBurnable public immutable minterBurner;\n\n /**\n * @notice Initializes the MintBurnOFTAdapter contract.\n *\n * @param _token The address of the underlying ERC20 token.\n * @param _minterBurner The contract responsible for minting and burning tokens.\n * @param _lzEndpoint The LayerZero endpoint address.\n * @param _delegate The address of the delegate.\n *\n * @dev Calls the OFTCore constructor with the token's decimals, the endpoint, and the delegate.\n */\n constructor(\n address _token,\n IMintableBurnable _minterBurner,\n address _lzEndpoint,\n address _delegate\n ) OFTCore(IERC20Metadata(_token).decimals(), _lzEndpoint, _delegate) {\n innerToken = IERC20(_token);\n minterBurner = _minterBurner;\n }\n\n /**\n * @notice Retrieves the address of the underlying ERC20 token.\n *\n * @return The address of the adapted ERC20 token.\n *\n * @dev In the case of MintBurnOFTAdapter, address(this) and erc20 are NOT the same contract.\n */\n function token() public view returns (address) {\n return address(innerToken);\n }\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the underlying token to send.\n *\n * @return requiresApproval True if approval is required, false otherwise.\n *\n * @dev In this MintBurnOFTAdapter, approval is NOT required because it uses mint and burn privileges.\n */\n function approvalRequired() external pure virtual returns (bool) {\n return false;\n }\n\n /**\n * @notice Burns tokens from the sender's balance to prepare for sending.\n *\n * @param _from The address to debit the tokens from.\n * @param _amountLD The amount of tokens to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination chain ID.\n *\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, i.e., 1 token in, 1 token out.\n * If the 'innerToken' applies something like a transfer fee, the default will NOT work.\n * A pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual override returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n (amountSentLD, amountReceivedLD) = _debitView(_amountLD, _minAmountLD, _dstEid);\n // Burns tokens from the caller.\n minterBurner.burn(_from, amountSentLD);\n }\n\n /**\n * @notice Mints tokens to the specified address upon receiving them.\n *\n * @param _to The address to credit the tokens to.\n * @param _amountLD The amount of tokens to credit in local decimals.\n *\n * @return amountReceivedLD The amount of tokens actually received in local decimals.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, i.e., 1 token in, 1 token out.\n * If the 'innerToken' applies something like a transfer fee, the default will NOT work.\n * A pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /* _srcEid */\n ) internal virtual override returns (uint256 amountReceivedLD) {\n if (_to == address(0x0)) _to = address(0xdead); // _mint(...) does not support address(0x0)\n // Mints the tokens and transfers to the recipient.\n minterBurner.mint(_to, _amountLD);\n // In the case of NON-default OFTAdapter, the amountLD MIGHT not be equal to amountReceivedLD.\n return _amountLD;\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/OFTAdapter.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IERC20Metadata, IERC20 } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IOFT, OFTCore } from \"./OFTCore.sol\";\n\n/**\n * @title OFTAdapter Contract\n * @dev OFTAdapter is a contract that adapts an ERC-20 token to the OFT functionality.\n *\n * @dev For existing ERC20 tokens, this can be used to convert the token to crosschain compatibility.\n * @dev WARNING: ONLY 1 of these should exist for a given global mesh,\n * unless you make a NON-default implementation of OFT and needs to be done very carefully.\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountSentLD/amountReceivedLD.\n */\nabstract contract OFTAdapter is OFTCore {\n using SafeERC20 for IERC20;\n\n IERC20 internal immutable innerToken;\n\n /**\n * @dev Constructor for the OFTAdapter contract.\n * @param _token The address of the ERC-20 token to be adapted.\n * @param _lzEndpoint The LayerZero endpoint address.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(\n address _token,\n address _lzEndpoint,\n address _delegate\n ) OFTCore(IERC20Metadata(_token).decimals(), _lzEndpoint, _delegate) {\n innerToken = IERC20(_token);\n }\n\n /**\n * @dev Retrieves the address of the underlying ERC20 implementation.\n * @return The address of the adapted ERC-20 token.\n *\n * @dev In the case of OFTAdapter, address(this) and erc20 are NOT the same contract.\n */\n function token() public view returns (address) {\n return address(innerToken);\n }\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the 'token()' to send.\n * @return requiresApproval Needs approval of the underlying token implementation.\n *\n * @dev In the case of default OFTAdapter, approval is required.\n * @dev In non-default OFTAdapter contracts with something like mint and burn privileges, it would NOT need approval.\n */\n function approvalRequired() external pure virtual returns (bool) {\n return true;\n }\n\n /**\n * @dev Locks tokens from the sender's specified balance in this contract.\n * @param _from The address to debit from.\n * @param _amountLD The amount of tokens to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination chain ID.\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev msg.sender will need to approve this _amountLD of tokens to be locked inside of the contract.\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual override returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n (amountSentLD, amountReceivedLD) = _debitView(_amountLD, _minAmountLD, _dstEid);\n // @dev Lock tokens by moving them into this contract from the caller.\n innerToken.safeTransferFrom(_from, address(this), amountSentLD);\n }\n\n /**\n * @dev Credits tokens to the specified address.\n * @param _to The address to credit the tokens to.\n * @param _amountLD The amount of tokens to credit in local decimals.\n * @dev _srcEid The source chain ID.\n * @return amountReceivedLD The amount of tokens ACTUALLY received in local decimals.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /*_srcEid*/\n ) internal virtual override returns (uint256 amountReceivedLD) {\n // @dev Unlock the tokens and transfer to the recipient.\n innerToken.safeTransfer(_to, _amountLD);\n // @dev In the case of NON-default OFTAdapter, the amountLD MIGHT not be == amountReceivedLD.\n return _amountLD;\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/OFTCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { OApp, Origin } from \"@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol\";\nimport { OAppOptionsType3 } from \"@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol\";\nimport { IOAppMsgInspector } from \"@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppMsgInspector.sol\";\n\nimport { OAppPreCrimeSimulator } from \"@layerzerolabs/oapp-evm/contracts/precrime/OAppPreCrimeSimulator.sol\";\n\nimport { IOFT, SendParam, OFTLimit, OFTReceipt, OFTFeeDetail, MessagingReceipt, MessagingFee } from \"./interfaces/IOFT.sol\";\nimport { OFTMsgCodec } from \"./libs/OFTMsgCodec.sol\";\nimport { OFTComposeMsgCodec } from \"./libs/OFTComposeMsgCodec.sol\";\n\n/**\n * @title OFTCore\n * @dev Abstract contract for the OftChain (OFT) token.\n */\nabstract contract OFTCore is IOFT, OApp, OAppPreCrimeSimulator, OAppOptionsType3 {\n using OFTMsgCodec for bytes;\n using OFTMsgCodec for bytes32;\n\n // @notice Provides a conversion rate when swapping between denominations of SD and LD\n // - shareDecimals == SD == shared Decimals\n // - localDecimals == LD == local decimals\n // @dev Considers that tokens have different decimal amounts on various chains.\n // @dev eg.\n // For a token\n // - locally with 4 decimals --> 1.2345 => uint(12345)\n // - remotely with 2 decimals --> 1.23 => uint(123)\n // - The conversion rate would be 10 ** (4 - 2) = 100\n // @dev If you want to send 1.2345 -> (uint 12345), you CANNOT represent that value on the remote,\n // you can only display 1.23 -> uint(123).\n // @dev To preserve the dust that would otherwise be lost on that conversion,\n // we need to unify a denomination that can be represented on ALL chains inside of the OFT mesh\n uint256 public immutable decimalConversionRate;\n\n // @notice Msg types that are used to identify the various OFT operations.\n // @dev This can be extended in child contracts for non-default oft operations\n // @dev These values are used in things like combineOptions() in OAppOptionsType3.sol.\n uint16 public constant SEND = 1;\n uint16 public constant SEND_AND_CALL = 2;\n\n // Address of an optional contract to inspect both 'message' and 'options'\n address public msgInspector;\n event MsgInspectorSet(address inspector);\n\n /**\n * @dev Constructor.\n * @param _localDecimals The decimals of the token on the local chain (this chain).\n * @param _endpoint The address of the LayerZero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(uint8 _localDecimals, address _endpoint, address _delegate) OApp(_endpoint, _delegate) {\n if (_localDecimals < sharedDecimals()) revert InvalidLocalDecimals();\n decimalConversionRate = 10 ** (_localDecimals - sharedDecimals());\n }\n\n /**\n * @notice Retrieves interfaceID and the version of the OFT.\n * @return interfaceId The interface ID.\n * @return version The version.\n *\n * @dev interfaceId: This specific interface ID is '0x02e49c2c'.\n * @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.\n * @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.\n * ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)\n */\n function oftVersion() external pure virtual returns (bytes4 interfaceId, uint64 version) {\n return (type(IOFT).interfaceId, 1);\n }\n\n /**\n * @dev Retrieves the shared decimals of the OFT.\n * @return The shared decimals of the OFT.\n *\n * @dev Sets an implicit cap on the amount of tokens, over uint64.max() will need some sort of outbound cap / totalSupply cap\n * Lowest common decimal denominator between chains.\n * Defaults to 6 decimal places to provide up to 18,446,744,073,709.551615 units (max uint64).\n * For tokens exceeding this totalSupply(), they will need to override the sharedDecimals function with something smaller.\n * ie. 4 sharedDecimals would be 1,844,674,407,370,955.1615\n */\n function sharedDecimals() public view virtual returns (uint8) {\n return 6;\n }\n\n /**\n * @dev Sets the message inspector address for the OFT.\n * @param _msgInspector The address of the message inspector.\n *\n * @dev This is an optional contract that can be used to inspect both 'message' and 'options'.\n * @dev Set it to address(0) to disable it, or set it to a contract address to enable it.\n */\n function setMsgInspector(address _msgInspector) public virtual onlyOwner {\n msgInspector = _msgInspector;\n emit MsgInspectorSet(_msgInspector);\n }\n\n /**\n * @notice Provides the fee breakdown and settings data for an OFT. Unused in the default implementation.\n * @param _sendParam The parameters for the send operation.\n * @return oftLimit The OFT limit information.\n * @return oftFeeDetails The details of OFT fees.\n * @return oftReceipt The OFT receipt information.\n */\n function quoteOFT(\n SendParam calldata _sendParam\n )\n external\n view\n virtual\n returns (OFTLimit memory oftLimit, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory oftReceipt)\n {\n uint256 minAmountLD = 0; // Unused in the default implementation.\n uint256 maxAmountLD = IERC20(this.token()).totalSupply(); // Unused in the default implementation.\n oftLimit = OFTLimit(minAmountLD, maxAmountLD);\n\n // Unused in the default implementation; reserved for future complex fee details.\n oftFeeDetails = new OFTFeeDetail[](0);\n\n // @dev This is the same as the send() operation, but without the actual send.\n // - amountSentLD is the amount in local decimals that would be sent from the sender.\n // - amountReceivedLD is the amount in local decimals that will be credited to the recipient on the remote OFT instance.\n // @dev The amountSentLD MIGHT not equal the amount the user actually receives. HOWEVER, the default does.\n (uint256 amountSentLD, uint256 amountReceivedLD) = _debitView(\n _sendParam.amountLD,\n _sendParam.minAmountLD,\n _sendParam.dstEid\n );\n oftReceipt = OFTReceipt(amountSentLD, amountReceivedLD);\n }\n\n /**\n * @notice Provides a quote for the send() operation.\n * @param _sendParam The parameters for the send() operation.\n * @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.\n * @return msgFee The calculated LayerZero messaging fee from the send() operation.\n *\n * @dev MessagingFee: LayerZero msg fee\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n */\n function quoteSend(\n SendParam calldata _sendParam,\n bool _payInLzToken\n ) external view virtual returns (MessagingFee memory msgFee) {\n // @dev mock the amount to receive, this is the same operation used in the send().\n // The quote is as similar as possible to the actual send() operation.\n (, uint256 amountReceivedLD) = _debitView(_sendParam.amountLD, _sendParam.minAmountLD, _sendParam.dstEid);\n\n // @dev Builds the options and OFT message to quote in the endpoint.\n (bytes memory message, bytes memory options) = _buildMsgAndOptions(_sendParam, amountReceivedLD);\n\n // @dev Calculates the LayerZero fee for the send() operation.\n return _quote(_sendParam.dstEid, message, options, _payInLzToken);\n }\n\n /**\n * @dev Executes the send operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The calculated fee for the send() operation.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds.\n * @return msgReceipt The receipt for the send operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) external payable virtual returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) {\n return _send(_sendParam, _fee, _refundAddress);\n }\n\n /**\n * @dev Internal function to execute the send operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The calculated fee for the send() operation.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds.\n * @return msgReceipt The receipt for the send operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function _send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) internal virtual returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) {\n // @dev Applies the token transfers regarding this send() operation.\n // - amountSentLD is the amount in local decimals that was ACTUALLY sent/debited from the sender.\n // - amountReceivedLD is the amount in local decimals that will be received/credited to the recipient on the remote OFT instance.\n (uint256 amountSentLD, uint256 amountReceivedLD) = _debit(\n msg.sender,\n _sendParam.amountLD,\n _sendParam.minAmountLD,\n _sendParam.dstEid\n );\n\n // @dev Builds the options and OFT message to quote in the endpoint.\n (bytes memory message, bytes memory options) = _buildMsgAndOptions(_sendParam, amountReceivedLD);\n\n // @dev Sends the message to the LayerZero endpoint and returns the LayerZero msg receipt.\n msgReceipt = _lzSend(_sendParam.dstEid, message, options, _fee, _refundAddress);\n // @dev Formulate the OFT receipt.\n oftReceipt = OFTReceipt(amountSentLD, amountReceivedLD);\n\n emit OFTSent(msgReceipt.guid, _sendParam.dstEid, msg.sender, amountSentLD, amountReceivedLD);\n }\n\n /**\n * @dev Internal function to build the message and options.\n * @param _sendParam The parameters for the send() operation.\n * @param _amountLD The amount in local decimals.\n * @return message The encoded message.\n * @return options The encoded options.\n */\n function _buildMsgAndOptions(\n SendParam calldata _sendParam,\n uint256 _amountLD\n ) internal view virtual returns (bytes memory message, bytes memory options) {\n bool hasCompose;\n // @dev This generated message has the msg.sender encoded into the payload so the remote knows who the caller is.\n (message, hasCompose) = OFTMsgCodec.encode(\n _sendParam.to,\n _toSD(_amountLD),\n // @dev Must be include a non empty bytes if you want to compose, EVEN if you dont need it on the remote.\n // EVEN if you dont require an arbitrary payload to be sent... eg. '0x01'\n _sendParam.composeMsg\n );\n // @dev Change the msg type depending if its composed or not.\n uint16 msgType = hasCompose ? SEND_AND_CALL : SEND;\n // @dev Combine the callers _extraOptions with the enforced options via the OAppOptionsType3.\n options = combineOptions(_sendParam.dstEid, msgType, _sendParam.extraOptions);\n\n // @dev Optionally inspect the message and options depending if the OApp owner has set a msg inspector.\n // @dev If it fails inspection, needs to revert in the implementation. ie. does not rely on return boolean\n address inspector = msgInspector; // caches the msgInspector to avoid potential double storage read\n if (inspector != address(0)) IOAppMsgInspector(inspector).inspect(message, options);\n }\n\n /**\n * @dev Internal function to handle the receive on the LayerZero endpoint.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The encoded message.\n * @dev _executor The address of the executor.\n * @dev _extraData Additional data.\n */\n function _lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address /*_executor*/, // @dev unused in the default implementation.\n bytes calldata /*_extraData*/ // @dev unused in the default implementation.\n ) internal virtual override {\n // @dev The src sending chain doesnt know the address length on this chain (potentially non-evm)\n // Thus everything is bytes32() encoded in flight.\n address toAddress = _message.sendTo().bytes32ToAddress();\n // @dev Credit the amountLD to the recipient and return the ACTUAL amount the recipient received in local decimals\n uint256 amountReceivedLD = _credit(toAddress, _toLD(_message.amountSD()), _origin.srcEid);\n\n if (_message.isComposed()) {\n // @dev Proprietary composeMsg format for the OFT.\n bytes memory composeMsg = OFTComposeMsgCodec.encode(\n _origin.nonce,\n _origin.srcEid,\n amountReceivedLD,\n _message.composeMsg()\n );\n\n // @dev Stores the lzCompose payload that will be executed in a separate tx.\n // Standardizes functionality for executing arbitrary contract invocation on some non-evm chains.\n // @dev The off-chain executor will listen and process the msg based on the src-chain-callers compose options passed.\n // @dev The index is used when a OApp needs to compose multiple msgs on lzReceive.\n // For default OFT implementation there is only 1 compose msg per lzReceive, thus its always 0.\n endpoint.sendCompose(toAddress, _guid, 0 /* the index of the composed message*/, composeMsg);\n }\n\n emit OFTReceived(_guid, _origin.srcEid, toAddress, amountReceivedLD);\n }\n\n /**\n * @dev Internal function to handle the OAppPreCrimeSimulator simulated receive.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The LayerZero message.\n * @param _executor The address of the off-chain executor.\n * @param _extraData Arbitrary data passed by the msg executor.\n *\n * @dev Enables the preCrime simulator to mock sending lzReceive() messages,\n * routes the msg down from the OAppPreCrimeSimulator, and back up to the OAppReceiver.\n */\n function _lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual override {\n _lzReceive(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Check if the peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint ID to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n *\n * @dev Enables OAppPreCrimeSimulator to check whether a potential Inbound Packet is from a trusted source.\n */\n function isPeer(uint32 _eid, bytes32 _peer) public view virtual override returns (bool) {\n return peers[_eid] == _peer;\n }\n\n /**\n * @dev Internal function to remove dust from the given local decimal amount.\n * @param _amountLD The amount in local decimals.\n * @return amountLD The amount after removing dust.\n *\n * @dev Prevents the loss of dust when moving amounts between chains with different decimals.\n * @dev eg. uint(123) with a conversion rate of 100 becomes uint(100).\n */\n function _removeDust(uint256 _amountLD) internal view virtual returns (uint256 amountLD) {\n return (_amountLD / decimalConversionRate) * decimalConversionRate;\n }\n\n /**\n * @dev Internal function to convert an amount from shared decimals into local decimals.\n * @param _amountSD The amount in shared decimals.\n * @return amountLD The amount in local decimals.\n */\n function _toLD(uint64 _amountSD) internal view virtual returns (uint256 amountLD) {\n return _amountSD * decimalConversionRate;\n }\n\n /**\n * @dev Internal function to convert an amount from local decimals into shared decimals.\n * @param _amountLD The amount in local decimals.\n * @return amountSD The amount in shared decimals.\n */\n function _toSD(uint256 _amountLD) internal view virtual returns (uint64 amountSD) {\n return uint64(_amountLD / decimalConversionRate);\n }\n\n /**\n * @dev Internal function to mock the amount mutation from a OFT debit() operation.\n * @param _amountLD The amount to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @dev _dstEid The destination endpoint ID.\n * @return amountSentLD The amount sent, in local decimals.\n * @return amountReceivedLD The amount to be received on the remote chain, in local decimals.\n *\n * @dev This is where things like fees would be calculated and deducted from the amount to be received on the remote.\n */\n function _debitView(\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 /*_dstEid*/\n ) internal view virtual returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n // @dev Remove the dust so nothing is lost on the conversion between chains with different decimals for the token.\n amountSentLD = _removeDust(_amountLD);\n // @dev The amount to send is the same as amount received in the default implementation.\n amountReceivedLD = amountSentLD;\n\n // @dev Check for slippage.\n if (amountReceivedLD < _minAmountLD) {\n revert SlippageExceeded(amountReceivedLD, _minAmountLD);\n }\n }\n\n /**\n * @dev Internal function to perform a debit operation.\n * @param _from The address to debit.\n * @param _amountLD The amount to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination endpoint ID.\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev Defined here but are intended to be overriden depending on the OFT implementation.\n * @dev Depending on OFT implementation the _amountLD could differ from the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual returns (uint256 amountSentLD, uint256 amountReceivedLD);\n\n /**\n * @dev Internal function to perform a credit operation.\n * @param _to The address to credit.\n * @param _amountLD The amount to credit in local decimals.\n * @param _srcEid The source endpoint ID.\n * @return amountReceivedLD The amount ACTUALLY received in local decimals.\n *\n * @dev Defined here but are intended to be overriden depending on the OFT implementation.\n * @dev Depending on OFT implementation the _amountLD could differ from the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 _srcEid\n ) internal virtual returns (uint256 amountReceivedLD);\n}\n" + }, + "@openzeppelin/contracts/access/AccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/AccessControl.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\nimport \"../utils/Context.sol\";\nimport \"../utils/Strings.sol\";\nimport \"../utils/introspection/ERC165.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\n * members except through off-chain means by accessing the contract event logs. Some\n * applications may benefit from on-chain enumerability, for those cases see\n * {AccessControlEnumerable}.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```\n * function foo() public {\n * require(hasRole(MY_ROLE, msg.sender));\n * ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it.\n */\nabstract contract AccessControl is Context, IAccessControl, ERC165 {\n struct RoleData {\n mapping(address => bool) members;\n bytes32 adminRole;\n }\n\n mapping(bytes32 => RoleData) private _roles;\n\n bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n /**\n * @dev Modifier that checks that an account has a specific role. Reverts\n * with a standardized message including the required role.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n *\n * _Available since v4.1._\n */\n modifier onlyRole(bytes32 role) {\n _checkRole(role, _msgSender());\n _;\n }\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) public view override returns (bool) {\n return _roles[role].members[account];\n }\n\n /**\n * @dev Revert with a standard message if `account` is missing `role`.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n */\n function _checkRole(bytes32 role, address account) internal view {\n if (!hasRole(role, account)) {\n revert(\n string(\n abi.encodePacked(\n \"AccessControl: account \",\n Strings.toHexString(uint160(account), 20),\n \" is missing role \",\n Strings.toHexString(uint256(role), 32)\n )\n )\n );\n }\n }\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) public view override returns (bytes32) {\n return _roles[role].adminRole;\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _grantRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _revokeRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been revoked `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) public virtual override {\n require(account == _msgSender(), \"AccessControl: can only renounce roles for self\");\n\n _revokeRole(role, account);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event. Note that unlike {grantRole}, this function doesn't perform any\n * checks on the calling account.\n *\n * [WARNING]\n * ====\n * This function should only be called from the constructor when setting\n * up the initial roles for the system.\n *\n * Using this function in any other way is effectively circumventing the admin\n * system imposed by {AccessControl}.\n * ====\n *\n * NOTE: This function is deprecated in favor of {_grantRole}.\n */\n function _setupRole(bytes32 role, address account) internal virtual {\n _grantRole(role, account);\n }\n\n /**\n * @dev Sets `adminRole` as ``role``'s admin role.\n *\n * Emits a {RoleAdminChanged} event.\n */\n function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n bytes32 previousAdminRole = getRoleAdmin(role);\n _roles[role].adminRole = adminRole;\n emit RoleAdminChanged(role, previousAdminRole, adminRole);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * Internal function without access restriction.\n */\n function _grantRole(bytes32 role, address account) internal virtual {\n if (!hasRole(role, account)) {\n _roles[role].members[account] = true;\n emit RoleGranted(role, account, _msgSender());\n }\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * Internal function without access restriction.\n */\n function _revokeRole(bytes32 role, address account) internal virtual {\n if (hasRole(role, account)) {\n _roles[role].members[account] = false;\n emit RoleRevoked(role, account, _msgSender());\n }\n }\n}\n" + }, + "@openzeppelin/contracts/access/AccessControlEnumerable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/AccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControlEnumerable.sol\";\nimport \"./AccessControl.sol\";\nimport \"../utils/structs/EnumerableSet.sol\";\n\n/**\n * @dev Extension of {AccessControl} that allows enumerating the members of each role.\n */\nabstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {\n using EnumerableSet for EnumerableSet.AddressSet;\n\n mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) public view override returns (address) {\n return _roleMembers[role].at(index);\n }\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) public view override returns (uint256) {\n return _roleMembers[role].length();\n }\n\n /**\n * @dev Overload {_grantRole} to track enumerable memberships\n */\n function _grantRole(bytes32 role, address account) internal virtual override {\n super._grantRole(role, account);\n _roleMembers[role].add(account);\n }\n\n /**\n * @dev Overload {_revokeRole} to track enumerable memberships\n */\n function _revokeRole(bytes32 role, address account) internal virtual override {\n super._revokeRole(role, account);\n _roleMembers[role].remove(account);\n }\n}\n" + }, + "@openzeppelin/contracts/access/IAccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n /**\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n *\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n * {RoleAdminChanged} not being emitted signaling this.\n *\n * _Available since v3.1._\n */\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n /**\n * @dev Emitted when `account` is granted `role`.\n *\n * `sender` is the account that originated the contract call, an admin role\n * bearer except when using {AccessControl-_setupRole}.\n */\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Emitted when `account` is revoked `role`.\n *\n * `sender` is the account that originated the contract call:\n * - if using `revokeRole`, it is the admin role bearer\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\n */\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) external view returns (bool);\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been granted `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) external;\n}\n" + }, + "@openzeppelin/contracts/access/IAccessControlEnumerable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\n\n/**\n * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.\n */\ninterface IAccessControlEnumerable is IAccessControl {\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) external view returns (address);\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) external view returns (uint256);\n}\n" + }, + "@openzeppelin/contracts/access/Ownable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n" + }, + "@openzeppelin/contracts/security/Pausable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/ERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/ERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"./extensions/IERC20Metadata.sol\";\nimport \"../../utils/Context.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20, IERC20Metadata {\n mapping(address => uint256) private _balances;\n\n mapping(address => mapping(address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n\n /**\n * @dev Sets the values for {name} and {symbol}.\n *\n * The default value of {decimals} is 18. To select a different value for\n * {decimals} you should overload it.\n *\n * All two of these values are immutable: they can only be set once during\n * construction.\n */\n constructor(string memory name_, string memory symbol_) {\n _name = name_;\n _symbol = symbol_;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless this function is\n * overridden;\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `recipient` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n _approve(_msgSender(), spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * Requirements:\n *\n * - `sender` and `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n * - the caller must have allowance for ``sender``'s tokens of at least\n * `amount`.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public virtual override returns (bool) {\n _transfer(sender, recipient, amount);\n\n uint256 currentAllowance = _allowances[sender][_msgSender()];\n require(currentAllowance >= amount, \"ERC20: transfer amount exceeds allowance\");\n unchecked {\n _approve(sender, _msgSender(), currentAllowance - amount);\n }\n\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n uint256 currentAllowance = _allowances[_msgSender()][spender];\n require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n unchecked {\n _approve(_msgSender(), spender, currentAllowance - subtractedValue);\n }\n\n return true;\n }\n\n /**\n * @dev Moves `amount` of tokens from `sender` to `recipient`.\n *\n * This internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `sender` cannot be the zero address.\n * - `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n */\n function _transfer(\n address sender,\n address recipient,\n uint256 amount\n ) internal virtual {\n require(sender != address(0), \"ERC20: transfer from the zero address\");\n require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(sender, recipient, amount);\n\n uint256 senderBalance = _balances[sender];\n require(senderBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n unchecked {\n _balances[sender] = senderBalance - amount;\n }\n _balances[recipient] += amount;\n\n emit Transfer(sender, recipient, amount);\n\n _afterTokenTransfer(sender, recipient, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply += amount;\n _balances[account] += amount;\n emit Transfer(address(0), account, amount);\n\n _afterTokenTransfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n uint256 accountBalance = _balances[account];\n require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n unchecked {\n _balances[account] = accountBalance - amount;\n }\n _totalSupply -= amount;\n\n emit Transfer(account, address(0), amount);\n\n _afterTokenTransfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n\n /**\n * @dev Hook that is called after any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * has been transferred to `to`.\n * - when `from` is zero, `amount` tokens have been minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _afterTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC721 token receiver interface\n * @dev Interface for any contract that wants to support safeTransfers\n * from ERC721 asset contracts.\n */\ninterface IERC721Receiver {\n /**\n * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}\n * by `operator` from `from`, this function is called.\n *\n * It must return its Solidity selector to confirm the token transfer.\n * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.\n *\n * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.\n */\n function onERC721Received(\n address operator,\n address from,\n uint256 tokenId,\n bytes calldata data\n ) external returns (bytes4);\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/ERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC165.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n *\n * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.\n */\nabstract contract ERC165 is IERC165 {\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IERC165).interfaceId;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/IERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeMath.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)\n\npragma solidity ^0.8.0;\n\n// CAUTION\n// This version of SafeMath should only be used with Solidity 0.8 or later,\n// because it relies on the compiler's built in overflow checks.\n\n/**\n * @dev Wrappers over Solidity's arithmetic operations.\n *\n * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler\n * now has built in overflow checking.\n */\nlibrary SafeMath {\n /**\n * @dev Returns the addition of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n uint256 c = a + b;\n if (c < a) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the substraction of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b > a) return (false, 0);\n return (true, a - b);\n }\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n // benefit is lost if 'b' is also tested.\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n if (a == 0) return (true, 0);\n uint256 c = a * b;\n if (c / a != b) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the division of two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a / b);\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a % b);\n }\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `+` operator.\n *\n * Requirements:\n *\n * - Addition cannot overflow.\n */\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\n return a + b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting on\n * overflow (when the result is negative).\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\n return a - b;\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `*` operator.\n *\n * Requirements:\n *\n * - Multiplication cannot overflow.\n */\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\n return a * b;\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator.\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\n return a / b;\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting when dividing by zero.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\n return a % b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\n * overflow (when the result is negative).\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {trySub}.\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b <= a, errorMessage);\n return a - b;\n }\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting with custom message on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator. Note: this function uses a\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\n * uses an invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a / b;\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting with custom message when dividing by zero.\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {tryMod}.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a % b;\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Strings.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n bytes16 private constant _HEX_SYMBOLS = \"0123456789abcdef\";\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n // Inspired by OraclizeAPI's implementation - MIT licence\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\n\n if (value == 0) {\n return \"0\";\n }\n uint256 temp = value;\n uint256 digits;\n while (temp != 0) {\n digits++;\n temp /= 10;\n }\n bytes memory buffer = new bytes(digits);\n while (value != 0) {\n digits -= 1;\n buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\n value /= 10;\n }\n return string(buffer);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n if (value == 0) {\n return \"0x00\";\n }\n uint256 temp = value;\n uint256 length = 0;\n while (temp != 0) {\n length++;\n temp >>= 8;\n }\n return toHexString(value, length);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _HEX_SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/structs/EnumerableSet.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 => uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n" + }, + "contracts/automation/AbstractCCIPBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IRouterClient } from \"@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol\";\nimport { Client } from \"@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol\";\n\nabstract contract AbstractCCIPBridgeHelperModule is AbstractSafeModule {\n /**\n * @notice Bridges a token from the source chain to the destination chain using CCIP\n * @param ccipRouter The CCIP router contract\n * @param destinationChainSelector The selector for the destination chain\n * @param token The token to bridge\n * @param amount The amount of token to bridge\n */\n function _bridgeTokenWithCCIP(\n IRouterClient ccipRouter,\n uint64 destinationChainSelector,\n IERC20 token,\n uint256 amount\n ) internal {\n bool success;\n\n // Approve CCIP Router to move the token\n success = safeContract.execTransactionFromModule(\n address(token),\n 0, // Value\n abi.encodeWithSelector(token.approve.selector, ccipRouter, amount),\n 0 // Call\n );\n require(success, \"Failed to approve token\");\n\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(token),\n amount: amount\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory ccipMessage = Client.EVM2AnyMessage({\n receiver: abi.encode(address(safeContract)), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n // Get CCIP fee\n uint256 ccipFee = ccipRouter.getFee(\n destinationChainSelector,\n ccipMessage\n );\n\n // Send CCIP message\n success = safeContract.execTransactionFromModule(\n address(ccipRouter),\n ccipFee, // Value\n abi.encodeWithSelector(\n ccipRouter.ccipSend.selector,\n destinationChainSelector,\n ccipMessage\n ),\n 0 // Call\n );\n require(success, \"Failed to send CCIP message\");\n }\n}\n" + }, + "contracts/automation/AbstractLZBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\n\nimport { IOFT, SendParam } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\nimport { MessagingFee } from \"@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol\";\nimport { OptionsBuilder } from \"@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nabstract contract AbstractLZBridgeHelperModule is AbstractSafeModule {\n using OptionsBuilder for bytes;\n\n /**\n * @dev Bridges token using LayerZero.\n * @param lzEndpointId LayerZero endpoint id.\n * @param token Token to bridge.\n * @param lzAdapter LZ Adapter to use.\n * @param amount Amount of token to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n * @param isNativeToken Whether the token is native token.\n */\n function _bridgeTokenWithLz(\n uint32 lzEndpointId,\n IERC20 token,\n IOFT lzAdapter,\n uint256 amount,\n uint256 slippageBps,\n bool isNativeToken\n ) internal {\n bool success;\n\n if (!isNativeToken) {\n // Approve LZ Adapter to move the token\n success = safeContract.execTransactionFromModule(\n address(token),\n 0, // Value\n abi.encodeWithSelector(\n token.approve.selector,\n address(lzAdapter),\n amount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve token\");\n }\n\n // Calculate minimum amount to receive\n uint256 minAmount = (amount * (10000 - slippageBps)) / 10000;\n\n // Hardcoded gaslimit of 400k\n bytes memory options = OptionsBuilder\n .newOptions()\n .addExecutorLzReceiveOption(400000, 0);\n\n // Build send param\n SendParam memory sendParam = SendParam({\n dstEid: lzEndpointId,\n to: bytes32(uint256(uint160(address(safeContract)))),\n amountLD: amount,\n minAmountLD: minAmount,\n extraOptions: options,\n composeMsg: bytes(\"\"),\n oftCmd: bytes(\"\")\n });\n\n // Compute fees\n MessagingFee memory msgFee = lzAdapter.quoteSend(sendParam, false);\n\n uint256 value = isNativeToken\n ? amount + msgFee.nativeFee\n : msgFee.nativeFee;\n\n // Execute transaction\n success = safeContract.execTransactionFromModule(\n address(lzAdapter),\n value,\n abi.encodeWithSelector(\n lzAdapter.send.selector,\n sendParam,\n msgFee,\n address(safeContract)\n ),\n 0\n );\n require(success, \"Failed to bridge token\");\n }\n}\n" + }, + "contracts/automation/AbstractSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { ISafe } from \"../interfaces/ISafe.sol\";\n\nabstract contract AbstractSafeModule is AccessControlEnumerable {\n ISafe public immutable safeContract;\n\n bytes32 public constant OPERATOR_ROLE = keccak256(\"OPERATOR_ROLE\");\n\n modifier onlySafe() {\n require(\n msg.sender == address(safeContract),\n \"Caller is not the safe contract\"\n );\n _;\n }\n\n modifier onlyOperator() {\n require(\n hasRole(OPERATOR_ROLE, msg.sender),\n \"Caller is not an operator\"\n );\n _;\n }\n\n constructor(address _safeContract) {\n safeContract = ISafe(_safeContract);\n _grantRole(DEFAULT_ADMIN_ROLE, address(safeContract));\n _grantRole(OPERATOR_ROLE, address(safeContract));\n }\n\n /**\n * @dev Helps recovering any tokens accidentally sent to this module.\n * @param token Token to transfer. 0x0 to transfer Native token.\n * @param amount Amount to transfer. 0 to transfer all balance.\n */\n function transferTokens(address token, uint256 amount) external onlySafe {\n if (address(token) == address(0)) {\n // Move ETH\n amount = amount > 0 ? amount : address(this).balance;\n payable(address(safeContract)).transfer(amount);\n return;\n }\n\n // Move all balance if amount set to 0\n amount = amount > 0 ? amount : IERC20(token).balanceOf(address(this));\n\n // Transfer to Safe contract\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(token).transfer(address(safeContract), amount);\n }\n\n receive() external payable {\n // Accept ETH to pay for bridge fees\n }\n}\n" + }, + "contracts/automation/BaseBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// solhint-disable-next-line max-line-length\nimport { AbstractCCIPBridgeHelperModule, AbstractSafeModule, IRouterClient } from \"./AbstractCCIPBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { BridgedWOETHStrategy } from \"../strategies/BridgedWOETHStrategy.sol\";\n\ncontract BaseBridgeHelperModule is\n AccessControlEnumerable,\n AbstractCCIPBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0x98a0CbeF61bD2D21435f433bE4CD42B56B38CC93);\n IWETH9 public constant weth =\n IWETH9(0x4200000000000000000000000000000000000006);\n IERC20 public constant oethb =\n IERC20(0xDBFeFD2e8460a6Ee4955A68582F85708BAEA60A3);\n IERC4626 public constant bridgedWOETH =\n IERC4626(0xD8724322f44E5c58D7A815F542036fb17DbbF839);\n\n BridgedWOETHStrategy public constant bridgedWOETHStrategy =\n BridgedWOETHStrategy(0x80c864704DD06C3693ed5179190786EE38ACf835);\n\n IRouterClient public constant CCIP_ROUTER =\n IRouterClient(0x881e3A65B4d4a04dD529061dd0071cf975F58bCD);\n\n uint64 public constant CCIP_ETHEREUM_CHAIN_SELECTOR = 5009297550715157269;\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Ethereum.\n * @param woethAmount Amount of wOETH to bridge.\n */\n function bridgeWOETHToEthereum(uint256 woethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_ETHEREUM_CHAIN_SELECTOR,\n IERC20(address(bridgedWOETH)),\n woethAmount\n );\n }\n\n /**\n * @dev Bridges WETH to Ethereum.\n * @param wethAmount Amount of WETH to bridge.\n */\n function bridgeWETHToEthereum(uint256 wethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_ETHEREUM_CHAIN_SELECTOR,\n IERC20(address(weth)),\n wethAmount\n );\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem the wOETH for WETH using the Vault.\n * @return Amount of WETH received.\n */\n function depositWOETH(uint256 woethAmount, bool redeemWithVault)\n external\n onlyOperator\n returns (uint256)\n {\n return _depositWOETH(woethAmount, redeemWithVault);\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy and bridges it to Ethereum.\n * @param woethAmount Amount of wOETH to deposit.\n * @return Amount of WETH received.\n */\n function depositWOETHAndBridgeWETH(uint256 woethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n uint256 wethAmount = _depositWOETH(woethAmount, true);\n bridgeWETHToEthereum(wethAmount);\n return wethAmount;\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem the wOETH for WETH using the Vault.\n * @return Amount of WETH received.\n */\n function _depositWOETH(uint256 woethAmount, bool redeemWithVault)\n internal\n returns (uint256)\n {\n // Update oracle price\n bridgedWOETHStrategy.updateWOETHOraclePrice();\n\n // Rebase to account for any yields from price update\n vault.rebase();\n\n uint256 oethbAmount = oethb.balanceOf(address(safeContract));\n\n // Approve bridgedWOETH strategy to move wOETH\n bool success = safeContract.execTransactionFromModule(\n address(bridgedWOETH),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETH.approve.selector,\n address(bridgedWOETHStrategy),\n woethAmount\n ),\n 0 // Call\n );\n\n // Deposit to bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.depositBridgedWOETH.selector,\n woethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to deposit bridged WOETH\");\n\n oethbAmount = oethb.balanceOf(address(safeContract)) - oethbAmount;\n\n // Rebase to account for any yields from price update\n // and backing asset change from deposit\n vault.rebase();\n\n if (!redeemWithVault) {\n return oethbAmount;\n }\n\n // Redeem for WETH using Vault\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethbAmount,\n oethbAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETHb\");\n\n return oethbAmount;\n }\n\n /**\n * @dev Deposits WETH into the Vault and redeems wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to deposit.\n * @return Amount of wOETH received.\n */\n function depositWETHAndRedeemWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _withdrawWOETH(wethAmount);\n }\n\n function depositWETHAndBridgeWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n uint256 woethAmount = _withdrawWOETH(wethAmount);\n bridgeWOETHToEthereum(woethAmount);\n return woethAmount;\n }\n\n /**\n * @dev Withdraws wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to use to withdraw.\n * @return Amount of wOETH received.\n */\n function _withdrawWOETH(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETHb with WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETHb\");\n\n // Approve bridgedWOETH strategy to move OETHb\n success = safeContract.execTransactionFromModule(\n address(oethb),\n 0, // Value\n abi.encodeWithSelector(\n oethb.approve.selector,\n address(bridgedWOETHStrategy),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETHb\");\n\n uint256 woethAmount = bridgedWOETH.balanceOf(address(safeContract));\n\n // Withdraw from bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.withdrawBridgedWOETH.selector,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to withdraw bridged WOETH\");\n\n woethAmount =\n bridgedWOETH.balanceOf(address(safeContract)) -\n woethAmount;\n\n return woethAmount;\n }\n}\n" + }, + "contracts/automation/ClaimBribesSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { ICLGauge } from \"../interfaces/aerodrome/ICLGauge.sol\";\nimport { ICLPool } from \"../interfaces/aerodrome/ICLPool.sol\";\n\nstruct BribePoolInfo {\n address poolAddress;\n address rewardContractAddress;\n address[] rewardTokens;\n}\n\ninterface IAerodromeVoter {\n function claimBribes(\n address[] memory _bribes,\n address[][] memory _tokens,\n uint256 _tokenId\n ) external;\n}\n\ninterface IVeNFT {\n function ownerOf(uint256 tokenId) external view returns (address);\n\n function ownerToNFTokenIdList(address owner, uint256 index)\n external\n view\n returns (uint256);\n}\n\ninterface ICLRewardContract {\n function rewards(uint256 index) external view returns (address);\n\n function rewardsListLength() external view returns (uint256);\n}\n\ncontract ClaimBribesSafeModule is AbstractSafeModule {\n IAerodromeVoter public immutable voter;\n address public immutable veNFT;\n\n uint256[] nftIds;\n mapping(uint256 => uint256) nftIdIndex;\n\n BribePoolInfo[] bribePools;\n mapping(address => uint256) bribePoolIndex;\n\n event NFTIdAdded(uint256 nftId);\n event NFTIdRemoved(uint256 nftId);\n\n event BribePoolAdded(address bribePool);\n event BribePoolRemoved(address bribePool);\n\n constructor(\n address _safeContract,\n address _voter,\n address _veNFT\n ) AbstractSafeModule(_safeContract) {\n voter = IAerodromeVoter(_voter);\n veNFT = _veNFT;\n }\n\n /**\n * @dev Claim bribes for a range of NFTs\n * @param nftIndexStart The start index of the NFTs\n * @param nftIndexEnd The end index of the NFTs\n * @param silent Doesn't revert if the claim fails when true\n */\n function claimBribes(\n uint256 nftIndexStart,\n uint256 nftIndexEnd,\n bool silent\n ) external onlyOperator {\n if (nftIndexEnd < nftIndexStart) {\n (nftIndexStart, nftIndexEnd) = (nftIndexEnd, nftIndexStart);\n }\n uint256 nftCount = nftIds.length;\n nftIndexEnd = nftCount < nftIndexEnd ? nftCount : nftIndexEnd;\n\n (\n address[] memory rewardContractAddresses,\n address[][] memory rewardTokens\n ) = _getRewardsInfoArray();\n\n for (uint256 i = nftIndexStart; i < nftIndexEnd; i++) {\n uint256 nftId = nftIds[i];\n bool success = safeContract.execTransactionFromModule(\n address(voter),\n 0, // Value\n abi.encodeWithSelector(\n IAerodromeVoter.claimBribes.selector,\n rewardContractAddresses,\n rewardTokens,\n nftId\n ),\n 0 // Call\n );\n\n require(success || silent, \"ClaimBribes failed\");\n }\n }\n\n /**\n * @dev Get the reward contract address and reward tokens for all pools\n * @return rewardContractAddresses The reward contract addresses\n * @return rewardTokens The reward tokens\n */\n function _getRewardsInfoArray()\n internal\n view\n returns (\n address[] memory rewardContractAddresses,\n address[][] memory rewardTokens\n )\n {\n BribePoolInfo[] memory _bribePools = bribePools;\n uint256 bribePoolCount = _bribePools.length;\n rewardContractAddresses = new address[](bribePoolCount);\n rewardTokens = new address[][](bribePoolCount);\n\n for (uint256 i = 0; i < bribePoolCount; i++) {\n rewardContractAddresses[i] = _bribePools[i].rewardContractAddress;\n rewardTokens[i] = _bribePools[i].rewardTokens;\n }\n }\n\n /***************************************\n NFT Management\n ****************************************/\n /**\n * @dev Add NFT IDs to the list\n * @param _nftIds The NFT IDs to add\n */\n function addNFTIds(uint256[] memory _nftIds) external onlyOperator {\n for (uint256 i = 0; i < _nftIds.length; i++) {\n uint256 nftId = _nftIds[i];\n if (nftIdExists(nftId)) {\n // If it already exists, skip\n continue;\n }\n\n // Make sure the NFT is owned by the Safe\n require(\n IVeNFT(veNFT).ownerOf(nftId) == address(safeContract),\n \"NFT not owned by safe\"\n );\n\n nftIdIndex[nftId] = nftIds.length;\n nftIds.push(nftId);\n\n emit NFTIdAdded(nftId);\n }\n }\n\n /**\n * @dev Remove NFT IDs from the list\n * @param _nftIds The NFT IDs to remove\n */\n function removeNFTIds(uint256[] memory _nftIds) external onlyOperator {\n for (uint256 i = 0; i < _nftIds.length; i++) {\n uint256 nftId = _nftIds[i];\n if (!nftIdExists(nftId)) {\n // If it doesn't exist, skip\n continue;\n }\n\n uint256 index = nftIdIndex[nftId];\n uint256 lastNftId = nftIds[nftIds.length - 1];\n nftIds[index] = lastNftId;\n nftIdIndex[lastNftId] = index;\n nftIds.pop();\n\n emit NFTIdRemoved(nftId);\n }\n }\n\n /**\n * @dev Check if a NFT exists on the list\n * @param nftId The NFT ID to check\n * @return true if the NFT ID exists, false otherwise\n */\n function nftIdExists(uint256 nftId) public view returns (bool) {\n uint256 index = nftIdIndex[nftId];\n uint256[] memory _nftIds = nftIds;\n return (index < _nftIds.length) && _nftIds[index] == nftId;\n }\n\n /**\n * @dev Get the length of the nftIds list\n * @return The length of the nftIds list\n */\n function getNFTIdsLength() external view returns (uint256) {\n return nftIds.length;\n }\n\n /**\n * @dev Get all NFT IDs\n * @return The NFT IDs\n */\n function getAllNFTIds() external view returns (uint256[] memory) {\n return nftIds;\n }\n\n /**\n * @dev Fetch all NFT IDs from the veNFT contract\n * @notice This can revert if Safe owns too many NFTs since tx will be huge.\n * This function is public, anyone can call it, since it only fetches\n * the NFT IDs owned by the Safe. It shouldn't cause us any issues.\n */\n function fetchNFTIds() external {\n // Purge the array\n delete nftIds;\n\n uint256 i = 0;\n while (true) {\n uint256 nftId = IVeNFT(veNFT).ownerToNFTokenIdList(\n address(safeContract),\n i\n );\n if (nftId == 0) {\n break;\n }\n\n nftIdIndex[nftId] = nftIds.length;\n nftIds.push(nftId);\n i++;\n }\n }\n\n /**\n * @dev Remove all NFT IDs from the list\n */\n function removeAllNFTIds() external onlyOperator {\n uint256 length = nftIds.length;\n for (uint256 i = 0; i < length; i++) {\n uint256 nftId = nftIds[i];\n delete nftIdIndex[nftId];\n emit NFTIdRemoved(nftId);\n }\n\n delete nftIds;\n }\n\n /***************************************\n Bribe Pool Management\n ****************************************/\n // @dev Whitelist a pool to claim bribes from\n // @param _poolAddress The address of the pool to whitelist\n function addBribePool(address _poolAddress, bool _isVotingContract)\n external\n onlySafe\n {\n BribePoolInfo memory bribePool;\n\n if (_isVotingContract) {\n bribePool = BribePoolInfo({\n poolAddress: _poolAddress,\n rewardContractAddress: _poolAddress,\n rewardTokens: _getRewardTokenAddresses(_poolAddress)\n });\n } else {\n // Find the gauge address\n address _gaugeAddress = ICLPool(_poolAddress).gauge();\n // And the reward contract address\n address _rewardContractAddress = ICLGauge(_gaugeAddress)\n .feesVotingReward();\n\n bribePool = BribePoolInfo({\n poolAddress: _poolAddress,\n rewardContractAddress: _rewardContractAddress,\n rewardTokens: _getRewardTokenAddresses(_rewardContractAddress)\n });\n }\n\n if (bribePoolExists(_poolAddress)) {\n // Update if it already exists\n bribePools[bribePoolIndex[_poolAddress]] = bribePool;\n } else {\n // If not, Append to the list\n bribePoolIndex[_poolAddress] = bribePools.length;\n bribePools.push(bribePool);\n }\n\n emit BribePoolAdded(_poolAddress);\n }\n\n /**\n * @dev Update the reward token addresses for all pools\n */\n function updateRewardTokenAddresses() external onlyOperator {\n BribePoolInfo[] storage _bribePools = bribePools;\n for (uint256 i = 0; i < _bribePools.length; i++) {\n BribePoolInfo storage bribePool = _bribePools[i];\n bribePool.rewardTokens = _getRewardTokenAddresses(\n bribePool.rewardContractAddress == bribePool.poolAddress\n ? bribePool.poolAddress\n : bribePool.rewardContractAddress\n );\n }\n }\n\n /**\n * @dev Get the reward token addresses for a given reward contract address\n * @param _rewardContractAddress The address of the reward contract\n * @return _rewardTokens The reward token addresses\n */\n function _getRewardTokenAddresses(address _rewardContractAddress)\n internal\n view\n returns (address[] memory)\n {\n address[] memory _rewardTokens = new address[](\n ICLRewardContract(_rewardContractAddress).rewardsListLength()\n );\n for (uint256 i = 0; i < _rewardTokens.length; i++) {\n _rewardTokens[i] = ICLRewardContract(_rewardContractAddress)\n .rewards(i);\n }\n\n return _rewardTokens;\n }\n\n /**\n * @dev Remove a bribe pool from the list\n * @param _poolAddress The address of the pool to remove\n */\n function removeBribePool(address _poolAddress) external onlySafe {\n if (!bribePoolExists(_poolAddress)) {\n // If it doesn't exist, skip\n return;\n }\n\n uint256 index = bribePoolIndex[_poolAddress];\n BribePoolInfo memory lastBribePool = bribePools[bribePools.length - 1];\n bribePools[index] = lastBribePool;\n bribePoolIndex[lastBribePool.poolAddress] = index;\n bribePools.pop();\n\n emit BribePoolRemoved(_poolAddress);\n }\n\n /**\n * @dev Check if a bribe pool exists\n * @param bribePool The address of the pool to check\n * @return true if the pool exists, false otherwise\n */\n function bribePoolExists(address bribePool) public view returns (bool) {\n BribePoolInfo[] memory _bribePools = bribePools;\n uint256 poolIndex = bribePoolIndex[bribePool];\n return\n poolIndex < _bribePools.length &&\n _bribePools[poolIndex].poolAddress == bribePool;\n }\n\n /**\n * @dev Get the length of the bribe pools list\n * @return The length of the bribe pools list\n */\n function getBribePoolsLength() external view returns (uint256) {\n return bribePools.length;\n }\n}\n" + }, + "contracts/automation/ClaimStrategyRewardsSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\n\nimport { ISafe } from \"../interfaces/ISafe.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract ClaimStrategyRewardsSafeModule is AbstractSafeModule {\n using SafeERC20 for IERC20;\n\n mapping(address => bool) public isStrategyWhitelisted;\n address[] public strategies;\n\n event StrategyAdded(address strategy);\n event StrategyRemoved(address strategy);\n\n event ClaimRewardsFailed(address strategy);\n\n constructor(\n address _safeAddress,\n address operator,\n address[] memory _strategies\n ) AbstractSafeModule(_safeAddress) {\n _grantRole(OPERATOR_ROLE, operator);\n\n // Whitelist all strategies\n for (uint256 i = 0; i < _strategies.length; i++) {\n _addStrategy(_strategies[i]);\n }\n }\n\n /**\n * @dev Claim rewards from all whitelisted strategies\n * @param silent Doesn't revert on error if set to true\n */\n function claimRewards(bool silent) external onlyRole(OPERATOR_ROLE) {\n uint256 strategiesLength = strategies.length;\n for (uint256 i = 0; i < strategiesLength; i++) {\n address strategy = strategies[i];\n\n // Execute `collectRewardTokens` for all strategies\n bool success = safeContract.execTransactionFromModule(\n strategy, // To\n 0, // Value\n abi.encodeWithSelector(IStrategy.collectRewardTokens.selector),\n 0 // Call\n );\n\n if (!success) {\n emit ClaimRewardsFailed(strategy);\n }\n\n require(success || silent, \"Failed to claim rewards\");\n }\n }\n\n /**\n * @dev Add a strategy to the whitelist\n * @param _strategy The address of the strategy to add\n */\n function addStrategy(address _strategy)\n external\n onlyRole(DEFAULT_ADMIN_ROLE)\n {\n _addStrategy(_strategy);\n }\n\n function _addStrategy(address _strategy) internal {\n require(\n !isStrategyWhitelisted[_strategy],\n \"Strategy already whitelisted\"\n );\n isStrategyWhitelisted[_strategy] = true;\n strategies.push(_strategy);\n emit StrategyAdded(_strategy);\n }\n\n /**\n * @dev Remove a strategy from the whitelist\n * @param _strategy The address of the strategy to remove\n */\n function removeStrategy(address _strategy)\n external\n onlyRole(DEFAULT_ADMIN_ROLE)\n {\n require(isStrategyWhitelisted[_strategy], \"Strategy not whitelisted\");\n isStrategyWhitelisted[_strategy] = false;\n\n for (uint256 i = 0; i < strategies.length; i++) {\n if (strategies[i] == _strategy) {\n strategies[i] = strategies[strategies.length - 1];\n strategies.pop();\n break;\n }\n }\n\n emit StrategyRemoved(_strategy);\n }\n}\n" + }, + "contracts/automation/CollectXOGNRewardsModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IXOGN {\n function collectRewards() external;\n}\n\ncontract CollectXOGNRewardsModule is AbstractSafeModule {\n IXOGN public constant xogn =\n IXOGN(0x63898b3b6Ef3d39332082178656E9862bee45C57);\n address public constant rewardsSource =\n 0x7609c88E5880e934dd3A75bCFef44E31b1Badb8b;\n IERC20 public constant ogn =\n IERC20(0x8207c1FfC5B6804F6024322CcF34F29c3541Ae26);\n\n constructor(address _safeContract, address operator)\n AbstractSafeModule(_safeContract)\n {\n _grantRole(OPERATOR_ROLE, operator);\n }\n\n function collectRewards() external onlyOperator {\n uint256 balance = ogn.balanceOf(address(safeContract));\n\n bool success = safeContract.execTransactionFromModule(\n address(xogn),\n 0, // Value\n abi.encodeWithSelector(IXOGN.collectRewards.selector),\n 0 // Call\n );\n\n require(success, \"Failed to collect rewards\");\n\n balance = ogn.balanceOf(address(safeContract)) - balance;\n\n if (balance == 0) {\n return;\n }\n\n success = safeContract.execTransactionFromModule(\n address(ogn),\n 0, // Value\n abi.encodeWithSelector(\n IERC20.transfer.selector,\n rewardsSource,\n balance\n ),\n 0 // Call\n );\n\n require(success, \"Failed to collect rewards\");\n }\n}\n" + }, + "contracts/automation/CurvePoolBoosterBribesModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\n\ninterface ICurvePoolBooster {\n function manageTotalRewardAmount(\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external;\n\n function manageNumberOfPeriods(\n uint8 extraNumberOfPeriods,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external;\n}\n\ncontract CurvePoolBoosterBribesModule is AbstractSafeModule {\n address[] public POOLS;\n\n event PoolBoosterAddressAdded(address pool);\n event PoolBoosterAddressRemoved(address pool);\n\n constructor(\n address _safeContract,\n address _operator,\n address[] memory _pools\n ) AbstractSafeModule(_safeContract) {\n _grantRole(OPERATOR_ROLE, _operator);\n _addPoolBoosterAddress(_pools);\n }\n\n function addPoolBoosterAddress(address[] memory pools)\n external\n onlyOperator\n {\n _addPoolBoosterAddress(pools);\n }\n\n function _addPoolBoosterAddress(address[] memory pools) internal {\n for (uint256 i = 0; i < pools.length; i++) {\n POOLS.push(pools[i]);\n emit PoolBoosterAddressAdded(pools[i]);\n }\n }\n\n function removePoolBoosterAddress(address[] calldata pools)\n external\n onlyOperator\n {\n for (uint256 i = 0; i < pools.length; i++) {\n _removePoolBoosterAddress(pools[i]);\n }\n }\n\n function _removePoolBoosterAddress(address pool) internal {\n uint256 length = POOLS.length;\n for (uint256 i = 0; i < length; i++) {\n if (POOLS[i] == pool) {\n POOLS[i] = POOLS[length - 1];\n POOLS.pop();\n emit PoolBoosterAddressRemoved(pool);\n }\n }\n }\n\n function manageBribes() external onlyOperator {\n uint256 length = POOLS.length;\n for (uint256 i = 0; i < length; i++) {\n address poolBoosterAddress = POOLS[i];\n\n // PoolBooster need to have a balance of at least 0.002 ether to operate\n // 0.001 ether are used for the bridge fee\n require(\n poolBoosterAddress.balance > 0.002 ether,\n \"Insufficient balance for bribes\"\n );\n\n require(\n safeContract.execTransactionFromModule(\n poolBoosterAddress,\n 0, // Value\n abi.encodeWithSelector(\n ICurvePoolBooster.manageNumberOfPeriods.selector,\n 1, // extraNumberOfPeriods\n 0.001 ether, // bridgeFee\n 1000000 // additionalGasLimit\n ),\n 0\n ),\n \"Manage number of periods failed\"\n );\n\n require(\n safeContract.execTransactionFromModule(\n poolBoosterAddress,\n 0, // Value\n abi.encodeWithSelector(\n ICurvePoolBooster.manageTotalRewardAmount.selector,\n 0.001 ether, // bridgeFee\n 1000000 // additionalGasLimit\n ),\n 0\n ),\n \"Manage total reward failed\"\n );\n }\n }\n}\n" + }, + "contracts/automation/EthereumBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractLZBridgeHelperModule, AbstractSafeModule } from \"./AbstractLZBridgeHelperModule.sol\";\nimport { AbstractCCIPBridgeHelperModule, IRouterClient } from \"./AbstractCCIPBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IOFT } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract EthereumBridgeHelperModule is\n AccessControlEnumerable,\n AbstractLZBridgeHelperModule,\n AbstractCCIPBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0x39254033945AA2E4809Cc2977E7087BEE48bd7Ab);\n IWETH9 public constant weth =\n IWETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);\n IERC20 public constant oeth =\n IERC20(0x856c4Efb76C1D1AE02e20CEB03A2A6a08b0b8dC3);\n IERC4626 public constant woeth =\n IERC4626(0xDcEe70654261AF21C44c093C300eD3Bb97b78192);\n\n uint32 public constant LZ_PLUME_ENDPOINT_ID = 30370;\n IOFT public constant LZ_WOETH_OMNICHAIN_ADAPTER =\n IOFT(0x7d1bEa5807e6af125826d56ff477745BB89972b8);\n IOFT public constant LZ_ETH_OMNICHAIN_ADAPTER =\n IOFT(0x77b2043768d28E9C9aB44E1aBfC95944bcE57931);\n\n IRouterClient public constant CCIP_ROUTER =\n IRouterClient(0x80226fc0Ee2b096224EeAc085Bb9a8cba1146f7D);\n\n uint64 public constant CCIP_BASE_CHAIN_SELECTOR = 15971525489660198786;\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Plume.\n * @param woethAmount Amount of wOETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWOETHToPlume(uint256 woethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_PLUME_ENDPOINT_ID,\n woeth,\n LZ_WOETH_OMNICHAIN_ADAPTER,\n woethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Bridges wOETH to Base using CCIP.\n * @param woethAmount Amount of wOETH to bridge.\n */\n function bridgeWOETHToBase(uint256 woethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_BASE_CHAIN_SELECTOR,\n woeth,\n woethAmount\n );\n }\n\n /**\n * @dev Bridges wETH to Plume.\n * @param wethAmount Amount of wETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWETHToPlume(uint256 wethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n // Unwrap into ETH\n safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(weth.withdraw.selector, wethAmount),\n 0 // Call\n );\n\n _bridgeTokenWithLz(\n LZ_PLUME_ENDPOINT_ID,\n IERC20(address(weth)),\n LZ_ETH_OMNICHAIN_ADAPTER,\n wethAmount,\n slippageBps,\n true\n );\n }\n\n /**\n * @dev Bridges wETH to Base using CCIP.\n * @param wethAmount Amount of wETH to bridge.\n */\n function bridgeWETHToBase(uint256 wethAmount) public payable onlyOperator {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_BASE_CHAIN_SELECTOR,\n IERC20(address(weth)),\n wethAmount\n );\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH.\n * @param wethAmount Amount of WETH to mint.\n * @param useNativeToken Whether to use native token to mint.\n * @return Amount of wOETH minted.\n */\n function mintAndWrap(uint256 wethAmount, bool useNativeToken)\n external\n onlyOperator\n returns (uint256)\n {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n return _mintAndWrap(wethAmount);\n }\n\n function wrapETH(uint256 ethAmount) public payable onlyOperator {\n // Deposit ETH into WETH\n safeContract.execTransactionFromModule(\n address(weth),\n ethAmount, // Value\n abi.encodeWithSelector(weth.deposit.selector),\n 0 // Call\n );\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH.\n * @param wethAmount Amount of WETH to mint.\n * @return Amount of wOETH minted.\n */\n function _mintAndWrap(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETH\");\n\n // Approve wOETH to move OETH\n success = safeContract.execTransactionFromModule(\n address(oeth),\n 0, // Value\n abi.encodeWithSelector(\n oeth.approve.selector,\n address(woeth),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETH\");\n\n uint256 woethAmount = woeth.balanceOf(address(safeContract));\n\n // Wrap OETH into wOETH\n success = safeContract.execTransactionFromModule(\n address(woeth),\n 0, // Value\n abi.encodeWithSelector(\n woeth.deposit.selector,\n wethAmount,\n address(safeContract)\n ),\n 0 // Call\n );\n require(success, \"Failed to wrap OETH\");\n\n // Compute amount of wOETH minted\n return woeth.balanceOf(address(safeContract)) - woethAmount;\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH, then bridges it to Plume.\n * @param wethAmount Amount of WETH to mint.\n * @param slippageBps Bridge slippage in 10^4 basis points.\n * @param useNativeToken Whether to use native token to mint.\n */\n function mintWrapAndBridgeToPlume(\n uint256 wethAmount,\n uint256 slippageBps,\n bool useNativeToken\n ) external payable onlyOperator {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n uint256 woethAmount = _mintAndWrap(wethAmount);\n bridgeWOETHToPlume(woethAmount, slippageBps);\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH, then bridges it to Base using CCIP.\n * @param wethAmount Amount of WETH to mint.\n * @param useNativeToken Whether to use native token to mint.\n */\n function mintWrapAndBridgeToBase(uint256 wethAmount, bool useNativeToken)\n external\n payable\n onlyOperator\n {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n uint256 woethAmount = _mintAndWrap(wethAmount);\n bridgeWOETHToBase(woethAmount);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH.\n * @param woethAmount Amount of wOETH to unwrap.\n * @return Amount of WETH received.\n */\n function unwrapAndRedeem(uint256 woethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _unwrapAndRedeem(woethAmount);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH.\n * @param woethAmount Amount of wOETH to unwrap.\n * @return Amount of WETH received.\n */\n function _unwrapAndRedeem(uint256 woethAmount) internal returns (uint256) {\n uint256 oethAmount = oeth.balanceOf(address(safeContract));\n\n // Unwrap wOETH\n bool success = safeContract.execTransactionFromModule(\n address(woeth),\n 0, // Value\n abi.encodeWithSelector(\n woeth.redeem.selector,\n woethAmount,\n address(safeContract),\n address(safeContract)\n ),\n 0 // Call\n );\n require(success, \"Failed to unwrap wOETH\");\n\n oethAmount = oeth.balanceOf(address(safeContract)) - oethAmount;\n\n // Redeem OETH using Vault to get WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethAmount,\n oethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETH\");\n\n return oethAmount;\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH, then bridges it to Plume.\n * @param woethAmount Amount of wOETH to unwrap.\n * @param slippageBps Bridge slippage in 10^4 basis points.\n */\n function unwrapRedeemAndBridgeToPlume(\n uint256 woethAmount,\n uint256 slippageBps\n ) external payable onlyOperator {\n uint256 wethAmount = _unwrapAndRedeem(woethAmount);\n // Unwrap into ETH\n safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(weth.withdraw.selector, wethAmount),\n 0 // Call\n );\n\n bridgeWETHToPlume(wethAmount, slippageBps);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH, then bridges it to Base using CCIP.\n * @param woethAmount Amount of wOETH to unwrap.\n */\n function unwrapRedeemAndBridgeToBase(uint256 woethAmount)\n external\n payable\n onlyOperator\n {\n uint256 wethAmount = _unwrapAndRedeem(woethAmount);\n bridgeWETHToBase(wethAmount);\n }\n}\n" + }, + "contracts/automation/PlumeBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { AbstractLZBridgeHelperModule } from \"./AbstractLZBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IOFT } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { BridgedWOETHStrategy } from \"../strategies/BridgedWOETHStrategy.sol\";\n\ncontract PlumeBridgeHelperModule is\n AccessControlEnumerable,\n AbstractLZBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0xc8c8F8bEA5631A8AF26440AF32a55002138cB76a);\n IWETH9 public constant weth =\n IWETH9(0xca59cA09E5602fAe8B629DeE83FfA819741f14be);\n IERC20 public constant oethp =\n IERC20(0xFCbe50DbE43bF7E5C88C6F6Fb9ef432D4165406E);\n IERC4626 public constant bridgedWOETH =\n IERC4626(0xD8724322f44E5c58D7A815F542036fb17DbbF839);\n\n uint32 public constant LZ_ETHEREUM_ENDPOINT_ID = 30101;\n IOFT public constant LZ_WOETH_OMNICHAIN_ADAPTER =\n IOFT(0x592CB6A596E7919930bF49a27AdAeCA7C055e4DB);\n IOFT public constant LZ_ETH_OMNICHAIN_ADAPTER =\n IOFT(0x4683CE822272CD66CEa73F5F1f9f5cBcaEF4F066);\n\n BridgedWOETHStrategy public constant bridgedWOETHStrategy =\n BridgedWOETHStrategy(0x1E3EdD5e019207D6355Ea77F724b1F1BF639B569);\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Ethereum.\n * @param woethAmount Amount of wOETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWOETHToEthereum(uint256 woethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_ETHEREUM_ENDPOINT_ID,\n IERC20(address(bridgedWOETH)),\n LZ_WOETH_OMNICHAIN_ADAPTER,\n woethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Bridges wETH to Ethereum.\n * @param wethAmount Amount of wETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWETHToEthereum(uint256 wethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_ETHEREUM_ENDPOINT_ID,\n IERC20(address(weth)),\n LZ_ETH_OMNICHAIN_ADAPTER,\n wethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem with Vault.\n * @return Amount of OETHp received.\n */\n function depositWOETH(uint256 woethAmount, bool redeemWithVault)\n external\n onlyOperator\n returns (uint256)\n {\n return _depositWOETH(woethAmount, redeemWithVault);\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy and bridges it to Ethereum.\n * @param woethAmount Amount of wOETH to deposit.\n * @param slippageBps Slippage in 10^4 basis points.\n * @return Amount of WETH received.\n */\n function depositWOETHAndBridgeWETH(uint256 woethAmount, uint256 slippageBps)\n external\n payable\n onlyOperator\n returns (uint256)\n {\n uint256 wethAmount = _depositWOETH(woethAmount, true);\n bridgeWETHToEthereum(wethAmount, slippageBps);\n return wethAmount;\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem with Vault.\n * @return Amount of OETHp received.\n */\n function _depositWOETH(uint256 woethAmount, bool redeemWithVault)\n internal\n returns (uint256)\n {\n // Update oracle price\n bridgedWOETHStrategy.updateWOETHOraclePrice();\n\n // Rebase to account for any yields from price update\n vault.rebase();\n\n uint256 oethpAmount = oethp.balanceOf(address(safeContract));\n\n // Approve bridgedWOETH strategy to move wOETH\n bool success = safeContract.execTransactionFromModule(\n address(bridgedWOETH),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETH.approve.selector,\n address(bridgedWOETHStrategy),\n woethAmount\n ),\n 0 // Call\n );\n\n // Deposit to bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.depositBridgedWOETH.selector,\n woethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to deposit bridged WOETH\");\n\n oethpAmount = oethp.balanceOf(address(safeContract)) - oethpAmount;\n\n // Rebase to account for any yields from price update\n // and backing asset change from deposit\n vault.rebase();\n\n if (!redeemWithVault) {\n return oethpAmount;\n }\n\n // Redeem for WETH using Vault\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethpAmount,\n oethpAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETHp\");\n\n return oethpAmount;\n }\n\n /**\n * @dev Deposits wETH into the vault.\n * @param wethAmount Amount of wETH to deposit.\n * @return Amount of OETHp received.\n */\n function depositWETHAndRedeemWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _withdrawWOETH(wethAmount);\n }\n\n /**\n * @dev Deposits wETH into the vault and bridges it to Ethereum.\n * @param wethAmount Amount of wETH to deposit.\n * @param slippageBps Slippage in 10^4 basis points.\n * @return Amount of WOETH received.\n */\n function depositWETHAndBridgeWOETH(uint256 wethAmount, uint256 slippageBps)\n external\n payable\n onlyOperator\n returns (uint256)\n {\n uint256 woethAmount = _withdrawWOETH(wethAmount);\n bridgeWOETHToEthereum(woethAmount, slippageBps);\n return woethAmount;\n }\n\n /**\n * @dev Withdraws wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to use to withdraw.\n * @return Amount of wOETH received.\n */\n function _withdrawWOETH(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETHp with WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETHp\");\n\n // Approve bridgedWOETH strategy to move OETHp\n success = safeContract.execTransactionFromModule(\n address(oethp),\n 0, // Value\n abi.encodeWithSelector(\n oethp.approve.selector,\n address(bridgedWOETHStrategy),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETHp\");\n\n uint256 woethAmount = bridgedWOETH.balanceOf(address(safeContract));\n\n // Withdraw from bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.withdrawBridgedWOETH.selector,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to withdraw bridged WOETH\");\n\n woethAmount =\n bridgedWOETH.balanceOf(address(safeContract)) -\n woethAmount;\n\n return woethAmount;\n }\n}\n" + }, + "contracts/beacon/BeaconConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to request validator consolidation on the beacon chain.\n * @author Origin Protocol Inc\n */\nlibrary BeaconConsolidation {\n /// @notice The address the validator consolidation requests are sent\n /// See https://eips.ethereum.org/EIPS/eip-7251\n address internal constant CONSOLIDATION_REQUEST_ADDRESS =\n 0x0000BBdDc7CE488642fb579F8B00f3a590007251;\n\n function request(bytes calldata source, bytes calldata target)\n internal\n returns (uint256 fee_)\n {\n require(source.length == 48, \"Invalid source byte length\");\n require(target.length == 48, \"Invalid target byte length\");\n\n fee_ = fee();\n\n // Call the Consolidation Request contract with the public keys of the source and target\n // validators packed together.\n // This does not have a function signature, so we use a call\n (bool success, ) = CONSOLIDATION_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(source, target)\n );\n\n require(success, \"Consolidation request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the consolidation request contract\n (bool success, bytes memory result) = CONSOLIDATION_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/beacon/BeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"./BeaconProofsLib.sol\";\n\n/**\n * @title Verifies merkle proofs of beacon chain data.\n * @author Origin Protocol Inc\n */\ncontract BeaconProofs {\n /// @notice Verifies the validator public key to the beacon block root\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view {\n BeaconProofsLib.verifyValidatorPubkey(\n beaconBlockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n withdrawalAddress\n );\n }\n\n /// @notice Verifies the balances container to the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerRoot The merkle root of the the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata balancesContainerProof\n ) external view {\n BeaconProofsLib.verifyBalancesContainer(\n beaconBlockRoot,\n balancesContainerRoot,\n balancesContainerProof\n );\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param balanceProof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalanceGwei) {\n validatorBalanceGwei = BeaconProofsLib.verifyValidatorBalance(\n balancesContainerRoot,\n validatorBalanceLeaf,\n balanceProof,\n validatorIndex\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the slot of the first pending deposit to the beacon block root\n /// BeaconBlock.state.PendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero would be a good choice.\n /// @param firstPendingDepositSlotProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view returns (bool isEmptyDepositQueue) {\n isEmptyDepositQueue = BeaconProofsLib.verifyFirstPendingDepositSlot(\n beaconBlockRoot,\n slot,\n firstPendingDepositSlotProof\n );\n }\n}\n" + }, + "contracts/beacon/BeaconProofsLib.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Merkle } from \"./Merkle.sol\";\nimport { Endian } from \"./Endian.sol\";\n\n/**\n * @title Library to verify merkle proofs of beacon chain data.\n * @author Origin Protocol Inc\n */\nlibrary BeaconProofsLib {\n // Known generalized indices in the beacon block\n /// @dev BeaconBlock.slot\n uint256 internal constant SLOT_GENERALIZED_INDEX = 8;\n /// @dev BeaconBlock.state.PendingDeposits[0]\n uint256 internal constant FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX =\n 198105366528;\n /// @dev BeaconBlock.state.PendingDeposits[0].slot\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX =\n 1584842932228;\n /// @dev BeaconBlock.body.executionPayload.blockNumber\n uint256 internal constant BLOCK_NUMBER_GENERALIZED_INDEX = 6438;\n /// @dev BeaconBlock.state.validators\n uint256 internal constant VALIDATORS_CONTAINER_GENERALIZED_INDEX = 715;\n /// @dev BeaconBlock.state.balances\n uint256 internal constant BALANCES_CONTAINER_GENERALIZED_INDEX = 716;\n\n /// @dev Number of bytes in the proof to the first pending deposit.\n /// 37 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0]\n uint256 internal constant FIRST_PENDING_DEPOSIT_PROOF_LENGTH = 37 * 32;\n /// @dev Number of bytes in the proof to the slot of the first pending deposit.\n /// 40 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0].slot\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_PROOF_LENGTH = 40 * 32;\n\n /// @dev Merkle height of the Balances container\n /// BeaconBlock.state.balances\n uint256 internal constant BALANCES_HEIGHT = 39;\n /// @dev Merkle height of the Validators container\n /// BeaconBlock.state.validators\n uint256 internal constant VALIDATORS_HEIGHT = 41;\n /// @dev Merkle height of the Validator container\n /// BeaconBlock.state.validators[validatorIndex]\n uint256 internal constant VALIDATOR_HEIGHT = 3;\n\n /// @dev Position of the pubkey field in the Validator container.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;\n\n /// @notice Verifies the validator public key to the beacon block root\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n require(\n // 53 * 32 bytes = 1696 bytes\n validatorPubKeyProof.length == 1696,\n \"Invalid proof length\"\n );\n\n // BeaconBlock.state.validators[validatorIndex]\n uint256 generalizedIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_HEIGHT,\n validatorIndex\n );\n // BeaconBlock.state.validators[validatorIndex].pubkey\n generalizedIndex = concatGenIndices(\n generalizedIndex,\n VALIDATOR_HEIGHT,\n VALIDATOR_PUBKEY_INDEX\n );\n\n // Get the withdrawal address from the first witness in the pubkey merkle proof.\n address withdrawalAddressFromProof;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // The first 32 bytes of the proof is the withdrawal credential so load it into memory.\n calldatacopy(0, validatorPubKeyProof.offset, 32)\n // Cast the 32 bytes in memory to an address which is the last 20 bytes.\n withdrawalAddressFromProof := mload(0)\n }\n require(\n withdrawalAddressFromProof == withdrawalAddress,\n \"Invalid withdrawal address\"\n );\n\n require(\n Merkle.verifyInclusionSha256({\n proof: validatorPubKeyProof,\n root: beaconBlockRoot,\n leaf: pubKeyHash,\n index: generalizedIndex\n }),\n \"Invalid validator pubkey proof\"\n );\n }\n\n /// @notice Verifies the balances container to the beacon block root.\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param balancesContainerRoot The merkle root of the the balances container.\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata balancesContainerProof\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n require(\n // 9 * 32 bytes = 288 bytes\n balancesContainerProof.length == 288,\n \"Invalid proof length\"\n );\n\n // BeaconBlock.state.balances\n require(\n Merkle.verifyInclusionSha256({\n proof: balancesContainerProof,\n root: beaconBlockRoot,\n leaf: balancesContainerRoot,\n index: BALANCES_CONTAINER_GENERALIZED_INDEX\n }),\n \"Invalid balance container proof\"\n );\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param balanceProof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for.\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index.\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) internal view returns (uint256 validatorBalanceGwei) {\n require(balancesContainerRoot != bytes32(0), \"Invalid container root\");\n require(\n // 39 * 32 bytes = 1248 bytes\n balanceProof.length == 1248,\n \"Invalid proof length\"\n );\n\n // Four balances are stored in each leaf so the validator index is divided by 4\n uint64 balanceIndex = validatorIndex / 4;\n\n // Get the index within the balances container, not the Beacon Block\n // BeaconBlock.state.balances[balanceIndex]\n uint256 generalizedIndex = concatGenIndices(\n 1,\n BALANCES_HEIGHT,\n balanceIndex\n );\n\n validatorBalanceGwei = balanceAtIndex(\n validatorBalanceLeaf,\n validatorIndex\n );\n\n require(\n Merkle.verifyInclusionSha256({\n proof: balanceProof,\n root: balancesContainerRoot,\n leaf: validatorBalanceLeaf,\n index: generalizedIndex\n }),\n \"Invalid balance proof\"\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the slot of the first pending deposit to the beacon block root\n /// BeaconBlock.state.PendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero would be a good choice.\n /// @param firstPendingDepositSlotProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) internal view returns (bool isEmptyDepositQueue) {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n require(\n // 40 * 32 bytes = 1280 bytes\n firstPendingDepositSlotProof.length == 1280 ||\n // 37 * 32 bytes = 1184 bytes\n firstPendingDepositSlotProof.length == 1184,\n \"Invalid proof length\"\n );\n\n // slither-disable-next-line uninitialized-local\n uint256 generalizedIndex;\n // slither-disable-next-line uninitialized-local\n bytes32 leaf;\n // If the deposit queue is empty\n if (\n firstPendingDepositSlotProof.length ==\n FIRST_PENDING_DEPOSIT_PROOF_LENGTH\n ) {\n isEmptyDepositQueue = true;\n // use an empty leaf node as the root of the first pending deposit\n // when the deposit queue is empty\n leaf = bytes32(0);\n // BeaconBlock.state.PendingDeposits[0]\n generalizedIndex = FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX;\n } else if (\n firstPendingDepositSlotProof.length ==\n FIRST_PENDING_DEPOSIT_SLOT_PROOF_LENGTH\n ) {\n // Convert uint64 slot number to a little endian bytes32\n leaf = Endian.toLittleEndianUint64(slot);\n // BeaconBlock.state.PendingDeposits[0].slot\n generalizedIndex = FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX;\n } else {\n revert(\"Invalid proof length\");\n }\n\n require(\n Merkle.verifyInclusionSha256({\n proof: firstPendingDepositSlotProof,\n root: beaconBlockRoot,\n leaf: leaf,\n index: generalizedIndex\n }),\n \"Invalid pending deposit proof\"\n );\n }\n\n ////////////////////////////////////////////////////\n /// Internal Helper Functions\n ////////////////////////////////////////////////////\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n internal\n pure\n returns (uint256)\n {\n uint256 bitShiftAmount = (validatorIndex % 4) * 64;\n return\n Endian.fromLittleEndianUint64(\n bytes32((uint256(validatorBalanceLeaf) << bitShiftAmount))\n );\n }\n\n /// @notice Concatenates two beacon chain generalized indices into one.\n /// @param genIndex The first generalized index or 1 if calculating for a single container.\n /// @param height The merkle tree height of the second container. eg 39 for balances, 41 for validators.\n /// @param index The index within the second container. eg the validator index.\n /// @return genIndex The concatenated generalized index.\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) internal pure returns (uint256) {\n return (genIndex << height) | index;\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to retrieve beacon block roots.\n * @author Origin Protocol Inc\n */\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the beacon block root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Roots contract to get the parent block root.\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/Endian.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n/**\n * @title Library to handle conversion between little-endian and big-endian formats.\n * @author Origin Protocol Inc\n */\nlibrary Endian {\n /**\n * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64\n * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type\n * @return n The big endian-formatted uint64\n * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits),\n * but it is immediately truncated to a uint64 (i.e. 64 bits)\n * through a right-shift/shr operation.\n */\n function fromLittleEndianUint64(bytes32 lenum)\n internal\n pure\n returns (uint64 n)\n {\n // the number needs to be stored in little-endian encoding (ie in bytes 0-8)\n n = uint64(uint256(lenum >> 192));\n // forgefmt: disable-next-item\n return\n (n >> 56) |\n ((0x00FF000000000000 & n) >> 40) |\n ((0x0000FF0000000000 & n) >> 24) |\n ((0x000000FF00000000 & n) >> 8) |\n ((0x00000000FF000000 & n) << 8) |\n ((0x0000000000FF0000 & n) << 24) |\n ((0x000000000000FF00 & n) << 40) |\n ((0x00000000000000FF & n) << 56);\n }\n\n function toLittleEndianUint64(uint64 benum)\n internal\n pure\n returns (bytes32 n)\n {\n // Convert to little-endian by reversing byte order\n uint64 reversed = (benum >> 56) |\n ((0x00FF000000000000 & benum) >> 40) |\n ((0x0000FF0000000000 & benum) >> 24) |\n ((0x000000FF00000000 & benum) >> 8) |\n ((0x00000000FF000000 & benum) << 8) |\n ((0x0000000000FF0000 & benum) << 24) |\n ((0x000000000000FF00 & benum) << 40) |\n ((0x00000000000000FF & benum) << 56);\n\n // Store the little-endian uint64 in the least significant 64 bits of bytes32\n n = bytes32(uint256(reversed));\n // Shift to most significant bits\n n = n << 192;\n }\n}\n" + }, + "contracts/beacon/Merkle.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev These functions deal with verification of Merkle Tree proofs.\n *\n * The tree and the proofs can be generated using our\n * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].\n * You will find a quickstart guide in the readme.\n *\n * WARNING: You should avoid using leaf values that are 64 bytes long prior to\n * hashing, or use a hash function other than keccak256 for hashing leaves.\n * This is because the concatenation of a sorted pair of internal nodes in\n * the merkle tree could be reinterpreted as a leaf value.\n * OpenZeppelin's JavaScript library generates merkle trees that are safe\n * against this attack out of the box.\n */\nlibrary Merkle {\n error InvalidProofLength();\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function verifyInclusionSha256(\n bytes memory proof,\n bytes32 root,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bool) {\n return processInclusionProofSha256(proof, leaf, index) == root;\n }\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * _Available since v4.4._\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function processInclusionProofSha256(\n bytes memory proof,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bytes32) {\n require(\n proof.length != 0 && proof.length % 32 == 0,\n InvalidProofLength()\n );\n bytes32[1] memory computedHash = [leaf];\n for (uint256 i = 32; i <= proof.length; i += 32) {\n if (index % 2 == 0) {\n // if ith bit of index is 0, then computedHash is a left sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(computedHash))\n mstore(0x20, mload(add(proof, i)))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n index := div(index, 2)\n }\n } else {\n // if ith bit of index is 1, then computedHash is a right sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(add(proof, i)))\n mstore(0x20, mload(computedHash))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n index := div(index, 2)\n }\n }\n }\n return computedHash[0];\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to request full or partial withdrawals from validators on the beacon chain.\n * @author Origin Protocol Inc\n */\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/bridges/OmnichainL2Adapter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.0;\n\nimport { MintBurnOFTAdapter } from \"@layerzerolabs/oft-evm/contracts/MintBurnOFTAdapter.sol\";\nimport { IMintableBurnable } from \"@layerzerolabs/oft-evm/contracts/interfaces/IMintableBurnable.sol\";\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\n\n/// NOTE: It's necessary to inherit from Ownable instead of Governable\n/// because OFTCore uses Ownable to manage the governor.\n/// Ownable uses slot 0 for storing the address, whereas Goveranble\n/// stores it in a computed slot.\n\n/// @notice Omnichain uses a deployed ERC-20 token and safeERC20\n/// to interact with the OFTCore contract.\n/// On L2, we follow the mint and burn mechanism.\n/// The adapter should have minter and burner roles.\n\n/// @title Omnichain L2 Adapter\ncontract OmnichainL2Adapter is MintBurnOFTAdapter {\n constructor(\n address _token,\n address _lzEndpoint,\n address _governor\n )\n MintBurnOFTAdapter(\n _token,\n IMintableBurnable(_token),\n _lzEndpoint,\n _governor\n )\n Ownable()\n {\n _transferOwnership(_governor);\n }\n\n /// @inheritdoc MintBurnOFTAdapter\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n )\n internal\n virtual\n override\n returns (uint256 amountSentLD, uint256 amountReceivedLD)\n {\n (amountSentLD, amountReceivedLD) = _debitView(\n _amountLD,\n _minAmountLD,\n _dstEid\n );\n // Burns tokens from the caller.\n IMintableERC20(address(minterBurner)).burn(_from, amountSentLD);\n }\n\n /// @inheritdoc MintBurnOFTAdapter\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /* _srcEid */\n ) internal virtual override returns (uint256 amountReceivedLD) {\n if (_to == address(0x0)) _to = address(0xdead); // _mint(...) does not support address(0x0)\n // Mints the tokens and transfers to the recipient.\n IMintableERC20(address(minterBurner)).mint(_to, _amountLD);\n // In the case of NON-default OFTAdapter, the amountLD MIGHT not be equal to amountReceivedLD.\n return _amountLD;\n }\n}\n\ninterface IMintableERC20 {\n function mint(address to, uint256 value) external;\n\n function burn(address to, uint256 value) external;\n}\n" + }, + "contracts/bridges/OmnichainMainnetAdapter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.0;\n\nimport { OFTAdapter } from \"@layerzerolabs/oft-evm/contracts/OFTAdapter.sol\";\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\n\n/// NOTE: It's necessary to inherit from Ownable instead of Governable\n/// because OFTCore uses Ownable to manage the governor.\n/// Ownable uses slot 0 for storing the address, whereas Goveranble\n/// stores it in a computed slot.\n\n/// @notice Omnichain uses a deployed ERC-20 token and safeERC20\n/// to interact with the OFTCore contract.\n/// On Ethereum, we follow the lock and unlock mechanism.\n\n/// @title Omnichain Mainnet Adapter\ncontract OmnichainMainnetAdapter is OFTAdapter {\n constructor(\n address _token,\n address _lzEndpoint,\n address _governor\n ) OFTAdapter(_token, _lzEndpoint, _governor) Ownable() {\n _transferOwnership(_governor);\n }\n}\n" + }, + "contracts/buyback/AbstractBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { ICVXLocker } from \"../interfaces/ICVXLocker.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\n\nabstract contract AbstractBuyback is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n event SwapRouterUpdated(address indexed _address);\n\n event RewardsSourceUpdated(address indexed _address);\n event TreasuryManagerUpdated(address indexed _address);\n event CVXShareBpsUpdated(uint256 bps);\n\n // Emitted whenever OUSD/OETH is swapped for OGN/CVX or any other token\n event OTokenBuyback(\n address indexed oToken,\n address indexed swappedFor,\n uint256 swapAmountIn,\n uint256 amountOut\n );\n\n // Address of 1-inch Swap Router\n address public swapRouter;\n\n // slither-disable-next-line constable-states\n address private __deprecated_ousd;\n // slither-disable-next-line constable-states\n address private __deprecated_ogv;\n // slither-disable-next-line constable-states\n address private __deprecated_usdt;\n // slither-disable-next-line constable-states\n address private __deprecated_weth9;\n\n // Address that receives OGN after swaps\n address public rewardsSource;\n\n // Address that receives all other tokens after swaps\n address public treasuryManager;\n\n // slither-disable-next-line constable-states\n uint256 private __deprecated_treasuryBps;\n\n address public immutable oToken;\n address public immutable ogn;\n address public immutable cvx;\n address public immutable cvxLocker;\n\n // Amount of `oToken` balance to use for OGN buyback\n uint256 public balanceForOGN;\n\n // Amount of `oToken` balance to use for CVX buyback\n uint256 public balanceForCVX;\n\n // Percentage of `oToken` balance to be used for CVX\n uint256 public cvxShareBps; // 10000 = 100%\n\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) {\n // Make sure nobody owns the implementation contract\n _setGovernor(address(0));\n\n oToken = _oToken;\n ogn = _ogn;\n cvx = _cvx;\n cvxLocker = _cvxLocker;\n }\n\n /**\n * @param _swapRouter Address of Uniswap V3 Router\n * @param _strategistAddr Address of Strategist multi-sig wallet\n * @param _treasuryManagerAddr Address that receives the treasury's share of OUSD\n * @param _rewardsSource Address of RewardsSource contract\n * @param _cvxShareBps Percentage of balance to use for CVX\n */\n function initialize(\n address _swapRouter,\n address _strategistAddr,\n address _treasuryManagerAddr,\n address _rewardsSource,\n uint256 _cvxShareBps\n ) external onlyGovernor initializer {\n _setStrategistAddr(_strategistAddr);\n\n _setSwapRouter(_swapRouter);\n _setRewardsSource(_rewardsSource);\n\n _setTreasuryManager(_treasuryManagerAddr);\n\n _setCVXShareBps(_cvxShareBps);\n }\n\n /**\n * @dev Set address of Uniswap Universal Router for performing liquidation\n * of platform fee tokens. Setting to 0x0 will pause swaps.\n *\n * @param _router Address of the Uniswap Universal router\n */\n function setSwapRouter(address _router) external onlyGovernor {\n _setSwapRouter(_router);\n }\n\n function _setSwapRouter(address _router) internal {\n address oldRouter = swapRouter;\n swapRouter = _router;\n\n if (oldRouter != address(0)) {\n // Remove allowance of old router, if any\n\n if (IERC20(ogn).allowance(address(this), oldRouter) != 0) {\n // slither-disable-next-line unused-return\n IERC20(ogn).safeApprove(oldRouter, 0);\n }\n\n if (IERC20(cvx).allowance(address(this), oldRouter) != 0) {\n // slither-disable-next-line unused-return\n IERC20(cvx).safeApprove(oldRouter, 0);\n }\n }\n\n emit SwapRouterUpdated(_router);\n }\n\n /**\n * @dev Sets the address that receives the OGN buyback rewards\n * @param _address Address\n */\n function setRewardsSource(address _address) external onlyGovernor {\n _setRewardsSource(_address);\n }\n\n function _setRewardsSource(address _address) internal {\n require(_address != address(0), \"Address not set\");\n rewardsSource = _address;\n emit RewardsSourceUpdated(_address);\n }\n\n /**\n * @dev Sets the address that can receive and manage the funds for Treasury\n * @param _address Address\n */\n function setTreasuryManager(address _address) external onlyGovernor {\n _setTreasuryManager(_address);\n }\n\n function _setTreasuryManager(address _address) internal {\n require(_address != address(0), \"Address not set\");\n treasuryManager = _address;\n emit TreasuryManagerUpdated(_address);\n }\n\n /**\n * @dev Sets the percentage of oToken to use for Flywheel tokens\n * @param _bps BPS, 10000 to 100%\n */\n function setCVXShareBps(uint256 _bps) external onlyGovernor {\n _setCVXShareBps(_bps);\n }\n\n function _setCVXShareBps(uint256 _bps) internal {\n require(_bps <= 10000, \"Invalid bps value\");\n cvxShareBps = _bps;\n emit CVXShareBpsUpdated(_bps);\n }\n\n /**\n * @dev Computes the split of oToken balance that can be\n * used for OGN and CVX buybacks.\n */\n function _updateBuybackSplits()\n internal\n returns (uint256 _balanceForOGN, uint256 _balanceForCVX)\n {\n _balanceForOGN = balanceForOGN;\n _balanceForCVX = balanceForCVX;\n\n uint256 totalBalance = IERC20(oToken).balanceOf(address(this));\n uint256 unsplitBalance = totalBalance - _balanceForOGN - _balanceForCVX;\n\n // Check if all balance is accounted for\n if (unsplitBalance != 0) {\n // If not, split unaccounted balance based on `cvxShareBps`\n uint256 addToCVX = (unsplitBalance * cvxShareBps) / 10000;\n _balanceForCVX = _balanceForCVX + addToCVX;\n _balanceForOGN = _balanceForOGN + unsplitBalance - addToCVX;\n\n // Update storage\n balanceForOGN = _balanceForOGN;\n balanceForCVX = _balanceForCVX;\n }\n }\n\n function updateBuybackSplits() external onlyGovernor {\n // slither-disable-next-line unused-return\n _updateBuybackSplits();\n }\n\n function _swapToken(\n address tokenOut,\n uint256 oTokenAmount,\n uint256 minAmountOut,\n bytes calldata swapData\n ) internal returns (uint256 amountOut) {\n require(oTokenAmount > 0, \"Invalid Swap Amount\");\n require(swapRouter != address(0), \"Swap Router not set\");\n require(minAmountOut > 0, \"Invalid minAmount\");\n\n // Transfer OToken to Swapper for swapping\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(oToken).transfer(swapRouter, oTokenAmount);\n\n // Swap\n amountOut = ISwapper(swapRouter).swap(\n oToken,\n tokenOut,\n oTokenAmount,\n minAmountOut,\n swapData\n );\n\n require(amountOut >= minAmountOut, \"Higher Slippage\");\n\n emit OTokenBuyback(oToken, tokenOut, oTokenAmount, amountOut);\n }\n\n /**\n * @dev Swaps `oTokenAmount` to OGN\n * @param oTokenAmount Amount of OUSD/OETH to swap\n * @param minOGN Minimum OGN to receive for oTokenAmount\n * @param swapData 1inch Swap Data\n */\n function swapForOGN(\n uint256 oTokenAmount,\n uint256 minOGN,\n bytes calldata swapData\n ) external onlyGovernorOrStrategist nonReentrant {\n (uint256 _amountForOGN, ) = _updateBuybackSplits();\n require(_amountForOGN >= oTokenAmount, \"Balance underflow\");\n require(rewardsSource != address(0), \"RewardsSource contract not set\");\n\n unchecked {\n // Subtract the amount to swap from net balance\n balanceForOGN = _amountForOGN - oTokenAmount;\n }\n\n uint256 ognReceived = _swapToken(ogn, oTokenAmount, minOGN, swapData);\n\n // Transfer OGN received to RewardsSource contract\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(ogn).transfer(rewardsSource, ognReceived);\n }\n\n /**\n * @dev Swaps `oTokenAmount` to CVX\n * @param oTokenAmount Amount of OUSD/OETH to swap\n * @param minCVX Minimum CVX to receive for oTokenAmount\n * @param swapData 1inch Swap Data\n */\n function swapForCVX(\n uint256 oTokenAmount,\n uint256 minCVX,\n bytes calldata swapData\n ) external onlyGovernorOrStrategist nonReentrant {\n (, uint256 _amountForCVX) = _updateBuybackSplits();\n require(_amountForCVX >= oTokenAmount, \"Balance underflow\");\n\n unchecked {\n // Subtract the amount to swap from net balance\n balanceForCVX = _amountForCVX - oTokenAmount;\n }\n\n uint256 cvxReceived = _swapToken(cvx, oTokenAmount, minCVX, swapData);\n\n // Lock all CVX\n _lockAllCVX(cvxReceived);\n }\n\n /**\n * @dev Locks all CVX held by the contract on behalf of the Treasury Manager\n */\n function lockAllCVX() external onlyGovernorOrStrategist {\n _lockAllCVX(IERC20(cvx).balanceOf(address(this)));\n }\n\n function _lockAllCVX(uint256 cvxAmount) internal {\n require(\n treasuryManager != address(0),\n \"Treasury manager address not set\"\n );\n\n // Lock all available CVX on behalf of `treasuryManager`\n ICVXLocker(cvxLocker).lock(treasuryManager, cvxAmount, 0);\n }\n\n /**\n * @dev Approve CVX Locker to move CVX held by this contract\n */\n function safeApproveAllTokens() external onlyGovernorOrStrategist {\n IERC20(cvx).safeApprove(cvxLocker, type(uint256).max);\n }\n\n /**\n * @notice Owner function to withdraw a specific amount of a token\n * @param token token to be transferered\n * @param amount amount of the token to be transferred\n */\n function transferToken(address token, uint256 amount)\n external\n onlyGovernor\n nonReentrant\n {\n IERC20(token).safeTransfer(_governor(), amount);\n }\n}\n" + }, + "contracts/buyback/ARMBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract ARMBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/buyback/OETHBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract OETHBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/buyback/OUSDBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract OUSDBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/echidna/Debugger.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary Debugger {\n event Debug(string debugString);\n event Debug(string description, string data);\n event Debug(string prefix, string description, string data);\n event Debug(string description, bytes32 data);\n event Debug(string prefix, string description, bytes32 data);\n event Debug(string description, uint256 data);\n event Debug(string prefix, string description, uint256 data);\n event Debug(string description, int256 data);\n event Debug(string prefix, string description, int256 data);\n event Debug(string description, address data);\n event Debug(string prefix, string description, address data);\n event Debug(string description, bool data);\n event Debug(string prefix, string description, bool data);\n\n function log(string memory debugString) internal {\n emit Debug(debugString);\n }\n\n function log(string memory description, string memory data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n string memory data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, bytes32 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n bytes32 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, uint256 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n uint256 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, int256 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n int256 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, address data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n address data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, bool data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n bool data\n ) internal {\n emit Debug(prefix, description, data);\n }\n}\n" + }, + "contracts/echidna/Echidna.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaTestApproval.sol\";\n\n/**\n * @title Echidna test contract for OUSD\n * @notice Target contract to be tested, containing all mixins\n * @author Rappie\n */\ncontract Echidna is EchidnaTestApproval {\n\n}\n" + }, + "contracts/echidna/EchidnaConfig.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Top-level mixin for configuring the desired fuzzing setup\n * @author Rappie\n */\ncontract EchidnaConfig {\n address internal constant ADDRESS_VAULT = address(0x10000);\n address internal constant ADDRESS_OUTSIDER_USER = address(0x20000);\n\n address internal constant ADDRESS_USER0 = address(0x30000);\n address internal constant ADDRESS_USER1 = address(0x40000);\n\n // Will be set in EchidnaSetup constructor\n address internal ADDRESS_OUTSIDER_CONTRACT;\n address internal ADDRESS_CONTRACT0;\n address internal ADDRESS_CONTRACT1;\n\n // Toggle known issues\n //\n // This can be used to skip tests that are known to fail. This is useful\n // when debugging a specific issue, but should be disabled when running\n // the full test suite.\n //\n // True => skip tests that are known to fail\n // False => run all tests\n //\n bool internal constant TOGGLE_KNOWN_ISSUES = false;\n\n // Toggle known issues within limits\n //\n // Same as TOGGLE_KNOWN_ISSUES, but also skip tests that are known to fail\n // within limits set by the variables below.\n //\n bool internal constant TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS = true;\n\n // Starting balance\n //\n // Gives OUSD a non-zero starting supply, which can be useful to ignore\n // certain edge cases.\n //\n // The starting balance is given to outsider accounts that are not used as\n // accounts while fuzzing.\n //\n bool internal constant TOGGLE_STARTING_BALANCE = true;\n uint256 internal constant STARTING_BALANCE = 1_000_000e18;\n\n // Change supply\n //\n // Set a limit to the amount of change per rebase, which can be useful to\n // ignore certain edge cases.\n //\n // True => limit the amount of change to a percentage of total supply\n // False => no limit\n //\n bool internal constant TOGGLE_CHANGESUPPLY_LIMIT = true;\n uint256 internal constant CHANGESUPPLY_DIVISOR = 10; // 10% of total supply\n\n // Mint limit\n //\n // Set a limit the amount minted per mint, which can be useful to\n // ignore certain edge cases.\n //\n // True => limit the amount of minted tokens\n // False => no limit\n //\n bool internal constant TOGGLE_MINT_LIMIT = true;\n uint256 internal constant MINT_MODULO = 1_000_000_000_000e18;\n\n // Known rounding errors\n uint256 internal constant TRANSFER_ROUNDING_ERROR = 1e18 - 1;\n uint256 internal constant OPT_IN_ROUNDING_ERROR = 1e18 - 1;\n uint256 internal constant MINT_ROUNDING_ERROR = 1e18 - 1;\n\n /**\n * @notice Modifier to skip tests that are known to fail\n * @dev see TOGGLE_KNOWN_ISSUES for more information\n */\n modifier hasKnownIssue() {\n if (TOGGLE_KNOWN_ISSUES) return;\n _;\n }\n\n /**\n * @notice Modifier to skip tests that are known to fail within limits\n * @dev see TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS for more information\n */\n modifier hasKnownIssueWithinLimits() {\n if (TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS) return;\n _;\n }\n\n /**\n * @notice Translate an account ID to an address\n * @param accountId The ID of the account\n * @return account The address of the account\n */\n function getAccount(uint8 accountId)\n internal\n view\n returns (address account)\n {\n accountId = accountId / 64;\n if (accountId == 0) return account = ADDRESS_USER0;\n if (accountId == 1) return account = ADDRESS_USER1;\n if (accountId == 2) return account = ADDRESS_CONTRACT0;\n if (accountId == 3) return account = ADDRESS_CONTRACT1;\n require(false, \"Unknown account ID\");\n }\n}\n" + }, + "contracts/echidna/EchidnaDebug.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport \"./EchidnaHelper.sol\";\nimport \"./Debugger.sol\";\n\nimport \"../token/OUSD.sol\";\n\n/**\n * @title Room for random debugging functions\n * @author Rappie\n */\ncontract EchidnaDebug is EchidnaHelper {\n function debugOUSD() public pure {\n // assert(ousd.balanceOf(ADDRESS_USER0) == 1000);\n // assert(ousd.rebaseState(ADDRESS_USER0) != OUSD.RebaseOptions.OptIn);\n // assert(Address.isContract(ADDRESS_CONTRACT0));\n // Debugger.log(\"nonRebasingSupply\", ousd.nonRebasingSupply());\n // assert(false);\n }\n}\n" + }, + "contracts/echidna/EchidnaHelper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaSetup.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin containing helper functions\n * @author Rappie\n */\ncontract EchidnaHelper is EchidnaSetup {\n /**\n * @notice Mint tokens to an account\n * @param toAcc Account to mint to\n * @param amount Amount to mint\n * @return Amount minted (in case of capped mint with modulo)\n */\n function mint(uint8 toAcc, uint256 amount) public returns (uint256) {\n address to = getAccount(toAcc);\n\n if (TOGGLE_MINT_LIMIT) {\n amount = amount % MINT_MODULO;\n }\n\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(to, amount);\n\n return amount;\n }\n\n /**\n * @notice Burn tokens from an account\n * @param fromAcc Account to burn from\n * @param amount Amount to burn\n */\n function burn(uint8 fromAcc, uint256 amount) public {\n address from = getAccount(fromAcc);\n hevm.prank(ADDRESS_VAULT);\n ousd.burn(from, amount);\n }\n\n /**\n * @notice Change the total supply of OUSD (rebase)\n * @param amount New total supply\n */\n function changeSupply(uint256 amount) public {\n if (TOGGLE_CHANGESUPPLY_LIMIT) {\n amount =\n ousd.totalSupply() +\n (amount % (ousd.totalSupply() / CHANGESUPPLY_DIVISOR));\n }\n\n hevm.prank(ADDRESS_VAULT);\n ousd.changeSupply(amount);\n }\n\n /**\n * @notice Transfer tokens between accounts\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function transfer(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n ousd.transfer(to, amount);\n }\n\n /**\n * @notice Transfer approved tokens between accounts\n * @param authorizedAcc Account that is authorized to transfer\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function transferFrom(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n ousd.transferFrom(from, to, amount);\n }\n\n /**\n * @notice Opt in to rebasing\n * @param targetAcc Account to opt in\n */\n function optIn(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n hevm.prank(target);\n ousd.rebaseOptIn();\n }\n\n /**\n * @notice Opt out of rebasing\n * @param targetAcc Account to opt out\n */\n function optOut(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n hevm.prank(target);\n ousd.rebaseOptOut();\n }\n\n /**\n * @notice Approve an account to spend OUSD\n * @param ownerAcc Account that owns the OUSD\n * @param spenderAcc Account that is approved to spend the OUSD\n * @param amount Amount to approve\n */\n function approve(\n uint8 ownerAcc,\n uint8 spenderAcc,\n uint256 amount\n ) public {\n address owner = getAccount(ownerAcc);\n address spender = getAccount(spenderAcc);\n hevm.prank(owner);\n // slither-disable-next-line unused-return\n ousd.approve(spender, amount);\n }\n\n /**\n * @notice Get the sum of all OUSD balances\n * @return total Total balance\n */\n function getTotalBalance() public view returns (uint256 total) {\n total += ousd.balanceOf(ADDRESS_VAULT);\n total += ousd.balanceOf(ADDRESS_OUTSIDER_USER);\n total += ousd.balanceOf(ADDRESS_OUTSIDER_CONTRACT);\n total += ousd.balanceOf(ADDRESS_USER0);\n total += ousd.balanceOf(ADDRESS_USER1);\n total += ousd.balanceOf(ADDRESS_CONTRACT0);\n total += ousd.balanceOf(ADDRESS_CONTRACT1);\n }\n\n /**\n * @notice Get the sum of all non-rebasing OUSD balances\n * @return total Total balance\n */\n function getTotalNonRebasingBalance() public returns (uint256 total) {\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_VAULT)\n ? ousd.balanceOf(ADDRESS_VAULT)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_OUTSIDER_USER)\n ? ousd.balanceOf(ADDRESS_OUTSIDER_USER)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_OUTSIDER_CONTRACT)\n ? ousd.balanceOf(ADDRESS_OUTSIDER_CONTRACT)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_USER0)\n ? ousd.balanceOf(ADDRESS_USER0)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_USER1)\n ? ousd.balanceOf(ADDRESS_USER1)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_CONTRACT0)\n ? ousd.balanceOf(ADDRESS_CONTRACT0)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_CONTRACT1)\n ? ousd.balanceOf(ADDRESS_CONTRACT1)\n : 0;\n }\n}\n" + }, + "contracts/echidna/EchidnaSetup.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./IHevm.sol\";\nimport \"./EchidnaConfig.sol\";\nimport \"./OUSDEchidna.sol\";\n\ncontract Dummy {}\n\n/**\n * @title Mixin for setup and deployment\n * @author Rappie\n */\ncontract EchidnaSetup is EchidnaConfig {\n IHevm hevm = IHevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);\n OUSDEchidna ousd = new OUSDEchidna();\n\n /**\n * @notice Deploy the OUSD contract and set up initial state\n */\n constructor() {\n ousd.initialize(ADDRESS_VAULT, 1e18);\n\n // Deploy dummny contracts as users\n Dummy outsider = new Dummy();\n ADDRESS_OUTSIDER_CONTRACT = address(outsider);\n Dummy dummy0 = new Dummy();\n ADDRESS_CONTRACT0 = address(dummy0);\n Dummy dummy1 = new Dummy();\n ADDRESS_CONTRACT1 = address(dummy1);\n\n // Start out with a reasonable amount of OUSD\n if (TOGGLE_STARTING_BALANCE) {\n // Rebasing tokens\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(ADDRESS_OUTSIDER_USER, STARTING_BALANCE / 2);\n\n // Non-rebasing tokens\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(ADDRESS_OUTSIDER_CONTRACT, STARTING_BALANCE / 2);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestAccounting.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestSupply.sol\";\n\n/**\n * @title Mixin for testing accounting functions\n * @author Rappie\n */\ncontract EchidnaTestAccounting is EchidnaTestSupply {\n /**\n * @notice After opting in, balance should not increase. (Ok to lose rounding funds doing this)\n * @param targetAcc Account to opt in\n */\n function testOptInBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optIn(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter <= balanceBefore);\n }\n\n /**\n * @notice After opting out, balance should remain the same\n * @param targetAcc Account to opt out\n */\n function testOptOutBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optOut(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Account balance should remain the same after opting in minus rounding error\n * @param targetAcc Account to opt in\n */\n function testOptInBalanceRounding(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optIn(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n int256 delta = int256(balanceAfter) - int256(balanceBefore);\n Debugger.log(\"delta\", delta);\n\n // slither-disable-next-line tautology\n assert(-1 * delta >= 0);\n assert(-1 * delta <= int256(OPT_IN_ROUNDING_ERROR));\n }\n\n /**\n * @notice After opting in, total supply should remain the same\n * @param targetAcc Account to opt in\n */\n function testOptInTotalSupply(uint8 targetAcc) public {\n uint256 totalSupplyBefore = ousd.totalSupply();\n optIn(targetAcc);\n uint256 totalSupplyAfter = ousd.totalSupply();\n\n assert(totalSupplyAfter == totalSupplyBefore);\n }\n\n /**\n * @notice After opting out, total supply should remain the same\n * @param targetAcc Account to opt out\n */\n function testOptOutTotalSupply(uint8 targetAcc) public {\n uint256 totalSupplyBefore = ousd.totalSupply();\n optOut(targetAcc);\n uint256 totalSupplyAfter = ousd.totalSupply();\n\n assert(totalSupplyAfter == totalSupplyBefore);\n }\n\n /**\n * @notice Account balance should remain the same when a smart contract auto converts\n * @param targetAcc Account to auto convert\n */\n function testAutoConvertBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n // slither-disable-next-line unused-return\n ousd._isNonRebasingAccountEchidna(target);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice The `balanceOf` function should never revert\n * @param targetAcc Account to check balance of\n */\n function testBalanceOfShouldNotRevert(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n // slither-disable-next-line unused-return\n try ousd.balanceOf(target) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestApproval.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaTestMintBurn.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin for testing approval related functions\n * @author Rappie\n */\ncontract EchidnaTestApproval is EchidnaTestMintBurn {\n /**\n * @notice Performing `transferFrom` with an amount inside the allowance should not revert\n * @param authorizedAcc The account that is authorized to transfer\n * @param fromAcc The account that is transferring\n * @param toAcc The account that is receiving\n * @param amount The amount to transfer\n */\n function testTransferFromShouldNotRevert(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount <= ousd.balanceOf(from));\n require(amount <= ousd.allowance(from, authorized));\n\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n try ousd.transferFrom(from, to, amount) {\n // pass\n } catch {\n assert(false);\n }\n }\n\n /**\n * @notice Performing `transferFrom` with an amount outside the allowance should revert\n * @param authorizedAcc The account that is authorized to transfer\n * @param fromAcc The account that is transferring\n * @param toAcc The account that is receiving\n * @param amount The amount to transfer\n */\n function testTransferFromShouldRevert(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount > 0);\n require(\n !(amount <= ousd.balanceOf(from) &&\n amount <= ousd.allowance(from, authorized))\n );\n\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n try ousd.transferFrom(from, to, amount) {\n assert(false);\n } catch {\n // pass\n }\n }\n\n /**\n * @notice Approving an amount should update the allowance and overwrite any previous allowance\n * @param ownerAcc The account that is approving\n * @param spenderAcc The account that is being approved\n * @param amount The amount to approve\n */\n function testApprove(\n uint8 ownerAcc,\n uint8 spenderAcc,\n uint256 amount\n ) public {\n address owner = getAccount(ownerAcc);\n address spender = getAccount(spenderAcc);\n\n approve(ownerAcc, spenderAcc, amount);\n uint256 allowanceAfter1 = ousd.allowance(owner, spender);\n\n assert(allowanceAfter1 == amount);\n\n approve(ownerAcc, spenderAcc, amount / 2);\n uint256 allowanceAfter2 = ousd.allowance(owner, spender);\n\n assert(allowanceAfter2 == amount / 2);\n }\n}\n" + }, + "contracts/echidna/EchidnaTestMintBurn.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestAccounting.sol\";\n\n/**\n * @title Mixin for testing Mint and Burn functions\n * @author Rappie\n */\ncontract EchidnaTestMintBurn is EchidnaTestAccounting {\n /**\n * @notice Minting 0 tokens should not affect account balance\n * @param targetAcc Account to mint to\n */\n function testMintZeroBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n mint(targetAcc, 0);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Burning 0 tokens should not affect account balance\n * @param targetAcc Account to burn from\n */\n function testBurnZeroBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n burn(targetAcc, 0);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Minting tokens must increase the account balance by at least amount\n * @param targetAcc Account to mint to\n * @param amount Amount to mint\n * @custom:error testMintBalance(uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * testMintBalance(0,1)\n * Event sequence:\n * Debug(«balanceBefore», 0)\n * Debug(«balanceAfter», 0)\n */\n function testMintBalance(uint8 targetAcc, uint256 amount)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n uint256 amountMinted = mint(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n Debugger.log(\"amountMinted\", amountMinted);\n Debugger.log(\"balanceBefore\", balanceBefore);\n Debugger.log(\"balanceAfter\", balanceAfter);\n\n assert(balanceAfter >= balanceBefore + amountMinted);\n }\n\n /**\n * @notice Burning tokens must decrease the account balance by at least amount\n * @param targetAcc Account to burn from\n * @param amount Amount to burn\n * @custom:error testBurnBalance(uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * mint(0,3)\n * testBurnBalance(0,1)\n * Event sequence:\n * Debug(«balanceBefore», 2)\n * Debug(«balanceAfter», 2)\n */\n function testBurnBalance(uint8 targetAcc, uint256 amount)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n burn(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n Debugger.log(\"balanceBefore\", balanceBefore);\n Debugger.log(\"balanceAfter\", balanceAfter);\n\n assert(balanceAfter <= balanceBefore - amount);\n }\n\n /**\n * @notice Minting tokens should not increase the account balance by less than rounding error above amount\n * @param targetAcc Account to mint to\n * @param amount Amount to mint\n */\n function testMintBalanceRounding(uint8 targetAcc, uint256 amount) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n uint256 amountMinted = mint(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n int256 delta = int256(balanceAfter) - int256(balanceBefore);\n\n // delta == amount, if no error\n // delta < amount, if too little is minted\n // delta > amount, if too much is minted\n int256 error = int256(amountMinted) - delta;\n\n assert(error >= 0);\n assert(error <= int256(MINT_ROUNDING_ERROR));\n }\n\n /**\n * @notice A burn of an account balance must result in a zero balance\n * @param targetAcc Account to burn from\n */\n function testBurnAllBalanceToZero(uint8 targetAcc) public hasKnownIssue {\n address target = getAccount(targetAcc);\n\n burn(targetAcc, ousd.balanceOf(target));\n assert(ousd.balanceOf(target) == 0);\n }\n\n /**\n * @notice You should always be able to burn an account's balance\n * @param targetAcc Account to burn from\n */\n function testBurnAllBalanceShouldNotRevert(uint8 targetAcc)\n public\n hasKnownIssue\n {\n address target = getAccount(targetAcc);\n uint256 balance = ousd.balanceOf(target);\n\n hevm.prank(ADDRESS_VAULT);\n try ousd.burn(target, balance) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestSupply.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestTransfer.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n/**\n * @title Mixin for testing supply related functions\n * @author Rappie\n */\ncontract EchidnaTestSupply is EchidnaTestTransfer {\n using StableMath for uint256;\n\n uint256 prevRebasingCreditsPerToken = type(uint256).max;\n\n /**\n * @notice After a `changeSupply`, the total supply should exactly\n * match the target total supply. (This is needed to ensure successive\n * rebases are correct).\n * @param supply New total supply\n * @custom:error testChangeSupply(uint256): failed!💥\n * Call sequence:\n * testChangeSupply(1044505275072865171609)\n * Event sequence:\n * TotalSupplyUpdatedHighres(1044505275072865171610, 1000000000000000000000000, 957391048054055578595)\n */\n function testChangeSupply(uint256 supply)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n hevm.prank(ADDRESS_VAULT);\n ousd.changeSupply(supply);\n\n assert(ousd.totalSupply() == supply);\n }\n\n /**\n * @notice The total supply must not be less than the sum of account balances.\n * (The difference will go into future rebases)\n * @custom:error testTotalSupplyLessThanTotalBalance(): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(1)\n * optOut(64)\n * transfer(0,64,1)\n * testTotalSupplyLessThanTotalBalance()\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000001000001)\n * Debug(«totalBalance», 1000000000000000001000002)\n */\n function testTotalSupplyLessThanTotalBalance()\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n uint256 totalSupply = ousd.totalSupply();\n uint256 totalBalance = getTotalBalance();\n\n Debugger.log(\"totalSupply\", totalSupply);\n Debugger.log(\"totalBalance\", totalBalance);\n\n assert(totalSupply >= totalBalance);\n }\n\n /**\n * @notice Non-rebasing supply should not be larger than total supply\n * @custom:error testNonRebasingSupplyVsTotalSupply(): failed!💥\n * Call sequence:\n * mint(0,2)\n * changeSupply(3)\n * burn(0,1)\n * optOut(0)\n * testNonRebasingSupplyVsTotalSupply()\n */\n function testNonRebasingSupplyVsTotalSupply() public hasKnownIssue {\n uint256 nonRebasingSupply = ousd.nonRebasingSupply();\n uint256 totalSupply = ousd.totalSupply();\n\n assert(nonRebasingSupply <= totalSupply);\n }\n\n /**\n * @notice Global `rebasingCreditsPerToken` should never increase\n * @custom:error testRebasingCreditsPerTokenNotIncreased(): failed!💥\n * Call sequence:\n * testRebasingCreditsPerTokenNotIncreased()\n * changeSupply(1)\n * testRebasingCreditsPerTokenNotIncreased()\n */\n function testRebasingCreditsPerTokenNotIncreased() public hasKnownIssue {\n uint256 curRebasingCreditsPerToken = ousd\n .rebasingCreditsPerTokenHighres();\n\n Debugger.log(\n \"prevRebasingCreditsPerToken\",\n prevRebasingCreditsPerToken\n );\n Debugger.log(\"curRebasingCreditsPerToken\", curRebasingCreditsPerToken);\n\n assert(curRebasingCreditsPerToken <= prevRebasingCreditsPerToken);\n\n prevRebasingCreditsPerToken = curRebasingCreditsPerToken;\n }\n\n /**\n * @notice The rebasing credits per token ratio must greater than zero\n */\n function testRebasingCreditsPerTokenAboveZero() public {\n assert(ousd.rebasingCreditsPerTokenHighres() > 0);\n }\n\n /**\n * @notice The sum of all non-rebasing balances should not be larger than\n * non-rebasing supply\n * @custom:error testTotalNonRebasingSupplyLessThanTotalBalance(): failed!💥\n * Call sequence\n * mint(0,2)\n * changeSupply(1)\n * optOut(0)\n * burn(0,1)\n * testTotalNonRebasingSupplyLessThanTotalBalance()\n * Event sequence:\n * Debug(«totalNonRebasingSupply», 500000000000000000000001)\n * Debug(«totalNonRebasingBalance», 500000000000000000000002)\n */\n function testTotalNonRebasingSupplyLessThanTotalBalance()\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n uint256 totalNonRebasingSupply = ousd.nonRebasingSupply();\n uint256 totalNonRebasingBalance = getTotalNonRebasingBalance();\n\n Debugger.log(\"totalNonRebasingSupply\", totalNonRebasingSupply);\n Debugger.log(\"totalNonRebasingBalance\", totalNonRebasingBalance);\n\n assert(totalNonRebasingSupply >= totalNonRebasingBalance);\n }\n\n /**\n * @notice An accounts credits / credits per token should not be larger it's balance\n * @param targetAcc The account to check\n */\n function testCreditsPerTokenVsBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n (uint256 credits, uint256 creditsPerToken, ) = ousd\n .creditsBalanceOfHighres(target);\n uint256 expectedBalance = credits.divPrecisely(creditsPerToken);\n\n uint256 balance = ousd.balanceOf(target);\n\n Debugger.log(\"credits\", credits);\n Debugger.log(\"creditsPerToken\", creditsPerToken);\n Debugger.log(\"expectedBalance\", expectedBalance);\n Debugger.log(\"balance\", balance);\n\n assert(expectedBalance == balance);\n }\n}\n" + }, + "contracts/echidna/EchidnaTestTransfer.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin for testing transfer related functions\n * @author Rappie\n */\ncontract EchidnaTestTransfer is EchidnaDebug {\n /**\n * @notice The receiving account's balance after a transfer must not increase by\n * less than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferBalanceReceivedLess(uint8,uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * mint(64,2)\n * testTransferBalanceReceivedLess(64,0,1)\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000000500002)\n * Debug(«toBalBefore», 0)\n * Debug(«toBalAfter», 0)\n */\n function testTransferBalanceReceivedLess(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue hasKnownIssueWithinLimits {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n\n assert(toBalAfter >= toBalBefore + amount);\n }\n\n /**\n * @notice The receiving account's balance after a transfer must not\n * increase by more than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceReceivedMore(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n\n assert(toBalAfter <= toBalBefore + amount);\n }\n\n /**\n * @notice The sending account's balance after a transfer must not\n * decrease by less than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferBalanceSentLess(uint8,uint8,uint256): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(1)\n * testTransferBalanceSentLess(0,64,1)\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000000500001)\n * Debug(«fromBalBefore», 1)\n * Debug(«fromBalAfter», 1)\n */\n function testTransferBalanceSentLess(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue hasKnownIssueWithinLimits {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n\n assert(fromBalAfter <= fromBalBefore - amount);\n }\n\n /**\n * @notice The sending account's balance after a transfer must not\n * decrease by more than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceSentMore(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n\n assert(fromBalAfter >= fromBalBefore - amount);\n }\n\n /**\n * @notice The receiving account's balance after a transfer must not\n * increase by less than the amount transferred (minus rounding error)\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceReceivedLessRounding(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n int256 toDelta = int256(toBalAfter) - int256(toBalBefore);\n\n // delta == amount, if no error\n // delta < amount, if too little is sent\n // delta > amount, if too much is sent\n int256 error = int256(amount) - toDelta;\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n Debugger.log(\"toDelta\", toDelta);\n Debugger.log(\"error\", error);\n\n assert(error >= 0);\n assert(error <= int256(TRANSFER_ROUNDING_ERROR));\n }\n\n /**\n * @notice The sending account's balance after a transfer must\n * not decrease by less than the amount transferred (minus rounding error)\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceSentLessRounding(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n int256 fromDelta = int256(fromBalAfter) - int256(fromBalBefore);\n\n // delta == -amount, if no error\n // delta < -amount, if too much is sent\n // delta > -amount, if too little is sent\n int256 error = int256(amount) + fromDelta;\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n Debugger.log(\"fromDelta\", fromDelta);\n Debugger.log(\"error\", error);\n\n assert(error >= 0);\n assert(error <= int256(TRANSFER_ROUNDING_ERROR));\n }\n\n /**\n * @notice An account should always be able to successfully transfer\n * an amount within its balance.\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferWithinBalanceDoesNotRevert(uint8,uint8,uint8): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(3)\n * optOut(0)\n * testTransferWithinBalanceDoesNotRevert(0,128,2)\n * optIn(0)\n * testTransferWithinBalanceDoesNotRevert(128,0,1)\n * Event sequence:\n * error Revert Panic(17): SafeMath over-/under-flows\n */\n function testTransferWithinBalanceDoesNotRevert(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount > 0);\n amount = amount % ousd.balanceOf(from);\n\n Debugger.log(\"Total supply\", ousd.totalSupply());\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(to, amount) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n\n /**\n * @notice An account should never be able to successfully transfer\n * an amount greater than their balance.\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferExceedingBalanceReverts(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n amount = ousd.balanceOf(from) + 1 + amount;\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(to, amount) {\n assert(false);\n } catch {\n assert(true);\n }\n }\n\n /**\n * @notice A transfer to the same account should not change that account's balance\n * @param targetAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferSelf(uint8 targetAcc, uint256 amount) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n transfer(targetAcc, targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceBefore == balanceAfter);\n }\n\n /**\n * @notice Transfers to the zero account revert\n * @param fromAcc Account to transfer from\n * @param amount Amount to transfer\n */\n function testTransferToZeroAddress(uint8 fromAcc, uint256 amount) public {\n address from = getAccount(fromAcc);\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(address(0), amount) {\n assert(false);\n } catch {\n assert(true);\n }\n }\n}\n" + }, + "contracts/echidna/IHevm.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// https://github.com/ethereum/hevm/blob/main/doc/src/controlling-the-unit-testing-environment.md#cheat-codes\n\ninterface IHevm {\n function warp(uint256 x) external;\n\n function roll(uint256 x) external;\n\n function store(\n address c,\n bytes32 loc,\n bytes32 val\n ) external;\n\n function load(address c, bytes32 loc) external returns (bytes32 val);\n\n function sign(uint256 sk, bytes32 digest)\n external\n returns (\n uint8 v,\n bytes32 r,\n bytes32 s\n );\n\n function addr(uint256 sk) external returns (address addr);\n\n function ffi(string[] calldata) external returns (bytes memory);\n\n function prank(address sender) external;\n}\n" + }, + "contracts/echidna/OUSDEchidna.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../token/OUSD.sol\";\n\ncontract OUSDEchidna is OUSD {\n constructor() OUSD() {}\n\n function _isNonRebasingAccountEchidna(address _account)\n public\n returns (bool)\n {\n _autoMigrate(_account);\n return alternativeCreditsPerToken[_account] > 0;\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/governance/InitializableGovernable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD InitializableGovernable Contract\n * @author Origin Protocol Inc\n */\nimport { Initializable } from \"../utils/Initializable.sol\";\n\nimport { Governable } from \"./Governable.sol\";\n\ncontract InitializableGovernable is Governable, Initializable {\n function _initialize(address _newGovernor) internal {\n _changeGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/governance/Strategizable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"./Governable.sol\";\n\ncontract Strategizable is Governable {\n event StrategistUpdated(address _address);\n\n // Address of strategist\n address public strategistAddr;\n\n // For future use\n uint256[50] private __gap;\n\n /**\n * @dev Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @dev Set address of Strategist\n * @param _address Address of Strategist\n */\n function setStrategistAddr(address _address) external onlyGovernor {\n _setStrategistAddr(_address);\n }\n\n /**\n * @dev Set address of Strategist\n * @param _address Address of Strategist\n */\n function _setStrategistAddr(address _address) internal {\n strategistAddr = _address;\n emit StrategistUpdated(_address);\n }\n}\n" + }, + "contracts/harvest/AbstractHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IUniswapV2Router } from \"../interfaces/uniswap/IUniswapV2Router02.sol\";\nimport { IUniswapV3Router } from \"../interfaces/uniswap/IUniswapV3Router.sol\";\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { ICurvePool } from \"../strategies/ICurvePool.sol\";\nimport \"../utils/Helpers.sol\";\n\nabstract contract AbstractHarvester is Governable {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using StableMath for uint256;\n\n enum SwapPlatform {\n UniswapV2Compatible,\n UniswapV3,\n Balancer,\n Curve\n }\n\n event SupportedStrategyUpdate(address strategyAddress, bool isSupported);\n event RewardTokenConfigUpdated(\n address tokenAddress,\n uint16 allowedSlippageBps,\n uint16 harvestRewardBps,\n SwapPlatform swapPlatform,\n address swapPlatformAddr,\n bytes swapData,\n uint256 liquidationLimit,\n bool doSwapRewardToken\n );\n event RewardTokenSwapped(\n address indexed rewardToken,\n address indexed swappedInto,\n SwapPlatform swapPlatform,\n uint256 amountIn,\n uint256 amountOut\n );\n event RewardProceedsTransferred(\n address indexed token,\n address farmer,\n uint256 protcolYield,\n uint256 farmerFee\n );\n event RewardProceedsAddressChanged(address newProceedsAddress);\n\n error EmptyAddress();\n error InvalidSlippageBps();\n error InvalidHarvestRewardBps();\n\n error InvalidSwapPlatform(SwapPlatform swapPlatform);\n\n error InvalidUniswapV2PathLength();\n error InvalidTokenInSwapPath(address token);\n error EmptyBalancerPoolId();\n error InvalidCurvePoolAssetIndex(address token);\n\n error UnsupportedStrategy(address strategyAddress);\n\n error SlippageError(uint256 actualBalance, uint256 minExpected);\n error BalanceMismatchAfterSwap(uint256 actualBalance, uint256 minExpected);\n\n // Configuration properties for harvesting logic of reward tokens\n struct RewardTokenConfig {\n // Max allowed slippage when swapping reward token for a stablecoin denominated in basis points.\n uint16 allowedSlippageBps;\n // Reward when calling a harvest function denominated in basis points.\n uint16 harvestRewardBps;\n // Address of compatible exchange protocol (Uniswap V2/V3, SushiSwap, Balancer and Curve).\n address swapPlatformAddr;\n /* When true the reward token is being swapped. In a need of (temporarily) disabling the swapping of\n * a reward token this needs to be set to false.\n */\n bool doSwapRewardToken;\n // Platform to use for Swapping\n SwapPlatform swapPlatform;\n /* How much token can be sold per one harvest call. If the balance of rewards tokens\n * exceeds that limit multiple harvest calls are required to harvest all of the tokens.\n * Set it to MAX_INT to effectively disable the limit.\n */\n uint256 liquidationLimit;\n }\n\n mapping(address => RewardTokenConfig) public rewardTokenConfigs;\n mapping(address => bool) public supportedStrategies;\n\n address public immutable vaultAddress;\n\n /**\n * Address receiving rewards proceeds. Initially the Vault contract later will possibly\n * be replaced by another contract that eases out rewards distribution.\n **/\n address public rewardProceedsAddress;\n\n /**\n * All tokens are swapped to this token before it gets transferred\n * to the `rewardProceedsAddress`. USDT for OUSD and WETH for OETH.\n **/\n address public immutable baseTokenAddress;\n // Cached decimals for `baseTokenAddress`\n uint256 public immutable baseTokenDecimals;\n\n // Uniswap V2 path for reward tokens using Uniswap V2 Router\n mapping(address => address[]) public uniswapV2Path;\n // Uniswap V3 path for reward tokens using Uniswap V3 Router\n mapping(address => bytes) public uniswapV3Path;\n // Pool ID to use for reward tokens on Balancer\n mapping(address => bytes32) public balancerPoolId;\n\n struct CurvePoolIndices {\n // Casted into uint128 and stored in a struct to save gas\n uint128 rewardTokenIndex;\n uint128 baseTokenIndex;\n }\n // Packed indices of assets on the Curve pool\n mapping(address => CurvePoolIndices) public curvePoolIndices;\n\n constructor(address _vaultAddress, address _baseTokenAddress) {\n require(_vaultAddress != address(0));\n require(_baseTokenAddress != address(0));\n\n vaultAddress = _vaultAddress;\n baseTokenAddress = _baseTokenAddress;\n\n // Cache decimals as well\n baseTokenDecimals = Helpers.getDecimals(_baseTokenAddress);\n }\n\n /***************************************\n Configuration\n ****************************************/\n\n /**\n * Set the Address receiving rewards proceeds.\n * @param _rewardProceedsAddress Address of the reward token\n */\n function setRewardProceedsAddress(address _rewardProceedsAddress)\n external\n onlyGovernor\n {\n if (_rewardProceedsAddress == address(0)) {\n revert EmptyAddress();\n }\n\n rewardProceedsAddress = _rewardProceedsAddress;\n emit RewardProceedsAddressChanged(_rewardProceedsAddress);\n }\n\n /**\n * @dev Add/update a reward token configuration that holds harvesting config variables\n * @param _tokenAddress Address of the reward token\n * @param tokenConfig.allowedSlippageBps uint16 maximum allowed slippage denominated in basis points.\n * Example: 300 == 3% slippage\n * @param tokenConfig.harvestRewardBps uint16 amount of reward tokens the caller of the function is rewarded.\n * Example: 100 == 1%\n * @param tokenConfig.swapPlatformAddr Address Address of a UniswapV2 compatible contract to perform\n * the exchange from reward tokens to stablecoin (currently hard-coded to USDT)\n * @param tokenConfig.liquidationLimit uint256 Maximum amount of token to be sold per one swap function call.\n * When value is 0 there is no limit.\n * @param tokenConfig.doSwapRewardToken bool Disables swapping of the token when set to true,\n * does not cause it to revert though.\n * @param tokenConfig.swapPlatform SwapPlatform to use for Swapping\n * @param swapData Additional data required for swapping\n */\n function setRewardTokenConfig(\n address _tokenAddress,\n RewardTokenConfig calldata tokenConfig,\n bytes calldata swapData\n ) external onlyGovernor {\n if (tokenConfig.allowedSlippageBps > 1000) {\n revert InvalidSlippageBps();\n }\n\n if (tokenConfig.harvestRewardBps > 1000) {\n revert InvalidHarvestRewardBps();\n }\n\n address newRouterAddress = tokenConfig.swapPlatformAddr;\n if (newRouterAddress == address(0)) {\n // Swap router address should be non zero address\n revert EmptyAddress();\n }\n\n address oldRouterAddress = rewardTokenConfigs[_tokenAddress]\n .swapPlatformAddr;\n rewardTokenConfigs[_tokenAddress] = tokenConfig;\n\n // Revert if feed does not exist\n // slither-disable-next-line unused-return\n IOracle(IVault(vaultAddress).priceProvider()).price(_tokenAddress);\n\n IERC20 token = IERC20(_tokenAddress);\n // if changing token swap provider cancel existing allowance\n if (\n /* oldRouterAddress == address(0) when there is no pre-existing\n * configuration for said rewards token\n */\n oldRouterAddress != address(0) &&\n oldRouterAddress != newRouterAddress\n ) {\n token.safeApprove(oldRouterAddress, 0);\n }\n\n // Give SwapRouter infinite approval when needed\n if (oldRouterAddress != newRouterAddress) {\n token.safeApprove(newRouterAddress, 0);\n token.safeApprove(newRouterAddress, type(uint256).max);\n }\n\n SwapPlatform _platform = tokenConfig.swapPlatform;\n if (_platform == SwapPlatform.UniswapV2Compatible) {\n uniswapV2Path[_tokenAddress] = _decodeUniswapV2Path(\n swapData,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.UniswapV3) {\n uniswapV3Path[_tokenAddress] = _decodeUniswapV3Path(\n swapData,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.Balancer) {\n balancerPoolId[_tokenAddress] = _decodeBalancerPoolId(\n swapData,\n newRouterAddress,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.Curve) {\n curvePoolIndices[_tokenAddress] = _decodeCurvePoolIndices(\n swapData,\n newRouterAddress,\n _tokenAddress\n );\n } else {\n // Note: This code is unreachable since Solidity reverts when\n // the value is outside the range of defined values of the enum\n // (even if it's under the max length of the base type)\n revert InvalidSwapPlatform(_platform);\n }\n\n emit RewardTokenConfigUpdated(\n _tokenAddress,\n tokenConfig.allowedSlippageBps,\n tokenConfig.harvestRewardBps,\n _platform,\n newRouterAddress,\n swapData,\n tokenConfig.liquidationLimit,\n tokenConfig.doSwapRewardToken\n );\n }\n\n /**\n * @dev Decodes the data passed into Uniswap V2 path and validates\n * it to make sure the path is for `token` to `baseToken`\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param token The address of the reward token\n * @return path The validated Uniswap V2 path\n */\n function _decodeUniswapV2Path(bytes calldata data, address token)\n internal\n view\n returns (address[] memory path)\n {\n (path) = abi.decode(data, (address[]));\n uint256 len = path.length;\n\n if (len < 2) {\n // Path should have at least two tokens\n revert InvalidUniswapV2PathLength();\n }\n\n // Do some validation\n if (path[0] != token) {\n revert InvalidTokenInSwapPath(path[0]);\n }\n\n if (path[len - 1] != baseTokenAddress) {\n revert InvalidTokenInSwapPath(path[len - 1]);\n }\n }\n\n /**\n * @dev Decodes the data passed into Uniswap V3 path and validates\n * it to make sure the path is for `token` to `baseToken`\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param token The address of the reward token\n * @return path The validated Uniswap V3 path\n */\n function _decodeUniswapV3Path(bytes calldata data, address token)\n internal\n view\n returns (bytes calldata path)\n {\n path = data;\n\n address decodedAddress = address(uint160(bytes20(data[0:20])));\n\n if (decodedAddress != token) {\n // Invalid Reward Token in swap path\n revert InvalidTokenInSwapPath(decodedAddress);\n }\n\n decodedAddress = address(uint160(bytes20(data[path.length - 20:])));\n if (decodedAddress != baseTokenAddress) {\n // Invalid Base Token in swap path\n revert InvalidTokenInSwapPath(decodedAddress);\n }\n }\n\n /**\n * @dev Decodes the data passed to Balancer Pool ID\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @return poolId The pool ID\n */\n function _decodeBalancerPoolId(\n bytes calldata data,\n address balancerVault,\n address token\n ) internal view returns (bytes32 poolId) {\n (poolId) = abi.decode(data, (bytes32));\n\n if (poolId == bytes32(0)) {\n revert EmptyBalancerPoolId();\n }\n\n IBalancerVault bVault = IBalancerVault(balancerVault);\n\n // Note: this reverts if token is not a pool asset\n // slither-disable-next-line unused-return\n bVault.getPoolTokenInfo(poolId, token);\n\n // slither-disable-next-line unused-return\n bVault.getPoolTokenInfo(poolId, baseTokenAddress);\n }\n\n /**\n * @dev Decodes the data passed to get the pool indices and\n * checks it against the Curve Pool to make sure it's\n * not misconfigured. The indices are packed into a single\n * uint256 for gas savings\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param poolAddress Curve pool address\n * @param token The address of the reward token\n * @return indices Packed pool asset indices\n */\n function _decodeCurvePoolIndices(\n bytes calldata data,\n address poolAddress,\n address token\n ) internal view returns (CurvePoolIndices memory indices) {\n indices = abi.decode(data, (CurvePoolIndices));\n\n ICurvePool pool = ICurvePool(poolAddress);\n if (token != pool.coins(indices.rewardTokenIndex)) {\n revert InvalidCurvePoolAssetIndex(token);\n }\n if (baseTokenAddress != pool.coins(indices.baseTokenIndex)) {\n revert InvalidCurvePoolAssetIndex(baseTokenAddress);\n }\n }\n\n /**\n * @dev Flags a strategy as supported or not supported one\n * @param _strategyAddress Address of the strategy\n * @param _isSupported Bool marking strategy as supported or not supported\n */\n function setSupportedStrategy(address _strategyAddress, bool _isSupported)\n external\n onlyGovernor\n {\n supportedStrategies[_strategyAddress] = _isSupported;\n emit SupportedStrategyUpdate(_strategyAddress, _isSupported);\n }\n\n /***************************************\n Rewards\n ****************************************/\n\n /**\n * @dev Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform. Can be called by anyone.\n * Rewards incentivizing the caller are sent to the caller of this function.\n * @param _strategyAddr Address of the strategy to collect rewards from\n */\n function harvestAndSwap(address _strategyAddr) external nonReentrant {\n // Remember _harvest function checks for the validity of _strategyAddr\n _harvestAndSwap(_strategyAddr, msg.sender);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform. Can be called by anyone\n * @param _strategyAddr Address of the strategy to collect rewards from\n * @param _rewardTo Address where to send a share of harvest rewards to as an incentive\n * for executing this function\n */\n function harvestAndSwap(address _strategyAddr, address _rewardTo)\n external\n nonReentrant\n {\n // Remember _harvest function checks for the validity of _strategyAddr\n _harvestAndSwap(_strategyAddr, _rewardTo);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform\n * @param _strategyAddr Address of the strategy to collect rewards from\n * @param _rewardTo Address where to send a share of harvest rewards to as an incentive\n * for executing this function\n */\n function _harvestAndSwap(address _strategyAddr, address _rewardTo)\n internal\n {\n _harvest(_strategyAddr);\n IStrategy strategy = IStrategy(_strategyAddr);\n address[] memory rewardTokens = strategy.getRewardTokenAddresses();\n IOracle priceProvider = IOracle(IVault(vaultAddress).priceProvider());\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; ++i) {\n _swap(rewardTokens[i], _rewardTo, priceProvider);\n }\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform\n * @param _strategyAddr Address of the strategy to collect rewards from.\n */\n function _harvest(address _strategyAddr) internal virtual {\n if (!supportedStrategies[_strategyAddr]) {\n revert UnsupportedStrategy(_strategyAddr);\n }\n\n IStrategy strategy = IStrategy(_strategyAddr);\n strategy.collectRewardTokens();\n }\n\n /**\n * @dev Swap a reward token for the base token on the configured\n * swap platform. The token must have a registered price feed\n * with the price provider\n * @param _swapToken Address of the token to swap\n * @param _rewardTo Address where to send the share of harvest rewards to\n * @param _priceProvider Oracle to get prices of the swap token\n */\n function _swap(\n address _swapToken,\n address _rewardTo,\n IOracle _priceProvider\n ) internal virtual {\n uint256 balance = IERC20(_swapToken).balanceOf(address(this));\n\n // No need to swap if the reward token is the base token. eg USDT or WETH.\n // There is also no limit on the transfer. Everything in the harvester will be transferred\n // to the Dripper regardless of the liquidationLimit config.\n if (_swapToken == baseTokenAddress) {\n IERC20(_swapToken).safeTransfer(rewardProceedsAddress, balance);\n // currently not paying the farmer any rewards as there is no swap\n emit RewardProceedsTransferred(\n baseTokenAddress,\n address(0),\n balance,\n 0\n );\n return;\n }\n\n RewardTokenConfig memory tokenConfig = rewardTokenConfigs[_swapToken];\n\n /* This will trigger a return when reward token configuration has not yet been set\n * or we have temporarily disabled swapping of specific reward token via setting\n * doSwapRewardToken to false.\n */\n if (!tokenConfig.doSwapRewardToken) {\n return;\n }\n\n if (balance == 0) {\n return;\n }\n\n if (tokenConfig.liquidationLimit > 0) {\n balance = Math.min(balance, tokenConfig.liquidationLimit);\n }\n\n // This'll revert if there is no price feed\n uint256 oraclePrice = _priceProvider.price(_swapToken);\n\n // Oracle price is 1e18\n uint256 minExpected = (balance *\n (1e4 - tokenConfig.allowedSlippageBps) * // max allowed slippage\n oraclePrice).scaleBy(\n baseTokenDecimals,\n Helpers.getDecimals(_swapToken)\n ) /\n 1e4 / // fix the max slippage decimal position\n 1e18; // and oracle price decimals position\n\n // Do the swap\n uint256 amountReceived = _doSwap(\n tokenConfig.swapPlatform,\n tokenConfig.swapPlatformAddr,\n _swapToken,\n balance,\n minExpected\n );\n\n if (amountReceived < minExpected) {\n revert SlippageError(amountReceived, minExpected);\n }\n\n emit RewardTokenSwapped(\n _swapToken,\n baseTokenAddress,\n tokenConfig.swapPlatform,\n balance,\n amountReceived\n );\n\n IERC20 baseToken = IERC20(baseTokenAddress);\n uint256 baseTokenBalance = baseToken.balanceOf(address(this));\n if (baseTokenBalance < amountReceived) {\n // Note: It's possible to bypass this check by transferring `baseToken`\n // directly to Harvester before calling the `harvestAndSwap`. However,\n // there's no incentive for an attacker to do that. Doing a balance diff\n // will increase the gas cost significantly\n revert BalanceMismatchAfterSwap(baseTokenBalance, amountReceived);\n }\n\n // Farmer only gets fee from the base amount they helped farm,\n // They do not get anything from anything that already was there\n // on the Harvester\n uint256 farmerFee = amountReceived.mulTruncateScale(\n tokenConfig.harvestRewardBps,\n 1e4\n );\n uint256 protocolYield = baseTokenBalance - farmerFee;\n\n baseToken.safeTransfer(rewardProceedsAddress, protocolYield);\n baseToken.safeTransfer(_rewardTo, farmerFee);\n emit RewardProceedsTransferred(\n baseTokenAddress,\n _rewardTo,\n protocolYield,\n farmerFee\n );\n }\n\n function _doSwap(\n SwapPlatform swapPlatform,\n address routerAddress,\n address rewardTokenAddress,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n if (swapPlatform == SwapPlatform.UniswapV2Compatible) {\n return\n _swapWithUniswapV2(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.UniswapV3) {\n return\n _swapWithUniswapV3(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.Balancer) {\n return\n _swapWithBalancer(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.Curve) {\n return\n _swapWithCurve(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else {\n // Should never be invoked since we catch invalid values\n // in the `setRewardTokenConfig` function before it's set\n revert InvalidSwapPlatform(swapPlatform);\n }\n }\n\n /**\n * @dev Swaps the token to `baseToken` with Uniswap V2\n *\n * @param routerAddress Uniswap V2 Router address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithUniswapV2(\n address routerAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n address[] memory path = uniswapV2Path[swapToken];\n\n uint256[] memory amounts = IUniswapV2Router(routerAddress)\n .swapExactTokensForTokens(\n amountIn,\n minAmountOut,\n path,\n address(this),\n block.timestamp\n );\n\n amountOut = amounts[amounts.length - 1];\n }\n\n /**\n * @dev Swaps the token to `baseToken` with Uniswap V3\n *\n * @param routerAddress Uniswap V3 Router address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithUniswapV3(\n address routerAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n bytes memory path = uniswapV3Path[swapToken];\n\n IUniswapV3Router.ExactInputParams memory params = IUniswapV3Router\n .ExactInputParams({\n path: path,\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: amountIn,\n amountOutMinimum: minAmountOut\n });\n amountOut = IUniswapV3Router(routerAddress).exactInput(params);\n }\n\n /**\n * @dev Swaps the token to `baseToken` on Balancer\n *\n * @param balancerVaultAddress BalancerVaultAddress\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithBalancer(\n address balancerVaultAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n bytes32 poolId = balancerPoolId[swapToken];\n\n IBalancerVault.SingleSwap memory singleSwap = IBalancerVault\n .SingleSwap({\n poolId: poolId,\n kind: IBalancerVault.SwapKind.GIVEN_IN,\n assetIn: swapToken,\n assetOut: baseTokenAddress,\n amount: amountIn,\n userData: hex\"\"\n });\n\n IBalancerVault.FundManagement memory fundMgmt = IBalancerVault\n .FundManagement({\n sender: address(this),\n fromInternalBalance: false,\n recipient: payable(address(this)),\n toInternalBalance: false\n });\n\n amountOut = IBalancerVault(balancerVaultAddress).swap(\n singleSwap,\n fundMgmt,\n minAmountOut,\n block.timestamp\n );\n }\n\n /**\n * @dev Swaps the token to `baseToken` on Curve\n *\n * @param poolAddress Curve Pool Address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithCurve(\n address poolAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n CurvePoolIndices memory indices = curvePoolIndices[swapToken];\n\n // Note: Not all CurvePools return the `amountOut`, make sure\n // to use only pool that do. Otherwise the swap would revert\n // always\n amountOut = ICurvePool(poolAddress).exchange(\n uint256(indices.rewardTokenIndex),\n uint256(indices.baseTokenIndex),\n amountIn,\n minAmountOut\n );\n }\n}\n" + }, + "contracts/harvest/Dripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\n/**\n * @title OUSD Dripper\n *\n * The dripper contract smooths out the yield from point-in-time yield events\n * and spreads the yield out over a configurable time period. This ensures a\n * continuous per block yield to makes users happy as their next rebase\n * amount is always moving up. Also, this makes historical day to day yields\n * smooth, rather than going from a near zero day, to a large APY day, then\n * back to a near zero day again.\n *\n *\n * Design notes\n * - USDT has a smaller resolution than the number of seconds\n * in a week, which can make per second payouts have a rounding error. However\n * the total effect is not large - cents per day, and this money is\n * not lost, just distributed in the future. While we could use a higher\n * decimal precision for the drip perSecond, we chose simpler code.\n * - By calculating the changing drip rates on collects only, harvests and yield\n * events don't have to call anything on this contract or pay any extra gas.\n * Collect() is already be paying for a single write, since it has to reset\n * the lastCollect time.\n * - By having a collectAndRebase method, and having our external systems call\n * that, the OUSD vault does not need any changes, not even to know the address\n * of the dripper.\n * - A rejected design was to retro-calculate the drip rate on each collect,\n * based on the balance at the time of the collect. While this would have\n * required less state, and would also have made the contract respond more quickly\n * to new income, it would break the predictability that is this contract's entire\n * purpose. If we did this, the amount of fundsAvailable() would make sharp increases\n * when funds were deposited.\n * - When the dripper recalculates the rate, it targets spending the balance over\n * the duration. This means that every time that collect is called, if no\n * new funds have been deposited the duration is being pushed back and the\n * rate decreases. This is expected, and ends up following a smoother but\n * longer curve the more collect() is called without incoming yield.\n *\n */\n\ncontract Dripper is Governable {\n using SafeERC20 for IERC20;\n\n struct Drip {\n uint64 lastCollect; // overflows 262 billion years after the sun dies\n uint192 perSecond; // drip rate per second\n }\n\n address immutable vault; // OUSD vault\n address immutable token; // token to drip out\n uint256 public dripDuration; // in seconds\n Drip public drip; // active drip parameters\n\n constructor(address _vault, address _token) {\n vault = _vault;\n token = _token;\n }\n\n /// @notice How much funds have dripped out already and are currently\n // available to be sent to the vault.\n /// @return The amount that would be sent if a collect was called\n function availableFunds() external view returns (uint256) {\n uint256 balance = IERC20(token).balanceOf(address(this));\n return _availableFunds(balance, drip);\n }\n\n /// @notice Collect all dripped funds and send to vault.\n /// Recalculate new drip rate.\n function collect() external {\n _collect();\n }\n\n /// @notice Collect all dripped funds, send to vault, recalculate new drip\n /// rate, and rebase OUSD.\n function collectAndRebase() external {\n _collect();\n IVault(vault).rebase();\n }\n\n /// @dev Change the drip duration. Governor only.\n /// @param _durationSeconds the number of seconds to drip out the entire\n /// balance over if no collects were called during that time.\n function setDripDuration(uint256 _durationSeconds)\n external\n virtual\n onlyGovernor\n {\n require(_durationSeconds > 0, \"duration must be non-zero\");\n dripDuration = _durationSeconds;\n _collect(); // duration change take immediate effect\n }\n\n /// @dev Transfer out ERC20 tokens held by the contract. Governor only.\n /// @param _asset ERC20 token address\n /// @param _amount amount to transfer\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /// @dev Calculate available funds by taking the lower of either the\n /// currently dripped out funds or the balance available.\n /// Uses passed in parameters to calculate with for gas savings.\n /// @param _balance current balance in contract\n /// @param _drip current drip parameters\n function _availableFunds(uint256 _balance, Drip memory _drip)\n internal\n view\n returns (uint256)\n {\n uint256 elapsed = block.timestamp - _drip.lastCollect;\n uint256 allowed = (elapsed * _drip.perSecond);\n return (allowed > _balance) ? _balance : allowed;\n }\n\n /// @dev Sends the currently dripped funds to be vault, and sets\n /// the new drip rate based on the new balance.\n function _collect() internal virtual {\n // Calculate send\n uint256 balance = IERC20(token).balanceOf(address(this));\n uint256 amountToSend = _availableFunds(balance, drip);\n uint256 remaining = balance - amountToSend;\n // Calculate new drip perSecond\n // Gas savings by setting entire struct at one time\n drip = Drip({\n perSecond: uint192(remaining / dripDuration),\n lastCollect: uint64(block.timestamp)\n });\n // Send funds\n IERC20(token).safeTransfer(vault, amountToSend);\n }\n\n /// @dev Transfer out all ERC20 held by the contract. Governor only.\n /// @param _asset ERC20 token address\n function transferAllToken(address _asset, address _receiver)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(\n _receiver,\n IERC20(_asset).balanceOf(address(this))\n );\n }\n}\n" + }, + "contracts/harvest/FixedRateDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Dripper } from \"./Dripper.sol\";\n\n/**\n * @title Fixed Rate Dripper\n *\n * Similar to the Dripper, Fixed Rate Dripper drips out yield per second.\n * However the Strategist decides the rate and it doesn't change after\n * a drip.\n *\n */\n\ncontract FixedRateDripper is Dripper {\n using SafeERC20 for IERC20;\n\n event DripRateUpdated(uint192 oldDripRate, uint192 newDripRate);\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vault).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n constructor(address _vault, address _token) Dripper(_vault, _token) {}\n\n /// @inheritdoc Dripper\n function setDripDuration(uint256) external virtual override {\n // Not used in FixedRateDripper\n revert(\"Drip duration disabled\");\n }\n\n /// @inheritdoc Dripper\n function _collect() internal virtual override {\n // Calculate amount to send\n uint256 balance = IERC20(token).balanceOf(address(this));\n uint256 amountToSend = _availableFunds(balance, drip);\n\n // Update timestamp\n drip.lastCollect = uint64(block.timestamp);\n\n // Send funds\n IERC20(token).safeTransfer(vault, amountToSend);\n }\n\n /**\n * @dev Sets the drip rate. Callable by Strategist or Governor.\n * Can be set to zero to stop dripper.\n * @param _perSecond Rate of WETH to drip per second\n */\n function setDripRate(uint192 _perSecond) external onlyGovernorOrStrategist {\n emit DripRateUpdated(_perSecond, drip.perSecond);\n\n /**\n * Note: It's important to call `_collect` before updating\n * the drip rate especially on a new proxy contract.\n * When `lastCollect` is not set/initialized, the elapsed\n * time would be calculated as `block.number` seconds,\n * resulting in a huge yield, if `collect` isn't called first.\n */\n // Collect at existing rate\n _collect();\n\n // Update rate\n drip.perSecond = _perSecond;\n }\n}\n" + }, + "contracts/harvest/Harvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractHarvester } from \"./AbstractHarvester.sol\";\n\ncontract Harvester is AbstractHarvester {\n constructor(address _vault, address _usdtAddress)\n AbstractHarvester(_vault, _usdtAddress)\n {}\n}\n" + }, + "contracts/harvest/OETHBaseHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { ISwapRouter } from \"../interfaces/aerodrome/ISwapRouter.sol\";\n\ncontract OETHBaseHarvester is Governable {\n using SafeERC20 for IERC20;\n\n IVault public immutable vault;\n IStrategy public immutable amoStrategy;\n IERC20 public immutable aero;\n IERC20 public immutable weth;\n ISwapRouter public immutable swapRouter;\n\n address public operatorAddr;\n\n // Similar sig to `AbstractHarvester.RewardTokenSwapped` for\n // future compatibility with monitoring\n event RewardTokenSwapped(\n address indexed rewardToken,\n address indexed swappedInto,\n uint8 swapPlatform,\n uint256 amountIn,\n uint256 amountOut\n );\n\n event OperatorChanged(address oldOperator, address newOperator);\n event YieldSent(address recipient, uint256 yield, uint256 fee);\n\n /**\n * @notice Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == vault.strategistAddr() || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @notice Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategistOrOperator() {\n require(\n msg.sender == operatorAddr ||\n msg.sender == vault.strategistAddr() ||\n isGovernor(),\n \"Caller is not the Operator or Strategist or Governor\"\n );\n _;\n }\n\n constructor(\n address _vault,\n address _amoStrategy,\n address _aero,\n address _weth,\n address _swapRouter\n ) {\n vault = IVault(_vault);\n amoStrategy = IStrategy(_amoStrategy);\n aero = IERC20(_aero);\n weth = IERC20(_weth);\n swapRouter = ISwapRouter(_swapRouter);\n }\n\n /**\n * @dev Changes the operator address which can call `harvest`\n * @param _operatorAddr New operator address\n */\n function setOperatorAddr(address _operatorAddr) external onlyGovernor {\n emit OperatorChanged(operatorAddr, _operatorAddr);\n operatorAddr = _operatorAddr;\n }\n\n /**\n * @notice Collects AERO from AMO strategy and\n * sends it to the Strategist multisig.\n * Anyone can call it.\n */\n function harvest() external onlyGovernorOrStrategistOrOperator {\n address strategistAddr = vault.strategistAddr();\n require(strategistAddr != address(0), \"Guardian address not set\");\n\n // Collect all AERO\n amoStrategy.collectRewardTokens();\n\n uint256 aeroBalance = aero.balanceOf(address(this));\n if (aeroBalance == 0) {\n // Do nothing if there's no AERO to transfer\n return;\n }\n\n // Transfer everything to Strategist\n aero.safeTransfer(strategistAddr, aeroBalance);\n }\n\n /**\n * @notice Harvests AERO from AMO strategy and then swaps some (or all)\n * of it into WETH to distribute yield and fee.\n * When `feeBps` is set to 10000 (100%), all WETH received is\n * sent to strategist.\n *\n * @param aeroToSwap Amount of AERO to swap\n * @param minWETHExpected Min. amount of WETH to expect\n * @param feeBps Performance fee bps (Sent to strategist)\n * @param sendYieldToDripper Sends yield to Dripper, if set to true.\n * Otherwise, to the Guardian\n */\n function harvestAndSwap(\n uint256 aeroToSwap,\n uint256 minWETHExpected,\n uint256 feeBps,\n bool sendYieldToDripper\n ) external onlyGovernorOrStrategist {\n address strategistAddr = vault.strategistAddr();\n require(strategistAddr != address(0), \"Guardian address not set\");\n\n // Yields can either be sent to the Dripper or Strategist\n address yieldRecipient = sendYieldToDripper\n ? vault.dripper()\n : strategistAddr;\n require(yieldRecipient != address(0), \"Yield recipient not set\");\n\n require(feeBps <= 10000, \"Invalid Fee Bps\");\n\n // Collect all AERO\n amoStrategy.collectRewardTokens();\n\n uint256 aeroBalance = aero.balanceOf(address(this));\n if (aeroBalance == 0) {\n // Do nothing if there's no AERO to transfer/swap\n return;\n }\n\n if (aeroToSwap > 0) {\n if (aeroBalance < aeroToSwap) {\n // Transfer in balance from the multisig as needed\n // slither-disable-next-line unchecked-transfer arbitrary-send-erc20\n aero.safeTransferFrom(\n strategistAddr,\n address(this),\n aeroToSwap - aeroBalance\n );\n }\n\n _doSwap(aeroToSwap, minWETHExpected);\n\n // Figure out AERO left in contract after swap\n aeroBalance = aero.balanceOf(address(this));\n }\n\n // Transfer out any leftover AERO after swap\n if (aeroBalance > 0) {\n aero.safeTransfer(strategistAddr, aeroBalance);\n }\n\n // Computes using all balance the contract holds,\n // not just the WETH received from swap. Use `transferToken`\n // if there's any WETH left that needs to be taken out\n uint256 availableWETHBalance = weth.balanceOf(address(this));\n // Computation rounds in favor of protocol\n uint256 fee = (availableWETHBalance * feeBps) / 10000;\n uint256 yield = availableWETHBalance - fee;\n\n // Transfer yield, if any\n if (yield > 0) {\n weth.safeTransfer(yieldRecipient, yield);\n }\n\n // Transfer fee to the Guardian, if any\n if (fee > 0) {\n weth.safeTransfer(strategistAddr, fee);\n }\n\n emit YieldSent(yieldRecipient, yield, fee);\n }\n\n /**\n * @notice Swaps AERO to WETH on Aerodrome\n * @param aeroToSwap Amount of AERO to swap\n * @param minWETHExpected Min. amount of WETH to expect\n */\n function _doSwap(uint256 aeroToSwap, uint256 minWETHExpected) internal {\n // Let the swap router move funds\n aero.approve(address(swapRouter), aeroToSwap);\n\n // Do the swap\n uint256 wethReceived = swapRouter.exactInputSingle(\n ISwapRouter.ExactInputSingleParams({\n tokenIn: address(aero),\n tokenOut: address(weth),\n tickSpacing: 200, // From AERO/WETH pool contract\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: aeroToSwap,\n amountOutMinimum: minWETHExpected,\n sqrtPriceLimitX96: 0\n })\n );\n\n emit RewardTokenSwapped(\n address(aero),\n address(weth),\n 0,\n aeroToSwap,\n wethReceived\n );\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * the contract, i.e. mistaken sends.\n * Also, allows to transfer any AERO left in the contract.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n virtual\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n}\n" + }, + "contracts/harvest/OETHDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Dripper } from \"./Dripper.sol\";\n\n/**\n * @title OETH Dripper Contract\n * @author Origin Protocol Inc\n */\ncontract OETHDripper is Dripper {\n constructor(address _vault, address _token) Dripper(_vault, _token) {}\n}\n" + }, + "contracts/harvest/OETHFixedRateDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { FixedRateDripper } from \"./FixedRateDripper.sol\";\n\n/**\n * @title OETH FixedRateDripper Contract\n * @author Origin Protocol Inc\n */\ncontract OETHFixedRateDripper is FixedRateDripper {\n constructor(address _vault, address _token)\n FixedRateDripper(_vault, _token)\n {}\n}\n" + }, + "contracts/harvest/OETHHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractHarvester } from \"./AbstractHarvester.sol\";\n\ncontract OETHHarvester is AbstractHarvester {\n constructor(address _vault, address _wethAddress)\n AbstractHarvester(_vault, _wethAddress)\n {}\n}\n" + }, + "contracts/harvest/OETHHarvesterSimple.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\n\n/// @title OETH Harvester Simple Contract\n/// @notice Contract to harvest rewards from strategies\n/// @author Origin Protocol Inc\ncontract OETHHarvesterSimple is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n ////////////////////////////////////////////////////\n /// --- CONSTANTS & IMMUTABLES\n ////////////////////////////////////////////////////\n /// @notice wrapped native token address (WETH or wS)\n address public immutable wrappedNativeToken;\n\n ////////////////////////////////////////////////////\n /// --- STORAGE\n ////////////////////////////////////////////////////\n /// @notice Dripper address\n address public dripper;\n\n /// @notice Mapping of supported strategies\n mapping(address => bool) public supportedStrategies;\n\n /// @notice Gap for upgrade safety\n uint256[48] private ___gap;\n\n ////////////////////////////////////////////////////\n /// --- EVENTS\n ////////////////////////////////////////////////////\n event Harvested(\n address indexed strategy,\n address token,\n uint256 amount,\n address indexed receiver\n );\n event SupportedStrategyUpdated(address strategy, bool status);\n event DripperUpdated(address dripper);\n\n ////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////\n constructor(address _wrappedNativeToken) {\n wrappedNativeToken = _wrappedNativeToken;\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /// @notice Initialize the contract\n function initialize() external onlyGovernor initializer {\n // Call it to set `initialized` to true and to prevent the implementation\n // from getting initialized in future through the proxy\n }\n\n ////////////////////////////////////////////////////\n /// --- MUTATIVE FUNCTIONS\n ////////////////////////////////////////////////////\n /// @notice Harvest rewards from a strategy and transfer to strategist or dripper\n /// @param _strategy Address of the strategy to harvest\n function harvestAndTransfer(address _strategy) external {\n _harvestAndTransfer(_strategy);\n }\n\n /// @notice Harvest rewards from multiple strategies and transfer to strategist or dripper\n /// @param _strategies Array of strategy addresses to harvest\n function harvestAndTransfer(address[] calldata _strategies) external {\n for (uint256 i = 0; i < _strategies.length; i++) {\n _harvestAndTransfer(_strategies[i]);\n }\n }\n\n /// @notice Internal logic to harvest rewards from a strategy\n function _harvestAndTransfer(address _strategy) internal virtual {\n // Ensure strategy is supported\n require(supportedStrategies[_strategy], \"Strategy not supported\");\n\n // Store locally for some gas savings\n address _strategist = strategistAddr;\n address _dripper = dripper;\n\n // Harvest rewards\n IStrategy(_strategy).collectRewardTokens();\n\n // Cache reward tokens\n address[] memory rewardTokens = IStrategy(_strategy)\n .getRewardTokenAddresses();\n\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; i++) {\n // Cache balance\n address token = rewardTokens[i];\n uint256 balance = IERC20(token).balanceOf(address(this));\n if (balance > 0) {\n // Determine receiver\n address receiver = token == wrappedNativeToken\n ? _dripper\n : _strategist;\n require(receiver != address(0), \"Invalid receiver\");\n\n // Transfer to the Strategist or the Dripper\n IERC20(token).safeTransfer(receiver, balance);\n emit Harvested(_strategy, token, balance, receiver);\n }\n }\n }\n\n ////////////////////////////////////////////////////\n /// --- GOVERNANCE\n ////////////////////////////////////////////////////\n /// @notice Set supported strategy\n /// @param _strategy Address of the strategy\n /// @param _isSupported Boolean indicating if strategy is supported\n function setSupportedStrategy(address _strategy, bool _isSupported)\n external\n onlyGovernorOrStrategist\n {\n require(_strategy != address(0), \"Invalid strategy\");\n supportedStrategies[_strategy] = _isSupported;\n emit SupportedStrategyUpdated(_strategy, _isSupported);\n }\n\n /// @notice Transfer tokens to strategist\n /// @param _asset Address of the token\n /// @param _amount Amount of tokens to transfer\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernorOrStrategist\n {\n IERC20(_asset).safeTransfer(strategistAddr, _amount);\n }\n\n /// @notice Set the dripper address\n /// @param _dripper Address of the dripper\n function setDripper(address _dripper) external onlyGovernor {\n _setDripper(_dripper);\n }\n\n /// @notice Internal logic to set the dripper address\n function _setDripper(address _dripper) internal {\n require(_dripper != address(0), \"Invalid dripper\");\n dripper = _dripper;\n emit DripperUpdated(_dripper);\n }\n}\n" + }, + "contracts/harvest/OSonicHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SuperOETHHarvester } from \"./SuperOETHHarvester.sol\";\n\ncontract OSonicHarvester is SuperOETHHarvester {\n /// @param _wrappedNativeToken Address of the native Wrapped S (wS) token\n constructor(address _wrappedNativeToken)\n SuperOETHHarvester(_wrappedNativeToken)\n {}\n}\n" + }, + "contracts/harvest/SuperOETHHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHHarvesterSimple, IERC20, IStrategy, SafeERC20 } from \"./OETHHarvesterSimple.sol\";\n\ncontract SuperOETHHarvester is OETHHarvesterSimple {\n using SafeERC20 for IERC20;\n\n constructor(address _wrappedNativeToken)\n OETHHarvesterSimple(_wrappedNativeToken)\n {}\n\n /// @inheritdoc OETHHarvesterSimple\n function _harvestAndTransfer(address _strategy) internal virtual override {\n // Ensure strategy is supported\n require(supportedStrategies[_strategy], \"Strategy not supported\");\n\n address receiver = strategistAddr;\n require(receiver != address(0), \"Invalid receiver\");\n\n // Harvest rewards\n IStrategy(_strategy).collectRewardTokens();\n\n // Cache reward tokens\n address[] memory rewardTokens = IStrategy(_strategy)\n .getRewardTokenAddresses();\n\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; i++) {\n // Cache balance\n address token = rewardTokens[i];\n uint256 balance = IERC20(token).balanceOf(address(this));\n if (balance > 0) {\n // Transfer everything to the strategist\n IERC20(token).safeTransfer(receiver, balance);\n emit Harvested(_strategy, token, balance, receiver);\n }\n }\n }\n}\n" + }, + "contracts/interfaces/aerodrome/IAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ICLPool } from \"./ICLPool.sol\";\n\ninterface IAMOStrategy {\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth);\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth);\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n error OutsideExpectedTickRange(int24 currentTick);\n\n function governor() external view returns (address);\n\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) external;\n\n function clPool() external view returns (ICLPool);\n\n function vaultAddress() external view returns (address);\n\n function poolWethShareVarianceAllowed() external view returns (uint256);\n\n function poolWethShare() external view returns (uint256);\n\n function tokenId() external view returns (uint256);\n\n function withdrawAll() external;\n\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external;\n\n function setWithdrawLiquidityShare(uint128 share) external;\n\n function lowerTick() external view returns (int24);\n\n function upperTick() external view returns (int24);\n\n function getPoolX96Price() external view returns (uint160 _sqrtRatioX96);\n\n function sqrtRatioX96TickLower() external view returns (uint160);\n\n function sqrtRatioX96TickHigher() external view returns (uint160);\n\n function tickSpacing() external view returns (int24);\n\n function allowedWethShareStart() external view returns (uint256);\n\n function allowedWethShareEnd() external view returns (uint256);\n\n function claimGovernance() external;\n\n function transferGovernance(address _governor) external;\n\n function getPositionPrincipal()\n external\n view\n returns (uint256 _amountWeth, uint256 _amountOethb);\n}\n" + }, + "contracts/interfaces/aerodrome/ICLGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\ninterface ICLGauge {\n /// @notice Returns the claimable rewards for a given account and tokenId\n /// @dev Throws if account is not the position owner\n /// @dev pool.updateRewardsGrowthGlobal() needs to be called first, to return the correct claimable rewards\n /// @param account The address of the user\n /// @param tokenId The tokenId of the position\n /// @return The amount of claimable reward\n function earned(address account, uint256 tokenId)\n external\n view\n returns (uint256);\n\n /// @notice Retrieve rewards for all tokens owned by an account\n /// @dev Throws if not called by the voter\n /// @param account The account of the user\n function getReward(address account) external;\n\n /// @notice Retrieve rewards for a tokenId\n /// @dev Throws if not called by the position owner\n /// @param tokenId The tokenId of the position\n function getReward(uint256 tokenId) external;\n\n /// @notice Notifies gauge of gauge rewards.\n /// @param amount Amount of gauge rewards (emissions) to notify. Must be greater than 604_800.\n function notifyRewardAmount(uint256 amount) external;\n\n /// @dev Notifies gauge of gauge rewards without distributing its fees.\n /// Assumes gauge reward tokens is 18 decimals.\n /// If not 18 decimals, rewardRate may have rounding issues.\n /// @param amount Amount of gauge rewards (emissions) to notify. Must be greater than 604_800.\n function notifyRewardWithoutClaim(uint256 amount) external;\n\n /// @notice Used to deposit a CL position into the gauge\n /// @notice Allows the user to receive emissions instead of fees\n /// @param tokenId The tokenId of the position\n function deposit(uint256 tokenId) external;\n\n /// @notice Used to withdraw a CL position from the gauge\n /// @notice Allows the user to receive fees instead of emissions\n /// @notice Outstanding emissions will be collected on withdrawal\n /// @param tokenId The tokenId of the position\n function withdraw(uint256 tokenId) external;\n\n // /// @notice Fetch all tokenIds staked by a given account\n // /// @param depositor The address of the user\n // /// @return The tokenIds of the staked positions\n // function stakedValues(address depositor) external view returns (uint256[] memory);\n\n // /// @notice Fetch a staked tokenId by index\n // /// @param depositor The address of the user\n // /// @param index The index of the staked tokenId\n // /// @return The tokenId of the staked position\n // function stakedByIndex(address depositor, uint256 index) external view returns (uint256);\n\n // /// @notice Check whether a position is staked in the gauge by a certain user\n // /// @param depositor The address of the user\n // /// @param tokenId The tokenId of the position\n // /// @return Whether the position is staked in the gauge\n // function stakedContains(address depositor, uint256 tokenId) external view returns (bool);\n\n // /// @notice The amount of positions staked in the gauge by a certain user\n // /// @param depositor The address of the user\n // /// @return The amount of positions staked in the gauge\n // function stakedLength(address depositor) external view returns (uint256);\n\n function feesVotingReward() external view returns (address);\n}\n" + }, + "contracts/interfaces/aerodrome/ICLPool.sol": { + "content": "pragma solidity >=0.5.0;\n\n/// @title The interface for a CL Pool\n/// @notice A CL pool facilitates swapping and automated market making between any two assets that strictly conform\n/// to the ERC20 specification\n/// @dev The pool interface is broken up into many smaller pieces\ninterface ICLPool {\n function slot0()\n external\n view\n returns (\n uint160 sqrtPriceX96,\n int24 tick,\n uint16 observationIndex,\n uint16 observationCardinality,\n uint16 observationCardinalityNext,\n bool unlocked\n );\n\n /// @notice The first of the two tokens of the pool, sorted by address\n /// @return The token contract address\n function token0() external view returns (address);\n\n /// @notice The second of the two tokens of the pool, sorted by address\n /// @return The token contract address\n function token1() external view returns (address);\n\n function tickSpacing() external view returns (int24);\n\n /// @notice The gauge corresponding to this pool\n /// @return The gauge contract address\n function gauge() external view returns (address);\n\n /// @notice The currently in range liquidity available to the pool\n /// @dev This value has no relationship to the total liquidity across all ticks\n /// @dev This value includes staked liquidity\n function liquidity() external view returns (uint128);\n\n /// @notice Look up information about a specific tick in the pool\n /// @param tick The tick to look up\n /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or\n /// tick upper,\n /// liquidityNet how much liquidity changes when the pool price crosses the tick,\n /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,\n /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,\n /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick\n /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from\n /// the current tick,\n /// secondsOutside the seconds spent on the other side of the tick from the current tick,\n /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise\n /// equal to false.\n /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.\n /// In addition, these values are only relative and must be used only in comparison to previous snapshots for\n /// a specific position.\n function ticks(int24 tick)\n external\n view\n returns (\n uint128 liquidityGross,\n int128 liquidityNet,\n uint256 feeGrowthOutside0X128,\n uint256 feeGrowthOutside1X128,\n int56 tickCumulativeOutside,\n uint160 secondsPerLiquidityOutsideX128,\n uint32 secondsOutside,\n bool initialized\n );\n}\n" + }, + "contracts/interfaces/aerodrome/INonfungiblePositionManager.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title Non-fungible token for positions\n/// @notice Wraps CL positions in a non-fungible token interface which allows for them to be transferred\n/// and authorized.\n// slither-disable-start erc20-interface\ninterface INonfungiblePositionManager {\n /**\n * @dev See {IERC721-approve}.\n */\n function approve(address to, uint256 tokenId) external;\n\n /**\n * @dev See {IERC721-getApproved}.\n */\n function getApproved(uint256 tokenId) external returns (address);\n\n /**\n * @dev See {IERC721-ownerOf}.\n */\n function ownerOf(uint256 tokenId) external view returns (address);\n\n /// @notice Returns the position information associated with a given token ID.\n /// @dev Throws if the token ID is not valid.\n /// @param tokenId The ID of the token that represents the position\n /// @return nonce The nonce for permits\n /// @return operator The address that is approved for spending\n /// @return token0 The address of the token0 for a specific pool\n /// @return token1 The address of the token1 for a specific pool\n /// @return tickSpacing The tick spacing associated with the pool\n /// @return tickLower The lower end of the tick range for the position\n /// @return tickUpper The higher end of the tick range for the position\n /// @return liquidity The liquidity of the position\n /// @return feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position\n /// @return feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position\n /// @return tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation\n /// @return tokensOwed1 The uncollected amount of token1 owed to the position as of the last computation\n function positions(uint256 tokenId)\n external\n view\n returns (\n uint96 nonce,\n address operator,\n address token0,\n address token1,\n int24 tickSpacing,\n int24 tickLower,\n int24 tickUpper,\n uint128 liquidity,\n uint256 feeGrowthInside0LastX128,\n uint256 feeGrowthInside1LastX128,\n uint128 tokensOwed0,\n uint128 tokensOwed1\n );\n\n struct MintParams {\n address token0;\n address token1;\n int24 tickSpacing;\n int24 tickLower;\n int24 tickUpper;\n uint256 amount0Desired;\n uint256 amount1Desired;\n uint256 amount0Min;\n uint256 amount1Min;\n address recipient;\n uint256 deadline;\n uint160 sqrtPriceX96;\n }\n\n /// @notice Creates a new position wrapped in a NFT\n /// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized\n /// a method does not exist, i.e. the pool is assumed to be initialized.\n /// @param params The params necessary to mint a position, encoded as `MintParams` in calldata\n /// @return tokenId The ID of the token that represents the minted position\n /// @return liquidity The amount of liquidity for this position\n /// @return amount0 The amount of token0\n /// @return amount1 The amount of token1\n function mint(MintParams calldata params)\n external\n payable\n returns (\n uint256 tokenId,\n uint128 liquidity,\n uint256 amount0,\n uint256 amount1\n );\n\n struct IncreaseLiquidityParams {\n uint256 tokenId;\n uint256 amount0Desired;\n uint256 amount1Desired;\n uint256 amount0Min;\n uint256 amount1Min;\n uint256 deadline;\n }\n\n /// @notice Increases the amount of liquidity in a position, with tokens paid by the `msg.sender`\n /// @param params tokenId The ID of the token for which liquidity is being increased,\n /// amount0Desired The desired amount of token0 to be spent,\n /// amount1Desired The desired amount of token1 to be spent,\n /// amount0Min The minimum amount of token0 to spend, which serves as a slippage check,\n /// amount1Min The minimum amount of token1 to spend, which serves as a slippage check,\n /// deadline The time by which the transaction must be included to effect the change\n /// @return liquidity The new liquidity amount as a result of the increase\n /// @return amount0 The amount of token0 to acheive resulting liquidity\n /// @return amount1 The amount of token1 to acheive resulting liquidity\n function increaseLiquidity(IncreaseLiquidityParams calldata params)\n external\n payable\n returns (\n uint128 liquidity,\n uint256 amount0,\n uint256 amount1\n );\n\n struct DecreaseLiquidityParams {\n uint256 tokenId;\n uint128 liquidity;\n uint256 amount0Min;\n uint256 amount1Min;\n uint256 deadline;\n }\n\n /// @notice Decreases the amount of liquidity in a position and accounts it to the position\n /// @param params tokenId The ID of the token for which liquidity is being decreased,\n /// amount The amount by which liquidity will be decreased,\n /// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity,\n /// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity,\n /// deadline The time by which the transaction must be included to effect the change\n /// @return amount0 The amount of token0 accounted to the position's tokens owed\n /// @return amount1 The amount of token1 accounted to the position's tokens owed\n /// @dev The use of this function can cause a loss to users of the NonfungiblePositionManager\n /// @dev for tokens that have very high decimals.\n /// @dev The amount of tokens necessary for the loss is: 3.4028237e+38.\n /// @dev This is equivalent to 1e20 value with 18 decimals.\n function decreaseLiquidity(DecreaseLiquidityParams calldata params)\n external\n payable\n returns (uint256 amount0, uint256 amount1);\n\n struct CollectParams {\n uint256 tokenId;\n address recipient;\n uint128 amount0Max;\n uint128 amount1Max;\n }\n\n /// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient\n /// @notice Used to update staked positions before deposit and withdraw\n /// @param params tokenId The ID of the NFT for which tokens are being collected,\n /// recipient The account that should receive the tokens,\n /// amount0Max The maximum amount of token0 to collect,\n /// amount1Max The maximum amount of token1 to collect\n /// @return amount0 The amount of fees collected in token0\n /// @return amount1 The amount of fees collected in token1\n function collect(CollectParams calldata params)\n external\n payable\n returns (uint256 amount0, uint256 amount1);\n\n /// @notice Burns a token ID, which deletes it from the NFT contract. The token must have 0 liquidity and all tokens\n /// must be collected first.\n /// @param tokenId The ID of the token that is being burned\n function burn(uint256 tokenId) external payable;\n\n /// @notice Sets a new Token Descriptor\n /// @param _tokenDescriptor Address of the new Token Descriptor to be chosen\n function setTokenDescriptor(address _tokenDescriptor) external;\n\n /// @notice Sets a new Owner address\n /// @param _owner Address of the new Owner to be chosen\n function setOwner(address _owner) external;\n}\n// slither-disable-end erc20-interface\n" + }, + "contracts/interfaces/aerodrome/IQuoterV2.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title QuoterV2 Interface\n/// @notice Supports quoting the calculated amounts from exact input or exact output swaps.\n/// @notice For each pool also tells you the number of initialized ticks crossed and the sqrt price of the\n/// pool after the swap.\n/// @dev These functions are not marked view because they rely on calling non-view functions and reverting\n/// to compute the result. They are also not gas efficient and should not be called on-chain.\ninterface IQuoterV2 {\n /// @notice Returns the amount out received for a given exact input swap without executing the swap\n /// @param path The path of the swap, i.e. each token pair and the pool tick spacing\n /// @param amountIn The amount of the first token to swap\n /// @return amountOut The amount of the last token that would be received\n /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path\n /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for\n /// each pool in the path\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactInput(bytes memory path, uint256 amountIn)\n external\n returns (\n uint256 amountOut,\n uint160[] memory sqrtPriceX96AfterList,\n uint32[] memory initializedTicksCrossedList,\n uint256 gasEstimate\n );\n\n struct QuoteExactInputSingleParams {\n address tokenIn;\n address tokenOut;\n uint256 amountIn;\n int24 tickSpacing;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Returns the amount out received for a given exact input but for a swap of a single pool\n /// @param params The params for the quote, encoded as `QuoteExactInputSingleParams`\n /// tokenIn The token being swapped in\n /// tokenOut The token being swapped out\n /// tickSpacing The tick spacing of the token pool to consider for the pair\n /// amountIn The desired input amount\n /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap\n /// @return amountOut The amount of `tokenOut` that would be received\n /// @return sqrtPriceX96After The sqrt price of the pool after the swap\n /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactInputSingle(QuoteExactInputSingleParams memory params)\n external\n returns (\n uint256 amountOut,\n uint160 sqrtPriceX96After,\n uint32 initializedTicksCrossed,\n uint256 gasEstimate\n );\n\n /// @notice Returns the amount in required for a given exact output swap without executing the swap\n /// @param path The path of the swap, i.e. each token pair and the pool tick spacing.\n /// Path must be provided in reverse order\n /// @param amountOut The amount of the last token to receive\n /// @return amountIn The amount of first token required to be paid\n /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path\n /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for\n /// each pool in the path\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactOutput(bytes memory path, uint256 amountOut)\n external\n returns (\n uint256 amountIn,\n uint160[] memory sqrtPriceX96AfterList,\n uint32[] memory initializedTicksCrossedList,\n uint256 gasEstimate\n );\n\n struct QuoteExactOutputSingleParams {\n address tokenIn;\n address tokenOut;\n uint256 amount;\n int24 tickSpacing;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Returns the amount in required to receive the given exact output amount but for a swap of a single pool\n /// @param params The params for the quote, encoded as `QuoteExactOutputSingleParams`\n /// tokenIn The token being swapped in\n /// tokenOut The token being swapped out\n /// tickSpacing The tick spacing of the token pool to consider for the pair\n /// amountOut The desired output amount\n /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap\n /// @return amountIn The amount required as the input for the swap in order to receive `amountOut`\n /// @return sqrtPriceX96After The sqrt price of the pool after the swap\n /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactOutputSingle(QuoteExactOutputSingleParams memory params)\n external\n returns (\n uint256 amountIn,\n uint160 sqrtPriceX96After,\n uint32 initializedTicksCrossed,\n uint256 gasEstimate\n );\n}\n" + }, + "contracts/interfaces/aerodrome/ISugarHelper.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\npragma abicoder v2;\n\nimport { INonfungiblePositionManager } from \"./INonfungiblePositionManager.sol\";\n\ninterface ISugarHelper {\n struct PopulatedTick {\n int24 tick;\n uint160 sqrtRatioX96;\n int128 liquidityNet;\n uint128 liquidityGross;\n }\n\n ///\n /// Wrappers for LiquidityAmounts\n ///\n\n function getAmountsForLiquidity(\n uint160 sqrtRatioX96,\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96,\n uint128 liquidity\n ) external pure returns (uint256 amount0, uint256 amount1);\n\n function getLiquidityForAmounts(\n uint256 amount0,\n uint256 amount1,\n uint160 sqrtRatioX96,\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96\n ) external pure returns (uint128 liquidity);\n\n /// @notice Computes the amount of token0 for a given amount of token1 and price range\n /// @param amount1 Amount of token1 to estimate liquidity\n /// @param pool Address of the pool to be used\n /// @param sqrtRatioX96 A sqrt price representing the current pool prices\n /// @param tickLow Lower tick boundary\n /// @param tickLow Upper tick boundary\n /// @dev If the given pool address is not the zero address, will fetch `sqrtRatioX96` from pool\n /// @return amount0 Estimated amount of token0\n function estimateAmount0(\n uint256 amount1,\n address pool,\n uint160 sqrtRatioX96,\n int24 tickLow,\n int24 tickHigh\n ) external view returns (uint256 amount0);\n\n /// @notice Computes the amount of token1 for a given amount of token0 and price range\n /// @param amount0 Amount of token0 to estimate liquidity\n /// @param pool Address of the pool to be used\n /// @param sqrtRatioX96 A sqrt price representing the current pool prices\n /// @param tickLow Lower tick boundary\n /// @param tickLow Upper tick boundary\n /// @dev If the given pool address is not the zero address, will fetch `sqrtRatioX96` from pool\n /// @return amount1 Estimated amount of token1\n function estimateAmount1(\n uint256 amount0,\n address pool,\n uint160 sqrtRatioX96,\n int24 tickLow,\n int24 tickHigh\n ) external view returns (uint256 amount1);\n\n ///\n /// Wrappers for PositionValue\n ///\n\n function principal(\n INonfungiblePositionManager positionManager,\n uint256 tokenId,\n uint160 sqrtRatioX96\n ) external view returns (uint256 amount0, uint256 amount1);\n\n function fees(INonfungiblePositionManager positionManager, uint256 tokenId)\n external\n view\n returns (uint256 amount0, uint256 amount1);\n\n ///\n /// Wrappers for TickMath\n ///\n\n function getSqrtRatioAtTick(int24 tick)\n external\n pure\n returns (uint160 sqrtRatioX96);\n\n function getTickAtSqrtRatio(uint160 sqrtRatioX96)\n external\n pure\n returns (int24 tick);\n\n /// @notice Fetches Tick Data for all populated Ticks in given bitmaps\n /// @param pool Address of the pool from which to fetch data\n /// @param startTick Tick from which the first bitmap will be fetched\n /// @dev The number of bitmaps fetched by this function should always be `MAX_BITMAPS`,\n /// unless there are less than `MAX_BITMAPS` left to iterate through\n /// @return populatedTicks Array of all Populated Ticks in the provided bitmaps\n function getPopulatedTicks(address pool, int24 startTick)\n external\n view\n returns (PopulatedTick[] memory populatedTicks);\n}\n" + }, + "contracts/interfaces/aerodrome/ISwapRouter.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title Router token swapping functionality\n/// @notice Functions for swapping tokens via CL\ninterface ISwapRouter {\n struct ExactInputSingleParams {\n address tokenIn;\n address tokenOut;\n int24 tickSpacing;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInputSingle(ExactInputSingleParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactOutputSingleParams {\n address tokenIn;\n address tokenOut;\n int24 tickSpacing;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutputSingle(ExactOutputSingleParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n\n struct ExactOutputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutput(ExactOutputParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n}\n" + }, + "contracts/interfaces/balancer/IBalancerVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"../../utils/InitializableAbstractStrategy.sol\";\n\ninterface IBalancerVault {\n enum WeightedPoolJoinKind {\n INIT,\n EXACT_TOKENS_IN_FOR_BPT_OUT,\n TOKEN_IN_FOR_EXACT_BPT_OUT,\n ALL_TOKENS_IN_FOR_EXACT_BPT_OUT,\n ADD_TOKEN\n }\n\n enum WeightedPoolExitKind {\n EXACT_BPT_IN_FOR_ONE_TOKEN_OUT,\n EXACT_BPT_IN_FOR_TOKENS_OUT,\n BPT_IN_FOR_EXACT_TOKENS_OUT,\n REMOVE_TOKEN\n }\n\n /**\n * @dev Called by users to join a Pool, which transfers tokens from `sender` into the Pool's balance. This will\n * trigger custom Pool behavior, which will typically grant something in return to `recipient` - often tokenized\n * Pool shares.\n *\n * If the caller is not `sender`, it must be an authorized relayer for them.\n *\n * The `assets` and `maxAmountsIn` arrays must have the same length, and each entry indicates the maximum amount\n * to send for each asset. The amounts to send are decided by the Pool and not the Vault: it just enforces\n * these maximums.\n *\n * If joining a Pool that holds WETH, it is possible to send ETH directly: the Vault will do the wrapping. To enable\n * this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead of the\n * WETH address. Note that it is not possible to combine ETH and WETH in the same join. Any excess ETH will be sent\n * back to the caller (not the sender, which is important for relayers).\n *\n * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when\n * interacting with Pools that register and deregister tokens frequently. If sending ETH however, the array must be\n * sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the final\n * `assets` array might not be sorted. Pools with no registered tokens cannot be joined.\n *\n * If `fromInternalBalance` is true, the caller's Internal Balance will be preferred: ERC20 transfers will only\n * be made for the difference between the requested amount and Internal Balance (if any). Note that ETH cannot be\n * withdrawn from Internal Balance: attempting to do so will trigger a revert.\n *\n * This causes the Vault to call the `IBasePool.onJoinPool` hook on the Pool's contract, where Pools implement\n * their own custom logic. This typically requires additional information from the user (such as the expected number\n * of Pool shares). This can be encoded in the `userData` argument, which is ignored by the Vault and passed\n * directly to the Pool's contract, as is `recipient`.\n *\n * Emits a `PoolBalanceChanged` event.\n */\n function joinPool(\n bytes32 poolId,\n address sender,\n address recipient,\n JoinPoolRequest memory request\n ) external payable;\n\n struct JoinPoolRequest {\n address[] assets;\n uint256[] maxAmountsIn;\n bytes userData;\n bool fromInternalBalance;\n }\n\n /**\n * @dev Called by users to exit a Pool, which transfers tokens from the Pool's balance to `recipient`. This will\n * trigger custom Pool behavior, which will typically ask for something in return from `sender` - often tokenized\n * Pool shares. The amount of tokens that can be withdrawn is limited by the Pool's `cash` balance (see\n * `getPoolTokenInfo`).\n *\n * If the caller is not `sender`, it must be an authorized relayer for them.\n *\n * The `tokens` and `minAmountsOut` arrays must have the same length, and each entry in these indicates the minimum\n * token amount to receive for each token contract. The amounts to send are decided by the Pool and not the Vault:\n * it just enforces these minimums.\n *\n * If exiting a Pool that holds WETH, it is possible to receive ETH directly: the Vault will do the unwrapping. To\n * enable this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead\n * of the WETH address. Note that it is not possible to combine ETH and WETH in the same exit.\n *\n * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when\n * interacting with Pools that register and deregister tokens frequently. If receiving ETH however, the array must\n * be sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the\n * final `assets` array might not be sorted. Pools with no registered tokens cannot be exited.\n *\n * If `toInternalBalance` is true, the tokens will be deposited to `recipient`'s Internal Balance. Otherwise,\n * an ERC20 transfer will be performed. Note that ETH cannot be deposited to Internal Balance: attempting to\n * do so will trigger a revert.\n *\n * `minAmountsOut` is the minimum amount of tokens the user expects to get out of the Pool, for each token in the\n * `tokens` array. This array must match the Pool's registered tokens.\n *\n * This causes the Vault to call the `IBasePool.onExitPool` hook on the Pool's contract, where Pools implement\n * their own custom logic. This typically requires additional information from the user (such as the expected number\n * of Pool shares to return). This can be encoded in the `userData` argument, which is ignored by the Vault and\n * passed directly to the Pool's contract.\n *\n * Emits a `PoolBalanceChanged` event.\n */\n function exitPool(\n bytes32 poolId,\n address sender,\n address payable recipient,\n ExitPoolRequest memory request\n ) external;\n\n struct ExitPoolRequest {\n address[] assets;\n uint256[] minAmountsOut;\n bytes userData;\n bool toInternalBalance;\n }\n\n /**\n * @dev Returns a Pool's registered tokens, the total balance for each, and the latest block when *any* of\n * the tokens' `balances` changed.\n *\n * The order of the `tokens` array is the same order that will be used in `joinPool`, `exitPool`, as well as in all\n * Pool hooks (where applicable). Calls to `registerTokens` and `deregisterTokens` may change this order.\n *\n * If a Pool only registers tokens once, and these are sorted in ascending order, they will be stored in the same\n * order as passed to `registerTokens`.\n *\n * Total balances include both tokens held by the Vault and those withdrawn by the Pool's Asset Managers. These are\n * the amounts used by joins, exits and swaps. For a detailed breakdown of token balances, use `getPoolTokenInfo`\n * instead.\n */\n function getPoolTokens(bytes32 poolId)\n external\n view\n returns (\n IERC20[] memory tokens,\n uint256[] memory balances,\n uint256 lastChangeBlock\n );\n\n /**\n * @dev Performs a set of user balance operations, which involve Internal Balance (deposit, withdraw or transfer)\n * and plain ERC20 transfers using the Vault's allowance. This last feature is particularly useful for relayers, as\n * it lets integrators reuse a user's Vault allowance.\n *\n * For each operation, if the caller is not `sender`, it must be an authorized relayer for them.\n */\n function manageUserBalance(UserBalanceOp[] memory ops) external payable;\n\n struct UserBalanceOp {\n UserBalanceOpKind kind;\n address asset;\n uint256 amount;\n address sender;\n address payable recipient;\n }\n\n enum UserBalanceOpKind {\n DEPOSIT_INTERNAL,\n WITHDRAW_INTERNAL,\n TRANSFER_INTERNAL,\n TRANSFER_EXTERNAL\n }\n\n enum SwapKind {\n GIVEN_IN,\n GIVEN_OUT\n }\n\n struct SingleSwap {\n bytes32 poolId;\n SwapKind kind;\n address assetIn;\n address assetOut;\n uint256 amount;\n bytes userData;\n }\n\n struct FundManagement {\n address sender;\n bool fromInternalBalance;\n address payable recipient;\n bool toInternalBalance;\n }\n\n function swap(\n SingleSwap calldata singleSwap,\n FundManagement calldata funds,\n uint256 limit,\n uint256 deadline\n ) external returns (uint256 amountCalculated);\n\n function getPoolTokenInfo(bytes32 poolId, address token)\n external\n view\n returns (\n uint256 cash,\n uint256 managed,\n uint256 lastChangeBlock,\n address assetManager\n );\n}\n" + }, + "contracts/interfaces/balancer/IMetaStablePool.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IRateProvider } from \"./IRateProvider.sol\";\n\ninterface IMetaStablePool {\n function getRateProviders()\n external\n view\n returns (IRateProvider[] memory providers);\n}\n" + }, + "contracts/interfaces/balancer/IOracleWeightedPool.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// The three values that can be queried:\n//\n// - PAIR_PRICE: the price of the tokens in the Pool, expressed as the price of the second token in units of the\n// first token. For example, if token A is worth $2, and token B is worth $4, the pair price will be 2.0.\n// Note that the price is computed *including* the tokens decimals. This means that the pair price of a Pool with\n// DAI and USDC will be close to 1.0, despite DAI having 18 decimals and USDC 6.\n//\n// - BPT_PRICE: the price of the Pool share token (BPT), in units of the first token.\n// Note that the price is computed *including* the tokens decimals. This means that the BPT price of a Pool with\n// USDC in which BPT is worth $5 will be 5.0, despite the BPT having 18 decimals and USDC 6.\n//\n// - INVARIANT: the value of the Pool's invariant, which serves as a measure of its liquidity.\nenum Variable {\n PAIR_PRICE,\n BPT_PRICE,\n INVARIANT\n}\n\n/**\n * @dev Information for a Time Weighted Average query.\n *\n * Each query computes the average over a window of duration `secs` seconds that ended `ago` seconds ago. For\n * example, the average over the past 30 minutes is computed by settings secs to 1800 and ago to 0. If secs is 1800\n * and ago is 1800 as well, the average between 60 and 30 minutes ago is computed instead.\n */\nstruct OracleAverageQuery {\n Variable variable;\n uint256 secs;\n uint256 ago;\n}\n\ninterface IOracleWeightedPool {\n /**\n * @dev Returns the time average weighted price corresponding to each of `queries`. Prices are represented as 18\n * decimal fixed point values.\n */\n function getTimeWeightedAverage(OracleAverageQuery[] memory queries)\n external\n view\n returns (uint256[] memory results);\n}\n" + }, + "contracts/interfaces/balancer/IRateProvider.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity ^0.8.0;\n\ninterface IRateProvider {\n function getRate() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/chainlink/AggregatorV3Interface.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorV3Interface {\n function decimals() external view returns (uint8);\n\n function description() external view returns (string memory);\n\n function version() external view returns (uint256);\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(uint80 _roundId)\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n\n function latestRoundData()\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n enum BalanceProofLevel {\n Container,\n BeaconBlock\n }\n\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view returns (bool isEmptyDepositQueue);\n\n function verifyBlockNumber(\n bytes32 beaconBlockRoot,\n uint256 blockNumber,\n bytes calldata blockNumberProof\n ) external view;\n\n function verifySlot(\n bytes32 beaconBlockRoot,\n uint256 slot,\n bytes calldata slotProof\n ) external view;\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n external\n pure\n returns (uint256);\n\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) external pure returns (uint256);\n}\n" + }, + "contracts/interfaces/IBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBuyback {\n function swap() external;\n}\n" + }, + "contracts/interfaces/ICampaignRemoteManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICampaignRemoteManager {\n function createCampaign(\n CampaignCreationParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n function manageCampaign(\n CampaignManagementParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n function closeCampaign(\n CampaignClosingParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n struct CampaignCreationParams {\n uint256 chainId;\n address gauge;\n address manager;\n address rewardToken;\n uint8 numberOfPeriods;\n uint256 maxRewardPerVote;\n uint256 totalRewardAmount;\n address[] addresses;\n address hook;\n bool isWhitelist;\n }\n\n struct CampaignManagementParams {\n uint256 campaignId;\n address rewardToken;\n uint8 numberOfPeriods;\n uint256 totalRewardAmount;\n uint256 maxRewardPerVote;\n }\n\n struct CampaignClosingParams {\n uint256 campaignId;\n }\n}\n" + }, + "contracts/interfaces/IChildLiquidityGaugeFactory.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IChildLiquidityGaugeFactory {\n event DeployedGauge(\n address indexed _implementation,\n address indexed _lp_token,\n address indexed _deployer,\n bytes32 _salt,\n address _gauge\n );\n event Minted(\n address indexed _user,\n address indexed _gauge,\n uint256 _new_total\n );\n event TransferOwnership(address _old_owner, address _new_owner);\n event UpdateCallProxy(address _old_call_proxy, address _new_call_proxy);\n event UpdateImplementation(\n address _old_implementation,\n address _new_implementation\n );\n event UpdateManager(address _manager);\n event UpdateMirrored(address indexed _gauge, bool _mirrored);\n event UpdateRoot(address _factory, address _implementation);\n event UpdateVotingEscrow(\n address _old_voting_escrow,\n address _new_voting_escrow\n );\n\n function accept_transfer_ownership() external;\n\n function call_proxy() external view returns (address);\n\n function commit_transfer_ownership(address _future_owner) external;\n\n function crv() external view returns (address);\n\n function deploy_gauge(address _lp_token, bytes32 _salt)\n external\n returns (address);\n\n function deploy_gauge(\n address _lp_token,\n bytes32 _salt,\n address _manager\n ) external returns (address);\n\n function future_owner() external view returns (address);\n\n function gauge_data(address arg0) external view returns (uint256);\n\n function get_gauge(uint256 arg0) external view returns (address);\n\n function get_gauge_count() external view returns (uint256);\n\n function get_gauge_from_lp_token(address arg0)\n external\n view\n returns (address);\n\n function get_implementation() external view returns (address);\n\n function is_mirrored(address _gauge) external view returns (bool);\n\n function is_valid_gauge(address _gauge) external view returns (bool);\n\n function last_request(address _gauge) external view returns (uint256);\n\n function manager() external view returns (address);\n\n function mint(address _gauge) external;\n\n function mint_many(address[32] memory _gauges) external;\n\n function minted(address arg0, address arg1) external view returns (uint256);\n\n function owner() external view returns (address);\n\n function root_factory() external view returns (address);\n\n function root_implementation() external view returns (address);\n\n function set_call_proxy(address _new_call_proxy) external;\n\n function set_crv(address _crv) external;\n\n function set_implementation(address _implementation) external;\n\n function set_manager(address _new_manager) external;\n\n function set_mirrored(address _gauge, bool _mirrored) external;\n\n function set_root(address _factory, address _implementation) external;\n\n function set_voting_escrow(address _voting_escrow) external;\n\n function version() external view returns (string memory);\n\n function voting_escrow() external view returns (address);\n}\n" + }, + "contracts/interfaces/IComptroller.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IComptroller {\n // Claim all the COMP accrued by specific holders in specific markets for their supplies and/or borrows\n function claimComp(\n address[] memory holders,\n address[] memory cTokens,\n bool borrowers,\n bool suppliers\n ) external;\n\n function oracle() external view returns (address);\n}\n" + }, + "contracts/interfaces/IConsolidation.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IConsolidationSource {\n function confirmConsolidation()\n external\n returns (uint256 consolidationCount);\n}\n\ninterface IConsolidationTarget {\n function requestConsolidation(\n bytes32 lastSourcePubKeyHash,\n bytes32 targetPubKeyHash\n ) external;\n}\n" + }, + "contracts/interfaces/ICurveLiquidityGaugeV6.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveLiquidityGaugeV6 {\n event ApplyOwnership(address admin);\n event Approval(\n address indexed _owner,\n address indexed _spender,\n uint256 _value\n );\n event CommitOwnership(address admin);\n event Deposit(address indexed provider, uint256 value);\n event SetGaugeManager(address _gauge_manager);\n event Transfer(address indexed _from, address indexed _to, uint256 _value);\n event UpdateLiquidityLimit(\n address indexed user,\n uint256 original_balance,\n uint256 original_supply,\n uint256 working_balance,\n uint256 working_supply\n );\n event Withdraw(address indexed provider, uint256 value);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_reward(address _reward_token, address _distributor) external;\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function claim_rewards() external;\n\n function claim_rewards(address _addr) external;\n\n function claim_rewards(address _addr, address _receiver) external;\n\n function claimable_reward(address _user, address _reward_token)\n external\n view\n returns (uint256);\n\n function claimable_tokens(address addr) external returns (uint256);\n\n function claimed_reward(address _addr, address _token)\n external\n view\n returns (uint256);\n\n function decimals() external view returns (uint256);\n\n function decreaseAllowance(address _spender, uint256 _subtracted_value)\n external\n returns (bool);\n\n function deposit(uint256 _value) external;\n\n function deposit(uint256 _value, address _addr) external;\n\n function deposit(\n uint256 _value,\n address _addr,\n bool _claim_rewards\n ) external;\n\n function deposit_reward_token(address _reward_token, uint256 _amount)\n external;\n\n function deposit_reward_token(\n address _reward_token,\n uint256 _amount,\n uint256 _epoch\n ) external;\n\n function factory() external view returns (address);\n\n function future_epoch_time() external view returns (uint256);\n\n function increaseAllowance(address _spender, uint256 _added_value)\n external\n returns (bool);\n\n function inflation_rate() external view returns (uint256);\n\n function integrate_checkpoint() external view returns (uint256);\n\n function integrate_checkpoint_of(address arg0)\n external\n view\n returns (uint256);\n\n function integrate_fraction(address arg0) external view returns (uint256);\n\n function integrate_inv_supply(uint256 arg0) external view returns (uint256);\n\n function integrate_inv_supply_of(address arg0)\n external\n view\n returns (uint256);\n\n function is_killed() external view returns (bool);\n\n function kick(address addr) external;\n\n function lp_token() external view returns (address);\n\n function manager() external view returns (address);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function period() external view returns (int128);\n\n function period_timestamp(uint256 arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function reward_count() external view returns (uint256);\n\n function reward_integral_for(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function reward_tokens(uint256 arg0) external view returns (address);\n\n function rewards_receiver(address arg0) external view returns (address);\n\n function salt() external view returns (bytes32);\n\n function set_gauge_manager(address _gauge_manager) external;\n\n function set_killed(bool _is_killed) external;\n\n function set_reward_distributor(address _reward_token, address _distributor)\n external;\n\n function set_rewards_receiver(address _receiver) external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function user_checkpoint(address addr) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw(uint256 _value) external;\n\n function withdraw(uint256 _value, bool _claim_rewards) external;\n\n function working_balances(address arg0) external view returns (uint256);\n\n function working_supply() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/ICurveMinter.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveMinter {\n event Minted(address indexed recipient, address gauge, uint256 minted);\n\n function allowed_to_mint_for(address arg0, address arg1)\n external\n view\n returns (bool);\n\n function controller() external view returns (address);\n\n function mint(address gauge_addr) external;\n\n function mint_for(address gauge_addr, address _for) external;\n\n function mint_many(address[8] memory gauge_addrs) external;\n\n function minted(address arg0, address arg1) external view returns (uint256);\n\n function toggle_approve_mint(address minting_user) external;\n\n function token() external view returns (address);\n}\n" + }, + "contracts/interfaces/ICurveStableSwapNG.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveStableSwapNG {\n event AddLiquidity(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event ApplyNewFee(uint256 fee, uint256 offpeg_fee_multiplier);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event RampA(\n uint256 old_A,\n uint256 new_A,\n uint256 initial_time,\n uint256 future_time\n );\n event RemoveLiquidity(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 token_supply\n );\n event RemoveLiquidityImbalance(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event RemoveLiquidityOne(\n address indexed provider,\n int128 token_id,\n uint256 token_amount,\n uint256 coin_amount,\n uint256 token_supply\n );\n event SetNewMATime(uint256 ma_exp_time, uint256 D_ma_time);\n event StopRampA(uint256 A, uint256 t);\n event TokenExchange(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event TokenExchangeUnderlying(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event Transfer(\n address indexed sender,\n address indexed receiver,\n uint256 value\n );\n\n function A() external view returns (uint256);\n\n function A_precise() external view returns (uint256);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function D_ma_time() external view returns (uint256);\n\n function D_oracle() external view returns (uint256);\n\n function N_COINS() external view returns (uint256);\n\n function add_liquidity(uint256[] memory _amounts, uint256 _min_mint_amount)\n external\n returns (uint256);\n\n function add_liquidity(\n uint256[] memory _amounts,\n uint256 _min_mint_amount,\n address _receiver\n ) external returns (uint256);\n\n function admin_balances(uint256 arg0) external view returns (uint256);\n\n function admin_fee() external view returns (uint256);\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function balances(uint256 i) external view returns (uint256);\n\n function calc_token_amount(uint256[] memory _amounts, bool _is_deposit)\n external\n view\n returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _burn_amount, int128 i)\n external\n view\n returns (uint256);\n\n function coins(uint256 arg0) external view returns (address);\n\n function decimals() external view returns (uint8);\n\n function dynamic_fee(int128 i, int128 j) external view returns (uint256);\n\n function ema_price(uint256 i) external view returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external returns (uint256);\n\n function exchange_received(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external returns (uint256);\n\n function exchange_received(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external returns (uint256);\n\n function fee() external view returns (uint256);\n\n function future_A() external view returns (uint256);\n\n function future_A_time() external view returns (uint256);\n\n function get_balances() external view returns (uint256[] memory);\n\n function get_dx(\n int128 i,\n int128 j,\n uint256 dy\n ) external view returns (uint256);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n\n function get_p(uint256 i) external view returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function initial_A() external view returns (uint256);\n\n function initial_A_time() external view returns (uint256);\n\n function last_price(uint256 i) external view returns (uint256);\n\n function ma_exp_time() external view returns (uint256);\n\n function ma_last_time() external view returns (uint256);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function offpeg_fee_multiplier() external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function price_oracle(uint256 i) external view returns (uint256);\n\n function ramp_A(uint256 _future_A, uint256 _future_time) external;\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts\n ) external returns (uint256[] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts,\n address _receiver\n ) external returns (uint256[] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts,\n address _receiver,\n bool _claim_admin_fees\n ) external returns (uint256[] memory);\n\n function remove_liquidity_imbalance(\n uint256[] memory _amounts,\n uint256 _max_burn_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[] memory _amounts,\n uint256 _max_burn_amount,\n address _receiver\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received,\n address _receiver\n ) external returns (uint256);\n\n function salt() external view returns (bytes32);\n\n function set_ma_exp_time(uint256 _ma_exp_time, uint256 _D_ma_time) external;\n\n function set_new_fee(uint256 _new_fee, uint256 _new_offpeg_fee_multiplier)\n external;\n\n function stop_ramp_A() external;\n\n function stored_rates() external view returns (uint256[] memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw_admin_fees() external;\n}\n" + }, + "contracts/interfaces/ICurveXChainLiquidityGauge.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveXChainLiquidityGauge {\n event Approval(\n address indexed _owner,\n address indexed _spender,\n uint256 _value\n );\n event Deposit(address indexed provider, uint256 value);\n event SetGaugeManager(address _gauge_manager);\n event Transfer(address indexed _from, address indexed _to, uint256 _value);\n event UpdateLiquidityLimit(\n address indexed user,\n uint256 original_balance,\n uint256 original_supply,\n uint256 working_balance,\n uint256 working_supply\n );\n event Withdraw(address indexed provider, uint256 value);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_reward(address _reward_token, address _distributor) external;\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function claim_rewards() external;\n\n function claim_rewards(address _addr) external;\n\n function claim_rewards(address _addr, address _receiver) external;\n\n function claimable_reward(address _user, address _reward_token)\n external\n view\n returns (uint256);\n\n function claimable_tokens(address addr) external returns (uint256);\n\n function claimed_reward(address _addr, address _token)\n external\n view\n returns (uint256);\n\n function decimals() external view returns (uint256);\n\n function decreaseAllowance(address _spender, uint256 _subtracted_value)\n external\n returns (bool);\n\n function deposit(uint256 _value) external;\n\n function deposit(uint256 _value, address _addr) external;\n\n function deposit(\n uint256 _value,\n address _addr,\n bool _claim_rewards\n ) external;\n\n function deposit_reward_token(address _reward_token, uint256 _amount)\n external;\n\n function deposit_reward_token(\n address _reward_token,\n uint256 _amount,\n uint256 _epoch\n ) external;\n\n function factory() external view returns (address);\n\n function increaseAllowance(address _spender, uint256 _added_value)\n external\n returns (bool);\n\n function inflation_rate(uint256 arg0) external view returns (uint256);\n\n function initialize(\n address _lp_token,\n address _root,\n address _manager\n ) external;\n\n function integrate_checkpoint() external view returns (uint256);\n\n function integrate_checkpoint_of(address arg0)\n external\n view\n returns (uint256);\n\n function integrate_fraction(address arg0) external view returns (uint256);\n\n function integrate_inv_supply(int128 arg0) external view returns (uint256);\n\n function integrate_inv_supply_of(address arg0)\n external\n view\n returns (uint256);\n\n function is_killed() external view returns (bool);\n\n function lp_token() external view returns (address);\n\n function manager() external view returns (address);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function period() external view returns (int128);\n\n function period_timestamp(int128 arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function recover_remaining(address _reward_token) external;\n\n function reward_count() external view returns (uint256);\n\n function reward_integral_for(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function reward_remaining(address arg0) external view returns (uint256);\n\n function reward_tokens(uint256 arg0) external view returns (address);\n\n function rewards_receiver(address arg0) external view returns (address);\n\n function root_gauge() external view returns (address);\n\n function set_gauge_manager(address _gauge_manager) external;\n\n function set_killed(bool _is_killed) external;\n\n function set_manager(address _gauge_manager) external;\n\n function set_reward_distributor(address _reward_token, address _distributor)\n external;\n\n function set_rewards_receiver(address _receiver) external;\n\n function set_root_gauge(address _root) external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function update_voting_escrow() external;\n\n function user_checkpoint(address addr) external returns (bool);\n\n function version() external view returns (string memory);\n\n function voting_escrow() external view returns (address);\n\n function withdraw(uint256 _value) external;\n\n function withdraw(uint256 _value, bool _claim_rewards) external;\n\n function withdraw(\n uint256 _value,\n bool _claim_rewards,\n address _receiver\n ) external;\n\n function working_balances(address arg0) external view returns (uint256);\n\n function working_supply() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/ICVXLocker.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface ICVXLocker {\n function lock(\n address _account,\n uint256 _amount,\n uint256 _spendRatio\n ) external;\n\n function lockedBalanceOf(address _account) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/IDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IDripper {\n /// @notice How much funds have dripped out already and are currently\n // available to be sent to the vault.\n /// @return The amount that would be sent if a collect was called\n function availableFunds() external view returns (uint256);\n\n /// @notice Collect all dripped funds and send to vault.\n /// Recalculate new drip rate.\n function collect() external;\n\n /// @notice Collect all dripped funds, send to vault, recalculate new drip\n /// rate, and rebase mToken.\n function collectAndRebase() external;\n\n /// @notice Change the drip duration. Governor only.\n /// @param _durationSeconds the number of seconds to drip out the entire\n /// balance over if no collects were called during that time.\n function setDripDuration(uint256 _durationSeconds) external;\n\n /// @dev Transfer out ERC20 tokens held by the contract. Governor only.\n /// @param _asset ERC20 token address\n /// @param _amount amount to transfer\n function transferToken(address _asset, uint256 _amount) external;\n}\n" + }, + "contracts/interfaces/IEthUsdOracle.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IEthUsdOracle {\n /**\n * @notice Returns ETH price in USD.\n * @return Price in USD with 6 decimal digits.\n */\n function ethUsdPrice() external view returns (uint256);\n\n /**\n * @notice Returns token price in USD.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in USD with 6 decimal digits.\n */\n function tokUsdPrice(string calldata symbol)\n external\n view\n returns (uint256);\n\n /**\n * @notice Returns the asset price in ETH.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in ETH with 8 decimal digits.\n */\n function tokEthPrice(string calldata symbol)\n external\n view\n returns (uint256);\n}\n\ninterface IViewEthUsdOracle {\n /**\n * @notice Returns ETH price in USD.\n * @return Price in USD with 6 decimal digits.\n */\n function ethUsdPrice() external view returns (uint256);\n\n /**\n * @notice Returns token price in USD.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in USD with 6 decimal digits.\n */\n function tokUsdPrice(string calldata symbol)\n external\n view\n returns (uint256);\n\n /**\n * @notice Returns the asset price in ETH.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in ETH with 8 decimal digits.\n */\n function tokEthPrice(string calldata symbol)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/IFraxETHMinter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IFraxETHMinter {\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares);\n}\n" + }, + "contracts/interfaces/IGetExchangeRateToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IGetExchangeRateToken {\n function getExchangeRate() external view returns (uint256 _exchangeRate);\n}\n" + }, + "contracts/interfaces/IMinMaxOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IMinMaxOracle {\n //Assuming 8 decimals\n function priceMin(string calldata symbol) external view returns (uint256);\n\n function priceMax(string calldata symbol) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IMockVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"./IVault.sol\";\n\ninterface IMockVault is IVault {\n function outstandingWithdrawalsAmount() external view returns (uint256);\n\n function wethAvailable() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IOETHZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOETHZapper {\n function deposit() external payable returns (uint256);\n}\n" + }, + "contracts/interfaces/IOneInch.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/// 1Inch swap data\nstruct SwapDescription {\n IERC20 srcToken; // contract address of a token to sell\n IERC20 dstToken; // contract address of a token to buy\n address payable srcReceiver;\n address payable dstReceiver; // Receiver of destination currency. default: fromAddress\n uint256 amount;\n uint256 minReturnAmount;\n uint256 flags;\n}\n\n/// @title Interface for making arbitrary calls during swap\ninterface IAggregationExecutor {\n /// @notice propagates information about original msg.sender and executes arbitrary data\n function execute(address msgSender) external payable; // 0x4b64e492\n}\n\ninterface IOneInchRouter {\n /// @notice Performs a swap, delegating all calls encoded in `data` to `executor`.\n function swap(\n IAggregationExecutor executor,\n SwapDescription calldata desc,\n bytes calldata permit,\n bytes calldata data\n ) external returns (uint256 returnAmount, uint256 spentAmount);\n\n /// @notice Performs swap using Uniswap exchange. Wraps and unwraps ETH if required.\n function unoswapTo(\n address payable recipient,\n IERC20 srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) external payable returns (uint256 returnAmount);\n\n /// @notice Performs swap using Uniswap V3 exchange. Wraps and unwraps ETH if required.\n function uniswapV3SwapTo(\n address payable recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) external payable returns (uint256 returnAmount);\n}\n" + }, + "contracts/interfaces/IOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOracle {\n /**\n * @dev returns the asset price in USD, in 8 decimal digits.\n *\n * The version of priceProvider deployed for OETH has 18 decimal digits\n */\n function price(address asset) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IOUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOUSD {\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n function _totalSupply() external view returns (uint256);\n\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address _account) external view returns (uint256);\n\n function burn(address account, uint256 amount) external;\n\n function changeSupply(uint256 _newTotalSupply) external;\n\n function claimGovernance() external;\n\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256);\n\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n );\n\n function decimals() external view returns (uint8);\n\n function decreaseAllowance(address _spender, uint256 _subtractedValue)\n external\n returns (bool);\n\n function governor() external view returns (address);\n\n function increaseAllowance(address _spender, uint256 _addedValue)\n external\n returns (bool);\n\n function initialize(\n string memory _nameArg,\n string memory _symbolArg,\n address _vaultAddress\n ) external;\n\n function isGovernor() external view returns (bool);\n\n function isUpgraded(address) external view returns (uint256);\n\n function mint(address _account, uint256 _amount) external;\n\n function name() external view returns (string memory);\n\n function nonRebasingCreditsPerToken(address)\n external\n view\n returns (uint256);\n\n function nonRebasingSupply() external view returns (uint256);\n\n function rebaseOptIn() external;\n\n function rebaseOptOut() external;\n\n function rebaseState(address) external view returns (uint8);\n\n function rebasingCredits() external view returns (uint256);\n\n function rebasingCreditsHighres() external view returns (uint256);\n\n function rebasingCreditsPerToken() external view returns (uint256);\n\n function rebasingCreditsPerTokenHighres() external view returns (uint256);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function transferGovernance(address _newGovernor) external;\n\n function vaultAddress() external view returns (address);\n}\n" + }, + "contracts/interfaces/IPriceOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPriceOracle {\n /**\n * @dev returns the asset price in USD, 6 decimal digits.\n * Compatible with the Open Price Feed.\n */\n function price(string calldata symbol) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IRETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IRETH {\n function getEthValue(uint256 _rethAmount) external view returns (uint256);\n\n function getRethValue(uint256 _ethAmount) external view returns (uint256);\n\n function getExchangeRate() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function balanceOf(address account) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 amount) external returns (bool);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/ISafe.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ISafe {\n function execTransactionFromModule(\n address,\n uint256,\n bytes memory,\n uint8\n ) external returns (bool);\n}\n" + }, + "contracts/interfaces/ISfrxETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface ISfrxETH {\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 amount\n );\n event Deposit(\n address indexed caller,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n event NewRewardsCycle(uint32 indexed cycleEnd, uint256 rewardAmount);\n event Transfer(address indexed from, address indexed to, uint256 amount);\n event Withdraw(\n address indexed caller,\n address indexed receiver,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address spender, uint256 amount) external returns (bool);\n\n function asset() external view returns (address);\n\n function balanceOf(address) external view returns (uint256);\n\n function convertToAssets(uint256 shares) external view returns (uint256);\n\n function convertToShares(uint256 assets) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit(uint256 assets, address receiver)\n external\n returns (uint256 shares);\n\n function depositWithSignature(\n uint256 assets,\n address receiver,\n uint256 deadline,\n bool approveMax,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external returns (uint256 shares);\n\n function lastRewardAmount() external view returns (uint192);\n\n function lastSync() external view returns (uint32);\n\n function maxDeposit(address) external view returns (uint256);\n\n function maxMint(address) external view returns (uint256);\n\n function maxRedeem(address owner) external view returns (uint256);\n\n function maxWithdraw(address owner) external view returns (uint256);\n\n function mint(uint256 shares, address receiver)\n external\n returns (uint256 assets);\n\n function name() external view returns (string memory);\n\n function nonces(address) external view returns (uint256);\n\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function previewDeposit(uint256 assets) external view returns (uint256);\n\n function previewMint(uint256 shares) external view returns (uint256);\n\n function previewRedeem(uint256 shares) external view returns (uint256);\n\n function previewWithdraw(uint256 assets) external view returns (uint256);\n\n function pricePerShare() external view returns (uint256);\n\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets);\n\n function rewardsCycleEnd() external view returns (uint32);\n\n function rewardsCycleLength() external view returns (uint32);\n\n function symbol() external view returns (string memory);\n\n function syncRewards() external;\n\n function totalAssets() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address to, uint256 amount) external returns (bool);\n\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/ISwapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ISwapper {\n /**\n * @param fromAsset The token address of the asset being sold.\n * @param toAsset The token address of the asset being purchased.\n * @param fromAssetAmount The amount of assets being sold.\n * @param minToAssetAmmount The minimum amount of assets to be purchased.\n * @param data tx.data returned from 1Inch's /v5.0/1/swap API\n */\n function swap(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n}\n" + }, + "contracts/interfaces/ITimelock.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ITimelock {\n function delay() external view returns (uint256);\n\n function GRACE_PERIOD() external view returns (uint256);\n\n function acceptAdmin() external;\n\n function queuedTransactions(bytes32 hash) external view returns (bool);\n\n function queueTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external returns (bytes32);\n\n function cancelTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external;\n\n function executeTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external payable returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ITimelockController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ITimelockController {\n function grantRole(bytes32 role, address account) external;\n\n function revokeRole(bytes32 role, address account) external;\n\n function renounceRole(bytes32 role, address account) external;\n\n function hasRole(bytes32 role, address account)\n external\n view\n returns (bool);\n\n function executeBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt\n ) external payable;\n\n function scheduleBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt,\n uint256 delay\n ) external;\n\n function hashOperationBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt\n ) external view returns (bytes32);\n\n function isOperationDone(bytes32 opHash) external view returns (bool);\n\n function isOperationReady(bytes32 opHash) external view returns (bool);\n\n function isOperation(bytes32 opHash) external view returns (bool);\n\n function getMinDelay() external view returns (uint256);\n\n function updateDelay(uint256 newDelay) external;\n\n function CANCELLER_ROLE() external view returns (bytes32);\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/interfaces/IWstETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWstETH {\n /**\n * @notice Get amount of wstETH for a given amount of stETH\n * @param _stETHAmount amount of stETH\n * @return Amount of wstETH for a given stETH amount\n */\n function getWstETHByStETH(uint256 _stETHAmount)\n external\n view\n returns (uint256);\n\n /**\n * @notice Get amount of stETH for a given amount of wstETH\n * @param _wstETHAmount amount of wstETH\n * @return Amount of stETH for a given wstETH amount\n */\n function getStETHByWstETH(uint256 _wstETHAmount)\n external\n view\n returns (uint256);\n\n /**\n * @notice Get amount of stETH for a one wstETH\n * @return Amount of stETH for 1 wstETH\n */\n function stEthPerToken() external view returns (uint256);\n\n /**\n * @notice Get amount of wstETH for a one stETH\n * @return Amount of wstETH for a 1 stETH\n */\n function tokensPerStEth() external view returns (uint256);\n\n /**\n * @notice Exchanges stETH to wstETH\n * @param _stETHAmount amount of stETH to wrap in exchange for wstETH\n * @dev Requirements:\n * - `_stETHAmount` must be non-zero\n * - msg.sender must approve at least `_stETHAmount` stETH to this\n * contract.\n * - msg.sender must have at least `_stETHAmount` of stETH.\n * User should first approve _stETHAmount to the WstETH contract\n * @return Amount of wstETH user receives after wrap\n */\n function wrap(uint256 _stETHAmount) external returns (uint256);\n\n /**\n * @notice Exchanges wstETH to stETH\n * @param _wstETHAmount amount of wstETH to uwrap in exchange for stETH\n * @dev Requirements:\n * - `_wstETHAmount` must be non-zero\n * - msg.sender must have at least `_wstETHAmount` wstETH.\n * @return Amount of stETH user receives after unwrap\n */\n function unwrap(uint256 _wstETHAmount) external returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/compound/ICompoundOracle.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\ninterface ICompoundOracle {\n function getUnderlyingPrice(address) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/ILens.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\nimport \"./compound/ICompoundOracle.sol\";\nimport \"./IMorpho.sol\";\n\ninterface ILens {\n /// STORAGE ///\n\n function MAX_BASIS_POINTS() external view returns (uint256);\n\n function WAD() external view returns (uint256);\n\n function morpho() external view returns (IMorpho);\n\n function comptroller() external view returns (IComptroller);\n\n /// GENERAL ///\n\n function getTotalSupply()\n external\n view\n returns (\n uint256 p2pSupplyAmount,\n uint256 poolSupplyAmount,\n uint256 totalSupplyAmount\n );\n\n function getTotalBorrow()\n external\n view\n returns (\n uint256 p2pBorrowAmount,\n uint256 poolBorrowAmount,\n uint256 totalBorrowAmount\n );\n\n /// MARKETS ///\n\n function isMarketCreated(address _poolToken) external view returns (bool);\n\n function isMarketCreatedAndNotPaused(address _poolToken)\n external\n view\n returns (bool);\n\n function isMarketCreatedAndNotPausedNorPartiallyPaused(address _poolToken)\n external\n view\n returns (bool);\n\n function getAllMarkets()\n external\n view\n returns (address[] memory marketsCreated_);\n\n function getMainMarketData(address _poolToken)\n external\n view\n returns (\n uint256 avgSupplyRatePerBlock,\n uint256 avgBorrowRatePerBlock,\n uint256 p2pSupplyAmount,\n uint256 p2pBorrowAmount,\n uint256 poolSupplyAmount,\n uint256 poolBorrowAmount\n );\n\n function getAdvancedMarketData(address _poolToken)\n external\n view\n returns (\n uint256 p2pSupplyIndex,\n uint256 p2pBorrowIndex,\n uint256 poolSupplyIndex,\n uint256 poolBorrowIndex,\n uint32 lastUpdateBlockNumber,\n uint256 p2pSupplyDelta,\n uint256 p2pBorrowDelta\n );\n\n function getMarketConfiguration(address _poolToken)\n external\n view\n returns (\n address underlying,\n bool isCreated,\n bool p2pDisabled,\n bool isPaused,\n bool isPartiallyPaused,\n uint16 reserveFactor,\n uint16 p2pIndexCursor,\n uint256 collateralFactor\n );\n\n function getTotalMarketSupply(address _poolToken)\n external\n view\n returns (uint256 p2pSupplyAmount, uint256 poolSupplyAmount);\n\n function getTotalMarketBorrow(address _poolToken)\n external\n view\n returns (uint256 p2pBorrowAmount, uint256 poolBorrowAmount);\n\n /// INDEXES ///\n\n function getCurrentP2PSupplyIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentP2PBorrowIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentPoolIndexes(address _poolToken)\n external\n view\n returns (\n uint256 currentPoolSupplyIndex,\n uint256 currentPoolBorrowIndex\n );\n\n function getIndexes(address _poolToken, bool _computeUpdatedIndexes)\n external\n view\n returns (\n uint256 p2pSupplyIndex,\n uint256 p2pBorrowIndex,\n uint256 poolSupplyIndex,\n uint256 poolBorrowIndex\n );\n\n /// USERS ///\n\n function getEnteredMarkets(address _user)\n external\n view\n returns (address[] memory enteredMarkets);\n\n function getUserHealthFactor(\n address _user,\n address[] calldata _updatedMarkets\n ) external view returns (uint256);\n\n function getUserBalanceStates(\n address _user,\n address[] calldata _updatedMarkets\n )\n external\n view\n returns (\n uint256 collateralValue,\n uint256 debtValue,\n uint256 maxDebtValue\n );\n\n function getCurrentSupplyBalanceInOf(address _poolToken, address _user)\n external\n view\n returns (\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getCurrentBorrowBalanceInOf(address _poolToken, address _user)\n external\n view\n returns (\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getUserMaxCapacitiesForAsset(address _user, address _poolToken)\n external\n view\n returns (uint256 withdrawable, uint256 borrowable);\n\n function getUserHypotheticalBalanceStates(\n address _user,\n address _poolToken,\n uint256 _withdrawnAmount,\n uint256 _borrowedAmount\n ) external view returns (uint256 debtValue, uint256 maxDebtValue);\n\n function getUserLiquidityDataForAsset(\n address _user,\n address _poolToken,\n bool _computeUpdatedIndexes,\n ICompoundOracle _oracle\n ) external view returns (Types.AssetLiquidityData memory assetData);\n\n function isLiquidatable(address _user, address[] memory _updatedMarkets)\n external\n view\n returns (bool);\n\n function computeLiquidationRepayAmount(\n address _user,\n address _poolTokenBorrowed,\n address _poolTokenCollateral,\n address[] calldata _updatedMarkets\n ) external view returns (uint256 toRepay);\n\n /// RATES ///\n\n function getAverageSupplyRatePerBlock(address _poolToken)\n external\n view\n returns (\n uint256 avgSupplyRatePerBlock,\n uint256 p2pSupplyAmount,\n uint256 poolSupplyAmount\n );\n\n function getAverageBorrowRatePerBlock(address _poolToken)\n external\n view\n returns (\n uint256 avgBorrowRatePerBlock,\n uint256 p2pBorrowAmount,\n uint256 poolBorrowAmount\n );\n\n function getNextUserSupplyRatePerBlock(\n address _poolToken,\n address _user,\n uint256 _amount\n )\n external\n view\n returns (\n uint256 nextSupplyRatePerBlock,\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getNextUserBorrowRatePerBlock(\n address _poolToken,\n address _user,\n uint256 _amount\n )\n external\n view\n returns (\n uint256 nextBorrowRatePerBlock,\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getCurrentUserSupplyRatePerBlock(address _poolToken, address _user)\n external\n view\n returns (uint256);\n\n function getCurrentUserBorrowRatePerBlock(address _poolToken, address _user)\n external\n view\n returns (uint256);\n\n function getRatesPerBlock(address _poolToken)\n external\n view\n returns (\n uint256 p2pSupplyRate,\n uint256 p2pBorrowRate,\n uint256 poolSupplyRate,\n uint256 poolBorrowRate\n );\n\n /// REWARDS ///\n\n function getUserUnclaimedRewards(\n address[] calldata _poolTokens,\n address _user\n ) external view returns (uint256 unclaimedRewards);\n\n function getAccruedSupplierComp(\n address _supplier,\n address _poolToken,\n uint256 _balance\n ) external view returns (uint256);\n\n function getAccruedBorrowerComp(\n address _borrower,\n address _poolToken,\n uint256 _balance\n ) external view returns (uint256);\n\n function getCurrentCompSupplyIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentCompBorrowIndex(address _poolToken)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/IMorpho.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\nimport \"./Types.sol\";\nimport \"../IComptroller.sol\";\nimport \"./compound/ICompoundOracle.sol\";\n\n// prettier-ignore\ninterface IMorpho {\n function comptroller() external view returns (IComptroller);\n function supply(address _poolTokenAddress, address _onBehalf, uint256 _amount) external;\n function supply(address _poolTokenAddress, address _onBehalf, uint256 _amount, uint256 _maxGasForMatching) external;\n function withdraw(address _poolTokenAddress, uint256 _amount) external;\n function claimRewards(\n address[] calldata _cTokenAddresses,\n bool _tradeForMorphoToken\n ) external returns (uint256 claimedAmount);\n}\n" + }, + "contracts/interfaces/morpho/Types.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\n/// @title Types.\n/// @author Morpho Labs.\n/// @custom:contact security@morpho.xyz\n/// @dev Common types and structs used in Moprho contracts.\nlibrary Types {\n /// ENUMS ///\n\n enum PositionType {\n SUPPLIERS_IN_P2P,\n SUPPLIERS_ON_POOL,\n BORROWERS_IN_P2P,\n BORROWERS_ON_POOL\n }\n\n /// STRUCTS ///\n\n struct SupplyBalance {\n uint256 inP2P; // In supplier's peer-to-peer unit, a unit that grows in underlying value, to keep track of the interests earned by suppliers in peer-to-peer. Multiply by the peer-to-peer supply index to get the underlying amount.\n uint256 onPool; // In cToken. Multiply by the pool supply index to get the underlying amount.\n }\n\n struct BorrowBalance {\n uint256 inP2P; // In borrower's peer-to-peer unit, a unit that grows in underlying value, to keep track of the interests paid by borrowers in peer-to-peer. Multiply by the peer-to-peer borrow index to get the underlying amount.\n uint256 onPool; // In cdUnit, a unit that grows in value, to keep track of the debt increase when borrowers are on Compound. Multiply by the pool borrow index to get the underlying amount.\n }\n\n // Max gas to consume during the matching process for supply, borrow, withdraw and repay functions.\n struct MaxGasForMatching {\n uint64 supply;\n uint64 borrow;\n uint64 withdraw;\n uint64 repay;\n }\n\n struct Delta {\n uint256 p2pSupplyDelta; // Difference between the stored peer-to-peer supply amount and the real peer-to-peer supply amount (in pool supply unit).\n uint256 p2pBorrowDelta; // Difference between the stored peer-to-peer borrow amount and the real peer-to-peer borrow amount (in pool borrow unit).\n uint256 p2pSupplyAmount; // Sum of all stored peer-to-peer supply (in peer-to-peer supply unit).\n uint256 p2pBorrowAmount; // Sum of all stored peer-to-peer borrow (in peer-to-peer borrow unit).\n }\n\n struct AssetLiquidityData {\n uint256 collateralValue; // The collateral value of the asset.\n uint256 maxDebtValue; // The maximum possible debt value of the asset.\n uint256 debtValue; // The debt value of the asset.\n uint256 underlyingPrice; // The price of the token.\n uint256 collateralFactor; // The liquidation threshold applied on this token.\n }\n\n struct LiquidityData {\n uint256 collateralValue; // The collateral value.\n uint256 maxDebtValue; // The maximum debt value possible.\n uint256 debtValue; // The debt value.\n }\n\n // Variables are packed together to save gas (will not exceed their limit during Morpho's lifetime).\n struct LastPoolIndexes {\n uint32 lastUpdateBlockNumber; // The last time the peer-to-peer indexes were updated.\n uint112 lastSupplyPoolIndex; // Last pool supply index.\n uint112 lastBorrowPoolIndex; // Last pool borrow index.\n }\n\n struct MarketParameters {\n uint16 reserveFactor; // Proportion of the interest earned by users sent to the DAO for each market, in basis point (100% = 10 000). The value is set at market creation.\n uint16 p2pIndexCursor; // Position of the peer-to-peer rate in the pool's spread. Determine the weights of the weighted arithmetic average in the indexes computations ((1 - p2pIndexCursor) * r^S + p2pIndexCursor * r^B) (in basis point).\n }\n\n struct MarketStatus {\n bool isCreated; // Whether or not this market is created.\n bool isPaused; // Whether the market is paused or not (all entry points on Morpho are frozen; supply, borrow, withdraw, repay and liquidate).\n bool isPartiallyPaused; // Whether the market is partially paused or not (only supply and borrow are frozen).\n }\n}\n" + }, + "contracts/interfaces/plume/IFeeRegistry.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\ninterface IFeeRegistry {\n function registerFee(\n bool isTokenA,\n uint32 binId,\n uint256 binFeeInQuote\n ) external;\n}\n" + }, + "contracts/interfaces/plume/ILiquidityRegistry.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface ILiquidityRegistry {\n function notifyBinLiquidity(\n IMaverickV2Pool pool,\n uint256 tokenId,\n uint32 binId,\n uint256 currentBinLpBalance\n ) external;\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Factory.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IFeeRegistry } from \"./IFeeRegistry.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2Factory {\n error FactorAlreadyInitialized();\n error FactorNotInitialized();\n error FactoryInvalidTokenOrder(IERC20 _tokenA, IERC20 _tokenB);\n error FactoryInvalidFee();\n error FactoryInvalidKinds(uint8 kinds);\n error FactoryInvalidTickSpacing(uint256 tickSpacing);\n error FactoryInvalidLookback(uint256 lookback);\n error FactoryInvalidTokenDecimals(uint8 decimalsA, uint8 decimalsB);\n error FactoryPoolAlreadyExists(\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds,\n address accessor\n );\n error FactoryAccessorMustBeNonZero();\n error NotImplemented();\n\n event PoolCreated(\n IMaverickV2Pool poolAddress,\n uint8 protocolFeeRatio,\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n int32 activeTick,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds,\n address accessor\n );\n event SetFactoryProtocolFeeReceiver(address receiver);\n event SetFactoryProtocolFeeRegistry(IFeeRegistry registry);\n\n struct DeployParameters {\n uint64 feeAIn;\n uint64 feeBIn;\n uint32 lookback;\n int32 activeTick;\n uint64 tokenAScale;\n uint64 tokenBScale;\n // slot\n IERC20 tokenA;\n // slot\n IERC20 tokenB;\n // slot\n uint16 tickSpacing;\n uint8 options;\n address accessor;\n }\n\n /**\n * @notice Called by deployer library to initialize a pool.\n */\n function deployParameters()\n external\n view\n returns (\n uint64 feeAIn,\n uint64 feeBIn,\n uint32 lookback,\n int32 activeTick,\n uint64 tokenAScale,\n uint64 tokenBScale,\n // slot\n IERC20 tokenA,\n // slot\n IERC20 tokenB,\n // slot\n uint16 tickSpacing,\n uint8 options,\n address accessor\n );\n\n /**\n * @notice Create a new MaverickV2Pool with symmetric swap fees.\n * @param fee Fraction of the pool swap amount that is retained as an LP in\n * D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * E.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function create(\n uint64 fee,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external returns (IMaverickV2Pool);\n\n /**\n * @notice Create a new MaverickV2Pool.\n * @param feeAIn Fraction of the pool swap amount for tokenA-input swaps\n * that is retained as an LP in D18 scale.\n * @param feeBIn Fraction of the pool swap amount for tokenB-input swaps\n * that is retained as an LP in D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * e.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function create(\n uint64 feeAIn,\n uint64 feeBIn,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external returns (IMaverickV2Pool);\n\n /**\n * @notice Bool indicating whether the pool was deployed from this factory.\n */\n function isFactoryPool(IMaverickV2Pool pool) external view returns (bool);\n\n /**\n * @notice Address that receives the protocol fee\n */\n function protocolFeeReceiver() external view returns (address);\n\n /**\n * @notice Address notified on swaps of the protocol fee\n */\n function protocolFeeRegistry() external view returns (IFeeRegistry);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds\n ) external view returns (IMaverickV2Pool);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(\n IERC20 _tokenA,\n IERC20 _tokenB,\n uint256 startIndex,\n uint256 endIndex\n ) external view returns (IMaverickV2Pool[] memory pools);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(uint256 startIndex, uint256 endIndex)\n external\n view\n returns (IMaverickV2Pool[] memory pools);\n\n /**\n * @notice Count of permissionless pools.\n */\n function poolCount() external view returns (uint256 _poolCount);\n\n /**\n * @notice Count of pools for a given accessor and token pair. For\n * permissionless pools, pass `accessor = address(0)`.\n */\n function poolByTokenCount(\n IERC20 _tokenA,\n IERC20 _tokenB,\n address accessor\n ) external view returns (uint256 _poolCount);\n\n /**\n * @notice Get the current factory owner.\n */\n function owner() external view returns (address);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2LiquidityManager.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\nimport { IMaverickV2Position } from \"./IMaverickV2Position.sol\";\nimport { IMaverickV2PoolLens } from \"./IMaverickV2PoolLens.sol\";\n\ninterface IMaverickV2LiquidityManager {\n error LiquidityManagerNotFactoryPool();\n error LiquidityManagerNotTokenIdOwner();\n\n /**\n * @notice Maverick V2 NFT position contract that tracks NFT-based\n * liquditiy positions.\n */\n function position() external view returns (IMaverickV2Position);\n\n /**\n * @notice Create Maverick V2 pool. Function is a pass through to the pool\n * factory and is provided here so that is can be assembled as part of a\n * multicall transaction.\n */\n function createPool(\n uint64 fee,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external payable returns (IMaverickV2Pool pool);\n\n /**\n * @notice Add Liquidity position NFT for msg.sender by specifying\n * msg.sender's token index.\n * @dev Token index is different from tokenId.\n * On the Position NFT contract a user can own multiple NFT tokenIds and\n * these are indexes by an enumeration index which is the `index` input\n * here.\n *\n * See addLiquidity for a description of the add params.\n */\n function addPositionLiquidityToSenderByTokenIndex(\n IMaverickV2Pool pool,\n uint256 index,\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs\n )\n external\n payable\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds\n );\n\n /**\n * @notice Mint new tokenId in the Position NFt contract to msg.sender.\n * Both mints an NFT and adds liquidity to the pool that is held by the\n * NFT.\n */\n function mintPositionNftToSender(\n IMaverickV2Pool pool,\n bytes calldata packedSqrtPriceBreaks,\n bytes[] calldata packedArgs\n )\n external\n payable\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds,\n uint256 tokenId\n );\n\n /**\n * @notice Donates liqudity to a pool that is held by the position contract\n * and will never be retrievable. Can be used to start a pool and ensure\n * there will always be a base level of liquditiy in the pool.\n */\n function donateLiquidity(\n IMaverickV2Pool pool,\n IMaverickV2Pool.AddLiquidityParams memory args\n ) external payable;\n\n /**\n * @notice Packs sqrtPrice breaks array with this format: [length,\n * array[0], array[1],..., array[length-1]] where length is 1 byte.\n */\n function packUint88Array(uint88[] memory fullArray)\n external\n pure\n returns (bytes memory packedArray);\n\n /**\n * @notice Packs addLiquidity paramters array element-wise.\n */\n function packAddLiquidityArgsArray(\n IMaverickV2Pool.AddLiquidityParams[] memory args\n ) external pure returns (bytes[] memory argsPacked);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Pool.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\n\ninterface IMaverickV2Pool {\n error PoolZeroLiquidityAdded();\n error PoolMinimumLiquidityNotMet();\n error PoolLocked();\n error PoolInvalidFee();\n error PoolTicksNotSorted(uint256 index, int256 previousTick, int256 tick);\n error PoolTicksAmountsLengthMismatch(\n uint256 ticksLength,\n uint256 amountsLength\n );\n error PoolBinIdsAmountsLengthMismatch(\n uint256 binIdsLength,\n uint256 amountsLength\n );\n error PoolKindNotSupported(uint256 kinds, uint256 kind);\n error PoolInsufficientBalance(\n uint256 deltaLpAmount,\n uint256 accountBalance\n );\n error PoolReservesExceedMaximum(uint256 amount);\n error PoolValueExceedsBits(uint256 amount, uint256 bits);\n error PoolTickMaxExceeded(uint256 tick);\n error PoolMigrateBinFirst();\n error PoolCurrentTickBeyondSwapLimit(int32 startingTick);\n error PoolSenderNotAccessor(address sender_, address accessor);\n error PoolSenderNotFactory(address sender_, address accessor);\n error PoolFunctionNotImplemented();\n error PoolTokenNotSolvent(\n uint256 internalReserve,\n uint256 tokenBalance,\n IERC20 token\n );\n error PoolNoProtocolFeeReceiverSet();\n\n event PoolSwap(\n address sender,\n address recipient,\n SwapParams params,\n uint256 amountIn,\n uint256 amountOut\n );\n event PoolFlashLoan(\n address sender,\n address recipient,\n uint256 amountA,\n uint256 amountB\n );\n event PoolProtocolFeeCollected(IERC20 token, uint256 protocolFee);\n\n event PoolAddLiquidity(\n address sender,\n address recipient,\n uint256 subaccount,\n AddLiquidityParams params,\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] binIds\n );\n\n event PoolMigrateBinsUpStack(\n address sender,\n uint32 binId,\n uint32 maxRecursion\n );\n\n event PoolRemoveLiquidity(\n address sender,\n address recipient,\n uint256 subaccount,\n RemoveLiquidityParams params,\n uint256 tokenAOut,\n uint256 tokenBOut\n );\n\n event PoolTickState(int32 tick, uint256 reserveA, uint256 reserveB);\n event PoolTickBinUpdate(int32 tick, uint8 kind, uint32 binId);\n event PoolSqrtPrice(uint256 sqrtPrice);\n\n /**\n * @notice Tick state parameters.\n */\n struct TickState {\n uint128 reserveA;\n uint128 reserveB;\n uint128 totalSupply;\n uint32[4] binIdsByTick;\n }\n\n /**\n * @notice Tick data parameters.\n * @param currentReserveA Current reserve of token A.\n * @param currentReserveB Current reserve of token B.\n * @param currentLiquidity Current liquidity amount.\n */\n struct TickData {\n uint256 currentReserveA;\n uint256 currentReserveB;\n uint256 currentLiquidity;\n }\n\n /**\n * @notice Bin state parameters.\n * @param mergeBinBalance LP token balance that this bin possesses of the merge bin.\n * @param mergeId Bin ID of the bin that this bin has merged into.\n * @param totalSupply Total amount of LP tokens in this bin.\n * @param kind One of the 4 kinds (0=static, 1=right, 2=left, 3=both).\n * @param tick The lower price tick of the bin in its current state.\n * @param tickBalance Balance of the tick.\n */\n struct BinState {\n uint128 mergeBinBalance;\n uint128 tickBalance;\n uint128 totalSupply;\n uint8 kind;\n int32 tick;\n uint32 mergeId;\n }\n\n /**\n * @notice Parameters for swap.\n * @param amount Amount of the token that is either the input if exactOutput is false\n * or the output if exactOutput is true.\n * @param tokenAIn Boolean indicating whether tokenA is the input.\n * @param exactOutput Boolean indicating whether the amount specified is\n * the exact output amount (true).\n * @param tickLimit The furthest tick a swap will execute in. If no limit\n * is desired, value should be set to type(int32).max for a tokenAIn swap\n * and type(int32).min for a swap where tokenB is the input.\n */\n struct SwapParams {\n uint256 amount;\n bool tokenAIn;\n bool exactOutput;\n int32 tickLimit;\n }\n\n /**\n * @notice Parameters associated with adding liquidity.\n * @param kind One of the 4 kinds (0=static, 1=right, 2=left, 3=both).\n * @param ticks Array of ticks to add liquidity to.\n * @param amounts Array of bin LP amounts to add.\n */\n struct AddLiquidityParams {\n uint8 kind;\n int32[] ticks;\n uint128[] amounts;\n }\n\n /**\n * @notice Parameters for each bin that will have liquidity removed.\n * @param binIds Index array of the bins losing liquidity.\n * @param amounts Array of bin LP amounts to remove.\n */\n struct RemoveLiquidityParams {\n uint32[] binIds;\n uint128[] amounts;\n }\n\n /**\n * @notice State of the pool.\n * @param reserveA Pool tokenA balanceOf at end of last operation\n * @param reserveB Pool tokenB balanceOf at end of last operation\n * @param lastTwaD8 Value of log time weighted average price at last block.\n * Value is 8-decimal scale and is in the fractional tick domain. E.g. a\n * value of 12.3e8 indicates the TWAP was 3/10ths of the way into the 12th\n * tick.\n * @param lastLogPriceD8 Value of log price at last block. Value is\n * 8-decimal scale and is in the fractional tick domain. E.g. a value of\n * 12.3e8 indicates the price was 3/10ths of the way into the 12th tick.\n * @param lastTimestamp Last block.timestamp value in seconds for latest\n * swap transaction.\n * @param activeTick Current tick position that contains the active bins.\n * @param isLocked Pool isLocked, E.g., locked or unlocked; isLocked values\n * defined in Pool.sol.\n * @param binCounter Index of the last bin created.\n * @param protocolFeeRatioD3 Ratio of the swap fee that is kept for the\n * protocol.\n */\n struct State {\n uint128 reserveA;\n uint128 reserveB;\n int64 lastTwaD8;\n int64 lastLogPriceD8;\n uint40 lastTimestamp;\n int32 activeTick;\n bool isLocked;\n uint32 binCounter;\n uint8 protocolFeeRatioD3;\n }\n\n /**\n * @notice Internal data used for data passing between Pool and Bin code.\n */\n struct BinDelta {\n uint128 deltaA;\n uint128 deltaB;\n }\n\n /**\n * @notice 1-15 number to represent the active kinds.\n * @notice 0b0001 = static;\n * @notice 0b0010 = right;\n * @notice 0b0100 = left;\n * @notice 0b1000 = both;\n *\n * E.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function kinds() external view returns (uint8 _kinds);\n\n /**\n * @notice Pool swap fee for the given direction (A-in or B-in swap) in\n * 18-decimal format. E.g. 0.01e18 is a 1% swap fee.\n */\n function fee(bool tokenAIn) external view returns (uint256);\n\n /**\n * @notice TickSpacing of pool where 1.0001^tickSpacing is the bin width.\n */\n function tickSpacing() external view returns (uint256);\n\n /**\n * @notice Lookback period of pool in seconds.\n */\n function lookback() external view returns (uint256);\n\n /**\n * @notice Address of Pool accessor. This is Zero address for\n * permissionless pools.\n */\n function accessor() external view returns (address);\n\n /**\n * @notice Pool tokenA. Address of tokenA is such that tokenA < tokenB.\n */\n function tokenA() external view returns (IERC20);\n\n /**\n * @notice Pool tokenB.\n */\n function tokenB() external view returns (IERC20);\n\n /**\n * @notice Deploying factory of the pool and also contract that has ability\n * to set and collect protocol fees for the pool.\n */\n function factory() external view returns (IMaverickV2Factory);\n\n /**\n * @notice Most significant bit of scale value is a flag to indicate whether\n * tokenA has more or less than 18 decimals. Scale is used in conjuction\n * with Math.toScale/Math.fromScale functions to convert from token amounts\n * to D18 scale internal pool accounting.\n */\n function tokenAScale() external view returns (uint256);\n\n /**\n * @notice Most significant bit of scale value is a flag to indicate whether\n * tokenA has more or less than 18 decimals. Scale is used in conjuction\n * with Math.toScale/Math.fromScale functions to convert from token amounts\n * to D18 scale internal pool accounting.\n */\n function tokenBScale() external view returns (uint256);\n\n /**\n * @notice ID of bin at input tick position and kind.\n */\n function binIdByTickKind(int32 tick, uint256 kind)\n external\n view\n returns (uint32);\n\n /**\n * @notice Accumulated tokenA protocol fee.\n */\n function protocolFeeA() external view returns (uint128);\n\n /**\n * @notice Accumulated tokenB protocol fee.\n */\n function protocolFeeB() external view returns (uint128);\n\n /**\n * @notice Lending fee rate on flash loans.\n */\n function lendingFeeRateD18() external view returns (uint256);\n\n /**\n * @notice External function to get the current time-weighted average price.\n */\n function getCurrentTwa() external view returns (int256);\n\n /**\n * @notice External function to get the state of the pool.\n */\n function getState() external view returns (State memory);\n\n /**\n * @notice Return state of Bin at input binId.\n */\n function getBin(uint32 binId) external view returns (BinState memory bin);\n\n /**\n * @notice Return state of Tick at input tick position.\n */\n function getTick(int32 tick)\n external\n view\n returns (TickState memory tickState);\n\n /**\n * @notice Retrieves the balance of a user within a bin.\n * @param user The user's address.\n * @param subaccount The subaccount for the user.\n * @param binId The ID of the bin.\n */\n function balanceOf(\n address user,\n uint256 subaccount,\n uint32 binId\n ) external view returns (uint128 lpToken);\n\n /**\n * @notice Add liquidity to a pool. This function allows users to deposit\n * tokens into a liquidity pool.\n * @dev This function will call `maverickV2AddLiquidityCallback` on the\n * calling contract to collect the tokenA/tokenB payment.\n * @param recipient The account that will receive credit for the added liquidity.\n * @param subaccount The account that will receive credit for the added liquidity.\n * @param params Parameters containing the details for adding liquidity,\n * such as token types and amounts.\n * @param data Bytes information that gets passed to the callback.\n * @return tokenAAmount The amount of token A added to the pool.\n * @return tokenBAmount The amount of token B added to the pool.\n * @return binIds An array of bin IDs where the liquidity is stored.\n */\n function addLiquidity(\n address recipient,\n uint256 subaccount,\n AddLiquidityParams calldata params,\n bytes calldata data\n )\n external\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds\n );\n\n /**\n * @notice Removes liquidity from the pool.\n * @dev Liquidy can only be removed from a bin that is either unmerged or\n * has a mergeId of an unmerged bin. If a bin is merged more than one\n * level deep, it must be migrated up the merge stack to the root bin\n * before liquidity removal.\n * @param recipient The address to receive the tokens.\n * @param subaccount The subaccount for the recipient.\n * @param params The parameters for removing liquidity.\n * @return tokenAOut The amount of token A received.\n * @return tokenBOut The amount of token B received.\n */\n function removeLiquidity(\n address recipient,\n uint256 subaccount,\n RemoveLiquidityParams calldata params\n ) external returns (uint256 tokenAOut, uint256 tokenBOut);\n\n /**\n * @notice Migrate bins up the linked list of merged bins so that its\n * mergeId is the currrent active bin.\n * @dev Liquidy can only be removed from a bin that is either unmerged or\n * has a mergeId of an unmerged bin. If a bin is merged more than one\n * level deep, it must be migrated up the merge stack to the root bin\n * before liquidity removal.\n * @param binId The ID of the bin to migrate.\n * @param maxRecursion The maximum recursion depth for the migration.\n */\n function migrateBinUpStack(uint32 binId, uint32 maxRecursion) external;\n\n /**\n * @notice Swap tokenA/tokenB assets in the pool. The swap user has two\n * options for funding their swap.\n * - The user can push the input token amount to the pool before calling\n * the swap function. In order to avoid having the pool call the callback,\n * the user should pass a zero-length `data` bytes object with the swap\n * call.\n * - The user can send the input token amount to the pool when the pool\n * calls the `maverickV2SwapCallback` function on the calling contract.\n * That callback has input parameters that specify the token address of the\n * input token, the input and output amounts, and the bytes data sent to\n * the swap function.\n * @dev If the users elects to do a callback-based swap, the output\n * assets will be sent before the callback is called, allowing the user to\n * execute flash swaps. However, the pool does have reentrancy protection,\n * so a swapper will not be able to interact with the same pool again\n * while they are in the callback function.\n * @param recipient The address to receive the output tokens.\n * @param params Parameters containing the details of the swap\n * @param data Bytes information that gets passed to the callback.\n */\n function swap(\n address recipient,\n SwapParams memory params,\n bytes calldata data\n ) external returns (uint256 amountIn, uint256 amountOut);\n\n /**\n * @notice Loan tokenA/tokenB assets from the pool to recipient. The fee\n * rate of a loan is determined by `lendingFeeRateD18`, which is set at the\n * protocol level by the factory. This function calls\n * `maverickV2FlashLoanCallback` on the calling contract. At the end of\n * the callback, the caller must pay back the loan with fee (if there is a\n * fee).\n * @param recipient The address to receive the loaned tokens.\n * @param amountB Loan amount of tokenA sent to recipient.\n * @param amountB Loan amount of tokenB sent to recipient.\n * @param data Bytes information that gets passed to the callback.\n */\n function flashLoan(\n address recipient,\n uint256 amountA,\n uint256 amountB,\n bytes calldata data\n ) external;\n\n /**\n * @notice Distributes accumulated protocol fee to factory protocolFeeReceiver\n */\n function distributeFees(bool isTokenA)\n external\n returns (uint256 protocolFee, IERC20 token);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2PoolLens.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2PoolLens {\n error LensTargetPriceOutOfBounds(\n uint256 targetSqrtPrice,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n );\n error LensTooLittleLiquidity(\n uint256 relativeLiquidityAmount,\n uint256 deltaA,\n uint256 deltaB\n );\n error LensTargetingTokenWithNoDelta(\n bool targetIsA,\n uint256 deltaA,\n uint256 deltaB\n );\n\n /**\n * @notice Add liquidity slippage parameters for a distribution of liquidity.\n * @param pool Pool where liquidity is being added.\n * @param kind Bin kind; all bins must have the same kind in a given call\n * to addLiquidity.\n * @param ticks Array of tick values to add liquidity to.\n * @param relativeLiquidityAmounts Relative liquidity amounts for the\n * specified ticks. Liquidity in this case is not bin LP balance, it is\n * the bin liquidity as defined by liquidity = deltaA / (sqrt(upper) -\n * sqrt(lower)) or deltaB = liquidity / sqrt(lower) - liquidity /\n * sqrt(upper).\n * @param addSpec Slippage specification.\n */\n struct AddParamsViewInputs {\n IMaverickV2Pool pool;\n uint8 kind;\n int32[] ticks;\n uint128[] relativeLiquidityAmounts;\n AddParamsSpecification addSpec;\n }\n\n /**\n * @notice Multi-price add param specification.\n * @param slippageFactorD18 Max slippage allowed as a percent in D18 scale. e.g. 1% slippage is 0.01e18\n * @param numberOfPriceBreaksPerSide Number of price break values on either\n * side of current price.\n * @param targetAmount Target token contribution amount in tokenA if\n * targetIsA is true, otherwise this is the target amount for tokenB.\n * @param targetIsA Indicates if the target amount is for tokenA or tokenB\n */\n struct AddParamsSpecification {\n uint256 slippageFactorD18;\n uint256 numberOfPriceBreaksPerSide;\n uint256 targetAmount;\n bool targetIsA;\n }\n\n /**\n * @notice Specification for deriving create pool parameters. Creating a\n * pool in the liquidity manager has several steps:\n *\n * - Deploy pool\n * - Donate a small amount of initial liquidity in the activeTick\n * - Execute a small swap to set the pool price to the desired value\n * - Add liquidity\n *\n * In order to execute these steps, the caller must specify the parameters\n * of each step. The PoolLens has helper function to derive the values\n * used by the LiquidityManager, but this struct is the input to that\n * helper function and represents the core intent of the pool creator.\n *\n * @param fee Fraction of the pool swap amount that is retained as an LP in\n * D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * e.g. a pool with all 4 modes will have kinds = b1111 = 15\n * @param initialTargetB Amount of B to be donated to the pool after pool\n * create. This amount needs to be big enough to meet the minimum bin\n * liquidity.\n * @param sqrtPrice Target sqrt price of the pool.\n * @param kind Bin kind; all bins must have the same kind in a given call\n * to addLiquidity.\n * @param ticks Array of tick values to add liquidity to.\n * @param relativeLiquidityAmounts Relative liquidity amounts for the\n * specified ticks. Liquidity in this case is not bin LP balance, it is\n * the bin liquidity as defined by liquidity = deltaA / (sqrt(upper) -\n * sqrt(lower)) or deltaB = liquidity / sqrt(lower) - liquidity /\n * sqrt(upper).\n * @param targetAmount Target token contribution amount in tokenA if\n * targetIsA is true, otherwise this is the target amount for tokenB.\n * @param targetIsA Indicates if the target amount is for tokenA or tokenB\n */\n struct CreateAndAddParamsViewInputs {\n uint64 feeAIn;\n uint64 feeBIn;\n uint16 tickSpacing;\n uint32 lookback;\n IERC20 tokenA;\n IERC20 tokenB;\n int32 activeTick;\n uint8 kinds;\n // donate params\n uint256 initialTargetB;\n uint256 sqrtPrice;\n // add target\n uint8 kind;\n int32[] ticks;\n uint128[] relativeLiquidityAmounts;\n uint256 targetAmount;\n bool targetIsA;\n }\n\n struct Output {\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256[] deltaAs;\n uint256[] deltaBs;\n uint128[] deltaLpBalances;\n }\n\n struct Reserves {\n uint256 amountA;\n uint256 amountB;\n }\n\n struct BinPositionKinds {\n uint128[4] values;\n }\n\n struct PoolState {\n IMaverickV2Pool.TickState[] tickStateMapping;\n IMaverickV2Pool.BinState[] binStateMapping;\n BinPositionKinds[] binIdByTickKindMapping;\n IMaverickV2Pool.State state;\n Reserves protocolFees;\n }\n\n struct BoostedPositionSpecification {\n IMaverickV2Pool pool;\n uint32[] binIds;\n uint128[] ratios;\n uint8 kind;\n }\n\n struct CreateAndAddParamsInputs {\n uint64 feeAIn;\n uint64 feeBIn;\n uint16 tickSpacing;\n uint32 lookback;\n IERC20 tokenA;\n IERC20 tokenB;\n int32 activeTick;\n uint8 kinds;\n // donate params\n IMaverickV2Pool.AddLiquidityParams donateParams;\n // swap params\n uint256 swapAmount;\n // add params\n IMaverickV2Pool.AddLiquidityParams addParams;\n bytes[] packedAddParams;\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256 preAddReserveA;\n uint256 preAddReserveB;\n }\n\n struct TickDeltas {\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256[] deltaAs;\n uint256[] deltaBs;\n }\n\n /**\n * @notice Converts add parameter slippage specification into add\n * parameters. The return values are given in both raw format and as packed\n * values that can be used in the LiquidityManager contract.\n */\n function getAddLiquidityParams(AddParamsViewInputs memory params)\n external\n view\n returns (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint88[] memory sqrtPriceBreaks,\n IMaverickV2Pool.AddLiquidityParams[] memory addParams,\n IMaverickV2PoolLens.TickDeltas[] memory tickDeltas\n );\n\n /**\n * @notice Converts add parameter slippage specification and new pool\n * specification into CreateAndAddParamsInputs parameters that can be used in the\n * LiquidityManager contract.\n */\n function getCreatePoolAtPriceAndAddLiquidityParams(\n CreateAndAddParamsViewInputs memory params\n ) external view returns (CreateAndAddParamsInputs memory output);\n\n /**\n * @notice View function that provides information about pool ticks within\n * a tick radius from the activeTick. Ticks with no reserves are not\n * included in part o f the return array.\n */\n function getTicksAroundActive(IMaverickV2Pool pool, int32 tickRadius)\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates\n );\n\n /**\n * @notice View function that provides information about pool ticks within\n * a range. Ticks with no reserves are not included in part o f the return\n * array.\n */\n function getTicks(\n IMaverickV2Pool pool,\n int32 tickStart,\n int32 tickEnd\n )\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates\n );\n\n /**\n * @notice View function that provides information about pool ticks within\n * a range. Information returned includes all pool state needed to emulate\n * a swap off chain. Ticks with no reserves are not included in part o f\n * the return array.\n */\n function getTicksAroundActiveWLiquidity(\n IMaverickV2Pool pool,\n int32 tickRadius\n )\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates,\n uint256[] memory liquidities,\n uint256[] memory sqrtLowerTickPrices,\n uint256[] memory sqrtUpperTickPrices,\n IMaverickV2Pool.State memory poolState,\n uint256 sqrtPrice,\n uint256 feeAIn,\n uint256 feeBIn\n );\n\n /**\n * @notice View function that provides pool state information.\n */\n function getFullPoolState(\n IMaverickV2Pool pool,\n uint32 binStart,\n uint32 binEnd\n ) external view returns (PoolState memory poolState);\n\n /**\n * @notice View function that provides price and liquidity of a given tick.\n */\n function getTickSqrtPriceAndL(IMaverickV2Pool pool, int32 tick)\n external\n view\n returns (uint256 sqrtPrice, uint256 liquidity);\n\n /**\n * @notice Pool sqrt price.\n */\n function getPoolSqrtPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 sqrtPrice);\n\n /**\n * @notice Pool price.\n */\n function getPoolPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 price);\n\n /**\n * @notice Token scale of two tokens in a pool.\n */\n function tokenScales(IMaverickV2Pool pool)\n external\n view\n returns (uint256 tokenAScale, uint256 tokenBScale);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Position.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\nimport { ILiquidityRegistry } from \"./ILiquidityRegistry.sol\";\n\ninterface IMaverickV2Position {\n event PositionClearData(uint256 indexed tokenId);\n event PositionSetData(\n uint256 indexed tokenId,\n uint256 index,\n PositionPoolBinIds newData\n );\n event SetLpReward(ILiquidityRegistry lpReward);\n\n error PositionDuplicatePool(uint256 index, IMaverickV2Pool pool);\n error PositionNotFactoryPool();\n error PositionPermissionedLiquidityPool();\n\n struct PositionPoolBinIds {\n IMaverickV2Pool pool;\n uint32[] binIds;\n }\n\n struct PositionFullInformation {\n PositionPoolBinIds poolBinIds;\n uint256 amountA;\n uint256 amountB;\n uint256[] binAAmounts;\n uint256[] binBAmounts;\n int32[] ticks;\n uint256[] liquidities;\n }\n\n /**\n * @notice Factory that tracks lp rewards.\n */\n function lpReward() external view returns (ILiquidityRegistry);\n\n /**\n * @notice Pool factory.\n */\n function factory() external view returns (IMaverickV2Factory);\n\n /**\n * @notice Mint NFT that holds liquidity in a Maverick V2 Pool. To mint\n * liquidity to an NFT, add liquidity to bins in a pool where the\n * add liquidity recipient is this contract and the subaccount is the\n * tokenId. LiquidityManager can be used to simplify minting Position NFTs.\n */\n function mint(\n address recipient,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external returns (uint256 tokenId);\n\n /**\n * @notice Overwrites tokenId pool/binId information for a given data index.\n */\n function setTokenIdData(\n uint256 tokenId,\n uint256 index,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n\n /**\n * @notice Overwrites entire pool/binId data set for a given tokenId.\n */\n function setTokenIdData(uint256 tokenId, PositionPoolBinIds[] memory data)\n external;\n\n /**\n * @notice Append new pool/binIds data array to tokenId.\n */\n function appendTokenIdData(\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n\n /**\n * @notice Get array pool/binIds data for a given tokenId.\n */\n function getTokenIdData(uint256 tokenId)\n external\n view\n returns (PositionPoolBinIds[] memory);\n\n /**\n * @notice Get value from array of pool/binIds data for a given tokenId.\n */\n function getTokenIdData(uint256 tokenId, uint256 index)\n external\n view\n returns (PositionPoolBinIds memory);\n\n /**\n * @notice Length of array of pool/binIds data for a given tokenId.\n */\n function tokenIdDataLength(uint256 tokenId)\n external\n view\n returns (uint256 length);\n\n /**\n * @notice Remove liquidity from tokenId for a given pool. User can\n * specify arbitrary bins to remove from for their subaccount in the pool\n * even if those bins are not in the tokenIdData set.\n */\n function removeLiquidity(\n uint256 tokenId,\n address recipient,\n IMaverickV2Pool pool,\n IMaverickV2Pool.RemoveLiquidityParams memory params\n ) external returns (uint256 tokenAAmount, uint256 tokenBAmount);\n\n /**\n * @notice Remove liquidity from tokenId for a given pool to sender. User\n * can specify arbitrary bins to remove from for their subaccount in the\n * pool even if those bins are not in the tokenIdData set.\n */\n function removeLiquidityToSender(\n uint256 tokenId,\n IMaverickV2Pool pool,\n IMaverickV2Pool.RemoveLiquidityParams memory params\n ) external returns (uint256 tokenAAmount, uint256 tokenBAmount);\n\n /**\n * @notice NFT asset information for a given range of pool/binIds indexes.\n * This function only returns the liquidity in the pools/binIds stored as\n * part of the tokenIdData, but it is possible that the NFT has additional\n * liquidity in pools/binIds that have not been recorded.\n */\n function tokenIdPositionInformation(\n uint256 tokenId,\n uint256 startIndex,\n uint256 stopIndex\n ) external view returns (PositionFullInformation[] memory output);\n\n /**\n * @notice NFT asset information for a given pool/binIds index. This\n * function only returns the liquidity in the pools/binIds stored as part\n * of the tokenIdData, but it is possible that the NFT has additional\n * liquidity in pools/binIds that have not been recorded.\n */\n function tokenIdPositionInformation(uint256 tokenId, uint256 index)\n external\n view\n returns (PositionFullInformation memory output);\n\n /**\n * @notice Get remove parameters for removing a fractional part of the\n * liquidity owned by a given tokenId. The fractional factor to remove is\n * given by proporationD18 in 18-decimal scale.\n */\n function getRemoveParams(\n uint256 tokenId,\n uint256 index,\n uint256 proportionD18\n )\n external\n view\n returns (IMaverickV2Pool.RemoveLiquidityParams memory params);\n\n /**\n * @notice Register the bin balances in the nft with the LpReward contract.\n */\n function checkpointBinLpBalance(\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Quoter.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2Quoter {\n error QuoterInvalidSwap();\n error QuoterInvalidAddLiquidity();\n\n /**\n * @notice Calculates a swap on a MaverickV2Pool and returns the resulting\n * amount and estimated gas. The gas estimate is only a rough estimate and\n * may not match a swap's gas.\n * @param pool The MaverickV2Pool to swap on.\n * @param amount The input amount.\n * @param tokenAIn Indicates if token A is the input token.\n * @param exactOutput Indicates if the amount is the output amount (true)\n * or input amount (false). If the tickLimit is reached, the full value of\n * the exactOutput may not be returned because the pool will stop swapping\n * before the whole order is filled.\n * @param tickLimit The tick limit for the swap. Once the swap lands in\n * this tick, it will stop and return the output amount swapped up to that\n * tick.\n */\n function calculateSwap(\n IMaverickV2Pool pool,\n uint128 amount,\n bool tokenAIn,\n bool exactOutput,\n int32 tickLimit\n )\n external\n returns (\n uint256 amountIn,\n uint256 amountOut,\n uint256 gasEstimate\n );\n\n /**\n * @notice Calculates a multihop swap and returns the resulting amount and\n * estimated gas. The gas estimate is only a rough estimate and\n * may not match a swap's gas.\n * @param path The path of pools to swap through. Path is given by an\n * packed array of (pool, tokenAIn) tuples. So each step in the path is 160\n * + 8 = 168 bits of data. e.g. path = abi.encodePacked(pool1, true, pool2, false);\n * @param amount The input amount.\n * @param exactOutput A boolean indicating if exact output is required.\n */\n function calculateMultiHopSwap(\n bytes memory path,\n uint256 amount,\n bool exactOutput\n ) external returns (uint256 returnAmount, uint256 gasEstimate);\n\n /**\n * @notice Computes the token amounts required for a given set of\n * addLiquidity parameters. The gas estimate is only a rough estimate and\n * may not match a add's gas.\n */\n function calculateAddLiquidity(\n IMaverickV2Pool pool,\n IMaverickV2Pool.AddLiquidityParams calldata params\n )\n external\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 gasEstimate\n );\n\n /**\n * @notice Pool's sqrt price.\n */\n function poolSqrtPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 sqrtPrice);\n}\n" + }, + "contracts/interfaces/plume/IPoolDistributor.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.0;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IPoolDistributor {\n function rewardToken() external view returns (address);\n\n function claimLp(\n address recipient,\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds,\n uint256 epoch\n ) external returns (uint256 amount);\n}\n" + }, + "contracts/interfaces/plume/IVotingDistributor.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.0;\n\ninterface IVotingDistributor {\n function lastEpoch() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/poolBooster/IMerklDistributor.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IMerklDistributor {\n struct CampaignParameters {\n // POPULATED ONCE CREATED\n\n // ID of the campaign. This can be left as a null bytes32 when creating campaigns\n // on Merkl.\n bytes32 campaignId;\n // CHOSEN BY CAMPAIGN CREATOR\n\n // Address of the campaign creator, if marked as address(0), it will be overriden with the\n // address of the `msg.sender` creating the campaign\n address creator;\n // Address of the token used as a reward\n address rewardToken;\n // Amount of `rewardToken` to distribute across all the epochs\n // Amount distributed per epoch is `amount/numEpoch`\n uint256 amount;\n // Type of campaign\n uint32 campaignType;\n // Timestamp at which the campaign should start\n uint32 startTimestamp;\n // Duration of the campaign in seconds. Has to be a multiple of EPOCH = 3600\n uint32 duration;\n // Extra data to pass to specify the campaign\n bytes campaignData;\n }\n\n function createCampaign(CampaignParameters memory newCampaign)\n external\n returns (bytes32);\n\n function signAndCreateCampaign(\n CampaignParameters memory newCampaign,\n bytes memory _signature\n ) external returns (bytes32);\n\n function sign(bytes memory _signature) external;\n\n function rewardTokenMinAmounts(address _rewardToken)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/poolBooster/IPoolBoostCentralRegistry.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPoolBoostCentralRegistry {\n /**\n * @dev all the supported pool booster types are listed here. It is possible\n * to have multiple versions of the factory that supports the same type of\n * pool booster. Factories are immutable and this can happen when a factory\n * or related pool booster required code update.\n * e.g. \"PoolBoosterSwapxDouble\" & \"PoolBoosterSwapxDouble_v2\"\n */\n enum PoolBoosterType {\n // Supports bribing 2 contracts per pool. Appropriate for Ichi vault concentrated\n // liquidity pools where (which is expected in most/all cases) both pool gauges\n // require bribing.\n SwapXDoubleBooster,\n // Supports bribing a single contract per pool. Appropriate for Classic Stable &\n // Classic Volatile pools and Ichi vaults where only 1 side (1 of the 2 gauges)\n // needs bribing\n SwapXSingleBooster,\n // Supports bribing a single contract per pool. Appropriate for Metropolis pools\n MetropolisBooster,\n // Supports creating a Merkl campaign.\n MerklBooster\n }\n\n struct PoolBoosterEntry {\n address boosterAddress;\n address ammPoolAddress;\n PoolBoosterType boosterType;\n }\n\n event PoolBoosterCreated(\n address poolBoosterAddress,\n address ammPoolAddress,\n PoolBoosterType poolBoosterType,\n address factoryAddress\n );\n event PoolBoosterRemoved(address poolBoosterAddress);\n\n function emitPoolBoosterCreated(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n PoolBoosterType _boosterType\n ) external;\n\n function emitPoolBoosterRemoved(address _poolBoosterAddress) external;\n}\n" + }, + "contracts/interfaces/poolBooster/IPoolBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPoolBooster {\n event BribeExecuted(uint256 amount);\n\n /// @notice Execute the bribe action\n function bribe() external;\n}\n" + }, + "contracts/interfaces/poolBooster/ISwapXAlgebraBribe.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBribe {\n /// @notice Notify a bribe amount\n /// @dev Rewards are saved into NEXT EPOCH mapping.\n function notifyRewardAmount(address _rewardsToken, uint256 reward) external;\n}\n" + }, + "contracts/interfaces/sonic/INodeDriver.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\n\ninterface INodeDriver {\n /// Seal epoch. Called BEFORE epoch sealing made by the client itself.\n function sealEpoch(\n uint256[] calldata offlineTimes,\n uint256[] calldata offlineBlocks,\n uint256[] calldata uptimes,\n uint256[] calldata originatedTxsFee\n ) external;\n}\n" + }, + "contracts/interfaces/sonic/ISFC.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\n\n/**\n * @title Special Fee Contract for Sonic network\n * @notice The SFC maintains a list of validators and delegators and distributes rewards to them.\n * @custom:security-contact security@fantom.foundation\n */\ninterface ISFC {\n error StakeIsFullySlashed();\n\n event CreatedValidator(\n uint256 indexed validatorID,\n address indexed auth,\n uint256 createdEpoch,\n uint256 createdTime\n );\n event Delegated(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 amount\n );\n event Undelegated(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 indexed wrID,\n uint256 amount\n );\n event Withdrawn(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 indexed wrID,\n uint256 amount,\n uint256 penalty\n );\n event ClaimedRewards(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 rewards\n );\n event RestakedRewards(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 rewards\n );\n event BurntFTM(uint256 amount);\n event UpdatedSlashingRefundRatio(\n uint256 indexed validatorID,\n uint256 refundRatio\n );\n event RefundedSlashedLegacyDelegation(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 amount\n );\n\n event DeactivatedValidator(\n uint256 indexed validatorID,\n uint256 deactivatedEpoch,\n uint256 deactivatedTime\n );\n event ChangedValidatorStatus(uint256 indexed validatorID, uint256 status);\n event AnnouncedRedirection(address indexed from, address indexed to);\n\n function currentSealedEpoch() external view returns (uint256);\n\n function getEpochSnapshot(uint256 epoch)\n external\n view\n returns (\n uint256 endTime,\n uint256 endBlock,\n uint256 epochFee,\n uint256 baseRewardPerSecond,\n uint256 totalStake,\n uint256 totalSupply\n );\n\n function getStake(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getValidator(uint256 validatorID)\n external\n view\n returns (\n uint256 status,\n uint256 receivedStake,\n address auth,\n uint256 createdEpoch,\n uint256 createdTime,\n uint256 deactivatedTime,\n uint256 deactivatedEpoch\n );\n\n function getValidatorID(address auth) external view returns (uint256);\n\n function getValidatorPubkey(uint256 validatorID)\n external\n view\n returns (bytes memory);\n\n function pubkeyAddressvalidatorID(address pubkeyAddress)\n external\n view\n returns (uint256);\n\n function getWithdrawalRequest(\n address delegator,\n uint256 validatorID,\n uint256 wrID\n )\n external\n view\n returns (\n uint256 epoch,\n uint256 time,\n uint256 amount\n );\n\n function isOwner() external view returns (bool);\n\n function lastValidatorID() external view returns (uint256);\n\n function minGasPrice() external view returns (uint256);\n\n function owner() external view returns (address);\n\n function renounceOwnership() external;\n\n function slashingRefundRatio(uint256 validatorID)\n external\n view\n returns (uint256);\n\n function stashedRewardsUntilEpoch(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function totalActiveStake() external view returns (uint256);\n\n function totalStake() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function transferOwnership(address newOwner) external;\n\n function treasuryAddress() external view returns (address);\n\n function version() external pure returns (bytes3);\n\n function currentEpoch() external view returns (uint256);\n\n function updateConstsAddress(address v) external;\n\n function constsAddress() external view returns (address);\n\n function getEpochValidatorIDs(uint256 epoch)\n external\n view\n returns (uint256[] memory);\n\n function getEpochReceivedStake(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochAccumulatedRewardPerToken(\n uint256 epoch,\n uint256 validatorID\n ) external view returns (uint256);\n\n function getEpochAccumulatedUptime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochAverageUptime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint32);\n\n function getEpochAccumulatedOriginatedTxsFee(\n uint256 epoch,\n uint256 validatorID\n ) external view returns (uint256);\n\n function getEpochOfflineTime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochOfflineBlocks(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochEndBlock(uint256 epoch) external view returns (uint256);\n\n function rewardsStash(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function createValidator(bytes calldata pubkey) external payable;\n\n function getSelfStake(uint256 validatorID) external view returns (uint256);\n\n function delegate(uint256 validatorID) external payable;\n\n function undelegate(\n uint256 validatorID,\n uint256 wrID,\n uint256 amount\n ) external;\n\n function isSlashed(uint256 validatorID) external view returns (bool);\n\n function withdraw(uint256 validatorID, uint256 wrID) external;\n\n function deactivateValidator(uint256 validatorID, uint256 status) external;\n\n function pendingRewards(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function stashRewards(address delegator, uint256 validatorID) external;\n\n function claimRewards(uint256 validatorID) external;\n\n function restakeRewards(uint256 validatorID) external;\n\n function updateSlashingRefundRatio(uint256 validatorID, uint256 refundRatio)\n external;\n\n function updateTreasuryAddress(address v) external;\n\n function burnFTM(uint256 amount) external;\n\n function sealEpoch(\n uint256[] calldata offlineTime,\n uint256[] calldata offlineBlocks,\n uint256[] calldata uptimes,\n uint256[] calldata originatedTxsFee\n ) external;\n\n function sealEpochValidators(uint256[] calldata nextValidatorIDs) external;\n\n function initialize(\n uint256 sealedEpoch,\n uint256 _totalSupply,\n address nodeDriver,\n address consts,\n address _owner\n ) external;\n\n function setGenesisValidator(\n address auth,\n uint256 validatorID,\n bytes calldata pubkey,\n uint256 createdTime\n ) external;\n\n function setGenesisDelegation(\n address delegator,\n uint256 validatorID,\n uint256 stake\n ) external;\n\n function updateStakeSubscriberAddress(address v) external;\n\n function stakeSubscriberAddress() external view returns (address);\n\n function setRedirectionAuthorizer(address v) external;\n\n function announceRedirection(address to) external;\n\n function initiateRedirection(address from, address to) external;\n\n function redirect(address to) external;\n}\n" + }, + "contracts/interfaces/sonic/ISwapXGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IGauge {\n function owner() external view returns (address);\n\n function TOKEN() external view returns (address);\n\n function DISTRIBUTION() external view returns (address);\n\n function balanceOf(address account) external view returns (uint256);\n\n function claimFees() external returns (uint256 claimed0, uint256 claimed1);\n\n function deposit(uint256 amount) external;\n\n function depositAll() external;\n\n function earned(address account) external view returns (uint256);\n\n function getReward() external;\n\n function getReward(address _user) external;\n\n function isForPair() external view returns (bool);\n\n function lastTimeRewardApplicable() external view returns (uint256);\n\n function lastUpdateTime() external view returns (uint256);\n\n function notifyRewardAmount(address token, uint256 reward) external;\n\n function periodFinish() external view returns (uint256);\n\n function rewardForDuration() external view returns (uint256);\n\n function rewardPerToken() external view returns (uint256);\n\n function rewardPerTokenStored() external view returns (uint256);\n\n function rewardRate() external view returns (uint256);\n\n function rewardToken() external view returns (address);\n\n function rewards(address) external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function userRewardPerTokenPaid(address) external view returns (uint256);\n\n function withdraw(uint256 amount) external;\n\n function withdrawAll() external;\n\n function withdrawAllAndHarvest() external;\n\n function withdrawExcess(address token, uint256 amount) external;\n\n function emergency() external returns (bool);\n\n function emergencyWithdraw() external;\n\n function activateEmergencyMode() external;\n\n function stopEmergencyMode() external;\n}\n" + }, + "contracts/interfaces/sonic/ISwapXPair.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IPair {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n\n event Mint(address indexed sender, uint256 amount0, uint256 amount1);\n event Burn(\n address indexed sender,\n uint256 amount0,\n uint256 amount1,\n address indexed to\n );\n event Swap(\n address indexed sender,\n uint256 amount0In,\n uint256 amount1In,\n uint256 amount0Out,\n uint256 amount1Out,\n address indexed to\n );\n event Claim(\n address indexed sender,\n address indexed recipient,\n uint256 amount0,\n uint256 amount1\n );\n\n function metadata()\n external\n view\n returns (\n uint256 dec0,\n uint256 dec1,\n uint256 r0,\n uint256 r1,\n bool st,\n address t0,\n address t1\n );\n\n function claimFees() external returns (uint256, uint256);\n\n function tokens() external view returns (address, address);\n\n function token0() external view returns (address);\n\n function token1() external view returns (address);\n\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function swap(\n uint256 amount0Out,\n uint256 amount1Out,\n address to,\n bytes calldata data\n ) external;\n\n function burn(address to)\n external\n returns (uint256 amount0, uint256 amount1);\n\n function mint(address to) external returns (uint256 liquidity);\n\n function getReserves()\n external\n view\n returns (\n uint256 _reserve0,\n uint256 _reserve1,\n uint256 _blockTimestampLast\n );\n\n function getAmountOut(uint256, address) external view returns (uint256);\n\n // ERC20 methods\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n\n function totalSupply() external view returns (uint256);\n\n function balanceOf(address) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 value) external returns (bool);\n\n function claimable0(address _user) external view returns (uint256);\n\n function claimable1(address _user) external view returns (uint256);\n\n function isStable() external view returns (bool);\n\n function skim(address to) external;\n\n function sync() external;\n}\n" + }, + "contracts/interfaces/sonic/IVoterV3.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IVoterV3 {\n /// @notice create a gauge\n function createGauge(address _pool, uint256 _gaugeType)\n external\n returns (\n address _gauge,\n address _internal_bribe,\n address _external_bribe\n );\n\n function gauges(address _pool) external view returns (address _gauge);\n}\n" + }, + "contracts/interfaces/sonic/IWrappedSonic.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWrappedSonic {\n event Deposit(address indexed account, uint256 value);\n event Withdrawal(address indexed account, uint256 value);\n event Transfer(address indexed from, address indexed to, uint256 value);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 value) external returns (bool);\n\n function balanceOf(address account) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function depositFor(address account) external payable returns (bool);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address to, uint256 value) external returns (bool);\n\n function transferFrom(\n address from,\n address to,\n uint256 value\n ) external returns (bool);\n\n function withdraw(uint256 value) external;\n\n function withdrawTo(address account, uint256 value) external returns (bool);\n}\n" + }, + "contracts/interfaces/Tether.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// slither-disable-start erc20-interface\ninterface Tether {\n function transfer(address to, uint256 value) external;\n\n function transferFrom(\n address from,\n address to,\n uint256 value\n ) external;\n\n function balanceOf(address) external view returns (uint256);\n\n function approve(address _spender, uint256 _value) external;\n}\n// slither-disable-end erc20-interface\n" + }, + "contracts/interfaces/uniswap/IUniswapUniversalRouter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapUniversalRouter {\n /// @notice Executes encoded commands along with provided inputs. Reverts if deadline has expired.\n /// @param commands A set of concatenated commands, each 1 byte in length\n /// @param inputs An array of byte strings containing abi encoded inputs for each command\n /// @param deadline The deadline by which the transaction must be executed\n function execute(\n bytes calldata commands,\n bytes[] calldata inputs,\n uint256 deadline\n ) external payable;\n\n function execute(bytes calldata commands, bytes[] calldata inputs)\n external\n payable;\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV2Pair.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapV2Pair {\n function token0() external view returns (address);\n\n function token1() external view returns (address);\n\n function getReserves()\n external\n view\n returns (\n uint112 reserve0,\n uint112 reserve1,\n uint32 blockTimestampLast\n );\n\n function price0CumulativeLast() external view returns (uint256);\n\n function price1CumulativeLast() external view returns (uint256);\n\n function sync() external;\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV2Router02.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapV2Router {\n function WETH() external pure returns (address);\n\n function swapExactTokensForTokens(\n uint256 amountIn,\n uint256 amountOutMin,\n address[] calldata path,\n address to,\n uint256 deadline\n ) external returns (uint256[] memory amounts);\n\n function addLiquidity(\n address tokenA,\n address tokenB,\n uint256 amountADesired,\n uint256 amountBDesired,\n uint256 amountAMin,\n uint256 amountBMin,\n address to,\n uint256 deadline\n )\n external\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 liquidity\n );\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV3Router.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// -- Solididy v0.5.x compatible interface\ninterface IUniswapV3Router {\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n}\n" + }, + "contracts/mocks/beacon/ExecutionLayerConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { GeneralPurposeToConsensusLayerRequest } from \"./GeneralPurposeToConsensusLayerRequest.sol\";\n\ncontract ExecutionLayerConsolidation is GeneralPurposeToConsensusLayerRequest {\n event ConsolidationRequestIssued(bytes sourceKey, bytes targetKey);\n bytes public lastSource;\n bytes public lastTarget;\n\n function handleRequest(bytes calldata data) internal override {\n // parameters should consist of twice the 48 bytes for 2 public keys\n require(data.length == 96, \"Invalid Consolidation data\");\n lastSource = data[:48];\n lastTarget = data[48:];\n\n emit ConsolidationRequestIssued(lastSource, lastTarget);\n }\n}\n" + }, + "contracts/mocks/beacon/ExecutionLayerWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { GeneralPurposeToConsensusLayerRequest } from \"./GeneralPurposeToConsensusLayerRequest.sol\";\n\ncontract ExecutionLayerWithdrawal is GeneralPurposeToConsensusLayerRequest {\n event WithdrawalRequestIssued(bytes publicKey, uint64 amount);\n\n bytes public lastPublicKey;\n uint64 public lastAmount;\n\n function handleRequest(bytes calldata data) internal override {\n // parameters should consist of 48 bytes for public key and 8 bytes for uint64 amount\n require(data.length == 56, \"Invalid Withdrawal data\");\n lastPublicKey = data[:48];\n lastAmount = uint64(bytes8(data[48:]));\n emit WithdrawalRequestIssued(lastPublicKey, lastAmount);\n }\n}\n" + }, + "contracts/mocks/beacon/GeneralPurposeToConsensusLayerRequest.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nabstract contract GeneralPurposeToConsensusLayerRequest {\n // solhint-disable no-complex-fallback\n fallback() external payable {\n // fee requested\n if (msg.data.length == 0) {\n uint256 fee = _fee();\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Return a uint256 value\n mstore(0, fee)\n return(0, 32) // Return 32 bytes from memory\n }\n }\n\n // else handle request\n handleRequest(msg.data);\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _fee() internal virtual returns (uint256) {\n return 1;\n }\n\n function handleRequest(bytes calldata data) internal virtual;\n}\n" + }, + "contracts/mocks/BurnableERC20.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ninterface IBurnableERC20 {\n function burn(uint256 value) external returns (bool);\n\n function burnFrom(address account, uint256 value) external returns (bool);\n}\n\n/**\n * @title BurnableERC20\n * @dev Exposes the burn function of ERC20 for tests\n */\nabstract contract BurnableERC20 is IBurnableERC20, ERC20 {\n /**\n * @dev Function to burn tokens\n * @param value The amount of tokens to burn.\n * @return A boolean that indicates if the operation was successful.\n */\n function burn(uint256 value) public virtual override returns (bool) {\n _burn(msg.sender, value);\n return true;\n }\n\n /**\n * @dev Function to burn tokens from a specific account\n * @param account The address with the tokens to burn.\n * @param value The amount of tokens to burn.\n * @return A boolean that indicates if the operation was successful.\n */\n function burnFrom(address account, uint256 value)\n public\n override\n returns (bool)\n {\n _burn(account, value);\n return true;\n }\n}\n" + }, + "contracts/mocks/curve/Mock3CRV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20, MintableERC20 } from \"../MintableERC20.sol\";\nimport { BurnableERC20 } from \"../BurnableERC20.sol\";\n\ncontract Mock3CRV is MintableERC20, BurnableERC20 {\n constructor() ERC20(\"Curve.fi DAI/USDC/USDT\", \"3Crv\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/curve/MockBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { MockRewardPool } from \"./MockRewardPool.sol\";\n\nimport { IRewardStaking } from \"../../strategies/IRewardStaking.sol\";\nimport { IMintableERC20, MintableERC20, ERC20 } from \"../MintableERC20.sol\";\nimport { IBurnableERC20, BurnableERC20 } from \"../BurnableERC20.sol\";\n\ncontract MockDepositToken is MintableERC20, BurnableERC20 {\n constructor() ERC20(\"DCVX\", \"CVX Deposit Token\") {}\n}\n\ncontract MockBooster {\n using SafeERC20 for IERC20;\n\n struct PoolInfo {\n address lptoken;\n address token;\n address crvRewards;\n }\n\n address public minter; // this is CVx for the booster on live\n address public crv; // Curve rewards token\n address public cvx; // Convex rewards token\n mapping(uint256 => PoolInfo) public poolInfo;\n\n constructor(\n address _rewardsMinter,\n address _crv,\n address _cvx\n ) public {\n minter = _rewardsMinter;\n crv = _crv;\n cvx = _cvx;\n }\n\n function setPool(uint256 pid, address _lpToken)\n external\n returns (address rewards)\n {\n address token = address(new MockDepositToken());\n // Deploy a new Convex Rewards Pool\n rewards = address(\n new MockRewardPool(pid, token, crv, cvx, address(this))\n );\n\n poolInfo[pid] = PoolInfo({\n lptoken: _lpToken,\n token: token,\n crvRewards: rewards\n });\n }\n\n function deposit(\n uint256 _pid,\n uint256 _amount,\n bool _stake\n ) public returns (bool) {\n PoolInfo storage pool = poolInfo[_pid];\n\n address lptoken = pool.lptoken;\n\n // hold on to the Curve LP tokens\n IERC20(lptoken).safeTransferFrom(msg.sender, address(this), _amount);\n\n address token = pool.token;\n if (_stake) {\n // mint Convex pool LP tokens and stake in rewards contract on user behalf\n IMintableERC20(token).mint(_amount);\n address rewardContract = pool.crvRewards;\n IERC20(token).safeApprove(rewardContract, 0);\n IERC20(token).safeApprove(rewardContract, _amount);\n IRewardStaking(rewardContract).stakeFor(msg.sender, _amount);\n } else {\n // mint Convex pool LP tokens and send to user\n IMintableERC20(token).mint(_amount);\n IERC20(token).transfer(msg.sender, _amount);\n }\n return true;\n }\n\n // Deposit all Curve LP tokens and stake\n function depositAll(uint256 _pid, bool _stake) external returns (bool) {\n address lptoken = poolInfo[_pid].lptoken;\n uint256 balance = IERC20(lptoken).balanceOf(msg.sender);\n deposit(_pid, balance, _stake);\n return true;\n }\n\n // withdraw Curve LP tokens\n function _withdraw(\n uint256 _pid,\n uint256 _amount,\n address _from,\n address _to\n ) internal {\n PoolInfo storage pool = poolInfo[_pid];\n\n // burn the Convex pool LP tokens\n IBurnableERC20(pool.token).burnFrom(_from, _amount);\n\n // return the Curve LP tokens\n IERC20(pool.lptoken).safeTransfer(_to, _amount);\n }\n\n // withdraw Curve LP tokens\n function withdraw(uint256 _pid, uint256 _amount) public returns (bool) {\n _withdraw(_pid, _amount, msg.sender, msg.sender);\n return true;\n }\n\n // withdraw all Curve LP tokens\n function withdrawAll(uint256 _pid) public returns (bool) {\n address token = poolInfo[_pid].token;\n uint256 userBal = IERC20(token).balanceOf(msg.sender);\n withdraw(_pid, userBal);\n return true;\n }\n\n // allow reward contracts to send here and withdraw to user\n function withdrawTo(\n uint256 _pid,\n uint256 _amount,\n address _to\n ) external returns (bool) {\n address rewardContract = poolInfo[_pid].crvRewards;\n require(msg.sender == rewardContract, \"!auth\");\n\n _withdraw(_pid, _amount, msg.sender, _to);\n return true;\n }\n\n // callback from reward contract when crv is received.\n function rewardClaimed(\n uint256 _pid,\n // solhint-disable-next-line no-unused-vars\n address _address,\n uint256 _amount\n ) external returns (bool) {\n address rewardContract = poolInfo[_pid].crvRewards;\n require(msg.sender == rewardContract, \"!auth\");\n\n //mint reward tokens\n // and transfer it\n IMintableERC20(minter).mint(_amount);\n IERC20(minter).transfer(msg.sender, _amount);\n return true;\n }\n}\n" + }, + "contracts/mocks/curve/MockCRV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../MintableERC20.sol\";\n\ncontract MockCRV is MintableERC20 {\n constructor() ERC20(\"Curve DAO Token\", \"CRV\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/curve/MockCRVMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\n\ncontract MockCRVMinter {\n address crv;\n\n constructor(address _crv) {\n crv = _crv;\n }\n\n function mint(address _address) external {\n uint256 amount = 2e18;\n IMintableERC20(crv).mint(amount);\n IERC20(crv).transfer(_address, amount);\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveAbstractMetapool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { MintableERC20, IMintableERC20 } from \"../MintableERC20.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport \"../../utils/Helpers.sol\";\n\nabstract contract MockCurveAbstractMetapool is MintableERC20 {\n using StableMath for uint256;\n\n address[] public coins;\n uint256[2] public balances;\n\n // Returns the same amount of LP tokens in 1e18 decimals\n function add_liquidity(uint256[2] calldata _amounts, uint256 _minAmount)\n external\n returns (uint256 lpAmount)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transferFrom(\n msg.sender,\n address(this),\n _amounts[i]\n );\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to sum\n lpAmount += _amounts[i].scaleBy(18, assetDecimals);\n balances[i] = balances[i] + _amounts[i];\n }\n }\n // Hacky way of simulating slippage to check _minAmount\n if (lpAmount == 29000e18) lpAmount = 14500e18;\n require(lpAmount >= _minAmount, \"Slippage ruined your day\");\n // Send LP token to sender, e.g. 3CRV\n _mint(msg.sender, lpAmount);\n }\n\n // Dumb implementation that returns the same amount\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n public\n view\n returns (uint256 lpAmount)\n {\n uint256 assetDecimals = Helpers.getDecimals(coins[uint128(_index)]);\n lpAmount = _amount.scaleBy(assetDecimals, 18);\n }\n\n function remove_liquidity_one_coin(\n uint256 _lpAmount,\n int128 _index,\n // solhint-disable-next-line no-unused-vars\n uint256 _minAmount\n ) external returns (uint256 amount) {\n _burn(msg.sender, _lpAmount);\n uint256[] memory amounts = new uint256[](coins.length);\n amounts[uint128(_index)] = _lpAmount;\n amount = calc_withdraw_one_coin(_lpAmount, _index);\n balances[uint128(_index)] -= amount;\n IERC20(coins[uint128(_index)]).transfer(msg.sender, amount);\n }\n\n function get_virtual_price() external pure returns (uint256) {\n return 1e18;\n }\n\n // solhint-disable-next-line no-unused-vars\n function remove_liquidity(uint256 _amount, uint256[2] memory _min_amounts)\n public\n returns (uint256[2] memory amounts)\n {\n _burn(msg.sender, _amount);\n uint256 totalSupply = totalSupply();\n for (uint256 i = 0; i < 2; i++) {\n amounts[i] = totalSupply > 0\n ? (_amount * IERC20(coins[i]).balanceOf(address(this))) /\n totalSupply\n : IERC20(coins[i]).balanceOf(address(this));\n balances[i] -= amounts[i];\n IERC20(coins[i]).transfer(msg.sender, amounts[i]);\n }\n }\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens\n ) public returns (uint256) {\n return\n _remove_liquidity_imbalance(\n _amounts,\n _max_burned_tokens,\n msg.sender\n );\n }\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens,\n address _reveiver\n ) public returns (uint256) {\n return\n _remove_liquidity_imbalance(\n _amounts,\n _max_burned_tokens,\n _reveiver\n );\n }\n\n function _remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens,\n address _reveiver\n ) internal returns (uint256 lpTokens) {\n lpTokens = _max_burned_tokens;\n _burn(msg.sender, lpTokens);\n for (uint256 i = 0; i < _amounts.length; i++) {\n balances[i] -= _amounts[i];\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transfer(_reveiver, _amounts[i]);\n }\n }\n }\n\n // Dumb implementation that sums the scaled amounts\n function calc_token_amount(uint256[2] memory _amounts, bool)\n public\n view\n returns (uint256 lpTokens)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to lpTokens\n lpTokens += _amounts[i].scaleBy(18, assetDecimals);\n }\n }\n\n /// @notice 0.02% fee\n function fee() external pure returns (uint256) {\n return 2000000;\n }\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n\n function burnFrom(address from, uint256 value) public {\n _burn(from, value);\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveGauge.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { ICurveGauge } from \"../../strategies/ICurveGauge.sol\";\n\ncontract MockCurveGauge is ICurveGauge {\n mapping(address => uint256) private _balances;\n address lpToken;\n\n constructor(address _lpToken) {\n lpToken = _lpToken;\n }\n\n function balanceOf(address account) public view override returns (uint256) {\n return _balances[account];\n }\n\n function deposit(uint256 _value, address _account) external override {\n IERC20(lpToken).transferFrom(msg.sender, address(this), _value);\n _balances[_account] += _value;\n }\n\n function withdraw(uint256 _value) external override {\n IERC20(lpToken).transfer(msg.sender, _value);\n // solhint-disable-next-line reentrancy\n _balances[msg.sender] -= _value;\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveMetapool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { MockCurveAbstractMetapool } from \"./MockCurveAbstractMetapool.sol\";\nimport \"../MintableERC20.sol\";\n\ncontract MockCurveMetapool is MockCurveAbstractMetapool {\n constructor(address[2] memory _coins)\n ERC20(\"Curve.fi 3pool/OUSD metapool\", \"3crv_OUSD\")\n {\n coins = _coins;\n }\n}\n" + }, + "contracts/mocks/curve/MockCurvePool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IBurnableERC20 } from \"../BurnableERC20.sol\";\n\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\nimport { ICurvePool } from \"../../strategies/ICurvePool.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport \"../../utils/Helpers.sol\";\n\ncontract MockCurvePool {\n using StableMath for uint256;\n\n address[] public coins;\n uint256[3] public balances;\n address lpToken;\n uint256 public slippage = 1 ether;\n\n constructor(address[3] memory _coins, address _lpToken) {\n coins = _coins;\n lpToken = _lpToken;\n }\n\n function setCoins(address[] memory _coins) external {\n coins = _coins;\n }\n\n // Returns the same amount of LP tokens in 1e18 decimals\n function add_liquidity(uint256[3] calldata _amounts, uint256 _minAmount)\n external\n {\n uint256 sum = 0;\n for (uint256 i = 0; i < _amounts.length; i++) {\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transferFrom(\n msg.sender,\n address(this),\n _amounts[i]\n );\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to sum\n sum += _amounts[i].scaleBy(18, assetDecimals);\n balances[i] = balances[i] + _amounts[i];\n }\n }\n // Hacky way of simulating slippage to check _minAmount\n if (sum == 29000e18) sum = 14500e18;\n require(sum >= _minAmount, \"Slippage ruined your day\");\n // Send LP token to sender, e.g. 3CRV\n IMintableERC20(lpToken).mint(sum);\n IERC20(lpToken).transfer(msg.sender, sum);\n }\n\n // Dumb implementation that returns the same amount\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n public\n view\n returns (uint256)\n {\n uint256 assetDecimals = Helpers.getDecimals(coins[uint128(_index)]);\n return _amount.scaleBy(assetDecimals, 18);\n }\n\n function remove_liquidity_one_coin(\n uint256 _amount,\n int128 _index,\n // solhint-disable-next-line no-unused-vars\n uint256 _minAmount\n ) external {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _amount);\n uint256[] memory amounts = new uint256[](coins.length);\n amounts[uint128(_index)] = _amount;\n uint256 coinAmount = calc_withdraw_one_coin(_amount, _index);\n balances[uint128(_index)] -= coinAmount;\n IERC20(coins[uint128(_index)]).transfer(msg.sender, coinAmount);\n }\n\n function get_virtual_price() external pure returns (uint256) {\n return 1e18;\n }\n\n // solhint-disable-next-line no-unused-vars\n function remove_liquidity(uint256 _lpAmount, uint256[3] memory _min_amounts)\n public\n {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _lpAmount);\n uint256 totalSupply = IERC20(lpToken).totalSupply();\n for (uint256 i = 0; i < 3; i++) {\n uint256 coinAmount = totalSupply > 0\n ? (_lpAmount * IERC20(coins[i]).balanceOf(address(this))) /\n totalSupply\n : IERC20(coins[i]).balanceOf(address(this));\n balances[i] -= coinAmount;\n IERC20(coins[i]).transfer(msg.sender, coinAmount);\n }\n }\n\n function remove_liquidity_imbalance(\n uint256[3] memory _amounts,\n uint256 _max_burned_tokens\n ) public {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _max_burned_tokens);\n // For each coin, transfer to the caller\n for (uint256 i = 0; i < _amounts.length; i++) {\n balances[i] -= _amounts[i];\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transfer(msg.sender, _amounts[i]);\n }\n }\n }\n\n // Dumb implementation that sums the scaled amounts\n function calc_token_amount(uint256[3] memory _amounts, bool)\n public\n view\n returns (uint256 lpTokens)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to lpTokens\n lpTokens += _amounts[i].scaleBy(18, assetDecimals);\n }\n }\n\n function fee() external pure returns (uint256) {\n return 1000000;\n }\n\n function exchange(\n uint256 coin0,\n uint256 coin1,\n uint256 amountIn,\n uint256 minAmountOut\n ) external returns (uint256 amountOut) {\n IERC20(coins[coin0]).transferFrom(msg.sender, address(this), amountIn);\n amountOut = (minAmountOut * slippage) / 1 ether;\n require(amountOut >= minAmountOut, \"Slippage error\");\n IMintableERC20(coins[coin1]).mintTo(msg.sender, amountOut);\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n}\n" + }, + "contracts/mocks/curve/MockCVX.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../MintableERC20.sol\";\n\ncontract MockCVX is MintableERC20 {\n constructor() ERC20(\"CVX\", \"CVX DAO Token\") {}\n}\n" + }, + "contracts/mocks/curve/MockRewardPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\ninterface IDeposit {\n function poolInfo(uint256)\n external\n view\n returns (\n address,\n address,\n address,\n address,\n address,\n bool\n );\n\n function rewardClaimed(\n uint256,\n address,\n uint256\n ) external;\n\n function withdrawTo(\n uint256,\n uint256,\n address\n ) external;\n}\n\ncontract MockRewardPool {\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 public pid;\n address public stakingToken;\n address public rewardTokenA;\n address public rewardTokenB;\n address public operator;\n\n uint256 private _totalSupply;\n\n mapping(address => uint256) private _balances;\n mapping(address => uint256) public rewards;\n\n constructor(\n uint256 _pid,\n address _stakingToken,\n address _rewardTokenA,\n address _rewardTokenB,\n address _operator\n ) public {\n pid = _pid;\n stakingToken = _stakingToken;\n rewardTokenA = _rewardTokenA;\n rewardTokenB = _rewardTokenB;\n operator = _operator;\n }\n\n function totalSupply() public view returns (uint256) {\n return _totalSupply;\n }\n\n function balanceOf(address account) public view returns (uint256) {\n return _balances[account];\n }\n\n function stakeFor(address _for, uint256 _amount) public returns (bool) {\n require(_amount > 0, \"RewardPool : Cannot stake 0\");\n\n //give to _for\n _totalSupply = _totalSupply.add(_amount);\n _balances[_for] = _balances[_for].add(_amount);\n\n //take away from sender\n IERC20(stakingToken).safeTransferFrom(\n msg.sender,\n address(this),\n _amount\n );\n\n return true;\n }\n\n function withdrawAndUnwrap(uint256 amount, bool claim)\n public\n returns (bool)\n {\n _totalSupply = _totalSupply.sub(amount);\n _balances[msg.sender] = _balances[msg.sender].sub(amount);\n\n //tell operator to withdraw from here directly to user\n IDeposit(operator).withdrawTo(pid, amount, msg.sender);\n\n //get rewards too\n if (claim) {\n getReward(msg.sender, true);\n }\n return true;\n }\n\n function withdrawAllAndUnwrap(bool claim) external {\n withdrawAndUnwrap(_balances[msg.sender], claim);\n }\n\n // solhint-disable-next-line no-unused-vars\n function getReward(address _account, bool _claimExtras)\n public\n returns (bool)\n {\n IMintableERC20(rewardTokenA).mint(2 * 1e18);\n IERC20(rewardTokenA).transfer(_account, 2 * 1e18);\n\n IMintableERC20(rewardTokenB).mint(3 * 1e18);\n IERC20(rewardTokenB).transfer(_account, 3 * 1e18);\n\n return true;\n }\n\n function getReward() public returns (bool) {\n getReward(msg.sender, true);\n }\n}\n" + }, + "contracts/mocks/ForceEtherSender.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract ForceEtherSender {\n // Constructor to optionally receive Ether upon deployment\n constructor() payable {}\n\n // Function to allow the contract to receive Ether\n receive() external payable {}\n\n // Function to self-destruct and force-send Ether to an address\n function forceSend(address payable recipient) external {\n // Requires that the contract has a balance greater than 0\n require(address(this).balance > 0, \"No Ether to send\");\n\n // selfdestruct sends all Ether held by the contract to the recipient\n selfdestruct(recipient);\n }\n}\n" + }, + "contracts/mocks/MintableERC20.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ninterface IMintableERC20 {\n function mint(uint256 value) external;\n\n function mintTo(address to, uint256 value) external;\n}\n\n/**\n * @title MintableERC20\n * @dev Exposes the mint function of ERC20 for tests\n */\nabstract contract MintableERC20 is IMintableERC20, ERC20 {\n /**\n * @dev Function to mint tokens\n * @param _value The amount of tokens to mint.\n */\n function mint(uint256 _value) public virtual override {\n _mint(msg.sender, _value);\n }\n\n /**\n * @dev Function to mint tokens\n * @param _to Address to mint to.\n * @param _value The amount of tokens to mint.\n */\n function mintTo(address _to, uint256 _value) public virtual override {\n _mint(_to, _value);\n }\n}\n" + }, + "contracts/mocks/Mock1InchSwapRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { SwapDescription } from \"../interfaces/IOneInch.sol\";\n\ncontract Mock1InchSwapRouter {\n using SafeERC20 for IERC20;\n\n event MockSwap(address executor, bytes permitData, bytes executorData);\n\n event MockSwapDesc(\n address srcToken,\n address dstToken,\n address srcReceiver,\n address dstReceiver,\n uint256 amount,\n uint256 minReturnAmount,\n uint256 flags\n );\n\n event MockUnoswapTo(\n address recipient,\n address srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] pools\n );\n\n event MockUniswapV3SwapTo(\n address recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] pools\n );\n\n /**\n * @dev transfers the shource asset and returns the minReturnAmount of the destination asset.\n */\n function swap(\n address executor,\n SwapDescription calldata desc,\n bytes calldata permitData,\n bytes calldata executorData\n ) public returns (uint256 returnAmount, uint256 spentAmount) {\n // Transfer the source tokens to the receiver contract\n IERC20(desc.srcToken).safeTransferFrom(\n msg.sender,\n desc.srcReceiver,\n desc.amount\n );\n\n // Transfer the destination tokens to the recipient\n IERC20(desc.dstToken).safeTransfer(\n desc.dstReceiver,\n desc.minReturnAmount\n );\n\n emit MockSwap(executor, permitData, executorData);\n _swapDesc(desc);\n returnAmount = 0;\n spentAmount = 0;\n }\n\n function _swapDesc(SwapDescription calldata desc) public {\n emit MockSwapDesc(\n address(desc.srcToken),\n address(desc.dstToken),\n desc.srcReceiver,\n desc.dstReceiver,\n desc.amount,\n desc.minReturnAmount,\n desc.flags\n );\n }\n\n /**\n * @dev only transfers the source asset to this contract.\n * Ideally it would return the destination asset but that's encoded in the pools array.\n */\n function unoswapTo(\n address payable recipient,\n address srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) public returns (uint256 returnAmount) {\n // transfer the from asset from the caller\n IERC20(srcToken).safeTransferFrom(msg.sender, address(this), amount);\n\n emit MockUnoswapTo(recipient, srcToken, amount, minReturn, pools);\n returnAmount = 0;\n }\n\n /**\n * @dev does not do any transfers. Just emits MockUniswapV3SwapTo.\n */\n function uniswapV3SwapTo(\n address payable recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) public returns (uint256 returnAmount) {\n emit MockUniswapV3SwapTo(recipient, amount, minReturn, pools);\n returnAmount = 0;\n }\n}\n" + }, + "contracts/mocks/MockAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { IAaveLendingPool, ILendingPoolAddressesProvider } from \"../strategies/IAave.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// 1. User calls 'getLendingPool'\n// 2. User calls 'deposit' (Aave)\n// - Deposit their underlying\n// - Mint aToken to them\n// 3. User calls redeem (aToken)\n// - Retrieve their aToken\n// - Return equal amount of underlying\n\ncontract MockAToken is MintableERC20 {\n address public lendingPool;\n IERC20 public underlyingToken;\n using SafeERC20 for IERC20;\n\n constructor(\n address _lendingPool,\n string memory _name,\n string memory _symbol,\n IERC20 _underlyingToken\n ) ERC20(_name, _symbol) {\n lendingPool = _lendingPool;\n underlyingToken = _underlyingToken;\n // addMinter(_lendingPool);\n }\n\n function decimals() public view override returns (uint8) {\n return ERC20(address(underlyingToken)).decimals();\n }\n\n function poolRedeem(uint256 _amount, address _to) external {\n require(msg.sender == lendingPool, \"pool only\");\n // Redeem these a Tokens\n _burn(_to, _amount);\n // For the underlying\n underlyingToken.safeTransferFrom(lendingPool, _to, _amount);\n }\n}\n\ncontract MockAave is IAaveLendingPool, ILendingPoolAddressesProvider {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n mapping(address => address) reserveToAToken;\n address pool = address(this);\n address payable core = payable(address(this));\n uint256 factor;\n\n function addAToken(address _aToken, address _underlying) public {\n IERC20(_underlying).safeApprove(_aToken, 0);\n IERC20(_underlying).safeApprove(_aToken, type(uint256).max);\n reserveToAToken[_underlying] = _aToken;\n }\n\n // set the reserve factor / basically the interest on deposit\n // in 18 precision\n // so 0.5% would be 5 * 10 ^ 15\n function setFactor(uint256 factor_) public {\n factor = factor_;\n }\n\n function deposit(\n address _reserve,\n uint256 _amount,\n address _to,\n uint16 /*_referralCode*/\n ) external override {\n uint256 previousBal = IERC20(reserveToAToken[_reserve]).balanceOf(\n msg.sender\n );\n uint256 interest = previousBal.mulTruncate(factor);\n MintableERC20(reserveToAToken[_reserve]).mintTo(msg.sender, interest);\n // Take their reserve\n IERC20(_reserve).safeTransferFrom(msg.sender, address(this), _amount);\n // Credit them with aToken\n MintableERC20(reserveToAToken[_reserve]).mintTo(_to, _amount);\n }\n\n function withdraw(\n address asset,\n uint256 amount,\n address to\n ) external override returns (uint256) {\n MockAToken atoken = MockAToken(reserveToAToken[asset]);\n atoken.poolRedeem(amount, to);\n return amount;\n }\n\n function getLendingPool() external view override returns (address) {\n return pool;\n }\n}\n" + }, + "contracts/mocks/MockAaveIncentivesController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { MockStkAave } from \"./MockStkAave.sol\";\n\ncontract MockAaveIncentivesController {\n mapping(address => uint256) private rewards;\n MockStkAave public REWARD_TOKEN;\n\n constructor(address _reward_token) {\n REWARD_TOKEN = MockStkAave(_reward_token);\n }\n\n function setRewardsBalance(address user, uint256 amount) external {\n rewards[user] = amount;\n }\n\n /**\n * @dev Returns the total of rewards of an user, already accrued + not yet accrued\n * @param user The address of the user\n * @return The rewards\n **/\n // solhint-disable-next-line no-unused-vars\n function getRewardsBalance(address[] calldata assets, address user)\n external\n view\n returns (uint256)\n {\n return rewards[user];\n }\n\n /**\n * @dev Claims reward for an user, on all the assets of the lending pool, accumulating the pending rewards\n * @param amount Amount of rewards to claim\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewards(\n // solhint-disable-next-line no-unused-vars\n address[] calldata assets,\n uint256 amount,\n address to\n ) external returns (uint256) {\n require(amount > 0);\n require(rewards[to] == amount);\n REWARD_TOKEN.mint(amount);\n require(REWARD_TOKEN.transfer(to, amount));\n // solhint-disable-next-line reentrancy\n rewards[to] = 0;\n return amount;\n }\n}\n" + }, + "contracts/mocks/MockAAVEToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAAVEToken is MintableERC20 {\n constructor() ERC20(\"AAVE\", \"AAVE\") {}\n}\n" + }, + "contracts/mocks/MockAero.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAero is MintableERC20 {\n constructor() ERC20(\"Aerodrome\", \"AERO\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/MockAura.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAura is MintableERC20 {\n constructor() ERC20(\"Aura\", \"AURA\") {}\n}\n" + }, + "contracts/mocks/MockBAL.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockBAL is MintableERC20 {\n constructor() ERC20(\"Balancer\", \"BAL\") {}\n}\n" + }, + "contracts/mocks/MockBalancerVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// import \"hardhat/console.sol\";\n\ncontract MockBalancerVault {\n using StableMath for uint256;\n uint256 public slippage = 1 ether;\n bool public transferDisabled = false;\n bool public slippageErrorDisabled = false;\n\n function swap(\n IBalancerVault.SingleSwap calldata singleSwap,\n IBalancerVault.FundManagement calldata funds,\n uint256 minAmountOut,\n uint256\n ) external returns (uint256 amountCalculated) {\n amountCalculated = (minAmountOut * slippage) / 1 ether;\n if (!slippageErrorDisabled) {\n require(amountCalculated >= minAmountOut, \"Slippage error\");\n }\n IERC20(singleSwap.assetIn).transferFrom(\n funds.sender,\n address(this),\n singleSwap.amount\n );\n if (!transferDisabled) {\n MintableERC20(singleSwap.assetOut).mintTo(\n funds.recipient,\n amountCalculated\n );\n }\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n\n function getPoolTokenInfo(bytes32 poolId, address token)\n external\n view\n returns (\n uint256,\n uint256,\n uint256,\n address\n )\n {}\n\n function disableTransfer() external {\n transferDisabled = true;\n }\n\n function disableSlippageError() external {\n slippageErrorDisabled = true;\n }\n}\n" + }, + "contracts/mocks/MockBeaconConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconConsolidation } from \"../beacon/BeaconConsolidation.sol\";\n\ncontract MockBeaconConsolidation {\n function fee() external view returns (uint256) {\n return BeaconConsolidation.fee();\n }\n\n function request(bytes calldata source, bytes calldata target)\n external\n returns (uint256 fee_)\n {\n return BeaconConsolidation.request(source, target);\n }\n}\n" + }, + "contracts/mocks/MockBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"../beacon/BeaconProofsLib.sol\";\nimport { BeaconProofs } from \"../beacon/BeaconProofs.sol\";\n\ncontract MockBeaconProofs is BeaconProofs {\n function concatGenIndices(\n uint256 index1,\n uint256 height2,\n uint256 index2\n ) external pure returns (uint256 genIndex) {\n return BeaconProofsLib.concatGenIndices(index1, height2, index2);\n }\n}\n" + }, + "contracts/mocks/MockBeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconRoots } from \"../beacon/BeaconRoots.sol\";\n\ncontract MockBeaconRoots {\n // Mapping to simulate the ring buffer: timestamp => beacon block root\n mapping(uint256 => bytes32) internal _beaconRoots;\n\n // Event to log when a new root is set (for testing)\n event RootSet(uint256 indexed timestamp, bytes32 root);\n\n // Fallback function to handle raw 32-byte timestamp input\n // solhint-disable no-complex-fallback\n fallback() external {\n // Ensure input is exactly 32 bytes (big-endian encoded timestamp)\n require(msg.data.length == 32, \"Input must be 32 bytes\");\n\n // Decode the 32-byte input as a uint256 timestamp (big-endian)\n uint256 timestamp = abi.decode(msg.data, (uint256));\n\n // Don't do any validation of timestamp so we can test any block\n\n // Retrieve the root. Will return bytes32(0) if not set.\n bytes32 root = _beaconRoots[timestamp];\n\n // Return the 32-byte root directly\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0, root)\n return(0, 32)\n }\n }\n\n // Mock function to set a beacon block root (for testing)\n function setBeaconRoot(uint256 timestamp, bytes32 root) external {\n require(timestamp > 0, \"Invalid timestamp\");\n require(root != bytes32(0), \"Invalid root\");\n\n // Store the root at the given timestamp\n _beaconRoots[timestamp] = root;\n\n emit RootSet(timestamp, root);\n }\n\n function setBeaconRoot(bytes32 root) external {\n require(root != bytes32(0), \"Invalid root\");\n\n // Store the root at the given timestamp\n _beaconRoots[block.timestamp] = root;\n\n emit RootSet(block.timestamp, root);\n }\n\n function parentBlockRoot(uint64 timestamp)\n external\n view\n returns (bytes32 parentRoot)\n {\n return BeaconRoots.parentBlockRoot(timestamp);\n }\n\n function latestBlockRoot()\n external\n view\n returns (bytes32 parentRoot, uint64 timestamp)\n {\n timestamp = uint64(block.timestamp);\n parentRoot = BeaconRoots.parentBlockRoot(timestamp);\n }\n}\n" + }, + "contracts/mocks/MockChainlinkOracleFeed.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\ncontract MockChainlinkOracleFeed is AggregatorV3Interface {\n int256 price;\n uint8 numDecimals;\n\n constructor(int256 _price, uint8 _decimals) {\n price = _price;\n numDecimals = _decimals;\n }\n\n function decimals() external view override returns (uint8) {\n return numDecimals;\n }\n\n function description() external pure override returns (string memory) {\n return \"MockOracleEthFeed\";\n }\n\n function version() external pure override returns (uint256) {\n return 1;\n }\n\n function setPrice(int256 _price) public {\n price = _price;\n }\n\n function setDecimals(uint8 _decimals) public {\n numDecimals = _decimals;\n }\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(uint80 _roundId)\n external\n view\n override\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n )\n {\n roundId = _roundId;\n answer = price;\n startedAt = 0;\n updatedAt = 0;\n answeredInRound = 0;\n }\n\n function latestRoundData()\n external\n view\n override\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n )\n {\n roundId = 0;\n answer = price;\n startedAt = 0;\n updatedAt = block.timestamp;\n answeredInRound = 0;\n }\n}\n" + }, + "contracts/mocks/MockCOMP.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockCOMP is MintableERC20 {\n constructor() ERC20(\"COMP\", \"COMP\") {}\n}\n" + }, + "contracts/mocks/MockComptroller.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract MockComptroller {\n // Claim all the COMP accrued by specific holders in specific markets for their supplies and/or borrows\n function claimComp(\n address[] memory holders,\n address[] memory cTokens,\n bool borrowers,\n bool suppliers\n ) external {}\n}\n" + }, + "contracts/mocks/MockCToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { ICERC20 } from \"../strategies/ICompound.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockCToken is ICERC20, ERC20 {\n using StableMath for uint256;\n\n IERC20 public underlyingToken;\n // underlying = cToken * exchangeRate\n // cToken = underlying / exchangeRate\n uint256 exchangeRate;\n address public override comptroller;\n\n constructor(ERC20 _underlyingToken, address _comptroller)\n ERC20(\"cMock\", \"cMK\")\n {\n uint8 underlyingDecimals = _underlyingToken.decimals();\n // if has 18 dp, exchange rate should be 1e26\n // if has 8 dp, exchange rate should be 1e18\n if (underlyingDecimals > 8) {\n exchangeRate = 10**uint256(18 + underlyingDecimals - 10);\n } else if (underlyingDecimals < 8) {\n // e.g. 18-8+6 = 16\n exchangeRate = 10**uint256(18 - 8 + underlyingDecimals);\n } else {\n exchangeRate = 1e18;\n }\n underlyingToken = _underlyingToken;\n comptroller = _comptroller;\n }\n\n function decimals() public pure override returns (uint8) {\n return 8;\n }\n\n function mint(uint256 mintAmount) public override returns (uint256) {\n // Credit them with cToken\n _mint(msg.sender, mintAmount.divPrecisely(exchangeRate));\n // Take their reserve\n underlyingToken.transferFrom(msg.sender, address(this), mintAmount);\n return 0;\n }\n\n function redeem(uint256 redeemAmount) external override returns (uint256) {\n uint256 tokenAmount = redeemAmount.mulTruncate(exchangeRate);\n // Burn the cToken\n _burn(msg.sender, redeemAmount);\n // Transfer underlying to caller\n underlyingToken.transfer(msg.sender, tokenAmount);\n return 0;\n }\n\n function redeemUnderlying(uint256 redeemAmount)\n external\n override\n returns (uint256)\n {\n uint256 cTokens = redeemAmount.divPrecisely(exchangeRate);\n // Burn the cToken\n _burn(msg.sender, cTokens);\n // Transfer underlying to caller\n underlyingToken.transfer(msg.sender, redeemAmount);\n return 0;\n }\n\n function balanceOfUnderlying(address owner)\n external\n view\n override\n returns (uint256)\n {\n uint256 cTokenBal = this.balanceOf(owner);\n return cTokenBal.mulTruncate(exchangeRate);\n }\n\n function balanceOf(address owner)\n public\n view\n override(ICERC20, ERC20)\n returns (uint256)\n {\n return ERC20.balanceOf(owner);\n }\n\n function updateExchangeRate()\n internal\n view\n returns (uint256 newExchangeRate)\n {\n uint256 factor = 100002 * (10**13); // 0.002%\n newExchangeRate = exchangeRate.mulTruncate(factor);\n }\n\n function exchangeRateStored() external view override returns (uint256) {\n return exchangeRate;\n }\n\n function supplyRatePerBlock() external pure override returns (uint256) {\n return 141 * (10**8);\n }\n}\n" + }, + "contracts/mocks/MockCVXLocker.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MockCVXLocker {\n address public immutable cvx;\n mapping(address => uint256) public lockedBalanceOf;\n\n constructor(address _cvx) {\n cvx = _cvx;\n }\n\n function lock(\n address _account,\n uint256 _amount,\n uint256\n ) external {\n lockedBalanceOf[_account] += _amount;\n ERC20(cvx).transferFrom(msg.sender, address(this), _amount);\n }\n\n function unlockAllTokens(address _account) external {\n lockedBalanceOf[_account] = 0;\n ERC20(cvx).transfer(_account, lockedBalanceOf[_account]);\n }\n}\n" + }, + "contracts/mocks/MockDAI.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockDAI is MintableERC20 {\n constructor() ERC20(\"DAI\", \"DAI\") {}\n}\n" + }, + "contracts/mocks/MockDepositContract.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IDepositContract } from \"./../interfaces/IDepositContract.sol\";\n\ncontract MockDepositContract is IDepositContract {\n uint256 deposit_count;\n\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable override {\n require(pubkey.length == 48, \"DepositContract: invalid pubkey length\");\n require(\n withdrawal_credentials.length == 32,\n \"DepositContract: invalid withdrawal_credentials length\"\n );\n require(\n signature.length == 96,\n \"DepositContract: invalid signature length\"\n );\n\n // Check deposit amount\n require(msg.value >= 1 ether, \"DepositContract: deposit value too low\");\n require(\n msg.value % 1 gwei == 0,\n \"DepositContract: deposit value not multiple of gwei\"\n );\n uint256 deposit_amount = msg.value / 1 gwei;\n require(\n deposit_amount <= type(uint64).max,\n \"DepositContract: deposit value too high\"\n );\n\n // Emit `DepositEvent` log\n bytes memory amount = to_little_endian_64(uint64(deposit_amount));\n emit DepositEvent(\n pubkey,\n withdrawal_credentials,\n amount,\n signature,\n to_little_endian_64(uint64(deposit_count))\n );\n require(\n deposit_data_root != 0,\n \"DepositContract: invalid deposit_data_root\"\n );\n }\n\n function get_deposit_root() external view override returns (bytes32) {\n // just return some bytes32\n return sha256(abi.encodePacked(deposit_count, bytes16(0)));\n }\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view override returns (bytes memory) {\n return to_little_endian_64(uint64(deposit_count));\n }\n\n function to_little_endian_64(uint64 value)\n internal\n pure\n returns (bytes memory ret)\n {\n ret = new bytes(8);\n bytes8 bytesValue = bytes8(value);\n // Byteswapping during copying to bytes.\n ret[0] = bytesValue[7];\n ret[1] = bytesValue[6];\n ret[2] = bytesValue[5];\n ret[3] = bytesValue[4];\n ret[4] = bytesValue[3];\n ret[5] = bytesValue[2];\n ret[6] = bytesValue[1];\n ret[7] = bytesValue[0];\n }\n}\n" + }, + "contracts/mocks/MockEvilDAI.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract MockEvilDAI is MintableERC20 {\n address host;\n address realCoin;\n\n constructor(address _host, address _realCoin) ERC20(\"DAI\", \"DAI\") {\n host = _host;\n realCoin = _realCoin;\n }\n\n function transferFrom(\n // solhint-disable-next-line no-unused-vars\n address _from,\n // solhint-disable-next-line no-unused-vars\n address _to,\n uint256 _amount\n ) public override returns (bool) {\n // call mint again!\n if (_amount != 69) {\n IVault(host).mint(address(this), 69, 0);\n }\n return true;\n }\n}\n" + }, + "contracts/mocks/MockEvilReentrantContract.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IRateProvider } from \"../interfaces/balancer/IRateProvider.sol\";\n\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { IERC20 } from \"../utils/InitializableAbstractStrategy.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockEvilReentrantContract {\n using StableMath for uint256;\n\n IBalancerVault public immutable balancerVault;\n IERC20 public immutable reth;\n IERC20 public immutable weth;\n IVault public immutable oethVault;\n address public immutable poolAddress;\n bytes32 public immutable balancerPoolId;\n\n constructor(\n address _balancerVault,\n address _oethVault,\n address _reth,\n address _weth,\n address _poolAddress,\n bytes32 _poolId\n ) {\n balancerVault = IBalancerVault(_balancerVault);\n oethVault = IVault(_oethVault);\n reth = IERC20(_reth);\n weth = IERC20(_weth);\n poolAddress = _poolAddress;\n balancerPoolId = _poolId;\n }\n\n function doEvilStuff() public {\n address priceProvider = oethVault.priceProvider();\n uint256 rethPrice = IOracle(priceProvider).price(address(reth));\n\n // 1. Join pool\n uint256[] memory amounts = new uint256[](2);\n amounts[0] = uint256(10 ether);\n amounts[1] = rethPrice * 10;\n\n address[] memory assets = new address[](2);\n assets[0] = address(reth);\n assets[1] = address(weth);\n\n uint256 minBPT = getBPTExpected(assets, amounts).mulTruncate(\n 0.99 ether\n );\n\n bytes memory joinUserData = abi.encode(\n IBalancerVault.WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,\n amounts,\n minBPT\n );\n\n IBalancerVault.JoinPoolRequest memory joinRequest = IBalancerVault\n .JoinPoolRequest(assets, amounts, joinUserData, false);\n\n balancerVault.joinPool(\n balancerPoolId,\n address(this),\n address(this),\n joinRequest\n );\n\n uint256 bptTokenBalance = IERC20(poolAddress).balanceOf(address(this));\n\n // 2. Redeem as ETH\n bytes memory exitUserData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT,\n bptTokenBalance,\n 1\n );\n\n assets[1] = address(0); // Receive ETH instead of WETH\n uint256[] memory exitAmounts = new uint256[](2);\n exitAmounts[1] = 15 ether;\n IBalancerVault.ExitPoolRequest memory exitRequest = IBalancerVault\n .ExitPoolRequest(assets, exitAmounts, exitUserData, false);\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n payable(address(this)),\n exitRequest\n );\n bptTokenBalance = IERC20(poolAddress).balanceOf(address(this));\n }\n\n function getBPTExpected(address[] memory _assets, uint256[] memory _amounts)\n internal\n view\n virtual\n returns (uint256 bptExpected)\n {\n // Get the oracle from the OETH Vault\n address priceProvider = oethVault.priceProvider();\n\n for (uint256 i = 0; i < _assets.length; ++i) {\n uint256 strategyAssetMarketPrice = IOracle(priceProvider).price(\n _assets[i]\n );\n // convert asset amount to ETH amount\n bptExpected =\n bptExpected +\n _amounts[i].mulTruncate(strategyAssetMarketPrice);\n }\n\n uint256 bptRate = IRateProvider(poolAddress).getRate();\n // Convert ETH amount to BPT amount\n bptExpected = bptExpected.divPrecisely(bptRate);\n }\n\n function approveAllTokens() public {\n // Approve all tokens\n weth.approve(address(oethVault), type(uint256).max);\n reth.approve(poolAddress, type(uint256).max);\n weth.approve(poolAddress, type(uint256).max);\n reth.approve(address(balancerVault), type(uint256).max);\n weth.approve(address(balancerVault), type(uint256).max);\n }\n\n receive() external payable {\n // 3. Try to mint OETH\n oethVault.mint(address(weth), 1 ether, 0.9 ether);\n }\n}\n" + }, + "contracts/mocks/MockfrxETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockfrxETH is MintableERC20 {\n constructor() ERC20(\"frxETH\", \"frxETH\") {}\n}\n" + }, + "contracts/mocks/MockFrxETHMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockFrxETHMinter {\n address public immutable frxETH;\n address public immutable sfrxETH;\n\n constructor(address _frxETH, address _sfrxETH) {\n frxETH = _frxETH;\n sfrxETH = _sfrxETH;\n }\n\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares)\n {\n IMintableERC20(frxETH).mintTo(sfrxETH, msg.value);\n IMintableERC20(sfrxETH).mintTo(recipient, msg.value);\n shares = msg.value;\n }\n}\n" + }, + "contracts/mocks/MockLimitedWrappedOusd.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WrappedOusd } from \"../token/WrappedOusd.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MockLimitedWrappedOusd is WrappedOusd {\n constructor(ERC20 underlying_) WrappedOusd(underlying_) {}\n\n function maxDeposit(address)\n public\n view\n virtual\n override\n returns (uint256)\n {\n return 1e18;\n }\n}\n" + }, + "contracts/mocks/MockMaverickDistributor.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Pool } from \"../interfaces/plume/IMaverickV2Pool.sol\";\n\ncontract MockMaverickDistributor {\n IERC20 public immutable rewardToken;\n\n uint256 public lastEpoch;\n uint256 public rewardAmount;\n\n constructor(address _rewardToken) {\n rewardToken = IERC20(_rewardToken);\n }\n\n function setLastEpoch(uint256 _epoch) external {\n lastEpoch = _epoch;\n }\n\n function setRewardTokenAmount(uint256 _amount) external {\n rewardAmount = _amount;\n }\n\n function claimLp(\n address recipient,\n uint256,\n IMaverickV2Pool,\n uint32[] memory,\n uint256\n ) external returns (uint256 amount) {\n rewardToken.transfer(recipient, rewardAmount);\n return rewardAmount;\n }\n}\n" + }, + "contracts/mocks/MockMetadataToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// IERC20Metadata is used in the resolveAsset function in contracts/utils/assets.js\n// We just need to import it here to make its ABI available to Hardhat\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n" + }, + "contracts/mocks/MockMintableUniswapPair.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport \"./MockUniswapPair.sol\";\n\ncontract MockMintableUniswapPair is MockUniswapPair, MintableERC20 {\n constructor(\n address _token0,\n address _token1,\n uint112 _reserve0,\n uint112 _reserve1\n )\n MockUniswapPair(_token0, _token1, _reserve0, _reserve1)\n ERC20(\"Uniswap V2\", \"UNI-v2\")\n {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/MockNonRebasing.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { OUSD } from \"../token/OUSD.sol\";\n\ncontract MockNonRebasing {\n OUSD oUSD;\n\n function setOUSD(address _oUSDAddress) public {\n oUSD = OUSD(_oUSDAddress);\n }\n\n function rebaseOptIn() public {\n oUSD.rebaseOptIn();\n }\n\n function rebaseOptOut() public {\n oUSD.rebaseOptOut();\n }\n\n function transfer(address _to, uint256 _value) public {\n oUSD.transfer(_to, _value);\n }\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) public {\n oUSD.transferFrom(_from, _to, _value);\n }\n\n function approve(address _spender, uint256 _addedValue) public {\n oUSD.approve(_spender, _addedValue);\n }\n\n function mintOusd(\n address _vaultContract,\n address _asset,\n uint256 _amount\n ) public {\n IVault(_vaultContract).mint(_asset, _amount, 0);\n }\n\n function redeemOusd(address _vaultContract, uint256 _amount) public {\n IVault(_vaultContract).redeem(_amount, 0);\n }\n\n function approveFor(\n address _contract,\n address _spender,\n uint256 _addedValue\n ) public {\n IERC20(_contract).approve(_spender, _addedValue);\n }\n}\n" + }, + "contracts/mocks/MockNonStandardToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\nimport \"./MintableERC20.sol\";\n\n/**\n * Mock token contract to simulate tokens that don't\n * throw/revert when a transfer/transferFrom call fails\n */\ncontract MockNonStandardToken is MintableERC20 {\n using SafeMath for uint256;\n\n constructor() ERC20(\"NonStandardToken\", \"NonStandardToken\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n\n function transfer(address recipient, uint256 amount)\n public\n override\n returns (bool)\n {\n if (balanceOf(msg.sender) < amount) {\n // Fail silently\n return false;\n }\n\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public override returns (bool) {\n if (balanceOf(sender) < amount) {\n // Fail silently\n return false;\n }\n\n _transfer(sender, recipient, amount);\n _approve(\n sender,\n _msgSender(),\n allowance(sender, _msgSender()).sub(\n amount,\n \"ERC20: transfer amount exceeds allowance\"\n )\n );\n return true;\n }\n}\n" + }, + "contracts/mocks/MockOETHVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultCore } from \"../vault/OETHVaultCore.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MockOETHVault is OETHVaultCore {\n using StableMath for uint256;\n\n constructor(address _weth) OETHVaultCore(_weth) {\n _setGovernor(msg.sender);\n }\n\n function supportAsset(address asset) external {\n assets[asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(0),\n decimals: 18,\n allowedOracleSlippageBps: 0\n });\n\n allAssets.push(asset);\n }\n}\n" + }, + "contracts/mocks/MockOETHVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"../vault/OETHVaultAdmin.sol\";\n\ncontract MockOETHVaultAdmin is OETHVaultAdmin {\n constructor(address _weth) OETHVaultAdmin(_weth) {}\n\n // fetches the WETH amount in outstanding withdrawals\n function outstandingWithdrawalsAmount()\n external\n view\n returns (uint256 wethAmount)\n {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n wethAmount = queue.queued - queue.claimed;\n }\n\n function wethAvailable() external view returns (uint256) {\n return _wethAvailable();\n }\n}\n" + }, + "contracts/mocks/MockOGN.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./BurnableERC20.sol\";\nimport \"./MintableERC20.sol\";\n\n/**\n * @title Origin token (OGN).\n *\n * @dev Token that allows minting and burning.\n * @dev Important note:\n * @dev There is a known race condition in the ERC20 standard on the approve() method.\n * @dev See details: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n * @dev The Origin token contract implements the increaseApproval() and decreaseApproval() methods.\n * @dev It is strongly recommended to use those methods rather than approve()\n * @dev when updating the token allowance.\n */\ncontract MockOGN is MintableERC20, BurnableERC20 {\n event SetWhitelistExpiration(uint256 expiration);\n event AllowedTransactorAdded(address sender);\n event AllowedTransactorRemoved(address sender);\n event AddCallSpenderWhitelist(address enabler, address spender);\n event RemoveCallSpenderWhitelist(address disabler, address spender);\n\n mapping(address => bool) public callSpenderWhitelist;\n address public owner = msg.sender;\n // UNIX timestamp (in seconds) after which this whitelist no longer applies\n uint256 public whitelistExpiration;\n // While the whitelist is active, either the sender or recipient must be\n // in allowedTransactors.\n mapping(address => bool) public allowedTransactors;\n\n // @dev Constructor that gives msg.sender all initial tokens.\n constructor(uint256 _initialSupply) ERC20(\"OriginToken\", \"OGN\") {\n owner = msg.sender;\n _mint(owner, _initialSupply);\n }\n\n //\n // approveAndCall methods\n //\n\n // @dev Add spender to whitelist of spenders for approveAndCall\n // @param _spender Address to add\n function addCallSpenderWhitelist(address _spender) public onlyOwner {\n callSpenderWhitelist[_spender] = true;\n emit AddCallSpenderWhitelist(msg.sender, _spender);\n }\n\n // @dev Remove spender from whitelist of spenders for approveAndCall\n // @param _spender Address to remove\n function removeCallSpenderWhitelist(address _spender) public onlyOwner {\n delete callSpenderWhitelist[_spender];\n emit RemoveCallSpenderWhitelist(msg.sender, _spender);\n }\n\n // @dev Approve transfer of tokens and make a contract call in a single\n // @dev transaction. This allows a DApp to avoid requiring two MetaMask\n // @dev approvals for a single logical action, such as creating a listing,\n // @dev which requires the seller to approve a token transfer and the\n // @dev marketplace contract to transfer tokens from the seller.\n //\n // @dev This is based on the ERC827 function approveAndCall and avoids\n // @dev security issues by only working with a whitelisted set of _spender\n // @dev addresses. The other difference is that the combination of this\n // @dev function ensures that the proxied function call receives the\n // @dev msg.sender for this function as its first parameter.\n //\n // @param _spender The address that will spend the funds.\n // @param _value The amount of tokens to be spent.\n // @param _selector Function selector for function to be called.\n // @param _callParams Packed, encoded parameters, omitting the first parameter which is always msg.sender\n function approveAndCallWithSender(\n address _spender,\n uint256 _value,\n bytes4 _selector,\n bytes memory _callParams\n ) public payable returns (bool) {\n require(_spender != address(this), \"token contract can't be approved\");\n require(callSpenderWhitelist[_spender], \"spender not in whitelist\");\n\n require(super.approve(_spender, _value), \"approve failed\");\n\n bytes memory callData = abi.encodePacked(\n _selector,\n uint256(uint160(msg.sender)),\n _callParams\n );\n // solium-disable-next-line security/no-call-value\n (bool success, ) = _spender.call{ value: msg.value }(callData);\n require(success, \"proxied call failed\");\n return true;\n }\n\n //\n // Functions for maintaining whitelist\n //\n\n modifier onlyOwner() {\n require(msg.sender == owner);\n _;\n }\n modifier allowedTransfer(address _from, address _to) {\n require(\n // solium-disable-next-line operator-whitespace\n !whitelistActive() ||\n allowedTransactors[_from] ||\n allowedTransactors[_to],\n \"neither sender nor recipient are allowed\"\n );\n _;\n }\n\n function whitelistActive() public view returns (bool) {\n return block.timestamp < whitelistExpiration;\n }\n\n function addAllowedTransactor(address _transactor) public onlyOwner {\n emit AllowedTransactorAdded(_transactor);\n allowedTransactors[_transactor] = true;\n }\n\n function removeAllowedTransactor(address _transactor) public onlyOwner {\n emit AllowedTransactorRemoved(_transactor);\n delete allowedTransactors[_transactor];\n }\n\n /**\n * @dev Set the whitelist expiration, after which the whitelist no longer\n * applies.\n */\n function setWhitelistExpiration(uint256 _expiration) public onlyOwner {\n // allow only if whitelist expiration hasn't yet been set, or if the\n // whitelist expiration hasn't passed yet\n require(\n whitelistExpiration == 0 || whitelistActive(),\n \"an expired whitelist cannot be extended\"\n );\n // prevent possible mistakes in calling this function\n require(\n _expiration >= block.timestamp + 1 days,\n \"whitelist expiration not far enough into the future\"\n );\n emit SetWhitelistExpiration(_expiration);\n whitelistExpiration = _expiration;\n }\n\n //\n // ERC20 transfer functions that have been overridden to enforce the\n // whitelist.\n //\n\n function transfer(address _to, uint256 _value)\n public\n override\n allowedTransfer(msg.sender, _to)\n returns (bool)\n {\n return super.transfer(_to, _value);\n }\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) public override allowedTransfer(_from, _to) returns (bool) {\n return super.transferFrom(_from, _to, _value);\n }\n}\n" + }, + "contracts/mocks/MockOGV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockOGV is MintableERC20 {\n constructor() ERC20(\"OGV\", \"OGV\") {}\n}\n" + }, + "contracts/mocks/MockOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/IPriceOracle.sol\";\nimport \"../interfaces/IMinMaxOracle.sol\";\n\n/**\n * Mock of both price Oracle and min max oracles\n */\ncontract MockOracle is IPriceOracle, IMinMaxOracle {\n mapping(bytes32 => uint256) prices;\n mapping(bytes32 => uint256[]) pricesMinMax;\n uint256 ethMin;\n uint256 ethMax;\n\n /**\n * @dev returns the asset price in USD, 6 decimal digits.\n * Compatible with the Open Price Feed.\n */\n function price(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n return prices[keccak256(abi.encodePacked(symbol))];\n }\n\n /**\n * @dev sets the price of the asset in USD, 6 decimal digits\n *\n */\n function setPrice(string calldata symbol, uint256 _price) external {\n prices[keccak256(abi.encodePacked(symbol))] = _price;\n }\n\n /**\n * @dev sets the min and max price of ETH in USD, 6 decimal digits\n *\n */\n function setEthPriceMinMax(uint256 _min, uint256 _max) external {\n ethMin = _min;\n ethMax = _max;\n }\n\n /**\n * @dev sets the prices Min Max for a specific symbol in ETH, 8 decimal digits\n *\n */\n function setTokPriceMinMax(\n string calldata symbol,\n uint256 _min,\n uint256 _max\n ) external {\n pricesMinMax[keccak256(abi.encodePacked(symbol))] = [_min, _max];\n }\n\n /**\n * @dev get the price of asset in ETH, 8 decimal digits.\n */\n function priceMin(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n uint256[] storage pMinMax = pricesMinMax[\n keccak256(abi.encodePacked(symbol))\n ];\n return (pMinMax[0] * ethMin) / 1e6;\n }\n\n /**\n * @dev get the price of asset in USD, 8 decimal digits.\n * Not needed for now\n */\n function priceMax(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n uint256[] storage pMinMax = pricesMinMax[\n keccak256(abi.encodePacked(symbol))\n ];\n return (pMinMax[1] * ethMax) / 1e6;\n }\n}\n" + }, + "contracts/mocks/MockOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"../oracle/AbstractOracleRouter.sol\";\n\n// @notice Oracle Router required for testing environment\ncontract MockOracleRouter is AbstractOracleRouter {\n struct FeedMetadata {\n address feedAddress;\n uint256 maxStaleness;\n }\n\n mapping(address => FeedMetadata) public assetToFeedMetadata;\n\n /* @dev Override feed and maxStaleness information for a particular asset\n * @param _asset the asset to override feed for\n * @param _feed new feed\n * @param _maxStaleness new maximum time allowed for feed data to be stale\n */\n function setFeed(\n address _asset,\n address _feed,\n uint256 _maxStaleness\n ) external {\n assetToFeedMetadata[_asset] = FeedMetadata(_feed, _maxStaleness);\n }\n\n /*\n * The dev version of the Oracle doesn't need to gas optimize and cache the decimals\n */\n function getDecimals(address _feed) internal view override returns (uint8) {\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n return AggregatorV3Interface(_feed).decimals();\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n FeedMetadata storage fm = assetToFeedMetadata[asset];\n feedAddress = fm.feedAddress;\n maxStaleness = fm.maxStaleness;\n }\n}\n" + }, + "contracts/mocks/MockOracleRouterNoStale.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { OracleRouter } from \"../oracle/OracleRouter.sol\";\nimport { OETHOracleRouter } from \"../oracle/OETHOracleRouter.sol\";\n\n// @notice Oracle Router used to bypass staleness\ncontract MockOracleRouterNoStale is OracleRouter {\n function feedMetadata(address asset)\n internal\n pure\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n (feedAddress, ) = super.feedMetadata(asset);\n maxStaleness = 365 days;\n }\n}\n\n// @notice Oracle Router used to bypass staleness\ncontract MockOETHOracleRouterNoStale is OETHOracleRouter {\n constructor() OETHOracleRouter() {}\n\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n (feedAddress, ) = super.feedMetadata(asset);\n maxStaleness = 365 days;\n }\n}\n" + }, + "contracts/mocks/MockOracleWeightedPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Variable, OracleAverageQuery, IOracleWeightedPool } from \"../interfaces/balancer/IOracleWeightedPool.sol\";\n\ncontract MockOracleWeightedPool is IOracleWeightedPool {\n uint256[] public nextResults;\n\n constructor() {\n nextResults = [1 ether, 1 ether];\n }\n\n function getTimeWeightedAverage(OracleAverageQuery[] memory)\n external\n view\n override\n returns (uint256[] memory results)\n {\n return nextResults;\n }\n\n function setNextResults(uint256[] calldata results) external {\n nextResults = results;\n }\n}\n" + }, + "contracts/mocks/MockPartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PartialWithdrawal } from \"../beacon/PartialWithdrawal.sol\";\n\ncontract MockPartialWithdrawal {\n function fee() external view returns (uint256) {\n return PartialWithdrawal.fee();\n }\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n external\n returns (uint256 fee_)\n {\n return PartialWithdrawal.request(validatorPubKey, amount);\n }\n}\n" + }, + "contracts/mocks/MockRebornMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n// solhint-disable-next-line no-console\nimport \"hardhat/console.sol\";\n\ncontract Sanctum {\n address public asset;\n address public vault;\n address public reborner;\n bool public shouldAttack = false;\n // should selfdestruct in the constructor\n bool public shouldDestruct = false;\n uint256 public targetMethod;\n address public ousdContract;\n\n constructor(address _asset, address _vault) {\n asset = _asset;\n vault = _vault;\n }\n\n function deploy(uint256 salt, bytes memory bytecode)\n public\n returns (address addr)\n {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt)\n }\n require(addr != address(0), \"Create2: Failed on deploy\");\n }\n\n function computeAddress(uint256 salt, bytes memory bytecode)\n public\n view\n returns (address)\n {\n bytes32 bytecodeHashHash = keccak256(bytecode);\n bytes32 _data = keccak256(\n abi.encodePacked(\n bytes1(0xff),\n address(this),\n salt,\n bytecodeHashHash\n )\n );\n return address(bytes20(_data << 96));\n }\n\n function setShouldAttack(bool _shouldAttack) public {\n shouldAttack = _shouldAttack;\n }\n\n // should call selfdestruct in the constructor\n function setShouldDesctruct(bool _shouldDestruct) public {\n shouldDestruct = _shouldDestruct;\n }\n\n function setTargetMethod(uint256 target) public {\n targetMethod = target;\n }\n\n function setOUSDAddress(address _ousdContract) public {\n ousdContract = _ousdContract;\n }\n}\n\ncontract Reborner {\n Sanctum sanctum;\n bool logging = false;\n\n constructor(address _sanctum) {\n log(\"We are created...\");\n sanctum = Sanctum(_sanctum);\n if (sanctum.shouldAttack()) {\n log(\"We are attacking now...\");\n\n uint256 target = sanctum.targetMethod();\n\n if (target == 1) {\n redeem();\n } else if (target == 2) {\n transfer();\n } else {\n mint();\n }\n }\n\n if (sanctum.shouldDestruct()) {\n bye();\n }\n }\n\n function mint() public {\n log(\"We are attempting to mint..\");\n address asset = sanctum.asset();\n address vault = sanctum.vault();\n IERC20(asset).approve(vault, 1e18);\n IVault(vault).mint(asset, 1e18, 0);\n log(\"We are now minting..\");\n }\n\n function redeem() public {\n log(\"We are attempting to redeem..\");\n address vault = sanctum.vault();\n IVault(vault).redeem(1e18, 1e18);\n log(\"We are now redeeming..\");\n }\n\n function transfer() public {\n log(\"We are attempting to transfer..\");\n address ousd = sanctum.ousdContract();\n require(IERC20(ousd).transfer(address(1), 1e18), \"transfer failed\");\n log(\"We are now transfering..\");\n }\n\n function bye() public {\n log(\"We are now destructing..\");\n selfdestruct(payable(msg.sender));\n }\n\n function log(string memory message) internal view {\n if (logging) {\n // solhint-disable-next-line no-console\n console.log(message);\n }\n }\n}\n" + }, + "contracts/mocks/MockRETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport \"../interfaces/IGetExchangeRateToken.sol\";\n\ncontract MockRETH is MintableERC20, IGetExchangeRateToken {\n uint256 private exchangeRate = 12e17;\n\n constructor() ERC20(\"Rocket Pool ETH\", \"rETH\") {}\n\n function getExchangeRate() external view override returns (uint256) {\n return exchangeRate;\n }\n\n function setExchangeRate(uint256 _rate) external {\n exchangeRate = _rate;\n }\n}\n" + }, + "contracts/mocks/MockRoosterAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Rooster AMO strategy exposing extra functionality\n * @author Origin Protocol Inc\n */\n\nimport { RoosterAMOStrategy } from \"../strategies/plume/RoosterAMOStrategy.sol\";\nimport { IMaverickV2Pool } from \"../interfaces/plume/IMaverickV2Pool.sol\";\n\ncontract MockRoosterAMOStrategy is RoosterAMOStrategy {\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethpAddress,\n address _liquidityManager,\n address _poolLens,\n address _maverickPosition,\n address _maverickQuoter,\n address _mPool,\n bool _upperTickAtParity,\n address _votingDistributor,\n address _poolDistributor\n )\n RoosterAMOStrategy(\n _stratConfig,\n _wethAddress,\n _oethpAddress,\n _liquidityManager,\n _poolLens,\n _maverickPosition,\n _maverickQuoter,\n _mPool,\n _upperTickAtParity,\n _votingDistributor,\n _poolDistributor\n )\n {}\n\n function getCurrentWethShare() external view returns (uint256) {\n uint256 _currentPrice = getPoolSqrtPrice();\n\n return _getWethShare(_currentPrice);\n }\n}\n" + }, + "contracts/mocks/MockSFC.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockSFC {\n error ZeroAmount();\n error TransferFailed();\n error StakeIsFullySlashed();\n\n // Mapping of delegator address to validator ID to amount delegated\n mapping(address => mapping(uint256 => uint256)) public delegations;\n // Mapping of delegator address to validator ID to withdrawal request ID to amount\n mapping(address => mapping(uint256 => mapping(uint256 => uint256)))\n public withdraws;\n // validator ID -> slashing refund ratio (allows to withdraw slashed stake)\n mapping(uint256 => uint256) public slashingRefundRatio;\n\n function getStake(address delegator, uint256 validatorID)\n external\n view\n returns (uint256)\n {\n return delegations[delegator][validatorID];\n }\n\n function delegate(uint256 validatorID) external payable {\n if (msg.value == 0) {\n revert ZeroAmount();\n }\n delegations[msg.sender][validatorID] += msg.value;\n }\n\n function undelegate(\n uint256 validatorID,\n uint256 wrID,\n uint256 amount\n ) external {\n require(\n delegations[msg.sender][validatorID] >= amount,\n \"insufficient stake\"\n );\n require(\n withdraws[msg.sender][validatorID][wrID] == 0,\n \"withdrawal request already exists\"\n );\n\n delegations[msg.sender][validatorID] -= amount;\n withdraws[msg.sender][validatorID][wrID] = amount;\n }\n\n function withdraw(uint256 validatorID, uint256 wrID) external {\n require(withdraws[msg.sender][validatorID][wrID] > 0, \"no withdrawal\");\n\n uint256 withdrawAmount = withdraws[msg.sender][validatorID][wrID];\n uint256 penalty = (withdrawAmount *\n (1e18 - slashingRefundRatio[validatorID])) / 1e18;\n\n if (penalty >= withdrawAmount) {\n revert StakeIsFullySlashed();\n }\n\n (bool sent, ) = msg.sender.call{ value: withdrawAmount - penalty }(\"\");\n if (!sent) {\n revert TransferFailed();\n }\n }\n\n function pendingRewards(address delegator, uint256 validatorID)\n external\n view\n returns (uint256)\n {}\n\n function claimRewards(uint256 validatorID) external {}\n\n function restakeRewards(uint256 validatorID) external {}\n\n /// @param refundRatio the percentage of the staked amount that can be refunded. 0.1e18 = 10%, 1e18 = 100%\n function slashValidator(uint256 validatorID, uint256 refundRatio) external {\n require(refundRatio <= 1e18, \"invalid refund ratio\");\n slashingRefundRatio[validatorID] = refundRatio;\n }\n}\n" + }, + "contracts/mocks/MocksfrxETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MocksfrxETH is MintableERC20 {\n address public frxETH;\n\n constructor(address _frxETH) ERC20(\"sfrxETH\", \"sfrxETH\") {\n frxETH = _frxETH;\n }\n\n function setMockfrxETHAddress(address _frxETH) external {\n frxETH = _frxETH;\n }\n\n function deposit(uint256 assets, address receiver)\n external\n returns (uint256 shares)\n {\n ERC20(frxETH).transferFrom(msg.sender, address(this), assets);\n\n _mint(receiver, assets);\n\n return assets;\n }\n\n function maxWithdraw(address owner) external view returns (uint256) {\n return balanceOf(owner);\n }\n\n function setMaxWithdrawableBalance(address owner, uint256 balance)\n external\n {\n uint256 currentBalance = balanceOf(owner);\n if (currentBalance > balance) {\n _burn(owner, currentBalance - balance);\n } else if (balance > currentBalance) {\n _mint(owner, balance - currentBalance);\n }\n }\n\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets) {\n _burn(owner, shares);\n\n ERC20(frxETH).transfer(receiver, shares);\n\n assets = shares;\n }\n\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares) {\n _burn(owner, assets);\n\n ERC20(frxETH).transfer(receiver, assets);\n\n shares = assets;\n }\n\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares)\n {\n _mint(recipient, msg.value);\n shares = msg.value;\n }\n}\n" + }, + "contracts/mocks/MockSSV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockSSV is MintableERC20 {\n constructor() ERC20(\"SSV Token\", \"SSV\") {}\n}\n" + }, + "contracts/mocks/MockSSVNetwork.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Cluster } from \"./../interfaces/ISSVNetwork.sol\";\n\ncontract MockSSVNetwork {\n function registerValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function exitValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds\n ) external {}\n\n function removeValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external {}\n\n function deposit(\n address clusterOwner,\n uint64[] calldata operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function setFeeRecipientAddress(address recipient) external {}\n}\n" + }, + "contracts/mocks/MockstETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockstETH is MintableERC20 {\n constructor() ERC20(\"stETH\", \"stETH\") {}\n}\n" + }, + "contracts/mocks/MockStkAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"./MintableERC20.sol\";\n\ncontract MockStkAave is MintableERC20 {\n uint256 public COOLDOWN_SECONDS = 864000;\n uint256 public UNSTAKE_WINDOW = 172800;\n address public STAKED_TOKEN;\n\n mapping(address => uint256) public stakerRewardsToClaim;\n mapping(address => uint256) public stakersCooldowns;\n\n using SafeERC20 for IERC20;\n\n constructor(address _stakedToken) ERC20(\"Staked Aave\", \"stkAAVE\") {\n STAKED_TOKEN = _stakedToken;\n }\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n\n function setStakedToken(address _stakedToken) external {\n STAKED_TOKEN = _stakedToken;\n }\n\n /**\n * @dev Redeems staked tokens, and stop earning rewards\n * @param to Address to redeem to\n * @param amount Amount to redeem\n **/\n function redeem(address to, uint256 amount) external {\n uint256 cooldownStartTimestamp = stakersCooldowns[msg.sender];\n uint256 windowStart = cooldownStartTimestamp + COOLDOWN_SECONDS;\n require(amount != 0, \"INVALID_ZERO_AMOUNT\");\n require(block.timestamp > windowStart, \"INSUFFICIENT_COOLDOWN\");\n require(\n block.timestamp - windowStart <= UNSTAKE_WINDOW,\n \"UNSTAKE_WINDOW_FINISHED\"\n );\n uint256 balanceOfMessageSender = balanceOf(msg.sender);\n uint256 amountToRedeem = (amount > balanceOfMessageSender)\n ? balanceOfMessageSender\n : amount;\n\n stakersCooldowns[msg.sender] = 0;\n _burn(msg.sender, amountToRedeem);\n IERC20(STAKED_TOKEN).safeTransfer(to, amountToRedeem);\n }\n\n /**\n * @dev Activates the cooldown period to unstake\n * - It can't be called if the user is not staking\n **/\n function cooldown() external {\n require(balanceOf(msg.sender) != 0, \"INVALID_BALANCE_ON_COOLDOWN\");\n stakersCooldowns[msg.sender] = block.timestamp;\n }\n\n /**\n * @dev Test helper function to allow changing the cooldown\n **/\n function setCooldown(address account, uint256 _cooldown) external {\n stakersCooldowns[account] = _cooldown;\n }\n}\n" + }, + "contracts/mocks/MockStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract MockStrategy {\n address[] public assets;\n\n address public withdrawAllAsset;\n address public withdrawAllRecipient;\n\n constructor() {}\n\n function deposit(address asset, uint256 amount) external {}\n\n function depositAll() external {}\n\n function withdraw(\n address recipient,\n address asset,\n uint256 amount\n ) external {\n IERC20(asset).transfer(recipient, amount);\n }\n\n function withdrawAll() external {\n IERC20(withdrawAllAsset).transfer(\n withdrawAllRecipient,\n IERC20(withdrawAllAsset).balanceOf(address(this))\n );\n }\n\n function checkBalance(address asset)\n external\n view\n returns (uint256 balance)\n {\n balance = IERC20(asset).balanceOf(address(this));\n }\n\n function supportsAsset(address) external view returns (bool) {\n return true;\n }\n\n function collectRewardTokens() external {}\n\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return new address[](0);\n }\n\n function setWithdrawAll(address asset, address recipient) external {\n withdrawAllAsset = asset;\n withdrawAllRecipient = recipient;\n }\n}\n" + }, + "contracts/mocks/MockSwapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IMintableERC20 } from \"./MintableERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract MockSwapper {\n uint256 public nextOutAmount;\n\n function swap(\n // solhint-disable-next-line no-unused-vars\n address _fromAsset,\n address _toAsset,\n // solhint-disable-next-line no-unused-vars\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n // solhint-disable-next-line no-unused-vars\n bytes calldata _data\n ) external returns (uint256 toAssetAmount) {\n toAssetAmount = (nextOutAmount > 0) ? nextOutAmount : _minToAssetAmount;\n nextOutAmount = 0;\n IMintableERC20(_toAsset).mint(toAssetAmount);\n IERC20(_toAsset).transfer(msg.sender, toAssetAmount);\n }\n\n function setNextOutAmount(uint256 _nextOutAmount) public {\n nextOutAmount = _nextOutAmount;\n }\n}\n" + }, + "contracts/mocks/MockTUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockTUSD is MintableERC20 {\n constructor() ERC20(\"TrueUSD\", \"TUSD\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/MockUniswapPair.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IUniswapV2Pair } from \"../interfaces/uniswap/IUniswapV2Pair.sol\";\n\ncontract MockUniswapPair is IUniswapV2Pair {\n address tok0;\n address tok1;\n uint112 reserve0;\n uint112 reserve1;\n uint256 blockTimestampLast;\n\n bool public hasSynced = false;\n\n constructor(\n address _token0,\n address _token1,\n uint112 _reserve0,\n uint112 _reserve1\n ) {\n tok0 = _token0;\n tok1 = _token1;\n reserve0 = _reserve0;\n reserve1 = _reserve1;\n blockTimestampLast = block.timestamp;\n }\n\n function token0() external view override returns (address) {\n return tok0;\n }\n\n function token1() external view override returns (address) {\n return tok1;\n }\n\n function getReserves()\n external\n view\n override\n returns (\n uint112,\n uint112,\n uint32\n )\n {\n return (reserve0, reserve1, uint32(blockTimestampLast));\n }\n\n function setReserves(uint112 _reserve0, uint112 _reserve1) public {\n reserve0 = _reserve0;\n reserve1 = _reserve1;\n blockTimestampLast = block.timestamp;\n }\n\n // CAUTION This will not work if you setReserves multiple times over\n // multiple different blocks because then it wouldn't be a continuous\n // reserve factor over that blockTimestamp, this assumes an even reserve\n // ratio all the way through\n function price0CumulativeLast() external view override returns (uint256) {\n return\n uint256(FixedPoint.fraction(reserve1, reserve0)._x) *\n blockTimestampLast;\n }\n\n function price1CumulativeLast() external view override returns (uint256) {\n return\n uint256(FixedPoint.fraction(reserve0, reserve1)._x) *\n blockTimestampLast;\n }\n\n function sync() external override {\n hasSynced = true;\n }\n\n function checkHasSynced() external view {\n require(hasSynced, \"Not synced\");\n }\n}\n\n// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))\nlibrary FixedPoint {\n // range: [0, 2**112 - 1]\n // resolution: 1 / 2**112\n struct uq112x112 {\n uint224 _x;\n }\n\n // returns a uq112x112 which represents the ratio of the numerator to the denominator\n // equivalent to encode(numerator).div(denominator)\n function fraction(uint112 numerator, uint112 denominator)\n internal\n pure\n returns (uq112x112 memory)\n {\n require(denominator > 0, \"FixedPoint: DIV_BY_ZERO\");\n return uq112x112((uint224(numerator) << 112) / denominator);\n }\n}\n" + }, + "contracts/mocks/MockUniswapRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { IUniswapV2Router } from \"../interfaces/uniswap/IUniswapV2Router02.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockUniswapRouter is IUniswapV2Router {\n using StableMath for uint256;\n\n mapping(address => address) public pairMaps;\n uint256 public slippage = 1 ether;\n\n function initialize(\n address[] calldata _0tokens,\n address[] calldata _1tokens\n ) public {\n require(\n _0tokens.length == _1tokens.length,\n \"Mock token pairs should be of the same length\"\n );\n for (uint256 i = 0; i < _0tokens.length; i++) {\n pairMaps[_0tokens[i]] = _1tokens[i];\n }\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n\n function swapExactTokensForTokens(\n uint256 amountIn,\n uint256 amountOutMin,\n address[] calldata path,\n address to,\n // solhint-disable-next-line no-unused-vars\n uint256\n ) external override returns (uint256[] memory amountsOut) {\n address tok0 = path[0];\n address tok1 = path[path.length - 1];\n\n uint256 amountOut = (amountOutMin * slippage) / 1 ether;\n require(amountOut >= amountOutMin, \"Slippage error\");\n\n IERC20(tok0).transferFrom(msg.sender, address(this), amountIn);\n MintableERC20(tok1).mintTo(to, amountOut);\n\n amountsOut = new uint256[](path.length);\n amountsOut[path.length - 1] = amountOut;\n }\n\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut)\n {\n (address tok0, address tok1) = _getFirstAndLastToken(params.path);\n\n amountOut = (params.amountOutMinimum * slippage) / 1 ether;\n\n IERC20(tok0).transferFrom(msg.sender, address(this), params.amountIn);\n MintableERC20(tok1).mintTo(params.recipient, amountOut);\n\n require(\n amountOut >= params.amountOutMinimum,\n \"UniswapMock: amountOut less than amountOutMinimum\"\n );\n return amountOut;\n }\n\n function addLiquidity(\n address tokenA,\n address tokenB,\n uint256 amountADesired,\n uint256 amountBDesired,\n uint256 amountAMin,\n uint256 amountBMin,\n address to,\n uint256 deadline\n )\n external\n override\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 liquidity\n )\n {\n // this is needed to make this contract whole else it'd be just virtual\n }\n\n function WETH() external pure override returns (address) {\n return address(0);\n }\n\n // Universal router mock\n function execute(\n bytes calldata,\n bytes[] calldata inputs,\n uint256\n ) external payable {\n uint256 inLen = inputs.length;\n for (uint256 i = 0; i < inLen; ++i) {\n (\n address recipient,\n ,\n uint256 amountOutMinimum,\n bytes memory path,\n\n ) = abi.decode(inputs[i], (address, uint256, uint256, bytes, bool));\n\n (address token0, address token1) = _getFirstAndLastToken(path);\n\n amountOutMinimum = amountOutMinimum.scaleBy(\n Helpers.getDecimals(token0),\n Helpers.getDecimals(token1)\n );\n\n MintableERC20(token1).mintTo(recipient, amountOutMinimum);\n }\n }\n\n function _getFirstAndLastToken(bytes memory path)\n internal\n view\n returns (address token0, address token1)\n {\n bytes memory tok0Bytes = new bytes(20);\n for (uint256 j = 0; j < 20; ++j) {\n tok0Bytes[j] = path[j];\n }\n token0 = address(bytes20(tok0Bytes));\n\n if (pairMaps[token0] != address(0)) {\n token0 = pairMaps[token0];\n }\n\n bytes memory tok1Bytes = new bytes(20);\n uint256 tok1Offset = path.length - 20;\n for (uint256 j = 0; j < 20; ++j) {\n tok1Bytes[j] = path[j + tok1Offset];\n }\n token1 = address(bytes20(tok1Bytes));\n\n if (pairMaps[token1] != address(0)) {\n token1 = pairMaps[token1];\n }\n }\n}\n" + }, + "contracts/mocks/MockUSDC.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDC is MintableERC20 {\n constructor() ERC20(\"USDC Coin\", \"USDC\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n}\n" + }, + "contracts/mocks/MockUSDS.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDS is MintableERC20 {\n constructor() ERC20(\"USDS\", \"USDS\") {}\n}\n" + }, + "contracts/mocks/MockUSDT.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDT is MintableERC20 {\n constructor() ERC20(\"USDT Coin\", \"USDT\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n}\n" + }, + "contracts/mocks/MockVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultCore } from \"../vault/VaultCore.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { VaultInitializer } from \"../vault/VaultInitializer.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MockVault is VaultCore {\n using StableMath for uint256;\n\n uint256 storedTotalValue;\n\n function setTotalValue(uint256 _value) public {\n storedTotalValue = _value;\n }\n\n function totalValue() external view override returns (uint256) {\n return storedTotalValue;\n }\n\n function _totalValue() internal view override returns (uint256) {\n return storedTotalValue;\n }\n\n function _checkBalance(address _asset)\n internal\n view\n override\n returns (uint256 balance)\n {\n // Avoids rounding errors by returning the total value\n // in a single currency\n if (allAssets[0] == _asset) {\n uint256 decimals = Helpers.getDecimals(_asset);\n return storedTotalValue.scaleBy(decimals, 18);\n } else {\n return 0;\n }\n }\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external onlyGovernor {\n maxSupplyDiff = _maxSupplyDiff;\n }\n}\n" + }, + "contracts/mocks/MockVaultCoreInstantRebase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultCore } from \"../vault/VaultCore.sol\";\n\ncontract MockVaultCoreInstantRebase is VaultCore {\n function _nextYield(uint256 supply, uint256 vaultValue)\n internal\n view\n override\n returns (uint256 yield, uint256 targetRate)\n {\n if (vaultValue <= supply) {\n return (0, 0);\n }\n yield = vaultValue - supply;\n return (yield, 0);\n }\n}\n" + }, + "contracts/mocks/MockWETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\n// Just importing to \"unbreak\" coverage tests\nimport { IERC721Receiver } from \"@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol\";\n\ncontract MockWETH is MintableERC20 {\n constructor() ERC20(\"WETH\", \"WETH\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/MockWS.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockWS is MintableERC20 {\n constructor() ERC20(\"Wrapped Sonic\", \"wS\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/TestUpgradedOUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../token/OUSD.sol\";\n\n// used to alter internal state of OUSD contract\ncontract TestUpgradedOUSD is OUSD {\n constructor() OUSD() {}\n\n function overwriteCreditBalances(address _account, uint256 _creditBalance)\n public\n {\n creditBalances[_account] = _creditBalance;\n }\n\n function overwriteAlternativeCPT(address _account, uint256 _acpt) public {\n alternativeCreditsPerToken[_account] = _acpt;\n }\n\n function overwriteRebaseState(address _account, RebaseOptions _rebaseOption)\n public\n {\n rebaseState[_account] = _rebaseOption;\n }\n}\n" + }, + "contracts/oracle/AbstractOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\n// @notice Abstract functionality that is shared between various Oracle Routers\nabstract contract AbstractOracleRouter is IOracle {\n using StableMath for uint256;\n using SafeCast for int256;\n\n uint256 internal constant MIN_DRIFT = 0.7e18;\n uint256 internal constant MAX_DRIFT = 1.3e18;\n address internal constant FIXED_PRICE =\n 0x0000000000000000000000000000000000000001;\n // Maximum allowed staleness buffer above normal Oracle maximum staleness\n uint256 internal constant STALENESS_BUFFER = 1 days;\n mapping(address => uint8) internal decimalsCache;\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n returns (address feedAddress, uint256 maxStaleness);\n\n /**\n * @notice Returns the total price in 18 digit unit for a given asset.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n if (shouldBePegged(asset)) {\n require(_price <= MAX_DRIFT, \"Oracle: Price exceeds max\");\n require(_price >= MIN_DRIFT, \"Oracle: Price under min\");\n }\n return _price;\n }\n\n function getDecimals(address _feed) internal view virtual returns (uint8) {\n uint8 decimals = decimalsCache[_feed];\n require(decimals > 0, \"Oracle: Decimals not cached\");\n return decimals;\n }\n\n /**\n * @notice Before an asset/feed price is fetches for the first time the\n * decimals need to be cached. This is a gas optimization\n * @param asset address of the asset\n * @return uint8 corresponding asset decimals\n */\n function cacheDecimals(address asset) external returns (uint8) {\n (address _feed, ) = feedMetadata(asset);\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n uint8 decimals = AggregatorV3Interface(_feed).decimals();\n decimalsCache[_feed] = decimals;\n return decimals;\n }\n\n function shouldBePegged(address _asset) internal view returns (bool) {\n string memory symbol = Helpers.getSymbol(_asset);\n bytes32 symbolHash = keccak256(abi.encodePacked(symbol));\n return\n symbolHash == keccak256(abi.encodePacked(\"DAI\")) ||\n symbolHash == keccak256(abi.encodePacked(\"USDC\")) ||\n symbolHash == keccak256(abi.encodePacked(\"USDT\"));\n }\n}\n" + }, + "contracts/oracle/OETHBaseOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\n// @notice Oracle Router (for OETH on Base) that denominates all prices in ETH\ncontract OETHBaseOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n using SafeCast for int256;\n\n address constant WETH = 0x4200000000000000000000000000000000000006;\n address constant WOETH = 0xD8724322f44E5c58D7A815F542036fb17DbbF839;\n address constant WOETH_CHAINLINK_FEED =\n 0xe96EB1EDa83d18cbac224233319FA5071464e1b9;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == WETH) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == WOETH) {\n // Chainlink: https://data.chain.link/feeds/base/base/woeth-oeth-exchange-rate\n // Bridged wOETH/OETH\n feedAddress = WOETH_CHAINLINK_FEED;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OETHFixedOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHOracleRouter } from \"./OETHOracleRouter.sol\";\n\n// @notice Oracle Router that returns 1e18 for all prices\n// used solely for deployment to testnets\ncontract OETHFixedOracle is OETHOracleRouter {\n constructor() OETHOracleRouter() {}\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n // solhint-disable-next-line no-unused-vars\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n // fixes price for all of the assets\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n }\n}\n" + }, + "contracts/oracle/OETHOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// @notice Oracle Router that denominates all prices in ETH\ncontract OETHOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = uint256(_iprice).scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == 0x5E8422345238F34275888049021821E8E08CAa1f) {\n // frxETH/ETH\n feedAddress = 0xC58F3385FBc1C8AD2c0C9a061D7c13b141D7A5Df;\n maxStaleness = 18 hours + STALENESS_BUFFER;\n } else if (asset == 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/steth-eth\n // Chainlink: stETH/ETH\n feedAddress = 0x86392dC19c0b719886221c78AB11eb8Cf5c52812;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xae78736Cd615f374D3085123A210448E74Fc6393) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/reth-eth\n // Chainlink: rETH/ETH\n feedAddress = 0x536218f9E9Eb48863970252233c8F271f554C2d0;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xD533a949740bb3306d119CC777fa900bA034cd52) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/crv-eth\n // Chainlink: CRV/ETH\n feedAddress = 0x8a12Be339B0cD1829b91Adc01977caa5E9ac121e;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/cvx-eth\n // Chainlink: CVX/ETH\n feedAddress = 0xC9CbF687f43176B302F03f5e58470b77D07c61c6;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xBe9895146f7AF43049ca1c1AE358B0541Ea49704) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/cbeth-eth\n // Chainlink: cbETH/ETH\n feedAddress = 0xF017fcB346A1885194689bA23Eff2fE6fA5C483b;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xba100000625a3754423978a60c9317c58a424e3D) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/bal-eth\n // Chainlink: BAL/ETH\n feedAddress = 0xC1438AA3823A6Ba0C159CfA8D98dF5A994bA120b;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OETHPlumeOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\n// @notice Oracle Router (for OETH on Plume) that denominates all prices in ETH\ncontract OETHPlumeOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n using SafeCast for int256;\n\n address constant WETH = 0xca59cA09E5602fAe8B629DeE83FfA819741f14be;\n address constant WOETH = 0xD8724322f44E5c58D7A815F542036fb17DbbF839;\n // Ref: https://docs.eo.app/docs/eprice/feed-addresses/plume\n address constant WOETH_ORACLE_FEED =\n 0x4915600Ed7d85De62011433eEf0BD5399f677e9b;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == WETH) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == WOETH) {\n // Chainlink: https://data.chain.link/feeds/base/base/woeth-oeth-exchange-rate\n // Bridged wOETH/OETH\n feedAddress = WOETH_ORACLE_FEED;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\n\n// @notice Oracle Router that denominates all prices in USD\ncontract OracleRouter is AbstractOracleRouter {\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n pure\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n /* + STALENESS_BUFFER is added in case Oracle for some reason doesn't\n * update on heartbeat and we add a generous buffer amount.\n */\n if (asset == 0x6B175474E89094C44Da98b954EedeAC495271d0F) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/dai-usd\n // Chainlink: DAI/USD\n feedAddress = 0xAed0c38402a5d19df6E4c03F4E2DceD6e29c1ee9;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xdC035D45d973E3EC169d2276DDab16f1e407384F) {\n // https://data.chain.link/feeds/ethereum/mainnet/usds-usd\n // Chainlink: USDS/USD\n feedAddress = 0xfF30586cD0F29eD462364C7e81375FC0C71219b1;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/usdc-usd\n // Chainlink: USDC/USD\n feedAddress = 0x8fFfFfd4AfB6115b954Bd326cbe7B4BA576818f6;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xdAC17F958D2ee523a2206206994597C13D831ec7) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/usdt-usd\n // Chainlink: USDT/USD\n feedAddress = 0x3E7d1eAB13ad0104d2750B8863b489D65364e32D;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xc00e94Cb662C3520282E6f5717214004A7f26888) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/comp-usd\n // Chainlink: COMP/USD\n feedAddress = 0xdbd020CAeF83eFd542f4De03e3cF0C28A4428bd5;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/aave-usd\n // Chainlink: AAVE/USD\n feedAddress = 0x547a514d5e3769680Ce22B2361c10Ea13619e8a9;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xD533a949740bb3306d119CC777fa900bA034cd52) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/crv-usd\n // Chainlink: CRV/USD\n feedAddress = 0xCd627aA160A6fA45Eb793D19Ef54f5062F20f33f;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B) {\n // Chainlink: CVX/USD\n feedAddress = 0xd962fC30A72A84cE50161031391756Bf2876Af5D;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OSonicOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHFixedOracle } from \"./OETHFixedOracle.sol\";\n\n// @notice Oracle Router that returns 1e18 for all prices\n// used solely for deployment to testnets\ncontract OSonicOracleRouter is OETHFixedOracle {\n constructor() OETHFixedOracle() {}\n}\n" + }, + "contracts/poolBooster/AbstractPoolBoosterFactory.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IPoolBoostCentralRegistry } from \"../interfaces/poolBooster/IPoolBoostCentralRegistry.sol\";\n\n/**\n * @title Abstract Pool booster factory\n * @author Origin Protocol Inc\n */\ncontract AbstractPoolBoosterFactory is Governable {\n struct PoolBoosterEntry {\n address boosterAddress;\n address ammPoolAddress;\n IPoolBoostCentralRegistry.PoolBoosterType boosterType;\n }\n\n // @notice address of Origin Sonic\n address public immutable oSonic;\n // @notice Central registry contract\n IPoolBoostCentralRegistry public immutable centralRegistry;\n\n // @notice list of all the pool boosters created by this factory\n PoolBoosterEntry[] public poolBoosters;\n // @notice mapping of AMM pool to pool booster\n mapping(address => PoolBoosterEntry) public poolBoosterFromPool;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) {\n require(_oSonic != address(0), \"Invalid oSonic address\");\n require(_governor != address(0), \"Invalid governor address\");\n require(\n _centralRegistry != address(0),\n \"Invalid central registry address\"\n );\n\n oSonic = _oSonic;\n centralRegistry = IPoolBoostCentralRegistry(_centralRegistry);\n _setGovernor(_governor);\n }\n\n /**\n * @notice Goes over all the pool boosters created by this factory and\n * calls bribe() on them.\n * @param _exclusionList A list of pool booster addresses to skip when\n * calling this function.\n */\n function bribeAll(address[] memory _exclusionList) external {\n uint256 lengthI = poolBoosters.length;\n for (uint256 i = 0; i < lengthI; i++) {\n address poolBoosterAddress = poolBoosters[i].boosterAddress;\n bool skipBribeCall = false;\n uint256 lengthJ = _exclusionList.length;\n for (uint256 j = 0; j < lengthJ; j++) {\n // pool booster in exclusion list\n if (_exclusionList[j] == poolBoosterAddress) {\n skipBribeCall = true;\n break;\n }\n }\n\n if (!skipBribeCall) {\n IPoolBooster(poolBoosterAddress).bribe();\n }\n }\n }\n\n /**\n * @notice Removes the pool booster from the internal list of pool boosters.\n * @dev This action does not destroy the pool booster contract nor does it\n * stop the yield delegation to it.\n * @param _poolBoosterAddress address of the pool booster\n */\n function removePoolBooster(address _poolBoosterAddress)\n external\n onlyGovernor\n {\n uint256 boostersLen = poolBoosters.length;\n for (uint256 i = 0; i < boostersLen; ++i) {\n if (poolBoosters[i].boosterAddress == _poolBoosterAddress) {\n // erase mapping\n delete poolBoosterFromPool[poolBoosters[i].ammPoolAddress];\n\n // overwrite current pool booster with the last entry in the list\n poolBoosters[i] = poolBoosters[boostersLen - 1];\n // drop the last entry\n poolBoosters.pop();\n\n centralRegistry.emitPoolBoosterRemoved(_poolBoosterAddress);\n break;\n }\n }\n }\n\n function _storePoolBoosterEntry(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType _boosterType\n ) internal {\n PoolBoosterEntry memory entry = PoolBoosterEntry(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType\n );\n\n poolBoosters.push(entry);\n poolBoosterFromPool[_ammPoolAddress] = entry;\n\n // emit the events of the pool booster created\n centralRegistry.emitPoolBoosterCreated(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType\n );\n }\n\n function _deployContract(bytes memory _bytecode, uint256 _salt)\n internal\n returns (address _address)\n {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _address := create2(\n 0,\n add(_bytecode, 0x20),\n mload(_bytecode),\n _salt\n )\n }\n\n require(\n _address.code.length > 0 && _address != address(0),\n \"Failed creating a pool booster\"\n );\n }\n\n // pre-compute the address of the deployed contract that will be\n // created when create2 is called\n function _computeAddress(bytes memory _bytecode, uint256 _salt)\n internal\n view\n returns (address)\n {\n bytes32 hash = keccak256(\n abi.encodePacked(\n bytes1(0xff),\n address(this),\n _salt,\n keccak256(_bytecode)\n )\n );\n\n // cast last 20 bytes of hash to address\n return address(uint160(uint256(hash)));\n }\n\n function poolBoosterLength() external view returns (uint256) {\n return poolBoosters.length;\n }\n}\n" + }, + "contracts/poolBooster/PoolBoostCentralRegistry.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IPoolBoostCentralRegistry } from \"../interfaces/poolBooster/IPoolBoostCentralRegistry.sol\";\n\n/**\n * @title Contract that holds all governance approved pool booster Factory\n * implementation deployments\n * @author Origin Protocol Inc\n */\ncontract PoolBoostCentralRegistry is Governable, IPoolBoostCentralRegistry {\n event FactoryApproved(address factoryAddress);\n event FactoryRemoved(address factoryAddress);\n\n // @notice List of approved factories\n address[] public factories;\n\n modifier onlyApprovedFactories() {\n require(isApprovedFactory(msg.sender), \"Not an approved factory\");\n _;\n }\n\n constructor() {\n // set the governor of the implementation contract to zero address\n _setGovernor(address(0));\n }\n\n /**\n * @notice Adds a factory address to the approved factory addresses\n * @param _factoryAddress address of the factory\n */\n function approveFactory(address _factoryAddress) external onlyGovernor {\n require(_factoryAddress != address(0), \"Invalid address\");\n require(\n !isApprovedFactory(_factoryAddress),\n \"Factory already approved\"\n );\n\n factories.push(_factoryAddress);\n emit FactoryApproved(_factoryAddress);\n }\n\n /**\n * @notice Removes the factory from approved factory addresses\n * @param _factoryAddress address of the factory\n */\n function removeFactory(address _factoryAddress) external onlyGovernor {\n require(_factoryAddress != address(0), \"Invalid address\");\n\n uint256 length = factories.length;\n bool factoryRemoved = false;\n for (uint256 i = 0; i < length; i++) {\n if (factories[i] != _factoryAddress) {\n continue;\n }\n\n factories[i] = factories[length - 1];\n factories.pop();\n emit FactoryRemoved(_factoryAddress);\n factoryRemoved = true;\n break;\n }\n require(factoryRemoved, \"Not an approved factory\");\n\n emit FactoryRemoved(_factoryAddress);\n }\n\n /**\n * @notice Emits a pool booster created event\n * @dev This has been created as a convenience method for the monitoring to have\n * an index of all of the created pool boosters by only listening to the\n * events of this contract.\n * @param _poolBoosterAddress address of the pool booster created\n * @param _ammPoolAddress address of the AMM pool forwarding yield to the pool booster\n * @param _boosterType PoolBoosterType the type of the pool booster\n */\n function emitPoolBoosterCreated(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n PoolBoosterType _boosterType\n ) external onlyApprovedFactories {\n emit PoolBoosterCreated(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType,\n msg.sender // address of the factory\n );\n }\n\n /**\n * @notice Emits a pool booster removed event\n * @dev This has been created as a convenience method for the monitoring to have\n * an index of all of the removed pool boosters by only listening to the\n * events of this contract.\n * @param _poolBoosterAddress address of the pool booster to be removed\n */\n function emitPoolBoosterRemoved(address _poolBoosterAddress)\n external\n onlyApprovedFactories\n {\n emit PoolBoosterRemoved(_poolBoosterAddress);\n }\n\n /**\n * @notice Returns true if the factory is approved\n * @param _factoryAddress address of the factory\n */\n function isApprovedFactory(address _factoryAddress)\n public\n view\n returns (bool)\n {\n uint256 length = factories.length;\n for (uint256 i = 0; i < length; i++) {\n if (factories[i] == _factoryAddress) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * @notice Returns all supported factories\n */\n function getAllFactories() external view returns (address[] memory) {\n return factories;\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactoryMerkl.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterMerkl } from \"./PoolBoosterMerkl.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Merkl pool boosters.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactoryMerkl is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n /// @notice address of the Merkl distributor\n address public merklDistributor;\n\n /// @notice event emitted when the Merkl distributor is updated\n event MerklDistributorUpdated(address newDistributor);\n\n /**\n * @param _oSonic address of the OSonic token\n * @param _governor address governor\n * @param _centralRegistry address of the central registry\n * @param _merklDistributor address of the Merkl distributor\n */\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry,\n address _merklDistributor\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {\n _setMerklDistributor(_merklDistributor);\n }\n\n /**\n * @dev Create a Pool Booster for Merkl.\n * @param _campaignType The type of campaign to create. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _campaignDuration The duration of the campaign in seconds\n * @param campaignData The data to be used for the campaign. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * This should be fetched from the Merkl UI.\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterMerkl(\n uint32 _campaignType,\n address _ammPoolAddress,\n uint32 _campaignDuration,\n bytes calldata campaignData,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n require(_campaignDuration > 1 hours, \"Invalid campaign duration\");\n require(campaignData.length > 0, \"Invalid campaign data\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterMerkl).creationCode,\n abi.encode(\n oSonic,\n merklDistributor,\n _campaignDuration,\n _campaignType,\n governor(),\n campaignData\n )\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.MerklBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for Merkl.\n * @param _campaignType The type of campaign to create. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterMerkl` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n uint32 _campaignType,\n address _ammPoolAddress,\n uint32 _campaignDuration,\n bytes calldata campaignData,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n require(_campaignDuration > 1 hours, \"Invalid campaign duration\");\n require(campaignData.length > 0, \"Invalid campaign data\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterMerkl).creationCode,\n abi.encode(\n oSonic,\n merklDistributor,\n _campaignDuration,\n _campaignType,\n governor(),\n campaignData\n )\n ),\n _salt\n );\n }\n\n /**\n * @dev Set the address of the Merkl distributor\n * @param _merklDistributor The address of the Merkl distributor\n */\n function setMerklDistributor(address _merklDistributor)\n external\n onlyGovernor\n {\n _setMerklDistributor(_merklDistributor);\n }\n\n function _setMerklDistributor(address _merklDistributor) internal {\n require(\n _merklDistributor != address(0),\n \"Invalid merklDistributor address\"\n );\n merklDistributor = _merklDistributor;\n emit MerklDistributorUpdated(_merklDistributor);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactoryMetropolis.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterMetropolis } from \"./PoolBoosterMetropolis.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Metropolis pool boosters.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactoryMetropolis is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n address public immutable rewardFactory;\n address public immutable voter;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n // @param address _rewardFactory address of the Metropolis reward factory\n // @param address _voter address of the Metropolis voter\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry,\n address _rewardFactory,\n address _voter\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {\n rewardFactory = _rewardFactory;\n voter = _voter;\n }\n\n /**\n * @dev Create a Pool Booster for Metropolis pool.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterMetropolis(address _ammPoolAddress, uint256 _salt)\n external\n onlyGovernor\n {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterMetropolis).creationCode,\n abi.encode(oSonic, rewardFactory, _ammPoolAddress, voter)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.MetropolisBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for Metropolis pool.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterMetropolis` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(address _ammPoolAddress, uint256 _salt)\n external\n view\n returns (address)\n {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterMetropolis).creationCode,\n abi.encode(oSonic, rewardFactory, _ammPoolAddress, voter)\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactorySwapxDouble.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterSwapxDouble } from \"./PoolBoosterSwapxDouble.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Swapx Ichi pool boosters where both of the\n * gauges need incentivizing.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactorySwapxDouble is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {}\n\n /**\n * @dev Create a Pool Booster for SwapX Ichi vault based pool where 2 Bribe contracts need to be\n * bribed\n * @param _bribeAddressOS address of the Bribes.sol(Bribe) contract for the OS token side\n * @param _bribeAddressOther address of the Bribes.sol(Bribe) contract for the other token in the pool\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _split 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n * bribe contract and 60% to other bribe contract\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterSwapxDouble(\n address _bribeAddressOS,\n address _bribeAddressOther,\n address _ammPoolAddress,\n uint256 _split,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterSwapxDouble).creationCode,\n abi.encode(_bribeAddressOS, _bribeAddressOther, oSonic, _split)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.SwapXDoubleBooster\n );\n }\n\n /**\n * @dev Compute the address of the pool booster to be deployed.\n * @param _bribeAddressOS address of the Bribes.sol(Bribe) contract for the OS token side\n * @param _bribeAddressOther address of the Bribes.sol(Bribe) contract for the other token in the pool\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _split 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n * bribe contract and 60% to other bribe contract\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterSwapxDouble` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n address _bribeAddressOS,\n address _bribeAddressOther,\n address _ammPoolAddress,\n uint256 _split,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterSwapxDouble).creationCode,\n abi.encode(\n _bribeAddressOS,\n _bribeAddressOther,\n oSonic,\n _split\n )\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactorySwapxSingle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterSwapxSingle } from \"./PoolBoosterSwapxSingle.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Swapx Single pool boosters - where a single\n * gauge is created for a pool. this is appropriate for Classic Stable & Classic\n * Volatile SwapX pools.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactorySwapxSingle is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {}\n\n /**\n * @dev Create a Pool Booster for SwapX classic volatile or classic stable pools where\n * a single Bribe contract is incentivized.\n * @param _bribeAddress address of the Bribes.sol contract\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterSwapxSingle(\n address _bribeAddress,\n address _ammPoolAddress,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterSwapxSingle).creationCode,\n abi.encode(_bribeAddress, oSonic)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.SwapXSingleBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for SwapX classic volatile or classic stable pools where\n * a single Bribe contract is incentivized.\n * @param _bribeAddress address of the Bribes.sol contract\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterSwapxSingle` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n address _bribeAddress,\n address _ammPoolAddress,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterSwapxSingle).creationCode,\n abi.encode(_bribeAddress, oSonic)\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterMerkl.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IMerklDistributor } from \"../interfaces/poolBooster/IMerklDistributor.sol\";\n\ninterface IERC1271 {\n /**\n * @dev Should return whether the signature provided is valid for the provided data\n * @param hash Hash of the data to be signed\n * @param signature Signature byte array associated with _data\n */\n function isValidSignature(bytes32 hash, bytes memory signature)\n external\n view\n returns (bytes4 magicValue);\n}\n\n/**\n * @title Pool booster for Merkl distributor\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterMerkl is IPoolBooster, IERC1271 {\n /// @notice address of merkl distributor\n IMerklDistributor public immutable merklDistributor;\n /// @notice address of the OS token\n IERC20 public immutable rewardToken;\n /// @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n /// @notice Campaign duration in seconds\n uint32 public immutable duration; // -> should be immutable\n /// @notice Campaign type\n uint32 public immutable campaignType;\n /// @notice Owner of the campaign\n address public immutable creator;\n /// @notice Campaign data\n bytes public campaignData;\n\n constructor(\n address _rewardToken,\n address _merklDistributor,\n uint32 _duration,\n uint32 _campaignType,\n address _creator,\n bytes memory _campaignData\n ) {\n require(_rewardToken != address(0), \"Invalid rewardToken address\");\n require(\n _merklDistributor != address(0),\n \"Invalid merklDistributor address\"\n );\n require(_campaignData.length > 0, \"Invalid campaignData\");\n require(_duration > 1 hours, \"Invalid duration\");\n\n campaignType = _campaignType;\n duration = _duration;\n creator = _creator;\n\n merklDistributor = IMerklDistributor(_merklDistributor);\n rewardToken = IERC20(_rewardToken);\n campaignData = _campaignData;\n }\n\n /// @notice Create a campaign on the Merkl distributor\n function bribe() external override {\n // Ensure token is approved for the Merkl distributor\n uint256 minAmount = merklDistributor.rewardTokenMinAmounts(\n address(rewardToken)\n );\n require(minAmount > 0, \"Min reward amount must be > 0\");\n\n // if balance too small or below threshold, do no bribes\n uint256 balance = rewardToken.balanceOf(address(this));\n if (\n balance < MIN_BRIBE_AMOUNT ||\n (balance * 1 hours < minAmount * duration)\n ) {\n return;\n }\n\n // Approve the bribe contract to spend the reward token\n rewardToken.approve(address(merklDistributor), balance);\n\n // Notify the bribe contract of the reward amount\n merklDistributor.signAndCreateCampaign(\n IMerklDistributor.CampaignParameters({\n campaignId: bytes32(0),\n creator: creator,\n rewardToken: address(rewardToken),\n amount: balance,\n campaignType: campaignType,\n startTimestamp: getNextPeriodStartTime(),\n duration: duration,\n campaignData: campaignData\n }),\n bytes(\"\")\n );\n emit BribeExecuted(balance);\n }\n\n /// @notice Used to sign a campaign on the Merkl distributor\n function isValidSignature(bytes32, bytes memory)\n external\n view\n override\n returns (bytes4 magicValue)\n {\n require(msg.sender == address(merklDistributor), \"Invalid sender\");\n // bytes4(keccak256(\"isValidSignature(bytes32,bytes)\")) == 0x1626ba7e\n return bytes4(0x1626ba7e);\n }\n\n /// @notice Returns the timestamp for the start of the next period based on the configured duration\n function getNextPeriodStartTime() public view returns (uint32) {\n // Calculate the timestamp for the next period boundary\n return uint32((block.timestamp / duration + 1) * duration);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterMetropolis.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @title Pool booster for Metropolis pools\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterMetropolis is IPoolBooster {\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice address of the pool\n address public immutable pool;\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n IRewarderFactory public immutable rewardFactory;\n\n IVoter public immutable voter;\n\n constructor(\n address _osToken,\n address _rewardFactory,\n address _pool,\n address _voter\n ) {\n require(_pool != address(0), \"Invalid pool address\");\n pool = _pool;\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n\n rewardFactory = IRewarderFactory(_rewardFactory);\n\n voter = IVoter(_voter);\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n (, uint256 minBribeAmount) = rewardFactory.getWhitelistedTokenInfo(\n address(osToken)\n );\n if (balance < MIN_BRIBE_AMOUNT || balance < minBribeAmount) {\n return;\n }\n\n uint256 id = voter.getCurrentVotingPeriod() + 1;\n\n // Deploy a rewarder\n IRewarder rewarder = IRewarder(\n rewardFactory.createBribeRewarder(address(osToken), pool)\n );\n\n // Approve the rewarder to spend the balance\n osToken.approve(address(rewarder), balance);\n\n // Fund and bribe the rewarder\n rewarder.fundAndBribe(id, id, balance);\n\n emit BribeExecuted(balance);\n }\n}\n\ninterface IRewarderFactory {\n function createBribeRewarder(address token, address pool)\n external\n returns (address rewarder);\n\n function getWhitelistedTokenInfo(address token)\n external\n view\n returns (bool isWhitelisted, uint256 minBribeAmount);\n}\n\ninterface IRewarder {\n function fundAndBribe(\n uint256 startId,\n uint256 lastId,\n uint256 amountPerPeriod\n ) external payable;\n}\n\ninterface IVoter {\n function getCurrentVotingPeriod() external view returns (uint256);\n}\n" + }, + "contracts/poolBooster/PoolBoosterSwapxDouble.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBribe } from \"../interfaces/poolBooster/ISwapXAlgebraBribe.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n/**\n * @title Pool booster for SwapX concentrated liquidity where 2 gauges are created for\n * every pool. Ichi vaults currently have such setup.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterSwapxDouble is IPoolBooster {\n using StableMath for uint256;\n\n // @notice address of the Bribes.sol(Bribe) contract for the OS token side\n IBribe public immutable bribeContractOS;\n // @notice address of the Bribes.sol(Bribe) contract for the other token in the pool\n IBribe public immutable bribeContractOther;\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n // bribe contract and 60% to other bribe contract\n uint256 public immutable split;\n\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n constructor(\n address _bribeContractOS,\n address _bribeContractOther,\n address _osToken,\n uint256 _split\n ) {\n require(\n _bribeContractOS != address(0),\n \"Invalid bribeContractOS address\"\n );\n require(\n _bribeContractOther != address(0),\n \"Invalid bribeContractOther address\"\n );\n // expect it to be between 1% & 99%\n require(_split > 1e16 && _split < 99e16, \"Unexpected split amount\");\n\n bribeContractOS = IBribe(_bribeContractOS);\n bribeContractOther = IBribe(_bribeContractOther);\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n split = _split;\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n if (balance < MIN_BRIBE_AMOUNT) {\n return;\n }\n\n uint256 osBribeAmount = balance.mulTruncate(split);\n uint256 otherBribeAmount = balance - osBribeAmount;\n\n osToken.approve(address(bribeContractOS), osBribeAmount);\n osToken.approve(address(bribeContractOther), otherBribeAmount);\n\n bribeContractOS.notifyRewardAmount(address(osToken), osBribeAmount);\n bribeContractOther.notifyRewardAmount(\n address(osToken),\n otherBribeAmount\n );\n\n emit BribeExecuted(balance);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterSwapxSingle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBribe } from \"../interfaces/poolBooster/ISwapXAlgebraBribe.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @title Pool booster for SwapX for Classic Stable Pools and Classic Volatile Pools\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterSwapxSingle is IPoolBooster {\n // @notice address of the Bribes.sol(Bribe) contract\n IBribe public immutable bribeContract;\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n constructor(address _bribeContract, address _osToken) {\n require(_bribeContract != address(0), \"Invalid bribeContract address\");\n bribeContract = IBribe(_bribeContract);\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n if (balance < MIN_BRIBE_AMOUNT) {\n return;\n }\n\n osToken.approve(address(bribeContract), balance);\n\n bribeContract.notifyRewardAmount(address(osToken), balance);\n emit BribeExecuted(balance);\n }\n}\n" + }, + "contracts/proxies/BaseProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice BridgedBaseWOETHProxy delegates calls to BridgedWOETH implementation\n */\ncontract BridgedBaseWOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseVaultProxy delegates calls to OETHBaseVault implementation\n */\ncontract OETHBaseVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseProxy delegates calls to OETH implementation\n */\ncontract OETHBaseProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHBaseProxy delegates calls to WOETH implementation\n */\ncontract WOETHBaseProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseDripperProxy delegates calls to a FixedRateDripper implementation\n */\ncontract OETHBaseDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice AerodromeAMOStrategyProxy delegates calls to AerodromeAMOStrategy implementation\n */\ncontract AerodromeAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BridgedWOETHStrategyProxy delegates calls to BridgedWOETHStrategy implementation\n */\ncontract BridgedWOETHStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseHarvesterProxy delegates calls to a SuperOETHHarvester implementation\n */\ncontract OETHBaseHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseCurveAMOProxy delegates calls to a OETHBaseCurveAMO implementation\n */\ncontract OETHBaseCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/proxies/InitializeGovernedUpgradeabilityProxy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { Governable } from \"../governance/Governable.sol\";\n\n/**\n * @title BaseGovernedUpgradeabilityProxy\n * @dev This contract combines an upgradeability proxy with our governor system.\n * It is based on an older version of OpenZeppelins BaseUpgradeabilityProxy\n * with Solidity ^0.8.0.\n * @author Origin Protocol Inc\n */\ncontract InitializeGovernedUpgradeabilityProxy is Governable {\n /**\n * @dev Emitted when the implementation is upgraded.\n * @param implementation Address of the new implementation.\n */\n event Upgraded(address indexed implementation);\n\n constructor() {\n _setGovernor(msg.sender);\n }\n\n /**\n * @dev Contract initializer with Governor enforcement\n * @param _logic Address of the initial implementation.\n * @param _initGovernor Address of the initial Governor.\n * @param _data Data to send as msg.data to the implementation to initialize\n * the proxied contract.\n * It should include the signature and the parameters of the function to be\n * called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n * This parameter is optional, if no data is given the initialization call\n * to proxied contract will be skipped.\n */\n function initialize(\n address _logic,\n address _initGovernor,\n bytes calldata _data\n ) public payable onlyGovernor {\n require(_implementation() == address(0));\n require(_logic != address(0), \"Implementation not set\");\n assert(\n IMPLEMENTATION_SLOT ==\n bytes32(uint256(keccak256(\"eip1967.proxy.implementation\")) - 1)\n );\n _setImplementation(_logic);\n if (_data.length > 0) {\n (bool success, ) = _logic.delegatecall(_data);\n require(success);\n }\n _changeGovernor(_initGovernor);\n }\n\n /**\n * @return The address of the proxy admin/it's also the governor.\n */\n function admin() external view returns (address) {\n return _governor();\n }\n\n /**\n * @return The address of the implementation.\n */\n function implementation() external view returns (address) {\n return _implementation();\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy.\n * Only the admin can call this function.\n * @param _newImplementation Address of the new implementation.\n */\n function upgradeTo(address _newImplementation) external onlyGovernor {\n _upgradeTo(_newImplementation);\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy and call a function\n * on the new implementation.\n * This is useful to initialize the proxied contract.\n * @param newImplementation Address of the new implementation.\n * @param data Data to send as msg.data in the low level call.\n * It should include the signature and the parameters of the function to be called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n */\n function upgradeToAndCall(address newImplementation, bytes calldata data)\n external\n payable\n onlyGovernor\n {\n _upgradeTo(newImplementation);\n (bool success, ) = newImplementation.delegatecall(data);\n require(success);\n }\n\n /**\n * @dev Fallback function.\n * Implemented entirely in `_fallback`.\n */\n fallback() external payable {\n _fallback();\n }\n\n /**\n * @dev Delegates execution to an implementation contract.\n * This is a low level function that doesn't return to its internal call site.\n * It will return to the external caller whatever the implementation returns.\n * @param _impl Address to delegate.\n */\n function _delegate(address _impl) internal {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(gas(), _impl, 0, calldatasize(), 0, 0)\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n /**\n * @dev Function that is run as the first thing in the fallback function.\n * Can be redefined in derived contracts to add functionality.\n * Redefinitions must call super._willFallback().\n */\n function _willFallback() internal {}\n\n /**\n * @dev fallback implementation.\n * Extracted to enable manual triggering.\n */\n function _fallback() internal {\n _willFallback();\n _delegate(_implementation());\n }\n\n /**\n * @dev Storage slot with the address of the current implementation.\n * This is the keccak-256 hash of \"eip1967.proxy.implementation\" subtracted by 1, and is\n * validated in the constructor.\n */\n bytes32 internal constant IMPLEMENTATION_SLOT =\n 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n /**\n * @dev Returns the current implementation.\n * @return impl Address of the current implementation\n */\n function _implementation() internal view returns (address impl) {\n bytes32 slot = IMPLEMENTATION_SLOT;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n impl := sload(slot)\n }\n }\n\n /**\n * @dev Upgrades the proxy to a new implementation.\n * @param newImplementation Address of the new implementation.\n */\n function _upgradeTo(address newImplementation) internal {\n _setImplementation(newImplementation);\n emit Upgraded(newImplementation);\n }\n\n /**\n * @dev Sets the implementation address of the proxy.\n * @param newImplementation Address of the new implementation.\n */\n function _setImplementation(address newImplementation) internal {\n require(\n Address.isContract(newImplementation),\n \"Cannot set a proxy implementation to a non-contract address\"\n );\n\n bytes32 slot = IMPLEMENTATION_SLOT;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(slot, newImplementation)\n }\n }\n}\n" + }, + "contracts/proxies/PlumeProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OETHPlumeVaultProxy delegates calls to OETHPlumeVault implementation\n */\ncontract OETHPlumeVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHPlumeProxy delegates calls to OETH implementation\n */\ncontract OETHPlumeProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHPlumeProxy delegates calls to WOETH implementation\n */\ncontract WOETHPlumeProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice RoosterAMOStrategyProxy delegates calls to a RoosterAMOStrategy implementation\n */\ncontract RoosterAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/proxies/Proxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OUSDProxy delegates calls to an OUSD implementation\n */\ncontract OUSDProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WrappedOUSDProxy delegates calls to a WrappedOUSD implementation\n */\ncontract WrappedOUSDProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice VaultProxy delegates calls to a Vault implementation\n */\ncontract VaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CompoundStrategyProxy delegates calls to a CompoundStrategy implementation\n */\ncontract CompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice AaveStrategyProxy delegates calls to a AaveStrategy implementation\n */\ncontract AaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ConvexStrategyProxy delegates calls to a ConvexStrategy implementation\n */\ncontract ConvexStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice HarvesterProxy delegates calls to a Harvester implementation\n */\ncontract HarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice DripperProxy delegates calls to a Dripper implementation\n */\ncontract DripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoCompoundStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\n */\ncontract MorphoCompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ConvexOUSDMetaStrategyProxy delegates calls to a ConvexOUSDMetaStrategy implementation\n */\ncontract ConvexOUSDMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoAaveStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\n */\ncontract MorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHProxy delegates calls to nowhere for now\n */\ncontract OETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHProxy delegates calls to nowhere for now\n */\ncontract WOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHVaultProxy delegates calls to a Vault implementation\n */\ncontract OETHVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHDripperProxy delegates calls to a OETHDripper implementation\n */\ncontract OETHDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHHarvesterProxy delegates calls to a Harvester implementation\n */\ncontract OETHHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CurveEthStrategyProxy delegates calls to a CurveEthStrategy implementation\n */\ncontract ConvexEthMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BuybackProxy delegates calls to Buyback implementation\n */\ncontract BuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHMorphoAaveStrategyProxy delegates calls to a MorphoAaveStrategy implementation\n */\ncontract OETHMorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBalancerMetaPoolrEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\n */\ncontract OETHBalancerMetaPoolrEthStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice OETHBalancerMetaPoolwstEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\n */\ncontract OETHBalancerMetaPoolwstEthStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MakerDsrStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MakerDsrStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBuybackProxy delegates calls to Buyback implementation\n */\ncontract OETHBuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BridgedWOETHProxy delegates calls to BridgedWOETH implementation\n */\ncontract BridgedWOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice NativeStakingSSVStrategyProxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulatorProxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulatorProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingSSVStrategy2Proxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategy2Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulator2Proxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulator2Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingSSVStrategy3Proxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategy3Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulator3Proxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulator3Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MetaMorphoStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MetaMorphoStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ARMBuybackProxy delegates calls to Buyback implementation\n */\ncontract ARMBuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoGauntletPrimeUSDCStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MorphoGauntletPrimeUSDCStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MorphoGauntletPrimeUSDTStrategyProxy delegates calls to a Generalized4626USDTStrategy implementation\n */\ncontract MorphoGauntletPrimeUSDTStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice CurvePoolBoosterProxy delegates calls to a CurvePoolBooster implementation\n */\ncontract CurvePoolBoosterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHFixedRateDripperProxy delegates calls to a OETHFixedRateDripper implementation\n */\ncontract OETHFixedRateDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHSimpleHarvesterProxy delegates calls to a OETHSimpleHarvester implementation\n */\ncontract OETHSimpleHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice PoolBoostCentralRegistryProxy delegates calls to the PoolBoostCentralRegistry implementation\n */\ncontract PoolBoostCentralRegistryProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MakerSSRStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MakerSSRStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OUSDCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\n */\ncontract OUSDCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\n */\ncontract OETHCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CompoundingStakingSSVStrategyProxy delegates calls to a CompoundingStakingSSVStrategy implementation\n */\ncontract CompoundingStakingSSVStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n" + }, + "contracts/proxies/SonicProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OSonicVaultProxy delegates calls to OSonicVault implementation\n */\ncontract OSonicVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicProxy delegates calls to OSonic implementation\n */\ncontract OSonicProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOSonicProxy delegates calls to WOSonic implementation\n */\ncontract WOSonicProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicDripperProxy delegates calls to a FixedRateDripper implementation\n */\ncontract OSonicDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice SonicStakingStrategyProxy delegates calls to SonicStakingStrategy implementation\n */\ncontract SonicStakingStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicHarvesterProxy delegates calls to a OSonicHarvester implementation\n */\ncontract OSonicHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice SonicSwapXAMOStrategyProxy delegates calls to a SonicSwapXAMOStrategy implementation\n */\ncontract SonicSwapXAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/strategies/AaveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Aave Strategy\n * @notice Investment strategy for investing stablecoins via Aave\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport \"./IAave.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\nimport { IAaveStakedToken } from \"./IAaveStakeToken.sol\";\nimport { IAaveIncentivesController } from \"./IAaveIncentivesController.sol\";\n\ncontract AaveStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n\n uint16 constant referralCode = 92;\n\n IAaveIncentivesController public incentivesController;\n IAaveStakedToken public stkAave;\n\n /**\n * @param _stratConfig The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as AAVE needs several extra\n * addresses for the rewards program.\n * @param _rewardTokenAddresses Address of the AAVE token\n * @param _assets Addresses of supported assets\n * @param _pTokens Platform Token corresponding addresses\n * @param _incentivesAddress Address of the AAVE incentives controller\n * @param _stkAaveAddress Address of the stkAave contract\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // AAVE\n address[] calldata _assets,\n address[] calldata _pTokens,\n address _incentivesAddress,\n address _stkAaveAddress\n ) external onlyGovernor initializer {\n incentivesController = IAaveIncentivesController(_incentivesAddress);\n stkAave = IAaveStakedToken(_stkAaveAddress);\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Deposit asset into Aave\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Aave\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n // Following line also doubles as a check that we are depositing\n // an asset that we support.\n emit Deposit(_asset, _getATokenFor(_asset), _amount);\n _getLendingPool().deposit(_asset, _amount, address(this), referralCode);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Aave\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Aave\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n emit Withdrawal(_asset, _getATokenFor(_asset), _amount);\n uint256 actual = _getLendingPool().withdraw(\n _asset,\n _amount,\n address(this)\n );\n require(actual == _amount, \"Did not withdraw enough\");\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n // Redeem entire balance of aToken\n IERC20 asset = IERC20(assetsMapped[i]);\n address aToken = _getATokenFor(assetsMapped[i]);\n uint256 balance = IERC20(aToken).balanceOf(address(this));\n if (balance > 0) {\n uint256 actual = _getLendingPool().withdraw(\n address(asset),\n balance,\n address(this)\n );\n require(actual == balance, \"Did not withdraw enough\");\n\n uint256 assetBalance = asset.balanceOf(address(this));\n // Transfer entire balance to Vault\n asset.safeTransfer(vaultAddress, assetBalance);\n\n emit Withdrawal(address(asset), aToken, assetBalance);\n }\n }\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n // Balance is always with token aToken decimals\n address aToken = _getATokenFor(_asset);\n balance = IERC20(aToken).balanceOf(address(this));\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Approve the spending of all assets by their corresponding aToken,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n address lendingPool = address(_getLendingPool());\n // approve the pool to spend the Asset\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n address asset = assetsMapped[i];\n // Safe approval\n IERC20(asset).safeApprove(lendingPool, 0);\n IERC20(asset).safeApprove(lendingPool, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / aTokens\n We need to give the AAVE lending pool approval to transfer the\n asset.\n * @param _asset Address of the asset to approve\n * @param _aToken Address of the aToken\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _aToken)\n internal\n override\n {\n address lendingPool = address(_getLendingPool());\n IERC20(_asset).safeApprove(lendingPool, 0);\n IERC20(_asset).safeApprove(lendingPool, type(uint256).max);\n }\n\n /**\n * @dev Get the aToken wrapped in the IERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return Corresponding aToken to this asset\n */\n function _getATokenFor(address _asset) internal view returns (address) {\n address aToken = assetToPToken[_asset];\n require(aToken != address(0), \"aToken does not exist\");\n return aToken;\n }\n\n /**\n * @dev Get the current address of the Aave lending pool, which is the gateway to\n * depositing.\n * @return Current lending pool implementation\n */\n function _getLendingPool() internal view returns (IAaveLendingPool) {\n address lendingPool = ILendingPoolAddressesProvider(platformAddress)\n .getLendingPool();\n require(lendingPool != address(0), \"Lending pool does not exist\");\n return IAaveLendingPool(lendingPool);\n }\n\n /**\n * @dev Collect stkAave, convert it to AAVE send to Vault.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n if (address(stkAave) == address(0)) {\n return;\n }\n\n // Check staked AAVE cooldown timer\n uint256 cooldown = stkAave.stakersCooldowns(address(this));\n uint256 windowStart = cooldown + stkAave.COOLDOWN_SECONDS();\n uint256 windowEnd = windowStart + stkAave.UNSTAKE_WINDOW();\n\n // If inside the unlock window, then we can redeem stkAave\n // for AAVE and send it to the vault.\n if (block.timestamp > windowStart && block.timestamp <= windowEnd) {\n // Redeem to AAVE\n uint256 stkAaveBalance = stkAave.balanceOf(address(this));\n stkAave.redeem(address(this), stkAaveBalance);\n\n // Transfer AAVE to harvesterAddress\n uint256 aaveBalance = IERC20(rewardTokenAddresses[0]).balanceOf(\n address(this)\n );\n if (aaveBalance > 0) {\n IERC20(rewardTokenAddresses[0]).safeTransfer(\n harvesterAddress,\n aaveBalance\n );\n }\n }\n\n // Collect available rewards and restart the cooldown timer, if either of\n // those should be run.\n if (block.timestamp > windowStart || cooldown == 0) {\n uint256 assetsLen = assetsMapped.length;\n // aToken addresses for incentives controller\n address[] memory aTokens = new address[](assetsLen);\n for (uint256 i = 0; i < assetsLen; ++i) {\n aTokens[i] = _getATokenFor(assetsMapped[i]);\n }\n\n // 1. If we have rewards availabile, collect them\n uint256 pendingRewards = incentivesController.getRewardsBalance(\n aTokens,\n address(this)\n );\n if (pendingRewards > 0) {\n // Because getting more stkAAVE from the incentives controller\n // with claimRewards() may push the stkAAVE cooldown time\n // forward, it is called after stakedAAVE has been turned into\n // AAVE.\n uint256 collected = incentivesController.claimRewards(\n aTokens,\n pendingRewards,\n address(this)\n );\n require(collected == pendingRewards, \"AAVE reward difference\");\n }\n\n // 2. Start cooldown counting down.\n if (stkAave.balanceOf(address(this)) > 0) {\n // Protected with if since cooldown call would revert\n // if no stkAave balance.\n stkAave.cooldown();\n }\n }\n }\n}\n" + }, + "contracts/strategies/AbstractCompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base Compound Abstract Strategy\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { ICERC20 } from \"./ICompound.sol\";\nimport { IComptroller } from \"../interfaces/IComptroller.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\nabstract contract AbstractCompoundStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n\n int256[50] private __reserved;\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Get the cToken wrapped in the ICERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return Corresponding cToken to this asset\n */\n function _getCTokenFor(address _asset) internal view returns (ICERC20) {\n address cToken = assetToPToken[_asset];\n require(cToken != address(0), \"cToken does not exist\");\n return ICERC20(cToken);\n }\n\n /**\n * @dev Converts an underlying amount into cToken amount\n * cTokenAmt = (underlying * 1e18) / exchangeRate\n * @param _cToken cToken for which to change\n * @param _underlying Amount of underlying to convert\n * @return amount Equivalent amount of cTokens\n */\n function _convertUnderlyingToCToken(ICERC20 _cToken, uint256 _underlying)\n internal\n view\n returns (uint256 amount)\n {\n // e.g. 1e18*1e18 / 205316390724364402565641705 = 50e8\n // e.g. 1e8*1e18 / 205316390724364402565641705 = 0.45 or 0\n amount = (_underlying * 1e18) / _cToken.exchangeRateStored();\n }\n}\n" + }, + "contracts/strategies/AbstractConvexMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { ICurveMetaPool } from \"./ICurveMetaPool.sol\";\nimport { IERC20, AbstractCurveStrategy, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\nabstract contract AbstractConvexMetaStrategy is AbstractCurveStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n event MaxWithdrawalSlippageUpdated(\n uint256 _prevMaxSlippagePercentage,\n uint256 _newMaxSlippagePercentage\n );\n\n // used to circumvent the stack too deep issue\n struct InitConfig {\n address cvxDepositorAddress; //Address of the Convex depositor(AKA booster) for this pool\n address metapoolAddress; //Address of the Curve MetaPool\n address metapoolMainToken; //Address of Main metapool token\n address cvxRewardStakerAddress; //Address of the CVX rewards staker\n address metapoolLPToken; //Address of metapool LP token\n uint256 cvxDepositorPTokenId; //Pid of the pool referred to by Depositor and staker\n }\n\n address internal cvxDepositorAddress;\n address internal cvxRewardStakerAddress;\n uint256 internal cvxDepositorPTokenId;\n ICurveMetaPool internal metapool;\n IERC20 internal metapoolMainToken;\n IERC20 internal metapoolLPToken;\n // Ordered list of metapool assets\n address[] internal metapoolAssets;\n // Max withdrawal slippage denominated in 1e18 (1e18 == 100%)\n uint256 public maxWithdrawalSlippage;\n uint128 internal crvCoinIndex;\n uint128 internal mainCoinIndex;\n\n int256[41] private ___reserved;\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * DAI, USDC, USDT\n * @param _pTokens Platform Token corresponding addresses\n * @param initConfig Various addresses and info for initialization state\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets,\n address[] calldata _pTokens,\n InitConfig calldata initConfig\n ) external onlyGovernor initializer {\n require(_assets.length == 3, \"Must have exactly three assets\");\n // Should be set prior to abstract initialize call otherwise\n // abstractSetPToken calls will fail\n cvxDepositorAddress = initConfig.cvxDepositorAddress;\n pTokenAddress = _pTokens[0];\n metapool = ICurveMetaPool(initConfig.metapoolAddress);\n metapoolMainToken = IERC20(initConfig.metapoolMainToken);\n cvxRewardStakerAddress = initConfig.cvxRewardStakerAddress;\n metapoolLPToken = IERC20(initConfig.metapoolLPToken);\n cvxDepositorPTokenId = initConfig.cvxDepositorPTokenId;\n maxWithdrawalSlippage = 1e16;\n\n metapoolAssets = [metapool.coins(0), metapool.coins(1)];\n crvCoinIndex = _getMetapoolCoinIndex(pTokenAddress);\n mainCoinIndex = _getMetapoolCoinIndex(initConfig.metapoolMainToken);\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n balance = 0;\n\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 contractPTokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (contractPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = contractPTokens.mulTruncate(virtual_price);\n balance += value;\n }\n\n /* We intentionally omit the metapoolLp tokens held by the metastrategyContract\n * since the contract should never (except in the middle of deposit/withdrawal\n * transaction) hold any amount of those tokens in normal operation. There\n * could be tokens sent to it by a 3rd party and we decide to actively ignore\n * those.\n */\n uint256 metapoolGaugePTokens = IRewardStaking(cvxRewardStakerAddress)\n .balanceOf(address(this));\n\n if (metapoolGaugePTokens > 0) {\n uint256 value = metapoolGaugePTokens.mulTruncate(\n metapool.get_virtual_price()\n );\n balance += value;\n }\n\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = balance.scaleBy(assetDecimals, 18) / THREEPOOL_ASSET_COUNT;\n }\n\n /**\n * @dev This function is completely analogous to _calcCurveTokenAmount[AbstractCurveStrategy]\n * and just utilizes different Curve (meta)pool API\n */\n function _calcCurveMetaTokenAmount(uint128 _coinIndex, uint256 _amount)\n internal\n returns (uint256 requiredMetapoolLP)\n {\n uint256[2] memory _amounts = [uint256(0), uint256(0)];\n _amounts[uint256(_coinIndex)] = _amount;\n\n // LP required when removing required asset ignoring fees\n uint256 lpRequiredNoFees = metapool.calc_token_amount(_amounts, false);\n /* LP required if fees would apply to entirety of removed amount\n *\n * fee is 1e10 denominated number: https://curve.readthedocs.io/exchange-pools.html#StableSwap.fee\n */\n uint256 lpRequiredFullFees = lpRequiredNoFees.mulTruncateScale(\n 1e10 + metapool.fee(),\n 1e10\n );\n\n /* asset received when withdrawing full fee applicable LP accounting for\n * slippage and fees\n */\n uint256 assetReceivedForFullLPFees = metapool.calc_withdraw_one_coin(\n lpRequiredFullFees,\n int128(_coinIndex)\n );\n\n // exact amount of LP required\n requiredMetapoolLP =\n (lpRequiredFullFees * _amount) /\n assetReceivedForFullLPFees;\n }\n\n function _approveBase() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // 3Pool for LP token (required for removing liquidity)\n pToken.safeApprove(platformAddress, 0);\n pToken.safeApprove(platformAddress, type(uint256).max);\n // Gauge for LP token\n metapoolLPToken.safeApprove(cvxDepositorAddress, 0);\n metapoolLPToken.safeApprove(cvxDepositorAddress, type(uint256).max);\n // Metapool for LP token\n pToken.safeApprove(address(metapool), 0);\n pToken.safeApprove(address(metapool), type(uint256).max);\n // Metapool for Metapool main token\n metapoolMainToken.safeApprove(address(metapool), 0);\n metapoolMainToken.safeApprove(address(metapool), type(uint256).max);\n }\n\n /**\n * @dev Get the index of the coin\n */\n function _getMetapoolCoinIndex(address _asset)\n internal\n view\n returns (uint128)\n {\n for (uint128 i = 0; i < 2; i++) {\n if (metapoolAssets[i] == _asset) return i;\n }\n revert(\"Invalid Metapool asset\");\n }\n\n /**\n * @dev Sets max withdrawal slippage that is considered when removing\n * liquidity from Metapools.\n * @param _maxWithdrawalSlippage Max withdrawal slippage denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxWithdrawalSlippage should actually be 0.1% (1e15)\n * for production usage. Contract allows as low value as 0% for confirming\n * correct behavior in test suite.\n */\n function setMaxWithdrawalSlippage(uint256 _maxWithdrawalSlippage)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(\n _maxWithdrawalSlippage <= 1e18,\n \"Max withdrawal slippage needs to be between 0% - 100%\"\n );\n emit MaxWithdrawalSlippageUpdated(\n maxWithdrawalSlippage,\n _maxWithdrawalSlippage\n );\n maxWithdrawalSlippage = _maxWithdrawalSlippage;\n }\n\n /**\n * @dev Collect accumulated CRV and CVX and send to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n IRewardStaking(cvxRewardStakerAddress).getReward();\n _collectRewardTokens();\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/AbstractCurveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve 3Pool Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\nabstract contract AbstractCurveStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 internal constant MAX_SLIPPAGE = 1e16; // 1%, same as the Curve UI\n // number of assets in Curve 3Pool (USDC, DAI, USDT)\n uint256 internal constant THREEPOOL_ASSET_COUNT = 3;\n address internal pTokenAddress;\n\n int256[49] private __reserved;\n\n /**\n * @dev Deposit asset into the Curve 3Pool\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_amount > 0, \"Must deposit something\");\n emit Deposit(_asset, pTokenAddress, _amount);\n\n // 3Pool requires passing deposit amounts for all 3 assets, set to 0 for\n // all\n uint256[3] memory _amounts;\n uint256 poolCoinIndex = _getCoinIndex(_asset);\n // Set the amount on the asset we want to deposit\n _amounts[poolCoinIndex] = _amount;\n ICurvePool curvePool = ICurvePool(platformAddress);\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n uint256 depositValue = _amount.scaleBy(18, assetDecimals).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = depositValue.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n // Do the deposit to 3pool\n curvePool.add_liquidity(_amounts, minMintAmount);\n _lpDepositAll();\n }\n\n function _lpDepositAll() internal virtual;\n\n /**\n * @dev Deposit the entire balance of any supported asset into the Curve 3pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n uint256 depositValue = 0;\n ICurvePool curvePool = ICurvePool(platformAddress);\n uint256 curveVirtualPrice = curvePool.get_virtual_price();\n\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address assetAddress = assetsMapped[i];\n uint256 balance = IERC20(assetAddress).balanceOf(address(this));\n if (balance > 0) {\n uint256 poolCoinIndex = _getCoinIndex(assetAddress);\n // Set the amount on the asset we want to deposit\n _amounts[poolCoinIndex] = balance;\n uint256 assetDecimals = Helpers.getDecimals(assetAddress);\n // Get value of deposit in Curve LP token to later determine\n // the minMintAmount argument for add_liquidity\n depositValue =\n depositValue +\n balance.scaleBy(18, assetDecimals).divPrecisely(\n curveVirtualPrice\n );\n emit Deposit(assetAddress, pTokenAddress, balance);\n }\n }\n\n uint256 minMintAmount = depositValue.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n // Do the deposit to 3pool\n curvePool.add_liquidity(_amounts, minMintAmount);\n\n /* In case of Curve Strategy all assets are mapped to the same pToken (3CrvLP). Let\n * descendants further handle the pToken. By either deploying it to the metapool and\n * resulting tokens in Gauge. Or deploying pTokens directly to the Gauge.\n */\n _lpDepositAll();\n }\n\n function _lpWithdraw(uint256 numCrvTokens) internal virtual;\n\n function _lpWithdrawAll() internal virtual;\n\n /**\n * @dev Withdraw asset from Curve 3Pool\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Invalid amount\");\n\n emit Withdrawal(_asset, pTokenAddress, _amount);\n\n uint256 contractCrv3Tokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n\n uint256 coinIndex = _getCoinIndex(_asset);\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 requiredCrv3Tokens = _calcCurveTokenAmount(coinIndex, _amount);\n\n // We have enough LP tokens, make sure they are all on this contract\n if (contractCrv3Tokens < requiredCrv3Tokens) {\n _lpWithdraw(requiredCrv3Tokens - contractCrv3Tokens);\n }\n\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n _amounts[coinIndex] = _amount;\n\n curvePool.remove_liquidity_imbalance(_amounts, requiredCrv3Tokens);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Calculate amount of LP required when withdrawing specific amount of one\n * of the underlying assets accounting for fees and slippage.\n *\n * Curve pools unfortunately do not contain a calculation function for\n * amount of LP required when withdrawing a specific amount of one of the\n * underlying tokens and also accounting for fees (Curve's calc_token_amount\n * does account for slippage but not fees).\n *\n * Steps taken to calculate the metric:\n * - get amount of LP required if fees wouldn't apply\n * - increase the LP amount as if fees would apply to the entirety of the underlying\n * asset withdrawal. (when withdrawing only one coin fees apply only to amounts\n * of other assets pool would return in case of balanced removal - since those need\n * to be swapped for the single underlying asset being withdrawn)\n * - get amount of underlying asset withdrawn (this Curve function does consider slippage\n * and fees) when using the increased LP amount. As LP amount is slightly over-increased\n * so is amount of underlying assets returned.\n * - since we know exactly how much asset we require take the rate of LP required for asset\n * withdrawn to get the exact amount of LP.\n */\n function _calcCurveTokenAmount(uint256 _coinIndex, uint256 _amount)\n internal\n returns (uint256 required3Crv)\n {\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n _amounts[_coinIndex] = _amount;\n\n // LP required when removing required asset ignoring fees\n uint256 lpRequiredNoFees = curvePool.calc_token_amount(_amounts, false);\n /* LP required if fees would apply to entirety of removed amount\n *\n * fee is 1e10 denominated number: https://curve.readthedocs.io/exchange-pools.html#StableSwap.fee\n */\n uint256 lpRequiredFullFees = lpRequiredNoFees.mulTruncateScale(\n 1e10 + curvePool.fee(),\n 1e10\n );\n\n /* asset received when withdrawing full fee applicable LP accounting for\n * slippage and fees\n */\n uint256 assetReceivedForFullLPFees = curvePool.calc_withdraw_one_coin(\n lpRequiredFullFees,\n int128(uint128(_coinIndex))\n );\n\n // exact amount of LP required\n required3Crv =\n (lpRequiredFullFees * _amount) /\n assetReceivedForFullLPFees;\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n _lpWithdrawAll();\n // Withdraws are proportional to assets held by 3Pool\n uint256[3] memory minWithdrawAmounts = [\n uint256(0),\n uint256(0),\n uint256(0)\n ];\n\n // Remove liquidity\n ICurvePool threePool = ICurvePool(platformAddress);\n threePool.remove_liquidity(\n IERC20(pTokenAddress).balanceOf(address(this)),\n minWithdrawAmounts\n );\n // Transfer assets out of Vault\n // Note that Curve will provide all 3 of the assets in 3pool even if\n // we have not set PToken addresses for all of them in this strategy\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n IERC20 asset = IERC20(threePool.coins(i));\n asset.safeTransfer(vaultAddress, asset.balanceOf(address(this)));\n }\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 totalPTokens = IERC20(pTokenAddress).balanceOf(address(this));\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (totalPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = (totalPTokens * virtual_price) / 1e18;\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = value.scaleBy(assetDecimals, 18) / THREEPOOL_ASSET_COUNT;\n }\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n // This strategy is a special case since it only supports one asset\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n _approveAsset(assetsMapped[i]);\n }\n }\n\n /**\n * @dev Call the necessary approvals for the Curve pool and gauge\n * @param _asset Address of the asset\n */\n function _abstractSetPToken(address _asset, address) internal override {\n _approveAsset(_asset);\n }\n\n function _approveAsset(address _asset) internal {\n IERC20 asset = IERC20(_asset);\n // 3Pool for asset (required for adding liquidity)\n asset.safeApprove(platformAddress, 0);\n asset.safeApprove(platformAddress, type(uint256).max);\n }\n\n function _approveBase() internal virtual;\n\n /**\n * @dev Get the index of the coin\n */\n function _getCoinIndex(address _asset) internal view returns (uint256) {\n for (uint256 i = 0; i < 3; i++) {\n if (assetsMapped[i] == _asset) return i;\n }\n revert(\"Invalid 3pool asset\");\n }\n}\n" + }, + "contracts/strategies/aerodrome/AerodromeAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Aerodrome AMO strategy\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nimport { ISugarHelper } from \"../../interfaces/aerodrome/ISugarHelper.sol\";\nimport { INonfungiblePositionManager } from \"../../interfaces/aerodrome/INonfungiblePositionManager.sol\";\nimport { ISwapRouter } from \"../../interfaces/aerodrome/ISwapRouter.sol\";\nimport { ICLPool } from \"../../interfaces/aerodrome/ICLPool.sol\";\nimport { ICLGauge } from \"../../interfaces/aerodrome/ICLGauge.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\n\ncontract AerodromeAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n using SafeCast for uint256;\n\n /************************************************\n Important (!) setup configuration\n *************************************************/\n\n /**\n * In order to be able to remove a reasonable amount of complexity from the contract one of the\n * preconditions for this contract to function correctly is to have an outside account mint a small\n * amount of liquidity in the tick space where the contract will deploy's its liquidity and then send\n * that NFT LP position to a dead address (transfer to zero address not allowed.) See example of such\n * NFT LP token:\n * https://basescan.org/token/0x827922686190790b37229fd06084350e74485b72?a=413296#inventory\n */\n\n /***************************************\n Storage slot members\n ****************************************/\n\n /// @notice tokenId of the liquidity position\n uint256 public tokenId;\n /// @dev Minimum amount of tokens the strategy would be able to withdraw from the pool.\n /// minimum amount of tokens are withdrawn at a 1:1 price\n uint256 public underlyingAssets;\n /// @notice Marks the start of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareStart;\n /// @notice Marks the end of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareEnd;\n /// @dev reserved for inheritance\n int256[46] private __reserved;\n\n /***************************************\n Constants, structs and events\n ****************************************/\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the OETHb token contract\n address public immutable OETHb;\n /// @notice lower tick set to -1 representing the price of 1.0001 of WETH for 1 OETHb.\n int24 public immutable lowerTick;\n /// @notice lower tick set to 0 representing the price of 1.0000 of WETH for 1 OETHb.\n int24 public immutable upperTick;\n /// @notice tick spacing of the pool (set to 1)\n int24 public immutable tickSpacing;\n /// @notice the swapRouter for performing swaps\n ISwapRouter public immutable swapRouter;\n /// @notice the underlying AMO Slipstream pool\n ICLPool public immutable clPool;\n /// @notice the gauge for the corresponding Slipstream pool (clPool)\n /// @dev can become an immutable once the gauge is created on the base main-net\n ICLGauge public immutable clGauge;\n /// @notice the Position manager contract that is used to manage the pool's position\n INonfungiblePositionManager public immutable positionManager;\n /// @notice helper contract for liquidity and ticker math\n ISugarHelper public immutable helper;\n /// @notice sqrtRatioX96TickLower\n /// @dev tick lower has value -1 and represents the lowest price of WETH priced in OETHb. Meaning the pool\n /// offers less than 1 OETHb for 1 WETH. In other terms to get 1 OETHB the swap needs to offer 1.0001 WETH\n /// this is where purchasing OETHb with WETH within the liquidity position is most expensive\n uint160 public immutable sqrtRatioX96TickLower;\n /// @notice sqrtRatioX96TickHigher\n /// @dev tick higher has value 0 and represents 1:1 price parity of WETH to OETHb\n uint160 public immutable sqrtRatioX96TickHigher;\n /// @dev tick closest to 1:1 price parity\n /// Correctly assessing which tick is closer to 1:1 price parity is important since it affects\n /// the way we calculate the underlying assets in check Balance. The underlying aerodrome pool\n /// orders the tokens depending on the values of their addresses. If OETH token is token0 in the pool\n /// then sqrtRatioX96TickClosestToParity=sqrtRatioX96TickLower. If it is token1 in the pool then\n /// sqrtRatioX96TickClosestToParity=sqrtRatioX96TickHigher\n uint160 public immutable sqrtRatioX96TickClosestToParity;\n\n /// @dev a threshold under which the contract no longer allows for the protocol to rebalance. Guarding\n /// against a strategist / guardian being taken over and with multiple transactions draining the\n /// protocol funds.\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth); // 0x989e5ca8\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth); // 0xa6737d87\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n ); // 0x3681e8e0\n error OutsideExpectedTickRange(int24 currentTick); // 0x5a2eba75\n\n event PoolRebalanced(uint256 currentPoolWethShare);\n\n event PoolWethShareIntervalUpdated(\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n\n event LiquidityRemoved(\n uint256 withdrawLiquidityShare,\n uint256 removedWETHAmount,\n uint256 removedOETHbAmount,\n uint256 wethAmountCollected,\n uint256 oethbAmountCollected,\n uint256 underlyingAssets\n );\n\n event LiquidityAdded(\n uint256 wethAmountDesired,\n uint256 oethbAmountDesired,\n uint256 wethAmountSupplied,\n uint256 oethbAmountSupplied,\n uint256 tokenId,\n uint256 underlyingAssets\n );\n\n event UnderlyingAssetsUpdated(uint256 underlyingAssets);\n\n /**\n * @dev Un-stakes the token from the gauge for the execution duration of\n * the function and after that re-stakes it back in.\n *\n * It is important that the token is unstaked and owned by the strategy contract\n * during any liquidity altering operations and that it is re-staked back into the\n * gauge after liquidity changes. If the token fails to re-stake back to the\n * gauge it is not earning incentives.\n */\n // all functions using this modifier are used by functions with reentrancy check\n // slither-disable-start reentrancy-no-eth\n modifier gaugeUnstakeAndRestake() {\n // because of solidity short-circuit _isLpTokenStakedInGauge doesn't get called\n // when tokenId == 0\n if (tokenId != 0 && _isLpTokenStakedInGauge()) {\n clGauge.withdraw(tokenId);\n }\n _;\n // because of solidity short-circuit _isLpTokenStakedInGauge doesn't get called\n // when tokenId == 0\n if (tokenId != 0 && !_isLpTokenStakedInGauge()) {\n /**\n * It can happen that a withdrawal (or a full withdrawal) transactions would\n * remove all of the liquidity from the token with a NFT token still existing.\n * In that case the token can not be staked into the gauge, as some liquidity\n * needs to be added to it first.\n */\n if (_getLiquidity() > 0) {\n // if token liquidity changes the positionManager requires re-approval.\n // to any contract pre-approved to handle the token.\n positionManager.approve(address(clGauge), tokenId);\n clGauge.deposit(tokenId);\n }\n }\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice the constructor\n /// @dev This contract is intended to be used as a proxy. To prevent the\n /// potential confusion of having a functional implementation contract\n /// the constructor has the `initializer` modifier. This way the\n /// `initialize` function can not be called on the implementation contract.\n /// For the same reason the implementation contract also has the governor\n /// set to a zero address.\n /// @param _stratConfig the basic strategy configuration\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _oethbAddress Address of the Erc20 OETHb Token contract\n /// @param _swapRouter Address of the Aerodrome Universal Swap Router\n /// @param _nonfungiblePositionManager Address of position manager to add/remove\n /// the liquidity\n /// @param _clPool Address of the Aerodrome concentrated liquidity pool\n /// @param _clGauge Address of the Aerodrome slipstream pool gauge\n /// @param _sugarHelper Address of the Aerodrome Sugar helper contract\n /// @param _lowerBoundingTick Smaller bounding tick of our liquidity position\n /// @param _upperBoundingTick Larger bounding tick of our liquidity position\n /// @param _tickClosestToParity Tick that is closer to 1:1 price parity\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethbAddress,\n address _swapRouter,\n address _nonfungiblePositionManager,\n address _clPool,\n address _clGauge,\n address _sugarHelper,\n int24 _lowerBoundingTick,\n int24 _upperBoundingTick,\n int24 _tickClosestToParity\n ) initializer InitializableAbstractStrategy(_stratConfig) {\n require(\n _lowerBoundingTick == _tickClosestToParity ||\n _upperBoundingTick == _tickClosestToParity,\n \"Misconfigured tickClosestToParity\"\n );\n require(\n ICLPool(_clPool).token0() == _wethAddress,\n \"Only WETH supported as token0\"\n );\n require(\n ICLPool(_clPool).token1() == _oethbAddress,\n \"Only OETHb supported as token1\"\n );\n int24 _tickSpacing = ICLPool(_clPool).tickSpacing();\n // when we generalize AMO we might support other tick spacings\n require(_tickSpacing == 1, \"Unsupported tickSpacing\");\n\n WETH = _wethAddress;\n OETHb = _oethbAddress;\n swapRouter = ISwapRouter(_swapRouter);\n positionManager = INonfungiblePositionManager(\n _nonfungiblePositionManager\n );\n clPool = ICLPool(_clPool);\n clGauge = ICLGauge(_clGauge);\n helper = ISugarHelper(_sugarHelper);\n sqrtRatioX96TickLower = ISugarHelper(_sugarHelper).getSqrtRatioAtTick(\n _lowerBoundingTick\n );\n sqrtRatioX96TickHigher = ISugarHelper(_sugarHelper).getSqrtRatioAtTick(\n _upperBoundingTick\n );\n sqrtRatioX96TickClosestToParity = ISugarHelper(_sugarHelper)\n .getSqrtRatioAtTick(_tickClosestToParity);\n\n lowerTick = _lowerBoundingTick;\n upperTick = _upperBoundingTick;\n tickSpacing = _tickSpacing;\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /**\n * @notice initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n */\n function initialize(address[] memory _rewardTokenAddresses)\n external\n onlyGovernor\n initializer\n {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n new address[](0),\n new address[](0)\n );\n }\n\n /***************************************\n Configuration \n ****************************************/\n\n /**\n * @notice Set allowed pool weth share interval. After the rebalance happens\n * the share of WETH token in the ticker needs to be withing the specifications\n * of the interval.\n *\n * @param _allowedWethShareStart Start of WETH share interval expressed as 18 decimal amount\n * @param _allowedWethShareEnd End of WETH share interval expressed as 18 decimal amount\n */\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external onlyGovernor {\n require(\n _allowedWethShareStart < _allowedWethShareEnd,\n \"Invalid interval\"\n );\n // can not go below 1% weth share\n require(_allowedWethShareStart > 0.01 ether, \"Invalid interval start\");\n // can not go above 95% weth share\n require(_allowedWethShareEnd < 0.95 ether, \"Invalid interval end\");\n\n allowedWethShareStart = _allowedWethShareStart;\n allowedWethShareEnd = _allowedWethShareEnd;\n emit PoolWethShareIntervalUpdated(\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n\n /***************************************\n Periphery utils\n ****************************************/\n\n function _isLpTokenStakedInGauge() internal view returns (bool) {\n require(tokenId != 0, \"Missing NFT LP token\");\n\n address owner = positionManager.ownerOf(tokenId);\n require(\n owner == address(clGauge) || owner == address(this),\n \"Unexpected token owner\"\n );\n return owner == address(clGauge);\n }\n\n /***************************************\n Strategy overrides \n ****************************************/\n\n /**\n * @notice Deposit an amount of assets into the strategy contract. Calling deposit doesn't\n * automatically deposit funds into the underlying Aerodrome pool\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposit WETH to the strategy contract. This function does not add liquidity to the\n * underlying Aerodrome pool.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance > 1e12) {\n _deposit(WETH, _wethBalance);\n }\n }\n\n /**\n * @dev Deposit WETH to the contract. This function doesn't deposit the liquidity to the\n * pool, that is done via the rebalance call.\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n emit Deposit(_asset, address(0), _amount);\n\n // if the pool price is not within the expected interval leave the WETH on the contract\n // as to not break the mints\n (bool _isExpectedRange, ) = _checkForExpectedPoolPrice(false);\n if (_isExpectedRange) {\n // deposit funds into the underlying pool\n _rebalance(0, false, 0);\n }\n }\n\n /**\n * @notice Rebalance the pool to the desired token split and Deposit any WETH on the contract to the\n * underlying aerodrome pool. Print the required amount of corresponding OETHb. After the rebalancing is\n * done burn any potentially remaining OETHb tokens still on the strategy contract.\n *\n * This function has a slightly different behaviour depending on the status of the underlying Aerodrome\n * slipstream pool. The function consists of the following 3 steps:\n * 1. withdrawPartialLiquidity -> so that moving the activeTrading price via a swap is cheaper\n * 2. swapToDesiredPosition -> move active trading price in the pool to be able to deposit WETH & OETHb\n * tokens with the desired pre-configured shares\n * 3. addLiquidity -> add liquidity into the pool respecting share split configuration\n *\n * Scenario 1: When there is no liquidity in the pool from the strategy but there is from other LPs then\n * only step 1 is skipped. (It is important to note that liquidity needs to exist in the configured\n * strategy tick ranges in order for the swap to be possible) Step 3 mints new liquidity position\n * instead of adding to an existing one.\n * Scenario 2: When there is strategy's liquidity in the pool all 3 steps are taken\n *\n *\n * Exact _amountToSwap, _swapWeth & _minTokenReceived parameters shall be determined by simulating the\n * transaction off-chain. The strategy checks that after the swap the share of the tokens is in the\n * expected ranges.\n *\n * @param _amountToSwap The amount of the token to swap\n * @param _swapWeth Swap using WETH when true, use OETHb when false\n * @param _minTokenReceived Slippage check -> minimum amount of token expected in return\n */\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) external nonReentrant onlyGovernorOrStrategist {\n _rebalance(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n\n function _rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n /**\n * Would be nice to check if there is any total liquidity in the pool before performing this swap\n * but there is no easy way to do that in UniswapV3:\n * - clPool.liquidity() -> only liquidity in the active tick\n * - asset[1&2].balanceOf(address(clPool)) -> will include uncollected tokens of LP providers\n * after their liquidity position has been decreased\n */\n\n /**\n * When rebalance is called for the first time there is no strategy\n * liquidity in the pool yet. The liquidity removal is thus skipped.\n * Also execute this function when WETH is required for the swap.\n */\n if (tokenId != 0 && _swapWeth && _amountToSwap > 0) {\n _ensureWETHBalance(_amountToSwap);\n }\n\n // in some cases we will just want to add liquidity and not issue a swap to move the\n // active trading position within the pool\n if (_amountToSwap > 0) {\n _swapToDesiredPosition(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n // calling check liquidity early so we don't get unexpected errors when adding liquidity\n // in the later stages of this function\n _checkForExpectedPoolPrice(true);\n\n _addLiquidity();\n\n // this call shouldn't be necessary, since adding liquidity shouldn't affect the active\n // trading price. It is a defensive programming measure.\n (, uint256 _wethSharePct) = _checkForExpectedPoolPrice(true);\n\n // revert if protocol insolvent\n _solvencyAssert();\n\n emit PoolRebalanced(_wethSharePct);\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethbSupply = IERC20(OETHb).totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethbSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /**\n * @dev Decrease partial or all liquidity from the pool.\n * @param _liquidityToDecrease The amount of liquidity to remove expressed in 18 decimal point\n */\n function _removeLiquidity(uint256 _liquidityToDecrease)\n internal\n gaugeUnstakeAndRestake\n {\n require(_liquidityToDecrease > 0, \"Must remove some liquidity\");\n\n uint128 _liquidity = _getLiquidity();\n // need to convert to uint256 since intermittent result is to big for uint128 to handle\n uint128 _liquidityToRemove = uint256(_liquidity)\n .mulTruncate(_liquidityToDecrease)\n .toUint128();\n\n /**\n * There is no liquidity to remove -> exit function early. This can happen after a\n * withdraw/withdrawAll removes all of the liquidity while retaining the NFT token.\n */\n if (_liquidity == 0 || _liquidityToRemove == 0) {\n return;\n }\n\n (uint256 _amountWeth, uint256 _amountOethb) = positionManager\n .decreaseLiquidity(\n // Both expected amounts can be 0 since we don't really care if any swaps\n // happen just before the liquidity removal.\n INonfungiblePositionManager.DecreaseLiquidityParams({\n tokenId: tokenId,\n liquidity: _liquidityToRemove,\n amount0Min: 0,\n amount1Min: 0,\n deadline: block.timestamp\n })\n );\n\n (\n uint256 _amountWethCollected,\n uint256 _amountOethbCollected\n ) = positionManager.collect(\n INonfungiblePositionManager.CollectParams({\n tokenId: tokenId,\n recipient: address(this),\n amount0Max: type(uint128).max, // defaults to all tokens owed\n amount1Max: type(uint128).max // defaults to all tokens owed\n })\n );\n\n _updateUnderlyingAssets();\n\n emit LiquidityRemoved(\n _liquidityToDecrease,\n _amountWeth, //removedWethAmount\n _amountOethb, //removedOethbAmount\n _amountWethCollected,\n _amountOethbCollected,\n underlyingAssets\n );\n\n _burnOethbOnTheContract();\n }\n\n /**\n * @dev Perform a swap so that after the swap the ticker has the desired WETH to OETHb token share.\n */\n function _swapToDesiredPosition(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n IERC20 _tokenToSwap = IERC20(_swapWeth ? WETH : OETHb);\n uint256 _balance = _tokenToSwap.balanceOf(address(this));\n\n if (_balance < _amountToSwap) {\n // This should never trigger since _ensureWETHBalance will already\n // throw an error if there is not enough WETH\n if (_swapWeth) {\n revert NotEnoughWethForSwap(_balance, _amountToSwap);\n }\n // if swapping OETHb\n uint256 mintForSwap = _amountToSwap - _balance;\n IVault(vaultAddress).mintForStrategy(mintForSwap);\n }\n\n // approve the specific amount of WETH required\n if (_swapWeth) {\n IERC20(WETH).approve(address(swapRouter), _amountToSwap);\n }\n\n // Swap it\n swapRouter.exactInputSingle(\n // sqrtPriceLimitX96 is just a rough sanity check that we are within 0 -> 1 tick\n // a more fine check is performed in _checkForExpectedPoolPrice\n // Note: this needs further work if we want to generalize this approach\n ISwapRouter.ExactInputSingleParams({\n tokenIn: address(_tokenToSwap),\n tokenOut: _swapWeth ? OETHb : WETH,\n tickSpacing: tickSpacing, // set to 1\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: _amountToSwap,\n amountOutMinimum: _minTokenReceived, // slippage check\n sqrtPriceLimitX96: _swapWeth\n ? sqrtRatioX96TickLower\n : sqrtRatioX96TickHigher\n })\n );\n\n /**\n * In the interest of each function in _rebalance to leave the contract state as\n * clean as possible the OETHb tokens here are burned. This decreases the\n * dependence where `_swapToDesiredPosition` function relies on later functions\n * (`addLiquidity`) to burn the OETHb. Reducing the risk of error introduction.\n */\n _burnOethbOnTheContract();\n }\n\n /**\n * @dev Add liquidity into the pool in the pre-configured WETH to OETHb share ratios\n * defined by the allowedPoolWethShareStart|End interval. This function will respect\n * liquidity ratios when there is no liquidity yet in the pool. If liquidity is already\n * present then it relies on the `_swapToDesiredPosition` function in a step before\n * to already move the trading price to desired position (with some tolerance).\n */\n // rebalance already has re-entrency checks\n // slither-disable-start reentrancy-no-eth\n function _addLiquidity() internal gaugeUnstakeAndRestake {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n // don't deposit small liquidity amounts\n if (_wethBalance <= 1e12) {\n return;\n }\n\n uint160 _currentPrice = getPoolX96Price();\n /**\n * Sanity check active trading price is positioned within our desired tick.\n *\n * We revert when price is equal to the lower tick even though that is still\n * a valid amount in regards to ticker position by Sugar.estimateAmount call.\n * Current price equaling tick bound at the 1:1 price parity results in\n * uint overfow when calculating the OETHb balance to deposit.\n */\n if (\n _currentPrice <= sqrtRatioX96TickLower ||\n _currentPrice >= sqrtRatioX96TickHigher\n ) {\n revert OutsideExpectedTickRange(getCurrentTradingTick());\n }\n\n /**\n * If estimateAmount1 call fails it could be due to _currentPrice being really\n * close to a tick and amount1 is a larger number than the sugar helper is able\n * to compute.\n *\n * If token addresses were reversed estimateAmount0 would be required here\n */\n uint256 _oethbRequired = helper.estimateAmount1(\n _wethBalance,\n address(0), // no need to pass pool address when current price is specified\n _currentPrice,\n lowerTick,\n upperTick\n );\n\n if (_oethbRequired > _oethbBalance) {\n IVault(vaultAddress).mintForStrategy(\n _oethbRequired - _oethbBalance\n );\n }\n\n // approve the specific amount of WETH required\n IERC20(WETH).approve(address(positionManager), _wethBalance);\n\n uint256 _wethAmountSupplied;\n uint256 _oethbAmountSupplied;\n if (tokenId == 0) {\n (\n tokenId,\n ,\n _wethAmountSupplied,\n _oethbAmountSupplied\n ) = positionManager.mint(\n /** amount0Min & amount1Min are left at 0 because slippage protection is ensured by the\n * _checkForExpectedPoolPrice\n *›\n * Also sqrtPriceX96 is 0 because the pool is already created\n * non zero amount attempts to create a new instance of the pool\n */\n INonfungiblePositionManager.MintParams({\n token0: WETH,\n token1: OETHb,\n tickSpacing: tickSpacing,\n tickLower: lowerTick,\n tickUpper: upperTick,\n amount0Desired: _wethBalance,\n amount1Desired: _oethbRequired,\n amount0Min: 0,\n amount1Min: 0,\n recipient: address(this),\n deadline: block.timestamp,\n sqrtPriceX96: 0\n })\n );\n } else {\n (, _wethAmountSupplied, _oethbAmountSupplied) = positionManager\n .increaseLiquidity(\n /** amount0Min & amount1Min are left at 0 because slippage protection is ensured by the\n * _checkForExpectedPoolPrice\n */\n INonfungiblePositionManager.IncreaseLiquidityParams({\n tokenId: tokenId,\n amount0Desired: _wethBalance,\n amount1Desired: _oethbRequired,\n amount0Min: 0,\n amount1Min: 0,\n deadline: block.timestamp\n })\n );\n }\n\n _updateUnderlyingAssets();\n emit LiquidityAdded(\n _wethBalance, // wethAmountDesired\n _oethbRequired, // oethbAmountDesired\n _wethAmountSupplied, // wethAmountSupplied\n _oethbAmountSupplied, // oethbAmountSupplied\n tokenId, // tokenId\n underlyingAssets\n );\n\n // burn remaining OETHb\n _burnOethbOnTheContract();\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @dev Check that the Aerodrome pool price is within the expected\n * parameters.\n * This function works whether the strategy contract has liquidity\n * position in the pool or not. The function returns _wethSharePct\n * as a gas optimization measure.\n * @param throwException when set to true the function throws an exception\n * when pool's price is not within expected range.\n * @return _isExpectedRange Bool expressing price is within expected range\n * @return _wethSharePct Share of WETH owned by this strategy contract in the\n * configured ticker.\n */\n function _checkForExpectedPoolPrice(bool throwException)\n internal\n view\n returns (bool _isExpectedRange, uint256 _wethSharePct)\n {\n require(\n allowedWethShareStart != 0 && allowedWethShareEnd != 0,\n \"Weth share interval not set\"\n );\n\n uint160 _currentPrice = getPoolX96Price();\n\n /**\n * First check we are in expected tick range\n *\n * We revert even though price being equal to the lower tick would still\n * count being within lower tick for the purpose of Sugar.estimateAmount calls\n */\n if (\n _currentPrice <= sqrtRatioX96TickLower ||\n _currentPrice >= sqrtRatioX96TickHigher\n ) {\n if (throwException) {\n revert OutsideExpectedTickRange(getCurrentTradingTick());\n }\n return (false, 0);\n }\n\n // 18 decimal number expressed WETH tick share\n _wethSharePct = _getWethShare(_currentPrice);\n\n if (\n _wethSharePct < allowedWethShareStart ||\n _wethSharePct > allowedWethShareEnd\n ) {\n if (throwException) {\n revert PoolRebalanceOutOfBounds(\n _wethSharePct,\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n return (false, _wethSharePct);\n }\n\n return (true, _wethSharePct);\n }\n\n /**\n * Burns any OETHb tokens remaining on the strategy contract\n */\n function _burnOethbOnTheContract() internal {\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n if (_oethbBalance > 1e12) {\n IVault(vaultAddress).burnForStrategy(_oethbBalance);\n }\n }\n\n /// @dev This function assumes there are no uncollected tokens in the clPool owned by the strategy contract.\n /// For that reason any liquidity withdrawals must also collect the tokens.\n function _updateUnderlyingAssets() internal {\n if (tokenId == 0) {\n underlyingAssets = 0;\n emit UnderlyingAssetsUpdated(underlyingAssets);\n return;\n }\n\n uint128 _liquidity = _getLiquidity();\n\n /**\n * Our net value represent the smallest amount of tokens we are able to extract from the position\n * given our liquidity.\n *\n * The least amount of tokens extraditable from the position is where the active trading price is\n * at the ticker 0 meaning the pool is offering 1:1 trades between WETH & OETHb. At that moment the pool\n * consists completely of OETHb and no WETH.\n *\n * The more swaps from WETH -> OETHb happen on the pool the more the price starts to move towards the -1\n * ticker making OETHb (priced in WETH) more expensive.\n *\n * An additional note: when liquidity is 0 then the helper returns 0 for both token amounts. And the\n * function set underlying assets to 0.\n */\n (uint256 _wethAmount, uint256 _oethbAmount) = helper\n .getAmountsForLiquidity(\n sqrtRatioX96TickClosestToParity, // sqrtRatioX96\n sqrtRatioX96TickLower, // sqrtRatioAX96\n sqrtRatioX96TickHigher, // sqrtRatioBX96\n _liquidity\n );\n\n require(_wethAmount == 0, \"Non zero wethAmount\");\n underlyingAssets = _oethbAmount;\n emit UnderlyingAssetsUpdated(underlyingAssets);\n }\n\n /**\n * @dev This function removes the appropriate amount of liquidity to assure that the required\n * amount of WETH is available on the contract\n *\n * @param _amount WETH balance required on the contract\n */\n function _ensureWETHBalance(uint256 _amount) internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance >= _amount) {\n return;\n }\n\n require(tokenId != 0, \"No liquidity available\");\n uint256 _additionalWethRequired = _amount - _wethBalance;\n (uint256 _wethInThePool, ) = getPositionPrincipal();\n\n if (_wethInThePool < _additionalWethRequired) {\n revert NotEnoughWethLiquidity(\n _wethInThePool,\n _additionalWethRequired\n );\n }\n\n uint256 shareOfWethToRemove = Math.min(\n _additionalWethRequired.divPrecisely(_wethInThePool) + 1,\n 1e18\n );\n _removeLiquidity(shareOfWethToRemove);\n }\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset WETH address\n * @param _amount Amount of WETH to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n _ensureWETHBalance(_amount);\n\n _withdraw(_recipient, _amount);\n }\n\n /**\n * @notice Withdraw WETH and sends it to the Vault.\n */\n function withdrawAll() external override onlyVault nonReentrant {\n if (tokenId != 0) {\n _removeLiquidity(1e18);\n }\n\n uint256 _balance = IERC20(WETH).balanceOf(address(this));\n if (_balance > 0) {\n _withdraw(vaultAddress, _balance);\n }\n }\n\n function _withdraw(address _recipient, uint256 _amount) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n IERC20(WETH).safeTransfer(_recipient, _amount);\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /**\n * @dev Collect the AERO token from the gauge\n */\n function _collectRewardTokens() internal override {\n if (tokenId != 0 && _isLpTokenStakedInGauge()) {\n clGauge.getReward(tokenId);\n }\n super._collectRewardTokens();\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /**\n * @dev Approve the spending of all assets\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n // to add liquidity to the clPool\n IERC20(OETHb).approve(address(positionManager), type(uint256).max);\n // to be able to rebalance using the swapRouter\n IERC20(OETHb).approve(address(swapRouter), type(uint256).max);\n\n /* the behaviour of this strategy has slightly changed and WETH could be\n * present on the contract between the transactions. For that reason we are\n * un-approving WETH to the swapRouter & positionManager and only approving\n * the required amount before a transaction\n */\n IERC20(WETH).approve(address(swapRouter), 0);\n IERC20(WETH).approve(address(positionManager), 0);\n }\n\n /***************************************\n Balances and Fees\n ****************************************/\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256)\n {\n require(_asset == WETH, \"Only WETH supported\");\n\n // we could in theory deposit to the strategy and forget to call rebalance in the same\n // governance transaction batch. In that case the WETH that is on the strategy contract\n // also needs to be accounted for.\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n // just paranoia check, in case there is OETHb in the strategy that for some reason hasn't\n // been burned yet.\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n return underlyingAssets + _wethBalance + _oethbBalance;\n }\n\n /**\n * @dev Returns the balance of both tokens in a given position (excluding fees)\n * @return _amountWeth Amount of WETH in position\n * @return _amountOethb Amount of OETHb in position\n */\n function getPositionPrincipal()\n public\n view\n returns (uint256 _amountWeth, uint256 _amountOethb)\n {\n if (tokenId == 0) {\n return (0, 0);\n }\n\n uint160 _sqrtRatioX96 = getPoolX96Price();\n (_amountWeth, _amountOethb) = helper.principal(\n positionManager,\n tokenId,\n _sqrtRatioX96\n );\n }\n\n /**\n * @notice Returns the current pool price in X96 format\n * @return _sqrtRatioX96 Pool price\n */\n function getPoolX96Price() public view returns (uint160 _sqrtRatioX96) {\n (_sqrtRatioX96, , , , , ) = clPool.slot0();\n }\n\n /**\n * @notice Returns the current active trading tick of the underlying pool\n * @return _currentTick Current pool trading tick\n */\n function getCurrentTradingTick() public view returns (int24 _currentTick) {\n (, _currentTick, , , , ) = clPool.slot0();\n }\n\n /**\n * @notice Returns the percentage of WETH liquidity in the configured ticker\n * owned by this strategy contract.\n * @return uint256 1e18 denominated percentage expressing the share\n */\n function getWETHShare() external view returns (uint256) {\n uint160 _currentPrice = getPoolX96Price();\n return _getWethShare(_currentPrice);\n }\n\n /**\n * @notice Returns the amount of liquidity in the contract's LP position\n * @return _liquidity Amount of liquidity in the position\n */\n function _getLiquidity() internal view returns (uint128 _liquidity) {\n if (tokenId == 0) {\n revert(\"No LP position\");\n }\n\n (, , , , , , , _liquidity, , , , ) = positionManager.positions(tokenId);\n }\n\n function _getWethShare(uint160 _currentPrice)\n internal\n view\n returns (uint256)\n {\n /**\n * If estimateAmount1 call fails it could be due to _currentPrice being really\n * close to a tick and amount1 too big to compute.\n *\n * If token addresses were reversed estimateAmount0 would be required here\n */\n uint256 _normalizedWethAmount = 1 ether;\n uint256 _correspondingOethAmount = helper.estimateAmount1(\n _normalizedWethAmount,\n address(0), // no need to pass pool address when current price is specified\n _currentPrice,\n lowerTick,\n upperTick\n );\n\n // 18 decimal number expressed weth tick share\n return\n _normalizedWethAmount.divPrecisely(\n _normalizedWethAmount + _correspondingOethAmount\n );\n }\n\n /***************************************\n Hidden functions\n ****************************************/\n /// @inheritdoc InitializableAbstractStrategy\n function setPTokenAddress(address, address) external override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /// @inheritdoc InitializableAbstractStrategy\n function removePToken(uint256) external override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Not supported\n */\n function _abstractSetPToken(address, address) internal override {\n // the deployer shall call safeApproveAllTokens() to set necessary approvals\n revert(\"Unsupported method\");\n }\n\n /***************************************\n ERC721 management\n ****************************************/\n\n /// @notice Callback function for whenever a NFT is transferred to this contract\n // solhint-disable-next-line max-line-length\n /// Ref: https://docs.openzeppelin.com/contracts/3.x/api/token/erc721#IERC721Receiver-onERC721Received-address-address-uint256-bytes-\n function onERC721Received(\n address,\n address,\n uint256,\n bytes calldata\n ) external returns (bytes4) {\n return this.onERC721Received.selector;\n }\n}\n" + }, + "contracts/strategies/balancer/AbstractAuraStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Base Balancer Abstract Strategy\n * @author Origin Protocol Inc\n */\n\nimport { AbstractBalancerStrategy } from \"./AbstractBalancerStrategy.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IERC4626 } from \"../../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport { IRewardStaking } from \"../IRewardStaking.sol\";\n\nabstract contract AbstractAuraStrategy is AbstractBalancerStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n /// @notice Address of the Aura rewards pool\n address public immutable auraRewardPoolAddress;\n\n // renamed from __reserved to not shadow AbstractBalancerStrategy.__reserved,\n int256[50] private __reserved_baseAuraStrategy;\n\n constructor(address _auraRewardPoolAddress) {\n auraRewardPoolAddress = _auraRewardPoolAddress;\n }\n\n /**\n * @dev Deposit all Balancer Pool Tokens (BPT) in this strategy contract\n * to the Aura rewards pool.\n */\n function _lpDepositAll() internal virtual override {\n uint256 bptBalance = IERC20(platformAddress).balanceOf(address(this));\n uint256 auraLp = IERC4626(auraRewardPoolAddress).deposit(\n bptBalance,\n address(this)\n );\n require(bptBalance == auraLp, \"Aura LP != BPT\");\n }\n\n /**\n * @dev Withdraw `numBPTTokens` Balancer Pool Tokens (BPT) from\n * the Aura rewards pool to this strategy contract.\n * @param numBPTTokens Number of Balancer Pool Tokens (BPT) to withdraw\n */\n function _lpWithdraw(uint256 numBPTTokens) internal virtual override {\n IRewardStaking(auraRewardPoolAddress).withdrawAndUnwrap(\n numBPTTokens,\n true // also claim reward tokens\n );\n }\n\n /**\n * @dev Withdraw all Balancer Pool Tokens (BPT) from\n * the Aura rewards pool to this strategy contract.\n */\n function _lpWithdrawAll() internal virtual override {\n // Get all the strategy's BPTs in Aura\n // maxRedeem is implemented as balanceOf(address) in Aura\n uint256 bptBalance = IERC4626(auraRewardPoolAddress).maxRedeem(\n address(this)\n );\n\n IRewardStaking(auraRewardPoolAddress).withdrawAndUnwrap(\n bptBalance,\n true // also claim reward tokens\n );\n }\n\n /**\n * @notice Collects BAL and AURA tokens from the rewards pool.\n */\n function collectRewardTokens()\n external\n virtual\n override\n onlyHarvester\n nonReentrant\n {\n /* Similar to Convex, calling this function collects both of the\n * accrued BAL and AURA tokens.\n */\n IRewardStaking(auraRewardPoolAddress).getReward();\n _collectRewardTokens();\n }\n\n /// @notice Balancer Pool Tokens (BPT) in the Balancer pool and the Aura rewards pool.\n function _getBalancerPoolTokens()\n internal\n view\n override\n returns (uint256 balancerPoolTokens)\n {\n balancerPoolTokens =\n IERC20(platformAddress).balanceOf(address(this)) +\n // maxRedeem is implemented as balanceOf(address) in Aura\n IERC4626(auraRewardPoolAddress).maxRedeem(address(this));\n }\n\n function _approveBase() internal virtual override {\n super._approveBase();\n\n IERC20 pToken = IERC20(platformAddress);\n pToken.safeApprove(auraRewardPoolAddress, type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/balancer/AbstractBalancerStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Balancer Abstract Strategy\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\nimport { IRateProvider } from \"../../interfaces/balancer/IRateProvider.sol\";\nimport { VaultReentrancyLib } from \"./VaultReentrancyLib.sol\";\nimport { IOracle } from \"../../interfaces/IOracle.sol\";\nimport { IWstETH } from \"../../interfaces/IWstETH.sol\";\nimport { IERC4626 } from \"../../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nabstract contract AbstractBalancerStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n address public immutable rETH;\n address public immutable stETH;\n address public immutable wstETH;\n address public immutable frxETH;\n address public immutable sfrxETH;\n\n /// @notice Address of the Balancer vault\n IBalancerVault public immutable balancerVault;\n /// @notice Balancer pool identifier\n bytes32 public immutable balancerPoolId;\n\n // Max withdrawal deviation denominated in 1e18 (1e18 == 100%)\n uint256 public maxWithdrawalDeviation;\n // Max deposit deviation denominated in 1e18 (1e18 == 100%)\n uint256 public maxDepositDeviation;\n\n int256[48] private __reserved;\n\n struct BaseBalancerConfig {\n address rEthAddress; // Address of the rETH token\n address stEthAddress; // Address of the stETH token\n address wstEthAddress; // Address of the wstETH token\n address frxEthAddress; // Address of the frxEth token\n address sfrxEthAddress; // Address of the sfrxEth token\n address balancerVaultAddress; // Address of the Balancer vault\n bytes32 balancerPoolId; // Balancer pool identifier\n }\n\n event MaxWithdrawalDeviationUpdated(\n uint256 _prevMaxDeviationPercentage,\n uint256 _newMaxDeviationPercentage\n );\n event MaxDepositDeviationUpdated(\n uint256 _prevMaxDeviationPercentage,\n uint256 _newMaxDeviationPercentage\n );\n\n /**\n * @dev Ensure we are not in a Vault context when this function is called, by attempting a no-op internal\n * balance operation. If we are already in a Vault transaction (e.g., a swap, join, or exit), the Vault's\n * reentrancy protection will cause this function to revert.\n *\n * Use this modifier with any function that can cause a state change in a pool and is either public itself,\n * or called by a public function *outside* a Vault operation (e.g., join, exit, or swap).\n *\n * This is to protect against Balancer's read-only re-entrancy vulnerability:\n * https://www.notion.so/originprotocol/Balancer-read-only-reentrancy-c686e72c82414ef18fa34312bb02e11b\n */\n modifier whenNotInBalancerVaultContext() {\n VaultReentrancyLib.ensureNotInVaultContext(balancerVault);\n _;\n }\n\n constructor(BaseBalancerConfig memory _balancerConfig) {\n rETH = _balancerConfig.rEthAddress;\n stETH = _balancerConfig.stEthAddress;\n wstETH = _balancerConfig.wstEthAddress;\n frxETH = _balancerConfig.frxEthAddress;\n sfrxETH = _balancerConfig.sfrxEthAddress;\n\n balancerVault = IBalancerVault(_balancerConfig.balancerVaultAddress);\n balancerPoolId = _balancerConfig.balancerPoolId;\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Balancer's strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of BAL & AURA\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * WETH, stETH\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // BAL & AURA\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n maxWithdrawalDeviation = 1e16;\n maxDepositDeviation = 1e16;\n\n emit MaxWithdrawalDeviationUpdated(0, maxWithdrawalDeviation);\n emit MaxDepositDeviationUpdated(0, maxDepositDeviation);\n\n IERC20[] memory poolAssets = _getPoolAssets();\n require(\n poolAssets.length == _assets.length,\n \"Pool assets length mismatch\"\n );\n for (uint256 i = 0; i < _assets.length; ++i) {\n address asset = _fromPoolAsset(address(poolAssets[i]));\n require(_assets[i] == asset, \"Pool assets mismatch\");\n }\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @notice Get strategy's share of an assets in the Balancer pool.\n * This is not denominated in OUSD/ETH value of the assets in the Balancer pool.\n * @param _asset Address of the Vault collateral asset\n * @return amount the amount of vault collateral assets\n *\n * IMPORTANT if this function is overridden it needs to have a whenNotInBalancerVaultContext\n * modifier on it or it is susceptible to read-only re-entrancy attack\n *\n * @dev it is important that this function is not affected by reporting inflated\n * values of assets in case of any pool manipulation. Such a manipulation could easily\n * exploit the protocol by:\n * - minting OETH\n * - tilting Balancer pool to report higher balances of assets\n * - rebasing() -> all that extra token balances get distributed to OETH holders\n * - tilting pool back\n * - redeeming OETH\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n whenNotInBalancerVaultContext\n returns (uint256 amount)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n\n uint256 bptBalance = _getBalancerPoolTokens();\n\n /* To calculate the worth of queried asset:\n * - assume that all tokens normalized to their ETH value have an equal split balance\n * in the pool when it is balanced\n * - multiply the BPT amount with the bpt rate to get the ETH denominated amount\n * of strategy's holdings\n * - divide that by the number of tokens we support in the pool to get ETH denominated\n * amount that is applicable to each supported token in the pool.\n *\n * It would be possible to support only 1 asset in the pool (and be exposed to all\n * the assets while holding BPT tokens) and deposit/withdraw/checkBalance using only\n * that asset. TBD: changes to other functions still required if we ever decide to\n * go with such configuration.\n */\n amount = (bptBalance.mulTruncate(\n IRateProvider(platformAddress).getRate()\n ) / assetsMapped.length);\n\n /* If the pool asset is equal to (strategy )_asset it means that a rate\n * provider for that asset exists and that asset is not necessarily\n * pegged to a unit (ETH).\n *\n * Because this function returns the balance of the asset and is not denominated in\n * ETH units we need to convert the ETH denominated amount to asset amount.\n */\n if (_toPoolAsset(_asset) == _asset) {\n amount = amount.divPrecisely(_getRateProviderRate(_asset));\n }\n }\n\n /**\n * @notice Returns the value of all assets managed by this strategy.\n * Uses the Balancer pool's rate (virtual price) to convert the strategy's\n * Balancer Pool Tokens (BPT) to ETH value.\n * @return value The ETH value\n *\n * IMPORTANT if this function is overridden it needs to have a whenNotInBalancerVaultContext\n * modifier on it or it is susceptible to read-only re-entrancy attack\n */\n function checkBalance()\n external\n view\n virtual\n whenNotInBalancerVaultContext\n returns (uint256 value)\n {\n uint256 bptBalance = _getBalancerPoolTokens();\n\n // Convert BPT to ETH value\n value = bptBalance.mulTruncate(\n IRateProvider(platformAddress).getRate()\n );\n }\n\n /// @notice Balancer Pool Tokens (BPT) in the Balancer pool.\n function _getBalancerPoolTokens()\n internal\n view\n virtual\n returns (uint256 balancerPoolTokens)\n {\n balancerPoolTokens = IERC20(platformAddress).balanceOf(address(this));\n }\n\n /* solhint-disable max-line-length */\n /**\n * @notice BPT price is calculated by taking the rate from the rateProvider of the asset in\n * question. If one does not exist it defaults to 1e18. To get the final BPT expected that\n * is multiplied by the underlying asset amount divided by BPT token rate. BPT token rate is\n * similar to Curve's virtual_price and expresses how much has the price of BPT appreciated\n * (e.g. due to swap fees) in relation to the underlying assets\n *\n * Using the above approach makes the strategy vulnerable to a possible MEV attack using\n * flash loan to manipulate the pool before a deposit/withdrawal since the function ignores\n * market values of the assets being priced in BPT.\n *\n * At the time of writing there is no safe on-chain approach to pricing BPT in a way that it\n * would make it invulnerable to MEV pool manipulation. See recent Balancer exploit:\n * https://www.notion.so/originprotocol/Balancer-OETH-strategy-9becdea132704e588782a919d7d471eb?pvs=4#1cf07de12fc64f1888072321e0644348\n *\n * To mitigate MEV possibilities during deposits and withdraws, the VaultValueChecker will use checkBalance before and after the move\n * to ensure the expected changes took place.\n *\n * @param _asset Address of the Balancer pool asset\n * @param _amount Amount of the Balancer pool asset\n * @return bptExpected of BPT expected in exchange for the asset\n *\n * @dev\n * bptAssetPrice = 1e18 (asset peg) * pool_asset_rate\n *\n * bptExpected = bptAssetPrice * asset_amount / BPT_token_rate\n *\n * bptExpected = 1e18 (asset peg) * pool_asset_rate * asset_amount / BPT_token_rate\n * bptExpected = asset_amount * pool_asset_rate / BPT_token_rate\n *\n * further information available here:\n * https://www.notion.so/originprotocol/Balancer-OETH-strategy-9becdea132704e588782a919d7d471eb?pvs=4#ce01495ae70346d8971f5dced809fb83\n */\n /* solhint-enable max-line-length */\n function _getBPTExpected(address _asset, uint256 _amount)\n internal\n view\n virtual\n returns (uint256 bptExpected)\n {\n uint256 bptRate = IRateProvider(platformAddress).getRate();\n uint256 poolAssetRate = _getRateProviderRate(_asset);\n bptExpected = _amount.mulTruncate(poolAssetRate).divPrecisely(bptRate);\n }\n\n function _getBPTExpected(\n address[] memory _assets,\n uint256[] memory _amounts\n ) internal view virtual returns (uint256 bptExpected) {\n require(_assets.length == _amounts.length, \"Assets & amounts mismatch\");\n\n for (uint256 i = 0; i < _assets.length; ++i) {\n uint256 poolAssetRate = _getRateProviderRate(_assets[i]);\n // convert asset amount to ETH amount\n bptExpected += _amounts[i].mulTruncate(poolAssetRate);\n }\n\n uint256 bptRate = IRateProvider(platformAddress).getRate();\n // Convert ETH amount to BPT amount\n bptExpected = bptExpected.divPrecisely(bptRate);\n }\n\n function _lpDepositAll() internal virtual;\n\n function _lpWithdraw(uint256 numBPTTokens) internal virtual;\n\n function _lpWithdrawAll() internal virtual;\n\n /**\n * @notice Balancer returns assets and rateProviders for corresponding assets ordered\n * by numerical order.\n */\n function _getPoolAssets() internal view returns (IERC20[] memory assets) {\n // slither-disable-next-line unused-return\n (assets, , ) = balancerVault.getPoolTokens(balancerPoolId);\n }\n\n /**\n * @dev If an asset is rebasing the Balancer pools have a wrapped versions of assets\n * that the strategy supports. This function converts the pool(wrapped) asset\n * and corresponding amount to strategy asset.\n */\n function _toPoolAsset(address asset, uint256 amount)\n internal\n view\n returns (address poolAsset, uint256 poolAmount)\n {\n if (asset == stETH) {\n poolAsset = wstETH;\n if (amount > 0) {\n poolAmount = IWstETH(wstETH).getWstETHByStETH(amount);\n }\n } else if (asset == frxETH) {\n poolAsset = sfrxETH;\n if (amount > 0) {\n poolAmount = IERC4626(sfrxETH).convertToShares(amount);\n }\n } else {\n poolAsset = asset;\n poolAmount = amount;\n }\n }\n\n /**\n * @dev Converts a Vault collateral asset to a Balancer pool asset.\n * stETH becomes wstETH, frxETH becomes sfrxETH and everything else stays the same.\n * @param asset Address of the Vault collateral asset.\n * @return Address of the Balancer pool asset.\n */\n function _toPoolAsset(address asset) internal view returns (address) {\n if (asset == stETH) {\n return wstETH;\n } else if (asset == frxETH) {\n return sfrxETH;\n }\n return asset;\n }\n\n /**\n * @dev Converts rebasing asset to its wrapped counterpart.\n */\n function _wrapPoolAsset(address asset, uint256 amount)\n internal\n returns (address wrappedAsset, uint256 wrappedAmount)\n {\n if (asset == stETH) {\n wrappedAsset = wstETH;\n if (amount > 0) {\n wrappedAmount = IWstETH(wstETH).wrap(amount);\n }\n } else if (asset == frxETH) {\n wrappedAsset = sfrxETH;\n if (amount > 0) {\n wrappedAmount = IERC4626(sfrxETH).deposit(\n amount,\n address(this)\n );\n }\n } else {\n wrappedAsset = asset;\n wrappedAmount = amount;\n }\n }\n\n /**\n * @dev Converts wrapped asset to its rebasing counterpart.\n */\n function _unwrapPoolAsset(address asset, uint256 amount)\n internal\n returns (uint256 unwrappedAmount)\n {\n if (asset == stETH) {\n unwrappedAmount = IWstETH(wstETH).unwrap(amount);\n } else if (asset == frxETH) {\n unwrappedAmount = IERC4626(sfrxETH).withdraw(\n amount,\n address(this),\n address(this)\n );\n } else {\n unwrappedAmount = amount;\n }\n }\n\n /**\n * @dev If an asset is rebasing the Balancer pools have a wrapped versions of assets\n * that the strategy supports. This function converts the rebasing strategy asset\n * and corresponding amount to wrapped(pool) asset.\n */\n function _fromPoolAsset(address poolAsset, uint256 poolAmount)\n internal\n view\n returns (address asset, uint256 amount)\n {\n if (poolAsset == wstETH) {\n asset = stETH;\n if (poolAmount > 0) {\n amount = IWstETH(wstETH).getStETHByWstETH(poolAmount);\n }\n } else if (poolAsset == sfrxETH) {\n asset = frxETH;\n if (poolAmount > 0) {\n amount = IERC4626(sfrxETH).convertToAssets(poolAmount);\n }\n } else {\n asset = poolAsset;\n amount = poolAmount;\n }\n }\n\n function _fromPoolAsset(address poolAsset)\n internal\n view\n returns (address asset)\n {\n if (poolAsset == wstETH) {\n asset = stETH;\n } else if (poolAsset == sfrxETH) {\n asset = frxETH;\n } else {\n asset = poolAsset;\n }\n }\n\n /**\n * @notice Sets max withdrawal deviation that is considered when removing\n * liquidity from Balancer pools.\n * @param _maxWithdrawalDeviation Max withdrawal deviation denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxWithdrawalDeviation will be 1% (1e16) for production\n * usage. Vault value checker in combination with checkBalance will\n * catch any unexpected manipulation.\n */\n function setMaxWithdrawalDeviation(uint256 _maxWithdrawalDeviation)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(\n _maxWithdrawalDeviation <= 1e18,\n \"Withdrawal dev. out of bounds\"\n );\n emit MaxWithdrawalDeviationUpdated(\n maxWithdrawalDeviation,\n _maxWithdrawalDeviation\n );\n maxWithdrawalDeviation = _maxWithdrawalDeviation;\n }\n\n /**\n * @notice Sets max deposit deviation that is considered when adding\n * liquidity to Balancer pools.\n * @param _maxDepositDeviation Max deposit deviation denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxDepositDeviation will default to 1% (1e16)\n * for production usage. Vault value checker in combination with\n * checkBalance will catch any unexpected manipulation.\n */\n function setMaxDepositDeviation(uint256 _maxDepositDeviation)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(_maxDepositDeviation <= 1e18, \"Deposit dev. out of bounds\");\n emit MaxDepositDeviationUpdated(\n maxDepositDeviation,\n _maxDepositDeviation\n );\n maxDepositDeviation = _maxDepositDeviation;\n }\n\n function _approveBase() internal virtual {\n IERC20 pToken = IERC20(platformAddress);\n // Balancer vault for BPT token (required for removing liquidity)\n pToken.safeApprove(address(balancerVault), type(uint256).max);\n }\n\n function _getRateProviderRate(address _asset)\n internal\n view\n virtual\n returns (uint256);\n}\n" + }, + "contracts/strategies/balancer/BalancerMetaPoolStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Balancer MetaStablePool Strategy\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { AbstractAuraStrategy, AbstractBalancerStrategy } from \"./AbstractAuraStrategy.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\nimport { IRateProvider } from \"../../interfaces/balancer/IRateProvider.sol\";\nimport { IMetaStablePool } from \"../../interfaces/balancer/IMetaStablePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\ncontract BalancerMetaPoolStrategy is AbstractAuraStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(\n BaseStrategyConfig memory _stratConfig,\n BaseBalancerConfig memory _balancerConfig,\n address _auraRewardPoolAddress\n )\n InitializableAbstractStrategy(_stratConfig)\n AbstractBalancerStrategy(_balancerConfig)\n AbstractAuraStrategy(_auraRewardPoolAddress)\n {}\n\n /**\n * @notice There are no plans to configure BalancerMetaPool as a default\n * asset strategy. For that reason there is no need to support this\n * functionality.\n */\n function deposit(address, uint256)\n external\n override\n onlyVault\n nonReentrant\n {\n revert(\"Not supported\");\n }\n\n /**\n * @notice There are no plans to configure BalancerMetaPool as a default\n * asset strategy. For that reason there is no need to support this\n * functionality.\n */\n function deposit(address[] calldata, uint256[] calldata)\n external\n onlyVault\n nonReentrant\n {\n revert(\"Not supported\");\n }\n\n /**\n * @notice Deposits all supported assets in this strategy contract to the Balancer pool.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 assetsLength = assetsMapped.length;\n address[] memory strategyAssets = new address[](assetsLength);\n uint256[] memory strategyAmounts = new uint256[](assetsLength);\n\n // For each vault collateral asset\n for (uint256 i = 0; i < assetsLength; ++i) {\n strategyAssets[i] = assetsMapped[i];\n // Get the asset balance in this strategy contract\n strategyAmounts[i] = IERC20(strategyAssets[i]).balanceOf(\n address(this)\n );\n }\n _deposit(strategyAssets, strategyAmounts);\n }\n\n /*\n * _deposit doesn't require a read-only re-entrancy protection since during the deposit\n * the function enters the Balancer Vault Context. If this function were called as part of\n * the attacking contract (while intercepting execution flow upon receiving ETH) the read-only\n * protection of the Balancer Vault would be triggered. Since the attacking contract would\n * already be in the Balancer Vault context and wouldn't be able to enter it again.\n */\n function _deposit(\n address[] memory _strategyAssets,\n uint256[] memory _strategyAmounts\n ) internal {\n require(\n _strategyAssets.length == _strategyAmounts.length,\n \"Array length missmatch\"\n );\n\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n\n uint256[] memory strategyAssetAmountsToPoolAssetAmounts = new uint256[](\n _strategyAssets.length\n );\n address[] memory strategyAssetsToPoolAssets = new address[](\n _strategyAssets.length\n );\n\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n address strategyAsset = _strategyAssets[i];\n uint256 strategyAmount = _strategyAmounts[i];\n\n require(\n assetToPToken[strategyAsset] != address(0),\n \"Unsupported asset\"\n );\n strategyAssetsToPoolAssets[i] = _toPoolAsset(strategyAsset);\n\n if (strategyAmount > 0) {\n emit Deposit(strategyAsset, platformAddress, strategyAmount);\n\n // wrap rebasing assets like stETH and frxETH to wstETH and sfrxETH\n (, strategyAssetAmountsToPoolAssetAmounts[i]) = _wrapPoolAsset(\n strategyAsset,\n strategyAmount\n );\n }\n }\n\n uint256[] memory amountsIn = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n for (uint256 i = 0; i < tokens.length; ++i) {\n // Convert IERC20 type to address\n poolAssets[i] = address(tokens[i]);\n\n // For each of the mapped assets\n for (uint256 j = 0; j < strategyAssetsToPoolAssets.length; ++j) {\n // If the pool asset is the same as the mapped asset\n if (poolAssets[i] == strategyAssetsToPoolAssets[j]) {\n amountsIn[i] = strategyAssetAmountsToPoolAssetAmounts[j];\n }\n }\n }\n\n uint256 minBPT = _getBPTExpected(\n strategyAssetsToPoolAssets,\n strategyAssetAmountsToPoolAssetAmounts\n );\n uint256 minBPTwDeviation = minBPT.mulTruncate(\n 1e18 - maxDepositDeviation\n );\n\n /* EXACT_TOKENS_IN_FOR_BPT_OUT:\n * User sends precise quantities of tokens, and receives an\n * estimated but unknown (computed at run time) quantity of BPT.\n *\n * ['uint256', 'uint256[]', 'uint256']\n * [EXACT_TOKENS_IN_FOR_BPT_OUT, amountsIn, minimumBPT]\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,\n amountsIn,\n minBPTwDeviation\n );\n\n IBalancerVault.JoinPoolRequest memory request = IBalancerVault\n .JoinPoolRequest(poolAssets, amountsIn, userData, false);\n\n // Add the pool assets in this strategy to the balancer pool\n balancerVault.joinPool(\n balancerPoolId,\n address(this),\n address(this),\n request\n );\n\n // Deposit the Balancer Pool Tokens (BPT) into Aura\n _lpDepositAll();\n }\n\n /**\n * @notice Withdraw a Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAsset Address of the Vault collateral asset\n * @param _strategyAmount The amount of Vault collateral assets to withdraw\n */\n function withdraw(\n address _recipient,\n address _strategyAsset,\n uint256 _strategyAmount\n ) external override onlyVault nonReentrant {\n address[] memory strategyAssets = new address[](1);\n uint256[] memory strategyAmounts = new uint256[](1);\n strategyAssets[0] = _strategyAsset;\n strategyAmounts[0] = _strategyAmount;\n\n _withdraw(_recipient, strategyAssets, strategyAmounts);\n }\n\n /**\n * @notice Withdraw multiple Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAssets Addresses of the Vault collateral assets\n * @param _strategyAmounts The amounts of Vault collateral assets to withdraw\n */\n function withdraw(\n address _recipient,\n address[] calldata _strategyAssets,\n uint256[] calldata _strategyAmounts\n ) external onlyVault nonReentrant {\n _withdraw(_recipient, _strategyAssets, _strategyAmounts);\n }\n\n /**\n * @dev Withdraw multiple Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAssets Addresses of the Vault collateral assets\n * @param _strategyAmounts The amounts of Vault collateral assets to withdraw\n *\n * _withdrawal doesn't require a read-only re-entrancy protection since during the withdrawal\n * the function enters the Balancer Vault Context. If this function were called as part of\n * the attacking contract (while intercepting execution flow upon receiving ETH) the read-only\n * protection of the Balancer Vault would be triggered. Since the attacking contract would\n * already be in the Balancer Vault context and wouldn't be able to enter it again.\n */\n function _withdraw(\n address _recipient,\n address[] memory _strategyAssets,\n uint256[] memory _strategyAmounts\n ) internal {\n require(\n _strategyAssets.length == _strategyAmounts.length,\n \"Invalid input arrays\"\n );\n\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n require(\n assetToPToken[_strategyAssets[i]] != address(0),\n \"Unsupported asset\"\n );\n }\n\n // STEP 1 - Calculate the Balancer pool assets and amounts from the vault collateral assets\n\n // Get all the supported balancer pool assets\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n // Calculate the balancer pool assets and amounts to withdraw\n uint256[] memory poolAssetsAmountsOut = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n // Is the wrapped asset amount indexed by the assets array, not the order of the Balancer pool tokens\n // eg wstETH and sfrxETH amounts, not the stETH and frxETH amounts\n uint256[] memory strategyAssetsToPoolAssetsAmounts = new uint256[](\n _strategyAssets.length\n );\n\n // For each of the Balancer pool assets\n for (uint256 i = 0; i < tokens.length; ++i) {\n poolAssets[i] = address(tokens[i]);\n\n // Convert the Balancer pool asset back to a vault collateral asset\n address strategyAsset = _fromPoolAsset(poolAssets[i]);\n\n // for each of the vault assets\n for (uint256 j = 0; j < _strategyAssets.length; ++j) {\n // If the vault asset equals the vault asset mapped from the Balancer pool asset\n if (_strategyAssets[j] == strategyAsset) {\n (, poolAssetsAmountsOut[i]) = _toPoolAsset(\n strategyAsset,\n _strategyAmounts[j]\n );\n strategyAssetsToPoolAssetsAmounts[j] = poolAssetsAmountsOut[\n i\n ];\n\n /* Because of the potential Balancer rounding error mentioned below\n * the contract might receive 1-2 WEI smaller amount than required\n * in the withdraw user data encoding. If slightly lesser token amount\n * is received the strategy can not unwrap the pool asset as it is\n * smaller than expected.\n *\n * For that reason we `overshoot` the required tokens expected to\n * circumvent the error\n */\n if (poolAssetsAmountsOut[i] > 0) {\n poolAssetsAmountsOut[i] += 2;\n }\n }\n }\n }\n\n // STEP 2 - Calculate the max about of Balancer Pool Tokens (BPT) to withdraw\n\n // Estimate the required amount of Balancer Pool Tokens (BPT) for the assets\n uint256 maxBPTtoWithdraw = _getBPTExpected(\n poolAssets,\n /* all non 0 values are overshot by 2 WEI and with the expected mainnet\n * ~1% withdrawal deviation, the 2 WEI aren't important\n */\n poolAssetsAmountsOut\n );\n // Increase BPTs by the max allowed deviation\n // Any excess BPTs will be left in this strategy contract\n maxBPTtoWithdraw = maxBPTtoWithdraw.mulTruncate(\n 1e18 + maxWithdrawalDeviation\n );\n\n // STEP 3 - Withdraw the Balancer Pool Tokens (BPT) from Aura to this strategy contract\n\n // Withdraw BPT from Aura allowing for BPTs left in this strategy contract from previous withdrawals\n _lpWithdraw(\n maxBPTtoWithdraw - IERC20(platformAddress).balanceOf(address(this))\n );\n\n // STEP 4 - Withdraw the balancer pool assets from the pool\n\n /* Custom asset exit: BPT_IN_FOR_EXACT_TOKENS_OUT:\n * User sends an estimated but unknown (computed at run time) quantity of BPT,\n * and receives precise quantities of specified tokens.\n *\n * ['uint256', 'uint256[]', 'uint256']\n * [BPT_IN_FOR_EXACT_TOKENS_OUT, amountsOut, maxBPTAmountIn]\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.BPT_IN_FOR_EXACT_TOKENS_OUT,\n poolAssetsAmountsOut,\n maxBPTtoWithdraw\n );\n\n IBalancerVault.ExitPoolRequest memory request = IBalancerVault\n .ExitPoolRequest(\n poolAssets,\n /* We specify the exact amount of a tokens we are expecting in the encoded\n * userData, for that reason we don't need to specify the amountsOut here.\n *\n * Also Balancer has a rounding issue that can make a transaction fail:\n * https://github.com/balancer/balancer-v2-monorepo/issues/2541\n * which is an extra reason why this field is empty.\n */\n new uint256[](tokens.length),\n userData,\n false\n );\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n /* Payable keyword is required because of the IBalancerVault interface even though\n * this strategy shall never be receiving native ETH\n */\n payable(address(this)),\n request\n );\n\n // STEP 5 - Re-deposit any left over BPT tokens back into Aura\n /* When concluding how much of BPT we need to withdraw from Aura we overshoot by\n * roughly around 1% (initial mainnet setting of maxWithdrawalDeviation). After exiting\n * the pool strategy could have left over BPT tokens that are not earning boosted yield.\n * We re-deploy those back in.\n */\n _lpDepositAll();\n\n // STEP 6 - Unswap balancer pool assets to vault collateral assets and send to the vault.\n\n // For each of the specified assets\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n // Unwrap assets like wstETH and sfrxETH to rebasing assets stETH and frxETH\n if (strategyAssetsToPoolAssetsAmounts[i] > 0) {\n _unwrapPoolAsset(\n _strategyAssets[i],\n strategyAssetsToPoolAssetsAmounts[i]\n );\n }\n\n // Transfer the vault collateral assets to the recipient, which is typically the vault\n if (_strategyAmounts[i] > 0) {\n IERC20(_strategyAssets[i]).safeTransfer(\n _recipient,\n _strategyAmounts[i]\n );\n\n emit Withdrawal(\n _strategyAssets[i],\n platformAddress,\n _strategyAmounts[i]\n );\n }\n }\n }\n\n /**\n * @notice Withdraws all supported Vault collateral assets from the Balancer pool\n * and send to the OToken's Vault.\n *\n * Is only executable by the OToken's Vault or the Governor.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n // STEP 1 - Withdraw all Balancer Pool Tokens (BPT) from Aura to this strategy contract\n\n _lpWithdrawAll();\n // Get the BPTs withdrawn from Aura plus any that were already in this strategy contract\n uint256 BPTtoWithdraw = IERC20(platformAddress).balanceOf(\n address(this)\n );\n // Get the balancer pool assets and their total balances\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n uint256[] memory minAmountsOut = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n for (uint256 i = 0; i < tokens.length; ++i) {\n poolAssets[i] = address(tokens[i]);\n }\n\n // STEP 2 - Withdraw the Balancer pool assets from the pool\n /* Proportional exit: EXACT_BPT_IN_FOR_TOKENS_OUT:\n * User sends a precise quantity of BPT, and receives an estimated but unknown\n * (computed at run time) quantity of a single token\n *\n * ['uint256', 'uint256']\n * [EXACT_BPT_IN_FOR_TOKENS_OUT, bptAmountIn]\n *\n * It is ok to pass an empty minAmountsOut since tilting the pool in any direction\n * when doing a proportional exit can only be beneficial to the strategy. Since\n * it will receive more of the underlying tokens for the BPT traded in.\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.EXACT_BPT_IN_FOR_TOKENS_OUT,\n BPTtoWithdraw\n );\n\n IBalancerVault.ExitPoolRequest memory request = IBalancerVault\n .ExitPoolRequest(poolAssets, minAmountsOut, userData, false);\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n /* Payable keyword is required because of the IBalancerVault interface even though\n * this strategy shall never be receiving native ETH\n */\n payable(address(this)),\n request\n );\n\n // STEP 3 - Convert the balancer pool assets to the vault collateral assets and send to the vault\n // For each of the Balancer pool assets\n for (uint256 i = 0; i < tokens.length; ++i) {\n address poolAsset = address(tokens[i]);\n // Convert the balancer pool asset to the strategy asset\n address strategyAsset = _fromPoolAsset(poolAsset);\n // Get the balancer pool assets withdraw from the pool plus any that were already in this strategy contract\n uint256 poolAssetAmount = IERC20(poolAsset).balanceOf(\n address(this)\n );\n\n // Unwrap assets like wstETH and sfrxETH to rebasing assets stETH and frxETH\n uint256 unwrappedAmount = 0;\n if (poolAssetAmount > 0) {\n unwrappedAmount = _unwrapPoolAsset(\n strategyAsset,\n poolAssetAmount\n );\n }\n\n // Transfer the vault collateral assets to the vault\n if (unwrappedAmount > 0) {\n IERC20(strategyAsset).safeTransfer(\n vaultAddress,\n unwrappedAmount\n );\n emit Withdrawal(\n strategyAsset,\n platformAddress,\n unwrappedAmount\n );\n }\n }\n }\n\n /**\n * @notice Approves the Balancer Vault to transfer poolAsset counterparts\n * of all of the supported assets from this strategy. E.g. stETH is a supported\n * strategy and Balancer Vault gets unlimited approval to transfer wstETH.\n *\n * If Balancer pool uses a wrapped version of a supported asset then also approve\n * unlimited usage of an asset to the contract responsible for wrapping.\n *\n * Approve unlimited spending by Balancer Vault and Aura reward pool of the\n * pool BPT tokens.\n *\n * Is only executable by the Governor.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n _abstractSetPToken(assetsMapped[i], platformAddress);\n }\n _approveBase();\n }\n\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address) internal override {\n address poolAsset = _toPoolAsset(_asset);\n if (_asset == stETH) {\n // slither-disable-next-line unused-return\n IERC20(stETH).approve(wstETH, type(uint256).max);\n } else if (_asset == frxETH) {\n // slither-disable-next-line unused-return\n IERC20(frxETH).approve(sfrxETH, type(uint256).max);\n }\n _approveAsset(poolAsset);\n }\n\n /**\n * @dev Approves the Balancer Vault to transfer an asset from\n * this strategy. The assets could be a Vault collateral asset\n * like WETH or rETH; or a Balancer pool asset that wraps the vault asset\n * like wstETH or sfrxETH.\n */\n function _approveAsset(address _asset) internal {\n IERC20 asset = IERC20(_asset);\n // slither-disable-next-line unused-return\n asset.approve(address(balancerVault), type(uint256).max);\n }\n\n /**\n * @notice Returns the rate supplied by the Balancer configured rate\n * provider. Rate is used to normalize the token to common underlying\n * pool denominator. (ETH for ETH Liquid staking derivatives)\n *\n * @param _asset Address of the Balancer pool asset\n * @return rate of the corresponding asset\n */\n function _getRateProviderRate(address _asset)\n internal\n view\n override\n returns (uint256)\n {\n IMetaStablePool pool = IMetaStablePool(platformAddress);\n IRateProvider[] memory providers = pool.getRateProviders();\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n\n uint256 providersLength = providers.length;\n for (uint256 i = 0; i < providersLength; ++i) {\n // _assets and corresponding rate providers are all in the same order\n if (address(tokens[i]) == _asset) {\n // rate provider doesn't exist, defaults to 1e18\n if (address(providers[i]) == address(0)) {\n return 1e18;\n }\n return providers[i].getRate();\n }\n }\n\n // should never happen\n assert(false);\n }\n}\n" + }, + "contracts/strategies/balancer/VaultReentrancyLib.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity >=0.7.0 <0.9.0;\n\nimport \"../../utils/BalancerErrors.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\n\nlibrary VaultReentrancyLib {\n /**\n * @dev Ensure we are not in a Vault context when this function is called, by attempting a no-op internal\n * balance operation. If we are already in a Vault transaction (e.g., a swap, join, or exit), the Vault's\n * reentrancy protection will cause this function to revert.\n *\n * The exact function call doesn't really matter: we're just trying to trigger the Vault reentrancy check\n * (and not hurt anything in case it works). An empty operation array with no specific operation at all works\n * for that purpose, and is also the least expensive in terms of gas and bytecode size.\n *\n * Call this at the top of any function that can cause a state change in a pool and is either public itself,\n * or called by a public function *outside* a Vault operation (e.g., join, exit, or swap).\n *\n * If this is *not* called in functions that are vulnerable to the read-only reentrancy issue described\n * here (https://forum.balancer.fi/t/reentrancy-vulnerability-scope-expanded/4345), those functions are unsafe,\n * and subject to manipulation that may result in loss of funds.\n */\n function ensureNotInVaultContext(IBalancerVault vault) internal view {\n // Perform the following operation to trigger the Vault's reentrancy guard:\n //\n // IBalancerVault.UserBalanceOp[] memory noop = new IBalancerVault.UserBalanceOp[](0);\n // _vault.manageUserBalance(noop);\n //\n // However, use a static call so that it can be a view function (even though the function is non-view).\n // This allows the library to be used more widely, as some functions that need to be protected might be\n // view.\n //\n // This staticcall always reverts, but we need to make sure it doesn't fail due to a re-entrancy attack.\n // Staticcalls consume all gas forwarded to them on a revert caused by storage modification.\n // By default, almost the entire available gas is forwarded to the staticcall,\n // causing the entire call to revert with an 'out of gas' error.\n //\n // We set the gas limit to 10k for the staticcall to\n // avoid wasting gas when it reverts due to storage modification.\n // `manageUserBalance` is a non-reentrant function in the Vault, so calling it invokes `_enterNonReentrant`\n // in the `ReentrancyGuard` contract, reproduced here:\n //\n // function _enterNonReentrant() private {\n // // If the Vault is actually being reentered, it will revert in the first line, at the `_require` that\n // // checks the reentrancy flag, with \"BAL#400\" (corresponding to Errors.REENTRANCY) in the revertData.\n // // The full revertData will be: `abi.encodeWithSignature(\"Error(string)\", \"BAL#400\")`.\n // _require(_status != _ENTERED, Errors.REENTRANCY);\n //\n // // If the Vault is not being reentered, the check above will pass: but it will *still* revert,\n // // because the next line attempts to modify storage during a staticcall. However, this type of\n // // failure results in empty revertData.\n // _status = _ENTERED;\n // }\n //\n // So based on this analysis, there are only two possible revertData values: empty, or abi.encoded BAL#400.\n //\n // It is of course much more bytecode and gas efficient to check for zero-length revertData than to compare it\n // to the encoded REENTRANCY revertData.\n //\n // While it should be impossible for the call to fail in any other way (especially since it reverts before\n // `manageUserBalance` even gets called), any other error would generate non-zero revertData, so checking for\n // empty data guards against this case too.\n\n (, bytes memory revertData) = address(vault).staticcall{ gas: 10_000 }(\n abi.encodeWithSelector(vault.manageUserBalance.selector, 0)\n );\n\n _require(revertData.length == 0, Errors.REENTRANCY);\n }\n}\n" + }, + "contracts/strategies/BaseCurveAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for the Curve OETH/WETH pool\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { ICurveStableSwapNG } from \"../interfaces/ICurveStableSwapNG.sol\";\nimport { ICurveXChainLiquidityGauge } from \"../interfaces/ICurveXChainLiquidityGauge.sol\";\nimport { IChildLiquidityGaugeFactory } from \"../interfaces/IChildLiquidityGaugeFactory.sol\";\n\ncontract BaseCurveAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @dev a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n // New immutable variables that must be set in the constructor\n /**\n * @notice Address of the Wrapped ETH (WETH) contract.\n */\n IWETH9 public immutable weth;\n\n /**\n * @notice Address of the OETH token contract.\n */\n IERC20 public immutable oeth;\n\n /**\n * @notice Address of the LP (Liquidity Provider) token contract.\n */\n IERC20 public immutable lpToken;\n\n /**\n * @notice Address of the Curve StableSwap NG pool contract.\n */\n ICurveStableSwapNG public immutable curvePool;\n\n /**\n * @notice Address of the Curve X-Chain Liquidity Gauge contract.\n */\n ICurveXChainLiquidityGauge public immutable gauge;\n\n /**\n * @notice Address of the Child Liquidity Gauge Factory contract.\n */\n IChildLiquidityGaugeFactory public immutable gaugeFactory;\n\n // Ordered list of pool assets\n uint128 public immutable oethCoinIndex;\n uint128 public immutable wethCoinIndex;\n\n /**\n * @notice Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n uint256 public maxSlippage;\n\n event MaxSlippageUpdated(uint256 newMaxSlippage);\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balancesBefore = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffBefore = balancesBefore[wethCoinIndex].toInt256() -\n balancesBefore[oethCoinIndex].toInt256();\n\n _;\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balancesAfter = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffAfter = balancesAfter[wethCoinIndex].toInt256() -\n balancesAfter[oethCoinIndex].toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of ETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _oeth,\n address _weth,\n address _gauge,\n address _gaugeFactory,\n uint128 _oethCoinIndex,\n uint128 _wethCoinIndex\n ) InitializableAbstractStrategy(_baseConfig) {\n oethCoinIndex = _oethCoinIndex;\n wethCoinIndex = _wethCoinIndex;\n\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveStableSwapNG(_baseConfig.platformAddress);\n\n oeth = IERC20(_oeth);\n weth = IWETH9(_weth);\n gauge = ICurveXChainLiquidityGauge(_gauge);\n gaugeFactory = IChildLiquidityGaugeFactory(_gaugeFactory);\n\n _setGovernor(address(0));\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV\n * @param _maxSlippage Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV\n uint256 _maxSlippage\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n address[] memory _assets = new address[](1);\n _assets[0] = address(weth);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n _setMaxSlippage(_maxSlippage);\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit WETH into the Curve pool\n * @param _weth Address of Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to deposit.\n */\n function deposit(address _weth, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_weth, _amount);\n }\n\n function _deposit(address _weth, uint256 _wethAmount) internal {\n require(_wethAmount > 0, \"Must deposit something\");\n require(_weth == address(weth), \"Can only deposit WETH\");\n\n emit Deposit(_weth, address(lpToken), _wethAmount);\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 oethToAdd = uint256(\n _max(\n 0,\n balances[wethCoinIndex].toInt256() +\n _wethAmount.toInt256() -\n balances[oethCoinIndex].toInt256()\n )\n );\n\n /* Add so much OETH so that the pool ends up being balanced. And at minimum\n * add as much OETH as WETH and at maximum twice as much OETH.\n */\n oethToAdd = Math.max(oethToAdd, _wethAmount);\n oethToAdd = Math.min(oethToAdd, _wethAmount * 2);\n\n /* Mint OETH with a strategy that attempts to contribute to stability of OETH/WETH pool. Try\n * to mint so much OETH that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OETH minted will always be at least equal or greater\n * to WETH amount deployed. And never larger than twice the WETH amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(oethToAdd);\n\n emit Deposit(address(oeth), address(lpToken), oethToAdd);\n\n uint256[] memory _amounts = new uint256[](2);\n _amounts[wethCoinIndex] = _wethAmount;\n _amounts[oethCoinIndex] = oethToAdd;\n\n uint256 valueInLpTokens = (_wethAmount + oethToAdd).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Do the deposit to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(_amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool's LP tokens into the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n /**\n * @notice Deposit the strategy's entire balance of WETH into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = weth.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(weth), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _weth Address of the Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to withdraw.\n */\n function withdraw(\n address _recipient,\n address _weth,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_weth == address(weth), \"Can only withdraw WETH\");\n\n emit Withdrawal(_weth, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(_amount);\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough WETH on balanced removal\n */\n uint256[] memory _minWithdrawalAmounts = new uint256[](2);\n _minWithdrawalAmounts[wethCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OETH and any that was left in the strategy\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n\n // Transfer WETH to the recipient\n require(\n weth.transfer(_recipient, _amount),\n \"Transfer of WETH not successful\"\n );\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n function calcTokenToBurn(uint256 _wethAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much WETH\n * we want we can determine how much of OETH we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolWETHBalance = curvePool.balances(wethCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolWETHBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_wethAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = gauge.balanceOf(address(this));\n // Can not withdraw zero LP tokens from the gauge\n if (gaugeTokens == 0) return;\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[] memory minWithdrawAmounts = new uint256[](2);\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(\n lpToken.balanceOf(address(this)),\n minWithdrawAmounts\n );\n\n // Burn all OETH\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Get the strategy contract's WETH balance.\n // This includes all that was removed from the Curve pool and\n // any ether that was sitting in the strategy contract before the removal.\n uint256 ethBalance = weth.balanceOf(address(this));\n require(\n weth.transfer(vaultAddress, ethBalance),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethBalance);\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[] memory amounts = new uint256[](2);\n amounts[oethCoinIndex] = _oTokens;\n\n // Convert OETH to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool LP tokens to the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Deposit(address(oeth), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough ETH.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove OTokens from the Curve pool\n uint256 oethToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n oethCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /**\n * @notice One-sided remove of ETH from the Curve pool, convert to WETH\n * and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for ETH.\n * @dev Curve pool LP tokens is used rather than WETH assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of ETH. Curve's `calc_token_amount` functioun does not include fees.\n * A 3rd party libary can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * caclulate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Curve gauge and remove ETH from the Curve pool\n uint256 ethAmount = _withdrawAndRemoveFromPool(\n _lpTokens,\n wethCoinIndex\n );\n\n // Transfer WETH to the vault\n require(\n weth.transfer(vaultAddress, ethAmount),\n \"Transfer of WETH not successful\"\n );\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(weth), address(lpToken), ethAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the Convex pool and\n * do a one-sided remove of ETH or OETH from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the Convex pool.\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = ETH, 1 = OETH.\n * @return coinsRemoved The amount of ETH or OETH removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Curve gauge\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to ETH value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n // Apply slippage to ETH value\n uint256 minAmount = valueInEth.mulTruncate(uint256(1e18) - maxSlippage);\n\n // Remove just the ETH from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethbSupply = oeth.totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethbSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // CRV rewards flow.\n //---\n // CRV inflation:\n // Gauge receive CRV rewards from inflation.\n // Each checkpoint on the gauge send this CRV inflation to gauge factory.\n // This strategy should call mint on the gauge factory to collect the CRV rewards.\n // ---\n // Extra rewards:\n // Calling claim_rewards on the gauge will only claim extra rewards (outside of CRV).\n // ---\n\n // Mint CRV on Child Liquidity gauge factory\n gaugeFactory.mint(address(gauge));\n // Collect extra gauge rewards (outside of CRV)\n gauge.claim_rewards();\n\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _lpAmount) internal {\n // withdraw lp tokens from the gauge without claiming rewards\n gauge.withdraw(_lpAmount);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // WETH balance needed here for the balance check that happens from vault during depositing.\n balance = weth.balanceOf(address(this));\n uint256 lpTokens = gauge.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += (lpTokens * curvePool.get_virtual_price()) / 1e18;\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(weth);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Sets the maximum slippage allowed for any swap/liquidity operation\n * @param _maxSlippage Maximum slippage allowed, 1e18 = 100%.\n */\n function setMaxSlippage(uint256 _maxSlippage) external onlyGovernor {\n _setMaxSlippage(_maxSlippage);\n }\n\n function _setMaxSlippage(uint256 _maxSlippage) internal {\n require(_maxSlippage <= 5e16, \"Slippage must be less than 100%\");\n maxSlippage = _maxSlippage;\n emit MaxSlippageUpdated(_maxSlippage);\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OETH (required for adding liquidity)\n // slither-disable-next-line unused-return\n oeth.approve(platformAddress, type(uint256).max);\n\n // Approve Curve pool for WETH (required for adding liquidity)\n // slither-disable-next-line unused-return\n weth.approve(platformAddress, type(uint256).max);\n\n // Approve Curve gauge contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Curve gauge.\n // slither-disable-next-line unused-return\n lpToken.approve(address(gauge), type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/BridgedWOETHStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, SafeERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { AggregatorV3Interface } from \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\n\ncontract BridgedWOETHStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using StableMath for uint128;\n using SafeCast for uint256;\n using SafeERC20 for IERC20;\n\n event MaxPriceDiffBpsUpdated(uint128 oldValue, uint128 newValue);\n event WOETHPriceUpdated(uint128 oldValue, uint128 newValue);\n\n IWETH9 public immutable weth;\n IERC20 public immutable bridgedWOETH;\n IERC20 public immutable oethb;\n\n uint256 public constant MAX_PRICE_STALENESS = 2 days;\n\n uint128 public lastOraclePrice;\n uint128 public maxPriceDiffBps;\n\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _weth,\n address _bridgedWOETH,\n address _oethb\n ) InitializableAbstractStrategy(_stratConfig) {\n weth = IWETH9(_weth);\n bridgedWOETH = IERC20(_bridgedWOETH);\n oethb = IERC20(_oethb);\n }\n\n function initialize(uint128 _maxPriceDiffBps)\n external\n onlyGovernor\n initializer\n {\n InitializableAbstractStrategy._initialize(\n new address[](0), // No reward tokens\n new address[](0), // No assets\n new address[](0) // No pTokens\n );\n\n _setMaxPriceDiffBps(_maxPriceDiffBps);\n }\n\n /**\n * @dev Sets the max price diff bps for the wOETH value appreciation\n * @param _maxPriceDiffBps Bps value, 10k == 100%\n */\n function setMaxPriceDiffBps(uint128 _maxPriceDiffBps)\n external\n onlyGovernor\n {\n _setMaxPriceDiffBps(_maxPriceDiffBps);\n }\n\n /**\n * @dev Sets the max price diff bps for the wOETH value appreciation\n * @param _maxPriceDiffBps Bps value, 10k == 100%\n */\n function _setMaxPriceDiffBps(uint128 _maxPriceDiffBps) internal {\n require(\n _maxPriceDiffBps > 0 && _maxPriceDiffBps <= 10000,\n \"Invalid bps value\"\n );\n\n emit MaxPriceDiffBpsUpdated(maxPriceDiffBps, _maxPriceDiffBps);\n\n maxPriceDiffBps = _maxPriceDiffBps;\n }\n\n /**\n * @dev Wrapper for _updateWOETHOraclePrice with nonReentrant flag\n * @return The latest price of wOETH from Oracle\n */\n function updateWOETHOraclePrice() external nonReentrant returns (uint256) {\n return _updateWOETHOraclePrice();\n }\n\n /**\n * @dev Finds the value of bridged wOETH from the Oracle.\n * Ensures that it's within the bounds and reasonable.\n * And stores it.\n *\n * NOTE: Intentionally not caching `Vault.priceProvider` here,\n * since doing so would mean that we also have to update this\n * strategy every time there's a change in oracle router.\n * Besides on L2, the gas is considerably cheaper than mainnet.\n *\n * @return Latest price from oracle\n */\n function _updateWOETHOraclePrice() internal returns (uint256) {\n // WETH price per unit of bridged wOETH\n uint256 oraclePrice = IOracle(IVault(vaultAddress).priceProvider())\n .price(address(bridgedWOETH));\n\n // 1 wOETH > 1 WETH, always\n require(oraclePrice > 1 ether, \"Invalid wOETH value\");\n\n uint128 oraclePrice128 = oraclePrice.toUint128();\n\n // Do some checks\n if (lastOraclePrice > 0) {\n // Make sure the value only goes up\n require(oraclePrice128 >= lastOraclePrice, \"Negative wOETH yield\");\n\n // lastOraclePrice * (1 + maxPriceDiffBps)\n uint256 maxPrice = (lastOraclePrice * (1e4 + maxPriceDiffBps)) /\n 1e4;\n\n // And that it's within the bounds.\n require(oraclePrice128 <= maxPrice, \"Price diff beyond threshold\");\n }\n\n emit WOETHPriceUpdated(lastOraclePrice, oraclePrice128);\n\n // Store the price\n lastOraclePrice = oraclePrice128;\n\n return oraclePrice;\n }\n\n /**\n * @dev Computes & returns the value of given wOETH in WETH\n * @param woethAmount Amount of wOETH\n * @return Value of wOETH in WETH (using the last stored oracle price)\n */\n function getBridgedWOETHValue(uint256 woethAmount)\n public\n view\n returns (uint256)\n {\n return (woethAmount * lastOraclePrice) / 1 ether;\n }\n\n /**\n * @dev Takes in bridged wOETH and mints & returns\n * equivalent amount of OETHb.\n * @param woethAmount Amount of bridged wOETH to transfer in\n */\n function depositBridgedWOETH(uint256 woethAmount)\n external\n onlyGovernorOrStrategist\n nonReentrant\n {\n // Update wOETH price\n uint256 oraclePrice = _updateWOETHOraclePrice();\n\n // Figure out how much they are worth\n uint256 oethToMint = (woethAmount * oraclePrice) / 1 ether;\n\n require(oethToMint > 0, \"Invalid deposit amount\");\n\n // There's no pToken, however, it just uses WOETH address in the event\n emit Deposit(address(weth), address(bridgedWOETH), oethToMint);\n\n // Mint OETHb tokens and transfer it to the caller\n IVault(vaultAddress).mintForStrategy(oethToMint);\n\n // Transfer out minted OETHb\n // slither-disable-next-line unchecked-transfer unused-return\n oethb.transfer(msg.sender, oethToMint);\n\n // Transfer in all bridged wOETH tokens\n // slither-disable-next-line unchecked-transfer unused-return\n bridgedWOETH.transferFrom(msg.sender, address(this), woethAmount);\n }\n\n /**\n * @dev Takes in OETHb and burns it and returns\n * equivalent amount of bridged wOETH.\n * @param oethToBurn Amount of OETHb to burn\n */\n function withdrawBridgedWOETH(uint256 oethToBurn)\n external\n onlyGovernorOrStrategist\n nonReentrant\n {\n // Update wOETH price\n uint256 oraclePrice = _updateWOETHOraclePrice();\n\n // Figure out how much they are worth\n uint256 woethAmount = (oethToBurn * 1 ether) / oraclePrice;\n\n require(woethAmount > 0, \"Invalid withdraw amount\");\n\n // There's no pToken, however, it just uses WOETH address in the event\n emit Withdrawal(address(weth), address(bridgedWOETH), oethToBurn);\n\n // Transfer WOETH back\n // slither-disable-next-line unchecked-transfer unused-return\n bridgedWOETH.transfer(msg.sender, woethAmount);\n\n // Transfer in OETHb\n // slither-disable-next-line unchecked-transfer unused-return\n oethb.transferFrom(msg.sender, address(this), oethToBurn);\n\n // Burn OETHb\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n }\n\n /**\n * @notice Returns the amount of backing WETH the strategy holds\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // Figure out how much wOETH is worth at the time.\n // Always uses the last stored oracle price.\n // Call updateWOETHOraclePrice manually to pull in latest yields.\n\n // NOTE: If the contract has been deployed but the call to\n // `updateWOETHOraclePrice()` has never been made, then this\n // will return zero. It should be fine because the strategy\n // should update the price whenever a deposit/withdraw happens.\n\n // If `updateWOETHOraclePrice()` hasn't been called in a while,\n // the strategy will underreport its holdings but never overreport it.\n\n balance =\n (bridgedWOETH.balanceOf(address(this)) * lastOraclePrice) /\n 1 ether;\n }\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n // Strategist deposits bridged wOETH but the contract only\n // reports the balance in WETH. As far as Vault is concerned,\n // it isn't aware of bridged wOETH token\n return _asset == address(weth);\n }\n\n /***************************************\n Overridden methods\n ****************************************/\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function transferToken(address _asset, uint256 _amount)\n public\n override\n onlyGovernor\n {\n require(\n _asset != address(bridgedWOETH) && _asset != address(weth),\n \"Cannot transfer supported asset\"\n );\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice deposit() function not used for this strategy\n */\n function deposit(address, uint256)\n external\n override\n onlyVault\n nonReentrant\n {\n // Use depositBridgedWOETH() instead\n require(false, \"Deposit disabled\");\n }\n\n /**\n * @notice depositAll() function not used for this strategy\n */\n function depositAll() external override onlyVault nonReentrant {\n // Use depositBridgedWOETH() instead\n require(false, \"Deposit disabled\");\n }\n\n /**\n * @notice withdraw() function not used for this strategy\n */\n function withdraw(\n // solhint-disable-next-line no-unused-vars\n address _recipient,\n // solhint-disable-next-line no-unused-vars\n address _asset,\n // solhint-disable-next-line no-unused-vars\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(false, \"Withdrawal disabled\");\n }\n\n /**\n * @notice withdrawAll() function not used for this strategy\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n // Withdrawal disabled\n }\n\n function _abstractSetPToken(address, address) internal override {\n revert(\"No pTokens are used\");\n }\n\n function safeApproveAllTokens() external override {}\n\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function removePToken(uint256) external override {\n revert(\"No pTokens are used\");\n }\n\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function collectRewardTokens() external override {}\n}\n" + }, + "contracts/strategies/CompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Compound Strategy\n * @notice Investment strategy for Compound like lending platforms. eg Compound and Flux\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICERC20 } from \"./ICompound.sol\";\nimport { AbstractCompoundStrategy, InitializableAbstractStrategy } from \"./AbstractCompoundStrategy.sol\";\nimport { IComptroller } from \"../interfaces/IComptroller.sol\";\nimport { IERC20 } from \"../utils/InitializableAbstractStrategy.sol\";\n\ncontract CompoundStrategy is AbstractCompoundStrategy {\n using SafeERC20 for IERC20;\n event SkippedWithdrawal(address asset, uint256 amount);\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @notice initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @notice Collect accumulated COMP and send to Harvester.\n */\n function collectRewardTokens()\n external\n virtual\n override\n onlyHarvester\n nonReentrant\n {\n // Claim COMP from Comptroller\n ICERC20 cToken = _getCTokenFor(assetsMapped[0]);\n IComptroller comptroller = IComptroller(cToken.comptroller());\n // Only collect from active cTokens, saves gas\n address[] memory ctokensToCollect = new address[](assetsMapped.length);\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n ctokensToCollect[i] = address(_getCTokenFor(assetsMapped[i]));\n }\n // Claim only for this strategy\n address[] memory claimers = new address[](1);\n claimers[0] = address(this);\n // Claim COMP from Comptroller. Only collect for supply, saves gas\n comptroller.claimComp(claimers, ctokensToCollect, false, true);\n // Transfer COMP to Harvester\n IERC20 rewardToken = IERC20(rewardTokenAddresses[0]);\n uint256 balance = rewardToken.balanceOf(address(this));\n emit RewardTokenCollected(\n harvesterAddress,\n rewardTokenAddresses[0],\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n\n /**\n * @notice Deposit asset into the underlying platform\n * @param _asset Address of asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit an asset into the underlying platform\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n ICERC20 cToken = _getCTokenFor(_asset);\n emit Deposit(_asset, address(cToken), _amount);\n require(cToken.mint(_amount) == 0, \"cToken mint failed\");\n }\n\n /**\n * @notice Deposit the entire balance of any supported asset in the strategy into the underlying platform\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n uint256 assetBalance = asset.balanceOf(address(this));\n if (assetBalance > 0) {\n _deposit(address(asset), assetBalance);\n }\n }\n }\n\n /**\n * @notice Withdraw an asset from the underlying platform\n * @param _recipient Address to receive withdrawn assets\n * @param _asset Address of the asset to withdraw\n * @param _amount Amount of assets to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n ICERC20 cToken = _getCTokenFor(_asset);\n // If redeeming 0 cTokens, just skip, else COMP will revert\n uint256 cTokensToRedeem = _convertUnderlyingToCToken(cToken, _amount);\n if (cTokensToRedeem == 0) {\n emit SkippedWithdrawal(_asset, _amount);\n return;\n }\n\n emit Withdrawal(_asset, address(cToken), _amount);\n require(cToken.redeemUnderlying(_amount) == 0, \"Redeem failed\");\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / cTokens\n * We need to approve the cToken and give it permission to spend the asset\n * @param _asset Address of the asset to approve. eg DAI\n * @param _pToken The pToken for the approval. eg cDAI or fDAI\n */\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n // Safe approval\n IERC20(_asset).safeApprove(_pToken, 0);\n IERC20(_asset).safeApprove(_pToken, type(uint256).max);\n }\n\n /**\n * @notice Remove all supported assets from the underlying platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n // Redeem entire balance of cToken\n ICERC20 cToken = _getCTokenFor(address(asset));\n uint256 cTokenBalance = cToken.balanceOf(address(this));\n if (cTokenBalance > 0) {\n require(cToken.redeem(cTokenBalance) == 0, \"Redeem failed\");\n uint256 assetBalance = asset.balanceOf(address(this));\n // Transfer entire balance to Vault\n asset.safeTransfer(vaultAddress, assetBalance);\n\n emit Withdrawal(address(asset), address(cToken), assetBalance);\n }\n }\n }\n\n /**\n * @notice Get the total asset value held in the underlying platform\n * This includes any interest that was generated since depositing.\n * The exchange rate between the cToken and asset gradually increases,\n * causing the cToken to be worth more corresponding asset.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n // Balance is always with token cToken decimals\n ICERC20 cToken = _getCTokenFor(_asset);\n balance = _checkBalance(cToken);\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * underlying = (cTokenAmt * exchangeRate) / 1e18\n * @param _cToken cToken for which to check balance\n * @return balance Total value of the asset in the platform\n */\n function _checkBalance(ICERC20 _cToken)\n internal\n view\n returns (uint256 balance)\n {\n // e.g. 50e8*205316390724364402565641705 / 1e18 = 1.0265..e18\n balance =\n (_cToken.balanceOf(address(this)) * _cToken.exchangeRateStored()) /\n 1e18;\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding cToken,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens() external override {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n address cToken = assetToPToken[address(asset)];\n // Safe approval\n asset.safeApprove(cToken, 0);\n asset.safeApprove(cToken, type(uint256).max);\n }\n }\n}\n" + }, + "contracts/strategies/ConvexEthMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Convex Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for the Curve OETH/ETH pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { ICurveETHPoolV1 } from \"./ICurveETHPoolV1.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\n\ncontract ConvexEthMetaStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 public constant MAX_SLIPPAGE = 1e16; // 1%, same as the Curve UI\n address public constant ETH_ADDRESS =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n // The following slots have been deprecated with immutable variables\n // slither-disable-next-line constable-states\n address private _deprecated_cvxDepositorAddress;\n // slither-disable-next-line constable-states\n address private _deprecated_cvxRewardStaker;\n // slither-disable-next-line constable-states\n uint256 private _deprecated_cvxDepositorPTokenId;\n // slither-disable-next-line constable-states\n address private _deprecated_curvePool;\n // slither-disable-next-line constable-states\n address private _deprecated_lpToken;\n // slither-disable-next-line constable-states\n address private _deprecated_oeth;\n // slither-disable-next-line constable-states\n address private _deprecated_weth;\n\n // Ordered list of pool assets\n // slither-disable-next-line constable-states\n uint128 private _deprecated_oethCoinIndex;\n // slither-disable-next-line constable-states\n uint128 private _deprecated_ethCoinIndex;\n\n // New immutable variables that must be set in the constructor\n address public immutable cvxDepositorAddress;\n IRewardStaking public immutable cvxRewardStaker;\n uint256 public immutable cvxDepositorPTokenId;\n ICurveETHPoolV1 public immutable curvePool;\n IERC20 public immutable lpToken;\n IERC20 public immutable oeth;\n IWETH9 public immutable weth;\n\n // Ordered list of pool assets\n uint128 public constant oethCoinIndex = 1;\n uint128 public constant ethCoinIndex = 0;\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier only works on functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balancesBefore = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffBefore = int256(balancesBefore[ethCoinIndex]) -\n int256(balancesBefore[oethCoinIndex]);\n\n _;\n\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balancesAfter = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffAfter = int256(balancesAfter[ethCoinIndex]) -\n int256(balancesAfter[oethCoinIndex]);\n\n if (diffBefore <= 0) {\n // If the pool was originally imbalanced in favor of OETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n }\n if (diffBefore >= 0) {\n // If the pool was originally imbalanced in favor of ETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n // Used to circumvent the stack too deep issue\n struct ConvexEthMetaConfig {\n address cvxDepositorAddress; //Address of the Convex depositor(AKA booster) for this pool\n address cvxRewardStakerAddress; //Address of the CVX rewards staker\n uint256 cvxDepositorPTokenId; //Pid of the pool referred to by Depositor and staker\n address oethAddress; //Address of OETH token\n address wethAddress; //Address of WETH\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n ConvexEthMetaConfig memory _convexConfig\n ) InitializableAbstractStrategy(_baseConfig) {\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveETHPoolV1(_baseConfig.platformAddress);\n\n cvxDepositorAddress = _convexConfig.cvxDepositorAddress;\n cvxRewardStaker = IRewardStaking(_convexConfig.cvxRewardStakerAddress);\n cvxDepositorPTokenId = _convexConfig.cvxDepositorPTokenId;\n oeth = IERC20(_convexConfig.oethAddress);\n weth = IWETH9(_convexConfig.wethAddress);\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. eg WETH\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets // WETH\n ) external onlyGovernor initializer {\n require(_assets.length == 1, \"Must have exactly one asset\");\n require(_assets[0] == address(weth), \"Asset not WETH\");\n\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit WETH into the Curve pool\n * @param _weth Address of Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to deposit.\n */\n function deposit(address _weth, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_weth, _amount);\n }\n\n function _deposit(address _weth, uint256 _wethAmount) internal {\n require(_wethAmount > 0, \"Must deposit something\");\n require(_weth == address(weth), \"Can only deposit WETH\");\n weth.withdraw(_wethAmount);\n\n emit Deposit(_weth, address(lpToken), _wethAmount);\n\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 oethToAdd = uint256(\n _max(\n 0,\n int256(balances[ethCoinIndex]) +\n int256(_wethAmount) -\n int256(balances[oethCoinIndex])\n )\n );\n\n /* Add so much OETH so that the pool ends up being balanced. And at minimum\n * add as much OETH as WETH and at maximum twice as much OETH.\n */\n oethToAdd = Math.max(oethToAdd, _wethAmount);\n oethToAdd = Math.min(oethToAdd, _wethAmount * 2);\n\n /* Mint OETH with a strategy that attempts to contribute to stability of OETH/WETH pool. Try\n * to mint so much OETH that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OETH minted will always be at least equal or greater\n * to WETH amount deployed. And never larger than twice the WETH amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(oethToAdd);\n\n emit Deposit(address(oeth), address(lpToken), oethToAdd);\n\n uint256[2] memory _amounts;\n _amounts[ethCoinIndex] = _wethAmount;\n _amounts[oethCoinIndex] = oethToAdd;\n\n uint256 valueInLpTokens = (_wethAmount + oethToAdd).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Do the deposit to the Curve pool\n // slither-disable-next-line arbitrary-send\n uint256 lpDeposited = curvePool.add_liquidity{ value: _wethAmount }(\n _amounts,\n minMintAmount\n );\n\n // Deposit the Curve pool's LP tokens into the Convex rewards pool\n require(\n IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n lpDeposited,\n true // Deposit with staking\n ),\n \"Depositing LP to Convex not successful\"\n );\n }\n\n /**\n * @notice Deposit the strategy's entire balance of WETH into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = weth.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(weth), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _weth Address of the Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to withdraw.\n */\n function withdraw(\n address _recipient,\n address _weth,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Invalid amount\");\n require(_weth == address(weth), \"Can only withdraw WETH\");\n\n emit Withdrawal(_weth, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(_amount);\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough WETH on balanced removal\n */\n uint256[2] memory _minWithdrawalAmounts = [uint256(0), uint256(0)];\n _minWithdrawalAmounts[ethCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OETH and any that was left in the strategy\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n\n // Transfer WETH to the recipient\n weth.deposit{ value: _amount }();\n require(\n weth.transfer(_recipient, _amount),\n \"Transfer of WETH not successful\"\n );\n }\n\n function calcTokenToBurn(uint256 _wethAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much WETH\n * we want we can determine how much of OETH we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolWETHBalance = curvePool.balances(ethCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolWETHBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_wethAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = cvxRewardStaker.balanceOf(address(this));\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[2] memory minWithdrawAmounts = [uint256(0), uint256(0)];\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(\n lpToken.balanceOf(address(this)),\n minWithdrawAmounts\n );\n\n // Burn all OETH\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Get the strategy contract's ether balance.\n // This includes all that was removed from the Curve pool and\n // any ether that was sitting in the strategy contract before the removal.\n uint256 ethBalance = address(this).balance;\n // Convert all the strategy contract's ether to WETH and transfer to the vault.\n weth.deposit{ value: ethBalance }();\n require(\n weth.transfer(vaultAddress, ethBalance),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethBalance);\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[2] memory amounts = [uint256(0), uint256(0)];\n amounts[oethCoinIndex] = _oTokens;\n\n // Convert OETH to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n\n // Deposit the Curve pool LP tokens to the Convex rewards pool\n require(\n IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n lpDeposited,\n true // Deposit with staking\n ),\n \"Failed to Deposit LP to Convex\"\n );\n\n emit Deposit(address(oeth), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough ETH.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove OTokens from the Curve pool\n uint256 oethToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n oethCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /**\n * @notice One-sided remove of ETH from the Curve pool, convert to WETH\n * and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for ETH.\n * @dev Curve pool LP tokens is used rather than WETH assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of ETH. Curve's `calc_token_amount` functioun does not include fees.\n * A 3rd party libary can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * caclulate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove ETH from the Curve pool\n uint256 ethAmount = _withdrawAndRemoveFromPool(_lpTokens, ethCoinIndex);\n\n // Convert ETH to WETH and transfer to the vault\n weth.deposit{ value: ethAmount }();\n require(\n weth.transfer(vaultAddress, ethAmount),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the Convex pool and\n * do a one-sided remove of ETH or OETH from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the Convex pool.\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = ETH, 1 = OETH.\n * @return coinsRemoved The amount of ETH or OETH removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Convex pool\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to ETH value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n // Apply slippage to ETH value\n uint256 minAmount = valueInEth.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Remove just the ETH from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV and CVX rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n cvxRewardStaker.getReward();\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _wethAmount) internal {\n // withdraw and unwrap with claim takes back the lpTokens\n // and also collects the rewards for deposit\n cvxRewardStaker.withdrawAndUnwrap(_wethAmount, true);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // Eth balance needed here for the balance check that happens from vault during depositing.\n balance = address(this).balance;\n uint256 lpTokens = cvxRewardStaker.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += (lpTokens * curvePool.get_virtual_price()) / 1e18;\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(weth);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @notice Accept unwrapped WETH\n */\n receive() external payable {}\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OETH (required for adding liquidity)\n // No approval is needed for ETH\n // slither-disable-next-line unused-return\n oeth.approve(platformAddress, type(uint256).max);\n\n // Approve Convex deposit contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Convex rewards pool\n // slither-disable-next-line unused-return\n lpToken.approve(cvxDepositorAddress, type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/ConvexGeneralizedMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { AbstractConvexMetaStrategy } from \"./AbstractConvexMetaStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract ConvexGeneralizedMetaStrategy is AbstractConvexMetaStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /* Take 3pool LP and deposit it to metapool. Take the LP from metapool\n * and deposit them to Convex.\n */\n function _lpDepositAll() internal override {\n IERC20 threePoolLp = IERC20(pTokenAddress);\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 threePoolLpBalance = threePoolLp.balanceOf(address(this));\n uint256 curve3PoolVirtualPrice = curvePool.get_virtual_price();\n uint256 threePoolLpDollarValue = threePoolLpBalance.mulTruncate(\n curve3PoolVirtualPrice\n );\n\n uint256[2] memory _amounts = [0, threePoolLpBalance];\n\n uint256 metapoolVirtualPrice = metapool.get_virtual_price();\n /**\n * First convert all the deposited tokens to dollar values,\n * then divide by virtual price to convert to metapool LP tokens\n * and apply the max slippage\n */\n uint256 minReceived = threePoolLpDollarValue\n .divPrecisely(metapoolVirtualPrice)\n .mulTruncate(uint256(1e18) - MAX_SLIPPAGE);\n\n uint256 metapoolLp = metapool.add_liquidity(_amounts, minReceived);\n\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n metapoolLp,\n true // Deposit with staking\n );\n\n require(success, \"Failed to deposit to Convex\");\n }\n\n /**\n * Withdraw the specified amount of tokens from the gauge. And use all the resulting tokens\n * to remove liquidity from metapool\n * @param num3CrvTokens Number of Convex 3pool LP tokens to withdraw from metapool\n */\n function _lpWithdraw(uint256 num3CrvTokens) internal override {\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n uint256 requiredMetapoolLpTokens = _calcCurveMetaTokenAmount(\n crvCoinIndex,\n num3CrvTokens\n );\n\n require(\n requiredMetapoolLpTokens <= gaugeTokens,\n string(\n bytes.concat(\n bytes(\"Attempting to withdraw \"),\n bytes(Strings.toString(requiredMetapoolLpTokens)),\n bytes(\", metapoolLP but only \"),\n bytes(Strings.toString(gaugeTokens)),\n bytes(\" available.\")\n )\n )\n );\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards for deposit\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n requiredMetapoolLpTokens,\n true\n );\n\n if (requiredMetapoolLpTokens > 0) {\n // slither-disable-next-line unused-return\n metapool.remove_liquidity_one_coin(\n requiredMetapoolLpTokens,\n int128(crvCoinIndex),\n num3CrvTokens\n );\n }\n }\n\n function _lpWithdrawAll() internal override {\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n gaugeTokens,\n true\n );\n\n if (gaugeTokens > 0) {\n uint256 burnDollarAmount = gaugeTokens.mulTruncate(\n metapool.get_virtual_price()\n );\n uint256 curve3PoolExpected = burnDollarAmount.divPrecisely(\n ICurvePool(platformAddress).get_virtual_price()\n );\n\n // Always withdraw all of the available metapool LP tokens (similar to how we always deposit all)\n // slither-disable-next-line unused-return\n metapool.remove_liquidity_one_coin(\n gaugeTokens,\n int128(crvCoinIndex),\n curve3PoolExpected -\n curve3PoolExpected.mulTruncate(maxWithdrawalSlippage)\n );\n }\n }\n}\n" + }, + "contracts/strategies/ConvexOUSDMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { AbstractConvexMetaStrategy } from \"./AbstractConvexMetaStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract ConvexOUSDMetaStrategy is AbstractConvexMetaStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /* Take 3pool LP and mint the corresponding amount of ousd. Deposit and stake that to\n * ousd Curve Metapool. Take the LP from metapool and deposit them to Convex.\n */\n function _lpDepositAll() internal override {\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 threePoolLpBalance = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n uint256 curve3PoolVirtualPrice = curvePool.get_virtual_price();\n uint256 threePoolLpDollarValue = threePoolLpBalance.mulTruncate(\n curve3PoolVirtualPrice\n );\n\n // safe to cast since min value is at least 0\n uint256 ousdToAdd = uint256(\n _max(\n 0,\n int256(\n metapool.balances(crvCoinIndex).mulTruncate(\n curve3PoolVirtualPrice\n )\n ) -\n int256(metapool.balances(mainCoinIndex)) +\n int256(threePoolLpDollarValue)\n )\n );\n\n /* Add so much OUSD so that the pool ends up being balanced. And at minimum\n * add twice as much OUSD as 3poolLP and at maximum at twice as\n * much OUSD.\n */\n ousdToAdd = Math.max(ousdToAdd, threePoolLpDollarValue);\n ousdToAdd = Math.min(ousdToAdd, threePoolLpDollarValue * 2);\n\n /* Mint OUSD with a strategy that attempts to contribute to stability of OUSD metapool. Try\n * to mint so much OUSD that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OUSD minted will always be at least equal or greater\n * to stablecoin(DAI, USDC, USDT) amount of 3CRVLP deployed. And never larger than twice the\n * stablecoin amount of 3CRVLP deployed even if it would have a further beneficial effect\n * on pool stability.\n */\n if (ousdToAdd > 0) {\n IVault(vaultAddress).mintForStrategy(ousdToAdd);\n }\n\n uint256[2] memory _amounts = [ousdToAdd, threePoolLpBalance];\n\n uint256 metapoolVirtualPrice = metapool.get_virtual_price();\n /**\n * First convert all the deposited tokens to dollar values,\n * then divide by virtual price to convert to metapool LP tokens\n * and apply the max slippage\n */\n uint256 minReceived = (ousdToAdd + threePoolLpDollarValue)\n .divPrecisely(metapoolVirtualPrice)\n .mulTruncate(uint256(1e18) - MAX_SLIPPAGE);\n\n uint256 metapoolLp = metapool.add_liquidity(_amounts, minReceived);\n\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n metapoolLp,\n true // Deposit with staking\n );\n\n require(success, \"Failed to deposit to Convex\");\n }\n\n /**\n * Withdraw the specified amount of tokens from the gauge. And use all the resulting tokens\n * to remove liquidity from metapool\n * @param num3CrvTokens Number of 3CRV tokens to withdraw from metapool\n */\n function _lpWithdraw(uint256 num3CrvTokens) internal override {\n ICurvePool curvePool = ICurvePool(platformAddress);\n /* The rate between coins in the metapool determines the rate at which metapool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much 3crvLp\n * we want we can determine how much of OUSD we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 crvPoolBalance = metapool.balances(crvCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * metapool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * metapoolLPToken.totalSupply()) / crvPoolBalance;\n // simplifying below to: `uint256 diff = (num3CrvTokens - 1) * k` causes loss of precision\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = crvPoolBalance * k -\n (crvPoolBalance - num3CrvTokens - 1) * k;\n uint256 lpToBurn = diff / 1e36;\n\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n require(\n lpToBurn <= gaugeTokens,\n string(\n bytes.concat(\n bytes(\"Attempting to withdraw \"),\n bytes(Strings.toString(lpToBurn)),\n bytes(\", metapoolLP but only \"),\n bytes(Strings.toString(gaugeTokens)),\n bytes(\" available.\")\n )\n )\n );\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards for deposit\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n lpToBurn,\n true\n );\n\n // calculate the min amount of OUSD expected for the specified amount of LP tokens\n uint256 minOUSDAmount = lpToBurn.mulTruncate(\n metapool.get_virtual_price()\n ) -\n num3CrvTokens.mulTruncate(curvePool.get_virtual_price()) -\n 1;\n\n // withdraw the liquidity from metapool\n uint256[2] memory _removedAmounts = metapool.remove_liquidity(\n lpToBurn,\n [minOUSDAmount, num3CrvTokens]\n );\n\n IVault(vaultAddress).burnForStrategy(_removedAmounts[mainCoinIndex]);\n }\n\n function _lpWithdrawAll() internal override {\n IERC20 metapoolErc20 = IERC20(address(metapool));\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n gaugeTokens,\n true\n );\n\n uint256[2] memory _minAmounts = [uint256(0), uint256(0)];\n uint256[2] memory _removedAmounts = metapool.remove_liquidity(\n metapoolErc20.balanceOf(address(this)),\n _minAmounts\n );\n\n IVault(vaultAddress).burnForStrategy(_removedAmounts[mainCoinIndex]);\n }\n}\n" + }, + "contracts/strategies/ConvexStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { IERC20, AbstractCurveStrategy, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\n/*\n * IMPORTANT(!) If ConvexStrategy needs to be re-deployed, it requires new\n * proxy contract with fresh storage slots. Changes in `AbstractCurveStrategy`\n * storage slots would break existing implementation.\n *\n * Remove this notice if ConvexStrategy is re-deployed\n */\ncontract ConvexStrategy is AbstractCurveStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n address internal cvxDepositorAddress;\n address internal cvxRewardStakerAddress;\n // slither-disable-next-line constable-states\n address private _deprecated_cvxRewardTokenAddress;\n uint256 internal cvxDepositorPTokenId;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * DAI, USDC, USDT\n * @param _pTokens Platform Token corresponding addresses\n * @param _cvxDepositorAddress Address of the Convex depositor(AKA booster) for this pool\n * @param _cvxRewardStakerAddress Address of the CVX rewards staker\n * @param _cvxDepositorPTokenId Pid of the pool referred to by Depositor and staker\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets,\n address[] calldata _pTokens,\n address _cvxDepositorAddress,\n address _cvxRewardStakerAddress,\n uint256 _cvxDepositorPTokenId\n ) external onlyGovernor initializer {\n require(_assets.length == 3, \"Must have exactly three assets\");\n // Should be set prior to abstract initialize call otherwise\n // abstractSetPToken calls will fail\n cvxDepositorAddress = _cvxDepositorAddress;\n cvxRewardStakerAddress = _cvxRewardStakerAddress;\n cvxDepositorPTokenId = _cvxDepositorPTokenId;\n pTokenAddress = _pTokens[0];\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n function _lpDepositAll() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // Deposit with staking\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n pToken.balanceOf(address(this)),\n true\n );\n require(success, \"Failed to deposit to Convex\");\n }\n\n function _lpWithdraw(uint256 numCrvTokens) internal override {\n uint256 gaugePTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n // Not enough in this contract or in the Gauge, can't proceed\n require(numCrvTokens > gaugePTokens, \"Insufficient 3CRV balance\");\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards to this\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n numCrvTokens,\n true\n );\n }\n\n function _lpWithdrawAll() internal override {\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards to this\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n IRewardStaking(cvxRewardStakerAddress).balanceOf(address(this)),\n true\n );\n }\n\n function _approveBase() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // 3Pool for LP token (required for removing liquidity)\n pToken.safeApprove(platformAddress, 0);\n pToken.safeApprove(platformAddress, type(uint256).max);\n // Gauge for LP token\n pToken.safeApprove(cvxDepositorAddress, 0);\n pToken.safeApprove(cvxDepositorAddress, type(uint256).max);\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 contractPTokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n uint256 gaugePTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n uint256 totalPTokens = contractPTokens + gaugePTokens;\n\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (totalPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = (totalPTokens * virtual_price) / 1e18;\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = value.scaleBy(assetDecimals, 18) / 3;\n }\n }\n\n /**\n * @dev Collect accumulated CRV and CVX and send to Vault.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n IRewardStaking(cvxRewardStakerAddress).getReward();\n _collectRewardTokens();\n }\n}\n" + }, + "contracts/strategies/CurveAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for a Curve pool using an OToken.\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { ICurveStableSwapNG } from \"../interfaces/ICurveStableSwapNG.sol\";\nimport { ICurveLiquidityGaugeV6 } from \"../interfaces/ICurveLiquidityGaugeV6.sol\";\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\nimport { ICurveMinter } from \"../interfaces/ICurveMinter.sol\";\n\ncontract CurveAMOStrategy is InitializableAbstractStrategy {\n using SafeCast for uint256;\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n /**\n * @dev a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n // New immutable variables that must be set in the constructor\n /**\n * @notice Address of the hard asset (weth, usdt, usdc).\n */\n IERC20 public immutable hardAsset;\n\n /**\n * @notice Address of the OTOKEN token contract.\n */\n IERC20 public immutable oToken;\n\n /**\n * @notice Address of the LP (Liquidity Provider) token contract.\n */\n IERC20 public immutable lpToken;\n\n /**\n * @notice Address of the Curve StableSwap NG pool contract.\n */\n ICurveStableSwapNG public immutable curvePool;\n\n /**\n * @notice Address of the Curve X-Chain Liquidity Gauge contract.\n */\n ICurveLiquidityGaugeV6 public immutable gauge;\n\n /**\n * @notice Address of the Curve Minter contract.\n */\n ICurveMinter public immutable minter;\n\n /**\n * @notice Index of the OTOKEN and hardAsset in the Curve pool.\n */\n uint128 public immutable otokenCoinIndex;\n uint128 public immutable hardAssetCoinIndex;\n\n /**\n * @notice Decimals of the OTOKEN and hardAsset.\n */\n uint8 public immutable decimalsOToken;\n uint8 public immutable decimalsHardAsset;\n\n /**\n * @notice Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n uint256 public maxSlippage;\n\n event MaxSlippageUpdated(uint256 newMaxSlippage);\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the hard asset and OToken balances in the Curve pool\n uint256[] memory balancesBefore = curvePool.get_balances();\n // diff = hardAsset balance - OTOKEN balance\n int256 diffBefore = (\n balancesBefore[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() - balancesBefore[otokenCoinIndex].toInt256();\n\n _;\n\n // Get the hard asset and OToken balances in the Curve pool\n uint256[] memory balancesAfter = curvePool.get_balances();\n // diff = hardAsset balance - OTOKEN balance\n int256 diffAfter = (\n balancesAfter[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() - balancesAfter[otokenCoinIndex].toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OTOKEN, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of hardAsset, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _otoken,\n address _hardAsset,\n address _gauge,\n address _minter\n ) InitializableAbstractStrategy(_baseConfig) {\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveStableSwapNG(_baseConfig.platformAddress);\n minter = ICurveMinter(_minter);\n\n oToken = IERC20(_otoken);\n hardAsset = IERC20(_hardAsset);\n gauge = ICurveLiquidityGaugeV6(_gauge);\n decimalsHardAsset = IBasicToken(_hardAsset).decimals();\n decimalsOToken = IBasicToken(_otoken).decimals();\n\n (hardAssetCoinIndex, otokenCoinIndex) = curvePool.coins(0) == _hardAsset\n ? (0, 1)\n : (1, 0);\n require(\n curvePool.coins(otokenCoinIndex) == _otoken &&\n curvePool.coins(hardAssetCoinIndex) == _hardAsset,\n \"Invalid coin indexes\"\n );\n require(gauge.lp_token() == address(curvePool), \"Invalid pool\");\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV\n * @param _maxSlippage Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV\n uint256 _maxSlippage\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n address[] memory _assets = new address[](1);\n _assets[0] = address(hardAsset);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n _setMaxSlippage(_maxSlippage);\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit hard asset into the Curve pool\n * @param _hardAsset Address of hard asset contract.\n * @param _amount Amount of hard asset to deposit.\n */\n function deposit(address _hardAsset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_hardAsset, _amount);\n }\n\n function _deposit(address _hardAsset, uint256 _hardAssetAmount) internal {\n require(_hardAssetAmount > 0, \"Must deposit something\");\n require(_hardAsset == address(hardAsset), \"Unsupported asset\");\n\n emit Deposit(_hardAsset, address(lpToken), _hardAssetAmount);\n uint256 scaledHardAssetAmount = _hardAssetAmount.scaleBy(\n decimalsOToken,\n decimalsHardAsset\n );\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 otokenToAdd = uint256(\n _max(\n 0,\n (\n balances[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() +\n scaledHardAssetAmount.toInt256() -\n balances[otokenCoinIndex].toInt256()\n )\n );\n\n /* Add so much OTOKEN so that the pool ends up being balanced. And at minimum\n * add as much OTOKEN as hard asset and at maximum twice as much OTOKEN.\n */\n otokenToAdd = Math.max(otokenToAdd, scaledHardAssetAmount);\n otokenToAdd = Math.min(otokenToAdd, scaledHardAssetAmount * 2);\n\n /* Mint OTOKEN with a strategy that attempts to contribute to stability of OTOKEN/hardAsset pool. Try\n * to mint so much OTOKEN that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OTOKEN minted will always be at least equal or greater\n * to hardAsset amount deployed. And never larger than twice the hardAsset amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(otokenToAdd);\n\n emit Deposit(address(oToken), address(lpToken), otokenToAdd);\n\n uint256[] memory _amounts = new uint256[](2);\n _amounts[hardAssetCoinIndex] = _hardAssetAmount;\n _amounts[otokenCoinIndex] = otokenToAdd;\n\n uint256 valueInLpTokens = (scaledHardAssetAmount + otokenToAdd)\n .divPrecisely(curvePool.get_virtual_price());\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Do the deposit to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(_amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool's LP tokens into the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n /**\n * @notice Deposit the strategy's entire balance of hardAsset into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = hardAsset.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(hardAsset), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw hardAsset and OTOKEN from the Curve pool, burn the OTOKEN,\n * transfer hardAsset to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _hardAsset Address of the hardAsset contract.\n * @param _amount Amount of hardAsset to withdraw.\n */\n function withdraw(\n address _recipient,\n address _hardAsset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(\n _hardAsset == address(hardAsset),\n \"Can only withdraw hard asset\"\n );\n\n emit Withdrawal(_hardAsset, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(\n _amount.scaleBy(decimalsOToken, decimalsHardAsset)\n );\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough hardAsset on balanced removal\n */\n uint256[] memory _minWithdrawalAmounts = new uint256[](2);\n _minWithdrawalAmounts[hardAssetCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OTOKEN and any that was left in the strategy\n uint256 otokenToBurn = oToken.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n\n // Transfer hardAsset to the recipient\n hardAsset.safeTransfer(_recipient, _amount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n function calcTokenToBurn(uint256 _hardAssetAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much hardAsset\n * we want we can determine how much of OTOKEN we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolHardAssetBalance = curvePool\n .balances(hardAssetCoinIndex)\n .scaleBy(decimalsOToken, decimalsHardAsset);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolHardAssetBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_hardAssetAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all hardAsset and OTOKEN from the Curve pool, burn the OTOKEN,\n * transfer hardAsset to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = gauge.balanceOf(address(this));\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[] memory minWithdrawAmounts = new uint256[](2);\n\n // Check balance of LP tokens in the strategy, if 0 return\n uint256 lpBalance = lpToken.balanceOf(address(this));\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n if (lpBalance > 0) {\n curvePool.remove_liquidity(lpBalance, minWithdrawAmounts);\n }\n\n // Burn all OTOKEN\n uint256 otokenToBurn = oToken.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n // Get the strategy contract's hardAsset balance.\n // This includes all that was removed from the Curve pool and\n // any hardAsset that was sitting in the strategy contract before the removal.\n uint256 hardAssetBalance = hardAsset.balanceOf(address(this));\n hardAsset.safeTransfer(vaultAddress, hardAssetBalance);\n\n if (hardAssetBalance > 0)\n emit Withdrawal(\n address(hardAsset),\n address(lpToken),\n hardAssetBalance\n );\n if (otokenToBurn > 0)\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many hardAsset.\n * The OToken/Asset, eg OTOKEN/hardAsset, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[] memory amounts = new uint256[](2);\n amounts[otokenCoinIndex] = _oTokens;\n\n // Convert OTOKEN to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool LP tokens to the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Deposit(address(oToken), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough hardAsset.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from gauge and remove OTokens from the Curve pool\n uint256 otokenToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n otokenCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n }\n\n /**\n * @notice One-sided remove of hardAsset from the Curve pool and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many hardAsset.\n * The OToken/Asset, eg OTOKEN/hardAsset, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for hardAsset.\n * @dev Curve pool LP tokens is used rather than hardAsset assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of hardAsset. Curve's `calc_token_amount` function does not include fees.\n * A 3rd party library can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * calculate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Curve gauge and remove hardAsset from the Curve pool\n uint256 hardAssetAmount = _withdrawAndRemoveFromPool(\n _lpTokens,\n hardAssetCoinIndex\n );\n\n // Transfer hardAsset to the vault\n hardAsset.safeTransfer(vaultAddress, hardAssetAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(hardAsset), address(lpToken), hardAssetAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the gauge and\n * do a one-sided remove of hardAsset or OTOKEN from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the gauge\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = hardAsset, 1 = OTOKEN.\n * @return coinsRemoved The amount of hardAsset or OTOKEN removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Curve gauge\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to hardAsset value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n\n if (coinIndex == hardAssetCoinIndex) {\n valueInEth = valueInEth.scaleBy(decimalsHardAsset, decimalsOToken);\n }\n\n // Apply slippage to hardAsset value\n uint256 minAmount = valueInEth.mulTruncate(uint256(1e18) - maxSlippage);\n\n // Remove just the hardAsset from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOtokenSupply = oToken.totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOtokenSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV rewards from inflation\n minter.mint(address(gauge));\n\n // Collect extra gauge rewards (outside of CRV)\n gauge.claim_rewards();\n\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _lpAmount) internal {\n require(\n gauge.balanceOf(address(this)) >= _lpAmount,\n \"Insufficient LP tokens\"\n );\n // withdraw lp tokens from the gauge without claiming rewards\n gauge.withdraw(_lpAmount);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(hardAsset), \"Unsupported asset\");\n\n // hardAsset balance needed here for the balance check that happens from vault during depositing.\n balance = hardAsset.balanceOf(address(this));\n uint256 lpTokens = gauge.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += ((lpTokens * curvePool.get_virtual_price()) / 1e18)\n .scaleBy(decimalsHardAsset, decimalsOToken);\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(hardAsset);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Sets the maximum slippage allowed for any swap/liquidity operation\n * @param _maxSlippage Maximum slippage allowed, 1e18 = 100%.\n */\n function setMaxSlippage(uint256 _maxSlippage) external onlyGovernor {\n _setMaxSlippage(_maxSlippage);\n }\n\n function _setMaxSlippage(uint256 _maxSlippage) internal {\n require(_maxSlippage <= 5e16, \"Slippage must be less than 100%\");\n maxSlippage = _maxSlippage;\n emit MaxSlippageUpdated(_maxSlippage);\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OTOKEN (required for adding liquidity)\n // slither-disable-next-line unused-return\n oToken.approve(platformAddress, type(uint256).max);\n\n // Approve Curve pool for hardAsset (required for adding liquidity)\n // slither-disable-next-line unused-return\n hardAsset.safeApprove(platformAddress, type(uint256).max);\n\n // Approve Curve gauge contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Curve gauge.\n // slither-disable-next-line unused-return\n lpToken.approve(address(gauge), type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/CurvePoolBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport { ICampaignRemoteManager } from \"../interfaces/ICampaignRemoteManager.sol\";\n\n/// @title CurvePoolBooster\n/// @author Origin Protocol\n/// @notice Contract to manage interactions with VotemarketV2 for a dedicated Curve pool/gauge.\ncontract CurvePoolBooster is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n ////////////////////////////////////////////////////\n /// --- CONSTANTS && IMMUTABLES\n ////////////////////////////////////////////////////\n /// @notice Base fee for the contract, 100%\n uint16 public constant FEE_BASE = 10_000;\n\n /// @notice Address of the gauge to manage\n address public immutable gauge;\n\n /// @notice Address of the reward token\n address public immutable rewardToken;\n\n /// @notice Chain id of the target chain\n uint256 public immutable targetChainId;\n\n ////////////////////////////////////////////////////\n /// --- STORAGE\n ////////////////////////////////////////////////////\n /// @notice Fee in FEE_BASE unit payed when managing campaign.\n uint16 public fee;\n\n /// @notice Address of the fee collector\n address public feeCollector;\n\n /// @notice Address of the campaignRemoteManager linked to VotemarketV2\n address public campaignRemoteManager;\n\n /// @notice Address of votemarket in L2\n address public votemarket;\n\n /// @notice Id of the campaign created\n uint256 public campaignId;\n\n ////////////////////////////////////////////////////\n /// --- EVENTS\n ////////////////////////////////////////////////////\n event FeeUpdated(uint16 newFee);\n event FeeCollected(address feeCollector, uint256 feeAmount);\n event FeeCollectorUpdated(address newFeeCollector);\n event VotemarketUpdated(address newVotemarket);\n event CampaignRemoteManagerUpdated(address newCampaignRemoteManager);\n event CampaignCreated(\n address gauge,\n address rewardToken,\n uint256 maxRewardPerVote,\n uint256 totalRewardAmount\n );\n event CampaignIdUpdated(uint256 newId);\n event CampaignClosed(uint256 campaignId);\n event TotalRewardAmountUpdated(uint256 extraTotalRewardAmount);\n event NumberOfPeriodsUpdated(uint8 extraNumberOfPeriods);\n event RewardPerVoteUpdated(uint256 newMaxRewardPerVote);\n event TokensRescued(address token, uint256 amount, address receiver);\n\n ////////////////////////////////////////////////////\n /// --- CONSTRUCTOR && INITIALIZATION\n ////////////////////////////////////////////////////\n constructor(\n uint256 _targetChainId,\n address _rewardToken,\n address _gauge\n ) {\n targetChainId = _targetChainId;\n rewardToken = _rewardToken;\n gauge = _gauge;\n\n // Prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /// @notice initialize function, to set up initial internal state\n /// @param _strategist Address of the strategist\n /// @param _fee Fee in FEE_BASE unit payed when managing campaign\n /// @param _feeCollector Address of the fee collector\n function initialize(\n address _strategist,\n uint16 _fee,\n address _feeCollector,\n address _campaignRemoteManager,\n address _votemarket\n ) external onlyGovernor initializer {\n _setStrategistAddr(_strategist);\n _setFee(_fee);\n _setFeeCollector(_feeCollector);\n _setCampaignRemoteManager(_campaignRemoteManager);\n _setVotemarket(_votemarket);\n }\n\n ////////////////////////////////////////////////////\n /// --- MUTATIVE FUNCTIONS\n ////////////////////////////////////////////////////\n /// @notice Create a new campaign on VotemarketV2\n /// @dev This will use all token available in this contract\n /// @param numberOfPeriods Duration of the campaign in weeks\n /// @param maxRewardPerVote Maximum reward per vote to distribute, to avoid overspending\n /// @param blacklist List of addresses to exclude from the campaign\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function createCampaign(\n uint8 numberOfPeriods,\n uint256 maxRewardPerVote,\n address[] calldata blacklist,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId == 0, \"Campaign already created\");\n require(numberOfPeriods > 1, \"Invalid number of periods\");\n require(maxRewardPerVote > 0, \"Invalid reward per vote\");\n\n // Handle fee (if any)\n uint256 balanceSubFee = _handleFee();\n\n // Approve the balanceSubFee to the campaign manager\n IERC20(rewardToken).safeApprove(campaignRemoteManager, 0);\n IERC20(rewardToken).safeApprove(campaignRemoteManager, balanceSubFee);\n\n // Create a new campaign\n ICampaignRemoteManager(campaignRemoteManager).createCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignCreationParams({\n chainId: targetChainId,\n gauge: gauge,\n manager: address(this),\n rewardToken: rewardToken,\n numberOfPeriods: numberOfPeriods,\n maxRewardPerVote: maxRewardPerVote,\n totalRewardAmount: balanceSubFee,\n addresses: blacklist,\n hook: address(0),\n isWhitelist: false\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit CampaignCreated(\n gauge,\n rewardToken,\n maxRewardPerVote,\n balanceSubFee\n );\n }\n\n /// @notice Manage the total reward amount of the campaign\n /// @dev This function should be called after the campaign is created\n /// @dev This will use all the token available in this contract\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageTotalRewardAmount(\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n\n // Handle fee (if any)\n uint256 balanceSubFee = _handleFee();\n\n // Approve the total reward amount to the campaign manager\n IERC20(rewardToken).safeApprove(campaignRemoteManager, 0);\n IERC20(rewardToken).safeApprove(campaignRemoteManager, balanceSubFee);\n\n // Manage the campaign\n // https://github.com/stake-dao/votemarket-v2/blob/main/packages/votemarket/src/Votemarket.sol#L668\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: 0,\n totalRewardAmount: balanceSubFee,\n maxRewardPerVote: 0\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit TotalRewardAmountUpdated(balanceSubFee);\n }\n\n /// @notice Manage the number of periods of the campaign\n /// @dev This function should be called after the campaign is created\n /// @param extraNumberOfPeriods Number of additional periods (cannot be 0)\n /// that will be added to already existing amount of periods.\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageNumberOfPeriods(\n uint8 extraNumberOfPeriods,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n require(extraNumberOfPeriods > 0, \"Invalid number of periods\");\n\n // Manage the campaign\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: extraNumberOfPeriods,\n totalRewardAmount: 0,\n maxRewardPerVote: 0\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit NumberOfPeriodsUpdated(extraNumberOfPeriods);\n }\n\n /// @notice Manage the reward per vote of the campaign\n /// @dev This function should be called after the campaign is created\n /// @param newMaxRewardPerVote New maximum reward per vote\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageRewardPerVote(\n uint256 newMaxRewardPerVote,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n require(newMaxRewardPerVote > 0, \"Invalid reward per vote\");\n\n // Manage the campaign\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: 0,\n totalRewardAmount: 0,\n maxRewardPerVote: newMaxRewardPerVote\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit RewardPerVoteUpdated(newMaxRewardPerVote);\n }\n\n /// @notice Close the campaign.\n /// @dev This function only work on the L2 chain. Not on mainnet.\n /// @dev The _campaignId parameter is not related to the campaignId of this contract, allowing greater flexibility.\n /// @param _campaignId Id of the campaign to close\n function closeCampaign(\n uint256 _campaignId,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n ICampaignRemoteManager(campaignRemoteManager).closeCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignClosingParams({\n campaignId: campaignId\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n emit CampaignClosed(_campaignId);\n }\n\n /// @notice calculate the fee amount and transfer it to the feeCollector\n /// @return Balance after fee\n function _handleFee() internal returns (uint256) {\n // Cache current rewardToken balance\n uint256 balance = IERC20(rewardToken).balanceOf(address(this));\n require(balance > 0, \"No reward to manage\");\n\n uint256 feeAmount = (balance * fee) / FEE_BASE;\n\n // If there is a fee, transfer it to the feeCollector\n if (feeAmount > 0) {\n // Transfer the fee to the feeCollector\n IERC20(rewardToken).safeTransfer(feeCollector, feeAmount);\n emit FeeCollected(feeCollector, feeAmount);\n\n return IERC20(rewardToken).balanceOf(address(this));\n }\n\n // Return remaining balance\n return balance;\n }\n\n ////////////////////////////////////////////////////\n /// --- GOVERNANCE && OPERATION\n ////////////////////////////////////////////////////\n /// @notice Set the campaign id\n /// @dev Only callable by the governor or strategist\n /// @param _campaignId New campaign id\n function setCampaignId(uint256 _campaignId)\n external\n onlyGovernorOrStrategist\n {\n campaignId = _campaignId;\n emit CampaignIdUpdated(_campaignId);\n }\n\n /// @notice Rescue ETH from the contract\n /// @dev Only callable by the governor or strategist\n /// @param receiver Address to receive the ETH\n function rescueETH(address receiver)\n external\n nonReentrant\n onlyGovernorOrStrategist\n {\n require(receiver != address(0), \"Invalid receiver\");\n uint256 balance = address(this).balance;\n (bool success, ) = receiver.call{ value: balance }(\"\");\n require(success, \"Transfer failed\");\n emit TokensRescued(address(0), balance, receiver);\n }\n\n /// @notice Rescue ERC20 tokens from the contract\n /// @dev Only callable by the governor or strategist\n /// @param token Address of the token to rescue\n function rescueToken(address token, address receiver)\n external\n nonReentrant\n onlyGovernor\n {\n require(receiver != address(0), \"Invalid receiver\");\n uint256 balance = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransfer(receiver, balance);\n emit TokensRescued(token, balance, receiver);\n }\n\n /// @notice Set the fee\n /// @dev Only callable by the governor\n /// @param _fee New fee\n function setFee(uint16 _fee) external onlyGovernor {\n _setFee(_fee);\n }\n\n /// @notice Internal logic to set the fee\n function _setFee(uint16 _fee) internal {\n require(_fee <= FEE_BASE / 2, \"Fee too high\");\n fee = _fee;\n emit FeeUpdated(_fee);\n }\n\n /// @notice Set the fee collector\n /// @dev Only callable by the governor\n /// @param _feeCollector New fee collector\n function setFeeCollector(address _feeCollector) external onlyGovernor {\n _setFeeCollector(_feeCollector);\n }\n\n /// @notice Internal logic to set the fee collector\n function _setFeeCollector(address _feeCollector) internal {\n require(_feeCollector != address(0), \"Invalid fee collector\");\n feeCollector = _feeCollector;\n emit FeeCollectorUpdated(_feeCollector);\n }\n\n /// @notice Set the campaignRemoteManager\n /// @param _campaignRemoteManager New campaignRemoteManager address\n function setCampaignRemoteManager(address _campaignRemoteManager)\n external\n onlyGovernor\n {\n _setCampaignRemoteManager(_campaignRemoteManager);\n }\n\n /// @notice Internal logic to set the campaignRemoteManager\n /// @param _campaignRemoteManager New campaignRemoteManager address\n function _setCampaignRemoteManager(address _campaignRemoteManager)\n internal\n {\n require(\n _campaignRemoteManager != address(0),\n \"Invalid campaignRemoteManager\"\n );\n campaignRemoteManager = _campaignRemoteManager;\n emit CampaignRemoteManagerUpdated(_campaignRemoteManager);\n }\n\n /// @notice Set the votemarket address\n /// @param _votemarket New votemarket address\n function setVotemarket(address _votemarket) external onlyGovernor {\n _setVotemarket(_votemarket);\n }\n\n /// @notice Internal logic to set the votemarket address\n function _setVotemarket(address _votemarket) internal onlyGovernor {\n require(_votemarket != address(0), \"Invalid votemarket\");\n votemarket = _votemarket;\n emit VotemarketUpdated(_votemarket);\n }\n\n receive() external payable {}\n}\n" + }, + "contracts/strategies/Generalized4626Strategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Generalized 4626 Strategy\n * @notice Investment strategy for ERC-4626 Tokenized Vaults\n * @author Origin Protocol Inc\n */\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\ncontract Generalized4626Strategy is InitializableAbstractStrategy {\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecate_shareToken;\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecate_assetToken;\n\n IERC20 public immutable shareToken;\n IERC20 public immutable assetToken;\n\n // For future use\n uint256[50] private __gap;\n\n /**\n * @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n * and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n * @param _assetToken Address of the ERC-4626 asset token. eg frxETH or DAI\n */\n constructor(BaseStrategyConfig memory _baseConfig, address _assetToken)\n InitializableAbstractStrategy(_baseConfig)\n {\n shareToken = IERC20(_baseConfig.platformAddress);\n assetToken = IERC20(_assetToken);\n }\n\n function initialize() external virtual onlyGovernor initializer {\n address[] memory rewardTokens = new address[](0);\n address[] memory assets = new address[](1);\n address[] memory pTokens = new address[](1);\n\n assets[0] = address(assetToken);\n pTokens[0] = address(platformAddress);\n\n InitializableAbstractStrategy._initialize(\n rewardTokens,\n assets,\n pTokens\n );\n }\n\n /**\n * @dev Deposit assets by converting them to shares\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit assets by converting them to shares\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal virtual {\n require(_amount > 0, \"Must deposit something\");\n require(_asset == address(assetToken), \"Unexpected asset address\");\n\n // slither-disable-next-line unused-return\n IERC4626(platformAddress).deposit(_amount, address(this));\n emit Deposit(_asset, address(shareToken), _amount);\n }\n\n /**\n * @dev Deposit the entire balance of assetToken to gain shareToken\n */\n function depositAll() external virtual override onlyVault nonReentrant {\n uint256 balance = assetToken.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(assetToken), balance);\n }\n }\n\n /**\n * @dev Withdraw asset by burning shares\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n require(_asset == address(assetToken), \"Unexpected asset address\");\n\n // slither-disable-next-line unused-return\n IERC4626(platformAddress).withdraw(_amount, _recipient, address(this));\n emit Withdrawal(_asset, address(shareToken), _amount);\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / share tokens\n */\n function _abstractSetPToken(address, address) internal virtual override {\n _approveBase();\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll()\n external\n virtual\n override\n onlyVaultOrGovernor\n nonReentrant\n {\n uint256 shareBalance = shareToken.balanceOf(address(this));\n uint256 assetAmount = 0;\n if (shareBalance > 0) {\n assetAmount = IERC4626(platformAddress).redeem(\n shareBalance,\n vaultAddress,\n address(this)\n );\n emit Withdrawal(\n address(assetToken),\n address(shareToken),\n assetAmount\n );\n }\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(_asset == address(assetToken), \"Unexpected asset address\");\n /* We are intentionally not counting the amount of assetToken parked on the\n * contract toward the checkBalance. The deposit and withdraw functions\n * should not result in assetToken being unused and owned by this strategy\n * contract.\n */\n IERC4626 platform = IERC4626(platformAddress);\n return platform.previewRedeem(platform.balanceOf(address(this)));\n }\n\n /**\n * @notice Governor approves the ERC-4626 Tokenized Vault to spend the asset.\n */\n function safeApproveAllTokens() external override onlyGovernor {\n _approveBase();\n }\n\n function _approveBase() internal virtual {\n // Approval the asset to be transferred to the ERC-4626 Tokenized Vault.\n // Used by the ERC-4626 deposit() and mint() functions\n // slither-disable-next-line unused-return\n assetToken.approve(platformAddress, type(uint256).max);\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset)\n public\n view\n virtual\n override\n returns (bool)\n {\n return _asset == address(assetToken);\n }\n\n /**\n * @notice is not supported for this strategy as the asset and\n * ERC-4626 Tokenized Vault are set at deploy time.\n * @dev If the ERC-4626 Tokenized Vault needed to be changed, a new\n * contract would need to be deployed and the proxy updated.\n */\n function setPTokenAddress(address, address) external override onlyGovernor {\n revert(\"unsupported function\");\n }\n\n /**\n * @notice is not supported for this strategy as the asset and\n * ERC-4626 Tokenized Vault are set at deploy time.\n * @dev If the ERC-4626 Tokenized Vault needed to be changed, a new\n * contract would need to be deployed and the proxy updated.\n */\n function removePToken(uint256) external override onlyGovernor {\n revert(\"unsupported function\");\n }\n}\n" + }, + "contracts/strategies/Generalized4626USDTStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IUSDT {\n // Tether's approve does not return a bool like standard IERC20 contracts\n // slither-disable-next-line erc20-interface\n function approve(address _spender, uint256 _value) external;\n}\n\n/**\n * @title Generalized 4626 Strategy when asset is Tether USD (USDT)\n * @notice Investment strategy for ERC-4626 Tokenized Vaults for the USDT asset.\n * @author Origin Protocol Inc\n */\nimport { Generalized4626Strategy } from \"./Generalized4626Strategy.sol\";\n\ncontract Generalized4626USDTStrategy is Generalized4626Strategy {\n /**\n * @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n * and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n * @param _assetToken Address of the ERC-4626 asset token. eg frxETH or DAI\n */\n constructor(BaseStrategyConfig memory _baseConfig, address _assetToken)\n Generalized4626Strategy(_baseConfig, _assetToken)\n {}\n\n /// @dev Override for Tether as USDT does not return a bool on approve.\n /// Using assetToken.approve will fail as it expects a bool return value\n function _approveBase() internal virtual override {\n // Approval the asset to be transferred to the ERC-4626 Tokenized Vault.\n // Used by the ERC-4626 deposit() and mint() functions\n // slither-disable-next-line unused-return\n IUSDT(address(assetToken)).approve(platformAddress, type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/IAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface for Aaves Lending Pool\n * Documentation: https://developers.aave.com/#lendingpool\n */\ninterface IAaveLendingPool {\n /**\n * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.\n * - E.g. User deposits 100 USDC and gets in return 100 aUSDC\n * @param asset The address of the underlying asset to deposit\n * @param amount The amount to be deposited\n * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user\n * wants to receive them on his own wallet, or a different address if the beneficiary of aTokens\n * is a different wallet\n * @param referralCode Code used to register the integrator originating the operation, for potential rewards.\n * 0 if the action is executed directly by the user, without any middle-man\n **/\n function deposit(\n address asset,\n uint256 amount,\n address onBehalfOf,\n uint16 referralCode\n ) external;\n\n /**\n * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned\n * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC\n * @param asset The address of the underlying asset to withdraw\n * @param amount The underlying amount to be withdrawn\n * - Send the value type(uint256).max in order to withdraw the whole aToken balance\n * @param to Address that will receive the underlying, same as msg.sender if the user\n * wants to receive it on his own wallet, or a different address if the beneficiary is a\n * different wallet\n * @return The final amount withdrawn\n **/\n function withdraw(\n address asset,\n uint256 amount,\n address to\n ) external returns (uint256);\n}\n\n/**\n * @dev Interface for Aaves Lending Pool\n * Documentation: https://developers.aave.com/#lendingpooladdressesprovider\n */\ninterface ILendingPoolAddressesProvider {\n /**\n * @notice Get the current address for Aave LendingPool\n * @dev Lending pool is the core contract on which to call deposit\n */\n function getLendingPool() external view returns (address);\n}\n" + }, + "contracts/strategies/IAaveIncentivesController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IAaveIncentivesController {\n event RewardsAccrued(address indexed user, uint256 amount);\n\n event RewardsClaimed(\n address indexed user,\n address indexed to,\n uint256 amount\n );\n\n event RewardsClaimed(\n address indexed user,\n address indexed to,\n address indexed claimer,\n uint256 amount\n );\n\n event ClaimerSet(address indexed user, address indexed claimer);\n\n /*\n * @dev Returns the configuration of the distribution for a certain asset\n * @param asset The address of the reference asset of the distribution\n * @return The asset index, the emission per second and the last updated timestamp\n **/\n function getAssetData(address asset)\n external\n view\n returns (\n uint256,\n uint256,\n uint256\n );\n\n /**\n * @dev Whitelists an address to claim the rewards on behalf of another address\n * @param user The address of the user\n * @param claimer The address of the claimer\n */\n function setClaimer(address user, address claimer) external;\n\n /**\n * @dev Returns the whitelisted claimer for a certain address (0x0 if not set)\n * @param user The address of the user\n * @return The claimer address\n */\n function getClaimer(address user) external view returns (address);\n\n /**\n * @dev Configure assets for a certain rewards emission\n * @param assets The assets to incentivize\n * @param emissionsPerSecond The emission for each asset\n */\n function configureAssets(\n address[] calldata assets,\n uint256[] calldata emissionsPerSecond\n ) external;\n\n /**\n * @dev Called by the corresponding asset on any update that affects the rewards distribution\n * @param asset The address of the user\n * @param userBalance The balance of the user of the asset in the lending pool\n * @param totalSupply The total supply of the asset in the lending pool\n **/\n function handleAction(\n address asset,\n uint256 userBalance,\n uint256 totalSupply\n ) external;\n\n /**\n * @dev Returns the total of rewards of an user, already accrued + not yet accrued\n * @param user The address of the user\n * @return The rewards\n **/\n function getRewardsBalance(address[] calldata assets, address user)\n external\n view\n returns (uint256);\n\n /**\n * @dev Claims reward for an user, on all the assets of the lending pool,\n * accumulating the pending rewards\n * @param amount Amount of rewards to claim\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewards(\n address[] calldata assets,\n uint256 amount,\n address to\n ) external returns (uint256);\n\n /**\n * @dev Claims reward for an user on behalf, on all the assets of the\n * lending pool, accumulating the pending rewards. The caller must\n * be whitelisted via \"allowClaimOnBehalf\" function by the RewardsAdmin role manager\n * @param amount Amount of rewards to claim\n * @param user Address to check and claim rewards\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewardsOnBehalf(\n address[] calldata assets,\n uint256 amount,\n address user,\n address to\n ) external returns (uint256);\n\n /**\n * @dev returns the unclaimed rewards of the user\n * @param user the address of the user\n * @return the unclaimed user rewards\n */\n function getUserUnclaimedRewards(address user)\n external\n view\n returns (uint256);\n\n /**\n * @dev returns the unclaimed rewards of the user\n * @param user the address of the user\n * @param asset The asset to incentivize\n * @return the user index for the asset\n */\n function getUserAssetData(address user, address asset)\n external\n view\n returns (uint256);\n\n /**\n * @dev for backward compatibility with previous implementation of the Incentives controller\n */\n function REWARD_TOKEN() external view returns (address);\n\n /**\n * @dev for backward compatibility with previous implementation of the Incentives controller\n */\n function PRECISION() external view returns (uint8);\n\n /**\n * @dev Gets the distribution end timestamp of the emissions\n */\n function DISTRIBUTION_END() external view returns (uint256);\n}\n" + }, + "contracts/strategies/IAaveStakeToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IAaveStakedToken {\n function COOLDOWN_SECONDS() external returns (uint256);\n\n function UNSTAKE_WINDOW() external returns (uint256);\n\n function balanceOf(address addr) external returns (uint256);\n\n function redeem(address to, uint256 amount) external;\n\n function stakersCooldowns(address addr) external returns (uint256);\n\n function cooldown() external;\n}\n" + }, + "contracts/strategies/ICompound.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @dev Compound C Token interface\n * Documentation: https://compound.finance/developers/ctokens\n */\ninterface ICERC20 {\n /**\n * @notice The mint function transfers an asset into the protocol, which begins accumulating\n * interest based on the current Supply Rate for the asset. The user receives a quantity of\n * cTokens equal to the underlying tokens supplied, divided by the current Exchange Rate.\n * @param mintAmount The amount of the asset to be supplied, in units of the underlying asset.\n * @return 0 on success, otherwise an Error codes\n */\n function mint(uint256 mintAmount) external returns (uint256);\n\n /**\n * @notice Sender redeems cTokens in exchange for the underlying asset\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param redeemTokens The number of cTokens to redeem into underlying\n * @return uint 0=success, otherwise an error code.\n */\n function redeem(uint256 redeemTokens) external returns (uint256);\n\n /**\n * @notice The redeem underlying function converts cTokens into a specified quantity of the underlying\n * asset, and returns them to the user. The amount of cTokens redeemed is equal to the quantity of\n * underlying tokens received, divided by the current Exchange Rate. The amount redeemed must be less\n * than the user's Account Liquidity and the market's available liquidity.\n * @param redeemAmount The amount of underlying to be redeemed.\n * @return 0 on success, otherwise an error code.\n */\n function redeemUnderlying(uint256 redeemAmount) external returns (uint256);\n\n /**\n * @notice The user's underlying balance, representing their assets in the protocol, is equal to\n * the user's cToken balance multiplied by the Exchange Rate.\n * @param owner The account to get the underlying balance of.\n * @return The amount of underlying currently owned by the account.\n */\n function balanceOfUnderlying(address owner) external returns (uint256);\n\n /**\n * @notice Calculates the exchange rate from the underlying to the CToken\n * @dev This function does not accrue interest before calculating the exchange rate\n * @return Calculated exchange rate scaled by 1e18\n */\n function exchangeRateStored() external view returns (uint256);\n\n /**\n * @notice Get the token balance of the `owner`\n * @param owner The address of the account to query\n * @return The number of tokens owned by `owner`\n */\n function balanceOf(address owner) external view returns (uint256);\n\n /**\n * @notice Get the supply rate per block for supplying the token to Compound.\n */\n function supplyRatePerBlock() external view returns (uint256);\n\n /**\n * @notice Address of the Compound Comptroller.\n */\n function comptroller() external view returns (address);\n}\n" + }, + "contracts/strategies/IConvexDeposits.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IConvexDeposits {\n function deposit(\n uint256 _pid,\n uint256 _amount,\n bool _stake\n ) external returns (bool);\n\n function deposit(\n uint256 _amount,\n bool _lock,\n address _stakeAddress\n ) external;\n}\n" + }, + "contracts/strategies/ICRVMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICRVMinter {\n function mint(address gaugeAddress) external;\n}\n" + }, + "contracts/strategies/ICurveETHPoolV1.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurveETHPoolV1 {\n event AddLiquidity(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event ApplyNewFee(uint256 fee);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event CommitNewFee(uint256 new_fee);\n event RampA(\n uint256 old_A,\n uint256 new_A,\n uint256 initial_time,\n uint256 future_time\n );\n event RemoveLiquidity(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 token_supply\n );\n event RemoveLiquidityImbalance(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event RemoveLiquidityOne(\n address indexed provider,\n uint256 token_amount,\n uint256 coin_amount,\n uint256 token_supply\n );\n event StopRampA(uint256 A, uint256 t);\n event TokenExchange(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event Transfer(\n address indexed sender,\n address indexed receiver,\n uint256 value\n );\n\n function A() external view returns (uint256);\n\n function A_precise() external view returns (uint256);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_liquidity(uint256[2] memory _amounts, uint256 _min_mint_amount)\n external\n payable\n returns (uint256);\n\n function add_liquidity(\n uint256[2] memory _amounts,\n uint256 _min_mint_amount,\n address _receiver\n ) external payable returns (uint256);\n\n function admin_action_deadline() external view returns (uint256);\n\n function admin_balances(uint256 i) external view returns (uint256);\n\n function admin_fee() external view returns (uint256);\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function apply_new_fee() external;\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function balances(uint256 arg0) external view returns (uint256);\n\n function calc_token_amount(uint256[2] memory _amounts, bool _is_deposit)\n external\n view\n returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _burn_amount, int128 i)\n external\n view\n returns (uint256);\n\n function coins(uint256 arg0) external view returns (address);\n\n function commit_new_fee(uint256 _new_fee) external;\n\n function decimals() external view returns (uint256);\n\n function ema_price() external view returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external payable returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external payable returns (uint256);\n\n function fee() external view returns (uint256);\n\n function future_A() external view returns (uint256);\n\n function future_A_time() external view returns (uint256);\n\n function future_fee() external view returns (uint256);\n\n function get_balances() external view returns (uint256[2] memory);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n\n function get_p() external view returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function initial_A() external view returns (uint256);\n\n function initial_A_time() external view returns (uint256);\n\n function initialize(\n string memory _name,\n string memory _symbol,\n address[4] memory _coins,\n uint256[4] memory _rate_multipliers,\n uint256 _A,\n uint256 _fee\n ) external;\n\n function last_price() external view returns (uint256);\n\n function ma_exp_time() external view returns (uint256);\n\n function ma_last_time() external view returns (uint256);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function price_oracle() external view returns (uint256);\n\n function ramp_A(uint256 _future_A, uint256 _future_time) external;\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[2] memory _min_amounts\n ) external returns (uint256[2] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[2] memory _min_amounts,\n address _receiver\n ) external returns (uint256[2] memory);\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burn_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burn_amount,\n address _receiver\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received,\n address _receiver\n ) external returns (uint256);\n\n function set_ma_exp_time(uint256 _ma_exp_time) external;\n\n function stop_ramp_A() external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw_admin_fees() external;\n}\n" + }, + "contracts/strategies/ICurveGauge.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurveGauge {\n function balanceOf(address account) external view returns (uint256);\n\n function deposit(uint256 value, address account) external;\n\n function withdraw(uint256 value) external;\n}\n" + }, + "contracts/strategies/ICurveMetaPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface ICurveMetaPool {\n function add_liquidity(uint256[2] calldata amounts, uint256 min_mint_amount)\n external\n returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function remove_liquidity(uint256 _amount, uint256[2] calldata min_amounts)\n external\n returns (uint256[2] calldata);\n\n function remove_liquidity_one_coin(\n uint256 _token_amount,\n int128 i,\n uint256 min_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[2] calldata amounts,\n uint256 max_burn_amount\n ) external returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _token_amount, int128 i)\n external\n view\n returns (uint256);\n\n function balances(uint256 i) external view returns (uint256);\n\n function calc_token_amount(uint256[2] calldata amounts, bool deposit)\n external\n view\n returns (uint256);\n\n function base_pool() external view returns (address);\n\n function fee() external view returns (uint256);\n\n function coins(uint256 i) external view returns (address);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 dx,\n uint256 min_dy\n ) external returns (uint256);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n}\n" + }, + "contracts/strategies/ICurvePool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurvePool {\n function get_virtual_price() external view returns (uint256);\n\n function add_liquidity(uint256[3] calldata _amounts, uint256 _min) external;\n\n function balances(uint256) external view returns (uint256);\n\n function calc_token_amount(uint256[3] calldata _amounts, bool _deposit)\n external\n returns (uint256);\n\n function fee() external view returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _amount,\n int128 _index,\n uint256 _minAmount\n ) external;\n\n function remove_liquidity(\n uint256 _amount,\n uint256[3] calldata _minWithdrawAmounts\n ) external;\n\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n external\n view\n returns (uint256);\n\n function exchange(\n uint256 i,\n uint256 j,\n uint256 dx,\n uint256 min_dy\n ) external returns (uint256);\n\n function coins(uint256 _index) external view returns (address);\n\n function remove_liquidity_imbalance(\n uint256[3] calldata _amounts,\n uint256 maxBurnAmount\n ) external;\n}\n" + }, + "contracts/strategies/IRewardStaking.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IRewardStaking {\n function stakeFor(address, uint256) external;\n\n function stake(uint256) external;\n\n function withdraw(uint256 amount, bool claim) external;\n\n function withdrawAndUnwrap(uint256 amount, bool claim) external;\n\n function earned(address account) external view returns (uint256);\n\n function getReward() external;\n\n function getReward(address _account, bool _claimExtras) external;\n\n function extraRewardsLength() external returns (uint256);\n\n function extraRewards(uint256 _pid) external returns (address);\n\n function rewardToken() external returns (address);\n\n function balanceOf(address account) external view returns (uint256);\n}\n" + }, + "contracts/strategies/MorphoAaveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Morpho Aave Strategy\n * @notice Investment strategy for investing stablecoins via Morpho (Aave)\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { IMorpho } from \"../interfaces/morpho/IMorpho.sol\";\nimport { ILens } from \"../interfaces/morpho/ILens.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MorphoAaveStrategy is InitializableAbstractStrategy {\n address public constant MORPHO = 0x777777c9898D384F785Ee44Acfe945efDFf5f3E0;\n address public constant LENS = 0x507fA343d0A90786d86C7cd885f5C49263A91FF4;\n\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @dev Initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Approve the spending of all assets by main Morpho contract,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address asset = assetsMapped[i];\n\n // Safe approval\n IERC20(asset).safeApprove(MORPHO, 0);\n IERC20(asset).safeApprove(MORPHO, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset\n * We need to approve and allow Morpho to move them\n * @param _asset Address of the asset to approve\n * @param _pToken The pToken for the approval\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n IERC20(_asset).safeApprove(MORPHO, 0);\n IERC20(_asset).safeApprove(MORPHO, type(uint256).max);\n }\n\n /**\n * @dev Collect accumulated rewards and send them to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Morpho Aave-v2 doesn't distribute reward tokens\n // solhint-disable-next-line max-line-length\n // Ref: https://developers.morpho.xyz/interact-with-morpho/get-started/interact-with-morpho/claim-rewards#morpho-aave-v2\n }\n\n /**\n * @dev Get the amount of rewards pending to be collected from the protocol\n */\n function getPendingRewards() external view returns (uint256 balance) {\n // Morpho Aave-v2 doesn't distribute reward tokens\n // solhint-disable-next-line max-line-length\n // Ref: https://developers.morpho.xyz/interact-with-morpho/get-started/interact-with-morpho/claim-rewards#morpho-aave-v2\n return 0;\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n\n address pToken = address(_getPTokenFor(_asset));\n\n IMorpho(MORPHO).supply(\n pToken,\n address(this), // the address of the user you want to supply on behalf of\n _amount\n );\n emit Deposit(_asset, pToken, _amount);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Morpho\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Morpho\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n address pToken = address(_getPTokenFor(_asset));\n\n IMorpho(MORPHO).withdraw(pToken, _amount);\n emit Withdrawal(_asset, pToken, _amount);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = _checkBalance(assetsMapped[i]);\n if (balance > 0) {\n _withdraw(vaultAddress, assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Return total value of an asset held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n return _checkBalance(_asset);\n }\n\n function _checkBalance(address _asset)\n internal\n view\n returns (uint256 balance)\n {\n address pToken = address(_getPTokenFor(_asset));\n\n // Total value represented by decimal position of underlying token\n (, , balance) = ILens(LENS).getCurrentSupplyBalanceInOf(\n pToken,\n address(this)\n );\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Get the pToken wrapped in the IERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return pToken Corresponding pToken to this asset\n */\n function _getPTokenFor(address _asset) internal view returns (IERC20) {\n address pToken = assetToPToken[_asset];\n require(pToken != address(0), \"pToken does not exist\");\n return IERC20(pToken);\n }\n}\n" + }, + "contracts/strategies/MorphoCompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Morpho Compound Strategy\n * @notice Investment strategy for investing stablecoins via Morpho (Compound)\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, AbstractCompoundStrategy, InitializableAbstractStrategy } from \"./AbstractCompoundStrategy.sol\";\nimport { IMorpho } from \"../interfaces/morpho/IMorpho.sol\";\nimport { ILens } from \"../interfaces/morpho/ILens.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MorphoCompoundStrategy is AbstractCompoundStrategy {\n address public constant MORPHO = 0x8888882f8f843896699869179fB6E4f7e3B58888;\n address public constant LENS = 0x930f1b46e1D081Ec1524efD95752bE3eCe51EF67;\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @dev Initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Approve the spending of all assets by main Morpho contract,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address asset = assetsMapped[i];\n\n // Safe approval\n IERC20(asset).safeApprove(MORPHO, 0);\n IERC20(asset).safeApprove(MORPHO, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset\n * We need to approve and allow Morpho to move them\n * @param _asset Address of the asset to approve\n * @param _pToken The pToken for the approval\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n IERC20(_asset).safeApprove(MORPHO, 0);\n IERC20(_asset).safeApprove(MORPHO, type(uint256).max);\n }\n\n /**\n * @dev Collect accumulated rewards and send them to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n /**\n * Gas considerations. We could query Morpho LENS's `getUserUnclaimedRewards` for each\n * cToken separately and only claimRewards where it is economically feasible. Each call\n * (out of 3) costs ~60k gas extra.\n *\n * Each extra cToken in the `poolTokens` of `claimRewards` function makes that call\n * 89-120k more expensive gas wise.\n *\n * With Lens query in case where:\n * - there is only 1 reward token to collect. Net gas usage in best case would be\n * 3*60 - 2*120 = -60k -> saving 60k gas\n * - there are 2 reward tokens to collect. Net gas usage in best case would be\n * 3*60 - 120 = 60k -> more expensive for 60k gas\n * - there are 3 reward tokens to collect. Net gas usage in best case would be\n * 3*60 = 180k -> more expensive for 180k gas\n *\n * For the above reasoning such \"optimization\" is not implemented\n */\n\n address[] memory poolTokens = new address[](assetsMapped.length);\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n poolTokens[i] = assetToPToken[assetsMapped[i]];\n }\n\n // slither-disable-next-line unused-return\n IMorpho(MORPHO).claimRewards(\n poolTokens, // The addresses of the underlying protocol's pools to claim rewards from\n false // Whether to trade the accrued rewards for MORPHO token, with a premium\n );\n\n // Transfer COMP to Harvester\n IERC20 rewardToken = IERC20(rewardTokenAddresses[0]);\n uint256 balance = rewardToken.balanceOf(address(this));\n emit RewardTokenCollected(\n harvesterAddress,\n rewardTokenAddresses[0],\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n\n /**\n * @dev Get the amount of rewards pending to be collected from the protocol\n */\n function getPendingRewards() external view returns (uint256 balance) {\n address[] memory poolTokens = new address[](assetsMapped.length);\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n poolTokens[i] = assetToPToken[assetsMapped[i]];\n }\n\n return ILens(LENS).getUserUnclaimedRewards(poolTokens, address(this));\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n\n IMorpho(MORPHO).supply(\n address(_getCTokenFor(_asset)),\n address(this), // the address of the user you want to supply on behalf of\n _amount\n );\n emit Deposit(_asset, address(_getCTokenFor(_asset)), _amount);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Morpho\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Morpho\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n address pToken = assetToPToken[_asset];\n\n IMorpho(MORPHO).withdraw(pToken, _amount);\n emit Withdrawal(_asset, address(_getCTokenFor(_asset)), _amount);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = _checkBalance(assetsMapped[i]);\n if (balance > 0) {\n _withdraw(vaultAddress, assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Return total value of an asset held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n return _checkBalance(_asset);\n }\n\n function _checkBalance(address _asset)\n internal\n view\n returns (uint256 balance)\n {\n address pToken = assetToPToken[_asset];\n\n // Total value represented by decimal position of underlying token\n (, , balance) = ILens(LENS).getCurrentSupplyBalanceInOf(\n pToken,\n address(this)\n );\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with\n /// `platformAddress` not used so empty address\n /// `vaultAddress` the address of the OETH Vault contract\n /// @param _wethAddress Address of the WETH Token contract\n /// @param _ssvToken Address of the SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconProofs,\n _beaconGenesisTimestamp\n )\n {\n SSV_TOKEN = _ssvToken;\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// @param _rewardTokenAddresses Not used so empty array\n /// @param _assets Not used so empty array\n /// @param _pTokens Not used so empty array\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n safeApproveAllTokens();\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH that was transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _asset, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(_asset, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, WETH, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Accounts for all the assets managed by this strategy which includes:\n /// 1. The current WETH in this strategy contract\n /// 2. The last verified ETH balance, total deposits and total validator balances\n /// @param _asset Address of WETH asset.\n /// @return balance Total value in ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by the strategy.\n /// @param _asset The address of the WETH token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for validator registration.\n function safeApproveAllTokens() public override {\n // Approves the SSV Network contract to transfer SSV tokens when validators are registered\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n * The new ETH will be accounted for in `checkBalance` after the next snapBalances and verifyBalances txs.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n /// @dev This strategy does not use a platform token like the old Aave and Compound strategies.\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instead of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract.\n /// Withdrawals from validators also accumulate as ETH in this strategy contract.\n /// It's too complex to separate the rewards from withdrawals so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can now regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable {\n using SafeERC20 for IERC20;\n\n /// @notice The amount of ETH in wei that is required for a deposit to a new validator.\n /// Initially this is 32 ETH, but will be reduced to 1 ETH after P2P's APIs have been updated\n /// to support deposits of 1 ETH.\n uint256 public constant DEPOSIT_AMOUNT_WEI = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n /// @notice The timestamp of the Beacon chain genesis.\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\n uint64 public immutable BEACON_GENESIS_TIMESTAMP;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// Deposit data for new compounding validators.\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountWei Amount of ETH in wei that has been deposited to the beacon chain deposit contract\n /// @param blockNumber Block number when the deposit was made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either PENDING or PROVEN\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint32 depositIndex;\n DepositStatus status;\n }\n /// @notice Mapping of the root of a deposit (depositDataRoot) to its data\n mapping(bytes32 => DepositData) public deposits;\n /// @notice List of deposit roots that are still to be verified as processed on the beacon chain\n bytes32[] public depositsRoots;\n\n // Validator data\n\n struct ValidatorData {\n bytes32 pubKeyHash; // Hash of the validator's public key using the Beacon Chain's format\n uint64 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes and indexes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n ValidatorData[] internal verifiedValidators;\n /// @notice State of the new compounding validators with a 0x02 withdrawal credential prefix.\n /// Uses the Beacon chain hashing for BLSPubkey which is\n /// sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => VALIDATOR_STATE) public validatorState;\n\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n uint64 timestamp;\n uint128 ethBalance;\n }\n // TODO is it more efficient to use the block root rather than hashing it?\n /// @notice Mapping of the block root to the balances at that slot\n mapping(bytes32 => Balances) public snappedBalances;\n uint64 public lastSnapTimestamp;\n uint128 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[50] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITED, // The validator has been verified to have a zero balance\n REMOVED // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified and is ready to be staked\n }\n\n event RegistratorChanged(address indexed newAddress);\n event SourceStrategyAdded(address indexed strategy);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n bytes32 indexed depositDataRoot,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint64 indexed validatorIndex\n );\n event DepositVerified(bytes32 indexed depositDataRoot, uint256 amountWei);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(\n uint256 indexed timestamp,\n bytes32 indexed blockRoot,\n uint256 ethBalance\n );\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 wethBalance,\n uint256 ethBalance\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_PROOFS = _beaconProofs;\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\n\n require(\n block.timestamp > _beaconGenesisTimestamp,\n \"Invalid genesis timestamp\"\n );\n }\n\n /***************************************\n Admin Functions\n ****************************************/\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /***************************************`\n Validator Management\n ****************************************/\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator {\n require(publicKey.length == 48, \"Invalid public key length\");\n\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validatorState[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validator validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validator,\n uint64 depositAmountGwei\n ) external onlyRegistrator {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validator.pubkey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == VALIDATOR_STATE.REGISTERED ||\n currentState == VALIDATOR_STATE.VERIFIED),\n \"Not registered or verified\"\n );\n require(\n currentState == VALIDATOR_STATE.VERIFIED ||\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validator.pubkey,\n withdrawalCredentials,\n validator.signature,\n validator.depositDataRoot\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == VALIDATOR_STATE.REGISTERED) {\n validatorState[pubKeyHash] = VALIDATOR_STATE.STAKED;\n }\n\n /// After the Pectra upgrade the validators have a new restriction when proposing\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\n /// forward. Each slot is created at strict 12 second intervals and those slots can\n /// either have blocks attached to them or not. This way using the block.timestamp\n /// the slot number can easily be calculated.\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\n BEACON_GENESIS_TIMESTAMP) / 12;\n\n // Store the deposit data for verifyDeposit and verifyBalances\n deposits[validator.depositDataRoot] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n slot: depositSlot,\n depositIndex: SafeCast.toUint32(depositsRoots.length),\n status: DepositStatus.PENDING\n });\n depositsRoots.push(validator.depositDataRoot);\n\n emit ETHStaked(\n pubKeyHash,\n validator.depositDataRoot,\n validator.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then only the amount in excess of 32 ETH will be withdrawn.\n /// Only the Registrator can call this function.\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\n /// If no ETH balance, the tx will revert.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n payable\n onlyRegistrator\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove the validator from the SSV Cluster after it has exited.\n /// Make sure `validatorWithdrawal` is called with a zero amount and the validator has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == VALIDATOR_STATE.EXITED ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exited\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validatorState[pubKeyHash] = VALIDATOR_STATE.REMOVED;\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /***************************************\n SSV Management\n ****************************************/\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n Beacon Chain Proofs\n ****************************************/\n\n /// @notice Verifies a validator's index to its public key.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.STAKED,\n \"Validator not staked\"\n );\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key.\n // Also verify the validator's withdrawal credential points to this strategy.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n address(this) // Withdrawal address is this strategy\n );\n\n // Store the validator state as verified\n validatorState[pubKeyHash] = VALIDATOR_STATE.VERIFIED;\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(\n ValidatorData({ pubKeyHash: pubKeyHash, index: validatorIndex })\n );\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n ///\n /// Important: this function has a limitation where the `verificationSlot` that is passed by the off-chain\n /// verifier requires a slot immediately after it to propose a block otherwise the `BeaconRoots.parentBlockRoot`\n /// will fail. This shouldn't be a problem, since by the current behaviour of beacon chain only 1%-3% slots\n /// don't propose a block.\n /// @param depositDataRoot The root of the deposit data that was stored when\n /// the deposit was made on the execution layer.\n /// @param verificationSlot Any slot on or after the deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\n /// set for the next block timestamp in 12 seconds time.\n /// @param firstPendingDepositSlot The slot of the first pending deposit in the beacon chain.\n /// Can be anything if the deposit queue is empty, but zero is a good choice.\n /// @param firstPendingDepositSlotProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n bytes32 depositDataRoot,\n uint64 verificationSlot,\n uint64 firstPendingDepositSlot,\n bytes calldata firstPendingDepositSlotProof\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositDataRoot];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n validatorState[deposit.pubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n // The verification slot must be after the deposit's slot.\n // This is needed for when the deposit queue is empty.\n require(deposit.slot < verificationSlot, \"Slot not after deposit\");\n\n // Calculate the next block timestamp from the verification slot.\n uint64 nextBlockTimestamp = 12 *\n verificationSlot +\n BEACON_GENESIS_TIMESTAMP +\n // Add 12 seconds for the next block\n 12;\n // Get the parent beacon block root of the next block which is the block root of the verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDepositSlot(\n blockRoot,\n firstPendingDepositSlot,\n firstPendingDepositSlotProof\n );\n\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the deposit queue is empty then our deposit must have been processed on the beacon chain.\n // The deposit slot can be zero for validators consolidating to a compounding validator. We can\n // not guarantee that the deposit has been processed in that case.\n require(\n deposit.slot < firstPendingDepositSlot || isDepositQueueEmpty,\n \"Deposit likely not processed\"\n );\n\n // After verifying the proof, update the contract storage\n deposits[depositDataRoot].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n bytes32 lastDepositDataRoot = depositsRoots[depositsRoots.length - 1];\n depositsRoots[deposit.depositIndex] = lastDepositDataRoot;\n deposits[lastDepositDataRoot].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositsRoots.pop();\n\n emit DepositVerified(\n depositDataRoot,\n uint256(deposit.amountGwei) * 1 gwei\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block and beacon block root\n /// of the slot that is associated with the previous block.\n ///\n /// When snapping / verifying balance it is of a high importance that there is no\n /// miss-match in respect to ETH that is held by the contract and balances that are\n /// verified on the validators.\n ///\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\n /// constructing a block on the beacon chain consist of:\n /// - process_withdrawals: ETH is deducted from the validator's balance\n /// - process_execution_payload: immediately after the previous step executing all the\n /// transactions\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\n /// contained in the withdrawal credentials of the exited validators\n ///\n /// That means that balance increases which are part of the post-block execution state are\n /// done within the block, but the transaction that are contained within that block can not\n /// see / interact with the balance from the exited validators. Only transactions in the\n /// next block can do that.\n ///\n /// When snap balances is performed the state of the chain is snapped across 2 separate\n /// chain states:\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\n /// will be referred to as Y - 1 as it makes no difference in the argument\n ///\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\n /// miss-counting ETH or much more dangerous double counting of the ETH.\n ///\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\n /// - ETH balance in the contract on block X\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\n /// - ETH balance in validators that are active in slot Y - 1\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\n /// and have their balance visible to transactions in slot Y and corresponding block X\n /// (or sooner)\n ///\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\n ///\n /// *ETH balance in the contract on block X*\n ///\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\n ///\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\n /// would result in double counting the `stakedEth` since it would be present once in the\n /// snapped contract balance and the second time in deposit storage variables.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\n ///\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\n /// contract at block Z. The execution layer doesn't have direct access to the state of\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\n /// and could also be part of the validator balances. It does that by verifying that at\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\n /// the last snap till now all are still in queue. Which ensures they can not be part of\n /// the validator balances in later steps.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in validators that are active in slot Y - 1*\n ///\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\n /// correctness.\n ///\n /// This behaviour is correct.\n ///\n /// *The withdrawn validators*\n ///\n /// The withdrawn validators could have their balances deducted in any slot before slot\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\n /// look at the \"worst case scenario\" where the validator withdrawal is processed in the\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\n /// even if the validator exits at the latest possible time it is paramount that the ETH\n /// balance on the execution layer is recorded in the next block. Correctly accounting\n /// for the withdrawn ETH.\n ///\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\n /// validator balance verification as well as execution layer contract balance snap.\n ///\n /// This behaviour is correct.\n ///\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n /// Can only be called by the registrator.\n function snapBalances() external onlyRegistrator {\n _snapBalances();\n }\n\n function _snapBalances() internal {\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(\n SafeCast.toUint64(block.timestamp)\n );\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the balances in the mapping\n snappedBalances[blockRoot] = Balances({\n timestamp: SafeCast.toUint64(block.timestamp),\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n // Store the snapped timestamp\n lastSnapTimestamp = SafeCast.toUint64(block.timestamp);\n\n emit BalancesSnapped(block.timestamp, blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct VerifyBalancesParams {\n bytes32 blockRoot;\n uint64 firstPendingDepositSlot;\n // BeaconBlock.BeaconBlockBody.deposits[0].slot\n bytes firstPendingDepositSlotProof;\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n /// @param params a `VerifyBalancesParams` struct containing the following:\n /// blockRoot - the beacon block root emitted from `snapBalance` in `BalancesSnapped`\n /// firstPendingDepositSlot - The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero would be a good choice.\n /// firstPendingDepositSlotProof - The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// balancesContainerRoot - the merkle root of the balances container\n /// balancesContainerProof - The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// validatorBalanceLeaves - Array of leaf nodes containing the validator balance with three other balances.\n /// validatorBalanceProofs - Array of merkle proofs for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(VerifyBalancesParams calldata params) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalances[params.blockRoot];\n // Check the balances are the latest\n require(lastSnapTimestamp > 0, \"No snapped balances\");\n require(balancesMem.timestamp == lastSnapTimestamp, \"Stale snap\");\n\n uint256 depositsCount = depositsRoots.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit to the beacon block root\n bool isEmptyDepositQueue = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDepositSlot(\n params.blockRoot,\n params.firstPendingDepositSlot,\n params.firstPendingDepositSlotProof\n );\n\n // If there are no deposits in the beacon chain queue then our deposits must have been processed.\n // If the deposits have been processed, each deposit will need to be verified with `verifyDeposit`\n require(!isEmptyDepositQueue, \"Deposits have been processed\");\n // If a validator is converted from a sweeping validator to a compounding validator, any balance in excess\n // of the min 32 ETH is put in the pending deposit queue. This will have a slot value of zero unfortunately.\n // We can not prove the strategy's deposits are still pending with a zero slot value so revert the tx.\n // Another snapBalances will need to be taken that does not have consolidation deposits at the front of the\n // beacon chain deposit queue.\n require(\n params.firstPendingDepositSlot > 0,\n \"Invalid first pending deposit\"\n );\n\n // For each staking strategy's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n bytes32 depositDataRoot = depositsRoots[i];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit slot is before the\n // slot of the staking strategy's deposit.\n // If the deposit has been processed, it will need to be verified with `verifyDeposit`\n require(\n params.firstPendingDepositSlot <\n deposits[depositDataRoot].slot,\n \"Deposit has been processed\"\n );\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei +=\n uint256(deposits[depositDataRoot].amountGwei) *\n 1 gwei;\n }\n }\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n params.validatorBalanceProofs.length == verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n params.validatorBalanceLeaves.length == verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n params.blockRoot,\n params.balancesContainerRoot,\n params.balancesContainerProof\n );\n\n // for each validator in reserve order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n params.balancesContainerRoot,\n params.validatorBalanceLeaves[i],\n params.validatorBalanceProofs[i],\n verifiedValidators[i].index\n );\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n validatorState[\n verifiedValidators[i].pubKeyHash\n ] = VALIDATOR_STATE.EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += uint256(validatorBalanceGwei) * 1 gwei;\n }\n }\n\n uint256 wethBalance = IWETH9(WETH).balanceOf(address(this));\n\n // Store the verified balance in storage\n lastVerifiedEthBalance = SafeCast.toUint128(\n totalDepositsWei + totalValidatorBalance + balancesMem.ethBalance\n );\n // Reset the last snap timestamp so a new snapBalances has to be made\n lastSnapTimestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n wethBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /***************************************\n WETH and ETH Accounting\n ****************************************/\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n // The min is required as more WETH can be withdrawn than deposited\n // as the strategy earns consensus and execution rewards.\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance\n if (lastVerifiedEthBalance > _ethAmount) {\n lastVerifiedEthBalance -= SafeCast.toUint128(_ethAmount);\n } else {\n // This can happen if all ETH in the validators was withdrawn\n // and there was more consensus rewards since the last balance verification.\n // Or it can happen if ETH is donated to this strategy.\n lastVerifiedEthBalance = 0;\n }\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += SafeCast.toUint128(_wethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /***************************************\n View Functions\n ****************************************/\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return An array of `ValidatorData` containing the public key hash and validator index.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorData[] memory)\n {\n return verifiedValidators;\n }\n\n struct DepositView {\n bytes32 root;\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n }\n\n /// @notice Returns the deposits that are still to be verified.\n /// These may or may not have been processed by the beacon chain.\n /// @return pendingDeposits An array of `DepositView` containing the deposit root, public key hash,\n /// amount in Gwei and the slot of the deposit.\n function getPendingDeposits()\n external\n view\n returns (DepositView[] memory pendingDeposits)\n {\n uint256 depositsCount = depositsRoots.length;\n pendingDeposits = new DepositView[](depositsCount);\n for (uint256 i = 0; i < depositsCount; ++i) {\n DepositData memory deposit = deposits[depositsRoots[i]];\n pendingDeposits[i] = DepositView({\n root: depositsRoots[i],\n pubKeyHash: deposit.pubKeyHash,\n amountGwei: deposit.amountGwei,\n slot: deposit.slot\n });\n }\n }\n}\n" + }, + "contracts/strategies/NativeStaking/FeeAccumulator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\n/**\n * @title Fee Accumulator for Native Staking SSV Strategy\n * @notice Receives execution rewards which includes tx fees and\n * MEV rewards like tx priority and tx ordering.\n * It does NOT include swept ETH from beacon chain consensus rewards or full validator withdrawals.\n * @author Origin Protocol Inc\n */\ncontract FeeAccumulator {\n /// @notice The address of the Native Staking Strategy\n address public immutable STRATEGY;\n\n event ExecutionRewardsCollected(address indexed strategy, uint256 amount);\n\n /**\n * @param _strategy Address of the Native Staking Strategy\n */\n constructor(address _strategy) {\n STRATEGY = _strategy;\n }\n\n /**\n * @notice sends all ETH in this FeeAccumulator contract to the Native Staking Strategy.\n * @return eth The amount of execution rewards that were sent to the Native Staking Strategy\n */\n function collect() external returns (uint256 eth) {\n require(msg.sender == STRATEGY, \"Caller is not the Strategy\");\n\n eth = address(this).balance;\n if (eth > 0) {\n // Send the ETH to the Native Staking Strategy\n Address.sendValue(payable(STRATEGY), eth);\n\n emit ExecutionRewardsCollected(STRATEGY, eth);\n }\n }\n\n /**\n * @dev Accept ETH\n */\n receive() external payable {}\n}\n" + }, + "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { FeeAccumulator } from \"./FeeAccumulator.sol\";\nimport { ValidatorAccountant } from \"./ValidatorAccountant.sol\";\nimport { ISSVNetwork } from \"../../interfaces/ISSVNetwork.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/// @title Native Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract NativeStakingSSVStrategy is\n ValidatorAccountant,\n InitializableAbstractStrategy\n{\n using SafeERC20 for IERC20;\n\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n /// @notice Fee collector address\n /// @dev this address will receive maximal extractable value (MEV) rewards. These are\n /// rewards for arranging transactions in a way that benefits the validator.\n address payable public immutable FEE_ACCUMULATOR_ADDRESS;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[49] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n /// @param _feeAccumulator Address of the fee accumulator receiving execution layer validator rewards\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n uint256 _maxValidators,\n address _feeAccumulator,\n address _beaconChainDepositContract\n )\n InitializableAbstractStrategy(_baseConfig)\n ValidatorAccountant(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _maxValidators\n )\n {\n SSV_TOKEN = _ssvToken;\n FEE_ACCUMULATOR_ADDRESS = payable(_feeAccumulator);\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract.\n /// @param _rewardTokenAddresses Address of reward token for platform\n /// @param _assets Addresses of initial supported assets\n /// @param _pTokens Platform Token corresponding addresses\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n\n // Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\n FEE_ACCUMULATOR_ADDRESS\n );\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n /// @param _asset Address of asset to deposit. Has to be WETH.\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n depositedWethAccountedFor += _amount;\n _deposit(_asset, _amount);\n }\n\n /// @dev Deposit WETH to this strategy so it can later be staked into a validator.\n /// @param _asset Address of WETH\n /// @param _amount Amount of WETH to deposit\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n /*\n * We could do a check here that would revert when \"_amount % 32 ether != 0\". With the idea of\n * not allowing deposits that will result in WETH sitting on the strategy after all the possible batches\n * of 32ETH have been staked.\n * But someone could mess with our strategy by sending some WETH to it. And we might want to deposit just\n * enough WETH to add it up to 32 so it can be staked. For that reason the check is left out.\n *\n * WETH sitting on the strategy won't interfere with the accounting since accounting only operates on ETH.\n */\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n depositedWethAccountedFor = wethBalance;\n\n _deposit(WETH, newWeth);\n }\n }\n\n /// @notice Withdraw WETH from this contract. Used only if some WETH for is lingering on the contract.\n /// That can happen when:\n /// - after mints if the strategy is the default\n /// - time between depositToStrategy and stakeEth\n /// - the deposit was not a multiple of 32 WETH\n /// - someone sent WETH directly to this contract\n /// Will NOT revert if the strategy is paused from an accounting failure.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n _wethWithdrawn(_amount);\n\n IERC20(_asset).safeTransfer(_recipient, _amount);\n emit Withdrawal(_asset, address(0), _amount);\n }\n\n /// @notice transfer all WETH deposits back to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `exitSsvValidator` and `removeSsvValidator` operations.\n /// This does not withdraw any execution rewards from the FeeAccumulator or\n /// consensus rewards in this strategy.\n /// Any ETH in this strategy that was swept from a full validator withdrawal will not be withdrawn.\n /// ETH from full validator withdrawals is sent to the Vault using `doAccounting`.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n if (wethBalance > 0) {\n _withdraw(vaultAddress, WETH, wethBalance);\n }\n }\n\n /// @notice Returns the total value of (W)ETH that is staked to the validators\n /// and WETH deposits that are still to be staked.\n /// This does not include ETH from consensus rewards sitting in this strategy\n /// or ETH from MEV rewards in the FeeAccumulator. These rewards are harvested\n /// and sent to the Dripper so will eventually be sent to the Vault as WETH.\n /// @param _asset Address of weth asset\n /// @return balance Total value of (W)ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n balance =\n // add the ETH that has been staked in validators\n activeDepositedValidators *\n FULL_STAKE +\n // add the WETH in the strategy from deposits that are still to be staked\n IERC20(WETH).balanceOf(address(this));\n }\n\n function pause() external onlyStrategist {\n _pause();\n }\n\n /// @notice Returns bool indicating whether asset is supported by strategy.\n /// @param _asset The address of the asset token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for deposits\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\n function setFeeRecipient() external {\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\n FEE_ACCUMULATOR_ADDRESS\n );\n }\n\n /**\n * @notice Only accept ETH from the FeeAccumulator and the WETH contract - required when\n * unwrapping WETH just before staking it to the validator.\n * The strategy will also receive ETH from the priority fees of transactions when producing blocks\n * as defined in EIP-1559.\n * The tx fees come from the Beacon chain so do not need any EVM level permissions to receive ETH.\n * The tx fees are paid with each block produced. They are not included in the consensus rewards\n * which are periodically swept from the validators to this strategy.\n * For accounting purposes, the priority fees of transactions will be considered consensus rewards\n * and will be included in the AccountingConsensusRewards event.\n * @dev don't want to receive donations from anyone else as donations over the fuse limits will\n * mess with the accounting of the consensus rewards and validator full withdrawals.\n */\n receive() external payable {\n require(\n msg.sender == FEE_ACCUMULATOR_ADDRESS || msg.sender == WETH,\n \"Eth not from allowed contracts\"\n );\n }\n\n /***************************************\n Internal functions\n ****************************************/\n\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Convert accumulated ETH to WETH and send to the Harvester.\n /// Will revert if the strategy is paused for accounting.\n function _collectRewardTokens() internal override whenNotPaused {\n // collect ETH from execution rewards from the fee accumulator\n uint256 executionRewards = FeeAccumulator(FEE_ACCUMULATOR_ADDRESS)\n .collect();\n\n // total ETH rewards to be harvested = execution rewards + consensus rewards\n uint256 ethRewards = executionRewards + consensusRewards;\n\n require(\n address(this).balance >= ethRewards,\n \"Insufficient eth balance\"\n );\n\n if (ethRewards > 0) {\n // reset the counter keeping track of beacon chain consensus rewards\n consensusRewards = 0;\n\n // Convert ETH rewards to WETH\n IWETH9(WETH).deposit{ value: ethRewards }();\n\n IERC20(WETH).safeTransfer(harvesterAddress, ethRewards);\n emit RewardTokenCollected(harvesterAddress, WETH, ethRewards);\n }\n }\n\n /// @dev emits Withdrawal event from NativeStakingSSVStrategy\n function _wethWithdrawnToVault(uint256 _amount) internal override {\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _wethWithdrawn(uint256 _amount) internal override {\n /* In an ideal world we wouldn't need to reduce the deduction amount when the\n * depositedWethAccountedFor is smaller than the _amount.\n *\n * The reason this is required is that a malicious actor could sent WETH directly\n * to this contract and that would circumvent the increase of depositedWethAccountedFor\n * property. When the ETH would be staked the depositedWethAccountedFor amount could\n * be deducted so much that it would be negative.\n */\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n }\n}\n" + }, + "contracts/strategies/NativeStaking/ValidatorAccountant.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ValidatorRegistrator } from \"./ValidatorRegistrator.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\n\n/// @title Validator Accountant\n/// @notice Attributes the ETH swept from beacon chain validators to this strategy contract\n/// as either full or partial withdrawals. Partial withdrawals being consensus rewards.\n/// Full withdrawals are from exited validators.\n/// @author Origin Protocol Inc\nabstract contract ValidatorAccountant is ValidatorRegistrator {\n /// @notice The minimum amount of blocks that need to pass between two calls to manuallyFixAccounting\n uint256 public constant MIN_FIX_ACCOUNTING_CADENCE = 7200; // 1 day\n\n /// @notice Keeps track of the total consensus rewards swept from the beacon chain\n uint256 public consensusRewards;\n\n /// @notice start of fuse interval\n uint256 public fuseIntervalStart;\n /// @notice end of fuse interval\n uint256 public fuseIntervalEnd;\n /// @notice last block number manuallyFixAccounting has been called\n uint256 public lastFixAccountingBlockNumber;\n\n uint256[49] private __gap;\n\n event FuseIntervalUpdated(uint256 start, uint256 end);\n event AccountingFullyWithdrawnValidator(\n uint256 noOfValidators,\n uint256 remainingValidators,\n uint256 wethSentToVault\n );\n event AccountingValidatorSlashed(\n uint256 remainingValidators,\n uint256 wethSentToVault\n );\n event AccountingConsensusRewards(uint256 amount);\n\n event AccountingManuallyFixed(\n int256 validatorsDelta,\n int256 consensusRewardsDelta,\n uint256 wethToVault\n );\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n uint256 _maxValidators\n )\n ValidatorRegistrator(\n _wethAddress,\n _vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _maxValidators\n )\n {}\n\n /// @notice set fuse interval values\n function setFuseInterval(\n uint256 _fuseIntervalStart,\n uint256 _fuseIntervalEnd\n ) external onlyGovernor {\n require(\n _fuseIntervalStart < _fuseIntervalEnd &&\n _fuseIntervalEnd < 32 ether &&\n _fuseIntervalEnd - _fuseIntervalStart >= 4 ether,\n \"Incorrect fuse interval\"\n );\n\n fuseIntervalStart = _fuseIntervalStart;\n fuseIntervalEnd = _fuseIntervalEnd;\n\n emit FuseIntervalUpdated(_fuseIntervalStart, _fuseIntervalEnd);\n }\n\n /* solhint-disable max-line-length */\n /// This notion page offers a good explanation of how the accounting functions\n /// https://www.notion.so/originprotocol/Limited-simplified-native-staking-accounting-67a217c8420d40678eb943b9da0ee77d\n /// In short, after dividing by 32, if the ETH remaining on the contract falls between 0 and fuseIntervalStart,\n /// the accounting function will treat that ETH as Beacon chain consensus rewards.\n /// On the contrary, if after dividing by 32, the ETH remaining on the contract falls between fuseIntervalEnd and 32,\n /// the accounting function will treat that as a validator slashing.\n /// @notice Perform the accounting attributing beacon chain ETH to either full or partial withdrawals. Returns true when\n /// accounting is valid and fuse isn't \"blown\". Returns false when fuse is blown.\n /// @dev This function could in theory be permission-less but lets allow only the Registrator (Defender Action) to call it\n /// for now.\n /// @return accountingValid true if accounting was successful, false if fuse is blown\n /* solhint-enable max-line-length */\n function doAccounting()\n external\n onlyRegistrator\n whenNotPaused\n nonReentrant\n returns (bool accountingValid)\n {\n // pause the accounting on failure\n accountingValid = _doAccounting(true);\n }\n\n // slither-disable-start reentrancy-eth\n function _doAccounting(bool pauseOnFail)\n internal\n returns (bool accountingValid)\n {\n if (address(this).balance < consensusRewards) {\n return _failAccounting(pauseOnFail);\n }\n\n // Calculate all the new ETH that has been swept to the contract since the last accounting\n uint256 newSweptETH = address(this).balance - consensusRewards;\n accountingValid = true;\n\n // send the ETH that is from fully withdrawn validators to the Vault\n if (newSweptETH >= FULL_STAKE) {\n uint256 fullyWithdrawnValidators;\n // explicitly cast to uint256 as we want to round to a whole number of validators\n fullyWithdrawnValidators = uint256(newSweptETH / FULL_STAKE);\n activeDepositedValidators -= fullyWithdrawnValidators;\n\n uint256 wethToVault = FULL_STAKE * fullyWithdrawnValidators;\n IWETH9(WETH).deposit{ value: wethToVault }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, wethToVault);\n _wethWithdrawnToVault(wethToVault);\n\n emit AccountingFullyWithdrawnValidator(\n fullyWithdrawnValidators,\n activeDepositedValidators,\n wethToVault\n );\n }\n\n uint256 ethRemaining = address(this).balance - consensusRewards;\n // should be less than a whole validator stake\n require(ethRemaining < FULL_STAKE, \"Unexpected accounting\");\n\n // If no Beacon chain consensus rewards swept\n if (ethRemaining == 0) {\n // do nothing\n return accountingValid;\n } else if (ethRemaining < fuseIntervalStart) {\n // Beacon chain consensus rewards swept (partial validator withdrawals)\n // solhint-disable-next-line reentrancy\n consensusRewards += ethRemaining;\n emit AccountingConsensusRewards(ethRemaining);\n } else if (ethRemaining > fuseIntervalEnd) {\n // Beacon chain consensus rewards swept but also a slashed validator fully exited\n IWETH9(WETH).deposit{ value: ethRemaining }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, ethRemaining);\n activeDepositedValidators -= 1;\n\n _wethWithdrawnToVault(ethRemaining);\n\n emit AccountingValidatorSlashed(\n activeDepositedValidators,\n ethRemaining\n );\n }\n // Oh no... Fuse is blown. The Strategist needs to adjust the accounting values.\n else {\n return _failAccounting(pauseOnFail);\n }\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @dev pause any further accounting if required and return false\n function _failAccounting(bool pauseOnFail)\n internal\n returns (bool accountingValid)\n {\n // pause if not already\n if (pauseOnFail) {\n _pause();\n }\n // fail the accounting\n accountingValid = false;\n }\n\n /// @notice Allow the Strategist to fix the accounting of this strategy and unpause.\n /// @param _validatorsDelta adjust the active validators by up to plus three or minus three\n /// @param _consensusRewardsDelta adjust the accounted for consensus rewards up or down\n /// @param _ethToVaultAmount the amount of ETH that gets wrapped into WETH and sent to the Vault\n /// @dev There is a case when a validator(s) gets slashed so much that the eth swept from\n /// the beacon chain enters the fuse area and there are no consensus rewards on the contract\n /// to \"dip into\"/use. To increase the amount of unaccounted ETH over the fuse end interval\n /// we need to reduce the amount of active deposited validators and immediately send WETH\n /// to the vault, so it doesn't interfere with further accounting.\n function manuallyFixAccounting(\n int256 _validatorsDelta,\n int256 _consensusRewardsDelta,\n uint256 _ethToVaultAmount\n ) external onlyStrategist whenPaused nonReentrant {\n require(\n lastFixAccountingBlockNumber + MIN_FIX_ACCOUNTING_CADENCE <\n block.number,\n \"Fix accounting called too soon\"\n );\n require(\n _validatorsDelta >= -3 &&\n _validatorsDelta <= 3 &&\n // new value must be positive\n int256(activeDepositedValidators) + _validatorsDelta >= 0,\n \"Invalid validatorsDelta\"\n );\n require(\n _consensusRewardsDelta >= -332 ether &&\n _consensusRewardsDelta <= 332 ether &&\n // new value must be positive\n int256(consensusRewards) + _consensusRewardsDelta >= 0,\n \"Invalid consensusRewardsDelta\"\n );\n require(_ethToVaultAmount <= 32 ether * 3, \"Invalid wethToVaultAmount\");\n\n activeDepositedValidators = uint256(\n int256(activeDepositedValidators) + _validatorsDelta\n );\n consensusRewards = uint256(\n int256(consensusRewards) + _consensusRewardsDelta\n );\n lastFixAccountingBlockNumber = block.number;\n if (_ethToVaultAmount > 0) {\n IWETH9(WETH).deposit{ value: _ethToVaultAmount }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, _ethToVaultAmount);\n _wethWithdrawnToVault(_ethToVaultAmount);\n }\n\n emit AccountingManuallyFixed(\n _validatorsDelta,\n _consensusRewardsDelta,\n _ethToVaultAmount\n );\n\n // rerun the accounting to see if it has now been fixed.\n // Do not pause the accounting on failure as it is already paused\n require(_doAccounting(false), \"Fuse still blown\");\n\n // unpause since doAccounting was successful\n _unpause();\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n /// @dev allows for NativeStakingSSVStrategy contract to emit the Withdrawal event\n function _wethWithdrawnToVault(uint256 _amount) internal virtual;\n}\n" + }, + "contracts/strategies/NativeStaking/ValidatorRegistrator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Registrator of the validators\n * @notice This contract implements all the required functionality to register, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract ValidatorRegistrator is Governable, Pausable {\n /// @notice The maximum amount of ETH that can be staked by a validator\n /// @dev this can change in the future with EIP-7251, Increase the MAX_EFFECTIVE_BALANCE\n uint256 public constant FULL_STAKE = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Maximum number of validators that can be registered in this strategy\n uint256 public immutable MAX_VALIDATORS;\n\n /// @notice Address of the registrator - allowed to register, exit and remove validators\n address public validatorRegistrator;\n /// @notice The number of validators that have 32 (!) ETH actively deposited. When a new deposit\n /// to a validator happens this number increases, when a validator exit is detected this number\n /// decreases.\n uint256 public activeDepositedValidators;\n /// @notice State of the validators keccak256(pubKey) => state\n mapping(bytes32 => VALIDATOR_STATE) public validatorsStates;\n /// @notice The account that is allowed to modify stakeETHThreshold and reset stakeETHTally\n address public stakingMonitor;\n /// @notice Amount of ETH that can be staked before staking on the contract is suspended\n /// and the `stakingMonitor` needs to approve further staking by calling `resetStakeETHTally`\n uint256 public stakeETHThreshold;\n /// @notice Amount of ETH that has been staked since the `stakingMonitor` last called `resetStakeETHTally`.\n /// This can not go above `stakeETHThreshold`.\n uint256 public stakeETHTally;\n\n // For future use\n uint256[47] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n EXITING, // exit message has been posted and validator is in the process of exiting\n EXIT_COMPLETE // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event ETHStaked(bytes32 indexed pubKeyHash, bytes pubKey, uint256 amount);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event SSVValidatorExitInitiated(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event SSVValidatorExitCompleted(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event StakeETHThresholdChanged(uint256 amount);\n event StakeETHTallyReset();\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(\n msg.sender == validatorRegistrator,\n \"Caller is not the Registrator\"\n );\n _;\n }\n\n /// @dev Throws if called by any account other than the Staking monitor\n modifier onlyStakingMonitor() {\n require(msg.sender == stakingMonitor, \"Caller is not the Monitor\");\n _;\n }\n\n /// @dev Throws if called by any account other than the Strategist\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(VAULT_ADDRESS).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n uint256 _maxValidators\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n MAX_VALIDATORS = _maxValidators;\n }\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Set the address of the staking monitor that is allowed to reset stakeETHTally\n function setStakingMonitor(address _address) external onlyGovernor {\n stakingMonitor = _address;\n emit StakingMonitorChanged(_address);\n }\n\n /// @notice Set the amount of ETH that can be staked before staking monitor\n // needs to a approve further staking by resetting the stake ETH tally\n function setStakeETHThreshold(uint256 _amount) external onlyGovernor {\n stakeETHThreshold = _amount;\n emit StakeETHThresholdChanged(_amount);\n }\n\n /// @notice Reset the stakeETHTally\n function resetStakeETHTally() external onlyStakingMonitor {\n stakeETHTally = 0;\n emit StakeETHTallyReset();\n }\n\n /// @notice Stakes WETH to the node validators\n /// @param validators A list of validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n // slither-disable-start reentrancy-eth\n function stakeEth(ValidatorStakeData[] calldata validators)\n external\n onlyRegistrator\n whenNotPaused\n nonReentrant\n {\n uint256 requiredETH = validators.length * FULL_STAKE;\n\n // Check there is enough WETH from the deposits sitting in this strategy contract\n require(\n requiredETH <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(\n activeDepositedValidators + validators.length <= MAX_VALIDATORS,\n \"Max validators reached\"\n );\n\n require(\n stakeETHTally + requiredETH <= stakeETHThreshold,\n \"Staking ETH over threshold\"\n );\n stakeETHTally += requiredETH;\n\n // Convert required ETH from WETH\n IWETH9(WETH).withdraw(requiredETH);\n _wethWithdrawn(requiredETH);\n\n /* 0x01 to indicate that withdrawal credentials will contain an EOA address that the sweeping function\n * can sweep funds to.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x01),\n bytes11(0),\n address(this)\n );\n\n // For each validator\n for (uint256 i = 0; i < validators.length; ++i) {\n bytes32 pubKeyHash = keccak256(validators[i].pubkey);\n\n require(\n validatorsStates[pubKeyHash] == VALIDATOR_STATE.REGISTERED,\n \"Validator not registered\"\n );\n\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: FULL_STAKE\n }(\n validators[i].pubkey,\n withdrawalCredentials,\n validators[i].signature,\n validators[i].depositDataRoot\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.STAKED;\n\n emit ETHStaked(pubKeyHash, validators[i].pubkey, FULL_STAKE);\n }\n // save gas by changing this storage variable only once rather each time in the loop.\n activeDepositedValidators += validators.length;\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Registers a new validator in the SSV Cluster.\n /// Only the registrator can call this function.\n /// @param publicKeys The public keys of the validators\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for each validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function registerSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n require(\n publicKeys.length == sharesData.length,\n \"Pubkey sharesData mismatch\"\n );\n // Check each public key has not already been used\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n emit SSVValidatorRegistered(pubKeyHash, publicKeys[i], operatorIds);\n }\n\n ISSVNetwork(SSV_NETWORK).bulkRegisterValidator(\n publicKeys,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n }\n\n /// @notice Exit validators from the Beacon chain.\n /// The staked ETH will eventually swept to this native staking strategy.\n /// Only the registrator can call this function.\n /// @param publicKeys List of SSV validator public keys\n /// @param operatorIds The operator IDs of the SSV Cluster\n function exitSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external onlyRegistrator whenNotPaused nonReentrant {\n ISSVNetwork(SSV_NETWORK).bulkExitValidator(publicKeys, operatorIds);\n\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n\n // Check each validator has not already been staked.\n // This would normally be done before the external call but is after\n // so only one for loop of the validators is needed.\n require(\n currentState == VALIDATOR_STATE.STAKED,\n \"Validator not staked\"\n );\n\n // Store the new validator state\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXITING;\n\n emit SSVValidatorExitInitiated(\n pubKeyHash,\n publicKeys[i],\n operatorIds\n );\n }\n }\n\n /// @notice Remove validators from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKeys List of SSV validator public keys\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function removeSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused nonReentrant {\n ISSVNetwork(SSV_NETWORK).bulkRemoveValidator(\n publicKeys,\n operatorIds,\n cluster\n );\n\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n\n // Check each validator is either registered or exited.\n // This would normally be done before the external call but is after\n // so only one for loop of the validators is needed.\n require(\n currentState == VALIDATOR_STATE.EXITING ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exiting\"\n );\n\n // Store the new validator state\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXIT_COMPLETE;\n\n emit SSVValidatorExitCompleted(\n pubKeyHash,\n publicKeys[i],\n operatorIds\n );\n }\n }\n\n /// @notice Deposits more SSV Tokens to the SSV Network contract which is used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// uses \"onlyStrategist\" modifier so continuous front-running can't DOS our maintenance service\n /// that tries to top up SSV tokens.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function depositSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyStrategist {\n ISSVNetwork(SSV_NETWORK).deposit(\n address(this),\n operatorIds,\n ssvAmount,\n cluster\n );\n }\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _wethWithdrawn(uint256 _amount) internal virtual;\n}\n" + }, + "contracts/strategies/plume/RoosterAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Rooster AMO strategy\n * @author Origin Protocol Inc\n * @custom:security-contact security@originprotocol.com\n */\nimport { Math as MathRooster } from \"../../../lib/rooster/v2-common/libraries/Math.sol\";\nimport { Math as Math_v5 } from \"../../../lib/rooster/openzeppelin-custom/contracts/utils/math/Math.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { IMaverickV2Pool } from \"../../interfaces/plume/IMaverickV2Pool.sol\";\nimport { IMaverickV2Quoter } from \"../../interfaces/plume/IMaverickV2Quoter.sol\";\nimport { IMaverickV2LiquidityManager } from \"../../interfaces/plume/IMaverickV2LiquidityManager.sol\";\nimport { IMaverickV2PoolLens } from \"../../interfaces/plume/IMaverickV2PoolLens.sol\";\nimport { IMaverickV2Position } from \"../../interfaces/plume/IMaverickV2Position.sol\";\nimport { IVotingDistributor } from \"../../interfaces/plume/IVotingDistributor.sol\";\nimport { IPoolDistributor } from \"../../interfaces/plume/IPoolDistributor.sol\";\n// importing custom version of rooster TickMath because of dependency collision. Maverick uses\n// a newer OpenZepplin Math library with functionality that is not present in 4.4.2 (the one we use)\nimport { TickMath } from \"../../../lib/rooster/v2-common/libraries/TickMath.sol\";\n\ncontract RoosterAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n using SafeCast for uint256;\n\n /***************************************\n Storage slot members\n ****************************************/\n\n /// @notice NFT tokenId of the liquidity position\n ///\n /// @dev starts with value of 1 and can not be 0\n // solhint-disable-next-line max-line-length\n /// https://github.com/rooster-protocol/rooster-contracts/blob/fbfecbc519e4495b12598024a42630b4a8ea4489/v2-common/contracts/base/Nft.sol#L14\n uint256 public tokenId;\n /// @dev Minimum amount of tokens the strategy would be able to withdraw from the pool.\n /// minimum amount of tokens are withdrawn at a 1:1 price\n /// Important: Underlying assets contains only assets that are deposited in the underlying Rooster pool.\n /// WETH or OETH held by this contract is not accounted for in underlying assets\n uint256 public underlyingAssets;\n /// @notice Marks the start of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareStart;\n /// @notice Marks the end of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareEnd;\n /// @dev reserved for inheritance\n int256[46] private __reserved;\n\n /***************************************\n Constants, structs and events\n ****************************************/\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the OETH token contract\n address public immutable OETH;\n /// @notice the underlying AMO Maverick (Rooster) pool\n IMaverickV2Pool public immutable mPool;\n /// @notice the Liquidity manager used to add liquidity to the pool\n IMaverickV2LiquidityManager public immutable liquidityManager;\n /// @notice the Maverick V2 poolLens\n ///\n /// @dev only used to provide the pool's current sqrtPrice\n IMaverickV2PoolLens public immutable poolLens;\n /// @notice the Maverick V2 position\n ///\n /// @dev provides details of the NFT LP position and offers functions to\n /// remove the liquidity.\n IMaverickV2Position public immutable maverickPosition;\n /// @notice the Maverick Quoter\n IMaverickV2Quoter public immutable quoter;\n /// @notice the Maverick Voting Distributor\n IVotingDistributor public immutable votingDistributor;\n /// @notice the Maverick Pool Distributor\n IPoolDistributor public immutable poolDistributor;\n\n /// @notice sqrtPriceTickLower\n /// @dev tick lower represents the lower price of OETH priced in WETH. Meaning the pool\n /// offers more than 1 OETH for 1 WETH. In other terms to get 1 OETH the swap needs to offer 0.9999 WETH\n /// this is where purchasing OETH with WETH within the liquidity position is the cheapest.\n ///\n /// _____________________\n /// | | |\n /// | WETH | OETH |\n /// | | |\n /// | | |\n /// --------- * ---- * ---------- * ---------\n /// currentPrice\n /// sqrtPriceHigher-(1:1 parity)\n /// sqrtPriceLower\n ///\n ///\n /// Price is defined as price of token1 in terms of token0. (token1 / token0)\n /// @notice sqrtPriceTickLower - OETH is priced 0.9999 WETH\n uint256 public immutable sqrtPriceTickLower;\n /// @notice sqrtPriceTickHigher\n /// @dev tick higher represents 1:1 price parity of WETH to OETH\n uint256 public immutable sqrtPriceTickHigher;\n /// @dev price at parity (in OETH this is equal to sqrtPriceTickHigher)\n uint256 public immutable sqrtPriceAtParity;\n /// @notice The tick where the strategy deploys the liquidity to\n int32 public constant TICK_NUMBER = -1;\n /// @notice Minimum liquidity that must be exceeded to continue with the action\n /// e.g. deposit, add liquidity\n uint256 public constant ACTION_THRESHOLD = 1e12;\n /// @notice Maverick pool static liquidity bin type\n uint8 public constant MAV_STATIC_BIN_KIND = 0;\n /// @dev a threshold under which the contract no longer allows for the protocol to rebalance. Guarding\n /// against a strategist / guardian being taken over and with multiple transactions draining the\n /// protocol funds.\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n /// @notice Emitted when the allowed interval within which the strategy contract is allowed to deposit\n /// liquidity to the underlying pool is updated.\n /// @param allowedWethShareStart The start of the interval\n /// @param allowedWethShareEnd The end of the interval\n event PoolWethShareIntervalUpdated(\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n /// @notice Emitted when liquidity is removed from the underlying pool\n /// @param withdrawLiquidityShare Share of strategy's liquidity that has been removed\n /// @param removedWETHAmount The amount of WETH removed\n /// @param removedOETHAmount The amount of OETH removed\n /// @param underlyingAssets Updated amount of strategy's underlying assets\n event LiquidityRemoved(\n uint256 withdrawLiquidityShare,\n uint256 removedWETHAmount,\n uint256 removedOETHAmount,\n uint256 underlyingAssets\n );\n\n /// @notice Emitted when the underlying pool is rebalanced\n /// @param currentPoolWethShare The resulting share of strategy's liquidity\n /// in the TICK_NUMBER\n event PoolRebalanced(uint256 currentPoolWethShare);\n\n /// @notice Emitted when the amount of underlying assets the strategy hold as\n /// liquidity in the pool is updated.\n /// @param underlyingAssets Updated amount of strategy's underlying assets\n event UnderlyingAssetsUpdated(uint256 underlyingAssets);\n\n /// @notice Emitted when liquidity is added to the underlying pool\n /// @param wethAmountDesired Amount of WETH desired to be deposited\n /// @param oethAmountDesired Amount of OETH desired to be deposited\n /// @param wethAmountSupplied Amount of WETH deposited\n /// @param oethAmountSupplied Amount of OETH deposited\n /// @param tokenId NFT liquidity token id\n /// @param underlyingAssets Updated amount of underlying assets\n event LiquidityAdded(\n uint256 wethAmountDesired,\n uint256 oethAmountDesired,\n uint256 wethAmountSupplied,\n uint256 oethAmountSupplied,\n uint256 tokenId,\n uint256 underlyingAssets\n ); // 0x1530ec74\n\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n ); // 0x3681e8e0\n\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth); // 0x989e5ca8\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth); // 0xa6737d87\n error OutsideExpectedTickRange(); // 0xa6e1bad2\n error SlippageCheck(uint256 tokenReceived); // 0x355cdb78\n\n /// @notice the constructor\n /// @dev This contract is intended to be used as a proxy. To prevent the\n /// potential confusion of having a functional implementation contract\n /// the constructor has the `initializer` modifier. This way the\n /// `initialize` function can not be called on the implementation contract.\n /// For the same reason the implementation contract also has the governor\n /// set to a zero address.\n /// @param _stratConfig the basic strategy configuration\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _oethAddress Address of the Erc20 OETH Token contract\n /// @param _liquidityManager Address of liquidity manager to add\n /// the liquidity\n /// @param _poolLens Address of the pool lens contract\n /// @param _maverickPosition Address of the Maverick's position contract\n /// @param _maverickQuoter Address of the Maverick's Quoter contract\n /// @param _mPool Address of the Rooster concentrated liquidity pool\n /// @param _upperTickAtParity Bool when true upperTick is the one where the\n /// price of OETH and WETH are at parity\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethAddress,\n address _liquidityManager,\n address _poolLens,\n address _maverickPosition,\n address _maverickQuoter,\n address _mPool,\n bool _upperTickAtParity,\n address _votingDistributor,\n address _poolDistributor\n ) initializer InitializableAbstractStrategy(_stratConfig) {\n require(\n address(IMaverickV2Pool(_mPool).tokenA()) == _wethAddress,\n \"WETH not TokenA\"\n );\n require(\n address(IMaverickV2Pool(_mPool).tokenB()) == _oethAddress,\n \"OETH not TokenB\"\n );\n require(\n _liquidityManager != address(0),\n \"LiquidityManager zero address not allowed\"\n );\n require(\n _maverickQuoter != address(0),\n \"Quoter zero address not allowed\"\n );\n require(_poolLens != address(0), \"PoolLens zero address not allowed\");\n require(\n _maverickPosition != address(0),\n \"Position zero address not allowed\"\n );\n require(\n _votingDistributor != address(0),\n \"Voting distributor zero address not allowed\"\n );\n require(\n _poolDistributor != address(0),\n \"Pool distributor zero address not allowed\"\n );\n\n uint256 _tickSpacing = IMaverickV2Pool(_mPool).tickSpacing();\n require(_tickSpacing == 1, \"Unsupported tickSpacing\");\n\n // tickSpacing == 1\n (sqrtPriceTickLower, sqrtPriceTickHigher) = TickMath.tickSqrtPrices(\n _tickSpacing,\n TICK_NUMBER\n );\n sqrtPriceAtParity = _upperTickAtParity\n ? sqrtPriceTickHigher\n : sqrtPriceTickLower;\n\n WETH = _wethAddress;\n OETH = _oethAddress;\n liquidityManager = IMaverickV2LiquidityManager(_liquidityManager);\n poolLens = IMaverickV2PoolLens(_poolLens);\n maverickPosition = IMaverickV2Position(_maverickPosition);\n quoter = IMaverickV2Quoter(_maverickQuoter);\n mPool = IMaverickV2Pool(_mPool);\n votingDistributor = IVotingDistributor(_votingDistributor);\n poolDistributor = IPoolDistributor(_poolDistributor);\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /**\n * @notice initialize function, to set up initial internal state\n */\n function initialize() external onlyGovernor initializer {\n // Read reward\n address[] memory _rewardTokens = new address[](1);\n _rewardTokens[0] = poolDistributor.rewardToken();\n\n require(_rewardTokens[0] != address(0), \"No reward token configured\");\n\n InitializableAbstractStrategy._initialize(\n _rewardTokens,\n new address[](0),\n new address[](0)\n );\n }\n\n /***************************************\n Configuration \n ****************************************/\n\n /**\n * @notice Set allowed pool weth share interval. After the rebalance happens\n * the share of WETH token in the ticker needs to be within the specifications\n * of the interval.\n *\n * @param _allowedWethShareStart Start of WETH share interval expressed as 18 decimal amount\n * @param _allowedWethShareEnd End of WETH share interval expressed as 18 decimal amount\n */\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external onlyGovernor {\n require(\n _allowedWethShareStart < _allowedWethShareEnd,\n \"Invalid interval\"\n );\n // can not go below 1% weth share\n require(_allowedWethShareStart > 0.01 ether, \"Invalid interval start\");\n // can not go above 95% weth share\n require(_allowedWethShareEnd < 0.95 ether, \"Invalid interval end\");\n\n allowedWethShareStart = _allowedWethShareStart;\n allowedWethShareEnd = _allowedWethShareEnd;\n emit PoolWethShareIntervalUpdated(\n _allowedWethShareStart,\n _allowedWethShareEnd\n );\n }\n\n /***************************************\n Strategy overrides \n ****************************************/\n\n /**\n * @notice Deposits funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposits all the funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n _deposit(WETH, _wethBalance);\n }\n\n /**\n * @dev Deposits funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n * Before this function can be called the initial pool position needs to already\n * be minted.\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n require(tokenId > 0, \"Initial position not minted\");\n emit Deposit(_asset, address(0), _amount);\n\n // if the pool price is not within the expected interval leave the WETH on the contract\n // as to not break the mints - in case it would be configured as a default asset strategy\n (bool _isExpectedRange, ) = _checkForExpectedPoolPrice(false);\n if (_isExpectedRange) {\n // deposit funds into the underlying pool. Because no swap is performed there is no\n // need to remove any of the liquidity beforehand.\n _rebalance(0, false, 0, 0);\n }\n }\n\n /**\n * @notice Withdraw an `amount` of WETH from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset WETH address\n * @param _amount Amount of WETH to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n _ensureWETHBalance(_amount);\n\n _withdraw(_recipient, _amount);\n }\n\n /**\n * @notice Withdraw WETH and sends it to the Vault.\n */\n function withdrawAll() external override onlyVault nonReentrant {\n if (tokenId != 0) {\n _removeLiquidity(1e18);\n }\n\n uint256 _balance = IERC20(WETH).balanceOf(address(this));\n if (_balance > 0) {\n _withdraw(vaultAddress, _balance);\n }\n }\n\n function _withdraw(address _recipient, uint256 _amount) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n * @return bool True when the _asset is WETH\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /**\n * @dev Approve the spending amounts for the assets\n */\n function _approveTokenAmounts(\n uint256 _wethAllowance,\n uint256 _oethAllowance\n ) internal {\n IERC20(WETH).approve(address(liquidityManager), _wethAllowance);\n IERC20(OETH).approve(address(liquidityManager), _oethAllowance);\n }\n\n /***************************************\n Liquidity management\n ****************************************/\n /**\n * @dev Add liquidity into the pool in the pre-configured WETH to OETH share ratios\n * defined by the allowedPoolWethShareStart|End interval.\n *\n * Normally a PoolLens contract is used to prepare the parameters to add liquidity to the\n * Rooster pools. It has some errors when doing those calculation and for that reason a\n * much more accurate Quoter contract is used. This is possible due to our requirement of\n * adding liquidity only to one tick - PoolLens supports adding liquidity into multiple ticks\n * using different distribution ratios.\n */\n function _addLiquidity() internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n // don't deposit small liquidity amounts\n if (_wethBalance <= ACTION_THRESHOLD) {\n return;\n }\n\n (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n ) = _getAddLiquidityParams(_wethBalance, 1e30);\n\n if (OETHRequired > _oethBalance) {\n IVault(vaultAddress).mintForStrategy(OETHRequired - _oethBalance);\n }\n\n _approveTokenAmounts(WETHRequired, OETHRequired);\n\n (\n uint256 _wethAmount,\n uint256 _oethAmount,\n uint32[] memory binIds\n ) = liquidityManager.addPositionLiquidityToSenderByTokenIndex(\n mPool,\n 0, // NFT token index\n packedSqrtPriceBreaks,\n packedArgs\n );\n\n require(binIds.length == 1, \"Unexpected binIds length\");\n\n // burn remaining OETH\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n\n // needs to be called after _updateUnderlyingAssets so the updated amount\n // is reflected in the event\n emit LiquidityAdded(\n _wethBalance, // wethAmountDesired\n OETHRequired, // oethAmountDesired\n _wethAmount, // wethAmountSupplied\n _oethAmount, // oethAmountSupplied\n tokenId, // tokenId\n underlyingAssets\n );\n }\n\n /**\n * @dev The function creates liquidity parameters required to be able to add liquidity to the pool.\n * The function needs to handle the 3 different cases of the way liquidity is added:\n * - only WETH present in the tick\n * - only OETH present in the tick\n * - both tokens present in the tick\n *\n */\n function _getAddLiquidityParams(uint256 _maxWETH, uint256 _maxOETH)\n internal\n returns (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n )\n {\n IMaverickV2Pool.AddLiquidityParams[]\n memory addParams = new IMaverickV2Pool.AddLiquidityParams[](1);\n int32[] memory ticks = new int32[](1);\n uint128[] memory amounts = new uint128[](1);\n ticks[0] = TICK_NUMBER;\n // arbitrary LP amount\n amounts[0] = 1e24;\n\n // construct value for Quoter with arbitrary LP amount\n IMaverickV2Pool.AddLiquidityParams memory addParam = IMaverickV2Pool\n .AddLiquidityParams({\n kind: MAV_STATIC_BIN_KIND,\n ticks: ticks,\n amounts: amounts\n });\n\n // get the WETH and OETH required to get the proportion of tokens required\n // given the arbitrary liquidity\n (WETHRequired, OETHRequired, ) = quoter.calculateAddLiquidity(\n mPool,\n addParam\n );\n\n /**\n * If either token required is 0 then the tick consists only of the other token. In that\n * case the liquidity calculations need to be done using the non 0 token. By setting the\n * tokenRequired from 0 to 1 the `min` in next step will ignore that (the bigger) value.\n */\n WETHRequired = WETHRequired == 0 ? 1 : WETHRequired;\n OETHRequired = OETHRequired == 0 ? 1 : OETHRequired;\n\n addParam.amounts[0] = Math_v5\n .min(\n ((_maxWETH - 1) * 1e24) / WETHRequired,\n ((_maxOETH - 1) * 1e24) / OETHRequired\n )\n .toUint128();\n\n // update the quotes with the actual amounts\n (WETHRequired, OETHRequired, ) = quoter.calculateAddLiquidity(\n mPool,\n addParam\n );\n\n require(_maxWETH >= WETHRequired, \"More WETH required than specified\");\n require(_maxOETH >= OETHRequired, \"More OETH required than specified\");\n\n // organize values to be used by manager\n addParams[0] = addParam;\n packedArgs = liquidityManager.packAddLiquidityArgsArray(addParams);\n // price can stay 0 if array only has one element\n packedSqrtPriceBreaks = liquidityManager.packUint88Array(\n new uint88[](1)\n );\n }\n\n /**\n * @dev Check that the Rooster pool price is within the expected\n * parameters.\n * This function works whether the strategy contract has liquidity\n * position in the pool or not. The function returns _wethSharePct\n * as a gas optimization measure.\n * @param _throwException when set to true the function throws an exception\n * when pool's price is not within expected range.\n * @return _isExpectedRange Bool expressing price is within expected range\n * @return _wethSharePct Share of WETH owned by this strategy contract in the\n * configured ticker.\n */\n function _checkForExpectedPoolPrice(bool _throwException)\n internal\n view\n returns (bool _isExpectedRange, uint256 _wethSharePct)\n {\n require(\n allowedWethShareStart != 0 && allowedWethShareEnd != 0,\n \"Weth share interval not set\"\n );\n\n uint256 _currentPrice = getPoolSqrtPrice();\n\n /**\n * First check pool price is in expected tick range\n *\n * A revert is issued even though price being equal to the lower bound as that can not\n * be within the approved tick range.\n */\n if (\n _currentPrice <= sqrtPriceTickLower ||\n _currentPrice >= sqrtPriceTickHigher\n ) {\n if (_throwException) {\n revert OutsideExpectedTickRange();\n }\n\n return (false, _currentPrice <= sqrtPriceTickLower ? 0 : 1e18);\n }\n\n // 18 decimal number expressed WETH tick share\n _wethSharePct = _getWethShare(_currentPrice);\n\n if (\n _wethSharePct < allowedWethShareStart ||\n _wethSharePct > allowedWethShareEnd\n ) {\n if (_throwException) {\n revert PoolRebalanceOutOfBounds(\n _wethSharePct,\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n return (false, _wethSharePct);\n }\n\n return (true, _wethSharePct);\n }\n\n /**\n * @notice Rebalance the pool to the desired token split and Deposit any WETH on the contract to the\n * underlying rooster pool. Print the required amount of corresponding OETH. After the rebalancing is\n * done burn any potentially remaining OETH tokens still on the strategy contract.\n *\n * This function has a slightly different behaviour depending on the status of the underlying Rooster\n * pool. The function consists of the following 3 steps:\n * 1. withdrawLiquidityOption -> this is a configurable option where either only part of the liquidity\n * necessary for the swap is removed, or all of it. This way the rebalance\n * is able to optimize for volume, for efficiency or anything in between\n * 2. swapToDesiredPosition -> move active trading price in the pool to be able to deposit WETH & OETH\n * tokens with the desired pre-configured ratios\n * 3. addLiquidity -> add liquidity into the pool respecting ratio split configuration\n *\n *\n * Exact _amountToSwap, _swapWeth & _minTokenReceived parameters shall be determined by simulating the\n * transaction off-chain. The strategy checks that after the swap the share of the tokens is in the\n * expected ranges.\n *\n * @param _amountToSwap The amount of the token to swap\n * @param _swapWeth Swap using WETH when true, use OETH when false\n * @param _minTokenReceived Slippage check -> minimum amount of token expected in return\n * @param _liquidityToRemovePct Percentage of liquidity to remove -> the percentage amount of liquidity to\n * remove before performing the swap. 1e18 denominated\n */\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived,\n uint256 _liquidityToRemovePct\n ) external nonReentrant onlyGovernorOrStrategist {\n _rebalance(\n _amountToSwap,\n _swapWeth,\n _minTokenReceived,\n _liquidityToRemovePct\n );\n }\n\n // slither-disable-start reentrancy-no-eth\n function _rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived,\n uint256 _liquidityToRemovePct\n ) internal {\n // Remove the required amount of liquidity\n if (_liquidityToRemovePct > 0) {\n _removeLiquidity(_liquidityToRemovePct);\n }\n\n // in some cases (e.g. deposits) we will just want to add liquidity and not\n // issue a swap to move the active trading position within the pool. Before or after a\n // deposit or as a standalone call the strategist might issue a rebalance to move the\n // active trading price to a more desired position.\n if (_amountToSwap > 0) {\n // In case liquidity has been removed and there is still not enough WETH owned by the\n // strategy contract remove additional required amount of WETH.\n if (_swapWeth) _ensureWETHBalance(_amountToSwap);\n\n _swapToDesiredPosition(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n\n // calling check liquidity early so we don't get unexpected errors when adding liquidity\n // in the later stages of this function\n _checkForExpectedPoolPrice(true);\n\n _addLiquidity();\n\n // this call shouldn't be necessary, since adding liquidity shouldn't affect the active\n // trading price. It is a defensive programming measure.\n (, uint256 _wethSharePct) = _checkForExpectedPoolPrice(true);\n\n // revert if protocol insolvent\n _solvencyAssert();\n\n emit PoolRebalanced(_wethSharePct);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @dev Perform a swap so that after the swap the tick has the desired WETH to OETH token share.\n */\n function _swapToDesiredPosition(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n IERC20 _tokenToSwap = IERC20(_swapWeth ? WETH : OETH);\n uint256 _balance = _tokenToSwap.balanceOf(address(this));\n\n if (_balance < _amountToSwap) {\n // This should never trigger since _ensureWETHBalance will already\n // throw an error if there is not enough WETH\n if (_swapWeth) {\n revert NotEnoughWethForSwap(_balance, _amountToSwap);\n }\n // if swapping OETH\n uint256 mintForSwap = _amountToSwap - _balance;\n IVault(vaultAddress).mintForStrategy(mintForSwap);\n }\n\n // SafeERC20 is used for IERC20 transfers. Not sure why slither complains\n // slither-disable-next-line unchecked-transfer\n _tokenToSwap.transfer(address(mPool), _amountToSwap);\n\n // tickLimit: the furthest tick a swap will execute in. If no limit is desired,\n // value should be set to type(int32).max for a tokenAIn (WETH) swap\n // and type(int32).min for a swap where tokenB (OETH) is the input\n\n IMaverickV2Pool.SwapParams memory swapParams = IMaverickV2Pool\n // exactOutput defines whether the amount specified is the output\n // or the input amount of the swap\n .SwapParams({\n amount: _amountToSwap,\n tokenAIn: _swapWeth,\n exactOutput: false,\n tickLimit: TICK_NUMBER\n });\n\n // swaps without a callback as the assets are already sent to the pool\n (, uint256 amountOut) = mPool.swap(\n address(this),\n swapParams,\n bytes(\"\")\n );\n\n /**\n * There could be additional checks here for validating minTokenReceived is within the\n * expected range (e.g. 99% - 101% of the token sent in). Though that doesn't provide\n * any additional security. After the swap the `_checkForExpectedPoolPrice` validates\n * that the swap has moved the price into the expected tick (# -1).\n *\n * If the guardian forgets to set a `_minTokenReceived` and a sandwich attack bends\n * the pool before the swap the `_checkForExpectedPoolPrice` will fail the transaction.\n *\n * A check would not prevent a compromised guardian from stealing funds as multiple\n * transactions each loosing smaller amount of funds are still possible.\n */\n if (amountOut < _minTokenReceived) {\n revert SlippageCheck(amountOut);\n }\n\n /**\n * In the interest of each function in `_rebalance` to leave the contract state as\n * clean as possible the OETH tokens here are burned. This decreases the\n * dependence where `_swapToDesiredPosition` function relies on later functions\n * (`addLiquidity`) to burn the OETH. Reducing the risk of error introduction.\n */\n _burnOethOnTheContract();\n }\n\n /**\n * @dev This function removes the appropriate amount of liquidity to ensure that the required\n * amount of WETH is available on the contract\n *\n * @param _amount WETH balance required on the contract\n */\n function _ensureWETHBalance(uint256 _amount) internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance >= _amount) {\n return;\n }\n\n require(tokenId != 0, \"No liquidity available\");\n uint256 _additionalWethRequired = _amount - _wethBalance;\n (uint256 _wethInThePool, ) = getPositionPrincipal();\n\n if (_wethInThePool < _additionalWethRequired) {\n revert NotEnoughWethLiquidity(\n _wethInThePool,\n _additionalWethRequired\n );\n }\n\n uint256 shareOfWethToRemove = _wethInThePool <= 1\n ? 1e18\n : Math_v5.min(\n /**\n * When dealing with shares of liquidity to remove there is always some\n * rounding involved. After extensive fuzz testing the below approach\n * yielded the best results where the strategy overdraws the least and\n * never removes insufficient amount of WETH.\n */\n (_additionalWethRequired + 2).divPrecisely(_wethInThePool - 1) +\n 2,\n 1e18\n );\n\n _removeLiquidity(shareOfWethToRemove);\n }\n\n /**\n * @dev Decrease partial or all liquidity from the pool.\n * @param _liquidityToDecrease The amount of liquidity to remove denominated in 1e18\n */\n function _removeLiquidity(uint256 _liquidityToDecrease) internal {\n require(_liquidityToDecrease > 0, \"Must remove some liquidity\");\n require(\n _liquidityToDecrease <= 1e18,\n \"Can not remove more than 100% of liquidity\"\n );\n\n // 0 indicates the first (and only) bin in the NFT LP position.\n IMaverickV2Pool.RemoveLiquidityParams memory params = maverickPosition\n .getRemoveParams(tokenId, 0, _liquidityToDecrease);\n (uint256 _amountWeth, uint256 _amountOeth) = maverickPosition\n .removeLiquidityToSender(tokenId, mPool, params);\n\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n\n // needs to be called after the _updateUnderlyingAssets so the updated amount is reflected\n // in the event\n emit LiquidityRemoved(\n _liquidityToDecrease,\n _amountWeth,\n _amountOeth,\n underlyingAssets\n );\n }\n\n /**\n * @dev Burns any OETH tokens remaining on the strategy contract if the balance is\n * above the action threshold.\n */\n function _burnOethOnTheContract() internal {\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(_oethBalance);\n }\n\n /**\n * @notice Returns the percentage of WETH liquidity in the configured ticker\n * owned by this strategy contract.\n * @return uint256 1e18 denominated percentage expressing the share\n */\n function getWETHShare() external view returns (uint256) {\n uint256 _currentPrice = getPoolSqrtPrice();\n return _getWethShare(_currentPrice);\n }\n\n /**\n * @dev Returns the share of WETH in tick denominated in 1e18\n */\n function _getWethShare(uint256 _currentPrice)\n internal\n view\n returns (uint256)\n {\n (\n uint256 wethAmount,\n uint256 oethAmount\n ) = _reservesInTickForGivenPriceAndLiquidity(\n sqrtPriceTickLower,\n sqrtPriceTickHigher,\n _currentPrice,\n 1e24\n );\n\n return wethAmount.divPrecisely(wethAmount + oethAmount);\n }\n\n /**\n * @notice Returns the current pool price in square root\n * @return Square root of the pool price\n */\n function getPoolSqrtPrice() public view returns (uint256) {\n return poolLens.getPoolSqrtPrice(mPool);\n }\n\n /**\n * @notice Returns the current active trading tick of the underlying pool\n * @return _currentTick Current pool trading tick\n */\n function getCurrentTradingTick() public view returns (int32 _currentTick) {\n _currentTick = mPool.getState().activeTick;\n }\n\n /**\n * @notice Mint the initial NFT position\n *\n * @dev This amount is \"gifted\" to the strategy contract and will count as a yield\n * surplus.\n */\n // slither-disable-start reentrancy-no-eth\n function mintInitialPosition() external onlyGovernor nonReentrant {\n require(tokenId == 0, \"Initial position already minted\");\n (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n ) = _getAddLiquidityParams(1e16, 1e16);\n\n // Mint rounded up OETH amount\n if (OETHRequired > 0) {\n IVault(vaultAddress).mintForStrategy(OETHRequired);\n }\n\n _approveTokenAmounts(WETHRequired, OETHRequired);\n\n // Store the tokenId before calling updateUnderlyingAssets as it relies on the tokenId\n // not being 0\n (, , , tokenId) = liquidityManager.mintPositionNftToSender(\n mPool,\n packedSqrtPriceBreaks,\n packedArgs\n );\n\n // burn remaining OETH\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @notice Returns the balance of tokens the strategy holds in the LP position\n * @return _amountWeth Amount of WETH in position\n * @return _amountOeth Amount of OETH in position\n */\n function getPositionPrincipal()\n public\n view\n returns (uint256 _amountWeth, uint256 _amountOeth)\n {\n if (tokenId == 0) {\n return (0, 0);\n }\n\n (_amountWeth, _amountOeth, ) = _getPositionInformation();\n }\n\n /**\n * @dev Returns the balance of tokens the strategy holds in the LP position\n * @return _amountWeth Amount of WETH in position\n * @return _amountOeth Amount of OETH in position\n * @return liquidity Amount of liquidity in the position\n */\n function _getPositionInformation()\n internal\n view\n returns (\n uint256 _amountWeth,\n uint256 _amountOeth,\n uint256 liquidity\n )\n {\n IMaverickV2Position.PositionFullInformation\n memory positionInfo = maverickPosition.tokenIdPositionInformation(\n tokenId,\n 0\n );\n\n require(\n positionInfo.liquidities.length == 1,\n \"Unexpected liquidities length\"\n );\n require(positionInfo.ticks.length == 1, \"Unexpected ticks length\");\n\n _amountWeth = positionInfo.amountA;\n _amountOeth = positionInfo.amountB;\n liquidity = positionInfo.liquidities[0];\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99.8%) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethSupply = IERC20(OETH).totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethSupply) < SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /**\n * @dev Collect Rooster reward token, and send it to the harvesterAddress\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Do nothing if there's no position minted\n if (tokenId > 0) {\n uint32[] memory binIds = new uint32[](1);\n IMaverickV2Pool.TickState memory tickState = mPool.getTick(\n TICK_NUMBER\n );\n // get the binId for the MAV_STATIC_BIN_KIND in tick TICK_NUMBER (-1)\n binIds[0] = tickState.binIdsByTick[0];\n\n uint256 lastEpoch = votingDistributor.lastEpoch();\n\n poolDistributor.claimLp(\n address(this),\n tokenId,\n mPool,\n binIds,\n lastEpoch\n );\n }\n\n // Run the internal inherited function\n _collectRewardTokens();\n }\n\n /***************************************\n Balances and Fees\n ****************************************/\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256)\n {\n require(_asset == WETH, \"Only WETH supported\");\n\n // because of PoolLens inaccuracy there is usually some dust WETH left on the contract\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n // just paranoia check, in case there is OETH in the strategy that for some reason hasn't\n // been burned yet. This should always be 0.\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n return underlyingAssets + _wethBalance + _oethBalance;\n }\n\n /// @dev This function updates the amount of underlying assets with the approach of the least possible\n /// total tokens extracted for the current liquidity in the pool.\n function _updateUnderlyingAssets() internal {\n /**\n * Our net value represent the smallest amount of tokens we are able to extract from the position\n * given our liquidity.\n *\n * The least amount of tokens ex-tractable from the position is where the active trading price is\n * at the edge between tick -1 & tick 0. There the pool is offering 1:1 trades between WETH & OETH.\n * At that moment the pool consists completely of WETH and no OETH.\n *\n * The more swaps from OETH -> WETH happen on the pool the more the price starts to move away from the tick 0\n * towards the middle of tick -1 making OETH (priced in WETH) cheaper.\n */\n\n uint256 _wethAmount = tokenId == 0 ? 0 : _balanceInPosition();\n\n underlyingAssets = _wethAmount;\n emit UnderlyingAssetsUpdated(_wethAmount);\n }\n\n /**\n * @dev Strategy reserves (which consist only of WETH in case of Rooster - Plume pool)\n * when the tick price is closest to parity - assuring the lowest amount of tokens\n * returned for the current position liquidity.\n */\n function _balanceInPosition() internal view returns (uint256 _wethBalance) {\n (, , uint256 liquidity) = _getPositionInformation();\n\n uint256 _oethBalance;\n\n (_wethBalance, _oethBalance) = _reservesInTickForGivenPriceAndLiquidity(\n sqrtPriceTickLower,\n sqrtPriceTickHigher,\n sqrtPriceAtParity,\n liquidity\n );\n\n require(_oethBalance == 0, \"Non zero oethBalance\");\n }\n\n /**\n * @notice Tick dominance denominated in 1e18\n * @return _tickDominance The share of liquidity in TICK_NUMBER tick owned\n * by the strategy contract denominated in 1e18\n */\n function tickDominance() public view returns (uint256 _tickDominance) {\n IMaverickV2Pool.TickState memory tickState = mPool.getTick(TICK_NUMBER);\n\n uint256 wethReserve = tickState.reserveA;\n uint256 oethReserve = tickState.reserveB;\n\n // prettier-ignore\n (uint256 _amountWeth, uint256 _amountOeth, ) = _getPositionInformation();\n\n if (wethReserve + oethReserve == 0) {\n return 0;\n }\n\n _tickDominance = (_amountWeth + _amountOeth).divPrecisely(\n wethReserve + oethReserve\n );\n }\n\n /***************************************\n Hidden functions\n ****************************************/\n /**\n * @dev Unsupported\n */\n function setPTokenAddress(address, address) external pure override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function removePToken(uint256) external pure override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function _abstractSetPToken(address, address) internal pure override {\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function safeApproveAllTokens() external pure override {\n // all the amounts are approved at the time required\n revert(\"Unsupported method\");\n }\n\n /***************************************\n Maverick liquidity utilities\n ****************************************/\n\n /// @notice Calculates deltaA = liquidity * (sqrt(upper) - sqrt(lower))\n /// Calculates deltaB = liquidity / sqrt(lower) - liquidity / sqrt(upper),\n /// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower))\n ///\n /// @dev refactored from here:\n // solhint-disable-next-line max-line-length\n /// https://github.com/rooster-protocol/rooster-contracts/blob/main/v2-supplemental/contracts/libraries/LiquidityUtilities.sol#L665-L695\n function _reservesInTickForGivenPriceAndLiquidity(\n uint256 _lowerSqrtPrice,\n uint256 _upperSqrtPrice,\n uint256 _newSqrtPrice,\n uint256 _liquidity\n ) internal pure returns (uint128 reserveA, uint128 reserveB) {\n if (_liquidity == 0) {\n (reserveA, reserveB) = (0, 0);\n } else {\n uint256 lowerEdge = MathRooster.max(_lowerSqrtPrice, _newSqrtPrice);\n\n reserveA = MathRooster\n .mulCeil(\n _liquidity,\n MathRooster.clip(\n MathRooster.min(_upperSqrtPrice, _newSqrtPrice),\n _lowerSqrtPrice\n )\n )\n .toUint128();\n reserveB = MathRooster\n .mulDivCeil(\n _liquidity,\n 1e18 * MathRooster.clip(_upperSqrtPrice, lowerEdge),\n _upperSqrtPrice * lowerEdge\n )\n .toUint128();\n }\n }\n}\n" + }, + "contracts/strategies/sonic/SonicStakingStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SonicValidatorDelegator } from \"./SonicValidatorDelegator.sol\";\nimport { IWrappedSonic } from \"../../interfaces/sonic/IWrappedSonic.sol\";\n\n/**\n * @title Staking Strategy for Sonic's native S currency\n * @author Origin Protocol Inc\n */\ncontract SonicStakingStrategy is SonicValidatorDelegator {\n // For future use\n uint256[50] private __gap;\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wrappedSonic,\n address _sfc\n ) SonicValidatorDelegator(_baseConfig, _wrappedSonic, _sfc) {}\n\n /// @notice Deposit wrapped S asset into the underlying platform.\n /// @param _asset Address of asset to deposit. Has to be Wrapped Sonic (wS).\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposit Wrapped Sonic (wS) to this strategy and delegate to a validator.\n * @param _asset Address of Wrapped Sonic (wS) token\n * @param _amount Amount of Wrapped Sonic (wS) to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal virtual {\n require(_amount > 0, \"Must deposit something\");\n\n _delegate(_amount);\n emit Deposit(_asset, address(0), _amount);\n }\n\n /**\n * @notice Deposit the entire balance of wrapped S in this strategy contract into\n * the underlying platform.\n */\n function depositAll() external virtual override onlyVault nonReentrant {\n uint256 wSBalance = IERC20(wrappedSonic).balanceOf(address(this));\n\n if (wSBalance > 0) {\n _deposit(wrappedSonic, wSBalance);\n }\n }\n\n /// @notice Withdraw Wrapped Sonic (wS) from this strategy contract.\n /// Used only if some wS is lingering on the contract.\n /// That can happen only when someone sends wS directly to this contract\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset Address of the Wrapped Sonic (wS) token\n /// @param _amount Amount of Wrapped Sonic (wS) to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal override {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(_asset).transfer(_recipient, _amount);\n\n emit Withdrawal(wrappedSonic, address(0), _amount);\n }\n\n /// @notice Transfer all Wrapped Sonic (wS) deposits back to the vault.\n /// This does not withdraw from delegated validators. That has to be done separately with `undelegate`.\n /// Any native S in this strategy will be withdrawn.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 balance = address(this).balance;\n if (balance > 0) {\n IWrappedSonic(wrappedSonic).deposit{ value: balance }();\n }\n uint256 wSBalance = IERC20(wrappedSonic).balanceOf(address(this));\n if (wSBalance > 0) {\n _withdraw(vaultAddress, wrappedSonic, wSBalance);\n }\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset token\n */\n function supportsAsset(address _asset)\n public\n view\n virtual\n override\n returns (bool)\n {\n return _asset == wrappedSonic;\n }\n\n /**\n * @notice is not supported for this strategy as the\n * Wrapped Sonic (wS) token is set at deploy time.\n */\n function setPTokenAddress(address, address)\n external\n view\n override\n onlyGovernor\n {\n revert(\"unsupported function\");\n }\n\n /// @notice is not used by this strategy as all staking rewards are restaked\n function collectRewardTokens() external override nonReentrant {\n revert(\"unsupported function\");\n }\n\n /**\n * @notice is not supported for this strategy as the\n * Wrapped Sonic (wS) token is set at deploy time.\n */\n function removePToken(uint256) external view override onlyGovernor {\n revert(\"unsupported function\");\n }\n\n /// @dev is not used by this strategy but must be implemented as it's abstract\n /// in the inherited `InitializableAbstractStrategy` contract.\n function _abstractSetPToken(address, address) internal virtual override {}\n\n /// @notice is not used by this strategy\n function safeApproveAllTokens() external override onlyGovernor {}\n}\n" + }, + "contracts/strategies/sonic/SonicSwapXAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title SwapX Algorithmic Market Maker (AMO) Strategy\n * @notice AMO strategy for the SwapX OS/wS stable pool\n * @author Origin Protocol Inc\n */\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport { sqrt } from \"../../utils/PRBMath.sol\";\nimport { IBasicToken } from \"../../interfaces/IBasicToken.sol\";\nimport { IPair } from \"../../interfaces/sonic/ISwapXPair.sol\";\nimport { IGauge } from \"../../interfaces/sonic/ISwapXGauge.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\n\ncontract SonicSwapXAMOStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @notice a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n /// @notice Precision for the SwapX Stable AMM (sAMM) invariant k.\n uint256 public constant PRECISION = 1e18;\n\n /// @notice Address of the Wrapped S (wS) token.\n address public immutable ws;\n\n /// @notice Address of the OS token contract.\n address public immutable os;\n\n /// @notice Address of the SwapX Stable pool contract.\n address public immutable pool;\n\n /// @notice Address of the SwapX Gauge contract.\n address public immutable gauge;\n\n /// @notice The max amount the OS/wS price can deviate from peg (1e18)\n /// before deposits are reverted scaled to 18 decimals.\n /// eg 0.01e18 or 1e16 is 1% which is 100 basis points.\n /// This is the amount below and above peg so a 50 basis point deviation (0.005e18)\n /// allows a price range from 0.995 to 1.005.\n uint256 public maxDepeg;\n\n event SwapOTokensToPool(\n uint256 osMinted,\n uint256 wsDepositAmount,\n uint256 osDepositAmount,\n uint256 lpTokens\n );\n event SwapAssetsToPool(\n uint256 wsSwapped,\n uint256 lpTokens,\n uint256 osBurnt\n );\n event MaxDepegUpdated(uint256 maxDepeg);\n\n /**\n * @dev Verifies that the caller is the Strategist of the Vault.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Skim the SwapX pool in case any extra wS or OS tokens were added\n */\n modifier skimPool() {\n IPair(pool).skim(address(this));\n _;\n }\n\n /**\n * @dev Checks the pool is balanced enough to allow deposits.\n */\n modifier nearBalancedPool() {\n // OS/wS price = wS / OS\n // Get the OS/wS price for selling 1 OS for wS\n // As OS is 1, the wS amount is the OS/wS price\n uint256 sellPrice = IPair(pool).getAmountOut(1e18, os);\n\n // Get the amount of OS received from selling 1 wS. This is buying OS.\n uint256 osAmount = IPair(pool).getAmountOut(1e18, ws);\n // Convert to a OS/wS price = wS / OS\n uint256 buyPrice = 1e36 / osAmount;\n\n uint256 pegPrice = 1e18;\n\n require(\n sellPrice >= pegPrice - maxDepeg && buyPrice <= pegPrice + maxDepeg,\n \"price out of range\"\n );\n _;\n }\n\n /**\n * @dev Checks the pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do swaps against the pool.\n * Deposits and withdrawals are proportional to the pool's balances hence don't need this check.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the pool\n (uint256 wsReservesBefore, uint256 osReservesBefore, ) = IPair(pool)\n .getReserves();\n // diff = wS balance - OS balance\n int256 diffBefore = wsReservesBefore.toInt256() -\n osReservesBefore.toInt256();\n\n _;\n\n // Get the asset and OToken balances in the pool\n (uint256 wsReservesAfter, uint256 osReservesAfter, ) = IPair(pool)\n .getReserves();\n // diff = wS balance - OS balance\n int256 diffAfter = wsReservesAfter.toInt256() -\n osReservesAfter.toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OS, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"Assets overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of wS, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"OTokens overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n /**\n * @param _baseConfig The `platformAddress` is the address of the SwapX pool.\n * The `vaultAddress` is the address of the Origin Sonic Vault.\n * @param _os Address of the OS token.\n * @param _ws Address of the Wrapped S (wS) token.\n * @param _gauge Address of the SwapX gauge for the pool.\n */\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _os,\n address _ws,\n address _gauge\n ) InitializableAbstractStrategy(_baseConfig) {\n // Check the pool tokens are correct\n require(\n IPair(_baseConfig.platformAddress).token0() == _ws &&\n IPair(_baseConfig.platformAddress).token1() == _os,\n \"Incorrect pool tokens\"\n );\n // Checked both tokens are to 18 decimals\n require(\n IBasicToken(_ws).decimals() == 18 &&\n IBasicToken(_os).decimals() == 18,\n \"Incorrect token decimals\"\n );\n // Check the SwapX pool is a Stable AMM (sAMM)\n require(\n IPair(_baseConfig.platformAddress).isStable() == true,\n \"Pool not stable\"\n );\n // Check the gauge is for the pool\n require(\n IGauge(_gauge).TOKEN() == _baseConfig.platformAddress,\n \"Incorrect gauge\"\n );\n\n // Set the immutable variables\n os = _os;\n ws = _ws;\n pool = _baseConfig.platformAddress;\n gauge = _gauge;\n\n // This is an implementation contract. The governor is set in the proxy contract.\n _setGovernor(address(0));\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as SwapX strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Array containing SWPx token address\n * @param _maxDepeg The max amount the OS/wS price can deviate from peg (1e18) before deposits are reverted.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n uint256 _maxDepeg\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = pool;\n\n address[] memory _assets = new address[](1);\n _assets[0] = ws;\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n maxDepeg = _maxDepeg;\n\n _approveBase();\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit an amount of Wrapped S (wS) into the SwapX pool.\n * Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @dev This tx must be wrapped by the VaultValueChecker.\n * To minimize loses, the pool should be rebalanced before depositing.\n * The pool's OS/wS price must be within the maxDepeg range.\n * @param _asset Address of Wrapped S (wS) token.\n * @param _wsAmount Amount of Wrapped S (wS) tokens to deposit.\n */\n function deposit(address _asset, uint256 _wsAmount)\n external\n override\n onlyVault\n nonReentrant\n skimPool\n nearBalancedPool\n {\n require(_asset == ws, \"Unsupported asset\");\n require(_wsAmount > 0, \"Must deposit something\");\n\n (uint256 osDepositAmount, ) = _deposit(_wsAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the deposited wS tokens\n emit Deposit(ws, pool, _wsAmount);\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osDepositAmount);\n }\n\n /**\n * @notice Deposit all the strategy's Wrapped S (wS) tokens into the SwapX pool.\n * Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @dev This tx must be wrapped by the VaultValueChecker.\n * To minimize loses, the pool should be rebalanced before depositing.\n * The pool's OS/wS price must be within the maxDepeg range.\n */\n function depositAll()\n external\n override\n onlyVault\n nonReentrant\n skimPool\n nearBalancedPool\n {\n uint256 wsBalance = IERC20(ws).balanceOf(address(this));\n if (wsBalance > 0) {\n (uint256 osDepositAmount, ) = _deposit(wsBalance);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the deposited wS tokens\n emit Deposit(ws, pool, wsBalance);\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osDepositAmount);\n }\n }\n\n /**\n * @dev Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @param _wsAmount Amount of Wrapped S (wS) tokens to deposit.\n * @return osDepositAmount Amount of OS tokens minted and deposited into the pool.\n * @return lpTokens Amount of SwapX pool LP tokens minted and deposited into the gauge.\n */\n function _deposit(uint256 _wsAmount)\n internal\n returns (uint256 osDepositAmount, uint256 lpTokens)\n {\n // Calculate the required amount of OS to mint based on the wS amount.\n osDepositAmount = _calcTokensToMint(_wsAmount);\n\n // Mint the required OS tokens to this strategy\n IVault(vaultAddress).mintForStrategy(osDepositAmount);\n\n // Add wS and OS liquidity to the pool and stake in gauge\n lpTokens = _depositToPoolAndGauge(_wsAmount, osDepositAmount);\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw wS and OS from the SwapX pool, burn the OS,\n * and transfer the wS to the recipient.\n * @param _recipient Address of the Vault.\n * @param _asset Address of the Wrapped S (wS) contract.\n * @param _wsAmount Amount of Wrapped S (wS) to withdraw.\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _wsAmount\n ) external override onlyVault nonReentrant skimPool {\n require(_wsAmount > 0, \"Must withdraw something\");\n require(_asset == ws, \"Unsupported asset\");\n // This strategy can't be set as a default strategy for wS in the Vault.\n // This means the recipient must always be the Vault.\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n // Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n uint256 lpTokens = _calcTokensToBurn(_wsAmount);\n\n // Withdraw pool LP tokens from the gauge and remove assets from from the pool\n _withdrawFromGaugeAndPool(lpTokens);\n\n // Burn all the removed OS and any that was left in the strategy\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Transfer wS to the recipient\n // Note there can be a dust amount of wS left in the strategy as\n // the burn of the pool's LP tokens is rounded up\n require(\n IERC20(ws).balanceOf(address(this)) >= _wsAmount,\n \"Not enough wS removed from pool\"\n );\n IERC20(ws).safeTransfer(_recipient, _wsAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the withdrawn wS tokens\n emit Withdrawal(ws, pool, _wsAmount);\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n }\n\n /**\n * @notice Withdraw all pool LP tokens from the gauge,\n * remove all wS and OS from the SwapX pool,\n * burn all the OS tokens,\n * and transfer all the wS to the Vault contract.\n * @dev There is no solvency check here as withdrawAll can be called to\n * quickly secure assets to the Vault in emergencies.\n */\n function withdrawAll()\n external\n override\n onlyVaultOrGovernor\n nonReentrant\n skimPool\n {\n // Get all the pool LP tokens the strategy has staked in the gauge\n uint256 lpTokens = IGauge(gauge).balanceOf(address(this));\n // Can not withdraw zero LP tokens from the gauge\n if (lpTokens == 0) return;\n\n if (IGauge(gauge).emergency()) {\n // The gauge is in emergency mode\n _emergencyWithdrawFromGaugeAndPool();\n } else {\n // Withdraw pool LP tokens from the gauge and remove assets from from the pool\n _withdrawFromGaugeAndPool(lpTokens);\n }\n\n // Burn all OS in this strategy contract\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Get the strategy contract's wS balance.\n // This includes all that was removed from the SwapX pool and\n // any that was sitting in the strategy contract before the removal.\n uint256 wsBalance = IERC20(ws).balanceOf(address(this));\n IERC20(ws).safeTransfer(vaultAddress, wsBalance);\n\n // Emit event for the withdrawn wS tokens\n emit Withdrawal(ws, pool, wsBalance);\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n }\n\n /***************************************\n Pool Rebalancing\n ****************************************/\n\n /** @notice Used when there is more OS than wS in the pool.\n * wS and OS is removed from the pool, the received wS is swapped for OS\n * and the left over OS in the strategy is burnt.\n * The OS/wS price is < 1.0 so OS is being bought at a discount.\n * @param _wsAmount Amount of Wrapped S (wS) to swap into the pool.\n */\n function swapAssetsToPool(uint256 _wsAmount)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n skimPool\n {\n require(_wsAmount > 0, \"Must swap something\");\n\n // 1. Partially remove liquidity so there’s enough wS for the swap\n\n // Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n uint256 lpTokens = _calcTokensToBurn(_wsAmount);\n require(lpTokens > 0, \"No LP tokens to burn\");\n\n _withdrawFromGaugeAndPool(lpTokens);\n\n // 2. Swap wS for OS against the pool\n // Swap exact amount of wS for OS against the pool\n // There can be a dust amount of wS left in the strategy as the burn of the pool's LP tokens is rounded up\n _swapExactTokensForTokens(_wsAmount, ws, os);\n\n // 3. Burn all the OS left in the strategy from the remove liquidity and swap\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n // Emit event for the swap\n emit SwapAssetsToPool(_wsAmount, lpTokens, osToBurn);\n }\n\n /**\n * @notice Used when there is more wS than OS in the pool.\n * OS is minted and swapped for wS against the pool,\n * more OS is minted and added back into the pool with the swapped out wS.\n * The OS/wS price is > 1.0 so OS is being sold at a premium.\n * @param _osAmount Amount of OS to swap into the pool.\n */\n function swapOTokensToPool(uint256 _osAmount)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n skimPool\n {\n require(_osAmount > 0, \"Must swap something\");\n\n // 1. Mint OS so it can be swapped into the pool\n\n // There can be OS in the strategy from skimming the pool\n uint256 osInStrategy = IERC20(os).balanceOf(address(this));\n require(_osAmount >= osInStrategy, \"Too much OS in strategy\");\n uint256 osToMint = _osAmount - osInStrategy;\n\n // Mint the required OS tokens to this strategy\n IVault(vaultAddress).mintForStrategy(osToMint);\n\n // 2. Swap OS for wS against the pool\n _swapExactTokensForTokens(_osAmount, os, ws);\n\n // The wS is from the swap and any wS that was sitting in the strategy\n uint256 wsDepositAmount = IERC20(ws).balanceOf(address(this));\n\n // 3. Add wS and OS back to the pool in proportion to the pool's reserves\n (uint256 osDepositAmount, uint256 lpTokens) = _deposit(wsDepositAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osToMint + osDepositAmount);\n // Emit event for the swap\n emit SwapOTokensToPool(\n osToMint,\n wsDepositAmount,\n osDepositAmount,\n lpTokens\n );\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Get the wS value of assets in the strategy and SwapX pool.\n * The value of the assets in the pool is calculated assuming the pool is balanced.\n * This way the value can not be manipulated by changing the pool's token balances.\n * @param _asset Address of the Wrapped S (wS) token\n * @return balance Total value in wS.\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == ws, \"Unsupported asset\");\n\n // wS balance needed here for the balance check that happens from vault during depositing.\n balance = IERC20(ws).balanceOf(address(this));\n\n // This assumes 1 gauge LP token = 1 pool LP token\n uint256 lpTokens = IGauge(gauge).balanceOf(address(this));\n if (lpTokens == 0) return balance;\n\n // Add the strategy’s share of the wS and OS tokens in the SwapX pool if the pool was balanced.\n balance += _lpValue(lpTokens);\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == ws;\n }\n\n /**\n * @notice Collect accumulated SWPx (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect SWPx rewards from the gauge\n IGauge(gauge).getReward();\n\n _collectRewardTokens();\n }\n\n /***************************************\n Internal SwapX Pool and Gauge Functions\n ****************************************/\n\n /**\n * @dev Calculate the required amount of OS to mint based on the wS amount.\n * This ensures the proportion of OS tokens being added to the pool matches the proportion of wS tokens.\n * For example, if the added wS tokens is 10% of existing wS tokens in the pool,\n * then the OS tokens being added should also be 10% of the OS tokens in the pool.\n * @param _wsAmount Amount of Wrapped S (wS) to be added to the pool.\n * @return osAmount Amount of OS to be minted and added to the pool.\n */\n function _calcTokensToMint(uint256 _wsAmount)\n internal\n view\n returns (uint256 osAmount)\n {\n (uint256 wsReserves, uint256 osReserves, ) = IPair(pool).getReserves();\n require(wsReserves > 0, \"Empty pool\");\n\n // OS to add = (wS being added * OS in pool) / wS in pool\n osAmount = (_wsAmount * osReserves) / wsReserves;\n }\n\n /**\n * @dev Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n * from the pool.\n * @param _wsAmount Amount of Wrapped S (wS) to be removed from the pool.\n * @return lpTokens Amount of SwapX pool LP tokens to burn.\n */\n function _calcTokensToBurn(uint256 _wsAmount)\n internal\n view\n returns (uint256 lpTokens)\n {\n /* The SwapX pool proportionally returns the reserve tokens when removing liquidity.\n * First, calculate the proportion of required wS tokens against the pools wS reserves.\n * That same proportion is used to calculate the required amount of pool LP tokens.\n * For example, if the required wS tokens is 10% of the pool's wS reserves,\n * then 10% of the pool's LP supply needs to be burned.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognizant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on, the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n (uint256 wsReserves, , ) = IPair(pool).getReserves();\n require(wsReserves > 0, \"Empty pool\");\n\n lpTokens = (_wsAmount * IPair(pool).totalSupply()) / wsReserves;\n lpTokens += 1; // Add 1 to ensure we get enough LP tokens with rounding\n }\n\n /**\n * @dev Deposit Wrapped S (wS) and OS liquidity to the SwapX pool\n * and stake the pool's LP token in the gauge.\n * @param _wsAmount Amount of Wrapped S (wS) to deposit.\n * @param _osAmount Amount of OS to deposit.\n * @return lpTokens Amount of SwapX pool LP tokens minted.\n */\n function _depositToPoolAndGauge(uint256 _wsAmount, uint256 _osAmount)\n internal\n returns (uint256 lpTokens)\n {\n // Transfer wS to the pool\n IERC20(ws).safeTransfer(pool, _wsAmount);\n // Transfer OS to the pool\n IERC20(os).safeTransfer(pool, _osAmount);\n\n // Mint LP tokens from the pool\n lpTokens = IPair(pool).mint(address(this));\n\n // Deposit the pool's LP tokens into the gauge\n IGauge(gauge).deposit(lpTokens);\n }\n\n /**\n * @dev Withdraw pool LP tokens from the gauge and remove wS and OS from the pool.\n * @param _lpTokens Amount of SwapX pool LP tokens to withdraw from the gauge\n */\n function _withdrawFromGaugeAndPool(uint256 _lpTokens) internal {\n require(\n IGauge(gauge).balanceOf(address(this)) >= _lpTokens,\n \"Not enough LP tokens in gauge\"\n );\n\n // Withdraw pool LP tokens from the gauge\n IGauge(gauge).withdraw(_lpTokens);\n\n // Transfer the pool LP tokens to the pool\n IERC20(pool).safeTransfer(pool, _lpTokens);\n\n // Burn the LP tokens and transfer the wS and OS back to the strategy\n IPair(pool).burn(address(this));\n }\n\n /**\n * @dev Withdraw all pool LP tokens from the gauge when it's in emergency mode\n * and remove wS and OS from the pool.\n */\n function _emergencyWithdrawFromGaugeAndPool() internal {\n // Withdraw all pool LP tokens from the gauge\n IGauge(gauge).emergencyWithdraw();\n\n // Get the pool LP tokens in strategy\n uint256 _lpTokens = IERC20(pool).balanceOf(address(this));\n\n // Transfer the pool LP tokens to the pool\n IERC20(pool).safeTransfer(pool, _lpTokens);\n\n // Burn the LP tokens and transfer the wS and OS back to the strategy\n IPair(pool).burn(address(this));\n }\n\n /**\n * @dev Swap exact amount of tokens for another token against the pool.\n * @param _amountIn Amount of tokens to swap into the pool.\n * @param _tokenIn Address of the token going into the pool.\n * @param _tokenOut Address of the token being swapped out of the pool.\n */\n function _swapExactTokensForTokens(\n uint256 _amountIn,\n address _tokenIn,\n address _tokenOut\n ) internal {\n // Transfer in tokens to the pool\n IERC20(_tokenIn).safeTransfer(pool, _amountIn);\n\n // Calculate how much out tokens we get from the swap\n uint256 amountOut = IPair(pool).getAmountOut(_amountIn, _tokenIn);\n\n // Safety check that we are dealing with the correct pool tokens\n require(\n (_tokenIn == ws && _tokenOut == os) ||\n (_tokenIn == os && _tokenOut == ws),\n \"Unsupported swap\"\n );\n\n // Work out the correct order of the amounts for the pool\n (uint256 amount0, uint256 amount1) = _tokenIn == ws\n ? (uint256(0), amountOut)\n : (amountOut, 0);\n\n // Perform the swap on the pool\n IPair(pool).swap(amount0, amount1, address(this), new bytes(0));\n\n // The slippage protection against the amount out is indirectly done\n // via the improvePoolBalance\n }\n\n /// @dev Calculate the value of a LP position in a SwapX stable pool\n /// if the pool was balanced.\n /// @param _lpTokens Amount of LP tokens in the SwapX pool\n /// @return value The wS value of the LP tokens when the pool is balanced\n function _lpValue(uint256 _lpTokens) internal view returns (uint256 value) {\n // Get total supply of LP tokens\n uint256 totalSupply = IPair(pool).totalSupply();\n if (totalSupply == 0) return 0;\n\n // Get the current reserves of the pool\n (uint256 wsReserves, uint256 osReserves, ) = IPair(pool).getReserves();\n\n // Calculate the invariant of the pool assuming both tokens have 18 decimals.\n // k is scaled to 18 decimals.\n uint256 k = _invariant(wsReserves, osReserves);\n\n // If x = y, let’s denote x = y = z (where z is the common reserve value)\n // Substitute z into the invariant:\n // k = z^3 * z + z * z^3\n // k = 2 * z^4\n // Going back the other way to calculate the common reserve value z\n // z = (k / 2) ^ (1/4)\n // the total value of the pool when x = y is 2 * z, which is 2 * (k / 2) ^ (1/4)\n uint256 zSquared = sqrt((k * 1e18) / 2); // 18 + 18 = 36 decimals becomes 18 decimals after sqrt\n uint256 z = sqrt(zSquared * 1e18); // 18 + 18 = 36 decimals becomes 18 decimals after sqrt\n uint256 totalValueOfPool = 2 * z;\n\n // lp value = lp tokens * value of pool / total supply\n value = (_lpTokens * totalValueOfPool) / totalSupply;\n }\n\n /**\n * @dev Compute the invariant for a SwapX stable pool.\n * This assumed both x and y tokens are to 18 decimals which is checked in the constructor.\n * invariant: k = x^3 * y + x * y^3\n * @dev This implementation is copied from SwapX's Pair contract.\n * @param _x The amount of Wrapped S (wS) tokens in the pool\n * @param _y The amount of the OS tokens in the pool\n * @return k The invariant of the SwapX stable pool\n */\n function _invariant(uint256 _x, uint256 _y)\n internal\n pure\n returns (uint256 k)\n {\n uint256 _a = (_x * _y) / PRECISION;\n uint256 _b = ((_x * _x) / PRECISION + (_y * _y) / PRECISION);\n // slither-disable-next-line divide-before-multiply\n k = (_a * _b) / PRECISION;\n }\n\n /**\n * @dev Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalSupply = IERC20(os).totalSupply();\n\n if (\n _totalSupply > 0 &&\n _totalVaultValue.divPrecisely(_totalSupply) < SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Setters\n ****************************************/\n\n /**\n * @notice Set the maximum deviation from the OS/wS peg (1e18) before deposits are reverted.\n * @param _maxDepeg the OS/wS price from peg (1e18) in 18 decimals.\n * eg 0.01e18 or 1e16 is 1% which is 100 basis points.\n */\n function setMaxDepeg(uint256 _maxDepeg) external onlyGovernor {\n maxDepeg = _maxDepeg;\n\n emit MaxDepegUpdated(_maxDepeg);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve SwapX gauge contract to transfer SwapX pool LP tokens\n // This is needed for deposits of SwapX pool LP tokens into the gauge.\n // slither-disable-next-line unused-return\n IPair(pool).approve(address(gauge), type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/sonic/SonicValidatorDelegator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { ISFC } from \"../../interfaces/sonic/ISFC.sol\";\nimport { IWrappedSonic } from \"../../interfaces/sonic/IWrappedSonic.sol\";\n\n/**\n * @title Manages delegation to Sonic validators\n * @notice This contract implements all the required functionality to delegate to,\n undelegate from and withdraw from validators.\n * @author Origin Protocol Inc\n */\nabstract contract SonicValidatorDelegator is InitializableAbstractStrategy {\n /// @notice Address of Sonic's wrapped S token\n address public immutable wrappedSonic;\n /// @notice Sonic's Special Fee Contract (SFC)\n ISFC public immutable sfc;\n\n /// @notice a unique ID for each withdrawal request\n uint256 public nextWithdrawId;\n /// @notice Sonic (S) that is pending withdrawal after undelegating\n uint256 public pendingWithdrawals;\n\n /// @notice List of supported validator IDs that can be delegated to\n uint256[] public supportedValidators;\n\n /// @notice Default validator id to deposit to\n uint256 public defaultValidatorId;\n\n struct WithdrawRequest {\n uint256 validatorId;\n uint256 undelegatedAmount;\n uint256 timestamp;\n }\n /// @notice Mapping of withdrawIds to validatorIds and undelegatedAmounts\n mapping(uint256 => WithdrawRequest) public withdrawals;\n\n /// @notice Address of the registrator - allowed to register, exit and remove validators\n address public validatorRegistrator;\n\n // For future use\n uint256[44] private __gap;\n\n event Delegated(uint256 indexed validatorId, uint256 delegatedAmount);\n event Undelegated(\n uint256 indexed withdrawId,\n uint256 indexed validatorId,\n uint256 undelegatedAmount\n );\n event Withdrawn(\n uint256 indexed withdrawId,\n uint256 indexed validatorId,\n uint256 undelegatedAmount,\n uint256 withdrawnAmount\n );\n event RegistratorChanged(address indexed newAddress);\n event SupportedValidator(uint256 indexed validatorId);\n event UnsupportedValidator(uint256 indexed validatorId);\n event DefaultValidatorIdChanged(uint256 indexed validatorId);\n\n /// @dev Throws if called by any account other than the Registrator or Strategist\n modifier onlyRegistratorOrStrategist() {\n require(\n msg.sender == validatorRegistrator ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Registrator or Strategist\"\n );\n _;\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wrappedSonic,\n address _sfc\n ) InitializableAbstractStrategy(_baseConfig) {\n wrappedSonic = _wrappedSonic;\n sfc = ISFC(_sfc);\n }\n\n function initialize() external virtual onlyGovernor initializer {\n address[] memory rewardTokens = new address[](0);\n address[] memory assets = new address[](1);\n address[] memory pTokens = new address[](1);\n\n assets[0] = address(wrappedSonic);\n pTokens[0] = address(platformAddress);\n\n InitializableAbstractStrategy._initialize(\n rewardTokens,\n assets,\n pTokens\n );\n }\n\n /// @notice Returns the total value of Sonic (S) that is delegated validators.\n /// Wrapped Sonic (wS) deposits that are still to be delegated and any undelegated amounts\n /// still pending a withdrawal.\n /// @param _asset Address of Wrapped Sonic (wS) token\n /// @return balance Total value managed by the strategy\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n\n // add the Wrapped Sonic (wS) in the strategy from deposits that are still to be delegated\n // and any undelegated amounts still pending a withdrawal\n balance =\n IERC20(wrappedSonic).balanceOf(address(this)) +\n pendingWithdrawals;\n\n // For each supported validator, get the staked amount and pending rewards\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; i++) {\n uint256 validator = supportedValidators[i];\n balance += sfc.getStake(address(this), validator);\n balance += sfc.pendingRewards(address(this), validator);\n }\n }\n\n /**\n * @dev Delegate from this strategy to a specific Sonic validator. Called\n * automatically on asset deposit\n * @param _amount the amount of Sonic (S) to delegate.\n */\n function _delegate(uint256 _amount) internal {\n require(\n isSupportedValidator(defaultValidatorId),\n \"Validator not supported\"\n );\n\n // unwrap Wrapped Sonic (wS) to native Sonic (S)\n IWrappedSonic(wrappedSonic).withdraw(_amount);\n\n //slither-disable-next-line arbitrary-send-eth\n sfc.delegate{ value: _amount }(defaultValidatorId);\n\n emit Delegated(defaultValidatorId, _amount);\n }\n\n /**\n * @notice Undelegate from a specific Sonic validator.\n * This needs to be followed by a `withdrawFromSFC` two weeks later.\n * @param _validatorId The Sonic validator ID to undelegate from.\n * @param _undelegateAmount the amount of Sonic (S) to undelegate.\n * @return withdrawId The unique ID of the withdrawal request.\n */\n function undelegate(uint256 _validatorId, uint256 _undelegateAmount)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n returns (uint256 withdrawId)\n {\n withdrawId = _undelegate(_validatorId, _undelegateAmount);\n }\n\n function _undelegate(uint256 _validatorId, uint256 _undelegateAmount)\n internal\n returns (uint256 withdrawId)\n {\n // Can still undelegate even if the validator is no longer supported\n require(_undelegateAmount > 0, \"Must undelegate something\");\n\n uint256 amountDelegated = sfc.getStake(address(this), _validatorId);\n require(\n _undelegateAmount <= amountDelegated,\n \"Insufficient delegation\"\n );\n\n withdrawId = nextWithdrawId++;\n\n withdrawals[withdrawId] = WithdrawRequest(\n _validatorId,\n _undelegateAmount,\n block.timestamp\n );\n pendingWithdrawals += _undelegateAmount;\n\n sfc.undelegate(_validatorId, withdrawId, _undelegateAmount);\n\n emit Undelegated(withdrawId, _validatorId, _undelegateAmount);\n }\n\n /**\n * @notice Withdraw native S from a previously undelegated validator.\n * The native S is wrapped wS and transferred to the Vault.\n * @param _withdrawId The unique withdraw ID used to `undelegate`\n * @return withdrawnAmount The amount of Sonic (S) withdrawn.\n * This can be less than the undelegated amount in the event of slashing.\n */\n function withdrawFromSFC(uint256 _withdrawId)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n returns (uint256 withdrawnAmount)\n {\n require(_withdrawId < nextWithdrawId, \"Invalid withdrawId\");\n\n // Can still withdraw even if the validator is no longer supported\n // Load the withdrawal from storage into memory\n WithdrawRequest memory withdrawal = withdrawals[_withdrawId];\n require(!isWithdrawnFromSFC(_withdrawId), \"Already withdrawn\");\n\n withdrawals[_withdrawId].undelegatedAmount = 0;\n pendingWithdrawals -= withdrawal.undelegatedAmount;\n\n uint256 sBalanceBefore = address(this).balance;\n\n // Try to withdraw from SFC\n try sfc.withdraw(withdrawal.validatorId, _withdrawId) {\n // continue below\n } catch (bytes memory err) {\n bytes4 errorSelector = bytes4(err);\n\n // If the validator has been fully slashed, SFC's withdraw function will\n // revert with a StakeIsFullySlashed custom error.\n if (errorSelector == ISFC.StakeIsFullySlashed.selector) {\n // The validator was fully slashed, so all the delegated amounts were lost.\n // Will swallow the error as we still want to update the\n // withdrawals and pendingWithdrawals storage variables.\n\n // The return param defaults to zero but lets set it explicitly so it's clear\n withdrawnAmount = 0;\n\n emit Withdrawn(\n _withdrawId,\n withdrawal.validatorId,\n withdrawal.undelegatedAmount,\n withdrawnAmount\n );\n\n // Exit here as there is nothing to transfer to the Vault\n return withdrawnAmount;\n } else {\n // Bubble up any other SFC custom errors.\n // Inline assembly is currently the only way to generically rethrow the exact same custom error\n // from the raw bytes err in a catch block while preserving its original selector and parameters.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n revert(add(32, err), mload(err))\n }\n }\n }\n\n // Set return parameter\n withdrawnAmount = address(this).balance - sBalanceBefore;\n\n // Wrap Sonic (S) to Wrapped Sonic (wS)\n IWrappedSonic(wrappedSonic).deposit{ value: withdrawnAmount }();\n\n // Transfer the Wrapped Sonic (wS) to the Vault\n _withdraw(vaultAddress, wrappedSonic, withdrawnAmount);\n\n // withdrawal.undelegatedAmount & withdrawnAmount can differ in case of slashing\n emit Withdrawn(\n _withdrawId,\n withdrawal.validatorId,\n withdrawal.undelegatedAmount,\n withdrawnAmount\n );\n }\n\n /// @notice returns a bool whether a withdrawalId has already been withdrawn or not\n /// @param _withdrawId The unique withdraw ID used to `undelegate`\n function isWithdrawnFromSFC(uint256 _withdrawId)\n public\n view\n returns (bool)\n {\n WithdrawRequest memory withdrawal = withdrawals[_withdrawId];\n require(withdrawal.validatorId > 0, \"Invalid withdrawId\");\n return withdrawal.undelegatedAmount == 0;\n }\n\n /**\n * @notice Restake any pending validator rewards for all supported validators\n * @param _validatorIds List of Sonic validator IDs to restake rewards\n */\n function restakeRewards(uint256[] calldata _validatorIds)\n external\n nonReentrant\n {\n for (uint256 i = 0; i < _validatorIds.length; ++i) {\n require(\n isSupportedValidator(_validatorIds[i]),\n \"Validator not supported\"\n );\n\n uint256 rewards = sfc.pendingRewards(\n address(this),\n _validatorIds[i]\n );\n\n if (rewards > 0) {\n sfc.restakeRewards(_validatorIds[i]);\n }\n }\n\n // The SFC contract will emit Delegated and RestakedRewards events.\n // The checkBalance function should not change as the pending rewards will moved to the staked amount.\n }\n\n /**\n * @notice Claim any pending rewards from validators\n * @param _validatorIds List of Sonic validator IDs to claim rewards\n */\n function collectRewards(uint256[] calldata _validatorIds)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n {\n uint256 sBalanceBefore = address(this).balance;\n\n for (uint256 i = 0; i < _validatorIds.length; ++i) {\n uint256 rewards = sfc.pendingRewards(\n address(this),\n _validatorIds[i]\n );\n\n if (rewards > 0) {\n // The SFC contract will emit ClaimedRewards(delegator (this), validatorId, rewards)\n sfc.claimRewards(_validatorIds[i]);\n }\n }\n\n uint256 rewardsAmount = address(this).balance - sBalanceBefore;\n\n // Wrap Sonic (S) to Wrapped Sonic (wS)\n IWrappedSonic(wrappedSonic).deposit{ value: rewardsAmount }();\n\n // Transfer the Wrapped Sonic (wS) to the Vault\n _withdraw(vaultAddress, wrappedSonic, rewardsAmount);\n }\n\n /**\n * @notice To receive native S from SFC and Wrapped Sonic (wS)\n *\n * @dev This does not prevent donating S tokens to the contract\n * as wrappedSonic has a `withdrawTo` function where a third party\n * owner of wrappedSonic can withdraw to this contract.\n */\n receive() external payable {\n require(\n msg.sender == address(sfc) || msg.sender == wrappedSonic,\n \"S not from allowed contracts\"\n );\n }\n\n /***************************************\n Admin functions\n ****************************************/\n\n /// @notice Set the address of the Registrator which can undelegate, withdraw and collect rewards\n /// @param _validatorRegistrator The address of the Registrator\n function setRegistrator(address _validatorRegistrator)\n external\n onlyGovernor\n {\n validatorRegistrator = _validatorRegistrator;\n emit RegistratorChanged(_validatorRegistrator);\n }\n\n /// @notice Set the default validatorId to delegate to on deposit\n /// @param _validatorId The validator identifier. eg 18\n function setDefaultValidatorId(uint256 _validatorId)\n external\n onlyRegistratorOrStrategist\n {\n require(isSupportedValidator(_validatorId), \"Validator not supported\");\n defaultValidatorId = _validatorId;\n emit DefaultValidatorIdChanged(_validatorId);\n }\n\n /// @notice Allows a validator to be delegated to by the Registrator\n /// @param _validatorId The validator identifier. eg 18\n function supportValidator(uint256 _validatorId) external onlyGovernor {\n require(\n !isSupportedValidator(_validatorId),\n \"Validator already supported\"\n );\n\n supportedValidators.push(_validatorId);\n\n emit SupportedValidator(_validatorId);\n }\n\n /// @notice Removes a validator from the supported list.\n /// Unsupported validators can still be undelegated from, withdrawn from and rewards collected.\n /// @param _validatorId The validator identifier. eg 18\n function unsupportValidator(uint256 _validatorId) external onlyGovernor {\n require(isSupportedValidator(_validatorId), \"Validator not supported\");\n\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; ++i) {\n if (supportedValidators[i] == _validatorId) {\n supportedValidators[i] = supportedValidators[validatorLen - 1];\n supportedValidators.pop();\n break;\n }\n }\n\n uint256 stake = sfc.getStake(address(this), _validatorId);\n\n // undelegate if validator still has funds staked\n if (stake > 0) {\n _undelegate(_validatorId, stake);\n }\n emit UnsupportedValidator(_validatorId);\n }\n\n /// @notice Returns the length of the supportedValidators array\n function supportedValidatorsLength() external view returns (uint256) {\n return supportedValidators.length;\n }\n\n /// @notice Returns whether a validator is supported by this strategy\n /// @param _validatorId The validator identifier\n function isSupportedValidator(uint256 _validatorId)\n public\n view\n returns (bool)\n {\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; ++i) {\n if (supportedValidators[i] == _validatorId) {\n return true;\n }\n }\n return false;\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal virtual;\n}\n" + }, + "contracts/strategies/VaultValueChecker.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IOUSD } from \"../interfaces/IOUSD.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract VaultValueChecker {\n IVault public immutable vault;\n IOUSD public immutable ousd;\n // Snapshot expiration time in seconds.\n // Used to prevent accidental use of an old snapshot, but\n // is not zero to allow easy testing of strategist actions in fork testing\n uint256 constant SNAPSHOT_EXPIRES = 5 * 60;\n\n struct Snapshot {\n uint256 vaultValue;\n uint256 totalSupply;\n uint256 time;\n }\n // By doing per user snapshots, we prevent a reentrancy attack\n // from a third party that updates the snapshot in the middle\n // of an allocation process\n\n mapping(address => Snapshot) public snapshots;\n\n constructor(address _vault, address _ousd) {\n vault = IVault(_vault);\n ousd = IOUSD(_ousd);\n }\n\n function takeSnapshot() external {\n snapshots[msg.sender] = Snapshot({\n vaultValue: vault.totalValue(),\n totalSupply: ousd.totalSupply(),\n time: block.timestamp\n });\n }\n\n function checkDelta(\n int256 expectedProfit,\n int256 profitVariance,\n int256 expectedVaultChange,\n int256 vaultChangeVariance\n ) external {\n // Intentionaly not view so that this method shows up in TX builders\n Snapshot memory snapshot = snapshots[msg.sender];\n int256 vaultChange = toInt256(vault.totalValue()) -\n toInt256(snapshot.vaultValue);\n int256 supplyChange = toInt256(ousd.totalSupply()) -\n toInt256(snapshot.totalSupply);\n int256 profit = vaultChange - supplyChange;\n\n require(\n snapshot.time >= block.timestamp - SNAPSHOT_EXPIRES,\n \"Snapshot too old\"\n );\n require(snapshot.time <= block.timestamp, \"Snapshot too new\");\n require(profit >= expectedProfit - profitVariance, \"Profit too low\");\n require(profit <= expectedProfit + profitVariance, \"Profit too high\");\n require(\n vaultChange >= expectedVaultChange - vaultChangeVariance,\n \"Vault value change too low\"\n );\n require(\n vaultChange <= expectedVaultChange + vaultChangeVariance,\n \"Vault value change too high\"\n );\n }\n\n function toInt256(uint256 value) internal pure returns (int256) {\n // From openzeppelin math/SafeCast.sol\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(\n value <= uint256(type(int256).max),\n \"SafeCast: value doesn't fit in an int256\"\n );\n return int256(value);\n }\n}\n\ncontract OETHVaultValueChecker is VaultValueChecker {\n constructor(address _vault, address _ousd)\n VaultValueChecker(_vault, _ousd)\n {}\n}\n" + }, + "contracts/swapper/Swapper1InchV5.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @notice 1Inch Pathfinder V5 implementation of the general ISwapper interface.\n * @author Origin Protocol Inc\n * @dev It is possible that dust token amounts are left in this contract after a swap.\n * This can happen with some tokens that don't send the full transfer amount.\n * These dust amounts can build up over time and be used by anyone who calls the `swap` function.\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IAggregationExecutor, IOneInchRouter, SwapDescription } from \"../interfaces/IOneInch.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\n\ncontract Swapper1InchV5 is ISwapper {\n using SafeERC20 for IERC20;\n\n /// @notice 1Inch router contract to give allowance to perform swaps\n address public constant SWAP_ROUTER =\n 0x1111111254EEB25477B68fb85Ed929f73A960582;\n\n // swap(address,(address,address,address,address,uint256,uint256,uint256),bytes,bytes)\n bytes4 internal constant SWAP_SELECTOR = 0x12aa3caf;\n // unoswapTo(address,address,uint256,uint256,uint256[])\n bytes4 internal constant UNISWAP_SELECTOR = 0xf78dc253;\n // uniswapV3SwapTo(address,uint256,uint256,uint256[])\n bytes4 internal constant UNISWAPV3_SELECTOR = 0xbc80f1a8;\n\n /**\n * @notice Strategist swaps assets sitting in the contract of the `assetHolder`.\n * @param _fromAsset The token address of the asset being sold by the vault.\n * @param _toAsset The token address of the asset being purchased by the vault.\n * @param _fromAssetAmount The amount of assets being sold by the vault.\n * @param _minToAssetAmount The minimum amount of assets to be purchased.\n * @param _data RLP encoded executer address and bytes data. This is re-encoded tx.data from 1Inch swap API\n */\n function swap(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n ) external override returns (uint256 toAssetAmount) {\n // Decode the function selector from the RLP encoded _data param\n bytes4 swapSelector = bytes4(_data[:4]);\n\n if (swapSelector == SWAP_SELECTOR) {\n // Decode the executer address and data from the RLP encoded _data param\n (, address executer, bytes memory executerData) = abi.decode(\n _data,\n (bytes4, address, bytes)\n );\n SwapDescription memory swapDesc = SwapDescription({\n srcToken: IERC20(_fromAsset),\n dstToken: IERC20(_toAsset),\n srcReceiver: payable(executer),\n dstReceiver: payable(msg.sender),\n amount: _fromAssetAmount,\n minReturnAmount: _minToAssetAmount,\n flags: 4 // 1st bit _PARTIAL_FILL, 2nd bit _REQUIRES_EXTRA_ETH, 3rd bit _SHOULD_CLAIM\n });\n (toAssetAmount, ) = IOneInchRouter(SWAP_ROUTER).swap(\n IAggregationExecutor(executer),\n swapDesc,\n hex\"\",\n executerData\n );\n } else if (swapSelector == UNISWAP_SELECTOR) {\n // Need to get the Uniswap pools data from the _data param\n (, uint256[] memory pools) = abi.decode(_data, (bytes4, uint256[]));\n toAssetAmount = IOneInchRouter(SWAP_ROUTER).unoswapTo(\n payable(msg.sender),\n IERC20(_fromAsset),\n _fromAssetAmount,\n _minToAssetAmount,\n pools\n );\n } else if (swapSelector == UNISWAPV3_SELECTOR) {\n // Need to get the Uniswap pools data from the _data param\n // slither-disable-next-line uninitialized-storage\n (, uint256[] memory pools) = abi.decode(_data, (bytes4, uint256[]));\n toAssetAmount = IOneInchRouter(SWAP_ROUTER).uniswapV3SwapTo(\n payable(msg.sender),\n _fromAssetAmount,\n _minToAssetAmount,\n pools\n );\n } else {\n revert(\"Unsupported swap function\");\n }\n }\n\n /**\n * @notice Approve assets for swapping.\n * @param _assets Array of token addresses to approve.\n * @dev unlimited approval is used as no tokens sit in this contract outside a transaction.\n */\n function approveAssets(address[] memory _assets) external {\n for (uint256 i = 0; i < _assets.length; ++i) {\n // Give the 1Inch router approval to transfer unlimited assets\n IERC20(_assets[i]).safeApprove(SWAP_ROUTER, type(uint256).max);\n }\n }\n}\n" + }, + "contracts/token/BridgedWOETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\n\ncontract BridgedWOETH is\n Governable,\n AccessControlEnumerable,\n Initializable,\n ERC20\n{\n bytes32 public constant MINTER_ROLE = keccak256(\"MINTER_ROLE\");\n bytes32 public constant BURNER_ROLE = keccak256(\"BURNER_ROLE\");\n\n constructor() ERC20(\"Wrapped OETH\", \"wOETH\") {\n // Nobody owns the implementation\n _setGovernor(address(0));\n }\n\n /**\n * @dev Initialize the proxy and set the Governor\n */\n function initialize() external initializer {\n // Governor can grant Minter/Burner roles\n _setupRole(DEFAULT_ADMIN_ROLE, _governor());\n }\n\n /**\n * @dev Mint tokens for `account`\n * @param account Address to mint tokens for\n * @param amount Amount of tokens to mint\n */\n function mint(address account, uint256 amount)\n external\n onlyRole(MINTER_ROLE)\n nonReentrant\n {\n _mint(account, amount);\n }\n\n /**\n * @dev Burns tokens from `account`\n * @param account Address to burn tokens from\n * @param amount Amount of tokens to burn\n */\n function burn(address account, uint256 amount)\n external\n onlyRole(BURNER_ROLE)\n nonReentrant\n {\n _burn(account, amount);\n }\n\n /**\n * @dev Burns tokens from `msg.sender`\n * @param amount Amount of tokens to burn\n */\n function burn(uint256 amount) external onlyRole(BURNER_ROLE) nonReentrant {\n _burn(msg.sender, amount);\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return \"Wrapped OETH\";\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return \"wOETH\";\n }\n\n /**\n * @dev Returns the decimals of the token\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETH is OUSD {\n function symbol() external pure override returns (string memory) {\n return \"OETH\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Origin Ether\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETHBase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBase is OUSD {\n constructor() {\n // Nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n function symbol() external pure override returns (string memory) {\n return \"superOETHb\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Super OETH\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETHPlume.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title Super OETH (Plume) Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETHPlume is OUSD {\n constructor() {\n // Nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n function symbol() external pure override returns (string memory) {\n return \"superOETHp\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Super OETH\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OSonic.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title Origin Sonic (OS) token on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonic is OUSD {\n function symbol() external pure override returns (string memory) {\n return \"OS\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Origin Sonic\";\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/token/OUSDResolutionUpgrade.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract OUSDResolutionUpgrade {\n enum RebaseOptions {\n NotSet,\n OptOut,\n OptIn\n }\n\n // From Initializable\n bool private initialized;\n bool private initializing;\n uint256[50] private ______igap;\n\n // From InitializableERC20Detailed\n uint256[100] private _____ugap;\n string private _name;\n string private _symbol;\n uint8 private _decimals;\n\n // From OUSD\n uint256 private constant MAX_SUPPLY = ~uint128(0); // (2^128) - 1\n uint256 public _totalSupply;\n mapping(address => mapping(address => uint256)) private _allowances;\n address public vaultAddress = address(0);\n mapping(address => uint256) private _creditBalances;\n uint256 private _rebasingCredits;\n uint256 private _rebasingCreditsPerToken;\n uint256 public nonRebasingSupply;\n mapping(address => uint256) public nonRebasingCreditsPerToken;\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) public isUpgraded;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() public view returns (uint256) {\n return _rebasingCreditsPerToken / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCredits() public view returns (uint256) {\n return _rebasingCredits / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() public view returns (uint256) {\n return _rebasingCreditsPerToken;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() public view returns (uint256) {\n return _rebasingCredits;\n }\n\n function upgradeGlobals() external {\n require(isUpgraded[address(0)] == 0, \"Globals already upgraded\");\n require(_rebasingCredits > 0, \"Sanity _rebasingCredits\");\n require(\n _rebasingCreditsPerToken > 0,\n \"Sanity _rebasingCreditsPerToken\"\n );\n isUpgraded[address(0)] = 1;\n _rebasingCredits = _rebasingCredits * RESOLUTION_INCREASE;\n _rebasingCreditsPerToken =\n _rebasingCreditsPerToken *\n RESOLUTION_INCREASE;\n }\n\n function upgradeAccounts(address[] calldata accounts) external {\n for (uint256 i = 0; i < accounts.length; i++) {\n address account = accounts[i];\n require(account != address(0), \"Reserved\");\n require(isUpgraded[account] == 0, \"Account already upgraded\");\n isUpgraded[account] = 1;\n\n // Handle special for non-rebasing accounts\n uint256 nrc = nonRebasingCreditsPerToken[account];\n if (nrc > 0) {\n require(nrc < 1e18, \"Account already highres\");\n nonRebasingCreditsPerToken[account] = nrc * RESOLUTION_INCREASE;\n }\n // Upgrade balance\n uint256 balance = _creditBalances[account];\n require(balance > 0, \"Will not upgrade zero balance\");\n _creditBalances[account] = balance * RESOLUTION_INCREASE;\n }\n }\n\n function creditsBalanceOfHighres(address _account)\n public\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n _creditBalances[_account],\n _creditsPerToken(_account),\n isUpgraded[_account] == 1\n );\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n if (nonRebasingCreditsPerToken[_account] != 0) {\n return nonRebasingCreditsPerToken[_account];\n } else {\n return _rebasingCreditsPerToken;\n }\n }\n}\n" + }, + "contracts/token/WOETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC4626 } from \"../../lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { OETH } from \"./OETH.sol\";\n\n/**\n * @title Wrapped OETH Token Contract\n * @author Origin Protocol Inc\n *\n * @dev An important capability of this contract is that it isn't susceptible to changes of the\n * exchange rate of WOETH/OETH if/when someone sends the underlying asset (OETH) to the contract.\n * If OETH weren't rebasing this could be achieved by solely tracking the ERC20 transfers of the OETH\n * token on mint, deposit, redeem, withdraw. The issue is that OETH is rebasing and OETH balances\n * will change when the token rebases.\n * For that reason the contract logic checks the actual underlying OETH token balance only once\n * (either on a fresh contract creation or upgrade) and considering the WOETH supply and\n * rebasingCreditsPerToken calculates the _adjuster. Once the adjuster is calculated any donations\n * to the contract are ignored. The totalSupply (instead of querying OETH balance) works off of\n * adjuster the current WOETH supply and rebasingCreditsPerToken. This makes WOETH value accrual\n * completely follow OETH's value accrual.\n * WOETH is safe to use in lending markets as the VualtCore's _rebase contains safeguards preventing\n * any sudden large rebases.\n */\n\ncontract WOETH is ERC4626, Governable, Initializable {\n using SafeERC20 for IERC20;\n /* This is a 1e27 adjustment constant that expresses the difference in exchange rate between\n * OETH's rebase since inception (expressed with rebasingCreditsPerToken) and WOETH to OETH\n * conversion.\n *\n * If WOETH and OETH are deployed at the same time, the value of adjuster is a neutral 1e27\n */\n uint256 public adjuster;\n uint256[49] private __gap;\n\n // no need to set ERC20 name and symbol since they are overridden in WOETH & WOETHBase\n constructor(ERC20 underlying_) ERC20(\"\", \"\") ERC4626(underlying_) {}\n\n /**\n * @notice Enable OETH rebasing for this contract\n */\n function initialize() external onlyGovernor initializer {\n OETH(address(asset())).rebaseOptIn();\n\n initialize2();\n }\n\n /**\n * @notice secondary initializer that newly deployed contracts will execute as part\n * of primary initialize function and the existing contracts will have it called\n * as a governance operation.\n */\n function initialize2() public onlyGovernor {\n require(adjuster == 0, \"Initialize2 already called\");\n\n if (totalSupply() == 0) {\n adjuster = 1e27;\n } else {\n adjuster =\n (rebasingCreditsPerTokenHighres() *\n ERC20(asset()).balanceOf(address(this))) /\n totalSupply();\n }\n }\n\n function name()\n public\n view\n virtual\n override(ERC20, IERC20Metadata)\n returns (string memory)\n {\n return \"Wrapped OETH\";\n }\n\n function symbol()\n public\n view\n virtual\n override(ERC20, IERC20Metadata)\n returns (string memory)\n {\n return \"wOETH\";\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends. Cannot transfer OETH\n * @param asset_ Address for the asset\n * @param amount_ Amount of the asset to transfer\n */\n function transferToken(address asset_, uint256 amount_)\n external\n onlyGovernor\n {\n require(asset_ != address(asset()), \"Cannot collect core asset\");\n IERC20(asset_).safeTransfer(governor(), amount_);\n }\n\n /// @inheritdoc ERC4626\n function convertToShares(uint256 assets)\n public\n view\n virtual\n override\n returns (uint256 shares)\n {\n return (assets * rebasingCreditsPerTokenHighres()) / adjuster;\n }\n\n /// @inheritdoc ERC4626\n function convertToAssets(uint256 shares)\n public\n view\n virtual\n override\n returns (uint256 assets)\n {\n return (shares * adjuster) / rebasingCreditsPerTokenHighres();\n }\n\n /// @inheritdoc ERC4626\n function totalAssets() public view override returns (uint256) {\n return (totalSupply() * adjuster) / rebasingCreditsPerTokenHighres();\n }\n\n function rebasingCreditsPerTokenHighres() internal view returns (uint256) {\n return OETH(asset()).rebasingCreditsPerTokenHighres();\n }\n}\n" + }, + "contracts/token/WOETHBase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WOETH } from \"./WOETH.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\n\ncontract WOETHBase is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name() public view virtual override returns (string memory) {\n return \"Wrapped Super OETH\";\n }\n\n function symbol() public view virtual override returns (string memory) {\n return \"wsuperOETHb\";\n }\n}\n" + }, + "contracts/token/WOETHPlume.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WOETH } from \"./WOETH.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @title wOETH (Plume) Token Contract\n * @author Origin Protocol Inc\n */\n\ncontract WOETHPlume is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name() public view virtual override returns (string memory) {\n return \"Wrapped Super OETH\";\n }\n\n function symbol() public view virtual override returns (string memory) {\n return \"wsuperOETHp\";\n }\n}\n" + }, + "contracts/token/WOSonic.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { WOETH } from \"./WOETH.sol\";\n\n/**\n * @title Wrapped Origin Sonic (wOS) token on Sonic\n * @author Origin Protocol Inc\n */\ncontract WOSonic is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"Wrapped OS\";\n }\n\n function symbol()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"wOS\";\n }\n}\n" + }, + "contracts/token/WrappedOusd.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { WOETH } from \"./WOETH.sol\";\n\n/**\n * @title Wrapped OUSD Token Contract\n * @author Origin Protocol Inc\n */\ncontract WrappedOusd is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"Wrapped OUSD\";\n }\n\n function symbol()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"WOUSD\";\n }\n}\n" + }, + "contracts/utils/AerodromeAMOQuoter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.7;\n\nimport { ICLPool } from \"../interfaces/aerodrome/ICLPool.sol\";\nimport { IQuoterV2 } from \"../interfaces/aerodrome/IQuoterV2.sol\";\nimport { IAMOStrategy } from \"../interfaces/aerodrome/IAMOStrategy.sol\";\n\n/// @title QuoterHelper\n/// @author Origin Protocol\n/// @notice Helper for Aerodrome AMO Quoter, as `_quoteAmountToSwapBeforeRebalance` use try/catch method and\n/// this can only be used when calling external contracts.\ncontract QuoterHelper {\n ////////////////////////////////////////////////////////////////\n /// --- STRUCTS & ENUMS\n ////////////////////////////////////////////////////////////////\n enum RevertReasons {\n DefaultStatus,\n RebalanceOutOfBounds,\n NotInExpectedTickRange,\n NotEnoughWethForSwap,\n NotEnoughWethLiquidity,\n UnexpectedError,\n Found,\n NotFound\n }\n\n struct RebalanceStatus {\n RevertReasons reason;\n uint256 currentPoolWETHShare; // Case 1\n uint256 allowedWETHShareStart; // Case 1\n uint256 allowedWETHShareEnd; // Case 1\n int24 currentTick; // Case 2\n uint256 balanceWETH; // Case 3\n uint256 amountWETH; // Case 3\n string revertMessage;\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTANT & IMMUTABLE\n ////////////////////////////////////////////////////////////////\n uint256 public constant BINARY_MIN_AMOUNT = 1 wei;\n\n uint256 public constant BINARY_MAX_ITERATIONS = 40;\n uint256 public constant PERCENTAGE_BASE = 1e18; // 100%\n uint256 public constant ALLOWED_VARIANCE_PERCENTAGE = 1e15; // 0.1%\n\n ////////////////////////////////////////////////////////////////\n /// --- VARIABLES STORAGE\n ////////////////////////////////////////////////////////////////\n ICLPool public immutable clPool;\n IQuoterV2 public immutable quoterV2;\n IAMOStrategy public immutable strategy;\n\n address public originalGovernor;\n\n ////////////////////////////////////////////////////////////////\n /// --- ERRORS & EVENTS\n ////////////////////////////////////////////////////////////////\n error UnexpectedError(string message);\n error OutOfIterations(uint256 iterations);\n error ValidAmount(\n uint256 amount,\n uint256 iterations,\n bool swapWETHForOETHB\n );\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////////////////\n constructor(IAMOStrategy _strategy, IQuoterV2 _quoterV2) {\n strategy = _strategy;\n quoterV2 = _quoterV2;\n clPool = _strategy.clPool();\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- FUNCTIONS\n ////////////////////////////////////////////////////////////////\n /// @notice This call can only end with a revert.\n function getAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) public {\n if (\n overrideBottomWethShare != type(uint256).max ||\n overrideTopWethShare != type(uint256).max\n ) {\n // Current values\n uint256 shareStart = strategy.allowedWethShareStart();\n uint256 shareEnd = strategy.allowedWethShareEnd();\n\n // Override values\n if (overrideBottomWethShare != type(uint256).max) {\n shareStart = overrideBottomWethShare;\n }\n if (overrideTopWethShare != type(uint256).max) {\n shareEnd = overrideTopWethShare;\n }\n\n strategy.setAllowedPoolWethShareInterval(shareStart, shareEnd);\n }\n\n uint256 iterations = 0;\n uint256 low = BINARY_MIN_AMOUNT;\n uint256 high;\n (high, ) = strategy.getPositionPrincipal();\n int24 lowerTick = strategy.lowerTick();\n int24 upperTick = strategy.upperTick();\n bool swapWETHForOETHB = getSwapDirectionForRebalance();\n\n while (low <= high && iterations < BINARY_MAX_ITERATIONS) {\n uint256 mid = (low + high) / 2;\n\n RebalanceStatus memory status = getRebalanceStatus(\n mid,\n swapWETHForOETHB\n );\n\n // Best case, we found the `amount` that will reach the target pool share!\n // We can revert with the amount and the number of iterations\n if (status.reason == RevertReasons.Found) {\n revert ValidAmount(mid, iterations, swapWETHForOETHB);\n }\n\n // If the rebalance failed then we should try to change the amount.\n // We will handle all possible revert reasons here.\n\n // Case 1: Rebalance out of bounds\n // If the pool is out of bounds, we need to adjust the amount to reach the target pool share\n if (status.reason == RevertReasons.RebalanceOutOfBounds) {\n // If the current pool share is less than the target pool share, we need to increase the amount\n if (\n swapWETHForOETHB\n ? status.currentPoolWETHShare <\n status.allowedWETHShareStart\n : status.currentPoolWETHShare >\n status.allowedWETHShareEnd\n ) {\n low = mid + 1;\n }\n // Else we need to decrease the amount\n else {\n high = mid;\n }\n }\n\n // Case 2: Not in expected tick range\n // If the pool is not in the expected tick range, we need to adjust the amount\n // to reach the target pool share\n if (status.reason == RevertReasons.NotInExpectedTickRange) {\n // If we are buying OETHb and the current tick is greater than the lower tick,\n //we need to increase the amount in order to continue to push price down.\n // If we are selling OETHb and the current tick is less than the upper tick,\n // we need to increase the amount in order to continue to push price up.\n if (\n swapWETHForOETHB\n ? status.currentTick > lowerTick\n : status.currentTick < upperTick\n ) {\n low = mid + 1;\n }\n // Else we need to decrease the amount\n else {\n high = mid;\n }\n }\n\n // Case 3: Not enough WETH for swap\n // If we don't have enough WETH to swap, we need to decrease the amount\n // This error can happen, when initial value of mid is too high, so we need to decrease it\n if (status.reason == RevertReasons.NotEnoughWethForSwap) {\n high = mid;\n }\n\n // Case 4: Not enough WETH liquidity\n // If we don't have enough WETH liquidity\n // Revert for the moment, we need to improve this\n if (status.reason == RevertReasons.NotEnoughWethLiquidity) {\n revert(\"Quoter: Not enough WETH liquidity\");\n }\n\n // Case 5: Unexpected error\n // Worst case, it reverted with an unexpected error.\n if (status.reason == RevertReasons.UnexpectedError) {\n revert UnexpectedError(status.revertMessage);\n }\n\n iterations++;\n }\n\n // Case 6: Out of iterations\n // If we didn't find the amount after the max iterations, we need to revert.\n revert OutOfIterations(iterations);\n }\n\n /// @notice Get the status of the rebalance\n /// @param amount The amount of token to swap\n /// @param swapWETH True if we need to swap WETH for OETHb, false otherwise\n /// @return status The status of the rebalance\n function getRebalanceStatus(uint256 amount, bool swapWETH)\n public\n returns (RebalanceStatus memory status)\n {\n try strategy.rebalance(amount, swapWETH, 0) {\n status.reason = RevertReasons.Found;\n return status;\n } catch Error(string memory reason) {\n status.reason = RevertReasons.UnexpectedError;\n status.revertMessage = reason;\n } catch (bytes memory reason) {\n bytes4 receivedSelector = bytes4(reason);\n\n // Case 1: Rebalance out of bounds\n if (\n receivedSelector ==\n IAMOStrategy.PoolRebalanceOutOfBounds.selector\n ) {\n uint256 currentPoolWETHShare;\n uint256 allowedWETHShareStart;\n uint256 allowedWETHShareEnd;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n currentPoolWETHShare := mload(add(reason, 0x24))\n allowedWETHShareStart := mload(add(reason, 0x44))\n allowedWETHShareEnd := mload(add(reason, 0x64))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.RebalanceOutOfBounds,\n currentPoolWETHShare: currentPoolWETHShare,\n allowedWETHShareStart: allowedWETHShareStart,\n allowedWETHShareEnd: allowedWETHShareEnd,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 2: Not in expected tick range\n if (\n receivedSelector ==\n IAMOStrategy.OutsideExpectedTickRange.selector\n ) {\n int24 currentTick;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n currentTick := mload(add(reason, 0x24))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.NotInExpectedTickRange,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: currentTick,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 3: Not enough WETH for swap\n if (\n receivedSelector == IAMOStrategy.NotEnoughWethForSwap.selector\n ) {\n uint256 balanceWETH;\n uint256 amountWETH;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n balanceWETH := mload(add(reason, 0x24))\n amountWETH := mload(add(reason, 0x44))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.NotEnoughWethForSwap,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: balanceWETH,\n amountWETH: amountWETH,\n revertMessage: \"\"\n });\n }\n\n // Case 4: Not enough WETH liquidity\n if (\n receivedSelector == IAMOStrategy.NotEnoughWethLiquidity.selector\n ) {\n return\n RebalanceStatus({\n reason: RevertReasons.NotEnoughWethLiquidity,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 5: Unexpected error\n return\n RebalanceStatus({\n reason: RevertReasons.UnexpectedError,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: abi.decode(reason, (string))\n });\n }\n }\n\n /// @notice Get the swap direction to reach the target price before rebalance.\n /// @return bool True if we need to swap WETH for OETHb, false otherwise.\n function getSwapDirectionForRebalance() public view returns (bool) {\n uint160 currentPrice = strategy.getPoolX96Price();\n uint160 ticker0Price = strategy.sqrtRatioX96TickLower();\n uint160 ticker1Price = strategy.sqrtRatioX96TickHigher();\n uint256 allowedWethShareStart = strategy.allowedWethShareStart();\n uint256 allowedWethShareEnd = strategy.allowedWethShareEnd();\n uint160 mid = uint160(allowedWethShareStart + allowedWethShareEnd) / 2;\n // slither-disable-start divide-before-multiply\n uint160 targetPrice = (ticker0Price *\n mid +\n ticker1Price *\n (1 ether - mid)) / 1 ether;\n // slither-disable-end divide-before-multiply\n\n return currentPrice > targetPrice;\n }\n\n // returns total amount in the position principal of the Aerodrome AMO strategy. Needed as a\n // separate function because of the limitation in local variable count in getAmountToSwapToReachPrice\n function getTotalStrategyPosition() internal returns (uint256) {\n (uint256 wethAmount, uint256 oethBalance) = strategy\n .getPositionPrincipal();\n return wethAmount + oethBalance;\n }\n\n /// @notice Get the amount of tokens to swap to reach the target price.\n /// @dev This act like a quoter, i.e. the transaction is not performed.\n /// @dev Because the amount to swap can be largely overestimated, because CLAMM alow partial orders,\n /// i.e. when we ask to swap a very large amount, with a close priceLimite, it will swap only a part of it,\n /// and not revert. So if overestimated amount is to high, use a custom maxAmount to avoid this issue.\n /// @param sqrtPriceTargetX96 The target price to reach.\n /// @return amount The amount of tokens to swap.\n /// @return iterations The number of iterations to find the amount.\n /// @return swapWETHForOETHB True if we need to swap WETH for OETHb, false otherwise.\n /// @return sqrtPriceX96After The price after the swap.\n function getAmountToSwapToReachPrice(uint160 sqrtPriceTargetX96)\n public\n returns (\n uint256,\n uint256,\n bool,\n uint160\n )\n {\n uint256 iterations = 0;\n uint256 low = BINARY_MIN_AMOUNT;\n // high search start is twice the position principle of Aerodrome AMO strategy.\n // should be more than enough\n uint256 high = getTotalStrategyPosition() * 2;\n bool swapWETHForOETHB = getSwapDirection(sqrtPriceTargetX96);\n\n while (low <= high && iterations < BINARY_MAX_ITERATIONS) {\n uint256 mid = (low + high) / 2;\n\n // Call QuoterV2 from SugarHelper\n (uint256 amountOut, uint160 sqrtPriceX96After, , ) = quoterV2\n .quoteExactInputSingle(\n IQuoterV2.QuoteExactInputSingleParams({\n tokenIn: swapWETHForOETHB\n ? clPool.token0()\n : clPool.token1(),\n tokenOut: swapWETHForOETHB\n ? clPool.token1()\n : clPool.token0(),\n amountIn: mid,\n tickSpacing: strategy.tickSpacing(),\n sqrtPriceLimitX96: sqrtPriceTargetX96\n })\n );\n\n if (\n isWithinAllowedVariance(sqrtPriceX96After, sqrtPriceTargetX96)\n ) {\n /** Very important to return `amountOut` instead of `mid` as the first return parameter.\n * The issues was that when quoting we impose a swap price limit (sqrtPriceLimitX96: sqrtPriceTargetX96)\n * and in that case the `amountIn` acts like a maximum amount to swap. And we don't know how much\n * of that amount was actually consumed. For that reason we \"estimate\" it by returning the\n * amountOut since that is only going to be a couple of basis point away from amountIn in the\n * worst cases.\n *\n * Note: we could be returning mid instead of amountOut in cases when those values are only basis\n * points apart (assuming that complete balance of amountIn has been consumed) but that might increase\n * complexity too much in an already complex contract.\n */\n return (\n amountOut,\n iterations,\n swapWETHForOETHB,\n sqrtPriceX96After\n );\n } else if (low == high) {\n // target swap amount not found.\n // might be that \"high\" amount is too low on start\n revert(\"SwapAmountNotFound\");\n } else if (\n swapWETHForOETHB\n ? sqrtPriceX96After > sqrtPriceTargetX96\n : sqrtPriceX96After < sqrtPriceTargetX96\n ) {\n low = mid + 1;\n } else {\n high = mid;\n }\n iterations++;\n }\n\n revert OutOfIterations(iterations);\n }\n\n /// @notice Check if the current price is within the allowed variance in comparison to the target price\n /// @return bool True if the current price is within the allowed variance, false otherwise\n function isWithinAllowedVariance(\n uint160 sqrtPriceCurrentX96,\n uint160 sqrtPriceTargetX96\n ) public view returns (bool) {\n uint160 range = strategy.sqrtRatioX96TickHigher() -\n strategy.sqrtRatioX96TickLower();\n if (sqrtPriceCurrentX96 > sqrtPriceTargetX96) {\n return\n (sqrtPriceCurrentX96 - sqrtPriceTargetX96) <=\n (ALLOWED_VARIANCE_PERCENTAGE * range) / PERCENTAGE_BASE;\n } else {\n return\n (sqrtPriceTargetX96 - sqrtPriceCurrentX96) <=\n (ALLOWED_VARIANCE_PERCENTAGE * range) / PERCENTAGE_BASE;\n }\n }\n\n /// @notice Get the swap direction to reach the target price.\n /// @param sqrtPriceTargetX96 The target price to reach.\n /// @return bool True if we need to swap WETH for OETHb, false otherwise.\n function getSwapDirection(uint160 sqrtPriceTargetX96)\n public\n view\n returns (bool)\n {\n uint160 currentPrice = strategy.getPoolX96Price();\n return currentPrice > sqrtPriceTargetX96;\n }\n\n function claimGovernanceOnAMO() public {\n originalGovernor = strategy.governor();\n strategy.claimGovernance();\n }\n\n function giveBackGovernanceOnAMO() public {\n require(\n originalGovernor != address(0),\n \"Quoter: Original governor not set\"\n );\n strategy.transferGovernance(originalGovernor);\n }\n}\n\n/// @title AerodromeAMOQuoter\n/// @author Origin Protocol\n/// @notice Quoter for Aerodrome AMO\ncontract AerodromeAMOQuoter {\n ////////////////////////////////////////////////////////////////\n /// --- STRUCTS & ENUMS\n ///////////////////////////////////////////////////////////////\n struct Data {\n uint256 amount;\n uint256 iterations;\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- VARIABLES STORAGE\n ////////////////////////////////////////////////////////////////\n QuoterHelper public immutable quoterHelper;\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////////////////\n constructor(address _strategy, address _quoterV2) {\n quoterHelper = new QuoterHelper(\n IAMOStrategy(_strategy),\n IQuoterV2(_quoterV2)\n );\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- ERRORS & EVENTS\n ////////////////////////////////////////////////////////////////\n event ValueFound(uint256 value, uint256 iterations, bool swapWETHForOETHB);\n event ValueFoundBis(\n uint256 value,\n uint256 iterations,\n bool swapWETHForOETHB,\n uint160 sqrtPriceAfterX96\n );\n event ValueNotFound(string message);\n\n ////////////////////////////////////////////////////////////////\n /// --- FUNCTIONS\n ////////////////////////////////////////////////////////////////\n /// @notice Use this to get the amount to swap before rebalance\n /// @dev This call will only revert, check the logs to get returned values.\n /// @dev Need to perform this call while impersonating the governor or strategist of AMO.\n /// @return data Data struct with the amount and the number of iterations\n function quoteAmountToSwapBeforeRebalance()\n public\n returns (Data memory data)\n {\n return\n _quoteAmountToSwapBeforeRebalance(\n type(uint256).max,\n type(uint256).max\n );\n }\n\n /// @notice Use this to get the amount to swap before rebalance and\n /// update allowedWethShareStart and allowedWethShareEnd on AMO.\n /// @dev This call will only revert, check the logs to get returned values.\n /// @dev Need to perform this call while impersonating the governor of AMO.\n /// @param overrideBottomWethShare New value for the allowedWethShareStart on AMO.\n /// Use type(uint256).max to keep same value.\n /// @param overrideTopWethShare New value for the allowedWethShareEnd on AMO.\n /// Use type(uint256).max to keep same value.\n /// @return data Data struct with the amount and the number of iterations\n function quoteAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) public returns (Data memory data) {\n return\n _quoteAmountToSwapBeforeRebalance(\n overrideBottomWethShare,\n overrideTopWethShare\n );\n }\n\n /// @notice Internal logic for quoteAmountToSwapBeforeRebalance.\n function _quoteAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) internal returns (Data memory data) {\n try\n quoterHelper.getAmountToSwapBeforeRebalance(\n overrideBottomWethShare,\n overrideTopWethShare\n )\n {\n revert(\"Previous call should only revert, it cannot succeed\");\n } catch (bytes memory reason) {\n bytes4 receivedSelector = bytes4(reason);\n if (receivedSelector == QuoterHelper.ValidAmount.selector) {\n uint256 value;\n uint256 iterations;\n bool swapWETHForOETHB;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n value := mload(add(reason, 0x24))\n iterations := mload(add(reason, 0x44))\n swapWETHForOETHB := mload(add(reason, 0x64))\n }\n emit ValueFound(value, iterations, swapWETHForOETHB);\n return Data({ amount: value, iterations: iterations });\n }\n\n if (receivedSelector == QuoterHelper.OutOfIterations.selector) {\n emit ValueNotFound(\"Out of iterations\");\n revert(\"Out of iterations\");\n }\n\n emit ValueNotFound(\"Unexpected error\");\n revert(abi.decode(reason, (string)));\n }\n }\n\n /// @notice Use this to get the amount to swap to reach the target price after swap.\n /// @dev This call will only revert, check the logs to get returned values.\n /// @param sqrtPriceTargetX96 The target price to reach.\n function quoteAmountToSwapToReachPrice(uint160 sqrtPriceTargetX96) public {\n (\n uint256 amount,\n uint256 iterations,\n bool swapWETHForOETHB,\n uint160 sqrtPriceAfterX96\n ) = quoterHelper.getAmountToSwapToReachPrice(sqrtPriceTargetX96);\n\n emit ValueFoundBis(\n amount,\n iterations,\n swapWETHForOETHB,\n sqrtPriceAfterX96\n );\n }\n\n function claimGovernance() public {\n quoterHelper.claimGovernanceOnAMO();\n }\n\n function giveBackGovernance() public {\n quoterHelper.giveBackGovernanceOnAMO();\n }\n}\n" + }, + "contracts/utils/BalancerErrors.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity >=0.7.4 <0.9.0;\n\n// solhint-disable\n\n/**\n * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are\n * supported.\n * Uses the default 'BAL' prefix for the error code\n */\nfunction _require(bool condition, uint256 errorCode) pure {\n if (!condition) _revert(errorCode);\n}\n\n/**\n * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are\n * supported.\n */\nfunction _require(\n bool condition,\n uint256 errorCode,\n bytes3 prefix\n) pure {\n if (!condition) _revert(errorCode, prefix);\n}\n\n/**\n * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported.\n * Uses the default 'BAL' prefix for the error code\n */\nfunction _revert(uint256 errorCode) pure {\n _revert(errorCode, 0x42414c); // This is the raw byte representation of \"BAL\"\n}\n\n/**\n * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported.\n */\nfunction _revert(uint256 errorCode, bytes3 prefix) pure {\n uint256 prefixUint = uint256(uint24(prefix));\n // We're going to dynamically create a revert string based on the error code, with the following format:\n // 'BAL#{errorCode}'\n // where the code is left-padded with zeroes to three digits (so they range from 000 to 999).\n //\n // We don't have revert strings embedded in the contract to save bytecode size: it takes much less space to store a\n // number (8 to 16 bits) than the individual string characters.\n //\n // The dynamic string creation algorithm that follows could be implemented in Solidity, but assembly allows for a\n // much denser implementation, again saving bytecode size. Given this function unconditionally reverts, this is a\n // safe place to rely on it without worrying about how its usage might affect e.g. memory contents.\n assembly {\n // First, we need to compute the ASCII representation of the error code. We assume that it is in the 0-999\n // range, so we only need to convert three digits. To convert the digits to ASCII, we add 0x30, the value for\n // the '0' character.\n\n let units := add(mod(errorCode, 10), 0x30)\n\n errorCode := div(errorCode, 10)\n let tenths := add(mod(errorCode, 10), 0x30)\n\n errorCode := div(errorCode, 10)\n let hundreds := add(mod(errorCode, 10), 0x30)\n\n // With the individual characters, we can now construct the full string.\n // We first append the '#' character (0x23) to the prefix. In the case of 'BAL', it results in 0x42414c23 ('BAL#')\n // Then, we shift this by 24 (to provide space for the 3 bytes of the error code), and add the\n // characters to it, each shifted by a multiple of 8.\n // The revert reason is then shifted left by 200 bits (256 minus the length of the string, 7 characters * 8 bits\n // per character = 56) to locate it in the most significant part of the 256 slot (the beginning of a byte\n // array).\n let formattedPrefix := shl(24, add(0x23, shl(8, prefixUint)))\n\n let revertReason := shl(\n 200,\n add(\n formattedPrefix,\n add(add(units, shl(8, tenths)), shl(16, hundreds))\n )\n )\n\n // We can now encode the reason in memory, which can be safely overwritten as we're about to revert. The encoded\n // message will have the following layout:\n // [ revert reason identifier ] [ string location offset ] [ string length ] [ string contents ]\n\n // The Solidity revert reason identifier is 0x08c739a0, the function selector of the Error(string) function. We\n // also write zeroes to the next 28 bytes of memory, but those are about to be overwritten.\n mstore(\n 0x0,\n 0x08c379a000000000000000000000000000000000000000000000000000000000\n )\n // Next is the offset to the location of the string, which will be placed immediately after (20 bytes away).\n mstore(\n 0x04,\n 0x0000000000000000000000000000000000000000000000000000000000000020\n )\n // The string length is fixed: 7 characters.\n mstore(0x24, 7)\n // Finally, the string itself is stored.\n mstore(0x44, revertReason)\n\n // Even if the string is only 7 bytes long, we need to return a full 32 byte slot containing it. The length of\n // the encoded message is therefore 4 + 32 + 32 + 32 = 100.\n revert(0, 100)\n }\n}\n\nlibrary Errors {\n // Math\n uint256 internal constant ADD_OVERFLOW = 0;\n uint256 internal constant SUB_OVERFLOW = 1;\n uint256 internal constant SUB_UNDERFLOW = 2;\n uint256 internal constant MUL_OVERFLOW = 3;\n uint256 internal constant ZERO_DIVISION = 4;\n uint256 internal constant DIV_INTERNAL = 5;\n uint256 internal constant X_OUT_OF_BOUNDS = 6;\n uint256 internal constant Y_OUT_OF_BOUNDS = 7;\n uint256 internal constant PRODUCT_OUT_OF_BOUNDS = 8;\n uint256 internal constant INVALID_EXPONENT = 9;\n\n // Input\n uint256 internal constant OUT_OF_BOUNDS = 100;\n uint256 internal constant UNSORTED_ARRAY = 101;\n uint256 internal constant UNSORTED_TOKENS = 102;\n uint256 internal constant INPUT_LENGTH_MISMATCH = 103;\n uint256 internal constant ZERO_TOKEN = 104;\n uint256 internal constant INSUFFICIENT_DATA = 105;\n\n // Shared pools\n uint256 internal constant MIN_TOKENS = 200;\n uint256 internal constant MAX_TOKENS = 201;\n uint256 internal constant MAX_SWAP_FEE_PERCENTAGE = 202;\n uint256 internal constant MIN_SWAP_FEE_PERCENTAGE = 203;\n uint256 internal constant MINIMUM_BPT = 204;\n uint256 internal constant CALLER_NOT_VAULT = 205;\n uint256 internal constant UNINITIALIZED = 206;\n uint256 internal constant BPT_IN_MAX_AMOUNT = 207;\n uint256 internal constant BPT_OUT_MIN_AMOUNT = 208;\n uint256 internal constant EXPIRED_PERMIT = 209;\n uint256 internal constant NOT_TWO_TOKENS = 210;\n uint256 internal constant DISABLED = 211;\n\n // Pools\n uint256 internal constant MIN_AMP = 300;\n uint256 internal constant MAX_AMP = 301;\n uint256 internal constant MIN_WEIGHT = 302;\n uint256 internal constant MAX_STABLE_TOKENS = 303;\n uint256 internal constant MAX_IN_RATIO = 304;\n uint256 internal constant MAX_OUT_RATIO = 305;\n uint256 internal constant MIN_BPT_IN_FOR_TOKEN_OUT = 306;\n uint256 internal constant MAX_OUT_BPT_FOR_TOKEN_IN = 307;\n uint256 internal constant NORMALIZED_WEIGHT_INVARIANT = 308;\n uint256 internal constant INVALID_TOKEN = 309;\n uint256 internal constant UNHANDLED_JOIN_KIND = 310;\n uint256 internal constant ZERO_INVARIANT = 311;\n uint256 internal constant ORACLE_INVALID_SECONDS_QUERY = 312;\n uint256 internal constant ORACLE_NOT_INITIALIZED = 313;\n uint256 internal constant ORACLE_QUERY_TOO_OLD = 314;\n uint256 internal constant ORACLE_INVALID_INDEX = 315;\n uint256 internal constant ORACLE_BAD_SECS = 316;\n uint256 internal constant AMP_END_TIME_TOO_CLOSE = 317;\n uint256 internal constant AMP_ONGOING_UPDATE = 318;\n uint256 internal constant AMP_RATE_TOO_HIGH = 319;\n uint256 internal constant AMP_NO_ONGOING_UPDATE = 320;\n uint256 internal constant STABLE_INVARIANT_DIDNT_CONVERGE = 321;\n uint256 internal constant STABLE_GET_BALANCE_DIDNT_CONVERGE = 322;\n uint256 internal constant RELAYER_NOT_CONTRACT = 323;\n uint256 internal constant BASE_POOL_RELAYER_NOT_CALLED = 324;\n uint256 internal constant REBALANCING_RELAYER_REENTERED = 325;\n uint256 internal constant GRADUAL_UPDATE_TIME_TRAVEL = 326;\n uint256 internal constant SWAPS_DISABLED = 327;\n uint256 internal constant CALLER_IS_NOT_LBP_OWNER = 328;\n uint256 internal constant PRICE_RATE_OVERFLOW = 329;\n uint256 internal constant INVALID_JOIN_EXIT_KIND_WHILE_SWAPS_DISABLED = 330;\n uint256 internal constant WEIGHT_CHANGE_TOO_FAST = 331;\n uint256 internal constant LOWER_GREATER_THAN_UPPER_TARGET = 332;\n uint256 internal constant UPPER_TARGET_TOO_HIGH = 333;\n uint256 internal constant UNHANDLED_BY_LINEAR_POOL = 334;\n uint256 internal constant OUT_OF_TARGET_RANGE = 335;\n uint256 internal constant UNHANDLED_EXIT_KIND = 336;\n uint256 internal constant UNAUTHORIZED_EXIT = 337;\n uint256 internal constant MAX_MANAGEMENT_SWAP_FEE_PERCENTAGE = 338;\n uint256 internal constant UNHANDLED_BY_MANAGED_POOL = 339;\n uint256 internal constant UNHANDLED_BY_PHANTOM_POOL = 340;\n uint256 internal constant TOKEN_DOES_NOT_HAVE_RATE_PROVIDER = 341;\n uint256 internal constant INVALID_INITIALIZATION = 342;\n uint256 internal constant OUT_OF_NEW_TARGET_RANGE = 343;\n uint256 internal constant FEATURE_DISABLED = 344;\n uint256 internal constant UNINITIALIZED_POOL_CONTROLLER = 345;\n uint256 internal constant SET_SWAP_FEE_DURING_FEE_CHANGE = 346;\n uint256 internal constant SET_SWAP_FEE_PENDING_FEE_CHANGE = 347;\n uint256 internal constant CHANGE_TOKENS_DURING_WEIGHT_CHANGE = 348;\n uint256 internal constant CHANGE_TOKENS_PENDING_WEIGHT_CHANGE = 349;\n uint256 internal constant MAX_WEIGHT = 350;\n uint256 internal constant UNAUTHORIZED_JOIN = 351;\n uint256 internal constant MAX_MANAGEMENT_AUM_FEE_PERCENTAGE = 352;\n uint256 internal constant FRACTIONAL_TARGET = 353;\n uint256 internal constant ADD_OR_REMOVE_BPT = 354;\n uint256 internal constant INVALID_CIRCUIT_BREAKER_BOUNDS = 355;\n uint256 internal constant CIRCUIT_BREAKER_TRIPPED = 356;\n uint256 internal constant MALICIOUS_QUERY_REVERT = 357;\n uint256 internal constant JOINS_EXITS_DISABLED = 358;\n\n // Lib\n uint256 internal constant REENTRANCY = 400;\n uint256 internal constant SENDER_NOT_ALLOWED = 401;\n uint256 internal constant PAUSED = 402;\n uint256 internal constant PAUSE_WINDOW_EXPIRED = 403;\n uint256 internal constant MAX_PAUSE_WINDOW_DURATION = 404;\n uint256 internal constant MAX_BUFFER_PERIOD_DURATION = 405;\n uint256 internal constant INSUFFICIENT_BALANCE = 406;\n uint256 internal constant INSUFFICIENT_ALLOWANCE = 407;\n uint256 internal constant ERC20_TRANSFER_FROM_ZERO_ADDRESS = 408;\n uint256 internal constant ERC20_TRANSFER_TO_ZERO_ADDRESS = 409;\n uint256 internal constant ERC20_MINT_TO_ZERO_ADDRESS = 410;\n uint256 internal constant ERC20_BURN_FROM_ZERO_ADDRESS = 411;\n uint256 internal constant ERC20_APPROVE_FROM_ZERO_ADDRESS = 412;\n uint256 internal constant ERC20_APPROVE_TO_ZERO_ADDRESS = 413;\n uint256 internal constant ERC20_TRANSFER_EXCEEDS_ALLOWANCE = 414;\n uint256 internal constant ERC20_DECREASED_ALLOWANCE_BELOW_ZERO = 415;\n uint256 internal constant ERC20_TRANSFER_EXCEEDS_BALANCE = 416;\n uint256 internal constant ERC20_BURN_EXCEEDS_ALLOWANCE = 417;\n uint256 internal constant SAFE_ERC20_CALL_FAILED = 418;\n uint256 internal constant ADDRESS_INSUFFICIENT_BALANCE = 419;\n uint256 internal constant ADDRESS_CANNOT_SEND_VALUE = 420;\n uint256 internal constant SAFE_CAST_VALUE_CANT_FIT_INT256 = 421;\n uint256 internal constant GRANT_SENDER_NOT_ADMIN = 422;\n uint256 internal constant REVOKE_SENDER_NOT_ADMIN = 423;\n uint256 internal constant RENOUNCE_SENDER_NOT_ALLOWED = 424;\n uint256 internal constant BUFFER_PERIOD_EXPIRED = 425;\n uint256 internal constant CALLER_IS_NOT_OWNER = 426;\n uint256 internal constant NEW_OWNER_IS_ZERO = 427;\n uint256 internal constant CODE_DEPLOYMENT_FAILED = 428;\n uint256 internal constant CALL_TO_NON_CONTRACT = 429;\n uint256 internal constant LOW_LEVEL_CALL_FAILED = 430;\n uint256 internal constant NOT_PAUSED = 431;\n uint256 internal constant ADDRESS_ALREADY_ALLOWLISTED = 432;\n uint256 internal constant ADDRESS_NOT_ALLOWLISTED = 433;\n uint256 internal constant ERC20_BURN_EXCEEDS_BALANCE = 434;\n uint256 internal constant INVALID_OPERATION = 435;\n uint256 internal constant CODEC_OVERFLOW = 436;\n uint256 internal constant IN_RECOVERY_MODE = 437;\n uint256 internal constant NOT_IN_RECOVERY_MODE = 438;\n uint256 internal constant INDUCED_FAILURE = 439;\n uint256 internal constant EXPIRED_SIGNATURE = 440;\n uint256 internal constant MALFORMED_SIGNATURE = 441;\n uint256 internal constant SAFE_CAST_VALUE_CANT_FIT_UINT64 = 442;\n uint256 internal constant UNHANDLED_FEE_TYPE = 443;\n uint256 internal constant BURN_FROM_ZERO = 444;\n\n // Vault\n uint256 internal constant INVALID_POOL_ID = 500;\n uint256 internal constant CALLER_NOT_POOL = 501;\n uint256 internal constant SENDER_NOT_ASSET_MANAGER = 502;\n uint256 internal constant USER_DOESNT_ALLOW_RELAYER = 503;\n uint256 internal constant INVALID_SIGNATURE = 504;\n uint256 internal constant EXIT_BELOW_MIN = 505;\n uint256 internal constant JOIN_ABOVE_MAX = 506;\n uint256 internal constant SWAP_LIMIT = 507;\n uint256 internal constant SWAP_DEADLINE = 508;\n uint256 internal constant CANNOT_SWAP_SAME_TOKEN = 509;\n uint256 internal constant UNKNOWN_AMOUNT_IN_FIRST_SWAP = 510;\n uint256 internal constant MALCONSTRUCTED_MULTIHOP_SWAP = 511;\n uint256 internal constant INTERNAL_BALANCE_OVERFLOW = 512;\n uint256 internal constant INSUFFICIENT_INTERNAL_BALANCE = 513;\n uint256 internal constant INVALID_ETH_INTERNAL_BALANCE = 514;\n uint256 internal constant INVALID_POST_LOAN_BALANCE = 515;\n uint256 internal constant INSUFFICIENT_ETH = 516;\n uint256 internal constant UNALLOCATED_ETH = 517;\n uint256 internal constant ETH_TRANSFER = 518;\n uint256 internal constant CANNOT_USE_ETH_SENTINEL = 519;\n uint256 internal constant TOKENS_MISMATCH = 520;\n uint256 internal constant TOKEN_NOT_REGISTERED = 521;\n uint256 internal constant TOKEN_ALREADY_REGISTERED = 522;\n uint256 internal constant TOKENS_ALREADY_SET = 523;\n uint256 internal constant TOKENS_LENGTH_MUST_BE_2 = 524;\n uint256 internal constant NONZERO_TOKEN_BALANCE = 525;\n uint256 internal constant BALANCE_TOTAL_OVERFLOW = 526;\n uint256 internal constant POOL_NO_TOKENS = 527;\n uint256 internal constant INSUFFICIENT_FLASH_LOAN_BALANCE = 528;\n\n // Fees\n uint256 internal constant SWAP_FEE_PERCENTAGE_TOO_HIGH = 600;\n uint256 internal constant FLASH_LOAN_FEE_PERCENTAGE_TOO_HIGH = 601;\n uint256 internal constant INSUFFICIENT_FLASH_LOAN_FEE_AMOUNT = 602;\n uint256 internal constant AUM_FEE_PERCENTAGE_TOO_HIGH = 603;\n\n // FeeSplitter\n uint256 internal constant SPLITTER_FEE_PERCENTAGE_TOO_HIGH = 700;\n\n // Misc\n uint256 internal constant UNIMPLEMENTED = 998;\n uint256 internal constant SHOULD_NOT_HAPPEN = 999;\n}\n" + }, + "contracts/utils/DepositContractUtils.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract DepositContractUtils {\n function calculateDepositDataRoot(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature\n ) public pure returns (bytes32 node) {\n uint256 deposit_amount = 32 ether / 1 gwei;\n bytes memory amount = to_little_endian_64(uint64(deposit_amount));\n\n // Compute deposit data root (`DepositData` hash tree root)\n bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, bytes16(0)));\n bytes32 signature_root = sha256(\n abi.encodePacked(\n sha256(abi.encodePacked(signature[:64])),\n sha256(abi.encodePacked(signature[64:], bytes32(0)))\n )\n );\n node = sha256(\n abi.encodePacked(\n sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)),\n sha256(abi.encodePacked(amount, bytes24(0), signature_root))\n )\n );\n }\n\n function to_little_endian_64(uint64 value)\n internal\n pure\n returns (bytes memory ret)\n {\n ret = new bytes(8);\n bytes8 bytesValue = bytes8(value);\n // Byteswapping during copying to bytes.\n ret[0] = bytesValue[7];\n ret[1] = bytesValue[6];\n ret[2] = bytesValue[5];\n ret[3] = bytesValue[4];\n ret[4] = bytesValue[3];\n ret[5] = bytesValue[2];\n ret[6] = bytesValue[1];\n ret[7] = bytesValue[0];\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/utils/InitializableERC20Detailed.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @dev Optional functions from the ERC20 standard.\n * Converted from openzeppelin/contracts/token/ERC20/ERC20Detailed.sol\n * @author Origin Protocol Inc\n */\nabstract contract InitializableERC20Detailed is IERC20 {\n // Storage gap to skip storage from prior to OUSD reset\n uint256[100] private _____gap;\n\n string private _name;\n string private _symbol;\n uint8 private _decimals;\n\n /**\n * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of\n * these values are immutable: they can only be set once during\n * construction.\n * @notice To avoid variable shadowing appended `Arg` after arguments name.\n */\n function _initialize(\n string memory nameArg,\n string memory symbolArg,\n uint8 decimalsArg\n ) internal {\n _name = nameArg;\n _symbol = symbolArg;\n _decimals = decimalsArg;\n }\n\n /**\n * @notice Returns the name of the token.\n */\n function name() public view returns (string memory) {\n return _name;\n }\n\n /**\n * @notice Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view returns (string memory) {\n return _symbol;\n }\n\n /**\n * @notice Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5,05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei.\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view returns (uint8) {\n return _decimals;\n }\n}\n" + }, + "contracts/utils/PRBMath.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// Copied from the PRBMath library\n// https://github.com/PaulRBerg/prb-math/blob/main/src/Common.sol\n\n/// @notice Calculates the square root of x using the Babylonian method.\n///\n/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.\n///\n/// Notes:\n/// - If x is not a perfect square, the result is rounded down.\n/// - Credits to OpenZeppelin for the explanations in comments below.\n///\n/// @param x The uint256 number for which to calculate the square root.\n/// @return result The result as a uint256.\n/// @custom:smtchecker abstract-function-nondet\nfunction sqrt(uint256 x) pure returns (uint256 result) {\n if (x == 0) {\n return 0;\n }\n\n // For our first guess, we calculate the biggest power of 2 which is smaller than the square root of x.\n //\n // We know that the \"msb\" (most significant bit) of x is a power of 2 such that we have:\n //\n // $$\n // msb(x) <= x <= 2*msb(x)$\n // $$\n //\n // We write $msb(x)$ as $2^k$, and we get:\n //\n // $$\n // k = log_2(x)\n // $$\n //\n // Thus, we can write the initial inequality as:\n //\n // $$\n // 2^{log_2(x)} <= x <= 2*2^{log_2(x)+1} \\\\\n // sqrt(2^k) <= sqrt(x) < sqrt(2^{k+1}) \\\\\n // 2^{k/2} <= sqrt(x) < 2^{(k+1)/2} <= 2^{(k/2)+1}\n // $$\n //\n // Consequently, $2^{log_2(x) /2} is a good first approximation of sqrt(x) with at least one correct bit.\n uint256 xAux = uint256(x);\n result = 1;\n if (xAux >= 2**128) {\n xAux >>= 128;\n result <<= 64;\n }\n if (xAux >= 2**64) {\n xAux >>= 64;\n result <<= 32;\n }\n if (xAux >= 2**32) {\n xAux >>= 32;\n result <<= 16;\n }\n if (xAux >= 2**16) {\n xAux >>= 16;\n result <<= 8;\n }\n if (xAux >= 2**8) {\n xAux >>= 8;\n result <<= 4;\n }\n if (xAux >= 2**4) {\n xAux >>= 4;\n result <<= 2;\n }\n if (xAux >= 2**2) {\n result <<= 1;\n }\n\n // At this point, `result` is an estimation with at least one bit of precision. We know the true value has at\n // most 128 bits, since it is the square root of a uint256. Newton's method converges quadratically (precision\n // doubles at every iteration). We thus need at most 7 iteration to turn our partial result with one bit of\n // precision into the expected uint128 result.\n unchecked {\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n\n // If x is not a perfect square, round the result toward zero.\n uint256 roundedResult = x / result;\n if (result >= roundedResult) {\n result = roundedResult;\n }\n }\n}\n" + }, + "contracts/utils/StableMath.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\n// Based on StableMath from Stability Labs Pty. Ltd.\n// https://github.com/mstable/mStable-contracts/blob/master/contracts/shared/StableMath.sol\n\nlibrary StableMath {\n using SafeMath for uint256;\n\n /**\n * @dev Scaling unit for use in specific calculations,\n * where 1 * 10**18, or 1e18 represents a unit '1'\n */\n uint256 private constant FULL_SCALE = 1e18;\n\n /***************************************\n Helpers\n ****************************************/\n\n /**\n * @dev Adjust the scale of an integer\n * @param to Decimals to scale to\n * @param from Decimals to scale from\n */\n function scaleBy(\n uint256 x,\n uint256 to,\n uint256 from\n ) internal pure returns (uint256) {\n if (to > from) {\n x = x.mul(10**(to - from));\n } else if (to < from) {\n // slither-disable-next-line divide-before-multiply\n x = x.div(10**(from - to));\n }\n return x;\n }\n\n /***************************************\n Precise Arithmetic\n ****************************************/\n\n /**\n * @dev Multiplies two precise units, and then truncates by the full scale\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit\n */\n function mulTruncate(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulTruncateScale(x, y, FULL_SCALE);\n }\n\n /**\n * @dev Multiplies two precise units, and then truncates by the given scale. For example,\n * when calculating 90% of 10e18, (10e18 * 9e17) / 1e18 = (9e36) / 1e18 = 9e18\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @param scale Scale unit\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit\n */\n function mulTruncateScale(\n uint256 x,\n uint256 y,\n uint256 scale\n ) internal pure returns (uint256) {\n // e.g. assume scale = fullScale\n // z = 10e18 * 9e17 = 9e36\n uint256 z = x.mul(y);\n // return 9e36 / 1e18 = 9e18\n return z.div(scale);\n }\n\n /**\n * @dev Multiplies two precise units, and then truncates by the full scale, rounding up the result\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit, rounded up to the closest base unit.\n */\n function mulTruncateCeil(uint256 x, uint256 y)\n internal\n pure\n returns (uint256)\n {\n // e.g. 8e17 * 17268172638 = 138145381104e17\n uint256 scaled = x.mul(y);\n // e.g. 138145381104e17 + 9.99...e17 = 138145381113.99...e17\n uint256 ceil = scaled.add(FULL_SCALE.sub(1));\n // e.g. 13814538111.399...e18 / 1e18 = 13814538111\n return ceil.div(FULL_SCALE);\n }\n\n /**\n * @dev Precisely divides two units, by first scaling the left hand operand. Useful\n * for finding percentage weightings, i.e. 8e18/10e18 = 80% (or 8e17)\n * @param x Left hand input to division\n * @param y Right hand input to division\n * @return Result after multiplying the left operand by the scale, and\n * executing the division on the right hand input.\n */\n function divPrecisely(uint256 x, uint256 y)\n internal\n pure\n returns (uint256)\n {\n // e.g. 8e18 * 1e18 = 8e36\n uint256 z = x.mul(FULL_SCALE);\n // e.g. 8e36 / 10e18 = 8e17\n return z.div(y);\n }\n}\n" + }, + "contracts/vault/OETHBaseVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"./OETHVaultAdmin.sol\";\n\n/**\n * @title OETH Base VaultAdmin Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBaseVaultAdmin is OETHVaultAdmin {\n constructor(address _weth) OETHVaultAdmin(_weth) {}\n}\n" + }, + "contracts/vault/OETHBaseVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { OETHVaultCore } from \"./OETHVaultCore.sol\";\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\n\n/**\n * @title OETH Base VaultCore Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBaseVaultCore is OETHVaultCore {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(address _weth) OETHVaultCore(_weth) {}\n\n // @inheritdoc OETHVaultCore\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n override\n {\n // Only Strategist or Governor can redeem using the Vault for now.\n // We don't have the onlyGovernorOrStrategist modifier on VaultCore.\n // Since we won't be using that modifier anywhere in the VaultCore as well,\n // the check has been added inline instead of moving it to VaultStorage.\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n\n super._redeem(_amount, _minimumUnitAmount);\n }\n}\n" + }, + "contracts/vault/OETHBaseZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\n\ncontract OETHBaseZapper {\n IERC20 public immutable oethb;\n IERC4626 public immutable woethb;\n IVault public immutable vault;\n\n IWETH9 public constant weth =\n IWETH9(0x4200000000000000000000000000000000000006);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(\n address _oethb,\n address _woethb,\n address _vault\n ) {\n oethb = IERC20(_oethb);\n woethb = IERC4626(_woethb);\n vault = IVault(_vault);\n\n weth.approve(address(_vault), type(uint256).max);\n IERC20(_oethb).approve(_woethb, type(uint256).max);\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return.\n * Will verify that the user is sent 1:1 for ETH.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit ETH and receive superOETHb in return\n * Will verify that the user is sent 1:1 for ETH.\n * @return Amount of OETH sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap ETH\n weth.deposit{ value: balance }();\n\n // Mint with WETH\n return _mint(balance, msg.sender);\n }\n\n /**\n * @dev Deposit ETH and receive superOETHb in return\n * @param minReceived min amount of wsuperOETHb to receive\n * @return Amount of wsuperOETHb sent to user\n */\n function depositETHForWrappedTokens(uint256 minReceived)\n external\n payable\n returns (uint256)\n {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap ETH\n weth.deposit{ value: balance }();\n\n // Mint with WETH\n uint256 mintedOethb = _mint(balance, address(this));\n\n // Wrap superOETHb into wsuperOETHb\n uint256 mintedWoethb = woethb.deposit(mintedOethb, msg.sender);\n\n require(mintedWoethb >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWoethb;\n }\n\n /**\n * @dev Deposit WETH and receive superOETHb in return\n * @param wethAmount Amount of WETH to deposit\n * @param minReceived min amount of wsuperOETHb to receive\n * @return Amount of wsuperOETHb sent to user\n */\n function depositWETHForWrappedTokens(\n uint256 wethAmount,\n uint256 minReceived\n ) external returns (uint256) {\n // slither-disable-next-line unchecked-transfer unused-return\n weth.transferFrom(msg.sender, address(this), wethAmount);\n\n emit Zap(msg.sender, address(weth), wethAmount);\n\n // Mint with WETH\n uint256 mintedOethb = _mint(wethAmount, address(this));\n\n // Wrap superOETHb into wsuperOETHb\n uint256 mintedWoethb = woethb.deposit(mintedOethb, msg.sender);\n\n require(mintedWoethb >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWoethb;\n }\n\n /**\n * @dev Internal function to mint superOETHb with WETH\n * @param minOETH Minimum amount of OETH to for user to receive\n * @param recipient Address that receives the tokens\n * @return Amount of OETH sent to user\n */\n function _mint(uint256 minOETH, address recipient)\n internal\n returns (uint256)\n {\n uint256 toMint = weth.balanceOf(address(this));\n vault.mint(address(weth), toMint, minOETH);\n uint256 mintedAmount = oethb.balanceOf(address(this));\n require(mintedAmount >= minOETH, \"Zapper: not enough minted\");\n\n if (recipient != address(this)) {\n require(oethb.transfer(recipient, mintedAmount));\n }\n\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/OETHVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Vault } from \"./Vault.sol\";\n\n/**\n * @title OETH Vault Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVault is Vault {\n\n}\n" + }, + "contracts/vault/OETHVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { VaultAdmin } from \"./VaultAdmin.sol\";\n\n/**\n * @title OETH VaultAdmin Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVaultAdmin is VaultAdmin {\n using SafeERC20 for IERC20;\n\n address public immutable weth;\n\n constructor(address _weth) {\n weth = _weth;\n }\n\n /**\n * @notice Adds a strategy to the mint whitelist.\n * Reverts if strategy isn't approved on Vault.\n * @param strategyAddr Strategy address\n */\n function addStrategyToMintWhitelist(address strategyAddr)\n external\n onlyGovernor\n {\n require(strategies[strategyAddr].isSupported, \"Strategy not approved\");\n\n require(\n !isMintWhitelistedStrategy[strategyAddr],\n \"Already whitelisted\"\n );\n\n isMintWhitelistedStrategy[strategyAddr] = true;\n\n emit StrategyAddedToMintWhitelist(strategyAddr);\n }\n\n /**\n * @notice Removes a strategy from the mint whitelist.\n * @param strategyAddr Strategy address\n */\n function removeStrategyFromMintWhitelist(address strategyAddr)\n external\n onlyGovernor\n {\n // Intentionally skipping `strategies.isSupported` check since\n // we may wanna remove an address even after removing the strategy\n\n require(isMintWhitelistedStrategy[strategyAddr], \"Not whitelisted\");\n\n isMintWhitelistedStrategy[strategyAddr] = false;\n\n emit StrategyRemovedFromMintWhitelist(strategyAddr);\n }\n\n /// @dev Simplified version of the deposit function as WETH is the only supported asset.\n function _depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal override {\n require(\n strategies[_strategyToAddress].isSupported,\n \"Invalid to Strategy\"\n );\n require(\n _assets.length == 1 && _amounts.length == 1 && _assets[0] == weth,\n \"Only WETH is supported\"\n );\n\n // Check the there is enough WETH to transfer once the WETH reserved for the withdrawal queue is accounted for\n require(_amounts[0] <= _wethAvailable(), \"Not enough WETH available\");\n\n // Send required amount of funds to the strategy\n IERC20(weth).safeTransfer(_strategyToAddress, _amounts[0]);\n\n // Deposit all the funds that have been sent to the strategy\n IStrategy(_strategyToAddress).depositAll();\n }\n\n function _withdrawFromStrategy(\n address _recipient,\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal override {\n super._withdrawFromStrategy(\n _recipient,\n _strategyFromAddress,\n _assets,\n _amounts\n );\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n function _withdrawAllFromStrategy(address _strategyAddr) internal override {\n super._withdrawAllFromStrategy(_strategyAddr);\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n function _withdrawAllFromStrategies() internal override {\n super._withdrawAllFromStrategies();\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n /// @dev Calculate how much WETH in the vault is not reserved for the withdrawal queue.\n // That is, it is available to be redeemed or deposited into a strategy.\n function _wethAvailable() internal view returns (uint256 wethAvailable) {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n uint256 outstandingWithdrawals = queue.queued - queue.claimed;\n\n // The amount of sitting in WETH in the vault\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // If there is not enough WETH in the vault to cover the outstanding withdrawals\n if (wethBalance <= outstandingWithdrawals) {\n return 0;\n }\n\n return wethBalance - outstandingWithdrawals;\n }\n\n function _swapCollateral(\n address,\n address,\n uint256,\n uint256,\n bytes calldata\n ) internal pure override returns (uint256) {\n revert(\"Collateral swap not supported\");\n }\n}\n" + }, + "contracts/vault/OETHVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { VaultCore } from \"./VaultCore.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\n\n/**\n * @title OETH VaultCore Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVaultCore is VaultCore {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n address public immutable weth;\n uint256 public wethAssetIndex;\n\n // For future use (because OETHBaseVaultCore inherits from this)\n uint256[50] private __gap;\n\n constructor(address _weth) {\n weth = _weth;\n }\n\n /**\n * @dev Caches WETH's index in `allAssets` variable.\n * Reduces gas usage by redeem by caching that.\n */\n function cacheWETHAssetIndex() external onlyGovernor {\n uint256 assetCount = allAssets.length;\n for (uint256 i; i < assetCount; ++i) {\n if (allAssets[i] == weth) {\n wethAssetIndex = i;\n break;\n }\n }\n\n require(allAssets[wethAssetIndex] == weth, \"Invalid WETH Asset Index\");\n }\n\n // @inheritdoc VaultCore\n function mintForStrategy(uint256 amount)\n external\n override\n whenNotCapitalPaused\n {\n require(\n strategies[msg.sender].isSupported == true,\n \"Unsupported strategy\"\n );\n require(\n isMintWhitelistedStrategy[msg.sender] == true,\n \"Not whitelisted strategy\"\n );\n\n emit Mint(msg.sender, amount);\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, amount);\n }\n\n // @inheritdoc VaultCore\n function burnForStrategy(uint256 amount)\n external\n override\n whenNotCapitalPaused\n {\n require(\n strategies[msg.sender].isSupported == true,\n \"Unsupported strategy\"\n );\n require(\n isMintWhitelistedStrategy[msg.sender] == true,\n \"Not whitelisted strategy\"\n );\n\n emit Redeem(msg.sender, amount);\n\n // Burn OTokens\n oUSD.burn(msg.sender, amount);\n }\n\n // @inheritdoc VaultCore\n // slither-disable-start reentrancy-no-eth\n function _mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) internal virtual override {\n require(_asset == weth, \"Unsupported asset for minting\");\n require(_amount > 0, \"Amount must be greater than 0\");\n require(\n _amount >= _minimumOusdAmount,\n \"Mint amount lower than minimum\"\n );\n\n emit Mint(msg.sender, _amount);\n\n // Rebase must happen before any transfers occur.\n if (!rebasePaused && _amount >= rebaseThreshold) {\n _rebase();\n }\n\n // Mint oTokens\n oUSD.mint(msg.sender, _amount);\n\n // Transfer the deposited coins to the vault\n IERC20(_asset).safeTransferFrom(msg.sender, address(this), _amount);\n\n // Give priority to the withdrawal queue for the new WETH liquidity\n _addWithdrawalQueueLiquidity();\n\n // Auto-allocate if necessary\n if (_amount >= autoAllocateThreshold) {\n _allocate();\n }\n }\n\n // slither-disable-end reentrancy-no-eth\n\n // @inheritdoc VaultCore\n function _calculateRedeemOutputs(uint256 _amount)\n internal\n view\n virtual\n override\n returns (uint256[] memory outputs)\n {\n // Overrides `VaultCore._calculateRedeemOutputs` to redeem with only\n // WETH instead of LST-mix. Doesn't change the function signature\n // for backward compatibility\n\n // Calculate redeem fee\n if (redeemFeeBps > 0) {\n uint256 redeemFee = _amount.mulTruncateScale(redeemFeeBps, 1e4);\n _amount = _amount - redeemFee;\n }\n\n // Ensure that the WETH index is cached\n uint256 _wethAssetIndex = wethAssetIndex;\n require(\n allAssets[_wethAssetIndex] == weth,\n \"WETH Asset index not cached\"\n );\n\n outputs = new uint256[](allAssets.length);\n outputs[_wethAssetIndex] = _amount;\n }\n\n // @inheritdoc VaultCore\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n override\n {\n // Override `VaultCore._redeem` to simplify it. Gets rid of oracle\n // usage and looping through all assets for LST-mix redeem. Instead\n // does a simple WETH-only redeem.\n emit Redeem(msg.sender, _amount);\n\n if (_amount == 0) {\n return;\n }\n\n // Amount excluding fees\n // No fee for the strategist or the governor, makes it easier to do operations\n uint256 amountMinusFee = (msg.sender == strategistAddr || isGovernor())\n ? _amount\n : _calculateRedeemOutputs(_amount)[wethAssetIndex];\n\n require(\n amountMinusFee >= _minimumUnitAmount,\n \"Redeem amount lower than minimum\"\n );\n\n // Is there enough WETH in the Vault available after accounting for the withdrawal queue\n require(_wethAvailable() >= amountMinusFee, \"Liquidity error\");\n\n // Transfer WETH minus the fee to the redeemer\n IERC20(weth).safeTransfer(msg.sender, amountMinusFee);\n\n // Burn OETH from user (including fees)\n oUSD.burn(msg.sender, _amount);\n\n // Prevent insolvency\n _postRedeem(_amount);\n }\n\n /**\n * @notice Request an asynchronous withdrawal of WETH in exchange for OETH.\n * The OETH is burned on request and the WETH is transferred to the withdrawer on claim.\n * This request can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal this request's `queued` amount.\n * There is a minimum of 10 minutes before a request can be claimed. After that, the request just needs\n * enough WETH liquidity in the Vault to satisfy all the outstanding requests to that point in the queue.\n * OETH is converted to WETH at 1:1.\n * @param _amount Amount of OETH to burn.\n * @return requestId Unique ID for the withdrawal request\n * @return queued Cumulative total of all WETH queued including already claimed requests.\n */\n function requestWithdrawal(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256 requestId, uint256 queued)\n {\n require(withdrawalClaimDelay > 0, \"Async withdrawals not enabled\");\n\n // The check that the requester has enough OETH is done in to later burn call\n\n requestId = withdrawalQueueMetadata.nextWithdrawalIndex;\n queued = withdrawalQueueMetadata.queued + _amount;\n\n // Store the next withdrawal request\n withdrawalQueueMetadata.nextWithdrawalIndex = SafeCast.toUint128(\n requestId + 1\n );\n // Store the updated queued amount which reserves WETH in the withdrawal queue\n // and reduces the vault's total assets\n withdrawalQueueMetadata.queued = SafeCast.toUint128(queued);\n // Store the user's withdrawal request\n withdrawalRequests[requestId] = WithdrawalRequest({\n withdrawer: msg.sender,\n claimed: false,\n timestamp: uint40(block.timestamp),\n amount: SafeCast.toUint128(_amount),\n queued: SafeCast.toUint128(queued)\n });\n\n // Burn the user's OETH\n oUSD.burn(msg.sender, _amount);\n\n // Prevent withdrawal if the vault is solvent by more than the allowed percentage\n _postRedeem(_amount);\n\n emit WithdrawalRequested(msg.sender, requestId, _amount, queued);\n }\n\n // slither-disable-start reentrancy-no-eth\n /**\n * @notice Claim a previously requested withdrawal once it is claimable.\n * This request can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal this request's `queued` amount and 10 minutes has passed.\n * If the requests is not claimable, the transaction will revert with `Queue pending liquidity`.\n * If the request is not older than 10 minutes, the transaction will revert with `Claim delay not met`.\n * OETH is converted to WETH at 1:1.\n * @param _requestId Unique ID for the withdrawal request\n * @return amount Amount of WETH transferred to the withdrawer\n */\n function claimWithdrawal(uint256 _requestId)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256 amount)\n {\n // Try and get more liquidity if there is not enough available\n if (\n withdrawalRequests[_requestId].queued >\n withdrawalQueueMetadata.claimable\n ) {\n // Add any WETH to the withdrawal queue\n // this needs to remain here as:\n // - Vault can be funded and `addWithdrawalQueueLiquidity` is not externally called\n // - funds can be withdrawn from a strategy\n //\n // Those funds need to be added to withdrawal queue liquidity\n _addWithdrawalQueueLiquidity();\n }\n\n amount = _claimWithdrawal(_requestId);\n\n // transfer WETH from the vault to the withdrawer\n IERC20(weth).safeTransfer(msg.sender, amount);\n\n // Prevent insolvency\n _postRedeem(amount);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @notice Claim a previously requested withdrawals once they are claimable.\n * This requests can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal each request's `queued` amount and 10 minutes has passed.\n * If one of the requests is not claimable, the whole transaction will revert with `Queue pending liquidity`.\n * If one of the requests is not older than 10 minutes,\n * the whole transaction will revert with `Claim delay not met`.\n * @param _requestIds Unique ID of each withdrawal request\n * @return amounts Amount of WETH received for each request\n * @return totalAmount Total amount of WETH transferred to the withdrawer\n */\n function claimWithdrawals(uint256[] calldata _requestIds)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256[] memory amounts, uint256 totalAmount)\n {\n // Add any WETH to the withdrawal queue\n // this needs to remain here as:\n // - Vault can be funded and `addWithdrawalQueueLiquidity` is not externally called\n // - funds can be withdrawn from a strategy\n //\n // Those funds need to be added to withdrawal queue liquidity\n _addWithdrawalQueueLiquidity();\n\n amounts = new uint256[](_requestIds.length);\n for (uint256 i; i < _requestIds.length; ++i) {\n amounts[i] = _claimWithdrawal(_requestIds[i]);\n totalAmount += amounts[i];\n }\n\n // transfer all the claimed WETH from the vault to the withdrawer\n IERC20(weth).safeTransfer(msg.sender, totalAmount);\n\n // Prevent insolvency\n _postRedeem(totalAmount);\n }\n\n function _claimWithdrawal(uint256 requestId)\n internal\n returns (uint256 amount)\n {\n require(withdrawalClaimDelay > 0, \"Async withdrawals not enabled\");\n\n // Load the structs from storage into memory\n WithdrawalRequest memory request = withdrawalRequests[requestId];\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n require(\n request.timestamp + withdrawalClaimDelay <= block.timestamp,\n \"Claim delay not met\"\n );\n // If there isn't enough reserved liquidity in the queue to claim\n require(request.queued <= queue.claimable, \"Queue pending liquidity\");\n require(request.withdrawer == msg.sender, \"Not requester\");\n require(request.claimed == false, \"Already claimed\");\n\n // Store the request as claimed\n withdrawalRequests[requestId].claimed = true;\n // Store the updated claimed amount\n withdrawalQueueMetadata.claimed = queue.claimed + request.amount;\n\n emit WithdrawalClaimed(msg.sender, requestId, request.amount);\n\n return request.amount;\n }\n\n /// @notice Adds WETH to the withdrawal queue if there is a funding shortfall.\n /// @dev is called from the Native Staking strategy when validator withdrawals are processed.\n /// It also called before any WETH is allocated to a strategy.\n function addWithdrawalQueueLiquidity() external {\n _addWithdrawalQueueLiquidity();\n }\n\n /// @dev Adds WETH to the withdrawal queue if there is a funding shortfall.\n /// This assumes 1 WETH equal 1 OETH.\n function _addWithdrawalQueueLiquidity()\n internal\n returns (uint256 addedClaimable)\n {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // Check if the claimable WETH is less than the queued amount\n uint256 queueShortfall = queue.queued - queue.claimable;\n\n // No need to do anything is the withdrawal queue is full funded\n if (queueShortfall == 0) {\n return 0;\n }\n\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // Of the claimable withdrawal requests, how much is unclaimed?\n // That is, the amount of WETH that is currently allocated for the withdrawal queue\n uint256 allocatedWeth = queue.claimable - queue.claimed;\n\n // If there is no unallocated WETH then there is nothing to add to the queue\n if (wethBalance <= allocatedWeth) {\n return 0;\n }\n\n uint256 unallocatedWeth = wethBalance - allocatedWeth;\n\n // the new claimable amount is the smaller of the queue shortfall or unallocated weth\n addedClaimable = queueShortfall < unallocatedWeth\n ? queueShortfall\n : unallocatedWeth;\n uint256 newClaimable = queue.claimable + addedClaimable;\n\n // Store the new claimable amount back to storage\n withdrawalQueueMetadata.claimable = SafeCast.toUint128(newClaimable);\n\n // emit a WithdrawalClaimable event\n emit WithdrawalClaimable(newClaimable, addedClaimable);\n }\n\n /***************************************\n View Functions\n ****************************************/\n\n /// @dev Calculate how much WETH in the vault is not reserved for the withdrawal queue.\n // That is, it is available to be redeemed or deposited into a strategy.\n function _wethAvailable() internal view returns (uint256 wethAvailable) {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n uint256 outstandingWithdrawals = queue.queued - queue.claimed;\n\n // The amount of sitting in WETH in the vault\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // If there is not enough WETH in the vault to cover the outstanding withdrawals\n if (wethBalance <= outstandingWithdrawals) {\n return 0;\n }\n\n return wethBalance - outstandingWithdrawals;\n }\n\n /// @dev Get the balance of an asset held in Vault and all strategies\n /// less any WETH that is reserved for the withdrawal queue.\n /// WETH is the only asset that can return a non-zero balance.\n /// All other assets will return 0 even if there is some dust amounts left in the Vault.\n /// For example, there is 1 wei left of stETH in the OETH Vault but will return 0 in this function.\n ///\n /// If there is not enough WETH in the vault and all strategies to cover all outstanding\n /// withdrawal requests then return a WETH balance of 0\n function _checkBalance(address _asset)\n internal\n view\n override\n returns (uint256 balance)\n {\n if (_asset != weth) {\n return 0;\n }\n\n // Get the WETH in the vault and the strategies\n balance = super._checkBalance(_asset);\n\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // If the vault becomes insolvent enough that the total value in the vault and all strategies\n // is less than the outstanding withdrawals.\n // For example, there was a mass slashing event and most users request a withdrawal.\n if (balance + queue.claimed < queue.queued) {\n return 0;\n }\n\n // Need to remove WETH that is reserved for the withdrawal queue\n return balance + queue.claimed - queue.queued;\n }\n\n /**\n * @notice Allocate unallocated funds on Vault to strategies.\n **/\n function allocate() external override whenNotCapitalPaused nonReentrant {\n // Add any unallocated WETH to the withdrawal queue first\n _addWithdrawalQueueLiquidity();\n\n _allocate();\n }\n\n /// @dev Allocate WETH to the default WETH strategy if there is excess to the Vault buffer.\n /// This is called from either `mint` or `allocate` and assumes `_addWithdrawalQueueLiquidity`\n /// has been called before this function.\n function _allocate() internal override {\n // No need to do anything if no default strategy for WETH\n address depositStrategyAddr = assetDefaultStrategies[weth];\n if (depositStrategyAddr == address(0)) return;\n\n uint256 wethAvailableInVault = _wethAvailable();\n // No need to do anything if there isn't any WETH in the vault to allocate\n if (wethAvailableInVault == 0) return;\n\n // Calculate the target buffer for the vault using the total supply\n uint256 totalSupply = oUSD.totalSupply();\n uint256 targetBuffer = totalSupply.mulTruncate(vaultBuffer);\n\n // If available WETH in the Vault is below or equal the target buffer then there's nothing to allocate\n if (wethAvailableInVault <= targetBuffer) return;\n\n // The amount of assets to allocate to the default strategy\n uint256 allocateAmount = wethAvailableInVault - targetBuffer;\n\n IStrategy strategy = IStrategy(depositStrategyAddr);\n // Transfer WETH to the strategy and call the strategy's deposit function\n IERC20(weth).safeTransfer(address(strategy), allocateAmount);\n strategy.deposit(weth, allocateAmount);\n\n emit AssetAllocated(weth, depositStrategyAddr, allocateAmount);\n }\n\n /// @dev The total value of all WETH held by the vault and all its strategies\n /// less any WETH that is reserved for the withdrawal queue.\n ///\n // If there is not enough WETH in the vault and all strategies to cover all outstanding\n // withdrawal requests then return a total value of 0.\n function _totalValue() internal view override returns (uint256 value) {\n // As WETH is the only asset, just return the WETH balance\n return _checkBalance(weth);\n }\n\n /// @dev Only WETH is supported in the OETH Vault so return the WETH balance only\n /// Any ETH balances in the Vault will be ignored.\n /// Amounts from previously supported vault assets will also be ignored.\n /// For example, there is 1 wei left of stETH in the OETH Vault but is will be ignored.\n function _totalValueInVault()\n internal\n view\n override\n returns (uint256 value)\n {\n value = IERC20(weth).balanceOf(address(this));\n }\n}\n" + }, + "contracts/vault/OETHZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { ISfrxETH } from \"../interfaces/ISfrxETH.sol\";\n\ncontract OETHZapper {\n IERC20 public immutable oeth;\n IVault public immutable vault;\n\n IWETH9 public constant weth =\n IWETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);\n IERC20 public constant frxeth =\n IERC20(0x5E8422345238F34275888049021821E8E08CAa1f);\n ISfrxETH public constant sfrxeth =\n ISfrxETH(0xac3E018457B222d93114458476f3E3416Abbe38F);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(address _oeth, address _vault) {\n oeth = IERC20(_oeth);\n vault = IVault(_vault);\n\n weth.approve(address(_vault), type(uint256).max);\n frxeth.approve(address(_vault), type(uint256).max);\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return.\n * Will verify that the user is sent 1:1 for ETH.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return\n * Will verify that the user is sent 1:1 for ETH.\n * @return Amount of OETH sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n weth.deposit{ value: balance }();\n emit Zap(msg.sender, ETH_MARKER, balance);\n return _mint(address(weth), balance);\n }\n\n /**\n * @dev Deposit SFRXETH to the vault and receive OETH in return\n * @param amount Amount of SFRXETH to deposit\n * @param minOETH Minimum amount of OETH to receive\n * @return Amount of OETH sent to user\n */\n function depositSFRXETH(uint256 amount, uint256 minOETH)\n external\n returns (uint256)\n {\n sfrxeth.redeem(amount, address(this), msg.sender);\n emit Zap(msg.sender, address(sfrxeth), amount);\n return _mint(address(frxeth), minOETH);\n }\n\n /**\n * @dev Internal function to mint OETH from an asset\n * @param asset Address of asset for the vault to mint from\n * @param minOETH Minimum amount of OETH to for user to receive\n * @return Amount of OETH sent to user\n */\n function _mint(address asset, uint256 minOETH) internal returns (uint256) {\n uint256 toMint = IERC20(asset).balanceOf(address(this));\n vault.mint(asset, toMint, minOETH);\n uint256 mintedAmount = oeth.balanceOf(address(this));\n require(mintedAmount >= minOETH, \"Zapper: not enough minted\");\n require(oeth.transfer(msg.sender, mintedAmount));\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/OSonicVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"./OETHVaultAdmin.sol\";\n\n/**\n * @title Origin Sonic VaultAdmin contract on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonicVaultAdmin is OETHVaultAdmin {\n /// @param _wS Sonic's Wrapped S token\n constructor(address _wS) OETHVaultAdmin(_wS) {}\n\n /***************************************\n Asset Config\n ****************************************/\n\n /**\n * @notice Add a supported asset to the contract, i.e. one that can be to mint OTokens.\n * @dev Overridden to remove price provider integration\n * @param _asset Address of asset\n * @param _unitConversion 0 decimals, 1 exchange rate\n */\n function supportAsset(address _asset, uint8 _unitConversion)\n external\n override\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Asset already supported\");\n\n assets[_asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(_unitConversion),\n decimals: 0, // will be overridden in _cacheDecimals\n allowedOracleSlippageBps: 0 // 0% by default\n });\n\n _cacheDecimals(_asset);\n allAssets.push(_asset);\n\n emit AssetSupported(_asset);\n }\n}\n" + }, + "contracts/vault/OSonicVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultCore } from \"./OETHVaultCore.sol\";\n\n/**\n * @title Origin Sonic VaultCore contract on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonicVaultCore is OETHVaultCore {\n /// @param _wS Sonic's Wrapped S token\n constructor(address _wS) OETHVaultCore(_wS) {}\n\n /**\n * @notice Instant redeem is not supported on Sonic.\n * Use the asynchronous `requestWithdrawal` a `claimWithdrawal` instead.\n */\n function _redeem(uint256, uint256) internal override {\n revert(\"unsupported function\");\n }\n}\n" + }, + "contracts/vault/OSonicZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWrappedSonic } from \"../interfaces/sonic/IWrappedSonic.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\n\n/**\n * @title Zapper for Origin Sonic (OS) tokens\n * @author Origin Protocol Inc\n */\ncontract OSonicZapper {\n IERC20 public immutable OS;\n IERC4626 public immutable wOS;\n IVault public immutable vault;\n\n IWrappedSonic public constant wS =\n IWrappedSonic(0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(\n address _OS,\n address _wOS,\n address _vault\n ) {\n OS = IERC20(_OS);\n wOS = IERC4626(_wOS);\n vault = IVault(_vault);\n\n wS.approve(address(_vault), type(uint256).max);\n IERC20(_OS).approve(_wOS, type(uint256).max);\n }\n\n /**\n * @dev Deposit native S currency and receive Origin Sonic (OS) tokens in return.\n * Will verify that the user is sent 1:1 for S.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit native S currency and receive Origin Sonic (OS) tokens in return.\n * Will verify that the user is sent 1:1 for S.\n * @return Amount of Origin Sonic (OS) tokens sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap native S\n wS.deposit{ value: balance }();\n\n // Mint Origin Sonic (OS) with Wrapped Sonic (wS)\n return _mint(balance, msg.sender);\n }\n\n /**\n * @dev Deposit S and receive Wrapped Origin Sonic (wOS) in return\n * @param minReceived min amount of Wrapped Origin Sonic (wOS) to receive\n * @return Amount of Wrapped Origin Sonic (wOS) tokens sent to user\n */\n function depositSForWrappedTokens(uint256 minReceived)\n external\n payable\n returns (uint256)\n {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap S\n wS.deposit{ value: balance }();\n\n // Mint with Wrapped Sonic\n uint256 mintOS = _mint(balance, address(this));\n\n // Wrap Origin Sonic (OS) into Wrapped Origin Sonic (wOS)\n uint256 mintedWOS = wOS.deposit(mintOS, msg.sender);\n\n require(mintedWOS >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWOS;\n }\n\n /**\n * @dev Deposit Wrapped Sonic (wS) tokens and receive Wrapped Origin Sonic (wOS) tokens in return\n * @param wSAmount Amount of Wrapped Sonic (wS) to deposit\n * @param minReceived min amount of Wrapped Origin Sonic (wOS) token to receive\n * @return Amount of Wrapped Origin Sonic (wOS) tokens sent to user\n */\n function depositWSForWrappedTokens(uint256 wSAmount, uint256 minReceived)\n external\n returns (uint256)\n {\n // slither-disable-next-line unchecked-transfer unused-return\n wS.transferFrom(msg.sender, address(this), wSAmount);\n\n emit Zap(msg.sender, address(wS), wSAmount);\n\n // Mint with Wrapped Sonic (wS)\n uint256 mintedOS = _mint(wSAmount, address(this));\n\n // Wrap Origin Sonic (OS) tokens into Wrapped Origin Sonic (wOS) tokens\n uint256 mintedWOS = wOS.deposit(mintedOS, msg.sender);\n\n require(mintedWOS >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWOS;\n }\n\n /**\n * @dev Internal function to mint Origin Sonic (OS) with Wrapped S (wS)\n * @param minOS Minimum amount of Origin Sonic (OS) tokens the user can receive\n * @param recipient Address that receives the tokens\n * @return Amount of Origin Sonic (OS) tokens sent to the recipient\n */\n function _mint(uint256 minOS, address recipient)\n internal\n returns (uint256)\n {\n uint256 toMint = wS.balanceOf(address(this));\n vault.mint(address(wS), toMint, minOS);\n uint256 mintedAmount = OS.balanceOf(address(this));\n require(mintedAmount >= minOS, \"Zapper: not enough minted\");\n\n if (recipient != address(this)) {\n require(OS.transfer(recipient, mintedAmount));\n }\n\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/Vault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD VaultInitializer Contract\n * @notice The VaultInitializer sets up the initial contract.\n * @author Origin Protocol Inc\n */\nimport { VaultInitializer } from \"./VaultInitializer.sol\";\nimport { VaultAdmin } from \"./VaultAdmin.sol\";\n\ncontract Vault is VaultInitializer, VaultAdmin {}\n" + }, + "contracts/vault/VaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultAdmin contract\n * @notice The VaultAdmin contract makes configuration and admin calls on the vault.\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport \"./VaultStorage.sol\";\n\ncontract VaultAdmin is VaultStorage {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /***************************************\n Configuration\n ****************************************/\n\n /**\n * @notice Set address of price provider.\n * @param _priceProvider Address of price provider\n */\n function setPriceProvider(address _priceProvider) external onlyGovernor {\n priceProvider = _priceProvider;\n emit PriceProviderUpdated(_priceProvider);\n }\n\n /**\n * @notice Set a fee in basis points to be charged for a redeem.\n * @param _redeemFeeBps Basis point fee to be charged\n */\n function setRedeemFeeBps(uint256 _redeemFeeBps) external onlyGovernor {\n require(_redeemFeeBps <= 1000, \"Redeem fee should not be over 10%\");\n redeemFeeBps = _redeemFeeBps;\n emit RedeemFeeUpdated(_redeemFeeBps);\n }\n\n /**\n * @notice Set a buffer of assets to keep in the Vault to handle most\n * redemptions without needing to spend gas unwinding assets from a Strategy.\n * @param _vaultBuffer Percentage using 18 decimals. 100% = 1e18.\n */\n function setVaultBuffer(uint256 _vaultBuffer)\n external\n onlyGovernorOrStrategist\n {\n require(_vaultBuffer <= 1e18, \"Invalid value\");\n vaultBuffer = _vaultBuffer;\n emit VaultBufferUpdated(_vaultBuffer);\n }\n\n /**\n * @notice Sets the minimum amount of OTokens in a mint to trigger an\n * automatic allocation of funds afterwords.\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setAutoAllocateThreshold(uint256 _threshold)\n external\n onlyGovernor\n {\n autoAllocateThreshold = _threshold;\n emit AllocateThresholdUpdated(_threshold);\n }\n\n /**\n * @notice Set a minimum amount of OTokens in a mint or redeem that triggers a\n * rebase\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setRebaseThreshold(uint256 _threshold) external onlyGovernor {\n rebaseThreshold = _threshold;\n emit RebaseThresholdUpdated(_threshold);\n }\n\n /**\n * @notice Set address of Strategist\n * @param _address Address of Strategist\n */\n function setStrategistAddr(address _address) external onlyGovernor {\n strategistAddr = _address;\n emit StrategistUpdated(_address);\n }\n\n /**\n * @notice Set the default Strategy for an asset, i.e. the one which the asset\n will be automatically allocated to and withdrawn from\n * @param _asset Address of the asset\n * @param _strategy Address of the Strategy\n */\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external\n onlyGovernorOrStrategist\n {\n emit AssetDefaultStrategyUpdated(_asset, _strategy);\n // If its a zero address being passed for the strategy we are removing\n // the default strategy\n if (_strategy != address(0)) {\n // Make sure the strategy meets some criteria\n require(strategies[_strategy].isSupported, \"Strategy not approved\");\n IStrategy strategy = IStrategy(_strategy);\n require(assets[_asset].isSupported, \"Asset is not supported\");\n require(\n strategy.supportsAsset(_asset),\n \"Asset not supported by Strategy\"\n );\n }\n assetDefaultStrategies[_asset] = _strategy;\n }\n\n /**\n * @notice Set maximum amount of OTokens that can at any point be minted and deployed\n * to strategy (used only by ConvexOUSDMetaStrategy for now).\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setNetOusdMintForStrategyThreshold(uint256 _threshold)\n external\n onlyGovernor\n {\n /**\n * Because `netOusdMintedForStrategy` check in vault core works both ways\n * (positive and negative) the actual impact of the amount of OToken minted\n * could be double the threshold. E.g.:\n * - contract has threshold set to 100\n * - state of netOusdMinted is -90\n * - in effect it can mint 190 OToken and still be within limits\n *\n * We are somewhat mitigating this behaviour by resetting the netOusdMinted\n * counter whenever new threshold is set. So it can only move one threshold\n * amount in each direction. This also enables us to reduce the threshold\n * amount and not have problems with current netOusdMinted being near\n * limits on either side.\n */\n netOusdMintedForStrategy = 0;\n netOusdMintForStrategyThreshold = _threshold;\n emit NetOusdMintForStrategyThresholdChanged(_threshold);\n }\n\n /**\n * @notice Changes the async withdrawal claim period for OETH & superOETHb\n * @param _delay Delay period (should be between 10 mins to 7 days).\n * Set to 0 to disable async withdrawals\n */\n function setWithdrawalClaimDelay(uint256 _delay) external onlyGovernor {\n require(\n _delay == 0 || (_delay >= 10 minutes && _delay <= 15 days),\n \"Invalid claim delay period\"\n );\n withdrawalClaimDelay = _delay;\n emit WithdrawalClaimDelayUpdated(_delay);\n }\n\n /**\n * @notice Set a yield streaming max rate. This spreads yield over\n * time if it is above the max rate.\n * @param yearlyApr in 1e18 notation. 3 * 1e18 = 3% APR\n */\n function setRebaseRateMax(uint256 yearlyApr)\n external\n onlyGovernorOrStrategist\n {\n // The old yield will be at the old rate\n IVault(address(this)).rebase();\n // Change the rate\n uint256 newPerSecond = yearlyApr / 100 / 365 days;\n require(newPerSecond <= MAX_REBASE_PER_SECOND, \"Rate too high\");\n rebasePerSecondMax = newPerSecond.toUint64();\n emit RebasePerSecondMaxChanged(newPerSecond);\n }\n\n /**\n * @notice Set the drip duration period\n * @param _dripDuration Time in seconds to target a constant yield rate\n */\n function setDripDuration(uint256 _dripDuration)\n external\n onlyGovernorOrStrategist\n {\n // The old yield will be at the old rate\n IVault(address(this)).rebase();\n dripDuration = _dripDuration.toUint64();\n emit DripDurationChanged(_dripDuration);\n }\n\n /***************************************\n Swaps\n ****************************************/\n\n /**\n * @notice Strategist swaps collateral assets sitting in the vault.\n * @param _fromAsset The token address of the asset being sold by the vault.\n * @param _toAsset The token address of the asset being purchased by the vault.\n * @param _fromAssetAmount The amount of assets being sold by the vault.\n * @param _minToAssetAmount The minimum amount of assets to be purchased.\n * @param _data implementation specific data. eg 1Inch swap data\n * @return toAssetAmount The amount of toAssets that was received from the swap\n */\n function swapCollateral(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n )\n external\n nonReentrant\n onlyGovernorOrStrategist\n returns (uint256 toAssetAmount)\n {\n toAssetAmount = _swapCollateral(\n _fromAsset,\n _toAsset,\n _fromAssetAmount,\n _minToAssetAmount,\n _data\n );\n }\n\n function _swapCollateral(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n ) internal virtual returns (uint256 toAssetAmount) {\n // Check fromAsset and toAsset are valid\n Asset memory fromAssetConfig = assets[_fromAsset];\n Asset memory toAssetConfig = assets[_toAsset];\n require(fromAssetConfig.isSupported, \"From asset is not supported\");\n require(toAssetConfig.isSupported, \"To asset is not supported\");\n\n // Load swap config into memory to avoid separate SLOADs\n SwapConfig memory config = swapConfig;\n\n // Scope a new block to remove toAssetBalBefore from the scope of swapCollateral.\n // This avoids a stack too deep error.\n {\n uint256 toAssetBalBefore = IERC20(_toAsset).balanceOf(\n address(this)\n );\n\n // Transfer from assets to the swapper contract\n IERC20(_fromAsset).safeTransfer(config.swapper, _fromAssetAmount);\n\n // Call to the Swapper contract to do the actual swap\n // The -1 is required for stETH which sometimes transfers 1 wei less than what was specified.\n // slither-disable-next-line unused-return\n ISwapper(config.swapper).swap(\n _fromAsset,\n _toAsset,\n _fromAssetAmount - 1,\n _minToAssetAmount,\n _data\n );\n\n // Compute the change in asset balance held by the Vault\n toAssetAmount =\n IERC20(_toAsset).balanceOf(address(this)) -\n toAssetBalBefore;\n }\n\n // Check the to assets returned is above slippage amount specified by the strategist\n require(\n toAssetAmount >= _minToAssetAmount,\n \"Strategist slippage limit\"\n );\n\n // Scope a new block to remove minOracleToAssetAmount from the scope of swapCollateral.\n // This avoids a stack too deep error.\n {\n // Check the slippage against the Oracle in case the strategist made a mistake or has become malicious.\n // to asset amount = from asset amount * from asset price / to asset price\n uint256 minOracleToAssetAmount = (_fromAssetAmount *\n (1e4 - fromAssetConfig.allowedOracleSlippageBps) *\n IOracle(priceProvider).price(_fromAsset)) /\n (IOracle(priceProvider).price(_toAsset) *\n (1e4 + toAssetConfig.allowedOracleSlippageBps));\n\n // Scale both sides up to 18 decimals to compare\n require(\n toAssetAmount.scaleBy(18, toAssetConfig.decimals) >=\n minOracleToAssetAmount.scaleBy(\n 18,\n fromAssetConfig.decimals\n ),\n \"Oracle slippage limit exceeded\"\n );\n }\n\n // Check the vault's total value hasn't gone below the OToken total supply\n // by more than the allowed percentage.\n require(\n IVault(address(this)).totalValue() >=\n (oUSD.totalSupply() * ((1e4 - config.allowedUndervalueBps))) /\n 1e4,\n \"Allowed value < supply\"\n );\n\n emit Swapped(_fromAsset, _toAsset, _fromAssetAmount, toAssetAmount);\n }\n\n /***************************************\n Swap Config\n ****************************************/\n\n /**\n * @notice Set the contract the performs swaps of collateral assets.\n * @param _swapperAddr Address of the Swapper contract that implements the ISwapper interface.\n */\n function setSwapper(address _swapperAddr) external onlyGovernor {\n swapConfig.swapper = _swapperAddr;\n emit SwapperChanged(_swapperAddr);\n }\n\n /// @notice Contract that swaps the vault's collateral assets\n function swapper() external view returns (address swapper_) {\n swapper_ = swapConfig.swapper;\n }\n\n /**\n * @notice Set max allowed percentage the vault total value can drop below the OToken total supply in basis points\n * when executing collateral swaps.\n * @param _basis Percentage in basis points. eg 100 == 1%\n */\n function setSwapAllowedUndervalue(uint16 _basis) external onlyGovernor {\n require(_basis < 10001, \"Invalid basis points\");\n swapConfig.allowedUndervalueBps = _basis;\n emit SwapAllowedUndervalueChanged(_basis);\n }\n\n /**\n * @notice Max allowed percentage the vault total value can drop below the OToken total supply in basis points\n * when executing a collateral swap.\n * For example 100 == 1%\n * @return value Percentage in basis points.\n */\n function allowedSwapUndervalue() external view returns (uint256 value) {\n value = swapConfig.allowedUndervalueBps;\n }\n\n /**\n * @notice Set the allowed slippage from the Oracle price for collateral asset swaps.\n * @param _asset Address of the asset token.\n * @param _allowedOracleSlippageBps allowed slippage from Oracle in basis points. eg 20 = 0.2%. Max 10%.\n */\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external\n onlyGovernor\n {\n require(assets[_asset].isSupported, \"Asset not supported\");\n require(_allowedOracleSlippageBps < 1000, \"Slippage too high\");\n\n assets[_asset].allowedOracleSlippageBps = _allowedOracleSlippageBps;\n\n emit SwapSlippageChanged(_asset, _allowedOracleSlippageBps);\n }\n\n /***************************************\n Asset Config\n ****************************************/\n\n /**\n * @notice Add a supported asset to the contract, i.e. one that can be\n * to mint OTokens.\n * @param _asset Address of asset\n */\n function supportAsset(address _asset, uint8 _unitConversion)\n external\n virtual\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Asset already supported\");\n\n assets[_asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(_unitConversion),\n decimals: 0, // will be overridden in _cacheDecimals\n allowedOracleSlippageBps: 0 // 0% by default\n });\n\n _cacheDecimals(_asset);\n allAssets.push(_asset);\n\n // Verify that our oracle supports the asset\n // slither-disable-next-line unused-return\n IOracle(priceProvider).price(_asset);\n\n emit AssetSupported(_asset);\n }\n\n /**\n * @notice Remove a supported asset from the Vault\n * @param _asset Address of asset\n */\n function removeAsset(address _asset) external onlyGovernor {\n require(assets[_asset].isSupported, \"Asset not supported\");\n\n // 1e13 for 18 decimals. And 10 for 6 decimals\n uint256 maxDustBalance = uint256(1e13).scaleBy(\n assets[_asset].decimals,\n 18\n );\n\n require(\n IVault(address(this)).checkBalance(_asset) <= maxDustBalance,\n \"Vault still holds asset\"\n );\n\n uint256 assetsCount = allAssets.length;\n uint256 assetIndex = assetsCount; // initialize at invalid index\n for (uint256 i = 0; i < assetsCount; ++i) {\n if (allAssets[i] == _asset) {\n assetIndex = i;\n break;\n }\n }\n\n // Note: If asset is not found in `allAssets`, the following line\n // will revert with an out-of-bound error. However, there's no\n // reason why an asset would have `Asset.isSupported = true` but\n // not exist in `allAssets`.\n\n // Update allAssets array\n allAssets[assetIndex] = allAssets[assetsCount - 1];\n allAssets.pop();\n\n // Reset default strategy\n assetDefaultStrategies[_asset] = address(0);\n emit AssetDefaultStrategyUpdated(_asset, address(0));\n\n // Remove asset from storage\n delete assets[_asset];\n\n emit AssetRemoved(_asset);\n }\n\n /**\n * @notice Cache decimals on OracleRouter for a particular asset. This action\n * is required before that asset's price can be accessed.\n * @param _asset Address of asset token\n */\n function cacheDecimals(address _asset) external onlyGovernor {\n _cacheDecimals(_asset);\n }\n\n /***************************************\n Strategy Config\n ****************************************/\n\n /**\n * @notice Add a strategy to the Vault.\n * @param _addr Address of the strategy to add\n */\n function approveStrategy(address _addr) external onlyGovernor {\n require(!strategies[_addr].isSupported, \"Strategy already approved\");\n strategies[_addr] = Strategy({ isSupported: true, _deprecated: 0 });\n allStrategies.push(_addr);\n emit StrategyApproved(_addr);\n }\n\n /**\n * @notice Remove a strategy from the Vault.\n * @param _addr Address of the strategy to remove\n */\n\n function removeStrategy(address _addr) external onlyGovernor {\n require(strategies[_addr].isSupported, \"Strategy not approved\");\n\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n require(\n assetDefaultStrategies[allAssets[i]] != _addr,\n \"Strategy is default for an asset\"\n );\n }\n\n // Initialize strategyIndex with out of bounds result so function will\n // revert if no valid index found\n uint256 stratCount = allStrategies.length;\n uint256 strategyIndex = stratCount;\n for (uint256 i = 0; i < stratCount; ++i) {\n if (allStrategies[i] == _addr) {\n strategyIndex = i;\n break;\n }\n }\n\n if (strategyIndex < stratCount) {\n allStrategies[strategyIndex] = allStrategies[stratCount - 1];\n allStrategies.pop();\n\n // Mark the strategy as not supported\n strategies[_addr].isSupported = false;\n\n // Withdraw all assets\n IStrategy strategy = IStrategy(_addr);\n strategy.withdrawAll();\n\n emit StrategyRemoved(_addr);\n }\n }\n\n /***************************************\n Strategies\n ****************************************/\n\n /**\n * @notice Deposit multiple assets from the vault into the strategy.\n * @param _strategyToAddress Address of the Strategy to deposit assets into.\n * @param _assets Array of asset address that will be deposited into the strategy.\n * @param _amounts Array of amounts of each corresponding asset to deposit.\n */\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external onlyGovernorOrStrategist nonReentrant {\n _depositToStrategy(_strategyToAddress, _assets, _amounts);\n }\n\n function _depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal virtual {\n require(\n strategies[_strategyToAddress].isSupported,\n \"Invalid to Strategy\"\n );\n require(_assets.length == _amounts.length, \"Parameter length mismatch\");\n\n uint256 assetCount = _assets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n address assetAddr = _assets[i];\n require(\n IStrategy(_strategyToAddress).supportsAsset(assetAddr),\n \"Asset unsupported\"\n );\n // Send required amount of funds to the strategy\n IERC20(assetAddr).safeTransfer(_strategyToAddress, _amounts[i]);\n }\n\n // Deposit all the funds that have been sent to the strategy\n IStrategy(_strategyToAddress).depositAll();\n }\n\n /**\n * @notice Withdraw multiple assets from the strategy to the vault.\n * @param _strategyFromAddress Address of the Strategy to withdraw assets from.\n * @param _assets Array of asset address that will be withdrawn from the strategy.\n * @param _amounts Array of amounts of each corresponding asset to withdraw.\n */\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external onlyGovernorOrStrategist nonReentrant {\n _withdrawFromStrategy(\n address(this),\n _strategyFromAddress,\n _assets,\n _amounts\n );\n }\n\n /**\n * @param _recipient can either be a strategy or the Vault\n */\n function _withdrawFromStrategy(\n address _recipient,\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal virtual {\n require(\n strategies[_strategyFromAddress].isSupported,\n \"Invalid from Strategy\"\n );\n require(_assets.length == _amounts.length, \"Parameter length mismatch\");\n\n uint256 assetCount = _assets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n // Withdraw from Strategy to the recipient\n IStrategy(_strategyFromAddress).withdraw(\n _recipient,\n _assets[i],\n _amounts[i]\n );\n }\n }\n\n /**\n * @notice Sets the maximum allowable difference between\n * total supply and backing assets' value.\n */\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external onlyGovernor {\n maxSupplyDiff = _maxSupplyDiff;\n emit MaxSupplyDiffChanged(_maxSupplyDiff);\n }\n\n /**\n * @notice Sets the trusteeAddress that can receive a portion of yield.\n * Setting to the zero address disables this feature.\n */\n function setTrusteeAddress(address _address) external onlyGovernor {\n trusteeAddress = _address;\n emit TrusteeAddressChanged(_address);\n }\n\n /**\n * @notice Sets the TrusteeFeeBps to the percentage of yield that should be\n * received in basis points.\n */\n function setTrusteeFeeBps(uint256 _basis) external onlyGovernor {\n require(_basis <= 5000, \"basis cannot exceed 50%\");\n trusteeFeeBps = _basis;\n emit TrusteeFeeBpsChanged(_basis);\n }\n\n /**\n * @notice Set OToken Metapool strategy\n * @param _ousdMetaStrategy Address of OToken metapool strategy\n */\n function setOusdMetaStrategy(address _ousdMetaStrategy)\n external\n onlyGovernor\n {\n ousdMetaStrategy = _ousdMetaStrategy;\n emit OusdMetaStrategyUpdated(_ousdMetaStrategy);\n }\n\n /***************************************\n Pause\n ****************************************/\n\n /**\n * @notice Set the deposit paused flag to true to prevent rebasing.\n */\n function pauseRebase() external onlyGovernorOrStrategist {\n rebasePaused = true;\n emit RebasePaused();\n }\n\n /**\n * @notice Set the deposit paused flag to true to allow rebasing.\n */\n function unpauseRebase() external onlyGovernorOrStrategist {\n rebasePaused = false;\n emit RebaseUnpaused();\n }\n\n /**\n * @notice Set the deposit paused flag to true to prevent capital movement.\n */\n function pauseCapital() external onlyGovernorOrStrategist {\n capitalPaused = true;\n emit CapitalPaused();\n }\n\n /**\n * @notice Set the deposit paused flag to false to enable capital movement.\n */\n function unpauseCapital() external onlyGovernorOrStrategist {\n capitalPaused = false;\n emit CapitalUnpaused();\n }\n\n /***************************************\n Utils\n ****************************************/\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Only unsupported assets\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /***************************************\n Strategies Admin\n ****************************************/\n\n /**\n * @notice Withdraws all assets from the strategy and sends assets to the Vault.\n * @param _strategyAddr Strategy address.\n */\n function withdrawAllFromStrategy(address _strategyAddr)\n external\n onlyGovernorOrStrategist\n {\n _withdrawAllFromStrategy(_strategyAddr);\n }\n\n function _withdrawAllFromStrategy(address _strategyAddr) internal virtual {\n require(\n strategies[_strategyAddr].isSupported,\n \"Strategy is not supported\"\n );\n IStrategy strategy = IStrategy(_strategyAddr);\n strategy.withdrawAll();\n }\n\n /**\n * @notice Withdraws all assets from all the strategies and sends assets to the Vault.\n */\n function withdrawAllFromStrategies() external onlyGovernorOrStrategist {\n _withdrawAllFromStrategies();\n }\n\n function _withdrawAllFromStrategies() internal virtual {\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n IStrategy(allStrategies[i]).withdrawAll();\n }\n }\n\n /***************************************\n Utils\n ****************************************/\n\n function _cacheDecimals(address token) internal {\n Asset storage tokenAsset = assets[token];\n if (tokenAsset.decimals != 0) {\n return;\n }\n uint8 decimals = IBasicToken(token).decimals();\n require(decimals >= 6 && decimals <= 18, \"Unexpected precision\");\n tokenAsset.decimals = decimals;\n }\n}\n" + }, + "contracts/vault/VaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultCore contract\n * @notice The Vault contract stores assets. On a deposit, OTokens will be minted\n and sent to the depositor. On a withdrawal, OTokens will be burned and\n assets will be sent to the withdrawer. The Vault accepts deposits of\n interest from yield bearing strategies which will modify the supply\n of OTokens.\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IGetExchangeRateToken } from \"../interfaces/IGetExchangeRateToken.sol\";\n\nimport \"./VaultInitializer.sol\";\n\ncontract VaultCore is VaultInitializer {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n /// @dev max signed int\n uint256 internal constant MAX_INT = uint256(type(int256).max);\n\n /**\n * @dev Verifies that the rebasing is not paused.\n */\n modifier whenNotRebasePaused() {\n require(!rebasePaused, \"Rebasing paused\");\n _;\n }\n\n /**\n * @dev Verifies that the deposits are not paused.\n */\n modifier whenNotCapitalPaused() {\n require(!capitalPaused, \"Capital paused\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the AMO strategy.\n */\n modifier onlyOusdMetaStrategy() {\n require(\n msg.sender == ousdMetaStrategy,\n \"Caller is not the OUSD meta strategy\"\n );\n _;\n }\n\n /**\n * @notice Deposit a supported asset and mint OTokens.\n * @param _asset Address of the asset being deposited\n * @param _amount Amount of the asset being deposited\n * @param _minimumOusdAmount Minimum OTokens to mint\n */\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external whenNotCapitalPaused nonReentrant {\n _mint(_asset, _amount, _minimumOusdAmount);\n }\n\n /**\n * @dev Deposit a supported asset and mint OTokens.\n * @param _asset Address of the asset being deposited\n * @param _amount Amount of the asset being deposited\n * @param _minimumOusdAmount Minimum OTokens to mint\n */\n function _mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) internal virtual {\n require(assets[_asset].isSupported, \"Asset is not supported\");\n require(_amount > 0, \"Amount must be greater than 0\");\n\n uint256 units = _toUnits(_amount, _asset);\n uint256 unitPrice = _toUnitPrice(_asset, true);\n uint256 priceAdjustedDeposit = (units * unitPrice) / 1e18;\n\n if (_minimumOusdAmount > 0) {\n require(\n priceAdjustedDeposit >= _minimumOusdAmount,\n \"Mint amount lower than minimum\"\n );\n }\n\n emit Mint(msg.sender, priceAdjustedDeposit);\n\n // Rebase must happen before any transfers occur.\n if (priceAdjustedDeposit >= rebaseThreshold && !rebasePaused) {\n _rebase();\n }\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, priceAdjustedDeposit);\n\n // Transfer the deposited coins to the vault\n IERC20 asset = IERC20(_asset);\n asset.safeTransferFrom(msg.sender, address(this), _amount);\n\n if (priceAdjustedDeposit >= autoAllocateThreshold) {\n _allocate();\n }\n }\n\n /**\n * @notice Mint OTokens for a Metapool Strategy\n * @param _amount Amount of the asset being deposited\n *\n * Notice: can't use `nonReentrant` modifier since the `mint` function can\n * call `allocate`, and that can trigger `ConvexOUSDMetaStrategy` to call this function\n * while the execution of the `mint` has not yet completed -> causing a `nonReentrant` collision.\n *\n * Also important to understand is that this is a limitation imposed by the test suite.\n * Production / mainnet contracts should never be configured in a way where mint/redeem functions\n * that are moving funds between the Vault and end user wallets can influence strategies\n * utilizing this function.\n */\n function mintForStrategy(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n onlyOusdMetaStrategy\n {\n require(_amount < MAX_INT, \"Amount too high\");\n\n emit Mint(msg.sender, _amount);\n\n // safe to cast because of the require check at the beginning of the function\n netOusdMintedForStrategy += int256(_amount);\n\n require(\n abs(netOusdMintedForStrategy) < netOusdMintForStrategyThreshold,\n \"Minted ousd surpassed netOusdMintForStrategyThreshold.\"\n );\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, _amount);\n }\n\n // In memoriam\n\n /**\n * @notice Withdraw a supported asset and burn OTokens.\n * @param _amount Amount of OTokens to burn\n * @param _minimumUnitAmount Minimum stablecoin units to receive in return\n */\n function redeem(uint256 _amount, uint256 _minimumUnitAmount)\n external\n whenNotCapitalPaused\n nonReentrant\n {\n _redeem(_amount, _minimumUnitAmount);\n }\n\n /**\n * @notice Withdraw a supported asset and burn OTokens.\n * @param _amount Amount of OTokens to burn\n * @param _minimumUnitAmount Minimum stablecoin units to receive in return\n */\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n {\n // Calculate redemption outputs\n uint256[] memory outputs = _calculateRedeemOutputs(_amount);\n\n emit Redeem(msg.sender, _amount);\n\n // Send outputs\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n if (outputs[i] == 0) continue;\n\n address assetAddr = allAssets[i];\n\n if (IERC20(assetAddr).balanceOf(address(this)) >= outputs[i]) {\n // Use Vault funds first if sufficient\n IERC20(assetAddr).safeTransfer(msg.sender, outputs[i]);\n } else {\n address strategyAddr = assetDefaultStrategies[assetAddr];\n if (strategyAddr != address(0)) {\n // Nothing in Vault, but something in Strategy, send from there\n IStrategy strategy = IStrategy(strategyAddr);\n strategy.withdraw(msg.sender, assetAddr, outputs[i]);\n } else {\n // Cant find funds anywhere\n revert(\"Liquidity error\");\n }\n }\n }\n\n if (_minimumUnitAmount > 0) {\n uint256 unitTotal = 0;\n for (uint256 i = 0; i < outputs.length; ++i) {\n unitTotal += _toUnits(outputs[i], allAssets[i]);\n }\n require(\n unitTotal >= _minimumUnitAmount,\n \"Redeem amount lower than minimum\"\n );\n }\n\n oUSD.burn(msg.sender, _amount);\n\n _postRedeem(_amount);\n }\n\n function _postRedeem(uint256 _amount) internal {\n // Until we can prove that we won't affect the prices of our assets\n // by withdrawing them, this should be here.\n // It's possible that a strategy was off on its asset total, perhaps\n // a reward token sold for more or for less than anticipated.\n uint256 totalUnits = 0;\n if (_amount >= rebaseThreshold && !rebasePaused) {\n totalUnits = _rebase();\n } else {\n totalUnits = _totalValue();\n }\n\n // Check that the OTokens are backed by enough assets\n if (maxSupplyDiff > 0) {\n // If there are more outstanding withdrawal requests than assets in the vault and strategies\n // then the available assets will be negative and totalUnits will be rounded up to zero.\n // As we don't know the exact shortfall amount, we will reject all redeem and withdrawals\n require(totalUnits > 0, \"Too many outstanding requests\");\n\n // Allow a max difference of maxSupplyDiff% between\n // backing assets value and OUSD total supply\n uint256 diff = oUSD.totalSupply().divPrecisely(totalUnits);\n require(\n (diff > 1e18 ? diff - 1e18 : 1e18 - diff) <= maxSupplyDiff,\n \"Backing supply liquidity error\"\n );\n }\n }\n\n /**\n * @notice Burn OTokens for Metapool Strategy\n * @param _amount Amount of OUSD to burn\n *\n * @dev Notice: can't use `nonReentrant` modifier since the `redeem` function could\n * require withdrawal on `ConvexOUSDMetaStrategy` and that one can call `burnForStrategy`\n * while the execution of the `redeem` has not yet completed -> causing a `nonReentrant` collision.\n *\n * Also important to understand is that this is a limitation imposed by the test suite.\n * Production / mainnet contracts should never be configured in a way where mint/redeem functions\n * that are moving funds between the Vault and end user wallets can influence strategies\n * utilizing this function.\n */\n function burnForStrategy(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n onlyOusdMetaStrategy\n {\n require(_amount < MAX_INT, \"Amount too high\");\n\n emit Redeem(msg.sender, _amount);\n\n // safe to cast because of the require check at the beginning of the function\n netOusdMintedForStrategy -= int256(_amount);\n\n require(\n abs(netOusdMintedForStrategy) < netOusdMintForStrategyThreshold,\n \"Attempting to burn too much OUSD.\"\n );\n\n // Burn OTokens\n oUSD.burn(msg.sender, _amount);\n }\n\n /**\n * @notice Allocate unallocated funds on Vault to strategies.\n **/\n function allocate() external virtual whenNotCapitalPaused nonReentrant {\n _allocate();\n }\n\n /**\n * @dev Allocate unallocated funds on Vault to strategies.\n **/\n function _allocate() internal virtual {\n uint256 vaultValue = _totalValueInVault();\n // Nothing in vault to allocate\n if (vaultValue == 0) return;\n uint256 strategiesValue = _totalValueInStrategies();\n // We have a method that does the same as this, gas optimisation\n uint256 calculatedTotalValue = vaultValue + strategiesValue;\n\n // We want to maintain a buffer on the Vault so calculate a percentage\n // modifier to multiply each amount being allocated by to enforce the\n // vault buffer\n uint256 vaultBufferModifier;\n if (strategiesValue == 0) {\n // Nothing in Strategies, allocate 100% minus the vault buffer to\n // strategies\n vaultBufferModifier = uint256(1e18) - vaultBuffer;\n } else {\n vaultBufferModifier =\n (vaultBuffer * calculatedTotalValue) /\n vaultValue;\n if (1e18 > vaultBufferModifier) {\n // E.g. 1e18 - (1e17 * 10e18)/5e18 = 8e17\n // (5e18 * 8e17) / 1e18 = 4e18 allocated from Vault\n vaultBufferModifier = uint256(1e18) - vaultBufferModifier;\n } else {\n // We need to let the buffer fill\n return;\n }\n }\n if (vaultBufferModifier == 0) return;\n\n // Iterate over all assets in the Vault and allocate to the appropriate\n // strategy\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(allAssets[i]);\n uint256 assetBalance = asset.balanceOf(address(this));\n // No balance, nothing to do here\n if (assetBalance == 0) continue;\n\n // Multiply the balance by the vault buffer modifier and truncate\n // to the scale of the asset decimals\n uint256 allocateAmount = assetBalance.mulTruncate(\n vaultBufferModifier\n );\n\n address depositStrategyAddr = assetDefaultStrategies[\n address(asset)\n ];\n\n if (depositStrategyAddr != address(0) && allocateAmount > 0) {\n IStrategy strategy = IStrategy(depositStrategyAddr);\n // Transfer asset to Strategy and call deposit method to\n // mint or take required action\n asset.safeTransfer(address(strategy), allocateAmount);\n strategy.deposit(address(asset), allocateAmount);\n emit AssetAllocated(\n address(asset),\n depositStrategyAddr,\n allocateAmount\n );\n }\n }\n }\n\n /**\n * @notice Calculate the total value of assets held by the Vault and all\n * strategies and update the supply of OTokens.\n */\n function rebase() external virtual nonReentrant {\n _rebase();\n }\n\n /**\n * @dev Calculate the total value of assets held by the Vault and all\n * strategies and update the supply of OTokens, optionally sending a\n * portion of the yield to the trustee.\n * @return totalUnits Total balance of Vault in units\n */\n function _rebase() internal whenNotRebasePaused returns (uint256) {\n uint256 supply = oUSD.totalSupply();\n uint256 vaultValue = _totalValue();\n // If no supply yet, do not rebase\n if (supply == 0) {\n return vaultValue;\n }\n\n // Calculate yield and new supply\n (uint256 yield, uint256 targetRate) = _nextYield(supply, vaultValue);\n uint256 newSupply = supply + yield;\n // Only rebase upwards and if we have enough backing funds\n if (newSupply <= supply || newSupply > vaultValue) {\n return vaultValue;\n }\n\n rebasePerSecondTarget = uint64(_min(targetRate, type(uint64).max));\n lastRebase = uint64(block.timestamp); // Intentional cast\n\n // Fee collection on yield\n address _trusteeAddress = trusteeAddress; // gas savings\n uint256 fee = 0;\n if (_trusteeAddress != address(0)) {\n fee = (yield * trusteeFeeBps) / 1e4;\n if (fee > 0) {\n require(fee < yield, \"Fee must not be greater than yield\");\n oUSD.mint(_trusteeAddress, fee);\n }\n }\n emit YieldDistribution(_trusteeAddress, yield, fee);\n\n // Only ratchet OToken supply upwards\n // Final check uses latest totalSupply\n if (newSupply > oUSD.totalSupply()) {\n oUSD.changeSupply(newSupply);\n }\n return vaultValue;\n }\n\n /**\n * @notice Calculates the amount that would rebase at next rebase.\n * This is before any fees.\n * @return yield amount of expected yield\n */\n function previewYield() external view returns (uint256 yield) {\n (yield, ) = _nextYield(oUSD.totalSupply(), _totalValue());\n return yield;\n }\n\n function _nextYield(uint256 supply, uint256 vaultValue)\n internal\n view\n virtual\n returns (uint256 yield, uint256 targetRate)\n {\n uint256 nonRebasing = oUSD.nonRebasingSupply();\n uint256 rebasing = supply - nonRebasing;\n uint256 elapsed = block.timestamp - lastRebase;\n targetRate = rebasePerSecondTarget;\n\n if (\n elapsed == 0 || // Yield only once per block.\n rebasing == 0 || // No yield if there are no rebasing tokens to give it to.\n supply > vaultValue || // No yield if we do not have yield to give.\n block.timestamp >= type(uint64).max // No yield if we are too far in the future to calculate it correctly.\n ) {\n return (0, targetRate);\n }\n\n // Start with the full difference available\n yield = vaultValue - supply;\n\n // Cap via optional automatic duration smoothing\n uint256 _dripDuration = dripDuration;\n if (_dripDuration > 1) {\n // If we are able to sustain an increased drip rate for\n // double the duration, then increase the target drip rate\n targetRate = _max(targetRate, yield / (_dripDuration * 2));\n // If we cannot sustain the target rate any more,\n // then rebase what we can, and reduce the target\n targetRate = _min(targetRate, yield / _dripDuration);\n // drip at the new target rate\n yield = _min(yield, targetRate * elapsed);\n }\n\n // Cap per second. elapsed is not 1e18 denominated\n yield = _min(yield, (rebasing * elapsed * rebasePerSecondMax) / 1e18);\n\n // Cap at a hard max per rebase, to avoid long durations resulting in huge rebases\n yield = _min(yield, (rebasing * MAX_REBASE) / 1e18);\n\n return (yield, targetRate);\n }\n\n /**\n * @notice Determine the total value of assets held by the vault and its\n * strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function totalValue() external view virtual returns (uint256 value) {\n value = _totalValue();\n }\n\n /**\n * @dev Internal Calculate the total value of the assets held by the\n * vault and its strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValue() internal view virtual returns (uint256 value) {\n return _totalValueInVault() + _totalValueInStrategies();\n }\n\n /**\n * @dev Internal to calculate total value of all assets held in Vault.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInVault()\n internal\n view\n virtual\n returns (uint256 value)\n {\n uint256 assetCount = allAssets.length;\n for (uint256 y; y < assetCount; ++y) {\n address assetAddr = allAssets[y];\n uint256 balance = IERC20(assetAddr).balanceOf(address(this));\n if (balance > 0) {\n value += _toUnits(balance, assetAddr);\n }\n }\n }\n\n /**\n * @dev Internal to calculate total value of all assets held in Strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInStrategies() internal view returns (uint256 value) {\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n value = value + _totalValueInStrategy(allStrategies[i]);\n }\n }\n\n /**\n * @dev Internal to calculate total value of all assets held by strategy.\n * @param _strategyAddr Address of the strategy\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInStrategy(address _strategyAddr)\n internal\n view\n returns (uint256 value)\n {\n IStrategy strategy = IStrategy(_strategyAddr);\n uint256 assetCount = allAssets.length;\n for (uint256 y; y < assetCount; ++y) {\n address assetAddr = allAssets[y];\n if (strategy.supportsAsset(assetAddr)) {\n uint256 balance = strategy.checkBalance(assetAddr);\n if (balance > 0) {\n value += _toUnits(balance, assetAddr);\n }\n }\n }\n }\n\n /**\n * @notice Get the balance of an asset held in Vault and all strategies.\n * @param _asset Address of asset\n * @return uint256 Balance of asset in decimals of asset\n */\n function checkBalance(address _asset) external view returns (uint256) {\n return _checkBalance(_asset);\n }\n\n /**\n * @notice Get the balance of an asset held in Vault and all strategies.\n * @param _asset Address of asset\n * @return balance Balance of asset in decimals of asset\n */\n function _checkBalance(address _asset)\n internal\n view\n virtual\n returns (uint256 balance)\n {\n IERC20 asset = IERC20(_asset);\n balance = asset.balanceOf(address(this));\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n IStrategy strategy = IStrategy(allStrategies[i]);\n if (strategy.supportsAsset(_asset)) {\n balance = balance + strategy.checkBalance(_asset);\n }\n }\n }\n\n /**\n * @notice Calculate the outputs for a redeem function, i.e. the mix of\n * coins that will be returned\n */\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory)\n {\n return _calculateRedeemOutputs(_amount);\n }\n\n /**\n * @dev Calculate the outputs for a redeem function, i.e. the mix of\n * coins that will be returned.\n * @return outputs Array of amounts respective to the supported assets\n */\n function _calculateRedeemOutputs(uint256 _amount)\n internal\n view\n virtual\n returns (uint256[] memory outputs)\n {\n // We always give out coins in proportion to how many we have,\n // Now if all coins were the same value, this math would easy,\n // just take the percentage of each coin, and multiply by the\n // value to be given out. But if coins are worth more than $1,\n // then we would end up handing out too many coins. We need to\n // adjust by the total value of coins.\n //\n // To do this, we total up the value of our coins, by their\n // percentages. Then divide what we would otherwise give out by\n // this number.\n //\n // Let say we have 100 DAI at $1.06 and 200 USDT at $1.00.\n // So for every 1 DAI we give out, we'll be handing out 2 USDT\n // Our total output ratio is: 33% * 1.06 + 66% * 1.00 = 1.02\n //\n // So when calculating the output, we take the percentage of\n // each coin, times the desired output value, divided by the\n // totalOutputRatio.\n //\n // For example, withdrawing: 30 OUSD:\n // DAI 33% * 30 / 1.02 = 9.80 DAI\n // USDT = 66 % * 30 / 1.02 = 19.60 USDT\n //\n // Checking these numbers:\n // 9.80 DAI * 1.06 = $10.40\n // 19.60 USDT * 1.00 = $19.60\n //\n // And so the user gets $10.40 + $19.60 = $30 worth of value.\n\n uint256 assetCount = allAssets.length;\n uint256[] memory assetUnits = new uint256[](assetCount);\n uint256[] memory assetBalances = new uint256[](assetCount);\n outputs = new uint256[](assetCount);\n\n // Calculate redeem fee\n if (redeemFeeBps > 0) {\n uint256 redeemFee = _amount.mulTruncateScale(redeemFeeBps, 1e4);\n _amount = _amount - redeemFee;\n }\n\n // Calculate assets balances and decimals once,\n // for a large gas savings.\n uint256 totalUnits = 0;\n for (uint256 i = 0; i < assetCount; ++i) {\n address assetAddr = allAssets[i];\n uint256 balance = _checkBalance(assetAddr);\n assetBalances[i] = balance;\n assetUnits[i] = _toUnits(balance, assetAddr);\n totalUnits = totalUnits + assetUnits[i];\n }\n // Calculate totalOutputRatio\n uint256 totalOutputRatio = 0;\n for (uint256 i = 0; i < assetCount; ++i) {\n uint256 unitPrice = _toUnitPrice(allAssets[i], false);\n uint256 ratio = (assetUnits[i] * unitPrice) / totalUnits;\n totalOutputRatio = totalOutputRatio + ratio;\n }\n // Calculate final outputs\n uint256 factor = _amount.divPrecisely(totalOutputRatio);\n for (uint256 i = 0; i < assetCount; ++i) {\n outputs[i] = (assetBalances[i] * factor) / totalUnits;\n }\n }\n\n /***************************************\n Pricing\n ****************************************/\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * Never goes above 1, since that is how we price mints.\n * @param asset address of the asset\n * @return price uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed\n */\n function priceUnitMint(address asset)\n external\n view\n returns (uint256 price)\n {\n /* need to supply 1 asset unit in asset's decimals and can not just hard-code\n * to 1e18 and ignore calling `_toUnits` since we need to consider assets\n * with the exchange rate\n */\n uint256 units = _toUnits(\n uint256(1e18).scaleBy(_getDecimals(asset), 18),\n asset\n );\n price = (_toUnitPrice(asset, true) * units) / 1e18;\n }\n\n /**\n * @notice Returns the total price in 18 digit unit for a given asset.\n * Never goes below 1, since that is how we price redeems\n * @param asset Address of the asset\n * @return price uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed\n */\n function priceUnitRedeem(address asset)\n external\n view\n returns (uint256 price)\n {\n /* need to supply 1 asset unit in asset's decimals and can not just hard-code\n * to 1e18 and ignore calling `_toUnits` since we need to consider assets\n * with the exchange rate\n */\n uint256 units = _toUnits(\n uint256(1e18).scaleBy(_getDecimals(asset), 18),\n asset\n );\n price = (_toUnitPrice(asset, false) * units) / 1e18;\n }\n\n /***************************************\n Utils\n ****************************************/\n\n /**\n * @dev Convert a quantity of a token into 1e18 fixed decimal \"units\"\n * in the underlying base (USD/ETH) used by the vault.\n * Price is not taken into account, only quantity.\n *\n * Examples of this conversion:\n *\n * - 1e18 DAI becomes 1e18 units (same decimals)\n * - 1e6 USDC becomes 1e18 units (decimal conversion)\n * - 1e18 rETH becomes 1.2e18 units (exchange rate conversion)\n *\n * @param _raw Quantity of asset\n * @param _asset Core Asset address\n * @return value 1e18 normalized quantity of units\n */\n function _toUnits(uint256 _raw, address _asset)\n internal\n view\n returns (uint256)\n {\n UnitConversion conversion = assets[_asset].unitConversion;\n if (conversion == UnitConversion.DECIMALS) {\n return _raw.scaleBy(18, _getDecimals(_asset));\n } else if (conversion == UnitConversion.GETEXCHANGERATE) {\n uint256 exchangeRate = IGetExchangeRateToken(_asset)\n .getExchangeRate();\n return (_raw * exchangeRate) / 1e18;\n } else {\n revert(\"Unsupported conversion type\");\n }\n }\n\n /**\n * @dev Returns asset's unit price accounting for different asset types\n * and takes into account the context in which that price exists -\n * - mint or redeem.\n *\n * Note: since we are returning the price of the unit and not the one of the\n * asset (see comment above how 1 rETH exchanges for 1.2 units) we need\n * to make the Oracle price adjustment as well since we are pricing the\n * units and not the assets.\n *\n * The price also snaps to a \"full unit price\" in case a mint or redeem\n * action would be unfavourable to the protocol.\n *\n */\n function _toUnitPrice(address _asset, bool isMint)\n internal\n view\n returns (uint256 price)\n {\n UnitConversion conversion = assets[_asset].unitConversion;\n price = IOracle(priceProvider).price(_asset);\n\n if (conversion == UnitConversion.GETEXCHANGERATE) {\n uint256 exchangeRate = IGetExchangeRateToken(_asset)\n .getExchangeRate();\n price = (price * 1e18) / exchangeRate;\n } else if (conversion != UnitConversion.DECIMALS) {\n revert(\"Unsupported conversion type\");\n }\n\n /* At this stage the price is already adjusted to the unit\n * so the price checks are agnostic to underlying asset being\n * pegged to a USD or to an ETH or having a custom exchange rate.\n */\n require(price <= MAX_UNIT_PRICE_DRIFT, \"Vault: Price exceeds max\");\n require(price >= MIN_UNIT_PRICE_DRIFT, \"Vault: Price under min\");\n\n if (isMint) {\n /* Never price a normalized unit price for more than one\n * unit of OETH/OUSD when minting.\n */\n if (price > 1e18) {\n price = 1e18;\n }\n require(price >= MINT_MINIMUM_UNIT_PRICE, \"Asset price below peg\");\n } else {\n /* Never give out more than 1 normalized unit amount of assets\n * for one unit of OETH/OUSD when redeeming.\n */\n if (price < 1e18) {\n price = 1e18;\n }\n }\n }\n\n /**\n * @dev Get the number of decimals of a token asset\n * @param _asset Address of the asset\n * @return decimals number of decimals\n */\n function _getDecimals(address _asset)\n internal\n view\n returns (uint256 decimals)\n {\n decimals = assets[_asset].decimals;\n require(decimals > 0, \"Decimals not cached\");\n }\n\n /**\n * @notice Return the number of assets supported by the Vault.\n */\n function getAssetCount() public view returns (uint256) {\n return allAssets.length;\n }\n\n /**\n * @notice Gets the vault configuration of a supported asset.\n * @param _asset Address of the token asset\n */\n function getAssetConfig(address _asset)\n public\n view\n returns (Asset memory config)\n {\n config = assets[_asset];\n }\n\n /**\n * @notice Return all vault asset addresses in order\n */\n function getAllAssets() external view returns (address[] memory) {\n return allAssets;\n }\n\n /**\n * @notice Return the number of strategies active on the Vault.\n */\n function getStrategyCount() external view returns (uint256) {\n return allStrategies.length;\n }\n\n /**\n * @notice Return the array of all strategies\n */\n function getAllStrategies() external view returns (address[] memory) {\n return allStrategies;\n }\n\n /**\n * @notice Returns whether the vault supports the asset\n * @param _asset address of the asset\n * @return true if supported\n */\n function isSupportedAsset(address _asset) external view returns (bool) {\n return assets[_asset].isSupported;\n }\n\n function ADMIN_IMPLEMENTATION() external view returns (address adminImpl) {\n bytes32 slot = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n adminImpl := sload(slot)\n }\n }\n\n /**\n * @dev Falldown to the admin implementation\n * @notice This is a catch all for all functions not declared in core\n */\n // solhint-disable-next-line no-complex-fallback\n fallback() external {\n bytes32 slot = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(\n gas(),\n sload(slot),\n 0,\n calldatasize(),\n 0,\n 0\n )\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n function abs(int256 x) private pure returns (uint256) {\n require(x < int256(MAX_INT), \"Amount too high\");\n return x >= 0 ? uint256(x) : uint256(-x);\n }\n\n function _min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n function _max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a > b ? a : b;\n }\n}\n" + }, + "contracts/vault/VaultInitializer.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultInitializer contract\n * @notice The Vault contract initializes the vault.\n * @author Origin Protocol Inc\n */\n\nimport \"./VaultStorage.sol\";\n\ncontract VaultInitializer is VaultStorage {\n function initialize(address _priceProvider, address _oToken)\n external\n onlyGovernor\n initializer\n {\n require(_priceProvider != address(0), \"PriceProvider address is zero\");\n require(_oToken != address(0), \"oToken address is zero\");\n\n oUSD = OUSD(_oToken);\n\n priceProvider = _priceProvider;\n\n rebasePaused = false;\n capitalPaused = true;\n\n // Initial redeem fee of 0 basis points\n redeemFeeBps = 0;\n // Initial Vault buffer of 0%\n vaultBuffer = 0;\n // Initial allocate threshold of 25,000 OUSD\n autoAllocateThreshold = 25000e18;\n // Threshold for rebasing\n rebaseThreshold = 1000e18;\n // Initialize all strategies\n allStrategies = new address[](0);\n // Start with drip duration disabled\n dripDuration = 1;\n }\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + }, + "contracts/zapper/WOETHCCIPZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IRouterClient } from \"@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol\";\nimport { Client } from \"@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol\";\n// solhint-disable-next-line max-line-length\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"./../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IOETHZapper } from \"./../interfaces/IOETHZapper.sol\";\n\n/**\n * @title WOETH CCIP Zapper Contract\n * @notice Helps to directly convert ETH on mainnet into WOETH on L2s.\n * @author Origin Protocol Inc\n */\n\ncontract WOETHCCIPZapper {\n /**\n * @dev Event emitted when a zap occurs\n * @param messageId Unique message identifier for each zap\n * @param sender Address initiating the zap\n * @param recipient Recipient address at destination chain\n * @param amount Amount of ETH zapped\n */\n event Zap(\n bytes32 indexed messageId,\n address sender,\n address recipient,\n uint256 amount\n );\n\n // @dev Thrown when Zap amount is less than fee.\n error AmountLessThanFee();\n\n /**\n * @dev The destination chain selector\n */\n uint64 public immutable destinationChainSelector;\n\n /**\n * @dev The WOETH source chain (Mainnet)\n */\n IERC4626 public immutable woethOnSourceChain;\n\n /**\n * @dev The WOETH destination chain (Arbitrum)\n */\n IERC20 public immutable woethOnDestinationChain;\n\n /**\n * @dev The OETH zapper contract address\n */\n IOETHZapper public immutable oethZapper;\n\n /**\n * @dev The CCIP router contract address\n */\n IRouterClient public immutable ccipRouter;\n\n /**\n * @dev The OETH token contract address\n */\n IERC20 public immutable oeth;\n\n constructor(\n address _ccipRouter,\n uint64 _destinationChainSelector,\n IERC4626 _woethOnSourceChain,\n IERC20 _woethOnDestinationChain,\n IOETHZapper _oethZapper,\n IERC20 _oeth\n ) {\n ccipRouter = IRouterClient(_ccipRouter);\n destinationChainSelector = _destinationChainSelector;\n woethOnSourceChain = _woethOnSourceChain;\n woethOnDestinationChain = _woethOnDestinationChain;\n oethZapper = _oethZapper;\n oeth = _oeth;\n\n // Max allowance for Router and WOETH contracts\n _oeth.approve(address(_woethOnSourceChain), type(uint256).max); // for wrapping\n _woethOnSourceChain.approve(address(_ccipRouter), type(uint256).max); // for zapping\n }\n\n /**\n * @notice Accepts ETH, zaps for OETH, wraps it for WOETH and sends it to the destination chain (arbitrum)\n * @param receiver The address of the EOA on the destination chain\n * @return messageId The ID of the message that was sent\n */\n function zap(address receiver)\n external\n payable\n returns (bytes32 messageId)\n {\n return _zap(receiver, msg.value);\n }\n\n /**\n * @notice Used to estimate fee for CCIP transaction\n * @param amount The amount of ETH to be zapped\n * @param receiver The address of the EOA on the destination chain\n * @return feeAmount The CCIP tx fee in ETH.\n */\n\n function getFee(uint256 amount, address receiver)\n public\n view\n returns (uint256 feeAmount)\n {\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(woethOnSourceChain),\n amount: amount\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({\n receiver: abi.encode(receiver), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n feeAmount = ccipRouter.getFee(destinationChainSelector, message);\n }\n\n /**\n * @dev Deposit ETH and receive WOETH in L2.\n * @dev Note that the WOETH will be sent to the msg.sender at destination chain as well.\n */\n receive() external payable {\n _zap(msg.sender, msg.value);\n }\n\n function _zap(address receiver, uint256 amount)\n internal\n returns (bytes32 messageId)\n {\n // Estimate fee for zapping.\n uint256 feeAmount = getFee(amount, receiver);\n if (amount < feeAmount) {\n revert AmountLessThanFee();\n }\n\n // Convert only the msg.value - fees amount to WOETH.\n amount -= feeAmount;\n\n // 1.) Zap for OETH\n uint256 oethReceived = oethZapper.deposit{ value: amount }();\n\n // 2.) Wrap the received woeth\n uint256 woethReceived = woethOnSourceChain.deposit(\n oethReceived,\n address(this)\n );\n\n // 3.) Setup params for CCIP transfer\n\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(woethOnSourceChain),\n amount: woethReceived\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({\n receiver: abi.encode(receiver), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n // See: https://docs.chain.link/ccip/best-practices#setting-gaslimit\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n // ZAP ϟ\n //slither-disable-next-line arbitrary-send-eth\n messageId = ccipRouter.ccipSend{ value: feeAmount }(\n destinationChainSelector,\n message\n );\n\n // Emit Zap event with message details\n emit Zap(messageId, msg.sender, receiver, amount);\n\n // Return the message ID\n return messageId;\n }\n}\n" + }, + "hardhat/console.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.4.22 <0.9.0;\n\nlibrary console {\n address constant CONSOLE_ADDRESS =\n 0x000000000000000000636F6e736F6c652e6c6f67;\n\n function _sendLogPayloadImplementation(bytes memory payload) internal view {\n address consoleAddress = CONSOLE_ADDRESS;\n /// @solidity memory-safe-assembly\n assembly {\n pop(\n staticcall(\n gas(),\n consoleAddress,\n add(payload, 32),\n mload(payload),\n 0,\n 0\n )\n )\n }\n }\n\n function _castToPure(\n function(bytes memory) internal view fnIn\n ) internal pure returns (function(bytes memory) pure fnOut) {\n assembly {\n fnOut := fnIn\n }\n }\n\n function _sendLogPayload(bytes memory payload) internal pure {\n _castToPure(_sendLogPayloadImplementation)(payload);\n }\n\n function log() internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log()\"));\n }\n\n function logInt(int256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(int256)\", p0));\n }\n\n function logUint(uint256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256)\", p0));\n }\n\n function logString(string memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n }\n\n function logBool(bool p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n }\n\n function logAddress(address p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n }\n\n function logBytes(bytes memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes)\", p0));\n }\n\n function logBytes1(bytes1 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes1)\", p0));\n }\n\n function logBytes2(bytes2 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes2)\", p0));\n }\n\n function logBytes3(bytes3 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes3)\", p0));\n }\n\n function logBytes4(bytes4 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes4)\", p0));\n }\n\n function logBytes5(bytes5 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes5)\", p0));\n }\n\n function logBytes6(bytes6 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes6)\", p0));\n }\n\n function logBytes7(bytes7 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes7)\", p0));\n }\n\n function logBytes8(bytes8 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes8)\", p0));\n }\n\n function logBytes9(bytes9 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes9)\", p0));\n }\n\n function logBytes10(bytes10 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes10)\", p0));\n }\n\n function logBytes11(bytes11 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes11)\", p0));\n }\n\n function logBytes12(bytes12 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes12)\", p0));\n }\n\n function logBytes13(bytes13 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes13)\", p0));\n }\n\n function logBytes14(bytes14 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes14)\", p0));\n }\n\n function logBytes15(bytes15 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes15)\", p0));\n }\n\n function logBytes16(bytes16 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes16)\", p0));\n }\n\n function logBytes17(bytes17 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes17)\", p0));\n }\n\n function logBytes18(bytes18 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes18)\", p0));\n }\n\n function logBytes19(bytes19 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes19)\", p0));\n }\n\n function logBytes20(bytes20 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes20)\", p0));\n }\n\n function logBytes21(bytes21 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes21)\", p0));\n }\n\n function logBytes22(bytes22 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes22)\", p0));\n }\n\n function logBytes23(bytes23 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes23)\", p0));\n }\n\n function logBytes24(bytes24 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes24)\", p0));\n }\n\n function logBytes25(bytes25 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes25)\", p0));\n }\n\n function logBytes26(bytes26 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes26)\", p0));\n }\n\n function logBytes27(bytes27 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes27)\", p0));\n }\n\n function logBytes28(bytes28 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes28)\", p0));\n }\n\n function logBytes29(bytes29 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes29)\", p0));\n }\n\n function logBytes30(bytes30 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes30)\", p0));\n }\n\n function logBytes31(bytes31 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes31)\", p0));\n }\n\n function logBytes32(bytes32 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes32)\", p0));\n }\n\n function log(uint256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256)\", p0));\n }\n\n function log(string memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n }\n\n function log(bool p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n }\n\n function log(address p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n }\n\n function log(uint256 p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256)\", p0, p1));\n }\n\n function log(uint256 p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string)\", p0, p1));\n }\n\n function log(uint256 p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool)\", p0, p1));\n }\n\n function log(uint256 p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address)\", p0, p1));\n }\n\n function log(string memory p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256)\", p0, p1));\n }\n\n function log(string memory p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string)\", p0, p1));\n }\n\n function log(string memory p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool)\", p0, p1));\n }\n\n function log(string memory p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address)\", p0, p1));\n }\n\n function log(bool p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256)\", p0, p1));\n }\n\n function log(bool p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string)\", p0, p1));\n }\n\n function log(bool p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool)\", p0, p1));\n }\n\n function log(bool p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address)\", p0, p1));\n }\n\n function log(address p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256)\", p0, p1));\n }\n\n function log(address p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string)\", p0, p1));\n }\n\n function log(address p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool)\", p0, p1));\n }\n\n function log(address p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address)\", p0, p1));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,address)\", p0, p1, p2, p3));\n }\n}\n" + }, + "lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC4626 } from \"../../../../interfaces/IERC4626.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\n// From Open Zeppelin draft PR commit:\n// fac43034dca85ff539db3fc8aa2a7084b843d454\n// https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3171\n\nabstract contract ERC4626 is ERC20, IERC4626 {\n IERC20Metadata private immutable _asset;\n\n constructor(IERC20Metadata __asset) {\n _asset = __asset;\n }\n\n /** @dev See {IERC4262-asset} */\n function asset() public view virtual override returns (address) {\n return address(_asset);\n }\n\n /** @dev See {IERC4262-totalAssets} */\n function totalAssets() public view virtual override returns (uint256) {\n return _asset.balanceOf(address(this));\n }\n\n /**\n * @dev See {IERC4262-convertToShares}\n *\n * Will revert if asserts > 0, totalSupply > 0 and totalAssets = 0. That corresponds to a case where any asset\n * would represent an infinite amout of shares.\n */\n function convertToShares(uint256 assets) public view virtual override returns (uint256 shares) {\n uint256 supply = totalSupply();\n\n return\n (assets == 0 || supply == 0)\n ? (assets * 10**decimals()) / 10**_asset.decimals()\n : (assets * supply) / totalAssets();\n }\n\n /** @dev See {IERC4262-convertToAssets} */\n function convertToAssets(uint256 shares) public view virtual override returns (uint256 assets) {\n uint256 supply = totalSupply();\n\n return (supply == 0) ? (shares * 10**_asset.decimals()) / 10**decimals() : (shares * totalAssets()) / supply;\n }\n\n /** @dev See {IERC4262-maxDeposit} */\n function maxDeposit(address) public view virtual override returns (uint256) {\n return type(uint256).max;\n }\n\n /** @dev See {IERC4262-maxMint} */\n function maxMint(address) public view virtual override returns (uint256) {\n return type(uint256).max;\n }\n\n /** @dev See {IERC4262-maxWithdraw} */\n function maxWithdraw(address owner) public view virtual override returns (uint256) {\n return convertToAssets(balanceOf(owner));\n }\n\n /** @dev See {IERC4262-maxRedeem} */\n function maxRedeem(address owner) public view virtual override returns (uint256) {\n return balanceOf(owner);\n }\n\n /** @dev See {IERC4262-previewDeposit} */\n function previewDeposit(uint256 assets) public view virtual override returns (uint256) {\n return convertToShares(assets);\n }\n\n /** @dev See {IERC4262-previewMint} */\n function previewMint(uint256 shares) public view virtual override returns (uint256) {\n uint256 assets = convertToAssets(shares);\n return assets + (convertToShares(assets) < shares ? 1 : 0);\n }\n\n /** @dev See {IERC4262-previewWithdraw} */\n function previewWithdraw(uint256 assets) public view virtual override returns (uint256) {\n uint256 shares = convertToShares(assets);\n return shares + (convertToAssets(shares) < assets ? 1 : 0);\n }\n\n /** @dev See {IERC4262-previewRedeem} */\n function previewRedeem(uint256 shares) public view virtual override returns (uint256) {\n return convertToAssets(shares);\n }\n\n /** @dev See {IERC4262-deposit} */\n function deposit(uint256 assets, address receiver) public virtual override returns (uint256) {\n require(assets <= maxDeposit(receiver), \"ERC4626: deposit more then max\");\n\n address caller = _msgSender();\n uint256 shares = previewDeposit(assets);\n\n // if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through\n // the tokensToSend hook, so we need to transfer before we mint to keep the invariants.\n SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);\n _mint(receiver, shares);\n\n emit Deposit(caller, receiver, assets, shares);\n\n return shares;\n }\n\n /** @dev See {IERC4262-mint} */\n function mint(uint256 shares, address receiver) public virtual override returns (uint256) {\n require(shares <= maxMint(receiver), \"ERC4626: mint more then max\");\n\n address caller = _msgSender();\n uint256 assets = previewMint(shares);\n\n // if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through\n // the tokensToSend hook, so we need to transfer before we mint to keep the invariants.\n SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);\n _mint(receiver, shares);\n\n emit Deposit(caller, receiver, assets, shares);\n\n return assets;\n }\n\n /** @dev See {IERC4262-withdraw} */\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) public virtual override returns (uint256) {\n require(assets <= maxWithdraw(owner), \"ERC4626: withdraw more then max\");\n\n address caller = _msgSender();\n uint256 shares = previewWithdraw(assets);\n\n if (caller != owner) {\n _spendAllowance(owner, caller, shares);\n }\n\n // if _asset is ERC777, transfer can call reenter AFTER the transfer happens through\n // the tokensReceived hook, so we need to transfer after we burn to keep the invariants.\n _burn(owner, shares);\n SafeERC20.safeTransfer(_asset, receiver, assets);\n\n emit Withdraw(caller, receiver, owner, assets, shares);\n\n return shares;\n }\n\n /** @dev See {IERC4262-redeem} */\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) public virtual override returns (uint256) {\n require(shares <= maxRedeem(owner), \"ERC4626: redeem more then max\");\n\n address caller = _msgSender();\n uint256 assets = previewRedeem(shares);\n\n if (caller != owner) {\n _spendAllowance(owner, caller, shares);\n }\n\n // if _asset is ERC777, transfer can call reenter AFTER the transfer happens through\n // the tokensReceived hook, so we need to transfer after we burn to keep the invariants.\n _burn(owner, shares);\n SafeERC20.safeTransfer(_asset, receiver, assets);\n\n emit Withdraw(caller, receiver, owner, assets, shares);\n\n return assets;\n }\n\n // Included here, since this method was not yet present in\n // the version of Open Zeppelin ERC20 code we use.\n function _spendAllowance(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n uint256 currentAllowance = allowance(owner, spender);\n if (currentAllowance != type(uint256).max) {\n require(currentAllowance >= amount, \"ERC20: insufficient allowance\");\n unchecked {\n _approve(owner, spender, currentAllowance - amount);\n }\n }\n }\n}" + }, + "lib/openzeppelin/interfaces/IERC4626.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IERC4626 is IERC20, IERC20Metadata {\n event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);\n\n event Withdraw(\n address indexed caller,\n address indexed receiver,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n\n /**\n * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.\n *\n * - MUST be an ERC-20 token contract.\n * - MUST NOT revert.\n */\n function asset() external view returns (address assetTokenAddress);\n\n /**\n * @dev Returns the total amount of the underlying asset that is “managed” by Vault.\n *\n * - SHOULD include any compounding that occurs from yield.\n * - MUST be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT revert.\n */\n function totalAssets() external view returns (uint256 totalManagedAssets);\n\n /**\n * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal\n * scenario where all the conditions are met.\n *\n * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT show any variations depending on the caller.\n * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n * - MUST NOT revert.\n *\n * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n * from.\n */\n function convertToShares(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal\n * scenario where all the conditions are met.\n *\n * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT show any variations depending on the caller.\n * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n * - MUST NOT revert.\n *\n * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n * from.\n */\n function convertToAssets(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,\n * through a deposit call.\n *\n * - MUST return a limited value if receiver is subject to some deposit limit.\n * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.\n * - MUST NOT revert.\n */\n function maxDeposit(address receiver) external view returns (uint256 maxAssets);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given\n * current on-chain conditions.\n *\n * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit\n * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called\n * in the same transaction.\n * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the\n * deposit would be accepted, regardless if the user has enough tokens approved, etc.\n * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by depositing.\n */\n function previewDeposit(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.\n *\n * - MUST emit the Deposit event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * deposit execution, and are accounted for during deposit.\n * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not\n * approving enough underlying tokens to the Vault contract, etc).\n *\n * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n */\n function deposit(uint256 assets, address receiver) external returns (uint256 shares);\n\n /**\n * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.\n * - MUST return a limited value if receiver is subject to some mint limit.\n * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.\n * - MUST NOT revert.\n */\n function maxMint(address receiver) external view returns (uint256 maxShares);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given\n * current on-chain conditions.\n *\n * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call\n * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the\n * same transaction.\n * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint\n * would be accepted, regardless if the user has enough tokens approved, etc.\n * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by minting.\n */\n function previewMint(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.\n *\n * - MUST emit the Deposit event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint\n * execution, and are accounted for during mint.\n * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not\n * approving enough underlying tokens to the Vault contract, etc).\n *\n * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n */\n function mint(uint256 shares, address receiver) external returns (uint256 assets);\n\n /**\n * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the\n * Vault, through a withdraw call.\n *\n * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n * - MUST NOT revert.\n */\n function maxWithdraw(address owner) external view returns (uint256 maxAssets);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,\n * given current on-chain conditions.\n *\n * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw\n * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if\n * called\n * in the same transaction.\n * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though\n * the withdrawal would be accepted, regardless if the user has enough shares, etc.\n * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by depositing.\n */\n function previewWithdraw(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.\n *\n * - MUST emit the Withdraw event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * withdraw execution, and are accounted for during withdraw.\n * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner\n * not having enough shares, etc).\n *\n * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n * Those methods should be performed separately.\n */\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares);\n\n /**\n * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,\n * through a redeem call.\n *\n * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.\n * - MUST NOT revert.\n */\n function maxRedeem(address owner) external view returns (uint256 maxShares);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,\n * given current on-chain conditions.\n *\n * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call\n * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the\n * same transaction.\n * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the\n * redemption would be accepted, regardless if the user has enough shares, etc.\n * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by redeeming.\n */\n function previewRedeem(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.\n *\n * - MUST emit the Withdraw event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * redeem execution, and are accounted for during redeem.\n * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner\n * not having enough shares, etc).\n *\n * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n * Those methods should be performed separately.\n */\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets);\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.20;\n\nimport {Panic} from \"../Panic.sol\";\nimport {SafeCast} from \"./SafeCast.sol\";\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n enum Rounding {\n Floor, // Toward negative infinity\n Ceil, // Toward positive infinity\n Trunc, // Toward zero\n Expand // Away from zero\n }\n\n /**\n * @dev Return the 512-bit addition of two uint256.\n *\n * The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low.\n */\n function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {\n assembly (\"memory-safe\") {\n low := add(a, b)\n high := lt(low, a)\n }\n }\n\n /**\n * @dev Return the 512-bit multiplication of two uint256.\n *\n * The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low.\n */\n function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {\n // 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use\n // the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n // variables such that product = high * 2²⁵⁶ + low.\n assembly (\"memory-safe\") {\n let mm := mulmod(a, b, not(0))\n low := mul(a, b)\n high := sub(sub(mm, low), lt(mm, low))\n }\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, with a success flag (no overflow).\n */\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a + b;\n success = c >= a;\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).\n */\n function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a - b;\n success = c <= a;\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).\n */\n function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a * b;\n assembly (\"memory-safe\") {\n // Only true when the multiplication doesn't overflow\n // (c / a == b) || (a == 0)\n success := or(eq(div(c, a), b), iszero(a))\n }\n // equivalent to: success ? c : 0\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the division of two unsigned integers, with a success flag (no division by zero).\n */\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n success = b > 0;\n assembly (\"memory-safe\") {\n // The `DIV` opcode returns zero when the denominator is 0.\n result := div(a, b)\n }\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).\n */\n function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n success = b > 0;\n assembly (\"memory-safe\") {\n // The `MOD` opcode returns zero when the denominator is 0.\n result := mod(a, b)\n }\n }\n }\n\n /**\n * @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing.\n */\n function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {\n (bool success, uint256 result) = tryAdd(a, b);\n return ternary(success, result, type(uint256).max);\n }\n\n /**\n * @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.\n */\n function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {\n (, uint256 result) = trySub(a, b);\n return result;\n }\n\n /**\n * @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing.\n */\n function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {\n (bool success, uint256 result) = tryMul(a, b);\n return ternary(success, result, type(uint256).max);\n }\n\n /**\n * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.\n *\n * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.\n * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute\n * one branch when needed, making this function more expensive.\n */\n function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {\n unchecked {\n // branchless ternary works because:\n // b ^ (a ^ b) == a\n // b ^ 0 == b\n return b ^ ((a ^ b) * SafeCast.toUint(condition));\n }\n }\n\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return ternary(a > b, a, b);\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return ternary(a < b, a, b);\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds towards infinity instead\n * of rounding towards zero.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n if (b == 0) {\n // Guarantee the same behavior as in a regular Solidity division.\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n\n // The following calculation ensures accurate ceiling division without overflow.\n // Since a is non-zero, (a - 1) / b will not overflow.\n // The largest possible result occurs when (a - 1) / b is type(uint256).max,\n // but the largest value we can obtain is type(uint256).max - 1, which happens\n // when a = type(uint256).max and b = 1.\n unchecked {\n return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);\n }\n }\n\n /**\n * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or\n * denominator == 0.\n *\n * Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by\n * Uniswap Labs also under MIT license.\n */\n function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {\n unchecked {\n (uint256 high, uint256 low) = mul512(x, y);\n\n // Handle non-overflow cases, 256 by 256 division.\n if (high == 0) {\n // Solidity will revert if denominator == 0, unlike the div opcode on its own.\n // The surrounding unchecked block does not change this fact.\n // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.\n return low / denominator;\n }\n\n // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.\n if (denominator <= high) {\n Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));\n }\n\n ///////////////////////////////////////////////\n // 512 by 256 division.\n ///////////////////////////////////////////////\n\n // Make division exact by subtracting the remainder from [high low].\n uint256 remainder;\n assembly (\"memory-safe\") {\n // Compute remainder using mulmod.\n remainder := mulmod(x, y, denominator)\n\n // Subtract 256 bit number from 512 bit number.\n high := sub(high, gt(remainder, low))\n low := sub(low, remainder)\n }\n\n // Factor powers of two out of denominator and compute largest power of two divisor of denominator.\n // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.\n\n uint256 twos = denominator & (0 - denominator);\n assembly (\"memory-safe\") {\n // Divide denominator by twos.\n denominator := div(denominator, twos)\n\n // Divide [high low] by twos.\n low := div(low, twos)\n\n // Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.\n twos := add(div(sub(0, twos), twos), 1)\n }\n\n // Shift in bits from high into low.\n low |= high * twos;\n\n // Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such\n // that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for\n // four bits. That is, denominator * inv ≡ 1 mod 2⁴.\n uint256 inverse = (3 * denominator) ^ 2;\n\n // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also\n // works in modular arithmetic, doubling the correct bits in each step.\n inverse *= 2 - denominator * inverse; // inverse mod 2⁸\n inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶\n inverse *= 2 - denominator * inverse; // inverse mod 2³²\n inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴\n inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸\n inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶\n\n // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n // This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is\n // less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and high\n // is no longer required.\n result = low * inverse;\n return result;\n }\n }\n\n /**\n * @dev Calculates x * y / denominator with full precision, following the selected rounding direction.\n */\n function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {\n return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);\n }\n\n /**\n * @dev Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256.\n */\n function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) {\n unchecked {\n (uint256 high, uint256 low) = mul512(x, y);\n if (high >= 1 << n) {\n Panic.panic(Panic.UNDER_OVERFLOW);\n }\n return (high << (256 - n)) | (low >> n);\n }\n }\n\n /**\n * @dev Calculates x * y >> n with full precision, following the selected rounding direction.\n */\n function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) {\n return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 0);\n }\n\n /**\n * @dev Calculate the modular multiplicative inverse of a number in Z/nZ.\n *\n * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.\n * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.\n *\n * If the input value is not inversible, 0 is returned.\n *\n * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the\n * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.\n */\n function invMod(uint256 a, uint256 n) internal pure returns (uint256) {\n unchecked {\n if (n == 0) return 0;\n\n // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)\n // Used to compute integers x and y such that: ax + ny = gcd(a, n).\n // When the gcd is 1, then the inverse of a modulo n exists and it's x.\n // ax + ny = 1\n // ax = 1 + (-y)n\n // ax ≡ 1 (mod n) # x is the inverse of a modulo n\n\n // If the remainder is 0 the gcd is n right away.\n uint256 remainder = a % n;\n uint256 gcd = n;\n\n // Therefore the initial coefficients are:\n // ax + ny = gcd(a, n) = n\n // 0a + 1n = n\n int256 x = 0;\n int256 y = 1;\n\n while (remainder != 0) {\n uint256 quotient = gcd / remainder;\n\n (gcd, remainder) = (\n // The old remainder is the next gcd to try.\n remainder,\n // Compute the next remainder.\n // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd\n // where gcd is at most n (capped to type(uint256).max)\n gcd - remainder * quotient\n );\n\n (x, y) = (\n // Increment the coefficient of a.\n y,\n // Decrement the coefficient of n.\n // Can overflow, but the result is casted to uint256 so that the\n // next value of y is \"wrapped around\" to a value between 0 and n - 1.\n x - y * int256(quotient)\n );\n }\n\n if (gcd != 1) return 0; // No inverse exists.\n return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.\n }\n }\n\n /**\n * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.\n *\n * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is\n * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that\n * `a**(p-2)` is the modular multiplicative inverse of a in Fp.\n *\n * NOTE: this function does NOT check that `p` is a prime greater than `2`.\n */\n function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {\n unchecked {\n return Math.modExp(a, p - 2, p);\n }\n }\n\n /**\n * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)\n *\n * Requirements:\n * - modulus can't be zero\n * - underlying staticcall to precompile must succeed\n *\n * IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make\n * sure the chain you're using it on supports the precompiled contract for modular exponentiation\n * at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,\n * the underlying function will succeed given the lack of a revert, but the result may be incorrectly\n * interpreted as 0.\n */\n function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {\n (bool success, uint256 result) = tryModExp(b, e, m);\n if (!success) {\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n return result;\n }\n\n /**\n * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).\n * It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying\n * to operate modulo 0 or if the underlying precompile reverted.\n *\n * IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain\n * you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in\n * https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack\n * of a revert, but the result may be incorrectly interpreted as 0.\n */\n function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {\n if (m == 0) return (false, 0);\n assembly (\"memory-safe\") {\n let ptr := mload(0x40)\n // | Offset | Content | Content (Hex) |\n // |-----------|------------|--------------------------------------------------------------------|\n // | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x60:0x7f | value of b | 0x<.............................................................b> |\n // | 0x80:0x9f | value of e | 0x<.............................................................e> |\n // | 0xa0:0xbf | value of m | 0x<.............................................................m> |\n mstore(ptr, 0x20)\n mstore(add(ptr, 0x20), 0x20)\n mstore(add(ptr, 0x40), 0x20)\n mstore(add(ptr, 0x60), b)\n mstore(add(ptr, 0x80), e)\n mstore(add(ptr, 0xa0), m)\n\n // Given the result < m, it's guaranteed to fit in 32 bytes,\n // so we can use the memory scratch space located at offset 0.\n success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)\n result := mload(0x00)\n }\n }\n\n /**\n * @dev Variant of {modExp} that supports inputs of arbitrary length.\n */\n function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {\n (bool success, bytes memory result) = tryModExp(b, e, m);\n if (!success) {\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n return result;\n }\n\n /**\n * @dev Variant of {tryModExp} that supports inputs of arbitrary length.\n */\n function tryModExp(\n bytes memory b,\n bytes memory e,\n bytes memory m\n ) internal view returns (bool success, bytes memory result) {\n if (_zeroBytes(m)) return (false, new bytes(0));\n\n uint256 mLen = m.length;\n\n // Encode call args in result and move the free memory pointer\n result = abi.encodePacked(b.length, e.length, mLen, b, e, m);\n\n assembly (\"memory-safe\") {\n let dataPtr := add(result, 0x20)\n // Write result on top of args to avoid allocating extra memory.\n success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)\n // Overwrite the length.\n // result.length > returndatasize() is guaranteed because returndatasize() == m.length\n mstore(result, mLen)\n // Set the memory pointer after the returned data.\n mstore(0x40, add(dataPtr, mLen))\n }\n }\n\n /**\n * @dev Returns whether the provided byte array is zero.\n */\n function _zeroBytes(bytes memory byteArray) private pure returns (bool) {\n for (uint256 i = 0; i < byteArray.length; ++i) {\n if (byteArray[i] != 0) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded\n * towards zero.\n *\n * This method is based on Newton's method for computing square roots; the algorithm is restricted to only\n * using integer operations.\n */\n function sqrt(uint256 a) internal pure returns (uint256) {\n unchecked {\n // Take care of easy edge cases when a == 0 or a == 1\n if (a <= 1) {\n return a;\n }\n\n // In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a\n // sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between\n // the current value as `ε_n = | x_n - sqrt(a) |`.\n //\n // For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root\n // of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is\n // bigger than any uint256.\n //\n // By noticing that\n // `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`\n // we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar\n // to the msb function.\n uint256 aa = a;\n uint256 xn = 1;\n\n if (aa >= (1 << 128)) {\n aa >>= 128;\n xn <<= 64;\n }\n if (aa >= (1 << 64)) {\n aa >>= 64;\n xn <<= 32;\n }\n if (aa >= (1 << 32)) {\n aa >>= 32;\n xn <<= 16;\n }\n if (aa >= (1 << 16)) {\n aa >>= 16;\n xn <<= 8;\n }\n if (aa >= (1 << 8)) {\n aa >>= 8;\n xn <<= 4;\n }\n if (aa >= (1 << 4)) {\n aa >>= 4;\n xn <<= 2;\n }\n if (aa >= (1 << 2)) {\n xn <<= 1;\n }\n\n // We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).\n //\n // We can refine our estimation by noticing that the middle of that interval minimizes the error.\n // If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).\n // This is going to be our x_0 (and ε_0)\n xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)\n\n // From here, Newton's method give us:\n // x_{n+1} = (x_n + a / x_n) / 2\n //\n // One should note that:\n // x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a\n // = ((x_n² + a) / (2 * x_n))² - a\n // = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a\n // = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)\n // = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)\n // = (x_n² - a)² / (2 * x_n)²\n // = ((x_n² - a) / (2 * x_n))²\n // ≥ 0\n // Which proves that for all n ≥ 1, sqrt(a) ≤ x_n\n //\n // This gives us the proof of quadratic convergence of the sequence:\n // ε_{n+1} = | x_{n+1} - sqrt(a) |\n // = | (x_n + a / x_n) / 2 - sqrt(a) |\n // = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |\n // = | (x_n - sqrt(a))² / (2 * x_n) |\n // = | ε_n² / (2 * x_n) |\n // = ε_n² / | (2 * x_n) |\n //\n // For the first iteration, we have a special case where x_0 is known:\n // ε_1 = ε_0² / | (2 * x_0) |\n // ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))\n // ≤ 2**(2*e-4) / (3 * 2**(e-1))\n // ≤ 2**(e-3) / 3\n // ≤ 2**(e-3-log2(3))\n // ≤ 2**(e-4.5)\n //\n // For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:\n // ε_{n+1} = ε_n² / | (2 * x_n) |\n // ≤ (2**(e-k))² / (2 * 2**(e-1))\n // ≤ 2**(2*e-2*k) / 2**e\n // ≤ 2**(e-2*k)\n xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above\n xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5\n xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9\n xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18\n xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36\n xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72\n\n // Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision\n // ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either\n // sqrt(a) or sqrt(a) + 1.\n return xn - SafeCast.toUint(xn > a / xn);\n }\n }\n\n /**\n * @dev Calculates sqrt(a), following the selected rounding direction.\n */\n function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = sqrt(a);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);\n }\n }\n\n /**\n * @dev Return the log in base 2 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n */\n function log2(uint256 x) internal pure returns (uint256 r) {\n // If value has upper 128 bits set, log2 result is at least 128\n r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;\n // If upper 64 bits of 128-bit half set, add 64 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;\n // If upper 32 bits of 64-bit half set, add 32 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;\n // If upper 16 bits of 32-bit half set, add 16 to result\n r |= SafeCast.toUint((x >> r) > 0xffff) << 4;\n // If upper 8 bits of 16-bit half set, add 8 to result\n r |= SafeCast.toUint((x >> r) > 0xff) << 3;\n // If upper 4 bits of 8-bit half set, add 4 to result\n r |= SafeCast.toUint((x >> r) > 0xf) << 2;\n\n // Shifts value right by the current result and use it as an index into this lookup table:\n //\n // | x (4 bits) | index | table[index] = MSB position |\n // |------------|---------|-----------------------------|\n // | 0000 | 0 | table[0] = 0 |\n // | 0001 | 1 | table[1] = 0 |\n // | 0010 | 2 | table[2] = 1 |\n // | 0011 | 3 | table[3] = 1 |\n // | 0100 | 4 | table[4] = 2 |\n // | 0101 | 5 | table[5] = 2 |\n // | 0110 | 6 | table[6] = 2 |\n // | 0111 | 7 | table[7] = 2 |\n // | 1000 | 8 | table[8] = 3 |\n // | 1001 | 9 | table[9] = 3 |\n // | 1010 | 10 | table[10] = 3 |\n // | 1011 | 11 | table[11] = 3 |\n // | 1100 | 12 | table[12] = 3 |\n // | 1101 | 13 | table[13] = 3 |\n // | 1110 | 14 | table[14] = 3 |\n // | 1111 | 15 | table[15] = 3 |\n //\n // The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.\n assembly (\"memory-safe\") {\n r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))\n }\n }\n\n /**\n * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log2(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);\n }\n }\n\n /**\n * @dev Return the log in base 10 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n */\n function log10(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >= 10 ** 64) {\n value /= 10 ** 64;\n result += 64;\n }\n if (value >= 10 ** 32) {\n value /= 10 ** 32;\n result += 32;\n }\n if (value >= 10 ** 16) {\n value /= 10 ** 16;\n result += 16;\n }\n if (value >= 10 ** 8) {\n value /= 10 ** 8;\n result += 8;\n }\n if (value >= 10 ** 4) {\n value /= 10 ** 4;\n result += 4;\n }\n if (value >= 10 ** 2) {\n value /= 10 ** 2;\n result += 2;\n }\n if (value >= 10 ** 1) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log10(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);\n }\n }\n\n /**\n * @dev Return the log in base 256 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n *\n * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n */\n function log256(uint256 x) internal pure returns (uint256 r) {\n // If value has upper 128 bits set, log2 result is at least 128\n r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;\n // If upper 64 bits of 128-bit half set, add 64 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;\n // If upper 32 bits of 64-bit half set, add 32 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;\n // If upper 16 bits of 32-bit half set, add 16 to result\n r |= SafeCast.toUint((x >> r) > 0xffff) << 4;\n // Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8\n return (r >> 3) | SafeCast.toUint((x >> r) > 0xff);\n }\n\n /**\n * @dev Return the log in base 256, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log256(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);\n }\n }\n\n /**\n * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.\n */\n function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {\n return uint8(rounding) % 2 == 1;\n }\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)\n// This file was procedurally generated from scripts/generate/templates/SafeCast.js.\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n */\nlibrary SafeCast {\n /**\n * @dev Value doesn't fit in an uint of `bits` size.\n */\n error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);\n\n /**\n * @dev An int value doesn't fit in an uint of `bits` size.\n */\n error SafeCastOverflowedIntToUint(int256 value);\n\n /**\n * @dev Value doesn't fit in an int of `bits` size.\n */\n error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);\n\n /**\n * @dev An uint value doesn't fit in an int of `bits` size.\n */\n error SafeCastOverflowedUintToInt(uint256 value);\n\n /**\n * @dev Returns the downcasted uint248 from uint256, reverting on\n * overflow (when the input is greater than largest uint248).\n *\n * Counterpart to Solidity's `uint248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n */\n function toUint248(uint256 value) internal pure returns (uint248) {\n if (value > type(uint248).max) {\n revert SafeCastOverflowedUintDowncast(248, value);\n }\n return uint248(value);\n }\n\n /**\n * @dev Returns the downcasted uint240 from uint256, reverting on\n * overflow (when the input is greater than largest uint240).\n *\n * Counterpart to Solidity's `uint240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n */\n function toUint240(uint256 value) internal pure returns (uint240) {\n if (value > type(uint240).max) {\n revert SafeCastOverflowedUintDowncast(240, value);\n }\n return uint240(value);\n }\n\n /**\n * @dev Returns the downcasted uint232 from uint256, reverting on\n * overflow (when the input is greater than largest uint232).\n *\n * Counterpart to Solidity's `uint232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n */\n function toUint232(uint256 value) internal pure returns (uint232) {\n if (value > type(uint232).max) {\n revert SafeCastOverflowedUintDowncast(232, value);\n }\n return uint232(value);\n }\n\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n if (value > type(uint224).max) {\n revert SafeCastOverflowedUintDowncast(224, value);\n }\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint216 from uint256, reverting on\n * overflow (when the input is greater than largest uint216).\n *\n * Counterpart to Solidity's `uint216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n */\n function toUint216(uint256 value) internal pure returns (uint216) {\n if (value > type(uint216).max) {\n revert SafeCastOverflowedUintDowncast(216, value);\n }\n return uint216(value);\n }\n\n /**\n * @dev Returns the downcasted uint208 from uint256, reverting on\n * overflow (when the input is greater than largest uint208).\n *\n * Counterpart to Solidity's `uint208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n */\n function toUint208(uint256 value) internal pure returns (uint208) {\n if (value > type(uint208).max) {\n revert SafeCastOverflowedUintDowncast(208, value);\n }\n return uint208(value);\n }\n\n /**\n * @dev Returns the downcasted uint200 from uint256, reverting on\n * overflow (when the input is greater than largest uint200).\n *\n * Counterpart to Solidity's `uint200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n */\n function toUint200(uint256 value) internal pure returns (uint200) {\n if (value > type(uint200).max) {\n revert SafeCastOverflowedUintDowncast(200, value);\n }\n return uint200(value);\n }\n\n /**\n * @dev Returns the downcasted uint192 from uint256, reverting on\n * overflow (when the input is greater than largest uint192).\n *\n * Counterpart to Solidity's `uint192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n */\n function toUint192(uint256 value) internal pure returns (uint192) {\n if (value > type(uint192).max) {\n revert SafeCastOverflowedUintDowncast(192, value);\n }\n return uint192(value);\n }\n\n /**\n * @dev Returns the downcasted uint184 from uint256, reverting on\n * overflow (when the input is greater than largest uint184).\n *\n * Counterpart to Solidity's `uint184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n */\n function toUint184(uint256 value) internal pure returns (uint184) {\n if (value > type(uint184).max) {\n revert SafeCastOverflowedUintDowncast(184, value);\n }\n return uint184(value);\n }\n\n /**\n * @dev Returns the downcasted uint176 from uint256, reverting on\n * overflow (when the input is greater than largest uint176).\n *\n * Counterpart to Solidity's `uint176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n */\n function toUint176(uint256 value) internal pure returns (uint176) {\n if (value > type(uint176).max) {\n revert SafeCastOverflowedUintDowncast(176, value);\n }\n return uint176(value);\n }\n\n /**\n * @dev Returns the downcasted uint168 from uint256, reverting on\n * overflow (when the input is greater than largest uint168).\n *\n * Counterpart to Solidity's `uint168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n */\n function toUint168(uint256 value) internal pure returns (uint168) {\n if (value > type(uint168).max) {\n revert SafeCastOverflowedUintDowncast(168, value);\n }\n return uint168(value);\n }\n\n /**\n * @dev Returns the downcasted uint160 from uint256, reverting on\n * overflow (when the input is greater than largest uint160).\n *\n * Counterpart to Solidity's `uint160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n */\n function toUint160(uint256 value) internal pure returns (uint160) {\n if (value > type(uint160).max) {\n revert SafeCastOverflowedUintDowncast(160, value);\n }\n return uint160(value);\n }\n\n /**\n * @dev Returns the downcasted uint152 from uint256, reverting on\n * overflow (when the input is greater than largest uint152).\n *\n * Counterpart to Solidity's `uint152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n */\n function toUint152(uint256 value) internal pure returns (uint152) {\n if (value > type(uint152).max) {\n revert SafeCastOverflowedUintDowncast(152, value);\n }\n return uint152(value);\n }\n\n /**\n * @dev Returns the downcasted uint144 from uint256, reverting on\n * overflow (when the input is greater than largest uint144).\n *\n * Counterpart to Solidity's `uint144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n */\n function toUint144(uint256 value) internal pure returns (uint144) {\n if (value > type(uint144).max) {\n revert SafeCastOverflowedUintDowncast(144, value);\n }\n return uint144(value);\n }\n\n /**\n * @dev Returns the downcasted uint136 from uint256, reverting on\n * overflow (when the input is greater than largest uint136).\n *\n * Counterpart to Solidity's `uint136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n */\n function toUint136(uint256 value) internal pure returns (uint136) {\n if (value > type(uint136).max) {\n revert SafeCastOverflowedUintDowncast(136, value);\n }\n return uint136(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n if (value > type(uint128).max) {\n revert SafeCastOverflowedUintDowncast(128, value);\n }\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint120 from uint256, reverting on\n * overflow (when the input is greater than largest uint120).\n *\n * Counterpart to Solidity's `uint120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n */\n function toUint120(uint256 value) internal pure returns (uint120) {\n if (value > type(uint120).max) {\n revert SafeCastOverflowedUintDowncast(120, value);\n }\n return uint120(value);\n }\n\n /**\n * @dev Returns the downcasted uint112 from uint256, reverting on\n * overflow (when the input is greater than largest uint112).\n *\n * Counterpart to Solidity's `uint112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n */\n function toUint112(uint256 value) internal pure returns (uint112) {\n if (value > type(uint112).max) {\n revert SafeCastOverflowedUintDowncast(112, value);\n }\n return uint112(value);\n }\n\n /**\n * @dev Returns the downcasted uint104 from uint256, reverting on\n * overflow (when the input is greater than largest uint104).\n *\n * Counterpart to Solidity's `uint104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n */\n function toUint104(uint256 value) internal pure returns (uint104) {\n if (value > type(uint104).max) {\n revert SafeCastOverflowedUintDowncast(104, value);\n }\n return uint104(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n if (value > type(uint96).max) {\n revert SafeCastOverflowedUintDowncast(96, value);\n }\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint88 from uint256, reverting on\n * overflow (when the input is greater than largest uint88).\n *\n * Counterpart to Solidity's `uint88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n */\n function toUint88(uint256 value) internal pure returns (uint88) {\n if (value > type(uint88).max) {\n revert SafeCastOverflowedUintDowncast(88, value);\n }\n return uint88(value);\n }\n\n /**\n * @dev Returns the downcasted uint80 from uint256, reverting on\n * overflow (when the input is greater than largest uint80).\n *\n * Counterpart to Solidity's `uint80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n */\n function toUint80(uint256 value) internal pure returns (uint80) {\n if (value > type(uint80).max) {\n revert SafeCastOverflowedUintDowncast(80, value);\n }\n return uint80(value);\n }\n\n /**\n * @dev Returns the downcasted uint72 from uint256, reverting on\n * overflow (when the input is greater than largest uint72).\n *\n * Counterpart to Solidity's `uint72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n */\n function toUint72(uint256 value) internal pure returns (uint72) {\n if (value > type(uint72).max) {\n revert SafeCastOverflowedUintDowncast(72, value);\n }\n return uint72(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n if (value > type(uint64).max) {\n revert SafeCastOverflowedUintDowncast(64, value);\n }\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint56 from uint256, reverting on\n * overflow (when the input is greater than largest uint56).\n *\n * Counterpart to Solidity's `uint56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n */\n function toUint56(uint256 value) internal pure returns (uint56) {\n if (value > type(uint56).max) {\n revert SafeCastOverflowedUintDowncast(56, value);\n }\n return uint56(value);\n }\n\n /**\n * @dev Returns the downcasted uint48 from uint256, reverting on\n * overflow (when the input is greater than largest uint48).\n *\n * Counterpart to Solidity's `uint48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n */\n function toUint48(uint256 value) internal pure returns (uint48) {\n if (value > type(uint48).max) {\n revert SafeCastOverflowedUintDowncast(48, value);\n }\n return uint48(value);\n }\n\n /**\n * @dev Returns the downcasted uint40 from uint256, reverting on\n * overflow (when the input is greater than largest uint40).\n *\n * Counterpart to Solidity's `uint40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n */\n function toUint40(uint256 value) internal pure returns (uint40) {\n if (value > type(uint40).max) {\n revert SafeCastOverflowedUintDowncast(40, value);\n }\n return uint40(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n if (value > type(uint32).max) {\n revert SafeCastOverflowedUintDowncast(32, value);\n }\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint24 from uint256, reverting on\n * overflow (when the input is greater than largest uint24).\n *\n * Counterpart to Solidity's `uint24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n */\n function toUint24(uint256 value) internal pure returns (uint24) {\n if (value > type(uint24).max) {\n revert SafeCastOverflowedUintDowncast(24, value);\n }\n return uint24(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n if (value > type(uint16).max) {\n revert SafeCastOverflowedUintDowncast(16, value);\n }\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n if (value > type(uint8).max) {\n revert SafeCastOverflowedUintDowncast(8, value);\n }\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n if (value < 0) {\n revert SafeCastOverflowedIntToUint(value);\n }\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int248 from int256, reverting on\n * overflow (when the input is less than smallest int248 or\n * greater than largest int248).\n *\n * Counterpart to Solidity's `int248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n */\n function toInt248(int256 value) internal pure returns (int248 downcasted) {\n downcasted = int248(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(248, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int240 from int256, reverting on\n * overflow (when the input is less than smallest int240 or\n * greater than largest int240).\n *\n * Counterpart to Solidity's `int240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n */\n function toInt240(int256 value) internal pure returns (int240 downcasted) {\n downcasted = int240(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(240, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int232 from int256, reverting on\n * overflow (when the input is less than smallest int232 or\n * greater than largest int232).\n *\n * Counterpart to Solidity's `int232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n */\n function toInt232(int256 value) internal pure returns (int232 downcasted) {\n downcasted = int232(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(232, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int224 from int256, reverting on\n * overflow (when the input is less than smallest int224 or\n * greater than largest int224).\n *\n * Counterpart to Solidity's `int224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toInt224(int256 value) internal pure returns (int224 downcasted) {\n downcasted = int224(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(224, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int216 from int256, reverting on\n * overflow (when the input is less than smallest int216 or\n * greater than largest int216).\n *\n * Counterpart to Solidity's `int216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n */\n function toInt216(int256 value) internal pure returns (int216 downcasted) {\n downcasted = int216(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(216, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int208 from int256, reverting on\n * overflow (when the input is less than smallest int208 or\n * greater than largest int208).\n *\n * Counterpart to Solidity's `int208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n */\n function toInt208(int256 value) internal pure returns (int208 downcasted) {\n downcasted = int208(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(208, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int200 from int256, reverting on\n * overflow (when the input is less than smallest int200 or\n * greater than largest int200).\n *\n * Counterpart to Solidity's `int200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n */\n function toInt200(int256 value) internal pure returns (int200 downcasted) {\n downcasted = int200(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(200, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int192 from int256, reverting on\n * overflow (when the input is less than smallest int192 or\n * greater than largest int192).\n *\n * Counterpart to Solidity's `int192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n */\n function toInt192(int256 value) internal pure returns (int192 downcasted) {\n downcasted = int192(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(192, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int184 from int256, reverting on\n * overflow (when the input is less than smallest int184 or\n * greater than largest int184).\n *\n * Counterpart to Solidity's `int184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n */\n function toInt184(int256 value) internal pure returns (int184 downcasted) {\n downcasted = int184(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(184, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int176 from int256, reverting on\n * overflow (when the input is less than smallest int176 or\n * greater than largest int176).\n *\n * Counterpart to Solidity's `int176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n */\n function toInt176(int256 value) internal pure returns (int176 downcasted) {\n downcasted = int176(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(176, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int168 from int256, reverting on\n * overflow (when the input is less than smallest int168 or\n * greater than largest int168).\n *\n * Counterpart to Solidity's `int168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n */\n function toInt168(int256 value) internal pure returns (int168 downcasted) {\n downcasted = int168(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(168, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int160 from int256, reverting on\n * overflow (when the input is less than smallest int160 or\n * greater than largest int160).\n *\n * Counterpart to Solidity's `int160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n */\n function toInt160(int256 value) internal pure returns (int160 downcasted) {\n downcasted = int160(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(160, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int152 from int256, reverting on\n * overflow (when the input is less than smallest int152 or\n * greater than largest int152).\n *\n * Counterpart to Solidity's `int152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n */\n function toInt152(int256 value) internal pure returns (int152 downcasted) {\n downcasted = int152(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(152, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int144 from int256, reverting on\n * overflow (when the input is less than smallest int144 or\n * greater than largest int144).\n *\n * Counterpart to Solidity's `int144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n */\n function toInt144(int256 value) internal pure returns (int144 downcasted) {\n downcasted = int144(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(144, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int136 from int256, reverting on\n * overflow (when the input is less than smallest int136 or\n * greater than largest int136).\n *\n * Counterpart to Solidity's `int136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n */\n function toInt136(int256 value) internal pure returns (int136 downcasted) {\n downcasted = int136(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(136, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toInt128(int256 value) internal pure returns (int128 downcasted) {\n downcasted = int128(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(128, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int120 from int256, reverting on\n * overflow (when the input is less than smallest int120 or\n * greater than largest int120).\n *\n * Counterpart to Solidity's `int120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n */\n function toInt120(int256 value) internal pure returns (int120 downcasted) {\n downcasted = int120(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(120, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int112 from int256, reverting on\n * overflow (when the input is less than smallest int112 or\n * greater than largest int112).\n *\n * Counterpart to Solidity's `int112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n */\n function toInt112(int256 value) internal pure returns (int112 downcasted) {\n downcasted = int112(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(112, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int104 from int256, reverting on\n * overflow (when the input is less than smallest int104 or\n * greater than largest int104).\n *\n * Counterpart to Solidity's `int104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n */\n function toInt104(int256 value) internal pure returns (int104 downcasted) {\n downcasted = int104(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(104, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int96 from int256, reverting on\n * overflow (when the input is less than smallest int96 or\n * greater than largest int96).\n *\n * Counterpart to Solidity's `int96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toInt96(int256 value) internal pure returns (int96 downcasted) {\n downcasted = int96(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(96, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int88 from int256, reverting on\n * overflow (when the input is less than smallest int88 or\n * greater than largest int88).\n *\n * Counterpart to Solidity's `int88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n */\n function toInt88(int256 value) internal pure returns (int88 downcasted) {\n downcasted = int88(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(88, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int80 from int256, reverting on\n * overflow (when the input is less than smallest int80 or\n * greater than largest int80).\n *\n * Counterpart to Solidity's `int80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n */\n function toInt80(int256 value) internal pure returns (int80 downcasted) {\n downcasted = int80(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(80, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int72 from int256, reverting on\n * overflow (when the input is less than smallest int72 or\n * greater than largest int72).\n *\n * Counterpart to Solidity's `int72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n */\n function toInt72(int256 value) internal pure returns (int72 downcasted) {\n downcasted = int72(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(72, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toInt64(int256 value) internal pure returns (int64 downcasted) {\n downcasted = int64(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(64, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int56 from int256, reverting on\n * overflow (when the input is less than smallest int56 or\n * greater than largest int56).\n *\n * Counterpart to Solidity's `int56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n */\n function toInt56(int256 value) internal pure returns (int56 downcasted) {\n downcasted = int56(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(56, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int48 from int256, reverting on\n * overflow (when the input is less than smallest int48 or\n * greater than largest int48).\n *\n * Counterpart to Solidity's `int48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n */\n function toInt48(int256 value) internal pure returns (int48 downcasted) {\n downcasted = int48(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(48, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int40 from int256, reverting on\n * overflow (when the input is less than smallest int40 or\n * greater than largest int40).\n *\n * Counterpart to Solidity's `int40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n */\n function toInt40(int256 value) internal pure returns (int40 downcasted) {\n downcasted = int40(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(40, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toInt32(int256 value) internal pure returns (int32 downcasted) {\n downcasted = int32(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(32, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int24 from int256, reverting on\n * overflow (when the input is less than smallest int24 or\n * greater than largest int24).\n *\n * Counterpart to Solidity's `int24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n */\n function toInt24(int256 value) internal pure returns (int24 downcasted) {\n downcasted = int24(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(24, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toInt16(int256 value) internal pure returns (int16 downcasted) {\n downcasted = int16(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(16, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n */\n function toInt8(int256 value) internal pure returns (int8 downcasted) {\n downcasted = int8(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(8, value);\n }\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n if (value > uint256(type(int256).max)) {\n revert SafeCastOverflowedUintToInt(value);\n }\n return int256(value);\n }\n\n /**\n * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.\n */\n function toUint(bool b) internal pure returns (uint256 u) {\n assembly (\"memory-safe\") {\n u := iszero(iszero(b))\n }\n }\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/Panic.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Helper library for emitting standardized panic codes.\n *\n * ```solidity\n * contract Example {\n * using Panic for uint256;\n *\n * // Use any of the declared internal constants\n * function foo() { Panic.GENERIC.panic(); }\n *\n * // Alternatively\n * function foo() { Panic.panic(Panic.GENERIC); }\n * }\n * ```\n *\n * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].\n *\n * _Available since v5.1._\n */\n// slither-disable-next-line unused-state\nlibrary Panic {\n /// @dev generic / unspecified error\n uint256 internal constant GENERIC = 0x00;\n /// @dev used by the assert() builtin\n uint256 internal constant ASSERT = 0x01;\n /// @dev arithmetic underflow or overflow\n uint256 internal constant UNDER_OVERFLOW = 0x11;\n /// @dev division or modulo by zero\n uint256 internal constant DIVISION_BY_ZERO = 0x12;\n /// @dev enum conversion error\n uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;\n /// @dev invalid encoding in storage\n uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;\n /// @dev empty array pop\n uint256 internal constant EMPTY_ARRAY_POP = 0x31;\n /// @dev array out of bounds access\n uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;\n /// @dev resource error (too large allocation or too large array)\n uint256 internal constant RESOURCE_ERROR = 0x41;\n /// @dev calling invalid internal function\n uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;\n\n /// @dev Reverts with a panic code. Recommended to use with\n /// the internal constants with predefined codes.\n function panic(uint256 code) internal pure {\n assembly (\"memory-safe\") {\n mstore(0x00, 0x4e487b71)\n mstore(0x20, code)\n revert(0x1c, 0x24)\n }\n }\n}" + }, + "lib/rooster/v2-common/libraries/Constants.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\n// factory contraints on pools\nuint8 constant MAX_PROTOCOL_FEE_RATIO_D3 = 0.25e3; // 25%\nuint256 constant MAX_PROTOCOL_LENDING_FEE_RATE_D18 = 0.02e18; // 2%\nuint64 constant MAX_POOL_FEE_D18 = 0.9e18; // 90%\nuint64 constant MIN_LOOKBACK = 1 seconds;\n\n// pool constraints\nuint8 constant NUMBER_OF_KINDS = 4;\nint32 constant NUMBER_OF_KINDS_32 = int32(int8(NUMBER_OF_KINDS));\nuint256 constant MAX_TICK = 322_378; // max price 1e14 in D18 scale\nint32 constant MAX_TICK_32 = int32(int256(MAX_TICK));\nint32 constant MIN_TICK_32 = int32(-int256(MAX_TICK));\nuint256 constant MAX_BINS_TO_MERGE = 3;\nuint128 constant MINIMUM_LIQUIDITY = 1e8;\n\n// accessor named constants\nuint8 constant ALL_KINDS_MASK = 0xF; // 0b1111\nuint8 constant PERMISSIONED_LIQUIDITY_MASK = 0x10; // 0b010000\nuint8 constant PERMISSIONED_SWAP_MASK = 0x20; // 0b100000\nuint8 constant OPTIONS_MASK = ALL_KINDS_MASK | PERMISSIONED_LIQUIDITY_MASK | PERMISSIONED_SWAP_MASK; // 0b111111\n\n// named values\naddress constant MERGED_LP_BALANCE_ADDRESS = address(0);\nuint256 constant MERGED_LP_BALANCE_SUBACCOUNT = 0;\nuint128 constant ONE = 1e18;\nuint128 constant ONE_SQUARED = 1e36;\nint256 constant INT256_ONE = 1e18;\nuint256 constant ONE_D8 = 1e8;\nuint256 constant ONE_D3 = 1e3;\nint40 constant INT_ONE_D8 = 1e8;\nint40 constant HALF_TICK_D8 = 0.5e8;\nuint8 constant DEFAULT_DECIMALS = 18;\nuint256 constant DEFAULT_SCALE = 1;\nbytes constant EMPTY_PRICE_BREAKS = hex\"010000000000000000000000\";" + }, + "lib/rooster/v2-common/libraries/Math.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport {Math as OzMath} from \"../../openzeppelin-custom/contracts/utils/math/Math.sol\";\n\nimport {ONE, DEFAULT_SCALE, DEFAULT_DECIMALS, INT_ONE_D8, ONE_SQUARED} from \"./Constants.sol\";\n\n/**\n * @notice Math functions.\n */\nlibrary Math {\n /**\n * @notice Returns the lesser of two values.\n * @param x First uint256 value.\n * @param y Second uint256 value.\n */\n function min(uint256 x, uint256 y) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), lt(y, x)))\n }\n }\n\n /**\n * @notice Returns the lesser of two uint128 values.\n * @param x First uint128 value.\n * @param y Second uint128 value.\n */\n function min128(uint128 x, uint128 y) internal pure returns (uint128 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), lt(y, x)))\n }\n }\n\n /**\n * @notice Returns the lesser of two int256 values.\n * @param x First int256 value.\n * @param y Second int256 value.\n */\n function min(int256 x, int256 y) internal pure returns (int256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), slt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two uint256 values.\n * @param x First uint256 value.\n * @param y Second uint256 value.\n */\n function max(uint256 x, uint256 y) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), gt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two int256 values.\n * @param x First int256 value.\n * @param y Second int256 value.\n */\n function max(int256 x, int256 y) internal pure returns (int256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), sgt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two uint128 values.\n * @param x First uint128 value.\n * @param y Second uint128 value.\n */\n function max128(uint128 x, uint128 y) internal pure returns (uint128 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), gt(y, x)))\n }\n }\n\n /**\n * @notice Thresholds a value to be within the specified bounds.\n * @param value The value to bound.\n * @param lowerLimit The minimum allowable value.\n * @param upperLimit The maximum allowable value.\n */\n function boundValue(\n uint256 value,\n uint256 lowerLimit,\n uint256 upperLimit\n ) internal pure returns (uint256 outputValue) {\n outputValue = min(max(value, lowerLimit), upperLimit);\n }\n\n /**\n * @notice Returns the difference between two uint128 values or zero if the result would be negative.\n * @param x The minuend.\n * @param y The subtrahend.\n */\n function clip128(uint128 x, uint128 y) internal pure returns (uint128) {\n unchecked {\n return x < y ? 0 : x - y;\n }\n }\n\n /**\n * @notice Returns the difference between two uint256 values or zero if the result would be negative.\n * @param x The minuend.\n * @param y The subtrahend.\n */\n function clip(uint256 x, uint256 y) internal pure returns (uint256) {\n unchecked {\n return x < y ? 0 : x - y;\n }\n }\n\n /**\n * @notice Divides one uint256 by another, rounding down to the nearest\n * integer.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divFloor(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivFloor(x, ONE, y);\n }\n\n /**\n * @notice Divides one uint256 by another, rounding up to the nearest integer.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divCeil(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivCeil(x, ONE, y);\n }\n\n /**\n * @notice Multiplies two uint256 values and then divides by ONE, rounding down.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulFloor(uint256 x, uint256 y) internal pure returns (uint256) {\n return OzMath.mulDiv(x, y, ONE);\n }\n\n /**\n * @notice Multiplies two uint256 values and then divides by ONE, rounding up.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulCeil(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivCeil(x, y, ONE);\n }\n\n /**\n * @notice Calculates the multiplicative inverse of a uint256, rounding down.\n * @param x The value to invert.\n */\n function invFloor(uint256 x) internal pure returns (uint256) {\n unchecked {\n return ONE_SQUARED / x;\n }\n }\n\n /**\n * @notice Calculates the multiplicative inverse of a uint256, rounding up.\n * @param denominator The value to invert.\n */\n function invCeil(uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // divide z - 1 by the denominator and add 1.\n z := add(div(sub(ONE_SQUARED, 1), denominator), 1)\n }\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding down.\n * @param x The multiplicand.\n * @param y The multiplier.\n * @param k The divisor.\n */\n function mulDivFloor(uint256 x, uint256 y, uint256 k) internal pure returns (uint256 result) {\n result = OzMath.mulDiv(x, y, max(1, k));\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding up if there's a remainder.\n * @param x The multiplicand.\n * @param y The multiplier.\n * @param k The divisor.\n */\n function mulDivCeil(uint256 x, uint256 y, uint256 k) internal pure returns (uint256 result) {\n result = mulDivFloor(x, y, k);\n if (mulmod(x, y, max(1, k)) != 0) result = result + 1;\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding\n * down. Will revert if `x * y` is larger than `type(uint256).max`.\n * @param x The first operand for multiplication.\n * @param y The second operand for multiplication.\n * @param denominator The divisor after multiplication.\n */\n function mulDivDown(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // Store x * y in z for now.\n z := mul(x, y)\n if iszero(denominator) {\n denominator := 1\n }\n\n if iszero(or(iszero(x), eq(div(z, x), y))) {\n revert(0, 0)\n }\n\n // Divide z by the denominator.\n z := div(z, denominator)\n }\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding\n * up. Will revert if `x * y` is larger than `type(uint256).max`.\n * @param x The first operand for multiplication.\n * @param y The second operand for multiplication.\n * @param denominator The divisor after multiplication.\n */\n function mulDivUp(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // Store x * y in z for now.\n z := mul(x, y)\n if iszero(denominator) {\n denominator := 1\n }\n\n if iszero(or(iszero(x), eq(div(z, x), y))) {\n revert(0, 0)\n }\n\n // First, divide z - 1 by the denominator and add 1.\n // We allow z - 1 to underflow if z is 0, because we multiply the\n // end result by 0 if z is zero, ensuring we return 0 if z is zero.\n z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))\n }\n }\n\n /**\n * @notice Multiplies a uint256 by another and divides by a constant,\n * rounding down. Will revert if `x * y` is larger than\n * `type(uint256).max`.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulDown(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivDown(x, y, ONE);\n }\n\n /**\n * @notice Divides a uint256 by another, rounding down the result. Will\n * revert if `x * 1e18` is larger than `type(uint256).max`.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divDown(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivDown(x, ONE, y);\n }\n\n /**\n * @notice Divides a uint256 by another, rounding up the result. Will\n * revert if `x * 1e18` is larger than `type(uint256).max`.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divUp(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivUp(x, ONE, y);\n }\n\n /**\n * @notice Scales a number based on a difference in decimals from a default.\n * @param decimals The new decimal precision.\n */\n function scale(uint8 decimals) internal pure returns (uint256) {\n unchecked {\n if (decimals == DEFAULT_DECIMALS) {\n return DEFAULT_SCALE;\n } else {\n return 10 ** (DEFAULT_DECIMALS - decimals);\n }\n }\n }\n\n /**\n * @notice Adjusts a scaled amount to the token decimal scale.\n * @param amount The scaled amount.\n * @param scaleFactor The scaling factor to adjust by.\n * @param ceil Whether to round up (true) or down (false).\n */\n function ammScaleToTokenScale(uint256 amount, uint256 scaleFactor, bool ceil) internal pure returns (uint256 z) {\n unchecked {\n if (scaleFactor == DEFAULT_SCALE || amount == 0) {\n return amount;\n } else {\n if (!ceil) return amount / scaleFactor;\n assembly (\"memory-safe\") {\n z := add(div(sub(amount, 1), scaleFactor), 1)\n }\n }\n }\n }\n\n /**\n * @notice Adjusts a token amount to the D18 AMM scale.\n * @param amount The amount in token scale.\n * @param scaleFactor The scale factor for adjustment.\n */\n function tokenScaleToAmmScale(uint256 amount, uint256 scaleFactor) internal pure returns (uint256) {\n if (scaleFactor == DEFAULT_SCALE) {\n return amount;\n } else {\n return amount * scaleFactor;\n }\n }\n\n /**\n * @notice Returns the absolute value of a signed 32-bit integer.\n * @param x The integer to take the absolute value of.\n */\n function abs32(int32 x) internal pure returns (uint32) {\n unchecked {\n return uint32(x < 0 ? -x : x);\n }\n }\n\n /**\n * @notice Returns the absolute value of a signed 256-bit integer.\n * @param x The integer to take the absolute value of.\n */\n function abs(int256 x) internal pure returns (uint256) {\n unchecked {\n return uint256(x < 0 ? -x : x);\n }\n }\n\n /**\n * @notice Calculates the integer square root of a uint256 rounded down.\n * @param x The number to take the square root of.\n */\n function sqrt(uint256 x) internal pure returns (uint256 z) {\n // from https://github.com/transmissions11/solmate/blob/e8f96f25d48fe702117ce76c79228ca4f20206cb/src/utils/FixedPointMathLib.sol\n assembly (\"memory-safe\") {\n let y := x\n z := 181\n\n if iszero(lt(y, 0x10000000000000000000000000000000000)) {\n y := shr(128, y)\n z := shl(64, z)\n }\n if iszero(lt(y, 0x1000000000000000000)) {\n y := shr(64, y)\n z := shl(32, z)\n }\n if iszero(lt(y, 0x10000000000)) {\n y := shr(32, y)\n z := shl(16, z)\n }\n if iszero(lt(y, 0x1000000)) {\n y := shr(16, y)\n z := shl(8, z)\n }\n\n z := shr(18, mul(z, add(y, 65536)))\n\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n\n z := sub(z, lt(div(x, z), z))\n }\n }\n\n /**\n * @notice Computes the floor of a D8-scaled number as an int32, ignoring\n * potential overflow in the cast.\n * @param val The D8-scaled number.\n */\n function floorD8Unchecked(int256 val) internal pure returns (int32) {\n int32 val32;\n bool check;\n unchecked {\n val32 = int32(val / INT_ONE_D8);\n check = (val < 0 && val % INT_ONE_D8 != 0);\n }\n return check ? val32 - 1 : val32;\n }\n}" + }, + "lib/rooster/v2-common/libraries/TickMath.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport {Math as OzMath} from \"../../openzeppelin-custom/contracts/utils/math/Math.sol\";\nimport {Math} from \"./Math.sol\";\nimport {MAX_TICK, ONE} from \"./Constants.sol\";\n\n/**\n * @notice Math functions related to tick operations.\n */\n// slither-disable-start divide-before-multiply\nlibrary TickMath {\n using Math for uint256;\n\n error TickMaxExceeded(int256 tick);\n\n /**\n * @notice Compute the lower and upper sqrtPrice of a tick.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n */\n function tickSqrtPrices(\n uint256 tickSpacing,\n int32 _tick\n ) internal pure returns (uint256 sqrtLowerPrice, uint256 sqrtUpperPrice) {\n unchecked {\n sqrtLowerPrice = tickSqrtPrice(tickSpacing, _tick);\n sqrtUpperPrice = tickSqrtPrice(tickSpacing, _tick + 1);\n }\n }\n\n /**\n * @notice Compute the base tick value from the pool tick and the\n * tickSpacing. Revert if base tick is beyond the max tick boundary.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n */\n function subTickIndex(uint256 tickSpacing, int32 _tick) internal pure returns (uint32 subTick) {\n subTick = Math.abs32(_tick);\n subTick *= uint32(tickSpacing);\n if (subTick > MAX_TICK) {\n revert TickMaxExceeded(_tick);\n }\n }\n\n /**\n * @notice Calculate the square root price for a given tick and tick spacing.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n * @return _result The square root price.\n */\n function tickSqrtPrice(uint256 tickSpacing, int32 _tick) internal pure returns (uint256 _result) {\n unchecked {\n uint256 tick = subTickIndex(tickSpacing, _tick);\n\n uint256 ratio = tick & 0x1 != 0 ? 0xfffcb933bd6fad9d3af5f0b9f25db4d6 : 0x100000000000000000000000000000000;\n if (tick & 0x2 != 0) ratio = (ratio * 0xfff97272373d41fd789c8cb37ffcaa1c) >> 128;\n if (tick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656ac9229c67059486f389) >> 128;\n if (tick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e81259b3cddc7a064941) >> 128;\n if (tick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f67b19e8887e0bd251eb7) >> 128;\n if (tick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98cd2e57b660be99eb2c4a) >> 128;\n if (tick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c9838804e327cb417cafcb) >> 128;\n if (tick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99d51e2cc356c2f617dbe0) >> 128;\n if (tick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900aecf64236ab31f1f9dcb5) >> 128;\n if (tick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac4d9194200696907cf2e37) >> 128;\n if (tick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b88206f8abe8a3b44dd9be) >> 128;\n if (tick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c578ef4f1d17b2b235d480) >> 128;\n if (tick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd254ee83bdd3f248e7e785e) >> 128;\n if (tick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d8f7dd10e744d913d033333) >> 128;\n if (tick & 0x4000 != 0) ratio = (ratio * 0x70d869a156ddd32a39e257bc3f50aa9b) >> 128;\n if (tick & 0x8000 != 0) ratio = (ratio * 0x31be135f97da6e09a19dc367e3b6da40) >> 128;\n if (tick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7e5a9780b0cc4e25d61a56) >> 128;\n if (tick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedbcb3a6ccb7ce618d14225) >> 128;\n if (tick & 0x40000 != 0) ratio = (ratio * 0x2216e584f630389b2052b8db590e) >> 128;\n if (_tick > 0) ratio = type(uint256).max / ratio;\n _result = (ratio * ONE) >> 128;\n }\n }\n\n /**\n * @notice Calculate liquidity of a tick.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n */\n function getTickL(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n ) internal pure returns (uint256 liquidity) {\n // known:\n // - sqrt price values are different\n // - reserveA and reserveB fit in 128 bit\n // - sqrt price is in (1e-7, 1e7)\n // - D18 max for uint256 is 1.15e59\n // - D18 min is 1e-18\n\n unchecked {\n // diff is in (5e-12, 4e6); max tick spacing is 10_000\n uint256 diff = sqrtUpperTickPrice - sqrtLowerTickPrice;\n\n // Need to maximize precision by shifting small values A and B up so\n // that they use more of the available bit range. Two constraints to\n // consider: we need A * B * diff / sqrtPrice to be bigger than 1e-18\n // when the bump is not in play. This constrains the threshold for\n // bumping to be at least 77 bit; ie, either a or b needs 2^77 which\n // means that term A * B * diff / sqrtPrice > 1e-18.\n //\n // At the other end, the second constraint is that b^2 needs to fit in\n // a 256-bit number, so, post bump, the max reserve value needs to be\n // less than 6e22. With a 78-bit threshold and a 57-bit bump, we have A\n // and B are in (1.4e-1, 4.4e22 (2^(78+57))) with bump, and one of A or\n // B is at least 2^78 without the bump, but the other reserve value may\n // be as small as 1 wei.\n uint256 precisionBump = 0;\n if ((reserveA >> 78) == 0 && (reserveB >> 78) == 0) {\n precisionBump = 57;\n reserveA <<= precisionBump;\n reserveB <<= precisionBump;\n }\n\n if (reserveB == 0) return Math.divDown(reserveA, diff) >> precisionBump;\n if (reserveA == 0)\n return Math.mulDivDown(reserveB.mulDown(sqrtLowerTickPrice), sqrtUpperTickPrice, diff) >> precisionBump;\n\n // b is in (7.2e-9 (2^57 / 1e7 / 2), 2.8e29 (2^(78+57) * 1e7 / 2)) with bump\n // b is in a subset of the same range without bump\n uint256 b = (reserveA.divDown(sqrtUpperTickPrice) + reserveB.mulDown(sqrtLowerTickPrice)) >> 1;\n\n // b^2 is in (5.1e-17, 4.8e58); and will not overflow on either end;\n // A*B is in (3e-13 (2^78 / 1e18 * 1e-18), 1.9e45) without bump and is in a subset range with bump\n // A*B*diff/sqrtUpper is in (1.5e-17 (3e-13 * 5e-12 * 1e7), 7.6e58);\n\n // Since b^2 is at the upper edge of the precision range, we are not\n // able to multiply the argument of the sqrt by 1e18, instead, we move\n // this factor outside of the sqrt. The resulting loss of precision\n // means that this liquidity value is a lower bound on the tick\n // liquidity\n return\n OzMath.mulDiv(\n b +\n Math.sqrt(\n (OzMath.mulDiv(b, b, ONE) +\n OzMath.mulDiv(reserveB.mulFloor(reserveA), diff, sqrtUpperTickPrice))\n ) *\n 1e9,\n sqrtUpperTickPrice,\n diff\n ) >> precisionBump;\n }\n }\n\n /**\n * @notice Calculate square root price of a tick. Returns left edge of the\n * tick if the tick has no reserves.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n * @return sqrtPrice The calculated square root price.\n */\n function getSqrtPrice(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice,\n uint256 liquidity\n ) internal pure returns (uint256 sqrtPrice) {\n unchecked {\n if (reserveA == 0) {\n return sqrtLowerTickPrice;\n }\n if (reserveB == 0) {\n return sqrtUpperTickPrice;\n }\n sqrtPrice = Math.sqrt(\n ONE *\n (reserveA + liquidity.mulDown(sqrtLowerTickPrice)).divDown(\n reserveB + liquidity.divDown(sqrtUpperTickPrice)\n )\n );\n sqrtPrice = Math.boundValue(sqrtPrice, sqrtLowerTickPrice, sqrtUpperTickPrice);\n }\n }\n\n /**\n * @notice Calculate square root price of a tick. Returns left edge of the\n * tick if the tick has no reserves.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n * @return sqrtPrice The calculated square root price.\n * @return liquidity The calculated liquidity.\n */\n function getTickSqrtPriceAndL(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n ) internal pure returns (uint256 sqrtPrice, uint256 liquidity) {\n liquidity = getTickL(reserveA, reserveB, sqrtLowerTickPrice, sqrtUpperTickPrice);\n sqrtPrice = getSqrtPrice(reserveA, reserveB, sqrtLowerTickPrice, sqrtUpperTickPrice, liquidity);\n }\n}\n// slither-disable-end divide-before-multiply" + }, + "solidity-bytes-utils/contracts/BytesLib.sol": { + "content": "// SPDX-License-Identifier: Unlicense\n/*\n * @title Solidity Bytes Arrays Utils\n * @author Gonçalo Sá \n *\n * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.\n * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.\n */\npragma solidity >=0.8.0 <0.9.0;\n\n\nlibrary BytesLib {\n function concat(\n bytes memory _preBytes,\n bytes memory _postBytes\n )\n internal\n pure\n returns (bytes memory)\n {\n bytes memory tempBytes;\n\n assembly {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // Store the length of the first bytes array at the beginning of\n // the memory for tempBytes.\n let length := mload(_preBytes)\n mstore(tempBytes, length)\n\n // Maintain a memory counter for the current write location in the\n // temp bytes array by adding the 32 bytes for the array length to\n // the starting location.\n let mc := add(tempBytes, 0x20)\n // Stop copying when the memory counter reaches the length of the\n // first bytes array.\n let end := add(mc, length)\n\n for {\n // Initialize a copy counter to the start of the _preBytes data,\n // 32 bytes into its memory.\n let cc := add(_preBytes, 0x20)\n } lt(mc, end) {\n // Increase both counters by 32 bytes each iteration.\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // Write the _preBytes data into the tempBytes memory 32 bytes\n // at a time.\n mstore(mc, mload(cc))\n }\n\n // Add the length of _postBytes to the current length of tempBytes\n // and store it as the new length in the first 32 bytes of the\n // tempBytes memory.\n length := mload(_postBytes)\n mstore(tempBytes, add(length, mload(tempBytes)))\n\n // Move the memory counter back from a multiple of 0x20 to the\n // actual end of the _preBytes data.\n mc := end\n // Stop copying when the memory counter reaches the new combined\n // length of the arrays.\n end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n // Update the free-memory pointer by padding our last write location\n // to 32 bytes: add 31 bytes to the end of tempBytes to move to the\n // next 32 byte block, then round down to the nearest multiple of\n // 32. If the sum of the length of the two arrays is zero then add\n // one before rounding down to leave a blank 32 bytes (the length block with 0).\n mstore(0x40, and(\n add(add(end, iszero(add(length, mload(_preBytes)))), 31),\n not(31) // Round down to the nearest 32 bytes.\n ))\n }\n\n return tempBytes;\n }\n\n function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {\n assembly {\n // Read the first 32 bytes of _preBytes storage, which is the length\n // of the array. (We don't need to use the offset into the slot\n // because arrays use the entire slot.)\n let fslot := sload(_preBytes.slot)\n // Arrays of 31 bytes or less have an even value in their slot,\n // while longer arrays have an odd value. The actual length is\n // the slot divided by two for odd values, and the lowest order\n // byte divided by two for even values.\n // If the slot is even, bitwise and the slot with 255 and divide by\n // two to get the length. If the slot is odd, bitwise and the slot\n // with -1 and divide by two.\n let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)\n let mlength := mload(_postBytes)\n let newlength := add(slength, mlength)\n // slength can contain both the length and contents of the array\n // if length < 32 bytes so let's prepare for that\n // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage\n switch add(lt(slength, 32), lt(newlength, 32))\n case 2 {\n // Since the new array still fits in the slot, we just need to\n // update the contents of the slot.\n // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length\n sstore(\n _preBytes.slot,\n // all the modifications to the slot are inside this\n // next block\n add(\n // we can just add to the slot contents because the\n // bytes we want to change are the LSBs\n fslot,\n add(\n mul(\n div(\n // load the bytes from memory\n mload(add(_postBytes, 0x20)),\n // zero all bytes to the right\n exp(0x100, sub(32, mlength))\n ),\n // and now shift left the number of bytes to\n // leave space for the length in the slot\n exp(0x100, sub(32, newlength))\n ),\n // increase length by the double of the memory\n // bytes length\n mul(mlength, 2)\n )\n )\n )\n }\n case 1 {\n // The stored value fits in the slot, but the combined value\n // will exceed it.\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n let sc := add(keccak256(0x0, 0x20), div(slength, 32))\n\n // save new length\n sstore(_preBytes.slot, add(mul(newlength, 2), 1))\n\n // The contents of the _postBytes array start 32 bytes into\n // the structure. Our first read should obtain the `submod`\n // bytes that can fit into the unused space in the last word\n // of the stored array. To get this, we read 32 bytes starting\n // from `submod`, so the data we read overlaps with the array\n // contents by `submod` bytes. Masking the lowest-order\n // `submod` bytes allows us to add that value directly to the\n // stored value.\n\n let submod := sub(32, slength)\n let mc := add(_postBytes, submod)\n let end := add(_postBytes, mlength)\n let mask := sub(exp(0x100, submod), 1)\n\n sstore(\n sc,\n add(\n and(\n fslot,\n 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00\n ),\n and(mload(mc), mask)\n )\n )\n\n for {\n mc := add(mc, 0x20)\n sc := add(sc, 1)\n } lt(mc, end) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n sstore(sc, mload(mc))\n }\n\n mask := exp(0x100, sub(mc, end))\n\n sstore(sc, mul(div(mload(mc), mask), mask))\n }\n default {\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n // Start copying to the last used word of the stored array.\n let sc := add(keccak256(0x0, 0x20), div(slength, 32))\n\n // save new length\n sstore(_preBytes.slot, add(mul(newlength, 2), 1))\n\n // Copy over the first `submod` bytes of the new data as in\n // case 1 above.\n let slengthmod := mod(slength, 32)\n let mlengthmod := mod(mlength, 32)\n let submod := sub(32, slengthmod)\n let mc := add(_postBytes, submod)\n let end := add(_postBytes, mlength)\n let mask := sub(exp(0x100, submod), 1)\n\n sstore(sc, add(sload(sc), and(mload(mc), mask)))\n\n for {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } lt(mc, end) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n sstore(sc, mload(mc))\n }\n\n mask := exp(0x100, sub(mc, end))\n\n sstore(sc, mul(div(mload(mc), mask), mask))\n }\n }\n }\n\n function slice(\n bytes memory _bytes,\n uint256 _start,\n uint256 _length\n )\n internal\n pure\n returns (bytes memory)\n {\n // We're using the unchecked block below because otherwise execution ends \n // with the native overflow error code.\n unchecked {\n require(_length + 31 >= _length, \"slice_overflow\");\n }\n require(_bytes.length >= _start + _length, \"slice_outOfBounds\");\n\n bytes memory tempBytes;\n\n assembly {\n switch iszero(_length)\n case 0 {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // The first word of the slice result is potentially a partial\n // word read from the original array. To read it, we calculate\n // the length of that partial word and start copying that many\n // bytes into the array. The first word we copy will start with\n // data we don't care about, but the last `lengthmod` bytes will\n // land at the beginning of the contents of the new array. When\n // we're done copying, we overwrite the full first word with\n // the actual length of the slice.\n let lengthmod := and(_length, 31)\n\n // The multiplication in the next line is necessary\n // because when slicing multiples of 32 bytes (lengthmod == 0)\n // the following copy loop was copying the origin's length\n // and then ending prematurely not copying everything it should.\n let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))\n let end := add(mc, _length)\n\n for {\n // The multiplication in the next line has the same exact purpose\n // as the one above.\n let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n mstore(tempBytes, _length)\n\n //update free-memory pointer\n //allocating the array padded to 32 bytes like the compiler does now\n mstore(0x40, and(add(mc, 31), not(31)))\n }\n //if we want a zero-length slice let's just return a zero-length array\n default {\n tempBytes := mload(0x40)\n //zero out the 32 bytes slice we are about to return\n //we need to do it because Solidity does not garbage collect\n mstore(tempBytes, 0)\n\n mstore(0x40, add(tempBytes, 0x20))\n }\n }\n\n return tempBytes;\n }\n\n function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {\n require(_bytes.length >= _start + 20, \"toAddress_outOfBounds\");\n address tempAddress;\n\n assembly {\n tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)\n }\n\n return tempAddress;\n }\n\n function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {\n require(_bytes.length >= _start + 1 , \"toUint8_outOfBounds\");\n uint8 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x1), _start))\n }\n\n return tempUint;\n }\n\n function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {\n require(_bytes.length >= _start + 2, \"toUint16_outOfBounds\");\n uint16 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x2), _start))\n }\n\n return tempUint;\n }\n\n function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {\n require(_bytes.length >= _start + 4, \"toUint32_outOfBounds\");\n uint32 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x4), _start))\n }\n\n return tempUint;\n }\n\n function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {\n require(_bytes.length >= _start + 8, \"toUint64_outOfBounds\");\n uint64 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x8), _start))\n }\n\n return tempUint;\n }\n\n function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {\n require(_bytes.length >= _start + 12, \"toUint96_outOfBounds\");\n uint96 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0xc), _start))\n }\n\n return tempUint;\n }\n\n function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {\n require(_bytes.length >= _start + 16, \"toUint128_outOfBounds\");\n uint128 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x10), _start))\n }\n\n return tempUint;\n }\n\n function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {\n require(_bytes.length >= _start + 32, \"toUint256_outOfBounds\");\n uint256 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x20), _start))\n }\n\n return tempUint;\n }\n\n function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {\n require(_bytes.length >= _start + 32, \"toBytes32_outOfBounds\");\n bytes32 tempBytes32;\n\n assembly {\n tempBytes32 := mload(add(add(_bytes, 0x20), _start))\n }\n\n return tempBytes32;\n }\n\n function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {\n bool success = true;\n\n assembly {\n let length := mload(_preBytes)\n\n // if lengths don't match the arrays are not equal\n switch eq(length, mload(_postBytes))\n case 1 {\n // cb is a circuit breaker in the for loop since there's\n // no said feature for inline assembly loops\n // cb = 1 - don't breaker\n // cb = 0 - break\n let cb := 1\n\n let mc := add(_preBytes, 0x20)\n let end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n // the next line is the loop condition:\n // while(uint256(mc < end) + cb == 2)\n } eq(add(lt(mc, end), cb), 2) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // if any of these checks fails then arrays are not equal\n if iszero(eq(mload(mc), mload(cc))) {\n // unsuccess:\n success := 0\n cb := 0\n }\n }\n }\n default {\n // unsuccess:\n success := 0\n }\n }\n\n return success;\n }\n\n function equalStorage(\n bytes storage _preBytes,\n bytes memory _postBytes\n )\n internal\n view\n returns (bool)\n {\n bool success = true;\n\n assembly {\n // we know _preBytes_offset is 0\n let fslot := sload(_preBytes.slot)\n // Decode the length of the stored array like in concatStorage().\n let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)\n let mlength := mload(_postBytes)\n\n // if lengths don't match the arrays are not equal\n switch eq(slength, mlength)\n case 1 {\n // slength can contain both the length and contents of the array\n // if length < 32 bytes so let's prepare for that\n // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage\n if iszero(iszero(slength)) {\n switch lt(slength, 32)\n case 1 {\n // blank the last byte which is the length\n fslot := mul(div(fslot, 0x100), 0x100)\n\n if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {\n // unsuccess:\n success := 0\n }\n }\n default {\n // cb is a circuit breaker in the for loop since there's\n // no said feature for inline assembly loops\n // cb = 1 - don't breaker\n // cb = 0 - break\n let cb := 1\n\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n let sc := keccak256(0x0, 0x20)\n\n let mc := add(_postBytes, 0x20)\n let end := add(mc, mlength)\n\n // the next line is the loop condition:\n // while(uint256(mc < end) + cb == 2)\n for {} eq(add(lt(mc, end), cb), 2) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n if iszero(eq(sload(sc), mload(mc))) {\n // unsuccess:\n success := 0\n cb := 0\n }\n }\n }\n }\n }\n default {\n // unsuccess:\n success := 0\n }\n }\n\n return success;\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/df854a646f7e413f41affca8d5e99ba6.json b/contracts/deployments/hoodi/solcInputs/df854a646f7e413f41affca8d5e99ba6.json new file mode 100644 index 0000000000..3894f83e0e --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/df854a646f7e413f41affca8d5e99ba6.json @@ -0,0 +1,1152 @@ +{ + "language": "Solidity", + "sources": { + "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {Client} from \"../libraries/Client.sol\";\n\ninterface IRouterClient {\n error UnsupportedDestinationChain(uint64 destChainSelector);\n error InsufficientFeeTokenAmount();\n error InvalidMsgValue();\n\n /// @notice Checks if the given chain ID is supported for sending/receiving.\n /// @param chainSelector The chain to check.\n /// @return supported is true if it is supported, false if not.\n function isChainSupported(uint64 chainSelector) external view returns (bool supported);\n\n /// @notice Gets a list of all supported tokens which can be sent or received\n /// to/from a given chain id.\n /// @param chainSelector The chainSelector.\n /// @return tokens The addresses of all tokens that are supported.\n function getSupportedTokens(uint64 chainSelector) external view returns (address[] memory tokens);\n\n /// @param destinationChainSelector The destination chainSelector\n /// @param message The cross-chain CCIP message including data and/or tokens\n /// @return fee returns execution fee for the message\n /// delivery to destination chain, denominated in the feeToken specified in the message.\n /// @dev Reverts with appropriate reason upon invalid message.\n function getFee(\n uint64 destinationChainSelector,\n Client.EVM2AnyMessage memory message\n ) external view returns (uint256 fee);\n\n /// @notice Request a message to be sent to the destination chain\n /// @param destinationChainSelector The destination chain ID\n /// @param message The cross-chain CCIP message including data and/or tokens\n /// @return messageId The message ID\n /// @dev Note if msg.value is larger than the required fee (from getFee) we accept\n /// the overpayment with no refund.\n /// @dev Reverts with appropriate reason upon invalid message.\n function ccipSend(\n uint64 destinationChainSelector,\n Client.EVM2AnyMessage calldata message\n ) external payable returns (bytes32);\n}\n" + }, + "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// End consumer library.\nlibrary Client {\n /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.\n struct EVMTokenAmount {\n address token; // token address on the local chain.\n uint256 amount; // Amount of tokens.\n }\n\n struct Any2EVMMessage {\n bytes32 messageId; // MessageId corresponding to ccipSend on source.\n uint64 sourceChainSelector; // Source chain selector.\n bytes sender; // abi.decode(sender) if coming from an EVM chain.\n bytes data; // payload sent in original message.\n EVMTokenAmount[] destTokenAmounts; // Tokens and their amounts in their destination chain representation.\n }\n\n // If extraArgs is empty bytes, the default is 200k gas limit.\n struct EVM2AnyMessage {\n bytes receiver; // abi.encode(receiver address) for dest EVM chains\n bytes data; // Data payload\n EVMTokenAmount[] tokenAmounts; // Token transfers\n address feeToken; // Address of feeToken. address(0) means you will send msg.value.\n bytes extraArgs; // Populate this with _argsToBytes(EVMExtraArgsV1)\n }\n\n // bytes4(keccak256(\"CCIP EVMExtraArgsV1\"));\n bytes4 public constant EVM_EXTRA_ARGS_V1_TAG = 0x97a657c9;\n struct EVMExtraArgsV1 {\n uint256 gasLimit;\n }\n\n function _argsToBytes(EVMExtraArgsV1 memory extraArgs) internal pure returns (bytes memory bts) {\n return abi.encodeWithSelector(EVM_EXTRA_ARGS_V1_TAG, extraArgs);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport \"@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol\";\n\nlibrary ExecutorOptions {\n using CalldataBytesLib for bytes;\n\n uint8 internal constant WORKER_ID = 1;\n\n uint8 internal constant OPTION_TYPE_LZRECEIVE = 1;\n uint8 internal constant OPTION_TYPE_NATIVE_DROP = 2;\n uint8 internal constant OPTION_TYPE_LZCOMPOSE = 3;\n uint8 internal constant OPTION_TYPE_ORDERED_EXECUTION = 4;\n uint8 internal constant OPTION_TYPE_LZREAD = 5;\n\n error Executor_InvalidLzReceiveOption();\n error Executor_InvalidNativeDropOption();\n error Executor_InvalidLzComposeOption();\n error Executor_InvalidLzReadOption();\n\n /// @dev decode the next executor option from the options starting from the specified cursor\n /// @param _options [executor_id][executor_option][executor_id][executor_option]...\n /// executor_option = [option_size][option_type][option]\n /// option_size = len(option_type) + len(option)\n /// executor_id: uint8, option_size: uint16, option_type: uint8, option: bytes\n /// @param _cursor the cursor to start decoding from\n /// @return optionType the type of the option\n /// @return option the option of the executor\n /// @return cursor the cursor to start decoding the next executor option\n function nextExecutorOption(\n bytes calldata _options,\n uint256 _cursor\n ) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) {\n unchecked {\n // skip worker id\n cursor = _cursor + 1;\n\n // read option size\n uint16 size = _options.toU16(cursor);\n cursor += 2;\n\n // read option type\n optionType = _options.toU8(cursor);\n\n // startCursor and endCursor are used to slice the option from _options\n uint256 startCursor = cursor + 1; // skip option type\n uint256 endCursor = cursor + size;\n option = _options[startCursor:endCursor];\n cursor += size;\n }\n }\n\n function decodeLzReceiveOption(bytes calldata _option) internal pure returns (uint128 gas, uint128 value) {\n if (_option.length != 16 && _option.length != 32) revert Executor_InvalidLzReceiveOption();\n gas = _option.toU128(0);\n value = _option.length == 32 ? _option.toU128(16) : 0;\n }\n\n function decodeNativeDropOption(bytes calldata _option) internal pure returns (uint128 amount, bytes32 receiver) {\n if (_option.length != 48) revert Executor_InvalidNativeDropOption();\n amount = _option.toU128(0);\n receiver = _option.toB32(16);\n }\n\n function decodeLzComposeOption(\n bytes calldata _option\n ) internal pure returns (uint16 index, uint128 gas, uint128 value) {\n if (_option.length != 18 && _option.length != 34) revert Executor_InvalidLzComposeOption();\n index = _option.toU16(0);\n gas = _option.toU128(2);\n value = _option.length == 34 ? _option.toU128(18) : 0;\n }\n\n function decodeLzReadOption(\n bytes calldata _option\n ) internal pure returns (uint128 gas, uint32 calldataSize, uint128 value) {\n if (_option.length != 20 && _option.length != 36) revert Executor_InvalidLzReadOption();\n gas = _option.toU128(0);\n calldataSize = _option.toU32(16);\n value = _option.length == 36 ? _option.toU128(20) : 0;\n }\n\n function encodeLzReceiveOption(uint128 _gas, uint128 _value) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_gas) : abi.encodePacked(_gas, _value);\n }\n\n function encodeNativeDropOption(uint128 _amount, bytes32 _receiver) internal pure returns (bytes memory) {\n return abi.encodePacked(_amount, _receiver);\n }\n\n function encodeLzComposeOption(uint16 _index, uint128 _gas, uint128 _value) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_index, _gas) : abi.encodePacked(_index, _gas, _value);\n }\n\n function encodeLzReadOption(\n uint128 _gas,\n uint32 _calldataSize,\n uint128 _value\n ) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_gas, _calldataSize) : abi.encodePacked(_gas, _calldataSize, _value);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/DVNOptions.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport { BytesLib } from \"solidity-bytes-utils/contracts/BytesLib.sol\";\n\nimport { BitMap256 } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/BitMaps.sol\";\nimport { CalldataBytesLib } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol\";\n\nlibrary DVNOptions {\n using CalldataBytesLib for bytes;\n using BytesLib for bytes;\n\n uint8 internal constant WORKER_ID = 2;\n uint8 internal constant OPTION_TYPE_PRECRIME = 1;\n\n error DVN_InvalidDVNIdx();\n error DVN_InvalidDVNOptions(uint256 cursor);\n\n /// @dev group dvn options by its idx\n /// @param _options [dvn_id][dvn_option][dvn_id][dvn_option]...\n /// dvn_option = [option_size][dvn_idx][option_type][option]\n /// option_size = len(dvn_idx) + len(option_type) + len(option)\n /// dvn_id: uint8, dvn_idx: uint8, option_size: uint16, option_type: uint8, option: bytes\n /// @return dvnOptions the grouped options, still share the same format of _options\n /// @return dvnIndices the dvn indices\n function groupDVNOptionsByIdx(\n bytes memory _options\n ) internal pure returns (bytes[] memory dvnOptions, uint8[] memory dvnIndices) {\n if (_options.length == 0) return (dvnOptions, dvnIndices);\n\n uint8 numDVNs = getNumDVNs(_options);\n\n // if there is only 1 dvn, we can just return the whole options\n if (numDVNs == 1) {\n dvnOptions = new bytes[](1);\n dvnOptions[0] = _options;\n\n dvnIndices = new uint8[](1);\n dvnIndices[0] = _options.toUint8(3); // dvn idx\n return (dvnOptions, dvnIndices);\n }\n\n // otherwise, we need to group the options by dvn_idx\n dvnIndices = new uint8[](numDVNs);\n dvnOptions = new bytes[](numDVNs);\n unchecked {\n uint256 cursor = 0;\n uint256 start = 0;\n uint8 lastDVNIdx = 255; // 255 is an invalid dvn_idx\n\n while (cursor < _options.length) {\n ++cursor; // skip worker_id\n\n // optionLength asserted in getNumDVNs (skip check)\n uint16 optionLength = _options.toUint16(cursor);\n cursor += 2;\n\n // dvnIdx asserted in getNumDVNs (skip check)\n uint8 dvnIdx = _options.toUint8(cursor);\n\n // dvnIdx must equal to the lastDVNIdx for the first option\n // so it is always skipped in the first option\n // this operation slices out options whenever the scan finds a different lastDVNIdx\n if (lastDVNIdx == 255) {\n lastDVNIdx = dvnIdx;\n } else if (dvnIdx != lastDVNIdx) {\n uint256 len = cursor - start - 3; // 3 is for worker_id and option_length\n bytes memory opt = _options.slice(start, len);\n _insertDVNOptions(dvnOptions, dvnIndices, lastDVNIdx, opt);\n\n // reset the start and lastDVNIdx\n start += len;\n lastDVNIdx = dvnIdx;\n }\n\n cursor += optionLength;\n }\n\n // skip check the cursor here because the cursor is asserted in getNumDVNs\n // if we have reached the end of the options, we need to process the last dvn\n uint256 size = cursor - start;\n bytes memory op = _options.slice(start, size);\n _insertDVNOptions(dvnOptions, dvnIndices, lastDVNIdx, op);\n\n // revert dvnIndices to start from 0\n for (uint8 i = 0; i < numDVNs; ++i) {\n --dvnIndices[i];\n }\n }\n }\n\n function _insertDVNOptions(\n bytes[] memory _dvnOptions,\n uint8[] memory _dvnIndices,\n uint8 _dvnIdx,\n bytes memory _newOptions\n ) internal pure {\n // dvnIdx starts from 0 but default value of dvnIndices is 0,\n // so we tell if the slot is empty by adding 1 to dvnIdx\n if (_dvnIdx == 255) revert DVN_InvalidDVNIdx();\n uint8 dvnIdxAdj = _dvnIdx + 1;\n\n for (uint256 j = 0; j < _dvnIndices.length; ++j) {\n uint8 index = _dvnIndices[j];\n if (dvnIdxAdj == index) {\n _dvnOptions[j] = abi.encodePacked(_dvnOptions[j], _newOptions);\n break;\n } else if (index == 0) {\n // empty slot, that means it is the first time we see this dvn\n _dvnIndices[j] = dvnIdxAdj;\n _dvnOptions[j] = _newOptions;\n break;\n }\n }\n }\n\n /// @dev get the number of unique dvns\n /// @param _options the format is the same as groupDVNOptionsByIdx\n function getNumDVNs(bytes memory _options) internal pure returns (uint8 numDVNs) {\n uint256 cursor = 0;\n BitMap256 bitmap;\n\n // find number of unique dvn_idx\n unchecked {\n while (cursor < _options.length) {\n ++cursor; // skip worker_id\n\n uint16 optionLength = _options.toUint16(cursor);\n cursor += 2;\n if (optionLength < 2) revert DVN_InvalidDVNOptions(cursor); // at least 1 byte for dvn_idx and 1 byte for option_type\n\n uint8 dvnIdx = _options.toUint8(cursor);\n\n // if dvnIdx is not set, increment numDVNs\n // max num of dvns is 255, 255 is an invalid dvn_idx\n // The order of the dvnIdx is not required to be sequential, as enforcing the order may weaken\n // the composability of the options. e.g. if we refrain from enforcing the order, an OApp that has\n // already enforced certain options can append additional options to the end of the enforced\n // ones without restrictions.\n if (dvnIdx == 255) revert DVN_InvalidDVNIdx();\n if (!bitmap.get(dvnIdx)) {\n ++numDVNs;\n bitmap = bitmap.set(dvnIdx);\n }\n\n cursor += optionLength;\n }\n }\n if (cursor != _options.length) revert DVN_InvalidDVNOptions(cursor);\n }\n\n /// @dev decode the next dvn option from _options starting from the specified cursor\n /// @param _options the format is the same as groupDVNOptionsByIdx\n /// @param _cursor the cursor to start decoding\n /// @return optionType the type of the option\n /// @return option the option\n /// @return cursor the cursor to start decoding the next option\n function nextDVNOption(\n bytes calldata _options,\n uint256 _cursor\n ) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) {\n unchecked {\n // skip worker id\n cursor = _cursor + 1;\n\n // read option size\n uint16 size = _options.toU16(cursor);\n cursor += 2;\n\n // read option type\n optionType = _options.toU8(cursor + 1); // skip dvn_idx\n\n // startCursor and endCursor are used to slice the option from _options\n uint256 startCursor = cursor + 2; // skip option type and dvn_idx\n uint256 endCursor = cursor + size;\n option = _options[startCursor:endCursor];\n cursor += size;\n }\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { IMessageLibManager } from \"./IMessageLibManager.sol\";\nimport { IMessagingComposer } from \"./IMessagingComposer.sol\";\nimport { IMessagingChannel } from \"./IMessagingChannel.sol\";\nimport { IMessagingContext } from \"./IMessagingContext.sol\";\n\nstruct MessagingParams {\n uint32 dstEid;\n bytes32 receiver;\n bytes message;\n bytes options;\n bool payInLzToken;\n}\n\nstruct MessagingReceipt {\n bytes32 guid;\n uint64 nonce;\n MessagingFee fee;\n}\n\nstruct MessagingFee {\n uint256 nativeFee;\n uint256 lzTokenFee;\n}\n\nstruct Origin {\n uint32 srcEid;\n bytes32 sender;\n uint64 nonce;\n}\n\ninterface ILayerZeroEndpointV2 is IMessageLibManager, IMessagingComposer, IMessagingChannel, IMessagingContext {\n event PacketSent(bytes encodedPayload, bytes options, address sendLibrary);\n\n event PacketVerified(Origin origin, address receiver, bytes32 payloadHash);\n\n event PacketDelivered(Origin origin, address receiver);\n\n event LzReceiveAlert(\n address indexed receiver,\n address indexed executor,\n Origin origin,\n bytes32 guid,\n uint256 gas,\n uint256 value,\n bytes message,\n bytes extraData,\n bytes reason\n );\n\n event LzTokenSet(address token);\n\n event DelegateSet(address sender, address delegate);\n\n function quote(MessagingParams calldata _params, address _sender) external view returns (MessagingFee memory);\n\n function send(\n MessagingParams calldata _params,\n address _refundAddress\n ) external payable returns (MessagingReceipt memory);\n\n function verify(Origin calldata _origin, address _receiver, bytes32 _payloadHash) external;\n\n function verifiable(Origin calldata _origin, address _receiver) external view returns (bool);\n\n function initializable(Origin calldata _origin, address _receiver) external view returns (bool);\n\n function lzReceive(\n Origin calldata _origin,\n address _receiver,\n bytes32 _guid,\n bytes calldata _message,\n bytes calldata _extraData\n ) external payable;\n\n // oapp can burn messages partially by calling this function with its own business logic if messages are verified in order\n function clear(address _oapp, Origin calldata _origin, bytes32 _guid, bytes calldata _message) external;\n\n function setLzToken(address _lzToken) external;\n\n function lzToken() external view returns (address);\n\n function nativeToken() external view returns (address);\n\n function setDelegate(address _delegate) external;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { Origin } from \"./ILayerZeroEndpointV2.sol\";\n\ninterface ILayerZeroReceiver {\n function allowInitializePath(Origin calldata _origin) external view returns (bool);\n\n function nextNonce(uint32 _eid, bytes32 _sender) external view returns (uint64);\n\n function lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) external payable;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessageLib.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { IERC165 } from \"@openzeppelin/contracts/utils/introspection/IERC165.sol\";\n\nimport { SetConfigParam } from \"./IMessageLibManager.sol\";\n\nenum MessageLibType {\n Send,\n Receive,\n SendAndReceive\n}\n\ninterface IMessageLib is IERC165 {\n function setConfig(address _oapp, SetConfigParam[] calldata _config) external;\n\n function getConfig(uint32 _eid, address _oapp, uint32 _configType) external view returns (bytes memory config);\n\n function isSupportedEid(uint32 _eid) external view returns (bool);\n\n // message libs of same major version are compatible\n function version() external view returns (uint64 major, uint8 minor, uint8 endpointVersion);\n\n function messageLibType() external view returns (MessageLibType);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessageLibManager.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nstruct SetConfigParam {\n uint32 eid;\n uint32 configType;\n bytes config;\n}\n\ninterface IMessageLibManager {\n struct Timeout {\n address lib;\n uint256 expiry;\n }\n\n event LibraryRegistered(address newLib);\n event DefaultSendLibrarySet(uint32 eid, address newLib);\n event DefaultReceiveLibrarySet(uint32 eid, address newLib);\n event DefaultReceiveLibraryTimeoutSet(uint32 eid, address oldLib, uint256 expiry);\n event SendLibrarySet(address sender, uint32 eid, address newLib);\n event ReceiveLibrarySet(address receiver, uint32 eid, address newLib);\n event ReceiveLibraryTimeoutSet(address receiver, uint32 eid, address oldLib, uint256 timeout);\n\n function registerLibrary(address _lib) external;\n\n function isRegisteredLibrary(address _lib) external view returns (bool);\n\n function getRegisteredLibraries() external view returns (address[] memory);\n\n function setDefaultSendLibrary(uint32 _eid, address _newLib) external;\n\n function defaultSendLibrary(uint32 _eid) external view returns (address);\n\n function setDefaultReceiveLibrary(uint32 _eid, address _newLib, uint256 _gracePeriod) external;\n\n function defaultReceiveLibrary(uint32 _eid) external view returns (address);\n\n function setDefaultReceiveLibraryTimeout(uint32 _eid, address _lib, uint256 _expiry) external;\n\n function defaultReceiveLibraryTimeout(uint32 _eid) external view returns (address lib, uint256 expiry);\n\n function isSupportedEid(uint32 _eid) external view returns (bool);\n\n function isValidReceiveLibrary(address _receiver, uint32 _eid, address _lib) external view returns (bool);\n\n /// ------------------- OApp interfaces -------------------\n function setSendLibrary(address _oapp, uint32 _eid, address _newLib) external;\n\n function getSendLibrary(address _sender, uint32 _eid) external view returns (address lib);\n\n function isDefaultSendLibrary(address _sender, uint32 _eid) external view returns (bool);\n\n function setReceiveLibrary(address _oapp, uint32 _eid, address _newLib, uint256 _gracePeriod) external;\n\n function getReceiveLibrary(address _receiver, uint32 _eid) external view returns (address lib, bool isDefault);\n\n function setReceiveLibraryTimeout(address _oapp, uint32 _eid, address _lib, uint256 _expiry) external;\n\n function receiveLibraryTimeout(address _receiver, uint32 _eid) external view returns (address lib, uint256 expiry);\n\n function setConfig(address _oapp, address _lib, SetConfigParam[] calldata _params) external;\n\n function getConfig(\n address _oapp,\n address _lib,\n uint32 _eid,\n uint32 _configType\n ) external view returns (bytes memory config);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingChannel.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingChannel {\n event InboundNonceSkipped(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce);\n event PacketNilified(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);\n event PacketBurnt(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);\n\n function eid() external view returns (uint32);\n\n // this is an emergency function if a message cannot be verified for some reasons\n // required to provide _nextNonce to avoid race condition\n function skip(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce) external;\n\n function nilify(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;\n\n function burn(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;\n\n function nextGuid(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (bytes32);\n\n function inboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);\n\n function outboundNonce(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (uint64);\n\n function inboundPayloadHash(\n address _receiver,\n uint32 _srcEid,\n bytes32 _sender,\n uint64 _nonce\n ) external view returns (bytes32);\n\n function lazyInboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingComposer.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingComposer {\n event ComposeSent(address from, address to, bytes32 guid, uint16 index, bytes message);\n event ComposeDelivered(address from, address to, bytes32 guid, uint16 index);\n event LzComposeAlert(\n address indexed from,\n address indexed to,\n address indexed executor,\n bytes32 guid,\n uint16 index,\n uint256 gas,\n uint256 value,\n bytes message,\n bytes extraData,\n bytes reason\n );\n\n function composeQueue(\n address _from,\n address _to,\n bytes32 _guid,\n uint16 _index\n ) external view returns (bytes32 messageHash);\n\n function sendCompose(address _to, bytes32 _guid, uint16 _index, bytes calldata _message) external;\n\n function lzCompose(\n address _from,\n address _to,\n bytes32 _guid,\n uint16 _index,\n bytes calldata _message,\n bytes calldata _extraData\n ) external payable;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingContext.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingContext {\n function isSendingMessage() external view returns (bool);\n\n function getSendContext() external view returns (uint32 dstEid, address sender);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ISendLib.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { MessagingFee } from \"./ILayerZeroEndpointV2.sol\";\nimport { IMessageLib } from \"./IMessageLib.sol\";\n\nstruct Packet {\n uint64 nonce;\n uint32 srcEid;\n address sender;\n uint32 dstEid;\n bytes32 receiver;\n bytes32 guid;\n bytes message;\n}\n\ninterface ISendLib is IMessageLib {\n function send(\n Packet calldata _packet,\n bytes calldata _options,\n bool _payInLzToken\n ) external returns (MessagingFee memory, bytes memory encodedPacket);\n\n function quote(\n Packet calldata _packet,\n bytes calldata _options,\n bool _payInLzToken\n ) external view returns (MessagingFee memory);\n\n function setTreasury(address _treasury) external;\n\n function withdrawFee(address _to, uint256 _amount) external;\n\n function withdrawLzTokenFee(address _lzToken, address _to, uint256 _amount) external;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/AddressCast.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nlibrary AddressCast {\n error AddressCast_InvalidSizeForAddress();\n error AddressCast_InvalidAddress();\n\n function toBytes32(bytes calldata _addressBytes) internal pure returns (bytes32 result) {\n if (_addressBytes.length > 32) revert AddressCast_InvalidAddress();\n result = bytes32(_addressBytes);\n unchecked {\n uint256 offset = 32 - _addressBytes.length;\n result = result >> (offset * 8);\n }\n }\n\n function toBytes32(address _address) internal pure returns (bytes32 result) {\n result = bytes32(uint256(uint160(_address)));\n }\n\n function toBytes(bytes32 _addressBytes32, uint256 _size) internal pure returns (bytes memory result) {\n if (_size == 0 || _size > 32) revert AddressCast_InvalidSizeForAddress();\n result = new bytes(_size);\n unchecked {\n uint256 offset = 256 - _size * 8;\n assembly {\n mstore(add(result, 32), shl(offset, _addressBytes32))\n }\n }\n }\n\n function toAddress(bytes32 _addressBytes32) internal pure returns (address result) {\n result = address(uint160(uint256(_addressBytes32)));\n }\n\n function toAddress(bytes calldata _addressBytes) internal pure returns (address result) {\n if (_addressBytes.length != 20) revert AddressCast_InvalidAddress();\n result = address(bytes20(_addressBytes));\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nlibrary CalldataBytesLib {\n function toU8(bytes calldata _bytes, uint256 _start) internal pure returns (uint8) {\n return uint8(_bytes[_start]);\n }\n\n function toU16(bytes calldata _bytes, uint256 _start) internal pure returns (uint16) {\n unchecked {\n uint256 end = _start + 2;\n return uint16(bytes2(_bytes[_start:end]));\n }\n }\n\n function toU32(bytes calldata _bytes, uint256 _start) internal pure returns (uint32) {\n unchecked {\n uint256 end = _start + 4;\n return uint32(bytes4(_bytes[_start:end]));\n }\n }\n\n function toU64(bytes calldata _bytes, uint256 _start) internal pure returns (uint64) {\n unchecked {\n uint256 end = _start + 8;\n return uint64(bytes8(_bytes[_start:end]));\n }\n }\n\n function toU128(bytes calldata _bytes, uint256 _start) internal pure returns (uint128) {\n unchecked {\n uint256 end = _start + 16;\n return uint128(bytes16(_bytes[_start:end]));\n }\n }\n\n function toU256(bytes calldata _bytes, uint256 _start) internal pure returns (uint256) {\n unchecked {\n uint256 end = _start + 32;\n return uint256(bytes32(_bytes[_start:end]));\n }\n }\n\n function toAddr(bytes calldata _bytes, uint256 _start) internal pure returns (address) {\n unchecked {\n uint256 end = _start + 20;\n return address(bytes20(_bytes[_start:end]));\n }\n }\n\n function toB32(bytes calldata _bytes, uint256 _start) internal pure returns (bytes32) {\n unchecked {\n uint256 end = _start + 32;\n return bytes32(_bytes[_start:end]);\n }\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/BitMaps.sol": { + "content": "// SPDX-License-Identifier: MIT\n\n// modified from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/structs/BitMaps.sol\npragma solidity ^0.8.20;\n\ntype BitMap256 is uint256;\n\nusing BitMaps for BitMap256 global;\n\nlibrary BitMaps {\n /**\n * @dev Returns whether the bit at `index` is set.\n */\n function get(BitMap256 bitmap, uint8 index) internal pure returns (bool) {\n uint256 mask = 1 << index;\n return BitMap256.unwrap(bitmap) & mask != 0;\n }\n\n /**\n * @dev Sets the bit at `index`.\n */\n function set(BitMap256 bitmap, uint8 index) internal pure returns (BitMap256) {\n uint256 mask = 1 << index;\n return BitMap256.wrap(BitMap256.unwrap(bitmap) | mask);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport { Packet } from \"../../interfaces/ISendLib.sol\";\nimport { AddressCast } from \"../../libs/AddressCast.sol\";\n\nlibrary PacketV1Codec {\n using AddressCast for address;\n using AddressCast for bytes32;\n\n uint8 internal constant PACKET_VERSION = 1;\n\n // header (version + nonce + path)\n // version\n uint256 private constant PACKET_VERSION_OFFSET = 0;\n // nonce\n uint256 private constant NONCE_OFFSET = 1;\n // path\n uint256 private constant SRC_EID_OFFSET = 9;\n uint256 private constant SENDER_OFFSET = 13;\n uint256 private constant DST_EID_OFFSET = 45;\n uint256 private constant RECEIVER_OFFSET = 49;\n // payload (guid + message)\n uint256 private constant GUID_OFFSET = 81; // keccak256(nonce + path)\n uint256 private constant MESSAGE_OFFSET = 113;\n\n function encode(Packet memory _packet) internal pure returns (bytes memory encodedPacket) {\n encodedPacket = abi.encodePacked(\n PACKET_VERSION,\n _packet.nonce,\n _packet.srcEid,\n _packet.sender.toBytes32(),\n _packet.dstEid,\n _packet.receiver,\n _packet.guid,\n _packet.message\n );\n }\n\n function encodePacketHeader(Packet memory _packet) internal pure returns (bytes memory) {\n return\n abi.encodePacked(\n PACKET_VERSION,\n _packet.nonce,\n _packet.srcEid,\n _packet.sender.toBytes32(),\n _packet.dstEid,\n _packet.receiver\n );\n }\n\n function encodePayload(Packet memory _packet) internal pure returns (bytes memory) {\n return abi.encodePacked(_packet.guid, _packet.message);\n }\n\n function header(bytes calldata _packet) internal pure returns (bytes calldata) {\n return _packet[0:GUID_OFFSET];\n }\n\n function version(bytes calldata _packet) internal pure returns (uint8) {\n return uint8(bytes1(_packet[PACKET_VERSION_OFFSET:NONCE_OFFSET]));\n }\n\n function nonce(bytes calldata _packet) internal pure returns (uint64) {\n return uint64(bytes8(_packet[NONCE_OFFSET:SRC_EID_OFFSET]));\n }\n\n function srcEid(bytes calldata _packet) internal pure returns (uint32) {\n return uint32(bytes4(_packet[SRC_EID_OFFSET:SENDER_OFFSET]));\n }\n\n function sender(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[SENDER_OFFSET:DST_EID_OFFSET]);\n }\n\n function senderAddressB20(bytes calldata _packet) internal pure returns (address) {\n return sender(_packet).toAddress();\n }\n\n function dstEid(bytes calldata _packet) internal pure returns (uint32) {\n return uint32(bytes4(_packet[DST_EID_OFFSET:RECEIVER_OFFSET]));\n }\n\n function receiver(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[RECEIVER_OFFSET:GUID_OFFSET]);\n }\n\n function receiverB20(bytes calldata _packet) internal pure returns (address) {\n return receiver(_packet).toAddress();\n }\n\n function guid(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[GUID_OFFSET:MESSAGE_OFFSET]);\n }\n\n function message(bytes calldata _packet) internal pure returns (bytes calldata) {\n return bytes(_packet[MESSAGE_OFFSET:]);\n }\n\n function payload(bytes calldata _packet) internal pure returns (bytes calldata) {\n return bytes(_packet[GUID_OFFSET:]);\n }\n\n function payloadHash(bytes calldata _packet) internal pure returns (bytes32) {\n return keccak256(payload(_packet));\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { ILayerZeroEndpointV2 } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\n\n/**\n * @title IOAppCore\n */\ninterface IOAppCore {\n // Custom error messages\n error OnlyPeer(uint32 eid, bytes32 sender);\n error NoPeer(uint32 eid);\n error InvalidEndpointCall();\n error InvalidDelegate();\n\n // Event emitted when a peer (OApp) is set for a corresponding endpoint\n event PeerSet(uint32 eid, bytes32 peer);\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n */\n function oAppVersion() external view returns (uint64 senderVersion, uint64 receiverVersion);\n\n /**\n * @notice Retrieves the LayerZero endpoint associated with the OApp.\n * @return iEndpoint The LayerZero endpoint as an interface.\n */\n function endpoint() external view returns (ILayerZeroEndpointV2 iEndpoint);\n\n /**\n * @notice Retrieves the peer (OApp) associated with a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @return peer The peer address (OApp instance) associated with the corresponding endpoint.\n */\n function peers(uint32 _eid) external view returns (bytes32 peer);\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n */\n function setPeer(uint32 _eid, bytes32 _peer) external;\n\n /**\n * @notice Sets the delegate address for the OApp Core.\n * @param _delegate The address of the delegate to be set.\n */\n function setDelegate(address _delegate) external;\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppMsgInspector.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n/**\n * @title IOAppMsgInspector\n * @dev Interface for the OApp Message Inspector, allowing examination of message and options contents.\n */\ninterface IOAppMsgInspector {\n // Custom error message for inspection failure\n error InspectionFailed(bytes message, bytes options);\n\n /**\n * @notice Allows the inspector to examine LayerZero message contents and optionally throw a revert if invalid.\n * @param _message The message payload to be inspected.\n * @param _options Additional options or parameters for inspection.\n * @return valid A boolean indicating whether the inspection passed (true) or failed (false).\n *\n * @dev Optionally done as a revert, OR use the boolean provided to handle the failure.\n */\n function inspect(bytes calldata _message, bytes calldata _options) external view returns (bool valid);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppOptionsType3.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Struct representing enforced option parameters.\n */\nstruct EnforcedOptionParam {\n uint32 eid; // Endpoint ID\n uint16 msgType; // Message Type\n bytes options; // Additional options\n}\n\n/**\n * @title IOAppOptionsType3\n * @dev Interface for the OApp with Type 3 Options, allowing the setting and combining of enforced options.\n */\ninterface IOAppOptionsType3 {\n // Custom error message for invalid options\n error InvalidOptions(bytes options);\n\n // Event emitted when enforced options are set\n event EnforcedOptionSet(EnforcedOptionParam[] _enforcedOptions);\n\n /**\n * @notice Sets enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n */\n function setEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) external;\n\n /**\n * @notice Combines options for a given endpoint and message type.\n * @param _eid The endpoint ID.\n * @param _msgType The OApp message type.\n * @param _extraOptions Additional options passed by the caller.\n * @return options The combination of caller specified options AND enforced options.\n */\n function combineOptions(\n uint32 _eid,\n uint16 _msgType,\n bytes calldata _extraOptions\n ) external view returns (bytes memory options);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\nimport { ILayerZeroReceiver, Origin } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroReceiver.sol\";\n\ninterface IOAppReceiver is ILayerZeroReceiver {\n /**\n * @notice Indicates whether an address is an approved composeMsg sender to the Endpoint.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _message The lzReceive payload.\n * @param _sender The sender address.\n * @return isSender Is a valid sender.\n *\n * @dev Applications can optionally choose to implement a separate composeMsg sender that is NOT the bridging layer.\n * @dev The default sender IS the OAppReceiver implementer.\n */\n function isComposeMsgSender(\n Origin calldata _origin,\n bytes calldata _message,\n address _sender\n ) external view returns (bool isSender);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IOAppOptionsType3, EnforcedOptionParam } from \"../interfaces/IOAppOptionsType3.sol\";\n\n/**\n * @title OAppOptionsType3\n * @dev Abstract contract implementing the IOAppOptionsType3 interface with type 3 options.\n */\nabstract contract OAppOptionsType3 is IOAppOptionsType3, Ownable {\n uint16 internal constant OPTION_TYPE_3 = 3;\n\n // @dev The \"msgType\" should be defined in the child contract.\n mapping(uint32 eid => mapping(uint16 msgType => bytes enforcedOption)) public enforcedOptions;\n\n /**\n * @dev Sets the enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Provides a way for the OApp to enforce things like paying for PreCrime, AND/OR minimum dst lzReceive gas amounts etc.\n * @dev These enforced options can vary as the potential options/execution on the remote may differ as per the msgType.\n * eg. Amount of lzReceive() gas necessary to deliver a lzCompose() message adds overhead you dont want to pay\n * if you are only making a standard LayerZero message ie. lzReceive() WITHOUT sendCompose().\n */\n function setEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) public virtual onlyOwner {\n _setEnforcedOptions(_enforcedOptions);\n }\n\n /**\n * @dev Sets the enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n *\n * @dev Provides a way for the OApp to enforce things like paying for PreCrime, AND/OR minimum dst lzReceive gas amounts etc.\n * @dev These enforced options can vary as the potential options/execution on the remote may differ as per the msgType.\n * eg. Amount of lzReceive() gas necessary to deliver a lzCompose() message adds overhead you dont want to pay\n * if you are only making a standard LayerZero message ie. lzReceive() WITHOUT sendCompose().\n */\n function _setEnforcedOptions(EnforcedOptionParam[] memory _enforcedOptions) internal virtual {\n for (uint256 i = 0; i < _enforcedOptions.length; i++) {\n // @dev Enforced options are only available for optionType 3, as type 1 and 2 dont support combining.\n _assertOptionsType3(_enforcedOptions[i].options);\n enforcedOptions[_enforcedOptions[i].eid][_enforcedOptions[i].msgType] = _enforcedOptions[i].options;\n }\n\n emit EnforcedOptionSet(_enforcedOptions);\n }\n\n /**\n * @notice Combines options for a given endpoint and message type.\n * @param _eid The endpoint ID.\n * @param _msgType The OAPP message type.\n * @param _extraOptions Additional options passed by the caller.\n * @return options The combination of caller specified options AND enforced options.\n *\n * @dev If there is an enforced lzReceive option:\n * - {gasLimit: 200k, msg.value: 1 ether} AND a caller supplies a lzReceive option: {gasLimit: 100k, msg.value: 0.5 ether}\n * - The resulting options will be {gasLimit: 300k, msg.value: 1.5 ether} when the message is executed on the remote lzReceive() function.\n * @dev This presence of duplicated options is handled off-chain in the verifier/executor.\n */\n function combineOptions(\n uint32 _eid,\n uint16 _msgType,\n bytes calldata _extraOptions\n ) public view virtual returns (bytes memory) {\n bytes memory enforced = enforcedOptions[_eid][_msgType];\n\n // No enforced options, pass whatever the caller supplied, even if it's empty or legacy type 1/2 options.\n if (enforced.length == 0) return _extraOptions;\n\n // No caller options, return enforced\n if (_extraOptions.length == 0) return enforced;\n\n // @dev If caller provided _extraOptions, must be type 3 as its the ONLY type that can be combined.\n if (_extraOptions.length >= 2) {\n _assertOptionsType3(_extraOptions);\n // @dev Remove the first 2 bytes containing the type from the _extraOptions and combine with enforced.\n return bytes.concat(enforced, _extraOptions[2:]);\n }\n\n // No valid set of options was found.\n revert InvalidOptions(_extraOptions);\n }\n\n /**\n * @dev Internal function to assert that options are of type 3.\n * @param _options The options to be checked.\n */\n function _assertOptionsType3(bytes memory _options) internal pure virtual {\n uint16 optionsType;\n assembly {\n optionsType := mload(add(_options, 2))\n }\n if (optionsType != OPTION_TYPE_3) revert InvalidOptions(_options);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { BytesLib } from \"solidity-bytes-utils/contracts/BytesLib.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { ExecutorOptions } from \"@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol\";\nimport { DVNOptions } from \"@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/DVNOptions.sol\";\n\n/**\n * @title OptionsBuilder\n * @dev Library for building and encoding various message options.\n */\nlibrary OptionsBuilder {\n using SafeCast for uint256;\n using BytesLib for bytes;\n\n // Constants for options types\n uint16 internal constant TYPE_1 = 1; // legacy options type 1\n uint16 internal constant TYPE_2 = 2; // legacy options type 2\n uint16 internal constant TYPE_3 = 3;\n\n // Custom error message\n error InvalidSize(uint256 max, uint256 actual);\n error InvalidOptionType(uint16 optionType);\n\n // Modifier to ensure only options of type 3 are used\n modifier onlyType3(bytes memory _options) {\n if (_options.toUint16(0) != TYPE_3) revert InvalidOptionType(_options.toUint16(0));\n _;\n }\n\n /**\n * @dev Creates a new options container with type 3.\n * @return options The newly created options container.\n */\n function newOptions() internal pure returns (bytes memory) {\n return abi.encodePacked(TYPE_3);\n }\n\n /**\n * @dev Adds an executor LZ receive option to the existing options.\n * @param _options The existing options container.\n * @param _gas The gasLimit used on the lzReceive() function in the OApp.\n * @param _value The msg.value passed to the lzReceive() function in the OApp.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed by the executor\n * eg. if (_gas: 200k, and _value: 1 ether) AND (_gas: 100k, _value: 0.5 ether) are sent in an option to the LayerZeroEndpoint,\n * that becomes (300k, 1.5 ether) when the message is executed on the remote lzReceive() function.\n */\n function addExecutorLzReceiveOption(\n bytes memory _options,\n uint128 _gas,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzReceiveOption(_gas, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZRECEIVE, option);\n }\n\n /**\n * @dev Adds an executor native drop option to the existing options.\n * @param _options The existing options container.\n * @param _amount The amount for the native value that is airdropped to the 'receiver'.\n * @param _receiver The receiver address for the native drop option.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed by the executor on the remote chain.\n */\n function addExecutorNativeDropOption(\n bytes memory _options,\n uint128 _amount,\n bytes32 _receiver\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeNativeDropOption(_amount, _receiver);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_NATIVE_DROP, option);\n }\n\n // /**\n // * @dev Adds an executor native drop option to the existing options.\n // * @param _options The existing options container.\n // * @param _amount The amount for the native value that is airdropped to the 'receiver'.\n // * @param _receiver The receiver address for the native drop option.\n // * @return options The updated options container.\n // *\n // * @dev When multiples of this option are added, they are summed by the executor on the remote chain.\n // */\n function addExecutorLzReadOption(\n bytes memory _options,\n uint128 _gas,\n uint32 _size,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzReadOption(_gas, _size, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZREAD, option);\n }\n\n /**\n * @dev Adds an executor LZ compose option to the existing options.\n * @param _options The existing options container.\n * @param _index The index for the lzCompose() function call.\n * @param _gas The gasLimit for the lzCompose() function call.\n * @param _value The msg.value for the lzCompose() function call.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed PER index by the executor on the remote chain.\n * @dev If the OApp sends N lzCompose calls on the remote, you must provide N incremented indexes starting with 0.\n * ie. When your remote OApp composes (N = 3) messages, you must set this option for index 0,1,2\n */\n function addExecutorLzComposeOption(\n bytes memory _options,\n uint16 _index,\n uint128 _gas,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzComposeOption(_index, _gas, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZCOMPOSE, option);\n }\n\n /**\n * @dev Adds an executor ordered execution option to the existing options.\n * @param _options The existing options container.\n * @return options The updated options container.\n */\n function addExecutorOrderedExecutionOption(\n bytes memory _options\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_ORDERED_EXECUTION, bytes(\"\"));\n }\n\n /**\n * @dev Adds a DVN pre-crime option to the existing options.\n * @param _options The existing options container.\n * @param _dvnIdx The DVN index for the pre-crime option.\n * @return options The updated options container.\n */\n function addDVNPreCrimeOption(\n bytes memory _options,\n uint8 _dvnIdx\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return addDVNOption(_options, _dvnIdx, DVNOptions.OPTION_TYPE_PRECRIME, bytes(\"\"));\n }\n\n /**\n * @dev Adds an executor option to the existing options.\n * @param _options The existing options container.\n * @param _optionType The type of the executor option.\n * @param _option The encoded data for the executor option.\n * @return options The updated options container.\n */\n function addExecutorOption(\n bytes memory _options,\n uint8 _optionType,\n bytes memory _option\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return\n abi.encodePacked(\n _options,\n ExecutorOptions.WORKER_ID,\n _option.length.toUint16() + 1, // +1 for optionType\n _optionType,\n _option\n );\n }\n\n /**\n * @dev Adds a DVN option to the existing options.\n * @param _options The existing options container.\n * @param _dvnIdx The DVN index for the DVN option.\n * @param _optionType The type of the DVN option.\n * @param _option The encoded data for the DVN option.\n * @return options The updated options container.\n */\n function addDVNOption(\n bytes memory _options,\n uint8 _dvnIdx,\n uint8 _optionType,\n bytes memory _option\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return\n abi.encodePacked(\n _options,\n DVNOptions.WORKER_ID,\n _option.length.toUint16() + 2, // +2 for optionType and dvnIdx\n _dvnIdx,\n _optionType,\n _option\n );\n }\n\n /**\n * @dev Encodes legacy options of type 1.\n * @param _executionGas The gasLimit value passed to lzReceive().\n * @return legacyOptions The encoded legacy options.\n */\n function encodeLegacyOptionsType1(uint256 _executionGas) internal pure returns (bytes memory) {\n if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);\n return abi.encodePacked(TYPE_1, _executionGas);\n }\n\n /**\n * @dev Encodes legacy options of type 2.\n * @param _executionGas The gasLimit value passed to lzReceive().\n * @param _nativeForDst The amount of native air dropped to the receiver.\n * @param _receiver The _nativeForDst receiver address.\n * @return legacyOptions The encoded legacy options of type 2.\n */\n function encodeLegacyOptionsType2(\n uint256 _executionGas,\n uint256 _nativeForDst,\n bytes memory _receiver // @dev Use bytes instead of bytes32 in legacy type 2 for _receiver.\n ) internal pure returns (bytes memory) {\n if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);\n if (_nativeForDst > type(uint128).max) revert InvalidSize(type(uint128).max, _nativeForDst);\n if (_receiver.length > 32) revert InvalidSize(32, _receiver.length);\n return abi.encodePacked(TYPE_2, _executionGas, _nativeForDst, _receiver);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n// @dev Import the 'MessagingFee' and 'MessagingReceipt' so it's exposed to OApp implementers\n// solhint-disable-next-line no-unused-import\nimport { OAppSender, MessagingFee, MessagingReceipt } from \"./OAppSender.sol\";\n// @dev Import the 'Origin' so it's exposed to OApp implementers\n// solhint-disable-next-line no-unused-import\nimport { OAppReceiver, Origin } from \"./OAppReceiver.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OApp\n * @dev Abstract contract serving as the base for OApp implementation, combining OAppSender and OAppReceiver functionality.\n */\nabstract contract OApp is OAppSender, OAppReceiver {\n /**\n * @dev Constructor to initialize the OApp with the provided endpoint and owner.\n * @param _endpoint The address of the LOCAL LayerZero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(address _endpoint, address _delegate) OAppCore(_endpoint, _delegate) {}\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol implementation.\n * @return receiverVersion The version of the OAppReceiver.sol implementation.\n */\n function oAppVersion()\n public\n pure\n virtual\n override(OAppSender, OAppReceiver)\n returns (uint64 senderVersion, uint64 receiverVersion)\n {\n return (SENDER_VERSION, RECEIVER_VERSION);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IOAppCore, ILayerZeroEndpointV2 } from \"./interfaces/IOAppCore.sol\";\n\n/**\n * @title OAppCore\n * @dev Abstract contract implementing the IOAppCore interface with basic OApp configurations.\n */\nabstract contract OAppCore is IOAppCore, Ownable {\n // The LayerZero endpoint associated with the given OApp\n ILayerZeroEndpointV2 public immutable endpoint;\n\n // Mapping to store peers associated with corresponding endpoints\n mapping(uint32 eid => bytes32 peer) public peers;\n\n /**\n * @dev Constructor to initialize the OAppCore with the provided endpoint and delegate.\n * @param _endpoint The address of the LOCAL Layer Zero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n *\n * @dev The delegate typically should be set as the owner of the contract.\n */\n constructor(address _endpoint, address _delegate) {\n endpoint = ILayerZeroEndpointV2(_endpoint);\n\n if (_delegate == address(0)) revert InvalidDelegate();\n endpoint.setDelegate(_delegate);\n }\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.\n * @dev Set this to bytes32(0) to remove the peer address.\n * @dev Peer is a bytes32 to accommodate non-evm chains.\n */\n function setPeer(uint32 _eid, bytes32 _peer) public virtual onlyOwner {\n _setPeer(_eid, _peer);\n }\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n *\n * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.\n * @dev Set this to bytes32(0) to remove the peer address.\n * @dev Peer is a bytes32 to accommodate non-evm chains.\n */\n function _setPeer(uint32 _eid, bytes32 _peer) internal virtual {\n peers[_eid] = _peer;\n emit PeerSet(_eid, _peer);\n }\n\n /**\n * @notice Internal function to get the peer address associated with a specific endpoint; reverts if NOT set.\n * ie. the peer is set to bytes32(0).\n * @param _eid The endpoint ID.\n * @return peer The address of the peer associated with the specified endpoint.\n */\n function _getPeerOrRevert(uint32 _eid) internal view virtual returns (bytes32) {\n bytes32 peer = peers[_eid];\n if (peer == bytes32(0)) revert NoPeer(_eid);\n return peer;\n }\n\n /**\n * @notice Sets the delegate address for the OApp.\n * @param _delegate The address of the delegate to be set.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Provides the ability for a delegate to set configs, on behalf of the OApp, directly on the Endpoint contract.\n */\n function setDelegate(address _delegate) public onlyOwner {\n endpoint.setDelegate(_delegate);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IOAppReceiver, Origin } from \"./interfaces/IOAppReceiver.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OAppReceiver\n * @dev Abstract contract implementing the ILayerZeroReceiver interface and extending OAppCore for OApp receivers.\n */\nabstract contract OAppReceiver is IOAppReceiver, OAppCore {\n // Custom error message for when the caller is not the registered endpoint/\n error OnlyEndpoint(address addr);\n\n // @dev The version of the OAppReceiver implementation.\n // @dev Version is bumped when changes are made to this contract.\n uint64 internal constant RECEIVER_VERSION = 2;\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n *\n * @dev Providing 0 as the default for OAppSender version. Indicates that the OAppSender is not implemented.\n * ie. this is a RECEIVE only OApp.\n * @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions.\n */\n function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {\n return (0, RECEIVER_VERSION);\n }\n\n /**\n * @notice Indicates whether an address is an approved composeMsg sender to the Endpoint.\n * @dev _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @dev _message The lzReceive payload.\n * @param _sender The sender address.\n * @return isSender Is a valid sender.\n *\n * @dev Applications can optionally choose to implement separate composeMsg senders that are NOT the bridging layer.\n * @dev The default sender IS the OAppReceiver implementer.\n */\n function isComposeMsgSender(\n Origin calldata /*_origin*/,\n bytes calldata /*_message*/,\n address _sender\n ) public view virtual returns (bool) {\n return _sender == address(this);\n }\n\n /**\n * @notice Checks if the path initialization is allowed based on the provided origin.\n * @param origin The origin information containing the source endpoint and sender address.\n * @return Whether the path has been initialized.\n *\n * @dev This indicates to the endpoint that the OApp has enabled msgs for this particular path to be received.\n * @dev This defaults to assuming if a peer has been set, its initialized.\n * Can be overridden by the OApp if there is other logic to determine this.\n */\n function allowInitializePath(Origin calldata origin) public view virtual returns (bool) {\n return peers[origin.srcEid] == origin.sender;\n }\n\n /**\n * @notice Retrieves the next nonce for a given source endpoint and sender address.\n * @dev _srcEid The source endpoint ID.\n * @dev _sender The sender address.\n * @return nonce The next nonce.\n *\n * @dev The path nonce starts from 1. If 0 is returned it means that there is NO nonce ordered enforcement.\n * @dev Is required by the off-chain executor to determine the OApp expects msg execution is ordered.\n * @dev This is also enforced by the OApp.\n * @dev By default this is NOT enabled. ie. nextNonce is hardcoded to return 0.\n */\n function nextNonce(uint32 /*_srcEid*/, bytes32 /*_sender*/) public view virtual returns (uint64 nonce) {\n return 0;\n }\n\n /**\n * @dev Entry point for receiving messages or packets from the endpoint.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The payload of the received message.\n * @param _executor The address of the executor for the received message.\n * @param _extraData Additional arbitrary data provided by the corresponding executor.\n *\n * @dev Entry point for receiving msg/packet from the LayerZero endpoint.\n */\n function lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) public payable virtual {\n // Ensures that only the endpoint can attempt to lzReceive() messages to this OApp.\n if (address(endpoint) != msg.sender) revert OnlyEndpoint(msg.sender);\n\n // Ensure that the sender matches the expected peer for the source endpoint.\n if (_getPeerOrRevert(_origin.srcEid) != _origin.sender) revert OnlyPeer(_origin.srcEid, _origin.sender);\n\n // Call the internal OApp implementation of lzReceive.\n _lzReceive(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Internal function to implement lzReceive logic without needing to copy the basic parameter validation.\n */\n function _lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual;\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { SafeERC20, IERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { MessagingParams, MessagingFee, MessagingReceipt } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OAppSender\n * @dev Abstract contract implementing the OAppSender functionality for sending messages to a LayerZero endpoint.\n */\nabstract contract OAppSender is OAppCore {\n using SafeERC20 for IERC20;\n\n // Custom error messages\n error NotEnoughNative(uint256 msgValue);\n error LzTokenUnavailable();\n\n // @dev The version of the OAppSender implementation.\n // @dev Version is bumped when changes are made to this contract.\n uint64 internal constant SENDER_VERSION = 1;\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n *\n * @dev Providing 0 as the default for OAppReceiver version. Indicates that the OAppReceiver is not implemented.\n * ie. this is a SEND only OApp.\n * @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions\n */\n function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {\n return (SENDER_VERSION, 0);\n }\n\n /**\n * @dev Internal function to interact with the LayerZero EndpointV2.quote() for fee calculation.\n * @param _dstEid The destination endpoint ID.\n * @param _message The message payload.\n * @param _options Additional options for the message.\n * @param _payInLzToken Flag indicating whether to pay the fee in LZ tokens.\n * @return fee The calculated MessagingFee for the message.\n * - nativeFee: The native fee for the message.\n * - lzTokenFee: The LZ token fee for the message.\n */\n function _quote(\n uint32 _dstEid,\n bytes memory _message,\n bytes memory _options,\n bool _payInLzToken\n ) internal view virtual returns (MessagingFee memory fee) {\n return\n endpoint.quote(\n MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _payInLzToken),\n address(this)\n );\n }\n\n /**\n * @dev Internal function to interact with the LayerZero EndpointV2.send() for sending a message.\n * @param _dstEid The destination endpoint ID.\n * @param _message The message payload.\n * @param _options Additional options for the message.\n * @param _fee The calculated LayerZero fee for the message.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess fee values sent to the endpoint.\n * @return receipt The receipt for the sent message.\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function _lzSend(\n uint32 _dstEid,\n bytes memory _message,\n bytes memory _options,\n MessagingFee memory _fee,\n address _refundAddress\n ) internal virtual returns (MessagingReceipt memory receipt) {\n // @dev Push corresponding fees to the endpoint, any excess is sent back to the _refundAddress from the endpoint.\n uint256 messageValue = _payNative(_fee.nativeFee);\n if (_fee.lzTokenFee > 0) _payLzToken(_fee.lzTokenFee);\n\n return\n // solhint-disable-next-line check-send-result\n endpoint.send{ value: messageValue }(\n MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _fee.lzTokenFee > 0),\n _refundAddress\n );\n }\n\n /**\n * @dev Internal function to pay the native fee associated with the message.\n * @param _nativeFee The native fee to be paid.\n * @return nativeFee The amount of native currency paid.\n *\n * @dev If the OApp needs to initiate MULTIPLE LayerZero messages in a single transaction,\n * this will need to be overridden because msg.value would contain multiple lzFees.\n * @dev Should be overridden in the event the LayerZero endpoint requires a different native currency.\n * @dev Some EVMs use an ERC20 as a method for paying transactions/gasFees.\n * @dev The endpoint is EITHER/OR, ie. it will NOT support both types of native payment at a time.\n */\n function _payNative(uint256 _nativeFee) internal virtual returns (uint256 nativeFee) {\n if (msg.value != _nativeFee) revert NotEnoughNative(msg.value);\n return _nativeFee;\n }\n\n /**\n * @dev Internal function to pay the LZ token fee associated with the message.\n * @param _lzTokenFee The LZ token fee to be paid.\n *\n * @dev If the caller is trying to pay in the specified lzToken, then the lzTokenFee is passed to the endpoint.\n * @dev Any excess sent, is passed back to the specified _refundAddress in the _lzSend().\n */\n function _payLzToken(uint256 _lzTokenFee) internal virtual {\n // @dev Cannot cache the token because it is not immutable in the endpoint.\n address lzToken = endpoint.lzToken();\n if (lzToken == address(0)) revert LzTokenUnavailable();\n\n // Pay LZ token fee by sending tokens to the endpoint.\n IERC20(lzToken).safeTransferFrom(msg.sender, address(endpoint), _lzTokenFee);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/interfaces/IOAppPreCrimeSimulator.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n// @dev Import the Origin so it's exposed to OAppPreCrimeSimulator implementers.\n// solhint-disable-next-line no-unused-import\nimport { InboundPacket, Origin } from \"../libs/Packet.sol\";\n\n/**\n * @title IOAppPreCrimeSimulator Interface\n * @dev Interface for the preCrime simulation functionality in an OApp.\n */\ninterface IOAppPreCrimeSimulator {\n // @dev simulation result used in PreCrime implementation\n error SimulationResult(bytes result);\n error OnlySelf();\n\n /**\n * @dev Emitted when the preCrime contract address is set.\n * @param preCrimeAddress The address of the preCrime contract.\n */\n event PreCrimeSet(address preCrimeAddress);\n\n /**\n * @dev Retrieves the address of the preCrime contract implementation.\n * @return The address of the preCrime contract.\n */\n function preCrime() external view returns (address);\n\n /**\n * @dev Retrieves the address of the OApp contract.\n * @return The address of the OApp contract.\n */\n function oApp() external view returns (address);\n\n /**\n * @dev Sets the preCrime contract address.\n * @param _preCrime The address of the preCrime contract.\n */\n function setPreCrime(address _preCrime) external;\n\n /**\n * @dev Mocks receiving a packet, then reverts with a series of data to infer the state/result.\n * @param _packets An array of LayerZero InboundPacket objects representing received packets.\n */\n function lzReceiveAndRevert(InboundPacket[] calldata _packets) external payable;\n\n /**\n * @dev checks if the specified peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint Id to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n */\n function isPeer(uint32 _eid, bytes32 _peer) external view returns (bool);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/interfaces/IPreCrime.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\nstruct PreCrimePeer {\n uint32 eid;\n bytes32 preCrime;\n bytes32 oApp;\n}\n\n// TODO not done yet\ninterface IPreCrime {\n error OnlyOffChain();\n\n // for simulate()\n error PacketOversize(uint256 max, uint256 actual);\n error PacketUnsorted();\n error SimulationFailed(bytes reason);\n\n // for preCrime()\n error SimulationResultNotFound(uint32 eid);\n error InvalidSimulationResult(uint32 eid, bytes reason);\n error CrimeFound(bytes crime);\n\n function getConfig(bytes[] calldata _packets, uint256[] calldata _packetMsgValues) external returns (bytes memory);\n\n function simulate(\n bytes[] calldata _packets,\n uint256[] calldata _packetMsgValues\n ) external payable returns (bytes memory);\n\n function buildSimulationResult() external view returns (bytes memory);\n\n function preCrime(\n bytes[] calldata _packets,\n uint256[] calldata _packetMsgValues,\n bytes[] calldata _simulations\n ) external;\n\n function version() external view returns (uint64 major, uint8 minor);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/libs/Packet.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Origin } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\nimport { PacketV1Codec } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol\";\n\n/**\n * @title InboundPacket\n * @dev Structure representing an inbound packet received by the contract.\n */\nstruct InboundPacket {\n Origin origin; // Origin information of the packet.\n uint32 dstEid; // Destination endpointId of the packet.\n address receiver; // Receiver address for the packet.\n bytes32 guid; // Unique identifier of the packet.\n uint256 value; // msg.value of the packet.\n address executor; // Executor address for the packet.\n bytes message; // Message payload of the packet.\n bytes extraData; // Additional arbitrary data for the packet.\n}\n\n/**\n * @title PacketDecoder\n * @dev Library for decoding LayerZero packets.\n */\nlibrary PacketDecoder {\n using PacketV1Codec for bytes;\n\n /**\n * @dev Decode an inbound packet from the given packet data.\n * @param _packet The packet data to decode.\n * @return packet An InboundPacket struct representing the decoded packet.\n */\n function decode(bytes calldata _packet) internal pure returns (InboundPacket memory packet) {\n packet.origin = Origin(_packet.srcEid(), _packet.sender(), _packet.nonce());\n packet.dstEid = _packet.dstEid();\n packet.receiver = _packet.receiverB20();\n packet.guid = _packet.guid();\n packet.message = _packet.message();\n }\n\n /**\n * @dev Decode multiple inbound packets from the given packet data and associated message values.\n * @param _packets An array of packet data to decode.\n * @param _packetMsgValues An array of associated message values for each packet.\n * @return packets An array of InboundPacket structs representing the decoded packets.\n */\n function decode(\n bytes[] calldata _packets,\n uint256[] memory _packetMsgValues\n ) internal pure returns (InboundPacket[] memory packets) {\n packets = new InboundPacket[](_packets.length);\n for (uint256 i = 0; i < _packets.length; i++) {\n bytes calldata packet = _packets[i];\n packets[i] = PacketDecoder.decode(packet);\n // @dev Allows the verifier to specify the msg.value that gets passed in lzReceive.\n packets[i].value = _packetMsgValues[i];\n }\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/OAppPreCrimeSimulator.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IPreCrime } from \"./interfaces/IPreCrime.sol\";\nimport { IOAppPreCrimeSimulator, InboundPacket, Origin } from \"./interfaces/IOAppPreCrimeSimulator.sol\";\n\n/**\n * @title OAppPreCrimeSimulator\n * @dev Abstract contract serving as the base for preCrime simulation functionality in an OApp.\n */\nabstract contract OAppPreCrimeSimulator is IOAppPreCrimeSimulator, Ownable {\n // The address of the preCrime implementation.\n address public preCrime;\n\n /**\n * @dev Retrieves the address of the OApp contract.\n * @return The address of the OApp contract.\n *\n * @dev The simulator contract is the base contract for the OApp by default.\n * @dev If the simulator is a separate contract, override this function.\n */\n function oApp() external view virtual returns (address) {\n return address(this);\n }\n\n /**\n * @dev Sets the preCrime contract address.\n * @param _preCrime The address of the preCrime contract.\n */\n function setPreCrime(address _preCrime) public virtual onlyOwner {\n preCrime = _preCrime;\n emit PreCrimeSet(_preCrime);\n }\n\n /**\n * @dev Interface for pre-crime simulations. Always reverts at the end with the simulation results.\n * @param _packets An array of InboundPacket objects representing received packets to be delivered.\n *\n * @dev WARNING: MUST revert at the end with the simulation results.\n * @dev Gives the preCrime implementation the ability to mock sending packets to the lzReceive function,\n * WITHOUT actually executing them.\n */\n function lzReceiveAndRevert(InboundPacket[] calldata _packets) public payable virtual {\n for (uint256 i = 0; i < _packets.length; i++) {\n InboundPacket calldata packet = _packets[i];\n\n // Ignore packets that are not from trusted peers.\n if (!isPeer(packet.origin.srcEid, packet.origin.sender)) continue;\n\n // @dev Because a verifier is calling this function, it doesnt have access to executor params:\n // - address _executor\n // - bytes calldata _extraData\n // preCrime will NOT work for OApps that rely on these two parameters inside of their _lzReceive().\n // They are instead stubbed to default values, address(0) and bytes(\"\")\n // @dev Calling this.lzReceiveSimulate removes ability for assembly return 0 callstack exit,\n // which would cause the revert to be ignored.\n this.lzReceiveSimulate{ value: packet.value }(\n packet.origin,\n packet.guid,\n packet.message,\n packet.executor,\n packet.extraData\n );\n }\n\n // @dev Revert with the simulation results. msg.sender must implement IPreCrime.buildSimulationResult().\n revert SimulationResult(IPreCrime(msg.sender).buildSimulationResult());\n }\n\n /**\n * @dev Is effectively an internal function because msg.sender must be address(this).\n * Allows resetting the call stack for 'internal' calls.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _guid The unique identifier of the packet.\n * @param _message The message payload of the packet.\n * @param _executor The executor address for the packet.\n * @param _extraData Additional data for the packet.\n */\n function lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) external payable virtual {\n // @dev Ensure ONLY can be called 'internally'.\n if (msg.sender != address(this)) revert OnlySelf();\n _lzReceiveSimulate(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Internal function to handle the OAppPreCrimeSimulator simulated receive.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The GUID of the LayerZero message.\n * @param _message The LayerZero message.\n * @param _executor The address of the off-chain executor.\n * @param _extraData Arbitrary data passed by the msg executor.\n *\n * @dev Enables the preCrime simulator to mock sending lzReceive() messages,\n * routes the msg down from the OAppPreCrimeSimulator, and back up to the OAppReceiver.\n */\n function _lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual;\n\n /**\n * @dev checks if the specified peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint Id to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n */\n function isPeer(uint32 _eid, bytes32 _peer) public view virtual returns (bool);\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/interfaces/IMintableBurnable.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\n/// @title Interface for mintable and burnable tokens\ninterface IMintableBurnable {\n \n /**\n * @notice Burns tokens from a specified account\n * @param _from Address from which tokens will be burned\n * @param _amount Amount of tokens to be burned\n * @return success Indicates whether the operation was successful\n */\n function burn(address _from, uint256 _amount) external returns (bool success);\n\n /**\n * @notice Mints tokens to a specified account\n * @param _to Address to which tokens will be minted\n * @param _amount Amount of tokens to be minted\n * @return success Indicates whether the operation was successful\n */\n function mint(address _to, uint256 _amount) external returns (bool success);\n}" + }, + "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { MessagingReceipt, MessagingFee } from \"@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol\";\n\n/**\n * @dev Struct representing token parameters for the OFT send() operation.\n */\nstruct SendParam {\n uint32 dstEid; // Destination endpoint ID.\n bytes32 to; // Recipient address.\n uint256 amountLD; // Amount to send in local decimals.\n uint256 minAmountLD; // Minimum amount to send in local decimals.\n bytes extraOptions; // Additional options supplied by the caller to be used in the LayerZero message.\n bytes composeMsg; // The composed message for the send() operation.\n bytes oftCmd; // The OFT command to be executed, unused in default OFT implementations.\n}\n\n/**\n * @dev Struct representing OFT limit information.\n * @dev These amounts can change dynamically and are up the specific oft implementation.\n */\nstruct OFTLimit {\n uint256 minAmountLD; // Minimum amount in local decimals that can be sent to the recipient.\n uint256 maxAmountLD; // Maximum amount in local decimals that can be sent to the recipient.\n}\n\n/**\n * @dev Struct representing OFT receipt information.\n */\nstruct OFTReceipt {\n uint256 amountSentLD; // Amount of tokens ACTUALLY debited from the sender in local decimals.\n // @dev In non-default implementations, the amountReceivedLD COULD differ from this value.\n uint256 amountReceivedLD; // Amount of tokens to be received on the remote side.\n}\n\n/**\n * @dev Struct representing OFT fee details.\n * @dev Future proof mechanism to provide a standardized way to communicate fees to things like a UI.\n */\nstruct OFTFeeDetail {\n int256 feeAmountLD; // Amount of the fee in local decimals.\n string description; // Description of the fee.\n}\n\n/**\n * @title IOFT\n * @dev Interface for the OftChain (OFT) token.\n * @dev Does not inherit ERC20 to accommodate usage by OFTAdapter as well.\n * @dev This specific interface ID is '0x02e49c2c'.\n */\ninterface IOFT {\n // Custom error messages\n error InvalidLocalDecimals();\n error SlippageExceeded(uint256 amountLD, uint256 minAmountLD);\n\n // Events\n event OFTSent(\n bytes32 indexed guid, // GUID of the OFT message.\n uint32 dstEid, // Destination Endpoint ID.\n address indexed fromAddress, // Address of the sender on the src chain.\n uint256 amountSentLD, // Amount of tokens sent in local decimals.\n uint256 amountReceivedLD // Amount of tokens received in local decimals.\n );\n event OFTReceived(\n bytes32 indexed guid, // GUID of the OFT message.\n uint32 srcEid, // Source Endpoint ID.\n address indexed toAddress, // Address of the recipient on the dst chain.\n uint256 amountReceivedLD // Amount of tokens received in local decimals.\n );\n\n /**\n * @notice Retrieves interfaceID and the version of the OFT.\n * @return interfaceId The interface ID.\n * @return version The version.\n *\n * @dev interfaceId: This specific interface ID is '0x02e49c2c'.\n * @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.\n * @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.\n * ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)\n */\n function oftVersion() external view returns (bytes4 interfaceId, uint64 version);\n\n /**\n * @notice Retrieves the address of the token associated with the OFT.\n * @return token The address of the ERC20 token implementation.\n */\n function token() external view returns (address);\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the 'token()' to send.\n * @return requiresApproval Needs approval of the underlying token implementation.\n *\n * @dev Allows things like wallet implementers to determine integration requirements,\n * without understanding the underlying token implementation.\n */\n function approvalRequired() external view returns (bool);\n\n /**\n * @notice Retrieves the shared decimals of the OFT.\n * @return sharedDecimals The shared decimals of the OFT.\n */\n function sharedDecimals() external view returns (uint8);\n\n /**\n * @notice Provides the fee breakdown and settings data for an OFT. Unused in the default implementation.\n * @param _sendParam The parameters for the send operation.\n * @return limit The OFT limit information.\n * @return oftFeeDetails The details of OFT fees.\n * @return receipt The OFT receipt information.\n */\n function quoteOFT(\n SendParam calldata _sendParam\n ) external view returns (OFTLimit memory, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory);\n\n /**\n * @notice Provides a quote for the send() operation.\n * @param _sendParam The parameters for the send() operation.\n * @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.\n * @return fee The calculated LayerZero messaging fee from the send() operation.\n *\n * @dev MessagingFee: LayerZero msg fee\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n */\n function quoteSend(SendParam calldata _sendParam, bool _payInLzToken) external view returns (MessagingFee memory);\n\n /**\n * @notice Executes the send() operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The fee information supplied by the caller.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds from fees etc. on the src.\n * @return receipt The LayerZero messaging receipt from the send() operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) external payable returns (MessagingReceipt memory, OFTReceipt memory);\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/libs/OFTComposeMsgCodec.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nlibrary OFTComposeMsgCodec {\n // Offset constants for decoding composed messages\n uint8 private constant NONCE_OFFSET = 8;\n uint8 private constant SRC_EID_OFFSET = 12;\n uint8 private constant AMOUNT_LD_OFFSET = 44;\n uint8 private constant COMPOSE_FROM_OFFSET = 76;\n\n /**\n * @dev Encodes a OFT composed message.\n * @param _nonce The nonce value.\n * @param _srcEid The source endpoint ID.\n * @param _amountLD The amount in local decimals.\n * @param _composeMsg The composed message.\n * @return _msg The encoded Composed message.\n */\n function encode(\n uint64 _nonce,\n uint32 _srcEid,\n uint256 _amountLD,\n bytes memory _composeMsg // 0x[composeFrom][composeMsg]\n ) internal pure returns (bytes memory _msg) {\n _msg = abi.encodePacked(_nonce, _srcEid, _amountLD, _composeMsg);\n }\n\n /**\n * @dev Retrieves the nonce for the composed message.\n * @param _msg The message.\n * @return The nonce value.\n */\n function nonce(bytes calldata _msg) internal pure returns (uint64) {\n return uint64(bytes8(_msg[:NONCE_OFFSET]));\n }\n\n /**\n * @dev Retrieves the source endpoint ID for the composed message.\n * @param _msg The message.\n * @return The source endpoint ID.\n */\n function srcEid(bytes calldata _msg) internal pure returns (uint32) {\n return uint32(bytes4(_msg[NONCE_OFFSET:SRC_EID_OFFSET]));\n }\n\n /**\n * @dev Retrieves the amount in local decimals from the composed message.\n * @param _msg The message.\n * @return The amount in local decimals.\n */\n function amountLD(bytes calldata _msg) internal pure returns (uint256) {\n return uint256(bytes32(_msg[SRC_EID_OFFSET:AMOUNT_LD_OFFSET]));\n }\n\n /**\n * @dev Retrieves the composeFrom value from the composed message.\n * @param _msg The message.\n * @return The composeFrom value.\n */\n function composeFrom(bytes calldata _msg) internal pure returns (bytes32) {\n return bytes32(_msg[AMOUNT_LD_OFFSET:COMPOSE_FROM_OFFSET]);\n }\n\n /**\n * @dev Retrieves the composed message.\n * @param _msg The message.\n * @return The composed message.\n */\n function composeMsg(bytes calldata _msg) internal pure returns (bytes memory) {\n return _msg[COMPOSE_FROM_OFFSET:];\n }\n\n /**\n * @dev Converts an address to bytes32.\n * @param _addr The address to convert.\n * @return The bytes32 representation of the address.\n */\n function addressToBytes32(address _addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(_addr)));\n }\n\n /**\n * @dev Converts bytes32 to an address.\n * @param _b The bytes32 value to convert.\n * @return The address representation of bytes32.\n */\n function bytes32ToAddress(bytes32 _b) internal pure returns (address) {\n return address(uint160(uint256(_b)));\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/libs/OFTMsgCodec.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nlibrary OFTMsgCodec {\n // Offset constants for encoding and decoding OFT messages\n uint8 private constant SEND_TO_OFFSET = 32;\n uint8 private constant SEND_AMOUNT_SD_OFFSET = 40;\n\n /**\n * @dev Encodes an OFT LayerZero message.\n * @param _sendTo The recipient address.\n * @param _amountShared The amount in shared decimals.\n * @param _composeMsg The composed message.\n * @return _msg The encoded message.\n * @return hasCompose A boolean indicating whether the message has a composed payload.\n */\n function encode(\n bytes32 _sendTo,\n uint64 _amountShared,\n bytes memory _composeMsg\n ) internal view returns (bytes memory _msg, bool hasCompose) {\n hasCompose = _composeMsg.length > 0;\n // @dev Remote chains will want to know the composed function caller ie. msg.sender on the src.\n _msg = hasCompose\n ? abi.encodePacked(_sendTo, _amountShared, addressToBytes32(msg.sender), _composeMsg)\n : abi.encodePacked(_sendTo, _amountShared);\n }\n\n /**\n * @dev Checks if the OFT message is composed.\n * @param _msg The OFT message.\n * @return A boolean indicating whether the message is composed.\n */\n function isComposed(bytes calldata _msg) internal pure returns (bool) {\n return _msg.length > SEND_AMOUNT_SD_OFFSET;\n }\n\n /**\n * @dev Retrieves the recipient address from the OFT message.\n * @param _msg The OFT message.\n * @return The recipient address.\n */\n function sendTo(bytes calldata _msg) internal pure returns (bytes32) {\n return bytes32(_msg[:SEND_TO_OFFSET]);\n }\n\n /**\n * @dev Retrieves the amount in shared decimals from the OFT message.\n * @param _msg The OFT message.\n * @return The amount in shared decimals.\n */\n function amountSD(bytes calldata _msg) internal pure returns (uint64) {\n return uint64(bytes8(_msg[SEND_TO_OFFSET:SEND_AMOUNT_SD_OFFSET]));\n }\n\n /**\n * @dev Retrieves the composed message from the OFT message.\n * @param _msg The OFT message.\n * @return The composed message.\n */\n function composeMsg(bytes calldata _msg) internal pure returns (bytes memory) {\n return _msg[SEND_AMOUNT_SD_OFFSET:];\n }\n\n /**\n * @dev Converts an address to bytes32.\n * @param _addr The address to convert.\n * @return The bytes32 representation of the address.\n */\n function addressToBytes32(address _addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(_addr)));\n }\n\n /**\n * @dev Converts bytes32 to an address.\n * @param _b The bytes32 value to convert.\n * @return The address representation of bytes32.\n */\n function bytes32ToAddress(bytes32 _b) internal pure returns (address) {\n return address(uint160(uint256(_b)));\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/MintBurnOFTAdapter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\n// External imports\nimport { IERC20, IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\n// Local imports\nimport { IMintableBurnable } from \"./interfaces/IMintableBurnable.sol\";\nimport { OFTCore } from \"./OFTCore.sol\";\n\n/**\n * @title MintBurnOFTAdapter\n * @notice A variant of the standard OFT Adapter that uses an existing ERC20's mint and burn mechanisms for cross-chain transfers.\n *\n * @dev Inherits from OFTCore and provides implementations for _debit and _credit functions using a mintable and burnable token.\n */\nabstract contract MintBurnOFTAdapter is OFTCore {\n /// @dev The underlying ERC20 token.\n IERC20 internal immutable innerToken;\n\n /// @notice The contract responsible for minting and burning tokens.\n IMintableBurnable public immutable minterBurner;\n\n /**\n * @notice Initializes the MintBurnOFTAdapter contract.\n *\n * @param _token The address of the underlying ERC20 token.\n * @param _minterBurner The contract responsible for minting and burning tokens.\n * @param _lzEndpoint The LayerZero endpoint address.\n * @param _delegate The address of the delegate.\n *\n * @dev Calls the OFTCore constructor with the token's decimals, the endpoint, and the delegate.\n */\n constructor(\n address _token,\n IMintableBurnable _minterBurner,\n address _lzEndpoint,\n address _delegate\n ) OFTCore(IERC20Metadata(_token).decimals(), _lzEndpoint, _delegate) {\n innerToken = IERC20(_token);\n minterBurner = _minterBurner;\n }\n\n /**\n * @notice Retrieves the address of the underlying ERC20 token.\n *\n * @return The address of the adapted ERC20 token.\n *\n * @dev In the case of MintBurnOFTAdapter, address(this) and erc20 are NOT the same contract.\n */\n function token() public view returns (address) {\n return address(innerToken);\n }\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the underlying token to send.\n *\n * @return requiresApproval True if approval is required, false otherwise.\n *\n * @dev In this MintBurnOFTAdapter, approval is NOT required because it uses mint and burn privileges.\n */\n function approvalRequired() external pure virtual returns (bool) {\n return false;\n }\n\n /**\n * @notice Burns tokens from the sender's balance to prepare for sending.\n *\n * @param _from The address to debit the tokens from.\n * @param _amountLD The amount of tokens to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination chain ID.\n *\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, i.e., 1 token in, 1 token out.\n * If the 'innerToken' applies something like a transfer fee, the default will NOT work.\n * A pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual override returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n (amountSentLD, amountReceivedLD) = _debitView(_amountLD, _minAmountLD, _dstEid);\n // Burns tokens from the caller.\n minterBurner.burn(_from, amountSentLD);\n }\n\n /**\n * @notice Mints tokens to the specified address upon receiving them.\n *\n * @param _to The address to credit the tokens to.\n * @param _amountLD The amount of tokens to credit in local decimals.\n *\n * @return amountReceivedLD The amount of tokens actually received in local decimals.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, i.e., 1 token in, 1 token out.\n * If the 'innerToken' applies something like a transfer fee, the default will NOT work.\n * A pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /* _srcEid */\n ) internal virtual override returns (uint256 amountReceivedLD) {\n if (_to == address(0x0)) _to = address(0xdead); // _mint(...) does not support address(0x0)\n // Mints the tokens and transfers to the recipient.\n minterBurner.mint(_to, _amountLD);\n // In the case of NON-default OFTAdapter, the amountLD MIGHT not be equal to amountReceivedLD.\n return _amountLD;\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/OFTAdapter.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IERC20Metadata, IERC20 } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IOFT, OFTCore } from \"./OFTCore.sol\";\n\n/**\n * @title OFTAdapter Contract\n * @dev OFTAdapter is a contract that adapts an ERC-20 token to the OFT functionality.\n *\n * @dev For existing ERC20 tokens, this can be used to convert the token to crosschain compatibility.\n * @dev WARNING: ONLY 1 of these should exist for a given global mesh,\n * unless you make a NON-default implementation of OFT and needs to be done very carefully.\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountSentLD/amountReceivedLD.\n */\nabstract contract OFTAdapter is OFTCore {\n using SafeERC20 for IERC20;\n\n IERC20 internal immutable innerToken;\n\n /**\n * @dev Constructor for the OFTAdapter contract.\n * @param _token The address of the ERC-20 token to be adapted.\n * @param _lzEndpoint The LayerZero endpoint address.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(\n address _token,\n address _lzEndpoint,\n address _delegate\n ) OFTCore(IERC20Metadata(_token).decimals(), _lzEndpoint, _delegate) {\n innerToken = IERC20(_token);\n }\n\n /**\n * @dev Retrieves the address of the underlying ERC20 implementation.\n * @return The address of the adapted ERC-20 token.\n *\n * @dev In the case of OFTAdapter, address(this) and erc20 are NOT the same contract.\n */\n function token() public view returns (address) {\n return address(innerToken);\n }\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the 'token()' to send.\n * @return requiresApproval Needs approval of the underlying token implementation.\n *\n * @dev In the case of default OFTAdapter, approval is required.\n * @dev In non-default OFTAdapter contracts with something like mint and burn privileges, it would NOT need approval.\n */\n function approvalRequired() external pure virtual returns (bool) {\n return true;\n }\n\n /**\n * @dev Locks tokens from the sender's specified balance in this contract.\n * @param _from The address to debit from.\n * @param _amountLD The amount of tokens to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination chain ID.\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev msg.sender will need to approve this _amountLD of tokens to be locked inside of the contract.\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual override returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n (amountSentLD, amountReceivedLD) = _debitView(_amountLD, _minAmountLD, _dstEid);\n // @dev Lock tokens by moving them into this contract from the caller.\n innerToken.safeTransferFrom(_from, address(this), amountSentLD);\n }\n\n /**\n * @dev Credits tokens to the specified address.\n * @param _to The address to credit the tokens to.\n * @param _amountLD The amount of tokens to credit in local decimals.\n * @dev _srcEid The source chain ID.\n * @return amountReceivedLD The amount of tokens ACTUALLY received in local decimals.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /*_srcEid*/\n ) internal virtual override returns (uint256 amountReceivedLD) {\n // @dev Unlock the tokens and transfer to the recipient.\n innerToken.safeTransfer(_to, _amountLD);\n // @dev In the case of NON-default OFTAdapter, the amountLD MIGHT not be == amountReceivedLD.\n return _amountLD;\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/OFTCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { OApp, Origin } from \"@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol\";\nimport { OAppOptionsType3 } from \"@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol\";\nimport { IOAppMsgInspector } from \"@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppMsgInspector.sol\";\n\nimport { OAppPreCrimeSimulator } from \"@layerzerolabs/oapp-evm/contracts/precrime/OAppPreCrimeSimulator.sol\";\n\nimport { IOFT, SendParam, OFTLimit, OFTReceipt, OFTFeeDetail, MessagingReceipt, MessagingFee } from \"./interfaces/IOFT.sol\";\nimport { OFTMsgCodec } from \"./libs/OFTMsgCodec.sol\";\nimport { OFTComposeMsgCodec } from \"./libs/OFTComposeMsgCodec.sol\";\n\n/**\n * @title OFTCore\n * @dev Abstract contract for the OftChain (OFT) token.\n */\nabstract contract OFTCore is IOFT, OApp, OAppPreCrimeSimulator, OAppOptionsType3 {\n using OFTMsgCodec for bytes;\n using OFTMsgCodec for bytes32;\n\n // @notice Provides a conversion rate when swapping between denominations of SD and LD\n // - shareDecimals == SD == shared Decimals\n // - localDecimals == LD == local decimals\n // @dev Considers that tokens have different decimal amounts on various chains.\n // @dev eg.\n // For a token\n // - locally with 4 decimals --> 1.2345 => uint(12345)\n // - remotely with 2 decimals --> 1.23 => uint(123)\n // - The conversion rate would be 10 ** (4 - 2) = 100\n // @dev If you want to send 1.2345 -> (uint 12345), you CANNOT represent that value on the remote,\n // you can only display 1.23 -> uint(123).\n // @dev To preserve the dust that would otherwise be lost on that conversion,\n // we need to unify a denomination that can be represented on ALL chains inside of the OFT mesh\n uint256 public immutable decimalConversionRate;\n\n // @notice Msg types that are used to identify the various OFT operations.\n // @dev This can be extended in child contracts for non-default oft operations\n // @dev These values are used in things like combineOptions() in OAppOptionsType3.sol.\n uint16 public constant SEND = 1;\n uint16 public constant SEND_AND_CALL = 2;\n\n // Address of an optional contract to inspect both 'message' and 'options'\n address public msgInspector;\n event MsgInspectorSet(address inspector);\n\n /**\n * @dev Constructor.\n * @param _localDecimals The decimals of the token on the local chain (this chain).\n * @param _endpoint The address of the LayerZero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(uint8 _localDecimals, address _endpoint, address _delegate) OApp(_endpoint, _delegate) {\n if (_localDecimals < sharedDecimals()) revert InvalidLocalDecimals();\n decimalConversionRate = 10 ** (_localDecimals - sharedDecimals());\n }\n\n /**\n * @notice Retrieves interfaceID and the version of the OFT.\n * @return interfaceId The interface ID.\n * @return version The version.\n *\n * @dev interfaceId: This specific interface ID is '0x02e49c2c'.\n * @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.\n * @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.\n * ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)\n */\n function oftVersion() external pure virtual returns (bytes4 interfaceId, uint64 version) {\n return (type(IOFT).interfaceId, 1);\n }\n\n /**\n * @dev Retrieves the shared decimals of the OFT.\n * @return The shared decimals of the OFT.\n *\n * @dev Sets an implicit cap on the amount of tokens, over uint64.max() will need some sort of outbound cap / totalSupply cap\n * Lowest common decimal denominator between chains.\n * Defaults to 6 decimal places to provide up to 18,446,744,073,709.551615 units (max uint64).\n * For tokens exceeding this totalSupply(), they will need to override the sharedDecimals function with something smaller.\n * ie. 4 sharedDecimals would be 1,844,674,407,370,955.1615\n */\n function sharedDecimals() public view virtual returns (uint8) {\n return 6;\n }\n\n /**\n * @dev Sets the message inspector address for the OFT.\n * @param _msgInspector The address of the message inspector.\n *\n * @dev This is an optional contract that can be used to inspect both 'message' and 'options'.\n * @dev Set it to address(0) to disable it, or set it to a contract address to enable it.\n */\n function setMsgInspector(address _msgInspector) public virtual onlyOwner {\n msgInspector = _msgInspector;\n emit MsgInspectorSet(_msgInspector);\n }\n\n /**\n * @notice Provides the fee breakdown and settings data for an OFT. Unused in the default implementation.\n * @param _sendParam The parameters for the send operation.\n * @return oftLimit The OFT limit information.\n * @return oftFeeDetails The details of OFT fees.\n * @return oftReceipt The OFT receipt information.\n */\n function quoteOFT(\n SendParam calldata _sendParam\n )\n external\n view\n virtual\n returns (OFTLimit memory oftLimit, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory oftReceipt)\n {\n uint256 minAmountLD = 0; // Unused in the default implementation.\n uint256 maxAmountLD = IERC20(this.token()).totalSupply(); // Unused in the default implementation.\n oftLimit = OFTLimit(minAmountLD, maxAmountLD);\n\n // Unused in the default implementation; reserved for future complex fee details.\n oftFeeDetails = new OFTFeeDetail[](0);\n\n // @dev This is the same as the send() operation, but without the actual send.\n // - amountSentLD is the amount in local decimals that would be sent from the sender.\n // - amountReceivedLD is the amount in local decimals that will be credited to the recipient on the remote OFT instance.\n // @dev The amountSentLD MIGHT not equal the amount the user actually receives. HOWEVER, the default does.\n (uint256 amountSentLD, uint256 amountReceivedLD) = _debitView(\n _sendParam.amountLD,\n _sendParam.minAmountLD,\n _sendParam.dstEid\n );\n oftReceipt = OFTReceipt(amountSentLD, amountReceivedLD);\n }\n\n /**\n * @notice Provides a quote for the send() operation.\n * @param _sendParam The parameters for the send() operation.\n * @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.\n * @return msgFee The calculated LayerZero messaging fee from the send() operation.\n *\n * @dev MessagingFee: LayerZero msg fee\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n */\n function quoteSend(\n SendParam calldata _sendParam,\n bool _payInLzToken\n ) external view virtual returns (MessagingFee memory msgFee) {\n // @dev mock the amount to receive, this is the same operation used in the send().\n // The quote is as similar as possible to the actual send() operation.\n (, uint256 amountReceivedLD) = _debitView(_sendParam.amountLD, _sendParam.minAmountLD, _sendParam.dstEid);\n\n // @dev Builds the options and OFT message to quote in the endpoint.\n (bytes memory message, bytes memory options) = _buildMsgAndOptions(_sendParam, amountReceivedLD);\n\n // @dev Calculates the LayerZero fee for the send() operation.\n return _quote(_sendParam.dstEid, message, options, _payInLzToken);\n }\n\n /**\n * @dev Executes the send operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The calculated fee for the send() operation.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds.\n * @return msgReceipt The receipt for the send operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) external payable virtual returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) {\n return _send(_sendParam, _fee, _refundAddress);\n }\n\n /**\n * @dev Internal function to execute the send operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The calculated fee for the send() operation.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds.\n * @return msgReceipt The receipt for the send operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function _send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) internal virtual returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) {\n // @dev Applies the token transfers regarding this send() operation.\n // - amountSentLD is the amount in local decimals that was ACTUALLY sent/debited from the sender.\n // - amountReceivedLD is the amount in local decimals that will be received/credited to the recipient on the remote OFT instance.\n (uint256 amountSentLD, uint256 amountReceivedLD) = _debit(\n msg.sender,\n _sendParam.amountLD,\n _sendParam.minAmountLD,\n _sendParam.dstEid\n );\n\n // @dev Builds the options and OFT message to quote in the endpoint.\n (bytes memory message, bytes memory options) = _buildMsgAndOptions(_sendParam, amountReceivedLD);\n\n // @dev Sends the message to the LayerZero endpoint and returns the LayerZero msg receipt.\n msgReceipt = _lzSend(_sendParam.dstEid, message, options, _fee, _refundAddress);\n // @dev Formulate the OFT receipt.\n oftReceipt = OFTReceipt(amountSentLD, amountReceivedLD);\n\n emit OFTSent(msgReceipt.guid, _sendParam.dstEid, msg.sender, amountSentLD, amountReceivedLD);\n }\n\n /**\n * @dev Internal function to build the message and options.\n * @param _sendParam The parameters for the send() operation.\n * @param _amountLD The amount in local decimals.\n * @return message The encoded message.\n * @return options The encoded options.\n */\n function _buildMsgAndOptions(\n SendParam calldata _sendParam,\n uint256 _amountLD\n ) internal view virtual returns (bytes memory message, bytes memory options) {\n bool hasCompose;\n // @dev This generated message has the msg.sender encoded into the payload so the remote knows who the caller is.\n (message, hasCompose) = OFTMsgCodec.encode(\n _sendParam.to,\n _toSD(_amountLD),\n // @dev Must be include a non empty bytes if you want to compose, EVEN if you dont need it on the remote.\n // EVEN if you dont require an arbitrary payload to be sent... eg. '0x01'\n _sendParam.composeMsg\n );\n // @dev Change the msg type depending if its composed or not.\n uint16 msgType = hasCompose ? SEND_AND_CALL : SEND;\n // @dev Combine the callers _extraOptions with the enforced options via the OAppOptionsType3.\n options = combineOptions(_sendParam.dstEid, msgType, _sendParam.extraOptions);\n\n // @dev Optionally inspect the message and options depending if the OApp owner has set a msg inspector.\n // @dev If it fails inspection, needs to revert in the implementation. ie. does not rely on return boolean\n address inspector = msgInspector; // caches the msgInspector to avoid potential double storage read\n if (inspector != address(0)) IOAppMsgInspector(inspector).inspect(message, options);\n }\n\n /**\n * @dev Internal function to handle the receive on the LayerZero endpoint.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The encoded message.\n * @dev _executor The address of the executor.\n * @dev _extraData Additional data.\n */\n function _lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address /*_executor*/, // @dev unused in the default implementation.\n bytes calldata /*_extraData*/ // @dev unused in the default implementation.\n ) internal virtual override {\n // @dev The src sending chain doesnt know the address length on this chain (potentially non-evm)\n // Thus everything is bytes32() encoded in flight.\n address toAddress = _message.sendTo().bytes32ToAddress();\n // @dev Credit the amountLD to the recipient and return the ACTUAL amount the recipient received in local decimals\n uint256 amountReceivedLD = _credit(toAddress, _toLD(_message.amountSD()), _origin.srcEid);\n\n if (_message.isComposed()) {\n // @dev Proprietary composeMsg format for the OFT.\n bytes memory composeMsg = OFTComposeMsgCodec.encode(\n _origin.nonce,\n _origin.srcEid,\n amountReceivedLD,\n _message.composeMsg()\n );\n\n // @dev Stores the lzCompose payload that will be executed in a separate tx.\n // Standardizes functionality for executing arbitrary contract invocation on some non-evm chains.\n // @dev The off-chain executor will listen and process the msg based on the src-chain-callers compose options passed.\n // @dev The index is used when a OApp needs to compose multiple msgs on lzReceive.\n // For default OFT implementation there is only 1 compose msg per lzReceive, thus its always 0.\n endpoint.sendCompose(toAddress, _guid, 0 /* the index of the composed message*/, composeMsg);\n }\n\n emit OFTReceived(_guid, _origin.srcEid, toAddress, amountReceivedLD);\n }\n\n /**\n * @dev Internal function to handle the OAppPreCrimeSimulator simulated receive.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The LayerZero message.\n * @param _executor The address of the off-chain executor.\n * @param _extraData Arbitrary data passed by the msg executor.\n *\n * @dev Enables the preCrime simulator to mock sending lzReceive() messages,\n * routes the msg down from the OAppPreCrimeSimulator, and back up to the OAppReceiver.\n */\n function _lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual override {\n _lzReceive(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Check if the peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint ID to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n *\n * @dev Enables OAppPreCrimeSimulator to check whether a potential Inbound Packet is from a trusted source.\n */\n function isPeer(uint32 _eid, bytes32 _peer) public view virtual override returns (bool) {\n return peers[_eid] == _peer;\n }\n\n /**\n * @dev Internal function to remove dust from the given local decimal amount.\n * @param _amountLD The amount in local decimals.\n * @return amountLD The amount after removing dust.\n *\n * @dev Prevents the loss of dust when moving amounts between chains with different decimals.\n * @dev eg. uint(123) with a conversion rate of 100 becomes uint(100).\n */\n function _removeDust(uint256 _amountLD) internal view virtual returns (uint256 amountLD) {\n return (_amountLD / decimalConversionRate) * decimalConversionRate;\n }\n\n /**\n * @dev Internal function to convert an amount from shared decimals into local decimals.\n * @param _amountSD The amount in shared decimals.\n * @return amountLD The amount in local decimals.\n */\n function _toLD(uint64 _amountSD) internal view virtual returns (uint256 amountLD) {\n return _amountSD * decimalConversionRate;\n }\n\n /**\n * @dev Internal function to convert an amount from local decimals into shared decimals.\n * @param _amountLD The amount in local decimals.\n * @return amountSD The amount in shared decimals.\n */\n function _toSD(uint256 _amountLD) internal view virtual returns (uint64 amountSD) {\n return uint64(_amountLD / decimalConversionRate);\n }\n\n /**\n * @dev Internal function to mock the amount mutation from a OFT debit() operation.\n * @param _amountLD The amount to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @dev _dstEid The destination endpoint ID.\n * @return amountSentLD The amount sent, in local decimals.\n * @return amountReceivedLD The amount to be received on the remote chain, in local decimals.\n *\n * @dev This is where things like fees would be calculated and deducted from the amount to be received on the remote.\n */\n function _debitView(\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 /*_dstEid*/\n ) internal view virtual returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n // @dev Remove the dust so nothing is lost on the conversion between chains with different decimals for the token.\n amountSentLD = _removeDust(_amountLD);\n // @dev The amount to send is the same as amount received in the default implementation.\n amountReceivedLD = amountSentLD;\n\n // @dev Check for slippage.\n if (amountReceivedLD < _minAmountLD) {\n revert SlippageExceeded(amountReceivedLD, _minAmountLD);\n }\n }\n\n /**\n * @dev Internal function to perform a debit operation.\n * @param _from The address to debit.\n * @param _amountLD The amount to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination endpoint ID.\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev Defined here but are intended to be overriden depending on the OFT implementation.\n * @dev Depending on OFT implementation the _amountLD could differ from the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual returns (uint256 amountSentLD, uint256 amountReceivedLD);\n\n /**\n * @dev Internal function to perform a credit operation.\n * @param _to The address to credit.\n * @param _amountLD The amount to credit in local decimals.\n * @param _srcEid The source endpoint ID.\n * @return amountReceivedLD The amount ACTUALLY received in local decimals.\n *\n * @dev Defined here but are intended to be overriden depending on the OFT implementation.\n * @dev Depending on OFT implementation the _amountLD could differ from the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 _srcEid\n ) internal virtual returns (uint256 amountReceivedLD);\n}\n" + }, + "@openzeppelin/contracts/access/AccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/AccessControl.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\nimport \"../utils/Context.sol\";\nimport \"../utils/Strings.sol\";\nimport \"../utils/introspection/ERC165.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\n * members except through off-chain means by accessing the contract event logs. Some\n * applications may benefit from on-chain enumerability, for those cases see\n * {AccessControlEnumerable}.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```\n * function foo() public {\n * require(hasRole(MY_ROLE, msg.sender));\n * ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it.\n */\nabstract contract AccessControl is Context, IAccessControl, ERC165 {\n struct RoleData {\n mapping(address => bool) members;\n bytes32 adminRole;\n }\n\n mapping(bytes32 => RoleData) private _roles;\n\n bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n /**\n * @dev Modifier that checks that an account has a specific role. Reverts\n * with a standardized message including the required role.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n *\n * _Available since v4.1._\n */\n modifier onlyRole(bytes32 role) {\n _checkRole(role, _msgSender());\n _;\n }\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) public view override returns (bool) {\n return _roles[role].members[account];\n }\n\n /**\n * @dev Revert with a standard message if `account` is missing `role`.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n */\n function _checkRole(bytes32 role, address account) internal view {\n if (!hasRole(role, account)) {\n revert(\n string(\n abi.encodePacked(\n \"AccessControl: account \",\n Strings.toHexString(uint160(account), 20),\n \" is missing role \",\n Strings.toHexString(uint256(role), 32)\n )\n )\n );\n }\n }\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) public view override returns (bytes32) {\n return _roles[role].adminRole;\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _grantRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _revokeRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been revoked `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) public virtual override {\n require(account == _msgSender(), \"AccessControl: can only renounce roles for self\");\n\n _revokeRole(role, account);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event. Note that unlike {grantRole}, this function doesn't perform any\n * checks on the calling account.\n *\n * [WARNING]\n * ====\n * This function should only be called from the constructor when setting\n * up the initial roles for the system.\n *\n * Using this function in any other way is effectively circumventing the admin\n * system imposed by {AccessControl}.\n * ====\n *\n * NOTE: This function is deprecated in favor of {_grantRole}.\n */\n function _setupRole(bytes32 role, address account) internal virtual {\n _grantRole(role, account);\n }\n\n /**\n * @dev Sets `adminRole` as ``role``'s admin role.\n *\n * Emits a {RoleAdminChanged} event.\n */\n function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n bytes32 previousAdminRole = getRoleAdmin(role);\n _roles[role].adminRole = adminRole;\n emit RoleAdminChanged(role, previousAdminRole, adminRole);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * Internal function without access restriction.\n */\n function _grantRole(bytes32 role, address account) internal virtual {\n if (!hasRole(role, account)) {\n _roles[role].members[account] = true;\n emit RoleGranted(role, account, _msgSender());\n }\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * Internal function without access restriction.\n */\n function _revokeRole(bytes32 role, address account) internal virtual {\n if (hasRole(role, account)) {\n _roles[role].members[account] = false;\n emit RoleRevoked(role, account, _msgSender());\n }\n }\n}\n" + }, + "@openzeppelin/contracts/access/AccessControlEnumerable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/AccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControlEnumerable.sol\";\nimport \"./AccessControl.sol\";\nimport \"../utils/structs/EnumerableSet.sol\";\n\n/**\n * @dev Extension of {AccessControl} that allows enumerating the members of each role.\n */\nabstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {\n using EnumerableSet for EnumerableSet.AddressSet;\n\n mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) public view override returns (address) {\n return _roleMembers[role].at(index);\n }\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) public view override returns (uint256) {\n return _roleMembers[role].length();\n }\n\n /**\n * @dev Overload {_grantRole} to track enumerable memberships\n */\n function _grantRole(bytes32 role, address account) internal virtual override {\n super._grantRole(role, account);\n _roleMembers[role].add(account);\n }\n\n /**\n * @dev Overload {_revokeRole} to track enumerable memberships\n */\n function _revokeRole(bytes32 role, address account) internal virtual override {\n super._revokeRole(role, account);\n _roleMembers[role].remove(account);\n }\n}\n" + }, + "@openzeppelin/contracts/access/IAccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n /**\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n *\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n * {RoleAdminChanged} not being emitted signaling this.\n *\n * _Available since v3.1._\n */\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n /**\n * @dev Emitted when `account` is granted `role`.\n *\n * `sender` is the account that originated the contract call, an admin role\n * bearer except when using {AccessControl-_setupRole}.\n */\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Emitted when `account` is revoked `role`.\n *\n * `sender` is the account that originated the contract call:\n * - if using `revokeRole`, it is the admin role bearer\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\n */\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) external view returns (bool);\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been granted `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) external;\n}\n" + }, + "@openzeppelin/contracts/access/IAccessControlEnumerable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\n\n/**\n * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.\n */\ninterface IAccessControlEnumerable is IAccessControl {\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) external view returns (address);\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) external view returns (uint256);\n}\n" + }, + "@openzeppelin/contracts/access/Ownable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n" + }, + "@openzeppelin/contracts/security/Pausable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/ERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/ERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"./extensions/IERC20Metadata.sol\";\nimport \"../../utils/Context.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20, IERC20Metadata {\n mapping(address => uint256) private _balances;\n\n mapping(address => mapping(address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n\n /**\n * @dev Sets the values for {name} and {symbol}.\n *\n * The default value of {decimals} is 18. To select a different value for\n * {decimals} you should overload it.\n *\n * All two of these values are immutable: they can only be set once during\n * construction.\n */\n constructor(string memory name_, string memory symbol_) {\n _name = name_;\n _symbol = symbol_;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless this function is\n * overridden;\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `recipient` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n _approve(_msgSender(), spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * Requirements:\n *\n * - `sender` and `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n * - the caller must have allowance for ``sender``'s tokens of at least\n * `amount`.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public virtual override returns (bool) {\n _transfer(sender, recipient, amount);\n\n uint256 currentAllowance = _allowances[sender][_msgSender()];\n require(currentAllowance >= amount, \"ERC20: transfer amount exceeds allowance\");\n unchecked {\n _approve(sender, _msgSender(), currentAllowance - amount);\n }\n\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n uint256 currentAllowance = _allowances[_msgSender()][spender];\n require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n unchecked {\n _approve(_msgSender(), spender, currentAllowance - subtractedValue);\n }\n\n return true;\n }\n\n /**\n * @dev Moves `amount` of tokens from `sender` to `recipient`.\n *\n * This internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `sender` cannot be the zero address.\n * - `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n */\n function _transfer(\n address sender,\n address recipient,\n uint256 amount\n ) internal virtual {\n require(sender != address(0), \"ERC20: transfer from the zero address\");\n require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(sender, recipient, amount);\n\n uint256 senderBalance = _balances[sender];\n require(senderBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n unchecked {\n _balances[sender] = senderBalance - amount;\n }\n _balances[recipient] += amount;\n\n emit Transfer(sender, recipient, amount);\n\n _afterTokenTransfer(sender, recipient, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply += amount;\n _balances[account] += amount;\n emit Transfer(address(0), account, amount);\n\n _afterTokenTransfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n uint256 accountBalance = _balances[account];\n require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n unchecked {\n _balances[account] = accountBalance - amount;\n }\n _totalSupply -= amount;\n\n emit Transfer(account, address(0), amount);\n\n _afterTokenTransfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n\n /**\n * @dev Hook that is called after any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * has been transferred to `to`.\n * - when `from` is zero, `amount` tokens have been minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _afterTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC721 token receiver interface\n * @dev Interface for any contract that wants to support safeTransfers\n * from ERC721 asset contracts.\n */\ninterface IERC721Receiver {\n /**\n * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}\n * by `operator` from `from`, this function is called.\n *\n * It must return its Solidity selector to confirm the token transfer.\n * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.\n *\n * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.\n */\n function onERC721Received(\n address operator,\n address from,\n uint256 tokenId,\n bytes calldata data\n ) external returns (bytes4);\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/ERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC165.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n *\n * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.\n */\nabstract contract ERC165 is IERC165 {\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IERC165).interfaceId;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/IERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeMath.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)\n\npragma solidity ^0.8.0;\n\n// CAUTION\n// This version of SafeMath should only be used with Solidity 0.8 or later,\n// because it relies on the compiler's built in overflow checks.\n\n/**\n * @dev Wrappers over Solidity's arithmetic operations.\n *\n * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler\n * now has built in overflow checking.\n */\nlibrary SafeMath {\n /**\n * @dev Returns the addition of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n uint256 c = a + b;\n if (c < a) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the substraction of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b > a) return (false, 0);\n return (true, a - b);\n }\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n // benefit is lost if 'b' is also tested.\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n if (a == 0) return (true, 0);\n uint256 c = a * b;\n if (c / a != b) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the division of two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a / b);\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a % b);\n }\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `+` operator.\n *\n * Requirements:\n *\n * - Addition cannot overflow.\n */\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\n return a + b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting on\n * overflow (when the result is negative).\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\n return a - b;\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `*` operator.\n *\n * Requirements:\n *\n * - Multiplication cannot overflow.\n */\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\n return a * b;\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator.\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\n return a / b;\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting when dividing by zero.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\n return a % b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\n * overflow (when the result is negative).\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {trySub}.\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b <= a, errorMessage);\n return a - b;\n }\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting with custom message on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator. Note: this function uses a\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\n * uses an invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a / b;\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting with custom message when dividing by zero.\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {tryMod}.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a % b;\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Strings.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n bytes16 private constant _HEX_SYMBOLS = \"0123456789abcdef\";\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n // Inspired by OraclizeAPI's implementation - MIT licence\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\n\n if (value == 0) {\n return \"0\";\n }\n uint256 temp = value;\n uint256 digits;\n while (temp != 0) {\n digits++;\n temp /= 10;\n }\n bytes memory buffer = new bytes(digits);\n while (value != 0) {\n digits -= 1;\n buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\n value /= 10;\n }\n return string(buffer);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n if (value == 0) {\n return \"0x00\";\n }\n uint256 temp = value;\n uint256 length = 0;\n while (temp != 0) {\n length++;\n temp >>= 8;\n }\n return toHexString(value, length);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _HEX_SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/structs/EnumerableSet.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 => uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n" + }, + "contracts/automation/AbstractCCIPBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IRouterClient } from \"@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol\";\nimport { Client } from \"@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol\";\n\nabstract contract AbstractCCIPBridgeHelperModule is AbstractSafeModule {\n /**\n * @notice Bridges a token from the source chain to the destination chain using CCIP\n * @param ccipRouter The CCIP router contract\n * @param destinationChainSelector The selector for the destination chain\n * @param token The token to bridge\n * @param amount The amount of token to bridge\n */\n function _bridgeTokenWithCCIP(\n IRouterClient ccipRouter,\n uint64 destinationChainSelector,\n IERC20 token,\n uint256 amount\n ) internal {\n bool success;\n\n // Approve CCIP Router to move the token\n success = safeContract.execTransactionFromModule(\n address(token),\n 0, // Value\n abi.encodeWithSelector(token.approve.selector, ccipRouter, amount),\n 0 // Call\n );\n require(success, \"Failed to approve token\");\n\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(token),\n amount: amount\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory ccipMessage = Client.EVM2AnyMessage({\n receiver: abi.encode(address(safeContract)), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n // Get CCIP fee\n uint256 ccipFee = ccipRouter.getFee(\n destinationChainSelector,\n ccipMessage\n );\n\n // Send CCIP message\n success = safeContract.execTransactionFromModule(\n address(ccipRouter),\n ccipFee, // Value\n abi.encodeWithSelector(\n ccipRouter.ccipSend.selector,\n destinationChainSelector,\n ccipMessage\n ),\n 0 // Call\n );\n require(success, \"Failed to send CCIP message\");\n }\n}\n" + }, + "contracts/automation/AbstractLZBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\n\nimport { IOFT, SendParam } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\nimport { MessagingFee } from \"@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol\";\nimport { OptionsBuilder } from \"@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nabstract contract AbstractLZBridgeHelperModule is AbstractSafeModule {\n using OptionsBuilder for bytes;\n\n /**\n * @dev Bridges token using LayerZero.\n * @param lzEndpointId LayerZero endpoint id.\n * @param token Token to bridge.\n * @param lzAdapter LZ Adapter to use.\n * @param amount Amount of token to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n * @param isNativeToken Whether the token is native token.\n */\n function _bridgeTokenWithLz(\n uint32 lzEndpointId,\n IERC20 token,\n IOFT lzAdapter,\n uint256 amount,\n uint256 slippageBps,\n bool isNativeToken\n ) internal {\n bool success;\n\n if (!isNativeToken) {\n // Approve LZ Adapter to move the token\n success = safeContract.execTransactionFromModule(\n address(token),\n 0, // Value\n abi.encodeWithSelector(\n token.approve.selector,\n address(lzAdapter),\n amount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve token\");\n }\n\n // Calculate minimum amount to receive\n uint256 minAmount = (amount * (10000 - slippageBps)) / 10000;\n\n // Hardcoded gaslimit of 400k\n bytes memory options = OptionsBuilder\n .newOptions()\n .addExecutorLzReceiveOption(400000, 0);\n\n // Build send param\n SendParam memory sendParam = SendParam({\n dstEid: lzEndpointId,\n to: bytes32(uint256(uint160(address(safeContract)))),\n amountLD: amount,\n minAmountLD: minAmount,\n extraOptions: options,\n composeMsg: bytes(\"\"),\n oftCmd: bytes(\"\")\n });\n\n // Compute fees\n MessagingFee memory msgFee = lzAdapter.quoteSend(sendParam, false);\n\n uint256 value = isNativeToken\n ? amount + msgFee.nativeFee\n : msgFee.nativeFee;\n\n // Execute transaction\n success = safeContract.execTransactionFromModule(\n address(lzAdapter),\n value,\n abi.encodeWithSelector(\n lzAdapter.send.selector,\n sendParam,\n msgFee,\n address(safeContract)\n ),\n 0\n );\n require(success, \"Failed to bridge token\");\n }\n}\n" + }, + "contracts/automation/AbstractSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { ISafe } from \"../interfaces/ISafe.sol\";\n\nabstract contract AbstractSafeModule is AccessControlEnumerable {\n ISafe public immutable safeContract;\n\n bytes32 public constant OPERATOR_ROLE = keccak256(\"OPERATOR_ROLE\");\n\n modifier onlySafe() {\n require(\n msg.sender == address(safeContract),\n \"Caller is not the safe contract\"\n );\n _;\n }\n\n modifier onlyOperator() {\n require(\n hasRole(OPERATOR_ROLE, msg.sender),\n \"Caller is not an operator\"\n );\n _;\n }\n\n constructor(address _safeContract) {\n safeContract = ISafe(_safeContract);\n _grantRole(DEFAULT_ADMIN_ROLE, address(safeContract));\n _grantRole(OPERATOR_ROLE, address(safeContract));\n }\n\n /**\n * @dev Helps recovering any tokens accidentally sent to this module.\n * @param token Token to transfer. 0x0 to transfer Native token.\n * @param amount Amount to transfer. 0 to transfer all balance.\n */\n function transferTokens(address token, uint256 amount) external onlySafe {\n if (address(token) == address(0)) {\n // Move ETH\n amount = amount > 0 ? amount : address(this).balance;\n payable(address(safeContract)).transfer(amount);\n return;\n }\n\n // Move all balance if amount set to 0\n amount = amount > 0 ? amount : IERC20(token).balanceOf(address(this));\n\n // Transfer to Safe contract\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(token).transfer(address(safeContract), amount);\n }\n\n receive() external payable {\n // Accept ETH to pay for bridge fees\n }\n}\n" + }, + "contracts/automation/BaseBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// solhint-disable-next-line max-line-length\nimport { AbstractCCIPBridgeHelperModule, AbstractSafeModule, IRouterClient } from \"./AbstractCCIPBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { BridgedWOETHStrategy } from \"../strategies/BridgedWOETHStrategy.sol\";\n\ncontract BaseBridgeHelperModule is\n AccessControlEnumerable,\n AbstractCCIPBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0x98a0CbeF61bD2D21435f433bE4CD42B56B38CC93);\n IWETH9 public constant weth =\n IWETH9(0x4200000000000000000000000000000000000006);\n IERC20 public constant oethb =\n IERC20(0xDBFeFD2e8460a6Ee4955A68582F85708BAEA60A3);\n IERC4626 public constant bridgedWOETH =\n IERC4626(0xD8724322f44E5c58D7A815F542036fb17DbbF839);\n\n BridgedWOETHStrategy public constant bridgedWOETHStrategy =\n BridgedWOETHStrategy(0x80c864704DD06C3693ed5179190786EE38ACf835);\n\n IRouterClient public constant CCIP_ROUTER =\n IRouterClient(0x881e3A65B4d4a04dD529061dd0071cf975F58bCD);\n\n uint64 public constant CCIP_ETHEREUM_CHAIN_SELECTOR = 5009297550715157269;\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Ethereum.\n * @param woethAmount Amount of wOETH to bridge.\n */\n function bridgeWOETHToEthereum(uint256 woethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_ETHEREUM_CHAIN_SELECTOR,\n IERC20(address(bridgedWOETH)),\n woethAmount\n );\n }\n\n /**\n * @dev Bridges WETH to Ethereum.\n * @param wethAmount Amount of WETH to bridge.\n */\n function bridgeWETHToEthereum(uint256 wethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_ETHEREUM_CHAIN_SELECTOR,\n IERC20(address(weth)),\n wethAmount\n );\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem the wOETH for WETH using the Vault.\n * @return Amount of WETH received.\n */\n function depositWOETH(uint256 woethAmount, bool redeemWithVault)\n external\n onlyOperator\n returns (uint256)\n {\n return _depositWOETH(woethAmount, redeemWithVault);\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy and bridges it to Ethereum.\n * @param woethAmount Amount of wOETH to deposit.\n * @return Amount of WETH received.\n */\n function depositWOETHAndBridgeWETH(uint256 woethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n uint256 wethAmount = _depositWOETH(woethAmount, true);\n bridgeWETHToEthereum(wethAmount);\n return wethAmount;\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem the wOETH for WETH using the Vault.\n * @return Amount of WETH received.\n */\n function _depositWOETH(uint256 woethAmount, bool redeemWithVault)\n internal\n returns (uint256)\n {\n // Update oracle price\n bridgedWOETHStrategy.updateWOETHOraclePrice();\n\n // Rebase to account for any yields from price update\n vault.rebase();\n\n uint256 oethbAmount = oethb.balanceOf(address(safeContract));\n\n // Approve bridgedWOETH strategy to move wOETH\n bool success = safeContract.execTransactionFromModule(\n address(bridgedWOETH),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETH.approve.selector,\n address(bridgedWOETHStrategy),\n woethAmount\n ),\n 0 // Call\n );\n\n // Deposit to bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.depositBridgedWOETH.selector,\n woethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to deposit bridged WOETH\");\n\n oethbAmount = oethb.balanceOf(address(safeContract)) - oethbAmount;\n\n // Rebase to account for any yields from price update\n // and backing asset change from deposit\n vault.rebase();\n\n if (!redeemWithVault) {\n return oethbAmount;\n }\n\n // Redeem for WETH using Vault\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethbAmount,\n oethbAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETHb\");\n\n return oethbAmount;\n }\n\n /**\n * @dev Deposits WETH into the Vault and redeems wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to deposit.\n * @return Amount of wOETH received.\n */\n function depositWETHAndRedeemWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _withdrawWOETH(wethAmount);\n }\n\n function depositWETHAndBridgeWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n uint256 woethAmount = _withdrawWOETH(wethAmount);\n bridgeWOETHToEthereum(woethAmount);\n return woethAmount;\n }\n\n /**\n * @dev Withdraws wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to use to withdraw.\n * @return Amount of wOETH received.\n */\n function _withdrawWOETH(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETHb with WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETHb\");\n\n // Approve bridgedWOETH strategy to move OETHb\n success = safeContract.execTransactionFromModule(\n address(oethb),\n 0, // Value\n abi.encodeWithSelector(\n oethb.approve.selector,\n address(bridgedWOETHStrategy),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETHb\");\n\n uint256 woethAmount = bridgedWOETH.balanceOf(address(safeContract));\n\n // Withdraw from bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.withdrawBridgedWOETH.selector,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to withdraw bridged WOETH\");\n\n woethAmount =\n bridgedWOETH.balanceOf(address(safeContract)) -\n woethAmount;\n\n return woethAmount;\n }\n}\n" + }, + "contracts/automation/ClaimBribesSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { ICLGauge } from \"../interfaces/aerodrome/ICLGauge.sol\";\nimport { ICLPool } from \"../interfaces/aerodrome/ICLPool.sol\";\n\nstruct BribePoolInfo {\n address poolAddress;\n address rewardContractAddress;\n address[] rewardTokens;\n}\n\ninterface IAerodromeVoter {\n function claimBribes(\n address[] memory _bribes,\n address[][] memory _tokens,\n uint256 _tokenId\n ) external;\n}\n\ninterface IVeNFT {\n function ownerOf(uint256 tokenId) external view returns (address);\n\n function ownerToNFTokenIdList(address owner, uint256 index)\n external\n view\n returns (uint256);\n}\n\ninterface ICLRewardContract {\n function rewards(uint256 index) external view returns (address);\n\n function rewardsListLength() external view returns (uint256);\n}\n\ncontract ClaimBribesSafeModule is AbstractSafeModule {\n IAerodromeVoter public immutable voter;\n address public immutable veNFT;\n\n uint256[] nftIds;\n mapping(uint256 => uint256) nftIdIndex;\n\n BribePoolInfo[] bribePools;\n mapping(address => uint256) bribePoolIndex;\n\n event NFTIdAdded(uint256 nftId);\n event NFTIdRemoved(uint256 nftId);\n\n event BribePoolAdded(address bribePool);\n event BribePoolRemoved(address bribePool);\n\n constructor(\n address _safeContract,\n address _voter,\n address _veNFT\n ) AbstractSafeModule(_safeContract) {\n voter = IAerodromeVoter(_voter);\n veNFT = _veNFT;\n }\n\n /**\n * @dev Claim bribes for a range of NFTs\n * @param nftIndexStart The start index of the NFTs\n * @param nftIndexEnd The end index of the NFTs\n * @param silent Doesn't revert if the claim fails when true\n */\n function claimBribes(\n uint256 nftIndexStart,\n uint256 nftIndexEnd,\n bool silent\n ) external onlyOperator {\n if (nftIndexEnd < nftIndexStart) {\n (nftIndexStart, nftIndexEnd) = (nftIndexEnd, nftIndexStart);\n }\n uint256 nftCount = nftIds.length;\n nftIndexEnd = nftCount < nftIndexEnd ? nftCount : nftIndexEnd;\n\n (\n address[] memory rewardContractAddresses,\n address[][] memory rewardTokens\n ) = _getRewardsInfoArray();\n\n for (uint256 i = nftIndexStart; i < nftIndexEnd; i++) {\n uint256 nftId = nftIds[i];\n bool success = safeContract.execTransactionFromModule(\n address(voter),\n 0, // Value\n abi.encodeWithSelector(\n IAerodromeVoter.claimBribes.selector,\n rewardContractAddresses,\n rewardTokens,\n nftId\n ),\n 0 // Call\n );\n\n require(success || silent, \"ClaimBribes failed\");\n }\n }\n\n /**\n * @dev Get the reward contract address and reward tokens for all pools\n * @return rewardContractAddresses The reward contract addresses\n * @return rewardTokens The reward tokens\n */\n function _getRewardsInfoArray()\n internal\n view\n returns (\n address[] memory rewardContractAddresses,\n address[][] memory rewardTokens\n )\n {\n BribePoolInfo[] memory _bribePools = bribePools;\n uint256 bribePoolCount = _bribePools.length;\n rewardContractAddresses = new address[](bribePoolCount);\n rewardTokens = new address[][](bribePoolCount);\n\n for (uint256 i = 0; i < bribePoolCount; i++) {\n rewardContractAddresses[i] = _bribePools[i].rewardContractAddress;\n rewardTokens[i] = _bribePools[i].rewardTokens;\n }\n }\n\n /***************************************\n NFT Management\n ****************************************/\n /**\n * @dev Add NFT IDs to the list\n * @param _nftIds The NFT IDs to add\n */\n function addNFTIds(uint256[] memory _nftIds) external onlyOperator {\n for (uint256 i = 0; i < _nftIds.length; i++) {\n uint256 nftId = _nftIds[i];\n if (nftIdExists(nftId)) {\n // If it already exists, skip\n continue;\n }\n\n // Make sure the NFT is owned by the Safe\n require(\n IVeNFT(veNFT).ownerOf(nftId) == address(safeContract),\n \"NFT not owned by safe\"\n );\n\n nftIdIndex[nftId] = nftIds.length;\n nftIds.push(nftId);\n\n emit NFTIdAdded(nftId);\n }\n }\n\n /**\n * @dev Remove NFT IDs from the list\n * @param _nftIds The NFT IDs to remove\n */\n function removeNFTIds(uint256[] memory _nftIds) external onlyOperator {\n for (uint256 i = 0; i < _nftIds.length; i++) {\n uint256 nftId = _nftIds[i];\n if (!nftIdExists(nftId)) {\n // If it doesn't exist, skip\n continue;\n }\n\n uint256 index = nftIdIndex[nftId];\n uint256 lastNftId = nftIds[nftIds.length - 1];\n nftIds[index] = lastNftId;\n nftIdIndex[lastNftId] = index;\n nftIds.pop();\n\n emit NFTIdRemoved(nftId);\n }\n }\n\n /**\n * @dev Check if a NFT exists on the list\n * @param nftId The NFT ID to check\n * @return true if the NFT ID exists, false otherwise\n */\n function nftIdExists(uint256 nftId) public view returns (bool) {\n uint256 index = nftIdIndex[nftId];\n uint256[] memory _nftIds = nftIds;\n return (index < _nftIds.length) && _nftIds[index] == nftId;\n }\n\n /**\n * @dev Get the length of the nftIds list\n * @return The length of the nftIds list\n */\n function getNFTIdsLength() external view returns (uint256) {\n return nftIds.length;\n }\n\n /**\n * @dev Get all NFT IDs\n * @return The NFT IDs\n */\n function getAllNFTIds() external view returns (uint256[] memory) {\n return nftIds;\n }\n\n /**\n * @dev Fetch all NFT IDs from the veNFT contract\n * @notice This can revert if Safe owns too many NFTs since tx will be huge.\n * This function is public, anyone can call it, since it only fetches\n * the NFT IDs owned by the Safe. It shouldn't cause us any issues.\n */\n function fetchNFTIds() external {\n // Purge the array\n delete nftIds;\n\n uint256 i = 0;\n while (true) {\n uint256 nftId = IVeNFT(veNFT).ownerToNFTokenIdList(\n address(safeContract),\n i\n );\n if (nftId == 0) {\n break;\n }\n\n nftIdIndex[nftId] = nftIds.length;\n nftIds.push(nftId);\n i++;\n }\n }\n\n /**\n * @dev Remove all NFT IDs from the list\n */\n function removeAllNFTIds() external onlyOperator {\n uint256 length = nftIds.length;\n for (uint256 i = 0; i < length; i++) {\n uint256 nftId = nftIds[i];\n delete nftIdIndex[nftId];\n emit NFTIdRemoved(nftId);\n }\n\n delete nftIds;\n }\n\n /***************************************\n Bribe Pool Management\n ****************************************/\n // @dev Whitelist a pool to claim bribes from\n // @param _poolAddress The address of the pool to whitelist\n function addBribePool(address _poolAddress, bool _isVotingContract)\n external\n onlySafe\n {\n BribePoolInfo memory bribePool;\n\n if (_isVotingContract) {\n bribePool = BribePoolInfo({\n poolAddress: _poolAddress,\n rewardContractAddress: _poolAddress,\n rewardTokens: _getRewardTokenAddresses(_poolAddress)\n });\n } else {\n // Find the gauge address\n address _gaugeAddress = ICLPool(_poolAddress).gauge();\n // And the reward contract address\n address _rewardContractAddress = ICLGauge(_gaugeAddress)\n .feesVotingReward();\n\n bribePool = BribePoolInfo({\n poolAddress: _poolAddress,\n rewardContractAddress: _rewardContractAddress,\n rewardTokens: _getRewardTokenAddresses(_rewardContractAddress)\n });\n }\n\n if (bribePoolExists(_poolAddress)) {\n // Update if it already exists\n bribePools[bribePoolIndex[_poolAddress]] = bribePool;\n } else {\n // If not, Append to the list\n bribePoolIndex[_poolAddress] = bribePools.length;\n bribePools.push(bribePool);\n }\n\n emit BribePoolAdded(_poolAddress);\n }\n\n /**\n * @dev Update the reward token addresses for all pools\n */\n function updateRewardTokenAddresses() external onlyOperator {\n BribePoolInfo[] storage _bribePools = bribePools;\n for (uint256 i = 0; i < _bribePools.length; i++) {\n BribePoolInfo storage bribePool = _bribePools[i];\n bribePool.rewardTokens = _getRewardTokenAddresses(\n bribePool.rewardContractAddress == bribePool.poolAddress\n ? bribePool.poolAddress\n : bribePool.rewardContractAddress\n );\n }\n }\n\n /**\n * @dev Get the reward token addresses for a given reward contract address\n * @param _rewardContractAddress The address of the reward contract\n * @return _rewardTokens The reward token addresses\n */\n function _getRewardTokenAddresses(address _rewardContractAddress)\n internal\n view\n returns (address[] memory)\n {\n address[] memory _rewardTokens = new address[](\n ICLRewardContract(_rewardContractAddress).rewardsListLength()\n );\n for (uint256 i = 0; i < _rewardTokens.length; i++) {\n _rewardTokens[i] = ICLRewardContract(_rewardContractAddress)\n .rewards(i);\n }\n\n return _rewardTokens;\n }\n\n /**\n * @dev Remove a bribe pool from the list\n * @param _poolAddress The address of the pool to remove\n */\n function removeBribePool(address _poolAddress) external onlySafe {\n if (!bribePoolExists(_poolAddress)) {\n // If it doesn't exist, skip\n return;\n }\n\n uint256 index = bribePoolIndex[_poolAddress];\n BribePoolInfo memory lastBribePool = bribePools[bribePools.length - 1];\n bribePools[index] = lastBribePool;\n bribePoolIndex[lastBribePool.poolAddress] = index;\n bribePools.pop();\n\n emit BribePoolRemoved(_poolAddress);\n }\n\n /**\n * @dev Check if a bribe pool exists\n * @param bribePool The address of the pool to check\n * @return true if the pool exists, false otherwise\n */\n function bribePoolExists(address bribePool) public view returns (bool) {\n BribePoolInfo[] memory _bribePools = bribePools;\n uint256 poolIndex = bribePoolIndex[bribePool];\n return\n poolIndex < _bribePools.length &&\n _bribePools[poolIndex].poolAddress == bribePool;\n }\n\n /**\n * @dev Get the length of the bribe pools list\n * @return The length of the bribe pools list\n */\n function getBribePoolsLength() external view returns (uint256) {\n return bribePools.length;\n }\n}\n" + }, + "contracts/automation/ClaimStrategyRewardsSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\n\nimport { ISafe } from \"../interfaces/ISafe.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract ClaimStrategyRewardsSafeModule is AbstractSafeModule {\n using SafeERC20 for IERC20;\n\n mapping(address => bool) public isStrategyWhitelisted;\n address[] public strategies;\n\n event StrategyAdded(address strategy);\n event StrategyRemoved(address strategy);\n\n event ClaimRewardsFailed(address strategy);\n\n constructor(\n address _safeAddress,\n address operator,\n address[] memory _strategies\n ) AbstractSafeModule(_safeAddress) {\n _grantRole(OPERATOR_ROLE, operator);\n\n // Whitelist all strategies\n for (uint256 i = 0; i < _strategies.length; i++) {\n _addStrategy(_strategies[i]);\n }\n }\n\n /**\n * @dev Claim rewards from all whitelisted strategies\n * @param silent Doesn't revert on error if set to true\n */\n function claimRewards(bool silent) external onlyRole(OPERATOR_ROLE) {\n uint256 strategiesLength = strategies.length;\n for (uint256 i = 0; i < strategiesLength; i++) {\n address strategy = strategies[i];\n\n // Execute `collectRewardTokens` for all strategies\n bool success = safeContract.execTransactionFromModule(\n strategy, // To\n 0, // Value\n abi.encodeWithSelector(IStrategy.collectRewardTokens.selector),\n 0 // Call\n );\n\n if (!success) {\n emit ClaimRewardsFailed(strategy);\n }\n\n require(success || silent, \"Failed to claim rewards\");\n }\n }\n\n /**\n * @dev Add a strategy to the whitelist\n * @param _strategy The address of the strategy to add\n */\n function addStrategy(address _strategy)\n external\n onlyRole(DEFAULT_ADMIN_ROLE)\n {\n _addStrategy(_strategy);\n }\n\n function _addStrategy(address _strategy) internal {\n require(\n !isStrategyWhitelisted[_strategy],\n \"Strategy already whitelisted\"\n );\n isStrategyWhitelisted[_strategy] = true;\n strategies.push(_strategy);\n emit StrategyAdded(_strategy);\n }\n\n /**\n * @dev Remove a strategy from the whitelist\n * @param _strategy The address of the strategy to remove\n */\n function removeStrategy(address _strategy)\n external\n onlyRole(DEFAULT_ADMIN_ROLE)\n {\n require(isStrategyWhitelisted[_strategy], \"Strategy not whitelisted\");\n isStrategyWhitelisted[_strategy] = false;\n\n for (uint256 i = 0; i < strategies.length; i++) {\n if (strategies[i] == _strategy) {\n strategies[i] = strategies[strategies.length - 1];\n strategies.pop();\n break;\n }\n }\n\n emit StrategyRemoved(_strategy);\n }\n}\n" + }, + "contracts/automation/CollectXOGNRewardsModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IXOGN {\n function collectRewards() external;\n}\n\ncontract CollectXOGNRewardsModule is AbstractSafeModule {\n IXOGN public constant xogn =\n IXOGN(0x63898b3b6Ef3d39332082178656E9862bee45C57);\n address public constant rewardsSource =\n 0x7609c88E5880e934dd3A75bCFef44E31b1Badb8b;\n IERC20 public constant ogn =\n IERC20(0x8207c1FfC5B6804F6024322CcF34F29c3541Ae26);\n\n constructor(address _safeContract, address operator)\n AbstractSafeModule(_safeContract)\n {\n _grantRole(OPERATOR_ROLE, operator);\n }\n\n function collectRewards() external onlyOperator {\n uint256 balance = ogn.balanceOf(address(safeContract));\n\n bool success = safeContract.execTransactionFromModule(\n address(xogn),\n 0, // Value\n abi.encodeWithSelector(IXOGN.collectRewards.selector),\n 0 // Call\n );\n\n require(success, \"Failed to collect rewards\");\n\n balance = ogn.balanceOf(address(safeContract)) - balance;\n\n if (balance == 0) {\n return;\n }\n\n success = safeContract.execTransactionFromModule(\n address(ogn),\n 0, // Value\n abi.encodeWithSelector(\n IERC20.transfer.selector,\n rewardsSource,\n balance\n ),\n 0 // Call\n );\n\n require(success, \"Failed to collect rewards\");\n }\n}\n" + }, + "contracts/automation/CurvePoolBoosterBribesModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\n\ninterface ICurvePoolBooster {\n function manageTotalRewardAmount(\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external;\n\n function manageNumberOfPeriods(\n uint8 extraNumberOfPeriods,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external;\n}\n\ncontract CurvePoolBoosterBribesModule is AbstractSafeModule {\n address[] public POOLS;\n\n event PoolBoosterAddressAdded(address pool);\n event PoolBoosterAddressRemoved(address pool);\n\n constructor(\n address _safeContract,\n address _operator,\n address[] memory _pools\n ) AbstractSafeModule(_safeContract) {\n _grantRole(OPERATOR_ROLE, _operator);\n _addPoolBoosterAddress(_pools);\n }\n\n function addPoolBoosterAddress(address[] memory pools)\n external\n onlyOperator\n {\n _addPoolBoosterAddress(pools);\n }\n\n function _addPoolBoosterAddress(address[] memory pools) internal {\n for (uint256 i = 0; i < pools.length; i++) {\n POOLS.push(pools[i]);\n emit PoolBoosterAddressAdded(pools[i]);\n }\n }\n\n function removePoolBoosterAddress(address[] calldata pools)\n external\n onlyOperator\n {\n for (uint256 i = 0; i < pools.length; i++) {\n _removePoolBoosterAddress(pools[i]);\n }\n }\n\n function _removePoolBoosterAddress(address pool) internal {\n uint256 length = POOLS.length;\n for (uint256 i = 0; i < length; i++) {\n if (POOLS[i] == pool) {\n POOLS[i] = POOLS[length - 1];\n POOLS.pop();\n emit PoolBoosterAddressRemoved(pool);\n }\n }\n }\n\n function manageBribes() external onlyOperator {\n uint256 length = POOLS.length;\n for (uint256 i = 0; i < length; i++) {\n address poolBoosterAddress = POOLS[i];\n\n // PoolBooster need to have a balance of at least 0.002 ether to operate\n // 0.001 ether are used for the bridge fee\n require(\n poolBoosterAddress.balance > 0.002 ether,\n \"Insufficient balance for bribes\"\n );\n\n require(\n safeContract.execTransactionFromModule(\n poolBoosterAddress,\n 0, // Value\n abi.encodeWithSelector(\n ICurvePoolBooster.manageNumberOfPeriods.selector,\n 1, // extraNumberOfPeriods\n 0.001 ether, // bridgeFee\n 1000000 // additionalGasLimit\n ),\n 0\n ),\n \"Manage number of periods failed\"\n );\n\n require(\n safeContract.execTransactionFromModule(\n poolBoosterAddress,\n 0, // Value\n abi.encodeWithSelector(\n ICurvePoolBooster.manageTotalRewardAmount.selector,\n 0.001 ether, // bridgeFee\n 1000000 // additionalGasLimit\n ),\n 0\n ),\n \"Manage total reward failed\"\n );\n }\n }\n}\n" + }, + "contracts/automation/EthereumBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractLZBridgeHelperModule, AbstractSafeModule } from \"./AbstractLZBridgeHelperModule.sol\";\nimport { AbstractCCIPBridgeHelperModule, IRouterClient } from \"./AbstractCCIPBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IOFT } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract EthereumBridgeHelperModule is\n AccessControlEnumerable,\n AbstractLZBridgeHelperModule,\n AbstractCCIPBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0x39254033945AA2E4809Cc2977E7087BEE48bd7Ab);\n IWETH9 public constant weth =\n IWETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);\n IERC20 public constant oeth =\n IERC20(0x856c4Efb76C1D1AE02e20CEB03A2A6a08b0b8dC3);\n IERC4626 public constant woeth =\n IERC4626(0xDcEe70654261AF21C44c093C300eD3Bb97b78192);\n\n uint32 public constant LZ_PLUME_ENDPOINT_ID = 30370;\n IOFT public constant LZ_WOETH_OMNICHAIN_ADAPTER =\n IOFT(0x7d1bEa5807e6af125826d56ff477745BB89972b8);\n IOFT public constant LZ_ETH_OMNICHAIN_ADAPTER =\n IOFT(0x77b2043768d28E9C9aB44E1aBfC95944bcE57931);\n\n IRouterClient public constant CCIP_ROUTER =\n IRouterClient(0x80226fc0Ee2b096224EeAc085Bb9a8cba1146f7D);\n\n uint64 public constant CCIP_BASE_CHAIN_SELECTOR = 15971525489660198786;\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Plume.\n * @param woethAmount Amount of wOETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWOETHToPlume(uint256 woethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_PLUME_ENDPOINT_ID,\n woeth,\n LZ_WOETH_OMNICHAIN_ADAPTER,\n woethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Bridges wOETH to Base using CCIP.\n * @param woethAmount Amount of wOETH to bridge.\n */\n function bridgeWOETHToBase(uint256 woethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_BASE_CHAIN_SELECTOR,\n woeth,\n woethAmount\n );\n }\n\n /**\n * @dev Bridges wETH to Plume.\n * @param wethAmount Amount of wETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWETHToPlume(uint256 wethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n // Unwrap into ETH\n safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(weth.withdraw.selector, wethAmount),\n 0 // Call\n );\n\n _bridgeTokenWithLz(\n LZ_PLUME_ENDPOINT_ID,\n IERC20(address(weth)),\n LZ_ETH_OMNICHAIN_ADAPTER,\n wethAmount,\n slippageBps,\n true\n );\n }\n\n /**\n * @dev Bridges wETH to Base using CCIP.\n * @param wethAmount Amount of wETH to bridge.\n */\n function bridgeWETHToBase(uint256 wethAmount) public payable onlyOperator {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_BASE_CHAIN_SELECTOR,\n IERC20(address(weth)),\n wethAmount\n );\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH.\n * @param wethAmount Amount of WETH to mint.\n * @param useNativeToken Whether to use native token to mint.\n * @return Amount of wOETH minted.\n */\n function mintAndWrap(uint256 wethAmount, bool useNativeToken)\n external\n onlyOperator\n returns (uint256)\n {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n return _mintAndWrap(wethAmount);\n }\n\n function wrapETH(uint256 ethAmount) public payable onlyOperator {\n // Deposit ETH into WETH\n safeContract.execTransactionFromModule(\n address(weth),\n ethAmount, // Value\n abi.encodeWithSelector(weth.deposit.selector),\n 0 // Call\n );\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH.\n * @param wethAmount Amount of WETH to mint.\n * @return Amount of wOETH minted.\n */\n function _mintAndWrap(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETH\");\n\n // Approve wOETH to move OETH\n success = safeContract.execTransactionFromModule(\n address(oeth),\n 0, // Value\n abi.encodeWithSelector(\n oeth.approve.selector,\n address(woeth),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETH\");\n\n uint256 woethAmount = woeth.balanceOf(address(safeContract));\n\n // Wrap OETH into wOETH\n success = safeContract.execTransactionFromModule(\n address(woeth),\n 0, // Value\n abi.encodeWithSelector(\n woeth.deposit.selector,\n wethAmount,\n address(safeContract)\n ),\n 0 // Call\n );\n require(success, \"Failed to wrap OETH\");\n\n // Compute amount of wOETH minted\n return woeth.balanceOf(address(safeContract)) - woethAmount;\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH, then bridges it to Plume.\n * @param wethAmount Amount of WETH to mint.\n * @param slippageBps Bridge slippage in 10^4 basis points.\n * @param useNativeToken Whether to use native token to mint.\n */\n function mintWrapAndBridgeToPlume(\n uint256 wethAmount,\n uint256 slippageBps,\n bool useNativeToken\n ) external payable onlyOperator {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n uint256 woethAmount = _mintAndWrap(wethAmount);\n bridgeWOETHToPlume(woethAmount, slippageBps);\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH, then bridges it to Base using CCIP.\n * @param wethAmount Amount of WETH to mint.\n * @param useNativeToken Whether to use native token to mint.\n */\n function mintWrapAndBridgeToBase(uint256 wethAmount, bool useNativeToken)\n external\n payable\n onlyOperator\n {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n uint256 woethAmount = _mintAndWrap(wethAmount);\n bridgeWOETHToBase(woethAmount);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH.\n * @param woethAmount Amount of wOETH to unwrap.\n * @return Amount of WETH received.\n */\n function unwrapAndRedeem(uint256 woethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _unwrapAndRedeem(woethAmount);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH.\n * @param woethAmount Amount of wOETH to unwrap.\n * @return Amount of WETH received.\n */\n function _unwrapAndRedeem(uint256 woethAmount) internal returns (uint256) {\n uint256 oethAmount = oeth.balanceOf(address(safeContract));\n\n // Unwrap wOETH\n bool success = safeContract.execTransactionFromModule(\n address(woeth),\n 0, // Value\n abi.encodeWithSelector(\n woeth.redeem.selector,\n woethAmount,\n address(safeContract),\n address(safeContract)\n ),\n 0 // Call\n );\n require(success, \"Failed to unwrap wOETH\");\n\n oethAmount = oeth.balanceOf(address(safeContract)) - oethAmount;\n\n // Redeem OETH using Vault to get WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethAmount,\n oethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETH\");\n\n return oethAmount;\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH, then bridges it to Plume.\n * @param woethAmount Amount of wOETH to unwrap.\n * @param slippageBps Bridge slippage in 10^4 basis points.\n */\n function unwrapRedeemAndBridgeToPlume(\n uint256 woethAmount,\n uint256 slippageBps\n ) external payable onlyOperator {\n uint256 wethAmount = _unwrapAndRedeem(woethAmount);\n // Unwrap into ETH\n safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(weth.withdraw.selector, wethAmount),\n 0 // Call\n );\n\n bridgeWETHToPlume(wethAmount, slippageBps);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH, then bridges it to Base using CCIP.\n * @param woethAmount Amount of wOETH to unwrap.\n */\n function unwrapRedeemAndBridgeToBase(uint256 woethAmount)\n external\n payable\n onlyOperator\n {\n uint256 wethAmount = _unwrapAndRedeem(woethAmount);\n bridgeWETHToBase(wethAmount);\n }\n}\n" + }, + "contracts/automation/PlumeBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { AbstractLZBridgeHelperModule } from \"./AbstractLZBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IOFT } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { BridgedWOETHStrategy } from \"../strategies/BridgedWOETHStrategy.sol\";\n\ncontract PlumeBridgeHelperModule is\n AccessControlEnumerable,\n AbstractLZBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0xc8c8F8bEA5631A8AF26440AF32a55002138cB76a);\n IWETH9 public constant weth =\n IWETH9(0xca59cA09E5602fAe8B629DeE83FfA819741f14be);\n IERC20 public constant oethp =\n IERC20(0xFCbe50DbE43bF7E5C88C6F6Fb9ef432D4165406E);\n IERC4626 public constant bridgedWOETH =\n IERC4626(0xD8724322f44E5c58D7A815F542036fb17DbbF839);\n\n uint32 public constant LZ_ETHEREUM_ENDPOINT_ID = 30101;\n IOFT public constant LZ_WOETH_OMNICHAIN_ADAPTER =\n IOFT(0x592CB6A596E7919930bF49a27AdAeCA7C055e4DB);\n IOFT public constant LZ_ETH_OMNICHAIN_ADAPTER =\n IOFT(0x4683CE822272CD66CEa73F5F1f9f5cBcaEF4F066);\n\n BridgedWOETHStrategy public constant bridgedWOETHStrategy =\n BridgedWOETHStrategy(0x1E3EdD5e019207D6355Ea77F724b1F1BF639B569);\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Ethereum.\n * @param woethAmount Amount of wOETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWOETHToEthereum(uint256 woethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_ETHEREUM_ENDPOINT_ID,\n IERC20(address(bridgedWOETH)),\n LZ_WOETH_OMNICHAIN_ADAPTER,\n woethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Bridges wETH to Ethereum.\n * @param wethAmount Amount of wETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWETHToEthereum(uint256 wethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_ETHEREUM_ENDPOINT_ID,\n IERC20(address(weth)),\n LZ_ETH_OMNICHAIN_ADAPTER,\n wethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem with Vault.\n * @return Amount of OETHp received.\n */\n function depositWOETH(uint256 woethAmount, bool redeemWithVault)\n external\n onlyOperator\n returns (uint256)\n {\n return _depositWOETH(woethAmount, redeemWithVault);\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy and bridges it to Ethereum.\n * @param woethAmount Amount of wOETH to deposit.\n * @param slippageBps Slippage in 10^4 basis points.\n * @return Amount of WETH received.\n */\n function depositWOETHAndBridgeWETH(uint256 woethAmount, uint256 slippageBps)\n external\n payable\n onlyOperator\n returns (uint256)\n {\n uint256 wethAmount = _depositWOETH(woethAmount, true);\n bridgeWETHToEthereum(wethAmount, slippageBps);\n return wethAmount;\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem with Vault.\n * @return Amount of OETHp received.\n */\n function _depositWOETH(uint256 woethAmount, bool redeemWithVault)\n internal\n returns (uint256)\n {\n // Update oracle price\n bridgedWOETHStrategy.updateWOETHOraclePrice();\n\n // Rebase to account for any yields from price update\n vault.rebase();\n\n uint256 oethpAmount = oethp.balanceOf(address(safeContract));\n\n // Approve bridgedWOETH strategy to move wOETH\n bool success = safeContract.execTransactionFromModule(\n address(bridgedWOETH),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETH.approve.selector,\n address(bridgedWOETHStrategy),\n woethAmount\n ),\n 0 // Call\n );\n\n // Deposit to bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.depositBridgedWOETH.selector,\n woethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to deposit bridged WOETH\");\n\n oethpAmount = oethp.balanceOf(address(safeContract)) - oethpAmount;\n\n // Rebase to account for any yields from price update\n // and backing asset change from deposit\n vault.rebase();\n\n if (!redeemWithVault) {\n return oethpAmount;\n }\n\n // Redeem for WETH using Vault\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethpAmount,\n oethpAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETHp\");\n\n return oethpAmount;\n }\n\n /**\n * @dev Deposits wETH into the vault.\n * @param wethAmount Amount of wETH to deposit.\n * @return Amount of OETHp received.\n */\n function depositWETHAndRedeemWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _withdrawWOETH(wethAmount);\n }\n\n /**\n * @dev Deposits wETH into the vault and bridges it to Ethereum.\n * @param wethAmount Amount of wETH to deposit.\n * @param slippageBps Slippage in 10^4 basis points.\n * @return Amount of WOETH received.\n */\n function depositWETHAndBridgeWOETH(uint256 wethAmount, uint256 slippageBps)\n external\n payable\n onlyOperator\n returns (uint256)\n {\n uint256 woethAmount = _withdrawWOETH(wethAmount);\n bridgeWOETHToEthereum(woethAmount, slippageBps);\n return woethAmount;\n }\n\n /**\n * @dev Withdraws wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to use to withdraw.\n * @return Amount of wOETH received.\n */\n function _withdrawWOETH(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETHp with WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETHp\");\n\n // Approve bridgedWOETH strategy to move OETHp\n success = safeContract.execTransactionFromModule(\n address(oethp),\n 0, // Value\n abi.encodeWithSelector(\n oethp.approve.selector,\n address(bridgedWOETHStrategy),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETHp\");\n\n uint256 woethAmount = bridgedWOETH.balanceOf(address(safeContract));\n\n // Withdraw from bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.withdrawBridgedWOETH.selector,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to withdraw bridged WOETH\");\n\n woethAmount =\n bridgedWOETH.balanceOf(address(safeContract)) -\n woethAmount;\n\n return woethAmount;\n }\n}\n" + }, + "contracts/beacon/BeaconConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to request validator consolidation on the beacon chain.\n * @author Origin Protocol Inc\n */\nlibrary BeaconConsolidation {\n /// @notice The address the validator consolidation requests are sent\n /// See https://eips.ethereum.org/EIPS/eip-7251\n address internal constant CONSOLIDATION_REQUEST_ADDRESS =\n 0x0000BBdDc7CE488642fb579F8B00f3a590007251;\n\n function request(bytes calldata source, bytes calldata target)\n internal\n returns (uint256 fee_)\n {\n require(source.length == 48, \"Invalid source byte length\");\n require(target.length == 48, \"Invalid target byte length\");\n\n fee_ = fee();\n\n // Call the Consolidation Request contract with the public keys of the source and target\n // validators packed together.\n // This does not have a function signature, so we use a call\n (bool success, ) = CONSOLIDATION_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(source, target)\n );\n\n require(success, \"Consolidation request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the consolidation request contract\n (bool success, bytes memory result) = CONSOLIDATION_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/beacon/BeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"./BeaconProofsLib.sol\";\nimport { IBeaconProofs } from \"../interfaces/IBeaconProofs.sol\";\n\n/**\n * @title Verifies merkle proofs of beacon chain data.\n * @author Origin Protocol Inc\n */\ncontract BeaconProofs is IBeaconProofs {\n /// @notice Verifies the validator index is for the given validator public key.\n /// Also verify the validator's withdrawal credential points to the withdrawal address.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param proof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata proof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view {\n BeaconProofsLib.verifyValidator(\n beaconBlockRoot,\n pubKeyHash,\n proof,\n validatorIndex,\n withdrawalAddress\n );\n }\n\n /// @notice Verifies a validator's withdrawable epoch to the beacon block root\n /// for a given validator index.\n /// Also verifies the validator's public key for the given validator index.\n /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n /// @param beaconBlockRoot The root of the beacon block\n /// @param validatorIndex The validator index to verify the withdrawable epoch for.\n /// @param withdrawableEpoch The withdrawable epoch to verify in big endian uint64 format\n /// @param withdrawableEpochProof The merkle proof for the validator's withdrawable epoch to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorPubKeyProof The merkle proof for the validator public key in a sub tree of height two.\n /// This is 2 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof,\n bytes calldata validatorPubKeyProof\n ) external view {\n BeaconProofsLib.verifyValidatorWithdrawableEpoch(\n beaconBlockRoot,\n validatorIndex,\n withdrawableEpoch,\n withdrawableEpochProof\n );\n\n // Get the third 32 byte witness from the withdrawable epoch proof\n // 2 * 32 bytes = 64 bytes offset\n bytes32 subTreeRoot = bytes32(withdrawableEpochProof[64:96]);\n\n BeaconProofsLib.verifyValidatorPubKeySubTree(\n subTreeRoot,\n pubKeyHash,\n validatorPubKeyProof\n );\n }\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint64 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view {\n BeaconProofsLib.verifyValidatorWithdrawableEpoch(\n beaconBlockRoot,\n validatorIndex,\n withdrawableEpoch,\n withdrawableEpochProof\n );\n }\n\n /// @notice Verifies the balances container to the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerRoot The merkle root of the the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata balancesContainerProof\n ) external view {\n BeaconProofsLib.verifyBalancesContainer(\n beaconBlockRoot,\n balancesContainerRoot,\n balancesContainerProof\n );\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param balanceProof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalanceGwei) {\n validatorBalanceGwei = BeaconProofsLib.verifyValidatorBalance(\n balancesContainerRoot,\n validatorBalanceLeaf,\n balanceProof,\n validatorIndex\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the pubKey and slot of the first pending deposit to the beacon block root.\n /// BeaconBlock.state.PendingDeposits[0].pubKey\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero would be a good choice.\n /// @param pubKeyHash The hash of the validator public key for the first pending deposit.\n /// Use zero bytes if the deposit queue is empty.\n /// @param firstPendingDepositProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].pubKey when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes32 pubKeyHash,\n bytes calldata firstPendingDepositProof\n ) external view returns (bool isEmptyDepositQueue) {\n isEmptyDepositQueue = BeaconProofsLib.verifyFirstPendingDeposit(\n beaconBlockRoot,\n slot,\n pubKeyHash,\n firstPendingDepositProof\n );\n }\n}\n" + }, + "contracts/beacon/BeaconProofsLib.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Merkle } from \"./Merkle.sol\";\nimport { Endian } from \"./Endian.sol\";\n\n/**\n * @title Library to verify merkle proofs of beacon chain data.\n * @author Origin Protocol Inc\n */\nlibrary BeaconProofsLib {\n // Known generalized indices in the beacon block\n /// @dev BeaconBlock.state.PendingDeposits[0]\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, pending deposits at index 34\n /// Pending deposits container: height 28, first deposit at index 0\n /// ((2 ^ 3 + 3) * 2 ^ 6 + 34) * 2 ^ 28 + 0 = 198105366528\n uint256 internal constant FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX =\n 198105366528;\n /// @dev BeaconBlock.state.PendingDeposits[0].pubkey\n /// Pending Deposit container: height 3, pubkey at index 0\n /// (((2 ^ 3 + 3) * 2 ^ 6 + 34) * 2 ^ 28 + 0) * 2 ^ 3 + 0 = 1584842932224\n uint256 internal constant FIRST_PENDING_DEPOSIT_PUBKEY_GENERALIZED_INDEX =\n 1584842932224;\n /// @dev BeaconBlock.state.validators\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, validators at index 11\n /// (2 ^ 3 + 3) * 2 ^ 6 + 11 = 715\n uint256 internal constant VALIDATORS_CONTAINER_GENERALIZED_INDEX = 715;\n /// @dev BeaconBlock.state.balances\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, balances at index 13\n /// (2 ^ 3 + 3) * 2 ^ 6 + 13 = 716\n uint256 internal constant BALANCES_CONTAINER_GENERALIZED_INDEX = 716;\n\n /// @dev Number of bytes in the proof to the first pending deposit.\n /// 37 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0]\n /// 37 * 32 bytes = 1184 bytes\n uint256 internal constant FIRST_PENDING_DEPOSIT_PROOF_LENGTH = 1184;\n /// @dev Number of bytes in the proof from the pubKey of the first pending deposit to the beacon block root.\n /// 40 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0].pubKey\n /// 40 * 32 bytes = 1280 bytes\n uint256 internal constant FIRST_PENDING_DEPOSIT_PUBKEY_PROOF_LENGTH = 1280;\n /// The slot is at index 4 in the Pending Deposits container.\n /// The sub tree from the right node from the root is a tree of height 2.\n /// The first 32 bytes witness is an empty bytes32 as there are\n /// no items after the slot in the Pending Deposits container.\n /// The second 32 bytes witness is a hash or two empty bytes32.\n bytes internal constant PENDING_DEPOSIT_SLOT_PROOF =\n // solhint-disable-next-line max-line-length\n hex\"0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b\";\n\n /// @dev Merkle height of the Balances container\n /// BeaconBlock.state.balances\n uint256 internal constant BALANCES_HEIGHT = 39;\n /// @dev Merkle height of the Validators container list\n /// BeaconBlock.state.validators\n uint256 internal constant VALIDATORS_LIST_HEIGHT = 41;\n /// @dev Merkle height of the Validator container\n /// BeaconBlock.state.validators[validatorIndex]\n uint256 internal constant VALIDATOR_CONTAINER_HEIGHT = 3;\n\n /// @dev Position of the pubkey field in the Validator container.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;\n /// @dev Position of the withdrawable epoch field in the Validator container.\n /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n uint256 internal constant VALIDATOR_WITHDRAWABLE_EPOCH_INDEX = 7;\n\n /// @notice Verifies the validator index is for the given validator public key.\n /// Also verify the validator's withdrawal credential points to the withdrawal address.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param proof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata proof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.validators[validatorIndex]\n uint256 generalizedIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_LIST_HEIGHT,\n validatorIndex\n );\n // BeaconBlock.state.validators[validatorIndex].pubkey\n generalizedIndex = concatGenIndices(\n generalizedIndex,\n VALIDATOR_CONTAINER_HEIGHT,\n VALIDATOR_PUBKEY_INDEX\n );\n\n // Get the withdrawal address from the first witness in the pubkey merkle proof.\n address withdrawalAddressFromProof;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // The first 32 bytes of the proof is the withdrawal credential so load it into memory.\n calldatacopy(0, proof.offset, 32)\n // Cast the 32 bytes in memory to an address which is the last 20 bytes.\n withdrawalAddressFromProof := mload(0)\n }\n require(\n withdrawalAddressFromProof == withdrawalAddress,\n \"Invalid withdrawal address\"\n );\n\n require(\n // 53 * 32 bytes = 1696 bytes\n proof.length == 1696 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: pubKeyHash,\n index: generalizedIndex\n }),\n \"Invalid validator proof\"\n );\n }\n\n /// @notice Verifies a validator's withdrawable epoch to the beacon block root\n /// for a given validator index.\n /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n /// @param beaconBlockRoot The root of the beacon block\n /// @param validatorIndex The validator index to verify the withdrawable epoch for.\n /// @param withdrawableEpoch The withdrawable epoch to verify in big endian uint64 format\n /// @param proof The merkle proof for the validator's withdrawable epoch to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyValidatorWithdrawableEpoch(\n bytes32 beaconBlockRoot,\n uint64 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata proof\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.validators[validatorIndex]\n uint256 exitEpochGenIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_LIST_HEIGHT,\n validatorIndex\n );\n // BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n exitEpochGenIndex = concatGenIndices(\n exitEpochGenIndex,\n VALIDATOR_CONTAINER_HEIGHT,\n VALIDATOR_WITHDRAWABLE_EPOCH_INDEX\n );\n\n require(\n // 53 * 32 bytes = 1696 bytes\n proof.length == 1696 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: Endian.toLittleEndianUint64(withdrawableEpoch),\n index: exitEpochGenIndex\n }),\n \"Invalid withdrawable proof\"\n );\n }\n\n /// @param subTreeRoot The third 32 byte witness from the withdrawable epoch proof\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param proof The merkle proof for the validator public key in a sub tree of height two.\n /// This is 2 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyValidatorPubKeySubTree(\n bytes32 subTreeRoot,\n bytes32 pubKeyHash,\n bytes calldata proof\n ) internal view {\n // Tree height 2 and pub key is at index 0\n // index = 2 ^ 2 + 0 = 4\n require(\n // 2 * 32 bytes = 64 bytes\n proof.length == 64 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: subTreeRoot,\n leaf: pubKeyHash,\n index: 4\n }),\n \"Invalid pub key proof\"\n );\n }\n\n /// @notice Verifies the balances container to the beacon block root.\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param balancesContainerRoot The merkle root of the the balances container.\n /// @param proof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata proof\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.balances\n require(\n // 9 * 32 bytes = 288 bytes\n proof.length == 288 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: balancesContainerRoot,\n index: BALANCES_CONTAINER_GENERALIZED_INDEX\n }),\n \"Invalid balance container proof\"\n );\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param proof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for.\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index.\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata proof,\n uint64 validatorIndex\n ) internal view returns (uint256 validatorBalanceGwei) {\n require(balancesContainerRoot != bytes32(0), \"Invalid container root\");\n\n // Four balances are stored in each leaf so the validator index is divided by 4\n uint64 balanceIndex = validatorIndex / 4;\n\n // Get the index within the balances container, not the Beacon Block\n // BeaconBlock.state.balances[balanceIndex]\n uint256 generalizedIndex = concatGenIndices(\n 1,\n BALANCES_HEIGHT,\n balanceIndex\n );\n\n validatorBalanceGwei = balanceAtIndex(\n validatorBalanceLeaf,\n validatorIndex\n );\n\n require(\n // 39 * 32 bytes = 1248 bytes\n proof.length == 1248 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: balancesContainerRoot,\n leaf: validatorBalanceLeaf,\n index: generalizedIndex\n }),\n \"Invalid balance proof\"\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the pubKey and slot of the first pending deposit to the beacon block root.\n /// BeaconBlock.state.pendingDeposits[0].pubKey\n /// BeaconBlock.state.pendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty, but zero would be a good choice.\n /// @param pubKeyHash The hash of the validator public key for the first pending deposit.\n /// Use zero bytes if the deposit queue is empty.\n /// @param proof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].pubKey when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes32 pubKeyHash,\n bytes calldata proof\n ) internal view returns (bool isEmptyDepositQueue) {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // If the deposit queue is empty\n if (proof.length == FIRST_PENDING_DEPOSIT_PROOF_LENGTH) {\n require(\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: bytes32(0),\n index: FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX\n }),\n \"Invalid empty deposits proof\"\n );\n return true;\n }\n\n // Verify the public key of the first pending deposit\n // BeaconBlock.state.PendingDeposits[0].pubKey\n require(\n proof.length == FIRST_PENDING_DEPOSIT_PUBKEY_PROOF_LENGTH &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: pubKeyHash,\n index: FIRST_PENDING_DEPOSIT_PUBKEY_GENERALIZED_INDEX\n }),\n \"Invalid deposit pub key proof\"\n );\n\n // Now verify the slot of the first pending deposit\n\n // Get the third 32 bytes witness from the first pending deposit pubKey proof\n // 2 * 32 bytes = 64 bytes offset\n bytes32 slotRoot = bytes32(proof[64:96]);\n\n // Sub tree height 2 and slot is at index 0 in the sub tree\n // index = 2 ^ 2 + 0 = 4\n require(\n Merkle.verifyInclusionSha256({\n proof: PENDING_DEPOSIT_SLOT_PROOF,\n root: slotRoot,\n leaf: Endian.toLittleEndianUint64(slot),\n index: 4\n }),\n \"Invalid deposit slot\"\n );\n }\n\n ////////////////////////////////////////////////////\n /// Internal Helper Functions\n ////////////////////////////////////////////////////\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n internal\n pure\n returns (uint256)\n {\n uint256 bitShiftAmount = (validatorIndex % 4) * 64;\n return\n Endian.fromLittleEndianUint64(\n bytes32((uint256(validatorBalanceLeaf) << bitShiftAmount))\n );\n }\n\n /// @notice Concatenates two beacon chain generalized indices into one.\n /// @param genIndex The first generalized index or 1 if calculating for a single container.\n /// @param height The merkle tree height of the second container. eg 39 for balances, 41 for validators.\n /// @param index The index within the second container. eg the validator index.\n /// @return genIndex The concatenated generalized index.\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) internal pure returns (uint256) {\n return (genIndex << height) | index;\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to retrieve beacon block roots.\n * @author Origin Protocol Inc\n */\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the beacon block root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Roots contract to get the parent block root.\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/Endian.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n/**\n * @title Library to handle conversion between little-endian and big-endian formats.\n * @author Origin Protocol Inc\n */\nlibrary Endian {\n /**\n * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64\n * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type\n * @return n The big endian-formatted uint64\n * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits),\n * but it is immediately truncated to a uint64 (i.e. 64 bits)\n * through a right-shift/shr operation.\n */\n function fromLittleEndianUint64(bytes32 lenum)\n internal\n pure\n returns (uint64 n)\n {\n // the number needs to be stored in little-endian encoding (ie in bytes 0-8)\n n = uint64(uint256(lenum >> 192));\n // forgefmt: disable-next-item\n return\n (n >> 56) |\n ((0x00FF000000000000 & n) >> 40) |\n ((0x0000FF0000000000 & n) >> 24) |\n ((0x000000FF00000000 & n) >> 8) |\n ((0x00000000FF000000 & n) << 8) |\n ((0x0000000000FF0000 & n) << 24) |\n ((0x000000000000FF00 & n) << 40) |\n ((0x00000000000000FF & n) << 56);\n }\n\n function toLittleEndianUint64(uint64 benum)\n internal\n pure\n returns (bytes32 n)\n {\n // Convert to little-endian by reversing byte order\n uint64 reversed = (benum >> 56) |\n ((0x00FF000000000000 & benum) >> 40) |\n ((0x0000FF0000000000 & benum) >> 24) |\n ((0x000000FF00000000 & benum) >> 8) |\n ((0x00000000FF000000 & benum) << 8) |\n ((0x0000000000FF0000 & benum) << 24) |\n ((0x000000000000FF00 & benum) << 40) |\n ((0x00000000000000FF & benum) << 56);\n\n // Store the little-endian uint64 in the least significant 64 bits of bytes32\n n = bytes32(uint256(reversed));\n // Shift to most significant bits\n n = n << 192;\n }\n}\n" + }, + "contracts/beacon/Merkle.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev These functions deal with verification of Merkle Tree proofs.\n *\n * The tree and the proofs can be generated using our\n * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].\n * You will find a quickstart guide in the readme.\n *\n * WARNING: You should avoid using leaf values that are 64 bytes long prior to\n * hashing, or use a hash function other than keccak256 for hashing leaves.\n * This is because the concatenation of a sorted pair of internal nodes in\n * the merkle tree could be reinterpreted as a leaf value.\n * OpenZeppelin's JavaScript library generates merkle trees that are safe\n * against this attack out of the box.\n */\nlibrary Merkle {\n error InvalidProofLength();\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function verifyInclusionSha256(\n bytes memory proof,\n bytes32 root,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bool) {\n return processInclusionProofSha256(proof, leaf, index) == root;\n }\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * _Available since v4.4._\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function processInclusionProofSha256(\n bytes memory proof,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bytes32) {\n require(\n proof.length != 0 && proof.length % 32 == 0,\n InvalidProofLength()\n );\n bytes32[1] memory computedHash = [leaf];\n for (uint256 i = 32; i <= proof.length; i += 32) {\n if (index % 2 == 0) {\n // if ith bit of index is 0, then computedHash is a left sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(computedHash))\n mstore(0x20, mload(add(proof, i)))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n }\n } else {\n // if ith bit of index is 1, then computedHash is a right sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(add(proof, i)))\n mstore(0x20, mload(computedHash))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n }\n }\n index = index / 2;\n }\n return computedHash[0];\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to request full or partial withdrawals from validators on the beacon chain.\n * @author Origin Protocol Inc\n */\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n /// @notice Requests a partial withdrawal for a given validator public key and amount.\n /// @param validatorPubKey The public key of the validator to withdraw from\n /// @param amount The amount of ETH to withdraw\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n /// @notice Gets fee for withdrawal requests contract on Beacon chain\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/bridges/OmnichainL2Adapter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.0;\n\nimport { MintBurnOFTAdapter } from \"@layerzerolabs/oft-evm/contracts/MintBurnOFTAdapter.sol\";\nimport { IMintableBurnable } from \"@layerzerolabs/oft-evm/contracts/interfaces/IMintableBurnable.sol\";\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\n\n/// NOTE: It's necessary to inherit from Ownable instead of Governable\n/// because OFTCore uses Ownable to manage the governor.\n/// Ownable uses slot 0 for storing the address, whereas Goveranble\n/// stores it in a computed slot.\n\n/// @notice Omnichain uses a deployed ERC-20 token and safeERC20\n/// to interact with the OFTCore contract.\n/// On L2, we follow the mint and burn mechanism.\n/// The adapter should have minter and burner roles.\n\n/// @title Omnichain L2 Adapter\ncontract OmnichainL2Adapter is MintBurnOFTAdapter {\n constructor(\n address _token,\n address _lzEndpoint,\n address _governor\n )\n MintBurnOFTAdapter(\n _token,\n IMintableBurnable(_token),\n _lzEndpoint,\n _governor\n )\n Ownable()\n {\n _transferOwnership(_governor);\n }\n\n /// @inheritdoc MintBurnOFTAdapter\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n )\n internal\n virtual\n override\n returns (uint256 amountSentLD, uint256 amountReceivedLD)\n {\n (amountSentLD, amountReceivedLD) = _debitView(\n _amountLD,\n _minAmountLD,\n _dstEid\n );\n // Burns tokens from the caller.\n IMintableERC20(address(minterBurner)).burn(_from, amountSentLD);\n }\n\n /// @inheritdoc MintBurnOFTAdapter\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /* _srcEid */\n ) internal virtual override returns (uint256 amountReceivedLD) {\n if (_to == address(0x0)) _to = address(0xdead); // _mint(...) does not support address(0x0)\n // Mints the tokens and transfers to the recipient.\n IMintableERC20(address(minterBurner)).mint(_to, _amountLD);\n // In the case of NON-default OFTAdapter, the amountLD MIGHT not be equal to amountReceivedLD.\n return _amountLD;\n }\n}\n\ninterface IMintableERC20 {\n function mint(address to, uint256 value) external;\n\n function burn(address to, uint256 value) external;\n}\n" + }, + "contracts/bridges/OmnichainMainnetAdapter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.0;\n\nimport { OFTAdapter } from \"@layerzerolabs/oft-evm/contracts/OFTAdapter.sol\";\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\n\n/// NOTE: It's necessary to inherit from Ownable instead of Governable\n/// because OFTCore uses Ownable to manage the governor.\n/// Ownable uses slot 0 for storing the address, whereas Goveranble\n/// stores it in a computed slot.\n\n/// @notice Omnichain uses a deployed ERC-20 token and safeERC20\n/// to interact with the OFTCore contract.\n/// On Ethereum, we follow the lock and unlock mechanism.\n\n/// @title Omnichain Mainnet Adapter\ncontract OmnichainMainnetAdapter is OFTAdapter {\n constructor(\n address _token,\n address _lzEndpoint,\n address _governor\n ) OFTAdapter(_token, _lzEndpoint, _governor) Ownable() {\n _transferOwnership(_governor);\n }\n}\n" + }, + "contracts/buyback/AbstractBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { ICVXLocker } from \"../interfaces/ICVXLocker.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\n\nabstract contract AbstractBuyback is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n event SwapRouterUpdated(address indexed _address);\n\n event RewardsSourceUpdated(address indexed _address);\n event TreasuryManagerUpdated(address indexed _address);\n event CVXShareBpsUpdated(uint256 bps);\n\n // Emitted whenever OUSD/OETH is swapped for OGN/CVX or any other token\n event OTokenBuyback(\n address indexed oToken,\n address indexed swappedFor,\n uint256 swapAmountIn,\n uint256 amountOut\n );\n\n // Address of 1-inch Swap Router\n address public swapRouter;\n\n // slither-disable-next-line constable-states\n address private __deprecated_ousd;\n // slither-disable-next-line constable-states\n address private __deprecated_ogv;\n // slither-disable-next-line constable-states\n address private __deprecated_usdt;\n // slither-disable-next-line constable-states\n address private __deprecated_weth9;\n\n // Address that receives OGN after swaps\n address public rewardsSource;\n\n // Address that receives all other tokens after swaps\n address public treasuryManager;\n\n // slither-disable-next-line constable-states\n uint256 private __deprecated_treasuryBps;\n\n address public immutable oToken;\n address public immutable ogn;\n address public immutable cvx;\n address public immutable cvxLocker;\n\n // Amount of `oToken` balance to use for OGN buyback\n uint256 public balanceForOGN;\n\n // Amount of `oToken` balance to use for CVX buyback\n uint256 public balanceForCVX;\n\n // Percentage of `oToken` balance to be used for CVX\n uint256 public cvxShareBps; // 10000 = 100%\n\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) {\n // Make sure nobody owns the implementation contract\n _setGovernor(address(0));\n\n oToken = _oToken;\n ogn = _ogn;\n cvx = _cvx;\n cvxLocker = _cvxLocker;\n }\n\n /**\n * @param _swapRouter Address of Uniswap V3 Router\n * @param _strategistAddr Address of Strategist multi-sig wallet\n * @param _treasuryManagerAddr Address that receives the treasury's share of OUSD\n * @param _rewardsSource Address of RewardsSource contract\n * @param _cvxShareBps Percentage of balance to use for CVX\n */\n function initialize(\n address _swapRouter,\n address _strategistAddr,\n address _treasuryManagerAddr,\n address _rewardsSource,\n uint256 _cvxShareBps\n ) external onlyGovernor initializer {\n _setStrategistAddr(_strategistAddr);\n\n _setSwapRouter(_swapRouter);\n _setRewardsSource(_rewardsSource);\n\n _setTreasuryManager(_treasuryManagerAddr);\n\n _setCVXShareBps(_cvxShareBps);\n }\n\n /**\n * @dev Set address of Uniswap Universal Router for performing liquidation\n * of platform fee tokens. Setting to 0x0 will pause swaps.\n *\n * @param _router Address of the Uniswap Universal router\n */\n function setSwapRouter(address _router) external onlyGovernor {\n _setSwapRouter(_router);\n }\n\n function _setSwapRouter(address _router) internal {\n address oldRouter = swapRouter;\n swapRouter = _router;\n\n if (oldRouter != address(0)) {\n // Remove allowance of old router, if any\n\n if (IERC20(ogn).allowance(address(this), oldRouter) != 0) {\n // slither-disable-next-line unused-return\n IERC20(ogn).safeApprove(oldRouter, 0);\n }\n\n if (IERC20(cvx).allowance(address(this), oldRouter) != 0) {\n // slither-disable-next-line unused-return\n IERC20(cvx).safeApprove(oldRouter, 0);\n }\n }\n\n emit SwapRouterUpdated(_router);\n }\n\n /**\n * @dev Sets the address that receives the OGN buyback rewards\n * @param _address Address\n */\n function setRewardsSource(address _address) external onlyGovernor {\n _setRewardsSource(_address);\n }\n\n function _setRewardsSource(address _address) internal {\n require(_address != address(0), \"Address not set\");\n rewardsSource = _address;\n emit RewardsSourceUpdated(_address);\n }\n\n /**\n * @dev Sets the address that can receive and manage the funds for Treasury\n * @param _address Address\n */\n function setTreasuryManager(address _address) external onlyGovernor {\n _setTreasuryManager(_address);\n }\n\n function _setTreasuryManager(address _address) internal {\n require(_address != address(0), \"Address not set\");\n treasuryManager = _address;\n emit TreasuryManagerUpdated(_address);\n }\n\n /**\n * @dev Sets the percentage of oToken to use for Flywheel tokens\n * @param _bps BPS, 10000 to 100%\n */\n function setCVXShareBps(uint256 _bps) external onlyGovernor {\n _setCVXShareBps(_bps);\n }\n\n function _setCVXShareBps(uint256 _bps) internal {\n require(_bps <= 10000, \"Invalid bps value\");\n cvxShareBps = _bps;\n emit CVXShareBpsUpdated(_bps);\n }\n\n /**\n * @dev Computes the split of oToken balance that can be\n * used for OGN and CVX buybacks.\n */\n function _updateBuybackSplits()\n internal\n returns (uint256 _balanceForOGN, uint256 _balanceForCVX)\n {\n _balanceForOGN = balanceForOGN;\n _balanceForCVX = balanceForCVX;\n\n uint256 totalBalance = IERC20(oToken).balanceOf(address(this));\n uint256 unsplitBalance = totalBalance - _balanceForOGN - _balanceForCVX;\n\n // Check if all balance is accounted for\n if (unsplitBalance != 0) {\n // If not, split unaccounted balance based on `cvxShareBps`\n uint256 addToCVX = (unsplitBalance * cvxShareBps) / 10000;\n _balanceForCVX = _balanceForCVX + addToCVX;\n _balanceForOGN = _balanceForOGN + unsplitBalance - addToCVX;\n\n // Update storage\n balanceForOGN = _balanceForOGN;\n balanceForCVX = _balanceForCVX;\n }\n }\n\n function updateBuybackSplits() external onlyGovernor {\n // slither-disable-next-line unused-return\n _updateBuybackSplits();\n }\n\n function _swapToken(\n address tokenOut,\n uint256 oTokenAmount,\n uint256 minAmountOut,\n bytes calldata swapData\n ) internal returns (uint256 amountOut) {\n require(oTokenAmount > 0, \"Invalid Swap Amount\");\n require(swapRouter != address(0), \"Swap Router not set\");\n require(minAmountOut > 0, \"Invalid minAmount\");\n\n // Transfer OToken to Swapper for swapping\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(oToken).transfer(swapRouter, oTokenAmount);\n\n // Swap\n amountOut = ISwapper(swapRouter).swap(\n oToken,\n tokenOut,\n oTokenAmount,\n minAmountOut,\n swapData\n );\n\n require(amountOut >= minAmountOut, \"Higher Slippage\");\n\n emit OTokenBuyback(oToken, tokenOut, oTokenAmount, amountOut);\n }\n\n /**\n * @dev Swaps `oTokenAmount` to OGN\n * @param oTokenAmount Amount of OUSD/OETH to swap\n * @param minOGN Minimum OGN to receive for oTokenAmount\n * @param swapData 1inch Swap Data\n */\n function swapForOGN(\n uint256 oTokenAmount,\n uint256 minOGN,\n bytes calldata swapData\n ) external onlyGovernorOrStrategist nonReentrant {\n (uint256 _amountForOGN, ) = _updateBuybackSplits();\n require(_amountForOGN >= oTokenAmount, \"Balance underflow\");\n require(rewardsSource != address(0), \"RewardsSource contract not set\");\n\n unchecked {\n // Subtract the amount to swap from net balance\n balanceForOGN = _amountForOGN - oTokenAmount;\n }\n\n uint256 ognReceived = _swapToken(ogn, oTokenAmount, minOGN, swapData);\n\n // Transfer OGN received to RewardsSource contract\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(ogn).transfer(rewardsSource, ognReceived);\n }\n\n /**\n * @dev Swaps `oTokenAmount` to CVX\n * @param oTokenAmount Amount of OUSD/OETH to swap\n * @param minCVX Minimum CVX to receive for oTokenAmount\n * @param swapData 1inch Swap Data\n */\n function swapForCVX(\n uint256 oTokenAmount,\n uint256 minCVX,\n bytes calldata swapData\n ) external onlyGovernorOrStrategist nonReentrant {\n (, uint256 _amountForCVX) = _updateBuybackSplits();\n require(_amountForCVX >= oTokenAmount, \"Balance underflow\");\n\n unchecked {\n // Subtract the amount to swap from net balance\n balanceForCVX = _amountForCVX - oTokenAmount;\n }\n\n uint256 cvxReceived = _swapToken(cvx, oTokenAmount, minCVX, swapData);\n\n // Lock all CVX\n _lockAllCVX(cvxReceived);\n }\n\n /**\n * @dev Locks all CVX held by the contract on behalf of the Treasury Manager\n */\n function lockAllCVX() external onlyGovernorOrStrategist {\n _lockAllCVX(IERC20(cvx).balanceOf(address(this)));\n }\n\n function _lockAllCVX(uint256 cvxAmount) internal {\n require(\n treasuryManager != address(0),\n \"Treasury manager address not set\"\n );\n\n // Lock all available CVX on behalf of `treasuryManager`\n ICVXLocker(cvxLocker).lock(treasuryManager, cvxAmount, 0);\n }\n\n /**\n * @dev Approve CVX Locker to move CVX held by this contract\n */\n function safeApproveAllTokens() external onlyGovernorOrStrategist {\n IERC20(cvx).safeApprove(cvxLocker, type(uint256).max);\n }\n\n /**\n * @notice Owner function to withdraw a specific amount of a token\n * @param token token to be transferered\n * @param amount amount of the token to be transferred\n */\n function transferToken(address token, uint256 amount)\n external\n onlyGovernor\n nonReentrant\n {\n IERC20(token).safeTransfer(_governor(), amount);\n }\n}\n" + }, + "contracts/buyback/ARMBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract ARMBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/buyback/OETHBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract OETHBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/buyback/OUSDBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract OUSDBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/echidna/Debugger.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary Debugger {\n event Debug(string debugString);\n event Debug(string description, string data);\n event Debug(string prefix, string description, string data);\n event Debug(string description, bytes32 data);\n event Debug(string prefix, string description, bytes32 data);\n event Debug(string description, uint256 data);\n event Debug(string prefix, string description, uint256 data);\n event Debug(string description, int256 data);\n event Debug(string prefix, string description, int256 data);\n event Debug(string description, address data);\n event Debug(string prefix, string description, address data);\n event Debug(string description, bool data);\n event Debug(string prefix, string description, bool data);\n\n function log(string memory debugString) internal {\n emit Debug(debugString);\n }\n\n function log(string memory description, string memory data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n string memory data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, bytes32 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n bytes32 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, uint256 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n uint256 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, int256 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n int256 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, address data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n address data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, bool data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n bool data\n ) internal {\n emit Debug(prefix, description, data);\n }\n}\n" + }, + "contracts/echidna/Echidna.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaTestApproval.sol\";\n\n/**\n * @title Echidna test contract for OUSD\n * @notice Target contract to be tested, containing all mixins\n * @author Rappie\n */\ncontract Echidna is EchidnaTestApproval {\n\n}\n" + }, + "contracts/echidna/EchidnaConfig.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Top-level mixin for configuring the desired fuzzing setup\n * @author Rappie\n */\ncontract EchidnaConfig {\n address internal constant ADDRESS_VAULT = address(0x10000);\n address internal constant ADDRESS_OUTSIDER_USER = address(0x20000);\n\n address internal constant ADDRESS_USER0 = address(0x30000);\n address internal constant ADDRESS_USER1 = address(0x40000);\n\n // Will be set in EchidnaSetup constructor\n address internal ADDRESS_OUTSIDER_CONTRACT;\n address internal ADDRESS_CONTRACT0;\n address internal ADDRESS_CONTRACT1;\n\n // Toggle known issues\n //\n // This can be used to skip tests that are known to fail. This is useful\n // when debugging a specific issue, but should be disabled when running\n // the full test suite.\n //\n // True => skip tests that are known to fail\n // False => run all tests\n //\n bool internal constant TOGGLE_KNOWN_ISSUES = false;\n\n // Toggle known issues within limits\n //\n // Same as TOGGLE_KNOWN_ISSUES, but also skip tests that are known to fail\n // within limits set by the variables below.\n //\n bool internal constant TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS = true;\n\n // Starting balance\n //\n // Gives OUSD a non-zero starting supply, which can be useful to ignore\n // certain edge cases.\n //\n // The starting balance is given to outsider accounts that are not used as\n // accounts while fuzzing.\n //\n bool internal constant TOGGLE_STARTING_BALANCE = true;\n uint256 internal constant STARTING_BALANCE = 1_000_000e18;\n\n // Change supply\n //\n // Set a limit to the amount of change per rebase, which can be useful to\n // ignore certain edge cases.\n //\n // True => limit the amount of change to a percentage of total supply\n // False => no limit\n //\n bool internal constant TOGGLE_CHANGESUPPLY_LIMIT = true;\n uint256 internal constant CHANGESUPPLY_DIVISOR = 10; // 10% of total supply\n\n // Mint limit\n //\n // Set a limit the amount minted per mint, which can be useful to\n // ignore certain edge cases.\n //\n // True => limit the amount of minted tokens\n // False => no limit\n //\n bool internal constant TOGGLE_MINT_LIMIT = true;\n uint256 internal constant MINT_MODULO = 1_000_000_000_000e18;\n\n // Known rounding errors\n uint256 internal constant TRANSFER_ROUNDING_ERROR = 1e18 - 1;\n uint256 internal constant OPT_IN_ROUNDING_ERROR = 1e18 - 1;\n uint256 internal constant MINT_ROUNDING_ERROR = 1e18 - 1;\n\n /**\n * @notice Modifier to skip tests that are known to fail\n * @dev see TOGGLE_KNOWN_ISSUES for more information\n */\n modifier hasKnownIssue() {\n if (TOGGLE_KNOWN_ISSUES) return;\n _;\n }\n\n /**\n * @notice Modifier to skip tests that are known to fail within limits\n * @dev see TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS for more information\n */\n modifier hasKnownIssueWithinLimits() {\n if (TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS) return;\n _;\n }\n\n /**\n * @notice Translate an account ID to an address\n * @param accountId The ID of the account\n * @return account The address of the account\n */\n function getAccount(uint8 accountId)\n internal\n view\n returns (address account)\n {\n accountId = accountId / 64;\n if (accountId == 0) return account = ADDRESS_USER0;\n if (accountId == 1) return account = ADDRESS_USER1;\n if (accountId == 2) return account = ADDRESS_CONTRACT0;\n if (accountId == 3) return account = ADDRESS_CONTRACT1;\n require(false, \"Unknown account ID\");\n }\n}\n" + }, + "contracts/echidna/EchidnaDebug.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport \"./EchidnaHelper.sol\";\nimport \"./Debugger.sol\";\n\nimport \"../token/OUSD.sol\";\n\n/**\n * @title Room for random debugging functions\n * @author Rappie\n */\ncontract EchidnaDebug is EchidnaHelper {\n function debugOUSD() public pure {\n // assert(ousd.balanceOf(ADDRESS_USER0) == 1000);\n // assert(ousd.rebaseState(ADDRESS_USER0) != OUSD.RebaseOptions.OptIn);\n // assert(Address.isContract(ADDRESS_CONTRACT0));\n // Debugger.log(\"nonRebasingSupply\", ousd.nonRebasingSupply());\n // assert(false);\n }\n}\n" + }, + "contracts/echidna/EchidnaHelper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaSetup.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin containing helper functions\n * @author Rappie\n */\ncontract EchidnaHelper is EchidnaSetup {\n /**\n * @notice Mint tokens to an account\n * @param toAcc Account to mint to\n * @param amount Amount to mint\n * @return Amount minted (in case of capped mint with modulo)\n */\n function mint(uint8 toAcc, uint256 amount) public returns (uint256) {\n address to = getAccount(toAcc);\n\n if (TOGGLE_MINT_LIMIT) {\n amount = amount % MINT_MODULO;\n }\n\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(to, amount);\n\n return amount;\n }\n\n /**\n * @notice Burn tokens from an account\n * @param fromAcc Account to burn from\n * @param amount Amount to burn\n */\n function burn(uint8 fromAcc, uint256 amount) public {\n address from = getAccount(fromAcc);\n hevm.prank(ADDRESS_VAULT);\n ousd.burn(from, amount);\n }\n\n /**\n * @notice Change the total supply of OUSD (rebase)\n * @param amount New total supply\n */\n function changeSupply(uint256 amount) public {\n if (TOGGLE_CHANGESUPPLY_LIMIT) {\n amount =\n ousd.totalSupply() +\n (amount % (ousd.totalSupply() / CHANGESUPPLY_DIVISOR));\n }\n\n hevm.prank(ADDRESS_VAULT);\n ousd.changeSupply(amount);\n }\n\n /**\n * @notice Transfer tokens between accounts\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function transfer(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n ousd.transfer(to, amount);\n }\n\n /**\n * @notice Transfer approved tokens between accounts\n * @param authorizedAcc Account that is authorized to transfer\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function transferFrom(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n ousd.transferFrom(from, to, amount);\n }\n\n /**\n * @notice Opt in to rebasing\n * @param targetAcc Account to opt in\n */\n function optIn(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n hevm.prank(target);\n ousd.rebaseOptIn();\n }\n\n /**\n * @notice Opt out of rebasing\n * @param targetAcc Account to opt out\n */\n function optOut(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n hevm.prank(target);\n ousd.rebaseOptOut();\n }\n\n /**\n * @notice Approve an account to spend OUSD\n * @param ownerAcc Account that owns the OUSD\n * @param spenderAcc Account that is approved to spend the OUSD\n * @param amount Amount to approve\n */\n function approve(\n uint8 ownerAcc,\n uint8 spenderAcc,\n uint256 amount\n ) public {\n address owner = getAccount(ownerAcc);\n address spender = getAccount(spenderAcc);\n hevm.prank(owner);\n // slither-disable-next-line unused-return\n ousd.approve(spender, amount);\n }\n\n /**\n * @notice Get the sum of all OUSD balances\n * @return total Total balance\n */\n function getTotalBalance() public view returns (uint256 total) {\n total += ousd.balanceOf(ADDRESS_VAULT);\n total += ousd.balanceOf(ADDRESS_OUTSIDER_USER);\n total += ousd.balanceOf(ADDRESS_OUTSIDER_CONTRACT);\n total += ousd.balanceOf(ADDRESS_USER0);\n total += ousd.balanceOf(ADDRESS_USER1);\n total += ousd.balanceOf(ADDRESS_CONTRACT0);\n total += ousd.balanceOf(ADDRESS_CONTRACT1);\n }\n\n /**\n * @notice Get the sum of all non-rebasing OUSD balances\n * @return total Total balance\n */\n function getTotalNonRebasingBalance() public returns (uint256 total) {\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_VAULT)\n ? ousd.balanceOf(ADDRESS_VAULT)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_OUTSIDER_USER)\n ? ousd.balanceOf(ADDRESS_OUTSIDER_USER)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_OUTSIDER_CONTRACT)\n ? ousd.balanceOf(ADDRESS_OUTSIDER_CONTRACT)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_USER0)\n ? ousd.balanceOf(ADDRESS_USER0)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_USER1)\n ? ousd.balanceOf(ADDRESS_USER1)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_CONTRACT0)\n ? ousd.balanceOf(ADDRESS_CONTRACT0)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_CONTRACT1)\n ? ousd.balanceOf(ADDRESS_CONTRACT1)\n : 0;\n }\n}\n" + }, + "contracts/echidna/EchidnaSetup.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./IHevm.sol\";\nimport \"./EchidnaConfig.sol\";\nimport \"./OUSDEchidna.sol\";\n\ncontract Dummy {}\n\n/**\n * @title Mixin for setup and deployment\n * @author Rappie\n */\ncontract EchidnaSetup is EchidnaConfig {\n IHevm hevm = IHevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);\n OUSDEchidna ousd = new OUSDEchidna();\n\n /**\n * @notice Deploy the OUSD contract and set up initial state\n */\n constructor() {\n ousd.initialize(ADDRESS_VAULT, 1e18);\n\n // Deploy dummny contracts as users\n Dummy outsider = new Dummy();\n ADDRESS_OUTSIDER_CONTRACT = address(outsider);\n Dummy dummy0 = new Dummy();\n ADDRESS_CONTRACT0 = address(dummy0);\n Dummy dummy1 = new Dummy();\n ADDRESS_CONTRACT1 = address(dummy1);\n\n // Start out with a reasonable amount of OUSD\n if (TOGGLE_STARTING_BALANCE) {\n // Rebasing tokens\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(ADDRESS_OUTSIDER_USER, STARTING_BALANCE / 2);\n\n // Non-rebasing tokens\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(ADDRESS_OUTSIDER_CONTRACT, STARTING_BALANCE / 2);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestAccounting.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestSupply.sol\";\n\n/**\n * @title Mixin for testing accounting functions\n * @author Rappie\n */\ncontract EchidnaTestAccounting is EchidnaTestSupply {\n /**\n * @notice After opting in, balance should not increase. (Ok to lose rounding funds doing this)\n * @param targetAcc Account to opt in\n */\n function testOptInBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optIn(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter <= balanceBefore);\n }\n\n /**\n * @notice After opting out, balance should remain the same\n * @param targetAcc Account to opt out\n */\n function testOptOutBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optOut(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Account balance should remain the same after opting in minus rounding error\n * @param targetAcc Account to opt in\n */\n function testOptInBalanceRounding(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optIn(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n int256 delta = int256(balanceAfter) - int256(balanceBefore);\n Debugger.log(\"delta\", delta);\n\n // slither-disable-next-line tautology\n assert(-1 * delta >= 0);\n assert(-1 * delta <= int256(OPT_IN_ROUNDING_ERROR));\n }\n\n /**\n * @notice After opting in, total supply should remain the same\n * @param targetAcc Account to opt in\n */\n function testOptInTotalSupply(uint8 targetAcc) public {\n uint256 totalSupplyBefore = ousd.totalSupply();\n optIn(targetAcc);\n uint256 totalSupplyAfter = ousd.totalSupply();\n\n assert(totalSupplyAfter == totalSupplyBefore);\n }\n\n /**\n * @notice After opting out, total supply should remain the same\n * @param targetAcc Account to opt out\n */\n function testOptOutTotalSupply(uint8 targetAcc) public {\n uint256 totalSupplyBefore = ousd.totalSupply();\n optOut(targetAcc);\n uint256 totalSupplyAfter = ousd.totalSupply();\n\n assert(totalSupplyAfter == totalSupplyBefore);\n }\n\n /**\n * @notice Account balance should remain the same when a smart contract auto converts\n * @param targetAcc Account to auto convert\n */\n function testAutoConvertBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n // slither-disable-next-line unused-return\n ousd._isNonRebasingAccountEchidna(target);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice The `balanceOf` function should never revert\n * @param targetAcc Account to check balance of\n */\n function testBalanceOfShouldNotRevert(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n // slither-disable-next-line unused-return\n try ousd.balanceOf(target) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestApproval.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaTestMintBurn.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin for testing approval related functions\n * @author Rappie\n */\ncontract EchidnaTestApproval is EchidnaTestMintBurn {\n /**\n * @notice Performing `transferFrom` with an amount inside the allowance should not revert\n * @param authorizedAcc The account that is authorized to transfer\n * @param fromAcc The account that is transferring\n * @param toAcc The account that is receiving\n * @param amount The amount to transfer\n */\n function testTransferFromShouldNotRevert(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount <= ousd.balanceOf(from));\n require(amount <= ousd.allowance(from, authorized));\n\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n try ousd.transferFrom(from, to, amount) {\n // pass\n } catch {\n assert(false);\n }\n }\n\n /**\n * @notice Performing `transferFrom` with an amount outside the allowance should revert\n * @param authorizedAcc The account that is authorized to transfer\n * @param fromAcc The account that is transferring\n * @param toAcc The account that is receiving\n * @param amount The amount to transfer\n */\n function testTransferFromShouldRevert(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount > 0);\n require(\n !(amount <= ousd.balanceOf(from) &&\n amount <= ousd.allowance(from, authorized))\n );\n\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n try ousd.transferFrom(from, to, amount) {\n assert(false);\n } catch {\n // pass\n }\n }\n\n /**\n * @notice Approving an amount should update the allowance and overwrite any previous allowance\n * @param ownerAcc The account that is approving\n * @param spenderAcc The account that is being approved\n * @param amount The amount to approve\n */\n function testApprove(\n uint8 ownerAcc,\n uint8 spenderAcc,\n uint256 amount\n ) public {\n address owner = getAccount(ownerAcc);\n address spender = getAccount(spenderAcc);\n\n approve(ownerAcc, spenderAcc, amount);\n uint256 allowanceAfter1 = ousd.allowance(owner, spender);\n\n assert(allowanceAfter1 == amount);\n\n approve(ownerAcc, spenderAcc, amount / 2);\n uint256 allowanceAfter2 = ousd.allowance(owner, spender);\n\n assert(allowanceAfter2 == amount / 2);\n }\n}\n" + }, + "contracts/echidna/EchidnaTestMintBurn.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestAccounting.sol\";\n\n/**\n * @title Mixin for testing Mint and Burn functions\n * @author Rappie\n */\ncontract EchidnaTestMintBurn is EchidnaTestAccounting {\n /**\n * @notice Minting 0 tokens should not affect account balance\n * @param targetAcc Account to mint to\n */\n function testMintZeroBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n mint(targetAcc, 0);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Burning 0 tokens should not affect account balance\n * @param targetAcc Account to burn from\n */\n function testBurnZeroBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n burn(targetAcc, 0);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Minting tokens must increase the account balance by at least amount\n * @param targetAcc Account to mint to\n * @param amount Amount to mint\n * @custom:error testMintBalance(uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * testMintBalance(0,1)\n * Event sequence:\n * Debug(«balanceBefore», 0)\n * Debug(«balanceAfter», 0)\n */\n function testMintBalance(uint8 targetAcc, uint256 amount)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n uint256 amountMinted = mint(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n Debugger.log(\"amountMinted\", amountMinted);\n Debugger.log(\"balanceBefore\", balanceBefore);\n Debugger.log(\"balanceAfter\", balanceAfter);\n\n assert(balanceAfter >= balanceBefore + amountMinted);\n }\n\n /**\n * @notice Burning tokens must decrease the account balance by at least amount\n * @param targetAcc Account to burn from\n * @param amount Amount to burn\n * @custom:error testBurnBalance(uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * mint(0,3)\n * testBurnBalance(0,1)\n * Event sequence:\n * Debug(«balanceBefore», 2)\n * Debug(«balanceAfter», 2)\n */\n function testBurnBalance(uint8 targetAcc, uint256 amount)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n burn(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n Debugger.log(\"balanceBefore\", balanceBefore);\n Debugger.log(\"balanceAfter\", balanceAfter);\n\n assert(balanceAfter <= balanceBefore - amount);\n }\n\n /**\n * @notice Minting tokens should not increase the account balance by less than rounding error above amount\n * @param targetAcc Account to mint to\n * @param amount Amount to mint\n */\n function testMintBalanceRounding(uint8 targetAcc, uint256 amount) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n uint256 amountMinted = mint(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n int256 delta = int256(balanceAfter) - int256(balanceBefore);\n\n // delta == amount, if no error\n // delta < amount, if too little is minted\n // delta > amount, if too much is minted\n int256 error = int256(amountMinted) - delta;\n\n assert(error >= 0);\n assert(error <= int256(MINT_ROUNDING_ERROR));\n }\n\n /**\n * @notice A burn of an account balance must result in a zero balance\n * @param targetAcc Account to burn from\n */\n function testBurnAllBalanceToZero(uint8 targetAcc) public hasKnownIssue {\n address target = getAccount(targetAcc);\n\n burn(targetAcc, ousd.balanceOf(target));\n assert(ousd.balanceOf(target) == 0);\n }\n\n /**\n * @notice You should always be able to burn an account's balance\n * @param targetAcc Account to burn from\n */\n function testBurnAllBalanceShouldNotRevert(uint8 targetAcc)\n public\n hasKnownIssue\n {\n address target = getAccount(targetAcc);\n uint256 balance = ousd.balanceOf(target);\n\n hevm.prank(ADDRESS_VAULT);\n try ousd.burn(target, balance) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestSupply.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestTransfer.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n/**\n * @title Mixin for testing supply related functions\n * @author Rappie\n */\ncontract EchidnaTestSupply is EchidnaTestTransfer {\n using StableMath for uint256;\n\n uint256 prevRebasingCreditsPerToken = type(uint256).max;\n\n /**\n * @notice After a `changeSupply`, the total supply should exactly\n * match the target total supply. (This is needed to ensure successive\n * rebases are correct).\n * @param supply New total supply\n * @custom:error testChangeSupply(uint256): failed!💥\n * Call sequence:\n * testChangeSupply(1044505275072865171609)\n * Event sequence:\n * TotalSupplyUpdatedHighres(1044505275072865171610, 1000000000000000000000000, 957391048054055578595)\n */\n function testChangeSupply(uint256 supply)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n hevm.prank(ADDRESS_VAULT);\n ousd.changeSupply(supply);\n\n assert(ousd.totalSupply() == supply);\n }\n\n /**\n * @notice The total supply must not be less than the sum of account balances.\n * (The difference will go into future rebases)\n * @custom:error testTotalSupplyLessThanTotalBalance(): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(1)\n * optOut(64)\n * transfer(0,64,1)\n * testTotalSupplyLessThanTotalBalance()\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000001000001)\n * Debug(«totalBalance», 1000000000000000001000002)\n */\n function testTotalSupplyLessThanTotalBalance()\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n uint256 totalSupply = ousd.totalSupply();\n uint256 totalBalance = getTotalBalance();\n\n Debugger.log(\"totalSupply\", totalSupply);\n Debugger.log(\"totalBalance\", totalBalance);\n\n assert(totalSupply >= totalBalance);\n }\n\n /**\n * @notice Non-rebasing supply should not be larger than total supply\n * @custom:error testNonRebasingSupplyVsTotalSupply(): failed!💥\n * Call sequence:\n * mint(0,2)\n * changeSupply(3)\n * burn(0,1)\n * optOut(0)\n * testNonRebasingSupplyVsTotalSupply()\n */\n function testNonRebasingSupplyVsTotalSupply() public hasKnownIssue {\n uint256 nonRebasingSupply = ousd.nonRebasingSupply();\n uint256 totalSupply = ousd.totalSupply();\n\n assert(nonRebasingSupply <= totalSupply);\n }\n\n /**\n * @notice Global `rebasingCreditsPerToken` should never increase\n * @custom:error testRebasingCreditsPerTokenNotIncreased(): failed!💥\n * Call sequence:\n * testRebasingCreditsPerTokenNotIncreased()\n * changeSupply(1)\n * testRebasingCreditsPerTokenNotIncreased()\n */\n function testRebasingCreditsPerTokenNotIncreased() public hasKnownIssue {\n uint256 curRebasingCreditsPerToken = ousd\n .rebasingCreditsPerTokenHighres();\n\n Debugger.log(\n \"prevRebasingCreditsPerToken\",\n prevRebasingCreditsPerToken\n );\n Debugger.log(\"curRebasingCreditsPerToken\", curRebasingCreditsPerToken);\n\n assert(curRebasingCreditsPerToken <= prevRebasingCreditsPerToken);\n\n prevRebasingCreditsPerToken = curRebasingCreditsPerToken;\n }\n\n /**\n * @notice The rebasing credits per token ratio must greater than zero\n */\n function testRebasingCreditsPerTokenAboveZero() public {\n assert(ousd.rebasingCreditsPerTokenHighres() > 0);\n }\n\n /**\n * @notice The sum of all non-rebasing balances should not be larger than\n * non-rebasing supply\n * @custom:error testTotalNonRebasingSupplyLessThanTotalBalance(): failed!💥\n * Call sequence\n * mint(0,2)\n * changeSupply(1)\n * optOut(0)\n * burn(0,1)\n * testTotalNonRebasingSupplyLessThanTotalBalance()\n * Event sequence:\n * Debug(«totalNonRebasingSupply», 500000000000000000000001)\n * Debug(«totalNonRebasingBalance», 500000000000000000000002)\n */\n function testTotalNonRebasingSupplyLessThanTotalBalance()\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n uint256 totalNonRebasingSupply = ousd.nonRebasingSupply();\n uint256 totalNonRebasingBalance = getTotalNonRebasingBalance();\n\n Debugger.log(\"totalNonRebasingSupply\", totalNonRebasingSupply);\n Debugger.log(\"totalNonRebasingBalance\", totalNonRebasingBalance);\n\n assert(totalNonRebasingSupply >= totalNonRebasingBalance);\n }\n\n /**\n * @notice An accounts credits / credits per token should not be larger it's balance\n * @param targetAcc The account to check\n */\n function testCreditsPerTokenVsBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n (uint256 credits, uint256 creditsPerToken, ) = ousd\n .creditsBalanceOfHighres(target);\n uint256 expectedBalance = credits.divPrecisely(creditsPerToken);\n\n uint256 balance = ousd.balanceOf(target);\n\n Debugger.log(\"credits\", credits);\n Debugger.log(\"creditsPerToken\", creditsPerToken);\n Debugger.log(\"expectedBalance\", expectedBalance);\n Debugger.log(\"balance\", balance);\n\n assert(expectedBalance == balance);\n }\n}\n" + }, + "contracts/echidna/EchidnaTestTransfer.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin for testing transfer related functions\n * @author Rappie\n */\ncontract EchidnaTestTransfer is EchidnaDebug {\n /**\n * @notice The receiving account's balance after a transfer must not increase by\n * less than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferBalanceReceivedLess(uint8,uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * mint(64,2)\n * testTransferBalanceReceivedLess(64,0,1)\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000000500002)\n * Debug(«toBalBefore», 0)\n * Debug(«toBalAfter», 0)\n */\n function testTransferBalanceReceivedLess(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue hasKnownIssueWithinLimits {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n\n assert(toBalAfter >= toBalBefore + amount);\n }\n\n /**\n * @notice The receiving account's balance after a transfer must not\n * increase by more than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceReceivedMore(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n\n assert(toBalAfter <= toBalBefore + amount);\n }\n\n /**\n * @notice The sending account's balance after a transfer must not\n * decrease by less than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferBalanceSentLess(uint8,uint8,uint256): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(1)\n * testTransferBalanceSentLess(0,64,1)\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000000500001)\n * Debug(«fromBalBefore», 1)\n * Debug(«fromBalAfter», 1)\n */\n function testTransferBalanceSentLess(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue hasKnownIssueWithinLimits {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n\n assert(fromBalAfter <= fromBalBefore - amount);\n }\n\n /**\n * @notice The sending account's balance after a transfer must not\n * decrease by more than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceSentMore(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n\n assert(fromBalAfter >= fromBalBefore - amount);\n }\n\n /**\n * @notice The receiving account's balance after a transfer must not\n * increase by less than the amount transferred (minus rounding error)\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceReceivedLessRounding(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n int256 toDelta = int256(toBalAfter) - int256(toBalBefore);\n\n // delta == amount, if no error\n // delta < amount, if too little is sent\n // delta > amount, if too much is sent\n int256 error = int256(amount) - toDelta;\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n Debugger.log(\"toDelta\", toDelta);\n Debugger.log(\"error\", error);\n\n assert(error >= 0);\n assert(error <= int256(TRANSFER_ROUNDING_ERROR));\n }\n\n /**\n * @notice The sending account's balance after a transfer must\n * not decrease by less than the amount transferred (minus rounding error)\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceSentLessRounding(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n int256 fromDelta = int256(fromBalAfter) - int256(fromBalBefore);\n\n // delta == -amount, if no error\n // delta < -amount, if too much is sent\n // delta > -amount, if too little is sent\n int256 error = int256(amount) + fromDelta;\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n Debugger.log(\"fromDelta\", fromDelta);\n Debugger.log(\"error\", error);\n\n assert(error >= 0);\n assert(error <= int256(TRANSFER_ROUNDING_ERROR));\n }\n\n /**\n * @notice An account should always be able to successfully transfer\n * an amount within its balance.\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferWithinBalanceDoesNotRevert(uint8,uint8,uint8): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(3)\n * optOut(0)\n * testTransferWithinBalanceDoesNotRevert(0,128,2)\n * optIn(0)\n * testTransferWithinBalanceDoesNotRevert(128,0,1)\n * Event sequence:\n * error Revert Panic(17): SafeMath over-/under-flows\n */\n function testTransferWithinBalanceDoesNotRevert(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount > 0);\n amount = amount % ousd.balanceOf(from);\n\n Debugger.log(\"Total supply\", ousd.totalSupply());\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(to, amount) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n\n /**\n * @notice An account should never be able to successfully transfer\n * an amount greater than their balance.\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferExceedingBalanceReverts(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n amount = ousd.balanceOf(from) + 1 + amount;\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(to, amount) {\n assert(false);\n } catch {\n assert(true);\n }\n }\n\n /**\n * @notice A transfer to the same account should not change that account's balance\n * @param targetAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferSelf(uint8 targetAcc, uint256 amount) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n transfer(targetAcc, targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceBefore == balanceAfter);\n }\n\n /**\n * @notice Transfers to the zero account revert\n * @param fromAcc Account to transfer from\n * @param amount Amount to transfer\n */\n function testTransferToZeroAddress(uint8 fromAcc, uint256 amount) public {\n address from = getAccount(fromAcc);\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(address(0), amount) {\n assert(false);\n } catch {\n assert(true);\n }\n }\n}\n" + }, + "contracts/echidna/IHevm.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// https://github.com/ethereum/hevm/blob/main/doc/src/controlling-the-unit-testing-environment.md#cheat-codes\n\ninterface IHevm {\n function warp(uint256 x) external;\n\n function roll(uint256 x) external;\n\n function store(\n address c,\n bytes32 loc,\n bytes32 val\n ) external;\n\n function load(address c, bytes32 loc) external returns (bytes32 val);\n\n function sign(uint256 sk, bytes32 digest)\n external\n returns (\n uint8 v,\n bytes32 r,\n bytes32 s\n );\n\n function addr(uint256 sk) external returns (address addr);\n\n function ffi(string[] calldata) external returns (bytes memory);\n\n function prank(address sender) external;\n}\n" + }, + "contracts/echidna/OUSDEchidna.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../token/OUSD.sol\";\n\ncontract OUSDEchidna is OUSD {\n constructor() OUSD() {}\n\n function _isNonRebasingAccountEchidna(address _account)\n public\n returns (bool)\n {\n _autoMigrate(_account);\n return alternativeCreditsPerToken[_account] > 0;\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/governance/InitializableGovernable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD InitializableGovernable Contract\n * @author Origin Protocol Inc\n */\nimport { Initializable } from \"../utils/Initializable.sol\";\n\nimport { Governable } from \"./Governable.sol\";\n\ncontract InitializableGovernable is Governable, Initializable {\n function _initialize(address _newGovernor) internal {\n _changeGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/governance/Strategizable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"./Governable.sol\";\n\ncontract Strategizable is Governable {\n event StrategistUpdated(address _address);\n\n // Address of strategist\n address public strategistAddr;\n\n // For future use\n uint256[50] private __gap;\n\n /**\n * @dev Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @dev Set address of Strategist\n * @param _address Address of Strategist\n */\n function setStrategistAddr(address _address) external onlyGovernor {\n _setStrategistAddr(_address);\n }\n\n /**\n * @dev Set address of Strategist\n * @param _address Address of Strategist\n */\n function _setStrategistAddr(address _address) internal {\n strategistAddr = _address;\n emit StrategistUpdated(_address);\n }\n}\n" + }, + "contracts/harvest/AbstractHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IUniswapV2Router } from \"../interfaces/uniswap/IUniswapV2Router02.sol\";\nimport { IUniswapV3Router } from \"../interfaces/uniswap/IUniswapV3Router.sol\";\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { ICurvePool } from \"../strategies/ICurvePool.sol\";\nimport \"../utils/Helpers.sol\";\n\nabstract contract AbstractHarvester is Governable {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using StableMath for uint256;\n\n enum SwapPlatform {\n UniswapV2Compatible,\n UniswapV3,\n Balancer,\n Curve\n }\n\n event SupportedStrategyUpdate(address strategyAddress, bool isSupported);\n event RewardTokenConfigUpdated(\n address tokenAddress,\n uint16 allowedSlippageBps,\n uint16 harvestRewardBps,\n SwapPlatform swapPlatform,\n address swapPlatformAddr,\n bytes swapData,\n uint256 liquidationLimit,\n bool doSwapRewardToken\n );\n event RewardTokenSwapped(\n address indexed rewardToken,\n address indexed swappedInto,\n SwapPlatform swapPlatform,\n uint256 amountIn,\n uint256 amountOut\n );\n event RewardProceedsTransferred(\n address indexed token,\n address farmer,\n uint256 protcolYield,\n uint256 farmerFee\n );\n event RewardProceedsAddressChanged(address newProceedsAddress);\n\n error EmptyAddress();\n error InvalidSlippageBps();\n error InvalidHarvestRewardBps();\n\n error InvalidSwapPlatform(SwapPlatform swapPlatform);\n\n error InvalidUniswapV2PathLength();\n error InvalidTokenInSwapPath(address token);\n error EmptyBalancerPoolId();\n error InvalidCurvePoolAssetIndex(address token);\n\n error UnsupportedStrategy(address strategyAddress);\n\n error SlippageError(uint256 actualBalance, uint256 minExpected);\n error BalanceMismatchAfterSwap(uint256 actualBalance, uint256 minExpected);\n\n // Configuration properties for harvesting logic of reward tokens\n struct RewardTokenConfig {\n // Max allowed slippage when swapping reward token for a stablecoin denominated in basis points.\n uint16 allowedSlippageBps;\n // Reward when calling a harvest function denominated in basis points.\n uint16 harvestRewardBps;\n // Address of compatible exchange protocol (Uniswap V2/V3, SushiSwap, Balancer and Curve).\n address swapPlatformAddr;\n /* When true the reward token is being swapped. In a need of (temporarily) disabling the swapping of\n * a reward token this needs to be set to false.\n */\n bool doSwapRewardToken;\n // Platform to use for Swapping\n SwapPlatform swapPlatform;\n /* How much token can be sold per one harvest call. If the balance of rewards tokens\n * exceeds that limit multiple harvest calls are required to harvest all of the tokens.\n * Set it to MAX_INT to effectively disable the limit.\n */\n uint256 liquidationLimit;\n }\n\n mapping(address => RewardTokenConfig) public rewardTokenConfigs;\n mapping(address => bool) public supportedStrategies;\n\n address public immutable vaultAddress;\n\n /**\n * Address receiving rewards proceeds. Initially the Vault contract later will possibly\n * be replaced by another contract that eases out rewards distribution.\n **/\n address public rewardProceedsAddress;\n\n /**\n * All tokens are swapped to this token before it gets transferred\n * to the `rewardProceedsAddress`. USDT for OUSD and WETH for OETH.\n **/\n address public immutable baseTokenAddress;\n // Cached decimals for `baseTokenAddress`\n uint256 public immutable baseTokenDecimals;\n\n // Uniswap V2 path for reward tokens using Uniswap V2 Router\n mapping(address => address[]) public uniswapV2Path;\n // Uniswap V3 path for reward tokens using Uniswap V3 Router\n mapping(address => bytes) public uniswapV3Path;\n // Pool ID to use for reward tokens on Balancer\n mapping(address => bytes32) public balancerPoolId;\n\n struct CurvePoolIndices {\n // Casted into uint128 and stored in a struct to save gas\n uint128 rewardTokenIndex;\n uint128 baseTokenIndex;\n }\n // Packed indices of assets on the Curve pool\n mapping(address => CurvePoolIndices) public curvePoolIndices;\n\n constructor(address _vaultAddress, address _baseTokenAddress) {\n require(_vaultAddress != address(0));\n require(_baseTokenAddress != address(0));\n\n vaultAddress = _vaultAddress;\n baseTokenAddress = _baseTokenAddress;\n\n // Cache decimals as well\n baseTokenDecimals = Helpers.getDecimals(_baseTokenAddress);\n }\n\n /***************************************\n Configuration\n ****************************************/\n\n /**\n * Set the Address receiving rewards proceeds.\n * @param _rewardProceedsAddress Address of the reward token\n */\n function setRewardProceedsAddress(address _rewardProceedsAddress)\n external\n onlyGovernor\n {\n if (_rewardProceedsAddress == address(0)) {\n revert EmptyAddress();\n }\n\n rewardProceedsAddress = _rewardProceedsAddress;\n emit RewardProceedsAddressChanged(_rewardProceedsAddress);\n }\n\n /**\n * @dev Add/update a reward token configuration that holds harvesting config variables\n * @param _tokenAddress Address of the reward token\n * @param tokenConfig.allowedSlippageBps uint16 maximum allowed slippage denominated in basis points.\n * Example: 300 == 3% slippage\n * @param tokenConfig.harvestRewardBps uint16 amount of reward tokens the caller of the function is rewarded.\n * Example: 100 == 1%\n * @param tokenConfig.swapPlatformAddr Address Address of a UniswapV2 compatible contract to perform\n * the exchange from reward tokens to stablecoin (currently hard-coded to USDT)\n * @param tokenConfig.liquidationLimit uint256 Maximum amount of token to be sold per one swap function call.\n * When value is 0 there is no limit.\n * @param tokenConfig.doSwapRewardToken bool Disables swapping of the token when set to true,\n * does not cause it to revert though.\n * @param tokenConfig.swapPlatform SwapPlatform to use for Swapping\n * @param swapData Additional data required for swapping\n */\n function setRewardTokenConfig(\n address _tokenAddress,\n RewardTokenConfig calldata tokenConfig,\n bytes calldata swapData\n ) external onlyGovernor {\n if (tokenConfig.allowedSlippageBps > 1000) {\n revert InvalidSlippageBps();\n }\n\n if (tokenConfig.harvestRewardBps > 1000) {\n revert InvalidHarvestRewardBps();\n }\n\n address newRouterAddress = tokenConfig.swapPlatformAddr;\n if (newRouterAddress == address(0)) {\n // Swap router address should be non zero address\n revert EmptyAddress();\n }\n\n address oldRouterAddress = rewardTokenConfigs[_tokenAddress]\n .swapPlatformAddr;\n rewardTokenConfigs[_tokenAddress] = tokenConfig;\n\n // Revert if feed does not exist\n // slither-disable-next-line unused-return\n IOracle(IVault(vaultAddress).priceProvider()).price(_tokenAddress);\n\n IERC20 token = IERC20(_tokenAddress);\n // if changing token swap provider cancel existing allowance\n if (\n /* oldRouterAddress == address(0) when there is no pre-existing\n * configuration for said rewards token\n */\n oldRouterAddress != address(0) &&\n oldRouterAddress != newRouterAddress\n ) {\n token.safeApprove(oldRouterAddress, 0);\n }\n\n // Give SwapRouter infinite approval when needed\n if (oldRouterAddress != newRouterAddress) {\n token.safeApprove(newRouterAddress, 0);\n token.safeApprove(newRouterAddress, type(uint256).max);\n }\n\n SwapPlatform _platform = tokenConfig.swapPlatform;\n if (_platform == SwapPlatform.UniswapV2Compatible) {\n uniswapV2Path[_tokenAddress] = _decodeUniswapV2Path(\n swapData,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.UniswapV3) {\n uniswapV3Path[_tokenAddress] = _decodeUniswapV3Path(\n swapData,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.Balancer) {\n balancerPoolId[_tokenAddress] = _decodeBalancerPoolId(\n swapData,\n newRouterAddress,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.Curve) {\n curvePoolIndices[_tokenAddress] = _decodeCurvePoolIndices(\n swapData,\n newRouterAddress,\n _tokenAddress\n );\n } else {\n // Note: This code is unreachable since Solidity reverts when\n // the value is outside the range of defined values of the enum\n // (even if it's under the max length of the base type)\n revert InvalidSwapPlatform(_platform);\n }\n\n emit RewardTokenConfigUpdated(\n _tokenAddress,\n tokenConfig.allowedSlippageBps,\n tokenConfig.harvestRewardBps,\n _platform,\n newRouterAddress,\n swapData,\n tokenConfig.liquidationLimit,\n tokenConfig.doSwapRewardToken\n );\n }\n\n /**\n * @dev Decodes the data passed into Uniswap V2 path and validates\n * it to make sure the path is for `token` to `baseToken`\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param token The address of the reward token\n * @return path The validated Uniswap V2 path\n */\n function _decodeUniswapV2Path(bytes calldata data, address token)\n internal\n view\n returns (address[] memory path)\n {\n (path) = abi.decode(data, (address[]));\n uint256 len = path.length;\n\n if (len < 2) {\n // Path should have at least two tokens\n revert InvalidUniswapV2PathLength();\n }\n\n // Do some validation\n if (path[0] != token) {\n revert InvalidTokenInSwapPath(path[0]);\n }\n\n if (path[len - 1] != baseTokenAddress) {\n revert InvalidTokenInSwapPath(path[len - 1]);\n }\n }\n\n /**\n * @dev Decodes the data passed into Uniswap V3 path and validates\n * it to make sure the path is for `token` to `baseToken`\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param token The address of the reward token\n * @return path The validated Uniswap V3 path\n */\n function _decodeUniswapV3Path(bytes calldata data, address token)\n internal\n view\n returns (bytes calldata path)\n {\n path = data;\n\n address decodedAddress = address(uint160(bytes20(data[0:20])));\n\n if (decodedAddress != token) {\n // Invalid Reward Token in swap path\n revert InvalidTokenInSwapPath(decodedAddress);\n }\n\n decodedAddress = address(uint160(bytes20(data[path.length - 20:])));\n if (decodedAddress != baseTokenAddress) {\n // Invalid Base Token in swap path\n revert InvalidTokenInSwapPath(decodedAddress);\n }\n }\n\n /**\n * @dev Decodes the data passed to Balancer Pool ID\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @return poolId The pool ID\n */\n function _decodeBalancerPoolId(\n bytes calldata data,\n address balancerVault,\n address token\n ) internal view returns (bytes32 poolId) {\n (poolId) = abi.decode(data, (bytes32));\n\n if (poolId == bytes32(0)) {\n revert EmptyBalancerPoolId();\n }\n\n IBalancerVault bVault = IBalancerVault(balancerVault);\n\n // Note: this reverts if token is not a pool asset\n // slither-disable-next-line unused-return\n bVault.getPoolTokenInfo(poolId, token);\n\n // slither-disable-next-line unused-return\n bVault.getPoolTokenInfo(poolId, baseTokenAddress);\n }\n\n /**\n * @dev Decodes the data passed to get the pool indices and\n * checks it against the Curve Pool to make sure it's\n * not misconfigured. The indices are packed into a single\n * uint256 for gas savings\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param poolAddress Curve pool address\n * @param token The address of the reward token\n * @return indices Packed pool asset indices\n */\n function _decodeCurvePoolIndices(\n bytes calldata data,\n address poolAddress,\n address token\n ) internal view returns (CurvePoolIndices memory indices) {\n indices = abi.decode(data, (CurvePoolIndices));\n\n ICurvePool pool = ICurvePool(poolAddress);\n if (token != pool.coins(indices.rewardTokenIndex)) {\n revert InvalidCurvePoolAssetIndex(token);\n }\n if (baseTokenAddress != pool.coins(indices.baseTokenIndex)) {\n revert InvalidCurvePoolAssetIndex(baseTokenAddress);\n }\n }\n\n /**\n * @dev Flags a strategy as supported or not supported one\n * @param _strategyAddress Address of the strategy\n * @param _isSupported Bool marking strategy as supported or not supported\n */\n function setSupportedStrategy(address _strategyAddress, bool _isSupported)\n external\n onlyGovernor\n {\n supportedStrategies[_strategyAddress] = _isSupported;\n emit SupportedStrategyUpdate(_strategyAddress, _isSupported);\n }\n\n /***************************************\n Rewards\n ****************************************/\n\n /**\n * @dev Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform. Can be called by anyone.\n * Rewards incentivizing the caller are sent to the caller of this function.\n * @param _strategyAddr Address of the strategy to collect rewards from\n */\n function harvestAndSwap(address _strategyAddr) external nonReentrant {\n // Remember _harvest function checks for the validity of _strategyAddr\n _harvestAndSwap(_strategyAddr, msg.sender);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform. Can be called by anyone\n * @param _strategyAddr Address of the strategy to collect rewards from\n * @param _rewardTo Address where to send a share of harvest rewards to as an incentive\n * for executing this function\n */\n function harvestAndSwap(address _strategyAddr, address _rewardTo)\n external\n nonReentrant\n {\n // Remember _harvest function checks for the validity of _strategyAddr\n _harvestAndSwap(_strategyAddr, _rewardTo);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform\n * @param _strategyAddr Address of the strategy to collect rewards from\n * @param _rewardTo Address where to send a share of harvest rewards to as an incentive\n * for executing this function\n */\n function _harvestAndSwap(address _strategyAddr, address _rewardTo)\n internal\n {\n _harvest(_strategyAddr);\n IStrategy strategy = IStrategy(_strategyAddr);\n address[] memory rewardTokens = strategy.getRewardTokenAddresses();\n IOracle priceProvider = IOracle(IVault(vaultAddress).priceProvider());\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; ++i) {\n _swap(rewardTokens[i], _rewardTo, priceProvider);\n }\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform\n * @param _strategyAddr Address of the strategy to collect rewards from.\n */\n function _harvest(address _strategyAddr) internal virtual {\n if (!supportedStrategies[_strategyAddr]) {\n revert UnsupportedStrategy(_strategyAddr);\n }\n\n IStrategy strategy = IStrategy(_strategyAddr);\n strategy.collectRewardTokens();\n }\n\n /**\n * @dev Swap a reward token for the base token on the configured\n * swap platform. The token must have a registered price feed\n * with the price provider\n * @param _swapToken Address of the token to swap\n * @param _rewardTo Address where to send the share of harvest rewards to\n * @param _priceProvider Oracle to get prices of the swap token\n */\n function _swap(\n address _swapToken,\n address _rewardTo,\n IOracle _priceProvider\n ) internal virtual {\n uint256 balance = IERC20(_swapToken).balanceOf(address(this));\n\n // No need to swap if the reward token is the base token. eg USDT or WETH.\n // There is also no limit on the transfer. Everything in the harvester will be transferred\n // to the Dripper regardless of the liquidationLimit config.\n if (_swapToken == baseTokenAddress) {\n IERC20(_swapToken).safeTransfer(rewardProceedsAddress, balance);\n // currently not paying the farmer any rewards as there is no swap\n emit RewardProceedsTransferred(\n baseTokenAddress,\n address(0),\n balance,\n 0\n );\n return;\n }\n\n RewardTokenConfig memory tokenConfig = rewardTokenConfigs[_swapToken];\n\n /* This will trigger a return when reward token configuration has not yet been set\n * or we have temporarily disabled swapping of specific reward token via setting\n * doSwapRewardToken to false.\n */\n if (!tokenConfig.doSwapRewardToken) {\n return;\n }\n\n if (balance == 0) {\n return;\n }\n\n if (tokenConfig.liquidationLimit > 0) {\n balance = Math.min(balance, tokenConfig.liquidationLimit);\n }\n\n // This'll revert if there is no price feed\n uint256 oraclePrice = _priceProvider.price(_swapToken);\n\n // Oracle price is 1e18\n uint256 minExpected = (balance *\n (1e4 - tokenConfig.allowedSlippageBps) * // max allowed slippage\n oraclePrice).scaleBy(\n baseTokenDecimals,\n Helpers.getDecimals(_swapToken)\n ) /\n 1e4 / // fix the max slippage decimal position\n 1e18; // and oracle price decimals position\n\n // Do the swap\n uint256 amountReceived = _doSwap(\n tokenConfig.swapPlatform,\n tokenConfig.swapPlatformAddr,\n _swapToken,\n balance,\n minExpected\n );\n\n if (amountReceived < minExpected) {\n revert SlippageError(amountReceived, minExpected);\n }\n\n emit RewardTokenSwapped(\n _swapToken,\n baseTokenAddress,\n tokenConfig.swapPlatform,\n balance,\n amountReceived\n );\n\n IERC20 baseToken = IERC20(baseTokenAddress);\n uint256 baseTokenBalance = baseToken.balanceOf(address(this));\n if (baseTokenBalance < amountReceived) {\n // Note: It's possible to bypass this check by transferring `baseToken`\n // directly to Harvester before calling the `harvestAndSwap`. However,\n // there's no incentive for an attacker to do that. Doing a balance diff\n // will increase the gas cost significantly\n revert BalanceMismatchAfterSwap(baseTokenBalance, amountReceived);\n }\n\n // Farmer only gets fee from the base amount they helped farm,\n // They do not get anything from anything that already was there\n // on the Harvester\n uint256 farmerFee = amountReceived.mulTruncateScale(\n tokenConfig.harvestRewardBps,\n 1e4\n );\n uint256 protocolYield = baseTokenBalance - farmerFee;\n\n baseToken.safeTransfer(rewardProceedsAddress, protocolYield);\n baseToken.safeTransfer(_rewardTo, farmerFee);\n emit RewardProceedsTransferred(\n baseTokenAddress,\n _rewardTo,\n protocolYield,\n farmerFee\n );\n }\n\n function _doSwap(\n SwapPlatform swapPlatform,\n address routerAddress,\n address rewardTokenAddress,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n if (swapPlatform == SwapPlatform.UniswapV2Compatible) {\n return\n _swapWithUniswapV2(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.UniswapV3) {\n return\n _swapWithUniswapV3(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.Balancer) {\n return\n _swapWithBalancer(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.Curve) {\n return\n _swapWithCurve(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else {\n // Should never be invoked since we catch invalid values\n // in the `setRewardTokenConfig` function before it's set\n revert InvalidSwapPlatform(swapPlatform);\n }\n }\n\n /**\n * @dev Swaps the token to `baseToken` with Uniswap V2\n *\n * @param routerAddress Uniswap V2 Router address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithUniswapV2(\n address routerAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n address[] memory path = uniswapV2Path[swapToken];\n\n uint256[] memory amounts = IUniswapV2Router(routerAddress)\n .swapExactTokensForTokens(\n amountIn,\n minAmountOut,\n path,\n address(this),\n block.timestamp\n );\n\n amountOut = amounts[amounts.length - 1];\n }\n\n /**\n * @dev Swaps the token to `baseToken` with Uniswap V3\n *\n * @param routerAddress Uniswap V3 Router address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithUniswapV3(\n address routerAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n bytes memory path = uniswapV3Path[swapToken];\n\n IUniswapV3Router.ExactInputParams memory params = IUniswapV3Router\n .ExactInputParams({\n path: path,\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: amountIn,\n amountOutMinimum: minAmountOut\n });\n amountOut = IUniswapV3Router(routerAddress).exactInput(params);\n }\n\n /**\n * @dev Swaps the token to `baseToken` on Balancer\n *\n * @param balancerVaultAddress BalancerVaultAddress\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithBalancer(\n address balancerVaultAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n bytes32 poolId = balancerPoolId[swapToken];\n\n IBalancerVault.SingleSwap memory singleSwap = IBalancerVault\n .SingleSwap({\n poolId: poolId,\n kind: IBalancerVault.SwapKind.GIVEN_IN,\n assetIn: swapToken,\n assetOut: baseTokenAddress,\n amount: amountIn,\n userData: hex\"\"\n });\n\n IBalancerVault.FundManagement memory fundMgmt = IBalancerVault\n .FundManagement({\n sender: address(this),\n fromInternalBalance: false,\n recipient: payable(address(this)),\n toInternalBalance: false\n });\n\n amountOut = IBalancerVault(balancerVaultAddress).swap(\n singleSwap,\n fundMgmt,\n minAmountOut,\n block.timestamp\n );\n }\n\n /**\n * @dev Swaps the token to `baseToken` on Curve\n *\n * @param poolAddress Curve Pool Address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithCurve(\n address poolAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n CurvePoolIndices memory indices = curvePoolIndices[swapToken];\n\n // Note: Not all CurvePools return the `amountOut`, make sure\n // to use only pool that do. Otherwise the swap would revert\n // always\n amountOut = ICurvePool(poolAddress).exchange(\n uint256(indices.rewardTokenIndex),\n uint256(indices.baseTokenIndex),\n amountIn,\n minAmountOut\n );\n }\n}\n" + }, + "contracts/harvest/Dripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\n/**\n * @title OUSD Dripper\n *\n * The dripper contract smooths out the yield from point-in-time yield events\n * and spreads the yield out over a configurable time period. This ensures a\n * continuous per block yield to makes users happy as their next rebase\n * amount is always moving up. Also, this makes historical day to day yields\n * smooth, rather than going from a near zero day, to a large APY day, then\n * back to a near zero day again.\n *\n *\n * Design notes\n * - USDT has a smaller resolution than the number of seconds\n * in a week, which can make per second payouts have a rounding error. However\n * the total effect is not large - cents per day, and this money is\n * not lost, just distributed in the future. While we could use a higher\n * decimal precision for the drip perSecond, we chose simpler code.\n * - By calculating the changing drip rates on collects only, harvests and yield\n * events don't have to call anything on this contract or pay any extra gas.\n * Collect() is already be paying for a single write, since it has to reset\n * the lastCollect time.\n * - By having a collectAndRebase method, and having our external systems call\n * that, the OUSD vault does not need any changes, not even to know the address\n * of the dripper.\n * - A rejected design was to retro-calculate the drip rate on each collect,\n * based on the balance at the time of the collect. While this would have\n * required less state, and would also have made the contract respond more quickly\n * to new income, it would break the predictability that is this contract's entire\n * purpose. If we did this, the amount of fundsAvailable() would make sharp increases\n * when funds were deposited.\n * - When the dripper recalculates the rate, it targets spending the balance over\n * the duration. This means that every time that collect is called, if no\n * new funds have been deposited the duration is being pushed back and the\n * rate decreases. This is expected, and ends up following a smoother but\n * longer curve the more collect() is called without incoming yield.\n *\n */\n\ncontract Dripper is Governable {\n using SafeERC20 for IERC20;\n\n struct Drip {\n uint64 lastCollect; // overflows 262 billion years after the sun dies\n uint192 perSecond; // drip rate per second\n }\n\n address immutable vault; // OUSD vault\n address immutable token; // token to drip out\n uint256 public dripDuration; // in seconds\n Drip public drip; // active drip parameters\n\n constructor(address _vault, address _token) {\n vault = _vault;\n token = _token;\n }\n\n /// @notice How much funds have dripped out already and are currently\n // available to be sent to the vault.\n /// @return The amount that would be sent if a collect was called\n function availableFunds() external view returns (uint256) {\n uint256 balance = IERC20(token).balanceOf(address(this));\n return _availableFunds(balance, drip);\n }\n\n /// @notice Collect all dripped funds and send to vault.\n /// Recalculate new drip rate.\n function collect() external {\n _collect();\n }\n\n /// @notice Collect all dripped funds, send to vault, recalculate new drip\n /// rate, and rebase OUSD.\n function collectAndRebase() external {\n _collect();\n IVault(vault).rebase();\n }\n\n /// @dev Change the drip duration. Governor only.\n /// @param _durationSeconds the number of seconds to drip out the entire\n /// balance over if no collects were called during that time.\n function setDripDuration(uint256 _durationSeconds)\n external\n virtual\n onlyGovernor\n {\n require(_durationSeconds > 0, \"duration must be non-zero\");\n dripDuration = _durationSeconds;\n _collect(); // duration change take immediate effect\n }\n\n /// @dev Transfer out ERC20 tokens held by the contract. Governor only.\n /// @param _asset ERC20 token address\n /// @param _amount amount to transfer\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /// @dev Calculate available funds by taking the lower of either the\n /// currently dripped out funds or the balance available.\n /// Uses passed in parameters to calculate with for gas savings.\n /// @param _balance current balance in contract\n /// @param _drip current drip parameters\n function _availableFunds(uint256 _balance, Drip memory _drip)\n internal\n view\n returns (uint256)\n {\n uint256 elapsed = block.timestamp - _drip.lastCollect;\n uint256 allowed = (elapsed * _drip.perSecond);\n return (allowed > _balance) ? _balance : allowed;\n }\n\n /// @dev Sends the currently dripped funds to be vault, and sets\n /// the new drip rate based on the new balance.\n function _collect() internal virtual {\n // Calculate send\n uint256 balance = IERC20(token).balanceOf(address(this));\n uint256 amountToSend = _availableFunds(balance, drip);\n uint256 remaining = balance - amountToSend;\n // Calculate new drip perSecond\n // Gas savings by setting entire struct at one time\n drip = Drip({\n perSecond: uint192(remaining / dripDuration),\n lastCollect: uint64(block.timestamp)\n });\n // Send funds\n IERC20(token).safeTransfer(vault, amountToSend);\n }\n\n /// @dev Transfer out all ERC20 held by the contract. Governor only.\n /// @param _asset ERC20 token address\n function transferAllToken(address _asset, address _receiver)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(\n _receiver,\n IERC20(_asset).balanceOf(address(this))\n );\n }\n}\n" + }, + "contracts/harvest/FixedRateDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Dripper } from \"./Dripper.sol\";\n\n/**\n * @title Fixed Rate Dripper\n *\n * Similar to the Dripper, Fixed Rate Dripper drips out yield per second.\n * However the Strategist decides the rate and it doesn't change after\n * a drip.\n *\n */\n\ncontract FixedRateDripper is Dripper {\n using SafeERC20 for IERC20;\n\n event DripRateUpdated(uint192 oldDripRate, uint192 newDripRate);\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vault).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n constructor(address _vault, address _token) Dripper(_vault, _token) {}\n\n /// @inheritdoc Dripper\n function setDripDuration(uint256) external virtual override {\n // Not used in FixedRateDripper\n revert(\"Drip duration disabled\");\n }\n\n /// @inheritdoc Dripper\n function _collect() internal virtual override {\n // Calculate amount to send\n uint256 balance = IERC20(token).balanceOf(address(this));\n uint256 amountToSend = _availableFunds(balance, drip);\n\n // Update timestamp\n drip.lastCollect = uint64(block.timestamp);\n\n // Send funds\n IERC20(token).safeTransfer(vault, amountToSend);\n }\n\n /**\n * @dev Sets the drip rate. Callable by Strategist or Governor.\n * Can be set to zero to stop dripper.\n * @param _perSecond Rate of WETH to drip per second\n */\n function setDripRate(uint192 _perSecond) external onlyGovernorOrStrategist {\n emit DripRateUpdated(_perSecond, drip.perSecond);\n\n /**\n * Note: It's important to call `_collect` before updating\n * the drip rate especially on a new proxy contract.\n * When `lastCollect` is not set/initialized, the elapsed\n * time would be calculated as `block.number` seconds,\n * resulting in a huge yield, if `collect` isn't called first.\n */\n // Collect at existing rate\n _collect();\n\n // Update rate\n drip.perSecond = _perSecond;\n }\n}\n" + }, + "contracts/harvest/Harvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractHarvester } from \"./AbstractHarvester.sol\";\n\ncontract Harvester is AbstractHarvester {\n constructor(address _vault, address _usdtAddress)\n AbstractHarvester(_vault, _usdtAddress)\n {}\n}\n" + }, + "contracts/harvest/OETHBaseHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { ISwapRouter } from \"../interfaces/aerodrome/ISwapRouter.sol\";\n\ncontract OETHBaseHarvester is Governable {\n using SafeERC20 for IERC20;\n\n IVault public immutable vault;\n IStrategy public immutable amoStrategy;\n IERC20 public immutable aero;\n IERC20 public immutable weth;\n ISwapRouter public immutable swapRouter;\n\n address public operatorAddr;\n\n // Similar sig to `AbstractHarvester.RewardTokenSwapped` for\n // future compatibility with monitoring\n event RewardTokenSwapped(\n address indexed rewardToken,\n address indexed swappedInto,\n uint8 swapPlatform,\n uint256 amountIn,\n uint256 amountOut\n );\n\n event OperatorChanged(address oldOperator, address newOperator);\n event YieldSent(address recipient, uint256 yield, uint256 fee);\n\n /**\n * @notice Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == vault.strategistAddr() || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @notice Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategistOrOperator() {\n require(\n msg.sender == operatorAddr ||\n msg.sender == vault.strategistAddr() ||\n isGovernor(),\n \"Caller is not the Operator or Strategist or Governor\"\n );\n _;\n }\n\n constructor(\n address _vault,\n address _amoStrategy,\n address _aero,\n address _weth,\n address _swapRouter\n ) {\n vault = IVault(_vault);\n amoStrategy = IStrategy(_amoStrategy);\n aero = IERC20(_aero);\n weth = IERC20(_weth);\n swapRouter = ISwapRouter(_swapRouter);\n }\n\n /**\n * @dev Changes the operator address which can call `harvest`\n * @param _operatorAddr New operator address\n */\n function setOperatorAddr(address _operatorAddr) external onlyGovernor {\n emit OperatorChanged(operatorAddr, _operatorAddr);\n operatorAddr = _operatorAddr;\n }\n\n /**\n * @notice Collects AERO from AMO strategy and\n * sends it to the Strategist multisig.\n * Anyone can call it.\n */\n function harvest() external onlyGovernorOrStrategistOrOperator {\n address strategistAddr = vault.strategistAddr();\n require(strategistAddr != address(0), \"Guardian address not set\");\n\n // Collect all AERO\n amoStrategy.collectRewardTokens();\n\n uint256 aeroBalance = aero.balanceOf(address(this));\n if (aeroBalance == 0) {\n // Do nothing if there's no AERO to transfer\n return;\n }\n\n // Transfer everything to Strategist\n aero.safeTransfer(strategistAddr, aeroBalance);\n }\n\n /**\n * @notice Harvests AERO from AMO strategy and then swaps some (or all)\n * of it into WETH to distribute yield and fee.\n * When `feeBps` is set to 10000 (100%), all WETH received is\n * sent to strategist.\n *\n * @param aeroToSwap Amount of AERO to swap\n * @param minWETHExpected Min. amount of WETH to expect\n * @param feeBps Performance fee bps (Sent to strategist)\n * @param sendYieldToDripper Sends yield to Dripper, if set to true.\n * Otherwise, to the Guardian\n */\n function harvestAndSwap(\n uint256 aeroToSwap,\n uint256 minWETHExpected,\n uint256 feeBps,\n bool sendYieldToDripper\n ) external onlyGovernorOrStrategist {\n address strategistAddr = vault.strategistAddr();\n require(strategistAddr != address(0), \"Guardian address not set\");\n\n // Yields can either be sent to the Dripper or Strategist\n address yieldRecipient = sendYieldToDripper\n ? vault.dripper()\n : strategistAddr;\n require(yieldRecipient != address(0), \"Yield recipient not set\");\n\n require(feeBps <= 10000, \"Invalid Fee Bps\");\n\n // Collect all AERO\n amoStrategy.collectRewardTokens();\n\n uint256 aeroBalance = aero.balanceOf(address(this));\n if (aeroBalance == 0) {\n // Do nothing if there's no AERO to transfer/swap\n return;\n }\n\n if (aeroToSwap > 0) {\n if (aeroBalance < aeroToSwap) {\n // Transfer in balance from the multisig as needed\n // slither-disable-next-line unchecked-transfer arbitrary-send-erc20\n aero.safeTransferFrom(\n strategistAddr,\n address(this),\n aeroToSwap - aeroBalance\n );\n }\n\n _doSwap(aeroToSwap, minWETHExpected);\n\n // Figure out AERO left in contract after swap\n aeroBalance = aero.balanceOf(address(this));\n }\n\n // Transfer out any leftover AERO after swap\n if (aeroBalance > 0) {\n aero.safeTransfer(strategistAddr, aeroBalance);\n }\n\n // Computes using all balance the contract holds,\n // not just the WETH received from swap. Use `transferToken`\n // if there's any WETH left that needs to be taken out\n uint256 availableWETHBalance = weth.balanceOf(address(this));\n // Computation rounds in favor of protocol\n uint256 fee = (availableWETHBalance * feeBps) / 10000;\n uint256 yield = availableWETHBalance - fee;\n\n // Transfer yield, if any\n if (yield > 0) {\n weth.safeTransfer(yieldRecipient, yield);\n }\n\n // Transfer fee to the Guardian, if any\n if (fee > 0) {\n weth.safeTransfer(strategistAddr, fee);\n }\n\n emit YieldSent(yieldRecipient, yield, fee);\n }\n\n /**\n * @notice Swaps AERO to WETH on Aerodrome\n * @param aeroToSwap Amount of AERO to swap\n * @param minWETHExpected Min. amount of WETH to expect\n */\n function _doSwap(uint256 aeroToSwap, uint256 minWETHExpected) internal {\n // Let the swap router move funds\n aero.approve(address(swapRouter), aeroToSwap);\n\n // Do the swap\n uint256 wethReceived = swapRouter.exactInputSingle(\n ISwapRouter.ExactInputSingleParams({\n tokenIn: address(aero),\n tokenOut: address(weth),\n tickSpacing: 200, // From AERO/WETH pool contract\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: aeroToSwap,\n amountOutMinimum: minWETHExpected,\n sqrtPriceLimitX96: 0\n })\n );\n\n emit RewardTokenSwapped(\n address(aero),\n address(weth),\n 0,\n aeroToSwap,\n wethReceived\n );\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * the contract, i.e. mistaken sends.\n * Also, allows to transfer any AERO left in the contract.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n virtual\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n}\n" + }, + "contracts/harvest/OETHDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Dripper } from \"./Dripper.sol\";\n\n/**\n * @title OETH Dripper Contract\n * @author Origin Protocol Inc\n */\ncontract OETHDripper is Dripper {\n constructor(address _vault, address _token) Dripper(_vault, _token) {}\n}\n" + }, + "contracts/harvest/OETHFixedRateDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { FixedRateDripper } from \"./FixedRateDripper.sol\";\n\n/**\n * @title OETH FixedRateDripper Contract\n * @author Origin Protocol Inc\n */\ncontract OETHFixedRateDripper is FixedRateDripper {\n constructor(address _vault, address _token)\n FixedRateDripper(_vault, _token)\n {}\n}\n" + }, + "contracts/harvest/OETHHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractHarvester } from \"./AbstractHarvester.sol\";\n\ncontract OETHHarvester is AbstractHarvester {\n constructor(address _vault, address _wethAddress)\n AbstractHarvester(_vault, _wethAddress)\n {}\n}\n" + }, + "contracts/harvest/OETHHarvesterSimple.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\n\n/// @title OETH Harvester Simple Contract\n/// @notice Contract to harvest rewards from strategies\n/// @author Origin Protocol Inc\ncontract OETHHarvesterSimple is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n ////////////////////////////////////////////////////\n /// --- CONSTANTS & IMMUTABLES\n ////////////////////////////////////////////////////\n /// @notice wrapped native token address (WETH or wS)\n address public immutable wrappedNativeToken;\n\n ////////////////////////////////////////////////////\n /// --- STORAGE\n ////////////////////////////////////////////////////\n /// @notice Dripper address\n address public dripper;\n\n /// @notice Mapping of supported strategies\n mapping(address => bool) public supportedStrategies;\n\n /// @notice Gap for upgrade safety\n uint256[48] private ___gap;\n\n ////////////////////////////////////////////////////\n /// --- EVENTS\n ////////////////////////////////////////////////////\n event Harvested(\n address indexed strategy,\n address token,\n uint256 amount,\n address indexed receiver\n );\n event SupportedStrategyUpdated(address strategy, bool status);\n event DripperUpdated(address dripper);\n\n ////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////\n constructor(address _wrappedNativeToken) {\n wrappedNativeToken = _wrappedNativeToken;\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /// @notice Initialize the contract\n function initialize() external onlyGovernor initializer {\n // Call it to set `initialized` to true and to prevent the implementation\n // from getting initialized in future through the proxy\n }\n\n ////////////////////////////////////////////////////\n /// --- MUTATIVE FUNCTIONS\n ////////////////////////////////////////////////////\n /// @notice Harvest rewards from a strategy and transfer to strategist or dripper\n /// @param _strategy Address of the strategy to harvest\n function harvestAndTransfer(address _strategy) external {\n _harvestAndTransfer(_strategy);\n }\n\n /// @notice Harvest rewards from multiple strategies and transfer to strategist or dripper\n /// @param _strategies Array of strategy addresses to harvest\n function harvestAndTransfer(address[] calldata _strategies) external {\n for (uint256 i = 0; i < _strategies.length; i++) {\n _harvestAndTransfer(_strategies[i]);\n }\n }\n\n /// @notice Internal logic to harvest rewards from a strategy\n function _harvestAndTransfer(address _strategy) internal virtual {\n // Ensure strategy is supported\n require(supportedStrategies[_strategy], \"Strategy not supported\");\n\n // Store locally for some gas savings\n address _strategist = strategistAddr;\n address _dripper = dripper;\n\n // Harvest rewards\n IStrategy(_strategy).collectRewardTokens();\n\n // Cache reward tokens\n address[] memory rewardTokens = IStrategy(_strategy)\n .getRewardTokenAddresses();\n\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; i++) {\n // Cache balance\n address token = rewardTokens[i];\n uint256 balance = IERC20(token).balanceOf(address(this));\n if (balance > 0) {\n // Determine receiver\n address receiver = token == wrappedNativeToken\n ? _dripper\n : _strategist;\n require(receiver != address(0), \"Invalid receiver\");\n\n // Transfer to the Strategist or the Dripper\n IERC20(token).safeTransfer(receiver, balance);\n emit Harvested(_strategy, token, balance, receiver);\n }\n }\n }\n\n ////////////////////////////////////////////////////\n /// --- GOVERNANCE\n ////////////////////////////////////////////////////\n /// @notice Set supported strategy\n /// @param _strategy Address of the strategy\n /// @param _isSupported Boolean indicating if strategy is supported\n function setSupportedStrategy(address _strategy, bool _isSupported)\n external\n onlyGovernorOrStrategist\n {\n require(_strategy != address(0), \"Invalid strategy\");\n supportedStrategies[_strategy] = _isSupported;\n emit SupportedStrategyUpdated(_strategy, _isSupported);\n }\n\n /// @notice Transfer tokens to strategist\n /// @param _asset Address of the token\n /// @param _amount Amount of tokens to transfer\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernorOrStrategist\n {\n IERC20(_asset).safeTransfer(strategistAddr, _amount);\n }\n\n /// @notice Set the dripper address\n /// @param _dripper Address of the dripper\n function setDripper(address _dripper) external onlyGovernor {\n _setDripper(_dripper);\n }\n\n /// @notice Internal logic to set the dripper address\n function _setDripper(address _dripper) internal {\n require(_dripper != address(0), \"Invalid dripper\");\n dripper = _dripper;\n emit DripperUpdated(_dripper);\n }\n}\n" + }, + "contracts/harvest/OSonicHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SuperOETHHarvester } from \"./SuperOETHHarvester.sol\";\n\ncontract OSonicHarvester is SuperOETHHarvester {\n /// @param _wrappedNativeToken Address of the native Wrapped S (wS) token\n constructor(address _wrappedNativeToken)\n SuperOETHHarvester(_wrappedNativeToken)\n {}\n}\n" + }, + "contracts/harvest/SuperOETHHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHHarvesterSimple, IERC20, IStrategy, SafeERC20 } from \"./OETHHarvesterSimple.sol\";\n\ncontract SuperOETHHarvester is OETHHarvesterSimple {\n using SafeERC20 for IERC20;\n\n constructor(address _wrappedNativeToken)\n OETHHarvesterSimple(_wrappedNativeToken)\n {}\n\n /// @inheritdoc OETHHarvesterSimple\n function _harvestAndTransfer(address _strategy) internal virtual override {\n // Ensure strategy is supported\n require(supportedStrategies[_strategy], \"Strategy not supported\");\n\n address receiver = strategistAddr;\n require(receiver != address(0), \"Invalid receiver\");\n\n // Harvest rewards\n IStrategy(_strategy).collectRewardTokens();\n\n // Cache reward tokens\n address[] memory rewardTokens = IStrategy(_strategy)\n .getRewardTokenAddresses();\n\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; i++) {\n // Cache balance\n address token = rewardTokens[i];\n uint256 balance = IERC20(token).balanceOf(address(this));\n if (balance > 0) {\n // Transfer everything to the strategist\n IERC20(token).safeTransfer(receiver, balance);\n emit Harvested(_strategy, token, balance, receiver);\n }\n }\n }\n}\n" + }, + "contracts/interfaces/aerodrome/IAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ICLPool } from \"./ICLPool.sol\";\n\ninterface IAMOStrategy {\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth);\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth);\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n error OutsideExpectedTickRange(int24 currentTick);\n\n function governor() external view returns (address);\n\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) external;\n\n function clPool() external view returns (ICLPool);\n\n function vaultAddress() external view returns (address);\n\n function poolWethShareVarianceAllowed() external view returns (uint256);\n\n function poolWethShare() external view returns (uint256);\n\n function tokenId() external view returns (uint256);\n\n function withdrawAll() external;\n\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external;\n\n function setWithdrawLiquidityShare(uint128 share) external;\n\n function lowerTick() external view returns (int24);\n\n function upperTick() external view returns (int24);\n\n function getPoolX96Price() external view returns (uint160 _sqrtRatioX96);\n\n function sqrtRatioX96TickLower() external view returns (uint160);\n\n function sqrtRatioX96TickHigher() external view returns (uint160);\n\n function tickSpacing() external view returns (int24);\n\n function allowedWethShareStart() external view returns (uint256);\n\n function allowedWethShareEnd() external view returns (uint256);\n\n function claimGovernance() external;\n\n function transferGovernance(address _governor) external;\n\n function getPositionPrincipal()\n external\n view\n returns (uint256 _amountWeth, uint256 _amountOethb);\n}\n" + }, + "contracts/interfaces/aerodrome/ICLGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\ninterface ICLGauge {\n /// @notice Returns the claimable rewards for a given account and tokenId\n /// @dev Throws if account is not the position owner\n /// @dev pool.updateRewardsGrowthGlobal() needs to be called first, to return the correct claimable rewards\n /// @param account The address of the user\n /// @param tokenId The tokenId of the position\n /// @return The amount of claimable reward\n function earned(address account, uint256 tokenId)\n external\n view\n returns (uint256);\n\n /// @notice Retrieve rewards for all tokens owned by an account\n /// @dev Throws if not called by the voter\n /// @param account The account of the user\n function getReward(address account) external;\n\n /// @notice Retrieve rewards for a tokenId\n /// @dev Throws if not called by the position owner\n /// @param tokenId The tokenId of the position\n function getReward(uint256 tokenId) external;\n\n /// @notice Notifies gauge of gauge rewards.\n /// @param amount Amount of gauge rewards (emissions) to notify. Must be greater than 604_800.\n function notifyRewardAmount(uint256 amount) external;\n\n /// @dev Notifies gauge of gauge rewards without distributing its fees.\n /// Assumes gauge reward tokens is 18 decimals.\n /// If not 18 decimals, rewardRate may have rounding issues.\n /// @param amount Amount of gauge rewards (emissions) to notify. Must be greater than 604_800.\n function notifyRewardWithoutClaim(uint256 amount) external;\n\n /// @notice Used to deposit a CL position into the gauge\n /// @notice Allows the user to receive emissions instead of fees\n /// @param tokenId The tokenId of the position\n function deposit(uint256 tokenId) external;\n\n /// @notice Used to withdraw a CL position from the gauge\n /// @notice Allows the user to receive fees instead of emissions\n /// @notice Outstanding emissions will be collected on withdrawal\n /// @param tokenId The tokenId of the position\n function withdraw(uint256 tokenId) external;\n\n // /// @notice Fetch all tokenIds staked by a given account\n // /// @param depositor The address of the user\n // /// @return The tokenIds of the staked positions\n // function stakedValues(address depositor) external view returns (uint256[] memory);\n\n // /// @notice Fetch a staked tokenId by index\n // /// @param depositor The address of the user\n // /// @param index The index of the staked tokenId\n // /// @return The tokenId of the staked position\n // function stakedByIndex(address depositor, uint256 index) external view returns (uint256);\n\n // /// @notice Check whether a position is staked in the gauge by a certain user\n // /// @param depositor The address of the user\n // /// @param tokenId The tokenId of the position\n // /// @return Whether the position is staked in the gauge\n // function stakedContains(address depositor, uint256 tokenId) external view returns (bool);\n\n // /// @notice The amount of positions staked in the gauge by a certain user\n // /// @param depositor The address of the user\n // /// @return The amount of positions staked in the gauge\n // function stakedLength(address depositor) external view returns (uint256);\n\n function feesVotingReward() external view returns (address);\n}\n" + }, + "contracts/interfaces/aerodrome/ICLPool.sol": { + "content": "pragma solidity >=0.5.0;\n\n/// @title The interface for a CL Pool\n/// @notice A CL pool facilitates swapping and automated market making between any two assets that strictly conform\n/// to the ERC20 specification\n/// @dev The pool interface is broken up into many smaller pieces\ninterface ICLPool {\n function slot0()\n external\n view\n returns (\n uint160 sqrtPriceX96,\n int24 tick,\n uint16 observationIndex,\n uint16 observationCardinality,\n uint16 observationCardinalityNext,\n bool unlocked\n );\n\n /// @notice The first of the two tokens of the pool, sorted by address\n /// @return The token contract address\n function token0() external view returns (address);\n\n /// @notice The second of the two tokens of the pool, sorted by address\n /// @return The token contract address\n function token1() external view returns (address);\n\n function tickSpacing() external view returns (int24);\n\n /// @notice The gauge corresponding to this pool\n /// @return The gauge contract address\n function gauge() external view returns (address);\n\n /// @notice The currently in range liquidity available to the pool\n /// @dev This value has no relationship to the total liquidity across all ticks\n /// @dev This value includes staked liquidity\n function liquidity() external view returns (uint128);\n\n /// @notice Look up information about a specific tick in the pool\n /// @param tick The tick to look up\n /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or\n /// tick upper,\n /// liquidityNet how much liquidity changes when the pool price crosses the tick,\n /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,\n /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,\n /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick\n /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from\n /// the current tick,\n /// secondsOutside the seconds spent on the other side of the tick from the current tick,\n /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise\n /// equal to false.\n /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.\n /// In addition, these values are only relative and must be used only in comparison to previous snapshots for\n /// a specific position.\n function ticks(int24 tick)\n external\n view\n returns (\n uint128 liquidityGross,\n int128 liquidityNet,\n uint256 feeGrowthOutside0X128,\n uint256 feeGrowthOutside1X128,\n int56 tickCumulativeOutside,\n uint160 secondsPerLiquidityOutsideX128,\n uint32 secondsOutside,\n bool initialized\n );\n}\n" + }, + "contracts/interfaces/aerodrome/INonfungiblePositionManager.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title Non-fungible token for positions\n/// @notice Wraps CL positions in a non-fungible token interface which allows for them to be transferred\n/// and authorized.\n// slither-disable-start erc20-interface\ninterface INonfungiblePositionManager {\n /**\n * @dev See {IERC721-approve}.\n */\n function approve(address to, uint256 tokenId) external;\n\n /**\n * @dev See {IERC721-getApproved}.\n */\n function getApproved(uint256 tokenId) external returns (address);\n\n /**\n * @dev See {IERC721-ownerOf}.\n */\n function ownerOf(uint256 tokenId) external view returns (address);\n\n /// @notice Returns the position information associated with a given token ID.\n /// @dev Throws if the token ID is not valid.\n /// @param tokenId The ID of the token that represents the position\n /// @return nonce The nonce for permits\n /// @return operator The address that is approved for spending\n /// @return token0 The address of the token0 for a specific pool\n /// @return token1 The address of the token1 for a specific pool\n /// @return tickSpacing The tick spacing associated with the pool\n /// @return tickLower The lower end of the tick range for the position\n /// @return tickUpper The higher end of the tick range for the position\n /// @return liquidity The liquidity of the position\n /// @return feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position\n /// @return feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position\n /// @return tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation\n /// @return tokensOwed1 The uncollected amount of token1 owed to the position as of the last computation\n function positions(uint256 tokenId)\n external\n view\n returns (\n uint96 nonce,\n address operator,\n address token0,\n address token1,\n int24 tickSpacing,\n int24 tickLower,\n int24 tickUpper,\n uint128 liquidity,\n uint256 feeGrowthInside0LastX128,\n uint256 feeGrowthInside1LastX128,\n uint128 tokensOwed0,\n uint128 tokensOwed1\n );\n\n struct MintParams {\n address token0;\n address token1;\n int24 tickSpacing;\n int24 tickLower;\n int24 tickUpper;\n uint256 amount0Desired;\n uint256 amount1Desired;\n uint256 amount0Min;\n uint256 amount1Min;\n address recipient;\n uint256 deadline;\n uint160 sqrtPriceX96;\n }\n\n /// @notice Creates a new position wrapped in a NFT\n /// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized\n /// a method does not exist, i.e. the pool is assumed to be initialized.\n /// @param params The params necessary to mint a position, encoded as `MintParams` in calldata\n /// @return tokenId The ID of the token that represents the minted position\n /// @return liquidity The amount of liquidity for this position\n /// @return amount0 The amount of token0\n /// @return amount1 The amount of token1\n function mint(MintParams calldata params)\n external\n payable\n returns (\n uint256 tokenId,\n uint128 liquidity,\n uint256 amount0,\n uint256 amount1\n );\n\n struct IncreaseLiquidityParams {\n uint256 tokenId;\n uint256 amount0Desired;\n uint256 amount1Desired;\n uint256 amount0Min;\n uint256 amount1Min;\n uint256 deadline;\n }\n\n /// @notice Increases the amount of liquidity in a position, with tokens paid by the `msg.sender`\n /// @param params tokenId The ID of the token for which liquidity is being increased,\n /// amount0Desired The desired amount of token0 to be spent,\n /// amount1Desired The desired amount of token1 to be spent,\n /// amount0Min The minimum amount of token0 to spend, which serves as a slippage check,\n /// amount1Min The minimum amount of token1 to spend, which serves as a slippage check,\n /// deadline The time by which the transaction must be included to effect the change\n /// @return liquidity The new liquidity amount as a result of the increase\n /// @return amount0 The amount of token0 to acheive resulting liquidity\n /// @return amount1 The amount of token1 to acheive resulting liquidity\n function increaseLiquidity(IncreaseLiquidityParams calldata params)\n external\n payable\n returns (\n uint128 liquidity,\n uint256 amount0,\n uint256 amount1\n );\n\n struct DecreaseLiquidityParams {\n uint256 tokenId;\n uint128 liquidity;\n uint256 amount0Min;\n uint256 amount1Min;\n uint256 deadline;\n }\n\n /// @notice Decreases the amount of liquidity in a position and accounts it to the position\n /// @param params tokenId The ID of the token for which liquidity is being decreased,\n /// amount The amount by which liquidity will be decreased,\n /// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity,\n /// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity,\n /// deadline The time by which the transaction must be included to effect the change\n /// @return amount0 The amount of token0 accounted to the position's tokens owed\n /// @return amount1 The amount of token1 accounted to the position's tokens owed\n /// @dev The use of this function can cause a loss to users of the NonfungiblePositionManager\n /// @dev for tokens that have very high decimals.\n /// @dev The amount of tokens necessary for the loss is: 3.4028237e+38.\n /// @dev This is equivalent to 1e20 value with 18 decimals.\n function decreaseLiquidity(DecreaseLiquidityParams calldata params)\n external\n payable\n returns (uint256 amount0, uint256 amount1);\n\n struct CollectParams {\n uint256 tokenId;\n address recipient;\n uint128 amount0Max;\n uint128 amount1Max;\n }\n\n /// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient\n /// @notice Used to update staked positions before deposit and withdraw\n /// @param params tokenId The ID of the NFT for which tokens are being collected,\n /// recipient The account that should receive the tokens,\n /// amount0Max The maximum amount of token0 to collect,\n /// amount1Max The maximum amount of token1 to collect\n /// @return amount0 The amount of fees collected in token0\n /// @return amount1 The amount of fees collected in token1\n function collect(CollectParams calldata params)\n external\n payable\n returns (uint256 amount0, uint256 amount1);\n\n /// @notice Burns a token ID, which deletes it from the NFT contract. The token must have 0 liquidity and all tokens\n /// must be collected first.\n /// @param tokenId The ID of the token that is being burned\n function burn(uint256 tokenId) external payable;\n\n /// @notice Sets a new Token Descriptor\n /// @param _tokenDescriptor Address of the new Token Descriptor to be chosen\n function setTokenDescriptor(address _tokenDescriptor) external;\n\n /// @notice Sets a new Owner address\n /// @param _owner Address of the new Owner to be chosen\n function setOwner(address _owner) external;\n}\n// slither-disable-end erc20-interface\n" + }, + "contracts/interfaces/aerodrome/IQuoterV2.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title QuoterV2 Interface\n/// @notice Supports quoting the calculated amounts from exact input or exact output swaps.\n/// @notice For each pool also tells you the number of initialized ticks crossed and the sqrt price of the\n/// pool after the swap.\n/// @dev These functions are not marked view because they rely on calling non-view functions and reverting\n/// to compute the result. They are also not gas efficient and should not be called on-chain.\ninterface IQuoterV2 {\n /// @notice Returns the amount out received for a given exact input swap without executing the swap\n /// @param path The path of the swap, i.e. each token pair and the pool tick spacing\n /// @param amountIn The amount of the first token to swap\n /// @return amountOut The amount of the last token that would be received\n /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path\n /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for\n /// each pool in the path\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactInput(bytes memory path, uint256 amountIn)\n external\n returns (\n uint256 amountOut,\n uint160[] memory sqrtPriceX96AfterList,\n uint32[] memory initializedTicksCrossedList,\n uint256 gasEstimate\n );\n\n struct QuoteExactInputSingleParams {\n address tokenIn;\n address tokenOut;\n uint256 amountIn;\n int24 tickSpacing;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Returns the amount out received for a given exact input but for a swap of a single pool\n /// @param params The params for the quote, encoded as `QuoteExactInputSingleParams`\n /// tokenIn The token being swapped in\n /// tokenOut The token being swapped out\n /// tickSpacing The tick spacing of the token pool to consider for the pair\n /// amountIn The desired input amount\n /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap\n /// @return amountOut The amount of `tokenOut` that would be received\n /// @return sqrtPriceX96After The sqrt price of the pool after the swap\n /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactInputSingle(QuoteExactInputSingleParams memory params)\n external\n returns (\n uint256 amountOut,\n uint160 sqrtPriceX96After,\n uint32 initializedTicksCrossed,\n uint256 gasEstimate\n );\n\n /// @notice Returns the amount in required for a given exact output swap without executing the swap\n /// @param path The path of the swap, i.e. each token pair and the pool tick spacing.\n /// Path must be provided in reverse order\n /// @param amountOut The amount of the last token to receive\n /// @return amountIn The amount of first token required to be paid\n /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path\n /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for\n /// each pool in the path\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactOutput(bytes memory path, uint256 amountOut)\n external\n returns (\n uint256 amountIn,\n uint160[] memory sqrtPriceX96AfterList,\n uint32[] memory initializedTicksCrossedList,\n uint256 gasEstimate\n );\n\n struct QuoteExactOutputSingleParams {\n address tokenIn;\n address tokenOut;\n uint256 amount;\n int24 tickSpacing;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Returns the amount in required to receive the given exact output amount but for a swap of a single pool\n /// @param params The params for the quote, encoded as `QuoteExactOutputSingleParams`\n /// tokenIn The token being swapped in\n /// tokenOut The token being swapped out\n /// tickSpacing The tick spacing of the token pool to consider for the pair\n /// amountOut The desired output amount\n /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap\n /// @return amountIn The amount required as the input for the swap in order to receive `amountOut`\n /// @return sqrtPriceX96After The sqrt price of the pool after the swap\n /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactOutputSingle(QuoteExactOutputSingleParams memory params)\n external\n returns (\n uint256 amountIn,\n uint160 sqrtPriceX96After,\n uint32 initializedTicksCrossed,\n uint256 gasEstimate\n );\n}\n" + }, + "contracts/interfaces/aerodrome/ISugarHelper.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\npragma abicoder v2;\n\nimport { INonfungiblePositionManager } from \"./INonfungiblePositionManager.sol\";\n\ninterface ISugarHelper {\n struct PopulatedTick {\n int24 tick;\n uint160 sqrtRatioX96;\n int128 liquidityNet;\n uint128 liquidityGross;\n }\n\n ///\n /// Wrappers for LiquidityAmounts\n ///\n\n function getAmountsForLiquidity(\n uint160 sqrtRatioX96,\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96,\n uint128 liquidity\n ) external pure returns (uint256 amount0, uint256 amount1);\n\n function getLiquidityForAmounts(\n uint256 amount0,\n uint256 amount1,\n uint160 sqrtRatioX96,\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96\n ) external pure returns (uint128 liquidity);\n\n /// @notice Computes the amount of token0 for a given amount of token1 and price range\n /// @param amount1 Amount of token1 to estimate liquidity\n /// @param pool Address of the pool to be used\n /// @param sqrtRatioX96 A sqrt price representing the current pool prices\n /// @param tickLow Lower tick boundary\n /// @param tickLow Upper tick boundary\n /// @dev If the given pool address is not the zero address, will fetch `sqrtRatioX96` from pool\n /// @return amount0 Estimated amount of token0\n function estimateAmount0(\n uint256 amount1,\n address pool,\n uint160 sqrtRatioX96,\n int24 tickLow,\n int24 tickHigh\n ) external view returns (uint256 amount0);\n\n /// @notice Computes the amount of token1 for a given amount of token0 and price range\n /// @param amount0 Amount of token0 to estimate liquidity\n /// @param pool Address of the pool to be used\n /// @param sqrtRatioX96 A sqrt price representing the current pool prices\n /// @param tickLow Lower tick boundary\n /// @param tickLow Upper tick boundary\n /// @dev If the given pool address is not the zero address, will fetch `sqrtRatioX96` from pool\n /// @return amount1 Estimated amount of token1\n function estimateAmount1(\n uint256 amount0,\n address pool,\n uint160 sqrtRatioX96,\n int24 tickLow,\n int24 tickHigh\n ) external view returns (uint256 amount1);\n\n ///\n /// Wrappers for PositionValue\n ///\n\n function principal(\n INonfungiblePositionManager positionManager,\n uint256 tokenId,\n uint160 sqrtRatioX96\n ) external view returns (uint256 amount0, uint256 amount1);\n\n function fees(INonfungiblePositionManager positionManager, uint256 tokenId)\n external\n view\n returns (uint256 amount0, uint256 amount1);\n\n ///\n /// Wrappers for TickMath\n ///\n\n function getSqrtRatioAtTick(int24 tick)\n external\n pure\n returns (uint160 sqrtRatioX96);\n\n function getTickAtSqrtRatio(uint160 sqrtRatioX96)\n external\n pure\n returns (int24 tick);\n\n /// @notice Fetches Tick Data for all populated Ticks in given bitmaps\n /// @param pool Address of the pool from which to fetch data\n /// @param startTick Tick from which the first bitmap will be fetched\n /// @dev The number of bitmaps fetched by this function should always be `MAX_BITMAPS`,\n /// unless there are less than `MAX_BITMAPS` left to iterate through\n /// @return populatedTicks Array of all Populated Ticks in the provided bitmaps\n function getPopulatedTicks(address pool, int24 startTick)\n external\n view\n returns (PopulatedTick[] memory populatedTicks);\n}\n" + }, + "contracts/interfaces/aerodrome/ISwapRouter.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title Router token swapping functionality\n/// @notice Functions for swapping tokens via CL\ninterface ISwapRouter {\n struct ExactInputSingleParams {\n address tokenIn;\n address tokenOut;\n int24 tickSpacing;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInputSingle(ExactInputSingleParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactOutputSingleParams {\n address tokenIn;\n address tokenOut;\n int24 tickSpacing;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutputSingle(ExactOutputSingleParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n\n struct ExactOutputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutput(ExactOutputParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n}\n" + }, + "contracts/interfaces/balancer/IBalancerVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"../../utils/InitializableAbstractStrategy.sol\";\n\ninterface IBalancerVault {\n enum WeightedPoolJoinKind {\n INIT,\n EXACT_TOKENS_IN_FOR_BPT_OUT,\n TOKEN_IN_FOR_EXACT_BPT_OUT,\n ALL_TOKENS_IN_FOR_EXACT_BPT_OUT,\n ADD_TOKEN\n }\n\n enum WeightedPoolExitKind {\n EXACT_BPT_IN_FOR_ONE_TOKEN_OUT,\n EXACT_BPT_IN_FOR_TOKENS_OUT,\n BPT_IN_FOR_EXACT_TOKENS_OUT,\n REMOVE_TOKEN\n }\n\n /**\n * @dev Called by users to join a Pool, which transfers tokens from `sender` into the Pool's balance. This will\n * trigger custom Pool behavior, which will typically grant something in return to `recipient` - often tokenized\n * Pool shares.\n *\n * If the caller is not `sender`, it must be an authorized relayer for them.\n *\n * The `assets` and `maxAmountsIn` arrays must have the same length, and each entry indicates the maximum amount\n * to send for each asset. The amounts to send are decided by the Pool and not the Vault: it just enforces\n * these maximums.\n *\n * If joining a Pool that holds WETH, it is possible to send ETH directly: the Vault will do the wrapping. To enable\n * this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead of the\n * WETH address. Note that it is not possible to combine ETH and WETH in the same join. Any excess ETH will be sent\n * back to the caller (not the sender, which is important for relayers).\n *\n * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when\n * interacting with Pools that register and deregister tokens frequently. If sending ETH however, the array must be\n * sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the final\n * `assets` array might not be sorted. Pools with no registered tokens cannot be joined.\n *\n * If `fromInternalBalance` is true, the caller's Internal Balance will be preferred: ERC20 transfers will only\n * be made for the difference between the requested amount and Internal Balance (if any). Note that ETH cannot be\n * withdrawn from Internal Balance: attempting to do so will trigger a revert.\n *\n * This causes the Vault to call the `IBasePool.onJoinPool` hook on the Pool's contract, where Pools implement\n * their own custom logic. This typically requires additional information from the user (such as the expected number\n * of Pool shares). This can be encoded in the `userData` argument, which is ignored by the Vault and passed\n * directly to the Pool's contract, as is `recipient`.\n *\n * Emits a `PoolBalanceChanged` event.\n */\n function joinPool(\n bytes32 poolId,\n address sender,\n address recipient,\n JoinPoolRequest memory request\n ) external payable;\n\n struct JoinPoolRequest {\n address[] assets;\n uint256[] maxAmountsIn;\n bytes userData;\n bool fromInternalBalance;\n }\n\n /**\n * @dev Called by users to exit a Pool, which transfers tokens from the Pool's balance to `recipient`. This will\n * trigger custom Pool behavior, which will typically ask for something in return from `sender` - often tokenized\n * Pool shares. The amount of tokens that can be withdrawn is limited by the Pool's `cash` balance (see\n * `getPoolTokenInfo`).\n *\n * If the caller is not `sender`, it must be an authorized relayer for them.\n *\n * The `tokens` and `minAmountsOut` arrays must have the same length, and each entry in these indicates the minimum\n * token amount to receive for each token contract. The amounts to send are decided by the Pool and not the Vault:\n * it just enforces these minimums.\n *\n * If exiting a Pool that holds WETH, it is possible to receive ETH directly: the Vault will do the unwrapping. To\n * enable this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead\n * of the WETH address. Note that it is not possible to combine ETH and WETH in the same exit.\n *\n * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when\n * interacting with Pools that register and deregister tokens frequently. If receiving ETH however, the array must\n * be sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the\n * final `assets` array might not be sorted. Pools with no registered tokens cannot be exited.\n *\n * If `toInternalBalance` is true, the tokens will be deposited to `recipient`'s Internal Balance. Otherwise,\n * an ERC20 transfer will be performed. Note that ETH cannot be deposited to Internal Balance: attempting to\n * do so will trigger a revert.\n *\n * `minAmountsOut` is the minimum amount of tokens the user expects to get out of the Pool, for each token in the\n * `tokens` array. This array must match the Pool's registered tokens.\n *\n * This causes the Vault to call the `IBasePool.onExitPool` hook on the Pool's contract, where Pools implement\n * their own custom logic. This typically requires additional information from the user (such as the expected number\n * of Pool shares to return). This can be encoded in the `userData` argument, which is ignored by the Vault and\n * passed directly to the Pool's contract.\n *\n * Emits a `PoolBalanceChanged` event.\n */\n function exitPool(\n bytes32 poolId,\n address sender,\n address payable recipient,\n ExitPoolRequest memory request\n ) external;\n\n struct ExitPoolRequest {\n address[] assets;\n uint256[] minAmountsOut;\n bytes userData;\n bool toInternalBalance;\n }\n\n /**\n * @dev Returns a Pool's registered tokens, the total balance for each, and the latest block when *any* of\n * the tokens' `balances` changed.\n *\n * The order of the `tokens` array is the same order that will be used in `joinPool`, `exitPool`, as well as in all\n * Pool hooks (where applicable). Calls to `registerTokens` and `deregisterTokens` may change this order.\n *\n * If a Pool only registers tokens once, and these are sorted in ascending order, they will be stored in the same\n * order as passed to `registerTokens`.\n *\n * Total balances include both tokens held by the Vault and those withdrawn by the Pool's Asset Managers. These are\n * the amounts used by joins, exits and swaps. For a detailed breakdown of token balances, use `getPoolTokenInfo`\n * instead.\n */\n function getPoolTokens(bytes32 poolId)\n external\n view\n returns (\n IERC20[] memory tokens,\n uint256[] memory balances,\n uint256 lastChangeBlock\n );\n\n /**\n * @dev Performs a set of user balance operations, which involve Internal Balance (deposit, withdraw or transfer)\n * and plain ERC20 transfers using the Vault's allowance. This last feature is particularly useful for relayers, as\n * it lets integrators reuse a user's Vault allowance.\n *\n * For each operation, if the caller is not `sender`, it must be an authorized relayer for them.\n */\n function manageUserBalance(UserBalanceOp[] memory ops) external payable;\n\n struct UserBalanceOp {\n UserBalanceOpKind kind;\n address asset;\n uint256 amount;\n address sender;\n address payable recipient;\n }\n\n enum UserBalanceOpKind {\n DEPOSIT_INTERNAL,\n WITHDRAW_INTERNAL,\n TRANSFER_INTERNAL,\n TRANSFER_EXTERNAL\n }\n\n enum SwapKind {\n GIVEN_IN,\n GIVEN_OUT\n }\n\n struct SingleSwap {\n bytes32 poolId;\n SwapKind kind;\n address assetIn;\n address assetOut;\n uint256 amount;\n bytes userData;\n }\n\n struct FundManagement {\n address sender;\n bool fromInternalBalance;\n address payable recipient;\n bool toInternalBalance;\n }\n\n function swap(\n SingleSwap calldata singleSwap,\n FundManagement calldata funds,\n uint256 limit,\n uint256 deadline\n ) external returns (uint256 amountCalculated);\n\n function getPoolTokenInfo(bytes32 poolId, address token)\n external\n view\n returns (\n uint256 cash,\n uint256 managed,\n uint256 lastChangeBlock,\n address assetManager\n );\n}\n" + }, + "contracts/interfaces/balancer/IMetaStablePool.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IRateProvider } from \"./IRateProvider.sol\";\n\ninterface IMetaStablePool {\n function getRateProviders()\n external\n view\n returns (IRateProvider[] memory providers);\n}\n" + }, + "contracts/interfaces/balancer/IOracleWeightedPool.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// The three values that can be queried:\n//\n// - PAIR_PRICE: the price of the tokens in the Pool, expressed as the price of the second token in units of the\n// first token. For example, if token A is worth $2, and token B is worth $4, the pair price will be 2.0.\n// Note that the price is computed *including* the tokens decimals. This means that the pair price of a Pool with\n// DAI and USDC will be close to 1.0, despite DAI having 18 decimals and USDC 6.\n//\n// - BPT_PRICE: the price of the Pool share token (BPT), in units of the first token.\n// Note that the price is computed *including* the tokens decimals. This means that the BPT price of a Pool with\n// USDC in which BPT is worth $5 will be 5.0, despite the BPT having 18 decimals and USDC 6.\n//\n// - INVARIANT: the value of the Pool's invariant, which serves as a measure of its liquidity.\nenum Variable {\n PAIR_PRICE,\n BPT_PRICE,\n INVARIANT\n}\n\n/**\n * @dev Information for a Time Weighted Average query.\n *\n * Each query computes the average over a window of duration `secs` seconds that ended `ago` seconds ago. For\n * example, the average over the past 30 minutes is computed by settings secs to 1800 and ago to 0. If secs is 1800\n * and ago is 1800 as well, the average between 60 and 30 minutes ago is computed instead.\n */\nstruct OracleAverageQuery {\n Variable variable;\n uint256 secs;\n uint256 ago;\n}\n\ninterface IOracleWeightedPool {\n /**\n * @dev Returns the time average weighted price corresponding to each of `queries`. Prices are represented as 18\n * decimal fixed point values.\n */\n function getTimeWeightedAverage(OracleAverageQuery[] memory queries)\n external\n view\n returns (uint256[] memory results);\n}\n" + }, + "contracts/interfaces/balancer/IRateProvider.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity ^0.8.0;\n\ninterface IRateProvider {\n function getRate() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/chainlink/AggregatorV3Interface.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorV3Interface {\n function decimals() external view returns (uint8);\n\n function description() external view returns (string memory);\n\n function version() external view returns (uint256);\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(uint80 _roundId)\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n\n function latestRoundData()\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof,\n bytes calldata validatorPubKeyProof\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint64 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes32 pubKeyHash,\n bytes calldata firstPendingDepositProof\n ) external view returns (bool isEmptyDepositQueue);\n}\n" + }, + "contracts/interfaces/IBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBuyback {\n function swap() external;\n}\n" + }, + "contracts/interfaces/ICampaignRemoteManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICampaignRemoteManager {\n function createCampaign(\n CampaignCreationParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n function manageCampaign(\n CampaignManagementParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n function closeCampaign(\n CampaignClosingParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n struct CampaignCreationParams {\n uint256 chainId;\n address gauge;\n address manager;\n address rewardToken;\n uint8 numberOfPeriods;\n uint256 maxRewardPerVote;\n uint256 totalRewardAmount;\n address[] addresses;\n address hook;\n bool isWhitelist;\n }\n\n struct CampaignManagementParams {\n uint256 campaignId;\n address rewardToken;\n uint8 numberOfPeriods;\n uint256 totalRewardAmount;\n uint256 maxRewardPerVote;\n }\n\n struct CampaignClosingParams {\n uint256 campaignId;\n }\n}\n" + }, + "contracts/interfaces/IChildLiquidityGaugeFactory.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IChildLiquidityGaugeFactory {\n event DeployedGauge(\n address indexed _implementation,\n address indexed _lp_token,\n address indexed _deployer,\n bytes32 _salt,\n address _gauge\n );\n event Minted(\n address indexed _user,\n address indexed _gauge,\n uint256 _new_total\n );\n event TransferOwnership(address _old_owner, address _new_owner);\n event UpdateCallProxy(address _old_call_proxy, address _new_call_proxy);\n event UpdateImplementation(\n address _old_implementation,\n address _new_implementation\n );\n event UpdateManager(address _manager);\n event UpdateMirrored(address indexed _gauge, bool _mirrored);\n event UpdateRoot(address _factory, address _implementation);\n event UpdateVotingEscrow(\n address _old_voting_escrow,\n address _new_voting_escrow\n );\n\n function accept_transfer_ownership() external;\n\n function call_proxy() external view returns (address);\n\n function commit_transfer_ownership(address _future_owner) external;\n\n function crv() external view returns (address);\n\n function deploy_gauge(address _lp_token, bytes32 _salt)\n external\n returns (address);\n\n function deploy_gauge(\n address _lp_token,\n bytes32 _salt,\n address _manager\n ) external returns (address);\n\n function future_owner() external view returns (address);\n\n function gauge_data(address arg0) external view returns (uint256);\n\n function get_gauge(uint256 arg0) external view returns (address);\n\n function get_gauge_count() external view returns (uint256);\n\n function get_gauge_from_lp_token(address arg0)\n external\n view\n returns (address);\n\n function get_implementation() external view returns (address);\n\n function is_mirrored(address _gauge) external view returns (bool);\n\n function is_valid_gauge(address _gauge) external view returns (bool);\n\n function last_request(address _gauge) external view returns (uint256);\n\n function manager() external view returns (address);\n\n function mint(address _gauge) external;\n\n function mint_many(address[32] memory _gauges) external;\n\n function minted(address arg0, address arg1) external view returns (uint256);\n\n function owner() external view returns (address);\n\n function root_factory() external view returns (address);\n\n function root_implementation() external view returns (address);\n\n function set_call_proxy(address _new_call_proxy) external;\n\n function set_crv(address _crv) external;\n\n function set_implementation(address _implementation) external;\n\n function set_manager(address _new_manager) external;\n\n function set_mirrored(address _gauge, bool _mirrored) external;\n\n function set_root(address _factory, address _implementation) external;\n\n function set_voting_escrow(address _voting_escrow) external;\n\n function version() external view returns (string memory);\n\n function voting_escrow() external view returns (address);\n}\n" + }, + "contracts/interfaces/IComptroller.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IComptroller {\n // Claim all the COMP accrued by specific holders in specific markets for their supplies and/or borrows\n function claimComp(\n address[] memory holders,\n address[] memory cTokens,\n bool borrowers,\n bool suppliers\n ) external;\n\n function oracle() external view returns (address);\n}\n" + }, + "contracts/interfaces/IConsolidation.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IConsolidationSource {\n function confirmConsolidation()\n external\n returns (uint256 consolidationCount);\n}\n\ninterface IConsolidationTarget {\n function requestConsolidation(\n bytes32 lastSourcePubKeyHash,\n bytes32 targetPubKeyHash\n ) external;\n}\n" + }, + "contracts/interfaces/ICurveLiquidityGaugeV6.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveLiquidityGaugeV6 {\n event ApplyOwnership(address admin);\n event Approval(\n address indexed _owner,\n address indexed _spender,\n uint256 _value\n );\n event CommitOwnership(address admin);\n event Deposit(address indexed provider, uint256 value);\n event SetGaugeManager(address _gauge_manager);\n event Transfer(address indexed _from, address indexed _to, uint256 _value);\n event UpdateLiquidityLimit(\n address indexed user,\n uint256 original_balance,\n uint256 original_supply,\n uint256 working_balance,\n uint256 working_supply\n );\n event Withdraw(address indexed provider, uint256 value);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_reward(address _reward_token, address _distributor) external;\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function claim_rewards() external;\n\n function claim_rewards(address _addr) external;\n\n function claim_rewards(address _addr, address _receiver) external;\n\n function claimable_reward(address _user, address _reward_token)\n external\n view\n returns (uint256);\n\n function claimable_tokens(address addr) external returns (uint256);\n\n function claimed_reward(address _addr, address _token)\n external\n view\n returns (uint256);\n\n function decimals() external view returns (uint256);\n\n function decreaseAllowance(address _spender, uint256 _subtracted_value)\n external\n returns (bool);\n\n function deposit(uint256 _value) external;\n\n function deposit(uint256 _value, address _addr) external;\n\n function deposit(\n uint256 _value,\n address _addr,\n bool _claim_rewards\n ) external;\n\n function deposit_reward_token(address _reward_token, uint256 _amount)\n external;\n\n function deposit_reward_token(\n address _reward_token,\n uint256 _amount,\n uint256 _epoch\n ) external;\n\n function factory() external view returns (address);\n\n function future_epoch_time() external view returns (uint256);\n\n function increaseAllowance(address _spender, uint256 _added_value)\n external\n returns (bool);\n\n function inflation_rate() external view returns (uint256);\n\n function integrate_checkpoint() external view returns (uint256);\n\n function integrate_checkpoint_of(address arg0)\n external\n view\n returns (uint256);\n\n function integrate_fraction(address arg0) external view returns (uint256);\n\n function integrate_inv_supply(uint256 arg0) external view returns (uint256);\n\n function integrate_inv_supply_of(address arg0)\n external\n view\n returns (uint256);\n\n function is_killed() external view returns (bool);\n\n function kick(address addr) external;\n\n function lp_token() external view returns (address);\n\n function manager() external view returns (address);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function period() external view returns (int128);\n\n function period_timestamp(uint256 arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function reward_count() external view returns (uint256);\n\n function reward_integral_for(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function reward_tokens(uint256 arg0) external view returns (address);\n\n function rewards_receiver(address arg0) external view returns (address);\n\n function salt() external view returns (bytes32);\n\n function set_gauge_manager(address _gauge_manager) external;\n\n function set_killed(bool _is_killed) external;\n\n function set_reward_distributor(address _reward_token, address _distributor)\n external;\n\n function set_rewards_receiver(address _receiver) external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function user_checkpoint(address addr) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw(uint256 _value) external;\n\n function withdraw(uint256 _value, bool _claim_rewards) external;\n\n function working_balances(address arg0) external view returns (uint256);\n\n function working_supply() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/ICurveMinter.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveMinter {\n event Minted(address indexed recipient, address gauge, uint256 minted);\n\n function allowed_to_mint_for(address arg0, address arg1)\n external\n view\n returns (bool);\n\n function controller() external view returns (address);\n\n function mint(address gauge_addr) external;\n\n function mint_for(address gauge_addr, address _for) external;\n\n function mint_many(address[8] memory gauge_addrs) external;\n\n function minted(address arg0, address arg1) external view returns (uint256);\n\n function toggle_approve_mint(address minting_user) external;\n\n function token() external view returns (address);\n}\n" + }, + "contracts/interfaces/ICurveStableSwapNG.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveStableSwapNG {\n event AddLiquidity(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event ApplyNewFee(uint256 fee, uint256 offpeg_fee_multiplier);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event RampA(\n uint256 old_A,\n uint256 new_A,\n uint256 initial_time,\n uint256 future_time\n );\n event RemoveLiquidity(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 token_supply\n );\n event RemoveLiquidityImbalance(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event RemoveLiquidityOne(\n address indexed provider,\n int128 token_id,\n uint256 token_amount,\n uint256 coin_amount,\n uint256 token_supply\n );\n event SetNewMATime(uint256 ma_exp_time, uint256 D_ma_time);\n event StopRampA(uint256 A, uint256 t);\n event TokenExchange(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event TokenExchangeUnderlying(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event Transfer(\n address indexed sender,\n address indexed receiver,\n uint256 value\n );\n\n function A() external view returns (uint256);\n\n function A_precise() external view returns (uint256);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function D_ma_time() external view returns (uint256);\n\n function D_oracle() external view returns (uint256);\n\n function N_COINS() external view returns (uint256);\n\n function add_liquidity(uint256[] memory _amounts, uint256 _min_mint_amount)\n external\n returns (uint256);\n\n function add_liquidity(\n uint256[] memory _amounts,\n uint256 _min_mint_amount,\n address _receiver\n ) external returns (uint256);\n\n function admin_balances(uint256 arg0) external view returns (uint256);\n\n function admin_fee() external view returns (uint256);\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function balances(uint256 i) external view returns (uint256);\n\n function calc_token_amount(uint256[] memory _amounts, bool _is_deposit)\n external\n view\n returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _burn_amount, int128 i)\n external\n view\n returns (uint256);\n\n function coins(uint256 arg0) external view returns (address);\n\n function decimals() external view returns (uint8);\n\n function dynamic_fee(int128 i, int128 j) external view returns (uint256);\n\n function ema_price(uint256 i) external view returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external returns (uint256);\n\n function exchange_received(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external returns (uint256);\n\n function exchange_received(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external returns (uint256);\n\n function fee() external view returns (uint256);\n\n function future_A() external view returns (uint256);\n\n function future_A_time() external view returns (uint256);\n\n function get_balances() external view returns (uint256[] memory);\n\n function get_dx(\n int128 i,\n int128 j,\n uint256 dy\n ) external view returns (uint256);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n\n function get_p(uint256 i) external view returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function initial_A() external view returns (uint256);\n\n function initial_A_time() external view returns (uint256);\n\n function last_price(uint256 i) external view returns (uint256);\n\n function ma_exp_time() external view returns (uint256);\n\n function ma_last_time() external view returns (uint256);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function offpeg_fee_multiplier() external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function price_oracle(uint256 i) external view returns (uint256);\n\n function ramp_A(uint256 _future_A, uint256 _future_time) external;\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts\n ) external returns (uint256[] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts,\n address _receiver\n ) external returns (uint256[] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts,\n address _receiver,\n bool _claim_admin_fees\n ) external returns (uint256[] memory);\n\n function remove_liquidity_imbalance(\n uint256[] memory _amounts,\n uint256 _max_burn_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[] memory _amounts,\n uint256 _max_burn_amount,\n address _receiver\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received,\n address _receiver\n ) external returns (uint256);\n\n function salt() external view returns (bytes32);\n\n function set_ma_exp_time(uint256 _ma_exp_time, uint256 _D_ma_time) external;\n\n function set_new_fee(uint256 _new_fee, uint256 _new_offpeg_fee_multiplier)\n external;\n\n function stop_ramp_A() external;\n\n function stored_rates() external view returns (uint256[] memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw_admin_fees() external;\n}\n" + }, + "contracts/interfaces/ICurveXChainLiquidityGauge.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveXChainLiquidityGauge {\n event Approval(\n address indexed _owner,\n address indexed _spender,\n uint256 _value\n );\n event Deposit(address indexed provider, uint256 value);\n event SetGaugeManager(address _gauge_manager);\n event Transfer(address indexed _from, address indexed _to, uint256 _value);\n event UpdateLiquidityLimit(\n address indexed user,\n uint256 original_balance,\n uint256 original_supply,\n uint256 working_balance,\n uint256 working_supply\n );\n event Withdraw(address indexed provider, uint256 value);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_reward(address _reward_token, address _distributor) external;\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function claim_rewards() external;\n\n function claim_rewards(address _addr) external;\n\n function claim_rewards(address _addr, address _receiver) external;\n\n function claimable_reward(address _user, address _reward_token)\n external\n view\n returns (uint256);\n\n function claimable_tokens(address addr) external returns (uint256);\n\n function claimed_reward(address _addr, address _token)\n external\n view\n returns (uint256);\n\n function decimals() external view returns (uint256);\n\n function decreaseAllowance(address _spender, uint256 _subtracted_value)\n external\n returns (bool);\n\n function deposit(uint256 _value) external;\n\n function deposit(uint256 _value, address _addr) external;\n\n function deposit(\n uint256 _value,\n address _addr,\n bool _claim_rewards\n ) external;\n\n function deposit_reward_token(address _reward_token, uint256 _amount)\n external;\n\n function deposit_reward_token(\n address _reward_token,\n uint256 _amount,\n uint256 _epoch\n ) external;\n\n function factory() external view returns (address);\n\n function increaseAllowance(address _spender, uint256 _added_value)\n external\n returns (bool);\n\n function inflation_rate(uint256 arg0) external view returns (uint256);\n\n function initialize(\n address _lp_token,\n address _root,\n address _manager\n ) external;\n\n function integrate_checkpoint() external view returns (uint256);\n\n function integrate_checkpoint_of(address arg0)\n external\n view\n returns (uint256);\n\n function integrate_fraction(address arg0) external view returns (uint256);\n\n function integrate_inv_supply(int128 arg0) external view returns (uint256);\n\n function integrate_inv_supply_of(address arg0)\n external\n view\n returns (uint256);\n\n function is_killed() external view returns (bool);\n\n function lp_token() external view returns (address);\n\n function manager() external view returns (address);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function period() external view returns (int128);\n\n function period_timestamp(int128 arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function recover_remaining(address _reward_token) external;\n\n function reward_count() external view returns (uint256);\n\n function reward_integral_for(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function reward_remaining(address arg0) external view returns (uint256);\n\n function reward_tokens(uint256 arg0) external view returns (address);\n\n function rewards_receiver(address arg0) external view returns (address);\n\n function root_gauge() external view returns (address);\n\n function set_gauge_manager(address _gauge_manager) external;\n\n function set_killed(bool _is_killed) external;\n\n function set_manager(address _gauge_manager) external;\n\n function set_reward_distributor(address _reward_token, address _distributor)\n external;\n\n function set_rewards_receiver(address _receiver) external;\n\n function set_root_gauge(address _root) external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function update_voting_escrow() external;\n\n function user_checkpoint(address addr) external returns (bool);\n\n function version() external view returns (string memory);\n\n function voting_escrow() external view returns (address);\n\n function withdraw(uint256 _value) external;\n\n function withdraw(uint256 _value, bool _claim_rewards) external;\n\n function withdraw(\n uint256 _value,\n bool _claim_rewards,\n address _receiver\n ) external;\n\n function working_balances(address arg0) external view returns (uint256);\n\n function working_supply() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/ICVXLocker.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface ICVXLocker {\n function lock(\n address _account,\n uint256 _amount,\n uint256 _spendRatio\n ) external;\n\n function lockedBalanceOf(address _account) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/IDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IDripper {\n /// @notice How much funds have dripped out already and are currently\n // available to be sent to the vault.\n /// @return The amount that would be sent if a collect was called\n function availableFunds() external view returns (uint256);\n\n /// @notice Collect all dripped funds and send to vault.\n /// Recalculate new drip rate.\n function collect() external;\n\n /// @notice Collect all dripped funds, send to vault, recalculate new drip\n /// rate, and rebase mToken.\n function collectAndRebase() external;\n\n /// @notice Change the drip duration. Governor only.\n /// @param _durationSeconds the number of seconds to drip out the entire\n /// balance over if no collects were called during that time.\n function setDripDuration(uint256 _durationSeconds) external;\n\n /// @dev Transfer out ERC20 tokens held by the contract. Governor only.\n /// @param _asset ERC20 token address\n /// @param _amount amount to transfer\n function transferToken(address _asset, uint256 _amount) external;\n}\n" + }, + "contracts/interfaces/IEthUsdOracle.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IEthUsdOracle {\n /**\n * @notice Returns ETH price in USD.\n * @return Price in USD with 6 decimal digits.\n */\n function ethUsdPrice() external view returns (uint256);\n\n /**\n * @notice Returns token price in USD.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in USD with 6 decimal digits.\n */\n function tokUsdPrice(string calldata symbol)\n external\n view\n returns (uint256);\n\n /**\n * @notice Returns the asset price in ETH.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in ETH with 8 decimal digits.\n */\n function tokEthPrice(string calldata symbol)\n external\n view\n returns (uint256);\n}\n\ninterface IViewEthUsdOracle {\n /**\n * @notice Returns ETH price in USD.\n * @return Price in USD with 6 decimal digits.\n */\n function ethUsdPrice() external view returns (uint256);\n\n /**\n * @notice Returns token price in USD.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in USD with 6 decimal digits.\n */\n function tokUsdPrice(string calldata symbol)\n external\n view\n returns (uint256);\n\n /**\n * @notice Returns the asset price in ETH.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in ETH with 8 decimal digits.\n */\n function tokEthPrice(string calldata symbol)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/IFraxETHMinter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IFraxETHMinter {\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares);\n}\n" + }, + "contracts/interfaces/IGetExchangeRateToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IGetExchangeRateToken {\n function getExchangeRate() external view returns (uint256 _exchangeRate);\n}\n" + }, + "contracts/interfaces/IMinMaxOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IMinMaxOracle {\n //Assuming 8 decimals\n function priceMin(string calldata symbol) external view returns (uint256);\n\n function priceMax(string calldata symbol) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IMockVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"./IVault.sol\";\n\ninterface IMockVault is IVault {\n function outstandingWithdrawalsAmount() external view returns (uint256);\n\n function wethAvailable() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IOETHZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOETHZapper {\n function deposit() external payable returns (uint256);\n}\n" + }, + "contracts/interfaces/IOneInch.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/// 1Inch swap data\nstruct SwapDescription {\n IERC20 srcToken; // contract address of a token to sell\n IERC20 dstToken; // contract address of a token to buy\n address payable srcReceiver;\n address payable dstReceiver; // Receiver of destination currency. default: fromAddress\n uint256 amount;\n uint256 minReturnAmount;\n uint256 flags;\n}\n\n/// @title Interface for making arbitrary calls during swap\ninterface IAggregationExecutor {\n /// @notice propagates information about original msg.sender and executes arbitrary data\n function execute(address msgSender) external payable; // 0x4b64e492\n}\n\ninterface IOneInchRouter {\n /// @notice Performs a swap, delegating all calls encoded in `data` to `executor`.\n function swap(\n IAggregationExecutor executor,\n SwapDescription calldata desc,\n bytes calldata permit,\n bytes calldata data\n ) external returns (uint256 returnAmount, uint256 spentAmount);\n\n /// @notice Performs swap using Uniswap exchange. Wraps and unwraps ETH if required.\n function unoswapTo(\n address payable recipient,\n IERC20 srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) external payable returns (uint256 returnAmount);\n\n /// @notice Performs swap using Uniswap V3 exchange. Wraps and unwraps ETH if required.\n function uniswapV3SwapTo(\n address payable recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) external payable returns (uint256 returnAmount);\n}\n" + }, + "contracts/interfaces/IOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOracle {\n /**\n * @dev returns the asset price in USD, in 8 decimal digits.\n *\n * The version of priceProvider deployed for OETH has 18 decimal digits\n */\n function price(address asset) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IOUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOUSD {\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n function _totalSupply() external view returns (uint256);\n\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address _account) external view returns (uint256);\n\n function burn(address account, uint256 amount) external;\n\n function changeSupply(uint256 _newTotalSupply) external;\n\n function claimGovernance() external;\n\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256);\n\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n );\n\n function decimals() external view returns (uint8);\n\n function decreaseAllowance(address _spender, uint256 _subtractedValue)\n external\n returns (bool);\n\n function governor() external view returns (address);\n\n function increaseAllowance(address _spender, uint256 _addedValue)\n external\n returns (bool);\n\n function initialize(\n string memory _nameArg,\n string memory _symbolArg,\n address _vaultAddress\n ) external;\n\n function isGovernor() external view returns (bool);\n\n function isUpgraded(address) external view returns (uint256);\n\n function mint(address _account, uint256 _amount) external;\n\n function name() external view returns (string memory);\n\n function nonRebasingCreditsPerToken(address)\n external\n view\n returns (uint256);\n\n function nonRebasingSupply() external view returns (uint256);\n\n function rebaseOptIn() external;\n\n function rebaseOptOut() external;\n\n function rebaseState(address) external view returns (uint8);\n\n function rebasingCredits() external view returns (uint256);\n\n function rebasingCreditsHighres() external view returns (uint256);\n\n function rebasingCreditsPerToken() external view returns (uint256);\n\n function rebasingCreditsPerTokenHighres() external view returns (uint256);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function transferGovernance(address _newGovernor) external;\n\n function vaultAddress() external view returns (address);\n}\n" + }, + "contracts/interfaces/IPriceOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPriceOracle {\n /**\n * @dev returns the asset price in USD, 6 decimal digits.\n * Compatible with the Open Price Feed.\n */\n function price(string calldata symbol) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IRETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IRETH {\n function getEthValue(uint256 _rethAmount) external view returns (uint256);\n\n function getRethValue(uint256 _ethAmount) external view returns (uint256);\n\n function getExchangeRate() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function balanceOf(address account) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 amount) external returns (bool);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/ISafe.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ISafe {\n function execTransactionFromModule(\n address,\n uint256,\n bytes memory,\n uint8\n ) external returns (bool);\n}\n" + }, + "contracts/interfaces/ISfrxETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface ISfrxETH {\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 amount\n );\n event Deposit(\n address indexed caller,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n event NewRewardsCycle(uint32 indexed cycleEnd, uint256 rewardAmount);\n event Transfer(address indexed from, address indexed to, uint256 amount);\n event Withdraw(\n address indexed caller,\n address indexed receiver,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address spender, uint256 amount) external returns (bool);\n\n function asset() external view returns (address);\n\n function balanceOf(address) external view returns (uint256);\n\n function convertToAssets(uint256 shares) external view returns (uint256);\n\n function convertToShares(uint256 assets) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit(uint256 assets, address receiver)\n external\n returns (uint256 shares);\n\n function depositWithSignature(\n uint256 assets,\n address receiver,\n uint256 deadline,\n bool approveMax,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external returns (uint256 shares);\n\n function lastRewardAmount() external view returns (uint192);\n\n function lastSync() external view returns (uint32);\n\n function maxDeposit(address) external view returns (uint256);\n\n function maxMint(address) external view returns (uint256);\n\n function maxRedeem(address owner) external view returns (uint256);\n\n function maxWithdraw(address owner) external view returns (uint256);\n\n function mint(uint256 shares, address receiver)\n external\n returns (uint256 assets);\n\n function name() external view returns (string memory);\n\n function nonces(address) external view returns (uint256);\n\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function previewDeposit(uint256 assets) external view returns (uint256);\n\n function previewMint(uint256 shares) external view returns (uint256);\n\n function previewRedeem(uint256 shares) external view returns (uint256);\n\n function previewWithdraw(uint256 assets) external view returns (uint256);\n\n function pricePerShare() external view returns (uint256);\n\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets);\n\n function rewardsCycleEnd() external view returns (uint32);\n\n function rewardsCycleLength() external view returns (uint32);\n\n function symbol() external view returns (string memory);\n\n function syncRewards() external;\n\n function totalAssets() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address to, uint256 amount) external returns (bool);\n\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/ISwapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ISwapper {\n /**\n * @param fromAsset The token address of the asset being sold.\n * @param toAsset The token address of the asset being purchased.\n * @param fromAssetAmount The amount of assets being sold.\n * @param minToAssetAmmount The minimum amount of assets to be purchased.\n * @param data tx.data returned from 1Inch's /v5.0/1/swap API\n */\n function swap(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n}\n" + }, + "contracts/interfaces/ITimelock.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ITimelock {\n function delay() external view returns (uint256);\n\n function GRACE_PERIOD() external view returns (uint256);\n\n function acceptAdmin() external;\n\n function queuedTransactions(bytes32 hash) external view returns (bool);\n\n function queueTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external returns (bytes32);\n\n function cancelTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external;\n\n function executeTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external payable returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ITimelockController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ITimelockController {\n function grantRole(bytes32 role, address account) external;\n\n function revokeRole(bytes32 role, address account) external;\n\n function renounceRole(bytes32 role, address account) external;\n\n function hasRole(bytes32 role, address account)\n external\n view\n returns (bool);\n\n function executeBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt\n ) external payable;\n\n function scheduleBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt,\n uint256 delay\n ) external;\n\n function hashOperationBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt\n ) external view returns (bytes32);\n\n function isOperationDone(bytes32 opHash) external view returns (bool);\n\n function isOperationReady(bytes32 opHash) external view returns (bool);\n\n function isOperation(bytes32 opHash) external view returns (bool);\n\n function getMinDelay() external view returns (uint256);\n\n function updateDelay(uint256 newDelay) external;\n\n function CANCELLER_ROLE() external view returns (bytes32);\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/interfaces/IWstETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWstETH {\n /**\n * @notice Get amount of wstETH for a given amount of stETH\n * @param _stETHAmount amount of stETH\n * @return Amount of wstETH for a given stETH amount\n */\n function getWstETHByStETH(uint256 _stETHAmount)\n external\n view\n returns (uint256);\n\n /**\n * @notice Get amount of stETH for a given amount of wstETH\n * @param _wstETHAmount amount of wstETH\n * @return Amount of stETH for a given wstETH amount\n */\n function getStETHByWstETH(uint256 _wstETHAmount)\n external\n view\n returns (uint256);\n\n /**\n * @notice Get amount of stETH for a one wstETH\n * @return Amount of stETH for 1 wstETH\n */\n function stEthPerToken() external view returns (uint256);\n\n /**\n * @notice Get amount of wstETH for a one stETH\n * @return Amount of wstETH for a 1 stETH\n */\n function tokensPerStEth() external view returns (uint256);\n\n /**\n * @notice Exchanges stETH to wstETH\n * @param _stETHAmount amount of stETH to wrap in exchange for wstETH\n * @dev Requirements:\n * - `_stETHAmount` must be non-zero\n * - msg.sender must approve at least `_stETHAmount` stETH to this\n * contract.\n * - msg.sender must have at least `_stETHAmount` of stETH.\n * User should first approve _stETHAmount to the WstETH contract\n * @return Amount of wstETH user receives after wrap\n */\n function wrap(uint256 _stETHAmount) external returns (uint256);\n\n /**\n * @notice Exchanges wstETH to stETH\n * @param _wstETHAmount amount of wstETH to uwrap in exchange for stETH\n * @dev Requirements:\n * - `_wstETHAmount` must be non-zero\n * - msg.sender must have at least `_wstETHAmount` wstETH.\n * @return Amount of stETH user receives after unwrap\n */\n function unwrap(uint256 _wstETHAmount) external returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/compound/ICompoundOracle.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\ninterface ICompoundOracle {\n function getUnderlyingPrice(address) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/ILens.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\nimport \"./compound/ICompoundOracle.sol\";\nimport \"./IMorpho.sol\";\n\ninterface ILens {\n /// STORAGE ///\n\n function MAX_BASIS_POINTS() external view returns (uint256);\n\n function WAD() external view returns (uint256);\n\n function morpho() external view returns (IMorpho);\n\n function comptroller() external view returns (IComptroller);\n\n /// GENERAL ///\n\n function getTotalSupply()\n external\n view\n returns (\n uint256 p2pSupplyAmount,\n uint256 poolSupplyAmount,\n uint256 totalSupplyAmount\n );\n\n function getTotalBorrow()\n external\n view\n returns (\n uint256 p2pBorrowAmount,\n uint256 poolBorrowAmount,\n uint256 totalBorrowAmount\n );\n\n /// MARKETS ///\n\n function isMarketCreated(address _poolToken) external view returns (bool);\n\n function isMarketCreatedAndNotPaused(address _poolToken)\n external\n view\n returns (bool);\n\n function isMarketCreatedAndNotPausedNorPartiallyPaused(address _poolToken)\n external\n view\n returns (bool);\n\n function getAllMarkets()\n external\n view\n returns (address[] memory marketsCreated_);\n\n function getMainMarketData(address _poolToken)\n external\n view\n returns (\n uint256 avgSupplyRatePerBlock,\n uint256 avgBorrowRatePerBlock,\n uint256 p2pSupplyAmount,\n uint256 p2pBorrowAmount,\n uint256 poolSupplyAmount,\n uint256 poolBorrowAmount\n );\n\n function getAdvancedMarketData(address _poolToken)\n external\n view\n returns (\n uint256 p2pSupplyIndex,\n uint256 p2pBorrowIndex,\n uint256 poolSupplyIndex,\n uint256 poolBorrowIndex,\n uint32 lastUpdateBlockNumber,\n uint256 p2pSupplyDelta,\n uint256 p2pBorrowDelta\n );\n\n function getMarketConfiguration(address _poolToken)\n external\n view\n returns (\n address underlying,\n bool isCreated,\n bool p2pDisabled,\n bool isPaused,\n bool isPartiallyPaused,\n uint16 reserveFactor,\n uint16 p2pIndexCursor,\n uint256 collateralFactor\n );\n\n function getTotalMarketSupply(address _poolToken)\n external\n view\n returns (uint256 p2pSupplyAmount, uint256 poolSupplyAmount);\n\n function getTotalMarketBorrow(address _poolToken)\n external\n view\n returns (uint256 p2pBorrowAmount, uint256 poolBorrowAmount);\n\n /// INDEXES ///\n\n function getCurrentP2PSupplyIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentP2PBorrowIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentPoolIndexes(address _poolToken)\n external\n view\n returns (\n uint256 currentPoolSupplyIndex,\n uint256 currentPoolBorrowIndex\n );\n\n function getIndexes(address _poolToken, bool _computeUpdatedIndexes)\n external\n view\n returns (\n uint256 p2pSupplyIndex,\n uint256 p2pBorrowIndex,\n uint256 poolSupplyIndex,\n uint256 poolBorrowIndex\n );\n\n /// USERS ///\n\n function getEnteredMarkets(address _user)\n external\n view\n returns (address[] memory enteredMarkets);\n\n function getUserHealthFactor(\n address _user,\n address[] calldata _updatedMarkets\n ) external view returns (uint256);\n\n function getUserBalanceStates(\n address _user,\n address[] calldata _updatedMarkets\n )\n external\n view\n returns (\n uint256 collateralValue,\n uint256 debtValue,\n uint256 maxDebtValue\n );\n\n function getCurrentSupplyBalanceInOf(address _poolToken, address _user)\n external\n view\n returns (\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getCurrentBorrowBalanceInOf(address _poolToken, address _user)\n external\n view\n returns (\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getUserMaxCapacitiesForAsset(address _user, address _poolToken)\n external\n view\n returns (uint256 withdrawable, uint256 borrowable);\n\n function getUserHypotheticalBalanceStates(\n address _user,\n address _poolToken,\n uint256 _withdrawnAmount,\n uint256 _borrowedAmount\n ) external view returns (uint256 debtValue, uint256 maxDebtValue);\n\n function getUserLiquidityDataForAsset(\n address _user,\n address _poolToken,\n bool _computeUpdatedIndexes,\n ICompoundOracle _oracle\n ) external view returns (Types.AssetLiquidityData memory assetData);\n\n function isLiquidatable(address _user, address[] memory _updatedMarkets)\n external\n view\n returns (bool);\n\n function computeLiquidationRepayAmount(\n address _user,\n address _poolTokenBorrowed,\n address _poolTokenCollateral,\n address[] calldata _updatedMarkets\n ) external view returns (uint256 toRepay);\n\n /// RATES ///\n\n function getAverageSupplyRatePerBlock(address _poolToken)\n external\n view\n returns (\n uint256 avgSupplyRatePerBlock,\n uint256 p2pSupplyAmount,\n uint256 poolSupplyAmount\n );\n\n function getAverageBorrowRatePerBlock(address _poolToken)\n external\n view\n returns (\n uint256 avgBorrowRatePerBlock,\n uint256 p2pBorrowAmount,\n uint256 poolBorrowAmount\n );\n\n function getNextUserSupplyRatePerBlock(\n address _poolToken,\n address _user,\n uint256 _amount\n )\n external\n view\n returns (\n uint256 nextSupplyRatePerBlock,\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getNextUserBorrowRatePerBlock(\n address _poolToken,\n address _user,\n uint256 _amount\n )\n external\n view\n returns (\n uint256 nextBorrowRatePerBlock,\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getCurrentUserSupplyRatePerBlock(address _poolToken, address _user)\n external\n view\n returns (uint256);\n\n function getCurrentUserBorrowRatePerBlock(address _poolToken, address _user)\n external\n view\n returns (uint256);\n\n function getRatesPerBlock(address _poolToken)\n external\n view\n returns (\n uint256 p2pSupplyRate,\n uint256 p2pBorrowRate,\n uint256 poolSupplyRate,\n uint256 poolBorrowRate\n );\n\n /// REWARDS ///\n\n function getUserUnclaimedRewards(\n address[] calldata _poolTokens,\n address _user\n ) external view returns (uint256 unclaimedRewards);\n\n function getAccruedSupplierComp(\n address _supplier,\n address _poolToken,\n uint256 _balance\n ) external view returns (uint256);\n\n function getAccruedBorrowerComp(\n address _borrower,\n address _poolToken,\n uint256 _balance\n ) external view returns (uint256);\n\n function getCurrentCompSupplyIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentCompBorrowIndex(address _poolToken)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/IMorpho.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\nimport \"./Types.sol\";\nimport \"../IComptroller.sol\";\nimport \"./compound/ICompoundOracle.sol\";\n\n// prettier-ignore\ninterface IMorpho {\n function comptroller() external view returns (IComptroller);\n function supply(address _poolTokenAddress, address _onBehalf, uint256 _amount) external;\n function supply(address _poolTokenAddress, address _onBehalf, uint256 _amount, uint256 _maxGasForMatching) external;\n function withdraw(address _poolTokenAddress, uint256 _amount) external;\n function claimRewards(\n address[] calldata _cTokenAddresses,\n bool _tradeForMorphoToken\n ) external returns (uint256 claimedAmount);\n}\n" + }, + "contracts/interfaces/morpho/Types.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\n/// @title Types.\n/// @author Morpho Labs.\n/// @custom:contact security@morpho.xyz\n/// @dev Common types and structs used in Moprho contracts.\nlibrary Types {\n /// ENUMS ///\n\n enum PositionType {\n SUPPLIERS_IN_P2P,\n SUPPLIERS_ON_POOL,\n BORROWERS_IN_P2P,\n BORROWERS_ON_POOL\n }\n\n /// STRUCTS ///\n\n struct SupplyBalance {\n uint256 inP2P; // In supplier's peer-to-peer unit, a unit that grows in underlying value, to keep track of the interests earned by suppliers in peer-to-peer. Multiply by the peer-to-peer supply index to get the underlying amount.\n uint256 onPool; // In cToken. Multiply by the pool supply index to get the underlying amount.\n }\n\n struct BorrowBalance {\n uint256 inP2P; // In borrower's peer-to-peer unit, a unit that grows in underlying value, to keep track of the interests paid by borrowers in peer-to-peer. Multiply by the peer-to-peer borrow index to get the underlying amount.\n uint256 onPool; // In cdUnit, a unit that grows in value, to keep track of the debt increase when borrowers are on Compound. Multiply by the pool borrow index to get the underlying amount.\n }\n\n // Max gas to consume during the matching process for supply, borrow, withdraw and repay functions.\n struct MaxGasForMatching {\n uint64 supply;\n uint64 borrow;\n uint64 withdraw;\n uint64 repay;\n }\n\n struct Delta {\n uint256 p2pSupplyDelta; // Difference between the stored peer-to-peer supply amount and the real peer-to-peer supply amount (in pool supply unit).\n uint256 p2pBorrowDelta; // Difference between the stored peer-to-peer borrow amount and the real peer-to-peer borrow amount (in pool borrow unit).\n uint256 p2pSupplyAmount; // Sum of all stored peer-to-peer supply (in peer-to-peer supply unit).\n uint256 p2pBorrowAmount; // Sum of all stored peer-to-peer borrow (in peer-to-peer borrow unit).\n }\n\n struct AssetLiquidityData {\n uint256 collateralValue; // The collateral value of the asset.\n uint256 maxDebtValue; // The maximum possible debt value of the asset.\n uint256 debtValue; // The debt value of the asset.\n uint256 underlyingPrice; // The price of the token.\n uint256 collateralFactor; // The liquidation threshold applied on this token.\n }\n\n struct LiquidityData {\n uint256 collateralValue; // The collateral value.\n uint256 maxDebtValue; // The maximum debt value possible.\n uint256 debtValue; // The debt value.\n }\n\n // Variables are packed together to save gas (will not exceed their limit during Morpho's lifetime).\n struct LastPoolIndexes {\n uint32 lastUpdateBlockNumber; // The last time the peer-to-peer indexes were updated.\n uint112 lastSupplyPoolIndex; // Last pool supply index.\n uint112 lastBorrowPoolIndex; // Last pool borrow index.\n }\n\n struct MarketParameters {\n uint16 reserveFactor; // Proportion of the interest earned by users sent to the DAO for each market, in basis point (100% = 10 000). The value is set at market creation.\n uint16 p2pIndexCursor; // Position of the peer-to-peer rate in the pool's spread. Determine the weights of the weighted arithmetic average in the indexes computations ((1 - p2pIndexCursor) * r^S + p2pIndexCursor * r^B) (in basis point).\n }\n\n struct MarketStatus {\n bool isCreated; // Whether or not this market is created.\n bool isPaused; // Whether the market is paused or not (all entry points on Morpho are frozen; supply, borrow, withdraw, repay and liquidate).\n bool isPartiallyPaused; // Whether the market is partially paused or not (only supply and borrow are frozen).\n }\n}\n" + }, + "contracts/interfaces/plume/IFeeRegistry.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\ninterface IFeeRegistry {\n function registerFee(\n bool isTokenA,\n uint32 binId,\n uint256 binFeeInQuote\n ) external;\n}\n" + }, + "contracts/interfaces/plume/ILiquidityRegistry.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface ILiquidityRegistry {\n function notifyBinLiquidity(\n IMaverickV2Pool pool,\n uint256 tokenId,\n uint32 binId,\n uint256 currentBinLpBalance\n ) external;\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Factory.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IFeeRegistry } from \"./IFeeRegistry.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2Factory {\n error FactorAlreadyInitialized();\n error FactorNotInitialized();\n error FactoryInvalidTokenOrder(IERC20 _tokenA, IERC20 _tokenB);\n error FactoryInvalidFee();\n error FactoryInvalidKinds(uint8 kinds);\n error FactoryInvalidTickSpacing(uint256 tickSpacing);\n error FactoryInvalidLookback(uint256 lookback);\n error FactoryInvalidTokenDecimals(uint8 decimalsA, uint8 decimalsB);\n error FactoryPoolAlreadyExists(\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds,\n address accessor\n );\n error FactoryAccessorMustBeNonZero();\n error NotImplemented();\n\n event PoolCreated(\n IMaverickV2Pool poolAddress,\n uint8 protocolFeeRatio,\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n int32 activeTick,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds,\n address accessor\n );\n event SetFactoryProtocolFeeReceiver(address receiver);\n event SetFactoryProtocolFeeRegistry(IFeeRegistry registry);\n\n struct DeployParameters {\n uint64 feeAIn;\n uint64 feeBIn;\n uint32 lookback;\n int32 activeTick;\n uint64 tokenAScale;\n uint64 tokenBScale;\n // slot\n IERC20 tokenA;\n // slot\n IERC20 tokenB;\n // slot\n uint16 tickSpacing;\n uint8 options;\n address accessor;\n }\n\n /**\n * @notice Called by deployer library to initialize a pool.\n */\n function deployParameters()\n external\n view\n returns (\n uint64 feeAIn,\n uint64 feeBIn,\n uint32 lookback,\n int32 activeTick,\n uint64 tokenAScale,\n uint64 tokenBScale,\n // slot\n IERC20 tokenA,\n // slot\n IERC20 tokenB,\n // slot\n uint16 tickSpacing,\n uint8 options,\n address accessor\n );\n\n /**\n * @notice Create a new MaverickV2Pool with symmetric swap fees.\n * @param fee Fraction of the pool swap amount that is retained as an LP in\n * D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * E.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function create(\n uint64 fee,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external returns (IMaverickV2Pool);\n\n /**\n * @notice Create a new MaverickV2Pool.\n * @param feeAIn Fraction of the pool swap amount for tokenA-input swaps\n * that is retained as an LP in D18 scale.\n * @param feeBIn Fraction of the pool swap amount for tokenB-input swaps\n * that is retained as an LP in D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * e.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function create(\n uint64 feeAIn,\n uint64 feeBIn,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external returns (IMaverickV2Pool);\n\n /**\n * @notice Bool indicating whether the pool was deployed from this factory.\n */\n function isFactoryPool(IMaverickV2Pool pool) external view returns (bool);\n\n /**\n * @notice Address that receives the protocol fee\n */\n function protocolFeeReceiver() external view returns (address);\n\n /**\n * @notice Address notified on swaps of the protocol fee\n */\n function protocolFeeRegistry() external view returns (IFeeRegistry);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds\n ) external view returns (IMaverickV2Pool);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(\n IERC20 _tokenA,\n IERC20 _tokenB,\n uint256 startIndex,\n uint256 endIndex\n ) external view returns (IMaverickV2Pool[] memory pools);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(uint256 startIndex, uint256 endIndex)\n external\n view\n returns (IMaverickV2Pool[] memory pools);\n\n /**\n * @notice Count of permissionless pools.\n */\n function poolCount() external view returns (uint256 _poolCount);\n\n /**\n * @notice Count of pools for a given accessor and token pair. For\n * permissionless pools, pass `accessor = address(0)`.\n */\n function poolByTokenCount(\n IERC20 _tokenA,\n IERC20 _tokenB,\n address accessor\n ) external view returns (uint256 _poolCount);\n\n /**\n * @notice Get the current factory owner.\n */\n function owner() external view returns (address);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2LiquidityManager.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\nimport { IMaverickV2Position } from \"./IMaverickV2Position.sol\";\nimport { IMaverickV2PoolLens } from \"./IMaverickV2PoolLens.sol\";\n\ninterface IMaverickV2LiquidityManager {\n error LiquidityManagerNotFactoryPool();\n error LiquidityManagerNotTokenIdOwner();\n\n /**\n * @notice Maverick V2 NFT position contract that tracks NFT-based\n * liquditiy positions.\n */\n function position() external view returns (IMaverickV2Position);\n\n /**\n * @notice Create Maverick V2 pool. Function is a pass through to the pool\n * factory and is provided here so that is can be assembled as part of a\n * multicall transaction.\n */\n function createPool(\n uint64 fee,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external payable returns (IMaverickV2Pool pool);\n\n /**\n * @notice Add Liquidity position NFT for msg.sender by specifying\n * msg.sender's token index.\n * @dev Token index is different from tokenId.\n * On the Position NFT contract a user can own multiple NFT tokenIds and\n * these are indexes by an enumeration index which is the `index` input\n * here.\n *\n * See addLiquidity for a description of the add params.\n */\n function addPositionLiquidityToSenderByTokenIndex(\n IMaverickV2Pool pool,\n uint256 index,\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs\n )\n external\n payable\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds\n );\n\n /**\n * @notice Mint new tokenId in the Position NFt contract to msg.sender.\n * Both mints an NFT and adds liquidity to the pool that is held by the\n * NFT.\n */\n function mintPositionNftToSender(\n IMaverickV2Pool pool,\n bytes calldata packedSqrtPriceBreaks,\n bytes[] calldata packedArgs\n )\n external\n payable\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds,\n uint256 tokenId\n );\n\n /**\n * @notice Donates liqudity to a pool that is held by the position contract\n * and will never be retrievable. Can be used to start a pool and ensure\n * there will always be a base level of liquditiy in the pool.\n */\n function donateLiquidity(\n IMaverickV2Pool pool,\n IMaverickV2Pool.AddLiquidityParams memory args\n ) external payable;\n\n /**\n * @notice Packs sqrtPrice breaks array with this format: [length,\n * array[0], array[1],..., array[length-1]] where length is 1 byte.\n */\n function packUint88Array(uint88[] memory fullArray)\n external\n pure\n returns (bytes memory packedArray);\n\n /**\n * @notice Packs addLiquidity paramters array element-wise.\n */\n function packAddLiquidityArgsArray(\n IMaverickV2Pool.AddLiquidityParams[] memory args\n ) external pure returns (bytes[] memory argsPacked);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Pool.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\n\ninterface IMaverickV2Pool {\n error PoolZeroLiquidityAdded();\n error PoolMinimumLiquidityNotMet();\n error PoolLocked();\n error PoolInvalidFee();\n error PoolTicksNotSorted(uint256 index, int256 previousTick, int256 tick);\n error PoolTicksAmountsLengthMismatch(\n uint256 ticksLength,\n uint256 amountsLength\n );\n error PoolBinIdsAmountsLengthMismatch(\n uint256 binIdsLength,\n uint256 amountsLength\n );\n error PoolKindNotSupported(uint256 kinds, uint256 kind);\n error PoolInsufficientBalance(\n uint256 deltaLpAmount,\n uint256 accountBalance\n );\n error PoolReservesExceedMaximum(uint256 amount);\n error PoolValueExceedsBits(uint256 amount, uint256 bits);\n error PoolTickMaxExceeded(uint256 tick);\n error PoolMigrateBinFirst();\n error PoolCurrentTickBeyondSwapLimit(int32 startingTick);\n error PoolSenderNotAccessor(address sender_, address accessor);\n error PoolSenderNotFactory(address sender_, address accessor);\n error PoolFunctionNotImplemented();\n error PoolTokenNotSolvent(\n uint256 internalReserve,\n uint256 tokenBalance,\n IERC20 token\n );\n error PoolNoProtocolFeeReceiverSet();\n\n event PoolSwap(\n address sender,\n address recipient,\n SwapParams params,\n uint256 amountIn,\n uint256 amountOut\n );\n event PoolFlashLoan(\n address sender,\n address recipient,\n uint256 amountA,\n uint256 amountB\n );\n event PoolProtocolFeeCollected(IERC20 token, uint256 protocolFee);\n\n event PoolAddLiquidity(\n address sender,\n address recipient,\n uint256 subaccount,\n AddLiquidityParams params,\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] binIds\n );\n\n event PoolMigrateBinsUpStack(\n address sender,\n uint32 binId,\n uint32 maxRecursion\n );\n\n event PoolRemoveLiquidity(\n address sender,\n address recipient,\n uint256 subaccount,\n RemoveLiquidityParams params,\n uint256 tokenAOut,\n uint256 tokenBOut\n );\n\n event PoolTickState(int32 tick, uint256 reserveA, uint256 reserveB);\n event PoolTickBinUpdate(int32 tick, uint8 kind, uint32 binId);\n event PoolSqrtPrice(uint256 sqrtPrice);\n\n /**\n * @notice Tick state parameters.\n */\n struct TickState {\n uint128 reserveA;\n uint128 reserveB;\n uint128 totalSupply;\n uint32[4] binIdsByTick;\n }\n\n /**\n * @notice Tick data parameters.\n * @param currentReserveA Current reserve of token A.\n * @param currentReserveB Current reserve of token B.\n * @param currentLiquidity Current liquidity amount.\n */\n struct TickData {\n uint256 currentReserveA;\n uint256 currentReserveB;\n uint256 currentLiquidity;\n }\n\n /**\n * @notice Bin state parameters.\n * @param mergeBinBalance LP token balance that this bin possesses of the merge bin.\n * @param mergeId Bin ID of the bin that this bin has merged into.\n * @param totalSupply Total amount of LP tokens in this bin.\n * @param kind One of the 4 kinds (0=static, 1=right, 2=left, 3=both).\n * @param tick The lower price tick of the bin in its current state.\n * @param tickBalance Balance of the tick.\n */\n struct BinState {\n uint128 mergeBinBalance;\n uint128 tickBalance;\n uint128 totalSupply;\n uint8 kind;\n int32 tick;\n uint32 mergeId;\n }\n\n /**\n * @notice Parameters for swap.\n * @param amount Amount of the token that is either the input if exactOutput is false\n * or the output if exactOutput is true.\n * @param tokenAIn Boolean indicating whether tokenA is the input.\n * @param exactOutput Boolean indicating whether the amount specified is\n * the exact output amount (true).\n * @param tickLimit The furthest tick a swap will execute in. If no limit\n * is desired, value should be set to type(int32).max for a tokenAIn swap\n * and type(int32).min for a swap where tokenB is the input.\n */\n struct SwapParams {\n uint256 amount;\n bool tokenAIn;\n bool exactOutput;\n int32 tickLimit;\n }\n\n /**\n * @notice Parameters associated with adding liquidity.\n * @param kind One of the 4 kinds (0=static, 1=right, 2=left, 3=both).\n * @param ticks Array of ticks to add liquidity to.\n * @param amounts Array of bin LP amounts to add.\n */\n struct AddLiquidityParams {\n uint8 kind;\n int32[] ticks;\n uint128[] amounts;\n }\n\n /**\n * @notice Parameters for each bin that will have liquidity removed.\n * @param binIds Index array of the bins losing liquidity.\n * @param amounts Array of bin LP amounts to remove.\n */\n struct RemoveLiquidityParams {\n uint32[] binIds;\n uint128[] amounts;\n }\n\n /**\n * @notice State of the pool.\n * @param reserveA Pool tokenA balanceOf at end of last operation\n * @param reserveB Pool tokenB balanceOf at end of last operation\n * @param lastTwaD8 Value of log time weighted average price at last block.\n * Value is 8-decimal scale and is in the fractional tick domain. E.g. a\n * value of 12.3e8 indicates the TWAP was 3/10ths of the way into the 12th\n * tick.\n * @param lastLogPriceD8 Value of log price at last block. Value is\n * 8-decimal scale and is in the fractional tick domain. E.g. a value of\n * 12.3e8 indicates the price was 3/10ths of the way into the 12th tick.\n * @param lastTimestamp Last block.timestamp value in seconds for latest\n * swap transaction.\n * @param activeTick Current tick position that contains the active bins.\n * @param isLocked Pool isLocked, E.g., locked or unlocked; isLocked values\n * defined in Pool.sol.\n * @param binCounter Index of the last bin created.\n * @param protocolFeeRatioD3 Ratio of the swap fee that is kept for the\n * protocol.\n */\n struct State {\n uint128 reserveA;\n uint128 reserveB;\n int64 lastTwaD8;\n int64 lastLogPriceD8;\n uint40 lastTimestamp;\n int32 activeTick;\n bool isLocked;\n uint32 binCounter;\n uint8 protocolFeeRatioD3;\n }\n\n /**\n * @notice Internal data used for data passing between Pool and Bin code.\n */\n struct BinDelta {\n uint128 deltaA;\n uint128 deltaB;\n }\n\n /**\n * @notice 1-15 number to represent the active kinds.\n * @notice 0b0001 = static;\n * @notice 0b0010 = right;\n * @notice 0b0100 = left;\n * @notice 0b1000 = both;\n *\n * E.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function kinds() external view returns (uint8 _kinds);\n\n /**\n * @notice Pool swap fee for the given direction (A-in or B-in swap) in\n * 18-decimal format. E.g. 0.01e18 is a 1% swap fee.\n */\n function fee(bool tokenAIn) external view returns (uint256);\n\n /**\n * @notice TickSpacing of pool where 1.0001^tickSpacing is the bin width.\n */\n function tickSpacing() external view returns (uint256);\n\n /**\n * @notice Lookback period of pool in seconds.\n */\n function lookback() external view returns (uint256);\n\n /**\n * @notice Address of Pool accessor. This is Zero address for\n * permissionless pools.\n */\n function accessor() external view returns (address);\n\n /**\n * @notice Pool tokenA. Address of tokenA is such that tokenA < tokenB.\n */\n function tokenA() external view returns (IERC20);\n\n /**\n * @notice Pool tokenB.\n */\n function tokenB() external view returns (IERC20);\n\n /**\n * @notice Deploying factory of the pool and also contract that has ability\n * to set and collect protocol fees for the pool.\n */\n function factory() external view returns (IMaverickV2Factory);\n\n /**\n * @notice Most significant bit of scale value is a flag to indicate whether\n * tokenA has more or less than 18 decimals. Scale is used in conjuction\n * with Math.toScale/Math.fromScale functions to convert from token amounts\n * to D18 scale internal pool accounting.\n */\n function tokenAScale() external view returns (uint256);\n\n /**\n * @notice Most significant bit of scale value is a flag to indicate whether\n * tokenA has more or less than 18 decimals. Scale is used in conjuction\n * with Math.toScale/Math.fromScale functions to convert from token amounts\n * to D18 scale internal pool accounting.\n */\n function tokenBScale() external view returns (uint256);\n\n /**\n * @notice ID of bin at input tick position and kind.\n */\n function binIdByTickKind(int32 tick, uint256 kind)\n external\n view\n returns (uint32);\n\n /**\n * @notice Accumulated tokenA protocol fee.\n */\n function protocolFeeA() external view returns (uint128);\n\n /**\n * @notice Accumulated tokenB protocol fee.\n */\n function protocolFeeB() external view returns (uint128);\n\n /**\n * @notice Lending fee rate on flash loans.\n */\n function lendingFeeRateD18() external view returns (uint256);\n\n /**\n * @notice External function to get the current time-weighted average price.\n */\n function getCurrentTwa() external view returns (int256);\n\n /**\n * @notice External function to get the state of the pool.\n */\n function getState() external view returns (State memory);\n\n /**\n * @notice Return state of Bin at input binId.\n */\n function getBin(uint32 binId) external view returns (BinState memory bin);\n\n /**\n * @notice Return state of Tick at input tick position.\n */\n function getTick(int32 tick)\n external\n view\n returns (TickState memory tickState);\n\n /**\n * @notice Retrieves the balance of a user within a bin.\n * @param user The user's address.\n * @param subaccount The subaccount for the user.\n * @param binId The ID of the bin.\n */\n function balanceOf(\n address user,\n uint256 subaccount,\n uint32 binId\n ) external view returns (uint128 lpToken);\n\n /**\n * @notice Add liquidity to a pool. This function allows users to deposit\n * tokens into a liquidity pool.\n * @dev This function will call `maverickV2AddLiquidityCallback` on the\n * calling contract to collect the tokenA/tokenB payment.\n * @param recipient The account that will receive credit for the added liquidity.\n * @param subaccount The account that will receive credit for the added liquidity.\n * @param params Parameters containing the details for adding liquidity,\n * such as token types and amounts.\n * @param data Bytes information that gets passed to the callback.\n * @return tokenAAmount The amount of token A added to the pool.\n * @return tokenBAmount The amount of token B added to the pool.\n * @return binIds An array of bin IDs where the liquidity is stored.\n */\n function addLiquidity(\n address recipient,\n uint256 subaccount,\n AddLiquidityParams calldata params,\n bytes calldata data\n )\n external\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds\n );\n\n /**\n * @notice Removes liquidity from the pool.\n * @dev Liquidy can only be removed from a bin that is either unmerged or\n * has a mergeId of an unmerged bin. If a bin is merged more than one\n * level deep, it must be migrated up the merge stack to the root bin\n * before liquidity removal.\n * @param recipient The address to receive the tokens.\n * @param subaccount The subaccount for the recipient.\n * @param params The parameters for removing liquidity.\n * @return tokenAOut The amount of token A received.\n * @return tokenBOut The amount of token B received.\n */\n function removeLiquidity(\n address recipient,\n uint256 subaccount,\n RemoveLiquidityParams calldata params\n ) external returns (uint256 tokenAOut, uint256 tokenBOut);\n\n /**\n * @notice Migrate bins up the linked list of merged bins so that its\n * mergeId is the currrent active bin.\n * @dev Liquidy can only be removed from a bin that is either unmerged or\n * has a mergeId of an unmerged bin. If a bin is merged more than one\n * level deep, it must be migrated up the merge stack to the root bin\n * before liquidity removal.\n * @param binId The ID of the bin to migrate.\n * @param maxRecursion The maximum recursion depth for the migration.\n */\n function migrateBinUpStack(uint32 binId, uint32 maxRecursion) external;\n\n /**\n * @notice Swap tokenA/tokenB assets in the pool. The swap user has two\n * options for funding their swap.\n * - The user can push the input token amount to the pool before calling\n * the swap function. In order to avoid having the pool call the callback,\n * the user should pass a zero-length `data` bytes object with the swap\n * call.\n * - The user can send the input token amount to the pool when the pool\n * calls the `maverickV2SwapCallback` function on the calling contract.\n * That callback has input parameters that specify the token address of the\n * input token, the input and output amounts, and the bytes data sent to\n * the swap function.\n * @dev If the users elects to do a callback-based swap, the output\n * assets will be sent before the callback is called, allowing the user to\n * execute flash swaps. However, the pool does have reentrancy protection,\n * so a swapper will not be able to interact with the same pool again\n * while they are in the callback function.\n * @param recipient The address to receive the output tokens.\n * @param params Parameters containing the details of the swap\n * @param data Bytes information that gets passed to the callback.\n */\n function swap(\n address recipient,\n SwapParams memory params,\n bytes calldata data\n ) external returns (uint256 amountIn, uint256 amountOut);\n\n /**\n * @notice Loan tokenA/tokenB assets from the pool to recipient. The fee\n * rate of a loan is determined by `lendingFeeRateD18`, which is set at the\n * protocol level by the factory. This function calls\n * `maverickV2FlashLoanCallback` on the calling contract. At the end of\n * the callback, the caller must pay back the loan with fee (if there is a\n * fee).\n * @param recipient The address to receive the loaned tokens.\n * @param amountB Loan amount of tokenA sent to recipient.\n * @param amountB Loan amount of tokenB sent to recipient.\n * @param data Bytes information that gets passed to the callback.\n */\n function flashLoan(\n address recipient,\n uint256 amountA,\n uint256 amountB,\n bytes calldata data\n ) external;\n\n /**\n * @notice Distributes accumulated protocol fee to factory protocolFeeReceiver\n */\n function distributeFees(bool isTokenA)\n external\n returns (uint256 protocolFee, IERC20 token);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2PoolLens.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2PoolLens {\n error LensTargetPriceOutOfBounds(\n uint256 targetSqrtPrice,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n );\n error LensTooLittleLiquidity(\n uint256 relativeLiquidityAmount,\n uint256 deltaA,\n uint256 deltaB\n );\n error LensTargetingTokenWithNoDelta(\n bool targetIsA,\n uint256 deltaA,\n uint256 deltaB\n );\n\n /**\n * @notice Add liquidity slippage parameters for a distribution of liquidity.\n * @param pool Pool where liquidity is being added.\n * @param kind Bin kind; all bins must have the same kind in a given call\n * to addLiquidity.\n * @param ticks Array of tick values to add liquidity to.\n * @param relativeLiquidityAmounts Relative liquidity amounts for the\n * specified ticks. Liquidity in this case is not bin LP balance, it is\n * the bin liquidity as defined by liquidity = deltaA / (sqrt(upper) -\n * sqrt(lower)) or deltaB = liquidity / sqrt(lower) - liquidity /\n * sqrt(upper).\n * @param addSpec Slippage specification.\n */\n struct AddParamsViewInputs {\n IMaverickV2Pool pool;\n uint8 kind;\n int32[] ticks;\n uint128[] relativeLiquidityAmounts;\n AddParamsSpecification addSpec;\n }\n\n /**\n * @notice Multi-price add param specification.\n * @param slippageFactorD18 Max slippage allowed as a percent in D18 scale. e.g. 1% slippage is 0.01e18\n * @param numberOfPriceBreaksPerSide Number of price break values on either\n * side of current price.\n * @param targetAmount Target token contribution amount in tokenA if\n * targetIsA is true, otherwise this is the target amount for tokenB.\n * @param targetIsA Indicates if the target amount is for tokenA or tokenB\n */\n struct AddParamsSpecification {\n uint256 slippageFactorD18;\n uint256 numberOfPriceBreaksPerSide;\n uint256 targetAmount;\n bool targetIsA;\n }\n\n /**\n * @notice Specification for deriving create pool parameters. Creating a\n * pool in the liquidity manager has several steps:\n *\n * - Deploy pool\n * - Donate a small amount of initial liquidity in the activeTick\n * - Execute a small swap to set the pool price to the desired value\n * - Add liquidity\n *\n * In order to execute these steps, the caller must specify the parameters\n * of each step. The PoolLens has helper function to derive the values\n * used by the LiquidityManager, but this struct is the input to that\n * helper function and represents the core intent of the pool creator.\n *\n * @param fee Fraction of the pool swap amount that is retained as an LP in\n * D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * e.g. a pool with all 4 modes will have kinds = b1111 = 15\n * @param initialTargetB Amount of B to be donated to the pool after pool\n * create. This amount needs to be big enough to meet the minimum bin\n * liquidity.\n * @param sqrtPrice Target sqrt price of the pool.\n * @param kind Bin kind; all bins must have the same kind in a given call\n * to addLiquidity.\n * @param ticks Array of tick values to add liquidity to.\n * @param relativeLiquidityAmounts Relative liquidity amounts for the\n * specified ticks. Liquidity in this case is not bin LP balance, it is\n * the bin liquidity as defined by liquidity = deltaA / (sqrt(upper) -\n * sqrt(lower)) or deltaB = liquidity / sqrt(lower) - liquidity /\n * sqrt(upper).\n * @param targetAmount Target token contribution amount in tokenA if\n * targetIsA is true, otherwise this is the target amount for tokenB.\n * @param targetIsA Indicates if the target amount is for tokenA or tokenB\n */\n struct CreateAndAddParamsViewInputs {\n uint64 feeAIn;\n uint64 feeBIn;\n uint16 tickSpacing;\n uint32 lookback;\n IERC20 tokenA;\n IERC20 tokenB;\n int32 activeTick;\n uint8 kinds;\n // donate params\n uint256 initialTargetB;\n uint256 sqrtPrice;\n // add target\n uint8 kind;\n int32[] ticks;\n uint128[] relativeLiquidityAmounts;\n uint256 targetAmount;\n bool targetIsA;\n }\n\n struct Output {\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256[] deltaAs;\n uint256[] deltaBs;\n uint128[] deltaLpBalances;\n }\n\n struct Reserves {\n uint256 amountA;\n uint256 amountB;\n }\n\n struct BinPositionKinds {\n uint128[4] values;\n }\n\n struct PoolState {\n IMaverickV2Pool.TickState[] tickStateMapping;\n IMaverickV2Pool.BinState[] binStateMapping;\n BinPositionKinds[] binIdByTickKindMapping;\n IMaverickV2Pool.State state;\n Reserves protocolFees;\n }\n\n struct BoostedPositionSpecification {\n IMaverickV2Pool pool;\n uint32[] binIds;\n uint128[] ratios;\n uint8 kind;\n }\n\n struct CreateAndAddParamsInputs {\n uint64 feeAIn;\n uint64 feeBIn;\n uint16 tickSpacing;\n uint32 lookback;\n IERC20 tokenA;\n IERC20 tokenB;\n int32 activeTick;\n uint8 kinds;\n // donate params\n IMaverickV2Pool.AddLiquidityParams donateParams;\n // swap params\n uint256 swapAmount;\n // add params\n IMaverickV2Pool.AddLiquidityParams addParams;\n bytes[] packedAddParams;\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256 preAddReserveA;\n uint256 preAddReserveB;\n }\n\n struct TickDeltas {\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256[] deltaAs;\n uint256[] deltaBs;\n }\n\n /**\n * @notice Converts add parameter slippage specification into add\n * parameters. The return values are given in both raw format and as packed\n * values that can be used in the LiquidityManager contract.\n */\n function getAddLiquidityParams(AddParamsViewInputs memory params)\n external\n view\n returns (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint88[] memory sqrtPriceBreaks,\n IMaverickV2Pool.AddLiquidityParams[] memory addParams,\n IMaverickV2PoolLens.TickDeltas[] memory tickDeltas\n );\n\n /**\n * @notice Converts add parameter slippage specification and new pool\n * specification into CreateAndAddParamsInputs parameters that can be used in the\n * LiquidityManager contract.\n */\n function getCreatePoolAtPriceAndAddLiquidityParams(\n CreateAndAddParamsViewInputs memory params\n ) external view returns (CreateAndAddParamsInputs memory output);\n\n /**\n * @notice View function that provides information about pool ticks within\n * a tick radius from the activeTick. Ticks with no reserves are not\n * included in part o f the return array.\n */\n function getTicksAroundActive(IMaverickV2Pool pool, int32 tickRadius)\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates\n );\n\n /**\n * @notice View function that provides information about pool ticks within\n * a range. Ticks with no reserves are not included in part o f the return\n * array.\n */\n function getTicks(\n IMaverickV2Pool pool,\n int32 tickStart,\n int32 tickEnd\n )\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates\n );\n\n /**\n * @notice View function that provides information about pool ticks within\n * a range. Information returned includes all pool state needed to emulate\n * a swap off chain. Ticks with no reserves are not included in part o f\n * the return array.\n */\n function getTicksAroundActiveWLiquidity(\n IMaverickV2Pool pool,\n int32 tickRadius\n )\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates,\n uint256[] memory liquidities,\n uint256[] memory sqrtLowerTickPrices,\n uint256[] memory sqrtUpperTickPrices,\n IMaverickV2Pool.State memory poolState,\n uint256 sqrtPrice,\n uint256 feeAIn,\n uint256 feeBIn\n );\n\n /**\n * @notice View function that provides pool state information.\n */\n function getFullPoolState(\n IMaverickV2Pool pool,\n uint32 binStart,\n uint32 binEnd\n ) external view returns (PoolState memory poolState);\n\n /**\n * @notice View function that provides price and liquidity of a given tick.\n */\n function getTickSqrtPriceAndL(IMaverickV2Pool pool, int32 tick)\n external\n view\n returns (uint256 sqrtPrice, uint256 liquidity);\n\n /**\n * @notice Pool sqrt price.\n */\n function getPoolSqrtPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 sqrtPrice);\n\n /**\n * @notice Pool price.\n */\n function getPoolPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 price);\n\n /**\n * @notice Token scale of two tokens in a pool.\n */\n function tokenScales(IMaverickV2Pool pool)\n external\n view\n returns (uint256 tokenAScale, uint256 tokenBScale);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Position.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\nimport { ILiquidityRegistry } from \"./ILiquidityRegistry.sol\";\n\ninterface IMaverickV2Position {\n event PositionClearData(uint256 indexed tokenId);\n event PositionSetData(\n uint256 indexed tokenId,\n uint256 index,\n PositionPoolBinIds newData\n );\n event SetLpReward(ILiquidityRegistry lpReward);\n\n error PositionDuplicatePool(uint256 index, IMaverickV2Pool pool);\n error PositionNotFactoryPool();\n error PositionPermissionedLiquidityPool();\n\n struct PositionPoolBinIds {\n IMaverickV2Pool pool;\n uint32[] binIds;\n }\n\n struct PositionFullInformation {\n PositionPoolBinIds poolBinIds;\n uint256 amountA;\n uint256 amountB;\n uint256[] binAAmounts;\n uint256[] binBAmounts;\n int32[] ticks;\n uint256[] liquidities;\n }\n\n /**\n * @notice Factory that tracks lp rewards.\n */\n function lpReward() external view returns (ILiquidityRegistry);\n\n /**\n * @notice Pool factory.\n */\n function factory() external view returns (IMaverickV2Factory);\n\n /**\n * @notice Mint NFT that holds liquidity in a Maverick V2 Pool. To mint\n * liquidity to an NFT, add liquidity to bins in a pool where the\n * add liquidity recipient is this contract and the subaccount is the\n * tokenId. LiquidityManager can be used to simplify minting Position NFTs.\n */\n function mint(\n address recipient,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external returns (uint256 tokenId);\n\n /**\n * @notice Overwrites tokenId pool/binId information for a given data index.\n */\n function setTokenIdData(\n uint256 tokenId,\n uint256 index,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n\n /**\n * @notice Overwrites entire pool/binId data set for a given tokenId.\n */\n function setTokenIdData(uint256 tokenId, PositionPoolBinIds[] memory data)\n external;\n\n /**\n * @notice Append new pool/binIds data array to tokenId.\n */\n function appendTokenIdData(\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n\n /**\n * @notice Get array pool/binIds data for a given tokenId.\n */\n function getTokenIdData(uint256 tokenId)\n external\n view\n returns (PositionPoolBinIds[] memory);\n\n /**\n * @notice Get value from array of pool/binIds data for a given tokenId.\n */\n function getTokenIdData(uint256 tokenId, uint256 index)\n external\n view\n returns (PositionPoolBinIds memory);\n\n /**\n * @notice Length of array of pool/binIds data for a given tokenId.\n */\n function tokenIdDataLength(uint256 tokenId)\n external\n view\n returns (uint256 length);\n\n /**\n * @notice Remove liquidity from tokenId for a given pool. User can\n * specify arbitrary bins to remove from for their subaccount in the pool\n * even if those bins are not in the tokenIdData set.\n */\n function removeLiquidity(\n uint256 tokenId,\n address recipient,\n IMaverickV2Pool pool,\n IMaverickV2Pool.RemoveLiquidityParams memory params\n ) external returns (uint256 tokenAAmount, uint256 tokenBAmount);\n\n /**\n * @notice Remove liquidity from tokenId for a given pool to sender. User\n * can specify arbitrary bins to remove from for their subaccount in the\n * pool even if those bins are not in the tokenIdData set.\n */\n function removeLiquidityToSender(\n uint256 tokenId,\n IMaverickV2Pool pool,\n IMaverickV2Pool.RemoveLiquidityParams memory params\n ) external returns (uint256 tokenAAmount, uint256 tokenBAmount);\n\n /**\n * @notice NFT asset information for a given range of pool/binIds indexes.\n * This function only returns the liquidity in the pools/binIds stored as\n * part of the tokenIdData, but it is possible that the NFT has additional\n * liquidity in pools/binIds that have not been recorded.\n */\n function tokenIdPositionInformation(\n uint256 tokenId,\n uint256 startIndex,\n uint256 stopIndex\n ) external view returns (PositionFullInformation[] memory output);\n\n /**\n * @notice NFT asset information for a given pool/binIds index. This\n * function only returns the liquidity in the pools/binIds stored as part\n * of the tokenIdData, but it is possible that the NFT has additional\n * liquidity in pools/binIds that have not been recorded.\n */\n function tokenIdPositionInformation(uint256 tokenId, uint256 index)\n external\n view\n returns (PositionFullInformation memory output);\n\n /**\n * @notice Get remove parameters for removing a fractional part of the\n * liquidity owned by a given tokenId. The fractional factor to remove is\n * given by proporationD18 in 18-decimal scale.\n */\n function getRemoveParams(\n uint256 tokenId,\n uint256 index,\n uint256 proportionD18\n )\n external\n view\n returns (IMaverickV2Pool.RemoveLiquidityParams memory params);\n\n /**\n * @notice Register the bin balances in the nft with the LpReward contract.\n */\n function checkpointBinLpBalance(\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Quoter.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2Quoter {\n error QuoterInvalidSwap();\n error QuoterInvalidAddLiquidity();\n\n /**\n * @notice Calculates a swap on a MaverickV2Pool and returns the resulting\n * amount and estimated gas. The gas estimate is only a rough estimate and\n * may not match a swap's gas.\n * @param pool The MaverickV2Pool to swap on.\n * @param amount The input amount.\n * @param tokenAIn Indicates if token A is the input token.\n * @param exactOutput Indicates if the amount is the output amount (true)\n * or input amount (false). If the tickLimit is reached, the full value of\n * the exactOutput may not be returned because the pool will stop swapping\n * before the whole order is filled.\n * @param tickLimit The tick limit for the swap. Once the swap lands in\n * this tick, it will stop and return the output amount swapped up to that\n * tick.\n */\n function calculateSwap(\n IMaverickV2Pool pool,\n uint128 amount,\n bool tokenAIn,\n bool exactOutput,\n int32 tickLimit\n )\n external\n returns (\n uint256 amountIn,\n uint256 amountOut,\n uint256 gasEstimate\n );\n\n /**\n * @notice Calculates a multihop swap and returns the resulting amount and\n * estimated gas. The gas estimate is only a rough estimate and\n * may not match a swap's gas.\n * @param path The path of pools to swap through. Path is given by an\n * packed array of (pool, tokenAIn) tuples. So each step in the path is 160\n * + 8 = 168 bits of data. e.g. path = abi.encodePacked(pool1, true, pool2, false);\n * @param amount The input amount.\n * @param exactOutput A boolean indicating if exact output is required.\n */\n function calculateMultiHopSwap(\n bytes memory path,\n uint256 amount,\n bool exactOutput\n ) external returns (uint256 returnAmount, uint256 gasEstimate);\n\n /**\n * @notice Computes the token amounts required for a given set of\n * addLiquidity parameters. The gas estimate is only a rough estimate and\n * may not match a add's gas.\n */\n function calculateAddLiquidity(\n IMaverickV2Pool pool,\n IMaverickV2Pool.AddLiquidityParams calldata params\n )\n external\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 gasEstimate\n );\n\n /**\n * @notice Pool's sqrt price.\n */\n function poolSqrtPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 sqrtPrice);\n}\n" + }, + "contracts/interfaces/plume/IPoolDistributor.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.0;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IPoolDistributor {\n function rewardToken() external view returns (address);\n\n function claimLp(\n address recipient,\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds,\n uint256 epoch\n ) external returns (uint256 amount);\n}\n" + }, + "contracts/interfaces/plume/IVotingDistributor.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.0;\n\ninterface IVotingDistributor {\n function lastEpoch() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/poolBooster/IMerklDistributor.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IMerklDistributor {\n struct CampaignParameters {\n // POPULATED ONCE CREATED\n\n // ID of the campaign. This can be left as a null bytes32 when creating campaigns\n // on Merkl.\n bytes32 campaignId;\n // CHOSEN BY CAMPAIGN CREATOR\n\n // Address of the campaign creator, if marked as address(0), it will be overriden with the\n // address of the `msg.sender` creating the campaign\n address creator;\n // Address of the token used as a reward\n address rewardToken;\n // Amount of `rewardToken` to distribute across all the epochs\n // Amount distributed per epoch is `amount/numEpoch`\n uint256 amount;\n // Type of campaign\n uint32 campaignType;\n // Timestamp at which the campaign should start\n uint32 startTimestamp;\n // Duration of the campaign in seconds. Has to be a multiple of EPOCH = 3600\n uint32 duration;\n // Extra data to pass to specify the campaign\n bytes campaignData;\n }\n\n function createCampaign(CampaignParameters memory newCampaign)\n external\n returns (bytes32);\n\n function signAndCreateCampaign(\n CampaignParameters memory newCampaign,\n bytes memory _signature\n ) external returns (bytes32);\n\n function sign(bytes memory _signature) external;\n\n function rewardTokenMinAmounts(address _rewardToken)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/poolBooster/IPoolBoostCentralRegistry.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPoolBoostCentralRegistry {\n /**\n * @dev all the supported pool booster types are listed here. It is possible\n * to have multiple versions of the factory that supports the same type of\n * pool booster. Factories are immutable and this can happen when a factory\n * or related pool booster required code update.\n * e.g. \"PoolBoosterSwapxDouble\" & \"PoolBoosterSwapxDouble_v2\"\n */\n enum PoolBoosterType {\n // Supports bribing 2 contracts per pool. Appropriate for Ichi vault concentrated\n // liquidity pools where (which is expected in most/all cases) both pool gauges\n // require bribing.\n SwapXDoubleBooster,\n // Supports bribing a single contract per pool. Appropriate for Classic Stable &\n // Classic Volatile pools and Ichi vaults where only 1 side (1 of the 2 gauges)\n // needs bribing\n SwapXSingleBooster,\n // Supports bribing a single contract per pool. Appropriate for Metropolis pools\n MetropolisBooster,\n // Supports creating a Merkl campaign.\n MerklBooster\n }\n\n struct PoolBoosterEntry {\n address boosterAddress;\n address ammPoolAddress;\n PoolBoosterType boosterType;\n }\n\n event PoolBoosterCreated(\n address poolBoosterAddress,\n address ammPoolAddress,\n PoolBoosterType poolBoosterType,\n address factoryAddress\n );\n event PoolBoosterRemoved(address poolBoosterAddress);\n\n function emitPoolBoosterCreated(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n PoolBoosterType _boosterType\n ) external;\n\n function emitPoolBoosterRemoved(address _poolBoosterAddress) external;\n}\n" + }, + "contracts/interfaces/poolBooster/IPoolBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPoolBooster {\n event BribeExecuted(uint256 amount);\n\n /// @notice Execute the bribe action\n function bribe() external;\n}\n" + }, + "contracts/interfaces/poolBooster/ISwapXAlgebraBribe.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBribe {\n /// @notice Notify a bribe amount\n /// @dev Rewards are saved into NEXT EPOCH mapping.\n function notifyRewardAmount(address _rewardsToken, uint256 reward) external;\n}\n" + }, + "contracts/interfaces/sonic/INodeDriver.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\n\ninterface INodeDriver {\n /// Seal epoch. Called BEFORE epoch sealing made by the client itself.\n function sealEpoch(\n uint256[] calldata offlineTimes,\n uint256[] calldata offlineBlocks,\n uint256[] calldata uptimes,\n uint256[] calldata originatedTxsFee\n ) external;\n}\n" + }, + "contracts/interfaces/sonic/ISFC.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\n\n/**\n * @title Special Fee Contract for Sonic network\n * @notice The SFC maintains a list of validators and delegators and distributes rewards to them.\n * @custom:security-contact security@fantom.foundation\n */\ninterface ISFC {\n error StakeIsFullySlashed();\n\n event CreatedValidator(\n uint256 indexed validatorID,\n address indexed auth,\n uint256 createdEpoch,\n uint256 createdTime\n );\n event Delegated(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 amount\n );\n event Undelegated(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 indexed wrID,\n uint256 amount\n );\n event Withdrawn(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 indexed wrID,\n uint256 amount,\n uint256 penalty\n );\n event ClaimedRewards(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 rewards\n );\n event RestakedRewards(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 rewards\n );\n event BurntFTM(uint256 amount);\n event UpdatedSlashingRefundRatio(\n uint256 indexed validatorID,\n uint256 refundRatio\n );\n event RefundedSlashedLegacyDelegation(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 amount\n );\n\n event DeactivatedValidator(\n uint256 indexed validatorID,\n uint256 deactivatedEpoch,\n uint256 deactivatedTime\n );\n event ChangedValidatorStatus(uint256 indexed validatorID, uint256 status);\n event AnnouncedRedirection(address indexed from, address indexed to);\n\n function currentSealedEpoch() external view returns (uint256);\n\n function getEpochSnapshot(uint256 epoch)\n external\n view\n returns (\n uint256 endTime,\n uint256 endBlock,\n uint256 epochFee,\n uint256 baseRewardPerSecond,\n uint256 totalStake,\n uint256 totalSupply\n );\n\n function getStake(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getValidator(uint256 validatorID)\n external\n view\n returns (\n uint256 status,\n uint256 receivedStake,\n address auth,\n uint256 createdEpoch,\n uint256 createdTime,\n uint256 deactivatedTime,\n uint256 deactivatedEpoch\n );\n\n function getValidatorID(address auth) external view returns (uint256);\n\n function getValidatorPubkey(uint256 validatorID)\n external\n view\n returns (bytes memory);\n\n function pubkeyAddressvalidatorID(address pubkeyAddress)\n external\n view\n returns (uint256);\n\n function getWithdrawalRequest(\n address delegator,\n uint256 validatorID,\n uint256 wrID\n )\n external\n view\n returns (\n uint256 epoch,\n uint256 time,\n uint256 amount\n );\n\n function isOwner() external view returns (bool);\n\n function lastValidatorID() external view returns (uint256);\n\n function minGasPrice() external view returns (uint256);\n\n function owner() external view returns (address);\n\n function renounceOwnership() external;\n\n function slashingRefundRatio(uint256 validatorID)\n external\n view\n returns (uint256);\n\n function stashedRewardsUntilEpoch(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function totalActiveStake() external view returns (uint256);\n\n function totalStake() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function transferOwnership(address newOwner) external;\n\n function treasuryAddress() external view returns (address);\n\n function version() external pure returns (bytes3);\n\n function currentEpoch() external view returns (uint256);\n\n function updateConstsAddress(address v) external;\n\n function constsAddress() external view returns (address);\n\n function getEpochValidatorIDs(uint256 epoch)\n external\n view\n returns (uint256[] memory);\n\n function getEpochReceivedStake(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochAccumulatedRewardPerToken(\n uint256 epoch,\n uint256 validatorID\n ) external view returns (uint256);\n\n function getEpochAccumulatedUptime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochAverageUptime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint32);\n\n function getEpochAccumulatedOriginatedTxsFee(\n uint256 epoch,\n uint256 validatorID\n ) external view returns (uint256);\n\n function getEpochOfflineTime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochOfflineBlocks(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochEndBlock(uint256 epoch) external view returns (uint256);\n\n function rewardsStash(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function createValidator(bytes calldata pubkey) external payable;\n\n function getSelfStake(uint256 validatorID) external view returns (uint256);\n\n function delegate(uint256 validatorID) external payable;\n\n function undelegate(\n uint256 validatorID,\n uint256 wrID,\n uint256 amount\n ) external;\n\n function isSlashed(uint256 validatorID) external view returns (bool);\n\n function withdraw(uint256 validatorID, uint256 wrID) external;\n\n function deactivateValidator(uint256 validatorID, uint256 status) external;\n\n function pendingRewards(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function stashRewards(address delegator, uint256 validatorID) external;\n\n function claimRewards(uint256 validatorID) external;\n\n function restakeRewards(uint256 validatorID) external;\n\n function updateSlashingRefundRatio(uint256 validatorID, uint256 refundRatio)\n external;\n\n function updateTreasuryAddress(address v) external;\n\n function burnFTM(uint256 amount) external;\n\n function sealEpoch(\n uint256[] calldata offlineTime,\n uint256[] calldata offlineBlocks,\n uint256[] calldata uptimes,\n uint256[] calldata originatedTxsFee\n ) external;\n\n function sealEpochValidators(uint256[] calldata nextValidatorIDs) external;\n\n function initialize(\n uint256 sealedEpoch,\n uint256 _totalSupply,\n address nodeDriver,\n address consts,\n address _owner\n ) external;\n\n function setGenesisValidator(\n address auth,\n uint256 validatorID,\n bytes calldata pubkey,\n uint256 createdTime\n ) external;\n\n function setGenesisDelegation(\n address delegator,\n uint256 validatorID,\n uint256 stake\n ) external;\n\n function updateStakeSubscriberAddress(address v) external;\n\n function stakeSubscriberAddress() external view returns (address);\n\n function setRedirectionAuthorizer(address v) external;\n\n function announceRedirection(address to) external;\n\n function initiateRedirection(address from, address to) external;\n\n function redirect(address to) external;\n}\n" + }, + "contracts/interfaces/sonic/ISwapXGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IGauge {\n function owner() external view returns (address);\n\n function TOKEN() external view returns (address);\n\n function DISTRIBUTION() external view returns (address);\n\n function balanceOf(address account) external view returns (uint256);\n\n function claimFees() external returns (uint256 claimed0, uint256 claimed1);\n\n function deposit(uint256 amount) external;\n\n function depositAll() external;\n\n function earned(address account) external view returns (uint256);\n\n function getReward() external;\n\n function getReward(address _user) external;\n\n function isForPair() external view returns (bool);\n\n function lastTimeRewardApplicable() external view returns (uint256);\n\n function lastUpdateTime() external view returns (uint256);\n\n function notifyRewardAmount(address token, uint256 reward) external;\n\n function periodFinish() external view returns (uint256);\n\n function rewardForDuration() external view returns (uint256);\n\n function rewardPerToken() external view returns (uint256);\n\n function rewardPerTokenStored() external view returns (uint256);\n\n function rewardRate() external view returns (uint256);\n\n function rewardToken() external view returns (address);\n\n function rewards(address) external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function userRewardPerTokenPaid(address) external view returns (uint256);\n\n function withdraw(uint256 amount) external;\n\n function withdrawAll() external;\n\n function withdrawAllAndHarvest() external;\n\n function withdrawExcess(address token, uint256 amount) external;\n\n function emergency() external returns (bool);\n\n function emergencyWithdraw() external;\n\n function activateEmergencyMode() external;\n\n function stopEmergencyMode() external;\n}\n" + }, + "contracts/interfaces/sonic/ISwapXPair.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IPair {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n\n event Mint(address indexed sender, uint256 amount0, uint256 amount1);\n event Burn(\n address indexed sender,\n uint256 amount0,\n uint256 amount1,\n address indexed to\n );\n event Swap(\n address indexed sender,\n uint256 amount0In,\n uint256 amount1In,\n uint256 amount0Out,\n uint256 amount1Out,\n address indexed to\n );\n event Claim(\n address indexed sender,\n address indexed recipient,\n uint256 amount0,\n uint256 amount1\n );\n\n function metadata()\n external\n view\n returns (\n uint256 dec0,\n uint256 dec1,\n uint256 r0,\n uint256 r1,\n bool st,\n address t0,\n address t1\n );\n\n function claimFees() external returns (uint256, uint256);\n\n function tokens() external view returns (address, address);\n\n function token0() external view returns (address);\n\n function token1() external view returns (address);\n\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function swap(\n uint256 amount0Out,\n uint256 amount1Out,\n address to,\n bytes calldata data\n ) external;\n\n function burn(address to)\n external\n returns (uint256 amount0, uint256 amount1);\n\n function mint(address to) external returns (uint256 liquidity);\n\n function getReserves()\n external\n view\n returns (\n uint256 _reserve0,\n uint256 _reserve1,\n uint256 _blockTimestampLast\n );\n\n function getAmountOut(uint256, address) external view returns (uint256);\n\n // ERC20 methods\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n\n function totalSupply() external view returns (uint256);\n\n function balanceOf(address) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 value) external returns (bool);\n\n function claimable0(address _user) external view returns (uint256);\n\n function claimable1(address _user) external view returns (uint256);\n\n function isStable() external view returns (bool);\n\n function skim(address to) external;\n\n function sync() external;\n}\n" + }, + "contracts/interfaces/sonic/IVoterV3.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IVoterV3 {\n /// @notice create a gauge\n function createGauge(address _pool, uint256 _gaugeType)\n external\n returns (\n address _gauge,\n address _internal_bribe,\n address _external_bribe\n );\n\n function gauges(address _pool) external view returns (address _gauge);\n}\n" + }, + "contracts/interfaces/sonic/IWrappedSonic.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWrappedSonic {\n event Deposit(address indexed account, uint256 value);\n event Withdrawal(address indexed account, uint256 value);\n event Transfer(address indexed from, address indexed to, uint256 value);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 value) external returns (bool);\n\n function balanceOf(address account) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function depositFor(address account) external payable returns (bool);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address to, uint256 value) external returns (bool);\n\n function transferFrom(\n address from,\n address to,\n uint256 value\n ) external returns (bool);\n\n function withdraw(uint256 value) external;\n\n function withdrawTo(address account, uint256 value) external returns (bool);\n}\n" + }, + "contracts/interfaces/Tether.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// slither-disable-start erc20-interface\ninterface Tether {\n function transfer(address to, uint256 value) external;\n\n function transferFrom(\n address from,\n address to,\n uint256 value\n ) external;\n\n function balanceOf(address) external view returns (uint256);\n\n function approve(address _spender, uint256 _value) external;\n}\n// slither-disable-end erc20-interface\n" + }, + "contracts/interfaces/uniswap/IUniswapUniversalRouter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapUniversalRouter {\n /// @notice Executes encoded commands along with provided inputs. Reverts if deadline has expired.\n /// @param commands A set of concatenated commands, each 1 byte in length\n /// @param inputs An array of byte strings containing abi encoded inputs for each command\n /// @param deadline The deadline by which the transaction must be executed\n function execute(\n bytes calldata commands,\n bytes[] calldata inputs,\n uint256 deadline\n ) external payable;\n\n function execute(bytes calldata commands, bytes[] calldata inputs)\n external\n payable;\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV2Pair.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapV2Pair {\n function token0() external view returns (address);\n\n function token1() external view returns (address);\n\n function getReserves()\n external\n view\n returns (\n uint112 reserve0,\n uint112 reserve1,\n uint32 blockTimestampLast\n );\n\n function price0CumulativeLast() external view returns (uint256);\n\n function price1CumulativeLast() external view returns (uint256);\n\n function sync() external;\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV2Router02.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapV2Router {\n function WETH() external pure returns (address);\n\n function swapExactTokensForTokens(\n uint256 amountIn,\n uint256 amountOutMin,\n address[] calldata path,\n address to,\n uint256 deadline\n ) external returns (uint256[] memory amounts);\n\n function addLiquidity(\n address tokenA,\n address tokenB,\n uint256 amountADesired,\n uint256 amountBDesired,\n uint256 amountAMin,\n uint256 amountBMin,\n address to,\n uint256 deadline\n )\n external\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 liquidity\n );\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV3Router.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// -- Solididy v0.5.x compatible interface\ninterface IUniswapV3Router {\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n}\n" + }, + "contracts/mocks/beacon/ExecutionLayerConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { GeneralPurposeToConsensusLayerRequest } from \"./GeneralPurposeToConsensusLayerRequest.sol\";\n\ncontract ExecutionLayerConsolidation is GeneralPurposeToConsensusLayerRequest {\n event ConsolidationRequestIssued(bytes sourceKey, bytes targetKey);\n bytes public lastSource;\n bytes public lastTarget;\n\n function handleRequest(bytes calldata data) internal override {\n // parameters should consist of twice the 48 bytes for 2 public keys\n require(data.length == 96, \"Invalid Consolidation data\");\n lastSource = data[:48];\n lastTarget = data[48:];\n\n emit ConsolidationRequestIssued(lastSource, lastTarget);\n }\n}\n" + }, + "contracts/mocks/beacon/ExecutionLayerWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { GeneralPurposeToConsensusLayerRequest } from \"./GeneralPurposeToConsensusLayerRequest.sol\";\n\ncontract ExecutionLayerWithdrawal is GeneralPurposeToConsensusLayerRequest {\n event WithdrawalRequestIssued(bytes publicKey, uint64 amount);\n\n bytes public lastPublicKey;\n uint64 public lastAmount;\n\n function handleRequest(bytes calldata data) internal override {\n // parameters should consist of 48 bytes for public key and 8 bytes for uint64 amount\n require(data.length == 56, \"Invalid Withdrawal data\");\n lastPublicKey = data[:48];\n lastAmount = uint64(bytes8(data[48:]));\n emit WithdrawalRequestIssued(lastPublicKey, lastAmount);\n }\n}\n" + }, + "contracts/mocks/beacon/GeneralPurposeToConsensusLayerRequest.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nabstract contract GeneralPurposeToConsensusLayerRequest {\n // solhint-disable no-complex-fallback\n fallback() external payable {\n // fee requested\n if (msg.data.length == 0) {\n uint256 fee = _fee();\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Return a uint256 value\n mstore(0, fee)\n return(0, 32) // Return 32 bytes from memory\n }\n }\n\n // else handle request\n handleRequest(msg.data);\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _fee() internal virtual returns (uint256) {\n return 1;\n }\n\n function handleRequest(bytes calldata data) internal virtual;\n}\n" + }, + "contracts/mocks/BurnableERC20.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ninterface IBurnableERC20 {\n function burn(uint256 value) external returns (bool);\n\n function burnFrom(address account, uint256 value) external returns (bool);\n}\n\n/**\n * @title BurnableERC20\n * @dev Exposes the burn function of ERC20 for tests\n */\nabstract contract BurnableERC20 is IBurnableERC20, ERC20 {\n /**\n * @dev Function to burn tokens\n * @param value The amount of tokens to burn.\n * @return A boolean that indicates if the operation was successful.\n */\n function burn(uint256 value) public virtual override returns (bool) {\n _burn(msg.sender, value);\n return true;\n }\n\n /**\n * @dev Function to burn tokens from a specific account\n * @param account The address with the tokens to burn.\n * @param value The amount of tokens to burn.\n * @return A boolean that indicates if the operation was successful.\n */\n function burnFrom(address account, uint256 value)\n public\n override\n returns (bool)\n {\n _burn(account, value);\n return true;\n }\n}\n" + }, + "contracts/mocks/curve/Mock3CRV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20, MintableERC20 } from \"../MintableERC20.sol\";\nimport { BurnableERC20 } from \"../BurnableERC20.sol\";\n\ncontract Mock3CRV is MintableERC20, BurnableERC20 {\n constructor() ERC20(\"Curve.fi DAI/USDC/USDT\", \"3Crv\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/curve/MockBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { MockRewardPool } from \"./MockRewardPool.sol\";\n\nimport { IRewardStaking } from \"../../strategies/IRewardStaking.sol\";\nimport { IMintableERC20, MintableERC20, ERC20 } from \"../MintableERC20.sol\";\nimport { IBurnableERC20, BurnableERC20 } from \"../BurnableERC20.sol\";\n\ncontract MockDepositToken is MintableERC20, BurnableERC20 {\n constructor() ERC20(\"DCVX\", \"CVX Deposit Token\") {}\n}\n\ncontract MockBooster {\n using SafeERC20 for IERC20;\n\n struct PoolInfo {\n address lptoken;\n address token;\n address crvRewards;\n }\n\n address public minter; // this is CVx for the booster on live\n address public crv; // Curve rewards token\n address public cvx; // Convex rewards token\n mapping(uint256 => PoolInfo) public poolInfo;\n\n constructor(\n address _rewardsMinter,\n address _crv,\n address _cvx\n ) public {\n minter = _rewardsMinter;\n crv = _crv;\n cvx = _cvx;\n }\n\n function setPool(uint256 pid, address _lpToken)\n external\n returns (address rewards)\n {\n address token = address(new MockDepositToken());\n // Deploy a new Convex Rewards Pool\n rewards = address(\n new MockRewardPool(pid, token, crv, cvx, address(this))\n );\n\n poolInfo[pid] = PoolInfo({\n lptoken: _lpToken,\n token: token,\n crvRewards: rewards\n });\n }\n\n function deposit(\n uint256 _pid,\n uint256 _amount,\n bool _stake\n ) public returns (bool) {\n PoolInfo storage pool = poolInfo[_pid];\n\n address lptoken = pool.lptoken;\n\n // hold on to the Curve LP tokens\n IERC20(lptoken).safeTransferFrom(msg.sender, address(this), _amount);\n\n address token = pool.token;\n if (_stake) {\n // mint Convex pool LP tokens and stake in rewards contract on user behalf\n IMintableERC20(token).mint(_amount);\n address rewardContract = pool.crvRewards;\n IERC20(token).safeApprove(rewardContract, 0);\n IERC20(token).safeApprove(rewardContract, _amount);\n IRewardStaking(rewardContract).stakeFor(msg.sender, _amount);\n } else {\n // mint Convex pool LP tokens and send to user\n IMintableERC20(token).mint(_amount);\n IERC20(token).transfer(msg.sender, _amount);\n }\n return true;\n }\n\n // Deposit all Curve LP tokens and stake\n function depositAll(uint256 _pid, bool _stake) external returns (bool) {\n address lptoken = poolInfo[_pid].lptoken;\n uint256 balance = IERC20(lptoken).balanceOf(msg.sender);\n deposit(_pid, balance, _stake);\n return true;\n }\n\n // withdraw Curve LP tokens\n function _withdraw(\n uint256 _pid,\n uint256 _amount,\n address _from,\n address _to\n ) internal {\n PoolInfo storage pool = poolInfo[_pid];\n\n // burn the Convex pool LP tokens\n IBurnableERC20(pool.token).burnFrom(_from, _amount);\n\n // return the Curve LP tokens\n IERC20(pool.lptoken).safeTransfer(_to, _amount);\n }\n\n // withdraw Curve LP tokens\n function withdraw(uint256 _pid, uint256 _amount) public returns (bool) {\n _withdraw(_pid, _amount, msg.sender, msg.sender);\n return true;\n }\n\n // withdraw all Curve LP tokens\n function withdrawAll(uint256 _pid) public returns (bool) {\n address token = poolInfo[_pid].token;\n uint256 userBal = IERC20(token).balanceOf(msg.sender);\n withdraw(_pid, userBal);\n return true;\n }\n\n // allow reward contracts to send here and withdraw to user\n function withdrawTo(\n uint256 _pid,\n uint256 _amount,\n address _to\n ) external returns (bool) {\n address rewardContract = poolInfo[_pid].crvRewards;\n require(msg.sender == rewardContract, \"!auth\");\n\n _withdraw(_pid, _amount, msg.sender, _to);\n return true;\n }\n\n // callback from reward contract when crv is received.\n function rewardClaimed(\n uint256 _pid,\n // solhint-disable-next-line no-unused-vars\n address _address,\n uint256 _amount\n ) external returns (bool) {\n address rewardContract = poolInfo[_pid].crvRewards;\n require(msg.sender == rewardContract, \"!auth\");\n\n //mint reward tokens\n // and transfer it\n IMintableERC20(minter).mint(_amount);\n IERC20(minter).transfer(msg.sender, _amount);\n return true;\n }\n}\n" + }, + "contracts/mocks/curve/MockCRV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../MintableERC20.sol\";\n\ncontract MockCRV is MintableERC20 {\n constructor() ERC20(\"Curve DAO Token\", \"CRV\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/curve/MockCRVMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\n\ncontract MockCRVMinter {\n address crv;\n\n constructor(address _crv) {\n crv = _crv;\n }\n\n function mint(address _address) external {\n uint256 amount = 2e18;\n IMintableERC20(crv).mint(amount);\n IERC20(crv).transfer(_address, amount);\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveAbstractMetapool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { MintableERC20, IMintableERC20 } from \"../MintableERC20.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport \"../../utils/Helpers.sol\";\n\nabstract contract MockCurveAbstractMetapool is MintableERC20 {\n using StableMath for uint256;\n\n address[] public coins;\n uint256[2] public balances;\n\n // Returns the same amount of LP tokens in 1e18 decimals\n function add_liquidity(uint256[2] calldata _amounts, uint256 _minAmount)\n external\n returns (uint256 lpAmount)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transferFrom(\n msg.sender,\n address(this),\n _amounts[i]\n );\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to sum\n lpAmount += _amounts[i].scaleBy(18, assetDecimals);\n balances[i] = balances[i] + _amounts[i];\n }\n }\n // Hacky way of simulating slippage to check _minAmount\n if (lpAmount == 29000e18) lpAmount = 14500e18;\n require(lpAmount >= _minAmount, \"Slippage ruined your day\");\n // Send LP token to sender, e.g. 3CRV\n _mint(msg.sender, lpAmount);\n }\n\n // Dumb implementation that returns the same amount\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n public\n view\n returns (uint256 lpAmount)\n {\n uint256 assetDecimals = Helpers.getDecimals(coins[uint128(_index)]);\n lpAmount = _amount.scaleBy(assetDecimals, 18);\n }\n\n function remove_liquidity_one_coin(\n uint256 _lpAmount,\n int128 _index,\n // solhint-disable-next-line no-unused-vars\n uint256 _minAmount\n ) external returns (uint256 amount) {\n _burn(msg.sender, _lpAmount);\n uint256[] memory amounts = new uint256[](coins.length);\n amounts[uint128(_index)] = _lpAmount;\n amount = calc_withdraw_one_coin(_lpAmount, _index);\n balances[uint128(_index)] -= amount;\n IERC20(coins[uint128(_index)]).transfer(msg.sender, amount);\n }\n\n function get_virtual_price() external pure returns (uint256) {\n return 1e18;\n }\n\n // solhint-disable-next-line no-unused-vars\n function remove_liquidity(uint256 _amount, uint256[2] memory _min_amounts)\n public\n returns (uint256[2] memory amounts)\n {\n _burn(msg.sender, _amount);\n uint256 totalSupply = totalSupply();\n for (uint256 i = 0; i < 2; i++) {\n amounts[i] = totalSupply > 0\n ? (_amount * IERC20(coins[i]).balanceOf(address(this))) /\n totalSupply\n : IERC20(coins[i]).balanceOf(address(this));\n balances[i] -= amounts[i];\n IERC20(coins[i]).transfer(msg.sender, amounts[i]);\n }\n }\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens\n ) public returns (uint256) {\n return\n _remove_liquidity_imbalance(\n _amounts,\n _max_burned_tokens,\n msg.sender\n );\n }\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens,\n address _reveiver\n ) public returns (uint256) {\n return\n _remove_liquidity_imbalance(\n _amounts,\n _max_burned_tokens,\n _reveiver\n );\n }\n\n function _remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens,\n address _reveiver\n ) internal returns (uint256 lpTokens) {\n lpTokens = _max_burned_tokens;\n _burn(msg.sender, lpTokens);\n for (uint256 i = 0; i < _amounts.length; i++) {\n balances[i] -= _amounts[i];\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transfer(_reveiver, _amounts[i]);\n }\n }\n }\n\n // Dumb implementation that sums the scaled amounts\n function calc_token_amount(uint256[2] memory _amounts, bool)\n public\n view\n returns (uint256 lpTokens)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to lpTokens\n lpTokens += _amounts[i].scaleBy(18, assetDecimals);\n }\n }\n\n /// @notice 0.02% fee\n function fee() external pure returns (uint256) {\n return 2000000;\n }\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n\n function burnFrom(address from, uint256 value) public {\n _burn(from, value);\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveGauge.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { ICurveGauge } from \"../../strategies/ICurveGauge.sol\";\n\ncontract MockCurveGauge is ICurveGauge {\n mapping(address => uint256) private _balances;\n address lpToken;\n\n constructor(address _lpToken) {\n lpToken = _lpToken;\n }\n\n function balanceOf(address account) public view override returns (uint256) {\n return _balances[account];\n }\n\n function deposit(uint256 _value, address _account) external override {\n IERC20(lpToken).transferFrom(msg.sender, address(this), _value);\n _balances[_account] += _value;\n }\n\n function withdraw(uint256 _value) external override {\n IERC20(lpToken).transfer(msg.sender, _value);\n // solhint-disable-next-line reentrancy\n _balances[msg.sender] -= _value;\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveMetapool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { MockCurveAbstractMetapool } from \"./MockCurveAbstractMetapool.sol\";\nimport \"../MintableERC20.sol\";\n\ncontract MockCurveMetapool is MockCurveAbstractMetapool {\n constructor(address[2] memory _coins)\n ERC20(\"Curve.fi 3pool/OUSD metapool\", \"3crv_OUSD\")\n {\n coins = _coins;\n }\n}\n" + }, + "contracts/mocks/curve/MockCurvePool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IBurnableERC20 } from \"../BurnableERC20.sol\";\n\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\nimport { ICurvePool } from \"../../strategies/ICurvePool.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport \"../../utils/Helpers.sol\";\n\ncontract MockCurvePool {\n using StableMath for uint256;\n\n address[] public coins;\n uint256[3] public balances;\n address lpToken;\n uint256 public slippage = 1 ether;\n\n constructor(address[3] memory _coins, address _lpToken) {\n coins = _coins;\n lpToken = _lpToken;\n }\n\n function setCoins(address[] memory _coins) external {\n coins = _coins;\n }\n\n // Returns the same amount of LP tokens in 1e18 decimals\n function add_liquidity(uint256[3] calldata _amounts, uint256 _minAmount)\n external\n {\n uint256 sum = 0;\n for (uint256 i = 0; i < _amounts.length; i++) {\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transferFrom(\n msg.sender,\n address(this),\n _amounts[i]\n );\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to sum\n sum += _amounts[i].scaleBy(18, assetDecimals);\n balances[i] = balances[i] + _amounts[i];\n }\n }\n // Hacky way of simulating slippage to check _minAmount\n if (sum == 29000e18) sum = 14500e18;\n require(sum >= _minAmount, \"Slippage ruined your day\");\n // Send LP token to sender, e.g. 3CRV\n IMintableERC20(lpToken).mint(sum);\n IERC20(lpToken).transfer(msg.sender, sum);\n }\n\n // Dumb implementation that returns the same amount\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n public\n view\n returns (uint256)\n {\n uint256 assetDecimals = Helpers.getDecimals(coins[uint128(_index)]);\n return _amount.scaleBy(assetDecimals, 18);\n }\n\n function remove_liquidity_one_coin(\n uint256 _amount,\n int128 _index,\n // solhint-disable-next-line no-unused-vars\n uint256 _minAmount\n ) external {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _amount);\n uint256[] memory amounts = new uint256[](coins.length);\n amounts[uint128(_index)] = _amount;\n uint256 coinAmount = calc_withdraw_one_coin(_amount, _index);\n balances[uint128(_index)] -= coinAmount;\n IERC20(coins[uint128(_index)]).transfer(msg.sender, coinAmount);\n }\n\n function get_virtual_price() external pure returns (uint256) {\n return 1e18;\n }\n\n // solhint-disable-next-line no-unused-vars\n function remove_liquidity(uint256 _lpAmount, uint256[3] memory _min_amounts)\n public\n {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _lpAmount);\n uint256 totalSupply = IERC20(lpToken).totalSupply();\n for (uint256 i = 0; i < 3; i++) {\n uint256 coinAmount = totalSupply > 0\n ? (_lpAmount * IERC20(coins[i]).balanceOf(address(this))) /\n totalSupply\n : IERC20(coins[i]).balanceOf(address(this));\n balances[i] -= coinAmount;\n IERC20(coins[i]).transfer(msg.sender, coinAmount);\n }\n }\n\n function remove_liquidity_imbalance(\n uint256[3] memory _amounts,\n uint256 _max_burned_tokens\n ) public {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _max_burned_tokens);\n // For each coin, transfer to the caller\n for (uint256 i = 0; i < _amounts.length; i++) {\n balances[i] -= _amounts[i];\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transfer(msg.sender, _amounts[i]);\n }\n }\n }\n\n // Dumb implementation that sums the scaled amounts\n function calc_token_amount(uint256[3] memory _amounts, bool)\n public\n view\n returns (uint256 lpTokens)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to lpTokens\n lpTokens += _amounts[i].scaleBy(18, assetDecimals);\n }\n }\n\n function fee() external pure returns (uint256) {\n return 1000000;\n }\n\n function exchange(\n uint256 coin0,\n uint256 coin1,\n uint256 amountIn,\n uint256 minAmountOut\n ) external returns (uint256 amountOut) {\n IERC20(coins[coin0]).transferFrom(msg.sender, address(this), amountIn);\n amountOut = (minAmountOut * slippage) / 1 ether;\n require(amountOut >= minAmountOut, \"Slippage error\");\n IMintableERC20(coins[coin1]).mintTo(msg.sender, amountOut);\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n}\n" + }, + "contracts/mocks/curve/MockCVX.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../MintableERC20.sol\";\n\ncontract MockCVX is MintableERC20 {\n constructor() ERC20(\"CVX\", \"CVX DAO Token\") {}\n}\n" + }, + "contracts/mocks/curve/MockRewardPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\ninterface IDeposit {\n function poolInfo(uint256)\n external\n view\n returns (\n address,\n address,\n address,\n address,\n address,\n bool\n );\n\n function rewardClaimed(\n uint256,\n address,\n uint256\n ) external;\n\n function withdrawTo(\n uint256,\n uint256,\n address\n ) external;\n}\n\ncontract MockRewardPool {\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 public pid;\n address public stakingToken;\n address public rewardTokenA;\n address public rewardTokenB;\n address public operator;\n\n uint256 private _totalSupply;\n\n mapping(address => uint256) private _balances;\n mapping(address => uint256) public rewards;\n\n constructor(\n uint256 _pid,\n address _stakingToken,\n address _rewardTokenA,\n address _rewardTokenB,\n address _operator\n ) public {\n pid = _pid;\n stakingToken = _stakingToken;\n rewardTokenA = _rewardTokenA;\n rewardTokenB = _rewardTokenB;\n operator = _operator;\n }\n\n function totalSupply() public view returns (uint256) {\n return _totalSupply;\n }\n\n function balanceOf(address account) public view returns (uint256) {\n return _balances[account];\n }\n\n function stakeFor(address _for, uint256 _amount) public returns (bool) {\n require(_amount > 0, \"RewardPool : Cannot stake 0\");\n\n //give to _for\n _totalSupply = _totalSupply.add(_amount);\n _balances[_for] = _balances[_for].add(_amount);\n\n //take away from sender\n IERC20(stakingToken).safeTransferFrom(\n msg.sender,\n address(this),\n _amount\n );\n\n return true;\n }\n\n function withdrawAndUnwrap(uint256 amount, bool claim)\n public\n returns (bool)\n {\n _totalSupply = _totalSupply.sub(amount);\n _balances[msg.sender] = _balances[msg.sender].sub(amount);\n\n //tell operator to withdraw from here directly to user\n IDeposit(operator).withdrawTo(pid, amount, msg.sender);\n\n //get rewards too\n if (claim) {\n getReward(msg.sender, true);\n }\n return true;\n }\n\n function withdrawAllAndUnwrap(bool claim) external {\n withdrawAndUnwrap(_balances[msg.sender], claim);\n }\n\n // solhint-disable-next-line no-unused-vars\n function getReward(address _account, bool _claimExtras)\n public\n returns (bool)\n {\n IMintableERC20(rewardTokenA).mint(2 * 1e18);\n IERC20(rewardTokenA).transfer(_account, 2 * 1e18);\n\n IMintableERC20(rewardTokenB).mint(3 * 1e18);\n IERC20(rewardTokenB).transfer(_account, 3 * 1e18);\n\n return true;\n }\n\n function getReward() public returns (bool) {\n getReward(msg.sender, true);\n }\n}\n" + }, + "contracts/mocks/ForceEtherSender.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract ForceEtherSender {\n // Constructor to optionally receive Ether upon deployment\n constructor() payable {}\n\n // Function to allow the contract to receive Ether\n receive() external payable {}\n\n // Function to self-destruct and force-send Ether to an address\n function forceSend(address payable recipient) external {\n // Requires that the contract has a balance greater than 0\n require(address(this).balance > 0, \"No Ether to send\");\n\n // selfdestruct sends all Ether held by the contract to the recipient\n selfdestruct(recipient);\n }\n}\n" + }, + "contracts/mocks/MintableERC20.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ninterface IMintableERC20 {\n function mint(uint256 value) external;\n\n function mintTo(address to, uint256 value) external;\n}\n\n/**\n * @title MintableERC20\n * @dev Exposes the mint function of ERC20 for tests\n */\nabstract contract MintableERC20 is IMintableERC20, ERC20 {\n /**\n * @dev Function to mint tokens\n * @param _value The amount of tokens to mint.\n */\n function mint(uint256 _value) public virtual override {\n _mint(msg.sender, _value);\n }\n\n /**\n * @dev Function to mint tokens\n * @param _to Address to mint to.\n * @param _value The amount of tokens to mint.\n */\n function mintTo(address _to, uint256 _value) public virtual override {\n _mint(_to, _value);\n }\n}\n" + }, + "contracts/mocks/Mock1InchSwapRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { SwapDescription } from \"../interfaces/IOneInch.sol\";\n\ncontract Mock1InchSwapRouter {\n using SafeERC20 for IERC20;\n\n event MockSwap(address executor, bytes permitData, bytes executorData);\n\n event MockSwapDesc(\n address srcToken,\n address dstToken,\n address srcReceiver,\n address dstReceiver,\n uint256 amount,\n uint256 minReturnAmount,\n uint256 flags\n );\n\n event MockUnoswapTo(\n address recipient,\n address srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] pools\n );\n\n event MockUniswapV3SwapTo(\n address recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] pools\n );\n\n /**\n * @dev transfers the shource asset and returns the minReturnAmount of the destination asset.\n */\n function swap(\n address executor,\n SwapDescription calldata desc,\n bytes calldata permitData,\n bytes calldata executorData\n ) public returns (uint256 returnAmount, uint256 spentAmount) {\n // Transfer the source tokens to the receiver contract\n IERC20(desc.srcToken).safeTransferFrom(\n msg.sender,\n desc.srcReceiver,\n desc.amount\n );\n\n // Transfer the destination tokens to the recipient\n IERC20(desc.dstToken).safeTransfer(\n desc.dstReceiver,\n desc.minReturnAmount\n );\n\n emit MockSwap(executor, permitData, executorData);\n _swapDesc(desc);\n returnAmount = 0;\n spentAmount = 0;\n }\n\n function _swapDesc(SwapDescription calldata desc) public {\n emit MockSwapDesc(\n address(desc.srcToken),\n address(desc.dstToken),\n desc.srcReceiver,\n desc.dstReceiver,\n desc.amount,\n desc.minReturnAmount,\n desc.flags\n );\n }\n\n /**\n * @dev only transfers the source asset to this contract.\n * Ideally it would return the destination asset but that's encoded in the pools array.\n */\n function unoswapTo(\n address payable recipient,\n address srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) public returns (uint256 returnAmount) {\n // transfer the from asset from the caller\n IERC20(srcToken).safeTransferFrom(msg.sender, address(this), amount);\n\n emit MockUnoswapTo(recipient, srcToken, amount, minReturn, pools);\n returnAmount = 0;\n }\n\n /**\n * @dev does not do any transfers. Just emits MockUniswapV3SwapTo.\n */\n function uniswapV3SwapTo(\n address payable recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) public returns (uint256 returnAmount) {\n emit MockUniswapV3SwapTo(recipient, amount, minReturn, pools);\n returnAmount = 0;\n }\n}\n" + }, + "contracts/mocks/MockAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { IAaveLendingPool, ILendingPoolAddressesProvider } from \"../strategies/IAave.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// 1. User calls 'getLendingPool'\n// 2. User calls 'deposit' (Aave)\n// - Deposit their underlying\n// - Mint aToken to them\n// 3. User calls redeem (aToken)\n// - Retrieve their aToken\n// - Return equal amount of underlying\n\ncontract MockAToken is MintableERC20 {\n address public lendingPool;\n IERC20 public underlyingToken;\n using SafeERC20 for IERC20;\n\n constructor(\n address _lendingPool,\n string memory _name,\n string memory _symbol,\n IERC20 _underlyingToken\n ) ERC20(_name, _symbol) {\n lendingPool = _lendingPool;\n underlyingToken = _underlyingToken;\n // addMinter(_lendingPool);\n }\n\n function decimals() public view override returns (uint8) {\n return ERC20(address(underlyingToken)).decimals();\n }\n\n function poolRedeem(uint256 _amount, address _to) external {\n require(msg.sender == lendingPool, \"pool only\");\n // Redeem these a Tokens\n _burn(_to, _amount);\n // For the underlying\n underlyingToken.safeTransferFrom(lendingPool, _to, _amount);\n }\n}\n\ncontract MockAave is IAaveLendingPool, ILendingPoolAddressesProvider {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n mapping(address => address) reserveToAToken;\n address pool = address(this);\n address payable core = payable(address(this));\n uint256 factor;\n\n function addAToken(address _aToken, address _underlying) public {\n IERC20(_underlying).safeApprove(_aToken, 0);\n IERC20(_underlying).safeApprove(_aToken, type(uint256).max);\n reserveToAToken[_underlying] = _aToken;\n }\n\n // set the reserve factor / basically the interest on deposit\n // in 18 precision\n // so 0.5% would be 5 * 10 ^ 15\n function setFactor(uint256 factor_) public {\n factor = factor_;\n }\n\n function deposit(\n address _reserve,\n uint256 _amount,\n address _to,\n uint16 /*_referralCode*/\n ) external override {\n uint256 previousBal = IERC20(reserveToAToken[_reserve]).balanceOf(\n msg.sender\n );\n uint256 interest = previousBal.mulTruncate(factor);\n MintableERC20(reserveToAToken[_reserve]).mintTo(msg.sender, interest);\n // Take their reserve\n IERC20(_reserve).safeTransferFrom(msg.sender, address(this), _amount);\n // Credit them with aToken\n MintableERC20(reserveToAToken[_reserve]).mintTo(_to, _amount);\n }\n\n function withdraw(\n address asset,\n uint256 amount,\n address to\n ) external override returns (uint256) {\n MockAToken atoken = MockAToken(reserveToAToken[asset]);\n atoken.poolRedeem(amount, to);\n return amount;\n }\n\n function getLendingPool() external view override returns (address) {\n return pool;\n }\n}\n" + }, + "contracts/mocks/MockAaveIncentivesController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { MockStkAave } from \"./MockStkAave.sol\";\n\ncontract MockAaveIncentivesController {\n mapping(address => uint256) private rewards;\n MockStkAave public REWARD_TOKEN;\n\n constructor(address _reward_token) {\n REWARD_TOKEN = MockStkAave(_reward_token);\n }\n\n function setRewardsBalance(address user, uint256 amount) external {\n rewards[user] = amount;\n }\n\n /**\n * @dev Returns the total of rewards of an user, already accrued + not yet accrued\n * @param user The address of the user\n * @return The rewards\n **/\n // solhint-disable-next-line no-unused-vars\n function getRewardsBalance(address[] calldata assets, address user)\n external\n view\n returns (uint256)\n {\n return rewards[user];\n }\n\n /**\n * @dev Claims reward for an user, on all the assets of the lending pool, accumulating the pending rewards\n * @param amount Amount of rewards to claim\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewards(\n // solhint-disable-next-line no-unused-vars\n address[] calldata assets,\n uint256 amount,\n address to\n ) external returns (uint256) {\n require(amount > 0);\n require(rewards[to] == amount);\n REWARD_TOKEN.mint(amount);\n require(REWARD_TOKEN.transfer(to, amount));\n // solhint-disable-next-line reentrancy\n rewards[to] = 0;\n return amount;\n }\n}\n" + }, + "contracts/mocks/MockAAVEToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAAVEToken is MintableERC20 {\n constructor() ERC20(\"AAVE\", \"AAVE\") {}\n}\n" + }, + "contracts/mocks/MockAero.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAero is MintableERC20 {\n constructor() ERC20(\"Aerodrome\", \"AERO\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/MockAura.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAura is MintableERC20 {\n constructor() ERC20(\"Aura\", \"AURA\") {}\n}\n" + }, + "contracts/mocks/MockBAL.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockBAL is MintableERC20 {\n constructor() ERC20(\"Balancer\", \"BAL\") {}\n}\n" + }, + "contracts/mocks/MockBalancerVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// import \"hardhat/console.sol\";\n\ncontract MockBalancerVault {\n using StableMath for uint256;\n uint256 public slippage = 1 ether;\n bool public transferDisabled = false;\n bool public slippageErrorDisabled = false;\n\n function swap(\n IBalancerVault.SingleSwap calldata singleSwap,\n IBalancerVault.FundManagement calldata funds,\n uint256 minAmountOut,\n uint256\n ) external returns (uint256 amountCalculated) {\n amountCalculated = (minAmountOut * slippage) / 1 ether;\n if (!slippageErrorDisabled) {\n require(amountCalculated >= minAmountOut, \"Slippage error\");\n }\n IERC20(singleSwap.assetIn).transferFrom(\n funds.sender,\n address(this),\n singleSwap.amount\n );\n if (!transferDisabled) {\n MintableERC20(singleSwap.assetOut).mintTo(\n funds.recipient,\n amountCalculated\n );\n }\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n\n function getPoolTokenInfo(bytes32 poolId, address token)\n external\n view\n returns (\n uint256,\n uint256,\n uint256,\n address\n )\n {}\n\n function disableTransfer() external {\n transferDisabled = true;\n }\n\n function disableSlippageError() external {\n slippageErrorDisabled = true;\n }\n}\n" + }, + "contracts/mocks/MockBeaconConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconConsolidation } from \"../beacon/BeaconConsolidation.sol\";\n\ncontract MockBeaconConsolidation {\n function fee() external view returns (uint256) {\n return BeaconConsolidation.fee();\n }\n\n function request(bytes calldata source, bytes calldata target)\n external\n returns (uint256 fee_)\n {\n return BeaconConsolidation.request(source, target);\n }\n}\n" + }, + "contracts/mocks/MockBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"../beacon/BeaconProofsLib.sol\";\nimport { BeaconProofs } from \"../beacon/BeaconProofs.sol\";\n\ncontract MockBeaconProofs is BeaconProofs {\n function concatGenIndices(\n uint256 index1,\n uint256 height2,\n uint256 index2\n ) external pure returns (uint256 genIndex) {\n return BeaconProofsLib.concatGenIndices(index1, height2, index2);\n }\n}\n" + }, + "contracts/mocks/MockBeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconRoots } from \"../beacon/BeaconRoots.sol\";\n\ncontract MockBeaconRoots {\n // Mapping to simulate the ring buffer: timestamp => beacon block root\n mapping(uint256 => bytes32) internal _beaconRoots;\n\n // Event to log when a new root is set (for testing)\n event RootSet(uint256 indexed timestamp, bytes32 root);\n\n // Fallback function to handle raw 32-byte timestamp input\n // solhint-disable no-complex-fallback\n fallback() external {\n // Ensure input is exactly 32 bytes (big-endian encoded timestamp)\n require(msg.data.length == 32, \"Input must be 32 bytes\");\n\n // Decode the 32-byte input as a uint256 timestamp (big-endian)\n uint256 timestamp = abi.decode(msg.data, (uint256));\n\n // Don't do any validation of timestamp so we can test any block\n\n // Retrieve the root. Will return bytes32(0) if not set.\n bytes32 root = _beaconRoots[timestamp];\n\n // Return the 32-byte root directly\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0, root)\n return(0, 32)\n }\n }\n\n // Mock function to set a beacon block root (for testing)\n function setBeaconRoot(uint256 timestamp, bytes32 root) external {\n require(timestamp > 0, \"Invalid timestamp\");\n require(root != bytes32(0), \"Invalid root\");\n\n // Store the root at the given timestamp\n _beaconRoots[timestamp] = root;\n\n emit RootSet(timestamp, root);\n }\n\n function setBeaconRoot(bytes32 root) external {\n require(root != bytes32(0), \"Invalid root\");\n\n // Store the root at the given timestamp\n _beaconRoots[block.timestamp] = root;\n\n emit RootSet(block.timestamp, root);\n }\n\n function parentBlockRoot(uint64 timestamp)\n external\n view\n returns (bytes32 parentRoot)\n {\n return BeaconRoots.parentBlockRoot(timestamp);\n }\n\n function latestBlockRoot()\n external\n view\n returns (bytes32 parentRoot, uint64 timestamp)\n {\n timestamp = uint64(block.timestamp);\n parentRoot = BeaconRoots.parentBlockRoot(timestamp);\n }\n}\n" + }, + "contracts/mocks/MockChainlinkOracleFeed.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\ncontract MockChainlinkOracleFeed is AggregatorV3Interface {\n int256 price;\n uint8 numDecimals;\n\n constructor(int256 _price, uint8 _decimals) {\n price = _price;\n numDecimals = _decimals;\n }\n\n function decimals() external view override returns (uint8) {\n return numDecimals;\n }\n\n function description() external pure override returns (string memory) {\n return \"MockOracleEthFeed\";\n }\n\n function version() external pure override returns (uint256) {\n return 1;\n }\n\n function setPrice(int256 _price) public {\n price = _price;\n }\n\n function setDecimals(uint8 _decimals) public {\n numDecimals = _decimals;\n }\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(uint80 _roundId)\n external\n view\n override\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n )\n {\n roundId = _roundId;\n answer = price;\n startedAt = 0;\n updatedAt = 0;\n answeredInRound = 0;\n }\n\n function latestRoundData()\n external\n view\n override\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n )\n {\n roundId = 0;\n answer = price;\n startedAt = 0;\n updatedAt = block.timestamp;\n answeredInRound = 0;\n }\n}\n" + }, + "contracts/mocks/MockCOMP.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockCOMP is MintableERC20 {\n constructor() ERC20(\"COMP\", \"COMP\") {}\n}\n" + }, + "contracts/mocks/MockComptroller.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract MockComptroller {\n // Claim all the COMP accrued by specific holders in specific markets for their supplies and/or borrows\n function claimComp(\n address[] memory holders,\n address[] memory cTokens,\n bool borrowers,\n bool suppliers\n ) external {}\n}\n" + }, + "contracts/mocks/MockCToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { ICERC20 } from \"../strategies/ICompound.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockCToken is ICERC20, ERC20 {\n using StableMath for uint256;\n\n IERC20 public underlyingToken;\n // underlying = cToken * exchangeRate\n // cToken = underlying / exchangeRate\n uint256 exchangeRate;\n address public override comptroller;\n\n constructor(ERC20 _underlyingToken, address _comptroller)\n ERC20(\"cMock\", \"cMK\")\n {\n uint8 underlyingDecimals = _underlyingToken.decimals();\n // if has 18 dp, exchange rate should be 1e26\n // if has 8 dp, exchange rate should be 1e18\n if (underlyingDecimals > 8) {\n exchangeRate = 10**uint256(18 + underlyingDecimals - 10);\n } else if (underlyingDecimals < 8) {\n // e.g. 18-8+6 = 16\n exchangeRate = 10**uint256(18 - 8 + underlyingDecimals);\n } else {\n exchangeRate = 1e18;\n }\n underlyingToken = _underlyingToken;\n comptroller = _comptroller;\n }\n\n function decimals() public pure override returns (uint8) {\n return 8;\n }\n\n function mint(uint256 mintAmount) public override returns (uint256) {\n // Credit them with cToken\n _mint(msg.sender, mintAmount.divPrecisely(exchangeRate));\n // Take their reserve\n underlyingToken.transferFrom(msg.sender, address(this), mintAmount);\n return 0;\n }\n\n function redeem(uint256 redeemAmount) external override returns (uint256) {\n uint256 tokenAmount = redeemAmount.mulTruncate(exchangeRate);\n // Burn the cToken\n _burn(msg.sender, redeemAmount);\n // Transfer underlying to caller\n underlyingToken.transfer(msg.sender, tokenAmount);\n return 0;\n }\n\n function redeemUnderlying(uint256 redeemAmount)\n external\n override\n returns (uint256)\n {\n uint256 cTokens = redeemAmount.divPrecisely(exchangeRate);\n // Burn the cToken\n _burn(msg.sender, cTokens);\n // Transfer underlying to caller\n underlyingToken.transfer(msg.sender, redeemAmount);\n return 0;\n }\n\n function balanceOfUnderlying(address owner)\n external\n view\n override\n returns (uint256)\n {\n uint256 cTokenBal = this.balanceOf(owner);\n return cTokenBal.mulTruncate(exchangeRate);\n }\n\n function balanceOf(address owner)\n public\n view\n override(ICERC20, ERC20)\n returns (uint256)\n {\n return ERC20.balanceOf(owner);\n }\n\n function updateExchangeRate()\n internal\n view\n returns (uint256 newExchangeRate)\n {\n uint256 factor = 100002 * (10**13); // 0.002%\n newExchangeRate = exchangeRate.mulTruncate(factor);\n }\n\n function exchangeRateStored() external view override returns (uint256) {\n return exchangeRate;\n }\n\n function supplyRatePerBlock() external pure override returns (uint256) {\n return 141 * (10**8);\n }\n}\n" + }, + "contracts/mocks/MockCVXLocker.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MockCVXLocker {\n address public immutable cvx;\n mapping(address => uint256) public lockedBalanceOf;\n\n constructor(address _cvx) {\n cvx = _cvx;\n }\n\n function lock(\n address _account,\n uint256 _amount,\n uint256\n ) external {\n lockedBalanceOf[_account] += _amount;\n ERC20(cvx).transferFrom(msg.sender, address(this), _amount);\n }\n\n function unlockAllTokens(address _account) external {\n lockedBalanceOf[_account] = 0;\n ERC20(cvx).transfer(_account, lockedBalanceOf[_account]);\n }\n}\n" + }, + "contracts/mocks/MockDAI.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockDAI is MintableERC20 {\n constructor() ERC20(\"DAI\", \"DAI\") {}\n}\n" + }, + "contracts/mocks/MockDepositContract.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IDepositContract } from \"./../interfaces/IDepositContract.sol\";\n\ncontract MockDepositContract is IDepositContract {\n uint256 deposit_count;\n\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable override {\n require(pubkey.length == 48, \"DepositContract: invalid pubkey length\");\n require(\n withdrawal_credentials.length == 32,\n \"DepositContract: invalid withdrawal_credentials length\"\n );\n require(\n signature.length == 96,\n \"DepositContract: invalid signature length\"\n );\n\n // Check deposit amount\n require(msg.value >= 1 ether, \"DepositContract: deposit value too low\");\n require(\n msg.value % 1 gwei == 0,\n \"DepositContract: deposit value not multiple of gwei\"\n );\n uint256 deposit_amount = msg.value / 1 gwei;\n require(\n deposit_amount <= type(uint64).max,\n \"DepositContract: deposit value too high\"\n );\n\n // Emit `DepositEvent` log\n bytes memory amount = to_little_endian_64(uint64(deposit_amount));\n emit DepositEvent(\n pubkey,\n withdrawal_credentials,\n amount,\n signature,\n to_little_endian_64(uint64(deposit_count))\n );\n require(\n deposit_data_root != 0,\n \"DepositContract: invalid deposit_data_root\"\n );\n }\n\n function get_deposit_root() external view override returns (bytes32) {\n // just return some bytes32\n return sha256(abi.encodePacked(deposit_count, bytes16(0)));\n }\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view override returns (bytes memory) {\n return to_little_endian_64(uint64(deposit_count));\n }\n\n function to_little_endian_64(uint64 value)\n internal\n pure\n returns (bytes memory ret)\n {\n ret = new bytes(8);\n bytes8 bytesValue = bytes8(value);\n // Byteswapping during copying to bytes.\n ret[0] = bytesValue[7];\n ret[1] = bytesValue[6];\n ret[2] = bytesValue[5];\n ret[3] = bytesValue[4];\n ret[4] = bytesValue[3];\n ret[5] = bytesValue[2];\n ret[6] = bytesValue[1];\n ret[7] = bytesValue[0];\n }\n}\n" + }, + "contracts/mocks/MockEvilDAI.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract MockEvilDAI is MintableERC20 {\n address host;\n address realCoin;\n\n constructor(address _host, address _realCoin) ERC20(\"DAI\", \"DAI\") {\n host = _host;\n realCoin = _realCoin;\n }\n\n function transferFrom(\n // solhint-disable-next-line no-unused-vars\n address _from,\n // solhint-disable-next-line no-unused-vars\n address _to,\n uint256 _amount\n ) public override returns (bool) {\n // call mint again!\n if (_amount != 69) {\n IVault(host).mint(address(this), 69, 0);\n }\n return true;\n }\n}\n" + }, + "contracts/mocks/MockEvilReentrantContract.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IRateProvider } from \"../interfaces/balancer/IRateProvider.sol\";\n\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { IERC20 } from \"../utils/InitializableAbstractStrategy.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockEvilReentrantContract {\n using StableMath for uint256;\n\n IBalancerVault public immutable balancerVault;\n IERC20 public immutable reth;\n IERC20 public immutable weth;\n IVault public immutable oethVault;\n address public immutable poolAddress;\n bytes32 public immutable balancerPoolId;\n\n constructor(\n address _balancerVault,\n address _oethVault,\n address _reth,\n address _weth,\n address _poolAddress,\n bytes32 _poolId\n ) {\n balancerVault = IBalancerVault(_balancerVault);\n oethVault = IVault(_oethVault);\n reth = IERC20(_reth);\n weth = IERC20(_weth);\n poolAddress = _poolAddress;\n balancerPoolId = _poolId;\n }\n\n function doEvilStuff() public {\n address priceProvider = oethVault.priceProvider();\n uint256 rethPrice = IOracle(priceProvider).price(address(reth));\n\n // 1. Join pool\n uint256[] memory amounts = new uint256[](2);\n amounts[0] = uint256(10 ether);\n amounts[1] = rethPrice * 10;\n\n address[] memory assets = new address[](2);\n assets[0] = address(reth);\n assets[1] = address(weth);\n\n uint256 minBPT = getBPTExpected(assets, amounts).mulTruncate(\n 0.99 ether\n );\n\n bytes memory joinUserData = abi.encode(\n IBalancerVault.WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,\n amounts,\n minBPT\n );\n\n IBalancerVault.JoinPoolRequest memory joinRequest = IBalancerVault\n .JoinPoolRequest(assets, amounts, joinUserData, false);\n\n balancerVault.joinPool(\n balancerPoolId,\n address(this),\n address(this),\n joinRequest\n );\n\n uint256 bptTokenBalance = IERC20(poolAddress).balanceOf(address(this));\n\n // 2. Redeem as ETH\n bytes memory exitUserData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT,\n bptTokenBalance,\n 1\n );\n\n assets[1] = address(0); // Receive ETH instead of WETH\n uint256[] memory exitAmounts = new uint256[](2);\n exitAmounts[1] = 15 ether;\n IBalancerVault.ExitPoolRequest memory exitRequest = IBalancerVault\n .ExitPoolRequest(assets, exitAmounts, exitUserData, false);\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n payable(address(this)),\n exitRequest\n );\n bptTokenBalance = IERC20(poolAddress).balanceOf(address(this));\n }\n\n function getBPTExpected(address[] memory _assets, uint256[] memory _amounts)\n internal\n view\n virtual\n returns (uint256 bptExpected)\n {\n // Get the oracle from the OETH Vault\n address priceProvider = oethVault.priceProvider();\n\n for (uint256 i = 0; i < _assets.length; ++i) {\n uint256 strategyAssetMarketPrice = IOracle(priceProvider).price(\n _assets[i]\n );\n // convert asset amount to ETH amount\n bptExpected =\n bptExpected +\n _amounts[i].mulTruncate(strategyAssetMarketPrice);\n }\n\n uint256 bptRate = IRateProvider(poolAddress).getRate();\n // Convert ETH amount to BPT amount\n bptExpected = bptExpected.divPrecisely(bptRate);\n }\n\n function approveAllTokens() public {\n // Approve all tokens\n weth.approve(address(oethVault), type(uint256).max);\n reth.approve(poolAddress, type(uint256).max);\n weth.approve(poolAddress, type(uint256).max);\n reth.approve(address(balancerVault), type(uint256).max);\n weth.approve(address(balancerVault), type(uint256).max);\n }\n\n receive() external payable {\n // 3. Try to mint OETH\n oethVault.mint(address(weth), 1 ether, 0.9 ether);\n }\n}\n" + }, + "contracts/mocks/MockfrxETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockfrxETH is MintableERC20 {\n constructor() ERC20(\"frxETH\", \"frxETH\") {}\n}\n" + }, + "contracts/mocks/MockFrxETHMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockFrxETHMinter {\n address public immutable frxETH;\n address public immutable sfrxETH;\n\n constructor(address _frxETH, address _sfrxETH) {\n frxETH = _frxETH;\n sfrxETH = _sfrxETH;\n }\n\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares)\n {\n IMintableERC20(frxETH).mintTo(sfrxETH, msg.value);\n IMintableERC20(sfrxETH).mintTo(recipient, msg.value);\n shares = msg.value;\n }\n}\n" + }, + "contracts/mocks/MockLimitedWrappedOusd.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WrappedOusd } from \"../token/WrappedOusd.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MockLimitedWrappedOusd is WrappedOusd {\n constructor(ERC20 underlying_) WrappedOusd(underlying_) {}\n\n function maxDeposit(address)\n public\n view\n virtual\n override\n returns (uint256)\n {\n return 1e18;\n }\n}\n" + }, + "contracts/mocks/MockMaverickDistributor.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Pool } from \"../interfaces/plume/IMaverickV2Pool.sol\";\n\ncontract MockMaverickDistributor {\n IERC20 public immutable rewardToken;\n\n uint256 public lastEpoch;\n uint256 public rewardAmount;\n\n constructor(address _rewardToken) {\n rewardToken = IERC20(_rewardToken);\n }\n\n function setLastEpoch(uint256 _epoch) external {\n lastEpoch = _epoch;\n }\n\n function setRewardTokenAmount(uint256 _amount) external {\n rewardAmount = _amount;\n }\n\n function claimLp(\n address recipient,\n uint256,\n IMaverickV2Pool,\n uint32[] memory,\n uint256\n ) external returns (uint256 amount) {\n rewardToken.transfer(recipient, rewardAmount);\n return rewardAmount;\n }\n}\n" + }, + "contracts/mocks/MockMetadataToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// IERC20Metadata is used in the resolveAsset function in contracts/utils/assets.js\n// We just need to import it here to make its ABI available to Hardhat\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n" + }, + "contracts/mocks/MockMintableUniswapPair.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport \"./MockUniswapPair.sol\";\n\ncontract MockMintableUniswapPair is MockUniswapPair, MintableERC20 {\n constructor(\n address _token0,\n address _token1,\n uint112 _reserve0,\n uint112 _reserve1\n )\n MockUniswapPair(_token0, _token1, _reserve0, _reserve1)\n ERC20(\"Uniswap V2\", \"UNI-v2\")\n {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/MockNonRebasing.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { OUSD } from \"../token/OUSD.sol\";\n\ncontract MockNonRebasing {\n OUSD oUSD;\n\n function setOUSD(address _oUSDAddress) public {\n oUSD = OUSD(_oUSDAddress);\n }\n\n function rebaseOptIn() public {\n oUSD.rebaseOptIn();\n }\n\n function rebaseOptOut() public {\n oUSD.rebaseOptOut();\n }\n\n function transfer(address _to, uint256 _value) public {\n oUSD.transfer(_to, _value);\n }\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) public {\n oUSD.transferFrom(_from, _to, _value);\n }\n\n function approve(address _spender, uint256 _addedValue) public {\n oUSD.approve(_spender, _addedValue);\n }\n\n function mintOusd(\n address _vaultContract,\n address _asset,\n uint256 _amount\n ) public {\n IVault(_vaultContract).mint(_asset, _amount, 0);\n }\n\n function redeemOusd(address _vaultContract, uint256 _amount) public {\n IVault(_vaultContract).redeem(_amount, 0);\n }\n\n function approveFor(\n address _contract,\n address _spender,\n uint256 _addedValue\n ) public {\n IERC20(_contract).approve(_spender, _addedValue);\n }\n}\n" + }, + "contracts/mocks/MockNonStandardToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\nimport \"./MintableERC20.sol\";\n\n/**\n * Mock token contract to simulate tokens that don't\n * throw/revert when a transfer/transferFrom call fails\n */\ncontract MockNonStandardToken is MintableERC20 {\n using SafeMath for uint256;\n\n constructor() ERC20(\"NonStandardToken\", \"NonStandardToken\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n\n function transfer(address recipient, uint256 amount)\n public\n override\n returns (bool)\n {\n if (balanceOf(msg.sender) < amount) {\n // Fail silently\n return false;\n }\n\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public override returns (bool) {\n if (balanceOf(sender) < amount) {\n // Fail silently\n return false;\n }\n\n _transfer(sender, recipient, amount);\n _approve(\n sender,\n _msgSender(),\n allowance(sender, _msgSender()).sub(\n amount,\n \"ERC20: transfer amount exceeds allowance\"\n )\n );\n return true;\n }\n}\n" + }, + "contracts/mocks/MockOETHVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultCore } from \"../vault/OETHVaultCore.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MockOETHVault is OETHVaultCore {\n using StableMath for uint256;\n\n constructor(address _weth) OETHVaultCore(_weth) {\n _setGovernor(msg.sender);\n }\n\n function supportAsset(address asset) external {\n assets[asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(0),\n decimals: 18,\n allowedOracleSlippageBps: 0\n });\n\n allAssets.push(asset);\n }\n}\n" + }, + "contracts/mocks/MockOETHVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"../vault/OETHVaultAdmin.sol\";\n\ncontract MockOETHVaultAdmin is OETHVaultAdmin {\n constructor(address _weth) OETHVaultAdmin(_weth) {}\n\n // fetches the WETH amount in outstanding withdrawals\n function outstandingWithdrawalsAmount()\n external\n view\n returns (uint256 wethAmount)\n {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n wethAmount = queue.queued - queue.claimed;\n }\n\n function wethAvailable() external view returns (uint256) {\n return _wethAvailable();\n }\n}\n" + }, + "contracts/mocks/MockOGN.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./BurnableERC20.sol\";\nimport \"./MintableERC20.sol\";\n\n/**\n * @title Origin token (OGN).\n *\n * @dev Token that allows minting and burning.\n * @dev Important note:\n * @dev There is a known race condition in the ERC20 standard on the approve() method.\n * @dev See details: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n * @dev The Origin token contract implements the increaseApproval() and decreaseApproval() methods.\n * @dev It is strongly recommended to use those methods rather than approve()\n * @dev when updating the token allowance.\n */\ncontract MockOGN is MintableERC20, BurnableERC20 {\n event SetWhitelistExpiration(uint256 expiration);\n event AllowedTransactorAdded(address sender);\n event AllowedTransactorRemoved(address sender);\n event AddCallSpenderWhitelist(address enabler, address spender);\n event RemoveCallSpenderWhitelist(address disabler, address spender);\n\n mapping(address => bool) public callSpenderWhitelist;\n address public owner = msg.sender;\n // UNIX timestamp (in seconds) after which this whitelist no longer applies\n uint256 public whitelistExpiration;\n // While the whitelist is active, either the sender or recipient must be\n // in allowedTransactors.\n mapping(address => bool) public allowedTransactors;\n\n // @dev Constructor that gives msg.sender all initial tokens.\n constructor(uint256 _initialSupply) ERC20(\"OriginToken\", \"OGN\") {\n owner = msg.sender;\n _mint(owner, _initialSupply);\n }\n\n //\n // approveAndCall methods\n //\n\n // @dev Add spender to whitelist of spenders for approveAndCall\n // @param _spender Address to add\n function addCallSpenderWhitelist(address _spender) public onlyOwner {\n callSpenderWhitelist[_spender] = true;\n emit AddCallSpenderWhitelist(msg.sender, _spender);\n }\n\n // @dev Remove spender from whitelist of spenders for approveAndCall\n // @param _spender Address to remove\n function removeCallSpenderWhitelist(address _spender) public onlyOwner {\n delete callSpenderWhitelist[_spender];\n emit RemoveCallSpenderWhitelist(msg.sender, _spender);\n }\n\n // @dev Approve transfer of tokens and make a contract call in a single\n // @dev transaction. This allows a DApp to avoid requiring two MetaMask\n // @dev approvals for a single logical action, such as creating a listing,\n // @dev which requires the seller to approve a token transfer and the\n // @dev marketplace contract to transfer tokens from the seller.\n //\n // @dev This is based on the ERC827 function approveAndCall and avoids\n // @dev security issues by only working with a whitelisted set of _spender\n // @dev addresses. The other difference is that the combination of this\n // @dev function ensures that the proxied function call receives the\n // @dev msg.sender for this function as its first parameter.\n //\n // @param _spender The address that will spend the funds.\n // @param _value The amount of tokens to be spent.\n // @param _selector Function selector for function to be called.\n // @param _callParams Packed, encoded parameters, omitting the first parameter which is always msg.sender\n function approveAndCallWithSender(\n address _spender,\n uint256 _value,\n bytes4 _selector,\n bytes memory _callParams\n ) public payable returns (bool) {\n require(_spender != address(this), \"token contract can't be approved\");\n require(callSpenderWhitelist[_spender], \"spender not in whitelist\");\n\n require(super.approve(_spender, _value), \"approve failed\");\n\n bytes memory callData = abi.encodePacked(\n _selector,\n uint256(uint160(msg.sender)),\n _callParams\n );\n // solium-disable-next-line security/no-call-value\n (bool success, ) = _spender.call{ value: msg.value }(callData);\n require(success, \"proxied call failed\");\n return true;\n }\n\n //\n // Functions for maintaining whitelist\n //\n\n modifier onlyOwner() {\n require(msg.sender == owner);\n _;\n }\n modifier allowedTransfer(address _from, address _to) {\n require(\n // solium-disable-next-line operator-whitespace\n !whitelistActive() ||\n allowedTransactors[_from] ||\n allowedTransactors[_to],\n \"neither sender nor recipient are allowed\"\n );\n _;\n }\n\n function whitelistActive() public view returns (bool) {\n return block.timestamp < whitelistExpiration;\n }\n\n function addAllowedTransactor(address _transactor) public onlyOwner {\n emit AllowedTransactorAdded(_transactor);\n allowedTransactors[_transactor] = true;\n }\n\n function removeAllowedTransactor(address _transactor) public onlyOwner {\n emit AllowedTransactorRemoved(_transactor);\n delete allowedTransactors[_transactor];\n }\n\n /**\n * @dev Set the whitelist expiration, after which the whitelist no longer\n * applies.\n */\n function setWhitelistExpiration(uint256 _expiration) public onlyOwner {\n // allow only if whitelist expiration hasn't yet been set, or if the\n // whitelist expiration hasn't passed yet\n require(\n whitelistExpiration == 0 || whitelistActive(),\n \"an expired whitelist cannot be extended\"\n );\n // prevent possible mistakes in calling this function\n require(\n _expiration >= block.timestamp + 1 days,\n \"whitelist expiration not far enough into the future\"\n );\n emit SetWhitelistExpiration(_expiration);\n whitelistExpiration = _expiration;\n }\n\n //\n // ERC20 transfer functions that have been overridden to enforce the\n // whitelist.\n //\n\n function transfer(address _to, uint256 _value)\n public\n override\n allowedTransfer(msg.sender, _to)\n returns (bool)\n {\n return super.transfer(_to, _value);\n }\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) public override allowedTransfer(_from, _to) returns (bool) {\n return super.transferFrom(_from, _to, _value);\n }\n}\n" + }, + "contracts/mocks/MockOGV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockOGV is MintableERC20 {\n constructor() ERC20(\"OGV\", \"OGV\") {}\n}\n" + }, + "contracts/mocks/MockOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/IPriceOracle.sol\";\nimport \"../interfaces/IMinMaxOracle.sol\";\n\n/**\n * Mock of both price Oracle and min max oracles\n */\ncontract MockOracle is IPriceOracle, IMinMaxOracle {\n mapping(bytes32 => uint256) prices;\n mapping(bytes32 => uint256[]) pricesMinMax;\n uint256 ethMin;\n uint256 ethMax;\n\n /**\n * @dev returns the asset price in USD, 6 decimal digits.\n * Compatible with the Open Price Feed.\n */\n function price(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n return prices[keccak256(abi.encodePacked(symbol))];\n }\n\n /**\n * @dev sets the price of the asset in USD, 6 decimal digits\n *\n */\n function setPrice(string calldata symbol, uint256 _price) external {\n prices[keccak256(abi.encodePacked(symbol))] = _price;\n }\n\n /**\n * @dev sets the min and max price of ETH in USD, 6 decimal digits\n *\n */\n function setEthPriceMinMax(uint256 _min, uint256 _max) external {\n ethMin = _min;\n ethMax = _max;\n }\n\n /**\n * @dev sets the prices Min Max for a specific symbol in ETH, 8 decimal digits\n *\n */\n function setTokPriceMinMax(\n string calldata symbol,\n uint256 _min,\n uint256 _max\n ) external {\n pricesMinMax[keccak256(abi.encodePacked(symbol))] = [_min, _max];\n }\n\n /**\n * @dev get the price of asset in ETH, 8 decimal digits.\n */\n function priceMin(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n uint256[] storage pMinMax = pricesMinMax[\n keccak256(abi.encodePacked(symbol))\n ];\n return (pMinMax[0] * ethMin) / 1e6;\n }\n\n /**\n * @dev get the price of asset in USD, 8 decimal digits.\n * Not needed for now\n */\n function priceMax(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n uint256[] storage pMinMax = pricesMinMax[\n keccak256(abi.encodePacked(symbol))\n ];\n return (pMinMax[1] * ethMax) / 1e6;\n }\n}\n" + }, + "contracts/mocks/MockOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"../oracle/AbstractOracleRouter.sol\";\n\n// @notice Oracle Router required for testing environment\ncontract MockOracleRouter is AbstractOracleRouter {\n struct FeedMetadata {\n address feedAddress;\n uint256 maxStaleness;\n }\n\n mapping(address => FeedMetadata) public assetToFeedMetadata;\n\n /* @dev Override feed and maxStaleness information for a particular asset\n * @param _asset the asset to override feed for\n * @param _feed new feed\n * @param _maxStaleness new maximum time allowed for feed data to be stale\n */\n function setFeed(\n address _asset,\n address _feed,\n uint256 _maxStaleness\n ) external {\n assetToFeedMetadata[_asset] = FeedMetadata(_feed, _maxStaleness);\n }\n\n /*\n * The dev version of the Oracle doesn't need to gas optimize and cache the decimals\n */\n function getDecimals(address _feed) internal view override returns (uint8) {\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n return AggregatorV3Interface(_feed).decimals();\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n FeedMetadata storage fm = assetToFeedMetadata[asset];\n feedAddress = fm.feedAddress;\n maxStaleness = fm.maxStaleness;\n }\n}\n" + }, + "contracts/mocks/MockOracleRouterNoStale.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { OracleRouter } from \"../oracle/OracleRouter.sol\";\nimport { OETHOracleRouter } from \"../oracle/OETHOracleRouter.sol\";\n\n// @notice Oracle Router used to bypass staleness\ncontract MockOracleRouterNoStale is OracleRouter {\n function feedMetadata(address asset)\n internal\n pure\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n (feedAddress, ) = super.feedMetadata(asset);\n maxStaleness = 365 days;\n }\n}\n\n// @notice Oracle Router used to bypass staleness\ncontract MockOETHOracleRouterNoStale is OETHOracleRouter {\n constructor() OETHOracleRouter() {}\n\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n (feedAddress, ) = super.feedMetadata(asset);\n maxStaleness = 365 days;\n }\n}\n" + }, + "contracts/mocks/MockOracleWeightedPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Variable, OracleAverageQuery, IOracleWeightedPool } from \"../interfaces/balancer/IOracleWeightedPool.sol\";\n\ncontract MockOracleWeightedPool is IOracleWeightedPool {\n uint256[] public nextResults;\n\n constructor() {\n nextResults = [1 ether, 1 ether];\n }\n\n function getTimeWeightedAverage(OracleAverageQuery[] memory)\n external\n view\n override\n returns (uint256[] memory results)\n {\n return nextResults;\n }\n\n function setNextResults(uint256[] calldata results) external {\n nextResults = results;\n }\n}\n" + }, + "contracts/mocks/MockPartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PartialWithdrawal } from \"../beacon/PartialWithdrawal.sol\";\n\ncontract MockPartialWithdrawal {\n function fee() external view returns (uint256) {\n return PartialWithdrawal.fee();\n }\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n external\n returns (uint256 fee_)\n {\n return PartialWithdrawal.request(validatorPubKey, amount);\n }\n}\n" + }, + "contracts/mocks/MockRebornMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n// solhint-disable-next-line no-console\nimport \"hardhat/console.sol\";\n\ncontract Sanctum {\n address public asset;\n address public vault;\n address public reborner;\n bool public shouldAttack = false;\n // should selfdestruct in the constructor\n bool public shouldDestruct = false;\n uint256 public targetMethod;\n address public ousdContract;\n\n constructor(address _asset, address _vault) {\n asset = _asset;\n vault = _vault;\n }\n\n function deploy(uint256 salt, bytes memory bytecode)\n public\n returns (address addr)\n {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt)\n }\n require(addr != address(0), \"Create2: Failed on deploy\");\n }\n\n function computeAddress(uint256 salt, bytes memory bytecode)\n public\n view\n returns (address)\n {\n bytes32 bytecodeHashHash = keccak256(bytecode);\n bytes32 _data = keccak256(\n abi.encodePacked(\n bytes1(0xff),\n address(this),\n salt,\n bytecodeHashHash\n )\n );\n return address(bytes20(_data << 96));\n }\n\n function setShouldAttack(bool _shouldAttack) public {\n shouldAttack = _shouldAttack;\n }\n\n // should call selfdestruct in the constructor\n function setShouldDesctruct(bool _shouldDestruct) public {\n shouldDestruct = _shouldDestruct;\n }\n\n function setTargetMethod(uint256 target) public {\n targetMethod = target;\n }\n\n function setOUSDAddress(address _ousdContract) public {\n ousdContract = _ousdContract;\n }\n}\n\ncontract Reborner {\n Sanctum sanctum;\n bool logging = false;\n\n constructor(address _sanctum) {\n log(\"We are created...\");\n sanctum = Sanctum(_sanctum);\n if (sanctum.shouldAttack()) {\n log(\"We are attacking now...\");\n\n uint256 target = sanctum.targetMethod();\n\n if (target == 1) {\n redeem();\n } else if (target == 2) {\n transfer();\n } else {\n mint();\n }\n }\n\n if (sanctum.shouldDestruct()) {\n bye();\n }\n }\n\n function mint() public {\n log(\"We are attempting to mint..\");\n address asset = sanctum.asset();\n address vault = sanctum.vault();\n IERC20(asset).approve(vault, 1e18);\n IVault(vault).mint(asset, 1e18, 0);\n log(\"We are now minting..\");\n }\n\n function redeem() public {\n log(\"We are attempting to redeem..\");\n address vault = sanctum.vault();\n IVault(vault).redeem(1e18, 1e18);\n log(\"We are now redeeming..\");\n }\n\n function transfer() public {\n log(\"We are attempting to transfer..\");\n address ousd = sanctum.ousdContract();\n require(IERC20(ousd).transfer(address(1), 1e18), \"transfer failed\");\n log(\"We are now transfering..\");\n }\n\n function bye() public {\n log(\"We are now destructing..\");\n selfdestruct(payable(msg.sender));\n }\n\n function log(string memory message) internal view {\n if (logging) {\n // solhint-disable-next-line no-console\n console.log(message);\n }\n }\n}\n" + }, + "contracts/mocks/MockRETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport \"../interfaces/IGetExchangeRateToken.sol\";\n\ncontract MockRETH is MintableERC20, IGetExchangeRateToken {\n uint256 private exchangeRate = 12e17;\n\n constructor() ERC20(\"Rocket Pool ETH\", \"rETH\") {}\n\n function getExchangeRate() external view override returns (uint256) {\n return exchangeRate;\n }\n\n function setExchangeRate(uint256 _rate) external {\n exchangeRate = _rate;\n }\n}\n" + }, + "contracts/mocks/MockRoosterAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Rooster AMO strategy exposing extra functionality\n * @author Origin Protocol Inc\n */\n\nimport { RoosterAMOStrategy } from \"../strategies/plume/RoosterAMOStrategy.sol\";\nimport { IMaverickV2Pool } from \"../interfaces/plume/IMaverickV2Pool.sol\";\n\ncontract MockRoosterAMOStrategy is RoosterAMOStrategy {\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethpAddress,\n address _liquidityManager,\n address _poolLens,\n address _maverickPosition,\n address _maverickQuoter,\n address _mPool,\n bool _upperTickAtParity,\n address _votingDistributor,\n address _poolDistributor\n )\n RoosterAMOStrategy(\n _stratConfig,\n _wethAddress,\n _oethpAddress,\n _liquidityManager,\n _poolLens,\n _maverickPosition,\n _maverickQuoter,\n _mPool,\n _upperTickAtParity,\n _votingDistributor,\n _poolDistributor\n )\n {}\n\n function getCurrentWethShare() external view returns (uint256) {\n uint256 _currentPrice = getPoolSqrtPrice();\n\n return _getWethShare(_currentPrice);\n }\n}\n" + }, + "contracts/mocks/MockSFC.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockSFC {\n error ZeroAmount();\n error TransferFailed();\n error StakeIsFullySlashed();\n\n // Mapping of delegator address to validator ID to amount delegated\n mapping(address => mapping(uint256 => uint256)) public delegations;\n // Mapping of delegator address to validator ID to withdrawal request ID to amount\n mapping(address => mapping(uint256 => mapping(uint256 => uint256)))\n public withdraws;\n // validator ID -> slashing refund ratio (allows to withdraw slashed stake)\n mapping(uint256 => uint256) public slashingRefundRatio;\n\n function getStake(address delegator, uint256 validatorID)\n external\n view\n returns (uint256)\n {\n return delegations[delegator][validatorID];\n }\n\n function delegate(uint256 validatorID) external payable {\n if (msg.value == 0) {\n revert ZeroAmount();\n }\n delegations[msg.sender][validatorID] += msg.value;\n }\n\n function undelegate(\n uint256 validatorID,\n uint256 wrID,\n uint256 amount\n ) external {\n require(\n delegations[msg.sender][validatorID] >= amount,\n \"insufficient stake\"\n );\n require(\n withdraws[msg.sender][validatorID][wrID] == 0,\n \"withdrawal request already exists\"\n );\n\n delegations[msg.sender][validatorID] -= amount;\n withdraws[msg.sender][validatorID][wrID] = amount;\n }\n\n function withdraw(uint256 validatorID, uint256 wrID) external {\n require(withdraws[msg.sender][validatorID][wrID] > 0, \"no withdrawal\");\n\n uint256 withdrawAmount = withdraws[msg.sender][validatorID][wrID];\n uint256 penalty = (withdrawAmount *\n (1e18 - slashingRefundRatio[validatorID])) / 1e18;\n\n if (penalty >= withdrawAmount) {\n revert StakeIsFullySlashed();\n }\n\n (bool sent, ) = msg.sender.call{ value: withdrawAmount - penalty }(\"\");\n if (!sent) {\n revert TransferFailed();\n }\n }\n\n function pendingRewards(address delegator, uint256 validatorID)\n external\n view\n returns (uint256)\n {}\n\n function claimRewards(uint256 validatorID) external {}\n\n function restakeRewards(uint256 validatorID) external {}\n\n /// @param refundRatio the percentage of the staked amount that can be refunded. 0.1e18 = 10%, 1e18 = 100%\n function slashValidator(uint256 validatorID, uint256 refundRatio) external {\n require(refundRatio <= 1e18, \"invalid refund ratio\");\n slashingRefundRatio[validatorID] = refundRatio;\n }\n}\n" + }, + "contracts/mocks/MocksfrxETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MocksfrxETH is MintableERC20 {\n address public frxETH;\n\n constructor(address _frxETH) ERC20(\"sfrxETH\", \"sfrxETH\") {\n frxETH = _frxETH;\n }\n\n function setMockfrxETHAddress(address _frxETH) external {\n frxETH = _frxETH;\n }\n\n function deposit(uint256 assets, address receiver)\n external\n returns (uint256 shares)\n {\n ERC20(frxETH).transferFrom(msg.sender, address(this), assets);\n\n _mint(receiver, assets);\n\n return assets;\n }\n\n function maxWithdraw(address owner) external view returns (uint256) {\n return balanceOf(owner);\n }\n\n function setMaxWithdrawableBalance(address owner, uint256 balance)\n external\n {\n uint256 currentBalance = balanceOf(owner);\n if (currentBalance > balance) {\n _burn(owner, currentBalance - balance);\n } else if (balance > currentBalance) {\n _mint(owner, balance - currentBalance);\n }\n }\n\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets) {\n _burn(owner, shares);\n\n ERC20(frxETH).transfer(receiver, shares);\n\n assets = shares;\n }\n\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares) {\n _burn(owner, assets);\n\n ERC20(frxETH).transfer(receiver, assets);\n\n shares = assets;\n }\n\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares)\n {\n _mint(recipient, msg.value);\n shares = msg.value;\n }\n}\n" + }, + "contracts/mocks/MockSSV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockSSV is MintableERC20 {\n constructor() ERC20(\"SSV Token\", \"SSV\") {}\n}\n" + }, + "contracts/mocks/MockSSVNetwork.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Cluster } from \"./../interfaces/ISSVNetwork.sol\";\n\ncontract MockSSVNetwork {\n function registerValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function exitValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds\n ) external {}\n\n function removeValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external {}\n\n function deposit(\n address clusterOwner,\n uint64[] calldata operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function setFeeRecipientAddress(address recipient) external {}\n}\n" + }, + "contracts/mocks/MockstETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockstETH is MintableERC20 {\n constructor() ERC20(\"stETH\", \"stETH\") {}\n}\n" + }, + "contracts/mocks/MockStkAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"./MintableERC20.sol\";\n\ncontract MockStkAave is MintableERC20 {\n uint256 public COOLDOWN_SECONDS = 864000;\n uint256 public UNSTAKE_WINDOW = 172800;\n address public STAKED_TOKEN;\n\n mapping(address => uint256) public stakerRewardsToClaim;\n mapping(address => uint256) public stakersCooldowns;\n\n using SafeERC20 for IERC20;\n\n constructor(address _stakedToken) ERC20(\"Staked Aave\", \"stkAAVE\") {\n STAKED_TOKEN = _stakedToken;\n }\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n\n function setStakedToken(address _stakedToken) external {\n STAKED_TOKEN = _stakedToken;\n }\n\n /**\n * @dev Redeems staked tokens, and stop earning rewards\n * @param to Address to redeem to\n * @param amount Amount to redeem\n **/\n function redeem(address to, uint256 amount) external {\n uint256 cooldownStartTimestamp = stakersCooldowns[msg.sender];\n uint256 windowStart = cooldownStartTimestamp + COOLDOWN_SECONDS;\n require(amount != 0, \"INVALID_ZERO_AMOUNT\");\n require(block.timestamp > windowStart, \"INSUFFICIENT_COOLDOWN\");\n require(\n block.timestamp - windowStart <= UNSTAKE_WINDOW,\n \"UNSTAKE_WINDOW_FINISHED\"\n );\n uint256 balanceOfMessageSender = balanceOf(msg.sender);\n uint256 amountToRedeem = (amount > balanceOfMessageSender)\n ? balanceOfMessageSender\n : amount;\n\n stakersCooldowns[msg.sender] = 0;\n _burn(msg.sender, amountToRedeem);\n IERC20(STAKED_TOKEN).safeTransfer(to, amountToRedeem);\n }\n\n /**\n * @dev Activates the cooldown period to unstake\n * - It can't be called if the user is not staking\n **/\n function cooldown() external {\n require(balanceOf(msg.sender) != 0, \"INVALID_BALANCE_ON_COOLDOWN\");\n stakersCooldowns[msg.sender] = block.timestamp;\n }\n\n /**\n * @dev Test helper function to allow changing the cooldown\n **/\n function setCooldown(address account, uint256 _cooldown) external {\n stakersCooldowns[account] = _cooldown;\n }\n}\n" + }, + "contracts/mocks/MockStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract MockStrategy {\n address[] public assets;\n\n address public withdrawAllAsset;\n address public withdrawAllRecipient;\n\n constructor() {}\n\n function deposit(address asset, uint256 amount) external {}\n\n function depositAll() external {}\n\n function withdraw(\n address recipient,\n address asset,\n uint256 amount\n ) external {\n IERC20(asset).transfer(recipient, amount);\n }\n\n function withdrawAll() external {\n IERC20(withdrawAllAsset).transfer(\n withdrawAllRecipient,\n IERC20(withdrawAllAsset).balanceOf(address(this))\n );\n }\n\n function checkBalance(address asset)\n external\n view\n returns (uint256 balance)\n {\n balance = IERC20(asset).balanceOf(address(this));\n }\n\n function supportsAsset(address) external view returns (bool) {\n return true;\n }\n\n function collectRewardTokens() external {}\n\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return new address[](0);\n }\n\n function setWithdrawAll(address asset, address recipient) external {\n withdrawAllAsset = asset;\n withdrawAllRecipient = recipient;\n }\n}\n" + }, + "contracts/mocks/MockSwapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IMintableERC20 } from \"./MintableERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract MockSwapper {\n uint256 public nextOutAmount;\n\n function swap(\n // solhint-disable-next-line no-unused-vars\n address _fromAsset,\n address _toAsset,\n // solhint-disable-next-line no-unused-vars\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n // solhint-disable-next-line no-unused-vars\n bytes calldata _data\n ) external returns (uint256 toAssetAmount) {\n toAssetAmount = (nextOutAmount > 0) ? nextOutAmount : _minToAssetAmount;\n nextOutAmount = 0;\n IMintableERC20(_toAsset).mint(toAssetAmount);\n IERC20(_toAsset).transfer(msg.sender, toAssetAmount);\n }\n\n function setNextOutAmount(uint256 _nextOutAmount) public {\n nextOutAmount = _nextOutAmount;\n }\n}\n" + }, + "contracts/mocks/MockTUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockTUSD is MintableERC20 {\n constructor() ERC20(\"TrueUSD\", \"TUSD\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/MockUniswapPair.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IUniswapV2Pair } from \"../interfaces/uniswap/IUniswapV2Pair.sol\";\n\ncontract MockUniswapPair is IUniswapV2Pair {\n address tok0;\n address tok1;\n uint112 reserve0;\n uint112 reserve1;\n uint256 blockTimestampLast;\n\n bool public hasSynced = false;\n\n constructor(\n address _token0,\n address _token1,\n uint112 _reserve0,\n uint112 _reserve1\n ) {\n tok0 = _token0;\n tok1 = _token1;\n reserve0 = _reserve0;\n reserve1 = _reserve1;\n blockTimestampLast = block.timestamp;\n }\n\n function token0() external view override returns (address) {\n return tok0;\n }\n\n function token1() external view override returns (address) {\n return tok1;\n }\n\n function getReserves()\n external\n view\n override\n returns (\n uint112,\n uint112,\n uint32\n )\n {\n return (reserve0, reserve1, uint32(blockTimestampLast));\n }\n\n function setReserves(uint112 _reserve0, uint112 _reserve1) public {\n reserve0 = _reserve0;\n reserve1 = _reserve1;\n blockTimestampLast = block.timestamp;\n }\n\n // CAUTION This will not work if you setReserves multiple times over\n // multiple different blocks because then it wouldn't be a continuous\n // reserve factor over that blockTimestamp, this assumes an even reserve\n // ratio all the way through\n function price0CumulativeLast() external view override returns (uint256) {\n return\n uint256(FixedPoint.fraction(reserve1, reserve0)._x) *\n blockTimestampLast;\n }\n\n function price1CumulativeLast() external view override returns (uint256) {\n return\n uint256(FixedPoint.fraction(reserve0, reserve1)._x) *\n blockTimestampLast;\n }\n\n function sync() external override {\n hasSynced = true;\n }\n\n function checkHasSynced() external view {\n require(hasSynced, \"Not synced\");\n }\n}\n\n// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))\nlibrary FixedPoint {\n // range: [0, 2**112 - 1]\n // resolution: 1 / 2**112\n struct uq112x112 {\n uint224 _x;\n }\n\n // returns a uq112x112 which represents the ratio of the numerator to the denominator\n // equivalent to encode(numerator).div(denominator)\n function fraction(uint112 numerator, uint112 denominator)\n internal\n pure\n returns (uq112x112 memory)\n {\n require(denominator > 0, \"FixedPoint: DIV_BY_ZERO\");\n return uq112x112((uint224(numerator) << 112) / denominator);\n }\n}\n" + }, + "contracts/mocks/MockUniswapRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { IUniswapV2Router } from \"../interfaces/uniswap/IUniswapV2Router02.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockUniswapRouter is IUniswapV2Router {\n using StableMath for uint256;\n\n mapping(address => address) public pairMaps;\n uint256 public slippage = 1 ether;\n\n function initialize(\n address[] calldata _0tokens,\n address[] calldata _1tokens\n ) public {\n require(\n _0tokens.length == _1tokens.length,\n \"Mock token pairs should be of the same length\"\n );\n for (uint256 i = 0; i < _0tokens.length; i++) {\n pairMaps[_0tokens[i]] = _1tokens[i];\n }\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n\n function swapExactTokensForTokens(\n uint256 amountIn,\n uint256 amountOutMin,\n address[] calldata path,\n address to,\n // solhint-disable-next-line no-unused-vars\n uint256\n ) external override returns (uint256[] memory amountsOut) {\n address tok0 = path[0];\n address tok1 = path[path.length - 1];\n\n uint256 amountOut = (amountOutMin * slippage) / 1 ether;\n require(amountOut >= amountOutMin, \"Slippage error\");\n\n IERC20(tok0).transferFrom(msg.sender, address(this), amountIn);\n MintableERC20(tok1).mintTo(to, amountOut);\n\n amountsOut = new uint256[](path.length);\n amountsOut[path.length - 1] = amountOut;\n }\n\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut)\n {\n (address tok0, address tok1) = _getFirstAndLastToken(params.path);\n\n amountOut = (params.amountOutMinimum * slippage) / 1 ether;\n\n IERC20(tok0).transferFrom(msg.sender, address(this), params.amountIn);\n MintableERC20(tok1).mintTo(params.recipient, amountOut);\n\n require(\n amountOut >= params.amountOutMinimum,\n \"UniswapMock: amountOut less than amountOutMinimum\"\n );\n return amountOut;\n }\n\n function addLiquidity(\n address tokenA,\n address tokenB,\n uint256 amountADesired,\n uint256 amountBDesired,\n uint256 amountAMin,\n uint256 amountBMin,\n address to,\n uint256 deadline\n )\n external\n override\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 liquidity\n )\n {\n // this is needed to make this contract whole else it'd be just virtual\n }\n\n function WETH() external pure override returns (address) {\n return address(0);\n }\n\n // Universal router mock\n function execute(\n bytes calldata,\n bytes[] calldata inputs,\n uint256\n ) external payable {\n uint256 inLen = inputs.length;\n for (uint256 i = 0; i < inLen; ++i) {\n (\n address recipient,\n ,\n uint256 amountOutMinimum,\n bytes memory path,\n\n ) = abi.decode(inputs[i], (address, uint256, uint256, bytes, bool));\n\n (address token0, address token1) = _getFirstAndLastToken(path);\n\n amountOutMinimum = amountOutMinimum.scaleBy(\n Helpers.getDecimals(token0),\n Helpers.getDecimals(token1)\n );\n\n MintableERC20(token1).mintTo(recipient, amountOutMinimum);\n }\n }\n\n function _getFirstAndLastToken(bytes memory path)\n internal\n view\n returns (address token0, address token1)\n {\n bytes memory tok0Bytes = new bytes(20);\n for (uint256 j = 0; j < 20; ++j) {\n tok0Bytes[j] = path[j];\n }\n token0 = address(bytes20(tok0Bytes));\n\n if (pairMaps[token0] != address(0)) {\n token0 = pairMaps[token0];\n }\n\n bytes memory tok1Bytes = new bytes(20);\n uint256 tok1Offset = path.length - 20;\n for (uint256 j = 0; j < 20; ++j) {\n tok1Bytes[j] = path[j + tok1Offset];\n }\n token1 = address(bytes20(tok1Bytes));\n\n if (pairMaps[token1] != address(0)) {\n token1 = pairMaps[token1];\n }\n }\n}\n" + }, + "contracts/mocks/MockUSDC.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDC is MintableERC20 {\n constructor() ERC20(\"USDC Coin\", \"USDC\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n}\n" + }, + "contracts/mocks/MockUSDS.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDS is MintableERC20 {\n constructor() ERC20(\"USDS\", \"USDS\") {}\n}\n" + }, + "contracts/mocks/MockUSDT.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDT is MintableERC20 {\n constructor() ERC20(\"USDT Coin\", \"USDT\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n}\n" + }, + "contracts/mocks/MockVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultCore } from \"../vault/VaultCore.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { VaultInitializer } from \"../vault/VaultInitializer.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MockVault is VaultCore {\n using StableMath for uint256;\n\n uint256 storedTotalValue;\n\n function setTotalValue(uint256 _value) public {\n storedTotalValue = _value;\n }\n\n function totalValue() external view override returns (uint256) {\n return storedTotalValue;\n }\n\n function _totalValue() internal view override returns (uint256) {\n return storedTotalValue;\n }\n\n function _checkBalance(address _asset)\n internal\n view\n override\n returns (uint256 balance)\n {\n // Avoids rounding errors by returning the total value\n // in a single currency\n if (allAssets[0] == _asset) {\n uint256 decimals = Helpers.getDecimals(_asset);\n return storedTotalValue.scaleBy(decimals, 18);\n } else {\n return 0;\n }\n }\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external onlyGovernor {\n maxSupplyDiff = _maxSupplyDiff;\n }\n}\n" + }, + "contracts/mocks/MockVaultCoreInstantRebase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultCore } from \"../vault/VaultCore.sol\";\n\ncontract MockVaultCoreInstantRebase is VaultCore {\n function _nextYield(uint256 supply, uint256 vaultValue)\n internal\n view\n override\n returns (uint256 yield, uint256 targetRate)\n {\n if (vaultValue <= supply) {\n return (0, 0);\n }\n yield = vaultValue - supply;\n return (yield, 0);\n }\n}\n" + }, + "contracts/mocks/MockWETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\n// Just importing to \"unbreak\" coverage tests\nimport { IERC721Receiver } from \"@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol\";\n\ncontract MockWETH is MintableERC20 {\n constructor() ERC20(\"WETH\", \"WETH\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/MockWS.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockWS is MintableERC20 {\n constructor() ERC20(\"Wrapped Sonic\", \"wS\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/TestUpgradedOUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../token/OUSD.sol\";\n\n// used to alter internal state of OUSD contract\ncontract TestUpgradedOUSD is OUSD {\n constructor() OUSD() {}\n\n function overwriteCreditBalances(address _account, uint256 _creditBalance)\n public\n {\n creditBalances[_account] = _creditBalance;\n }\n\n function overwriteAlternativeCPT(address _account, uint256 _acpt) public {\n alternativeCreditsPerToken[_account] = _acpt;\n }\n\n function overwriteRebaseState(address _account, RebaseOptions _rebaseOption)\n public\n {\n rebaseState[_account] = _rebaseOption;\n }\n}\n" + }, + "contracts/oracle/AbstractOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\n// @notice Abstract functionality that is shared between various Oracle Routers\nabstract contract AbstractOracleRouter is IOracle {\n using StableMath for uint256;\n using SafeCast for int256;\n\n uint256 internal constant MIN_DRIFT = 0.7e18;\n uint256 internal constant MAX_DRIFT = 1.3e18;\n address internal constant FIXED_PRICE =\n 0x0000000000000000000000000000000000000001;\n // Maximum allowed staleness buffer above normal Oracle maximum staleness\n uint256 internal constant STALENESS_BUFFER = 1 days;\n mapping(address => uint8) internal decimalsCache;\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n returns (address feedAddress, uint256 maxStaleness);\n\n /**\n * @notice Returns the total price in 18 digit unit for a given asset.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n if (shouldBePegged(asset)) {\n require(_price <= MAX_DRIFT, \"Oracle: Price exceeds max\");\n require(_price >= MIN_DRIFT, \"Oracle: Price under min\");\n }\n return _price;\n }\n\n function getDecimals(address _feed) internal view virtual returns (uint8) {\n uint8 decimals = decimalsCache[_feed];\n require(decimals > 0, \"Oracle: Decimals not cached\");\n return decimals;\n }\n\n /**\n * @notice Before an asset/feed price is fetches for the first time the\n * decimals need to be cached. This is a gas optimization\n * @param asset address of the asset\n * @return uint8 corresponding asset decimals\n */\n function cacheDecimals(address asset) external returns (uint8) {\n (address _feed, ) = feedMetadata(asset);\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n uint8 decimals = AggregatorV3Interface(_feed).decimals();\n decimalsCache[_feed] = decimals;\n return decimals;\n }\n\n function shouldBePegged(address _asset) internal view returns (bool) {\n string memory symbol = Helpers.getSymbol(_asset);\n bytes32 symbolHash = keccak256(abi.encodePacked(symbol));\n return\n symbolHash == keccak256(abi.encodePacked(\"DAI\")) ||\n symbolHash == keccak256(abi.encodePacked(\"USDC\")) ||\n symbolHash == keccak256(abi.encodePacked(\"USDT\"));\n }\n}\n" + }, + "contracts/oracle/OETHBaseOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\n// @notice Oracle Router (for OETH on Base) that denominates all prices in ETH\ncontract OETHBaseOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n using SafeCast for int256;\n\n address constant WETH = 0x4200000000000000000000000000000000000006;\n address constant WOETH = 0xD8724322f44E5c58D7A815F542036fb17DbbF839;\n address constant WOETH_CHAINLINK_FEED =\n 0xe96EB1EDa83d18cbac224233319FA5071464e1b9;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == WETH) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == WOETH) {\n // Chainlink: https://data.chain.link/feeds/base/base/woeth-oeth-exchange-rate\n // Bridged wOETH/OETH\n feedAddress = WOETH_CHAINLINK_FEED;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OETHFixedOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHOracleRouter } from \"./OETHOracleRouter.sol\";\n\n// @notice Oracle Router that returns 1e18 for all prices\n// used solely for deployment to testnets\ncontract OETHFixedOracle is OETHOracleRouter {\n constructor() OETHOracleRouter() {}\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n // solhint-disable-next-line no-unused-vars\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n // fixes price for all of the assets\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n }\n}\n" + }, + "contracts/oracle/OETHOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// @notice Oracle Router that denominates all prices in ETH\ncontract OETHOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = uint256(_iprice).scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == 0x5E8422345238F34275888049021821E8E08CAa1f) {\n // frxETH/ETH\n feedAddress = 0xC58F3385FBc1C8AD2c0C9a061D7c13b141D7A5Df;\n maxStaleness = 18 hours + STALENESS_BUFFER;\n } else if (asset == 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/steth-eth\n // Chainlink: stETH/ETH\n feedAddress = 0x86392dC19c0b719886221c78AB11eb8Cf5c52812;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xae78736Cd615f374D3085123A210448E74Fc6393) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/reth-eth\n // Chainlink: rETH/ETH\n feedAddress = 0x536218f9E9Eb48863970252233c8F271f554C2d0;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xD533a949740bb3306d119CC777fa900bA034cd52) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/crv-eth\n // Chainlink: CRV/ETH\n feedAddress = 0x8a12Be339B0cD1829b91Adc01977caa5E9ac121e;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/cvx-eth\n // Chainlink: CVX/ETH\n feedAddress = 0xC9CbF687f43176B302F03f5e58470b77D07c61c6;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xBe9895146f7AF43049ca1c1AE358B0541Ea49704) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/cbeth-eth\n // Chainlink: cbETH/ETH\n feedAddress = 0xF017fcB346A1885194689bA23Eff2fE6fA5C483b;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xba100000625a3754423978a60c9317c58a424e3D) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/bal-eth\n // Chainlink: BAL/ETH\n feedAddress = 0xC1438AA3823A6Ba0C159CfA8D98dF5A994bA120b;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OETHPlumeOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\n// @notice Oracle Router (for OETH on Plume) that denominates all prices in ETH\ncontract OETHPlumeOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n using SafeCast for int256;\n\n address constant WETH = 0xca59cA09E5602fAe8B629DeE83FfA819741f14be;\n address constant WOETH = 0xD8724322f44E5c58D7A815F542036fb17DbbF839;\n // Ref: https://docs.eo.app/docs/eprice/feed-addresses/plume\n address constant WOETH_ORACLE_FEED =\n 0x4915600Ed7d85De62011433eEf0BD5399f677e9b;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == WETH) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == WOETH) {\n // Chainlink: https://data.chain.link/feeds/base/base/woeth-oeth-exchange-rate\n // Bridged wOETH/OETH\n feedAddress = WOETH_ORACLE_FEED;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\n\n// @notice Oracle Router that denominates all prices in USD\ncontract OracleRouter is AbstractOracleRouter {\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n pure\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n /* + STALENESS_BUFFER is added in case Oracle for some reason doesn't\n * update on heartbeat and we add a generous buffer amount.\n */\n if (asset == 0x6B175474E89094C44Da98b954EedeAC495271d0F) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/dai-usd\n // Chainlink: DAI/USD\n feedAddress = 0xAed0c38402a5d19df6E4c03F4E2DceD6e29c1ee9;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xdC035D45d973E3EC169d2276DDab16f1e407384F) {\n // https://data.chain.link/feeds/ethereum/mainnet/usds-usd\n // Chainlink: USDS/USD\n feedAddress = 0xfF30586cD0F29eD462364C7e81375FC0C71219b1;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/usdc-usd\n // Chainlink: USDC/USD\n feedAddress = 0x8fFfFfd4AfB6115b954Bd326cbe7B4BA576818f6;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xdAC17F958D2ee523a2206206994597C13D831ec7) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/usdt-usd\n // Chainlink: USDT/USD\n feedAddress = 0x3E7d1eAB13ad0104d2750B8863b489D65364e32D;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xc00e94Cb662C3520282E6f5717214004A7f26888) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/comp-usd\n // Chainlink: COMP/USD\n feedAddress = 0xdbd020CAeF83eFd542f4De03e3cF0C28A4428bd5;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/aave-usd\n // Chainlink: AAVE/USD\n feedAddress = 0x547a514d5e3769680Ce22B2361c10Ea13619e8a9;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xD533a949740bb3306d119CC777fa900bA034cd52) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/crv-usd\n // Chainlink: CRV/USD\n feedAddress = 0xCd627aA160A6fA45Eb793D19Ef54f5062F20f33f;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B) {\n // Chainlink: CVX/USD\n feedAddress = 0xd962fC30A72A84cE50161031391756Bf2876Af5D;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OSonicOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHFixedOracle } from \"./OETHFixedOracle.sol\";\n\n// @notice Oracle Router that returns 1e18 for all prices\n// used solely for deployment to testnets\ncontract OSonicOracleRouter is OETHFixedOracle {\n constructor() OETHFixedOracle() {}\n}\n" + }, + "contracts/poolBooster/AbstractPoolBoosterFactory.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IPoolBoostCentralRegistry } from \"../interfaces/poolBooster/IPoolBoostCentralRegistry.sol\";\n\n/**\n * @title Abstract Pool booster factory\n * @author Origin Protocol Inc\n */\ncontract AbstractPoolBoosterFactory is Governable {\n struct PoolBoosterEntry {\n address boosterAddress;\n address ammPoolAddress;\n IPoolBoostCentralRegistry.PoolBoosterType boosterType;\n }\n\n // @notice address of Origin Sonic\n address public immutable oSonic;\n // @notice Central registry contract\n IPoolBoostCentralRegistry public immutable centralRegistry;\n\n // @notice list of all the pool boosters created by this factory\n PoolBoosterEntry[] public poolBoosters;\n // @notice mapping of AMM pool to pool booster\n mapping(address => PoolBoosterEntry) public poolBoosterFromPool;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) {\n require(_oSonic != address(0), \"Invalid oSonic address\");\n require(_governor != address(0), \"Invalid governor address\");\n require(\n _centralRegistry != address(0),\n \"Invalid central registry address\"\n );\n\n oSonic = _oSonic;\n centralRegistry = IPoolBoostCentralRegistry(_centralRegistry);\n _setGovernor(_governor);\n }\n\n /**\n * @notice Goes over all the pool boosters created by this factory and\n * calls bribe() on them.\n * @param _exclusionList A list of pool booster addresses to skip when\n * calling this function.\n */\n function bribeAll(address[] memory _exclusionList) external {\n uint256 lengthI = poolBoosters.length;\n for (uint256 i = 0; i < lengthI; i++) {\n address poolBoosterAddress = poolBoosters[i].boosterAddress;\n bool skipBribeCall = false;\n uint256 lengthJ = _exclusionList.length;\n for (uint256 j = 0; j < lengthJ; j++) {\n // pool booster in exclusion list\n if (_exclusionList[j] == poolBoosterAddress) {\n skipBribeCall = true;\n break;\n }\n }\n\n if (!skipBribeCall) {\n IPoolBooster(poolBoosterAddress).bribe();\n }\n }\n }\n\n /**\n * @notice Removes the pool booster from the internal list of pool boosters.\n * @dev This action does not destroy the pool booster contract nor does it\n * stop the yield delegation to it.\n * @param _poolBoosterAddress address of the pool booster\n */\n function removePoolBooster(address _poolBoosterAddress)\n external\n onlyGovernor\n {\n uint256 boostersLen = poolBoosters.length;\n for (uint256 i = 0; i < boostersLen; ++i) {\n if (poolBoosters[i].boosterAddress == _poolBoosterAddress) {\n // erase mapping\n delete poolBoosterFromPool[poolBoosters[i].ammPoolAddress];\n\n // overwrite current pool booster with the last entry in the list\n poolBoosters[i] = poolBoosters[boostersLen - 1];\n // drop the last entry\n poolBoosters.pop();\n\n centralRegistry.emitPoolBoosterRemoved(_poolBoosterAddress);\n break;\n }\n }\n }\n\n function _storePoolBoosterEntry(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType _boosterType\n ) internal {\n PoolBoosterEntry memory entry = PoolBoosterEntry(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType\n );\n\n poolBoosters.push(entry);\n poolBoosterFromPool[_ammPoolAddress] = entry;\n\n // emit the events of the pool booster created\n centralRegistry.emitPoolBoosterCreated(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType\n );\n }\n\n function _deployContract(bytes memory _bytecode, uint256 _salt)\n internal\n returns (address _address)\n {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _address := create2(\n 0,\n add(_bytecode, 0x20),\n mload(_bytecode),\n _salt\n )\n }\n\n require(\n _address.code.length > 0 && _address != address(0),\n \"Failed creating a pool booster\"\n );\n }\n\n // pre-compute the address of the deployed contract that will be\n // created when create2 is called\n function _computeAddress(bytes memory _bytecode, uint256 _salt)\n internal\n view\n returns (address)\n {\n bytes32 hash = keccak256(\n abi.encodePacked(\n bytes1(0xff),\n address(this),\n _salt,\n keccak256(_bytecode)\n )\n );\n\n // cast last 20 bytes of hash to address\n return address(uint160(uint256(hash)));\n }\n\n function poolBoosterLength() external view returns (uint256) {\n return poolBoosters.length;\n }\n}\n" + }, + "contracts/poolBooster/PoolBoostCentralRegistry.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IPoolBoostCentralRegistry } from \"../interfaces/poolBooster/IPoolBoostCentralRegistry.sol\";\n\n/**\n * @title Contract that holds all governance approved pool booster Factory\n * implementation deployments\n * @author Origin Protocol Inc\n */\ncontract PoolBoostCentralRegistry is Governable, IPoolBoostCentralRegistry {\n event FactoryApproved(address factoryAddress);\n event FactoryRemoved(address factoryAddress);\n\n // @notice List of approved factories\n address[] public factories;\n\n modifier onlyApprovedFactories() {\n require(isApprovedFactory(msg.sender), \"Not an approved factory\");\n _;\n }\n\n constructor() {\n // set the governor of the implementation contract to zero address\n _setGovernor(address(0));\n }\n\n /**\n * @notice Adds a factory address to the approved factory addresses\n * @param _factoryAddress address of the factory\n */\n function approveFactory(address _factoryAddress) external onlyGovernor {\n require(_factoryAddress != address(0), \"Invalid address\");\n require(\n !isApprovedFactory(_factoryAddress),\n \"Factory already approved\"\n );\n\n factories.push(_factoryAddress);\n emit FactoryApproved(_factoryAddress);\n }\n\n /**\n * @notice Removes the factory from approved factory addresses\n * @param _factoryAddress address of the factory\n */\n function removeFactory(address _factoryAddress) external onlyGovernor {\n require(_factoryAddress != address(0), \"Invalid address\");\n\n uint256 length = factories.length;\n bool factoryRemoved = false;\n for (uint256 i = 0; i < length; i++) {\n if (factories[i] != _factoryAddress) {\n continue;\n }\n\n factories[i] = factories[length - 1];\n factories.pop();\n emit FactoryRemoved(_factoryAddress);\n factoryRemoved = true;\n break;\n }\n require(factoryRemoved, \"Not an approved factory\");\n\n emit FactoryRemoved(_factoryAddress);\n }\n\n /**\n * @notice Emits a pool booster created event\n * @dev This has been created as a convenience method for the monitoring to have\n * an index of all of the created pool boosters by only listening to the\n * events of this contract.\n * @param _poolBoosterAddress address of the pool booster created\n * @param _ammPoolAddress address of the AMM pool forwarding yield to the pool booster\n * @param _boosterType PoolBoosterType the type of the pool booster\n */\n function emitPoolBoosterCreated(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n PoolBoosterType _boosterType\n ) external onlyApprovedFactories {\n emit PoolBoosterCreated(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType,\n msg.sender // address of the factory\n );\n }\n\n /**\n * @notice Emits a pool booster removed event\n * @dev This has been created as a convenience method for the monitoring to have\n * an index of all of the removed pool boosters by only listening to the\n * events of this contract.\n * @param _poolBoosterAddress address of the pool booster to be removed\n */\n function emitPoolBoosterRemoved(address _poolBoosterAddress)\n external\n onlyApprovedFactories\n {\n emit PoolBoosterRemoved(_poolBoosterAddress);\n }\n\n /**\n * @notice Returns true if the factory is approved\n * @param _factoryAddress address of the factory\n */\n function isApprovedFactory(address _factoryAddress)\n public\n view\n returns (bool)\n {\n uint256 length = factories.length;\n for (uint256 i = 0; i < length; i++) {\n if (factories[i] == _factoryAddress) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * @notice Returns all supported factories\n */\n function getAllFactories() external view returns (address[] memory) {\n return factories;\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactoryMerkl.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterMerkl } from \"./PoolBoosterMerkl.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Merkl pool boosters.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactoryMerkl is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n /// @notice address of the Merkl distributor\n address public merklDistributor;\n\n /// @notice event emitted when the Merkl distributor is updated\n event MerklDistributorUpdated(address newDistributor);\n\n /**\n * @param _oSonic address of the OSonic token\n * @param _governor address governor\n * @param _centralRegistry address of the central registry\n * @param _merklDistributor address of the Merkl distributor\n */\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry,\n address _merklDistributor\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {\n _setMerklDistributor(_merklDistributor);\n }\n\n /**\n * @dev Create a Pool Booster for Merkl.\n * @param _campaignType The type of campaign to create. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _campaignDuration The duration of the campaign in seconds\n * @param campaignData The data to be used for the campaign. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * This should be fetched from the Merkl UI.\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterMerkl(\n uint32 _campaignType,\n address _ammPoolAddress,\n uint32 _campaignDuration,\n bytes calldata campaignData,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n require(_campaignDuration > 1 hours, \"Invalid campaign duration\");\n require(campaignData.length > 0, \"Invalid campaign data\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterMerkl).creationCode,\n abi.encode(\n oSonic,\n merklDistributor,\n _campaignDuration,\n _campaignType,\n governor(),\n campaignData\n )\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.MerklBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for Merkl.\n * @param _campaignType The type of campaign to create. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterMerkl` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n uint32 _campaignType,\n address _ammPoolAddress,\n uint32 _campaignDuration,\n bytes calldata campaignData,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n require(_campaignDuration > 1 hours, \"Invalid campaign duration\");\n require(campaignData.length > 0, \"Invalid campaign data\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterMerkl).creationCode,\n abi.encode(\n oSonic,\n merklDistributor,\n _campaignDuration,\n _campaignType,\n governor(),\n campaignData\n )\n ),\n _salt\n );\n }\n\n /**\n * @dev Set the address of the Merkl distributor\n * @param _merklDistributor The address of the Merkl distributor\n */\n function setMerklDistributor(address _merklDistributor)\n external\n onlyGovernor\n {\n _setMerklDistributor(_merklDistributor);\n }\n\n function _setMerklDistributor(address _merklDistributor) internal {\n require(\n _merklDistributor != address(0),\n \"Invalid merklDistributor address\"\n );\n merklDistributor = _merklDistributor;\n emit MerklDistributorUpdated(_merklDistributor);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactoryMetropolis.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterMetropolis } from \"./PoolBoosterMetropolis.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Metropolis pool boosters.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactoryMetropolis is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n address public immutable rewardFactory;\n address public immutable voter;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n // @param address _rewardFactory address of the Metropolis reward factory\n // @param address _voter address of the Metropolis voter\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry,\n address _rewardFactory,\n address _voter\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {\n rewardFactory = _rewardFactory;\n voter = _voter;\n }\n\n /**\n * @dev Create a Pool Booster for Metropolis pool.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterMetropolis(address _ammPoolAddress, uint256 _salt)\n external\n onlyGovernor\n {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterMetropolis).creationCode,\n abi.encode(oSonic, rewardFactory, _ammPoolAddress, voter)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.MetropolisBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for Metropolis pool.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterMetropolis` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(address _ammPoolAddress, uint256 _salt)\n external\n view\n returns (address)\n {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterMetropolis).creationCode,\n abi.encode(oSonic, rewardFactory, _ammPoolAddress, voter)\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactorySwapxDouble.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterSwapxDouble } from \"./PoolBoosterSwapxDouble.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Swapx Ichi pool boosters where both of the\n * gauges need incentivizing.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactorySwapxDouble is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {}\n\n /**\n * @dev Create a Pool Booster for SwapX Ichi vault based pool where 2 Bribe contracts need to be\n * bribed\n * @param _bribeAddressOS address of the Bribes.sol(Bribe) contract for the OS token side\n * @param _bribeAddressOther address of the Bribes.sol(Bribe) contract for the other token in the pool\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _split 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n * bribe contract and 60% to other bribe contract\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterSwapxDouble(\n address _bribeAddressOS,\n address _bribeAddressOther,\n address _ammPoolAddress,\n uint256 _split,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterSwapxDouble).creationCode,\n abi.encode(_bribeAddressOS, _bribeAddressOther, oSonic, _split)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.SwapXDoubleBooster\n );\n }\n\n /**\n * @dev Compute the address of the pool booster to be deployed.\n * @param _bribeAddressOS address of the Bribes.sol(Bribe) contract for the OS token side\n * @param _bribeAddressOther address of the Bribes.sol(Bribe) contract for the other token in the pool\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _split 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n * bribe contract and 60% to other bribe contract\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterSwapxDouble` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n address _bribeAddressOS,\n address _bribeAddressOther,\n address _ammPoolAddress,\n uint256 _split,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterSwapxDouble).creationCode,\n abi.encode(\n _bribeAddressOS,\n _bribeAddressOther,\n oSonic,\n _split\n )\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactorySwapxSingle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterSwapxSingle } from \"./PoolBoosterSwapxSingle.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Swapx Single pool boosters - where a single\n * gauge is created for a pool. this is appropriate for Classic Stable & Classic\n * Volatile SwapX pools.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactorySwapxSingle is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {}\n\n /**\n * @dev Create a Pool Booster for SwapX classic volatile or classic stable pools where\n * a single Bribe contract is incentivized.\n * @param _bribeAddress address of the Bribes.sol contract\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterSwapxSingle(\n address _bribeAddress,\n address _ammPoolAddress,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterSwapxSingle).creationCode,\n abi.encode(_bribeAddress, oSonic)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.SwapXSingleBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for SwapX classic volatile or classic stable pools where\n * a single Bribe contract is incentivized.\n * @param _bribeAddress address of the Bribes.sol contract\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterSwapxSingle` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n address _bribeAddress,\n address _ammPoolAddress,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterSwapxSingle).creationCode,\n abi.encode(_bribeAddress, oSonic)\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterMerkl.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IMerklDistributor } from \"../interfaces/poolBooster/IMerklDistributor.sol\";\n\ninterface IERC1271 {\n /**\n * @dev Should return whether the signature provided is valid for the provided data\n * @param hash Hash of the data to be signed\n * @param signature Signature byte array associated with _data\n */\n function isValidSignature(bytes32 hash, bytes memory signature)\n external\n view\n returns (bytes4 magicValue);\n}\n\n/**\n * @title Pool booster for Merkl distributor\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterMerkl is IPoolBooster, IERC1271 {\n /// @notice address of merkl distributor\n IMerklDistributor public immutable merklDistributor;\n /// @notice address of the OS token\n IERC20 public immutable rewardToken;\n /// @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n /// @notice Campaign duration in seconds\n uint32 public immutable duration; // -> should be immutable\n /// @notice Campaign type\n uint32 public immutable campaignType;\n /// @notice Owner of the campaign\n address public immutable creator;\n /// @notice Campaign data\n bytes public campaignData;\n\n constructor(\n address _rewardToken,\n address _merklDistributor,\n uint32 _duration,\n uint32 _campaignType,\n address _creator,\n bytes memory _campaignData\n ) {\n require(_rewardToken != address(0), \"Invalid rewardToken address\");\n require(\n _merklDistributor != address(0),\n \"Invalid merklDistributor address\"\n );\n require(_campaignData.length > 0, \"Invalid campaignData\");\n require(_duration > 1 hours, \"Invalid duration\");\n\n campaignType = _campaignType;\n duration = _duration;\n creator = _creator;\n\n merklDistributor = IMerklDistributor(_merklDistributor);\n rewardToken = IERC20(_rewardToken);\n campaignData = _campaignData;\n }\n\n /// @notice Create a campaign on the Merkl distributor\n function bribe() external override {\n // Ensure token is approved for the Merkl distributor\n uint256 minAmount = merklDistributor.rewardTokenMinAmounts(\n address(rewardToken)\n );\n require(minAmount > 0, \"Min reward amount must be > 0\");\n\n // if balance too small or below threshold, do no bribes\n uint256 balance = rewardToken.balanceOf(address(this));\n if (\n balance < MIN_BRIBE_AMOUNT ||\n (balance * 1 hours < minAmount * duration)\n ) {\n return;\n }\n\n // Approve the bribe contract to spend the reward token\n rewardToken.approve(address(merklDistributor), balance);\n\n // Notify the bribe contract of the reward amount\n merklDistributor.signAndCreateCampaign(\n IMerklDistributor.CampaignParameters({\n campaignId: bytes32(0),\n creator: creator,\n rewardToken: address(rewardToken),\n amount: balance,\n campaignType: campaignType,\n startTimestamp: getNextPeriodStartTime(),\n duration: duration,\n campaignData: campaignData\n }),\n bytes(\"\")\n );\n emit BribeExecuted(balance);\n }\n\n /// @notice Used to sign a campaign on the Merkl distributor\n function isValidSignature(bytes32, bytes memory)\n external\n view\n override\n returns (bytes4 magicValue)\n {\n require(msg.sender == address(merklDistributor), \"Invalid sender\");\n // bytes4(keccak256(\"isValidSignature(bytes32,bytes)\")) == 0x1626ba7e\n return bytes4(0x1626ba7e);\n }\n\n /// @notice Returns the timestamp for the start of the next period based on the configured duration\n function getNextPeriodStartTime() public view returns (uint32) {\n // Calculate the timestamp for the next period boundary\n return uint32((block.timestamp / duration + 1) * duration);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterMetropolis.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @title Pool booster for Metropolis pools\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterMetropolis is IPoolBooster {\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice address of the pool\n address public immutable pool;\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n IRewarderFactory public immutable rewardFactory;\n\n IVoter public immutable voter;\n\n constructor(\n address _osToken,\n address _rewardFactory,\n address _pool,\n address _voter\n ) {\n require(_pool != address(0), \"Invalid pool address\");\n pool = _pool;\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n\n rewardFactory = IRewarderFactory(_rewardFactory);\n\n voter = IVoter(_voter);\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n (, uint256 minBribeAmount) = rewardFactory.getWhitelistedTokenInfo(\n address(osToken)\n );\n if (balance < MIN_BRIBE_AMOUNT || balance < minBribeAmount) {\n return;\n }\n\n uint256 id = voter.getCurrentVotingPeriod() + 1;\n\n // Deploy a rewarder\n IRewarder rewarder = IRewarder(\n rewardFactory.createBribeRewarder(address(osToken), pool)\n );\n\n // Approve the rewarder to spend the balance\n osToken.approve(address(rewarder), balance);\n\n // Fund and bribe the rewarder\n rewarder.fundAndBribe(id, id, balance);\n\n emit BribeExecuted(balance);\n }\n}\n\ninterface IRewarderFactory {\n function createBribeRewarder(address token, address pool)\n external\n returns (address rewarder);\n\n function getWhitelistedTokenInfo(address token)\n external\n view\n returns (bool isWhitelisted, uint256 minBribeAmount);\n}\n\ninterface IRewarder {\n function fundAndBribe(\n uint256 startId,\n uint256 lastId,\n uint256 amountPerPeriod\n ) external payable;\n}\n\ninterface IVoter {\n function getCurrentVotingPeriod() external view returns (uint256);\n}\n" + }, + "contracts/poolBooster/PoolBoosterSwapxDouble.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBribe } from \"../interfaces/poolBooster/ISwapXAlgebraBribe.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n/**\n * @title Pool booster for SwapX concentrated liquidity where 2 gauges are created for\n * every pool. Ichi vaults currently have such setup.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterSwapxDouble is IPoolBooster {\n using StableMath for uint256;\n\n // @notice address of the Bribes.sol(Bribe) contract for the OS token side\n IBribe public immutable bribeContractOS;\n // @notice address of the Bribes.sol(Bribe) contract for the other token in the pool\n IBribe public immutable bribeContractOther;\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n // bribe contract and 60% to other bribe contract\n uint256 public immutable split;\n\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n constructor(\n address _bribeContractOS,\n address _bribeContractOther,\n address _osToken,\n uint256 _split\n ) {\n require(\n _bribeContractOS != address(0),\n \"Invalid bribeContractOS address\"\n );\n require(\n _bribeContractOther != address(0),\n \"Invalid bribeContractOther address\"\n );\n // expect it to be between 1% & 99%\n require(_split > 1e16 && _split < 99e16, \"Unexpected split amount\");\n\n bribeContractOS = IBribe(_bribeContractOS);\n bribeContractOther = IBribe(_bribeContractOther);\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n split = _split;\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n if (balance < MIN_BRIBE_AMOUNT) {\n return;\n }\n\n uint256 osBribeAmount = balance.mulTruncate(split);\n uint256 otherBribeAmount = balance - osBribeAmount;\n\n osToken.approve(address(bribeContractOS), osBribeAmount);\n osToken.approve(address(bribeContractOther), otherBribeAmount);\n\n bribeContractOS.notifyRewardAmount(address(osToken), osBribeAmount);\n bribeContractOther.notifyRewardAmount(\n address(osToken),\n otherBribeAmount\n );\n\n emit BribeExecuted(balance);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterSwapxSingle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBribe } from \"../interfaces/poolBooster/ISwapXAlgebraBribe.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @title Pool booster for SwapX for Classic Stable Pools and Classic Volatile Pools\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterSwapxSingle is IPoolBooster {\n // @notice address of the Bribes.sol(Bribe) contract\n IBribe public immutable bribeContract;\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n constructor(address _bribeContract, address _osToken) {\n require(_bribeContract != address(0), \"Invalid bribeContract address\");\n bribeContract = IBribe(_bribeContract);\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n if (balance < MIN_BRIBE_AMOUNT) {\n return;\n }\n\n osToken.approve(address(bribeContract), balance);\n\n bribeContract.notifyRewardAmount(address(osToken), balance);\n emit BribeExecuted(balance);\n }\n}\n" + }, + "contracts/proxies/BaseProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice BridgedBaseWOETHProxy delegates calls to BridgedWOETH implementation\n */\ncontract BridgedBaseWOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseVaultProxy delegates calls to OETHBaseVault implementation\n */\ncontract OETHBaseVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseProxy delegates calls to OETH implementation\n */\ncontract OETHBaseProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHBaseProxy delegates calls to WOETH implementation\n */\ncontract WOETHBaseProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseDripperProxy delegates calls to a FixedRateDripper implementation\n */\ncontract OETHBaseDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice AerodromeAMOStrategyProxy delegates calls to AerodromeAMOStrategy implementation\n */\ncontract AerodromeAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BridgedWOETHStrategyProxy delegates calls to BridgedWOETHStrategy implementation\n */\ncontract BridgedWOETHStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseHarvesterProxy delegates calls to a SuperOETHHarvester implementation\n */\ncontract OETHBaseHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseCurveAMOProxy delegates calls to a OETHBaseCurveAMO implementation\n */\ncontract OETHBaseCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/proxies/InitializeGovernedUpgradeabilityProxy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { Governable } from \"../governance/Governable.sol\";\n\n/**\n * @title BaseGovernedUpgradeabilityProxy\n * @dev This contract combines an upgradeability proxy with our governor system.\n * It is based on an older version of OpenZeppelins BaseUpgradeabilityProxy\n * with Solidity ^0.8.0.\n * @author Origin Protocol Inc\n */\ncontract InitializeGovernedUpgradeabilityProxy is Governable {\n /**\n * @dev Emitted when the implementation is upgraded.\n * @param implementation Address of the new implementation.\n */\n event Upgraded(address indexed implementation);\n\n constructor() {\n _setGovernor(msg.sender);\n }\n\n /**\n * @dev Contract initializer with Governor enforcement\n * @param _logic Address of the initial implementation.\n * @param _initGovernor Address of the initial Governor.\n * @param _data Data to send as msg.data to the implementation to initialize\n * the proxied contract.\n * It should include the signature and the parameters of the function to be\n * called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n * This parameter is optional, if no data is given the initialization call\n * to proxied contract will be skipped.\n */\n function initialize(\n address _logic,\n address _initGovernor,\n bytes calldata _data\n ) public payable onlyGovernor {\n require(_implementation() == address(0));\n require(_logic != address(0), \"Implementation not set\");\n assert(\n IMPLEMENTATION_SLOT ==\n bytes32(uint256(keccak256(\"eip1967.proxy.implementation\")) - 1)\n );\n _setImplementation(_logic);\n if (_data.length > 0) {\n (bool success, ) = _logic.delegatecall(_data);\n require(success);\n }\n _changeGovernor(_initGovernor);\n }\n\n /**\n * @return The address of the proxy admin/it's also the governor.\n */\n function admin() external view returns (address) {\n return _governor();\n }\n\n /**\n * @return The address of the implementation.\n */\n function implementation() external view returns (address) {\n return _implementation();\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy.\n * Only the admin can call this function.\n * @param _newImplementation Address of the new implementation.\n */\n function upgradeTo(address _newImplementation) external onlyGovernor {\n _upgradeTo(_newImplementation);\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy and call a function\n * on the new implementation.\n * This is useful to initialize the proxied contract.\n * @param newImplementation Address of the new implementation.\n * @param data Data to send as msg.data in the low level call.\n * It should include the signature and the parameters of the function to be called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n */\n function upgradeToAndCall(address newImplementation, bytes calldata data)\n external\n payable\n onlyGovernor\n {\n _upgradeTo(newImplementation);\n (bool success, ) = newImplementation.delegatecall(data);\n require(success);\n }\n\n /**\n * @dev Fallback function.\n * Implemented entirely in `_fallback`.\n */\n fallback() external payable {\n _fallback();\n }\n\n /**\n * @dev Delegates execution to an implementation contract.\n * This is a low level function that doesn't return to its internal call site.\n * It will return to the external caller whatever the implementation returns.\n * @param _impl Address to delegate.\n */\n function _delegate(address _impl) internal {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(gas(), _impl, 0, calldatasize(), 0, 0)\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n /**\n * @dev Function that is run as the first thing in the fallback function.\n * Can be redefined in derived contracts to add functionality.\n * Redefinitions must call super._willFallback().\n */\n function _willFallback() internal {}\n\n /**\n * @dev fallback implementation.\n * Extracted to enable manual triggering.\n */\n function _fallback() internal {\n _willFallback();\n _delegate(_implementation());\n }\n\n /**\n * @dev Storage slot with the address of the current implementation.\n * This is the keccak-256 hash of \"eip1967.proxy.implementation\" subtracted by 1, and is\n * validated in the constructor.\n */\n bytes32 internal constant IMPLEMENTATION_SLOT =\n 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n /**\n * @dev Returns the current implementation.\n * @return impl Address of the current implementation\n */\n function _implementation() internal view returns (address impl) {\n bytes32 slot = IMPLEMENTATION_SLOT;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n impl := sload(slot)\n }\n }\n\n /**\n * @dev Upgrades the proxy to a new implementation.\n * @param newImplementation Address of the new implementation.\n */\n function _upgradeTo(address newImplementation) internal {\n _setImplementation(newImplementation);\n emit Upgraded(newImplementation);\n }\n\n /**\n * @dev Sets the implementation address of the proxy.\n * @param newImplementation Address of the new implementation.\n */\n function _setImplementation(address newImplementation) internal {\n require(\n Address.isContract(newImplementation),\n \"Cannot set a proxy implementation to a non-contract address\"\n );\n\n bytes32 slot = IMPLEMENTATION_SLOT;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(slot, newImplementation)\n }\n }\n}\n" + }, + "contracts/proxies/PlumeProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OETHPlumeVaultProxy delegates calls to OETHPlumeVault implementation\n */\ncontract OETHPlumeVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHPlumeProxy delegates calls to OETH implementation\n */\ncontract OETHPlumeProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHPlumeProxy delegates calls to WOETH implementation\n */\ncontract WOETHPlumeProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice RoosterAMOStrategyProxy delegates calls to a RoosterAMOStrategy implementation\n */\ncontract RoosterAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/proxies/Proxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OUSDProxy delegates calls to an OUSD implementation\n */\ncontract OUSDProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WrappedOUSDProxy delegates calls to a WrappedOUSD implementation\n */\ncontract WrappedOUSDProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice VaultProxy delegates calls to a Vault implementation\n */\ncontract VaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CompoundStrategyProxy delegates calls to a CompoundStrategy implementation\n */\ncontract CompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice AaveStrategyProxy delegates calls to a AaveStrategy implementation\n */\ncontract AaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ConvexStrategyProxy delegates calls to a ConvexStrategy implementation\n */\ncontract ConvexStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice HarvesterProxy delegates calls to a Harvester implementation\n */\ncontract HarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice DripperProxy delegates calls to a Dripper implementation\n */\ncontract DripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoCompoundStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\n */\ncontract MorphoCompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ConvexOUSDMetaStrategyProxy delegates calls to a ConvexOUSDMetaStrategy implementation\n */\ncontract ConvexOUSDMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoAaveStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\n */\ncontract MorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHProxy delegates calls to nowhere for now\n */\ncontract OETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHProxy delegates calls to nowhere for now\n */\ncontract WOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHVaultProxy delegates calls to a Vault implementation\n */\ncontract OETHVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHDripperProxy delegates calls to a OETHDripper implementation\n */\ncontract OETHDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHHarvesterProxy delegates calls to a Harvester implementation\n */\ncontract OETHHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CurveEthStrategyProxy delegates calls to a CurveEthStrategy implementation\n */\ncontract ConvexEthMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BuybackProxy delegates calls to Buyback implementation\n */\ncontract BuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHMorphoAaveStrategyProxy delegates calls to a MorphoAaveStrategy implementation\n */\ncontract OETHMorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBalancerMetaPoolrEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\n */\ncontract OETHBalancerMetaPoolrEthStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice OETHBalancerMetaPoolwstEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\n */\ncontract OETHBalancerMetaPoolwstEthStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MakerDsrStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MakerDsrStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBuybackProxy delegates calls to Buyback implementation\n */\ncontract OETHBuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BridgedWOETHProxy delegates calls to BridgedWOETH implementation\n */\ncontract BridgedWOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice NativeStakingSSVStrategyProxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulatorProxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulatorProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingSSVStrategy2Proxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategy2Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulator2Proxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulator2Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingSSVStrategy3Proxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategy3Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulator3Proxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulator3Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MetaMorphoStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MetaMorphoStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ARMBuybackProxy delegates calls to Buyback implementation\n */\ncontract ARMBuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoGauntletPrimeUSDCStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MorphoGauntletPrimeUSDCStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MorphoGauntletPrimeUSDTStrategyProxy delegates calls to a Generalized4626USDTStrategy implementation\n */\ncontract MorphoGauntletPrimeUSDTStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice CurvePoolBoosterProxy delegates calls to a CurvePoolBooster implementation\n */\ncontract CurvePoolBoosterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHFixedRateDripperProxy delegates calls to a OETHFixedRateDripper implementation\n */\ncontract OETHFixedRateDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHSimpleHarvesterProxy delegates calls to a OETHSimpleHarvester implementation\n */\ncontract OETHSimpleHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice PoolBoostCentralRegistryProxy delegates calls to the PoolBoostCentralRegistry implementation\n */\ncontract PoolBoostCentralRegistryProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MakerSSRStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MakerSSRStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OUSDCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\n */\ncontract OUSDCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\n */\ncontract OETHCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CompoundingStakingSSVStrategyProxy delegates calls to a CompoundingStakingSSVStrategy implementation\n */\ncontract CompoundingStakingSSVStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n" + }, + "contracts/proxies/SonicProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OSonicVaultProxy delegates calls to OSonicVault implementation\n */\ncontract OSonicVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicProxy delegates calls to OSonic implementation\n */\ncontract OSonicProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOSonicProxy delegates calls to WOSonic implementation\n */\ncontract WOSonicProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicDripperProxy delegates calls to a FixedRateDripper implementation\n */\ncontract OSonicDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice SonicStakingStrategyProxy delegates calls to SonicStakingStrategy implementation\n */\ncontract SonicStakingStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicHarvesterProxy delegates calls to a OSonicHarvester implementation\n */\ncontract OSonicHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice SonicSwapXAMOStrategyProxy delegates calls to a SonicSwapXAMOStrategy implementation\n */\ncontract SonicSwapXAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/strategies/AaveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Aave Strategy\n * @notice Investment strategy for investing stablecoins via Aave\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport \"./IAave.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\nimport { IAaveStakedToken } from \"./IAaveStakeToken.sol\";\nimport { IAaveIncentivesController } from \"./IAaveIncentivesController.sol\";\n\ncontract AaveStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n\n uint16 constant referralCode = 92;\n\n IAaveIncentivesController public incentivesController;\n IAaveStakedToken public stkAave;\n\n /**\n * @param _stratConfig The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as AAVE needs several extra\n * addresses for the rewards program.\n * @param _rewardTokenAddresses Address of the AAVE token\n * @param _assets Addresses of supported assets\n * @param _pTokens Platform Token corresponding addresses\n * @param _incentivesAddress Address of the AAVE incentives controller\n * @param _stkAaveAddress Address of the stkAave contract\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // AAVE\n address[] calldata _assets,\n address[] calldata _pTokens,\n address _incentivesAddress,\n address _stkAaveAddress\n ) external onlyGovernor initializer {\n incentivesController = IAaveIncentivesController(_incentivesAddress);\n stkAave = IAaveStakedToken(_stkAaveAddress);\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Deposit asset into Aave\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Aave\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n // Following line also doubles as a check that we are depositing\n // an asset that we support.\n emit Deposit(_asset, _getATokenFor(_asset), _amount);\n _getLendingPool().deposit(_asset, _amount, address(this), referralCode);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Aave\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Aave\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n emit Withdrawal(_asset, _getATokenFor(_asset), _amount);\n uint256 actual = _getLendingPool().withdraw(\n _asset,\n _amount,\n address(this)\n );\n require(actual == _amount, \"Did not withdraw enough\");\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n // Redeem entire balance of aToken\n IERC20 asset = IERC20(assetsMapped[i]);\n address aToken = _getATokenFor(assetsMapped[i]);\n uint256 balance = IERC20(aToken).balanceOf(address(this));\n if (balance > 0) {\n uint256 actual = _getLendingPool().withdraw(\n address(asset),\n balance,\n address(this)\n );\n require(actual == balance, \"Did not withdraw enough\");\n\n uint256 assetBalance = asset.balanceOf(address(this));\n // Transfer entire balance to Vault\n asset.safeTransfer(vaultAddress, assetBalance);\n\n emit Withdrawal(address(asset), aToken, assetBalance);\n }\n }\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n // Balance is always with token aToken decimals\n address aToken = _getATokenFor(_asset);\n balance = IERC20(aToken).balanceOf(address(this));\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Approve the spending of all assets by their corresponding aToken,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n address lendingPool = address(_getLendingPool());\n // approve the pool to spend the Asset\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n address asset = assetsMapped[i];\n // Safe approval\n IERC20(asset).safeApprove(lendingPool, 0);\n IERC20(asset).safeApprove(lendingPool, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / aTokens\n We need to give the AAVE lending pool approval to transfer the\n asset.\n * @param _asset Address of the asset to approve\n * @param _aToken Address of the aToken\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _aToken)\n internal\n override\n {\n address lendingPool = address(_getLendingPool());\n IERC20(_asset).safeApprove(lendingPool, 0);\n IERC20(_asset).safeApprove(lendingPool, type(uint256).max);\n }\n\n /**\n * @dev Get the aToken wrapped in the IERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return Corresponding aToken to this asset\n */\n function _getATokenFor(address _asset) internal view returns (address) {\n address aToken = assetToPToken[_asset];\n require(aToken != address(0), \"aToken does not exist\");\n return aToken;\n }\n\n /**\n * @dev Get the current address of the Aave lending pool, which is the gateway to\n * depositing.\n * @return Current lending pool implementation\n */\n function _getLendingPool() internal view returns (IAaveLendingPool) {\n address lendingPool = ILendingPoolAddressesProvider(platformAddress)\n .getLendingPool();\n require(lendingPool != address(0), \"Lending pool does not exist\");\n return IAaveLendingPool(lendingPool);\n }\n\n /**\n * @dev Collect stkAave, convert it to AAVE send to Vault.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n if (address(stkAave) == address(0)) {\n return;\n }\n\n // Check staked AAVE cooldown timer\n uint256 cooldown = stkAave.stakersCooldowns(address(this));\n uint256 windowStart = cooldown + stkAave.COOLDOWN_SECONDS();\n uint256 windowEnd = windowStart + stkAave.UNSTAKE_WINDOW();\n\n // If inside the unlock window, then we can redeem stkAave\n // for AAVE and send it to the vault.\n if (block.timestamp > windowStart && block.timestamp <= windowEnd) {\n // Redeem to AAVE\n uint256 stkAaveBalance = stkAave.balanceOf(address(this));\n stkAave.redeem(address(this), stkAaveBalance);\n\n // Transfer AAVE to harvesterAddress\n uint256 aaveBalance = IERC20(rewardTokenAddresses[0]).balanceOf(\n address(this)\n );\n if (aaveBalance > 0) {\n IERC20(rewardTokenAddresses[0]).safeTransfer(\n harvesterAddress,\n aaveBalance\n );\n }\n }\n\n // Collect available rewards and restart the cooldown timer, if either of\n // those should be run.\n if (block.timestamp > windowStart || cooldown == 0) {\n uint256 assetsLen = assetsMapped.length;\n // aToken addresses for incentives controller\n address[] memory aTokens = new address[](assetsLen);\n for (uint256 i = 0; i < assetsLen; ++i) {\n aTokens[i] = _getATokenFor(assetsMapped[i]);\n }\n\n // 1. If we have rewards availabile, collect them\n uint256 pendingRewards = incentivesController.getRewardsBalance(\n aTokens,\n address(this)\n );\n if (pendingRewards > 0) {\n // Because getting more stkAAVE from the incentives controller\n // with claimRewards() may push the stkAAVE cooldown time\n // forward, it is called after stakedAAVE has been turned into\n // AAVE.\n uint256 collected = incentivesController.claimRewards(\n aTokens,\n pendingRewards,\n address(this)\n );\n require(collected == pendingRewards, \"AAVE reward difference\");\n }\n\n // 2. Start cooldown counting down.\n if (stkAave.balanceOf(address(this)) > 0) {\n // Protected with if since cooldown call would revert\n // if no stkAave balance.\n stkAave.cooldown();\n }\n }\n }\n}\n" + }, + "contracts/strategies/AbstractCompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base Compound Abstract Strategy\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { ICERC20 } from \"./ICompound.sol\";\nimport { IComptroller } from \"../interfaces/IComptroller.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\nabstract contract AbstractCompoundStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n\n int256[50] private __reserved;\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Get the cToken wrapped in the ICERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return Corresponding cToken to this asset\n */\n function _getCTokenFor(address _asset) internal view returns (ICERC20) {\n address cToken = assetToPToken[_asset];\n require(cToken != address(0), \"cToken does not exist\");\n return ICERC20(cToken);\n }\n\n /**\n * @dev Converts an underlying amount into cToken amount\n * cTokenAmt = (underlying * 1e18) / exchangeRate\n * @param _cToken cToken for which to change\n * @param _underlying Amount of underlying to convert\n * @return amount Equivalent amount of cTokens\n */\n function _convertUnderlyingToCToken(ICERC20 _cToken, uint256 _underlying)\n internal\n view\n returns (uint256 amount)\n {\n // e.g. 1e18*1e18 / 205316390724364402565641705 = 50e8\n // e.g. 1e8*1e18 / 205316390724364402565641705 = 0.45 or 0\n amount = (_underlying * 1e18) / _cToken.exchangeRateStored();\n }\n}\n" + }, + "contracts/strategies/AbstractConvexMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { ICurveMetaPool } from \"./ICurveMetaPool.sol\";\nimport { IERC20, AbstractCurveStrategy, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\nabstract contract AbstractConvexMetaStrategy is AbstractCurveStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n event MaxWithdrawalSlippageUpdated(\n uint256 _prevMaxSlippagePercentage,\n uint256 _newMaxSlippagePercentage\n );\n\n // used to circumvent the stack too deep issue\n struct InitConfig {\n address cvxDepositorAddress; //Address of the Convex depositor(AKA booster) for this pool\n address metapoolAddress; //Address of the Curve MetaPool\n address metapoolMainToken; //Address of Main metapool token\n address cvxRewardStakerAddress; //Address of the CVX rewards staker\n address metapoolLPToken; //Address of metapool LP token\n uint256 cvxDepositorPTokenId; //Pid of the pool referred to by Depositor and staker\n }\n\n address internal cvxDepositorAddress;\n address internal cvxRewardStakerAddress;\n uint256 internal cvxDepositorPTokenId;\n ICurveMetaPool internal metapool;\n IERC20 internal metapoolMainToken;\n IERC20 internal metapoolLPToken;\n // Ordered list of metapool assets\n address[] internal metapoolAssets;\n // Max withdrawal slippage denominated in 1e18 (1e18 == 100%)\n uint256 public maxWithdrawalSlippage;\n uint128 internal crvCoinIndex;\n uint128 internal mainCoinIndex;\n\n int256[41] private ___reserved;\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * DAI, USDC, USDT\n * @param _pTokens Platform Token corresponding addresses\n * @param initConfig Various addresses and info for initialization state\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets,\n address[] calldata _pTokens,\n InitConfig calldata initConfig\n ) external onlyGovernor initializer {\n require(_assets.length == 3, \"Must have exactly three assets\");\n // Should be set prior to abstract initialize call otherwise\n // abstractSetPToken calls will fail\n cvxDepositorAddress = initConfig.cvxDepositorAddress;\n pTokenAddress = _pTokens[0];\n metapool = ICurveMetaPool(initConfig.metapoolAddress);\n metapoolMainToken = IERC20(initConfig.metapoolMainToken);\n cvxRewardStakerAddress = initConfig.cvxRewardStakerAddress;\n metapoolLPToken = IERC20(initConfig.metapoolLPToken);\n cvxDepositorPTokenId = initConfig.cvxDepositorPTokenId;\n maxWithdrawalSlippage = 1e16;\n\n metapoolAssets = [metapool.coins(0), metapool.coins(1)];\n crvCoinIndex = _getMetapoolCoinIndex(pTokenAddress);\n mainCoinIndex = _getMetapoolCoinIndex(initConfig.metapoolMainToken);\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n balance = 0;\n\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 contractPTokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (contractPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = contractPTokens.mulTruncate(virtual_price);\n balance += value;\n }\n\n /* We intentionally omit the metapoolLp tokens held by the metastrategyContract\n * since the contract should never (except in the middle of deposit/withdrawal\n * transaction) hold any amount of those tokens in normal operation. There\n * could be tokens sent to it by a 3rd party and we decide to actively ignore\n * those.\n */\n uint256 metapoolGaugePTokens = IRewardStaking(cvxRewardStakerAddress)\n .balanceOf(address(this));\n\n if (metapoolGaugePTokens > 0) {\n uint256 value = metapoolGaugePTokens.mulTruncate(\n metapool.get_virtual_price()\n );\n balance += value;\n }\n\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = balance.scaleBy(assetDecimals, 18) / THREEPOOL_ASSET_COUNT;\n }\n\n /**\n * @dev This function is completely analogous to _calcCurveTokenAmount[AbstractCurveStrategy]\n * and just utilizes different Curve (meta)pool API\n */\n function _calcCurveMetaTokenAmount(uint128 _coinIndex, uint256 _amount)\n internal\n returns (uint256 requiredMetapoolLP)\n {\n uint256[2] memory _amounts = [uint256(0), uint256(0)];\n _amounts[uint256(_coinIndex)] = _amount;\n\n // LP required when removing required asset ignoring fees\n uint256 lpRequiredNoFees = metapool.calc_token_amount(_amounts, false);\n /* LP required if fees would apply to entirety of removed amount\n *\n * fee is 1e10 denominated number: https://curve.readthedocs.io/exchange-pools.html#StableSwap.fee\n */\n uint256 lpRequiredFullFees = lpRequiredNoFees.mulTruncateScale(\n 1e10 + metapool.fee(),\n 1e10\n );\n\n /* asset received when withdrawing full fee applicable LP accounting for\n * slippage and fees\n */\n uint256 assetReceivedForFullLPFees = metapool.calc_withdraw_one_coin(\n lpRequiredFullFees,\n int128(_coinIndex)\n );\n\n // exact amount of LP required\n requiredMetapoolLP =\n (lpRequiredFullFees * _amount) /\n assetReceivedForFullLPFees;\n }\n\n function _approveBase() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // 3Pool for LP token (required for removing liquidity)\n pToken.safeApprove(platformAddress, 0);\n pToken.safeApprove(platformAddress, type(uint256).max);\n // Gauge for LP token\n metapoolLPToken.safeApprove(cvxDepositorAddress, 0);\n metapoolLPToken.safeApprove(cvxDepositorAddress, type(uint256).max);\n // Metapool for LP token\n pToken.safeApprove(address(metapool), 0);\n pToken.safeApprove(address(metapool), type(uint256).max);\n // Metapool for Metapool main token\n metapoolMainToken.safeApprove(address(metapool), 0);\n metapoolMainToken.safeApprove(address(metapool), type(uint256).max);\n }\n\n /**\n * @dev Get the index of the coin\n */\n function _getMetapoolCoinIndex(address _asset)\n internal\n view\n returns (uint128)\n {\n for (uint128 i = 0; i < 2; i++) {\n if (metapoolAssets[i] == _asset) return i;\n }\n revert(\"Invalid Metapool asset\");\n }\n\n /**\n * @dev Sets max withdrawal slippage that is considered when removing\n * liquidity from Metapools.\n * @param _maxWithdrawalSlippage Max withdrawal slippage denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxWithdrawalSlippage should actually be 0.1% (1e15)\n * for production usage. Contract allows as low value as 0% for confirming\n * correct behavior in test suite.\n */\n function setMaxWithdrawalSlippage(uint256 _maxWithdrawalSlippage)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(\n _maxWithdrawalSlippage <= 1e18,\n \"Max withdrawal slippage needs to be between 0% - 100%\"\n );\n emit MaxWithdrawalSlippageUpdated(\n maxWithdrawalSlippage,\n _maxWithdrawalSlippage\n );\n maxWithdrawalSlippage = _maxWithdrawalSlippage;\n }\n\n /**\n * @dev Collect accumulated CRV and CVX and send to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n IRewardStaking(cvxRewardStakerAddress).getReward();\n _collectRewardTokens();\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/AbstractCurveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve 3Pool Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\nabstract contract AbstractCurveStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 internal constant MAX_SLIPPAGE = 1e16; // 1%, same as the Curve UI\n // number of assets in Curve 3Pool (USDC, DAI, USDT)\n uint256 internal constant THREEPOOL_ASSET_COUNT = 3;\n address internal pTokenAddress;\n\n int256[49] private __reserved;\n\n /**\n * @dev Deposit asset into the Curve 3Pool\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_amount > 0, \"Must deposit something\");\n emit Deposit(_asset, pTokenAddress, _amount);\n\n // 3Pool requires passing deposit amounts for all 3 assets, set to 0 for\n // all\n uint256[3] memory _amounts;\n uint256 poolCoinIndex = _getCoinIndex(_asset);\n // Set the amount on the asset we want to deposit\n _amounts[poolCoinIndex] = _amount;\n ICurvePool curvePool = ICurvePool(platformAddress);\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n uint256 depositValue = _amount.scaleBy(18, assetDecimals).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = depositValue.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n // Do the deposit to 3pool\n curvePool.add_liquidity(_amounts, minMintAmount);\n _lpDepositAll();\n }\n\n function _lpDepositAll() internal virtual;\n\n /**\n * @dev Deposit the entire balance of any supported asset into the Curve 3pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n uint256 depositValue = 0;\n ICurvePool curvePool = ICurvePool(platformAddress);\n uint256 curveVirtualPrice = curvePool.get_virtual_price();\n\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address assetAddress = assetsMapped[i];\n uint256 balance = IERC20(assetAddress).balanceOf(address(this));\n if (balance > 0) {\n uint256 poolCoinIndex = _getCoinIndex(assetAddress);\n // Set the amount on the asset we want to deposit\n _amounts[poolCoinIndex] = balance;\n uint256 assetDecimals = Helpers.getDecimals(assetAddress);\n // Get value of deposit in Curve LP token to later determine\n // the minMintAmount argument for add_liquidity\n depositValue =\n depositValue +\n balance.scaleBy(18, assetDecimals).divPrecisely(\n curveVirtualPrice\n );\n emit Deposit(assetAddress, pTokenAddress, balance);\n }\n }\n\n uint256 minMintAmount = depositValue.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n // Do the deposit to 3pool\n curvePool.add_liquidity(_amounts, minMintAmount);\n\n /* In case of Curve Strategy all assets are mapped to the same pToken (3CrvLP). Let\n * descendants further handle the pToken. By either deploying it to the metapool and\n * resulting tokens in Gauge. Or deploying pTokens directly to the Gauge.\n */\n _lpDepositAll();\n }\n\n function _lpWithdraw(uint256 numCrvTokens) internal virtual;\n\n function _lpWithdrawAll() internal virtual;\n\n /**\n * @dev Withdraw asset from Curve 3Pool\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Invalid amount\");\n\n emit Withdrawal(_asset, pTokenAddress, _amount);\n\n uint256 contractCrv3Tokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n\n uint256 coinIndex = _getCoinIndex(_asset);\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 requiredCrv3Tokens = _calcCurveTokenAmount(coinIndex, _amount);\n\n // We have enough LP tokens, make sure they are all on this contract\n if (contractCrv3Tokens < requiredCrv3Tokens) {\n _lpWithdraw(requiredCrv3Tokens - contractCrv3Tokens);\n }\n\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n _amounts[coinIndex] = _amount;\n\n curvePool.remove_liquidity_imbalance(_amounts, requiredCrv3Tokens);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Calculate amount of LP required when withdrawing specific amount of one\n * of the underlying assets accounting for fees and slippage.\n *\n * Curve pools unfortunately do not contain a calculation function for\n * amount of LP required when withdrawing a specific amount of one of the\n * underlying tokens and also accounting for fees (Curve's calc_token_amount\n * does account for slippage but not fees).\n *\n * Steps taken to calculate the metric:\n * - get amount of LP required if fees wouldn't apply\n * - increase the LP amount as if fees would apply to the entirety of the underlying\n * asset withdrawal. (when withdrawing only one coin fees apply only to amounts\n * of other assets pool would return in case of balanced removal - since those need\n * to be swapped for the single underlying asset being withdrawn)\n * - get amount of underlying asset withdrawn (this Curve function does consider slippage\n * and fees) when using the increased LP amount. As LP amount is slightly over-increased\n * so is amount of underlying assets returned.\n * - since we know exactly how much asset we require take the rate of LP required for asset\n * withdrawn to get the exact amount of LP.\n */\n function _calcCurveTokenAmount(uint256 _coinIndex, uint256 _amount)\n internal\n returns (uint256 required3Crv)\n {\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n _amounts[_coinIndex] = _amount;\n\n // LP required when removing required asset ignoring fees\n uint256 lpRequiredNoFees = curvePool.calc_token_amount(_amounts, false);\n /* LP required if fees would apply to entirety of removed amount\n *\n * fee is 1e10 denominated number: https://curve.readthedocs.io/exchange-pools.html#StableSwap.fee\n */\n uint256 lpRequiredFullFees = lpRequiredNoFees.mulTruncateScale(\n 1e10 + curvePool.fee(),\n 1e10\n );\n\n /* asset received when withdrawing full fee applicable LP accounting for\n * slippage and fees\n */\n uint256 assetReceivedForFullLPFees = curvePool.calc_withdraw_one_coin(\n lpRequiredFullFees,\n int128(uint128(_coinIndex))\n );\n\n // exact amount of LP required\n required3Crv =\n (lpRequiredFullFees * _amount) /\n assetReceivedForFullLPFees;\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n _lpWithdrawAll();\n // Withdraws are proportional to assets held by 3Pool\n uint256[3] memory minWithdrawAmounts = [\n uint256(0),\n uint256(0),\n uint256(0)\n ];\n\n // Remove liquidity\n ICurvePool threePool = ICurvePool(platformAddress);\n threePool.remove_liquidity(\n IERC20(pTokenAddress).balanceOf(address(this)),\n minWithdrawAmounts\n );\n // Transfer assets out of Vault\n // Note that Curve will provide all 3 of the assets in 3pool even if\n // we have not set PToken addresses for all of them in this strategy\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n IERC20 asset = IERC20(threePool.coins(i));\n asset.safeTransfer(vaultAddress, asset.balanceOf(address(this)));\n }\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 totalPTokens = IERC20(pTokenAddress).balanceOf(address(this));\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (totalPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = (totalPTokens * virtual_price) / 1e18;\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = value.scaleBy(assetDecimals, 18) / THREEPOOL_ASSET_COUNT;\n }\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n // This strategy is a special case since it only supports one asset\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n _approveAsset(assetsMapped[i]);\n }\n }\n\n /**\n * @dev Call the necessary approvals for the Curve pool and gauge\n * @param _asset Address of the asset\n */\n function _abstractSetPToken(address _asset, address) internal override {\n _approveAsset(_asset);\n }\n\n function _approveAsset(address _asset) internal {\n IERC20 asset = IERC20(_asset);\n // 3Pool for asset (required for adding liquidity)\n asset.safeApprove(platformAddress, 0);\n asset.safeApprove(platformAddress, type(uint256).max);\n }\n\n function _approveBase() internal virtual;\n\n /**\n * @dev Get the index of the coin\n */\n function _getCoinIndex(address _asset) internal view returns (uint256) {\n for (uint256 i = 0; i < 3; i++) {\n if (assetsMapped[i] == _asset) return i;\n }\n revert(\"Invalid 3pool asset\");\n }\n}\n" + }, + "contracts/strategies/aerodrome/AerodromeAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Aerodrome AMO strategy\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nimport { ISugarHelper } from \"../../interfaces/aerodrome/ISugarHelper.sol\";\nimport { INonfungiblePositionManager } from \"../../interfaces/aerodrome/INonfungiblePositionManager.sol\";\nimport { ISwapRouter } from \"../../interfaces/aerodrome/ISwapRouter.sol\";\nimport { ICLPool } from \"../../interfaces/aerodrome/ICLPool.sol\";\nimport { ICLGauge } from \"../../interfaces/aerodrome/ICLGauge.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\n\ncontract AerodromeAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n using SafeCast for uint256;\n\n /************************************************\n Important (!) setup configuration\n *************************************************/\n\n /**\n * In order to be able to remove a reasonable amount of complexity from the contract one of the\n * preconditions for this contract to function correctly is to have an outside account mint a small\n * amount of liquidity in the tick space where the contract will deploy's its liquidity and then send\n * that NFT LP position to a dead address (transfer to zero address not allowed.) See example of such\n * NFT LP token:\n * https://basescan.org/token/0x827922686190790b37229fd06084350e74485b72?a=413296#inventory\n */\n\n /***************************************\n Storage slot members\n ****************************************/\n\n /// @notice tokenId of the liquidity position\n uint256 public tokenId;\n /// @dev Minimum amount of tokens the strategy would be able to withdraw from the pool.\n /// minimum amount of tokens are withdrawn at a 1:1 price\n uint256 public underlyingAssets;\n /// @notice Marks the start of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareStart;\n /// @notice Marks the end of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareEnd;\n /// @dev reserved for inheritance\n int256[46] private __reserved;\n\n /***************************************\n Constants, structs and events\n ****************************************/\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the OETHb token contract\n address public immutable OETHb;\n /// @notice lower tick set to -1 representing the price of 1.0001 of WETH for 1 OETHb.\n int24 public immutable lowerTick;\n /// @notice lower tick set to 0 representing the price of 1.0000 of WETH for 1 OETHb.\n int24 public immutable upperTick;\n /// @notice tick spacing of the pool (set to 1)\n int24 public immutable tickSpacing;\n /// @notice the swapRouter for performing swaps\n ISwapRouter public immutable swapRouter;\n /// @notice the underlying AMO Slipstream pool\n ICLPool public immutable clPool;\n /// @notice the gauge for the corresponding Slipstream pool (clPool)\n /// @dev can become an immutable once the gauge is created on the base main-net\n ICLGauge public immutable clGauge;\n /// @notice the Position manager contract that is used to manage the pool's position\n INonfungiblePositionManager public immutable positionManager;\n /// @notice helper contract for liquidity and ticker math\n ISugarHelper public immutable helper;\n /// @notice sqrtRatioX96TickLower\n /// @dev tick lower has value -1 and represents the lowest price of WETH priced in OETHb. Meaning the pool\n /// offers less than 1 OETHb for 1 WETH. In other terms to get 1 OETHB the swap needs to offer 1.0001 WETH\n /// this is where purchasing OETHb with WETH within the liquidity position is most expensive\n uint160 public immutable sqrtRatioX96TickLower;\n /// @notice sqrtRatioX96TickHigher\n /// @dev tick higher has value 0 and represents 1:1 price parity of WETH to OETHb\n uint160 public immutable sqrtRatioX96TickHigher;\n /// @dev tick closest to 1:1 price parity\n /// Correctly assessing which tick is closer to 1:1 price parity is important since it affects\n /// the way we calculate the underlying assets in check Balance. The underlying aerodrome pool\n /// orders the tokens depending on the values of their addresses. If OETH token is token0 in the pool\n /// then sqrtRatioX96TickClosestToParity=sqrtRatioX96TickLower. If it is token1 in the pool then\n /// sqrtRatioX96TickClosestToParity=sqrtRatioX96TickHigher\n uint160 public immutable sqrtRatioX96TickClosestToParity;\n\n /// @dev a threshold under which the contract no longer allows for the protocol to rebalance. Guarding\n /// against a strategist / guardian being taken over and with multiple transactions draining the\n /// protocol funds.\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth); // 0x989e5ca8\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth); // 0xa6737d87\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n ); // 0x3681e8e0\n error OutsideExpectedTickRange(int24 currentTick); // 0x5a2eba75\n\n event PoolRebalanced(uint256 currentPoolWethShare);\n\n event PoolWethShareIntervalUpdated(\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n\n event LiquidityRemoved(\n uint256 withdrawLiquidityShare,\n uint256 removedWETHAmount,\n uint256 removedOETHbAmount,\n uint256 wethAmountCollected,\n uint256 oethbAmountCollected,\n uint256 underlyingAssets\n );\n\n event LiquidityAdded(\n uint256 wethAmountDesired,\n uint256 oethbAmountDesired,\n uint256 wethAmountSupplied,\n uint256 oethbAmountSupplied,\n uint256 tokenId,\n uint256 underlyingAssets\n );\n\n event UnderlyingAssetsUpdated(uint256 underlyingAssets);\n\n /**\n * @dev Un-stakes the token from the gauge for the execution duration of\n * the function and after that re-stakes it back in.\n *\n * It is important that the token is unstaked and owned by the strategy contract\n * during any liquidity altering operations and that it is re-staked back into the\n * gauge after liquidity changes. If the token fails to re-stake back to the\n * gauge it is not earning incentives.\n */\n // all functions using this modifier are used by functions with reentrancy check\n // slither-disable-start reentrancy-no-eth\n modifier gaugeUnstakeAndRestake() {\n // because of solidity short-circuit _isLpTokenStakedInGauge doesn't get called\n // when tokenId == 0\n if (tokenId != 0 && _isLpTokenStakedInGauge()) {\n clGauge.withdraw(tokenId);\n }\n _;\n // because of solidity short-circuit _isLpTokenStakedInGauge doesn't get called\n // when tokenId == 0\n if (tokenId != 0 && !_isLpTokenStakedInGauge()) {\n /**\n * It can happen that a withdrawal (or a full withdrawal) transactions would\n * remove all of the liquidity from the token with a NFT token still existing.\n * In that case the token can not be staked into the gauge, as some liquidity\n * needs to be added to it first.\n */\n if (_getLiquidity() > 0) {\n // if token liquidity changes the positionManager requires re-approval.\n // to any contract pre-approved to handle the token.\n positionManager.approve(address(clGauge), tokenId);\n clGauge.deposit(tokenId);\n }\n }\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice the constructor\n /// @dev This contract is intended to be used as a proxy. To prevent the\n /// potential confusion of having a functional implementation contract\n /// the constructor has the `initializer` modifier. This way the\n /// `initialize` function can not be called on the implementation contract.\n /// For the same reason the implementation contract also has the governor\n /// set to a zero address.\n /// @param _stratConfig the basic strategy configuration\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _oethbAddress Address of the Erc20 OETHb Token contract\n /// @param _swapRouter Address of the Aerodrome Universal Swap Router\n /// @param _nonfungiblePositionManager Address of position manager to add/remove\n /// the liquidity\n /// @param _clPool Address of the Aerodrome concentrated liquidity pool\n /// @param _clGauge Address of the Aerodrome slipstream pool gauge\n /// @param _sugarHelper Address of the Aerodrome Sugar helper contract\n /// @param _lowerBoundingTick Smaller bounding tick of our liquidity position\n /// @param _upperBoundingTick Larger bounding tick of our liquidity position\n /// @param _tickClosestToParity Tick that is closer to 1:1 price parity\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethbAddress,\n address _swapRouter,\n address _nonfungiblePositionManager,\n address _clPool,\n address _clGauge,\n address _sugarHelper,\n int24 _lowerBoundingTick,\n int24 _upperBoundingTick,\n int24 _tickClosestToParity\n ) initializer InitializableAbstractStrategy(_stratConfig) {\n require(\n _lowerBoundingTick == _tickClosestToParity ||\n _upperBoundingTick == _tickClosestToParity,\n \"Misconfigured tickClosestToParity\"\n );\n require(\n ICLPool(_clPool).token0() == _wethAddress,\n \"Only WETH supported as token0\"\n );\n require(\n ICLPool(_clPool).token1() == _oethbAddress,\n \"Only OETHb supported as token1\"\n );\n int24 _tickSpacing = ICLPool(_clPool).tickSpacing();\n // when we generalize AMO we might support other tick spacings\n require(_tickSpacing == 1, \"Unsupported tickSpacing\");\n\n WETH = _wethAddress;\n OETHb = _oethbAddress;\n swapRouter = ISwapRouter(_swapRouter);\n positionManager = INonfungiblePositionManager(\n _nonfungiblePositionManager\n );\n clPool = ICLPool(_clPool);\n clGauge = ICLGauge(_clGauge);\n helper = ISugarHelper(_sugarHelper);\n sqrtRatioX96TickLower = ISugarHelper(_sugarHelper).getSqrtRatioAtTick(\n _lowerBoundingTick\n );\n sqrtRatioX96TickHigher = ISugarHelper(_sugarHelper).getSqrtRatioAtTick(\n _upperBoundingTick\n );\n sqrtRatioX96TickClosestToParity = ISugarHelper(_sugarHelper)\n .getSqrtRatioAtTick(_tickClosestToParity);\n\n lowerTick = _lowerBoundingTick;\n upperTick = _upperBoundingTick;\n tickSpacing = _tickSpacing;\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /**\n * @notice initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n */\n function initialize(address[] memory _rewardTokenAddresses)\n external\n onlyGovernor\n initializer\n {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n new address[](0),\n new address[](0)\n );\n }\n\n /***************************************\n Configuration \n ****************************************/\n\n /**\n * @notice Set allowed pool weth share interval. After the rebalance happens\n * the share of WETH token in the ticker needs to be withing the specifications\n * of the interval.\n *\n * @param _allowedWethShareStart Start of WETH share interval expressed as 18 decimal amount\n * @param _allowedWethShareEnd End of WETH share interval expressed as 18 decimal amount\n */\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external onlyGovernor {\n require(\n _allowedWethShareStart < _allowedWethShareEnd,\n \"Invalid interval\"\n );\n // can not go below 1% weth share\n require(_allowedWethShareStart > 0.01 ether, \"Invalid interval start\");\n // can not go above 95% weth share\n require(_allowedWethShareEnd < 0.95 ether, \"Invalid interval end\");\n\n allowedWethShareStart = _allowedWethShareStart;\n allowedWethShareEnd = _allowedWethShareEnd;\n emit PoolWethShareIntervalUpdated(\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n\n /***************************************\n Periphery utils\n ****************************************/\n\n function _isLpTokenStakedInGauge() internal view returns (bool) {\n require(tokenId != 0, \"Missing NFT LP token\");\n\n address owner = positionManager.ownerOf(tokenId);\n require(\n owner == address(clGauge) || owner == address(this),\n \"Unexpected token owner\"\n );\n return owner == address(clGauge);\n }\n\n /***************************************\n Strategy overrides \n ****************************************/\n\n /**\n * @notice Deposit an amount of assets into the strategy contract. Calling deposit doesn't\n * automatically deposit funds into the underlying Aerodrome pool\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposit WETH to the strategy contract. This function does not add liquidity to the\n * underlying Aerodrome pool.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance > 1e12) {\n _deposit(WETH, _wethBalance);\n }\n }\n\n /**\n * @dev Deposit WETH to the contract. This function doesn't deposit the liquidity to the\n * pool, that is done via the rebalance call.\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n emit Deposit(_asset, address(0), _amount);\n\n // if the pool price is not within the expected interval leave the WETH on the contract\n // as to not break the mints\n (bool _isExpectedRange, ) = _checkForExpectedPoolPrice(false);\n if (_isExpectedRange) {\n // deposit funds into the underlying pool\n _rebalance(0, false, 0);\n }\n }\n\n /**\n * @notice Rebalance the pool to the desired token split and Deposit any WETH on the contract to the\n * underlying aerodrome pool. Print the required amount of corresponding OETHb. After the rebalancing is\n * done burn any potentially remaining OETHb tokens still on the strategy contract.\n *\n * This function has a slightly different behaviour depending on the status of the underlying Aerodrome\n * slipstream pool. The function consists of the following 3 steps:\n * 1. withdrawPartialLiquidity -> so that moving the activeTrading price via a swap is cheaper\n * 2. swapToDesiredPosition -> move active trading price in the pool to be able to deposit WETH & OETHb\n * tokens with the desired pre-configured shares\n * 3. addLiquidity -> add liquidity into the pool respecting share split configuration\n *\n * Scenario 1: When there is no liquidity in the pool from the strategy but there is from other LPs then\n * only step 1 is skipped. (It is important to note that liquidity needs to exist in the configured\n * strategy tick ranges in order for the swap to be possible) Step 3 mints new liquidity position\n * instead of adding to an existing one.\n * Scenario 2: When there is strategy's liquidity in the pool all 3 steps are taken\n *\n *\n * Exact _amountToSwap, _swapWeth & _minTokenReceived parameters shall be determined by simulating the\n * transaction off-chain. The strategy checks that after the swap the share of the tokens is in the\n * expected ranges.\n *\n * @param _amountToSwap The amount of the token to swap\n * @param _swapWeth Swap using WETH when true, use OETHb when false\n * @param _minTokenReceived Slippage check -> minimum amount of token expected in return\n */\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) external nonReentrant onlyGovernorOrStrategist {\n _rebalance(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n\n function _rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n /**\n * Would be nice to check if there is any total liquidity in the pool before performing this swap\n * but there is no easy way to do that in UniswapV3:\n * - clPool.liquidity() -> only liquidity in the active tick\n * - asset[1&2].balanceOf(address(clPool)) -> will include uncollected tokens of LP providers\n * after their liquidity position has been decreased\n */\n\n /**\n * When rebalance is called for the first time there is no strategy\n * liquidity in the pool yet. The liquidity removal is thus skipped.\n * Also execute this function when WETH is required for the swap.\n */\n if (tokenId != 0 && _swapWeth && _amountToSwap > 0) {\n _ensureWETHBalance(_amountToSwap);\n }\n\n // in some cases we will just want to add liquidity and not issue a swap to move the\n // active trading position within the pool\n if (_amountToSwap > 0) {\n _swapToDesiredPosition(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n // calling check liquidity early so we don't get unexpected errors when adding liquidity\n // in the later stages of this function\n _checkForExpectedPoolPrice(true);\n\n _addLiquidity();\n\n // this call shouldn't be necessary, since adding liquidity shouldn't affect the active\n // trading price. It is a defensive programming measure.\n (, uint256 _wethSharePct) = _checkForExpectedPoolPrice(true);\n\n // revert if protocol insolvent\n _solvencyAssert();\n\n emit PoolRebalanced(_wethSharePct);\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethbSupply = IERC20(OETHb).totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethbSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /**\n * @dev Decrease partial or all liquidity from the pool.\n * @param _liquidityToDecrease The amount of liquidity to remove expressed in 18 decimal point\n */\n function _removeLiquidity(uint256 _liquidityToDecrease)\n internal\n gaugeUnstakeAndRestake\n {\n require(_liquidityToDecrease > 0, \"Must remove some liquidity\");\n\n uint128 _liquidity = _getLiquidity();\n // need to convert to uint256 since intermittent result is to big for uint128 to handle\n uint128 _liquidityToRemove = uint256(_liquidity)\n .mulTruncate(_liquidityToDecrease)\n .toUint128();\n\n /**\n * There is no liquidity to remove -> exit function early. This can happen after a\n * withdraw/withdrawAll removes all of the liquidity while retaining the NFT token.\n */\n if (_liquidity == 0 || _liquidityToRemove == 0) {\n return;\n }\n\n (uint256 _amountWeth, uint256 _amountOethb) = positionManager\n .decreaseLiquidity(\n // Both expected amounts can be 0 since we don't really care if any swaps\n // happen just before the liquidity removal.\n INonfungiblePositionManager.DecreaseLiquidityParams({\n tokenId: tokenId,\n liquidity: _liquidityToRemove,\n amount0Min: 0,\n amount1Min: 0,\n deadline: block.timestamp\n })\n );\n\n (\n uint256 _amountWethCollected,\n uint256 _amountOethbCollected\n ) = positionManager.collect(\n INonfungiblePositionManager.CollectParams({\n tokenId: tokenId,\n recipient: address(this),\n amount0Max: type(uint128).max, // defaults to all tokens owed\n amount1Max: type(uint128).max // defaults to all tokens owed\n })\n );\n\n _updateUnderlyingAssets();\n\n emit LiquidityRemoved(\n _liquidityToDecrease,\n _amountWeth, //removedWethAmount\n _amountOethb, //removedOethbAmount\n _amountWethCollected,\n _amountOethbCollected,\n underlyingAssets\n );\n\n _burnOethbOnTheContract();\n }\n\n /**\n * @dev Perform a swap so that after the swap the ticker has the desired WETH to OETHb token share.\n */\n function _swapToDesiredPosition(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n IERC20 _tokenToSwap = IERC20(_swapWeth ? WETH : OETHb);\n uint256 _balance = _tokenToSwap.balanceOf(address(this));\n\n if (_balance < _amountToSwap) {\n // This should never trigger since _ensureWETHBalance will already\n // throw an error if there is not enough WETH\n if (_swapWeth) {\n revert NotEnoughWethForSwap(_balance, _amountToSwap);\n }\n // if swapping OETHb\n uint256 mintForSwap = _amountToSwap - _balance;\n IVault(vaultAddress).mintForStrategy(mintForSwap);\n }\n\n // approve the specific amount of WETH required\n if (_swapWeth) {\n IERC20(WETH).approve(address(swapRouter), _amountToSwap);\n }\n\n // Swap it\n swapRouter.exactInputSingle(\n // sqrtPriceLimitX96 is just a rough sanity check that we are within 0 -> 1 tick\n // a more fine check is performed in _checkForExpectedPoolPrice\n // Note: this needs further work if we want to generalize this approach\n ISwapRouter.ExactInputSingleParams({\n tokenIn: address(_tokenToSwap),\n tokenOut: _swapWeth ? OETHb : WETH,\n tickSpacing: tickSpacing, // set to 1\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: _amountToSwap,\n amountOutMinimum: _minTokenReceived, // slippage check\n sqrtPriceLimitX96: _swapWeth\n ? sqrtRatioX96TickLower\n : sqrtRatioX96TickHigher\n })\n );\n\n /**\n * In the interest of each function in _rebalance to leave the contract state as\n * clean as possible the OETHb tokens here are burned. This decreases the\n * dependence where `_swapToDesiredPosition` function relies on later functions\n * (`addLiquidity`) to burn the OETHb. Reducing the risk of error introduction.\n */\n _burnOethbOnTheContract();\n }\n\n /**\n * @dev Add liquidity into the pool in the pre-configured WETH to OETHb share ratios\n * defined by the allowedPoolWethShareStart|End interval. This function will respect\n * liquidity ratios when there is no liquidity yet in the pool. If liquidity is already\n * present then it relies on the `_swapToDesiredPosition` function in a step before\n * to already move the trading price to desired position (with some tolerance).\n */\n // rebalance already has re-entrency checks\n // slither-disable-start reentrancy-no-eth\n function _addLiquidity() internal gaugeUnstakeAndRestake {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n // don't deposit small liquidity amounts\n if (_wethBalance <= 1e12) {\n return;\n }\n\n uint160 _currentPrice = getPoolX96Price();\n /**\n * Sanity check active trading price is positioned within our desired tick.\n *\n * We revert when price is equal to the lower tick even though that is still\n * a valid amount in regards to ticker position by Sugar.estimateAmount call.\n * Current price equaling tick bound at the 1:1 price parity results in\n * uint overfow when calculating the OETHb balance to deposit.\n */\n if (\n _currentPrice <= sqrtRatioX96TickLower ||\n _currentPrice >= sqrtRatioX96TickHigher\n ) {\n revert OutsideExpectedTickRange(getCurrentTradingTick());\n }\n\n /**\n * If estimateAmount1 call fails it could be due to _currentPrice being really\n * close to a tick and amount1 is a larger number than the sugar helper is able\n * to compute.\n *\n * If token addresses were reversed estimateAmount0 would be required here\n */\n uint256 _oethbRequired = helper.estimateAmount1(\n _wethBalance,\n address(0), // no need to pass pool address when current price is specified\n _currentPrice,\n lowerTick,\n upperTick\n );\n\n if (_oethbRequired > _oethbBalance) {\n IVault(vaultAddress).mintForStrategy(\n _oethbRequired - _oethbBalance\n );\n }\n\n // approve the specific amount of WETH required\n IERC20(WETH).approve(address(positionManager), _wethBalance);\n\n uint256 _wethAmountSupplied;\n uint256 _oethbAmountSupplied;\n if (tokenId == 0) {\n (\n tokenId,\n ,\n _wethAmountSupplied,\n _oethbAmountSupplied\n ) = positionManager.mint(\n /** amount0Min & amount1Min are left at 0 because slippage protection is ensured by the\n * _checkForExpectedPoolPrice\n *›\n * Also sqrtPriceX96 is 0 because the pool is already created\n * non zero amount attempts to create a new instance of the pool\n */\n INonfungiblePositionManager.MintParams({\n token0: WETH,\n token1: OETHb,\n tickSpacing: tickSpacing,\n tickLower: lowerTick,\n tickUpper: upperTick,\n amount0Desired: _wethBalance,\n amount1Desired: _oethbRequired,\n amount0Min: 0,\n amount1Min: 0,\n recipient: address(this),\n deadline: block.timestamp,\n sqrtPriceX96: 0\n })\n );\n } else {\n (, _wethAmountSupplied, _oethbAmountSupplied) = positionManager\n .increaseLiquidity(\n /** amount0Min & amount1Min are left at 0 because slippage protection is ensured by the\n * _checkForExpectedPoolPrice\n */\n INonfungiblePositionManager.IncreaseLiquidityParams({\n tokenId: tokenId,\n amount0Desired: _wethBalance,\n amount1Desired: _oethbRequired,\n amount0Min: 0,\n amount1Min: 0,\n deadline: block.timestamp\n })\n );\n }\n\n _updateUnderlyingAssets();\n emit LiquidityAdded(\n _wethBalance, // wethAmountDesired\n _oethbRequired, // oethbAmountDesired\n _wethAmountSupplied, // wethAmountSupplied\n _oethbAmountSupplied, // oethbAmountSupplied\n tokenId, // tokenId\n underlyingAssets\n );\n\n // burn remaining OETHb\n _burnOethbOnTheContract();\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @dev Check that the Aerodrome pool price is within the expected\n * parameters.\n * This function works whether the strategy contract has liquidity\n * position in the pool or not. The function returns _wethSharePct\n * as a gas optimization measure.\n * @param throwException when set to true the function throws an exception\n * when pool's price is not within expected range.\n * @return _isExpectedRange Bool expressing price is within expected range\n * @return _wethSharePct Share of WETH owned by this strategy contract in the\n * configured ticker.\n */\n function _checkForExpectedPoolPrice(bool throwException)\n internal\n view\n returns (bool _isExpectedRange, uint256 _wethSharePct)\n {\n require(\n allowedWethShareStart != 0 && allowedWethShareEnd != 0,\n \"Weth share interval not set\"\n );\n\n uint160 _currentPrice = getPoolX96Price();\n\n /**\n * First check we are in expected tick range\n *\n * We revert even though price being equal to the lower tick would still\n * count being within lower tick for the purpose of Sugar.estimateAmount calls\n */\n if (\n _currentPrice <= sqrtRatioX96TickLower ||\n _currentPrice >= sqrtRatioX96TickHigher\n ) {\n if (throwException) {\n revert OutsideExpectedTickRange(getCurrentTradingTick());\n }\n return (false, 0);\n }\n\n // 18 decimal number expressed WETH tick share\n _wethSharePct = _getWethShare(_currentPrice);\n\n if (\n _wethSharePct < allowedWethShareStart ||\n _wethSharePct > allowedWethShareEnd\n ) {\n if (throwException) {\n revert PoolRebalanceOutOfBounds(\n _wethSharePct,\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n return (false, _wethSharePct);\n }\n\n return (true, _wethSharePct);\n }\n\n /**\n * Burns any OETHb tokens remaining on the strategy contract\n */\n function _burnOethbOnTheContract() internal {\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n if (_oethbBalance > 1e12) {\n IVault(vaultAddress).burnForStrategy(_oethbBalance);\n }\n }\n\n /// @dev This function assumes there are no uncollected tokens in the clPool owned by the strategy contract.\n /// For that reason any liquidity withdrawals must also collect the tokens.\n function _updateUnderlyingAssets() internal {\n if (tokenId == 0) {\n underlyingAssets = 0;\n emit UnderlyingAssetsUpdated(underlyingAssets);\n return;\n }\n\n uint128 _liquidity = _getLiquidity();\n\n /**\n * Our net value represent the smallest amount of tokens we are able to extract from the position\n * given our liquidity.\n *\n * The least amount of tokens extraditable from the position is where the active trading price is\n * at the ticker 0 meaning the pool is offering 1:1 trades between WETH & OETHb. At that moment the pool\n * consists completely of OETHb and no WETH.\n *\n * The more swaps from WETH -> OETHb happen on the pool the more the price starts to move towards the -1\n * ticker making OETHb (priced in WETH) more expensive.\n *\n * An additional note: when liquidity is 0 then the helper returns 0 for both token amounts. And the\n * function set underlying assets to 0.\n */\n (uint256 _wethAmount, uint256 _oethbAmount) = helper\n .getAmountsForLiquidity(\n sqrtRatioX96TickClosestToParity, // sqrtRatioX96\n sqrtRatioX96TickLower, // sqrtRatioAX96\n sqrtRatioX96TickHigher, // sqrtRatioBX96\n _liquidity\n );\n\n require(_wethAmount == 0, \"Non zero wethAmount\");\n underlyingAssets = _oethbAmount;\n emit UnderlyingAssetsUpdated(underlyingAssets);\n }\n\n /**\n * @dev This function removes the appropriate amount of liquidity to assure that the required\n * amount of WETH is available on the contract\n *\n * @param _amount WETH balance required on the contract\n */\n function _ensureWETHBalance(uint256 _amount) internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance >= _amount) {\n return;\n }\n\n require(tokenId != 0, \"No liquidity available\");\n uint256 _additionalWethRequired = _amount - _wethBalance;\n (uint256 _wethInThePool, ) = getPositionPrincipal();\n\n if (_wethInThePool < _additionalWethRequired) {\n revert NotEnoughWethLiquidity(\n _wethInThePool,\n _additionalWethRequired\n );\n }\n\n uint256 shareOfWethToRemove = Math.min(\n _additionalWethRequired.divPrecisely(_wethInThePool) + 1,\n 1e18\n );\n _removeLiquidity(shareOfWethToRemove);\n }\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset WETH address\n * @param _amount Amount of WETH to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n _ensureWETHBalance(_amount);\n\n _withdraw(_recipient, _amount);\n }\n\n /**\n * @notice Withdraw WETH and sends it to the Vault.\n */\n function withdrawAll() external override onlyVault nonReentrant {\n if (tokenId != 0) {\n _removeLiquidity(1e18);\n }\n\n uint256 _balance = IERC20(WETH).balanceOf(address(this));\n if (_balance > 0) {\n _withdraw(vaultAddress, _balance);\n }\n }\n\n function _withdraw(address _recipient, uint256 _amount) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n IERC20(WETH).safeTransfer(_recipient, _amount);\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /**\n * @dev Collect the AERO token from the gauge\n */\n function _collectRewardTokens() internal override {\n if (tokenId != 0 && _isLpTokenStakedInGauge()) {\n clGauge.getReward(tokenId);\n }\n super._collectRewardTokens();\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /**\n * @dev Approve the spending of all assets\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n // to add liquidity to the clPool\n IERC20(OETHb).approve(address(positionManager), type(uint256).max);\n // to be able to rebalance using the swapRouter\n IERC20(OETHb).approve(address(swapRouter), type(uint256).max);\n\n /* the behaviour of this strategy has slightly changed and WETH could be\n * present on the contract between the transactions. For that reason we are\n * un-approving WETH to the swapRouter & positionManager and only approving\n * the required amount before a transaction\n */\n IERC20(WETH).approve(address(swapRouter), 0);\n IERC20(WETH).approve(address(positionManager), 0);\n }\n\n /***************************************\n Balances and Fees\n ****************************************/\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256)\n {\n require(_asset == WETH, \"Only WETH supported\");\n\n // we could in theory deposit to the strategy and forget to call rebalance in the same\n // governance transaction batch. In that case the WETH that is on the strategy contract\n // also needs to be accounted for.\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n // just paranoia check, in case there is OETHb in the strategy that for some reason hasn't\n // been burned yet.\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n return underlyingAssets + _wethBalance + _oethbBalance;\n }\n\n /**\n * @dev Returns the balance of both tokens in a given position (excluding fees)\n * @return _amountWeth Amount of WETH in position\n * @return _amountOethb Amount of OETHb in position\n */\n function getPositionPrincipal()\n public\n view\n returns (uint256 _amountWeth, uint256 _amountOethb)\n {\n if (tokenId == 0) {\n return (0, 0);\n }\n\n uint160 _sqrtRatioX96 = getPoolX96Price();\n (_amountWeth, _amountOethb) = helper.principal(\n positionManager,\n tokenId,\n _sqrtRatioX96\n );\n }\n\n /**\n * @notice Returns the current pool price in X96 format\n * @return _sqrtRatioX96 Pool price\n */\n function getPoolX96Price() public view returns (uint160 _sqrtRatioX96) {\n (_sqrtRatioX96, , , , , ) = clPool.slot0();\n }\n\n /**\n * @notice Returns the current active trading tick of the underlying pool\n * @return _currentTick Current pool trading tick\n */\n function getCurrentTradingTick() public view returns (int24 _currentTick) {\n (, _currentTick, , , , ) = clPool.slot0();\n }\n\n /**\n * @notice Returns the percentage of WETH liquidity in the configured ticker\n * owned by this strategy contract.\n * @return uint256 1e18 denominated percentage expressing the share\n */\n function getWETHShare() external view returns (uint256) {\n uint160 _currentPrice = getPoolX96Price();\n return _getWethShare(_currentPrice);\n }\n\n /**\n * @notice Returns the amount of liquidity in the contract's LP position\n * @return _liquidity Amount of liquidity in the position\n */\n function _getLiquidity() internal view returns (uint128 _liquidity) {\n if (tokenId == 0) {\n revert(\"No LP position\");\n }\n\n (, , , , , , , _liquidity, , , , ) = positionManager.positions(tokenId);\n }\n\n function _getWethShare(uint160 _currentPrice)\n internal\n view\n returns (uint256)\n {\n /**\n * If estimateAmount1 call fails it could be due to _currentPrice being really\n * close to a tick and amount1 too big to compute.\n *\n * If token addresses were reversed estimateAmount0 would be required here\n */\n uint256 _normalizedWethAmount = 1 ether;\n uint256 _correspondingOethAmount = helper.estimateAmount1(\n _normalizedWethAmount,\n address(0), // no need to pass pool address when current price is specified\n _currentPrice,\n lowerTick,\n upperTick\n );\n\n // 18 decimal number expressed weth tick share\n return\n _normalizedWethAmount.divPrecisely(\n _normalizedWethAmount + _correspondingOethAmount\n );\n }\n\n /***************************************\n Hidden functions\n ****************************************/\n /// @inheritdoc InitializableAbstractStrategy\n function setPTokenAddress(address, address) external override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /// @inheritdoc InitializableAbstractStrategy\n function removePToken(uint256) external override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Not supported\n */\n function _abstractSetPToken(address, address) internal override {\n // the deployer shall call safeApproveAllTokens() to set necessary approvals\n revert(\"Unsupported method\");\n }\n\n /***************************************\n ERC721 management\n ****************************************/\n\n /// @notice Callback function for whenever a NFT is transferred to this contract\n // solhint-disable-next-line max-line-length\n /// Ref: https://docs.openzeppelin.com/contracts/3.x/api/token/erc721#IERC721Receiver-onERC721Received-address-address-uint256-bytes-\n function onERC721Received(\n address,\n address,\n uint256,\n bytes calldata\n ) external returns (bytes4) {\n return this.onERC721Received.selector;\n }\n}\n" + }, + "contracts/strategies/balancer/AbstractAuraStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Base Balancer Abstract Strategy\n * @author Origin Protocol Inc\n */\n\nimport { AbstractBalancerStrategy } from \"./AbstractBalancerStrategy.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IERC4626 } from \"../../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport { IRewardStaking } from \"../IRewardStaking.sol\";\n\nabstract contract AbstractAuraStrategy is AbstractBalancerStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n /// @notice Address of the Aura rewards pool\n address public immutable auraRewardPoolAddress;\n\n // renamed from __reserved to not shadow AbstractBalancerStrategy.__reserved,\n int256[50] private __reserved_baseAuraStrategy;\n\n constructor(address _auraRewardPoolAddress) {\n auraRewardPoolAddress = _auraRewardPoolAddress;\n }\n\n /**\n * @dev Deposit all Balancer Pool Tokens (BPT) in this strategy contract\n * to the Aura rewards pool.\n */\n function _lpDepositAll() internal virtual override {\n uint256 bptBalance = IERC20(platformAddress).balanceOf(address(this));\n uint256 auraLp = IERC4626(auraRewardPoolAddress).deposit(\n bptBalance,\n address(this)\n );\n require(bptBalance == auraLp, \"Aura LP != BPT\");\n }\n\n /**\n * @dev Withdraw `numBPTTokens` Balancer Pool Tokens (BPT) from\n * the Aura rewards pool to this strategy contract.\n * @param numBPTTokens Number of Balancer Pool Tokens (BPT) to withdraw\n */\n function _lpWithdraw(uint256 numBPTTokens) internal virtual override {\n IRewardStaking(auraRewardPoolAddress).withdrawAndUnwrap(\n numBPTTokens,\n true // also claim reward tokens\n );\n }\n\n /**\n * @dev Withdraw all Balancer Pool Tokens (BPT) from\n * the Aura rewards pool to this strategy contract.\n */\n function _lpWithdrawAll() internal virtual override {\n // Get all the strategy's BPTs in Aura\n // maxRedeem is implemented as balanceOf(address) in Aura\n uint256 bptBalance = IERC4626(auraRewardPoolAddress).maxRedeem(\n address(this)\n );\n\n IRewardStaking(auraRewardPoolAddress).withdrawAndUnwrap(\n bptBalance,\n true // also claim reward tokens\n );\n }\n\n /**\n * @notice Collects BAL and AURA tokens from the rewards pool.\n */\n function collectRewardTokens()\n external\n virtual\n override\n onlyHarvester\n nonReentrant\n {\n /* Similar to Convex, calling this function collects both of the\n * accrued BAL and AURA tokens.\n */\n IRewardStaking(auraRewardPoolAddress).getReward();\n _collectRewardTokens();\n }\n\n /// @notice Balancer Pool Tokens (BPT) in the Balancer pool and the Aura rewards pool.\n function _getBalancerPoolTokens()\n internal\n view\n override\n returns (uint256 balancerPoolTokens)\n {\n balancerPoolTokens =\n IERC20(platformAddress).balanceOf(address(this)) +\n // maxRedeem is implemented as balanceOf(address) in Aura\n IERC4626(auraRewardPoolAddress).maxRedeem(address(this));\n }\n\n function _approveBase() internal virtual override {\n super._approveBase();\n\n IERC20 pToken = IERC20(platformAddress);\n pToken.safeApprove(auraRewardPoolAddress, type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/balancer/AbstractBalancerStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Balancer Abstract Strategy\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\nimport { IRateProvider } from \"../../interfaces/balancer/IRateProvider.sol\";\nimport { VaultReentrancyLib } from \"./VaultReentrancyLib.sol\";\nimport { IOracle } from \"../../interfaces/IOracle.sol\";\nimport { IWstETH } from \"../../interfaces/IWstETH.sol\";\nimport { IERC4626 } from \"../../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nabstract contract AbstractBalancerStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n address public immutable rETH;\n address public immutable stETH;\n address public immutable wstETH;\n address public immutable frxETH;\n address public immutable sfrxETH;\n\n /// @notice Address of the Balancer vault\n IBalancerVault public immutable balancerVault;\n /// @notice Balancer pool identifier\n bytes32 public immutable balancerPoolId;\n\n // Max withdrawal deviation denominated in 1e18 (1e18 == 100%)\n uint256 public maxWithdrawalDeviation;\n // Max deposit deviation denominated in 1e18 (1e18 == 100%)\n uint256 public maxDepositDeviation;\n\n int256[48] private __reserved;\n\n struct BaseBalancerConfig {\n address rEthAddress; // Address of the rETH token\n address stEthAddress; // Address of the stETH token\n address wstEthAddress; // Address of the wstETH token\n address frxEthAddress; // Address of the frxEth token\n address sfrxEthAddress; // Address of the sfrxEth token\n address balancerVaultAddress; // Address of the Balancer vault\n bytes32 balancerPoolId; // Balancer pool identifier\n }\n\n event MaxWithdrawalDeviationUpdated(\n uint256 _prevMaxDeviationPercentage,\n uint256 _newMaxDeviationPercentage\n );\n event MaxDepositDeviationUpdated(\n uint256 _prevMaxDeviationPercentage,\n uint256 _newMaxDeviationPercentage\n );\n\n /**\n * @dev Ensure we are not in a Vault context when this function is called, by attempting a no-op internal\n * balance operation. If we are already in a Vault transaction (e.g., a swap, join, or exit), the Vault's\n * reentrancy protection will cause this function to revert.\n *\n * Use this modifier with any function that can cause a state change in a pool and is either public itself,\n * or called by a public function *outside* a Vault operation (e.g., join, exit, or swap).\n *\n * This is to protect against Balancer's read-only re-entrancy vulnerability:\n * https://www.notion.so/originprotocol/Balancer-read-only-reentrancy-c686e72c82414ef18fa34312bb02e11b\n */\n modifier whenNotInBalancerVaultContext() {\n VaultReentrancyLib.ensureNotInVaultContext(balancerVault);\n _;\n }\n\n constructor(BaseBalancerConfig memory _balancerConfig) {\n rETH = _balancerConfig.rEthAddress;\n stETH = _balancerConfig.stEthAddress;\n wstETH = _balancerConfig.wstEthAddress;\n frxETH = _balancerConfig.frxEthAddress;\n sfrxETH = _balancerConfig.sfrxEthAddress;\n\n balancerVault = IBalancerVault(_balancerConfig.balancerVaultAddress);\n balancerPoolId = _balancerConfig.balancerPoolId;\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Balancer's strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of BAL & AURA\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * WETH, stETH\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // BAL & AURA\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n maxWithdrawalDeviation = 1e16;\n maxDepositDeviation = 1e16;\n\n emit MaxWithdrawalDeviationUpdated(0, maxWithdrawalDeviation);\n emit MaxDepositDeviationUpdated(0, maxDepositDeviation);\n\n IERC20[] memory poolAssets = _getPoolAssets();\n require(\n poolAssets.length == _assets.length,\n \"Pool assets length mismatch\"\n );\n for (uint256 i = 0; i < _assets.length; ++i) {\n address asset = _fromPoolAsset(address(poolAssets[i]));\n require(_assets[i] == asset, \"Pool assets mismatch\");\n }\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @notice Get strategy's share of an assets in the Balancer pool.\n * This is not denominated in OUSD/ETH value of the assets in the Balancer pool.\n * @param _asset Address of the Vault collateral asset\n * @return amount the amount of vault collateral assets\n *\n * IMPORTANT if this function is overridden it needs to have a whenNotInBalancerVaultContext\n * modifier on it or it is susceptible to read-only re-entrancy attack\n *\n * @dev it is important that this function is not affected by reporting inflated\n * values of assets in case of any pool manipulation. Such a manipulation could easily\n * exploit the protocol by:\n * - minting OETH\n * - tilting Balancer pool to report higher balances of assets\n * - rebasing() -> all that extra token balances get distributed to OETH holders\n * - tilting pool back\n * - redeeming OETH\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n whenNotInBalancerVaultContext\n returns (uint256 amount)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n\n uint256 bptBalance = _getBalancerPoolTokens();\n\n /* To calculate the worth of queried asset:\n * - assume that all tokens normalized to their ETH value have an equal split balance\n * in the pool when it is balanced\n * - multiply the BPT amount with the bpt rate to get the ETH denominated amount\n * of strategy's holdings\n * - divide that by the number of tokens we support in the pool to get ETH denominated\n * amount that is applicable to each supported token in the pool.\n *\n * It would be possible to support only 1 asset in the pool (and be exposed to all\n * the assets while holding BPT tokens) and deposit/withdraw/checkBalance using only\n * that asset. TBD: changes to other functions still required if we ever decide to\n * go with such configuration.\n */\n amount = (bptBalance.mulTruncate(\n IRateProvider(platformAddress).getRate()\n ) / assetsMapped.length);\n\n /* If the pool asset is equal to (strategy )_asset it means that a rate\n * provider for that asset exists and that asset is not necessarily\n * pegged to a unit (ETH).\n *\n * Because this function returns the balance of the asset and is not denominated in\n * ETH units we need to convert the ETH denominated amount to asset amount.\n */\n if (_toPoolAsset(_asset) == _asset) {\n amount = amount.divPrecisely(_getRateProviderRate(_asset));\n }\n }\n\n /**\n * @notice Returns the value of all assets managed by this strategy.\n * Uses the Balancer pool's rate (virtual price) to convert the strategy's\n * Balancer Pool Tokens (BPT) to ETH value.\n * @return value The ETH value\n *\n * IMPORTANT if this function is overridden it needs to have a whenNotInBalancerVaultContext\n * modifier on it or it is susceptible to read-only re-entrancy attack\n */\n function checkBalance()\n external\n view\n virtual\n whenNotInBalancerVaultContext\n returns (uint256 value)\n {\n uint256 bptBalance = _getBalancerPoolTokens();\n\n // Convert BPT to ETH value\n value = bptBalance.mulTruncate(\n IRateProvider(platformAddress).getRate()\n );\n }\n\n /// @notice Balancer Pool Tokens (BPT) in the Balancer pool.\n function _getBalancerPoolTokens()\n internal\n view\n virtual\n returns (uint256 balancerPoolTokens)\n {\n balancerPoolTokens = IERC20(platformAddress).balanceOf(address(this));\n }\n\n /* solhint-disable max-line-length */\n /**\n * @notice BPT price is calculated by taking the rate from the rateProvider of the asset in\n * question. If one does not exist it defaults to 1e18. To get the final BPT expected that\n * is multiplied by the underlying asset amount divided by BPT token rate. BPT token rate is\n * similar to Curve's virtual_price and expresses how much has the price of BPT appreciated\n * (e.g. due to swap fees) in relation to the underlying assets\n *\n * Using the above approach makes the strategy vulnerable to a possible MEV attack using\n * flash loan to manipulate the pool before a deposit/withdrawal since the function ignores\n * market values of the assets being priced in BPT.\n *\n * At the time of writing there is no safe on-chain approach to pricing BPT in a way that it\n * would make it invulnerable to MEV pool manipulation. See recent Balancer exploit:\n * https://www.notion.so/originprotocol/Balancer-OETH-strategy-9becdea132704e588782a919d7d471eb?pvs=4#1cf07de12fc64f1888072321e0644348\n *\n * To mitigate MEV possibilities during deposits and withdraws, the VaultValueChecker will use checkBalance before and after the move\n * to ensure the expected changes took place.\n *\n * @param _asset Address of the Balancer pool asset\n * @param _amount Amount of the Balancer pool asset\n * @return bptExpected of BPT expected in exchange for the asset\n *\n * @dev\n * bptAssetPrice = 1e18 (asset peg) * pool_asset_rate\n *\n * bptExpected = bptAssetPrice * asset_amount / BPT_token_rate\n *\n * bptExpected = 1e18 (asset peg) * pool_asset_rate * asset_amount / BPT_token_rate\n * bptExpected = asset_amount * pool_asset_rate / BPT_token_rate\n *\n * further information available here:\n * https://www.notion.so/originprotocol/Balancer-OETH-strategy-9becdea132704e588782a919d7d471eb?pvs=4#ce01495ae70346d8971f5dced809fb83\n */\n /* solhint-enable max-line-length */\n function _getBPTExpected(address _asset, uint256 _amount)\n internal\n view\n virtual\n returns (uint256 bptExpected)\n {\n uint256 bptRate = IRateProvider(platformAddress).getRate();\n uint256 poolAssetRate = _getRateProviderRate(_asset);\n bptExpected = _amount.mulTruncate(poolAssetRate).divPrecisely(bptRate);\n }\n\n function _getBPTExpected(\n address[] memory _assets,\n uint256[] memory _amounts\n ) internal view virtual returns (uint256 bptExpected) {\n require(_assets.length == _amounts.length, \"Assets & amounts mismatch\");\n\n for (uint256 i = 0; i < _assets.length; ++i) {\n uint256 poolAssetRate = _getRateProviderRate(_assets[i]);\n // convert asset amount to ETH amount\n bptExpected += _amounts[i].mulTruncate(poolAssetRate);\n }\n\n uint256 bptRate = IRateProvider(platformAddress).getRate();\n // Convert ETH amount to BPT amount\n bptExpected = bptExpected.divPrecisely(bptRate);\n }\n\n function _lpDepositAll() internal virtual;\n\n function _lpWithdraw(uint256 numBPTTokens) internal virtual;\n\n function _lpWithdrawAll() internal virtual;\n\n /**\n * @notice Balancer returns assets and rateProviders for corresponding assets ordered\n * by numerical order.\n */\n function _getPoolAssets() internal view returns (IERC20[] memory assets) {\n // slither-disable-next-line unused-return\n (assets, , ) = balancerVault.getPoolTokens(balancerPoolId);\n }\n\n /**\n * @dev If an asset is rebasing the Balancer pools have a wrapped versions of assets\n * that the strategy supports. This function converts the pool(wrapped) asset\n * and corresponding amount to strategy asset.\n */\n function _toPoolAsset(address asset, uint256 amount)\n internal\n view\n returns (address poolAsset, uint256 poolAmount)\n {\n if (asset == stETH) {\n poolAsset = wstETH;\n if (amount > 0) {\n poolAmount = IWstETH(wstETH).getWstETHByStETH(amount);\n }\n } else if (asset == frxETH) {\n poolAsset = sfrxETH;\n if (amount > 0) {\n poolAmount = IERC4626(sfrxETH).convertToShares(amount);\n }\n } else {\n poolAsset = asset;\n poolAmount = amount;\n }\n }\n\n /**\n * @dev Converts a Vault collateral asset to a Balancer pool asset.\n * stETH becomes wstETH, frxETH becomes sfrxETH and everything else stays the same.\n * @param asset Address of the Vault collateral asset.\n * @return Address of the Balancer pool asset.\n */\n function _toPoolAsset(address asset) internal view returns (address) {\n if (asset == stETH) {\n return wstETH;\n } else if (asset == frxETH) {\n return sfrxETH;\n }\n return asset;\n }\n\n /**\n * @dev Converts rebasing asset to its wrapped counterpart.\n */\n function _wrapPoolAsset(address asset, uint256 amount)\n internal\n returns (address wrappedAsset, uint256 wrappedAmount)\n {\n if (asset == stETH) {\n wrappedAsset = wstETH;\n if (amount > 0) {\n wrappedAmount = IWstETH(wstETH).wrap(amount);\n }\n } else if (asset == frxETH) {\n wrappedAsset = sfrxETH;\n if (amount > 0) {\n wrappedAmount = IERC4626(sfrxETH).deposit(\n amount,\n address(this)\n );\n }\n } else {\n wrappedAsset = asset;\n wrappedAmount = amount;\n }\n }\n\n /**\n * @dev Converts wrapped asset to its rebasing counterpart.\n */\n function _unwrapPoolAsset(address asset, uint256 amount)\n internal\n returns (uint256 unwrappedAmount)\n {\n if (asset == stETH) {\n unwrappedAmount = IWstETH(wstETH).unwrap(amount);\n } else if (asset == frxETH) {\n unwrappedAmount = IERC4626(sfrxETH).withdraw(\n amount,\n address(this),\n address(this)\n );\n } else {\n unwrappedAmount = amount;\n }\n }\n\n /**\n * @dev If an asset is rebasing the Balancer pools have a wrapped versions of assets\n * that the strategy supports. This function converts the rebasing strategy asset\n * and corresponding amount to wrapped(pool) asset.\n */\n function _fromPoolAsset(address poolAsset, uint256 poolAmount)\n internal\n view\n returns (address asset, uint256 amount)\n {\n if (poolAsset == wstETH) {\n asset = stETH;\n if (poolAmount > 0) {\n amount = IWstETH(wstETH).getStETHByWstETH(poolAmount);\n }\n } else if (poolAsset == sfrxETH) {\n asset = frxETH;\n if (poolAmount > 0) {\n amount = IERC4626(sfrxETH).convertToAssets(poolAmount);\n }\n } else {\n asset = poolAsset;\n amount = poolAmount;\n }\n }\n\n function _fromPoolAsset(address poolAsset)\n internal\n view\n returns (address asset)\n {\n if (poolAsset == wstETH) {\n asset = stETH;\n } else if (poolAsset == sfrxETH) {\n asset = frxETH;\n } else {\n asset = poolAsset;\n }\n }\n\n /**\n * @notice Sets max withdrawal deviation that is considered when removing\n * liquidity from Balancer pools.\n * @param _maxWithdrawalDeviation Max withdrawal deviation denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxWithdrawalDeviation will be 1% (1e16) for production\n * usage. Vault value checker in combination with checkBalance will\n * catch any unexpected manipulation.\n */\n function setMaxWithdrawalDeviation(uint256 _maxWithdrawalDeviation)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(\n _maxWithdrawalDeviation <= 1e18,\n \"Withdrawal dev. out of bounds\"\n );\n emit MaxWithdrawalDeviationUpdated(\n maxWithdrawalDeviation,\n _maxWithdrawalDeviation\n );\n maxWithdrawalDeviation = _maxWithdrawalDeviation;\n }\n\n /**\n * @notice Sets max deposit deviation that is considered when adding\n * liquidity to Balancer pools.\n * @param _maxDepositDeviation Max deposit deviation denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxDepositDeviation will default to 1% (1e16)\n * for production usage. Vault value checker in combination with\n * checkBalance will catch any unexpected manipulation.\n */\n function setMaxDepositDeviation(uint256 _maxDepositDeviation)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(_maxDepositDeviation <= 1e18, \"Deposit dev. out of bounds\");\n emit MaxDepositDeviationUpdated(\n maxDepositDeviation,\n _maxDepositDeviation\n );\n maxDepositDeviation = _maxDepositDeviation;\n }\n\n function _approveBase() internal virtual {\n IERC20 pToken = IERC20(platformAddress);\n // Balancer vault for BPT token (required for removing liquidity)\n pToken.safeApprove(address(balancerVault), type(uint256).max);\n }\n\n function _getRateProviderRate(address _asset)\n internal\n view\n virtual\n returns (uint256);\n}\n" + }, + "contracts/strategies/balancer/BalancerMetaPoolStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Balancer MetaStablePool Strategy\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { AbstractAuraStrategy, AbstractBalancerStrategy } from \"./AbstractAuraStrategy.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\nimport { IRateProvider } from \"../../interfaces/balancer/IRateProvider.sol\";\nimport { IMetaStablePool } from \"../../interfaces/balancer/IMetaStablePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\ncontract BalancerMetaPoolStrategy is AbstractAuraStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(\n BaseStrategyConfig memory _stratConfig,\n BaseBalancerConfig memory _balancerConfig,\n address _auraRewardPoolAddress\n )\n InitializableAbstractStrategy(_stratConfig)\n AbstractBalancerStrategy(_balancerConfig)\n AbstractAuraStrategy(_auraRewardPoolAddress)\n {}\n\n /**\n * @notice There are no plans to configure BalancerMetaPool as a default\n * asset strategy. For that reason there is no need to support this\n * functionality.\n */\n function deposit(address, uint256)\n external\n override\n onlyVault\n nonReentrant\n {\n revert(\"Not supported\");\n }\n\n /**\n * @notice There are no plans to configure BalancerMetaPool as a default\n * asset strategy. For that reason there is no need to support this\n * functionality.\n */\n function deposit(address[] calldata, uint256[] calldata)\n external\n onlyVault\n nonReentrant\n {\n revert(\"Not supported\");\n }\n\n /**\n * @notice Deposits all supported assets in this strategy contract to the Balancer pool.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 assetsLength = assetsMapped.length;\n address[] memory strategyAssets = new address[](assetsLength);\n uint256[] memory strategyAmounts = new uint256[](assetsLength);\n\n // For each vault collateral asset\n for (uint256 i = 0; i < assetsLength; ++i) {\n strategyAssets[i] = assetsMapped[i];\n // Get the asset balance in this strategy contract\n strategyAmounts[i] = IERC20(strategyAssets[i]).balanceOf(\n address(this)\n );\n }\n _deposit(strategyAssets, strategyAmounts);\n }\n\n /*\n * _deposit doesn't require a read-only re-entrancy protection since during the deposit\n * the function enters the Balancer Vault Context. If this function were called as part of\n * the attacking contract (while intercepting execution flow upon receiving ETH) the read-only\n * protection of the Balancer Vault would be triggered. Since the attacking contract would\n * already be in the Balancer Vault context and wouldn't be able to enter it again.\n */\n function _deposit(\n address[] memory _strategyAssets,\n uint256[] memory _strategyAmounts\n ) internal {\n require(\n _strategyAssets.length == _strategyAmounts.length,\n \"Array length missmatch\"\n );\n\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n\n uint256[] memory strategyAssetAmountsToPoolAssetAmounts = new uint256[](\n _strategyAssets.length\n );\n address[] memory strategyAssetsToPoolAssets = new address[](\n _strategyAssets.length\n );\n\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n address strategyAsset = _strategyAssets[i];\n uint256 strategyAmount = _strategyAmounts[i];\n\n require(\n assetToPToken[strategyAsset] != address(0),\n \"Unsupported asset\"\n );\n strategyAssetsToPoolAssets[i] = _toPoolAsset(strategyAsset);\n\n if (strategyAmount > 0) {\n emit Deposit(strategyAsset, platformAddress, strategyAmount);\n\n // wrap rebasing assets like stETH and frxETH to wstETH and sfrxETH\n (, strategyAssetAmountsToPoolAssetAmounts[i]) = _wrapPoolAsset(\n strategyAsset,\n strategyAmount\n );\n }\n }\n\n uint256[] memory amountsIn = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n for (uint256 i = 0; i < tokens.length; ++i) {\n // Convert IERC20 type to address\n poolAssets[i] = address(tokens[i]);\n\n // For each of the mapped assets\n for (uint256 j = 0; j < strategyAssetsToPoolAssets.length; ++j) {\n // If the pool asset is the same as the mapped asset\n if (poolAssets[i] == strategyAssetsToPoolAssets[j]) {\n amountsIn[i] = strategyAssetAmountsToPoolAssetAmounts[j];\n }\n }\n }\n\n uint256 minBPT = _getBPTExpected(\n strategyAssetsToPoolAssets,\n strategyAssetAmountsToPoolAssetAmounts\n );\n uint256 minBPTwDeviation = minBPT.mulTruncate(\n 1e18 - maxDepositDeviation\n );\n\n /* EXACT_TOKENS_IN_FOR_BPT_OUT:\n * User sends precise quantities of tokens, and receives an\n * estimated but unknown (computed at run time) quantity of BPT.\n *\n * ['uint256', 'uint256[]', 'uint256']\n * [EXACT_TOKENS_IN_FOR_BPT_OUT, amountsIn, minimumBPT]\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,\n amountsIn,\n minBPTwDeviation\n );\n\n IBalancerVault.JoinPoolRequest memory request = IBalancerVault\n .JoinPoolRequest(poolAssets, amountsIn, userData, false);\n\n // Add the pool assets in this strategy to the balancer pool\n balancerVault.joinPool(\n balancerPoolId,\n address(this),\n address(this),\n request\n );\n\n // Deposit the Balancer Pool Tokens (BPT) into Aura\n _lpDepositAll();\n }\n\n /**\n * @notice Withdraw a Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAsset Address of the Vault collateral asset\n * @param _strategyAmount The amount of Vault collateral assets to withdraw\n */\n function withdraw(\n address _recipient,\n address _strategyAsset,\n uint256 _strategyAmount\n ) external override onlyVault nonReentrant {\n address[] memory strategyAssets = new address[](1);\n uint256[] memory strategyAmounts = new uint256[](1);\n strategyAssets[0] = _strategyAsset;\n strategyAmounts[0] = _strategyAmount;\n\n _withdraw(_recipient, strategyAssets, strategyAmounts);\n }\n\n /**\n * @notice Withdraw multiple Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAssets Addresses of the Vault collateral assets\n * @param _strategyAmounts The amounts of Vault collateral assets to withdraw\n */\n function withdraw(\n address _recipient,\n address[] calldata _strategyAssets,\n uint256[] calldata _strategyAmounts\n ) external onlyVault nonReentrant {\n _withdraw(_recipient, _strategyAssets, _strategyAmounts);\n }\n\n /**\n * @dev Withdraw multiple Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAssets Addresses of the Vault collateral assets\n * @param _strategyAmounts The amounts of Vault collateral assets to withdraw\n *\n * _withdrawal doesn't require a read-only re-entrancy protection since during the withdrawal\n * the function enters the Balancer Vault Context. If this function were called as part of\n * the attacking contract (while intercepting execution flow upon receiving ETH) the read-only\n * protection of the Balancer Vault would be triggered. Since the attacking contract would\n * already be in the Balancer Vault context and wouldn't be able to enter it again.\n */\n function _withdraw(\n address _recipient,\n address[] memory _strategyAssets,\n uint256[] memory _strategyAmounts\n ) internal {\n require(\n _strategyAssets.length == _strategyAmounts.length,\n \"Invalid input arrays\"\n );\n\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n require(\n assetToPToken[_strategyAssets[i]] != address(0),\n \"Unsupported asset\"\n );\n }\n\n // STEP 1 - Calculate the Balancer pool assets and amounts from the vault collateral assets\n\n // Get all the supported balancer pool assets\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n // Calculate the balancer pool assets and amounts to withdraw\n uint256[] memory poolAssetsAmountsOut = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n // Is the wrapped asset amount indexed by the assets array, not the order of the Balancer pool tokens\n // eg wstETH and sfrxETH amounts, not the stETH and frxETH amounts\n uint256[] memory strategyAssetsToPoolAssetsAmounts = new uint256[](\n _strategyAssets.length\n );\n\n // For each of the Balancer pool assets\n for (uint256 i = 0; i < tokens.length; ++i) {\n poolAssets[i] = address(tokens[i]);\n\n // Convert the Balancer pool asset back to a vault collateral asset\n address strategyAsset = _fromPoolAsset(poolAssets[i]);\n\n // for each of the vault assets\n for (uint256 j = 0; j < _strategyAssets.length; ++j) {\n // If the vault asset equals the vault asset mapped from the Balancer pool asset\n if (_strategyAssets[j] == strategyAsset) {\n (, poolAssetsAmountsOut[i]) = _toPoolAsset(\n strategyAsset,\n _strategyAmounts[j]\n );\n strategyAssetsToPoolAssetsAmounts[j] = poolAssetsAmountsOut[\n i\n ];\n\n /* Because of the potential Balancer rounding error mentioned below\n * the contract might receive 1-2 WEI smaller amount than required\n * in the withdraw user data encoding. If slightly lesser token amount\n * is received the strategy can not unwrap the pool asset as it is\n * smaller than expected.\n *\n * For that reason we `overshoot` the required tokens expected to\n * circumvent the error\n */\n if (poolAssetsAmountsOut[i] > 0) {\n poolAssetsAmountsOut[i] += 2;\n }\n }\n }\n }\n\n // STEP 2 - Calculate the max about of Balancer Pool Tokens (BPT) to withdraw\n\n // Estimate the required amount of Balancer Pool Tokens (BPT) for the assets\n uint256 maxBPTtoWithdraw = _getBPTExpected(\n poolAssets,\n /* all non 0 values are overshot by 2 WEI and with the expected mainnet\n * ~1% withdrawal deviation, the 2 WEI aren't important\n */\n poolAssetsAmountsOut\n );\n // Increase BPTs by the max allowed deviation\n // Any excess BPTs will be left in this strategy contract\n maxBPTtoWithdraw = maxBPTtoWithdraw.mulTruncate(\n 1e18 + maxWithdrawalDeviation\n );\n\n // STEP 3 - Withdraw the Balancer Pool Tokens (BPT) from Aura to this strategy contract\n\n // Withdraw BPT from Aura allowing for BPTs left in this strategy contract from previous withdrawals\n _lpWithdraw(\n maxBPTtoWithdraw - IERC20(platformAddress).balanceOf(address(this))\n );\n\n // STEP 4 - Withdraw the balancer pool assets from the pool\n\n /* Custom asset exit: BPT_IN_FOR_EXACT_TOKENS_OUT:\n * User sends an estimated but unknown (computed at run time) quantity of BPT,\n * and receives precise quantities of specified tokens.\n *\n * ['uint256', 'uint256[]', 'uint256']\n * [BPT_IN_FOR_EXACT_TOKENS_OUT, amountsOut, maxBPTAmountIn]\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.BPT_IN_FOR_EXACT_TOKENS_OUT,\n poolAssetsAmountsOut,\n maxBPTtoWithdraw\n );\n\n IBalancerVault.ExitPoolRequest memory request = IBalancerVault\n .ExitPoolRequest(\n poolAssets,\n /* We specify the exact amount of a tokens we are expecting in the encoded\n * userData, for that reason we don't need to specify the amountsOut here.\n *\n * Also Balancer has a rounding issue that can make a transaction fail:\n * https://github.com/balancer/balancer-v2-monorepo/issues/2541\n * which is an extra reason why this field is empty.\n */\n new uint256[](tokens.length),\n userData,\n false\n );\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n /* Payable keyword is required because of the IBalancerVault interface even though\n * this strategy shall never be receiving native ETH\n */\n payable(address(this)),\n request\n );\n\n // STEP 5 - Re-deposit any left over BPT tokens back into Aura\n /* When concluding how much of BPT we need to withdraw from Aura we overshoot by\n * roughly around 1% (initial mainnet setting of maxWithdrawalDeviation). After exiting\n * the pool strategy could have left over BPT tokens that are not earning boosted yield.\n * We re-deploy those back in.\n */\n _lpDepositAll();\n\n // STEP 6 - Unswap balancer pool assets to vault collateral assets and send to the vault.\n\n // For each of the specified assets\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n // Unwrap assets like wstETH and sfrxETH to rebasing assets stETH and frxETH\n if (strategyAssetsToPoolAssetsAmounts[i] > 0) {\n _unwrapPoolAsset(\n _strategyAssets[i],\n strategyAssetsToPoolAssetsAmounts[i]\n );\n }\n\n // Transfer the vault collateral assets to the recipient, which is typically the vault\n if (_strategyAmounts[i] > 0) {\n IERC20(_strategyAssets[i]).safeTransfer(\n _recipient,\n _strategyAmounts[i]\n );\n\n emit Withdrawal(\n _strategyAssets[i],\n platformAddress,\n _strategyAmounts[i]\n );\n }\n }\n }\n\n /**\n * @notice Withdraws all supported Vault collateral assets from the Balancer pool\n * and send to the OToken's Vault.\n *\n * Is only executable by the OToken's Vault or the Governor.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n // STEP 1 - Withdraw all Balancer Pool Tokens (BPT) from Aura to this strategy contract\n\n _lpWithdrawAll();\n // Get the BPTs withdrawn from Aura plus any that were already in this strategy contract\n uint256 BPTtoWithdraw = IERC20(platformAddress).balanceOf(\n address(this)\n );\n // Get the balancer pool assets and their total balances\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n uint256[] memory minAmountsOut = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n for (uint256 i = 0; i < tokens.length; ++i) {\n poolAssets[i] = address(tokens[i]);\n }\n\n // STEP 2 - Withdraw the Balancer pool assets from the pool\n /* Proportional exit: EXACT_BPT_IN_FOR_TOKENS_OUT:\n * User sends a precise quantity of BPT, and receives an estimated but unknown\n * (computed at run time) quantity of a single token\n *\n * ['uint256', 'uint256']\n * [EXACT_BPT_IN_FOR_TOKENS_OUT, bptAmountIn]\n *\n * It is ok to pass an empty minAmountsOut since tilting the pool in any direction\n * when doing a proportional exit can only be beneficial to the strategy. Since\n * it will receive more of the underlying tokens for the BPT traded in.\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.EXACT_BPT_IN_FOR_TOKENS_OUT,\n BPTtoWithdraw\n );\n\n IBalancerVault.ExitPoolRequest memory request = IBalancerVault\n .ExitPoolRequest(poolAssets, minAmountsOut, userData, false);\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n /* Payable keyword is required because of the IBalancerVault interface even though\n * this strategy shall never be receiving native ETH\n */\n payable(address(this)),\n request\n );\n\n // STEP 3 - Convert the balancer pool assets to the vault collateral assets and send to the vault\n // For each of the Balancer pool assets\n for (uint256 i = 0; i < tokens.length; ++i) {\n address poolAsset = address(tokens[i]);\n // Convert the balancer pool asset to the strategy asset\n address strategyAsset = _fromPoolAsset(poolAsset);\n // Get the balancer pool assets withdraw from the pool plus any that were already in this strategy contract\n uint256 poolAssetAmount = IERC20(poolAsset).balanceOf(\n address(this)\n );\n\n // Unwrap assets like wstETH and sfrxETH to rebasing assets stETH and frxETH\n uint256 unwrappedAmount = 0;\n if (poolAssetAmount > 0) {\n unwrappedAmount = _unwrapPoolAsset(\n strategyAsset,\n poolAssetAmount\n );\n }\n\n // Transfer the vault collateral assets to the vault\n if (unwrappedAmount > 0) {\n IERC20(strategyAsset).safeTransfer(\n vaultAddress,\n unwrappedAmount\n );\n emit Withdrawal(\n strategyAsset,\n platformAddress,\n unwrappedAmount\n );\n }\n }\n }\n\n /**\n * @notice Approves the Balancer Vault to transfer poolAsset counterparts\n * of all of the supported assets from this strategy. E.g. stETH is a supported\n * strategy and Balancer Vault gets unlimited approval to transfer wstETH.\n *\n * If Balancer pool uses a wrapped version of a supported asset then also approve\n * unlimited usage of an asset to the contract responsible for wrapping.\n *\n * Approve unlimited spending by Balancer Vault and Aura reward pool of the\n * pool BPT tokens.\n *\n * Is only executable by the Governor.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n _abstractSetPToken(assetsMapped[i], platformAddress);\n }\n _approveBase();\n }\n\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address) internal override {\n address poolAsset = _toPoolAsset(_asset);\n if (_asset == stETH) {\n // slither-disable-next-line unused-return\n IERC20(stETH).approve(wstETH, type(uint256).max);\n } else if (_asset == frxETH) {\n // slither-disable-next-line unused-return\n IERC20(frxETH).approve(sfrxETH, type(uint256).max);\n }\n _approveAsset(poolAsset);\n }\n\n /**\n * @dev Approves the Balancer Vault to transfer an asset from\n * this strategy. The assets could be a Vault collateral asset\n * like WETH or rETH; or a Balancer pool asset that wraps the vault asset\n * like wstETH or sfrxETH.\n */\n function _approveAsset(address _asset) internal {\n IERC20 asset = IERC20(_asset);\n // slither-disable-next-line unused-return\n asset.approve(address(balancerVault), type(uint256).max);\n }\n\n /**\n * @notice Returns the rate supplied by the Balancer configured rate\n * provider. Rate is used to normalize the token to common underlying\n * pool denominator. (ETH for ETH Liquid staking derivatives)\n *\n * @param _asset Address of the Balancer pool asset\n * @return rate of the corresponding asset\n */\n function _getRateProviderRate(address _asset)\n internal\n view\n override\n returns (uint256)\n {\n IMetaStablePool pool = IMetaStablePool(platformAddress);\n IRateProvider[] memory providers = pool.getRateProviders();\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n\n uint256 providersLength = providers.length;\n for (uint256 i = 0; i < providersLength; ++i) {\n // _assets and corresponding rate providers are all in the same order\n if (address(tokens[i]) == _asset) {\n // rate provider doesn't exist, defaults to 1e18\n if (address(providers[i]) == address(0)) {\n return 1e18;\n }\n return providers[i].getRate();\n }\n }\n\n // should never happen\n assert(false);\n }\n}\n" + }, + "contracts/strategies/balancer/VaultReentrancyLib.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity >=0.7.0 <0.9.0;\n\nimport \"../../utils/BalancerErrors.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\n\nlibrary VaultReentrancyLib {\n /**\n * @dev Ensure we are not in a Vault context when this function is called, by attempting a no-op internal\n * balance operation. If we are already in a Vault transaction (e.g., a swap, join, or exit), the Vault's\n * reentrancy protection will cause this function to revert.\n *\n * The exact function call doesn't really matter: we're just trying to trigger the Vault reentrancy check\n * (and not hurt anything in case it works). An empty operation array with no specific operation at all works\n * for that purpose, and is also the least expensive in terms of gas and bytecode size.\n *\n * Call this at the top of any function that can cause a state change in a pool and is either public itself,\n * or called by a public function *outside* a Vault operation (e.g., join, exit, or swap).\n *\n * If this is *not* called in functions that are vulnerable to the read-only reentrancy issue described\n * here (https://forum.balancer.fi/t/reentrancy-vulnerability-scope-expanded/4345), those functions are unsafe,\n * and subject to manipulation that may result in loss of funds.\n */\n function ensureNotInVaultContext(IBalancerVault vault) internal view {\n // Perform the following operation to trigger the Vault's reentrancy guard:\n //\n // IBalancerVault.UserBalanceOp[] memory noop = new IBalancerVault.UserBalanceOp[](0);\n // _vault.manageUserBalance(noop);\n //\n // However, use a static call so that it can be a view function (even though the function is non-view).\n // This allows the library to be used more widely, as some functions that need to be protected might be\n // view.\n //\n // This staticcall always reverts, but we need to make sure it doesn't fail due to a re-entrancy attack.\n // Staticcalls consume all gas forwarded to them on a revert caused by storage modification.\n // By default, almost the entire available gas is forwarded to the staticcall,\n // causing the entire call to revert with an 'out of gas' error.\n //\n // We set the gas limit to 10k for the staticcall to\n // avoid wasting gas when it reverts due to storage modification.\n // `manageUserBalance` is a non-reentrant function in the Vault, so calling it invokes `_enterNonReentrant`\n // in the `ReentrancyGuard` contract, reproduced here:\n //\n // function _enterNonReentrant() private {\n // // If the Vault is actually being reentered, it will revert in the first line, at the `_require` that\n // // checks the reentrancy flag, with \"BAL#400\" (corresponding to Errors.REENTRANCY) in the revertData.\n // // The full revertData will be: `abi.encodeWithSignature(\"Error(string)\", \"BAL#400\")`.\n // _require(_status != _ENTERED, Errors.REENTRANCY);\n //\n // // If the Vault is not being reentered, the check above will pass: but it will *still* revert,\n // // because the next line attempts to modify storage during a staticcall. However, this type of\n // // failure results in empty revertData.\n // _status = _ENTERED;\n // }\n //\n // So based on this analysis, there are only two possible revertData values: empty, or abi.encoded BAL#400.\n //\n // It is of course much more bytecode and gas efficient to check for zero-length revertData than to compare it\n // to the encoded REENTRANCY revertData.\n //\n // While it should be impossible for the call to fail in any other way (especially since it reverts before\n // `manageUserBalance` even gets called), any other error would generate non-zero revertData, so checking for\n // empty data guards against this case too.\n\n (, bytes memory revertData) = address(vault).staticcall{ gas: 10_000 }(\n abi.encodeWithSelector(vault.manageUserBalance.selector, 0)\n );\n\n _require(revertData.length == 0, Errors.REENTRANCY);\n }\n}\n" + }, + "contracts/strategies/BaseCurveAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for the Curve OETH/WETH pool\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { ICurveStableSwapNG } from \"../interfaces/ICurveStableSwapNG.sol\";\nimport { ICurveXChainLiquidityGauge } from \"../interfaces/ICurveXChainLiquidityGauge.sol\";\nimport { IChildLiquidityGaugeFactory } from \"../interfaces/IChildLiquidityGaugeFactory.sol\";\n\ncontract BaseCurveAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @dev a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n // New immutable variables that must be set in the constructor\n /**\n * @notice Address of the Wrapped ETH (WETH) contract.\n */\n IWETH9 public immutable weth;\n\n /**\n * @notice Address of the OETH token contract.\n */\n IERC20 public immutable oeth;\n\n /**\n * @notice Address of the LP (Liquidity Provider) token contract.\n */\n IERC20 public immutable lpToken;\n\n /**\n * @notice Address of the Curve StableSwap NG pool contract.\n */\n ICurveStableSwapNG public immutable curvePool;\n\n /**\n * @notice Address of the Curve X-Chain Liquidity Gauge contract.\n */\n ICurveXChainLiquidityGauge public immutable gauge;\n\n /**\n * @notice Address of the Child Liquidity Gauge Factory contract.\n */\n IChildLiquidityGaugeFactory public immutable gaugeFactory;\n\n // Ordered list of pool assets\n uint128 public immutable oethCoinIndex;\n uint128 public immutable wethCoinIndex;\n\n /**\n * @notice Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n uint256 public maxSlippage;\n\n event MaxSlippageUpdated(uint256 newMaxSlippage);\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balancesBefore = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffBefore = balancesBefore[wethCoinIndex].toInt256() -\n balancesBefore[oethCoinIndex].toInt256();\n\n _;\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balancesAfter = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffAfter = balancesAfter[wethCoinIndex].toInt256() -\n balancesAfter[oethCoinIndex].toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of ETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _oeth,\n address _weth,\n address _gauge,\n address _gaugeFactory,\n uint128 _oethCoinIndex,\n uint128 _wethCoinIndex\n ) InitializableAbstractStrategy(_baseConfig) {\n oethCoinIndex = _oethCoinIndex;\n wethCoinIndex = _wethCoinIndex;\n\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveStableSwapNG(_baseConfig.platformAddress);\n\n oeth = IERC20(_oeth);\n weth = IWETH9(_weth);\n gauge = ICurveXChainLiquidityGauge(_gauge);\n gaugeFactory = IChildLiquidityGaugeFactory(_gaugeFactory);\n\n _setGovernor(address(0));\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV\n * @param _maxSlippage Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV\n uint256 _maxSlippage\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n address[] memory _assets = new address[](1);\n _assets[0] = address(weth);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n _setMaxSlippage(_maxSlippage);\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit WETH into the Curve pool\n * @param _weth Address of Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to deposit.\n */\n function deposit(address _weth, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_weth, _amount);\n }\n\n function _deposit(address _weth, uint256 _wethAmount) internal {\n require(_wethAmount > 0, \"Must deposit something\");\n require(_weth == address(weth), \"Can only deposit WETH\");\n\n emit Deposit(_weth, address(lpToken), _wethAmount);\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 oethToAdd = uint256(\n _max(\n 0,\n balances[wethCoinIndex].toInt256() +\n _wethAmount.toInt256() -\n balances[oethCoinIndex].toInt256()\n )\n );\n\n /* Add so much OETH so that the pool ends up being balanced. And at minimum\n * add as much OETH as WETH and at maximum twice as much OETH.\n */\n oethToAdd = Math.max(oethToAdd, _wethAmount);\n oethToAdd = Math.min(oethToAdd, _wethAmount * 2);\n\n /* Mint OETH with a strategy that attempts to contribute to stability of OETH/WETH pool. Try\n * to mint so much OETH that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OETH minted will always be at least equal or greater\n * to WETH amount deployed. And never larger than twice the WETH amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(oethToAdd);\n\n emit Deposit(address(oeth), address(lpToken), oethToAdd);\n\n uint256[] memory _amounts = new uint256[](2);\n _amounts[wethCoinIndex] = _wethAmount;\n _amounts[oethCoinIndex] = oethToAdd;\n\n uint256 valueInLpTokens = (_wethAmount + oethToAdd).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Do the deposit to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(_amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool's LP tokens into the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n /**\n * @notice Deposit the strategy's entire balance of WETH into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = weth.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(weth), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _weth Address of the Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to withdraw.\n */\n function withdraw(\n address _recipient,\n address _weth,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_weth == address(weth), \"Can only withdraw WETH\");\n\n emit Withdrawal(_weth, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(_amount);\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough WETH on balanced removal\n */\n uint256[] memory _minWithdrawalAmounts = new uint256[](2);\n _minWithdrawalAmounts[wethCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OETH and any that was left in the strategy\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n\n // Transfer WETH to the recipient\n require(\n weth.transfer(_recipient, _amount),\n \"Transfer of WETH not successful\"\n );\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n function calcTokenToBurn(uint256 _wethAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much WETH\n * we want we can determine how much of OETH we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolWETHBalance = curvePool.balances(wethCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolWETHBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_wethAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = gauge.balanceOf(address(this));\n // Can not withdraw zero LP tokens from the gauge\n if (gaugeTokens == 0) return;\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[] memory minWithdrawAmounts = new uint256[](2);\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(\n lpToken.balanceOf(address(this)),\n minWithdrawAmounts\n );\n\n // Burn all OETH\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Get the strategy contract's WETH balance.\n // This includes all that was removed from the Curve pool and\n // any ether that was sitting in the strategy contract before the removal.\n uint256 ethBalance = weth.balanceOf(address(this));\n require(\n weth.transfer(vaultAddress, ethBalance),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethBalance);\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[] memory amounts = new uint256[](2);\n amounts[oethCoinIndex] = _oTokens;\n\n // Convert OETH to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool LP tokens to the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Deposit(address(oeth), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough ETH.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove OTokens from the Curve pool\n uint256 oethToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n oethCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /**\n * @notice One-sided remove of ETH from the Curve pool, convert to WETH\n * and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for ETH.\n * @dev Curve pool LP tokens is used rather than WETH assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of ETH. Curve's `calc_token_amount` functioun does not include fees.\n * A 3rd party libary can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * caclulate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Curve gauge and remove ETH from the Curve pool\n uint256 ethAmount = _withdrawAndRemoveFromPool(\n _lpTokens,\n wethCoinIndex\n );\n\n // Transfer WETH to the vault\n require(\n weth.transfer(vaultAddress, ethAmount),\n \"Transfer of WETH not successful\"\n );\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(weth), address(lpToken), ethAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the Convex pool and\n * do a one-sided remove of ETH or OETH from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the Convex pool.\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = ETH, 1 = OETH.\n * @return coinsRemoved The amount of ETH or OETH removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Curve gauge\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to ETH value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n // Apply slippage to ETH value\n uint256 minAmount = valueInEth.mulTruncate(uint256(1e18) - maxSlippage);\n\n // Remove just the ETH from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethbSupply = oeth.totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethbSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // CRV rewards flow.\n //---\n // CRV inflation:\n // Gauge receive CRV rewards from inflation.\n // Each checkpoint on the gauge send this CRV inflation to gauge factory.\n // This strategy should call mint on the gauge factory to collect the CRV rewards.\n // ---\n // Extra rewards:\n // Calling claim_rewards on the gauge will only claim extra rewards (outside of CRV).\n // ---\n\n // Mint CRV on Child Liquidity gauge factory\n gaugeFactory.mint(address(gauge));\n // Collect extra gauge rewards (outside of CRV)\n gauge.claim_rewards();\n\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _lpAmount) internal {\n // withdraw lp tokens from the gauge without claiming rewards\n gauge.withdraw(_lpAmount);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // WETH balance needed here for the balance check that happens from vault during depositing.\n balance = weth.balanceOf(address(this));\n uint256 lpTokens = gauge.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += (lpTokens * curvePool.get_virtual_price()) / 1e18;\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(weth);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Sets the maximum slippage allowed for any swap/liquidity operation\n * @param _maxSlippage Maximum slippage allowed, 1e18 = 100%.\n */\n function setMaxSlippage(uint256 _maxSlippage) external onlyGovernor {\n _setMaxSlippage(_maxSlippage);\n }\n\n function _setMaxSlippage(uint256 _maxSlippage) internal {\n require(_maxSlippage <= 5e16, \"Slippage must be less than 100%\");\n maxSlippage = _maxSlippage;\n emit MaxSlippageUpdated(_maxSlippage);\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OETH (required for adding liquidity)\n // slither-disable-next-line unused-return\n oeth.approve(platformAddress, type(uint256).max);\n\n // Approve Curve pool for WETH (required for adding liquidity)\n // slither-disable-next-line unused-return\n weth.approve(platformAddress, type(uint256).max);\n\n // Approve Curve gauge contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Curve gauge.\n // slither-disable-next-line unused-return\n lpToken.approve(address(gauge), type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/BridgedWOETHStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, SafeERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { AggregatorV3Interface } from \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\n\ncontract BridgedWOETHStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using StableMath for uint128;\n using SafeCast for uint256;\n using SafeERC20 for IERC20;\n\n event MaxPriceDiffBpsUpdated(uint128 oldValue, uint128 newValue);\n event WOETHPriceUpdated(uint128 oldValue, uint128 newValue);\n\n IWETH9 public immutable weth;\n IERC20 public immutable bridgedWOETH;\n IERC20 public immutable oethb;\n\n uint256 public constant MAX_PRICE_STALENESS = 2 days;\n\n uint128 public lastOraclePrice;\n uint128 public maxPriceDiffBps;\n\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _weth,\n address _bridgedWOETH,\n address _oethb\n ) InitializableAbstractStrategy(_stratConfig) {\n weth = IWETH9(_weth);\n bridgedWOETH = IERC20(_bridgedWOETH);\n oethb = IERC20(_oethb);\n }\n\n function initialize(uint128 _maxPriceDiffBps)\n external\n onlyGovernor\n initializer\n {\n InitializableAbstractStrategy._initialize(\n new address[](0), // No reward tokens\n new address[](0), // No assets\n new address[](0) // No pTokens\n );\n\n _setMaxPriceDiffBps(_maxPriceDiffBps);\n }\n\n /**\n * @dev Sets the max price diff bps for the wOETH value appreciation\n * @param _maxPriceDiffBps Bps value, 10k == 100%\n */\n function setMaxPriceDiffBps(uint128 _maxPriceDiffBps)\n external\n onlyGovernor\n {\n _setMaxPriceDiffBps(_maxPriceDiffBps);\n }\n\n /**\n * @dev Sets the max price diff bps for the wOETH value appreciation\n * @param _maxPriceDiffBps Bps value, 10k == 100%\n */\n function _setMaxPriceDiffBps(uint128 _maxPriceDiffBps) internal {\n require(\n _maxPriceDiffBps > 0 && _maxPriceDiffBps <= 10000,\n \"Invalid bps value\"\n );\n\n emit MaxPriceDiffBpsUpdated(maxPriceDiffBps, _maxPriceDiffBps);\n\n maxPriceDiffBps = _maxPriceDiffBps;\n }\n\n /**\n * @dev Wrapper for _updateWOETHOraclePrice with nonReentrant flag\n * @return The latest price of wOETH from Oracle\n */\n function updateWOETHOraclePrice() external nonReentrant returns (uint256) {\n return _updateWOETHOraclePrice();\n }\n\n /**\n * @dev Finds the value of bridged wOETH from the Oracle.\n * Ensures that it's within the bounds and reasonable.\n * And stores it.\n *\n * NOTE: Intentionally not caching `Vault.priceProvider` here,\n * since doing so would mean that we also have to update this\n * strategy every time there's a change in oracle router.\n * Besides on L2, the gas is considerably cheaper than mainnet.\n *\n * @return Latest price from oracle\n */\n function _updateWOETHOraclePrice() internal returns (uint256) {\n // WETH price per unit of bridged wOETH\n uint256 oraclePrice = IOracle(IVault(vaultAddress).priceProvider())\n .price(address(bridgedWOETH));\n\n // 1 wOETH > 1 WETH, always\n require(oraclePrice > 1 ether, \"Invalid wOETH value\");\n\n uint128 oraclePrice128 = oraclePrice.toUint128();\n\n // Do some checks\n if (lastOraclePrice > 0) {\n // Make sure the value only goes up\n require(oraclePrice128 >= lastOraclePrice, \"Negative wOETH yield\");\n\n // lastOraclePrice * (1 + maxPriceDiffBps)\n uint256 maxPrice = (lastOraclePrice * (1e4 + maxPriceDiffBps)) /\n 1e4;\n\n // And that it's within the bounds.\n require(oraclePrice128 <= maxPrice, \"Price diff beyond threshold\");\n }\n\n emit WOETHPriceUpdated(lastOraclePrice, oraclePrice128);\n\n // Store the price\n lastOraclePrice = oraclePrice128;\n\n return oraclePrice;\n }\n\n /**\n * @dev Computes & returns the value of given wOETH in WETH\n * @param woethAmount Amount of wOETH\n * @return Value of wOETH in WETH (using the last stored oracle price)\n */\n function getBridgedWOETHValue(uint256 woethAmount)\n public\n view\n returns (uint256)\n {\n return (woethAmount * lastOraclePrice) / 1 ether;\n }\n\n /**\n * @dev Takes in bridged wOETH and mints & returns\n * equivalent amount of OETHb.\n * @param woethAmount Amount of bridged wOETH to transfer in\n */\n function depositBridgedWOETH(uint256 woethAmount)\n external\n onlyGovernorOrStrategist\n nonReentrant\n {\n // Update wOETH price\n uint256 oraclePrice = _updateWOETHOraclePrice();\n\n // Figure out how much they are worth\n uint256 oethToMint = (woethAmount * oraclePrice) / 1 ether;\n\n require(oethToMint > 0, \"Invalid deposit amount\");\n\n // There's no pToken, however, it just uses WOETH address in the event\n emit Deposit(address(weth), address(bridgedWOETH), oethToMint);\n\n // Mint OETHb tokens and transfer it to the caller\n IVault(vaultAddress).mintForStrategy(oethToMint);\n\n // Transfer out minted OETHb\n // slither-disable-next-line unchecked-transfer unused-return\n oethb.transfer(msg.sender, oethToMint);\n\n // Transfer in all bridged wOETH tokens\n // slither-disable-next-line unchecked-transfer unused-return\n bridgedWOETH.transferFrom(msg.sender, address(this), woethAmount);\n }\n\n /**\n * @dev Takes in OETHb and burns it and returns\n * equivalent amount of bridged wOETH.\n * @param oethToBurn Amount of OETHb to burn\n */\n function withdrawBridgedWOETH(uint256 oethToBurn)\n external\n onlyGovernorOrStrategist\n nonReentrant\n {\n // Update wOETH price\n uint256 oraclePrice = _updateWOETHOraclePrice();\n\n // Figure out how much they are worth\n uint256 woethAmount = (oethToBurn * 1 ether) / oraclePrice;\n\n require(woethAmount > 0, \"Invalid withdraw amount\");\n\n // There's no pToken, however, it just uses WOETH address in the event\n emit Withdrawal(address(weth), address(bridgedWOETH), oethToBurn);\n\n // Transfer WOETH back\n // slither-disable-next-line unchecked-transfer unused-return\n bridgedWOETH.transfer(msg.sender, woethAmount);\n\n // Transfer in OETHb\n // slither-disable-next-line unchecked-transfer unused-return\n oethb.transferFrom(msg.sender, address(this), oethToBurn);\n\n // Burn OETHb\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n }\n\n /**\n * @notice Returns the amount of backing WETH the strategy holds\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // Figure out how much wOETH is worth at the time.\n // Always uses the last stored oracle price.\n // Call updateWOETHOraclePrice manually to pull in latest yields.\n\n // NOTE: If the contract has been deployed but the call to\n // `updateWOETHOraclePrice()` has never been made, then this\n // will return zero. It should be fine because the strategy\n // should update the price whenever a deposit/withdraw happens.\n\n // If `updateWOETHOraclePrice()` hasn't been called in a while,\n // the strategy will underreport its holdings but never overreport it.\n\n balance =\n (bridgedWOETH.balanceOf(address(this)) * lastOraclePrice) /\n 1 ether;\n }\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n // Strategist deposits bridged wOETH but the contract only\n // reports the balance in WETH. As far as Vault is concerned,\n // it isn't aware of bridged wOETH token\n return _asset == address(weth);\n }\n\n /***************************************\n Overridden methods\n ****************************************/\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function transferToken(address _asset, uint256 _amount)\n public\n override\n onlyGovernor\n {\n require(\n _asset != address(bridgedWOETH) && _asset != address(weth),\n \"Cannot transfer supported asset\"\n );\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice deposit() function not used for this strategy\n */\n function deposit(address, uint256)\n external\n override\n onlyVault\n nonReentrant\n {\n // Use depositBridgedWOETH() instead\n require(false, \"Deposit disabled\");\n }\n\n /**\n * @notice depositAll() function not used for this strategy\n */\n function depositAll() external override onlyVault nonReentrant {\n // Use depositBridgedWOETH() instead\n require(false, \"Deposit disabled\");\n }\n\n /**\n * @notice withdraw() function not used for this strategy\n */\n function withdraw(\n // solhint-disable-next-line no-unused-vars\n address _recipient,\n // solhint-disable-next-line no-unused-vars\n address _asset,\n // solhint-disable-next-line no-unused-vars\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(false, \"Withdrawal disabled\");\n }\n\n /**\n * @notice withdrawAll() function not used for this strategy\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n // Withdrawal disabled\n }\n\n function _abstractSetPToken(address, address) internal override {\n revert(\"No pTokens are used\");\n }\n\n function safeApproveAllTokens() external override {}\n\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function removePToken(uint256) external override {\n revert(\"No pTokens are used\");\n }\n\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function collectRewardTokens() external override {}\n}\n" + }, + "contracts/strategies/CompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Compound Strategy\n * @notice Investment strategy for Compound like lending platforms. eg Compound and Flux\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICERC20 } from \"./ICompound.sol\";\nimport { AbstractCompoundStrategy, InitializableAbstractStrategy } from \"./AbstractCompoundStrategy.sol\";\nimport { IComptroller } from \"../interfaces/IComptroller.sol\";\nimport { IERC20 } from \"../utils/InitializableAbstractStrategy.sol\";\n\ncontract CompoundStrategy is AbstractCompoundStrategy {\n using SafeERC20 for IERC20;\n event SkippedWithdrawal(address asset, uint256 amount);\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @notice initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @notice Collect accumulated COMP and send to Harvester.\n */\n function collectRewardTokens()\n external\n virtual\n override\n onlyHarvester\n nonReentrant\n {\n // Claim COMP from Comptroller\n ICERC20 cToken = _getCTokenFor(assetsMapped[0]);\n IComptroller comptroller = IComptroller(cToken.comptroller());\n // Only collect from active cTokens, saves gas\n address[] memory ctokensToCollect = new address[](assetsMapped.length);\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n ctokensToCollect[i] = address(_getCTokenFor(assetsMapped[i]));\n }\n // Claim only for this strategy\n address[] memory claimers = new address[](1);\n claimers[0] = address(this);\n // Claim COMP from Comptroller. Only collect for supply, saves gas\n comptroller.claimComp(claimers, ctokensToCollect, false, true);\n // Transfer COMP to Harvester\n IERC20 rewardToken = IERC20(rewardTokenAddresses[0]);\n uint256 balance = rewardToken.balanceOf(address(this));\n emit RewardTokenCollected(\n harvesterAddress,\n rewardTokenAddresses[0],\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n\n /**\n * @notice Deposit asset into the underlying platform\n * @param _asset Address of asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit an asset into the underlying platform\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n ICERC20 cToken = _getCTokenFor(_asset);\n emit Deposit(_asset, address(cToken), _amount);\n require(cToken.mint(_amount) == 0, \"cToken mint failed\");\n }\n\n /**\n * @notice Deposit the entire balance of any supported asset in the strategy into the underlying platform\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n uint256 assetBalance = asset.balanceOf(address(this));\n if (assetBalance > 0) {\n _deposit(address(asset), assetBalance);\n }\n }\n }\n\n /**\n * @notice Withdraw an asset from the underlying platform\n * @param _recipient Address to receive withdrawn assets\n * @param _asset Address of the asset to withdraw\n * @param _amount Amount of assets to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n ICERC20 cToken = _getCTokenFor(_asset);\n // If redeeming 0 cTokens, just skip, else COMP will revert\n uint256 cTokensToRedeem = _convertUnderlyingToCToken(cToken, _amount);\n if (cTokensToRedeem == 0) {\n emit SkippedWithdrawal(_asset, _amount);\n return;\n }\n\n emit Withdrawal(_asset, address(cToken), _amount);\n require(cToken.redeemUnderlying(_amount) == 0, \"Redeem failed\");\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / cTokens\n * We need to approve the cToken and give it permission to spend the asset\n * @param _asset Address of the asset to approve. eg DAI\n * @param _pToken The pToken for the approval. eg cDAI or fDAI\n */\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n // Safe approval\n IERC20(_asset).safeApprove(_pToken, 0);\n IERC20(_asset).safeApprove(_pToken, type(uint256).max);\n }\n\n /**\n * @notice Remove all supported assets from the underlying platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n // Redeem entire balance of cToken\n ICERC20 cToken = _getCTokenFor(address(asset));\n uint256 cTokenBalance = cToken.balanceOf(address(this));\n if (cTokenBalance > 0) {\n require(cToken.redeem(cTokenBalance) == 0, \"Redeem failed\");\n uint256 assetBalance = asset.balanceOf(address(this));\n // Transfer entire balance to Vault\n asset.safeTransfer(vaultAddress, assetBalance);\n\n emit Withdrawal(address(asset), address(cToken), assetBalance);\n }\n }\n }\n\n /**\n * @notice Get the total asset value held in the underlying platform\n * This includes any interest that was generated since depositing.\n * The exchange rate between the cToken and asset gradually increases,\n * causing the cToken to be worth more corresponding asset.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n // Balance is always with token cToken decimals\n ICERC20 cToken = _getCTokenFor(_asset);\n balance = _checkBalance(cToken);\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * underlying = (cTokenAmt * exchangeRate) / 1e18\n * @param _cToken cToken for which to check balance\n * @return balance Total value of the asset in the platform\n */\n function _checkBalance(ICERC20 _cToken)\n internal\n view\n returns (uint256 balance)\n {\n // e.g. 50e8*205316390724364402565641705 / 1e18 = 1.0265..e18\n balance =\n (_cToken.balanceOf(address(this)) * _cToken.exchangeRateStored()) /\n 1e18;\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding cToken,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens() external override {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n address cToken = assetToPToken[address(asset)];\n // Safe approval\n asset.safeApprove(cToken, 0);\n asset.safeApprove(cToken, type(uint256).max);\n }\n }\n}\n" + }, + "contracts/strategies/ConvexEthMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Convex Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for the Curve OETH/ETH pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { ICurveETHPoolV1 } from \"./ICurveETHPoolV1.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\n\ncontract ConvexEthMetaStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 public constant MAX_SLIPPAGE = 1e16; // 1%, same as the Curve UI\n address public constant ETH_ADDRESS =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n // The following slots have been deprecated with immutable variables\n // slither-disable-next-line constable-states\n address private _deprecated_cvxDepositorAddress;\n // slither-disable-next-line constable-states\n address private _deprecated_cvxRewardStaker;\n // slither-disable-next-line constable-states\n uint256 private _deprecated_cvxDepositorPTokenId;\n // slither-disable-next-line constable-states\n address private _deprecated_curvePool;\n // slither-disable-next-line constable-states\n address private _deprecated_lpToken;\n // slither-disable-next-line constable-states\n address private _deprecated_oeth;\n // slither-disable-next-line constable-states\n address private _deprecated_weth;\n\n // Ordered list of pool assets\n // slither-disable-next-line constable-states\n uint128 private _deprecated_oethCoinIndex;\n // slither-disable-next-line constable-states\n uint128 private _deprecated_ethCoinIndex;\n\n // New immutable variables that must be set in the constructor\n address public immutable cvxDepositorAddress;\n IRewardStaking public immutable cvxRewardStaker;\n uint256 public immutable cvxDepositorPTokenId;\n ICurveETHPoolV1 public immutable curvePool;\n IERC20 public immutable lpToken;\n IERC20 public immutable oeth;\n IWETH9 public immutable weth;\n\n // Ordered list of pool assets\n uint128 public constant oethCoinIndex = 1;\n uint128 public constant ethCoinIndex = 0;\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier only works on functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balancesBefore = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffBefore = int256(balancesBefore[ethCoinIndex]) -\n int256(balancesBefore[oethCoinIndex]);\n\n _;\n\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balancesAfter = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffAfter = int256(balancesAfter[ethCoinIndex]) -\n int256(balancesAfter[oethCoinIndex]);\n\n if (diffBefore <= 0) {\n // If the pool was originally imbalanced in favor of OETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n }\n if (diffBefore >= 0) {\n // If the pool was originally imbalanced in favor of ETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n // Used to circumvent the stack too deep issue\n struct ConvexEthMetaConfig {\n address cvxDepositorAddress; //Address of the Convex depositor(AKA booster) for this pool\n address cvxRewardStakerAddress; //Address of the CVX rewards staker\n uint256 cvxDepositorPTokenId; //Pid of the pool referred to by Depositor and staker\n address oethAddress; //Address of OETH token\n address wethAddress; //Address of WETH\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n ConvexEthMetaConfig memory _convexConfig\n ) InitializableAbstractStrategy(_baseConfig) {\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveETHPoolV1(_baseConfig.platformAddress);\n\n cvxDepositorAddress = _convexConfig.cvxDepositorAddress;\n cvxRewardStaker = IRewardStaking(_convexConfig.cvxRewardStakerAddress);\n cvxDepositorPTokenId = _convexConfig.cvxDepositorPTokenId;\n oeth = IERC20(_convexConfig.oethAddress);\n weth = IWETH9(_convexConfig.wethAddress);\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. eg WETH\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets // WETH\n ) external onlyGovernor initializer {\n require(_assets.length == 1, \"Must have exactly one asset\");\n require(_assets[0] == address(weth), \"Asset not WETH\");\n\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit WETH into the Curve pool\n * @param _weth Address of Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to deposit.\n */\n function deposit(address _weth, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_weth, _amount);\n }\n\n function _deposit(address _weth, uint256 _wethAmount) internal {\n require(_wethAmount > 0, \"Must deposit something\");\n require(_weth == address(weth), \"Can only deposit WETH\");\n weth.withdraw(_wethAmount);\n\n emit Deposit(_weth, address(lpToken), _wethAmount);\n\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 oethToAdd = uint256(\n _max(\n 0,\n int256(balances[ethCoinIndex]) +\n int256(_wethAmount) -\n int256(balances[oethCoinIndex])\n )\n );\n\n /* Add so much OETH so that the pool ends up being balanced. And at minimum\n * add as much OETH as WETH and at maximum twice as much OETH.\n */\n oethToAdd = Math.max(oethToAdd, _wethAmount);\n oethToAdd = Math.min(oethToAdd, _wethAmount * 2);\n\n /* Mint OETH with a strategy that attempts to contribute to stability of OETH/WETH pool. Try\n * to mint so much OETH that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OETH minted will always be at least equal or greater\n * to WETH amount deployed. And never larger than twice the WETH amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(oethToAdd);\n\n emit Deposit(address(oeth), address(lpToken), oethToAdd);\n\n uint256[2] memory _amounts;\n _amounts[ethCoinIndex] = _wethAmount;\n _amounts[oethCoinIndex] = oethToAdd;\n\n uint256 valueInLpTokens = (_wethAmount + oethToAdd).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Do the deposit to the Curve pool\n // slither-disable-next-line arbitrary-send\n uint256 lpDeposited = curvePool.add_liquidity{ value: _wethAmount }(\n _amounts,\n minMintAmount\n );\n\n // Deposit the Curve pool's LP tokens into the Convex rewards pool\n require(\n IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n lpDeposited,\n true // Deposit with staking\n ),\n \"Depositing LP to Convex not successful\"\n );\n }\n\n /**\n * @notice Deposit the strategy's entire balance of WETH into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = weth.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(weth), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _weth Address of the Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to withdraw.\n */\n function withdraw(\n address _recipient,\n address _weth,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Invalid amount\");\n require(_weth == address(weth), \"Can only withdraw WETH\");\n\n emit Withdrawal(_weth, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(_amount);\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough WETH on balanced removal\n */\n uint256[2] memory _minWithdrawalAmounts = [uint256(0), uint256(0)];\n _minWithdrawalAmounts[ethCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OETH and any that was left in the strategy\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n\n // Transfer WETH to the recipient\n weth.deposit{ value: _amount }();\n require(\n weth.transfer(_recipient, _amount),\n \"Transfer of WETH not successful\"\n );\n }\n\n function calcTokenToBurn(uint256 _wethAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much WETH\n * we want we can determine how much of OETH we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolWETHBalance = curvePool.balances(ethCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolWETHBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_wethAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = cvxRewardStaker.balanceOf(address(this));\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[2] memory minWithdrawAmounts = [uint256(0), uint256(0)];\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(\n lpToken.balanceOf(address(this)),\n minWithdrawAmounts\n );\n\n // Burn all OETH\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Get the strategy contract's ether balance.\n // This includes all that was removed from the Curve pool and\n // any ether that was sitting in the strategy contract before the removal.\n uint256 ethBalance = address(this).balance;\n // Convert all the strategy contract's ether to WETH and transfer to the vault.\n weth.deposit{ value: ethBalance }();\n require(\n weth.transfer(vaultAddress, ethBalance),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethBalance);\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[2] memory amounts = [uint256(0), uint256(0)];\n amounts[oethCoinIndex] = _oTokens;\n\n // Convert OETH to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n\n // Deposit the Curve pool LP tokens to the Convex rewards pool\n require(\n IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n lpDeposited,\n true // Deposit with staking\n ),\n \"Failed to Deposit LP to Convex\"\n );\n\n emit Deposit(address(oeth), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough ETH.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove OTokens from the Curve pool\n uint256 oethToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n oethCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /**\n * @notice One-sided remove of ETH from the Curve pool, convert to WETH\n * and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for ETH.\n * @dev Curve pool LP tokens is used rather than WETH assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of ETH. Curve's `calc_token_amount` functioun does not include fees.\n * A 3rd party libary can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * caclulate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove ETH from the Curve pool\n uint256 ethAmount = _withdrawAndRemoveFromPool(_lpTokens, ethCoinIndex);\n\n // Convert ETH to WETH and transfer to the vault\n weth.deposit{ value: ethAmount }();\n require(\n weth.transfer(vaultAddress, ethAmount),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the Convex pool and\n * do a one-sided remove of ETH or OETH from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the Convex pool.\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = ETH, 1 = OETH.\n * @return coinsRemoved The amount of ETH or OETH removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Convex pool\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to ETH value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n // Apply slippage to ETH value\n uint256 minAmount = valueInEth.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Remove just the ETH from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV and CVX rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n cvxRewardStaker.getReward();\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _wethAmount) internal {\n // withdraw and unwrap with claim takes back the lpTokens\n // and also collects the rewards for deposit\n cvxRewardStaker.withdrawAndUnwrap(_wethAmount, true);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // Eth balance needed here for the balance check that happens from vault during depositing.\n balance = address(this).balance;\n uint256 lpTokens = cvxRewardStaker.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += (lpTokens * curvePool.get_virtual_price()) / 1e18;\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(weth);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @notice Accept unwrapped WETH\n */\n receive() external payable {}\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OETH (required for adding liquidity)\n // No approval is needed for ETH\n // slither-disable-next-line unused-return\n oeth.approve(platformAddress, type(uint256).max);\n\n // Approve Convex deposit contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Convex rewards pool\n // slither-disable-next-line unused-return\n lpToken.approve(cvxDepositorAddress, type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/ConvexGeneralizedMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { AbstractConvexMetaStrategy } from \"./AbstractConvexMetaStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract ConvexGeneralizedMetaStrategy is AbstractConvexMetaStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /* Take 3pool LP and deposit it to metapool. Take the LP from metapool\n * and deposit them to Convex.\n */\n function _lpDepositAll() internal override {\n IERC20 threePoolLp = IERC20(pTokenAddress);\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 threePoolLpBalance = threePoolLp.balanceOf(address(this));\n uint256 curve3PoolVirtualPrice = curvePool.get_virtual_price();\n uint256 threePoolLpDollarValue = threePoolLpBalance.mulTruncate(\n curve3PoolVirtualPrice\n );\n\n uint256[2] memory _amounts = [0, threePoolLpBalance];\n\n uint256 metapoolVirtualPrice = metapool.get_virtual_price();\n /**\n * First convert all the deposited tokens to dollar values,\n * then divide by virtual price to convert to metapool LP tokens\n * and apply the max slippage\n */\n uint256 minReceived = threePoolLpDollarValue\n .divPrecisely(metapoolVirtualPrice)\n .mulTruncate(uint256(1e18) - MAX_SLIPPAGE);\n\n uint256 metapoolLp = metapool.add_liquidity(_amounts, minReceived);\n\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n metapoolLp,\n true // Deposit with staking\n );\n\n require(success, \"Failed to deposit to Convex\");\n }\n\n /**\n * Withdraw the specified amount of tokens from the gauge. And use all the resulting tokens\n * to remove liquidity from metapool\n * @param num3CrvTokens Number of Convex 3pool LP tokens to withdraw from metapool\n */\n function _lpWithdraw(uint256 num3CrvTokens) internal override {\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n uint256 requiredMetapoolLpTokens = _calcCurveMetaTokenAmount(\n crvCoinIndex,\n num3CrvTokens\n );\n\n require(\n requiredMetapoolLpTokens <= gaugeTokens,\n string(\n bytes.concat(\n bytes(\"Attempting to withdraw \"),\n bytes(Strings.toString(requiredMetapoolLpTokens)),\n bytes(\", metapoolLP but only \"),\n bytes(Strings.toString(gaugeTokens)),\n bytes(\" available.\")\n )\n )\n );\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards for deposit\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n requiredMetapoolLpTokens,\n true\n );\n\n if (requiredMetapoolLpTokens > 0) {\n // slither-disable-next-line unused-return\n metapool.remove_liquidity_one_coin(\n requiredMetapoolLpTokens,\n int128(crvCoinIndex),\n num3CrvTokens\n );\n }\n }\n\n function _lpWithdrawAll() internal override {\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n gaugeTokens,\n true\n );\n\n if (gaugeTokens > 0) {\n uint256 burnDollarAmount = gaugeTokens.mulTruncate(\n metapool.get_virtual_price()\n );\n uint256 curve3PoolExpected = burnDollarAmount.divPrecisely(\n ICurvePool(platformAddress).get_virtual_price()\n );\n\n // Always withdraw all of the available metapool LP tokens (similar to how we always deposit all)\n // slither-disable-next-line unused-return\n metapool.remove_liquidity_one_coin(\n gaugeTokens,\n int128(crvCoinIndex),\n curve3PoolExpected -\n curve3PoolExpected.mulTruncate(maxWithdrawalSlippage)\n );\n }\n }\n}\n" + }, + "contracts/strategies/ConvexOUSDMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { AbstractConvexMetaStrategy } from \"./AbstractConvexMetaStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract ConvexOUSDMetaStrategy is AbstractConvexMetaStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /* Take 3pool LP and mint the corresponding amount of ousd. Deposit and stake that to\n * ousd Curve Metapool. Take the LP from metapool and deposit them to Convex.\n */\n function _lpDepositAll() internal override {\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 threePoolLpBalance = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n uint256 curve3PoolVirtualPrice = curvePool.get_virtual_price();\n uint256 threePoolLpDollarValue = threePoolLpBalance.mulTruncate(\n curve3PoolVirtualPrice\n );\n\n // safe to cast since min value is at least 0\n uint256 ousdToAdd = uint256(\n _max(\n 0,\n int256(\n metapool.balances(crvCoinIndex).mulTruncate(\n curve3PoolVirtualPrice\n )\n ) -\n int256(metapool.balances(mainCoinIndex)) +\n int256(threePoolLpDollarValue)\n )\n );\n\n /* Add so much OUSD so that the pool ends up being balanced. And at minimum\n * add twice as much OUSD as 3poolLP and at maximum at twice as\n * much OUSD.\n */\n ousdToAdd = Math.max(ousdToAdd, threePoolLpDollarValue);\n ousdToAdd = Math.min(ousdToAdd, threePoolLpDollarValue * 2);\n\n /* Mint OUSD with a strategy that attempts to contribute to stability of OUSD metapool. Try\n * to mint so much OUSD that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OUSD minted will always be at least equal or greater\n * to stablecoin(DAI, USDC, USDT) amount of 3CRVLP deployed. And never larger than twice the\n * stablecoin amount of 3CRVLP deployed even if it would have a further beneficial effect\n * on pool stability.\n */\n if (ousdToAdd > 0) {\n IVault(vaultAddress).mintForStrategy(ousdToAdd);\n }\n\n uint256[2] memory _amounts = [ousdToAdd, threePoolLpBalance];\n\n uint256 metapoolVirtualPrice = metapool.get_virtual_price();\n /**\n * First convert all the deposited tokens to dollar values,\n * then divide by virtual price to convert to metapool LP tokens\n * and apply the max slippage\n */\n uint256 minReceived = (ousdToAdd + threePoolLpDollarValue)\n .divPrecisely(metapoolVirtualPrice)\n .mulTruncate(uint256(1e18) - MAX_SLIPPAGE);\n\n uint256 metapoolLp = metapool.add_liquidity(_amounts, minReceived);\n\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n metapoolLp,\n true // Deposit with staking\n );\n\n require(success, \"Failed to deposit to Convex\");\n }\n\n /**\n * Withdraw the specified amount of tokens from the gauge. And use all the resulting tokens\n * to remove liquidity from metapool\n * @param num3CrvTokens Number of 3CRV tokens to withdraw from metapool\n */\n function _lpWithdraw(uint256 num3CrvTokens) internal override {\n ICurvePool curvePool = ICurvePool(platformAddress);\n /* The rate between coins in the metapool determines the rate at which metapool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much 3crvLp\n * we want we can determine how much of OUSD we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 crvPoolBalance = metapool.balances(crvCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * metapool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * metapoolLPToken.totalSupply()) / crvPoolBalance;\n // simplifying below to: `uint256 diff = (num3CrvTokens - 1) * k` causes loss of precision\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = crvPoolBalance * k -\n (crvPoolBalance - num3CrvTokens - 1) * k;\n uint256 lpToBurn = diff / 1e36;\n\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n require(\n lpToBurn <= gaugeTokens,\n string(\n bytes.concat(\n bytes(\"Attempting to withdraw \"),\n bytes(Strings.toString(lpToBurn)),\n bytes(\", metapoolLP but only \"),\n bytes(Strings.toString(gaugeTokens)),\n bytes(\" available.\")\n )\n )\n );\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards for deposit\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n lpToBurn,\n true\n );\n\n // calculate the min amount of OUSD expected for the specified amount of LP tokens\n uint256 minOUSDAmount = lpToBurn.mulTruncate(\n metapool.get_virtual_price()\n ) -\n num3CrvTokens.mulTruncate(curvePool.get_virtual_price()) -\n 1;\n\n // withdraw the liquidity from metapool\n uint256[2] memory _removedAmounts = metapool.remove_liquidity(\n lpToBurn,\n [minOUSDAmount, num3CrvTokens]\n );\n\n IVault(vaultAddress).burnForStrategy(_removedAmounts[mainCoinIndex]);\n }\n\n function _lpWithdrawAll() internal override {\n IERC20 metapoolErc20 = IERC20(address(metapool));\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n gaugeTokens,\n true\n );\n\n uint256[2] memory _minAmounts = [uint256(0), uint256(0)];\n uint256[2] memory _removedAmounts = metapool.remove_liquidity(\n metapoolErc20.balanceOf(address(this)),\n _minAmounts\n );\n\n IVault(vaultAddress).burnForStrategy(_removedAmounts[mainCoinIndex]);\n }\n}\n" + }, + "contracts/strategies/ConvexStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { IERC20, AbstractCurveStrategy, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\n/*\n * IMPORTANT(!) If ConvexStrategy needs to be re-deployed, it requires new\n * proxy contract with fresh storage slots. Changes in `AbstractCurveStrategy`\n * storage slots would break existing implementation.\n *\n * Remove this notice if ConvexStrategy is re-deployed\n */\ncontract ConvexStrategy is AbstractCurveStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n address internal cvxDepositorAddress;\n address internal cvxRewardStakerAddress;\n // slither-disable-next-line constable-states\n address private _deprecated_cvxRewardTokenAddress;\n uint256 internal cvxDepositorPTokenId;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * DAI, USDC, USDT\n * @param _pTokens Platform Token corresponding addresses\n * @param _cvxDepositorAddress Address of the Convex depositor(AKA booster) for this pool\n * @param _cvxRewardStakerAddress Address of the CVX rewards staker\n * @param _cvxDepositorPTokenId Pid of the pool referred to by Depositor and staker\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets,\n address[] calldata _pTokens,\n address _cvxDepositorAddress,\n address _cvxRewardStakerAddress,\n uint256 _cvxDepositorPTokenId\n ) external onlyGovernor initializer {\n require(_assets.length == 3, \"Must have exactly three assets\");\n // Should be set prior to abstract initialize call otherwise\n // abstractSetPToken calls will fail\n cvxDepositorAddress = _cvxDepositorAddress;\n cvxRewardStakerAddress = _cvxRewardStakerAddress;\n cvxDepositorPTokenId = _cvxDepositorPTokenId;\n pTokenAddress = _pTokens[0];\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n function _lpDepositAll() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // Deposit with staking\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n pToken.balanceOf(address(this)),\n true\n );\n require(success, \"Failed to deposit to Convex\");\n }\n\n function _lpWithdraw(uint256 numCrvTokens) internal override {\n uint256 gaugePTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n // Not enough in this contract or in the Gauge, can't proceed\n require(numCrvTokens > gaugePTokens, \"Insufficient 3CRV balance\");\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards to this\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n numCrvTokens,\n true\n );\n }\n\n function _lpWithdrawAll() internal override {\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards to this\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n IRewardStaking(cvxRewardStakerAddress).balanceOf(address(this)),\n true\n );\n }\n\n function _approveBase() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // 3Pool for LP token (required for removing liquidity)\n pToken.safeApprove(platformAddress, 0);\n pToken.safeApprove(platformAddress, type(uint256).max);\n // Gauge for LP token\n pToken.safeApprove(cvxDepositorAddress, 0);\n pToken.safeApprove(cvxDepositorAddress, type(uint256).max);\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 contractPTokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n uint256 gaugePTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n uint256 totalPTokens = contractPTokens + gaugePTokens;\n\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (totalPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = (totalPTokens * virtual_price) / 1e18;\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = value.scaleBy(assetDecimals, 18) / 3;\n }\n }\n\n /**\n * @dev Collect accumulated CRV and CVX and send to Vault.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n IRewardStaking(cvxRewardStakerAddress).getReward();\n _collectRewardTokens();\n }\n}\n" + }, + "contracts/strategies/CurveAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for a Curve pool using an OToken.\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { ICurveStableSwapNG } from \"../interfaces/ICurveStableSwapNG.sol\";\nimport { ICurveLiquidityGaugeV6 } from \"../interfaces/ICurveLiquidityGaugeV6.sol\";\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\nimport { ICurveMinter } from \"../interfaces/ICurveMinter.sol\";\n\ncontract CurveAMOStrategy is InitializableAbstractStrategy {\n using SafeCast for uint256;\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n /**\n * @dev a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n // New immutable variables that must be set in the constructor\n /**\n * @notice Address of the hard asset (weth, usdt, usdc).\n */\n IERC20 public immutable hardAsset;\n\n /**\n * @notice Address of the OTOKEN token contract.\n */\n IERC20 public immutable oToken;\n\n /**\n * @notice Address of the LP (Liquidity Provider) token contract.\n */\n IERC20 public immutable lpToken;\n\n /**\n * @notice Address of the Curve StableSwap NG pool contract.\n */\n ICurveStableSwapNG public immutable curvePool;\n\n /**\n * @notice Address of the Curve X-Chain Liquidity Gauge contract.\n */\n ICurveLiquidityGaugeV6 public immutable gauge;\n\n /**\n * @notice Address of the Curve Minter contract.\n */\n ICurveMinter public immutable minter;\n\n /**\n * @notice Index of the OTOKEN and hardAsset in the Curve pool.\n */\n uint128 public immutable otokenCoinIndex;\n uint128 public immutable hardAssetCoinIndex;\n\n /**\n * @notice Decimals of the OTOKEN and hardAsset.\n */\n uint8 public immutable decimalsOToken;\n uint8 public immutable decimalsHardAsset;\n\n /**\n * @notice Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n uint256 public maxSlippage;\n\n event MaxSlippageUpdated(uint256 newMaxSlippage);\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the hard asset and OToken balances in the Curve pool\n uint256[] memory balancesBefore = curvePool.get_balances();\n // diff = hardAsset balance - OTOKEN balance\n int256 diffBefore = (\n balancesBefore[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() - balancesBefore[otokenCoinIndex].toInt256();\n\n _;\n\n // Get the hard asset and OToken balances in the Curve pool\n uint256[] memory balancesAfter = curvePool.get_balances();\n // diff = hardAsset balance - OTOKEN balance\n int256 diffAfter = (\n balancesAfter[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() - balancesAfter[otokenCoinIndex].toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OTOKEN, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of hardAsset, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _otoken,\n address _hardAsset,\n address _gauge,\n address _minter\n ) InitializableAbstractStrategy(_baseConfig) {\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveStableSwapNG(_baseConfig.platformAddress);\n minter = ICurveMinter(_minter);\n\n oToken = IERC20(_otoken);\n hardAsset = IERC20(_hardAsset);\n gauge = ICurveLiquidityGaugeV6(_gauge);\n decimalsHardAsset = IBasicToken(_hardAsset).decimals();\n decimalsOToken = IBasicToken(_otoken).decimals();\n\n (hardAssetCoinIndex, otokenCoinIndex) = curvePool.coins(0) == _hardAsset\n ? (0, 1)\n : (1, 0);\n require(\n curvePool.coins(otokenCoinIndex) == _otoken &&\n curvePool.coins(hardAssetCoinIndex) == _hardAsset,\n \"Invalid coin indexes\"\n );\n require(gauge.lp_token() == address(curvePool), \"Invalid pool\");\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV\n * @param _maxSlippage Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV\n uint256 _maxSlippage\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n address[] memory _assets = new address[](1);\n _assets[0] = address(hardAsset);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n _setMaxSlippage(_maxSlippage);\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit hard asset into the Curve pool\n * @param _hardAsset Address of hard asset contract.\n * @param _amount Amount of hard asset to deposit.\n */\n function deposit(address _hardAsset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_hardAsset, _amount);\n }\n\n function _deposit(address _hardAsset, uint256 _hardAssetAmount) internal {\n require(_hardAssetAmount > 0, \"Must deposit something\");\n require(_hardAsset == address(hardAsset), \"Unsupported asset\");\n\n emit Deposit(_hardAsset, address(lpToken), _hardAssetAmount);\n uint256 scaledHardAssetAmount = _hardAssetAmount.scaleBy(\n decimalsOToken,\n decimalsHardAsset\n );\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 otokenToAdd = uint256(\n _max(\n 0,\n (\n balances[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() +\n scaledHardAssetAmount.toInt256() -\n balances[otokenCoinIndex].toInt256()\n )\n );\n\n /* Add so much OTOKEN so that the pool ends up being balanced. And at minimum\n * add as much OTOKEN as hard asset and at maximum twice as much OTOKEN.\n */\n otokenToAdd = Math.max(otokenToAdd, scaledHardAssetAmount);\n otokenToAdd = Math.min(otokenToAdd, scaledHardAssetAmount * 2);\n\n /* Mint OTOKEN with a strategy that attempts to contribute to stability of OTOKEN/hardAsset pool. Try\n * to mint so much OTOKEN that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OTOKEN minted will always be at least equal or greater\n * to hardAsset amount deployed. And never larger than twice the hardAsset amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(otokenToAdd);\n\n emit Deposit(address(oToken), address(lpToken), otokenToAdd);\n\n uint256[] memory _amounts = new uint256[](2);\n _amounts[hardAssetCoinIndex] = _hardAssetAmount;\n _amounts[otokenCoinIndex] = otokenToAdd;\n\n uint256 valueInLpTokens = (scaledHardAssetAmount + otokenToAdd)\n .divPrecisely(curvePool.get_virtual_price());\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Do the deposit to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(_amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool's LP tokens into the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n /**\n * @notice Deposit the strategy's entire balance of hardAsset into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = hardAsset.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(hardAsset), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw hardAsset and OTOKEN from the Curve pool, burn the OTOKEN,\n * transfer hardAsset to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _hardAsset Address of the hardAsset contract.\n * @param _amount Amount of hardAsset to withdraw.\n */\n function withdraw(\n address _recipient,\n address _hardAsset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(\n _hardAsset == address(hardAsset),\n \"Can only withdraw hard asset\"\n );\n\n emit Withdrawal(_hardAsset, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(\n _amount.scaleBy(decimalsOToken, decimalsHardAsset)\n );\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough hardAsset on balanced removal\n */\n uint256[] memory _minWithdrawalAmounts = new uint256[](2);\n _minWithdrawalAmounts[hardAssetCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OTOKEN and any that was left in the strategy\n uint256 otokenToBurn = oToken.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n\n // Transfer hardAsset to the recipient\n hardAsset.safeTransfer(_recipient, _amount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n function calcTokenToBurn(uint256 _hardAssetAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much hardAsset\n * we want we can determine how much of OTOKEN we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolHardAssetBalance = curvePool\n .balances(hardAssetCoinIndex)\n .scaleBy(decimalsOToken, decimalsHardAsset);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolHardAssetBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_hardAssetAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all hardAsset and OTOKEN from the Curve pool, burn the OTOKEN,\n * transfer hardAsset to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = gauge.balanceOf(address(this));\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[] memory minWithdrawAmounts = new uint256[](2);\n\n // Check balance of LP tokens in the strategy, if 0 return\n uint256 lpBalance = lpToken.balanceOf(address(this));\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n if (lpBalance > 0) {\n curvePool.remove_liquidity(lpBalance, minWithdrawAmounts);\n }\n\n // Burn all OTOKEN\n uint256 otokenToBurn = oToken.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n // Get the strategy contract's hardAsset balance.\n // This includes all that was removed from the Curve pool and\n // any hardAsset that was sitting in the strategy contract before the removal.\n uint256 hardAssetBalance = hardAsset.balanceOf(address(this));\n hardAsset.safeTransfer(vaultAddress, hardAssetBalance);\n\n if (hardAssetBalance > 0)\n emit Withdrawal(\n address(hardAsset),\n address(lpToken),\n hardAssetBalance\n );\n if (otokenToBurn > 0)\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many hardAsset.\n * The OToken/Asset, eg OTOKEN/hardAsset, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[] memory amounts = new uint256[](2);\n amounts[otokenCoinIndex] = _oTokens;\n\n // Convert OTOKEN to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool LP tokens to the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Deposit(address(oToken), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough hardAsset.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from gauge and remove OTokens from the Curve pool\n uint256 otokenToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n otokenCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n }\n\n /**\n * @notice One-sided remove of hardAsset from the Curve pool and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many hardAsset.\n * The OToken/Asset, eg OTOKEN/hardAsset, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for hardAsset.\n * @dev Curve pool LP tokens is used rather than hardAsset assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of hardAsset. Curve's `calc_token_amount` function does not include fees.\n * A 3rd party library can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * calculate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Curve gauge and remove hardAsset from the Curve pool\n uint256 hardAssetAmount = _withdrawAndRemoveFromPool(\n _lpTokens,\n hardAssetCoinIndex\n );\n\n // Transfer hardAsset to the vault\n hardAsset.safeTransfer(vaultAddress, hardAssetAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(hardAsset), address(lpToken), hardAssetAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the gauge and\n * do a one-sided remove of hardAsset or OTOKEN from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the gauge\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = hardAsset, 1 = OTOKEN.\n * @return coinsRemoved The amount of hardAsset or OTOKEN removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Curve gauge\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to hardAsset value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n\n if (coinIndex == hardAssetCoinIndex) {\n valueInEth = valueInEth.scaleBy(decimalsHardAsset, decimalsOToken);\n }\n\n // Apply slippage to hardAsset value\n uint256 minAmount = valueInEth.mulTruncate(uint256(1e18) - maxSlippage);\n\n // Remove just the hardAsset from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOtokenSupply = oToken.totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOtokenSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV rewards from inflation\n minter.mint(address(gauge));\n\n // Collect extra gauge rewards (outside of CRV)\n gauge.claim_rewards();\n\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _lpAmount) internal {\n require(\n gauge.balanceOf(address(this)) >= _lpAmount,\n \"Insufficient LP tokens\"\n );\n // withdraw lp tokens from the gauge without claiming rewards\n gauge.withdraw(_lpAmount);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(hardAsset), \"Unsupported asset\");\n\n // hardAsset balance needed here for the balance check that happens from vault during depositing.\n balance = hardAsset.balanceOf(address(this));\n uint256 lpTokens = gauge.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += ((lpTokens * curvePool.get_virtual_price()) / 1e18)\n .scaleBy(decimalsHardAsset, decimalsOToken);\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(hardAsset);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Sets the maximum slippage allowed for any swap/liquidity operation\n * @param _maxSlippage Maximum slippage allowed, 1e18 = 100%.\n */\n function setMaxSlippage(uint256 _maxSlippage) external onlyGovernor {\n _setMaxSlippage(_maxSlippage);\n }\n\n function _setMaxSlippage(uint256 _maxSlippage) internal {\n require(_maxSlippage <= 5e16, \"Slippage must be less than 100%\");\n maxSlippage = _maxSlippage;\n emit MaxSlippageUpdated(_maxSlippage);\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OTOKEN (required for adding liquidity)\n // slither-disable-next-line unused-return\n oToken.approve(platformAddress, type(uint256).max);\n\n // Approve Curve pool for hardAsset (required for adding liquidity)\n // slither-disable-next-line unused-return\n hardAsset.safeApprove(platformAddress, type(uint256).max);\n\n // Approve Curve gauge contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Curve gauge.\n // slither-disable-next-line unused-return\n lpToken.approve(address(gauge), type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/CurvePoolBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport { ICampaignRemoteManager } from \"../interfaces/ICampaignRemoteManager.sol\";\n\n/// @title CurvePoolBooster\n/// @author Origin Protocol\n/// @notice Contract to manage interactions with VotemarketV2 for a dedicated Curve pool/gauge.\ncontract CurvePoolBooster is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n ////////////////////////////////////////////////////\n /// --- CONSTANTS && IMMUTABLES\n ////////////////////////////////////////////////////\n /// @notice Base fee for the contract, 100%\n uint16 public constant FEE_BASE = 10_000;\n\n /// @notice Address of the gauge to manage\n address public immutable gauge;\n\n /// @notice Address of the reward token\n address public immutable rewardToken;\n\n /// @notice Chain id of the target chain\n uint256 public immutable targetChainId;\n\n ////////////////////////////////////////////////////\n /// --- STORAGE\n ////////////////////////////////////////////////////\n /// @notice Fee in FEE_BASE unit payed when managing campaign.\n uint16 public fee;\n\n /// @notice Address of the fee collector\n address public feeCollector;\n\n /// @notice Address of the campaignRemoteManager linked to VotemarketV2\n address public campaignRemoteManager;\n\n /// @notice Address of votemarket in L2\n address public votemarket;\n\n /// @notice Id of the campaign created\n uint256 public campaignId;\n\n ////////////////////////////////////////////////////\n /// --- EVENTS\n ////////////////////////////////////////////////////\n event FeeUpdated(uint16 newFee);\n event FeeCollected(address feeCollector, uint256 feeAmount);\n event FeeCollectorUpdated(address newFeeCollector);\n event VotemarketUpdated(address newVotemarket);\n event CampaignRemoteManagerUpdated(address newCampaignRemoteManager);\n event CampaignCreated(\n address gauge,\n address rewardToken,\n uint256 maxRewardPerVote,\n uint256 totalRewardAmount\n );\n event CampaignIdUpdated(uint256 newId);\n event CampaignClosed(uint256 campaignId);\n event TotalRewardAmountUpdated(uint256 extraTotalRewardAmount);\n event NumberOfPeriodsUpdated(uint8 extraNumberOfPeriods);\n event RewardPerVoteUpdated(uint256 newMaxRewardPerVote);\n event TokensRescued(address token, uint256 amount, address receiver);\n\n ////////////////////////////////////////////////////\n /// --- CONSTRUCTOR && INITIALIZATION\n ////////////////////////////////////////////////////\n constructor(\n uint256 _targetChainId,\n address _rewardToken,\n address _gauge\n ) {\n targetChainId = _targetChainId;\n rewardToken = _rewardToken;\n gauge = _gauge;\n\n // Prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /// @notice initialize function, to set up initial internal state\n /// @param _strategist Address of the strategist\n /// @param _fee Fee in FEE_BASE unit payed when managing campaign\n /// @param _feeCollector Address of the fee collector\n function initialize(\n address _strategist,\n uint16 _fee,\n address _feeCollector,\n address _campaignRemoteManager,\n address _votemarket\n ) external onlyGovernor initializer {\n _setStrategistAddr(_strategist);\n _setFee(_fee);\n _setFeeCollector(_feeCollector);\n _setCampaignRemoteManager(_campaignRemoteManager);\n _setVotemarket(_votemarket);\n }\n\n ////////////////////////////////////////////////////\n /// --- MUTATIVE FUNCTIONS\n ////////////////////////////////////////////////////\n /// @notice Create a new campaign on VotemarketV2\n /// @dev This will use all token available in this contract\n /// @param numberOfPeriods Duration of the campaign in weeks\n /// @param maxRewardPerVote Maximum reward per vote to distribute, to avoid overspending\n /// @param blacklist List of addresses to exclude from the campaign\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function createCampaign(\n uint8 numberOfPeriods,\n uint256 maxRewardPerVote,\n address[] calldata blacklist,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId == 0, \"Campaign already created\");\n require(numberOfPeriods > 1, \"Invalid number of periods\");\n require(maxRewardPerVote > 0, \"Invalid reward per vote\");\n\n // Handle fee (if any)\n uint256 balanceSubFee = _handleFee();\n\n // Approve the balanceSubFee to the campaign manager\n IERC20(rewardToken).safeApprove(campaignRemoteManager, 0);\n IERC20(rewardToken).safeApprove(campaignRemoteManager, balanceSubFee);\n\n // Create a new campaign\n ICampaignRemoteManager(campaignRemoteManager).createCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignCreationParams({\n chainId: targetChainId,\n gauge: gauge,\n manager: address(this),\n rewardToken: rewardToken,\n numberOfPeriods: numberOfPeriods,\n maxRewardPerVote: maxRewardPerVote,\n totalRewardAmount: balanceSubFee,\n addresses: blacklist,\n hook: address(0),\n isWhitelist: false\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit CampaignCreated(\n gauge,\n rewardToken,\n maxRewardPerVote,\n balanceSubFee\n );\n }\n\n /// @notice Manage the total reward amount of the campaign\n /// @dev This function should be called after the campaign is created\n /// @dev This will use all the token available in this contract\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageTotalRewardAmount(\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n\n // Handle fee (if any)\n uint256 balanceSubFee = _handleFee();\n\n // Approve the total reward amount to the campaign manager\n IERC20(rewardToken).safeApprove(campaignRemoteManager, 0);\n IERC20(rewardToken).safeApprove(campaignRemoteManager, balanceSubFee);\n\n // Manage the campaign\n // https://github.com/stake-dao/votemarket-v2/blob/main/packages/votemarket/src/Votemarket.sol#L668\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: 0,\n totalRewardAmount: balanceSubFee,\n maxRewardPerVote: 0\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit TotalRewardAmountUpdated(balanceSubFee);\n }\n\n /// @notice Manage the number of periods of the campaign\n /// @dev This function should be called after the campaign is created\n /// @param extraNumberOfPeriods Number of additional periods (cannot be 0)\n /// that will be added to already existing amount of periods.\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageNumberOfPeriods(\n uint8 extraNumberOfPeriods,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n require(extraNumberOfPeriods > 0, \"Invalid number of periods\");\n\n // Manage the campaign\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: extraNumberOfPeriods,\n totalRewardAmount: 0,\n maxRewardPerVote: 0\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit NumberOfPeriodsUpdated(extraNumberOfPeriods);\n }\n\n /// @notice Manage the reward per vote of the campaign\n /// @dev This function should be called after the campaign is created\n /// @param newMaxRewardPerVote New maximum reward per vote\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageRewardPerVote(\n uint256 newMaxRewardPerVote,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n require(newMaxRewardPerVote > 0, \"Invalid reward per vote\");\n\n // Manage the campaign\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: 0,\n totalRewardAmount: 0,\n maxRewardPerVote: newMaxRewardPerVote\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit RewardPerVoteUpdated(newMaxRewardPerVote);\n }\n\n /// @notice Close the campaign.\n /// @dev This function only work on the L2 chain. Not on mainnet.\n /// @dev The _campaignId parameter is not related to the campaignId of this contract, allowing greater flexibility.\n /// @param _campaignId Id of the campaign to close\n function closeCampaign(\n uint256 _campaignId,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n ICampaignRemoteManager(campaignRemoteManager).closeCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignClosingParams({\n campaignId: campaignId\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n emit CampaignClosed(_campaignId);\n }\n\n /// @notice calculate the fee amount and transfer it to the feeCollector\n /// @return Balance after fee\n function _handleFee() internal returns (uint256) {\n // Cache current rewardToken balance\n uint256 balance = IERC20(rewardToken).balanceOf(address(this));\n require(balance > 0, \"No reward to manage\");\n\n uint256 feeAmount = (balance * fee) / FEE_BASE;\n\n // If there is a fee, transfer it to the feeCollector\n if (feeAmount > 0) {\n // Transfer the fee to the feeCollector\n IERC20(rewardToken).safeTransfer(feeCollector, feeAmount);\n emit FeeCollected(feeCollector, feeAmount);\n\n return IERC20(rewardToken).balanceOf(address(this));\n }\n\n // Return remaining balance\n return balance;\n }\n\n ////////////////////////////////////////////////////\n /// --- GOVERNANCE && OPERATION\n ////////////////////////////////////////////////////\n /// @notice Set the campaign id\n /// @dev Only callable by the governor or strategist\n /// @param _campaignId New campaign id\n function setCampaignId(uint256 _campaignId)\n external\n onlyGovernorOrStrategist\n {\n campaignId = _campaignId;\n emit CampaignIdUpdated(_campaignId);\n }\n\n /// @notice Rescue ETH from the contract\n /// @dev Only callable by the governor or strategist\n /// @param receiver Address to receive the ETH\n function rescueETH(address receiver)\n external\n nonReentrant\n onlyGovernorOrStrategist\n {\n require(receiver != address(0), \"Invalid receiver\");\n uint256 balance = address(this).balance;\n (bool success, ) = receiver.call{ value: balance }(\"\");\n require(success, \"Transfer failed\");\n emit TokensRescued(address(0), balance, receiver);\n }\n\n /// @notice Rescue ERC20 tokens from the contract\n /// @dev Only callable by the governor or strategist\n /// @param token Address of the token to rescue\n function rescueToken(address token, address receiver)\n external\n nonReentrant\n onlyGovernor\n {\n require(receiver != address(0), \"Invalid receiver\");\n uint256 balance = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransfer(receiver, balance);\n emit TokensRescued(token, balance, receiver);\n }\n\n /// @notice Set the fee\n /// @dev Only callable by the governor\n /// @param _fee New fee\n function setFee(uint16 _fee) external onlyGovernor {\n _setFee(_fee);\n }\n\n /// @notice Internal logic to set the fee\n function _setFee(uint16 _fee) internal {\n require(_fee <= FEE_BASE / 2, \"Fee too high\");\n fee = _fee;\n emit FeeUpdated(_fee);\n }\n\n /// @notice Set the fee collector\n /// @dev Only callable by the governor\n /// @param _feeCollector New fee collector\n function setFeeCollector(address _feeCollector) external onlyGovernor {\n _setFeeCollector(_feeCollector);\n }\n\n /// @notice Internal logic to set the fee collector\n function _setFeeCollector(address _feeCollector) internal {\n require(_feeCollector != address(0), \"Invalid fee collector\");\n feeCollector = _feeCollector;\n emit FeeCollectorUpdated(_feeCollector);\n }\n\n /// @notice Set the campaignRemoteManager\n /// @param _campaignRemoteManager New campaignRemoteManager address\n function setCampaignRemoteManager(address _campaignRemoteManager)\n external\n onlyGovernor\n {\n _setCampaignRemoteManager(_campaignRemoteManager);\n }\n\n /// @notice Internal logic to set the campaignRemoteManager\n /// @param _campaignRemoteManager New campaignRemoteManager address\n function _setCampaignRemoteManager(address _campaignRemoteManager)\n internal\n {\n require(\n _campaignRemoteManager != address(0),\n \"Invalid campaignRemoteManager\"\n );\n campaignRemoteManager = _campaignRemoteManager;\n emit CampaignRemoteManagerUpdated(_campaignRemoteManager);\n }\n\n /// @notice Set the votemarket address\n /// @param _votemarket New votemarket address\n function setVotemarket(address _votemarket) external onlyGovernor {\n _setVotemarket(_votemarket);\n }\n\n /// @notice Internal logic to set the votemarket address\n function _setVotemarket(address _votemarket) internal onlyGovernor {\n require(_votemarket != address(0), \"Invalid votemarket\");\n votemarket = _votemarket;\n emit VotemarketUpdated(_votemarket);\n }\n\n receive() external payable {}\n}\n" + }, + "contracts/strategies/Generalized4626Strategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Generalized 4626 Strategy\n * @notice Investment strategy for ERC-4626 Tokenized Vaults\n * @author Origin Protocol Inc\n */\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\ncontract Generalized4626Strategy is InitializableAbstractStrategy {\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecate_shareToken;\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecate_assetToken;\n\n IERC20 public immutable shareToken;\n IERC20 public immutable assetToken;\n\n // For future use\n uint256[50] private __gap;\n\n /**\n * @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n * and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n * @param _assetToken Address of the ERC-4626 asset token. eg frxETH or DAI\n */\n constructor(BaseStrategyConfig memory _baseConfig, address _assetToken)\n InitializableAbstractStrategy(_baseConfig)\n {\n shareToken = IERC20(_baseConfig.platformAddress);\n assetToken = IERC20(_assetToken);\n }\n\n function initialize() external virtual onlyGovernor initializer {\n address[] memory rewardTokens = new address[](0);\n address[] memory assets = new address[](1);\n address[] memory pTokens = new address[](1);\n\n assets[0] = address(assetToken);\n pTokens[0] = address(platformAddress);\n\n InitializableAbstractStrategy._initialize(\n rewardTokens,\n assets,\n pTokens\n );\n }\n\n /**\n * @dev Deposit assets by converting them to shares\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit assets by converting them to shares\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal virtual {\n require(_amount > 0, \"Must deposit something\");\n require(_asset == address(assetToken), \"Unexpected asset address\");\n\n // slither-disable-next-line unused-return\n IERC4626(platformAddress).deposit(_amount, address(this));\n emit Deposit(_asset, address(shareToken), _amount);\n }\n\n /**\n * @dev Deposit the entire balance of assetToken to gain shareToken\n */\n function depositAll() external virtual override onlyVault nonReentrant {\n uint256 balance = assetToken.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(assetToken), balance);\n }\n }\n\n /**\n * @dev Withdraw asset by burning shares\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n require(_asset == address(assetToken), \"Unexpected asset address\");\n\n // slither-disable-next-line unused-return\n IERC4626(platformAddress).withdraw(_amount, _recipient, address(this));\n emit Withdrawal(_asset, address(shareToken), _amount);\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / share tokens\n */\n function _abstractSetPToken(address, address) internal virtual override {\n _approveBase();\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll()\n external\n virtual\n override\n onlyVaultOrGovernor\n nonReentrant\n {\n uint256 shareBalance = shareToken.balanceOf(address(this));\n uint256 assetAmount = 0;\n if (shareBalance > 0) {\n assetAmount = IERC4626(platformAddress).redeem(\n shareBalance,\n vaultAddress,\n address(this)\n );\n emit Withdrawal(\n address(assetToken),\n address(shareToken),\n assetAmount\n );\n }\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(_asset == address(assetToken), \"Unexpected asset address\");\n /* We are intentionally not counting the amount of assetToken parked on the\n * contract toward the checkBalance. The deposit and withdraw functions\n * should not result in assetToken being unused and owned by this strategy\n * contract.\n */\n IERC4626 platform = IERC4626(platformAddress);\n return platform.previewRedeem(platform.balanceOf(address(this)));\n }\n\n /**\n * @notice Governor approves the ERC-4626 Tokenized Vault to spend the asset.\n */\n function safeApproveAllTokens() external override onlyGovernor {\n _approveBase();\n }\n\n function _approveBase() internal virtual {\n // Approval the asset to be transferred to the ERC-4626 Tokenized Vault.\n // Used by the ERC-4626 deposit() and mint() functions\n // slither-disable-next-line unused-return\n assetToken.approve(platformAddress, type(uint256).max);\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset)\n public\n view\n virtual\n override\n returns (bool)\n {\n return _asset == address(assetToken);\n }\n\n /**\n * @notice is not supported for this strategy as the asset and\n * ERC-4626 Tokenized Vault are set at deploy time.\n * @dev If the ERC-4626 Tokenized Vault needed to be changed, a new\n * contract would need to be deployed and the proxy updated.\n */\n function setPTokenAddress(address, address) external override onlyGovernor {\n revert(\"unsupported function\");\n }\n\n /**\n * @notice is not supported for this strategy as the asset and\n * ERC-4626 Tokenized Vault are set at deploy time.\n * @dev If the ERC-4626 Tokenized Vault needed to be changed, a new\n * contract would need to be deployed and the proxy updated.\n */\n function removePToken(uint256) external override onlyGovernor {\n revert(\"unsupported function\");\n }\n}\n" + }, + "contracts/strategies/Generalized4626USDTStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IUSDT {\n // Tether's approve does not return a bool like standard IERC20 contracts\n // slither-disable-next-line erc20-interface\n function approve(address _spender, uint256 _value) external;\n}\n\n/**\n * @title Generalized 4626 Strategy when asset is Tether USD (USDT)\n * @notice Investment strategy for ERC-4626 Tokenized Vaults for the USDT asset.\n * @author Origin Protocol Inc\n */\nimport { Generalized4626Strategy } from \"./Generalized4626Strategy.sol\";\n\ncontract Generalized4626USDTStrategy is Generalized4626Strategy {\n /**\n * @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n * and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n * @param _assetToken Address of the ERC-4626 asset token. eg frxETH or DAI\n */\n constructor(BaseStrategyConfig memory _baseConfig, address _assetToken)\n Generalized4626Strategy(_baseConfig, _assetToken)\n {}\n\n /// @dev Override for Tether as USDT does not return a bool on approve.\n /// Using assetToken.approve will fail as it expects a bool return value\n function _approveBase() internal virtual override {\n // Approval the asset to be transferred to the ERC-4626 Tokenized Vault.\n // Used by the ERC-4626 deposit() and mint() functions\n // slither-disable-next-line unused-return\n IUSDT(address(assetToken)).approve(platformAddress, type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/IAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface for Aaves Lending Pool\n * Documentation: https://developers.aave.com/#lendingpool\n */\ninterface IAaveLendingPool {\n /**\n * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.\n * - E.g. User deposits 100 USDC and gets in return 100 aUSDC\n * @param asset The address of the underlying asset to deposit\n * @param amount The amount to be deposited\n * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user\n * wants to receive them on his own wallet, or a different address if the beneficiary of aTokens\n * is a different wallet\n * @param referralCode Code used to register the integrator originating the operation, for potential rewards.\n * 0 if the action is executed directly by the user, without any middle-man\n **/\n function deposit(\n address asset,\n uint256 amount,\n address onBehalfOf,\n uint16 referralCode\n ) external;\n\n /**\n * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned\n * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC\n * @param asset The address of the underlying asset to withdraw\n * @param amount The underlying amount to be withdrawn\n * - Send the value type(uint256).max in order to withdraw the whole aToken balance\n * @param to Address that will receive the underlying, same as msg.sender if the user\n * wants to receive it on his own wallet, or a different address if the beneficiary is a\n * different wallet\n * @return The final amount withdrawn\n **/\n function withdraw(\n address asset,\n uint256 amount,\n address to\n ) external returns (uint256);\n}\n\n/**\n * @dev Interface for Aaves Lending Pool\n * Documentation: https://developers.aave.com/#lendingpooladdressesprovider\n */\ninterface ILendingPoolAddressesProvider {\n /**\n * @notice Get the current address for Aave LendingPool\n * @dev Lending pool is the core contract on which to call deposit\n */\n function getLendingPool() external view returns (address);\n}\n" + }, + "contracts/strategies/IAaveIncentivesController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IAaveIncentivesController {\n event RewardsAccrued(address indexed user, uint256 amount);\n\n event RewardsClaimed(\n address indexed user,\n address indexed to,\n uint256 amount\n );\n\n event RewardsClaimed(\n address indexed user,\n address indexed to,\n address indexed claimer,\n uint256 amount\n );\n\n event ClaimerSet(address indexed user, address indexed claimer);\n\n /*\n * @dev Returns the configuration of the distribution for a certain asset\n * @param asset The address of the reference asset of the distribution\n * @return The asset index, the emission per second and the last updated timestamp\n **/\n function getAssetData(address asset)\n external\n view\n returns (\n uint256,\n uint256,\n uint256\n );\n\n /**\n * @dev Whitelists an address to claim the rewards on behalf of another address\n * @param user The address of the user\n * @param claimer The address of the claimer\n */\n function setClaimer(address user, address claimer) external;\n\n /**\n * @dev Returns the whitelisted claimer for a certain address (0x0 if not set)\n * @param user The address of the user\n * @return The claimer address\n */\n function getClaimer(address user) external view returns (address);\n\n /**\n * @dev Configure assets for a certain rewards emission\n * @param assets The assets to incentivize\n * @param emissionsPerSecond The emission for each asset\n */\n function configureAssets(\n address[] calldata assets,\n uint256[] calldata emissionsPerSecond\n ) external;\n\n /**\n * @dev Called by the corresponding asset on any update that affects the rewards distribution\n * @param asset The address of the user\n * @param userBalance The balance of the user of the asset in the lending pool\n * @param totalSupply The total supply of the asset in the lending pool\n **/\n function handleAction(\n address asset,\n uint256 userBalance,\n uint256 totalSupply\n ) external;\n\n /**\n * @dev Returns the total of rewards of an user, already accrued + not yet accrued\n * @param user The address of the user\n * @return The rewards\n **/\n function getRewardsBalance(address[] calldata assets, address user)\n external\n view\n returns (uint256);\n\n /**\n * @dev Claims reward for an user, on all the assets of the lending pool,\n * accumulating the pending rewards\n * @param amount Amount of rewards to claim\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewards(\n address[] calldata assets,\n uint256 amount,\n address to\n ) external returns (uint256);\n\n /**\n * @dev Claims reward for an user on behalf, on all the assets of the\n * lending pool, accumulating the pending rewards. The caller must\n * be whitelisted via \"allowClaimOnBehalf\" function by the RewardsAdmin role manager\n * @param amount Amount of rewards to claim\n * @param user Address to check and claim rewards\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewardsOnBehalf(\n address[] calldata assets,\n uint256 amount,\n address user,\n address to\n ) external returns (uint256);\n\n /**\n * @dev returns the unclaimed rewards of the user\n * @param user the address of the user\n * @return the unclaimed user rewards\n */\n function getUserUnclaimedRewards(address user)\n external\n view\n returns (uint256);\n\n /**\n * @dev returns the unclaimed rewards of the user\n * @param user the address of the user\n * @param asset The asset to incentivize\n * @return the user index for the asset\n */\n function getUserAssetData(address user, address asset)\n external\n view\n returns (uint256);\n\n /**\n * @dev for backward compatibility with previous implementation of the Incentives controller\n */\n function REWARD_TOKEN() external view returns (address);\n\n /**\n * @dev for backward compatibility with previous implementation of the Incentives controller\n */\n function PRECISION() external view returns (uint8);\n\n /**\n * @dev Gets the distribution end timestamp of the emissions\n */\n function DISTRIBUTION_END() external view returns (uint256);\n}\n" + }, + "contracts/strategies/IAaveStakeToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IAaveStakedToken {\n function COOLDOWN_SECONDS() external returns (uint256);\n\n function UNSTAKE_WINDOW() external returns (uint256);\n\n function balanceOf(address addr) external returns (uint256);\n\n function redeem(address to, uint256 amount) external;\n\n function stakersCooldowns(address addr) external returns (uint256);\n\n function cooldown() external;\n}\n" + }, + "contracts/strategies/ICompound.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @dev Compound C Token interface\n * Documentation: https://compound.finance/developers/ctokens\n */\ninterface ICERC20 {\n /**\n * @notice The mint function transfers an asset into the protocol, which begins accumulating\n * interest based on the current Supply Rate for the asset. The user receives a quantity of\n * cTokens equal to the underlying tokens supplied, divided by the current Exchange Rate.\n * @param mintAmount The amount of the asset to be supplied, in units of the underlying asset.\n * @return 0 on success, otherwise an Error codes\n */\n function mint(uint256 mintAmount) external returns (uint256);\n\n /**\n * @notice Sender redeems cTokens in exchange for the underlying asset\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param redeemTokens The number of cTokens to redeem into underlying\n * @return uint 0=success, otherwise an error code.\n */\n function redeem(uint256 redeemTokens) external returns (uint256);\n\n /**\n * @notice The redeem underlying function converts cTokens into a specified quantity of the underlying\n * asset, and returns them to the user. The amount of cTokens redeemed is equal to the quantity of\n * underlying tokens received, divided by the current Exchange Rate. The amount redeemed must be less\n * than the user's Account Liquidity and the market's available liquidity.\n * @param redeemAmount The amount of underlying to be redeemed.\n * @return 0 on success, otherwise an error code.\n */\n function redeemUnderlying(uint256 redeemAmount) external returns (uint256);\n\n /**\n * @notice The user's underlying balance, representing their assets in the protocol, is equal to\n * the user's cToken balance multiplied by the Exchange Rate.\n * @param owner The account to get the underlying balance of.\n * @return The amount of underlying currently owned by the account.\n */\n function balanceOfUnderlying(address owner) external returns (uint256);\n\n /**\n * @notice Calculates the exchange rate from the underlying to the CToken\n * @dev This function does not accrue interest before calculating the exchange rate\n * @return Calculated exchange rate scaled by 1e18\n */\n function exchangeRateStored() external view returns (uint256);\n\n /**\n * @notice Get the token balance of the `owner`\n * @param owner The address of the account to query\n * @return The number of tokens owned by `owner`\n */\n function balanceOf(address owner) external view returns (uint256);\n\n /**\n * @notice Get the supply rate per block for supplying the token to Compound.\n */\n function supplyRatePerBlock() external view returns (uint256);\n\n /**\n * @notice Address of the Compound Comptroller.\n */\n function comptroller() external view returns (address);\n}\n" + }, + "contracts/strategies/IConvexDeposits.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IConvexDeposits {\n function deposit(\n uint256 _pid,\n uint256 _amount,\n bool _stake\n ) external returns (bool);\n\n function deposit(\n uint256 _amount,\n bool _lock,\n address _stakeAddress\n ) external;\n}\n" + }, + "contracts/strategies/ICRVMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICRVMinter {\n function mint(address gaugeAddress) external;\n}\n" + }, + "contracts/strategies/ICurveETHPoolV1.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurveETHPoolV1 {\n event AddLiquidity(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event ApplyNewFee(uint256 fee);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event CommitNewFee(uint256 new_fee);\n event RampA(\n uint256 old_A,\n uint256 new_A,\n uint256 initial_time,\n uint256 future_time\n );\n event RemoveLiquidity(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 token_supply\n );\n event RemoveLiquidityImbalance(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event RemoveLiquidityOne(\n address indexed provider,\n uint256 token_amount,\n uint256 coin_amount,\n uint256 token_supply\n );\n event StopRampA(uint256 A, uint256 t);\n event TokenExchange(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event Transfer(\n address indexed sender,\n address indexed receiver,\n uint256 value\n );\n\n function A() external view returns (uint256);\n\n function A_precise() external view returns (uint256);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_liquidity(uint256[2] memory _amounts, uint256 _min_mint_amount)\n external\n payable\n returns (uint256);\n\n function add_liquidity(\n uint256[2] memory _amounts,\n uint256 _min_mint_amount,\n address _receiver\n ) external payable returns (uint256);\n\n function admin_action_deadline() external view returns (uint256);\n\n function admin_balances(uint256 i) external view returns (uint256);\n\n function admin_fee() external view returns (uint256);\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function apply_new_fee() external;\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function balances(uint256 arg0) external view returns (uint256);\n\n function calc_token_amount(uint256[2] memory _amounts, bool _is_deposit)\n external\n view\n returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _burn_amount, int128 i)\n external\n view\n returns (uint256);\n\n function coins(uint256 arg0) external view returns (address);\n\n function commit_new_fee(uint256 _new_fee) external;\n\n function decimals() external view returns (uint256);\n\n function ema_price() external view returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external payable returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external payable returns (uint256);\n\n function fee() external view returns (uint256);\n\n function future_A() external view returns (uint256);\n\n function future_A_time() external view returns (uint256);\n\n function future_fee() external view returns (uint256);\n\n function get_balances() external view returns (uint256[2] memory);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n\n function get_p() external view returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function initial_A() external view returns (uint256);\n\n function initial_A_time() external view returns (uint256);\n\n function initialize(\n string memory _name,\n string memory _symbol,\n address[4] memory _coins,\n uint256[4] memory _rate_multipliers,\n uint256 _A,\n uint256 _fee\n ) external;\n\n function last_price() external view returns (uint256);\n\n function ma_exp_time() external view returns (uint256);\n\n function ma_last_time() external view returns (uint256);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function price_oracle() external view returns (uint256);\n\n function ramp_A(uint256 _future_A, uint256 _future_time) external;\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[2] memory _min_amounts\n ) external returns (uint256[2] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[2] memory _min_amounts,\n address _receiver\n ) external returns (uint256[2] memory);\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burn_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burn_amount,\n address _receiver\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received,\n address _receiver\n ) external returns (uint256);\n\n function set_ma_exp_time(uint256 _ma_exp_time) external;\n\n function stop_ramp_A() external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw_admin_fees() external;\n}\n" + }, + "contracts/strategies/ICurveGauge.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurveGauge {\n function balanceOf(address account) external view returns (uint256);\n\n function deposit(uint256 value, address account) external;\n\n function withdraw(uint256 value) external;\n}\n" + }, + "contracts/strategies/ICurveMetaPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface ICurveMetaPool {\n function add_liquidity(uint256[2] calldata amounts, uint256 min_mint_amount)\n external\n returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function remove_liquidity(uint256 _amount, uint256[2] calldata min_amounts)\n external\n returns (uint256[2] calldata);\n\n function remove_liquidity_one_coin(\n uint256 _token_amount,\n int128 i,\n uint256 min_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[2] calldata amounts,\n uint256 max_burn_amount\n ) external returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _token_amount, int128 i)\n external\n view\n returns (uint256);\n\n function balances(uint256 i) external view returns (uint256);\n\n function calc_token_amount(uint256[2] calldata amounts, bool deposit)\n external\n view\n returns (uint256);\n\n function base_pool() external view returns (address);\n\n function fee() external view returns (uint256);\n\n function coins(uint256 i) external view returns (address);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 dx,\n uint256 min_dy\n ) external returns (uint256);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n}\n" + }, + "contracts/strategies/ICurvePool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurvePool {\n function get_virtual_price() external view returns (uint256);\n\n function add_liquidity(uint256[3] calldata _amounts, uint256 _min) external;\n\n function balances(uint256) external view returns (uint256);\n\n function calc_token_amount(uint256[3] calldata _amounts, bool _deposit)\n external\n returns (uint256);\n\n function fee() external view returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _amount,\n int128 _index,\n uint256 _minAmount\n ) external;\n\n function remove_liquidity(\n uint256 _amount,\n uint256[3] calldata _minWithdrawAmounts\n ) external;\n\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n external\n view\n returns (uint256);\n\n function exchange(\n uint256 i,\n uint256 j,\n uint256 dx,\n uint256 min_dy\n ) external returns (uint256);\n\n function coins(uint256 _index) external view returns (address);\n\n function remove_liquidity_imbalance(\n uint256[3] calldata _amounts,\n uint256 maxBurnAmount\n ) external;\n}\n" + }, + "contracts/strategies/IRewardStaking.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IRewardStaking {\n function stakeFor(address, uint256) external;\n\n function stake(uint256) external;\n\n function withdraw(uint256 amount, bool claim) external;\n\n function withdrawAndUnwrap(uint256 amount, bool claim) external;\n\n function earned(address account) external view returns (uint256);\n\n function getReward() external;\n\n function getReward(address _account, bool _claimExtras) external;\n\n function extraRewardsLength() external returns (uint256);\n\n function extraRewards(uint256 _pid) external returns (address);\n\n function rewardToken() external returns (address);\n\n function balanceOf(address account) external view returns (uint256);\n}\n" + }, + "contracts/strategies/MorphoAaveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Morpho Aave Strategy\n * @notice Investment strategy for investing stablecoins via Morpho (Aave)\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { IMorpho } from \"../interfaces/morpho/IMorpho.sol\";\nimport { ILens } from \"../interfaces/morpho/ILens.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MorphoAaveStrategy is InitializableAbstractStrategy {\n address public constant MORPHO = 0x777777c9898D384F785Ee44Acfe945efDFf5f3E0;\n address public constant LENS = 0x507fA343d0A90786d86C7cd885f5C49263A91FF4;\n\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @dev Initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Approve the spending of all assets by main Morpho contract,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address asset = assetsMapped[i];\n\n // Safe approval\n IERC20(asset).safeApprove(MORPHO, 0);\n IERC20(asset).safeApprove(MORPHO, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset\n * We need to approve and allow Morpho to move them\n * @param _asset Address of the asset to approve\n * @param _pToken The pToken for the approval\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n IERC20(_asset).safeApprove(MORPHO, 0);\n IERC20(_asset).safeApprove(MORPHO, type(uint256).max);\n }\n\n /**\n * @dev Collect accumulated rewards and send them to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Morpho Aave-v2 doesn't distribute reward tokens\n // solhint-disable-next-line max-line-length\n // Ref: https://developers.morpho.xyz/interact-with-morpho/get-started/interact-with-morpho/claim-rewards#morpho-aave-v2\n }\n\n /**\n * @dev Get the amount of rewards pending to be collected from the protocol\n */\n function getPendingRewards() external view returns (uint256 balance) {\n // Morpho Aave-v2 doesn't distribute reward tokens\n // solhint-disable-next-line max-line-length\n // Ref: https://developers.morpho.xyz/interact-with-morpho/get-started/interact-with-morpho/claim-rewards#morpho-aave-v2\n return 0;\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n\n address pToken = address(_getPTokenFor(_asset));\n\n IMorpho(MORPHO).supply(\n pToken,\n address(this), // the address of the user you want to supply on behalf of\n _amount\n );\n emit Deposit(_asset, pToken, _amount);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Morpho\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Morpho\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n address pToken = address(_getPTokenFor(_asset));\n\n IMorpho(MORPHO).withdraw(pToken, _amount);\n emit Withdrawal(_asset, pToken, _amount);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = _checkBalance(assetsMapped[i]);\n if (balance > 0) {\n _withdraw(vaultAddress, assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Return total value of an asset held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n return _checkBalance(_asset);\n }\n\n function _checkBalance(address _asset)\n internal\n view\n returns (uint256 balance)\n {\n address pToken = address(_getPTokenFor(_asset));\n\n // Total value represented by decimal position of underlying token\n (, , balance) = ILens(LENS).getCurrentSupplyBalanceInOf(\n pToken,\n address(this)\n );\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Get the pToken wrapped in the IERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return pToken Corresponding pToken to this asset\n */\n function _getPTokenFor(address _asset) internal view returns (IERC20) {\n address pToken = assetToPToken[_asset];\n require(pToken != address(0), \"pToken does not exist\");\n return IERC20(pToken);\n }\n}\n" + }, + "contracts/strategies/MorphoCompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Morpho Compound Strategy\n * @notice Investment strategy for investing stablecoins via Morpho (Compound)\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, AbstractCompoundStrategy, InitializableAbstractStrategy } from \"./AbstractCompoundStrategy.sol\";\nimport { IMorpho } from \"../interfaces/morpho/IMorpho.sol\";\nimport { ILens } from \"../interfaces/morpho/ILens.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MorphoCompoundStrategy is AbstractCompoundStrategy {\n address public constant MORPHO = 0x8888882f8f843896699869179fB6E4f7e3B58888;\n address public constant LENS = 0x930f1b46e1D081Ec1524efD95752bE3eCe51EF67;\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @dev Initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Approve the spending of all assets by main Morpho contract,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address asset = assetsMapped[i];\n\n // Safe approval\n IERC20(asset).safeApprove(MORPHO, 0);\n IERC20(asset).safeApprove(MORPHO, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset\n * We need to approve and allow Morpho to move them\n * @param _asset Address of the asset to approve\n * @param _pToken The pToken for the approval\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n IERC20(_asset).safeApprove(MORPHO, 0);\n IERC20(_asset).safeApprove(MORPHO, type(uint256).max);\n }\n\n /**\n * @dev Collect accumulated rewards and send them to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n /**\n * Gas considerations. We could query Morpho LENS's `getUserUnclaimedRewards` for each\n * cToken separately and only claimRewards where it is economically feasible. Each call\n * (out of 3) costs ~60k gas extra.\n *\n * Each extra cToken in the `poolTokens` of `claimRewards` function makes that call\n * 89-120k more expensive gas wise.\n *\n * With Lens query in case where:\n * - there is only 1 reward token to collect. Net gas usage in best case would be\n * 3*60 - 2*120 = -60k -> saving 60k gas\n * - there are 2 reward tokens to collect. Net gas usage in best case would be\n * 3*60 - 120 = 60k -> more expensive for 60k gas\n * - there are 3 reward tokens to collect. Net gas usage in best case would be\n * 3*60 = 180k -> more expensive for 180k gas\n *\n * For the above reasoning such \"optimization\" is not implemented\n */\n\n address[] memory poolTokens = new address[](assetsMapped.length);\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n poolTokens[i] = assetToPToken[assetsMapped[i]];\n }\n\n // slither-disable-next-line unused-return\n IMorpho(MORPHO).claimRewards(\n poolTokens, // The addresses of the underlying protocol's pools to claim rewards from\n false // Whether to trade the accrued rewards for MORPHO token, with a premium\n );\n\n // Transfer COMP to Harvester\n IERC20 rewardToken = IERC20(rewardTokenAddresses[0]);\n uint256 balance = rewardToken.balanceOf(address(this));\n emit RewardTokenCollected(\n harvesterAddress,\n rewardTokenAddresses[0],\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n\n /**\n * @dev Get the amount of rewards pending to be collected from the protocol\n */\n function getPendingRewards() external view returns (uint256 balance) {\n address[] memory poolTokens = new address[](assetsMapped.length);\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n poolTokens[i] = assetToPToken[assetsMapped[i]];\n }\n\n return ILens(LENS).getUserUnclaimedRewards(poolTokens, address(this));\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n\n IMorpho(MORPHO).supply(\n address(_getCTokenFor(_asset)),\n address(this), // the address of the user you want to supply on behalf of\n _amount\n );\n emit Deposit(_asset, address(_getCTokenFor(_asset)), _amount);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Morpho\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Morpho\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n address pToken = assetToPToken[_asset];\n\n IMorpho(MORPHO).withdraw(pToken, _amount);\n emit Withdrawal(_asset, address(_getCTokenFor(_asset)), _amount);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = _checkBalance(assetsMapped[i]);\n if (balance > 0) {\n _withdraw(vaultAddress, assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Return total value of an asset held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n return _checkBalance(_asset);\n }\n\n function _checkBalance(address _asset)\n internal\n view\n returns (uint256 balance)\n {\n address pToken = assetToPToken[_asset];\n\n // Total value represented by decimal position of underlying token\n (, , balance) = ILens(LENS).getCurrentSupplyBalanceInOf(\n pToken,\n address(this)\n );\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with\n /// `platformAddress` not used so empty address\n /// `vaultAddress` the address of the OETH Vault contract\n /// @param _wethAddress Address of the WETH Token contract\n /// @param _ssvToken Address of the SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconProofs,\n _beaconGenesisTimestamp\n )\n {\n SSV_TOKEN = _ssvToken;\n\n // Make sure nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// @param _rewardTokenAddresses Not used so empty array\n /// @param _assets Not used so empty array\n /// @param _pTokens Not used so empty array\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n safeApproveAllTokens();\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH that was transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// @param _recipient Address to receive withdrawn assets.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH to withdraw.\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(WETH, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Accounts for all the assets managed by this strategy which includes:\n /// 1. The current WETH in this strategy contract\n /// 2. The last verified ETH balance, total deposits and total validator balances\n /// @param _asset Address of WETH asset.\n /// @return balance Total value in ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by the strategy.\n /// @param _asset The address of the WETH token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for validator registration.\n function safeApproveAllTokens() public override {\n // Approves the SSV Network contract to transfer SSV tokens when validators are registered\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n * The new ETH will be accounted for in `checkBalance` after the next snapBalances and verifyBalances txs.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n /// @notice is not supported for this strategy as there is no platform token.\n function setPTokenAddress(address, address) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @notice is not supported for this strategy as there is no platform token.\n function removePToken(uint256) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @dev This strategy does not use a platform token like the old Aave and Compound strategies.\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instead of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract.\n /// Withdrawals from validators also accumulate as ETH in this strategy contract.\n /// It's too complex to separate the rewards from withdrawals so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can now regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingView.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/**\n * @title Viewing contract for the Compounding Staking Strategy.\n * @author Origin Protocol Inc\n */\ncontract CompoundingStakingStrategyView {\n /// @notice The address of the Compounding Staking Strategy contract\n CompoundingValidatorManager public immutable stakingStrategy;\n\n constructor(address _stakingStrategy) {\n stakingStrategy = CompoundingValidatorManager(_stakingStrategy);\n }\n\n struct ValidatorView {\n bytes32 pubKeyHash;\n uint64 index;\n CompoundingValidatorManager.ValidatorState state;\n }\n\n struct DepositView {\n uint256 depositID;\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint256 withdrawableEpoch;\n }\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return validators An array of `ValidatorView` containing the public key hash, validator index and state.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorView[] memory validators)\n {\n uint256 validatorCount = stakingStrategy.verifiedValidatorsLength();\n validators = new ValidatorView[](validatorCount);\n for (uint256 i = 0; i < validatorCount; ++i) {\n bytes32 pubKeyHash = stakingStrategy.verifiedValidators(i);\n (\n CompoundingValidatorManager.ValidatorState state,\n uint64 index\n ) = stakingStrategy.validator(pubKeyHash);\n validators[i] = ValidatorView({\n pubKeyHash: pubKeyHash,\n index: index,\n state: state\n });\n }\n }\n\n /// @notice Returns the deposits that are still to be verified.\n /// These may or may not have been processed by the beacon chain.\n /// @return pendingDeposits An array of `DepositView` containing the deposit ID, public key hash,\n /// amount in Gwei and the slot of the deposit.\n function getPendingDeposits()\n external\n view\n returns (DepositView[] memory pendingDeposits)\n {\n uint256 depositsCount = stakingStrategy.depositListLength();\n pendingDeposits = new DepositView[](depositsCount);\n for (uint256 i = 0; i < depositsCount; ++i) {\n (\n bytes32 pubKeyHash,\n uint64 amountGwei,\n uint64 slot,\n ,\n ,\n uint256 withdrawableEpoch\n ) = stakingStrategy.deposits(stakingStrategy.depositList(i));\n pendingDeposits[i] = DepositView({\n depositID: stakingStrategy.depositList(i),\n pubKeyHash: pubKeyHash,\n amountGwei: amountGwei,\n slot: slot,\n withdrawableEpoch: withdrawableEpoch\n });\n }\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable {\n using SafeERC20 for IERC20;\n\n /// @dev The amount of ETH in wei that is required for a deposit to a new validator.\n /// Initially this is 32 ETH, but will be reduced to 1 ETH after P2P's APIs have been updated\n /// to support deposits of 1 ETH.\n uint256 internal constant DEPOSIT_AMOUNT_WEI = 32 ether;\n /// @dev The maximum number of deposits that are waiting to be verified as processed on the beacon chain.\n uint256 internal constant MAX_DEPOSITS = 12;\n /// @dev The maximum number of validators that can be verified.\n uint256 internal constant MAX_VERIFIED_VALIDATORS = 48;\n /// @dev The default withdrawable epoch value on the Beacon chain.\n /// A value in the far future means the validator is not exiting.\n uint64 internal constant FAR_FUTURE_EPOCH = type(uint64).max;\n /// @dev The number of seconds between each beacon chain slot.\n uint64 internal constant SLOT_DURATION = 12;\n /// @dev The number of slots in each beacon chain epoch.\n uint64 internal constant SLOTS_PER_EPOCH = 32;\n /// @dev Minimum time in seconds to allow snapped balances to be verified.\n /// Set to 1 epoch as the pending deposits only changes every epoch.\n /// That's also enough time to generate the proofs and call `verifyBalances`.\n uint64 internal constant SNAP_BALANCES_DELAY =\n SLOTS_PER_EPOCH * SLOT_DURATION;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n /// @notice The timestamp of the Beacon chain genesis.\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\n uint64 public immutable BEACON_GENESIS_TIMESTAMP;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// Deposit data for new compounding validators.\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified and is ready to be staked\n }\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountWei Amount of ETH in wei that has been deposited to the beacon chain deposit contract\n /// @param blockNumber Block number when the deposit was made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either PENDING or VERIFIED\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint32 depositIndex;\n DepositStatus status;\n uint64 withdrawableEpoch;\n }\n /// @notice Restricts to only one deposit to an unverified validator at a time.\n /// This is to limit front-running attacks of deposits to the beacon chain contract.\n bool public firstDeposit;\n /// @notice Unique identifier of the next validator deposit.\n uint128 public nextDepositID;\n /// @notice Mapping of the deposit ID to the deposit data\n mapping(uint256 => DepositData) public deposits;\n /// @notice List of strategy deposit IDs to a validator.\n /// The list can be for deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n /// The list may not be ordered by time of deposit.\n /// Removed deposits will move the last deposit to the removed index.\n uint256[] public depositList;\n\n // Validator data\n enum ValidatorState {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITING, // The validator has been requested to exit or has been verified as forced exit\n EXITED, // The validator has been verified to have a zero balance\n REMOVED, // validator has funds withdrawn to the EigenPod and is removed from the SSV\n INVALID // The validator has been front-run and the withdrawal address is not this strategy\n }\n\n struct ValidatorData {\n ValidatorState state;\n uint64 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n bytes32[] public verifiedValidators;\n /// @notice Mapping of the hash of the validator's public key to the validator state and index.\n /// Uses the Beacon chain hashing for BLSPubkey which is sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => ValidatorData) public validator;\n\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n uint64 timestamp;\n uint128 ethBalance;\n }\n /// @notice Mapping of the block root to the balances at that slot\n mapping(bytes32 => Balances) public snappedBalances;\n /// @notice The timestamp of the last snapshot taken\n uint64 public lastSnapTimestamp;\n /// @notice The last verified ETH balance of the strategy\n uint128 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[50] private __gap;\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event FirstDepositReset();\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n uint256 indexed depositID,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint64 indexed validatorIndex\n );\n event ValidatorInvalid(bytes32 indexed pubKeyHash);\n event DepositVerified(uint256 indexed depositID, uint256 amountWei);\n event DepositToValidatorExiting(\n uint256 indexed depositID,\n uint256 amountWei,\n uint64 withdrawableEpoch\n );\n event DepositValidatorExited(uint256 indexed depositID, uint256 amountWei);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(bytes32 indexed blockRoot, uint256 ethBalance);\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 ethBalance\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_PROOFS = _beaconProofs;\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\n\n require(\n block.timestamp > _beaconGenesisTimestamp,\n \"Invalid genesis timestamp\"\n );\n }\n\n /**\n *\n * Admin Functions\n *\n */\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Reset the `firstDeposit` flag to false so deposits to unverified validators can be made again.\n function resetFirstDeposit() external onlyGovernor {\n require(firstDeposit, \"No first deposit\");\n\n firstDeposit = false;\n\n emit FirstDepositReset();\n }\n\n /**\n *\n * Validator Management\n *\n */\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validator[pubKeyHash].state == ValidatorState.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validator[pubKeyHash].state = ValidatorState.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n struct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n }\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validatorStakeData validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validatorStakeData,\n uint64 depositAmountGwei\n ) external onlyRegistrator {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(depositList.length < MAX_DEPOSITS, \"Max deposits\");\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validatorStakeData.pubkey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.VERIFIED),\n \"Not registered or verified\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n if (currentState == ValidatorState.REGISTERED) {\n // Can only have one pending deposit to an unverified validator at a time.\n // This is to limit front-running deposit attacks to a single deposit.\n // The exiting deposit needs to be verified before another deposit can be made.\n // If there was a front-running attack, the validator needs to be verified as invalid\n // and the Governor calls `resetFirstDeposit` to set `firstDeposit` to false.\n require(!firstDeposit, \"Existing first deposit\");\n // Limits the amount of ETH that can be at risk from a front-running deposit attack.\n require(\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n // Limits the number of validator balance proofs to verifyBalances\n require(\n verifiedValidators.length + 1 < MAX_VERIFIED_VALIDATORS,\n \"Max validators\"\n );\n\n // Flag a deposit to an unverified validator so only no other deposits can be made\n // to an unverified validator.\n firstDeposit = true;\n }\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validatorStakeData.pubkey,\n withdrawalCredentials,\n validatorStakeData.signature,\n validatorStakeData.depositDataRoot\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == ValidatorState.REGISTERED) {\n validator[pubKeyHash].state = ValidatorState.STAKED;\n }\n\n /// After the Pectra upgrade the validators have a new restriction when proposing\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\n /// forward. Each slot is created at strict 12 second intervals and those slots can\n /// either have blocks attached to them or not. This way using the block.timestamp\n /// the slot number can easily be calculated.\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\n BEACON_GENESIS_TIMESTAMP) / SLOT_DURATION;\n\n // Store the deposit data for verifyDeposit and verifyBalances\n uint256 depositID = nextDepositID++;\n deposits[depositID] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n slot: depositSlot,\n depositIndex: SafeCast.toUint32(depositList.length),\n status: DepositStatus.PENDING,\n withdrawableEpoch: FAR_FUTURE_EPOCH\n });\n depositList.push(depositID);\n\n emit ETHStaked(\n pubKeyHash,\n depositID,\n validatorStakeData.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then only the amount in excess of 32 ETH will be withdrawn.\n /// Only the Registrator can call this function.\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\n /// If no ETH balance, the tx will revert.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n payable\n onlyRegistrator\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n require(\n currentState == ValidatorState.VERIFIED,\n \"Validator not verified\"\n );\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n // If a full withdrawal (validator exit)\n if (amountGwei == 0) {\n // Store the validator state as exiting so no more deposits can be made to it.\n validator[pubKeyHash].state = ValidatorState.EXITING;\n }\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove the validator from the SSV Cluster after:\n /// - the validator has been exited from `validatorWithdrawal` or slashed\n /// - the validator has incorrectly registered and can not be staked to\n /// - the initial deposit was front-run and the withdrawal address is not this strategy's address.\n /// Make sure `validatorWithdrawal` is called with a zero amount and the validator has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.EXITED ||\n currentState == ValidatorState.INVALID,\n \"Validator not regd or exited\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validator[pubKeyHash].state = ValidatorState.REMOVED;\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /**\n *\n * SSV Management\n *\n */\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be withdrawn from the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /**\n *\n * Beacon Chain Proofs\n *\n */\n\n /// @notice Verifies a validator's index to its public key.\n /// Adds to the list of verified validators if the validator's withdrawal address is this strategy's address.\n /// Marks the validator as invalid and removes the deposit if the withdrawal address is not this strategy's address.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param withdrawalAddress The withdrawal address of the validator which should be this strategy's address.\n /// If the withdrawal address is not this strategy's address, the initial deposit was front-run\n /// and the validator is marked as invalid.\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n address withdrawalAddress,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validator[pubKeyHash].state == ValidatorState.STAKED,\n \"Validator not staked\"\n );\n\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key.\n // Also verify the validator's withdrawal credential points to the `withdrawalAddress`.\n IBeaconProofs(BEACON_PROOFS).verifyValidator(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n withdrawalAddress\n );\n\n // If the initial deposit was front-run and the withdrawal address is not this strategy\n if (withdrawalAddress != address(this)) {\n validator[pubKeyHash] = ValidatorData({\n state: ValidatorState.INVALID,\n index: validatorIndex\n });\n\n // Find and remove the deposit as the funds can not be recovered\n uint256 depositCount = depositList.length;\n for (uint256 i = 0; i < depositCount; i++) {\n DepositData memory deposit = deposits[depositList[i]];\n if (deposit.pubKeyHash == pubKeyHash) {\n _removeDeposit(depositList[i], deposit);\n break;\n }\n }\n\n // Leave the `firstDeposit` flag as true so no more deposits to unverified validators can be made.\n // The Governor has to reset the `firstDeposit` to false before another deposit to\n // an unverified validator can be made.\n // The Governor can set a new `validatorRegistrator` if they suspect it has been compromised.\n\n emit ValidatorInvalid(pubKeyHash);\n return;\n }\n\n // Store the validator state as verified\n validator[pubKeyHash] = ValidatorData({\n state: ValidatorState.VERIFIED,\n index: validatorIndex\n });\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(pubKeyHash);\n\n // Reset the firstDeposit flag as the first deposit to an unverified validator has been verified.\n firstDeposit = false;\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n struct FirstPendingDepositProofData {\n uint64 slot;\n uint64 validatorIndex;\n bytes32 pubKeyHash;\n bytes pendingDepositPubKeyProof;\n bytes withdrawableEpochProof;\n bytes validatorPubKeyProof;\n }\n\n struct DepositValidatorProofData {\n uint64 withdrawableEpoch;\n bytes withdrawableEpochProof;\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n ///\n /// Important: this function has a limitation where the `verificationSlot` that is passed by the off-chain\n /// verifier requires a slot immediately after it to propose a block otherwise the `BeaconRoots.parentBlockRoot`\n /// will fail. This shouldn't be a problem, since by the current behaviour of beacon chain only 1%-3% slots\n /// don't propose a block.\n /// @param depositID The deposit ID emitted in `ETHStaked` from the `stakeEth` function.\n /// @param depositProcessedSlot Any slot on or after the strategy's deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\n /// set for the next block timestamp in 12 seconds time.\n /// @param firstDepositValidatorCreatedSlot The slot on or after when the validator of the first pending deposit\n /// was created on the beacon chain. This is used to verify the validator has not exited.\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n uint256 depositID,\n uint64 depositProcessedSlot,\n uint64 firstDepositValidatorCreatedSlot,\n FirstPendingDepositProofData calldata firstPendingDeposit,\n DepositValidatorProofData calldata strategyValidatorData\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositID];\n ValidatorData memory strategyValidator = validator[deposit.pubKeyHash];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n strategyValidator.state == ValidatorState.VERIFIED,\n \"Validator not verified\"\n );\n // The verification slot must be after the deposit's slot.\n // This is needed for when the deposit queue is empty.\n require(deposit.slot < depositProcessedSlot, \"Slot not after deposit\");\n require(\n depositProcessedSlot <= firstDepositValidatorCreatedSlot,\n \"Invalid verification slots\"\n );\n\n // Get the parent beacon block root of the next block which is the block root of the deposit verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 depositBlockRoot = BeaconRoots.parentBlockRoot(\n _calcNextBlockTimestamp(depositProcessedSlot)\n );\n\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n depositBlockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.pubKeyHash,\n firstPendingDeposit.pendingDepositPubKeyProof\n );\n\n // If the deposit queue is not empty\n if (!isDepositQueueEmpty) {\n // Get the parent beacon block root of the next block which is\n // the block root of the validator verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 validatorBlockRoot = BeaconRoots.parentBlockRoot(\n _calcNextBlockTimestamp(firstDepositValidatorCreatedSlot)\n );\n\n // Verify the validator of the first pending deposit is not exiting.\n // If it is exiting we can't be sure this deposit has not been postponed in the deposit queue.\n // Hence we can not verify if the strategy's deposit has been processed or not.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n validatorBlockRoot,\n firstPendingDeposit.validatorIndex,\n firstPendingDeposit.pubKeyHash,\n FAR_FUTURE_EPOCH,\n firstPendingDeposit.withdrawableEpochProof,\n firstPendingDeposit.validatorPubKeyProof\n );\n }\n\n // Verify the withdrawableEpoch on the validator of the strategy's deposit\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n depositBlockRoot,\n strategyValidator.index,\n strategyValidatorData.withdrawableEpoch,\n strategyValidatorData.withdrawableEpochProof\n );\n\n // If the validator is exiting because it has been slashed\n if (strategyValidatorData.withdrawableEpoch != FAR_FUTURE_EPOCH) {\n // Store the exit epoch in the deposit data\n deposit.withdrawableEpoch = strategyValidatorData.withdrawableEpoch;\n\n emit DepositToValidatorExiting(\n depositID,\n uint256(deposit.amountGwei) * 1 gwei,\n strategyValidatorData.withdrawableEpoch\n );\n\n validator[deposit.pubKeyHash].state = ValidatorState.EXITING;\n\n // Leave the deposit status as PENDING\n return;\n }\n\n // solhint-disable max-line-length\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the deposit queue is empty then our deposit must have been processed on the beacon chain.\n // The deposit slot can be zero for validators consolidating to a compounding validator or 0x01 validator\n // being promoted to a compounding one. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // We can not guarantee that the deposit has been processed in that case.\n // solhint-enable max-line-length\n require(\n deposit.slot < firstPendingDeposit.slot || isDepositQueueEmpty,\n \"Deposit likely not processed\"\n );\n\n // Remove the deposit now it has been verified as processed on the beacon chain.\n _removeDeposit(depositID, deposit);\n\n emit DepositVerified(depositID, uint256(deposit.amountGwei) * 1 gwei);\n }\n\n function _removeDeposit(uint256 depositID, DepositData memory deposit)\n internal\n {\n // After verifying the proof, update the contract storage\n deposits[depositID].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n uint256 lastDeposit = depositList[depositList.length - 1];\n depositList[deposit.depositIndex] = lastDeposit;\n deposits[lastDeposit].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositList.pop();\n }\n\n /// @dev Calculates the timestamp of the next execution block from the given slot.\n /// @param slot The beacon chain slot number used for merkle proof verification.\n function _calcNextBlockTimestamp(uint64 slot)\n internal\n view\n returns (uint64)\n {\n // Calculate the next block timestamp from the slot.\n return SLOT_DURATION * slot + BEACON_GENESIS_TIMESTAMP + SLOT_DURATION;\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block and beacon block root\n /// of the slot that is associated with the previous block.\n ///\n /// When snapping / verifying balance it is of a high importance that there is no\n /// miss-match in respect to ETH that is held by the contract and balances that are\n /// verified on the validators.\n ///\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\n /// constructing a block on the beacon chain consist of:\n /// - process_withdrawals: ETH is deducted from the validator's balance\n /// - process_execution_payload: immediately after the previous step executing all the\n /// transactions\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\n /// contained in the withdrawal credentials of the exited validators\n ///\n /// That means that balance increases which are part of the post-block execution state are\n /// done within the block, but the transaction that are contained within that block can not\n /// see / interact with the balance from the exited validators. Only transactions in the\n /// next block can do that.\n ///\n /// When snap balances is performed the state of the chain is snapped across 2 separate\n /// chain states:\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\n /// will be referred to as Y - 1 as it makes no difference in the argument\n ///\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\n /// miss-counting ETH or much more dangerous double counting of the ETH.\n ///\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\n /// - ETH balance in the contract on block X\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\n /// - ETH balance in validators that are active in slot Y - 1\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\n /// and have their balance visible to transactions in slot Y and corresponding block X\n /// (or sooner)\n ///\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\n ///\n /// *ETH balance in the contract on block X*\n ///\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\n ///\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\n /// would result in double counting the `stakedEth` since it would be present once in the\n /// snapped contract balance and the second time in deposit storage variables.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\n ///\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\n /// contract at block Z. The execution layer doesn't have direct access to the state of\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\n /// and could also be part of the validator balances. It does that by verifying that at\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\n /// the last snap till now all are still in queue. Which ensures they can not be part of\n /// the validator balances in later steps.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in validators that are active in slot Y - 1*\n ///\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\n /// correctness.\n ///\n /// This behaviour is correct.\n ///\n /// *The withdrawn validators*\n ///\n /// The withdrawn validators could have their balances deducted in any slot before slot\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\n /// look at the \"worst case scenario\" where the validator withdrawal is processed in the\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\n /// even if the validator exits at the latest possible time it is paramount that the ETH\n /// balance on the execution layer is recorded in the next block. Correctly accounting\n /// for the withdrawn ETH.\n ///\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\n /// validator balance verification as well as execution layer contract balance snap.\n ///\n /// This behaviour is correct.\n ///\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n function snapBalances() external {\n uint64 currentTimestamp = SafeCast.toUint64(block.timestamp);\n require(\n lastSnapTimestamp + SNAP_BALANCES_DELAY < currentTimestamp,\n \"Snap too soon\"\n );\n\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(currentTimestamp);\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the balances in the mapping\n snappedBalances[blockRoot] = Balances({\n timestamp: currentTimestamp,\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n // Store the snapped timestamp\n lastSnapTimestamp = currentTimestamp;\n\n emit BalancesSnapped(blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct BalanceProofs {\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n /// @param snapBlockRoot The beacon block root emitted from `snapBalance` in `BalancesSnapped`.\n /// @param balanceProofs a `BalanceProofs` struct containing the following:\n /// balancesContainerRoot - the merkle root of the balances container\n /// balancesContainerProof - The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// validatorBalanceLeaves - Array of leaf nodes containing the validator balance with three other balances.\n /// validatorBalanceProofs - Array of merkle proofs for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(\n bytes32 snapBlockRoot,\n uint64 validatorVerificationBlockTimestamp,\n FirstPendingDepositProofData calldata firstPendingDeposit,\n BalanceProofs calldata balanceProofs\n ) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalances[snapBlockRoot];\n // Check the balances are the latest\n require(lastSnapTimestamp > 0, \"No snapped balances\");\n require(balancesMem.timestamp == lastSnapTimestamp, \"Stale snap\");\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n balanceProofs.validatorBalanceProofs.length ==\n verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n balanceProofs.validatorBalanceLeaves.length ==\n verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n snapBlockRoot,\n balanceProofs.balancesContainerRoot,\n balanceProofs.balancesContainerProof\n );\n\n // for each validator in reserve order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n balanceProofs.balancesContainerRoot,\n balanceProofs.validatorBalanceLeaves[i],\n balanceProofs.validatorBalanceProofs[i],\n validator[verifiedValidators[i]].index\n );\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n // This could have been in VERIFIED or EXITING state\n validator[verifiedValidators[i]].state = ValidatorState\n .EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += validatorBalanceGwei * 1 gwei;\n }\n }\n\n uint256 depositsCount = depositList.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification.\n // This section is after the validator balance verifications so an exited validator will be marked\n // as EXITED before the deposits are verified. If there was a deposit to an exited validator\n // then the deposit can only be removed once the validator is fully exited.\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n snapBlockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.pubKeyHash,\n firstPendingDeposit.pendingDepositPubKeyProof\n );\n\n // If there are no deposits in the beacon chain queue then our deposits must have been processed.\n // If the deposits have been processed, each deposit will need to be verified with `verifyDeposit`\n require(!isDepositQueueEmpty, \"Deposits have been processed\");\n\n // The verification of the validator the first pending deposit is for must be on or after when\n // `snapBalances` was called.\n require(\n balancesMem.timestamp <= validatorVerificationBlockTimestamp,\n \"Invalid validator timestamp\"\n );\n\n // Verify the validator of the first pending deposit is not exiting by checking\n // the withdrawable epoch is far into the future.\n // If it is exiting we can't be sure this deposit has not been postponed in the deposit queue.\n // Hence we can not verify if the strategy's deposit has been processed or not.\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n // Get the parent beacon block root of the next block which is\n // the block root of the validator verification slot.\n // This will revert if the slot after the verification slot was missed.\n BeaconRoots.parentBlockRoot(\n validatorVerificationBlockTimestamp\n ),\n firstPendingDeposit.validatorIndex,\n firstPendingDeposit.pubKeyHash,\n // Validator is not exiting\n FAR_FUTURE_EPOCH,\n firstPendingDeposit.withdrawableEpochProof,\n firstPendingDeposit.validatorPubKeyProof\n );\n\n // solhint-disable max-line-length\n // If a validator is converted from a sweeping validator to a compounding validator, any balance in excess\n // of the min 32 ETH is put in the pending deposit queue. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // This will have a slot value of zero unfortunately.\n // We can not prove the strategy's deposits are still pending with a zero slot value so revert the tx.\n // Another snapBalances will need to be taken that does not have consolidation deposits at the front of the\n // beacon chain deposit queue.\n // solhint-enable max-line-length\n require(\n firstPendingDeposit.slot > 0,\n \"Invalid first pending deposit\"\n );\n\n // Calculate the epoch at the time of the snapBalances\n uint64 verificationEpoch = (SafeCast.toUint64(\n balancesMem.timestamp\n ) - BEACON_GENESIS_TIMESTAMP) / (SLOT_DURATION * SLOTS_PER_EPOCH);\n\n // For each staking strategy's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n uint256 depositID = depositList[i];\n DepositData memory depositData = deposits[depositID];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit slot is before the slot of the staking strategy's deposit.\n // If the deposit has been processed, it will need to be verified with `verifyDeposit`.\n // OR the deposit is to an exiting validator so check it is still not withdrawable.\n // If the validator is not withdrawable, then the deposit can not have been processed yet.\n // If the validator is now withdrawable, then the deposit may have been processed. The strategy\n // now has to wait until the validator's balance is verified to be zero.\n // OR the validator has exited and the deposit is now verified as processed.\n require(\n firstPendingDeposit.slot < depositData.slot ||\n (verificationEpoch < depositData.withdrawableEpoch &&\n depositData.withdrawableEpoch !=\n FAR_FUTURE_EPOCH) ||\n validator[depositData.pubKeyHash].state ==\n ValidatorState.EXITED,\n \"Deposit likely processed\"\n );\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei += uint256(depositData.amountGwei) * 1 gwei;\n\n // Remove the deposit if the validator has exited.\n if (\n validator[depositData.pubKeyHash].state ==\n ValidatorState.EXITED\n ) {\n _removeDeposit(depositID, depositData);\n\n emit DepositValidatorExited(\n depositID,\n uint256(depositData.amountGwei) * 1 gwei\n );\n }\n }\n }\n\n // Store the verified balance in storage\n lastVerifiedEthBalance = SafeCast.toUint128(\n totalDepositsWei + totalValidatorBalance + balancesMem.ethBalance\n );\n // Reset the last snap timestamp so a new snapBalances has to be made\n lastSnapTimestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n require(pubKey.length == 48, \"Invalid public key length\");\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /**\n *\n * WETH and ETH Accounting\n *\n */\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n // The min is required as more WETH can be withdrawn than deposited\n // as the strategy earns consensus and execution rewards.\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance.\n // The ETH balance in this strategy contract can be more than the last verified ETH balance\n // due to partial withdrawals or full exits being processed by the beacon chain since the last snapBalances.\n // It can also happen from execution rewards (MEV) or ETH donations.\n lastVerifiedEthBalance -= SafeCast.toUint128(\n Math.min(uint256(lastVerifiedEthBalance), _ethAmount)\n );\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += SafeCast.toUint128(_wethAmount);\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /**\n *\n * View Functions\n *\n */\n\n /// @notice Returns the number of deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n function depositListLength() external view returns (uint256) {\n return depositList.length;\n }\n\n /// @notice Returns the number of verified validators.\n function verifiedValidatorsLength() external view returns (uint256) {\n return verifiedValidators.length;\n }\n}\n" + }, + "contracts/strategies/NativeStaking/FeeAccumulator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\n/**\n * @title Fee Accumulator for Native Staking SSV Strategy\n * @notice Receives execution rewards which includes tx fees and\n * MEV rewards like tx priority and tx ordering.\n * It does NOT include swept ETH from beacon chain consensus rewards or full validator withdrawals.\n * @author Origin Protocol Inc\n */\ncontract FeeAccumulator {\n /// @notice The address of the Native Staking Strategy\n address public immutable STRATEGY;\n\n event ExecutionRewardsCollected(address indexed strategy, uint256 amount);\n\n /**\n * @param _strategy Address of the Native Staking Strategy\n */\n constructor(address _strategy) {\n STRATEGY = _strategy;\n }\n\n /**\n * @notice sends all ETH in this FeeAccumulator contract to the Native Staking Strategy.\n * @return eth The amount of execution rewards that were sent to the Native Staking Strategy\n */\n function collect() external returns (uint256 eth) {\n require(msg.sender == STRATEGY, \"Caller is not the Strategy\");\n\n eth = address(this).balance;\n if (eth > 0) {\n // Send the ETH to the Native Staking Strategy\n Address.sendValue(payable(STRATEGY), eth);\n\n emit ExecutionRewardsCollected(STRATEGY, eth);\n }\n }\n\n /**\n * @dev Accept ETH\n */\n receive() external payable {}\n}\n" + }, + "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { FeeAccumulator } from \"./FeeAccumulator.sol\";\nimport { ValidatorAccountant } from \"./ValidatorAccountant.sol\";\nimport { ISSVNetwork } from \"../../interfaces/ISSVNetwork.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/// @title Native Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract NativeStakingSSVStrategy is\n ValidatorAccountant,\n InitializableAbstractStrategy\n{\n using SafeERC20 for IERC20;\n\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n /// @notice Fee collector address\n /// @dev this address will receive maximal extractable value (MEV) rewards. These are\n /// rewards for arranging transactions in a way that benefits the validator.\n address payable public immutable FEE_ACCUMULATOR_ADDRESS;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[49] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n /// @param _feeAccumulator Address of the fee accumulator receiving execution layer validator rewards\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n uint256 _maxValidators,\n address _feeAccumulator,\n address _beaconChainDepositContract\n )\n InitializableAbstractStrategy(_baseConfig)\n ValidatorAccountant(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _maxValidators\n )\n {\n SSV_TOKEN = _ssvToken;\n FEE_ACCUMULATOR_ADDRESS = payable(_feeAccumulator);\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract.\n /// @param _rewardTokenAddresses Address of reward token for platform\n /// @param _assets Addresses of initial supported assets\n /// @param _pTokens Platform Token corresponding addresses\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n\n // Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\n FEE_ACCUMULATOR_ADDRESS\n );\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n /// @param _asset Address of asset to deposit. Has to be WETH.\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n depositedWethAccountedFor += _amount;\n _deposit(_asset, _amount);\n }\n\n /// @dev Deposit WETH to this strategy so it can later be staked into a validator.\n /// @param _asset Address of WETH\n /// @param _amount Amount of WETH to deposit\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n /*\n * We could do a check here that would revert when \"_amount % 32 ether != 0\". With the idea of\n * not allowing deposits that will result in WETH sitting on the strategy after all the possible batches\n * of 32ETH have been staked.\n * But someone could mess with our strategy by sending some WETH to it. And we might want to deposit just\n * enough WETH to add it up to 32 so it can be staked. For that reason the check is left out.\n *\n * WETH sitting on the strategy won't interfere with the accounting since accounting only operates on ETH.\n */\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n depositedWethAccountedFor = wethBalance;\n\n _deposit(WETH, newWeth);\n }\n }\n\n /// @notice Withdraw WETH from this contract. Used only if some WETH for is lingering on the contract.\n /// That can happen when:\n /// - after mints if the strategy is the default\n /// - time between depositToStrategy and stakeEth\n /// - the deposit was not a multiple of 32 WETH\n /// - someone sent WETH directly to this contract\n /// Will NOT revert if the strategy is paused from an accounting failure.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n _wethWithdrawn(_amount);\n\n IERC20(_asset).safeTransfer(_recipient, _amount);\n emit Withdrawal(_asset, address(0), _amount);\n }\n\n /// @notice transfer all WETH deposits back to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `exitSsvValidator` and `removeSsvValidator` operations.\n /// This does not withdraw any execution rewards from the FeeAccumulator or\n /// consensus rewards in this strategy.\n /// Any ETH in this strategy that was swept from a full validator withdrawal will not be withdrawn.\n /// ETH from full validator withdrawals is sent to the Vault using `doAccounting`.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n if (wethBalance > 0) {\n _withdraw(vaultAddress, WETH, wethBalance);\n }\n }\n\n /// @notice Returns the total value of (W)ETH that is staked to the validators\n /// and WETH deposits that are still to be staked.\n /// This does not include ETH from consensus rewards sitting in this strategy\n /// or ETH from MEV rewards in the FeeAccumulator. These rewards are harvested\n /// and sent to the Dripper so will eventually be sent to the Vault as WETH.\n /// @param _asset Address of weth asset\n /// @return balance Total value of (W)ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n balance =\n // add the ETH that has been staked in validators\n activeDepositedValidators *\n FULL_STAKE +\n // add the WETH in the strategy from deposits that are still to be staked\n IERC20(WETH).balanceOf(address(this));\n }\n\n function pause() external onlyStrategist {\n _pause();\n }\n\n /// @notice Returns bool indicating whether asset is supported by strategy.\n /// @param _asset The address of the asset token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for deposits\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\n function setFeeRecipient() external {\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\n FEE_ACCUMULATOR_ADDRESS\n );\n }\n\n /**\n * @notice Only accept ETH from the FeeAccumulator and the WETH contract - required when\n * unwrapping WETH just before staking it to the validator.\n * The strategy will also receive ETH from the priority fees of transactions when producing blocks\n * as defined in EIP-1559.\n * The tx fees come from the Beacon chain so do not need any EVM level permissions to receive ETH.\n * The tx fees are paid with each block produced. They are not included in the consensus rewards\n * which are periodically swept from the validators to this strategy.\n * For accounting purposes, the priority fees of transactions will be considered consensus rewards\n * and will be included in the AccountingConsensusRewards event.\n * @dev don't want to receive donations from anyone else as donations over the fuse limits will\n * mess with the accounting of the consensus rewards and validator full withdrawals.\n */\n receive() external payable {\n require(\n msg.sender == FEE_ACCUMULATOR_ADDRESS || msg.sender == WETH,\n \"Eth not from allowed contracts\"\n );\n }\n\n /***************************************\n Internal functions\n ****************************************/\n\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Convert accumulated ETH to WETH and send to the Harvester.\n /// Will revert if the strategy is paused for accounting.\n function _collectRewardTokens() internal override whenNotPaused {\n // collect ETH from execution rewards from the fee accumulator\n uint256 executionRewards = FeeAccumulator(FEE_ACCUMULATOR_ADDRESS)\n .collect();\n\n // total ETH rewards to be harvested = execution rewards + consensus rewards\n uint256 ethRewards = executionRewards + consensusRewards;\n\n require(\n address(this).balance >= ethRewards,\n \"Insufficient eth balance\"\n );\n\n if (ethRewards > 0) {\n // reset the counter keeping track of beacon chain consensus rewards\n consensusRewards = 0;\n\n // Convert ETH rewards to WETH\n IWETH9(WETH).deposit{ value: ethRewards }();\n\n IERC20(WETH).safeTransfer(harvesterAddress, ethRewards);\n emit RewardTokenCollected(harvesterAddress, WETH, ethRewards);\n }\n }\n\n /// @dev emits Withdrawal event from NativeStakingSSVStrategy\n function _wethWithdrawnToVault(uint256 _amount) internal override {\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _wethWithdrawn(uint256 _amount) internal override {\n /* In an ideal world we wouldn't need to reduce the deduction amount when the\n * depositedWethAccountedFor is smaller than the _amount.\n *\n * The reason this is required is that a malicious actor could sent WETH directly\n * to this contract and that would circumvent the increase of depositedWethAccountedFor\n * property. When the ETH would be staked the depositedWethAccountedFor amount could\n * be deducted so much that it would be negative.\n */\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n }\n}\n" + }, + "contracts/strategies/NativeStaking/ValidatorAccountant.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ValidatorRegistrator } from \"./ValidatorRegistrator.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\n\n/// @title Validator Accountant\n/// @notice Attributes the ETH swept from beacon chain validators to this strategy contract\n/// as either full or partial withdrawals. Partial withdrawals being consensus rewards.\n/// Full withdrawals are from exited validators.\n/// @author Origin Protocol Inc\nabstract contract ValidatorAccountant is ValidatorRegistrator {\n /// @notice The minimum amount of blocks that need to pass between two calls to manuallyFixAccounting\n uint256 public constant MIN_FIX_ACCOUNTING_CADENCE = 7200; // 1 day\n\n /// @notice Keeps track of the total consensus rewards swept from the beacon chain\n uint256 public consensusRewards;\n\n /// @notice start of fuse interval\n uint256 public fuseIntervalStart;\n /// @notice end of fuse interval\n uint256 public fuseIntervalEnd;\n /// @notice last block number manuallyFixAccounting has been called\n uint256 public lastFixAccountingBlockNumber;\n\n uint256[49] private __gap;\n\n event FuseIntervalUpdated(uint256 start, uint256 end);\n event AccountingFullyWithdrawnValidator(\n uint256 noOfValidators,\n uint256 remainingValidators,\n uint256 wethSentToVault\n );\n event AccountingValidatorSlashed(\n uint256 remainingValidators,\n uint256 wethSentToVault\n );\n event AccountingConsensusRewards(uint256 amount);\n\n event AccountingManuallyFixed(\n int256 validatorsDelta,\n int256 consensusRewardsDelta,\n uint256 wethToVault\n );\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n uint256 _maxValidators\n )\n ValidatorRegistrator(\n _wethAddress,\n _vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _maxValidators\n )\n {}\n\n /// @notice set fuse interval values\n function setFuseInterval(\n uint256 _fuseIntervalStart,\n uint256 _fuseIntervalEnd\n ) external onlyGovernor {\n require(\n _fuseIntervalStart < _fuseIntervalEnd &&\n _fuseIntervalEnd < 32 ether &&\n _fuseIntervalEnd - _fuseIntervalStart >= 4 ether,\n \"Incorrect fuse interval\"\n );\n\n fuseIntervalStart = _fuseIntervalStart;\n fuseIntervalEnd = _fuseIntervalEnd;\n\n emit FuseIntervalUpdated(_fuseIntervalStart, _fuseIntervalEnd);\n }\n\n /* solhint-disable max-line-length */\n /// This notion page offers a good explanation of how the accounting functions\n /// https://www.notion.so/originprotocol/Limited-simplified-native-staking-accounting-67a217c8420d40678eb943b9da0ee77d\n /// In short, after dividing by 32, if the ETH remaining on the contract falls between 0 and fuseIntervalStart,\n /// the accounting function will treat that ETH as Beacon chain consensus rewards.\n /// On the contrary, if after dividing by 32, the ETH remaining on the contract falls between fuseIntervalEnd and 32,\n /// the accounting function will treat that as a validator slashing.\n /// @notice Perform the accounting attributing beacon chain ETH to either full or partial withdrawals. Returns true when\n /// accounting is valid and fuse isn't \"blown\". Returns false when fuse is blown.\n /// @dev This function could in theory be permission-less but lets allow only the Registrator (Defender Action) to call it\n /// for now.\n /// @return accountingValid true if accounting was successful, false if fuse is blown\n /* solhint-enable max-line-length */\n function doAccounting()\n external\n onlyRegistrator\n whenNotPaused\n nonReentrant\n returns (bool accountingValid)\n {\n // pause the accounting on failure\n accountingValid = _doAccounting(true);\n }\n\n // slither-disable-start reentrancy-eth\n function _doAccounting(bool pauseOnFail)\n internal\n returns (bool accountingValid)\n {\n if (address(this).balance < consensusRewards) {\n return _failAccounting(pauseOnFail);\n }\n\n // Calculate all the new ETH that has been swept to the contract since the last accounting\n uint256 newSweptETH = address(this).balance - consensusRewards;\n accountingValid = true;\n\n // send the ETH that is from fully withdrawn validators to the Vault\n if (newSweptETH >= FULL_STAKE) {\n uint256 fullyWithdrawnValidators;\n // explicitly cast to uint256 as we want to round to a whole number of validators\n fullyWithdrawnValidators = uint256(newSweptETH / FULL_STAKE);\n activeDepositedValidators -= fullyWithdrawnValidators;\n\n uint256 wethToVault = FULL_STAKE * fullyWithdrawnValidators;\n IWETH9(WETH).deposit{ value: wethToVault }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, wethToVault);\n _wethWithdrawnToVault(wethToVault);\n\n emit AccountingFullyWithdrawnValidator(\n fullyWithdrawnValidators,\n activeDepositedValidators,\n wethToVault\n );\n }\n\n uint256 ethRemaining = address(this).balance - consensusRewards;\n // should be less than a whole validator stake\n require(ethRemaining < FULL_STAKE, \"Unexpected accounting\");\n\n // If no Beacon chain consensus rewards swept\n if (ethRemaining == 0) {\n // do nothing\n return accountingValid;\n } else if (ethRemaining < fuseIntervalStart) {\n // Beacon chain consensus rewards swept (partial validator withdrawals)\n // solhint-disable-next-line reentrancy\n consensusRewards += ethRemaining;\n emit AccountingConsensusRewards(ethRemaining);\n } else if (ethRemaining > fuseIntervalEnd) {\n // Beacon chain consensus rewards swept but also a slashed validator fully exited\n IWETH9(WETH).deposit{ value: ethRemaining }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, ethRemaining);\n activeDepositedValidators -= 1;\n\n _wethWithdrawnToVault(ethRemaining);\n\n emit AccountingValidatorSlashed(\n activeDepositedValidators,\n ethRemaining\n );\n }\n // Oh no... Fuse is blown. The Strategist needs to adjust the accounting values.\n else {\n return _failAccounting(pauseOnFail);\n }\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @dev pause any further accounting if required and return false\n function _failAccounting(bool pauseOnFail)\n internal\n returns (bool accountingValid)\n {\n // pause if not already\n if (pauseOnFail) {\n _pause();\n }\n // fail the accounting\n accountingValid = false;\n }\n\n /// @notice Allow the Strategist to fix the accounting of this strategy and unpause.\n /// @param _validatorsDelta adjust the active validators by up to plus three or minus three\n /// @param _consensusRewardsDelta adjust the accounted for consensus rewards up or down\n /// @param _ethToVaultAmount the amount of ETH that gets wrapped into WETH and sent to the Vault\n /// @dev There is a case when a validator(s) gets slashed so much that the eth swept from\n /// the beacon chain enters the fuse area and there are no consensus rewards on the contract\n /// to \"dip into\"/use. To increase the amount of unaccounted ETH over the fuse end interval\n /// we need to reduce the amount of active deposited validators and immediately send WETH\n /// to the vault, so it doesn't interfere with further accounting.\n function manuallyFixAccounting(\n int256 _validatorsDelta,\n int256 _consensusRewardsDelta,\n uint256 _ethToVaultAmount\n ) external onlyStrategist whenPaused nonReentrant {\n require(\n lastFixAccountingBlockNumber + MIN_FIX_ACCOUNTING_CADENCE <\n block.number,\n \"Fix accounting called too soon\"\n );\n require(\n _validatorsDelta >= -3 &&\n _validatorsDelta <= 3 &&\n // new value must be positive\n int256(activeDepositedValidators) + _validatorsDelta >= 0,\n \"Invalid validatorsDelta\"\n );\n require(\n _consensusRewardsDelta >= -332 ether &&\n _consensusRewardsDelta <= 332 ether &&\n // new value must be positive\n int256(consensusRewards) + _consensusRewardsDelta >= 0,\n \"Invalid consensusRewardsDelta\"\n );\n require(_ethToVaultAmount <= 32 ether * 3, \"Invalid wethToVaultAmount\");\n\n activeDepositedValidators = uint256(\n int256(activeDepositedValidators) + _validatorsDelta\n );\n consensusRewards = uint256(\n int256(consensusRewards) + _consensusRewardsDelta\n );\n lastFixAccountingBlockNumber = block.number;\n if (_ethToVaultAmount > 0) {\n IWETH9(WETH).deposit{ value: _ethToVaultAmount }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, _ethToVaultAmount);\n _wethWithdrawnToVault(_ethToVaultAmount);\n }\n\n emit AccountingManuallyFixed(\n _validatorsDelta,\n _consensusRewardsDelta,\n _ethToVaultAmount\n );\n\n // rerun the accounting to see if it has now been fixed.\n // Do not pause the accounting on failure as it is already paused\n require(_doAccounting(false), \"Fuse still blown\");\n\n // unpause since doAccounting was successful\n _unpause();\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n /// @dev allows for NativeStakingSSVStrategy contract to emit the Withdrawal event\n function _wethWithdrawnToVault(uint256 _amount) internal virtual;\n}\n" + }, + "contracts/strategies/NativeStaking/ValidatorRegistrator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Registrator of the validators\n * @notice This contract implements all the required functionality to register, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract ValidatorRegistrator is Governable, Pausable {\n /// @notice The maximum amount of ETH that can be staked by a validator\n /// @dev this can change in the future with EIP-7251, Increase the MAX_EFFECTIVE_BALANCE\n uint256 public constant FULL_STAKE = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Maximum number of validators that can be registered in this strategy\n uint256 public immutable MAX_VALIDATORS;\n\n /// @notice Address of the registrator - allowed to register, exit and remove validators\n address public validatorRegistrator;\n /// @notice The number of validators that have 32 (!) ETH actively deposited. When a new deposit\n /// to a validator happens this number increases, when a validator exit is detected this number\n /// decreases.\n uint256 public activeDepositedValidators;\n /// @notice State of the validators keccak256(pubKey) => state\n mapping(bytes32 => VALIDATOR_STATE) public validatorsStates;\n /// @notice The account that is allowed to modify stakeETHThreshold and reset stakeETHTally\n address public stakingMonitor;\n /// @notice Amount of ETH that can be staked before staking on the contract is suspended\n /// and the `stakingMonitor` needs to approve further staking by calling `resetStakeETHTally`\n uint256 public stakeETHThreshold;\n /// @notice Amount of ETH that has been staked since the `stakingMonitor` last called `resetStakeETHTally`.\n /// This can not go above `stakeETHThreshold`.\n uint256 public stakeETHTally;\n\n // For future use\n uint256[47] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n EXITING, // exit message has been posted and validator is in the process of exiting\n EXIT_COMPLETE // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event ETHStaked(bytes32 indexed pubKeyHash, bytes pubKey, uint256 amount);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event SSVValidatorExitInitiated(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event SSVValidatorExitCompleted(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event StakeETHThresholdChanged(uint256 amount);\n event StakeETHTallyReset();\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(\n msg.sender == validatorRegistrator,\n \"Caller is not the Registrator\"\n );\n _;\n }\n\n /// @dev Throws if called by any account other than the Staking monitor\n modifier onlyStakingMonitor() {\n require(msg.sender == stakingMonitor, \"Caller is not the Monitor\");\n _;\n }\n\n /// @dev Throws if called by any account other than the Strategist\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(VAULT_ADDRESS).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n uint256 _maxValidators\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n MAX_VALIDATORS = _maxValidators;\n }\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Set the address of the staking monitor that is allowed to reset stakeETHTally\n function setStakingMonitor(address _address) external onlyGovernor {\n stakingMonitor = _address;\n emit StakingMonitorChanged(_address);\n }\n\n /// @notice Set the amount of ETH that can be staked before staking monitor\n // needs to a approve further staking by resetting the stake ETH tally\n function setStakeETHThreshold(uint256 _amount) external onlyGovernor {\n stakeETHThreshold = _amount;\n emit StakeETHThresholdChanged(_amount);\n }\n\n /// @notice Reset the stakeETHTally\n function resetStakeETHTally() external onlyStakingMonitor {\n stakeETHTally = 0;\n emit StakeETHTallyReset();\n }\n\n /// @notice Stakes WETH to the node validators\n /// @param validators A list of validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n // slither-disable-start reentrancy-eth\n function stakeEth(ValidatorStakeData[] calldata validators)\n external\n onlyRegistrator\n whenNotPaused\n nonReentrant\n {\n uint256 requiredETH = validators.length * FULL_STAKE;\n\n // Check there is enough WETH from the deposits sitting in this strategy contract\n require(\n requiredETH <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(\n activeDepositedValidators + validators.length <= MAX_VALIDATORS,\n \"Max validators reached\"\n );\n\n require(\n stakeETHTally + requiredETH <= stakeETHThreshold,\n \"Staking ETH over threshold\"\n );\n stakeETHTally += requiredETH;\n\n // Convert required ETH from WETH\n IWETH9(WETH).withdraw(requiredETH);\n _wethWithdrawn(requiredETH);\n\n /* 0x01 to indicate that withdrawal credentials will contain an EOA address that the sweeping function\n * can sweep funds to.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x01),\n bytes11(0),\n address(this)\n );\n\n // For each validator\n for (uint256 i = 0; i < validators.length; ++i) {\n bytes32 pubKeyHash = keccak256(validators[i].pubkey);\n\n require(\n validatorsStates[pubKeyHash] == VALIDATOR_STATE.REGISTERED,\n \"Validator not registered\"\n );\n\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: FULL_STAKE\n }(\n validators[i].pubkey,\n withdrawalCredentials,\n validators[i].signature,\n validators[i].depositDataRoot\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.STAKED;\n\n emit ETHStaked(pubKeyHash, validators[i].pubkey, FULL_STAKE);\n }\n // save gas by changing this storage variable only once rather each time in the loop.\n activeDepositedValidators += validators.length;\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Registers a new validator in the SSV Cluster.\n /// Only the registrator can call this function.\n /// @param publicKeys The public keys of the validators\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for each validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function registerSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n require(\n publicKeys.length == sharesData.length,\n \"Pubkey sharesData mismatch\"\n );\n // Check each public key has not already been used\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n emit SSVValidatorRegistered(pubKeyHash, publicKeys[i], operatorIds);\n }\n\n ISSVNetwork(SSV_NETWORK).bulkRegisterValidator(\n publicKeys,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n }\n\n /// @notice Exit validators from the Beacon chain.\n /// The staked ETH will eventually swept to this native staking strategy.\n /// Only the registrator can call this function.\n /// @param publicKeys List of SSV validator public keys\n /// @param operatorIds The operator IDs of the SSV Cluster\n function exitSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external onlyRegistrator whenNotPaused nonReentrant {\n ISSVNetwork(SSV_NETWORK).bulkExitValidator(publicKeys, operatorIds);\n\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n\n // Check each validator has not already been staked.\n // This would normally be done before the external call but is after\n // so only one for loop of the validators is needed.\n require(\n currentState == VALIDATOR_STATE.STAKED,\n \"Validator not staked\"\n );\n\n // Store the new validator state\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXITING;\n\n emit SSVValidatorExitInitiated(\n pubKeyHash,\n publicKeys[i],\n operatorIds\n );\n }\n }\n\n /// @notice Remove validators from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKeys List of SSV validator public keys\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function removeSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused nonReentrant {\n ISSVNetwork(SSV_NETWORK).bulkRemoveValidator(\n publicKeys,\n operatorIds,\n cluster\n );\n\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n\n // Check each validator is either registered or exited.\n // This would normally be done before the external call but is after\n // so only one for loop of the validators is needed.\n require(\n currentState == VALIDATOR_STATE.EXITING ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exiting\"\n );\n\n // Store the new validator state\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXIT_COMPLETE;\n\n emit SSVValidatorExitCompleted(\n pubKeyHash,\n publicKeys[i],\n operatorIds\n );\n }\n }\n\n /// @notice Deposits more SSV Tokens to the SSV Network contract which is used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// uses \"onlyStrategist\" modifier so continuous front-running can't DOS our maintenance service\n /// that tries to top up SSV tokens.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function depositSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyStrategist {\n ISSVNetwork(SSV_NETWORK).deposit(\n address(this),\n operatorIds,\n ssvAmount,\n cluster\n );\n }\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _wethWithdrawn(uint256 _amount) internal virtual;\n}\n" + }, + "contracts/strategies/plume/RoosterAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Rooster AMO strategy\n * @author Origin Protocol Inc\n * @custom:security-contact security@originprotocol.com\n */\nimport { Math as MathRooster } from \"../../../lib/rooster/v2-common/libraries/Math.sol\";\nimport { Math as Math_v5 } from \"../../../lib/rooster/openzeppelin-custom/contracts/utils/math/Math.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { IMaverickV2Pool } from \"../../interfaces/plume/IMaverickV2Pool.sol\";\nimport { IMaverickV2Quoter } from \"../../interfaces/plume/IMaverickV2Quoter.sol\";\nimport { IMaverickV2LiquidityManager } from \"../../interfaces/plume/IMaverickV2LiquidityManager.sol\";\nimport { IMaverickV2PoolLens } from \"../../interfaces/plume/IMaverickV2PoolLens.sol\";\nimport { IMaverickV2Position } from \"../../interfaces/plume/IMaverickV2Position.sol\";\nimport { IVotingDistributor } from \"../../interfaces/plume/IVotingDistributor.sol\";\nimport { IPoolDistributor } from \"../../interfaces/plume/IPoolDistributor.sol\";\n// importing custom version of rooster TickMath because of dependency collision. Maverick uses\n// a newer OpenZepplin Math library with functionality that is not present in 4.4.2 (the one we use)\nimport { TickMath } from \"../../../lib/rooster/v2-common/libraries/TickMath.sol\";\n\ncontract RoosterAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n using SafeCast for uint256;\n\n /***************************************\n Storage slot members\n ****************************************/\n\n /// @notice NFT tokenId of the liquidity position\n ///\n /// @dev starts with value of 1 and can not be 0\n // solhint-disable-next-line max-line-length\n /// https://github.com/rooster-protocol/rooster-contracts/blob/fbfecbc519e4495b12598024a42630b4a8ea4489/v2-common/contracts/base/Nft.sol#L14\n uint256 public tokenId;\n /// @dev Minimum amount of tokens the strategy would be able to withdraw from the pool.\n /// minimum amount of tokens are withdrawn at a 1:1 price\n /// Important: Underlying assets contains only assets that are deposited in the underlying Rooster pool.\n /// WETH or OETH held by this contract is not accounted for in underlying assets\n uint256 public underlyingAssets;\n /// @notice Marks the start of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareStart;\n /// @notice Marks the end of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareEnd;\n /// @dev reserved for inheritance\n int256[46] private __reserved;\n\n /***************************************\n Constants, structs and events\n ****************************************/\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the OETH token contract\n address public immutable OETH;\n /// @notice the underlying AMO Maverick (Rooster) pool\n IMaverickV2Pool public immutable mPool;\n /// @notice the Liquidity manager used to add liquidity to the pool\n IMaverickV2LiquidityManager public immutable liquidityManager;\n /// @notice the Maverick V2 poolLens\n ///\n /// @dev only used to provide the pool's current sqrtPrice\n IMaverickV2PoolLens public immutable poolLens;\n /// @notice the Maverick V2 position\n ///\n /// @dev provides details of the NFT LP position and offers functions to\n /// remove the liquidity.\n IMaverickV2Position public immutable maverickPosition;\n /// @notice the Maverick Quoter\n IMaverickV2Quoter public immutable quoter;\n /// @notice the Maverick Voting Distributor\n IVotingDistributor public immutable votingDistributor;\n /// @notice the Maverick Pool Distributor\n IPoolDistributor public immutable poolDistributor;\n\n /// @notice sqrtPriceTickLower\n /// @dev tick lower represents the lower price of OETH priced in WETH. Meaning the pool\n /// offers more than 1 OETH for 1 WETH. In other terms to get 1 OETH the swap needs to offer 0.9999 WETH\n /// this is where purchasing OETH with WETH within the liquidity position is the cheapest.\n ///\n /// _____________________\n /// | | |\n /// | WETH | OETH |\n /// | | |\n /// | | |\n /// --------- * ---- * ---------- * ---------\n /// currentPrice\n /// sqrtPriceHigher-(1:1 parity)\n /// sqrtPriceLower\n ///\n ///\n /// Price is defined as price of token1 in terms of token0. (token1 / token0)\n /// @notice sqrtPriceTickLower - OETH is priced 0.9999 WETH\n uint256 public immutable sqrtPriceTickLower;\n /// @notice sqrtPriceTickHigher\n /// @dev tick higher represents 1:1 price parity of WETH to OETH\n uint256 public immutable sqrtPriceTickHigher;\n /// @dev price at parity (in OETH this is equal to sqrtPriceTickHigher)\n uint256 public immutable sqrtPriceAtParity;\n /// @notice The tick where the strategy deploys the liquidity to\n int32 public constant TICK_NUMBER = -1;\n /// @notice Minimum liquidity that must be exceeded to continue with the action\n /// e.g. deposit, add liquidity\n uint256 public constant ACTION_THRESHOLD = 1e12;\n /// @notice Maverick pool static liquidity bin type\n uint8 public constant MAV_STATIC_BIN_KIND = 0;\n /// @dev a threshold under which the contract no longer allows for the protocol to rebalance. Guarding\n /// against a strategist / guardian being taken over and with multiple transactions draining the\n /// protocol funds.\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n /// @notice Emitted when the allowed interval within which the strategy contract is allowed to deposit\n /// liquidity to the underlying pool is updated.\n /// @param allowedWethShareStart The start of the interval\n /// @param allowedWethShareEnd The end of the interval\n event PoolWethShareIntervalUpdated(\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n /// @notice Emitted when liquidity is removed from the underlying pool\n /// @param withdrawLiquidityShare Share of strategy's liquidity that has been removed\n /// @param removedWETHAmount The amount of WETH removed\n /// @param removedOETHAmount The amount of OETH removed\n /// @param underlyingAssets Updated amount of strategy's underlying assets\n event LiquidityRemoved(\n uint256 withdrawLiquidityShare,\n uint256 removedWETHAmount,\n uint256 removedOETHAmount,\n uint256 underlyingAssets\n );\n\n /// @notice Emitted when the underlying pool is rebalanced\n /// @param currentPoolWethShare The resulting share of strategy's liquidity\n /// in the TICK_NUMBER\n event PoolRebalanced(uint256 currentPoolWethShare);\n\n /// @notice Emitted when the amount of underlying assets the strategy hold as\n /// liquidity in the pool is updated.\n /// @param underlyingAssets Updated amount of strategy's underlying assets\n event UnderlyingAssetsUpdated(uint256 underlyingAssets);\n\n /// @notice Emitted when liquidity is added to the underlying pool\n /// @param wethAmountDesired Amount of WETH desired to be deposited\n /// @param oethAmountDesired Amount of OETH desired to be deposited\n /// @param wethAmountSupplied Amount of WETH deposited\n /// @param oethAmountSupplied Amount of OETH deposited\n /// @param tokenId NFT liquidity token id\n /// @param underlyingAssets Updated amount of underlying assets\n event LiquidityAdded(\n uint256 wethAmountDesired,\n uint256 oethAmountDesired,\n uint256 wethAmountSupplied,\n uint256 oethAmountSupplied,\n uint256 tokenId,\n uint256 underlyingAssets\n ); // 0x1530ec74\n\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n ); // 0x3681e8e0\n\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth); // 0x989e5ca8\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth); // 0xa6737d87\n error OutsideExpectedTickRange(); // 0xa6e1bad2\n error SlippageCheck(uint256 tokenReceived); // 0x355cdb78\n\n /// @notice the constructor\n /// @dev This contract is intended to be used as a proxy. To prevent the\n /// potential confusion of having a functional implementation contract\n /// the constructor has the `initializer` modifier. This way the\n /// `initialize` function can not be called on the implementation contract.\n /// For the same reason the implementation contract also has the governor\n /// set to a zero address.\n /// @param _stratConfig the basic strategy configuration\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _oethAddress Address of the Erc20 OETH Token contract\n /// @param _liquidityManager Address of liquidity manager to add\n /// the liquidity\n /// @param _poolLens Address of the pool lens contract\n /// @param _maverickPosition Address of the Maverick's position contract\n /// @param _maverickQuoter Address of the Maverick's Quoter contract\n /// @param _mPool Address of the Rooster concentrated liquidity pool\n /// @param _upperTickAtParity Bool when true upperTick is the one where the\n /// price of OETH and WETH are at parity\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethAddress,\n address _liquidityManager,\n address _poolLens,\n address _maverickPosition,\n address _maverickQuoter,\n address _mPool,\n bool _upperTickAtParity,\n address _votingDistributor,\n address _poolDistributor\n ) initializer InitializableAbstractStrategy(_stratConfig) {\n require(\n address(IMaverickV2Pool(_mPool).tokenA()) == _wethAddress,\n \"WETH not TokenA\"\n );\n require(\n address(IMaverickV2Pool(_mPool).tokenB()) == _oethAddress,\n \"OETH not TokenB\"\n );\n require(\n _liquidityManager != address(0),\n \"LiquidityManager zero address not allowed\"\n );\n require(\n _maverickQuoter != address(0),\n \"Quoter zero address not allowed\"\n );\n require(_poolLens != address(0), \"PoolLens zero address not allowed\");\n require(\n _maverickPosition != address(0),\n \"Position zero address not allowed\"\n );\n require(\n _votingDistributor != address(0),\n \"Voting distributor zero address not allowed\"\n );\n require(\n _poolDistributor != address(0),\n \"Pool distributor zero address not allowed\"\n );\n\n uint256 _tickSpacing = IMaverickV2Pool(_mPool).tickSpacing();\n require(_tickSpacing == 1, \"Unsupported tickSpacing\");\n\n // tickSpacing == 1\n (sqrtPriceTickLower, sqrtPriceTickHigher) = TickMath.tickSqrtPrices(\n _tickSpacing,\n TICK_NUMBER\n );\n sqrtPriceAtParity = _upperTickAtParity\n ? sqrtPriceTickHigher\n : sqrtPriceTickLower;\n\n WETH = _wethAddress;\n OETH = _oethAddress;\n liquidityManager = IMaverickV2LiquidityManager(_liquidityManager);\n poolLens = IMaverickV2PoolLens(_poolLens);\n maverickPosition = IMaverickV2Position(_maverickPosition);\n quoter = IMaverickV2Quoter(_maverickQuoter);\n mPool = IMaverickV2Pool(_mPool);\n votingDistributor = IVotingDistributor(_votingDistributor);\n poolDistributor = IPoolDistributor(_poolDistributor);\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /**\n * @notice initialize function, to set up initial internal state\n */\n function initialize() external onlyGovernor initializer {\n // Read reward\n address[] memory _rewardTokens = new address[](1);\n _rewardTokens[0] = poolDistributor.rewardToken();\n\n require(_rewardTokens[0] != address(0), \"No reward token configured\");\n\n InitializableAbstractStrategy._initialize(\n _rewardTokens,\n new address[](0),\n new address[](0)\n );\n }\n\n /***************************************\n Configuration \n ****************************************/\n\n /**\n * @notice Set allowed pool weth share interval. After the rebalance happens\n * the share of WETH token in the ticker needs to be within the specifications\n * of the interval.\n *\n * @param _allowedWethShareStart Start of WETH share interval expressed as 18 decimal amount\n * @param _allowedWethShareEnd End of WETH share interval expressed as 18 decimal amount\n */\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external onlyGovernor {\n require(\n _allowedWethShareStart < _allowedWethShareEnd,\n \"Invalid interval\"\n );\n // can not go below 1% weth share\n require(_allowedWethShareStart > 0.01 ether, \"Invalid interval start\");\n // can not go above 95% weth share\n require(_allowedWethShareEnd < 0.95 ether, \"Invalid interval end\");\n\n allowedWethShareStart = _allowedWethShareStart;\n allowedWethShareEnd = _allowedWethShareEnd;\n emit PoolWethShareIntervalUpdated(\n _allowedWethShareStart,\n _allowedWethShareEnd\n );\n }\n\n /***************************************\n Strategy overrides \n ****************************************/\n\n /**\n * @notice Deposits funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposits all the funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n _deposit(WETH, _wethBalance);\n }\n\n /**\n * @dev Deposits funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n * Before this function can be called the initial pool position needs to already\n * be minted.\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n require(tokenId > 0, \"Initial position not minted\");\n emit Deposit(_asset, address(0), _amount);\n\n // if the pool price is not within the expected interval leave the WETH on the contract\n // as to not break the mints - in case it would be configured as a default asset strategy\n (bool _isExpectedRange, ) = _checkForExpectedPoolPrice(false);\n if (_isExpectedRange) {\n // deposit funds into the underlying pool. Because no swap is performed there is no\n // need to remove any of the liquidity beforehand.\n _rebalance(0, false, 0, 0);\n }\n }\n\n /**\n * @notice Withdraw an `amount` of WETH from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset WETH address\n * @param _amount Amount of WETH to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n _ensureWETHBalance(_amount);\n\n _withdraw(_recipient, _amount);\n }\n\n /**\n * @notice Withdraw WETH and sends it to the Vault.\n */\n function withdrawAll() external override onlyVault nonReentrant {\n if (tokenId != 0) {\n _removeLiquidity(1e18);\n }\n\n uint256 _balance = IERC20(WETH).balanceOf(address(this));\n if (_balance > 0) {\n _withdraw(vaultAddress, _balance);\n }\n }\n\n function _withdraw(address _recipient, uint256 _amount) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n * @return bool True when the _asset is WETH\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /**\n * @dev Approve the spending amounts for the assets\n */\n function _approveTokenAmounts(\n uint256 _wethAllowance,\n uint256 _oethAllowance\n ) internal {\n IERC20(WETH).approve(address(liquidityManager), _wethAllowance);\n IERC20(OETH).approve(address(liquidityManager), _oethAllowance);\n }\n\n /***************************************\n Liquidity management\n ****************************************/\n /**\n * @dev Add liquidity into the pool in the pre-configured WETH to OETH share ratios\n * defined by the allowedPoolWethShareStart|End interval.\n *\n * Normally a PoolLens contract is used to prepare the parameters to add liquidity to the\n * Rooster pools. It has some errors when doing those calculation and for that reason a\n * much more accurate Quoter contract is used. This is possible due to our requirement of\n * adding liquidity only to one tick - PoolLens supports adding liquidity into multiple ticks\n * using different distribution ratios.\n */\n function _addLiquidity() internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n // don't deposit small liquidity amounts\n if (_wethBalance <= ACTION_THRESHOLD) {\n return;\n }\n\n (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n ) = _getAddLiquidityParams(_wethBalance, 1e30);\n\n if (OETHRequired > _oethBalance) {\n IVault(vaultAddress).mintForStrategy(OETHRequired - _oethBalance);\n }\n\n _approveTokenAmounts(WETHRequired, OETHRequired);\n\n (\n uint256 _wethAmount,\n uint256 _oethAmount,\n uint32[] memory binIds\n ) = liquidityManager.addPositionLiquidityToSenderByTokenIndex(\n mPool,\n 0, // NFT token index\n packedSqrtPriceBreaks,\n packedArgs\n );\n\n require(binIds.length == 1, \"Unexpected binIds length\");\n\n // burn remaining OETH\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n\n // needs to be called after _updateUnderlyingAssets so the updated amount\n // is reflected in the event\n emit LiquidityAdded(\n _wethBalance, // wethAmountDesired\n OETHRequired, // oethAmountDesired\n _wethAmount, // wethAmountSupplied\n _oethAmount, // oethAmountSupplied\n tokenId, // tokenId\n underlyingAssets\n );\n }\n\n /**\n * @dev The function creates liquidity parameters required to be able to add liquidity to the pool.\n * The function needs to handle the 3 different cases of the way liquidity is added:\n * - only WETH present in the tick\n * - only OETH present in the tick\n * - both tokens present in the tick\n *\n */\n function _getAddLiquidityParams(uint256 _maxWETH, uint256 _maxOETH)\n internal\n returns (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n )\n {\n IMaverickV2Pool.AddLiquidityParams[]\n memory addParams = new IMaverickV2Pool.AddLiquidityParams[](1);\n int32[] memory ticks = new int32[](1);\n uint128[] memory amounts = new uint128[](1);\n ticks[0] = TICK_NUMBER;\n // arbitrary LP amount\n amounts[0] = 1e24;\n\n // construct value for Quoter with arbitrary LP amount\n IMaverickV2Pool.AddLiquidityParams memory addParam = IMaverickV2Pool\n .AddLiquidityParams({\n kind: MAV_STATIC_BIN_KIND,\n ticks: ticks,\n amounts: amounts\n });\n\n // get the WETH and OETH required to get the proportion of tokens required\n // given the arbitrary liquidity\n (WETHRequired, OETHRequired, ) = quoter.calculateAddLiquidity(\n mPool,\n addParam\n );\n\n /**\n * If either token required is 0 then the tick consists only of the other token. In that\n * case the liquidity calculations need to be done using the non 0 token. By setting the\n * tokenRequired from 0 to 1 the `min` in next step will ignore that (the bigger) value.\n */\n WETHRequired = WETHRequired == 0 ? 1 : WETHRequired;\n OETHRequired = OETHRequired == 0 ? 1 : OETHRequired;\n\n addParam.amounts[0] = Math_v5\n .min(\n ((_maxWETH - 1) * 1e24) / WETHRequired,\n ((_maxOETH - 1) * 1e24) / OETHRequired\n )\n .toUint128();\n\n // update the quotes with the actual amounts\n (WETHRequired, OETHRequired, ) = quoter.calculateAddLiquidity(\n mPool,\n addParam\n );\n\n require(_maxWETH >= WETHRequired, \"More WETH required than specified\");\n require(_maxOETH >= OETHRequired, \"More OETH required than specified\");\n\n // organize values to be used by manager\n addParams[0] = addParam;\n packedArgs = liquidityManager.packAddLiquidityArgsArray(addParams);\n // price can stay 0 if array only has one element\n packedSqrtPriceBreaks = liquidityManager.packUint88Array(\n new uint88[](1)\n );\n }\n\n /**\n * @dev Check that the Rooster pool price is within the expected\n * parameters.\n * This function works whether the strategy contract has liquidity\n * position in the pool or not. The function returns _wethSharePct\n * as a gas optimization measure.\n * @param _throwException when set to true the function throws an exception\n * when pool's price is not within expected range.\n * @return _isExpectedRange Bool expressing price is within expected range\n * @return _wethSharePct Share of WETH owned by this strategy contract in the\n * configured ticker.\n */\n function _checkForExpectedPoolPrice(bool _throwException)\n internal\n view\n returns (bool _isExpectedRange, uint256 _wethSharePct)\n {\n require(\n allowedWethShareStart != 0 && allowedWethShareEnd != 0,\n \"Weth share interval not set\"\n );\n\n uint256 _currentPrice = getPoolSqrtPrice();\n\n /**\n * First check pool price is in expected tick range\n *\n * A revert is issued even though price being equal to the lower bound as that can not\n * be within the approved tick range.\n */\n if (\n _currentPrice <= sqrtPriceTickLower ||\n _currentPrice >= sqrtPriceTickHigher\n ) {\n if (_throwException) {\n revert OutsideExpectedTickRange();\n }\n\n return (false, _currentPrice <= sqrtPriceTickLower ? 0 : 1e18);\n }\n\n // 18 decimal number expressed WETH tick share\n _wethSharePct = _getWethShare(_currentPrice);\n\n if (\n _wethSharePct < allowedWethShareStart ||\n _wethSharePct > allowedWethShareEnd\n ) {\n if (_throwException) {\n revert PoolRebalanceOutOfBounds(\n _wethSharePct,\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n return (false, _wethSharePct);\n }\n\n return (true, _wethSharePct);\n }\n\n /**\n * @notice Rebalance the pool to the desired token split and Deposit any WETH on the contract to the\n * underlying rooster pool. Print the required amount of corresponding OETH. After the rebalancing is\n * done burn any potentially remaining OETH tokens still on the strategy contract.\n *\n * This function has a slightly different behaviour depending on the status of the underlying Rooster\n * pool. The function consists of the following 3 steps:\n * 1. withdrawLiquidityOption -> this is a configurable option where either only part of the liquidity\n * necessary for the swap is removed, or all of it. This way the rebalance\n * is able to optimize for volume, for efficiency or anything in between\n * 2. swapToDesiredPosition -> move active trading price in the pool to be able to deposit WETH & OETH\n * tokens with the desired pre-configured ratios\n * 3. addLiquidity -> add liquidity into the pool respecting ratio split configuration\n *\n *\n * Exact _amountToSwap, _swapWeth & _minTokenReceived parameters shall be determined by simulating the\n * transaction off-chain. The strategy checks that after the swap the share of the tokens is in the\n * expected ranges.\n *\n * @param _amountToSwap The amount of the token to swap\n * @param _swapWeth Swap using WETH when true, use OETH when false\n * @param _minTokenReceived Slippage check -> minimum amount of token expected in return\n * @param _liquidityToRemovePct Percentage of liquidity to remove -> the percentage amount of liquidity to\n * remove before performing the swap. 1e18 denominated\n */\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived,\n uint256 _liquidityToRemovePct\n ) external nonReentrant onlyGovernorOrStrategist {\n _rebalance(\n _amountToSwap,\n _swapWeth,\n _minTokenReceived,\n _liquidityToRemovePct\n );\n }\n\n // slither-disable-start reentrancy-no-eth\n function _rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived,\n uint256 _liquidityToRemovePct\n ) internal {\n // Remove the required amount of liquidity\n if (_liquidityToRemovePct > 0) {\n _removeLiquidity(_liquidityToRemovePct);\n }\n\n // in some cases (e.g. deposits) we will just want to add liquidity and not\n // issue a swap to move the active trading position within the pool. Before or after a\n // deposit or as a standalone call the strategist might issue a rebalance to move the\n // active trading price to a more desired position.\n if (_amountToSwap > 0) {\n // In case liquidity has been removed and there is still not enough WETH owned by the\n // strategy contract remove additional required amount of WETH.\n if (_swapWeth) _ensureWETHBalance(_amountToSwap);\n\n _swapToDesiredPosition(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n\n // calling check liquidity early so we don't get unexpected errors when adding liquidity\n // in the later stages of this function\n _checkForExpectedPoolPrice(true);\n\n _addLiquidity();\n\n // this call shouldn't be necessary, since adding liquidity shouldn't affect the active\n // trading price. It is a defensive programming measure.\n (, uint256 _wethSharePct) = _checkForExpectedPoolPrice(true);\n\n // revert if protocol insolvent\n _solvencyAssert();\n\n emit PoolRebalanced(_wethSharePct);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @dev Perform a swap so that after the swap the tick has the desired WETH to OETH token share.\n */\n function _swapToDesiredPosition(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n IERC20 _tokenToSwap = IERC20(_swapWeth ? WETH : OETH);\n uint256 _balance = _tokenToSwap.balanceOf(address(this));\n\n if (_balance < _amountToSwap) {\n // This should never trigger since _ensureWETHBalance will already\n // throw an error if there is not enough WETH\n if (_swapWeth) {\n revert NotEnoughWethForSwap(_balance, _amountToSwap);\n }\n // if swapping OETH\n uint256 mintForSwap = _amountToSwap - _balance;\n IVault(vaultAddress).mintForStrategy(mintForSwap);\n }\n\n // SafeERC20 is used for IERC20 transfers. Not sure why slither complains\n // slither-disable-next-line unchecked-transfer\n _tokenToSwap.transfer(address(mPool), _amountToSwap);\n\n // tickLimit: the furthest tick a swap will execute in. If no limit is desired,\n // value should be set to type(int32).max for a tokenAIn (WETH) swap\n // and type(int32).min for a swap where tokenB (OETH) is the input\n\n IMaverickV2Pool.SwapParams memory swapParams = IMaverickV2Pool\n // exactOutput defines whether the amount specified is the output\n // or the input amount of the swap\n .SwapParams({\n amount: _amountToSwap,\n tokenAIn: _swapWeth,\n exactOutput: false,\n tickLimit: TICK_NUMBER\n });\n\n // swaps without a callback as the assets are already sent to the pool\n (, uint256 amountOut) = mPool.swap(\n address(this),\n swapParams,\n bytes(\"\")\n );\n\n /**\n * There could be additional checks here for validating minTokenReceived is within the\n * expected range (e.g. 99% - 101% of the token sent in). Though that doesn't provide\n * any additional security. After the swap the `_checkForExpectedPoolPrice` validates\n * that the swap has moved the price into the expected tick (# -1).\n *\n * If the guardian forgets to set a `_minTokenReceived` and a sandwich attack bends\n * the pool before the swap the `_checkForExpectedPoolPrice` will fail the transaction.\n *\n * A check would not prevent a compromised guardian from stealing funds as multiple\n * transactions each loosing smaller amount of funds are still possible.\n */\n if (amountOut < _minTokenReceived) {\n revert SlippageCheck(amountOut);\n }\n\n /**\n * In the interest of each function in `_rebalance` to leave the contract state as\n * clean as possible the OETH tokens here are burned. This decreases the\n * dependence where `_swapToDesiredPosition` function relies on later functions\n * (`addLiquidity`) to burn the OETH. Reducing the risk of error introduction.\n */\n _burnOethOnTheContract();\n }\n\n /**\n * @dev This function removes the appropriate amount of liquidity to ensure that the required\n * amount of WETH is available on the contract\n *\n * @param _amount WETH balance required on the contract\n */\n function _ensureWETHBalance(uint256 _amount) internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance >= _amount) {\n return;\n }\n\n require(tokenId != 0, \"No liquidity available\");\n uint256 _additionalWethRequired = _amount - _wethBalance;\n (uint256 _wethInThePool, ) = getPositionPrincipal();\n\n if (_wethInThePool < _additionalWethRequired) {\n revert NotEnoughWethLiquidity(\n _wethInThePool,\n _additionalWethRequired\n );\n }\n\n uint256 shareOfWethToRemove = _wethInThePool <= 1\n ? 1e18\n : Math_v5.min(\n /**\n * When dealing with shares of liquidity to remove there is always some\n * rounding involved. After extensive fuzz testing the below approach\n * yielded the best results where the strategy overdraws the least and\n * never removes insufficient amount of WETH.\n */\n (_additionalWethRequired + 2).divPrecisely(_wethInThePool - 1) +\n 2,\n 1e18\n );\n\n _removeLiquidity(shareOfWethToRemove);\n }\n\n /**\n * @dev Decrease partial or all liquidity from the pool.\n * @param _liquidityToDecrease The amount of liquidity to remove denominated in 1e18\n */\n function _removeLiquidity(uint256 _liquidityToDecrease) internal {\n require(_liquidityToDecrease > 0, \"Must remove some liquidity\");\n require(\n _liquidityToDecrease <= 1e18,\n \"Can not remove more than 100% of liquidity\"\n );\n\n // 0 indicates the first (and only) bin in the NFT LP position.\n IMaverickV2Pool.RemoveLiquidityParams memory params = maverickPosition\n .getRemoveParams(tokenId, 0, _liquidityToDecrease);\n (uint256 _amountWeth, uint256 _amountOeth) = maverickPosition\n .removeLiquidityToSender(tokenId, mPool, params);\n\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n\n // needs to be called after the _updateUnderlyingAssets so the updated amount is reflected\n // in the event\n emit LiquidityRemoved(\n _liquidityToDecrease,\n _amountWeth,\n _amountOeth,\n underlyingAssets\n );\n }\n\n /**\n * @dev Burns any OETH tokens remaining on the strategy contract if the balance is\n * above the action threshold.\n */\n function _burnOethOnTheContract() internal {\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(_oethBalance);\n }\n\n /**\n * @notice Returns the percentage of WETH liquidity in the configured ticker\n * owned by this strategy contract.\n * @return uint256 1e18 denominated percentage expressing the share\n */\n function getWETHShare() external view returns (uint256) {\n uint256 _currentPrice = getPoolSqrtPrice();\n return _getWethShare(_currentPrice);\n }\n\n /**\n * @dev Returns the share of WETH in tick denominated in 1e18\n */\n function _getWethShare(uint256 _currentPrice)\n internal\n view\n returns (uint256)\n {\n (\n uint256 wethAmount,\n uint256 oethAmount\n ) = _reservesInTickForGivenPriceAndLiquidity(\n sqrtPriceTickLower,\n sqrtPriceTickHigher,\n _currentPrice,\n 1e24\n );\n\n return wethAmount.divPrecisely(wethAmount + oethAmount);\n }\n\n /**\n * @notice Returns the current pool price in square root\n * @return Square root of the pool price\n */\n function getPoolSqrtPrice() public view returns (uint256) {\n return poolLens.getPoolSqrtPrice(mPool);\n }\n\n /**\n * @notice Returns the current active trading tick of the underlying pool\n * @return _currentTick Current pool trading tick\n */\n function getCurrentTradingTick() public view returns (int32 _currentTick) {\n _currentTick = mPool.getState().activeTick;\n }\n\n /**\n * @notice Mint the initial NFT position\n *\n * @dev This amount is \"gifted\" to the strategy contract and will count as a yield\n * surplus.\n */\n // slither-disable-start reentrancy-no-eth\n function mintInitialPosition() external onlyGovernor nonReentrant {\n require(tokenId == 0, \"Initial position already minted\");\n (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n ) = _getAddLiquidityParams(1e16, 1e16);\n\n // Mint rounded up OETH amount\n if (OETHRequired > 0) {\n IVault(vaultAddress).mintForStrategy(OETHRequired);\n }\n\n _approveTokenAmounts(WETHRequired, OETHRequired);\n\n // Store the tokenId before calling updateUnderlyingAssets as it relies on the tokenId\n // not being 0\n (, , , tokenId) = liquidityManager.mintPositionNftToSender(\n mPool,\n packedSqrtPriceBreaks,\n packedArgs\n );\n\n // burn remaining OETH\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @notice Returns the balance of tokens the strategy holds in the LP position\n * @return _amountWeth Amount of WETH in position\n * @return _amountOeth Amount of OETH in position\n */\n function getPositionPrincipal()\n public\n view\n returns (uint256 _amountWeth, uint256 _amountOeth)\n {\n if (tokenId == 0) {\n return (0, 0);\n }\n\n (_amountWeth, _amountOeth, ) = _getPositionInformation();\n }\n\n /**\n * @dev Returns the balance of tokens the strategy holds in the LP position\n * @return _amountWeth Amount of WETH in position\n * @return _amountOeth Amount of OETH in position\n * @return liquidity Amount of liquidity in the position\n */\n function _getPositionInformation()\n internal\n view\n returns (\n uint256 _amountWeth,\n uint256 _amountOeth,\n uint256 liquidity\n )\n {\n IMaverickV2Position.PositionFullInformation\n memory positionInfo = maverickPosition.tokenIdPositionInformation(\n tokenId,\n 0\n );\n\n require(\n positionInfo.liquidities.length == 1,\n \"Unexpected liquidities length\"\n );\n require(positionInfo.ticks.length == 1, \"Unexpected ticks length\");\n\n _amountWeth = positionInfo.amountA;\n _amountOeth = positionInfo.amountB;\n liquidity = positionInfo.liquidities[0];\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99.8%) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethSupply = IERC20(OETH).totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethSupply) < SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /**\n * @dev Collect Rooster reward token, and send it to the harvesterAddress\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Do nothing if there's no position minted\n if (tokenId > 0) {\n uint32[] memory binIds = new uint32[](1);\n IMaverickV2Pool.TickState memory tickState = mPool.getTick(\n TICK_NUMBER\n );\n // get the binId for the MAV_STATIC_BIN_KIND in tick TICK_NUMBER (-1)\n binIds[0] = tickState.binIdsByTick[0];\n\n uint256 lastEpoch = votingDistributor.lastEpoch();\n\n poolDistributor.claimLp(\n address(this),\n tokenId,\n mPool,\n binIds,\n lastEpoch\n );\n }\n\n // Run the internal inherited function\n _collectRewardTokens();\n }\n\n /***************************************\n Balances and Fees\n ****************************************/\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256)\n {\n require(_asset == WETH, \"Only WETH supported\");\n\n // because of PoolLens inaccuracy there is usually some dust WETH left on the contract\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n // just paranoia check, in case there is OETH in the strategy that for some reason hasn't\n // been burned yet. This should always be 0.\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n return underlyingAssets + _wethBalance + _oethBalance;\n }\n\n /// @dev This function updates the amount of underlying assets with the approach of the least possible\n /// total tokens extracted for the current liquidity in the pool.\n function _updateUnderlyingAssets() internal {\n /**\n * Our net value represent the smallest amount of tokens we are able to extract from the position\n * given our liquidity.\n *\n * The least amount of tokens ex-tractable from the position is where the active trading price is\n * at the edge between tick -1 & tick 0. There the pool is offering 1:1 trades between WETH & OETH.\n * At that moment the pool consists completely of WETH and no OETH.\n *\n * The more swaps from OETH -> WETH happen on the pool the more the price starts to move away from the tick 0\n * towards the middle of tick -1 making OETH (priced in WETH) cheaper.\n */\n\n uint256 _wethAmount = tokenId == 0 ? 0 : _balanceInPosition();\n\n underlyingAssets = _wethAmount;\n emit UnderlyingAssetsUpdated(_wethAmount);\n }\n\n /**\n * @dev Strategy reserves (which consist only of WETH in case of Rooster - Plume pool)\n * when the tick price is closest to parity - assuring the lowest amount of tokens\n * returned for the current position liquidity.\n */\n function _balanceInPosition() internal view returns (uint256 _wethBalance) {\n (, , uint256 liquidity) = _getPositionInformation();\n\n uint256 _oethBalance;\n\n (_wethBalance, _oethBalance) = _reservesInTickForGivenPriceAndLiquidity(\n sqrtPriceTickLower,\n sqrtPriceTickHigher,\n sqrtPriceAtParity,\n liquidity\n );\n\n require(_oethBalance == 0, \"Non zero oethBalance\");\n }\n\n /**\n * @notice Tick dominance denominated in 1e18\n * @return _tickDominance The share of liquidity in TICK_NUMBER tick owned\n * by the strategy contract denominated in 1e18\n */\n function tickDominance() public view returns (uint256 _tickDominance) {\n IMaverickV2Pool.TickState memory tickState = mPool.getTick(TICK_NUMBER);\n\n uint256 wethReserve = tickState.reserveA;\n uint256 oethReserve = tickState.reserveB;\n\n // prettier-ignore\n (uint256 _amountWeth, uint256 _amountOeth, ) = _getPositionInformation();\n\n if (wethReserve + oethReserve == 0) {\n return 0;\n }\n\n _tickDominance = (_amountWeth + _amountOeth).divPrecisely(\n wethReserve + oethReserve\n );\n }\n\n /***************************************\n Hidden functions\n ****************************************/\n /**\n * @dev Unsupported\n */\n function setPTokenAddress(address, address) external pure override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function removePToken(uint256) external pure override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function _abstractSetPToken(address, address) internal pure override {\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function safeApproveAllTokens() external pure override {\n // all the amounts are approved at the time required\n revert(\"Unsupported method\");\n }\n\n /***************************************\n Maverick liquidity utilities\n ****************************************/\n\n /// @notice Calculates deltaA = liquidity * (sqrt(upper) - sqrt(lower))\n /// Calculates deltaB = liquidity / sqrt(lower) - liquidity / sqrt(upper),\n /// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower))\n ///\n /// @dev refactored from here:\n // solhint-disable-next-line max-line-length\n /// https://github.com/rooster-protocol/rooster-contracts/blob/main/v2-supplemental/contracts/libraries/LiquidityUtilities.sol#L665-L695\n function _reservesInTickForGivenPriceAndLiquidity(\n uint256 _lowerSqrtPrice,\n uint256 _upperSqrtPrice,\n uint256 _newSqrtPrice,\n uint256 _liquidity\n ) internal pure returns (uint128 reserveA, uint128 reserveB) {\n if (_liquidity == 0) {\n (reserveA, reserveB) = (0, 0);\n } else {\n uint256 lowerEdge = MathRooster.max(_lowerSqrtPrice, _newSqrtPrice);\n\n reserveA = MathRooster\n .mulCeil(\n _liquidity,\n MathRooster.clip(\n MathRooster.min(_upperSqrtPrice, _newSqrtPrice),\n _lowerSqrtPrice\n )\n )\n .toUint128();\n reserveB = MathRooster\n .mulDivCeil(\n _liquidity,\n 1e18 * MathRooster.clip(_upperSqrtPrice, lowerEdge),\n _upperSqrtPrice * lowerEdge\n )\n .toUint128();\n }\n }\n}\n" + }, + "contracts/strategies/sonic/SonicStakingStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SonicValidatorDelegator } from \"./SonicValidatorDelegator.sol\";\nimport { IWrappedSonic } from \"../../interfaces/sonic/IWrappedSonic.sol\";\n\n/**\n * @title Staking Strategy for Sonic's native S currency\n * @author Origin Protocol Inc\n */\ncontract SonicStakingStrategy is SonicValidatorDelegator {\n // For future use\n uint256[50] private __gap;\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wrappedSonic,\n address _sfc\n ) SonicValidatorDelegator(_baseConfig, _wrappedSonic, _sfc) {}\n\n /// @notice Deposit wrapped S asset into the underlying platform.\n /// @param _asset Address of asset to deposit. Has to be Wrapped Sonic (wS).\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposit Wrapped Sonic (wS) to this strategy and delegate to a validator.\n * @param _asset Address of Wrapped Sonic (wS) token\n * @param _amount Amount of Wrapped Sonic (wS) to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal virtual {\n require(_amount > 0, \"Must deposit something\");\n\n _delegate(_amount);\n emit Deposit(_asset, address(0), _amount);\n }\n\n /**\n * @notice Deposit the entire balance of wrapped S in this strategy contract into\n * the underlying platform.\n */\n function depositAll() external virtual override onlyVault nonReentrant {\n uint256 wSBalance = IERC20(wrappedSonic).balanceOf(address(this));\n\n if (wSBalance > 0) {\n _deposit(wrappedSonic, wSBalance);\n }\n }\n\n /// @notice Withdraw Wrapped Sonic (wS) from this strategy contract.\n /// Used only if some wS is lingering on the contract.\n /// That can happen only when someone sends wS directly to this contract\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset Address of the Wrapped Sonic (wS) token\n /// @param _amount Amount of Wrapped Sonic (wS) to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal override {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(_asset).transfer(_recipient, _amount);\n\n emit Withdrawal(wrappedSonic, address(0), _amount);\n }\n\n /// @notice Transfer all Wrapped Sonic (wS) deposits back to the vault.\n /// This does not withdraw from delegated validators. That has to be done separately with `undelegate`.\n /// Any native S in this strategy will be withdrawn.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 balance = address(this).balance;\n if (balance > 0) {\n IWrappedSonic(wrappedSonic).deposit{ value: balance }();\n }\n uint256 wSBalance = IERC20(wrappedSonic).balanceOf(address(this));\n if (wSBalance > 0) {\n _withdraw(vaultAddress, wrappedSonic, wSBalance);\n }\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset token\n */\n function supportsAsset(address _asset)\n public\n view\n virtual\n override\n returns (bool)\n {\n return _asset == wrappedSonic;\n }\n\n /**\n * @notice is not supported for this strategy as the\n * Wrapped Sonic (wS) token is set at deploy time.\n */\n function setPTokenAddress(address, address)\n external\n view\n override\n onlyGovernor\n {\n revert(\"unsupported function\");\n }\n\n /// @notice is not used by this strategy as all staking rewards are restaked\n function collectRewardTokens() external override nonReentrant {\n revert(\"unsupported function\");\n }\n\n /**\n * @notice is not supported for this strategy as the\n * Wrapped Sonic (wS) token is set at deploy time.\n */\n function removePToken(uint256) external view override onlyGovernor {\n revert(\"unsupported function\");\n }\n\n /// @dev is not used by this strategy but must be implemented as it's abstract\n /// in the inherited `InitializableAbstractStrategy` contract.\n function _abstractSetPToken(address, address) internal virtual override {}\n\n /// @notice is not used by this strategy\n function safeApproveAllTokens() external override onlyGovernor {}\n}\n" + }, + "contracts/strategies/sonic/SonicSwapXAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title SwapX Algorithmic Market Maker (AMO) Strategy\n * @notice AMO strategy for the SwapX OS/wS stable pool\n * @author Origin Protocol Inc\n */\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport { sqrt } from \"../../utils/PRBMath.sol\";\nimport { IBasicToken } from \"../../interfaces/IBasicToken.sol\";\nimport { IPair } from \"../../interfaces/sonic/ISwapXPair.sol\";\nimport { IGauge } from \"../../interfaces/sonic/ISwapXGauge.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\n\ncontract SonicSwapXAMOStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @notice a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n /// @notice Precision for the SwapX Stable AMM (sAMM) invariant k.\n uint256 public constant PRECISION = 1e18;\n\n /// @notice Address of the Wrapped S (wS) token.\n address public immutable ws;\n\n /// @notice Address of the OS token contract.\n address public immutable os;\n\n /// @notice Address of the SwapX Stable pool contract.\n address public immutable pool;\n\n /// @notice Address of the SwapX Gauge contract.\n address public immutable gauge;\n\n /// @notice The max amount the OS/wS price can deviate from peg (1e18)\n /// before deposits are reverted scaled to 18 decimals.\n /// eg 0.01e18 or 1e16 is 1% which is 100 basis points.\n /// This is the amount below and above peg so a 50 basis point deviation (0.005e18)\n /// allows a price range from 0.995 to 1.005.\n uint256 public maxDepeg;\n\n event SwapOTokensToPool(\n uint256 osMinted,\n uint256 wsDepositAmount,\n uint256 osDepositAmount,\n uint256 lpTokens\n );\n event SwapAssetsToPool(\n uint256 wsSwapped,\n uint256 lpTokens,\n uint256 osBurnt\n );\n event MaxDepegUpdated(uint256 maxDepeg);\n\n /**\n * @dev Verifies that the caller is the Strategist of the Vault.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Skim the SwapX pool in case any extra wS or OS tokens were added\n */\n modifier skimPool() {\n IPair(pool).skim(address(this));\n _;\n }\n\n /**\n * @dev Checks the pool is balanced enough to allow deposits.\n */\n modifier nearBalancedPool() {\n // OS/wS price = wS / OS\n // Get the OS/wS price for selling 1 OS for wS\n // As OS is 1, the wS amount is the OS/wS price\n uint256 sellPrice = IPair(pool).getAmountOut(1e18, os);\n\n // Get the amount of OS received from selling 1 wS. This is buying OS.\n uint256 osAmount = IPair(pool).getAmountOut(1e18, ws);\n // Convert to a OS/wS price = wS / OS\n uint256 buyPrice = 1e36 / osAmount;\n\n uint256 pegPrice = 1e18;\n\n require(\n sellPrice >= pegPrice - maxDepeg && buyPrice <= pegPrice + maxDepeg,\n \"price out of range\"\n );\n _;\n }\n\n /**\n * @dev Checks the pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do swaps against the pool.\n * Deposits and withdrawals are proportional to the pool's balances hence don't need this check.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the pool\n (uint256 wsReservesBefore, uint256 osReservesBefore, ) = IPair(pool)\n .getReserves();\n // diff = wS balance - OS balance\n int256 diffBefore = wsReservesBefore.toInt256() -\n osReservesBefore.toInt256();\n\n _;\n\n // Get the asset and OToken balances in the pool\n (uint256 wsReservesAfter, uint256 osReservesAfter, ) = IPair(pool)\n .getReserves();\n // diff = wS balance - OS balance\n int256 diffAfter = wsReservesAfter.toInt256() -\n osReservesAfter.toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OS, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"Assets overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of wS, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"OTokens overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n /**\n * @param _baseConfig The `platformAddress` is the address of the SwapX pool.\n * The `vaultAddress` is the address of the Origin Sonic Vault.\n * @param _os Address of the OS token.\n * @param _ws Address of the Wrapped S (wS) token.\n * @param _gauge Address of the SwapX gauge for the pool.\n */\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _os,\n address _ws,\n address _gauge\n ) InitializableAbstractStrategy(_baseConfig) {\n // Check the pool tokens are correct\n require(\n IPair(_baseConfig.platformAddress).token0() == _ws &&\n IPair(_baseConfig.platformAddress).token1() == _os,\n \"Incorrect pool tokens\"\n );\n // Checked both tokens are to 18 decimals\n require(\n IBasicToken(_ws).decimals() == 18 &&\n IBasicToken(_os).decimals() == 18,\n \"Incorrect token decimals\"\n );\n // Check the SwapX pool is a Stable AMM (sAMM)\n require(\n IPair(_baseConfig.platformAddress).isStable() == true,\n \"Pool not stable\"\n );\n // Check the gauge is for the pool\n require(\n IGauge(_gauge).TOKEN() == _baseConfig.platformAddress,\n \"Incorrect gauge\"\n );\n\n // Set the immutable variables\n os = _os;\n ws = _ws;\n pool = _baseConfig.platformAddress;\n gauge = _gauge;\n\n // This is an implementation contract. The governor is set in the proxy contract.\n _setGovernor(address(0));\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as SwapX strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Array containing SWPx token address\n * @param _maxDepeg The max amount the OS/wS price can deviate from peg (1e18) before deposits are reverted.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n uint256 _maxDepeg\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = pool;\n\n address[] memory _assets = new address[](1);\n _assets[0] = ws;\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n maxDepeg = _maxDepeg;\n\n _approveBase();\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit an amount of Wrapped S (wS) into the SwapX pool.\n * Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @dev This tx must be wrapped by the VaultValueChecker.\n * To minimize loses, the pool should be rebalanced before depositing.\n * The pool's OS/wS price must be within the maxDepeg range.\n * @param _asset Address of Wrapped S (wS) token.\n * @param _wsAmount Amount of Wrapped S (wS) tokens to deposit.\n */\n function deposit(address _asset, uint256 _wsAmount)\n external\n override\n onlyVault\n nonReentrant\n skimPool\n nearBalancedPool\n {\n require(_asset == ws, \"Unsupported asset\");\n require(_wsAmount > 0, \"Must deposit something\");\n\n (uint256 osDepositAmount, ) = _deposit(_wsAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the deposited wS tokens\n emit Deposit(ws, pool, _wsAmount);\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osDepositAmount);\n }\n\n /**\n * @notice Deposit all the strategy's Wrapped S (wS) tokens into the SwapX pool.\n * Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @dev This tx must be wrapped by the VaultValueChecker.\n * To minimize loses, the pool should be rebalanced before depositing.\n * The pool's OS/wS price must be within the maxDepeg range.\n */\n function depositAll()\n external\n override\n onlyVault\n nonReentrant\n skimPool\n nearBalancedPool\n {\n uint256 wsBalance = IERC20(ws).balanceOf(address(this));\n if (wsBalance > 0) {\n (uint256 osDepositAmount, ) = _deposit(wsBalance);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the deposited wS tokens\n emit Deposit(ws, pool, wsBalance);\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osDepositAmount);\n }\n }\n\n /**\n * @dev Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @param _wsAmount Amount of Wrapped S (wS) tokens to deposit.\n * @return osDepositAmount Amount of OS tokens minted and deposited into the pool.\n * @return lpTokens Amount of SwapX pool LP tokens minted and deposited into the gauge.\n */\n function _deposit(uint256 _wsAmount)\n internal\n returns (uint256 osDepositAmount, uint256 lpTokens)\n {\n // Calculate the required amount of OS to mint based on the wS amount.\n osDepositAmount = _calcTokensToMint(_wsAmount);\n\n // Mint the required OS tokens to this strategy\n IVault(vaultAddress).mintForStrategy(osDepositAmount);\n\n // Add wS and OS liquidity to the pool and stake in gauge\n lpTokens = _depositToPoolAndGauge(_wsAmount, osDepositAmount);\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw wS and OS from the SwapX pool, burn the OS,\n * and transfer the wS to the recipient.\n * @param _recipient Address of the Vault.\n * @param _asset Address of the Wrapped S (wS) contract.\n * @param _wsAmount Amount of Wrapped S (wS) to withdraw.\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _wsAmount\n ) external override onlyVault nonReentrant skimPool {\n require(_wsAmount > 0, \"Must withdraw something\");\n require(_asset == ws, \"Unsupported asset\");\n // This strategy can't be set as a default strategy for wS in the Vault.\n // This means the recipient must always be the Vault.\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n // Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n uint256 lpTokens = _calcTokensToBurn(_wsAmount);\n\n // Withdraw pool LP tokens from the gauge and remove assets from from the pool\n _withdrawFromGaugeAndPool(lpTokens);\n\n // Burn all the removed OS and any that was left in the strategy\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Transfer wS to the recipient\n // Note there can be a dust amount of wS left in the strategy as\n // the burn of the pool's LP tokens is rounded up\n require(\n IERC20(ws).balanceOf(address(this)) >= _wsAmount,\n \"Not enough wS removed from pool\"\n );\n IERC20(ws).safeTransfer(_recipient, _wsAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the withdrawn wS tokens\n emit Withdrawal(ws, pool, _wsAmount);\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n }\n\n /**\n * @notice Withdraw all pool LP tokens from the gauge,\n * remove all wS and OS from the SwapX pool,\n * burn all the OS tokens,\n * and transfer all the wS to the Vault contract.\n * @dev There is no solvency check here as withdrawAll can be called to\n * quickly secure assets to the Vault in emergencies.\n */\n function withdrawAll()\n external\n override\n onlyVaultOrGovernor\n nonReentrant\n skimPool\n {\n // Get all the pool LP tokens the strategy has staked in the gauge\n uint256 lpTokens = IGauge(gauge).balanceOf(address(this));\n // Can not withdraw zero LP tokens from the gauge\n if (lpTokens == 0) return;\n\n if (IGauge(gauge).emergency()) {\n // The gauge is in emergency mode\n _emergencyWithdrawFromGaugeAndPool();\n } else {\n // Withdraw pool LP tokens from the gauge and remove assets from from the pool\n _withdrawFromGaugeAndPool(lpTokens);\n }\n\n // Burn all OS in this strategy contract\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Get the strategy contract's wS balance.\n // This includes all that was removed from the SwapX pool and\n // any that was sitting in the strategy contract before the removal.\n uint256 wsBalance = IERC20(ws).balanceOf(address(this));\n IERC20(ws).safeTransfer(vaultAddress, wsBalance);\n\n // Emit event for the withdrawn wS tokens\n emit Withdrawal(ws, pool, wsBalance);\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n }\n\n /***************************************\n Pool Rebalancing\n ****************************************/\n\n /** @notice Used when there is more OS than wS in the pool.\n * wS and OS is removed from the pool, the received wS is swapped for OS\n * and the left over OS in the strategy is burnt.\n * The OS/wS price is < 1.0 so OS is being bought at a discount.\n * @param _wsAmount Amount of Wrapped S (wS) to swap into the pool.\n */\n function swapAssetsToPool(uint256 _wsAmount)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n skimPool\n {\n require(_wsAmount > 0, \"Must swap something\");\n\n // 1. Partially remove liquidity so there’s enough wS for the swap\n\n // Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n uint256 lpTokens = _calcTokensToBurn(_wsAmount);\n require(lpTokens > 0, \"No LP tokens to burn\");\n\n _withdrawFromGaugeAndPool(lpTokens);\n\n // 2. Swap wS for OS against the pool\n // Swap exact amount of wS for OS against the pool\n // There can be a dust amount of wS left in the strategy as the burn of the pool's LP tokens is rounded up\n _swapExactTokensForTokens(_wsAmount, ws, os);\n\n // 3. Burn all the OS left in the strategy from the remove liquidity and swap\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n // Emit event for the swap\n emit SwapAssetsToPool(_wsAmount, lpTokens, osToBurn);\n }\n\n /**\n * @notice Used when there is more wS than OS in the pool.\n * OS is minted and swapped for wS against the pool,\n * more OS is minted and added back into the pool with the swapped out wS.\n * The OS/wS price is > 1.0 so OS is being sold at a premium.\n * @param _osAmount Amount of OS to swap into the pool.\n */\n function swapOTokensToPool(uint256 _osAmount)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n skimPool\n {\n require(_osAmount > 0, \"Must swap something\");\n\n // 1. Mint OS so it can be swapped into the pool\n\n // There can be OS in the strategy from skimming the pool\n uint256 osInStrategy = IERC20(os).balanceOf(address(this));\n require(_osAmount >= osInStrategy, \"Too much OS in strategy\");\n uint256 osToMint = _osAmount - osInStrategy;\n\n // Mint the required OS tokens to this strategy\n IVault(vaultAddress).mintForStrategy(osToMint);\n\n // 2. Swap OS for wS against the pool\n _swapExactTokensForTokens(_osAmount, os, ws);\n\n // The wS is from the swap and any wS that was sitting in the strategy\n uint256 wsDepositAmount = IERC20(ws).balanceOf(address(this));\n\n // 3. Add wS and OS back to the pool in proportion to the pool's reserves\n (uint256 osDepositAmount, uint256 lpTokens) = _deposit(wsDepositAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osToMint + osDepositAmount);\n // Emit event for the swap\n emit SwapOTokensToPool(\n osToMint,\n wsDepositAmount,\n osDepositAmount,\n lpTokens\n );\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Get the wS value of assets in the strategy and SwapX pool.\n * The value of the assets in the pool is calculated assuming the pool is balanced.\n * This way the value can not be manipulated by changing the pool's token balances.\n * @param _asset Address of the Wrapped S (wS) token\n * @return balance Total value in wS.\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == ws, \"Unsupported asset\");\n\n // wS balance needed here for the balance check that happens from vault during depositing.\n balance = IERC20(ws).balanceOf(address(this));\n\n // This assumes 1 gauge LP token = 1 pool LP token\n uint256 lpTokens = IGauge(gauge).balanceOf(address(this));\n if (lpTokens == 0) return balance;\n\n // Add the strategy’s share of the wS and OS tokens in the SwapX pool if the pool was balanced.\n balance += _lpValue(lpTokens);\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == ws;\n }\n\n /**\n * @notice Collect accumulated SWPx (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect SWPx rewards from the gauge\n IGauge(gauge).getReward();\n\n _collectRewardTokens();\n }\n\n /***************************************\n Internal SwapX Pool and Gauge Functions\n ****************************************/\n\n /**\n * @dev Calculate the required amount of OS to mint based on the wS amount.\n * This ensures the proportion of OS tokens being added to the pool matches the proportion of wS tokens.\n * For example, if the added wS tokens is 10% of existing wS tokens in the pool,\n * then the OS tokens being added should also be 10% of the OS tokens in the pool.\n * @param _wsAmount Amount of Wrapped S (wS) to be added to the pool.\n * @return osAmount Amount of OS to be minted and added to the pool.\n */\n function _calcTokensToMint(uint256 _wsAmount)\n internal\n view\n returns (uint256 osAmount)\n {\n (uint256 wsReserves, uint256 osReserves, ) = IPair(pool).getReserves();\n require(wsReserves > 0, \"Empty pool\");\n\n // OS to add = (wS being added * OS in pool) / wS in pool\n osAmount = (_wsAmount * osReserves) / wsReserves;\n }\n\n /**\n * @dev Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n * from the pool.\n * @param _wsAmount Amount of Wrapped S (wS) to be removed from the pool.\n * @return lpTokens Amount of SwapX pool LP tokens to burn.\n */\n function _calcTokensToBurn(uint256 _wsAmount)\n internal\n view\n returns (uint256 lpTokens)\n {\n /* The SwapX pool proportionally returns the reserve tokens when removing liquidity.\n * First, calculate the proportion of required wS tokens against the pools wS reserves.\n * That same proportion is used to calculate the required amount of pool LP tokens.\n * For example, if the required wS tokens is 10% of the pool's wS reserves,\n * then 10% of the pool's LP supply needs to be burned.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognizant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on, the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n (uint256 wsReserves, , ) = IPair(pool).getReserves();\n require(wsReserves > 0, \"Empty pool\");\n\n lpTokens = (_wsAmount * IPair(pool).totalSupply()) / wsReserves;\n lpTokens += 1; // Add 1 to ensure we get enough LP tokens with rounding\n }\n\n /**\n * @dev Deposit Wrapped S (wS) and OS liquidity to the SwapX pool\n * and stake the pool's LP token in the gauge.\n * @param _wsAmount Amount of Wrapped S (wS) to deposit.\n * @param _osAmount Amount of OS to deposit.\n * @return lpTokens Amount of SwapX pool LP tokens minted.\n */\n function _depositToPoolAndGauge(uint256 _wsAmount, uint256 _osAmount)\n internal\n returns (uint256 lpTokens)\n {\n // Transfer wS to the pool\n IERC20(ws).safeTransfer(pool, _wsAmount);\n // Transfer OS to the pool\n IERC20(os).safeTransfer(pool, _osAmount);\n\n // Mint LP tokens from the pool\n lpTokens = IPair(pool).mint(address(this));\n\n // Deposit the pool's LP tokens into the gauge\n IGauge(gauge).deposit(lpTokens);\n }\n\n /**\n * @dev Withdraw pool LP tokens from the gauge and remove wS and OS from the pool.\n * @param _lpTokens Amount of SwapX pool LP tokens to withdraw from the gauge\n */\n function _withdrawFromGaugeAndPool(uint256 _lpTokens) internal {\n require(\n IGauge(gauge).balanceOf(address(this)) >= _lpTokens,\n \"Not enough LP tokens in gauge\"\n );\n\n // Withdraw pool LP tokens from the gauge\n IGauge(gauge).withdraw(_lpTokens);\n\n // Transfer the pool LP tokens to the pool\n IERC20(pool).safeTransfer(pool, _lpTokens);\n\n // Burn the LP tokens and transfer the wS and OS back to the strategy\n IPair(pool).burn(address(this));\n }\n\n /**\n * @dev Withdraw all pool LP tokens from the gauge when it's in emergency mode\n * and remove wS and OS from the pool.\n */\n function _emergencyWithdrawFromGaugeAndPool() internal {\n // Withdraw all pool LP tokens from the gauge\n IGauge(gauge).emergencyWithdraw();\n\n // Get the pool LP tokens in strategy\n uint256 _lpTokens = IERC20(pool).balanceOf(address(this));\n\n // Transfer the pool LP tokens to the pool\n IERC20(pool).safeTransfer(pool, _lpTokens);\n\n // Burn the LP tokens and transfer the wS and OS back to the strategy\n IPair(pool).burn(address(this));\n }\n\n /**\n * @dev Swap exact amount of tokens for another token against the pool.\n * @param _amountIn Amount of tokens to swap into the pool.\n * @param _tokenIn Address of the token going into the pool.\n * @param _tokenOut Address of the token being swapped out of the pool.\n */\n function _swapExactTokensForTokens(\n uint256 _amountIn,\n address _tokenIn,\n address _tokenOut\n ) internal {\n // Transfer in tokens to the pool\n IERC20(_tokenIn).safeTransfer(pool, _amountIn);\n\n // Calculate how much out tokens we get from the swap\n uint256 amountOut = IPair(pool).getAmountOut(_amountIn, _tokenIn);\n\n // Safety check that we are dealing with the correct pool tokens\n require(\n (_tokenIn == ws && _tokenOut == os) ||\n (_tokenIn == os && _tokenOut == ws),\n \"Unsupported swap\"\n );\n\n // Work out the correct order of the amounts for the pool\n (uint256 amount0, uint256 amount1) = _tokenIn == ws\n ? (uint256(0), amountOut)\n : (amountOut, 0);\n\n // Perform the swap on the pool\n IPair(pool).swap(amount0, amount1, address(this), new bytes(0));\n\n // The slippage protection against the amount out is indirectly done\n // via the improvePoolBalance\n }\n\n /// @dev Calculate the value of a LP position in a SwapX stable pool\n /// if the pool was balanced.\n /// @param _lpTokens Amount of LP tokens in the SwapX pool\n /// @return value The wS value of the LP tokens when the pool is balanced\n function _lpValue(uint256 _lpTokens) internal view returns (uint256 value) {\n // Get total supply of LP tokens\n uint256 totalSupply = IPair(pool).totalSupply();\n if (totalSupply == 0) return 0;\n\n // Get the current reserves of the pool\n (uint256 wsReserves, uint256 osReserves, ) = IPair(pool).getReserves();\n\n // Calculate the invariant of the pool assuming both tokens have 18 decimals.\n // k is scaled to 18 decimals.\n uint256 k = _invariant(wsReserves, osReserves);\n\n // If x = y, let’s denote x = y = z (where z is the common reserve value)\n // Substitute z into the invariant:\n // k = z^3 * z + z * z^3\n // k = 2 * z^4\n // Going back the other way to calculate the common reserve value z\n // z = (k / 2) ^ (1/4)\n // the total value of the pool when x = y is 2 * z, which is 2 * (k / 2) ^ (1/4)\n uint256 zSquared = sqrt((k * 1e18) / 2); // 18 + 18 = 36 decimals becomes 18 decimals after sqrt\n uint256 z = sqrt(zSquared * 1e18); // 18 + 18 = 36 decimals becomes 18 decimals after sqrt\n uint256 totalValueOfPool = 2 * z;\n\n // lp value = lp tokens * value of pool / total supply\n value = (_lpTokens * totalValueOfPool) / totalSupply;\n }\n\n /**\n * @dev Compute the invariant for a SwapX stable pool.\n * This assumed both x and y tokens are to 18 decimals which is checked in the constructor.\n * invariant: k = x^3 * y + x * y^3\n * @dev This implementation is copied from SwapX's Pair contract.\n * @param _x The amount of Wrapped S (wS) tokens in the pool\n * @param _y The amount of the OS tokens in the pool\n * @return k The invariant of the SwapX stable pool\n */\n function _invariant(uint256 _x, uint256 _y)\n internal\n pure\n returns (uint256 k)\n {\n uint256 _a = (_x * _y) / PRECISION;\n uint256 _b = ((_x * _x) / PRECISION + (_y * _y) / PRECISION);\n // slither-disable-next-line divide-before-multiply\n k = (_a * _b) / PRECISION;\n }\n\n /**\n * @dev Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalSupply = IERC20(os).totalSupply();\n\n if (\n _totalSupply > 0 &&\n _totalVaultValue.divPrecisely(_totalSupply) < SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Setters\n ****************************************/\n\n /**\n * @notice Set the maximum deviation from the OS/wS peg (1e18) before deposits are reverted.\n * @param _maxDepeg the OS/wS price from peg (1e18) in 18 decimals.\n * eg 0.01e18 or 1e16 is 1% which is 100 basis points.\n */\n function setMaxDepeg(uint256 _maxDepeg) external onlyGovernor {\n maxDepeg = _maxDepeg;\n\n emit MaxDepegUpdated(_maxDepeg);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve SwapX gauge contract to transfer SwapX pool LP tokens\n // This is needed for deposits of SwapX pool LP tokens into the gauge.\n // slither-disable-next-line unused-return\n IPair(pool).approve(address(gauge), type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/sonic/SonicValidatorDelegator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { ISFC } from \"../../interfaces/sonic/ISFC.sol\";\nimport { IWrappedSonic } from \"../../interfaces/sonic/IWrappedSonic.sol\";\n\n/**\n * @title Manages delegation to Sonic validators\n * @notice This contract implements all the required functionality to delegate to,\n undelegate from and withdraw from validators.\n * @author Origin Protocol Inc\n */\nabstract contract SonicValidatorDelegator is InitializableAbstractStrategy {\n /// @notice Address of Sonic's wrapped S token\n address public immutable wrappedSonic;\n /// @notice Sonic's Special Fee Contract (SFC)\n ISFC public immutable sfc;\n\n /// @notice a unique ID for each withdrawal request\n uint256 public nextWithdrawId;\n /// @notice Sonic (S) that is pending withdrawal after undelegating\n uint256 public pendingWithdrawals;\n\n /// @notice List of supported validator IDs that can be delegated to\n uint256[] public supportedValidators;\n\n /// @notice Default validator id to deposit to\n uint256 public defaultValidatorId;\n\n struct WithdrawRequest {\n uint256 validatorId;\n uint256 undelegatedAmount;\n uint256 timestamp;\n }\n /// @notice Mapping of withdrawIds to validatorIds and undelegatedAmounts\n mapping(uint256 => WithdrawRequest) public withdrawals;\n\n /// @notice Address of the registrator - allowed to register, exit and remove validators\n address public validatorRegistrator;\n\n // For future use\n uint256[44] private __gap;\n\n event Delegated(uint256 indexed validatorId, uint256 delegatedAmount);\n event Undelegated(\n uint256 indexed withdrawId,\n uint256 indexed validatorId,\n uint256 undelegatedAmount\n );\n event Withdrawn(\n uint256 indexed withdrawId,\n uint256 indexed validatorId,\n uint256 undelegatedAmount,\n uint256 withdrawnAmount\n );\n event RegistratorChanged(address indexed newAddress);\n event SupportedValidator(uint256 indexed validatorId);\n event UnsupportedValidator(uint256 indexed validatorId);\n event DefaultValidatorIdChanged(uint256 indexed validatorId);\n\n /// @dev Throws if called by any account other than the Registrator or Strategist\n modifier onlyRegistratorOrStrategist() {\n require(\n msg.sender == validatorRegistrator ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Registrator or Strategist\"\n );\n _;\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wrappedSonic,\n address _sfc\n ) InitializableAbstractStrategy(_baseConfig) {\n wrappedSonic = _wrappedSonic;\n sfc = ISFC(_sfc);\n }\n\n function initialize() external virtual onlyGovernor initializer {\n address[] memory rewardTokens = new address[](0);\n address[] memory assets = new address[](1);\n address[] memory pTokens = new address[](1);\n\n assets[0] = address(wrappedSonic);\n pTokens[0] = address(platformAddress);\n\n InitializableAbstractStrategy._initialize(\n rewardTokens,\n assets,\n pTokens\n );\n }\n\n /// @notice Returns the total value of Sonic (S) that is delegated validators.\n /// Wrapped Sonic (wS) deposits that are still to be delegated and any undelegated amounts\n /// still pending a withdrawal.\n /// @param _asset Address of Wrapped Sonic (wS) token\n /// @return balance Total value managed by the strategy\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n\n // add the Wrapped Sonic (wS) in the strategy from deposits that are still to be delegated\n // and any undelegated amounts still pending a withdrawal\n balance =\n IERC20(wrappedSonic).balanceOf(address(this)) +\n pendingWithdrawals;\n\n // For each supported validator, get the staked amount and pending rewards\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; i++) {\n uint256 validator = supportedValidators[i];\n balance += sfc.getStake(address(this), validator);\n balance += sfc.pendingRewards(address(this), validator);\n }\n }\n\n /**\n * @dev Delegate from this strategy to a specific Sonic validator. Called\n * automatically on asset deposit\n * @param _amount the amount of Sonic (S) to delegate.\n */\n function _delegate(uint256 _amount) internal {\n require(\n isSupportedValidator(defaultValidatorId),\n \"Validator not supported\"\n );\n\n // unwrap Wrapped Sonic (wS) to native Sonic (S)\n IWrappedSonic(wrappedSonic).withdraw(_amount);\n\n //slither-disable-next-line arbitrary-send-eth\n sfc.delegate{ value: _amount }(defaultValidatorId);\n\n emit Delegated(defaultValidatorId, _amount);\n }\n\n /**\n * @notice Undelegate from a specific Sonic validator.\n * This needs to be followed by a `withdrawFromSFC` two weeks later.\n * @param _validatorId The Sonic validator ID to undelegate from.\n * @param _undelegateAmount the amount of Sonic (S) to undelegate.\n * @return withdrawId The unique ID of the withdrawal request.\n */\n function undelegate(uint256 _validatorId, uint256 _undelegateAmount)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n returns (uint256 withdrawId)\n {\n withdrawId = _undelegate(_validatorId, _undelegateAmount);\n }\n\n function _undelegate(uint256 _validatorId, uint256 _undelegateAmount)\n internal\n returns (uint256 withdrawId)\n {\n // Can still undelegate even if the validator is no longer supported\n require(_undelegateAmount > 0, \"Must undelegate something\");\n\n uint256 amountDelegated = sfc.getStake(address(this), _validatorId);\n require(\n _undelegateAmount <= amountDelegated,\n \"Insufficient delegation\"\n );\n\n withdrawId = nextWithdrawId++;\n\n withdrawals[withdrawId] = WithdrawRequest(\n _validatorId,\n _undelegateAmount,\n block.timestamp\n );\n pendingWithdrawals += _undelegateAmount;\n\n sfc.undelegate(_validatorId, withdrawId, _undelegateAmount);\n\n emit Undelegated(withdrawId, _validatorId, _undelegateAmount);\n }\n\n /**\n * @notice Withdraw native S from a previously undelegated validator.\n * The native S is wrapped wS and transferred to the Vault.\n * @param _withdrawId The unique withdraw ID used to `undelegate`\n * @return withdrawnAmount The amount of Sonic (S) withdrawn.\n * This can be less than the undelegated amount in the event of slashing.\n */\n function withdrawFromSFC(uint256 _withdrawId)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n returns (uint256 withdrawnAmount)\n {\n require(_withdrawId < nextWithdrawId, \"Invalid withdrawId\");\n\n // Can still withdraw even if the validator is no longer supported\n // Load the withdrawal from storage into memory\n WithdrawRequest memory withdrawal = withdrawals[_withdrawId];\n require(!isWithdrawnFromSFC(_withdrawId), \"Already withdrawn\");\n\n withdrawals[_withdrawId].undelegatedAmount = 0;\n pendingWithdrawals -= withdrawal.undelegatedAmount;\n\n uint256 sBalanceBefore = address(this).balance;\n\n // Try to withdraw from SFC\n try sfc.withdraw(withdrawal.validatorId, _withdrawId) {\n // continue below\n } catch (bytes memory err) {\n bytes4 errorSelector = bytes4(err);\n\n // If the validator has been fully slashed, SFC's withdraw function will\n // revert with a StakeIsFullySlashed custom error.\n if (errorSelector == ISFC.StakeIsFullySlashed.selector) {\n // The validator was fully slashed, so all the delegated amounts were lost.\n // Will swallow the error as we still want to update the\n // withdrawals and pendingWithdrawals storage variables.\n\n // The return param defaults to zero but lets set it explicitly so it's clear\n withdrawnAmount = 0;\n\n emit Withdrawn(\n _withdrawId,\n withdrawal.validatorId,\n withdrawal.undelegatedAmount,\n withdrawnAmount\n );\n\n // Exit here as there is nothing to transfer to the Vault\n return withdrawnAmount;\n } else {\n // Bubble up any other SFC custom errors.\n // Inline assembly is currently the only way to generically rethrow the exact same custom error\n // from the raw bytes err in a catch block while preserving its original selector and parameters.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n revert(add(32, err), mload(err))\n }\n }\n }\n\n // Set return parameter\n withdrawnAmount = address(this).balance - sBalanceBefore;\n\n // Wrap Sonic (S) to Wrapped Sonic (wS)\n IWrappedSonic(wrappedSonic).deposit{ value: withdrawnAmount }();\n\n // Transfer the Wrapped Sonic (wS) to the Vault\n _withdraw(vaultAddress, wrappedSonic, withdrawnAmount);\n\n // withdrawal.undelegatedAmount & withdrawnAmount can differ in case of slashing\n emit Withdrawn(\n _withdrawId,\n withdrawal.validatorId,\n withdrawal.undelegatedAmount,\n withdrawnAmount\n );\n }\n\n /// @notice returns a bool whether a withdrawalId has already been withdrawn or not\n /// @param _withdrawId The unique withdraw ID used to `undelegate`\n function isWithdrawnFromSFC(uint256 _withdrawId)\n public\n view\n returns (bool)\n {\n WithdrawRequest memory withdrawal = withdrawals[_withdrawId];\n require(withdrawal.validatorId > 0, \"Invalid withdrawId\");\n return withdrawal.undelegatedAmount == 0;\n }\n\n /**\n * @notice Restake any pending validator rewards for all supported validators\n * @param _validatorIds List of Sonic validator IDs to restake rewards\n */\n function restakeRewards(uint256[] calldata _validatorIds)\n external\n nonReentrant\n {\n for (uint256 i = 0; i < _validatorIds.length; ++i) {\n require(\n isSupportedValidator(_validatorIds[i]),\n \"Validator not supported\"\n );\n\n uint256 rewards = sfc.pendingRewards(\n address(this),\n _validatorIds[i]\n );\n\n if (rewards > 0) {\n sfc.restakeRewards(_validatorIds[i]);\n }\n }\n\n // The SFC contract will emit Delegated and RestakedRewards events.\n // The checkBalance function should not change as the pending rewards will moved to the staked amount.\n }\n\n /**\n * @notice Claim any pending rewards from validators\n * @param _validatorIds List of Sonic validator IDs to claim rewards\n */\n function collectRewards(uint256[] calldata _validatorIds)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n {\n uint256 sBalanceBefore = address(this).balance;\n\n for (uint256 i = 0; i < _validatorIds.length; ++i) {\n uint256 rewards = sfc.pendingRewards(\n address(this),\n _validatorIds[i]\n );\n\n if (rewards > 0) {\n // The SFC contract will emit ClaimedRewards(delegator (this), validatorId, rewards)\n sfc.claimRewards(_validatorIds[i]);\n }\n }\n\n uint256 rewardsAmount = address(this).balance - sBalanceBefore;\n\n // Wrap Sonic (S) to Wrapped Sonic (wS)\n IWrappedSonic(wrappedSonic).deposit{ value: rewardsAmount }();\n\n // Transfer the Wrapped Sonic (wS) to the Vault\n _withdraw(vaultAddress, wrappedSonic, rewardsAmount);\n }\n\n /**\n * @notice To receive native S from SFC and Wrapped Sonic (wS)\n *\n * @dev This does not prevent donating S tokens to the contract\n * as wrappedSonic has a `withdrawTo` function where a third party\n * owner of wrappedSonic can withdraw to this contract.\n */\n receive() external payable {\n require(\n msg.sender == address(sfc) || msg.sender == wrappedSonic,\n \"S not from allowed contracts\"\n );\n }\n\n /***************************************\n Admin functions\n ****************************************/\n\n /// @notice Set the address of the Registrator which can undelegate, withdraw and collect rewards\n /// @param _validatorRegistrator The address of the Registrator\n function setRegistrator(address _validatorRegistrator)\n external\n onlyGovernor\n {\n validatorRegistrator = _validatorRegistrator;\n emit RegistratorChanged(_validatorRegistrator);\n }\n\n /// @notice Set the default validatorId to delegate to on deposit\n /// @param _validatorId The validator identifier. eg 18\n function setDefaultValidatorId(uint256 _validatorId)\n external\n onlyRegistratorOrStrategist\n {\n require(isSupportedValidator(_validatorId), \"Validator not supported\");\n defaultValidatorId = _validatorId;\n emit DefaultValidatorIdChanged(_validatorId);\n }\n\n /// @notice Allows a validator to be delegated to by the Registrator\n /// @param _validatorId The validator identifier. eg 18\n function supportValidator(uint256 _validatorId) external onlyGovernor {\n require(\n !isSupportedValidator(_validatorId),\n \"Validator already supported\"\n );\n\n supportedValidators.push(_validatorId);\n\n emit SupportedValidator(_validatorId);\n }\n\n /// @notice Removes a validator from the supported list.\n /// Unsupported validators can still be undelegated from, withdrawn from and rewards collected.\n /// @param _validatorId The validator identifier. eg 18\n function unsupportValidator(uint256 _validatorId) external onlyGovernor {\n require(isSupportedValidator(_validatorId), \"Validator not supported\");\n\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; ++i) {\n if (supportedValidators[i] == _validatorId) {\n supportedValidators[i] = supportedValidators[validatorLen - 1];\n supportedValidators.pop();\n break;\n }\n }\n\n uint256 stake = sfc.getStake(address(this), _validatorId);\n\n // undelegate if validator still has funds staked\n if (stake > 0) {\n _undelegate(_validatorId, stake);\n }\n emit UnsupportedValidator(_validatorId);\n }\n\n /// @notice Returns the length of the supportedValidators array\n function supportedValidatorsLength() external view returns (uint256) {\n return supportedValidators.length;\n }\n\n /// @notice Returns whether a validator is supported by this strategy\n /// @param _validatorId The validator identifier\n function isSupportedValidator(uint256 _validatorId)\n public\n view\n returns (bool)\n {\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; ++i) {\n if (supportedValidators[i] == _validatorId) {\n return true;\n }\n }\n return false;\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal virtual;\n}\n" + }, + "contracts/strategies/VaultValueChecker.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IOUSD } from \"../interfaces/IOUSD.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract VaultValueChecker {\n IVault public immutable vault;\n IOUSD public immutable ousd;\n // Snapshot expiration time in seconds.\n // Used to prevent accidental use of an old snapshot, but\n // is not zero to allow easy testing of strategist actions in fork testing\n uint256 constant SNAPSHOT_EXPIRES = 5 * 60;\n\n struct Snapshot {\n uint256 vaultValue;\n uint256 totalSupply;\n uint256 time;\n }\n // By doing per user snapshots, we prevent a reentrancy attack\n // from a third party that updates the snapshot in the middle\n // of an allocation process\n\n mapping(address => Snapshot) public snapshots;\n\n constructor(address _vault, address _ousd) {\n vault = IVault(_vault);\n ousd = IOUSD(_ousd);\n }\n\n function takeSnapshot() external {\n snapshots[msg.sender] = Snapshot({\n vaultValue: vault.totalValue(),\n totalSupply: ousd.totalSupply(),\n time: block.timestamp\n });\n }\n\n function checkDelta(\n int256 expectedProfit,\n int256 profitVariance,\n int256 expectedVaultChange,\n int256 vaultChangeVariance\n ) external {\n // Intentionaly not view so that this method shows up in TX builders\n Snapshot memory snapshot = snapshots[msg.sender];\n int256 vaultChange = toInt256(vault.totalValue()) -\n toInt256(snapshot.vaultValue);\n int256 supplyChange = toInt256(ousd.totalSupply()) -\n toInt256(snapshot.totalSupply);\n int256 profit = vaultChange - supplyChange;\n\n require(\n snapshot.time >= block.timestamp - SNAPSHOT_EXPIRES,\n \"Snapshot too old\"\n );\n require(snapshot.time <= block.timestamp, \"Snapshot too new\");\n require(profit >= expectedProfit - profitVariance, \"Profit too low\");\n require(profit <= expectedProfit + profitVariance, \"Profit too high\");\n require(\n vaultChange >= expectedVaultChange - vaultChangeVariance,\n \"Vault value change too low\"\n );\n require(\n vaultChange <= expectedVaultChange + vaultChangeVariance,\n \"Vault value change too high\"\n );\n }\n\n function toInt256(uint256 value) internal pure returns (int256) {\n // From openzeppelin math/SafeCast.sol\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(\n value <= uint256(type(int256).max),\n \"SafeCast: value doesn't fit in an int256\"\n );\n return int256(value);\n }\n}\n\ncontract OETHVaultValueChecker is VaultValueChecker {\n constructor(address _vault, address _ousd)\n VaultValueChecker(_vault, _ousd)\n {}\n}\n" + }, + "contracts/swapper/Swapper1InchV5.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @notice 1Inch Pathfinder V5 implementation of the general ISwapper interface.\n * @author Origin Protocol Inc\n * @dev It is possible that dust token amounts are left in this contract after a swap.\n * This can happen with some tokens that don't send the full transfer amount.\n * These dust amounts can build up over time and be used by anyone who calls the `swap` function.\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IAggregationExecutor, IOneInchRouter, SwapDescription } from \"../interfaces/IOneInch.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\n\ncontract Swapper1InchV5 is ISwapper {\n using SafeERC20 for IERC20;\n\n /// @notice 1Inch router contract to give allowance to perform swaps\n address public constant SWAP_ROUTER =\n 0x1111111254EEB25477B68fb85Ed929f73A960582;\n\n // swap(address,(address,address,address,address,uint256,uint256,uint256),bytes,bytes)\n bytes4 internal constant SWAP_SELECTOR = 0x12aa3caf;\n // unoswapTo(address,address,uint256,uint256,uint256[])\n bytes4 internal constant UNISWAP_SELECTOR = 0xf78dc253;\n // uniswapV3SwapTo(address,uint256,uint256,uint256[])\n bytes4 internal constant UNISWAPV3_SELECTOR = 0xbc80f1a8;\n\n /**\n * @notice Strategist swaps assets sitting in the contract of the `assetHolder`.\n * @param _fromAsset The token address of the asset being sold by the vault.\n * @param _toAsset The token address of the asset being purchased by the vault.\n * @param _fromAssetAmount The amount of assets being sold by the vault.\n * @param _minToAssetAmount The minimum amount of assets to be purchased.\n * @param _data RLP encoded executer address and bytes data. This is re-encoded tx.data from 1Inch swap API\n */\n function swap(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n ) external override returns (uint256 toAssetAmount) {\n // Decode the function selector from the RLP encoded _data param\n bytes4 swapSelector = bytes4(_data[:4]);\n\n if (swapSelector == SWAP_SELECTOR) {\n // Decode the executer address and data from the RLP encoded _data param\n (, address executer, bytes memory executerData) = abi.decode(\n _data,\n (bytes4, address, bytes)\n );\n SwapDescription memory swapDesc = SwapDescription({\n srcToken: IERC20(_fromAsset),\n dstToken: IERC20(_toAsset),\n srcReceiver: payable(executer),\n dstReceiver: payable(msg.sender),\n amount: _fromAssetAmount,\n minReturnAmount: _minToAssetAmount,\n flags: 4 // 1st bit _PARTIAL_FILL, 2nd bit _REQUIRES_EXTRA_ETH, 3rd bit _SHOULD_CLAIM\n });\n (toAssetAmount, ) = IOneInchRouter(SWAP_ROUTER).swap(\n IAggregationExecutor(executer),\n swapDesc,\n hex\"\",\n executerData\n );\n } else if (swapSelector == UNISWAP_SELECTOR) {\n // Need to get the Uniswap pools data from the _data param\n (, uint256[] memory pools) = abi.decode(_data, (bytes4, uint256[]));\n toAssetAmount = IOneInchRouter(SWAP_ROUTER).unoswapTo(\n payable(msg.sender),\n IERC20(_fromAsset),\n _fromAssetAmount,\n _minToAssetAmount,\n pools\n );\n } else if (swapSelector == UNISWAPV3_SELECTOR) {\n // Need to get the Uniswap pools data from the _data param\n // slither-disable-next-line uninitialized-storage\n (, uint256[] memory pools) = abi.decode(_data, (bytes4, uint256[]));\n toAssetAmount = IOneInchRouter(SWAP_ROUTER).uniswapV3SwapTo(\n payable(msg.sender),\n _fromAssetAmount,\n _minToAssetAmount,\n pools\n );\n } else {\n revert(\"Unsupported swap function\");\n }\n }\n\n /**\n * @notice Approve assets for swapping.\n * @param _assets Array of token addresses to approve.\n * @dev unlimited approval is used as no tokens sit in this contract outside a transaction.\n */\n function approveAssets(address[] memory _assets) external {\n for (uint256 i = 0; i < _assets.length; ++i) {\n // Give the 1Inch router approval to transfer unlimited assets\n IERC20(_assets[i]).safeApprove(SWAP_ROUTER, type(uint256).max);\n }\n }\n}\n" + }, + "contracts/token/BridgedWOETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\n\ncontract BridgedWOETH is\n Governable,\n AccessControlEnumerable,\n Initializable,\n ERC20\n{\n bytes32 public constant MINTER_ROLE = keccak256(\"MINTER_ROLE\");\n bytes32 public constant BURNER_ROLE = keccak256(\"BURNER_ROLE\");\n\n constructor() ERC20(\"Wrapped OETH\", \"wOETH\") {\n // Nobody owns the implementation\n _setGovernor(address(0));\n }\n\n /**\n * @dev Initialize the proxy and set the Governor\n */\n function initialize() external initializer {\n // Governor can grant Minter/Burner roles\n _setupRole(DEFAULT_ADMIN_ROLE, _governor());\n }\n\n /**\n * @dev Mint tokens for `account`\n * @param account Address to mint tokens for\n * @param amount Amount of tokens to mint\n */\n function mint(address account, uint256 amount)\n external\n onlyRole(MINTER_ROLE)\n nonReentrant\n {\n _mint(account, amount);\n }\n\n /**\n * @dev Burns tokens from `account`\n * @param account Address to burn tokens from\n * @param amount Amount of tokens to burn\n */\n function burn(address account, uint256 amount)\n external\n onlyRole(BURNER_ROLE)\n nonReentrant\n {\n _burn(account, amount);\n }\n\n /**\n * @dev Burns tokens from `msg.sender`\n * @param amount Amount of tokens to burn\n */\n function burn(uint256 amount) external onlyRole(BURNER_ROLE) nonReentrant {\n _burn(msg.sender, amount);\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return \"Wrapped OETH\";\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return \"wOETH\";\n }\n\n /**\n * @dev Returns the decimals of the token\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETH is OUSD {\n function symbol() external pure override returns (string memory) {\n return \"OETH\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Origin Ether\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETHBase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBase is OUSD {\n constructor() {\n // Nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n function symbol() external pure override returns (string memory) {\n return \"superOETHb\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Super OETH\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETHPlume.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title Super OETH (Plume) Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETHPlume is OUSD {\n constructor() {\n // Nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n function symbol() external pure override returns (string memory) {\n return \"superOETHp\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Super OETH\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OSonic.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title Origin Sonic (OS) token on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonic is OUSD {\n function symbol() external pure override returns (string memory) {\n return \"OS\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Origin Sonic\";\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/token/OUSDResolutionUpgrade.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract OUSDResolutionUpgrade {\n enum RebaseOptions {\n NotSet,\n OptOut,\n OptIn\n }\n\n // From Initializable\n bool private initialized;\n bool private initializing;\n uint256[50] private ______igap;\n\n // From InitializableERC20Detailed\n uint256[100] private _____ugap;\n string private _name;\n string private _symbol;\n uint8 private _decimals;\n\n // From OUSD\n uint256 private constant MAX_SUPPLY = ~uint128(0); // (2^128) - 1\n uint256 public _totalSupply;\n mapping(address => mapping(address => uint256)) private _allowances;\n address public vaultAddress = address(0);\n mapping(address => uint256) private _creditBalances;\n uint256 private _rebasingCredits;\n uint256 private _rebasingCreditsPerToken;\n uint256 public nonRebasingSupply;\n mapping(address => uint256) public nonRebasingCreditsPerToken;\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) public isUpgraded;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() public view returns (uint256) {\n return _rebasingCreditsPerToken / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCredits() public view returns (uint256) {\n return _rebasingCredits / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() public view returns (uint256) {\n return _rebasingCreditsPerToken;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() public view returns (uint256) {\n return _rebasingCredits;\n }\n\n function upgradeGlobals() external {\n require(isUpgraded[address(0)] == 0, \"Globals already upgraded\");\n require(_rebasingCredits > 0, \"Sanity _rebasingCredits\");\n require(\n _rebasingCreditsPerToken > 0,\n \"Sanity _rebasingCreditsPerToken\"\n );\n isUpgraded[address(0)] = 1;\n _rebasingCredits = _rebasingCredits * RESOLUTION_INCREASE;\n _rebasingCreditsPerToken =\n _rebasingCreditsPerToken *\n RESOLUTION_INCREASE;\n }\n\n function upgradeAccounts(address[] calldata accounts) external {\n for (uint256 i = 0; i < accounts.length; i++) {\n address account = accounts[i];\n require(account != address(0), \"Reserved\");\n require(isUpgraded[account] == 0, \"Account already upgraded\");\n isUpgraded[account] = 1;\n\n // Handle special for non-rebasing accounts\n uint256 nrc = nonRebasingCreditsPerToken[account];\n if (nrc > 0) {\n require(nrc < 1e18, \"Account already highres\");\n nonRebasingCreditsPerToken[account] = nrc * RESOLUTION_INCREASE;\n }\n // Upgrade balance\n uint256 balance = _creditBalances[account];\n require(balance > 0, \"Will not upgrade zero balance\");\n _creditBalances[account] = balance * RESOLUTION_INCREASE;\n }\n }\n\n function creditsBalanceOfHighres(address _account)\n public\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n _creditBalances[_account],\n _creditsPerToken(_account),\n isUpgraded[_account] == 1\n );\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n if (nonRebasingCreditsPerToken[_account] != 0) {\n return nonRebasingCreditsPerToken[_account];\n } else {\n return _rebasingCreditsPerToken;\n }\n }\n}\n" + }, + "contracts/token/WOETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC4626 } from \"../../lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { OETH } from \"./OETH.sol\";\n\n/**\n * @title Wrapped OETH Token Contract\n * @author Origin Protocol Inc\n *\n * @dev An important capability of this contract is that it isn't susceptible to changes of the\n * exchange rate of WOETH/OETH if/when someone sends the underlying asset (OETH) to the contract.\n * If OETH weren't rebasing this could be achieved by solely tracking the ERC20 transfers of the OETH\n * token on mint, deposit, redeem, withdraw. The issue is that OETH is rebasing and OETH balances\n * will change when the token rebases.\n * For that reason the contract logic checks the actual underlying OETH token balance only once\n * (either on a fresh contract creation or upgrade) and considering the WOETH supply and\n * rebasingCreditsPerToken calculates the _adjuster. Once the adjuster is calculated any donations\n * to the contract are ignored. The totalSupply (instead of querying OETH balance) works off of\n * adjuster the current WOETH supply and rebasingCreditsPerToken. This makes WOETH value accrual\n * completely follow OETH's value accrual.\n * WOETH is safe to use in lending markets as the VualtCore's _rebase contains safeguards preventing\n * any sudden large rebases.\n */\n\ncontract WOETH is ERC4626, Governable, Initializable {\n using SafeERC20 for IERC20;\n /* This is a 1e27 adjustment constant that expresses the difference in exchange rate between\n * OETH's rebase since inception (expressed with rebasingCreditsPerToken) and WOETH to OETH\n * conversion.\n *\n * If WOETH and OETH are deployed at the same time, the value of adjuster is a neutral 1e27\n */\n uint256 public adjuster;\n uint256[49] private __gap;\n\n // no need to set ERC20 name and symbol since they are overridden in WOETH & WOETHBase\n constructor(ERC20 underlying_) ERC20(\"\", \"\") ERC4626(underlying_) {}\n\n /**\n * @notice Enable OETH rebasing for this contract\n */\n function initialize() external onlyGovernor initializer {\n OETH(address(asset())).rebaseOptIn();\n\n initialize2();\n }\n\n /**\n * @notice secondary initializer that newly deployed contracts will execute as part\n * of primary initialize function and the existing contracts will have it called\n * as a governance operation.\n */\n function initialize2() public onlyGovernor {\n require(adjuster == 0, \"Initialize2 already called\");\n\n if (totalSupply() == 0) {\n adjuster = 1e27;\n } else {\n adjuster =\n (rebasingCreditsPerTokenHighres() *\n ERC20(asset()).balanceOf(address(this))) /\n totalSupply();\n }\n }\n\n function name()\n public\n view\n virtual\n override(ERC20, IERC20Metadata)\n returns (string memory)\n {\n return \"Wrapped OETH\";\n }\n\n function symbol()\n public\n view\n virtual\n override(ERC20, IERC20Metadata)\n returns (string memory)\n {\n return \"wOETH\";\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends. Cannot transfer OETH\n * @param asset_ Address for the asset\n * @param amount_ Amount of the asset to transfer\n */\n function transferToken(address asset_, uint256 amount_)\n external\n onlyGovernor\n {\n require(asset_ != address(asset()), \"Cannot collect core asset\");\n IERC20(asset_).safeTransfer(governor(), amount_);\n }\n\n /// @inheritdoc ERC4626\n function convertToShares(uint256 assets)\n public\n view\n virtual\n override\n returns (uint256 shares)\n {\n return (assets * rebasingCreditsPerTokenHighres()) / adjuster;\n }\n\n /// @inheritdoc ERC4626\n function convertToAssets(uint256 shares)\n public\n view\n virtual\n override\n returns (uint256 assets)\n {\n return (shares * adjuster) / rebasingCreditsPerTokenHighres();\n }\n\n /// @inheritdoc ERC4626\n function totalAssets() public view override returns (uint256) {\n return (totalSupply() * adjuster) / rebasingCreditsPerTokenHighres();\n }\n\n function rebasingCreditsPerTokenHighres() internal view returns (uint256) {\n return OETH(asset()).rebasingCreditsPerTokenHighres();\n }\n}\n" + }, + "contracts/token/WOETHBase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WOETH } from \"./WOETH.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\n\ncontract WOETHBase is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name() public view virtual override returns (string memory) {\n return \"Wrapped Super OETH\";\n }\n\n function symbol() public view virtual override returns (string memory) {\n return \"wsuperOETHb\";\n }\n}\n" + }, + "contracts/token/WOETHPlume.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WOETH } from \"./WOETH.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @title wOETH (Plume) Token Contract\n * @author Origin Protocol Inc\n */\n\ncontract WOETHPlume is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name() public view virtual override returns (string memory) {\n return \"Wrapped Super OETH\";\n }\n\n function symbol() public view virtual override returns (string memory) {\n return \"wsuperOETHp\";\n }\n}\n" + }, + "contracts/token/WOSonic.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { WOETH } from \"./WOETH.sol\";\n\n/**\n * @title Wrapped Origin Sonic (wOS) token on Sonic\n * @author Origin Protocol Inc\n */\ncontract WOSonic is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"Wrapped OS\";\n }\n\n function symbol()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"wOS\";\n }\n}\n" + }, + "contracts/token/WrappedOusd.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { WOETH } from \"./WOETH.sol\";\n\n/**\n * @title Wrapped OUSD Token Contract\n * @author Origin Protocol Inc\n */\ncontract WrappedOusd is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"Wrapped OUSD\";\n }\n\n function symbol()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"WOUSD\";\n }\n}\n" + }, + "contracts/utils/AerodromeAMOQuoter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.7;\n\nimport { ICLPool } from \"../interfaces/aerodrome/ICLPool.sol\";\nimport { IQuoterV2 } from \"../interfaces/aerodrome/IQuoterV2.sol\";\nimport { IAMOStrategy } from \"../interfaces/aerodrome/IAMOStrategy.sol\";\n\n/// @title QuoterHelper\n/// @author Origin Protocol\n/// @notice Helper for Aerodrome AMO Quoter, as `_quoteAmountToSwapBeforeRebalance` use try/catch method and\n/// this can only be used when calling external contracts.\ncontract QuoterHelper {\n ////////////////////////////////////////////////////////////////\n /// --- STRUCTS & ENUMS\n ////////////////////////////////////////////////////////////////\n enum RevertReasons {\n DefaultStatus,\n RebalanceOutOfBounds,\n NotInExpectedTickRange,\n NotEnoughWethForSwap,\n NotEnoughWethLiquidity,\n UnexpectedError,\n Found,\n NotFound\n }\n\n struct RebalanceStatus {\n RevertReasons reason;\n uint256 currentPoolWETHShare; // Case 1\n uint256 allowedWETHShareStart; // Case 1\n uint256 allowedWETHShareEnd; // Case 1\n int24 currentTick; // Case 2\n uint256 balanceWETH; // Case 3\n uint256 amountWETH; // Case 3\n string revertMessage;\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTANT & IMMUTABLE\n ////////////////////////////////////////////////////////////////\n uint256 public constant BINARY_MIN_AMOUNT = 1 wei;\n\n uint256 public constant BINARY_MAX_ITERATIONS = 40;\n uint256 public constant PERCENTAGE_BASE = 1e18; // 100%\n uint256 public constant ALLOWED_VARIANCE_PERCENTAGE = 1e15; // 0.1%\n\n ////////////////////////////////////////////////////////////////\n /// --- VARIABLES STORAGE\n ////////////////////////////////////////////////////////////////\n ICLPool public immutable clPool;\n IQuoterV2 public immutable quoterV2;\n IAMOStrategy public immutable strategy;\n\n address public originalGovernor;\n\n ////////////////////////////////////////////////////////////////\n /// --- ERRORS & EVENTS\n ////////////////////////////////////////////////////////////////\n error UnexpectedError(string message);\n error OutOfIterations(uint256 iterations);\n error ValidAmount(\n uint256 amount,\n uint256 iterations,\n bool swapWETHForOETHB\n );\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////////////////\n constructor(IAMOStrategy _strategy, IQuoterV2 _quoterV2) {\n strategy = _strategy;\n quoterV2 = _quoterV2;\n clPool = _strategy.clPool();\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- FUNCTIONS\n ////////////////////////////////////////////////////////////////\n /// @notice This call can only end with a revert.\n function getAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) public {\n if (\n overrideBottomWethShare != type(uint256).max ||\n overrideTopWethShare != type(uint256).max\n ) {\n // Current values\n uint256 shareStart = strategy.allowedWethShareStart();\n uint256 shareEnd = strategy.allowedWethShareEnd();\n\n // Override values\n if (overrideBottomWethShare != type(uint256).max) {\n shareStart = overrideBottomWethShare;\n }\n if (overrideTopWethShare != type(uint256).max) {\n shareEnd = overrideTopWethShare;\n }\n\n strategy.setAllowedPoolWethShareInterval(shareStart, shareEnd);\n }\n\n uint256 iterations = 0;\n uint256 low = BINARY_MIN_AMOUNT;\n uint256 high;\n (high, ) = strategy.getPositionPrincipal();\n int24 lowerTick = strategy.lowerTick();\n int24 upperTick = strategy.upperTick();\n bool swapWETHForOETHB = getSwapDirectionForRebalance();\n\n while (low <= high && iterations < BINARY_MAX_ITERATIONS) {\n uint256 mid = (low + high) / 2;\n\n RebalanceStatus memory status = getRebalanceStatus(\n mid,\n swapWETHForOETHB\n );\n\n // Best case, we found the `amount` that will reach the target pool share!\n // We can revert with the amount and the number of iterations\n if (status.reason == RevertReasons.Found) {\n revert ValidAmount(mid, iterations, swapWETHForOETHB);\n }\n\n // If the rebalance failed then we should try to change the amount.\n // We will handle all possible revert reasons here.\n\n // Case 1: Rebalance out of bounds\n // If the pool is out of bounds, we need to adjust the amount to reach the target pool share\n if (status.reason == RevertReasons.RebalanceOutOfBounds) {\n // If the current pool share is less than the target pool share, we need to increase the amount\n if (\n swapWETHForOETHB\n ? status.currentPoolWETHShare <\n status.allowedWETHShareStart\n : status.currentPoolWETHShare >\n status.allowedWETHShareEnd\n ) {\n low = mid + 1;\n }\n // Else we need to decrease the amount\n else {\n high = mid;\n }\n }\n\n // Case 2: Not in expected tick range\n // If the pool is not in the expected tick range, we need to adjust the amount\n // to reach the target pool share\n if (status.reason == RevertReasons.NotInExpectedTickRange) {\n // If we are buying OETHb and the current tick is greater than the lower tick,\n //we need to increase the amount in order to continue to push price down.\n // If we are selling OETHb and the current tick is less than the upper tick,\n // we need to increase the amount in order to continue to push price up.\n if (\n swapWETHForOETHB\n ? status.currentTick > lowerTick\n : status.currentTick < upperTick\n ) {\n low = mid + 1;\n }\n // Else we need to decrease the amount\n else {\n high = mid;\n }\n }\n\n // Case 3: Not enough WETH for swap\n // If we don't have enough WETH to swap, we need to decrease the amount\n // This error can happen, when initial value of mid is too high, so we need to decrease it\n if (status.reason == RevertReasons.NotEnoughWethForSwap) {\n high = mid;\n }\n\n // Case 4: Not enough WETH liquidity\n // If we don't have enough WETH liquidity\n // Revert for the moment, we need to improve this\n if (status.reason == RevertReasons.NotEnoughWethLiquidity) {\n revert(\"Quoter: Not enough WETH liquidity\");\n }\n\n // Case 5: Unexpected error\n // Worst case, it reverted with an unexpected error.\n if (status.reason == RevertReasons.UnexpectedError) {\n revert UnexpectedError(status.revertMessage);\n }\n\n iterations++;\n }\n\n // Case 6: Out of iterations\n // If we didn't find the amount after the max iterations, we need to revert.\n revert OutOfIterations(iterations);\n }\n\n /// @notice Get the status of the rebalance\n /// @param amount The amount of token to swap\n /// @param swapWETH True if we need to swap WETH for OETHb, false otherwise\n /// @return status The status of the rebalance\n function getRebalanceStatus(uint256 amount, bool swapWETH)\n public\n returns (RebalanceStatus memory status)\n {\n try strategy.rebalance(amount, swapWETH, 0) {\n status.reason = RevertReasons.Found;\n return status;\n } catch Error(string memory reason) {\n status.reason = RevertReasons.UnexpectedError;\n status.revertMessage = reason;\n } catch (bytes memory reason) {\n bytes4 receivedSelector = bytes4(reason);\n\n // Case 1: Rebalance out of bounds\n if (\n receivedSelector ==\n IAMOStrategy.PoolRebalanceOutOfBounds.selector\n ) {\n uint256 currentPoolWETHShare;\n uint256 allowedWETHShareStart;\n uint256 allowedWETHShareEnd;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n currentPoolWETHShare := mload(add(reason, 0x24))\n allowedWETHShareStart := mload(add(reason, 0x44))\n allowedWETHShareEnd := mload(add(reason, 0x64))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.RebalanceOutOfBounds,\n currentPoolWETHShare: currentPoolWETHShare,\n allowedWETHShareStart: allowedWETHShareStart,\n allowedWETHShareEnd: allowedWETHShareEnd,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 2: Not in expected tick range\n if (\n receivedSelector ==\n IAMOStrategy.OutsideExpectedTickRange.selector\n ) {\n int24 currentTick;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n currentTick := mload(add(reason, 0x24))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.NotInExpectedTickRange,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: currentTick,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 3: Not enough WETH for swap\n if (\n receivedSelector == IAMOStrategy.NotEnoughWethForSwap.selector\n ) {\n uint256 balanceWETH;\n uint256 amountWETH;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n balanceWETH := mload(add(reason, 0x24))\n amountWETH := mload(add(reason, 0x44))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.NotEnoughWethForSwap,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: balanceWETH,\n amountWETH: amountWETH,\n revertMessage: \"\"\n });\n }\n\n // Case 4: Not enough WETH liquidity\n if (\n receivedSelector == IAMOStrategy.NotEnoughWethLiquidity.selector\n ) {\n return\n RebalanceStatus({\n reason: RevertReasons.NotEnoughWethLiquidity,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 5: Unexpected error\n return\n RebalanceStatus({\n reason: RevertReasons.UnexpectedError,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: abi.decode(reason, (string))\n });\n }\n }\n\n /// @notice Get the swap direction to reach the target price before rebalance.\n /// @return bool True if we need to swap WETH for OETHb, false otherwise.\n function getSwapDirectionForRebalance() public view returns (bool) {\n uint160 currentPrice = strategy.getPoolX96Price();\n uint160 ticker0Price = strategy.sqrtRatioX96TickLower();\n uint160 ticker1Price = strategy.sqrtRatioX96TickHigher();\n uint256 allowedWethShareStart = strategy.allowedWethShareStart();\n uint256 allowedWethShareEnd = strategy.allowedWethShareEnd();\n uint160 mid = uint160(allowedWethShareStart + allowedWethShareEnd) / 2;\n // slither-disable-start divide-before-multiply\n uint160 targetPrice = (ticker0Price *\n mid +\n ticker1Price *\n (1 ether - mid)) / 1 ether;\n // slither-disable-end divide-before-multiply\n\n return currentPrice > targetPrice;\n }\n\n // returns total amount in the position principal of the Aerodrome AMO strategy. Needed as a\n // separate function because of the limitation in local variable count in getAmountToSwapToReachPrice\n function getTotalStrategyPosition() internal returns (uint256) {\n (uint256 wethAmount, uint256 oethBalance) = strategy\n .getPositionPrincipal();\n return wethAmount + oethBalance;\n }\n\n /// @notice Get the amount of tokens to swap to reach the target price.\n /// @dev This act like a quoter, i.e. the transaction is not performed.\n /// @dev Because the amount to swap can be largely overestimated, because CLAMM alow partial orders,\n /// i.e. when we ask to swap a very large amount, with a close priceLimite, it will swap only a part of it,\n /// and not revert. So if overestimated amount is to high, use a custom maxAmount to avoid this issue.\n /// @param sqrtPriceTargetX96 The target price to reach.\n /// @return amount The amount of tokens to swap.\n /// @return iterations The number of iterations to find the amount.\n /// @return swapWETHForOETHB True if we need to swap WETH for OETHb, false otherwise.\n /// @return sqrtPriceX96After The price after the swap.\n function getAmountToSwapToReachPrice(uint160 sqrtPriceTargetX96)\n public\n returns (\n uint256,\n uint256,\n bool,\n uint160\n )\n {\n uint256 iterations = 0;\n uint256 low = BINARY_MIN_AMOUNT;\n // high search start is twice the position principle of Aerodrome AMO strategy.\n // should be more than enough\n uint256 high = getTotalStrategyPosition() * 2;\n bool swapWETHForOETHB = getSwapDirection(sqrtPriceTargetX96);\n\n while (low <= high && iterations < BINARY_MAX_ITERATIONS) {\n uint256 mid = (low + high) / 2;\n\n // Call QuoterV2 from SugarHelper\n (uint256 amountOut, uint160 sqrtPriceX96After, , ) = quoterV2\n .quoteExactInputSingle(\n IQuoterV2.QuoteExactInputSingleParams({\n tokenIn: swapWETHForOETHB\n ? clPool.token0()\n : clPool.token1(),\n tokenOut: swapWETHForOETHB\n ? clPool.token1()\n : clPool.token0(),\n amountIn: mid,\n tickSpacing: strategy.tickSpacing(),\n sqrtPriceLimitX96: sqrtPriceTargetX96\n })\n );\n\n if (\n isWithinAllowedVariance(sqrtPriceX96After, sqrtPriceTargetX96)\n ) {\n /** Very important to return `amountOut` instead of `mid` as the first return parameter.\n * The issues was that when quoting we impose a swap price limit (sqrtPriceLimitX96: sqrtPriceTargetX96)\n * and in that case the `amountIn` acts like a maximum amount to swap. And we don't know how much\n * of that amount was actually consumed. For that reason we \"estimate\" it by returning the\n * amountOut since that is only going to be a couple of basis point away from amountIn in the\n * worst cases.\n *\n * Note: we could be returning mid instead of amountOut in cases when those values are only basis\n * points apart (assuming that complete balance of amountIn has been consumed) but that might increase\n * complexity too much in an already complex contract.\n */\n return (\n amountOut,\n iterations,\n swapWETHForOETHB,\n sqrtPriceX96After\n );\n } else if (low == high) {\n // target swap amount not found.\n // might be that \"high\" amount is too low on start\n revert(\"SwapAmountNotFound\");\n } else if (\n swapWETHForOETHB\n ? sqrtPriceX96After > sqrtPriceTargetX96\n : sqrtPriceX96After < sqrtPriceTargetX96\n ) {\n low = mid + 1;\n } else {\n high = mid;\n }\n iterations++;\n }\n\n revert OutOfIterations(iterations);\n }\n\n /// @notice Check if the current price is within the allowed variance in comparison to the target price\n /// @return bool True if the current price is within the allowed variance, false otherwise\n function isWithinAllowedVariance(\n uint160 sqrtPriceCurrentX96,\n uint160 sqrtPriceTargetX96\n ) public view returns (bool) {\n uint160 range = strategy.sqrtRatioX96TickHigher() -\n strategy.sqrtRatioX96TickLower();\n if (sqrtPriceCurrentX96 > sqrtPriceTargetX96) {\n return\n (sqrtPriceCurrentX96 - sqrtPriceTargetX96) <=\n (ALLOWED_VARIANCE_PERCENTAGE * range) / PERCENTAGE_BASE;\n } else {\n return\n (sqrtPriceTargetX96 - sqrtPriceCurrentX96) <=\n (ALLOWED_VARIANCE_PERCENTAGE * range) / PERCENTAGE_BASE;\n }\n }\n\n /// @notice Get the swap direction to reach the target price.\n /// @param sqrtPriceTargetX96 The target price to reach.\n /// @return bool True if we need to swap WETH for OETHb, false otherwise.\n function getSwapDirection(uint160 sqrtPriceTargetX96)\n public\n view\n returns (bool)\n {\n uint160 currentPrice = strategy.getPoolX96Price();\n return currentPrice > sqrtPriceTargetX96;\n }\n\n function claimGovernanceOnAMO() public {\n originalGovernor = strategy.governor();\n strategy.claimGovernance();\n }\n\n function giveBackGovernanceOnAMO() public {\n require(\n originalGovernor != address(0),\n \"Quoter: Original governor not set\"\n );\n strategy.transferGovernance(originalGovernor);\n }\n}\n\n/// @title AerodromeAMOQuoter\n/// @author Origin Protocol\n/// @notice Quoter for Aerodrome AMO\ncontract AerodromeAMOQuoter {\n ////////////////////////////////////////////////////////////////\n /// --- STRUCTS & ENUMS\n ///////////////////////////////////////////////////////////////\n struct Data {\n uint256 amount;\n uint256 iterations;\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- VARIABLES STORAGE\n ////////////////////////////////////////////////////////////////\n QuoterHelper public immutable quoterHelper;\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////////////////\n constructor(address _strategy, address _quoterV2) {\n quoterHelper = new QuoterHelper(\n IAMOStrategy(_strategy),\n IQuoterV2(_quoterV2)\n );\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- ERRORS & EVENTS\n ////////////////////////////////////////////////////////////////\n event ValueFound(uint256 value, uint256 iterations, bool swapWETHForOETHB);\n event ValueFoundBis(\n uint256 value,\n uint256 iterations,\n bool swapWETHForOETHB,\n uint160 sqrtPriceAfterX96\n );\n event ValueNotFound(string message);\n\n ////////////////////////////////////////////////////////////////\n /// --- FUNCTIONS\n ////////////////////////////////////////////////////////////////\n /// @notice Use this to get the amount to swap before rebalance\n /// @dev This call will only revert, check the logs to get returned values.\n /// @dev Need to perform this call while impersonating the governor or strategist of AMO.\n /// @return data Data struct with the amount and the number of iterations\n function quoteAmountToSwapBeforeRebalance()\n public\n returns (Data memory data)\n {\n return\n _quoteAmountToSwapBeforeRebalance(\n type(uint256).max,\n type(uint256).max\n );\n }\n\n /// @notice Use this to get the amount to swap before rebalance and\n /// update allowedWethShareStart and allowedWethShareEnd on AMO.\n /// @dev This call will only revert, check the logs to get returned values.\n /// @dev Need to perform this call while impersonating the governor of AMO.\n /// @param overrideBottomWethShare New value for the allowedWethShareStart on AMO.\n /// Use type(uint256).max to keep same value.\n /// @param overrideTopWethShare New value for the allowedWethShareEnd on AMO.\n /// Use type(uint256).max to keep same value.\n /// @return data Data struct with the amount and the number of iterations\n function quoteAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) public returns (Data memory data) {\n return\n _quoteAmountToSwapBeforeRebalance(\n overrideBottomWethShare,\n overrideTopWethShare\n );\n }\n\n /// @notice Internal logic for quoteAmountToSwapBeforeRebalance.\n function _quoteAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) internal returns (Data memory data) {\n try\n quoterHelper.getAmountToSwapBeforeRebalance(\n overrideBottomWethShare,\n overrideTopWethShare\n )\n {\n revert(\"Previous call should only revert, it cannot succeed\");\n } catch (bytes memory reason) {\n bytes4 receivedSelector = bytes4(reason);\n if (receivedSelector == QuoterHelper.ValidAmount.selector) {\n uint256 value;\n uint256 iterations;\n bool swapWETHForOETHB;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n value := mload(add(reason, 0x24))\n iterations := mload(add(reason, 0x44))\n swapWETHForOETHB := mload(add(reason, 0x64))\n }\n emit ValueFound(value, iterations, swapWETHForOETHB);\n return Data({ amount: value, iterations: iterations });\n }\n\n if (receivedSelector == QuoterHelper.OutOfIterations.selector) {\n emit ValueNotFound(\"Out of iterations\");\n revert(\"Out of iterations\");\n }\n\n emit ValueNotFound(\"Unexpected error\");\n revert(abi.decode(reason, (string)));\n }\n }\n\n /// @notice Use this to get the amount to swap to reach the target price after swap.\n /// @dev This call will only revert, check the logs to get returned values.\n /// @param sqrtPriceTargetX96 The target price to reach.\n function quoteAmountToSwapToReachPrice(uint160 sqrtPriceTargetX96) public {\n (\n uint256 amount,\n uint256 iterations,\n bool swapWETHForOETHB,\n uint160 sqrtPriceAfterX96\n ) = quoterHelper.getAmountToSwapToReachPrice(sqrtPriceTargetX96);\n\n emit ValueFoundBis(\n amount,\n iterations,\n swapWETHForOETHB,\n sqrtPriceAfterX96\n );\n }\n\n function claimGovernance() public {\n quoterHelper.claimGovernanceOnAMO();\n }\n\n function giveBackGovernance() public {\n quoterHelper.giveBackGovernanceOnAMO();\n }\n}\n" + }, + "contracts/utils/BalancerErrors.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity >=0.7.4 <0.9.0;\n\n// solhint-disable\n\n/**\n * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are\n * supported.\n * Uses the default 'BAL' prefix for the error code\n */\nfunction _require(bool condition, uint256 errorCode) pure {\n if (!condition) _revert(errorCode);\n}\n\n/**\n * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are\n * supported.\n */\nfunction _require(\n bool condition,\n uint256 errorCode,\n bytes3 prefix\n) pure {\n if (!condition) _revert(errorCode, prefix);\n}\n\n/**\n * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported.\n * Uses the default 'BAL' prefix for the error code\n */\nfunction _revert(uint256 errorCode) pure {\n _revert(errorCode, 0x42414c); // This is the raw byte representation of \"BAL\"\n}\n\n/**\n * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported.\n */\nfunction _revert(uint256 errorCode, bytes3 prefix) pure {\n uint256 prefixUint = uint256(uint24(prefix));\n // We're going to dynamically create a revert string based on the error code, with the following format:\n // 'BAL#{errorCode}'\n // where the code is left-padded with zeroes to three digits (so they range from 000 to 999).\n //\n // We don't have revert strings embedded in the contract to save bytecode size: it takes much less space to store a\n // number (8 to 16 bits) than the individual string characters.\n //\n // The dynamic string creation algorithm that follows could be implemented in Solidity, but assembly allows for a\n // much denser implementation, again saving bytecode size. Given this function unconditionally reverts, this is a\n // safe place to rely on it without worrying about how its usage might affect e.g. memory contents.\n assembly {\n // First, we need to compute the ASCII representation of the error code. We assume that it is in the 0-999\n // range, so we only need to convert three digits. To convert the digits to ASCII, we add 0x30, the value for\n // the '0' character.\n\n let units := add(mod(errorCode, 10), 0x30)\n\n errorCode := div(errorCode, 10)\n let tenths := add(mod(errorCode, 10), 0x30)\n\n errorCode := div(errorCode, 10)\n let hundreds := add(mod(errorCode, 10), 0x30)\n\n // With the individual characters, we can now construct the full string.\n // We first append the '#' character (0x23) to the prefix. In the case of 'BAL', it results in 0x42414c23 ('BAL#')\n // Then, we shift this by 24 (to provide space for the 3 bytes of the error code), and add the\n // characters to it, each shifted by a multiple of 8.\n // The revert reason is then shifted left by 200 bits (256 minus the length of the string, 7 characters * 8 bits\n // per character = 56) to locate it in the most significant part of the 256 slot (the beginning of a byte\n // array).\n let formattedPrefix := shl(24, add(0x23, shl(8, prefixUint)))\n\n let revertReason := shl(\n 200,\n add(\n formattedPrefix,\n add(add(units, shl(8, tenths)), shl(16, hundreds))\n )\n )\n\n // We can now encode the reason in memory, which can be safely overwritten as we're about to revert. The encoded\n // message will have the following layout:\n // [ revert reason identifier ] [ string location offset ] [ string length ] [ string contents ]\n\n // The Solidity revert reason identifier is 0x08c739a0, the function selector of the Error(string) function. We\n // also write zeroes to the next 28 bytes of memory, but those are about to be overwritten.\n mstore(\n 0x0,\n 0x08c379a000000000000000000000000000000000000000000000000000000000\n )\n // Next is the offset to the location of the string, which will be placed immediately after (20 bytes away).\n mstore(\n 0x04,\n 0x0000000000000000000000000000000000000000000000000000000000000020\n )\n // The string length is fixed: 7 characters.\n mstore(0x24, 7)\n // Finally, the string itself is stored.\n mstore(0x44, revertReason)\n\n // Even if the string is only 7 bytes long, we need to return a full 32 byte slot containing it. The length of\n // the encoded message is therefore 4 + 32 + 32 + 32 = 100.\n revert(0, 100)\n }\n}\n\nlibrary Errors {\n // Math\n uint256 internal constant ADD_OVERFLOW = 0;\n uint256 internal constant SUB_OVERFLOW = 1;\n uint256 internal constant SUB_UNDERFLOW = 2;\n uint256 internal constant MUL_OVERFLOW = 3;\n uint256 internal constant ZERO_DIVISION = 4;\n uint256 internal constant DIV_INTERNAL = 5;\n uint256 internal constant X_OUT_OF_BOUNDS = 6;\n uint256 internal constant Y_OUT_OF_BOUNDS = 7;\n uint256 internal constant PRODUCT_OUT_OF_BOUNDS = 8;\n uint256 internal constant INVALID_EXPONENT = 9;\n\n // Input\n uint256 internal constant OUT_OF_BOUNDS = 100;\n uint256 internal constant UNSORTED_ARRAY = 101;\n uint256 internal constant UNSORTED_TOKENS = 102;\n uint256 internal constant INPUT_LENGTH_MISMATCH = 103;\n uint256 internal constant ZERO_TOKEN = 104;\n uint256 internal constant INSUFFICIENT_DATA = 105;\n\n // Shared pools\n uint256 internal constant MIN_TOKENS = 200;\n uint256 internal constant MAX_TOKENS = 201;\n uint256 internal constant MAX_SWAP_FEE_PERCENTAGE = 202;\n uint256 internal constant MIN_SWAP_FEE_PERCENTAGE = 203;\n uint256 internal constant MINIMUM_BPT = 204;\n uint256 internal constant CALLER_NOT_VAULT = 205;\n uint256 internal constant UNINITIALIZED = 206;\n uint256 internal constant BPT_IN_MAX_AMOUNT = 207;\n uint256 internal constant BPT_OUT_MIN_AMOUNT = 208;\n uint256 internal constant EXPIRED_PERMIT = 209;\n uint256 internal constant NOT_TWO_TOKENS = 210;\n uint256 internal constant DISABLED = 211;\n\n // Pools\n uint256 internal constant MIN_AMP = 300;\n uint256 internal constant MAX_AMP = 301;\n uint256 internal constant MIN_WEIGHT = 302;\n uint256 internal constant MAX_STABLE_TOKENS = 303;\n uint256 internal constant MAX_IN_RATIO = 304;\n uint256 internal constant MAX_OUT_RATIO = 305;\n uint256 internal constant MIN_BPT_IN_FOR_TOKEN_OUT = 306;\n uint256 internal constant MAX_OUT_BPT_FOR_TOKEN_IN = 307;\n uint256 internal constant NORMALIZED_WEIGHT_INVARIANT = 308;\n uint256 internal constant INVALID_TOKEN = 309;\n uint256 internal constant UNHANDLED_JOIN_KIND = 310;\n uint256 internal constant ZERO_INVARIANT = 311;\n uint256 internal constant ORACLE_INVALID_SECONDS_QUERY = 312;\n uint256 internal constant ORACLE_NOT_INITIALIZED = 313;\n uint256 internal constant ORACLE_QUERY_TOO_OLD = 314;\n uint256 internal constant ORACLE_INVALID_INDEX = 315;\n uint256 internal constant ORACLE_BAD_SECS = 316;\n uint256 internal constant AMP_END_TIME_TOO_CLOSE = 317;\n uint256 internal constant AMP_ONGOING_UPDATE = 318;\n uint256 internal constant AMP_RATE_TOO_HIGH = 319;\n uint256 internal constant AMP_NO_ONGOING_UPDATE = 320;\n uint256 internal constant STABLE_INVARIANT_DIDNT_CONVERGE = 321;\n uint256 internal constant STABLE_GET_BALANCE_DIDNT_CONVERGE = 322;\n uint256 internal constant RELAYER_NOT_CONTRACT = 323;\n uint256 internal constant BASE_POOL_RELAYER_NOT_CALLED = 324;\n uint256 internal constant REBALANCING_RELAYER_REENTERED = 325;\n uint256 internal constant GRADUAL_UPDATE_TIME_TRAVEL = 326;\n uint256 internal constant SWAPS_DISABLED = 327;\n uint256 internal constant CALLER_IS_NOT_LBP_OWNER = 328;\n uint256 internal constant PRICE_RATE_OVERFLOW = 329;\n uint256 internal constant INVALID_JOIN_EXIT_KIND_WHILE_SWAPS_DISABLED = 330;\n uint256 internal constant WEIGHT_CHANGE_TOO_FAST = 331;\n uint256 internal constant LOWER_GREATER_THAN_UPPER_TARGET = 332;\n uint256 internal constant UPPER_TARGET_TOO_HIGH = 333;\n uint256 internal constant UNHANDLED_BY_LINEAR_POOL = 334;\n uint256 internal constant OUT_OF_TARGET_RANGE = 335;\n uint256 internal constant UNHANDLED_EXIT_KIND = 336;\n uint256 internal constant UNAUTHORIZED_EXIT = 337;\n uint256 internal constant MAX_MANAGEMENT_SWAP_FEE_PERCENTAGE = 338;\n uint256 internal constant UNHANDLED_BY_MANAGED_POOL = 339;\n uint256 internal constant UNHANDLED_BY_PHANTOM_POOL = 340;\n uint256 internal constant TOKEN_DOES_NOT_HAVE_RATE_PROVIDER = 341;\n uint256 internal constant INVALID_INITIALIZATION = 342;\n uint256 internal constant OUT_OF_NEW_TARGET_RANGE = 343;\n uint256 internal constant FEATURE_DISABLED = 344;\n uint256 internal constant UNINITIALIZED_POOL_CONTROLLER = 345;\n uint256 internal constant SET_SWAP_FEE_DURING_FEE_CHANGE = 346;\n uint256 internal constant SET_SWAP_FEE_PENDING_FEE_CHANGE = 347;\n uint256 internal constant CHANGE_TOKENS_DURING_WEIGHT_CHANGE = 348;\n uint256 internal constant CHANGE_TOKENS_PENDING_WEIGHT_CHANGE = 349;\n uint256 internal constant MAX_WEIGHT = 350;\n uint256 internal constant UNAUTHORIZED_JOIN = 351;\n uint256 internal constant MAX_MANAGEMENT_AUM_FEE_PERCENTAGE = 352;\n uint256 internal constant FRACTIONAL_TARGET = 353;\n uint256 internal constant ADD_OR_REMOVE_BPT = 354;\n uint256 internal constant INVALID_CIRCUIT_BREAKER_BOUNDS = 355;\n uint256 internal constant CIRCUIT_BREAKER_TRIPPED = 356;\n uint256 internal constant MALICIOUS_QUERY_REVERT = 357;\n uint256 internal constant JOINS_EXITS_DISABLED = 358;\n\n // Lib\n uint256 internal constant REENTRANCY = 400;\n uint256 internal constant SENDER_NOT_ALLOWED = 401;\n uint256 internal constant PAUSED = 402;\n uint256 internal constant PAUSE_WINDOW_EXPIRED = 403;\n uint256 internal constant MAX_PAUSE_WINDOW_DURATION = 404;\n uint256 internal constant MAX_BUFFER_PERIOD_DURATION = 405;\n uint256 internal constant INSUFFICIENT_BALANCE = 406;\n uint256 internal constant INSUFFICIENT_ALLOWANCE = 407;\n uint256 internal constant ERC20_TRANSFER_FROM_ZERO_ADDRESS = 408;\n uint256 internal constant ERC20_TRANSFER_TO_ZERO_ADDRESS = 409;\n uint256 internal constant ERC20_MINT_TO_ZERO_ADDRESS = 410;\n uint256 internal constant ERC20_BURN_FROM_ZERO_ADDRESS = 411;\n uint256 internal constant ERC20_APPROVE_FROM_ZERO_ADDRESS = 412;\n uint256 internal constant ERC20_APPROVE_TO_ZERO_ADDRESS = 413;\n uint256 internal constant ERC20_TRANSFER_EXCEEDS_ALLOWANCE = 414;\n uint256 internal constant ERC20_DECREASED_ALLOWANCE_BELOW_ZERO = 415;\n uint256 internal constant ERC20_TRANSFER_EXCEEDS_BALANCE = 416;\n uint256 internal constant ERC20_BURN_EXCEEDS_ALLOWANCE = 417;\n uint256 internal constant SAFE_ERC20_CALL_FAILED = 418;\n uint256 internal constant ADDRESS_INSUFFICIENT_BALANCE = 419;\n uint256 internal constant ADDRESS_CANNOT_SEND_VALUE = 420;\n uint256 internal constant SAFE_CAST_VALUE_CANT_FIT_INT256 = 421;\n uint256 internal constant GRANT_SENDER_NOT_ADMIN = 422;\n uint256 internal constant REVOKE_SENDER_NOT_ADMIN = 423;\n uint256 internal constant RENOUNCE_SENDER_NOT_ALLOWED = 424;\n uint256 internal constant BUFFER_PERIOD_EXPIRED = 425;\n uint256 internal constant CALLER_IS_NOT_OWNER = 426;\n uint256 internal constant NEW_OWNER_IS_ZERO = 427;\n uint256 internal constant CODE_DEPLOYMENT_FAILED = 428;\n uint256 internal constant CALL_TO_NON_CONTRACT = 429;\n uint256 internal constant LOW_LEVEL_CALL_FAILED = 430;\n uint256 internal constant NOT_PAUSED = 431;\n uint256 internal constant ADDRESS_ALREADY_ALLOWLISTED = 432;\n uint256 internal constant ADDRESS_NOT_ALLOWLISTED = 433;\n uint256 internal constant ERC20_BURN_EXCEEDS_BALANCE = 434;\n uint256 internal constant INVALID_OPERATION = 435;\n uint256 internal constant CODEC_OVERFLOW = 436;\n uint256 internal constant IN_RECOVERY_MODE = 437;\n uint256 internal constant NOT_IN_RECOVERY_MODE = 438;\n uint256 internal constant INDUCED_FAILURE = 439;\n uint256 internal constant EXPIRED_SIGNATURE = 440;\n uint256 internal constant MALFORMED_SIGNATURE = 441;\n uint256 internal constant SAFE_CAST_VALUE_CANT_FIT_UINT64 = 442;\n uint256 internal constant UNHANDLED_FEE_TYPE = 443;\n uint256 internal constant BURN_FROM_ZERO = 444;\n\n // Vault\n uint256 internal constant INVALID_POOL_ID = 500;\n uint256 internal constant CALLER_NOT_POOL = 501;\n uint256 internal constant SENDER_NOT_ASSET_MANAGER = 502;\n uint256 internal constant USER_DOESNT_ALLOW_RELAYER = 503;\n uint256 internal constant INVALID_SIGNATURE = 504;\n uint256 internal constant EXIT_BELOW_MIN = 505;\n uint256 internal constant JOIN_ABOVE_MAX = 506;\n uint256 internal constant SWAP_LIMIT = 507;\n uint256 internal constant SWAP_DEADLINE = 508;\n uint256 internal constant CANNOT_SWAP_SAME_TOKEN = 509;\n uint256 internal constant UNKNOWN_AMOUNT_IN_FIRST_SWAP = 510;\n uint256 internal constant MALCONSTRUCTED_MULTIHOP_SWAP = 511;\n uint256 internal constant INTERNAL_BALANCE_OVERFLOW = 512;\n uint256 internal constant INSUFFICIENT_INTERNAL_BALANCE = 513;\n uint256 internal constant INVALID_ETH_INTERNAL_BALANCE = 514;\n uint256 internal constant INVALID_POST_LOAN_BALANCE = 515;\n uint256 internal constant INSUFFICIENT_ETH = 516;\n uint256 internal constant UNALLOCATED_ETH = 517;\n uint256 internal constant ETH_TRANSFER = 518;\n uint256 internal constant CANNOT_USE_ETH_SENTINEL = 519;\n uint256 internal constant TOKENS_MISMATCH = 520;\n uint256 internal constant TOKEN_NOT_REGISTERED = 521;\n uint256 internal constant TOKEN_ALREADY_REGISTERED = 522;\n uint256 internal constant TOKENS_ALREADY_SET = 523;\n uint256 internal constant TOKENS_LENGTH_MUST_BE_2 = 524;\n uint256 internal constant NONZERO_TOKEN_BALANCE = 525;\n uint256 internal constant BALANCE_TOTAL_OVERFLOW = 526;\n uint256 internal constant POOL_NO_TOKENS = 527;\n uint256 internal constant INSUFFICIENT_FLASH_LOAN_BALANCE = 528;\n\n // Fees\n uint256 internal constant SWAP_FEE_PERCENTAGE_TOO_HIGH = 600;\n uint256 internal constant FLASH_LOAN_FEE_PERCENTAGE_TOO_HIGH = 601;\n uint256 internal constant INSUFFICIENT_FLASH_LOAN_FEE_AMOUNT = 602;\n uint256 internal constant AUM_FEE_PERCENTAGE_TOO_HIGH = 603;\n\n // FeeSplitter\n uint256 internal constant SPLITTER_FEE_PERCENTAGE_TOO_HIGH = 700;\n\n // Misc\n uint256 internal constant UNIMPLEMENTED = 998;\n uint256 internal constant SHOULD_NOT_HAPPEN = 999;\n}\n" + }, + "contracts/utils/DepositContractUtils.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract DepositContractUtils {\n function calculateDepositDataRoot(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature\n ) public pure returns (bytes32 node) {\n uint256 deposit_amount = 32 ether / 1 gwei;\n bytes memory amount = to_little_endian_64(uint64(deposit_amount));\n\n // Compute deposit data root (`DepositData` hash tree root)\n bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, bytes16(0)));\n bytes32 signature_root = sha256(\n abi.encodePacked(\n sha256(abi.encodePacked(signature[:64])),\n sha256(abi.encodePacked(signature[64:], bytes32(0)))\n )\n );\n node = sha256(\n abi.encodePacked(\n sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)),\n sha256(abi.encodePacked(amount, bytes24(0), signature_root))\n )\n );\n }\n\n function to_little_endian_64(uint64 value)\n internal\n pure\n returns (bytes memory ret)\n {\n ret = new bytes(8);\n bytes8 bytesValue = bytes8(value);\n // Byteswapping during copying to bytes.\n ret[0] = bytesValue[7];\n ret[1] = bytesValue[6];\n ret[2] = bytesValue[5];\n ret[3] = bytesValue[4];\n ret[4] = bytesValue[3];\n ret[5] = bytesValue[2];\n ret[6] = bytesValue[1];\n ret[7] = bytesValue[0];\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/utils/InitializableERC20Detailed.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @dev Optional functions from the ERC20 standard.\n * Converted from openzeppelin/contracts/token/ERC20/ERC20Detailed.sol\n * @author Origin Protocol Inc\n */\nabstract contract InitializableERC20Detailed is IERC20 {\n // Storage gap to skip storage from prior to OUSD reset\n uint256[100] private _____gap;\n\n string private _name;\n string private _symbol;\n uint8 private _decimals;\n\n /**\n * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of\n * these values are immutable: they can only be set once during\n * construction.\n * @notice To avoid variable shadowing appended `Arg` after arguments name.\n */\n function _initialize(\n string memory nameArg,\n string memory symbolArg,\n uint8 decimalsArg\n ) internal {\n _name = nameArg;\n _symbol = symbolArg;\n _decimals = decimalsArg;\n }\n\n /**\n * @notice Returns the name of the token.\n */\n function name() public view returns (string memory) {\n return _name;\n }\n\n /**\n * @notice Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view returns (string memory) {\n return _symbol;\n }\n\n /**\n * @notice Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5,05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei.\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view returns (uint8) {\n return _decimals;\n }\n}\n" + }, + "contracts/utils/PRBMath.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// Copied from the PRBMath library\n// https://github.com/PaulRBerg/prb-math/blob/main/src/Common.sol\n\n/// @notice Calculates the square root of x using the Babylonian method.\n///\n/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.\n///\n/// Notes:\n/// - If x is not a perfect square, the result is rounded down.\n/// - Credits to OpenZeppelin for the explanations in comments below.\n///\n/// @param x The uint256 number for which to calculate the square root.\n/// @return result The result as a uint256.\n/// @custom:smtchecker abstract-function-nondet\nfunction sqrt(uint256 x) pure returns (uint256 result) {\n if (x == 0) {\n return 0;\n }\n\n // For our first guess, we calculate the biggest power of 2 which is smaller than the square root of x.\n //\n // We know that the \"msb\" (most significant bit) of x is a power of 2 such that we have:\n //\n // $$\n // msb(x) <= x <= 2*msb(x)$\n // $$\n //\n // We write $msb(x)$ as $2^k$, and we get:\n //\n // $$\n // k = log_2(x)\n // $$\n //\n // Thus, we can write the initial inequality as:\n //\n // $$\n // 2^{log_2(x)} <= x <= 2*2^{log_2(x)+1} \\\\\n // sqrt(2^k) <= sqrt(x) < sqrt(2^{k+1}) \\\\\n // 2^{k/2} <= sqrt(x) < 2^{(k+1)/2} <= 2^{(k/2)+1}\n // $$\n //\n // Consequently, $2^{log_2(x) /2} is a good first approximation of sqrt(x) with at least one correct bit.\n uint256 xAux = uint256(x);\n result = 1;\n if (xAux >= 2**128) {\n xAux >>= 128;\n result <<= 64;\n }\n if (xAux >= 2**64) {\n xAux >>= 64;\n result <<= 32;\n }\n if (xAux >= 2**32) {\n xAux >>= 32;\n result <<= 16;\n }\n if (xAux >= 2**16) {\n xAux >>= 16;\n result <<= 8;\n }\n if (xAux >= 2**8) {\n xAux >>= 8;\n result <<= 4;\n }\n if (xAux >= 2**4) {\n xAux >>= 4;\n result <<= 2;\n }\n if (xAux >= 2**2) {\n result <<= 1;\n }\n\n // At this point, `result` is an estimation with at least one bit of precision. We know the true value has at\n // most 128 bits, since it is the square root of a uint256. Newton's method converges quadratically (precision\n // doubles at every iteration). We thus need at most 7 iteration to turn our partial result with one bit of\n // precision into the expected uint128 result.\n unchecked {\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n\n // If x is not a perfect square, round the result toward zero.\n uint256 roundedResult = x / result;\n if (result >= roundedResult) {\n result = roundedResult;\n }\n }\n}\n" + }, + "contracts/utils/StableMath.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\n// Based on StableMath from Stability Labs Pty. Ltd.\n// https://github.com/mstable/mStable-contracts/blob/master/contracts/shared/StableMath.sol\n\nlibrary StableMath {\n using SafeMath for uint256;\n\n /**\n * @dev Scaling unit for use in specific calculations,\n * where 1 * 10**18, or 1e18 represents a unit '1'\n */\n uint256 private constant FULL_SCALE = 1e18;\n\n /***************************************\n Helpers\n ****************************************/\n\n /**\n * @dev Adjust the scale of an integer\n * @param to Decimals to scale to\n * @param from Decimals to scale from\n */\n function scaleBy(\n uint256 x,\n uint256 to,\n uint256 from\n ) internal pure returns (uint256) {\n if (to > from) {\n x = x.mul(10**(to - from));\n } else if (to < from) {\n // slither-disable-next-line divide-before-multiply\n x = x.div(10**(from - to));\n }\n return x;\n }\n\n /***************************************\n Precise Arithmetic\n ****************************************/\n\n /**\n * @dev Multiplies two precise units, and then truncates by the full scale\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit\n */\n function mulTruncate(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulTruncateScale(x, y, FULL_SCALE);\n }\n\n /**\n * @dev Multiplies two precise units, and then truncates by the given scale. For example,\n * when calculating 90% of 10e18, (10e18 * 9e17) / 1e18 = (9e36) / 1e18 = 9e18\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @param scale Scale unit\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit\n */\n function mulTruncateScale(\n uint256 x,\n uint256 y,\n uint256 scale\n ) internal pure returns (uint256) {\n // e.g. assume scale = fullScale\n // z = 10e18 * 9e17 = 9e36\n uint256 z = x.mul(y);\n // return 9e36 / 1e18 = 9e18\n return z.div(scale);\n }\n\n /**\n * @dev Multiplies two precise units, and then truncates by the full scale, rounding up the result\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit, rounded up to the closest base unit.\n */\n function mulTruncateCeil(uint256 x, uint256 y)\n internal\n pure\n returns (uint256)\n {\n // e.g. 8e17 * 17268172638 = 138145381104e17\n uint256 scaled = x.mul(y);\n // e.g. 138145381104e17 + 9.99...e17 = 138145381113.99...e17\n uint256 ceil = scaled.add(FULL_SCALE.sub(1));\n // e.g. 13814538111.399...e18 / 1e18 = 13814538111\n return ceil.div(FULL_SCALE);\n }\n\n /**\n * @dev Precisely divides two units, by first scaling the left hand operand. Useful\n * for finding percentage weightings, i.e. 8e18/10e18 = 80% (or 8e17)\n * @param x Left hand input to division\n * @param y Right hand input to division\n * @return Result after multiplying the left operand by the scale, and\n * executing the division on the right hand input.\n */\n function divPrecisely(uint256 x, uint256 y)\n internal\n pure\n returns (uint256)\n {\n // e.g. 8e18 * 1e18 = 8e36\n uint256 z = x.mul(FULL_SCALE);\n // e.g. 8e36 / 10e18 = 8e17\n return z.div(y);\n }\n}\n" + }, + "contracts/vault/OETHBaseVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"./OETHVaultAdmin.sol\";\n\n/**\n * @title OETH Base VaultAdmin Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBaseVaultAdmin is OETHVaultAdmin {\n constructor(address _weth) OETHVaultAdmin(_weth) {}\n}\n" + }, + "contracts/vault/OETHBaseVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { OETHVaultCore } from \"./OETHVaultCore.sol\";\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\n\n/**\n * @title OETH Base VaultCore Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBaseVaultCore is OETHVaultCore {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(address _weth) OETHVaultCore(_weth) {}\n\n // @inheritdoc OETHVaultCore\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n override\n {\n // Only Strategist or Governor can redeem using the Vault for now.\n // We don't have the onlyGovernorOrStrategist modifier on VaultCore.\n // Since we won't be using that modifier anywhere in the VaultCore as well,\n // the check has been added inline instead of moving it to VaultStorage.\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n\n super._redeem(_amount, _minimumUnitAmount);\n }\n}\n" + }, + "contracts/vault/OETHBaseZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\n\ncontract OETHBaseZapper {\n IERC20 public immutable oethb;\n IERC4626 public immutable woethb;\n IVault public immutable vault;\n\n IWETH9 public constant weth =\n IWETH9(0x4200000000000000000000000000000000000006);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(\n address _oethb,\n address _woethb,\n address _vault\n ) {\n oethb = IERC20(_oethb);\n woethb = IERC4626(_woethb);\n vault = IVault(_vault);\n\n weth.approve(address(_vault), type(uint256).max);\n IERC20(_oethb).approve(_woethb, type(uint256).max);\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return.\n * Will verify that the user is sent 1:1 for ETH.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit ETH and receive superOETHb in return\n * Will verify that the user is sent 1:1 for ETH.\n * @return Amount of OETH sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap ETH\n weth.deposit{ value: balance }();\n\n // Mint with WETH\n return _mint(balance, msg.sender);\n }\n\n /**\n * @dev Deposit ETH and receive superOETHb in return\n * @param minReceived min amount of wsuperOETHb to receive\n * @return Amount of wsuperOETHb sent to user\n */\n function depositETHForWrappedTokens(uint256 minReceived)\n external\n payable\n returns (uint256)\n {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap ETH\n weth.deposit{ value: balance }();\n\n // Mint with WETH\n uint256 mintedOethb = _mint(balance, address(this));\n\n // Wrap superOETHb into wsuperOETHb\n uint256 mintedWoethb = woethb.deposit(mintedOethb, msg.sender);\n\n require(mintedWoethb >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWoethb;\n }\n\n /**\n * @dev Deposit WETH and receive superOETHb in return\n * @param wethAmount Amount of WETH to deposit\n * @param minReceived min amount of wsuperOETHb to receive\n * @return Amount of wsuperOETHb sent to user\n */\n function depositWETHForWrappedTokens(\n uint256 wethAmount,\n uint256 minReceived\n ) external returns (uint256) {\n // slither-disable-next-line unchecked-transfer unused-return\n weth.transferFrom(msg.sender, address(this), wethAmount);\n\n emit Zap(msg.sender, address(weth), wethAmount);\n\n // Mint with WETH\n uint256 mintedOethb = _mint(wethAmount, address(this));\n\n // Wrap superOETHb into wsuperOETHb\n uint256 mintedWoethb = woethb.deposit(mintedOethb, msg.sender);\n\n require(mintedWoethb >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWoethb;\n }\n\n /**\n * @dev Internal function to mint superOETHb with WETH\n * @param minOETH Minimum amount of OETH to for user to receive\n * @param recipient Address that receives the tokens\n * @return Amount of OETH sent to user\n */\n function _mint(uint256 minOETH, address recipient)\n internal\n returns (uint256)\n {\n uint256 toMint = weth.balanceOf(address(this));\n vault.mint(address(weth), toMint, minOETH);\n uint256 mintedAmount = oethb.balanceOf(address(this));\n require(mintedAmount >= minOETH, \"Zapper: not enough minted\");\n\n if (recipient != address(this)) {\n require(oethb.transfer(recipient, mintedAmount));\n }\n\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/OETHVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Vault } from \"./Vault.sol\";\n\n/**\n * @title OETH Vault Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVault is Vault {\n\n}\n" + }, + "contracts/vault/OETHVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { VaultAdmin } from \"./VaultAdmin.sol\";\n\n/**\n * @title OETH VaultAdmin Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVaultAdmin is VaultAdmin {\n using SafeERC20 for IERC20;\n\n address public immutable weth;\n\n constructor(address _weth) {\n weth = _weth;\n }\n\n /**\n * @notice Adds a strategy to the mint whitelist.\n * Reverts if strategy isn't approved on Vault.\n * @param strategyAddr Strategy address\n */\n function addStrategyToMintWhitelist(address strategyAddr)\n external\n onlyGovernor\n {\n require(strategies[strategyAddr].isSupported, \"Strategy not approved\");\n\n require(\n !isMintWhitelistedStrategy[strategyAddr],\n \"Already whitelisted\"\n );\n\n isMintWhitelistedStrategy[strategyAddr] = true;\n\n emit StrategyAddedToMintWhitelist(strategyAddr);\n }\n\n /**\n * @notice Removes a strategy from the mint whitelist.\n * @param strategyAddr Strategy address\n */\n function removeStrategyFromMintWhitelist(address strategyAddr)\n external\n onlyGovernor\n {\n // Intentionally skipping `strategies.isSupported` check since\n // we may wanna remove an address even after removing the strategy\n\n require(isMintWhitelistedStrategy[strategyAddr], \"Not whitelisted\");\n\n isMintWhitelistedStrategy[strategyAddr] = false;\n\n emit StrategyRemovedFromMintWhitelist(strategyAddr);\n }\n\n /// @dev Simplified version of the deposit function as WETH is the only supported asset.\n function _depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal override {\n require(\n strategies[_strategyToAddress].isSupported,\n \"Invalid to Strategy\"\n );\n require(\n _assets.length == 1 && _amounts.length == 1 && _assets[0] == weth,\n \"Only WETH is supported\"\n );\n\n // Check the there is enough WETH to transfer once the WETH reserved for the withdrawal queue is accounted for\n require(_amounts[0] <= _wethAvailable(), \"Not enough WETH available\");\n\n // Send required amount of funds to the strategy\n IERC20(weth).safeTransfer(_strategyToAddress, _amounts[0]);\n\n // Deposit all the funds that have been sent to the strategy\n IStrategy(_strategyToAddress).depositAll();\n }\n\n function _withdrawFromStrategy(\n address _recipient,\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal override {\n super._withdrawFromStrategy(\n _recipient,\n _strategyFromAddress,\n _assets,\n _amounts\n );\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n function _withdrawAllFromStrategy(address _strategyAddr) internal override {\n super._withdrawAllFromStrategy(_strategyAddr);\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n function _withdrawAllFromStrategies() internal override {\n super._withdrawAllFromStrategies();\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n /// @dev Calculate how much WETH in the vault is not reserved for the withdrawal queue.\n // That is, it is available to be redeemed or deposited into a strategy.\n function _wethAvailable() internal view returns (uint256 wethAvailable) {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n uint256 outstandingWithdrawals = queue.queued - queue.claimed;\n\n // The amount of sitting in WETH in the vault\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // If there is not enough WETH in the vault to cover the outstanding withdrawals\n if (wethBalance <= outstandingWithdrawals) {\n return 0;\n }\n\n return wethBalance - outstandingWithdrawals;\n }\n\n function _swapCollateral(\n address,\n address,\n uint256,\n uint256,\n bytes calldata\n ) internal pure override returns (uint256) {\n revert(\"Collateral swap not supported\");\n }\n}\n" + }, + "contracts/vault/OETHVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { VaultCore } from \"./VaultCore.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\n\n/**\n * @title OETH VaultCore Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVaultCore is VaultCore {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n address public immutable weth;\n uint256 public wethAssetIndex;\n\n // For future use (because OETHBaseVaultCore inherits from this)\n uint256[50] private __gap;\n\n constructor(address _weth) {\n weth = _weth;\n }\n\n /**\n * @dev Caches WETH's index in `allAssets` variable.\n * Reduces gas usage by redeem by caching that.\n */\n function cacheWETHAssetIndex() external onlyGovernor {\n uint256 assetCount = allAssets.length;\n for (uint256 i; i < assetCount; ++i) {\n if (allAssets[i] == weth) {\n wethAssetIndex = i;\n break;\n }\n }\n\n require(allAssets[wethAssetIndex] == weth, \"Invalid WETH Asset Index\");\n }\n\n // @inheritdoc VaultCore\n function mintForStrategy(uint256 amount)\n external\n override\n whenNotCapitalPaused\n {\n require(\n strategies[msg.sender].isSupported == true,\n \"Unsupported strategy\"\n );\n require(\n isMintWhitelistedStrategy[msg.sender] == true,\n \"Not whitelisted strategy\"\n );\n\n emit Mint(msg.sender, amount);\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, amount);\n }\n\n // @inheritdoc VaultCore\n function burnForStrategy(uint256 amount)\n external\n override\n whenNotCapitalPaused\n {\n require(\n strategies[msg.sender].isSupported == true,\n \"Unsupported strategy\"\n );\n require(\n isMintWhitelistedStrategy[msg.sender] == true,\n \"Not whitelisted strategy\"\n );\n\n emit Redeem(msg.sender, amount);\n\n // Burn OTokens\n oUSD.burn(msg.sender, amount);\n }\n\n // @inheritdoc VaultCore\n // slither-disable-start reentrancy-no-eth\n function _mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) internal virtual override {\n require(_asset == weth, \"Unsupported asset for minting\");\n require(_amount > 0, \"Amount must be greater than 0\");\n require(\n _amount >= _minimumOusdAmount,\n \"Mint amount lower than minimum\"\n );\n\n emit Mint(msg.sender, _amount);\n\n // Rebase must happen before any transfers occur.\n if (!rebasePaused && _amount >= rebaseThreshold) {\n _rebase();\n }\n\n // Mint oTokens\n oUSD.mint(msg.sender, _amount);\n\n // Transfer the deposited coins to the vault\n IERC20(_asset).safeTransferFrom(msg.sender, address(this), _amount);\n\n // Give priority to the withdrawal queue for the new WETH liquidity\n _addWithdrawalQueueLiquidity();\n\n // Auto-allocate if necessary\n if (_amount >= autoAllocateThreshold) {\n _allocate();\n }\n }\n\n // slither-disable-end reentrancy-no-eth\n\n // @inheritdoc VaultCore\n function _calculateRedeemOutputs(uint256 _amount)\n internal\n view\n virtual\n override\n returns (uint256[] memory outputs)\n {\n // Overrides `VaultCore._calculateRedeemOutputs` to redeem with only\n // WETH instead of LST-mix. Doesn't change the function signature\n // for backward compatibility\n\n // Calculate redeem fee\n if (redeemFeeBps > 0) {\n uint256 redeemFee = _amount.mulTruncateScale(redeemFeeBps, 1e4);\n _amount = _amount - redeemFee;\n }\n\n // Ensure that the WETH index is cached\n uint256 _wethAssetIndex = wethAssetIndex;\n require(\n allAssets[_wethAssetIndex] == weth,\n \"WETH Asset index not cached\"\n );\n\n outputs = new uint256[](allAssets.length);\n outputs[_wethAssetIndex] = _amount;\n }\n\n // @inheritdoc VaultCore\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n override\n {\n // Override `VaultCore._redeem` to simplify it. Gets rid of oracle\n // usage and looping through all assets for LST-mix redeem. Instead\n // does a simple WETH-only redeem.\n emit Redeem(msg.sender, _amount);\n\n if (_amount == 0) {\n return;\n }\n\n // Amount excluding fees\n // No fee for the strategist or the governor, makes it easier to do operations\n uint256 amountMinusFee = (msg.sender == strategistAddr || isGovernor())\n ? _amount\n : _calculateRedeemOutputs(_amount)[wethAssetIndex];\n\n require(\n amountMinusFee >= _minimumUnitAmount,\n \"Redeem amount lower than minimum\"\n );\n\n // Is there enough WETH in the Vault available after accounting for the withdrawal queue\n require(_wethAvailable() >= amountMinusFee, \"Liquidity error\");\n\n // Transfer WETH minus the fee to the redeemer\n IERC20(weth).safeTransfer(msg.sender, amountMinusFee);\n\n // Burn OETH from user (including fees)\n oUSD.burn(msg.sender, _amount);\n\n // Prevent insolvency\n _postRedeem(_amount);\n }\n\n /**\n * @notice Request an asynchronous withdrawal of WETH in exchange for OETH.\n * The OETH is burned on request and the WETH is transferred to the withdrawer on claim.\n * This request can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal this request's `queued` amount.\n * There is a minimum of 10 minutes before a request can be claimed. After that, the request just needs\n * enough WETH liquidity in the Vault to satisfy all the outstanding requests to that point in the queue.\n * OETH is converted to WETH at 1:1.\n * @param _amount Amount of OETH to burn.\n * @return requestId Unique ID for the withdrawal request\n * @return queued Cumulative total of all WETH queued including already claimed requests.\n */\n function requestWithdrawal(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256 requestId, uint256 queued)\n {\n require(withdrawalClaimDelay > 0, \"Async withdrawals not enabled\");\n\n // The check that the requester has enough OETH is done in to later burn call\n\n requestId = withdrawalQueueMetadata.nextWithdrawalIndex;\n queued = withdrawalQueueMetadata.queued + _amount;\n\n // Store the next withdrawal request\n withdrawalQueueMetadata.nextWithdrawalIndex = SafeCast.toUint128(\n requestId + 1\n );\n // Store the updated queued amount which reserves WETH in the withdrawal queue\n // and reduces the vault's total assets\n withdrawalQueueMetadata.queued = SafeCast.toUint128(queued);\n // Store the user's withdrawal request\n withdrawalRequests[requestId] = WithdrawalRequest({\n withdrawer: msg.sender,\n claimed: false,\n timestamp: uint40(block.timestamp),\n amount: SafeCast.toUint128(_amount),\n queued: SafeCast.toUint128(queued)\n });\n\n // Burn the user's OETH\n oUSD.burn(msg.sender, _amount);\n\n // Prevent withdrawal if the vault is solvent by more than the allowed percentage\n _postRedeem(_amount);\n\n emit WithdrawalRequested(msg.sender, requestId, _amount, queued);\n }\n\n // slither-disable-start reentrancy-no-eth\n /**\n * @notice Claim a previously requested withdrawal once it is claimable.\n * This request can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal this request's `queued` amount and 10 minutes has passed.\n * If the requests is not claimable, the transaction will revert with `Queue pending liquidity`.\n * If the request is not older than 10 minutes, the transaction will revert with `Claim delay not met`.\n * OETH is converted to WETH at 1:1.\n * @param _requestId Unique ID for the withdrawal request\n * @return amount Amount of WETH transferred to the withdrawer\n */\n function claimWithdrawal(uint256 _requestId)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256 amount)\n {\n // Try and get more liquidity if there is not enough available\n if (\n withdrawalRequests[_requestId].queued >\n withdrawalQueueMetadata.claimable\n ) {\n // Add any WETH to the withdrawal queue\n // this needs to remain here as:\n // - Vault can be funded and `addWithdrawalQueueLiquidity` is not externally called\n // - funds can be withdrawn from a strategy\n //\n // Those funds need to be added to withdrawal queue liquidity\n _addWithdrawalQueueLiquidity();\n }\n\n amount = _claimWithdrawal(_requestId);\n\n // transfer WETH from the vault to the withdrawer\n IERC20(weth).safeTransfer(msg.sender, amount);\n\n // Prevent insolvency\n _postRedeem(amount);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @notice Claim a previously requested withdrawals once they are claimable.\n * This requests can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal each request's `queued` amount and 10 minutes has passed.\n * If one of the requests is not claimable, the whole transaction will revert with `Queue pending liquidity`.\n * If one of the requests is not older than 10 minutes,\n * the whole transaction will revert with `Claim delay not met`.\n * @param _requestIds Unique ID of each withdrawal request\n * @return amounts Amount of WETH received for each request\n * @return totalAmount Total amount of WETH transferred to the withdrawer\n */\n function claimWithdrawals(uint256[] calldata _requestIds)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256[] memory amounts, uint256 totalAmount)\n {\n // Add any WETH to the withdrawal queue\n // this needs to remain here as:\n // - Vault can be funded and `addWithdrawalQueueLiquidity` is not externally called\n // - funds can be withdrawn from a strategy\n //\n // Those funds need to be added to withdrawal queue liquidity\n _addWithdrawalQueueLiquidity();\n\n amounts = new uint256[](_requestIds.length);\n for (uint256 i; i < _requestIds.length; ++i) {\n amounts[i] = _claimWithdrawal(_requestIds[i]);\n totalAmount += amounts[i];\n }\n\n // transfer all the claimed WETH from the vault to the withdrawer\n IERC20(weth).safeTransfer(msg.sender, totalAmount);\n\n // Prevent insolvency\n _postRedeem(totalAmount);\n }\n\n function _claimWithdrawal(uint256 requestId)\n internal\n returns (uint256 amount)\n {\n require(withdrawalClaimDelay > 0, \"Async withdrawals not enabled\");\n\n // Load the structs from storage into memory\n WithdrawalRequest memory request = withdrawalRequests[requestId];\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n require(\n request.timestamp + withdrawalClaimDelay <= block.timestamp,\n \"Claim delay not met\"\n );\n // If there isn't enough reserved liquidity in the queue to claim\n require(request.queued <= queue.claimable, \"Queue pending liquidity\");\n require(request.withdrawer == msg.sender, \"Not requester\");\n require(request.claimed == false, \"Already claimed\");\n\n // Store the request as claimed\n withdrawalRequests[requestId].claimed = true;\n // Store the updated claimed amount\n withdrawalQueueMetadata.claimed = queue.claimed + request.amount;\n\n emit WithdrawalClaimed(msg.sender, requestId, request.amount);\n\n return request.amount;\n }\n\n /// @notice Adds WETH to the withdrawal queue if there is a funding shortfall.\n /// @dev is called from the Native Staking strategy when validator withdrawals are processed.\n /// It also called before any WETH is allocated to a strategy.\n function addWithdrawalQueueLiquidity() external {\n _addWithdrawalQueueLiquidity();\n }\n\n /// @dev Adds WETH to the withdrawal queue if there is a funding shortfall.\n /// This assumes 1 WETH equal 1 OETH.\n function _addWithdrawalQueueLiquidity()\n internal\n returns (uint256 addedClaimable)\n {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // Check if the claimable WETH is less than the queued amount\n uint256 queueShortfall = queue.queued - queue.claimable;\n\n // No need to do anything is the withdrawal queue is full funded\n if (queueShortfall == 0) {\n return 0;\n }\n\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // Of the claimable withdrawal requests, how much is unclaimed?\n // That is, the amount of WETH that is currently allocated for the withdrawal queue\n uint256 allocatedWeth = queue.claimable - queue.claimed;\n\n // If there is no unallocated WETH then there is nothing to add to the queue\n if (wethBalance <= allocatedWeth) {\n return 0;\n }\n\n uint256 unallocatedWeth = wethBalance - allocatedWeth;\n\n // the new claimable amount is the smaller of the queue shortfall or unallocated weth\n addedClaimable = queueShortfall < unallocatedWeth\n ? queueShortfall\n : unallocatedWeth;\n uint256 newClaimable = queue.claimable + addedClaimable;\n\n // Store the new claimable amount back to storage\n withdrawalQueueMetadata.claimable = SafeCast.toUint128(newClaimable);\n\n // emit a WithdrawalClaimable event\n emit WithdrawalClaimable(newClaimable, addedClaimable);\n }\n\n /***************************************\n View Functions\n ****************************************/\n\n /// @dev Calculate how much WETH in the vault is not reserved for the withdrawal queue.\n // That is, it is available to be redeemed or deposited into a strategy.\n function _wethAvailable() internal view returns (uint256 wethAvailable) {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n uint256 outstandingWithdrawals = queue.queued - queue.claimed;\n\n // The amount of sitting in WETH in the vault\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // If there is not enough WETH in the vault to cover the outstanding withdrawals\n if (wethBalance <= outstandingWithdrawals) {\n return 0;\n }\n\n return wethBalance - outstandingWithdrawals;\n }\n\n /// @dev Get the balance of an asset held in Vault and all strategies\n /// less any WETH that is reserved for the withdrawal queue.\n /// WETH is the only asset that can return a non-zero balance.\n /// All other assets will return 0 even if there is some dust amounts left in the Vault.\n /// For example, there is 1 wei left of stETH in the OETH Vault but will return 0 in this function.\n ///\n /// If there is not enough WETH in the vault and all strategies to cover all outstanding\n /// withdrawal requests then return a WETH balance of 0\n function _checkBalance(address _asset)\n internal\n view\n override\n returns (uint256 balance)\n {\n if (_asset != weth) {\n return 0;\n }\n\n // Get the WETH in the vault and the strategies\n balance = super._checkBalance(_asset);\n\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // If the vault becomes insolvent enough that the total value in the vault and all strategies\n // is less than the outstanding withdrawals.\n // For example, there was a mass slashing event and most users request a withdrawal.\n if (balance + queue.claimed < queue.queued) {\n return 0;\n }\n\n // Need to remove WETH that is reserved for the withdrawal queue\n return balance + queue.claimed - queue.queued;\n }\n\n /**\n * @notice Allocate unallocated funds on Vault to strategies.\n **/\n function allocate() external override whenNotCapitalPaused nonReentrant {\n // Add any unallocated WETH to the withdrawal queue first\n _addWithdrawalQueueLiquidity();\n\n _allocate();\n }\n\n /// @dev Allocate WETH to the default WETH strategy if there is excess to the Vault buffer.\n /// This is called from either `mint` or `allocate` and assumes `_addWithdrawalQueueLiquidity`\n /// has been called before this function.\n function _allocate() internal override {\n // No need to do anything if no default strategy for WETH\n address depositStrategyAddr = assetDefaultStrategies[weth];\n if (depositStrategyAddr == address(0)) return;\n\n uint256 wethAvailableInVault = _wethAvailable();\n // No need to do anything if there isn't any WETH in the vault to allocate\n if (wethAvailableInVault == 0) return;\n\n // Calculate the target buffer for the vault using the total supply\n uint256 totalSupply = oUSD.totalSupply();\n uint256 targetBuffer = totalSupply.mulTruncate(vaultBuffer);\n\n // If available WETH in the Vault is below or equal the target buffer then there's nothing to allocate\n if (wethAvailableInVault <= targetBuffer) return;\n\n // The amount of assets to allocate to the default strategy\n uint256 allocateAmount = wethAvailableInVault - targetBuffer;\n\n IStrategy strategy = IStrategy(depositStrategyAddr);\n // Transfer WETH to the strategy and call the strategy's deposit function\n IERC20(weth).safeTransfer(address(strategy), allocateAmount);\n strategy.deposit(weth, allocateAmount);\n\n emit AssetAllocated(weth, depositStrategyAddr, allocateAmount);\n }\n\n /// @dev The total value of all WETH held by the vault and all its strategies\n /// less any WETH that is reserved for the withdrawal queue.\n ///\n // If there is not enough WETH in the vault and all strategies to cover all outstanding\n // withdrawal requests then return a total value of 0.\n function _totalValue() internal view override returns (uint256 value) {\n // As WETH is the only asset, just return the WETH balance\n return _checkBalance(weth);\n }\n\n /// @dev Only WETH is supported in the OETH Vault so return the WETH balance only\n /// Any ETH balances in the Vault will be ignored.\n /// Amounts from previously supported vault assets will also be ignored.\n /// For example, there is 1 wei left of stETH in the OETH Vault but is will be ignored.\n function _totalValueInVault()\n internal\n view\n override\n returns (uint256 value)\n {\n value = IERC20(weth).balanceOf(address(this));\n }\n}\n" + }, + "contracts/vault/OETHZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { ISfrxETH } from \"../interfaces/ISfrxETH.sol\";\n\ncontract OETHZapper {\n IERC20 public immutable oeth;\n IVault public immutable vault;\n\n IWETH9 public constant weth =\n IWETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);\n IERC20 public constant frxeth =\n IERC20(0x5E8422345238F34275888049021821E8E08CAa1f);\n ISfrxETH public constant sfrxeth =\n ISfrxETH(0xac3E018457B222d93114458476f3E3416Abbe38F);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(address _oeth, address _vault) {\n oeth = IERC20(_oeth);\n vault = IVault(_vault);\n\n weth.approve(address(_vault), type(uint256).max);\n frxeth.approve(address(_vault), type(uint256).max);\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return.\n * Will verify that the user is sent 1:1 for ETH.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return\n * Will verify that the user is sent 1:1 for ETH.\n * @return Amount of OETH sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n weth.deposit{ value: balance }();\n emit Zap(msg.sender, ETH_MARKER, balance);\n return _mint(address(weth), balance);\n }\n\n /**\n * @dev Deposit SFRXETH to the vault and receive OETH in return\n * @param amount Amount of SFRXETH to deposit\n * @param minOETH Minimum amount of OETH to receive\n * @return Amount of OETH sent to user\n */\n function depositSFRXETH(uint256 amount, uint256 minOETH)\n external\n returns (uint256)\n {\n sfrxeth.redeem(amount, address(this), msg.sender);\n emit Zap(msg.sender, address(sfrxeth), amount);\n return _mint(address(frxeth), minOETH);\n }\n\n /**\n * @dev Internal function to mint OETH from an asset\n * @param asset Address of asset for the vault to mint from\n * @param minOETH Minimum amount of OETH to for user to receive\n * @return Amount of OETH sent to user\n */\n function _mint(address asset, uint256 minOETH) internal returns (uint256) {\n uint256 toMint = IERC20(asset).balanceOf(address(this));\n vault.mint(asset, toMint, minOETH);\n uint256 mintedAmount = oeth.balanceOf(address(this));\n require(mintedAmount >= minOETH, \"Zapper: not enough minted\");\n require(oeth.transfer(msg.sender, mintedAmount));\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/OSonicVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"./OETHVaultAdmin.sol\";\n\n/**\n * @title Origin Sonic VaultAdmin contract on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonicVaultAdmin is OETHVaultAdmin {\n /// @param _wS Sonic's Wrapped S token\n constructor(address _wS) OETHVaultAdmin(_wS) {}\n\n /***************************************\n Asset Config\n ****************************************/\n\n /**\n * @notice Add a supported asset to the contract, i.e. one that can be to mint OTokens.\n * @dev Overridden to remove price provider integration\n * @param _asset Address of asset\n * @param _unitConversion 0 decimals, 1 exchange rate\n */\n function supportAsset(address _asset, uint8 _unitConversion)\n external\n override\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Asset already supported\");\n\n assets[_asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(_unitConversion),\n decimals: 0, // will be overridden in _cacheDecimals\n allowedOracleSlippageBps: 0 // 0% by default\n });\n\n _cacheDecimals(_asset);\n allAssets.push(_asset);\n\n emit AssetSupported(_asset);\n }\n}\n" + }, + "contracts/vault/OSonicVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultCore } from \"./OETHVaultCore.sol\";\n\n/**\n * @title Origin Sonic VaultCore contract on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonicVaultCore is OETHVaultCore {\n /// @param _wS Sonic's Wrapped S token\n constructor(address _wS) OETHVaultCore(_wS) {}\n\n /**\n * @notice Instant redeem is not supported on Sonic.\n * Use the asynchronous `requestWithdrawal` a `claimWithdrawal` instead.\n */\n function _redeem(uint256, uint256) internal override {\n revert(\"unsupported function\");\n }\n}\n" + }, + "contracts/vault/OSonicZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWrappedSonic } from \"../interfaces/sonic/IWrappedSonic.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\n\n/**\n * @title Zapper for Origin Sonic (OS) tokens\n * @author Origin Protocol Inc\n */\ncontract OSonicZapper {\n IERC20 public immutable OS;\n IERC4626 public immutable wOS;\n IVault public immutable vault;\n\n IWrappedSonic public constant wS =\n IWrappedSonic(0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(\n address _OS,\n address _wOS,\n address _vault\n ) {\n OS = IERC20(_OS);\n wOS = IERC4626(_wOS);\n vault = IVault(_vault);\n\n wS.approve(address(_vault), type(uint256).max);\n IERC20(_OS).approve(_wOS, type(uint256).max);\n }\n\n /**\n * @dev Deposit native S currency and receive Origin Sonic (OS) tokens in return.\n * Will verify that the user is sent 1:1 for S.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit native S currency and receive Origin Sonic (OS) tokens in return.\n * Will verify that the user is sent 1:1 for S.\n * @return Amount of Origin Sonic (OS) tokens sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap native S\n wS.deposit{ value: balance }();\n\n // Mint Origin Sonic (OS) with Wrapped Sonic (wS)\n return _mint(balance, msg.sender);\n }\n\n /**\n * @dev Deposit S and receive Wrapped Origin Sonic (wOS) in return\n * @param minReceived min amount of Wrapped Origin Sonic (wOS) to receive\n * @return Amount of Wrapped Origin Sonic (wOS) tokens sent to user\n */\n function depositSForWrappedTokens(uint256 minReceived)\n external\n payable\n returns (uint256)\n {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap S\n wS.deposit{ value: balance }();\n\n // Mint with Wrapped Sonic\n uint256 mintOS = _mint(balance, address(this));\n\n // Wrap Origin Sonic (OS) into Wrapped Origin Sonic (wOS)\n uint256 mintedWOS = wOS.deposit(mintOS, msg.sender);\n\n require(mintedWOS >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWOS;\n }\n\n /**\n * @dev Deposit Wrapped Sonic (wS) tokens and receive Wrapped Origin Sonic (wOS) tokens in return\n * @param wSAmount Amount of Wrapped Sonic (wS) to deposit\n * @param minReceived min amount of Wrapped Origin Sonic (wOS) token to receive\n * @return Amount of Wrapped Origin Sonic (wOS) tokens sent to user\n */\n function depositWSForWrappedTokens(uint256 wSAmount, uint256 minReceived)\n external\n returns (uint256)\n {\n // slither-disable-next-line unchecked-transfer unused-return\n wS.transferFrom(msg.sender, address(this), wSAmount);\n\n emit Zap(msg.sender, address(wS), wSAmount);\n\n // Mint with Wrapped Sonic (wS)\n uint256 mintedOS = _mint(wSAmount, address(this));\n\n // Wrap Origin Sonic (OS) tokens into Wrapped Origin Sonic (wOS) tokens\n uint256 mintedWOS = wOS.deposit(mintedOS, msg.sender);\n\n require(mintedWOS >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWOS;\n }\n\n /**\n * @dev Internal function to mint Origin Sonic (OS) with Wrapped S (wS)\n * @param minOS Minimum amount of Origin Sonic (OS) tokens the user can receive\n * @param recipient Address that receives the tokens\n * @return Amount of Origin Sonic (OS) tokens sent to the recipient\n */\n function _mint(uint256 minOS, address recipient)\n internal\n returns (uint256)\n {\n uint256 toMint = wS.balanceOf(address(this));\n vault.mint(address(wS), toMint, minOS);\n uint256 mintedAmount = OS.balanceOf(address(this));\n require(mintedAmount >= minOS, \"Zapper: not enough minted\");\n\n if (recipient != address(this)) {\n require(OS.transfer(recipient, mintedAmount));\n }\n\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/Vault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD VaultInitializer Contract\n * @notice The VaultInitializer sets up the initial contract.\n * @author Origin Protocol Inc\n */\nimport { VaultInitializer } from \"./VaultInitializer.sol\";\nimport { VaultAdmin } from \"./VaultAdmin.sol\";\n\ncontract Vault is VaultInitializer, VaultAdmin {}\n" + }, + "contracts/vault/VaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultAdmin contract\n * @notice The VaultAdmin contract makes configuration and admin calls on the vault.\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport \"./VaultStorage.sol\";\n\ncontract VaultAdmin is VaultStorage {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /***************************************\n Configuration\n ****************************************/\n\n /**\n * @notice Set address of price provider.\n * @param _priceProvider Address of price provider\n */\n function setPriceProvider(address _priceProvider) external onlyGovernor {\n priceProvider = _priceProvider;\n emit PriceProviderUpdated(_priceProvider);\n }\n\n /**\n * @notice Set a fee in basis points to be charged for a redeem.\n * @param _redeemFeeBps Basis point fee to be charged\n */\n function setRedeemFeeBps(uint256 _redeemFeeBps) external onlyGovernor {\n require(_redeemFeeBps <= 1000, \"Redeem fee should not be over 10%\");\n redeemFeeBps = _redeemFeeBps;\n emit RedeemFeeUpdated(_redeemFeeBps);\n }\n\n /**\n * @notice Set a buffer of assets to keep in the Vault to handle most\n * redemptions without needing to spend gas unwinding assets from a Strategy.\n * @param _vaultBuffer Percentage using 18 decimals. 100% = 1e18.\n */\n function setVaultBuffer(uint256 _vaultBuffer)\n external\n onlyGovernorOrStrategist\n {\n require(_vaultBuffer <= 1e18, \"Invalid value\");\n vaultBuffer = _vaultBuffer;\n emit VaultBufferUpdated(_vaultBuffer);\n }\n\n /**\n * @notice Sets the minimum amount of OTokens in a mint to trigger an\n * automatic allocation of funds afterwords.\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setAutoAllocateThreshold(uint256 _threshold)\n external\n onlyGovernor\n {\n autoAllocateThreshold = _threshold;\n emit AllocateThresholdUpdated(_threshold);\n }\n\n /**\n * @notice Set a minimum amount of OTokens in a mint or redeem that triggers a\n * rebase\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setRebaseThreshold(uint256 _threshold) external onlyGovernor {\n rebaseThreshold = _threshold;\n emit RebaseThresholdUpdated(_threshold);\n }\n\n /**\n * @notice Set address of Strategist\n * @param _address Address of Strategist\n */\n function setStrategistAddr(address _address) external onlyGovernor {\n strategistAddr = _address;\n emit StrategistUpdated(_address);\n }\n\n /**\n * @notice Set the default Strategy for an asset, i.e. the one which the asset\n will be automatically allocated to and withdrawn from\n * @param _asset Address of the asset\n * @param _strategy Address of the Strategy\n */\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external\n onlyGovernorOrStrategist\n {\n emit AssetDefaultStrategyUpdated(_asset, _strategy);\n // If its a zero address being passed for the strategy we are removing\n // the default strategy\n if (_strategy != address(0)) {\n // Make sure the strategy meets some criteria\n require(strategies[_strategy].isSupported, \"Strategy not approved\");\n IStrategy strategy = IStrategy(_strategy);\n require(assets[_asset].isSupported, \"Asset is not supported\");\n require(\n strategy.supportsAsset(_asset),\n \"Asset not supported by Strategy\"\n );\n }\n assetDefaultStrategies[_asset] = _strategy;\n }\n\n /**\n * @notice Set maximum amount of OTokens that can at any point be minted and deployed\n * to strategy (used only by ConvexOUSDMetaStrategy for now).\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setNetOusdMintForStrategyThreshold(uint256 _threshold)\n external\n onlyGovernor\n {\n /**\n * Because `netOusdMintedForStrategy` check in vault core works both ways\n * (positive and negative) the actual impact of the amount of OToken minted\n * could be double the threshold. E.g.:\n * - contract has threshold set to 100\n * - state of netOusdMinted is -90\n * - in effect it can mint 190 OToken and still be within limits\n *\n * We are somewhat mitigating this behaviour by resetting the netOusdMinted\n * counter whenever new threshold is set. So it can only move one threshold\n * amount in each direction. This also enables us to reduce the threshold\n * amount and not have problems with current netOusdMinted being near\n * limits on either side.\n */\n netOusdMintedForStrategy = 0;\n netOusdMintForStrategyThreshold = _threshold;\n emit NetOusdMintForStrategyThresholdChanged(_threshold);\n }\n\n /**\n * @notice Changes the async withdrawal claim period for OETH & superOETHb\n * @param _delay Delay period (should be between 10 mins to 7 days).\n * Set to 0 to disable async withdrawals\n */\n function setWithdrawalClaimDelay(uint256 _delay) external onlyGovernor {\n require(\n _delay == 0 || (_delay >= 10 minutes && _delay <= 15 days),\n \"Invalid claim delay period\"\n );\n withdrawalClaimDelay = _delay;\n emit WithdrawalClaimDelayUpdated(_delay);\n }\n\n /**\n * @notice Set a yield streaming max rate. This spreads yield over\n * time if it is above the max rate.\n * @param yearlyApr in 1e18 notation. 3 * 1e18 = 3% APR\n */\n function setRebaseRateMax(uint256 yearlyApr)\n external\n onlyGovernorOrStrategist\n {\n // The old yield will be at the old rate\n IVault(address(this)).rebase();\n // Change the rate\n uint256 newPerSecond = yearlyApr / 100 / 365 days;\n require(newPerSecond <= MAX_REBASE_PER_SECOND, \"Rate too high\");\n rebasePerSecondMax = newPerSecond.toUint64();\n emit RebasePerSecondMaxChanged(newPerSecond);\n }\n\n /**\n * @notice Set the drip duration period\n * @param _dripDuration Time in seconds to target a constant yield rate\n */\n function setDripDuration(uint256 _dripDuration)\n external\n onlyGovernorOrStrategist\n {\n // The old yield will be at the old rate\n IVault(address(this)).rebase();\n dripDuration = _dripDuration.toUint64();\n emit DripDurationChanged(_dripDuration);\n }\n\n /***************************************\n Swaps\n ****************************************/\n\n /**\n * @notice Strategist swaps collateral assets sitting in the vault.\n * @param _fromAsset The token address of the asset being sold by the vault.\n * @param _toAsset The token address of the asset being purchased by the vault.\n * @param _fromAssetAmount The amount of assets being sold by the vault.\n * @param _minToAssetAmount The minimum amount of assets to be purchased.\n * @param _data implementation specific data. eg 1Inch swap data\n * @return toAssetAmount The amount of toAssets that was received from the swap\n */\n function swapCollateral(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n )\n external\n nonReentrant\n onlyGovernorOrStrategist\n returns (uint256 toAssetAmount)\n {\n toAssetAmount = _swapCollateral(\n _fromAsset,\n _toAsset,\n _fromAssetAmount,\n _minToAssetAmount,\n _data\n );\n }\n\n function _swapCollateral(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n ) internal virtual returns (uint256 toAssetAmount) {\n // Check fromAsset and toAsset are valid\n Asset memory fromAssetConfig = assets[_fromAsset];\n Asset memory toAssetConfig = assets[_toAsset];\n require(fromAssetConfig.isSupported, \"From asset is not supported\");\n require(toAssetConfig.isSupported, \"To asset is not supported\");\n\n // Load swap config into memory to avoid separate SLOADs\n SwapConfig memory config = swapConfig;\n\n // Scope a new block to remove toAssetBalBefore from the scope of swapCollateral.\n // This avoids a stack too deep error.\n {\n uint256 toAssetBalBefore = IERC20(_toAsset).balanceOf(\n address(this)\n );\n\n // Transfer from assets to the swapper contract\n IERC20(_fromAsset).safeTransfer(config.swapper, _fromAssetAmount);\n\n // Call to the Swapper contract to do the actual swap\n // The -1 is required for stETH which sometimes transfers 1 wei less than what was specified.\n // slither-disable-next-line unused-return\n ISwapper(config.swapper).swap(\n _fromAsset,\n _toAsset,\n _fromAssetAmount - 1,\n _minToAssetAmount,\n _data\n );\n\n // Compute the change in asset balance held by the Vault\n toAssetAmount =\n IERC20(_toAsset).balanceOf(address(this)) -\n toAssetBalBefore;\n }\n\n // Check the to assets returned is above slippage amount specified by the strategist\n require(\n toAssetAmount >= _minToAssetAmount,\n \"Strategist slippage limit\"\n );\n\n // Scope a new block to remove minOracleToAssetAmount from the scope of swapCollateral.\n // This avoids a stack too deep error.\n {\n // Check the slippage against the Oracle in case the strategist made a mistake or has become malicious.\n // to asset amount = from asset amount * from asset price / to asset price\n uint256 minOracleToAssetAmount = (_fromAssetAmount *\n (1e4 - fromAssetConfig.allowedOracleSlippageBps) *\n IOracle(priceProvider).price(_fromAsset)) /\n (IOracle(priceProvider).price(_toAsset) *\n (1e4 + toAssetConfig.allowedOracleSlippageBps));\n\n // Scale both sides up to 18 decimals to compare\n require(\n toAssetAmount.scaleBy(18, toAssetConfig.decimals) >=\n minOracleToAssetAmount.scaleBy(\n 18,\n fromAssetConfig.decimals\n ),\n \"Oracle slippage limit exceeded\"\n );\n }\n\n // Check the vault's total value hasn't gone below the OToken total supply\n // by more than the allowed percentage.\n require(\n IVault(address(this)).totalValue() >=\n (oUSD.totalSupply() * ((1e4 - config.allowedUndervalueBps))) /\n 1e4,\n \"Allowed value < supply\"\n );\n\n emit Swapped(_fromAsset, _toAsset, _fromAssetAmount, toAssetAmount);\n }\n\n /***************************************\n Swap Config\n ****************************************/\n\n /**\n * @notice Set the contract the performs swaps of collateral assets.\n * @param _swapperAddr Address of the Swapper contract that implements the ISwapper interface.\n */\n function setSwapper(address _swapperAddr) external onlyGovernor {\n swapConfig.swapper = _swapperAddr;\n emit SwapperChanged(_swapperAddr);\n }\n\n /// @notice Contract that swaps the vault's collateral assets\n function swapper() external view returns (address swapper_) {\n swapper_ = swapConfig.swapper;\n }\n\n /**\n * @notice Set max allowed percentage the vault total value can drop below the OToken total supply in basis points\n * when executing collateral swaps.\n * @param _basis Percentage in basis points. eg 100 == 1%\n */\n function setSwapAllowedUndervalue(uint16 _basis) external onlyGovernor {\n require(_basis < 10001, \"Invalid basis points\");\n swapConfig.allowedUndervalueBps = _basis;\n emit SwapAllowedUndervalueChanged(_basis);\n }\n\n /**\n * @notice Max allowed percentage the vault total value can drop below the OToken total supply in basis points\n * when executing a collateral swap.\n * For example 100 == 1%\n * @return value Percentage in basis points.\n */\n function allowedSwapUndervalue() external view returns (uint256 value) {\n value = swapConfig.allowedUndervalueBps;\n }\n\n /**\n * @notice Set the allowed slippage from the Oracle price for collateral asset swaps.\n * @param _asset Address of the asset token.\n * @param _allowedOracleSlippageBps allowed slippage from Oracle in basis points. eg 20 = 0.2%. Max 10%.\n */\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external\n onlyGovernor\n {\n require(assets[_asset].isSupported, \"Asset not supported\");\n require(_allowedOracleSlippageBps < 1000, \"Slippage too high\");\n\n assets[_asset].allowedOracleSlippageBps = _allowedOracleSlippageBps;\n\n emit SwapSlippageChanged(_asset, _allowedOracleSlippageBps);\n }\n\n /***************************************\n Asset Config\n ****************************************/\n\n /**\n * @notice Add a supported asset to the contract, i.e. one that can be\n * to mint OTokens.\n * @param _asset Address of asset\n */\n function supportAsset(address _asset, uint8 _unitConversion)\n external\n virtual\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Asset already supported\");\n\n assets[_asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(_unitConversion),\n decimals: 0, // will be overridden in _cacheDecimals\n allowedOracleSlippageBps: 0 // 0% by default\n });\n\n _cacheDecimals(_asset);\n allAssets.push(_asset);\n\n // Verify that our oracle supports the asset\n // slither-disable-next-line unused-return\n IOracle(priceProvider).price(_asset);\n\n emit AssetSupported(_asset);\n }\n\n /**\n * @notice Remove a supported asset from the Vault\n * @param _asset Address of asset\n */\n function removeAsset(address _asset) external onlyGovernor {\n require(assets[_asset].isSupported, \"Asset not supported\");\n\n // 1e13 for 18 decimals. And 10 for 6 decimals\n uint256 maxDustBalance = uint256(1e13).scaleBy(\n assets[_asset].decimals,\n 18\n );\n\n require(\n IVault(address(this)).checkBalance(_asset) <= maxDustBalance,\n \"Vault still holds asset\"\n );\n\n uint256 assetsCount = allAssets.length;\n uint256 assetIndex = assetsCount; // initialize at invalid index\n for (uint256 i = 0; i < assetsCount; ++i) {\n if (allAssets[i] == _asset) {\n assetIndex = i;\n break;\n }\n }\n\n // Note: If asset is not found in `allAssets`, the following line\n // will revert with an out-of-bound error. However, there's no\n // reason why an asset would have `Asset.isSupported = true` but\n // not exist in `allAssets`.\n\n // Update allAssets array\n allAssets[assetIndex] = allAssets[assetsCount - 1];\n allAssets.pop();\n\n // Reset default strategy\n assetDefaultStrategies[_asset] = address(0);\n emit AssetDefaultStrategyUpdated(_asset, address(0));\n\n // Remove asset from storage\n delete assets[_asset];\n\n emit AssetRemoved(_asset);\n }\n\n /**\n * @notice Cache decimals on OracleRouter for a particular asset. This action\n * is required before that asset's price can be accessed.\n * @param _asset Address of asset token\n */\n function cacheDecimals(address _asset) external onlyGovernor {\n _cacheDecimals(_asset);\n }\n\n /***************************************\n Strategy Config\n ****************************************/\n\n /**\n * @notice Add a strategy to the Vault.\n * @param _addr Address of the strategy to add\n */\n function approveStrategy(address _addr) external onlyGovernor {\n require(!strategies[_addr].isSupported, \"Strategy already approved\");\n strategies[_addr] = Strategy({ isSupported: true, _deprecated: 0 });\n allStrategies.push(_addr);\n emit StrategyApproved(_addr);\n }\n\n /**\n * @notice Remove a strategy from the Vault.\n * @param _addr Address of the strategy to remove\n */\n\n function removeStrategy(address _addr) external onlyGovernor {\n require(strategies[_addr].isSupported, \"Strategy not approved\");\n\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n require(\n assetDefaultStrategies[allAssets[i]] != _addr,\n \"Strategy is default for an asset\"\n );\n }\n\n // Initialize strategyIndex with out of bounds result so function will\n // revert if no valid index found\n uint256 stratCount = allStrategies.length;\n uint256 strategyIndex = stratCount;\n for (uint256 i = 0; i < stratCount; ++i) {\n if (allStrategies[i] == _addr) {\n strategyIndex = i;\n break;\n }\n }\n\n if (strategyIndex < stratCount) {\n allStrategies[strategyIndex] = allStrategies[stratCount - 1];\n allStrategies.pop();\n\n // Mark the strategy as not supported\n strategies[_addr].isSupported = false;\n\n // Withdraw all assets\n IStrategy strategy = IStrategy(_addr);\n strategy.withdrawAll();\n\n emit StrategyRemoved(_addr);\n }\n }\n\n /***************************************\n Strategies\n ****************************************/\n\n /**\n * @notice Deposit multiple assets from the vault into the strategy.\n * @param _strategyToAddress Address of the Strategy to deposit assets into.\n * @param _assets Array of asset address that will be deposited into the strategy.\n * @param _amounts Array of amounts of each corresponding asset to deposit.\n */\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external onlyGovernorOrStrategist nonReentrant {\n _depositToStrategy(_strategyToAddress, _assets, _amounts);\n }\n\n function _depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal virtual {\n require(\n strategies[_strategyToAddress].isSupported,\n \"Invalid to Strategy\"\n );\n require(_assets.length == _amounts.length, \"Parameter length mismatch\");\n\n uint256 assetCount = _assets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n address assetAddr = _assets[i];\n require(\n IStrategy(_strategyToAddress).supportsAsset(assetAddr),\n \"Asset unsupported\"\n );\n // Send required amount of funds to the strategy\n IERC20(assetAddr).safeTransfer(_strategyToAddress, _amounts[i]);\n }\n\n // Deposit all the funds that have been sent to the strategy\n IStrategy(_strategyToAddress).depositAll();\n }\n\n /**\n * @notice Withdraw multiple assets from the strategy to the vault.\n * @param _strategyFromAddress Address of the Strategy to withdraw assets from.\n * @param _assets Array of asset address that will be withdrawn from the strategy.\n * @param _amounts Array of amounts of each corresponding asset to withdraw.\n */\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external onlyGovernorOrStrategist nonReentrant {\n _withdrawFromStrategy(\n address(this),\n _strategyFromAddress,\n _assets,\n _amounts\n );\n }\n\n /**\n * @param _recipient can either be a strategy or the Vault\n */\n function _withdrawFromStrategy(\n address _recipient,\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal virtual {\n require(\n strategies[_strategyFromAddress].isSupported,\n \"Invalid from Strategy\"\n );\n require(_assets.length == _amounts.length, \"Parameter length mismatch\");\n\n uint256 assetCount = _assets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n // Withdraw from Strategy to the recipient\n IStrategy(_strategyFromAddress).withdraw(\n _recipient,\n _assets[i],\n _amounts[i]\n );\n }\n }\n\n /**\n * @notice Sets the maximum allowable difference between\n * total supply and backing assets' value.\n */\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external onlyGovernor {\n maxSupplyDiff = _maxSupplyDiff;\n emit MaxSupplyDiffChanged(_maxSupplyDiff);\n }\n\n /**\n * @notice Sets the trusteeAddress that can receive a portion of yield.\n * Setting to the zero address disables this feature.\n */\n function setTrusteeAddress(address _address) external onlyGovernor {\n trusteeAddress = _address;\n emit TrusteeAddressChanged(_address);\n }\n\n /**\n * @notice Sets the TrusteeFeeBps to the percentage of yield that should be\n * received in basis points.\n */\n function setTrusteeFeeBps(uint256 _basis) external onlyGovernor {\n require(_basis <= 5000, \"basis cannot exceed 50%\");\n trusteeFeeBps = _basis;\n emit TrusteeFeeBpsChanged(_basis);\n }\n\n /**\n * @notice Set OToken Metapool strategy\n * @param _ousdMetaStrategy Address of OToken metapool strategy\n */\n function setOusdMetaStrategy(address _ousdMetaStrategy)\n external\n onlyGovernor\n {\n ousdMetaStrategy = _ousdMetaStrategy;\n emit OusdMetaStrategyUpdated(_ousdMetaStrategy);\n }\n\n /***************************************\n Pause\n ****************************************/\n\n /**\n * @notice Set the deposit paused flag to true to prevent rebasing.\n */\n function pauseRebase() external onlyGovernorOrStrategist {\n rebasePaused = true;\n emit RebasePaused();\n }\n\n /**\n * @notice Set the deposit paused flag to true to allow rebasing.\n */\n function unpauseRebase() external onlyGovernorOrStrategist {\n rebasePaused = false;\n emit RebaseUnpaused();\n }\n\n /**\n * @notice Set the deposit paused flag to true to prevent capital movement.\n */\n function pauseCapital() external onlyGovernorOrStrategist {\n capitalPaused = true;\n emit CapitalPaused();\n }\n\n /**\n * @notice Set the deposit paused flag to false to enable capital movement.\n */\n function unpauseCapital() external onlyGovernorOrStrategist {\n capitalPaused = false;\n emit CapitalUnpaused();\n }\n\n /***************************************\n Utils\n ****************************************/\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Only unsupported assets\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /***************************************\n Strategies Admin\n ****************************************/\n\n /**\n * @notice Withdraws all assets from the strategy and sends assets to the Vault.\n * @param _strategyAddr Strategy address.\n */\n function withdrawAllFromStrategy(address _strategyAddr)\n external\n onlyGovernorOrStrategist\n {\n _withdrawAllFromStrategy(_strategyAddr);\n }\n\n function _withdrawAllFromStrategy(address _strategyAddr) internal virtual {\n require(\n strategies[_strategyAddr].isSupported,\n \"Strategy is not supported\"\n );\n IStrategy strategy = IStrategy(_strategyAddr);\n strategy.withdrawAll();\n }\n\n /**\n * @notice Withdraws all assets from all the strategies and sends assets to the Vault.\n */\n function withdrawAllFromStrategies() external onlyGovernorOrStrategist {\n _withdrawAllFromStrategies();\n }\n\n function _withdrawAllFromStrategies() internal virtual {\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n IStrategy(allStrategies[i]).withdrawAll();\n }\n }\n\n /***************************************\n Utils\n ****************************************/\n\n function _cacheDecimals(address token) internal {\n Asset storage tokenAsset = assets[token];\n if (tokenAsset.decimals != 0) {\n return;\n }\n uint8 decimals = IBasicToken(token).decimals();\n require(decimals >= 6 && decimals <= 18, \"Unexpected precision\");\n tokenAsset.decimals = decimals;\n }\n}\n" + }, + "contracts/vault/VaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultCore contract\n * @notice The Vault contract stores assets. On a deposit, OTokens will be minted\n and sent to the depositor. On a withdrawal, OTokens will be burned and\n assets will be sent to the withdrawer. The Vault accepts deposits of\n interest from yield bearing strategies which will modify the supply\n of OTokens.\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IGetExchangeRateToken } from \"../interfaces/IGetExchangeRateToken.sol\";\n\nimport \"./VaultInitializer.sol\";\n\ncontract VaultCore is VaultInitializer {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n /// @dev max signed int\n uint256 internal constant MAX_INT = uint256(type(int256).max);\n\n /**\n * @dev Verifies that the rebasing is not paused.\n */\n modifier whenNotRebasePaused() {\n require(!rebasePaused, \"Rebasing paused\");\n _;\n }\n\n /**\n * @dev Verifies that the deposits are not paused.\n */\n modifier whenNotCapitalPaused() {\n require(!capitalPaused, \"Capital paused\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the AMO strategy.\n */\n modifier onlyOusdMetaStrategy() {\n require(\n msg.sender == ousdMetaStrategy,\n \"Caller is not the OUSD meta strategy\"\n );\n _;\n }\n\n /**\n * @notice Deposit a supported asset and mint OTokens.\n * @param _asset Address of the asset being deposited\n * @param _amount Amount of the asset being deposited\n * @param _minimumOusdAmount Minimum OTokens to mint\n */\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external whenNotCapitalPaused nonReentrant {\n _mint(_asset, _amount, _minimumOusdAmount);\n }\n\n /**\n * @dev Deposit a supported asset and mint OTokens.\n * @param _asset Address of the asset being deposited\n * @param _amount Amount of the asset being deposited\n * @param _minimumOusdAmount Minimum OTokens to mint\n */\n function _mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) internal virtual {\n require(assets[_asset].isSupported, \"Asset is not supported\");\n require(_amount > 0, \"Amount must be greater than 0\");\n\n uint256 units = _toUnits(_amount, _asset);\n uint256 unitPrice = _toUnitPrice(_asset, true);\n uint256 priceAdjustedDeposit = (units * unitPrice) / 1e18;\n\n if (_minimumOusdAmount > 0) {\n require(\n priceAdjustedDeposit >= _minimumOusdAmount,\n \"Mint amount lower than minimum\"\n );\n }\n\n emit Mint(msg.sender, priceAdjustedDeposit);\n\n // Rebase must happen before any transfers occur.\n if (priceAdjustedDeposit >= rebaseThreshold && !rebasePaused) {\n _rebase();\n }\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, priceAdjustedDeposit);\n\n // Transfer the deposited coins to the vault\n IERC20 asset = IERC20(_asset);\n asset.safeTransferFrom(msg.sender, address(this), _amount);\n\n if (priceAdjustedDeposit >= autoAllocateThreshold) {\n _allocate();\n }\n }\n\n /**\n * @notice Mint OTokens for a Metapool Strategy\n * @param _amount Amount of the asset being deposited\n *\n * Notice: can't use `nonReentrant` modifier since the `mint` function can\n * call `allocate`, and that can trigger `ConvexOUSDMetaStrategy` to call this function\n * while the execution of the `mint` has not yet completed -> causing a `nonReentrant` collision.\n *\n * Also important to understand is that this is a limitation imposed by the test suite.\n * Production / mainnet contracts should never be configured in a way where mint/redeem functions\n * that are moving funds between the Vault and end user wallets can influence strategies\n * utilizing this function.\n */\n function mintForStrategy(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n onlyOusdMetaStrategy\n {\n require(_amount < MAX_INT, \"Amount too high\");\n\n emit Mint(msg.sender, _amount);\n\n // safe to cast because of the require check at the beginning of the function\n netOusdMintedForStrategy += int256(_amount);\n\n require(\n abs(netOusdMintedForStrategy) < netOusdMintForStrategyThreshold,\n \"Minted ousd surpassed netOusdMintForStrategyThreshold.\"\n );\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, _amount);\n }\n\n // In memoriam\n\n /**\n * @notice Withdraw a supported asset and burn OTokens.\n * @param _amount Amount of OTokens to burn\n * @param _minimumUnitAmount Minimum stablecoin units to receive in return\n */\n function redeem(uint256 _amount, uint256 _minimumUnitAmount)\n external\n whenNotCapitalPaused\n nonReentrant\n {\n _redeem(_amount, _minimumUnitAmount);\n }\n\n /**\n * @notice Withdraw a supported asset and burn OTokens.\n * @param _amount Amount of OTokens to burn\n * @param _minimumUnitAmount Minimum stablecoin units to receive in return\n */\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n {\n // Calculate redemption outputs\n uint256[] memory outputs = _calculateRedeemOutputs(_amount);\n\n emit Redeem(msg.sender, _amount);\n\n // Send outputs\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n if (outputs[i] == 0) continue;\n\n address assetAddr = allAssets[i];\n\n if (IERC20(assetAddr).balanceOf(address(this)) >= outputs[i]) {\n // Use Vault funds first if sufficient\n IERC20(assetAddr).safeTransfer(msg.sender, outputs[i]);\n } else {\n address strategyAddr = assetDefaultStrategies[assetAddr];\n if (strategyAddr != address(0)) {\n // Nothing in Vault, but something in Strategy, send from there\n IStrategy strategy = IStrategy(strategyAddr);\n strategy.withdraw(msg.sender, assetAddr, outputs[i]);\n } else {\n // Cant find funds anywhere\n revert(\"Liquidity error\");\n }\n }\n }\n\n if (_minimumUnitAmount > 0) {\n uint256 unitTotal = 0;\n for (uint256 i = 0; i < outputs.length; ++i) {\n unitTotal += _toUnits(outputs[i], allAssets[i]);\n }\n require(\n unitTotal >= _minimumUnitAmount,\n \"Redeem amount lower than minimum\"\n );\n }\n\n oUSD.burn(msg.sender, _amount);\n\n _postRedeem(_amount);\n }\n\n function _postRedeem(uint256 _amount) internal {\n // Until we can prove that we won't affect the prices of our assets\n // by withdrawing them, this should be here.\n // It's possible that a strategy was off on its asset total, perhaps\n // a reward token sold for more or for less than anticipated.\n uint256 totalUnits = 0;\n if (_amount >= rebaseThreshold && !rebasePaused) {\n totalUnits = _rebase();\n } else {\n totalUnits = _totalValue();\n }\n\n // Check that the OTokens are backed by enough assets\n if (maxSupplyDiff > 0) {\n // If there are more outstanding withdrawal requests than assets in the vault and strategies\n // then the available assets will be negative and totalUnits will be rounded up to zero.\n // As we don't know the exact shortfall amount, we will reject all redeem and withdrawals\n require(totalUnits > 0, \"Too many outstanding requests\");\n\n // Allow a max difference of maxSupplyDiff% between\n // backing assets value and OUSD total supply\n uint256 diff = oUSD.totalSupply().divPrecisely(totalUnits);\n require(\n (diff > 1e18 ? diff - 1e18 : 1e18 - diff) <= maxSupplyDiff,\n \"Backing supply liquidity error\"\n );\n }\n }\n\n /**\n * @notice Burn OTokens for Metapool Strategy\n * @param _amount Amount of OUSD to burn\n *\n * @dev Notice: can't use `nonReentrant` modifier since the `redeem` function could\n * require withdrawal on `ConvexOUSDMetaStrategy` and that one can call `burnForStrategy`\n * while the execution of the `redeem` has not yet completed -> causing a `nonReentrant` collision.\n *\n * Also important to understand is that this is a limitation imposed by the test suite.\n * Production / mainnet contracts should never be configured in a way where mint/redeem functions\n * that are moving funds between the Vault and end user wallets can influence strategies\n * utilizing this function.\n */\n function burnForStrategy(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n onlyOusdMetaStrategy\n {\n require(_amount < MAX_INT, \"Amount too high\");\n\n emit Redeem(msg.sender, _amount);\n\n // safe to cast because of the require check at the beginning of the function\n netOusdMintedForStrategy -= int256(_amount);\n\n require(\n abs(netOusdMintedForStrategy) < netOusdMintForStrategyThreshold,\n \"Attempting to burn too much OUSD.\"\n );\n\n // Burn OTokens\n oUSD.burn(msg.sender, _amount);\n }\n\n /**\n * @notice Allocate unallocated funds on Vault to strategies.\n **/\n function allocate() external virtual whenNotCapitalPaused nonReentrant {\n _allocate();\n }\n\n /**\n * @dev Allocate unallocated funds on Vault to strategies.\n **/\n function _allocate() internal virtual {\n uint256 vaultValue = _totalValueInVault();\n // Nothing in vault to allocate\n if (vaultValue == 0) return;\n uint256 strategiesValue = _totalValueInStrategies();\n // We have a method that does the same as this, gas optimisation\n uint256 calculatedTotalValue = vaultValue + strategiesValue;\n\n // We want to maintain a buffer on the Vault so calculate a percentage\n // modifier to multiply each amount being allocated by to enforce the\n // vault buffer\n uint256 vaultBufferModifier;\n if (strategiesValue == 0) {\n // Nothing in Strategies, allocate 100% minus the vault buffer to\n // strategies\n vaultBufferModifier = uint256(1e18) - vaultBuffer;\n } else {\n vaultBufferModifier =\n (vaultBuffer * calculatedTotalValue) /\n vaultValue;\n if (1e18 > vaultBufferModifier) {\n // E.g. 1e18 - (1e17 * 10e18)/5e18 = 8e17\n // (5e18 * 8e17) / 1e18 = 4e18 allocated from Vault\n vaultBufferModifier = uint256(1e18) - vaultBufferModifier;\n } else {\n // We need to let the buffer fill\n return;\n }\n }\n if (vaultBufferModifier == 0) return;\n\n // Iterate over all assets in the Vault and allocate to the appropriate\n // strategy\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(allAssets[i]);\n uint256 assetBalance = asset.balanceOf(address(this));\n // No balance, nothing to do here\n if (assetBalance == 0) continue;\n\n // Multiply the balance by the vault buffer modifier and truncate\n // to the scale of the asset decimals\n uint256 allocateAmount = assetBalance.mulTruncate(\n vaultBufferModifier\n );\n\n address depositStrategyAddr = assetDefaultStrategies[\n address(asset)\n ];\n\n if (depositStrategyAddr != address(0) && allocateAmount > 0) {\n IStrategy strategy = IStrategy(depositStrategyAddr);\n // Transfer asset to Strategy and call deposit method to\n // mint or take required action\n asset.safeTransfer(address(strategy), allocateAmount);\n strategy.deposit(address(asset), allocateAmount);\n emit AssetAllocated(\n address(asset),\n depositStrategyAddr,\n allocateAmount\n );\n }\n }\n }\n\n /**\n * @notice Calculate the total value of assets held by the Vault and all\n * strategies and update the supply of OTokens.\n */\n function rebase() external virtual nonReentrant {\n _rebase();\n }\n\n /**\n * @dev Calculate the total value of assets held by the Vault and all\n * strategies and update the supply of OTokens, optionally sending a\n * portion of the yield to the trustee.\n * @return totalUnits Total balance of Vault in units\n */\n function _rebase() internal whenNotRebasePaused returns (uint256) {\n uint256 supply = oUSD.totalSupply();\n uint256 vaultValue = _totalValue();\n // If no supply yet, do not rebase\n if (supply == 0) {\n return vaultValue;\n }\n\n // Calculate yield and new supply\n (uint256 yield, uint256 targetRate) = _nextYield(supply, vaultValue);\n uint256 newSupply = supply + yield;\n // Only rebase upwards and if we have enough backing funds\n if (newSupply <= supply || newSupply > vaultValue) {\n return vaultValue;\n }\n\n rebasePerSecondTarget = uint64(_min(targetRate, type(uint64).max));\n lastRebase = uint64(block.timestamp); // Intentional cast\n\n // Fee collection on yield\n address _trusteeAddress = trusteeAddress; // gas savings\n uint256 fee = 0;\n if (_trusteeAddress != address(0)) {\n fee = (yield * trusteeFeeBps) / 1e4;\n if (fee > 0) {\n require(fee < yield, \"Fee must not be greater than yield\");\n oUSD.mint(_trusteeAddress, fee);\n }\n }\n emit YieldDistribution(_trusteeAddress, yield, fee);\n\n // Only ratchet OToken supply upwards\n // Final check uses latest totalSupply\n if (newSupply > oUSD.totalSupply()) {\n oUSD.changeSupply(newSupply);\n }\n return vaultValue;\n }\n\n /**\n * @notice Calculates the amount that would rebase at next rebase.\n * This is before any fees.\n * @return yield amount of expected yield\n */\n function previewYield() external view returns (uint256 yield) {\n (yield, ) = _nextYield(oUSD.totalSupply(), _totalValue());\n return yield;\n }\n\n function _nextYield(uint256 supply, uint256 vaultValue)\n internal\n view\n virtual\n returns (uint256 yield, uint256 targetRate)\n {\n uint256 nonRebasing = oUSD.nonRebasingSupply();\n uint256 rebasing = supply - nonRebasing;\n uint256 elapsed = block.timestamp - lastRebase;\n targetRate = rebasePerSecondTarget;\n\n if (\n elapsed == 0 || // Yield only once per block.\n rebasing == 0 || // No yield if there are no rebasing tokens to give it to.\n supply > vaultValue || // No yield if we do not have yield to give.\n block.timestamp >= type(uint64).max // No yield if we are too far in the future to calculate it correctly.\n ) {\n return (0, targetRate);\n }\n\n // Start with the full difference available\n yield = vaultValue - supply;\n\n // Cap via optional automatic duration smoothing\n uint256 _dripDuration = dripDuration;\n if (_dripDuration > 1) {\n // If we are able to sustain an increased drip rate for\n // double the duration, then increase the target drip rate\n targetRate = _max(targetRate, yield / (_dripDuration * 2));\n // If we cannot sustain the target rate any more,\n // then rebase what we can, and reduce the target\n targetRate = _min(targetRate, yield / _dripDuration);\n // drip at the new target rate\n yield = _min(yield, targetRate * elapsed);\n }\n\n // Cap per second. elapsed is not 1e18 denominated\n yield = _min(yield, (rebasing * elapsed * rebasePerSecondMax) / 1e18);\n\n // Cap at a hard max per rebase, to avoid long durations resulting in huge rebases\n yield = _min(yield, (rebasing * MAX_REBASE) / 1e18);\n\n return (yield, targetRate);\n }\n\n /**\n * @notice Determine the total value of assets held by the vault and its\n * strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function totalValue() external view virtual returns (uint256 value) {\n value = _totalValue();\n }\n\n /**\n * @dev Internal Calculate the total value of the assets held by the\n * vault and its strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValue() internal view virtual returns (uint256 value) {\n return _totalValueInVault() + _totalValueInStrategies();\n }\n\n /**\n * @dev Internal to calculate total value of all assets held in Vault.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInVault()\n internal\n view\n virtual\n returns (uint256 value)\n {\n uint256 assetCount = allAssets.length;\n for (uint256 y; y < assetCount; ++y) {\n address assetAddr = allAssets[y];\n uint256 balance = IERC20(assetAddr).balanceOf(address(this));\n if (balance > 0) {\n value += _toUnits(balance, assetAddr);\n }\n }\n }\n\n /**\n * @dev Internal to calculate total value of all assets held in Strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInStrategies() internal view returns (uint256 value) {\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n value = value + _totalValueInStrategy(allStrategies[i]);\n }\n }\n\n /**\n * @dev Internal to calculate total value of all assets held by strategy.\n * @param _strategyAddr Address of the strategy\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInStrategy(address _strategyAddr)\n internal\n view\n returns (uint256 value)\n {\n IStrategy strategy = IStrategy(_strategyAddr);\n uint256 assetCount = allAssets.length;\n for (uint256 y; y < assetCount; ++y) {\n address assetAddr = allAssets[y];\n if (strategy.supportsAsset(assetAddr)) {\n uint256 balance = strategy.checkBalance(assetAddr);\n if (balance > 0) {\n value += _toUnits(balance, assetAddr);\n }\n }\n }\n }\n\n /**\n * @notice Get the balance of an asset held in Vault and all strategies.\n * @param _asset Address of asset\n * @return uint256 Balance of asset in decimals of asset\n */\n function checkBalance(address _asset) external view returns (uint256) {\n return _checkBalance(_asset);\n }\n\n /**\n * @notice Get the balance of an asset held in Vault and all strategies.\n * @param _asset Address of asset\n * @return balance Balance of asset in decimals of asset\n */\n function _checkBalance(address _asset)\n internal\n view\n virtual\n returns (uint256 balance)\n {\n IERC20 asset = IERC20(_asset);\n balance = asset.balanceOf(address(this));\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n IStrategy strategy = IStrategy(allStrategies[i]);\n if (strategy.supportsAsset(_asset)) {\n balance = balance + strategy.checkBalance(_asset);\n }\n }\n }\n\n /**\n * @notice Calculate the outputs for a redeem function, i.e. the mix of\n * coins that will be returned\n */\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory)\n {\n return _calculateRedeemOutputs(_amount);\n }\n\n /**\n * @dev Calculate the outputs for a redeem function, i.e. the mix of\n * coins that will be returned.\n * @return outputs Array of amounts respective to the supported assets\n */\n function _calculateRedeemOutputs(uint256 _amount)\n internal\n view\n virtual\n returns (uint256[] memory outputs)\n {\n // We always give out coins in proportion to how many we have,\n // Now if all coins were the same value, this math would easy,\n // just take the percentage of each coin, and multiply by the\n // value to be given out. But if coins are worth more than $1,\n // then we would end up handing out too many coins. We need to\n // adjust by the total value of coins.\n //\n // To do this, we total up the value of our coins, by their\n // percentages. Then divide what we would otherwise give out by\n // this number.\n //\n // Let say we have 100 DAI at $1.06 and 200 USDT at $1.00.\n // So for every 1 DAI we give out, we'll be handing out 2 USDT\n // Our total output ratio is: 33% * 1.06 + 66% * 1.00 = 1.02\n //\n // So when calculating the output, we take the percentage of\n // each coin, times the desired output value, divided by the\n // totalOutputRatio.\n //\n // For example, withdrawing: 30 OUSD:\n // DAI 33% * 30 / 1.02 = 9.80 DAI\n // USDT = 66 % * 30 / 1.02 = 19.60 USDT\n //\n // Checking these numbers:\n // 9.80 DAI * 1.06 = $10.40\n // 19.60 USDT * 1.00 = $19.60\n //\n // And so the user gets $10.40 + $19.60 = $30 worth of value.\n\n uint256 assetCount = allAssets.length;\n uint256[] memory assetUnits = new uint256[](assetCount);\n uint256[] memory assetBalances = new uint256[](assetCount);\n outputs = new uint256[](assetCount);\n\n // Calculate redeem fee\n if (redeemFeeBps > 0) {\n uint256 redeemFee = _amount.mulTruncateScale(redeemFeeBps, 1e4);\n _amount = _amount - redeemFee;\n }\n\n // Calculate assets balances and decimals once,\n // for a large gas savings.\n uint256 totalUnits = 0;\n for (uint256 i = 0; i < assetCount; ++i) {\n address assetAddr = allAssets[i];\n uint256 balance = _checkBalance(assetAddr);\n assetBalances[i] = balance;\n assetUnits[i] = _toUnits(balance, assetAddr);\n totalUnits = totalUnits + assetUnits[i];\n }\n // Calculate totalOutputRatio\n uint256 totalOutputRatio = 0;\n for (uint256 i = 0; i < assetCount; ++i) {\n uint256 unitPrice = _toUnitPrice(allAssets[i], false);\n uint256 ratio = (assetUnits[i] * unitPrice) / totalUnits;\n totalOutputRatio = totalOutputRatio + ratio;\n }\n // Calculate final outputs\n uint256 factor = _amount.divPrecisely(totalOutputRatio);\n for (uint256 i = 0; i < assetCount; ++i) {\n outputs[i] = (assetBalances[i] * factor) / totalUnits;\n }\n }\n\n /***************************************\n Pricing\n ****************************************/\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * Never goes above 1, since that is how we price mints.\n * @param asset address of the asset\n * @return price uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed\n */\n function priceUnitMint(address asset)\n external\n view\n returns (uint256 price)\n {\n /* need to supply 1 asset unit in asset's decimals and can not just hard-code\n * to 1e18 and ignore calling `_toUnits` since we need to consider assets\n * with the exchange rate\n */\n uint256 units = _toUnits(\n uint256(1e18).scaleBy(_getDecimals(asset), 18),\n asset\n );\n price = (_toUnitPrice(asset, true) * units) / 1e18;\n }\n\n /**\n * @notice Returns the total price in 18 digit unit for a given asset.\n * Never goes below 1, since that is how we price redeems\n * @param asset Address of the asset\n * @return price uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed\n */\n function priceUnitRedeem(address asset)\n external\n view\n returns (uint256 price)\n {\n /* need to supply 1 asset unit in asset's decimals and can not just hard-code\n * to 1e18 and ignore calling `_toUnits` since we need to consider assets\n * with the exchange rate\n */\n uint256 units = _toUnits(\n uint256(1e18).scaleBy(_getDecimals(asset), 18),\n asset\n );\n price = (_toUnitPrice(asset, false) * units) / 1e18;\n }\n\n /***************************************\n Utils\n ****************************************/\n\n /**\n * @dev Convert a quantity of a token into 1e18 fixed decimal \"units\"\n * in the underlying base (USD/ETH) used by the vault.\n * Price is not taken into account, only quantity.\n *\n * Examples of this conversion:\n *\n * - 1e18 DAI becomes 1e18 units (same decimals)\n * - 1e6 USDC becomes 1e18 units (decimal conversion)\n * - 1e18 rETH becomes 1.2e18 units (exchange rate conversion)\n *\n * @param _raw Quantity of asset\n * @param _asset Core Asset address\n * @return value 1e18 normalized quantity of units\n */\n function _toUnits(uint256 _raw, address _asset)\n internal\n view\n returns (uint256)\n {\n UnitConversion conversion = assets[_asset].unitConversion;\n if (conversion == UnitConversion.DECIMALS) {\n return _raw.scaleBy(18, _getDecimals(_asset));\n } else if (conversion == UnitConversion.GETEXCHANGERATE) {\n uint256 exchangeRate = IGetExchangeRateToken(_asset)\n .getExchangeRate();\n return (_raw * exchangeRate) / 1e18;\n } else {\n revert(\"Unsupported conversion type\");\n }\n }\n\n /**\n * @dev Returns asset's unit price accounting for different asset types\n * and takes into account the context in which that price exists -\n * - mint or redeem.\n *\n * Note: since we are returning the price of the unit and not the one of the\n * asset (see comment above how 1 rETH exchanges for 1.2 units) we need\n * to make the Oracle price adjustment as well since we are pricing the\n * units and not the assets.\n *\n * The price also snaps to a \"full unit price\" in case a mint or redeem\n * action would be unfavourable to the protocol.\n *\n */\n function _toUnitPrice(address _asset, bool isMint)\n internal\n view\n returns (uint256 price)\n {\n UnitConversion conversion = assets[_asset].unitConversion;\n price = IOracle(priceProvider).price(_asset);\n\n if (conversion == UnitConversion.GETEXCHANGERATE) {\n uint256 exchangeRate = IGetExchangeRateToken(_asset)\n .getExchangeRate();\n price = (price * 1e18) / exchangeRate;\n } else if (conversion != UnitConversion.DECIMALS) {\n revert(\"Unsupported conversion type\");\n }\n\n /* At this stage the price is already adjusted to the unit\n * so the price checks are agnostic to underlying asset being\n * pegged to a USD or to an ETH or having a custom exchange rate.\n */\n require(price <= MAX_UNIT_PRICE_DRIFT, \"Vault: Price exceeds max\");\n require(price >= MIN_UNIT_PRICE_DRIFT, \"Vault: Price under min\");\n\n if (isMint) {\n /* Never price a normalized unit price for more than one\n * unit of OETH/OUSD when minting.\n */\n if (price > 1e18) {\n price = 1e18;\n }\n require(price >= MINT_MINIMUM_UNIT_PRICE, \"Asset price below peg\");\n } else {\n /* Never give out more than 1 normalized unit amount of assets\n * for one unit of OETH/OUSD when redeeming.\n */\n if (price < 1e18) {\n price = 1e18;\n }\n }\n }\n\n /**\n * @dev Get the number of decimals of a token asset\n * @param _asset Address of the asset\n * @return decimals number of decimals\n */\n function _getDecimals(address _asset)\n internal\n view\n returns (uint256 decimals)\n {\n decimals = assets[_asset].decimals;\n require(decimals > 0, \"Decimals not cached\");\n }\n\n /**\n * @notice Return the number of assets supported by the Vault.\n */\n function getAssetCount() public view returns (uint256) {\n return allAssets.length;\n }\n\n /**\n * @notice Gets the vault configuration of a supported asset.\n * @param _asset Address of the token asset\n */\n function getAssetConfig(address _asset)\n public\n view\n returns (Asset memory config)\n {\n config = assets[_asset];\n }\n\n /**\n * @notice Return all vault asset addresses in order\n */\n function getAllAssets() external view returns (address[] memory) {\n return allAssets;\n }\n\n /**\n * @notice Return the number of strategies active on the Vault.\n */\n function getStrategyCount() external view returns (uint256) {\n return allStrategies.length;\n }\n\n /**\n * @notice Return the array of all strategies\n */\n function getAllStrategies() external view returns (address[] memory) {\n return allStrategies;\n }\n\n /**\n * @notice Returns whether the vault supports the asset\n * @param _asset address of the asset\n * @return true if supported\n */\n function isSupportedAsset(address _asset) external view returns (bool) {\n return assets[_asset].isSupported;\n }\n\n function ADMIN_IMPLEMENTATION() external view returns (address adminImpl) {\n bytes32 slot = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n adminImpl := sload(slot)\n }\n }\n\n /**\n * @dev Falldown to the admin implementation\n * @notice This is a catch all for all functions not declared in core\n */\n // solhint-disable-next-line no-complex-fallback\n fallback() external {\n bytes32 slot = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(\n gas(),\n sload(slot),\n 0,\n calldatasize(),\n 0,\n 0\n )\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n function abs(int256 x) private pure returns (uint256) {\n require(x < int256(MAX_INT), \"Amount too high\");\n return x >= 0 ? uint256(x) : uint256(-x);\n }\n\n function _min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n function _max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a > b ? a : b;\n }\n}\n" + }, + "contracts/vault/VaultInitializer.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultInitializer contract\n * @notice The Vault contract initializes the vault.\n * @author Origin Protocol Inc\n */\n\nimport \"./VaultStorage.sol\";\n\ncontract VaultInitializer is VaultStorage {\n function initialize(address _priceProvider, address _oToken)\n external\n onlyGovernor\n initializer\n {\n require(_priceProvider != address(0), \"PriceProvider address is zero\");\n require(_oToken != address(0), \"oToken address is zero\");\n\n oUSD = OUSD(_oToken);\n\n priceProvider = _priceProvider;\n\n rebasePaused = false;\n capitalPaused = true;\n\n // Initial redeem fee of 0 basis points\n redeemFeeBps = 0;\n // Initial Vault buffer of 0%\n vaultBuffer = 0;\n // Initial allocate threshold of 25,000 OUSD\n autoAllocateThreshold = 25000e18;\n // Threshold for rebasing\n rebaseThreshold = 1000e18;\n // Initialize all strategies\n allStrategies = new address[](0);\n // Start with drip duration disabled\n dripDuration = 1;\n }\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + }, + "contracts/zapper/WOETHCCIPZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IRouterClient } from \"@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol\";\nimport { Client } from \"@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol\";\n// solhint-disable-next-line max-line-length\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"./../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IOETHZapper } from \"./../interfaces/IOETHZapper.sol\";\n\n/**\n * @title WOETH CCIP Zapper Contract\n * @notice Helps to directly convert ETH on mainnet into WOETH on L2s.\n * @author Origin Protocol Inc\n */\n\ncontract WOETHCCIPZapper {\n /**\n * @dev Event emitted when a zap occurs\n * @param messageId Unique message identifier for each zap\n * @param sender Address initiating the zap\n * @param recipient Recipient address at destination chain\n * @param amount Amount of ETH zapped\n */\n event Zap(\n bytes32 indexed messageId,\n address sender,\n address recipient,\n uint256 amount\n );\n\n // @dev Thrown when Zap amount is less than fee.\n error AmountLessThanFee();\n\n /**\n * @dev The destination chain selector\n */\n uint64 public immutable destinationChainSelector;\n\n /**\n * @dev The WOETH source chain (Mainnet)\n */\n IERC4626 public immutable woethOnSourceChain;\n\n /**\n * @dev The WOETH destination chain (Arbitrum)\n */\n IERC20 public immutable woethOnDestinationChain;\n\n /**\n * @dev The OETH zapper contract address\n */\n IOETHZapper public immutable oethZapper;\n\n /**\n * @dev The CCIP router contract address\n */\n IRouterClient public immutable ccipRouter;\n\n /**\n * @dev The OETH token contract address\n */\n IERC20 public immutable oeth;\n\n constructor(\n address _ccipRouter,\n uint64 _destinationChainSelector,\n IERC4626 _woethOnSourceChain,\n IERC20 _woethOnDestinationChain,\n IOETHZapper _oethZapper,\n IERC20 _oeth\n ) {\n ccipRouter = IRouterClient(_ccipRouter);\n destinationChainSelector = _destinationChainSelector;\n woethOnSourceChain = _woethOnSourceChain;\n woethOnDestinationChain = _woethOnDestinationChain;\n oethZapper = _oethZapper;\n oeth = _oeth;\n\n // Max allowance for Router and WOETH contracts\n _oeth.approve(address(_woethOnSourceChain), type(uint256).max); // for wrapping\n _woethOnSourceChain.approve(address(_ccipRouter), type(uint256).max); // for zapping\n }\n\n /**\n * @notice Accepts ETH, zaps for OETH, wraps it for WOETH and sends it to the destination chain (arbitrum)\n * @param receiver The address of the EOA on the destination chain\n * @return messageId The ID of the message that was sent\n */\n function zap(address receiver)\n external\n payable\n returns (bytes32 messageId)\n {\n return _zap(receiver, msg.value);\n }\n\n /**\n * @notice Used to estimate fee for CCIP transaction\n * @param amount The amount of ETH to be zapped\n * @param receiver The address of the EOA on the destination chain\n * @return feeAmount The CCIP tx fee in ETH.\n */\n\n function getFee(uint256 amount, address receiver)\n public\n view\n returns (uint256 feeAmount)\n {\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(woethOnSourceChain),\n amount: amount\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({\n receiver: abi.encode(receiver), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n feeAmount = ccipRouter.getFee(destinationChainSelector, message);\n }\n\n /**\n * @dev Deposit ETH and receive WOETH in L2.\n * @dev Note that the WOETH will be sent to the msg.sender at destination chain as well.\n */\n receive() external payable {\n _zap(msg.sender, msg.value);\n }\n\n function _zap(address receiver, uint256 amount)\n internal\n returns (bytes32 messageId)\n {\n // Estimate fee for zapping.\n uint256 feeAmount = getFee(amount, receiver);\n if (amount < feeAmount) {\n revert AmountLessThanFee();\n }\n\n // Convert only the msg.value - fees amount to WOETH.\n amount -= feeAmount;\n\n // 1.) Zap for OETH\n uint256 oethReceived = oethZapper.deposit{ value: amount }();\n\n // 2.) Wrap the received woeth\n uint256 woethReceived = woethOnSourceChain.deposit(\n oethReceived,\n address(this)\n );\n\n // 3.) Setup params for CCIP transfer\n\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(woethOnSourceChain),\n amount: woethReceived\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({\n receiver: abi.encode(receiver), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n // See: https://docs.chain.link/ccip/best-practices#setting-gaslimit\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n // ZAP ϟ\n //slither-disable-next-line arbitrary-send-eth\n messageId = ccipRouter.ccipSend{ value: feeAmount }(\n destinationChainSelector,\n message\n );\n\n // Emit Zap event with message details\n emit Zap(messageId, msg.sender, receiver, amount);\n\n // Return the message ID\n return messageId;\n }\n}\n" + }, + "hardhat/console.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.4.22 <0.9.0;\n\nlibrary console {\n address constant CONSOLE_ADDRESS =\n 0x000000000000000000636F6e736F6c652e6c6f67;\n\n function _sendLogPayloadImplementation(bytes memory payload) internal view {\n address consoleAddress = CONSOLE_ADDRESS;\n /// @solidity memory-safe-assembly\n assembly {\n pop(\n staticcall(\n gas(),\n consoleAddress,\n add(payload, 32),\n mload(payload),\n 0,\n 0\n )\n )\n }\n }\n\n function _castToPure(\n function(bytes memory) internal view fnIn\n ) internal pure returns (function(bytes memory) pure fnOut) {\n assembly {\n fnOut := fnIn\n }\n }\n\n function _sendLogPayload(bytes memory payload) internal pure {\n _castToPure(_sendLogPayloadImplementation)(payload);\n }\n\n function log() internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log()\"));\n }\n\n function logInt(int256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(int256)\", p0));\n }\n\n function logUint(uint256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256)\", p0));\n }\n\n function logString(string memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n }\n\n function logBool(bool p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n }\n\n function logAddress(address p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n }\n\n function logBytes(bytes memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes)\", p0));\n }\n\n function logBytes1(bytes1 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes1)\", p0));\n }\n\n function logBytes2(bytes2 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes2)\", p0));\n }\n\n function logBytes3(bytes3 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes3)\", p0));\n }\n\n function logBytes4(bytes4 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes4)\", p0));\n }\n\n function logBytes5(bytes5 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes5)\", p0));\n }\n\n function logBytes6(bytes6 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes6)\", p0));\n }\n\n function logBytes7(bytes7 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes7)\", p0));\n }\n\n function logBytes8(bytes8 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes8)\", p0));\n }\n\n function logBytes9(bytes9 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes9)\", p0));\n }\n\n function logBytes10(bytes10 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes10)\", p0));\n }\n\n function logBytes11(bytes11 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes11)\", p0));\n }\n\n function logBytes12(bytes12 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes12)\", p0));\n }\n\n function logBytes13(bytes13 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes13)\", p0));\n }\n\n function logBytes14(bytes14 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes14)\", p0));\n }\n\n function logBytes15(bytes15 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes15)\", p0));\n }\n\n function logBytes16(bytes16 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes16)\", p0));\n }\n\n function logBytes17(bytes17 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes17)\", p0));\n }\n\n function logBytes18(bytes18 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes18)\", p0));\n }\n\n function logBytes19(bytes19 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes19)\", p0));\n }\n\n function logBytes20(bytes20 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes20)\", p0));\n }\n\n function logBytes21(bytes21 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes21)\", p0));\n }\n\n function logBytes22(bytes22 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes22)\", p0));\n }\n\n function logBytes23(bytes23 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes23)\", p0));\n }\n\n function logBytes24(bytes24 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes24)\", p0));\n }\n\n function logBytes25(bytes25 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes25)\", p0));\n }\n\n function logBytes26(bytes26 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes26)\", p0));\n }\n\n function logBytes27(bytes27 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes27)\", p0));\n }\n\n function logBytes28(bytes28 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes28)\", p0));\n }\n\n function logBytes29(bytes29 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes29)\", p0));\n }\n\n function logBytes30(bytes30 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes30)\", p0));\n }\n\n function logBytes31(bytes31 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes31)\", p0));\n }\n\n function logBytes32(bytes32 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes32)\", p0));\n }\n\n function log(uint256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256)\", p0));\n }\n\n function log(string memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n }\n\n function log(bool p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n }\n\n function log(address p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n }\n\n function log(uint256 p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256)\", p0, p1));\n }\n\n function log(uint256 p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string)\", p0, p1));\n }\n\n function log(uint256 p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool)\", p0, p1));\n }\n\n function log(uint256 p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address)\", p0, p1));\n }\n\n function log(string memory p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256)\", p0, p1));\n }\n\n function log(string memory p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string)\", p0, p1));\n }\n\n function log(string memory p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool)\", p0, p1));\n }\n\n function log(string memory p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address)\", p0, p1));\n }\n\n function log(bool p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256)\", p0, p1));\n }\n\n function log(bool p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string)\", p0, p1));\n }\n\n function log(bool p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool)\", p0, p1));\n }\n\n function log(bool p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address)\", p0, p1));\n }\n\n function log(address p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256)\", p0, p1));\n }\n\n function log(address p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string)\", p0, p1));\n }\n\n function log(address p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool)\", p0, p1));\n }\n\n function log(address p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address)\", p0, p1));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,address)\", p0, p1, p2, p3));\n }\n}\n" + }, + "lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC4626 } from \"../../../../interfaces/IERC4626.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\n// From Open Zeppelin draft PR commit:\n// fac43034dca85ff539db3fc8aa2a7084b843d454\n// https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3171\n\nabstract contract ERC4626 is ERC20, IERC4626 {\n IERC20Metadata private immutable _asset;\n\n constructor(IERC20Metadata __asset) {\n _asset = __asset;\n }\n\n /** @dev See {IERC4262-asset} */\n function asset() public view virtual override returns (address) {\n return address(_asset);\n }\n\n /** @dev See {IERC4262-totalAssets} */\n function totalAssets() public view virtual override returns (uint256) {\n return _asset.balanceOf(address(this));\n }\n\n /**\n * @dev See {IERC4262-convertToShares}\n *\n * Will revert if asserts > 0, totalSupply > 0 and totalAssets = 0. That corresponds to a case where any asset\n * would represent an infinite amout of shares.\n */\n function convertToShares(uint256 assets) public view virtual override returns (uint256 shares) {\n uint256 supply = totalSupply();\n\n return\n (assets == 0 || supply == 0)\n ? (assets * 10**decimals()) / 10**_asset.decimals()\n : (assets * supply) / totalAssets();\n }\n\n /** @dev See {IERC4262-convertToAssets} */\n function convertToAssets(uint256 shares) public view virtual override returns (uint256 assets) {\n uint256 supply = totalSupply();\n\n return (supply == 0) ? (shares * 10**_asset.decimals()) / 10**decimals() : (shares * totalAssets()) / supply;\n }\n\n /** @dev See {IERC4262-maxDeposit} */\n function maxDeposit(address) public view virtual override returns (uint256) {\n return type(uint256).max;\n }\n\n /** @dev See {IERC4262-maxMint} */\n function maxMint(address) public view virtual override returns (uint256) {\n return type(uint256).max;\n }\n\n /** @dev See {IERC4262-maxWithdraw} */\n function maxWithdraw(address owner) public view virtual override returns (uint256) {\n return convertToAssets(balanceOf(owner));\n }\n\n /** @dev See {IERC4262-maxRedeem} */\n function maxRedeem(address owner) public view virtual override returns (uint256) {\n return balanceOf(owner);\n }\n\n /** @dev See {IERC4262-previewDeposit} */\n function previewDeposit(uint256 assets) public view virtual override returns (uint256) {\n return convertToShares(assets);\n }\n\n /** @dev See {IERC4262-previewMint} */\n function previewMint(uint256 shares) public view virtual override returns (uint256) {\n uint256 assets = convertToAssets(shares);\n return assets + (convertToShares(assets) < shares ? 1 : 0);\n }\n\n /** @dev See {IERC4262-previewWithdraw} */\n function previewWithdraw(uint256 assets) public view virtual override returns (uint256) {\n uint256 shares = convertToShares(assets);\n return shares + (convertToAssets(shares) < assets ? 1 : 0);\n }\n\n /** @dev See {IERC4262-previewRedeem} */\n function previewRedeem(uint256 shares) public view virtual override returns (uint256) {\n return convertToAssets(shares);\n }\n\n /** @dev See {IERC4262-deposit} */\n function deposit(uint256 assets, address receiver) public virtual override returns (uint256) {\n require(assets <= maxDeposit(receiver), \"ERC4626: deposit more then max\");\n\n address caller = _msgSender();\n uint256 shares = previewDeposit(assets);\n\n // if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through\n // the tokensToSend hook, so we need to transfer before we mint to keep the invariants.\n SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);\n _mint(receiver, shares);\n\n emit Deposit(caller, receiver, assets, shares);\n\n return shares;\n }\n\n /** @dev See {IERC4262-mint} */\n function mint(uint256 shares, address receiver) public virtual override returns (uint256) {\n require(shares <= maxMint(receiver), \"ERC4626: mint more then max\");\n\n address caller = _msgSender();\n uint256 assets = previewMint(shares);\n\n // if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through\n // the tokensToSend hook, so we need to transfer before we mint to keep the invariants.\n SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);\n _mint(receiver, shares);\n\n emit Deposit(caller, receiver, assets, shares);\n\n return assets;\n }\n\n /** @dev See {IERC4262-withdraw} */\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) public virtual override returns (uint256) {\n require(assets <= maxWithdraw(owner), \"ERC4626: withdraw more then max\");\n\n address caller = _msgSender();\n uint256 shares = previewWithdraw(assets);\n\n if (caller != owner) {\n _spendAllowance(owner, caller, shares);\n }\n\n // if _asset is ERC777, transfer can call reenter AFTER the transfer happens through\n // the tokensReceived hook, so we need to transfer after we burn to keep the invariants.\n _burn(owner, shares);\n SafeERC20.safeTransfer(_asset, receiver, assets);\n\n emit Withdraw(caller, receiver, owner, assets, shares);\n\n return shares;\n }\n\n /** @dev See {IERC4262-redeem} */\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) public virtual override returns (uint256) {\n require(shares <= maxRedeem(owner), \"ERC4626: redeem more then max\");\n\n address caller = _msgSender();\n uint256 assets = previewRedeem(shares);\n\n if (caller != owner) {\n _spendAllowance(owner, caller, shares);\n }\n\n // if _asset is ERC777, transfer can call reenter AFTER the transfer happens through\n // the tokensReceived hook, so we need to transfer after we burn to keep the invariants.\n _burn(owner, shares);\n SafeERC20.safeTransfer(_asset, receiver, assets);\n\n emit Withdraw(caller, receiver, owner, assets, shares);\n\n return assets;\n }\n\n // Included here, since this method was not yet present in\n // the version of Open Zeppelin ERC20 code we use.\n function _spendAllowance(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n uint256 currentAllowance = allowance(owner, spender);\n if (currentAllowance != type(uint256).max) {\n require(currentAllowance >= amount, \"ERC20: insufficient allowance\");\n unchecked {\n _approve(owner, spender, currentAllowance - amount);\n }\n }\n }\n}" + }, + "lib/openzeppelin/interfaces/IERC4626.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IERC4626 is IERC20, IERC20Metadata {\n event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);\n\n event Withdraw(\n address indexed caller,\n address indexed receiver,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n\n /**\n * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.\n *\n * - MUST be an ERC-20 token contract.\n * - MUST NOT revert.\n */\n function asset() external view returns (address assetTokenAddress);\n\n /**\n * @dev Returns the total amount of the underlying asset that is “managed” by Vault.\n *\n * - SHOULD include any compounding that occurs from yield.\n * - MUST be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT revert.\n */\n function totalAssets() external view returns (uint256 totalManagedAssets);\n\n /**\n * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal\n * scenario where all the conditions are met.\n *\n * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT show any variations depending on the caller.\n * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n * - MUST NOT revert.\n *\n * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n * from.\n */\n function convertToShares(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal\n * scenario where all the conditions are met.\n *\n * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT show any variations depending on the caller.\n * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n * - MUST NOT revert.\n *\n * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n * from.\n */\n function convertToAssets(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,\n * through a deposit call.\n *\n * - MUST return a limited value if receiver is subject to some deposit limit.\n * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.\n * - MUST NOT revert.\n */\n function maxDeposit(address receiver) external view returns (uint256 maxAssets);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given\n * current on-chain conditions.\n *\n * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit\n * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called\n * in the same transaction.\n * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the\n * deposit would be accepted, regardless if the user has enough tokens approved, etc.\n * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by depositing.\n */\n function previewDeposit(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.\n *\n * - MUST emit the Deposit event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * deposit execution, and are accounted for during deposit.\n * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not\n * approving enough underlying tokens to the Vault contract, etc).\n *\n * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n */\n function deposit(uint256 assets, address receiver) external returns (uint256 shares);\n\n /**\n * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.\n * - MUST return a limited value if receiver is subject to some mint limit.\n * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.\n * - MUST NOT revert.\n */\n function maxMint(address receiver) external view returns (uint256 maxShares);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given\n * current on-chain conditions.\n *\n * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call\n * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the\n * same transaction.\n * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint\n * would be accepted, regardless if the user has enough tokens approved, etc.\n * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by minting.\n */\n function previewMint(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.\n *\n * - MUST emit the Deposit event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint\n * execution, and are accounted for during mint.\n * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not\n * approving enough underlying tokens to the Vault contract, etc).\n *\n * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n */\n function mint(uint256 shares, address receiver) external returns (uint256 assets);\n\n /**\n * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the\n * Vault, through a withdraw call.\n *\n * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n * - MUST NOT revert.\n */\n function maxWithdraw(address owner) external view returns (uint256 maxAssets);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,\n * given current on-chain conditions.\n *\n * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw\n * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if\n * called\n * in the same transaction.\n * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though\n * the withdrawal would be accepted, regardless if the user has enough shares, etc.\n * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by depositing.\n */\n function previewWithdraw(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.\n *\n * - MUST emit the Withdraw event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * withdraw execution, and are accounted for during withdraw.\n * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner\n * not having enough shares, etc).\n *\n * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n * Those methods should be performed separately.\n */\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares);\n\n /**\n * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,\n * through a redeem call.\n *\n * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.\n * - MUST NOT revert.\n */\n function maxRedeem(address owner) external view returns (uint256 maxShares);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,\n * given current on-chain conditions.\n *\n * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call\n * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the\n * same transaction.\n * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the\n * redemption would be accepted, regardless if the user has enough shares, etc.\n * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by redeeming.\n */\n function previewRedeem(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.\n *\n * - MUST emit the Withdraw event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * redeem execution, and are accounted for during redeem.\n * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner\n * not having enough shares, etc).\n *\n * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n * Those methods should be performed separately.\n */\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets);\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.20;\n\nimport {Panic} from \"../Panic.sol\";\nimport {SafeCast} from \"./SafeCast.sol\";\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n enum Rounding {\n Floor, // Toward negative infinity\n Ceil, // Toward positive infinity\n Trunc, // Toward zero\n Expand // Away from zero\n }\n\n /**\n * @dev Return the 512-bit addition of two uint256.\n *\n * The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low.\n */\n function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {\n assembly (\"memory-safe\") {\n low := add(a, b)\n high := lt(low, a)\n }\n }\n\n /**\n * @dev Return the 512-bit multiplication of two uint256.\n *\n * The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low.\n */\n function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {\n // 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use\n // the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n // variables such that product = high * 2²⁵⁶ + low.\n assembly (\"memory-safe\") {\n let mm := mulmod(a, b, not(0))\n low := mul(a, b)\n high := sub(sub(mm, low), lt(mm, low))\n }\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, with a success flag (no overflow).\n */\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a + b;\n success = c >= a;\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).\n */\n function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a - b;\n success = c <= a;\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).\n */\n function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a * b;\n assembly (\"memory-safe\") {\n // Only true when the multiplication doesn't overflow\n // (c / a == b) || (a == 0)\n success := or(eq(div(c, a), b), iszero(a))\n }\n // equivalent to: success ? c : 0\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the division of two unsigned integers, with a success flag (no division by zero).\n */\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n success = b > 0;\n assembly (\"memory-safe\") {\n // The `DIV` opcode returns zero when the denominator is 0.\n result := div(a, b)\n }\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).\n */\n function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n success = b > 0;\n assembly (\"memory-safe\") {\n // The `MOD` opcode returns zero when the denominator is 0.\n result := mod(a, b)\n }\n }\n }\n\n /**\n * @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing.\n */\n function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {\n (bool success, uint256 result) = tryAdd(a, b);\n return ternary(success, result, type(uint256).max);\n }\n\n /**\n * @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.\n */\n function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {\n (, uint256 result) = trySub(a, b);\n return result;\n }\n\n /**\n * @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing.\n */\n function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {\n (bool success, uint256 result) = tryMul(a, b);\n return ternary(success, result, type(uint256).max);\n }\n\n /**\n * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.\n *\n * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.\n * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute\n * one branch when needed, making this function more expensive.\n */\n function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {\n unchecked {\n // branchless ternary works because:\n // b ^ (a ^ b) == a\n // b ^ 0 == b\n return b ^ ((a ^ b) * SafeCast.toUint(condition));\n }\n }\n\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return ternary(a > b, a, b);\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return ternary(a < b, a, b);\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds towards infinity instead\n * of rounding towards zero.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n if (b == 0) {\n // Guarantee the same behavior as in a regular Solidity division.\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n\n // The following calculation ensures accurate ceiling division without overflow.\n // Since a is non-zero, (a - 1) / b will not overflow.\n // The largest possible result occurs when (a - 1) / b is type(uint256).max,\n // but the largest value we can obtain is type(uint256).max - 1, which happens\n // when a = type(uint256).max and b = 1.\n unchecked {\n return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);\n }\n }\n\n /**\n * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or\n * denominator == 0.\n *\n * Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by\n * Uniswap Labs also under MIT license.\n */\n function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {\n unchecked {\n (uint256 high, uint256 low) = mul512(x, y);\n\n // Handle non-overflow cases, 256 by 256 division.\n if (high == 0) {\n // Solidity will revert if denominator == 0, unlike the div opcode on its own.\n // The surrounding unchecked block does not change this fact.\n // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.\n return low / denominator;\n }\n\n // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.\n if (denominator <= high) {\n Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));\n }\n\n ///////////////////////////////////////////////\n // 512 by 256 division.\n ///////////////////////////////////////////////\n\n // Make division exact by subtracting the remainder from [high low].\n uint256 remainder;\n assembly (\"memory-safe\") {\n // Compute remainder using mulmod.\n remainder := mulmod(x, y, denominator)\n\n // Subtract 256 bit number from 512 bit number.\n high := sub(high, gt(remainder, low))\n low := sub(low, remainder)\n }\n\n // Factor powers of two out of denominator and compute largest power of two divisor of denominator.\n // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.\n\n uint256 twos = denominator & (0 - denominator);\n assembly (\"memory-safe\") {\n // Divide denominator by twos.\n denominator := div(denominator, twos)\n\n // Divide [high low] by twos.\n low := div(low, twos)\n\n // Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.\n twos := add(div(sub(0, twos), twos), 1)\n }\n\n // Shift in bits from high into low.\n low |= high * twos;\n\n // Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such\n // that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for\n // four bits. That is, denominator * inv ≡ 1 mod 2⁴.\n uint256 inverse = (3 * denominator) ^ 2;\n\n // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also\n // works in modular arithmetic, doubling the correct bits in each step.\n inverse *= 2 - denominator * inverse; // inverse mod 2⁸\n inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶\n inverse *= 2 - denominator * inverse; // inverse mod 2³²\n inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴\n inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸\n inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶\n\n // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n // This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is\n // less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and high\n // is no longer required.\n result = low * inverse;\n return result;\n }\n }\n\n /**\n * @dev Calculates x * y / denominator with full precision, following the selected rounding direction.\n */\n function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {\n return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);\n }\n\n /**\n * @dev Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256.\n */\n function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) {\n unchecked {\n (uint256 high, uint256 low) = mul512(x, y);\n if (high >= 1 << n) {\n Panic.panic(Panic.UNDER_OVERFLOW);\n }\n return (high << (256 - n)) | (low >> n);\n }\n }\n\n /**\n * @dev Calculates x * y >> n with full precision, following the selected rounding direction.\n */\n function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) {\n return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 0);\n }\n\n /**\n * @dev Calculate the modular multiplicative inverse of a number in Z/nZ.\n *\n * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.\n * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.\n *\n * If the input value is not inversible, 0 is returned.\n *\n * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the\n * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.\n */\n function invMod(uint256 a, uint256 n) internal pure returns (uint256) {\n unchecked {\n if (n == 0) return 0;\n\n // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)\n // Used to compute integers x and y such that: ax + ny = gcd(a, n).\n // When the gcd is 1, then the inverse of a modulo n exists and it's x.\n // ax + ny = 1\n // ax = 1 + (-y)n\n // ax ≡ 1 (mod n) # x is the inverse of a modulo n\n\n // If the remainder is 0 the gcd is n right away.\n uint256 remainder = a % n;\n uint256 gcd = n;\n\n // Therefore the initial coefficients are:\n // ax + ny = gcd(a, n) = n\n // 0a + 1n = n\n int256 x = 0;\n int256 y = 1;\n\n while (remainder != 0) {\n uint256 quotient = gcd / remainder;\n\n (gcd, remainder) = (\n // The old remainder is the next gcd to try.\n remainder,\n // Compute the next remainder.\n // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd\n // where gcd is at most n (capped to type(uint256).max)\n gcd - remainder * quotient\n );\n\n (x, y) = (\n // Increment the coefficient of a.\n y,\n // Decrement the coefficient of n.\n // Can overflow, but the result is casted to uint256 so that the\n // next value of y is \"wrapped around\" to a value between 0 and n - 1.\n x - y * int256(quotient)\n );\n }\n\n if (gcd != 1) return 0; // No inverse exists.\n return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.\n }\n }\n\n /**\n * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.\n *\n * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is\n * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that\n * `a**(p-2)` is the modular multiplicative inverse of a in Fp.\n *\n * NOTE: this function does NOT check that `p` is a prime greater than `2`.\n */\n function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {\n unchecked {\n return Math.modExp(a, p - 2, p);\n }\n }\n\n /**\n * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)\n *\n * Requirements:\n * - modulus can't be zero\n * - underlying staticcall to precompile must succeed\n *\n * IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make\n * sure the chain you're using it on supports the precompiled contract for modular exponentiation\n * at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,\n * the underlying function will succeed given the lack of a revert, but the result may be incorrectly\n * interpreted as 0.\n */\n function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {\n (bool success, uint256 result) = tryModExp(b, e, m);\n if (!success) {\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n return result;\n }\n\n /**\n * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).\n * It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying\n * to operate modulo 0 or if the underlying precompile reverted.\n *\n * IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain\n * you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in\n * https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack\n * of a revert, but the result may be incorrectly interpreted as 0.\n */\n function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {\n if (m == 0) return (false, 0);\n assembly (\"memory-safe\") {\n let ptr := mload(0x40)\n // | Offset | Content | Content (Hex) |\n // |-----------|------------|--------------------------------------------------------------------|\n // | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x60:0x7f | value of b | 0x<.............................................................b> |\n // | 0x80:0x9f | value of e | 0x<.............................................................e> |\n // | 0xa0:0xbf | value of m | 0x<.............................................................m> |\n mstore(ptr, 0x20)\n mstore(add(ptr, 0x20), 0x20)\n mstore(add(ptr, 0x40), 0x20)\n mstore(add(ptr, 0x60), b)\n mstore(add(ptr, 0x80), e)\n mstore(add(ptr, 0xa0), m)\n\n // Given the result < m, it's guaranteed to fit in 32 bytes,\n // so we can use the memory scratch space located at offset 0.\n success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)\n result := mload(0x00)\n }\n }\n\n /**\n * @dev Variant of {modExp} that supports inputs of arbitrary length.\n */\n function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {\n (bool success, bytes memory result) = tryModExp(b, e, m);\n if (!success) {\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n return result;\n }\n\n /**\n * @dev Variant of {tryModExp} that supports inputs of arbitrary length.\n */\n function tryModExp(\n bytes memory b,\n bytes memory e,\n bytes memory m\n ) internal view returns (bool success, bytes memory result) {\n if (_zeroBytes(m)) return (false, new bytes(0));\n\n uint256 mLen = m.length;\n\n // Encode call args in result and move the free memory pointer\n result = abi.encodePacked(b.length, e.length, mLen, b, e, m);\n\n assembly (\"memory-safe\") {\n let dataPtr := add(result, 0x20)\n // Write result on top of args to avoid allocating extra memory.\n success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)\n // Overwrite the length.\n // result.length > returndatasize() is guaranteed because returndatasize() == m.length\n mstore(result, mLen)\n // Set the memory pointer after the returned data.\n mstore(0x40, add(dataPtr, mLen))\n }\n }\n\n /**\n * @dev Returns whether the provided byte array is zero.\n */\n function _zeroBytes(bytes memory byteArray) private pure returns (bool) {\n for (uint256 i = 0; i < byteArray.length; ++i) {\n if (byteArray[i] != 0) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded\n * towards zero.\n *\n * This method is based on Newton's method for computing square roots; the algorithm is restricted to only\n * using integer operations.\n */\n function sqrt(uint256 a) internal pure returns (uint256) {\n unchecked {\n // Take care of easy edge cases when a == 0 or a == 1\n if (a <= 1) {\n return a;\n }\n\n // In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a\n // sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between\n // the current value as `ε_n = | x_n - sqrt(a) |`.\n //\n // For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root\n // of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is\n // bigger than any uint256.\n //\n // By noticing that\n // `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`\n // we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar\n // to the msb function.\n uint256 aa = a;\n uint256 xn = 1;\n\n if (aa >= (1 << 128)) {\n aa >>= 128;\n xn <<= 64;\n }\n if (aa >= (1 << 64)) {\n aa >>= 64;\n xn <<= 32;\n }\n if (aa >= (1 << 32)) {\n aa >>= 32;\n xn <<= 16;\n }\n if (aa >= (1 << 16)) {\n aa >>= 16;\n xn <<= 8;\n }\n if (aa >= (1 << 8)) {\n aa >>= 8;\n xn <<= 4;\n }\n if (aa >= (1 << 4)) {\n aa >>= 4;\n xn <<= 2;\n }\n if (aa >= (1 << 2)) {\n xn <<= 1;\n }\n\n // We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).\n //\n // We can refine our estimation by noticing that the middle of that interval minimizes the error.\n // If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).\n // This is going to be our x_0 (and ε_0)\n xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)\n\n // From here, Newton's method give us:\n // x_{n+1} = (x_n + a / x_n) / 2\n //\n // One should note that:\n // x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a\n // = ((x_n² + a) / (2 * x_n))² - a\n // = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a\n // = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)\n // = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)\n // = (x_n² - a)² / (2 * x_n)²\n // = ((x_n² - a) / (2 * x_n))²\n // ≥ 0\n // Which proves that for all n ≥ 1, sqrt(a) ≤ x_n\n //\n // This gives us the proof of quadratic convergence of the sequence:\n // ε_{n+1} = | x_{n+1} - sqrt(a) |\n // = | (x_n + a / x_n) / 2 - sqrt(a) |\n // = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |\n // = | (x_n - sqrt(a))² / (2 * x_n) |\n // = | ε_n² / (2 * x_n) |\n // = ε_n² / | (2 * x_n) |\n //\n // For the first iteration, we have a special case where x_0 is known:\n // ε_1 = ε_0² / | (2 * x_0) |\n // ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))\n // ≤ 2**(2*e-4) / (3 * 2**(e-1))\n // ≤ 2**(e-3) / 3\n // ≤ 2**(e-3-log2(3))\n // ≤ 2**(e-4.5)\n //\n // For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:\n // ε_{n+1} = ε_n² / | (2 * x_n) |\n // ≤ (2**(e-k))² / (2 * 2**(e-1))\n // ≤ 2**(2*e-2*k) / 2**e\n // ≤ 2**(e-2*k)\n xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above\n xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5\n xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9\n xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18\n xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36\n xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72\n\n // Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision\n // ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either\n // sqrt(a) or sqrt(a) + 1.\n return xn - SafeCast.toUint(xn > a / xn);\n }\n }\n\n /**\n * @dev Calculates sqrt(a), following the selected rounding direction.\n */\n function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = sqrt(a);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);\n }\n }\n\n /**\n * @dev Return the log in base 2 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n */\n function log2(uint256 x) internal pure returns (uint256 r) {\n // If value has upper 128 bits set, log2 result is at least 128\n r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;\n // If upper 64 bits of 128-bit half set, add 64 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;\n // If upper 32 bits of 64-bit half set, add 32 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;\n // If upper 16 bits of 32-bit half set, add 16 to result\n r |= SafeCast.toUint((x >> r) > 0xffff) << 4;\n // If upper 8 bits of 16-bit half set, add 8 to result\n r |= SafeCast.toUint((x >> r) > 0xff) << 3;\n // If upper 4 bits of 8-bit half set, add 4 to result\n r |= SafeCast.toUint((x >> r) > 0xf) << 2;\n\n // Shifts value right by the current result and use it as an index into this lookup table:\n //\n // | x (4 bits) | index | table[index] = MSB position |\n // |------------|---------|-----------------------------|\n // | 0000 | 0 | table[0] = 0 |\n // | 0001 | 1 | table[1] = 0 |\n // | 0010 | 2 | table[2] = 1 |\n // | 0011 | 3 | table[3] = 1 |\n // | 0100 | 4 | table[4] = 2 |\n // | 0101 | 5 | table[5] = 2 |\n // | 0110 | 6 | table[6] = 2 |\n // | 0111 | 7 | table[7] = 2 |\n // | 1000 | 8 | table[8] = 3 |\n // | 1001 | 9 | table[9] = 3 |\n // | 1010 | 10 | table[10] = 3 |\n // | 1011 | 11 | table[11] = 3 |\n // | 1100 | 12 | table[12] = 3 |\n // | 1101 | 13 | table[13] = 3 |\n // | 1110 | 14 | table[14] = 3 |\n // | 1111 | 15 | table[15] = 3 |\n //\n // The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.\n assembly (\"memory-safe\") {\n r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))\n }\n }\n\n /**\n * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log2(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);\n }\n }\n\n /**\n * @dev Return the log in base 10 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n */\n function log10(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >= 10 ** 64) {\n value /= 10 ** 64;\n result += 64;\n }\n if (value >= 10 ** 32) {\n value /= 10 ** 32;\n result += 32;\n }\n if (value >= 10 ** 16) {\n value /= 10 ** 16;\n result += 16;\n }\n if (value >= 10 ** 8) {\n value /= 10 ** 8;\n result += 8;\n }\n if (value >= 10 ** 4) {\n value /= 10 ** 4;\n result += 4;\n }\n if (value >= 10 ** 2) {\n value /= 10 ** 2;\n result += 2;\n }\n if (value >= 10 ** 1) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log10(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);\n }\n }\n\n /**\n * @dev Return the log in base 256 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n *\n * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n */\n function log256(uint256 x) internal pure returns (uint256 r) {\n // If value has upper 128 bits set, log2 result is at least 128\n r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;\n // If upper 64 bits of 128-bit half set, add 64 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;\n // If upper 32 bits of 64-bit half set, add 32 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;\n // If upper 16 bits of 32-bit half set, add 16 to result\n r |= SafeCast.toUint((x >> r) > 0xffff) << 4;\n // Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8\n return (r >> 3) | SafeCast.toUint((x >> r) > 0xff);\n }\n\n /**\n * @dev Return the log in base 256, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log256(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);\n }\n }\n\n /**\n * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.\n */\n function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {\n return uint8(rounding) % 2 == 1;\n }\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)\n// This file was procedurally generated from scripts/generate/templates/SafeCast.js.\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n */\nlibrary SafeCast {\n /**\n * @dev Value doesn't fit in an uint of `bits` size.\n */\n error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);\n\n /**\n * @dev An int value doesn't fit in an uint of `bits` size.\n */\n error SafeCastOverflowedIntToUint(int256 value);\n\n /**\n * @dev Value doesn't fit in an int of `bits` size.\n */\n error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);\n\n /**\n * @dev An uint value doesn't fit in an int of `bits` size.\n */\n error SafeCastOverflowedUintToInt(uint256 value);\n\n /**\n * @dev Returns the downcasted uint248 from uint256, reverting on\n * overflow (when the input is greater than largest uint248).\n *\n * Counterpart to Solidity's `uint248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n */\n function toUint248(uint256 value) internal pure returns (uint248) {\n if (value > type(uint248).max) {\n revert SafeCastOverflowedUintDowncast(248, value);\n }\n return uint248(value);\n }\n\n /**\n * @dev Returns the downcasted uint240 from uint256, reverting on\n * overflow (when the input is greater than largest uint240).\n *\n * Counterpart to Solidity's `uint240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n */\n function toUint240(uint256 value) internal pure returns (uint240) {\n if (value > type(uint240).max) {\n revert SafeCastOverflowedUintDowncast(240, value);\n }\n return uint240(value);\n }\n\n /**\n * @dev Returns the downcasted uint232 from uint256, reverting on\n * overflow (when the input is greater than largest uint232).\n *\n * Counterpart to Solidity's `uint232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n */\n function toUint232(uint256 value) internal pure returns (uint232) {\n if (value > type(uint232).max) {\n revert SafeCastOverflowedUintDowncast(232, value);\n }\n return uint232(value);\n }\n\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n if (value > type(uint224).max) {\n revert SafeCastOverflowedUintDowncast(224, value);\n }\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint216 from uint256, reverting on\n * overflow (when the input is greater than largest uint216).\n *\n * Counterpart to Solidity's `uint216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n */\n function toUint216(uint256 value) internal pure returns (uint216) {\n if (value > type(uint216).max) {\n revert SafeCastOverflowedUintDowncast(216, value);\n }\n return uint216(value);\n }\n\n /**\n * @dev Returns the downcasted uint208 from uint256, reverting on\n * overflow (when the input is greater than largest uint208).\n *\n * Counterpart to Solidity's `uint208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n */\n function toUint208(uint256 value) internal pure returns (uint208) {\n if (value > type(uint208).max) {\n revert SafeCastOverflowedUintDowncast(208, value);\n }\n return uint208(value);\n }\n\n /**\n * @dev Returns the downcasted uint200 from uint256, reverting on\n * overflow (when the input is greater than largest uint200).\n *\n * Counterpart to Solidity's `uint200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n */\n function toUint200(uint256 value) internal pure returns (uint200) {\n if (value > type(uint200).max) {\n revert SafeCastOverflowedUintDowncast(200, value);\n }\n return uint200(value);\n }\n\n /**\n * @dev Returns the downcasted uint192 from uint256, reverting on\n * overflow (when the input is greater than largest uint192).\n *\n * Counterpart to Solidity's `uint192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n */\n function toUint192(uint256 value) internal pure returns (uint192) {\n if (value > type(uint192).max) {\n revert SafeCastOverflowedUintDowncast(192, value);\n }\n return uint192(value);\n }\n\n /**\n * @dev Returns the downcasted uint184 from uint256, reverting on\n * overflow (when the input is greater than largest uint184).\n *\n * Counterpart to Solidity's `uint184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n */\n function toUint184(uint256 value) internal pure returns (uint184) {\n if (value > type(uint184).max) {\n revert SafeCastOverflowedUintDowncast(184, value);\n }\n return uint184(value);\n }\n\n /**\n * @dev Returns the downcasted uint176 from uint256, reverting on\n * overflow (when the input is greater than largest uint176).\n *\n * Counterpart to Solidity's `uint176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n */\n function toUint176(uint256 value) internal pure returns (uint176) {\n if (value > type(uint176).max) {\n revert SafeCastOverflowedUintDowncast(176, value);\n }\n return uint176(value);\n }\n\n /**\n * @dev Returns the downcasted uint168 from uint256, reverting on\n * overflow (when the input is greater than largest uint168).\n *\n * Counterpart to Solidity's `uint168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n */\n function toUint168(uint256 value) internal pure returns (uint168) {\n if (value > type(uint168).max) {\n revert SafeCastOverflowedUintDowncast(168, value);\n }\n return uint168(value);\n }\n\n /**\n * @dev Returns the downcasted uint160 from uint256, reverting on\n * overflow (when the input is greater than largest uint160).\n *\n * Counterpart to Solidity's `uint160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n */\n function toUint160(uint256 value) internal pure returns (uint160) {\n if (value > type(uint160).max) {\n revert SafeCastOverflowedUintDowncast(160, value);\n }\n return uint160(value);\n }\n\n /**\n * @dev Returns the downcasted uint152 from uint256, reverting on\n * overflow (when the input is greater than largest uint152).\n *\n * Counterpart to Solidity's `uint152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n */\n function toUint152(uint256 value) internal pure returns (uint152) {\n if (value > type(uint152).max) {\n revert SafeCastOverflowedUintDowncast(152, value);\n }\n return uint152(value);\n }\n\n /**\n * @dev Returns the downcasted uint144 from uint256, reverting on\n * overflow (when the input is greater than largest uint144).\n *\n * Counterpart to Solidity's `uint144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n */\n function toUint144(uint256 value) internal pure returns (uint144) {\n if (value > type(uint144).max) {\n revert SafeCastOverflowedUintDowncast(144, value);\n }\n return uint144(value);\n }\n\n /**\n * @dev Returns the downcasted uint136 from uint256, reverting on\n * overflow (when the input is greater than largest uint136).\n *\n * Counterpart to Solidity's `uint136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n */\n function toUint136(uint256 value) internal pure returns (uint136) {\n if (value > type(uint136).max) {\n revert SafeCastOverflowedUintDowncast(136, value);\n }\n return uint136(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n if (value > type(uint128).max) {\n revert SafeCastOverflowedUintDowncast(128, value);\n }\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint120 from uint256, reverting on\n * overflow (when the input is greater than largest uint120).\n *\n * Counterpart to Solidity's `uint120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n */\n function toUint120(uint256 value) internal pure returns (uint120) {\n if (value > type(uint120).max) {\n revert SafeCastOverflowedUintDowncast(120, value);\n }\n return uint120(value);\n }\n\n /**\n * @dev Returns the downcasted uint112 from uint256, reverting on\n * overflow (when the input is greater than largest uint112).\n *\n * Counterpart to Solidity's `uint112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n */\n function toUint112(uint256 value) internal pure returns (uint112) {\n if (value > type(uint112).max) {\n revert SafeCastOverflowedUintDowncast(112, value);\n }\n return uint112(value);\n }\n\n /**\n * @dev Returns the downcasted uint104 from uint256, reverting on\n * overflow (when the input is greater than largest uint104).\n *\n * Counterpart to Solidity's `uint104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n */\n function toUint104(uint256 value) internal pure returns (uint104) {\n if (value > type(uint104).max) {\n revert SafeCastOverflowedUintDowncast(104, value);\n }\n return uint104(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n if (value > type(uint96).max) {\n revert SafeCastOverflowedUintDowncast(96, value);\n }\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint88 from uint256, reverting on\n * overflow (when the input is greater than largest uint88).\n *\n * Counterpart to Solidity's `uint88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n */\n function toUint88(uint256 value) internal pure returns (uint88) {\n if (value > type(uint88).max) {\n revert SafeCastOverflowedUintDowncast(88, value);\n }\n return uint88(value);\n }\n\n /**\n * @dev Returns the downcasted uint80 from uint256, reverting on\n * overflow (when the input is greater than largest uint80).\n *\n * Counterpart to Solidity's `uint80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n */\n function toUint80(uint256 value) internal pure returns (uint80) {\n if (value > type(uint80).max) {\n revert SafeCastOverflowedUintDowncast(80, value);\n }\n return uint80(value);\n }\n\n /**\n * @dev Returns the downcasted uint72 from uint256, reverting on\n * overflow (when the input is greater than largest uint72).\n *\n * Counterpart to Solidity's `uint72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n */\n function toUint72(uint256 value) internal pure returns (uint72) {\n if (value > type(uint72).max) {\n revert SafeCastOverflowedUintDowncast(72, value);\n }\n return uint72(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n if (value > type(uint64).max) {\n revert SafeCastOverflowedUintDowncast(64, value);\n }\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint56 from uint256, reverting on\n * overflow (when the input is greater than largest uint56).\n *\n * Counterpart to Solidity's `uint56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n */\n function toUint56(uint256 value) internal pure returns (uint56) {\n if (value > type(uint56).max) {\n revert SafeCastOverflowedUintDowncast(56, value);\n }\n return uint56(value);\n }\n\n /**\n * @dev Returns the downcasted uint48 from uint256, reverting on\n * overflow (when the input is greater than largest uint48).\n *\n * Counterpart to Solidity's `uint48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n */\n function toUint48(uint256 value) internal pure returns (uint48) {\n if (value > type(uint48).max) {\n revert SafeCastOverflowedUintDowncast(48, value);\n }\n return uint48(value);\n }\n\n /**\n * @dev Returns the downcasted uint40 from uint256, reverting on\n * overflow (when the input is greater than largest uint40).\n *\n * Counterpart to Solidity's `uint40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n */\n function toUint40(uint256 value) internal pure returns (uint40) {\n if (value > type(uint40).max) {\n revert SafeCastOverflowedUintDowncast(40, value);\n }\n return uint40(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n if (value > type(uint32).max) {\n revert SafeCastOverflowedUintDowncast(32, value);\n }\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint24 from uint256, reverting on\n * overflow (when the input is greater than largest uint24).\n *\n * Counterpart to Solidity's `uint24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n */\n function toUint24(uint256 value) internal pure returns (uint24) {\n if (value > type(uint24).max) {\n revert SafeCastOverflowedUintDowncast(24, value);\n }\n return uint24(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n if (value > type(uint16).max) {\n revert SafeCastOverflowedUintDowncast(16, value);\n }\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n if (value > type(uint8).max) {\n revert SafeCastOverflowedUintDowncast(8, value);\n }\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n if (value < 0) {\n revert SafeCastOverflowedIntToUint(value);\n }\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int248 from int256, reverting on\n * overflow (when the input is less than smallest int248 or\n * greater than largest int248).\n *\n * Counterpart to Solidity's `int248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n */\n function toInt248(int256 value) internal pure returns (int248 downcasted) {\n downcasted = int248(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(248, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int240 from int256, reverting on\n * overflow (when the input is less than smallest int240 or\n * greater than largest int240).\n *\n * Counterpart to Solidity's `int240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n */\n function toInt240(int256 value) internal pure returns (int240 downcasted) {\n downcasted = int240(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(240, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int232 from int256, reverting on\n * overflow (when the input is less than smallest int232 or\n * greater than largest int232).\n *\n * Counterpart to Solidity's `int232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n */\n function toInt232(int256 value) internal pure returns (int232 downcasted) {\n downcasted = int232(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(232, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int224 from int256, reverting on\n * overflow (when the input is less than smallest int224 or\n * greater than largest int224).\n *\n * Counterpart to Solidity's `int224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toInt224(int256 value) internal pure returns (int224 downcasted) {\n downcasted = int224(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(224, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int216 from int256, reverting on\n * overflow (when the input is less than smallest int216 or\n * greater than largest int216).\n *\n * Counterpart to Solidity's `int216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n */\n function toInt216(int256 value) internal pure returns (int216 downcasted) {\n downcasted = int216(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(216, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int208 from int256, reverting on\n * overflow (when the input is less than smallest int208 or\n * greater than largest int208).\n *\n * Counterpart to Solidity's `int208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n */\n function toInt208(int256 value) internal pure returns (int208 downcasted) {\n downcasted = int208(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(208, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int200 from int256, reverting on\n * overflow (when the input is less than smallest int200 or\n * greater than largest int200).\n *\n * Counterpart to Solidity's `int200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n */\n function toInt200(int256 value) internal pure returns (int200 downcasted) {\n downcasted = int200(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(200, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int192 from int256, reverting on\n * overflow (when the input is less than smallest int192 or\n * greater than largest int192).\n *\n * Counterpart to Solidity's `int192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n */\n function toInt192(int256 value) internal pure returns (int192 downcasted) {\n downcasted = int192(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(192, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int184 from int256, reverting on\n * overflow (when the input is less than smallest int184 or\n * greater than largest int184).\n *\n * Counterpart to Solidity's `int184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n */\n function toInt184(int256 value) internal pure returns (int184 downcasted) {\n downcasted = int184(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(184, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int176 from int256, reverting on\n * overflow (when the input is less than smallest int176 or\n * greater than largest int176).\n *\n * Counterpart to Solidity's `int176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n */\n function toInt176(int256 value) internal pure returns (int176 downcasted) {\n downcasted = int176(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(176, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int168 from int256, reverting on\n * overflow (when the input is less than smallest int168 or\n * greater than largest int168).\n *\n * Counterpart to Solidity's `int168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n */\n function toInt168(int256 value) internal pure returns (int168 downcasted) {\n downcasted = int168(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(168, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int160 from int256, reverting on\n * overflow (when the input is less than smallest int160 or\n * greater than largest int160).\n *\n * Counterpart to Solidity's `int160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n */\n function toInt160(int256 value) internal pure returns (int160 downcasted) {\n downcasted = int160(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(160, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int152 from int256, reverting on\n * overflow (when the input is less than smallest int152 or\n * greater than largest int152).\n *\n * Counterpart to Solidity's `int152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n */\n function toInt152(int256 value) internal pure returns (int152 downcasted) {\n downcasted = int152(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(152, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int144 from int256, reverting on\n * overflow (when the input is less than smallest int144 or\n * greater than largest int144).\n *\n * Counterpart to Solidity's `int144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n */\n function toInt144(int256 value) internal pure returns (int144 downcasted) {\n downcasted = int144(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(144, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int136 from int256, reverting on\n * overflow (when the input is less than smallest int136 or\n * greater than largest int136).\n *\n * Counterpart to Solidity's `int136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n */\n function toInt136(int256 value) internal pure returns (int136 downcasted) {\n downcasted = int136(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(136, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toInt128(int256 value) internal pure returns (int128 downcasted) {\n downcasted = int128(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(128, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int120 from int256, reverting on\n * overflow (when the input is less than smallest int120 or\n * greater than largest int120).\n *\n * Counterpart to Solidity's `int120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n */\n function toInt120(int256 value) internal pure returns (int120 downcasted) {\n downcasted = int120(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(120, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int112 from int256, reverting on\n * overflow (when the input is less than smallest int112 or\n * greater than largest int112).\n *\n * Counterpart to Solidity's `int112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n */\n function toInt112(int256 value) internal pure returns (int112 downcasted) {\n downcasted = int112(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(112, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int104 from int256, reverting on\n * overflow (when the input is less than smallest int104 or\n * greater than largest int104).\n *\n * Counterpart to Solidity's `int104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n */\n function toInt104(int256 value) internal pure returns (int104 downcasted) {\n downcasted = int104(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(104, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int96 from int256, reverting on\n * overflow (when the input is less than smallest int96 or\n * greater than largest int96).\n *\n * Counterpart to Solidity's `int96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toInt96(int256 value) internal pure returns (int96 downcasted) {\n downcasted = int96(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(96, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int88 from int256, reverting on\n * overflow (when the input is less than smallest int88 or\n * greater than largest int88).\n *\n * Counterpart to Solidity's `int88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n */\n function toInt88(int256 value) internal pure returns (int88 downcasted) {\n downcasted = int88(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(88, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int80 from int256, reverting on\n * overflow (when the input is less than smallest int80 or\n * greater than largest int80).\n *\n * Counterpart to Solidity's `int80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n */\n function toInt80(int256 value) internal pure returns (int80 downcasted) {\n downcasted = int80(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(80, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int72 from int256, reverting on\n * overflow (when the input is less than smallest int72 or\n * greater than largest int72).\n *\n * Counterpart to Solidity's `int72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n */\n function toInt72(int256 value) internal pure returns (int72 downcasted) {\n downcasted = int72(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(72, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toInt64(int256 value) internal pure returns (int64 downcasted) {\n downcasted = int64(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(64, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int56 from int256, reverting on\n * overflow (when the input is less than smallest int56 or\n * greater than largest int56).\n *\n * Counterpart to Solidity's `int56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n */\n function toInt56(int256 value) internal pure returns (int56 downcasted) {\n downcasted = int56(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(56, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int48 from int256, reverting on\n * overflow (when the input is less than smallest int48 or\n * greater than largest int48).\n *\n * Counterpart to Solidity's `int48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n */\n function toInt48(int256 value) internal pure returns (int48 downcasted) {\n downcasted = int48(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(48, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int40 from int256, reverting on\n * overflow (when the input is less than smallest int40 or\n * greater than largest int40).\n *\n * Counterpart to Solidity's `int40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n */\n function toInt40(int256 value) internal pure returns (int40 downcasted) {\n downcasted = int40(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(40, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toInt32(int256 value) internal pure returns (int32 downcasted) {\n downcasted = int32(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(32, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int24 from int256, reverting on\n * overflow (when the input is less than smallest int24 or\n * greater than largest int24).\n *\n * Counterpart to Solidity's `int24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n */\n function toInt24(int256 value) internal pure returns (int24 downcasted) {\n downcasted = int24(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(24, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toInt16(int256 value) internal pure returns (int16 downcasted) {\n downcasted = int16(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(16, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n */\n function toInt8(int256 value) internal pure returns (int8 downcasted) {\n downcasted = int8(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(8, value);\n }\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n if (value > uint256(type(int256).max)) {\n revert SafeCastOverflowedUintToInt(value);\n }\n return int256(value);\n }\n\n /**\n * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.\n */\n function toUint(bool b) internal pure returns (uint256 u) {\n assembly (\"memory-safe\") {\n u := iszero(iszero(b))\n }\n }\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/Panic.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Helper library for emitting standardized panic codes.\n *\n * ```solidity\n * contract Example {\n * using Panic for uint256;\n *\n * // Use any of the declared internal constants\n * function foo() { Panic.GENERIC.panic(); }\n *\n * // Alternatively\n * function foo() { Panic.panic(Panic.GENERIC); }\n * }\n * ```\n *\n * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].\n *\n * _Available since v5.1._\n */\n// slither-disable-next-line unused-state\nlibrary Panic {\n /// @dev generic / unspecified error\n uint256 internal constant GENERIC = 0x00;\n /// @dev used by the assert() builtin\n uint256 internal constant ASSERT = 0x01;\n /// @dev arithmetic underflow or overflow\n uint256 internal constant UNDER_OVERFLOW = 0x11;\n /// @dev division or modulo by zero\n uint256 internal constant DIVISION_BY_ZERO = 0x12;\n /// @dev enum conversion error\n uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;\n /// @dev invalid encoding in storage\n uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;\n /// @dev empty array pop\n uint256 internal constant EMPTY_ARRAY_POP = 0x31;\n /// @dev array out of bounds access\n uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;\n /// @dev resource error (too large allocation or too large array)\n uint256 internal constant RESOURCE_ERROR = 0x41;\n /// @dev calling invalid internal function\n uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;\n\n /// @dev Reverts with a panic code. Recommended to use with\n /// the internal constants with predefined codes.\n function panic(uint256 code) internal pure {\n assembly (\"memory-safe\") {\n mstore(0x00, 0x4e487b71)\n mstore(0x20, code)\n revert(0x1c, 0x24)\n }\n }\n}" + }, + "lib/rooster/v2-common/libraries/Constants.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\n// factory contraints on pools\nuint8 constant MAX_PROTOCOL_FEE_RATIO_D3 = 0.25e3; // 25%\nuint256 constant MAX_PROTOCOL_LENDING_FEE_RATE_D18 = 0.02e18; // 2%\nuint64 constant MAX_POOL_FEE_D18 = 0.9e18; // 90%\nuint64 constant MIN_LOOKBACK = 1 seconds;\n\n// pool constraints\nuint8 constant NUMBER_OF_KINDS = 4;\nint32 constant NUMBER_OF_KINDS_32 = int32(int8(NUMBER_OF_KINDS));\nuint256 constant MAX_TICK = 322_378; // max price 1e14 in D18 scale\nint32 constant MAX_TICK_32 = int32(int256(MAX_TICK));\nint32 constant MIN_TICK_32 = int32(-int256(MAX_TICK));\nuint256 constant MAX_BINS_TO_MERGE = 3;\nuint128 constant MINIMUM_LIQUIDITY = 1e8;\n\n// accessor named constants\nuint8 constant ALL_KINDS_MASK = 0xF; // 0b1111\nuint8 constant PERMISSIONED_LIQUIDITY_MASK = 0x10; // 0b010000\nuint8 constant PERMISSIONED_SWAP_MASK = 0x20; // 0b100000\nuint8 constant OPTIONS_MASK = ALL_KINDS_MASK | PERMISSIONED_LIQUIDITY_MASK | PERMISSIONED_SWAP_MASK; // 0b111111\n\n// named values\naddress constant MERGED_LP_BALANCE_ADDRESS = address(0);\nuint256 constant MERGED_LP_BALANCE_SUBACCOUNT = 0;\nuint128 constant ONE = 1e18;\nuint128 constant ONE_SQUARED = 1e36;\nint256 constant INT256_ONE = 1e18;\nuint256 constant ONE_D8 = 1e8;\nuint256 constant ONE_D3 = 1e3;\nint40 constant INT_ONE_D8 = 1e8;\nint40 constant HALF_TICK_D8 = 0.5e8;\nuint8 constant DEFAULT_DECIMALS = 18;\nuint256 constant DEFAULT_SCALE = 1;\nbytes constant EMPTY_PRICE_BREAKS = hex\"010000000000000000000000\";" + }, + "lib/rooster/v2-common/libraries/Math.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport {Math as OzMath} from \"../../openzeppelin-custom/contracts/utils/math/Math.sol\";\n\nimport {ONE, DEFAULT_SCALE, DEFAULT_DECIMALS, INT_ONE_D8, ONE_SQUARED} from \"./Constants.sol\";\n\n/**\n * @notice Math functions.\n */\nlibrary Math {\n /**\n * @notice Returns the lesser of two values.\n * @param x First uint256 value.\n * @param y Second uint256 value.\n */\n function min(uint256 x, uint256 y) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), lt(y, x)))\n }\n }\n\n /**\n * @notice Returns the lesser of two uint128 values.\n * @param x First uint128 value.\n * @param y Second uint128 value.\n */\n function min128(uint128 x, uint128 y) internal pure returns (uint128 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), lt(y, x)))\n }\n }\n\n /**\n * @notice Returns the lesser of two int256 values.\n * @param x First int256 value.\n * @param y Second int256 value.\n */\n function min(int256 x, int256 y) internal pure returns (int256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), slt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two uint256 values.\n * @param x First uint256 value.\n * @param y Second uint256 value.\n */\n function max(uint256 x, uint256 y) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), gt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two int256 values.\n * @param x First int256 value.\n * @param y Second int256 value.\n */\n function max(int256 x, int256 y) internal pure returns (int256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), sgt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two uint128 values.\n * @param x First uint128 value.\n * @param y Second uint128 value.\n */\n function max128(uint128 x, uint128 y) internal pure returns (uint128 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), gt(y, x)))\n }\n }\n\n /**\n * @notice Thresholds a value to be within the specified bounds.\n * @param value The value to bound.\n * @param lowerLimit The minimum allowable value.\n * @param upperLimit The maximum allowable value.\n */\n function boundValue(\n uint256 value,\n uint256 lowerLimit,\n uint256 upperLimit\n ) internal pure returns (uint256 outputValue) {\n outputValue = min(max(value, lowerLimit), upperLimit);\n }\n\n /**\n * @notice Returns the difference between two uint128 values or zero if the result would be negative.\n * @param x The minuend.\n * @param y The subtrahend.\n */\n function clip128(uint128 x, uint128 y) internal pure returns (uint128) {\n unchecked {\n return x < y ? 0 : x - y;\n }\n }\n\n /**\n * @notice Returns the difference between two uint256 values or zero if the result would be negative.\n * @param x The minuend.\n * @param y The subtrahend.\n */\n function clip(uint256 x, uint256 y) internal pure returns (uint256) {\n unchecked {\n return x < y ? 0 : x - y;\n }\n }\n\n /**\n * @notice Divides one uint256 by another, rounding down to the nearest\n * integer.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divFloor(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivFloor(x, ONE, y);\n }\n\n /**\n * @notice Divides one uint256 by another, rounding up to the nearest integer.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divCeil(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivCeil(x, ONE, y);\n }\n\n /**\n * @notice Multiplies two uint256 values and then divides by ONE, rounding down.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulFloor(uint256 x, uint256 y) internal pure returns (uint256) {\n return OzMath.mulDiv(x, y, ONE);\n }\n\n /**\n * @notice Multiplies two uint256 values and then divides by ONE, rounding up.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulCeil(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivCeil(x, y, ONE);\n }\n\n /**\n * @notice Calculates the multiplicative inverse of a uint256, rounding down.\n * @param x The value to invert.\n */\n function invFloor(uint256 x) internal pure returns (uint256) {\n unchecked {\n return ONE_SQUARED / x;\n }\n }\n\n /**\n * @notice Calculates the multiplicative inverse of a uint256, rounding up.\n * @param denominator The value to invert.\n */\n function invCeil(uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // divide z - 1 by the denominator and add 1.\n z := add(div(sub(ONE_SQUARED, 1), denominator), 1)\n }\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding down.\n * @param x The multiplicand.\n * @param y The multiplier.\n * @param k The divisor.\n */\n function mulDivFloor(uint256 x, uint256 y, uint256 k) internal pure returns (uint256 result) {\n result = OzMath.mulDiv(x, y, max(1, k));\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding up if there's a remainder.\n * @param x The multiplicand.\n * @param y The multiplier.\n * @param k The divisor.\n */\n function mulDivCeil(uint256 x, uint256 y, uint256 k) internal pure returns (uint256 result) {\n result = mulDivFloor(x, y, k);\n if (mulmod(x, y, max(1, k)) != 0) result = result + 1;\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding\n * down. Will revert if `x * y` is larger than `type(uint256).max`.\n * @param x The first operand for multiplication.\n * @param y The second operand for multiplication.\n * @param denominator The divisor after multiplication.\n */\n function mulDivDown(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // Store x * y in z for now.\n z := mul(x, y)\n if iszero(denominator) {\n denominator := 1\n }\n\n if iszero(or(iszero(x), eq(div(z, x), y))) {\n revert(0, 0)\n }\n\n // Divide z by the denominator.\n z := div(z, denominator)\n }\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding\n * up. Will revert if `x * y` is larger than `type(uint256).max`.\n * @param x The first operand for multiplication.\n * @param y The second operand for multiplication.\n * @param denominator The divisor after multiplication.\n */\n function mulDivUp(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // Store x * y in z for now.\n z := mul(x, y)\n if iszero(denominator) {\n denominator := 1\n }\n\n if iszero(or(iszero(x), eq(div(z, x), y))) {\n revert(0, 0)\n }\n\n // First, divide z - 1 by the denominator and add 1.\n // We allow z - 1 to underflow if z is 0, because we multiply the\n // end result by 0 if z is zero, ensuring we return 0 if z is zero.\n z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))\n }\n }\n\n /**\n * @notice Multiplies a uint256 by another and divides by a constant,\n * rounding down. Will revert if `x * y` is larger than\n * `type(uint256).max`.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulDown(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivDown(x, y, ONE);\n }\n\n /**\n * @notice Divides a uint256 by another, rounding down the result. Will\n * revert if `x * 1e18` is larger than `type(uint256).max`.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divDown(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivDown(x, ONE, y);\n }\n\n /**\n * @notice Divides a uint256 by another, rounding up the result. Will\n * revert if `x * 1e18` is larger than `type(uint256).max`.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divUp(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivUp(x, ONE, y);\n }\n\n /**\n * @notice Scales a number based on a difference in decimals from a default.\n * @param decimals The new decimal precision.\n */\n function scale(uint8 decimals) internal pure returns (uint256) {\n unchecked {\n if (decimals == DEFAULT_DECIMALS) {\n return DEFAULT_SCALE;\n } else {\n return 10 ** (DEFAULT_DECIMALS - decimals);\n }\n }\n }\n\n /**\n * @notice Adjusts a scaled amount to the token decimal scale.\n * @param amount The scaled amount.\n * @param scaleFactor The scaling factor to adjust by.\n * @param ceil Whether to round up (true) or down (false).\n */\n function ammScaleToTokenScale(uint256 amount, uint256 scaleFactor, bool ceil) internal pure returns (uint256 z) {\n unchecked {\n if (scaleFactor == DEFAULT_SCALE || amount == 0) {\n return amount;\n } else {\n if (!ceil) return amount / scaleFactor;\n assembly (\"memory-safe\") {\n z := add(div(sub(amount, 1), scaleFactor), 1)\n }\n }\n }\n }\n\n /**\n * @notice Adjusts a token amount to the D18 AMM scale.\n * @param amount The amount in token scale.\n * @param scaleFactor The scale factor for adjustment.\n */\n function tokenScaleToAmmScale(uint256 amount, uint256 scaleFactor) internal pure returns (uint256) {\n if (scaleFactor == DEFAULT_SCALE) {\n return amount;\n } else {\n return amount * scaleFactor;\n }\n }\n\n /**\n * @notice Returns the absolute value of a signed 32-bit integer.\n * @param x The integer to take the absolute value of.\n */\n function abs32(int32 x) internal pure returns (uint32) {\n unchecked {\n return uint32(x < 0 ? -x : x);\n }\n }\n\n /**\n * @notice Returns the absolute value of a signed 256-bit integer.\n * @param x The integer to take the absolute value of.\n */\n function abs(int256 x) internal pure returns (uint256) {\n unchecked {\n return uint256(x < 0 ? -x : x);\n }\n }\n\n /**\n * @notice Calculates the integer square root of a uint256 rounded down.\n * @param x The number to take the square root of.\n */\n function sqrt(uint256 x) internal pure returns (uint256 z) {\n // from https://github.com/transmissions11/solmate/blob/e8f96f25d48fe702117ce76c79228ca4f20206cb/src/utils/FixedPointMathLib.sol\n assembly (\"memory-safe\") {\n let y := x\n z := 181\n\n if iszero(lt(y, 0x10000000000000000000000000000000000)) {\n y := shr(128, y)\n z := shl(64, z)\n }\n if iszero(lt(y, 0x1000000000000000000)) {\n y := shr(64, y)\n z := shl(32, z)\n }\n if iszero(lt(y, 0x10000000000)) {\n y := shr(32, y)\n z := shl(16, z)\n }\n if iszero(lt(y, 0x1000000)) {\n y := shr(16, y)\n z := shl(8, z)\n }\n\n z := shr(18, mul(z, add(y, 65536)))\n\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n\n z := sub(z, lt(div(x, z), z))\n }\n }\n\n /**\n * @notice Computes the floor of a D8-scaled number as an int32, ignoring\n * potential overflow in the cast.\n * @param val The D8-scaled number.\n */\n function floorD8Unchecked(int256 val) internal pure returns (int32) {\n int32 val32;\n bool check;\n unchecked {\n val32 = int32(val / INT_ONE_D8);\n check = (val < 0 && val % INT_ONE_D8 != 0);\n }\n return check ? val32 - 1 : val32;\n }\n}" + }, + "lib/rooster/v2-common/libraries/TickMath.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport {Math as OzMath} from \"../../openzeppelin-custom/contracts/utils/math/Math.sol\";\nimport {Math} from \"./Math.sol\";\nimport {MAX_TICK, ONE} from \"./Constants.sol\";\n\n/**\n * @notice Math functions related to tick operations.\n */\n// slither-disable-start divide-before-multiply\nlibrary TickMath {\n using Math for uint256;\n\n error TickMaxExceeded(int256 tick);\n\n /**\n * @notice Compute the lower and upper sqrtPrice of a tick.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n */\n function tickSqrtPrices(\n uint256 tickSpacing,\n int32 _tick\n ) internal pure returns (uint256 sqrtLowerPrice, uint256 sqrtUpperPrice) {\n unchecked {\n sqrtLowerPrice = tickSqrtPrice(tickSpacing, _tick);\n sqrtUpperPrice = tickSqrtPrice(tickSpacing, _tick + 1);\n }\n }\n\n /**\n * @notice Compute the base tick value from the pool tick and the\n * tickSpacing. Revert if base tick is beyond the max tick boundary.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n */\n function subTickIndex(uint256 tickSpacing, int32 _tick) internal pure returns (uint32 subTick) {\n subTick = Math.abs32(_tick);\n subTick *= uint32(tickSpacing);\n if (subTick > MAX_TICK) {\n revert TickMaxExceeded(_tick);\n }\n }\n\n /**\n * @notice Calculate the square root price for a given tick and tick spacing.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n * @return _result The square root price.\n */\n function tickSqrtPrice(uint256 tickSpacing, int32 _tick) internal pure returns (uint256 _result) {\n unchecked {\n uint256 tick = subTickIndex(tickSpacing, _tick);\n\n uint256 ratio = tick & 0x1 != 0 ? 0xfffcb933bd6fad9d3af5f0b9f25db4d6 : 0x100000000000000000000000000000000;\n if (tick & 0x2 != 0) ratio = (ratio * 0xfff97272373d41fd789c8cb37ffcaa1c) >> 128;\n if (tick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656ac9229c67059486f389) >> 128;\n if (tick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e81259b3cddc7a064941) >> 128;\n if (tick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f67b19e8887e0bd251eb7) >> 128;\n if (tick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98cd2e57b660be99eb2c4a) >> 128;\n if (tick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c9838804e327cb417cafcb) >> 128;\n if (tick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99d51e2cc356c2f617dbe0) >> 128;\n if (tick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900aecf64236ab31f1f9dcb5) >> 128;\n if (tick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac4d9194200696907cf2e37) >> 128;\n if (tick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b88206f8abe8a3b44dd9be) >> 128;\n if (tick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c578ef4f1d17b2b235d480) >> 128;\n if (tick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd254ee83bdd3f248e7e785e) >> 128;\n if (tick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d8f7dd10e744d913d033333) >> 128;\n if (tick & 0x4000 != 0) ratio = (ratio * 0x70d869a156ddd32a39e257bc3f50aa9b) >> 128;\n if (tick & 0x8000 != 0) ratio = (ratio * 0x31be135f97da6e09a19dc367e3b6da40) >> 128;\n if (tick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7e5a9780b0cc4e25d61a56) >> 128;\n if (tick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedbcb3a6ccb7ce618d14225) >> 128;\n if (tick & 0x40000 != 0) ratio = (ratio * 0x2216e584f630389b2052b8db590e) >> 128;\n if (_tick > 0) ratio = type(uint256).max / ratio;\n _result = (ratio * ONE) >> 128;\n }\n }\n\n /**\n * @notice Calculate liquidity of a tick.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n */\n function getTickL(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n ) internal pure returns (uint256 liquidity) {\n // known:\n // - sqrt price values are different\n // - reserveA and reserveB fit in 128 bit\n // - sqrt price is in (1e-7, 1e7)\n // - D18 max for uint256 is 1.15e59\n // - D18 min is 1e-18\n\n unchecked {\n // diff is in (5e-12, 4e6); max tick spacing is 10_000\n uint256 diff = sqrtUpperTickPrice - sqrtLowerTickPrice;\n\n // Need to maximize precision by shifting small values A and B up so\n // that they use more of the available bit range. Two constraints to\n // consider: we need A * B * diff / sqrtPrice to be bigger than 1e-18\n // when the bump is not in play. This constrains the threshold for\n // bumping to be at least 77 bit; ie, either a or b needs 2^77 which\n // means that term A * B * diff / sqrtPrice > 1e-18.\n //\n // At the other end, the second constraint is that b^2 needs to fit in\n // a 256-bit number, so, post bump, the max reserve value needs to be\n // less than 6e22. With a 78-bit threshold and a 57-bit bump, we have A\n // and B are in (1.4e-1, 4.4e22 (2^(78+57))) with bump, and one of A or\n // B is at least 2^78 without the bump, but the other reserve value may\n // be as small as 1 wei.\n uint256 precisionBump = 0;\n if ((reserveA >> 78) == 0 && (reserveB >> 78) == 0) {\n precisionBump = 57;\n reserveA <<= precisionBump;\n reserveB <<= precisionBump;\n }\n\n if (reserveB == 0) return Math.divDown(reserveA, diff) >> precisionBump;\n if (reserveA == 0)\n return Math.mulDivDown(reserveB.mulDown(sqrtLowerTickPrice), sqrtUpperTickPrice, diff) >> precisionBump;\n\n // b is in (7.2e-9 (2^57 / 1e7 / 2), 2.8e29 (2^(78+57) * 1e7 / 2)) with bump\n // b is in a subset of the same range without bump\n uint256 b = (reserveA.divDown(sqrtUpperTickPrice) + reserveB.mulDown(sqrtLowerTickPrice)) >> 1;\n\n // b^2 is in (5.1e-17, 4.8e58); and will not overflow on either end;\n // A*B is in (3e-13 (2^78 / 1e18 * 1e-18), 1.9e45) without bump and is in a subset range with bump\n // A*B*diff/sqrtUpper is in (1.5e-17 (3e-13 * 5e-12 * 1e7), 7.6e58);\n\n // Since b^2 is at the upper edge of the precision range, we are not\n // able to multiply the argument of the sqrt by 1e18, instead, we move\n // this factor outside of the sqrt. The resulting loss of precision\n // means that this liquidity value is a lower bound on the tick\n // liquidity\n return\n OzMath.mulDiv(\n b +\n Math.sqrt(\n (OzMath.mulDiv(b, b, ONE) +\n OzMath.mulDiv(reserveB.mulFloor(reserveA), diff, sqrtUpperTickPrice))\n ) *\n 1e9,\n sqrtUpperTickPrice,\n diff\n ) >> precisionBump;\n }\n }\n\n /**\n * @notice Calculate square root price of a tick. Returns left edge of the\n * tick if the tick has no reserves.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n * @return sqrtPrice The calculated square root price.\n */\n function getSqrtPrice(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice,\n uint256 liquidity\n ) internal pure returns (uint256 sqrtPrice) {\n unchecked {\n if (reserveA == 0) {\n return sqrtLowerTickPrice;\n }\n if (reserveB == 0) {\n return sqrtUpperTickPrice;\n }\n sqrtPrice = Math.sqrt(\n ONE *\n (reserveA + liquidity.mulDown(sqrtLowerTickPrice)).divDown(\n reserveB + liquidity.divDown(sqrtUpperTickPrice)\n )\n );\n sqrtPrice = Math.boundValue(sqrtPrice, sqrtLowerTickPrice, sqrtUpperTickPrice);\n }\n }\n\n /**\n * @notice Calculate square root price of a tick. Returns left edge of the\n * tick if the tick has no reserves.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n * @return sqrtPrice The calculated square root price.\n * @return liquidity The calculated liquidity.\n */\n function getTickSqrtPriceAndL(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n ) internal pure returns (uint256 sqrtPrice, uint256 liquidity) {\n liquidity = getTickL(reserveA, reserveB, sqrtLowerTickPrice, sqrtUpperTickPrice);\n sqrtPrice = getSqrtPrice(reserveA, reserveB, sqrtLowerTickPrice, sqrtUpperTickPrice, liquidity);\n }\n}\n// slither-disable-end divide-before-multiply" + }, + "solidity-bytes-utils/contracts/BytesLib.sol": { + "content": "// SPDX-License-Identifier: Unlicense\n/*\n * @title Solidity Bytes Arrays Utils\n * @author Gonçalo Sá \n *\n * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.\n * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.\n */\npragma solidity >=0.8.0 <0.9.0;\n\n\nlibrary BytesLib {\n function concat(\n bytes memory _preBytes,\n bytes memory _postBytes\n )\n internal\n pure\n returns (bytes memory)\n {\n bytes memory tempBytes;\n\n assembly {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // Store the length of the first bytes array at the beginning of\n // the memory for tempBytes.\n let length := mload(_preBytes)\n mstore(tempBytes, length)\n\n // Maintain a memory counter for the current write location in the\n // temp bytes array by adding the 32 bytes for the array length to\n // the starting location.\n let mc := add(tempBytes, 0x20)\n // Stop copying when the memory counter reaches the length of the\n // first bytes array.\n let end := add(mc, length)\n\n for {\n // Initialize a copy counter to the start of the _preBytes data,\n // 32 bytes into its memory.\n let cc := add(_preBytes, 0x20)\n } lt(mc, end) {\n // Increase both counters by 32 bytes each iteration.\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // Write the _preBytes data into the tempBytes memory 32 bytes\n // at a time.\n mstore(mc, mload(cc))\n }\n\n // Add the length of _postBytes to the current length of tempBytes\n // and store it as the new length in the first 32 bytes of the\n // tempBytes memory.\n length := mload(_postBytes)\n mstore(tempBytes, add(length, mload(tempBytes)))\n\n // Move the memory counter back from a multiple of 0x20 to the\n // actual end of the _preBytes data.\n mc := end\n // Stop copying when the memory counter reaches the new combined\n // length of the arrays.\n end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n // Update the free-memory pointer by padding our last write location\n // to 32 bytes: add 31 bytes to the end of tempBytes to move to the\n // next 32 byte block, then round down to the nearest multiple of\n // 32. If the sum of the length of the two arrays is zero then add\n // one before rounding down to leave a blank 32 bytes (the length block with 0).\n mstore(0x40, and(\n add(add(end, iszero(add(length, mload(_preBytes)))), 31),\n not(31) // Round down to the nearest 32 bytes.\n ))\n }\n\n return tempBytes;\n }\n\n function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {\n assembly {\n // Read the first 32 bytes of _preBytes storage, which is the length\n // of the array. (We don't need to use the offset into the slot\n // because arrays use the entire slot.)\n let fslot := sload(_preBytes.slot)\n // Arrays of 31 bytes or less have an even value in their slot,\n // while longer arrays have an odd value. The actual length is\n // the slot divided by two for odd values, and the lowest order\n // byte divided by two for even values.\n // If the slot is even, bitwise and the slot with 255 and divide by\n // two to get the length. If the slot is odd, bitwise and the slot\n // with -1 and divide by two.\n let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)\n let mlength := mload(_postBytes)\n let newlength := add(slength, mlength)\n // slength can contain both the length and contents of the array\n // if length < 32 bytes so let's prepare for that\n // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage\n switch add(lt(slength, 32), lt(newlength, 32))\n case 2 {\n // Since the new array still fits in the slot, we just need to\n // update the contents of the slot.\n // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length\n sstore(\n _preBytes.slot,\n // all the modifications to the slot are inside this\n // next block\n add(\n // we can just add to the slot contents because the\n // bytes we want to change are the LSBs\n fslot,\n add(\n mul(\n div(\n // load the bytes from memory\n mload(add(_postBytes, 0x20)),\n // zero all bytes to the right\n exp(0x100, sub(32, mlength))\n ),\n // and now shift left the number of bytes to\n // leave space for the length in the slot\n exp(0x100, sub(32, newlength))\n ),\n // increase length by the double of the memory\n // bytes length\n mul(mlength, 2)\n )\n )\n )\n }\n case 1 {\n // The stored value fits in the slot, but the combined value\n // will exceed it.\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n let sc := add(keccak256(0x0, 0x20), div(slength, 32))\n\n // save new length\n sstore(_preBytes.slot, add(mul(newlength, 2), 1))\n\n // The contents of the _postBytes array start 32 bytes into\n // the structure. Our first read should obtain the `submod`\n // bytes that can fit into the unused space in the last word\n // of the stored array. To get this, we read 32 bytes starting\n // from `submod`, so the data we read overlaps with the array\n // contents by `submod` bytes. Masking the lowest-order\n // `submod` bytes allows us to add that value directly to the\n // stored value.\n\n let submod := sub(32, slength)\n let mc := add(_postBytes, submod)\n let end := add(_postBytes, mlength)\n let mask := sub(exp(0x100, submod), 1)\n\n sstore(\n sc,\n add(\n and(\n fslot,\n 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00\n ),\n and(mload(mc), mask)\n )\n )\n\n for {\n mc := add(mc, 0x20)\n sc := add(sc, 1)\n } lt(mc, end) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n sstore(sc, mload(mc))\n }\n\n mask := exp(0x100, sub(mc, end))\n\n sstore(sc, mul(div(mload(mc), mask), mask))\n }\n default {\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n // Start copying to the last used word of the stored array.\n let sc := add(keccak256(0x0, 0x20), div(slength, 32))\n\n // save new length\n sstore(_preBytes.slot, add(mul(newlength, 2), 1))\n\n // Copy over the first `submod` bytes of the new data as in\n // case 1 above.\n let slengthmod := mod(slength, 32)\n let mlengthmod := mod(mlength, 32)\n let submod := sub(32, slengthmod)\n let mc := add(_postBytes, submod)\n let end := add(_postBytes, mlength)\n let mask := sub(exp(0x100, submod), 1)\n\n sstore(sc, add(sload(sc), and(mload(mc), mask)))\n\n for {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } lt(mc, end) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n sstore(sc, mload(mc))\n }\n\n mask := exp(0x100, sub(mc, end))\n\n sstore(sc, mul(div(mload(mc), mask), mask))\n }\n }\n }\n\n function slice(\n bytes memory _bytes,\n uint256 _start,\n uint256 _length\n )\n internal\n pure\n returns (bytes memory)\n {\n // We're using the unchecked block below because otherwise execution ends \n // with the native overflow error code.\n unchecked {\n require(_length + 31 >= _length, \"slice_overflow\");\n }\n require(_bytes.length >= _start + _length, \"slice_outOfBounds\");\n\n bytes memory tempBytes;\n\n assembly {\n switch iszero(_length)\n case 0 {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // The first word of the slice result is potentially a partial\n // word read from the original array. To read it, we calculate\n // the length of that partial word and start copying that many\n // bytes into the array. The first word we copy will start with\n // data we don't care about, but the last `lengthmod` bytes will\n // land at the beginning of the contents of the new array. When\n // we're done copying, we overwrite the full first word with\n // the actual length of the slice.\n let lengthmod := and(_length, 31)\n\n // The multiplication in the next line is necessary\n // because when slicing multiples of 32 bytes (lengthmod == 0)\n // the following copy loop was copying the origin's length\n // and then ending prematurely not copying everything it should.\n let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))\n let end := add(mc, _length)\n\n for {\n // The multiplication in the next line has the same exact purpose\n // as the one above.\n let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n mstore(tempBytes, _length)\n\n //update free-memory pointer\n //allocating the array padded to 32 bytes like the compiler does now\n mstore(0x40, and(add(mc, 31), not(31)))\n }\n //if we want a zero-length slice let's just return a zero-length array\n default {\n tempBytes := mload(0x40)\n //zero out the 32 bytes slice we are about to return\n //we need to do it because Solidity does not garbage collect\n mstore(tempBytes, 0)\n\n mstore(0x40, add(tempBytes, 0x20))\n }\n }\n\n return tempBytes;\n }\n\n function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {\n require(_bytes.length >= _start + 20, \"toAddress_outOfBounds\");\n address tempAddress;\n\n assembly {\n tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)\n }\n\n return tempAddress;\n }\n\n function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {\n require(_bytes.length >= _start + 1 , \"toUint8_outOfBounds\");\n uint8 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x1), _start))\n }\n\n return tempUint;\n }\n\n function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {\n require(_bytes.length >= _start + 2, \"toUint16_outOfBounds\");\n uint16 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x2), _start))\n }\n\n return tempUint;\n }\n\n function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {\n require(_bytes.length >= _start + 4, \"toUint32_outOfBounds\");\n uint32 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x4), _start))\n }\n\n return tempUint;\n }\n\n function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {\n require(_bytes.length >= _start + 8, \"toUint64_outOfBounds\");\n uint64 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x8), _start))\n }\n\n return tempUint;\n }\n\n function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {\n require(_bytes.length >= _start + 12, \"toUint96_outOfBounds\");\n uint96 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0xc), _start))\n }\n\n return tempUint;\n }\n\n function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {\n require(_bytes.length >= _start + 16, \"toUint128_outOfBounds\");\n uint128 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x10), _start))\n }\n\n return tempUint;\n }\n\n function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {\n require(_bytes.length >= _start + 32, \"toUint256_outOfBounds\");\n uint256 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x20), _start))\n }\n\n return tempUint;\n }\n\n function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {\n require(_bytes.length >= _start + 32, \"toBytes32_outOfBounds\");\n bytes32 tempBytes32;\n\n assembly {\n tempBytes32 := mload(add(add(_bytes, 0x20), _start))\n }\n\n return tempBytes32;\n }\n\n function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {\n bool success = true;\n\n assembly {\n let length := mload(_preBytes)\n\n // if lengths don't match the arrays are not equal\n switch eq(length, mload(_postBytes))\n case 1 {\n // cb is a circuit breaker in the for loop since there's\n // no said feature for inline assembly loops\n // cb = 1 - don't breaker\n // cb = 0 - break\n let cb := 1\n\n let mc := add(_preBytes, 0x20)\n let end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n // the next line is the loop condition:\n // while(uint256(mc < end) + cb == 2)\n } eq(add(lt(mc, end), cb), 2) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // if any of these checks fails then arrays are not equal\n if iszero(eq(mload(mc), mload(cc))) {\n // unsuccess:\n success := 0\n cb := 0\n }\n }\n }\n default {\n // unsuccess:\n success := 0\n }\n }\n\n return success;\n }\n\n function equalStorage(\n bytes storage _preBytes,\n bytes memory _postBytes\n )\n internal\n view\n returns (bool)\n {\n bool success = true;\n\n assembly {\n // we know _preBytes_offset is 0\n let fslot := sload(_preBytes.slot)\n // Decode the length of the stored array like in concatStorage().\n let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)\n let mlength := mload(_postBytes)\n\n // if lengths don't match the arrays are not equal\n switch eq(slength, mlength)\n case 1 {\n // slength can contain both the length and contents of the array\n // if length < 32 bytes so let's prepare for that\n // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage\n if iszero(iszero(slength)) {\n switch lt(slength, 32)\n case 1 {\n // blank the last byte which is the length\n fslot := mul(div(fslot, 0x100), 0x100)\n\n if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {\n // unsuccess:\n success := 0\n }\n }\n default {\n // cb is a circuit breaker in the for loop since there's\n // no said feature for inline assembly loops\n // cb = 1 - don't breaker\n // cb = 0 - break\n let cb := 1\n\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n let sc := keccak256(0x0, 0x20)\n\n let mc := add(_postBytes, 0x20)\n let end := add(mc, mlength)\n\n // the next line is the loop condition:\n // while(uint256(mc < end) + cb == 2)\n for {} eq(add(lt(mc, end), cb), 2) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n if iszero(eq(sload(sc), mload(mc))) {\n // unsuccess:\n success := 0\n cb := 0\n }\n }\n }\n }\n }\n default {\n // unsuccess:\n success := 0\n }\n }\n\n return success;\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/e971b2fb0f925bb0e94e79f6b234f436.json b/contracts/deployments/hoodi/solcInputs/e971b2fb0f925bb0e94e79f6b234f436.json new file mode 100644 index 0000000000..e09fd379b7 --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/e971b2fb0f925bb0e94e79f6b234f436.json @@ -0,0 +1,129 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/security/Pausable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/beacon/BeaconOracle.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"./BeaconProofsLib.sol\";\nimport { BeaconRoots } from \"./BeaconRoots.sol\";\n\n/// @title Beacon Chain Oracle\n/// @notice An Oracle for mapping execution layer block numbers to beacon chain slots.\n/// @author Origin Protocol Inc\ncontract BeaconOracle {\n /// @notice Maps a block number to slot\n mapping(uint64 => uint64) internal _blockToSlot;\n /// @notice Maps a slot to a number\n mapping(uint64 => uint64) internal _slotToBlock;\n /// @notice Maps a slot to a beacon block root\n mapping(uint64 => bytes32) internal _slotToRoot;\n\n event BlockToSlot(\n bytes32 indexed blockRoot,\n uint64 indexed blockNumber,\n uint64 indexed slot\n );\n\n /// @notice Uses merkle a proof against the beacon block root to link\n /// an execution layer block number to a beacon chain slot.\n /// @param nextBlockTimestamp The timestamp of the block after the one being proven.\n /// @param blockNumber The execution layer block number.\n /// @param slot The beacon chain slot.\n /// @param slotProof The merkle proof witnesses for the slot against the beacon block root.\n /// @param blockProof The merkle proof witnesses for the block number against the beacon block root.\n function verifySlot(\n uint64 nextBlockTimestamp,\n uint64 blockNumber,\n uint64 slot,\n bytes calldata slotProof,\n bytes calldata blockProof\n ) external returns (bytes32 blockRoot) {\n require(_blockToSlot[blockNumber] == 0, \"Block already mapped\");\n\n // Get the parent beacon block root for the given timestamp.\n // This is the beacon block root of the previous slot.\n blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the slot to the beacon block root\n BeaconProofsLib.verifySlot(blockRoot, slot, slotProof);\n\n // Verify the block number to the beacon block root\n BeaconProofsLib.verifyBlockNumber(blockRoot, blockNumber, blockProof);\n\n // Store mappings\n _blockToSlot[blockNumber] = slot;\n _slotToBlock[slot] = blockNumber;\n _slotToRoot[slot] = blockRoot;\n\n emit BlockToSlot(blockRoot, blockNumber, slot);\n }\n\n /// @notice Returns the beacon chain slot for a given execution layer block number.\n function blockToSlot(uint64 blockNumber)\n external\n view\n returns (uint64 slot)\n {\n slot = _blockToSlot[blockNumber];\n\n require(slot != 0, \"Block not mapped\");\n }\n\n /// @notice Returns the execution layer block number for a given beacon chain slot.\n function slotToBlock(uint64 slot)\n external\n view\n returns (uint64 blockNumber)\n {\n blockNumber = _slotToBlock[slot];\n\n require(blockNumber != 0, \"Slot not mapped\");\n }\n\n /// @notice Returns the beacon block root for a given beacon chain slot.\n function slotToRoot(uint64 slot) external view returns (bytes32 blockRoot) {\n blockRoot = _slotToRoot[slot];\n\n require(blockRoot != 0, \"Slot not mapped\");\n }\n\n /// @notice Returns true if an execution layer block number has been mapped to a beacon chain slot.\n function isBlockMapped(uint64 blockNumber) external view returns (bool) {\n return _blockToSlot[blockNumber] != 0;\n }\n\n /// @notice Returns true if a beacon chain slot has been mapped to an execution layer block number.\n function isSlotMapped(uint64 slot) external view returns (bool) {\n return _slotToBlock[slot] != 0;\n }\n}\n" + }, + "contracts/beacon/BeaconProofsLib.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Merkle } from \"./Merkle.sol\";\nimport { Endian } from \"./Endian.sol\";\n\nlibrary BeaconProofsLib {\n // Known generalized indices in the beacon block\n // BeaconBlock.slot\n uint256 internal constant SLOT_GENERALIZED_INDEX = 8;\n // BeaconBlock.state.PendingDeposits[0].slot\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX =\n 1584842932228;\n // BeaconBlock.body.executionPayload.blockNumber\n uint256 internal constant BLOCK_NUMBER_GENERALIZED_INDEX = 6438;\n // BeaconBlock.state.validators\n uint256 internal constant VALIDATORS_CONTAINER_GENERALIZED_INDEX = 715;\n // BeaconBlock.state.balances\n uint256 internal constant BALANCES_CONTAINER_GENERALIZED_INDEX = 716;\n\n // Beacon Container Tree Heights\n uint256 internal constant BALANCES_HEIGHT = 39;\n uint256 internal constant VALIDATORS_HEIGHT = 41;\n uint256 internal constant VALIDATOR_HEIGHT = 3;\n\n /// @notice Fields in the Validator container for phase 0\n /// See https://ethereum.github.io/consensus-specs/specs/phase0/beacon-chain/#validator\n uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;\n\n enum BalanceProofLevel {\n Container,\n BeaconBlock\n }\n\n /// @notice Verifies the validator public key against the beacon block root\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash The beacon chain hash of the validator public key\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex\n ) internal view {\n // BeaconBlock.state.validators[validatorIndex].pubkey\n uint256 generalizedIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_HEIGHT,\n validatorIndex\n );\n generalizedIndex = concatGenIndices(\n generalizedIndex,\n VALIDATOR_HEIGHT,\n VALIDATOR_PUBKEY_INDEX\n );\n\n require(\n Merkle.verifyInclusionSha256({\n proof: validatorPubKeyProof,\n root: beaconBlockRoot,\n leaf: pubKeyHash,\n index: generalizedIndex\n }),\n \"Invalid validator pubkey proof\"\n );\n }\n\n /// @notice Verifies the balances container against the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerLeaf The leaf node containing the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) internal view {\n // BeaconBlock.state.balances\n require(\n Merkle.verifyInclusionSha256({\n proof: balancesContainerProof,\n root: beaconBlockRoot,\n leaf: balancesContainerLeaf,\n index: BALANCES_CONTAINER_GENERALIZED_INDEX\n }),\n \"Invalid balance container proof\"\n );\n }\n\n /// @notice Verifies the validator balance against the root of the Balances container\n /// or the beacon block root\n /// @param root The root of the Balances container or the beacon block root\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances\n /// @param balanceProof The merkle proof for the validator balance against the root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @param level The level of the balance proof, either Container or BeaconBlock\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 root,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex,\n BalanceProofLevel level\n ) internal view returns (uint256 validatorBalanceGwei) {\n // Four balances are stored in each leaf so the validator index is divided by 4\n uint64 balanceIndex = validatorIndex / 4;\n\n uint256 generalizedIndex;\n if (level == BalanceProofLevel.Container) {\n // Get the index within the balances container, not the Beacon Block\n // BeaconBlock.state.balances[balanceIndex]\n generalizedIndex = concatGenIndices(\n 1,\n BALANCES_HEIGHT,\n balanceIndex\n );\n }\n\n if (level == BalanceProofLevel.BeaconBlock) {\n generalizedIndex = concatGenIndices(\n BALANCES_CONTAINER_GENERALIZED_INDEX,\n BALANCES_HEIGHT,\n balanceIndex\n );\n }\n\n validatorBalanceGwei = balanceAtIndex(\n validatorBalanceLeaf,\n validatorIndex\n );\n\n require(\n Merkle.verifyInclusionSha256({\n proof: balanceProof,\n root: root,\n leaf: validatorBalanceLeaf,\n index: generalizedIndex\n }),\n \"Invalid balance container proof\"\n );\n }\n\n /// @notice Verifies the slot of the first pending deposit against the beacon block root\n /// BeaconBlock.state.PendingDeposits[0].slot\n /// @param beaconBlockRoot The root of the beacon block\n /// @param slot The beacon chain slot to verify\n /// @param firstPendingDepositSlotProof The merkle proof for the first pending deposit's slot\n /// against the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) internal view {\n // Convert uint64 slot number to a little endian bytes32\n bytes32 slotLeaf = Endian.toLittleEndianUint64(slot);\n\n require(\n Merkle.verifyInclusionSha256({\n proof: firstPendingDepositSlotProof,\n root: beaconBlockRoot,\n leaf: slotLeaf,\n index: FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX\n }),\n \"Invalid pending deposit proof\"\n );\n }\n\n /// @notice Verifies the block number to the the beacon block root\n /// BeaconBlock.body.executionPayload.blockNumber\n /// @param beaconBlockRoot The root of the beacon block\n /// @param blockNumber The execution layer block number to verify\n /// @param blockNumberProof The merkle proof for the block number against the beacon block\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyBlockNumber(\n bytes32 beaconBlockRoot,\n uint256 blockNumber,\n bytes calldata blockNumberProof\n ) internal view {\n // Convert uint64 block number to a little endian bytes32\n bytes32 blockNumberLeaf = Endian.toLittleEndianUint64(\n uint64(blockNumber)\n );\n require(\n Merkle.verifyInclusionSha256({\n proof: blockNumberProof,\n root: beaconBlockRoot,\n leaf: blockNumberLeaf,\n index: BLOCK_NUMBER_GENERALIZED_INDEX\n }),\n \"Invalid block number proof\"\n );\n }\n\n /// @notice Verifies the slot number against the beacon block root.\n /// BeaconBlock.slot\n /// @param beaconBlockRoot The root of the beacon block\n /// @param slot The beacon chain slot to verify\n /// @param slotProof The merkle proof for the slot against the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifySlot(\n bytes32 beaconBlockRoot,\n uint256 slot,\n bytes calldata slotProof\n ) internal view {\n require(\n Merkle.verifyInclusionSha256({\n proof: slotProof,\n root: beaconBlockRoot,\n leaf: Endian.toLittleEndianUint64(uint64(slot)),\n index: SLOT_GENERALIZED_INDEX\n }),\n \"Invalid slot number proof\"\n );\n }\n\n ////////////////////////////////////////////////////\n /// Internal Helper Functions\n ////////////////////////////////////////////////////\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n internal\n pure\n returns (uint256)\n {\n uint256 bitShiftAmount = (validatorIndex % 4) * 64;\n return\n Endian.fromLittleEndianUint64(\n bytes32((uint256(validatorBalanceLeaf) << bitShiftAmount))\n );\n }\n\n /// @notice Concatenates two beacon chain generalized indices into one.\n /// @param genIndex The first generalized index or 1 if calculating for a single container.\n /// @param height The merkle tree height of the second container. eg 39 for balances, 41 for validators.\n /// @param index The index within the second container. eg the validator index.\n /// @return genIndex The concatenated generalized index.\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) internal pure returns (uint256) {\n return (genIndex << height) | index;\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice The length of the beacon block root ring buffer\n uint256 internal constant BEACON_ROOTS_HISTORY_BUFFER_LENGTH = 8191;\n\n /// @notice Returns the Beacon Block Root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Commented out the following checks as it makes unit and fork testing very difficult.\n // require(block.timestamp >= timestamp, \"Timestamp in future\");\n // require(\n // block.timestamp - timestamp <\n // BEACON_ROOTS_HISTORY_BUFFER_LENGTH * 12,\n // \"Timestamp too old\"\n // );\n\n // Call the Beacon Block Root Oracle to get the parent block root\n // This does not have a function signature, so we use a staticcall\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/Endian.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary Endian {\n /**\n * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64\n * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type\n * @return n The big endian-formatted uint64\n * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits),\n * but it is immediately truncated to a uint64 (i.e. 64 bits)\n * through a right-shift/shr operation.\n */\n function fromLittleEndianUint64(bytes32 lenum)\n internal\n pure\n returns (uint64 n)\n {\n // the number needs to be stored in little-endian encoding (ie in bytes 0-8)\n n = uint64(uint256(lenum >> 192));\n // forgefmt: disable-next-item\n return\n (n >> 56) |\n ((0x00FF000000000000 & n) >> 40) |\n ((0x0000FF0000000000 & n) >> 24) |\n ((0x000000FF00000000 & n) >> 8) |\n ((0x00000000FF000000 & n) << 8) |\n ((0x0000000000FF0000 & n) << 24) |\n ((0x000000000000FF00 & n) << 40) |\n ((0x00000000000000FF & n) << 56);\n }\n\n function toLittleEndianUint64(uint64 benum)\n internal\n pure\n returns (bytes32 n)\n {\n // Convert to little-endian by reversing byte order\n uint64 reversed = (benum >> 56) |\n ((0x00FF000000000000 & benum) >> 40) |\n ((0x0000FF0000000000 & benum) >> 24) |\n ((0x000000FF00000000 & benum) >> 8) |\n ((0x00000000FF000000 & benum) << 8) |\n ((0x0000000000FF0000 & benum) << 24) |\n ((0x000000000000FF00 & benum) << 40) |\n ((0x00000000000000FF & benum) << 56);\n\n // Store the little-endian uint64 in the least significant 64 bits of bytes32\n n = bytes32(uint256(reversed));\n // Shift to most significant bits\n n = n << 192;\n }\n}\n" + }, + "contracts/beacon/Merkle.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev These functions deal with verification of Merkle Tree proofs.\n *\n * The tree and the proofs can be generated using our\n * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].\n * You will find a quickstart guide in the readme.\n *\n * WARNING: You should avoid using leaf values that are 64 bytes long prior to\n * hashing, or use a hash function other than keccak256 for hashing leaves.\n * This is because the concatenation of a sorted pair of internal nodes in\n * the merkle tree could be reinterpreted as a leaf value.\n * OpenZeppelin's JavaScript library generates merkle trees that are safe\n * against this attack out of the box.\n */\nlibrary Merkle {\n error InvalidProofLength();\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function verifyInclusionSha256(\n bytes memory proof,\n bytes32 root,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bool) {\n return processInclusionProofSha256(proof, leaf, index) == root;\n }\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * _Available since v4.4._\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function processInclusionProofSha256(\n bytes memory proof,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bytes32) {\n require(\n proof.length != 0 && proof.length % 32 == 0,\n InvalidProofLength()\n );\n bytes32[1] memory computedHash = [leaf];\n for (uint256 i = 32; i <= proof.length; i += 32) {\n if (index % 2 == 0) {\n // if ith bit of index is 0, then computedHash is a left sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(computedHash))\n mstore(0x20, mload(add(proof, i)))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n index := div(index, 2)\n }\n } else {\n // if ith bit of index is 1, then computedHash is a right sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(add(proof, i)))\n mstore(0x20, mload(computedHash))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n index := div(index, 2)\n }\n }\n }\n return computedHash[0];\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconOracle {\n function blockToSlot(uint64 blockNumber) external returns (uint64 slot);\n\n function slotToBlock(uint64 slot) external returns (uint64 blockNumber);\n\n function slotToRoot(uint64 slot) external returns (bytes32 blockRoot);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n enum BalanceProofLevel {\n Container,\n BeaconBlock\n }\n\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 root,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex,\n BalanceProofLevel level\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view;\n\n function verifyBlockNumber(\n bytes32 beaconBlockRoot,\n uint256 blockNumber,\n bytes calldata blockNumberProof\n ) external view;\n\n function verifySlot(\n bytes32 beaconBlockRoot,\n uint256 slot,\n bytes calldata slotProof\n ) external view;\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n external\n pure\n returns (uint256);\n\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) external pure returns (uint256);\n}\n" + }, + "contracts/interfaces/IConsolidation.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IConsolidationSource {\n function confirmConsolidation()\n external\n returns (uint256 consolidationCount);\n}\n\ninterface IConsolidationTarget {\n function requestConsolidation(\n bytes32 lastSourcePubKeyHash,\n bytes32 targetPubKeyHash\n ) external;\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/mocks/MockBeaconOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconOracle } from \"../beacon/BeaconOracle.sol\";\n\ncontract MockBeaconOracle is BeaconOracle {\n function mapSlot(\n uint64 blockNumber,\n uint64 slot,\n bytes32 _blockRoot\n ) external {\n _blockToSlot[blockNumber] = slot;\n _slotToBlock[slot] = blockNumber;\n _slotToRoot[slot] = _blockRoot;\n }\n}\n" + }, + "contracts/mocks/MockBeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconRoots } from \"../beacon/BeaconRoots.sol\";\n\ncontract MockBeaconRoots {\n // Mapping to simulate the ring buffer: timestamp => beacon block root\n mapping(uint256 => bytes32) internal _beaconRoots;\n\n // Event to log when a new root is set (for testing)\n event RootSet(uint256 indexed timestamp, bytes32 root);\n\n // Fallback function to handle raw 32-byte timestamp input\n // solhint-disable no-complex-fallback\n fallback() external {\n // Ensure input is exactly 32 bytes (big-endian encoded timestamp)\n require(msg.data.length == 32, \"Input must be 32 bytes\");\n\n // Decode the 32-byte input as a uint256 timestamp (big-endian)\n uint256 timestamp = abi.decode(msg.data, (uint256));\n\n // Don't do any validation of timestamp so we can test any block\n\n // Retrieve the root. Will return bytes32(0) if not set.\n bytes32 root = _beaconRoots[timestamp];\n\n // Return the 32-byte root directly\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0, root)\n return(0, 32)\n }\n }\n\n // Mock function to set a beacon block root (for testing)\n function setBeaconRoot(uint256 timestamp, bytes32 root) external {\n require(timestamp > 0, \"Invalid timestamp\");\n require(root != bytes32(0), \"Invalid root\");\n\n // Store the root at the given timestamp\n _beaconRoots[timestamp] = root;\n\n emit RootSet(timestamp, root);\n }\n\n function setBeaconRoot(bytes32 root) external {\n require(root != bytes32(0), \"Invalid root\");\n\n // Store the root at the given timestamp\n _beaconRoots[block.timestamp] = root;\n\n emit RootSet(block.timestamp, root);\n }\n\n function parentBlockRoot(uint64 timestamp)\n external\n view\n returns (bytes32 parentRoot)\n {\n return BeaconRoots.parentBlockRoot(timestamp);\n }\n\n function latestBlockRoot()\n external\n view\n returns (bytes32 parentRoot, uint64 timestamp)\n {\n timestamp = uint64(block.timestamp);\n parentRoot = BeaconRoots.parentBlockRoot(timestamp);\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconOracle Address of the Beacon Oracle contract that maps block numbers to slots\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconOracle,\n address _beaconProofs\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconOracle,\n _beaconProofs\n )\n {\n SSV_TOKEN = _ssvToken;\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract.\n /// @param _rewardTokenAddresses Address of reward token for platform\n /// @param _assets Addresses of initial supported assets\n /// @param _pTokens Platform Token corresponding addresses\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused for validator consolidation.\n /// @param _asset Address of asset to deposit. Has to be WETH.\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused for validator consolidation.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// Will revert if the strategy is paused for validator consolidation.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant whenNotPaused {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _asset, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertWethToEth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(_asset, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n /// Will revert if the strategy is paused for validator consolidation.\n function withdrawAll()\n external\n override\n onlyVaultOrGovernor\n nonReentrant\n whenNotPaused\n {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, WETH, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Returns the last verified balance of validator deposits, validator balance,\n /// WETH and ETH in the strategy contract.\n /// @param _asset Address of weth asset\n /// @return balance Total value of (W)ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by strategy.\n /// @param _asset The address of the asset token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for deposits\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instread of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract,\n /// but so does withdrawals from validators. It's too complex to separate the two\n /// so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IConsolidationSource } from \"../../interfaces/IConsolidation.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\nimport { IBeaconOracle } from \"../../interfaces/IBeaconOracle.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit, remove and consolidate validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable, Pausable {\n using SafeERC20 for IERC20;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Oracle contract that maps block numbers to slots\n address public immutable BEACON_ORACLE;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// Deposit data for new compounding validators.\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountWei Amount of ETH in wei that has been deposited to the beacon chain deposit contract\n /// @param blockNumber Block number when the deposit was made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either PENDING or PROVEN\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 blockNumber;\n uint32 depositIndex;\n DepositStatus status;\n }\n /// @notice Mapping of the root of a deposit (depositDataRoot) to its data\n mapping(bytes32 => DepositData) public deposits;\n /// @notice List of deposit roots that are still to be verified as processed on the beacon chain\n bytes32[] public depositsRoots;\n\n // Validator data\n\n struct ValidatorData {\n bytes32 pubKeyHash; // Hash of the validator's public key using the Beacon Chain's format\n uint64 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes and indexes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n ValidatorData[] public verifiedValidators;\n /// @notice State of the new compounding validators with a 0x02 withdrawal credential prefix.\n /// Uses the Beacon chain hashing for BLSPubkey which is\n /// sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => VALIDATOR_STATE) public validatorState;\n\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n uint64 timestamp;\n uint128 ethBalance;\n }\n // TODO is it more efficient to use the block root rather than hashing it?\n /// @notice Mapping of the block root to the balances at that slot\n mapping(bytes32 => Balances) public snappedBalances;\n uint64 public lastSnapTimestamp;\n uint128 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n bytes32 public consolidationLastPubKeyHash;\n address public consolidationSourceStrategy;\n mapping(address => bool) public consolidationSourceStrategies;\n\n // For future use\n uint256[50] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITED, // The validator has been verified to have a zero balance\n REMOVED // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified and is ready to be staked\n }\n\n event RegistratorChanged(address indexed newAddress);\n event SourceStrategyAdded(address indexed strategy);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n bytes32 indexed depositDataRoot,\n bytes pubKey,\n uint256 amountWei\n );\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint64 indexed validatorIndex\n );\n event DepositVerified(bytes32 indexed depositDataRoot, uint256 amountWei);\n event BalancesSnapped(\n uint256 indexed timestamp,\n bytes32 indexed blockRoot,\n uint256 ethBalance\n );\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 wethBalance,\n uint256 ethBalance\n );\n event ConsolidationRequested(\n bytes32 indexed lastSourcePubKeyHash,\n bytes32 indexed targetPubKeyHash,\n address indexed sourceStrategy\n );\n event ConsolidationVerified(\n bytes32 indexed lastSourcePubKeyHash,\n uint64 indexed lastValidatorIndex,\n uint256 consolidationCount\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconOracle Address of the Beacon Oracle contract that maps block numbers to slots\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconOracle,\n address _beaconProofs\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_ORACLE = _beaconOracle;\n BEACON_PROOFS = _beaconProofs;\n }\n\n /***************************************\n Admin Functions\n ****************************************/\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Adds support for a legacy staking strategy that can be used for consolidation.\n function addSourceStrategy(address _strategy) external onlyGovernor {\n consolidationSourceStrategies[_strategy] = true;\n\n emit SourceStrategyAdded(_strategy);\n }\n\n /***************************************`\n Validator Management\n ****************************************/\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validatorState[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validator validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validator,\n uint64 depositAmountGwei\n ) external onlyRegistrator whenNotPaused {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertEthToWeth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validator.pubkey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == VALIDATOR_STATE.REGISTERED ||\n currentState == VALIDATOR_STATE.VERIFIED),\n \"Not registered or verified\"\n );\n require(\n currentState == VALIDATOR_STATE.VERIFIED ||\n depositAmountWei == 1 ether,\n \"First deposit not 1 ETH\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validator.pubkey,\n withdrawalCredentials,\n validator.signature,\n validator.depositDataRoot\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == VALIDATOR_STATE.REGISTERED) {\n validatorState[pubKeyHash] = VALIDATOR_STATE.STAKED;\n }\n // Store the deposit data for verifyDeposit and verifyBalances\n deposits[validator.depositDataRoot] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n blockNumber: SafeCast.toUint64(block.number),\n depositIndex: SafeCast.toUint32(depositsRoots.length),\n status: DepositStatus.PENDING\n });\n depositsRoots.push(validator.depositDataRoot);\n\n emit ETHStaked(\n pubKeyHash,\n validator.depositDataRoot,\n validator.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// If the remaining balance is < 32 ETH then the validator will be exited.\n /// That can result in the ETH sent to the strategy being more than the requested amount.\n /// The staked ETH will eventually be withdrawn to this staking strategy.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n onlyRegistrator\n whenNotPaused\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove a sweeping validator from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == VALIDATOR_STATE.EXITED ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exited\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validatorState[pubKeyHash] = VALIDATOR_STATE.REMOVED;\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /// @notice Receives requests from supported legacy strategies to consolidate sweeping validators to\n /// a new compounding validator on this new strategy.\n /// @param lastSourcePubKeyHash The last source validator to be consolidated hashed using the Beacon Chain's format.\n /// @param targetPubKeyHash The target validator's public key hash using the Beacon Chain's format.\n function requestConsolidation(\n bytes32 lastSourcePubKeyHash,\n bytes32 targetPubKeyHash\n ) external whenNotPaused {\n require(\n consolidationSourceStrategies[msg.sender],\n \"Not a source strategy\"\n );\n\n // The target validator must be a compounding validator that has been verified\n require(\n validatorState[targetPubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Target validator not verified\"\n );\n\n // Ensure balances can not be verified until after the consolidation has been verified.\n lastSnapTimestamp = 0;\n\n // Store consolidation state\n consolidationLastPubKeyHash = lastSourcePubKeyHash;\n consolidationSourceStrategy = msg.sender;\n\n // Pause the strategy while the consolidation is in progress\n _pause();\n\n emit ConsolidationRequested(\n lastSourcePubKeyHash,\n targetPubKeyHash,\n msg.sender\n );\n }\n\n /***************************************\n SSV Management\n ****************************************/\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n Beacon Chain Proofs\n ****************************************/\n\n /// @notice Verifies a validator's index to its public key.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param validatorPubKeyProof The merkle proof that the validator's index matches its public key\n /// on the beacon chain.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.STAKED,\n \"Validator not staked\"\n );\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key\n IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex\n );\n\n // TODO verify the validator's withdrawal credential points to this strategy\n\n // Store the validator state as verified\n validatorState[pubKeyHash] = VALIDATOR_STATE.VERIFIED;\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(\n ValidatorData({ pubKeyHash: pubKeyHash, index: validatorIndex })\n );\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n /// @param depositDataRoot The root of the deposit data that was stored when\n /// the deposit was made on the execution layer.\n /// @param depositBlockNumber A block number that is on or after the block the deposit\n /// was made on the execution layer.\n /// @param processedSlot Any slot on or after the deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// @param firstPendingDepositSlot The slot of the first pending deposit in the beacon chain\n /// @param firstPendingDepositSlotProof The merkle proof that the slot of the first pending deposit\n /// matches the beacon chain.\n /// BeaconBlock.BeaconBlockBody.deposits[0].slot\n function verifyDeposit(\n bytes32 depositDataRoot,\n uint64 depositBlockNumber,\n uint64 processedSlot,\n uint64 firstPendingDepositSlot,\n bytes calldata firstPendingDepositSlotProof\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositDataRoot];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n validatorState[deposit.pubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n // The block number mapped to a slot needs to be the same block or after the deposit\n // was created on the execution layer.\n require(\n deposit.blockNumber <= depositBlockNumber,\n \"Deposit block before deposit\"\n );\n\n // Get the slot that is on or after the deposit was made on the execution layer.\n uint64 depositSlot = IBeaconOracle(BEACON_ORACLE).blockToSlot(\n depositBlockNumber\n );\n\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the first pending deposit's slot is 0 then there are no pending deposits so\n // our deposit must have been processed on the beacon chain.\n require(\n depositSlot < firstPendingDepositSlot ||\n firstPendingDepositSlot == 0,\n \"Deposit not processed\"\n );\n\n // The slot of the execution layer deposit must be before\n // the slot that the deposit was processed on the beacon chain.\n require(depositSlot < processedSlot, \"Slot not after deposit\");\n\n // Get the beacon block root for the slot that is on or after the deposit was processed on the beacon chain.\n bytes32 blockRoot = IBeaconOracle(BEACON_ORACLE).slotToRoot(\n processedSlot\n );\n\n // Verify the slot of the first pending deposit matches the beacon chain\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n blockRoot,\n firstPendingDepositSlot,\n firstPendingDepositSlotProof\n );\n\n // After verifying the proof, update the contract storage\n deposits[depositDataRoot].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n depositsRoots[deposit.depositIndex] = depositsRoots[\n depositsRoots.length - 1\n ];\n // Delete the last deposit from the list\n depositsRoots.pop();\n\n emit DepositVerified(\n depositDataRoot,\n uint256(deposit.amountGwei) * 1 gwei\n );\n }\n\n // TODO what if the last validator was exited rather than consolidated?\n function verifyConsolidation(\n uint64 parentBlockTimestamp,\n uint64 lastValidatorIndex,\n bytes calldata validatorPubKeyProof,\n bytes32 balancesLeaf,\n bytes calldata validatorBalanceProof\n ) external onlyRegistrator {\n bytes32 consolidationLastPubKeyHashMem = consolidationLastPubKeyHash;\n require(\n consolidationLastPubKeyHashMem != bytes32(0),\n \"No consolidations\"\n );\n\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(parentBlockTimestamp);\n // Verify the validator index has the same public key as the last source validator\n IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n blockRoot,\n consolidationLastPubKeyHashMem,\n validatorPubKeyProof,\n lastValidatorIndex\n );\n\n // Verify the balance of the last validator in the consolidation batch\n // is zero. If its not then the consolidation has not been completed.\n // This proof is to the beacon block root, not the balances container root.\n uint256 validatorBalance = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n blockRoot,\n balancesLeaf,\n validatorBalanceProof,\n lastValidatorIndex,\n IBeaconProofs.BalanceProofLevel.BeaconBlock\n );\n require(validatorBalance == 0, \"Last validator balance not zero\");\n\n // Call the old sweeping strategy to confirm the consolidation has been completed.\n // This will decrease the balance of the source strategy by 32 ETH for each validator being consolidated.\n uint256 consolidationCount = IConsolidationSource(\n consolidationSourceStrategy\n ).confirmConsolidation();\n\n // Increase the balance of this strategy by 32 ETH for each validator being consolidated.\n // This nets out the decrease in the source strategy's balance.\n lastVerifiedEthBalance += SafeCast.toUint128(\n consolidationCount * 32 ether\n );\n\n // Reset the stored consolidation state\n consolidationLastPubKeyHash = bytes32(0);\n consolidationSourceStrategy = address(0);\n\n emit ConsolidationVerified(\n consolidationLastPubKeyHashMem,\n lastValidatorIndex,\n consolidationCount\n );\n\n // Unpause now the balance of the target validator has been verified\n _unpause();\n\n // Take a snap of the balances so the actual balances of the new validator balances can be verified\n _snapBalances();\n }\n\n /// @notice Stores the current ETH balance at the current block.\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n /// Can not be called while a consolidation is in progress.\n function snapBalances() public whenNotPaused {\n _snapBalances();\n }\n\n function _snapBalances() internal {\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(\n SafeCast.toUint64(block.timestamp)\n );\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the balances in the mapping\n snappedBalances[blockRoot] = Balances({\n timestamp: SafeCast.toUint64(block.timestamp),\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n // Store the snapped timestamp\n lastSnapTimestamp = SafeCast.toUint64(block.timestamp);\n\n emit BalancesSnapped(block.timestamp, blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct VerifyBalancesParams {\n bytes32 blockRoot;\n uint64 firstPendingDepositSlot;\n // BeaconBlock.BeaconBlockBody.deposits[0].slot\n bytes firstPendingDepositSlotProof;\n bytes32 balancesContainerRoot;\n // BeaconBlock.state.validators\n bytes validatorContainerProof;\n bytes32[] validatorBalanceLeaves;\n // BeaconBlock.state.validators[validatorIndex].balance\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n function verifyBalances(VerifyBalancesParams calldata params) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalances[params.blockRoot];\n // Check the balances are the latest\n require(lastSnapTimestamp > 0, \"No snapped balances\");\n require(balancesMem.timestamp == lastSnapTimestamp, \"Stale snap\");\n\n uint256 depositsCount = depositsRoots.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit to the beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n params.blockRoot,\n params.firstPendingDepositSlot,\n params.firstPendingDepositSlotProof\n );\n\n uint64 firstPendingDepositBlockNumber = IBeaconOracle(BEACON_ORACLE)\n .slotToBlock(params.firstPendingDepositSlot);\n\n // For each native staking contract's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n bytes32 depositDataRoot = depositsRoots[i];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit block number is before the\n // block number of the staking strategy's deposit.\n // If it has it will need to be verified with `verifyDeposit`\n require(\n firstPendingDepositBlockNumber <\n deposits[depositDataRoot].blockNumber,\n \"Deposit has been processed\"\n );\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei +=\n uint256(deposits[depositDataRoot].amountGwei) *\n 1 gwei;\n }\n }\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n params.blockRoot,\n params.balancesContainerRoot,\n params.validatorContainerProof\n );\n\n // for each validator\n for (uint256 i = 0; i < verifiedValidatorsCount; ++i) {\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n params.balancesContainerRoot,\n params.validatorBalanceLeaves[i],\n params.validatorBalanceProofs[i],\n verifiedValidators[i].index,\n IBeaconProofs.BalanceProofLevel.Container\n );\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += uint256(validatorBalanceGwei) * 1 gwei;\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n validatorState[\n verifiedValidators[i].pubKeyHash\n ] = VALIDATOR_STATE.EXITED;\n\n // Remove the validator from the list of verified validators.\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n // Remove the validator with a zero balance from the list of verified validators\n // Move the last validator to the current index\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n }\n }\n }\n\n uint256 wethBalance = IWETH9(WETH).balanceOf(address(this));\n\n // Store the verified balance in storage\n lastVerifiedEthBalance = SafeCast.toUint128(\n totalDepositsWei + totalValidatorBalance + balancesMem.ethBalance\n );\n // Reset the last snap timestamp so a new snapBalances has to be made\n lastSnapTimestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n wethBalance,\n balancesMem.ethBalance\n );\n }\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /***************************************\n WETH and ETH Accounting\n ****************************************/\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n function _convertWethToEth(uint256 _ethAmount) internal {\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance\n if (lastVerifiedEthBalance > _ethAmount) {\n lastVerifiedEthBalance -= SafeCast.toUint128(_ethAmount);\n } else {\n // This can happen if all ETH in the validators was withdrawn\n // and there was more consensus rewards since the last balance verification.\n // Or it can happen if ETH is donated to this strategy.\n lastVerifiedEthBalance = 0;\n }\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n function _convertEthToWeth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += SafeCast.toUint128(_wethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address public dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/hoodi/solcInputs/feede9596003ed68e08471a2a7c65887.json b/contracts/deployments/hoodi/solcInputs/feede9596003ed68e08471a2a7c65887.json new file mode 100644 index 0000000000..4765290beb --- /dev/null +++ b/contracts/deployments/hoodi/solcInputs/feede9596003ed68e08471a2a7c65887.json @@ -0,0 +1,126 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/security/Pausable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "contracts/beacon/BeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"./BeaconProofsLib.sol\";\nimport { IBeaconProofs } from \"../interfaces/IBeaconProofs.sol\";\n\n/**\n * @title Verifies merkle proofs of beacon chain data.\n * @author Origin Protocol Inc\n */\ncontract BeaconProofs is IBeaconProofs {\n /// @notice Verifies the validator index is for the given validator public key.\n /// Also verify the validator's withdrawal credential points to the withdrawal address.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param proof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata proof,\n uint40 validatorIndex,\n address withdrawalAddress\n ) external view {\n BeaconProofsLib.verifyValidator(\n beaconBlockRoot,\n pubKeyHash,\n proof,\n validatorIndex,\n withdrawalAddress\n );\n }\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view {\n BeaconProofsLib.verifyValidatorWithdrawableEpoch(\n beaconBlockRoot,\n validatorIndex,\n withdrawableEpoch,\n withdrawableEpochProof\n );\n }\n\n /// @notice Verifies the balances container to the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerRoot The merkle root of the the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata balancesContainerProof\n ) external view {\n BeaconProofsLib.verifyBalancesContainer(\n beaconBlockRoot,\n balancesContainerRoot,\n balancesContainerProof\n );\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param balanceProof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint40 validatorIndex\n ) external view returns (uint256 validatorBalanceGwei) {\n validatorBalanceGwei = BeaconProofsLib.verifyValidatorBalance(\n balancesContainerRoot,\n validatorBalanceLeaf,\n balanceProof,\n validatorIndex\n );\n }\n\n /// @notice Verifies the pending deposits container to the beacon block root.\n /// BeaconBlock.state.pendingDeposits\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param pendingDepositsContainerRoot The merkle root of the the pending deposits container.\n /// @param proof The merkle proof for the pending deposits container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyPendingDepositsContainer(\n bytes32 beaconBlockRoot,\n bytes32 pendingDepositsContainerRoot,\n bytes calldata proof\n ) external view {\n BeaconProofsLib.verifyPendingDepositsContainer(\n beaconBlockRoot,\n pendingDepositsContainerRoot,\n proof\n );\n }\n\n /// @notice Verified a pending deposit to the root of the Pending Deposits container.\n /// @param pendingDepositsContainerRoot The merkle root of the Pending Deposits container.\n /// @param pendingDepositRoot The leaf node containing the validator balance with three other balances.\n /// @param proof The merkle proof for the pending deposit root to the Pending Deposits container root.\n /// This is 28 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param pendingDepositIndex The pending deposit index in the Pending Deposits container\n function verifyPendingDeposit(\n bytes32 pendingDepositsContainerRoot,\n bytes32 pendingDepositRoot,\n bytes calldata proof,\n uint32 pendingDepositIndex\n ) external view {\n BeaconProofsLib.verifyPendingDeposit(\n pendingDepositsContainerRoot,\n pendingDepositRoot,\n proof,\n pendingDepositIndex\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the slot of the first pending deposit to the beacon block root.\n /// BeaconBlock.state.pendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty.\n /// @param firstPendingDepositSlotProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view returns (bool isEmptyDepositQueue) {\n isEmptyDepositQueue = BeaconProofsLib.verifyFirstPendingDeposit(\n beaconBlockRoot,\n slot,\n firstPendingDepositSlotProof\n );\n }\n\n /// @notice Merkleizes a beacon chain pending deposit.\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param withdrawalCredentials The 32 byte withdrawal credentials.\n /// @param amountGwei The amount of the deposit in Gwei.\n /// @param signature The 96 byte BLS signature.\n /// @param slot The beacon chain slot the deposit was made in.\n /// @return root The merkle root of the pending deposit.\n function merkleizePendingDeposit(\n bytes32 pubKeyHash,\n bytes calldata withdrawalCredentials,\n uint64 amountGwei,\n bytes calldata signature,\n uint64 slot\n ) external pure returns (bytes32) {\n return\n BeaconProofsLib.merkleizePendingDeposit(\n pubKeyHash,\n withdrawalCredentials,\n amountGwei,\n signature,\n slot\n );\n }\n\n /// @notice Merkleizes a BLS signature used for validator deposits.\n /// @param signature The 96 byte BLS signature.\n /// @return root The merkle root of the signature.\n function merkleizeSignature(bytes calldata signature)\n external\n pure\n returns (bytes32 root)\n {\n return BeaconProofsLib.merkleizeSignature(signature);\n }\n}\n" + }, + "contracts/beacon/BeaconProofsLib.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Merkle } from \"./Merkle.sol\";\nimport { Endian } from \"./Endian.sol\";\n\n/**\n * @title Library to verify merkle proofs of beacon chain data.\n * @author Origin Protocol Inc\n */\nlibrary BeaconProofsLib {\n // Known generalized indices in the beacon block\n /// @dev BeaconBlock.state.PendingDeposits[0]\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, pending deposits at index 34\n /// Pending deposits container: height 28, first deposit at index 0\n /// ((2 ^ 3 + 3) * 2 ^ 6 + 34) * 2 ^ 28 + 0 = 198105366528\n uint256 internal constant FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX =\n 198105366528;\n /// @dev BeaconBlock.state.PendingDeposits[0].pubkey\n /// Pending Deposit container: height 3, pubkey at index 4\n /// (((2 ^ 3 + 3) * 2 ^ 6 + 34) * 2 ^ 28 + 0) * 2 ^ 3 + 4 = 1584842932228\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX =\n 1584842932228;\n /// @dev BeaconBlock.state.validators\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, validators at index 11\n /// (2 ^ 3 + 3) * 2 ^ 6 + 11 = 715\n uint256 internal constant VALIDATORS_CONTAINER_GENERALIZED_INDEX = 715;\n /// @dev BeaconBlock.state.balances\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, balances at index 12\n /// (2 ^ 3 + 3) * 2 ^ 6 + 12 = 716\n uint256 internal constant BALANCES_CONTAINER_GENERALIZED_INDEX = 716;\n\n /// @dev BeaconBlock.state.pendingDeposits\n /// Beacon block container: height 3, state at at index 3\n /// Beacon state container: height 6, balances at index 34\n /// (2 ^ 3 + 3) * 2 ^ 6 + 34 = 738\n uint256 internal constant PENDING_DEPOSITS_CONTAINER_GENERALIZED_INDEX =\n 738;\n\n /// @dev Number of bytes in the proof to the first pending deposit.\n /// 37 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0]\n /// 37 * 32 bytes = 1184 bytes\n uint256 internal constant FIRST_PENDING_DEPOSIT_PROOF_LENGTH = 1184;\n /// @dev Number of bytes in the proof from the slot of the first pending deposit to the beacon block root.\n /// 40 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0].slot\n /// 40 * 32 bytes = 1280 bytes\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_PROOF_LENGTH = 1280;\n\n /// @dev Merkle height of the Balances container\n /// BeaconBlock.state.balances\n uint256 internal constant BALANCES_HEIGHT = 39;\n /// @dev Merkle height of the Validators container list\n /// BeaconBlock.state.validators\n uint256 internal constant VALIDATORS_LIST_HEIGHT = 41;\n /// @dev Merkle height of the Pending Deposits container list\n /// BeaconBlock.state.pendingDeposits\n uint256 internal constant PENDING_DEPOSITS_LIST_HEIGHT = 28;\n /// @dev Merkle height of the Validator container\n /// BeaconBlock.state.validators[validatorIndex]\n uint256 internal constant VALIDATOR_CONTAINER_HEIGHT = 3;\n\n /// @dev Position of the pubkey field in the Validator container.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;\n /// @dev Position of the withdrawable epoch field in the Validator container.\n /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n uint256 internal constant VALIDATOR_WITHDRAWABLE_EPOCH_INDEX = 7;\n\n /// @notice Verifies the validator index is for the given validator public key.\n /// Also verify the validator's withdrawal credential points to the withdrawal address.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param proof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata proof,\n uint40 validatorIndex,\n address withdrawalAddress\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.validators[validatorIndex]\n uint256 generalizedIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_LIST_HEIGHT,\n validatorIndex\n );\n // BeaconBlock.state.validators[validatorIndex].pubkey\n generalizedIndex = concatGenIndices(\n generalizedIndex,\n VALIDATOR_CONTAINER_HEIGHT,\n VALIDATOR_PUBKEY_INDEX\n );\n\n // Get the withdrawal credentials from the first witness in the pubkey merkle proof.\n bytes32 withdrawalCredentialsFromProof = bytes32(proof[:32]);\n bytes32 withdrawalCredentials = bytes32(\n abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(withdrawalAddress)\n )\n );\n\n require(\n withdrawalCredentialsFromProof == withdrawalCredentials,\n \"Invalid withdrawal cred\"\n );\n\n require(\n // 53 * 32 bytes = 1696 bytes\n proof.length == 1696 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: pubKeyHash,\n index: generalizedIndex\n }),\n \"Invalid validator proof\"\n );\n }\n\n /// @notice Verifies a validator's withdrawable epoch to the beacon block root\n /// for a given validator index.\n /// BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n /// @param beaconBlockRoot The root of the beacon block\n /// @param validatorIndex The validator index to verify the withdrawable epoch for.\n /// @param withdrawableEpoch The withdrawable epoch to verify in big endian uint64 format\n /// @param proof The merkle proof for the validator's withdrawable epoch to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyValidatorWithdrawableEpoch(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata proof\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.validators[validatorIndex]\n uint256 exitEpochGenIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_LIST_HEIGHT,\n validatorIndex\n );\n // BeaconBlock.state.validators[validatorIndex].withdrawableEpoch\n exitEpochGenIndex = concatGenIndices(\n exitEpochGenIndex,\n VALIDATOR_CONTAINER_HEIGHT,\n VALIDATOR_WITHDRAWABLE_EPOCH_INDEX\n );\n\n require(\n // 53 * 32 bytes = 1696 bytes\n proof.length == 1696 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: Endian.toLittleEndianUint64(withdrawableEpoch),\n index: exitEpochGenIndex\n }),\n \"Invalid withdrawable proof\"\n );\n }\n\n /// @notice Verifies the balances container to the beacon block root.\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param balancesContainerRoot The merkle root of the the balances container.\n /// @param proof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata proof\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.balances\n require(\n // 9 * 32 bytes = 288 bytes\n proof.length == 288 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: balancesContainerRoot,\n index: BALANCES_CONTAINER_GENERALIZED_INDEX\n }),\n \"Invalid balance container proof\"\n );\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param proof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for.\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index.\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata proof,\n uint40 validatorIndex\n ) internal view returns (uint256 validatorBalanceGwei) {\n require(balancesContainerRoot != bytes32(0), \"Invalid container root\");\n\n // Four balances are stored in each leaf so the validator index is divided by 4\n uint64 balanceIndex = validatorIndex / 4;\n\n // Get the index within the balances container, not the Beacon Block\n // BeaconBlock.state.balances[balanceIndex]\n uint256 generalizedIndex = concatGenIndices(\n 1,\n BALANCES_HEIGHT,\n balanceIndex\n );\n\n validatorBalanceGwei = balanceAtIndex(\n validatorBalanceLeaf,\n validatorIndex\n );\n\n require(\n // 39 * 32 bytes = 1248 bytes\n proof.length == 1248 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: balancesContainerRoot,\n leaf: validatorBalanceLeaf,\n index: generalizedIndex\n }),\n \"Invalid balance proof\"\n );\n }\n\n /// @notice Verifies the pending deposits container to the beacon block root.\n /// BeaconBlock.state.pendingDeposits\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param pendingDepositsContainerRoot The merkle root of the the pending deposits container.\n /// @param proof The merkle proof for the pending deposits container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyPendingDepositsContainer(\n bytes32 beaconBlockRoot,\n bytes32 pendingDepositsContainerRoot,\n bytes calldata proof\n ) internal view {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // BeaconBlock.state.pendingDeposits\n require(\n // 9 * 32 bytes = 288 bytes\n proof.length == 288 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: pendingDepositsContainerRoot,\n index: PENDING_DEPOSITS_CONTAINER_GENERALIZED_INDEX\n }),\n \"Invalid deposit container proof\"\n );\n }\n\n /// @notice Verifies a pending deposit in the pending deposits container.\n /// BeaconBlock.state.pendingDeposits[depositIndex]\n /// @param pendingDepositsContainerRoot The merkle root of the pending deposits list container\n /// @param pendingDepositRoot The merkle root of the pending deposit to verify\n /// @param proof The merkle proof for the pending deposit root to the pending deposits list container root.\n /// This is 28 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param pendingDepositIndex The index in the pending deposits list container for the deposit to verify.\n function verifyPendingDeposit(\n bytes32 pendingDepositsContainerRoot,\n bytes32 pendingDepositRoot,\n bytes calldata proof,\n uint32 pendingDepositIndex\n ) internal view {\n require(pendingDepositsContainerRoot != bytes32(0), \"Invalid root\");\n // ssz-merkleizing a list which has a variable length, an additional\n // sha256(pending_deposits_root, pending_deposits_length) operation is done to get the actual pending deposits root\n // so the max pending deposit index is 2^(28 - 1)\n require(\n pendingDepositIndex < 2**(PENDING_DEPOSITS_LIST_HEIGHT - 1),\n \"Invalid deposit index\"\n );\n\n // BeaconBlock.state.pendingDeposits[depositIndex]\n uint256 generalizedIndex = concatGenIndices(\n 1,\n PENDING_DEPOSITS_LIST_HEIGHT,\n pendingDepositIndex\n );\n\n require(\n // 28 * 32 bytes = 896 bytes\n proof.length == 896 &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: pendingDepositsContainerRoot,\n leaf: pendingDepositRoot,\n index: generalizedIndex\n }),\n \"Invalid deposit proof\"\n );\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the slot of the first pending deposit to the beacon block root.\n /// BeaconBlock.state.pendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty.\n /// @param proof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata proof\n ) internal view returns (bool isEmptyDepositQueue) {\n require(beaconBlockRoot != bytes32(0), \"Invalid block root\");\n\n // If the deposit queue is empty\n if (proof.length == FIRST_PENDING_DEPOSIT_PROOF_LENGTH) {\n require(\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: bytes32(0),\n index: FIRST_PENDING_DEPOSIT_GENERALIZED_INDEX\n }),\n \"Invalid empty deposits proof\"\n );\n return true;\n }\n\n // Verify the public key of the first pending deposit\n // BeaconBlock.state.PendingDeposits[0].slot\n require(\n proof.length == FIRST_PENDING_DEPOSIT_SLOT_PROOF_LENGTH &&\n Merkle.verifyInclusionSha256({\n proof: proof,\n root: beaconBlockRoot,\n leaf: Endian.toLittleEndianUint64(slot),\n index: FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX\n }),\n \"Invalid deposit slot proof\"\n );\n }\n\n /// @notice Merkleizes a beacon chain pending deposit.\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param withdrawalCredentials The 32 byte withdrawal credentials.\n /// @param amountGwei The amount of the deposit in Gwei.\n /// @param signature The 96 byte BLS signature.\n /// @param slot The beacon chain slot the deposit was made in.\n /// @return root The merkle root of the pending deposit.\n function merkleizePendingDeposit(\n bytes32 pubKeyHash,\n bytes calldata withdrawalCredentials,\n uint64 amountGwei,\n bytes calldata signature,\n uint64 slot\n ) internal pure returns (bytes32 root) {\n bytes32[] memory leaves = new bytes32[](8);\n leaves[0] = pubKeyHash;\n leaves[1] = bytes32(withdrawalCredentials[:32]);\n leaves[2] = Endian.toLittleEndianUint64(amountGwei);\n leaves[3] = merkleizeSignature(signature);\n leaves[4] = Endian.toLittleEndianUint64(slot);\n leaves[5] = bytes32(0);\n leaves[6] = bytes32(0);\n leaves[7] = bytes32(0);\n\n root = Merkle.merkleizeSha256(leaves);\n }\n\n /// @notice Merkleizes a BLS signature used for validator deposits.\n /// @param signature The 96 byte BLS signature.\n /// @return root The merkle root of the signature.\n function merkleizeSignature(bytes calldata signature)\n internal\n pure\n returns (bytes32)\n {\n require(signature.length == 96, \"Invalid signature\");\n\n bytes32[] memory leaves = new bytes32[](4);\n leaves[0] = bytes32(signature[:32]);\n leaves[1] = bytes32(signature[32:64]);\n leaves[2] = bytes32(signature[64:96]);\n leaves[3] = bytes32(0);\n\n return Merkle.merkleizeSha256(leaves);\n }\n\n ////////////////////////////////////////////////////\n /// Internal Helper Functions\n ////////////////////////////////////////////////////\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint40 validatorIndex)\n internal\n pure\n returns (uint256)\n {\n uint256 bitShiftAmount = (validatorIndex % 4) * 64;\n return\n Endian.fromLittleEndianUint64(\n bytes32((uint256(validatorBalanceLeaf) << bitShiftAmount))\n );\n }\n\n /// @notice Concatenates two beacon chain generalized indices into one.\n /// @param genIndex The first generalized index or 1 if calculating for a single container.\n /// @param height The merkle tree height of the second container. eg 39 for balances, 41 for validators.\n /// @param index The index within the second container. eg the validator index.\n /// @return genIndex The concatenated generalized index.\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) internal pure returns (uint256) {\n return (genIndex << height) | index;\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to retrieve beacon block roots.\n * @author Origin Protocol Inc\n */\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the beacon block root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Roots contract to get the parent block root.\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/Endian.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n/**\n * @title Library to handle conversion between little-endian and big-endian formats.\n * @author Origin Protocol Inc\n */\nlibrary Endian {\n /**\n * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64\n * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type\n * @return n The big endian-formatted uint64\n * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits),\n * but it is immediately truncated to a uint64 (i.e. 64 bits)\n * through a right-shift/shr operation.\n */\n function fromLittleEndianUint64(bytes32 lenum)\n internal\n pure\n returns (uint64 n)\n {\n // the number needs to be stored in little-endian encoding (ie in bytes 0-8)\n n = uint64(uint256(lenum >> 192));\n // forgefmt: disable-next-item\n return\n (n >> 56) |\n ((0x00FF000000000000 & n) >> 40) |\n ((0x0000FF0000000000 & n) >> 24) |\n ((0x000000FF00000000 & n) >> 8) |\n ((0x00000000FF000000 & n) << 8) |\n ((0x0000000000FF0000 & n) << 24) |\n ((0x000000000000FF00 & n) << 40) |\n ((0x00000000000000FF & n) << 56);\n }\n\n function toLittleEndianUint64(uint64 benum)\n internal\n pure\n returns (bytes32 n)\n {\n // Convert to little-endian by reversing byte order\n uint64 reversed = (benum >> 56) |\n ((0x00FF000000000000 & benum) >> 40) |\n ((0x0000FF0000000000 & benum) >> 24) |\n ((0x000000FF00000000 & benum) >> 8) |\n ((0x00000000FF000000 & benum) << 8) |\n ((0x0000000000FF0000 & benum) << 24) |\n ((0x000000000000FF00 & benum) << 40) |\n ((0x00000000000000FF & benum) << 56);\n\n // Store the little-endian uint64 in the least significant 64 bits of bytes32\n n = bytes32(uint256(reversed));\n // Shift to most significant bits\n n = n << 192;\n }\n}\n" + }, + "contracts/beacon/Merkle.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev These functions deal with verification of Merkle Tree proofs.\n *\n * The tree and the proofs can be generated using our\n * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].\n * You will find a quickstart guide in the readme.\n *\n * WARNING: You should avoid using leaf values that are 64 bytes long prior to\n * hashing, or use a hash function other than keccak256 for hashing leaves.\n * This is because the concatenation of a sorted pair of internal nodes in\n * the merkle tree could be reinterpreted as a leaf value.\n * OpenZeppelin's JavaScript library generates merkle trees that are safe\n * against this attack out of the box.\n */\nlibrary Merkle {\n error InvalidProofLength();\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function verifyInclusionSha256(\n bytes memory proof,\n bytes32 root,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bool) {\n return processInclusionProofSha256(proof, leaf, index) == root;\n }\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * _Available since v4.4._\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function processInclusionProofSha256(\n bytes memory proof,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bytes32) {\n require(\n proof.length != 0 && proof.length % 32 == 0,\n InvalidProofLength()\n );\n bytes32[1] memory computedHash = [leaf];\n for (uint256 i = 32; i <= proof.length; i += 32) {\n if (index % 2 == 0) {\n // if ith bit of index is 0, then computedHash is a left sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(computedHash))\n mstore(0x20, mload(add(proof, i)))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n }\n } else {\n // if ith bit of index is 1, then computedHash is a right sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(add(proof, i)))\n mstore(0x20, mload(computedHash))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n }\n }\n index = index / 2;\n }\n return computedHash[0];\n }\n\n /**\n * @notice Returns the merkle root of a tree created from a set of leaves using sha256 as its hash function.\n * @param leaves the leaves of the merkle tree\n * @return The computed Merkle root of the tree.\n * @dev A pre-condition to this function is that leaves.length is a power of two.\n * If not, the function will merkleize the inputs incorrectly.\n */\n function merkleizeSha256(bytes32[] memory leaves)\n internal\n pure\n returns (bytes32)\n {\n //there are half as many nodes in the layer above the leaves\n uint256 numNodesInLayer = leaves.length / 2;\n //create a layer to store the internal nodes\n bytes32[] memory layer = new bytes32[](numNodesInLayer);\n //fill the layer with the pairwise hashes of the leaves\n for (uint256 i = 0; i < numNodesInLayer; i++) {\n layer[i] = sha256(\n abi.encodePacked(leaves[2 * i], leaves[2 * i + 1])\n );\n }\n //the next layer above has half as many nodes\n numNodesInLayer /= 2;\n //while we haven't computed the root\n while (numNodesInLayer != 0) {\n //overwrite the first numNodesInLayer nodes in layer with the pairwise hashes of their children\n for (uint256 i = 0; i < numNodesInLayer; i++) {\n layer[i] = sha256(\n abi.encodePacked(layer[2 * i], layer[2 * i + 1])\n );\n }\n //the next layer above has half as many nodes\n numNodesInLayer /= 2;\n }\n //the first node in the layer is the root\n return layer[0];\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Library to request full or partial withdrawals from validators on the beacon chain.\n * @author Origin Protocol Inc\n */\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n /// @notice Requests a partial withdrawal for a given validator public key and amount.\n /// @param validatorPubKey The public key of the validator to withdraw from\n /// @param amount The amount of ETH to withdraw\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n /// @notice Gets fee for withdrawal requests contract on Beacon chain\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint40 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint40 validatorIndex\n ) external view returns (uint256 validatorBalance);\n\n function verifyPendingDepositsContainer(\n bytes32 beaconBlockRoot,\n bytes32 pendingDepositsContainerRoot,\n bytes calldata proof\n ) external view;\n\n function verifyPendingDeposit(\n bytes32 pendingDepositsContainerRoot,\n bytes32 pendingDepositRoot,\n bytes calldata proof,\n uint32 pendingDepositIndex\n ) external view;\n\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view returns (bool isEmptyDepositQueue);\n\n function merkleizePendingDeposit(\n bytes32 pubKeyHash,\n bytes calldata withdrawalCredentials,\n uint64 amountGwei,\n bytes calldata signature,\n uint64 slot\n ) external pure returns (bytes32 root);\n\n function merkleizeSignature(bytes calldata signature)\n external\n pure\n returns (bytes32 root);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/mocks/beacon/EnhancedBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"../../beacon/BeaconProofsLib.sol\";\nimport { BeaconProofs } from \"../../beacon/BeaconProofs.sol\";\n\ncontract EnhancedBeaconProofs is BeaconProofs {\n function concatGenIndices(\n uint256 index1,\n uint256 height2,\n uint256 index2\n ) external pure returns (uint256 genIndex) {\n return BeaconProofsLib.concatGenIndices(index1, height2, index2);\n }\n}\n" + }, + "contracts/mocks/beacon/MockBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\n// solhint-disable no-unused-vars\n\n/**\n * @title Mock contract for test purposes verifying Merkle proofs\n * @author Origin Protocol Inc\n */\ncontract MockBeaconProofs is IBeaconProofs {\n /// @dev Number of bytes in the proof to the first pending deposit.\n /// 37 witness hashes of 32 bytes each concatenated together.\n /// BeaconBlock.state.PendingDeposits[0]\n /// 37 * 32 bytes = 1184 bytes\n uint256 internal constant FIRST_PENDING_DEPOSIT_PROOF_LENGTH = 1184;\n\n uint256 internal constant DEFAULT_VALIDATOR_BALANCE_GWEI = 33 ether / 1e9;\n // mapping of validator indexes to validator balances\n mapping(uint40 => uint256) public validatorBalances;\n\n function setValidatorBalance(uint40 index, uint256 validatorBalanceGwei)\n external\n {\n // set special max value instead of 0\n if (validatorBalanceGwei == 0) {\n validatorBalances[index] = type(uint256).max;\n } else {\n validatorBalances[index] = validatorBalanceGwei;\n }\n }\n\n /// @notice Verifies the validator index is for the given validator public key.\n /// Also verify the validator's withdrawal credential points to the withdrawal address.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param proof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidator(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata proof,\n uint40 validatorIndex,\n address withdrawalAddress\n ) external view {\n // always pass\n }\n\n function verifyValidatorWithdrawable(\n bytes32 beaconBlockRoot,\n uint40 validatorIndex,\n uint64 withdrawableEpoch,\n bytes calldata withdrawableEpochProof\n ) external view {\n // always pass\n }\n\n /// @notice Verifies the balances container to the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerRoot The merkle root of the the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerRoot,\n bytes calldata balancesContainerProof\n ) external view {\n // always pass\n }\n\n /// @notice Verifies the validator balance to the root of the Balances container.\n /// @param balancesContainerRoot The merkle root of the Balances container.\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances.\n /// @param balanceProof The merkle proof for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 balancesContainerRoot,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint40 validatorIndex\n ) external view returns (uint256 validatorBalanceGwei) {\n uint256 validatorBalance = validatorBalances[validatorIndex];\n\n // special setting representing 0 balance\n if (validatorBalance == type(uint256).max) {\n return 0;\n }\n // validator balance not set by the test cases\n else if (validatorBalance == 0) {\n return DEFAULT_VALIDATOR_BALANCE_GWEI;\n }\n\n return validatorBalance;\n }\n\n function verifyPendingDepositsContainer(\n bytes32 beaconBlockRoot,\n bytes32 pendingDepositsContainerRoot,\n bytes calldata proof\n ) external view {\n // always pass\n }\n\n function verifyPendingDeposit(\n bytes32 pendingDepositsContainerRoot,\n bytes32 pendingDepositRoot,\n bytes calldata proof,\n uint32 pendingDepositIndex\n ) external view {\n // always pass\n }\n\n /// @notice If the deposit queue is not empty,\n /// verify the slot of the first pending deposit to the beacon block root.\n /// BeaconBlock.state.pendingDeposits[0].slot\n /// If the deposit queue is empty, verify the root of the first pending deposit is empty\n /// BeaconBlock.state.PendingDeposits[0]\n /// @param beaconBlockRoot The root of the beacon block.\n /// @param slot The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be anything if the deposit queue is empty.\n /// @param firstPendingDepositSlotProof The merkle proof to the beacon block root. Can be either:\n /// - 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// - 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @return isEmptyDepositQueue True if the deposit queue is empty, false otherwise.\n function verifyFirstPendingDeposit(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view returns (bool isEmptyDepositQueue) {\n if (\n firstPendingDepositSlotProof.length ==\n FIRST_PENDING_DEPOSIT_PROOF_LENGTH\n ) {\n isEmptyDepositQueue = true;\n }\n }\n\n function merkleizePendingDeposit(\n bytes32 pubKeyHash,\n bytes calldata withdrawalCredentials,\n uint64 amountGwei,\n bytes calldata signature,\n uint64 slot\n ) external pure returns (bytes32) {\n return\n keccak256(\n abi.encodePacked(\n pubKeyHash,\n withdrawalCredentials,\n amountGwei,\n signature,\n slot\n )\n );\n }\n\n function merkleizeSignature(bytes calldata signature)\n external\n pure\n returns (bytes32 root)\n {\n return keccak256(abi.encodePacked(signature));\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with\n /// `platformAddress` not used so empty address\n /// `vaultAddress` the address of the OETH Vault contract\n /// @param _wethAddress Address of the WETH Token contract\n /// @param _ssvToken Address of the SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconProofs,\n _beaconGenesisTimestamp\n )\n {\n SSV_TOKEN = _ssvToken;\n\n // Make sure nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// @param _rewardTokenAddresses Not used so empty array\n /// @param _assets Not used so empty array\n /// @param _pTokens Not used so empty array\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n safeApproveAllTokens();\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators, `registerSsvValidator` and `stakeEth` must be used.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH that was transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// @param _recipient Address to receive withdrawn assets.\n /// @param _asset Address of the WETH token.\n /// @param _amount Amount of WETH to withdraw.\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertEthToWeth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(WETH, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Accounts for all the assets managed by this strategy which includes:\n /// 1. The current WETH in this strategy contract\n /// 2. The last verified ETH balance, total deposits and total validator balances\n /// @param _asset Address of WETH asset.\n /// @return balance Total value in ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by the strategy.\n /// @param _asset The address of the WETH token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for validator registration.\n function safeApproveAllTokens() public override {\n // Approves the SSV Network contract to transfer SSV tokens when validators are registered\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n * The new ETH will be accounted for in `checkBalance` after the next snapBalances and verifyBalances txs.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n /// @notice is not supported for this strategy as there is no platform token.\n function setPTokenAddress(address, address) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @notice is not supported for this strategy as there is no platform token.\n function removePToken(uint256) external pure override {\n revert(\"Unsupported function\");\n }\n\n /// @dev This strategy does not use a platform token like the old Aave and Compound strategies.\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instead of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract.\n /// Withdrawals from validators also accumulate as ETH in this strategy contract.\n /// It's too complex to separate the rewards from withdrawals so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can now regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingView.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/**\n * @title Viewing contract for the Compounding Staking Strategy.\n * @author Origin Protocol Inc\n */\ncontract CompoundingStakingStrategyView {\n /// @notice The address of the Compounding Staking Strategy contract\n CompoundingValidatorManager public immutable stakingStrategy;\n\n constructor(address _stakingStrategy) {\n stakingStrategy = CompoundingValidatorManager(_stakingStrategy);\n }\n\n struct ValidatorView {\n bytes32 pubKeyHash;\n uint64 index;\n CompoundingValidatorManager.ValidatorState state;\n }\n\n struct DepositView {\n bytes32 pendingDepositRoot;\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n }\n\n /// @notice Returns the strategy's active validators.\n /// These are the ones that have been verified and have a non-zero balance.\n /// @return validators An array of `ValidatorView` containing the public key hash, validator index and state.\n function getVerifiedValidators()\n external\n view\n returns (ValidatorView[] memory validators)\n {\n uint256 validatorCount = stakingStrategy.verifiedValidatorsLength();\n validators = new ValidatorView[](validatorCount);\n for (uint256 i = 0; i < validatorCount; ++i) {\n bytes32 pubKeyHash = stakingStrategy.verifiedValidators(i);\n (\n CompoundingValidatorManager.ValidatorState state,\n uint64 index\n ) = stakingStrategy.validator(pubKeyHash);\n validators[i] = ValidatorView({\n pubKeyHash: pubKeyHash,\n index: index,\n state: state\n });\n }\n }\n\n /// @notice Returns the deposits that are still to be verified.\n /// These may or may not have been processed by the beacon chain.\n /// @return pendingDeposits An array of `DepositView` containing the deposit ID, public key hash,\n /// amount in Gwei and the slot of the deposit.\n function getPendingDeposits()\n external\n view\n returns (DepositView[] memory pendingDeposits)\n {\n uint256 depositsCount = stakingStrategy.depositListLength();\n pendingDeposits = new DepositView[](depositsCount);\n for (uint256 i = 0; i < depositsCount; ++i) {\n (\n bytes32 pubKeyHash,\n uint64 amountGwei,\n uint64 slot,\n ,\n\n ) = stakingStrategy.deposits(stakingStrategy.depositList(i));\n pendingDeposits[i] = DepositView({\n pendingDepositRoot: stakingStrategy.depositList(i),\n pubKeyHash: pubKeyHash,\n amountGwei: amountGwei,\n slot: slot\n });\n }\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable, Pausable {\n using SafeERC20 for IERC20;\n\n /// @dev The amount of ETH in wei that is required for a deposit to a new validator.\n uint256 internal constant DEPOSIT_AMOUNT_WEI = 1 ether;\n /// @dev Validator balances over this amount will eventually become active on the beacon chain.\n /// Due to hysteresis, if the effective balance is 31 ETH, the actual balance\n /// must rise to 32.25 ETH to trigger an effective balance update to 32 ETH.\n /// https://eth2book.info/capella/part2/incentives/balances/#hysteresis\n uint256 internal constant MIN_ACTIVATION_BALANCE_GWEI = 32.25 ether / 1e9;\n /// @dev The maximum number of deposits that are waiting to be verified as processed on the beacon chain.\n uint256 internal constant MAX_DEPOSITS = 12;\n /// @dev The maximum number of validators that can be verified.\n uint256 internal constant MAX_VERIFIED_VALIDATORS = 48;\n /// @dev The default withdrawable epoch value on the Beacon chain.\n /// A value in the far future means the validator is not exiting.\n uint64 internal constant FAR_FUTURE_EPOCH = type(uint64).max;\n /// @dev The number of seconds between each beacon chain slot.\n uint64 internal constant SLOT_DURATION = 12;\n /// @dev The number of slots in each beacon chain epoch.\n uint64 internal constant SLOTS_PER_EPOCH = 32;\n /// @dev Minimum time in seconds to allow snapped balances to be verified.\n /// Set to 35 slots which is 3 slots more than 1 epoch (32 slots). Deposits get processed\n /// once per epoch. This larger than 1 epoch delay should achieve that `snapBalances` sometimes\n /// get called in the middle (or towards the end) of the epoch. Giving the off-chain script\n /// sufficient time after the end of the epoch to prepare the proofs and call `verifyBalances`.\n /// This is considering a malicious actor would keep calling `snapBalances` as frequent as possible\n /// to disturb our operations.\n uint64 internal constant SNAP_BALANCES_DELAY = 35 * SLOT_DURATION;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address internal immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address internal immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address internal immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n /// @notice The timestamp of the Beacon chain genesis.\n /// @dev this is different on Testnets like Hoodi so is set at deployment time.\n uint64 internal immutable BEACON_GENESIS_TIMESTAMP;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// @notice Deposit data for new compounding validators.\n /// @dev A `VERIFIED` deposit can mean 3 separate things:\n /// - a deposit has been processed by the beacon chain and shall be included in the\n /// balance of the next verifyBalances call\n /// - a deposit has been done to a slashed validator and has probably been recovered\n /// back to this strategy. Probably because we can not know for certain. This contract\n /// only detects when the validator has passed its withdrawal epoch. It is close to impossible\n /// to prove with Merkle Proofs that the postponed deposit this contract is responsible for\n /// creating is not present anymore in BeaconChain.state.pending_deposits. This in effect\n /// means that there might be a period where this contract thinks the deposit has been already\n /// returned as ETH balance before it happens. This will result in some days (or weeks)\n /// -> depending on the size of deposit queue of showing a deficit when calling `checkBalance`.\n /// As this only offsets the yield and doesn't cause a critical double-counting we are not addressing\n /// this issue.\n /// - A deposit has been done to the validator, but our deposit has been front run by a malicious\n /// actor. Funds in the deposit this contract makes are not recoverable.\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified\n }\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountGwei Amount of ETH in gwei that has been deposited to the beacon chain deposit contract\n /// @param slot The beacon chain slot number when the deposit has been made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either UNKNOWN, PENDING or VERIFIED\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 slot;\n uint32 depositIndex;\n DepositStatus status;\n }\n /// @notice Restricts to only one deposit to an unverified validator at a time.\n /// This is to limit front-running attacks of deposits to the beacon chain contract.\n ///\n /// @dev The value is set to true when a deposit to a new validator has been done that has\n /// not yet be verified.\n bool public firstDeposit;\n /// @notice Mapping of the pending deposit roots to the deposit data\n mapping(bytes32 => DepositData) public deposits;\n /// @notice List of strategy deposit IDs to a validator.\n /// The ID is the merkle root of the pending deposit data which is unique for each validator, amount and block.\n /// Duplicate pending deposit roots are prevented so can be used as an identifier to each strategy deposit.\n /// The list can be for deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n /// The list may not be ordered by time of deposit.\n /// Removed deposits will move the last deposit to the removed index.\n bytes32[] public depositList;\n\n enum ValidatorState {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n ACTIVE, // The validator balance is at least 32 ETH. The validator may not yet be active on the beacon chain.\n EXITING, // The validator has been requested to exit or has been verified as forced exit\n EXITED, // The validator has been verified to have a zero balance\n REMOVED, // validator has funds withdrawn to this strategy contract and is removed from the SSV\n INVALID // The validator has been front-run and the withdrawal address is not this strategy\n }\n\n // Validator data\n struct ValidatorData {\n ValidatorState state; // The state of the validator known to this contract\n uint40 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n bytes32[] public verifiedValidators;\n /// @notice Mapping of the hash of the validator's public key to the validator state and index.\n /// Uses the Beacon chain hashing for BLSPubkey which is sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => ValidatorData) public validator;\n\n /// @param blockRoot Beacon chain block root of the snapshot\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n bytes32 blockRoot;\n uint64 timestamp;\n uint128 ethBalance;\n }\n /// @notice Mapping of the block root to the balances at that slot\n Balances public snappedBalance;\n /// @notice The last verified ETH balance of the strategy\n uint256 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[41] private __gap;\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event FirstDepositReset();\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n bytes32 indexed pendingDepositRoot,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint40 indexed validatorIndex\n );\n event ValidatorInvalid(bytes32 indexed pubKeyHash);\n event DepositVerified(\n bytes32 indexed pendingDepositRoot,\n uint256 amountWei\n );\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(bytes32 indexed blockRoot, uint256 ethBalance);\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 ethBalance\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @dev Throws if called by any account other than the Registrator or Governor\n modifier onlyRegistratorOrGovernor() {\n require(\n msg.sender == validatorRegistrator || isGovernor(),\n \"Not Registrator or Governor\"\n );\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n /// @param _beaconGenesisTimestamp The timestamp of the Beacon chain's genesis.\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconProofs,\n uint64 _beaconGenesisTimestamp\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_PROOFS = _beaconProofs;\n BEACON_GENESIS_TIMESTAMP = _beaconGenesisTimestamp;\n\n require(\n block.timestamp > _beaconGenesisTimestamp,\n \"Invalid genesis timestamp\"\n );\n }\n\n /**\n *\n * Admin Functions\n *\n */\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Reset the `firstDeposit` flag to false so deposits to unverified validators can be made again.\n function resetFirstDeposit() external onlyGovernor {\n require(firstDeposit, \"No first deposit\");\n\n firstDeposit = false;\n\n emit FirstDepositReset();\n }\n\n function pause() external onlyRegistratorOrGovernor {\n _pause();\n }\n\n function unPause() external onlyGovernor {\n _unpause();\n }\n\n /**\n *\n * Validator Management\n *\n */\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validator[pubKeyHash].state == ValidatorState.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validator[pubKeyHash].state = ValidatorState.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n struct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n }\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// The the first deposit to a new validator, the amount must be 1 ETH.\n /// Another deposit of at least 31 ETH is required for the validator to be activated.\n /// This second deposit has to be done after the validator has been verified.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// There can not be two deposits to the same validator in the same block for the same amount.\n /// Function is pausable so in case a run-away Registrator can be prevented from continuing\n /// to deposit funds to slashed or undesired validators.\n /// @param validatorStakeData validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth,reentrancy-no-eth\n function stakeEth(\n ValidatorStakeData calldata validatorStakeData,\n uint64 depositAmountGwei\n ) external onlyRegistrator whenNotPaused {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(depositList.length < MAX_DEPOSITS, \"Max deposits\");\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertWethToEth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validatorStakeData.pubkey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can only stake to a validator has have been registered, verified or active.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.VERIFIED ||\n currentState == ValidatorState.ACTIVE),\n \"Not registered or verified\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n if (currentState == ValidatorState.REGISTERED) {\n // Can only have one pending deposit to an unverified validator at a time.\n // This is to limit front-running deposit attacks to a single deposit.\n // The exiting deposit needs to be verified before another deposit can be made.\n // If there was a front-running attack, the validator needs to be verified as invalid\n // and the Governor calls `resetFirstDeposit` to set `firstDeposit` to false.\n require(!firstDeposit, \"Existing first deposit\");\n // Limits the amount of ETH that can be at risk from a front-running deposit attack.\n require(\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n // Limits the number of validator balance proofs to verifyBalances\n require(\n verifiedValidators.length + 1 < MAX_VERIFIED_VALIDATORS,\n \"Max validators\"\n );\n\n // Flag a deposit to an unverified validator so only no other deposits can be made\n // to an unverified validator.\n firstDeposit = true;\n validator[pubKeyHash].state = ValidatorState.STAKED;\n }\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n /// After the Pectra upgrade the validators have a new restriction when proposing\n /// blocks. The timestamps are at strict intervals of 12 seconds from the genesis block\n /// forward. Each slot is created at strict 12 second intervals and those slots can\n /// either have blocks attached to them or not. This way using the block.timestamp\n /// the slot number can easily be calculated.\n uint64 depositSlot = (SafeCast.toUint64(block.timestamp) -\n BEACON_GENESIS_TIMESTAMP) / SLOT_DURATION;\n\n // Calculate the merkle root of the beacon chain pending deposit data.\n // This is used as the unique ID of the deposit.\n bytes32 pendingDepositRoot = IBeaconProofs(BEACON_PROOFS)\n .merkleizePendingDeposit(\n pubKeyHash,\n withdrawalCredentials,\n depositAmountGwei,\n validatorStakeData.signature,\n depositSlot\n );\n require(\n deposits[pendingDepositRoot].status == DepositStatus.UNKNOWN,\n \"Duplicate deposit\"\n );\n\n // Store the deposit data for verifyDeposit and verifyBalances\n deposits[pendingDepositRoot] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n slot: depositSlot,\n depositIndex: SafeCast.toUint32(depositList.length),\n status: DepositStatus.PENDING\n });\n depositList.push(pendingDepositRoot);\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validatorStakeData.pubkey,\n withdrawalCredentials,\n validatorStakeData.signature,\n validatorStakeData.depositDataRoot\n );\n\n emit ETHStaked(\n pubKeyHash,\n pendingDepositRoot,\n validatorStakeData.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth,reentrancy-no-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then only the amount in excess of 32 ETH will be withdrawn.\n /// Only the Registrator can call this function.\n /// 1 wei of value should be sent with the tx to pay for the withdrawal request fee.\n /// If no value sent, 1 wei will be taken from the strategy's ETH balance if it has any.\n /// If no ETH balance, the tx will revert.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n payable\n onlyRegistrator\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorData memory validatorDataMem = validator[pubKeyHash];\n // Validator full withdrawal could be denied due to multiple reasons:\n // - the validator has not been activated or active long enough\n // (current_epoch < activation_epoch + SHARD_COMMITTEE_PERIOD)\n // - the validator has pending balance to withdraw from a previous partial withdrawal request\n //\n // Meaning that the on-chain to beacon chain full withdrawal request could fail. Instead\n // of adding complexity of verifying if a validator is eligible for a full exit, we allow\n // multiple full withdrawal requests per validator.\n require(\n validatorDataMem.state == ValidatorState.ACTIVE ||\n validatorDataMem.state == ValidatorState.EXITING,\n \"Validator not active/exiting\"\n );\n\n // If a full withdrawal (validator exit)\n if (amountGwei == 0) {\n // For each staking strategy's deposits\n uint256 depositsCount = depositList.length;\n for (uint256 i = 0; i < depositsCount; ++i) {\n bytes32 pendingDepositRoot = depositList[i];\n // Check there is no pending deposits to the exiting validator\n require(\n pubKeyHash != deposits[pendingDepositRoot].pubKeyHash,\n \"Pending deposit\"\n );\n }\n\n // Store the validator state as exiting so no more deposits can be made to it.\n // This may already be EXITING if the previous exit request failed. eg the validator\n // was not active long enough.\n validator[pubKeyHash].state = ValidatorState.EXITING;\n }\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove the validator from the SSV Cluster after:\n /// - the validator has been exited from `validatorWithdrawal` or slashed\n /// - the validator has incorrectly registered and can not be staked to\n /// - the initial deposit was front-run and the withdrawal address is not this strategy's address.\n /// Make sure `validatorWithdrawal` is called with a zero amount and the validator has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n ValidatorState currentState = validator[pubKeyHash].state;\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == ValidatorState.REGISTERED ||\n currentState == ValidatorState.EXITED ||\n currentState == ValidatorState.INVALID,\n \"Validator not regd or exited\"\n );\n\n validator[pubKeyHash].state = ValidatorState.REMOVED;\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /**\n *\n * SSV Management\n *\n */\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be withdrawn from the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /**\n *\n * Beacon Chain Proofs\n *\n */\n\n /// @notice Verifies a validator's index to its public key.\n /// Adds to the list of verified validators if the validator's withdrawal address is this strategy's address.\n /// Marks the validator as invalid and removes the deposit if the withdrawal address is not this strategy's address.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param withdrawalAddress The withdrawal address of the validator which should be this strategy's address.\n /// If the withdrawal address is not this strategy's address, the initial deposit was front-run\n /// and the validator is marked as invalid.\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint40 validatorIndex,\n bytes32 pubKeyHash,\n address withdrawalAddress,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validator[pubKeyHash].state == ValidatorState.STAKED,\n \"Validator not staked\"\n );\n\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key.\n // Also verify the validator's withdrawal credential points to the `withdrawalAddress`.\n IBeaconProofs(BEACON_PROOFS).verifyValidator(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n withdrawalAddress\n );\n\n // Store the validator state as verified\n validator[pubKeyHash] = ValidatorData({\n state: ValidatorState.VERIFIED,\n index: validatorIndex\n });\n\n // If the initial deposit was front-run and the withdrawal address is not this strategy\n if (withdrawalAddress != address(this)) {\n // override the validator state\n validator[pubKeyHash].state = ValidatorState.INVALID;\n\n // Find and remove the deposit as the funds can not be recovered\n uint256 depositCount = depositList.length;\n for (uint256 i = 0; i < depositCount; i++) {\n DepositData memory deposit = deposits[depositList[i]];\n if (deposit.pubKeyHash == pubKeyHash) {\n // next verifyBalances will correctly account for the loss of a front-run\n // deposit. Doing it here accounts for the loss as soon as possible\n lastVerifiedEthBalance -= Math.min(\n lastVerifiedEthBalance,\n uint256(deposit.amountGwei) * 1 gwei\n );\n _removeDeposit(depositList[i], deposit);\n break;\n }\n }\n\n // Leave the `firstDeposit` flag as true so no more deposits to unverified validators can be made.\n // The Governor has to reset the `firstDeposit` to false before another deposit to\n // an unverified validator can be made.\n // The Governor can set a new `validatorRegistrator` if they suspect it has been compromised.\n\n emit ValidatorInvalid(pubKeyHash);\n return;\n }\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(pubKeyHash);\n\n // Reset the firstDeposit flag as the first deposit to an unverified validator has been verified.\n firstDeposit = false;\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n struct FirstPendingDepositSlotProofData {\n uint64 slot;\n bytes proof;\n }\n\n struct StrategyValidatorProofData {\n uint64 withdrawableEpoch;\n bytes withdrawableEpochProof;\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n ///\n /// Important: this function has a limitation where `depositProcessedSlot` that is passed by the off-chain\n /// verifier requires a slot immediately after it to propose a block otherwise the `BeaconRoots.parentBlockRoot`\n /// will fail. This shouldn't be a problem, since by the current behaviour of beacon chain only 1%-3% slots\n /// don't propose a block.\n /// @param pendingDepositRoot The unique identifier of the deposit emitted in `ETHStaked` from\n /// the `stakeEth` function.\n /// @param depositProcessedSlot Any slot on or after the strategy's deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// Can not be a slot before a missed slot as the Beacon Root contract will have the parent block root\n /// set for the next block timestamp in 12 seconds time.\n /// @param firstPendingDeposit a `FirstPendingDepositSlotProofData` struct containing:\n /// - slot: The beacon chain slot of the first deposit in the beacon chain's deposit queue.\n /// Can be any non-zero value if the deposit queue is empty.\n /// - proof: The merkle proof of the first pending deposit's slot to the beacon block root.\n /// Can be either:\n /// * 40 witness hashes for BeaconBlock.state.PendingDeposits[0].slot when the deposit queue is not empty.\n /// * 37 witness hashes for BeaconBlock.state.PendingDeposits[0] when the deposit queue is empty.\n /// The 32 byte witness hashes are concatenated together starting from the leaf node.\n /// @param strategyValidatorData a `StrategyValidatorProofData` struct containing:\n /// - withdrawableEpoch: The withdrawable epoch of the validator the strategy is depositing to.\n /// - withdrawableEpochProof: The merkle proof for the withdrawable epoch of the validator the strategy\n /// is depositing to, to the beacon block root.\n /// This is 53 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n bytes32 pendingDepositRoot,\n uint64 depositProcessedSlot,\n FirstPendingDepositSlotProofData calldata firstPendingDeposit,\n StrategyValidatorProofData calldata strategyValidatorData\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[pendingDepositRoot];\n ValidatorData memory strategyValidator = validator[deposit.pubKeyHash];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(firstPendingDeposit.slot != 0, \"Zero 1st pending deposit slot\");\n\n // We should allow the verification of deposits for validators that have been marked as exiting\n // to cover this situation:\n // - there are 2 pending deposits\n // - beacon chain has slashed the validator\n // - when verifyDeposit is called for the first deposit it sets the Validator state to EXITING\n // - verifyDeposit should allow a secondary call for the other deposit to a slashed validator\n require(\n strategyValidator.state == ValidatorState.VERIFIED ||\n strategyValidator.state == ValidatorState.ACTIVE ||\n strategyValidator.state == ValidatorState.EXITING,\n \"Not verified/active/exiting\"\n );\n // The verification slot must be after the deposit's slot.\n // This is needed for when the deposit queue is empty.\n require(deposit.slot < depositProcessedSlot, \"Slot not after deposit\");\n\n uint64 snapTimestamp = snappedBalance.timestamp;\n\n // This check prevents an accounting error that can happen if:\n // - snapBalances are snapped at the time of T\n // - deposit is processed on the beacon chain after time T and before verifyBalances()\n // - verifyDeposit is called before verifyBalances which removes a deposit from depositList\n // and deposit balance from totalDepositsWei\n // - verifyBalances is called under-reporting the strategy's balance\n require(\n (_calcNextBlockTimestamp(depositProcessedSlot) <= snapTimestamp) ||\n snapTimestamp == 0,\n \"Deposit after balance snapshot\"\n );\n\n // Get the parent beacon block root of the next block which is the block root of the deposit verification slot.\n // This will revert if the slot after the verification slot was missed.\n bytes32 depositBlockRoot = BeaconRoots.parentBlockRoot(\n _calcNextBlockTimestamp(depositProcessedSlot)\n );\n\n // Verify the slot of the first pending deposit matches the beacon chain\n bool isDepositQueueEmpty = IBeaconProofs(BEACON_PROOFS)\n .verifyFirstPendingDeposit(\n depositBlockRoot,\n firstPendingDeposit.slot,\n firstPendingDeposit.proof\n );\n\n // Verify the withdrawableEpoch on the validator of the strategy's deposit\n IBeaconProofs(BEACON_PROOFS).verifyValidatorWithdrawable(\n depositBlockRoot,\n strategyValidator.index,\n strategyValidatorData.withdrawableEpoch,\n strategyValidatorData.withdrawableEpochProof\n );\n\n uint64 firstPendingDepositEpoch = firstPendingDeposit.slot /\n SLOTS_PER_EPOCH;\n\n // If deposit queue is empty all deposits have certainly been processed. If not\n // a validator can either be not exiting and no further checks are required.\n // Or a validator is exiting then this function needs to make sure that the\n // pending deposit to an exited validator has certainly been processed. The\n // slot/epoch of first pending deposit is the one that contains the transaction\n // where the deposit to the ETH Deposit Contract has been made.\n //\n // Once the firstPendingDepositEpoch becomes greater than the withdrawableEpoch of\n // the slashed validator then the deposit has certainly been processed. When the beacon\n // chain reaches the withdrawableEpoch of the validator the deposit will no longer be\n // postponed. And any new deposits created (and present in the deposit queue)\n // will have an equal or larger withdrawableEpoch.\n require(\n strategyValidatorData.withdrawableEpoch == FAR_FUTURE_EPOCH ||\n strategyValidatorData.withdrawableEpoch <=\n firstPendingDepositEpoch ||\n isDepositQueueEmpty,\n \"Exit Deposit likely not proc.\"\n );\n\n // solhint-disable max-line-length\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the deposit queue is empty then our deposit must have been processed on the beacon chain.\n // The deposit slot can be zero for validators consolidating to a compounding validator or 0x01 validator\n // being promoted to a compounding one. Reference:\n // - [switch_to_compounding_validator](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-switch_to_compounding_validator\n // - [queue_excess_active_balance](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-queue_excess_active_balance)\n // - [process_consolidation_request](https://ethereum.github.io/consensus-specs/specs/electra/beacon-chain/#new-process_consolidation_request)\n // We can not guarantee that the deposit has been processed in that case.\n // solhint-enable max-line-length\n require(\n deposit.slot < firstPendingDeposit.slot || isDepositQueueEmpty,\n \"Deposit likely not processed\"\n );\n\n // Remove the deposit now it has been verified as processed on the beacon chain.\n _removeDeposit(pendingDepositRoot, deposit);\n\n emit DepositVerified(\n pendingDepositRoot,\n uint256(deposit.amountGwei) * 1 gwei\n );\n }\n\n function _removeDeposit(\n bytes32 pendingDepositRoot,\n DepositData memory deposit\n ) internal {\n // After verifying the proof, update the contract storage\n deposits[pendingDepositRoot].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n bytes32 lastDeposit = depositList[depositList.length - 1];\n depositList[deposit.depositIndex] = lastDeposit;\n deposits[lastDeposit].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositList.pop();\n }\n\n /// @dev Calculates the timestamp of the next execution block from the given slot.\n /// @param slot The beacon chain slot number used for merkle proof verification.\n function _calcNextBlockTimestamp(uint64 slot)\n internal\n view\n returns (uint64)\n {\n // Calculate the next block timestamp from the slot.\n return SLOT_DURATION * slot + BEACON_GENESIS_TIMESTAMP + SLOT_DURATION;\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block and beacon block root\n /// of the slot that is associated with the previous block.\n ///\n /// When snapping / verifying balance it is of a high importance that there is no\n /// miss-match in respect to ETH that is held by the contract and balances that are\n /// verified on the validators.\n ///\n /// First some context on the beacon-chain block building behaviour. Relevant parts of\n /// constructing a block on the beacon chain consist of:\n /// - process_withdrawals: ETH is deducted from the validator's balance\n /// - process_execution_payload: immediately after the previous step executing all the\n /// transactions\n /// - apply the withdrawals: adding ETH to the recipient which is the withdrawal address\n /// contained in the withdrawal credentials of the exited validators\n ///\n /// That means that balance increases which are part of the post-block execution state are\n /// done within the block, but the transaction that are contained within that block can not\n /// see / interact with the balance from the exited validators. Only transactions in the\n /// next block can do that.\n ///\n /// When snap balances is performed the state of the chain is snapped across 2 separate\n /// chain states:\n /// - ETH balance of the contract is recorded on block X -> and corresponding slot Y\n /// - beacon chain block root is recorded of block X - 1 -> and corresponding slot Y - 1\n /// given there were no missed slots. It could also be Y - 2, Y - 3 depending on how\n /// many slots have not managed to propose a block. For the sake of simplicity this slot\n /// will be referred to as Y - 1 as it makes no difference in the argument\n ///\n /// Given these 2 separate chain states it is paramount that verify balances can not experience\n /// miss-counting ETH or much more dangerous double counting of the ETH.\n ///\n /// When verifyBalances is called it is performed on the current block Z where Z > X. Verify\n /// balances adds up all the ETH (omitting WETH) controlled by this contract:\n /// - ETH balance in the contract on block X\n /// - ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1\n /// - ETH balance in validators that are active in slot Y - 1\n /// - skips the ETH balance in validators that have withdrawn in slot Y - 1 (or sooner)\n /// and have their balance visible to transactions in slot Y and corresponding block X\n /// (or sooner)\n ///\n /// Lets verify the correctness of ETH accounting given the above described behaviour.\n ///\n /// *ETH balance in the contract on block X*\n ///\n /// This is an ETH balance of the contract on a non current X block. Any ETH leaving the\n /// contract as a result of a withdrawal subtracts from the ETH accounted for on block X\n /// if `verifyBalances` has already been called. It also invalidates a `snapBalances` in\n /// case `verifyBalances` has not been called yet. Not performing this would result in not\n /// accounting for the withdrawn ETH that has happened anywhere in the block interval [X + 1, Z].\n ///\n /// Similarly to withdrawals any `stakeEth` deposits to the deposit contract adds to the ETH\n /// accounted for since the last `verifyBalances` has been called. And it invalidates the\n /// `snapBalances` in case `verifyBalances` hasn't been yet called. Not performing this\n /// would result in double counting the `stakedEth` since it would be present once in the\n /// snapped contract balance and the second time in deposit storage variables.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in Deposits on block Z that haven't been yet processed in slot Y - 1*\n ///\n /// The contract sums up all the ETH that has been deposited to the Beacon chain deposit\n /// contract at block Z. The execution layer doesn't have direct access to the state of\n /// deposits on the beacon chain. And if it is to sum up all the ETH that is marked to be\n /// deposited it needs to be sure to not double count ETH that is in deposits (storage vars)\n /// and could also be part of the validator balances. It does that by verifying that at\n /// slot Y - 1 none of the deposits visible on block Z have been processed. Meaning since\n /// the last snap till now all are still in queue. Which ensures they can not be part of\n /// the validator balances in later steps.\n ///\n /// This behaviour is correct.\n ///\n /// *ETH balance in validators that are active in slot Y - 1*\n ///\n /// The contract is verifying none of the deposits on Y - 1 slot have been processed and\n /// for that reason it checks the validator balances in the same slot. Ensuring accounting\n /// correctness.\n ///\n /// This behaviour is correct.\n ///\n /// *The withdrawn validators*\n ///\n /// The withdrawn validators could have their balances deducted in any slot before slot\n /// Y - 1 and the execution layer sees the balance increase in the subsequent slot. Lets\n /// look at the \"worst case scenario\" where the validator withdrawal is processed in the\n /// slot Y - 1 (snapped slot) and see their balance increase (in execution layer) in slot\n /// Y -> block X. The ETH balance on the contract is snapped at block X meaning that\n /// even if the validator exits at the latest possible time it is paramount that the ETH\n /// balance on the execution layer is recorded in the next block. Correctly accounting\n /// for the withdrawn ETH.\n ///\n /// Worth mentioning if the validator exit is processed by the slot Y and balance increase\n /// seen on the execution layer on block X + 1 the withdrawal is ignored by both the\n /// validator balance verification as well as execution layer contract balance snap.\n ///\n /// This behaviour is correct.\n ///\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n function snapBalances() external {\n uint64 currentTimestamp = SafeCast.toUint64(block.timestamp);\n require(\n snappedBalance.timestamp + SNAP_BALANCES_DELAY < currentTimestamp,\n \"Snap too soon\"\n );\n\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(currentTimestamp);\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the snapped balance\n snappedBalance = Balances({\n blockRoot: blockRoot,\n timestamp: currentTimestamp,\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n emit BalancesSnapped(blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct BalanceProofs {\n // BeaconBlock.state.balances\n bytes32 balancesContainerRoot;\n bytes balancesContainerProof;\n // BeaconBlock.state.balances[validatorIndex]\n bytes32[] validatorBalanceLeaves;\n bytes[] validatorBalanceProofs;\n }\n\n struct PendingDepositProofs {\n bytes32 pendingDepositContainerRoot;\n bytes pendingDepositContainerProof;\n uint32[] pendingDepositIndexes;\n bytes[] pendingDepositProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks each of the strategy's deposits are still to be processed by the beacon chain.\n /// @param balanceProofs a `BalanceProofs` struct containing the following:\n /// - balancesContainerRoot: The merkle root of the balances container\n /// - balancesContainerProof: The merkle proof for the balances container to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// - validatorBalanceLeaves: Array of leaf nodes containing the validator balance with three other balances.\n /// - validatorBalanceProofs: Array of merkle proofs for the validator balance to the Balances container root.\n /// This is 39 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// @param pendingDepositProofs a `PendingDepositProofs` struct containing the following:\n /// - pendingDepositContainerRoot: The merkle root of the pending deposits list container\n /// - pendingDepositContainerProof: The merkle proof from the pending deposits list container\n /// to the beacon block root.\n /// This is 9 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n /// - pendingDepositIndexes: Array of indexes in the pending deposits list container for each\n /// of the strategy's deposits.\n /// - pendingDepositProofs: Array of merkle proofs for each strategy deposit in the\n /// beacon chain's pending deposit list container to the pending deposits list container root.\n /// These are 28 witness hashes of 32 bytes each concatenated together starting from the leaf node.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(\n BalanceProofs calldata balanceProofs,\n PendingDepositProofs calldata pendingDepositProofs\n ) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalance;\n // Check the balances are the latest\n require(balancesMem.timestamp > 0, \"No snapped balances\");\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n uint256 depositsCount = depositList.length;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n balanceProofs.validatorBalanceProofs.length ==\n verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n balanceProofs.validatorBalanceLeaves.length ==\n verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n balancesMem.blockRoot,\n balanceProofs.balancesContainerRoot,\n balanceProofs.balancesContainerProof\n );\n\n bytes32[]\n memory validatorHashesMem = _getPendingDepositValidatorHashes(\n depositsCount\n );\n\n // for each validator in reverse order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n ValidatorData memory validatorDataMem = validator[\n verifiedValidators[i]\n ];\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n balanceProofs.balancesContainerRoot,\n balanceProofs.validatorBalanceLeaves[i],\n balanceProofs.validatorBalanceProofs[i],\n validatorDataMem.index\n );\n\n // If the validator has exited and the balance is now zero\n if (validatorBalanceGwei == 0) {\n // Check if there are any pending deposits to this validator\n bool depositPending = false;\n for (uint256 j = 0; j < validatorHashesMem.length; j++) {\n if (validatorHashesMem[j] == verifiedValidators[i]) {\n depositPending = true;\n break;\n }\n }\n\n // If validator has a pending deposit we can not remove due to\n // the following situation:\n // - validator has a pending deposit\n // - validator has been slashed\n // - sweep cycle has withdrawn all ETH from the validator. Balance is 0\n // - beacon chain has processed the deposit and set the validator balance\n // to deposit amount\n // - if validator is no longer in the list of verifiedValidators its\n // balance will not be considered and be under-counted.\n if (!depositPending) {\n // Store the validator state as exited\n // This could have been in VERIFIED, ACTIVE or EXITING state\n validator[verifiedValidators[i]].state = ValidatorState\n .EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n }\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n } else if (\n validatorDataMem.state == ValidatorState.VERIFIED &&\n validatorBalanceGwei > MIN_ACTIVATION_BALANCE_GWEI\n ) {\n // Store the validator state as active. This does not necessarily mean the\n // validator is active on the beacon chain yet. It just means the validator has\n // enough balance that it can become active.\n validator[verifiedValidators[i]].state = ValidatorState\n .ACTIVE;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += validatorBalanceGwei * 1 gwei;\n }\n }\n\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification.\n // This section is after the validator balance verifications so an exited validator will be marked\n // as EXITED before the deposits are verified. If there was a deposit to an exited validator\n // then the deposit can only be removed once the validator is fully exited.\n // It is possible that validator fully exits and a postponed deposit to an exited validator increases\n // its balance again. In such case the contract will erroneously consider a deposit applied before it\n // has been applied on the beacon chain showing a smaller than real `totalValidatorBalance`.\n if (depositsCount > 0) {\n require(\n pendingDepositProofs.pendingDepositProofs.length ==\n depositsCount,\n \"Invalid deposit proofs\"\n );\n require(\n pendingDepositProofs.pendingDepositIndexes.length ==\n depositsCount,\n \"Invalid deposit indexes\"\n );\n\n // Verify from the root of the pending deposit list container to the beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyPendingDepositsContainer(\n balancesMem.blockRoot,\n pendingDepositProofs.pendingDepositContainerRoot,\n pendingDepositProofs.pendingDepositContainerProof\n );\n\n // For each staking strategy's deposit.\n for (uint256 i = 0; i < depositsCount; ++i) {\n bytes32 pendingDepositRoot = depositList[i];\n\n // Verify the strategy's deposit is still pending on the beacon chain.\n IBeaconProofs(BEACON_PROOFS).verifyPendingDeposit(\n pendingDepositProofs.pendingDepositContainerRoot,\n pendingDepositRoot,\n pendingDepositProofs.pendingDepositProofs[i],\n pendingDepositProofs.pendingDepositIndexes[i]\n );\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei +=\n uint256(deposits[pendingDepositRoot].amountGwei) *\n 1 gwei;\n }\n }\n\n // Store the verified balance in storage\n lastVerifiedEthBalance =\n totalDepositsWei +\n totalValidatorBalance +\n balancesMem.ethBalance;\n // Reset the last snap timestamp so a new snapBalances has to be made\n snappedBalance.timestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice get a list of all validator hashes present in the pending deposits\n /// list can have duplicate entries\n function _getPendingDepositValidatorHashes(uint256 depositsCount)\n internal\n view\n returns (bytes32[] memory validatorHashes)\n {\n validatorHashes = new bytes32[](depositsCount);\n for (uint256 i = 0; i < depositsCount; i++) {\n validatorHashes[i] = deposits[depositList[i]].pubKeyHash;\n }\n }\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n require(pubKey.length == 48, \"Invalid public key\");\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /**\n *\n * WETH and ETH Accounting\n *\n */\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n // The min is required as more WETH can be withdrawn than deposited\n // as the strategy earns consensus and execution rewards.\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n /// @dev Converts ETH to WETH and updates the accounting.\n /// @param _ethAmount The amount of ETH in wei.\n function _convertEthToWeth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance.\n // The ETH balance in this strategy contract can be more than the last verified ETH balance\n // due to partial withdrawals or full exits being processed by the beacon chain since the last snapBalances.\n // It can also happen from execution rewards (MEV) or ETH donations.\n lastVerifiedEthBalance -= Math.min(lastVerifiedEthBalance, _ethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n snappedBalance.timestamp = 0;\n }\n\n /// @dev Converts WETH to ETH and updates the accounting.\n /// @param _wethAmount The amount of WETH in wei.\n function _convertWethToEth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += _wethAmount;\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n snappedBalance.timestamp = 0;\n }\n\n /**\n *\n * View Functions\n *\n */\n\n /// @notice Returns the number of deposits waiting to be verified as processed on the beacon chain,\n /// or deposits that have been verified to an exiting validator and is now waiting for the\n /// validator's balance to be swept.\n function depositListLength() external view returns (uint256) {\n return depositList.length;\n }\n\n /// @notice Returns the number of verified validators.\n function verifiedValidatorsLength() external view returns (uint256) {\n return verifiedValidators.length;\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/deployments/mainnet/.migrations.json b/contracts/deployments/mainnet/.migrations.json index b99e5a42d2..d644c34b0c 100644 --- a/contracts/deployments/mainnet/.migrations.json +++ b/contracts/deployments/mainnet/.migrations.json @@ -46,5 +46,6 @@ "147_claim_rewards_module_upgrade": 1751883056, "148_xogn_module_for_5_of_8_multisig": 1752085269, "149_xogn_module_7": 1752215483, - "151_curve_pb_module": 1754292289 + "151_curve_pb_module": 1754292289, + "152_beacon_root_testing": 1753781151 } \ No newline at end of file diff --git a/contracts/deployments/mainnet/CompoundingStakingSSVStrategyProxy.json b/contracts/deployments/mainnet/CompoundingStakingSSVStrategyProxy.json new file mode 100644 index 0000000000..d1615bf1f6 --- /dev/null +++ b/contracts/deployments/mainnet/CompoundingStakingSSVStrategyProxy.json @@ -0,0 +1,297 @@ +{ + "address": "0xaF04828Ed923216c77dC22a2fc8E077FDaDAA87d", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "GovernorshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousGovernor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "PendingGovernorshipTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "claimGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "governor", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "implementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_logic", + "type": "address" + }, + { + "internalType": "address", + "name": "_initGovernor", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "isGovernor", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newGovernor", + "type": "address" + } + ], + "name": "transferGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } + ], + "transactionHash": "0xceae8636454e54b3a9653f54d25cb19d0c48fec27651729bbecff422d652ae89", + "receipt": { + "to": null, + "from": "0x4b91827516f79d6F6a1F292eD99671663b09169a", + "contractAddress": "0xaF04828Ed923216c77dC22a2fc8E077FDaDAA87d", + "transactionIndex": 17, + "gasUsed": "599335", + "logsBloom": "0x00000000000000000000000000000000000000080000000000000000000000000000000000000000002000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000080002000000000000000000000000000010000000000000000000000000000000000000010000000000000000000000000400000000000000020000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0xfae3bdf0a3c9a4d5d14d9ab7a779a2f5de846797f740ce593915ff7944831aa1", + "transactionHash": "0x8601c561ad852a12d6e91d9f358664dcb78f4cc6743563518156e5063f53638d", + "logs": [ + { + "transactionIndex": 7, + "blockNumber": 22880110, + "transactionHash": "0xceae8636454e54b3a9653f54d25cb19d0c48fec27651729bbecff422d652ae89", + "address": "0xaF04828Ed923216c77dC22a2fc8E077FDaDAA87d", + "topics": [ + "0xc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000004b91827516f79d6f6a1f292ed99671663b09169a" + ], + "data": "0x", + "logIndex": 65, + "blockHash": "0xc37695c3d0835818958ef5d393f4e26de0808e225ec277585c7ee68e00b2c468" + } + ], + "blockNumber": 22423600, + "cumulativeGasUsed": "3142359", + "status": 1, + "byzantium": true + }, + "args": [], + "numDeployments": 1, + "solcInputHash": "83a2b22db77d0c3a03114fad0cdf6377", + "metadata": "{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"GovernorshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousGovernor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newGovernor\",\"type\":\"address\"}],\"name\":\"PendingGovernorshipTransfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"implementation\",\"type\":\"address\"}],\"name\":\"Upgraded\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"governor\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"implementation\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_logic\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_initGovernor\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isGovernor\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newGovernor\",\"type\":\"address\"}],\"name\":\"transferGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newImplementation\",\"type\":\"address\"}],\"name\":\"upgradeTo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"upgradeToAndCall\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"}],\"devdoc\":{\"events\":{\"Upgraded(address)\":{\"details\":\"Emitted when the implementation is upgraded.\",\"params\":{\"implementation\":\"Address of the new implementation.\"}}},\"kind\":\"dev\",\"methods\":{\"admin()\":{\"returns\":{\"_0\":\"The address of the proxy admin/it's also the governor.\"}},\"implementation()\":{\"returns\":{\"_0\":\"The address of the implementation.\"}},\"initialize(address,address,bytes)\":{\"details\":\"Contract initializer with Governor enforcement\",\"params\":{\"_data\":\"Data to send as msg.data to the implementation to initialize the proxied contract. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.\",\"_initGovernor\":\"Address of the initial Governor.\",\"_logic\":\"Address of the initial implementation.\"}},\"transferGovernance(address)\":{\"params\":{\"_newGovernor\":\"Address of the new Governor\"}},\"upgradeTo(address)\":{\"details\":\"Upgrade the backing implementation of the proxy. Only the admin can call this function.\",\"params\":{\"_newImplementation\":\"Address of the new implementation.\"}},\"upgradeToAndCall(address,bytes)\":{\"details\":\"Upgrade the backing implementation of the proxy and call a function on the new implementation. This is useful to initialize the proxied contract.\",\"params\":{\"data\":\"Data to send as msg.data in the low level call. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\",\"newImplementation\":\"Address of the new implementation.\"}}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"claimGovernance()\":{\"notice\":\"Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor.\"},\"governor()\":{\"notice\":\"Returns the address of the current Governor.\"},\"isGovernor()\":{\"notice\":\"Returns true if the caller is the current Governor.\"},\"transferGovernance(address)\":{\"notice\":\"Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete\"}},\"notice\":\"CompoundingStakingSSVStrategyProxy delegates calls to a CurveAMOStrategy implementation\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/proxies/Proxies.sol\":\"CompoundingStakingSSVStrategyProxy\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize, which returns 0 for contracts in\\n // construction, since the code is only stored at the end of the\\n // constructor execution.\\n\\n uint256 size;\\n assembly {\\n size := extcodesize(account)\\n }\\n return size > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCall(target, data, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n require(isContract(target), \\\"Address: static call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(isContract(target), \\\"Address: delegate call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x51b758a8815ecc9596c66c37d56b1d33883a444631a3f916b9fe65cb863ef7c4\",\"license\":\"MIT\"},\"contracts/governance/Governable.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\n/**\\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\\n * from owner to governor and renounce methods removed. Does not use\\n * Context.sol like Ownable.sol does for simplification.\\n * @author Origin Protocol Inc\\n */\\nabstract contract Governable {\\n // Storage position of the owner and pendingOwner of the contract\\n // keccak256(\\\"OUSD.governor\\\");\\n bytes32 private constant governorPosition =\\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\\n\\n // keccak256(\\\"OUSD.pending.governor\\\");\\n bytes32 private constant pendingGovernorPosition =\\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\\n\\n // keccak256(\\\"OUSD.reentry.status\\\");\\n bytes32 private constant reentryStatusPosition =\\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\\n\\n // See OpenZeppelin ReentrancyGuard implementation\\n uint256 constant _NOT_ENTERED = 1;\\n uint256 constant _ENTERED = 2;\\n\\n event PendingGovernorshipTransfer(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n event GovernorshipTransferred(\\n address indexed previousGovernor,\\n address indexed newGovernor\\n );\\n\\n /**\\n * @notice Returns the address of the current Governor.\\n */\\n function governor() public view returns (address) {\\n return _governor();\\n }\\n\\n /**\\n * @dev Returns the address of the current Governor.\\n */\\n function _governor() internal view returns (address governorOut) {\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n governorOut := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Returns the address of the pending Governor.\\n */\\n function _pendingGovernor()\\n internal\\n view\\n returns (address pendingGovernor)\\n {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n pendingGovernor := sload(position)\\n }\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the Governor.\\n */\\n modifier onlyGovernor() {\\n require(isGovernor(), \\\"Caller is not the Governor\\\");\\n _;\\n }\\n\\n /**\\n * @notice Returns true if the caller is the current Governor.\\n */\\n function isGovernor() public view returns (bool) {\\n return msg.sender == _governor();\\n }\\n\\n function _setGovernor(address newGovernor) internal {\\n emit GovernorshipTransferred(_governor(), newGovernor);\\n\\n bytes32 position = governorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @dev Prevents a contract from calling itself, directly or indirectly.\\n * Calling a `nonReentrant` function from another `nonReentrant`\\n * function is not supported. It is possible to prevent this from happening\\n * by making the `nonReentrant` function external, and make it call a\\n * `private` function that does the actual work.\\n */\\n modifier nonReentrant() {\\n bytes32 position = reentryStatusPosition;\\n uint256 _reentry_status;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n _reentry_status := sload(position)\\n }\\n\\n // On the first call to nonReentrant, _notEntered will be true\\n require(_reentry_status != _ENTERED, \\\"Reentrant call\\\");\\n\\n // Any calls to nonReentrant after this point will fail\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _ENTERED)\\n }\\n\\n _;\\n\\n // By storing the original value once again, a refund is triggered (see\\n // https://eips.ethereum.org/EIPS/eip-2200)\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, _NOT_ENTERED)\\n }\\n }\\n\\n function _setPendingGovernor(address newGovernor) internal {\\n bytes32 position = pendingGovernorPosition;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(position, newGovernor)\\n }\\n }\\n\\n /**\\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the current Governor. Must be claimed for this to complete\\n * @param _newGovernor Address of the new Governor\\n */\\n function transferGovernance(address _newGovernor) external onlyGovernor {\\n _setPendingGovernor(_newGovernor);\\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\\n }\\n\\n /**\\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\\n * Can only be called by the new Governor.\\n */\\n function claimGovernance() external {\\n require(\\n msg.sender == _pendingGovernor(),\\n \\\"Only the pending Governor can complete the claim\\\"\\n );\\n _changeGovernor(msg.sender);\\n }\\n\\n /**\\n * @dev Change Governance of the contract to a new account (`newGovernor`).\\n * @param _newGovernor Address of the new Governor\\n */\\n function _changeGovernor(address _newGovernor) internal {\\n require(_newGovernor != address(0), \\\"New Governor is address(0)\\\");\\n _setGovernor(_newGovernor);\\n }\\n}\\n\",\"keccak256\":\"0xf32f873c8bfbacf2e5f01d0cf37bc7f54fbd5aa656e95c8a599114229946f107\",\"license\":\"BUSL-1.1\"},\"contracts/proxies/InitializeGovernedUpgradeabilityProxy.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { Address } from \\\"@openzeppelin/contracts/utils/Address.sol\\\";\\n\\nimport { Governable } from \\\"../governance/Governable.sol\\\";\\n\\n/**\\n * @title BaseGovernedUpgradeabilityProxy\\n * @dev This contract combines an upgradeability proxy with our governor system.\\n * It is based on an older version of OpenZeppelins BaseUpgradeabilityProxy\\n * with Solidity ^0.8.0.\\n * @author Origin Protocol Inc\\n */\\ncontract InitializeGovernedUpgradeabilityProxy is Governable {\\n /**\\n * @dev Emitted when the implementation is upgraded.\\n * @param implementation Address of the new implementation.\\n */\\n event Upgraded(address indexed implementation);\\n\\n constructor() {\\n _setGovernor(msg.sender);\\n }\\n\\n /**\\n * @dev Contract initializer with Governor enforcement\\n * @param _logic Address of the initial implementation.\\n * @param _initGovernor Address of the initial Governor.\\n * @param _data Data to send as msg.data to the implementation to initialize\\n * the proxied contract.\\n * It should include the signature and the parameters of the function to be\\n * called, as described in\\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\\n * This parameter is optional, if no data is given the initialization call\\n * to proxied contract will be skipped.\\n */\\n function initialize(\\n address _logic,\\n address _initGovernor,\\n bytes calldata _data\\n ) public payable onlyGovernor {\\n require(_implementation() == address(0));\\n require(_logic != address(0), \\\"Implementation not set\\\");\\n assert(\\n IMPLEMENTATION_SLOT ==\\n bytes32(uint256(keccak256(\\\"eip1967.proxy.implementation\\\")) - 1)\\n );\\n _setImplementation(_logic);\\n if (_data.length > 0) {\\n (bool success, ) = _logic.delegatecall(_data);\\n require(success);\\n }\\n _changeGovernor(_initGovernor);\\n }\\n\\n /**\\n * @return The address of the proxy admin/it's also the governor.\\n */\\n function admin() external view returns (address) {\\n return _governor();\\n }\\n\\n /**\\n * @return The address of the implementation.\\n */\\n function implementation() external view returns (address) {\\n return _implementation();\\n }\\n\\n /**\\n * @dev Upgrade the backing implementation of the proxy.\\n * Only the admin can call this function.\\n * @param _newImplementation Address of the new implementation.\\n */\\n function upgradeTo(address _newImplementation) external onlyGovernor {\\n _upgradeTo(_newImplementation);\\n }\\n\\n /**\\n * @dev Upgrade the backing implementation of the proxy and call a function\\n * on the new implementation.\\n * This is useful to initialize the proxied contract.\\n * @param newImplementation Address of the new implementation.\\n * @param data Data to send as msg.data in the low level call.\\n * It should include the signature and the parameters of the function to be called, as described in\\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\\n */\\n function upgradeToAndCall(address newImplementation, bytes calldata data)\\n external\\n payable\\n onlyGovernor\\n {\\n _upgradeTo(newImplementation);\\n (bool success, ) = newImplementation.delegatecall(data);\\n require(success);\\n }\\n\\n /**\\n * @dev Fallback function.\\n * Implemented entirely in `_fallback`.\\n */\\n fallback() external payable {\\n _fallback();\\n }\\n\\n /**\\n * @dev Delegates execution to an implementation contract.\\n * This is a low level function that doesn't return to its internal call site.\\n * It will return to the external caller whatever the implementation returns.\\n * @param _impl Address to delegate.\\n */\\n function _delegate(address _impl) internal {\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n // Copy msg.data. We take full control of memory in this inline assembly\\n // block because it will not return to Solidity code. We overwrite the\\n // Solidity scratch pad at memory position 0.\\n calldatacopy(0, 0, calldatasize())\\n\\n // Call the implementation.\\n // out and outsize are 0 because we don't know the size yet.\\n let result := delegatecall(gas(), _impl, 0, calldatasize(), 0, 0)\\n\\n // Copy the returned data.\\n returndatacopy(0, 0, returndatasize())\\n\\n switch result\\n // delegatecall returns 0 on error.\\n case 0 {\\n revert(0, returndatasize())\\n }\\n default {\\n return(0, returndatasize())\\n }\\n }\\n }\\n\\n /**\\n * @dev Function that is run as the first thing in the fallback function.\\n * Can be redefined in derived contracts to add functionality.\\n * Redefinitions must call super._willFallback().\\n */\\n function _willFallback() internal {}\\n\\n /**\\n * @dev fallback implementation.\\n * Extracted to enable manual triggering.\\n */\\n function _fallback() internal {\\n _willFallback();\\n _delegate(_implementation());\\n }\\n\\n /**\\n * @dev Storage slot with the address of the current implementation.\\n * This is the keccak-256 hash of \\\"eip1967.proxy.implementation\\\" subtracted by 1, and is\\n * validated in the constructor.\\n */\\n bytes32 internal constant IMPLEMENTATION_SLOT =\\n 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\\n\\n /**\\n * @dev Returns the current implementation.\\n * @return impl Address of the current implementation\\n */\\n function _implementation() internal view returns (address impl) {\\n bytes32 slot = IMPLEMENTATION_SLOT;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n impl := sload(slot)\\n }\\n }\\n\\n /**\\n * @dev Upgrades the proxy to a new implementation.\\n * @param newImplementation Address of the new implementation.\\n */\\n function _upgradeTo(address newImplementation) internal {\\n _setImplementation(newImplementation);\\n emit Upgraded(newImplementation);\\n }\\n\\n /**\\n * @dev Sets the implementation address of the proxy.\\n * @param newImplementation Address of the new implementation.\\n */\\n function _setImplementation(address newImplementation) internal {\\n require(\\n Address.isContract(newImplementation),\\n \\\"Cannot set a proxy implementation to a non-contract address\\\"\\n );\\n\\n bytes32 slot = IMPLEMENTATION_SLOT;\\n\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(slot, newImplementation)\\n }\\n }\\n}\\n\",\"keccak256\":\"0x3f46ae39dced6fa90d8b65aa31a0a331438544ec876e2ec961a8e6b22e2e06c7\",\"license\":\"BUSL-1.1\"},\"contracts/proxies/Proxies.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { InitializeGovernedUpgradeabilityProxy } from \\\"./InitializeGovernedUpgradeabilityProxy.sol\\\";\\n\\n/**\\n * @notice OUSDProxy delegates calls to an OUSD implementation\\n */\\ncontract OUSDProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice WrappedOUSDProxy delegates calls to a WrappedOUSD implementation\\n */\\ncontract WrappedOUSDProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice VaultProxy delegates calls to a Vault implementation\\n */\\ncontract VaultProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice CompoundStrategyProxy delegates calls to a CompoundStrategy implementation\\n */\\ncontract CompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice AaveStrategyProxy delegates calls to a AaveStrategy implementation\\n */\\ncontract AaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice ConvexStrategyProxy delegates calls to a ConvexStrategy implementation\\n */\\ncontract ConvexStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice HarvesterProxy delegates calls to a Harvester implementation\\n */\\ncontract HarvesterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice DripperProxy delegates calls to a Dripper implementation\\n */\\ncontract DripperProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice MorphoCompoundStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\\n */\\ncontract MorphoCompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice ConvexOUSDMetaStrategyProxy delegates calls to a ConvexOUSDMetaStrategy implementation\\n */\\ncontract ConvexOUSDMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice MorphoAaveStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\\n */\\ncontract MorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHProxy delegates calls to nowhere for now\\n */\\ncontract OETHProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice WOETHProxy delegates calls to nowhere for now\\n */\\ncontract WOETHProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHVaultProxy delegates calls to a Vault implementation\\n */\\ncontract OETHVaultProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHDripperProxy delegates calls to a OETHDripper implementation\\n */\\ncontract OETHDripperProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHHarvesterProxy delegates calls to a Harvester implementation\\n */\\ncontract OETHHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice CurveEthStrategyProxy delegates calls to a CurveEthStrategy implementation\\n */\\ncontract ConvexEthMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice BuybackProxy delegates calls to Buyback implementation\\n */\\ncontract BuybackProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHMorphoAaveStrategyProxy delegates calls to a MorphoAaveStrategy implementation\\n */\\ncontract OETHMorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHBalancerMetaPoolrEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\\n */\\ncontract OETHBalancerMetaPoolrEthStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice OETHBalancerMetaPoolwstEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\\n */\\ncontract OETHBalancerMetaPoolwstEthStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MakerDsrStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MakerDsrStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHBuybackProxy delegates calls to Buyback implementation\\n */\\ncontract OETHBuybackProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice BridgedWOETHProxy delegates calls to BridgedWOETH implementation\\n */\\ncontract BridgedWOETHProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice NativeStakingSSVStrategyProxy delegates calls to NativeStakingSSVStrategy implementation\\n */\\ncontract NativeStakingSSVStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingFeeAccumulatorProxy delegates calls to FeeAccumulator implementation\\n */\\ncontract NativeStakingFeeAccumulatorProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingSSVStrategy2Proxy delegates calls to NativeStakingSSVStrategy implementation\\n */\\ncontract NativeStakingSSVStrategy2Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingFeeAccumulator2Proxy delegates calls to FeeAccumulator implementation\\n */\\ncontract NativeStakingFeeAccumulator2Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingSSVStrategy3Proxy delegates calls to NativeStakingSSVStrategy implementation\\n */\\ncontract NativeStakingSSVStrategy3Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice NativeStakingFeeAccumulator3Proxy delegates calls to FeeAccumulator implementation\\n */\\ncontract NativeStakingFeeAccumulator3Proxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MetaMorphoStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MetaMorphoStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice ARMBuybackProxy delegates calls to Buyback implementation\\n */\\ncontract ARMBuybackProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice MorphoGauntletPrimeUSDCStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MorphoGauntletPrimeUSDCStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MorphoGauntletPrimeUSDTStrategyProxy delegates calls to a Generalized4626USDTStrategy implementation\\n */\\ncontract MorphoGauntletPrimeUSDTStrategyProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice CurvePoolBoosterProxy delegates calls to a CurvePoolBooster implementation\\n */\\ncontract CurvePoolBoosterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHFixedRateDripperProxy delegates calls to a OETHFixedRateDripper implementation\\n */\\ncontract OETHFixedRateDripperProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OETHSimpleHarvesterProxy delegates calls to a OETHSimpleHarvester implementation\\n */\\ncontract OETHSimpleHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice PoolBoostCentralRegistryProxy delegates calls to the PoolBoostCentralRegistry implementation\\n */\\ncontract PoolBoostCentralRegistryProxy is\\n InitializeGovernedUpgradeabilityProxy\\n{\\n\\n}\\n\\n/**\\n * @notice MakerSSRStrategyProxy delegates calls to a Generalized4626Strategy implementation\\n */\\ncontract MakerSSRStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice OUSDCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\\n */\\ncontract OUSDCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\\n/**\\n * @notice CompoundingStakingSSVStrategyProxy delegates calls to a CurveAMOStrategy implementation\\n */\\ncontract CompoundingStakingSSVStrategyProxy is InitializeGovernedUpgradeabilityProxy {\\n\\n}\\n\",\"keccak256\":\"0x592bd46efa0f1e09251c34222d428fde6da5f931aa1197461d745167f7eaa696\",\"license\":\"BUSL-1.1\"}},\"version\":1}", + "bytecode": "0x6080604052348015600f57600080fd5b50601733601b565b6081565b6001600160a01b038116603a6000805160206109f98339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a36000805160206109f983398151915255565b610969806100906000396000f3fe6080604052600436106100865760003560e01c80635d36b190116100595780635d36b1901461010a578063c7af33521461011f578063cf7a1d7714610144578063d38bfff414610157578063f851a4401461009057610086565b80630c340a24146100905780633659cfe6146100c25780634f1ef286146100e25780635c60da1b146100f5575b61008e610177565b005b34801561009c57600080fd5b506100a5610197565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100ce57600080fd5b5061008e6100dd366004610750565b6101b4565b61008e6100f03660046107bb565b6101ed565b34801561010157600080fd5b506100a561028a565b34801561011657600080fd5b5061008e6102a2565b34801561012b57600080fd5b50610134610346565b60405190151581526020016100b9565b61008e61015236600461080e565b610377565b34801561016357600080fd5b5061008e610172366004610750565b6104e0565b6101956101906000805160206108f48339815191525490565b610584565b565b60006101af6000805160206109148339815191525490565b905090565b6101bc610346565b6101e15760405162461bcd60e51b81526004016101d89061086f565b60405180910390fd5b6101ea816105a8565b50565b6101f5610346565b6102115760405162461bcd60e51b81526004016101d89061086f565b61021a836105a8565b6000836001600160a01b031683836040516102369291906108a6565b600060405180830381855af49150503d8060008114610271576040519150601f19603f3d011682016040523d82523d6000602084013e610276565b606091505b505090508061028457600080fd5b50505050565b60006101af6000805160206108f48339815191525490565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b03161461033d5760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b60648201526084016101d8565b610195336105e8565b600061035e6000805160206109148339815191525490565b6001600160a01b0316336001600160a01b031614905090565b61037f610346565b61039b5760405162461bcd60e51b81526004016101d89061086f565b60006103b36000805160206108f48339815191525490565b6001600160a01b0316146103c657600080fd5b6001600160a01b0384166104155760405162461bcd60e51b8152602060048201526016602482015275125b5c1b195b595b9d185d1a5bdb881b9bdd081cd95d60521b60448201526064016101d8565b61044060017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd6108b6565b6000805160206108f48339815191521461045c5761045c6108dd565b61046584610647565b80156104d7576000846001600160a01b031683836040516104879291906108a6565b600060405180830381855af49150503d80600081146104c2576040519150601f19603f3d011682016040523d82523d6000602084013e6104c7565b606091505b50509050806104d557600080fd5b505b610284836105e8565b6104e8610346565b6105045760405162461bcd60e51b81526004016101d89061086f565b61052c817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b031661054c6000805160206109148339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b3660008037600080366000845af43d6000803e8080156105a3573d6000f35b3d6000fd5b6105b181610647565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b03811661063e5760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f72206973206164647265737328302900000000000060448201526064016101d8565b6101ea816106cd565b803b6106bb5760405162461bcd60e51b815260206004820152603b60248201527f43616e6e6f742073657420612070726f787920696d706c656d656e746174696f60448201527f6e20746f2061206e6f6e2d636f6e74726163742061646472657373000000000060648201526084016101d8565b6000805160206108f483398151915255565b806001600160a01b03166106ed6000805160206109148339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a360008051602061091483398151915255565b80356001600160a01b038116811461074b57600080fd5b919050565b60006020828403121561076257600080fd5b61076b82610734565b9392505050565b60008083601f84011261078457600080fd5b50813567ffffffffffffffff81111561079c57600080fd5b6020830191508360208285010111156107b457600080fd5b9250929050565b6000806000604084860312156107d057600080fd5b6107d984610734565b9250602084013567ffffffffffffffff8111156107f557600080fd5b61080186828701610772565b9497909650939450505050565b6000806000806060858703121561082457600080fd5b61082d85610734565b935061083b60208601610734565b9250604085013567ffffffffffffffff81111561085757600080fd5b61086387828801610772565b95989497509550505050565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b8183823760009101908152919050565b818103818111156108d757634e487b7160e01b600052601160045260246000fd5b92915050565b634e487b7160e01b600052600160045260246000fdfe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa26469706673582212206a3bb2a80be2e86868af023c797bd65344a8631c0e7020ae98cb1560299248c564736f6c634300081c00337bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a", + "deployedBytecode": "0x6080604052600436106100865760003560e01c80635d36b190116100595780635d36b1901461010a578063c7af33521461011f578063cf7a1d7714610144578063d38bfff414610157578063f851a4401461009057610086565b80630c340a24146100905780633659cfe6146100c25780634f1ef286146100e25780635c60da1b146100f5575b61008e610177565b005b34801561009c57600080fd5b506100a5610197565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100ce57600080fd5b5061008e6100dd366004610750565b6101b4565b61008e6100f03660046107bb565b6101ed565b34801561010157600080fd5b506100a561028a565b34801561011657600080fd5b5061008e6102a2565b34801561012b57600080fd5b50610134610346565b60405190151581526020016100b9565b61008e61015236600461080e565b610377565b34801561016357600080fd5b5061008e610172366004610750565b6104e0565b6101956101906000805160206108f48339815191525490565b610584565b565b60006101af6000805160206109148339815191525490565b905090565b6101bc610346565b6101e15760405162461bcd60e51b81526004016101d89061086f565b60405180910390fd5b6101ea816105a8565b50565b6101f5610346565b6102115760405162461bcd60e51b81526004016101d89061086f565b61021a836105a8565b6000836001600160a01b031683836040516102369291906108a6565b600060405180830381855af49150503d8060008114610271576040519150601f19603f3d011682016040523d82523d6000602084013e610276565b606091505b505090508061028457600080fd5b50505050565b60006101af6000805160206108f48339815191525490565b7f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db546001600160a01b0316336001600160a01b03161461033d5760405162461bcd60e51b815260206004820152603060248201527f4f6e6c79207468652070656e64696e6720476f7665726e6f722063616e20636f60448201526f6d706c6574652074686520636c61696d60801b60648201526084016101d8565b610195336105e8565b600061035e6000805160206109148339815191525490565b6001600160a01b0316336001600160a01b031614905090565b61037f610346565b61039b5760405162461bcd60e51b81526004016101d89061086f565b60006103b36000805160206108f48339815191525490565b6001600160a01b0316146103c657600080fd5b6001600160a01b0384166104155760405162461bcd60e51b8152602060048201526016602482015275125b5c1b195b595b9d185d1a5bdb881b9bdd081cd95d60521b60448201526064016101d8565b61044060017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd6108b6565b6000805160206108f48339815191521461045c5761045c6108dd565b61046584610647565b80156104d7576000846001600160a01b031683836040516104879291906108a6565b600060405180830381855af49150503d80600081146104c2576040519150601f19603f3d011682016040523d82523d6000602084013e6104c7565b606091505b50509050806104d557600080fd5b505b610284836105e8565b6104e8610346565b6105045760405162461bcd60e51b81526004016101d89061086f565b61052c817f44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db55565b806001600160a01b031661054c6000805160206109148339815191525490565b6001600160a01b03167fa39cc5eb22d0f34d8beaefee8a3f17cc229c1a1d1ef87a5ad47313487b1c4f0d60405160405180910390a350565b3660008037600080366000845af43d6000803e8080156105a3573d6000f35b3d6000fd5b6105b181610647565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b03811661063e5760405162461bcd60e51b815260206004820152601a60248201527f4e657720476f7665726e6f72206973206164647265737328302900000000000060448201526064016101d8565b6101ea816106cd565b803b6106bb5760405162461bcd60e51b815260206004820152603b60248201527f43616e6e6f742073657420612070726f787920696d706c656d656e746174696f60448201527f6e20746f2061206e6f6e2d636f6e74726163742061646472657373000000000060648201526084016101d8565b6000805160206108f483398151915255565b806001600160a01b03166106ed6000805160206109148339815191525490565b6001600160a01b03167fc7c0c772add429241571afb3805861fb3cfa2af374534088b76cdb4325a87e9a60405160405180910390a360008051602061091483398151915255565b80356001600160a01b038116811461074b57600080fd5b919050565b60006020828403121561076257600080fd5b61076b82610734565b9392505050565b60008083601f84011261078457600080fd5b50813567ffffffffffffffff81111561079c57600080fd5b6020830191508360208285010111156107b457600080fd5b9250929050565b6000806000604084860312156107d057600080fd5b6107d984610734565b9250602084013567ffffffffffffffff8111156107f557600080fd5b61080186828701610772565b9497909650939450505050565b6000806000806060858703121561082457600080fd5b61082d85610734565b935061083b60208601610734565b9250604085013567ffffffffffffffff81111561085757600080fd5b61086387828801610772565b95989497509550505050565b6020808252601a908201527f43616c6c6572206973206e6f742074686520476f7665726e6f72000000000000604082015260600190565b8183823760009101908152919050565b818103818111156108d757634e487b7160e01b600052601160045260246000fd5b92915050565b634e487b7160e01b600052600160045260246000fdfe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4aa26469706673582212206a3bb2a80be2e86868af023c797bd65344a8631c0e7020ae98cb1560299248c564736f6c634300081c0033", + "libraries": {}, + "devdoc": { + "events": { + "Upgraded(address)": { + "details": "Emitted when the implementation is upgraded.", + "params": { + "implementation": "Address of the new implementation." + } + } + }, + "kind": "dev", + "methods": { + "admin()": { + "returns": { + "_0": "The address of the proxy admin/it's also the governor." + } + }, + "implementation()": { + "returns": { + "_0": "The address of the implementation." + } + }, + "initialize(address,address,bytes)": { + "details": "Contract initializer with Governor enforcement", + "params": { + "_data": "Data to send as msg.data to the implementation to initialize the proxied contract. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.", + "_initGovernor": "Address of the initial Governor.", + "_logic": "Address of the initial implementation." + } + }, + "transferGovernance(address)": { + "params": { + "_newGovernor": "Address of the new Governor" + } + }, + "upgradeTo(address)": { + "details": "Upgrade the backing implementation of the proxy. Only the admin can call this function.", + "params": { + "_newImplementation": "Address of the new implementation." + } + }, + "upgradeToAndCall(address,bytes)": { + "details": "Upgrade the backing implementation of the proxy and call a function on the new implementation. This is useful to initialize the proxied contract.", + "params": { + "data": "Data to send as msg.data in the low level call. It should include the signature and the parameters of the function to be called, as described in https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.", + "newImplementation": "Address of the new implementation." + } + } + }, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "claimGovernance()": { + "notice": "Claim Governance of the contract to a new account (`newGovernor`). Can only be called by the new Governor." + }, + "governor()": { + "notice": "Returns the address of the current Governor." + }, + "isGovernor()": { + "notice": "Returns true if the caller is the current Governor." + }, + "transferGovernance(address)": { + "notice": "Transfers Governance of the contract to a new account (`newGovernor`). Can only be called by the current Governor. Must be claimed for this to complete" + } + }, + "notice": "CompoundingStakingSSVStrategyProxy delegates calls to a CompoundingStakingSSVStrategy implementation", + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} \ No newline at end of file diff --git a/contracts/deployments/mainnet/MockBeaconRoots.json b/contracts/deployments/mainnet/MockBeaconRoots.json new file mode 100644 index 0000000000..0953d24db2 --- /dev/null +++ b/contracts/deployments/mainnet/MockBeaconRoots.json @@ -0,0 +1,160 @@ +{ + "address": "0x4A50Bb6153965B94eB59882D80BCC7Db146212E6", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "root", + "type": "bytes32" + } + ], + "name": "RootSet", + "type": "event" + }, + { + "stateMutability": "nonpayable", + "type": "fallback" + }, + { + "inputs": [], + "name": "latestBlockRoot", + "outputs": [ + { + "internalType": "bytes32", + "name": "parentRoot", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "timestamp", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "timestamp", + "type": "uint64" + } + ], + "name": "parentBlockRoot", + "outputs": [ + { + "internalType": "bytes32", + "name": "parentRoot", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "root", + "type": "bytes32" + } + ], + "name": "setBeaconRoot", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "root", + "type": "bytes32" + } + ], + "name": "setBeaconRoot", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "transactionHash": "0xd6ffd26c5f2d8b590e0fa47d4fb9a6c151d9b74264e1bfe8c76148baaca1b2d4", + "receipt": { + "to": null, + "from": "0x3Ba227D87c2A7aB89EAaCEFbeD9bfa0D15Ad249A", + "contractAddress": "0x4A50Bb6153965B94eB59882D80BCC7Db146212E6", + "transactionIndex": 62, + "gasUsed": "305655", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x27b28d6cfd11aa680480a6d89d9422f26643979f717fadad6af8fa3f656a0fe2", + "transactionHash": "0xd6ffd26c5f2d8b590e0fa47d4fb9a6c151d9b74264e1bfe8c76148baaca1b2d4", + "logs": [], + "blockNumber": 23023887, + "cumulativeGasUsed": "3165149", + "status": 1, + "byzantium": true + }, + "args": [], + "numDeployments": 1, + "solcInputHash": "3de98f56666780054699342674f08b14", + "metadata": "{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"name\":\"RootSet\",\"type\":\"event\"},{\"stateMutability\":\"nonpayable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"latestBlockRoot\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"parentRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"}],\"name\":\"parentBlockRoot\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"parentRoot\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"name\":\"setBeaconRoot\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"name\":\"setBeaconRoot\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/mocks/MockBeaconRoots.sol\":\"MockBeaconRoots\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"contracts/beacon/BeaconRoots.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nlibrary BeaconRoots {\\n /// @notice The address of beacon block roots oracle\\n /// See https://eips.ethereum.org/EIPS/eip-4788\\n address internal constant BEACON_ROOTS_ADDRESS =\\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\\n\\n /// @notice Returns the Beacon Block Root for the previous block.\\n /// This comes from the Beacon Roots contract defined in EIP-4788.\\n /// This will revert if the block is more than 8,191 blocks old as\\n /// that is the size of the beacon root's ring buffer.\\n /// @param timestamp The timestamp of the block for which to get the parent root.\\n /// @return parentRoot The parent block root for the given timestamp.\\n function parentBlockRoot(uint64 timestamp)\\n internal\\n view\\n returns (bytes32 parentRoot)\\n {\\n // Call the Beacon Block Root Oracle to get the parent block root\\n // This does not have a function signature, so we use a staticcall.\\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\\n abi.encode(timestamp)\\n );\\n\\n require(success && result.length > 0, \\\"Invalid beacon timestamp\\\");\\n parentRoot = abi.decode(result, (bytes32));\\n }\\n}\\n\",\"keccak256\":\"0x8b678da4c214faae35a2a5dd99b32e2a12f68676ddc2620f0f684bc7c4bb517c\",\"license\":\"BUSL-1.1\"},\"contracts/mocks/MockBeaconRoots.sol\":{\"content\":\"// SPDX-License-Identifier: BUSL-1.1\\npragma solidity ^0.8.0;\\n\\nimport { BeaconRoots } from \\\"../beacon/BeaconRoots.sol\\\";\\n\\ncontract MockBeaconRoots {\\n // Mapping to simulate the ring buffer: timestamp => beacon block root\\n mapping(uint256 => bytes32) internal _beaconRoots;\\n\\n // Event to log when a new root is set (for testing)\\n event RootSet(uint256 indexed timestamp, bytes32 root);\\n\\n // Fallback function to handle raw 32-byte timestamp input\\n // solhint-disable no-complex-fallback\\n fallback() external {\\n // Ensure input is exactly 32 bytes (big-endian encoded timestamp)\\n require(msg.data.length == 32, \\\"Input must be 32 bytes\\\");\\n\\n // Decode the 32-byte input as a uint256 timestamp (big-endian)\\n uint256 timestamp = abi.decode(msg.data, (uint256));\\n\\n // Don't do any validation of timestamp so we can test any block\\n\\n // Retrieve the root. Will return bytes32(0) if not set.\\n bytes32 root = _beaconRoots[timestamp];\\n\\n // Return the 32-byte root directly\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n mstore(0, root)\\n return(0, 32)\\n }\\n }\\n\\n // Mock function to set a beacon block root (for testing)\\n function setBeaconRoot(uint256 timestamp, bytes32 root) external {\\n require(timestamp > 0, \\\"Invalid timestamp\\\");\\n require(root != bytes32(0), \\\"Invalid root\\\");\\n\\n // Store the root at the given timestamp\\n _beaconRoots[timestamp] = root;\\n\\n emit RootSet(timestamp, root);\\n }\\n\\n function setBeaconRoot(bytes32 root) external {\\n require(root != bytes32(0), \\\"Invalid root\\\");\\n\\n // Store the root at the given timestamp\\n _beaconRoots[block.timestamp] = root;\\n\\n emit RootSet(block.timestamp, root);\\n }\\n\\n function parentBlockRoot(uint64 timestamp)\\n external\\n view\\n returns (bytes32 parentRoot)\\n {\\n return BeaconRoots.parentBlockRoot(timestamp);\\n }\\n\\n function latestBlockRoot()\\n external\\n view\\n returns (bytes32 parentRoot, uint64 timestamp)\\n {\\n timestamp = uint64(block.timestamp);\\n parentRoot = BeaconRoots.parentBlockRoot(timestamp);\\n }\\n}\\n\",\"keccak256\":\"0xbef3bd6e254e9f3ac8174ad1c62610d137c08589c3b57846ecc8c31b0eadb96c\",\"license\":\"BUSL-1.1\"}},\"version\":1}", + "bytecode": "0x6080604052348015600f57600080fd5b506104918061001f6000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80636370dd3b146100be57806365190fad146100d3578063aa24b3c4146100e6578063be7c629b1461010c575b6020361461009a5760405162461bcd60e51b8152602060048201526016602482015275496e707574206d75737420626520333220627974657360501b60448201526064015b60405180910390fd5b60006100a636826103a7565b60008181526020818152604082205480835292935090f35b6100d16100cc3660046103c0565b610132565b005b6100d16100e13660046103a7565b6101fb565b6100f96100f43660046103e2565b61027e565b6040519081526020015b60405180910390f35b61011461028f565b6040805192835267ffffffffffffffff909116602083015201610103565b600082116101765760405162461bcd60e51b81526020600482015260116024820152700496e76616c69642074696d657374616d7607c1b6044820152606401610091565b806101b25760405162461bcd60e51b815260206004820152600c60248201526b125b9d985b1a59081c9bdbdd60a21b6044820152606401610091565b60008281526020818152604091829020839055905182815283917f1287e38cf3268df2db9ff3d0625e53548664e304cae1349b9575b02418226a0b910160405180910390a25050565b806102375760405162461bcd60e51b815260206004820152600c60248201526b125b9d985b1a59081c9bdbdd60a21b6044820152606401610091565b426000818152602081815260409182902084905590518381527f1287e38cf3268df2db9ff3d0625e53548664e304cae1349b9575b02418226a0b910160405180910390a250565b6000610289826102a1565b92915050565b60004261029b816102a1565b91509091565b6040805167ffffffffffffffff8316602082015260009182918291720f3df6d732807ef1319fb7b8bb8522d0beac02910160408051601f19818403018152908290526102ec91610413565b600060405180830381855afa9150503d8060008114610327576040519150601f19603f3d011682016040523d82523d6000602084013e61032c565b606091505b509150915081801561033f575060008151115b61038b5760405162461bcd60e51b815260206004820152601860248201527f496e76616c696420626561636f6e2074696d657374616d7000000000000000006044820152606401610091565b8080602001905181019061039f9190610442565b949350505050565b6000602082840312156103b957600080fd5b5035919050565b600080604083850312156103d357600080fd5b50508035926020909101359150565b6000602082840312156103f457600080fd5b813567ffffffffffffffff8116811461040c57600080fd5b9392505050565b6000825160005b81811015610434576020818601810151858301520161041a565b506000920191825250919050565b60006020828403121561045457600080fd5b505191905056fea2646970667358221220572e6bf12c9522ed92ef5a570bf0951c3ab805843607a7d5b686f11f825c794c64736f6c634300081c0033", + "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061004c5760003560e01c80636370dd3b146100be57806365190fad146100d3578063aa24b3c4146100e6578063be7c629b1461010c575b6020361461009a5760405162461bcd60e51b8152602060048201526016602482015275496e707574206d75737420626520333220627974657360501b60448201526064015b60405180910390fd5b60006100a636826103a7565b60008181526020818152604082205480835292935090f35b6100d16100cc3660046103c0565b610132565b005b6100d16100e13660046103a7565b6101fb565b6100f96100f43660046103e2565b61027e565b6040519081526020015b60405180910390f35b61011461028f565b6040805192835267ffffffffffffffff909116602083015201610103565b600082116101765760405162461bcd60e51b81526020600482015260116024820152700496e76616c69642074696d657374616d7607c1b6044820152606401610091565b806101b25760405162461bcd60e51b815260206004820152600c60248201526b125b9d985b1a59081c9bdbdd60a21b6044820152606401610091565b60008281526020818152604091829020839055905182815283917f1287e38cf3268df2db9ff3d0625e53548664e304cae1349b9575b02418226a0b910160405180910390a25050565b806102375760405162461bcd60e51b815260206004820152600c60248201526b125b9d985b1a59081c9bdbdd60a21b6044820152606401610091565b426000818152602081815260409182902084905590518381527f1287e38cf3268df2db9ff3d0625e53548664e304cae1349b9575b02418226a0b910160405180910390a250565b6000610289826102a1565b92915050565b60004261029b816102a1565b91509091565b6040805167ffffffffffffffff8316602082015260009182918291720f3df6d732807ef1319fb7b8bb8522d0beac02910160408051601f19818403018152908290526102ec91610413565b600060405180830381855afa9150503d8060008114610327576040519150601f19603f3d011682016040523d82523d6000602084013e61032c565b606091505b509150915081801561033f575060008151115b61038b5760405162461bcd60e51b815260206004820152601860248201527f496e76616c696420626561636f6e2074696d657374616d7000000000000000006044820152606401610091565b8080602001905181019061039f9190610442565b949350505050565b6000602082840312156103b957600080fd5b5035919050565b600080604083850312156103d357600080fd5b50508035926020909101359150565b6000602082840312156103f457600080fd5b813567ffffffffffffffff8116811461040c57600080fd5b9392505050565b6000825160005b81811015610434576020818601810151858301520161041a565b506000920191825250919050565b60006020828403121561045457600080fd5b505191905056fea2646970667358221220572e6bf12c9522ed92ef5a570bf0951c3ab805843607a7d5b686f11f825c794c64736f6c634300081c0033", + "libraries": {}, + "devdoc": { + "kind": "dev", + "methods": {}, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": {}, + "version": 1 + }, + "storageLayout": { + "storage": [ + { + "astId": 31043, + "contract": "contracts/mocks/MockBeaconRoots.sol:MockBeaconRoots", + "label": "_beaconRoots", + "offset": 0, + "slot": "0", + "type": "t_mapping(t_uint256,t_bytes32)" + } + ], + "types": { + "t_bytes32": { + "encoding": "inplace", + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_bytes32)": { + "encoding": "mapping", + "key": "t_uint256", + "label": "mapping(uint256 => bytes32)", + "numberOfBytes": "32", + "value": "t_bytes32" + }, + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } +} \ No newline at end of file diff --git a/contracts/deployments/mainnet/solcInputs/3de98f56666780054699342674f08b14.json b/contracts/deployments/mainnet/solcInputs/3de98f56666780054699342674f08b14.json new file mode 100644 index 0000000000..a0762a62f6 --- /dev/null +++ b/contracts/deployments/mainnet/solcInputs/3de98f56666780054699342674f08b14.json @@ -0,0 +1,1155 @@ +{ + "language": "Solidity", + "sources": { + "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {Client} from \"../libraries/Client.sol\";\n\ninterface IRouterClient {\n error UnsupportedDestinationChain(uint64 destChainSelector);\n error InsufficientFeeTokenAmount();\n error InvalidMsgValue();\n\n /// @notice Checks if the given chain ID is supported for sending/receiving.\n /// @param chainSelector The chain to check.\n /// @return supported is true if it is supported, false if not.\n function isChainSupported(uint64 chainSelector) external view returns (bool supported);\n\n /// @notice Gets a list of all supported tokens which can be sent or received\n /// to/from a given chain id.\n /// @param chainSelector The chainSelector.\n /// @return tokens The addresses of all tokens that are supported.\n function getSupportedTokens(uint64 chainSelector) external view returns (address[] memory tokens);\n\n /// @param destinationChainSelector The destination chainSelector\n /// @param message The cross-chain CCIP message including data and/or tokens\n /// @return fee returns execution fee for the message\n /// delivery to destination chain, denominated in the feeToken specified in the message.\n /// @dev Reverts with appropriate reason upon invalid message.\n function getFee(\n uint64 destinationChainSelector,\n Client.EVM2AnyMessage memory message\n ) external view returns (uint256 fee);\n\n /// @notice Request a message to be sent to the destination chain\n /// @param destinationChainSelector The destination chain ID\n /// @param message The cross-chain CCIP message including data and/or tokens\n /// @return messageId The message ID\n /// @dev Note if msg.value is larger than the required fee (from getFee) we accept\n /// the overpayment with no refund.\n /// @dev Reverts with appropriate reason upon invalid message.\n function ccipSend(\n uint64 destinationChainSelector,\n Client.EVM2AnyMessage calldata message\n ) external payable returns (bytes32);\n}\n" + }, + "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// End consumer library.\nlibrary Client {\n /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.\n struct EVMTokenAmount {\n address token; // token address on the local chain.\n uint256 amount; // Amount of tokens.\n }\n\n struct Any2EVMMessage {\n bytes32 messageId; // MessageId corresponding to ccipSend on source.\n uint64 sourceChainSelector; // Source chain selector.\n bytes sender; // abi.decode(sender) if coming from an EVM chain.\n bytes data; // payload sent in original message.\n EVMTokenAmount[] destTokenAmounts; // Tokens and their amounts in their destination chain representation.\n }\n\n // If extraArgs is empty bytes, the default is 200k gas limit.\n struct EVM2AnyMessage {\n bytes receiver; // abi.encode(receiver address) for dest EVM chains\n bytes data; // Data payload\n EVMTokenAmount[] tokenAmounts; // Token transfers\n address feeToken; // Address of feeToken. address(0) means you will send msg.value.\n bytes extraArgs; // Populate this with _argsToBytes(EVMExtraArgsV1)\n }\n\n // bytes4(keccak256(\"CCIP EVMExtraArgsV1\"));\n bytes4 public constant EVM_EXTRA_ARGS_V1_TAG = 0x97a657c9;\n struct EVMExtraArgsV1 {\n uint256 gasLimit;\n }\n\n function _argsToBytes(EVMExtraArgsV1 memory extraArgs) internal pure returns (bytes memory bts) {\n return abi.encodeWithSelector(EVM_EXTRA_ARGS_V1_TAG, extraArgs);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport \"@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol\";\n\nlibrary ExecutorOptions {\n using CalldataBytesLib for bytes;\n\n uint8 internal constant WORKER_ID = 1;\n\n uint8 internal constant OPTION_TYPE_LZRECEIVE = 1;\n uint8 internal constant OPTION_TYPE_NATIVE_DROP = 2;\n uint8 internal constant OPTION_TYPE_LZCOMPOSE = 3;\n uint8 internal constant OPTION_TYPE_ORDERED_EXECUTION = 4;\n uint8 internal constant OPTION_TYPE_LZREAD = 5;\n\n error Executor_InvalidLzReceiveOption();\n error Executor_InvalidNativeDropOption();\n error Executor_InvalidLzComposeOption();\n error Executor_InvalidLzReadOption();\n\n /// @dev decode the next executor option from the options starting from the specified cursor\n /// @param _options [executor_id][executor_option][executor_id][executor_option]...\n /// executor_option = [option_size][option_type][option]\n /// option_size = len(option_type) + len(option)\n /// executor_id: uint8, option_size: uint16, option_type: uint8, option: bytes\n /// @param _cursor the cursor to start decoding from\n /// @return optionType the type of the option\n /// @return option the option of the executor\n /// @return cursor the cursor to start decoding the next executor option\n function nextExecutorOption(\n bytes calldata _options,\n uint256 _cursor\n ) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) {\n unchecked {\n // skip worker id\n cursor = _cursor + 1;\n\n // read option size\n uint16 size = _options.toU16(cursor);\n cursor += 2;\n\n // read option type\n optionType = _options.toU8(cursor);\n\n // startCursor and endCursor are used to slice the option from _options\n uint256 startCursor = cursor + 1; // skip option type\n uint256 endCursor = cursor + size;\n option = _options[startCursor:endCursor];\n cursor += size;\n }\n }\n\n function decodeLzReceiveOption(bytes calldata _option) internal pure returns (uint128 gas, uint128 value) {\n if (_option.length != 16 && _option.length != 32) revert Executor_InvalidLzReceiveOption();\n gas = _option.toU128(0);\n value = _option.length == 32 ? _option.toU128(16) : 0;\n }\n\n function decodeNativeDropOption(bytes calldata _option) internal pure returns (uint128 amount, bytes32 receiver) {\n if (_option.length != 48) revert Executor_InvalidNativeDropOption();\n amount = _option.toU128(0);\n receiver = _option.toB32(16);\n }\n\n function decodeLzComposeOption(\n bytes calldata _option\n ) internal pure returns (uint16 index, uint128 gas, uint128 value) {\n if (_option.length != 18 && _option.length != 34) revert Executor_InvalidLzComposeOption();\n index = _option.toU16(0);\n gas = _option.toU128(2);\n value = _option.length == 34 ? _option.toU128(18) : 0;\n }\n\n function decodeLzReadOption(\n bytes calldata _option\n ) internal pure returns (uint128 gas, uint32 calldataSize, uint128 value) {\n if (_option.length != 20 && _option.length != 36) revert Executor_InvalidLzReadOption();\n gas = _option.toU128(0);\n calldataSize = _option.toU32(16);\n value = _option.length == 36 ? _option.toU128(20) : 0;\n }\n\n function encodeLzReceiveOption(uint128 _gas, uint128 _value) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_gas) : abi.encodePacked(_gas, _value);\n }\n\n function encodeNativeDropOption(uint128 _amount, bytes32 _receiver) internal pure returns (bytes memory) {\n return abi.encodePacked(_amount, _receiver);\n }\n\n function encodeLzComposeOption(uint16 _index, uint128 _gas, uint128 _value) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_index, _gas) : abi.encodePacked(_index, _gas, _value);\n }\n\n function encodeLzReadOption(\n uint128 _gas,\n uint32 _calldataSize,\n uint128 _value\n ) internal pure returns (bytes memory) {\n return _value == 0 ? abi.encodePacked(_gas, _calldataSize) : abi.encodePacked(_gas, _calldataSize, _value);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/DVNOptions.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport { BytesLib } from \"solidity-bytes-utils/contracts/BytesLib.sol\";\n\nimport { BitMap256 } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/BitMaps.sol\";\nimport { CalldataBytesLib } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol\";\n\nlibrary DVNOptions {\n using CalldataBytesLib for bytes;\n using BytesLib for bytes;\n\n uint8 internal constant WORKER_ID = 2;\n uint8 internal constant OPTION_TYPE_PRECRIME = 1;\n\n error DVN_InvalidDVNIdx();\n error DVN_InvalidDVNOptions(uint256 cursor);\n\n /// @dev group dvn options by its idx\n /// @param _options [dvn_id][dvn_option][dvn_id][dvn_option]...\n /// dvn_option = [option_size][dvn_idx][option_type][option]\n /// option_size = len(dvn_idx) + len(option_type) + len(option)\n /// dvn_id: uint8, dvn_idx: uint8, option_size: uint16, option_type: uint8, option: bytes\n /// @return dvnOptions the grouped options, still share the same format of _options\n /// @return dvnIndices the dvn indices\n function groupDVNOptionsByIdx(\n bytes memory _options\n ) internal pure returns (bytes[] memory dvnOptions, uint8[] memory dvnIndices) {\n if (_options.length == 0) return (dvnOptions, dvnIndices);\n\n uint8 numDVNs = getNumDVNs(_options);\n\n // if there is only 1 dvn, we can just return the whole options\n if (numDVNs == 1) {\n dvnOptions = new bytes[](1);\n dvnOptions[0] = _options;\n\n dvnIndices = new uint8[](1);\n dvnIndices[0] = _options.toUint8(3); // dvn idx\n return (dvnOptions, dvnIndices);\n }\n\n // otherwise, we need to group the options by dvn_idx\n dvnIndices = new uint8[](numDVNs);\n dvnOptions = new bytes[](numDVNs);\n unchecked {\n uint256 cursor = 0;\n uint256 start = 0;\n uint8 lastDVNIdx = 255; // 255 is an invalid dvn_idx\n\n while (cursor < _options.length) {\n ++cursor; // skip worker_id\n\n // optionLength asserted in getNumDVNs (skip check)\n uint16 optionLength = _options.toUint16(cursor);\n cursor += 2;\n\n // dvnIdx asserted in getNumDVNs (skip check)\n uint8 dvnIdx = _options.toUint8(cursor);\n\n // dvnIdx must equal to the lastDVNIdx for the first option\n // so it is always skipped in the first option\n // this operation slices out options whenever the scan finds a different lastDVNIdx\n if (lastDVNIdx == 255) {\n lastDVNIdx = dvnIdx;\n } else if (dvnIdx != lastDVNIdx) {\n uint256 len = cursor - start - 3; // 3 is for worker_id and option_length\n bytes memory opt = _options.slice(start, len);\n _insertDVNOptions(dvnOptions, dvnIndices, lastDVNIdx, opt);\n\n // reset the start and lastDVNIdx\n start += len;\n lastDVNIdx = dvnIdx;\n }\n\n cursor += optionLength;\n }\n\n // skip check the cursor here because the cursor is asserted in getNumDVNs\n // if we have reached the end of the options, we need to process the last dvn\n uint256 size = cursor - start;\n bytes memory op = _options.slice(start, size);\n _insertDVNOptions(dvnOptions, dvnIndices, lastDVNIdx, op);\n\n // revert dvnIndices to start from 0\n for (uint8 i = 0; i < numDVNs; ++i) {\n --dvnIndices[i];\n }\n }\n }\n\n function _insertDVNOptions(\n bytes[] memory _dvnOptions,\n uint8[] memory _dvnIndices,\n uint8 _dvnIdx,\n bytes memory _newOptions\n ) internal pure {\n // dvnIdx starts from 0 but default value of dvnIndices is 0,\n // so we tell if the slot is empty by adding 1 to dvnIdx\n if (_dvnIdx == 255) revert DVN_InvalidDVNIdx();\n uint8 dvnIdxAdj = _dvnIdx + 1;\n\n for (uint256 j = 0; j < _dvnIndices.length; ++j) {\n uint8 index = _dvnIndices[j];\n if (dvnIdxAdj == index) {\n _dvnOptions[j] = abi.encodePacked(_dvnOptions[j], _newOptions);\n break;\n } else if (index == 0) {\n // empty slot, that means it is the first time we see this dvn\n _dvnIndices[j] = dvnIdxAdj;\n _dvnOptions[j] = _newOptions;\n break;\n }\n }\n }\n\n /// @dev get the number of unique dvns\n /// @param _options the format is the same as groupDVNOptionsByIdx\n function getNumDVNs(bytes memory _options) internal pure returns (uint8 numDVNs) {\n uint256 cursor = 0;\n BitMap256 bitmap;\n\n // find number of unique dvn_idx\n unchecked {\n while (cursor < _options.length) {\n ++cursor; // skip worker_id\n\n uint16 optionLength = _options.toUint16(cursor);\n cursor += 2;\n if (optionLength < 2) revert DVN_InvalidDVNOptions(cursor); // at least 1 byte for dvn_idx and 1 byte for option_type\n\n uint8 dvnIdx = _options.toUint8(cursor);\n\n // if dvnIdx is not set, increment numDVNs\n // max num of dvns is 255, 255 is an invalid dvn_idx\n // The order of the dvnIdx is not required to be sequential, as enforcing the order may weaken\n // the composability of the options. e.g. if we refrain from enforcing the order, an OApp that has\n // already enforced certain options can append additional options to the end of the enforced\n // ones without restrictions.\n if (dvnIdx == 255) revert DVN_InvalidDVNIdx();\n if (!bitmap.get(dvnIdx)) {\n ++numDVNs;\n bitmap = bitmap.set(dvnIdx);\n }\n\n cursor += optionLength;\n }\n }\n if (cursor != _options.length) revert DVN_InvalidDVNOptions(cursor);\n }\n\n /// @dev decode the next dvn option from _options starting from the specified cursor\n /// @param _options the format is the same as groupDVNOptionsByIdx\n /// @param _cursor the cursor to start decoding\n /// @return optionType the type of the option\n /// @return option the option\n /// @return cursor the cursor to start decoding the next option\n function nextDVNOption(\n bytes calldata _options,\n uint256 _cursor\n ) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) {\n unchecked {\n // skip worker id\n cursor = _cursor + 1;\n\n // read option size\n uint16 size = _options.toU16(cursor);\n cursor += 2;\n\n // read option type\n optionType = _options.toU8(cursor + 1); // skip dvn_idx\n\n // startCursor and endCursor are used to slice the option from _options\n uint256 startCursor = cursor + 2; // skip option type and dvn_idx\n uint256 endCursor = cursor + size;\n option = _options[startCursor:endCursor];\n cursor += size;\n }\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { IMessageLibManager } from \"./IMessageLibManager.sol\";\nimport { IMessagingComposer } from \"./IMessagingComposer.sol\";\nimport { IMessagingChannel } from \"./IMessagingChannel.sol\";\nimport { IMessagingContext } from \"./IMessagingContext.sol\";\n\nstruct MessagingParams {\n uint32 dstEid;\n bytes32 receiver;\n bytes message;\n bytes options;\n bool payInLzToken;\n}\n\nstruct MessagingReceipt {\n bytes32 guid;\n uint64 nonce;\n MessagingFee fee;\n}\n\nstruct MessagingFee {\n uint256 nativeFee;\n uint256 lzTokenFee;\n}\n\nstruct Origin {\n uint32 srcEid;\n bytes32 sender;\n uint64 nonce;\n}\n\ninterface ILayerZeroEndpointV2 is IMessageLibManager, IMessagingComposer, IMessagingChannel, IMessagingContext {\n event PacketSent(bytes encodedPayload, bytes options, address sendLibrary);\n\n event PacketVerified(Origin origin, address receiver, bytes32 payloadHash);\n\n event PacketDelivered(Origin origin, address receiver);\n\n event LzReceiveAlert(\n address indexed receiver,\n address indexed executor,\n Origin origin,\n bytes32 guid,\n uint256 gas,\n uint256 value,\n bytes message,\n bytes extraData,\n bytes reason\n );\n\n event LzTokenSet(address token);\n\n event DelegateSet(address sender, address delegate);\n\n function quote(MessagingParams calldata _params, address _sender) external view returns (MessagingFee memory);\n\n function send(\n MessagingParams calldata _params,\n address _refundAddress\n ) external payable returns (MessagingReceipt memory);\n\n function verify(Origin calldata _origin, address _receiver, bytes32 _payloadHash) external;\n\n function verifiable(Origin calldata _origin, address _receiver) external view returns (bool);\n\n function initializable(Origin calldata _origin, address _receiver) external view returns (bool);\n\n function lzReceive(\n Origin calldata _origin,\n address _receiver,\n bytes32 _guid,\n bytes calldata _message,\n bytes calldata _extraData\n ) external payable;\n\n // oapp can burn messages partially by calling this function with its own business logic if messages are verified in order\n function clear(address _oapp, Origin calldata _origin, bytes32 _guid, bytes calldata _message) external;\n\n function setLzToken(address _lzToken) external;\n\n function lzToken() external view returns (address);\n\n function nativeToken() external view returns (address);\n\n function setDelegate(address _delegate) external;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { Origin } from \"./ILayerZeroEndpointV2.sol\";\n\ninterface ILayerZeroReceiver {\n function allowInitializePath(Origin calldata _origin) external view returns (bool);\n\n function nextNonce(uint32 _eid, bytes32 _sender) external view returns (uint64);\n\n function lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) external payable;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessageLib.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { IERC165 } from \"@openzeppelin/contracts/utils/introspection/IERC165.sol\";\n\nimport { SetConfigParam } from \"./IMessageLibManager.sol\";\n\nenum MessageLibType {\n Send,\n Receive,\n SendAndReceive\n}\n\ninterface IMessageLib is IERC165 {\n function setConfig(address _oapp, SetConfigParam[] calldata _config) external;\n\n function getConfig(uint32 _eid, address _oapp, uint32 _configType) external view returns (bytes memory config);\n\n function isSupportedEid(uint32 _eid) external view returns (bool);\n\n // message libs of same major version are compatible\n function version() external view returns (uint64 major, uint8 minor, uint8 endpointVersion);\n\n function messageLibType() external view returns (MessageLibType);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessageLibManager.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nstruct SetConfigParam {\n uint32 eid;\n uint32 configType;\n bytes config;\n}\n\ninterface IMessageLibManager {\n struct Timeout {\n address lib;\n uint256 expiry;\n }\n\n event LibraryRegistered(address newLib);\n event DefaultSendLibrarySet(uint32 eid, address newLib);\n event DefaultReceiveLibrarySet(uint32 eid, address newLib);\n event DefaultReceiveLibraryTimeoutSet(uint32 eid, address oldLib, uint256 expiry);\n event SendLibrarySet(address sender, uint32 eid, address newLib);\n event ReceiveLibrarySet(address receiver, uint32 eid, address newLib);\n event ReceiveLibraryTimeoutSet(address receiver, uint32 eid, address oldLib, uint256 timeout);\n\n function registerLibrary(address _lib) external;\n\n function isRegisteredLibrary(address _lib) external view returns (bool);\n\n function getRegisteredLibraries() external view returns (address[] memory);\n\n function setDefaultSendLibrary(uint32 _eid, address _newLib) external;\n\n function defaultSendLibrary(uint32 _eid) external view returns (address);\n\n function setDefaultReceiveLibrary(uint32 _eid, address _newLib, uint256 _gracePeriod) external;\n\n function defaultReceiveLibrary(uint32 _eid) external view returns (address);\n\n function setDefaultReceiveLibraryTimeout(uint32 _eid, address _lib, uint256 _expiry) external;\n\n function defaultReceiveLibraryTimeout(uint32 _eid) external view returns (address lib, uint256 expiry);\n\n function isSupportedEid(uint32 _eid) external view returns (bool);\n\n function isValidReceiveLibrary(address _receiver, uint32 _eid, address _lib) external view returns (bool);\n\n /// ------------------- OApp interfaces -------------------\n function setSendLibrary(address _oapp, uint32 _eid, address _newLib) external;\n\n function getSendLibrary(address _sender, uint32 _eid) external view returns (address lib);\n\n function isDefaultSendLibrary(address _sender, uint32 _eid) external view returns (bool);\n\n function setReceiveLibrary(address _oapp, uint32 _eid, address _newLib, uint256 _gracePeriod) external;\n\n function getReceiveLibrary(address _receiver, uint32 _eid) external view returns (address lib, bool isDefault);\n\n function setReceiveLibraryTimeout(address _oapp, uint32 _eid, address _lib, uint256 _expiry) external;\n\n function receiveLibraryTimeout(address _receiver, uint32 _eid) external view returns (address lib, uint256 expiry);\n\n function setConfig(address _oapp, address _lib, SetConfigParam[] calldata _params) external;\n\n function getConfig(\n address _oapp,\n address _lib,\n uint32 _eid,\n uint32 _configType\n ) external view returns (bytes memory config);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingChannel.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingChannel {\n event InboundNonceSkipped(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce);\n event PacketNilified(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);\n event PacketBurnt(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);\n\n function eid() external view returns (uint32);\n\n // this is an emergency function if a message cannot be verified for some reasons\n // required to provide _nextNonce to avoid race condition\n function skip(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce) external;\n\n function nilify(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;\n\n function burn(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;\n\n function nextGuid(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (bytes32);\n\n function inboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);\n\n function outboundNonce(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (uint64);\n\n function inboundPayloadHash(\n address _receiver,\n uint32 _srcEid,\n bytes32 _sender,\n uint64 _nonce\n ) external view returns (bytes32);\n\n function lazyInboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingComposer.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingComposer {\n event ComposeSent(address from, address to, bytes32 guid, uint16 index, bytes message);\n event ComposeDelivered(address from, address to, bytes32 guid, uint16 index);\n event LzComposeAlert(\n address indexed from,\n address indexed to,\n address indexed executor,\n bytes32 guid,\n uint16 index,\n uint256 gas,\n uint256 value,\n bytes message,\n bytes extraData,\n bytes reason\n );\n\n function composeQueue(\n address _from,\n address _to,\n bytes32 _guid,\n uint16 _index\n ) external view returns (bytes32 messageHash);\n\n function sendCompose(address _to, bytes32 _guid, uint16 _index, bytes calldata _message) external;\n\n function lzCompose(\n address _from,\n address _to,\n bytes32 _guid,\n uint16 _index,\n bytes calldata _message,\n bytes calldata _extraData\n ) external payable;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessagingContext.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\ninterface IMessagingContext {\n function isSendingMessage() external view returns (bool);\n\n function getSendContext() external view returns (uint32 dstEid, address sender);\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ISendLib.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.8.0;\n\nimport { MessagingFee } from \"./ILayerZeroEndpointV2.sol\";\nimport { IMessageLib } from \"./IMessageLib.sol\";\n\nstruct Packet {\n uint64 nonce;\n uint32 srcEid;\n address sender;\n uint32 dstEid;\n bytes32 receiver;\n bytes32 guid;\n bytes message;\n}\n\ninterface ISendLib is IMessageLib {\n function send(\n Packet calldata _packet,\n bytes calldata _options,\n bool _payInLzToken\n ) external returns (MessagingFee memory, bytes memory encodedPacket);\n\n function quote(\n Packet calldata _packet,\n bytes calldata _options,\n bool _payInLzToken\n ) external view returns (MessagingFee memory);\n\n function setTreasury(address _treasury) external;\n\n function withdrawFee(address _to, uint256 _amount) external;\n\n function withdrawLzTokenFee(address _lzToken, address _to, uint256 _amount) external;\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/AddressCast.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nlibrary AddressCast {\n error AddressCast_InvalidSizeForAddress();\n error AddressCast_InvalidAddress();\n\n function toBytes32(bytes calldata _addressBytes) internal pure returns (bytes32 result) {\n if (_addressBytes.length > 32) revert AddressCast_InvalidAddress();\n result = bytes32(_addressBytes);\n unchecked {\n uint256 offset = 32 - _addressBytes.length;\n result = result >> (offset * 8);\n }\n }\n\n function toBytes32(address _address) internal pure returns (bytes32 result) {\n result = bytes32(uint256(uint160(_address)));\n }\n\n function toBytes(bytes32 _addressBytes32, uint256 _size) internal pure returns (bytes memory result) {\n if (_size == 0 || _size > 32) revert AddressCast_InvalidSizeForAddress();\n result = new bytes(_size);\n unchecked {\n uint256 offset = 256 - _size * 8;\n assembly {\n mstore(add(result, 32), shl(offset, _addressBytes32))\n }\n }\n }\n\n function toAddress(bytes32 _addressBytes32) internal pure returns (address result) {\n result = address(uint160(uint256(_addressBytes32)));\n }\n\n function toAddress(bytes calldata _addressBytes) internal pure returns (address result) {\n if (_addressBytes.length != 20) revert AddressCast_InvalidAddress();\n result = address(bytes20(_addressBytes));\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nlibrary CalldataBytesLib {\n function toU8(bytes calldata _bytes, uint256 _start) internal pure returns (uint8) {\n return uint8(_bytes[_start]);\n }\n\n function toU16(bytes calldata _bytes, uint256 _start) internal pure returns (uint16) {\n unchecked {\n uint256 end = _start + 2;\n return uint16(bytes2(_bytes[_start:end]));\n }\n }\n\n function toU32(bytes calldata _bytes, uint256 _start) internal pure returns (uint32) {\n unchecked {\n uint256 end = _start + 4;\n return uint32(bytes4(_bytes[_start:end]));\n }\n }\n\n function toU64(bytes calldata _bytes, uint256 _start) internal pure returns (uint64) {\n unchecked {\n uint256 end = _start + 8;\n return uint64(bytes8(_bytes[_start:end]));\n }\n }\n\n function toU128(bytes calldata _bytes, uint256 _start) internal pure returns (uint128) {\n unchecked {\n uint256 end = _start + 16;\n return uint128(bytes16(_bytes[_start:end]));\n }\n }\n\n function toU256(bytes calldata _bytes, uint256 _start) internal pure returns (uint256) {\n unchecked {\n uint256 end = _start + 32;\n return uint256(bytes32(_bytes[_start:end]));\n }\n }\n\n function toAddr(bytes calldata _bytes, uint256 _start) internal pure returns (address) {\n unchecked {\n uint256 end = _start + 20;\n return address(bytes20(_bytes[_start:end]));\n }\n }\n\n function toB32(bytes calldata _bytes, uint256 _start) internal pure returns (bytes32) {\n unchecked {\n uint256 end = _start + 32;\n return bytes32(_bytes[_start:end]);\n }\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/BitMaps.sol": { + "content": "// SPDX-License-Identifier: MIT\n\n// modified from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/structs/BitMaps.sol\npragma solidity ^0.8.20;\n\ntype BitMap256 is uint256;\n\nusing BitMaps for BitMap256 global;\n\nlibrary BitMaps {\n /**\n * @dev Returns whether the bit at `index` is set.\n */\n function get(BitMap256 bitmap, uint8 index) internal pure returns (bool) {\n uint256 mask = 1 << index;\n return BitMap256.unwrap(bitmap) & mask != 0;\n }\n\n /**\n * @dev Sets the bit at `index`.\n */\n function set(BitMap256 bitmap, uint8 index) internal pure returns (BitMap256) {\n uint256 mask = 1 << index;\n return BitMap256.wrap(BitMap256.unwrap(bitmap) | mask);\n }\n}\n" + }, + "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol": { + "content": "// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nimport { Packet } from \"../../interfaces/ISendLib.sol\";\nimport { AddressCast } from \"../../libs/AddressCast.sol\";\n\nlibrary PacketV1Codec {\n using AddressCast for address;\n using AddressCast for bytes32;\n\n uint8 internal constant PACKET_VERSION = 1;\n\n // header (version + nonce + path)\n // version\n uint256 private constant PACKET_VERSION_OFFSET = 0;\n // nonce\n uint256 private constant NONCE_OFFSET = 1;\n // path\n uint256 private constant SRC_EID_OFFSET = 9;\n uint256 private constant SENDER_OFFSET = 13;\n uint256 private constant DST_EID_OFFSET = 45;\n uint256 private constant RECEIVER_OFFSET = 49;\n // payload (guid + message)\n uint256 private constant GUID_OFFSET = 81; // keccak256(nonce + path)\n uint256 private constant MESSAGE_OFFSET = 113;\n\n function encode(Packet memory _packet) internal pure returns (bytes memory encodedPacket) {\n encodedPacket = abi.encodePacked(\n PACKET_VERSION,\n _packet.nonce,\n _packet.srcEid,\n _packet.sender.toBytes32(),\n _packet.dstEid,\n _packet.receiver,\n _packet.guid,\n _packet.message\n );\n }\n\n function encodePacketHeader(Packet memory _packet) internal pure returns (bytes memory) {\n return\n abi.encodePacked(\n PACKET_VERSION,\n _packet.nonce,\n _packet.srcEid,\n _packet.sender.toBytes32(),\n _packet.dstEid,\n _packet.receiver\n );\n }\n\n function encodePayload(Packet memory _packet) internal pure returns (bytes memory) {\n return abi.encodePacked(_packet.guid, _packet.message);\n }\n\n function header(bytes calldata _packet) internal pure returns (bytes calldata) {\n return _packet[0:GUID_OFFSET];\n }\n\n function version(bytes calldata _packet) internal pure returns (uint8) {\n return uint8(bytes1(_packet[PACKET_VERSION_OFFSET:NONCE_OFFSET]));\n }\n\n function nonce(bytes calldata _packet) internal pure returns (uint64) {\n return uint64(bytes8(_packet[NONCE_OFFSET:SRC_EID_OFFSET]));\n }\n\n function srcEid(bytes calldata _packet) internal pure returns (uint32) {\n return uint32(bytes4(_packet[SRC_EID_OFFSET:SENDER_OFFSET]));\n }\n\n function sender(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[SENDER_OFFSET:DST_EID_OFFSET]);\n }\n\n function senderAddressB20(bytes calldata _packet) internal pure returns (address) {\n return sender(_packet).toAddress();\n }\n\n function dstEid(bytes calldata _packet) internal pure returns (uint32) {\n return uint32(bytes4(_packet[DST_EID_OFFSET:RECEIVER_OFFSET]));\n }\n\n function receiver(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[RECEIVER_OFFSET:GUID_OFFSET]);\n }\n\n function receiverB20(bytes calldata _packet) internal pure returns (address) {\n return receiver(_packet).toAddress();\n }\n\n function guid(bytes calldata _packet) internal pure returns (bytes32) {\n return bytes32(_packet[GUID_OFFSET:MESSAGE_OFFSET]);\n }\n\n function message(bytes calldata _packet) internal pure returns (bytes calldata) {\n return bytes(_packet[MESSAGE_OFFSET:]);\n }\n\n function payload(bytes calldata _packet) internal pure returns (bytes calldata) {\n return bytes(_packet[GUID_OFFSET:]);\n }\n\n function payloadHash(bytes calldata _packet) internal pure returns (bytes32) {\n return keccak256(payload(_packet));\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { ILayerZeroEndpointV2 } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\n\n/**\n * @title IOAppCore\n */\ninterface IOAppCore {\n // Custom error messages\n error OnlyPeer(uint32 eid, bytes32 sender);\n error NoPeer(uint32 eid);\n error InvalidEndpointCall();\n error InvalidDelegate();\n\n // Event emitted when a peer (OApp) is set for a corresponding endpoint\n event PeerSet(uint32 eid, bytes32 peer);\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n */\n function oAppVersion() external view returns (uint64 senderVersion, uint64 receiverVersion);\n\n /**\n * @notice Retrieves the LayerZero endpoint associated with the OApp.\n * @return iEndpoint The LayerZero endpoint as an interface.\n */\n function endpoint() external view returns (ILayerZeroEndpointV2 iEndpoint);\n\n /**\n * @notice Retrieves the peer (OApp) associated with a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @return peer The peer address (OApp instance) associated with the corresponding endpoint.\n */\n function peers(uint32 _eid) external view returns (bytes32 peer);\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n */\n function setPeer(uint32 _eid, bytes32 _peer) external;\n\n /**\n * @notice Sets the delegate address for the OApp Core.\n * @param _delegate The address of the delegate to be set.\n */\n function setDelegate(address _delegate) external;\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppMsgInspector.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n/**\n * @title IOAppMsgInspector\n * @dev Interface for the OApp Message Inspector, allowing examination of message and options contents.\n */\ninterface IOAppMsgInspector {\n // Custom error message for inspection failure\n error InspectionFailed(bytes message, bytes options);\n\n /**\n * @notice Allows the inspector to examine LayerZero message contents and optionally throw a revert if invalid.\n * @param _message The message payload to be inspected.\n * @param _options Additional options or parameters for inspection.\n * @return valid A boolean indicating whether the inspection passed (true) or failed (false).\n *\n * @dev Optionally done as a revert, OR use the boolean provided to handle the failure.\n */\n function inspect(bytes calldata _message, bytes calldata _options) external view returns (bool valid);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppOptionsType3.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Struct representing enforced option parameters.\n */\nstruct EnforcedOptionParam {\n uint32 eid; // Endpoint ID\n uint16 msgType; // Message Type\n bytes options; // Additional options\n}\n\n/**\n * @title IOAppOptionsType3\n * @dev Interface for the OApp with Type 3 Options, allowing the setting and combining of enforced options.\n */\ninterface IOAppOptionsType3 {\n // Custom error message for invalid options\n error InvalidOptions(bytes options);\n\n // Event emitted when enforced options are set\n event EnforcedOptionSet(EnforcedOptionParam[] _enforcedOptions);\n\n /**\n * @notice Sets enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n */\n function setEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) external;\n\n /**\n * @notice Combines options for a given endpoint and message type.\n * @param _eid The endpoint ID.\n * @param _msgType The OApp message type.\n * @param _extraOptions Additional options passed by the caller.\n * @return options The combination of caller specified options AND enforced options.\n */\n function combineOptions(\n uint32 _eid,\n uint16 _msgType,\n bytes calldata _extraOptions\n ) external view returns (bytes memory options);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\nimport { ILayerZeroReceiver, Origin } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroReceiver.sol\";\n\ninterface IOAppReceiver is ILayerZeroReceiver {\n /**\n * @notice Indicates whether an address is an approved composeMsg sender to the Endpoint.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _message The lzReceive payload.\n * @param _sender The sender address.\n * @return isSender Is a valid sender.\n *\n * @dev Applications can optionally choose to implement a separate composeMsg sender that is NOT the bridging layer.\n * @dev The default sender IS the OAppReceiver implementer.\n */\n function isComposeMsgSender(\n Origin calldata _origin,\n bytes calldata _message,\n address _sender\n ) external view returns (bool isSender);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IOAppOptionsType3, EnforcedOptionParam } from \"../interfaces/IOAppOptionsType3.sol\";\n\n/**\n * @title OAppOptionsType3\n * @dev Abstract contract implementing the IOAppOptionsType3 interface with type 3 options.\n */\nabstract contract OAppOptionsType3 is IOAppOptionsType3, Ownable {\n uint16 internal constant OPTION_TYPE_3 = 3;\n\n // @dev The \"msgType\" should be defined in the child contract.\n mapping(uint32 eid => mapping(uint16 msgType => bytes enforcedOption)) public enforcedOptions;\n\n /**\n * @dev Sets the enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Provides a way for the OApp to enforce things like paying for PreCrime, AND/OR minimum dst lzReceive gas amounts etc.\n * @dev These enforced options can vary as the potential options/execution on the remote may differ as per the msgType.\n * eg. Amount of lzReceive() gas necessary to deliver a lzCompose() message adds overhead you dont want to pay\n * if you are only making a standard LayerZero message ie. lzReceive() WITHOUT sendCompose().\n */\n function setEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) public virtual onlyOwner {\n _setEnforcedOptions(_enforcedOptions);\n }\n\n /**\n * @dev Sets the enforced options for specific endpoint and message type combinations.\n * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.\n *\n * @dev Provides a way for the OApp to enforce things like paying for PreCrime, AND/OR minimum dst lzReceive gas amounts etc.\n * @dev These enforced options can vary as the potential options/execution on the remote may differ as per the msgType.\n * eg. Amount of lzReceive() gas necessary to deliver a lzCompose() message adds overhead you dont want to pay\n * if you are only making a standard LayerZero message ie. lzReceive() WITHOUT sendCompose().\n */\n function _setEnforcedOptions(EnforcedOptionParam[] memory _enforcedOptions) internal virtual {\n for (uint256 i = 0; i < _enforcedOptions.length; i++) {\n // @dev Enforced options are only available for optionType 3, as type 1 and 2 dont support combining.\n _assertOptionsType3(_enforcedOptions[i].options);\n enforcedOptions[_enforcedOptions[i].eid][_enforcedOptions[i].msgType] = _enforcedOptions[i].options;\n }\n\n emit EnforcedOptionSet(_enforcedOptions);\n }\n\n /**\n * @notice Combines options for a given endpoint and message type.\n * @param _eid The endpoint ID.\n * @param _msgType The OAPP message type.\n * @param _extraOptions Additional options passed by the caller.\n * @return options The combination of caller specified options AND enforced options.\n *\n * @dev If there is an enforced lzReceive option:\n * - {gasLimit: 200k, msg.value: 1 ether} AND a caller supplies a lzReceive option: {gasLimit: 100k, msg.value: 0.5 ether}\n * - The resulting options will be {gasLimit: 300k, msg.value: 1.5 ether} when the message is executed on the remote lzReceive() function.\n * @dev This presence of duplicated options is handled off-chain in the verifier/executor.\n */\n function combineOptions(\n uint32 _eid,\n uint16 _msgType,\n bytes calldata _extraOptions\n ) public view virtual returns (bytes memory) {\n bytes memory enforced = enforcedOptions[_eid][_msgType];\n\n // No enforced options, pass whatever the caller supplied, even if it's empty or legacy type 1/2 options.\n if (enforced.length == 0) return _extraOptions;\n\n // No caller options, return enforced\n if (_extraOptions.length == 0) return enforced;\n\n // @dev If caller provided _extraOptions, must be type 3 as its the ONLY type that can be combined.\n if (_extraOptions.length >= 2) {\n _assertOptionsType3(_extraOptions);\n // @dev Remove the first 2 bytes containing the type from the _extraOptions and combine with enforced.\n return bytes.concat(enforced, _extraOptions[2:]);\n }\n\n // No valid set of options was found.\n revert InvalidOptions(_extraOptions);\n }\n\n /**\n * @dev Internal function to assert that options are of type 3.\n * @param _options The options to be checked.\n */\n function _assertOptionsType3(bytes memory _options) internal pure virtual {\n uint16 optionsType;\n assembly {\n optionsType := mload(add(_options, 2))\n }\n if (optionsType != OPTION_TYPE_3) revert InvalidOptions(_options);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { BytesLib } from \"solidity-bytes-utils/contracts/BytesLib.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { ExecutorOptions } from \"@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol\";\nimport { DVNOptions } from \"@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/DVNOptions.sol\";\n\n/**\n * @title OptionsBuilder\n * @dev Library for building and encoding various message options.\n */\nlibrary OptionsBuilder {\n using SafeCast for uint256;\n using BytesLib for bytes;\n\n // Constants for options types\n uint16 internal constant TYPE_1 = 1; // legacy options type 1\n uint16 internal constant TYPE_2 = 2; // legacy options type 2\n uint16 internal constant TYPE_3 = 3;\n\n // Custom error message\n error InvalidSize(uint256 max, uint256 actual);\n error InvalidOptionType(uint16 optionType);\n\n // Modifier to ensure only options of type 3 are used\n modifier onlyType3(bytes memory _options) {\n if (_options.toUint16(0) != TYPE_3) revert InvalidOptionType(_options.toUint16(0));\n _;\n }\n\n /**\n * @dev Creates a new options container with type 3.\n * @return options The newly created options container.\n */\n function newOptions() internal pure returns (bytes memory) {\n return abi.encodePacked(TYPE_3);\n }\n\n /**\n * @dev Adds an executor LZ receive option to the existing options.\n * @param _options The existing options container.\n * @param _gas The gasLimit used on the lzReceive() function in the OApp.\n * @param _value The msg.value passed to the lzReceive() function in the OApp.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed by the executor\n * eg. if (_gas: 200k, and _value: 1 ether) AND (_gas: 100k, _value: 0.5 ether) are sent in an option to the LayerZeroEndpoint,\n * that becomes (300k, 1.5 ether) when the message is executed on the remote lzReceive() function.\n */\n function addExecutorLzReceiveOption(\n bytes memory _options,\n uint128 _gas,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzReceiveOption(_gas, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZRECEIVE, option);\n }\n\n /**\n * @dev Adds an executor native drop option to the existing options.\n * @param _options The existing options container.\n * @param _amount The amount for the native value that is airdropped to the 'receiver'.\n * @param _receiver The receiver address for the native drop option.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed by the executor on the remote chain.\n */\n function addExecutorNativeDropOption(\n bytes memory _options,\n uint128 _amount,\n bytes32 _receiver\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeNativeDropOption(_amount, _receiver);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_NATIVE_DROP, option);\n }\n\n // /**\n // * @dev Adds an executor native drop option to the existing options.\n // * @param _options The existing options container.\n // * @param _amount The amount for the native value that is airdropped to the 'receiver'.\n // * @param _receiver The receiver address for the native drop option.\n // * @return options The updated options container.\n // *\n // * @dev When multiples of this option are added, they are summed by the executor on the remote chain.\n // */\n function addExecutorLzReadOption(\n bytes memory _options,\n uint128 _gas,\n uint32 _size,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzReadOption(_gas, _size, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZREAD, option);\n }\n\n /**\n * @dev Adds an executor LZ compose option to the existing options.\n * @param _options The existing options container.\n * @param _index The index for the lzCompose() function call.\n * @param _gas The gasLimit for the lzCompose() function call.\n * @param _value The msg.value for the lzCompose() function call.\n * @return options The updated options container.\n *\n * @dev When multiples of this option are added, they are summed PER index by the executor on the remote chain.\n * @dev If the OApp sends N lzCompose calls on the remote, you must provide N incremented indexes starting with 0.\n * ie. When your remote OApp composes (N = 3) messages, you must set this option for index 0,1,2\n */\n function addExecutorLzComposeOption(\n bytes memory _options,\n uint16 _index,\n uint128 _gas,\n uint128 _value\n ) internal pure onlyType3(_options) returns (bytes memory) {\n bytes memory option = ExecutorOptions.encodeLzComposeOption(_index, _gas, _value);\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZCOMPOSE, option);\n }\n\n /**\n * @dev Adds an executor ordered execution option to the existing options.\n * @param _options The existing options container.\n * @return options The updated options container.\n */\n function addExecutorOrderedExecutionOption(\n bytes memory _options\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_ORDERED_EXECUTION, bytes(\"\"));\n }\n\n /**\n * @dev Adds a DVN pre-crime option to the existing options.\n * @param _options The existing options container.\n * @param _dvnIdx The DVN index for the pre-crime option.\n * @return options The updated options container.\n */\n function addDVNPreCrimeOption(\n bytes memory _options,\n uint8 _dvnIdx\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return addDVNOption(_options, _dvnIdx, DVNOptions.OPTION_TYPE_PRECRIME, bytes(\"\"));\n }\n\n /**\n * @dev Adds an executor option to the existing options.\n * @param _options The existing options container.\n * @param _optionType The type of the executor option.\n * @param _option The encoded data for the executor option.\n * @return options The updated options container.\n */\n function addExecutorOption(\n bytes memory _options,\n uint8 _optionType,\n bytes memory _option\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return\n abi.encodePacked(\n _options,\n ExecutorOptions.WORKER_ID,\n _option.length.toUint16() + 1, // +1 for optionType\n _optionType,\n _option\n );\n }\n\n /**\n * @dev Adds a DVN option to the existing options.\n * @param _options The existing options container.\n * @param _dvnIdx The DVN index for the DVN option.\n * @param _optionType The type of the DVN option.\n * @param _option The encoded data for the DVN option.\n * @return options The updated options container.\n */\n function addDVNOption(\n bytes memory _options,\n uint8 _dvnIdx,\n uint8 _optionType,\n bytes memory _option\n ) internal pure onlyType3(_options) returns (bytes memory) {\n return\n abi.encodePacked(\n _options,\n DVNOptions.WORKER_ID,\n _option.length.toUint16() + 2, // +2 for optionType and dvnIdx\n _dvnIdx,\n _optionType,\n _option\n );\n }\n\n /**\n * @dev Encodes legacy options of type 1.\n * @param _executionGas The gasLimit value passed to lzReceive().\n * @return legacyOptions The encoded legacy options.\n */\n function encodeLegacyOptionsType1(uint256 _executionGas) internal pure returns (bytes memory) {\n if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);\n return abi.encodePacked(TYPE_1, _executionGas);\n }\n\n /**\n * @dev Encodes legacy options of type 2.\n * @param _executionGas The gasLimit value passed to lzReceive().\n * @param _nativeForDst The amount of native air dropped to the receiver.\n * @param _receiver The _nativeForDst receiver address.\n * @return legacyOptions The encoded legacy options of type 2.\n */\n function encodeLegacyOptionsType2(\n uint256 _executionGas,\n uint256 _nativeForDst,\n bytes memory _receiver // @dev Use bytes instead of bytes32 in legacy type 2 for _receiver.\n ) internal pure returns (bytes memory) {\n if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);\n if (_nativeForDst > type(uint128).max) revert InvalidSize(type(uint128).max, _nativeForDst);\n if (_receiver.length > 32) revert InvalidSize(32, _receiver.length);\n return abi.encodePacked(TYPE_2, _executionGas, _nativeForDst, _receiver);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n// @dev Import the 'MessagingFee' and 'MessagingReceipt' so it's exposed to OApp implementers\n// solhint-disable-next-line no-unused-import\nimport { OAppSender, MessagingFee, MessagingReceipt } from \"./OAppSender.sol\";\n// @dev Import the 'Origin' so it's exposed to OApp implementers\n// solhint-disable-next-line no-unused-import\nimport { OAppReceiver, Origin } from \"./OAppReceiver.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OApp\n * @dev Abstract contract serving as the base for OApp implementation, combining OAppSender and OAppReceiver functionality.\n */\nabstract contract OApp is OAppSender, OAppReceiver {\n /**\n * @dev Constructor to initialize the OApp with the provided endpoint and owner.\n * @param _endpoint The address of the LOCAL LayerZero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(address _endpoint, address _delegate) OAppCore(_endpoint, _delegate) {}\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol implementation.\n * @return receiverVersion The version of the OAppReceiver.sol implementation.\n */\n function oAppVersion()\n public\n pure\n virtual\n override(OAppSender, OAppReceiver)\n returns (uint64 senderVersion, uint64 receiverVersion)\n {\n return (SENDER_VERSION, RECEIVER_VERSION);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IOAppCore, ILayerZeroEndpointV2 } from \"./interfaces/IOAppCore.sol\";\n\n/**\n * @title OAppCore\n * @dev Abstract contract implementing the IOAppCore interface with basic OApp configurations.\n */\nabstract contract OAppCore is IOAppCore, Ownable {\n // The LayerZero endpoint associated with the given OApp\n ILayerZeroEndpointV2 public immutable endpoint;\n\n // Mapping to store peers associated with corresponding endpoints\n mapping(uint32 eid => bytes32 peer) public peers;\n\n /**\n * @dev Constructor to initialize the OAppCore with the provided endpoint and delegate.\n * @param _endpoint The address of the LOCAL Layer Zero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n *\n * @dev The delegate typically should be set as the owner of the contract.\n */\n constructor(address _endpoint, address _delegate) {\n endpoint = ILayerZeroEndpointV2(_endpoint);\n\n if (_delegate == address(0)) revert InvalidDelegate();\n endpoint.setDelegate(_delegate);\n }\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.\n * @dev Set this to bytes32(0) to remove the peer address.\n * @dev Peer is a bytes32 to accommodate non-evm chains.\n */\n function setPeer(uint32 _eid, bytes32 _peer) public virtual onlyOwner {\n _setPeer(_eid, _peer);\n }\n\n /**\n * @notice Sets the peer address (OApp instance) for a corresponding endpoint.\n * @param _eid The endpoint ID.\n * @param _peer The address of the peer to be associated with the corresponding endpoint.\n *\n * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.\n * @dev Set this to bytes32(0) to remove the peer address.\n * @dev Peer is a bytes32 to accommodate non-evm chains.\n */\n function _setPeer(uint32 _eid, bytes32 _peer) internal virtual {\n peers[_eid] = _peer;\n emit PeerSet(_eid, _peer);\n }\n\n /**\n * @notice Internal function to get the peer address associated with a specific endpoint; reverts if NOT set.\n * ie. the peer is set to bytes32(0).\n * @param _eid The endpoint ID.\n * @return peer The address of the peer associated with the specified endpoint.\n */\n function _getPeerOrRevert(uint32 _eid) internal view virtual returns (bytes32) {\n bytes32 peer = peers[_eid];\n if (peer == bytes32(0)) revert NoPeer(_eid);\n return peer;\n }\n\n /**\n * @notice Sets the delegate address for the OApp.\n * @param _delegate The address of the delegate to be set.\n *\n * @dev Only the owner/admin of the OApp can call this function.\n * @dev Provides the ability for a delegate to set configs, on behalf of the OApp, directly on the Endpoint contract.\n */\n function setDelegate(address _delegate) public onlyOwner {\n endpoint.setDelegate(_delegate);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppReceiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IOAppReceiver, Origin } from \"./interfaces/IOAppReceiver.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OAppReceiver\n * @dev Abstract contract implementing the ILayerZeroReceiver interface and extending OAppCore for OApp receivers.\n */\nabstract contract OAppReceiver is IOAppReceiver, OAppCore {\n // Custom error message for when the caller is not the registered endpoint/\n error OnlyEndpoint(address addr);\n\n // @dev The version of the OAppReceiver implementation.\n // @dev Version is bumped when changes are made to this contract.\n uint64 internal constant RECEIVER_VERSION = 2;\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n *\n * @dev Providing 0 as the default for OAppSender version. Indicates that the OAppSender is not implemented.\n * ie. this is a RECEIVE only OApp.\n * @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions.\n */\n function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {\n return (0, RECEIVER_VERSION);\n }\n\n /**\n * @notice Indicates whether an address is an approved composeMsg sender to the Endpoint.\n * @dev _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @dev _message The lzReceive payload.\n * @param _sender The sender address.\n * @return isSender Is a valid sender.\n *\n * @dev Applications can optionally choose to implement separate composeMsg senders that are NOT the bridging layer.\n * @dev The default sender IS the OAppReceiver implementer.\n */\n function isComposeMsgSender(\n Origin calldata /*_origin*/,\n bytes calldata /*_message*/,\n address _sender\n ) public view virtual returns (bool) {\n return _sender == address(this);\n }\n\n /**\n * @notice Checks if the path initialization is allowed based on the provided origin.\n * @param origin The origin information containing the source endpoint and sender address.\n * @return Whether the path has been initialized.\n *\n * @dev This indicates to the endpoint that the OApp has enabled msgs for this particular path to be received.\n * @dev This defaults to assuming if a peer has been set, its initialized.\n * Can be overridden by the OApp if there is other logic to determine this.\n */\n function allowInitializePath(Origin calldata origin) public view virtual returns (bool) {\n return peers[origin.srcEid] == origin.sender;\n }\n\n /**\n * @notice Retrieves the next nonce for a given source endpoint and sender address.\n * @dev _srcEid The source endpoint ID.\n * @dev _sender The sender address.\n * @return nonce The next nonce.\n *\n * @dev The path nonce starts from 1. If 0 is returned it means that there is NO nonce ordered enforcement.\n * @dev Is required by the off-chain executor to determine the OApp expects msg execution is ordered.\n * @dev This is also enforced by the OApp.\n * @dev By default this is NOT enabled. ie. nextNonce is hardcoded to return 0.\n */\n function nextNonce(uint32 /*_srcEid*/, bytes32 /*_sender*/) public view virtual returns (uint64 nonce) {\n return 0;\n }\n\n /**\n * @dev Entry point for receiving messages or packets from the endpoint.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The payload of the received message.\n * @param _executor The address of the executor for the received message.\n * @param _extraData Additional arbitrary data provided by the corresponding executor.\n *\n * @dev Entry point for receiving msg/packet from the LayerZero endpoint.\n */\n function lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) public payable virtual {\n // Ensures that only the endpoint can attempt to lzReceive() messages to this OApp.\n if (address(endpoint) != msg.sender) revert OnlyEndpoint(msg.sender);\n\n // Ensure that the sender matches the expected peer for the source endpoint.\n if (_getPeerOrRevert(_origin.srcEid) != _origin.sender) revert OnlyPeer(_origin.srcEid, _origin.sender);\n\n // Call the internal OApp implementation of lzReceive.\n _lzReceive(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Internal function to implement lzReceive logic without needing to copy the basic parameter validation.\n */\n function _lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual;\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { SafeERC20, IERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { MessagingParams, MessagingFee, MessagingReceipt } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\nimport { OAppCore } from \"./OAppCore.sol\";\n\n/**\n * @title OAppSender\n * @dev Abstract contract implementing the OAppSender functionality for sending messages to a LayerZero endpoint.\n */\nabstract contract OAppSender is OAppCore {\n using SafeERC20 for IERC20;\n\n // Custom error messages\n error NotEnoughNative(uint256 msgValue);\n error LzTokenUnavailable();\n\n // @dev The version of the OAppSender implementation.\n // @dev Version is bumped when changes are made to this contract.\n uint64 internal constant SENDER_VERSION = 1;\n\n /**\n * @notice Retrieves the OApp version information.\n * @return senderVersion The version of the OAppSender.sol contract.\n * @return receiverVersion The version of the OAppReceiver.sol contract.\n *\n * @dev Providing 0 as the default for OAppReceiver version. Indicates that the OAppReceiver is not implemented.\n * ie. this is a SEND only OApp.\n * @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions\n */\n function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {\n return (SENDER_VERSION, 0);\n }\n\n /**\n * @dev Internal function to interact with the LayerZero EndpointV2.quote() for fee calculation.\n * @param _dstEid The destination endpoint ID.\n * @param _message The message payload.\n * @param _options Additional options for the message.\n * @param _payInLzToken Flag indicating whether to pay the fee in LZ tokens.\n * @return fee The calculated MessagingFee for the message.\n * - nativeFee: The native fee for the message.\n * - lzTokenFee: The LZ token fee for the message.\n */\n function _quote(\n uint32 _dstEid,\n bytes memory _message,\n bytes memory _options,\n bool _payInLzToken\n ) internal view virtual returns (MessagingFee memory fee) {\n return\n endpoint.quote(\n MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _payInLzToken),\n address(this)\n );\n }\n\n /**\n * @dev Internal function to interact with the LayerZero EndpointV2.send() for sending a message.\n * @param _dstEid The destination endpoint ID.\n * @param _message The message payload.\n * @param _options Additional options for the message.\n * @param _fee The calculated LayerZero fee for the message.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess fee values sent to the endpoint.\n * @return receipt The receipt for the sent message.\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function _lzSend(\n uint32 _dstEid,\n bytes memory _message,\n bytes memory _options,\n MessagingFee memory _fee,\n address _refundAddress\n ) internal virtual returns (MessagingReceipt memory receipt) {\n // @dev Push corresponding fees to the endpoint, any excess is sent back to the _refundAddress from the endpoint.\n uint256 messageValue = _payNative(_fee.nativeFee);\n if (_fee.lzTokenFee > 0) _payLzToken(_fee.lzTokenFee);\n\n return\n // solhint-disable-next-line check-send-result\n endpoint.send{ value: messageValue }(\n MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _fee.lzTokenFee > 0),\n _refundAddress\n );\n }\n\n /**\n * @dev Internal function to pay the native fee associated with the message.\n * @param _nativeFee The native fee to be paid.\n * @return nativeFee The amount of native currency paid.\n *\n * @dev If the OApp needs to initiate MULTIPLE LayerZero messages in a single transaction,\n * this will need to be overridden because msg.value would contain multiple lzFees.\n * @dev Should be overridden in the event the LayerZero endpoint requires a different native currency.\n * @dev Some EVMs use an ERC20 as a method for paying transactions/gasFees.\n * @dev The endpoint is EITHER/OR, ie. it will NOT support both types of native payment at a time.\n */\n function _payNative(uint256 _nativeFee) internal virtual returns (uint256 nativeFee) {\n if (msg.value != _nativeFee) revert NotEnoughNative(msg.value);\n return _nativeFee;\n }\n\n /**\n * @dev Internal function to pay the LZ token fee associated with the message.\n * @param _lzTokenFee The LZ token fee to be paid.\n *\n * @dev If the caller is trying to pay in the specified lzToken, then the lzTokenFee is passed to the endpoint.\n * @dev Any excess sent, is passed back to the specified _refundAddress in the _lzSend().\n */\n function _payLzToken(uint256 _lzTokenFee) internal virtual {\n // @dev Cannot cache the token because it is not immutable in the endpoint.\n address lzToken = endpoint.lzToken();\n if (lzToken == address(0)) revert LzTokenUnavailable();\n\n // Pay LZ token fee by sending tokens to the endpoint.\n IERC20(lzToken).safeTransferFrom(msg.sender, address(endpoint), _lzTokenFee);\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/interfaces/IOAppPreCrimeSimulator.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\n// @dev Import the Origin so it's exposed to OAppPreCrimeSimulator implementers.\n// solhint-disable-next-line no-unused-import\nimport { InboundPacket, Origin } from \"../libs/Packet.sol\";\n\n/**\n * @title IOAppPreCrimeSimulator Interface\n * @dev Interface for the preCrime simulation functionality in an OApp.\n */\ninterface IOAppPreCrimeSimulator {\n // @dev simulation result used in PreCrime implementation\n error SimulationResult(bytes result);\n error OnlySelf();\n\n /**\n * @dev Emitted when the preCrime contract address is set.\n * @param preCrimeAddress The address of the preCrime contract.\n */\n event PreCrimeSet(address preCrimeAddress);\n\n /**\n * @dev Retrieves the address of the preCrime contract implementation.\n * @return The address of the preCrime contract.\n */\n function preCrime() external view returns (address);\n\n /**\n * @dev Retrieves the address of the OApp contract.\n * @return The address of the OApp contract.\n */\n function oApp() external view returns (address);\n\n /**\n * @dev Sets the preCrime contract address.\n * @param _preCrime The address of the preCrime contract.\n */\n function setPreCrime(address _preCrime) external;\n\n /**\n * @dev Mocks receiving a packet, then reverts with a series of data to infer the state/result.\n * @param _packets An array of LayerZero InboundPacket objects representing received packets.\n */\n function lzReceiveAndRevert(InboundPacket[] calldata _packets) external payable;\n\n /**\n * @dev checks if the specified peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint Id to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n */\n function isPeer(uint32 _eid, bytes32 _peer) external view returns (bool);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/interfaces/IPreCrime.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\nstruct PreCrimePeer {\n uint32 eid;\n bytes32 preCrime;\n bytes32 oApp;\n}\n\n// TODO not done yet\ninterface IPreCrime {\n error OnlyOffChain();\n\n // for simulate()\n error PacketOversize(uint256 max, uint256 actual);\n error PacketUnsorted();\n error SimulationFailed(bytes reason);\n\n // for preCrime()\n error SimulationResultNotFound(uint32 eid);\n error InvalidSimulationResult(uint32 eid, bytes reason);\n error CrimeFound(bytes crime);\n\n function getConfig(bytes[] calldata _packets, uint256[] calldata _packetMsgValues) external returns (bytes memory);\n\n function simulate(\n bytes[] calldata _packets,\n uint256[] calldata _packetMsgValues\n ) external payable returns (bytes memory);\n\n function buildSimulationResult() external view returns (bytes memory);\n\n function preCrime(\n bytes[] calldata _packets,\n uint256[] calldata _packetMsgValues,\n bytes[] calldata _simulations\n ) external;\n\n function version() external view returns (uint64 major, uint8 minor);\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/libs/Packet.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Origin } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol\";\nimport { PacketV1Codec } from \"@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol\";\n\n/**\n * @title InboundPacket\n * @dev Structure representing an inbound packet received by the contract.\n */\nstruct InboundPacket {\n Origin origin; // Origin information of the packet.\n uint32 dstEid; // Destination endpointId of the packet.\n address receiver; // Receiver address for the packet.\n bytes32 guid; // Unique identifier of the packet.\n uint256 value; // msg.value of the packet.\n address executor; // Executor address for the packet.\n bytes message; // Message payload of the packet.\n bytes extraData; // Additional arbitrary data for the packet.\n}\n\n/**\n * @title PacketDecoder\n * @dev Library for decoding LayerZero packets.\n */\nlibrary PacketDecoder {\n using PacketV1Codec for bytes;\n\n /**\n * @dev Decode an inbound packet from the given packet data.\n * @param _packet The packet data to decode.\n * @return packet An InboundPacket struct representing the decoded packet.\n */\n function decode(bytes calldata _packet) internal pure returns (InboundPacket memory packet) {\n packet.origin = Origin(_packet.srcEid(), _packet.sender(), _packet.nonce());\n packet.dstEid = _packet.dstEid();\n packet.receiver = _packet.receiverB20();\n packet.guid = _packet.guid();\n packet.message = _packet.message();\n }\n\n /**\n * @dev Decode multiple inbound packets from the given packet data and associated message values.\n * @param _packets An array of packet data to decode.\n * @param _packetMsgValues An array of associated message values for each packet.\n * @return packets An array of InboundPacket structs representing the decoded packets.\n */\n function decode(\n bytes[] calldata _packets,\n uint256[] memory _packetMsgValues\n ) internal pure returns (InboundPacket[] memory packets) {\n packets = new InboundPacket[](_packets.length);\n for (uint256 i = 0; i < _packets.length; i++) {\n bytes calldata packet = _packets[i];\n packets[i] = PacketDecoder.decode(packet);\n // @dev Allows the verifier to specify the msg.value that gets passed in lzReceive.\n packets[i].value = _packetMsgValues[i];\n }\n }\n}\n" + }, + "@layerzerolabs/oapp-evm/contracts/precrime/OAppPreCrimeSimulator.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { IPreCrime } from \"./interfaces/IPreCrime.sol\";\nimport { IOAppPreCrimeSimulator, InboundPacket, Origin } from \"./interfaces/IOAppPreCrimeSimulator.sol\";\n\n/**\n * @title OAppPreCrimeSimulator\n * @dev Abstract contract serving as the base for preCrime simulation functionality in an OApp.\n */\nabstract contract OAppPreCrimeSimulator is IOAppPreCrimeSimulator, Ownable {\n // The address of the preCrime implementation.\n address public preCrime;\n\n /**\n * @dev Retrieves the address of the OApp contract.\n * @return The address of the OApp contract.\n *\n * @dev The simulator contract is the base contract for the OApp by default.\n * @dev If the simulator is a separate contract, override this function.\n */\n function oApp() external view virtual returns (address) {\n return address(this);\n }\n\n /**\n * @dev Sets the preCrime contract address.\n * @param _preCrime The address of the preCrime contract.\n */\n function setPreCrime(address _preCrime) public virtual onlyOwner {\n preCrime = _preCrime;\n emit PreCrimeSet(_preCrime);\n }\n\n /**\n * @dev Interface for pre-crime simulations. Always reverts at the end with the simulation results.\n * @param _packets An array of InboundPacket objects representing received packets to be delivered.\n *\n * @dev WARNING: MUST revert at the end with the simulation results.\n * @dev Gives the preCrime implementation the ability to mock sending packets to the lzReceive function,\n * WITHOUT actually executing them.\n */\n function lzReceiveAndRevert(InboundPacket[] calldata _packets) public payable virtual {\n for (uint256 i = 0; i < _packets.length; i++) {\n InboundPacket calldata packet = _packets[i];\n\n // Ignore packets that are not from trusted peers.\n if (!isPeer(packet.origin.srcEid, packet.origin.sender)) continue;\n\n // @dev Because a verifier is calling this function, it doesnt have access to executor params:\n // - address _executor\n // - bytes calldata _extraData\n // preCrime will NOT work for OApps that rely on these two parameters inside of their _lzReceive().\n // They are instead stubbed to default values, address(0) and bytes(\"\")\n // @dev Calling this.lzReceiveSimulate removes ability for assembly return 0 callstack exit,\n // which would cause the revert to be ignored.\n this.lzReceiveSimulate{ value: packet.value }(\n packet.origin,\n packet.guid,\n packet.message,\n packet.executor,\n packet.extraData\n );\n }\n\n // @dev Revert with the simulation results. msg.sender must implement IPreCrime.buildSimulationResult().\n revert SimulationResult(IPreCrime(msg.sender).buildSimulationResult());\n }\n\n /**\n * @dev Is effectively an internal function because msg.sender must be address(this).\n * Allows resetting the call stack for 'internal' calls.\n * @param _origin The origin information containing the source endpoint and sender address.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address on the src chain.\n * - nonce: The nonce of the message.\n * @param _guid The unique identifier of the packet.\n * @param _message The message payload of the packet.\n * @param _executor The executor address for the packet.\n * @param _extraData Additional data for the packet.\n */\n function lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) external payable virtual {\n // @dev Ensure ONLY can be called 'internally'.\n if (msg.sender != address(this)) revert OnlySelf();\n _lzReceiveSimulate(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Internal function to handle the OAppPreCrimeSimulator simulated receive.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The GUID of the LayerZero message.\n * @param _message The LayerZero message.\n * @param _executor The address of the off-chain executor.\n * @param _extraData Arbitrary data passed by the msg executor.\n *\n * @dev Enables the preCrime simulator to mock sending lzReceive() messages,\n * routes the msg down from the OAppPreCrimeSimulator, and back up to the OAppReceiver.\n */\n function _lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual;\n\n /**\n * @dev checks if the specified peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint Id to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n */\n function isPeer(uint32 _eid, bytes32 _peer) public view virtual returns (bool);\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/interfaces/IMintableBurnable.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\n/// @title Interface for mintable and burnable tokens\ninterface IMintableBurnable {\n \n /**\n * @notice Burns tokens from a specified account\n * @param _from Address from which tokens will be burned\n * @param _amount Amount of tokens to be burned\n * @return success Indicates whether the operation was successful\n */\n function burn(address _from, uint256 _amount) external returns (bool success);\n\n /**\n * @notice Mints tokens to a specified account\n * @param _to Address to which tokens will be minted\n * @param _amount Amount of tokens to be minted\n * @return success Indicates whether the operation was successful\n */\n function mint(address _to, uint256 _amount) external returns (bool success);\n}" + }, + "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { MessagingReceipt, MessagingFee } from \"@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol\";\n\n/**\n * @dev Struct representing token parameters for the OFT send() operation.\n */\nstruct SendParam {\n uint32 dstEid; // Destination endpoint ID.\n bytes32 to; // Recipient address.\n uint256 amountLD; // Amount to send in local decimals.\n uint256 minAmountLD; // Minimum amount to send in local decimals.\n bytes extraOptions; // Additional options supplied by the caller to be used in the LayerZero message.\n bytes composeMsg; // The composed message for the send() operation.\n bytes oftCmd; // The OFT command to be executed, unused in default OFT implementations.\n}\n\n/**\n * @dev Struct representing OFT limit information.\n * @dev These amounts can change dynamically and are up the specific oft implementation.\n */\nstruct OFTLimit {\n uint256 minAmountLD; // Minimum amount in local decimals that can be sent to the recipient.\n uint256 maxAmountLD; // Maximum amount in local decimals that can be sent to the recipient.\n}\n\n/**\n * @dev Struct representing OFT receipt information.\n */\nstruct OFTReceipt {\n uint256 amountSentLD; // Amount of tokens ACTUALLY debited from the sender in local decimals.\n // @dev In non-default implementations, the amountReceivedLD COULD differ from this value.\n uint256 amountReceivedLD; // Amount of tokens to be received on the remote side.\n}\n\n/**\n * @dev Struct representing OFT fee details.\n * @dev Future proof mechanism to provide a standardized way to communicate fees to things like a UI.\n */\nstruct OFTFeeDetail {\n int256 feeAmountLD; // Amount of the fee in local decimals.\n string description; // Description of the fee.\n}\n\n/**\n * @title IOFT\n * @dev Interface for the OftChain (OFT) token.\n * @dev Does not inherit ERC20 to accommodate usage by OFTAdapter as well.\n * @dev This specific interface ID is '0x02e49c2c'.\n */\ninterface IOFT {\n // Custom error messages\n error InvalidLocalDecimals();\n error SlippageExceeded(uint256 amountLD, uint256 minAmountLD);\n\n // Events\n event OFTSent(\n bytes32 indexed guid, // GUID of the OFT message.\n uint32 dstEid, // Destination Endpoint ID.\n address indexed fromAddress, // Address of the sender on the src chain.\n uint256 amountSentLD, // Amount of tokens sent in local decimals.\n uint256 amountReceivedLD // Amount of tokens received in local decimals.\n );\n event OFTReceived(\n bytes32 indexed guid, // GUID of the OFT message.\n uint32 srcEid, // Source Endpoint ID.\n address indexed toAddress, // Address of the recipient on the dst chain.\n uint256 amountReceivedLD // Amount of tokens received in local decimals.\n );\n\n /**\n * @notice Retrieves interfaceID and the version of the OFT.\n * @return interfaceId The interface ID.\n * @return version The version.\n *\n * @dev interfaceId: This specific interface ID is '0x02e49c2c'.\n * @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.\n * @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.\n * ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)\n */\n function oftVersion() external view returns (bytes4 interfaceId, uint64 version);\n\n /**\n * @notice Retrieves the address of the token associated with the OFT.\n * @return token The address of the ERC20 token implementation.\n */\n function token() external view returns (address);\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the 'token()' to send.\n * @return requiresApproval Needs approval of the underlying token implementation.\n *\n * @dev Allows things like wallet implementers to determine integration requirements,\n * without understanding the underlying token implementation.\n */\n function approvalRequired() external view returns (bool);\n\n /**\n * @notice Retrieves the shared decimals of the OFT.\n * @return sharedDecimals The shared decimals of the OFT.\n */\n function sharedDecimals() external view returns (uint8);\n\n /**\n * @notice Provides the fee breakdown and settings data for an OFT. Unused in the default implementation.\n * @param _sendParam The parameters for the send operation.\n * @return limit The OFT limit information.\n * @return oftFeeDetails The details of OFT fees.\n * @return receipt The OFT receipt information.\n */\n function quoteOFT(\n SendParam calldata _sendParam\n ) external view returns (OFTLimit memory, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory);\n\n /**\n * @notice Provides a quote for the send() operation.\n * @param _sendParam The parameters for the send() operation.\n * @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.\n * @return fee The calculated LayerZero messaging fee from the send() operation.\n *\n * @dev MessagingFee: LayerZero msg fee\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n */\n function quoteSend(SendParam calldata _sendParam, bool _payInLzToken) external view returns (MessagingFee memory);\n\n /**\n * @notice Executes the send() operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The fee information supplied by the caller.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds from fees etc. on the src.\n * @return receipt The LayerZero messaging receipt from the send() operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) external payable returns (MessagingReceipt memory, OFTReceipt memory);\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/libs/OFTComposeMsgCodec.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nlibrary OFTComposeMsgCodec {\n // Offset constants for decoding composed messages\n uint8 private constant NONCE_OFFSET = 8;\n uint8 private constant SRC_EID_OFFSET = 12;\n uint8 private constant AMOUNT_LD_OFFSET = 44;\n uint8 private constant COMPOSE_FROM_OFFSET = 76;\n\n /**\n * @dev Encodes a OFT composed message.\n * @param _nonce The nonce value.\n * @param _srcEid The source endpoint ID.\n * @param _amountLD The amount in local decimals.\n * @param _composeMsg The composed message.\n * @return _msg The encoded Composed message.\n */\n function encode(\n uint64 _nonce,\n uint32 _srcEid,\n uint256 _amountLD,\n bytes memory _composeMsg // 0x[composeFrom][composeMsg]\n ) internal pure returns (bytes memory _msg) {\n _msg = abi.encodePacked(_nonce, _srcEid, _amountLD, _composeMsg);\n }\n\n /**\n * @dev Retrieves the nonce for the composed message.\n * @param _msg The message.\n * @return The nonce value.\n */\n function nonce(bytes calldata _msg) internal pure returns (uint64) {\n return uint64(bytes8(_msg[:NONCE_OFFSET]));\n }\n\n /**\n * @dev Retrieves the source endpoint ID for the composed message.\n * @param _msg The message.\n * @return The source endpoint ID.\n */\n function srcEid(bytes calldata _msg) internal pure returns (uint32) {\n return uint32(bytes4(_msg[NONCE_OFFSET:SRC_EID_OFFSET]));\n }\n\n /**\n * @dev Retrieves the amount in local decimals from the composed message.\n * @param _msg The message.\n * @return The amount in local decimals.\n */\n function amountLD(bytes calldata _msg) internal pure returns (uint256) {\n return uint256(bytes32(_msg[SRC_EID_OFFSET:AMOUNT_LD_OFFSET]));\n }\n\n /**\n * @dev Retrieves the composeFrom value from the composed message.\n * @param _msg The message.\n * @return The composeFrom value.\n */\n function composeFrom(bytes calldata _msg) internal pure returns (bytes32) {\n return bytes32(_msg[AMOUNT_LD_OFFSET:COMPOSE_FROM_OFFSET]);\n }\n\n /**\n * @dev Retrieves the composed message.\n * @param _msg The message.\n * @return The composed message.\n */\n function composeMsg(bytes calldata _msg) internal pure returns (bytes memory) {\n return _msg[COMPOSE_FROM_OFFSET:];\n }\n\n /**\n * @dev Converts an address to bytes32.\n * @param _addr The address to convert.\n * @return The bytes32 representation of the address.\n */\n function addressToBytes32(address _addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(_addr)));\n }\n\n /**\n * @dev Converts bytes32 to an address.\n * @param _b The bytes32 value to convert.\n * @return The address representation of bytes32.\n */\n function bytes32ToAddress(bytes32 _b) internal pure returns (address) {\n return address(uint160(uint256(_b)));\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/libs/OFTMsgCodec.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nlibrary OFTMsgCodec {\n // Offset constants for encoding and decoding OFT messages\n uint8 private constant SEND_TO_OFFSET = 32;\n uint8 private constant SEND_AMOUNT_SD_OFFSET = 40;\n\n /**\n * @dev Encodes an OFT LayerZero message.\n * @param _sendTo The recipient address.\n * @param _amountShared The amount in shared decimals.\n * @param _composeMsg The composed message.\n * @return _msg The encoded message.\n * @return hasCompose A boolean indicating whether the message has a composed payload.\n */\n function encode(\n bytes32 _sendTo,\n uint64 _amountShared,\n bytes memory _composeMsg\n ) internal view returns (bytes memory _msg, bool hasCompose) {\n hasCompose = _composeMsg.length > 0;\n // @dev Remote chains will want to know the composed function caller ie. msg.sender on the src.\n _msg = hasCompose\n ? abi.encodePacked(_sendTo, _amountShared, addressToBytes32(msg.sender), _composeMsg)\n : abi.encodePacked(_sendTo, _amountShared);\n }\n\n /**\n * @dev Checks if the OFT message is composed.\n * @param _msg The OFT message.\n * @return A boolean indicating whether the message is composed.\n */\n function isComposed(bytes calldata _msg) internal pure returns (bool) {\n return _msg.length > SEND_AMOUNT_SD_OFFSET;\n }\n\n /**\n * @dev Retrieves the recipient address from the OFT message.\n * @param _msg The OFT message.\n * @return The recipient address.\n */\n function sendTo(bytes calldata _msg) internal pure returns (bytes32) {\n return bytes32(_msg[:SEND_TO_OFFSET]);\n }\n\n /**\n * @dev Retrieves the amount in shared decimals from the OFT message.\n * @param _msg The OFT message.\n * @return The amount in shared decimals.\n */\n function amountSD(bytes calldata _msg) internal pure returns (uint64) {\n return uint64(bytes8(_msg[SEND_TO_OFFSET:SEND_AMOUNT_SD_OFFSET]));\n }\n\n /**\n * @dev Retrieves the composed message from the OFT message.\n * @param _msg The OFT message.\n * @return The composed message.\n */\n function composeMsg(bytes calldata _msg) internal pure returns (bytes memory) {\n return _msg[SEND_AMOUNT_SD_OFFSET:];\n }\n\n /**\n * @dev Converts an address to bytes32.\n * @param _addr The address to convert.\n * @return The bytes32 representation of the address.\n */\n function addressToBytes32(address _addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(_addr)));\n }\n\n /**\n * @dev Converts bytes32 to an address.\n * @param _b The bytes32 value to convert.\n * @return The address representation of bytes32.\n */\n function bytes32ToAddress(bytes32 _b) internal pure returns (address) {\n return address(uint160(uint256(_b)));\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/MintBurnOFTAdapter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\n// External imports\nimport { IERC20, IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\n// Local imports\nimport { IMintableBurnable } from \"./interfaces/IMintableBurnable.sol\";\nimport { OFTCore } from \"./OFTCore.sol\";\n\n/**\n * @title MintBurnOFTAdapter\n * @notice A variant of the standard OFT Adapter that uses an existing ERC20's mint and burn mechanisms for cross-chain transfers.\n *\n * @dev Inherits from OFTCore and provides implementations for _debit and _credit functions using a mintable and burnable token.\n */\nabstract contract MintBurnOFTAdapter is OFTCore {\n /// @dev The underlying ERC20 token.\n IERC20 internal immutable innerToken;\n\n /// @notice The contract responsible for minting and burning tokens.\n IMintableBurnable public immutable minterBurner;\n\n /**\n * @notice Initializes the MintBurnOFTAdapter contract.\n *\n * @param _token The address of the underlying ERC20 token.\n * @param _minterBurner The contract responsible for minting and burning tokens.\n * @param _lzEndpoint The LayerZero endpoint address.\n * @param _delegate The address of the delegate.\n *\n * @dev Calls the OFTCore constructor with the token's decimals, the endpoint, and the delegate.\n */\n constructor(\n address _token,\n IMintableBurnable _minterBurner,\n address _lzEndpoint,\n address _delegate\n ) OFTCore(IERC20Metadata(_token).decimals(), _lzEndpoint, _delegate) {\n innerToken = IERC20(_token);\n minterBurner = _minterBurner;\n }\n\n /**\n * @notice Retrieves the address of the underlying ERC20 token.\n *\n * @return The address of the adapted ERC20 token.\n *\n * @dev In the case of MintBurnOFTAdapter, address(this) and erc20 are NOT the same contract.\n */\n function token() public view returns (address) {\n return address(innerToken);\n }\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the underlying token to send.\n *\n * @return requiresApproval True if approval is required, false otherwise.\n *\n * @dev In this MintBurnOFTAdapter, approval is NOT required because it uses mint and burn privileges.\n */\n function approvalRequired() external pure virtual returns (bool) {\n return false;\n }\n\n /**\n * @notice Burns tokens from the sender's balance to prepare for sending.\n *\n * @param _from The address to debit the tokens from.\n * @param _amountLD The amount of tokens to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination chain ID.\n *\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, i.e., 1 token in, 1 token out.\n * If the 'innerToken' applies something like a transfer fee, the default will NOT work.\n * A pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual override returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n (amountSentLD, amountReceivedLD) = _debitView(_amountLD, _minAmountLD, _dstEid);\n // Burns tokens from the caller.\n minterBurner.burn(_from, amountSentLD);\n }\n\n /**\n * @notice Mints tokens to the specified address upon receiving them.\n *\n * @param _to The address to credit the tokens to.\n * @param _amountLD The amount of tokens to credit in local decimals.\n *\n * @return amountReceivedLD The amount of tokens actually received in local decimals.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, i.e., 1 token in, 1 token out.\n * If the 'innerToken' applies something like a transfer fee, the default will NOT work.\n * A pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /* _srcEid */\n ) internal virtual override returns (uint256 amountReceivedLD) {\n if (_to == address(0x0)) _to = address(0xdead); // _mint(...) does not support address(0x0)\n // Mints the tokens and transfers to the recipient.\n minterBurner.mint(_to, _amountLD);\n // In the case of NON-default OFTAdapter, the amountLD MIGHT not be equal to amountReceivedLD.\n return _amountLD;\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/OFTAdapter.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IERC20Metadata, IERC20 } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IOFT, OFTCore } from \"./OFTCore.sol\";\n\n/**\n * @title OFTAdapter Contract\n * @dev OFTAdapter is a contract that adapts an ERC-20 token to the OFT functionality.\n *\n * @dev For existing ERC20 tokens, this can be used to convert the token to crosschain compatibility.\n * @dev WARNING: ONLY 1 of these should exist for a given global mesh,\n * unless you make a NON-default implementation of OFT and needs to be done very carefully.\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountSentLD/amountReceivedLD.\n */\nabstract contract OFTAdapter is OFTCore {\n using SafeERC20 for IERC20;\n\n IERC20 internal immutable innerToken;\n\n /**\n * @dev Constructor for the OFTAdapter contract.\n * @param _token The address of the ERC-20 token to be adapted.\n * @param _lzEndpoint The LayerZero endpoint address.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(\n address _token,\n address _lzEndpoint,\n address _delegate\n ) OFTCore(IERC20Metadata(_token).decimals(), _lzEndpoint, _delegate) {\n innerToken = IERC20(_token);\n }\n\n /**\n * @dev Retrieves the address of the underlying ERC20 implementation.\n * @return The address of the adapted ERC-20 token.\n *\n * @dev In the case of OFTAdapter, address(this) and erc20 are NOT the same contract.\n */\n function token() public view returns (address) {\n return address(innerToken);\n }\n\n /**\n * @notice Indicates whether the OFT contract requires approval of the 'token()' to send.\n * @return requiresApproval Needs approval of the underlying token implementation.\n *\n * @dev In the case of default OFTAdapter, approval is required.\n * @dev In non-default OFTAdapter contracts with something like mint and burn privileges, it would NOT need approval.\n */\n function approvalRequired() external pure virtual returns (bool) {\n return true;\n }\n\n /**\n * @dev Locks tokens from the sender's specified balance in this contract.\n * @param _from The address to debit from.\n * @param _amountLD The amount of tokens to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination chain ID.\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev msg.sender will need to approve this _amountLD of tokens to be locked inside of the contract.\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual override returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n (amountSentLD, amountReceivedLD) = _debitView(_amountLD, _minAmountLD, _dstEid);\n // @dev Lock tokens by moving them into this contract from the caller.\n innerToken.safeTransferFrom(_from, address(this), amountSentLD);\n }\n\n /**\n * @dev Credits tokens to the specified address.\n * @param _to The address to credit the tokens to.\n * @param _amountLD The amount of tokens to credit in local decimals.\n * @dev _srcEid The source chain ID.\n * @return amountReceivedLD The amount of tokens ACTUALLY received in local decimals.\n *\n * @dev WARNING: The default OFTAdapter implementation assumes LOSSLESS transfers, ie. 1 token in, 1 token out.\n * IF the 'innerToken' applies something like a transfer fee, the default will NOT work...\n * a pre/post balance check will need to be done to calculate the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /*_srcEid*/\n ) internal virtual override returns (uint256 amountReceivedLD) {\n // @dev Unlock the tokens and transfer to the recipient.\n innerToken.safeTransfer(_to, _amountLD);\n // @dev In the case of NON-default OFTAdapter, the amountLD MIGHT not be == amountReceivedLD.\n return _amountLD;\n }\n}\n" + }, + "@layerzerolabs/oft-evm/contracts/OFTCore.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.20;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { OApp, Origin } from \"@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol\";\nimport { OAppOptionsType3 } from \"@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol\";\nimport { IOAppMsgInspector } from \"@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppMsgInspector.sol\";\n\nimport { OAppPreCrimeSimulator } from \"@layerzerolabs/oapp-evm/contracts/precrime/OAppPreCrimeSimulator.sol\";\n\nimport { IOFT, SendParam, OFTLimit, OFTReceipt, OFTFeeDetail, MessagingReceipt, MessagingFee } from \"./interfaces/IOFT.sol\";\nimport { OFTMsgCodec } from \"./libs/OFTMsgCodec.sol\";\nimport { OFTComposeMsgCodec } from \"./libs/OFTComposeMsgCodec.sol\";\n\n/**\n * @title OFTCore\n * @dev Abstract contract for the OftChain (OFT) token.\n */\nabstract contract OFTCore is IOFT, OApp, OAppPreCrimeSimulator, OAppOptionsType3 {\n using OFTMsgCodec for bytes;\n using OFTMsgCodec for bytes32;\n\n // @notice Provides a conversion rate when swapping between denominations of SD and LD\n // - shareDecimals == SD == shared Decimals\n // - localDecimals == LD == local decimals\n // @dev Considers that tokens have different decimal amounts on various chains.\n // @dev eg.\n // For a token\n // - locally with 4 decimals --> 1.2345 => uint(12345)\n // - remotely with 2 decimals --> 1.23 => uint(123)\n // - The conversion rate would be 10 ** (4 - 2) = 100\n // @dev If you want to send 1.2345 -> (uint 12345), you CANNOT represent that value on the remote,\n // you can only display 1.23 -> uint(123).\n // @dev To preserve the dust that would otherwise be lost on that conversion,\n // we need to unify a denomination that can be represented on ALL chains inside of the OFT mesh\n uint256 public immutable decimalConversionRate;\n\n // @notice Msg types that are used to identify the various OFT operations.\n // @dev This can be extended in child contracts for non-default oft operations\n // @dev These values are used in things like combineOptions() in OAppOptionsType3.sol.\n uint16 public constant SEND = 1;\n uint16 public constant SEND_AND_CALL = 2;\n\n // Address of an optional contract to inspect both 'message' and 'options'\n address public msgInspector;\n event MsgInspectorSet(address inspector);\n\n /**\n * @dev Constructor.\n * @param _localDecimals The decimals of the token on the local chain (this chain).\n * @param _endpoint The address of the LayerZero endpoint.\n * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.\n */\n constructor(uint8 _localDecimals, address _endpoint, address _delegate) OApp(_endpoint, _delegate) {\n if (_localDecimals < sharedDecimals()) revert InvalidLocalDecimals();\n decimalConversionRate = 10 ** (_localDecimals - sharedDecimals());\n }\n\n /**\n * @notice Retrieves interfaceID and the version of the OFT.\n * @return interfaceId The interface ID.\n * @return version The version.\n *\n * @dev interfaceId: This specific interface ID is '0x02e49c2c'.\n * @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.\n * @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.\n * ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)\n */\n function oftVersion() external pure virtual returns (bytes4 interfaceId, uint64 version) {\n return (type(IOFT).interfaceId, 1);\n }\n\n /**\n * @dev Retrieves the shared decimals of the OFT.\n * @return The shared decimals of the OFT.\n *\n * @dev Sets an implicit cap on the amount of tokens, over uint64.max() will need some sort of outbound cap / totalSupply cap\n * Lowest common decimal denominator between chains.\n * Defaults to 6 decimal places to provide up to 18,446,744,073,709.551615 units (max uint64).\n * For tokens exceeding this totalSupply(), they will need to override the sharedDecimals function with something smaller.\n * ie. 4 sharedDecimals would be 1,844,674,407,370,955.1615\n */\n function sharedDecimals() public view virtual returns (uint8) {\n return 6;\n }\n\n /**\n * @dev Sets the message inspector address for the OFT.\n * @param _msgInspector The address of the message inspector.\n *\n * @dev This is an optional contract that can be used to inspect both 'message' and 'options'.\n * @dev Set it to address(0) to disable it, or set it to a contract address to enable it.\n */\n function setMsgInspector(address _msgInspector) public virtual onlyOwner {\n msgInspector = _msgInspector;\n emit MsgInspectorSet(_msgInspector);\n }\n\n /**\n * @notice Provides the fee breakdown and settings data for an OFT. Unused in the default implementation.\n * @param _sendParam The parameters for the send operation.\n * @return oftLimit The OFT limit information.\n * @return oftFeeDetails The details of OFT fees.\n * @return oftReceipt The OFT receipt information.\n */\n function quoteOFT(\n SendParam calldata _sendParam\n )\n external\n view\n virtual\n returns (OFTLimit memory oftLimit, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory oftReceipt)\n {\n uint256 minAmountLD = 0; // Unused in the default implementation.\n uint256 maxAmountLD = IERC20(this.token()).totalSupply(); // Unused in the default implementation.\n oftLimit = OFTLimit(minAmountLD, maxAmountLD);\n\n // Unused in the default implementation; reserved for future complex fee details.\n oftFeeDetails = new OFTFeeDetail[](0);\n\n // @dev This is the same as the send() operation, but without the actual send.\n // - amountSentLD is the amount in local decimals that would be sent from the sender.\n // - amountReceivedLD is the amount in local decimals that will be credited to the recipient on the remote OFT instance.\n // @dev The amountSentLD MIGHT not equal the amount the user actually receives. HOWEVER, the default does.\n (uint256 amountSentLD, uint256 amountReceivedLD) = _debitView(\n _sendParam.amountLD,\n _sendParam.minAmountLD,\n _sendParam.dstEid\n );\n oftReceipt = OFTReceipt(amountSentLD, amountReceivedLD);\n }\n\n /**\n * @notice Provides a quote for the send() operation.\n * @param _sendParam The parameters for the send() operation.\n * @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.\n * @return msgFee The calculated LayerZero messaging fee from the send() operation.\n *\n * @dev MessagingFee: LayerZero msg fee\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n */\n function quoteSend(\n SendParam calldata _sendParam,\n bool _payInLzToken\n ) external view virtual returns (MessagingFee memory msgFee) {\n // @dev mock the amount to receive, this is the same operation used in the send().\n // The quote is as similar as possible to the actual send() operation.\n (, uint256 amountReceivedLD) = _debitView(_sendParam.amountLD, _sendParam.minAmountLD, _sendParam.dstEid);\n\n // @dev Builds the options and OFT message to quote in the endpoint.\n (bytes memory message, bytes memory options) = _buildMsgAndOptions(_sendParam, amountReceivedLD);\n\n // @dev Calculates the LayerZero fee for the send() operation.\n return _quote(_sendParam.dstEid, message, options, _payInLzToken);\n }\n\n /**\n * @dev Executes the send operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The calculated fee for the send() operation.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds.\n * @return msgReceipt The receipt for the send operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) external payable virtual returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) {\n return _send(_sendParam, _fee, _refundAddress);\n }\n\n /**\n * @dev Internal function to execute the send operation.\n * @param _sendParam The parameters for the send operation.\n * @param _fee The calculated fee for the send() operation.\n * - nativeFee: The native fee.\n * - lzTokenFee: The lzToken fee.\n * @param _refundAddress The address to receive any excess funds.\n * @return msgReceipt The receipt for the send operation.\n * @return oftReceipt The OFT receipt information.\n *\n * @dev MessagingReceipt: LayerZero msg receipt\n * - guid: The unique identifier for the sent message.\n * - nonce: The nonce of the sent message.\n * - fee: The LayerZero fee incurred for the message.\n */\n function _send(\n SendParam calldata _sendParam,\n MessagingFee calldata _fee,\n address _refundAddress\n ) internal virtual returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) {\n // @dev Applies the token transfers regarding this send() operation.\n // - amountSentLD is the amount in local decimals that was ACTUALLY sent/debited from the sender.\n // - amountReceivedLD is the amount in local decimals that will be received/credited to the recipient on the remote OFT instance.\n (uint256 amountSentLD, uint256 amountReceivedLD) = _debit(\n msg.sender,\n _sendParam.amountLD,\n _sendParam.minAmountLD,\n _sendParam.dstEid\n );\n\n // @dev Builds the options and OFT message to quote in the endpoint.\n (bytes memory message, bytes memory options) = _buildMsgAndOptions(_sendParam, amountReceivedLD);\n\n // @dev Sends the message to the LayerZero endpoint and returns the LayerZero msg receipt.\n msgReceipt = _lzSend(_sendParam.dstEid, message, options, _fee, _refundAddress);\n // @dev Formulate the OFT receipt.\n oftReceipt = OFTReceipt(amountSentLD, amountReceivedLD);\n\n emit OFTSent(msgReceipt.guid, _sendParam.dstEid, msg.sender, amountSentLD, amountReceivedLD);\n }\n\n /**\n * @dev Internal function to build the message and options.\n * @param _sendParam The parameters for the send() operation.\n * @param _amountLD The amount in local decimals.\n * @return message The encoded message.\n * @return options The encoded options.\n */\n function _buildMsgAndOptions(\n SendParam calldata _sendParam,\n uint256 _amountLD\n ) internal view virtual returns (bytes memory message, bytes memory options) {\n bool hasCompose;\n // @dev This generated message has the msg.sender encoded into the payload so the remote knows who the caller is.\n (message, hasCompose) = OFTMsgCodec.encode(\n _sendParam.to,\n _toSD(_amountLD),\n // @dev Must be include a non empty bytes if you want to compose, EVEN if you dont need it on the remote.\n // EVEN if you dont require an arbitrary payload to be sent... eg. '0x01'\n _sendParam.composeMsg\n );\n // @dev Change the msg type depending if its composed or not.\n uint16 msgType = hasCompose ? SEND_AND_CALL : SEND;\n // @dev Combine the callers _extraOptions with the enforced options via the OAppOptionsType3.\n options = combineOptions(_sendParam.dstEid, msgType, _sendParam.extraOptions);\n\n // @dev Optionally inspect the message and options depending if the OApp owner has set a msg inspector.\n // @dev If it fails inspection, needs to revert in the implementation. ie. does not rely on return boolean\n address inspector = msgInspector; // caches the msgInspector to avoid potential double storage read\n if (inspector != address(0)) IOAppMsgInspector(inspector).inspect(message, options);\n }\n\n /**\n * @dev Internal function to handle the receive on the LayerZero endpoint.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The encoded message.\n * @dev _executor The address of the executor.\n * @dev _extraData Additional data.\n */\n function _lzReceive(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address /*_executor*/, // @dev unused in the default implementation.\n bytes calldata /*_extraData*/ // @dev unused in the default implementation.\n ) internal virtual override {\n // @dev The src sending chain doesnt know the address length on this chain (potentially non-evm)\n // Thus everything is bytes32() encoded in flight.\n address toAddress = _message.sendTo().bytes32ToAddress();\n // @dev Credit the amountLD to the recipient and return the ACTUAL amount the recipient received in local decimals\n uint256 amountReceivedLD = _credit(toAddress, _toLD(_message.amountSD()), _origin.srcEid);\n\n if (_message.isComposed()) {\n // @dev Proprietary composeMsg format for the OFT.\n bytes memory composeMsg = OFTComposeMsgCodec.encode(\n _origin.nonce,\n _origin.srcEid,\n amountReceivedLD,\n _message.composeMsg()\n );\n\n // @dev Stores the lzCompose payload that will be executed in a separate tx.\n // Standardizes functionality for executing arbitrary contract invocation on some non-evm chains.\n // @dev The off-chain executor will listen and process the msg based on the src-chain-callers compose options passed.\n // @dev The index is used when a OApp needs to compose multiple msgs on lzReceive.\n // For default OFT implementation there is only 1 compose msg per lzReceive, thus its always 0.\n endpoint.sendCompose(toAddress, _guid, 0 /* the index of the composed message*/, composeMsg);\n }\n\n emit OFTReceived(_guid, _origin.srcEid, toAddress, amountReceivedLD);\n }\n\n /**\n * @dev Internal function to handle the OAppPreCrimeSimulator simulated receive.\n * @param _origin The origin information.\n * - srcEid: The source chain endpoint ID.\n * - sender: The sender address from the src chain.\n * - nonce: The nonce of the LayerZero message.\n * @param _guid The unique identifier for the received LayerZero message.\n * @param _message The LayerZero message.\n * @param _executor The address of the off-chain executor.\n * @param _extraData Arbitrary data passed by the msg executor.\n *\n * @dev Enables the preCrime simulator to mock sending lzReceive() messages,\n * routes the msg down from the OAppPreCrimeSimulator, and back up to the OAppReceiver.\n */\n function _lzReceiveSimulate(\n Origin calldata _origin,\n bytes32 _guid,\n bytes calldata _message,\n address _executor,\n bytes calldata _extraData\n ) internal virtual override {\n _lzReceive(_origin, _guid, _message, _executor, _extraData);\n }\n\n /**\n * @dev Check if the peer is considered 'trusted' by the OApp.\n * @param _eid The endpoint ID to check.\n * @param _peer The peer to check.\n * @return Whether the peer passed is considered 'trusted' by the OApp.\n *\n * @dev Enables OAppPreCrimeSimulator to check whether a potential Inbound Packet is from a trusted source.\n */\n function isPeer(uint32 _eid, bytes32 _peer) public view virtual override returns (bool) {\n return peers[_eid] == _peer;\n }\n\n /**\n * @dev Internal function to remove dust from the given local decimal amount.\n * @param _amountLD The amount in local decimals.\n * @return amountLD The amount after removing dust.\n *\n * @dev Prevents the loss of dust when moving amounts between chains with different decimals.\n * @dev eg. uint(123) with a conversion rate of 100 becomes uint(100).\n */\n function _removeDust(uint256 _amountLD) internal view virtual returns (uint256 amountLD) {\n return (_amountLD / decimalConversionRate) * decimalConversionRate;\n }\n\n /**\n * @dev Internal function to convert an amount from shared decimals into local decimals.\n * @param _amountSD The amount in shared decimals.\n * @return amountLD The amount in local decimals.\n */\n function _toLD(uint64 _amountSD) internal view virtual returns (uint256 amountLD) {\n return _amountSD * decimalConversionRate;\n }\n\n /**\n * @dev Internal function to convert an amount from local decimals into shared decimals.\n * @param _amountLD The amount in local decimals.\n * @return amountSD The amount in shared decimals.\n */\n function _toSD(uint256 _amountLD) internal view virtual returns (uint64 amountSD) {\n return uint64(_amountLD / decimalConversionRate);\n }\n\n /**\n * @dev Internal function to mock the amount mutation from a OFT debit() operation.\n * @param _amountLD The amount to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @dev _dstEid The destination endpoint ID.\n * @return amountSentLD The amount sent, in local decimals.\n * @return amountReceivedLD The amount to be received on the remote chain, in local decimals.\n *\n * @dev This is where things like fees would be calculated and deducted from the amount to be received on the remote.\n */\n function _debitView(\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 /*_dstEid*/\n ) internal view virtual returns (uint256 amountSentLD, uint256 amountReceivedLD) {\n // @dev Remove the dust so nothing is lost on the conversion between chains with different decimals for the token.\n amountSentLD = _removeDust(_amountLD);\n // @dev The amount to send is the same as amount received in the default implementation.\n amountReceivedLD = amountSentLD;\n\n // @dev Check for slippage.\n if (amountReceivedLD < _minAmountLD) {\n revert SlippageExceeded(amountReceivedLD, _minAmountLD);\n }\n }\n\n /**\n * @dev Internal function to perform a debit operation.\n * @param _from The address to debit.\n * @param _amountLD The amount to send in local decimals.\n * @param _minAmountLD The minimum amount to send in local decimals.\n * @param _dstEid The destination endpoint ID.\n * @return amountSentLD The amount sent in local decimals.\n * @return amountReceivedLD The amount received in local decimals on the remote.\n *\n * @dev Defined here but are intended to be overriden depending on the OFT implementation.\n * @dev Depending on OFT implementation the _amountLD could differ from the amountReceivedLD.\n */\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n ) internal virtual returns (uint256 amountSentLD, uint256 amountReceivedLD);\n\n /**\n * @dev Internal function to perform a credit operation.\n * @param _to The address to credit.\n * @param _amountLD The amount to credit in local decimals.\n * @param _srcEid The source endpoint ID.\n * @return amountReceivedLD The amount ACTUALLY received in local decimals.\n *\n * @dev Defined here but are intended to be overriden depending on the OFT implementation.\n * @dev Depending on OFT implementation the _amountLD could differ from the amountReceivedLD.\n */\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 _srcEid\n ) internal virtual returns (uint256 amountReceivedLD);\n}\n" + }, + "@openzeppelin/contracts/access/AccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/AccessControl.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\nimport \"../utils/Context.sol\";\nimport \"../utils/Strings.sol\";\nimport \"../utils/introspection/ERC165.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\n * members except through off-chain means by accessing the contract event logs. Some\n * applications may benefit from on-chain enumerability, for those cases see\n * {AccessControlEnumerable}.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```\n * function foo() public {\n * require(hasRole(MY_ROLE, msg.sender));\n * ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it.\n */\nabstract contract AccessControl is Context, IAccessControl, ERC165 {\n struct RoleData {\n mapping(address => bool) members;\n bytes32 adminRole;\n }\n\n mapping(bytes32 => RoleData) private _roles;\n\n bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n /**\n * @dev Modifier that checks that an account has a specific role. Reverts\n * with a standardized message including the required role.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n *\n * _Available since v4.1._\n */\n modifier onlyRole(bytes32 role) {\n _checkRole(role, _msgSender());\n _;\n }\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) public view override returns (bool) {\n return _roles[role].members[account];\n }\n\n /**\n * @dev Revert with a standard message if `account` is missing `role`.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n */\n function _checkRole(bytes32 role, address account) internal view {\n if (!hasRole(role, account)) {\n revert(\n string(\n abi.encodePacked(\n \"AccessControl: account \",\n Strings.toHexString(uint160(account), 20),\n \" is missing role \",\n Strings.toHexString(uint256(role), 32)\n )\n )\n );\n }\n }\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) public view override returns (bytes32) {\n return _roles[role].adminRole;\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _grantRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _revokeRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been revoked `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) public virtual override {\n require(account == _msgSender(), \"AccessControl: can only renounce roles for self\");\n\n _revokeRole(role, account);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event. Note that unlike {grantRole}, this function doesn't perform any\n * checks on the calling account.\n *\n * [WARNING]\n * ====\n * This function should only be called from the constructor when setting\n * up the initial roles for the system.\n *\n * Using this function in any other way is effectively circumventing the admin\n * system imposed by {AccessControl}.\n * ====\n *\n * NOTE: This function is deprecated in favor of {_grantRole}.\n */\n function _setupRole(bytes32 role, address account) internal virtual {\n _grantRole(role, account);\n }\n\n /**\n * @dev Sets `adminRole` as ``role``'s admin role.\n *\n * Emits a {RoleAdminChanged} event.\n */\n function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n bytes32 previousAdminRole = getRoleAdmin(role);\n _roles[role].adminRole = adminRole;\n emit RoleAdminChanged(role, previousAdminRole, adminRole);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * Internal function without access restriction.\n */\n function _grantRole(bytes32 role, address account) internal virtual {\n if (!hasRole(role, account)) {\n _roles[role].members[account] = true;\n emit RoleGranted(role, account, _msgSender());\n }\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * Internal function without access restriction.\n */\n function _revokeRole(bytes32 role, address account) internal virtual {\n if (hasRole(role, account)) {\n _roles[role].members[account] = false;\n emit RoleRevoked(role, account, _msgSender());\n }\n }\n}\n" + }, + "@openzeppelin/contracts/access/AccessControlEnumerable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/AccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControlEnumerable.sol\";\nimport \"./AccessControl.sol\";\nimport \"../utils/structs/EnumerableSet.sol\";\n\n/**\n * @dev Extension of {AccessControl} that allows enumerating the members of each role.\n */\nabstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {\n using EnumerableSet for EnumerableSet.AddressSet;\n\n mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) public view override returns (address) {\n return _roleMembers[role].at(index);\n }\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) public view override returns (uint256) {\n return _roleMembers[role].length();\n }\n\n /**\n * @dev Overload {_grantRole} to track enumerable memberships\n */\n function _grantRole(bytes32 role, address account) internal virtual override {\n super._grantRole(role, account);\n _roleMembers[role].add(account);\n }\n\n /**\n * @dev Overload {_revokeRole} to track enumerable memberships\n */\n function _revokeRole(bytes32 role, address account) internal virtual override {\n super._revokeRole(role, account);\n _roleMembers[role].remove(account);\n }\n}\n" + }, + "@openzeppelin/contracts/access/IAccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n /**\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n *\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n * {RoleAdminChanged} not being emitted signaling this.\n *\n * _Available since v3.1._\n */\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n /**\n * @dev Emitted when `account` is granted `role`.\n *\n * `sender` is the account that originated the contract call, an admin role\n * bearer except when using {AccessControl-_setupRole}.\n */\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Emitted when `account` is revoked `role`.\n *\n * `sender` is the account that originated the contract call:\n * - if using `revokeRole`, it is the admin role bearer\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\n */\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) external view returns (bool);\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been granted `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) external;\n}\n" + }, + "@openzeppelin/contracts/access/IAccessControlEnumerable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\n\n/**\n * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.\n */\ninterface IAccessControlEnumerable is IAccessControl {\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) external view returns (address);\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) external view returns (uint256);\n}\n" + }, + "@openzeppelin/contracts/access/Ownable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n" + }, + "@openzeppelin/contracts/security/Pausable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/ERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/ERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"./extensions/IERC20Metadata.sol\";\nimport \"../../utils/Context.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20, IERC20Metadata {\n mapping(address => uint256) private _balances;\n\n mapping(address => mapping(address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n\n /**\n * @dev Sets the values for {name} and {symbol}.\n *\n * The default value of {decimals} is 18. To select a different value for\n * {decimals} you should overload it.\n *\n * All two of these values are immutable: they can only be set once during\n * construction.\n */\n constructor(string memory name_, string memory symbol_) {\n _name = name_;\n _symbol = symbol_;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless this function is\n * overridden;\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `recipient` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n _approve(_msgSender(), spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * Requirements:\n *\n * - `sender` and `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n * - the caller must have allowance for ``sender``'s tokens of at least\n * `amount`.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public virtual override returns (bool) {\n _transfer(sender, recipient, amount);\n\n uint256 currentAllowance = _allowances[sender][_msgSender()];\n require(currentAllowance >= amount, \"ERC20: transfer amount exceeds allowance\");\n unchecked {\n _approve(sender, _msgSender(), currentAllowance - amount);\n }\n\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n uint256 currentAllowance = _allowances[_msgSender()][spender];\n require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n unchecked {\n _approve(_msgSender(), spender, currentAllowance - subtractedValue);\n }\n\n return true;\n }\n\n /**\n * @dev Moves `amount` of tokens from `sender` to `recipient`.\n *\n * This internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `sender` cannot be the zero address.\n * - `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n */\n function _transfer(\n address sender,\n address recipient,\n uint256 amount\n ) internal virtual {\n require(sender != address(0), \"ERC20: transfer from the zero address\");\n require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(sender, recipient, amount);\n\n uint256 senderBalance = _balances[sender];\n require(senderBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n unchecked {\n _balances[sender] = senderBalance - amount;\n }\n _balances[recipient] += amount;\n\n emit Transfer(sender, recipient, amount);\n\n _afterTokenTransfer(sender, recipient, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply += amount;\n _balances[account] += amount;\n emit Transfer(address(0), account, amount);\n\n _afterTokenTransfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n uint256 accountBalance = _balances[account];\n require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n unchecked {\n _balances[account] = accountBalance - amount;\n }\n _totalSupply -= amount;\n\n emit Transfer(account, address(0), amount);\n\n _afterTokenTransfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n\n /**\n * @dev Hook that is called after any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * has been transferred to `to`.\n * - when `from` is zero, `amount` tokens have been minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _afterTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC721 token receiver interface\n * @dev Interface for any contract that wants to support safeTransfers\n * from ERC721 asset contracts.\n */\ninterface IERC721Receiver {\n /**\n * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}\n * by `operator` from `from`, this function is called.\n *\n * It must return its Solidity selector to confirm the token transfer.\n * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.\n *\n * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.\n */\n function onERC721Received(\n address operator,\n address from,\n uint256 tokenId,\n bytes calldata data\n ) external returns (bytes4);\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/ERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC165.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n *\n * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.\n */\nabstract contract ERC165 is IERC165 {\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IERC165).interfaceId;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/IERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a >= b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a / b + (a % b == 0 ? 0 : 1);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128) {\n require(value >= type(int128).min && value <= type(int128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return int128(value);\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64) {\n require(value >= type(int64).min && value <= type(int64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return int64(value);\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32) {\n require(value >= type(int32).min && value <= type(int32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return int32(value);\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16) {\n require(value >= type(int16).min && value <= type(int16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return int16(value);\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits.\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8) {\n require(value >= type(int8).min && value <= type(int8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return int8(value);\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SafeMath.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)\n\npragma solidity ^0.8.0;\n\n// CAUTION\n// This version of SafeMath should only be used with Solidity 0.8 or later,\n// because it relies on the compiler's built in overflow checks.\n\n/**\n * @dev Wrappers over Solidity's arithmetic operations.\n *\n * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler\n * now has built in overflow checking.\n */\nlibrary SafeMath {\n /**\n * @dev Returns the addition of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n uint256 c = a + b;\n if (c < a) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the substraction of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b > a) return (false, 0);\n return (true, a - b);\n }\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\n *\n * _Available since v3.4._\n */\n function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n // benefit is lost if 'b' is also tested.\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n if (a == 0) return (true, 0);\n uint256 c = a * b;\n if (c / a != b) return (false, 0);\n return (true, c);\n }\n }\n\n /**\n * @dev Returns the division of two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a / b);\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\n *\n * _Available since v3.4._\n */\n function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n unchecked {\n if (b == 0) return (false, 0);\n return (true, a % b);\n }\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `+` operator.\n *\n * Requirements:\n *\n * - Addition cannot overflow.\n */\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\n return a + b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting on\n * overflow (when the result is negative).\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\n return a - b;\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `*` operator.\n *\n * Requirements:\n *\n * - Multiplication cannot overflow.\n */\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\n return a * b;\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator.\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\n return a / b;\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting when dividing by zero.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\n return a % b;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\n * overflow (when the result is negative).\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {trySub}.\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b <= a, errorMessage);\n return a - b;\n }\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers, reverting with custom message on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator. Note: this function uses a\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\n * uses an invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a / b;\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * reverting with custom message when dividing by zero.\n *\n * CAUTION: This function is deprecated because it requires allocating memory for the error\n * message unnecessarily. For custom revert reasons use {tryMod}.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function mod(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n unchecked {\n require(b > 0, errorMessage);\n return a % b;\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Strings.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n bytes16 private constant _HEX_SYMBOLS = \"0123456789abcdef\";\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n // Inspired by OraclizeAPI's implementation - MIT licence\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\n\n if (value == 0) {\n return \"0\";\n }\n uint256 temp = value;\n uint256 digits;\n while (temp != 0) {\n digits++;\n temp /= 10;\n }\n bytes memory buffer = new bytes(digits);\n while (value != 0) {\n digits -= 1;\n buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\n value /= 10;\n }\n return string(buffer);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n if (value == 0) {\n return \"0x00\";\n }\n uint256 temp = value;\n uint256 length = 0;\n while (temp != 0) {\n length++;\n temp >>= 8;\n }\n return toHexString(value, length);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _HEX_SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/structs/EnumerableSet.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 => uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n" + }, + "contracts/automation/AbstractCCIPBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IRouterClient } from \"@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol\";\nimport { Client } from \"@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol\";\n\nabstract contract AbstractCCIPBridgeHelperModule is AbstractSafeModule {\n /**\n * @notice Bridges a token from the source chain to the destination chain using CCIP\n * @param ccipRouter The CCIP router contract\n * @param destinationChainSelector The selector for the destination chain\n * @param token The token to bridge\n * @param amount The amount of token to bridge\n */\n function _bridgeTokenWithCCIP(\n IRouterClient ccipRouter,\n uint64 destinationChainSelector,\n IERC20 token,\n uint256 amount\n ) internal {\n bool success;\n\n // Approve CCIP Router to move the token\n success = safeContract.execTransactionFromModule(\n address(token),\n 0, // Value\n abi.encodeWithSelector(token.approve.selector, ccipRouter, amount),\n 0 // Call\n );\n require(success, \"Failed to approve token\");\n\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(token),\n amount: amount\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory ccipMessage = Client.EVM2AnyMessage({\n receiver: abi.encode(address(safeContract)), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n // Get CCIP fee\n uint256 ccipFee = ccipRouter.getFee(\n destinationChainSelector,\n ccipMessage\n );\n\n // Send CCIP message\n success = safeContract.execTransactionFromModule(\n address(ccipRouter),\n ccipFee, // Value\n abi.encodeWithSelector(\n ccipRouter.ccipSend.selector,\n destinationChainSelector,\n ccipMessage\n ),\n 0 // Call\n );\n require(success, \"Failed to send CCIP message\");\n }\n}\n" + }, + "contracts/automation/AbstractLZBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\n\nimport { IOFT, SendParam } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\nimport { MessagingFee } from \"@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol\";\nimport { OptionsBuilder } from \"@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nabstract contract AbstractLZBridgeHelperModule is AbstractSafeModule {\n using OptionsBuilder for bytes;\n\n /**\n * @dev Bridges token using LayerZero.\n * @param lzEndpointId LayerZero endpoint id.\n * @param token Token to bridge.\n * @param lzAdapter LZ Adapter to use.\n * @param amount Amount of token to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n * @param isNativeToken Whether the token is native token.\n */\n function _bridgeTokenWithLz(\n uint32 lzEndpointId,\n IERC20 token,\n IOFT lzAdapter,\n uint256 amount,\n uint256 slippageBps,\n bool isNativeToken\n ) internal {\n bool success;\n\n if (!isNativeToken) {\n // Approve LZ Adapter to move the token\n success = safeContract.execTransactionFromModule(\n address(token),\n 0, // Value\n abi.encodeWithSelector(\n token.approve.selector,\n address(lzAdapter),\n amount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve token\");\n }\n\n // Calculate minimum amount to receive\n uint256 minAmount = (amount * (10000 - slippageBps)) / 10000;\n\n // Hardcoded gaslimit of 400k\n bytes memory options = OptionsBuilder\n .newOptions()\n .addExecutorLzReceiveOption(400000, 0);\n\n // Build send param\n SendParam memory sendParam = SendParam({\n dstEid: lzEndpointId,\n to: bytes32(uint256(uint160(address(safeContract)))),\n amountLD: amount,\n minAmountLD: minAmount,\n extraOptions: options,\n composeMsg: bytes(\"\"),\n oftCmd: bytes(\"\")\n });\n\n // Compute fees\n MessagingFee memory msgFee = lzAdapter.quoteSend(sendParam, false);\n\n uint256 value = isNativeToken\n ? amount + msgFee.nativeFee\n : msgFee.nativeFee;\n\n // Execute transaction\n success = safeContract.execTransactionFromModule(\n address(lzAdapter),\n value,\n abi.encodeWithSelector(\n lzAdapter.send.selector,\n sendParam,\n msgFee,\n address(safeContract)\n ),\n 0\n );\n require(success, \"Failed to bridge token\");\n }\n}\n" + }, + "contracts/automation/AbstractSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { ISafe } from \"../interfaces/ISafe.sol\";\n\nabstract contract AbstractSafeModule is AccessControlEnumerable {\n ISafe public immutable safeContract;\n\n bytes32 public constant OPERATOR_ROLE = keccak256(\"OPERATOR_ROLE\");\n\n modifier onlySafe() {\n require(\n msg.sender == address(safeContract),\n \"Caller is not the safe contract\"\n );\n _;\n }\n\n modifier onlyOperator() {\n require(\n hasRole(OPERATOR_ROLE, msg.sender),\n \"Caller is not an operator\"\n );\n _;\n }\n\n constructor(address _safeContract) {\n safeContract = ISafe(_safeContract);\n _grantRole(DEFAULT_ADMIN_ROLE, address(safeContract));\n _grantRole(OPERATOR_ROLE, address(safeContract));\n }\n\n /**\n * @dev Helps recovering any tokens accidentally sent to this module.\n * @param token Token to transfer. 0x0 to transfer Native token.\n * @param amount Amount to transfer. 0 to transfer all balance.\n */\n function transferTokens(address token, uint256 amount) external onlySafe {\n if (address(token) == address(0)) {\n // Move ETH\n amount = amount > 0 ? amount : address(this).balance;\n payable(address(safeContract)).transfer(amount);\n return;\n }\n\n // Move all balance if amount set to 0\n amount = amount > 0 ? amount : IERC20(token).balanceOf(address(this));\n\n // Transfer to Safe contract\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(token).transfer(address(safeContract), amount);\n }\n\n receive() external payable {\n // Accept ETH to pay for bridge fees\n }\n}\n" + }, + "contracts/automation/BaseBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// solhint-disable-next-line max-line-length\nimport { AbstractCCIPBridgeHelperModule, AbstractSafeModule, IRouterClient } from \"./AbstractCCIPBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { BridgedWOETHStrategy } from \"../strategies/BridgedWOETHStrategy.sol\";\n\ncontract BaseBridgeHelperModule is\n AccessControlEnumerable,\n AbstractCCIPBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0x98a0CbeF61bD2D21435f433bE4CD42B56B38CC93);\n IWETH9 public constant weth =\n IWETH9(0x4200000000000000000000000000000000000006);\n IERC20 public constant oethb =\n IERC20(0xDBFeFD2e8460a6Ee4955A68582F85708BAEA60A3);\n IERC4626 public constant bridgedWOETH =\n IERC4626(0xD8724322f44E5c58D7A815F542036fb17DbbF839);\n\n BridgedWOETHStrategy public constant bridgedWOETHStrategy =\n BridgedWOETHStrategy(0x80c864704DD06C3693ed5179190786EE38ACf835);\n\n IRouterClient public constant CCIP_ROUTER =\n IRouterClient(0x881e3A65B4d4a04dD529061dd0071cf975F58bCD);\n\n uint64 public constant CCIP_ETHEREUM_CHAIN_SELECTOR = 5009297550715157269;\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Ethereum.\n * @param woethAmount Amount of wOETH to bridge.\n */\n function bridgeWOETHToEthereum(uint256 woethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_ETHEREUM_CHAIN_SELECTOR,\n IERC20(address(bridgedWOETH)),\n woethAmount\n );\n }\n\n /**\n * @dev Bridges WETH to Ethereum.\n * @param wethAmount Amount of WETH to bridge.\n */\n function bridgeWETHToEthereum(uint256 wethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_ETHEREUM_CHAIN_SELECTOR,\n IERC20(address(weth)),\n wethAmount\n );\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem the wOETH for WETH using the Vault.\n * @return Amount of WETH received.\n */\n function depositWOETH(uint256 woethAmount, bool redeemWithVault)\n external\n onlyOperator\n returns (uint256)\n {\n return _depositWOETH(woethAmount, redeemWithVault);\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy and bridges it to Ethereum.\n * @param woethAmount Amount of wOETH to deposit.\n * @return Amount of WETH received.\n */\n function depositWOETHAndBridgeWETH(uint256 woethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n uint256 wethAmount = _depositWOETH(woethAmount, true);\n bridgeWETHToEthereum(wethAmount);\n return wethAmount;\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem the wOETH for WETH using the Vault.\n * @return Amount of WETH received.\n */\n function _depositWOETH(uint256 woethAmount, bool redeemWithVault)\n internal\n returns (uint256)\n {\n // Update oracle price\n bridgedWOETHStrategy.updateWOETHOraclePrice();\n\n // Rebase to account for any yields from price update\n vault.rebase();\n\n uint256 oethbAmount = oethb.balanceOf(address(safeContract));\n\n // Approve bridgedWOETH strategy to move wOETH\n bool success = safeContract.execTransactionFromModule(\n address(bridgedWOETH),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETH.approve.selector,\n address(bridgedWOETHStrategy),\n woethAmount\n ),\n 0 // Call\n );\n\n // Deposit to bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.depositBridgedWOETH.selector,\n woethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to deposit bridged WOETH\");\n\n oethbAmount = oethb.balanceOf(address(safeContract)) - oethbAmount;\n\n // Rebase to account for any yields from price update\n // and backing asset change from deposit\n vault.rebase();\n\n if (!redeemWithVault) {\n return oethbAmount;\n }\n\n // Redeem for WETH using Vault\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethbAmount,\n oethbAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETHb\");\n\n return oethbAmount;\n }\n\n /**\n * @dev Deposits WETH into the Vault and redeems wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to deposit.\n * @return Amount of wOETH received.\n */\n function depositWETHAndRedeemWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _withdrawWOETH(wethAmount);\n }\n\n function depositWETHAndBridgeWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n uint256 woethAmount = _withdrawWOETH(wethAmount);\n bridgeWOETHToEthereum(woethAmount);\n return woethAmount;\n }\n\n /**\n * @dev Withdraws wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to use to withdraw.\n * @return Amount of wOETH received.\n */\n function _withdrawWOETH(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETHb with WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETHb\");\n\n // Approve bridgedWOETH strategy to move OETHb\n success = safeContract.execTransactionFromModule(\n address(oethb),\n 0, // Value\n abi.encodeWithSelector(\n oethb.approve.selector,\n address(bridgedWOETHStrategy),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETHb\");\n\n uint256 woethAmount = bridgedWOETH.balanceOf(address(safeContract));\n\n // Withdraw from bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.withdrawBridgedWOETH.selector,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to withdraw bridged WOETH\");\n\n woethAmount =\n bridgedWOETH.balanceOf(address(safeContract)) -\n woethAmount;\n\n return woethAmount;\n }\n}\n" + }, + "contracts/automation/ClaimBribesSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { ICLGauge } from \"../interfaces/aerodrome/ICLGauge.sol\";\nimport { ICLPool } from \"../interfaces/aerodrome/ICLPool.sol\";\n\nstruct BribePoolInfo {\n address poolAddress;\n address rewardContractAddress;\n address[] rewardTokens;\n}\n\ninterface IAerodromeVoter {\n function claimBribes(\n address[] memory _bribes,\n address[][] memory _tokens,\n uint256 _tokenId\n ) external;\n}\n\ninterface IVeNFT {\n function ownerOf(uint256 tokenId) external view returns (address);\n\n function ownerToNFTokenIdList(address owner, uint256 index)\n external\n view\n returns (uint256);\n}\n\ninterface ICLRewardContract {\n function rewards(uint256 index) external view returns (address);\n\n function rewardsListLength() external view returns (uint256);\n}\n\ncontract ClaimBribesSafeModule is AbstractSafeModule {\n IAerodromeVoter public immutable voter;\n address public immutable veNFT;\n\n uint256[] nftIds;\n mapping(uint256 => uint256) nftIdIndex;\n\n BribePoolInfo[] bribePools;\n mapping(address => uint256) bribePoolIndex;\n\n event NFTIdAdded(uint256 nftId);\n event NFTIdRemoved(uint256 nftId);\n\n event BribePoolAdded(address bribePool);\n event BribePoolRemoved(address bribePool);\n\n constructor(\n address _safeContract,\n address _voter,\n address _veNFT\n ) AbstractSafeModule(_safeContract) {\n voter = IAerodromeVoter(_voter);\n veNFT = _veNFT;\n }\n\n /**\n * @dev Claim bribes for a range of NFTs\n * @param nftIndexStart The start index of the NFTs\n * @param nftIndexEnd The end index of the NFTs\n * @param silent Doesn't revert if the claim fails when true\n */\n function claimBribes(\n uint256 nftIndexStart,\n uint256 nftIndexEnd,\n bool silent\n ) external onlyOperator {\n if (nftIndexEnd < nftIndexStart) {\n (nftIndexStart, nftIndexEnd) = (nftIndexEnd, nftIndexStart);\n }\n uint256 nftCount = nftIds.length;\n nftIndexEnd = nftCount < nftIndexEnd ? nftCount : nftIndexEnd;\n\n (\n address[] memory rewardContractAddresses,\n address[][] memory rewardTokens\n ) = _getRewardsInfoArray();\n\n for (uint256 i = nftIndexStart; i < nftIndexEnd; i++) {\n uint256 nftId = nftIds[i];\n bool success = safeContract.execTransactionFromModule(\n address(voter),\n 0, // Value\n abi.encodeWithSelector(\n IAerodromeVoter.claimBribes.selector,\n rewardContractAddresses,\n rewardTokens,\n nftId\n ),\n 0 // Call\n );\n\n require(success || silent, \"ClaimBribes failed\");\n }\n }\n\n /**\n * @dev Get the reward contract address and reward tokens for all pools\n * @return rewardContractAddresses The reward contract addresses\n * @return rewardTokens The reward tokens\n */\n function _getRewardsInfoArray()\n internal\n view\n returns (\n address[] memory rewardContractAddresses,\n address[][] memory rewardTokens\n )\n {\n BribePoolInfo[] memory _bribePools = bribePools;\n uint256 bribePoolCount = _bribePools.length;\n rewardContractAddresses = new address[](bribePoolCount);\n rewardTokens = new address[][](bribePoolCount);\n\n for (uint256 i = 0; i < bribePoolCount; i++) {\n rewardContractAddresses[i] = _bribePools[i].rewardContractAddress;\n rewardTokens[i] = _bribePools[i].rewardTokens;\n }\n }\n\n /***************************************\n NFT Management\n ****************************************/\n /**\n * @dev Add NFT IDs to the list\n * @param _nftIds The NFT IDs to add\n */\n function addNFTIds(uint256[] memory _nftIds) external onlyOperator {\n for (uint256 i = 0; i < _nftIds.length; i++) {\n uint256 nftId = _nftIds[i];\n if (nftIdExists(nftId)) {\n // If it already exists, skip\n continue;\n }\n\n // Make sure the NFT is owned by the Safe\n require(\n IVeNFT(veNFT).ownerOf(nftId) == address(safeContract),\n \"NFT not owned by safe\"\n );\n\n nftIdIndex[nftId] = nftIds.length;\n nftIds.push(nftId);\n\n emit NFTIdAdded(nftId);\n }\n }\n\n /**\n * @dev Remove NFT IDs from the list\n * @param _nftIds The NFT IDs to remove\n */\n function removeNFTIds(uint256[] memory _nftIds) external onlyOperator {\n for (uint256 i = 0; i < _nftIds.length; i++) {\n uint256 nftId = _nftIds[i];\n if (!nftIdExists(nftId)) {\n // If it doesn't exist, skip\n continue;\n }\n\n uint256 index = nftIdIndex[nftId];\n uint256 lastNftId = nftIds[nftIds.length - 1];\n nftIds[index] = lastNftId;\n nftIdIndex[lastNftId] = index;\n nftIds.pop();\n\n emit NFTIdRemoved(nftId);\n }\n }\n\n /**\n * @dev Check if a NFT exists on the list\n * @param nftId The NFT ID to check\n * @return true if the NFT ID exists, false otherwise\n */\n function nftIdExists(uint256 nftId) public view returns (bool) {\n uint256 index = nftIdIndex[nftId];\n uint256[] memory _nftIds = nftIds;\n return (index < _nftIds.length) && _nftIds[index] == nftId;\n }\n\n /**\n * @dev Get the length of the nftIds list\n * @return The length of the nftIds list\n */\n function getNFTIdsLength() external view returns (uint256) {\n return nftIds.length;\n }\n\n /**\n * @dev Get all NFT IDs\n * @return The NFT IDs\n */\n function getAllNFTIds() external view returns (uint256[] memory) {\n return nftIds;\n }\n\n /**\n * @dev Fetch all NFT IDs from the veNFT contract\n * @notice This can revert if Safe owns too many NFTs since tx will be huge.\n * This function is public, anyone can call it, since it only fetches\n * the NFT IDs owned by the Safe. It shouldn't cause us any issues.\n */\n function fetchNFTIds() external {\n // Purge the array\n delete nftIds;\n\n uint256 i = 0;\n while (true) {\n uint256 nftId = IVeNFT(veNFT).ownerToNFTokenIdList(\n address(safeContract),\n i\n );\n if (nftId == 0) {\n break;\n }\n\n nftIdIndex[nftId] = nftIds.length;\n nftIds.push(nftId);\n i++;\n }\n }\n\n /**\n * @dev Remove all NFT IDs from the list\n */\n function removeAllNFTIds() external onlyOperator {\n uint256 length = nftIds.length;\n for (uint256 i = 0; i < length; i++) {\n uint256 nftId = nftIds[i];\n delete nftIdIndex[nftId];\n emit NFTIdRemoved(nftId);\n }\n\n delete nftIds;\n }\n\n /***************************************\n Bribe Pool Management\n ****************************************/\n // @dev Whitelist a pool to claim bribes from\n // @param _poolAddress The address of the pool to whitelist\n function addBribePool(address _poolAddress, bool _isVotingContract)\n external\n onlySafe\n {\n BribePoolInfo memory bribePool;\n\n if (_isVotingContract) {\n bribePool = BribePoolInfo({\n poolAddress: _poolAddress,\n rewardContractAddress: _poolAddress,\n rewardTokens: _getRewardTokenAddresses(_poolAddress)\n });\n } else {\n // Find the gauge address\n address _gaugeAddress = ICLPool(_poolAddress).gauge();\n // And the reward contract address\n address _rewardContractAddress = ICLGauge(_gaugeAddress)\n .feesVotingReward();\n\n bribePool = BribePoolInfo({\n poolAddress: _poolAddress,\n rewardContractAddress: _rewardContractAddress,\n rewardTokens: _getRewardTokenAddresses(_rewardContractAddress)\n });\n }\n\n if (bribePoolExists(_poolAddress)) {\n // Update if it already exists\n bribePools[bribePoolIndex[_poolAddress]] = bribePool;\n } else {\n // If not, Append to the list\n bribePoolIndex[_poolAddress] = bribePools.length;\n bribePools.push(bribePool);\n }\n\n emit BribePoolAdded(_poolAddress);\n }\n\n /**\n * @dev Update the reward token addresses for all pools\n */\n function updateRewardTokenAddresses() external onlyOperator {\n BribePoolInfo[] storage _bribePools = bribePools;\n for (uint256 i = 0; i < _bribePools.length; i++) {\n BribePoolInfo storage bribePool = _bribePools[i];\n bribePool.rewardTokens = _getRewardTokenAddresses(\n bribePool.rewardContractAddress == bribePool.poolAddress\n ? bribePool.poolAddress\n : bribePool.rewardContractAddress\n );\n }\n }\n\n /**\n * @dev Get the reward token addresses for a given reward contract address\n * @param _rewardContractAddress The address of the reward contract\n * @return _rewardTokens The reward token addresses\n */\n function _getRewardTokenAddresses(address _rewardContractAddress)\n internal\n view\n returns (address[] memory)\n {\n address[] memory _rewardTokens = new address[](\n ICLRewardContract(_rewardContractAddress).rewardsListLength()\n );\n for (uint256 i = 0; i < _rewardTokens.length; i++) {\n _rewardTokens[i] = ICLRewardContract(_rewardContractAddress)\n .rewards(i);\n }\n\n return _rewardTokens;\n }\n\n /**\n * @dev Remove a bribe pool from the list\n * @param _poolAddress The address of the pool to remove\n */\n function removeBribePool(address _poolAddress) external onlySafe {\n if (!bribePoolExists(_poolAddress)) {\n // If it doesn't exist, skip\n return;\n }\n\n uint256 index = bribePoolIndex[_poolAddress];\n BribePoolInfo memory lastBribePool = bribePools[bribePools.length - 1];\n bribePools[index] = lastBribePool;\n bribePoolIndex[lastBribePool.poolAddress] = index;\n bribePools.pop();\n\n emit BribePoolRemoved(_poolAddress);\n }\n\n /**\n * @dev Check if a bribe pool exists\n * @param bribePool The address of the pool to check\n * @return true if the pool exists, false otherwise\n */\n function bribePoolExists(address bribePool) public view returns (bool) {\n BribePoolInfo[] memory _bribePools = bribePools;\n uint256 poolIndex = bribePoolIndex[bribePool];\n return\n poolIndex < _bribePools.length &&\n _bribePools[poolIndex].poolAddress == bribePool;\n }\n\n /**\n * @dev Get the length of the bribe pools list\n * @return The length of the bribe pools list\n */\n function getBribePoolsLength() external view returns (uint256) {\n return bribePools.length;\n }\n}\n" + }, + "contracts/automation/ClaimStrategyRewardsSafeModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\n\nimport { ISafe } from \"../interfaces/ISafe.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract ClaimStrategyRewardsSafeModule is AbstractSafeModule {\n using SafeERC20 for IERC20;\n\n mapping(address => bool) public isStrategyWhitelisted;\n address[] public strategies;\n\n event StrategyAdded(address strategy);\n event StrategyRemoved(address strategy);\n\n event ClaimRewardsFailed(address strategy);\n\n constructor(\n address _safeAddress,\n address operator,\n address[] memory _strategies\n ) AbstractSafeModule(_safeAddress) {\n _grantRole(OPERATOR_ROLE, operator);\n\n // Whitelist all strategies\n for (uint256 i = 0; i < _strategies.length; i++) {\n _addStrategy(_strategies[i]);\n }\n }\n\n /**\n * @dev Claim rewards from all whitelisted strategies\n * @param silent Doesn't revert on error if set to true\n */\n function claimRewards(bool silent) external onlyRole(OPERATOR_ROLE) {\n uint256 strategiesLength = strategies.length;\n for (uint256 i = 0; i < strategiesLength; i++) {\n address strategy = strategies[i];\n\n // Execute `collectRewardTokens` for all strategies\n bool success = safeContract.execTransactionFromModule(\n strategy, // To\n 0, // Value\n abi.encodeWithSelector(IStrategy.collectRewardTokens.selector),\n 0 // Call\n );\n\n if (!success) {\n emit ClaimRewardsFailed(strategy);\n }\n\n require(success || silent, \"Failed to claim rewards\");\n }\n }\n\n /**\n * @dev Add a strategy to the whitelist\n * @param _strategy The address of the strategy to add\n */\n function addStrategy(address _strategy)\n external\n onlyRole(DEFAULT_ADMIN_ROLE)\n {\n _addStrategy(_strategy);\n }\n\n function _addStrategy(address _strategy) internal {\n require(\n !isStrategyWhitelisted[_strategy],\n \"Strategy already whitelisted\"\n );\n isStrategyWhitelisted[_strategy] = true;\n strategies.push(_strategy);\n emit StrategyAdded(_strategy);\n }\n\n /**\n * @dev Remove a strategy from the whitelist\n * @param _strategy The address of the strategy to remove\n */\n function removeStrategy(address _strategy)\n external\n onlyRole(DEFAULT_ADMIN_ROLE)\n {\n require(isStrategyWhitelisted[_strategy], \"Strategy not whitelisted\");\n isStrategyWhitelisted[_strategy] = false;\n\n for (uint256 i = 0; i < strategies.length; i++) {\n if (strategies[i] == _strategy) {\n strategies[i] = strategies[strategies.length - 1];\n strategies.pop();\n break;\n }\n }\n\n emit StrategyRemoved(_strategy);\n }\n}\n" + }, + "contracts/automation/CollectXOGNRewardsModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IXOGN {\n function collectRewards() external;\n}\n\ncontract CollectXOGNRewardsModule is AbstractSafeModule {\n IXOGN public constant xogn =\n IXOGN(0x63898b3b6Ef3d39332082178656E9862bee45C57);\n address public constant rewardsSource =\n 0x7609c88E5880e934dd3A75bCFef44E31b1Badb8b;\n IERC20 public constant ogn =\n IERC20(0x8207c1FfC5B6804F6024322CcF34F29c3541Ae26);\n\n constructor(address _safeContract, address operator)\n AbstractSafeModule(_safeContract)\n {\n _grantRole(OPERATOR_ROLE, operator);\n }\n\n function collectRewards() external onlyOperator {\n uint256 balance = ogn.balanceOf(address(safeContract));\n\n bool success = safeContract.execTransactionFromModule(\n address(xogn),\n 0, // Value\n abi.encodeWithSelector(IXOGN.collectRewards.selector),\n 0 // Call\n );\n\n require(success, \"Failed to collect rewards\");\n\n balance = ogn.balanceOf(address(safeContract)) - balance;\n\n if (balance == 0) {\n return;\n }\n\n success = safeContract.execTransactionFromModule(\n address(ogn),\n 0, // Value\n abi.encodeWithSelector(\n IERC20.transfer.selector,\n rewardsSource,\n balance\n ),\n 0 // Call\n );\n\n require(success, \"Failed to collect rewards\");\n }\n}\n" + }, + "contracts/automation/EthereumBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractLZBridgeHelperModule, AbstractSafeModule } from \"./AbstractLZBridgeHelperModule.sol\";\nimport { AbstractCCIPBridgeHelperModule, IRouterClient } from \"./AbstractCCIPBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IOFT } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract EthereumBridgeHelperModule is\n AccessControlEnumerable,\n AbstractLZBridgeHelperModule,\n AbstractCCIPBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0x39254033945AA2E4809Cc2977E7087BEE48bd7Ab);\n IWETH9 public constant weth =\n IWETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);\n IERC20 public constant oeth =\n IERC20(0x856c4Efb76C1D1AE02e20CEB03A2A6a08b0b8dC3);\n IERC4626 public constant woeth =\n IERC4626(0xDcEe70654261AF21C44c093C300eD3Bb97b78192);\n\n uint32 public constant LZ_PLUME_ENDPOINT_ID = 30370;\n IOFT public constant LZ_WOETH_OMNICHAIN_ADAPTER =\n IOFT(0x7d1bEa5807e6af125826d56ff477745BB89972b8);\n IOFT public constant LZ_ETH_OMNICHAIN_ADAPTER =\n IOFT(0x77b2043768d28E9C9aB44E1aBfC95944bcE57931);\n\n IRouterClient public constant CCIP_ROUTER =\n IRouterClient(0x80226fc0Ee2b096224EeAc085Bb9a8cba1146f7D);\n\n uint64 public constant CCIP_BASE_CHAIN_SELECTOR = 15971525489660198786;\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Plume.\n * @param woethAmount Amount of wOETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWOETHToPlume(uint256 woethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_PLUME_ENDPOINT_ID,\n woeth,\n LZ_WOETH_OMNICHAIN_ADAPTER,\n woethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Bridges wOETH to Base using CCIP.\n * @param woethAmount Amount of wOETH to bridge.\n */\n function bridgeWOETHToBase(uint256 woethAmount)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_BASE_CHAIN_SELECTOR,\n woeth,\n woethAmount\n );\n }\n\n /**\n * @dev Bridges wETH to Plume.\n * @param wethAmount Amount of wETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWETHToPlume(uint256 wethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n // Unwrap into ETH\n safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(weth.withdraw.selector, wethAmount),\n 0 // Call\n );\n\n _bridgeTokenWithLz(\n LZ_PLUME_ENDPOINT_ID,\n IERC20(address(weth)),\n LZ_ETH_OMNICHAIN_ADAPTER,\n wethAmount,\n slippageBps,\n true\n );\n }\n\n /**\n * @dev Bridges wETH to Base using CCIP.\n * @param wethAmount Amount of wETH to bridge.\n */\n function bridgeWETHToBase(uint256 wethAmount) public payable onlyOperator {\n _bridgeTokenWithCCIP(\n CCIP_ROUTER,\n CCIP_BASE_CHAIN_SELECTOR,\n IERC20(address(weth)),\n wethAmount\n );\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH.\n * @param wethAmount Amount of WETH to mint.\n * @param useNativeToken Whether to use native token to mint.\n * @return Amount of wOETH minted.\n */\n function mintAndWrap(uint256 wethAmount, bool useNativeToken)\n external\n onlyOperator\n returns (uint256)\n {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n return _mintAndWrap(wethAmount);\n }\n\n function wrapETH(uint256 ethAmount) public payable onlyOperator {\n // Deposit ETH into WETH\n safeContract.execTransactionFromModule(\n address(weth),\n ethAmount, // Value\n abi.encodeWithSelector(weth.deposit.selector),\n 0 // Call\n );\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH.\n * @param wethAmount Amount of WETH to mint.\n * @return Amount of wOETH minted.\n */\n function _mintAndWrap(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETH\");\n\n // Approve wOETH to move OETH\n success = safeContract.execTransactionFromModule(\n address(oeth),\n 0, // Value\n abi.encodeWithSelector(\n oeth.approve.selector,\n address(woeth),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETH\");\n\n uint256 woethAmount = woeth.balanceOf(address(safeContract));\n\n // Wrap OETH into wOETH\n success = safeContract.execTransactionFromModule(\n address(woeth),\n 0, // Value\n abi.encodeWithSelector(\n woeth.deposit.selector,\n wethAmount,\n address(safeContract)\n ),\n 0 // Call\n );\n require(success, \"Failed to wrap OETH\");\n\n // Compute amount of wOETH minted\n return woeth.balanceOf(address(safeContract)) - woethAmount;\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH, then bridges it to Plume.\n * @param wethAmount Amount of WETH to mint.\n * @param slippageBps Bridge slippage in 10^4 basis points.\n * @param useNativeToken Whether to use native token to mint.\n */\n function mintWrapAndBridgeToPlume(\n uint256 wethAmount,\n uint256 slippageBps,\n bool useNativeToken\n ) external payable onlyOperator {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n uint256 woethAmount = _mintAndWrap(wethAmount);\n bridgeWOETHToPlume(woethAmount, slippageBps);\n }\n\n /**\n * @dev Mints OETH and wraps it into wOETH, then bridges it to Base using CCIP.\n * @param wethAmount Amount of WETH to mint.\n * @param useNativeToken Whether to use native token to mint.\n */\n function mintWrapAndBridgeToBase(uint256 wethAmount, bool useNativeToken)\n external\n payable\n onlyOperator\n {\n if (useNativeToken) {\n wrapETH(wethAmount);\n }\n\n uint256 woethAmount = _mintAndWrap(wethAmount);\n bridgeWOETHToBase(woethAmount);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH.\n * @param woethAmount Amount of wOETH to unwrap.\n * @return Amount of WETH received.\n */\n function unwrapAndRedeem(uint256 woethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _unwrapAndRedeem(woethAmount);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH.\n * @param woethAmount Amount of wOETH to unwrap.\n * @return Amount of WETH received.\n */\n function _unwrapAndRedeem(uint256 woethAmount) internal returns (uint256) {\n uint256 oethAmount = oeth.balanceOf(address(safeContract));\n\n // Unwrap wOETH\n bool success = safeContract.execTransactionFromModule(\n address(woeth),\n 0, // Value\n abi.encodeWithSelector(\n woeth.redeem.selector,\n woethAmount,\n address(safeContract),\n address(safeContract)\n ),\n 0 // Call\n );\n require(success, \"Failed to unwrap wOETH\");\n\n oethAmount = oeth.balanceOf(address(safeContract)) - oethAmount;\n\n // Redeem OETH using Vault to get WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethAmount,\n oethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETH\");\n\n return oethAmount;\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH, then bridges it to Plume.\n * @param woethAmount Amount of wOETH to unwrap.\n * @param slippageBps Bridge slippage in 10^4 basis points.\n */\n function unwrapRedeemAndBridgeToPlume(\n uint256 woethAmount,\n uint256 slippageBps\n ) external payable onlyOperator {\n uint256 wethAmount = _unwrapAndRedeem(woethAmount);\n // Unwrap into ETH\n safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(weth.withdraw.selector, wethAmount),\n 0 // Call\n );\n\n bridgeWETHToPlume(wethAmount, slippageBps);\n }\n\n /**\n * @dev Unwraps wOETH and redeems it to get WETH, then bridges it to Base using CCIP.\n * @param woethAmount Amount of wOETH to unwrap.\n */\n function unwrapRedeemAndBridgeToBase(uint256 woethAmount)\n external\n payable\n onlyOperator\n {\n uint256 wethAmount = _unwrapAndRedeem(woethAmount);\n bridgeWETHToBase(wethAmount);\n }\n}\n" + }, + "contracts/automation/PlumeBridgeHelperModule.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractSafeModule } from \"./AbstractSafeModule.sol\";\nimport { AbstractLZBridgeHelperModule } from \"./AbstractLZBridgeHelperModule.sol\";\n\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\n\nimport { IOFT } from \"@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { BridgedWOETHStrategy } from \"../strategies/BridgedWOETHStrategy.sol\";\n\ncontract PlumeBridgeHelperModule is\n AccessControlEnumerable,\n AbstractLZBridgeHelperModule\n{\n IVault public constant vault =\n IVault(0xc8c8F8bEA5631A8AF26440AF32a55002138cB76a);\n IWETH9 public constant weth =\n IWETH9(0xca59cA09E5602fAe8B629DeE83FfA819741f14be);\n IERC20 public constant oethp =\n IERC20(0xFCbe50DbE43bF7E5C88C6F6Fb9ef432D4165406E);\n IERC4626 public constant bridgedWOETH =\n IERC4626(0xD8724322f44E5c58D7A815F542036fb17DbbF839);\n\n uint32 public constant LZ_ETHEREUM_ENDPOINT_ID = 30101;\n IOFT public constant LZ_WOETH_OMNICHAIN_ADAPTER =\n IOFT(0x592CB6A596E7919930bF49a27AdAeCA7C055e4DB);\n IOFT public constant LZ_ETH_OMNICHAIN_ADAPTER =\n IOFT(0x4683CE822272CD66CEa73F5F1f9f5cBcaEF4F066);\n\n BridgedWOETHStrategy public constant bridgedWOETHStrategy =\n BridgedWOETHStrategy(0x1E3EdD5e019207D6355Ea77F724b1F1BF639B569);\n\n constructor(address _safeContract) AbstractSafeModule(_safeContract) {}\n\n /**\n * @dev Bridges wOETH to Ethereum.\n * @param woethAmount Amount of wOETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWOETHToEthereum(uint256 woethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_ETHEREUM_ENDPOINT_ID,\n IERC20(address(bridgedWOETH)),\n LZ_WOETH_OMNICHAIN_ADAPTER,\n woethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Bridges wETH to Ethereum.\n * @param wethAmount Amount of wETH to bridge.\n * @param slippageBps Slippage in 10^4 basis points.\n */\n function bridgeWETHToEthereum(uint256 wethAmount, uint256 slippageBps)\n public\n payable\n onlyOperator\n {\n _bridgeTokenWithLz(\n LZ_ETHEREUM_ENDPOINT_ID,\n IERC20(address(weth)),\n LZ_ETH_OMNICHAIN_ADAPTER,\n wethAmount,\n slippageBps,\n false\n );\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem with Vault.\n * @return Amount of OETHp received.\n */\n function depositWOETH(uint256 woethAmount, bool redeemWithVault)\n external\n onlyOperator\n returns (uint256)\n {\n return _depositWOETH(woethAmount, redeemWithVault);\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy and bridges it to Ethereum.\n * @param woethAmount Amount of wOETH to deposit.\n * @param slippageBps Slippage in 10^4 basis points.\n * @return Amount of WETH received.\n */\n function depositWOETHAndBridgeWETH(uint256 woethAmount, uint256 slippageBps)\n external\n payable\n onlyOperator\n returns (uint256)\n {\n uint256 wethAmount = _depositWOETH(woethAmount, true);\n bridgeWETHToEthereum(wethAmount, slippageBps);\n return wethAmount;\n }\n\n /**\n * @dev Deposits wOETH into the bridgedWOETH strategy.\n * @param woethAmount Amount of wOETH to deposit.\n * @param redeemWithVault Whether to redeem with Vault.\n * @return Amount of OETHp received.\n */\n function _depositWOETH(uint256 woethAmount, bool redeemWithVault)\n internal\n returns (uint256)\n {\n // Update oracle price\n bridgedWOETHStrategy.updateWOETHOraclePrice();\n\n // Rebase to account for any yields from price update\n vault.rebase();\n\n uint256 oethpAmount = oethp.balanceOf(address(safeContract));\n\n // Approve bridgedWOETH strategy to move wOETH\n bool success = safeContract.execTransactionFromModule(\n address(bridgedWOETH),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETH.approve.selector,\n address(bridgedWOETHStrategy),\n woethAmount\n ),\n 0 // Call\n );\n\n // Deposit to bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.depositBridgedWOETH.selector,\n woethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to deposit bridged WOETH\");\n\n oethpAmount = oethp.balanceOf(address(safeContract)) - oethpAmount;\n\n // Rebase to account for any yields from price update\n // and backing asset change from deposit\n vault.rebase();\n\n if (!redeemWithVault) {\n return oethpAmount;\n }\n\n // Redeem for WETH using Vault\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.redeem.selector,\n oethpAmount,\n oethpAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to redeem OETHp\");\n\n return oethpAmount;\n }\n\n /**\n * @dev Deposits wETH into the vault.\n * @param wethAmount Amount of wETH to deposit.\n * @return Amount of OETHp received.\n */\n function depositWETHAndRedeemWOETH(uint256 wethAmount)\n external\n onlyOperator\n returns (uint256)\n {\n return _withdrawWOETH(wethAmount);\n }\n\n /**\n * @dev Deposits wETH into the vault and bridges it to Ethereum.\n * @param wethAmount Amount of wETH to deposit.\n * @param slippageBps Slippage in 10^4 basis points.\n * @return Amount of WOETH received.\n */\n function depositWETHAndBridgeWOETH(uint256 wethAmount, uint256 slippageBps)\n external\n payable\n onlyOperator\n returns (uint256)\n {\n uint256 woethAmount = _withdrawWOETH(wethAmount);\n bridgeWOETHToEthereum(woethAmount, slippageBps);\n return woethAmount;\n }\n\n /**\n * @dev Withdraws wOETH from the bridgedWOETH strategy.\n * @param wethAmount Amount of WETH to use to withdraw.\n * @return Amount of wOETH received.\n */\n function _withdrawWOETH(uint256 wethAmount) internal returns (uint256) {\n // Approve Vault to move WETH\n bool success = safeContract.execTransactionFromModule(\n address(weth),\n 0, // Value\n abi.encodeWithSelector(\n weth.approve.selector,\n address(vault),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve WETH\");\n\n // Mint OETHp with WETH\n success = safeContract.execTransactionFromModule(\n address(vault),\n 0, // Value\n abi.encodeWithSelector(\n vault.mint.selector,\n address(weth),\n wethAmount,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to mint OETHp\");\n\n // Approve bridgedWOETH strategy to move OETHp\n success = safeContract.execTransactionFromModule(\n address(oethp),\n 0, // Value\n abi.encodeWithSelector(\n oethp.approve.selector,\n address(bridgedWOETHStrategy),\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to approve OETHp\");\n\n uint256 woethAmount = bridgedWOETH.balanceOf(address(safeContract));\n\n // Withdraw from bridgedWOETH strategy\n success = safeContract.execTransactionFromModule(\n address(bridgedWOETHStrategy),\n 0, // Value\n abi.encodeWithSelector(\n bridgedWOETHStrategy.withdrawBridgedWOETH.selector,\n wethAmount\n ),\n 0 // Call\n );\n require(success, \"Failed to withdraw bridged WOETH\");\n\n woethAmount =\n bridgedWOETH.balanceOf(address(safeContract)) -\n woethAmount;\n\n return woethAmount;\n }\n}\n" + }, + "contracts/beacon/BeaconConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary BeaconConsolidation {\n /// @notice The address the validator consolidation requests are sent\n /// See https://eips.ethereum.org/EIPS/eip-7251\n address internal constant CONSOLIDATION_REQUEST_ADDRESS =\n 0x0000BBdDc7CE488642fb579F8B00f3a590007251;\n\n function request(bytes calldata source, bytes calldata target)\n internal\n returns (uint256 fee_)\n {\n require(source.length == 48, \"Invalid source byte length\");\n require(target.length == 48, \"Invalid target byte length\");\n\n fee_ = fee();\n\n // Call the Consolidation Request contract with the public keys of the source and target\n // validators packed together.\n // This does not have a function signature, so we use a call\n (bool success, ) = CONSOLIDATION_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(source, target)\n );\n\n require(success, \"Consolidation request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the consolidation request contract\n (bool success, bytes memory result) = CONSOLIDATION_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/beacon/BeaconOracle.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"./BeaconProofsLib.sol\";\nimport { BeaconRoots } from \"./BeaconRoots.sol\";\n\n/// @title Beacon Chain Oracle\n/// @notice An Oracle for mapping execution layer block numbers to beacon chain slots.\n/// @author Origin Protocol Inc\ncontract BeaconOracle {\n /// @notice Maps a block number to slot\n mapping(uint64 => uint64) internal _blockToSlot;\n /// @notice Maps a slot to a number\n mapping(uint64 => uint64) internal _slotToBlock;\n /// @notice Maps a slot to a beacon block root\n mapping(uint64 => bytes32) internal _slotToRoot;\n\n event BlockToSlot(\n bytes32 indexed blockRoot,\n uint64 indexed blockNumber,\n uint64 indexed slot\n );\n\n /// @notice Uses merkle a proof against the beacon block root to link\n /// an execution layer block number to a beacon chain slot.\n /// @param nextBlockTimestamp The timestamp of the block after the one being proven.\n /// @param blockNumber The execution layer block number.\n /// @param slot The beacon chain slot.\n /// @param slotProof The merkle proof witnesses for the slot against the beacon block root.\n /// @param blockProof The merkle proof witnesses for the block number against the beacon block root.\n function verifySlot(\n uint64 nextBlockTimestamp,\n uint64 blockNumber,\n uint64 slot,\n bytes calldata slotProof,\n bytes calldata blockProof\n ) external returns (bytes32 blockRoot) {\n require(_blockToSlot[blockNumber] == 0, \"Block already mapped\");\n\n // Get the parent beacon block root for the given timestamp.\n // This is the beacon block root of the previous slot.\n blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the slot to the beacon block root\n BeaconProofsLib.verifySlot(blockRoot, slot, slotProof);\n\n // Verify the block number to the beacon block root\n BeaconProofsLib.verifyBlockNumber(blockRoot, blockNumber, blockProof);\n\n // Store mappings\n _blockToSlot[blockNumber] = slot;\n _slotToBlock[slot] = blockNumber;\n _slotToRoot[slot] = blockRoot;\n\n emit BlockToSlot(blockRoot, blockNumber, slot);\n }\n\n /// @notice Returns the beacon chain slot for a given execution layer block number.\n function blockToSlot(uint64 blockNumber)\n external\n view\n returns (uint64 slot)\n {\n slot = _blockToSlot[blockNumber];\n\n require(slot != 0, \"Block not mapped\");\n }\n\n /// @notice Returns the execution layer block number for a given beacon chain slot.\n function slotToBlock(uint64 slot)\n external\n view\n returns (uint64 blockNumber)\n {\n blockNumber = _slotToBlock[slot];\n\n require(blockNumber != 0, \"Slot not mapped\");\n }\n\n /// @notice Returns the beacon block root for a given beacon chain slot.\n function slotToRoot(uint64 slot) external view returns (bytes32 blockRoot) {\n blockRoot = _slotToRoot[slot];\n\n require(blockRoot != 0, \"Slot not mapped\");\n }\n\n /// @notice Returns true if an execution layer block number has been mapped to a beacon chain slot.\n function isBlockMapped(uint64 blockNumber) external view returns (bool) {\n return _blockToSlot[blockNumber] != 0;\n }\n\n /// @notice Returns true if a beacon chain slot has been mapped to an execution layer block number.\n function isSlotMapped(uint64 slot) external view returns (bool) {\n return _slotToBlock[slot] != 0;\n }\n}\n" + }, + "contracts/beacon/BeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"./BeaconProofsLib.sol\";\n\ncontract BeaconProofs {\n /// @notice Verifies the validator public key against the beacon block root\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash The beacon chain hash of the validator public key\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view {\n BeaconProofsLib.verifyValidatorPubkey(\n beaconBlockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n withdrawalAddress\n );\n }\n\n /// @notice Verifies the balances container against the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerLeaf The leaf node containing the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view {\n BeaconProofsLib.verifyBalancesContainer(\n beaconBlockRoot,\n balancesContainerLeaf,\n balancesContainerProof\n );\n }\n\n /// @notice Verifies the validator balance against the root of the Balances container\n /// or the beacon block root\n /// @param root The root of the Balances container or the beacon block root\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances\n /// @param balanceProof The merkle proof for the validator balance against the root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @param level The level of the balance proof, either Container or BeaconBlock\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 root,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex,\n BeaconProofsLib.BalanceProofLevel level\n ) external view returns (uint256 validatorBalanceGwei) {\n validatorBalanceGwei = BeaconProofsLib.verifyValidatorBalance(\n root,\n validatorBalanceLeaf,\n balanceProof,\n validatorIndex,\n level\n );\n }\n\n /// @notice Verifies the slot of the first pending deposit against the beacon block root\n /// BeaconBlock.state.PendingDeposits[0].slot\n /// @param beaconBlockRoot The root of the beacon block\n /// @param slot The beacon chain slot to verify\n /// @param firstPendingDepositSlotProof The merkle proof for the first pending deposit's slot\n /// against the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view {\n BeaconProofsLib.verifyFirstPendingDepositSlot(\n beaconBlockRoot,\n slot,\n firstPendingDepositSlotProof\n );\n }\n\n /// @notice Verifies the block number to the the beacon block root\n /// BeaconBlock.body.executionPayload.blockNumber\n /// @param beaconBlockRoot The root of the beacon block\n /// @param blockNumber The execution layer block number to verify\n /// @param blockNumberProof The merkle proof for the block number against the beacon block\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyBlockNumber(\n bytes32 beaconBlockRoot,\n uint256 blockNumber,\n bytes calldata blockNumberProof\n ) external view {\n BeaconProofsLib.verifyBlockNumber(\n beaconBlockRoot,\n blockNumber,\n blockNumberProof\n );\n }\n\n /// @notice Verifies the slot number against the beacon block root.\n /// BeaconBlock.slot\n /// @param beaconBlockRoot The root of the beacon block\n /// @param slot The beacon chain slot to verify\n /// @param slotProof The merkle proof for the slot against the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifySlot(\n bytes32 beaconBlockRoot,\n uint256 slot,\n bytes calldata slotProof\n ) external view {\n BeaconProofsLib.verifySlot(beaconBlockRoot, slot, slotProof);\n }\n}\n" + }, + "contracts/beacon/BeaconProofsLib.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Merkle } from \"./Merkle.sol\";\nimport { Endian } from \"./Endian.sol\";\n\nlibrary BeaconProofsLib {\n // Known generalized indices in the beacon block\n // BeaconBlock.slot\n uint256 internal constant SLOT_GENERALIZED_INDEX = 8;\n // BeaconBlock.state.PendingDeposits[0].slot\n uint256 internal constant FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX =\n 1584842932228;\n // BeaconBlock.body.executionPayload.blockNumber\n uint256 internal constant BLOCK_NUMBER_GENERALIZED_INDEX = 6438;\n // BeaconBlock.state.validators\n uint256 internal constant VALIDATORS_CONTAINER_GENERALIZED_INDEX = 715;\n // BeaconBlock.state.balances\n uint256 internal constant BALANCES_CONTAINER_GENERALIZED_INDEX = 716;\n\n // Beacon Container Tree Heights\n uint256 internal constant BALANCES_HEIGHT = 39;\n uint256 internal constant VALIDATORS_HEIGHT = 41;\n uint256 internal constant VALIDATOR_HEIGHT = 3;\n\n /// @notice Fields in the Validator container for phase 0\n /// See https://ethereum.github.io/consensus-specs/specs/phase0/beacon-chain/#validator\n uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;\n\n enum BalanceProofLevel {\n Container,\n BeaconBlock\n }\n\n /// @notice Verifies the validator public key against the beacon block root\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n /// @param beaconBlockRoot The root of the beacon block\n /// @param pubKeyHash The beacon chain hash of the validator public key\n /// @param validatorPubKeyProof The merkle proof for the validator public key to the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index\n /// @param withdrawalAddress The withdrawal address used in the validator's withdrawal credentials\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) internal view {\n // BeaconBlock.state.validators[validatorIndex].pubkey\n uint256 generalizedIndex = concatGenIndices(\n VALIDATORS_CONTAINER_GENERALIZED_INDEX,\n VALIDATORS_HEIGHT,\n validatorIndex\n );\n generalizedIndex = concatGenIndices(\n generalizedIndex,\n VALIDATOR_HEIGHT,\n VALIDATOR_PUBKEY_INDEX\n );\n\n // Get the withdrawal address from the first witness in the pubkey merkle proof.\n address withdrawalAddressFromProof;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // The first 32 bytes of the proof is the withdrawal credential so load it into memory.\n calldatacopy(0, validatorPubKeyProof.offset, 32)\n // Cast the 32 bytes in memory to an address which is the last 20 bytes.\n withdrawalAddressFromProof := mload(0)\n }\n require(\n withdrawalAddressFromProof == withdrawalAddress,\n \"Invalid withdrawal address\"\n );\n\n require(\n Merkle.verifyInclusionSha256({\n proof: validatorPubKeyProof,\n root: beaconBlockRoot,\n leaf: pubKeyHash,\n index: generalizedIndex\n }),\n \"Invalid validator pubkey proof\"\n );\n }\n\n /// @notice Verifies the balances container against the beacon block root\n /// BeaconBlock.state.balances\n /// @param beaconBlockRoot The root of the beacon block\n /// @param balancesContainerLeaf The leaf node containing the balances container\n /// @param balancesContainerProof The merkle proof for the balances container to the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) internal view {\n // BeaconBlock.state.balances\n require(\n Merkle.verifyInclusionSha256({\n proof: balancesContainerProof,\n root: beaconBlockRoot,\n leaf: balancesContainerLeaf,\n index: BALANCES_CONTAINER_GENERALIZED_INDEX\n }),\n \"Invalid balance container proof\"\n );\n }\n\n /// @notice Verifies the validator balance against the root of the Balances container\n /// or the beacon block root\n /// @param root The root of the Balances container or the beacon block root\n /// @param validatorBalanceLeaf The leaf node containing the validator balance with three other balances\n /// @param balanceProof The merkle proof for the validator balance against the root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n /// @param validatorIndex The validator index to verify the balance for\n /// @param level The level of the balance proof, either Container or BeaconBlock\n /// @return validatorBalanceGwei The balance in Gwei of the validator at the given index\n function verifyValidatorBalance(\n bytes32 root,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex,\n BalanceProofLevel level\n ) internal view returns (uint256 validatorBalanceGwei) {\n // Four balances are stored in each leaf so the validator index is divided by 4\n uint64 balanceIndex = validatorIndex / 4;\n\n // slither-disable-next-line uninitialized-local\n uint256 generalizedIndex;\n if (level == BalanceProofLevel.Container) {\n // Get the index within the balances container, not the Beacon Block\n // BeaconBlock.state.balances[balanceIndex]\n generalizedIndex = concatGenIndices(\n 1,\n BALANCES_HEIGHT,\n balanceIndex\n );\n }\n\n if (level == BalanceProofLevel.BeaconBlock) {\n generalizedIndex = concatGenIndices(\n BALANCES_CONTAINER_GENERALIZED_INDEX,\n BALANCES_HEIGHT,\n balanceIndex\n );\n }\n\n validatorBalanceGwei = balanceAtIndex(\n validatorBalanceLeaf,\n validatorIndex\n );\n\n require(\n Merkle.verifyInclusionSha256({\n proof: balanceProof,\n root: root,\n leaf: validatorBalanceLeaf,\n index: generalizedIndex\n }),\n \"Invalid balance proof\"\n );\n }\n\n /// @notice Verifies the slot of the first pending deposit against the beacon block root\n /// BeaconBlock.state.PendingDeposits[0].slot\n /// @param beaconBlockRoot The root of the beacon block\n /// @param slot The beacon chain slot to verify\n /// @param firstPendingDepositSlotProof The merkle proof for the first pending deposit's slot\n /// against the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) internal view {\n // Convert uint64 slot number to a little endian bytes32\n bytes32 slotLeaf = Endian.toLittleEndianUint64(slot);\n\n require(\n Merkle.verifyInclusionSha256({\n proof: firstPendingDepositSlotProof,\n root: beaconBlockRoot,\n leaf: slotLeaf,\n index: FIRST_PENDING_DEPOSIT_SLOT_GENERALIZED_INDEX\n }),\n \"Invalid pending deposit proof\"\n );\n }\n\n /// @notice Verifies the block number to the the beacon block root\n /// BeaconBlock.body.executionPayload.blockNumber\n /// @param beaconBlockRoot The root of the beacon block\n /// @param blockNumber The execution layer block number to verify\n /// @param blockNumberProof The merkle proof for the block number against the beacon block\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifyBlockNumber(\n bytes32 beaconBlockRoot,\n uint256 blockNumber,\n bytes calldata blockNumberProof\n ) internal view {\n // Convert uint64 block number to a little endian bytes32\n bytes32 blockNumberLeaf = Endian.toLittleEndianUint64(\n uint64(blockNumber)\n );\n require(\n Merkle.verifyInclusionSha256({\n proof: blockNumberProof,\n root: beaconBlockRoot,\n leaf: blockNumberLeaf,\n index: BLOCK_NUMBER_GENERALIZED_INDEX\n }),\n \"Invalid block number proof\"\n );\n }\n\n /// @notice Verifies the slot number against the beacon block root.\n /// BeaconBlock.slot\n /// @param beaconBlockRoot The root of the beacon block\n /// @param slot The beacon chain slot to verify\n /// @param slotProof The merkle proof for the slot against the beacon block root.\n /// This is the witness hashes concatenated together starting from the leaf node.\n function verifySlot(\n bytes32 beaconBlockRoot,\n uint256 slot,\n bytes calldata slotProof\n ) internal view {\n require(\n Merkle.verifyInclusionSha256({\n proof: slotProof,\n root: beaconBlockRoot,\n leaf: Endian.toLittleEndianUint64(uint64(slot)),\n index: SLOT_GENERALIZED_INDEX\n }),\n \"Invalid slot number proof\"\n );\n }\n\n ////////////////////////////////////////////////////\n /// Internal Helper Functions\n ////////////////////////////////////////////////////\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n internal\n pure\n returns (uint256)\n {\n uint256 bitShiftAmount = (validatorIndex % 4) * 64;\n return\n Endian.fromLittleEndianUint64(\n bytes32((uint256(validatorBalanceLeaf) << bitShiftAmount))\n );\n }\n\n /// @notice Concatenates two beacon chain generalized indices into one.\n /// @param genIndex The first generalized index or 1 if calculating for a single container.\n /// @param height The merkle tree height of the second container. eg 39 for balances, 41 for validators.\n /// @param index The index within the second container. eg the validator index.\n /// @return genIndex The concatenated generalized index.\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) internal pure returns (uint256) {\n return (genIndex << height) | index;\n }\n}\n" + }, + "contracts/beacon/BeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary BeaconRoots {\n /// @notice The address of beacon block roots oracle\n /// See https://eips.ethereum.org/EIPS/eip-4788\n address internal constant BEACON_ROOTS_ADDRESS =\n 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;\n\n /// @notice Returns the Beacon Block Root for the previous block.\n /// This comes from the Beacon Roots contract defined in EIP-4788.\n /// This will revert if the block is more than 8,191 blocks old as\n /// that is the size of the beacon root's ring buffer.\n /// @param timestamp The timestamp of the block for which to get the parent root.\n /// @return parentRoot The parent block root for the given timestamp.\n function parentBlockRoot(uint64 timestamp)\n internal\n view\n returns (bytes32 parentRoot)\n {\n // Call the Beacon Block Root Oracle to get the parent block root\n // This does not have a function signature, so we use a staticcall.\n (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(\n abi.encode(timestamp)\n );\n\n require(success && result.length > 0, \"Invalid beacon timestamp\");\n parentRoot = abi.decode(result, (bytes32));\n }\n}\n" + }, + "contracts/beacon/Endian.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nlibrary Endian {\n /**\n * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64\n * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type\n * @return n The big endian-formatted uint64\n * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits),\n * but it is immediately truncated to a uint64 (i.e. 64 bits)\n * through a right-shift/shr operation.\n */\n function fromLittleEndianUint64(bytes32 lenum)\n internal\n pure\n returns (uint64 n)\n {\n // the number needs to be stored in little-endian encoding (ie in bytes 0-8)\n n = uint64(uint256(lenum >> 192));\n // forgefmt: disable-next-item\n return\n (n >> 56) |\n ((0x00FF000000000000 & n) >> 40) |\n ((0x0000FF0000000000 & n) >> 24) |\n ((0x000000FF00000000 & n) >> 8) |\n ((0x00000000FF000000 & n) << 8) |\n ((0x0000000000FF0000 & n) << 24) |\n ((0x000000000000FF00 & n) << 40) |\n ((0x00000000000000FF & n) << 56);\n }\n\n function toLittleEndianUint64(uint64 benum)\n internal\n pure\n returns (bytes32 n)\n {\n // Convert to little-endian by reversing byte order\n uint64 reversed = (benum >> 56) |\n ((0x00FF000000000000 & benum) >> 40) |\n ((0x0000FF0000000000 & benum) >> 24) |\n ((0x000000FF00000000 & benum) >> 8) |\n ((0x00000000FF000000 & benum) << 8) |\n ((0x0000000000FF0000 & benum) << 24) |\n ((0x000000000000FF00 & benum) << 40) |\n ((0x00000000000000FF & benum) << 56);\n\n // Store the little-endian uint64 in the least significant 64 bits of bytes32\n n = bytes32(uint256(reversed));\n // Shift to most significant bits\n n = n << 192;\n }\n}\n" + }, + "contracts/beacon/Merkle.sol": { + "content": "// SPDX-License-Identifier: MIT\n// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev These functions deal with verification of Merkle Tree proofs.\n *\n * The tree and the proofs can be generated using our\n * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].\n * You will find a quickstart guide in the readme.\n *\n * WARNING: You should avoid using leaf values that are 64 bytes long prior to\n * hashing, or use a hash function other than keccak256 for hashing leaves.\n * This is because the concatenation of a sorted pair of internal nodes in\n * the merkle tree could be reinterpreted as a leaf value.\n * OpenZeppelin's JavaScript library generates merkle trees that are safe\n * against this attack out of the box.\n */\nlibrary Merkle {\n error InvalidProofLength();\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function verifyInclusionSha256(\n bytes memory proof,\n bytes32 root,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bool) {\n return processInclusionProofSha256(proof, leaf, index) == root;\n }\n\n /**\n * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up\n * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt\n * hash matches the root of the tree. The tree is built assuming `leaf` is\n * the 0 indexed `index`'th leaf from the bottom left of the tree.\n *\n * _Available since v4.4._\n *\n * Note this is for a Merkle tree using the sha256 hash function\n */\n function processInclusionProofSha256(\n bytes memory proof,\n bytes32 leaf,\n uint256 index\n ) internal view returns (bytes32) {\n require(\n proof.length != 0 && proof.length % 32 == 0,\n InvalidProofLength()\n );\n bytes32[1] memory computedHash = [leaf];\n for (uint256 i = 32; i <= proof.length; i += 32) {\n if (index % 2 == 0) {\n // if ith bit of index is 0, then computedHash is a left sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(computedHash))\n mstore(0x20, mload(add(proof, i)))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n index := div(index, 2)\n }\n } else {\n // if ith bit of index is 1, then computedHash is a right sibling\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0x00, mload(add(proof, i)))\n mstore(0x20, mload(computedHash))\n if iszero(\n staticcall(\n sub(gas(), 2000),\n 2,\n 0x00,\n 0x40,\n computedHash,\n 0x20\n )\n ) {\n revert(0, 0)\n }\n index := div(index, 2)\n }\n }\n }\n return computedHash[0];\n }\n}\n" + }, + "contracts/beacon/PartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary PartialWithdrawal {\n /// @notice The address where the withdrawal request is sent to\n /// See https://eips.ethereum.org/EIPS/eip-7002\n address internal constant WITHDRAWAL_REQUEST_ADDRESS =\n 0x00000961Ef480Eb55e80D19ad83579A64c007002;\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n internal\n returns (uint256 fee_)\n {\n require(validatorPubKey.length == 48, \"Invalid validator byte length\");\n fee_ = fee();\n\n // Call the Withdrawal Request contract with the validator public key\n // and amount to be withdrawn packed together\n\n // This is a general purpose EL to CL request:\n // https://eips.ethereum.org/EIPS/eip-7685\n (bool success, ) = WITHDRAWAL_REQUEST_ADDRESS.call{ value: fee_ }(\n abi.encodePacked(validatorPubKey, amount)\n );\n\n require(success, \"Withdrawal request failed\");\n }\n\n function fee() internal view returns (uint256) {\n // Get fee from the withdrawal request contract\n (bool success, bytes memory result) = WITHDRAWAL_REQUEST_ADDRESS\n .staticcall(\"\");\n\n require(success && result.length > 0, \"Failed to get fee\");\n return abi.decode(result, (uint256));\n }\n}\n" + }, + "contracts/bridges/OmnichainL2Adapter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.0;\n\nimport { MintBurnOFTAdapter } from \"@layerzerolabs/oft-evm/contracts/MintBurnOFTAdapter.sol\";\nimport { IMintableBurnable } from \"@layerzerolabs/oft-evm/contracts/interfaces/IMintableBurnable.sol\";\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\n\n/// NOTE: It's necessary to inherit from Ownable instead of Governable\n/// because OFTCore uses Ownable to manage the governor.\n/// Ownable uses slot 0 for storing the address, whereas Goveranble\n/// stores it in a computed slot.\n\n/// @notice Omnichain uses a deployed ERC-20 token and safeERC20\n/// to interact with the OFTCore contract.\n/// On L2, we follow the mint and burn mechanism.\n/// The adapter should have minter and burner roles.\n\n/// @title Omnichain L2 Adapter\ncontract OmnichainL2Adapter is MintBurnOFTAdapter {\n constructor(\n address _token,\n address _lzEndpoint,\n address _governor\n )\n MintBurnOFTAdapter(\n _token,\n IMintableBurnable(_token),\n _lzEndpoint,\n _governor\n )\n Ownable()\n {\n _transferOwnership(_governor);\n }\n\n /// @inheritdoc MintBurnOFTAdapter\n function _debit(\n address _from,\n uint256 _amountLD,\n uint256 _minAmountLD,\n uint32 _dstEid\n )\n internal\n virtual\n override\n returns (uint256 amountSentLD, uint256 amountReceivedLD)\n {\n (amountSentLD, amountReceivedLD) = _debitView(\n _amountLD,\n _minAmountLD,\n _dstEid\n );\n // Burns tokens from the caller.\n IMintableERC20(address(minterBurner)).burn(_from, amountSentLD);\n }\n\n /// @inheritdoc MintBurnOFTAdapter\n function _credit(\n address _to,\n uint256 _amountLD,\n uint32 /* _srcEid */\n ) internal virtual override returns (uint256 amountReceivedLD) {\n if (_to == address(0x0)) _to = address(0xdead); // _mint(...) does not support address(0x0)\n // Mints the tokens and transfers to the recipient.\n IMintableERC20(address(minterBurner)).mint(_to, _amountLD);\n // In the case of NON-default OFTAdapter, the amountLD MIGHT not be equal to amountReceivedLD.\n return _amountLD;\n }\n}\n\ninterface IMintableERC20 {\n function mint(address to, uint256 value) external;\n\n function burn(address to, uint256 value) external;\n}\n" + }, + "contracts/bridges/OmnichainMainnetAdapter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.0;\n\nimport { OFTAdapter } from \"@layerzerolabs/oft-evm/contracts/OFTAdapter.sol\";\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\n\n/// NOTE: It's necessary to inherit from Ownable instead of Governable\n/// because OFTCore uses Ownable to manage the governor.\n/// Ownable uses slot 0 for storing the address, whereas Goveranble\n/// stores it in a computed slot.\n\n/// @notice Omnichain uses a deployed ERC-20 token and safeERC20\n/// to interact with the OFTCore contract.\n/// On Ethereum, we follow the lock and unlock mechanism.\n\n/// @title Omnichain Mainnet Adapter\ncontract OmnichainMainnetAdapter is OFTAdapter {\n constructor(\n address _token,\n address _lzEndpoint,\n address _governor\n ) OFTAdapter(_token, _lzEndpoint, _governor) Ownable() {\n _transferOwnership(_governor);\n }\n}\n" + }, + "contracts/buyback/AbstractBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { ICVXLocker } from \"../interfaces/ICVXLocker.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\n\nabstract contract AbstractBuyback is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n event SwapRouterUpdated(address indexed _address);\n\n event RewardsSourceUpdated(address indexed _address);\n event TreasuryManagerUpdated(address indexed _address);\n event CVXShareBpsUpdated(uint256 bps);\n\n // Emitted whenever OUSD/OETH is swapped for OGN/CVX or any other token\n event OTokenBuyback(\n address indexed oToken,\n address indexed swappedFor,\n uint256 swapAmountIn,\n uint256 amountOut\n );\n\n // Address of 1-inch Swap Router\n address public swapRouter;\n\n // slither-disable-next-line constable-states\n address private __deprecated_ousd;\n // slither-disable-next-line constable-states\n address private __deprecated_ogv;\n // slither-disable-next-line constable-states\n address private __deprecated_usdt;\n // slither-disable-next-line constable-states\n address private __deprecated_weth9;\n\n // Address that receives OGN after swaps\n address public rewardsSource;\n\n // Address that receives all other tokens after swaps\n address public treasuryManager;\n\n // slither-disable-next-line constable-states\n uint256 private __deprecated_treasuryBps;\n\n address public immutable oToken;\n address public immutable ogn;\n address public immutable cvx;\n address public immutable cvxLocker;\n\n // Amount of `oToken` balance to use for OGN buyback\n uint256 public balanceForOGN;\n\n // Amount of `oToken` balance to use for CVX buyback\n uint256 public balanceForCVX;\n\n // Percentage of `oToken` balance to be used for CVX\n uint256 public cvxShareBps; // 10000 = 100%\n\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) {\n // Make sure nobody owns the implementation contract\n _setGovernor(address(0));\n\n oToken = _oToken;\n ogn = _ogn;\n cvx = _cvx;\n cvxLocker = _cvxLocker;\n }\n\n /**\n * @param _swapRouter Address of Uniswap V3 Router\n * @param _strategistAddr Address of Strategist multi-sig wallet\n * @param _treasuryManagerAddr Address that receives the treasury's share of OUSD\n * @param _rewardsSource Address of RewardsSource contract\n * @param _cvxShareBps Percentage of balance to use for CVX\n */\n function initialize(\n address _swapRouter,\n address _strategistAddr,\n address _treasuryManagerAddr,\n address _rewardsSource,\n uint256 _cvxShareBps\n ) external onlyGovernor initializer {\n _setStrategistAddr(_strategistAddr);\n\n _setSwapRouter(_swapRouter);\n _setRewardsSource(_rewardsSource);\n\n _setTreasuryManager(_treasuryManagerAddr);\n\n _setCVXShareBps(_cvxShareBps);\n }\n\n /**\n * @dev Set address of Uniswap Universal Router for performing liquidation\n * of platform fee tokens. Setting to 0x0 will pause swaps.\n *\n * @param _router Address of the Uniswap Universal router\n */\n function setSwapRouter(address _router) external onlyGovernor {\n _setSwapRouter(_router);\n }\n\n function _setSwapRouter(address _router) internal {\n address oldRouter = swapRouter;\n swapRouter = _router;\n\n if (oldRouter != address(0)) {\n // Remove allowance of old router, if any\n\n if (IERC20(ogn).allowance(address(this), oldRouter) != 0) {\n // slither-disable-next-line unused-return\n IERC20(ogn).safeApprove(oldRouter, 0);\n }\n\n if (IERC20(cvx).allowance(address(this), oldRouter) != 0) {\n // slither-disable-next-line unused-return\n IERC20(cvx).safeApprove(oldRouter, 0);\n }\n }\n\n emit SwapRouterUpdated(_router);\n }\n\n /**\n * @dev Sets the address that receives the OGN buyback rewards\n * @param _address Address\n */\n function setRewardsSource(address _address) external onlyGovernor {\n _setRewardsSource(_address);\n }\n\n function _setRewardsSource(address _address) internal {\n require(_address != address(0), \"Address not set\");\n rewardsSource = _address;\n emit RewardsSourceUpdated(_address);\n }\n\n /**\n * @dev Sets the address that can receive and manage the funds for Treasury\n * @param _address Address\n */\n function setTreasuryManager(address _address) external onlyGovernor {\n _setTreasuryManager(_address);\n }\n\n function _setTreasuryManager(address _address) internal {\n require(_address != address(0), \"Address not set\");\n treasuryManager = _address;\n emit TreasuryManagerUpdated(_address);\n }\n\n /**\n * @dev Sets the percentage of oToken to use for Flywheel tokens\n * @param _bps BPS, 10000 to 100%\n */\n function setCVXShareBps(uint256 _bps) external onlyGovernor {\n _setCVXShareBps(_bps);\n }\n\n function _setCVXShareBps(uint256 _bps) internal {\n require(_bps <= 10000, \"Invalid bps value\");\n cvxShareBps = _bps;\n emit CVXShareBpsUpdated(_bps);\n }\n\n /**\n * @dev Computes the split of oToken balance that can be\n * used for OGN and CVX buybacks.\n */\n function _updateBuybackSplits()\n internal\n returns (uint256 _balanceForOGN, uint256 _balanceForCVX)\n {\n _balanceForOGN = balanceForOGN;\n _balanceForCVX = balanceForCVX;\n\n uint256 totalBalance = IERC20(oToken).balanceOf(address(this));\n uint256 unsplitBalance = totalBalance - _balanceForOGN - _balanceForCVX;\n\n // Check if all balance is accounted for\n if (unsplitBalance != 0) {\n // If not, split unaccounted balance based on `cvxShareBps`\n uint256 addToCVX = (unsplitBalance * cvxShareBps) / 10000;\n _balanceForCVX = _balanceForCVX + addToCVX;\n _balanceForOGN = _balanceForOGN + unsplitBalance - addToCVX;\n\n // Update storage\n balanceForOGN = _balanceForOGN;\n balanceForCVX = _balanceForCVX;\n }\n }\n\n function updateBuybackSplits() external onlyGovernor {\n // slither-disable-next-line unused-return\n _updateBuybackSplits();\n }\n\n function _swapToken(\n address tokenOut,\n uint256 oTokenAmount,\n uint256 minAmountOut,\n bytes calldata swapData\n ) internal returns (uint256 amountOut) {\n require(oTokenAmount > 0, \"Invalid Swap Amount\");\n require(swapRouter != address(0), \"Swap Router not set\");\n require(minAmountOut > 0, \"Invalid minAmount\");\n\n // Transfer OToken to Swapper for swapping\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(oToken).transfer(swapRouter, oTokenAmount);\n\n // Swap\n amountOut = ISwapper(swapRouter).swap(\n oToken,\n tokenOut,\n oTokenAmount,\n minAmountOut,\n swapData\n );\n\n require(amountOut >= minAmountOut, \"Higher Slippage\");\n\n emit OTokenBuyback(oToken, tokenOut, oTokenAmount, amountOut);\n }\n\n /**\n * @dev Swaps `oTokenAmount` to OGN\n * @param oTokenAmount Amount of OUSD/OETH to swap\n * @param minOGN Minimum OGN to receive for oTokenAmount\n * @param swapData 1inch Swap Data\n */\n function swapForOGN(\n uint256 oTokenAmount,\n uint256 minOGN,\n bytes calldata swapData\n ) external onlyGovernorOrStrategist nonReentrant {\n (uint256 _amountForOGN, ) = _updateBuybackSplits();\n require(_amountForOGN >= oTokenAmount, \"Balance underflow\");\n require(rewardsSource != address(0), \"RewardsSource contract not set\");\n\n unchecked {\n // Subtract the amount to swap from net balance\n balanceForOGN = _amountForOGN - oTokenAmount;\n }\n\n uint256 ognReceived = _swapToken(ogn, oTokenAmount, minOGN, swapData);\n\n // Transfer OGN received to RewardsSource contract\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(ogn).transfer(rewardsSource, ognReceived);\n }\n\n /**\n * @dev Swaps `oTokenAmount` to CVX\n * @param oTokenAmount Amount of OUSD/OETH to swap\n * @param minCVX Minimum CVX to receive for oTokenAmount\n * @param swapData 1inch Swap Data\n */\n function swapForCVX(\n uint256 oTokenAmount,\n uint256 minCVX,\n bytes calldata swapData\n ) external onlyGovernorOrStrategist nonReentrant {\n (, uint256 _amountForCVX) = _updateBuybackSplits();\n require(_amountForCVX >= oTokenAmount, \"Balance underflow\");\n\n unchecked {\n // Subtract the amount to swap from net balance\n balanceForCVX = _amountForCVX - oTokenAmount;\n }\n\n uint256 cvxReceived = _swapToken(cvx, oTokenAmount, minCVX, swapData);\n\n // Lock all CVX\n _lockAllCVX(cvxReceived);\n }\n\n /**\n * @dev Locks all CVX held by the contract on behalf of the Treasury Manager\n */\n function lockAllCVX() external onlyGovernorOrStrategist {\n _lockAllCVX(IERC20(cvx).balanceOf(address(this)));\n }\n\n function _lockAllCVX(uint256 cvxAmount) internal {\n require(\n treasuryManager != address(0),\n \"Treasury manager address not set\"\n );\n\n // Lock all available CVX on behalf of `treasuryManager`\n ICVXLocker(cvxLocker).lock(treasuryManager, cvxAmount, 0);\n }\n\n /**\n * @dev Approve CVX Locker to move CVX held by this contract\n */\n function safeApproveAllTokens() external onlyGovernorOrStrategist {\n IERC20(cvx).safeApprove(cvxLocker, type(uint256).max);\n }\n\n /**\n * @notice Owner function to withdraw a specific amount of a token\n * @param token token to be transferered\n * @param amount amount of the token to be transferred\n */\n function transferToken(address token, uint256 amount)\n external\n onlyGovernor\n nonReentrant\n {\n IERC20(token).safeTransfer(_governor(), amount);\n }\n}\n" + }, + "contracts/buyback/ARMBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract ARMBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/buyback/OETHBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract OETHBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/buyback/OUSDBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractBuyback } from \"./AbstractBuyback.sol\";\n\ncontract OUSDBuyback is AbstractBuyback {\n constructor(\n address _oToken,\n address _ogn,\n address _cvx,\n address _cvxLocker\n ) AbstractBuyback(_oToken, _ogn, _cvx, _cvxLocker) {}\n}\n" + }, + "contracts/echidna/Debugger.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nlibrary Debugger {\n event Debug(string debugString);\n event Debug(string description, string data);\n event Debug(string prefix, string description, string data);\n event Debug(string description, bytes32 data);\n event Debug(string prefix, string description, bytes32 data);\n event Debug(string description, uint256 data);\n event Debug(string prefix, string description, uint256 data);\n event Debug(string description, int256 data);\n event Debug(string prefix, string description, int256 data);\n event Debug(string description, address data);\n event Debug(string prefix, string description, address data);\n event Debug(string description, bool data);\n event Debug(string prefix, string description, bool data);\n\n function log(string memory debugString) internal {\n emit Debug(debugString);\n }\n\n function log(string memory description, string memory data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n string memory data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, bytes32 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n bytes32 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, uint256 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n uint256 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, int256 data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n int256 data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, address data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n address data\n ) internal {\n emit Debug(prefix, description, data);\n }\n\n function log(string memory description, bool data) internal {\n emit Debug(description, data);\n }\n\n function log(\n string memory prefix,\n string memory description,\n bool data\n ) internal {\n emit Debug(prefix, description, data);\n }\n}\n" + }, + "contracts/echidna/Echidna.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaTestApproval.sol\";\n\n/**\n * @title Echidna test contract for OUSD\n * @notice Target contract to be tested, containing all mixins\n * @author Rappie\n */\ncontract Echidna is EchidnaTestApproval {\n\n}\n" + }, + "contracts/echidna/EchidnaConfig.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Top-level mixin for configuring the desired fuzzing setup\n * @author Rappie\n */\ncontract EchidnaConfig {\n address internal constant ADDRESS_VAULT = address(0x10000);\n address internal constant ADDRESS_OUTSIDER_USER = address(0x20000);\n\n address internal constant ADDRESS_USER0 = address(0x30000);\n address internal constant ADDRESS_USER1 = address(0x40000);\n\n // Will be set in EchidnaSetup constructor\n address internal ADDRESS_OUTSIDER_CONTRACT;\n address internal ADDRESS_CONTRACT0;\n address internal ADDRESS_CONTRACT1;\n\n // Toggle known issues\n //\n // This can be used to skip tests that are known to fail. This is useful\n // when debugging a specific issue, but should be disabled when running\n // the full test suite.\n //\n // True => skip tests that are known to fail\n // False => run all tests\n //\n bool internal constant TOGGLE_KNOWN_ISSUES = false;\n\n // Toggle known issues within limits\n //\n // Same as TOGGLE_KNOWN_ISSUES, but also skip tests that are known to fail\n // within limits set by the variables below.\n //\n bool internal constant TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS = true;\n\n // Starting balance\n //\n // Gives OUSD a non-zero starting supply, which can be useful to ignore\n // certain edge cases.\n //\n // The starting balance is given to outsider accounts that are not used as\n // accounts while fuzzing.\n //\n bool internal constant TOGGLE_STARTING_BALANCE = true;\n uint256 internal constant STARTING_BALANCE = 1_000_000e18;\n\n // Change supply\n //\n // Set a limit to the amount of change per rebase, which can be useful to\n // ignore certain edge cases.\n //\n // True => limit the amount of change to a percentage of total supply\n // False => no limit\n //\n bool internal constant TOGGLE_CHANGESUPPLY_LIMIT = true;\n uint256 internal constant CHANGESUPPLY_DIVISOR = 10; // 10% of total supply\n\n // Mint limit\n //\n // Set a limit the amount minted per mint, which can be useful to\n // ignore certain edge cases.\n //\n // True => limit the amount of minted tokens\n // False => no limit\n //\n bool internal constant TOGGLE_MINT_LIMIT = true;\n uint256 internal constant MINT_MODULO = 1_000_000_000_000e18;\n\n // Known rounding errors\n uint256 internal constant TRANSFER_ROUNDING_ERROR = 1e18 - 1;\n uint256 internal constant OPT_IN_ROUNDING_ERROR = 1e18 - 1;\n uint256 internal constant MINT_ROUNDING_ERROR = 1e18 - 1;\n\n /**\n * @notice Modifier to skip tests that are known to fail\n * @dev see TOGGLE_KNOWN_ISSUES for more information\n */\n modifier hasKnownIssue() {\n if (TOGGLE_KNOWN_ISSUES) return;\n _;\n }\n\n /**\n * @notice Modifier to skip tests that are known to fail within limits\n * @dev see TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS for more information\n */\n modifier hasKnownIssueWithinLimits() {\n if (TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS) return;\n _;\n }\n\n /**\n * @notice Translate an account ID to an address\n * @param accountId The ID of the account\n * @return account The address of the account\n */\n function getAccount(uint8 accountId)\n internal\n view\n returns (address account)\n {\n accountId = accountId / 64;\n if (accountId == 0) return account = ADDRESS_USER0;\n if (accountId == 1) return account = ADDRESS_USER1;\n if (accountId == 2) return account = ADDRESS_CONTRACT0;\n if (accountId == 3) return account = ADDRESS_CONTRACT1;\n require(false, \"Unknown account ID\");\n }\n}\n" + }, + "contracts/echidna/EchidnaDebug.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport \"./EchidnaHelper.sol\";\nimport \"./Debugger.sol\";\n\nimport \"../token/OUSD.sol\";\n\n/**\n * @title Room for random debugging functions\n * @author Rappie\n */\ncontract EchidnaDebug is EchidnaHelper {\n function debugOUSD() public pure {\n // assert(ousd.balanceOf(ADDRESS_USER0) == 1000);\n // assert(ousd.rebaseState(ADDRESS_USER0) != OUSD.RebaseOptions.OptIn);\n // assert(Address.isContract(ADDRESS_CONTRACT0));\n // Debugger.log(\"nonRebasingSupply\", ousd.nonRebasingSupply());\n // assert(false);\n }\n}\n" + }, + "contracts/echidna/EchidnaHelper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaSetup.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin containing helper functions\n * @author Rappie\n */\ncontract EchidnaHelper is EchidnaSetup {\n /**\n * @notice Mint tokens to an account\n * @param toAcc Account to mint to\n * @param amount Amount to mint\n * @return Amount minted (in case of capped mint with modulo)\n */\n function mint(uint8 toAcc, uint256 amount) public returns (uint256) {\n address to = getAccount(toAcc);\n\n if (TOGGLE_MINT_LIMIT) {\n amount = amount % MINT_MODULO;\n }\n\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(to, amount);\n\n return amount;\n }\n\n /**\n * @notice Burn tokens from an account\n * @param fromAcc Account to burn from\n * @param amount Amount to burn\n */\n function burn(uint8 fromAcc, uint256 amount) public {\n address from = getAccount(fromAcc);\n hevm.prank(ADDRESS_VAULT);\n ousd.burn(from, amount);\n }\n\n /**\n * @notice Change the total supply of OUSD (rebase)\n * @param amount New total supply\n */\n function changeSupply(uint256 amount) public {\n if (TOGGLE_CHANGESUPPLY_LIMIT) {\n amount =\n ousd.totalSupply() +\n (amount % (ousd.totalSupply() / CHANGESUPPLY_DIVISOR));\n }\n\n hevm.prank(ADDRESS_VAULT);\n ousd.changeSupply(amount);\n }\n\n /**\n * @notice Transfer tokens between accounts\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function transfer(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n ousd.transfer(to, amount);\n }\n\n /**\n * @notice Transfer approved tokens between accounts\n * @param authorizedAcc Account that is authorized to transfer\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function transferFrom(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n ousd.transferFrom(from, to, amount);\n }\n\n /**\n * @notice Opt in to rebasing\n * @param targetAcc Account to opt in\n */\n function optIn(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n hevm.prank(target);\n ousd.rebaseOptIn();\n }\n\n /**\n * @notice Opt out of rebasing\n * @param targetAcc Account to opt out\n */\n function optOut(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n hevm.prank(target);\n ousd.rebaseOptOut();\n }\n\n /**\n * @notice Approve an account to spend OUSD\n * @param ownerAcc Account that owns the OUSD\n * @param spenderAcc Account that is approved to spend the OUSD\n * @param amount Amount to approve\n */\n function approve(\n uint8 ownerAcc,\n uint8 spenderAcc,\n uint256 amount\n ) public {\n address owner = getAccount(ownerAcc);\n address spender = getAccount(spenderAcc);\n hevm.prank(owner);\n // slither-disable-next-line unused-return\n ousd.approve(spender, amount);\n }\n\n /**\n * @notice Get the sum of all OUSD balances\n * @return total Total balance\n */\n function getTotalBalance() public view returns (uint256 total) {\n total += ousd.balanceOf(ADDRESS_VAULT);\n total += ousd.balanceOf(ADDRESS_OUTSIDER_USER);\n total += ousd.balanceOf(ADDRESS_OUTSIDER_CONTRACT);\n total += ousd.balanceOf(ADDRESS_USER0);\n total += ousd.balanceOf(ADDRESS_USER1);\n total += ousd.balanceOf(ADDRESS_CONTRACT0);\n total += ousd.balanceOf(ADDRESS_CONTRACT1);\n }\n\n /**\n * @notice Get the sum of all non-rebasing OUSD balances\n * @return total Total balance\n */\n function getTotalNonRebasingBalance() public returns (uint256 total) {\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_VAULT)\n ? ousd.balanceOf(ADDRESS_VAULT)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_OUTSIDER_USER)\n ? ousd.balanceOf(ADDRESS_OUTSIDER_USER)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_OUTSIDER_CONTRACT)\n ? ousd.balanceOf(ADDRESS_OUTSIDER_CONTRACT)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_USER0)\n ? ousd.balanceOf(ADDRESS_USER0)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_USER1)\n ? ousd.balanceOf(ADDRESS_USER1)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_CONTRACT0)\n ? ousd.balanceOf(ADDRESS_CONTRACT0)\n : 0;\n total += ousd._isNonRebasingAccountEchidna(ADDRESS_CONTRACT1)\n ? ousd.balanceOf(ADDRESS_CONTRACT1)\n : 0;\n }\n}\n" + }, + "contracts/echidna/EchidnaSetup.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./IHevm.sol\";\nimport \"./EchidnaConfig.sol\";\nimport \"./OUSDEchidna.sol\";\n\ncontract Dummy {}\n\n/**\n * @title Mixin for setup and deployment\n * @author Rappie\n */\ncontract EchidnaSetup is EchidnaConfig {\n IHevm hevm = IHevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);\n OUSDEchidna ousd = new OUSDEchidna();\n\n /**\n * @notice Deploy the OUSD contract and set up initial state\n */\n constructor() {\n ousd.initialize(ADDRESS_VAULT, 1e18);\n\n // Deploy dummny contracts as users\n Dummy outsider = new Dummy();\n ADDRESS_OUTSIDER_CONTRACT = address(outsider);\n Dummy dummy0 = new Dummy();\n ADDRESS_CONTRACT0 = address(dummy0);\n Dummy dummy1 = new Dummy();\n ADDRESS_CONTRACT1 = address(dummy1);\n\n // Start out with a reasonable amount of OUSD\n if (TOGGLE_STARTING_BALANCE) {\n // Rebasing tokens\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(ADDRESS_OUTSIDER_USER, STARTING_BALANCE / 2);\n\n // Non-rebasing tokens\n hevm.prank(ADDRESS_VAULT);\n ousd.mint(ADDRESS_OUTSIDER_CONTRACT, STARTING_BALANCE / 2);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestAccounting.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestSupply.sol\";\n\n/**\n * @title Mixin for testing accounting functions\n * @author Rappie\n */\ncontract EchidnaTestAccounting is EchidnaTestSupply {\n /**\n * @notice After opting in, balance should not increase. (Ok to lose rounding funds doing this)\n * @param targetAcc Account to opt in\n */\n function testOptInBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optIn(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter <= balanceBefore);\n }\n\n /**\n * @notice After opting out, balance should remain the same\n * @param targetAcc Account to opt out\n */\n function testOptOutBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optOut(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Account balance should remain the same after opting in minus rounding error\n * @param targetAcc Account to opt in\n */\n function testOptInBalanceRounding(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n optIn(targetAcc);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n int256 delta = int256(balanceAfter) - int256(balanceBefore);\n Debugger.log(\"delta\", delta);\n\n // slither-disable-next-line tautology\n assert(-1 * delta >= 0);\n assert(-1 * delta <= int256(OPT_IN_ROUNDING_ERROR));\n }\n\n /**\n * @notice After opting in, total supply should remain the same\n * @param targetAcc Account to opt in\n */\n function testOptInTotalSupply(uint8 targetAcc) public {\n uint256 totalSupplyBefore = ousd.totalSupply();\n optIn(targetAcc);\n uint256 totalSupplyAfter = ousd.totalSupply();\n\n assert(totalSupplyAfter == totalSupplyBefore);\n }\n\n /**\n * @notice After opting out, total supply should remain the same\n * @param targetAcc Account to opt out\n */\n function testOptOutTotalSupply(uint8 targetAcc) public {\n uint256 totalSupplyBefore = ousd.totalSupply();\n optOut(targetAcc);\n uint256 totalSupplyAfter = ousd.totalSupply();\n\n assert(totalSupplyAfter == totalSupplyBefore);\n }\n\n /**\n * @notice Account balance should remain the same when a smart contract auto converts\n * @param targetAcc Account to auto convert\n */\n function testAutoConvertBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n // slither-disable-next-line unused-return\n ousd._isNonRebasingAccountEchidna(target);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice The `balanceOf` function should never revert\n * @param targetAcc Account to check balance of\n */\n function testBalanceOfShouldNotRevert(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n // slither-disable-next-line unused-return\n try ousd.balanceOf(target) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestApproval.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaTestMintBurn.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin for testing approval related functions\n * @author Rappie\n */\ncontract EchidnaTestApproval is EchidnaTestMintBurn {\n /**\n * @notice Performing `transferFrom` with an amount inside the allowance should not revert\n * @param authorizedAcc The account that is authorized to transfer\n * @param fromAcc The account that is transferring\n * @param toAcc The account that is receiving\n * @param amount The amount to transfer\n */\n function testTransferFromShouldNotRevert(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount <= ousd.balanceOf(from));\n require(amount <= ousd.allowance(from, authorized));\n\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n try ousd.transferFrom(from, to, amount) {\n // pass\n } catch {\n assert(false);\n }\n }\n\n /**\n * @notice Performing `transferFrom` with an amount outside the allowance should revert\n * @param authorizedAcc The account that is authorized to transfer\n * @param fromAcc The account that is transferring\n * @param toAcc The account that is receiving\n * @param amount The amount to transfer\n */\n function testTransferFromShouldRevert(\n uint8 authorizedAcc,\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address authorized = getAccount(authorizedAcc);\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount > 0);\n require(\n !(amount <= ousd.balanceOf(from) &&\n amount <= ousd.allowance(from, authorized))\n );\n\n hevm.prank(authorized);\n // slither-disable-next-line unchecked-transfer\n try ousd.transferFrom(from, to, amount) {\n assert(false);\n } catch {\n // pass\n }\n }\n\n /**\n * @notice Approving an amount should update the allowance and overwrite any previous allowance\n * @param ownerAcc The account that is approving\n * @param spenderAcc The account that is being approved\n * @param amount The amount to approve\n */\n function testApprove(\n uint8 ownerAcc,\n uint8 spenderAcc,\n uint256 amount\n ) public {\n address owner = getAccount(ownerAcc);\n address spender = getAccount(spenderAcc);\n\n approve(ownerAcc, spenderAcc, amount);\n uint256 allowanceAfter1 = ousd.allowance(owner, spender);\n\n assert(allowanceAfter1 == amount);\n\n approve(ownerAcc, spenderAcc, amount / 2);\n uint256 allowanceAfter2 = ousd.allowance(owner, spender);\n\n assert(allowanceAfter2 == amount / 2);\n }\n}\n" + }, + "contracts/echidna/EchidnaTestMintBurn.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestAccounting.sol\";\n\n/**\n * @title Mixin for testing Mint and Burn functions\n * @author Rappie\n */\ncontract EchidnaTestMintBurn is EchidnaTestAccounting {\n /**\n * @notice Minting 0 tokens should not affect account balance\n * @param targetAcc Account to mint to\n */\n function testMintZeroBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n mint(targetAcc, 0);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Burning 0 tokens should not affect account balance\n * @param targetAcc Account to burn from\n */\n function testBurnZeroBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n burn(targetAcc, 0);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceAfter == balanceBefore);\n }\n\n /**\n * @notice Minting tokens must increase the account balance by at least amount\n * @param targetAcc Account to mint to\n * @param amount Amount to mint\n * @custom:error testMintBalance(uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * testMintBalance(0,1)\n * Event sequence:\n * Debug(«balanceBefore», 0)\n * Debug(«balanceAfter», 0)\n */\n function testMintBalance(uint8 targetAcc, uint256 amount)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n uint256 amountMinted = mint(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n Debugger.log(\"amountMinted\", amountMinted);\n Debugger.log(\"balanceBefore\", balanceBefore);\n Debugger.log(\"balanceAfter\", balanceAfter);\n\n assert(balanceAfter >= balanceBefore + amountMinted);\n }\n\n /**\n * @notice Burning tokens must decrease the account balance by at least amount\n * @param targetAcc Account to burn from\n * @param amount Amount to burn\n * @custom:error testBurnBalance(uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * mint(0,3)\n * testBurnBalance(0,1)\n * Event sequence:\n * Debug(«balanceBefore», 2)\n * Debug(«balanceAfter», 2)\n */\n function testBurnBalance(uint8 targetAcc, uint256 amount)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n burn(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n Debugger.log(\"balanceBefore\", balanceBefore);\n Debugger.log(\"balanceAfter\", balanceAfter);\n\n assert(balanceAfter <= balanceBefore - amount);\n }\n\n /**\n * @notice Minting tokens should not increase the account balance by less than rounding error above amount\n * @param targetAcc Account to mint to\n * @param amount Amount to mint\n */\n function testMintBalanceRounding(uint8 targetAcc, uint256 amount) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n uint256 amountMinted = mint(targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n int256 delta = int256(balanceAfter) - int256(balanceBefore);\n\n // delta == amount, if no error\n // delta < amount, if too little is minted\n // delta > amount, if too much is minted\n int256 error = int256(amountMinted) - delta;\n\n assert(error >= 0);\n assert(error <= int256(MINT_ROUNDING_ERROR));\n }\n\n /**\n * @notice A burn of an account balance must result in a zero balance\n * @param targetAcc Account to burn from\n */\n function testBurnAllBalanceToZero(uint8 targetAcc) public hasKnownIssue {\n address target = getAccount(targetAcc);\n\n burn(targetAcc, ousd.balanceOf(target));\n assert(ousd.balanceOf(target) == 0);\n }\n\n /**\n * @notice You should always be able to burn an account's balance\n * @param targetAcc Account to burn from\n */\n function testBurnAllBalanceShouldNotRevert(uint8 targetAcc)\n public\n hasKnownIssue\n {\n address target = getAccount(targetAcc);\n uint256 balance = ousd.balanceOf(target);\n\n hevm.prank(ADDRESS_VAULT);\n try ousd.burn(target, balance) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n}\n" + }, + "contracts/echidna/EchidnaTestSupply.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./EchidnaTestTransfer.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n/**\n * @title Mixin for testing supply related functions\n * @author Rappie\n */\ncontract EchidnaTestSupply is EchidnaTestTransfer {\n using StableMath for uint256;\n\n uint256 prevRebasingCreditsPerToken = type(uint256).max;\n\n /**\n * @notice After a `changeSupply`, the total supply should exactly\n * match the target total supply. (This is needed to ensure successive\n * rebases are correct).\n * @param supply New total supply\n * @custom:error testChangeSupply(uint256): failed!💥\n * Call sequence:\n * testChangeSupply(1044505275072865171609)\n * Event sequence:\n * TotalSupplyUpdatedHighres(1044505275072865171610, 1000000000000000000000000, 957391048054055578595)\n */\n function testChangeSupply(uint256 supply)\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n hevm.prank(ADDRESS_VAULT);\n ousd.changeSupply(supply);\n\n assert(ousd.totalSupply() == supply);\n }\n\n /**\n * @notice The total supply must not be less than the sum of account balances.\n * (The difference will go into future rebases)\n * @custom:error testTotalSupplyLessThanTotalBalance(): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(1)\n * optOut(64)\n * transfer(0,64,1)\n * testTotalSupplyLessThanTotalBalance()\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000001000001)\n * Debug(«totalBalance», 1000000000000000001000002)\n */\n function testTotalSupplyLessThanTotalBalance()\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n uint256 totalSupply = ousd.totalSupply();\n uint256 totalBalance = getTotalBalance();\n\n Debugger.log(\"totalSupply\", totalSupply);\n Debugger.log(\"totalBalance\", totalBalance);\n\n assert(totalSupply >= totalBalance);\n }\n\n /**\n * @notice Non-rebasing supply should not be larger than total supply\n * @custom:error testNonRebasingSupplyVsTotalSupply(): failed!💥\n * Call sequence:\n * mint(0,2)\n * changeSupply(3)\n * burn(0,1)\n * optOut(0)\n * testNonRebasingSupplyVsTotalSupply()\n */\n function testNonRebasingSupplyVsTotalSupply() public hasKnownIssue {\n uint256 nonRebasingSupply = ousd.nonRebasingSupply();\n uint256 totalSupply = ousd.totalSupply();\n\n assert(nonRebasingSupply <= totalSupply);\n }\n\n /**\n * @notice Global `rebasingCreditsPerToken` should never increase\n * @custom:error testRebasingCreditsPerTokenNotIncreased(): failed!💥\n * Call sequence:\n * testRebasingCreditsPerTokenNotIncreased()\n * changeSupply(1)\n * testRebasingCreditsPerTokenNotIncreased()\n */\n function testRebasingCreditsPerTokenNotIncreased() public hasKnownIssue {\n uint256 curRebasingCreditsPerToken = ousd\n .rebasingCreditsPerTokenHighres();\n\n Debugger.log(\n \"prevRebasingCreditsPerToken\",\n prevRebasingCreditsPerToken\n );\n Debugger.log(\"curRebasingCreditsPerToken\", curRebasingCreditsPerToken);\n\n assert(curRebasingCreditsPerToken <= prevRebasingCreditsPerToken);\n\n prevRebasingCreditsPerToken = curRebasingCreditsPerToken;\n }\n\n /**\n * @notice The rebasing credits per token ratio must greater than zero\n */\n function testRebasingCreditsPerTokenAboveZero() public {\n assert(ousd.rebasingCreditsPerTokenHighres() > 0);\n }\n\n /**\n * @notice The sum of all non-rebasing balances should not be larger than\n * non-rebasing supply\n * @custom:error testTotalNonRebasingSupplyLessThanTotalBalance(): failed!💥\n * Call sequence\n * mint(0,2)\n * changeSupply(1)\n * optOut(0)\n * burn(0,1)\n * testTotalNonRebasingSupplyLessThanTotalBalance()\n * Event sequence:\n * Debug(«totalNonRebasingSupply», 500000000000000000000001)\n * Debug(«totalNonRebasingBalance», 500000000000000000000002)\n */\n function testTotalNonRebasingSupplyLessThanTotalBalance()\n public\n hasKnownIssue\n hasKnownIssueWithinLimits\n {\n uint256 totalNonRebasingSupply = ousd.nonRebasingSupply();\n uint256 totalNonRebasingBalance = getTotalNonRebasingBalance();\n\n Debugger.log(\"totalNonRebasingSupply\", totalNonRebasingSupply);\n Debugger.log(\"totalNonRebasingBalance\", totalNonRebasingBalance);\n\n assert(totalNonRebasingSupply >= totalNonRebasingBalance);\n }\n\n /**\n * @notice An accounts credits / credits per token should not be larger it's balance\n * @param targetAcc The account to check\n */\n function testCreditsPerTokenVsBalance(uint8 targetAcc) public {\n address target = getAccount(targetAcc);\n\n (uint256 credits, uint256 creditsPerToken, ) = ousd\n .creditsBalanceOfHighres(target);\n uint256 expectedBalance = credits.divPrecisely(creditsPerToken);\n\n uint256 balance = ousd.balanceOf(target);\n\n Debugger.log(\"credits\", credits);\n Debugger.log(\"creditsPerToken\", creditsPerToken);\n Debugger.log(\"expectedBalance\", expectedBalance);\n Debugger.log(\"balance\", balance);\n\n assert(expectedBalance == balance);\n }\n}\n" + }, + "contracts/echidna/EchidnaTestTransfer.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./EchidnaDebug.sol\";\nimport \"./Debugger.sol\";\n\n/**\n * @title Mixin for testing transfer related functions\n * @author Rappie\n */\ncontract EchidnaTestTransfer is EchidnaDebug {\n /**\n * @notice The receiving account's balance after a transfer must not increase by\n * less than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferBalanceReceivedLess(uint8,uint8,uint256): failed!💥\n * Call sequence:\n * changeSupply(1)\n * mint(64,2)\n * testTransferBalanceReceivedLess(64,0,1)\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000000500002)\n * Debug(«toBalBefore», 0)\n * Debug(«toBalAfter», 0)\n */\n function testTransferBalanceReceivedLess(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue hasKnownIssueWithinLimits {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n\n assert(toBalAfter >= toBalBefore + amount);\n }\n\n /**\n * @notice The receiving account's balance after a transfer must not\n * increase by more than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceReceivedMore(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n\n assert(toBalAfter <= toBalBefore + amount);\n }\n\n /**\n * @notice The sending account's balance after a transfer must not\n * decrease by less than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferBalanceSentLess(uint8,uint8,uint256): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(1)\n * testTransferBalanceSentLess(0,64,1)\n * Event sequence:\n * Debug(«totalSupply», 1000000000000000000500001)\n * Debug(«fromBalBefore», 1)\n * Debug(«fromBalAfter», 1)\n */\n function testTransferBalanceSentLess(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue hasKnownIssueWithinLimits {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n\n assert(fromBalAfter <= fromBalBefore - amount);\n }\n\n /**\n * @notice The sending account's balance after a transfer must not\n * decrease by more than the amount transferred\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceSentMore(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n\n assert(fromBalAfter >= fromBalBefore - amount);\n }\n\n /**\n * @notice The receiving account's balance after a transfer must not\n * increase by less than the amount transferred (minus rounding error)\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceReceivedLessRounding(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 toBalBefore = ousd.balanceOf(to);\n transfer(fromAcc, toAcc, amount);\n uint256 toBalAfter = ousd.balanceOf(to);\n\n int256 toDelta = int256(toBalAfter) - int256(toBalBefore);\n\n // delta == amount, if no error\n // delta < amount, if too little is sent\n // delta > amount, if too much is sent\n int256 error = int256(amount) - toDelta;\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"toBalBefore\", toBalBefore);\n Debugger.log(\"toBalAfter\", toBalAfter);\n Debugger.log(\"toDelta\", toDelta);\n Debugger.log(\"error\", error);\n\n assert(error >= 0);\n assert(error <= int256(TRANSFER_ROUNDING_ERROR));\n }\n\n /**\n * @notice The sending account's balance after a transfer must\n * not decrease by less than the amount transferred (minus rounding error)\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferBalanceSentLessRounding(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(from != to);\n\n uint256 fromBalBefore = ousd.balanceOf(from);\n transfer(fromAcc, toAcc, amount);\n uint256 fromBalAfter = ousd.balanceOf(from);\n\n int256 fromDelta = int256(fromBalAfter) - int256(fromBalBefore);\n\n // delta == -amount, if no error\n // delta < -amount, if too much is sent\n // delta > -amount, if too little is sent\n int256 error = int256(amount) + fromDelta;\n\n Debugger.log(\"totalSupply\", ousd.totalSupply());\n Debugger.log(\"fromBalBefore\", fromBalBefore);\n Debugger.log(\"fromBalAfter\", fromBalAfter);\n Debugger.log(\"fromDelta\", fromDelta);\n Debugger.log(\"error\", error);\n\n assert(error >= 0);\n assert(error <= int256(TRANSFER_ROUNDING_ERROR));\n }\n\n /**\n * @notice An account should always be able to successfully transfer\n * an amount within its balance.\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n * @custom:error testTransferWithinBalanceDoesNotRevert(uint8,uint8,uint8): failed!💥\n * Call sequence:\n * mint(0,1)\n * changeSupply(3)\n * optOut(0)\n * testTransferWithinBalanceDoesNotRevert(0,128,2)\n * optIn(0)\n * testTransferWithinBalanceDoesNotRevert(128,0,1)\n * Event sequence:\n * error Revert Panic(17): SafeMath over-/under-flows\n */\n function testTransferWithinBalanceDoesNotRevert(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public hasKnownIssue {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n require(amount > 0);\n amount = amount % ousd.balanceOf(from);\n\n Debugger.log(\"Total supply\", ousd.totalSupply());\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(to, amount) {\n assert(true);\n } catch {\n assert(false);\n }\n }\n\n /**\n * @notice An account should never be able to successfully transfer\n * an amount greater than their balance.\n * @param fromAcc Account to transfer from\n * @param toAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferExceedingBalanceReverts(\n uint8 fromAcc,\n uint8 toAcc,\n uint256 amount\n ) public {\n address from = getAccount(fromAcc);\n address to = getAccount(toAcc);\n\n amount = ousd.balanceOf(from) + 1 + amount;\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(to, amount) {\n assert(false);\n } catch {\n assert(true);\n }\n }\n\n /**\n * @notice A transfer to the same account should not change that account's balance\n * @param targetAcc Account to transfer to\n * @param amount Amount to transfer\n */\n function testTransferSelf(uint8 targetAcc, uint256 amount) public {\n address target = getAccount(targetAcc);\n\n uint256 balanceBefore = ousd.balanceOf(target);\n transfer(targetAcc, targetAcc, amount);\n uint256 balanceAfter = ousd.balanceOf(target);\n\n assert(balanceBefore == balanceAfter);\n }\n\n /**\n * @notice Transfers to the zero account revert\n * @param fromAcc Account to transfer from\n * @param amount Amount to transfer\n */\n function testTransferToZeroAddress(uint8 fromAcc, uint256 amount) public {\n address from = getAccount(fromAcc);\n\n hevm.prank(from);\n // slither-disable-next-line unchecked-transfer\n try ousd.transfer(address(0), amount) {\n assert(false);\n } catch {\n assert(true);\n }\n }\n}\n" + }, + "contracts/echidna/IHevm.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// https://github.com/ethereum/hevm/blob/main/doc/src/controlling-the-unit-testing-environment.md#cheat-codes\n\ninterface IHevm {\n function warp(uint256 x) external;\n\n function roll(uint256 x) external;\n\n function store(\n address c,\n bytes32 loc,\n bytes32 val\n ) external;\n\n function load(address c, bytes32 loc) external returns (bytes32 val);\n\n function sign(uint256 sk, bytes32 digest)\n external\n returns (\n uint8 v,\n bytes32 r,\n bytes32 s\n );\n\n function addr(uint256 sk) external returns (address addr);\n\n function ffi(string[] calldata) external returns (bytes memory);\n\n function prank(address sender) external;\n}\n" + }, + "contracts/echidna/OUSDEchidna.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../token/OUSD.sol\";\n\ncontract OUSDEchidna is OUSD {\n constructor() OUSD() {}\n\n function _isNonRebasingAccountEchidna(address _account)\n public\n returns (bool)\n {\n _autoMigrate(_account);\n return alternativeCreditsPerToken[_account] > 0;\n }\n}\n" + }, + "contracts/governance/Governable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base for contracts that are managed by the Origin Protocol's Governor.\n * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change\n * from owner to governor and renounce methods removed. Does not use\n * Context.sol like Ownable.sol does for simplification.\n * @author Origin Protocol Inc\n */\nabstract contract Governable {\n // Storage position of the owner and pendingOwner of the contract\n // keccak256(\"OUSD.governor\");\n bytes32 private constant governorPosition =\n 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a;\n\n // keccak256(\"OUSD.pending.governor\");\n bytes32 private constant pendingGovernorPosition =\n 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db;\n\n // keccak256(\"OUSD.reentry.status\");\n bytes32 private constant reentryStatusPosition =\n 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535;\n\n // See OpenZeppelin ReentrancyGuard implementation\n uint256 constant _NOT_ENTERED = 1;\n uint256 constant _ENTERED = 2;\n\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n\n /**\n * @notice Returns the address of the current Governor.\n */\n function governor() public view returns (address) {\n return _governor();\n }\n\n /**\n * @dev Returns the address of the current Governor.\n */\n function _governor() internal view returns (address governorOut) {\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n governorOut := sload(position)\n }\n }\n\n /**\n * @dev Returns the address of the pending Governor.\n */\n function _pendingGovernor()\n internal\n view\n returns (address pendingGovernor)\n {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n pendingGovernor := sload(position)\n }\n }\n\n /**\n * @dev Throws if called by any account other than the Governor.\n */\n modifier onlyGovernor() {\n require(isGovernor(), \"Caller is not the Governor\");\n _;\n }\n\n /**\n * @notice Returns true if the caller is the current Governor.\n */\n function isGovernor() public view returns (bool) {\n return msg.sender == _governor();\n }\n\n function _setGovernor(address newGovernor) internal {\n emit GovernorshipTransferred(_governor(), newGovernor);\n\n bytes32 position = governorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and make it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n bytes32 position = reentryStatusPosition;\n uint256 _reentry_status;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _reentry_status := sload(position)\n }\n\n // On the first call to nonReentrant, _notEntered will be true\n require(_reentry_status != _ENTERED, \"Reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _ENTERED)\n }\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, _NOT_ENTERED)\n }\n }\n\n function _setPendingGovernor(address newGovernor) internal {\n bytes32 position = pendingGovernorPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newGovernor)\n }\n }\n\n /**\n * @notice Transfers Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the current Governor. Must be claimed for this to complete\n * @param _newGovernor Address of the new Governor\n */\n function transferGovernance(address _newGovernor) external onlyGovernor {\n _setPendingGovernor(_newGovernor);\n emit PendingGovernorshipTransfer(_governor(), _newGovernor);\n }\n\n /**\n * @notice Claim Governance of the contract to a new account (`newGovernor`).\n * Can only be called by the new Governor.\n */\n function claimGovernance() external {\n require(\n msg.sender == _pendingGovernor(),\n \"Only the pending Governor can complete the claim\"\n );\n _changeGovernor(msg.sender);\n }\n\n /**\n * @dev Change Governance of the contract to a new account (`newGovernor`).\n * @param _newGovernor Address of the new Governor\n */\n function _changeGovernor(address _newGovernor) internal {\n require(_newGovernor != address(0), \"New Governor is address(0)\");\n _setGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/governance/InitializableGovernable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD InitializableGovernable Contract\n * @author Origin Protocol Inc\n */\nimport { Initializable } from \"../utils/Initializable.sol\";\n\nimport { Governable } from \"./Governable.sol\";\n\ncontract InitializableGovernable is Governable, Initializable {\n function _initialize(address _newGovernor) internal {\n _changeGovernor(_newGovernor);\n }\n}\n" + }, + "contracts/governance/Strategizable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"./Governable.sol\";\n\ncontract Strategizable is Governable {\n event StrategistUpdated(address _address);\n\n // Address of strategist\n address public strategistAddr;\n\n // For future use\n uint256[50] private __gap;\n\n /**\n * @dev Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @dev Set address of Strategist\n * @param _address Address of Strategist\n */\n function setStrategistAddr(address _address) external onlyGovernor {\n _setStrategistAddr(_address);\n }\n\n /**\n * @dev Set address of Strategist\n * @param _address Address of Strategist\n */\n function _setStrategistAddr(address _address) internal {\n strategistAddr = _address;\n emit StrategistUpdated(_address);\n }\n}\n" + }, + "contracts/harvest/AbstractHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IUniswapV2Router } from \"../interfaces/uniswap/IUniswapV2Router02.sol\";\nimport { IUniswapV3Router } from \"../interfaces/uniswap/IUniswapV3Router.sol\";\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { ICurvePool } from \"../strategies/ICurvePool.sol\";\nimport \"../utils/Helpers.sol\";\n\nabstract contract AbstractHarvester is Governable {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n using StableMath for uint256;\n\n enum SwapPlatform {\n UniswapV2Compatible,\n UniswapV3,\n Balancer,\n Curve\n }\n\n event SupportedStrategyUpdate(address strategyAddress, bool isSupported);\n event RewardTokenConfigUpdated(\n address tokenAddress,\n uint16 allowedSlippageBps,\n uint16 harvestRewardBps,\n SwapPlatform swapPlatform,\n address swapPlatformAddr,\n bytes swapData,\n uint256 liquidationLimit,\n bool doSwapRewardToken\n );\n event RewardTokenSwapped(\n address indexed rewardToken,\n address indexed swappedInto,\n SwapPlatform swapPlatform,\n uint256 amountIn,\n uint256 amountOut\n );\n event RewardProceedsTransferred(\n address indexed token,\n address farmer,\n uint256 protcolYield,\n uint256 farmerFee\n );\n event RewardProceedsAddressChanged(address newProceedsAddress);\n\n error EmptyAddress();\n error InvalidSlippageBps();\n error InvalidHarvestRewardBps();\n\n error InvalidSwapPlatform(SwapPlatform swapPlatform);\n\n error InvalidUniswapV2PathLength();\n error InvalidTokenInSwapPath(address token);\n error EmptyBalancerPoolId();\n error InvalidCurvePoolAssetIndex(address token);\n\n error UnsupportedStrategy(address strategyAddress);\n\n error SlippageError(uint256 actualBalance, uint256 minExpected);\n error BalanceMismatchAfterSwap(uint256 actualBalance, uint256 minExpected);\n\n // Configuration properties for harvesting logic of reward tokens\n struct RewardTokenConfig {\n // Max allowed slippage when swapping reward token for a stablecoin denominated in basis points.\n uint16 allowedSlippageBps;\n // Reward when calling a harvest function denominated in basis points.\n uint16 harvestRewardBps;\n // Address of compatible exchange protocol (Uniswap V2/V3, SushiSwap, Balancer and Curve).\n address swapPlatformAddr;\n /* When true the reward token is being swapped. In a need of (temporarily) disabling the swapping of\n * a reward token this needs to be set to false.\n */\n bool doSwapRewardToken;\n // Platform to use for Swapping\n SwapPlatform swapPlatform;\n /* How much token can be sold per one harvest call. If the balance of rewards tokens\n * exceeds that limit multiple harvest calls are required to harvest all of the tokens.\n * Set it to MAX_INT to effectively disable the limit.\n */\n uint256 liquidationLimit;\n }\n\n mapping(address => RewardTokenConfig) public rewardTokenConfigs;\n mapping(address => bool) public supportedStrategies;\n\n address public immutable vaultAddress;\n\n /**\n * Address receiving rewards proceeds. Initially the Vault contract later will possibly\n * be replaced by another contract that eases out rewards distribution.\n **/\n address public rewardProceedsAddress;\n\n /**\n * All tokens are swapped to this token before it gets transferred\n * to the `rewardProceedsAddress`. USDT for OUSD and WETH for OETH.\n **/\n address public immutable baseTokenAddress;\n // Cached decimals for `baseTokenAddress`\n uint256 public immutable baseTokenDecimals;\n\n // Uniswap V2 path for reward tokens using Uniswap V2 Router\n mapping(address => address[]) public uniswapV2Path;\n // Uniswap V3 path for reward tokens using Uniswap V3 Router\n mapping(address => bytes) public uniswapV3Path;\n // Pool ID to use for reward tokens on Balancer\n mapping(address => bytes32) public balancerPoolId;\n\n struct CurvePoolIndices {\n // Casted into uint128 and stored in a struct to save gas\n uint128 rewardTokenIndex;\n uint128 baseTokenIndex;\n }\n // Packed indices of assets on the Curve pool\n mapping(address => CurvePoolIndices) public curvePoolIndices;\n\n constructor(address _vaultAddress, address _baseTokenAddress) {\n require(_vaultAddress != address(0));\n require(_baseTokenAddress != address(0));\n\n vaultAddress = _vaultAddress;\n baseTokenAddress = _baseTokenAddress;\n\n // Cache decimals as well\n baseTokenDecimals = Helpers.getDecimals(_baseTokenAddress);\n }\n\n /***************************************\n Configuration\n ****************************************/\n\n /**\n * Set the Address receiving rewards proceeds.\n * @param _rewardProceedsAddress Address of the reward token\n */\n function setRewardProceedsAddress(address _rewardProceedsAddress)\n external\n onlyGovernor\n {\n if (_rewardProceedsAddress == address(0)) {\n revert EmptyAddress();\n }\n\n rewardProceedsAddress = _rewardProceedsAddress;\n emit RewardProceedsAddressChanged(_rewardProceedsAddress);\n }\n\n /**\n * @dev Add/update a reward token configuration that holds harvesting config variables\n * @param _tokenAddress Address of the reward token\n * @param tokenConfig.allowedSlippageBps uint16 maximum allowed slippage denominated in basis points.\n * Example: 300 == 3% slippage\n * @param tokenConfig.harvestRewardBps uint16 amount of reward tokens the caller of the function is rewarded.\n * Example: 100 == 1%\n * @param tokenConfig.swapPlatformAddr Address Address of a UniswapV2 compatible contract to perform\n * the exchange from reward tokens to stablecoin (currently hard-coded to USDT)\n * @param tokenConfig.liquidationLimit uint256 Maximum amount of token to be sold per one swap function call.\n * When value is 0 there is no limit.\n * @param tokenConfig.doSwapRewardToken bool Disables swapping of the token when set to true,\n * does not cause it to revert though.\n * @param tokenConfig.swapPlatform SwapPlatform to use for Swapping\n * @param swapData Additional data required for swapping\n */\n function setRewardTokenConfig(\n address _tokenAddress,\n RewardTokenConfig calldata tokenConfig,\n bytes calldata swapData\n ) external onlyGovernor {\n if (tokenConfig.allowedSlippageBps > 1000) {\n revert InvalidSlippageBps();\n }\n\n if (tokenConfig.harvestRewardBps > 1000) {\n revert InvalidHarvestRewardBps();\n }\n\n address newRouterAddress = tokenConfig.swapPlatformAddr;\n if (newRouterAddress == address(0)) {\n // Swap router address should be non zero address\n revert EmptyAddress();\n }\n\n address oldRouterAddress = rewardTokenConfigs[_tokenAddress]\n .swapPlatformAddr;\n rewardTokenConfigs[_tokenAddress] = tokenConfig;\n\n // Revert if feed does not exist\n // slither-disable-next-line unused-return\n IOracle(IVault(vaultAddress).priceProvider()).price(_tokenAddress);\n\n IERC20 token = IERC20(_tokenAddress);\n // if changing token swap provider cancel existing allowance\n if (\n /* oldRouterAddress == address(0) when there is no pre-existing\n * configuration for said rewards token\n */\n oldRouterAddress != address(0) &&\n oldRouterAddress != newRouterAddress\n ) {\n token.safeApprove(oldRouterAddress, 0);\n }\n\n // Give SwapRouter infinite approval when needed\n if (oldRouterAddress != newRouterAddress) {\n token.safeApprove(newRouterAddress, 0);\n token.safeApprove(newRouterAddress, type(uint256).max);\n }\n\n SwapPlatform _platform = tokenConfig.swapPlatform;\n if (_platform == SwapPlatform.UniswapV2Compatible) {\n uniswapV2Path[_tokenAddress] = _decodeUniswapV2Path(\n swapData,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.UniswapV3) {\n uniswapV3Path[_tokenAddress] = _decodeUniswapV3Path(\n swapData,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.Balancer) {\n balancerPoolId[_tokenAddress] = _decodeBalancerPoolId(\n swapData,\n newRouterAddress,\n _tokenAddress\n );\n } else if (_platform == SwapPlatform.Curve) {\n curvePoolIndices[_tokenAddress] = _decodeCurvePoolIndices(\n swapData,\n newRouterAddress,\n _tokenAddress\n );\n } else {\n // Note: This code is unreachable since Solidity reverts when\n // the value is outside the range of defined values of the enum\n // (even if it's under the max length of the base type)\n revert InvalidSwapPlatform(_platform);\n }\n\n emit RewardTokenConfigUpdated(\n _tokenAddress,\n tokenConfig.allowedSlippageBps,\n tokenConfig.harvestRewardBps,\n _platform,\n newRouterAddress,\n swapData,\n tokenConfig.liquidationLimit,\n tokenConfig.doSwapRewardToken\n );\n }\n\n /**\n * @dev Decodes the data passed into Uniswap V2 path and validates\n * it to make sure the path is for `token` to `baseToken`\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param token The address of the reward token\n * @return path The validated Uniswap V2 path\n */\n function _decodeUniswapV2Path(bytes calldata data, address token)\n internal\n view\n returns (address[] memory path)\n {\n (path) = abi.decode(data, (address[]));\n uint256 len = path.length;\n\n if (len < 2) {\n // Path should have at least two tokens\n revert InvalidUniswapV2PathLength();\n }\n\n // Do some validation\n if (path[0] != token) {\n revert InvalidTokenInSwapPath(path[0]);\n }\n\n if (path[len - 1] != baseTokenAddress) {\n revert InvalidTokenInSwapPath(path[len - 1]);\n }\n }\n\n /**\n * @dev Decodes the data passed into Uniswap V3 path and validates\n * it to make sure the path is for `token` to `baseToken`\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param token The address of the reward token\n * @return path The validated Uniswap V3 path\n */\n function _decodeUniswapV3Path(bytes calldata data, address token)\n internal\n view\n returns (bytes calldata path)\n {\n path = data;\n\n address decodedAddress = address(uint160(bytes20(data[0:20])));\n\n if (decodedAddress != token) {\n // Invalid Reward Token in swap path\n revert InvalidTokenInSwapPath(decodedAddress);\n }\n\n decodedAddress = address(uint160(bytes20(data[path.length - 20:])));\n if (decodedAddress != baseTokenAddress) {\n // Invalid Base Token in swap path\n revert InvalidTokenInSwapPath(decodedAddress);\n }\n }\n\n /**\n * @dev Decodes the data passed to Balancer Pool ID\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @return poolId The pool ID\n */\n function _decodeBalancerPoolId(\n bytes calldata data,\n address balancerVault,\n address token\n ) internal view returns (bytes32 poolId) {\n (poolId) = abi.decode(data, (bytes32));\n\n if (poolId == bytes32(0)) {\n revert EmptyBalancerPoolId();\n }\n\n IBalancerVault bVault = IBalancerVault(balancerVault);\n\n // Note: this reverts if token is not a pool asset\n // slither-disable-next-line unused-return\n bVault.getPoolTokenInfo(poolId, token);\n\n // slither-disable-next-line unused-return\n bVault.getPoolTokenInfo(poolId, baseTokenAddress);\n }\n\n /**\n * @dev Decodes the data passed to get the pool indices and\n * checks it against the Curve Pool to make sure it's\n * not misconfigured. The indices are packed into a single\n * uint256 for gas savings\n *\n * @param data Ecnoded data passed to the `setRewardTokenConfig`\n * @param poolAddress Curve pool address\n * @param token The address of the reward token\n * @return indices Packed pool asset indices\n */\n function _decodeCurvePoolIndices(\n bytes calldata data,\n address poolAddress,\n address token\n ) internal view returns (CurvePoolIndices memory indices) {\n indices = abi.decode(data, (CurvePoolIndices));\n\n ICurvePool pool = ICurvePool(poolAddress);\n if (token != pool.coins(indices.rewardTokenIndex)) {\n revert InvalidCurvePoolAssetIndex(token);\n }\n if (baseTokenAddress != pool.coins(indices.baseTokenIndex)) {\n revert InvalidCurvePoolAssetIndex(baseTokenAddress);\n }\n }\n\n /**\n * @dev Flags a strategy as supported or not supported one\n * @param _strategyAddress Address of the strategy\n * @param _isSupported Bool marking strategy as supported or not supported\n */\n function setSupportedStrategy(address _strategyAddress, bool _isSupported)\n external\n onlyGovernor\n {\n supportedStrategies[_strategyAddress] = _isSupported;\n emit SupportedStrategyUpdate(_strategyAddress, _isSupported);\n }\n\n /***************************************\n Rewards\n ****************************************/\n\n /**\n * @dev Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform. Can be called by anyone.\n * Rewards incentivizing the caller are sent to the caller of this function.\n * @param _strategyAddr Address of the strategy to collect rewards from\n */\n function harvestAndSwap(address _strategyAddr) external nonReentrant {\n // Remember _harvest function checks for the validity of _strategyAddr\n _harvestAndSwap(_strategyAddr, msg.sender);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform. Can be called by anyone\n * @param _strategyAddr Address of the strategy to collect rewards from\n * @param _rewardTo Address where to send a share of harvest rewards to as an incentive\n * for executing this function\n */\n function harvestAndSwap(address _strategyAddr, address _rewardTo)\n external\n nonReentrant\n {\n // Remember _harvest function checks for the validity of _strategyAddr\n _harvestAndSwap(_strategyAddr, _rewardTo);\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform\n * @param _strategyAddr Address of the strategy to collect rewards from\n * @param _rewardTo Address where to send a share of harvest rewards to as an incentive\n * for executing this function\n */\n function _harvestAndSwap(address _strategyAddr, address _rewardTo)\n internal\n {\n _harvest(_strategyAddr);\n IStrategy strategy = IStrategy(_strategyAddr);\n address[] memory rewardTokens = strategy.getRewardTokenAddresses();\n IOracle priceProvider = IOracle(IVault(vaultAddress).priceProvider());\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; ++i) {\n _swap(rewardTokens[i], _rewardTo, priceProvider);\n }\n }\n\n /**\n * @dev Collect reward tokens from a specific strategy and swap them for\n * base token on the configured swap platform\n * @param _strategyAddr Address of the strategy to collect rewards from.\n */\n function _harvest(address _strategyAddr) internal virtual {\n if (!supportedStrategies[_strategyAddr]) {\n revert UnsupportedStrategy(_strategyAddr);\n }\n\n IStrategy strategy = IStrategy(_strategyAddr);\n strategy.collectRewardTokens();\n }\n\n /**\n * @dev Swap a reward token for the base token on the configured\n * swap platform. The token must have a registered price feed\n * with the price provider\n * @param _swapToken Address of the token to swap\n * @param _rewardTo Address where to send the share of harvest rewards to\n * @param _priceProvider Oracle to get prices of the swap token\n */\n function _swap(\n address _swapToken,\n address _rewardTo,\n IOracle _priceProvider\n ) internal virtual {\n uint256 balance = IERC20(_swapToken).balanceOf(address(this));\n\n // No need to swap if the reward token is the base token. eg USDT or WETH.\n // There is also no limit on the transfer. Everything in the harvester will be transferred\n // to the Dripper regardless of the liquidationLimit config.\n if (_swapToken == baseTokenAddress) {\n IERC20(_swapToken).safeTransfer(rewardProceedsAddress, balance);\n // currently not paying the farmer any rewards as there is no swap\n emit RewardProceedsTransferred(\n baseTokenAddress,\n address(0),\n balance,\n 0\n );\n return;\n }\n\n RewardTokenConfig memory tokenConfig = rewardTokenConfigs[_swapToken];\n\n /* This will trigger a return when reward token configuration has not yet been set\n * or we have temporarily disabled swapping of specific reward token via setting\n * doSwapRewardToken to false.\n */\n if (!tokenConfig.doSwapRewardToken) {\n return;\n }\n\n if (balance == 0) {\n return;\n }\n\n if (tokenConfig.liquidationLimit > 0) {\n balance = Math.min(balance, tokenConfig.liquidationLimit);\n }\n\n // This'll revert if there is no price feed\n uint256 oraclePrice = _priceProvider.price(_swapToken);\n\n // Oracle price is 1e18\n uint256 minExpected = (balance *\n (1e4 - tokenConfig.allowedSlippageBps) * // max allowed slippage\n oraclePrice).scaleBy(\n baseTokenDecimals,\n Helpers.getDecimals(_swapToken)\n ) /\n 1e4 / // fix the max slippage decimal position\n 1e18; // and oracle price decimals position\n\n // Do the swap\n uint256 amountReceived = _doSwap(\n tokenConfig.swapPlatform,\n tokenConfig.swapPlatformAddr,\n _swapToken,\n balance,\n minExpected\n );\n\n if (amountReceived < minExpected) {\n revert SlippageError(amountReceived, minExpected);\n }\n\n emit RewardTokenSwapped(\n _swapToken,\n baseTokenAddress,\n tokenConfig.swapPlatform,\n balance,\n amountReceived\n );\n\n IERC20 baseToken = IERC20(baseTokenAddress);\n uint256 baseTokenBalance = baseToken.balanceOf(address(this));\n if (baseTokenBalance < amountReceived) {\n // Note: It's possible to bypass this check by transferring `baseToken`\n // directly to Harvester before calling the `harvestAndSwap`. However,\n // there's no incentive for an attacker to do that. Doing a balance diff\n // will increase the gas cost significantly\n revert BalanceMismatchAfterSwap(baseTokenBalance, amountReceived);\n }\n\n // Farmer only gets fee from the base amount they helped farm,\n // They do not get anything from anything that already was there\n // on the Harvester\n uint256 farmerFee = amountReceived.mulTruncateScale(\n tokenConfig.harvestRewardBps,\n 1e4\n );\n uint256 protocolYield = baseTokenBalance - farmerFee;\n\n baseToken.safeTransfer(rewardProceedsAddress, protocolYield);\n baseToken.safeTransfer(_rewardTo, farmerFee);\n emit RewardProceedsTransferred(\n baseTokenAddress,\n _rewardTo,\n protocolYield,\n farmerFee\n );\n }\n\n function _doSwap(\n SwapPlatform swapPlatform,\n address routerAddress,\n address rewardTokenAddress,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n if (swapPlatform == SwapPlatform.UniswapV2Compatible) {\n return\n _swapWithUniswapV2(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.UniswapV3) {\n return\n _swapWithUniswapV3(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.Balancer) {\n return\n _swapWithBalancer(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else if (swapPlatform == SwapPlatform.Curve) {\n return\n _swapWithCurve(\n routerAddress,\n rewardTokenAddress,\n amountIn,\n minAmountOut\n );\n } else {\n // Should never be invoked since we catch invalid values\n // in the `setRewardTokenConfig` function before it's set\n revert InvalidSwapPlatform(swapPlatform);\n }\n }\n\n /**\n * @dev Swaps the token to `baseToken` with Uniswap V2\n *\n * @param routerAddress Uniswap V2 Router address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithUniswapV2(\n address routerAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n address[] memory path = uniswapV2Path[swapToken];\n\n uint256[] memory amounts = IUniswapV2Router(routerAddress)\n .swapExactTokensForTokens(\n amountIn,\n minAmountOut,\n path,\n address(this),\n block.timestamp\n );\n\n amountOut = amounts[amounts.length - 1];\n }\n\n /**\n * @dev Swaps the token to `baseToken` with Uniswap V3\n *\n * @param routerAddress Uniswap V3 Router address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithUniswapV3(\n address routerAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n bytes memory path = uniswapV3Path[swapToken];\n\n IUniswapV3Router.ExactInputParams memory params = IUniswapV3Router\n .ExactInputParams({\n path: path,\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: amountIn,\n amountOutMinimum: minAmountOut\n });\n amountOut = IUniswapV3Router(routerAddress).exactInput(params);\n }\n\n /**\n * @dev Swaps the token to `baseToken` on Balancer\n *\n * @param balancerVaultAddress BalancerVaultAddress\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithBalancer(\n address balancerVaultAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n bytes32 poolId = balancerPoolId[swapToken];\n\n IBalancerVault.SingleSwap memory singleSwap = IBalancerVault\n .SingleSwap({\n poolId: poolId,\n kind: IBalancerVault.SwapKind.GIVEN_IN,\n assetIn: swapToken,\n assetOut: baseTokenAddress,\n amount: amountIn,\n userData: hex\"\"\n });\n\n IBalancerVault.FundManagement memory fundMgmt = IBalancerVault\n .FundManagement({\n sender: address(this),\n fromInternalBalance: false,\n recipient: payable(address(this)),\n toInternalBalance: false\n });\n\n amountOut = IBalancerVault(balancerVaultAddress).swap(\n singleSwap,\n fundMgmt,\n minAmountOut,\n block.timestamp\n );\n }\n\n /**\n * @dev Swaps the token to `baseToken` on Curve\n *\n * @param poolAddress Curve Pool Address\n * @param swapToken Address of the tokenIn\n * @param amountIn Amount of `swapToken` to swap\n * @param minAmountOut Minimum expected amount of `baseToken`\n *\n * @return amountOut Amount of `baseToken` received after the swap\n */\n function _swapWithCurve(\n address poolAddress,\n address swapToken,\n uint256 amountIn,\n uint256 minAmountOut\n ) internal returns (uint256 amountOut) {\n CurvePoolIndices memory indices = curvePoolIndices[swapToken];\n\n // Note: Not all CurvePools return the `amountOut`, make sure\n // to use only pool that do. Otherwise the swap would revert\n // always\n amountOut = ICurvePool(poolAddress).exchange(\n uint256(indices.rewardTokenIndex),\n uint256(indices.baseTokenIndex),\n amountIn,\n minAmountOut\n );\n }\n}\n" + }, + "contracts/harvest/Dripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\n/**\n * @title OUSD Dripper\n *\n * The dripper contract smooths out the yield from point-in-time yield events\n * and spreads the yield out over a configurable time period. This ensures a\n * continuous per block yield to makes users happy as their next rebase\n * amount is always moving up. Also, this makes historical day to day yields\n * smooth, rather than going from a near zero day, to a large APY day, then\n * back to a near zero day again.\n *\n *\n * Design notes\n * - USDT has a smaller resolution than the number of seconds\n * in a week, which can make per second payouts have a rounding error. However\n * the total effect is not large - cents per day, and this money is\n * not lost, just distributed in the future. While we could use a higher\n * decimal precision for the drip perSecond, we chose simpler code.\n * - By calculating the changing drip rates on collects only, harvests and yield\n * events don't have to call anything on this contract or pay any extra gas.\n * Collect() is already be paying for a single write, since it has to reset\n * the lastCollect time.\n * - By having a collectAndRebase method, and having our external systems call\n * that, the OUSD vault does not need any changes, not even to know the address\n * of the dripper.\n * - A rejected design was to retro-calculate the drip rate on each collect,\n * based on the balance at the time of the collect. While this would have\n * required less state, and would also have made the contract respond more quickly\n * to new income, it would break the predictability that is this contract's entire\n * purpose. If we did this, the amount of fundsAvailable() would make sharp increases\n * when funds were deposited.\n * - When the dripper recalculates the rate, it targets spending the balance over\n * the duration. This means that every time that collect is called, if no\n * new funds have been deposited the duration is being pushed back and the\n * rate decreases. This is expected, and ends up following a smoother but\n * longer curve the more collect() is called without incoming yield.\n *\n */\n\ncontract Dripper is Governable {\n using SafeERC20 for IERC20;\n\n struct Drip {\n uint64 lastCollect; // overflows 262 billion years after the sun dies\n uint192 perSecond; // drip rate per second\n }\n\n address immutable vault; // OUSD vault\n address immutable token; // token to drip out\n uint256 public dripDuration; // in seconds\n Drip public drip; // active drip parameters\n\n constructor(address _vault, address _token) {\n vault = _vault;\n token = _token;\n }\n\n /// @notice How much funds have dripped out already and are currently\n // available to be sent to the vault.\n /// @return The amount that would be sent if a collect was called\n function availableFunds() external view returns (uint256) {\n uint256 balance = IERC20(token).balanceOf(address(this));\n return _availableFunds(balance, drip);\n }\n\n /// @notice Collect all dripped funds and send to vault.\n /// Recalculate new drip rate.\n function collect() external {\n _collect();\n }\n\n /// @notice Collect all dripped funds, send to vault, recalculate new drip\n /// rate, and rebase OUSD.\n function collectAndRebase() external {\n _collect();\n IVault(vault).rebase();\n }\n\n /// @dev Change the drip duration. Governor only.\n /// @param _durationSeconds the number of seconds to drip out the entire\n /// balance over if no collects were called during that time.\n function setDripDuration(uint256 _durationSeconds)\n external\n virtual\n onlyGovernor\n {\n require(_durationSeconds > 0, \"duration must be non-zero\");\n dripDuration = _durationSeconds;\n _collect(); // duration change take immediate effect\n }\n\n /// @dev Transfer out ERC20 tokens held by the contract. Governor only.\n /// @param _asset ERC20 token address\n /// @param _amount amount to transfer\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /// @dev Calculate available funds by taking the lower of either the\n /// currently dripped out funds or the balance available.\n /// Uses passed in parameters to calculate with for gas savings.\n /// @param _balance current balance in contract\n /// @param _drip current drip parameters\n function _availableFunds(uint256 _balance, Drip memory _drip)\n internal\n view\n returns (uint256)\n {\n uint256 elapsed = block.timestamp - _drip.lastCollect;\n uint256 allowed = (elapsed * _drip.perSecond);\n return (allowed > _balance) ? _balance : allowed;\n }\n\n /// @dev Sends the currently dripped funds to be vault, and sets\n /// the new drip rate based on the new balance.\n function _collect() internal virtual {\n // Calculate send\n uint256 balance = IERC20(token).balanceOf(address(this));\n uint256 amountToSend = _availableFunds(balance, drip);\n uint256 remaining = balance - amountToSend;\n // Calculate new drip perSecond\n // Gas savings by setting entire struct at one time\n drip = Drip({\n perSecond: uint192(remaining / dripDuration),\n lastCollect: uint64(block.timestamp)\n });\n // Send funds\n IERC20(token).safeTransfer(vault, amountToSend);\n }\n\n /// @dev Transfer out all ERC20 held by the contract. Governor only.\n /// @param _asset ERC20 token address\n function transferAllToken(address _asset, address _receiver)\n external\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(\n _receiver,\n IERC20(_asset).balanceOf(address(this))\n );\n }\n}\n" + }, + "contracts/harvest/FixedRateDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Dripper } from \"./Dripper.sol\";\n\n/**\n * @title Fixed Rate Dripper\n *\n * Similar to the Dripper, Fixed Rate Dripper drips out yield per second.\n * However the Strategist decides the rate and it doesn't change after\n * a drip.\n *\n */\n\ncontract FixedRateDripper is Dripper {\n using SafeERC20 for IERC20;\n\n event DripRateUpdated(uint192 oldDripRate, uint192 newDripRate);\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vault).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n constructor(address _vault, address _token) Dripper(_vault, _token) {}\n\n /// @inheritdoc Dripper\n function setDripDuration(uint256) external virtual override {\n // Not used in FixedRateDripper\n revert(\"Drip duration disabled\");\n }\n\n /// @inheritdoc Dripper\n function _collect() internal virtual override {\n // Calculate amount to send\n uint256 balance = IERC20(token).balanceOf(address(this));\n uint256 amountToSend = _availableFunds(balance, drip);\n\n // Update timestamp\n drip.lastCollect = uint64(block.timestamp);\n\n // Send funds\n IERC20(token).safeTransfer(vault, amountToSend);\n }\n\n /**\n * @dev Sets the drip rate. Callable by Strategist or Governor.\n * Can be set to zero to stop dripper.\n * @param _perSecond Rate of WETH to drip per second\n */\n function setDripRate(uint192 _perSecond) external onlyGovernorOrStrategist {\n emit DripRateUpdated(_perSecond, drip.perSecond);\n\n /**\n * Note: It's important to call `_collect` before updating\n * the drip rate especially on a new proxy contract.\n * When `lastCollect` is not set/initialized, the elapsed\n * time would be calculated as `block.number` seconds,\n * resulting in a huge yield, if `collect` isn't called first.\n */\n // Collect at existing rate\n _collect();\n\n // Update rate\n drip.perSecond = _perSecond;\n }\n}\n" + }, + "contracts/harvest/Harvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractHarvester } from \"./AbstractHarvester.sol\";\n\ncontract Harvester is AbstractHarvester {\n constructor(address _vault, address _usdtAddress)\n AbstractHarvester(_vault, _usdtAddress)\n {}\n}\n" + }, + "contracts/harvest/OETHBaseHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { ISwapRouter } from \"../interfaces/aerodrome/ISwapRouter.sol\";\n\ncontract OETHBaseHarvester is Governable {\n using SafeERC20 for IERC20;\n\n IVault public immutable vault;\n IStrategy public immutable amoStrategy;\n IERC20 public immutable aero;\n IERC20 public immutable weth;\n ISwapRouter public immutable swapRouter;\n\n address public operatorAddr;\n\n // Similar sig to `AbstractHarvester.RewardTokenSwapped` for\n // future compatibility with monitoring\n event RewardTokenSwapped(\n address indexed rewardToken,\n address indexed swappedInto,\n uint8 swapPlatform,\n uint256 amountIn,\n uint256 amountOut\n );\n\n event OperatorChanged(address oldOperator, address newOperator);\n event YieldSent(address recipient, uint256 yield, uint256 fee);\n\n /**\n * @notice Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == vault.strategistAddr() || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @notice Verifies that the caller is either Governor or Strategist.\n */\n modifier onlyGovernorOrStrategistOrOperator() {\n require(\n msg.sender == operatorAddr ||\n msg.sender == vault.strategistAddr() ||\n isGovernor(),\n \"Caller is not the Operator or Strategist or Governor\"\n );\n _;\n }\n\n constructor(\n address _vault,\n address _amoStrategy,\n address _aero,\n address _weth,\n address _swapRouter\n ) {\n vault = IVault(_vault);\n amoStrategy = IStrategy(_amoStrategy);\n aero = IERC20(_aero);\n weth = IERC20(_weth);\n swapRouter = ISwapRouter(_swapRouter);\n }\n\n /**\n * @dev Changes the operator address which can call `harvest`\n * @param _operatorAddr New operator address\n */\n function setOperatorAddr(address _operatorAddr) external onlyGovernor {\n emit OperatorChanged(operatorAddr, _operatorAddr);\n operatorAddr = _operatorAddr;\n }\n\n /**\n * @notice Collects AERO from AMO strategy and\n * sends it to the Strategist multisig.\n * Anyone can call it.\n */\n function harvest() external onlyGovernorOrStrategistOrOperator {\n address strategistAddr = vault.strategistAddr();\n require(strategistAddr != address(0), \"Guardian address not set\");\n\n // Collect all AERO\n amoStrategy.collectRewardTokens();\n\n uint256 aeroBalance = aero.balanceOf(address(this));\n if (aeroBalance == 0) {\n // Do nothing if there's no AERO to transfer\n return;\n }\n\n // Transfer everything to Strategist\n aero.safeTransfer(strategistAddr, aeroBalance);\n }\n\n /**\n * @notice Harvests AERO from AMO strategy and then swaps some (or all)\n * of it into WETH to distribute yield and fee.\n * When `feeBps` is set to 10000 (100%), all WETH received is\n * sent to strategist.\n *\n * @param aeroToSwap Amount of AERO to swap\n * @param minWETHExpected Min. amount of WETH to expect\n * @param feeBps Performance fee bps (Sent to strategist)\n * @param sendYieldToDripper Sends yield to Dripper, if set to true.\n * Otherwise, to the Guardian\n */\n function harvestAndSwap(\n uint256 aeroToSwap,\n uint256 minWETHExpected,\n uint256 feeBps,\n bool sendYieldToDripper\n ) external onlyGovernorOrStrategist {\n address strategistAddr = vault.strategistAddr();\n require(strategistAddr != address(0), \"Guardian address not set\");\n\n // Yields can either be sent to the Dripper or Strategist\n address yieldRecipient = sendYieldToDripper\n ? vault.dripper()\n : strategistAddr;\n require(yieldRecipient != address(0), \"Yield recipient not set\");\n\n require(feeBps <= 10000, \"Invalid Fee Bps\");\n\n // Collect all AERO\n amoStrategy.collectRewardTokens();\n\n uint256 aeroBalance = aero.balanceOf(address(this));\n if (aeroBalance == 0) {\n // Do nothing if there's no AERO to transfer/swap\n return;\n }\n\n if (aeroToSwap > 0) {\n if (aeroBalance < aeroToSwap) {\n // Transfer in balance from the multisig as needed\n // slither-disable-next-line unchecked-transfer arbitrary-send-erc20\n aero.safeTransferFrom(\n strategistAddr,\n address(this),\n aeroToSwap - aeroBalance\n );\n }\n\n _doSwap(aeroToSwap, minWETHExpected);\n\n // Figure out AERO left in contract after swap\n aeroBalance = aero.balanceOf(address(this));\n }\n\n // Transfer out any leftover AERO after swap\n if (aeroBalance > 0) {\n aero.safeTransfer(strategistAddr, aeroBalance);\n }\n\n // Computes using all balance the contract holds,\n // not just the WETH received from swap. Use `transferToken`\n // if there's any WETH left that needs to be taken out\n uint256 availableWETHBalance = weth.balanceOf(address(this));\n // Computation rounds in favor of protocol\n uint256 fee = (availableWETHBalance * feeBps) / 10000;\n uint256 yield = availableWETHBalance - fee;\n\n // Transfer yield, if any\n if (yield > 0) {\n weth.safeTransfer(yieldRecipient, yield);\n }\n\n // Transfer fee to the Guardian, if any\n if (fee > 0) {\n weth.safeTransfer(strategistAddr, fee);\n }\n\n emit YieldSent(yieldRecipient, yield, fee);\n }\n\n /**\n * @notice Swaps AERO to WETH on Aerodrome\n * @param aeroToSwap Amount of AERO to swap\n * @param minWETHExpected Min. amount of WETH to expect\n */\n function _doSwap(uint256 aeroToSwap, uint256 minWETHExpected) internal {\n // Let the swap router move funds\n aero.approve(address(swapRouter), aeroToSwap);\n\n // Do the swap\n uint256 wethReceived = swapRouter.exactInputSingle(\n ISwapRouter.ExactInputSingleParams({\n tokenIn: address(aero),\n tokenOut: address(weth),\n tickSpacing: 200, // From AERO/WETH pool contract\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: aeroToSwap,\n amountOutMinimum: minWETHExpected,\n sqrtPriceLimitX96: 0\n })\n );\n\n emit RewardTokenSwapped(\n address(aero),\n address(weth),\n 0,\n aeroToSwap,\n wethReceived\n );\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * the contract, i.e. mistaken sends.\n * Also, allows to transfer any AERO left in the contract.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n virtual\n onlyGovernor\n {\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n}\n" + }, + "contracts/harvest/OETHDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Dripper } from \"./Dripper.sol\";\n\n/**\n * @title OETH Dripper Contract\n * @author Origin Protocol Inc\n */\ncontract OETHDripper is Dripper {\n constructor(address _vault, address _token) Dripper(_vault, _token) {}\n}\n" + }, + "contracts/harvest/OETHFixedRateDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { FixedRateDripper } from \"./FixedRateDripper.sol\";\n\n/**\n * @title OETH FixedRateDripper Contract\n * @author Origin Protocol Inc\n */\ncontract OETHFixedRateDripper is FixedRateDripper {\n constructor(address _vault, address _token)\n FixedRateDripper(_vault, _token)\n {}\n}\n" + }, + "contracts/harvest/OETHHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { AbstractHarvester } from \"./AbstractHarvester.sol\";\n\ncontract OETHHarvester is AbstractHarvester {\n constructor(address _vault, address _wethAddress)\n AbstractHarvester(_vault, _wethAddress)\n {}\n}\n" + }, + "contracts/harvest/OETHHarvesterSimple.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\n\n/// @title OETH Harvester Simple Contract\n/// @notice Contract to harvest rewards from strategies\n/// @author Origin Protocol Inc\ncontract OETHHarvesterSimple is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n ////////////////////////////////////////////////////\n /// --- CONSTANTS & IMMUTABLES\n ////////////////////////////////////////////////////\n /// @notice wrapped native token address (WETH or wS)\n address public immutable wrappedNativeToken;\n\n ////////////////////////////////////////////////////\n /// --- STORAGE\n ////////////////////////////////////////////////////\n /// @notice Dripper address\n address public dripper;\n\n /// @notice Mapping of supported strategies\n mapping(address => bool) public supportedStrategies;\n\n /// @notice Gap for upgrade safety\n uint256[48] private ___gap;\n\n ////////////////////////////////////////////////////\n /// --- EVENTS\n ////////////////////////////////////////////////////\n event Harvested(\n address indexed strategy,\n address token,\n uint256 amount,\n address indexed receiver\n );\n event SupportedStrategyUpdated(address strategy, bool status);\n event DripperUpdated(address dripper);\n\n ////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////\n constructor(address _wrappedNativeToken) {\n wrappedNativeToken = _wrappedNativeToken;\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /// @notice Initialize the contract\n function initialize() external onlyGovernor initializer {\n // Call it to set `initialized` to true and to prevent the implementation\n // from getting initialized in future through the proxy\n }\n\n ////////////////////////////////////////////////////\n /// --- MUTATIVE FUNCTIONS\n ////////////////////////////////////////////////////\n /// @notice Harvest rewards from a strategy and transfer to strategist or dripper\n /// @param _strategy Address of the strategy to harvest\n function harvestAndTransfer(address _strategy) external {\n _harvestAndTransfer(_strategy);\n }\n\n /// @notice Harvest rewards from multiple strategies and transfer to strategist or dripper\n /// @param _strategies Array of strategy addresses to harvest\n function harvestAndTransfer(address[] calldata _strategies) external {\n for (uint256 i = 0; i < _strategies.length; i++) {\n _harvestAndTransfer(_strategies[i]);\n }\n }\n\n /// @notice Internal logic to harvest rewards from a strategy\n function _harvestAndTransfer(address _strategy) internal virtual {\n // Ensure strategy is supported\n require(supportedStrategies[_strategy], \"Strategy not supported\");\n\n // Store locally for some gas savings\n address _strategist = strategistAddr;\n address _dripper = dripper;\n\n // Harvest rewards\n IStrategy(_strategy).collectRewardTokens();\n\n // Cache reward tokens\n address[] memory rewardTokens = IStrategy(_strategy)\n .getRewardTokenAddresses();\n\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; i++) {\n // Cache balance\n address token = rewardTokens[i];\n uint256 balance = IERC20(token).balanceOf(address(this));\n if (balance > 0) {\n // Determine receiver\n address receiver = token == wrappedNativeToken\n ? _dripper\n : _strategist;\n require(receiver != address(0), \"Invalid receiver\");\n\n // Transfer to the Strategist or the Dripper\n IERC20(token).safeTransfer(receiver, balance);\n emit Harvested(_strategy, token, balance, receiver);\n }\n }\n }\n\n ////////////////////////////////////////////////////\n /// --- GOVERNANCE\n ////////////////////////////////////////////////////\n /// @notice Set supported strategy\n /// @param _strategy Address of the strategy\n /// @param _isSupported Boolean indicating if strategy is supported\n function setSupportedStrategy(address _strategy, bool _isSupported)\n external\n onlyGovernorOrStrategist\n {\n require(_strategy != address(0), \"Invalid strategy\");\n supportedStrategies[_strategy] = _isSupported;\n emit SupportedStrategyUpdated(_strategy, _isSupported);\n }\n\n /// @notice Transfer tokens to strategist\n /// @param _asset Address of the token\n /// @param _amount Amount of tokens to transfer\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernorOrStrategist\n {\n IERC20(_asset).safeTransfer(strategistAddr, _amount);\n }\n\n /// @notice Set the dripper address\n /// @param _dripper Address of the dripper\n function setDripper(address _dripper) external onlyGovernor {\n _setDripper(_dripper);\n }\n\n /// @notice Internal logic to set the dripper address\n function _setDripper(address _dripper) internal {\n require(_dripper != address(0), \"Invalid dripper\");\n dripper = _dripper;\n emit DripperUpdated(_dripper);\n }\n}\n" + }, + "contracts/harvest/OSonicHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SuperOETHHarvester } from \"./SuperOETHHarvester.sol\";\n\ncontract OSonicHarvester is SuperOETHHarvester {\n /// @param _wrappedNativeToken Address of the native Wrapped S (wS) token\n constructor(address _wrappedNativeToken)\n SuperOETHHarvester(_wrappedNativeToken)\n {}\n}\n" + }, + "contracts/harvest/SuperOETHHarvester.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHHarvesterSimple, IERC20, IStrategy, SafeERC20 } from \"./OETHHarvesterSimple.sol\";\n\ncontract SuperOETHHarvester is OETHHarvesterSimple {\n using SafeERC20 for IERC20;\n\n constructor(address _wrappedNativeToken)\n OETHHarvesterSimple(_wrappedNativeToken)\n {}\n\n /// @inheritdoc OETHHarvesterSimple\n function _harvestAndTransfer(address _strategy) internal virtual override {\n // Ensure strategy is supported\n require(supportedStrategies[_strategy], \"Strategy not supported\");\n\n address receiver = strategistAddr;\n require(receiver != address(0), \"Invalid receiver\");\n\n // Harvest rewards\n IStrategy(_strategy).collectRewardTokens();\n\n // Cache reward tokens\n address[] memory rewardTokens = IStrategy(_strategy)\n .getRewardTokenAddresses();\n\n uint256 len = rewardTokens.length;\n for (uint256 i = 0; i < len; i++) {\n // Cache balance\n address token = rewardTokens[i];\n uint256 balance = IERC20(token).balanceOf(address(this));\n if (balance > 0) {\n // Transfer everything to the strategist\n IERC20(token).safeTransfer(receiver, balance);\n emit Harvested(_strategy, token, balance, receiver);\n }\n }\n }\n}\n" + }, + "contracts/interfaces/aerodrome/IAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ICLPool } from \"./ICLPool.sol\";\n\ninterface IAMOStrategy {\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth);\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth);\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n error OutsideExpectedTickRange(int24 currentTick);\n\n function governor() external view returns (address);\n\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) external;\n\n function clPool() external view returns (ICLPool);\n\n function vaultAddress() external view returns (address);\n\n function poolWethShareVarianceAllowed() external view returns (uint256);\n\n function poolWethShare() external view returns (uint256);\n\n function tokenId() external view returns (uint256);\n\n function withdrawAll() external;\n\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external;\n\n function setWithdrawLiquidityShare(uint128 share) external;\n\n function lowerTick() external view returns (int24);\n\n function upperTick() external view returns (int24);\n\n function getPoolX96Price() external view returns (uint160 _sqrtRatioX96);\n\n function sqrtRatioX96TickLower() external view returns (uint160);\n\n function sqrtRatioX96TickHigher() external view returns (uint160);\n\n function tickSpacing() external view returns (int24);\n\n function allowedWethShareStart() external view returns (uint256);\n\n function allowedWethShareEnd() external view returns (uint256);\n\n function claimGovernance() external;\n\n function transferGovernance(address _governor) external;\n\n function getPositionPrincipal()\n external\n view\n returns (uint256 _amountWeth, uint256 _amountOethb);\n}\n" + }, + "contracts/interfaces/aerodrome/ICLGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\ninterface ICLGauge {\n /// @notice Returns the claimable rewards for a given account and tokenId\n /// @dev Throws if account is not the position owner\n /// @dev pool.updateRewardsGrowthGlobal() needs to be called first, to return the correct claimable rewards\n /// @param account The address of the user\n /// @param tokenId The tokenId of the position\n /// @return The amount of claimable reward\n function earned(address account, uint256 tokenId)\n external\n view\n returns (uint256);\n\n /// @notice Retrieve rewards for all tokens owned by an account\n /// @dev Throws if not called by the voter\n /// @param account The account of the user\n function getReward(address account) external;\n\n /// @notice Retrieve rewards for a tokenId\n /// @dev Throws if not called by the position owner\n /// @param tokenId The tokenId of the position\n function getReward(uint256 tokenId) external;\n\n /// @notice Notifies gauge of gauge rewards.\n /// @param amount Amount of gauge rewards (emissions) to notify. Must be greater than 604_800.\n function notifyRewardAmount(uint256 amount) external;\n\n /// @dev Notifies gauge of gauge rewards without distributing its fees.\n /// Assumes gauge reward tokens is 18 decimals.\n /// If not 18 decimals, rewardRate may have rounding issues.\n /// @param amount Amount of gauge rewards (emissions) to notify. Must be greater than 604_800.\n function notifyRewardWithoutClaim(uint256 amount) external;\n\n /// @notice Used to deposit a CL position into the gauge\n /// @notice Allows the user to receive emissions instead of fees\n /// @param tokenId The tokenId of the position\n function deposit(uint256 tokenId) external;\n\n /// @notice Used to withdraw a CL position from the gauge\n /// @notice Allows the user to receive fees instead of emissions\n /// @notice Outstanding emissions will be collected on withdrawal\n /// @param tokenId The tokenId of the position\n function withdraw(uint256 tokenId) external;\n\n // /// @notice Fetch all tokenIds staked by a given account\n // /// @param depositor The address of the user\n // /// @return The tokenIds of the staked positions\n // function stakedValues(address depositor) external view returns (uint256[] memory);\n\n // /// @notice Fetch a staked tokenId by index\n // /// @param depositor The address of the user\n // /// @param index The index of the staked tokenId\n // /// @return The tokenId of the staked position\n // function stakedByIndex(address depositor, uint256 index) external view returns (uint256);\n\n // /// @notice Check whether a position is staked in the gauge by a certain user\n // /// @param depositor The address of the user\n // /// @param tokenId The tokenId of the position\n // /// @return Whether the position is staked in the gauge\n // function stakedContains(address depositor, uint256 tokenId) external view returns (bool);\n\n // /// @notice The amount of positions staked in the gauge by a certain user\n // /// @param depositor The address of the user\n // /// @return The amount of positions staked in the gauge\n // function stakedLength(address depositor) external view returns (uint256);\n\n function feesVotingReward() external view returns (address);\n}\n" + }, + "contracts/interfaces/aerodrome/ICLPool.sol": { + "content": "pragma solidity >=0.5.0;\n\n/// @title The interface for a CL Pool\n/// @notice A CL pool facilitates swapping and automated market making between any two assets that strictly conform\n/// to the ERC20 specification\n/// @dev The pool interface is broken up into many smaller pieces\ninterface ICLPool {\n function slot0()\n external\n view\n returns (\n uint160 sqrtPriceX96,\n int24 tick,\n uint16 observationIndex,\n uint16 observationCardinality,\n uint16 observationCardinalityNext,\n bool unlocked\n );\n\n /// @notice The first of the two tokens of the pool, sorted by address\n /// @return The token contract address\n function token0() external view returns (address);\n\n /// @notice The second of the two tokens of the pool, sorted by address\n /// @return The token contract address\n function token1() external view returns (address);\n\n function tickSpacing() external view returns (int24);\n\n /// @notice The gauge corresponding to this pool\n /// @return The gauge contract address\n function gauge() external view returns (address);\n\n /// @notice The currently in range liquidity available to the pool\n /// @dev This value has no relationship to the total liquidity across all ticks\n /// @dev This value includes staked liquidity\n function liquidity() external view returns (uint128);\n\n /// @notice Look up information about a specific tick in the pool\n /// @param tick The tick to look up\n /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or\n /// tick upper,\n /// liquidityNet how much liquidity changes when the pool price crosses the tick,\n /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,\n /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,\n /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick\n /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from\n /// the current tick,\n /// secondsOutside the seconds spent on the other side of the tick from the current tick,\n /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise\n /// equal to false.\n /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.\n /// In addition, these values are only relative and must be used only in comparison to previous snapshots for\n /// a specific position.\n function ticks(int24 tick)\n external\n view\n returns (\n uint128 liquidityGross,\n int128 liquidityNet,\n uint256 feeGrowthOutside0X128,\n uint256 feeGrowthOutside1X128,\n int56 tickCumulativeOutside,\n uint160 secondsPerLiquidityOutsideX128,\n uint32 secondsOutside,\n bool initialized\n );\n}\n" + }, + "contracts/interfaces/aerodrome/INonfungiblePositionManager.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title Non-fungible token for positions\n/// @notice Wraps CL positions in a non-fungible token interface which allows for them to be transferred\n/// and authorized.\n// slither-disable-start erc20-interface\ninterface INonfungiblePositionManager {\n /**\n * @dev See {IERC721-approve}.\n */\n function approve(address to, uint256 tokenId) external;\n\n /**\n * @dev See {IERC721-getApproved}.\n */\n function getApproved(uint256 tokenId) external returns (address);\n\n /**\n * @dev See {IERC721-ownerOf}.\n */\n function ownerOf(uint256 tokenId) external view returns (address);\n\n /// @notice Returns the position information associated with a given token ID.\n /// @dev Throws if the token ID is not valid.\n /// @param tokenId The ID of the token that represents the position\n /// @return nonce The nonce for permits\n /// @return operator The address that is approved for spending\n /// @return token0 The address of the token0 for a specific pool\n /// @return token1 The address of the token1 for a specific pool\n /// @return tickSpacing The tick spacing associated with the pool\n /// @return tickLower The lower end of the tick range for the position\n /// @return tickUpper The higher end of the tick range for the position\n /// @return liquidity The liquidity of the position\n /// @return feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position\n /// @return feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position\n /// @return tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation\n /// @return tokensOwed1 The uncollected amount of token1 owed to the position as of the last computation\n function positions(uint256 tokenId)\n external\n view\n returns (\n uint96 nonce,\n address operator,\n address token0,\n address token1,\n int24 tickSpacing,\n int24 tickLower,\n int24 tickUpper,\n uint128 liquidity,\n uint256 feeGrowthInside0LastX128,\n uint256 feeGrowthInside1LastX128,\n uint128 tokensOwed0,\n uint128 tokensOwed1\n );\n\n struct MintParams {\n address token0;\n address token1;\n int24 tickSpacing;\n int24 tickLower;\n int24 tickUpper;\n uint256 amount0Desired;\n uint256 amount1Desired;\n uint256 amount0Min;\n uint256 amount1Min;\n address recipient;\n uint256 deadline;\n uint160 sqrtPriceX96;\n }\n\n /// @notice Creates a new position wrapped in a NFT\n /// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized\n /// a method does not exist, i.e. the pool is assumed to be initialized.\n /// @param params The params necessary to mint a position, encoded as `MintParams` in calldata\n /// @return tokenId The ID of the token that represents the minted position\n /// @return liquidity The amount of liquidity for this position\n /// @return amount0 The amount of token0\n /// @return amount1 The amount of token1\n function mint(MintParams calldata params)\n external\n payable\n returns (\n uint256 tokenId,\n uint128 liquidity,\n uint256 amount0,\n uint256 amount1\n );\n\n struct IncreaseLiquidityParams {\n uint256 tokenId;\n uint256 amount0Desired;\n uint256 amount1Desired;\n uint256 amount0Min;\n uint256 amount1Min;\n uint256 deadline;\n }\n\n /// @notice Increases the amount of liquidity in a position, with tokens paid by the `msg.sender`\n /// @param params tokenId The ID of the token for which liquidity is being increased,\n /// amount0Desired The desired amount of token0 to be spent,\n /// amount1Desired The desired amount of token1 to be spent,\n /// amount0Min The minimum amount of token0 to spend, which serves as a slippage check,\n /// amount1Min The minimum amount of token1 to spend, which serves as a slippage check,\n /// deadline The time by which the transaction must be included to effect the change\n /// @return liquidity The new liquidity amount as a result of the increase\n /// @return amount0 The amount of token0 to acheive resulting liquidity\n /// @return amount1 The amount of token1 to acheive resulting liquidity\n function increaseLiquidity(IncreaseLiquidityParams calldata params)\n external\n payable\n returns (\n uint128 liquidity,\n uint256 amount0,\n uint256 amount1\n );\n\n struct DecreaseLiquidityParams {\n uint256 tokenId;\n uint128 liquidity;\n uint256 amount0Min;\n uint256 amount1Min;\n uint256 deadline;\n }\n\n /// @notice Decreases the amount of liquidity in a position and accounts it to the position\n /// @param params tokenId The ID of the token for which liquidity is being decreased,\n /// amount The amount by which liquidity will be decreased,\n /// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity,\n /// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity,\n /// deadline The time by which the transaction must be included to effect the change\n /// @return amount0 The amount of token0 accounted to the position's tokens owed\n /// @return amount1 The amount of token1 accounted to the position's tokens owed\n /// @dev The use of this function can cause a loss to users of the NonfungiblePositionManager\n /// @dev for tokens that have very high decimals.\n /// @dev The amount of tokens necessary for the loss is: 3.4028237e+38.\n /// @dev This is equivalent to 1e20 value with 18 decimals.\n function decreaseLiquidity(DecreaseLiquidityParams calldata params)\n external\n payable\n returns (uint256 amount0, uint256 amount1);\n\n struct CollectParams {\n uint256 tokenId;\n address recipient;\n uint128 amount0Max;\n uint128 amount1Max;\n }\n\n /// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient\n /// @notice Used to update staked positions before deposit and withdraw\n /// @param params tokenId The ID of the NFT for which tokens are being collected,\n /// recipient The account that should receive the tokens,\n /// amount0Max The maximum amount of token0 to collect,\n /// amount1Max The maximum amount of token1 to collect\n /// @return amount0 The amount of fees collected in token0\n /// @return amount1 The amount of fees collected in token1\n function collect(CollectParams calldata params)\n external\n payable\n returns (uint256 amount0, uint256 amount1);\n\n /// @notice Burns a token ID, which deletes it from the NFT contract. The token must have 0 liquidity and all tokens\n /// must be collected first.\n /// @param tokenId The ID of the token that is being burned\n function burn(uint256 tokenId) external payable;\n\n /// @notice Sets a new Token Descriptor\n /// @param _tokenDescriptor Address of the new Token Descriptor to be chosen\n function setTokenDescriptor(address _tokenDescriptor) external;\n\n /// @notice Sets a new Owner address\n /// @param _owner Address of the new Owner to be chosen\n function setOwner(address _owner) external;\n}\n// slither-disable-end erc20-interface\n" + }, + "contracts/interfaces/aerodrome/IQuoterV2.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title QuoterV2 Interface\n/// @notice Supports quoting the calculated amounts from exact input or exact output swaps.\n/// @notice For each pool also tells you the number of initialized ticks crossed and the sqrt price of the\n/// pool after the swap.\n/// @dev These functions are not marked view because they rely on calling non-view functions and reverting\n/// to compute the result. They are also not gas efficient and should not be called on-chain.\ninterface IQuoterV2 {\n /// @notice Returns the amount out received for a given exact input swap without executing the swap\n /// @param path The path of the swap, i.e. each token pair and the pool tick spacing\n /// @param amountIn The amount of the first token to swap\n /// @return amountOut The amount of the last token that would be received\n /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path\n /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for\n /// each pool in the path\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactInput(bytes memory path, uint256 amountIn)\n external\n returns (\n uint256 amountOut,\n uint160[] memory sqrtPriceX96AfterList,\n uint32[] memory initializedTicksCrossedList,\n uint256 gasEstimate\n );\n\n struct QuoteExactInputSingleParams {\n address tokenIn;\n address tokenOut;\n uint256 amountIn;\n int24 tickSpacing;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Returns the amount out received for a given exact input but for a swap of a single pool\n /// @param params The params for the quote, encoded as `QuoteExactInputSingleParams`\n /// tokenIn The token being swapped in\n /// tokenOut The token being swapped out\n /// tickSpacing The tick spacing of the token pool to consider for the pair\n /// amountIn The desired input amount\n /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap\n /// @return amountOut The amount of `tokenOut` that would be received\n /// @return sqrtPriceX96After The sqrt price of the pool after the swap\n /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactInputSingle(QuoteExactInputSingleParams memory params)\n external\n returns (\n uint256 amountOut,\n uint160 sqrtPriceX96After,\n uint32 initializedTicksCrossed,\n uint256 gasEstimate\n );\n\n /// @notice Returns the amount in required for a given exact output swap without executing the swap\n /// @param path The path of the swap, i.e. each token pair and the pool tick spacing.\n /// Path must be provided in reverse order\n /// @param amountOut The amount of the last token to receive\n /// @return amountIn The amount of first token required to be paid\n /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path\n /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for\n /// each pool in the path\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactOutput(bytes memory path, uint256 amountOut)\n external\n returns (\n uint256 amountIn,\n uint160[] memory sqrtPriceX96AfterList,\n uint32[] memory initializedTicksCrossedList,\n uint256 gasEstimate\n );\n\n struct QuoteExactOutputSingleParams {\n address tokenIn;\n address tokenOut;\n uint256 amount;\n int24 tickSpacing;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Returns the amount in required to receive the given exact output amount but for a swap of a single pool\n /// @param params The params for the quote, encoded as `QuoteExactOutputSingleParams`\n /// tokenIn The token being swapped in\n /// tokenOut The token being swapped out\n /// tickSpacing The tick spacing of the token pool to consider for the pair\n /// amountOut The desired output amount\n /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap\n /// @return amountIn The amount required as the input for the swap in order to receive `amountOut`\n /// @return sqrtPriceX96After The sqrt price of the pool after the swap\n /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed\n /// @return gasEstimate The estimate of the gas that the swap consumes\n function quoteExactOutputSingle(QuoteExactOutputSingleParams memory params)\n external\n returns (\n uint256 amountIn,\n uint160 sqrtPriceX96After,\n uint32 initializedTicksCrossed,\n uint256 gasEstimate\n );\n}\n" + }, + "contracts/interfaces/aerodrome/ISugarHelper.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\npragma abicoder v2;\n\nimport { INonfungiblePositionManager } from \"./INonfungiblePositionManager.sol\";\n\ninterface ISugarHelper {\n struct PopulatedTick {\n int24 tick;\n uint160 sqrtRatioX96;\n int128 liquidityNet;\n uint128 liquidityGross;\n }\n\n ///\n /// Wrappers for LiquidityAmounts\n ///\n\n function getAmountsForLiquidity(\n uint160 sqrtRatioX96,\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96,\n uint128 liquidity\n ) external pure returns (uint256 amount0, uint256 amount1);\n\n function getLiquidityForAmounts(\n uint256 amount0,\n uint256 amount1,\n uint160 sqrtRatioX96,\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96\n ) external pure returns (uint128 liquidity);\n\n /// @notice Computes the amount of token0 for a given amount of token1 and price range\n /// @param amount1 Amount of token1 to estimate liquidity\n /// @param pool Address of the pool to be used\n /// @param sqrtRatioX96 A sqrt price representing the current pool prices\n /// @param tickLow Lower tick boundary\n /// @param tickLow Upper tick boundary\n /// @dev If the given pool address is not the zero address, will fetch `sqrtRatioX96` from pool\n /// @return amount0 Estimated amount of token0\n function estimateAmount0(\n uint256 amount1,\n address pool,\n uint160 sqrtRatioX96,\n int24 tickLow,\n int24 tickHigh\n ) external view returns (uint256 amount0);\n\n /// @notice Computes the amount of token1 for a given amount of token0 and price range\n /// @param amount0 Amount of token0 to estimate liquidity\n /// @param pool Address of the pool to be used\n /// @param sqrtRatioX96 A sqrt price representing the current pool prices\n /// @param tickLow Lower tick boundary\n /// @param tickLow Upper tick boundary\n /// @dev If the given pool address is not the zero address, will fetch `sqrtRatioX96` from pool\n /// @return amount1 Estimated amount of token1\n function estimateAmount1(\n uint256 amount0,\n address pool,\n uint160 sqrtRatioX96,\n int24 tickLow,\n int24 tickHigh\n ) external view returns (uint256 amount1);\n\n ///\n /// Wrappers for PositionValue\n ///\n\n function principal(\n INonfungiblePositionManager positionManager,\n uint256 tokenId,\n uint160 sqrtRatioX96\n ) external view returns (uint256 amount0, uint256 amount1);\n\n function fees(INonfungiblePositionManager positionManager, uint256 tokenId)\n external\n view\n returns (uint256 amount0, uint256 amount1);\n\n ///\n /// Wrappers for TickMath\n ///\n\n function getSqrtRatioAtTick(int24 tick)\n external\n pure\n returns (uint160 sqrtRatioX96);\n\n function getTickAtSqrtRatio(uint160 sqrtRatioX96)\n external\n pure\n returns (int24 tick);\n\n /// @notice Fetches Tick Data for all populated Ticks in given bitmaps\n /// @param pool Address of the pool from which to fetch data\n /// @param startTick Tick from which the first bitmap will be fetched\n /// @dev The number of bitmaps fetched by this function should always be `MAX_BITMAPS`,\n /// unless there are less than `MAX_BITMAPS` left to iterate through\n /// @return populatedTicks Array of all Populated Ticks in the provided bitmaps\n function getPopulatedTicks(address pool, int24 startTick)\n external\n view\n returns (PopulatedTick[] memory populatedTicks);\n}\n" + }, + "contracts/interfaces/aerodrome/ISwapRouter.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.5;\npragma abicoder v2;\n\n/// @title Router token swapping functionality\n/// @notice Functions for swapping tokens via CL\ninterface ISwapRouter {\n struct ExactInputSingleParams {\n address tokenIn;\n address tokenOut;\n int24 tickSpacing;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInputSingle(ExactInputSingleParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n\n struct ExactOutputSingleParams {\n address tokenIn;\n address tokenOut;\n int24 tickSpacing;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n uint160 sqrtPriceLimitX96;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another token\n /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutputSingle(ExactOutputSingleParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n\n struct ExactOutputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountOut;\n uint256 amountInMaximum;\n }\n\n /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata\n /// @return amountIn The amount of the input token\n function exactOutput(ExactOutputParams calldata params)\n external\n payable\n returns (uint256 amountIn);\n}\n" + }, + "contracts/interfaces/balancer/IBalancerVault.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"../../utils/InitializableAbstractStrategy.sol\";\n\ninterface IBalancerVault {\n enum WeightedPoolJoinKind {\n INIT,\n EXACT_TOKENS_IN_FOR_BPT_OUT,\n TOKEN_IN_FOR_EXACT_BPT_OUT,\n ALL_TOKENS_IN_FOR_EXACT_BPT_OUT,\n ADD_TOKEN\n }\n\n enum WeightedPoolExitKind {\n EXACT_BPT_IN_FOR_ONE_TOKEN_OUT,\n EXACT_BPT_IN_FOR_TOKENS_OUT,\n BPT_IN_FOR_EXACT_TOKENS_OUT,\n REMOVE_TOKEN\n }\n\n /**\n * @dev Called by users to join a Pool, which transfers tokens from `sender` into the Pool's balance. This will\n * trigger custom Pool behavior, which will typically grant something in return to `recipient` - often tokenized\n * Pool shares.\n *\n * If the caller is not `sender`, it must be an authorized relayer for them.\n *\n * The `assets` and `maxAmountsIn` arrays must have the same length, and each entry indicates the maximum amount\n * to send for each asset. The amounts to send are decided by the Pool and not the Vault: it just enforces\n * these maximums.\n *\n * If joining a Pool that holds WETH, it is possible to send ETH directly: the Vault will do the wrapping. To enable\n * this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead of the\n * WETH address. Note that it is not possible to combine ETH and WETH in the same join. Any excess ETH will be sent\n * back to the caller (not the sender, which is important for relayers).\n *\n * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when\n * interacting with Pools that register and deregister tokens frequently. If sending ETH however, the array must be\n * sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the final\n * `assets` array might not be sorted. Pools with no registered tokens cannot be joined.\n *\n * If `fromInternalBalance` is true, the caller's Internal Balance will be preferred: ERC20 transfers will only\n * be made for the difference between the requested amount and Internal Balance (if any). Note that ETH cannot be\n * withdrawn from Internal Balance: attempting to do so will trigger a revert.\n *\n * This causes the Vault to call the `IBasePool.onJoinPool` hook on the Pool's contract, where Pools implement\n * their own custom logic. This typically requires additional information from the user (such as the expected number\n * of Pool shares). This can be encoded in the `userData` argument, which is ignored by the Vault and passed\n * directly to the Pool's contract, as is `recipient`.\n *\n * Emits a `PoolBalanceChanged` event.\n */\n function joinPool(\n bytes32 poolId,\n address sender,\n address recipient,\n JoinPoolRequest memory request\n ) external payable;\n\n struct JoinPoolRequest {\n address[] assets;\n uint256[] maxAmountsIn;\n bytes userData;\n bool fromInternalBalance;\n }\n\n /**\n * @dev Called by users to exit a Pool, which transfers tokens from the Pool's balance to `recipient`. This will\n * trigger custom Pool behavior, which will typically ask for something in return from `sender` - often tokenized\n * Pool shares. The amount of tokens that can be withdrawn is limited by the Pool's `cash` balance (see\n * `getPoolTokenInfo`).\n *\n * If the caller is not `sender`, it must be an authorized relayer for them.\n *\n * The `tokens` and `minAmountsOut` arrays must have the same length, and each entry in these indicates the minimum\n * token amount to receive for each token contract. The amounts to send are decided by the Pool and not the Vault:\n * it just enforces these minimums.\n *\n * If exiting a Pool that holds WETH, it is possible to receive ETH directly: the Vault will do the unwrapping. To\n * enable this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead\n * of the WETH address. Note that it is not possible to combine ETH and WETH in the same exit.\n *\n * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when\n * interacting with Pools that register and deregister tokens frequently. If receiving ETH however, the array must\n * be sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the\n * final `assets` array might not be sorted. Pools with no registered tokens cannot be exited.\n *\n * If `toInternalBalance` is true, the tokens will be deposited to `recipient`'s Internal Balance. Otherwise,\n * an ERC20 transfer will be performed. Note that ETH cannot be deposited to Internal Balance: attempting to\n * do so will trigger a revert.\n *\n * `minAmountsOut` is the minimum amount of tokens the user expects to get out of the Pool, for each token in the\n * `tokens` array. This array must match the Pool's registered tokens.\n *\n * This causes the Vault to call the `IBasePool.onExitPool` hook on the Pool's contract, where Pools implement\n * their own custom logic. This typically requires additional information from the user (such as the expected number\n * of Pool shares to return). This can be encoded in the `userData` argument, which is ignored by the Vault and\n * passed directly to the Pool's contract.\n *\n * Emits a `PoolBalanceChanged` event.\n */\n function exitPool(\n bytes32 poolId,\n address sender,\n address payable recipient,\n ExitPoolRequest memory request\n ) external;\n\n struct ExitPoolRequest {\n address[] assets;\n uint256[] minAmountsOut;\n bytes userData;\n bool toInternalBalance;\n }\n\n /**\n * @dev Returns a Pool's registered tokens, the total balance for each, and the latest block when *any* of\n * the tokens' `balances` changed.\n *\n * The order of the `tokens` array is the same order that will be used in `joinPool`, `exitPool`, as well as in all\n * Pool hooks (where applicable). Calls to `registerTokens` and `deregisterTokens` may change this order.\n *\n * If a Pool only registers tokens once, and these are sorted in ascending order, they will be stored in the same\n * order as passed to `registerTokens`.\n *\n * Total balances include both tokens held by the Vault and those withdrawn by the Pool's Asset Managers. These are\n * the amounts used by joins, exits and swaps. For a detailed breakdown of token balances, use `getPoolTokenInfo`\n * instead.\n */\n function getPoolTokens(bytes32 poolId)\n external\n view\n returns (\n IERC20[] memory tokens,\n uint256[] memory balances,\n uint256 lastChangeBlock\n );\n\n /**\n * @dev Performs a set of user balance operations, which involve Internal Balance (deposit, withdraw or transfer)\n * and plain ERC20 transfers using the Vault's allowance. This last feature is particularly useful for relayers, as\n * it lets integrators reuse a user's Vault allowance.\n *\n * For each operation, if the caller is not `sender`, it must be an authorized relayer for them.\n */\n function manageUserBalance(UserBalanceOp[] memory ops) external payable;\n\n struct UserBalanceOp {\n UserBalanceOpKind kind;\n address asset;\n uint256 amount;\n address sender;\n address payable recipient;\n }\n\n enum UserBalanceOpKind {\n DEPOSIT_INTERNAL,\n WITHDRAW_INTERNAL,\n TRANSFER_INTERNAL,\n TRANSFER_EXTERNAL\n }\n\n enum SwapKind {\n GIVEN_IN,\n GIVEN_OUT\n }\n\n struct SingleSwap {\n bytes32 poolId;\n SwapKind kind;\n address assetIn;\n address assetOut;\n uint256 amount;\n bytes userData;\n }\n\n struct FundManagement {\n address sender;\n bool fromInternalBalance;\n address payable recipient;\n bool toInternalBalance;\n }\n\n function swap(\n SingleSwap calldata singleSwap,\n FundManagement calldata funds,\n uint256 limit,\n uint256 deadline\n ) external returns (uint256 amountCalculated);\n\n function getPoolTokenInfo(bytes32 poolId, address token)\n external\n view\n returns (\n uint256 cash,\n uint256 managed,\n uint256 lastChangeBlock,\n address assetManager\n );\n}\n" + }, + "contracts/interfaces/balancer/IMetaStablePool.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IRateProvider } from \"./IRateProvider.sol\";\n\ninterface IMetaStablePool {\n function getRateProviders()\n external\n view\n returns (IRateProvider[] memory providers);\n}\n" + }, + "contracts/interfaces/balancer/IOracleWeightedPool.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// The three values that can be queried:\n//\n// - PAIR_PRICE: the price of the tokens in the Pool, expressed as the price of the second token in units of the\n// first token. For example, if token A is worth $2, and token B is worth $4, the pair price will be 2.0.\n// Note that the price is computed *including* the tokens decimals. This means that the pair price of a Pool with\n// DAI and USDC will be close to 1.0, despite DAI having 18 decimals and USDC 6.\n//\n// - BPT_PRICE: the price of the Pool share token (BPT), in units of the first token.\n// Note that the price is computed *including* the tokens decimals. This means that the BPT price of a Pool with\n// USDC in which BPT is worth $5 will be 5.0, despite the BPT having 18 decimals and USDC 6.\n//\n// - INVARIANT: the value of the Pool's invariant, which serves as a measure of its liquidity.\nenum Variable {\n PAIR_PRICE,\n BPT_PRICE,\n INVARIANT\n}\n\n/**\n * @dev Information for a Time Weighted Average query.\n *\n * Each query computes the average over a window of duration `secs` seconds that ended `ago` seconds ago. For\n * example, the average over the past 30 minutes is computed by settings secs to 1800 and ago to 0. If secs is 1800\n * and ago is 1800 as well, the average between 60 and 30 minutes ago is computed instead.\n */\nstruct OracleAverageQuery {\n Variable variable;\n uint256 secs;\n uint256 ago;\n}\n\ninterface IOracleWeightedPool {\n /**\n * @dev Returns the time average weighted price corresponding to each of `queries`. Prices are represented as 18\n * decimal fixed point values.\n */\n function getTimeWeightedAverage(OracleAverageQuery[] memory queries)\n external\n view\n returns (uint256[] memory results);\n}\n" + }, + "contracts/interfaces/balancer/IRateProvider.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity ^0.8.0;\n\ninterface IRateProvider {\n function getRate() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/chainlink/AggregatorV3Interface.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorV3Interface {\n function decimals() external view returns (uint8);\n\n function description() external view returns (string memory);\n\n function version() external view returns (uint256);\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(uint80 _roundId)\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n\n function latestRoundData()\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n}\n" + }, + "contracts/interfaces/IBasicToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBasicToken {\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/IBeaconOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconOracle {\n function blockToSlot(uint64 blockNumber) external returns (uint64 slot);\n\n function slotToBlock(uint64 slot) external returns (uint64 blockNumber);\n\n function slotToRoot(uint64 slot) external returns (bytes32 blockRoot);\n}\n" + }, + "contracts/interfaces/IBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBeaconProofs {\n enum BalanceProofLevel {\n Container,\n BeaconBlock\n }\n\n function verifyValidatorPubkey(\n bytes32 beaconBlockRoot,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof,\n uint64 validatorIndex,\n address withdrawalAddress\n ) external view;\n\n function verifyBalancesContainer(\n bytes32 beaconBlockRoot,\n bytes32 balancesContainerLeaf,\n bytes calldata balancesContainerProof\n ) external view;\n\n function verifyValidatorBalance(\n bytes32 root,\n bytes32 validatorBalanceLeaf,\n bytes calldata balanceProof,\n uint64 validatorIndex,\n BalanceProofLevel level\n ) external view returns (uint256 validatorBalance);\n\n function verifyFirstPendingDepositSlot(\n bytes32 beaconBlockRoot,\n uint64 slot,\n bytes calldata firstPendingDepositSlotProof\n ) external view;\n\n function verifyBlockNumber(\n bytes32 beaconBlockRoot,\n uint256 blockNumber,\n bytes calldata blockNumberProof\n ) external view;\n\n function verifySlot(\n bytes32 beaconBlockRoot,\n uint256 slot,\n bytes calldata slotProof\n ) external view;\n\n function balanceAtIndex(bytes32 validatorBalanceLeaf, uint64 validatorIndex)\n external\n pure\n returns (uint256);\n\n function concatGenIndices(\n uint256 genIndex,\n uint256 height,\n uint256 index\n ) external pure returns (uint256);\n}\n" + }, + "contracts/interfaces/IBuyback.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IBuyback {\n function swap() external;\n}\n" + }, + "contracts/interfaces/ICampaignRemoteManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICampaignRemoteManager {\n function createCampaign(\n CampaignCreationParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n function manageCampaign(\n CampaignManagementParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n function closeCampaign(\n CampaignClosingParams memory params,\n uint256 destinationChainId,\n uint256 additionalGasLimit,\n address votemarket\n ) external payable;\n\n struct CampaignCreationParams {\n uint256 chainId;\n address gauge;\n address manager;\n address rewardToken;\n uint8 numberOfPeriods;\n uint256 maxRewardPerVote;\n uint256 totalRewardAmount;\n address[] addresses;\n address hook;\n bool isWhitelist;\n }\n\n struct CampaignManagementParams {\n uint256 campaignId;\n address rewardToken;\n uint8 numberOfPeriods;\n uint256 totalRewardAmount;\n uint256 maxRewardPerVote;\n }\n\n struct CampaignClosingParams {\n uint256 campaignId;\n }\n}\n" + }, + "contracts/interfaces/IChildLiquidityGaugeFactory.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IChildLiquidityGaugeFactory {\n event DeployedGauge(\n address indexed _implementation,\n address indexed _lp_token,\n address indexed _deployer,\n bytes32 _salt,\n address _gauge\n );\n event Minted(\n address indexed _user,\n address indexed _gauge,\n uint256 _new_total\n );\n event TransferOwnership(address _old_owner, address _new_owner);\n event UpdateCallProxy(address _old_call_proxy, address _new_call_proxy);\n event UpdateImplementation(\n address _old_implementation,\n address _new_implementation\n );\n event UpdateManager(address _manager);\n event UpdateMirrored(address indexed _gauge, bool _mirrored);\n event UpdateRoot(address _factory, address _implementation);\n event UpdateVotingEscrow(\n address _old_voting_escrow,\n address _new_voting_escrow\n );\n\n function accept_transfer_ownership() external;\n\n function call_proxy() external view returns (address);\n\n function commit_transfer_ownership(address _future_owner) external;\n\n function crv() external view returns (address);\n\n function deploy_gauge(address _lp_token, bytes32 _salt)\n external\n returns (address);\n\n function deploy_gauge(\n address _lp_token,\n bytes32 _salt,\n address _manager\n ) external returns (address);\n\n function future_owner() external view returns (address);\n\n function gauge_data(address arg0) external view returns (uint256);\n\n function get_gauge(uint256 arg0) external view returns (address);\n\n function get_gauge_count() external view returns (uint256);\n\n function get_gauge_from_lp_token(address arg0)\n external\n view\n returns (address);\n\n function get_implementation() external view returns (address);\n\n function is_mirrored(address _gauge) external view returns (bool);\n\n function is_valid_gauge(address _gauge) external view returns (bool);\n\n function last_request(address _gauge) external view returns (uint256);\n\n function manager() external view returns (address);\n\n function mint(address _gauge) external;\n\n function mint_many(address[32] memory _gauges) external;\n\n function minted(address arg0, address arg1) external view returns (uint256);\n\n function owner() external view returns (address);\n\n function root_factory() external view returns (address);\n\n function root_implementation() external view returns (address);\n\n function set_call_proxy(address _new_call_proxy) external;\n\n function set_crv(address _crv) external;\n\n function set_implementation(address _implementation) external;\n\n function set_manager(address _new_manager) external;\n\n function set_mirrored(address _gauge, bool _mirrored) external;\n\n function set_root(address _factory, address _implementation) external;\n\n function set_voting_escrow(address _voting_escrow) external;\n\n function version() external view returns (string memory);\n\n function voting_escrow() external view returns (address);\n}\n" + }, + "contracts/interfaces/IComptroller.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IComptroller {\n // Claim all the COMP accrued by specific holders in specific markets for their supplies and/or borrows\n function claimComp(\n address[] memory holders,\n address[] memory cTokens,\n bool borrowers,\n bool suppliers\n ) external;\n\n function oracle() external view returns (address);\n}\n" + }, + "contracts/interfaces/IConsolidation.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface IConsolidationSource {\n function confirmConsolidation()\n external\n returns (uint256 consolidationCount);\n}\n\ninterface IConsolidationTarget {\n function requestConsolidation(\n bytes32 lastSourcePubKeyHash,\n bytes32 targetPubKeyHash\n ) external;\n}\n" + }, + "contracts/interfaces/ICurveLiquidityGaugeV6.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveLiquidityGaugeV6 {\n event ApplyOwnership(address admin);\n event Approval(\n address indexed _owner,\n address indexed _spender,\n uint256 _value\n );\n event CommitOwnership(address admin);\n event Deposit(address indexed provider, uint256 value);\n event SetGaugeManager(address _gauge_manager);\n event Transfer(address indexed _from, address indexed _to, uint256 _value);\n event UpdateLiquidityLimit(\n address indexed user,\n uint256 original_balance,\n uint256 original_supply,\n uint256 working_balance,\n uint256 working_supply\n );\n event Withdraw(address indexed provider, uint256 value);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_reward(address _reward_token, address _distributor) external;\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function claim_rewards() external;\n\n function claim_rewards(address _addr) external;\n\n function claim_rewards(address _addr, address _receiver) external;\n\n function claimable_reward(address _user, address _reward_token)\n external\n view\n returns (uint256);\n\n function claimable_tokens(address addr) external returns (uint256);\n\n function claimed_reward(address _addr, address _token)\n external\n view\n returns (uint256);\n\n function decimals() external view returns (uint256);\n\n function decreaseAllowance(address _spender, uint256 _subtracted_value)\n external\n returns (bool);\n\n function deposit(uint256 _value) external;\n\n function deposit(uint256 _value, address _addr) external;\n\n function deposit(\n uint256 _value,\n address _addr,\n bool _claim_rewards\n ) external;\n\n function deposit_reward_token(address _reward_token, uint256 _amount)\n external;\n\n function deposit_reward_token(\n address _reward_token,\n uint256 _amount,\n uint256 _epoch\n ) external;\n\n function factory() external view returns (address);\n\n function future_epoch_time() external view returns (uint256);\n\n function increaseAllowance(address _spender, uint256 _added_value)\n external\n returns (bool);\n\n function inflation_rate() external view returns (uint256);\n\n function integrate_checkpoint() external view returns (uint256);\n\n function integrate_checkpoint_of(address arg0)\n external\n view\n returns (uint256);\n\n function integrate_fraction(address arg0) external view returns (uint256);\n\n function integrate_inv_supply(uint256 arg0) external view returns (uint256);\n\n function integrate_inv_supply_of(address arg0)\n external\n view\n returns (uint256);\n\n function is_killed() external view returns (bool);\n\n function kick(address addr) external;\n\n function lp_token() external view returns (address);\n\n function manager() external view returns (address);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function period() external view returns (int128);\n\n function period_timestamp(uint256 arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function reward_count() external view returns (uint256);\n\n function reward_integral_for(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function reward_tokens(uint256 arg0) external view returns (address);\n\n function rewards_receiver(address arg0) external view returns (address);\n\n function salt() external view returns (bytes32);\n\n function set_gauge_manager(address _gauge_manager) external;\n\n function set_killed(bool _is_killed) external;\n\n function set_reward_distributor(address _reward_token, address _distributor)\n external;\n\n function set_rewards_receiver(address _receiver) external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function user_checkpoint(address addr) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw(uint256 _value) external;\n\n function withdraw(uint256 _value, bool _claim_rewards) external;\n\n function working_balances(address arg0) external view returns (uint256);\n\n function working_supply() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/ICurveMinter.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveMinter {\n event Minted(address indexed recipient, address gauge, uint256 minted);\n\n function allowed_to_mint_for(address arg0, address arg1)\n external\n view\n returns (bool);\n\n function controller() external view returns (address);\n\n function mint(address gauge_addr) external;\n\n function mint_for(address gauge_addr, address _for) external;\n\n function mint_many(address[8] memory gauge_addrs) external;\n\n function minted(address arg0, address arg1) external view returns (uint256);\n\n function toggle_approve_mint(address minting_user) external;\n\n function token() external view returns (address);\n}\n" + }, + "contracts/interfaces/ICurveStableSwapNG.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveStableSwapNG {\n event AddLiquidity(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event ApplyNewFee(uint256 fee, uint256 offpeg_fee_multiplier);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event RampA(\n uint256 old_A,\n uint256 new_A,\n uint256 initial_time,\n uint256 future_time\n );\n event RemoveLiquidity(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 token_supply\n );\n event RemoveLiquidityImbalance(\n address indexed provider,\n uint256[] token_amounts,\n uint256[] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event RemoveLiquidityOne(\n address indexed provider,\n int128 token_id,\n uint256 token_amount,\n uint256 coin_amount,\n uint256 token_supply\n );\n event SetNewMATime(uint256 ma_exp_time, uint256 D_ma_time);\n event StopRampA(uint256 A, uint256 t);\n event TokenExchange(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event TokenExchangeUnderlying(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event Transfer(\n address indexed sender,\n address indexed receiver,\n uint256 value\n );\n\n function A() external view returns (uint256);\n\n function A_precise() external view returns (uint256);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function D_ma_time() external view returns (uint256);\n\n function D_oracle() external view returns (uint256);\n\n function N_COINS() external view returns (uint256);\n\n function add_liquidity(uint256[] memory _amounts, uint256 _min_mint_amount)\n external\n returns (uint256);\n\n function add_liquidity(\n uint256[] memory _amounts,\n uint256 _min_mint_amount,\n address _receiver\n ) external returns (uint256);\n\n function admin_balances(uint256 arg0) external view returns (uint256);\n\n function admin_fee() external view returns (uint256);\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function balances(uint256 i) external view returns (uint256);\n\n function calc_token_amount(uint256[] memory _amounts, bool _is_deposit)\n external\n view\n returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _burn_amount, int128 i)\n external\n view\n returns (uint256);\n\n function coins(uint256 arg0) external view returns (address);\n\n function decimals() external view returns (uint8);\n\n function dynamic_fee(int128 i, int128 j) external view returns (uint256);\n\n function ema_price(uint256 i) external view returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external returns (uint256);\n\n function exchange_received(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external returns (uint256);\n\n function exchange_received(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external returns (uint256);\n\n function fee() external view returns (uint256);\n\n function future_A() external view returns (uint256);\n\n function future_A_time() external view returns (uint256);\n\n function get_balances() external view returns (uint256[] memory);\n\n function get_dx(\n int128 i,\n int128 j,\n uint256 dy\n ) external view returns (uint256);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n\n function get_p(uint256 i) external view returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function initial_A() external view returns (uint256);\n\n function initial_A_time() external view returns (uint256);\n\n function last_price(uint256 i) external view returns (uint256);\n\n function ma_exp_time() external view returns (uint256);\n\n function ma_last_time() external view returns (uint256);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function offpeg_fee_multiplier() external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function price_oracle(uint256 i) external view returns (uint256);\n\n function ramp_A(uint256 _future_A, uint256 _future_time) external;\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts\n ) external returns (uint256[] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts,\n address _receiver\n ) external returns (uint256[] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[] memory _min_amounts,\n address _receiver,\n bool _claim_admin_fees\n ) external returns (uint256[] memory);\n\n function remove_liquidity_imbalance(\n uint256[] memory _amounts,\n uint256 _max_burn_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[] memory _amounts,\n uint256 _max_burn_amount,\n address _receiver\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received,\n address _receiver\n ) external returns (uint256);\n\n function salt() external view returns (bytes32);\n\n function set_ma_exp_time(uint256 _ma_exp_time, uint256 _D_ma_time) external;\n\n function set_new_fee(uint256 _new_fee, uint256 _new_offpeg_fee_multiplier)\n external;\n\n function stop_ramp_A() external;\n\n function stored_rates() external view returns (uint256[] memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw_admin_fees() external;\n}\n" + }, + "contracts/interfaces/ICurveXChainLiquidityGauge.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.4;\n\ninterface ICurveXChainLiquidityGauge {\n event Approval(\n address indexed _owner,\n address indexed _spender,\n uint256 _value\n );\n event Deposit(address indexed provider, uint256 value);\n event SetGaugeManager(address _gauge_manager);\n event Transfer(address indexed _from, address indexed _to, uint256 _value);\n event UpdateLiquidityLimit(\n address indexed user,\n uint256 original_balance,\n uint256 original_supply,\n uint256 working_balance,\n uint256 working_supply\n );\n event Withdraw(address indexed provider, uint256 value);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_reward(address _reward_token, address _distributor) external;\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function claim_rewards() external;\n\n function claim_rewards(address _addr) external;\n\n function claim_rewards(address _addr, address _receiver) external;\n\n function claimable_reward(address _user, address _reward_token)\n external\n view\n returns (uint256);\n\n function claimable_tokens(address addr) external returns (uint256);\n\n function claimed_reward(address _addr, address _token)\n external\n view\n returns (uint256);\n\n function decimals() external view returns (uint256);\n\n function decreaseAllowance(address _spender, uint256 _subtracted_value)\n external\n returns (bool);\n\n function deposit(uint256 _value) external;\n\n function deposit(uint256 _value, address _addr) external;\n\n function deposit(\n uint256 _value,\n address _addr,\n bool _claim_rewards\n ) external;\n\n function deposit_reward_token(address _reward_token, uint256 _amount)\n external;\n\n function deposit_reward_token(\n address _reward_token,\n uint256 _amount,\n uint256 _epoch\n ) external;\n\n function factory() external view returns (address);\n\n function increaseAllowance(address _spender, uint256 _added_value)\n external\n returns (bool);\n\n function inflation_rate(uint256 arg0) external view returns (uint256);\n\n function initialize(\n address _lp_token,\n address _root,\n address _manager\n ) external;\n\n function integrate_checkpoint() external view returns (uint256);\n\n function integrate_checkpoint_of(address arg0)\n external\n view\n returns (uint256);\n\n function integrate_fraction(address arg0) external view returns (uint256);\n\n function integrate_inv_supply(int128 arg0) external view returns (uint256);\n\n function integrate_inv_supply_of(address arg0)\n external\n view\n returns (uint256);\n\n function is_killed() external view returns (bool);\n\n function lp_token() external view returns (address);\n\n function manager() external view returns (address);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function period() external view returns (int128);\n\n function period_timestamp(int128 arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function recover_remaining(address _reward_token) external;\n\n function reward_count() external view returns (uint256);\n\n function reward_integral_for(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function reward_remaining(address arg0) external view returns (uint256);\n\n function reward_tokens(uint256 arg0) external view returns (address);\n\n function rewards_receiver(address arg0) external view returns (address);\n\n function root_gauge() external view returns (address);\n\n function set_gauge_manager(address _gauge_manager) external;\n\n function set_killed(bool _is_killed) external;\n\n function set_manager(address _gauge_manager) external;\n\n function set_reward_distributor(address _reward_token, address _distributor)\n external;\n\n function set_rewards_receiver(address _receiver) external;\n\n function set_root_gauge(address _root) external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function update_voting_escrow() external;\n\n function user_checkpoint(address addr) external returns (bool);\n\n function version() external view returns (string memory);\n\n function voting_escrow() external view returns (address);\n\n function withdraw(uint256 _value) external;\n\n function withdraw(uint256 _value, bool _claim_rewards) external;\n\n function withdraw(\n uint256 _value,\n bool _claim_rewards,\n address _receiver\n ) external;\n\n function working_balances(address arg0) external view returns (uint256);\n\n function working_supply() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/ICVXLocker.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface ICVXLocker {\n function lock(\n address _account,\n uint256 _amount,\n uint256 _spendRatio\n ) external;\n\n function lockedBalanceOf(address _account) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IDepositContract.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDepositContract {\n /// @notice A processed deposit event.\n event DepositEvent(\n bytes pubkey,\n bytes withdrawal_credentials,\n bytes amount,\n bytes signature,\n bytes index\n );\n\n /// @notice Submit a Phase 0 DepositData object.\n /// @param pubkey A BLS12-381 public key.\n /// @param withdrawal_credentials Commitment to a public key for withdrawals.\n /// @param signature A BLS12-381 signature.\n /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.\n /// Used as a protection against malformed input.\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable;\n\n /// @notice Query the current deposit root hash.\n /// @return The deposit root hash.\n function get_deposit_root() external view returns (bytes32);\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view returns (bytes memory);\n}\n" + }, + "contracts/interfaces/IDripper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IDripper {\n /// @notice How much funds have dripped out already and are currently\n // available to be sent to the vault.\n /// @return The amount that would be sent if a collect was called\n function availableFunds() external view returns (uint256);\n\n /// @notice Collect all dripped funds and send to vault.\n /// Recalculate new drip rate.\n function collect() external;\n\n /// @notice Collect all dripped funds, send to vault, recalculate new drip\n /// rate, and rebase mToken.\n function collectAndRebase() external;\n\n /// @notice Change the drip duration. Governor only.\n /// @param _durationSeconds the number of seconds to drip out the entire\n /// balance over if no collects were called during that time.\n function setDripDuration(uint256 _durationSeconds) external;\n\n /// @dev Transfer out ERC20 tokens held by the contract. Governor only.\n /// @param _asset ERC20 token address\n /// @param _amount amount to transfer\n function transferToken(address _asset, uint256 _amount) external;\n}\n" + }, + "contracts/interfaces/IEthUsdOracle.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IEthUsdOracle {\n /**\n * @notice Returns ETH price in USD.\n * @return Price in USD with 6 decimal digits.\n */\n function ethUsdPrice() external view returns (uint256);\n\n /**\n * @notice Returns token price in USD.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in USD with 6 decimal digits.\n */\n function tokUsdPrice(string calldata symbol)\n external\n view\n returns (uint256);\n\n /**\n * @notice Returns the asset price in ETH.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in ETH with 8 decimal digits.\n */\n function tokEthPrice(string calldata symbol)\n external\n view\n returns (uint256);\n}\n\ninterface IViewEthUsdOracle {\n /**\n * @notice Returns ETH price in USD.\n * @return Price in USD with 6 decimal digits.\n */\n function ethUsdPrice() external view returns (uint256);\n\n /**\n * @notice Returns token price in USD.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in USD with 6 decimal digits.\n */\n function tokUsdPrice(string calldata symbol)\n external\n view\n returns (uint256);\n\n /**\n * @notice Returns the asset price in ETH.\n * @param symbol. Asset symbol. For ex. \"DAI\".\n * @return Price in ETH with 8 decimal digits.\n */\n function tokEthPrice(string calldata symbol)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/IFraxETHMinter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IFraxETHMinter {\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares);\n}\n" + }, + "contracts/interfaces/IGetExchangeRateToken.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IGetExchangeRateToken {\n function getExchangeRate() external view returns (uint256 _exchangeRate);\n}\n" + }, + "contracts/interfaces/IMinMaxOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IMinMaxOracle {\n //Assuming 8 decimals\n function priceMin(string calldata symbol) external view returns (uint256);\n\n function priceMax(string calldata symbol) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IMockVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"./IVault.sol\";\n\ninterface IMockVault is IVault {\n function outstandingWithdrawalsAmount() external view returns (uint256);\n\n function wethAvailable() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IOETHZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOETHZapper {\n function deposit() external payable returns (uint256);\n}\n" + }, + "contracts/interfaces/IOneInch.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/// 1Inch swap data\nstruct SwapDescription {\n IERC20 srcToken; // contract address of a token to sell\n IERC20 dstToken; // contract address of a token to buy\n address payable srcReceiver;\n address payable dstReceiver; // Receiver of destination currency. default: fromAddress\n uint256 amount;\n uint256 minReturnAmount;\n uint256 flags;\n}\n\n/// @title Interface for making arbitrary calls during swap\ninterface IAggregationExecutor {\n /// @notice propagates information about original msg.sender and executes arbitrary data\n function execute(address msgSender) external payable; // 0x4b64e492\n}\n\ninterface IOneInchRouter {\n /// @notice Performs a swap, delegating all calls encoded in `data` to `executor`.\n function swap(\n IAggregationExecutor executor,\n SwapDescription calldata desc,\n bytes calldata permit,\n bytes calldata data\n ) external returns (uint256 returnAmount, uint256 spentAmount);\n\n /// @notice Performs swap using Uniswap exchange. Wraps and unwraps ETH if required.\n function unoswapTo(\n address payable recipient,\n IERC20 srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) external payable returns (uint256 returnAmount);\n\n /// @notice Performs swap using Uniswap V3 exchange. Wraps and unwraps ETH if required.\n function uniswapV3SwapTo(\n address payable recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) external payable returns (uint256 returnAmount);\n}\n" + }, + "contracts/interfaces/IOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOracle {\n /**\n * @dev returns the asset price in USD, in 8 decimal digits.\n *\n * The version of priceProvider deployed for OETH has 18 decimal digits\n */\n function price(address asset) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IOUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IOUSD {\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event GovernorshipTransferred(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n event PendingGovernorshipTransfer(\n address indexed previousGovernor,\n address indexed newGovernor\n );\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n function _totalSupply() external view returns (uint256);\n\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address _account) external view returns (uint256);\n\n function burn(address account, uint256 amount) external;\n\n function changeSupply(uint256 _newTotalSupply) external;\n\n function claimGovernance() external;\n\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256);\n\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n );\n\n function decimals() external view returns (uint8);\n\n function decreaseAllowance(address _spender, uint256 _subtractedValue)\n external\n returns (bool);\n\n function governor() external view returns (address);\n\n function increaseAllowance(address _spender, uint256 _addedValue)\n external\n returns (bool);\n\n function initialize(\n string memory _nameArg,\n string memory _symbolArg,\n address _vaultAddress\n ) external;\n\n function isGovernor() external view returns (bool);\n\n function isUpgraded(address) external view returns (uint256);\n\n function mint(address _account, uint256 _amount) external;\n\n function name() external view returns (string memory);\n\n function nonRebasingCreditsPerToken(address)\n external\n view\n returns (uint256);\n\n function nonRebasingSupply() external view returns (uint256);\n\n function rebaseOptIn() external;\n\n function rebaseOptOut() external;\n\n function rebaseState(address) external view returns (uint8);\n\n function rebasingCredits() external view returns (uint256);\n\n function rebasingCreditsHighres() external view returns (uint256);\n\n function rebasingCreditsPerToken() external view returns (uint256);\n\n function rebasingCreditsPerTokenHighres() external view returns (uint256);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function transferGovernance(address _newGovernor) external;\n\n function vaultAddress() external view returns (address);\n}\n" + }, + "contracts/interfaces/IPriceOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPriceOracle {\n /**\n * @dev returns the asset price in USD, 6 decimal digits.\n * Compatible with the Open Price Feed.\n */\n function price(string calldata symbol) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/IRETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IRETH {\n function getEthValue(uint256 _rethAmount) external view returns (uint256);\n\n function getRethValue(uint256 _ethAmount) external view returns (uint256);\n\n function getExchangeRate() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function balanceOf(address account) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 amount) external returns (bool);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/interfaces/ISafe.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ISafe {\n function execTransactionFromModule(\n address,\n uint256,\n bytes memory,\n uint8\n ) external returns (bool);\n}\n" + }, + "contracts/interfaces/ISfrxETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface ISfrxETH {\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 amount\n );\n event Deposit(\n address indexed caller,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n event NewRewardsCycle(uint32 indexed cycleEnd, uint256 rewardAmount);\n event Transfer(address indexed from, address indexed to, uint256 amount);\n event Withdraw(\n address indexed caller,\n address indexed receiver,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address spender, uint256 amount) external returns (bool);\n\n function asset() external view returns (address);\n\n function balanceOf(address) external view returns (uint256);\n\n function convertToAssets(uint256 shares) external view returns (uint256);\n\n function convertToShares(uint256 assets) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit(uint256 assets, address receiver)\n external\n returns (uint256 shares);\n\n function depositWithSignature(\n uint256 assets,\n address receiver,\n uint256 deadline,\n bool approveMax,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external returns (uint256 shares);\n\n function lastRewardAmount() external view returns (uint192);\n\n function lastSync() external view returns (uint32);\n\n function maxDeposit(address) external view returns (uint256);\n\n function maxMint(address) external view returns (uint256);\n\n function maxRedeem(address owner) external view returns (uint256);\n\n function maxWithdraw(address owner) external view returns (uint256);\n\n function mint(uint256 shares, address receiver)\n external\n returns (uint256 assets);\n\n function name() external view returns (string memory);\n\n function nonces(address) external view returns (uint256);\n\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function previewDeposit(uint256 assets) external view returns (uint256);\n\n function previewMint(uint256 shares) external view returns (uint256);\n\n function previewRedeem(uint256 shares) external view returns (uint256);\n\n function previewWithdraw(uint256 assets) external view returns (uint256);\n\n function pricePerShare() external view returns (uint256);\n\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets);\n\n function rewardsCycleEnd() external view returns (uint32);\n\n function rewardsCycleLength() external view returns (uint32);\n\n function symbol() external view returns (string memory);\n\n function syncRewards() external;\n\n function totalAssets() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address to, uint256 amount) external returns (bool);\n\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares);\n}\n" + }, + "contracts/interfaces/ISSVNetwork.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct Cluster {\n uint32 validatorCount;\n uint64 networkFeeIndex;\n uint64 index;\n bool active;\n uint256 balance;\n}\n\ninterface ISSVNetwork {\n /**********/\n /* Errors */\n /**********/\n\n error CallerNotOwner(); // 0x5cd83192\n error CallerNotWhitelisted(); // 0x8c6e5d71\n error FeeTooLow(); // 0x732f9413\n error FeeExceedsIncreaseLimit(); // 0x958065d9\n error NoFeeDeclared(); // 0x1d226c30\n error ApprovalNotWithinTimeframe(); // 0x97e4b518\n error OperatorDoesNotExist(); // 0x961e3e8c\n error InsufficientBalance(); // 0xf4d678b8\n error ValidatorDoesNotExist(); // 0xe51315d2\n error ClusterNotLiquidatable(); // 0x60300a8d\n error InvalidPublicKeyLength(); // 0x637297a4\n error InvalidOperatorIdsLength(); // 0x38186224\n error ClusterAlreadyEnabled(); // 0x3babafd2\n error ClusterIsLiquidated(); // 0x95a0cf33\n error ClusterDoesNotExists(); // 0x185e2b16\n error IncorrectClusterState(); // 0x12e04c87\n error UnsortedOperatorsList(); // 0xdd020e25\n error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac\n error ExceedValidatorLimit(); // 0x6df5ab76\n error TokenTransferFailed(); // 0x045c4b02\n error SameFeeChangeNotAllowed(); // 0xc81272f8\n error FeeIncreaseNotAllowed(); // 0x410a2b6c\n error NotAuthorized(); // 0xea8e4eb5\n error OperatorsListNotUnique(); // 0xa5a1ff5d\n error OperatorAlreadyExists(); // 0x289c9494\n error TargetModuleDoesNotExist(); // 0x8f9195fb\n error MaxValueExceeded(); // 0x91aa3017\n error FeeTooHigh(); // 0xcd4e6167\n error PublicKeysSharesLengthMismatch(); // 0x9ad467b8\n error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938\n error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999\n error EmptyPublicKeysList(); // df83e679\n\n // legacy errors\n error ValidatorAlreadyExists(); // 0x8d09a73e\n error IncorrectValidatorState(); // 0x2feda3c1\n\n event AdminChanged(address previousAdmin, address newAdmin);\n event BeaconUpgraded(address indexed beacon);\n event ClusterDeposited(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event ClusterLiquidated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterReactivated(\n address indexed owner,\n uint64[] operatorIds,\n Cluster cluster\n );\n event ClusterWithdrawn(\n address indexed owner,\n uint64[] operatorIds,\n uint256 value,\n Cluster cluster\n );\n event DeclareOperatorFeePeriodUpdated(uint64 value);\n event ExecuteOperatorFeePeriodUpdated(uint64 value);\n event FeeRecipientAddressUpdated(\n address indexed owner,\n address recipientAddress\n );\n event Initialized(uint8 version);\n event LiquidationThresholdPeriodUpdated(uint64 value);\n event MinimumLiquidationCollateralUpdated(uint256 value);\n event NetworkEarningsWithdrawn(uint256 value, address recipient);\n event NetworkFeeUpdated(uint256 oldFee, uint256 newFee);\n event OperatorAdded(\n uint64 indexed operatorId,\n address indexed owner,\n bytes publicKey,\n uint256 fee\n );\n event OperatorFeeDeclarationCancelled(\n address indexed owner,\n uint64 indexed operatorId\n );\n event OperatorFeeDeclared(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeExecuted(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 blockNumber,\n uint256 fee\n );\n event OperatorFeeIncreaseLimitUpdated(uint64 value);\n event OperatorMaximumFeeUpdated(uint64 maxFee);\n event OperatorRemoved(uint64 indexed operatorId);\n event OperatorWhitelistUpdated(\n uint64 indexed operatorId,\n address whitelisted\n );\n event OperatorWithdrawn(\n address indexed owner,\n uint64 indexed operatorId,\n uint256 value\n );\n event OwnershipTransferStarted(\n address indexed previousOwner,\n address indexed newOwner\n );\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n event Upgraded(address indexed implementation);\n event ValidatorAdded(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n bytes shares,\n Cluster cluster\n );\n event ValidatorExited(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey\n );\n event ValidatorRemoved(\n address indexed owner,\n uint64[] operatorIds,\n bytes publicKey,\n Cluster cluster\n );\n\n fallback() external;\n\n function acceptOwnership() external;\n\n function cancelDeclaredOperatorFee(uint64 operatorId) external;\n\n function declareOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function deposit(\n address clusterOwner,\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function executeOperatorFee(uint64 operatorId) external;\n\n function exitValidator(bytes memory publicKey, uint64[] memory operatorIds)\n external;\n\n function bulkExitValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds\n ) external;\n\n function getVersion() external pure returns (string memory version);\n\n function initialize(\n address token_,\n address ssvOperators_,\n address ssvClusters_,\n address ssvDAO_,\n address ssvViews_,\n uint64 minimumBlocksBeforeLiquidation_,\n uint256 minimumLiquidationCollateral_,\n uint32 validatorsPerOperatorLimit_,\n uint64 declareOperatorFeePeriod_,\n uint64 executeOperatorFeePeriod_,\n uint64 operatorMaxFeeIncrease_\n ) external;\n\n function liquidate(\n address clusterOwner,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function owner() external view returns (address);\n\n function pendingOwner() external view returns (address);\n\n function proxiableUUID() external view returns (bytes32);\n\n function reactivate(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function reduceOperatorFee(uint64 operatorId, uint256 fee) external;\n\n function registerOperator(bytes memory publicKey, uint256 fee)\n external\n returns (uint64 id);\n\n function registerValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n bytes memory sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function removeOperator(uint64 operatorId) external;\n\n function removeValidator(\n bytes memory publicKey,\n uint64[] memory operatorIds,\n Cluster memory cluster\n ) external;\n\n function bulkRemoveValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external;\n\n function renounceOwnership() external;\n\n function setFeeRecipientAddress(address recipientAddress) external;\n\n function setOperatorWhitelist(uint64 operatorId, address whitelisted)\n external;\n\n function transferOwnership(address newOwner) external;\n\n function updateDeclareOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateExecuteOperatorFeePeriod(uint64 timeInSeconds) external;\n\n function updateLiquidationThresholdPeriod(uint64 blocks) external;\n\n function updateMaximumOperatorFee(uint64 maxFee) external;\n\n function updateMinimumLiquidationCollateral(uint256 amount) external;\n\n function updateModule(uint8 moduleId, address moduleAddress) external;\n\n function updateNetworkFee(uint256 fee) external;\n\n function updateOperatorFeeIncreaseLimit(uint64 percentage) external;\n\n function upgradeTo(address newImplementation) external;\n\n function upgradeToAndCall(address newImplementation, bytes memory data)\n external\n payable;\n\n function withdraw(\n uint64[] memory operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external;\n\n function withdrawAllOperatorEarnings(uint64 operatorId) external;\n\n function withdrawNetworkEarnings(uint256 amount) external;\n\n function withdrawOperatorEarnings(uint64 operatorId, uint256 amount)\n external;\n}\n" + }, + "contracts/interfaces/IStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Platform interface to integrate with lending platform like Compound, AAVE etc.\n */\ninterface IStrategy {\n /**\n * @dev Deposit the given asset to platform\n * @param _asset asset address\n * @param _amount Amount to deposit\n */\n function deposit(address _asset, uint256 _amount) external;\n\n /**\n * @dev Deposit the entire balance of all supported assets in the Strategy\n * to the platform\n */\n function depositAll() external;\n\n /**\n * @dev Withdraw given asset from Lending platform\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external;\n\n /**\n * @dev Liquidate all assets in strategy and return them to Vault.\n */\n function withdrawAll() external;\n\n /**\n * @dev Returns the current balance of the given asset.\n */\n function checkBalance(address _asset)\n external\n view\n returns (uint256 balance);\n\n /**\n * @dev Returns bool indicating whether strategy supports asset.\n */\n function supportsAsset(address _asset) external view returns (bool);\n\n /**\n * @dev Collect reward tokens from the Strategy.\n */\n function collectRewardTokens() external;\n\n /**\n * @dev The address array of the reward tokens for the Strategy.\n */\n function getRewardTokenAddresses() external view returns (address[] memory);\n\n function harvesterAddress() external view returns (address);\n\n function transferToken(address token, uint256 amount) external;\n\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external;\n}\n" + }, + "contracts/interfaces/ISwapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ISwapper {\n /**\n * @param fromAsset The token address of the asset being sold.\n * @param toAsset The token address of the asset being purchased.\n * @param fromAssetAmount The amount of assets being sold.\n * @param minToAssetAmmount The minimum amount of assets to be purchased.\n * @param data tx.data returned from 1Inch's /v5.0/1/swap API\n */\n function swap(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n}\n" + }, + "contracts/interfaces/ITimelock.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ITimelock {\n function delay() external view returns (uint256);\n\n function GRACE_PERIOD() external view returns (uint256);\n\n function acceptAdmin() external;\n\n function queuedTransactions(bytes32 hash) external view returns (bool);\n\n function queueTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external returns (bytes32);\n\n function cancelTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external;\n\n function executeTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external payable returns (bytes memory);\n}\n" + }, + "contracts/interfaces/ITimelockController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ITimelockController {\n function grantRole(bytes32 role, address account) external;\n\n function revokeRole(bytes32 role, address account) external;\n\n function renounceRole(bytes32 role, address account) external;\n\n function hasRole(bytes32 role, address account)\n external\n view\n returns (bool);\n\n function executeBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt\n ) external payable;\n\n function scheduleBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt,\n uint256 delay\n ) external;\n\n function hashOperationBatch(\n address[] calldata targets,\n uint256[] calldata values,\n bytes[] calldata payloads,\n bytes32 predecessor,\n bytes32 salt\n ) external view returns (bytes32);\n\n function isOperationDone(bytes32 opHash) external view returns (bool);\n\n function isOperationReady(bytes32 opHash) external view returns (bool);\n\n function isOperation(bytes32 opHash) external view returns (bool);\n\n function getMinDelay() external view returns (uint256);\n\n function updateDelay(uint256 newDelay) external;\n\n function CANCELLER_ROLE() external view returns (bytes32);\n}\n" + }, + "contracts/interfaces/IVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultStorage } from \"../vault/VaultStorage.sol\";\n\ninterface IVault {\n // slither-disable-start constable-states\n\n event AssetSupported(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event DripperChanged(address indexed _dripper);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n\n // Governable.sol\n function transferGovernance(address _newGovernor) external;\n\n function claimGovernance() external;\n\n function governor() external view returns (address);\n\n function ADMIN_IMPLEMENTATION() external view returns (address);\n\n // VaultAdmin.sol\n function setPriceProvider(address _priceProvider) external;\n\n function priceProvider() external view returns (address);\n\n function setRedeemFeeBps(uint256 _redeemFeeBps) external;\n\n function redeemFeeBps() external view returns (uint256);\n\n function setVaultBuffer(uint256 _vaultBuffer) external;\n\n function vaultBuffer() external view returns (uint256);\n\n function setAutoAllocateThreshold(uint256 _threshold) external;\n\n function autoAllocateThreshold() external view returns (uint256);\n\n function setRebaseThreshold(uint256 _threshold) external;\n\n function rebaseThreshold() external view returns (uint256);\n\n function setStrategistAddr(address _address) external;\n\n function strategistAddr() external view returns (address);\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;\n\n function maxSupplyDiff() external view returns (uint256);\n\n function setTrusteeAddress(address _address) external;\n\n function trusteeAddress() external view returns (address);\n\n function setTrusteeFeeBps(uint256 _basis) external;\n\n function trusteeFeeBps() external view returns (uint256);\n\n function ousdMetaStrategy() external view returns (address);\n\n function setSwapper(address _swapperAddr) external;\n\n function setSwapAllowedUndervalue(uint16 _percentageBps) external;\n\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external;\n\n function supportAsset(address _asset, uint8 _unitConversion) external;\n\n function approveStrategy(address _addr) external;\n\n function removeStrategy(address _addr) external;\n\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external;\n\n function assetDefaultStrategies(address _asset)\n external\n view\n returns (address);\n\n function pauseRebase() external;\n\n function unpauseRebase() external;\n\n function rebasePaused() external view returns (bool);\n\n function pauseCapital() external;\n\n function unpauseCapital() external;\n\n function capitalPaused() external view returns (bool);\n\n function transferToken(address _asset, uint256 _amount) external;\n\n function priceUnitMint(address asset) external view returns (uint256);\n\n function priceUnitRedeem(address asset) external view returns (uint256);\n\n function withdrawAllFromStrategy(address _strategyAddr) external;\n\n function withdrawAllFromStrategies() external;\n\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external;\n\n // VaultCore.sol\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external;\n\n function mintForStrategy(uint256 _amount) external;\n\n function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;\n\n function burnForStrategy(uint256 _amount) external;\n\n function allocate() external;\n\n function rebase() external;\n\n function swapCollateral(\n address fromAsset,\n address toAsset,\n uint256 fromAssetAmount,\n uint256 minToAssetAmount,\n bytes calldata data\n ) external returns (uint256 toAssetAmount);\n\n function totalValue() external view returns (uint256 value);\n\n function checkBalance(address _asset) external view returns (uint256);\n\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory);\n\n function getAssetCount() external view returns (uint256);\n\n function getAssetConfig(address _asset)\n external\n view\n returns (VaultStorage.Asset memory config);\n\n function getAllAssets() external view returns (address[] memory);\n\n function getStrategyCount() external view returns (uint256);\n\n function swapper() external view returns (address);\n\n function allowedSwapUndervalue() external view returns (uint256);\n\n function getAllStrategies() external view returns (address[] memory);\n\n function isSupportedAsset(address _asset) external view returns (bool);\n\n function netOusdMintForStrategyThreshold() external view returns (uint256);\n\n function setOusdMetaStrategy(address _ousdMetaStrategy) external;\n\n function setNetOusdMintForStrategyThreshold(uint256 _threshold) external;\n\n function netOusdMintedForStrategy() external view returns (int256);\n\n function setDripper(address _dripper) external;\n\n function dripper() external view returns (address);\n\n function weth() external view returns (address);\n\n function cacheWETHAssetIndex() external;\n\n function wethAssetIndex() external view returns (uint256);\n\n function initialize(address, address) external;\n\n function setAdminImpl(address) external;\n\n function removeAsset(address _asset) external;\n\n // These are OETH specific functions\n function addWithdrawalQueueLiquidity() external;\n\n function requestWithdrawal(uint256 _amount)\n external\n returns (uint256 requestId, uint256 queued);\n\n function claimWithdrawal(uint256 requestId)\n external\n returns (uint256 amount);\n\n function claimWithdrawals(uint256[] memory requestIds)\n external\n returns (uint256[] memory amounts, uint256 totalAmount);\n\n function withdrawalQueueMetadata()\n external\n view\n returns (VaultStorage.WithdrawalQueueMetadata memory);\n\n function withdrawalRequests(uint256 requestId)\n external\n view\n returns (VaultStorage.WithdrawalRequest memory);\n\n // OETHb specific functions\n function addStrategyToMintWhitelist(address strategyAddr) external;\n\n function removeStrategyFromMintWhitelist(address strategyAddr) external;\n\n function isMintWhitelistedStrategy(address strategyAddr)\n external\n view\n returns (bool);\n\n function withdrawalClaimDelay() external view returns (uint256);\n\n function setWithdrawalClaimDelay(uint256 newDelay) external;\n\n function lastRebase() external view returns (uint64);\n\n function dripDuration() external view returns (uint64);\n\n function setDripDuration(uint256 _dripDuration) external;\n\n function rebasePerSecondMax() external view returns (uint64);\n\n function setRebaseRateMax(uint256 yearlyApr) external;\n\n function rebasePerSecondTarget() external view returns (uint64);\n\n function previewYield() external view returns (uint256 yield);\n\n // slither-disable-end constable-states\n}\n" + }, + "contracts/interfaces/IWETH9.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH9 {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n function allowance(address, address) external view returns (uint256);\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function balanceOf(address) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function transferFrom(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/interfaces/IWstETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWstETH {\n /**\n * @notice Get amount of wstETH for a given amount of stETH\n * @param _stETHAmount amount of stETH\n * @return Amount of wstETH for a given stETH amount\n */\n function getWstETHByStETH(uint256 _stETHAmount)\n external\n view\n returns (uint256);\n\n /**\n * @notice Get amount of stETH for a given amount of wstETH\n * @param _wstETHAmount amount of wstETH\n * @return Amount of stETH for a given wstETH amount\n */\n function getStETHByWstETH(uint256 _wstETHAmount)\n external\n view\n returns (uint256);\n\n /**\n * @notice Get amount of stETH for a one wstETH\n * @return Amount of stETH for 1 wstETH\n */\n function stEthPerToken() external view returns (uint256);\n\n /**\n * @notice Get amount of wstETH for a one stETH\n * @return Amount of wstETH for a 1 stETH\n */\n function tokensPerStEth() external view returns (uint256);\n\n /**\n * @notice Exchanges stETH to wstETH\n * @param _stETHAmount amount of stETH to wrap in exchange for wstETH\n * @dev Requirements:\n * - `_stETHAmount` must be non-zero\n * - msg.sender must approve at least `_stETHAmount` stETH to this\n * contract.\n * - msg.sender must have at least `_stETHAmount` of stETH.\n * User should first approve _stETHAmount to the WstETH contract\n * @return Amount of wstETH user receives after wrap\n */\n function wrap(uint256 _stETHAmount) external returns (uint256);\n\n /**\n * @notice Exchanges wstETH to stETH\n * @param _wstETHAmount amount of wstETH to uwrap in exchange for stETH\n * @dev Requirements:\n * - `_wstETHAmount` must be non-zero\n * - msg.sender must have at least `_wstETHAmount` wstETH.\n * @return Amount of stETH user receives after unwrap\n */\n function unwrap(uint256 _wstETHAmount) external returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/compound/ICompoundOracle.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\ninterface ICompoundOracle {\n function getUnderlyingPrice(address) external view returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/ILens.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\nimport \"./compound/ICompoundOracle.sol\";\nimport \"./IMorpho.sol\";\n\ninterface ILens {\n /// STORAGE ///\n\n function MAX_BASIS_POINTS() external view returns (uint256);\n\n function WAD() external view returns (uint256);\n\n function morpho() external view returns (IMorpho);\n\n function comptroller() external view returns (IComptroller);\n\n /// GENERAL ///\n\n function getTotalSupply()\n external\n view\n returns (\n uint256 p2pSupplyAmount,\n uint256 poolSupplyAmount,\n uint256 totalSupplyAmount\n );\n\n function getTotalBorrow()\n external\n view\n returns (\n uint256 p2pBorrowAmount,\n uint256 poolBorrowAmount,\n uint256 totalBorrowAmount\n );\n\n /// MARKETS ///\n\n function isMarketCreated(address _poolToken) external view returns (bool);\n\n function isMarketCreatedAndNotPaused(address _poolToken)\n external\n view\n returns (bool);\n\n function isMarketCreatedAndNotPausedNorPartiallyPaused(address _poolToken)\n external\n view\n returns (bool);\n\n function getAllMarkets()\n external\n view\n returns (address[] memory marketsCreated_);\n\n function getMainMarketData(address _poolToken)\n external\n view\n returns (\n uint256 avgSupplyRatePerBlock,\n uint256 avgBorrowRatePerBlock,\n uint256 p2pSupplyAmount,\n uint256 p2pBorrowAmount,\n uint256 poolSupplyAmount,\n uint256 poolBorrowAmount\n );\n\n function getAdvancedMarketData(address _poolToken)\n external\n view\n returns (\n uint256 p2pSupplyIndex,\n uint256 p2pBorrowIndex,\n uint256 poolSupplyIndex,\n uint256 poolBorrowIndex,\n uint32 lastUpdateBlockNumber,\n uint256 p2pSupplyDelta,\n uint256 p2pBorrowDelta\n );\n\n function getMarketConfiguration(address _poolToken)\n external\n view\n returns (\n address underlying,\n bool isCreated,\n bool p2pDisabled,\n bool isPaused,\n bool isPartiallyPaused,\n uint16 reserveFactor,\n uint16 p2pIndexCursor,\n uint256 collateralFactor\n );\n\n function getTotalMarketSupply(address _poolToken)\n external\n view\n returns (uint256 p2pSupplyAmount, uint256 poolSupplyAmount);\n\n function getTotalMarketBorrow(address _poolToken)\n external\n view\n returns (uint256 p2pBorrowAmount, uint256 poolBorrowAmount);\n\n /// INDEXES ///\n\n function getCurrentP2PSupplyIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentP2PBorrowIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentPoolIndexes(address _poolToken)\n external\n view\n returns (\n uint256 currentPoolSupplyIndex,\n uint256 currentPoolBorrowIndex\n );\n\n function getIndexes(address _poolToken, bool _computeUpdatedIndexes)\n external\n view\n returns (\n uint256 p2pSupplyIndex,\n uint256 p2pBorrowIndex,\n uint256 poolSupplyIndex,\n uint256 poolBorrowIndex\n );\n\n /// USERS ///\n\n function getEnteredMarkets(address _user)\n external\n view\n returns (address[] memory enteredMarkets);\n\n function getUserHealthFactor(\n address _user,\n address[] calldata _updatedMarkets\n ) external view returns (uint256);\n\n function getUserBalanceStates(\n address _user,\n address[] calldata _updatedMarkets\n )\n external\n view\n returns (\n uint256 collateralValue,\n uint256 debtValue,\n uint256 maxDebtValue\n );\n\n function getCurrentSupplyBalanceInOf(address _poolToken, address _user)\n external\n view\n returns (\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getCurrentBorrowBalanceInOf(address _poolToken, address _user)\n external\n view\n returns (\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getUserMaxCapacitiesForAsset(address _user, address _poolToken)\n external\n view\n returns (uint256 withdrawable, uint256 borrowable);\n\n function getUserHypotheticalBalanceStates(\n address _user,\n address _poolToken,\n uint256 _withdrawnAmount,\n uint256 _borrowedAmount\n ) external view returns (uint256 debtValue, uint256 maxDebtValue);\n\n function getUserLiquidityDataForAsset(\n address _user,\n address _poolToken,\n bool _computeUpdatedIndexes,\n ICompoundOracle _oracle\n ) external view returns (Types.AssetLiquidityData memory assetData);\n\n function isLiquidatable(address _user, address[] memory _updatedMarkets)\n external\n view\n returns (bool);\n\n function computeLiquidationRepayAmount(\n address _user,\n address _poolTokenBorrowed,\n address _poolTokenCollateral,\n address[] calldata _updatedMarkets\n ) external view returns (uint256 toRepay);\n\n /// RATES ///\n\n function getAverageSupplyRatePerBlock(address _poolToken)\n external\n view\n returns (\n uint256 avgSupplyRatePerBlock,\n uint256 p2pSupplyAmount,\n uint256 poolSupplyAmount\n );\n\n function getAverageBorrowRatePerBlock(address _poolToken)\n external\n view\n returns (\n uint256 avgBorrowRatePerBlock,\n uint256 p2pBorrowAmount,\n uint256 poolBorrowAmount\n );\n\n function getNextUserSupplyRatePerBlock(\n address _poolToken,\n address _user,\n uint256 _amount\n )\n external\n view\n returns (\n uint256 nextSupplyRatePerBlock,\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getNextUserBorrowRatePerBlock(\n address _poolToken,\n address _user,\n uint256 _amount\n )\n external\n view\n returns (\n uint256 nextBorrowRatePerBlock,\n uint256 balanceOnPool,\n uint256 balanceInP2P,\n uint256 totalBalance\n );\n\n function getCurrentUserSupplyRatePerBlock(address _poolToken, address _user)\n external\n view\n returns (uint256);\n\n function getCurrentUserBorrowRatePerBlock(address _poolToken, address _user)\n external\n view\n returns (uint256);\n\n function getRatesPerBlock(address _poolToken)\n external\n view\n returns (\n uint256 p2pSupplyRate,\n uint256 p2pBorrowRate,\n uint256 poolSupplyRate,\n uint256 poolBorrowRate\n );\n\n /// REWARDS ///\n\n function getUserUnclaimedRewards(\n address[] calldata _poolTokens,\n address _user\n ) external view returns (uint256 unclaimedRewards);\n\n function getAccruedSupplierComp(\n address _supplier,\n address _poolToken,\n uint256 _balance\n ) external view returns (uint256);\n\n function getAccruedBorrowerComp(\n address _borrower,\n address _poolToken,\n uint256 _balance\n ) external view returns (uint256);\n\n function getCurrentCompSupplyIndex(address _poolToken)\n external\n view\n returns (uint256);\n\n function getCurrentCompBorrowIndex(address _poolToken)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/morpho/IMorpho.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\nimport \"./Types.sol\";\nimport \"../IComptroller.sol\";\nimport \"./compound/ICompoundOracle.sol\";\n\n// prettier-ignore\ninterface IMorpho {\n function comptroller() external view returns (IComptroller);\n function supply(address _poolTokenAddress, address _onBehalf, uint256 _amount) external;\n function supply(address _poolTokenAddress, address _onBehalf, uint256 _amount, uint256 _maxGasForMatching) external;\n function withdraw(address _poolTokenAddress, uint256 _amount) external;\n function claimRewards(\n address[] calldata _cTokenAddresses,\n bool _tradeForMorphoToken\n ) external returns (uint256 claimedAmount);\n}\n" + }, + "contracts/interfaces/morpho/Types.sol": { + "content": "// SPDX-License-Identifier: GNU AGPLv3\npragma solidity ^0.8.0;\n\n/// @title Types.\n/// @author Morpho Labs.\n/// @custom:contact security@morpho.xyz\n/// @dev Common types and structs used in Moprho contracts.\nlibrary Types {\n /// ENUMS ///\n\n enum PositionType {\n SUPPLIERS_IN_P2P,\n SUPPLIERS_ON_POOL,\n BORROWERS_IN_P2P,\n BORROWERS_ON_POOL\n }\n\n /// STRUCTS ///\n\n struct SupplyBalance {\n uint256 inP2P; // In supplier's peer-to-peer unit, a unit that grows in underlying value, to keep track of the interests earned by suppliers in peer-to-peer. Multiply by the peer-to-peer supply index to get the underlying amount.\n uint256 onPool; // In cToken. Multiply by the pool supply index to get the underlying amount.\n }\n\n struct BorrowBalance {\n uint256 inP2P; // In borrower's peer-to-peer unit, a unit that grows in underlying value, to keep track of the interests paid by borrowers in peer-to-peer. Multiply by the peer-to-peer borrow index to get the underlying amount.\n uint256 onPool; // In cdUnit, a unit that grows in value, to keep track of the debt increase when borrowers are on Compound. Multiply by the pool borrow index to get the underlying amount.\n }\n\n // Max gas to consume during the matching process for supply, borrow, withdraw and repay functions.\n struct MaxGasForMatching {\n uint64 supply;\n uint64 borrow;\n uint64 withdraw;\n uint64 repay;\n }\n\n struct Delta {\n uint256 p2pSupplyDelta; // Difference between the stored peer-to-peer supply amount and the real peer-to-peer supply amount (in pool supply unit).\n uint256 p2pBorrowDelta; // Difference between the stored peer-to-peer borrow amount and the real peer-to-peer borrow amount (in pool borrow unit).\n uint256 p2pSupplyAmount; // Sum of all stored peer-to-peer supply (in peer-to-peer supply unit).\n uint256 p2pBorrowAmount; // Sum of all stored peer-to-peer borrow (in peer-to-peer borrow unit).\n }\n\n struct AssetLiquidityData {\n uint256 collateralValue; // The collateral value of the asset.\n uint256 maxDebtValue; // The maximum possible debt value of the asset.\n uint256 debtValue; // The debt value of the asset.\n uint256 underlyingPrice; // The price of the token.\n uint256 collateralFactor; // The liquidation threshold applied on this token.\n }\n\n struct LiquidityData {\n uint256 collateralValue; // The collateral value.\n uint256 maxDebtValue; // The maximum debt value possible.\n uint256 debtValue; // The debt value.\n }\n\n // Variables are packed together to save gas (will not exceed their limit during Morpho's lifetime).\n struct LastPoolIndexes {\n uint32 lastUpdateBlockNumber; // The last time the peer-to-peer indexes were updated.\n uint112 lastSupplyPoolIndex; // Last pool supply index.\n uint112 lastBorrowPoolIndex; // Last pool borrow index.\n }\n\n struct MarketParameters {\n uint16 reserveFactor; // Proportion of the interest earned by users sent to the DAO for each market, in basis point (100% = 10 000). The value is set at market creation.\n uint16 p2pIndexCursor; // Position of the peer-to-peer rate in the pool's spread. Determine the weights of the weighted arithmetic average in the indexes computations ((1 - p2pIndexCursor) * r^S + p2pIndexCursor * r^B) (in basis point).\n }\n\n struct MarketStatus {\n bool isCreated; // Whether or not this market is created.\n bool isPaused; // Whether the market is paused or not (all entry points on Morpho are frozen; supply, borrow, withdraw, repay and liquidate).\n bool isPartiallyPaused; // Whether the market is partially paused or not (only supply and borrow are frozen).\n }\n}\n" + }, + "contracts/interfaces/plume/IFeeRegistry.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\ninterface IFeeRegistry {\n function registerFee(\n bool isTokenA,\n uint32 binId,\n uint256 binFeeInQuote\n ) external;\n}\n" + }, + "contracts/interfaces/plume/ILiquidityRegistry.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface ILiquidityRegistry {\n function notifyBinLiquidity(\n IMaverickV2Pool pool,\n uint256 tokenId,\n uint32 binId,\n uint256 currentBinLpBalance\n ) external;\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Factory.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IFeeRegistry } from \"./IFeeRegistry.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2Factory {\n error FactorAlreadyInitialized();\n error FactorNotInitialized();\n error FactoryInvalidTokenOrder(IERC20 _tokenA, IERC20 _tokenB);\n error FactoryInvalidFee();\n error FactoryInvalidKinds(uint8 kinds);\n error FactoryInvalidTickSpacing(uint256 tickSpacing);\n error FactoryInvalidLookback(uint256 lookback);\n error FactoryInvalidTokenDecimals(uint8 decimalsA, uint8 decimalsB);\n error FactoryPoolAlreadyExists(\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds,\n address accessor\n );\n error FactoryAccessorMustBeNonZero();\n error NotImplemented();\n\n event PoolCreated(\n IMaverickV2Pool poolAddress,\n uint8 protocolFeeRatio,\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n int32 activeTick,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds,\n address accessor\n );\n event SetFactoryProtocolFeeReceiver(address receiver);\n event SetFactoryProtocolFeeRegistry(IFeeRegistry registry);\n\n struct DeployParameters {\n uint64 feeAIn;\n uint64 feeBIn;\n uint32 lookback;\n int32 activeTick;\n uint64 tokenAScale;\n uint64 tokenBScale;\n // slot\n IERC20 tokenA;\n // slot\n IERC20 tokenB;\n // slot\n uint16 tickSpacing;\n uint8 options;\n address accessor;\n }\n\n /**\n * @notice Called by deployer library to initialize a pool.\n */\n function deployParameters()\n external\n view\n returns (\n uint64 feeAIn,\n uint64 feeBIn,\n uint32 lookback,\n int32 activeTick,\n uint64 tokenAScale,\n uint64 tokenBScale,\n // slot\n IERC20 tokenA,\n // slot\n IERC20 tokenB,\n // slot\n uint16 tickSpacing,\n uint8 options,\n address accessor\n );\n\n /**\n * @notice Create a new MaverickV2Pool with symmetric swap fees.\n * @param fee Fraction of the pool swap amount that is retained as an LP in\n * D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * E.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function create(\n uint64 fee,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external returns (IMaverickV2Pool);\n\n /**\n * @notice Create a new MaverickV2Pool.\n * @param feeAIn Fraction of the pool swap amount for tokenA-input swaps\n * that is retained as an LP in D18 scale.\n * @param feeBIn Fraction of the pool swap amount for tokenB-input swaps\n * that is retained as an LP in D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * e.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function create(\n uint64 feeAIn,\n uint64 feeBIn,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external returns (IMaverickV2Pool);\n\n /**\n * @notice Bool indicating whether the pool was deployed from this factory.\n */\n function isFactoryPool(IMaverickV2Pool pool) external view returns (bool);\n\n /**\n * @notice Address that receives the protocol fee\n */\n function protocolFeeReceiver() external view returns (address);\n\n /**\n * @notice Address notified on swaps of the protocol fee\n */\n function protocolFeeRegistry() external view returns (IFeeRegistry);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(\n uint256 feeAIn,\n uint256 feeBIn,\n uint256 tickSpacing,\n uint256 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n uint8 kinds\n ) external view returns (IMaverickV2Pool);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(\n IERC20 _tokenA,\n IERC20 _tokenB,\n uint256 startIndex,\n uint256 endIndex\n ) external view returns (IMaverickV2Pool[] memory pools);\n\n /**\n * @notice Lookup a pool for given parameters.\n */\n function lookup(uint256 startIndex, uint256 endIndex)\n external\n view\n returns (IMaverickV2Pool[] memory pools);\n\n /**\n * @notice Count of permissionless pools.\n */\n function poolCount() external view returns (uint256 _poolCount);\n\n /**\n * @notice Count of pools for a given accessor and token pair. For\n * permissionless pools, pass `accessor = address(0)`.\n */\n function poolByTokenCount(\n IERC20 _tokenA,\n IERC20 _tokenB,\n address accessor\n ) external view returns (uint256 _poolCount);\n\n /**\n * @notice Get the current factory owner.\n */\n function owner() external view returns (address);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2LiquidityManager.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\nimport { IMaverickV2Position } from \"./IMaverickV2Position.sol\";\nimport { IMaverickV2PoolLens } from \"./IMaverickV2PoolLens.sol\";\n\ninterface IMaverickV2LiquidityManager {\n error LiquidityManagerNotFactoryPool();\n error LiquidityManagerNotTokenIdOwner();\n\n /**\n * @notice Maverick V2 NFT position contract that tracks NFT-based\n * liquditiy positions.\n */\n function position() external view returns (IMaverickV2Position);\n\n /**\n * @notice Create Maverick V2 pool. Function is a pass through to the pool\n * factory and is provided here so that is can be assembled as part of a\n * multicall transaction.\n */\n function createPool(\n uint64 fee,\n uint16 tickSpacing,\n uint32 lookback,\n IERC20 tokenA,\n IERC20 tokenB,\n int32 activeTick,\n uint8 kinds\n ) external payable returns (IMaverickV2Pool pool);\n\n /**\n * @notice Add Liquidity position NFT for msg.sender by specifying\n * msg.sender's token index.\n * @dev Token index is different from tokenId.\n * On the Position NFT contract a user can own multiple NFT tokenIds and\n * these are indexes by an enumeration index which is the `index` input\n * here.\n *\n * See addLiquidity for a description of the add params.\n */\n function addPositionLiquidityToSenderByTokenIndex(\n IMaverickV2Pool pool,\n uint256 index,\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs\n )\n external\n payable\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds\n );\n\n /**\n * @notice Mint new tokenId in the Position NFt contract to msg.sender.\n * Both mints an NFT and adds liquidity to the pool that is held by the\n * NFT.\n */\n function mintPositionNftToSender(\n IMaverickV2Pool pool,\n bytes calldata packedSqrtPriceBreaks,\n bytes[] calldata packedArgs\n )\n external\n payable\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds,\n uint256 tokenId\n );\n\n /**\n * @notice Donates liqudity to a pool that is held by the position contract\n * and will never be retrievable. Can be used to start a pool and ensure\n * there will always be a base level of liquditiy in the pool.\n */\n function donateLiquidity(\n IMaverickV2Pool pool,\n IMaverickV2Pool.AddLiquidityParams memory args\n ) external payable;\n\n /**\n * @notice Packs sqrtPrice breaks array with this format: [length,\n * array[0], array[1],..., array[length-1]] where length is 1 byte.\n */\n function packUint88Array(uint88[] memory fullArray)\n external\n pure\n returns (bytes memory packedArray);\n\n /**\n * @notice Packs addLiquidity paramters array element-wise.\n */\n function packAddLiquidityArgsArray(\n IMaverickV2Pool.AddLiquidityParams[] memory args\n ) external pure returns (bytes[] memory argsPacked);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Pool.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\n\ninterface IMaverickV2Pool {\n error PoolZeroLiquidityAdded();\n error PoolMinimumLiquidityNotMet();\n error PoolLocked();\n error PoolInvalidFee();\n error PoolTicksNotSorted(uint256 index, int256 previousTick, int256 tick);\n error PoolTicksAmountsLengthMismatch(\n uint256 ticksLength,\n uint256 amountsLength\n );\n error PoolBinIdsAmountsLengthMismatch(\n uint256 binIdsLength,\n uint256 amountsLength\n );\n error PoolKindNotSupported(uint256 kinds, uint256 kind);\n error PoolInsufficientBalance(\n uint256 deltaLpAmount,\n uint256 accountBalance\n );\n error PoolReservesExceedMaximum(uint256 amount);\n error PoolValueExceedsBits(uint256 amount, uint256 bits);\n error PoolTickMaxExceeded(uint256 tick);\n error PoolMigrateBinFirst();\n error PoolCurrentTickBeyondSwapLimit(int32 startingTick);\n error PoolSenderNotAccessor(address sender_, address accessor);\n error PoolSenderNotFactory(address sender_, address accessor);\n error PoolFunctionNotImplemented();\n error PoolTokenNotSolvent(\n uint256 internalReserve,\n uint256 tokenBalance,\n IERC20 token\n );\n error PoolNoProtocolFeeReceiverSet();\n\n event PoolSwap(\n address sender,\n address recipient,\n SwapParams params,\n uint256 amountIn,\n uint256 amountOut\n );\n event PoolFlashLoan(\n address sender,\n address recipient,\n uint256 amountA,\n uint256 amountB\n );\n event PoolProtocolFeeCollected(IERC20 token, uint256 protocolFee);\n\n event PoolAddLiquidity(\n address sender,\n address recipient,\n uint256 subaccount,\n AddLiquidityParams params,\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] binIds\n );\n\n event PoolMigrateBinsUpStack(\n address sender,\n uint32 binId,\n uint32 maxRecursion\n );\n\n event PoolRemoveLiquidity(\n address sender,\n address recipient,\n uint256 subaccount,\n RemoveLiquidityParams params,\n uint256 tokenAOut,\n uint256 tokenBOut\n );\n\n event PoolTickState(int32 tick, uint256 reserveA, uint256 reserveB);\n event PoolTickBinUpdate(int32 tick, uint8 kind, uint32 binId);\n event PoolSqrtPrice(uint256 sqrtPrice);\n\n /**\n * @notice Tick state parameters.\n */\n struct TickState {\n uint128 reserveA;\n uint128 reserveB;\n uint128 totalSupply;\n uint32[4] binIdsByTick;\n }\n\n /**\n * @notice Tick data parameters.\n * @param currentReserveA Current reserve of token A.\n * @param currentReserveB Current reserve of token B.\n * @param currentLiquidity Current liquidity amount.\n */\n struct TickData {\n uint256 currentReserveA;\n uint256 currentReserveB;\n uint256 currentLiquidity;\n }\n\n /**\n * @notice Bin state parameters.\n * @param mergeBinBalance LP token balance that this bin possesses of the merge bin.\n * @param mergeId Bin ID of the bin that this bin has merged into.\n * @param totalSupply Total amount of LP tokens in this bin.\n * @param kind One of the 4 kinds (0=static, 1=right, 2=left, 3=both).\n * @param tick The lower price tick of the bin in its current state.\n * @param tickBalance Balance of the tick.\n */\n struct BinState {\n uint128 mergeBinBalance;\n uint128 tickBalance;\n uint128 totalSupply;\n uint8 kind;\n int32 tick;\n uint32 mergeId;\n }\n\n /**\n * @notice Parameters for swap.\n * @param amount Amount of the token that is either the input if exactOutput is false\n * or the output if exactOutput is true.\n * @param tokenAIn Boolean indicating whether tokenA is the input.\n * @param exactOutput Boolean indicating whether the amount specified is\n * the exact output amount (true).\n * @param tickLimit The furthest tick a swap will execute in. If no limit\n * is desired, value should be set to type(int32).max for a tokenAIn swap\n * and type(int32).min for a swap where tokenB is the input.\n */\n struct SwapParams {\n uint256 amount;\n bool tokenAIn;\n bool exactOutput;\n int32 tickLimit;\n }\n\n /**\n * @notice Parameters associated with adding liquidity.\n * @param kind One of the 4 kinds (0=static, 1=right, 2=left, 3=both).\n * @param ticks Array of ticks to add liquidity to.\n * @param amounts Array of bin LP amounts to add.\n */\n struct AddLiquidityParams {\n uint8 kind;\n int32[] ticks;\n uint128[] amounts;\n }\n\n /**\n * @notice Parameters for each bin that will have liquidity removed.\n * @param binIds Index array of the bins losing liquidity.\n * @param amounts Array of bin LP amounts to remove.\n */\n struct RemoveLiquidityParams {\n uint32[] binIds;\n uint128[] amounts;\n }\n\n /**\n * @notice State of the pool.\n * @param reserveA Pool tokenA balanceOf at end of last operation\n * @param reserveB Pool tokenB balanceOf at end of last operation\n * @param lastTwaD8 Value of log time weighted average price at last block.\n * Value is 8-decimal scale and is in the fractional tick domain. E.g. a\n * value of 12.3e8 indicates the TWAP was 3/10ths of the way into the 12th\n * tick.\n * @param lastLogPriceD8 Value of log price at last block. Value is\n * 8-decimal scale and is in the fractional tick domain. E.g. a value of\n * 12.3e8 indicates the price was 3/10ths of the way into the 12th tick.\n * @param lastTimestamp Last block.timestamp value in seconds for latest\n * swap transaction.\n * @param activeTick Current tick position that contains the active bins.\n * @param isLocked Pool isLocked, E.g., locked or unlocked; isLocked values\n * defined in Pool.sol.\n * @param binCounter Index of the last bin created.\n * @param protocolFeeRatioD3 Ratio of the swap fee that is kept for the\n * protocol.\n */\n struct State {\n uint128 reserveA;\n uint128 reserveB;\n int64 lastTwaD8;\n int64 lastLogPriceD8;\n uint40 lastTimestamp;\n int32 activeTick;\n bool isLocked;\n uint32 binCounter;\n uint8 protocolFeeRatioD3;\n }\n\n /**\n * @notice Internal data used for data passing between Pool and Bin code.\n */\n struct BinDelta {\n uint128 deltaA;\n uint128 deltaB;\n }\n\n /**\n * @notice 1-15 number to represent the active kinds.\n * @notice 0b0001 = static;\n * @notice 0b0010 = right;\n * @notice 0b0100 = left;\n * @notice 0b1000 = both;\n *\n * E.g. a pool with all 4 modes will have kinds = b1111 = 15\n */\n function kinds() external view returns (uint8 _kinds);\n\n /**\n * @notice Pool swap fee for the given direction (A-in or B-in swap) in\n * 18-decimal format. E.g. 0.01e18 is a 1% swap fee.\n */\n function fee(bool tokenAIn) external view returns (uint256);\n\n /**\n * @notice TickSpacing of pool where 1.0001^tickSpacing is the bin width.\n */\n function tickSpacing() external view returns (uint256);\n\n /**\n * @notice Lookback period of pool in seconds.\n */\n function lookback() external view returns (uint256);\n\n /**\n * @notice Address of Pool accessor. This is Zero address for\n * permissionless pools.\n */\n function accessor() external view returns (address);\n\n /**\n * @notice Pool tokenA. Address of tokenA is such that tokenA < tokenB.\n */\n function tokenA() external view returns (IERC20);\n\n /**\n * @notice Pool tokenB.\n */\n function tokenB() external view returns (IERC20);\n\n /**\n * @notice Deploying factory of the pool and also contract that has ability\n * to set and collect protocol fees for the pool.\n */\n function factory() external view returns (IMaverickV2Factory);\n\n /**\n * @notice Most significant bit of scale value is a flag to indicate whether\n * tokenA has more or less than 18 decimals. Scale is used in conjuction\n * with Math.toScale/Math.fromScale functions to convert from token amounts\n * to D18 scale internal pool accounting.\n */\n function tokenAScale() external view returns (uint256);\n\n /**\n * @notice Most significant bit of scale value is a flag to indicate whether\n * tokenA has more or less than 18 decimals. Scale is used in conjuction\n * with Math.toScale/Math.fromScale functions to convert from token amounts\n * to D18 scale internal pool accounting.\n */\n function tokenBScale() external view returns (uint256);\n\n /**\n * @notice ID of bin at input tick position and kind.\n */\n function binIdByTickKind(int32 tick, uint256 kind)\n external\n view\n returns (uint32);\n\n /**\n * @notice Accumulated tokenA protocol fee.\n */\n function protocolFeeA() external view returns (uint128);\n\n /**\n * @notice Accumulated tokenB protocol fee.\n */\n function protocolFeeB() external view returns (uint128);\n\n /**\n * @notice Lending fee rate on flash loans.\n */\n function lendingFeeRateD18() external view returns (uint256);\n\n /**\n * @notice External function to get the current time-weighted average price.\n */\n function getCurrentTwa() external view returns (int256);\n\n /**\n * @notice External function to get the state of the pool.\n */\n function getState() external view returns (State memory);\n\n /**\n * @notice Return state of Bin at input binId.\n */\n function getBin(uint32 binId) external view returns (BinState memory bin);\n\n /**\n * @notice Return state of Tick at input tick position.\n */\n function getTick(int32 tick)\n external\n view\n returns (TickState memory tickState);\n\n /**\n * @notice Retrieves the balance of a user within a bin.\n * @param user The user's address.\n * @param subaccount The subaccount for the user.\n * @param binId The ID of the bin.\n */\n function balanceOf(\n address user,\n uint256 subaccount,\n uint32 binId\n ) external view returns (uint128 lpToken);\n\n /**\n * @notice Add liquidity to a pool. This function allows users to deposit\n * tokens into a liquidity pool.\n * @dev This function will call `maverickV2AddLiquidityCallback` on the\n * calling contract to collect the tokenA/tokenB payment.\n * @param recipient The account that will receive credit for the added liquidity.\n * @param subaccount The account that will receive credit for the added liquidity.\n * @param params Parameters containing the details for adding liquidity,\n * such as token types and amounts.\n * @param data Bytes information that gets passed to the callback.\n * @return tokenAAmount The amount of token A added to the pool.\n * @return tokenBAmount The amount of token B added to the pool.\n * @return binIds An array of bin IDs where the liquidity is stored.\n */\n function addLiquidity(\n address recipient,\n uint256 subaccount,\n AddLiquidityParams calldata params,\n bytes calldata data\n )\n external\n returns (\n uint256 tokenAAmount,\n uint256 tokenBAmount,\n uint32[] memory binIds\n );\n\n /**\n * @notice Removes liquidity from the pool.\n * @dev Liquidy can only be removed from a bin that is either unmerged or\n * has a mergeId of an unmerged bin. If a bin is merged more than one\n * level deep, it must be migrated up the merge stack to the root bin\n * before liquidity removal.\n * @param recipient The address to receive the tokens.\n * @param subaccount The subaccount for the recipient.\n * @param params The parameters for removing liquidity.\n * @return tokenAOut The amount of token A received.\n * @return tokenBOut The amount of token B received.\n */\n function removeLiquidity(\n address recipient,\n uint256 subaccount,\n RemoveLiquidityParams calldata params\n ) external returns (uint256 tokenAOut, uint256 tokenBOut);\n\n /**\n * @notice Migrate bins up the linked list of merged bins so that its\n * mergeId is the currrent active bin.\n * @dev Liquidy can only be removed from a bin that is either unmerged or\n * has a mergeId of an unmerged bin. If a bin is merged more than one\n * level deep, it must be migrated up the merge stack to the root bin\n * before liquidity removal.\n * @param binId The ID of the bin to migrate.\n * @param maxRecursion The maximum recursion depth for the migration.\n */\n function migrateBinUpStack(uint32 binId, uint32 maxRecursion) external;\n\n /**\n * @notice Swap tokenA/tokenB assets in the pool. The swap user has two\n * options for funding their swap.\n * - The user can push the input token amount to the pool before calling\n * the swap function. In order to avoid having the pool call the callback,\n * the user should pass a zero-length `data` bytes object with the swap\n * call.\n * - The user can send the input token amount to the pool when the pool\n * calls the `maverickV2SwapCallback` function on the calling contract.\n * That callback has input parameters that specify the token address of the\n * input token, the input and output amounts, and the bytes data sent to\n * the swap function.\n * @dev If the users elects to do a callback-based swap, the output\n * assets will be sent before the callback is called, allowing the user to\n * execute flash swaps. However, the pool does have reentrancy protection,\n * so a swapper will not be able to interact with the same pool again\n * while they are in the callback function.\n * @param recipient The address to receive the output tokens.\n * @param params Parameters containing the details of the swap\n * @param data Bytes information that gets passed to the callback.\n */\n function swap(\n address recipient,\n SwapParams memory params,\n bytes calldata data\n ) external returns (uint256 amountIn, uint256 amountOut);\n\n /**\n * @notice Loan tokenA/tokenB assets from the pool to recipient. The fee\n * rate of a loan is determined by `lendingFeeRateD18`, which is set at the\n * protocol level by the factory. This function calls\n * `maverickV2FlashLoanCallback` on the calling contract. At the end of\n * the callback, the caller must pay back the loan with fee (if there is a\n * fee).\n * @param recipient The address to receive the loaned tokens.\n * @param amountB Loan amount of tokenA sent to recipient.\n * @param amountB Loan amount of tokenB sent to recipient.\n * @param data Bytes information that gets passed to the callback.\n */\n function flashLoan(\n address recipient,\n uint256 amountA,\n uint256 amountB,\n bytes calldata data\n ) external;\n\n /**\n * @notice Distributes accumulated protocol fee to factory protocolFeeReceiver\n */\n function distributeFees(bool isTokenA)\n external\n returns (uint256 protocolFee, IERC20 token);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2PoolLens.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2PoolLens {\n error LensTargetPriceOutOfBounds(\n uint256 targetSqrtPrice,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n );\n error LensTooLittleLiquidity(\n uint256 relativeLiquidityAmount,\n uint256 deltaA,\n uint256 deltaB\n );\n error LensTargetingTokenWithNoDelta(\n bool targetIsA,\n uint256 deltaA,\n uint256 deltaB\n );\n\n /**\n * @notice Add liquidity slippage parameters for a distribution of liquidity.\n * @param pool Pool where liquidity is being added.\n * @param kind Bin kind; all bins must have the same kind in a given call\n * to addLiquidity.\n * @param ticks Array of tick values to add liquidity to.\n * @param relativeLiquidityAmounts Relative liquidity amounts for the\n * specified ticks. Liquidity in this case is not bin LP balance, it is\n * the bin liquidity as defined by liquidity = deltaA / (sqrt(upper) -\n * sqrt(lower)) or deltaB = liquidity / sqrt(lower) - liquidity /\n * sqrt(upper).\n * @param addSpec Slippage specification.\n */\n struct AddParamsViewInputs {\n IMaverickV2Pool pool;\n uint8 kind;\n int32[] ticks;\n uint128[] relativeLiquidityAmounts;\n AddParamsSpecification addSpec;\n }\n\n /**\n * @notice Multi-price add param specification.\n * @param slippageFactorD18 Max slippage allowed as a percent in D18 scale. e.g. 1% slippage is 0.01e18\n * @param numberOfPriceBreaksPerSide Number of price break values on either\n * side of current price.\n * @param targetAmount Target token contribution amount in tokenA if\n * targetIsA is true, otherwise this is the target amount for tokenB.\n * @param targetIsA Indicates if the target amount is for tokenA or tokenB\n */\n struct AddParamsSpecification {\n uint256 slippageFactorD18;\n uint256 numberOfPriceBreaksPerSide;\n uint256 targetAmount;\n bool targetIsA;\n }\n\n /**\n * @notice Specification for deriving create pool parameters. Creating a\n * pool in the liquidity manager has several steps:\n *\n * - Deploy pool\n * - Donate a small amount of initial liquidity in the activeTick\n * - Execute a small swap to set the pool price to the desired value\n * - Add liquidity\n *\n * In order to execute these steps, the caller must specify the parameters\n * of each step. The PoolLens has helper function to derive the values\n * used by the LiquidityManager, but this struct is the input to that\n * helper function and represents the core intent of the pool creator.\n *\n * @param fee Fraction of the pool swap amount that is retained as an LP in\n * D18 scale.\n * @param tickSpacing Tick spacing of pool where 1.0001^tickSpacing is the\n * bin width.\n * @param lookback Pool lookback in seconds.\n * @param tokenA Address of tokenA.\n * @param tokenB Address of tokenB.\n * @param activeTick Tick position that contains the active bins.\n * @param kinds 1-15 number to represent the active kinds\n * 0b0001 = static;\n * 0b0010 = right;\n * 0b0100 = left;\n * 0b1000 = both.\n * e.g. a pool with all 4 modes will have kinds = b1111 = 15\n * @param initialTargetB Amount of B to be donated to the pool after pool\n * create. This amount needs to be big enough to meet the minimum bin\n * liquidity.\n * @param sqrtPrice Target sqrt price of the pool.\n * @param kind Bin kind; all bins must have the same kind in a given call\n * to addLiquidity.\n * @param ticks Array of tick values to add liquidity to.\n * @param relativeLiquidityAmounts Relative liquidity amounts for the\n * specified ticks. Liquidity in this case is not bin LP balance, it is\n * the bin liquidity as defined by liquidity = deltaA / (sqrt(upper) -\n * sqrt(lower)) or deltaB = liquidity / sqrt(lower) - liquidity /\n * sqrt(upper).\n * @param targetAmount Target token contribution amount in tokenA if\n * targetIsA is true, otherwise this is the target amount for tokenB.\n * @param targetIsA Indicates if the target amount is for tokenA or tokenB\n */\n struct CreateAndAddParamsViewInputs {\n uint64 feeAIn;\n uint64 feeBIn;\n uint16 tickSpacing;\n uint32 lookback;\n IERC20 tokenA;\n IERC20 tokenB;\n int32 activeTick;\n uint8 kinds;\n // donate params\n uint256 initialTargetB;\n uint256 sqrtPrice;\n // add target\n uint8 kind;\n int32[] ticks;\n uint128[] relativeLiquidityAmounts;\n uint256 targetAmount;\n bool targetIsA;\n }\n\n struct Output {\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256[] deltaAs;\n uint256[] deltaBs;\n uint128[] deltaLpBalances;\n }\n\n struct Reserves {\n uint256 amountA;\n uint256 amountB;\n }\n\n struct BinPositionKinds {\n uint128[4] values;\n }\n\n struct PoolState {\n IMaverickV2Pool.TickState[] tickStateMapping;\n IMaverickV2Pool.BinState[] binStateMapping;\n BinPositionKinds[] binIdByTickKindMapping;\n IMaverickV2Pool.State state;\n Reserves protocolFees;\n }\n\n struct BoostedPositionSpecification {\n IMaverickV2Pool pool;\n uint32[] binIds;\n uint128[] ratios;\n uint8 kind;\n }\n\n struct CreateAndAddParamsInputs {\n uint64 feeAIn;\n uint64 feeBIn;\n uint16 tickSpacing;\n uint32 lookback;\n IERC20 tokenA;\n IERC20 tokenB;\n int32 activeTick;\n uint8 kinds;\n // donate params\n IMaverickV2Pool.AddLiquidityParams donateParams;\n // swap params\n uint256 swapAmount;\n // add params\n IMaverickV2Pool.AddLiquidityParams addParams;\n bytes[] packedAddParams;\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256 preAddReserveA;\n uint256 preAddReserveB;\n }\n\n struct TickDeltas {\n uint256 deltaAOut;\n uint256 deltaBOut;\n uint256[] deltaAs;\n uint256[] deltaBs;\n }\n\n /**\n * @notice Converts add parameter slippage specification into add\n * parameters. The return values are given in both raw format and as packed\n * values that can be used in the LiquidityManager contract.\n */\n function getAddLiquidityParams(AddParamsViewInputs memory params)\n external\n view\n returns (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint88[] memory sqrtPriceBreaks,\n IMaverickV2Pool.AddLiquidityParams[] memory addParams,\n IMaverickV2PoolLens.TickDeltas[] memory tickDeltas\n );\n\n /**\n * @notice Converts add parameter slippage specification and new pool\n * specification into CreateAndAddParamsInputs parameters that can be used in the\n * LiquidityManager contract.\n */\n function getCreatePoolAtPriceAndAddLiquidityParams(\n CreateAndAddParamsViewInputs memory params\n ) external view returns (CreateAndAddParamsInputs memory output);\n\n /**\n * @notice View function that provides information about pool ticks within\n * a tick radius from the activeTick. Ticks with no reserves are not\n * included in part o f the return array.\n */\n function getTicksAroundActive(IMaverickV2Pool pool, int32 tickRadius)\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates\n );\n\n /**\n * @notice View function that provides information about pool ticks within\n * a range. Ticks with no reserves are not included in part o f the return\n * array.\n */\n function getTicks(\n IMaverickV2Pool pool,\n int32 tickStart,\n int32 tickEnd\n )\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates\n );\n\n /**\n * @notice View function that provides information about pool ticks within\n * a range. Information returned includes all pool state needed to emulate\n * a swap off chain. Ticks with no reserves are not included in part o f\n * the return array.\n */\n function getTicksAroundActiveWLiquidity(\n IMaverickV2Pool pool,\n int32 tickRadius\n )\n external\n view\n returns (\n int32[] memory ticks,\n IMaverickV2Pool.TickState[] memory tickStates,\n uint256[] memory liquidities,\n uint256[] memory sqrtLowerTickPrices,\n uint256[] memory sqrtUpperTickPrices,\n IMaverickV2Pool.State memory poolState,\n uint256 sqrtPrice,\n uint256 feeAIn,\n uint256 feeBIn\n );\n\n /**\n * @notice View function that provides pool state information.\n */\n function getFullPoolState(\n IMaverickV2Pool pool,\n uint32 binStart,\n uint32 binEnd\n ) external view returns (PoolState memory poolState);\n\n /**\n * @notice View function that provides price and liquidity of a given tick.\n */\n function getTickSqrtPriceAndL(IMaverickV2Pool pool, int32 tick)\n external\n view\n returns (uint256 sqrtPrice, uint256 liquidity);\n\n /**\n * @notice Pool sqrt price.\n */\n function getPoolSqrtPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 sqrtPrice);\n\n /**\n * @notice Pool price.\n */\n function getPoolPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 price);\n\n /**\n * @notice Token scale of two tokens in a pool.\n */\n function tokenScales(IMaverickV2Pool pool)\n external\n view\n returns (uint256 tokenAScale, uint256 tokenBScale);\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Position.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Factory } from \"./IMaverickV2Factory.sol\";\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\nimport { ILiquidityRegistry } from \"./ILiquidityRegistry.sol\";\n\ninterface IMaverickV2Position {\n event PositionClearData(uint256 indexed tokenId);\n event PositionSetData(\n uint256 indexed tokenId,\n uint256 index,\n PositionPoolBinIds newData\n );\n event SetLpReward(ILiquidityRegistry lpReward);\n\n error PositionDuplicatePool(uint256 index, IMaverickV2Pool pool);\n error PositionNotFactoryPool();\n error PositionPermissionedLiquidityPool();\n\n struct PositionPoolBinIds {\n IMaverickV2Pool pool;\n uint32[] binIds;\n }\n\n struct PositionFullInformation {\n PositionPoolBinIds poolBinIds;\n uint256 amountA;\n uint256 amountB;\n uint256[] binAAmounts;\n uint256[] binBAmounts;\n int32[] ticks;\n uint256[] liquidities;\n }\n\n /**\n * @notice Factory that tracks lp rewards.\n */\n function lpReward() external view returns (ILiquidityRegistry);\n\n /**\n * @notice Pool factory.\n */\n function factory() external view returns (IMaverickV2Factory);\n\n /**\n * @notice Mint NFT that holds liquidity in a Maverick V2 Pool. To mint\n * liquidity to an NFT, add liquidity to bins in a pool where the\n * add liquidity recipient is this contract and the subaccount is the\n * tokenId. LiquidityManager can be used to simplify minting Position NFTs.\n */\n function mint(\n address recipient,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external returns (uint256 tokenId);\n\n /**\n * @notice Overwrites tokenId pool/binId information for a given data index.\n */\n function setTokenIdData(\n uint256 tokenId,\n uint256 index,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n\n /**\n * @notice Overwrites entire pool/binId data set for a given tokenId.\n */\n function setTokenIdData(uint256 tokenId, PositionPoolBinIds[] memory data)\n external;\n\n /**\n * @notice Append new pool/binIds data array to tokenId.\n */\n function appendTokenIdData(\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n\n /**\n * @notice Get array pool/binIds data for a given tokenId.\n */\n function getTokenIdData(uint256 tokenId)\n external\n view\n returns (PositionPoolBinIds[] memory);\n\n /**\n * @notice Get value from array of pool/binIds data for a given tokenId.\n */\n function getTokenIdData(uint256 tokenId, uint256 index)\n external\n view\n returns (PositionPoolBinIds memory);\n\n /**\n * @notice Length of array of pool/binIds data for a given tokenId.\n */\n function tokenIdDataLength(uint256 tokenId)\n external\n view\n returns (uint256 length);\n\n /**\n * @notice Remove liquidity from tokenId for a given pool. User can\n * specify arbitrary bins to remove from for their subaccount in the pool\n * even if those bins are not in the tokenIdData set.\n */\n function removeLiquidity(\n uint256 tokenId,\n address recipient,\n IMaverickV2Pool pool,\n IMaverickV2Pool.RemoveLiquidityParams memory params\n ) external returns (uint256 tokenAAmount, uint256 tokenBAmount);\n\n /**\n * @notice Remove liquidity from tokenId for a given pool to sender. User\n * can specify arbitrary bins to remove from for their subaccount in the\n * pool even if those bins are not in the tokenIdData set.\n */\n function removeLiquidityToSender(\n uint256 tokenId,\n IMaverickV2Pool pool,\n IMaverickV2Pool.RemoveLiquidityParams memory params\n ) external returns (uint256 tokenAAmount, uint256 tokenBAmount);\n\n /**\n * @notice NFT asset information for a given range of pool/binIds indexes.\n * This function only returns the liquidity in the pools/binIds stored as\n * part of the tokenIdData, but it is possible that the NFT has additional\n * liquidity in pools/binIds that have not been recorded.\n */\n function tokenIdPositionInformation(\n uint256 tokenId,\n uint256 startIndex,\n uint256 stopIndex\n ) external view returns (PositionFullInformation[] memory output);\n\n /**\n * @notice NFT asset information for a given pool/binIds index. This\n * function only returns the liquidity in the pools/binIds stored as part\n * of the tokenIdData, but it is possible that the NFT has additional\n * liquidity in pools/binIds that have not been recorded.\n */\n function tokenIdPositionInformation(uint256 tokenId, uint256 index)\n external\n view\n returns (PositionFullInformation memory output);\n\n /**\n * @notice Get remove parameters for removing a fractional part of the\n * liquidity owned by a given tokenId. The fractional factor to remove is\n * given by proporationD18 in 18-decimal scale.\n */\n function getRemoveParams(\n uint256 tokenId,\n uint256 index,\n uint256 proportionD18\n )\n external\n view\n returns (IMaverickV2Pool.RemoveLiquidityParams memory params);\n\n /**\n * @notice Register the bin balances in the nft with the LpReward contract.\n */\n function checkpointBinLpBalance(\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds\n ) external;\n}\n" + }, + "contracts/interfaces/plume/IMaverickV2Quoter.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.25;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IMaverickV2Quoter {\n error QuoterInvalidSwap();\n error QuoterInvalidAddLiquidity();\n\n /**\n * @notice Calculates a swap on a MaverickV2Pool and returns the resulting\n * amount and estimated gas. The gas estimate is only a rough estimate and\n * may not match a swap's gas.\n * @param pool The MaverickV2Pool to swap on.\n * @param amount The input amount.\n * @param tokenAIn Indicates if token A is the input token.\n * @param exactOutput Indicates if the amount is the output amount (true)\n * or input amount (false). If the tickLimit is reached, the full value of\n * the exactOutput may not be returned because the pool will stop swapping\n * before the whole order is filled.\n * @param tickLimit The tick limit for the swap. Once the swap lands in\n * this tick, it will stop and return the output amount swapped up to that\n * tick.\n */\n function calculateSwap(\n IMaverickV2Pool pool,\n uint128 amount,\n bool tokenAIn,\n bool exactOutput,\n int32 tickLimit\n )\n external\n returns (\n uint256 amountIn,\n uint256 amountOut,\n uint256 gasEstimate\n );\n\n /**\n * @notice Calculates a multihop swap and returns the resulting amount and\n * estimated gas. The gas estimate is only a rough estimate and\n * may not match a swap's gas.\n * @param path The path of pools to swap through. Path is given by an\n * packed array of (pool, tokenAIn) tuples. So each step in the path is 160\n * + 8 = 168 bits of data. e.g. path = abi.encodePacked(pool1, true, pool2, false);\n * @param amount The input amount.\n * @param exactOutput A boolean indicating if exact output is required.\n */\n function calculateMultiHopSwap(\n bytes memory path,\n uint256 amount,\n bool exactOutput\n ) external returns (uint256 returnAmount, uint256 gasEstimate);\n\n /**\n * @notice Computes the token amounts required for a given set of\n * addLiquidity parameters. The gas estimate is only a rough estimate and\n * may not match a add's gas.\n */\n function calculateAddLiquidity(\n IMaverickV2Pool pool,\n IMaverickV2Pool.AddLiquidityParams calldata params\n )\n external\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 gasEstimate\n );\n\n /**\n * @notice Pool's sqrt price.\n */\n function poolSqrtPrice(IMaverickV2Pool pool)\n external\n view\n returns (uint256 sqrtPrice);\n}\n" + }, + "contracts/interfaces/plume/IPoolDistributor.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.0;\n\nimport { IMaverickV2Pool } from \"./IMaverickV2Pool.sol\";\n\ninterface IPoolDistributor {\n function rewardToken() external view returns (address);\n\n function claimLp(\n address recipient,\n uint256 tokenId,\n IMaverickV2Pool pool,\n uint32[] memory binIds,\n uint256 epoch\n ) external returns (uint256 amount);\n}\n" + }, + "contracts/interfaces/plume/IVotingDistributor.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity ^0.8.0;\n\ninterface IVotingDistributor {\n function lastEpoch() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/poolBooster/IMerklDistributor.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IMerklDistributor {\n struct CampaignParameters {\n // POPULATED ONCE CREATED\n\n // ID of the campaign. This can be left as a null bytes32 when creating campaigns\n // on Merkl.\n bytes32 campaignId;\n // CHOSEN BY CAMPAIGN CREATOR\n\n // Address of the campaign creator, if marked as address(0), it will be overriden with the\n // address of the `msg.sender` creating the campaign\n address creator;\n // Address of the token used as a reward\n address rewardToken;\n // Amount of `rewardToken` to distribute across all the epochs\n // Amount distributed per epoch is `amount/numEpoch`\n uint256 amount;\n // Type of campaign\n uint32 campaignType;\n // Timestamp at which the campaign should start\n uint32 startTimestamp;\n // Duration of the campaign in seconds. Has to be a multiple of EPOCH = 3600\n uint32 duration;\n // Extra data to pass to specify the campaign\n bytes campaignData;\n }\n\n function createCampaign(CampaignParameters memory newCampaign)\n external\n returns (bytes32);\n\n function signAndCreateCampaign(\n CampaignParameters memory newCampaign,\n bytes memory _signature\n ) external returns (bytes32);\n\n function sign(bytes memory _signature) external;\n\n function rewardTokenMinAmounts(address _rewardToken)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/interfaces/poolBooster/IPoolBoostCentralRegistry.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPoolBoostCentralRegistry {\n /**\n * @dev all the supported pool booster types are listed here. It is possible\n * to have multiple versions of the factory that supports the same type of\n * pool booster. Factories are immutable and this can happen when a factory\n * or related pool booster required code update.\n * e.g. \"PoolBoosterSwapxDouble\" & \"PoolBoosterSwapxDouble_v2\"\n */\n enum PoolBoosterType {\n // Supports bribing 2 contracts per pool. Appropriate for Ichi vault concentrated\n // liquidity pools where (which is expected in most/all cases) both pool gauges\n // require bribing.\n SwapXDoubleBooster,\n // Supports bribing a single contract per pool. Appropriate for Classic Stable &\n // Classic Volatile pools and Ichi vaults where only 1 side (1 of the 2 gauges)\n // needs bribing\n SwapXSingleBooster,\n // Supports bribing a single contract per pool. Appropriate for Metropolis pools\n MetropolisBooster,\n // Supports creating a Merkl campaign.\n MerklBooster\n }\n\n struct PoolBoosterEntry {\n address boosterAddress;\n address ammPoolAddress;\n PoolBoosterType boosterType;\n }\n\n event PoolBoosterCreated(\n address poolBoosterAddress,\n address ammPoolAddress,\n PoolBoosterType poolBoosterType,\n address factoryAddress\n );\n event PoolBoosterRemoved(address poolBoosterAddress);\n\n function emitPoolBoosterCreated(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n PoolBoosterType _boosterType\n ) external;\n\n function emitPoolBoosterRemoved(address _poolBoosterAddress) external;\n}\n" + }, + "contracts/interfaces/poolBooster/IPoolBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IPoolBooster {\n event BribeExecuted(uint256 amount);\n\n /// @notice Execute the bribe action\n function bribe() external;\n}\n" + }, + "contracts/interfaces/poolBooster/ISwapXAlgebraBribe.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IBribe {\n /// @notice Notify a bribe amount\n /// @dev Rewards are saved into NEXT EPOCH mapping.\n function notifyRewardAmount(address _rewardsToken, uint256 reward) external;\n}\n" + }, + "contracts/interfaces/sonic/INodeDriver.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\n\ninterface INodeDriver {\n /// Seal epoch. Called BEFORE epoch sealing made by the client itself.\n function sealEpoch(\n uint256[] calldata offlineTimes,\n uint256[] calldata offlineBlocks,\n uint256[] calldata uptimes,\n uint256[] calldata originatedTxsFee\n ) external;\n}\n" + }, + "contracts/interfaces/sonic/ISFC.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\n\n/**\n * @title Special Fee Contract for Sonic network\n * @notice The SFC maintains a list of validators and delegators and distributes rewards to them.\n * @custom:security-contact security@fantom.foundation\n */\ninterface ISFC {\n error StakeIsFullySlashed();\n\n event CreatedValidator(\n uint256 indexed validatorID,\n address indexed auth,\n uint256 createdEpoch,\n uint256 createdTime\n );\n event Delegated(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 amount\n );\n event Undelegated(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 indexed wrID,\n uint256 amount\n );\n event Withdrawn(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 indexed wrID,\n uint256 amount,\n uint256 penalty\n );\n event ClaimedRewards(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 rewards\n );\n event RestakedRewards(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 rewards\n );\n event BurntFTM(uint256 amount);\n event UpdatedSlashingRefundRatio(\n uint256 indexed validatorID,\n uint256 refundRatio\n );\n event RefundedSlashedLegacyDelegation(\n address indexed delegator,\n uint256 indexed validatorID,\n uint256 amount\n );\n\n event DeactivatedValidator(\n uint256 indexed validatorID,\n uint256 deactivatedEpoch,\n uint256 deactivatedTime\n );\n event ChangedValidatorStatus(uint256 indexed validatorID, uint256 status);\n event AnnouncedRedirection(address indexed from, address indexed to);\n\n function currentSealedEpoch() external view returns (uint256);\n\n function getEpochSnapshot(uint256 epoch)\n external\n view\n returns (\n uint256 endTime,\n uint256 endBlock,\n uint256 epochFee,\n uint256 baseRewardPerSecond,\n uint256 totalStake,\n uint256 totalSupply\n );\n\n function getStake(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getValidator(uint256 validatorID)\n external\n view\n returns (\n uint256 status,\n uint256 receivedStake,\n address auth,\n uint256 createdEpoch,\n uint256 createdTime,\n uint256 deactivatedTime,\n uint256 deactivatedEpoch\n );\n\n function getValidatorID(address auth) external view returns (uint256);\n\n function getValidatorPubkey(uint256 validatorID)\n external\n view\n returns (bytes memory);\n\n function pubkeyAddressvalidatorID(address pubkeyAddress)\n external\n view\n returns (uint256);\n\n function getWithdrawalRequest(\n address delegator,\n uint256 validatorID,\n uint256 wrID\n )\n external\n view\n returns (\n uint256 epoch,\n uint256 time,\n uint256 amount\n );\n\n function isOwner() external view returns (bool);\n\n function lastValidatorID() external view returns (uint256);\n\n function minGasPrice() external view returns (uint256);\n\n function owner() external view returns (address);\n\n function renounceOwnership() external;\n\n function slashingRefundRatio(uint256 validatorID)\n external\n view\n returns (uint256);\n\n function stashedRewardsUntilEpoch(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function totalActiveStake() external view returns (uint256);\n\n function totalStake() external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function transferOwnership(address newOwner) external;\n\n function treasuryAddress() external view returns (address);\n\n function version() external pure returns (bytes3);\n\n function currentEpoch() external view returns (uint256);\n\n function updateConstsAddress(address v) external;\n\n function constsAddress() external view returns (address);\n\n function getEpochValidatorIDs(uint256 epoch)\n external\n view\n returns (uint256[] memory);\n\n function getEpochReceivedStake(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochAccumulatedRewardPerToken(\n uint256 epoch,\n uint256 validatorID\n ) external view returns (uint256);\n\n function getEpochAccumulatedUptime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochAverageUptime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint32);\n\n function getEpochAccumulatedOriginatedTxsFee(\n uint256 epoch,\n uint256 validatorID\n ) external view returns (uint256);\n\n function getEpochOfflineTime(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochOfflineBlocks(uint256 epoch, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function getEpochEndBlock(uint256 epoch) external view returns (uint256);\n\n function rewardsStash(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function createValidator(bytes calldata pubkey) external payable;\n\n function getSelfStake(uint256 validatorID) external view returns (uint256);\n\n function delegate(uint256 validatorID) external payable;\n\n function undelegate(\n uint256 validatorID,\n uint256 wrID,\n uint256 amount\n ) external;\n\n function isSlashed(uint256 validatorID) external view returns (bool);\n\n function withdraw(uint256 validatorID, uint256 wrID) external;\n\n function deactivateValidator(uint256 validatorID, uint256 status) external;\n\n function pendingRewards(address delegator, uint256 validatorID)\n external\n view\n returns (uint256);\n\n function stashRewards(address delegator, uint256 validatorID) external;\n\n function claimRewards(uint256 validatorID) external;\n\n function restakeRewards(uint256 validatorID) external;\n\n function updateSlashingRefundRatio(uint256 validatorID, uint256 refundRatio)\n external;\n\n function updateTreasuryAddress(address v) external;\n\n function burnFTM(uint256 amount) external;\n\n function sealEpoch(\n uint256[] calldata offlineTime,\n uint256[] calldata offlineBlocks,\n uint256[] calldata uptimes,\n uint256[] calldata originatedTxsFee\n ) external;\n\n function sealEpochValidators(uint256[] calldata nextValidatorIDs) external;\n\n function initialize(\n uint256 sealedEpoch,\n uint256 _totalSupply,\n address nodeDriver,\n address consts,\n address _owner\n ) external;\n\n function setGenesisValidator(\n address auth,\n uint256 validatorID,\n bytes calldata pubkey,\n uint256 createdTime\n ) external;\n\n function setGenesisDelegation(\n address delegator,\n uint256 validatorID,\n uint256 stake\n ) external;\n\n function updateStakeSubscriberAddress(address v) external;\n\n function stakeSubscriberAddress() external view returns (address);\n\n function setRedirectionAuthorizer(address v) external;\n\n function announceRedirection(address to) external;\n\n function initiateRedirection(address from, address to) external;\n\n function redirect(address to) external;\n}\n" + }, + "contracts/interfaces/sonic/ISwapXGauge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IGauge {\n function owner() external view returns (address);\n\n function TOKEN() external view returns (address);\n\n function DISTRIBUTION() external view returns (address);\n\n function balanceOf(address account) external view returns (uint256);\n\n function claimFees() external returns (uint256 claimed0, uint256 claimed1);\n\n function deposit(uint256 amount) external;\n\n function depositAll() external;\n\n function earned(address account) external view returns (uint256);\n\n function getReward() external;\n\n function getReward(address _user) external;\n\n function isForPair() external view returns (bool);\n\n function lastTimeRewardApplicable() external view returns (uint256);\n\n function lastUpdateTime() external view returns (uint256);\n\n function notifyRewardAmount(address token, uint256 reward) external;\n\n function periodFinish() external view returns (uint256);\n\n function rewardForDuration() external view returns (uint256);\n\n function rewardPerToken() external view returns (uint256);\n\n function rewardPerTokenStored() external view returns (uint256);\n\n function rewardRate() external view returns (uint256);\n\n function rewardToken() external view returns (address);\n\n function rewards(address) external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function userRewardPerTokenPaid(address) external view returns (uint256);\n\n function withdraw(uint256 amount) external;\n\n function withdrawAll() external;\n\n function withdrawAllAndHarvest() external;\n\n function withdrawExcess(address token, uint256 amount) external;\n\n function emergency() external returns (bool);\n\n function emergencyWithdraw() external;\n\n function activateEmergencyMode() external;\n\n function stopEmergencyMode() external;\n}\n" + }, + "contracts/interfaces/sonic/ISwapXPair.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IPair {\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n\n event Mint(address indexed sender, uint256 amount0, uint256 amount1);\n event Burn(\n address indexed sender,\n uint256 amount0,\n uint256 amount1,\n address indexed to\n );\n event Swap(\n address indexed sender,\n uint256 amount0In,\n uint256 amount1In,\n uint256 amount0Out,\n uint256 amount1Out,\n address indexed to\n );\n event Claim(\n address indexed sender,\n address indexed recipient,\n uint256 amount0,\n uint256 amount1\n );\n\n function metadata()\n external\n view\n returns (\n uint256 dec0,\n uint256 dec1,\n uint256 r0,\n uint256 r1,\n bool st,\n address t0,\n address t1\n );\n\n function claimFees() external returns (uint256, uint256);\n\n function tokens() external view returns (address, address);\n\n function token0() external view returns (address);\n\n function token1() external view returns (address);\n\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function swap(\n uint256 amount0Out,\n uint256 amount1Out,\n address to,\n bytes calldata data\n ) external;\n\n function burn(address to)\n external\n returns (uint256 amount0, uint256 amount1);\n\n function mint(address to) external returns (uint256 liquidity);\n\n function getReserves()\n external\n view\n returns (\n uint256 _reserve0,\n uint256 _reserve1,\n uint256 _blockTimestampLast\n );\n\n function getAmountOut(uint256, address) external view returns (uint256);\n\n // ERC20 methods\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n function decimals() external view returns (uint8);\n\n function totalSupply() external view returns (uint256);\n\n function balanceOf(address) external view returns (uint256);\n\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 value) external returns (bool);\n\n function claimable0(address _user) external view returns (uint256);\n\n function claimable1(address _user) external view returns (uint256);\n\n function isStable() external view returns (bool);\n\n function skim(address to) external;\n\n function sync() external;\n}\n" + }, + "contracts/interfaces/sonic/IVoterV3.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IVoterV3 {\n /// @notice create a gauge\n function createGauge(address _pool, uint256 _gaugeType)\n external\n returns (\n address _gauge,\n address _internal_bribe,\n address _external_bribe\n );\n\n function gauges(address _pool) external view returns (address _gauge);\n}\n" + }, + "contracts/interfaces/sonic/IWrappedSonic.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWrappedSonic {\n event Deposit(address indexed account, uint256 value);\n event Withdrawal(address indexed account, uint256 value);\n event Transfer(address indexed from, address indexed to, uint256 value);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n\n function allowance(address owner, address spender)\n external\n view\n returns (uint256);\n\n function approve(address spender, uint256 value) external returns (bool);\n\n function balanceOf(address account) external view returns (uint256);\n\n function decimals() external view returns (uint8);\n\n function deposit() external payable;\n\n function depositFor(address account) external payable returns (bool);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address to, uint256 value) external returns (bool);\n\n function transferFrom(\n address from,\n address to,\n uint256 value\n ) external returns (bool);\n\n function withdraw(uint256 value) external;\n\n function withdrawTo(address account, uint256 value) external returns (bool);\n}\n" + }, + "contracts/interfaces/Tether.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// slither-disable-start erc20-interface\ninterface Tether {\n function transfer(address to, uint256 value) external;\n\n function transferFrom(\n address from,\n address to,\n uint256 value\n ) external;\n\n function balanceOf(address) external view returns (uint256);\n\n function approve(address _spender, uint256 _value) external;\n}\n// slither-disable-end erc20-interface\n" + }, + "contracts/interfaces/uniswap/IUniswapUniversalRouter.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapUniversalRouter {\n /// @notice Executes encoded commands along with provided inputs. Reverts if deadline has expired.\n /// @param commands A set of concatenated commands, each 1 byte in length\n /// @param inputs An array of byte strings containing abi encoded inputs for each command\n /// @param deadline The deadline by which the transaction must be executed\n function execute(\n bytes calldata commands,\n bytes[] calldata inputs,\n uint256 deadline\n ) external payable;\n\n function execute(bytes calldata commands, bytes[] calldata inputs)\n external\n payable;\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV2Pair.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapV2Pair {\n function token0() external view returns (address);\n\n function token1() external view returns (address);\n\n function getReserves()\n external\n view\n returns (\n uint112 reserve0,\n uint112 reserve1,\n uint32 blockTimestampLast\n );\n\n function price0CumulativeLast() external view returns (uint256);\n\n function price1CumulativeLast() external view returns (uint256);\n\n function sync() external;\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV2Router02.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IUniswapV2Router {\n function WETH() external pure returns (address);\n\n function swapExactTokensForTokens(\n uint256 amountIn,\n uint256 amountOutMin,\n address[] calldata path,\n address to,\n uint256 deadline\n ) external returns (uint256[] memory amounts);\n\n function addLiquidity(\n address tokenA,\n address tokenB,\n uint256 amountADesired,\n uint256 amountBDesired,\n uint256 amountAMin,\n uint256 amountBMin,\n address to,\n uint256 deadline\n )\n external\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 liquidity\n );\n}\n" + }, + "contracts/interfaces/uniswap/IUniswapV3Router.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n// -- Solididy v0.5.x compatible interface\ninterface IUniswapV3Router {\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path\n /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n}\n" + }, + "contracts/mocks/beacon/ExecutionLayerConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { GeneralPurposeToConsensusLayerRequest } from \"./GeneralPurposeToConsensusLayerRequest.sol\";\n\ncontract ExecutionLayerConsolidation is GeneralPurposeToConsensusLayerRequest {\n event ConsolidationRequestIssued(bytes sourceKey, bytes targetKey);\n bytes public lastSource;\n bytes public lastTarget;\n\n function handleRequest(bytes calldata data) internal override {\n // parameters should consist of twice the 48 bytes for 2 public keys\n require(data.length == 96, \"Invalid Consolidation data\");\n lastSource = data[:48];\n lastTarget = data[48:];\n\n emit ConsolidationRequestIssued(lastSource, lastTarget);\n }\n}\n" + }, + "contracts/mocks/beacon/ExecutionLayerWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { GeneralPurposeToConsensusLayerRequest } from \"./GeneralPurposeToConsensusLayerRequest.sol\";\n\ncontract ExecutionLayerWithdrawal is GeneralPurposeToConsensusLayerRequest {\n event WithdrawalRequestIssued(bytes publicKey, uint64 amount);\n\n bytes public lastPublicKey;\n uint64 public lastAmount;\n\n function handleRequest(bytes calldata data) internal override {\n // parameters should consist of 48 bytes for public key and 8 bytes for uint64 amount\n require(data.length == 56, \"Invalid Withdrawal data\");\n lastPublicKey = data[:48];\n lastAmount = uint64(bytes8(data[48:]));\n emit WithdrawalRequestIssued(lastPublicKey, lastAmount);\n }\n}\n" + }, + "contracts/mocks/beacon/GeneralPurposeToConsensusLayerRequest.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nabstract contract GeneralPurposeToConsensusLayerRequest {\n // solhint-disable no-complex-fallback\n fallback() external payable {\n // fee requested\n if (msg.data.length == 0) {\n uint256 fee = _fee();\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Return a uint256 value\n mstore(0, fee)\n return(0, 32) // Return 32 bytes from memory\n }\n }\n\n // else handle request\n handleRequest(msg.data);\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _fee() internal virtual returns (uint256) {\n return 1;\n }\n\n function handleRequest(bytes calldata data) internal virtual;\n}\n" + }, + "contracts/mocks/BurnableERC20.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ninterface IBurnableERC20 {\n function burn(uint256 value) external returns (bool);\n\n function burnFrom(address account, uint256 value) external returns (bool);\n}\n\n/**\n * @title BurnableERC20\n * @dev Exposes the burn function of ERC20 for tests\n */\nabstract contract BurnableERC20 is IBurnableERC20, ERC20 {\n /**\n * @dev Function to burn tokens\n * @param value The amount of tokens to burn.\n * @return A boolean that indicates if the operation was successful.\n */\n function burn(uint256 value) public virtual override returns (bool) {\n _burn(msg.sender, value);\n return true;\n }\n\n /**\n * @dev Function to burn tokens from a specific account\n * @param account The address with the tokens to burn.\n * @param value The amount of tokens to burn.\n * @return A boolean that indicates if the operation was successful.\n */\n function burnFrom(address account, uint256 value)\n public\n override\n returns (bool)\n {\n _burn(account, value);\n return true;\n }\n}\n" + }, + "contracts/mocks/curve/Mock3CRV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20, MintableERC20 } from \"../MintableERC20.sol\";\nimport { BurnableERC20 } from \"../BurnableERC20.sol\";\n\ncontract Mock3CRV is MintableERC20, BurnableERC20 {\n constructor() ERC20(\"Curve.fi DAI/USDC/USDT\", \"3Crv\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/curve/MockBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { MockRewardPool } from \"./MockRewardPool.sol\";\n\nimport { IRewardStaking } from \"../../strategies/IRewardStaking.sol\";\nimport { IMintableERC20, MintableERC20, ERC20 } from \"../MintableERC20.sol\";\nimport { IBurnableERC20, BurnableERC20 } from \"../BurnableERC20.sol\";\n\ncontract MockDepositToken is MintableERC20, BurnableERC20 {\n constructor() ERC20(\"DCVX\", \"CVX Deposit Token\") {}\n}\n\ncontract MockBooster {\n using SafeERC20 for IERC20;\n\n struct PoolInfo {\n address lptoken;\n address token;\n address crvRewards;\n }\n\n address public minter; // this is CVx for the booster on live\n address public crv; // Curve rewards token\n address public cvx; // Convex rewards token\n mapping(uint256 => PoolInfo) public poolInfo;\n\n constructor(\n address _rewardsMinter,\n address _crv,\n address _cvx\n ) public {\n minter = _rewardsMinter;\n crv = _crv;\n cvx = _cvx;\n }\n\n function setPool(uint256 pid, address _lpToken)\n external\n returns (address rewards)\n {\n address token = address(new MockDepositToken());\n // Deploy a new Convex Rewards Pool\n rewards = address(\n new MockRewardPool(pid, token, crv, cvx, address(this))\n );\n\n poolInfo[pid] = PoolInfo({\n lptoken: _lpToken,\n token: token,\n crvRewards: rewards\n });\n }\n\n function deposit(\n uint256 _pid,\n uint256 _amount,\n bool _stake\n ) public returns (bool) {\n PoolInfo storage pool = poolInfo[_pid];\n\n address lptoken = pool.lptoken;\n\n // hold on to the Curve LP tokens\n IERC20(lptoken).safeTransferFrom(msg.sender, address(this), _amount);\n\n address token = pool.token;\n if (_stake) {\n // mint Convex pool LP tokens and stake in rewards contract on user behalf\n IMintableERC20(token).mint(_amount);\n address rewardContract = pool.crvRewards;\n IERC20(token).safeApprove(rewardContract, 0);\n IERC20(token).safeApprove(rewardContract, _amount);\n IRewardStaking(rewardContract).stakeFor(msg.sender, _amount);\n } else {\n // mint Convex pool LP tokens and send to user\n IMintableERC20(token).mint(_amount);\n IERC20(token).transfer(msg.sender, _amount);\n }\n return true;\n }\n\n // Deposit all Curve LP tokens and stake\n function depositAll(uint256 _pid, bool _stake) external returns (bool) {\n address lptoken = poolInfo[_pid].lptoken;\n uint256 balance = IERC20(lptoken).balanceOf(msg.sender);\n deposit(_pid, balance, _stake);\n return true;\n }\n\n // withdraw Curve LP tokens\n function _withdraw(\n uint256 _pid,\n uint256 _amount,\n address _from,\n address _to\n ) internal {\n PoolInfo storage pool = poolInfo[_pid];\n\n // burn the Convex pool LP tokens\n IBurnableERC20(pool.token).burnFrom(_from, _amount);\n\n // return the Curve LP tokens\n IERC20(pool.lptoken).safeTransfer(_to, _amount);\n }\n\n // withdraw Curve LP tokens\n function withdraw(uint256 _pid, uint256 _amount) public returns (bool) {\n _withdraw(_pid, _amount, msg.sender, msg.sender);\n return true;\n }\n\n // withdraw all Curve LP tokens\n function withdrawAll(uint256 _pid) public returns (bool) {\n address token = poolInfo[_pid].token;\n uint256 userBal = IERC20(token).balanceOf(msg.sender);\n withdraw(_pid, userBal);\n return true;\n }\n\n // allow reward contracts to send here and withdraw to user\n function withdrawTo(\n uint256 _pid,\n uint256 _amount,\n address _to\n ) external returns (bool) {\n address rewardContract = poolInfo[_pid].crvRewards;\n require(msg.sender == rewardContract, \"!auth\");\n\n _withdraw(_pid, _amount, msg.sender, _to);\n return true;\n }\n\n // callback from reward contract when crv is received.\n function rewardClaimed(\n uint256 _pid,\n // solhint-disable-next-line no-unused-vars\n address _address,\n uint256 _amount\n ) external returns (bool) {\n address rewardContract = poolInfo[_pid].crvRewards;\n require(msg.sender == rewardContract, \"!auth\");\n\n //mint reward tokens\n // and transfer it\n IMintableERC20(minter).mint(_amount);\n IERC20(minter).transfer(msg.sender, _amount);\n return true;\n }\n}\n" + }, + "contracts/mocks/curve/MockCRV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../MintableERC20.sol\";\n\ncontract MockCRV is MintableERC20 {\n constructor() ERC20(\"Curve DAO Token\", \"CRV\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/curve/MockCRVMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\n\ncontract MockCRVMinter {\n address crv;\n\n constructor(address _crv) {\n crv = _crv;\n }\n\n function mint(address _address) external {\n uint256 amount = 2e18;\n IMintableERC20(crv).mint(amount);\n IERC20(crv).transfer(_address, amount);\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveAbstractMetapool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { MintableERC20, IMintableERC20 } from \"../MintableERC20.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport \"../../utils/Helpers.sol\";\n\nabstract contract MockCurveAbstractMetapool is MintableERC20 {\n using StableMath for uint256;\n\n address[] public coins;\n uint256[2] public balances;\n\n // Returns the same amount of LP tokens in 1e18 decimals\n function add_liquidity(uint256[2] calldata _amounts, uint256 _minAmount)\n external\n returns (uint256 lpAmount)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transferFrom(\n msg.sender,\n address(this),\n _amounts[i]\n );\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to sum\n lpAmount += _amounts[i].scaleBy(18, assetDecimals);\n balances[i] = balances[i] + _amounts[i];\n }\n }\n // Hacky way of simulating slippage to check _minAmount\n if (lpAmount == 29000e18) lpAmount = 14500e18;\n require(lpAmount >= _minAmount, \"Slippage ruined your day\");\n // Send LP token to sender, e.g. 3CRV\n _mint(msg.sender, lpAmount);\n }\n\n // Dumb implementation that returns the same amount\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n public\n view\n returns (uint256 lpAmount)\n {\n uint256 assetDecimals = Helpers.getDecimals(coins[uint128(_index)]);\n lpAmount = _amount.scaleBy(assetDecimals, 18);\n }\n\n function remove_liquidity_one_coin(\n uint256 _lpAmount,\n int128 _index,\n // solhint-disable-next-line no-unused-vars\n uint256 _minAmount\n ) external returns (uint256 amount) {\n _burn(msg.sender, _lpAmount);\n uint256[] memory amounts = new uint256[](coins.length);\n amounts[uint128(_index)] = _lpAmount;\n amount = calc_withdraw_one_coin(_lpAmount, _index);\n balances[uint128(_index)] -= amount;\n IERC20(coins[uint128(_index)]).transfer(msg.sender, amount);\n }\n\n function get_virtual_price() external pure returns (uint256) {\n return 1e18;\n }\n\n // solhint-disable-next-line no-unused-vars\n function remove_liquidity(uint256 _amount, uint256[2] memory _min_amounts)\n public\n returns (uint256[2] memory amounts)\n {\n _burn(msg.sender, _amount);\n uint256 totalSupply = totalSupply();\n for (uint256 i = 0; i < 2; i++) {\n amounts[i] = totalSupply > 0\n ? (_amount * IERC20(coins[i]).balanceOf(address(this))) /\n totalSupply\n : IERC20(coins[i]).balanceOf(address(this));\n balances[i] -= amounts[i];\n IERC20(coins[i]).transfer(msg.sender, amounts[i]);\n }\n }\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens\n ) public returns (uint256) {\n return\n _remove_liquidity_imbalance(\n _amounts,\n _max_burned_tokens,\n msg.sender\n );\n }\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens,\n address _reveiver\n ) public returns (uint256) {\n return\n _remove_liquidity_imbalance(\n _amounts,\n _max_burned_tokens,\n _reveiver\n );\n }\n\n function _remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burned_tokens,\n address _reveiver\n ) internal returns (uint256 lpTokens) {\n lpTokens = _max_burned_tokens;\n _burn(msg.sender, lpTokens);\n for (uint256 i = 0; i < _amounts.length; i++) {\n balances[i] -= _amounts[i];\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transfer(_reveiver, _amounts[i]);\n }\n }\n }\n\n // Dumb implementation that sums the scaled amounts\n function calc_token_amount(uint256[2] memory _amounts, bool)\n public\n view\n returns (uint256 lpTokens)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to lpTokens\n lpTokens += _amounts[i].scaleBy(18, assetDecimals);\n }\n }\n\n /// @notice 0.02% fee\n function fee() external pure returns (uint256) {\n return 2000000;\n }\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n\n function burnFrom(address from, uint256 value) public {\n _burn(from, value);\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveGauge.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { ICurveGauge } from \"../../strategies/ICurveGauge.sol\";\n\ncontract MockCurveGauge is ICurveGauge {\n mapping(address => uint256) private _balances;\n address lpToken;\n\n constructor(address _lpToken) {\n lpToken = _lpToken;\n }\n\n function balanceOf(address account) public view override returns (uint256) {\n return _balances[account];\n }\n\n function deposit(uint256 _value, address _account) external override {\n IERC20(lpToken).transferFrom(msg.sender, address(this), _value);\n _balances[_account] += _value;\n }\n\n function withdraw(uint256 _value) external override {\n IERC20(lpToken).transfer(msg.sender, _value);\n // solhint-disable-next-line reentrancy\n _balances[msg.sender] -= _value;\n }\n}\n" + }, + "contracts/mocks/curve/MockCurveMetapool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { MockCurveAbstractMetapool } from \"./MockCurveAbstractMetapool.sol\";\nimport \"../MintableERC20.sol\";\n\ncontract MockCurveMetapool is MockCurveAbstractMetapool {\n constructor(address[2] memory _coins)\n ERC20(\"Curve.fi 3pool/OUSD metapool\", \"3crv_OUSD\")\n {\n coins = _coins;\n }\n}\n" + }, + "contracts/mocks/curve/MockCurvePool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IBurnableERC20 } from \"../BurnableERC20.sol\";\n\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\nimport { ICurvePool } from \"../../strategies/ICurvePool.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport \"../../utils/Helpers.sol\";\n\ncontract MockCurvePool {\n using StableMath for uint256;\n\n address[] public coins;\n uint256[3] public balances;\n address lpToken;\n uint256 public slippage = 1 ether;\n\n constructor(address[3] memory _coins, address _lpToken) {\n coins = _coins;\n lpToken = _lpToken;\n }\n\n function setCoins(address[] memory _coins) external {\n coins = _coins;\n }\n\n // Returns the same amount of LP tokens in 1e18 decimals\n function add_liquidity(uint256[3] calldata _amounts, uint256 _minAmount)\n external\n {\n uint256 sum = 0;\n for (uint256 i = 0; i < _amounts.length; i++) {\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transferFrom(\n msg.sender,\n address(this),\n _amounts[i]\n );\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to sum\n sum += _amounts[i].scaleBy(18, assetDecimals);\n balances[i] = balances[i] + _amounts[i];\n }\n }\n // Hacky way of simulating slippage to check _minAmount\n if (sum == 29000e18) sum = 14500e18;\n require(sum >= _minAmount, \"Slippage ruined your day\");\n // Send LP token to sender, e.g. 3CRV\n IMintableERC20(lpToken).mint(sum);\n IERC20(lpToken).transfer(msg.sender, sum);\n }\n\n // Dumb implementation that returns the same amount\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n public\n view\n returns (uint256)\n {\n uint256 assetDecimals = Helpers.getDecimals(coins[uint128(_index)]);\n return _amount.scaleBy(assetDecimals, 18);\n }\n\n function remove_liquidity_one_coin(\n uint256 _amount,\n int128 _index,\n // solhint-disable-next-line no-unused-vars\n uint256 _minAmount\n ) external {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _amount);\n uint256[] memory amounts = new uint256[](coins.length);\n amounts[uint128(_index)] = _amount;\n uint256 coinAmount = calc_withdraw_one_coin(_amount, _index);\n balances[uint128(_index)] -= coinAmount;\n IERC20(coins[uint128(_index)]).transfer(msg.sender, coinAmount);\n }\n\n function get_virtual_price() external pure returns (uint256) {\n return 1e18;\n }\n\n // solhint-disable-next-line no-unused-vars\n function remove_liquidity(uint256 _lpAmount, uint256[3] memory _min_amounts)\n public\n {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _lpAmount);\n uint256 totalSupply = IERC20(lpToken).totalSupply();\n for (uint256 i = 0; i < 3; i++) {\n uint256 coinAmount = totalSupply > 0\n ? (_lpAmount * IERC20(coins[i]).balanceOf(address(this))) /\n totalSupply\n : IERC20(coins[i]).balanceOf(address(this));\n balances[i] -= coinAmount;\n IERC20(coins[i]).transfer(msg.sender, coinAmount);\n }\n }\n\n function remove_liquidity_imbalance(\n uint256[3] memory _amounts,\n uint256 _max_burned_tokens\n ) public {\n // Burn the Curve LP tokens\n IBurnableERC20(lpToken).burnFrom(msg.sender, _max_burned_tokens);\n // For each coin, transfer to the caller\n for (uint256 i = 0; i < _amounts.length; i++) {\n balances[i] -= _amounts[i];\n if (_amounts[i] > 0) {\n IERC20(coins[i]).transfer(msg.sender, _amounts[i]);\n }\n }\n }\n\n // Dumb implementation that sums the scaled amounts\n function calc_token_amount(uint256[3] memory _amounts, bool)\n public\n view\n returns (uint256 lpTokens)\n {\n for (uint256 i = 0; i < _amounts.length; i++) {\n uint256 assetDecimals = Helpers.getDecimals(coins[i]);\n // Convert to 1e18 and add to lpTokens\n lpTokens += _amounts[i].scaleBy(18, assetDecimals);\n }\n }\n\n function fee() external pure returns (uint256) {\n return 1000000;\n }\n\n function exchange(\n uint256 coin0,\n uint256 coin1,\n uint256 amountIn,\n uint256 minAmountOut\n ) external returns (uint256 amountOut) {\n IERC20(coins[coin0]).transferFrom(msg.sender, address(this), amountIn);\n amountOut = (minAmountOut * slippage) / 1 ether;\n require(amountOut >= minAmountOut, \"Slippage error\");\n IMintableERC20(coins[coin1]).mintTo(msg.sender, amountOut);\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n}\n" + }, + "contracts/mocks/curve/MockCVX.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../MintableERC20.sol\";\n\ncontract MockCVX is MintableERC20 {\n constructor() ERC20(\"CVX\", \"CVX DAO Token\") {}\n}\n" + }, + "contracts/mocks/curve/MockRewardPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IMintableERC20 } from \"../MintableERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\ninterface IDeposit {\n function poolInfo(uint256)\n external\n view\n returns (\n address,\n address,\n address,\n address,\n address,\n bool\n );\n\n function rewardClaimed(\n uint256,\n address,\n uint256\n ) external;\n\n function withdrawTo(\n uint256,\n uint256,\n address\n ) external;\n}\n\ncontract MockRewardPool {\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 public pid;\n address public stakingToken;\n address public rewardTokenA;\n address public rewardTokenB;\n address public operator;\n\n uint256 private _totalSupply;\n\n mapping(address => uint256) private _balances;\n mapping(address => uint256) public rewards;\n\n constructor(\n uint256 _pid,\n address _stakingToken,\n address _rewardTokenA,\n address _rewardTokenB,\n address _operator\n ) public {\n pid = _pid;\n stakingToken = _stakingToken;\n rewardTokenA = _rewardTokenA;\n rewardTokenB = _rewardTokenB;\n operator = _operator;\n }\n\n function totalSupply() public view returns (uint256) {\n return _totalSupply;\n }\n\n function balanceOf(address account) public view returns (uint256) {\n return _balances[account];\n }\n\n function stakeFor(address _for, uint256 _amount) public returns (bool) {\n require(_amount > 0, \"RewardPool : Cannot stake 0\");\n\n //give to _for\n _totalSupply = _totalSupply.add(_amount);\n _balances[_for] = _balances[_for].add(_amount);\n\n //take away from sender\n IERC20(stakingToken).safeTransferFrom(\n msg.sender,\n address(this),\n _amount\n );\n\n return true;\n }\n\n function withdrawAndUnwrap(uint256 amount, bool claim)\n public\n returns (bool)\n {\n _totalSupply = _totalSupply.sub(amount);\n _balances[msg.sender] = _balances[msg.sender].sub(amount);\n\n //tell operator to withdraw from here directly to user\n IDeposit(operator).withdrawTo(pid, amount, msg.sender);\n\n //get rewards too\n if (claim) {\n getReward(msg.sender, true);\n }\n return true;\n }\n\n function withdrawAllAndUnwrap(bool claim) external {\n withdrawAndUnwrap(_balances[msg.sender], claim);\n }\n\n // solhint-disable-next-line no-unused-vars\n function getReward(address _account, bool _claimExtras)\n public\n returns (bool)\n {\n IMintableERC20(rewardTokenA).mint(2 * 1e18);\n IERC20(rewardTokenA).transfer(_account, 2 * 1e18);\n\n IMintableERC20(rewardTokenB).mint(3 * 1e18);\n IERC20(rewardTokenB).transfer(_account, 3 * 1e18);\n\n return true;\n }\n\n function getReward() public returns (bool) {\n getReward(msg.sender, true);\n }\n}\n" + }, + "contracts/mocks/ForceEtherSender.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract ForceEtherSender {\n // Constructor to optionally receive Ether upon deployment\n constructor() payable {}\n\n // Function to allow the contract to receive Ether\n receive() external payable {}\n\n // Function to self-destruct and force-send Ether to an address\n function forceSend(address payable recipient) external {\n // Requires that the contract has a balance greater than 0\n require(address(this).balance > 0, \"No Ether to send\");\n\n // selfdestruct sends all Ether held by the contract to the recipient\n selfdestruct(recipient);\n }\n}\n" + }, + "contracts/mocks/MintableERC20.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ninterface IMintableERC20 {\n function mint(uint256 value) external;\n\n function mintTo(address to, uint256 value) external;\n}\n\n/**\n * @title MintableERC20\n * @dev Exposes the mint function of ERC20 for tests\n */\nabstract contract MintableERC20 is IMintableERC20, ERC20 {\n /**\n * @dev Function to mint tokens\n * @param _value The amount of tokens to mint.\n */\n function mint(uint256 _value) public virtual override {\n _mint(msg.sender, _value);\n }\n\n /**\n * @dev Function to mint tokens\n * @param _to Address to mint to.\n * @param _value The amount of tokens to mint.\n */\n function mintTo(address _to, uint256 _value) public virtual override {\n _mint(_to, _value);\n }\n}\n" + }, + "contracts/mocks/Mock1InchSwapRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { SwapDescription } from \"../interfaces/IOneInch.sol\";\n\ncontract Mock1InchSwapRouter {\n using SafeERC20 for IERC20;\n\n event MockSwap(address executor, bytes permitData, bytes executorData);\n\n event MockSwapDesc(\n address srcToken,\n address dstToken,\n address srcReceiver,\n address dstReceiver,\n uint256 amount,\n uint256 minReturnAmount,\n uint256 flags\n );\n\n event MockUnoswapTo(\n address recipient,\n address srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] pools\n );\n\n event MockUniswapV3SwapTo(\n address recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] pools\n );\n\n /**\n * @dev transfers the shource asset and returns the minReturnAmount of the destination asset.\n */\n function swap(\n address executor,\n SwapDescription calldata desc,\n bytes calldata permitData,\n bytes calldata executorData\n ) public returns (uint256 returnAmount, uint256 spentAmount) {\n // Transfer the source tokens to the receiver contract\n IERC20(desc.srcToken).safeTransferFrom(\n msg.sender,\n desc.srcReceiver,\n desc.amount\n );\n\n // Transfer the destination tokens to the recipient\n IERC20(desc.dstToken).safeTransfer(\n desc.dstReceiver,\n desc.minReturnAmount\n );\n\n emit MockSwap(executor, permitData, executorData);\n _swapDesc(desc);\n returnAmount = 0;\n spentAmount = 0;\n }\n\n function _swapDesc(SwapDescription calldata desc) public {\n emit MockSwapDesc(\n address(desc.srcToken),\n address(desc.dstToken),\n desc.srcReceiver,\n desc.dstReceiver,\n desc.amount,\n desc.minReturnAmount,\n desc.flags\n );\n }\n\n /**\n * @dev only transfers the source asset to this contract.\n * Ideally it would return the destination asset but that's encoded in the pools array.\n */\n function unoswapTo(\n address payable recipient,\n address srcToken,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) public returns (uint256 returnAmount) {\n // transfer the from asset from the caller\n IERC20(srcToken).safeTransferFrom(msg.sender, address(this), amount);\n\n emit MockUnoswapTo(recipient, srcToken, amount, minReturn, pools);\n returnAmount = 0;\n }\n\n /**\n * @dev does not do any transfers. Just emits MockUniswapV3SwapTo.\n */\n function uniswapV3SwapTo(\n address payable recipient,\n uint256 amount,\n uint256 minReturn,\n uint256[] calldata pools\n ) public returns (uint256 returnAmount) {\n emit MockUniswapV3SwapTo(recipient, amount, minReturn, pools);\n returnAmount = 0;\n }\n}\n" + }, + "contracts/mocks/MockAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { IAaveLendingPool, ILendingPoolAddressesProvider } from \"../strategies/IAave.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// 1. User calls 'getLendingPool'\n// 2. User calls 'deposit' (Aave)\n// - Deposit their underlying\n// - Mint aToken to them\n// 3. User calls redeem (aToken)\n// - Retrieve their aToken\n// - Return equal amount of underlying\n\ncontract MockAToken is MintableERC20 {\n address public lendingPool;\n IERC20 public underlyingToken;\n using SafeERC20 for IERC20;\n\n constructor(\n address _lendingPool,\n string memory _name,\n string memory _symbol,\n IERC20 _underlyingToken\n ) ERC20(_name, _symbol) {\n lendingPool = _lendingPool;\n underlyingToken = _underlyingToken;\n // addMinter(_lendingPool);\n }\n\n function decimals() public view override returns (uint8) {\n return ERC20(address(underlyingToken)).decimals();\n }\n\n function poolRedeem(uint256 _amount, address _to) external {\n require(msg.sender == lendingPool, \"pool only\");\n // Redeem these a Tokens\n _burn(_to, _amount);\n // For the underlying\n underlyingToken.safeTransferFrom(lendingPool, _to, _amount);\n }\n}\n\ncontract MockAave is IAaveLendingPool, ILendingPoolAddressesProvider {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n mapping(address => address) reserveToAToken;\n address pool = address(this);\n address payable core = payable(address(this));\n uint256 factor;\n\n function addAToken(address _aToken, address _underlying) public {\n IERC20(_underlying).safeApprove(_aToken, 0);\n IERC20(_underlying).safeApprove(_aToken, type(uint256).max);\n reserveToAToken[_underlying] = _aToken;\n }\n\n // set the reserve factor / basically the interest on deposit\n // in 18 precision\n // so 0.5% would be 5 * 10 ^ 15\n function setFactor(uint256 factor_) public {\n factor = factor_;\n }\n\n function deposit(\n address _reserve,\n uint256 _amount,\n address _to,\n uint16 /*_referralCode*/\n ) external override {\n uint256 previousBal = IERC20(reserveToAToken[_reserve]).balanceOf(\n msg.sender\n );\n uint256 interest = previousBal.mulTruncate(factor);\n MintableERC20(reserveToAToken[_reserve]).mintTo(msg.sender, interest);\n // Take their reserve\n IERC20(_reserve).safeTransferFrom(msg.sender, address(this), _amount);\n // Credit them with aToken\n MintableERC20(reserveToAToken[_reserve]).mintTo(_to, _amount);\n }\n\n function withdraw(\n address asset,\n uint256 amount,\n address to\n ) external override returns (uint256) {\n MockAToken atoken = MockAToken(reserveToAToken[asset]);\n atoken.poolRedeem(amount, to);\n return amount;\n }\n\n function getLendingPool() external view override returns (address) {\n return pool;\n }\n}\n" + }, + "contracts/mocks/MockAaveIncentivesController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { MockStkAave } from \"./MockStkAave.sol\";\n\ncontract MockAaveIncentivesController {\n mapping(address => uint256) private rewards;\n MockStkAave public REWARD_TOKEN;\n\n constructor(address _reward_token) {\n REWARD_TOKEN = MockStkAave(_reward_token);\n }\n\n function setRewardsBalance(address user, uint256 amount) external {\n rewards[user] = amount;\n }\n\n /**\n * @dev Returns the total of rewards of an user, already accrued + not yet accrued\n * @param user The address of the user\n * @return The rewards\n **/\n // solhint-disable-next-line no-unused-vars\n function getRewardsBalance(address[] calldata assets, address user)\n external\n view\n returns (uint256)\n {\n return rewards[user];\n }\n\n /**\n * @dev Claims reward for an user, on all the assets of the lending pool, accumulating the pending rewards\n * @param amount Amount of rewards to claim\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewards(\n // solhint-disable-next-line no-unused-vars\n address[] calldata assets,\n uint256 amount,\n address to\n ) external returns (uint256) {\n require(amount > 0);\n require(rewards[to] == amount);\n REWARD_TOKEN.mint(amount);\n require(REWARD_TOKEN.transfer(to, amount));\n // solhint-disable-next-line reentrancy\n rewards[to] = 0;\n return amount;\n }\n}\n" + }, + "contracts/mocks/MockAAVEToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAAVEToken is MintableERC20 {\n constructor() ERC20(\"AAVE\", \"AAVE\") {}\n}\n" + }, + "contracts/mocks/MockAero.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAero is MintableERC20 {\n constructor() ERC20(\"Aerodrome\", \"AERO\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/MockAura.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockAura is MintableERC20 {\n constructor() ERC20(\"Aura\", \"AURA\") {}\n}\n" + }, + "contracts/mocks/MockBAL.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockBAL is MintableERC20 {\n constructor() ERC20(\"Balancer\", \"BAL\") {}\n}\n" + }, + "contracts/mocks/MockBalancerVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// import \"hardhat/console.sol\";\n\ncontract MockBalancerVault {\n using StableMath for uint256;\n uint256 public slippage = 1 ether;\n bool public transferDisabled = false;\n bool public slippageErrorDisabled = false;\n\n function swap(\n IBalancerVault.SingleSwap calldata singleSwap,\n IBalancerVault.FundManagement calldata funds,\n uint256 minAmountOut,\n uint256\n ) external returns (uint256 amountCalculated) {\n amountCalculated = (minAmountOut * slippage) / 1 ether;\n if (!slippageErrorDisabled) {\n require(amountCalculated >= minAmountOut, \"Slippage error\");\n }\n IERC20(singleSwap.assetIn).transferFrom(\n funds.sender,\n address(this),\n singleSwap.amount\n );\n if (!transferDisabled) {\n MintableERC20(singleSwap.assetOut).mintTo(\n funds.recipient,\n amountCalculated\n );\n }\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n\n function getPoolTokenInfo(bytes32 poolId, address token)\n external\n view\n returns (\n uint256,\n uint256,\n uint256,\n address\n )\n {}\n\n function disableTransfer() external {\n transferDisabled = true;\n }\n\n function disableSlippageError() external {\n slippageErrorDisabled = true;\n }\n}\n" + }, + "contracts/mocks/MockBeaconConsolidation.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconConsolidation } from \"../beacon/BeaconConsolidation.sol\";\n\ncontract MockBeaconConsolidation {\n function fee() external view returns (uint256) {\n return BeaconConsolidation.fee();\n }\n\n function request(bytes calldata source, bytes calldata target)\n external\n returns (uint256 fee_)\n {\n return BeaconConsolidation.request(source, target);\n }\n}\n" + }, + "contracts/mocks/MockBeaconOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconOracle } from \"../beacon/BeaconOracle.sol\";\n\ncontract MockBeaconOracle is BeaconOracle {\n function mapSlot(\n uint64 blockNumber,\n uint64 slot,\n bytes32 _blockRoot\n ) external {\n _blockToSlot[blockNumber] = slot;\n _slotToBlock[slot] = blockNumber;\n _slotToRoot[slot] = _blockRoot;\n }\n}\n" + }, + "contracts/mocks/MockBeaconProofs.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconProofsLib } from \"../beacon/BeaconProofsLib.sol\";\nimport { BeaconProofs } from \"../beacon/BeaconProofs.sol\";\n\ncontract MockBeaconProofs is BeaconProofs {\n function concatGenIndices(\n uint256 index1,\n uint256 height2,\n uint256 index2\n ) external pure returns (uint256 genIndex) {\n return BeaconProofsLib.concatGenIndices(index1, height2, index2);\n }\n}\n" + }, + "contracts/mocks/MockBeaconRoots.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { BeaconRoots } from \"../beacon/BeaconRoots.sol\";\n\ncontract MockBeaconRoots {\n // Mapping to simulate the ring buffer: timestamp => beacon block root\n mapping(uint256 => bytes32) internal _beaconRoots;\n\n // Event to log when a new root is set (for testing)\n event RootSet(uint256 indexed timestamp, bytes32 root);\n\n // Fallback function to handle raw 32-byte timestamp input\n // solhint-disable no-complex-fallback\n fallback() external {\n // Ensure input is exactly 32 bytes (big-endian encoded timestamp)\n require(msg.data.length == 32, \"Input must be 32 bytes\");\n\n // Decode the 32-byte input as a uint256 timestamp (big-endian)\n uint256 timestamp = abi.decode(msg.data, (uint256));\n\n // Don't do any validation of timestamp so we can test any block\n\n // Retrieve the root. Will return bytes32(0) if not set.\n bytes32 root = _beaconRoots[timestamp];\n\n // Return the 32-byte root directly\n // solhint-disable-next-line no-inline-assembly\n assembly {\n mstore(0, root)\n return(0, 32)\n }\n }\n\n // Mock function to set a beacon block root (for testing)\n function setBeaconRoot(uint256 timestamp, bytes32 root) external {\n require(timestamp > 0, \"Invalid timestamp\");\n require(root != bytes32(0), \"Invalid root\");\n\n // Store the root at the given timestamp\n _beaconRoots[timestamp] = root;\n\n emit RootSet(timestamp, root);\n }\n\n function setBeaconRoot(bytes32 root) external {\n require(root != bytes32(0), \"Invalid root\");\n\n // Store the root at the given timestamp\n _beaconRoots[block.timestamp] = root;\n\n emit RootSet(block.timestamp, root);\n }\n\n function parentBlockRoot(uint64 timestamp)\n external\n view\n returns (bytes32 parentRoot)\n {\n return BeaconRoots.parentBlockRoot(timestamp);\n }\n\n function latestBlockRoot()\n external\n view\n returns (bytes32 parentRoot, uint64 timestamp)\n {\n timestamp = uint64(block.timestamp);\n parentRoot = BeaconRoots.parentBlockRoot(timestamp);\n }\n}\n" + }, + "contracts/mocks/MockChainlinkOracleFeed.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\ncontract MockChainlinkOracleFeed is AggregatorV3Interface {\n int256 price;\n uint8 numDecimals;\n\n constructor(int256 _price, uint8 _decimals) {\n price = _price;\n numDecimals = _decimals;\n }\n\n function decimals() external view override returns (uint8) {\n return numDecimals;\n }\n\n function description() external pure override returns (string memory) {\n return \"MockOracleEthFeed\";\n }\n\n function version() external pure override returns (uint256) {\n return 1;\n }\n\n function setPrice(int256 _price) public {\n price = _price;\n }\n\n function setDecimals(uint8 _decimals) public {\n numDecimals = _decimals;\n }\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(uint80 _roundId)\n external\n view\n override\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n )\n {\n roundId = _roundId;\n answer = price;\n startedAt = 0;\n updatedAt = 0;\n answeredInRound = 0;\n }\n\n function latestRoundData()\n external\n view\n override\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n )\n {\n roundId = 0;\n answer = price;\n startedAt = 0;\n updatedAt = block.timestamp;\n answeredInRound = 0;\n }\n}\n" + }, + "contracts/mocks/MockCOMP.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockCOMP is MintableERC20 {\n constructor() ERC20(\"COMP\", \"COMP\") {}\n}\n" + }, + "contracts/mocks/MockComptroller.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract MockComptroller {\n // Claim all the COMP accrued by specific holders in specific markets for their supplies and/or borrows\n function claimComp(\n address[] memory holders,\n address[] memory cTokens,\n bool borrowers,\n bool suppliers\n ) external {}\n}\n" + }, + "contracts/mocks/MockCToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport { ICERC20 } from \"../strategies/ICompound.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockCToken is ICERC20, ERC20 {\n using StableMath for uint256;\n\n IERC20 public underlyingToken;\n // underlying = cToken * exchangeRate\n // cToken = underlying / exchangeRate\n uint256 exchangeRate;\n address public override comptroller;\n\n constructor(ERC20 _underlyingToken, address _comptroller)\n ERC20(\"cMock\", \"cMK\")\n {\n uint8 underlyingDecimals = _underlyingToken.decimals();\n // if has 18 dp, exchange rate should be 1e26\n // if has 8 dp, exchange rate should be 1e18\n if (underlyingDecimals > 8) {\n exchangeRate = 10**uint256(18 + underlyingDecimals - 10);\n } else if (underlyingDecimals < 8) {\n // e.g. 18-8+6 = 16\n exchangeRate = 10**uint256(18 - 8 + underlyingDecimals);\n } else {\n exchangeRate = 1e18;\n }\n underlyingToken = _underlyingToken;\n comptroller = _comptroller;\n }\n\n function decimals() public pure override returns (uint8) {\n return 8;\n }\n\n function mint(uint256 mintAmount) public override returns (uint256) {\n // Credit them with cToken\n _mint(msg.sender, mintAmount.divPrecisely(exchangeRate));\n // Take their reserve\n underlyingToken.transferFrom(msg.sender, address(this), mintAmount);\n return 0;\n }\n\n function redeem(uint256 redeemAmount) external override returns (uint256) {\n uint256 tokenAmount = redeemAmount.mulTruncate(exchangeRate);\n // Burn the cToken\n _burn(msg.sender, redeemAmount);\n // Transfer underlying to caller\n underlyingToken.transfer(msg.sender, tokenAmount);\n return 0;\n }\n\n function redeemUnderlying(uint256 redeemAmount)\n external\n override\n returns (uint256)\n {\n uint256 cTokens = redeemAmount.divPrecisely(exchangeRate);\n // Burn the cToken\n _burn(msg.sender, cTokens);\n // Transfer underlying to caller\n underlyingToken.transfer(msg.sender, redeemAmount);\n return 0;\n }\n\n function balanceOfUnderlying(address owner)\n external\n view\n override\n returns (uint256)\n {\n uint256 cTokenBal = this.balanceOf(owner);\n return cTokenBal.mulTruncate(exchangeRate);\n }\n\n function balanceOf(address owner)\n public\n view\n override(ICERC20, ERC20)\n returns (uint256)\n {\n return ERC20.balanceOf(owner);\n }\n\n function updateExchangeRate()\n internal\n view\n returns (uint256 newExchangeRate)\n {\n uint256 factor = 100002 * (10**13); // 0.002%\n newExchangeRate = exchangeRate.mulTruncate(factor);\n }\n\n function exchangeRateStored() external view override returns (uint256) {\n return exchangeRate;\n }\n\n function supplyRatePerBlock() external pure override returns (uint256) {\n return 141 * (10**8);\n }\n}\n" + }, + "contracts/mocks/MockCVXLocker.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MockCVXLocker {\n address public immutable cvx;\n mapping(address => uint256) public lockedBalanceOf;\n\n constructor(address _cvx) {\n cvx = _cvx;\n }\n\n function lock(\n address _account,\n uint256 _amount,\n uint256\n ) external {\n lockedBalanceOf[_account] += _amount;\n ERC20(cvx).transferFrom(msg.sender, address(this), _amount);\n }\n\n function unlockAllTokens(address _account) external {\n lockedBalanceOf[_account] = 0;\n ERC20(cvx).transfer(_account, lockedBalanceOf[_account]);\n }\n}\n" + }, + "contracts/mocks/MockDAI.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockDAI is MintableERC20 {\n constructor() ERC20(\"DAI\", \"DAI\") {}\n}\n" + }, + "contracts/mocks/MockDepositContract.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IDepositContract } from \"./../interfaces/IDepositContract.sol\";\n\ncontract MockDepositContract is IDepositContract {\n uint256 deposit_count;\n\n function deposit(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature,\n bytes32 deposit_data_root\n ) external payable override {\n require(pubkey.length == 48, \"DepositContract: invalid pubkey length\");\n require(\n withdrawal_credentials.length == 32,\n \"DepositContract: invalid withdrawal_credentials length\"\n );\n require(\n signature.length == 96,\n \"DepositContract: invalid signature length\"\n );\n\n // Check deposit amount\n require(msg.value >= 1 ether, \"DepositContract: deposit value too low\");\n require(\n msg.value % 1 gwei == 0,\n \"DepositContract: deposit value not multiple of gwei\"\n );\n uint256 deposit_amount = msg.value / 1 gwei;\n require(\n deposit_amount <= type(uint64).max,\n \"DepositContract: deposit value too high\"\n );\n\n // Emit `DepositEvent` log\n bytes memory amount = to_little_endian_64(uint64(deposit_amount));\n emit DepositEvent(\n pubkey,\n withdrawal_credentials,\n amount,\n signature,\n to_little_endian_64(uint64(deposit_count))\n );\n require(\n deposit_data_root != 0,\n \"DepositContract: invalid deposit_data_root\"\n );\n }\n\n function get_deposit_root() external view override returns (bytes32) {\n // just return some bytes32\n return sha256(abi.encodePacked(deposit_count, bytes16(0)));\n }\n\n /// @notice Query the current deposit count.\n /// @return The deposit count encoded as a little endian 64-bit number.\n function get_deposit_count() external view override returns (bytes memory) {\n return to_little_endian_64(uint64(deposit_count));\n }\n\n function to_little_endian_64(uint64 value)\n internal\n pure\n returns (bytes memory ret)\n {\n ret = new bytes(8);\n bytes8 bytesValue = bytes8(value);\n // Byteswapping during copying to bytes.\n ret[0] = bytesValue[7];\n ret[1] = bytesValue[6];\n ret[2] = bytesValue[5];\n ret[3] = bytesValue[4];\n ret[4] = bytesValue[3];\n ret[5] = bytesValue[2];\n ret[6] = bytesValue[1];\n ret[7] = bytesValue[0];\n }\n}\n" + }, + "contracts/mocks/MockEvilDAI.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract MockEvilDAI is MintableERC20 {\n address host;\n address realCoin;\n\n constructor(address _host, address _realCoin) ERC20(\"DAI\", \"DAI\") {\n host = _host;\n realCoin = _realCoin;\n }\n\n function transferFrom(\n // solhint-disable-next-line no-unused-vars\n address _from,\n // solhint-disable-next-line no-unused-vars\n address _to,\n uint256 _amount\n ) public override returns (bool) {\n // call mint again!\n if (_amount != 69) {\n IVault(host).mint(address(this), 69, 0);\n }\n return true;\n }\n}\n" + }, + "contracts/mocks/MockEvilReentrantContract.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IRateProvider } from \"../interfaces/balancer/IRateProvider.sol\";\n\nimport { IBalancerVault } from \"../interfaces/balancer/IBalancerVault.sol\";\nimport { IERC20 } from \"../utils/InitializableAbstractStrategy.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockEvilReentrantContract {\n using StableMath for uint256;\n\n IBalancerVault public immutable balancerVault;\n IERC20 public immutable reth;\n IERC20 public immutable weth;\n IVault public immutable oethVault;\n address public immutable poolAddress;\n bytes32 public immutable balancerPoolId;\n\n constructor(\n address _balancerVault,\n address _oethVault,\n address _reth,\n address _weth,\n address _poolAddress,\n bytes32 _poolId\n ) {\n balancerVault = IBalancerVault(_balancerVault);\n oethVault = IVault(_oethVault);\n reth = IERC20(_reth);\n weth = IERC20(_weth);\n poolAddress = _poolAddress;\n balancerPoolId = _poolId;\n }\n\n function doEvilStuff() public {\n address priceProvider = oethVault.priceProvider();\n uint256 rethPrice = IOracle(priceProvider).price(address(reth));\n\n // 1. Join pool\n uint256[] memory amounts = new uint256[](2);\n amounts[0] = uint256(10 ether);\n amounts[1] = rethPrice * 10;\n\n address[] memory assets = new address[](2);\n assets[0] = address(reth);\n assets[1] = address(weth);\n\n uint256 minBPT = getBPTExpected(assets, amounts).mulTruncate(\n 0.99 ether\n );\n\n bytes memory joinUserData = abi.encode(\n IBalancerVault.WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,\n amounts,\n minBPT\n );\n\n IBalancerVault.JoinPoolRequest memory joinRequest = IBalancerVault\n .JoinPoolRequest(assets, amounts, joinUserData, false);\n\n balancerVault.joinPool(\n balancerPoolId,\n address(this),\n address(this),\n joinRequest\n );\n\n uint256 bptTokenBalance = IERC20(poolAddress).balanceOf(address(this));\n\n // 2. Redeem as ETH\n bytes memory exitUserData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT,\n bptTokenBalance,\n 1\n );\n\n assets[1] = address(0); // Receive ETH instead of WETH\n uint256[] memory exitAmounts = new uint256[](2);\n exitAmounts[1] = 15 ether;\n IBalancerVault.ExitPoolRequest memory exitRequest = IBalancerVault\n .ExitPoolRequest(assets, exitAmounts, exitUserData, false);\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n payable(address(this)),\n exitRequest\n );\n bptTokenBalance = IERC20(poolAddress).balanceOf(address(this));\n }\n\n function getBPTExpected(address[] memory _assets, uint256[] memory _amounts)\n internal\n view\n virtual\n returns (uint256 bptExpected)\n {\n // Get the oracle from the OETH Vault\n address priceProvider = oethVault.priceProvider();\n\n for (uint256 i = 0; i < _assets.length; ++i) {\n uint256 strategyAssetMarketPrice = IOracle(priceProvider).price(\n _assets[i]\n );\n // convert asset amount to ETH amount\n bptExpected =\n bptExpected +\n _amounts[i].mulTruncate(strategyAssetMarketPrice);\n }\n\n uint256 bptRate = IRateProvider(poolAddress).getRate();\n // Convert ETH amount to BPT amount\n bptExpected = bptExpected.divPrecisely(bptRate);\n }\n\n function approveAllTokens() public {\n // Approve all tokens\n weth.approve(address(oethVault), type(uint256).max);\n reth.approve(poolAddress, type(uint256).max);\n weth.approve(poolAddress, type(uint256).max);\n reth.approve(address(balancerVault), type(uint256).max);\n weth.approve(address(balancerVault), type(uint256).max);\n }\n\n receive() external payable {\n // 3. Try to mint OETH\n oethVault.mint(address(weth), 1 ether, 0.9 ether);\n }\n}\n" + }, + "contracts/mocks/MockfrxETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockfrxETH is MintableERC20 {\n constructor() ERC20(\"frxETH\", \"frxETH\") {}\n}\n" + }, + "contracts/mocks/MockFrxETHMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockFrxETHMinter {\n address public immutable frxETH;\n address public immutable sfrxETH;\n\n constructor(address _frxETH, address _sfrxETH) {\n frxETH = _frxETH;\n sfrxETH = _sfrxETH;\n }\n\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares)\n {\n IMintableERC20(frxETH).mintTo(sfrxETH, msg.value);\n IMintableERC20(sfrxETH).mintTo(recipient, msg.value);\n shares = msg.value;\n }\n}\n" + }, + "contracts/mocks/MockLimitedWrappedOusd.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WrappedOusd } from \"../token/WrappedOusd.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MockLimitedWrappedOusd is WrappedOusd {\n constructor(ERC20 underlying_) WrappedOusd(underlying_) {}\n\n function maxDeposit(address)\n public\n view\n virtual\n override\n returns (uint256)\n {\n return 1e18;\n }\n}\n" + }, + "contracts/mocks/MockMaverickDistributor.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IMaverickV2Pool } from \"../interfaces/plume/IMaverickV2Pool.sol\";\n\ncontract MockMaverickDistributor {\n IERC20 public immutable rewardToken;\n\n uint256 public lastEpoch;\n uint256 public rewardAmount;\n\n constructor(address _rewardToken) {\n rewardToken = IERC20(_rewardToken);\n }\n\n function setLastEpoch(uint256 _epoch) external {\n lastEpoch = _epoch;\n }\n\n function setRewardTokenAmount(uint256 _amount) external {\n rewardAmount = _amount;\n }\n\n function claimLp(\n address recipient,\n uint256,\n IMaverickV2Pool,\n uint32[] memory,\n uint256\n ) external returns (uint256 amount) {\n rewardToken.transfer(recipient, rewardAmount);\n return rewardAmount;\n }\n}\n" + }, + "contracts/mocks/MockMetadataToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// IERC20Metadata is used in the resolveAsset function in contracts/utils/assets.js\n// We just need to import it here to make its ABI available to Hardhat\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n" + }, + "contracts/mocks/MockMintableUniswapPair.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport \"./MockUniswapPair.sol\";\n\ncontract MockMintableUniswapPair is MockUniswapPair, MintableERC20 {\n constructor(\n address _token0,\n address _token1,\n uint112 _reserve0,\n uint112 _reserve1\n )\n MockUniswapPair(_token0, _token1, _reserve0, _reserve1)\n ERC20(\"Uniswap V2\", \"UNI-v2\")\n {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/MockNonRebasing.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nimport { OUSD } from \"../token/OUSD.sol\";\n\ncontract MockNonRebasing {\n OUSD oUSD;\n\n function setOUSD(address _oUSDAddress) public {\n oUSD = OUSD(_oUSDAddress);\n }\n\n function rebaseOptIn() public {\n oUSD.rebaseOptIn();\n }\n\n function rebaseOptOut() public {\n oUSD.rebaseOptOut();\n }\n\n function transfer(address _to, uint256 _value) public {\n oUSD.transfer(_to, _value);\n }\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) public {\n oUSD.transferFrom(_from, _to, _value);\n }\n\n function approve(address _spender, uint256 _addedValue) public {\n oUSD.approve(_spender, _addedValue);\n }\n\n function mintOusd(\n address _vaultContract,\n address _asset,\n uint256 _amount\n ) public {\n IVault(_vaultContract).mint(_asset, _amount, 0);\n }\n\n function redeemOusd(address _vaultContract, uint256 _amount) public {\n IVault(_vaultContract).redeem(_amount, 0);\n }\n\n function approveFor(\n address _contract,\n address _spender,\n uint256 _addedValue\n ) public {\n IERC20(_contract).approve(_spender, _addedValue);\n }\n}\n" + }, + "contracts/mocks/MockNonStandardToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\nimport \"./MintableERC20.sol\";\n\n/**\n * Mock token contract to simulate tokens that don't\n * throw/revert when a transfer/transferFrom call fails\n */\ncontract MockNonStandardToken is MintableERC20 {\n using SafeMath for uint256;\n\n constructor() ERC20(\"NonStandardToken\", \"NonStandardToken\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n\n function transfer(address recipient, uint256 amount)\n public\n override\n returns (bool)\n {\n if (balanceOf(msg.sender) < amount) {\n // Fail silently\n return false;\n }\n\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public override returns (bool) {\n if (balanceOf(sender) < amount) {\n // Fail silently\n return false;\n }\n\n _transfer(sender, recipient, amount);\n _approve(\n sender,\n _msgSender(),\n allowance(sender, _msgSender()).sub(\n amount,\n \"ERC20: transfer amount exceeds allowance\"\n )\n );\n return true;\n }\n}\n" + }, + "contracts/mocks/MockOETHVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultCore } from \"../vault/OETHVaultCore.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MockOETHVault is OETHVaultCore {\n using StableMath for uint256;\n\n constructor(address _weth) OETHVaultCore(_weth) {\n _setGovernor(msg.sender);\n }\n\n function supportAsset(address asset) external {\n assets[asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(0),\n decimals: 18,\n allowedOracleSlippageBps: 0\n });\n\n allAssets.push(asset);\n }\n}\n" + }, + "contracts/mocks/MockOETHVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"../vault/OETHVaultAdmin.sol\";\n\ncontract MockOETHVaultAdmin is OETHVaultAdmin {\n constructor(address _weth) OETHVaultAdmin(_weth) {}\n\n // fetches the WETH amount in outstanding withdrawals\n function outstandingWithdrawalsAmount()\n external\n view\n returns (uint256 wethAmount)\n {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n wethAmount = queue.queued - queue.claimed;\n }\n\n function wethAvailable() external view returns (uint256) {\n return _wethAvailable();\n }\n}\n" + }, + "contracts/mocks/MockOGN.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./BurnableERC20.sol\";\nimport \"./MintableERC20.sol\";\n\n/**\n * @title Origin token (OGN).\n *\n * @dev Token that allows minting and burning.\n * @dev Important note:\n * @dev There is a known race condition in the ERC20 standard on the approve() method.\n * @dev See details: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n * @dev The Origin token contract implements the increaseApproval() and decreaseApproval() methods.\n * @dev It is strongly recommended to use those methods rather than approve()\n * @dev when updating the token allowance.\n */\ncontract MockOGN is MintableERC20, BurnableERC20 {\n event SetWhitelistExpiration(uint256 expiration);\n event AllowedTransactorAdded(address sender);\n event AllowedTransactorRemoved(address sender);\n event AddCallSpenderWhitelist(address enabler, address spender);\n event RemoveCallSpenderWhitelist(address disabler, address spender);\n\n mapping(address => bool) public callSpenderWhitelist;\n address public owner = msg.sender;\n // UNIX timestamp (in seconds) after which this whitelist no longer applies\n uint256 public whitelistExpiration;\n // While the whitelist is active, either the sender or recipient must be\n // in allowedTransactors.\n mapping(address => bool) public allowedTransactors;\n\n // @dev Constructor that gives msg.sender all initial tokens.\n constructor(uint256 _initialSupply) ERC20(\"OriginToken\", \"OGN\") {\n owner = msg.sender;\n _mint(owner, _initialSupply);\n }\n\n //\n // approveAndCall methods\n //\n\n // @dev Add spender to whitelist of spenders for approveAndCall\n // @param _spender Address to add\n function addCallSpenderWhitelist(address _spender) public onlyOwner {\n callSpenderWhitelist[_spender] = true;\n emit AddCallSpenderWhitelist(msg.sender, _spender);\n }\n\n // @dev Remove spender from whitelist of spenders for approveAndCall\n // @param _spender Address to remove\n function removeCallSpenderWhitelist(address _spender) public onlyOwner {\n delete callSpenderWhitelist[_spender];\n emit RemoveCallSpenderWhitelist(msg.sender, _spender);\n }\n\n // @dev Approve transfer of tokens and make a contract call in a single\n // @dev transaction. This allows a DApp to avoid requiring two MetaMask\n // @dev approvals for a single logical action, such as creating a listing,\n // @dev which requires the seller to approve a token transfer and the\n // @dev marketplace contract to transfer tokens from the seller.\n //\n // @dev This is based on the ERC827 function approveAndCall and avoids\n // @dev security issues by only working with a whitelisted set of _spender\n // @dev addresses. The other difference is that the combination of this\n // @dev function ensures that the proxied function call receives the\n // @dev msg.sender for this function as its first parameter.\n //\n // @param _spender The address that will spend the funds.\n // @param _value The amount of tokens to be spent.\n // @param _selector Function selector for function to be called.\n // @param _callParams Packed, encoded parameters, omitting the first parameter which is always msg.sender\n function approveAndCallWithSender(\n address _spender,\n uint256 _value,\n bytes4 _selector,\n bytes memory _callParams\n ) public payable returns (bool) {\n require(_spender != address(this), \"token contract can't be approved\");\n require(callSpenderWhitelist[_spender], \"spender not in whitelist\");\n\n require(super.approve(_spender, _value), \"approve failed\");\n\n bytes memory callData = abi.encodePacked(\n _selector,\n uint256(uint160(msg.sender)),\n _callParams\n );\n // solium-disable-next-line security/no-call-value\n (bool success, ) = _spender.call{ value: msg.value }(callData);\n require(success, \"proxied call failed\");\n return true;\n }\n\n //\n // Functions for maintaining whitelist\n //\n\n modifier onlyOwner() {\n require(msg.sender == owner);\n _;\n }\n modifier allowedTransfer(address _from, address _to) {\n require(\n // solium-disable-next-line operator-whitespace\n !whitelistActive() ||\n allowedTransactors[_from] ||\n allowedTransactors[_to],\n \"neither sender nor recipient are allowed\"\n );\n _;\n }\n\n function whitelistActive() public view returns (bool) {\n return block.timestamp < whitelistExpiration;\n }\n\n function addAllowedTransactor(address _transactor) public onlyOwner {\n emit AllowedTransactorAdded(_transactor);\n allowedTransactors[_transactor] = true;\n }\n\n function removeAllowedTransactor(address _transactor) public onlyOwner {\n emit AllowedTransactorRemoved(_transactor);\n delete allowedTransactors[_transactor];\n }\n\n /**\n * @dev Set the whitelist expiration, after which the whitelist no longer\n * applies.\n */\n function setWhitelistExpiration(uint256 _expiration) public onlyOwner {\n // allow only if whitelist expiration hasn't yet been set, or if the\n // whitelist expiration hasn't passed yet\n require(\n whitelistExpiration == 0 || whitelistActive(),\n \"an expired whitelist cannot be extended\"\n );\n // prevent possible mistakes in calling this function\n require(\n _expiration >= block.timestamp + 1 days,\n \"whitelist expiration not far enough into the future\"\n );\n emit SetWhitelistExpiration(_expiration);\n whitelistExpiration = _expiration;\n }\n\n //\n // ERC20 transfer functions that have been overridden to enforce the\n // whitelist.\n //\n\n function transfer(address _to, uint256 _value)\n public\n override\n allowedTransfer(msg.sender, _to)\n returns (bool)\n {\n return super.transfer(_to, _value);\n }\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) public override allowedTransfer(_from, _to) returns (bool) {\n return super.transferFrom(_from, _to, _value);\n }\n}\n" + }, + "contracts/mocks/MockOGV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockOGV is MintableERC20 {\n constructor() ERC20(\"OGV\", \"OGV\") {}\n}\n" + }, + "contracts/mocks/MockOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/IPriceOracle.sol\";\nimport \"../interfaces/IMinMaxOracle.sol\";\n\n/**\n * Mock of both price Oracle and min max oracles\n */\ncontract MockOracle is IPriceOracle, IMinMaxOracle {\n mapping(bytes32 => uint256) prices;\n mapping(bytes32 => uint256[]) pricesMinMax;\n uint256 ethMin;\n uint256 ethMax;\n\n /**\n * @dev returns the asset price in USD, 6 decimal digits.\n * Compatible with the Open Price Feed.\n */\n function price(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n return prices[keccak256(abi.encodePacked(symbol))];\n }\n\n /**\n * @dev sets the price of the asset in USD, 6 decimal digits\n *\n */\n function setPrice(string calldata symbol, uint256 _price) external {\n prices[keccak256(abi.encodePacked(symbol))] = _price;\n }\n\n /**\n * @dev sets the min and max price of ETH in USD, 6 decimal digits\n *\n */\n function setEthPriceMinMax(uint256 _min, uint256 _max) external {\n ethMin = _min;\n ethMax = _max;\n }\n\n /**\n * @dev sets the prices Min Max for a specific symbol in ETH, 8 decimal digits\n *\n */\n function setTokPriceMinMax(\n string calldata symbol,\n uint256 _min,\n uint256 _max\n ) external {\n pricesMinMax[keccak256(abi.encodePacked(symbol))] = [_min, _max];\n }\n\n /**\n * @dev get the price of asset in ETH, 8 decimal digits.\n */\n function priceMin(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n uint256[] storage pMinMax = pricesMinMax[\n keccak256(abi.encodePacked(symbol))\n ];\n return (pMinMax[0] * ethMin) / 1e6;\n }\n\n /**\n * @dev get the price of asset in USD, 8 decimal digits.\n * Not needed for now\n */\n function priceMax(string calldata symbol)\n external\n view\n override\n returns (uint256)\n {\n uint256[] storage pMinMax = pricesMinMax[\n keccak256(abi.encodePacked(symbol))\n ];\n return (pMinMax[1] * ethMax) / 1e6;\n }\n}\n" + }, + "contracts/mocks/MockOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"../oracle/AbstractOracleRouter.sol\";\n\n// @notice Oracle Router required for testing environment\ncontract MockOracleRouter is AbstractOracleRouter {\n struct FeedMetadata {\n address feedAddress;\n uint256 maxStaleness;\n }\n\n mapping(address => FeedMetadata) public assetToFeedMetadata;\n\n /* @dev Override feed and maxStaleness information for a particular asset\n * @param _asset the asset to override feed for\n * @param _feed new feed\n * @param _maxStaleness new maximum time allowed for feed data to be stale\n */\n function setFeed(\n address _asset,\n address _feed,\n uint256 _maxStaleness\n ) external {\n assetToFeedMetadata[_asset] = FeedMetadata(_feed, _maxStaleness);\n }\n\n /*\n * The dev version of the Oracle doesn't need to gas optimize and cache the decimals\n */\n function getDecimals(address _feed) internal view override returns (uint8) {\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n return AggregatorV3Interface(_feed).decimals();\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n FeedMetadata storage fm = assetToFeedMetadata[asset];\n feedAddress = fm.feedAddress;\n maxStaleness = fm.maxStaleness;\n }\n}\n" + }, + "contracts/mocks/MockOracleRouterNoStale.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { OracleRouter } from \"../oracle/OracleRouter.sol\";\nimport { OETHOracleRouter } from \"../oracle/OETHOracleRouter.sol\";\n\n// @notice Oracle Router used to bypass staleness\ncontract MockOracleRouterNoStale is OracleRouter {\n function feedMetadata(address asset)\n internal\n pure\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n (feedAddress, ) = super.feedMetadata(asset);\n maxStaleness = 365 days;\n }\n}\n\n// @notice Oracle Router used to bypass staleness\ncontract MockOETHOracleRouterNoStale is OETHOracleRouter {\n constructor() OETHOracleRouter() {}\n\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n (feedAddress, ) = super.feedMetadata(asset);\n maxStaleness = 365 days;\n }\n}\n" + }, + "contracts/mocks/MockOracleWeightedPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Variable, OracleAverageQuery, IOracleWeightedPool } from \"../interfaces/balancer/IOracleWeightedPool.sol\";\n\ncontract MockOracleWeightedPool is IOracleWeightedPool {\n uint256[] public nextResults;\n\n constructor() {\n nextResults = [1 ether, 1 ether];\n }\n\n function getTimeWeightedAverage(OracleAverageQuery[] memory)\n external\n view\n override\n returns (uint256[] memory results)\n {\n return nextResults;\n }\n\n function setNextResults(uint256[] calldata results) external {\n nextResults = results;\n }\n}\n" + }, + "contracts/mocks/MockPartialWithdrawal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PartialWithdrawal } from \"../beacon/PartialWithdrawal.sol\";\n\ncontract MockPartialWithdrawal {\n function fee() external view returns (uint256) {\n return PartialWithdrawal.fee();\n }\n\n function request(bytes calldata validatorPubKey, uint64 amount)\n external\n returns (uint256 fee_)\n {\n return PartialWithdrawal.request(validatorPubKey, amount);\n }\n}\n" + }, + "contracts/mocks/MockRebornMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n// solhint-disable-next-line no-console\nimport \"hardhat/console.sol\";\n\ncontract Sanctum {\n address public asset;\n address public vault;\n address public reborner;\n bool public shouldAttack = false;\n // should selfdestruct in the constructor\n bool public shouldDestruct = false;\n uint256 public targetMethod;\n address public ousdContract;\n\n constructor(address _asset, address _vault) {\n asset = _asset;\n vault = _vault;\n }\n\n function deploy(uint256 salt, bytes memory bytecode)\n public\n returns (address addr)\n {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt)\n }\n require(addr != address(0), \"Create2: Failed on deploy\");\n }\n\n function computeAddress(uint256 salt, bytes memory bytecode)\n public\n view\n returns (address)\n {\n bytes32 bytecodeHashHash = keccak256(bytecode);\n bytes32 _data = keccak256(\n abi.encodePacked(\n bytes1(0xff),\n address(this),\n salt,\n bytecodeHashHash\n )\n );\n return address(bytes20(_data << 96));\n }\n\n function setShouldAttack(bool _shouldAttack) public {\n shouldAttack = _shouldAttack;\n }\n\n // should call selfdestruct in the constructor\n function setShouldDesctruct(bool _shouldDestruct) public {\n shouldDestruct = _shouldDestruct;\n }\n\n function setTargetMethod(uint256 target) public {\n targetMethod = target;\n }\n\n function setOUSDAddress(address _ousdContract) public {\n ousdContract = _ousdContract;\n }\n}\n\ncontract Reborner {\n Sanctum sanctum;\n bool logging = false;\n\n constructor(address _sanctum) {\n log(\"We are created...\");\n sanctum = Sanctum(_sanctum);\n if (sanctum.shouldAttack()) {\n log(\"We are attacking now...\");\n\n uint256 target = sanctum.targetMethod();\n\n if (target == 1) {\n redeem();\n } else if (target == 2) {\n transfer();\n } else {\n mint();\n }\n }\n\n if (sanctum.shouldDestruct()) {\n bye();\n }\n }\n\n function mint() public {\n log(\"We are attempting to mint..\");\n address asset = sanctum.asset();\n address vault = sanctum.vault();\n IERC20(asset).approve(vault, 1e18);\n IVault(vault).mint(asset, 1e18, 0);\n log(\"We are now minting..\");\n }\n\n function redeem() public {\n log(\"We are attempting to redeem..\");\n address vault = sanctum.vault();\n IVault(vault).redeem(1e18, 1e18);\n log(\"We are now redeeming..\");\n }\n\n function transfer() public {\n log(\"We are attempting to transfer..\");\n address ousd = sanctum.ousdContract();\n require(IERC20(ousd).transfer(address(1), 1e18), \"transfer failed\");\n log(\"We are now transfering..\");\n }\n\n function bye() public {\n log(\"We are now destructing..\");\n selfdestruct(payable(msg.sender));\n }\n\n function log(string memory message) internal view {\n if (logging) {\n // solhint-disable-next-line no-console\n console.log(message);\n }\n }\n}\n" + }, + "contracts/mocks/MockRETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\nimport \"../interfaces/IGetExchangeRateToken.sol\";\n\ncontract MockRETH is MintableERC20, IGetExchangeRateToken {\n uint256 private exchangeRate = 12e17;\n\n constructor() ERC20(\"Rocket Pool ETH\", \"rETH\") {}\n\n function getExchangeRate() external view override returns (uint256) {\n return exchangeRate;\n }\n\n function setExchangeRate(uint256 _rate) external {\n exchangeRate = _rate;\n }\n}\n" + }, + "contracts/mocks/MockRoosterAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Rooster AMO strategy exposing extra functionality\n * @author Origin Protocol Inc\n */\n\nimport { RoosterAMOStrategy } from \"../strategies/plume/RoosterAMOStrategy.sol\";\nimport { IMaverickV2Pool } from \"../interfaces/plume/IMaverickV2Pool.sol\";\n\ncontract MockRoosterAMOStrategy is RoosterAMOStrategy {\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethpAddress,\n address _liquidityManager,\n address _poolLens,\n address _maverickPosition,\n address _maverickQuoter,\n address _mPool,\n bool _upperTickAtParity,\n address _votingDistributor,\n address _poolDistributor\n )\n RoosterAMOStrategy(\n _stratConfig,\n _wethAddress,\n _oethpAddress,\n _liquidityManager,\n _poolLens,\n _maverickPosition,\n _maverickQuoter,\n _mPool,\n _upperTickAtParity,\n _votingDistributor,\n _poolDistributor\n )\n {}\n\n function getCurrentWethShare() external view returns (uint256) {\n uint256 _currentPrice = getPoolSqrtPrice();\n\n return _getWethShare(_currentPrice);\n }\n}\n" + }, + "contracts/mocks/MockSFC.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockSFC {\n error ZeroAmount();\n error TransferFailed();\n error StakeIsFullySlashed();\n\n // Mapping of delegator address to validator ID to amount delegated\n mapping(address => mapping(uint256 => uint256)) public delegations;\n // Mapping of delegator address to validator ID to withdrawal request ID to amount\n mapping(address => mapping(uint256 => mapping(uint256 => uint256)))\n public withdraws;\n // validator ID -> slashing refund ratio (allows to withdraw slashed stake)\n mapping(uint256 => uint256) public slashingRefundRatio;\n\n function getStake(address delegator, uint256 validatorID)\n external\n view\n returns (uint256)\n {\n return delegations[delegator][validatorID];\n }\n\n function delegate(uint256 validatorID) external payable {\n if (msg.value == 0) {\n revert ZeroAmount();\n }\n delegations[msg.sender][validatorID] += msg.value;\n }\n\n function undelegate(\n uint256 validatorID,\n uint256 wrID,\n uint256 amount\n ) external {\n require(\n delegations[msg.sender][validatorID] >= amount,\n \"insufficient stake\"\n );\n require(\n withdraws[msg.sender][validatorID][wrID] == 0,\n \"withdrawal request already exists\"\n );\n\n delegations[msg.sender][validatorID] -= amount;\n withdraws[msg.sender][validatorID][wrID] = amount;\n }\n\n function withdraw(uint256 validatorID, uint256 wrID) external {\n require(withdraws[msg.sender][validatorID][wrID] > 0, \"no withdrawal\");\n\n uint256 withdrawAmount = withdraws[msg.sender][validatorID][wrID];\n uint256 penalty = (withdrawAmount *\n (1e18 - slashingRefundRatio[validatorID])) / 1e18;\n\n if (penalty >= withdrawAmount) {\n revert StakeIsFullySlashed();\n }\n\n (bool sent, ) = msg.sender.call{ value: withdrawAmount - penalty }(\"\");\n if (!sent) {\n revert TransferFailed();\n }\n }\n\n function pendingRewards(address delegator, uint256 validatorID)\n external\n view\n returns (uint256)\n {}\n\n function claimRewards(uint256 validatorID) external {}\n\n function restakeRewards(uint256 validatorID) external {}\n\n /// @param refundRatio the percentage of the staked amount that can be refunded. 0.1e18 = 10%, 1e18 = 100%\n function slashValidator(uint256 validatorID, uint256 refundRatio) external {\n require(refundRatio <= 1e18, \"invalid refund ratio\");\n slashingRefundRatio[validatorID] = refundRatio;\n }\n}\n" + }, + "contracts/mocks/MocksfrxETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MocksfrxETH is MintableERC20 {\n address public frxETH;\n\n constructor(address _frxETH) ERC20(\"sfrxETH\", \"sfrxETH\") {\n frxETH = _frxETH;\n }\n\n function setMockfrxETHAddress(address _frxETH) external {\n frxETH = _frxETH;\n }\n\n function deposit(uint256 assets, address receiver)\n external\n returns (uint256 shares)\n {\n ERC20(frxETH).transferFrom(msg.sender, address(this), assets);\n\n _mint(receiver, assets);\n\n return assets;\n }\n\n function maxWithdraw(address owner) external view returns (uint256) {\n return balanceOf(owner);\n }\n\n function setMaxWithdrawableBalance(address owner, uint256 balance)\n external\n {\n uint256 currentBalance = balanceOf(owner);\n if (currentBalance > balance) {\n _burn(owner, currentBalance - balance);\n } else if (balance > currentBalance) {\n _mint(owner, balance - currentBalance);\n }\n }\n\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets) {\n _burn(owner, shares);\n\n ERC20(frxETH).transfer(receiver, shares);\n\n assets = shares;\n }\n\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares) {\n _burn(owner, assets);\n\n ERC20(frxETH).transfer(receiver, assets);\n\n shares = assets;\n }\n\n function submitAndDeposit(address recipient)\n external\n payable\n returns (uint256 shares)\n {\n _mint(recipient, msg.value);\n shares = msg.value;\n }\n}\n" + }, + "contracts/mocks/MockSSV.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockSSV is MintableERC20 {\n constructor() ERC20(\"SSV Token\", \"SSV\") {}\n}\n" + }, + "contracts/mocks/MockSSVNetwork.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Cluster } from \"./../interfaces/ISSVNetwork.sol\";\n\ncontract MockSSVNetwork {\n function registerValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function bulkRegisterValidator(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function exitValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds\n ) external {}\n\n function removeValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster memory cluster\n ) external {}\n\n function deposit(\n address clusterOwner,\n uint64[] calldata operatorIds,\n uint256 amount,\n Cluster memory cluster\n ) external {}\n\n function setFeeRecipientAddress(address recipient) external {}\n}\n" + }, + "contracts/mocks/MockstETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockstETH is MintableERC20 {\n constructor() ERC20(\"stETH\", \"stETH\") {}\n}\n" + }, + "contracts/mocks/MockStkAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"./MintableERC20.sol\";\n\ncontract MockStkAave is MintableERC20 {\n uint256 public COOLDOWN_SECONDS = 864000;\n uint256 public UNSTAKE_WINDOW = 172800;\n address public STAKED_TOKEN;\n\n mapping(address => uint256) public stakerRewardsToClaim;\n mapping(address => uint256) public stakersCooldowns;\n\n using SafeERC20 for IERC20;\n\n constructor(address _stakedToken) ERC20(\"Staked Aave\", \"stkAAVE\") {\n STAKED_TOKEN = _stakedToken;\n }\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n\n function setStakedToken(address _stakedToken) external {\n STAKED_TOKEN = _stakedToken;\n }\n\n /**\n * @dev Redeems staked tokens, and stop earning rewards\n * @param to Address to redeem to\n * @param amount Amount to redeem\n **/\n function redeem(address to, uint256 amount) external {\n uint256 cooldownStartTimestamp = stakersCooldowns[msg.sender];\n uint256 windowStart = cooldownStartTimestamp + COOLDOWN_SECONDS;\n require(amount != 0, \"INVALID_ZERO_AMOUNT\");\n require(block.timestamp > windowStart, \"INSUFFICIENT_COOLDOWN\");\n require(\n block.timestamp - windowStart <= UNSTAKE_WINDOW,\n \"UNSTAKE_WINDOW_FINISHED\"\n );\n uint256 balanceOfMessageSender = balanceOf(msg.sender);\n uint256 amountToRedeem = (amount > balanceOfMessageSender)\n ? balanceOfMessageSender\n : amount;\n\n stakersCooldowns[msg.sender] = 0;\n _burn(msg.sender, amountToRedeem);\n IERC20(STAKED_TOKEN).safeTransfer(to, amountToRedeem);\n }\n\n /**\n * @dev Activates the cooldown period to unstake\n * - It can't be called if the user is not staking\n **/\n function cooldown() external {\n require(balanceOf(msg.sender) != 0, \"INVALID_BALANCE_ON_COOLDOWN\");\n stakersCooldowns[msg.sender] = block.timestamp;\n }\n\n /**\n * @dev Test helper function to allow changing the cooldown\n **/\n function setCooldown(address account, uint256 _cooldown) external {\n stakersCooldowns[account] = _cooldown;\n }\n}\n" + }, + "contracts/mocks/MockStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract MockStrategy {\n address[] public assets;\n\n address public withdrawAllAsset;\n address public withdrawAllRecipient;\n\n constructor() {}\n\n function deposit(address asset, uint256 amount) external {}\n\n function depositAll() external {}\n\n function withdraw(\n address recipient,\n address asset,\n uint256 amount\n ) external {\n IERC20(asset).transfer(recipient, amount);\n }\n\n function withdrawAll() external {\n IERC20(withdrawAllAsset).transfer(\n withdrawAllRecipient,\n IERC20(withdrawAllAsset).balanceOf(address(this))\n );\n }\n\n function checkBalance(address asset)\n external\n view\n returns (uint256 balance)\n {\n balance = IERC20(asset).balanceOf(address(this));\n }\n\n function supportsAsset(address) external view returns (bool) {\n return true;\n }\n\n function collectRewardTokens() external {}\n\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return new address[](0);\n }\n\n function setWithdrawAll(address asset, address recipient) external {\n withdrawAllAsset = asset;\n withdrawAllRecipient = recipient;\n }\n}\n" + }, + "contracts/mocks/MockSwapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IMintableERC20 } from \"./MintableERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract MockSwapper {\n uint256 public nextOutAmount;\n\n function swap(\n // solhint-disable-next-line no-unused-vars\n address _fromAsset,\n address _toAsset,\n // solhint-disable-next-line no-unused-vars\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n // solhint-disable-next-line no-unused-vars\n bytes calldata _data\n ) external returns (uint256 toAssetAmount) {\n toAssetAmount = (nextOutAmount > 0) ? nextOutAmount : _minToAssetAmount;\n nextOutAmount = 0;\n IMintableERC20(_toAsset).mint(toAssetAmount);\n IERC20(_toAsset).transfer(msg.sender, toAssetAmount);\n }\n\n function setNextOutAmount(uint256 _nextOutAmount) public {\n nextOutAmount = _nextOutAmount;\n }\n}\n" + }, + "contracts/mocks/MockTUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockTUSD is MintableERC20 {\n constructor() ERC20(\"TrueUSD\", \"TUSD\") {}\n\n function decimals() public pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/mocks/MockUniswapPair.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IUniswapV2Pair } from \"../interfaces/uniswap/IUniswapV2Pair.sol\";\n\ncontract MockUniswapPair is IUniswapV2Pair {\n address tok0;\n address tok1;\n uint112 reserve0;\n uint112 reserve1;\n uint256 blockTimestampLast;\n\n bool public hasSynced = false;\n\n constructor(\n address _token0,\n address _token1,\n uint112 _reserve0,\n uint112 _reserve1\n ) {\n tok0 = _token0;\n tok1 = _token1;\n reserve0 = _reserve0;\n reserve1 = _reserve1;\n blockTimestampLast = block.timestamp;\n }\n\n function token0() external view override returns (address) {\n return tok0;\n }\n\n function token1() external view override returns (address) {\n return tok1;\n }\n\n function getReserves()\n external\n view\n override\n returns (\n uint112,\n uint112,\n uint32\n )\n {\n return (reserve0, reserve1, uint32(blockTimestampLast));\n }\n\n function setReserves(uint112 _reserve0, uint112 _reserve1) public {\n reserve0 = _reserve0;\n reserve1 = _reserve1;\n blockTimestampLast = block.timestamp;\n }\n\n // CAUTION This will not work if you setReserves multiple times over\n // multiple different blocks because then it wouldn't be a continuous\n // reserve factor over that blockTimestamp, this assumes an even reserve\n // ratio all the way through\n function price0CumulativeLast() external view override returns (uint256) {\n return\n uint256(FixedPoint.fraction(reserve1, reserve0)._x) *\n blockTimestampLast;\n }\n\n function price1CumulativeLast() external view override returns (uint256) {\n return\n uint256(FixedPoint.fraction(reserve0, reserve1)._x) *\n blockTimestampLast;\n }\n\n function sync() external override {\n hasSynced = true;\n }\n\n function checkHasSynced() external view {\n require(hasSynced, \"Not synced\");\n }\n}\n\n// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))\nlibrary FixedPoint {\n // range: [0, 2**112 - 1]\n // resolution: 1 / 2**112\n struct uq112x112 {\n uint224 _x;\n }\n\n // returns a uq112x112 which represents the ratio of the numerator to the denominator\n // equivalent to encode(numerator).div(denominator)\n function fraction(uint112 numerator, uint112 denominator)\n internal\n pure\n returns (uq112x112 memory)\n {\n require(denominator > 0, \"FixedPoint: DIV_BY_ZERO\");\n return uq112x112((uint224(numerator) << 112) / denominator);\n }\n}\n" + }, + "contracts/mocks/MockUniswapRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { MintableERC20 } from \"./MintableERC20.sol\";\nimport { IUniswapV2Router } from \"../interfaces/uniswap/IUniswapV2Router02.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MockUniswapRouter is IUniswapV2Router {\n using StableMath for uint256;\n\n mapping(address => address) public pairMaps;\n uint256 public slippage = 1 ether;\n\n function initialize(\n address[] calldata _0tokens,\n address[] calldata _1tokens\n ) public {\n require(\n _0tokens.length == _1tokens.length,\n \"Mock token pairs should be of the same length\"\n );\n for (uint256 i = 0; i < _0tokens.length; i++) {\n pairMaps[_0tokens[i]] = _1tokens[i];\n }\n }\n\n function setSlippage(uint256 _slippage) external {\n slippage = _slippage;\n }\n\n function swapExactTokensForTokens(\n uint256 amountIn,\n uint256 amountOutMin,\n address[] calldata path,\n address to,\n // solhint-disable-next-line no-unused-vars\n uint256\n ) external override returns (uint256[] memory amountsOut) {\n address tok0 = path[0];\n address tok1 = path[path.length - 1];\n\n uint256 amountOut = (amountOutMin * slippage) / 1 ether;\n require(amountOut >= amountOutMin, \"Slippage error\");\n\n IERC20(tok0).transferFrom(msg.sender, address(this), amountIn);\n MintableERC20(tok1).mintTo(to, amountOut);\n\n amountsOut = new uint256[](path.length);\n amountsOut[path.length - 1] = amountOut;\n }\n\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 deadline;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n function exactInput(ExactInputParams calldata params)\n external\n payable\n returns (uint256 amountOut)\n {\n (address tok0, address tok1) = _getFirstAndLastToken(params.path);\n\n amountOut = (params.amountOutMinimum * slippage) / 1 ether;\n\n IERC20(tok0).transferFrom(msg.sender, address(this), params.amountIn);\n MintableERC20(tok1).mintTo(params.recipient, amountOut);\n\n require(\n amountOut >= params.amountOutMinimum,\n \"UniswapMock: amountOut less than amountOutMinimum\"\n );\n return amountOut;\n }\n\n function addLiquidity(\n address tokenA,\n address tokenB,\n uint256 amountADesired,\n uint256 amountBDesired,\n uint256 amountAMin,\n uint256 amountBMin,\n address to,\n uint256 deadline\n )\n external\n override\n returns (\n uint256 amountA,\n uint256 amountB,\n uint256 liquidity\n )\n {\n // this is needed to make this contract whole else it'd be just virtual\n }\n\n function WETH() external pure override returns (address) {\n return address(0);\n }\n\n // Universal router mock\n function execute(\n bytes calldata,\n bytes[] calldata inputs,\n uint256\n ) external payable {\n uint256 inLen = inputs.length;\n for (uint256 i = 0; i < inLen; ++i) {\n (\n address recipient,\n ,\n uint256 amountOutMinimum,\n bytes memory path,\n\n ) = abi.decode(inputs[i], (address, uint256, uint256, bytes, bool));\n\n (address token0, address token1) = _getFirstAndLastToken(path);\n\n amountOutMinimum = amountOutMinimum.scaleBy(\n Helpers.getDecimals(token0),\n Helpers.getDecimals(token1)\n );\n\n MintableERC20(token1).mintTo(recipient, amountOutMinimum);\n }\n }\n\n function _getFirstAndLastToken(bytes memory path)\n internal\n view\n returns (address token0, address token1)\n {\n bytes memory tok0Bytes = new bytes(20);\n for (uint256 j = 0; j < 20; ++j) {\n tok0Bytes[j] = path[j];\n }\n token0 = address(bytes20(tok0Bytes));\n\n if (pairMaps[token0] != address(0)) {\n token0 = pairMaps[token0];\n }\n\n bytes memory tok1Bytes = new bytes(20);\n uint256 tok1Offset = path.length - 20;\n for (uint256 j = 0; j < 20; ++j) {\n tok1Bytes[j] = path[j + tok1Offset];\n }\n token1 = address(bytes20(tok1Bytes));\n\n if (pairMaps[token1] != address(0)) {\n token1 = pairMaps[token1];\n }\n }\n}\n" + }, + "contracts/mocks/MockUSDC.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDC is MintableERC20 {\n constructor() ERC20(\"USDC Coin\", \"USDC\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n}\n" + }, + "contracts/mocks/MockUSDS.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDS is MintableERC20 {\n constructor() ERC20(\"USDS\", \"USDS\") {}\n}\n" + }, + "contracts/mocks/MockUSDT.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockUSDT is MintableERC20 {\n constructor() ERC20(\"USDT Coin\", \"USDT\") {}\n\n function decimals() public pure override returns (uint8) {\n return 6;\n }\n}\n" + }, + "contracts/mocks/MockVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultCore } from \"../vault/VaultCore.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { VaultInitializer } from \"../vault/VaultInitializer.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MockVault is VaultCore {\n using StableMath for uint256;\n\n uint256 storedTotalValue;\n\n function setTotalValue(uint256 _value) public {\n storedTotalValue = _value;\n }\n\n function totalValue() external view override returns (uint256) {\n return storedTotalValue;\n }\n\n function _totalValue() internal view override returns (uint256) {\n return storedTotalValue;\n }\n\n function _checkBalance(address _asset)\n internal\n view\n override\n returns (uint256 balance)\n {\n // Avoids rounding errors by returning the total value\n // in a single currency\n if (allAssets[0] == _asset) {\n uint256 decimals = Helpers.getDecimals(_asset);\n return storedTotalValue.scaleBy(decimals, 18);\n } else {\n return 0;\n }\n }\n\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external onlyGovernor {\n maxSupplyDiff = _maxSupplyDiff;\n }\n}\n" + }, + "contracts/mocks/MockVaultCoreInstantRebase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { VaultCore } from \"../vault/VaultCore.sol\";\n\ncontract MockVaultCoreInstantRebase is VaultCore {\n function _nextYield(uint256 supply, uint256 vaultValue)\n internal\n view\n override\n returns (uint256 yield, uint256 targetRate)\n {\n if (vaultValue <= supply) {\n return (0, 0);\n }\n yield = vaultValue - supply;\n return (yield, 0);\n }\n}\n" + }, + "contracts/mocks/MockWETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\n// Just importing to \"unbreak\" coverage tests\nimport { IERC721Receiver } from \"@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol\";\n\ncontract MockWETH is MintableERC20 {\n constructor() ERC20(\"WETH\", \"WETH\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/MockWS.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"./MintableERC20.sol\";\n\ncontract MockWS is MintableERC20 {\n constructor() ERC20(\"Wrapped Sonic\", \"wS\") {}\n\n function deposit() external payable {\n _mint(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) external {\n _burn(msg.sender, wad);\n payable(msg.sender).transfer(wad);\n }\n}\n" + }, + "contracts/mocks/TestUpgradedOUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../token/OUSD.sol\";\n\n// used to alter internal state of OUSD contract\ncontract TestUpgradedOUSD is OUSD {\n constructor() OUSD() {}\n\n function overwriteCreditBalances(address _account, uint256 _creditBalance)\n public\n {\n creditBalances[_account] = _creditBalance;\n }\n\n function overwriteAlternativeCPT(address _account, uint256 _acpt) public {\n alternativeCreditsPerToken[_account] = _acpt;\n }\n\n function overwriteRebaseState(address _account, RebaseOptions _rebaseOption)\n public\n {\n rebaseState[_account] = _rebaseOption;\n }\n}\n" + }, + "contracts/oracle/AbstractOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\n// @notice Abstract functionality that is shared between various Oracle Routers\nabstract contract AbstractOracleRouter is IOracle {\n using StableMath for uint256;\n using SafeCast for int256;\n\n uint256 internal constant MIN_DRIFT = 0.7e18;\n uint256 internal constant MAX_DRIFT = 1.3e18;\n address internal constant FIXED_PRICE =\n 0x0000000000000000000000000000000000000001;\n // Maximum allowed staleness buffer above normal Oracle maximum staleness\n uint256 internal constant STALENESS_BUFFER = 1 days;\n mapping(address => uint8) internal decimalsCache;\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n returns (address feedAddress, uint256 maxStaleness);\n\n /**\n * @notice Returns the total price in 18 digit unit for a given asset.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n if (shouldBePegged(asset)) {\n require(_price <= MAX_DRIFT, \"Oracle: Price exceeds max\");\n require(_price >= MIN_DRIFT, \"Oracle: Price under min\");\n }\n return _price;\n }\n\n function getDecimals(address _feed) internal view virtual returns (uint8) {\n uint8 decimals = decimalsCache[_feed];\n require(decimals > 0, \"Oracle: Decimals not cached\");\n return decimals;\n }\n\n /**\n * @notice Before an asset/feed price is fetches for the first time the\n * decimals need to be cached. This is a gas optimization\n * @param asset address of the asset\n * @return uint8 corresponding asset decimals\n */\n function cacheDecimals(address asset) external returns (uint8) {\n (address _feed, ) = feedMetadata(asset);\n require(_feed != address(0), \"Asset not available\");\n require(_feed != FIXED_PRICE, \"Fixed price feeds not supported\");\n\n uint8 decimals = AggregatorV3Interface(_feed).decimals();\n decimalsCache[_feed] = decimals;\n return decimals;\n }\n\n function shouldBePegged(address _asset) internal view returns (bool) {\n string memory symbol = Helpers.getSymbol(_asset);\n bytes32 symbolHash = keccak256(abi.encodePacked(symbol));\n return\n symbolHash == keccak256(abi.encodePacked(\"DAI\")) ||\n symbolHash == keccak256(abi.encodePacked(\"USDC\")) ||\n symbolHash == keccak256(abi.encodePacked(\"USDT\"));\n }\n}\n" + }, + "contracts/oracle/OETHBaseOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\n// @notice Oracle Router (for OETH on Base) that denominates all prices in ETH\ncontract OETHBaseOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n using SafeCast for int256;\n\n address constant WETH = 0x4200000000000000000000000000000000000006;\n address constant WOETH = 0xD8724322f44E5c58D7A815F542036fb17DbbF839;\n address constant WOETH_CHAINLINK_FEED =\n 0xe96EB1EDa83d18cbac224233319FA5071464e1b9;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == WETH) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == WOETH) {\n // Chainlink: https://data.chain.link/feeds/base/base/woeth-oeth-exchange-rate\n // Bridged wOETH/OETH\n feedAddress = WOETH_CHAINLINK_FEED;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OETHFixedOracle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHOracleRouter } from \"./OETHOracleRouter.sol\";\n\n// @notice Oracle Router that returns 1e18 for all prices\n// used solely for deployment to testnets\ncontract OETHFixedOracle is OETHOracleRouter {\n constructor() OETHOracleRouter() {}\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n // solhint-disable-next-line no-unused-vars\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n // fixes price for all of the assets\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n }\n}\n" + }, + "contracts/oracle/OETHOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n// @notice Oracle Router that denominates all prices in ETH\ncontract OETHOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = uint256(_iprice).scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == 0x5E8422345238F34275888049021821E8E08CAa1f) {\n // frxETH/ETH\n feedAddress = 0xC58F3385FBc1C8AD2c0C9a061D7c13b141D7A5Df;\n maxStaleness = 18 hours + STALENESS_BUFFER;\n } else if (asset == 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/steth-eth\n // Chainlink: stETH/ETH\n feedAddress = 0x86392dC19c0b719886221c78AB11eb8Cf5c52812;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xae78736Cd615f374D3085123A210448E74Fc6393) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/reth-eth\n // Chainlink: rETH/ETH\n feedAddress = 0x536218f9E9Eb48863970252233c8F271f554C2d0;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xD533a949740bb3306d119CC777fa900bA034cd52) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/crv-eth\n // Chainlink: CRV/ETH\n feedAddress = 0x8a12Be339B0cD1829b91Adc01977caa5E9ac121e;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/cvx-eth\n // Chainlink: CVX/ETH\n feedAddress = 0xC9CbF687f43176B302F03f5e58470b77D07c61c6;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xBe9895146f7AF43049ca1c1AE358B0541Ea49704) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/cbeth-eth\n // Chainlink: cbETH/ETH\n feedAddress = 0xF017fcB346A1885194689bA23Eff2fE6fA5C483b;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xba100000625a3754423978a60c9317c58a424e3D) {\n // https://data.chain.link/ethereum/mainnet/crypto-eth/bal-eth\n // Chainlink: BAL/ETH\n feedAddress = 0xC1438AA3823A6Ba0C159CfA8D98dF5A994bA120b;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OETHPlumeOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\n\n// @notice Oracle Router (for OETH on Plume) that denominates all prices in ETH\ncontract OETHPlumeOracleRouter is AbstractOracleRouter {\n using StableMath for uint256;\n using SafeCast for int256;\n\n address constant WETH = 0xca59cA09E5602fAe8B629DeE83FfA819741f14be;\n address constant WOETH = 0xD8724322f44E5c58D7A815F542036fb17DbbF839;\n // Ref: https://docs.eo.app/docs/eprice/feed-addresses/plume\n address constant WOETH_ORACLE_FEED =\n 0x4915600Ed7d85De62011433eEf0BD5399f677e9b;\n\n constructor() {}\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * This implementation does not (!) do range checks as the\n * parent OracleRouter does.\n * @param asset address of the asset\n * @return uint256 unit price for 1 asset unit, in 18 decimal fixed\n */\n function price(address asset)\n external\n view\n virtual\n override\n returns (uint256)\n {\n (address _feed, uint256 maxStaleness) = feedMetadata(asset);\n if (_feed == FIXED_PRICE) {\n return 1e18;\n }\n require(_feed != address(0), \"Asset not available\");\n\n // slither-disable-next-line unused-return\n (, int256 _iprice, , uint256 updatedAt, ) = AggregatorV3Interface(_feed)\n .latestRoundData();\n\n require(\n updatedAt + maxStaleness >= block.timestamp,\n \"Oracle price too old\"\n );\n\n uint8 decimals = getDecimals(_feed);\n uint256 _price = _iprice.toUint256().scaleBy(18, decimals);\n return _price;\n }\n\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n view\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n if (asset == WETH) {\n // FIXED_PRICE: WETH/ETH\n feedAddress = FIXED_PRICE;\n maxStaleness = 0;\n } else if (asset == WOETH) {\n // Chainlink: https://data.chain.link/feeds/base/base/woeth-oeth-exchange-rate\n // Bridged wOETH/OETH\n feedAddress = WOETH_ORACLE_FEED;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { AbstractOracleRouter } from \"./AbstractOracleRouter.sol\";\n\n// @notice Oracle Router that denominates all prices in USD\ncontract OracleRouter is AbstractOracleRouter {\n /**\n * @dev The price feed contract to use for a particular asset along with\n * maximum data staleness\n * @param asset address of the asset\n * @return feedAddress address of the price feed for the asset\n * @return maxStaleness maximum acceptable data staleness duration\n */\n function feedMetadata(address asset)\n internal\n pure\n virtual\n override\n returns (address feedAddress, uint256 maxStaleness)\n {\n /* + STALENESS_BUFFER is added in case Oracle for some reason doesn't\n * update on heartbeat and we add a generous buffer amount.\n */\n if (asset == 0x6B175474E89094C44Da98b954EedeAC495271d0F) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/dai-usd\n // Chainlink: DAI/USD\n feedAddress = 0xAed0c38402a5d19df6E4c03F4E2DceD6e29c1ee9;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xdC035D45d973E3EC169d2276DDab16f1e407384F) {\n // https://data.chain.link/feeds/ethereum/mainnet/usds-usd\n // Chainlink: USDS/USD\n feedAddress = 0xfF30586cD0F29eD462364C7e81375FC0C71219b1;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/usdc-usd\n // Chainlink: USDC/USD\n feedAddress = 0x8fFfFfd4AfB6115b954Bd326cbe7B4BA576818f6;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xdAC17F958D2ee523a2206206994597C13D831ec7) {\n // https://data.chain.link/ethereum/mainnet/stablecoins/usdt-usd\n // Chainlink: USDT/USD\n feedAddress = 0x3E7d1eAB13ad0104d2750B8863b489D65364e32D;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0xc00e94Cb662C3520282E6f5717214004A7f26888) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/comp-usd\n // Chainlink: COMP/USD\n feedAddress = 0xdbd020CAeF83eFd542f4De03e3cF0C28A4428bd5;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/aave-usd\n // Chainlink: AAVE/USD\n feedAddress = 0x547a514d5e3769680Ce22B2361c10Ea13619e8a9;\n maxStaleness = 1 hours + STALENESS_BUFFER;\n } else if (asset == 0xD533a949740bb3306d119CC777fa900bA034cd52) {\n // https://data.chain.link/ethereum/mainnet/crypto-usd/crv-usd\n // Chainlink: CRV/USD\n feedAddress = 0xCd627aA160A6fA45Eb793D19Ef54f5062F20f33f;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else if (asset == 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B) {\n // Chainlink: CVX/USD\n feedAddress = 0xd962fC30A72A84cE50161031391756Bf2876Af5D;\n maxStaleness = 1 days + STALENESS_BUFFER;\n } else {\n revert(\"Asset not available\");\n }\n }\n}\n" + }, + "contracts/oracle/OSonicOracleRouter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHFixedOracle } from \"./OETHFixedOracle.sol\";\n\n// @notice Oracle Router that returns 1e18 for all prices\n// used solely for deployment to testnets\ncontract OSonicOracleRouter is OETHFixedOracle {\n constructor() OETHFixedOracle() {}\n}\n" + }, + "contracts/poolBooster/AbstractPoolBoosterFactory.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IPoolBoostCentralRegistry } from \"../interfaces/poolBooster/IPoolBoostCentralRegistry.sol\";\n\n/**\n * @title Abstract Pool booster factory\n * @author Origin Protocol Inc\n */\ncontract AbstractPoolBoosterFactory is Governable {\n struct PoolBoosterEntry {\n address boosterAddress;\n address ammPoolAddress;\n IPoolBoostCentralRegistry.PoolBoosterType boosterType;\n }\n\n // @notice address of Origin Sonic\n address public immutable oSonic;\n // @notice Central registry contract\n IPoolBoostCentralRegistry public immutable centralRegistry;\n\n // @notice list of all the pool boosters created by this factory\n PoolBoosterEntry[] public poolBoosters;\n // @notice mapping of AMM pool to pool booster\n mapping(address => PoolBoosterEntry) public poolBoosterFromPool;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) {\n require(_oSonic != address(0), \"Invalid oSonic address\");\n require(_governor != address(0), \"Invalid governor address\");\n require(\n _centralRegistry != address(0),\n \"Invalid central registry address\"\n );\n\n oSonic = _oSonic;\n centralRegistry = IPoolBoostCentralRegistry(_centralRegistry);\n _setGovernor(_governor);\n }\n\n /**\n * @notice Goes over all the pool boosters created by this factory and\n * calls bribe() on them.\n * @param _exclusionList A list of pool booster addresses to skip when\n * calling this function.\n */\n function bribeAll(address[] memory _exclusionList) external {\n uint256 lengthI = poolBoosters.length;\n for (uint256 i = 0; i < lengthI; i++) {\n address poolBoosterAddress = poolBoosters[i].boosterAddress;\n bool skipBribeCall = false;\n uint256 lengthJ = _exclusionList.length;\n for (uint256 j = 0; j < lengthJ; j++) {\n // pool booster in exclusion list\n if (_exclusionList[j] == poolBoosterAddress) {\n skipBribeCall = true;\n break;\n }\n }\n\n if (!skipBribeCall) {\n IPoolBooster(poolBoosterAddress).bribe();\n }\n }\n }\n\n /**\n * @notice Removes the pool booster from the internal list of pool boosters.\n * @dev This action does not destroy the pool booster contract nor does it\n * stop the yield delegation to it.\n * @param _poolBoosterAddress address of the pool booster\n */\n function removePoolBooster(address _poolBoosterAddress)\n external\n onlyGovernor\n {\n uint256 boostersLen = poolBoosters.length;\n for (uint256 i = 0; i < boostersLen; ++i) {\n if (poolBoosters[i].boosterAddress == _poolBoosterAddress) {\n // erase mapping\n delete poolBoosterFromPool[poolBoosters[i].ammPoolAddress];\n\n // overwrite current pool booster with the last entry in the list\n poolBoosters[i] = poolBoosters[boostersLen - 1];\n // drop the last entry\n poolBoosters.pop();\n\n centralRegistry.emitPoolBoosterRemoved(_poolBoosterAddress);\n break;\n }\n }\n }\n\n function _storePoolBoosterEntry(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType _boosterType\n ) internal {\n PoolBoosterEntry memory entry = PoolBoosterEntry(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType\n );\n\n poolBoosters.push(entry);\n poolBoosterFromPool[_ammPoolAddress] = entry;\n\n // emit the events of the pool booster created\n centralRegistry.emitPoolBoosterCreated(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType\n );\n }\n\n function _deployContract(bytes memory _bytecode, uint256 _salt)\n internal\n returns (address _address)\n {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n _address := create2(\n 0,\n add(_bytecode, 0x20),\n mload(_bytecode),\n _salt\n )\n }\n\n require(\n _address.code.length > 0 && _address != address(0),\n \"Failed creating a pool booster\"\n );\n }\n\n // pre-compute the address of the deployed contract that will be\n // created when create2 is called\n function _computeAddress(bytes memory _bytecode, uint256 _salt)\n internal\n view\n returns (address)\n {\n bytes32 hash = keccak256(\n abi.encodePacked(\n bytes1(0xff),\n address(this),\n _salt,\n keccak256(_bytecode)\n )\n );\n\n // cast last 20 bytes of hash to address\n return address(uint160(uint256(hash)));\n }\n\n function poolBoosterLength() external view returns (uint256) {\n return poolBoosters.length;\n }\n}\n" + }, + "contracts/poolBooster/PoolBoostCentralRegistry.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IPoolBoostCentralRegistry } from \"../interfaces/poolBooster/IPoolBoostCentralRegistry.sol\";\n\n/**\n * @title Contract that holds all governance approved pool booster Factory\n * implementation deployments\n * @author Origin Protocol Inc\n */\ncontract PoolBoostCentralRegistry is Governable, IPoolBoostCentralRegistry {\n event FactoryApproved(address factoryAddress);\n event FactoryRemoved(address factoryAddress);\n\n // @notice List of approved factories\n address[] public factories;\n\n modifier onlyApprovedFactories() {\n require(isApprovedFactory(msg.sender), \"Not an approved factory\");\n _;\n }\n\n constructor() {\n // set the governor of the implementation contract to zero address\n _setGovernor(address(0));\n }\n\n /**\n * @notice Adds a factory address to the approved factory addresses\n * @param _factoryAddress address of the factory\n */\n function approveFactory(address _factoryAddress) external onlyGovernor {\n require(_factoryAddress != address(0), \"Invalid address\");\n require(\n !isApprovedFactory(_factoryAddress),\n \"Factory already approved\"\n );\n\n factories.push(_factoryAddress);\n emit FactoryApproved(_factoryAddress);\n }\n\n /**\n * @notice Removes the factory from approved factory addresses\n * @param _factoryAddress address of the factory\n */\n function removeFactory(address _factoryAddress) external onlyGovernor {\n require(_factoryAddress != address(0), \"Invalid address\");\n\n uint256 length = factories.length;\n bool factoryRemoved = false;\n for (uint256 i = 0; i < length; i++) {\n if (factories[i] != _factoryAddress) {\n continue;\n }\n\n factories[i] = factories[length - 1];\n factories.pop();\n emit FactoryRemoved(_factoryAddress);\n factoryRemoved = true;\n break;\n }\n require(factoryRemoved, \"Not an approved factory\");\n\n emit FactoryRemoved(_factoryAddress);\n }\n\n /**\n * @notice Emits a pool booster created event\n * @dev This has been created as a convenience method for the monitoring to have\n * an index of all of the created pool boosters by only listening to the\n * events of this contract.\n * @param _poolBoosterAddress address of the pool booster created\n * @param _ammPoolAddress address of the AMM pool forwarding yield to the pool booster\n * @param _boosterType PoolBoosterType the type of the pool booster\n */\n function emitPoolBoosterCreated(\n address _poolBoosterAddress,\n address _ammPoolAddress,\n PoolBoosterType _boosterType\n ) external onlyApprovedFactories {\n emit PoolBoosterCreated(\n _poolBoosterAddress,\n _ammPoolAddress,\n _boosterType,\n msg.sender // address of the factory\n );\n }\n\n /**\n * @notice Emits a pool booster removed event\n * @dev This has been created as a convenience method for the monitoring to have\n * an index of all of the removed pool boosters by only listening to the\n * events of this contract.\n * @param _poolBoosterAddress address of the pool booster to be removed\n */\n function emitPoolBoosterRemoved(address _poolBoosterAddress)\n external\n onlyApprovedFactories\n {\n emit PoolBoosterRemoved(_poolBoosterAddress);\n }\n\n /**\n * @notice Returns true if the factory is approved\n * @param _factoryAddress address of the factory\n */\n function isApprovedFactory(address _factoryAddress)\n public\n view\n returns (bool)\n {\n uint256 length = factories.length;\n for (uint256 i = 0; i < length; i++) {\n if (factories[i] == _factoryAddress) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * @notice Returns all supported factories\n */\n function getAllFactories() external view returns (address[] memory) {\n return factories;\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactoryMerkl.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterMerkl } from \"./PoolBoosterMerkl.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Merkl pool boosters.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactoryMerkl is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n /// @notice address of the Merkl distributor\n address public merklDistributor;\n\n /// @notice event emitted when the Merkl distributor is updated\n event MerklDistributorUpdated(address newDistributor);\n\n /**\n * @param _oSonic address of the OSonic token\n * @param _governor address governor\n * @param _centralRegistry address of the central registry\n * @param _merklDistributor address of the Merkl distributor\n */\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry,\n address _merklDistributor\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {\n _setMerklDistributor(_merklDistributor);\n }\n\n /**\n * @dev Create a Pool Booster for Merkl.\n * @param _campaignType The type of campaign to create. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _campaignDuration The duration of the campaign in seconds\n * @param campaignData The data to be used for the campaign. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * This should be fetched from the Merkl UI.\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterMerkl(\n uint32 _campaignType,\n address _ammPoolAddress,\n uint32 _campaignDuration,\n bytes calldata campaignData,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n require(_campaignDuration > 1 hours, \"Invalid campaign duration\");\n require(campaignData.length > 0, \"Invalid campaign data\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterMerkl).creationCode,\n abi.encode(\n oSonic,\n merklDistributor,\n _campaignDuration,\n _campaignType,\n governor(),\n campaignData\n )\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.MerklBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for Merkl.\n * @param _campaignType The type of campaign to create. This is used to determine the type of\n * bribe contract to create. The type is defined in the MerklDistributor contract.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterMerkl` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n uint32 _campaignType,\n address _ammPoolAddress,\n uint32 _campaignDuration,\n bytes calldata campaignData,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n require(_campaignDuration > 1 hours, \"Invalid campaign duration\");\n require(campaignData.length > 0, \"Invalid campaign data\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterMerkl).creationCode,\n abi.encode(\n oSonic,\n merklDistributor,\n _campaignDuration,\n _campaignType,\n governor(),\n campaignData\n )\n ),\n _salt\n );\n }\n\n /**\n * @dev Set the address of the Merkl distributor\n * @param _merklDistributor The address of the Merkl distributor\n */\n function setMerklDistributor(address _merklDistributor)\n external\n onlyGovernor\n {\n _setMerklDistributor(_merklDistributor);\n }\n\n function _setMerklDistributor(address _merklDistributor) internal {\n require(\n _merklDistributor != address(0),\n \"Invalid merklDistributor address\"\n );\n merklDistributor = _merklDistributor;\n emit MerklDistributorUpdated(_merklDistributor);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactoryMetropolis.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterMetropolis } from \"./PoolBoosterMetropolis.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Metropolis pool boosters.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactoryMetropolis is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n address public immutable rewardFactory;\n address public immutable voter;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n // @param address _rewardFactory address of the Metropolis reward factory\n // @param address _voter address of the Metropolis voter\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry,\n address _rewardFactory,\n address _voter\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {\n rewardFactory = _rewardFactory;\n voter = _voter;\n }\n\n /**\n * @dev Create a Pool Booster for Metropolis pool.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterMetropolis(address _ammPoolAddress, uint256 _salt)\n external\n onlyGovernor\n {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterMetropolis).creationCode,\n abi.encode(oSonic, rewardFactory, _ammPoolAddress, voter)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.MetropolisBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for Metropolis pool.\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterMetropolis` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(address _ammPoolAddress, uint256 _salt)\n external\n view\n returns (address)\n {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterMetropolis).creationCode,\n abi.encode(oSonic, rewardFactory, _ammPoolAddress, voter)\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactorySwapxDouble.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterSwapxDouble } from \"./PoolBoosterSwapxDouble.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Swapx Ichi pool boosters where both of the\n * gauges need incentivizing.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactorySwapxDouble is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {}\n\n /**\n * @dev Create a Pool Booster for SwapX Ichi vault based pool where 2 Bribe contracts need to be\n * bribed\n * @param _bribeAddressOS address of the Bribes.sol(Bribe) contract for the OS token side\n * @param _bribeAddressOther address of the Bribes.sol(Bribe) contract for the other token in the pool\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _split 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n * bribe contract and 60% to other bribe contract\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterSwapxDouble(\n address _bribeAddressOS,\n address _bribeAddressOther,\n address _ammPoolAddress,\n uint256 _split,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterSwapxDouble).creationCode,\n abi.encode(_bribeAddressOS, _bribeAddressOther, oSonic, _split)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.SwapXDoubleBooster\n );\n }\n\n /**\n * @dev Compute the address of the pool booster to be deployed.\n * @param _bribeAddressOS address of the Bribes.sol(Bribe) contract for the OS token side\n * @param _bribeAddressOther address of the Bribes.sol(Bribe) contract for the other token in the pool\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _split 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n * bribe contract and 60% to other bribe contract\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterSwapxDouble` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n address _bribeAddressOS,\n address _bribeAddressOther,\n address _ammPoolAddress,\n uint256 _split,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterSwapxDouble).creationCode,\n abi.encode(\n _bribeAddressOS,\n _bribeAddressOther,\n oSonic,\n _split\n )\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterFactorySwapxSingle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { PoolBoosterSwapxSingle } from \"./PoolBoosterSwapxSingle.sol\";\nimport { AbstractPoolBoosterFactory, IPoolBoostCentralRegistry } from \"./AbstractPoolBoosterFactory.sol\";\n\n/**\n * @title Pool booster factory for creating Swapx Single pool boosters - where a single\n * gauge is created for a pool. this is appropriate for Classic Stable & Classic\n * Volatile SwapX pools.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterFactorySwapxSingle is AbstractPoolBoosterFactory {\n uint256 public constant version = 1;\n\n // @param address _oSonic address of the OSonic token\n // @param address _governor address governor\n // @param address _centralRegistry address of the central registry\n constructor(\n address _oSonic,\n address _governor,\n address _centralRegistry\n ) AbstractPoolBoosterFactory(_oSonic, _governor, _centralRegistry) {}\n\n /**\n * @dev Create a Pool Booster for SwapX classic volatile or classic stable pools where\n * a single Bribe contract is incentivized.\n * @param _bribeAddress address of the Bribes.sol contract\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `computePoolBoosterAddress` in order for the final deployed address\n * and pre-computed address to match\n */\n function createPoolBoosterSwapxSingle(\n address _bribeAddress,\n address _ammPoolAddress,\n uint256 _salt\n ) external onlyGovernor {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n address poolBoosterAddress = _deployContract(\n abi.encodePacked(\n type(PoolBoosterSwapxSingle).creationCode,\n abi.encode(_bribeAddress, oSonic)\n ),\n _salt\n );\n\n _storePoolBoosterEntry(\n poolBoosterAddress,\n _ammPoolAddress,\n IPoolBoostCentralRegistry.PoolBoosterType.SwapXSingleBooster\n );\n }\n\n /**\n * @dev Create a Pool Booster for SwapX classic volatile or classic stable pools where\n * a single Bribe contract is incentivized.\n * @param _bribeAddress address of the Bribes.sol contract\n * @param _ammPoolAddress address of the AMM pool where the yield originates from\n * @param _salt A unique number that affects the address of the pool booster created. Note: this number\n * should match the one from `createPoolBoosterSwapxSingle` in order for the final deployed address\n * and pre-computed address to match\n */\n function computePoolBoosterAddress(\n address _bribeAddress,\n address _ammPoolAddress,\n uint256 _salt\n ) external view returns (address) {\n require(\n _ammPoolAddress != address(0),\n \"Invalid ammPoolAddress address\"\n );\n require(_salt > 0, \"Invalid salt\");\n\n return\n _computeAddress(\n abi.encodePacked(\n type(PoolBoosterSwapxSingle).creationCode,\n abi.encode(_bribeAddress, oSonic)\n ),\n _salt\n );\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterMerkl.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IMerklDistributor } from \"../interfaces/poolBooster/IMerklDistributor.sol\";\n\ninterface IERC1271 {\n /**\n * @dev Should return whether the signature provided is valid for the provided data\n * @param hash Hash of the data to be signed\n * @param signature Signature byte array associated with _data\n */\n function isValidSignature(bytes32 hash, bytes memory signature)\n external\n view\n returns (bytes4 magicValue);\n}\n\n/**\n * @title Pool booster for Merkl distributor\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterMerkl is IPoolBooster, IERC1271 {\n /// @notice address of merkl distributor\n IMerklDistributor public immutable merklDistributor;\n /// @notice address of the OS token\n IERC20 public immutable rewardToken;\n /// @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n /// @notice Campaign duration in seconds\n uint32 public immutable duration; // -> should be immutable\n /// @notice Campaign type\n uint32 public immutable campaignType;\n /// @notice Owner of the campaign\n address public immutable creator;\n /// @notice Campaign data\n bytes public campaignData;\n\n constructor(\n address _rewardToken,\n address _merklDistributor,\n uint32 _duration,\n uint32 _campaignType,\n address _creator,\n bytes memory _campaignData\n ) {\n require(_rewardToken != address(0), \"Invalid rewardToken address\");\n require(\n _merklDistributor != address(0),\n \"Invalid merklDistributor address\"\n );\n require(_campaignData.length > 0, \"Invalid campaignData\");\n require(_duration > 1 hours, \"Invalid duration\");\n\n campaignType = _campaignType;\n duration = _duration;\n creator = _creator;\n\n merklDistributor = IMerklDistributor(_merklDistributor);\n rewardToken = IERC20(_rewardToken);\n campaignData = _campaignData;\n }\n\n /// @notice Create a campaign on the Merkl distributor\n function bribe() external override {\n // Ensure token is approved for the Merkl distributor\n uint256 minAmount = merklDistributor.rewardTokenMinAmounts(\n address(rewardToken)\n );\n require(minAmount > 0, \"Min reward amount must be > 0\");\n\n // if balance too small or below threshold, do no bribes\n uint256 balance = rewardToken.balanceOf(address(this));\n if (\n balance < MIN_BRIBE_AMOUNT ||\n (balance * 1 hours < minAmount * duration)\n ) {\n return;\n }\n\n // Approve the bribe contract to spend the reward token\n rewardToken.approve(address(merklDistributor), balance);\n\n // Notify the bribe contract of the reward amount\n merklDistributor.signAndCreateCampaign(\n IMerklDistributor.CampaignParameters({\n campaignId: bytes32(0),\n creator: creator,\n rewardToken: address(rewardToken),\n amount: balance,\n campaignType: campaignType,\n startTimestamp: getNextPeriodStartTime(),\n duration: duration,\n campaignData: campaignData\n }),\n bytes(\"\")\n );\n emit BribeExecuted(balance);\n }\n\n /// @notice Used to sign a campaign on the Merkl distributor\n function isValidSignature(bytes32, bytes memory)\n external\n view\n override\n returns (bytes4 magicValue)\n {\n require(msg.sender == address(merklDistributor), \"Invalid sender\");\n // bytes4(keccak256(\"isValidSignature(bytes32,bytes)\")) == 0x1626ba7e\n return bytes4(0x1626ba7e);\n }\n\n /// @notice Returns the timestamp for the start of the next period based on the configured duration\n function getNextPeriodStartTime() public view returns (uint32) {\n // Calculate the timestamp for the next period boundary\n return uint32((block.timestamp / duration + 1) * duration);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterMetropolis.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @title Pool booster for Metropolis pools\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterMetropolis is IPoolBooster {\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice address of the pool\n address public immutable pool;\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n IRewarderFactory public immutable rewardFactory;\n\n IVoter public immutable voter;\n\n constructor(\n address _osToken,\n address _rewardFactory,\n address _pool,\n address _voter\n ) {\n require(_pool != address(0), \"Invalid pool address\");\n pool = _pool;\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n\n rewardFactory = IRewarderFactory(_rewardFactory);\n\n voter = IVoter(_voter);\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n (, uint256 minBribeAmount) = rewardFactory.getWhitelistedTokenInfo(\n address(osToken)\n );\n if (balance < MIN_BRIBE_AMOUNT || balance < minBribeAmount) {\n return;\n }\n\n uint256 id = voter.getCurrentVotingPeriod() + 1;\n\n // Deploy a rewarder\n IRewarder rewarder = IRewarder(\n rewardFactory.createBribeRewarder(address(osToken), pool)\n );\n\n // Approve the rewarder to spend the balance\n osToken.approve(address(rewarder), balance);\n\n // Fund and bribe the rewarder\n rewarder.fundAndBribe(id, id, balance);\n\n emit BribeExecuted(balance);\n }\n}\n\ninterface IRewarderFactory {\n function createBribeRewarder(address token, address pool)\n external\n returns (address rewarder);\n\n function getWhitelistedTokenInfo(address token)\n external\n view\n returns (bool isWhitelisted, uint256 minBribeAmount);\n}\n\ninterface IRewarder {\n function fundAndBribe(\n uint256 startId,\n uint256 lastId,\n uint256 amountPerPeriod\n ) external payable;\n}\n\ninterface IVoter {\n function getCurrentVotingPeriod() external view returns (uint256);\n}\n" + }, + "contracts/poolBooster/PoolBoosterSwapxDouble.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBribe } from \"../interfaces/poolBooster/ISwapXAlgebraBribe.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\n/**\n * @title Pool booster for SwapX concentrated liquidity where 2 gauges are created for\n * every pool. Ichi vaults currently have such setup.\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterSwapxDouble is IPoolBooster {\n using StableMath for uint256;\n\n // @notice address of the Bribes.sol(Bribe) contract for the OS token side\n IBribe public immutable bribeContractOS;\n // @notice address of the Bribes.sol(Bribe) contract for the other token in the pool\n IBribe public immutable bribeContractOther;\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice 1e18 denominated split between OS and Other bribe. E.g. 0.4e17 means 40% to OS\n // bribe contract and 60% to other bribe contract\n uint256 public immutable split;\n\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n constructor(\n address _bribeContractOS,\n address _bribeContractOther,\n address _osToken,\n uint256 _split\n ) {\n require(\n _bribeContractOS != address(0),\n \"Invalid bribeContractOS address\"\n );\n require(\n _bribeContractOther != address(0),\n \"Invalid bribeContractOther address\"\n );\n // expect it to be between 1% & 99%\n require(_split > 1e16 && _split < 99e16, \"Unexpected split amount\");\n\n bribeContractOS = IBribe(_bribeContractOS);\n bribeContractOther = IBribe(_bribeContractOther);\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n split = _split;\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n if (balance < MIN_BRIBE_AMOUNT) {\n return;\n }\n\n uint256 osBribeAmount = balance.mulTruncate(split);\n uint256 otherBribeAmount = balance - osBribeAmount;\n\n osToken.approve(address(bribeContractOS), osBribeAmount);\n osToken.approve(address(bribeContractOther), otherBribeAmount);\n\n bribeContractOS.notifyRewardAmount(address(osToken), osBribeAmount);\n bribeContractOther.notifyRewardAmount(\n address(osToken),\n otherBribeAmount\n );\n\n emit BribeExecuted(balance);\n }\n}\n" + }, + "contracts/poolBooster/PoolBoosterSwapxSingle.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBribe } from \"../interfaces/poolBooster/ISwapXAlgebraBribe.sol\";\nimport { IPoolBooster } from \"../interfaces/poolBooster/IPoolBooster.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @title Pool booster for SwapX for Classic Stable Pools and Classic Volatile Pools\n * @author Origin Protocol Inc\n */\ncontract PoolBoosterSwapxSingle is IPoolBooster {\n // @notice address of the Bribes.sol(Bribe) contract\n IBribe public immutable bribeContract;\n // @notice address of the OS token\n IERC20 public immutable osToken;\n // @notice if balance under this amount the bribe action is skipped\n uint256 public constant MIN_BRIBE_AMOUNT = 1e10;\n\n constructor(address _bribeContract, address _osToken) {\n require(_bribeContract != address(0), \"Invalid bribeContract address\");\n bribeContract = IBribe(_bribeContract);\n // Abstract factory already validates this is not a zero address\n osToken = IERC20(_osToken);\n }\n\n function bribe() external override {\n uint256 balance = osToken.balanceOf(address(this));\n // balance too small, do no bribes\n if (balance < MIN_BRIBE_AMOUNT) {\n return;\n }\n\n osToken.approve(address(bribeContract), balance);\n\n bribeContract.notifyRewardAmount(address(osToken), balance);\n emit BribeExecuted(balance);\n }\n}\n" + }, + "contracts/proxies/BaseProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice BridgedBaseWOETHProxy delegates calls to BridgedWOETH implementation\n */\ncontract BridgedBaseWOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseVaultProxy delegates calls to OETHBaseVault implementation\n */\ncontract OETHBaseVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseProxy delegates calls to OETH implementation\n */\ncontract OETHBaseProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHBaseProxy delegates calls to WOETH implementation\n */\ncontract WOETHBaseProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseDripperProxy delegates calls to a FixedRateDripper implementation\n */\ncontract OETHBaseDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice AerodromeAMOStrategyProxy delegates calls to AerodromeAMOStrategy implementation\n */\ncontract AerodromeAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BridgedWOETHStrategyProxy delegates calls to BridgedWOETHStrategy implementation\n */\ncontract BridgedWOETHStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseHarvesterProxy delegates calls to a SuperOETHHarvester implementation\n */\ncontract OETHBaseHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBaseCurveAMOProxy delegates calls to a OETHBaseCurveAMO implementation\n */\ncontract OETHBaseCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/proxies/InitializeGovernedUpgradeabilityProxy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { Governable } from \"../governance/Governable.sol\";\n\n/**\n * @title BaseGovernedUpgradeabilityProxy\n * @dev This contract combines an upgradeability proxy with our governor system.\n * It is based on an older version of OpenZeppelins BaseUpgradeabilityProxy\n * with Solidity ^0.8.0.\n * @author Origin Protocol Inc\n */\ncontract InitializeGovernedUpgradeabilityProxy is Governable {\n /**\n * @dev Emitted when the implementation is upgraded.\n * @param implementation Address of the new implementation.\n */\n event Upgraded(address indexed implementation);\n\n constructor() {\n _setGovernor(msg.sender);\n }\n\n /**\n * @dev Contract initializer with Governor enforcement\n * @param _logic Address of the initial implementation.\n * @param _initGovernor Address of the initial Governor.\n * @param _data Data to send as msg.data to the implementation to initialize\n * the proxied contract.\n * It should include the signature and the parameters of the function to be\n * called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n * This parameter is optional, if no data is given the initialization call\n * to proxied contract will be skipped.\n */\n function initialize(\n address _logic,\n address _initGovernor,\n bytes calldata _data\n ) public payable onlyGovernor {\n require(_implementation() == address(0));\n require(_logic != address(0), \"Implementation not set\");\n assert(\n IMPLEMENTATION_SLOT ==\n bytes32(uint256(keccak256(\"eip1967.proxy.implementation\")) - 1)\n );\n _setImplementation(_logic);\n if (_data.length > 0) {\n (bool success, ) = _logic.delegatecall(_data);\n require(success);\n }\n _changeGovernor(_initGovernor);\n }\n\n /**\n * @return The address of the proxy admin/it's also the governor.\n */\n function admin() external view returns (address) {\n return _governor();\n }\n\n /**\n * @return The address of the implementation.\n */\n function implementation() external view returns (address) {\n return _implementation();\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy.\n * Only the admin can call this function.\n * @param _newImplementation Address of the new implementation.\n */\n function upgradeTo(address _newImplementation) external onlyGovernor {\n _upgradeTo(_newImplementation);\n }\n\n /**\n * @dev Upgrade the backing implementation of the proxy and call a function\n * on the new implementation.\n * This is useful to initialize the proxied contract.\n * @param newImplementation Address of the new implementation.\n * @param data Data to send as msg.data in the low level call.\n * It should include the signature and the parameters of the function to be called, as described in\n * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.\n */\n function upgradeToAndCall(address newImplementation, bytes calldata data)\n external\n payable\n onlyGovernor\n {\n _upgradeTo(newImplementation);\n (bool success, ) = newImplementation.delegatecall(data);\n require(success);\n }\n\n /**\n * @dev Fallback function.\n * Implemented entirely in `_fallback`.\n */\n fallback() external payable {\n _fallback();\n }\n\n /**\n * @dev Delegates execution to an implementation contract.\n * This is a low level function that doesn't return to its internal call site.\n * It will return to the external caller whatever the implementation returns.\n * @param _impl Address to delegate.\n */\n function _delegate(address _impl) internal {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(gas(), _impl, 0, calldatasize(), 0, 0)\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n /**\n * @dev Function that is run as the first thing in the fallback function.\n * Can be redefined in derived contracts to add functionality.\n * Redefinitions must call super._willFallback().\n */\n function _willFallback() internal {}\n\n /**\n * @dev fallback implementation.\n * Extracted to enable manual triggering.\n */\n function _fallback() internal {\n _willFallback();\n _delegate(_implementation());\n }\n\n /**\n * @dev Storage slot with the address of the current implementation.\n * This is the keccak-256 hash of \"eip1967.proxy.implementation\" subtracted by 1, and is\n * validated in the constructor.\n */\n bytes32 internal constant IMPLEMENTATION_SLOT =\n 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n /**\n * @dev Returns the current implementation.\n * @return impl Address of the current implementation\n */\n function _implementation() internal view returns (address impl) {\n bytes32 slot = IMPLEMENTATION_SLOT;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n impl := sload(slot)\n }\n }\n\n /**\n * @dev Upgrades the proxy to a new implementation.\n * @param newImplementation Address of the new implementation.\n */\n function _upgradeTo(address newImplementation) internal {\n _setImplementation(newImplementation);\n emit Upgraded(newImplementation);\n }\n\n /**\n * @dev Sets the implementation address of the proxy.\n * @param newImplementation Address of the new implementation.\n */\n function _setImplementation(address newImplementation) internal {\n require(\n Address.isContract(newImplementation),\n \"Cannot set a proxy implementation to a non-contract address\"\n );\n\n bytes32 slot = IMPLEMENTATION_SLOT;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(slot, newImplementation)\n }\n }\n}\n" + }, + "contracts/proxies/PlumeProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OETHPlumeVaultProxy delegates calls to OETHPlumeVault implementation\n */\ncontract OETHPlumeVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHPlumeProxy delegates calls to OETH implementation\n */\ncontract OETHPlumeProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHPlumeProxy delegates calls to WOETH implementation\n */\ncontract WOETHPlumeProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice RoosterAMOStrategyProxy delegates calls to a RoosterAMOStrategy implementation\n */\ncontract RoosterAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/proxies/Proxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OUSDProxy delegates calls to an OUSD implementation\n */\ncontract OUSDProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WrappedOUSDProxy delegates calls to a WrappedOUSD implementation\n */\ncontract WrappedOUSDProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice VaultProxy delegates calls to a Vault implementation\n */\ncontract VaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CompoundStrategyProxy delegates calls to a CompoundStrategy implementation\n */\ncontract CompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice AaveStrategyProxy delegates calls to a AaveStrategy implementation\n */\ncontract AaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ConvexStrategyProxy delegates calls to a ConvexStrategy implementation\n */\ncontract ConvexStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice HarvesterProxy delegates calls to a Harvester implementation\n */\ncontract HarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice DripperProxy delegates calls to a Dripper implementation\n */\ncontract DripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoCompoundStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\n */\ncontract MorphoCompoundStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ConvexOUSDMetaStrategyProxy delegates calls to a ConvexOUSDMetaStrategy implementation\n */\ncontract ConvexOUSDMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoAaveStrategyProxy delegates calls to a MorphoCompoundStrategy implementation\n */\ncontract MorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHProxy delegates calls to nowhere for now\n */\ncontract OETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOETHProxy delegates calls to nowhere for now\n */\ncontract WOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHVaultProxy delegates calls to a Vault implementation\n */\ncontract OETHVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHDripperProxy delegates calls to a OETHDripper implementation\n */\ncontract OETHDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHHarvesterProxy delegates calls to a Harvester implementation\n */\ncontract OETHHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CurveEthStrategyProxy delegates calls to a CurveEthStrategy implementation\n */\ncontract ConvexEthMetaStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BuybackProxy delegates calls to Buyback implementation\n */\ncontract BuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHMorphoAaveStrategyProxy delegates calls to a MorphoAaveStrategy implementation\n */\ncontract OETHMorphoAaveStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBalancerMetaPoolrEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\n */\ncontract OETHBalancerMetaPoolrEthStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice OETHBalancerMetaPoolwstEthStrategyProxy delegates calls to a BalancerMetaPoolStrategy implementation\n */\ncontract OETHBalancerMetaPoolwstEthStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MakerDsrStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MakerDsrStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHBuybackProxy delegates calls to Buyback implementation\n */\ncontract OETHBuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice BridgedWOETHProxy delegates calls to BridgedWOETH implementation\n */\ncontract BridgedWOETHProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice NativeStakingSSVStrategyProxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulatorProxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulatorProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingSSVStrategy2Proxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategy2Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulator2Proxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulator2Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingSSVStrategy3Proxy delegates calls to NativeStakingSSVStrategy implementation\n */\ncontract NativeStakingSSVStrategy3Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice NativeStakingFeeAccumulator3Proxy delegates calls to FeeAccumulator implementation\n */\ncontract NativeStakingFeeAccumulator3Proxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MetaMorphoStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MetaMorphoStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice ARMBuybackProxy delegates calls to Buyback implementation\n */\ncontract ARMBuybackProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice MorphoGauntletPrimeUSDCStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MorphoGauntletPrimeUSDCStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MorphoGauntletPrimeUSDTStrategyProxy delegates calls to a Generalized4626USDTStrategy implementation\n */\ncontract MorphoGauntletPrimeUSDTStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice CurvePoolBoosterProxy delegates calls to a CurvePoolBooster implementation\n */\ncontract CurvePoolBoosterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHFixedRateDripperProxy delegates calls to a OETHFixedRateDripper implementation\n */\ncontract OETHFixedRateDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHSimpleHarvesterProxy delegates calls to a OETHSimpleHarvester implementation\n */\ncontract OETHSimpleHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice PoolBoostCentralRegistryProxy delegates calls to the PoolBoostCentralRegistry implementation\n */\ncontract PoolBoostCentralRegistryProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n\n/**\n * @notice MakerSSRStrategyProxy delegates calls to a Generalized4626Strategy implementation\n */\ncontract MakerSSRStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OUSDCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\n */\ncontract OUSDCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OETHCurveAMOProxy delegates calls to a CurveAMOStrategy implementation\n */\ncontract OETHCurveAMOProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice CompoundingStakingSSVStrategyProxy delegates calls to a CompoundingStakingSSVStrategy implementation\n */\ncontract CompoundingStakingSSVStrategyProxy is\n InitializeGovernedUpgradeabilityProxy\n{\n\n}\n" + }, + "contracts/proxies/SonicProxies.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { InitializeGovernedUpgradeabilityProxy } from \"./InitializeGovernedUpgradeabilityProxy.sol\";\n\n/**\n * @notice OSonicVaultProxy delegates calls to OSonicVault implementation\n */\ncontract OSonicVaultProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicProxy delegates calls to OSonic implementation\n */\ncontract OSonicProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice WOSonicProxy delegates calls to WOSonic implementation\n */\ncontract WOSonicProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicDripperProxy delegates calls to a FixedRateDripper implementation\n */\ncontract OSonicDripperProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice SonicStakingStrategyProxy delegates calls to SonicStakingStrategy implementation\n */\ncontract SonicStakingStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice OSonicHarvesterProxy delegates calls to a OSonicHarvester implementation\n */\ncontract OSonicHarvesterProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n\n/**\n * @notice SonicSwapXAMOStrategyProxy delegates calls to a SonicSwapXAMOStrategy implementation\n */\ncontract SonicSwapXAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy {\n\n}\n" + }, + "contracts/strategies/AaveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Aave Strategy\n * @notice Investment strategy for investing stablecoins via Aave\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport \"./IAave.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\nimport { IAaveStakedToken } from \"./IAaveStakeToken.sol\";\nimport { IAaveIncentivesController } from \"./IAaveIncentivesController.sol\";\n\ncontract AaveStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n\n uint16 constant referralCode = 92;\n\n IAaveIncentivesController public incentivesController;\n IAaveStakedToken public stkAave;\n\n /**\n * @param _stratConfig The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as AAVE needs several extra\n * addresses for the rewards program.\n * @param _rewardTokenAddresses Address of the AAVE token\n * @param _assets Addresses of supported assets\n * @param _pTokens Platform Token corresponding addresses\n * @param _incentivesAddress Address of the AAVE incentives controller\n * @param _stkAaveAddress Address of the stkAave contract\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // AAVE\n address[] calldata _assets,\n address[] calldata _pTokens,\n address _incentivesAddress,\n address _stkAaveAddress\n ) external onlyGovernor initializer {\n incentivesController = IAaveIncentivesController(_incentivesAddress);\n stkAave = IAaveStakedToken(_stkAaveAddress);\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Deposit asset into Aave\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Aave\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n // Following line also doubles as a check that we are depositing\n // an asset that we support.\n emit Deposit(_asset, _getATokenFor(_asset), _amount);\n _getLendingPool().deposit(_asset, _amount, address(this), referralCode);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Aave\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Aave\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n emit Withdrawal(_asset, _getATokenFor(_asset), _amount);\n uint256 actual = _getLendingPool().withdraw(\n _asset,\n _amount,\n address(this)\n );\n require(actual == _amount, \"Did not withdraw enough\");\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n // Redeem entire balance of aToken\n IERC20 asset = IERC20(assetsMapped[i]);\n address aToken = _getATokenFor(assetsMapped[i]);\n uint256 balance = IERC20(aToken).balanceOf(address(this));\n if (balance > 0) {\n uint256 actual = _getLendingPool().withdraw(\n address(asset),\n balance,\n address(this)\n );\n require(actual == balance, \"Did not withdraw enough\");\n\n uint256 assetBalance = asset.balanceOf(address(this));\n // Transfer entire balance to Vault\n asset.safeTransfer(vaultAddress, assetBalance);\n\n emit Withdrawal(address(asset), aToken, assetBalance);\n }\n }\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n // Balance is always with token aToken decimals\n address aToken = _getATokenFor(_asset);\n balance = IERC20(aToken).balanceOf(address(this));\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Approve the spending of all assets by their corresponding aToken,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n address lendingPool = address(_getLendingPool());\n // approve the pool to spend the Asset\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n address asset = assetsMapped[i];\n // Safe approval\n IERC20(asset).safeApprove(lendingPool, 0);\n IERC20(asset).safeApprove(lendingPool, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / aTokens\n We need to give the AAVE lending pool approval to transfer the\n asset.\n * @param _asset Address of the asset to approve\n * @param _aToken Address of the aToken\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _aToken)\n internal\n override\n {\n address lendingPool = address(_getLendingPool());\n IERC20(_asset).safeApprove(lendingPool, 0);\n IERC20(_asset).safeApprove(lendingPool, type(uint256).max);\n }\n\n /**\n * @dev Get the aToken wrapped in the IERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return Corresponding aToken to this asset\n */\n function _getATokenFor(address _asset) internal view returns (address) {\n address aToken = assetToPToken[_asset];\n require(aToken != address(0), \"aToken does not exist\");\n return aToken;\n }\n\n /**\n * @dev Get the current address of the Aave lending pool, which is the gateway to\n * depositing.\n * @return Current lending pool implementation\n */\n function _getLendingPool() internal view returns (IAaveLendingPool) {\n address lendingPool = ILendingPoolAddressesProvider(platformAddress)\n .getLendingPool();\n require(lendingPool != address(0), \"Lending pool does not exist\");\n return IAaveLendingPool(lendingPool);\n }\n\n /**\n * @dev Collect stkAave, convert it to AAVE send to Vault.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n if (address(stkAave) == address(0)) {\n return;\n }\n\n // Check staked AAVE cooldown timer\n uint256 cooldown = stkAave.stakersCooldowns(address(this));\n uint256 windowStart = cooldown + stkAave.COOLDOWN_SECONDS();\n uint256 windowEnd = windowStart + stkAave.UNSTAKE_WINDOW();\n\n // If inside the unlock window, then we can redeem stkAave\n // for AAVE and send it to the vault.\n if (block.timestamp > windowStart && block.timestamp <= windowEnd) {\n // Redeem to AAVE\n uint256 stkAaveBalance = stkAave.balanceOf(address(this));\n stkAave.redeem(address(this), stkAaveBalance);\n\n // Transfer AAVE to harvesterAddress\n uint256 aaveBalance = IERC20(rewardTokenAddresses[0]).balanceOf(\n address(this)\n );\n if (aaveBalance > 0) {\n IERC20(rewardTokenAddresses[0]).safeTransfer(\n harvesterAddress,\n aaveBalance\n );\n }\n }\n\n // Collect available rewards and restart the cooldown timer, if either of\n // those should be run.\n if (block.timestamp > windowStart || cooldown == 0) {\n uint256 assetsLen = assetsMapped.length;\n // aToken addresses for incentives controller\n address[] memory aTokens = new address[](assetsLen);\n for (uint256 i = 0; i < assetsLen; ++i) {\n aTokens[i] = _getATokenFor(assetsMapped[i]);\n }\n\n // 1. If we have rewards availabile, collect them\n uint256 pendingRewards = incentivesController.getRewardsBalance(\n aTokens,\n address(this)\n );\n if (pendingRewards > 0) {\n // Because getting more stkAAVE from the incentives controller\n // with claimRewards() may push the stkAAVE cooldown time\n // forward, it is called after stakedAAVE has been turned into\n // AAVE.\n uint256 collected = incentivesController.claimRewards(\n aTokens,\n pendingRewards,\n address(this)\n );\n require(collected == pendingRewards, \"AAVE reward difference\");\n }\n\n // 2. Start cooldown counting down.\n if (stkAave.balanceOf(address(this)) > 0) {\n // Protected with if since cooldown call would revert\n // if no stkAave balance.\n stkAave.cooldown();\n }\n }\n }\n}\n" + }, + "contracts/strategies/AbstractCompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base Compound Abstract Strategy\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { ICERC20 } from \"./ICompound.sol\";\nimport { IComptroller } from \"../interfaces/IComptroller.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\nabstract contract AbstractCompoundStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n\n int256[50] private __reserved;\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Get the cToken wrapped in the ICERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return Corresponding cToken to this asset\n */\n function _getCTokenFor(address _asset) internal view returns (ICERC20) {\n address cToken = assetToPToken[_asset];\n require(cToken != address(0), \"cToken does not exist\");\n return ICERC20(cToken);\n }\n\n /**\n * @dev Converts an underlying amount into cToken amount\n * cTokenAmt = (underlying * 1e18) / exchangeRate\n * @param _cToken cToken for which to change\n * @param _underlying Amount of underlying to convert\n * @return amount Equivalent amount of cTokens\n */\n function _convertUnderlyingToCToken(ICERC20 _cToken, uint256 _underlying)\n internal\n view\n returns (uint256 amount)\n {\n // e.g. 1e18*1e18 / 205316390724364402565641705 = 50e8\n // e.g. 1e8*1e18 / 205316390724364402565641705 = 0.45 or 0\n amount = (_underlying * 1e18) / _cToken.exchangeRateStored();\n }\n}\n" + }, + "contracts/strategies/AbstractConvexMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { ICurveMetaPool } from \"./ICurveMetaPool.sol\";\nimport { IERC20, AbstractCurveStrategy, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\nabstract contract AbstractConvexMetaStrategy is AbstractCurveStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n event MaxWithdrawalSlippageUpdated(\n uint256 _prevMaxSlippagePercentage,\n uint256 _newMaxSlippagePercentage\n );\n\n // used to circumvent the stack too deep issue\n struct InitConfig {\n address cvxDepositorAddress; //Address of the Convex depositor(AKA booster) for this pool\n address metapoolAddress; //Address of the Curve MetaPool\n address metapoolMainToken; //Address of Main metapool token\n address cvxRewardStakerAddress; //Address of the CVX rewards staker\n address metapoolLPToken; //Address of metapool LP token\n uint256 cvxDepositorPTokenId; //Pid of the pool referred to by Depositor and staker\n }\n\n address internal cvxDepositorAddress;\n address internal cvxRewardStakerAddress;\n uint256 internal cvxDepositorPTokenId;\n ICurveMetaPool internal metapool;\n IERC20 internal metapoolMainToken;\n IERC20 internal metapoolLPToken;\n // Ordered list of metapool assets\n address[] internal metapoolAssets;\n // Max withdrawal slippage denominated in 1e18 (1e18 == 100%)\n uint256 public maxWithdrawalSlippage;\n uint128 internal crvCoinIndex;\n uint128 internal mainCoinIndex;\n\n int256[41] private ___reserved;\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * DAI, USDC, USDT\n * @param _pTokens Platform Token corresponding addresses\n * @param initConfig Various addresses and info for initialization state\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets,\n address[] calldata _pTokens,\n InitConfig calldata initConfig\n ) external onlyGovernor initializer {\n require(_assets.length == 3, \"Must have exactly three assets\");\n // Should be set prior to abstract initialize call otherwise\n // abstractSetPToken calls will fail\n cvxDepositorAddress = initConfig.cvxDepositorAddress;\n pTokenAddress = _pTokens[0];\n metapool = ICurveMetaPool(initConfig.metapoolAddress);\n metapoolMainToken = IERC20(initConfig.metapoolMainToken);\n cvxRewardStakerAddress = initConfig.cvxRewardStakerAddress;\n metapoolLPToken = IERC20(initConfig.metapoolLPToken);\n cvxDepositorPTokenId = initConfig.cvxDepositorPTokenId;\n maxWithdrawalSlippage = 1e16;\n\n metapoolAssets = [metapool.coins(0), metapool.coins(1)];\n crvCoinIndex = _getMetapoolCoinIndex(pTokenAddress);\n mainCoinIndex = _getMetapoolCoinIndex(initConfig.metapoolMainToken);\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n balance = 0;\n\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 contractPTokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (contractPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = contractPTokens.mulTruncate(virtual_price);\n balance += value;\n }\n\n /* We intentionally omit the metapoolLp tokens held by the metastrategyContract\n * since the contract should never (except in the middle of deposit/withdrawal\n * transaction) hold any amount of those tokens in normal operation. There\n * could be tokens sent to it by a 3rd party and we decide to actively ignore\n * those.\n */\n uint256 metapoolGaugePTokens = IRewardStaking(cvxRewardStakerAddress)\n .balanceOf(address(this));\n\n if (metapoolGaugePTokens > 0) {\n uint256 value = metapoolGaugePTokens.mulTruncate(\n metapool.get_virtual_price()\n );\n balance += value;\n }\n\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = balance.scaleBy(assetDecimals, 18) / THREEPOOL_ASSET_COUNT;\n }\n\n /**\n * @dev This function is completely analogous to _calcCurveTokenAmount[AbstractCurveStrategy]\n * and just utilizes different Curve (meta)pool API\n */\n function _calcCurveMetaTokenAmount(uint128 _coinIndex, uint256 _amount)\n internal\n returns (uint256 requiredMetapoolLP)\n {\n uint256[2] memory _amounts = [uint256(0), uint256(0)];\n _amounts[uint256(_coinIndex)] = _amount;\n\n // LP required when removing required asset ignoring fees\n uint256 lpRequiredNoFees = metapool.calc_token_amount(_amounts, false);\n /* LP required if fees would apply to entirety of removed amount\n *\n * fee is 1e10 denominated number: https://curve.readthedocs.io/exchange-pools.html#StableSwap.fee\n */\n uint256 lpRequiredFullFees = lpRequiredNoFees.mulTruncateScale(\n 1e10 + metapool.fee(),\n 1e10\n );\n\n /* asset received when withdrawing full fee applicable LP accounting for\n * slippage and fees\n */\n uint256 assetReceivedForFullLPFees = metapool.calc_withdraw_one_coin(\n lpRequiredFullFees,\n int128(_coinIndex)\n );\n\n // exact amount of LP required\n requiredMetapoolLP =\n (lpRequiredFullFees * _amount) /\n assetReceivedForFullLPFees;\n }\n\n function _approveBase() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // 3Pool for LP token (required for removing liquidity)\n pToken.safeApprove(platformAddress, 0);\n pToken.safeApprove(platformAddress, type(uint256).max);\n // Gauge for LP token\n metapoolLPToken.safeApprove(cvxDepositorAddress, 0);\n metapoolLPToken.safeApprove(cvxDepositorAddress, type(uint256).max);\n // Metapool for LP token\n pToken.safeApprove(address(metapool), 0);\n pToken.safeApprove(address(metapool), type(uint256).max);\n // Metapool for Metapool main token\n metapoolMainToken.safeApprove(address(metapool), 0);\n metapoolMainToken.safeApprove(address(metapool), type(uint256).max);\n }\n\n /**\n * @dev Get the index of the coin\n */\n function _getMetapoolCoinIndex(address _asset)\n internal\n view\n returns (uint128)\n {\n for (uint128 i = 0; i < 2; i++) {\n if (metapoolAssets[i] == _asset) return i;\n }\n revert(\"Invalid Metapool asset\");\n }\n\n /**\n * @dev Sets max withdrawal slippage that is considered when removing\n * liquidity from Metapools.\n * @param _maxWithdrawalSlippage Max withdrawal slippage denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxWithdrawalSlippage should actually be 0.1% (1e15)\n * for production usage. Contract allows as low value as 0% for confirming\n * correct behavior in test suite.\n */\n function setMaxWithdrawalSlippage(uint256 _maxWithdrawalSlippage)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(\n _maxWithdrawalSlippage <= 1e18,\n \"Max withdrawal slippage needs to be between 0% - 100%\"\n );\n emit MaxWithdrawalSlippageUpdated(\n maxWithdrawalSlippage,\n _maxWithdrawalSlippage\n );\n maxWithdrawalSlippage = _maxWithdrawalSlippage;\n }\n\n /**\n * @dev Collect accumulated CRV and CVX and send to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n IRewardStaking(cvxRewardStakerAddress).getReward();\n _collectRewardTokens();\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/AbstractCurveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve 3Pool Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\nabstract contract AbstractCurveStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 internal constant MAX_SLIPPAGE = 1e16; // 1%, same as the Curve UI\n // number of assets in Curve 3Pool (USDC, DAI, USDT)\n uint256 internal constant THREEPOOL_ASSET_COUNT = 3;\n address internal pTokenAddress;\n\n int256[49] private __reserved;\n\n /**\n * @dev Deposit asset into the Curve 3Pool\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_amount > 0, \"Must deposit something\");\n emit Deposit(_asset, pTokenAddress, _amount);\n\n // 3Pool requires passing deposit amounts for all 3 assets, set to 0 for\n // all\n uint256[3] memory _amounts;\n uint256 poolCoinIndex = _getCoinIndex(_asset);\n // Set the amount on the asset we want to deposit\n _amounts[poolCoinIndex] = _amount;\n ICurvePool curvePool = ICurvePool(platformAddress);\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n uint256 depositValue = _amount.scaleBy(18, assetDecimals).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = depositValue.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n // Do the deposit to 3pool\n curvePool.add_liquidity(_amounts, minMintAmount);\n _lpDepositAll();\n }\n\n function _lpDepositAll() internal virtual;\n\n /**\n * @dev Deposit the entire balance of any supported asset into the Curve 3pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n uint256 depositValue = 0;\n ICurvePool curvePool = ICurvePool(platformAddress);\n uint256 curveVirtualPrice = curvePool.get_virtual_price();\n\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address assetAddress = assetsMapped[i];\n uint256 balance = IERC20(assetAddress).balanceOf(address(this));\n if (balance > 0) {\n uint256 poolCoinIndex = _getCoinIndex(assetAddress);\n // Set the amount on the asset we want to deposit\n _amounts[poolCoinIndex] = balance;\n uint256 assetDecimals = Helpers.getDecimals(assetAddress);\n // Get value of deposit in Curve LP token to later determine\n // the minMintAmount argument for add_liquidity\n depositValue =\n depositValue +\n balance.scaleBy(18, assetDecimals).divPrecisely(\n curveVirtualPrice\n );\n emit Deposit(assetAddress, pTokenAddress, balance);\n }\n }\n\n uint256 minMintAmount = depositValue.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n // Do the deposit to 3pool\n curvePool.add_liquidity(_amounts, minMintAmount);\n\n /* In case of Curve Strategy all assets are mapped to the same pToken (3CrvLP). Let\n * descendants further handle the pToken. By either deploying it to the metapool and\n * resulting tokens in Gauge. Or deploying pTokens directly to the Gauge.\n */\n _lpDepositAll();\n }\n\n function _lpWithdraw(uint256 numCrvTokens) internal virtual;\n\n function _lpWithdrawAll() internal virtual;\n\n /**\n * @dev Withdraw asset from Curve 3Pool\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Invalid amount\");\n\n emit Withdrawal(_asset, pTokenAddress, _amount);\n\n uint256 contractCrv3Tokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n\n uint256 coinIndex = _getCoinIndex(_asset);\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 requiredCrv3Tokens = _calcCurveTokenAmount(coinIndex, _amount);\n\n // We have enough LP tokens, make sure they are all on this contract\n if (contractCrv3Tokens < requiredCrv3Tokens) {\n _lpWithdraw(requiredCrv3Tokens - contractCrv3Tokens);\n }\n\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n _amounts[coinIndex] = _amount;\n\n curvePool.remove_liquidity_imbalance(_amounts, requiredCrv3Tokens);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Calculate amount of LP required when withdrawing specific amount of one\n * of the underlying assets accounting for fees and slippage.\n *\n * Curve pools unfortunately do not contain a calculation function for\n * amount of LP required when withdrawing a specific amount of one of the\n * underlying tokens and also accounting for fees (Curve's calc_token_amount\n * does account for slippage but not fees).\n *\n * Steps taken to calculate the metric:\n * - get amount of LP required if fees wouldn't apply\n * - increase the LP amount as if fees would apply to the entirety of the underlying\n * asset withdrawal. (when withdrawing only one coin fees apply only to amounts\n * of other assets pool would return in case of balanced removal - since those need\n * to be swapped for the single underlying asset being withdrawn)\n * - get amount of underlying asset withdrawn (this Curve function does consider slippage\n * and fees) when using the increased LP amount. As LP amount is slightly over-increased\n * so is amount of underlying assets returned.\n * - since we know exactly how much asset we require take the rate of LP required for asset\n * withdrawn to get the exact amount of LP.\n */\n function _calcCurveTokenAmount(uint256 _coinIndex, uint256 _amount)\n internal\n returns (uint256 required3Crv)\n {\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256[3] memory _amounts = [uint256(0), uint256(0), uint256(0)];\n _amounts[_coinIndex] = _amount;\n\n // LP required when removing required asset ignoring fees\n uint256 lpRequiredNoFees = curvePool.calc_token_amount(_amounts, false);\n /* LP required if fees would apply to entirety of removed amount\n *\n * fee is 1e10 denominated number: https://curve.readthedocs.io/exchange-pools.html#StableSwap.fee\n */\n uint256 lpRequiredFullFees = lpRequiredNoFees.mulTruncateScale(\n 1e10 + curvePool.fee(),\n 1e10\n );\n\n /* asset received when withdrawing full fee applicable LP accounting for\n * slippage and fees\n */\n uint256 assetReceivedForFullLPFees = curvePool.calc_withdraw_one_coin(\n lpRequiredFullFees,\n int128(uint128(_coinIndex))\n );\n\n // exact amount of LP required\n required3Crv =\n (lpRequiredFullFees * _amount) /\n assetReceivedForFullLPFees;\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n _lpWithdrawAll();\n // Withdraws are proportional to assets held by 3Pool\n uint256[3] memory minWithdrawAmounts = [\n uint256(0),\n uint256(0),\n uint256(0)\n ];\n\n // Remove liquidity\n ICurvePool threePool = ICurvePool(platformAddress);\n threePool.remove_liquidity(\n IERC20(pTokenAddress).balanceOf(address(this)),\n minWithdrawAmounts\n );\n // Transfer assets out of Vault\n // Note that Curve will provide all 3 of the assets in 3pool even if\n // we have not set PToken addresses for all of them in this strategy\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n IERC20 asset = IERC20(threePool.coins(i));\n asset.safeTransfer(vaultAddress, asset.balanceOf(address(this)));\n }\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 totalPTokens = IERC20(pTokenAddress).balanceOf(address(this));\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (totalPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = (totalPTokens * virtual_price) / 1e18;\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = value.scaleBy(assetDecimals, 18) / THREEPOOL_ASSET_COUNT;\n }\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n // This strategy is a special case since it only supports one asset\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n _approveAsset(assetsMapped[i]);\n }\n }\n\n /**\n * @dev Call the necessary approvals for the Curve pool and gauge\n * @param _asset Address of the asset\n */\n function _abstractSetPToken(address _asset, address) internal override {\n _approveAsset(_asset);\n }\n\n function _approveAsset(address _asset) internal {\n IERC20 asset = IERC20(_asset);\n // 3Pool for asset (required for adding liquidity)\n asset.safeApprove(platformAddress, 0);\n asset.safeApprove(platformAddress, type(uint256).max);\n }\n\n function _approveBase() internal virtual;\n\n /**\n * @dev Get the index of the coin\n */\n function _getCoinIndex(address _asset) internal view returns (uint256) {\n for (uint256 i = 0; i < 3; i++) {\n if (assetsMapped[i] == _asset) return i;\n }\n revert(\"Invalid 3pool asset\");\n }\n}\n" + }, + "contracts/strategies/aerodrome/AerodromeAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Aerodrome AMO strategy\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nimport { ISugarHelper } from \"../../interfaces/aerodrome/ISugarHelper.sol\";\nimport { INonfungiblePositionManager } from \"../../interfaces/aerodrome/INonfungiblePositionManager.sol\";\nimport { ISwapRouter } from \"../../interfaces/aerodrome/ISwapRouter.sol\";\nimport { ICLPool } from \"../../interfaces/aerodrome/ICLPool.sol\";\nimport { ICLGauge } from \"../../interfaces/aerodrome/ICLGauge.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\n\ncontract AerodromeAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n using SafeCast for uint256;\n\n /************************************************\n Important (!) setup configuration\n *************************************************/\n\n /**\n * In order to be able to remove a reasonable amount of complexity from the contract one of the\n * preconditions for this contract to function correctly is to have an outside account mint a small\n * amount of liquidity in the tick space where the contract will deploy's its liquidity and then send\n * that NFT LP position to a dead address (transfer to zero address not allowed.) See example of such\n * NFT LP token:\n * https://basescan.org/token/0x827922686190790b37229fd06084350e74485b72?a=413296#inventory\n */\n\n /***************************************\n Storage slot members\n ****************************************/\n\n /// @notice tokenId of the liquidity position\n uint256 public tokenId;\n /// @dev Minimum amount of tokens the strategy would be able to withdraw from the pool.\n /// minimum amount of tokens are withdrawn at a 1:1 price\n uint256 public underlyingAssets;\n /// @notice Marks the start of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareStart;\n /// @notice Marks the end of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareEnd;\n /// @dev reserved for inheritance\n int256[46] private __reserved;\n\n /***************************************\n Constants, structs and events\n ****************************************/\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the OETHb token contract\n address public immutable OETHb;\n /// @notice lower tick set to -1 representing the price of 1.0001 of WETH for 1 OETHb.\n int24 public immutable lowerTick;\n /// @notice lower tick set to 0 representing the price of 1.0000 of WETH for 1 OETHb.\n int24 public immutable upperTick;\n /// @notice tick spacing of the pool (set to 1)\n int24 public immutable tickSpacing;\n /// @notice the swapRouter for performing swaps\n ISwapRouter public immutable swapRouter;\n /// @notice the underlying AMO Slipstream pool\n ICLPool public immutable clPool;\n /// @notice the gauge for the corresponding Slipstream pool (clPool)\n /// @dev can become an immutable once the gauge is created on the base main-net\n ICLGauge public immutable clGauge;\n /// @notice the Position manager contract that is used to manage the pool's position\n INonfungiblePositionManager public immutable positionManager;\n /// @notice helper contract for liquidity and ticker math\n ISugarHelper public immutable helper;\n /// @notice sqrtRatioX96TickLower\n /// @dev tick lower has value -1 and represents the lowest price of WETH priced in OETHb. Meaning the pool\n /// offers less than 1 OETHb for 1 WETH. In other terms to get 1 OETHB the swap needs to offer 1.0001 WETH\n /// this is where purchasing OETHb with WETH within the liquidity position is most expensive\n uint160 public immutable sqrtRatioX96TickLower;\n /// @notice sqrtRatioX96TickHigher\n /// @dev tick higher has value 0 and represents 1:1 price parity of WETH to OETHb\n uint160 public immutable sqrtRatioX96TickHigher;\n /// @dev tick closest to 1:1 price parity\n /// Correctly assessing which tick is closer to 1:1 price parity is important since it affects\n /// the way we calculate the underlying assets in check Balance. The underlying aerodrome pool\n /// orders the tokens depending on the values of their addresses. If OETH token is token0 in the pool\n /// then sqrtRatioX96TickClosestToParity=sqrtRatioX96TickLower. If it is token1 in the pool then\n /// sqrtRatioX96TickClosestToParity=sqrtRatioX96TickHigher\n uint160 public immutable sqrtRatioX96TickClosestToParity;\n\n /// @dev a threshold under which the contract no longer allows for the protocol to rebalance. Guarding\n /// against a strategist / guardian being taken over and with multiple transactions draining the\n /// protocol funds.\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth); // 0x989e5ca8\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth); // 0xa6737d87\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n ); // 0x3681e8e0\n error OutsideExpectedTickRange(int24 currentTick); // 0x5a2eba75\n\n event PoolRebalanced(uint256 currentPoolWethShare);\n\n event PoolWethShareIntervalUpdated(\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n\n event LiquidityRemoved(\n uint256 withdrawLiquidityShare,\n uint256 removedWETHAmount,\n uint256 removedOETHbAmount,\n uint256 wethAmountCollected,\n uint256 oethbAmountCollected,\n uint256 underlyingAssets\n );\n\n event LiquidityAdded(\n uint256 wethAmountDesired,\n uint256 oethbAmountDesired,\n uint256 wethAmountSupplied,\n uint256 oethbAmountSupplied,\n uint256 tokenId,\n uint256 underlyingAssets\n );\n\n event UnderlyingAssetsUpdated(uint256 underlyingAssets);\n\n /**\n * @dev Un-stakes the token from the gauge for the execution duration of\n * the function and after that re-stakes it back in.\n *\n * It is important that the token is unstaked and owned by the strategy contract\n * during any liquidity altering operations and that it is re-staked back into the\n * gauge after liquidity changes. If the token fails to re-stake back to the\n * gauge it is not earning incentives.\n */\n // all functions using this modifier are used by functions with reentrancy check\n // slither-disable-start reentrancy-no-eth\n modifier gaugeUnstakeAndRestake() {\n // because of solidity short-circuit _isLpTokenStakedInGauge doesn't get called\n // when tokenId == 0\n if (tokenId != 0 && _isLpTokenStakedInGauge()) {\n clGauge.withdraw(tokenId);\n }\n _;\n // because of solidity short-circuit _isLpTokenStakedInGauge doesn't get called\n // when tokenId == 0\n if (tokenId != 0 && !_isLpTokenStakedInGauge()) {\n /**\n * It can happen that a withdrawal (or a full withdrawal) transactions would\n * remove all of the liquidity from the token with a NFT token still existing.\n * In that case the token can not be staked into the gauge, as some liquidity\n * needs to be added to it first.\n */\n if (_getLiquidity() > 0) {\n // if token liquidity changes the positionManager requires re-approval.\n // to any contract pre-approved to handle the token.\n positionManager.approve(address(clGauge), tokenId);\n clGauge.deposit(tokenId);\n }\n }\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice the constructor\n /// @dev This contract is intended to be used as a proxy. To prevent the\n /// potential confusion of having a functional implementation contract\n /// the constructor has the `initializer` modifier. This way the\n /// `initialize` function can not be called on the implementation contract.\n /// For the same reason the implementation contract also has the governor\n /// set to a zero address.\n /// @param _stratConfig the basic strategy configuration\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _oethbAddress Address of the Erc20 OETHb Token contract\n /// @param _swapRouter Address of the Aerodrome Universal Swap Router\n /// @param _nonfungiblePositionManager Address of position manager to add/remove\n /// the liquidity\n /// @param _clPool Address of the Aerodrome concentrated liquidity pool\n /// @param _clGauge Address of the Aerodrome slipstream pool gauge\n /// @param _sugarHelper Address of the Aerodrome Sugar helper contract\n /// @param _lowerBoundingTick Smaller bounding tick of our liquidity position\n /// @param _upperBoundingTick Larger bounding tick of our liquidity position\n /// @param _tickClosestToParity Tick that is closer to 1:1 price parity\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethbAddress,\n address _swapRouter,\n address _nonfungiblePositionManager,\n address _clPool,\n address _clGauge,\n address _sugarHelper,\n int24 _lowerBoundingTick,\n int24 _upperBoundingTick,\n int24 _tickClosestToParity\n ) initializer InitializableAbstractStrategy(_stratConfig) {\n require(\n _lowerBoundingTick == _tickClosestToParity ||\n _upperBoundingTick == _tickClosestToParity,\n \"Misconfigured tickClosestToParity\"\n );\n require(\n ICLPool(_clPool).token0() == _wethAddress,\n \"Only WETH supported as token0\"\n );\n require(\n ICLPool(_clPool).token1() == _oethbAddress,\n \"Only OETHb supported as token1\"\n );\n int24 _tickSpacing = ICLPool(_clPool).tickSpacing();\n // when we generalize AMO we might support other tick spacings\n require(_tickSpacing == 1, \"Unsupported tickSpacing\");\n\n WETH = _wethAddress;\n OETHb = _oethbAddress;\n swapRouter = ISwapRouter(_swapRouter);\n positionManager = INonfungiblePositionManager(\n _nonfungiblePositionManager\n );\n clPool = ICLPool(_clPool);\n clGauge = ICLGauge(_clGauge);\n helper = ISugarHelper(_sugarHelper);\n sqrtRatioX96TickLower = ISugarHelper(_sugarHelper).getSqrtRatioAtTick(\n _lowerBoundingTick\n );\n sqrtRatioX96TickHigher = ISugarHelper(_sugarHelper).getSqrtRatioAtTick(\n _upperBoundingTick\n );\n sqrtRatioX96TickClosestToParity = ISugarHelper(_sugarHelper)\n .getSqrtRatioAtTick(_tickClosestToParity);\n\n lowerTick = _lowerBoundingTick;\n upperTick = _upperBoundingTick;\n tickSpacing = _tickSpacing;\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /**\n * @notice initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n */\n function initialize(address[] memory _rewardTokenAddresses)\n external\n onlyGovernor\n initializer\n {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n new address[](0),\n new address[](0)\n );\n }\n\n /***************************************\n Configuration \n ****************************************/\n\n /**\n * @notice Set allowed pool weth share interval. After the rebalance happens\n * the share of WETH token in the ticker needs to be withing the specifications\n * of the interval.\n *\n * @param _allowedWethShareStart Start of WETH share interval expressed as 18 decimal amount\n * @param _allowedWethShareEnd End of WETH share interval expressed as 18 decimal amount\n */\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external onlyGovernor {\n require(\n _allowedWethShareStart < _allowedWethShareEnd,\n \"Invalid interval\"\n );\n // can not go below 1% weth share\n require(_allowedWethShareStart > 0.01 ether, \"Invalid interval start\");\n // can not go above 95% weth share\n require(_allowedWethShareEnd < 0.95 ether, \"Invalid interval end\");\n\n allowedWethShareStart = _allowedWethShareStart;\n allowedWethShareEnd = _allowedWethShareEnd;\n emit PoolWethShareIntervalUpdated(\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n\n /***************************************\n Periphery utils\n ****************************************/\n\n function _isLpTokenStakedInGauge() internal view returns (bool) {\n require(tokenId != 0, \"Missing NFT LP token\");\n\n address owner = positionManager.ownerOf(tokenId);\n require(\n owner == address(clGauge) || owner == address(this),\n \"Unexpected token owner\"\n );\n return owner == address(clGauge);\n }\n\n /***************************************\n Strategy overrides \n ****************************************/\n\n /**\n * @notice Deposit an amount of assets into the strategy contract. Calling deposit doesn't\n * automatically deposit funds into the underlying Aerodrome pool\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposit WETH to the strategy contract. This function does not add liquidity to the\n * underlying Aerodrome pool.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance > 1e12) {\n _deposit(WETH, _wethBalance);\n }\n }\n\n /**\n * @dev Deposit WETH to the contract. This function doesn't deposit the liquidity to the\n * pool, that is done via the rebalance call.\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n emit Deposit(_asset, address(0), _amount);\n\n // if the pool price is not within the expected interval leave the WETH on the contract\n // as to not break the mints\n (bool _isExpectedRange, ) = _checkForExpectedPoolPrice(false);\n if (_isExpectedRange) {\n // deposit funds into the underlying pool\n _rebalance(0, false, 0);\n }\n }\n\n /**\n * @notice Rebalance the pool to the desired token split and Deposit any WETH on the contract to the\n * underlying aerodrome pool. Print the required amount of corresponding OETHb. After the rebalancing is\n * done burn any potentially remaining OETHb tokens still on the strategy contract.\n *\n * This function has a slightly different behaviour depending on the status of the underlying Aerodrome\n * slipstream pool. The function consists of the following 3 steps:\n * 1. withdrawPartialLiquidity -> so that moving the activeTrading price via a swap is cheaper\n * 2. swapToDesiredPosition -> move active trading price in the pool to be able to deposit WETH & OETHb\n * tokens with the desired pre-configured shares\n * 3. addLiquidity -> add liquidity into the pool respecting share split configuration\n *\n * Scenario 1: When there is no liquidity in the pool from the strategy but there is from other LPs then\n * only step 1 is skipped. (It is important to note that liquidity needs to exist in the configured\n * strategy tick ranges in order for the swap to be possible) Step 3 mints new liquidity position\n * instead of adding to an existing one.\n * Scenario 2: When there is strategy's liquidity in the pool all 3 steps are taken\n *\n *\n * Exact _amountToSwap, _swapWeth & _minTokenReceived parameters shall be determined by simulating the\n * transaction off-chain. The strategy checks that after the swap the share of the tokens is in the\n * expected ranges.\n *\n * @param _amountToSwap The amount of the token to swap\n * @param _swapWeth Swap using WETH when true, use OETHb when false\n * @param _minTokenReceived Slippage check -> minimum amount of token expected in return\n */\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) external nonReentrant onlyGovernorOrStrategist {\n _rebalance(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n\n function _rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n /**\n * Would be nice to check if there is any total liquidity in the pool before performing this swap\n * but there is no easy way to do that in UniswapV3:\n * - clPool.liquidity() -> only liquidity in the active tick\n * - asset[1&2].balanceOf(address(clPool)) -> will include uncollected tokens of LP providers\n * after their liquidity position has been decreased\n */\n\n /**\n * When rebalance is called for the first time there is no strategy\n * liquidity in the pool yet. The liquidity removal is thus skipped.\n * Also execute this function when WETH is required for the swap.\n */\n if (tokenId != 0 && _swapWeth && _amountToSwap > 0) {\n _ensureWETHBalance(_amountToSwap);\n }\n\n // in some cases we will just want to add liquidity and not issue a swap to move the\n // active trading position within the pool\n if (_amountToSwap > 0) {\n _swapToDesiredPosition(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n // calling check liquidity early so we don't get unexpected errors when adding liquidity\n // in the later stages of this function\n _checkForExpectedPoolPrice(true);\n\n _addLiquidity();\n\n // this call shouldn't be necessary, since adding liquidity shouldn't affect the active\n // trading price. It is a defensive programming measure.\n (, uint256 _wethSharePct) = _checkForExpectedPoolPrice(true);\n\n // revert if protocol insolvent\n _solvencyAssert();\n\n emit PoolRebalanced(_wethSharePct);\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethbSupply = IERC20(OETHb).totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethbSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /**\n * @dev Decrease partial or all liquidity from the pool.\n * @param _liquidityToDecrease The amount of liquidity to remove expressed in 18 decimal point\n */\n function _removeLiquidity(uint256 _liquidityToDecrease)\n internal\n gaugeUnstakeAndRestake\n {\n require(_liquidityToDecrease > 0, \"Must remove some liquidity\");\n\n uint128 _liquidity = _getLiquidity();\n // need to convert to uint256 since intermittent result is to big for uint128 to handle\n uint128 _liquidityToRemove = uint256(_liquidity)\n .mulTruncate(_liquidityToDecrease)\n .toUint128();\n\n /**\n * There is no liquidity to remove -> exit function early. This can happen after a\n * withdraw/withdrawAll removes all of the liquidity while retaining the NFT token.\n */\n if (_liquidity == 0 || _liquidityToRemove == 0) {\n return;\n }\n\n (uint256 _amountWeth, uint256 _amountOethb) = positionManager\n .decreaseLiquidity(\n // Both expected amounts can be 0 since we don't really care if any swaps\n // happen just before the liquidity removal.\n INonfungiblePositionManager.DecreaseLiquidityParams({\n tokenId: tokenId,\n liquidity: _liquidityToRemove,\n amount0Min: 0,\n amount1Min: 0,\n deadline: block.timestamp\n })\n );\n\n (\n uint256 _amountWethCollected,\n uint256 _amountOethbCollected\n ) = positionManager.collect(\n INonfungiblePositionManager.CollectParams({\n tokenId: tokenId,\n recipient: address(this),\n amount0Max: type(uint128).max, // defaults to all tokens owed\n amount1Max: type(uint128).max // defaults to all tokens owed\n })\n );\n\n _updateUnderlyingAssets();\n\n emit LiquidityRemoved(\n _liquidityToDecrease,\n _amountWeth, //removedWethAmount\n _amountOethb, //removedOethbAmount\n _amountWethCollected,\n _amountOethbCollected,\n underlyingAssets\n );\n\n _burnOethbOnTheContract();\n }\n\n /**\n * @dev Perform a swap so that after the swap the ticker has the desired WETH to OETHb token share.\n */\n function _swapToDesiredPosition(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n IERC20 _tokenToSwap = IERC20(_swapWeth ? WETH : OETHb);\n uint256 _balance = _tokenToSwap.balanceOf(address(this));\n\n if (_balance < _amountToSwap) {\n // This should never trigger since _ensureWETHBalance will already\n // throw an error if there is not enough WETH\n if (_swapWeth) {\n revert NotEnoughWethForSwap(_balance, _amountToSwap);\n }\n // if swapping OETHb\n uint256 mintForSwap = _amountToSwap - _balance;\n IVault(vaultAddress).mintForStrategy(mintForSwap);\n }\n\n // approve the specific amount of WETH required\n if (_swapWeth) {\n IERC20(WETH).approve(address(swapRouter), _amountToSwap);\n }\n\n // Swap it\n swapRouter.exactInputSingle(\n // sqrtPriceLimitX96 is just a rough sanity check that we are within 0 -> 1 tick\n // a more fine check is performed in _checkForExpectedPoolPrice\n // Note: this needs further work if we want to generalize this approach\n ISwapRouter.ExactInputSingleParams({\n tokenIn: address(_tokenToSwap),\n tokenOut: _swapWeth ? OETHb : WETH,\n tickSpacing: tickSpacing, // set to 1\n recipient: address(this),\n deadline: block.timestamp,\n amountIn: _amountToSwap,\n amountOutMinimum: _minTokenReceived, // slippage check\n sqrtPriceLimitX96: _swapWeth\n ? sqrtRatioX96TickLower\n : sqrtRatioX96TickHigher\n })\n );\n\n /**\n * In the interest of each function in _rebalance to leave the contract state as\n * clean as possible the OETHb tokens here are burned. This decreases the\n * dependence where `_swapToDesiredPosition` function relies on later functions\n * (`addLiquidity`) to burn the OETHb. Reducing the risk of error introduction.\n */\n _burnOethbOnTheContract();\n }\n\n /**\n * @dev Add liquidity into the pool in the pre-configured WETH to OETHb share ratios\n * defined by the allowedPoolWethShareStart|End interval. This function will respect\n * liquidity ratios when there is no liquidity yet in the pool. If liquidity is already\n * present then it relies on the `_swapToDesiredPosition` function in a step before\n * to already move the trading price to desired position (with some tolerance).\n */\n // rebalance already has re-entrency checks\n // slither-disable-start reentrancy-no-eth\n function _addLiquidity() internal gaugeUnstakeAndRestake {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n // don't deposit small liquidity amounts\n if (_wethBalance <= 1e12) {\n return;\n }\n\n uint160 _currentPrice = getPoolX96Price();\n /**\n * Sanity check active trading price is positioned within our desired tick.\n *\n * We revert when price is equal to the lower tick even though that is still\n * a valid amount in regards to ticker position by Sugar.estimateAmount call.\n * Current price equaling tick bound at the 1:1 price parity results in\n * uint overfow when calculating the OETHb balance to deposit.\n */\n if (\n _currentPrice <= sqrtRatioX96TickLower ||\n _currentPrice >= sqrtRatioX96TickHigher\n ) {\n revert OutsideExpectedTickRange(getCurrentTradingTick());\n }\n\n /**\n * If estimateAmount1 call fails it could be due to _currentPrice being really\n * close to a tick and amount1 is a larger number than the sugar helper is able\n * to compute.\n *\n * If token addresses were reversed estimateAmount0 would be required here\n */\n uint256 _oethbRequired = helper.estimateAmount1(\n _wethBalance,\n address(0), // no need to pass pool address when current price is specified\n _currentPrice,\n lowerTick,\n upperTick\n );\n\n if (_oethbRequired > _oethbBalance) {\n IVault(vaultAddress).mintForStrategy(\n _oethbRequired - _oethbBalance\n );\n }\n\n // approve the specific amount of WETH required\n IERC20(WETH).approve(address(positionManager), _wethBalance);\n\n uint256 _wethAmountSupplied;\n uint256 _oethbAmountSupplied;\n if (tokenId == 0) {\n (\n tokenId,\n ,\n _wethAmountSupplied,\n _oethbAmountSupplied\n ) = positionManager.mint(\n /** amount0Min & amount1Min are left at 0 because slippage protection is ensured by the\n * _checkForExpectedPoolPrice\n *›\n * Also sqrtPriceX96 is 0 because the pool is already created\n * non zero amount attempts to create a new instance of the pool\n */\n INonfungiblePositionManager.MintParams({\n token0: WETH,\n token1: OETHb,\n tickSpacing: tickSpacing,\n tickLower: lowerTick,\n tickUpper: upperTick,\n amount0Desired: _wethBalance,\n amount1Desired: _oethbRequired,\n amount0Min: 0,\n amount1Min: 0,\n recipient: address(this),\n deadline: block.timestamp,\n sqrtPriceX96: 0\n })\n );\n } else {\n (, _wethAmountSupplied, _oethbAmountSupplied) = positionManager\n .increaseLiquidity(\n /** amount0Min & amount1Min are left at 0 because slippage protection is ensured by the\n * _checkForExpectedPoolPrice\n */\n INonfungiblePositionManager.IncreaseLiquidityParams({\n tokenId: tokenId,\n amount0Desired: _wethBalance,\n amount1Desired: _oethbRequired,\n amount0Min: 0,\n amount1Min: 0,\n deadline: block.timestamp\n })\n );\n }\n\n _updateUnderlyingAssets();\n emit LiquidityAdded(\n _wethBalance, // wethAmountDesired\n _oethbRequired, // oethbAmountDesired\n _wethAmountSupplied, // wethAmountSupplied\n _oethbAmountSupplied, // oethbAmountSupplied\n tokenId, // tokenId\n underlyingAssets\n );\n\n // burn remaining OETHb\n _burnOethbOnTheContract();\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @dev Check that the Aerodrome pool price is within the expected\n * parameters.\n * This function works whether the strategy contract has liquidity\n * position in the pool or not. The function returns _wethSharePct\n * as a gas optimization measure.\n * @param throwException when set to true the function throws an exception\n * when pool's price is not within expected range.\n * @return _isExpectedRange Bool expressing price is within expected range\n * @return _wethSharePct Share of WETH owned by this strategy contract in the\n * configured ticker.\n */\n function _checkForExpectedPoolPrice(bool throwException)\n internal\n view\n returns (bool _isExpectedRange, uint256 _wethSharePct)\n {\n require(\n allowedWethShareStart != 0 && allowedWethShareEnd != 0,\n \"Weth share interval not set\"\n );\n\n uint160 _currentPrice = getPoolX96Price();\n\n /**\n * First check we are in expected tick range\n *\n * We revert even though price being equal to the lower tick would still\n * count being within lower tick for the purpose of Sugar.estimateAmount calls\n */\n if (\n _currentPrice <= sqrtRatioX96TickLower ||\n _currentPrice >= sqrtRatioX96TickHigher\n ) {\n if (throwException) {\n revert OutsideExpectedTickRange(getCurrentTradingTick());\n }\n return (false, 0);\n }\n\n // 18 decimal number expressed WETH tick share\n _wethSharePct = _getWethShare(_currentPrice);\n\n if (\n _wethSharePct < allowedWethShareStart ||\n _wethSharePct > allowedWethShareEnd\n ) {\n if (throwException) {\n revert PoolRebalanceOutOfBounds(\n _wethSharePct,\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n return (false, _wethSharePct);\n }\n\n return (true, _wethSharePct);\n }\n\n /**\n * Burns any OETHb tokens remaining on the strategy contract\n */\n function _burnOethbOnTheContract() internal {\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n if (_oethbBalance > 1e12) {\n IVault(vaultAddress).burnForStrategy(_oethbBalance);\n }\n }\n\n /// @dev This function assumes there are no uncollected tokens in the clPool owned by the strategy contract.\n /// For that reason any liquidity withdrawals must also collect the tokens.\n function _updateUnderlyingAssets() internal {\n if (tokenId == 0) {\n underlyingAssets = 0;\n emit UnderlyingAssetsUpdated(underlyingAssets);\n return;\n }\n\n uint128 _liquidity = _getLiquidity();\n\n /**\n * Our net value represent the smallest amount of tokens we are able to extract from the position\n * given our liquidity.\n *\n * The least amount of tokens extraditable from the position is where the active trading price is\n * at the ticker 0 meaning the pool is offering 1:1 trades between WETH & OETHb. At that moment the pool\n * consists completely of OETHb and no WETH.\n *\n * The more swaps from WETH -> OETHb happen on the pool the more the price starts to move towards the -1\n * ticker making OETHb (priced in WETH) more expensive.\n *\n * An additional note: when liquidity is 0 then the helper returns 0 for both token amounts. And the\n * function set underlying assets to 0.\n */\n (uint256 _wethAmount, uint256 _oethbAmount) = helper\n .getAmountsForLiquidity(\n sqrtRatioX96TickClosestToParity, // sqrtRatioX96\n sqrtRatioX96TickLower, // sqrtRatioAX96\n sqrtRatioX96TickHigher, // sqrtRatioBX96\n _liquidity\n );\n\n require(_wethAmount == 0, \"Non zero wethAmount\");\n underlyingAssets = _oethbAmount;\n emit UnderlyingAssetsUpdated(underlyingAssets);\n }\n\n /**\n * @dev This function removes the appropriate amount of liquidity to assure that the required\n * amount of WETH is available on the contract\n *\n * @param _amount WETH balance required on the contract\n */\n function _ensureWETHBalance(uint256 _amount) internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance >= _amount) {\n return;\n }\n\n require(tokenId != 0, \"No liquidity available\");\n uint256 _additionalWethRequired = _amount - _wethBalance;\n (uint256 _wethInThePool, ) = getPositionPrincipal();\n\n if (_wethInThePool < _additionalWethRequired) {\n revert NotEnoughWethLiquidity(\n _wethInThePool,\n _additionalWethRequired\n );\n }\n\n uint256 shareOfWethToRemove = Math.min(\n _additionalWethRequired.divPrecisely(_wethInThePool) + 1,\n 1e18\n );\n _removeLiquidity(shareOfWethToRemove);\n }\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset WETH address\n * @param _amount Amount of WETH to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n _ensureWETHBalance(_amount);\n\n _withdraw(_recipient, _amount);\n }\n\n /**\n * @notice Withdraw WETH and sends it to the Vault.\n */\n function withdrawAll() external override onlyVault nonReentrant {\n if (tokenId != 0) {\n _removeLiquidity(1e18);\n }\n\n uint256 _balance = IERC20(WETH).balanceOf(address(this));\n if (_balance > 0) {\n _withdraw(vaultAddress, _balance);\n }\n }\n\n function _withdraw(address _recipient, uint256 _amount) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n IERC20(WETH).safeTransfer(_recipient, _amount);\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /**\n * @dev Collect the AERO token from the gauge\n */\n function _collectRewardTokens() internal override {\n if (tokenId != 0 && _isLpTokenStakedInGauge()) {\n clGauge.getReward(tokenId);\n }\n super._collectRewardTokens();\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /**\n * @dev Approve the spending of all assets\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n // to add liquidity to the clPool\n IERC20(OETHb).approve(address(positionManager), type(uint256).max);\n // to be able to rebalance using the swapRouter\n IERC20(OETHb).approve(address(swapRouter), type(uint256).max);\n\n /* the behaviour of this strategy has slightly changed and WETH could be\n * present on the contract between the transactions. For that reason we are\n * un-approving WETH to the swapRouter & positionManager and only approving\n * the required amount before a transaction\n */\n IERC20(WETH).approve(address(swapRouter), 0);\n IERC20(WETH).approve(address(positionManager), 0);\n }\n\n /***************************************\n Balances and Fees\n ****************************************/\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256)\n {\n require(_asset == WETH, \"Only WETH supported\");\n\n // we could in theory deposit to the strategy and forget to call rebalance in the same\n // governance transaction batch. In that case the WETH that is on the strategy contract\n // also needs to be accounted for.\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n // just paranoia check, in case there is OETHb in the strategy that for some reason hasn't\n // been burned yet.\n uint256 _oethbBalance = IERC20(OETHb).balanceOf(address(this));\n return underlyingAssets + _wethBalance + _oethbBalance;\n }\n\n /**\n * @dev Returns the balance of both tokens in a given position (excluding fees)\n * @return _amountWeth Amount of WETH in position\n * @return _amountOethb Amount of OETHb in position\n */\n function getPositionPrincipal()\n public\n view\n returns (uint256 _amountWeth, uint256 _amountOethb)\n {\n if (tokenId == 0) {\n return (0, 0);\n }\n\n uint160 _sqrtRatioX96 = getPoolX96Price();\n (_amountWeth, _amountOethb) = helper.principal(\n positionManager,\n tokenId,\n _sqrtRatioX96\n );\n }\n\n /**\n * @notice Returns the current pool price in X96 format\n * @return _sqrtRatioX96 Pool price\n */\n function getPoolX96Price() public view returns (uint160 _sqrtRatioX96) {\n (_sqrtRatioX96, , , , , ) = clPool.slot0();\n }\n\n /**\n * @notice Returns the current active trading tick of the underlying pool\n * @return _currentTick Current pool trading tick\n */\n function getCurrentTradingTick() public view returns (int24 _currentTick) {\n (, _currentTick, , , , ) = clPool.slot0();\n }\n\n /**\n * @notice Returns the percentage of WETH liquidity in the configured ticker\n * owned by this strategy contract.\n * @return uint256 1e18 denominated percentage expressing the share\n */\n function getWETHShare() external view returns (uint256) {\n uint160 _currentPrice = getPoolX96Price();\n return _getWethShare(_currentPrice);\n }\n\n /**\n * @notice Returns the amount of liquidity in the contract's LP position\n * @return _liquidity Amount of liquidity in the position\n */\n function _getLiquidity() internal view returns (uint128 _liquidity) {\n if (tokenId == 0) {\n revert(\"No LP position\");\n }\n\n (, , , , , , , _liquidity, , , , ) = positionManager.positions(tokenId);\n }\n\n function _getWethShare(uint160 _currentPrice)\n internal\n view\n returns (uint256)\n {\n /**\n * If estimateAmount1 call fails it could be due to _currentPrice being really\n * close to a tick and amount1 too big to compute.\n *\n * If token addresses were reversed estimateAmount0 would be required here\n */\n uint256 _normalizedWethAmount = 1 ether;\n uint256 _correspondingOethAmount = helper.estimateAmount1(\n _normalizedWethAmount,\n address(0), // no need to pass pool address when current price is specified\n _currentPrice,\n lowerTick,\n upperTick\n );\n\n // 18 decimal number expressed weth tick share\n return\n _normalizedWethAmount.divPrecisely(\n _normalizedWethAmount + _correspondingOethAmount\n );\n }\n\n /***************************************\n Hidden functions\n ****************************************/\n /// @inheritdoc InitializableAbstractStrategy\n function setPTokenAddress(address, address) external override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /// @inheritdoc InitializableAbstractStrategy\n function removePToken(uint256) external override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Not supported\n */\n function _abstractSetPToken(address, address) internal override {\n // the deployer shall call safeApproveAllTokens() to set necessary approvals\n revert(\"Unsupported method\");\n }\n\n /***************************************\n ERC721 management\n ****************************************/\n\n /// @notice Callback function for whenever a NFT is transferred to this contract\n // solhint-disable-next-line max-line-length\n /// Ref: https://docs.openzeppelin.com/contracts/3.x/api/token/erc721#IERC721Receiver-onERC721Received-address-address-uint256-bytes-\n function onERC721Received(\n address,\n address,\n uint256,\n bytes calldata\n ) external returns (bytes4) {\n return this.onERC721Received.selector;\n }\n}\n" + }, + "contracts/strategies/balancer/AbstractAuraStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Base Balancer Abstract Strategy\n * @author Origin Protocol Inc\n */\n\nimport { AbstractBalancerStrategy } from \"./AbstractBalancerStrategy.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IERC4626 } from \"../../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport { IRewardStaking } from \"../IRewardStaking.sol\";\n\nabstract contract AbstractAuraStrategy is AbstractBalancerStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n /// @notice Address of the Aura rewards pool\n address public immutable auraRewardPoolAddress;\n\n // renamed from __reserved to not shadow AbstractBalancerStrategy.__reserved,\n int256[50] private __reserved_baseAuraStrategy;\n\n constructor(address _auraRewardPoolAddress) {\n auraRewardPoolAddress = _auraRewardPoolAddress;\n }\n\n /**\n * @dev Deposit all Balancer Pool Tokens (BPT) in this strategy contract\n * to the Aura rewards pool.\n */\n function _lpDepositAll() internal virtual override {\n uint256 bptBalance = IERC20(platformAddress).balanceOf(address(this));\n uint256 auraLp = IERC4626(auraRewardPoolAddress).deposit(\n bptBalance,\n address(this)\n );\n require(bptBalance == auraLp, \"Aura LP != BPT\");\n }\n\n /**\n * @dev Withdraw `numBPTTokens` Balancer Pool Tokens (BPT) from\n * the Aura rewards pool to this strategy contract.\n * @param numBPTTokens Number of Balancer Pool Tokens (BPT) to withdraw\n */\n function _lpWithdraw(uint256 numBPTTokens) internal virtual override {\n IRewardStaking(auraRewardPoolAddress).withdrawAndUnwrap(\n numBPTTokens,\n true // also claim reward tokens\n );\n }\n\n /**\n * @dev Withdraw all Balancer Pool Tokens (BPT) from\n * the Aura rewards pool to this strategy contract.\n */\n function _lpWithdrawAll() internal virtual override {\n // Get all the strategy's BPTs in Aura\n // maxRedeem is implemented as balanceOf(address) in Aura\n uint256 bptBalance = IERC4626(auraRewardPoolAddress).maxRedeem(\n address(this)\n );\n\n IRewardStaking(auraRewardPoolAddress).withdrawAndUnwrap(\n bptBalance,\n true // also claim reward tokens\n );\n }\n\n /**\n * @notice Collects BAL and AURA tokens from the rewards pool.\n */\n function collectRewardTokens()\n external\n virtual\n override\n onlyHarvester\n nonReentrant\n {\n /* Similar to Convex, calling this function collects both of the\n * accrued BAL and AURA tokens.\n */\n IRewardStaking(auraRewardPoolAddress).getReward();\n _collectRewardTokens();\n }\n\n /// @notice Balancer Pool Tokens (BPT) in the Balancer pool and the Aura rewards pool.\n function _getBalancerPoolTokens()\n internal\n view\n override\n returns (uint256 balancerPoolTokens)\n {\n balancerPoolTokens =\n IERC20(platformAddress).balanceOf(address(this)) +\n // maxRedeem is implemented as balanceOf(address) in Aura\n IERC4626(auraRewardPoolAddress).maxRedeem(address(this));\n }\n\n function _approveBase() internal virtual override {\n super._approveBase();\n\n IERC20 pToken = IERC20(platformAddress);\n pToken.safeApprove(auraRewardPoolAddress, type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/balancer/AbstractBalancerStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Balancer Abstract Strategy\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\nimport { IRateProvider } from \"../../interfaces/balancer/IRateProvider.sol\";\nimport { VaultReentrancyLib } from \"./VaultReentrancyLib.sol\";\nimport { IOracle } from \"../../interfaces/IOracle.sol\";\nimport { IWstETH } from \"../../interfaces/IWstETH.sol\";\nimport { IERC4626 } from \"../../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nabstract contract AbstractBalancerStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n address public immutable rETH;\n address public immutable stETH;\n address public immutable wstETH;\n address public immutable frxETH;\n address public immutable sfrxETH;\n\n /// @notice Address of the Balancer vault\n IBalancerVault public immutable balancerVault;\n /// @notice Balancer pool identifier\n bytes32 public immutable balancerPoolId;\n\n // Max withdrawal deviation denominated in 1e18 (1e18 == 100%)\n uint256 public maxWithdrawalDeviation;\n // Max deposit deviation denominated in 1e18 (1e18 == 100%)\n uint256 public maxDepositDeviation;\n\n int256[48] private __reserved;\n\n struct BaseBalancerConfig {\n address rEthAddress; // Address of the rETH token\n address stEthAddress; // Address of the stETH token\n address wstEthAddress; // Address of the wstETH token\n address frxEthAddress; // Address of the frxEth token\n address sfrxEthAddress; // Address of the sfrxEth token\n address balancerVaultAddress; // Address of the Balancer vault\n bytes32 balancerPoolId; // Balancer pool identifier\n }\n\n event MaxWithdrawalDeviationUpdated(\n uint256 _prevMaxDeviationPercentage,\n uint256 _newMaxDeviationPercentage\n );\n event MaxDepositDeviationUpdated(\n uint256 _prevMaxDeviationPercentage,\n uint256 _newMaxDeviationPercentage\n );\n\n /**\n * @dev Ensure we are not in a Vault context when this function is called, by attempting a no-op internal\n * balance operation. If we are already in a Vault transaction (e.g., a swap, join, or exit), the Vault's\n * reentrancy protection will cause this function to revert.\n *\n * Use this modifier with any function that can cause a state change in a pool and is either public itself,\n * or called by a public function *outside* a Vault operation (e.g., join, exit, or swap).\n *\n * This is to protect against Balancer's read-only re-entrancy vulnerability:\n * https://www.notion.so/originprotocol/Balancer-read-only-reentrancy-c686e72c82414ef18fa34312bb02e11b\n */\n modifier whenNotInBalancerVaultContext() {\n VaultReentrancyLib.ensureNotInVaultContext(balancerVault);\n _;\n }\n\n constructor(BaseBalancerConfig memory _balancerConfig) {\n rETH = _balancerConfig.rEthAddress;\n stETH = _balancerConfig.stEthAddress;\n wstETH = _balancerConfig.wstEthAddress;\n frxETH = _balancerConfig.frxEthAddress;\n sfrxETH = _balancerConfig.sfrxEthAddress;\n\n balancerVault = IBalancerVault(_balancerConfig.balancerVaultAddress);\n balancerPoolId = _balancerConfig.balancerPoolId;\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Balancer's strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of BAL & AURA\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * WETH, stETH\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // BAL & AURA\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n maxWithdrawalDeviation = 1e16;\n maxDepositDeviation = 1e16;\n\n emit MaxWithdrawalDeviationUpdated(0, maxWithdrawalDeviation);\n emit MaxDepositDeviationUpdated(0, maxDepositDeviation);\n\n IERC20[] memory poolAssets = _getPoolAssets();\n require(\n poolAssets.length == _assets.length,\n \"Pool assets length mismatch\"\n );\n for (uint256 i = 0; i < _assets.length; ++i) {\n address asset = _fromPoolAsset(address(poolAssets[i]));\n require(_assets[i] == asset, \"Pool assets mismatch\");\n }\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @notice Get strategy's share of an assets in the Balancer pool.\n * This is not denominated in OUSD/ETH value of the assets in the Balancer pool.\n * @param _asset Address of the Vault collateral asset\n * @return amount the amount of vault collateral assets\n *\n * IMPORTANT if this function is overridden it needs to have a whenNotInBalancerVaultContext\n * modifier on it or it is susceptible to read-only re-entrancy attack\n *\n * @dev it is important that this function is not affected by reporting inflated\n * values of assets in case of any pool manipulation. Such a manipulation could easily\n * exploit the protocol by:\n * - minting OETH\n * - tilting Balancer pool to report higher balances of assets\n * - rebasing() -> all that extra token balances get distributed to OETH holders\n * - tilting pool back\n * - redeeming OETH\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n whenNotInBalancerVaultContext\n returns (uint256 amount)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n\n uint256 bptBalance = _getBalancerPoolTokens();\n\n /* To calculate the worth of queried asset:\n * - assume that all tokens normalized to their ETH value have an equal split balance\n * in the pool when it is balanced\n * - multiply the BPT amount with the bpt rate to get the ETH denominated amount\n * of strategy's holdings\n * - divide that by the number of tokens we support in the pool to get ETH denominated\n * amount that is applicable to each supported token in the pool.\n *\n * It would be possible to support only 1 asset in the pool (and be exposed to all\n * the assets while holding BPT tokens) and deposit/withdraw/checkBalance using only\n * that asset. TBD: changes to other functions still required if we ever decide to\n * go with such configuration.\n */\n amount = (bptBalance.mulTruncate(\n IRateProvider(platformAddress).getRate()\n ) / assetsMapped.length);\n\n /* If the pool asset is equal to (strategy )_asset it means that a rate\n * provider for that asset exists and that asset is not necessarily\n * pegged to a unit (ETH).\n *\n * Because this function returns the balance of the asset and is not denominated in\n * ETH units we need to convert the ETH denominated amount to asset amount.\n */\n if (_toPoolAsset(_asset) == _asset) {\n amount = amount.divPrecisely(_getRateProviderRate(_asset));\n }\n }\n\n /**\n * @notice Returns the value of all assets managed by this strategy.\n * Uses the Balancer pool's rate (virtual price) to convert the strategy's\n * Balancer Pool Tokens (BPT) to ETH value.\n * @return value The ETH value\n *\n * IMPORTANT if this function is overridden it needs to have a whenNotInBalancerVaultContext\n * modifier on it or it is susceptible to read-only re-entrancy attack\n */\n function checkBalance()\n external\n view\n virtual\n whenNotInBalancerVaultContext\n returns (uint256 value)\n {\n uint256 bptBalance = _getBalancerPoolTokens();\n\n // Convert BPT to ETH value\n value = bptBalance.mulTruncate(\n IRateProvider(platformAddress).getRate()\n );\n }\n\n /// @notice Balancer Pool Tokens (BPT) in the Balancer pool.\n function _getBalancerPoolTokens()\n internal\n view\n virtual\n returns (uint256 balancerPoolTokens)\n {\n balancerPoolTokens = IERC20(platformAddress).balanceOf(address(this));\n }\n\n /* solhint-disable max-line-length */\n /**\n * @notice BPT price is calculated by taking the rate from the rateProvider of the asset in\n * question. If one does not exist it defaults to 1e18. To get the final BPT expected that\n * is multiplied by the underlying asset amount divided by BPT token rate. BPT token rate is\n * similar to Curve's virtual_price and expresses how much has the price of BPT appreciated\n * (e.g. due to swap fees) in relation to the underlying assets\n *\n * Using the above approach makes the strategy vulnerable to a possible MEV attack using\n * flash loan to manipulate the pool before a deposit/withdrawal since the function ignores\n * market values of the assets being priced in BPT.\n *\n * At the time of writing there is no safe on-chain approach to pricing BPT in a way that it\n * would make it invulnerable to MEV pool manipulation. See recent Balancer exploit:\n * https://www.notion.so/originprotocol/Balancer-OETH-strategy-9becdea132704e588782a919d7d471eb?pvs=4#1cf07de12fc64f1888072321e0644348\n *\n * To mitigate MEV possibilities during deposits and withdraws, the VaultValueChecker will use checkBalance before and after the move\n * to ensure the expected changes took place.\n *\n * @param _asset Address of the Balancer pool asset\n * @param _amount Amount of the Balancer pool asset\n * @return bptExpected of BPT expected in exchange for the asset\n *\n * @dev\n * bptAssetPrice = 1e18 (asset peg) * pool_asset_rate\n *\n * bptExpected = bptAssetPrice * asset_amount / BPT_token_rate\n *\n * bptExpected = 1e18 (asset peg) * pool_asset_rate * asset_amount / BPT_token_rate\n * bptExpected = asset_amount * pool_asset_rate / BPT_token_rate\n *\n * further information available here:\n * https://www.notion.so/originprotocol/Balancer-OETH-strategy-9becdea132704e588782a919d7d471eb?pvs=4#ce01495ae70346d8971f5dced809fb83\n */\n /* solhint-enable max-line-length */\n function _getBPTExpected(address _asset, uint256 _amount)\n internal\n view\n virtual\n returns (uint256 bptExpected)\n {\n uint256 bptRate = IRateProvider(platformAddress).getRate();\n uint256 poolAssetRate = _getRateProviderRate(_asset);\n bptExpected = _amount.mulTruncate(poolAssetRate).divPrecisely(bptRate);\n }\n\n function _getBPTExpected(\n address[] memory _assets,\n uint256[] memory _amounts\n ) internal view virtual returns (uint256 bptExpected) {\n require(_assets.length == _amounts.length, \"Assets & amounts mismatch\");\n\n for (uint256 i = 0; i < _assets.length; ++i) {\n uint256 poolAssetRate = _getRateProviderRate(_assets[i]);\n // convert asset amount to ETH amount\n bptExpected += _amounts[i].mulTruncate(poolAssetRate);\n }\n\n uint256 bptRate = IRateProvider(platformAddress).getRate();\n // Convert ETH amount to BPT amount\n bptExpected = bptExpected.divPrecisely(bptRate);\n }\n\n function _lpDepositAll() internal virtual;\n\n function _lpWithdraw(uint256 numBPTTokens) internal virtual;\n\n function _lpWithdrawAll() internal virtual;\n\n /**\n * @notice Balancer returns assets and rateProviders for corresponding assets ordered\n * by numerical order.\n */\n function _getPoolAssets() internal view returns (IERC20[] memory assets) {\n // slither-disable-next-line unused-return\n (assets, , ) = balancerVault.getPoolTokens(balancerPoolId);\n }\n\n /**\n * @dev If an asset is rebasing the Balancer pools have a wrapped versions of assets\n * that the strategy supports. This function converts the pool(wrapped) asset\n * and corresponding amount to strategy asset.\n */\n function _toPoolAsset(address asset, uint256 amount)\n internal\n view\n returns (address poolAsset, uint256 poolAmount)\n {\n if (asset == stETH) {\n poolAsset = wstETH;\n if (amount > 0) {\n poolAmount = IWstETH(wstETH).getWstETHByStETH(amount);\n }\n } else if (asset == frxETH) {\n poolAsset = sfrxETH;\n if (amount > 0) {\n poolAmount = IERC4626(sfrxETH).convertToShares(amount);\n }\n } else {\n poolAsset = asset;\n poolAmount = amount;\n }\n }\n\n /**\n * @dev Converts a Vault collateral asset to a Balancer pool asset.\n * stETH becomes wstETH, frxETH becomes sfrxETH and everything else stays the same.\n * @param asset Address of the Vault collateral asset.\n * @return Address of the Balancer pool asset.\n */\n function _toPoolAsset(address asset) internal view returns (address) {\n if (asset == stETH) {\n return wstETH;\n } else if (asset == frxETH) {\n return sfrxETH;\n }\n return asset;\n }\n\n /**\n * @dev Converts rebasing asset to its wrapped counterpart.\n */\n function _wrapPoolAsset(address asset, uint256 amount)\n internal\n returns (address wrappedAsset, uint256 wrappedAmount)\n {\n if (asset == stETH) {\n wrappedAsset = wstETH;\n if (amount > 0) {\n wrappedAmount = IWstETH(wstETH).wrap(amount);\n }\n } else if (asset == frxETH) {\n wrappedAsset = sfrxETH;\n if (amount > 0) {\n wrappedAmount = IERC4626(sfrxETH).deposit(\n amount,\n address(this)\n );\n }\n } else {\n wrappedAsset = asset;\n wrappedAmount = amount;\n }\n }\n\n /**\n * @dev Converts wrapped asset to its rebasing counterpart.\n */\n function _unwrapPoolAsset(address asset, uint256 amount)\n internal\n returns (uint256 unwrappedAmount)\n {\n if (asset == stETH) {\n unwrappedAmount = IWstETH(wstETH).unwrap(amount);\n } else if (asset == frxETH) {\n unwrappedAmount = IERC4626(sfrxETH).withdraw(\n amount,\n address(this),\n address(this)\n );\n } else {\n unwrappedAmount = amount;\n }\n }\n\n /**\n * @dev If an asset is rebasing the Balancer pools have a wrapped versions of assets\n * that the strategy supports. This function converts the rebasing strategy asset\n * and corresponding amount to wrapped(pool) asset.\n */\n function _fromPoolAsset(address poolAsset, uint256 poolAmount)\n internal\n view\n returns (address asset, uint256 amount)\n {\n if (poolAsset == wstETH) {\n asset = stETH;\n if (poolAmount > 0) {\n amount = IWstETH(wstETH).getStETHByWstETH(poolAmount);\n }\n } else if (poolAsset == sfrxETH) {\n asset = frxETH;\n if (poolAmount > 0) {\n amount = IERC4626(sfrxETH).convertToAssets(poolAmount);\n }\n } else {\n asset = poolAsset;\n amount = poolAmount;\n }\n }\n\n function _fromPoolAsset(address poolAsset)\n internal\n view\n returns (address asset)\n {\n if (poolAsset == wstETH) {\n asset = stETH;\n } else if (poolAsset == sfrxETH) {\n asset = frxETH;\n } else {\n asset = poolAsset;\n }\n }\n\n /**\n * @notice Sets max withdrawal deviation that is considered when removing\n * liquidity from Balancer pools.\n * @param _maxWithdrawalDeviation Max withdrawal deviation denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxWithdrawalDeviation will be 1% (1e16) for production\n * usage. Vault value checker in combination with checkBalance will\n * catch any unexpected manipulation.\n */\n function setMaxWithdrawalDeviation(uint256 _maxWithdrawalDeviation)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(\n _maxWithdrawalDeviation <= 1e18,\n \"Withdrawal dev. out of bounds\"\n );\n emit MaxWithdrawalDeviationUpdated(\n maxWithdrawalDeviation,\n _maxWithdrawalDeviation\n );\n maxWithdrawalDeviation = _maxWithdrawalDeviation;\n }\n\n /**\n * @notice Sets max deposit deviation that is considered when adding\n * liquidity to Balancer pools.\n * @param _maxDepositDeviation Max deposit deviation denominated in\n * wad (number with 18 decimals): 1e18 == 100%, 1e16 == 1%\n *\n * IMPORTANT Minimum maxDepositDeviation will default to 1% (1e16)\n * for production usage. Vault value checker in combination with\n * checkBalance will catch any unexpected manipulation.\n */\n function setMaxDepositDeviation(uint256 _maxDepositDeviation)\n external\n onlyVaultOrGovernorOrStrategist\n {\n require(_maxDepositDeviation <= 1e18, \"Deposit dev. out of bounds\");\n emit MaxDepositDeviationUpdated(\n maxDepositDeviation,\n _maxDepositDeviation\n );\n maxDepositDeviation = _maxDepositDeviation;\n }\n\n function _approveBase() internal virtual {\n IERC20 pToken = IERC20(platformAddress);\n // Balancer vault for BPT token (required for removing liquidity)\n pToken.safeApprove(address(balancerVault), type(uint256).max);\n }\n\n function _getRateProviderRate(address _asset)\n internal\n view\n virtual\n returns (uint256);\n}\n" + }, + "contracts/strategies/balancer/BalancerMetaPoolStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OETH Balancer MetaStablePool Strategy\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { AbstractAuraStrategy, AbstractBalancerStrategy } from \"./AbstractAuraStrategy.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\nimport { IRateProvider } from \"../../interfaces/balancer/IRateProvider.sol\";\nimport { IMetaStablePool } from \"../../interfaces/balancer/IMetaStablePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\ncontract BalancerMetaPoolStrategy is AbstractAuraStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(\n BaseStrategyConfig memory _stratConfig,\n BaseBalancerConfig memory _balancerConfig,\n address _auraRewardPoolAddress\n )\n InitializableAbstractStrategy(_stratConfig)\n AbstractBalancerStrategy(_balancerConfig)\n AbstractAuraStrategy(_auraRewardPoolAddress)\n {}\n\n /**\n * @notice There are no plans to configure BalancerMetaPool as a default\n * asset strategy. For that reason there is no need to support this\n * functionality.\n */\n function deposit(address, uint256)\n external\n override\n onlyVault\n nonReentrant\n {\n revert(\"Not supported\");\n }\n\n /**\n * @notice There are no plans to configure BalancerMetaPool as a default\n * asset strategy. For that reason there is no need to support this\n * functionality.\n */\n function deposit(address[] calldata, uint256[] calldata)\n external\n onlyVault\n nonReentrant\n {\n revert(\"Not supported\");\n }\n\n /**\n * @notice Deposits all supported assets in this strategy contract to the Balancer pool.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 assetsLength = assetsMapped.length;\n address[] memory strategyAssets = new address[](assetsLength);\n uint256[] memory strategyAmounts = new uint256[](assetsLength);\n\n // For each vault collateral asset\n for (uint256 i = 0; i < assetsLength; ++i) {\n strategyAssets[i] = assetsMapped[i];\n // Get the asset balance in this strategy contract\n strategyAmounts[i] = IERC20(strategyAssets[i]).balanceOf(\n address(this)\n );\n }\n _deposit(strategyAssets, strategyAmounts);\n }\n\n /*\n * _deposit doesn't require a read-only re-entrancy protection since during the deposit\n * the function enters the Balancer Vault Context. If this function were called as part of\n * the attacking contract (while intercepting execution flow upon receiving ETH) the read-only\n * protection of the Balancer Vault would be triggered. Since the attacking contract would\n * already be in the Balancer Vault context and wouldn't be able to enter it again.\n */\n function _deposit(\n address[] memory _strategyAssets,\n uint256[] memory _strategyAmounts\n ) internal {\n require(\n _strategyAssets.length == _strategyAmounts.length,\n \"Array length missmatch\"\n );\n\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n\n uint256[] memory strategyAssetAmountsToPoolAssetAmounts = new uint256[](\n _strategyAssets.length\n );\n address[] memory strategyAssetsToPoolAssets = new address[](\n _strategyAssets.length\n );\n\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n address strategyAsset = _strategyAssets[i];\n uint256 strategyAmount = _strategyAmounts[i];\n\n require(\n assetToPToken[strategyAsset] != address(0),\n \"Unsupported asset\"\n );\n strategyAssetsToPoolAssets[i] = _toPoolAsset(strategyAsset);\n\n if (strategyAmount > 0) {\n emit Deposit(strategyAsset, platformAddress, strategyAmount);\n\n // wrap rebasing assets like stETH and frxETH to wstETH and sfrxETH\n (, strategyAssetAmountsToPoolAssetAmounts[i]) = _wrapPoolAsset(\n strategyAsset,\n strategyAmount\n );\n }\n }\n\n uint256[] memory amountsIn = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n for (uint256 i = 0; i < tokens.length; ++i) {\n // Convert IERC20 type to address\n poolAssets[i] = address(tokens[i]);\n\n // For each of the mapped assets\n for (uint256 j = 0; j < strategyAssetsToPoolAssets.length; ++j) {\n // If the pool asset is the same as the mapped asset\n if (poolAssets[i] == strategyAssetsToPoolAssets[j]) {\n amountsIn[i] = strategyAssetAmountsToPoolAssetAmounts[j];\n }\n }\n }\n\n uint256 minBPT = _getBPTExpected(\n strategyAssetsToPoolAssets,\n strategyAssetAmountsToPoolAssetAmounts\n );\n uint256 minBPTwDeviation = minBPT.mulTruncate(\n 1e18 - maxDepositDeviation\n );\n\n /* EXACT_TOKENS_IN_FOR_BPT_OUT:\n * User sends precise quantities of tokens, and receives an\n * estimated but unknown (computed at run time) quantity of BPT.\n *\n * ['uint256', 'uint256[]', 'uint256']\n * [EXACT_TOKENS_IN_FOR_BPT_OUT, amountsIn, minimumBPT]\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,\n amountsIn,\n minBPTwDeviation\n );\n\n IBalancerVault.JoinPoolRequest memory request = IBalancerVault\n .JoinPoolRequest(poolAssets, amountsIn, userData, false);\n\n // Add the pool assets in this strategy to the balancer pool\n balancerVault.joinPool(\n balancerPoolId,\n address(this),\n address(this),\n request\n );\n\n // Deposit the Balancer Pool Tokens (BPT) into Aura\n _lpDepositAll();\n }\n\n /**\n * @notice Withdraw a Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAsset Address of the Vault collateral asset\n * @param _strategyAmount The amount of Vault collateral assets to withdraw\n */\n function withdraw(\n address _recipient,\n address _strategyAsset,\n uint256 _strategyAmount\n ) external override onlyVault nonReentrant {\n address[] memory strategyAssets = new address[](1);\n uint256[] memory strategyAmounts = new uint256[](1);\n strategyAssets[0] = _strategyAsset;\n strategyAmounts[0] = _strategyAmount;\n\n _withdraw(_recipient, strategyAssets, strategyAmounts);\n }\n\n /**\n * @notice Withdraw multiple Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAssets Addresses of the Vault collateral assets\n * @param _strategyAmounts The amounts of Vault collateral assets to withdraw\n */\n function withdraw(\n address _recipient,\n address[] calldata _strategyAssets,\n uint256[] calldata _strategyAmounts\n ) external onlyVault nonReentrant {\n _withdraw(_recipient, _strategyAssets, _strategyAmounts);\n }\n\n /**\n * @dev Withdraw multiple Vault collateral asset from the Balancer pool.\n * @param _recipient Address to receive the Vault collateral assets. Typically is the Vault.\n * @param _strategyAssets Addresses of the Vault collateral assets\n * @param _strategyAmounts The amounts of Vault collateral assets to withdraw\n *\n * _withdrawal doesn't require a read-only re-entrancy protection since during the withdrawal\n * the function enters the Balancer Vault Context. If this function were called as part of\n * the attacking contract (while intercepting execution flow upon receiving ETH) the read-only\n * protection of the Balancer Vault would be triggered. Since the attacking contract would\n * already be in the Balancer Vault context and wouldn't be able to enter it again.\n */\n function _withdraw(\n address _recipient,\n address[] memory _strategyAssets,\n uint256[] memory _strategyAmounts\n ) internal {\n require(\n _strategyAssets.length == _strategyAmounts.length,\n \"Invalid input arrays\"\n );\n\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n require(\n assetToPToken[_strategyAssets[i]] != address(0),\n \"Unsupported asset\"\n );\n }\n\n // STEP 1 - Calculate the Balancer pool assets and amounts from the vault collateral assets\n\n // Get all the supported balancer pool assets\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n // Calculate the balancer pool assets and amounts to withdraw\n uint256[] memory poolAssetsAmountsOut = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n // Is the wrapped asset amount indexed by the assets array, not the order of the Balancer pool tokens\n // eg wstETH and sfrxETH amounts, not the stETH and frxETH amounts\n uint256[] memory strategyAssetsToPoolAssetsAmounts = new uint256[](\n _strategyAssets.length\n );\n\n // For each of the Balancer pool assets\n for (uint256 i = 0; i < tokens.length; ++i) {\n poolAssets[i] = address(tokens[i]);\n\n // Convert the Balancer pool asset back to a vault collateral asset\n address strategyAsset = _fromPoolAsset(poolAssets[i]);\n\n // for each of the vault assets\n for (uint256 j = 0; j < _strategyAssets.length; ++j) {\n // If the vault asset equals the vault asset mapped from the Balancer pool asset\n if (_strategyAssets[j] == strategyAsset) {\n (, poolAssetsAmountsOut[i]) = _toPoolAsset(\n strategyAsset,\n _strategyAmounts[j]\n );\n strategyAssetsToPoolAssetsAmounts[j] = poolAssetsAmountsOut[\n i\n ];\n\n /* Because of the potential Balancer rounding error mentioned below\n * the contract might receive 1-2 WEI smaller amount than required\n * in the withdraw user data encoding. If slightly lesser token amount\n * is received the strategy can not unwrap the pool asset as it is\n * smaller than expected.\n *\n * For that reason we `overshoot` the required tokens expected to\n * circumvent the error\n */\n if (poolAssetsAmountsOut[i] > 0) {\n poolAssetsAmountsOut[i] += 2;\n }\n }\n }\n }\n\n // STEP 2 - Calculate the max about of Balancer Pool Tokens (BPT) to withdraw\n\n // Estimate the required amount of Balancer Pool Tokens (BPT) for the assets\n uint256 maxBPTtoWithdraw = _getBPTExpected(\n poolAssets,\n /* all non 0 values are overshot by 2 WEI and with the expected mainnet\n * ~1% withdrawal deviation, the 2 WEI aren't important\n */\n poolAssetsAmountsOut\n );\n // Increase BPTs by the max allowed deviation\n // Any excess BPTs will be left in this strategy contract\n maxBPTtoWithdraw = maxBPTtoWithdraw.mulTruncate(\n 1e18 + maxWithdrawalDeviation\n );\n\n // STEP 3 - Withdraw the Balancer Pool Tokens (BPT) from Aura to this strategy contract\n\n // Withdraw BPT from Aura allowing for BPTs left in this strategy contract from previous withdrawals\n _lpWithdraw(\n maxBPTtoWithdraw - IERC20(platformAddress).balanceOf(address(this))\n );\n\n // STEP 4 - Withdraw the balancer pool assets from the pool\n\n /* Custom asset exit: BPT_IN_FOR_EXACT_TOKENS_OUT:\n * User sends an estimated but unknown (computed at run time) quantity of BPT,\n * and receives precise quantities of specified tokens.\n *\n * ['uint256', 'uint256[]', 'uint256']\n * [BPT_IN_FOR_EXACT_TOKENS_OUT, amountsOut, maxBPTAmountIn]\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.BPT_IN_FOR_EXACT_TOKENS_OUT,\n poolAssetsAmountsOut,\n maxBPTtoWithdraw\n );\n\n IBalancerVault.ExitPoolRequest memory request = IBalancerVault\n .ExitPoolRequest(\n poolAssets,\n /* We specify the exact amount of a tokens we are expecting in the encoded\n * userData, for that reason we don't need to specify the amountsOut here.\n *\n * Also Balancer has a rounding issue that can make a transaction fail:\n * https://github.com/balancer/balancer-v2-monorepo/issues/2541\n * which is an extra reason why this field is empty.\n */\n new uint256[](tokens.length),\n userData,\n false\n );\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n /* Payable keyword is required because of the IBalancerVault interface even though\n * this strategy shall never be receiving native ETH\n */\n payable(address(this)),\n request\n );\n\n // STEP 5 - Re-deposit any left over BPT tokens back into Aura\n /* When concluding how much of BPT we need to withdraw from Aura we overshoot by\n * roughly around 1% (initial mainnet setting of maxWithdrawalDeviation). After exiting\n * the pool strategy could have left over BPT tokens that are not earning boosted yield.\n * We re-deploy those back in.\n */\n _lpDepositAll();\n\n // STEP 6 - Unswap balancer pool assets to vault collateral assets and send to the vault.\n\n // For each of the specified assets\n for (uint256 i = 0; i < _strategyAssets.length; ++i) {\n // Unwrap assets like wstETH and sfrxETH to rebasing assets stETH and frxETH\n if (strategyAssetsToPoolAssetsAmounts[i] > 0) {\n _unwrapPoolAsset(\n _strategyAssets[i],\n strategyAssetsToPoolAssetsAmounts[i]\n );\n }\n\n // Transfer the vault collateral assets to the recipient, which is typically the vault\n if (_strategyAmounts[i] > 0) {\n IERC20(_strategyAssets[i]).safeTransfer(\n _recipient,\n _strategyAmounts[i]\n );\n\n emit Withdrawal(\n _strategyAssets[i],\n platformAddress,\n _strategyAmounts[i]\n );\n }\n }\n }\n\n /**\n * @notice Withdraws all supported Vault collateral assets from the Balancer pool\n * and send to the OToken's Vault.\n *\n * Is only executable by the OToken's Vault or the Governor.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n // STEP 1 - Withdraw all Balancer Pool Tokens (BPT) from Aura to this strategy contract\n\n _lpWithdrawAll();\n // Get the BPTs withdrawn from Aura plus any that were already in this strategy contract\n uint256 BPTtoWithdraw = IERC20(platformAddress).balanceOf(\n address(this)\n );\n // Get the balancer pool assets and their total balances\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n uint256[] memory minAmountsOut = new uint256[](tokens.length);\n address[] memory poolAssets = new address[](tokens.length);\n for (uint256 i = 0; i < tokens.length; ++i) {\n poolAssets[i] = address(tokens[i]);\n }\n\n // STEP 2 - Withdraw the Balancer pool assets from the pool\n /* Proportional exit: EXACT_BPT_IN_FOR_TOKENS_OUT:\n * User sends a precise quantity of BPT, and receives an estimated but unknown\n * (computed at run time) quantity of a single token\n *\n * ['uint256', 'uint256']\n * [EXACT_BPT_IN_FOR_TOKENS_OUT, bptAmountIn]\n *\n * It is ok to pass an empty minAmountsOut since tilting the pool in any direction\n * when doing a proportional exit can only be beneficial to the strategy. Since\n * it will receive more of the underlying tokens for the BPT traded in.\n */\n bytes memory userData = abi.encode(\n IBalancerVault.WeightedPoolExitKind.EXACT_BPT_IN_FOR_TOKENS_OUT,\n BPTtoWithdraw\n );\n\n IBalancerVault.ExitPoolRequest memory request = IBalancerVault\n .ExitPoolRequest(poolAssets, minAmountsOut, userData, false);\n\n balancerVault.exitPool(\n balancerPoolId,\n address(this),\n /* Payable keyword is required because of the IBalancerVault interface even though\n * this strategy shall never be receiving native ETH\n */\n payable(address(this)),\n request\n );\n\n // STEP 3 - Convert the balancer pool assets to the vault collateral assets and send to the vault\n // For each of the Balancer pool assets\n for (uint256 i = 0; i < tokens.length; ++i) {\n address poolAsset = address(tokens[i]);\n // Convert the balancer pool asset to the strategy asset\n address strategyAsset = _fromPoolAsset(poolAsset);\n // Get the balancer pool assets withdraw from the pool plus any that were already in this strategy contract\n uint256 poolAssetAmount = IERC20(poolAsset).balanceOf(\n address(this)\n );\n\n // Unwrap assets like wstETH and sfrxETH to rebasing assets stETH and frxETH\n uint256 unwrappedAmount = 0;\n if (poolAssetAmount > 0) {\n unwrappedAmount = _unwrapPoolAsset(\n strategyAsset,\n poolAssetAmount\n );\n }\n\n // Transfer the vault collateral assets to the vault\n if (unwrappedAmount > 0) {\n IERC20(strategyAsset).safeTransfer(\n vaultAddress,\n unwrappedAmount\n );\n emit Withdrawal(\n strategyAsset,\n platformAddress,\n unwrappedAmount\n );\n }\n }\n }\n\n /**\n * @notice Approves the Balancer Vault to transfer poolAsset counterparts\n * of all of the supported assets from this strategy. E.g. stETH is a supported\n * strategy and Balancer Vault gets unlimited approval to transfer wstETH.\n *\n * If Balancer pool uses a wrapped version of a supported asset then also approve\n * unlimited usage of an asset to the contract responsible for wrapping.\n *\n * Approve unlimited spending by Balancer Vault and Aura reward pool of the\n * pool BPT tokens.\n *\n * Is only executable by the Governor.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n _abstractSetPToken(assetsMapped[i], platformAddress);\n }\n _approveBase();\n }\n\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address) internal override {\n address poolAsset = _toPoolAsset(_asset);\n if (_asset == stETH) {\n // slither-disable-next-line unused-return\n IERC20(stETH).approve(wstETH, type(uint256).max);\n } else if (_asset == frxETH) {\n // slither-disable-next-line unused-return\n IERC20(frxETH).approve(sfrxETH, type(uint256).max);\n }\n _approveAsset(poolAsset);\n }\n\n /**\n * @dev Approves the Balancer Vault to transfer an asset from\n * this strategy. The assets could be a Vault collateral asset\n * like WETH or rETH; or a Balancer pool asset that wraps the vault asset\n * like wstETH or sfrxETH.\n */\n function _approveAsset(address _asset) internal {\n IERC20 asset = IERC20(_asset);\n // slither-disable-next-line unused-return\n asset.approve(address(balancerVault), type(uint256).max);\n }\n\n /**\n * @notice Returns the rate supplied by the Balancer configured rate\n * provider. Rate is used to normalize the token to common underlying\n * pool denominator. (ETH for ETH Liquid staking derivatives)\n *\n * @param _asset Address of the Balancer pool asset\n * @return rate of the corresponding asset\n */\n function _getRateProviderRate(address _asset)\n internal\n view\n override\n returns (uint256)\n {\n IMetaStablePool pool = IMetaStablePool(platformAddress);\n IRateProvider[] memory providers = pool.getRateProviders();\n (IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(\n balancerPoolId\n );\n\n uint256 providersLength = providers.length;\n for (uint256 i = 0; i < providersLength; ++i) {\n // _assets and corresponding rate providers are all in the same order\n if (address(tokens[i]) == _asset) {\n // rate provider doesn't exist, defaults to 1e18\n if (address(providers[i]) == address(0)) {\n return 1e18;\n }\n return providers[i].getRate();\n }\n }\n\n // should never happen\n assert(false);\n }\n}\n" + }, + "contracts/strategies/balancer/VaultReentrancyLib.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity >=0.7.0 <0.9.0;\n\nimport \"../../utils/BalancerErrors.sol\";\nimport { IBalancerVault } from \"../../interfaces/balancer/IBalancerVault.sol\";\n\nlibrary VaultReentrancyLib {\n /**\n * @dev Ensure we are not in a Vault context when this function is called, by attempting a no-op internal\n * balance operation. If we are already in a Vault transaction (e.g., a swap, join, or exit), the Vault's\n * reentrancy protection will cause this function to revert.\n *\n * The exact function call doesn't really matter: we're just trying to trigger the Vault reentrancy check\n * (and not hurt anything in case it works). An empty operation array with no specific operation at all works\n * for that purpose, and is also the least expensive in terms of gas and bytecode size.\n *\n * Call this at the top of any function that can cause a state change in a pool and is either public itself,\n * or called by a public function *outside* a Vault operation (e.g., join, exit, or swap).\n *\n * If this is *not* called in functions that are vulnerable to the read-only reentrancy issue described\n * here (https://forum.balancer.fi/t/reentrancy-vulnerability-scope-expanded/4345), those functions are unsafe,\n * and subject to manipulation that may result in loss of funds.\n */\n function ensureNotInVaultContext(IBalancerVault vault) internal view {\n // Perform the following operation to trigger the Vault's reentrancy guard:\n //\n // IBalancerVault.UserBalanceOp[] memory noop = new IBalancerVault.UserBalanceOp[](0);\n // _vault.manageUserBalance(noop);\n //\n // However, use a static call so that it can be a view function (even though the function is non-view).\n // This allows the library to be used more widely, as some functions that need to be protected might be\n // view.\n //\n // This staticcall always reverts, but we need to make sure it doesn't fail due to a re-entrancy attack.\n // Staticcalls consume all gas forwarded to them on a revert caused by storage modification.\n // By default, almost the entire available gas is forwarded to the staticcall,\n // causing the entire call to revert with an 'out of gas' error.\n //\n // We set the gas limit to 10k for the staticcall to\n // avoid wasting gas when it reverts due to storage modification.\n // `manageUserBalance` is a non-reentrant function in the Vault, so calling it invokes `_enterNonReentrant`\n // in the `ReentrancyGuard` contract, reproduced here:\n //\n // function _enterNonReentrant() private {\n // // If the Vault is actually being reentered, it will revert in the first line, at the `_require` that\n // // checks the reentrancy flag, with \"BAL#400\" (corresponding to Errors.REENTRANCY) in the revertData.\n // // The full revertData will be: `abi.encodeWithSignature(\"Error(string)\", \"BAL#400\")`.\n // _require(_status != _ENTERED, Errors.REENTRANCY);\n //\n // // If the Vault is not being reentered, the check above will pass: but it will *still* revert,\n // // because the next line attempts to modify storage during a staticcall. However, this type of\n // // failure results in empty revertData.\n // _status = _ENTERED;\n // }\n //\n // So based on this analysis, there are only two possible revertData values: empty, or abi.encoded BAL#400.\n //\n // It is of course much more bytecode and gas efficient to check for zero-length revertData than to compare it\n // to the encoded REENTRANCY revertData.\n //\n // While it should be impossible for the call to fail in any other way (especially since it reverts before\n // `manageUserBalance` even gets called), any other error would generate non-zero revertData, so checking for\n // empty data guards against this case too.\n\n (, bytes memory revertData) = address(vault).staticcall{ gas: 10_000 }(\n abi.encodeWithSelector(vault.manageUserBalance.selector, 0)\n );\n\n _require(revertData.length == 0, Errors.REENTRANCY);\n }\n}\n" + }, + "contracts/strategies/BaseCurveAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for the Curve OETH/WETH pool\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { ICurveStableSwapNG } from \"../interfaces/ICurveStableSwapNG.sol\";\nimport { ICurveXChainLiquidityGauge } from \"../interfaces/ICurveXChainLiquidityGauge.sol\";\nimport { IChildLiquidityGaugeFactory } from \"../interfaces/IChildLiquidityGaugeFactory.sol\";\n\ncontract BaseCurveAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @dev a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n // New immutable variables that must be set in the constructor\n /**\n * @notice Address of the Wrapped ETH (WETH) contract.\n */\n IWETH9 public immutable weth;\n\n /**\n * @notice Address of the OETH token contract.\n */\n IERC20 public immutable oeth;\n\n /**\n * @notice Address of the LP (Liquidity Provider) token contract.\n */\n IERC20 public immutable lpToken;\n\n /**\n * @notice Address of the Curve StableSwap NG pool contract.\n */\n ICurveStableSwapNG public immutable curvePool;\n\n /**\n * @notice Address of the Curve X-Chain Liquidity Gauge contract.\n */\n ICurveXChainLiquidityGauge public immutable gauge;\n\n /**\n * @notice Address of the Child Liquidity Gauge Factory contract.\n */\n IChildLiquidityGaugeFactory public immutable gaugeFactory;\n\n // Ordered list of pool assets\n uint128 public immutable oethCoinIndex;\n uint128 public immutable wethCoinIndex;\n\n /**\n * @notice Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n uint256 public maxSlippage;\n\n event MaxSlippageUpdated(uint256 newMaxSlippage);\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balancesBefore = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffBefore = balancesBefore[wethCoinIndex].toInt256() -\n balancesBefore[oethCoinIndex].toInt256();\n\n _;\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balancesAfter = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffAfter = balancesAfter[wethCoinIndex].toInt256() -\n balancesAfter[oethCoinIndex].toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of ETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _oeth,\n address _weth,\n address _gauge,\n address _gaugeFactory,\n uint128 _oethCoinIndex,\n uint128 _wethCoinIndex\n ) InitializableAbstractStrategy(_baseConfig) {\n oethCoinIndex = _oethCoinIndex;\n wethCoinIndex = _wethCoinIndex;\n\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveStableSwapNG(_baseConfig.platformAddress);\n\n oeth = IERC20(_oeth);\n weth = IWETH9(_weth);\n gauge = ICurveXChainLiquidityGauge(_gauge);\n gaugeFactory = IChildLiquidityGaugeFactory(_gaugeFactory);\n\n _setGovernor(address(0));\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV\n * @param _maxSlippage Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV\n uint256 _maxSlippage\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n address[] memory _assets = new address[](1);\n _assets[0] = address(weth);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n _setMaxSlippage(_maxSlippage);\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit WETH into the Curve pool\n * @param _weth Address of Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to deposit.\n */\n function deposit(address _weth, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_weth, _amount);\n }\n\n function _deposit(address _weth, uint256 _wethAmount) internal {\n require(_wethAmount > 0, \"Must deposit something\");\n require(_weth == address(weth), \"Can only deposit WETH\");\n\n emit Deposit(_weth, address(lpToken), _wethAmount);\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 oethToAdd = uint256(\n _max(\n 0,\n balances[wethCoinIndex].toInt256() +\n _wethAmount.toInt256() -\n balances[oethCoinIndex].toInt256()\n )\n );\n\n /* Add so much OETH so that the pool ends up being balanced. And at minimum\n * add as much OETH as WETH and at maximum twice as much OETH.\n */\n oethToAdd = Math.max(oethToAdd, _wethAmount);\n oethToAdd = Math.min(oethToAdd, _wethAmount * 2);\n\n /* Mint OETH with a strategy that attempts to contribute to stability of OETH/WETH pool. Try\n * to mint so much OETH that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OETH minted will always be at least equal or greater\n * to WETH amount deployed. And never larger than twice the WETH amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(oethToAdd);\n\n emit Deposit(address(oeth), address(lpToken), oethToAdd);\n\n uint256[] memory _amounts = new uint256[](2);\n _amounts[wethCoinIndex] = _wethAmount;\n _amounts[oethCoinIndex] = oethToAdd;\n\n uint256 valueInLpTokens = (_wethAmount + oethToAdd).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Do the deposit to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(_amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool's LP tokens into the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n /**\n * @notice Deposit the strategy's entire balance of WETH into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = weth.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(weth), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _weth Address of the Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to withdraw.\n */\n function withdraw(\n address _recipient,\n address _weth,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_weth == address(weth), \"Can only withdraw WETH\");\n\n emit Withdrawal(_weth, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(_amount);\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough WETH on balanced removal\n */\n uint256[] memory _minWithdrawalAmounts = new uint256[](2);\n _minWithdrawalAmounts[wethCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OETH and any that was left in the strategy\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n\n // Transfer WETH to the recipient\n require(\n weth.transfer(_recipient, _amount),\n \"Transfer of WETH not successful\"\n );\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n function calcTokenToBurn(uint256 _wethAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much WETH\n * we want we can determine how much of OETH we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolWETHBalance = curvePool.balances(wethCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolWETHBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_wethAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = gauge.balanceOf(address(this));\n // Can not withdraw zero LP tokens from the gauge\n if (gaugeTokens == 0) return;\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[] memory minWithdrawAmounts = new uint256[](2);\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(\n lpToken.balanceOf(address(this)),\n minWithdrawAmounts\n );\n\n // Burn all OETH\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Get the strategy contract's WETH balance.\n // This includes all that was removed from the Curve pool and\n // any ether that was sitting in the strategy contract before the removal.\n uint256 ethBalance = weth.balanceOf(address(this));\n require(\n weth.transfer(vaultAddress, ethBalance),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethBalance);\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[] memory amounts = new uint256[](2);\n amounts[oethCoinIndex] = _oTokens;\n\n // Convert OETH to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool LP tokens to the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Deposit(address(oeth), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough ETH.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove OTokens from the Curve pool\n uint256 oethToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n oethCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /**\n * @notice One-sided remove of ETH from the Curve pool, convert to WETH\n * and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for ETH.\n * @dev Curve pool LP tokens is used rather than WETH assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of ETH. Curve's `calc_token_amount` functioun does not include fees.\n * A 3rd party libary can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * caclulate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Curve gauge and remove ETH from the Curve pool\n uint256 ethAmount = _withdrawAndRemoveFromPool(\n _lpTokens,\n wethCoinIndex\n );\n\n // Transfer WETH to the vault\n require(\n weth.transfer(vaultAddress, ethAmount),\n \"Transfer of WETH not successful\"\n );\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(weth), address(lpToken), ethAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the Convex pool and\n * do a one-sided remove of ETH or OETH from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the Convex pool.\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = ETH, 1 = OETH.\n * @return coinsRemoved The amount of ETH or OETH removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Curve gauge\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to ETH value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n // Apply slippage to ETH value\n uint256 minAmount = valueInEth.mulTruncate(uint256(1e18) - maxSlippage);\n\n // Remove just the ETH from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethbSupply = oeth.totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethbSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // CRV rewards flow.\n //---\n // CRV inflation:\n // Gauge receive CRV rewards from inflation.\n // Each checkpoint on the gauge send this CRV inflation to gauge factory.\n // This strategy should call mint on the gauge factory to collect the CRV rewards.\n // ---\n // Extra rewards:\n // Calling claim_rewards on the gauge will only claim extra rewards (outside of CRV).\n // ---\n\n // Mint CRV on Child Liquidity gauge factory\n gaugeFactory.mint(address(gauge));\n // Collect extra gauge rewards (outside of CRV)\n gauge.claim_rewards();\n\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _lpAmount) internal {\n // withdraw lp tokens from the gauge without claiming rewards\n gauge.withdraw(_lpAmount);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // WETH balance needed here for the balance check that happens from vault during depositing.\n balance = weth.balanceOf(address(this));\n uint256 lpTokens = gauge.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += (lpTokens * curvePool.get_virtual_price()) / 1e18;\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(weth);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Sets the maximum slippage allowed for any swap/liquidity operation\n * @param _maxSlippage Maximum slippage allowed, 1e18 = 100%.\n */\n function setMaxSlippage(uint256 _maxSlippage) external onlyGovernor {\n _setMaxSlippage(_maxSlippage);\n }\n\n function _setMaxSlippage(uint256 _maxSlippage) internal {\n require(_maxSlippage <= 5e16, \"Slippage must be less than 100%\");\n maxSlippage = _maxSlippage;\n emit MaxSlippageUpdated(_maxSlippage);\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OETH (required for adding liquidity)\n // slither-disable-next-line unused-return\n oeth.approve(platformAddress, type(uint256).max);\n\n // Approve Curve pool for WETH (required for adding liquidity)\n // slither-disable-next-line unused-return\n weth.approve(platformAddress, type(uint256).max);\n\n // Approve Curve gauge contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Curve gauge.\n // slither-disable-next-line unused-return\n lpToken.approve(address(gauge), type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/BridgedWOETHStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, SafeERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { AggregatorV3Interface } from \"../interfaces/chainlink/AggregatorV3Interface.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\n\ncontract BridgedWOETHStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using StableMath for uint128;\n using SafeCast for uint256;\n using SafeERC20 for IERC20;\n\n event MaxPriceDiffBpsUpdated(uint128 oldValue, uint128 newValue);\n event WOETHPriceUpdated(uint128 oldValue, uint128 newValue);\n\n IWETH9 public immutable weth;\n IERC20 public immutable bridgedWOETH;\n IERC20 public immutable oethb;\n\n uint256 public constant MAX_PRICE_STALENESS = 2 days;\n\n uint128 public lastOraclePrice;\n uint128 public maxPriceDiffBps;\n\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _weth,\n address _bridgedWOETH,\n address _oethb\n ) InitializableAbstractStrategy(_stratConfig) {\n weth = IWETH9(_weth);\n bridgedWOETH = IERC20(_bridgedWOETH);\n oethb = IERC20(_oethb);\n }\n\n function initialize(uint128 _maxPriceDiffBps)\n external\n onlyGovernor\n initializer\n {\n InitializableAbstractStrategy._initialize(\n new address[](0), // No reward tokens\n new address[](0), // No assets\n new address[](0) // No pTokens\n );\n\n _setMaxPriceDiffBps(_maxPriceDiffBps);\n }\n\n /**\n * @dev Sets the max price diff bps for the wOETH value appreciation\n * @param _maxPriceDiffBps Bps value, 10k == 100%\n */\n function setMaxPriceDiffBps(uint128 _maxPriceDiffBps)\n external\n onlyGovernor\n {\n _setMaxPriceDiffBps(_maxPriceDiffBps);\n }\n\n /**\n * @dev Sets the max price diff bps for the wOETH value appreciation\n * @param _maxPriceDiffBps Bps value, 10k == 100%\n */\n function _setMaxPriceDiffBps(uint128 _maxPriceDiffBps) internal {\n require(\n _maxPriceDiffBps > 0 && _maxPriceDiffBps <= 10000,\n \"Invalid bps value\"\n );\n\n emit MaxPriceDiffBpsUpdated(maxPriceDiffBps, _maxPriceDiffBps);\n\n maxPriceDiffBps = _maxPriceDiffBps;\n }\n\n /**\n * @dev Wrapper for _updateWOETHOraclePrice with nonReentrant flag\n * @return The latest price of wOETH from Oracle\n */\n function updateWOETHOraclePrice() external nonReentrant returns (uint256) {\n return _updateWOETHOraclePrice();\n }\n\n /**\n * @dev Finds the value of bridged wOETH from the Oracle.\n * Ensures that it's within the bounds and reasonable.\n * And stores it.\n *\n * NOTE: Intentionally not caching `Vault.priceProvider` here,\n * since doing so would mean that we also have to update this\n * strategy every time there's a change in oracle router.\n * Besides on L2, the gas is considerably cheaper than mainnet.\n *\n * @return Latest price from oracle\n */\n function _updateWOETHOraclePrice() internal returns (uint256) {\n // WETH price per unit of bridged wOETH\n uint256 oraclePrice = IOracle(IVault(vaultAddress).priceProvider())\n .price(address(bridgedWOETH));\n\n // 1 wOETH > 1 WETH, always\n require(oraclePrice > 1 ether, \"Invalid wOETH value\");\n\n uint128 oraclePrice128 = oraclePrice.toUint128();\n\n // Do some checks\n if (lastOraclePrice > 0) {\n // Make sure the value only goes up\n require(oraclePrice128 >= lastOraclePrice, \"Negative wOETH yield\");\n\n // lastOraclePrice * (1 + maxPriceDiffBps)\n uint256 maxPrice = (lastOraclePrice * (1e4 + maxPriceDiffBps)) /\n 1e4;\n\n // And that it's within the bounds.\n require(oraclePrice128 <= maxPrice, \"Price diff beyond threshold\");\n }\n\n emit WOETHPriceUpdated(lastOraclePrice, oraclePrice128);\n\n // Store the price\n lastOraclePrice = oraclePrice128;\n\n return oraclePrice;\n }\n\n /**\n * @dev Computes & returns the value of given wOETH in WETH\n * @param woethAmount Amount of wOETH\n * @return Value of wOETH in WETH (using the last stored oracle price)\n */\n function getBridgedWOETHValue(uint256 woethAmount)\n public\n view\n returns (uint256)\n {\n return (woethAmount * lastOraclePrice) / 1 ether;\n }\n\n /**\n * @dev Takes in bridged wOETH and mints & returns\n * equivalent amount of OETHb.\n * @param woethAmount Amount of bridged wOETH to transfer in\n */\n function depositBridgedWOETH(uint256 woethAmount)\n external\n onlyGovernorOrStrategist\n nonReentrant\n {\n // Update wOETH price\n uint256 oraclePrice = _updateWOETHOraclePrice();\n\n // Figure out how much they are worth\n uint256 oethToMint = (woethAmount * oraclePrice) / 1 ether;\n\n require(oethToMint > 0, \"Invalid deposit amount\");\n\n // There's no pToken, however, it just uses WOETH address in the event\n emit Deposit(address(weth), address(bridgedWOETH), oethToMint);\n\n // Mint OETHb tokens and transfer it to the caller\n IVault(vaultAddress).mintForStrategy(oethToMint);\n\n // Transfer out minted OETHb\n // slither-disable-next-line unchecked-transfer unused-return\n oethb.transfer(msg.sender, oethToMint);\n\n // Transfer in all bridged wOETH tokens\n // slither-disable-next-line unchecked-transfer unused-return\n bridgedWOETH.transferFrom(msg.sender, address(this), woethAmount);\n }\n\n /**\n * @dev Takes in OETHb and burns it and returns\n * equivalent amount of bridged wOETH.\n * @param oethToBurn Amount of OETHb to burn\n */\n function withdrawBridgedWOETH(uint256 oethToBurn)\n external\n onlyGovernorOrStrategist\n nonReentrant\n {\n // Update wOETH price\n uint256 oraclePrice = _updateWOETHOraclePrice();\n\n // Figure out how much they are worth\n uint256 woethAmount = (oethToBurn * 1 ether) / oraclePrice;\n\n require(woethAmount > 0, \"Invalid withdraw amount\");\n\n // There's no pToken, however, it just uses WOETH address in the event\n emit Withdrawal(address(weth), address(bridgedWOETH), oethToBurn);\n\n // Transfer WOETH back\n // slither-disable-next-line unchecked-transfer unused-return\n bridgedWOETH.transfer(msg.sender, woethAmount);\n\n // Transfer in OETHb\n // slither-disable-next-line unchecked-transfer unused-return\n oethb.transferFrom(msg.sender, address(this), oethToBurn);\n\n // Burn OETHb\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n }\n\n /**\n * @notice Returns the amount of backing WETH the strategy holds\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // Figure out how much wOETH is worth at the time.\n // Always uses the last stored oracle price.\n // Call updateWOETHOraclePrice manually to pull in latest yields.\n\n // NOTE: If the contract has been deployed but the call to\n // `updateWOETHOraclePrice()` has never been made, then this\n // will return zero. It should be fine because the strategy\n // should update the price whenever a deposit/withdraw happens.\n\n // If `updateWOETHOraclePrice()` hasn't been called in a while,\n // the strategy will underreport its holdings but never overreport it.\n\n balance =\n (bridgedWOETH.balanceOf(address(this)) * lastOraclePrice) /\n 1 ether;\n }\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n // Strategist deposits bridged wOETH but the contract only\n // reports the balance in WETH. As far as Vault is concerned,\n // it isn't aware of bridged wOETH token\n return _asset == address(weth);\n }\n\n /***************************************\n Overridden methods\n ****************************************/\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function transferToken(address _asset, uint256 _amount)\n public\n override\n onlyGovernor\n {\n require(\n _asset != address(bridgedWOETH) && _asset != address(weth),\n \"Cannot transfer supported asset\"\n );\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice deposit() function not used for this strategy\n */\n function deposit(address, uint256)\n external\n override\n onlyVault\n nonReentrant\n {\n // Use depositBridgedWOETH() instead\n require(false, \"Deposit disabled\");\n }\n\n /**\n * @notice depositAll() function not used for this strategy\n */\n function depositAll() external override onlyVault nonReentrant {\n // Use depositBridgedWOETH() instead\n require(false, \"Deposit disabled\");\n }\n\n /**\n * @notice withdraw() function not used for this strategy\n */\n function withdraw(\n // solhint-disable-next-line no-unused-vars\n address _recipient,\n // solhint-disable-next-line no-unused-vars\n address _asset,\n // solhint-disable-next-line no-unused-vars\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(false, \"Withdrawal disabled\");\n }\n\n /**\n * @notice withdrawAll() function not used for this strategy\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n // Withdrawal disabled\n }\n\n function _abstractSetPToken(address, address) internal override {\n revert(\"No pTokens are used\");\n }\n\n function safeApproveAllTokens() external override {}\n\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function removePToken(uint256) external override {\n revert(\"No pTokens are used\");\n }\n\n /**\n * @inheritdoc InitializableAbstractStrategy\n */\n function collectRewardTokens() external override {}\n}\n" + }, + "contracts/strategies/CompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Compound Strategy\n * @notice Investment strategy for Compound like lending platforms. eg Compound and Flux\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICERC20 } from \"./ICompound.sol\";\nimport { AbstractCompoundStrategy, InitializableAbstractStrategy } from \"./AbstractCompoundStrategy.sol\";\nimport { IComptroller } from \"../interfaces/IComptroller.sol\";\nimport { IERC20 } from \"../utils/InitializableAbstractStrategy.sol\";\n\ncontract CompoundStrategy is AbstractCompoundStrategy {\n using SafeERC20 for IERC20;\n event SkippedWithdrawal(address asset, uint256 amount);\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @notice initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @notice Collect accumulated COMP and send to Harvester.\n */\n function collectRewardTokens()\n external\n virtual\n override\n onlyHarvester\n nonReentrant\n {\n // Claim COMP from Comptroller\n ICERC20 cToken = _getCTokenFor(assetsMapped[0]);\n IComptroller comptroller = IComptroller(cToken.comptroller());\n // Only collect from active cTokens, saves gas\n address[] memory ctokensToCollect = new address[](assetsMapped.length);\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n ctokensToCollect[i] = address(_getCTokenFor(assetsMapped[i]));\n }\n // Claim only for this strategy\n address[] memory claimers = new address[](1);\n claimers[0] = address(this);\n // Claim COMP from Comptroller. Only collect for supply, saves gas\n comptroller.claimComp(claimers, ctokensToCollect, false, true);\n // Transfer COMP to Harvester\n IERC20 rewardToken = IERC20(rewardTokenAddresses[0]);\n uint256 balance = rewardToken.balanceOf(address(this));\n emit RewardTokenCollected(\n harvesterAddress,\n rewardTokenAddresses[0],\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n\n /**\n * @notice Deposit asset into the underlying platform\n * @param _asset Address of asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit an asset into the underlying platform\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n ICERC20 cToken = _getCTokenFor(_asset);\n emit Deposit(_asset, address(cToken), _amount);\n require(cToken.mint(_amount) == 0, \"cToken mint failed\");\n }\n\n /**\n * @notice Deposit the entire balance of any supported asset in the strategy into the underlying platform\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n uint256 assetBalance = asset.balanceOf(address(this));\n if (assetBalance > 0) {\n _deposit(address(asset), assetBalance);\n }\n }\n }\n\n /**\n * @notice Withdraw an asset from the underlying platform\n * @param _recipient Address to receive withdrawn assets\n * @param _asset Address of the asset to withdraw\n * @param _amount Amount of assets to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n ICERC20 cToken = _getCTokenFor(_asset);\n // If redeeming 0 cTokens, just skip, else COMP will revert\n uint256 cTokensToRedeem = _convertUnderlyingToCToken(cToken, _amount);\n if (cTokensToRedeem == 0) {\n emit SkippedWithdrawal(_asset, _amount);\n return;\n }\n\n emit Withdrawal(_asset, address(cToken), _amount);\n require(cToken.redeemUnderlying(_amount) == 0, \"Redeem failed\");\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / cTokens\n * We need to approve the cToken and give it permission to spend the asset\n * @param _asset Address of the asset to approve. eg DAI\n * @param _pToken The pToken for the approval. eg cDAI or fDAI\n */\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n // Safe approval\n IERC20(_asset).safeApprove(_pToken, 0);\n IERC20(_asset).safeApprove(_pToken, type(uint256).max);\n }\n\n /**\n * @notice Remove all supported assets from the underlying platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n // Redeem entire balance of cToken\n ICERC20 cToken = _getCTokenFor(address(asset));\n uint256 cTokenBalance = cToken.balanceOf(address(this));\n if (cTokenBalance > 0) {\n require(cToken.redeem(cTokenBalance) == 0, \"Redeem failed\");\n uint256 assetBalance = asset.balanceOf(address(this));\n // Transfer entire balance to Vault\n asset.safeTransfer(vaultAddress, assetBalance);\n\n emit Withdrawal(address(asset), address(cToken), assetBalance);\n }\n }\n }\n\n /**\n * @notice Get the total asset value held in the underlying platform\n * This includes any interest that was generated since depositing.\n * The exchange rate between the cToken and asset gradually increases,\n * causing the cToken to be worth more corresponding asset.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n // Balance is always with token cToken decimals\n ICERC20 cToken = _getCTokenFor(_asset);\n balance = _checkBalance(cToken);\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * underlying = (cTokenAmt * exchangeRate) / 1e18\n * @param _cToken cToken for which to check balance\n * @return balance Total value of the asset in the platform\n */\n function _checkBalance(ICERC20 _cToken)\n internal\n view\n returns (uint256 balance)\n {\n // e.g. 50e8*205316390724364402565641705 / 1e18 = 1.0265..e18\n balance =\n (_cToken.balanceOf(address(this)) * _cToken.exchangeRateStored()) /\n 1e18;\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding cToken,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens() external override {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(assetsMapped[i]);\n address cToken = assetToPToken[address(asset)];\n // Safe approval\n asset.safeApprove(cToken, 0);\n asset.safeApprove(cToken, type(uint256).max);\n }\n }\n}\n" + }, + "contracts/strategies/ConvexEthMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Convex Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for the Curve OETH/ETH pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { ICurveETHPoolV1 } from \"./ICurveETHPoolV1.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\n\ncontract ConvexEthMetaStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n uint256 public constant MAX_SLIPPAGE = 1e16; // 1%, same as the Curve UI\n address public constant ETH_ADDRESS =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n // The following slots have been deprecated with immutable variables\n // slither-disable-next-line constable-states\n address private _deprecated_cvxDepositorAddress;\n // slither-disable-next-line constable-states\n address private _deprecated_cvxRewardStaker;\n // slither-disable-next-line constable-states\n uint256 private _deprecated_cvxDepositorPTokenId;\n // slither-disable-next-line constable-states\n address private _deprecated_curvePool;\n // slither-disable-next-line constable-states\n address private _deprecated_lpToken;\n // slither-disable-next-line constable-states\n address private _deprecated_oeth;\n // slither-disable-next-line constable-states\n address private _deprecated_weth;\n\n // Ordered list of pool assets\n // slither-disable-next-line constable-states\n uint128 private _deprecated_oethCoinIndex;\n // slither-disable-next-line constable-states\n uint128 private _deprecated_ethCoinIndex;\n\n // New immutable variables that must be set in the constructor\n address public immutable cvxDepositorAddress;\n IRewardStaking public immutable cvxRewardStaker;\n uint256 public immutable cvxDepositorPTokenId;\n ICurveETHPoolV1 public immutable curvePool;\n IERC20 public immutable lpToken;\n IERC20 public immutable oeth;\n IWETH9 public immutable weth;\n\n // Ordered list of pool assets\n uint128 public constant oethCoinIndex = 1;\n uint128 public constant ethCoinIndex = 0;\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier only works on functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balancesBefore = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffBefore = int256(balancesBefore[ethCoinIndex]) -\n int256(balancesBefore[oethCoinIndex]);\n\n _;\n\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balancesAfter = curvePool.get_balances();\n // diff = ETH balance - OETH balance\n int256 diffAfter = int256(balancesAfter[ethCoinIndex]) -\n int256(balancesAfter[oethCoinIndex]);\n\n if (diffBefore <= 0) {\n // If the pool was originally imbalanced in favor of OETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n }\n if (diffBefore >= 0) {\n // If the pool was originally imbalanced in favor of ETH, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n // Used to circumvent the stack too deep issue\n struct ConvexEthMetaConfig {\n address cvxDepositorAddress; //Address of the Convex depositor(AKA booster) for this pool\n address cvxRewardStakerAddress; //Address of the CVX rewards staker\n uint256 cvxDepositorPTokenId; //Pid of the pool referred to by Depositor and staker\n address oethAddress; //Address of OETH token\n address wethAddress; //Address of WETH\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n ConvexEthMetaConfig memory _convexConfig\n ) InitializableAbstractStrategy(_baseConfig) {\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveETHPoolV1(_baseConfig.platformAddress);\n\n cvxDepositorAddress = _convexConfig.cvxDepositorAddress;\n cvxRewardStaker = IRewardStaking(_convexConfig.cvxRewardStakerAddress);\n cvxDepositorPTokenId = _convexConfig.cvxDepositorPTokenId;\n oeth = IERC20(_convexConfig.oethAddress);\n weth = IWETH9(_convexConfig.wethAddress);\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. eg WETH\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets // WETH\n ) external onlyGovernor initializer {\n require(_assets.length == 1, \"Must have exactly one asset\");\n require(_assets[0] == address(weth), \"Asset not WETH\");\n\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit WETH into the Curve pool\n * @param _weth Address of Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to deposit.\n */\n function deposit(address _weth, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_weth, _amount);\n }\n\n function _deposit(address _weth, uint256 _wethAmount) internal {\n require(_wethAmount > 0, \"Must deposit something\");\n require(_weth == address(weth), \"Can only deposit WETH\");\n weth.withdraw(_wethAmount);\n\n emit Deposit(_weth, address(lpToken), _wethAmount);\n\n // Get the asset and OToken balances in the Curve pool\n uint256[2] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 oethToAdd = uint256(\n _max(\n 0,\n int256(balances[ethCoinIndex]) +\n int256(_wethAmount) -\n int256(balances[oethCoinIndex])\n )\n );\n\n /* Add so much OETH so that the pool ends up being balanced. And at minimum\n * add as much OETH as WETH and at maximum twice as much OETH.\n */\n oethToAdd = Math.max(oethToAdd, _wethAmount);\n oethToAdd = Math.min(oethToAdd, _wethAmount * 2);\n\n /* Mint OETH with a strategy that attempts to contribute to stability of OETH/WETH pool. Try\n * to mint so much OETH that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OETH minted will always be at least equal or greater\n * to WETH amount deployed. And never larger than twice the WETH amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(oethToAdd);\n\n emit Deposit(address(oeth), address(lpToken), oethToAdd);\n\n uint256[2] memory _amounts;\n _amounts[ethCoinIndex] = _wethAmount;\n _amounts[oethCoinIndex] = oethToAdd;\n\n uint256 valueInLpTokens = (_wethAmount + oethToAdd).divPrecisely(\n curvePool.get_virtual_price()\n );\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Do the deposit to the Curve pool\n // slither-disable-next-line arbitrary-send\n uint256 lpDeposited = curvePool.add_liquidity{ value: _wethAmount }(\n _amounts,\n minMintAmount\n );\n\n // Deposit the Curve pool's LP tokens into the Convex rewards pool\n require(\n IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n lpDeposited,\n true // Deposit with staking\n ),\n \"Depositing LP to Convex not successful\"\n );\n }\n\n /**\n * @notice Deposit the strategy's entire balance of WETH into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = weth.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(weth), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _weth Address of the Wrapped ETH (WETH) contract.\n * @param _amount Amount of WETH to withdraw.\n */\n function withdraw(\n address _recipient,\n address _weth,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Invalid amount\");\n require(_weth == address(weth), \"Can only withdraw WETH\");\n\n emit Withdrawal(_weth, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(_amount);\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough WETH on balanced removal\n */\n uint256[2] memory _minWithdrawalAmounts = [uint256(0), uint256(0)];\n _minWithdrawalAmounts[ethCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OETH and any that was left in the strategy\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n\n // Transfer WETH to the recipient\n weth.deposit{ value: _amount }();\n require(\n weth.transfer(_recipient, _amount),\n \"Transfer of WETH not successful\"\n );\n }\n\n function calcTokenToBurn(uint256 _wethAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much WETH\n * we want we can determine how much of OETH we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolWETHBalance = curvePool.balances(ethCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolWETHBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_wethAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all ETH and OETH from the Curve pool, burn the OETH,\n * convert the ETH to WETH and transfer to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = cvxRewardStaker.balanceOf(address(this));\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[2] memory minWithdrawAmounts = [uint256(0), uint256(0)];\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(\n lpToken.balanceOf(address(this)),\n minWithdrawAmounts\n );\n\n // Burn all OETH\n uint256 oethToBurn = oeth.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n // Get the strategy contract's ether balance.\n // This includes all that was removed from the Curve pool and\n // any ether that was sitting in the strategy contract before the removal.\n uint256 ethBalance = address(this).balance;\n // Convert all the strategy contract's ether to WETH and transfer to the vault.\n weth.deposit{ value: ethBalance }();\n require(\n weth.transfer(vaultAddress, ethBalance),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethBalance);\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[2] memory amounts = [uint256(0), uint256(0)];\n amounts[oethCoinIndex] = _oTokens;\n\n // Convert OETH to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n\n // Deposit the Curve pool LP tokens to the Convex rewards pool\n require(\n IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n lpDeposited,\n true // Deposit with staking\n ),\n \"Failed to Deposit LP to Convex\"\n );\n\n emit Deposit(address(oeth), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough ETH.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove OTokens from the Curve pool\n uint256 oethToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n oethCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(oethToBurn);\n\n emit Withdrawal(address(oeth), address(lpToken), oethToBurn);\n }\n\n /**\n * @notice One-sided remove of ETH from the Curve pool, convert to WETH\n * and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many ETH.\n * The OToken/Asset, eg OETH/ETH, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for ETH.\n * @dev Curve pool LP tokens is used rather than WETH assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of ETH. Curve's `calc_token_amount` functioun does not include fees.\n * A 3rd party libary can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * caclulate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Convex and remove ETH from the Curve pool\n uint256 ethAmount = _withdrawAndRemoveFromPool(_lpTokens, ethCoinIndex);\n\n // Convert ETH to WETH and transfer to the vault\n weth.deposit{ value: ethAmount }();\n require(\n weth.transfer(vaultAddress, ethAmount),\n \"Transfer of WETH not successful\"\n );\n\n emit Withdrawal(address(weth), address(lpToken), ethAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the Convex pool and\n * do a one-sided remove of ETH or OETH from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the Convex pool.\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = ETH, 1 = OETH.\n * @return coinsRemoved The amount of ETH or OETH removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Convex pool\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to ETH value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n // Apply slippage to ETH value\n uint256 minAmount = valueInEth.mulTruncate(\n uint256(1e18) - MAX_SLIPPAGE\n );\n\n // Remove just the ETH from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV and CVX rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n cvxRewardStaker.getReward();\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _wethAmount) internal {\n // withdraw and unwrap with claim takes back the lpTokens\n // and also collects the rewards for deposit\n cvxRewardStaker.withdrawAndUnwrap(_wethAmount, true);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(weth), \"Unsupported asset\");\n\n // Eth balance needed here for the balance check that happens from vault during depositing.\n balance = address(this).balance;\n uint256 lpTokens = cvxRewardStaker.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += (lpTokens * curvePool.get_virtual_price()) / 1e18;\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(weth);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @notice Accept unwrapped WETH\n */\n receive() external payable {}\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OETH (required for adding liquidity)\n // No approval is needed for ETH\n // slither-disable-next-line unused-return\n oeth.approve(platformAddress, type(uint256).max);\n\n // Approve Convex deposit contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Convex rewards pool\n // slither-disable-next-line unused-return\n lpToken.approve(cvxDepositorAddress, type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/ConvexGeneralizedMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { AbstractConvexMetaStrategy } from \"./AbstractConvexMetaStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract ConvexGeneralizedMetaStrategy is AbstractConvexMetaStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /* Take 3pool LP and deposit it to metapool. Take the LP from metapool\n * and deposit them to Convex.\n */\n function _lpDepositAll() internal override {\n IERC20 threePoolLp = IERC20(pTokenAddress);\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 threePoolLpBalance = threePoolLp.balanceOf(address(this));\n uint256 curve3PoolVirtualPrice = curvePool.get_virtual_price();\n uint256 threePoolLpDollarValue = threePoolLpBalance.mulTruncate(\n curve3PoolVirtualPrice\n );\n\n uint256[2] memory _amounts = [0, threePoolLpBalance];\n\n uint256 metapoolVirtualPrice = metapool.get_virtual_price();\n /**\n * First convert all the deposited tokens to dollar values,\n * then divide by virtual price to convert to metapool LP tokens\n * and apply the max slippage\n */\n uint256 minReceived = threePoolLpDollarValue\n .divPrecisely(metapoolVirtualPrice)\n .mulTruncate(uint256(1e18) - MAX_SLIPPAGE);\n\n uint256 metapoolLp = metapool.add_liquidity(_amounts, minReceived);\n\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n metapoolLp,\n true // Deposit with staking\n );\n\n require(success, \"Failed to deposit to Convex\");\n }\n\n /**\n * Withdraw the specified amount of tokens from the gauge. And use all the resulting tokens\n * to remove liquidity from metapool\n * @param num3CrvTokens Number of Convex 3pool LP tokens to withdraw from metapool\n */\n function _lpWithdraw(uint256 num3CrvTokens) internal override {\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n uint256 requiredMetapoolLpTokens = _calcCurveMetaTokenAmount(\n crvCoinIndex,\n num3CrvTokens\n );\n\n require(\n requiredMetapoolLpTokens <= gaugeTokens,\n string(\n bytes.concat(\n bytes(\"Attempting to withdraw \"),\n bytes(Strings.toString(requiredMetapoolLpTokens)),\n bytes(\", metapoolLP but only \"),\n bytes(Strings.toString(gaugeTokens)),\n bytes(\" available.\")\n )\n )\n );\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards for deposit\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n requiredMetapoolLpTokens,\n true\n );\n\n if (requiredMetapoolLpTokens > 0) {\n // slither-disable-next-line unused-return\n metapool.remove_liquidity_one_coin(\n requiredMetapoolLpTokens,\n int128(crvCoinIndex),\n num3CrvTokens\n );\n }\n }\n\n function _lpWithdrawAll() internal override {\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n gaugeTokens,\n true\n );\n\n if (gaugeTokens > 0) {\n uint256 burnDollarAmount = gaugeTokens.mulTruncate(\n metapool.get_virtual_price()\n );\n uint256 curve3PoolExpected = burnDollarAmount.divPrecisely(\n ICurvePool(platformAddress).get_virtual_price()\n );\n\n // Always withdraw all of the available metapool LP tokens (similar to how we always deposit all)\n // slither-disable-next-line unused-return\n metapool.remove_liquidity_one_coin(\n gaugeTokens,\n int128(crvCoinIndex),\n curve3PoolExpected -\n curve3PoolExpected.mulTruncate(maxWithdrawalSlippage)\n );\n }\n }\n}\n" + }, + "contracts/strategies/ConvexOUSDMetaStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { AbstractConvexMetaStrategy } from \"./AbstractConvexMetaStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract ConvexOUSDMetaStrategy is AbstractConvexMetaStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /* Take 3pool LP and mint the corresponding amount of ousd. Deposit and stake that to\n * ousd Curve Metapool. Take the LP from metapool and deposit them to Convex.\n */\n function _lpDepositAll() internal override {\n ICurvePool curvePool = ICurvePool(platformAddress);\n\n uint256 threePoolLpBalance = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n uint256 curve3PoolVirtualPrice = curvePool.get_virtual_price();\n uint256 threePoolLpDollarValue = threePoolLpBalance.mulTruncate(\n curve3PoolVirtualPrice\n );\n\n // safe to cast since min value is at least 0\n uint256 ousdToAdd = uint256(\n _max(\n 0,\n int256(\n metapool.balances(crvCoinIndex).mulTruncate(\n curve3PoolVirtualPrice\n )\n ) -\n int256(metapool.balances(mainCoinIndex)) +\n int256(threePoolLpDollarValue)\n )\n );\n\n /* Add so much OUSD so that the pool ends up being balanced. And at minimum\n * add twice as much OUSD as 3poolLP and at maximum at twice as\n * much OUSD.\n */\n ousdToAdd = Math.max(ousdToAdd, threePoolLpDollarValue);\n ousdToAdd = Math.min(ousdToAdd, threePoolLpDollarValue * 2);\n\n /* Mint OUSD with a strategy that attempts to contribute to stability of OUSD metapool. Try\n * to mint so much OUSD that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OUSD minted will always be at least equal or greater\n * to stablecoin(DAI, USDC, USDT) amount of 3CRVLP deployed. And never larger than twice the\n * stablecoin amount of 3CRVLP deployed even if it would have a further beneficial effect\n * on pool stability.\n */\n if (ousdToAdd > 0) {\n IVault(vaultAddress).mintForStrategy(ousdToAdd);\n }\n\n uint256[2] memory _amounts = [ousdToAdd, threePoolLpBalance];\n\n uint256 metapoolVirtualPrice = metapool.get_virtual_price();\n /**\n * First convert all the deposited tokens to dollar values,\n * then divide by virtual price to convert to metapool LP tokens\n * and apply the max slippage\n */\n uint256 minReceived = (ousdToAdd + threePoolLpDollarValue)\n .divPrecisely(metapoolVirtualPrice)\n .mulTruncate(uint256(1e18) - MAX_SLIPPAGE);\n\n uint256 metapoolLp = metapool.add_liquidity(_amounts, minReceived);\n\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n metapoolLp,\n true // Deposit with staking\n );\n\n require(success, \"Failed to deposit to Convex\");\n }\n\n /**\n * Withdraw the specified amount of tokens from the gauge. And use all the resulting tokens\n * to remove liquidity from metapool\n * @param num3CrvTokens Number of 3CRV tokens to withdraw from metapool\n */\n function _lpWithdraw(uint256 num3CrvTokens) internal override {\n ICurvePool curvePool = ICurvePool(platformAddress);\n /* The rate between coins in the metapool determines the rate at which metapool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much 3crvLp\n * we want we can determine how much of OUSD we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 crvPoolBalance = metapool.balances(crvCoinIndex);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * metapool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * metapoolLPToken.totalSupply()) / crvPoolBalance;\n // simplifying below to: `uint256 diff = (num3CrvTokens - 1) * k` causes loss of precision\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = crvPoolBalance * k -\n (crvPoolBalance - num3CrvTokens - 1) * k;\n uint256 lpToBurn = diff / 1e36;\n\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n require(\n lpToBurn <= gaugeTokens,\n string(\n bytes.concat(\n bytes(\"Attempting to withdraw \"),\n bytes(Strings.toString(lpToBurn)),\n bytes(\", metapoolLP but only \"),\n bytes(Strings.toString(gaugeTokens)),\n bytes(\" available.\")\n )\n )\n );\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards for deposit\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n lpToBurn,\n true\n );\n\n // calculate the min amount of OUSD expected for the specified amount of LP tokens\n uint256 minOUSDAmount = lpToBurn.mulTruncate(\n metapool.get_virtual_price()\n ) -\n num3CrvTokens.mulTruncate(curvePool.get_virtual_price()) -\n 1;\n\n // withdraw the liquidity from metapool\n uint256[2] memory _removedAmounts = metapool.remove_liquidity(\n lpToBurn,\n [minOUSDAmount, num3CrvTokens]\n );\n\n IVault(vaultAddress).burnForStrategy(_removedAmounts[mainCoinIndex]);\n }\n\n function _lpWithdrawAll() internal override {\n IERC20 metapoolErc20 = IERC20(address(metapool));\n uint256 gaugeTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n gaugeTokens,\n true\n );\n\n uint256[2] memory _minAmounts = [uint256(0), uint256(0)];\n uint256[2] memory _removedAmounts = metapool.remove_liquidity(\n metapoolErc20.balanceOf(address(this)),\n _minAmounts\n );\n\n IVault(vaultAddress).burnForStrategy(_removedAmounts[mainCoinIndex]);\n }\n}\n" + }, + "contracts/strategies/ConvexStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Convex Strategy\n * @notice Investment strategy for investing stablecoins via Curve 3Pool\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { ICurvePool } from \"./ICurvePool.sol\";\nimport { IRewardStaking } from \"./IRewardStaking.sol\";\nimport { IConvexDeposits } from \"./IConvexDeposits.sol\";\nimport { IERC20, AbstractCurveStrategy, InitializableAbstractStrategy } from \"./AbstractCurveStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { Helpers } from \"../utils/Helpers.sol\";\n\n/*\n * IMPORTANT(!) If ConvexStrategy needs to be re-deployed, it requires new\n * proxy contract with fresh storage slots. Changes in `AbstractCurveStrategy`\n * storage slots would break existing implementation.\n *\n * Remove this notice if ConvexStrategy is re-deployed\n */\ncontract ConvexStrategy is AbstractCurveStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n\n address internal cvxDepositorAddress;\n address internal cvxRewardStakerAddress;\n // slither-disable-next-line constable-states\n address private _deprecated_cvxRewardTokenAddress;\n uint256 internal cvxDepositorPTokenId;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV & CVX\n * @param _assets Addresses of supported assets. MUST be passed in the same\n * order as returned by coins on the pool contract, i.e.\n * DAI, USDC, USDT\n * @param _pTokens Platform Token corresponding addresses\n * @param _cvxDepositorAddress Address of the Convex depositor(AKA booster) for this pool\n * @param _cvxRewardStakerAddress Address of the CVX rewards staker\n * @param _cvxDepositorPTokenId Pid of the pool referred to by Depositor and staker\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV + CVX\n address[] calldata _assets,\n address[] calldata _pTokens,\n address _cvxDepositorAddress,\n address _cvxRewardStakerAddress,\n uint256 _cvxDepositorPTokenId\n ) external onlyGovernor initializer {\n require(_assets.length == 3, \"Must have exactly three assets\");\n // Should be set prior to abstract initialize call otherwise\n // abstractSetPToken calls will fail\n cvxDepositorAddress = _cvxDepositorAddress;\n cvxRewardStakerAddress = _cvxRewardStakerAddress;\n cvxDepositorPTokenId = _cvxDepositorPTokenId;\n pTokenAddress = _pTokens[0];\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n _approveBase();\n }\n\n function _lpDepositAll() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // Deposit with staking\n bool success = IConvexDeposits(cvxDepositorAddress).deposit(\n cvxDepositorPTokenId,\n pToken.balanceOf(address(this)),\n true\n );\n require(success, \"Failed to deposit to Convex\");\n }\n\n function _lpWithdraw(uint256 numCrvTokens) internal override {\n uint256 gaugePTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n\n // Not enough in this contract or in the Gauge, can't proceed\n require(numCrvTokens > gaugePTokens, \"Insufficient 3CRV balance\");\n\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards to this\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n numCrvTokens,\n true\n );\n }\n\n function _lpWithdrawAll() internal override {\n // withdraw and unwrap with claim takes back the lpTokens and also collects the rewards to this\n IRewardStaking(cvxRewardStakerAddress).withdrawAndUnwrap(\n IRewardStaking(cvxRewardStakerAddress).balanceOf(address(this)),\n true\n );\n }\n\n function _approveBase() internal override {\n IERC20 pToken = IERC20(pTokenAddress);\n // 3Pool for LP token (required for removing liquidity)\n pToken.safeApprove(platformAddress, 0);\n pToken.safeApprove(platformAddress, type(uint256).max);\n // Gauge for LP token\n pToken.safeApprove(cvxDepositorAddress, 0);\n pToken.safeApprove(cvxDepositorAddress, type(uint256).max);\n }\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n public\n view\n override\n returns (uint256 balance)\n {\n require(assetToPToken[_asset] != address(0), \"Unsupported asset\");\n // LP tokens in this contract. This should generally be nothing as we\n // should always stake the full balance in the Gauge, but include for\n // safety\n uint256 contractPTokens = IERC20(pTokenAddress).balanceOf(\n address(this)\n );\n uint256 gaugePTokens = IRewardStaking(cvxRewardStakerAddress).balanceOf(\n address(this)\n );\n uint256 totalPTokens = contractPTokens + gaugePTokens;\n\n ICurvePool curvePool = ICurvePool(platformAddress);\n if (totalPTokens > 0) {\n uint256 virtual_price = curvePool.get_virtual_price();\n uint256 value = (totalPTokens * virtual_price) / 1e18;\n uint256 assetDecimals = Helpers.getDecimals(_asset);\n balance = value.scaleBy(assetDecimals, 18) / 3;\n }\n }\n\n /**\n * @dev Collect accumulated CRV and CVX and send to Vault.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV and CVX\n IRewardStaking(cvxRewardStakerAddress).getReward();\n _collectRewardTokens();\n }\n}\n" + }, + "contracts/strategies/CurveAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Curve Automated Market Maker (AMO) Strategy\n * @notice AMO strategy for a Curve pool using an OToken.\n * @author Origin Protocol Inc\n */\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { ICurveStableSwapNG } from \"../interfaces/ICurveStableSwapNG.sol\";\nimport { ICurveLiquidityGaugeV6 } from \"../interfaces/ICurveLiquidityGaugeV6.sol\";\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\nimport { ICurveMinter } from \"../interfaces/ICurveMinter.sol\";\n\ncontract CurveAMOStrategy is InitializableAbstractStrategy {\n using SafeCast for uint256;\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n /**\n * @dev a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n // New immutable variables that must be set in the constructor\n /**\n * @notice Address of the hard asset (weth, usdt, usdc).\n */\n IERC20 public immutable hardAsset;\n\n /**\n * @notice Address of the OTOKEN token contract.\n */\n IERC20 public immutable oToken;\n\n /**\n * @notice Address of the LP (Liquidity Provider) token contract.\n */\n IERC20 public immutable lpToken;\n\n /**\n * @notice Address of the Curve StableSwap NG pool contract.\n */\n ICurveStableSwapNG public immutable curvePool;\n\n /**\n * @notice Address of the Curve X-Chain Liquidity Gauge contract.\n */\n ICurveLiquidityGaugeV6 public immutable gauge;\n\n /**\n * @notice Address of the Curve Minter contract.\n */\n ICurveMinter public immutable minter;\n\n /**\n * @notice Index of the OTOKEN and hardAsset in the Curve pool.\n */\n uint128 public immutable otokenCoinIndex;\n uint128 public immutable hardAssetCoinIndex;\n\n /**\n * @notice Decimals of the OTOKEN and hardAsset.\n */\n uint8 public immutable decimalsOToken;\n uint8 public immutable decimalsHardAsset;\n\n /**\n * @notice Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n uint256 public maxSlippage;\n\n event MaxSlippageUpdated(uint256 newMaxSlippage);\n\n /**\n * @dev Verifies that the caller is the Strategist.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Checks the Curve pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do a single sided add or remove.\n * The standard deposit function adds to both sides of the pool in a way that\n * the pool's balance is not worsened.\n * Withdrawals are proportional so doesn't change the pools asset balance.\n */\n modifier improvePoolBalance() {\n // Get the hard asset and OToken balances in the Curve pool\n uint256[] memory balancesBefore = curvePool.get_balances();\n // diff = hardAsset balance - OTOKEN balance\n int256 diffBefore = (\n balancesBefore[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() - balancesBefore[otokenCoinIndex].toInt256();\n\n _;\n\n // Get the hard asset and OToken balances in the Curve pool\n uint256[] memory balancesAfter = curvePool.get_balances();\n // diff = hardAsset balance - OTOKEN balance\n int256 diffAfter = (\n balancesAfter[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() - balancesAfter[otokenCoinIndex].toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OTOKEN, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"OTokens overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of hardAsset, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"Assets overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _otoken,\n address _hardAsset,\n address _gauge,\n address _minter\n ) InitializableAbstractStrategy(_baseConfig) {\n lpToken = IERC20(_baseConfig.platformAddress);\n curvePool = ICurveStableSwapNG(_baseConfig.platformAddress);\n minter = ICurveMinter(_minter);\n\n oToken = IERC20(_otoken);\n hardAsset = IERC20(_hardAsset);\n gauge = ICurveLiquidityGaugeV6(_gauge);\n decimalsHardAsset = IBasicToken(_hardAsset).decimals();\n decimalsOToken = IBasicToken(_otoken).decimals();\n\n (hardAssetCoinIndex, otokenCoinIndex) = curvePool.coins(0) == _hardAsset\n ? (0, 1)\n : (1, 0);\n require(\n curvePool.coins(otokenCoinIndex) == _otoken &&\n curvePool.coins(hardAssetCoinIndex) == _hardAsset,\n \"Invalid coin indexes\"\n );\n require(gauge.lp_token() == address(curvePool), \"Invalid pool\");\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as Curve strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Address of CRV\n * @param _maxSlippage Maximum slippage allowed for adding/removing liquidity from the Curve pool.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses, // CRV\n uint256 _maxSlippage\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = address(curvePool);\n\n address[] memory _assets = new address[](1);\n _assets[0] = address(hardAsset);\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n _approveBase();\n _setMaxSlippage(_maxSlippage);\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit hard asset into the Curve pool\n * @param _hardAsset Address of hard asset contract.\n * @param _amount Amount of hard asset to deposit.\n */\n function deposit(address _hardAsset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_hardAsset, _amount);\n }\n\n function _deposit(address _hardAsset, uint256 _hardAssetAmount) internal {\n require(_hardAssetAmount > 0, \"Must deposit something\");\n require(_hardAsset == address(hardAsset), \"Unsupported asset\");\n\n emit Deposit(_hardAsset, address(lpToken), _hardAssetAmount);\n uint256 scaledHardAssetAmount = _hardAssetAmount.scaleBy(\n decimalsOToken,\n decimalsHardAsset\n );\n\n // Get the asset and OToken balances in the Curve pool\n uint256[] memory balances = curvePool.get_balances();\n // safe to cast since min value is at least 0\n uint256 otokenToAdd = uint256(\n _max(\n 0,\n (\n balances[hardAssetCoinIndex].scaleBy(\n decimalsOToken,\n decimalsHardAsset\n )\n ).toInt256() +\n scaledHardAssetAmount.toInt256() -\n balances[otokenCoinIndex].toInt256()\n )\n );\n\n /* Add so much OTOKEN so that the pool ends up being balanced. And at minimum\n * add as much OTOKEN as hard asset and at maximum twice as much OTOKEN.\n */\n otokenToAdd = Math.max(otokenToAdd, scaledHardAssetAmount);\n otokenToAdd = Math.min(otokenToAdd, scaledHardAssetAmount * 2);\n\n /* Mint OTOKEN with a strategy that attempts to contribute to stability of OTOKEN/hardAsset pool. Try\n * to mint so much OTOKEN that after deployment of liquidity pool ends up being balanced.\n *\n * To manage unpredictability minimal OTOKEN minted will always be at least equal or greater\n * to hardAsset amount deployed. And never larger than twice the hardAsset amount deployed even if\n * it would have a further beneficial effect on pool stability.\n */\n IVault(vaultAddress).mintForStrategy(otokenToAdd);\n\n emit Deposit(address(oToken), address(lpToken), otokenToAdd);\n\n uint256[] memory _amounts = new uint256[](2);\n _amounts[hardAssetCoinIndex] = _hardAssetAmount;\n _amounts[otokenCoinIndex] = otokenToAdd;\n\n uint256 valueInLpTokens = (scaledHardAssetAmount + otokenToAdd)\n .divPrecisely(curvePool.get_virtual_price());\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Do the deposit to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(_amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool's LP tokens into the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n /**\n * @notice Deposit the strategy's entire balance of hardAsset into the Curve pool\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 balance = hardAsset.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(hardAsset), balance);\n }\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw hardAsset and OTOKEN from the Curve pool, burn the OTOKEN,\n * transfer hardAsset to the recipient.\n * @param _recipient Address to receive withdrawn asset which is normally the Vault.\n * @param _hardAsset Address of the hardAsset contract.\n * @param _amount Amount of hardAsset to withdraw.\n */\n function withdraw(\n address _recipient,\n address _hardAsset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(\n _hardAsset == address(hardAsset),\n \"Can only withdraw hard asset\"\n );\n\n emit Withdrawal(_hardAsset, address(lpToken), _amount);\n\n uint256 requiredLpTokens = calcTokenToBurn(\n _amount.scaleBy(decimalsOToken, decimalsHardAsset)\n );\n\n _lpWithdraw(requiredLpTokens);\n\n /* math in requiredLpTokens should correctly calculate the amount of LP to remove\n * in that the strategy receives enough hardAsset on balanced removal\n */\n uint256[] memory _minWithdrawalAmounts = new uint256[](2);\n _minWithdrawalAmounts[hardAssetCoinIndex] = _amount;\n // slither-disable-next-line unused-return\n curvePool.remove_liquidity(requiredLpTokens, _minWithdrawalAmounts);\n\n // Burn all the removed OTOKEN and any that was left in the strategy\n uint256 otokenToBurn = oToken.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n\n // Transfer hardAsset to the recipient\n hardAsset.safeTransfer(_recipient, _amount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n }\n\n function calcTokenToBurn(uint256 _hardAssetAmount)\n internal\n view\n returns (uint256 lpToBurn)\n {\n /* The rate between coins in the pool determines the rate at which pool returns\n * tokens when doing balanced removal (remove_liquidity call). And by knowing how much hardAsset\n * we want we can determine how much of OTOKEN we receive by removing liquidity.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognisant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n uint256 poolHardAssetBalance = curvePool\n .balances(hardAssetCoinIndex)\n .scaleBy(decimalsOToken, decimalsHardAsset);\n /* K is multiplied by 1e36 which is used for higher precision calculation of required\n * pool LP tokens. Without it the end value can have rounding errors up to precision of\n * 10 digits. This way we move the decimal point by 36 places when doing the calculation\n * and again by 36 places when we are done with it.\n */\n uint256 k = (1e36 * lpToken.totalSupply()) / poolHardAssetBalance;\n // prettier-ignore\n // slither-disable-next-line divide-before-multiply\n uint256 diff = (_hardAssetAmount + 1) * k;\n lpToBurn = diff / 1e36;\n }\n\n /**\n * @notice Remove all hardAsset and OTOKEN from the Curve pool, burn the OTOKEN,\n * transfer hardAsset to the Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 gaugeTokens = gauge.balanceOf(address(this));\n _lpWithdraw(gaugeTokens);\n\n // Withdraws are proportional to assets held by 3Pool\n uint256[] memory minWithdrawAmounts = new uint256[](2);\n\n // Check balance of LP tokens in the strategy, if 0 return\n uint256 lpBalance = lpToken.balanceOf(address(this));\n\n // Remove liquidity\n // slither-disable-next-line unused-return\n if (lpBalance > 0) {\n curvePool.remove_liquidity(lpBalance, minWithdrawAmounts);\n }\n\n // Burn all OTOKEN\n uint256 otokenToBurn = oToken.balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n // Get the strategy contract's hardAsset balance.\n // This includes all that was removed from the Curve pool and\n // any hardAsset that was sitting in the strategy contract before the removal.\n uint256 hardAssetBalance = hardAsset.balanceOf(address(this));\n hardAsset.safeTransfer(vaultAddress, hardAssetBalance);\n\n if (hardAssetBalance > 0)\n emit Withdrawal(\n address(hardAsset),\n address(lpToken),\n hardAssetBalance\n );\n if (otokenToBurn > 0)\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n }\n\n /***************************************\n Curve pool Rebalancing\n ****************************************/\n\n /**\n * @notice Mint OTokens and one-sided add to the Curve pool.\n * This is used when the Curve pool does not have enough OTokens and too many hardAsset.\n * The OToken/Asset, eg OTOKEN/hardAsset, price with increase.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is increased.\n * The asset value of the strategy and vault is increased.\n * @param _oTokens The amount of OTokens to be minted and added to the pool.\n */\n function mintAndAddOTokens(uint256 _oTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n IVault(vaultAddress).mintForStrategy(_oTokens);\n\n uint256[] memory amounts = new uint256[](2);\n amounts[otokenCoinIndex] = _oTokens;\n\n // Convert OTOKEN to Curve pool LP tokens\n uint256 valueInLpTokens = (_oTokens).divPrecisely(\n curvePool.get_virtual_price()\n );\n // Apply slippage to LP tokens\n uint256 minMintAmount = valueInLpTokens.mulTruncate(\n uint256(1e18) - maxSlippage\n );\n\n // Add the minted OTokens to the Curve pool\n uint256 lpDeposited = curvePool.add_liquidity(amounts, minMintAmount);\n require(lpDeposited >= minMintAmount, \"Min LP amount error\");\n\n // Deposit the Curve pool LP tokens to the Curve gauge\n gauge.deposit(lpDeposited);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Deposit(address(oToken), address(lpToken), _oTokens);\n }\n\n /**\n * @notice One-sided remove of OTokens from the Curve pool which are then burned.\n * This is used when the Curve pool has too many OTokens and not enough hardAsset.\n * The amount of assets in the vault is unchanged.\n * The total supply of OTokens is reduced.\n * The asset value of the strategy and vault is reduced.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for OTokens.\n */\n function removeAndBurnOTokens(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from gauge and remove OTokens from the Curve pool\n uint256 otokenToBurn = _withdrawAndRemoveFromPool(\n _lpTokens,\n otokenCoinIndex\n );\n\n // The vault burns the OTokens from this strategy\n IVault(vaultAddress).burnForStrategy(otokenToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(oToken), address(lpToken), otokenToBurn);\n }\n\n /**\n * @notice One-sided remove of hardAsset from the Curve pool and transfer to the vault.\n * This is used when the Curve pool does not have enough OTokens and too many hardAsset.\n * The OToken/Asset, eg OTOKEN/hardAsset, price with decrease.\n * The amount of assets in the vault increases.\n * The total supply of OTokens does not change.\n * The asset value of the strategy reduces.\n * The asset value of the vault should be close to the same.\n * @param _lpTokens The amount of Curve pool LP tokens to be burned for hardAsset.\n * @dev Curve pool LP tokens is used rather than hardAsset assets as Curve does not\n * have a way to accurately calculate the amount of LP tokens for a required\n * amount of hardAsset. Curve's `calc_token_amount` function does not include fees.\n * A 3rd party library can be used that takes into account the fees, but this\n * is a gas intensive process. It's easier for the trusted strategist to\n * calculate the amount of Curve pool LP tokens required off-chain.\n */\n function removeOnlyAssets(uint256 _lpTokens)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n {\n // Withdraw Curve pool LP tokens from Curve gauge and remove hardAsset from the Curve pool\n uint256 hardAssetAmount = _withdrawAndRemoveFromPool(\n _lpTokens,\n hardAssetCoinIndex\n );\n\n // Transfer hardAsset to the vault\n hardAsset.safeTransfer(vaultAddress, hardAssetAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n emit Withdrawal(address(hardAsset), address(lpToken), hardAssetAmount);\n }\n\n /**\n * @dev Remove Curve pool LP tokens from the gauge and\n * do a one-sided remove of hardAsset or OTOKEN from the Curve pool.\n * @param _lpTokens The amount of Curve pool LP tokens to be removed from the gauge\n * @param coinIndex The index of the coin to be removed from the Curve pool. 0 = hardAsset, 1 = OTOKEN.\n * @return coinsRemoved The amount of hardAsset or OTOKEN removed from the Curve pool.\n */\n function _withdrawAndRemoveFromPool(uint256 _lpTokens, uint128 coinIndex)\n internal\n returns (uint256 coinsRemoved)\n {\n // Withdraw Curve pool LP tokens from Curve gauge\n _lpWithdraw(_lpTokens);\n\n // Convert Curve pool LP tokens to hardAsset value\n uint256 valueInEth = _lpTokens.mulTruncate(\n curvePool.get_virtual_price()\n );\n\n if (coinIndex == hardAssetCoinIndex) {\n valueInEth = valueInEth.scaleBy(decimalsHardAsset, decimalsOToken);\n }\n\n // Apply slippage to hardAsset value\n uint256 minAmount = valueInEth.mulTruncate(uint256(1e18) - maxSlippage);\n\n // Remove just the hardAsset from the Curve pool\n coinsRemoved = curvePool.remove_liquidity_one_coin(\n _lpTokens,\n int128(coinIndex),\n minAmount,\n address(this)\n );\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOtokenSupply = oToken.totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOtokenSupply) <\n SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Collect accumulated CRV (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect CRV rewards from inflation\n minter.mint(address(gauge));\n\n // Collect extra gauge rewards (outside of CRV)\n gauge.claim_rewards();\n\n _collectRewardTokens();\n }\n\n function _lpWithdraw(uint256 _lpAmount) internal {\n require(\n gauge.balanceOf(address(this)) >= _lpAmount,\n \"Insufficient LP tokens\"\n );\n // withdraw lp tokens from the gauge without claiming rewards\n gauge.withdraw(_lpAmount);\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == address(hardAsset), \"Unsupported asset\");\n\n // hardAsset balance needed here for the balance check that happens from vault during depositing.\n balance = hardAsset.balanceOf(address(this));\n uint256 lpTokens = gauge.balanceOf(address(this));\n if (lpTokens > 0) {\n balance += ((lpTokens * curvePool.get_virtual_price()) / 1e18)\n .scaleBy(decimalsHardAsset, decimalsOToken);\n }\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == address(hardAsset);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Sets the maximum slippage allowed for any swap/liquidity operation\n * @param _maxSlippage Maximum slippage allowed, 1e18 = 100%.\n */\n function setMaxSlippage(uint256 _maxSlippage) external onlyGovernor {\n _setMaxSlippage(_maxSlippage);\n }\n\n function _setMaxSlippage(uint256 _maxSlippage) internal {\n require(_maxSlippage <= 5e16, \"Slippage must be less than 100%\");\n maxSlippage = _maxSlippage;\n emit MaxSlippageUpdated(_maxSlippage);\n }\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n /**\n * @dev Since we are unwrapping WETH before depositing it to Curve\n * there is no need to set an approval for WETH on the Curve\n * pool\n * @param _asset Address of the asset\n * @param _pToken Address of the Curve LP token\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve Curve pool for OTOKEN (required for adding liquidity)\n // slither-disable-next-line unused-return\n oToken.approve(platformAddress, type(uint256).max);\n\n // Approve Curve pool for hardAsset (required for adding liquidity)\n // slither-disable-next-line unused-return\n hardAsset.safeApprove(platformAddress, type(uint256).max);\n\n // Approve Curve gauge contract to transfer Curve pool LP tokens\n // This is needed for deposits if Curve pool LP tokens into the Curve gauge.\n // slither-disable-next-line unused-return\n lpToken.approve(address(gauge), type(uint256).max);\n }\n\n /**\n * @dev Returns the largest of two numbers int256 version\n */\n function _max(int256 a, int256 b) internal pure returns (int256) {\n return a >= b ? a : b;\n }\n}\n" + }, + "contracts/strategies/CurvePoolBooster.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Strategizable } from \"../governance/Strategizable.sol\";\nimport { ICampaignRemoteManager } from \"../interfaces/ICampaignRemoteManager.sol\";\n\n/// @title CurvePoolBooster\n/// @author Origin Protocol\n/// @notice Contract to manage interactions with VotemarketV2 for a dedicated Curve pool/gauge.\ncontract CurvePoolBooster is Initializable, Strategizable {\n using SafeERC20 for IERC20;\n\n ////////////////////////////////////////////////////\n /// --- CONSTANTS && IMMUTABLES\n ////////////////////////////////////////////////////\n /// @notice Base fee for the contract, 100%\n uint16 public constant FEE_BASE = 10_000;\n\n /// @notice Address of the gauge to manage\n address public immutable gauge;\n\n /// @notice Address of the reward token\n address public immutable rewardToken;\n\n /// @notice Chain id of the target chain\n uint256 public immutable targetChainId;\n\n ////////////////////////////////////////////////////\n /// --- STORAGE\n ////////////////////////////////////////////////////\n /// @notice Fee in FEE_BASE unit payed when managing campaign.\n uint16 public fee;\n\n /// @notice Address of the fee collector\n address public feeCollector;\n\n /// @notice Address of the campaignRemoteManager linked to VotemarketV2\n address public campaignRemoteManager;\n\n /// @notice Address of votemarket in L2\n address public votemarket;\n\n /// @notice Id of the campaign created\n uint256 public campaignId;\n\n ////////////////////////////////////////////////////\n /// --- EVENTS\n ////////////////////////////////////////////////////\n event FeeUpdated(uint16 newFee);\n event FeeCollected(address feeCollector, uint256 feeAmount);\n event FeeCollectorUpdated(address newFeeCollector);\n event VotemarketUpdated(address newVotemarket);\n event CampaignRemoteManagerUpdated(address newCampaignRemoteManager);\n event CampaignCreated(\n address gauge,\n address rewardToken,\n uint256 maxRewardPerVote,\n uint256 totalRewardAmount\n );\n event CampaignIdUpdated(uint256 newId);\n event CampaignClosed(uint256 campaignId);\n event TotalRewardAmountUpdated(uint256 extraTotalRewardAmount);\n event NumberOfPeriodsUpdated(uint8 extraNumberOfPeriods);\n event RewardPerVoteUpdated(uint256 newMaxRewardPerVote);\n event TokensRescued(address token, uint256 amount, address receiver);\n\n ////////////////////////////////////////////////////\n /// --- CONSTRUCTOR && INITIALIZATION\n ////////////////////////////////////////////////////\n constructor(\n uint256 _targetChainId,\n address _rewardToken,\n address _gauge\n ) {\n targetChainId = _targetChainId;\n rewardToken = _rewardToken;\n gauge = _gauge;\n\n // Prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /// @notice initialize function, to set up initial internal state\n /// @param _strategist Address of the strategist\n /// @param _fee Fee in FEE_BASE unit payed when managing campaign\n /// @param _feeCollector Address of the fee collector\n function initialize(\n address _strategist,\n uint16 _fee,\n address _feeCollector,\n address _campaignRemoteManager,\n address _votemarket\n ) external onlyGovernor initializer {\n _setStrategistAddr(_strategist);\n _setFee(_fee);\n _setFeeCollector(_feeCollector);\n _setCampaignRemoteManager(_campaignRemoteManager);\n _setVotemarket(_votemarket);\n }\n\n ////////////////////////////////////////////////////\n /// --- MUTATIVE FUNCTIONS\n ////////////////////////////////////////////////////\n /// @notice Create a new campaign on VotemarketV2\n /// @dev This will use all token available in this contract\n /// @param numberOfPeriods Duration of the campaign in weeks\n /// @param maxRewardPerVote Maximum reward per vote to distribute, to avoid overspending\n /// @param blacklist List of addresses to exclude from the campaign\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function createCampaign(\n uint8 numberOfPeriods,\n uint256 maxRewardPerVote,\n address[] calldata blacklist,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId == 0, \"Campaign already created\");\n require(numberOfPeriods > 1, \"Invalid number of periods\");\n require(maxRewardPerVote > 0, \"Invalid reward per vote\");\n\n // Handle fee (if any)\n uint256 balanceSubFee = _handleFee();\n\n // Approve the balanceSubFee to the campaign manager\n IERC20(rewardToken).safeApprove(campaignRemoteManager, 0);\n IERC20(rewardToken).safeApprove(campaignRemoteManager, balanceSubFee);\n\n // Create a new campaign\n ICampaignRemoteManager(campaignRemoteManager).createCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignCreationParams({\n chainId: targetChainId,\n gauge: gauge,\n manager: address(this),\n rewardToken: rewardToken,\n numberOfPeriods: numberOfPeriods,\n maxRewardPerVote: maxRewardPerVote,\n totalRewardAmount: balanceSubFee,\n addresses: blacklist,\n hook: address(0),\n isWhitelist: false\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit CampaignCreated(\n gauge,\n rewardToken,\n maxRewardPerVote,\n balanceSubFee\n );\n }\n\n /// @notice Manage the total reward amount of the campaign\n /// @dev This function should be called after the campaign is created\n /// @dev This will use all the token available in this contract\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageTotalRewardAmount(\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n\n // Handle fee (if any)\n uint256 balanceSubFee = _handleFee();\n\n // Approve the total reward amount to the campaign manager\n IERC20(rewardToken).safeApprove(campaignRemoteManager, 0);\n IERC20(rewardToken).safeApprove(campaignRemoteManager, balanceSubFee);\n\n // Manage the campaign\n // https://github.com/stake-dao/votemarket-v2/blob/main/packages/votemarket/src/Votemarket.sol#L668\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: 0,\n totalRewardAmount: balanceSubFee,\n maxRewardPerVote: 0\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit TotalRewardAmountUpdated(balanceSubFee);\n }\n\n /// @notice Manage the number of periods of the campaign\n /// @dev This function should be called after the campaign is created\n /// @param extraNumberOfPeriods Number of additional periods (cannot be 0)\n /// that will be added to already existing amount of periods.\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageNumberOfPeriods(\n uint8 extraNumberOfPeriods,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n require(extraNumberOfPeriods > 0, \"Invalid number of periods\");\n\n // Manage the campaign\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: extraNumberOfPeriods,\n totalRewardAmount: 0,\n maxRewardPerVote: 0\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit NumberOfPeriodsUpdated(extraNumberOfPeriods);\n }\n\n /// @notice Manage the reward per vote of the campaign\n /// @dev This function should be called after the campaign is created\n /// @param newMaxRewardPerVote New maximum reward per vote\n /// @param bridgeFee Fee to pay for the bridge\n /// @param additionalGasLimit Additional gas limit for the bridge\n function manageRewardPerVote(\n uint256 newMaxRewardPerVote,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n require(campaignId != 0, \"Campaign not created\");\n require(newMaxRewardPerVote > 0, \"Invalid reward per vote\");\n\n // Manage the campaign\n ICampaignRemoteManager(campaignRemoteManager).manageCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignManagementParams({\n campaignId: campaignId,\n rewardToken: rewardToken,\n numberOfPeriods: 0,\n totalRewardAmount: 0,\n maxRewardPerVote: newMaxRewardPerVote\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n\n emit RewardPerVoteUpdated(newMaxRewardPerVote);\n }\n\n /// @notice Close the campaign.\n /// @dev This function only work on the L2 chain. Not on mainnet.\n /// @dev The _campaignId parameter is not related to the campaignId of this contract, allowing greater flexibility.\n /// @param _campaignId Id of the campaign to close\n function closeCampaign(\n uint256 _campaignId,\n uint256 bridgeFee,\n uint256 additionalGasLimit\n ) external nonReentrant onlyGovernorOrStrategist {\n ICampaignRemoteManager(campaignRemoteManager).closeCampaign{\n value: bridgeFee\n }(\n ICampaignRemoteManager.CampaignClosingParams({\n campaignId: campaignId\n }),\n targetChainId,\n additionalGasLimit,\n votemarket\n );\n emit CampaignClosed(_campaignId);\n }\n\n /// @notice calculate the fee amount and transfer it to the feeCollector\n /// @return Balance after fee\n function _handleFee() internal returns (uint256) {\n // Cache current rewardToken balance\n uint256 balance = IERC20(rewardToken).balanceOf(address(this));\n require(balance > 0, \"No reward to manage\");\n\n uint256 feeAmount = (balance * fee) / FEE_BASE;\n\n // If there is a fee, transfer it to the feeCollector\n if (feeAmount > 0) {\n // Transfer the fee to the feeCollector\n IERC20(rewardToken).safeTransfer(feeCollector, feeAmount);\n emit FeeCollected(feeCollector, feeAmount);\n\n return IERC20(rewardToken).balanceOf(address(this));\n }\n\n // Return remaining balance\n return balance;\n }\n\n ////////////////////////////////////////////////////\n /// --- GOVERNANCE && OPERATION\n ////////////////////////////////////////////////////\n /// @notice Set the campaign id\n /// @dev Only callable by the governor or strategist\n /// @param _campaignId New campaign id\n function setCampaignId(uint256 _campaignId)\n external\n onlyGovernorOrStrategist\n {\n campaignId = _campaignId;\n emit CampaignIdUpdated(_campaignId);\n }\n\n /// @notice Rescue ETH from the contract\n /// @dev Only callable by the governor or strategist\n /// @param receiver Address to receive the ETH\n function rescueETH(address receiver)\n external\n nonReentrant\n onlyGovernorOrStrategist\n {\n require(receiver != address(0), \"Invalid receiver\");\n uint256 balance = address(this).balance;\n (bool success, ) = receiver.call{ value: balance }(\"\");\n require(success, \"Transfer failed\");\n emit TokensRescued(address(0), balance, receiver);\n }\n\n /// @notice Rescue ERC20 tokens from the contract\n /// @dev Only callable by the governor or strategist\n /// @param token Address of the token to rescue\n function rescueToken(address token, address receiver)\n external\n nonReentrant\n onlyGovernor\n {\n require(receiver != address(0), \"Invalid receiver\");\n uint256 balance = IERC20(token).balanceOf(address(this));\n IERC20(token).safeTransfer(receiver, balance);\n emit TokensRescued(token, balance, receiver);\n }\n\n /// @notice Set the fee\n /// @dev Only callable by the governor\n /// @param _fee New fee\n function setFee(uint16 _fee) external onlyGovernor {\n _setFee(_fee);\n }\n\n /// @notice Internal logic to set the fee\n function _setFee(uint16 _fee) internal {\n require(_fee <= FEE_BASE / 2, \"Fee too high\");\n fee = _fee;\n emit FeeUpdated(_fee);\n }\n\n /// @notice Set the fee collector\n /// @dev Only callable by the governor\n /// @param _feeCollector New fee collector\n function setFeeCollector(address _feeCollector) external onlyGovernor {\n _setFeeCollector(_feeCollector);\n }\n\n /// @notice Internal logic to set the fee collector\n function _setFeeCollector(address _feeCollector) internal {\n require(_feeCollector != address(0), \"Invalid fee collector\");\n feeCollector = _feeCollector;\n emit FeeCollectorUpdated(_feeCollector);\n }\n\n /// @notice Set the campaignRemoteManager\n /// @param _campaignRemoteManager New campaignRemoteManager address\n function setCampaignRemoteManager(address _campaignRemoteManager)\n external\n onlyGovernor\n {\n _setCampaignRemoteManager(_campaignRemoteManager);\n }\n\n /// @notice Internal logic to set the campaignRemoteManager\n /// @param _campaignRemoteManager New campaignRemoteManager address\n function _setCampaignRemoteManager(address _campaignRemoteManager)\n internal\n {\n require(\n _campaignRemoteManager != address(0),\n \"Invalid campaignRemoteManager\"\n );\n campaignRemoteManager = _campaignRemoteManager;\n emit CampaignRemoteManagerUpdated(_campaignRemoteManager);\n }\n\n /// @notice Set the votemarket address\n /// @param _votemarket New votemarket address\n function setVotemarket(address _votemarket) external onlyGovernor {\n _setVotemarket(_votemarket);\n }\n\n /// @notice Internal logic to set the votemarket address\n function _setVotemarket(address _votemarket) internal onlyGovernor {\n require(_votemarket != address(0), \"Invalid votemarket\");\n votemarket = _votemarket;\n emit VotemarketUpdated(_votemarket);\n }\n\n receive() external payable {}\n}\n" + }, + "contracts/strategies/Generalized4626Strategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Generalized 4626 Strategy\n * @notice Investment strategy for ERC-4626 Tokenized Vaults\n * @author Origin Protocol Inc\n */\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\n\ncontract Generalized4626Strategy is InitializableAbstractStrategy {\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecate_shareToken;\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecate_assetToken;\n\n IERC20 public immutable shareToken;\n IERC20 public immutable assetToken;\n\n // For future use\n uint256[50] private __gap;\n\n /**\n * @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n * and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n * @param _assetToken Address of the ERC-4626 asset token. eg frxETH or DAI\n */\n constructor(BaseStrategyConfig memory _baseConfig, address _assetToken)\n InitializableAbstractStrategy(_baseConfig)\n {\n shareToken = IERC20(_baseConfig.platformAddress);\n assetToken = IERC20(_assetToken);\n }\n\n function initialize() external virtual onlyGovernor initializer {\n address[] memory rewardTokens = new address[](0);\n address[] memory assets = new address[](1);\n address[] memory pTokens = new address[](1);\n\n assets[0] = address(assetToken);\n pTokens[0] = address(platformAddress);\n\n InitializableAbstractStrategy._initialize(\n rewardTokens,\n assets,\n pTokens\n );\n }\n\n /**\n * @dev Deposit assets by converting them to shares\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit assets by converting them to shares\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal virtual {\n require(_amount > 0, \"Must deposit something\");\n require(_asset == address(assetToken), \"Unexpected asset address\");\n\n // slither-disable-next-line unused-return\n IERC4626(platformAddress).deposit(_amount, address(this));\n emit Deposit(_asset, address(shareToken), _amount);\n }\n\n /**\n * @dev Deposit the entire balance of assetToken to gain shareToken\n */\n function depositAll() external virtual override onlyVault nonReentrant {\n uint256 balance = assetToken.balanceOf(address(this));\n if (balance > 0) {\n _deposit(address(assetToken), balance);\n }\n }\n\n /**\n * @dev Withdraw asset by burning shares\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual override onlyVault nonReentrant {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n require(_asset == address(assetToken), \"Unexpected asset address\");\n\n // slither-disable-next-line unused-return\n IERC4626(platformAddress).withdraw(_amount, _recipient, address(this));\n emit Withdrawal(_asset, address(shareToken), _amount);\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset / share tokens\n */\n function _abstractSetPToken(address, address) internal virtual override {\n _approveBase();\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll()\n external\n virtual\n override\n onlyVaultOrGovernor\n nonReentrant\n {\n uint256 shareBalance = shareToken.balanceOf(address(this));\n uint256 assetAmount = 0;\n if (shareBalance > 0) {\n assetAmount = IERC4626(platformAddress).redeem(\n shareBalance,\n vaultAddress,\n address(this)\n );\n emit Withdrawal(\n address(assetToken),\n address(shareToken),\n assetAmount\n );\n }\n }\n\n /**\n * @notice Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(_asset == address(assetToken), \"Unexpected asset address\");\n /* We are intentionally not counting the amount of assetToken parked on the\n * contract toward the checkBalance. The deposit and withdraw functions\n * should not result in assetToken being unused and owned by this strategy\n * contract.\n */\n IERC4626 platform = IERC4626(platformAddress);\n return platform.previewRedeem(platform.balanceOf(address(this)));\n }\n\n /**\n * @notice Governor approves the ERC-4626 Tokenized Vault to spend the asset.\n */\n function safeApproveAllTokens() external override onlyGovernor {\n _approveBase();\n }\n\n function _approveBase() internal virtual {\n // Approval the asset to be transferred to the ERC-4626 Tokenized Vault.\n // Used by the ERC-4626 deposit() and mint() functions\n // slither-disable-next-line unused-return\n assetToken.approve(platformAddress, type(uint256).max);\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset)\n public\n view\n virtual\n override\n returns (bool)\n {\n return _asset == address(assetToken);\n }\n\n /**\n * @notice is not supported for this strategy as the asset and\n * ERC-4626 Tokenized Vault are set at deploy time.\n * @dev If the ERC-4626 Tokenized Vault needed to be changed, a new\n * contract would need to be deployed and the proxy updated.\n */\n function setPTokenAddress(address, address) external override onlyGovernor {\n revert(\"unsupported function\");\n }\n\n /**\n * @notice is not supported for this strategy as the asset and\n * ERC-4626 Tokenized Vault are set at deploy time.\n * @dev If the ERC-4626 Tokenized Vault needed to be changed, a new\n * contract would need to be deployed and the proxy updated.\n */\n function removePToken(uint256) external override onlyGovernor {\n revert(\"unsupported function\");\n }\n}\n" + }, + "contracts/strategies/Generalized4626USDTStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IUSDT {\n // Tether's approve does not return a bool like standard IERC20 contracts\n // slither-disable-next-line erc20-interface\n function approve(address _spender, uint256 _value) external;\n}\n\n/**\n * @title Generalized 4626 Strategy when asset is Tether USD (USDT)\n * @notice Investment strategy for ERC-4626 Tokenized Vaults for the USDT asset.\n * @author Origin Protocol Inc\n */\nimport { Generalized4626Strategy } from \"./Generalized4626Strategy.sol\";\n\ncontract Generalized4626USDTStrategy is Generalized4626Strategy {\n /**\n * @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n * and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n * @param _assetToken Address of the ERC-4626 asset token. eg frxETH or DAI\n */\n constructor(BaseStrategyConfig memory _baseConfig, address _assetToken)\n Generalized4626Strategy(_baseConfig, _assetToken)\n {}\n\n /// @dev Override for Tether as USDT does not return a bool on approve.\n /// Using assetToken.approve will fail as it expects a bool return value\n function _approveBase() internal virtual override {\n // Approval the asset to be transferred to the ERC-4626 Tokenized Vault.\n // Used by the ERC-4626 deposit() and mint() functions\n // slither-disable-next-line unused-return\n IUSDT(address(assetToken)).approve(platformAddress, type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/IAave.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface for Aaves Lending Pool\n * Documentation: https://developers.aave.com/#lendingpool\n */\ninterface IAaveLendingPool {\n /**\n * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.\n * - E.g. User deposits 100 USDC and gets in return 100 aUSDC\n * @param asset The address of the underlying asset to deposit\n * @param amount The amount to be deposited\n * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user\n * wants to receive them on his own wallet, or a different address if the beneficiary of aTokens\n * is a different wallet\n * @param referralCode Code used to register the integrator originating the operation, for potential rewards.\n * 0 if the action is executed directly by the user, without any middle-man\n **/\n function deposit(\n address asset,\n uint256 amount,\n address onBehalfOf,\n uint16 referralCode\n ) external;\n\n /**\n * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned\n * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC\n * @param asset The address of the underlying asset to withdraw\n * @param amount The underlying amount to be withdrawn\n * - Send the value type(uint256).max in order to withdraw the whole aToken balance\n * @param to Address that will receive the underlying, same as msg.sender if the user\n * wants to receive it on his own wallet, or a different address if the beneficiary is a\n * different wallet\n * @return The final amount withdrawn\n **/\n function withdraw(\n address asset,\n uint256 amount,\n address to\n ) external returns (uint256);\n}\n\n/**\n * @dev Interface for Aaves Lending Pool\n * Documentation: https://developers.aave.com/#lendingpooladdressesprovider\n */\ninterface ILendingPoolAddressesProvider {\n /**\n * @notice Get the current address for Aave LendingPool\n * @dev Lending pool is the core contract on which to call deposit\n */\n function getLendingPool() external view returns (address);\n}\n" + }, + "contracts/strategies/IAaveIncentivesController.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IAaveIncentivesController {\n event RewardsAccrued(address indexed user, uint256 amount);\n\n event RewardsClaimed(\n address indexed user,\n address indexed to,\n uint256 amount\n );\n\n event RewardsClaimed(\n address indexed user,\n address indexed to,\n address indexed claimer,\n uint256 amount\n );\n\n event ClaimerSet(address indexed user, address indexed claimer);\n\n /*\n * @dev Returns the configuration of the distribution for a certain asset\n * @param asset The address of the reference asset of the distribution\n * @return The asset index, the emission per second and the last updated timestamp\n **/\n function getAssetData(address asset)\n external\n view\n returns (\n uint256,\n uint256,\n uint256\n );\n\n /**\n * @dev Whitelists an address to claim the rewards on behalf of another address\n * @param user The address of the user\n * @param claimer The address of the claimer\n */\n function setClaimer(address user, address claimer) external;\n\n /**\n * @dev Returns the whitelisted claimer for a certain address (0x0 if not set)\n * @param user The address of the user\n * @return The claimer address\n */\n function getClaimer(address user) external view returns (address);\n\n /**\n * @dev Configure assets for a certain rewards emission\n * @param assets The assets to incentivize\n * @param emissionsPerSecond The emission for each asset\n */\n function configureAssets(\n address[] calldata assets,\n uint256[] calldata emissionsPerSecond\n ) external;\n\n /**\n * @dev Called by the corresponding asset on any update that affects the rewards distribution\n * @param asset The address of the user\n * @param userBalance The balance of the user of the asset in the lending pool\n * @param totalSupply The total supply of the asset in the lending pool\n **/\n function handleAction(\n address asset,\n uint256 userBalance,\n uint256 totalSupply\n ) external;\n\n /**\n * @dev Returns the total of rewards of an user, already accrued + not yet accrued\n * @param user The address of the user\n * @return The rewards\n **/\n function getRewardsBalance(address[] calldata assets, address user)\n external\n view\n returns (uint256);\n\n /**\n * @dev Claims reward for an user, on all the assets of the lending pool,\n * accumulating the pending rewards\n * @param amount Amount of rewards to claim\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewards(\n address[] calldata assets,\n uint256 amount,\n address to\n ) external returns (uint256);\n\n /**\n * @dev Claims reward for an user on behalf, on all the assets of the\n * lending pool, accumulating the pending rewards. The caller must\n * be whitelisted via \"allowClaimOnBehalf\" function by the RewardsAdmin role manager\n * @param amount Amount of rewards to claim\n * @param user Address to check and claim rewards\n * @param to Address that will be receiving the rewards\n * @return Rewards claimed\n **/\n function claimRewardsOnBehalf(\n address[] calldata assets,\n uint256 amount,\n address user,\n address to\n ) external returns (uint256);\n\n /**\n * @dev returns the unclaimed rewards of the user\n * @param user the address of the user\n * @return the unclaimed user rewards\n */\n function getUserUnclaimedRewards(address user)\n external\n view\n returns (uint256);\n\n /**\n * @dev returns the unclaimed rewards of the user\n * @param user the address of the user\n * @param asset The asset to incentivize\n * @return the user index for the asset\n */\n function getUserAssetData(address user, address asset)\n external\n view\n returns (uint256);\n\n /**\n * @dev for backward compatibility with previous implementation of the Incentives controller\n */\n function REWARD_TOKEN() external view returns (address);\n\n /**\n * @dev for backward compatibility with previous implementation of the Incentives controller\n */\n function PRECISION() external view returns (uint8);\n\n /**\n * @dev Gets the distribution end timestamp of the emissions\n */\n function DISTRIBUTION_END() external view returns (uint256);\n}\n" + }, + "contracts/strategies/IAaveStakeToken.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IAaveStakedToken {\n function COOLDOWN_SECONDS() external returns (uint256);\n\n function UNSTAKE_WINDOW() external returns (uint256);\n\n function balanceOf(address addr) external returns (uint256);\n\n function redeem(address to, uint256 amount) external;\n\n function stakersCooldowns(address addr) external returns (uint256);\n\n function cooldown() external;\n}\n" + }, + "contracts/strategies/ICompound.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @dev Compound C Token interface\n * Documentation: https://compound.finance/developers/ctokens\n */\ninterface ICERC20 {\n /**\n * @notice The mint function transfers an asset into the protocol, which begins accumulating\n * interest based on the current Supply Rate for the asset. The user receives a quantity of\n * cTokens equal to the underlying tokens supplied, divided by the current Exchange Rate.\n * @param mintAmount The amount of the asset to be supplied, in units of the underlying asset.\n * @return 0 on success, otherwise an Error codes\n */\n function mint(uint256 mintAmount) external returns (uint256);\n\n /**\n * @notice Sender redeems cTokens in exchange for the underlying asset\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param redeemTokens The number of cTokens to redeem into underlying\n * @return uint 0=success, otherwise an error code.\n */\n function redeem(uint256 redeemTokens) external returns (uint256);\n\n /**\n * @notice The redeem underlying function converts cTokens into a specified quantity of the underlying\n * asset, and returns them to the user. The amount of cTokens redeemed is equal to the quantity of\n * underlying tokens received, divided by the current Exchange Rate. The amount redeemed must be less\n * than the user's Account Liquidity and the market's available liquidity.\n * @param redeemAmount The amount of underlying to be redeemed.\n * @return 0 on success, otherwise an error code.\n */\n function redeemUnderlying(uint256 redeemAmount) external returns (uint256);\n\n /**\n * @notice The user's underlying balance, representing their assets in the protocol, is equal to\n * the user's cToken balance multiplied by the Exchange Rate.\n * @param owner The account to get the underlying balance of.\n * @return The amount of underlying currently owned by the account.\n */\n function balanceOfUnderlying(address owner) external returns (uint256);\n\n /**\n * @notice Calculates the exchange rate from the underlying to the CToken\n * @dev This function does not accrue interest before calculating the exchange rate\n * @return Calculated exchange rate scaled by 1e18\n */\n function exchangeRateStored() external view returns (uint256);\n\n /**\n * @notice Get the token balance of the `owner`\n * @param owner The address of the account to query\n * @return The number of tokens owned by `owner`\n */\n function balanceOf(address owner) external view returns (uint256);\n\n /**\n * @notice Get the supply rate per block for supplying the token to Compound.\n */\n function supplyRatePerBlock() external view returns (uint256);\n\n /**\n * @notice Address of the Compound Comptroller.\n */\n function comptroller() external view returns (address);\n}\n" + }, + "contracts/strategies/IConvexDeposits.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IConvexDeposits {\n function deposit(\n uint256 _pid,\n uint256 _amount,\n bool _stake\n ) external returns (bool);\n\n function deposit(\n uint256 _amount,\n bool _lock,\n address _stakeAddress\n ) external;\n}\n" + }, + "contracts/strategies/ICRVMinter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICRVMinter {\n function mint(address gaugeAddress) external;\n}\n" + }, + "contracts/strategies/ICurveETHPoolV1.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurveETHPoolV1 {\n event AddLiquidity(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event ApplyNewFee(uint256 fee);\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n event CommitNewFee(uint256 new_fee);\n event RampA(\n uint256 old_A,\n uint256 new_A,\n uint256 initial_time,\n uint256 future_time\n );\n event RemoveLiquidity(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 token_supply\n );\n event RemoveLiquidityImbalance(\n address indexed provider,\n uint256[2] token_amounts,\n uint256[2] fees,\n uint256 invariant,\n uint256 token_supply\n );\n event RemoveLiquidityOne(\n address indexed provider,\n uint256 token_amount,\n uint256 coin_amount,\n uint256 token_supply\n );\n event StopRampA(uint256 A, uint256 t);\n event TokenExchange(\n address indexed buyer,\n int128 sold_id,\n uint256 tokens_sold,\n int128 bought_id,\n uint256 tokens_bought\n );\n event Transfer(\n address indexed sender,\n address indexed receiver,\n uint256 value\n );\n\n function A() external view returns (uint256);\n\n function A_precise() external view returns (uint256);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function add_liquidity(uint256[2] memory _amounts, uint256 _min_mint_amount)\n external\n payable\n returns (uint256);\n\n function add_liquidity(\n uint256[2] memory _amounts,\n uint256 _min_mint_amount,\n address _receiver\n ) external payable returns (uint256);\n\n function admin_action_deadline() external view returns (uint256);\n\n function admin_balances(uint256 i) external view returns (uint256);\n\n function admin_fee() external view returns (uint256);\n\n function allowance(address arg0, address arg1)\n external\n view\n returns (uint256);\n\n function apply_new_fee() external;\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function balanceOf(address arg0) external view returns (uint256);\n\n function balances(uint256 arg0) external view returns (uint256);\n\n function calc_token_amount(uint256[2] memory _amounts, bool _is_deposit)\n external\n view\n returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _burn_amount, int128 i)\n external\n view\n returns (uint256);\n\n function coins(uint256 arg0) external view returns (address);\n\n function commit_new_fee(uint256 _new_fee) external;\n\n function decimals() external view returns (uint256);\n\n function ema_price() external view returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy\n ) external payable returns (uint256);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 _dx,\n uint256 _min_dy,\n address _receiver\n ) external payable returns (uint256);\n\n function fee() external view returns (uint256);\n\n function future_A() external view returns (uint256);\n\n function future_A_time() external view returns (uint256);\n\n function future_fee() external view returns (uint256);\n\n function get_balances() external view returns (uint256[2] memory);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n\n function get_p() external view returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function initial_A() external view returns (uint256);\n\n function initial_A_time() external view returns (uint256);\n\n function initialize(\n string memory _name,\n string memory _symbol,\n address[4] memory _coins,\n uint256[4] memory _rate_multipliers,\n uint256 _A,\n uint256 _fee\n ) external;\n\n function last_price() external view returns (uint256);\n\n function ma_exp_time() external view returns (uint256);\n\n function ma_last_time() external view returns (uint256);\n\n function name() external view returns (string memory);\n\n function nonces(address arg0) external view returns (uint256);\n\n function permit(\n address _owner,\n address _spender,\n uint256 _value,\n uint256 _deadline,\n uint8 _v,\n bytes32 _r,\n bytes32 _s\n ) external returns (bool);\n\n function price_oracle() external view returns (uint256);\n\n function ramp_A(uint256 _future_A, uint256 _future_time) external;\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[2] memory _min_amounts\n ) external returns (uint256[2] memory);\n\n function remove_liquidity(\n uint256 _burn_amount,\n uint256[2] memory _min_amounts,\n address _receiver\n ) external returns (uint256[2] memory);\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burn_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[2] memory _amounts,\n uint256 _max_burn_amount,\n address _receiver\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received\n ) external returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _burn_amount,\n int128 i,\n uint256 _min_received,\n address _receiver\n ) external returns (uint256);\n\n function set_ma_exp_time(uint256 _ma_exp_time) external;\n\n function stop_ramp_A() external;\n\n function symbol() external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function version() external view returns (string memory);\n\n function withdraw_admin_fees() external;\n}\n" + }, + "contracts/strategies/ICurveGauge.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurveGauge {\n function balanceOf(address account) external view returns (uint256);\n\n function deposit(uint256 value, address account) external;\n\n function withdraw(uint256 value) external;\n}\n" + }, + "contracts/strategies/ICurveMetaPool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface ICurveMetaPool {\n function add_liquidity(uint256[2] calldata amounts, uint256 min_mint_amount)\n external\n returns (uint256);\n\n function get_virtual_price() external view returns (uint256);\n\n function remove_liquidity(uint256 _amount, uint256[2] calldata min_amounts)\n external\n returns (uint256[2] calldata);\n\n function remove_liquidity_one_coin(\n uint256 _token_amount,\n int128 i,\n uint256 min_amount\n ) external returns (uint256);\n\n function remove_liquidity_imbalance(\n uint256[2] calldata amounts,\n uint256 max_burn_amount\n ) external returns (uint256);\n\n function calc_withdraw_one_coin(uint256 _token_amount, int128 i)\n external\n view\n returns (uint256);\n\n function balances(uint256 i) external view returns (uint256);\n\n function calc_token_amount(uint256[2] calldata amounts, bool deposit)\n external\n view\n returns (uint256);\n\n function base_pool() external view returns (address);\n\n function fee() external view returns (uint256);\n\n function coins(uint256 i) external view returns (address);\n\n function exchange(\n int128 i,\n int128 j,\n uint256 dx,\n uint256 min_dy\n ) external returns (uint256);\n\n function get_dy(\n int128 i,\n int128 j,\n uint256 dx\n ) external view returns (uint256);\n}\n" + }, + "contracts/strategies/ICurvePool.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface ICurvePool {\n function get_virtual_price() external view returns (uint256);\n\n function add_liquidity(uint256[3] calldata _amounts, uint256 _min) external;\n\n function balances(uint256) external view returns (uint256);\n\n function calc_token_amount(uint256[3] calldata _amounts, bool _deposit)\n external\n returns (uint256);\n\n function fee() external view returns (uint256);\n\n function remove_liquidity_one_coin(\n uint256 _amount,\n int128 _index,\n uint256 _minAmount\n ) external;\n\n function remove_liquidity(\n uint256 _amount,\n uint256[3] calldata _minWithdrawAmounts\n ) external;\n\n function calc_withdraw_one_coin(uint256 _amount, int128 _index)\n external\n view\n returns (uint256);\n\n function exchange(\n uint256 i,\n uint256 j,\n uint256 dx,\n uint256 min_dy\n ) external returns (uint256);\n\n function coins(uint256 _index) external view returns (address);\n\n function remove_liquidity_imbalance(\n uint256[3] calldata _amounts,\n uint256 maxBurnAmount\n ) external;\n}\n" + }, + "contracts/strategies/IRewardStaking.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ninterface IRewardStaking {\n function stakeFor(address, uint256) external;\n\n function stake(uint256) external;\n\n function withdraw(uint256 amount, bool claim) external;\n\n function withdrawAndUnwrap(uint256 amount, bool claim) external;\n\n function earned(address account) external view returns (uint256);\n\n function getReward() external;\n\n function getReward(address _account, bool _claimExtras) external;\n\n function extraRewardsLength() external returns (uint256);\n\n function extraRewards(uint256 _pid) external returns (address);\n\n function rewardToken() external returns (address);\n\n function balanceOf(address account) external view returns (uint256);\n}\n" + }, + "contracts/strategies/MorphoAaveStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Morpho Aave Strategy\n * @notice Investment strategy for investing stablecoins via Morpho (Aave)\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, InitializableAbstractStrategy } from \"../utils/InitializableAbstractStrategy.sol\";\nimport { IMorpho } from \"../interfaces/morpho/IMorpho.sol\";\nimport { ILens } from \"../interfaces/morpho/ILens.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\n\ncontract MorphoAaveStrategy is InitializableAbstractStrategy {\n address public constant MORPHO = 0x777777c9898D384F785Ee44Acfe945efDFf5f3E0;\n address public constant LENS = 0x507fA343d0A90786d86C7cd885f5C49263A91FF4;\n\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @dev Initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Approve the spending of all assets by main Morpho contract,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address asset = assetsMapped[i];\n\n // Safe approval\n IERC20(asset).safeApprove(MORPHO, 0);\n IERC20(asset).safeApprove(MORPHO, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset\n * We need to approve and allow Morpho to move them\n * @param _asset Address of the asset to approve\n * @param _pToken The pToken for the approval\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n IERC20(_asset).safeApprove(MORPHO, 0);\n IERC20(_asset).safeApprove(MORPHO, type(uint256).max);\n }\n\n /**\n * @dev Collect accumulated rewards and send them to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Morpho Aave-v2 doesn't distribute reward tokens\n // solhint-disable-next-line max-line-length\n // Ref: https://developers.morpho.xyz/interact-with-morpho/get-started/interact-with-morpho/claim-rewards#morpho-aave-v2\n }\n\n /**\n * @dev Get the amount of rewards pending to be collected from the protocol\n */\n function getPendingRewards() external view returns (uint256 balance) {\n // Morpho Aave-v2 doesn't distribute reward tokens\n // solhint-disable-next-line max-line-length\n // Ref: https://developers.morpho.xyz/interact-with-morpho/get-started/interact-with-morpho/claim-rewards#morpho-aave-v2\n return 0;\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n\n address pToken = address(_getPTokenFor(_asset));\n\n IMorpho(MORPHO).supply(\n pToken,\n address(this), // the address of the user you want to supply on behalf of\n _amount\n );\n emit Deposit(_asset, pToken, _amount);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Morpho\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Morpho\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n address pToken = address(_getPTokenFor(_asset));\n\n IMorpho(MORPHO).withdraw(pToken, _amount);\n emit Withdrawal(_asset, pToken, _amount);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = _checkBalance(assetsMapped[i]);\n if (balance > 0) {\n _withdraw(vaultAddress, assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Return total value of an asset held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n return _checkBalance(_asset);\n }\n\n function _checkBalance(address _asset)\n internal\n view\n returns (uint256 balance)\n {\n address pToken = address(_getPTokenFor(_asset));\n\n // Total value represented by decimal position of underlying token\n (, , balance) = ILens(LENS).getCurrentSupplyBalanceInOf(\n pToken,\n address(this)\n );\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return assetToPToken[_asset] != address(0);\n }\n\n /**\n * @dev Get the pToken wrapped in the IERC20 interface for this asset.\n * Fails if the pToken doesn't exist in our mappings.\n * @param _asset Address of the asset\n * @return pToken Corresponding pToken to this asset\n */\n function _getPTokenFor(address _asset) internal view returns (IERC20) {\n address pToken = assetToPToken[_asset];\n require(pToken != address(0), \"pToken does not exist\");\n return IERC20(pToken);\n }\n}\n" + }, + "contracts/strategies/MorphoCompoundStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Morpho Compound Strategy\n * @notice Investment strategy for investing stablecoins via Morpho (Compound)\n * @author Origin Protocol Inc\n */\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20, AbstractCompoundStrategy, InitializableAbstractStrategy } from \"./AbstractCompoundStrategy.sol\";\nimport { IMorpho } from \"../interfaces/morpho/IMorpho.sol\";\nimport { ILens } from \"../interfaces/morpho/ILens.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract MorphoCompoundStrategy is AbstractCompoundStrategy {\n address public constant MORPHO = 0x8888882f8f843896699869179fB6E4f7e3B58888;\n address public constant LENS = 0x930f1b46e1D081Ec1524efD95752bE3eCe51EF67;\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(BaseStrategyConfig memory _stratConfig)\n InitializableAbstractStrategy(_stratConfig)\n {}\n\n /**\n * @dev Initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n address[] calldata _assets,\n address[] calldata _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n }\n\n /**\n * @dev Approve the spending of all assets by main Morpho contract,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n uint256 assetCount = assetsMapped.length;\n for (uint256 i = 0; i < assetCount; i++) {\n address asset = assetsMapped[i];\n\n // Safe approval\n IERC20(asset).safeApprove(MORPHO, 0);\n IERC20(asset).safeApprove(MORPHO, type(uint256).max);\n }\n }\n\n /**\n * @dev Internal method to respond to the addition of new asset\n * We need to approve and allow Morpho to move them\n * @param _asset Address of the asset to approve\n * @param _pToken The pToken for the approval\n */\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {\n IERC20(_asset).safeApprove(MORPHO, 0);\n IERC20(_asset).safeApprove(MORPHO, type(uint256).max);\n }\n\n /**\n * @dev Collect accumulated rewards and send them to Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n /**\n * Gas considerations. We could query Morpho LENS's `getUserUnclaimedRewards` for each\n * cToken separately and only claimRewards where it is economically feasible. Each call\n * (out of 3) costs ~60k gas extra.\n *\n * Each extra cToken in the `poolTokens` of `claimRewards` function makes that call\n * 89-120k more expensive gas wise.\n *\n * With Lens query in case where:\n * - there is only 1 reward token to collect. Net gas usage in best case would be\n * 3*60 - 2*120 = -60k -> saving 60k gas\n * - there are 2 reward tokens to collect. Net gas usage in best case would be\n * 3*60 - 120 = 60k -> more expensive for 60k gas\n * - there are 3 reward tokens to collect. Net gas usage in best case would be\n * 3*60 = 180k -> more expensive for 180k gas\n *\n * For the above reasoning such \"optimization\" is not implemented\n */\n\n address[] memory poolTokens = new address[](assetsMapped.length);\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n poolTokens[i] = assetToPToken[assetsMapped[i]];\n }\n\n // slither-disable-next-line unused-return\n IMorpho(MORPHO).claimRewards(\n poolTokens, // The addresses of the underlying protocol's pools to claim rewards from\n false // Whether to trade the accrued rewards for MORPHO token, with a premium\n );\n\n // Transfer COMP to Harvester\n IERC20 rewardToken = IERC20(rewardTokenAddresses[0]);\n uint256 balance = rewardToken.balanceOf(address(this));\n emit RewardTokenCollected(\n harvesterAddress,\n rewardTokenAddresses[0],\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n\n /**\n * @dev Get the amount of rewards pending to be collected from the protocol\n */\n function getPendingRewards() external view returns (uint256 balance) {\n address[] memory poolTokens = new address[](assetsMapped.length);\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n poolTokens[i] = assetToPToken[assetsMapped[i]];\n }\n\n return ILens(LENS).getUserUnclaimedRewards(poolTokens, address(this));\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @dev Deposit asset into Morpho\n * @param _asset Address of asset to deposit\n * @param _amount Amount of asset to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n\n IMorpho(MORPHO).supply(\n address(_getCTokenFor(_asset)),\n address(this), // the address of the user you want to supply on behalf of\n _amount\n );\n emit Deposit(_asset, address(_getCTokenFor(_asset)), _amount);\n }\n\n /**\n * @dev Deposit the entire balance of any supported asset into Morpho\n */\n function depositAll() external override onlyVault nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = IERC20(assetsMapped[i]).balanceOf(address(this));\n if (balance > 0) {\n _deposit(assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Withdraw asset from Morpho\n * @param _recipient Address to receive withdrawn asset\n * @param _asset Address of asset to withdraw\n * @param _amount Amount of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n address pToken = assetToPToken[_asset];\n\n IMorpho(MORPHO).withdraw(pToken, _amount);\n emit Withdrawal(_asset, address(_getCTokenFor(_asset)), _amount);\n IERC20(_asset).safeTransfer(_recipient, _amount);\n }\n\n /**\n * @dev Remove all assets from platform and send them to Vault contract.\n */\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n for (uint256 i = 0; i < assetsMapped.length; i++) {\n uint256 balance = _checkBalance(assetsMapped[i]);\n if (balance > 0) {\n _withdraw(vaultAddress, assetsMapped[i], balance);\n }\n }\n }\n\n /**\n * @dev Return total value of an asset held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n return _checkBalance(_asset);\n }\n\n function _checkBalance(address _asset)\n internal\n view\n returns (uint256 balance)\n {\n address pToken = assetToPToken[_asset];\n\n // Total value represented by decimal position of underlying token\n (, , balance) = ILens(LENS).getCurrentSupplyBalanceInOf(\n pToken,\n address(this)\n );\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { CompoundingValidatorManager } from \"./CompoundingValidatorManager.sol\";\n\n/// @title Compounding Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract CompoundingStakingSSVStrategy is\n CompoundingValidatorManager,\n InitializableAbstractStrategy\n{\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n\n // For future use\n uint256[50] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _beaconOracle Address of the Beacon Oracle contract that maps block numbers to slots\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n address _beaconChainDepositContract,\n address _beaconOracle,\n address _beaconProofs\n )\n InitializableAbstractStrategy(_baseConfig)\n CompoundingValidatorManager(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _beaconOracle,\n _beaconProofs\n )\n {\n SSV_TOKEN = _ssvToken;\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract.\n /// @param _rewardTokenAddresses Address of reward token for platform\n /// @param _assets Addresses of initial supported assets\n /// @param _pTokens Platform Token corresponding addresses\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused for validator consolidation.\n /// @param _asset Address of asset to deposit. Has to be WETH.\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n\n // Account for the new WETH\n depositedWethAccountedFor += _amount;\n\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused for validator consolidation.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n // Account for the new WETH\n depositedWethAccountedFor = wethBalance;\n\n emit Deposit(WETH, address(0), newWeth);\n }\n }\n\n /// @notice Withdraw ETH and WETH from this strategy contract.\n /// Will revert if the strategy is paused for validator consolidation.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant whenNotPaused {\n require(_asset == WETH, \"Unsupported asset\");\n\n _withdraw(_recipient, _asset, _amount, address(this).balance);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _withdrawAmount,\n uint256 _ethBalance\n ) internal {\n require(_withdrawAmount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // Convert any ETH from validator partial withdrawals, exits\n // or execution rewards to WETH and do the necessary accounting.\n if (_ethBalance > 0) _convertWethToEth(_ethBalance);\n\n // Transfer WETH to the recipient and do the necessary accounting.\n _transferWeth(_withdrawAmount, _recipient);\n\n emit Withdrawal(_asset, address(0), _withdrawAmount);\n }\n\n /// @notice Transfer all WETH deposits, ETH from validator withdrawals and ETH from\n /// execution rewards in this strategy to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `validatorWithdrawal` operation.\n /// Will revert if the strategy is paused for validator consolidation.\n function withdrawAll()\n external\n override\n onlyVaultOrGovernor\n nonReentrant\n whenNotPaused\n {\n uint256 ethBalance = address(this).balance;\n uint256 withdrawAmount = IERC20(WETH).balanceOf(address(this)) +\n ethBalance;\n\n if (withdrawAmount > 0) {\n _withdraw(vaultAddress, WETH, withdrawAmount, ethBalance);\n }\n }\n\n /// @notice Returns the last verified balance of validator deposits, validator balance,\n /// WETH and ETH in the strategy contract.\n /// @param _asset Address of weth asset\n /// @return balance Total value of (W)ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n // Load the last verified balance from the storage\n // and add to the latest WETH balance of this strategy.\n balance =\n lastVerifiedEthBalance +\n IWETH9(WETH).balanceOf(address(this));\n }\n\n /// @notice Returns bool indicating whether asset is supported by strategy.\n /// @param _asset The address of the asset token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for deposits\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /**\n * @notice We can accept ETH directly to this contract from anyone as it does not impact our accounting\n * like it did in the legacy NativeStakingStrategy.\n */\n receive() external payable {}\n\n /***************************************\n Internal functions\n ****************************************/\n\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Consensus rewards are compounded to the validator's balance instread of being\n /// swept to this strategy contract.\n /// Execution rewards from MEV and tx priority accumulate as ETH in this strategy contract,\n /// but so does withdrawals from validators. It's too complex to separate the two\n /// so this function is not implemented.\n /// Besides, ETH rewards are not sent to the Dripper any more. The Vault can regulate\n /// the increase in assets.\n function _collectRewardTokens() internal pure override {\n revert(\"Unsupported function\");\n }\n}\n" + }, + "contracts/strategies/NativeStaking/CompoundingValidatorManager.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IConsolidationSource } from \"../../interfaces/IConsolidation.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconRoots } from \"../../beacon/BeaconRoots.sol\";\nimport { PartialWithdrawal } from \"../../beacon/PartialWithdrawal.sol\";\nimport { IBeaconProofs } from \"../../interfaces/IBeaconProofs.sol\";\nimport { IBeaconOracle } from \"../../interfaces/IBeaconOracle.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Validator lifecycle management contract\n * @notice This contract implements all the required functionality to\n * register, deposit, withdraw, exit, remove and consolidate validators.\n * @author Origin Protocol Inc\n */\nabstract contract CompoundingValidatorManager is Governable, Pausable {\n using SafeERC20 for IERC20;\n\n /// @notice The amount of ETH in wei that is required for a deposit to a new validator.\n /// Initially this is 32 ETH, but will be increased to 1 ETH after P2P's APIs have been updated\n /// to support deposits of 1 ETH.\n uint256 public constant DEPOSIT_AMOUNT_WEI = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Address of the Beacon Oracle contract that maps block numbers to slots\n address public immutable BEACON_ORACLE;\n /// @notice Address of the Beacon Proofs contract that verifies beacon chain data\n address public immutable BEACON_PROOFS;\n\n /// @notice Address of the registrator - allowed to register, withdraw, exit and remove validators\n address public validatorRegistrator;\n\n /// Deposit data for new compounding validators.\n\n /// @param pubKeyHash Hash of validator's public key using the Beacon Chain's format\n /// @param amountWei Amount of ETH in wei that has been deposited to the beacon chain deposit contract\n /// @param blockNumber Block number when the deposit was made\n /// @param depositIndex The index of the deposit in the list of active deposits\n /// @param status The status of the deposit, either PENDING or PROVEN\n struct DepositData {\n bytes32 pubKeyHash;\n uint64 amountGwei;\n uint64 blockNumber;\n uint32 depositIndex;\n DepositStatus status;\n }\n /// @notice Mapping of the root of a deposit (depositDataRoot) to its data\n mapping(bytes32 => DepositData) public deposits;\n /// @notice List of deposit roots that are still to be verified as processed on the beacon chain\n bytes32[] public depositsRoots;\n\n // Validator data\n\n struct ValidatorData {\n bytes32 pubKeyHash; // Hash of the validator's public key using the Beacon Chain's format\n uint64 index; // The index of the validator on the beacon chain\n }\n /// @notice List of validator public key hashes and indexes that have been verified to exist on the beacon chain.\n /// These have had a deposit processed and the validator's balance increased.\n /// Validators will be removed from this list when its verified they have a zero balance.\n ValidatorData[] internal verifiedValidators;\n /// @notice State of the new compounding validators with a 0x02 withdrawal credential prefix.\n /// Uses the Beacon chain hashing for BLSPubkey which is\n /// sha256(abi.encodePacked(validator.pubkey, bytes16(0)))\n mapping(bytes32 => VALIDATOR_STATE) public validatorState;\n\n /// @param timestamp Timestamp of the snapshot\n /// @param ethBalance The balance of ETH in the strategy contract at the snapshot\n struct Balances {\n uint64 timestamp;\n uint128 ethBalance;\n }\n // TODO is it more efficient to use the block root rather than hashing it?\n /// @notice Mapping of the block root to the balances at that slot\n mapping(bytes32 => Balances) public snappedBalances;\n uint64 public lastSnapTimestamp;\n uint128 public lastVerifiedEthBalance;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n bytes32 public consolidationLastPubKeyHash;\n address public consolidationSourceStrategy;\n mapping(address => bool) public consolidationSourceStrategies;\n\n // For future use\n uint256[50] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n VERIFIED, // validator has been verified to exist on the beacon chain\n EXITED, // The validator has been verified to have a zero balance\n REMOVED // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n enum DepositStatus {\n UNKNOWN, // default value\n PENDING, // deposit is pending and waiting to be verified\n VERIFIED // deposit has been verified and is ready to be staked\n }\n\n event RegistratorChanged(address indexed newAddress);\n event SourceStrategyAdded(address indexed strategy);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n uint64[] operatorIds\n );\n event SSVValidatorRemoved(bytes32 indexed pubKeyHash, uint64[] operatorIds);\n event ETHStaked(\n bytes32 indexed pubKeyHash,\n bytes32 indexed depositDataRoot,\n bytes pubKey,\n uint256 amountWei\n );\n event ValidatorVerified(\n bytes32 indexed pubKeyHash,\n uint64 indexed validatorIndex\n );\n event DepositVerified(bytes32 indexed depositDataRoot, uint256 amountWei);\n event ValidatorWithdraw(bytes32 indexed pubKeyHash, uint256 amountWei);\n event BalancesSnapped(\n uint256 indexed timestamp,\n bytes32 indexed blockRoot,\n uint256 ethBalance\n );\n event BalancesVerified(\n uint64 indexed timestamp,\n uint256 totalDepositsWei,\n uint256 totalValidatorBalance,\n uint256 wethBalance,\n uint256 ethBalance\n );\n event ConsolidationRequested(\n bytes32 indexed lastSourcePubKeyHash,\n bytes32 indexed targetPubKeyHash,\n address indexed sourceStrategy\n );\n event ConsolidationVerified(\n bytes32 indexed lastSourcePubKeyHash,\n uint64 indexed lastValidatorIndex,\n uint256 consolidationCount\n );\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(msg.sender == validatorRegistrator, \"Not Registrator\");\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _beaconOracle Address of the Beacon Oracle contract that maps block numbers to slots\n /// @param _beaconProofs Address of the Beacon Proofs contract that verifies beacon chain data\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n address _beaconOracle,\n address _beaconProofs\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n BEACON_ORACLE = _beaconOracle;\n BEACON_PROOFS = _beaconProofs;\n }\n\n /***************************************\n Admin Functions\n ****************************************/\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Adds support for a legacy staking strategy that can be used for consolidation.\n function addSourceStrategy(address _strategy) external onlyGovernor {\n consolidationSourceStrategies[_strategy] = true;\n\n emit SourceStrategyAdded(_strategy);\n }\n\n /***************************************`\n Validator Management\n ****************************************/\n\n /// @notice Registers a single validator in a SSV Cluster.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for the validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n bytes calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n // Check each public key has not already been used\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n // Store the validator state as registered\n validatorState[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n ISSVNetwork(SSV_NETWORK).registerValidator(\n publicKey,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n\n emit SSVValidatorRegistered(pubKeyHash, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stakes WETH in this strategy to a compounding validator.\n /// Does not convert any ETH sitting in this strategy to WETH.\n /// @param validator validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n /// @param depositAmountGwei The amount of WETH to stake to the validator in Gwei.\n // slither-disable-start reentrancy-eth\n function stakeEth(\n ValidatorStakeData calldata validator,\n uint64 depositAmountGwei\n ) external onlyRegistrator whenNotPaused {\n uint256 depositAmountWei = uint256(depositAmountGwei) * 1 gwei;\n // Check there is enough WETH from the deposits sitting in this strategy contract\n // There could be ETH from withdrawals but we'll ignore that. If it's really needed\n // the ETH can be withdrawn and then deposited back to the strategy.\n require(\n depositAmountWei <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n\n // Convert required ETH from WETH and do the necessary accounting\n _convertEthToWeth(depositAmountWei);\n\n // Hash the public key using the Beacon Chain's hashing for BLSPubkey\n bytes32 pubKeyHash = _hashPubKey(validator.pubkey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can only stake to a validator has have been registered or verified.\n // Can not stake to a validator that has been staked but not yet verified.\n require(\n (currentState == VALIDATOR_STATE.REGISTERED ||\n currentState == VALIDATOR_STATE.VERIFIED),\n \"Not registered or verified\"\n );\n require(\n currentState == VALIDATOR_STATE.VERIFIED ||\n depositAmountWei == DEPOSIT_AMOUNT_WEI,\n \"Invalid first deposit amount\"\n );\n require(depositAmountWei >= 1 ether, \"Deposit too small\");\n\n /* 0x02 to indicate that withdrawal credentials are for a compounding validator\n * that was introduced with the Pectra upgrade.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x02),\n bytes11(0),\n address(this)\n );\n\n // Deposit to the Beacon Chain deposit contract.\n // This will create a deposit in the beacon chain's pending deposit queue.\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: depositAmountWei\n }(\n validator.pubkey,\n withdrawalCredentials,\n validator.signature,\n validator.depositDataRoot\n );\n\n //// Update contract storage\n // Store the validator state if needed\n if (currentState == VALIDATOR_STATE.REGISTERED) {\n validatorState[pubKeyHash] = VALIDATOR_STATE.STAKED;\n }\n // Store the deposit data for verifyDeposit and verifyBalances\n deposits[validator.depositDataRoot] = DepositData({\n pubKeyHash: pubKeyHash,\n amountGwei: depositAmountGwei,\n blockNumber: SafeCast.toUint64(block.number),\n depositIndex: SafeCast.toUint32(depositsRoots.length),\n status: DepositStatus.PENDING\n });\n depositsRoots.push(validator.depositDataRoot);\n\n emit ETHStaked(\n pubKeyHash,\n validator.depositDataRoot,\n validator.pubkey,\n depositAmountWei\n );\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Request a full or partial withdrawal from a validator.\n /// A zero amount will trigger a full withdrawal.\n /// If the remaining balance is < 32 ETH then the request will fail.\n /// Only the Registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param amountGwei The amount of ETH to be withdrawn from the validator in Gwei.\n /// A zero amount will trigger a full withdrawal.\n // slither-disable-start reentrancy-no-eth\n function validatorWithdrawal(bytes calldata publicKey, uint64 amountGwei)\n external\n onlyRegistrator\n whenNotPaused\n {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n\n PartialWithdrawal.request(publicKey, amountGwei);\n\n // Do not remove from the list of verified validators.\n // This is done in the verifyBalances function once the validator's balance has been verified to be zero.\n // The validator state will be set to EXITED in the verifyBalances function.\n\n emit ValidatorWithdraw(pubKeyHash, uint256(amountGwei) * 1 gwei);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove a sweeping validator from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n // Hash the public key using the Beacon Chain's format\n bytes32 pubKeyHash = _hashPubKey(publicKey);\n VALIDATOR_STATE currentState = validatorState[pubKeyHash];\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == VALIDATOR_STATE.EXITED ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exited\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validatorState[pubKeyHash] = VALIDATOR_STATE.REMOVED;\n\n emit SSVValidatorRemoved(pubKeyHash, operatorIds);\n }\n\n /// @notice Receives requests from supported legacy strategies to consolidate sweeping validators to\n /// a new compounding validator on this new strategy.\n /// @param lastSourcePubKeyHash The last source validator to be consolidated hashed using the Beacon Chain's format.\n /// @param targetPubKeyHash The target validator's public key hash using the Beacon Chain's format.\n function requestConsolidation(\n bytes32 lastSourcePubKeyHash,\n bytes32 targetPubKeyHash\n ) external whenNotPaused {\n require(\n consolidationSourceStrategies[msg.sender],\n \"Not a source strategy\"\n );\n\n // The target validator must be a compounding validator that has been verified\n require(\n validatorState[targetPubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Target validator not verified\"\n );\n\n // Ensure balances can not be verified until after the consolidation has been verified.\n lastSnapTimestamp = 0;\n\n // Store consolidation state\n consolidationLastPubKeyHash = lastSourcePubKeyHash;\n consolidationSourceStrategy = msg.sender;\n\n // Pause the strategy while the consolidation is in progress\n _pause();\n\n emit ConsolidationRequested(\n lastSourcePubKeyHash,\n targetPubKeyHash,\n msg.sender\n );\n }\n\n /***************************************\n SSV Management\n ****************************************/\n\n // slither-disable-end reentrancy-no-eth\n\n /// `depositSSV` has been removed as `deposit` on the SSVNetwork contract can be called directly\n /// by the Strategist which is already holding SSV tokens.\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n Beacon Chain Proofs\n ****************************************/\n\n /// @notice Verifies a validator's index to its public key.\n /// @param nextBlockTimestamp The timestamp of the execution layer block after the beacon chain slot\n /// we are verifying.\n /// The next one is needed as the Beacon Oracle returns the parent beacon block root for a block timestamp,\n /// which is the beacon block root of the previous block.\n /// @param validatorIndex The index of the validator on the beacon chain.\n /// @param pubKeyHash The hash of the validator's public key using the Beacon Chain's format\n /// @param validatorPubKeyProof The merkle proof that the validator's index matches its public key\n /// on the beacon chain.\n /// BeaconBlock.state.validators[validatorIndex].pubkey\n function verifyValidator(\n uint64 nextBlockTimestamp,\n uint64 validatorIndex,\n bytes32 pubKeyHash,\n bytes calldata validatorPubKeyProof\n ) external {\n require(\n validatorState[pubKeyHash] == VALIDATOR_STATE.STAKED,\n \"Validator not staked\"\n );\n // Get the beacon block root of the slot we are verifying the validator in.\n // The parent beacon block root of the next block is the beacon block root of the slot we are verifying.\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(nextBlockTimestamp);\n\n // Verify the validator index is for the validator with the given public key\n IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n blockRoot,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n address(this) // Withdrawal address is this strategy\n );\n\n // TODO verify the validator's withdrawal credential points to this strategy\n\n // Store the validator state as verified\n validatorState[pubKeyHash] = VALIDATOR_STATE.VERIFIED;\n\n // Add the new validator to the list of verified validators\n verifiedValidators.push(\n ValidatorData({ pubKeyHash: pubKeyHash, index: validatorIndex })\n );\n\n emit ValidatorVerified(pubKeyHash, validatorIndex);\n }\n\n /// @notice Verifies a deposit on the execution layer has been processed by the beacon chain.\n /// This means the accounting of the strategy's ETH moves from a pending deposit to a validator balance.\n /// @param depositDataRoot The root of the deposit data that was stored when\n /// the deposit was made on the execution layer.\n /// @param depositBlockNumber A block number that is on or after the block the deposit\n /// was made on the execution layer.\n /// @param processedSlot Any slot on or after the deposit was processed on the beacon chain.\n /// Can not be a slot with pending deposits with the same slot as the deposit being verified.\n /// @param firstPendingDepositSlot The slot of the first pending deposit in the beacon chain\n /// @param firstPendingDepositSlotProof The merkle proof that the slot of the first pending deposit\n /// matches the beacon chain.\n /// BeaconBlock.BeaconBlockBody.deposits[0].slot\n // slither-disable-start reentrancy-no-eth\n function verifyDeposit(\n bytes32 depositDataRoot,\n uint64 depositBlockNumber,\n uint64 processedSlot,\n uint64 firstPendingDepositSlot,\n bytes calldata firstPendingDepositSlotProof\n ) external {\n // Load into memory the previously saved deposit data\n DepositData memory deposit = deposits[depositDataRoot];\n require(deposit.status == DepositStatus.PENDING, \"Deposit not pending\");\n require(\n validatorState[deposit.pubKeyHash] == VALIDATOR_STATE.VERIFIED,\n \"Validator not verified\"\n );\n // The deposit block number mapped to a slot needs to be the same block or after\n // the deposit in `stakeETH` was created on the execution layer.\n require(\n deposit.blockNumber <= depositBlockNumber,\n \"Deposit block before deposit\"\n );\n\n // Get the slot that is on or after the deposit was made on the execution layer.\n uint64 depositSlot = IBeaconOracle(BEACON_ORACLE).blockToSlot(\n depositBlockNumber\n );\n\n // Check the deposit slot is before the first pending deposit's slot on the beacon chain.\n // If this is not true then we can't guarantee the deposit has been processed by the beacon chain.\n // The deposit's slot can not be the same slot as the first pending deposit as there could be\n // many deposits in the same block, hence have the same pending deposit slot.\n // If the first pending deposit's slot is 0 then there are no pending deposits so\n // our deposit must have been processed on the beacon chain.\n require(\n depositSlot < firstPendingDepositSlot ||\n firstPendingDepositSlot == 0,\n \"Deposit not processed\"\n );\n\n // The slot of the execution layer deposit must be before\n // the slot that the deposit was processed on the beacon chain.\n require(depositSlot < processedSlot, \"Slot not after deposit\");\n\n // Get the beacon block root for the slot that is on or after the deposit was processed on the beacon chain.\n bytes32 blockRoot = IBeaconOracle(BEACON_ORACLE).slotToRoot(\n processedSlot\n );\n\n // Verify the slot of the first pending deposit matches the beacon chain\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n blockRoot,\n firstPendingDepositSlot,\n firstPendingDepositSlotProof\n );\n\n // After verifying the proof, update the contract storage\n deposits[depositDataRoot].status = DepositStatus.VERIFIED;\n // Move the last deposit to the index of the verified deposit\n bytes32 lastDepositDataRoot = depositsRoots[depositsRoots.length - 1];\n depositsRoots[deposit.depositIndex] = lastDepositDataRoot;\n deposits[lastDepositDataRoot].depositIndex = deposit.depositIndex;\n // Delete the last deposit from the list\n depositsRoots.pop();\n\n emit DepositVerified(\n depositDataRoot,\n uint256(deposit.amountGwei) * 1 gwei\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n // TODO what if the last validator was exited rather than consolidated?\n // slither-disable-start reentrancy-no-eth\n function verifyConsolidation(\n uint64 parentBlockTimestamp,\n uint64 lastValidatorIndex,\n bytes calldata validatorPubKeyProof,\n bytes32 balancesLeaf,\n bytes calldata validatorBalanceProof\n ) external onlyRegistrator {\n bytes32 consolidationLastPubKeyHashMem = consolidationLastPubKeyHash;\n require(\n consolidationLastPubKeyHashMem != bytes32(0),\n \"No consolidations\"\n );\n\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(parentBlockTimestamp);\n // Verify the validator index has the same public key as the last source validator\n IBeaconProofs(BEACON_PROOFS).verifyValidatorPubkey(\n blockRoot,\n consolidationLastPubKeyHashMem,\n validatorPubKeyProof,\n lastValidatorIndex,\n address(this) // Withdrawal address is this strategy\n );\n\n // Verify the balance of the last validator in the consolidation batch\n // is zero. If its not then the consolidation has not been completed.\n // This proof is to the beacon block root, not the balances container root.\n uint256 validatorBalance = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n blockRoot,\n balancesLeaf,\n validatorBalanceProof,\n lastValidatorIndex,\n IBeaconProofs.BalanceProofLevel.BeaconBlock\n );\n require(validatorBalance == 0, \"Last validator balance not zero\");\n\n // Call the old sweeping strategy to confirm the consolidation has been completed.\n // This will decrease the balance of the source strategy by 32 ETH for each validator being consolidated.\n uint256 consolidationCount = IConsolidationSource(\n consolidationSourceStrategy\n ).confirmConsolidation();\n\n // Increase the balance of this strategy by 32 ETH for each validator being consolidated.\n // This nets out the decrease in the source strategy's balance.\n lastVerifiedEthBalance += SafeCast.toUint128(\n consolidationCount * 32 ether\n );\n\n // Reset the stored consolidation state\n consolidationLastPubKeyHash = bytes32(0);\n consolidationSourceStrategy = address(0);\n\n emit ConsolidationVerified(\n consolidationLastPubKeyHashMem,\n lastValidatorIndex,\n consolidationCount\n );\n\n // Unpause now the balance of the target validator has been verified\n _unpause();\n\n // Take a snap of the balances so the actual balances of the new validator balances can be verified\n _snapBalances();\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Stores the current ETH balance at the current block.\n /// The validator balances on the beacon chain can then be proved with `verifyBalances`.\n /// Can only be called by the registrator.\n /// Can not be called while a consolidation is in progress.\n function snapBalances() external whenNotPaused onlyRegistrator {\n _snapBalances();\n }\n\n function _snapBalances() internal {\n bytes32 blockRoot = BeaconRoots.parentBlockRoot(\n SafeCast.toUint64(block.timestamp)\n );\n // Get the current ETH balance\n uint256 ethBalance = address(this).balance;\n\n // Store the balances in the mapping\n snappedBalances[blockRoot] = Balances({\n timestamp: SafeCast.toUint64(block.timestamp),\n ethBalance: SafeCast.toUint128(ethBalance)\n });\n\n // Store the snapped timestamp\n lastSnapTimestamp = SafeCast.toUint64(block.timestamp);\n\n emit BalancesSnapped(block.timestamp, blockRoot, ethBalance);\n }\n\n // A struct is used to avoid stack too deep errors\n struct VerifyBalancesParams {\n bytes32 blockRoot;\n uint64 firstPendingDepositSlot;\n // BeaconBlock.BeaconBlockBody.deposits[0].slot\n bytes firstPendingDepositSlotProof;\n bytes32 balancesContainerRoot;\n // BeaconBlock.state.validators\n bytes validatorContainerProof;\n bytes32[] validatorBalanceLeaves;\n // BeaconBlock.state.validators[validatorIndex].balance\n bytes[] validatorBalanceProofs;\n }\n\n /// @notice Verifies the balances of all active validators on the beacon chain\n /// and checks no pending deposits have been processed by the beacon chain.\n // slither-disable-start reentrancy-no-eth\n function verifyBalances(VerifyBalancesParams calldata params) external {\n // Load previously snapped balances for the given block root\n Balances memory balancesMem = snappedBalances[params.blockRoot];\n // Check the balances are the latest\n require(lastSnapTimestamp > 0, \"No snapped balances\");\n require(balancesMem.timestamp == lastSnapTimestamp, \"Stale snap\");\n\n uint256 depositsCount = depositsRoots.length;\n uint256 totalDepositsWei = 0;\n\n // If there are no deposits then we can skip the deposit verification\n if (depositsCount > 0) {\n // Verify the slot of the first pending deposit to the beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyFirstPendingDepositSlot(\n params.blockRoot,\n params.firstPendingDepositSlot,\n params.firstPendingDepositSlotProof\n );\n\n uint64 firstPendingDepositBlockNumber = IBeaconOracle(BEACON_ORACLE)\n .slotToBlock(params.firstPendingDepositSlot);\n\n // For each native staking contract's deposits\n for (uint256 i = 0; i < depositsCount; ++i) {\n bytes32 depositDataRoot = depositsRoots[i];\n\n // Check the stored deposit is still waiting to be processed on the beacon chain.\n // That is, the first pending deposit block number is before the\n // block number of the staking strategy's deposit.\n // If it has it will need to be verified with `verifyDeposit`\n require(\n firstPendingDepositBlockNumber <\n deposits[depositDataRoot].blockNumber,\n \"Deposit has been processed\"\n );\n\n // Convert the deposit amount from Gwei to Wei and add to the total\n totalDepositsWei +=\n uint256(deposits[depositDataRoot].amountGwei) *\n 1 gwei;\n }\n }\n\n uint256 verifiedValidatorsCount = verifiedValidators.length;\n uint256 totalValidatorBalance = 0;\n\n // If there are no verified validators then we can skip the balance verification\n if (verifiedValidatorsCount > 0) {\n require(\n params.validatorBalanceProofs.length == verifiedValidatorsCount,\n \"Invalid balance proofs\"\n );\n require(\n params.validatorBalanceLeaves.length == verifiedValidatorsCount,\n \"Invalid balance leaves\"\n );\n // verify beaconBlock.state.balances root to beacon block root\n IBeaconProofs(BEACON_PROOFS).verifyBalancesContainer(\n params.blockRoot,\n params.balancesContainerRoot,\n params.validatorContainerProof\n );\n\n // for each validator in reserve order so we can pop off exited validators at the end\n for (uint256 i = verifiedValidatorsCount; i > 0; ) {\n --i;\n // verify validator's balance in beaconBlock.state.balances to the\n // beaconBlock.state.balances container root\n uint256 validatorBalanceGwei = IBeaconProofs(BEACON_PROOFS)\n .verifyValidatorBalance(\n params.balancesContainerRoot,\n params.validatorBalanceLeaves[i],\n params.validatorBalanceProofs[i],\n verifiedValidators[i].index,\n IBeaconProofs.BalanceProofLevel.Container\n );\n\n // If the validator balance is zero\n if (validatorBalanceGwei == 0) {\n // Store the validator state as exited\n validatorState[\n verifiedValidators[i].pubKeyHash\n ] = VALIDATOR_STATE.EXITED;\n\n // Remove the validator with a zero balance from the list of verified validators\n\n // Reduce the count of verified validators which is the last index before the pop removes it.\n verifiedValidatorsCount -= 1;\n\n // Move the last validator that has already been verified to the current index.\n // There's an extra SSTORE if i is the last active validator but that's fine,\n // It's not a common case and the code is simpler this way.\n verifiedValidators[i] = verifiedValidators[\n verifiedValidatorsCount\n ];\n // Delete the last validator from the list\n verifiedValidators.pop();\n\n // The validator balance is zero so not need to add to totalValidatorBalance\n continue;\n }\n\n // convert Gwei balance to Wei and add to the total validator balance\n totalValidatorBalance += uint256(validatorBalanceGwei) * 1 gwei;\n }\n }\n\n uint256 wethBalance = IWETH9(WETH).balanceOf(address(this));\n\n // Store the verified balance in storage\n lastVerifiedEthBalance = SafeCast.toUint128(\n totalDepositsWei + totalValidatorBalance + balancesMem.ethBalance\n );\n // Reset the last snap timestamp so a new snapBalances has to be made\n lastSnapTimestamp = 0;\n\n emit BalancesVerified(\n balancesMem.timestamp,\n totalDepositsWei,\n totalValidatorBalance,\n wethBalance,\n balancesMem.ethBalance\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /***************************************\n WETH and ETH Accounting\n ****************************************/\n\n /// @dev Called when WETH is transferred out of the strategy so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _transferWeth(uint256 _amount, address _recipient) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // No change in ETH balance so no need to snapshot the balances\n }\n\n function _convertWethToEth(uint256 _ethAmount) internal {\n // slither-disable-next-line arbitrary-send-eth\n IWETH9(WETH).deposit{ value: _ethAmount }();\n\n depositedWethAccountedFor += _ethAmount;\n\n // Store the reduced ETH balance\n if (lastVerifiedEthBalance > _ethAmount) {\n lastVerifiedEthBalance -= SafeCast.toUint128(_ethAmount);\n } else {\n // This can happen if all ETH in the validators was withdrawn\n // and there was more consensus rewards since the last balance verification.\n // Or it can happen if ETH is donated to this strategy.\n lastVerifiedEthBalance = 0;\n }\n\n // The ETH balance was increased from WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n function _convertEthToWeth(uint256 _wethAmount) internal {\n IWETH9(WETH).withdraw(_wethAmount);\n\n uint256 deductAmount = Math.min(_wethAmount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n\n // Store the increased ETH balance\n lastVerifiedEthBalance += SafeCast.toUint128(_wethAmount);\n\n // The ETH balance was decreased to WETH so we need to invalidate the last balances snap.\n lastSnapTimestamp = 0;\n }\n\n /***************************************\n View Functions\n ****************************************/\n\n function getVerifiedValidators()\n external\n view\n returns (ValidatorData[] memory)\n {\n return verifiedValidators;\n }\n}\n" + }, + "contracts/strategies/NativeStaking/FeeAccumulator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\n/**\n * @title Fee Accumulator for Native Staking SSV Strategy\n * @notice Receives execution rewards which includes tx fees and\n * MEV rewards like tx priority and tx ordering.\n * It does NOT include swept ETH from beacon chain consensus rewards or full validator withdrawals.\n * @author Origin Protocol Inc\n */\ncontract FeeAccumulator {\n /// @notice The address of the Native Staking Strategy\n address public immutable STRATEGY;\n\n event ExecutionRewardsCollected(address indexed strategy, uint256 amount);\n\n /**\n * @param _strategy Address of the Native Staking Strategy\n */\n constructor(address _strategy) {\n STRATEGY = _strategy;\n }\n\n /**\n * @notice sends all ETH in this FeeAccumulator contract to the Native Staking Strategy.\n * @return eth The amount of execution rewards that were sent to the Native Staking Strategy\n */\n function collect() external returns (uint256 eth) {\n require(msg.sender == STRATEGY, \"Caller is not the Strategy\");\n\n eth = address(this).balance;\n if (eth > 0) {\n // Send the ETH to the Native Staking Strategy\n Address.sendValue(payable(STRATEGY), eth);\n\n emit ExecutionRewardsCollected(STRATEGY, eth);\n }\n }\n\n /**\n * @dev Accept ETH\n */\n receive() external payable {}\n}\n" + }, + "contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport { InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { FeeAccumulator } from \"./FeeAccumulator.sol\";\nimport { ValidatorAccountant } from \"./ValidatorAccountant.sol\";\nimport { ISSVNetwork } from \"../../interfaces/ISSVNetwork.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/// @title Native Staking SSV Strategy\n/// @notice Strategy to deploy funds into DVT validators powered by the SSV Network\n/// @author Origin Protocol Inc\n/// @dev This contract handles WETH and ETH and in some operations interchanges between the two. Any WETH that\n/// is on the contract across multiple blocks (and not just transitory within a transaction) is considered an\n/// asset. Meaning deposits increase the balance of the asset and withdrawal decrease it. As opposed to all\n/// our other strategies the WETH doesn't immediately get deposited into an underlying strategy and can be present\n/// across multiple blocks waiting to be unwrapped to ETH and staked to validators. This separation of WETH and ETH is\n/// required since the rewards (reward token) is also in ETH.\n///\n/// To simplify the accounting of WETH there is another difference in behavior compared to the other strategies.\n/// To withdraw WETH asset - exit message is posted to validators and the ETH hits this contract with multiple days\n/// delay. In order to simplify the WETH accounting upon detection of such an event the ValidatorAccountant\n/// immediately wraps ETH to WETH and sends it to the Vault.\n///\n/// On the other hand any ETH on the contract (across multiple blocks) is there either:\n/// - as a result of already accounted for consensus rewards\n/// - as a result of not yet accounted for consensus rewards\n/// - as a results of not yet accounted for full validator withdrawals (or validator slashes)\n///\n/// Even though the strategy assets and rewards are a very similar asset the consensus layer rewards and the\n/// execution layer rewards are considered rewards and those are dripped to the Vault over a configurable time\n/// interval and not immediately.\ncontract NativeStakingSSVStrategy is\n ValidatorAccountant,\n InitializableAbstractStrategy\n{\n using SafeERC20 for IERC20;\n\n /// @notice SSV ERC20 token that serves as a payment for operating SSV validators\n address public immutable SSV_TOKEN;\n /// @notice Fee collector address\n /// @dev this address will receive maximal extractable value (MEV) rewards. These are\n /// rewards for arranging transactions in a way that benefits the validator.\n address payable public immutable FEE_ACCUMULATOR_ADDRESS;\n\n /// @dev This contract receives WETH as the deposit asset, but unlike other strategies doesn't immediately\n /// deposit it to an underlying platform. Rather a special privilege account stakes it to the validators.\n /// For that reason calling WETH.balanceOf(this) in a deposit function can contain WETH that has just been\n /// deposited and also WETH that has previously been deposited. To keep a correct count we need to keep track\n /// of WETH that has already been accounted for.\n /// This value represents the amount of WETH balance of this contract that has already been accounted for by the\n /// deposit events.\n /// It is important to note that this variable is not concerned with WETH that is a result of full/partial\n /// withdrawal of the validators. It is strictly concerned with WETH that has been deposited and is waiting to\n /// be staked.\n uint256 public depositedWethAccountedFor;\n\n // For future use\n uint256[49] private __gap;\n\n /// @param _baseConfig Base strategy config with platformAddress (ERC-4626 Vault contract), eg sfrxETH or sDAI,\n /// and vaultAddress (OToken Vault contract), eg VaultProxy or OETHVaultProxy\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _ssvToken Address of the Erc20 SSV Token contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n /// @param _feeAccumulator Address of the fee accumulator receiving execution layer validator rewards\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wethAddress,\n address _ssvToken,\n address _ssvNetwork,\n uint256 _maxValidators,\n address _feeAccumulator,\n address _beaconChainDepositContract\n )\n InitializableAbstractStrategy(_baseConfig)\n ValidatorAccountant(\n _wethAddress,\n _baseConfig.vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _maxValidators\n )\n {\n SSV_TOKEN = _ssvToken;\n FEE_ACCUMULATOR_ADDRESS = payable(_feeAccumulator);\n }\n\n /// @notice Set up initial internal state including\n /// 1. approving the SSVNetwork to transfer SSV tokens from this strategy contract\n /// 2. setting the recipient of SSV validator MEV rewards to the FeeAccumulator contract.\n /// @param _rewardTokenAddresses Address of reward token for platform\n /// @param _assets Addresses of initial supported assets\n /// @param _pTokens Platform Token corresponding addresses\n function initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) external onlyGovernor initializer {\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n _pTokens\n );\n\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n\n // Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\n FEE_ACCUMULATOR_ADDRESS\n );\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just checks the asset is WETH and emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n /// @param _asset Address of asset to deposit. Has to be WETH.\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == WETH, \"Unsupported asset\");\n depositedWethAccountedFor += _amount;\n _deposit(_asset, _amount);\n }\n\n /// @dev Deposit WETH to this strategy so it can later be staked into a validator.\n /// @param _asset Address of WETH\n /// @param _amount Amount of WETH to deposit\n function _deposit(address _asset, uint256 _amount) internal {\n require(_amount > 0, \"Must deposit something\");\n /*\n * We could do a check here that would revert when \"_amount % 32 ether != 0\". With the idea of\n * not allowing deposits that will result in WETH sitting on the strategy after all the possible batches\n * of 32ETH have been staked.\n * But someone could mess with our strategy by sending some WETH to it. And we might want to deposit just\n * enough WETH to add it up to 32 so it can be staked. For that reason the check is left out.\n *\n * WETH sitting on the strategy won't interfere with the accounting since accounting only operates on ETH.\n */\n emit Deposit(_asset, address(0), _amount);\n }\n\n /// @notice Unlike other strategies, this does not deposit assets into the underlying platform.\n /// It just emits the Deposit event.\n /// To deposit WETH into validators `registerSsvValidator` and `stakeEth` must be used.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n function depositAll() external override onlyVault nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 newWeth = wethBalance - depositedWethAccountedFor;\n\n if (newWeth > 0) {\n depositedWethAccountedFor = wethBalance;\n\n _deposit(WETH, newWeth);\n }\n }\n\n /// @notice Withdraw WETH from this contract. Used only if some WETH for is lingering on the contract.\n /// That can happen when:\n /// - after mints if the strategy is the default\n /// - time between depositToStrategy and stakeEth\n /// - the deposit was not a multiple of 32 WETH\n /// - someone sent WETH directly to this contract\n /// Will NOT revert if the strategy is paused from an accounting failure.\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset WETH to withdraw\n /// @param _amount Amount of WETH to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n _wethWithdrawn(_amount);\n\n IERC20(_asset).safeTransfer(_recipient, _amount);\n emit Withdrawal(_asset, address(0), _amount);\n }\n\n /// @notice transfer all WETH deposits back to the vault.\n /// This does not withdraw from the validators. That has to be done separately with the\n /// `exitSsvValidator` and `removeSsvValidator` operations.\n /// This does not withdraw any execution rewards from the FeeAccumulator or\n /// consensus rewards in this strategy.\n /// Any ETH in this strategy that was swept from a full validator withdrawal will not be withdrawn.\n /// ETH from full validator withdrawals is sent to the Vault using `doAccounting`.\n /// Will NOT revert if the strategy is paused from an accounting failure.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 wethBalance = IERC20(WETH).balanceOf(address(this));\n if (wethBalance > 0) {\n _withdraw(vaultAddress, WETH, wethBalance);\n }\n }\n\n /// @notice Returns the total value of (W)ETH that is staked to the validators\n /// and WETH deposits that are still to be staked.\n /// This does not include ETH from consensus rewards sitting in this strategy\n /// or ETH from MEV rewards in the FeeAccumulator. These rewards are harvested\n /// and sent to the Dripper so will eventually be sent to the Vault as WETH.\n /// @param _asset Address of weth asset\n /// @return balance Total value of (W)ETH\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == WETH, \"Unsupported asset\");\n\n balance =\n // add the ETH that has been staked in validators\n activeDepositedValidators *\n FULL_STAKE +\n // add the WETH in the strategy from deposits that are still to be staked\n IERC20(WETH).balanceOf(address(this));\n }\n\n function pause() external onlyStrategist {\n _pause();\n }\n\n /// @notice Returns bool indicating whether asset is supported by strategy.\n /// @param _asset The address of the asset token.\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /// @notice Approves the SSV Network contract to transfer SSV tokens for deposits\n function safeApproveAllTokens() external override {\n // Approves the SSV Network contract to transfer SSV tokens for deposits\n IERC20(SSV_TOKEN).approve(SSV_NETWORK, type(uint256).max);\n }\n\n /// @notice Set the FeeAccumulator as the address for SSV validators to send MEV rewards to\n function setFeeRecipient() external {\n ISSVNetwork(SSV_NETWORK).setFeeRecipientAddress(\n FEE_ACCUMULATOR_ADDRESS\n );\n }\n\n /**\n * @notice Only accept ETH from the FeeAccumulator and the WETH contract - required when\n * unwrapping WETH just before staking it to the validator.\n * The strategy will also receive ETH from the priority fees of transactions when producing blocks\n * as defined in EIP-1559.\n * The tx fees come from the Beacon chain so do not need any EVM level permissions to receive ETH.\n * The tx fees are paid with each block produced. They are not included in the consensus rewards\n * which are periodically swept from the validators to this strategy.\n * For accounting purposes, the priority fees of transactions will be considered consensus rewards\n * and will be included in the AccountingConsensusRewards event.\n * @dev don't want to receive donations from anyone else as donations over the fuse limits will\n * mess with the accounting of the consensus rewards and validator full withdrawals.\n */\n receive() external payable {\n require(\n msg.sender == FEE_ACCUMULATOR_ADDRESS || msg.sender == WETH,\n \"Eth not from allowed contracts\"\n );\n }\n\n /***************************************\n Internal functions\n ****************************************/\n\n function _abstractSetPToken(address _asset, address) internal override {}\n\n /// @dev Convert accumulated ETH to WETH and send to the Harvester.\n /// Will revert if the strategy is paused for accounting.\n function _collectRewardTokens() internal override whenNotPaused {\n // collect ETH from execution rewards from the fee accumulator\n uint256 executionRewards = FeeAccumulator(FEE_ACCUMULATOR_ADDRESS)\n .collect();\n\n // total ETH rewards to be harvested = execution rewards + consensus rewards\n uint256 ethRewards = executionRewards + consensusRewards;\n\n require(\n address(this).balance >= ethRewards,\n \"Insufficient eth balance\"\n );\n\n if (ethRewards > 0) {\n // reset the counter keeping track of beacon chain consensus rewards\n consensusRewards = 0;\n\n // Convert ETH rewards to WETH\n IWETH9(WETH).deposit{ value: ethRewards }();\n\n IERC20(WETH).safeTransfer(harvesterAddress, ethRewards);\n emit RewardTokenCollected(harvesterAddress, WETH, ethRewards);\n }\n }\n\n /// @dev emits Withdrawal event from NativeStakingSSVStrategy\n function _wethWithdrawnToVault(uint256 _amount) internal override {\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _wethWithdrawn(uint256 _amount) internal override {\n /* In an ideal world we wouldn't need to reduce the deduction amount when the\n * depositedWethAccountedFor is smaller than the _amount.\n *\n * The reason this is required is that a malicious actor could sent WETH directly\n * to this contract and that would circumvent the increase of depositedWethAccountedFor\n * property. When the ETH would be staked the depositedWethAccountedFor amount could\n * be deducted so much that it would be negative.\n */\n uint256 deductAmount = Math.min(_amount, depositedWethAccountedFor);\n depositedWethAccountedFor -= deductAmount;\n }\n}\n" + }, + "contracts/strategies/NativeStaking/ValidatorAccountant.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ValidatorRegistrator } from \"./ValidatorRegistrator.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\n\n/// @title Validator Accountant\n/// @notice Attributes the ETH swept from beacon chain validators to this strategy contract\n/// as either full or partial withdrawals. Partial withdrawals being consensus rewards.\n/// Full withdrawals are from exited validators.\n/// @author Origin Protocol Inc\nabstract contract ValidatorAccountant is ValidatorRegistrator {\n /// @notice The minimum amount of blocks that need to pass between two calls to manuallyFixAccounting\n uint256 public constant MIN_FIX_ACCOUNTING_CADENCE = 7200; // 1 day\n\n /// @notice Keeps track of the total consensus rewards swept from the beacon chain\n uint256 public consensusRewards;\n\n /// @notice start of fuse interval\n uint256 public fuseIntervalStart;\n /// @notice end of fuse interval\n uint256 public fuseIntervalEnd;\n /// @notice last block number manuallyFixAccounting has been called\n uint256 public lastFixAccountingBlockNumber;\n\n uint256[49] private __gap;\n\n event FuseIntervalUpdated(uint256 start, uint256 end);\n event AccountingFullyWithdrawnValidator(\n uint256 noOfValidators,\n uint256 remainingValidators,\n uint256 wethSentToVault\n );\n event AccountingValidatorSlashed(\n uint256 remainingValidators,\n uint256 wethSentToVault\n );\n event AccountingConsensusRewards(uint256 amount);\n\n event AccountingManuallyFixed(\n int256 validatorsDelta,\n int256 consensusRewardsDelta,\n uint256 wethToVault\n );\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n uint256 _maxValidators\n )\n ValidatorRegistrator(\n _wethAddress,\n _vaultAddress,\n _beaconChainDepositContract,\n _ssvNetwork,\n _maxValidators\n )\n {}\n\n /// @notice set fuse interval values\n function setFuseInterval(\n uint256 _fuseIntervalStart,\n uint256 _fuseIntervalEnd\n ) external onlyGovernor {\n require(\n _fuseIntervalStart < _fuseIntervalEnd &&\n _fuseIntervalEnd < 32 ether &&\n _fuseIntervalEnd - _fuseIntervalStart >= 4 ether,\n \"Incorrect fuse interval\"\n );\n\n fuseIntervalStart = _fuseIntervalStart;\n fuseIntervalEnd = _fuseIntervalEnd;\n\n emit FuseIntervalUpdated(_fuseIntervalStart, _fuseIntervalEnd);\n }\n\n /* solhint-disable max-line-length */\n /// This notion page offers a good explanation of how the accounting functions\n /// https://www.notion.so/originprotocol/Limited-simplified-native-staking-accounting-67a217c8420d40678eb943b9da0ee77d\n /// In short, after dividing by 32, if the ETH remaining on the contract falls between 0 and fuseIntervalStart,\n /// the accounting function will treat that ETH as Beacon chain consensus rewards.\n /// On the contrary, if after dividing by 32, the ETH remaining on the contract falls between fuseIntervalEnd and 32,\n /// the accounting function will treat that as a validator slashing.\n /// @notice Perform the accounting attributing beacon chain ETH to either full or partial withdrawals. Returns true when\n /// accounting is valid and fuse isn't \"blown\". Returns false when fuse is blown.\n /// @dev This function could in theory be permission-less but lets allow only the Registrator (Defender Action) to call it\n /// for now.\n /// @return accountingValid true if accounting was successful, false if fuse is blown\n /* solhint-enable max-line-length */\n function doAccounting()\n external\n onlyRegistrator\n whenNotPaused\n nonReentrant\n returns (bool accountingValid)\n {\n // pause the accounting on failure\n accountingValid = _doAccounting(true);\n }\n\n // slither-disable-start reentrancy-eth\n function _doAccounting(bool pauseOnFail)\n internal\n returns (bool accountingValid)\n {\n if (address(this).balance < consensusRewards) {\n return _failAccounting(pauseOnFail);\n }\n\n // Calculate all the new ETH that has been swept to the contract since the last accounting\n uint256 newSweptETH = address(this).balance - consensusRewards;\n accountingValid = true;\n\n // send the ETH that is from fully withdrawn validators to the Vault\n if (newSweptETH >= FULL_STAKE) {\n uint256 fullyWithdrawnValidators;\n // explicitly cast to uint256 as we want to round to a whole number of validators\n fullyWithdrawnValidators = uint256(newSweptETH / FULL_STAKE);\n activeDepositedValidators -= fullyWithdrawnValidators;\n\n uint256 wethToVault = FULL_STAKE * fullyWithdrawnValidators;\n IWETH9(WETH).deposit{ value: wethToVault }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, wethToVault);\n _wethWithdrawnToVault(wethToVault);\n\n emit AccountingFullyWithdrawnValidator(\n fullyWithdrawnValidators,\n activeDepositedValidators,\n wethToVault\n );\n }\n\n uint256 ethRemaining = address(this).balance - consensusRewards;\n // should be less than a whole validator stake\n require(ethRemaining < FULL_STAKE, \"Unexpected accounting\");\n\n // If no Beacon chain consensus rewards swept\n if (ethRemaining == 0) {\n // do nothing\n return accountingValid;\n } else if (ethRemaining < fuseIntervalStart) {\n // Beacon chain consensus rewards swept (partial validator withdrawals)\n // solhint-disable-next-line reentrancy\n consensusRewards += ethRemaining;\n emit AccountingConsensusRewards(ethRemaining);\n } else if (ethRemaining > fuseIntervalEnd) {\n // Beacon chain consensus rewards swept but also a slashed validator fully exited\n IWETH9(WETH).deposit{ value: ethRemaining }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, ethRemaining);\n activeDepositedValidators -= 1;\n\n _wethWithdrawnToVault(ethRemaining);\n\n emit AccountingValidatorSlashed(\n activeDepositedValidators,\n ethRemaining\n );\n }\n // Oh no... Fuse is blown. The Strategist needs to adjust the accounting values.\n else {\n return _failAccounting(pauseOnFail);\n }\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @dev pause any further accounting if required and return false\n function _failAccounting(bool pauseOnFail)\n internal\n returns (bool accountingValid)\n {\n // pause if not already\n if (pauseOnFail) {\n _pause();\n }\n // fail the accounting\n accountingValid = false;\n }\n\n /// @notice Allow the Strategist to fix the accounting of this strategy and unpause.\n /// @param _validatorsDelta adjust the active validators by up to plus three or minus three\n /// @param _consensusRewardsDelta adjust the accounted for consensus rewards up or down\n /// @param _ethToVaultAmount the amount of ETH that gets wrapped into WETH and sent to the Vault\n /// @dev There is a case when a validator(s) gets slashed so much that the eth swept from\n /// the beacon chain enters the fuse area and there are no consensus rewards on the contract\n /// to \"dip into\"/use. To increase the amount of unaccounted ETH over the fuse end interval\n /// we need to reduce the amount of active deposited validators and immediately send WETH\n /// to the vault, so it doesn't interfere with further accounting.\n function manuallyFixAccounting(\n int256 _validatorsDelta,\n int256 _consensusRewardsDelta,\n uint256 _ethToVaultAmount\n ) external onlyStrategist whenPaused nonReentrant {\n require(\n lastFixAccountingBlockNumber + MIN_FIX_ACCOUNTING_CADENCE <\n block.number,\n \"Fix accounting called too soon\"\n );\n require(\n _validatorsDelta >= -3 &&\n _validatorsDelta <= 3 &&\n // new value must be positive\n int256(activeDepositedValidators) + _validatorsDelta >= 0,\n \"Invalid validatorsDelta\"\n );\n require(\n _consensusRewardsDelta >= -332 ether &&\n _consensusRewardsDelta <= 332 ether &&\n // new value must be positive\n int256(consensusRewards) + _consensusRewardsDelta >= 0,\n \"Invalid consensusRewardsDelta\"\n );\n require(_ethToVaultAmount <= 32 ether * 3, \"Invalid wethToVaultAmount\");\n\n activeDepositedValidators = uint256(\n int256(activeDepositedValidators) + _validatorsDelta\n );\n consensusRewards = uint256(\n int256(consensusRewards) + _consensusRewardsDelta\n );\n lastFixAccountingBlockNumber = block.number;\n if (_ethToVaultAmount > 0) {\n IWETH9(WETH).deposit{ value: _ethToVaultAmount }();\n // slither-disable-next-line unchecked-transfer\n IWETH9(WETH).transfer(VAULT_ADDRESS, _ethToVaultAmount);\n _wethWithdrawnToVault(_ethToVaultAmount);\n }\n\n emit AccountingManuallyFixed(\n _validatorsDelta,\n _consensusRewardsDelta,\n _ethToVaultAmount\n );\n\n // rerun the accounting to see if it has now been fixed.\n // Do not pause the accounting on failure as it is already paused\n require(_doAccounting(false), \"Fuse still blown\");\n\n // unpause since doAccounting was successful\n _unpause();\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n /// @dev allows for NativeStakingSSVStrategy contract to emit the Withdrawal event\n function _wethWithdrawnToVault(uint256 _amount) internal virtual;\n}\n" + }, + "contracts/strategies/NativeStaking/ValidatorRegistrator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Pausable } from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport { Governable } from \"../../governance/Governable.sol\";\nimport { IConsolidationTarget } from \"../../interfaces/IConsolidation.sol\";\nimport { IDepositContract } from \"../../interfaces/IDepositContract.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../../interfaces/IWETH9.sol\";\nimport { ISSVNetwork, Cluster } from \"../../interfaces/ISSVNetwork.sol\";\nimport { BeaconConsolidation } from \"../../beacon/BeaconConsolidation.sol\";\n\nstruct ValidatorStakeData {\n bytes pubkey;\n bytes signature;\n bytes32 depositDataRoot;\n}\n\n/**\n * @title Registrator of the validators\n * @notice This contract implements all the required functionality to register, exit and remove validators.\n * @author Origin Protocol Inc\n */\nabstract contract ValidatorRegistrator is Governable, Pausable {\n /// @notice The maximum amount of ETH that can be staked by a validator\n /// @dev this can change in the future with EIP-7251, Increase the MAX_EFFECTIVE_BALANCE\n uint256 public constant FULL_STAKE = 32 ether;\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the beacon chain deposit contract\n address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT;\n /// @notice The address of the SSV Network contract used to interface with\n address public immutable SSV_NETWORK;\n /// @notice Address of the OETH Vault proxy contract\n address public immutable VAULT_ADDRESS;\n /// @notice Maximum number of validators that can be registered in this strategy\n uint256 public immutable MAX_VALIDATORS;\n\n /// @notice Address of the registrator - allowed to register, exit and remove validators\n address public validatorRegistrator;\n /// @notice The number of validators that have 32 (!) ETH actively deposited. When a new deposit\n /// to a validator happens this number increases, when a validator exit is detected this number\n /// decreases.\n uint256 public activeDepositedValidators;\n /// @notice State of the validators keccak256(pubKey) => state\n mapping(bytes32 => VALIDATOR_STATE) public validatorsStates;\n /// @notice The account that is allowed to modify stakeETHThreshold and reset stakeETHTally\n address public stakingMonitor;\n /// @notice Amount of ETH that can be staked before staking on the contract is suspended\n /// and the `stakingMonitor` needs to approve further staking by calling `resetStakeETHTally`\n uint256 public stakeETHThreshold;\n /// @notice Amount of ETH that has been staked since the `stakingMonitor` last called `resetStakeETHTally`.\n /// This can not go above `stakeETHThreshold`.\n uint256 public stakeETHTally;\n\n /// @notice Number of validators currently being consolidated\n uint256 public consolidationCount;\n address public consolidationTargetStrategy;\n /// @notice Mapping of support target staking strategies that can be used for consolidation\n mapping(address => bool) public consolidationTargetStrategies;\n\n // For future use\n uint256[44] private __gap;\n\n enum VALIDATOR_STATE {\n NON_REGISTERED, // validator is not registered on the SSV network\n REGISTERED, // validator is registered on the SSV network\n STAKED, // validator has funds staked\n EXITING, // exit message has been posted and validator is in the process of exiting\n EXIT_COMPLETE // validator has funds withdrawn to the EigenPod and is removed from the SSV\n }\n\n event RegistratorChanged(address indexed newAddress);\n event StakingMonitorChanged(address indexed newAddress);\n event ETHStaked(bytes32 indexed pubKeyHash, bytes pubKey, uint256 amount);\n event SSVValidatorRegistered(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event SSVValidatorExitInitiated(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event SSVValidatorExitCompleted(\n bytes32 indexed pubKeyHash,\n bytes pubKey,\n uint64[] operatorIds\n );\n event ConsolidationRequested(\n bytes[] sourcePubKeys,\n bytes targetPubKey,\n address targetStakingStrategy,\n uint256 consolidationCount\n );\n event ConsolidationConfirmed(\n uint256 consolidationCount,\n uint256 activeDepositedValidators\n );\n event StakeETHThresholdChanged(uint256 amount);\n event StakeETHTallyReset();\n event TargetStrategyAdded(address indexed strategy);\n\n /// @dev Throws if called by any account other than the Registrator\n modifier onlyRegistrator() {\n require(\n msg.sender == validatorRegistrator,\n \"Caller is not the Registrator\"\n );\n _;\n }\n\n /// @dev Throws if called by any account other than the Staking monitor\n modifier onlyStakingMonitor() {\n require(msg.sender == stakingMonitor, \"Caller is not the Monitor\");\n _;\n }\n\n /// @dev Throws if called by any account other than the Strategist\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(VAULT_ADDRESS).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _vaultAddress Address of the Vault\n /// @param _beaconChainDepositContract Address of the beacon chain deposit contract\n /// @param _ssvNetwork Address of the SSV Network contract\n /// @param _maxValidators Maximum number of validators that can be registered in the strategy\n constructor(\n address _wethAddress,\n address _vaultAddress,\n address _beaconChainDepositContract,\n address _ssvNetwork,\n uint256 _maxValidators\n ) {\n WETH = _wethAddress;\n BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract;\n SSV_NETWORK = _ssvNetwork;\n VAULT_ADDRESS = _vaultAddress;\n MAX_VALIDATORS = _maxValidators;\n }\n\n /// @notice Set the address of the registrator which can register, exit and remove validators\n function setRegistrator(address _address) external onlyGovernor {\n validatorRegistrator = _address;\n emit RegistratorChanged(_address);\n }\n\n /// @notice Set the address of the staking monitor that is allowed to reset stakeETHTally\n function setStakingMonitor(address _address) external onlyGovernor {\n stakingMonitor = _address;\n emit StakingMonitorChanged(_address);\n }\n\n /// @notice Set the amount of ETH that can be staked before staking monitor\n // needs to a approve further staking by resetting the stake ETH tally\n function setStakeETHThreshold(uint256 _amount) external onlyGovernor {\n stakeETHThreshold = _amount;\n emit StakeETHThresholdChanged(_amount);\n }\n\n /// @notice Reset the stakeETHTally\n function resetStakeETHTally() external onlyStakingMonitor {\n stakeETHTally = 0;\n emit StakeETHTallyReset();\n }\n\n /// @notice Adds support for a new staking strategy that can be used for consolidation.\n function addTargetStrategy(address _strategy) external onlyGovernor {\n consolidationTargetStrategies[_strategy] = true;\n\n emit TargetStrategyAdded(_strategy);\n }\n\n /// @notice Stakes WETH to the node validators\n /// @param validators A list of validator data needed to stake.\n /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot.\n /// Only the registrator can call this function.\n // slither-disable-start reentrancy-eth\n function stakeEth(ValidatorStakeData[] calldata validators)\n external\n onlyRegistrator\n whenNotPaused\n nonReentrant\n {\n uint256 requiredETH = validators.length * FULL_STAKE;\n\n // Check there is enough WETH from the deposits sitting in this strategy contract\n require(\n requiredETH <= IWETH9(WETH).balanceOf(address(this)),\n \"Insufficient WETH\"\n );\n require(\n activeDepositedValidators + validators.length <= MAX_VALIDATORS,\n \"Max validators reached\"\n );\n\n require(\n stakeETHTally + requiredETH <= stakeETHThreshold,\n \"Staking ETH over threshold\"\n );\n stakeETHTally += requiredETH;\n\n // Convert required ETH from WETH\n IWETH9(WETH).withdraw(requiredETH);\n _wethWithdrawn(requiredETH);\n\n /* 0x01 to indicate that withdrawal credentials will contain an EOA address that the sweeping function\n * can sweep funds to.\n * bytes11(0) to fill up the required zeros\n * remaining bytes20 are for the address\n */\n bytes memory withdrawalCredentials = abi.encodePacked(\n bytes1(0x01),\n bytes11(0),\n address(this)\n );\n\n // For each validator\n for (uint256 i = 0; i < validators.length; ++i) {\n bytes32 pubKeyHash = keccak256(validators[i].pubkey);\n\n require(\n validatorsStates[pubKeyHash] == VALIDATOR_STATE.REGISTERED,\n \"Validator not registered\"\n );\n\n IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit{\n value: FULL_STAKE\n }(\n validators[i].pubkey,\n withdrawalCredentials,\n validators[i].signature,\n validators[i].depositDataRoot\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.STAKED;\n\n emit ETHStaked(pubKeyHash, validators[i].pubkey, FULL_STAKE);\n }\n // save gas by changing this storage variable only once rather each time in the loop.\n activeDepositedValidators += validators.length;\n }\n\n // slither-disable-end reentrancy-eth\n\n /// @notice Registers a new validator in the SSV Cluster.\n /// Only the registrator can call this function.\n /// @param publicKeys The public keys of the validators\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param sharesData The shares data for each validator\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function registerSsvValidators(\n bytes[] calldata publicKeys,\n uint64[] calldata operatorIds,\n bytes[] calldata sharesData,\n uint256 ssvAmount,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n require(\n publicKeys.length == sharesData.length,\n \"Pubkey sharesData mismatch\"\n );\n // Check each public key has not already been used\n bytes32 pubKeyHash;\n VALIDATOR_STATE currentState;\n for (uint256 i = 0; i < publicKeys.length; ++i) {\n pubKeyHash = keccak256(publicKeys[i]);\n currentState = validatorsStates[pubKeyHash];\n require(\n currentState == VALIDATOR_STATE.NON_REGISTERED,\n \"Validator already registered\"\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.REGISTERED;\n\n emit SSVValidatorRegistered(pubKeyHash, publicKeys[i], operatorIds);\n }\n\n ISSVNetwork(SSV_NETWORK).bulkRegisterValidator(\n publicKeys,\n operatorIds,\n sharesData,\n ssvAmount,\n cluster\n );\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Exit a validator from the Beacon chain.\n /// The staked ETH will eventually swept to this native staking strategy.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n // slither-disable-start reentrancy-no-eth\n function exitSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds\n ) external onlyRegistrator whenNotPaused {\n bytes32 pubKeyHash = keccak256(publicKey);\n VALIDATOR_STATE currentState = validatorsStates[pubKeyHash];\n require(currentState == VALIDATOR_STATE.STAKED, \"Validator not staked\");\n\n ISSVNetwork(SSV_NETWORK).exitValidator(publicKey, operatorIds);\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXITING;\n\n emit SSVValidatorExitInitiated(pubKeyHash, publicKey, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Remove a validator from the SSV Cluster.\n /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain.\n /// If removed before the validator has exited the beacon chain will result in the validator being slashed.\n /// Only the registrator can call this function.\n /// @param publicKey The public key of the validator\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n // slither-disable-start reentrancy-no-eth\n function removeSsvValidator(\n bytes calldata publicKey,\n uint64[] calldata operatorIds,\n Cluster calldata cluster\n ) external onlyRegistrator whenNotPaused {\n bytes32 pubKeyHash = keccak256(publicKey);\n VALIDATOR_STATE currentState = validatorsStates[pubKeyHash];\n // Can remove SSV validators that were incorrectly registered and can not be deposited to.\n require(\n currentState == VALIDATOR_STATE.EXITING ||\n currentState == VALIDATOR_STATE.REGISTERED,\n \"Validator not regd or exiting\"\n );\n\n ISSVNetwork(SSV_NETWORK).removeValidator(\n publicKey,\n operatorIds,\n cluster\n );\n\n validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXIT_COMPLETE;\n\n emit SSVValidatorExitCompleted(pubKeyHash, publicKey, operatorIds);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /// @notice Deposits more SSV Tokens to the SSV Network contract which is used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// uses \"onlyStrategist\" modifier so continuous front-running can't DOS our maintenance service\n /// that tries to top up SSV tokens.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function depositSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyStrategist {\n ISSVNetwork(SSV_NETWORK).deposit(\n address(this),\n operatorIds,\n ssvAmount,\n cluster\n );\n }\n\n /// @notice Withdraws excess SSV Tokens from the SSV Network contract which was used to pay the SSV Operators.\n /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds.\n /// @param operatorIds The operator IDs of the SSV Cluster\n /// @param ssvAmount The amount of SSV tokens to be deposited to the SSV cluster\n /// @param cluster The SSV cluster details including the validator count and SSV balance\n function withdrawSSV(\n uint64[] memory operatorIds,\n uint256 ssvAmount,\n Cluster memory cluster\n ) external onlyGovernor {\n ISSVNetwork(SSV_NETWORK).withdraw(operatorIds, ssvAmount, cluster);\n }\n\n /***************************************\n New Consolidation\n ****************************************/\n\n function requestConsolidation(\n bytes[] calldata sourcePubKeys,\n bytes calldata targetPubKey,\n address targetStakingStrategy\n ) external nonReentrant whenNotPaused onlyGovernor {\n require(\n consolidationTargetStrategies[targetStakingStrategy],\n \"Invalid target strategy\"\n );\n\n bytes32 targetPubKeyHash = keccak256(targetPubKey);\n bytes32 sourcePubKeyHash;\n for (uint256 i = 0; i < sourcePubKeys.length; ++i) {\n // hash the source validator's public key using the Beacon Chain's format\n sourcePubKeyHash = keccak256(sourcePubKeys[i]);\n require(sourcePubKeyHash != targetPubKeyHash, \"Self consolidation\");\n require(\n validatorsStates[sourcePubKeyHash] == VALIDATOR_STATE.STAKED,\n \"Source validator not staked\"\n );\n\n // Request consolidation from source to target validator\n BeaconConsolidation.request(sourcePubKeys[i], targetPubKey);\n\n // Store the state of the source validator as exiting so it can be removed\n // after the consolidation is confirmed\n validatorsStates[sourcePubKeyHash] == VALIDATOR_STATE.EXITING;\n }\n\n // Hash using the Beacon Chain's format\n bytes32 lastSourcePubKeyHash = _hashPubKey(\n sourcePubKeys[sourcePubKeys.length - 1]\n );\n // Call the new compounding staking strategy to validate the target validator\n IConsolidationTarget(targetStakingStrategy).requestConsolidation(\n lastSourcePubKeyHash,\n targetPubKeyHash\n );\n\n // Store the consolidation state\n consolidationCount = sourcePubKeys.length;\n consolidationTargetStrategy = targetStakingStrategy;\n\n // Pause the strategy to prevent further consolidations or validator exits\n _pause();\n\n emit ConsolidationRequested(\n sourcePubKeys,\n targetPubKey,\n targetStakingStrategy,\n sourcePubKeys.length\n );\n }\n\n function confirmConsolidation()\n external\n nonReentrant\n whenPaused\n returns (uint256 consolidationCount_)\n {\n // Check the caller is the target staking strategy\n require(\n msg.sender == consolidationTargetStrategy,\n \"Not target strategy\"\n );\n\n // Load the number of validators being consolidated into memory\n consolidationCount_ = consolidationCount;\n\n // Need to check this is from the new staking strategy\n require(consolidationCount_ > 0, \"No consolidation in progress\");\n\n // Store the reduced number of active deposited validators\n // managed by this strategy\n activeDepositedValidators -= consolidationCount_;\n\n // Reset the consolidation count\n consolidationCount = 0;\n consolidationTargetStrategy = address(0);\n\n // Unpause the strategy to allow further operations\n _unpause();\n\n emit ConsolidationConfirmed(\n consolidationCount_,\n activeDepositedValidators\n );\n }\n\n /// @notice Hash a validator public key using the Beacon Chain's format\n function _hashPubKey(bytes memory pubKey) internal pure returns (bytes32) {\n return sha256(abi.encodePacked(pubKey, bytes16(0)));\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n /// @dev Called when WETH is withdrawn from the strategy or staked to a validator so\n /// the strategy knows how much WETH it has on deposit.\n /// This is so it can emit the correct amount in the Deposit event in depositAll().\n function _wethWithdrawn(uint256 _amount) internal virtual;\n}\n" + }, + "contracts/strategies/plume/RoosterAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Rooster AMO strategy\n * @author Origin Protocol Inc\n * @custom:security-contact security@originprotocol.com\n */\nimport { Math as MathRooster } from \"../../../lib/rooster/v2-common/libraries/Math.sol\";\nimport { Math as Math_v5 } from \"../../../lib/rooster/openzeppelin-custom/contracts/utils/math/Math.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { IMaverickV2Pool } from \"../../interfaces/plume/IMaverickV2Pool.sol\";\nimport { IMaverickV2Quoter } from \"../../interfaces/plume/IMaverickV2Quoter.sol\";\nimport { IMaverickV2LiquidityManager } from \"../../interfaces/plume/IMaverickV2LiquidityManager.sol\";\nimport { IMaverickV2PoolLens } from \"../../interfaces/plume/IMaverickV2PoolLens.sol\";\nimport { IMaverickV2Position } from \"../../interfaces/plume/IMaverickV2Position.sol\";\nimport { IVotingDistributor } from \"../../interfaces/plume/IVotingDistributor.sol\";\nimport { IPoolDistributor } from \"../../interfaces/plume/IPoolDistributor.sol\";\n// importing custom version of rooster TickMath because of dependency collision. Maverick uses\n// a newer OpenZepplin Math library with functionality that is not present in 4.4.2 (the one we use)\nimport { TickMath } from \"../../../lib/rooster/v2-common/libraries/TickMath.sol\";\n\ncontract RoosterAMOStrategy is InitializableAbstractStrategy {\n using StableMath for uint256;\n using SafeERC20 for IERC20;\n using SafeCast for uint256;\n\n /***************************************\n Storage slot members\n ****************************************/\n\n /// @notice NFT tokenId of the liquidity position\n ///\n /// @dev starts with value of 1 and can not be 0\n // solhint-disable-next-line max-line-length\n /// https://github.com/rooster-protocol/rooster-contracts/blob/fbfecbc519e4495b12598024a42630b4a8ea4489/v2-common/contracts/base/Nft.sol#L14\n uint256 public tokenId;\n /// @dev Minimum amount of tokens the strategy would be able to withdraw from the pool.\n /// minimum amount of tokens are withdrawn at a 1:1 price\n /// Important: Underlying assets contains only assets that are deposited in the underlying Rooster pool.\n /// WETH or OETH held by this contract is not accounted for in underlying assets\n uint256 public underlyingAssets;\n /// @notice Marks the start of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareStart;\n /// @notice Marks the end of the interval that defines the allowed range of WETH share in\n /// the pre-configured pool's liquidity ticker\n uint256 public allowedWethShareEnd;\n /// @dev reserved for inheritance\n int256[46] private __reserved;\n\n /***************************************\n Constants, structs and events\n ****************************************/\n\n /// @notice The address of the Wrapped ETH (WETH) token contract\n address public immutable WETH;\n /// @notice The address of the OETH token contract\n address public immutable OETH;\n /// @notice the underlying AMO Maverick (Rooster) pool\n IMaverickV2Pool public immutable mPool;\n /// @notice the Liquidity manager used to add liquidity to the pool\n IMaverickV2LiquidityManager public immutable liquidityManager;\n /// @notice the Maverick V2 poolLens\n ///\n /// @dev only used to provide the pool's current sqrtPrice\n IMaverickV2PoolLens public immutable poolLens;\n /// @notice the Maverick V2 position\n ///\n /// @dev provides details of the NFT LP position and offers functions to\n /// remove the liquidity.\n IMaverickV2Position public immutable maverickPosition;\n /// @notice the Maverick Quoter\n IMaverickV2Quoter public immutable quoter;\n /// @notice the Maverick Voting Distributor\n IVotingDistributor public immutable votingDistributor;\n /// @notice the Maverick Pool Distributor\n IPoolDistributor public immutable poolDistributor;\n\n /// @notice sqrtPriceTickLower\n /// @dev tick lower represents the lower price of OETH priced in WETH. Meaning the pool\n /// offers more than 1 OETH for 1 WETH. In other terms to get 1 OETH the swap needs to offer 0.9999 WETH\n /// this is where purchasing OETH with WETH within the liquidity position is the cheapest.\n ///\n /// _____________________\n /// | | |\n /// | WETH | OETH |\n /// | | |\n /// | | |\n /// --------- * ---- * ---------- * ---------\n /// currentPrice\n /// sqrtPriceHigher-(1:1 parity)\n /// sqrtPriceLower\n ///\n ///\n /// Price is defined as price of token1 in terms of token0. (token1 / token0)\n /// @notice sqrtPriceTickLower - OETH is priced 0.9999 WETH\n uint256 public immutable sqrtPriceTickLower;\n /// @notice sqrtPriceTickHigher\n /// @dev tick higher represents 1:1 price parity of WETH to OETH\n uint256 public immutable sqrtPriceTickHigher;\n /// @dev price at parity (in OETH this is equal to sqrtPriceTickHigher)\n uint256 public immutable sqrtPriceAtParity;\n /// @notice The tick where the strategy deploys the liquidity to\n int32 public constant TICK_NUMBER = -1;\n /// @notice Minimum liquidity that must be exceeded to continue with the action\n /// e.g. deposit, add liquidity\n uint256 public constant ACTION_THRESHOLD = 1e12;\n /// @notice Maverick pool static liquidity bin type\n uint8 public constant MAV_STATIC_BIN_KIND = 0;\n /// @dev a threshold under which the contract no longer allows for the protocol to rebalance. Guarding\n /// against a strategist / guardian being taken over and with multiple transactions draining the\n /// protocol funds.\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n /// @notice Emitted when the allowed interval within which the strategy contract is allowed to deposit\n /// liquidity to the underlying pool is updated.\n /// @param allowedWethShareStart The start of the interval\n /// @param allowedWethShareEnd The end of the interval\n event PoolWethShareIntervalUpdated(\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n );\n /// @notice Emitted when liquidity is removed from the underlying pool\n /// @param withdrawLiquidityShare Share of strategy's liquidity that has been removed\n /// @param removedWETHAmount The amount of WETH removed\n /// @param removedOETHAmount The amount of OETH removed\n /// @param underlyingAssets Updated amount of strategy's underlying assets\n event LiquidityRemoved(\n uint256 withdrawLiquidityShare,\n uint256 removedWETHAmount,\n uint256 removedOETHAmount,\n uint256 underlyingAssets\n );\n\n /// @notice Emitted when the underlying pool is rebalanced\n /// @param currentPoolWethShare The resulting share of strategy's liquidity\n /// in the TICK_NUMBER\n event PoolRebalanced(uint256 currentPoolWethShare);\n\n /// @notice Emitted when the amount of underlying assets the strategy hold as\n /// liquidity in the pool is updated.\n /// @param underlyingAssets Updated amount of strategy's underlying assets\n event UnderlyingAssetsUpdated(uint256 underlyingAssets);\n\n /// @notice Emitted when liquidity is added to the underlying pool\n /// @param wethAmountDesired Amount of WETH desired to be deposited\n /// @param oethAmountDesired Amount of OETH desired to be deposited\n /// @param wethAmountSupplied Amount of WETH deposited\n /// @param oethAmountSupplied Amount of OETH deposited\n /// @param tokenId NFT liquidity token id\n /// @param underlyingAssets Updated amount of underlying assets\n event LiquidityAdded(\n uint256 wethAmountDesired,\n uint256 oethAmountDesired,\n uint256 wethAmountSupplied,\n uint256 oethAmountSupplied,\n uint256 tokenId,\n uint256 underlyingAssets\n ); // 0x1530ec74\n\n error PoolRebalanceOutOfBounds(\n uint256 currentPoolWethShare,\n uint256 allowedWethShareStart,\n uint256 allowedWethShareEnd\n ); // 0x3681e8e0\n\n error NotEnoughWethForSwap(uint256 wethBalance, uint256 requiredWeth); // 0x989e5ca8\n error NotEnoughWethLiquidity(uint256 wethBalance, uint256 requiredWeth); // 0xa6737d87\n error OutsideExpectedTickRange(); // 0xa6e1bad2\n error SlippageCheck(uint256 tokenReceived); // 0x355cdb78\n\n /// @notice the constructor\n /// @dev This contract is intended to be used as a proxy. To prevent the\n /// potential confusion of having a functional implementation contract\n /// the constructor has the `initializer` modifier. This way the\n /// `initialize` function can not be called on the implementation contract.\n /// For the same reason the implementation contract also has the governor\n /// set to a zero address.\n /// @param _stratConfig the basic strategy configuration\n /// @param _wethAddress Address of the Erc20 WETH Token contract\n /// @param _oethAddress Address of the Erc20 OETH Token contract\n /// @param _liquidityManager Address of liquidity manager to add\n /// the liquidity\n /// @param _poolLens Address of the pool lens contract\n /// @param _maverickPosition Address of the Maverick's position contract\n /// @param _maverickQuoter Address of the Maverick's Quoter contract\n /// @param _mPool Address of the Rooster concentrated liquidity pool\n /// @param _upperTickAtParity Bool when true upperTick is the one where the\n /// price of OETH and WETH are at parity\n constructor(\n BaseStrategyConfig memory _stratConfig,\n address _wethAddress,\n address _oethAddress,\n address _liquidityManager,\n address _poolLens,\n address _maverickPosition,\n address _maverickQuoter,\n address _mPool,\n bool _upperTickAtParity,\n address _votingDistributor,\n address _poolDistributor\n ) initializer InitializableAbstractStrategy(_stratConfig) {\n require(\n address(IMaverickV2Pool(_mPool).tokenA()) == _wethAddress,\n \"WETH not TokenA\"\n );\n require(\n address(IMaverickV2Pool(_mPool).tokenB()) == _oethAddress,\n \"OETH not TokenB\"\n );\n require(\n _liquidityManager != address(0),\n \"LiquidityManager zero address not allowed\"\n );\n require(\n _maverickQuoter != address(0),\n \"Quoter zero address not allowed\"\n );\n require(_poolLens != address(0), \"PoolLens zero address not allowed\");\n require(\n _maverickPosition != address(0),\n \"Position zero address not allowed\"\n );\n require(\n _votingDistributor != address(0),\n \"Voting distributor zero address not allowed\"\n );\n require(\n _poolDistributor != address(0),\n \"Pool distributor zero address not allowed\"\n );\n\n uint256 _tickSpacing = IMaverickV2Pool(_mPool).tickSpacing();\n require(_tickSpacing == 1, \"Unsupported tickSpacing\");\n\n // tickSpacing == 1\n (sqrtPriceTickLower, sqrtPriceTickHigher) = TickMath.tickSqrtPrices(\n _tickSpacing,\n TICK_NUMBER\n );\n sqrtPriceAtParity = _upperTickAtParity\n ? sqrtPriceTickHigher\n : sqrtPriceTickLower;\n\n WETH = _wethAddress;\n OETH = _oethAddress;\n liquidityManager = IMaverickV2LiquidityManager(_liquidityManager);\n poolLens = IMaverickV2PoolLens(_poolLens);\n maverickPosition = IMaverickV2Position(_maverickPosition);\n quoter = IMaverickV2Quoter(_maverickQuoter);\n mPool = IMaverickV2Pool(_mPool);\n votingDistributor = IVotingDistributor(_votingDistributor);\n poolDistributor = IPoolDistributor(_poolDistributor);\n\n // prevent implementation contract to be governed\n _setGovernor(address(0));\n }\n\n /**\n * @notice initialize function, to set up initial internal state\n */\n function initialize() external onlyGovernor initializer {\n // Read reward\n address[] memory _rewardTokens = new address[](1);\n _rewardTokens[0] = poolDistributor.rewardToken();\n\n require(_rewardTokens[0] != address(0), \"No reward token configured\");\n\n InitializableAbstractStrategy._initialize(\n _rewardTokens,\n new address[](0),\n new address[](0)\n );\n }\n\n /***************************************\n Configuration \n ****************************************/\n\n /**\n * @notice Set allowed pool weth share interval. After the rebalance happens\n * the share of WETH token in the ticker needs to be within the specifications\n * of the interval.\n *\n * @param _allowedWethShareStart Start of WETH share interval expressed as 18 decimal amount\n * @param _allowedWethShareEnd End of WETH share interval expressed as 18 decimal amount\n */\n function setAllowedPoolWethShareInterval(\n uint256 _allowedWethShareStart,\n uint256 _allowedWethShareEnd\n ) external onlyGovernor {\n require(\n _allowedWethShareStart < _allowedWethShareEnd,\n \"Invalid interval\"\n );\n // can not go below 1% weth share\n require(_allowedWethShareStart > 0.01 ether, \"Invalid interval start\");\n // can not go above 95% weth share\n require(_allowedWethShareEnd < 0.95 ether, \"Invalid interval end\");\n\n allowedWethShareStart = _allowedWethShareStart;\n allowedWethShareEnd = _allowedWethShareEnd;\n emit PoolWethShareIntervalUpdated(\n _allowedWethShareStart,\n _allowedWethShareEnd\n );\n }\n\n /***************************************\n Strategy overrides \n ****************************************/\n\n /**\n * @notice Deposits funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposits all the funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n */\n function depositAll() external override onlyVault nonReentrant {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n _deposit(WETH, _wethBalance);\n }\n\n /**\n * @dev Deposits funds to the strategy which deposits them to the\n * underlying Rooster pool if the pool price is within the expected interval.\n * Before this function can be called the initial pool position needs to already\n * be minted.\n * @param _asset Address of the asset to deposit\n * @param _amount Amount of assets to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must deposit something\");\n require(tokenId > 0, \"Initial position not minted\");\n emit Deposit(_asset, address(0), _amount);\n\n // if the pool price is not within the expected interval leave the WETH on the contract\n // as to not break the mints - in case it would be configured as a default asset strategy\n (bool _isExpectedRange, ) = _checkForExpectedPoolPrice(false);\n if (_isExpectedRange) {\n // deposit funds into the underlying pool. Because no swap is performed there is no\n // need to remove any of the liquidity beforehand.\n _rebalance(0, false, 0, 0);\n }\n }\n\n /**\n * @notice Withdraw an `amount` of WETH from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset WETH address\n * @param _amount Amount of WETH to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == WETH, \"Unsupported asset\");\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n _ensureWETHBalance(_amount);\n\n _withdraw(_recipient, _amount);\n }\n\n /**\n * @notice Withdraw WETH and sends it to the Vault.\n */\n function withdrawAll() external override onlyVault nonReentrant {\n if (tokenId != 0) {\n _removeLiquidity(1e18);\n }\n\n uint256 _balance = IERC20(WETH).balanceOf(address(this));\n if (_balance > 0) {\n _withdraw(vaultAddress, _balance);\n }\n }\n\n function _withdraw(address _recipient, uint256 _amount) internal {\n IERC20(WETH).safeTransfer(_recipient, _amount);\n emit Withdrawal(WETH, address(0), _amount);\n }\n\n /**\n * @dev Retuns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n * @return bool True when the _asset is WETH\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == WETH;\n }\n\n /**\n * @dev Approve the spending amounts for the assets\n */\n function _approveTokenAmounts(\n uint256 _wethAllowance,\n uint256 _oethAllowance\n ) internal {\n IERC20(WETH).approve(address(liquidityManager), _wethAllowance);\n IERC20(OETH).approve(address(liquidityManager), _oethAllowance);\n }\n\n /***************************************\n Liquidity management\n ****************************************/\n /**\n * @dev Add liquidity into the pool in the pre-configured WETH to OETH share ratios\n * defined by the allowedPoolWethShareStart|End interval.\n *\n * Normally a PoolLens contract is used to prepare the parameters to add liquidity to the\n * Rooster pools. It has some errors when doing those calculation and for that reason a\n * much more accurate Quoter contract is used. This is possible due to our requirement of\n * adding liquidity only to one tick - PoolLens supports adding liquidity into multiple ticks\n * using different distribution ratios.\n */\n function _addLiquidity() internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n // don't deposit small liquidity amounts\n if (_wethBalance <= ACTION_THRESHOLD) {\n return;\n }\n\n (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n ) = _getAddLiquidityParams(_wethBalance, 1e30);\n\n if (OETHRequired > _oethBalance) {\n IVault(vaultAddress).mintForStrategy(OETHRequired - _oethBalance);\n }\n\n _approveTokenAmounts(WETHRequired, OETHRequired);\n\n (\n uint256 _wethAmount,\n uint256 _oethAmount,\n uint32[] memory binIds\n ) = liquidityManager.addPositionLiquidityToSenderByTokenIndex(\n mPool,\n 0, // NFT token index\n packedSqrtPriceBreaks,\n packedArgs\n );\n\n require(binIds.length == 1, \"Unexpected binIds length\");\n\n // burn remaining OETH\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n\n // needs to be called after _updateUnderlyingAssets so the updated amount\n // is reflected in the event\n emit LiquidityAdded(\n _wethBalance, // wethAmountDesired\n OETHRequired, // oethAmountDesired\n _wethAmount, // wethAmountSupplied\n _oethAmount, // oethAmountSupplied\n tokenId, // tokenId\n underlyingAssets\n );\n }\n\n /**\n * @dev The function creates liquidity parameters required to be able to add liquidity to the pool.\n * The function needs to handle the 3 different cases of the way liquidity is added:\n * - only WETH present in the tick\n * - only OETH present in the tick\n * - both tokens present in the tick\n *\n */\n function _getAddLiquidityParams(uint256 _maxWETH, uint256 _maxOETH)\n internal\n returns (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n )\n {\n IMaverickV2Pool.AddLiquidityParams[]\n memory addParams = new IMaverickV2Pool.AddLiquidityParams[](1);\n int32[] memory ticks = new int32[](1);\n uint128[] memory amounts = new uint128[](1);\n ticks[0] = TICK_NUMBER;\n // arbitrary LP amount\n amounts[0] = 1e24;\n\n // construct value for Quoter with arbitrary LP amount\n IMaverickV2Pool.AddLiquidityParams memory addParam = IMaverickV2Pool\n .AddLiquidityParams({\n kind: MAV_STATIC_BIN_KIND,\n ticks: ticks,\n amounts: amounts\n });\n\n // get the WETH and OETH required to get the proportion of tokens required\n // given the arbitrary liquidity\n (WETHRequired, OETHRequired, ) = quoter.calculateAddLiquidity(\n mPool,\n addParam\n );\n\n /**\n * If either token required is 0 then the tick consists only of the other token. In that\n * case the liquidity calculations need to be done using the non 0 token. By setting the\n * tokenRequired from 0 to 1 the `min` in next step will ignore that (the bigger) value.\n */\n WETHRequired = WETHRequired == 0 ? 1 : WETHRequired;\n OETHRequired = OETHRequired == 0 ? 1 : OETHRequired;\n\n addParam.amounts[0] = Math_v5\n .min(\n ((_maxWETH - 1) * 1e24) / WETHRequired,\n ((_maxOETH - 1) * 1e24) / OETHRequired\n )\n .toUint128();\n\n // update the quotes with the actual amounts\n (WETHRequired, OETHRequired, ) = quoter.calculateAddLiquidity(\n mPool,\n addParam\n );\n\n require(_maxWETH >= WETHRequired, \"More WETH required than specified\");\n require(_maxOETH >= OETHRequired, \"More OETH required than specified\");\n\n // organize values to be used by manager\n addParams[0] = addParam;\n packedArgs = liquidityManager.packAddLiquidityArgsArray(addParams);\n // price can stay 0 if array only has one element\n packedSqrtPriceBreaks = liquidityManager.packUint88Array(\n new uint88[](1)\n );\n }\n\n /**\n * @dev Check that the Rooster pool price is within the expected\n * parameters.\n * This function works whether the strategy contract has liquidity\n * position in the pool or not. The function returns _wethSharePct\n * as a gas optimization measure.\n * @param _throwException when set to true the function throws an exception\n * when pool's price is not within expected range.\n * @return _isExpectedRange Bool expressing price is within expected range\n * @return _wethSharePct Share of WETH owned by this strategy contract in the\n * configured ticker.\n */\n function _checkForExpectedPoolPrice(bool _throwException)\n internal\n view\n returns (bool _isExpectedRange, uint256 _wethSharePct)\n {\n require(\n allowedWethShareStart != 0 && allowedWethShareEnd != 0,\n \"Weth share interval not set\"\n );\n\n uint256 _currentPrice = getPoolSqrtPrice();\n\n /**\n * First check pool price is in expected tick range\n *\n * A revert is issued even though price being equal to the lower bound as that can not\n * be within the approved tick range.\n */\n if (\n _currentPrice <= sqrtPriceTickLower ||\n _currentPrice >= sqrtPriceTickHigher\n ) {\n if (_throwException) {\n revert OutsideExpectedTickRange();\n }\n\n return (false, _currentPrice <= sqrtPriceTickLower ? 0 : 1e18);\n }\n\n // 18 decimal number expressed WETH tick share\n _wethSharePct = _getWethShare(_currentPrice);\n\n if (\n _wethSharePct < allowedWethShareStart ||\n _wethSharePct > allowedWethShareEnd\n ) {\n if (_throwException) {\n revert PoolRebalanceOutOfBounds(\n _wethSharePct,\n allowedWethShareStart,\n allowedWethShareEnd\n );\n }\n return (false, _wethSharePct);\n }\n\n return (true, _wethSharePct);\n }\n\n /**\n * @notice Rebalance the pool to the desired token split and Deposit any WETH on the contract to the\n * underlying rooster pool. Print the required amount of corresponding OETH. After the rebalancing is\n * done burn any potentially remaining OETH tokens still on the strategy contract.\n *\n * This function has a slightly different behaviour depending on the status of the underlying Rooster\n * pool. The function consists of the following 3 steps:\n * 1. withdrawLiquidityOption -> this is a configurable option where either only part of the liquidity\n * necessary for the swap is removed, or all of it. This way the rebalance\n * is able to optimize for volume, for efficiency or anything in between\n * 2. swapToDesiredPosition -> move active trading price in the pool to be able to deposit WETH & OETH\n * tokens with the desired pre-configured ratios\n * 3. addLiquidity -> add liquidity into the pool respecting ratio split configuration\n *\n *\n * Exact _amountToSwap, _swapWeth & _minTokenReceived parameters shall be determined by simulating the\n * transaction off-chain. The strategy checks that after the swap the share of the tokens is in the\n * expected ranges.\n *\n * @param _amountToSwap The amount of the token to swap\n * @param _swapWeth Swap using WETH when true, use OETH when false\n * @param _minTokenReceived Slippage check -> minimum amount of token expected in return\n * @param _liquidityToRemovePct Percentage of liquidity to remove -> the percentage amount of liquidity to\n * remove before performing the swap. 1e18 denominated\n */\n function rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived,\n uint256 _liquidityToRemovePct\n ) external nonReentrant onlyGovernorOrStrategist {\n _rebalance(\n _amountToSwap,\n _swapWeth,\n _minTokenReceived,\n _liquidityToRemovePct\n );\n }\n\n // slither-disable-start reentrancy-no-eth\n function _rebalance(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived,\n uint256 _liquidityToRemovePct\n ) internal {\n // Remove the required amount of liquidity\n if (_liquidityToRemovePct > 0) {\n _removeLiquidity(_liquidityToRemovePct);\n }\n\n // in some cases (e.g. deposits) we will just want to add liquidity and not\n // issue a swap to move the active trading position within the pool. Before or after a\n // deposit or as a standalone call the strategist might issue a rebalance to move the\n // active trading price to a more desired position.\n if (_amountToSwap > 0) {\n // In case liquidity has been removed and there is still not enough WETH owned by the\n // strategy contract remove additional required amount of WETH.\n if (_swapWeth) _ensureWETHBalance(_amountToSwap);\n\n _swapToDesiredPosition(_amountToSwap, _swapWeth, _minTokenReceived);\n }\n\n // calling check liquidity early so we don't get unexpected errors when adding liquidity\n // in the later stages of this function\n _checkForExpectedPoolPrice(true);\n\n _addLiquidity();\n\n // this call shouldn't be necessary, since adding liquidity shouldn't affect the active\n // trading price. It is a defensive programming measure.\n (, uint256 _wethSharePct) = _checkForExpectedPoolPrice(true);\n\n // revert if protocol insolvent\n _solvencyAssert();\n\n emit PoolRebalanced(_wethSharePct);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @dev Perform a swap so that after the swap the tick has the desired WETH to OETH token share.\n */\n function _swapToDesiredPosition(\n uint256 _amountToSwap,\n bool _swapWeth,\n uint256 _minTokenReceived\n ) internal {\n IERC20 _tokenToSwap = IERC20(_swapWeth ? WETH : OETH);\n uint256 _balance = _tokenToSwap.balanceOf(address(this));\n\n if (_balance < _amountToSwap) {\n // This should never trigger since _ensureWETHBalance will already\n // throw an error if there is not enough WETH\n if (_swapWeth) {\n revert NotEnoughWethForSwap(_balance, _amountToSwap);\n }\n // if swapping OETH\n uint256 mintForSwap = _amountToSwap - _balance;\n IVault(vaultAddress).mintForStrategy(mintForSwap);\n }\n\n // SafeERC20 is used for IERC20 transfers. Not sure why slither complains\n // slither-disable-next-line unchecked-transfer\n _tokenToSwap.transfer(address(mPool), _amountToSwap);\n\n // tickLimit: the furthest tick a swap will execute in. If no limit is desired,\n // value should be set to type(int32).max for a tokenAIn (WETH) swap\n // and type(int32).min for a swap where tokenB (OETH) is the input\n\n IMaverickV2Pool.SwapParams memory swapParams = IMaverickV2Pool\n // exactOutput defines whether the amount specified is the output\n // or the input amount of the swap\n .SwapParams({\n amount: _amountToSwap,\n tokenAIn: _swapWeth,\n exactOutput: false,\n tickLimit: TICK_NUMBER\n });\n\n // swaps without a callback as the assets are already sent to the pool\n (, uint256 amountOut) = mPool.swap(\n address(this),\n swapParams,\n bytes(\"\")\n );\n\n /**\n * There could be additional checks here for validating minTokenReceived is within the\n * expected range (e.g. 99% - 101% of the token sent in). Though that doesn't provide\n * any additional security. After the swap the `_checkForExpectedPoolPrice` validates\n * that the swap has moved the price into the expected tick (# -1).\n *\n * If the guardian forgets to set a `_minTokenReceived` and a sandwich attack bends\n * the pool before the swap the `_checkForExpectedPoolPrice` will fail the transaction.\n *\n * A check would not prevent a compromised guardian from stealing funds as multiple\n * transactions each loosing smaller amount of funds are still possible.\n */\n if (amountOut < _minTokenReceived) {\n revert SlippageCheck(amountOut);\n }\n\n /**\n * In the interest of each function in `_rebalance` to leave the contract state as\n * clean as possible the OETH tokens here are burned. This decreases the\n * dependence where `_swapToDesiredPosition` function relies on later functions\n * (`addLiquidity`) to burn the OETH. Reducing the risk of error introduction.\n */\n _burnOethOnTheContract();\n }\n\n /**\n * @dev This function removes the appropriate amount of liquidity to ensure that the required\n * amount of WETH is available on the contract\n *\n * @param _amount WETH balance required on the contract\n */\n function _ensureWETHBalance(uint256 _amount) internal {\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n if (_wethBalance >= _amount) {\n return;\n }\n\n require(tokenId != 0, \"No liquidity available\");\n uint256 _additionalWethRequired = _amount - _wethBalance;\n (uint256 _wethInThePool, ) = getPositionPrincipal();\n\n if (_wethInThePool < _additionalWethRequired) {\n revert NotEnoughWethLiquidity(\n _wethInThePool,\n _additionalWethRequired\n );\n }\n\n uint256 shareOfWethToRemove = _wethInThePool <= 1\n ? 1e18\n : Math_v5.min(\n /**\n * When dealing with shares of liquidity to remove there is always some\n * rounding involved. After extensive fuzz testing the below approach\n * yielded the best results where the strategy overdraws the least and\n * never removes insufficient amount of WETH.\n */\n (_additionalWethRequired + 2).divPrecisely(_wethInThePool - 1) +\n 2,\n 1e18\n );\n\n _removeLiquidity(shareOfWethToRemove);\n }\n\n /**\n * @dev Decrease partial or all liquidity from the pool.\n * @param _liquidityToDecrease The amount of liquidity to remove denominated in 1e18\n */\n function _removeLiquidity(uint256 _liquidityToDecrease) internal {\n require(_liquidityToDecrease > 0, \"Must remove some liquidity\");\n require(\n _liquidityToDecrease <= 1e18,\n \"Can not remove more than 100% of liquidity\"\n );\n\n // 0 indicates the first (and only) bin in the NFT LP position.\n IMaverickV2Pool.RemoveLiquidityParams memory params = maverickPosition\n .getRemoveParams(tokenId, 0, _liquidityToDecrease);\n (uint256 _amountWeth, uint256 _amountOeth) = maverickPosition\n .removeLiquidityToSender(tokenId, mPool, params);\n\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n\n // needs to be called after the _updateUnderlyingAssets so the updated amount is reflected\n // in the event\n emit LiquidityRemoved(\n _liquidityToDecrease,\n _amountWeth,\n _amountOeth,\n underlyingAssets\n );\n }\n\n /**\n * @dev Burns any OETH tokens remaining on the strategy contract if the balance is\n * above the action threshold.\n */\n function _burnOethOnTheContract() internal {\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(_oethBalance);\n }\n\n /**\n * @notice Returns the percentage of WETH liquidity in the configured ticker\n * owned by this strategy contract.\n * @return uint256 1e18 denominated percentage expressing the share\n */\n function getWETHShare() external view returns (uint256) {\n uint256 _currentPrice = getPoolSqrtPrice();\n return _getWethShare(_currentPrice);\n }\n\n /**\n * @dev Returns the share of WETH in tick denominated in 1e18\n */\n function _getWethShare(uint256 _currentPrice)\n internal\n view\n returns (uint256)\n {\n (\n uint256 wethAmount,\n uint256 oethAmount\n ) = _reservesInTickForGivenPriceAndLiquidity(\n sqrtPriceTickLower,\n sqrtPriceTickHigher,\n _currentPrice,\n 1e24\n );\n\n return wethAmount.divPrecisely(wethAmount + oethAmount);\n }\n\n /**\n * @notice Returns the current pool price in square root\n * @return Square root of the pool price\n */\n function getPoolSqrtPrice() public view returns (uint256) {\n return poolLens.getPoolSqrtPrice(mPool);\n }\n\n /**\n * @notice Returns the current active trading tick of the underlying pool\n * @return _currentTick Current pool trading tick\n */\n function getCurrentTradingTick() public view returns (int32 _currentTick) {\n _currentTick = mPool.getState().activeTick;\n }\n\n /**\n * @notice Mint the initial NFT position\n *\n * @dev This amount is \"gifted\" to the strategy contract and will count as a yield\n * surplus.\n */\n // slither-disable-start reentrancy-no-eth\n function mintInitialPosition() external onlyGovernor nonReentrant {\n require(tokenId == 0, \"Initial position already minted\");\n (\n bytes memory packedSqrtPriceBreaks,\n bytes[] memory packedArgs,\n uint256 WETHRequired,\n uint256 OETHRequired\n ) = _getAddLiquidityParams(1e16, 1e16);\n\n // Mint rounded up OETH amount\n if (OETHRequired > 0) {\n IVault(vaultAddress).mintForStrategy(OETHRequired);\n }\n\n _approveTokenAmounts(WETHRequired, OETHRequired);\n\n // Store the tokenId before calling updateUnderlyingAssets as it relies on the tokenId\n // not being 0\n (, , , tokenId) = liquidityManager.mintPositionNftToSender(\n mPool,\n packedSqrtPriceBreaks,\n packedArgs\n );\n\n // burn remaining OETH\n _burnOethOnTheContract();\n _updateUnderlyingAssets();\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @notice Returns the balance of tokens the strategy holds in the LP position\n * @return _amountWeth Amount of WETH in position\n * @return _amountOeth Amount of OETH in position\n */\n function getPositionPrincipal()\n public\n view\n returns (uint256 _amountWeth, uint256 _amountOeth)\n {\n if (tokenId == 0) {\n return (0, 0);\n }\n\n (_amountWeth, _amountOeth, ) = _getPositionInformation();\n }\n\n /**\n * @dev Returns the balance of tokens the strategy holds in the LP position\n * @return _amountWeth Amount of WETH in position\n * @return _amountOeth Amount of OETH in position\n * @return liquidity Amount of liquidity in the position\n */\n function _getPositionInformation()\n internal\n view\n returns (\n uint256 _amountWeth,\n uint256 _amountOeth,\n uint256 liquidity\n )\n {\n IMaverickV2Position.PositionFullInformation\n memory positionInfo = maverickPosition.tokenIdPositionInformation(\n tokenId,\n 0\n );\n\n require(\n positionInfo.liquidities.length == 1,\n \"Unexpected liquidities length\"\n );\n require(positionInfo.ticks.length == 1, \"Unexpected ticks length\");\n\n _amountWeth = positionInfo.amountA;\n _amountOeth = positionInfo.amountB;\n liquidity = positionInfo.liquidities[0];\n }\n\n /**\n * Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99.8%) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalOethSupply = IERC20(OETH).totalSupply();\n\n if (\n _totalVaultValue.divPrecisely(_totalOethSupply) < SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /**\n * @dev Collect Rooster reward token, and send it to the harvesterAddress\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Do nothing if there's no position minted\n if (tokenId > 0) {\n uint32[] memory binIds = new uint32[](1);\n IMaverickV2Pool.TickState memory tickState = mPool.getTick(\n TICK_NUMBER\n );\n // get the binId for the MAV_STATIC_BIN_KIND in tick TICK_NUMBER (-1)\n binIds[0] = tickState.binIdsByTick[0];\n\n uint256 lastEpoch = votingDistributor.lastEpoch();\n\n poolDistributor.claimLp(\n address(this),\n tokenId,\n mPool,\n binIds,\n lastEpoch\n );\n }\n\n // Run the internal inherited function\n _collectRewardTokens();\n }\n\n /***************************************\n Balances and Fees\n ****************************************/\n\n /**\n * @dev Get the total asset value held in the platform\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256)\n {\n require(_asset == WETH, \"Only WETH supported\");\n\n // because of PoolLens inaccuracy there is usually some dust WETH left on the contract\n uint256 _wethBalance = IERC20(WETH).balanceOf(address(this));\n // just paranoia check, in case there is OETH in the strategy that for some reason hasn't\n // been burned yet. This should always be 0.\n uint256 _oethBalance = IERC20(OETH).balanceOf(address(this));\n return underlyingAssets + _wethBalance + _oethBalance;\n }\n\n /// @dev This function updates the amount of underlying assets with the approach of the least possible\n /// total tokens extracted for the current liquidity in the pool.\n function _updateUnderlyingAssets() internal {\n /**\n * Our net value represent the smallest amount of tokens we are able to extract from the position\n * given our liquidity.\n *\n * The least amount of tokens ex-tractable from the position is where the active trading price is\n * at the edge between tick -1 & tick 0. There the pool is offering 1:1 trades between WETH & OETH.\n * At that moment the pool consists completely of WETH and no OETH.\n *\n * The more swaps from OETH -> WETH happen on the pool the more the price starts to move away from the tick 0\n * towards the middle of tick -1 making OETH (priced in WETH) cheaper.\n */\n\n uint256 _wethAmount = tokenId == 0 ? 0 : _balanceInPosition();\n\n underlyingAssets = _wethAmount;\n emit UnderlyingAssetsUpdated(_wethAmount);\n }\n\n /**\n * @dev Strategy reserves (which consist only of WETH in case of Rooster - Plume pool)\n * when the tick price is closest to parity - assuring the lowest amount of tokens\n * returned for the current position liquidity.\n */\n function _balanceInPosition() internal view returns (uint256 _wethBalance) {\n (, , uint256 liquidity) = _getPositionInformation();\n\n uint256 _oethBalance;\n\n (_wethBalance, _oethBalance) = _reservesInTickForGivenPriceAndLiquidity(\n sqrtPriceTickLower,\n sqrtPriceTickHigher,\n sqrtPriceAtParity,\n liquidity\n );\n\n require(_oethBalance == 0, \"Non zero oethBalance\");\n }\n\n /**\n * @notice Tick dominance denominated in 1e18\n * @return _tickDominance The share of liquidity in TICK_NUMBER tick owned\n * by the strategy contract denominated in 1e18\n */\n function tickDominance() public view returns (uint256 _tickDominance) {\n IMaverickV2Pool.TickState memory tickState = mPool.getTick(TICK_NUMBER);\n\n uint256 wethReserve = tickState.reserveA;\n uint256 oethReserve = tickState.reserveB;\n\n // prettier-ignore\n (uint256 _amountWeth, uint256 _amountOeth, ) = _getPositionInformation();\n\n if (wethReserve + oethReserve == 0) {\n return 0;\n }\n\n _tickDominance = (_amountWeth + _amountOeth).divPrecisely(\n wethReserve + oethReserve\n );\n }\n\n /***************************************\n Hidden functions\n ****************************************/\n /**\n * @dev Unsupported\n */\n function setPTokenAddress(address, address) external pure override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function removePToken(uint256) external pure override {\n // The pool tokens can never change.\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function _abstractSetPToken(address, address) internal pure override {\n revert(\"Unsupported method\");\n }\n\n /**\n * @dev Unsupported\n */\n function safeApproveAllTokens() external pure override {\n // all the amounts are approved at the time required\n revert(\"Unsupported method\");\n }\n\n /***************************************\n Maverick liquidity utilities\n ****************************************/\n\n /// @notice Calculates deltaA = liquidity * (sqrt(upper) - sqrt(lower))\n /// Calculates deltaB = liquidity / sqrt(lower) - liquidity / sqrt(upper),\n /// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower))\n ///\n /// @dev refactored from here:\n // solhint-disable-next-line max-line-length\n /// https://github.com/rooster-protocol/rooster-contracts/blob/main/v2-supplemental/contracts/libraries/LiquidityUtilities.sol#L665-L695\n function _reservesInTickForGivenPriceAndLiquidity(\n uint256 _lowerSqrtPrice,\n uint256 _upperSqrtPrice,\n uint256 _newSqrtPrice,\n uint256 _liquidity\n ) internal pure returns (uint128 reserveA, uint128 reserveB) {\n if (_liquidity == 0) {\n (reserveA, reserveB) = (0, 0);\n } else {\n uint256 lowerEdge = MathRooster.max(_lowerSqrtPrice, _newSqrtPrice);\n\n reserveA = MathRooster\n .mulCeil(\n _liquidity,\n MathRooster.clip(\n MathRooster.min(_upperSqrtPrice, _newSqrtPrice),\n _lowerSqrtPrice\n )\n )\n .toUint128();\n reserveB = MathRooster\n .mulDivCeil(\n _liquidity,\n 1e18 * MathRooster.clip(_upperSqrtPrice, lowerEdge),\n _upperSqrtPrice * lowerEdge\n )\n .toUint128();\n }\n }\n}\n" + }, + "contracts/strategies/sonic/SonicStakingStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SonicValidatorDelegator } from \"./SonicValidatorDelegator.sol\";\nimport { IWrappedSonic } from \"../../interfaces/sonic/IWrappedSonic.sol\";\n\n/**\n * @title Staking Strategy for Sonic's native S currency\n * @author Origin Protocol Inc\n */\ncontract SonicStakingStrategy is SonicValidatorDelegator {\n // For future use\n uint256[50] private __gap;\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wrappedSonic,\n address _sfc\n ) SonicValidatorDelegator(_baseConfig, _wrappedSonic, _sfc) {}\n\n /// @notice Deposit wrapped S asset into the underlying platform.\n /// @param _asset Address of asset to deposit. Has to be Wrapped Sonic (wS).\n /// @param _amount Amount of assets that were transferred to the strategy by the vault.\n function deposit(address _asset, uint256 _amount)\n external\n override\n onlyVault\n nonReentrant\n {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n _deposit(_asset, _amount);\n }\n\n /**\n * @notice Deposit Wrapped Sonic (wS) to this strategy and delegate to a validator.\n * @param _asset Address of Wrapped Sonic (wS) token\n * @param _amount Amount of Wrapped Sonic (wS) to deposit\n */\n function _deposit(address _asset, uint256 _amount) internal virtual {\n require(_amount > 0, \"Must deposit something\");\n\n _delegate(_amount);\n emit Deposit(_asset, address(0), _amount);\n }\n\n /**\n * @notice Deposit the entire balance of wrapped S in this strategy contract into\n * the underlying platform.\n */\n function depositAll() external virtual override onlyVault nonReentrant {\n uint256 wSBalance = IERC20(wrappedSonic).balanceOf(address(this));\n\n if (wSBalance > 0) {\n _deposit(wrappedSonic, wSBalance);\n }\n }\n\n /// @notice Withdraw Wrapped Sonic (wS) from this strategy contract.\n /// Used only if some wS is lingering on the contract.\n /// That can happen only when someone sends wS directly to this contract\n /// @param _recipient Address to receive withdrawn assets\n /// @param _asset Address of the Wrapped Sonic (wS) token\n /// @param _amount Amount of Wrapped Sonic (wS) to withdraw\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external override onlyVault nonReentrant {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n _withdraw(_recipient, _asset, _amount);\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal override {\n require(_amount > 0, \"Must withdraw something\");\n require(_recipient != address(0), \"Must specify recipient\");\n\n // slither-disable-next-line unchecked-transfer unused-return\n IERC20(_asset).transfer(_recipient, _amount);\n\n emit Withdrawal(wrappedSonic, address(0), _amount);\n }\n\n /// @notice Transfer all Wrapped Sonic (wS) deposits back to the vault.\n /// This does not withdraw from delegated validators. That has to be done separately with `undelegate`.\n /// Any native S in this strategy will be withdrawn.\n function withdrawAll() external override onlyVaultOrGovernor nonReentrant {\n uint256 balance = address(this).balance;\n if (balance > 0) {\n IWrappedSonic(wrappedSonic).deposit{ value: balance }();\n }\n uint256 wSBalance = IERC20(wrappedSonic).balanceOf(address(this));\n if (wSBalance > 0) {\n _withdraw(vaultAddress, wrappedSonic, wSBalance);\n }\n }\n\n /**\n * @dev Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset token\n */\n function supportsAsset(address _asset)\n public\n view\n virtual\n override\n returns (bool)\n {\n return _asset == wrappedSonic;\n }\n\n /**\n * @notice is not supported for this strategy as the\n * Wrapped Sonic (wS) token is set at deploy time.\n */\n function setPTokenAddress(address, address)\n external\n view\n override\n onlyGovernor\n {\n revert(\"unsupported function\");\n }\n\n /// @notice is not used by this strategy as all staking rewards are restaked\n function collectRewardTokens() external override nonReentrant {\n revert(\"unsupported function\");\n }\n\n /**\n * @notice is not supported for this strategy as the\n * Wrapped Sonic (wS) token is set at deploy time.\n */\n function removePToken(uint256) external view override onlyGovernor {\n revert(\"unsupported function\");\n }\n\n /// @dev is not used by this strategy but must be implemented as it's abstract\n /// in the inherited `InitializableAbstractStrategy` contract.\n function _abstractSetPToken(address, address) internal virtual override {}\n\n /// @notice is not used by this strategy\n function safeApproveAllTokens() external override onlyGovernor {}\n}\n" + }, + "contracts/strategies/sonic/SonicSwapXAMOStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title SwapX Algorithmic Market Maker (AMO) Strategy\n * @notice AMO strategy for the SwapX OS/wS stable pool\n * @author Origin Protocol Inc\n */\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { StableMath } from \"../../utils/StableMath.sol\";\nimport { sqrt } from \"../../utils/PRBMath.sol\";\nimport { IBasicToken } from \"../../interfaces/IBasicToken.sol\";\nimport { IPair } from \"../../interfaces/sonic/ISwapXPair.sol\";\nimport { IGauge } from \"../../interfaces/sonic/ISwapXGauge.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\n\ncontract SonicSwapXAMOStrategy is InitializableAbstractStrategy {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @notice a threshold under which the contract no longer allows for the protocol to manually rebalance.\n * Guarding against a strategist / guardian being taken over and with multiple transactions\n * draining the protocol funds.\n */\n uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether;\n\n /// @notice Precision for the SwapX Stable AMM (sAMM) invariant k.\n uint256 public constant PRECISION = 1e18;\n\n /// @notice Address of the Wrapped S (wS) token.\n address public immutable ws;\n\n /// @notice Address of the OS token contract.\n address public immutable os;\n\n /// @notice Address of the SwapX Stable pool contract.\n address public immutable pool;\n\n /// @notice Address of the SwapX Gauge contract.\n address public immutable gauge;\n\n /// @notice The max amount the OS/wS price can deviate from peg (1e18)\n /// before deposits are reverted scaled to 18 decimals.\n /// eg 0.01e18 or 1e16 is 1% which is 100 basis points.\n /// This is the amount below and above peg so a 50 basis point deviation (0.005e18)\n /// allows a price range from 0.995 to 1.005.\n uint256 public maxDepeg;\n\n event SwapOTokensToPool(\n uint256 osMinted,\n uint256 wsDepositAmount,\n uint256 osDepositAmount,\n uint256 lpTokens\n );\n event SwapAssetsToPool(\n uint256 wsSwapped,\n uint256 lpTokens,\n uint256 osBurnt\n );\n event MaxDepegUpdated(uint256 maxDepeg);\n\n /**\n * @dev Verifies that the caller is the Strategist of the Vault.\n */\n modifier onlyStrategist() {\n require(\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist\"\n );\n _;\n }\n\n /**\n * @dev Skim the SwapX pool in case any extra wS or OS tokens were added\n */\n modifier skimPool() {\n IPair(pool).skim(address(this));\n _;\n }\n\n /**\n * @dev Checks the pool is balanced enough to allow deposits.\n */\n modifier nearBalancedPool() {\n // OS/wS price = wS / OS\n // Get the OS/wS price for selling 1 OS for wS\n // As OS is 1, the wS amount is the OS/wS price\n uint256 sellPrice = IPair(pool).getAmountOut(1e18, os);\n\n // Get the amount of OS received from selling 1 wS. This is buying OS.\n uint256 osAmount = IPair(pool).getAmountOut(1e18, ws);\n // Convert to a OS/wS price = wS / OS\n uint256 buyPrice = 1e36 / osAmount;\n\n uint256 pegPrice = 1e18;\n\n require(\n sellPrice >= pegPrice - maxDepeg && buyPrice <= pegPrice + maxDepeg,\n \"price out of range\"\n );\n _;\n }\n\n /**\n * @dev Checks the pool's balances have improved and the balances\n * have not tipped to the other side.\n * This modifier is only applied to functions that do swaps against the pool.\n * Deposits and withdrawals are proportional to the pool's balances hence don't need this check.\n */\n modifier improvePoolBalance() {\n // Get the asset and OToken balances in the pool\n (uint256 wsReservesBefore, uint256 osReservesBefore, ) = IPair(pool)\n .getReserves();\n // diff = wS balance - OS balance\n int256 diffBefore = wsReservesBefore.toInt256() -\n osReservesBefore.toInt256();\n\n _;\n\n // Get the asset and OToken balances in the pool\n (uint256 wsReservesAfter, uint256 osReservesAfter, ) = IPair(pool)\n .getReserves();\n // diff = wS balance - OS balance\n int256 diffAfter = wsReservesAfter.toInt256() -\n osReservesAfter.toInt256();\n\n if (diffBefore == 0) {\n require(diffAfter == 0, \"Position balance is worsened\");\n } else if (diffBefore < 0) {\n // If the pool was originally imbalanced in favor of OS, then\n // we want to check that the pool is now more balanced\n require(diffAfter <= 0, \"Assets overshot peg\");\n require(diffBefore < diffAfter, \"OTokens balance worse\");\n } else if (diffBefore > 0) {\n // If the pool was originally imbalanced in favor of wS, then\n // we want to check that the pool is now more balanced\n require(diffAfter >= 0, \"OTokens overshot peg\");\n require(diffAfter < diffBefore, \"Assets balance worse\");\n }\n }\n\n /**\n * @param _baseConfig The `platformAddress` is the address of the SwapX pool.\n * The `vaultAddress` is the address of the Origin Sonic Vault.\n * @param _os Address of the OS token.\n * @param _ws Address of the Wrapped S (wS) token.\n * @param _gauge Address of the SwapX gauge for the pool.\n */\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _os,\n address _ws,\n address _gauge\n ) InitializableAbstractStrategy(_baseConfig) {\n // Check the pool tokens are correct\n require(\n IPair(_baseConfig.platformAddress).token0() == _ws &&\n IPair(_baseConfig.platformAddress).token1() == _os,\n \"Incorrect pool tokens\"\n );\n // Checked both tokens are to 18 decimals\n require(\n IBasicToken(_ws).decimals() == 18 &&\n IBasicToken(_os).decimals() == 18,\n \"Incorrect token decimals\"\n );\n // Check the SwapX pool is a Stable AMM (sAMM)\n require(\n IPair(_baseConfig.platformAddress).isStable() == true,\n \"Pool not stable\"\n );\n // Check the gauge is for the pool\n require(\n IGauge(_gauge).TOKEN() == _baseConfig.platformAddress,\n \"Incorrect gauge\"\n );\n\n // Set the immutable variables\n os = _os;\n ws = _ws;\n pool = _baseConfig.platformAddress;\n gauge = _gauge;\n\n // This is an implementation contract. The governor is set in the proxy contract.\n _setGovernor(address(0));\n }\n\n /**\n * Initializer for setting up strategy internal state. This overrides the\n * InitializableAbstractStrategy initializer as SwapX strategies don't fit\n * well within that abstraction.\n * @param _rewardTokenAddresses Array containing SWPx token address\n * @param _maxDepeg The max amount the OS/wS price can deviate from peg (1e18) before deposits are reverted.\n */\n function initialize(\n address[] calldata _rewardTokenAddresses,\n uint256 _maxDepeg\n ) external onlyGovernor initializer {\n address[] memory pTokens = new address[](1);\n pTokens[0] = pool;\n\n address[] memory _assets = new address[](1);\n _assets[0] = ws;\n\n InitializableAbstractStrategy._initialize(\n _rewardTokenAddresses,\n _assets,\n pTokens\n );\n\n maxDepeg = _maxDepeg;\n\n _approveBase();\n }\n\n /***************************************\n Deposit\n ****************************************/\n\n /**\n * @notice Deposit an amount of Wrapped S (wS) into the SwapX pool.\n * Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @dev This tx must be wrapped by the VaultValueChecker.\n * To minimize loses, the pool should be rebalanced before depositing.\n * The pool's OS/wS price must be within the maxDepeg range.\n * @param _asset Address of Wrapped S (wS) token.\n * @param _wsAmount Amount of Wrapped S (wS) tokens to deposit.\n */\n function deposit(address _asset, uint256 _wsAmount)\n external\n override\n onlyVault\n nonReentrant\n skimPool\n nearBalancedPool\n {\n require(_asset == ws, \"Unsupported asset\");\n require(_wsAmount > 0, \"Must deposit something\");\n\n (uint256 osDepositAmount, ) = _deposit(_wsAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the deposited wS tokens\n emit Deposit(ws, pool, _wsAmount);\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osDepositAmount);\n }\n\n /**\n * @notice Deposit all the strategy's Wrapped S (wS) tokens into the SwapX pool.\n * Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @dev This tx must be wrapped by the VaultValueChecker.\n * To minimize loses, the pool should be rebalanced before depositing.\n * The pool's OS/wS price must be within the maxDepeg range.\n */\n function depositAll()\n external\n override\n onlyVault\n nonReentrant\n skimPool\n nearBalancedPool\n {\n uint256 wsBalance = IERC20(ws).balanceOf(address(this));\n if (wsBalance > 0) {\n (uint256 osDepositAmount, ) = _deposit(wsBalance);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the deposited wS tokens\n emit Deposit(ws, pool, wsBalance);\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osDepositAmount);\n }\n }\n\n /**\n * @dev Mint OS in proportion to the pool's wS and OS reserves,\n * transfer Wrapped S (wS) and OS to the pool,\n * mint the pool's LP token and deposit in the gauge.\n * @param _wsAmount Amount of Wrapped S (wS) tokens to deposit.\n * @return osDepositAmount Amount of OS tokens minted and deposited into the pool.\n * @return lpTokens Amount of SwapX pool LP tokens minted and deposited into the gauge.\n */\n function _deposit(uint256 _wsAmount)\n internal\n returns (uint256 osDepositAmount, uint256 lpTokens)\n {\n // Calculate the required amount of OS to mint based on the wS amount.\n osDepositAmount = _calcTokensToMint(_wsAmount);\n\n // Mint the required OS tokens to this strategy\n IVault(vaultAddress).mintForStrategy(osDepositAmount);\n\n // Add wS and OS liquidity to the pool and stake in gauge\n lpTokens = _depositToPoolAndGauge(_wsAmount, osDepositAmount);\n }\n\n /***************************************\n Withdraw\n ****************************************/\n\n /**\n * @notice Withdraw wS and OS from the SwapX pool, burn the OS,\n * and transfer the wS to the recipient.\n * @param _recipient Address of the Vault.\n * @param _asset Address of the Wrapped S (wS) contract.\n * @param _wsAmount Amount of Wrapped S (wS) to withdraw.\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _wsAmount\n ) external override onlyVault nonReentrant skimPool {\n require(_wsAmount > 0, \"Must withdraw something\");\n require(_asset == ws, \"Unsupported asset\");\n // This strategy can't be set as a default strategy for wS in the Vault.\n // This means the recipient must always be the Vault.\n require(_recipient == vaultAddress, \"Only withdraw to vault allowed\");\n\n // Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n uint256 lpTokens = _calcTokensToBurn(_wsAmount);\n\n // Withdraw pool LP tokens from the gauge and remove assets from from the pool\n _withdrawFromGaugeAndPool(lpTokens);\n\n // Burn all the removed OS and any that was left in the strategy\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Transfer wS to the recipient\n // Note there can be a dust amount of wS left in the strategy as\n // the burn of the pool's LP tokens is rounded up\n require(\n IERC20(ws).balanceOf(address(this)) >= _wsAmount,\n \"Not enough wS removed from pool\"\n );\n IERC20(ws).safeTransfer(_recipient, _wsAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the withdrawn wS tokens\n emit Withdrawal(ws, pool, _wsAmount);\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n }\n\n /**\n * @notice Withdraw all pool LP tokens from the gauge,\n * remove all wS and OS from the SwapX pool,\n * burn all the OS tokens,\n * and transfer all the wS to the Vault contract.\n * @dev There is no solvency check here as withdrawAll can be called to\n * quickly secure assets to the Vault in emergencies.\n */\n function withdrawAll()\n external\n override\n onlyVaultOrGovernor\n nonReentrant\n skimPool\n {\n // Get all the pool LP tokens the strategy has staked in the gauge\n uint256 lpTokens = IGauge(gauge).balanceOf(address(this));\n // Can not withdraw zero LP tokens from the gauge\n if (lpTokens == 0) return;\n\n if (IGauge(gauge).emergency()) {\n // The gauge is in emergency mode\n _emergencyWithdrawFromGaugeAndPool();\n } else {\n // Withdraw pool LP tokens from the gauge and remove assets from from the pool\n _withdrawFromGaugeAndPool(lpTokens);\n }\n\n // Burn all OS in this strategy contract\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Get the strategy contract's wS balance.\n // This includes all that was removed from the SwapX pool and\n // any that was sitting in the strategy contract before the removal.\n uint256 wsBalance = IERC20(ws).balanceOf(address(this));\n IERC20(ws).safeTransfer(vaultAddress, wsBalance);\n\n // Emit event for the withdrawn wS tokens\n emit Withdrawal(ws, pool, wsBalance);\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n }\n\n /***************************************\n Pool Rebalancing\n ****************************************/\n\n /** @notice Used when there is more OS than wS in the pool.\n * wS and OS is removed from the pool, the received wS is swapped for OS\n * and the left over OS in the strategy is burnt.\n * The OS/wS price is < 1.0 so OS is being bought at a discount.\n * @param _wsAmount Amount of Wrapped S (wS) to swap into the pool.\n */\n function swapAssetsToPool(uint256 _wsAmount)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n skimPool\n {\n require(_wsAmount > 0, \"Must swap something\");\n\n // 1. Partially remove liquidity so there’s enough wS for the swap\n\n // Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n uint256 lpTokens = _calcTokensToBurn(_wsAmount);\n require(lpTokens > 0, \"No LP tokens to burn\");\n\n _withdrawFromGaugeAndPool(lpTokens);\n\n // 2. Swap wS for OS against the pool\n // Swap exact amount of wS for OS against the pool\n // There can be a dust amount of wS left in the strategy as the burn of the pool's LP tokens is rounded up\n _swapExactTokensForTokens(_wsAmount, ws, os);\n\n // 3. Burn all the OS left in the strategy from the remove liquidity and swap\n uint256 osToBurn = IERC20(os).balanceOf(address(this));\n IVault(vaultAddress).burnForStrategy(osToBurn);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the burnt OS tokens\n emit Withdrawal(os, pool, osToBurn);\n // Emit event for the swap\n emit SwapAssetsToPool(_wsAmount, lpTokens, osToBurn);\n }\n\n /**\n * @notice Used when there is more wS than OS in the pool.\n * OS is minted and swapped for wS against the pool,\n * more OS is minted and added back into the pool with the swapped out wS.\n * The OS/wS price is > 1.0 so OS is being sold at a premium.\n * @param _osAmount Amount of OS to swap into the pool.\n */\n function swapOTokensToPool(uint256 _osAmount)\n external\n onlyStrategist\n nonReentrant\n improvePoolBalance\n skimPool\n {\n require(_osAmount > 0, \"Must swap something\");\n\n // 1. Mint OS so it can be swapped into the pool\n\n // There can be OS in the strategy from skimming the pool\n uint256 osInStrategy = IERC20(os).balanceOf(address(this));\n require(_osAmount >= osInStrategy, \"Too much OS in strategy\");\n uint256 osToMint = _osAmount - osInStrategy;\n\n // Mint the required OS tokens to this strategy\n IVault(vaultAddress).mintForStrategy(osToMint);\n\n // 2. Swap OS for wS against the pool\n _swapExactTokensForTokens(_osAmount, os, ws);\n\n // The wS is from the swap and any wS that was sitting in the strategy\n uint256 wsDepositAmount = IERC20(ws).balanceOf(address(this));\n\n // 3. Add wS and OS back to the pool in proportion to the pool's reserves\n (uint256 osDepositAmount, uint256 lpTokens) = _deposit(wsDepositAmount);\n\n // Ensure solvency of the vault\n _solvencyAssert();\n\n // Emit event for the minted OS tokens\n emit Deposit(os, pool, osToMint + osDepositAmount);\n // Emit event for the swap\n emit SwapOTokensToPool(\n osToMint,\n wsDepositAmount,\n osDepositAmount,\n lpTokens\n );\n }\n\n /***************************************\n Assets and Rewards\n ****************************************/\n\n /**\n * @notice Get the wS value of assets in the strategy and SwapX pool.\n * The value of the assets in the pool is calculated assuming the pool is balanced.\n * This way the value can not be manipulated by changing the pool's token balances.\n * @param _asset Address of the Wrapped S (wS) token\n * @return balance Total value in wS.\n */\n function checkBalance(address _asset)\n external\n view\n override\n returns (uint256 balance)\n {\n require(_asset == ws, \"Unsupported asset\");\n\n // wS balance needed here for the balance check that happens from vault during depositing.\n balance = IERC20(ws).balanceOf(address(this));\n\n // This assumes 1 gauge LP token = 1 pool LP token\n uint256 lpTokens = IGauge(gauge).balanceOf(address(this));\n if (lpTokens == 0) return balance;\n\n // Add the strategy’s share of the wS and OS tokens in the SwapX pool if the pool was balanced.\n balance += _lpValue(lpTokens);\n }\n\n /**\n * @notice Returns bool indicating whether asset is supported by strategy\n * @param _asset Address of the asset\n */\n function supportsAsset(address _asset) public view override returns (bool) {\n return _asset == ws;\n }\n\n /**\n * @notice Collect accumulated SWPx (and other) rewards and send to the Harvester.\n */\n function collectRewardTokens()\n external\n override\n onlyHarvester\n nonReentrant\n {\n // Collect SWPx rewards from the gauge\n IGauge(gauge).getReward();\n\n _collectRewardTokens();\n }\n\n /***************************************\n Internal SwapX Pool and Gauge Functions\n ****************************************/\n\n /**\n * @dev Calculate the required amount of OS to mint based on the wS amount.\n * This ensures the proportion of OS tokens being added to the pool matches the proportion of wS tokens.\n * For example, if the added wS tokens is 10% of existing wS tokens in the pool,\n * then the OS tokens being added should also be 10% of the OS tokens in the pool.\n * @param _wsAmount Amount of Wrapped S (wS) to be added to the pool.\n * @return osAmount Amount of OS to be minted and added to the pool.\n */\n function _calcTokensToMint(uint256 _wsAmount)\n internal\n view\n returns (uint256 osAmount)\n {\n (uint256 wsReserves, uint256 osReserves, ) = IPair(pool).getReserves();\n require(wsReserves > 0, \"Empty pool\");\n\n // OS to add = (wS being added * OS in pool) / wS in pool\n osAmount = (_wsAmount * osReserves) / wsReserves;\n }\n\n /**\n * @dev Calculate how much pool LP tokens to burn to get the required amount of wS tokens back\n * from the pool.\n * @param _wsAmount Amount of Wrapped S (wS) to be removed from the pool.\n * @return lpTokens Amount of SwapX pool LP tokens to burn.\n */\n function _calcTokensToBurn(uint256 _wsAmount)\n internal\n view\n returns (uint256 lpTokens)\n {\n /* The SwapX pool proportionally returns the reserve tokens when removing liquidity.\n * First, calculate the proportion of required wS tokens against the pools wS reserves.\n * That same proportion is used to calculate the required amount of pool LP tokens.\n * For example, if the required wS tokens is 10% of the pool's wS reserves,\n * then 10% of the pool's LP supply needs to be burned.\n *\n * Because we are doing balanced removal we should be making profit when removing liquidity in a\n * pool tilted to either side.\n *\n * Important: A downside is that the Strategist / Governor needs to be\n * cognizant of not removing too much liquidity. And while the proposal to remove liquidity\n * is being voted on, the pool tilt might change so much that the proposal that has been valid while\n * created is no longer valid.\n */\n\n (uint256 wsReserves, , ) = IPair(pool).getReserves();\n require(wsReserves > 0, \"Empty pool\");\n\n lpTokens = (_wsAmount * IPair(pool).totalSupply()) / wsReserves;\n lpTokens += 1; // Add 1 to ensure we get enough LP tokens with rounding\n }\n\n /**\n * @dev Deposit Wrapped S (wS) and OS liquidity to the SwapX pool\n * and stake the pool's LP token in the gauge.\n * @param _wsAmount Amount of Wrapped S (wS) to deposit.\n * @param _osAmount Amount of OS to deposit.\n * @return lpTokens Amount of SwapX pool LP tokens minted.\n */\n function _depositToPoolAndGauge(uint256 _wsAmount, uint256 _osAmount)\n internal\n returns (uint256 lpTokens)\n {\n // Transfer wS to the pool\n IERC20(ws).safeTransfer(pool, _wsAmount);\n // Transfer OS to the pool\n IERC20(os).safeTransfer(pool, _osAmount);\n\n // Mint LP tokens from the pool\n lpTokens = IPair(pool).mint(address(this));\n\n // Deposit the pool's LP tokens into the gauge\n IGauge(gauge).deposit(lpTokens);\n }\n\n /**\n * @dev Withdraw pool LP tokens from the gauge and remove wS and OS from the pool.\n * @param _lpTokens Amount of SwapX pool LP tokens to withdraw from the gauge\n */\n function _withdrawFromGaugeAndPool(uint256 _lpTokens) internal {\n require(\n IGauge(gauge).balanceOf(address(this)) >= _lpTokens,\n \"Not enough LP tokens in gauge\"\n );\n\n // Withdraw pool LP tokens from the gauge\n IGauge(gauge).withdraw(_lpTokens);\n\n // Transfer the pool LP tokens to the pool\n IERC20(pool).safeTransfer(pool, _lpTokens);\n\n // Burn the LP tokens and transfer the wS and OS back to the strategy\n IPair(pool).burn(address(this));\n }\n\n /**\n * @dev Withdraw all pool LP tokens from the gauge when it's in emergency mode\n * and remove wS and OS from the pool.\n */\n function _emergencyWithdrawFromGaugeAndPool() internal {\n // Withdraw all pool LP tokens from the gauge\n IGauge(gauge).emergencyWithdraw();\n\n // Get the pool LP tokens in strategy\n uint256 _lpTokens = IERC20(pool).balanceOf(address(this));\n\n // Transfer the pool LP tokens to the pool\n IERC20(pool).safeTransfer(pool, _lpTokens);\n\n // Burn the LP tokens and transfer the wS and OS back to the strategy\n IPair(pool).burn(address(this));\n }\n\n /**\n * @dev Swap exact amount of tokens for another token against the pool.\n * @param _amountIn Amount of tokens to swap into the pool.\n * @param _tokenIn Address of the token going into the pool.\n * @param _tokenOut Address of the token being swapped out of the pool.\n */\n function _swapExactTokensForTokens(\n uint256 _amountIn,\n address _tokenIn,\n address _tokenOut\n ) internal {\n // Transfer in tokens to the pool\n IERC20(_tokenIn).safeTransfer(pool, _amountIn);\n\n // Calculate how much out tokens we get from the swap\n uint256 amountOut = IPair(pool).getAmountOut(_amountIn, _tokenIn);\n\n // Safety check that we are dealing with the correct pool tokens\n require(\n (_tokenIn == ws && _tokenOut == os) ||\n (_tokenIn == os && _tokenOut == ws),\n \"Unsupported swap\"\n );\n\n // Work out the correct order of the amounts for the pool\n (uint256 amount0, uint256 amount1) = _tokenIn == ws\n ? (uint256(0), amountOut)\n : (amountOut, 0);\n\n // Perform the swap on the pool\n IPair(pool).swap(amount0, amount1, address(this), new bytes(0));\n\n // The slippage protection against the amount out is indirectly done\n // via the improvePoolBalance\n }\n\n /// @dev Calculate the value of a LP position in a SwapX stable pool\n /// if the pool was balanced.\n /// @param _lpTokens Amount of LP tokens in the SwapX pool\n /// @return value The wS value of the LP tokens when the pool is balanced\n function _lpValue(uint256 _lpTokens) internal view returns (uint256 value) {\n // Get total supply of LP tokens\n uint256 totalSupply = IPair(pool).totalSupply();\n if (totalSupply == 0) return 0;\n\n // Get the current reserves of the pool\n (uint256 wsReserves, uint256 osReserves, ) = IPair(pool).getReserves();\n\n // Calculate the invariant of the pool assuming both tokens have 18 decimals.\n // k is scaled to 18 decimals.\n uint256 k = _invariant(wsReserves, osReserves);\n\n // If x = y, let’s denote x = y = z (where z is the common reserve value)\n // Substitute z into the invariant:\n // k = z^3 * z + z * z^3\n // k = 2 * z^4\n // Going back the other way to calculate the common reserve value z\n // z = (k / 2) ^ (1/4)\n // the total value of the pool when x = y is 2 * z, which is 2 * (k / 2) ^ (1/4)\n uint256 zSquared = sqrt((k * 1e18) / 2); // 18 + 18 = 36 decimals becomes 18 decimals after sqrt\n uint256 z = sqrt(zSquared * 1e18); // 18 + 18 = 36 decimals becomes 18 decimals after sqrt\n uint256 totalValueOfPool = 2 * z;\n\n // lp value = lp tokens * value of pool / total supply\n value = (_lpTokens * totalValueOfPool) / totalSupply;\n }\n\n /**\n * @dev Compute the invariant for a SwapX stable pool.\n * This assumed both x and y tokens are to 18 decimals which is checked in the constructor.\n * invariant: k = x^3 * y + x * y^3\n * @dev This implementation is copied from SwapX's Pair contract.\n * @param _x The amount of Wrapped S (wS) tokens in the pool\n * @param _y The amount of the OS tokens in the pool\n * @return k The invariant of the SwapX stable pool\n */\n function _invariant(uint256 _x, uint256 _y)\n internal\n pure\n returns (uint256 k)\n {\n uint256 _a = (_x * _y) / PRECISION;\n uint256 _b = ((_x * _x) / PRECISION + (_y * _y) / PRECISION);\n // slither-disable-next-line divide-before-multiply\n k = (_a * _b) / PRECISION;\n }\n\n /**\n * @dev Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can\n * keep rebalancing the pool in both directions making the protocol lose a tiny amount of\n * funds each time.\n *\n * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to\n * function.\n */\n function _solvencyAssert() internal view {\n uint256 _totalVaultValue = IVault(vaultAddress).totalValue();\n uint256 _totalSupply = IERC20(os).totalSupply();\n\n if (\n _totalSupply > 0 &&\n _totalVaultValue.divPrecisely(_totalSupply) < SOLVENCY_THRESHOLD\n ) {\n revert(\"Protocol insolvent\");\n }\n }\n\n /***************************************\n Setters\n ****************************************/\n\n /**\n * @notice Set the maximum deviation from the OS/wS peg (1e18) before deposits are reverted.\n * @param _maxDepeg the OS/wS price from peg (1e18) in 18 decimals.\n * eg 0.01e18 or 1e16 is 1% which is 100 basis points.\n */\n function setMaxDepeg(uint256 _maxDepeg) external onlyGovernor {\n maxDepeg = _maxDepeg;\n\n emit MaxDepegUpdated(_maxDepeg);\n }\n\n /***************************************\n Approvals\n ****************************************/\n\n /**\n * @notice Approve the spending of all assets by their corresponding pool tokens,\n * if for some reason is it necessary.\n */\n function safeApproveAllTokens()\n external\n override\n onlyGovernor\n nonReentrant\n {\n _approveBase();\n }\n\n // solhint-disable-next-line no-unused-vars\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n override\n {}\n\n function _approveBase() internal {\n // Approve SwapX gauge contract to transfer SwapX pool LP tokens\n // This is needed for deposits of SwapX pool LP tokens into the gauge.\n // slither-disable-next-line unused-return\n IPair(pool).approve(address(gauge), type(uint256).max);\n }\n}\n" + }, + "contracts/strategies/sonic/SonicValidatorDelegator.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20, InitializableAbstractStrategy } from \"../../utils/InitializableAbstractStrategy.sol\";\nimport { IVault } from \"../../interfaces/IVault.sol\";\nimport { ISFC } from \"../../interfaces/sonic/ISFC.sol\";\nimport { IWrappedSonic } from \"../../interfaces/sonic/IWrappedSonic.sol\";\n\n/**\n * @title Manages delegation to Sonic validators\n * @notice This contract implements all the required functionality to delegate to,\n undelegate from and withdraw from validators.\n * @author Origin Protocol Inc\n */\nabstract contract SonicValidatorDelegator is InitializableAbstractStrategy {\n /// @notice Address of Sonic's wrapped S token\n address public immutable wrappedSonic;\n /// @notice Sonic's Special Fee Contract (SFC)\n ISFC public immutable sfc;\n\n /// @notice a unique ID for each withdrawal request\n uint256 public nextWithdrawId;\n /// @notice Sonic (S) that is pending withdrawal after undelegating\n uint256 public pendingWithdrawals;\n\n /// @notice List of supported validator IDs that can be delegated to\n uint256[] public supportedValidators;\n\n /// @notice Default validator id to deposit to\n uint256 public defaultValidatorId;\n\n struct WithdrawRequest {\n uint256 validatorId;\n uint256 undelegatedAmount;\n uint256 timestamp;\n }\n /// @notice Mapping of withdrawIds to validatorIds and undelegatedAmounts\n mapping(uint256 => WithdrawRequest) public withdrawals;\n\n /// @notice Address of the registrator - allowed to register, exit and remove validators\n address public validatorRegistrator;\n\n // For future use\n uint256[44] private __gap;\n\n event Delegated(uint256 indexed validatorId, uint256 delegatedAmount);\n event Undelegated(\n uint256 indexed withdrawId,\n uint256 indexed validatorId,\n uint256 undelegatedAmount\n );\n event Withdrawn(\n uint256 indexed withdrawId,\n uint256 indexed validatorId,\n uint256 undelegatedAmount,\n uint256 withdrawnAmount\n );\n event RegistratorChanged(address indexed newAddress);\n event SupportedValidator(uint256 indexed validatorId);\n event UnsupportedValidator(uint256 indexed validatorId);\n event DefaultValidatorIdChanged(uint256 indexed validatorId);\n\n /// @dev Throws if called by any account other than the Registrator or Strategist\n modifier onlyRegistratorOrStrategist() {\n require(\n msg.sender == validatorRegistrator ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Registrator or Strategist\"\n );\n _;\n }\n\n constructor(\n BaseStrategyConfig memory _baseConfig,\n address _wrappedSonic,\n address _sfc\n ) InitializableAbstractStrategy(_baseConfig) {\n wrappedSonic = _wrappedSonic;\n sfc = ISFC(_sfc);\n }\n\n function initialize() external virtual onlyGovernor initializer {\n address[] memory rewardTokens = new address[](0);\n address[] memory assets = new address[](1);\n address[] memory pTokens = new address[](1);\n\n assets[0] = address(wrappedSonic);\n pTokens[0] = address(platformAddress);\n\n InitializableAbstractStrategy._initialize(\n rewardTokens,\n assets,\n pTokens\n );\n }\n\n /// @notice Returns the total value of Sonic (S) that is delegated validators.\n /// Wrapped Sonic (wS) deposits that are still to be delegated and any undelegated amounts\n /// still pending a withdrawal.\n /// @param _asset Address of Wrapped Sonic (wS) token\n /// @return balance Total value managed by the strategy\n function checkBalance(address _asset)\n external\n view\n virtual\n override\n returns (uint256 balance)\n {\n require(_asset == wrappedSonic, \"Unsupported asset\");\n\n // add the Wrapped Sonic (wS) in the strategy from deposits that are still to be delegated\n // and any undelegated amounts still pending a withdrawal\n balance =\n IERC20(wrappedSonic).balanceOf(address(this)) +\n pendingWithdrawals;\n\n // For each supported validator, get the staked amount and pending rewards\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; i++) {\n uint256 validator = supportedValidators[i];\n balance += sfc.getStake(address(this), validator);\n balance += sfc.pendingRewards(address(this), validator);\n }\n }\n\n /**\n * @dev Delegate from this strategy to a specific Sonic validator. Called\n * automatically on asset deposit\n * @param _amount the amount of Sonic (S) to delegate.\n */\n function _delegate(uint256 _amount) internal {\n require(\n isSupportedValidator(defaultValidatorId),\n \"Validator not supported\"\n );\n\n // unwrap Wrapped Sonic (wS) to native Sonic (S)\n IWrappedSonic(wrappedSonic).withdraw(_amount);\n\n //slither-disable-next-line arbitrary-send-eth\n sfc.delegate{ value: _amount }(defaultValidatorId);\n\n emit Delegated(defaultValidatorId, _amount);\n }\n\n /**\n * @notice Undelegate from a specific Sonic validator.\n * This needs to be followed by a `withdrawFromSFC` two weeks later.\n * @param _validatorId The Sonic validator ID to undelegate from.\n * @param _undelegateAmount the amount of Sonic (S) to undelegate.\n * @return withdrawId The unique ID of the withdrawal request.\n */\n function undelegate(uint256 _validatorId, uint256 _undelegateAmount)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n returns (uint256 withdrawId)\n {\n withdrawId = _undelegate(_validatorId, _undelegateAmount);\n }\n\n function _undelegate(uint256 _validatorId, uint256 _undelegateAmount)\n internal\n returns (uint256 withdrawId)\n {\n // Can still undelegate even if the validator is no longer supported\n require(_undelegateAmount > 0, \"Must undelegate something\");\n\n uint256 amountDelegated = sfc.getStake(address(this), _validatorId);\n require(\n _undelegateAmount <= amountDelegated,\n \"Insufficient delegation\"\n );\n\n withdrawId = nextWithdrawId++;\n\n withdrawals[withdrawId] = WithdrawRequest(\n _validatorId,\n _undelegateAmount,\n block.timestamp\n );\n pendingWithdrawals += _undelegateAmount;\n\n sfc.undelegate(_validatorId, withdrawId, _undelegateAmount);\n\n emit Undelegated(withdrawId, _validatorId, _undelegateAmount);\n }\n\n /**\n * @notice Withdraw native S from a previously undelegated validator.\n * The native S is wrapped wS and transferred to the Vault.\n * @param _withdrawId The unique withdraw ID used to `undelegate`\n * @return withdrawnAmount The amount of Sonic (S) withdrawn.\n * This can be less than the undelegated amount in the event of slashing.\n */\n function withdrawFromSFC(uint256 _withdrawId)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n returns (uint256 withdrawnAmount)\n {\n require(_withdrawId < nextWithdrawId, \"Invalid withdrawId\");\n\n // Can still withdraw even if the validator is no longer supported\n // Load the withdrawal from storage into memory\n WithdrawRequest memory withdrawal = withdrawals[_withdrawId];\n require(!isWithdrawnFromSFC(_withdrawId), \"Already withdrawn\");\n\n withdrawals[_withdrawId].undelegatedAmount = 0;\n pendingWithdrawals -= withdrawal.undelegatedAmount;\n\n uint256 sBalanceBefore = address(this).balance;\n\n // Try to withdraw from SFC\n try sfc.withdraw(withdrawal.validatorId, _withdrawId) {\n // continue below\n } catch (bytes memory err) {\n bytes4 errorSelector = bytes4(err);\n\n // If the validator has been fully slashed, SFC's withdraw function will\n // revert with a StakeIsFullySlashed custom error.\n if (errorSelector == ISFC.StakeIsFullySlashed.selector) {\n // The validator was fully slashed, so all the delegated amounts were lost.\n // Will swallow the error as we still want to update the\n // withdrawals and pendingWithdrawals storage variables.\n\n // The return param defaults to zero but lets set it explicitly so it's clear\n withdrawnAmount = 0;\n\n emit Withdrawn(\n _withdrawId,\n withdrawal.validatorId,\n withdrawal.undelegatedAmount,\n withdrawnAmount\n );\n\n // Exit here as there is nothing to transfer to the Vault\n return withdrawnAmount;\n } else {\n // Bubble up any other SFC custom errors.\n // Inline assembly is currently the only way to generically rethrow the exact same custom error\n // from the raw bytes err in a catch block while preserving its original selector and parameters.\n // solhint-disable-next-line no-inline-assembly\n assembly {\n revert(add(32, err), mload(err))\n }\n }\n }\n\n // Set return parameter\n withdrawnAmount = address(this).balance - sBalanceBefore;\n\n // Wrap Sonic (S) to Wrapped Sonic (wS)\n IWrappedSonic(wrappedSonic).deposit{ value: withdrawnAmount }();\n\n // Transfer the Wrapped Sonic (wS) to the Vault\n _withdraw(vaultAddress, wrappedSonic, withdrawnAmount);\n\n // withdrawal.undelegatedAmount & withdrawnAmount can differ in case of slashing\n emit Withdrawn(\n _withdrawId,\n withdrawal.validatorId,\n withdrawal.undelegatedAmount,\n withdrawnAmount\n );\n }\n\n /// @notice returns a bool whether a withdrawalId has already been withdrawn or not\n /// @param _withdrawId The unique withdraw ID used to `undelegate`\n function isWithdrawnFromSFC(uint256 _withdrawId)\n public\n view\n returns (bool)\n {\n WithdrawRequest memory withdrawal = withdrawals[_withdrawId];\n require(withdrawal.validatorId > 0, \"Invalid withdrawId\");\n return withdrawal.undelegatedAmount == 0;\n }\n\n /**\n * @notice Restake any pending validator rewards for all supported validators\n * @param _validatorIds List of Sonic validator IDs to restake rewards\n */\n function restakeRewards(uint256[] calldata _validatorIds)\n external\n nonReentrant\n {\n for (uint256 i = 0; i < _validatorIds.length; ++i) {\n require(\n isSupportedValidator(_validatorIds[i]),\n \"Validator not supported\"\n );\n\n uint256 rewards = sfc.pendingRewards(\n address(this),\n _validatorIds[i]\n );\n\n if (rewards > 0) {\n sfc.restakeRewards(_validatorIds[i]);\n }\n }\n\n // The SFC contract will emit Delegated and RestakedRewards events.\n // The checkBalance function should not change as the pending rewards will moved to the staked amount.\n }\n\n /**\n * @notice Claim any pending rewards from validators\n * @param _validatorIds List of Sonic validator IDs to claim rewards\n */\n function collectRewards(uint256[] calldata _validatorIds)\n external\n onlyRegistratorOrStrategist\n nonReentrant\n {\n uint256 sBalanceBefore = address(this).balance;\n\n for (uint256 i = 0; i < _validatorIds.length; ++i) {\n uint256 rewards = sfc.pendingRewards(\n address(this),\n _validatorIds[i]\n );\n\n if (rewards > 0) {\n // The SFC contract will emit ClaimedRewards(delegator (this), validatorId, rewards)\n sfc.claimRewards(_validatorIds[i]);\n }\n }\n\n uint256 rewardsAmount = address(this).balance - sBalanceBefore;\n\n // Wrap Sonic (S) to Wrapped Sonic (wS)\n IWrappedSonic(wrappedSonic).deposit{ value: rewardsAmount }();\n\n // Transfer the Wrapped Sonic (wS) to the Vault\n _withdraw(vaultAddress, wrappedSonic, rewardsAmount);\n }\n\n /**\n * @notice To receive native S from SFC and Wrapped Sonic (wS)\n *\n * @dev This does not prevent donating S tokens to the contract\n * as wrappedSonic has a `withdrawTo` function where a third party\n * owner of wrappedSonic can withdraw to this contract.\n */\n receive() external payable {\n require(\n msg.sender == address(sfc) || msg.sender == wrappedSonic,\n \"S not from allowed contracts\"\n );\n }\n\n /***************************************\n Admin functions\n ****************************************/\n\n /// @notice Set the address of the Registrator which can undelegate, withdraw and collect rewards\n /// @param _validatorRegistrator The address of the Registrator\n function setRegistrator(address _validatorRegistrator)\n external\n onlyGovernor\n {\n validatorRegistrator = _validatorRegistrator;\n emit RegistratorChanged(_validatorRegistrator);\n }\n\n /// @notice Set the default validatorId to delegate to on deposit\n /// @param _validatorId The validator identifier. eg 18\n function setDefaultValidatorId(uint256 _validatorId)\n external\n onlyRegistratorOrStrategist\n {\n require(isSupportedValidator(_validatorId), \"Validator not supported\");\n defaultValidatorId = _validatorId;\n emit DefaultValidatorIdChanged(_validatorId);\n }\n\n /// @notice Allows a validator to be delegated to by the Registrator\n /// @param _validatorId The validator identifier. eg 18\n function supportValidator(uint256 _validatorId) external onlyGovernor {\n require(\n !isSupportedValidator(_validatorId),\n \"Validator already supported\"\n );\n\n supportedValidators.push(_validatorId);\n\n emit SupportedValidator(_validatorId);\n }\n\n /// @notice Removes a validator from the supported list.\n /// Unsupported validators can still be undelegated from, withdrawn from and rewards collected.\n /// @param _validatorId The validator identifier. eg 18\n function unsupportValidator(uint256 _validatorId) external onlyGovernor {\n require(isSupportedValidator(_validatorId), \"Validator not supported\");\n\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; ++i) {\n if (supportedValidators[i] == _validatorId) {\n supportedValidators[i] = supportedValidators[validatorLen - 1];\n supportedValidators.pop();\n break;\n }\n }\n\n uint256 stake = sfc.getStake(address(this), _validatorId);\n\n // undelegate if validator still has funds staked\n if (stake > 0) {\n _undelegate(_validatorId, stake);\n }\n emit UnsupportedValidator(_validatorId);\n }\n\n /// @notice Returns the length of the supportedValidators array\n function supportedValidatorsLength() external view returns (uint256) {\n return supportedValidators.length;\n }\n\n /// @notice Returns whether a validator is supported by this strategy\n /// @param _validatorId The validator identifier\n function isSupportedValidator(uint256 _validatorId)\n public\n view\n returns (bool)\n {\n uint256 validatorLen = supportedValidators.length;\n for (uint256 i = 0; i < validatorLen; ++i) {\n if (supportedValidators[i] == _validatorId) {\n return true;\n }\n }\n return false;\n }\n\n function _withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) internal virtual;\n}\n" + }, + "contracts/strategies/VaultValueChecker.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IOUSD } from \"../interfaces/IOUSD.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\ncontract VaultValueChecker {\n IVault public immutable vault;\n IOUSD public immutable ousd;\n // Snapshot expiration time in seconds.\n // Used to prevent accidental use of an old snapshot, but\n // is not zero to allow easy testing of strategist actions in fork testing\n uint256 constant SNAPSHOT_EXPIRES = 5 * 60;\n\n struct Snapshot {\n uint256 vaultValue;\n uint256 totalSupply;\n uint256 time;\n }\n // By doing per user snapshots, we prevent a reentrancy attack\n // from a third party that updates the snapshot in the middle\n // of an allocation process\n\n mapping(address => Snapshot) public snapshots;\n\n constructor(address _vault, address _ousd) {\n vault = IVault(_vault);\n ousd = IOUSD(_ousd);\n }\n\n function takeSnapshot() external {\n snapshots[msg.sender] = Snapshot({\n vaultValue: vault.totalValue(),\n totalSupply: ousd.totalSupply(),\n time: block.timestamp\n });\n }\n\n function checkDelta(\n int256 expectedProfit,\n int256 profitVariance,\n int256 expectedVaultChange,\n int256 vaultChangeVariance\n ) external {\n // Intentionaly not view so that this method shows up in TX builders\n Snapshot memory snapshot = snapshots[msg.sender];\n int256 vaultChange = toInt256(vault.totalValue()) -\n toInt256(snapshot.vaultValue);\n int256 supplyChange = toInt256(ousd.totalSupply()) -\n toInt256(snapshot.totalSupply);\n int256 profit = vaultChange - supplyChange;\n\n require(\n snapshot.time >= block.timestamp - SNAPSHOT_EXPIRES,\n \"Snapshot too old\"\n );\n require(snapshot.time <= block.timestamp, \"Snapshot too new\");\n require(profit >= expectedProfit - profitVariance, \"Profit too low\");\n require(profit <= expectedProfit + profitVariance, \"Profit too high\");\n require(\n vaultChange >= expectedVaultChange - vaultChangeVariance,\n \"Vault value change too low\"\n );\n require(\n vaultChange <= expectedVaultChange + vaultChangeVariance,\n \"Vault value change too high\"\n );\n }\n\n function toInt256(uint256 value) internal pure returns (int256) {\n // From openzeppelin math/SafeCast.sol\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(\n value <= uint256(type(int256).max),\n \"SafeCast: value doesn't fit in an int256\"\n );\n return int256(value);\n }\n}\n\ncontract OETHVaultValueChecker is VaultValueChecker {\n constructor(address _vault, address _ousd)\n VaultValueChecker(_vault, _ousd)\n {}\n}\n" + }, + "contracts/swapper/Swapper1InchV5.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @notice 1Inch Pathfinder V5 implementation of the general ISwapper interface.\n * @author Origin Protocol Inc\n * @dev It is possible that dust token amounts are left in this contract after a swap.\n * This can happen with some tokens that don't send the full transfer amount.\n * These dust amounts can build up over time and be used by anyone who calls the `swap` function.\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IAggregationExecutor, IOneInchRouter, SwapDescription } from \"../interfaces/IOneInch.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\n\ncontract Swapper1InchV5 is ISwapper {\n using SafeERC20 for IERC20;\n\n /// @notice 1Inch router contract to give allowance to perform swaps\n address public constant SWAP_ROUTER =\n 0x1111111254EEB25477B68fb85Ed929f73A960582;\n\n // swap(address,(address,address,address,address,uint256,uint256,uint256),bytes,bytes)\n bytes4 internal constant SWAP_SELECTOR = 0x12aa3caf;\n // unoswapTo(address,address,uint256,uint256,uint256[])\n bytes4 internal constant UNISWAP_SELECTOR = 0xf78dc253;\n // uniswapV3SwapTo(address,uint256,uint256,uint256[])\n bytes4 internal constant UNISWAPV3_SELECTOR = 0xbc80f1a8;\n\n /**\n * @notice Strategist swaps assets sitting in the contract of the `assetHolder`.\n * @param _fromAsset The token address of the asset being sold by the vault.\n * @param _toAsset The token address of the asset being purchased by the vault.\n * @param _fromAssetAmount The amount of assets being sold by the vault.\n * @param _minToAssetAmount The minimum amount of assets to be purchased.\n * @param _data RLP encoded executer address and bytes data. This is re-encoded tx.data from 1Inch swap API\n */\n function swap(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n ) external override returns (uint256 toAssetAmount) {\n // Decode the function selector from the RLP encoded _data param\n bytes4 swapSelector = bytes4(_data[:4]);\n\n if (swapSelector == SWAP_SELECTOR) {\n // Decode the executer address and data from the RLP encoded _data param\n (, address executer, bytes memory executerData) = abi.decode(\n _data,\n (bytes4, address, bytes)\n );\n SwapDescription memory swapDesc = SwapDescription({\n srcToken: IERC20(_fromAsset),\n dstToken: IERC20(_toAsset),\n srcReceiver: payable(executer),\n dstReceiver: payable(msg.sender),\n amount: _fromAssetAmount,\n minReturnAmount: _minToAssetAmount,\n flags: 4 // 1st bit _PARTIAL_FILL, 2nd bit _REQUIRES_EXTRA_ETH, 3rd bit _SHOULD_CLAIM\n });\n (toAssetAmount, ) = IOneInchRouter(SWAP_ROUTER).swap(\n IAggregationExecutor(executer),\n swapDesc,\n hex\"\",\n executerData\n );\n } else if (swapSelector == UNISWAP_SELECTOR) {\n // Need to get the Uniswap pools data from the _data param\n (, uint256[] memory pools) = abi.decode(_data, (bytes4, uint256[]));\n toAssetAmount = IOneInchRouter(SWAP_ROUTER).unoswapTo(\n payable(msg.sender),\n IERC20(_fromAsset),\n _fromAssetAmount,\n _minToAssetAmount,\n pools\n );\n } else if (swapSelector == UNISWAPV3_SELECTOR) {\n // Need to get the Uniswap pools data from the _data param\n // slither-disable-next-line uninitialized-storage\n (, uint256[] memory pools) = abi.decode(_data, (bytes4, uint256[]));\n toAssetAmount = IOneInchRouter(SWAP_ROUTER).uniswapV3SwapTo(\n payable(msg.sender),\n _fromAssetAmount,\n _minToAssetAmount,\n pools\n );\n } else {\n revert(\"Unsupported swap function\");\n }\n }\n\n /**\n * @notice Approve assets for swapping.\n * @param _assets Array of token addresses to approve.\n * @dev unlimited approval is used as no tokens sit in this contract outside a transaction.\n */\n function approveAssets(address[] memory _assets) external {\n for (uint256 i = 0; i < _assets.length; ++i) {\n // Give the 1Inch router approval to transfer unlimited assets\n IERC20(_assets[i]).safeApprove(SWAP_ROUTER, type(uint256).max);\n }\n }\n}\n" + }, + "contracts/token/BridgedWOETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { AccessControlEnumerable } from \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\n\ncontract BridgedWOETH is\n Governable,\n AccessControlEnumerable,\n Initializable,\n ERC20\n{\n bytes32 public constant MINTER_ROLE = keccak256(\"MINTER_ROLE\");\n bytes32 public constant BURNER_ROLE = keccak256(\"BURNER_ROLE\");\n\n constructor() ERC20(\"Wrapped OETH\", \"wOETH\") {\n // Nobody owns the implementation\n _setGovernor(address(0));\n }\n\n /**\n * @dev Initialize the proxy and set the Governor\n */\n function initialize() external initializer {\n // Governor can grant Minter/Burner roles\n _setupRole(DEFAULT_ADMIN_ROLE, _governor());\n }\n\n /**\n * @dev Mint tokens for `account`\n * @param account Address to mint tokens for\n * @param amount Amount of tokens to mint\n */\n function mint(address account, uint256 amount)\n external\n onlyRole(MINTER_ROLE)\n nonReentrant\n {\n _mint(account, amount);\n }\n\n /**\n * @dev Burns tokens from `account`\n * @param account Address to burn tokens from\n * @param amount Amount of tokens to burn\n */\n function burn(address account, uint256 amount)\n external\n onlyRole(BURNER_ROLE)\n nonReentrant\n {\n _burn(account, amount);\n }\n\n /**\n * @dev Burns tokens from `msg.sender`\n * @param amount Amount of tokens to burn\n */\n function burn(uint256 amount) external onlyRole(BURNER_ROLE) nonReentrant {\n _burn(msg.sender, amount);\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return \"Wrapped OETH\";\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return \"wOETH\";\n }\n\n /**\n * @dev Returns the decimals of the token\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETH is OUSD {\n function symbol() external pure override returns (string memory) {\n return \"OETH\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Origin Ether\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETHBase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBase is OUSD {\n constructor() {\n // Nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n function symbol() external pure override returns (string memory) {\n return \"superOETHb\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Super OETH\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OETHPlume.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title Super OETH (Plume) Token Contract\n * @author Origin Protocol Inc\n */\ncontract OETHPlume is OUSD {\n constructor() {\n // Nobody owns the implementation contract\n _setGovernor(address(0));\n }\n\n function symbol() external pure override returns (string memory) {\n return \"superOETHp\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Super OETH\";\n }\n\n function decimals() external pure override returns (uint8) {\n return 18;\n }\n}\n" + }, + "contracts/token/OSonic.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OUSD } from \"./OUSD.sol\";\n\n/**\n * @title Origin Sonic (OS) token on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonic is OUSD {\n function symbol() external pure override returns (string memory) {\n return \"OS\";\n }\n\n function name() external pure override returns (string memory) {\n return \"Origin Sonic\";\n }\n}\n" + }, + "contracts/token/OUSD.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD Token Contract\n * @dev ERC20 compatible contract for OUSD\n * @dev Implements an elastic supply\n * @author Origin Protocol Inc\n */\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\ncontract OUSD is Governable {\n using SafeCast for int256;\n using SafeCast for uint256;\n\n /// @dev Event triggered when the supply changes\n /// @param totalSupply Updated token total supply\n /// @param rebasingCredits Updated token rebasing credits\n /// @param rebasingCreditsPerToken Updated token rebasing credits per token\n event TotalSupplyUpdatedHighres(\n uint256 totalSupply,\n uint256 rebasingCredits,\n uint256 rebasingCreditsPerToken\n );\n /// @dev Event triggered when an account opts in for rebasing\n /// @param account Address of the account\n event AccountRebasingEnabled(address account);\n /// @dev Event triggered when an account opts out of rebasing\n /// @param account Address of the account\n event AccountRebasingDisabled(address account);\n /// @dev Emitted when `value` tokens are moved from one account `from` to\n /// another `to`.\n /// @param from Address of the account tokens are moved from\n /// @param to Address of the account tokens are moved to\n /// @param value Amount of tokens transferred\n event Transfer(address indexed from, address indexed to, uint256 value);\n /// @dev Emitted when the allowance of a `spender` for an `owner` is set by\n /// a call to {approve}. `value` is the new allowance.\n /// @param owner Address of the owner approving allowance\n /// @param spender Address of the spender allowance is granted to\n /// @param value Amount of tokens spender can transfer\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n /// @dev Yield resulting from {changeSupply} that a `source` account would\n /// receive is directed to `target` account.\n /// @param source Address of the source forwarding the yield\n /// @param target Address of the target receiving the yield\n event YieldDelegated(address source, address target);\n /// @dev Yield delegation from `source` account to the `target` account is\n /// suspended.\n /// @param source Address of the source suspending yield forwarding\n /// @param target Address of the target no longer receiving yield from `source`\n /// account\n event YieldUndelegated(address source, address target);\n\n enum RebaseOptions {\n NotSet,\n StdNonRebasing,\n StdRebasing,\n YieldDelegationSource,\n YieldDelegationTarget\n }\n\n uint256[154] private _gap; // Slots to align with deployed contract\n uint256 private constant MAX_SUPPLY = type(uint128).max;\n /// @dev The amount of tokens in existence\n uint256 public totalSupply;\n mapping(address => mapping(address => uint256)) private allowances;\n /// @dev The vault with privileges to execute {mint}, {burn}\n /// and {changeSupply}\n address public vaultAddress;\n mapping(address => uint256) internal creditBalances;\n // the 2 storage variables below need trailing underscores to not name collide with public functions\n uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts)\n uint256 private rebasingCreditsPerToken_;\n /// @dev The amount of tokens that are not rebasing - receiving yield\n uint256 public nonRebasingSupply;\n mapping(address => uint256) internal alternativeCreditsPerToken;\n /// @dev A map of all addresses and their respective RebaseOptions\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) private __deprecated_isUpgraded;\n /// @dev A map of addresses that have yields forwarded to. This is an\n /// inverse mapping of {yieldFrom}\n /// Key Account forwarding yield\n /// Value Account receiving yield\n mapping(address => address) public yieldTo;\n /// @dev A map of addresses that are receiving the yield. This is an\n /// inverse mapping of {yieldTo}\n /// Key Account receiving yield\n /// Value Account forwarding yield\n mapping(address => address) public yieldFrom;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n uint256[34] private __gap; // including below gap totals up to 200\n\n /// @dev Verifies that the caller is the Governor or Strategist.\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /// @dev Initializes the contract and sets necessary variables.\n /// @param _vaultAddress Address of the vault contract\n /// @param _initialCreditsPerToken The starting rebasing credits per token.\n function initialize(address _vaultAddress, uint256 _initialCreditsPerToken)\n external\n onlyGovernor\n {\n require(_vaultAddress != address(0), \"Zero vault address\");\n require(vaultAddress == address(0), \"Already initialized\");\n\n rebasingCreditsPerToken_ = _initialCreditsPerToken;\n vaultAddress = _vaultAddress;\n }\n\n /// @dev Returns the symbol of the token, a shorter version\n /// of the name.\n function symbol() external pure virtual returns (string memory) {\n return \"OUSD\";\n }\n\n /// @dev Returns the name of the token.\n function name() external pure virtual returns (string memory) {\n return \"Origin Dollar\";\n }\n\n /// @dev Returns the number of decimals used to get its user representation.\n function decimals() external pure virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev Verifies that the caller is the Vault contract\n */\n modifier onlyVault() {\n require(vaultAddress == msg.sender, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() external view returns (uint256) {\n return rebasingCreditsPerToken_;\n }\n\n /**\n * @return Low resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() external view returns (uint256) {\n return rebasingCreditsPerToken_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() external view returns (uint256) {\n return rebasingCredits_;\n }\n\n /**\n * @return Low resolution total number of rebasing credits\n */\n function rebasingCredits() external view returns (uint256) {\n return rebasingCredits_ / RESOLUTION_INCREASE;\n }\n\n /**\n * @notice Gets the balance of the specified address.\n * @param _account Address to query the balance of.\n * @return A uint256 representing the amount of base units owned by the\n * specified address.\n */\n function balanceOf(address _account) public view returns (uint256) {\n RebaseOptions state = rebaseState[_account];\n if (state == RebaseOptions.YieldDelegationSource) {\n // Saves a slot read when transferring to or from a yield delegating source\n // since we know creditBalances equals the balance.\n return creditBalances[_account];\n }\n uint256 baseBalance = (creditBalances[_account] * 1e18) /\n _creditsPerToken(_account);\n if (state == RebaseOptions.YieldDelegationTarget) {\n // creditBalances of yieldFrom accounts equals token balances\n return baseBalance - creditBalances[yieldFrom[_account]];\n }\n return baseBalance;\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @dev Backwards compatible with old low res credits per token.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256) Credit balance and credits per token of the\n * address\n */\n function creditsBalanceOf(address _account)\n external\n view\n returns (uint256, uint256)\n {\n uint256 cpt = _creditsPerToken(_account);\n if (cpt == 1e27) {\n // For a period before the resolution upgrade, we created all new\n // contract accounts at high resolution. Since they are not changing\n // as a result of this upgrade, we will return their true values\n return (creditBalances[_account], cpt);\n } else {\n return (\n creditBalances[_account] / RESOLUTION_INCREASE,\n cpt / RESOLUTION_INCREASE\n );\n }\n }\n\n /**\n * @notice Gets the credits balance of the specified address.\n * @param _account The address to query the balance of.\n * @return (uint256, uint256, bool) Credit balance, credits per token of the\n * address, and isUpgraded\n */\n function creditsBalanceOfHighres(address _account)\n external\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n creditBalances[_account],\n _creditsPerToken(_account),\n true // all accounts have their resolution \"upgraded\"\n );\n }\n\n // Backwards compatible view\n function nonRebasingCreditsPerToken(address _account)\n external\n view\n returns (uint256)\n {\n return alternativeCreditsPerToken[_account];\n }\n\n /**\n * @notice Transfer tokens to a specified address.\n * @param _to the address to transfer to.\n * @param _value the amount to be transferred.\n * @return true on success.\n */\n function transfer(address _to, uint256 _value) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n\n _executeTransfer(msg.sender, _to, _value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n /**\n * @notice Transfer tokens from one address to another.\n * @param _from The address you want to send tokens from.\n * @param _to The address you want to transfer to.\n * @param _value The amount of tokens to be transferred.\n * @return true on success.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n require(_to != address(0), \"Transfer to zero address\");\n uint256 userAllowance = allowances[_from][msg.sender];\n require(_value <= userAllowance, \"Allowance exceeded\");\n\n unchecked {\n allowances[_from][msg.sender] = userAllowance - _value;\n }\n\n _executeTransfer(_from, _to, _value);\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function _executeTransfer(\n address _from,\n address _to,\n uint256 _value\n ) internal {\n (\n int256 fromRebasingCreditsDiff,\n int256 fromNonRebasingSupplyDiff\n ) = _adjustAccount(_from, -_value.toInt256());\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_to, _value.toInt256());\n\n _adjustGlobals(\n fromRebasingCreditsDiff + toRebasingCreditsDiff,\n fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff\n );\n }\n\n function _adjustAccount(address _account, int256 _balanceChange)\n internal\n returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff)\n {\n RebaseOptions state = rebaseState[_account];\n int256 currentBalance = balanceOf(_account).toInt256();\n if (currentBalance + _balanceChange < 0) {\n revert(\"Transfer amount exceeds balance\");\n }\n uint256 newBalance = (currentBalance + _balanceChange).toUint256();\n\n if (state == RebaseOptions.YieldDelegationSource) {\n address target = yieldTo[_account];\n uint256 targetOldBalance = balanceOf(target);\n uint256 targetNewCredits = _balanceToRebasingCredits(\n targetOldBalance + newBalance\n );\n rebasingCreditsDiff =\n targetNewCredits.toInt256() -\n creditBalances[target].toInt256();\n\n creditBalances[_account] = newBalance;\n creditBalances[target] = targetNewCredits;\n } else if (state == RebaseOptions.YieldDelegationTarget) {\n uint256 newCredits = _balanceToRebasingCredits(\n newBalance + creditBalances[yieldFrom[_account]]\n );\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n } else {\n _autoMigrate(_account);\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem > 0) {\n nonRebasingSupplyDiff = _balanceChange;\n if (alternativeCreditsPerTokenMem != 1e18) {\n alternativeCreditsPerToken[_account] = 1e18;\n }\n creditBalances[_account] = newBalance;\n } else {\n uint256 newCredits = _balanceToRebasingCredits(newBalance);\n rebasingCreditsDiff =\n newCredits.toInt256() -\n creditBalances[_account].toInt256();\n creditBalances[_account] = newCredits;\n }\n }\n }\n\n function _adjustGlobals(\n int256 _rebasingCreditsDiff,\n int256 _nonRebasingSupplyDiff\n ) internal {\n if (_rebasingCreditsDiff != 0) {\n rebasingCredits_ = (rebasingCredits_.toInt256() +\n _rebasingCreditsDiff).toUint256();\n }\n if (_nonRebasingSupplyDiff != 0) {\n nonRebasingSupply = (nonRebasingSupply.toInt256() +\n _nonRebasingSupplyDiff).toUint256();\n }\n }\n\n /**\n * @notice Function to check the amount of tokens that _owner has allowed\n * to `_spender`.\n * @param _owner The address which owns the funds.\n * @param _spender The address which will spend the funds.\n * @return The number of tokens still available for the _spender.\n */\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowances[_owner][_spender];\n }\n\n /**\n * @notice Approve the passed address to spend the specified amount of\n * tokens on behalf of msg.sender.\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return true on success.\n */\n function approve(address _spender, uint256 _value) external returns (bool) {\n allowances[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice Creates `_amount` tokens and assigns them to `_account`,\n * increasing the total supply.\n */\n function mint(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Mint to the zero address\");\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, _amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply + _amount;\n\n require(totalSupply < MAX_SUPPLY, \"Max supply\");\n emit Transfer(address(0), _account, _amount);\n }\n\n /**\n * @notice Destroys `_amount` tokens from `_account`,\n * reducing the total supply.\n */\n function burn(address _account, uint256 _amount) external onlyVault {\n require(_account != address(0), \"Burn from the zero address\");\n if (_amount == 0) {\n return;\n }\n\n // Account\n (\n int256 toRebasingCreditsDiff,\n int256 toNonRebasingSupplyDiff\n ) = _adjustAccount(_account, -_amount.toInt256());\n // Globals\n _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff);\n totalSupply = totalSupply - _amount;\n\n emit Transfer(_account, address(0), _amount);\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[\n _account\n ];\n if (alternativeCreditsPerTokenMem != 0) {\n return alternativeCreditsPerTokenMem;\n } else {\n return rebasingCreditsPerToken_;\n }\n }\n\n /**\n * @dev Auto migrate contracts to be non rebasing,\n * unless they have opted into yield.\n * @param _account Address of the account.\n */\n function _autoMigrate(address _account) internal {\n uint256 codeLen = _account.code.length;\n bool isEOA = (codeLen == 0) ||\n (codeLen == 23 && bytes3(_account.code) == 0xef0100);\n // In previous code versions, contracts would not have had their\n // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated\n // therefore we check the actual accounting used on the account as well.\n if (\n (!isEOA) &&\n rebaseState[_account] == RebaseOptions.NotSet &&\n alternativeCreditsPerToken[_account] == 0\n ) {\n _rebaseOptOut(_account);\n }\n }\n\n /**\n * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and\n * also balance that corresponds to those credits. The latter is important\n * when adjusting the contract's global nonRebasingSupply to circumvent any\n * possible rounding errors.\n *\n * @param _balance Balance of the account.\n */\n function _balanceToRebasingCredits(uint256 _balance)\n internal\n view\n returns (uint256 rebasingCredits)\n {\n // Rounds up, because we need to ensure that accounts always have\n // at least the balance that they should have.\n // Note this should always be used on an absolute account value,\n // not on a possibly negative diff, because then the rounding would be wrong.\n return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18;\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n * @param _account Address of the account.\n */\n function governanceRebaseOptIn(address _account) external onlyGovernor {\n require(_account != address(0), \"Zero address not allowed\");\n _rebaseOptIn(_account);\n }\n\n /**\n * @notice The calling account will start receiving yield after a successful call.\n */\n function rebaseOptIn() external {\n _rebaseOptIn(msg.sender);\n }\n\n function _rebaseOptIn(address _account) internal {\n uint256 balance = balanceOf(_account);\n\n // prettier-ignore\n require(\n alternativeCreditsPerToken[_account] > 0 ||\n // Accounts may explicitly `rebaseOptIn` regardless of\n // accounting if they have a 0 balance.\n creditBalances[_account] == 0\n ,\n \"Account must be non-rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n // prettier-ignore\n require(\n state == RebaseOptions.StdNonRebasing ||\n state == RebaseOptions.NotSet,\n \"Only standard non-rebasing accounts can opt in\"\n );\n\n uint256 newCredits = _balanceToRebasingCredits(balance);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdRebasing;\n alternativeCreditsPerToken[_account] = 0;\n creditBalances[_account] = newCredits;\n // Globals\n _adjustGlobals(newCredits.toInt256(), -balance.toInt256());\n\n emit AccountRebasingEnabled(_account);\n }\n\n /**\n * @notice The calling account will no longer receive yield\n */\n function rebaseOptOut() external {\n _rebaseOptOut(msg.sender);\n }\n\n function _rebaseOptOut(address _account) internal {\n require(\n alternativeCreditsPerToken[_account] == 0,\n \"Account must be rebasing\"\n );\n RebaseOptions state = rebaseState[_account];\n require(\n state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet,\n \"Only standard rebasing accounts can opt out\"\n );\n\n uint256 oldCredits = creditBalances[_account];\n uint256 balance = balanceOf(_account);\n\n // Account\n rebaseState[_account] = RebaseOptions.StdNonRebasing;\n alternativeCreditsPerToken[_account] = 1e18;\n creditBalances[_account] = balance;\n // Globals\n _adjustGlobals(-oldCredits.toInt256(), balance.toInt256());\n\n emit AccountRebasingDisabled(_account);\n }\n\n /**\n * @notice Distribute yield to users. This changes the exchange rate\n * between \"credits\" and OUSD tokens to change rebasing user's balances.\n * @param _newTotalSupply New total supply of OUSD.\n */\n function changeSupply(uint256 _newTotalSupply) external onlyVault {\n require(totalSupply > 0, \"Cannot increase 0 supply\");\n\n if (totalSupply == _newTotalSupply) {\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n return;\n }\n\n totalSupply = _newTotalSupply > MAX_SUPPLY\n ? MAX_SUPPLY\n : _newTotalSupply;\n\n uint256 rebasingSupply = totalSupply - nonRebasingSupply;\n // round up in the favour of the protocol\n rebasingCreditsPerToken_ =\n (rebasingCredits_ * 1e18 + rebasingSupply - 1) /\n rebasingSupply;\n\n require(rebasingCreditsPerToken_ > 0, \"Invalid change in supply\");\n\n emit TotalSupplyUpdatedHighres(\n totalSupply,\n rebasingCredits_,\n rebasingCreditsPerToken_\n );\n }\n\n /*\n * @notice Send the yield from one account to another account.\n * Each account keeps its own balances.\n */\n function delegateYield(address _from, address _to)\n external\n onlyGovernorOrStrategist\n {\n require(_from != address(0), \"Zero from address not allowed\");\n require(_to != address(0), \"Zero to address not allowed\");\n\n require(_from != _to, \"Cannot delegate to self\");\n require(\n yieldFrom[_to] == address(0) &&\n yieldTo[_to] == address(0) &&\n yieldFrom[_from] == address(0) &&\n yieldTo[_from] == address(0),\n \"Blocked by existing yield delegation\"\n );\n RebaseOptions stateFrom = rebaseState[_from];\n RebaseOptions stateTo = rebaseState[_to];\n\n require(\n stateFrom == RebaseOptions.NotSet ||\n stateFrom == RebaseOptions.StdNonRebasing ||\n stateFrom == RebaseOptions.StdRebasing,\n \"Invalid rebaseState from\"\n );\n\n require(\n stateTo == RebaseOptions.NotSet ||\n stateTo == RebaseOptions.StdNonRebasing ||\n stateTo == RebaseOptions.StdRebasing,\n \"Invalid rebaseState to\"\n );\n\n if (alternativeCreditsPerToken[_from] == 0) {\n _rebaseOptOut(_from);\n }\n if (alternativeCreditsPerToken[_to] > 0) {\n _rebaseOptIn(_to);\n }\n\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(_to);\n uint256 oldToCredits = creditBalances[_to];\n uint256 newToCredits = _balanceToRebasingCredits(\n fromBalance + toBalance\n );\n\n // Set up the bidirectional links\n yieldTo[_from] = _to;\n yieldFrom[_to] = _from;\n\n // Local\n rebaseState[_from] = RebaseOptions.YieldDelegationSource;\n alternativeCreditsPerToken[_from] = 1e18;\n creditBalances[_from] = fromBalance;\n rebaseState[_to] = RebaseOptions.YieldDelegationTarget;\n creditBalances[_to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, -(fromBalance).toInt256());\n emit YieldDelegated(_from, _to);\n }\n\n /*\n * @notice Stop sending the yield from one account to another account.\n */\n function undelegateYield(address _from) external onlyGovernorOrStrategist {\n // Require a delegation, which will also ensure a valid delegation\n require(yieldTo[_from] != address(0), \"Zero address not allowed\");\n\n address to = yieldTo[_from];\n uint256 fromBalance = balanceOf(_from);\n uint256 toBalance = balanceOf(to);\n uint256 oldToCredits = creditBalances[to];\n uint256 newToCredits = _balanceToRebasingCredits(toBalance);\n\n // Remove the bidirectional links\n yieldFrom[to] = address(0);\n yieldTo[_from] = address(0);\n\n // Local\n rebaseState[_from] = RebaseOptions.StdNonRebasing;\n // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()`\n creditBalances[_from] = fromBalance;\n rebaseState[to] = RebaseOptions.StdRebasing;\n // alternativeCreditsPerToken[to] already 0 from `delegateYield()`\n creditBalances[to] = newToCredits;\n\n // Global\n int256 creditsChange = newToCredits.toInt256() -\n oldToCredits.toInt256();\n _adjustGlobals(creditsChange, fromBalance.toInt256());\n emit YieldUndelegated(_from, to);\n }\n}\n" + }, + "contracts/token/OUSDResolutionUpgrade.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract OUSDResolutionUpgrade {\n enum RebaseOptions {\n NotSet,\n OptOut,\n OptIn\n }\n\n // From Initializable\n bool private initialized;\n bool private initializing;\n uint256[50] private ______igap;\n\n // From InitializableERC20Detailed\n uint256[100] private _____ugap;\n string private _name;\n string private _symbol;\n uint8 private _decimals;\n\n // From OUSD\n uint256 private constant MAX_SUPPLY = ~uint128(0); // (2^128) - 1\n uint256 public _totalSupply;\n mapping(address => mapping(address => uint256)) private _allowances;\n address public vaultAddress = address(0);\n mapping(address => uint256) private _creditBalances;\n uint256 private _rebasingCredits;\n uint256 private _rebasingCreditsPerToken;\n uint256 public nonRebasingSupply;\n mapping(address => uint256) public nonRebasingCreditsPerToken;\n mapping(address => RebaseOptions) public rebaseState;\n mapping(address => uint256) public isUpgraded;\n\n uint256 private constant RESOLUTION_INCREASE = 1e9;\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerToken() public view returns (uint256) {\n return _rebasingCreditsPerToken / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCredits() public view returns (uint256) {\n return _rebasingCredits / RESOLUTION_INCREASE;\n }\n\n /**\n * @return High resolution rebasingCreditsPerToken\n */\n function rebasingCreditsPerTokenHighres() public view returns (uint256) {\n return _rebasingCreditsPerToken;\n }\n\n /**\n * @return High resolution total number of rebasing credits\n */\n function rebasingCreditsHighres() public view returns (uint256) {\n return _rebasingCredits;\n }\n\n function upgradeGlobals() external {\n require(isUpgraded[address(0)] == 0, \"Globals already upgraded\");\n require(_rebasingCredits > 0, \"Sanity _rebasingCredits\");\n require(\n _rebasingCreditsPerToken > 0,\n \"Sanity _rebasingCreditsPerToken\"\n );\n isUpgraded[address(0)] = 1;\n _rebasingCredits = _rebasingCredits * RESOLUTION_INCREASE;\n _rebasingCreditsPerToken =\n _rebasingCreditsPerToken *\n RESOLUTION_INCREASE;\n }\n\n function upgradeAccounts(address[] calldata accounts) external {\n for (uint256 i = 0; i < accounts.length; i++) {\n address account = accounts[i];\n require(account != address(0), \"Reserved\");\n require(isUpgraded[account] == 0, \"Account already upgraded\");\n isUpgraded[account] = 1;\n\n // Handle special for non-rebasing accounts\n uint256 nrc = nonRebasingCreditsPerToken[account];\n if (nrc > 0) {\n require(nrc < 1e18, \"Account already highres\");\n nonRebasingCreditsPerToken[account] = nrc * RESOLUTION_INCREASE;\n }\n // Upgrade balance\n uint256 balance = _creditBalances[account];\n require(balance > 0, \"Will not upgrade zero balance\");\n _creditBalances[account] = balance * RESOLUTION_INCREASE;\n }\n }\n\n function creditsBalanceOfHighres(address _account)\n public\n view\n returns (\n uint256,\n uint256,\n bool\n )\n {\n return (\n _creditBalances[_account],\n _creditsPerToken(_account),\n isUpgraded[_account] == 1\n );\n }\n\n /**\n * @dev Get the credits per token for an account. Returns a fixed amount\n * if the account is non-rebasing.\n * @param _account Address of the account.\n */\n function _creditsPerToken(address _account)\n internal\n view\n returns (uint256)\n {\n if (nonRebasingCreditsPerToken[_account] != 0) {\n return nonRebasingCreditsPerToken[_account];\n } else {\n return _rebasingCreditsPerToken;\n }\n }\n}\n" + }, + "contracts/token/WOETH.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC4626 } from \"../../lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Governable } from \"../governance/Governable.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { OETH } from \"./OETH.sol\";\n\n/**\n * @title Wrapped OETH Token Contract\n * @author Origin Protocol Inc\n *\n * @dev An important capability of this contract is that it isn't susceptible to changes of the\n * exchange rate of WOETH/OETH if/when someone sends the underlying asset (OETH) to the contract.\n * If OETH weren't rebasing this could be achieved by solely tracking the ERC20 transfers of the OETH\n * token on mint, deposit, redeem, withdraw. The issue is that OETH is rebasing and OETH balances\n * will change when the token rebases.\n * For that reason the contract logic checks the actual underlying OETH token balance only once\n * (either on a fresh contract creation or upgrade) and considering the WOETH supply and\n * rebasingCreditsPerToken calculates the _adjuster. Once the adjuster is calculated any donations\n * to the contract are ignored. The totalSupply (instead of querying OETH balance) works off of\n * adjuster the current WOETH supply and rebasingCreditsPerToken. This makes WOETH value accrual\n * completely follow OETH's value accrual.\n * WOETH is safe to use in lending markets as the VualtCore's _rebase contains safeguards preventing\n * any sudden large rebases.\n */\n\ncontract WOETH is ERC4626, Governable, Initializable {\n using SafeERC20 for IERC20;\n /* This is a 1e27 adjustment constant that expresses the difference in exchange rate between\n * OETH's rebase since inception (expressed with rebasingCreditsPerToken) and WOETH to OETH\n * conversion.\n *\n * If WOETH and OETH are deployed at the same time, the value of adjuster is a neutral 1e27\n */\n uint256 public adjuster;\n uint256[49] private __gap;\n\n // no need to set ERC20 name and symbol since they are overridden in WOETH & WOETHBase\n constructor(ERC20 underlying_) ERC20(\"\", \"\") ERC4626(underlying_) {}\n\n /**\n * @notice Enable OETH rebasing for this contract\n */\n function initialize() external onlyGovernor initializer {\n OETH(address(asset())).rebaseOptIn();\n\n initialize2();\n }\n\n /**\n * @notice secondary initializer that newly deployed contracts will execute as part\n * of primary initialize function and the existing contracts will have it called\n * as a governance operation.\n */\n function initialize2() public onlyGovernor {\n require(adjuster == 0, \"Initialize2 already called\");\n\n if (totalSupply() == 0) {\n adjuster = 1e27;\n } else {\n adjuster =\n (rebasingCreditsPerTokenHighres() *\n ERC20(asset()).balanceOf(address(this))) /\n totalSupply();\n }\n }\n\n function name()\n public\n view\n virtual\n override(ERC20, IERC20Metadata)\n returns (string memory)\n {\n return \"Wrapped OETH\";\n }\n\n function symbol()\n public\n view\n virtual\n override(ERC20, IERC20Metadata)\n returns (string memory)\n {\n return \"wOETH\";\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends. Cannot transfer OETH\n * @param asset_ Address for the asset\n * @param amount_ Amount of the asset to transfer\n */\n function transferToken(address asset_, uint256 amount_)\n external\n onlyGovernor\n {\n require(asset_ != address(asset()), \"Cannot collect core asset\");\n IERC20(asset_).safeTransfer(governor(), amount_);\n }\n\n /// @inheritdoc ERC4626\n function convertToShares(uint256 assets)\n public\n view\n virtual\n override\n returns (uint256 shares)\n {\n return (assets * rebasingCreditsPerTokenHighres()) / adjuster;\n }\n\n /// @inheritdoc ERC4626\n function convertToAssets(uint256 shares)\n public\n view\n virtual\n override\n returns (uint256 assets)\n {\n return (shares * adjuster) / rebasingCreditsPerTokenHighres();\n }\n\n /// @inheritdoc ERC4626\n function totalAssets() public view override returns (uint256) {\n return (totalSupply() * adjuster) / rebasingCreditsPerTokenHighres();\n }\n\n function rebasingCreditsPerTokenHighres() internal view returns (uint256) {\n return OETH(asset()).rebasingCreditsPerTokenHighres();\n }\n}\n" + }, + "contracts/token/WOETHBase.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WOETH } from \"./WOETH.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @title OETH Token Contract\n * @author Origin Protocol Inc\n */\n\ncontract WOETHBase is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name() public view virtual override returns (string memory) {\n return \"Wrapped Super OETH\";\n }\n\n function symbol() public view virtual override returns (string memory) {\n return \"wsuperOETHb\";\n }\n}\n" + }, + "contracts/token/WOETHPlume.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { WOETH } from \"./WOETH.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @title wOETH (Plume) Token Contract\n * @author Origin Protocol Inc\n */\n\ncontract WOETHPlume is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name() public view virtual override returns (string memory) {\n return \"Wrapped Super OETH\";\n }\n\n function symbol() public view virtual override returns (string memory) {\n return \"wsuperOETHp\";\n }\n}\n" + }, + "contracts/token/WOSonic.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { WOETH } from \"./WOETH.sol\";\n\n/**\n * @title Wrapped Origin Sonic (wOS) token on Sonic\n * @author Origin Protocol Inc\n */\ncontract WOSonic is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"Wrapped OS\";\n }\n\n function symbol()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"wOS\";\n }\n}\n" + }, + "contracts/token/WrappedOusd.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { WOETH } from \"./WOETH.sol\";\n\n/**\n * @title Wrapped OUSD Token Contract\n * @author Origin Protocol Inc\n */\ncontract WrappedOusd is WOETH {\n constructor(ERC20 underlying_) WOETH(underlying_) {}\n\n function name()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"Wrapped OUSD\";\n }\n\n function symbol()\n public\n view\n virtual\n override(WOETH)\n returns (string memory)\n {\n return \"WOUSD\";\n }\n}\n" + }, + "contracts/utils/AerodromeAMOQuoter.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.7;\n\nimport { ICLPool } from \"../interfaces/aerodrome/ICLPool.sol\";\nimport { IQuoterV2 } from \"../interfaces/aerodrome/IQuoterV2.sol\";\nimport { IAMOStrategy } from \"../interfaces/aerodrome/IAMOStrategy.sol\";\n\n/// @title QuoterHelper\n/// @author Origin Protocol\n/// @notice Helper for Aerodrome AMO Quoter, as `_quoteAmountToSwapBeforeRebalance` use try/catch method and\n/// this can only be used when calling external contracts.\ncontract QuoterHelper {\n ////////////////////////////////////////////////////////////////\n /// --- STRUCTS & ENUMS\n ////////////////////////////////////////////////////////////////\n enum RevertReasons {\n DefaultStatus,\n RebalanceOutOfBounds,\n NotInExpectedTickRange,\n NotEnoughWethForSwap,\n NotEnoughWethLiquidity,\n UnexpectedError,\n Found,\n NotFound\n }\n\n struct RebalanceStatus {\n RevertReasons reason;\n uint256 currentPoolWETHShare; // Case 1\n uint256 allowedWETHShareStart; // Case 1\n uint256 allowedWETHShareEnd; // Case 1\n int24 currentTick; // Case 2\n uint256 balanceWETH; // Case 3\n uint256 amountWETH; // Case 3\n string revertMessage;\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTANT & IMMUTABLE\n ////////////////////////////////////////////////////////////////\n uint256 public constant BINARY_MIN_AMOUNT = 1 wei;\n\n uint256 public constant BINARY_MAX_ITERATIONS = 40;\n uint256 public constant PERCENTAGE_BASE = 1e18; // 100%\n uint256 public constant ALLOWED_VARIANCE_PERCENTAGE = 1e15; // 0.1%\n\n ////////////////////////////////////////////////////////////////\n /// --- VARIABLES STORAGE\n ////////////////////////////////////////////////////////////////\n ICLPool public immutable clPool;\n IQuoterV2 public immutable quoterV2;\n IAMOStrategy public immutable strategy;\n\n address public originalGovernor;\n\n ////////////////////////////////////////////////////////////////\n /// --- ERRORS & EVENTS\n ////////////////////////////////////////////////////////////////\n error UnexpectedError(string message);\n error OutOfIterations(uint256 iterations);\n error ValidAmount(\n uint256 amount,\n uint256 iterations,\n bool swapWETHForOETHB\n );\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////////////////\n constructor(IAMOStrategy _strategy, IQuoterV2 _quoterV2) {\n strategy = _strategy;\n quoterV2 = _quoterV2;\n clPool = _strategy.clPool();\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- FUNCTIONS\n ////////////////////////////////////////////////////////////////\n /// @notice This call can only end with a revert.\n function getAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) public {\n if (\n overrideBottomWethShare != type(uint256).max ||\n overrideTopWethShare != type(uint256).max\n ) {\n // Current values\n uint256 shareStart = strategy.allowedWethShareStart();\n uint256 shareEnd = strategy.allowedWethShareEnd();\n\n // Override values\n if (overrideBottomWethShare != type(uint256).max) {\n shareStart = overrideBottomWethShare;\n }\n if (overrideTopWethShare != type(uint256).max) {\n shareEnd = overrideTopWethShare;\n }\n\n strategy.setAllowedPoolWethShareInterval(shareStart, shareEnd);\n }\n\n uint256 iterations = 0;\n uint256 low = BINARY_MIN_AMOUNT;\n uint256 high;\n (high, ) = strategy.getPositionPrincipal();\n int24 lowerTick = strategy.lowerTick();\n int24 upperTick = strategy.upperTick();\n bool swapWETHForOETHB = getSwapDirectionForRebalance();\n\n while (low <= high && iterations < BINARY_MAX_ITERATIONS) {\n uint256 mid = (low + high) / 2;\n\n RebalanceStatus memory status = getRebalanceStatus(\n mid,\n swapWETHForOETHB\n );\n\n // Best case, we found the `amount` that will reach the target pool share!\n // We can revert with the amount and the number of iterations\n if (status.reason == RevertReasons.Found) {\n revert ValidAmount(mid, iterations, swapWETHForOETHB);\n }\n\n // If the rebalance failed then we should try to change the amount.\n // We will handle all possible revert reasons here.\n\n // Case 1: Rebalance out of bounds\n // If the pool is out of bounds, we need to adjust the amount to reach the target pool share\n if (status.reason == RevertReasons.RebalanceOutOfBounds) {\n // If the current pool share is less than the target pool share, we need to increase the amount\n if (\n swapWETHForOETHB\n ? status.currentPoolWETHShare <\n status.allowedWETHShareStart\n : status.currentPoolWETHShare >\n status.allowedWETHShareEnd\n ) {\n low = mid + 1;\n }\n // Else we need to decrease the amount\n else {\n high = mid;\n }\n }\n\n // Case 2: Not in expected tick range\n // If the pool is not in the expected tick range, we need to adjust the amount\n // to reach the target pool share\n if (status.reason == RevertReasons.NotInExpectedTickRange) {\n // If we are buying OETHb and the current tick is greater than the lower tick,\n //we need to increase the amount in order to continue to push price down.\n // If we are selling OETHb and the current tick is less than the upper tick,\n // we need to increase the amount in order to continue to push price up.\n if (\n swapWETHForOETHB\n ? status.currentTick > lowerTick\n : status.currentTick < upperTick\n ) {\n low = mid + 1;\n }\n // Else we need to decrease the amount\n else {\n high = mid;\n }\n }\n\n // Case 3: Not enough WETH for swap\n // If we don't have enough WETH to swap, we need to decrease the amount\n // This error can happen, when initial value of mid is too high, so we need to decrease it\n if (status.reason == RevertReasons.NotEnoughWethForSwap) {\n high = mid;\n }\n\n // Case 4: Not enough WETH liquidity\n // If we don't have enough WETH liquidity\n // Revert for the moment, we need to improve this\n if (status.reason == RevertReasons.NotEnoughWethLiquidity) {\n revert(\"Quoter: Not enough WETH liquidity\");\n }\n\n // Case 5: Unexpected error\n // Worst case, it reverted with an unexpected error.\n if (status.reason == RevertReasons.UnexpectedError) {\n revert UnexpectedError(status.revertMessage);\n }\n\n iterations++;\n }\n\n // Case 6: Out of iterations\n // If we didn't find the amount after the max iterations, we need to revert.\n revert OutOfIterations(iterations);\n }\n\n /// @notice Get the status of the rebalance\n /// @param amount The amount of token to swap\n /// @param swapWETH True if we need to swap WETH for OETHb, false otherwise\n /// @return status The status of the rebalance\n function getRebalanceStatus(uint256 amount, bool swapWETH)\n public\n returns (RebalanceStatus memory status)\n {\n try strategy.rebalance(amount, swapWETH, 0) {\n status.reason = RevertReasons.Found;\n return status;\n } catch Error(string memory reason) {\n status.reason = RevertReasons.UnexpectedError;\n status.revertMessage = reason;\n } catch (bytes memory reason) {\n bytes4 receivedSelector = bytes4(reason);\n\n // Case 1: Rebalance out of bounds\n if (\n receivedSelector ==\n IAMOStrategy.PoolRebalanceOutOfBounds.selector\n ) {\n uint256 currentPoolWETHShare;\n uint256 allowedWETHShareStart;\n uint256 allowedWETHShareEnd;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n currentPoolWETHShare := mload(add(reason, 0x24))\n allowedWETHShareStart := mload(add(reason, 0x44))\n allowedWETHShareEnd := mload(add(reason, 0x64))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.RebalanceOutOfBounds,\n currentPoolWETHShare: currentPoolWETHShare,\n allowedWETHShareStart: allowedWETHShareStart,\n allowedWETHShareEnd: allowedWETHShareEnd,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 2: Not in expected tick range\n if (\n receivedSelector ==\n IAMOStrategy.OutsideExpectedTickRange.selector\n ) {\n int24 currentTick;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n currentTick := mload(add(reason, 0x24))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.NotInExpectedTickRange,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: currentTick,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 3: Not enough WETH for swap\n if (\n receivedSelector == IAMOStrategy.NotEnoughWethForSwap.selector\n ) {\n uint256 balanceWETH;\n uint256 amountWETH;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n balanceWETH := mload(add(reason, 0x24))\n amountWETH := mload(add(reason, 0x44))\n }\n return\n RebalanceStatus({\n reason: RevertReasons.NotEnoughWethForSwap,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: balanceWETH,\n amountWETH: amountWETH,\n revertMessage: \"\"\n });\n }\n\n // Case 4: Not enough WETH liquidity\n if (\n receivedSelector == IAMOStrategy.NotEnoughWethLiquidity.selector\n ) {\n return\n RebalanceStatus({\n reason: RevertReasons.NotEnoughWethLiquidity,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: \"\"\n });\n }\n\n // Case 5: Unexpected error\n return\n RebalanceStatus({\n reason: RevertReasons.UnexpectedError,\n currentPoolWETHShare: 0,\n allowedWETHShareStart: 0,\n allowedWETHShareEnd: 0,\n currentTick: 0,\n balanceWETH: 0,\n amountWETH: 0,\n revertMessage: abi.decode(reason, (string))\n });\n }\n }\n\n /// @notice Get the swap direction to reach the target price before rebalance.\n /// @return bool True if we need to swap WETH for OETHb, false otherwise.\n function getSwapDirectionForRebalance() public view returns (bool) {\n uint160 currentPrice = strategy.getPoolX96Price();\n uint160 ticker0Price = strategy.sqrtRatioX96TickLower();\n uint160 ticker1Price = strategy.sqrtRatioX96TickHigher();\n uint256 allowedWethShareStart = strategy.allowedWethShareStart();\n uint256 allowedWethShareEnd = strategy.allowedWethShareEnd();\n uint160 mid = uint160(allowedWethShareStart + allowedWethShareEnd) / 2;\n // slither-disable-start divide-before-multiply\n uint160 targetPrice = (ticker0Price *\n mid +\n ticker1Price *\n (1 ether - mid)) / 1 ether;\n // slither-disable-end divide-before-multiply\n\n return currentPrice > targetPrice;\n }\n\n // returns total amount in the position principal of the Aerodrome AMO strategy. Needed as a\n // separate function because of the limitation in local variable count in getAmountToSwapToReachPrice\n function getTotalStrategyPosition() internal returns (uint256) {\n (uint256 wethAmount, uint256 oethBalance) = strategy\n .getPositionPrincipal();\n return wethAmount + oethBalance;\n }\n\n /// @notice Get the amount of tokens to swap to reach the target price.\n /// @dev This act like a quoter, i.e. the transaction is not performed.\n /// @dev Because the amount to swap can be largely overestimated, because CLAMM alow partial orders,\n /// i.e. when we ask to swap a very large amount, with a close priceLimite, it will swap only a part of it,\n /// and not revert. So if overestimated amount is to high, use a custom maxAmount to avoid this issue.\n /// @param sqrtPriceTargetX96 The target price to reach.\n /// @return amount The amount of tokens to swap.\n /// @return iterations The number of iterations to find the amount.\n /// @return swapWETHForOETHB True if we need to swap WETH for OETHb, false otherwise.\n /// @return sqrtPriceX96After The price after the swap.\n function getAmountToSwapToReachPrice(uint160 sqrtPriceTargetX96)\n public\n returns (\n uint256,\n uint256,\n bool,\n uint160\n )\n {\n uint256 iterations = 0;\n uint256 low = BINARY_MIN_AMOUNT;\n // high search start is twice the position principle of Aerodrome AMO strategy.\n // should be more than enough\n uint256 high = getTotalStrategyPosition() * 2;\n bool swapWETHForOETHB = getSwapDirection(sqrtPriceTargetX96);\n\n while (low <= high && iterations < BINARY_MAX_ITERATIONS) {\n uint256 mid = (low + high) / 2;\n\n // Call QuoterV2 from SugarHelper\n (uint256 amountOut, uint160 sqrtPriceX96After, , ) = quoterV2\n .quoteExactInputSingle(\n IQuoterV2.QuoteExactInputSingleParams({\n tokenIn: swapWETHForOETHB\n ? clPool.token0()\n : clPool.token1(),\n tokenOut: swapWETHForOETHB\n ? clPool.token1()\n : clPool.token0(),\n amountIn: mid,\n tickSpacing: strategy.tickSpacing(),\n sqrtPriceLimitX96: sqrtPriceTargetX96\n })\n );\n\n if (\n isWithinAllowedVariance(sqrtPriceX96After, sqrtPriceTargetX96)\n ) {\n /** Very important to return `amountOut` instead of `mid` as the first return parameter.\n * The issues was that when quoting we impose a swap price limit (sqrtPriceLimitX96: sqrtPriceTargetX96)\n * and in that case the `amountIn` acts like a maximum amount to swap. And we don't know how much\n * of that amount was actually consumed. For that reason we \"estimate\" it by returning the\n * amountOut since that is only going to be a couple of basis point away from amountIn in the\n * worst cases.\n *\n * Note: we could be returning mid instead of amountOut in cases when those values are only basis\n * points apart (assuming that complete balance of amountIn has been consumed) but that might increase\n * complexity too much in an already complex contract.\n */\n return (\n amountOut,\n iterations,\n swapWETHForOETHB,\n sqrtPriceX96After\n );\n } else if (low == high) {\n // target swap amount not found.\n // might be that \"high\" amount is too low on start\n revert(\"SwapAmountNotFound\");\n } else if (\n swapWETHForOETHB\n ? sqrtPriceX96After > sqrtPriceTargetX96\n : sqrtPriceX96After < sqrtPriceTargetX96\n ) {\n low = mid + 1;\n } else {\n high = mid;\n }\n iterations++;\n }\n\n revert OutOfIterations(iterations);\n }\n\n /// @notice Check if the current price is within the allowed variance in comparison to the target price\n /// @return bool True if the current price is within the allowed variance, false otherwise\n function isWithinAllowedVariance(\n uint160 sqrtPriceCurrentX96,\n uint160 sqrtPriceTargetX96\n ) public view returns (bool) {\n uint160 range = strategy.sqrtRatioX96TickHigher() -\n strategy.sqrtRatioX96TickLower();\n if (sqrtPriceCurrentX96 > sqrtPriceTargetX96) {\n return\n (sqrtPriceCurrentX96 - sqrtPriceTargetX96) <=\n (ALLOWED_VARIANCE_PERCENTAGE * range) / PERCENTAGE_BASE;\n } else {\n return\n (sqrtPriceTargetX96 - sqrtPriceCurrentX96) <=\n (ALLOWED_VARIANCE_PERCENTAGE * range) / PERCENTAGE_BASE;\n }\n }\n\n /// @notice Get the swap direction to reach the target price.\n /// @param sqrtPriceTargetX96 The target price to reach.\n /// @return bool True if we need to swap WETH for OETHb, false otherwise.\n function getSwapDirection(uint160 sqrtPriceTargetX96)\n public\n view\n returns (bool)\n {\n uint160 currentPrice = strategy.getPoolX96Price();\n return currentPrice > sqrtPriceTargetX96;\n }\n\n function claimGovernanceOnAMO() public {\n originalGovernor = strategy.governor();\n strategy.claimGovernance();\n }\n\n function giveBackGovernanceOnAMO() public {\n require(\n originalGovernor != address(0),\n \"Quoter: Original governor not set\"\n );\n strategy.transferGovernance(originalGovernor);\n }\n}\n\n/// @title AerodromeAMOQuoter\n/// @author Origin Protocol\n/// @notice Quoter for Aerodrome AMO\ncontract AerodromeAMOQuoter {\n ////////////////////////////////////////////////////////////////\n /// --- STRUCTS & ENUMS\n ///////////////////////////////////////////////////////////////\n struct Data {\n uint256 amount;\n uint256 iterations;\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- VARIABLES STORAGE\n ////////////////////////////////////////////////////////////////\n QuoterHelper public immutable quoterHelper;\n\n ////////////////////////////////////////////////////////////////\n /// --- CONSTRUCTOR\n ////////////////////////////////////////////////////////////////\n constructor(address _strategy, address _quoterV2) {\n quoterHelper = new QuoterHelper(\n IAMOStrategy(_strategy),\n IQuoterV2(_quoterV2)\n );\n }\n\n ////////////////////////////////////////////////////////////////\n /// --- ERRORS & EVENTS\n ////////////////////////////////////////////////////////////////\n event ValueFound(uint256 value, uint256 iterations, bool swapWETHForOETHB);\n event ValueFoundBis(\n uint256 value,\n uint256 iterations,\n bool swapWETHForOETHB,\n uint160 sqrtPriceAfterX96\n );\n event ValueNotFound(string message);\n\n ////////////////////////////////////////////////////////////////\n /// --- FUNCTIONS\n ////////////////////////////////////////////////////////////////\n /// @notice Use this to get the amount to swap before rebalance\n /// @dev This call will only revert, check the logs to get returned values.\n /// @dev Need to perform this call while impersonating the governor or strategist of AMO.\n /// @return data Data struct with the amount and the number of iterations\n function quoteAmountToSwapBeforeRebalance()\n public\n returns (Data memory data)\n {\n return\n _quoteAmountToSwapBeforeRebalance(\n type(uint256).max,\n type(uint256).max\n );\n }\n\n /// @notice Use this to get the amount to swap before rebalance and\n /// update allowedWethShareStart and allowedWethShareEnd on AMO.\n /// @dev This call will only revert, check the logs to get returned values.\n /// @dev Need to perform this call while impersonating the governor of AMO.\n /// @param overrideBottomWethShare New value for the allowedWethShareStart on AMO.\n /// Use type(uint256).max to keep same value.\n /// @param overrideTopWethShare New value for the allowedWethShareEnd on AMO.\n /// Use type(uint256).max to keep same value.\n /// @return data Data struct with the amount and the number of iterations\n function quoteAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) public returns (Data memory data) {\n return\n _quoteAmountToSwapBeforeRebalance(\n overrideBottomWethShare,\n overrideTopWethShare\n );\n }\n\n /// @notice Internal logic for quoteAmountToSwapBeforeRebalance.\n function _quoteAmountToSwapBeforeRebalance(\n uint256 overrideBottomWethShare,\n uint256 overrideTopWethShare\n ) internal returns (Data memory data) {\n try\n quoterHelper.getAmountToSwapBeforeRebalance(\n overrideBottomWethShare,\n overrideTopWethShare\n )\n {\n revert(\"Previous call should only revert, it cannot succeed\");\n } catch (bytes memory reason) {\n bytes4 receivedSelector = bytes4(reason);\n if (receivedSelector == QuoterHelper.ValidAmount.selector) {\n uint256 value;\n uint256 iterations;\n bool swapWETHForOETHB;\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n value := mload(add(reason, 0x24))\n iterations := mload(add(reason, 0x44))\n swapWETHForOETHB := mload(add(reason, 0x64))\n }\n emit ValueFound(value, iterations, swapWETHForOETHB);\n return Data({ amount: value, iterations: iterations });\n }\n\n if (receivedSelector == QuoterHelper.OutOfIterations.selector) {\n emit ValueNotFound(\"Out of iterations\");\n revert(\"Out of iterations\");\n }\n\n emit ValueNotFound(\"Unexpected error\");\n revert(abi.decode(reason, (string)));\n }\n }\n\n /// @notice Use this to get the amount to swap to reach the target price after swap.\n /// @dev This call will only revert, check the logs to get returned values.\n /// @param sqrtPriceTargetX96 The target price to reach.\n function quoteAmountToSwapToReachPrice(uint160 sqrtPriceTargetX96) public {\n (\n uint256 amount,\n uint256 iterations,\n bool swapWETHForOETHB,\n uint160 sqrtPriceAfterX96\n ) = quoterHelper.getAmountToSwapToReachPrice(sqrtPriceTargetX96);\n\n emit ValueFoundBis(\n amount,\n iterations,\n swapWETHForOETHB,\n sqrtPriceAfterX96\n );\n }\n\n function claimGovernance() public {\n quoterHelper.claimGovernanceOnAMO();\n }\n\n function giveBackGovernance() public {\n quoterHelper.giveBackGovernanceOnAMO();\n }\n}\n" + }, + "contracts/utils/BalancerErrors.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity >=0.7.4 <0.9.0;\n\n// solhint-disable\n\n/**\n * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are\n * supported.\n * Uses the default 'BAL' prefix for the error code\n */\nfunction _require(bool condition, uint256 errorCode) pure {\n if (!condition) _revert(errorCode);\n}\n\n/**\n * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are\n * supported.\n */\nfunction _require(\n bool condition,\n uint256 errorCode,\n bytes3 prefix\n) pure {\n if (!condition) _revert(errorCode, prefix);\n}\n\n/**\n * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported.\n * Uses the default 'BAL' prefix for the error code\n */\nfunction _revert(uint256 errorCode) pure {\n _revert(errorCode, 0x42414c); // This is the raw byte representation of \"BAL\"\n}\n\n/**\n * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported.\n */\nfunction _revert(uint256 errorCode, bytes3 prefix) pure {\n uint256 prefixUint = uint256(uint24(prefix));\n // We're going to dynamically create a revert string based on the error code, with the following format:\n // 'BAL#{errorCode}'\n // where the code is left-padded with zeroes to three digits (so they range from 000 to 999).\n //\n // We don't have revert strings embedded in the contract to save bytecode size: it takes much less space to store a\n // number (8 to 16 bits) than the individual string characters.\n //\n // The dynamic string creation algorithm that follows could be implemented in Solidity, but assembly allows for a\n // much denser implementation, again saving bytecode size. Given this function unconditionally reverts, this is a\n // safe place to rely on it without worrying about how its usage might affect e.g. memory contents.\n assembly {\n // First, we need to compute the ASCII representation of the error code. We assume that it is in the 0-999\n // range, so we only need to convert three digits. To convert the digits to ASCII, we add 0x30, the value for\n // the '0' character.\n\n let units := add(mod(errorCode, 10), 0x30)\n\n errorCode := div(errorCode, 10)\n let tenths := add(mod(errorCode, 10), 0x30)\n\n errorCode := div(errorCode, 10)\n let hundreds := add(mod(errorCode, 10), 0x30)\n\n // With the individual characters, we can now construct the full string.\n // We first append the '#' character (0x23) to the prefix. In the case of 'BAL', it results in 0x42414c23 ('BAL#')\n // Then, we shift this by 24 (to provide space for the 3 bytes of the error code), and add the\n // characters to it, each shifted by a multiple of 8.\n // The revert reason is then shifted left by 200 bits (256 minus the length of the string, 7 characters * 8 bits\n // per character = 56) to locate it in the most significant part of the 256 slot (the beginning of a byte\n // array).\n let formattedPrefix := shl(24, add(0x23, shl(8, prefixUint)))\n\n let revertReason := shl(\n 200,\n add(\n formattedPrefix,\n add(add(units, shl(8, tenths)), shl(16, hundreds))\n )\n )\n\n // We can now encode the reason in memory, which can be safely overwritten as we're about to revert. The encoded\n // message will have the following layout:\n // [ revert reason identifier ] [ string location offset ] [ string length ] [ string contents ]\n\n // The Solidity revert reason identifier is 0x08c739a0, the function selector of the Error(string) function. We\n // also write zeroes to the next 28 bytes of memory, but those are about to be overwritten.\n mstore(\n 0x0,\n 0x08c379a000000000000000000000000000000000000000000000000000000000\n )\n // Next is the offset to the location of the string, which will be placed immediately after (20 bytes away).\n mstore(\n 0x04,\n 0x0000000000000000000000000000000000000000000000000000000000000020\n )\n // The string length is fixed: 7 characters.\n mstore(0x24, 7)\n // Finally, the string itself is stored.\n mstore(0x44, revertReason)\n\n // Even if the string is only 7 bytes long, we need to return a full 32 byte slot containing it. The length of\n // the encoded message is therefore 4 + 32 + 32 + 32 = 100.\n revert(0, 100)\n }\n}\n\nlibrary Errors {\n // Math\n uint256 internal constant ADD_OVERFLOW = 0;\n uint256 internal constant SUB_OVERFLOW = 1;\n uint256 internal constant SUB_UNDERFLOW = 2;\n uint256 internal constant MUL_OVERFLOW = 3;\n uint256 internal constant ZERO_DIVISION = 4;\n uint256 internal constant DIV_INTERNAL = 5;\n uint256 internal constant X_OUT_OF_BOUNDS = 6;\n uint256 internal constant Y_OUT_OF_BOUNDS = 7;\n uint256 internal constant PRODUCT_OUT_OF_BOUNDS = 8;\n uint256 internal constant INVALID_EXPONENT = 9;\n\n // Input\n uint256 internal constant OUT_OF_BOUNDS = 100;\n uint256 internal constant UNSORTED_ARRAY = 101;\n uint256 internal constant UNSORTED_TOKENS = 102;\n uint256 internal constant INPUT_LENGTH_MISMATCH = 103;\n uint256 internal constant ZERO_TOKEN = 104;\n uint256 internal constant INSUFFICIENT_DATA = 105;\n\n // Shared pools\n uint256 internal constant MIN_TOKENS = 200;\n uint256 internal constant MAX_TOKENS = 201;\n uint256 internal constant MAX_SWAP_FEE_PERCENTAGE = 202;\n uint256 internal constant MIN_SWAP_FEE_PERCENTAGE = 203;\n uint256 internal constant MINIMUM_BPT = 204;\n uint256 internal constant CALLER_NOT_VAULT = 205;\n uint256 internal constant UNINITIALIZED = 206;\n uint256 internal constant BPT_IN_MAX_AMOUNT = 207;\n uint256 internal constant BPT_OUT_MIN_AMOUNT = 208;\n uint256 internal constant EXPIRED_PERMIT = 209;\n uint256 internal constant NOT_TWO_TOKENS = 210;\n uint256 internal constant DISABLED = 211;\n\n // Pools\n uint256 internal constant MIN_AMP = 300;\n uint256 internal constant MAX_AMP = 301;\n uint256 internal constant MIN_WEIGHT = 302;\n uint256 internal constant MAX_STABLE_TOKENS = 303;\n uint256 internal constant MAX_IN_RATIO = 304;\n uint256 internal constant MAX_OUT_RATIO = 305;\n uint256 internal constant MIN_BPT_IN_FOR_TOKEN_OUT = 306;\n uint256 internal constant MAX_OUT_BPT_FOR_TOKEN_IN = 307;\n uint256 internal constant NORMALIZED_WEIGHT_INVARIANT = 308;\n uint256 internal constant INVALID_TOKEN = 309;\n uint256 internal constant UNHANDLED_JOIN_KIND = 310;\n uint256 internal constant ZERO_INVARIANT = 311;\n uint256 internal constant ORACLE_INVALID_SECONDS_QUERY = 312;\n uint256 internal constant ORACLE_NOT_INITIALIZED = 313;\n uint256 internal constant ORACLE_QUERY_TOO_OLD = 314;\n uint256 internal constant ORACLE_INVALID_INDEX = 315;\n uint256 internal constant ORACLE_BAD_SECS = 316;\n uint256 internal constant AMP_END_TIME_TOO_CLOSE = 317;\n uint256 internal constant AMP_ONGOING_UPDATE = 318;\n uint256 internal constant AMP_RATE_TOO_HIGH = 319;\n uint256 internal constant AMP_NO_ONGOING_UPDATE = 320;\n uint256 internal constant STABLE_INVARIANT_DIDNT_CONVERGE = 321;\n uint256 internal constant STABLE_GET_BALANCE_DIDNT_CONVERGE = 322;\n uint256 internal constant RELAYER_NOT_CONTRACT = 323;\n uint256 internal constant BASE_POOL_RELAYER_NOT_CALLED = 324;\n uint256 internal constant REBALANCING_RELAYER_REENTERED = 325;\n uint256 internal constant GRADUAL_UPDATE_TIME_TRAVEL = 326;\n uint256 internal constant SWAPS_DISABLED = 327;\n uint256 internal constant CALLER_IS_NOT_LBP_OWNER = 328;\n uint256 internal constant PRICE_RATE_OVERFLOW = 329;\n uint256 internal constant INVALID_JOIN_EXIT_KIND_WHILE_SWAPS_DISABLED = 330;\n uint256 internal constant WEIGHT_CHANGE_TOO_FAST = 331;\n uint256 internal constant LOWER_GREATER_THAN_UPPER_TARGET = 332;\n uint256 internal constant UPPER_TARGET_TOO_HIGH = 333;\n uint256 internal constant UNHANDLED_BY_LINEAR_POOL = 334;\n uint256 internal constant OUT_OF_TARGET_RANGE = 335;\n uint256 internal constant UNHANDLED_EXIT_KIND = 336;\n uint256 internal constant UNAUTHORIZED_EXIT = 337;\n uint256 internal constant MAX_MANAGEMENT_SWAP_FEE_PERCENTAGE = 338;\n uint256 internal constant UNHANDLED_BY_MANAGED_POOL = 339;\n uint256 internal constant UNHANDLED_BY_PHANTOM_POOL = 340;\n uint256 internal constant TOKEN_DOES_NOT_HAVE_RATE_PROVIDER = 341;\n uint256 internal constant INVALID_INITIALIZATION = 342;\n uint256 internal constant OUT_OF_NEW_TARGET_RANGE = 343;\n uint256 internal constant FEATURE_DISABLED = 344;\n uint256 internal constant UNINITIALIZED_POOL_CONTROLLER = 345;\n uint256 internal constant SET_SWAP_FEE_DURING_FEE_CHANGE = 346;\n uint256 internal constant SET_SWAP_FEE_PENDING_FEE_CHANGE = 347;\n uint256 internal constant CHANGE_TOKENS_DURING_WEIGHT_CHANGE = 348;\n uint256 internal constant CHANGE_TOKENS_PENDING_WEIGHT_CHANGE = 349;\n uint256 internal constant MAX_WEIGHT = 350;\n uint256 internal constant UNAUTHORIZED_JOIN = 351;\n uint256 internal constant MAX_MANAGEMENT_AUM_FEE_PERCENTAGE = 352;\n uint256 internal constant FRACTIONAL_TARGET = 353;\n uint256 internal constant ADD_OR_REMOVE_BPT = 354;\n uint256 internal constant INVALID_CIRCUIT_BREAKER_BOUNDS = 355;\n uint256 internal constant CIRCUIT_BREAKER_TRIPPED = 356;\n uint256 internal constant MALICIOUS_QUERY_REVERT = 357;\n uint256 internal constant JOINS_EXITS_DISABLED = 358;\n\n // Lib\n uint256 internal constant REENTRANCY = 400;\n uint256 internal constant SENDER_NOT_ALLOWED = 401;\n uint256 internal constant PAUSED = 402;\n uint256 internal constant PAUSE_WINDOW_EXPIRED = 403;\n uint256 internal constant MAX_PAUSE_WINDOW_DURATION = 404;\n uint256 internal constant MAX_BUFFER_PERIOD_DURATION = 405;\n uint256 internal constant INSUFFICIENT_BALANCE = 406;\n uint256 internal constant INSUFFICIENT_ALLOWANCE = 407;\n uint256 internal constant ERC20_TRANSFER_FROM_ZERO_ADDRESS = 408;\n uint256 internal constant ERC20_TRANSFER_TO_ZERO_ADDRESS = 409;\n uint256 internal constant ERC20_MINT_TO_ZERO_ADDRESS = 410;\n uint256 internal constant ERC20_BURN_FROM_ZERO_ADDRESS = 411;\n uint256 internal constant ERC20_APPROVE_FROM_ZERO_ADDRESS = 412;\n uint256 internal constant ERC20_APPROVE_TO_ZERO_ADDRESS = 413;\n uint256 internal constant ERC20_TRANSFER_EXCEEDS_ALLOWANCE = 414;\n uint256 internal constant ERC20_DECREASED_ALLOWANCE_BELOW_ZERO = 415;\n uint256 internal constant ERC20_TRANSFER_EXCEEDS_BALANCE = 416;\n uint256 internal constant ERC20_BURN_EXCEEDS_ALLOWANCE = 417;\n uint256 internal constant SAFE_ERC20_CALL_FAILED = 418;\n uint256 internal constant ADDRESS_INSUFFICIENT_BALANCE = 419;\n uint256 internal constant ADDRESS_CANNOT_SEND_VALUE = 420;\n uint256 internal constant SAFE_CAST_VALUE_CANT_FIT_INT256 = 421;\n uint256 internal constant GRANT_SENDER_NOT_ADMIN = 422;\n uint256 internal constant REVOKE_SENDER_NOT_ADMIN = 423;\n uint256 internal constant RENOUNCE_SENDER_NOT_ALLOWED = 424;\n uint256 internal constant BUFFER_PERIOD_EXPIRED = 425;\n uint256 internal constant CALLER_IS_NOT_OWNER = 426;\n uint256 internal constant NEW_OWNER_IS_ZERO = 427;\n uint256 internal constant CODE_DEPLOYMENT_FAILED = 428;\n uint256 internal constant CALL_TO_NON_CONTRACT = 429;\n uint256 internal constant LOW_LEVEL_CALL_FAILED = 430;\n uint256 internal constant NOT_PAUSED = 431;\n uint256 internal constant ADDRESS_ALREADY_ALLOWLISTED = 432;\n uint256 internal constant ADDRESS_NOT_ALLOWLISTED = 433;\n uint256 internal constant ERC20_BURN_EXCEEDS_BALANCE = 434;\n uint256 internal constant INVALID_OPERATION = 435;\n uint256 internal constant CODEC_OVERFLOW = 436;\n uint256 internal constant IN_RECOVERY_MODE = 437;\n uint256 internal constant NOT_IN_RECOVERY_MODE = 438;\n uint256 internal constant INDUCED_FAILURE = 439;\n uint256 internal constant EXPIRED_SIGNATURE = 440;\n uint256 internal constant MALFORMED_SIGNATURE = 441;\n uint256 internal constant SAFE_CAST_VALUE_CANT_FIT_UINT64 = 442;\n uint256 internal constant UNHANDLED_FEE_TYPE = 443;\n uint256 internal constant BURN_FROM_ZERO = 444;\n\n // Vault\n uint256 internal constant INVALID_POOL_ID = 500;\n uint256 internal constant CALLER_NOT_POOL = 501;\n uint256 internal constant SENDER_NOT_ASSET_MANAGER = 502;\n uint256 internal constant USER_DOESNT_ALLOW_RELAYER = 503;\n uint256 internal constant INVALID_SIGNATURE = 504;\n uint256 internal constant EXIT_BELOW_MIN = 505;\n uint256 internal constant JOIN_ABOVE_MAX = 506;\n uint256 internal constant SWAP_LIMIT = 507;\n uint256 internal constant SWAP_DEADLINE = 508;\n uint256 internal constant CANNOT_SWAP_SAME_TOKEN = 509;\n uint256 internal constant UNKNOWN_AMOUNT_IN_FIRST_SWAP = 510;\n uint256 internal constant MALCONSTRUCTED_MULTIHOP_SWAP = 511;\n uint256 internal constant INTERNAL_BALANCE_OVERFLOW = 512;\n uint256 internal constant INSUFFICIENT_INTERNAL_BALANCE = 513;\n uint256 internal constant INVALID_ETH_INTERNAL_BALANCE = 514;\n uint256 internal constant INVALID_POST_LOAN_BALANCE = 515;\n uint256 internal constant INSUFFICIENT_ETH = 516;\n uint256 internal constant UNALLOCATED_ETH = 517;\n uint256 internal constant ETH_TRANSFER = 518;\n uint256 internal constant CANNOT_USE_ETH_SENTINEL = 519;\n uint256 internal constant TOKENS_MISMATCH = 520;\n uint256 internal constant TOKEN_NOT_REGISTERED = 521;\n uint256 internal constant TOKEN_ALREADY_REGISTERED = 522;\n uint256 internal constant TOKENS_ALREADY_SET = 523;\n uint256 internal constant TOKENS_LENGTH_MUST_BE_2 = 524;\n uint256 internal constant NONZERO_TOKEN_BALANCE = 525;\n uint256 internal constant BALANCE_TOTAL_OVERFLOW = 526;\n uint256 internal constant POOL_NO_TOKENS = 527;\n uint256 internal constant INSUFFICIENT_FLASH_LOAN_BALANCE = 528;\n\n // Fees\n uint256 internal constant SWAP_FEE_PERCENTAGE_TOO_HIGH = 600;\n uint256 internal constant FLASH_LOAN_FEE_PERCENTAGE_TOO_HIGH = 601;\n uint256 internal constant INSUFFICIENT_FLASH_LOAN_FEE_AMOUNT = 602;\n uint256 internal constant AUM_FEE_PERCENTAGE_TOO_HIGH = 603;\n\n // FeeSplitter\n uint256 internal constant SPLITTER_FEE_PERCENTAGE_TOO_HIGH = 700;\n\n // Misc\n uint256 internal constant UNIMPLEMENTED = 998;\n uint256 internal constant SHOULD_NOT_HAPPEN = 999;\n}\n" + }, + "contracts/utils/DepositContractUtils.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\ncontract DepositContractUtils {\n function calculateDepositDataRoot(\n bytes calldata pubkey,\n bytes calldata withdrawal_credentials,\n bytes calldata signature\n ) public pure returns (bytes32 node) {\n uint256 deposit_amount = 32 ether / 1 gwei;\n bytes memory amount = to_little_endian_64(uint64(deposit_amount));\n\n // Compute deposit data root (`DepositData` hash tree root)\n bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, bytes16(0)));\n bytes32 signature_root = sha256(\n abi.encodePacked(\n sha256(abi.encodePacked(signature[:64])),\n sha256(abi.encodePacked(signature[64:], bytes32(0)))\n )\n );\n node = sha256(\n abi.encodePacked(\n sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)),\n sha256(abi.encodePacked(amount, bytes24(0), signature_root))\n )\n );\n }\n\n function to_little_endian_64(uint64 value)\n internal\n pure\n returns (bytes memory ret)\n {\n ret = new bytes(8);\n bytes8 bytesValue = bytes8(value);\n // Byteswapping during copying to bytes.\n ret[0] = bytesValue[7];\n ret[1] = bytesValue[6];\n ret[2] = bytesValue[5];\n ret[3] = bytesValue[4];\n ret[4] = bytesValue[3];\n ret[5] = bytesValue[2];\n ret[6] = bytesValue[1];\n ret[7] = bytesValue[0];\n }\n}\n" + }, + "contracts/utils/Helpers.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IBasicToken } from \"../interfaces/IBasicToken.sol\";\n\nlibrary Helpers {\n /**\n * @notice Fetch the `symbol()` from an ERC20 token\n * @dev Grabs the `symbol()` from a contract\n * @param _token Address of the ERC20 token\n * @return string Symbol of the ERC20 token\n */\n function getSymbol(address _token) internal view returns (string memory) {\n string memory symbol = IBasicToken(_token).symbol();\n return symbol;\n }\n\n /**\n * @notice Fetch the `decimals()` from an ERC20 token\n * @dev Grabs the `decimals()` from a contract and fails if\n * the decimal value does not live within a certain range\n * @param _token Address of the ERC20 token\n * @return uint256 Decimals of the ERC20 token\n */\n function getDecimals(address _token) internal view returns (uint256) {\n uint256 decimals = IBasicToken(_token).decimals();\n require(\n decimals >= 4 && decimals <= 18,\n \"Token must have sufficient decimal places\"\n );\n\n return decimals;\n }\n}\n" + }, + "contracts/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract any contracts that need to initialize state after deployment.\n * @author Origin Protocol Inc\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(\n initializing || !initialized,\n \"Initializable: contract is already initialized\"\n );\n\n bool isTopLevelCall = !initializing;\n if (isTopLevelCall) {\n initializing = true;\n initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n initializing = false;\n }\n }\n\n uint256[50] private ______gap;\n}\n" + }, + "contracts/utils/InitializableAbstractStrategy.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title Base contract for vault strategies.\n * @author Origin Protocol Inc\n */\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\n\nabstract contract InitializableAbstractStrategy is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event PTokenAdded(address indexed _asset, address _pToken);\n event PTokenRemoved(address indexed _asset, address _pToken);\n event Deposit(address indexed _asset, address _pToken, uint256 _amount);\n event Withdrawal(address indexed _asset, address _pToken, uint256 _amount);\n event RewardTokenCollected(\n address recipient,\n address rewardToken,\n uint256 amount\n );\n event RewardTokenAddressesUpdated(\n address[] _oldAddresses,\n address[] _newAddresses\n );\n event HarvesterAddressesUpdated(\n address _oldHarvesterAddress,\n address _newHarvesterAddress\n );\n\n /// @notice Address of the underlying platform\n address public immutable platformAddress;\n /// @notice Address of the OToken vault\n address public immutable vaultAddress;\n\n /// @dev Replaced with an immutable variable\n // slither-disable-next-line constable-states\n address private _deprecated_platformAddress;\n\n /// @dev Replaced with an immutable\n // slither-disable-next-line constable-states\n address private _deprecated_vaultAddress;\n\n /// @notice asset => pToken (Platform Specific Token Address)\n mapping(address => address) public assetToPToken;\n\n /// @notice Full list of all assets supported by the strategy\n address[] internal assetsMapped;\n\n // Deprecated: Reward token address\n // slither-disable-next-line constable-states\n address private _deprecated_rewardTokenAddress;\n\n // Deprecated: now resides in Harvester's rewardTokenConfigs\n // slither-disable-next-line constable-states\n uint256 private _deprecated_rewardLiquidationThreshold;\n\n /// @notice Address of the Harvester contract allowed to collect reward tokens\n address public harvesterAddress;\n\n /// @notice Address of the reward tokens. eg CRV, BAL, CVX, AURA\n address[] public rewardTokenAddresses;\n\n /* Reserved for future expansion. Used to be 100 storage slots\n * and has decreased to accommodate:\n * - harvesterAddress\n * - rewardTokenAddresses\n */\n int256[98] private _reserved;\n\n struct BaseStrategyConfig {\n address platformAddress; // Address of the underlying platform\n address vaultAddress; // Address of the OToken's Vault\n }\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /**\n * @param _config The platform and OToken vault addresses\n */\n constructor(BaseStrategyConfig memory _config) {\n platformAddress = _config.platformAddress;\n vaultAddress = _config.vaultAddress;\n }\n\n /**\n * @dev Internal initialize function, to set up initial internal state\n * @param _rewardTokenAddresses Address of reward token for platform\n * @param _assets Addresses of initial supported assets\n * @param _pTokens Platform Token corresponding addresses\n */\n function _initialize(\n address[] memory _rewardTokenAddresses,\n address[] memory _assets,\n address[] memory _pTokens\n ) internal {\n rewardTokenAddresses = _rewardTokenAddresses;\n\n uint256 assetCount = _assets.length;\n require(assetCount == _pTokens.length, \"Invalid input arrays\");\n for (uint256 i = 0; i < assetCount; ++i) {\n _setPTokenAddress(_assets[i], _pTokens[i]);\n }\n }\n\n /**\n * @notice Collect accumulated reward token and send to Vault.\n */\n function collectRewardTokens() external virtual onlyHarvester nonReentrant {\n _collectRewardTokens();\n }\n\n /**\n * @dev Default implementation that transfers reward tokens to the Harvester\n * Implementing strategies need to add custom logic to collect the rewards.\n */\n function _collectRewardTokens() internal virtual {\n uint256 rewardTokenCount = rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n IERC20 rewardToken = IERC20(rewardTokenAddresses[i]);\n uint256 balance = rewardToken.balanceOf(address(this));\n if (balance > 0) {\n emit RewardTokenCollected(\n harvesterAddress,\n address(rewardToken),\n balance\n );\n rewardToken.safeTransfer(harvesterAddress, balance);\n }\n }\n }\n\n /**\n * @dev Verifies that the caller is the Vault.\n */\n modifier onlyVault() {\n require(msg.sender == vaultAddress, \"Caller is not the Vault\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Harvester.\n */\n modifier onlyHarvester() {\n require(msg.sender == harvesterAddress, \"Caller is not the Harvester\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault or Governor.\n */\n modifier onlyVaultOrGovernor() {\n require(\n msg.sender == vaultAddress || msg.sender == governor(),\n \"Caller is not the Vault or Governor\"\n );\n _;\n }\n\n /**\n * @dev Verifies that the caller is the Vault, Governor, or Strategist.\n */\n modifier onlyVaultOrGovernorOrStrategist() {\n require(\n msg.sender == vaultAddress ||\n msg.sender == governor() ||\n msg.sender == IVault(vaultAddress).strategistAddr(),\n \"Caller is not the Vault, Governor, or Strategist\"\n );\n _;\n }\n\n /**\n * @notice Set the reward token addresses. Any old addresses will be overwritten.\n * @param _rewardTokenAddresses Array of reward token addresses\n */\n function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses)\n external\n onlyGovernor\n {\n uint256 rewardTokenCount = _rewardTokenAddresses.length;\n for (uint256 i = 0; i < rewardTokenCount; ++i) {\n require(\n _rewardTokenAddresses[i] != address(0),\n \"Can not set an empty address as a reward token\"\n );\n }\n\n emit RewardTokenAddressesUpdated(\n rewardTokenAddresses,\n _rewardTokenAddresses\n );\n rewardTokenAddresses = _rewardTokenAddresses;\n }\n\n /**\n * @notice Get the reward token addresses.\n * @return address[] the reward token addresses.\n */\n function getRewardTokenAddresses()\n external\n view\n returns (address[] memory)\n {\n return rewardTokenAddresses;\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * This method can only be called by the system Governor\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function setPTokenAddress(address _asset, address _pToken)\n external\n virtual\n onlyGovernor\n {\n _setPTokenAddress(_asset, _pToken);\n }\n\n /**\n * @notice Remove a supported asset by passing its index.\n * This method can only be called by the system Governor\n * @param _assetIndex Index of the asset to be removed\n */\n function removePToken(uint256 _assetIndex) external virtual onlyGovernor {\n require(_assetIndex < assetsMapped.length, \"Invalid index\");\n address asset = assetsMapped[_assetIndex];\n address pToken = assetToPToken[asset];\n\n if (_assetIndex < assetsMapped.length - 1) {\n assetsMapped[_assetIndex] = assetsMapped[assetsMapped.length - 1];\n }\n assetsMapped.pop();\n assetToPToken[asset] = address(0);\n\n emit PTokenRemoved(asset, pToken);\n }\n\n /**\n * @notice Provide support for asset by passing its pToken address.\n * Add to internal mappings and execute the platform specific,\n * abstract method `_abstractSetPToken`\n * @param _asset Address for the asset\n * @param _pToken Address for the corresponding platform token\n */\n function _setPTokenAddress(address _asset, address _pToken) internal {\n require(assetToPToken[_asset] == address(0), \"pToken already set\");\n require(\n _asset != address(0) && _pToken != address(0),\n \"Invalid addresses\"\n );\n\n assetToPToken[_asset] = _pToken;\n assetsMapped.push(_asset);\n\n emit PTokenAdded(_asset, _pToken);\n\n _abstractSetPToken(_asset, _pToken);\n }\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * strategy contracts, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n public\n virtual\n onlyGovernor\n {\n require(!supportsAsset(_asset), \"Cannot transfer supported asset\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /**\n * @notice Set the Harvester contract that can collect rewards.\n * @param _harvesterAddress Address of the harvester contract.\n */\n function setHarvesterAddress(address _harvesterAddress)\n external\n onlyGovernor\n {\n emit HarvesterAddressesUpdated(harvesterAddress, _harvesterAddress);\n harvesterAddress = _harvesterAddress;\n }\n\n /***************************************\n Abstract\n ****************************************/\n\n function _abstractSetPToken(address _asset, address _pToken)\n internal\n virtual;\n\n function safeApproveAllTokens() external virtual;\n\n /**\n * @notice Deposit an amount of assets into the platform\n * @param _asset Address for the asset\n * @param _amount Units of asset to deposit\n */\n function deposit(address _asset, uint256 _amount) external virtual;\n\n /**\n * @notice Deposit all supported assets in this strategy contract to the platform\n */\n function depositAll() external virtual;\n\n /**\n * @notice Withdraw an `amount` of assets from the platform and\n * send to the `_recipient`.\n * @param _recipient Address to which the asset should be sent\n * @param _asset Address of the asset\n * @param _amount Units of asset to withdraw\n */\n function withdraw(\n address _recipient,\n address _asset,\n uint256 _amount\n ) external virtual;\n\n /**\n * @notice Withdraw all supported assets from platform and\n * sends to the OToken's Vault.\n */\n function withdrawAll() external virtual;\n\n /**\n * @notice Get the total asset value held in the platform.\n * This includes any interest that was generated since depositing.\n * @param _asset Address of the asset\n * @return balance Total value of the asset in the platform\n */\n function checkBalance(address _asset)\n external\n view\n virtual\n returns (uint256 balance);\n\n /**\n * @notice Check if an asset is supported.\n * @param _asset Address of the asset\n * @return bool Whether asset is supported\n */\n function supportsAsset(address _asset) public view virtual returns (bool);\n}\n" + }, + "contracts/utils/InitializableERC20Detailed.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @dev Optional functions from the ERC20 standard.\n * Converted from openzeppelin/contracts/token/ERC20/ERC20Detailed.sol\n * @author Origin Protocol Inc\n */\nabstract contract InitializableERC20Detailed is IERC20 {\n // Storage gap to skip storage from prior to OUSD reset\n uint256[100] private _____gap;\n\n string private _name;\n string private _symbol;\n uint8 private _decimals;\n\n /**\n * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of\n * these values are immutable: they can only be set once during\n * construction.\n * @notice To avoid variable shadowing appended `Arg` after arguments name.\n */\n function _initialize(\n string memory nameArg,\n string memory symbolArg,\n uint8 decimalsArg\n ) internal {\n _name = nameArg;\n _symbol = symbolArg;\n _decimals = decimalsArg;\n }\n\n /**\n * @notice Returns the name of the token.\n */\n function name() public view returns (string memory) {\n return _name;\n }\n\n /**\n * @notice Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view returns (string memory) {\n return _symbol;\n }\n\n /**\n * @notice Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5,05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei.\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view returns (uint8) {\n return _decimals;\n }\n}\n" + }, + "contracts/utils/PRBMath.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n// Copied from the PRBMath library\n// https://github.com/PaulRBerg/prb-math/blob/main/src/Common.sol\n\n/// @notice Calculates the square root of x using the Babylonian method.\n///\n/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.\n///\n/// Notes:\n/// - If x is not a perfect square, the result is rounded down.\n/// - Credits to OpenZeppelin for the explanations in comments below.\n///\n/// @param x The uint256 number for which to calculate the square root.\n/// @return result The result as a uint256.\n/// @custom:smtchecker abstract-function-nondet\nfunction sqrt(uint256 x) pure returns (uint256 result) {\n if (x == 0) {\n return 0;\n }\n\n // For our first guess, we calculate the biggest power of 2 which is smaller than the square root of x.\n //\n // We know that the \"msb\" (most significant bit) of x is a power of 2 such that we have:\n //\n // $$\n // msb(x) <= x <= 2*msb(x)$\n // $$\n //\n // We write $msb(x)$ as $2^k$, and we get:\n //\n // $$\n // k = log_2(x)\n // $$\n //\n // Thus, we can write the initial inequality as:\n //\n // $$\n // 2^{log_2(x)} <= x <= 2*2^{log_2(x)+1} \\\\\n // sqrt(2^k) <= sqrt(x) < sqrt(2^{k+1}) \\\\\n // 2^{k/2} <= sqrt(x) < 2^{(k+1)/2} <= 2^{(k/2)+1}\n // $$\n //\n // Consequently, $2^{log_2(x) /2} is a good first approximation of sqrt(x) with at least one correct bit.\n uint256 xAux = uint256(x);\n result = 1;\n if (xAux >= 2**128) {\n xAux >>= 128;\n result <<= 64;\n }\n if (xAux >= 2**64) {\n xAux >>= 64;\n result <<= 32;\n }\n if (xAux >= 2**32) {\n xAux >>= 32;\n result <<= 16;\n }\n if (xAux >= 2**16) {\n xAux >>= 16;\n result <<= 8;\n }\n if (xAux >= 2**8) {\n xAux >>= 8;\n result <<= 4;\n }\n if (xAux >= 2**4) {\n xAux >>= 4;\n result <<= 2;\n }\n if (xAux >= 2**2) {\n result <<= 1;\n }\n\n // At this point, `result` is an estimation with at least one bit of precision. We know the true value has at\n // most 128 bits, since it is the square root of a uint256. Newton's method converges quadratically (precision\n // doubles at every iteration). We thus need at most 7 iteration to turn our partial result with one bit of\n // precision into the expected uint128 result.\n unchecked {\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n result = (result + x / result) >> 1;\n\n // If x is not a perfect square, round the result toward zero.\n uint256 roundedResult = x / result;\n if (result >= roundedResult) {\n result = roundedResult;\n }\n }\n}\n" + }, + "contracts/utils/StableMath.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeMath } from \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\n// Based on StableMath from Stability Labs Pty. Ltd.\n// https://github.com/mstable/mStable-contracts/blob/master/contracts/shared/StableMath.sol\n\nlibrary StableMath {\n using SafeMath for uint256;\n\n /**\n * @dev Scaling unit for use in specific calculations,\n * where 1 * 10**18, or 1e18 represents a unit '1'\n */\n uint256 private constant FULL_SCALE = 1e18;\n\n /***************************************\n Helpers\n ****************************************/\n\n /**\n * @dev Adjust the scale of an integer\n * @param to Decimals to scale to\n * @param from Decimals to scale from\n */\n function scaleBy(\n uint256 x,\n uint256 to,\n uint256 from\n ) internal pure returns (uint256) {\n if (to > from) {\n x = x.mul(10**(to - from));\n } else if (to < from) {\n // slither-disable-next-line divide-before-multiply\n x = x.div(10**(from - to));\n }\n return x;\n }\n\n /***************************************\n Precise Arithmetic\n ****************************************/\n\n /**\n * @dev Multiplies two precise units, and then truncates by the full scale\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit\n */\n function mulTruncate(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulTruncateScale(x, y, FULL_SCALE);\n }\n\n /**\n * @dev Multiplies two precise units, and then truncates by the given scale. For example,\n * when calculating 90% of 10e18, (10e18 * 9e17) / 1e18 = (9e36) / 1e18 = 9e18\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @param scale Scale unit\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit\n */\n function mulTruncateScale(\n uint256 x,\n uint256 y,\n uint256 scale\n ) internal pure returns (uint256) {\n // e.g. assume scale = fullScale\n // z = 10e18 * 9e17 = 9e36\n uint256 z = x.mul(y);\n // return 9e36 / 1e18 = 9e18\n return z.div(scale);\n }\n\n /**\n * @dev Multiplies two precise units, and then truncates by the full scale, rounding up the result\n * @param x Left hand input to multiplication\n * @param y Right hand input to multiplication\n * @return Result after multiplying the two inputs and then dividing by the shared\n * scale unit, rounded up to the closest base unit.\n */\n function mulTruncateCeil(uint256 x, uint256 y)\n internal\n pure\n returns (uint256)\n {\n // e.g. 8e17 * 17268172638 = 138145381104e17\n uint256 scaled = x.mul(y);\n // e.g. 138145381104e17 + 9.99...e17 = 138145381113.99...e17\n uint256 ceil = scaled.add(FULL_SCALE.sub(1));\n // e.g. 13814538111.399...e18 / 1e18 = 13814538111\n return ceil.div(FULL_SCALE);\n }\n\n /**\n * @dev Precisely divides two units, by first scaling the left hand operand. Useful\n * for finding percentage weightings, i.e. 8e18/10e18 = 80% (or 8e17)\n * @param x Left hand input to division\n * @param y Right hand input to division\n * @return Result after multiplying the left operand by the scale, and\n * executing the division on the right hand input.\n */\n function divPrecisely(uint256 x, uint256 y)\n internal\n pure\n returns (uint256)\n {\n // e.g. 8e18 * 1e18 = 8e36\n uint256 z = x.mul(FULL_SCALE);\n // e.g. 8e36 / 10e18 = 8e17\n return z.div(y);\n }\n}\n" + }, + "contracts/vault/OETHBaseVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"./OETHVaultAdmin.sol\";\n\n/**\n * @title OETH Base VaultAdmin Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBaseVaultAdmin is OETHVaultAdmin {\n constructor(address _weth) OETHVaultAdmin(_weth) {}\n}\n" + }, + "contracts/vault/OETHBaseVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { OETHVaultCore } from \"./OETHVaultCore.sol\";\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\n\n/**\n * @title OETH Base VaultCore Contract\n * @author Origin Protocol Inc\n */\ncontract OETHBaseVaultCore is OETHVaultCore {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n constructor(address _weth) OETHVaultCore(_weth) {}\n\n // @inheritdoc OETHVaultCore\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n override\n {\n // Only Strategist or Governor can redeem using the Vault for now.\n // We don't have the onlyGovernorOrStrategist modifier on VaultCore.\n // Since we won't be using that modifier anywhere in the VaultCore as well,\n // the check has been added inline instead of moving it to VaultStorage.\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n\n super._redeem(_amount, _minimumUnitAmount);\n }\n}\n" + }, + "contracts/vault/OETHBaseZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\n\ncontract OETHBaseZapper {\n IERC20 public immutable oethb;\n IERC4626 public immutable woethb;\n IVault public immutable vault;\n\n IWETH9 public constant weth =\n IWETH9(0x4200000000000000000000000000000000000006);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(\n address _oethb,\n address _woethb,\n address _vault\n ) {\n oethb = IERC20(_oethb);\n woethb = IERC4626(_woethb);\n vault = IVault(_vault);\n\n weth.approve(address(_vault), type(uint256).max);\n IERC20(_oethb).approve(_woethb, type(uint256).max);\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return.\n * Will verify that the user is sent 1:1 for ETH.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit ETH and receive superOETHb in return\n * Will verify that the user is sent 1:1 for ETH.\n * @return Amount of OETH sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap ETH\n weth.deposit{ value: balance }();\n\n // Mint with WETH\n return _mint(balance, msg.sender);\n }\n\n /**\n * @dev Deposit ETH and receive superOETHb in return\n * @param minReceived min amount of wsuperOETHb to receive\n * @return Amount of wsuperOETHb sent to user\n */\n function depositETHForWrappedTokens(uint256 minReceived)\n external\n payable\n returns (uint256)\n {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap ETH\n weth.deposit{ value: balance }();\n\n // Mint with WETH\n uint256 mintedOethb = _mint(balance, address(this));\n\n // Wrap superOETHb into wsuperOETHb\n uint256 mintedWoethb = woethb.deposit(mintedOethb, msg.sender);\n\n require(mintedWoethb >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWoethb;\n }\n\n /**\n * @dev Deposit WETH and receive superOETHb in return\n * @param wethAmount Amount of WETH to deposit\n * @param minReceived min amount of wsuperOETHb to receive\n * @return Amount of wsuperOETHb sent to user\n */\n function depositWETHForWrappedTokens(\n uint256 wethAmount,\n uint256 minReceived\n ) external returns (uint256) {\n // slither-disable-next-line unchecked-transfer unused-return\n weth.transferFrom(msg.sender, address(this), wethAmount);\n\n emit Zap(msg.sender, address(weth), wethAmount);\n\n // Mint with WETH\n uint256 mintedOethb = _mint(wethAmount, address(this));\n\n // Wrap superOETHb into wsuperOETHb\n uint256 mintedWoethb = woethb.deposit(mintedOethb, msg.sender);\n\n require(mintedWoethb >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWoethb;\n }\n\n /**\n * @dev Internal function to mint superOETHb with WETH\n * @param minOETH Minimum amount of OETH to for user to receive\n * @param recipient Address that receives the tokens\n * @return Amount of OETH sent to user\n */\n function _mint(uint256 minOETH, address recipient)\n internal\n returns (uint256)\n {\n uint256 toMint = weth.balanceOf(address(this));\n vault.mint(address(weth), toMint, minOETH);\n uint256 mintedAmount = oethb.balanceOf(address(this));\n require(mintedAmount >= minOETH, \"Zapper: not enough minted\");\n\n if (recipient != address(this)) {\n require(oethb.transfer(recipient, mintedAmount));\n }\n\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/OETHVault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { Vault } from \"./Vault.sol\";\n\n/**\n * @title OETH Vault Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVault is Vault {\n\n}\n" + }, + "contracts/vault/OETHVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { VaultAdmin } from \"./VaultAdmin.sol\";\n\n/**\n * @title OETH VaultAdmin Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVaultAdmin is VaultAdmin {\n using SafeERC20 for IERC20;\n\n address public immutable weth;\n\n constructor(address _weth) {\n weth = _weth;\n }\n\n /**\n * @notice Adds a strategy to the mint whitelist.\n * Reverts if strategy isn't approved on Vault.\n * @param strategyAddr Strategy address\n */\n function addStrategyToMintWhitelist(address strategyAddr)\n external\n onlyGovernor\n {\n require(strategies[strategyAddr].isSupported, \"Strategy not approved\");\n\n require(\n !isMintWhitelistedStrategy[strategyAddr],\n \"Already whitelisted\"\n );\n\n isMintWhitelistedStrategy[strategyAddr] = true;\n\n emit StrategyAddedToMintWhitelist(strategyAddr);\n }\n\n /**\n * @notice Removes a strategy from the mint whitelist.\n * @param strategyAddr Strategy address\n */\n function removeStrategyFromMintWhitelist(address strategyAddr)\n external\n onlyGovernor\n {\n // Intentionally skipping `strategies.isSupported` check since\n // we may wanna remove an address even after removing the strategy\n\n require(isMintWhitelistedStrategy[strategyAddr], \"Not whitelisted\");\n\n isMintWhitelistedStrategy[strategyAddr] = false;\n\n emit StrategyRemovedFromMintWhitelist(strategyAddr);\n }\n\n /// @dev Simplified version of the deposit function as WETH is the only supported asset.\n function _depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal override {\n require(\n strategies[_strategyToAddress].isSupported,\n \"Invalid to Strategy\"\n );\n require(\n _assets.length == 1 && _amounts.length == 1 && _assets[0] == weth,\n \"Only WETH is supported\"\n );\n\n // Check the there is enough WETH to transfer once the WETH reserved for the withdrawal queue is accounted for\n require(_amounts[0] <= _wethAvailable(), \"Not enough WETH available\");\n\n // Send required amount of funds to the strategy\n IERC20(weth).safeTransfer(_strategyToAddress, _amounts[0]);\n\n // Deposit all the funds that have been sent to the strategy\n IStrategy(_strategyToAddress).depositAll();\n }\n\n function _withdrawFromStrategy(\n address _recipient,\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal override {\n super._withdrawFromStrategy(\n _recipient,\n _strategyFromAddress,\n _assets,\n _amounts\n );\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n function _withdrawAllFromStrategy(address _strategyAddr) internal override {\n super._withdrawAllFromStrategy(_strategyAddr);\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n function _withdrawAllFromStrategies() internal override {\n super._withdrawAllFromStrategies();\n\n IVault(address(this)).addWithdrawalQueueLiquidity();\n }\n\n /// @dev Calculate how much WETH in the vault is not reserved for the withdrawal queue.\n // That is, it is available to be redeemed or deposited into a strategy.\n function _wethAvailable() internal view returns (uint256 wethAvailable) {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n uint256 outstandingWithdrawals = queue.queued - queue.claimed;\n\n // The amount of sitting in WETH in the vault\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // If there is not enough WETH in the vault to cover the outstanding withdrawals\n if (wethBalance <= outstandingWithdrawals) {\n return 0;\n }\n\n return wethBalance - outstandingWithdrawals;\n }\n\n function _swapCollateral(\n address,\n address,\n uint256,\n uint256,\n bytes calldata\n ) internal pure override returns (uint256) {\n revert(\"Collateral swap not supported\");\n }\n}\n" + }, + "contracts/vault/OETHVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { VaultCore } from \"./VaultCore.sol\";\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\n\n/**\n * @title OETH VaultCore Contract\n * @author Origin Protocol Inc\n */\ncontract OETHVaultCore is VaultCore {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n\n address public immutable weth;\n uint256 public wethAssetIndex;\n\n // For future use (because OETHBaseVaultCore inherits from this)\n uint256[50] private __gap;\n\n constructor(address _weth) {\n weth = _weth;\n }\n\n /**\n * @dev Caches WETH's index in `allAssets` variable.\n * Reduces gas usage by redeem by caching that.\n */\n function cacheWETHAssetIndex() external onlyGovernor {\n uint256 assetCount = allAssets.length;\n for (uint256 i; i < assetCount; ++i) {\n if (allAssets[i] == weth) {\n wethAssetIndex = i;\n break;\n }\n }\n\n require(allAssets[wethAssetIndex] == weth, \"Invalid WETH Asset Index\");\n }\n\n // @inheritdoc VaultCore\n function mintForStrategy(uint256 amount)\n external\n override\n whenNotCapitalPaused\n {\n require(\n strategies[msg.sender].isSupported == true,\n \"Unsupported strategy\"\n );\n require(\n isMintWhitelistedStrategy[msg.sender] == true,\n \"Not whitelisted strategy\"\n );\n\n emit Mint(msg.sender, amount);\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, amount);\n }\n\n // @inheritdoc VaultCore\n function burnForStrategy(uint256 amount)\n external\n override\n whenNotCapitalPaused\n {\n require(\n strategies[msg.sender].isSupported == true,\n \"Unsupported strategy\"\n );\n require(\n isMintWhitelistedStrategy[msg.sender] == true,\n \"Not whitelisted strategy\"\n );\n\n emit Redeem(msg.sender, amount);\n\n // Burn OTokens\n oUSD.burn(msg.sender, amount);\n }\n\n // @inheritdoc VaultCore\n // slither-disable-start reentrancy-no-eth\n function _mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) internal virtual override {\n require(_asset == weth, \"Unsupported asset for minting\");\n require(_amount > 0, \"Amount must be greater than 0\");\n require(\n _amount >= _minimumOusdAmount,\n \"Mint amount lower than minimum\"\n );\n\n emit Mint(msg.sender, _amount);\n\n // Rebase must happen before any transfers occur.\n if (!rebasePaused && _amount >= rebaseThreshold) {\n _rebase();\n }\n\n // Mint oTokens\n oUSD.mint(msg.sender, _amount);\n\n // Transfer the deposited coins to the vault\n IERC20(_asset).safeTransferFrom(msg.sender, address(this), _amount);\n\n // Give priority to the withdrawal queue for the new WETH liquidity\n _addWithdrawalQueueLiquidity();\n\n // Auto-allocate if necessary\n if (_amount >= autoAllocateThreshold) {\n _allocate();\n }\n }\n\n // slither-disable-end reentrancy-no-eth\n\n // @inheritdoc VaultCore\n function _calculateRedeemOutputs(uint256 _amount)\n internal\n view\n virtual\n override\n returns (uint256[] memory outputs)\n {\n // Overrides `VaultCore._calculateRedeemOutputs` to redeem with only\n // WETH instead of LST-mix. Doesn't change the function signature\n // for backward compatibility\n\n // Calculate redeem fee\n if (redeemFeeBps > 0) {\n uint256 redeemFee = _amount.mulTruncateScale(redeemFeeBps, 1e4);\n _amount = _amount - redeemFee;\n }\n\n // Ensure that the WETH index is cached\n uint256 _wethAssetIndex = wethAssetIndex;\n require(\n allAssets[_wethAssetIndex] == weth,\n \"WETH Asset index not cached\"\n );\n\n outputs = new uint256[](allAssets.length);\n outputs[_wethAssetIndex] = _amount;\n }\n\n // @inheritdoc VaultCore\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n override\n {\n // Override `VaultCore._redeem` to simplify it. Gets rid of oracle\n // usage and looping through all assets for LST-mix redeem. Instead\n // does a simple WETH-only redeem.\n emit Redeem(msg.sender, _amount);\n\n if (_amount == 0) {\n return;\n }\n\n // Amount excluding fees\n // No fee for the strategist or the governor, makes it easier to do operations\n uint256 amountMinusFee = (msg.sender == strategistAddr || isGovernor())\n ? _amount\n : _calculateRedeemOutputs(_amount)[wethAssetIndex];\n\n require(\n amountMinusFee >= _minimumUnitAmount,\n \"Redeem amount lower than minimum\"\n );\n\n // Is there enough WETH in the Vault available after accounting for the withdrawal queue\n require(_wethAvailable() >= amountMinusFee, \"Liquidity error\");\n\n // Transfer WETH minus the fee to the redeemer\n IERC20(weth).safeTransfer(msg.sender, amountMinusFee);\n\n // Burn OETH from user (including fees)\n oUSD.burn(msg.sender, _amount);\n\n // Prevent insolvency\n _postRedeem(_amount);\n }\n\n /**\n * @notice Request an asynchronous withdrawal of WETH in exchange for OETH.\n * The OETH is burned on request and the WETH is transferred to the withdrawer on claim.\n * This request can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal this request's `queued` amount.\n * There is a minimum of 10 minutes before a request can be claimed. After that, the request just needs\n * enough WETH liquidity in the Vault to satisfy all the outstanding requests to that point in the queue.\n * OETH is converted to WETH at 1:1.\n * @param _amount Amount of OETH to burn.\n * @return requestId Unique ID for the withdrawal request\n * @return queued Cumulative total of all WETH queued including already claimed requests.\n */\n function requestWithdrawal(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256 requestId, uint256 queued)\n {\n require(withdrawalClaimDelay > 0, \"Async withdrawals not enabled\");\n\n // The check that the requester has enough OETH is done in to later burn call\n\n requestId = withdrawalQueueMetadata.nextWithdrawalIndex;\n queued = withdrawalQueueMetadata.queued + _amount;\n\n // Store the next withdrawal request\n withdrawalQueueMetadata.nextWithdrawalIndex = SafeCast.toUint128(\n requestId + 1\n );\n // Store the updated queued amount which reserves WETH in the withdrawal queue\n // and reduces the vault's total assets\n withdrawalQueueMetadata.queued = SafeCast.toUint128(queued);\n // Store the user's withdrawal request\n withdrawalRequests[requestId] = WithdrawalRequest({\n withdrawer: msg.sender,\n claimed: false,\n timestamp: uint40(block.timestamp),\n amount: SafeCast.toUint128(_amount),\n queued: SafeCast.toUint128(queued)\n });\n\n // Burn the user's OETH\n oUSD.burn(msg.sender, _amount);\n\n // Prevent withdrawal if the vault is solvent by more than the allowed percentage\n _postRedeem(_amount);\n\n emit WithdrawalRequested(msg.sender, requestId, _amount, queued);\n }\n\n // slither-disable-start reentrancy-no-eth\n /**\n * @notice Claim a previously requested withdrawal once it is claimable.\n * This request can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal this request's `queued` amount and 10 minutes has passed.\n * If the requests is not claimable, the transaction will revert with `Queue pending liquidity`.\n * If the request is not older than 10 minutes, the transaction will revert with `Claim delay not met`.\n * OETH is converted to WETH at 1:1.\n * @param _requestId Unique ID for the withdrawal request\n * @return amount Amount of WETH transferred to the withdrawer\n */\n function claimWithdrawal(uint256 _requestId)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256 amount)\n {\n // Try and get more liquidity if there is not enough available\n if (\n withdrawalRequests[_requestId].queued >\n withdrawalQueueMetadata.claimable\n ) {\n // Add any WETH to the withdrawal queue\n // this needs to remain here as:\n // - Vault can be funded and `addWithdrawalQueueLiquidity` is not externally called\n // - funds can be withdrawn from a strategy\n //\n // Those funds need to be added to withdrawal queue liquidity\n _addWithdrawalQueueLiquidity();\n }\n\n amount = _claimWithdrawal(_requestId);\n\n // transfer WETH from the vault to the withdrawer\n IERC20(weth).safeTransfer(msg.sender, amount);\n\n // Prevent insolvency\n _postRedeem(amount);\n }\n\n // slither-disable-end reentrancy-no-eth\n\n /**\n * @notice Claim a previously requested withdrawals once they are claimable.\n * This requests can be claimed once the withdrawal queue's `claimable` amount\n * is greater than or equal each request's `queued` amount and 10 minutes has passed.\n * If one of the requests is not claimable, the whole transaction will revert with `Queue pending liquidity`.\n * If one of the requests is not older than 10 minutes,\n * the whole transaction will revert with `Claim delay not met`.\n * @param _requestIds Unique ID of each withdrawal request\n * @return amounts Amount of WETH received for each request\n * @return totalAmount Total amount of WETH transferred to the withdrawer\n */\n function claimWithdrawals(uint256[] calldata _requestIds)\n external\n virtual\n whenNotCapitalPaused\n nonReentrant\n returns (uint256[] memory amounts, uint256 totalAmount)\n {\n // Add any WETH to the withdrawal queue\n // this needs to remain here as:\n // - Vault can be funded and `addWithdrawalQueueLiquidity` is not externally called\n // - funds can be withdrawn from a strategy\n //\n // Those funds need to be added to withdrawal queue liquidity\n _addWithdrawalQueueLiquidity();\n\n amounts = new uint256[](_requestIds.length);\n for (uint256 i; i < _requestIds.length; ++i) {\n amounts[i] = _claimWithdrawal(_requestIds[i]);\n totalAmount += amounts[i];\n }\n\n // transfer all the claimed WETH from the vault to the withdrawer\n IERC20(weth).safeTransfer(msg.sender, totalAmount);\n\n // Prevent insolvency\n _postRedeem(totalAmount);\n }\n\n function _claimWithdrawal(uint256 requestId)\n internal\n returns (uint256 amount)\n {\n require(withdrawalClaimDelay > 0, \"Async withdrawals not enabled\");\n\n // Load the structs from storage into memory\n WithdrawalRequest memory request = withdrawalRequests[requestId];\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n require(\n request.timestamp + withdrawalClaimDelay <= block.timestamp,\n \"Claim delay not met\"\n );\n // If there isn't enough reserved liquidity in the queue to claim\n require(request.queued <= queue.claimable, \"Queue pending liquidity\");\n require(request.withdrawer == msg.sender, \"Not requester\");\n require(request.claimed == false, \"Already claimed\");\n\n // Store the request as claimed\n withdrawalRequests[requestId].claimed = true;\n // Store the updated claimed amount\n withdrawalQueueMetadata.claimed = queue.claimed + request.amount;\n\n emit WithdrawalClaimed(msg.sender, requestId, request.amount);\n\n return request.amount;\n }\n\n /// @notice Adds WETH to the withdrawal queue if there is a funding shortfall.\n /// @dev is called from the Native Staking strategy when validator withdrawals are processed.\n /// It also called before any WETH is allocated to a strategy.\n function addWithdrawalQueueLiquidity() external {\n _addWithdrawalQueueLiquidity();\n }\n\n /// @dev Adds WETH to the withdrawal queue if there is a funding shortfall.\n /// This assumes 1 WETH equal 1 OETH.\n function _addWithdrawalQueueLiquidity()\n internal\n returns (uint256 addedClaimable)\n {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // Check if the claimable WETH is less than the queued amount\n uint256 queueShortfall = queue.queued - queue.claimable;\n\n // No need to do anything is the withdrawal queue is full funded\n if (queueShortfall == 0) {\n return 0;\n }\n\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // Of the claimable withdrawal requests, how much is unclaimed?\n // That is, the amount of WETH that is currently allocated for the withdrawal queue\n uint256 allocatedWeth = queue.claimable - queue.claimed;\n\n // If there is no unallocated WETH then there is nothing to add to the queue\n if (wethBalance <= allocatedWeth) {\n return 0;\n }\n\n uint256 unallocatedWeth = wethBalance - allocatedWeth;\n\n // the new claimable amount is the smaller of the queue shortfall or unallocated weth\n addedClaimable = queueShortfall < unallocatedWeth\n ? queueShortfall\n : unallocatedWeth;\n uint256 newClaimable = queue.claimable + addedClaimable;\n\n // Store the new claimable amount back to storage\n withdrawalQueueMetadata.claimable = SafeCast.toUint128(newClaimable);\n\n // emit a WithdrawalClaimable event\n emit WithdrawalClaimable(newClaimable, addedClaimable);\n }\n\n /***************************************\n View Functions\n ****************************************/\n\n /// @dev Calculate how much WETH in the vault is not reserved for the withdrawal queue.\n // That is, it is available to be redeemed or deposited into a strategy.\n function _wethAvailable() internal view returns (uint256 wethAvailable) {\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // The amount of WETH that is still to be claimed in the withdrawal queue\n uint256 outstandingWithdrawals = queue.queued - queue.claimed;\n\n // The amount of sitting in WETH in the vault\n uint256 wethBalance = IERC20(weth).balanceOf(address(this));\n\n // If there is not enough WETH in the vault to cover the outstanding withdrawals\n if (wethBalance <= outstandingWithdrawals) {\n return 0;\n }\n\n return wethBalance - outstandingWithdrawals;\n }\n\n /// @dev Get the balance of an asset held in Vault and all strategies\n /// less any WETH that is reserved for the withdrawal queue.\n /// WETH is the only asset that can return a non-zero balance.\n /// All other assets will return 0 even if there is some dust amounts left in the Vault.\n /// For example, there is 1 wei left of stETH in the OETH Vault but will return 0 in this function.\n ///\n /// If there is not enough WETH in the vault and all strategies to cover all outstanding\n /// withdrawal requests then return a WETH balance of 0\n function _checkBalance(address _asset)\n internal\n view\n override\n returns (uint256 balance)\n {\n if (_asset != weth) {\n return 0;\n }\n\n // Get the WETH in the vault and the strategies\n balance = super._checkBalance(_asset);\n\n WithdrawalQueueMetadata memory queue = withdrawalQueueMetadata;\n\n // If the vault becomes insolvent enough that the total value in the vault and all strategies\n // is less than the outstanding withdrawals.\n // For example, there was a mass slashing event and most users request a withdrawal.\n if (balance + queue.claimed < queue.queued) {\n return 0;\n }\n\n // Need to remove WETH that is reserved for the withdrawal queue\n return balance + queue.claimed - queue.queued;\n }\n\n /**\n * @notice Allocate unallocated funds on Vault to strategies.\n **/\n function allocate() external override whenNotCapitalPaused nonReentrant {\n // Add any unallocated WETH to the withdrawal queue first\n _addWithdrawalQueueLiquidity();\n\n _allocate();\n }\n\n /// @dev Allocate WETH to the default WETH strategy if there is excess to the Vault buffer.\n /// This is called from either `mint` or `allocate` and assumes `_addWithdrawalQueueLiquidity`\n /// has been called before this function.\n function _allocate() internal override {\n // No need to do anything if no default strategy for WETH\n address depositStrategyAddr = assetDefaultStrategies[weth];\n if (depositStrategyAddr == address(0)) return;\n\n uint256 wethAvailableInVault = _wethAvailable();\n // No need to do anything if there isn't any WETH in the vault to allocate\n if (wethAvailableInVault == 0) return;\n\n // Calculate the target buffer for the vault using the total supply\n uint256 totalSupply = oUSD.totalSupply();\n uint256 targetBuffer = totalSupply.mulTruncate(vaultBuffer);\n\n // If available WETH in the Vault is below or equal the target buffer then there's nothing to allocate\n if (wethAvailableInVault <= targetBuffer) return;\n\n // The amount of assets to allocate to the default strategy\n uint256 allocateAmount = wethAvailableInVault - targetBuffer;\n\n IStrategy strategy = IStrategy(depositStrategyAddr);\n // Transfer WETH to the strategy and call the strategy's deposit function\n IERC20(weth).safeTransfer(address(strategy), allocateAmount);\n strategy.deposit(weth, allocateAmount);\n\n emit AssetAllocated(weth, depositStrategyAddr, allocateAmount);\n }\n\n /// @dev The total value of all WETH held by the vault and all its strategies\n /// less any WETH that is reserved for the withdrawal queue.\n ///\n // If there is not enough WETH in the vault and all strategies to cover all outstanding\n // withdrawal requests then return a total value of 0.\n function _totalValue() internal view override returns (uint256 value) {\n // As WETH is the only asset, just return the WETH balance\n return _checkBalance(weth);\n }\n\n /// @dev Only WETH is supported in the OETH Vault so return the WETH balance only\n /// Any ETH balances in the Vault will be ignored.\n /// Amounts from previously supported vault assets will also be ignored.\n /// For example, there is 1 wei left of stETH in the OETH Vault but is will be ignored.\n function _totalValueInVault()\n internal\n view\n override\n returns (uint256 value)\n {\n value = IERC20(weth).balanceOf(address(this));\n }\n}\n" + }, + "contracts/vault/OETHZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWETH9 } from \"../interfaces/IWETH9.sol\";\nimport { ISfrxETH } from \"../interfaces/ISfrxETH.sol\";\n\ncontract OETHZapper {\n IERC20 public immutable oeth;\n IVault public immutable vault;\n\n IWETH9 public constant weth =\n IWETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);\n IERC20 public constant frxeth =\n IERC20(0x5E8422345238F34275888049021821E8E08CAa1f);\n ISfrxETH public constant sfrxeth =\n ISfrxETH(0xac3E018457B222d93114458476f3E3416Abbe38F);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(address _oeth, address _vault) {\n oeth = IERC20(_oeth);\n vault = IVault(_vault);\n\n weth.approve(address(_vault), type(uint256).max);\n frxeth.approve(address(_vault), type(uint256).max);\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return.\n * Will verify that the user is sent 1:1 for ETH.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit ETH and receive OETH in return\n * Will verify that the user is sent 1:1 for ETH.\n * @return Amount of OETH sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n weth.deposit{ value: balance }();\n emit Zap(msg.sender, ETH_MARKER, balance);\n return _mint(address(weth), balance);\n }\n\n /**\n * @dev Deposit SFRXETH to the vault and receive OETH in return\n * @param amount Amount of SFRXETH to deposit\n * @param minOETH Minimum amount of OETH to receive\n * @return Amount of OETH sent to user\n */\n function depositSFRXETH(uint256 amount, uint256 minOETH)\n external\n returns (uint256)\n {\n sfrxeth.redeem(amount, address(this), msg.sender);\n emit Zap(msg.sender, address(sfrxeth), amount);\n return _mint(address(frxeth), minOETH);\n }\n\n /**\n * @dev Internal function to mint OETH from an asset\n * @param asset Address of asset for the vault to mint from\n * @param minOETH Minimum amount of OETH to for user to receive\n * @return Amount of OETH sent to user\n */\n function _mint(address asset, uint256 minOETH) internal returns (uint256) {\n uint256 toMint = IERC20(asset).balanceOf(address(this));\n vault.mint(asset, toMint, minOETH);\n uint256 mintedAmount = oeth.balanceOf(address(this));\n require(mintedAmount >= minOETH, \"Zapper: not enough minted\");\n require(oeth.transfer(msg.sender, mintedAmount));\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/OSonicVaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultAdmin } from \"./OETHVaultAdmin.sol\";\n\n/**\n * @title Origin Sonic VaultAdmin contract on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonicVaultAdmin is OETHVaultAdmin {\n /// @param _wS Sonic's Wrapped S token\n constructor(address _wS) OETHVaultAdmin(_wS) {}\n\n /***************************************\n Asset Config\n ****************************************/\n\n /**\n * @notice Add a supported asset to the contract, i.e. one that can be to mint OTokens.\n * @dev Overridden to remove price provider integration\n * @param _asset Address of asset\n * @param _unitConversion 0 decimals, 1 exchange rate\n */\n function supportAsset(address _asset, uint8 _unitConversion)\n external\n override\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Asset already supported\");\n\n assets[_asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(_unitConversion),\n decimals: 0, // will be overridden in _cacheDecimals\n allowedOracleSlippageBps: 0 // 0% by default\n });\n\n _cacheDecimals(_asset);\n allAssets.push(_asset);\n\n emit AssetSupported(_asset);\n }\n}\n" + }, + "contracts/vault/OSonicVaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { OETHVaultCore } from \"./OETHVaultCore.sol\";\n\n/**\n * @title Origin Sonic VaultCore contract on Sonic\n * @author Origin Protocol Inc\n */\ncontract OSonicVaultCore is OETHVaultCore {\n /// @param _wS Sonic's Wrapped S token\n constructor(address _wS) OETHVaultCore(_wS) {}\n\n /**\n * @notice Instant redeem is not supported on Sonic.\n * Use the asynchronous `requestWithdrawal` a `claimWithdrawal` instead.\n */\n function _redeem(uint256, uint256) internal override {\n revert(\"unsupported function\");\n }\n}\n" + }, + "contracts/vault/OSonicZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { IWrappedSonic } from \"../interfaces/sonic/IWrappedSonic.sol\";\nimport { IERC4626 } from \"../../lib/openzeppelin/interfaces/IERC4626.sol\";\n\n/**\n * @title Zapper for Origin Sonic (OS) tokens\n * @author Origin Protocol Inc\n */\ncontract OSonicZapper {\n IERC20 public immutable OS;\n IERC4626 public immutable wOS;\n IVault public immutable vault;\n\n IWrappedSonic public constant wS =\n IWrappedSonic(0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38);\n address private constant ETH_MARKER =\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\n event Zap(address indexed minter, address indexed asset, uint256 amount);\n\n constructor(\n address _OS,\n address _wOS,\n address _vault\n ) {\n OS = IERC20(_OS);\n wOS = IERC4626(_wOS);\n vault = IVault(_vault);\n\n wS.approve(address(_vault), type(uint256).max);\n IERC20(_OS).approve(_wOS, type(uint256).max);\n }\n\n /**\n * @dev Deposit native S currency and receive Origin Sonic (OS) tokens in return.\n * Will verify that the user is sent 1:1 for S.\n */\n receive() external payable {\n deposit();\n }\n\n /**\n * @dev Deposit native S currency and receive Origin Sonic (OS) tokens in return.\n * Will verify that the user is sent 1:1 for S.\n * @return Amount of Origin Sonic (OS) tokens sent to user\n */\n function deposit() public payable returns (uint256) {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap native S\n wS.deposit{ value: balance }();\n\n // Mint Origin Sonic (OS) with Wrapped Sonic (wS)\n return _mint(balance, msg.sender);\n }\n\n /**\n * @dev Deposit S and receive Wrapped Origin Sonic (wOS) in return\n * @param minReceived min amount of Wrapped Origin Sonic (wOS) to receive\n * @return Amount of Wrapped Origin Sonic (wOS) tokens sent to user\n */\n function depositSForWrappedTokens(uint256 minReceived)\n external\n payable\n returns (uint256)\n {\n uint256 balance = address(this).balance;\n\n emit Zap(msg.sender, ETH_MARKER, balance);\n\n // Wrap S\n wS.deposit{ value: balance }();\n\n // Mint with Wrapped Sonic\n uint256 mintOS = _mint(balance, address(this));\n\n // Wrap Origin Sonic (OS) into Wrapped Origin Sonic (wOS)\n uint256 mintedWOS = wOS.deposit(mintOS, msg.sender);\n\n require(mintedWOS >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWOS;\n }\n\n /**\n * @dev Deposit Wrapped Sonic (wS) tokens and receive Wrapped Origin Sonic (wOS) tokens in return\n * @param wSAmount Amount of Wrapped Sonic (wS) to deposit\n * @param minReceived min amount of Wrapped Origin Sonic (wOS) token to receive\n * @return Amount of Wrapped Origin Sonic (wOS) tokens sent to user\n */\n function depositWSForWrappedTokens(uint256 wSAmount, uint256 minReceived)\n external\n returns (uint256)\n {\n // slither-disable-next-line unchecked-transfer unused-return\n wS.transferFrom(msg.sender, address(this), wSAmount);\n\n emit Zap(msg.sender, address(wS), wSAmount);\n\n // Mint with Wrapped Sonic (wS)\n uint256 mintedOS = _mint(wSAmount, address(this));\n\n // Wrap Origin Sonic (OS) tokens into Wrapped Origin Sonic (wOS) tokens\n uint256 mintedWOS = wOS.deposit(mintedOS, msg.sender);\n\n require(mintedWOS >= minReceived, \"Zapper: not enough minted\");\n\n return mintedWOS;\n }\n\n /**\n * @dev Internal function to mint Origin Sonic (OS) with Wrapped S (wS)\n * @param minOS Minimum amount of Origin Sonic (OS) tokens the user can receive\n * @param recipient Address that receives the tokens\n * @return Amount of Origin Sonic (OS) tokens sent to the recipient\n */\n function _mint(uint256 minOS, address recipient)\n internal\n returns (uint256)\n {\n uint256 toMint = wS.balanceOf(address(this));\n vault.mint(address(wS), toMint, minOS);\n uint256 mintedAmount = OS.balanceOf(address(this));\n require(mintedAmount >= minOS, \"Zapper: not enough minted\");\n\n if (recipient != address(this)) {\n require(OS.transfer(recipient, mintedAmount));\n }\n\n return mintedAmount;\n }\n}\n" + }, + "contracts/vault/Vault.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OUSD VaultInitializer Contract\n * @notice The VaultInitializer sets up the initial contract.\n * @author Origin Protocol Inc\n */\nimport { VaultInitializer } from \"./VaultInitializer.sol\";\nimport { VaultAdmin } from \"./VaultAdmin.sol\";\n\ncontract Vault is VaultInitializer, VaultAdmin {}\n" + }, + "contracts/vault/VaultAdmin.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultAdmin contract\n * @notice The VaultAdmin contract makes configuration and admin calls on the vault.\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { ISwapper } from \"../interfaces/ISwapper.sol\";\nimport { IVault } from \"../interfaces/IVault.sol\";\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { SafeCast } from \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\n\nimport \"./VaultStorage.sol\";\n\ncontract VaultAdmin is VaultStorage {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n using SafeCast for uint256;\n\n /**\n * @dev Verifies that the caller is the Governor or Strategist.\n */\n modifier onlyGovernorOrStrategist() {\n require(\n msg.sender == strategistAddr || isGovernor(),\n \"Caller is not the Strategist or Governor\"\n );\n _;\n }\n\n /***************************************\n Configuration\n ****************************************/\n\n /**\n * @notice Set address of price provider.\n * @param _priceProvider Address of price provider\n */\n function setPriceProvider(address _priceProvider) external onlyGovernor {\n priceProvider = _priceProvider;\n emit PriceProviderUpdated(_priceProvider);\n }\n\n /**\n * @notice Set a fee in basis points to be charged for a redeem.\n * @param _redeemFeeBps Basis point fee to be charged\n */\n function setRedeemFeeBps(uint256 _redeemFeeBps) external onlyGovernor {\n require(_redeemFeeBps <= 1000, \"Redeem fee should not be over 10%\");\n redeemFeeBps = _redeemFeeBps;\n emit RedeemFeeUpdated(_redeemFeeBps);\n }\n\n /**\n * @notice Set a buffer of assets to keep in the Vault to handle most\n * redemptions without needing to spend gas unwinding assets from a Strategy.\n * @param _vaultBuffer Percentage using 18 decimals. 100% = 1e18.\n */\n function setVaultBuffer(uint256 _vaultBuffer)\n external\n onlyGovernorOrStrategist\n {\n require(_vaultBuffer <= 1e18, \"Invalid value\");\n vaultBuffer = _vaultBuffer;\n emit VaultBufferUpdated(_vaultBuffer);\n }\n\n /**\n * @notice Sets the minimum amount of OTokens in a mint to trigger an\n * automatic allocation of funds afterwords.\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setAutoAllocateThreshold(uint256 _threshold)\n external\n onlyGovernor\n {\n autoAllocateThreshold = _threshold;\n emit AllocateThresholdUpdated(_threshold);\n }\n\n /**\n * @notice Set a minimum amount of OTokens in a mint or redeem that triggers a\n * rebase\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setRebaseThreshold(uint256 _threshold) external onlyGovernor {\n rebaseThreshold = _threshold;\n emit RebaseThresholdUpdated(_threshold);\n }\n\n /**\n * @notice Set address of Strategist\n * @param _address Address of Strategist\n */\n function setStrategistAddr(address _address) external onlyGovernor {\n strategistAddr = _address;\n emit StrategistUpdated(_address);\n }\n\n /**\n * @notice Set the default Strategy for an asset, i.e. the one which the asset\n will be automatically allocated to and withdrawn from\n * @param _asset Address of the asset\n * @param _strategy Address of the Strategy\n */\n function setAssetDefaultStrategy(address _asset, address _strategy)\n external\n onlyGovernorOrStrategist\n {\n emit AssetDefaultStrategyUpdated(_asset, _strategy);\n // If its a zero address being passed for the strategy we are removing\n // the default strategy\n if (_strategy != address(0)) {\n // Make sure the strategy meets some criteria\n require(strategies[_strategy].isSupported, \"Strategy not approved\");\n IStrategy strategy = IStrategy(_strategy);\n require(assets[_asset].isSupported, \"Asset is not supported\");\n require(\n strategy.supportsAsset(_asset),\n \"Asset not supported by Strategy\"\n );\n }\n assetDefaultStrategies[_asset] = _strategy;\n }\n\n /**\n * @notice Set maximum amount of OTokens that can at any point be minted and deployed\n * to strategy (used only by ConvexOUSDMetaStrategy for now).\n * @param _threshold OToken amount with 18 fixed decimals.\n */\n function setNetOusdMintForStrategyThreshold(uint256 _threshold)\n external\n onlyGovernor\n {\n /**\n * Because `netOusdMintedForStrategy` check in vault core works both ways\n * (positive and negative) the actual impact of the amount of OToken minted\n * could be double the threshold. E.g.:\n * - contract has threshold set to 100\n * - state of netOusdMinted is -90\n * - in effect it can mint 190 OToken and still be within limits\n *\n * We are somewhat mitigating this behaviour by resetting the netOusdMinted\n * counter whenever new threshold is set. So it can only move one threshold\n * amount in each direction. This also enables us to reduce the threshold\n * amount and not have problems with current netOusdMinted being near\n * limits on either side.\n */\n netOusdMintedForStrategy = 0;\n netOusdMintForStrategyThreshold = _threshold;\n emit NetOusdMintForStrategyThresholdChanged(_threshold);\n }\n\n /**\n * @notice Changes the async withdrawal claim period for OETH & superOETHb\n * @param _delay Delay period (should be between 10 mins to 7 days).\n * Set to 0 to disable async withdrawals\n */\n function setWithdrawalClaimDelay(uint256 _delay) external onlyGovernor {\n require(\n _delay == 0 || (_delay >= 10 minutes && _delay <= 15 days),\n \"Invalid claim delay period\"\n );\n withdrawalClaimDelay = _delay;\n emit WithdrawalClaimDelayUpdated(_delay);\n }\n\n /**\n * @notice Set a yield streaming max rate. This spreads yield over\n * time if it is above the max rate.\n * @param yearlyApr in 1e18 notation. 3 * 1e18 = 3% APR\n */\n function setRebaseRateMax(uint256 yearlyApr)\n external\n onlyGovernorOrStrategist\n {\n // The old yield will be at the old rate\n IVault(address(this)).rebase();\n // Change the rate\n uint256 newPerSecond = yearlyApr / 100 / 365 days;\n require(newPerSecond <= MAX_REBASE_PER_SECOND, \"Rate too high\");\n rebasePerSecondMax = newPerSecond.toUint64();\n emit RebasePerSecondMaxChanged(newPerSecond);\n }\n\n /**\n * @notice Set the drip duration period\n * @param _dripDuration Time in seconds to target a constant yield rate\n */\n function setDripDuration(uint256 _dripDuration)\n external\n onlyGovernorOrStrategist\n {\n // The old yield will be at the old rate\n IVault(address(this)).rebase();\n dripDuration = _dripDuration.toUint64();\n emit DripDurationChanged(_dripDuration);\n }\n\n /***************************************\n Swaps\n ****************************************/\n\n /**\n * @notice Strategist swaps collateral assets sitting in the vault.\n * @param _fromAsset The token address of the asset being sold by the vault.\n * @param _toAsset The token address of the asset being purchased by the vault.\n * @param _fromAssetAmount The amount of assets being sold by the vault.\n * @param _minToAssetAmount The minimum amount of assets to be purchased.\n * @param _data implementation specific data. eg 1Inch swap data\n * @return toAssetAmount The amount of toAssets that was received from the swap\n */\n function swapCollateral(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n )\n external\n nonReentrant\n onlyGovernorOrStrategist\n returns (uint256 toAssetAmount)\n {\n toAssetAmount = _swapCollateral(\n _fromAsset,\n _toAsset,\n _fromAssetAmount,\n _minToAssetAmount,\n _data\n );\n }\n\n function _swapCollateral(\n address _fromAsset,\n address _toAsset,\n uint256 _fromAssetAmount,\n uint256 _minToAssetAmount,\n bytes calldata _data\n ) internal virtual returns (uint256 toAssetAmount) {\n // Check fromAsset and toAsset are valid\n Asset memory fromAssetConfig = assets[_fromAsset];\n Asset memory toAssetConfig = assets[_toAsset];\n require(fromAssetConfig.isSupported, \"From asset is not supported\");\n require(toAssetConfig.isSupported, \"To asset is not supported\");\n\n // Load swap config into memory to avoid separate SLOADs\n SwapConfig memory config = swapConfig;\n\n // Scope a new block to remove toAssetBalBefore from the scope of swapCollateral.\n // This avoids a stack too deep error.\n {\n uint256 toAssetBalBefore = IERC20(_toAsset).balanceOf(\n address(this)\n );\n\n // Transfer from assets to the swapper contract\n IERC20(_fromAsset).safeTransfer(config.swapper, _fromAssetAmount);\n\n // Call to the Swapper contract to do the actual swap\n // The -1 is required for stETH which sometimes transfers 1 wei less than what was specified.\n // slither-disable-next-line unused-return\n ISwapper(config.swapper).swap(\n _fromAsset,\n _toAsset,\n _fromAssetAmount - 1,\n _minToAssetAmount,\n _data\n );\n\n // Compute the change in asset balance held by the Vault\n toAssetAmount =\n IERC20(_toAsset).balanceOf(address(this)) -\n toAssetBalBefore;\n }\n\n // Check the to assets returned is above slippage amount specified by the strategist\n require(\n toAssetAmount >= _minToAssetAmount,\n \"Strategist slippage limit\"\n );\n\n // Scope a new block to remove minOracleToAssetAmount from the scope of swapCollateral.\n // This avoids a stack too deep error.\n {\n // Check the slippage against the Oracle in case the strategist made a mistake or has become malicious.\n // to asset amount = from asset amount * from asset price / to asset price\n uint256 minOracleToAssetAmount = (_fromAssetAmount *\n (1e4 - fromAssetConfig.allowedOracleSlippageBps) *\n IOracle(priceProvider).price(_fromAsset)) /\n (IOracle(priceProvider).price(_toAsset) *\n (1e4 + toAssetConfig.allowedOracleSlippageBps));\n\n // Scale both sides up to 18 decimals to compare\n require(\n toAssetAmount.scaleBy(18, toAssetConfig.decimals) >=\n minOracleToAssetAmount.scaleBy(\n 18,\n fromAssetConfig.decimals\n ),\n \"Oracle slippage limit exceeded\"\n );\n }\n\n // Check the vault's total value hasn't gone below the OToken total supply\n // by more than the allowed percentage.\n require(\n IVault(address(this)).totalValue() >=\n (oUSD.totalSupply() * ((1e4 - config.allowedUndervalueBps))) /\n 1e4,\n \"Allowed value < supply\"\n );\n\n emit Swapped(_fromAsset, _toAsset, _fromAssetAmount, toAssetAmount);\n }\n\n /***************************************\n Swap Config\n ****************************************/\n\n /**\n * @notice Set the contract the performs swaps of collateral assets.\n * @param _swapperAddr Address of the Swapper contract that implements the ISwapper interface.\n */\n function setSwapper(address _swapperAddr) external onlyGovernor {\n swapConfig.swapper = _swapperAddr;\n emit SwapperChanged(_swapperAddr);\n }\n\n /// @notice Contract that swaps the vault's collateral assets\n function swapper() external view returns (address swapper_) {\n swapper_ = swapConfig.swapper;\n }\n\n /**\n * @notice Set max allowed percentage the vault total value can drop below the OToken total supply in basis points\n * when executing collateral swaps.\n * @param _basis Percentage in basis points. eg 100 == 1%\n */\n function setSwapAllowedUndervalue(uint16 _basis) external onlyGovernor {\n require(_basis < 10001, \"Invalid basis points\");\n swapConfig.allowedUndervalueBps = _basis;\n emit SwapAllowedUndervalueChanged(_basis);\n }\n\n /**\n * @notice Max allowed percentage the vault total value can drop below the OToken total supply in basis points\n * when executing a collateral swap.\n * For example 100 == 1%\n * @return value Percentage in basis points.\n */\n function allowedSwapUndervalue() external view returns (uint256 value) {\n value = swapConfig.allowedUndervalueBps;\n }\n\n /**\n * @notice Set the allowed slippage from the Oracle price for collateral asset swaps.\n * @param _asset Address of the asset token.\n * @param _allowedOracleSlippageBps allowed slippage from Oracle in basis points. eg 20 = 0.2%. Max 10%.\n */\n function setOracleSlippage(address _asset, uint16 _allowedOracleSlippageBps)\n external\n onlyGovernor\n {\n require(assets[_asset].isSupported, \"Asset not supported\");\n require(_allowedOracleSlippageBps < 1000, \"Slippage too high\");\n\n assets[_asset].allowedOracleSlippageBps = _allowedOracleSlippageBps;\n\n emit SwapSlippageChanged(_asset, _allowedOracleSlippageBps);\n }\n\n /***************************************\n Asset Config\n ****************************************/\n\n /**\n * @notice Add a supported asset to the contract, i.e. one that can be\n * to mint OTokens.\n * @param _asset Address of asset\n */\n function supportAsset(address _asset, uint8 _unitConversion)\n external\n virtual\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Asset already supported\");\n\n assets[_asset] = Asset({\n isSupported: true,\n unitConversion: UnitConversion(_unitConversion),\n decimals: 0, // will be overridden in _cacheDecimals\n allowedOracleSlippageBps: 0 // 0% by default\n });\n\n _cacheDecimals(_asset);\n allAssets.push(_asset);\n\n // Verify that our oracle supports the asset\n // slither-disable-next-line unused-return\n IOracle(priceProvider).price(_asset);\n\n emit AssetSupported(_asset);\n }\n\n /**\n * @notice Remove a supported asset from the Vault\n * @param _asset Address of asset\n */\n function removeAsset(address _asset) external onlyGovernor {\n require(assets[_asset].isSupported, \"Asset not supported\");\n\n // 1e13 for 18 decimals. And 10 for 6 decimals\n uint256 maxDustBalance = uint256(1e13).scaleBy(\n assets[_asset].decimals,\n 18\n );\n\n require(\n IVault(address(this)).checkBalance(_asset) <= maxDustBalance,\n \"Vault still holds asset\"\n );\n\n uint256 assetsCount = allAssets.length;\n uint256 assetIndex = assetsCount; // initialize at invalid index\n for (uint256 i = 0; i < assetsCount; ++i) {\n if (allAssets[i] == _asset) {\n assetIndex = i;\n break;\n }\n }\n\n // Note: If asset is not found in `allAssets`, the following line\n // will revert with an out-of-bound error. However, there's no\n // reason why an asset would have `Asset.isSupported = true` but\n // not exist in `allAssets`.\n\n // Update allAssets array\n allAssets[assetIndex] = allAssets[assetsCount - 1];\n allAssets.pop();\n\n // Reset default strategy\n assetDefaultStrategies[_asset] = address(0);\n emit AssetDefaultStrategyUpdated(_asset, address(0));\n\n // Remove asset from storage\n delete assets[_asset];\n\n emit AssetRemoved(_asset);\n }\n\n /**\n * @notice Cache decimals on OracleRouter for a particular asset. This action\n * is required before that asset's price can be accessed.\n * @param _asset Address of asset token\n */\n function cacheDecimals(address _asset) external onlyGovernor {\n _cacheDecimals(_asset);\n }\n\n /***************************************\n Strategy Config\n ****************************************/\n\n /**\n * @notice Add a strategy to the Vault.\n * @param _addr Address of the strategy to add\n */\n function approveStrategy(address _addr) external onlyGovernor {\n require(!strategies[_addr].isSupported, \"Strategy already approved\");\n strategies[_addr] = Strategy({ isSupported: true, _deprecated: 0 });\n allStrategies.push(_addr);\n emit StrategyApproved(_addr);\n }\n\n /**\n * @notice Remove a strategy from the Vault.\n * @param _addr Address of the strategy to remove\n */\n\n function removeStrategy(address _addr) external onlyGovernor {\n require(strategies[_addr].isSupported, \"Strategy not approved\");\n\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n require(\n assetDefaultStrategies[allAssets[i]] != _addr,\n \"Strategy is default for an asset\"\n );\n }\n\n // Initialize strategyIndex with out of bounds result so function will\n // revert if no valid index found\n uint256 stratCount = allStrategies.length;\n uint256 strategyIndex = stratCount;\n for (uint256 i = 0; i < stratCount; ++i) {\n if (allStrategies[i] == _addr) {\n strategyIndex = i;\n break;\n }\n }\n\n if (strategyIndex < stratCount) {\n allStrategies[strategyIndex] = allStrategies[stratCount - 1];\n allStrategies.pop();\n\n // Mark the strategy as not supported\n strategies[_addr].isSupported = false;\n\n // Withdraw all assets\n IStrategy strategy = IStrategy(_addr);\n strategy.withdrawAll();\n\n emit StrategyRemoved(_addr);\n }\n }\n\n /***************************************\n Strategies\n ****************************************/\n\n /**\n * @notice Deposit multiple assets from the vault into the strategy.\n * @param _strategyToAddress Address of the Strategy to deposit assets into.\n * @param _assets Array of asset address that will be deposited into the strategy.\n * @param _amounts Array of amounts of each corresponding asset to deposit.\n */\n function depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external onlyGovernorOrStrategist nonReentrant {\n _depositToStrategy(_strategyToAddress, _assets, _amounts);\n }\n\n function _depositToStrategy(\n address _strategyToAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal virtual {\n require(\n strategies[_strategyToAddress].isSupported,\n \"Invalid to Strategy\"\n );\n require(_assets.length == _amounts.length, \"Parameter length mismatch\");\n\n uint256 assetCount = _assets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n address assetAddr = _assets[i];\n require(\n IStrategy(_strategyToAddress).supportsAsset(assetAddr),\n \"Asset unsupported\"\n );\n // Send required amount of funds to the strategy\n IERC20(assetAddr).safeTransfer(_strategyToAddress, _amounts[i]);\n }\n\n // Deposit all the funds that have been sent to the strategy\n IStrategy(_strategyToAddress).depositAll();\n }\n\n /**\n * @notice Withdraw multiple assets from the strategy to the vault.\n * @param _strategyFromAddress Address of the Strategy to withdraw assets from.\n * @param _assets Array of asset address that will be withdrawn from the strategy.\n * @param _amounts Array of amounts of each corresponding asset to withdraw.\n */\n function withdrawFromStrategy(\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) external onlyGovernorOrStrategist nonReentrant {\n _withdrawFromStrategy(\n address(this),\n _strategyFromAddress,\n _assets,\n _amounts\n );\n }\n\n /**\n * @param _recipient can either be a strategy or the Vault\n */\n function _withdrawFromStrategy(\n address _recipient,\n address _strategyFromAddress,\n address[] calldata _assets,\n uint256[] calldata _amounts\n ) internal virtual {\n require(\n strategies[_strategyFromAddress].isSupported,\n \"Invalid from Strategy\"\n );\n require(_assets.length == _amounts.length, \"Parameter length mismatch\");\n\n uint256 assetCount = _assets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n // Withdraw from Strategy to the recipient\n IStrategy(_strategyFromAddress).withdraw(\n _recipient,\n _assets[i],\n _amounts[i]\n );\n }\n }\n\n /**\n * @notice Sets the maximum allowable difference between\n * total supply and backing assets' value.\n */\n function setMaxSupplyDiff(uint256 _maxSupplyDiff) external onlyGovernor {\n maxSupplyDiff = _maxSupplyDiff;\n emit MaxSupplyDiffChanged(_maxSupplyDiff);\n }\n\n /**\n * @notice Sets the trusteeAddress that can receive a portion of yield.\n * Setting to the zero address disables this feature.\n */\n function setTrusteeAddress(address _address) external onlyGovernor {\n trusteeAddress = _address;\n emit TrusteeAddressChanged(_address);\n }\n\n /**\n * @notice Sets the TrusteeFeeBps to the percentage of yield that should be\n * received in basis points.\n */\n function setTrusteeFeeBps(uint256 _basis) external onlyGovernor {\n require(_basis <= 5000, \"basis cannot exceed 50%\");\n trusteeFeeBps = _basis;\n emit TrusteeFeeBpsChanged(_basis);\n }\n\n /**\n * @notice Set OToken Metapool strategy\n * @param _ousdMetaStrategy Address of OToken metapool strategy\n */\n function setOusdMetaStrategy(address _ousdMetaStrategy)\n external\n onlyGovernor\n {\n ousdMetaStrategy = _ousdMetaStrategy;\n emit OusdMetaStrategyUpdated(_ousdMetaStrategy);\n }\n\n /***************************************\n Pause\n ****************************************/\n\n /**\n * @notice Set the deposit paused flag to true to prevent rebasing.\n */\n function pauseRebase() external onlyGovernorOrStrategist {\n rebasePaused = true;\n emit RebasePaused();\n }\n\n /**\n * @notice Set the deposit paused flag to true to allow rebasing.\n */\n function unpauseRebase() external onlyGovernorOrStrategist {\n rebasePaused = false;\n emit RebaseUnpaused();\n }\n\n /**\n * @notice Set the deposit paused flag to true to prevent capital movement.\n */\n function pauseCapital() external onlyGovernorOrStrategist {\n capitalPaused = true;\n emit CapitalPaused();\n }\n\n /**\n * @notice Set the deposit paused flag to false to enable capital movement.\n */\n function unpauseCapital() external onlyGovernorOrStrategist {\n capitalPaused = false;\n emit CapitalUnpaused();\n }\n\n /***************************************\n Utils\n ****************************************/\n\n /**\n * @notice Transfer token to governor. Intended for recovering tokens stuck in\n * contract, i.e. mistaken sends.\n * @param _asset Address for the asset\n * @param _amount Amount of the asset to transfer\n */\n function transferToken(address _asset, uint256 _amount)\n external\n onlyGovernor\n {\n require(!assets[_asset].isSupported, \"Only unsupported assets\");\n IERC20(_asset).safeTransfer(governor(), _amount);\n }\n\n /***************************************\n Strategies Admin\n ****************************************/\n\n /**\n * @notice Withdraws all assets from the strategy and sends assets to the Vault.\n * @param _strategyAddr Strategy address.\n */\n function withdrawAllFromStrategy(address _strategyAddr)\n external\n onlyGovernorOrStrategist\n {\n _withdrawAllFromStrategy(_strategyAddr);\n }\n\n function _withdrawAllFromStrategy(address _strategyAddr) internal virtual {\n require(\n strategies[_strategyAddr].isSupported,\n \"Strategy is not supported\"\n );\n IStrategy strategy = IStrategy(_strategyAddr);\n strategy.withdrawAll();\n }\n\n /**\n * @notice Withdraws all assets from all the strategies and sends assets to the Vault.\n */\n function withdrawAllFromStrategies() external onlyGovernorOrStrategist {\n _withdrawAllFromStrategies();\n }\n\n function _withdrawAllFromStrategies() internal virtual {\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n IStrategy(allStrategies[i]).withdrawAll();\n }\n }\n\n /***************************************\n Utils\n ****************************************/\n\n function _cacheDecimals(address token) internal {\n Asset storage tokenAsset = assets[token];\n if (tokenAsset.decimals != 0) {\n return;\n }\n uint8 decimals = IBasicToken(token).decimals();\n require(decimals >= 6 && decimals <= 18, \"Unexpected precision\");\n tokenAsset.decimals = decimals;\n }\n}\n" + }, + "contracts/vault/VaultCore.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultCore contract\n * @notice The Vault contract stores assets. On a deposit, OTokens will be minted\n and sent to the depositor. On a withdrawal, OTokens will be burned and\n assets will be sent to the withdrawer. The Vault accepts deposits of\n interest from yield bearing strategies which will modify the supply\n of OTokens.\n * @author Origin Protocol Inc\n */\n\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { StableMath } from \"../utils/StableMath.sol\";\nimport { IOracle } from \"../interfaces/IOracle.sol\";\nimport { IGetExchangeRateToken } from \"../interfaces/IGetExchangeRateToken.sol\";\n\nimport \"./VaultInitializer.sol\";\n\ncontract VaultCore is VaultInitializer {\n using SafeERC20 for IERC20;\n using StableMath for uint256;\n /// @dev max signed int\n uint256 internal constant MAX_INT = uint256(type(int256).max);\n\n /**\n * @dev Verifies that the rebasing is not paused.\n */\n modifier whenNotRebasePaused() {\n require(!rebasePaused, \"Rebasing paused\");\n _;\n }\n\n /**\n * @dev Verifies that the deposits are not paused.\n */\n modifier whenNotCapitalPaused() {\n require(!capitalPaused, \"Capital paused\");\n _;\n }\n\n /**\n * @dev Verifies that the caller is the AMO strategy.\n */\n modifier onlyOusdMetaStrategy() {\n require(\n msg.sender == ousdMetaStrategy,\n \"Caller is not the OUSD meta strategy\"\n );\n _;\n }\n\n /**\n * @notice Deposit a supported asset and mint OTokens.\n * @param _asset Address of the asset being deposited\n * @param _amount Amount of the asset being deposited\n * @param _minimumOusdAmount Minimum OTokens to mint\n */\n function mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) external whenNotCapitalPaused nonReentrant {\n _mint(_asset, _amount, _minimumOusdAmount);\n }\n\n /**\n * @dev Deposit a supported asset and mint OTokens.\n * @param _asset Address of the asset being deposited\n * @param _amount Amount of the asset being deposited\n * @param _minimumOusdAmount Minimum OTokens to mint\n */\n function _mint(\n address _asset,\n uint256 _amount,\n uint256 _minimumOusdAmount\n ) internal virtual {\n require(assets[_asset].isSupported, \"Asset is not supported\");\n require(_amount > 0, \"Amount must be greater than 0\");\n\n uint256 units = _toUnits(_amount, _asset);\n uint256 unitPrice = _toUnitPrice(_asset, true);\n uint256 priceAdjustedDeposit = (units * unitPrice) / 1e18;\n\n if (_minimumOusdAmount > 0) {\n require(\n priceAdjustedDeposit >= _minimumOusdAmount,\n \"Mint amount lower than minimum\"\n );\n }\n\n emit Mint(msg.sender, priceAdjustedDeposit);\n\n // Rebase must happen before any transfers occur.\n if (priceAdjustedDeposit >= rebaseThreshold && !rebasePaused) {\n _rebase();\n }\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, priceAdjustedDeposit);\n\n // Transfer the deposited coins to the vault\n IERC20 asset = IERC20(_asset);\n asset.safeTransferFrom(msg.sender, address(this), _amount);\n\n if (priceAdjustedDeposit >= autoAllocateThreshold) {\n _allocate();\n }\n }\n\n /**\n * @notice Mint OTokens for a Metapool Strategy\n * @param _amount Amount of the asset being deposited\n *\n * Notice: can't use `nonReentrant` modifier since the `mint` function can\n * call `allocate`, and that can trigger `ConvexOUSDMetaStrategy` to call this function\n * while the execution of the `mint` has not yet completed -> causing a `nonReentrant` collision.\n *\n * Also important to understand is that this is a limitation imposed by the test suite.\n * Production / mainnet contracts should never be configured in a way where mint/redeem functions\n * that are moving funds between the Vault and end user wallets can influence strategies\n * utilizing this function.\n */\n function mintForStrategy(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n onlyOusdMetaStrategy\n {\n require(_amount < MAX_INT, \"Amount too high\");\n\n emit Mint(msg.sender, _amount);\n\n // safe to cast because of the require check at the beginning of the function\n netOusdMintedForStrategy += int256(_amount);\n\n require(\n abs(netOusdMintedForStrategy) < netOusdMintForStrategyThreshold,\n \"Minted ousd surpassed netOusdMintForStrategyThreshold.\"\n );\n\n // Mint matching amount of OTokens\n oUSD.mint(msg.sender, _amount);\n }\n\n // In memoriam\n\n /**\n * @notice Withdraw a supported asset and burn OTokens.\n * @param _amount Amount of OTokens to burn\n * @param _minimumUnitAmount Minimum stablecoin units to receive in return\n */\n function redeem(uint256 _amount, uint256 _minimumUnitAmount)\n external\n whenNotCapitalPaused\n nonReentrant\n {\n _redeem(_amount, _minimumUnitAmount);\n }\n\n /**\n * @notice Withdraw a supported asset and burn OTokens.\n * @param _amount Amount of OTokens to burn\n * @param _minimumUnitAmount Minimum stablecoin units to receive in return\n */\n function _redeem(uint256 _amount, uint256 _minimumUnitAmount)\n internal\n virtual\n {\n // Calculate redemption outputs\n uint256[] memory outputs = _calculateRedeemOutputs(_amount);\n\n emit Redeem(msg.sender, _amount);\n\n // Send outputs\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n if (outputs[i] == 0) continue;\n\n address assetAddr = allAssets[i];\n\n if (IERC20(assetAddr).balanceOf(address(this)) >= outputs[i]) {\n // Use Vault funds first if sufficient\n IERC20(assetAddr).safeTransfer(msg.sender, outputs[i]);\n } else {\n address strategyAddr = assetDefaultStrategies[assetAddr];\n if (strategyAddr != address(0)) {\n // Nothing in Vault, but something in Strategy, send from there\n IStrategy strategy = IStrategy(strategyAddr);\n strategy.withdraw(msg.sender, assetAddr, outputs[i]);\n } else {\n // Cant find funds anywhere\n revert(\"Liquidity error\");\n }\n }\n }\n\n if (_minimumUnitAmount > 0) {\n uint256 unitTotal = 0;\n for (uint256 i = 0; i < outputs.length; ++i) {\n unitTotal += _toUnits(outputs[i], allAssets[i]);\n }\n require(\n unitTotal >= _minimumUnitAmount,\n \"Redeem amount lower than minimum\"\n );\n }\n\n oUSD.burn(msg.sender, _amount);\n\n _postRedeem(_amount);\n }\n\n function _postRedeem(uint256 _amount) internal {\n // Until we can prove that we won't affect the prices of our assets\n // by withdrawing them, this should be here.\n // It's possible that a strategy was off on its asset total, perhaps\n // a reward token sold for more or for less than anticipated.\n uint256 totalUnits = 0;\n if (_amount >= rebaseThreshold && !rebasePaused) {\n totalUnits = _rebase();\n } else {\n totalUnits = _totalValue();\n }\n\n // Check that the OTokens are backed by enough assets\n if (maxSupplyDiff > 0) {\n // If there are more outstanding withdrawal requests than assets in the vault and strategies\n // then the available assets will be negative and totalUnits will be rounded up to zero.\n // As we don't know the exact shortfall amount, we will reject all redeem and withdrawals\n require(totalUnits > 0, \"Too many outstanding requests\");\n\n // Allow a max difference of maxSupplyDiff% between\n // backing assets value and OUSD total supply\n uint256 diff = oUSD.totalSupply().divPrecisely(totalUnits);\n require(\n (diff > 1e18 ? diff - 1e18 : 1e18 - diff) <= maxSupplyDiff,\n \"Backing supply liquidity error\"\n );\n }\n }\n\n /**\n * @notice Burn OTokens for Metapool Strategy\n * @param _amount Amount of OUSD to burn\n *\n * @dev Notice: can't use `nonReentrant` modifier since the `redeem` function could\n * require withdrawal on `ConvexOUSDMetaStrategy` and that one can call `burnForStrategy`\n * while the execution of the `redeem` has not yet completed -> causing a `nonReentrant` collision.\n *\n * Also important to understand is that this is a limitation imposed by the test suite.\n * Production / mainnet contracts should never be configured in a way where mint/redeem functions\n * that are moving funds between the Vault and end user wallets can influence strategies\n * utilizing this function.\n */\n function burnForStrategy(uint256 _amount)\n external\n virtual\n whenNotCapitalPaused\n onlyOusdMetaStrategy\n {\n require(_amount < MAX_INT, \"Amount too high\");\n\n emit Redeem(msg.sender, _amount);\n\n // safe to cast because of the require check at the beginning of the function\n netOusdMintedForStrategy -= int256(_amount);\n\n require(\n abs(netOusdMintedForStrategy) < netOusdMintForStrategyThreshold,\n \"Attempting to burn too much OUSD.\"\n );\n\n // Burn OTokens\n oUSD.burn(msg.sender, _amount);\n }\n\n /**\n * @notice Allocate unallocated funds on Vault to strategies.\n **/\n function allocate() external virtual whenNotCapitalPaused nonReentrant {\n _allocate();\n }\n\n /**\n * @dev Allocate unallocated funds on Vault to strategies.\n **/\n function _allocate() internal virtual {\n uint256 vaultValue = _totalValueInVault();\n // Nothing in vault to allocate\n if (vaultValue == 0) return;\n uint256 strategiesValue = _totalValueInStrategies();\n // We have a method that does the same as this, gas optimisation\n uint256 calculatedTotalValue = vaultValue + strategiesValue;\n\n // We want to maintain a buffer on the Vault so calculate a percentage\n // modifier to multiply each amount being allocated by to enforce the\n // vault buffer\n uint256 vaultBufferModifier;\n if (strategiesValue == 0) {\n // Nothing in Strategies, allocate 100% minus the vault buffer to\n // strategies\n vaultBufferModifier = uint256(1e18) - vaultBuffer;\n } else {\n vaultBufferModifier =\n (vaultBuffer * calculatedTotalValue) /\n vaultValue;\n if (1e18 > vaultBufferModifier) {\n // E.g. 1e18 - (1e17 * 10e18)/5e18 = 8e17\n // (5e18 * 8e17) / 1e18 = 4e18 allocated from Vault\n vaultBufferModifier = uint256(1e18) - vaultBufferModifier;\n } else {\n // We need to let the buffer fill\n return;\n }\n }\n if (vaultBufferModifier == 0) return;\n\n // Iterate over all assets in the Vault and allocate to the appropriate\n // strategy\n uint256 assetCount = allAssets.length;\n for (uint256 i = 0; i < assetCount; ++i) {\n IERC20 asset = IERC20(allAssets[i]);\n uint256 assetBalance = asset.balanceOf(address(this));\n // No balance, nothing to do here\n if (assetBalance == 0) continue;\n\n // Multiply the balance by the vault buffer modifier and truncate\n // to the scale of the asset decimals\n uint256 allocateAmount = assetBalance.mulTruncate(\n vaultBufferModifier\n );\n\n address depositStrategyAddr = assetDefaultStrategies[\n address(asset)\n ];\n\n if (depositStrategyAddr != address(0) && allocateAmount > 0) {\n IStrategy strategy = IStrategy(depositStrategyAddr);\n // Transfer asset to Strategy and call deposit method to\n // mint or take required action\n asset.safeTransfer(address(strategy), allocateAmount);\n strategy.deposit(address(asset), allocateAmount);\n emit AssetAllocated(\n address(asset),\n depositStrategyAddr,\n allocateAmount\n );\n }\n }\n }\n\n /**\n * @notice Calculate the total value of assets held by the Vault and all\n * strategies and update the supply of OTokens.\n */\n function rebase() external virtual nonReentrant {\n _rebase();\n }\n\n /**\n * @dev Calculate the total value of assets held by the Vault and all\n * strategies and update the supply of OTokens, optionally sending a\n * portion of the yield to the trustee.\n * @return totalUnits Total balance of Vault in units\n */\n function _rebase() internal whenNotRebasePaused returns (uint256) {\n uint256 supply = oUSD.totalSupply();\n uint256 vaultValue = _totalValue();\n // If no supply yet, do not rebase\n if (supply == 0) {\n return vaultValue;\n }\n\n // Calculate yield and new supply\n (uint256 yield, uint256 targetRate) = _nextYield(supply, vaultValue);\n uint256 newSupply = supply + yield;\n // Only rebase upwards and if we have enough backing funds\n if (newSupply <= supply || newSupply > vaultValue) {\n return vaultValue;\n }\n\n rebasePerSecondTarget = uint64(_min(targetRate, type(uint64).max));\n lastRebase = uint64(block.timestamp); // Intentional cast\n\n // Fee collection on yield\n address _trusteeAddress = trusteeAddress; // gas savings\n uint256 fee = 0;\n if (_trusteeAddress != address(0)) {\n fee = (yield * trusteeFeeBps) / 1e4;\n if (fee > 0) {\n require(fee < yield, \"Fee must not be greater than yield\");\n oUSD.mint(_trusteeAddress, fee);\n }\n }\n emit YieldDistribution(_trusteeAddress, yield, fee);\n\n // Only ratchet OToken supply upwards\n // Final check uses latest totalSupply\n if (newSupply > oUSD.totalSupply()) {\n oUSD.changeSupply(newSupply);\n }\n return vaultValue;\n }\n\n /**\n * @notice Calculates the amount that would rebase at next rebase.\n * This is before any fees.\n * @return yield amount of expected yield\n */\n function previewYield() external view returns (uint256 yield) {\n (yield, ) = _nextYield(oUSD.totalSupply(), _totalValue());\n return yield;\n }\n\n function _nextYield(uint256 supply, uint256 vaultValue)\n internal\n view\n virtual\n returns (uint256 yield, uint256 targetRate)\n {\n uint256 nonRebasing = oUSD.nonRebasingSupply();\n uint256 rebasing = supply - nonRebasing;\n uint256 elapsed = block.timestamp - lastRebase;\n targetRate = rebasePerSecondTarget;\n\n if (\n elapsed == 0 || // Yield only once per block.\n rebasing == 0 || // No yield if there are no rebasing tokens to give it to.\n supply > vaultValue || // No yield if we do not have yield to give.\n block.timestamp >= type(uint64).max // No yield if we are too far in the future to calculate it correctly.\n ) {\n return (0, targetRate);\n }\n\n // Start with the full difference available\n yield = vaultValue - supply;\n\n // Cap via optional automatic duration smoothing\n uint256 _dripDuration = dripDuration;\n if (_dripDuration > 1) {\n // If we are able to sustain an increased drip rate for\n // double the duration, then increase the target drip rate\n targetRate = _max(targetRate, yield / (_dripDuration * 2));\n // If we cannot sustain the target rate any more,\n // then rebase what we can, and reduce the target\n targetRate = _min(targetRate, yield / _dripDuration);\n // drip at the new target rate\n yield = _min(yield, targetRate * elapsed);\n }\n\n // Cap per second. elapsed is not 1e18 denominated\n yield = _min(yield, (rebasing * elapsed * rebasePerSecondMax) / 1e18);\n\n // Cap at a hard max per rebase, to avoid long durations resulting in huge rebases\n yield = _min(yield, (rebasing * MAX_REBASE) / 1e18);\n\n return (yield, targetRate);\n }\n\n /**\n * @notice Determine the total value of assets held by the vault and its\n * strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function totalValue() external view virtual returns (uint256 value) {\n value = _totalValue();\n }\n\n /**\n * @dev Internal Calculate the total value of the assets held by the\n * vault and its strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValue() internal view virtual returns (uint256 value) {\n return _totalValueInVault() + _totalValueInStrategies();\n }\n\n /**\n * @dev Internal to calculate total value of all assets held in Vault.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInVault()\n internal\n view\n virtual\n returns (uint256 value)\n {\n uint256 assetCount = allAssets.length;\n for (uint256 y; y < assetCount; ++y) {\n address assetAddr = allAssets[y];\n uint256 balance = IERC20(assetAddr).balanceOf(address(this));\n if (balance > 0) {\n value += _toUnits(balance, assetAddr);\n }\n }\n }\n\n /**\n * @dev Internal to calculate total value of all assets held in Strategies.\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInStrategies() internal view returns (uint256 value) {\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n value = value + _totalValueInStrategy(allStrategies[i]);\n }\n }\n\n /**\n * @dev Internal to calculate total value of all assets held by strategy.\n * @param _strategyAddr Address of the strategy\n * @return value Total value in USD/ETH (1e18)\n */\n function _totalValueInStrategy(address _strategyAddr)\n internal\n view\n returns (uint256 value)\n {\n IStrategy strategy = IStrategy(_strategyAddr);\n uint256 assetCount = allAssets.length;\n for (uint256 y; y < assetCount; ++y) {\n address assetAddr = allAssets[y];\n if (strategy.supportsAsset(assetAddr)) {\n uint256 balance = strategy.checkBalance(assetAddr);\n if (balance > 0) {\n value += _toUnits(balance, assetAddr);\n }\n }\n }\n }\n\n /**\n * @notice Get the balance of an asset held in Vault and all strategies.\n * @param _asset Address of asset\n * @return uint256 Balance of asset in decimals of asset\n */\n function checkBalance(address _asset) external view returns (uint256) {\n return _checkBalance(_asset);\n }\n\n /**\n * @notice Get the balance of an asset held in Vault and all strategies.\n * @param _asset Address of asset\n * @return balance Balance of asset in decimals of asset\n */\n function _checkBalance(address _asset)\n internal\n view\n virtual\n returns (uint256 balance)\n {\n IERC20 asset = IERC20(_asset);\n balance = asset.balanceOf(address(this));\n uint256 stratCount = allStrategies.length;\n for (uint256 i = 0; i < stratCount; ++i) {\n IStrategy strategy = IStrategy(allStrategies[i]);\n if (strategy.supportsAsset(_asset)) {\n balance = balance + strategy.checkBalance(_asset);\n }\n }\n }\n\n /**\n * @notice Calculate the outputs for a redeem function, i.e. the mix of\n * coins that will be returned\n */\n function calculateRedeemOutputs(uint256 _amount)\n external\n view\n returns (uint256[] memory)\n {\n return _calculateRedeemOutputs(_amount);\n }\n\n /**\n * @dev Calculate the outputs for a redeem function, i.e. the mix of\n * coins that will be returned.\n * @return outputs Array of amounts respective to the supported assets\n */\n function _calculateRedeemOutputs(uint256 _amount)\n internal\n view\n virtual\n returns (uint256[] memory outputs)\n {\n // We always give out coins in proportion to how many we have,\n // Now if all coins were the same value, this math would easy,\n // just take the percentage of each coin, and multiply by the\n // value to be given out. But if coins are worth more than $1,\n // then we would end up handing out too many coins. We need to\n // adjust by the total value of coins.\n //\n // To do this, we total up the value of our coins, by their\n // percentages. Then divide what we would otherwise give out by\n // this number.\n //\n // Let say we have 100 DAI at $1.06 and 200 USDT at $1.00.\n // So for every 1 DAI we give out, we'll be handing out 2 USDT\n // Our total output ratio is: 33% * 1.06 + 66% * 1.00 = 1.02\n //\n // So when calculating the output, we take the percentage of\n // each coin, times the desired output value, divided by the\n // totalOutputRatio.\n //\n // For example, withdrawing: 30 OUSD:\n // DAI 33% * 30 / 1.02 = 9.80 DAI\n // USDT = 66 % * 30 / 1.02 = 19.60 USDT\n //\n // Checking these numbers:\n // 9.80 DAI * 1.06 = $10.40\n // 19.60 USDT * 1.00 = $19.60\n //\n // And so the user gets $10.40 + $19.60 = $30 worth of value.\n\n uint256 assetCount = allAssets.length;\n uint256[] memory assetUnits = new uint256[](assetCount);\n uint256[] memory assetBalances = new uint256[](assetCount);\n outputs = new uint256[](assetCount);\n\n // Calculate redeem fee\n if (redeemFeeBps > 0) {\n uint256 redeemFee = _amount.mulTruncateScale(redeemFeeBps, 1e4);\n _amount = _amount - redeemFee;\n }\n\n // Calculate assets balances and decimals once,\n // for a large gas savings.\n uint256 totalUnits = 0;\n for (uint256 i = 0; i < assetCount; ++i) {\n address assetAddr = allAssets[i];\n uint256 balance = _checkBalance(assetAddr);\n assetBalances[i] = balance;\n assetUnits[i] = _toUnits(balance, assetAddr);\n totalUnits = totalUnits + assetUnits[i];\n }\n // Calculate totalOutputRatio\n uint256 totalOutputRatio = 0;\n for (uint256 i = 0; i < assetCount; ++i) {\n uint256 unitPrice = _toUnitPrice(allAssets[i], false);\n uint256 ratio = (assetUnits[i] * unitPrice) / totalUnits;\n totalOutputRatio = totalOutputRatio + ratio;\n }\n // Calculate final outputs\n uint256 factor = _amount.divPrecisely(totalOutputRatio);\n for (uint256 i = 0; i < assetCount; ++i) {\n outputs[i] = (assetBalances[i] * factor) / totalUnits;\n }\n }\n\n /***************************************\n Pricing\n ****************************************/\n\n /**\n * @notice Returns the total price in 18 digit units for a given asset.\n * Never goes above 1, since that is how we price mints.\n * @param asset address of the asset\n * @return price uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed\n */\n function priceUnitMint(address asset)\n external\n view\n returns (uint256 price)\n {\n /* need to supply 1 asset unit in asset's decimals and can not just hard-code\n * to 1e18 and ignore calling `_toUnits` since we need to consider assets\n * with the exchange rate\n */\n uint256 units = _toUnits(\n uint256(1e18).scaleBy(_getDecimals(asset), 18),\n asset\n );\n price = (_toUnitPrice(asset, true) * units) / 1e18;\n }\n\n /**\n * @notice Returns the total price in 18 digit unit for a given asset.\n * Never goes below 1, since that is how we price redeems\n * @param asset Address of the asset\n * @return price uint256: unit (USD / ETH) price for 1 unit of the asset, in 18 decimal fixed\n */\n function priceUnitRedeem(address asset)\n external\n view\n returns (uint256 price)\n {\n /* need to supply 1 asset unit in asset's decimals and can not just hard-code\n * to 1e18 and ignore calling `_toUnits` since we need to consider assets\n * with the exchange rate\n */\n uint256 units = _toUnits(\n uint256(1e18).scaleBy(_getDecimals(asset), 18),\n asset\n );\n price = (_toUnitPrice(asset, false) * units) / 1e18;\n }\n\n /***************************************\n Utils\n ****************************************/\n\n /**\n * @dev Convert a quantity of a token into 1e18 fixed decimal \"units\"\n * in the underlying base (USD/ETH) used by the vault.\n * Price is not taken into account, only quantity.\n *\n * Examples of this conversion:\n *\n * - 1e18 DAI becomes 1e18 units (same decimals)\n * - 1e6 USDC becomes 1e18 units (decimal conversion)\n * - 1e18 rETH becomes 1.2e18 units (exchange rate conversion)\n *\n * @param _raw Quantity of asset\n * @param _asset Core Asset address\n * @return value 1e18 normalized quantity of units\n */\n function _toUnits(uint256 _raw, address _asset)\n internal\n view\n returns (uint256)\n {\n UnitConversion conversion = assets[_asset].unitConversion;\n if (conversion == UnitConversion.DECIMALS) {\n return _raw.scaleBy(18, _getDecimals(_asset));\n } else if (conversion == UnitConversion.GETEXCHANGERATE) {\n uint256 exchangeRate = IGetExchangeRateToken(_asset)\n .getExchangeRate();\n return (_raw * exchangeRate) / 1e18;\n } else {\n revert(\"Unsupported conversion type\");\n }\n }\n\n /**\n * @dev Returns asset's unit price accounting for different asset types\n * and takes into account the context in which that price exists -\n * - mint or redeem.\n *\n * Note: since we are returning the price of the unit and not the one of the\n * asset (see comment above how 1 rETH exchanges for 1.2 units) we need\n * to make the Oracle price adjustment as well since we are pricing the\n * units and not the assets.\n *\n * The price also snaps to a \"full unit price\" in case a mint or redeem\n * action would be unfavourable to the protocol.\n *\n */\n function _toUnitPrice(address _asset, bool isMint)\n internal\n view\n returns (uint256 price)\n {\n UnitConversion conversion = assets[_asset].unitConversion;\n price = IOracle(priceProvider).price(_asset);\n\n if (conversion == UnitConversion.GETEXCHANGERATE) {\n uint256 exchangeRate = IGetExchangeRateToken(_asset)\n .getExchangeRate();\n price = (price * 1e18) / exchangeRate;\n } else if (conversion != UnitConversion.DECIMALS) {\n revert(\"Unsupported conversion type\");\n }\n\n /* At this stage the price is already adjusted to the unit\n * so the price checks are agnostic to underlying asset being\n * pegged to a USD or to an ETH or having a custom exchange rate.\n */\n require(price <= MAX_UNIT_PRICE_DRIFT, \"Vault: Price exceeds max\");\n require(price >= MIN_UNIT_PRICE_DRIFT, \"Vault: Price under min\");\n\n if (isMint) {\n /* Never price a normalized unit price for more than one\n * unit of OETH/OUSD when minting.\n */\n if (price > 1e18) {\n price = 1e18;\n }\n require(price >= MINT_MINIMUM_UNIT_PRICE, \"Asset price below peg\");\n } else {\n /* Never give out more than 1 normalized unit amount of assets\n * for one unit of OETH/OUSD when redeeming.\n */\n if (price < 1e18) {\n price = 1e18;\n }\n }\n }\n\n /**\n * @dev Get the number of decimals of a token asset\n * @param _asset Address of the asset\n * @return decimals number of decimals\n */\n function _getDecimals(address _asset)\n internal\n view\n returns (uint256 decimals)\n {\n decimals = assets[_asset].decimals;\n require(decimals > 0, \"Decimals not cached\");\n }\n\n /**\n * @notice Return the number of assets supported by the Vault.\n */\n function getAssetCount() public view returns (uint256) {\n return allAssets.length;\n }\n\n /**\n * @notice Gets the vault configuration of a supported asset.\n * @param _asset Address of the token asset\n */\n function getAssetConfig(address _asset)\n public\n view\n returns (Asset memory config)\n {\n config = assets[_asset];\n }\n\n /**\n * @notice Return all vault asset addresses in order\n */\n function getAllAssets() external view returns (address[] memory) {\n return allAssets;\n }\n\n /**\n * @notice Return the number of strategies active on the Vault.\n */\n function getStrategyCount() external view returns (uint256) {\n return allStrategies.length;\n }\n\n /**\n * @notice Return the array of all strategies\n */\n function getAllStrategies() external view returns (address[] memory) {\n return allStrategies;\n }\n\n /**\n * @notice Returns whether the vault supports the asset\n * @param _asset address of the asset\n * @return true if supported\n */\n function isSupportedAsset(address _asset) external view returns (bool) {\n return assets[_asset].isSupported;\n }\n\n function ADMIN_IMPLEMENTATION() external view returns (address adminImpl) {\n bytes32 slot = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n adminImpl := sload(slot)\n }\n }\n\n /**\n * @dev Falldown to the admin implementation\n * @notice This is a catch all for all functions not declared in core\n */\n // solhint-disable-next-line no-complex-fallback\n fallback() external {\n bytes32 slot = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(\n gas(),\n sload(slot),\n 0,\n calldatasize(),\n 0,\n 0\n )\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n function abs(int256 x) private pure returns (uint256) {\n require(x < int256(MAX_INT), \"Amount too high\");\n return x >= 0 ? uint256(x) : uint256(-x);\n }\n\n function _min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n function _max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a > b ? a : b;\n }\n}\n" + }, + "contracts/vault/VaultInitializer.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultInitializer contract\n * @notice The Vault contract initializes the vault.\n * @author Origin Protocol Inc\n */\n\nimport \"./VaultStorage.sol\";\n\ncontract VaultInitializer is VaultStorage {\n function initialize(address _priceProvider, address _oToken)\n external\n onlyGovernor\n initializer\n {\n require(_priceProvider != address(0), \"PriceProvider address is zero\");\n require(_oToken != address(0), \"oToken address is zero\");\n\n oUSD = OUSD(_oToken);\n\n priceProvider = _priceProvider;\n\n rebasePaused = false;\n capitalPaused = true;\n\n // Initial redeem fee of 0 basis points\n redeemFeeBps = 0;\n // Initial Vault buffer of 0%\n vaultBuffer = 0;\n // Initial allocate threshold of 25,000 OUSD\n autoAllocateThreshold = 25000e18;\n // Threshold for rebasing\n rebaseThreshold = 1000e18;\n // Initialize all strategies\n allStrategies = new address[](0);\n // Start with drip duration disabled\n dripDuration = 1;\n }\n}\n" + }, + "contracts/vault/VaultStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\n/**\n * @title OToken VaultStorage contract\n * @notice The VaultStorage contract defines the storage for the Vault contracts\n * @author Origin Protocol Inc\n */\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\nimport { IStrategy } from \"../interfaces/IStrategy.sol\";\nimport { Governable } from \"../governance/Governable.sol\";\nimport { OUSD } from \"../token/OUSD.sol\";\nimport { Initializable } from \"../utils/Initializable.sol\";\nimport \"../utils/Helpers.sol\";\n\ncontract VaultStorage is Initializable, Governable {\n using SafeERC20 for IERC20;\n\n event AssetSupported(address _asset);\n event AssetRemoved(address _asset);\n event AssetDefaultStrategyUpdated(address _asset, address _strategy);\n event AssetAllocated(address _asset, address _strategy, uint256 _amount);\n event StrategyApproved(address _addr);\n event StrategyRemoved(address _addr);\n event Mint(address _addr, uint256 _value);\n event Redeem(address _addr, uint256 _value);\n event CapitalPaused();\n event CapitalUnpaused();\n event RebasePaused();\n event RebaseUnpaused();\n event VaultBufferUpdated(uint256 _vaultBuffer);\n event OusdMetaStrategyUpdated(address _ousdMetaStrategy);\n event RedeemFeeUpdated(uint256 _redeemFeeBps);\n event PriceProviderUpdated(address _priceProvider);\n event AllocateThresholdUpdated(uint256 _threshold);\n event RebaseThresholdUpdated(uint256 _threshold);\n event StrategistUpdated(address _address);\n event MaxSupplyDiffChanged(uint256 maxSupplyDiff);\n event YieldDistribution(address _to, uint256 _yield, uint256 _fee);\n event TrusteeFeeBpsChanged(uint256 _basis);\n event TrusteeAddressChanged(address _address);\n event NetOusdMintForStrategyThresholdChanged(uint256 _threshold);\n event SwapperChanged(address _address);\n event SwapAllowedUndervalueChanged(uint256 _basis);\n event SwapSlippageChanged(address _asset, uint256 _basis);\n event Swapped(\n address indexed _fromAsset,\n address indexed _toAsset,\n uint256 _fromAssetAmount,\n uint256 _toAssetAmount\n );\n event StrategyAddedToMintWhitelist(address indexed strategy);\n event StrategyRemovedFromMintWhitelist(address indexed strategy);\n event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);\n event DripDurationChanged(uint256 dripDuration);\n event WithdrawalRequested(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount,\n uint256 _queued\n );\n event WithdrawalClaimed(\n address indexed _withdrawer,\n uint256 indexed _requestId,\n uint256 _amount\n );\n event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable);\n event WithdrawalClaimDelayUpdated(uint256 _newDelay);\n\n // Since we are proxy, all state should be uninitalized.\n // Since this storage contract does not have logic directly on it\n // we should not be checking for to see if these variables can be constant.\n // slither-disable-start uninitialized-state\n // slither-disable-start constable-states\n\n // Assets supported by the Vault, i.e. Stablecoins\n enum UnitConversion {\n DECIMALS,\n GETEXCHANGERATE\n }\n // Changed to fit into a single storage slot so the decimals needs to be recached\n struct Asset {\n // Note: OETHVaultCore doesn't use `isSupported` when minting,\n // redeeming or checking balance of assets.\n bool isSupported;\n UnitConversion unitConversion;\n uint8 decimals;\n // Max allowed slippage from the Oracle price when swapping collateral assets in basis points.\n // For example 40 == 0.4% slippage\n uint16 allowedOracleSlippageBps;\n }\n\n /// @dev mapping of supported vault assets to their configuration\n mapping(address => Asset) internal assets;\n /// @dev list of all assets supported by the vault.\n address[] internal allAssets;\n\n // Strategies approved for use by the Vault\n struct Strategy {\n bool isSupported;\n uint256 _deprecated; // Deprecated storage slot\n }\n /// @dev mapping of strategy contracts to their configuration\n mapping(address => Strategy) public strategies;\n /// @dev list of all vault strategies\n address[] internal allStrategies;\n\n /// @notice Address of the Oracle price provider contract\n address public priceProvider;\n /// @notice pause rebasing if true\n bool public rebasePaused;\n /// @notice pause operations that change the OToken supply.\n /// eg mint, redeem, allocate, mint/burn for strategy\n bool public capitalPaused;\n /// @notice Redemption fee in basis points. eg 50 = 0.5%\n uint256 public redeemFeeBps;\n /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18.\n uint256 public vaultBuffer;\n /// @notice OToken mints over this amount automatically allocate funds. 18 decimals.\n uint256 public autoAllocateThreshold;\n /// @notice OToken mints over this amount automatically rebase. 18 decimals.\n uint256 public rebaseThreshold;\n\n /// @dev Address of the OToken token. eg OUSD or OETH.\n OUSD public oUSD;\n\n /// @dev Storage slot for the address of the VaultAdmin contract that is delegated to\n // keccak256(\"OUSD.vault.governor.admin.impl\");\n bytes32 public constant adminImplPosition =\n 0xa2bd3d3cf188a41358c8b401076eb59066b09dec5775650c0de4c55187d17bd9;\n\n /// @dev Address of the contract responsible for post rebase syncs with AMMs\n address private _deprecated_rebaseHooksAddr = address(0);\n\n /// @dev Deprecated: Address of Uniswap\n address private _deprecated_uniswapAddr = address(0);\n\n /// @notice Address of the Strategist\n address public strategistAddr = address(0);\n\n /// @notice Mapping of asset address to the Strategy that they should automatically\n // be allocated to\n mapping(address => address) public assetDefaultStrategies;\n\n /// @notice Max difference between total supply and total value of assets. 18 decimals.\n uint256 public maxSupplyDiff;\n\n /// @notice Trustee contract that can collect a percentage of yield\n address public trusteeAddress;\n\n /// @notice Amount of yield collected in basis points. eg 2000 = 20%\n uint256 public trusteeFeeBps;\n\n /// @dev Deprecated: Tokens that should be swapped for stablecoins\n address[] private _deprecated_swapTokens;\n\n uint256 constant MINT_MINIMUM_UNIT_PRICE = 0.998e18;\n\n /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral\n\n address public ousdMetaStrategy;\n\n /// @notice How much OTokens are currently minted by the strategy\n int256 public netOusdMintedForStrategy;\n\n /// @notice How much net total OTokens are allowed to be minted by all strategies\n uint256 public netOusdMintForStrategyThreshold;\n\n uint256 constant MIN_UNIT_PRICE_DRIFT = 0.7e18;\n uint256 constant MAX_UNIT_PRICE_DRIFT = 1.3e18;\n\n /// @notice Collateral swap configuration.\n /// @dev is packed into a single storage slot to save gas.\n struct SwapConfig {\n // Contract that swaps the vault's collateral assets\n address swapper;\n // Max allowed percentage the total value can drop below the total supply in basis points.\n // For example 100 == 1%\n uint16 allowedUndervalueBps;\n }\n SwapConfig internal swapConfig = SwapConfig(address(0), 0);\n\n // List of strategies that can mint oTokens directly\n // Used in OETHBaseVaultCore\n mapping(address => bool) public isMintWhitelistedStrategy;\n\n /// @notice Address of the Dripper contract that streams harvested rewards to the Vault\n /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract.\n address private _deprecated_dripper;\n\n /// Withdrawal Queue Storage /////\n\n struct WithdrawalQueueMetadata {\n // cumulative total of all withdrawal requests included the ones that have already been claimed\n uint128 queued;\n // cumulative total of all the requests that can be claimed including the ones that have already been claimed\n uint128 claimable;\n // total of all the requests that have been claimed\n uint128 claimed;\n // index of the next withdrawal request starting at 0\n uint128 nextWithdrawalIndex;\n }\n\n /// @notice Global metadata for the withdrawal queue including:\n /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed\n /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed\n /// claimed - total of all the requests that have been claimed\n /// nextWithdrawalIndex - index of the next withdrawal request starting at 0\n WithdrawalQueueMetadata public withdrawalQueueMetadata;\n\n struct WithdrawalRequest {\n address withdrawer;\n bool claimed;\n uint40 timestamp; // timestamp of the withdrawal request\n // Amount of oTokens to redeem. eg OETH\n uint128 amount;\n // cumulative total of all withdrawal requests including this one.\n // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount.\n uint128 queued;\n }\n\n /// @notice Mapping of withdrawal request indices to the user withdrawal request data\n mapping(uint256 => WithdrawalRequest) public withdrawalRequests;\n\n /// @notice Sets a minimum delay that is required to elapse between\n /// requesting async withdrawals and claiming the request.\n /// When set to 0 async withdrawals are disabled.\n uint256 public withdrawalClaimDelay;\n\n /// @notice Time in seconds that the vault last rebased yield.\n uint64 public lastRebase;\n\n /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable.\n uint64 public dripDuration;\n\n /// @notice max rebase percentage per second\n /// Can be used to set maximum yield of the protocol,\n /// spreading out yield over time\n uint64 public rebasePerSecondMax;\n\n /// @notice target rebase rate limit, based on past rates and funds available.\n uint64 public rebasePerSecondTarget;\n\n uint256 internal constant MAX_REBASE = 0.02 ether;\n uint256 internal constant MAX_REBASE_PER_SECOND =\n uint256(0.05 ether) / 1 days;\n\n // For future use\n uint256[43] private __gap;\n\n // slither-disable-end constable-states\n // slither-disable-end uninitialized-state\n\n /**\n * @notice set the implementation for the admin, this needs to be in a base class else we cannot set it\n * @param newImpl address of the implementation\n */\n function setAdminImpl(address newImpl) external onlyGovernor {\n require(\n Address.isContract(newImpl),\n \"new implementation is not a contract\"\n );\n bytes32 position = adminImplPosition;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(position, newImpl)\n }\n }\n}\n" + }, + "contracts/zapper/WOETHCCIPZapper.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.0;\n\nimport { IRouterClient } from \"@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol\";\nimport { Client } from \"@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol\";\n// solhint-disable-next-line max-line-length\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC4626 } from \"./../../lib/openzeppelin/interfaces/IERC4626.sol\";\nimport { IOETHZapper } from \"./../interfaces/IOETHZapper.sol\";\n\n/**\n * @title WOETH CCIP Zapper Contract\n * @notice Helps to directly convert ETH on mainnet into WOETH on L2s.\n * @author Origin Protocol Inc\n */\n\ncontract WOETHCCIPZapper {\n /**\n * @dev Event emitted when a zap occurs\n * @param messageId Unique message identifier for each zap\n * @param sender Address initiating the zap\n * @param recipient Recipient address at destination chain\n * @param amount Amount of ETH zapped\n */\n event Zap(\n bytes32 indexed messageId,\n address sender,\n address recipient,\n uint256 amount\n );\n\n // @dev Thrown when Zap amount is less than fee.\n error AmountLessThanFee();\n\n /**\n * @dev The destination chain selector\n */\n uint64 public immutable destinationChainSelector;\n\n /**\n * @dev The WOETH source chain (Mainnet)\n */\n IERC4626 public immutable woethOnSourceChain;\n\n /**\n * @dev The WOETH destination chain (Arbitrum)\n */\n IERC20 public immutable woethOnDestinationChain;\n\n /**\n * @dev The OETH zapper contract address\n */\n IOETHZapper public immutable oethZapper;\n\n /**\n * @dev The CCIP router contract address\n */\n IRouterClient public immutable ccipRouter;\n\n /**\n * @dev The OETH token contract address\n */\n IERC20 public immutable oeth;\n\n constructor(\n address _ccipRouter,\n uint64 _destinationChainSelector,\n IERC4626 _woethOnSourceChain,\n IERC20 _woethOnDestinationChain,\n IOETHZapper _oethZapper,\n IERC20 _oeth\n ) {\n ccipRouter = IRouterClient(_ccipRouter);\n destinationChainSelector = _destinationChainSelector;\n woethOnSourceChain = _woethOnSourceChain;\n woethOnDestinationChain = _woethOnDestinationChain;\n oethZapper = _oethZapper;\n oeth = _oeth;\n\n // Max allowance for Router and WOETH contracts\n _oeth.approve(address(_woethOnSourceChain), type(uint256).max); // for wrapping\n _woethOnSourceChain.approve(address(_ccipRouter), type(uint256).max); // for zapping\n }\n\n /**\n * @notice Accepts ETH, zaps for OETH, wraps it for WOETH and sends it to the destination chain (arbitrum)\n * @param receiver The address of the EOA on the destination chain\n * @return messageId The ID of the message that was sent\n */\n function zap(address receiver)\n external\n payable\n returns (bytes32 messageId)\n {\n return _zap(receiver, msg.value);\n }\n\n /**\n * @notice Used to estimate fee for CCIP transaction\n * @param amount The amount of ETH to be zapped\n * @param receiver The address of the EOA on the destination chain\n * @return feeAmount The CCIP tx fee in ETH.\n */\n\n function getFee(uint256 amount, address receiver)\n public\n view\n returns (uint256 feeAmount)\n {\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(woethOnSourceChain),\n amount: amount\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({\n receiver: abi.encode(receiver), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n feeAmount = ccipRouter.getFee(destinationChainSelector, message);\n }\n\n /**\n * @dev Deposit ETH and receive WOETH in L2.\n * @dev Note that the WOETH will be sent to the msg.sender at destination chain as well.\n */\n receive() external payable {\n _zap(msg.sender, msg.value);\n }\n\n function _zap(address receiver, uint256 amount)\n internal\n returns (bytes32 messageId)\n {\n // Estimate fee for zapping.\n uint256 feeAmount = getFee(amount, receiver);\n if (amount < feeAmount) {\n revert AmountLessThanFee();\n }\n\n // Convert only the msg.value - fees amount to WOETH.\n amount -= feeAmount;\n\n // 1.) Zap for OETH\n uint256 oethReceived = oethZapper.deposit{ value: amount }();\n\n // 2.) Wrap the received woeth\n uint256 woethReceived = woethOnSourceChain.deposit(\n oethReceived,\n address(this)\n );\n\n // 3.) Setup params for CCIP transfer\n\n Client.EVMTokenAmount[]\n memory tokenAmounts = new Client.EVMTokenAmount[](1);\n Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({\n token: address(woethOnSourceChain),\n amount: woethReceived\n });\n tokenAmounts[0] = tokenAmount;\n\n Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({\n receiver: abi.encode(receiver), // ABI-encoded receiver address\n data: abi.encode(\"\"),\n tokenAmounts: tokenAmounts,\n extraArgs: Client._argsToBytes(\n // See: https://docs.chain.link/ccip/best-practices#setting-gaslimit\n Client.EVMExtraArgsV1({ gasLimit: 0 })\n ),\n feeToken: address(0)\n });\n\n // ZAP ϟ\n //slither-disable-next-line arbitrary-send-eth\n messageId = ccipRouter.ccipSend{ value: feeAmount }(\n destinationChainSelector,\n message\n );\n\n // Emit Zap event with message details\n emit Zap(messageId, msg.sender, receiver, amount);\n\n // Return the message ID\n return messageId;\n }\n}\n" + }, + "hardhat/console.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.4.22 <0.9.0;\n\nlibrary console {\n address constant CONSOLE_ADDRESS =\n 0x000000000000000000636F6e736F6c652e6c6f67;\n\n function _sendLogPayloadImplementation(bytes memory payload) internal view {\n address consoleAddress = CONSOLE_ADDRESS;\n /// @solidity memory-safe-assembly\n assembly {\n pop(\n staticcall(\n gas(),\n consoleAddress,\n add(payload, 32),\n mload(payload),\n 0,\n 0\n )\n )\n }\n }\n\n function _castToPure(\n function(bytes memory) internal view fnIn\n ) internal pure returns (function(bytes memory) pure fnOut) {\n assembly {\n fnOut := fnIn\n }\n }\n\n function _sendLogPayload(bytes memory payload) internal pure {\n _castToPure(_sendLogPayloadImplementation)(payload);\n }\n\n function log() internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log()\"));\n }\n\n function logInt(int256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(int256)\", p0));\n }\n\n function logUint(uint256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256)\", p0));\n }\n\n function logString(string memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n }\n\n function logBool(bool p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n }\n\n function logAddress(address p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n }\n\n function logBytes(bytes memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes)\", p0));\n }\n\n function logBytes1(bytes1 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes1)\", p0));\n }\n\n function logBytes2(bytes2 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes2)\", p0));\n }\n\n function logBytes3(bytes3 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes3)\", p0));\n }\n\n function logBytes4(bytes4 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes4)\", p0));\n }\n\n function logBytes5(bytes5 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes5)\", p0));\n }\n\n function logBytes6(bytes6 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes6)\", p0));\n }\n\n function logBytes7(bytes7 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes7)\", p0));\n }\n\n function logBytes8(bytes8 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes8)\", p0));\n }\n\n function logBytes9(bytes9 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes9)\", p0));\n }\n\n function logBytes10(bytes10 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes10)\", p0));\n }\n\n function logBytes11(bytes11 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes11)\", p0));\n }\n\n function logBytes12(bytes12 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes12)\", p0));\n }\n\n function logBytes13(bytes13 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes13)\", p0));\n }\n\n function logBytes14(bytes14 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes14)\", p0));\n }\n\n function logBytes15(bytes15 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes15)\", p0));\n }\n\n function logBytes16(bytes16 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes16)\", p0));\n }\n\n function logBytes17(bytes17 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes17)\", p0));\n }\n\n function logBytes18(bytes18 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes18)\", p0));\n }\n\n function logBytes19(bytes19 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes19)\", p0));\n }\n\n function logBytes20(bytes20 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes20)\", p0));\n }\n\n function logBytes21(bytes21 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes21)\", p0));\n }\n\n function logBytes22(bytes22 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes22)\", p0));\n }\n\n function logBytes23(bytes23 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes23)\", p0));\n }\n\n function logBytes24(bytes24 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes24)\", p0));\n }\n\n function logBytes25(bytes25 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes25)\", p0));\n }\n\n function logBytes26(bytes26 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes26)\", p0));\n }\n\n function logBytes27(bytes27 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes27)\", p0));\n }\n\n function logBytes28(bytes28 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes28)\", p0));\n }\n\n function logBytes29(bytes29 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes29)\", p0));\n }\n\n function logBytes30(bytes30 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes30)\", p0));\n }\n\n function logBytes31(bytes31 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes31)\", p0));\n }\n\n function logBytes32(bytes32 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bytes32)\", p0));\n }\n\n function log(uint256 p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256)\", p0));\n }\n\n function log(string memory p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n }\n\n function log(bool p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n }\n\n function log(address p0) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n }\n\n function log(uint256 p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256)\", p0, p1));\n }\n\n function log(uint256 p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string)\", p0, p1));\n }\n\n function log(uint256 p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool)\", p0, p1));\n }\n\n function log(uint256 p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address)\", p0, p1));\n }\n\n function log(string memory p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256)\", p0, p1));\n }\n\n function log(string memory p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string)\", p0, p1));\n }\n\n function log(string memory p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool)\", p0, p1));\n }\n\n function log(string memory p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address)\", p0, p1));\n }\n\n function log(bool p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256)\", p0, p1));\n }\n\n function log(bool p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string)\", p0, p1));\n }\n\n function log(bool p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool)\", p0, p1));\n }\n\n function log(bool p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address)\", p0, p1));\n }\n\n function log(address p0, uint256 p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256)\", p0, p1));\n }\n\n function log(address p0, string memory p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string)\", p0, p1));\n }\n\n function log(address p0, bool p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool)\", p0, p1));\n }\n\n function log(address p0, address p1) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address)\", p0, p1));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool)\", p0, p1, p2));\n }\n\n function log(uint256 p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool)\", p0, p1, p2));\n }\n\n function log(string memory p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool)\", p0, p1, p2));\n }\n\n function log(bool p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool)\", p0, p1, p2));\n }\n\n function log(address p0, uint256 p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool)\", p0, p1, p2));\n }\n\n function log(address p0, string memory p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool)\", p0, p1, p2));\n }\n\n function log(address p0, bool p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, uint256 p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, string memory p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, bool p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool)\", p0, p1, p2));\n }\n\n function log(address p0, address p1, address p2) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address)\", p0, p1, p2));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(uint256 p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(string memory p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(bool p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, uint256 p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, string memory p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, bool p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, uint256 p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, string memory p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, bool p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,address)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, uint256 p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,uint256)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, string memory p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,string)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, bool p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,bool)\", p0, p1, p2, p3));\n }\n\n function log(address p0, address p1, address p2, address p3) internal pure {\n _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,address)\", p0, p1, p2, p3));\n }\n}\n" + }, + "lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC4626 } from \"../../../../interfaces/IERC4626.sol\";\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\n// From Open Zeppelin draft PR commit:\n// fac43034dca85ff539db3fc8aa2a7084b843d454\n// https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3171\n\nabstract contract ERC4626 is ERC20, IERC4626 {\n IERC20Metadata private immutable _asset;\n\n constructor(IERC20Metadata __asset) {\n _asset = __asset;\n }\n\n /** @dev See {IERC4262-asset} */\n function asset() public view virtual override returns (address) {\n return address(_asset);\n }\n\n /** @dev See {IERC4262-totalAssets} */\n function totalAssets() public view virtual override returns (uint256) {\n return _asset.balanceOf(address(this));\n }\n\n /**\n * @dev See {IERC4262-convertToShares}\n *\n * Will revert if asserts > 0, totalSupply > 0 and totalAssets = 0. That corresponds to a case where any asset\n * would represent an infinite amout of shares.\n */\n function convertToShares(uint256 assets) public view virtual override returns (uint256 shares) {\n uint256 supply = totalSupply();\n\n return\n (assets == 0 || supply == 0)\n ? (assets * 10**decimals()) / 10**_asset.decimals()\n : (assets * supply) / totalAssets();\n }\n\n /** @dev See {IERC4262-convertToAssets} */\n function convertToAssets(uint256 shares) public view virtual override returns (uint256 assets) {\n uint256 supply = totalSupply();\n\n return (supply == 0) ? (shares * 10**_asset.decimals()) / 10**decimals() : (shares * totalAssets()) / supply;\n }\n\n /** @dev See {IERC4262-maxDeposit} */\n function maxDeposit(address) public view virtual override returns (uint256) {\n return type(uint256).max;\n }\n\n /** @dev See {IERC4262-maxMint} */\n function maxMint(address) public view virtual override returns (uint256) {\n return type(uint256).max;\n }\n\n /** @dev See {IERC4262-maxWithdraw} */\n function maxWithdraw(address owner) public view virtual override returns (uint256) {\n return convertToAssets(balanceOf(owner));\n }\n\n /** @dev See {IERC4262-maxRedeem} */\n function maxRedeem(address owner) public view virtual override returns (uint256) {\n return balanceOf(owner);\n }\n\n /** @dev See {IERC4262-previewDeposit} */\n function previewDeposit(uint256 assets) public view virtual override returns (uint256) {\n return convertToShares(assets);\n }\n\n /** @dev See {IERC4262-previewMint} */\n function previewMint(uint256 shares) public view virtual override returns (uint256) {\n uint256 assets = convertToAssets(shares);\n return assets + (convertToShares(assets) < shares ? 1 : 0);\n }\n\n /** @dev See {IERC4262-previewWithdraw} */\n function previewWithdraw(uint256 assets) public view virtual override returns (uint256) {\n uint256 shares = convertToShares(assets);\n return shares + (convertToAssets(shares) < assets ? 1 : 0);\n }\n\n /** @dev See {IERC4262-previewRedeem} */\n function previewRedeem(uint256 shares) public view virtual override returns (uint256) {\n return convertToAssets(shares);\n }\n\n /** @dev See {IERC4262-deposit} */\n function deposit(uint256 assets, address receiver) public virtual override returns (uint256) {\n require(assets <= maxDeposit(receiver), \"ERC4626: deposit more then max\");\n\n address caller = _msgSender();\n uint256 shares = previewDeposit(assets);\n\n // if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through\n // the tokensToSend hook, so we need to transfer before we mint to keep the invariants.\n SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);\n _mint(receiver, shares);\n\n emit Deposit(caller, receiver, assets, shares);\n\n return shares;\n }\n\n /** @dev See {IERC4262-mint} */\n function mint(uint256 shares, address receiver) public virtual override returns (uint256) {\n require(shares <= maxMint(receiver), \"ERC4626: mint more then max\");\n\n address caller = _msgSender();\n uint256 assets = previewMint(shares);\n\n // if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through\n // the tokensToSend hook, so we need to transfer before we mint to keep the invariants.\n SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);\n _mint(receiver, shares);\n\n emit Deposit(caller, receiver, assets, shares);\n\n return assets;\n }\n\n /** @dev See {IERC4262-withdraw} */\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) public virtual override returns (uint256) {\n require(assets <= maxWithdraw(owner), \"ERC4626: withdraw more then max\");\n\n address caller = _msgSender();\n uint256 shares = previewWithdraw(assets);\n\n if (caller != owner) {\n _spendAllowance(owner, caller, shares);\n }\n\n // if _asset is ERC777, transfer can call reenter AFTER the transfer happens through\n // the tokensReceived hook, so we need to transfer after we burn to keep the invariants.\n _burn(owner, shares);\n SafeERC20.safeTransfer(_asset, receiver, assets);\n\n emit Withdraw(caller, receiver, owner, assets, shares);\n\n return shares;\n }\n\n /** @dev See {IERC4262-redeem} */\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) public virtual override returns (uint256) {\n require(shares <= maxRedeem(owner), \"ERC4626: redeem more then max\");\n\n address caller = _msgSender();\n uint256 assets = previewRedeem(shares);\n\n if (caller != owner) {\n _spendAllowance(owner, caller, shares);\n }\n\n // if _asset is ERC777, transfer can call reenter AFTER the transfer happens through\n // the tokensReceived hook, so we need to transfer after we burn to keep the invariants.\n _burn(owner, shares);\n SafeERC20.safeTransfer(_asset, receiver, assets);\n\n emit Withdraw(caller, receiver, owner, assets, shares);\n\n return assets;\n }\n\n // Included here, since this method was not yet present in\n // the version of Open Zeppelin ERC20 code we use.\n function _spendAllowance(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n uint256 currentAllowance = allowance(owner, spender);\n if (currentAllowance != type(uint256).max) {\n require(currentAllowance >= amount, \"ERC20: insufficient allowance\");\n unchecked {\n _approve(owner, spender, currentAllowance - amount);\n }\n }\n }\n}" + }, + "lib/openzeppelin/interfaces/IERC4626.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IERC4626 is IERC20, IERC20Metadata {\n event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);\n\n event Withdraw(\n address indexed caller,\n address indexed receiver,\n address indexed owner,\n uint256 assets,\n uint256 shares\n );\n\n /**\n * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.\n *\n * - MUST be an ERC-20 token contract.\n * - MUST NOT revert.\n */\n function asset() external view returns (address assetTokenAddress);\n\n /**\n * @dev Returns the total amount of the underlying asset that is “managed” by Vault.\n *\n * - SHOULD include any compounding that occurs from yield.\n * - MUST be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT revert.\n */\n function totalAssets() external view returns (uint256 totalManagedAssets);\n\n /**\n * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal\n * scenario where all the conditions are met.\n *\n * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT show any variations depending on the caller.\n * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n * - MUST NOT revert.\n *\n * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n * from.\n */\n function convertToShares(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal\n * scenario where all the conditions are met.\n *\n * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n * - MUST NOT show any variations depending on the caller.\n * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n * - MUST NOT revert.\n *\n * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n * from.\n */\n function convertToAssets(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,\n * through a deposit call.\n *\n * - MUST return a limited value if receiver is subject to some deposit limit.\n * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.\n * - MUST NOT revert.\n */\n function maxDeposit(address receiver) external view returns (uint256 maxAssets);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given\n * current on-chain conditions.\n *\n * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit\n * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called\n * in the same transaction.\n * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the\n * deposit would be accepted, regardless if the user has enough tokens approved, etc.\n * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by depositing.\n */\n function previewDeposit(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.\n *\n * - MUST emit the Deposit event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * deposit execution, and are accounted for during deposit.\n * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not\n * approving enough underlying tokens to the Vault contract, etc).\n *\n * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n */\n function deposit(uint256 assets, address receiver) external returns (uint256 shares);\n\n /**\n * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.\n * - MUST return a limited value if receiver is subject to some mint limit.\n * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.\n * - MUST NOT revert.\n */\n function maxMint(address receiver) external view returns (uint256 maxShares);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given\n * current on-chain conditions.\n *\n * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call\n * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the\n * same transaction.\n * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint\n * would be accepted, regardless if the user has enough tokens approved, etc.\n * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by minting.\n */\n function previewMint(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.\n *\n * - MUST emit the Deposit event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint\n * execution, and are accounted for during mint.\n * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not\n * approving enough underlying tokens to the Vault contract, etc).\n *\n * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n */\n function mint(uint256 shares, address receiver) external returns (uint256 assets);\n\n /**\n * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the\n * Vault, through a withdraw call.\n *\n * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n * - MUST NOT revert.\n */\n function maxWithdraw(address owner) external view returns (uint256 maxAssets);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,\n * given current on-chain conditions.\n *\n * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw\n * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if\n * called\n * in the same transaction.\n * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though\n * the withdrawal would be accepted, regardless if the user has enough shares, etc.\n * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by depositing.\n */\n function previewWithdraw(uint256 assets) external view returns (uint256 shares);\n\n /**\n * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.\n *\n * - MUST emit the Withdraw event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * withdraw execution, and are accounted for during withdraw.\n * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner\n * not having enough shares, etc).\n *\n * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n * Those methods should be performed separately.\n */\n function withdraw(\n uint256 assets,\n address receiver,\n address owner\n ) external returns (uint256 shares);\n\n /**\n * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,\n * through a redeem call.\n *\n * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.\n * - MUST NOT revert.\n */\n function maxRedeem(address owner) external view returns (uint256 maxShares);\n\n /**\n * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,\n * given current on-chain conditions.\n *\n * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call\n * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the\n * same transaction.\n * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the\n * redemption would be accepted, regardless if the user has enough shares, etc.\n * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n * - MUST NOT revert.\n *\n * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in\n * share price or some other type of condition, meaning the depositor will lose assets by redeeming.\n */\n function previewRedeem(uint256 shares) external view returns (uint256 assets);\n\n /**\n * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.\n *\n * - MUST emit the Withdraw event.\n * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n * redeem execution, and are accounted for during redeem.\n * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner\n * not having enough shares, etc).\n *\n * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n * Those methods should be performed separately.\n */\n function redeem(\n uint256 shares,\n address receiver,\n address owner\n ) external returns (uint256 assets);\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.20;\n\nimport {Panic} from \"../Panic.sol\";\nimport {SafeCast} from \"./SafeCast.sol\";\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n enum Rounding {\n Floor, // Toward negative infinity\n Ceil, // Toward positive infinity\n Trunc, // Toward zero\n Expand // Away from zero\n }\n\n /**\n * @dev Return the 512-bit addition of two uint256.\n *\n * The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low.\n */\n function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {\n assembly (\"memory-safe\") {\n low := add(a, b)\n high := lt(low, a)\n }\n }\n\n /**\n * @dev Return the 512-bit multiplication of two uint256.\n *\n * The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low.\n */\n function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {\n // 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use\n // the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n // variables such that product = high * 2²⁵⁶ + low.\n assembly (\"memory-safe\") {\n let mm := mulmod(a, b, not(0))\n low := mul(a, b)\n high := sub(sub(mm, low), lt(mm, low))\n }\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, with a success flag (no overflow).\n */\n function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a + b;\n success = c >= a;\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).\n */\n function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a - b;\n success = c <= a;\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).\n */\n function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n uint256 c = a * b;\n assembly (\"memory-safe\") {\n // Only true when the multiplication doesn't overflow\n // (c / a == b) || (a == 0)\n success := or(eq(div(c, a), b), iszero(a))\n }\n // equivalent to: success ? c : 0\n result = c * SafeCast.toUint(success);\n }\n }\n\n /**\n * @dev Returns the division of two unsigned integers, with a success flag (no division by zero).\n */\n function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n success = b > 0;\n assembly (\"memory-safe\") {\n // The `DIV` opcode returns zero when the denominator is 0.\n result := div(a, b)\n }\n }\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).\n */\n function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n unchecked {\n success = b > 0;\n assembly (\"memory-safe\") {\n // The `MOD` opcode returns zero when the denominator is 0.\n result := mod(a, b)\n }\n }\n }\n\n /**\n * @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing.\n */\n function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {\n (bool success, uint256 result) = tryAdd(a, b);\n return ternary(success, result, type(uint256).max);\n }\n\n /**\n * @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.\n */\n function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {\n (, uint256 result) = trySub(a, b);\n return result;\n }\n\n /**\n * @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing.\n */\n function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {\n (bool success, uint256 result) = tryMul(a, b);\n return ternary(success, result, type(uint256).max);\n }\n\n /**\n * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.\n *\n * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.\n * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute\n * one branch when needed, making this function more expensive.\n */\n function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {\n unchecked {\n // branchless ternary works because:\n // b ^ (a ^ b) == a\n // b ^ 0 == b\n return b ^ ((a ^ b) * SafeCast.toUint(condition));\n }\n }\n\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return ternary(a > b, a, b);\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return ternary(a < b, a, b);\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds towards infinity instead\n * of rounding towards zero.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n if (b == 0) {\n // Guarantee the same behavior as in a regular Solidity division.\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n\n // The following calculation ensures accurate ceiling division without overflow.\n // Since a is non-zero, (a - 1) / b will not overflow.\n // The largest possible result occurs when (a - 1) / b is type(uint256).max,\n // but the largest value we can obtain is type(uint256).max - 1, which happens\n // when a = type(uint256).max and b = 1.\n unchecked {\n return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);\n }\n }\n\n /**\n * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or\n * denominator == 0.\n *\n * Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by\n * Uniswap Labs also under MIT license.\n */\n function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {\n unchecked {\n (uint256 high, uint256 low) = mul512(x, y);\n\n // Handle non-overflow cases, 256 by 256 division.\n if (high == 0) {\n // Solidity will revert if denominator == 0, unlike the div opcode on its own.\n // The surrounding unchecked block does not change this fact.\n // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.\n return low / denominator;\n }\n\n // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.\n if (denominator <= high) {\n Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));\n }\n\n ///////////////////////////////////////////////\n // 512 by 256 division.\n ///////////////////////////////////////////////\n\n // Make division exact by subtracting the remainder from [high low].\n uint256 remainder;\n assembly (\"memory-safe\") {\n // Compute remainder using mulmod.\n remainder := mulmod(x, y, denominator)\n\n // Subtract 256 bit number from 512 bit number.\n high := sub(high, gt(remainder, low))\n low := sub(low, remainder)\n }\n\n // Factor powers of two out of denominator and compute largest power of two divisor of denominator.\n // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.\n\n uint256 twos = denominator & (0 - denominator);\n assembly (\"memory-safe\") {\n // Divide denominator by twos.\n denominator := div(denominator, twos)\n\n // Divide [high low] by twos.\n low := div(low, twos)\n\n // Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.\n twos := add(div(sub(0, twos), twos), 1)\n }\n\n // Shift in bits from high into low.\n low |= high * twos;\n\n // Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such\n // that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for\n // four bits. That is, denominator * inv ≡ 1 mod 2⁴.\n uint256 inverse = (3 * denominator) ^ 2;\n\n // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also\n // works in modular arithmetic, doubling the correct bits in each step.\n inverse *= 2 - denominator * inverse; // inverse mod 2⁸\n inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶\n inverse *= 2 - denominator * inverse; // inverse mod 2³²\n inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴\n inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸\n inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶\n\n // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n // This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is\n // less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and high\n // is no longer required.\n result = low * inverse;\n return result;\n }\n }\n\n /**\n * @dev Calculates x * y / denominator with full precision, following the selected rounding direction.\n */\n function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {\n return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);\n }\n\n /**\n * @dev Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256.\n */\n function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) {\n unchecked {\n (uint256 high, uint256 low) = mul512(x, y);\n if (high >= 1 << n) {\n Panic.panic(Panic.UNDER_OVERFLOW);\n }\n return (high << (256 - n)) | (low >> n);\n }\n }\n\n /**\n * @dev Calculates x * y >> n with full precision, following the selected rounding direction.\n */\n function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) {\n return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 0);\n }\n\n /**\n * @dev Calculate the modular multiplicative inverse of a number in Z/nZ.\n *\n * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.\n * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.\n *\n * If the input value is not inversible, 0 is returned.\n *\n * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the\n * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.\n */\n function invMod(uint256 a, uint256 n) internal pure returns (uint256) {\n unchecked {\n if (n == 0) return 0;\n\n // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)\n // Used to compute integers x and y such that: ax + ny = gcd(a, n).\n // When the gcd is 1, then the inverse of a modulo n exists and it's x.\n // ax + ny = 1\n // ax = 1 + (-y)n\n // ax ≡ 1 (mod n) # x is the inverse of a modulo n\n\n // If the remainder is 0 the gcd is n right away.\n uint256 remainder = a % n;\n uint256 gcd = n;\n\n // Therefore the initial coefficients are:\n // ax + ny = gcd(a, n) = n\n // 0a + 1n = n\n int256 x = 0;\n int256 y = 1;\n\n while (remainder != 0) {\n uint256 quotient = gcd / remainder;\n\n (gcd, remainder) = (\n // The old remainder is the next gcd to try.\n remainder,\n // Compute the next remainder.\n // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd\n // where gcd is at most n (capped to type(uint256).max)\n gcd - remainder * quotient\n );\n\n (x, y) = (\n // Increment the coefficient of a.\n y,\n // Decrement the coefficient of n.\n // Can overflow, but the result is casted to uint256 so that the\n // next value of y is \"wrapped around\" to a value between 0 and n - 1.\n x - y * int256(quotient)\n );\n }\n\n if (gcd != 1) return 0; // No inverse exists.\n return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.\n }\n }\n\n /**\n * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.\n *\n * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is\n * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that\n * `a**(p-2)` is the modular multiplicative inverse of a in Fp.\n *\n * NOTE: this function does NOT check that `p` is a prime greater than `2`.\n */\n function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {\n unchecked {\n return Math.modExp(a, p - 2, p);\n }\n }\n\n /**\n * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)\n *\n * Requirements:\n * - modulus can't be zero\n * - underlying staticcall to precompile must succeed\n *\n * IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make\n * sure the chain you're using it on supports the precompiled contract for modular exponentiation\n * at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,\n * the underlying function will succeed given the lack of a revert, but the result may be incorrectly\n * interpreted as 0.\n */\n function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {\n (bool success, uint256 result) = tryModExp(b, e, m);\n if (!success) {\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n return result;\n }\n\n /**\n * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).\n * It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying\n * to operate modulo 0 or if the underlying precompile reverted.\n *\n * IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain\n * you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in\n * https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack\n * of a revert, but the result may be incorrectly interpreted as 0.\n */\n function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {\n if (m == 0) return (false, 0);\n assembly (\"memory-safe\") {\n let ptr := mload(0x40)\n // | Offset | Content | Content (Hex) |\n // |-----------|------------|--------------------------------------------------------------------|\n // | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n // | 0x60:0x7f | value of b | 0x<.............................................................b> |\n // | 0x80:0x9f | value of e | 0x<.............................................................e> |\n // | 0xa0:0xbf | value of m | 0x<.............................................................m> |\n mstore(ptr, 0x20)\n mstore(add(ptr, 0x20), 0x20)\n mstore(add(ptr, 0x40), 0x20)\n mstore(add(ptr, 0x60), b)\n mstore(add(ptr, 0x80), e)\n mstore(add(ptr, 0xa0), m)\n\n // Given the result < m, it's guaranteed to fit in 32 bytes,\n // so we can use the memory scratch space located at offset 0.\n success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)\n result := mload(0x00)\n }\n }\n\n /**\n * @dev Variant of {modExp} that supports inputs of arbitrary length.\n */\n function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {\n (bool success, bytes memory result) = tryModExp(b, e, m);\n if (!success) {\n Panic.panic(Panic.DIVISION_BY_ZERO);\n }\n return result;\n }\n\n /**\n * @dev Variant of {tryModExp} that supports inputs of arbitrary length.\n */\n function tryModExp(\n bytes memory b,\n bytes memory e,\n bytes memory m\n ) internal view returns (bool success, bytes memory result) {\n if (_zeroBytes(m)) return (false, new bytes(0));\n\n uint256 mLen = m.length;\n\n // Encode call args in result and move the free memory pointer\n result = abi.encodePacked(b.length, e.length, mLen, b, e, m);\n\n assembly (\"memory-safe\") {\n let dataPtr := add(result, 0x20)\n // Write result on top of args to avoid allocating extra memory.\n success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)\n // Overwrite the length.\n // result.length > returndatasize() is guaranteed because returndatasize() == m.length\n mstore(result, mLen)\n // Set the memory pointer after the returned data.\n mstore(0x40, add(dataPtr, mLen))\n }\n }\n\n /**\n * @dev Returns whether the provided byte array is zero.\n */\n function _zeroBytes(bytes memory byteArray) private pure returns (bool) {\n for (uint256 i = 0; i < byteArray.length; ++i) {\n if (byteArray[i] != 0) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded\n * towards zero.\n *\n * This method is based on Newton's method for computing square roots; the algorithm is restricted to only\n * using integer operations.\n */\n function sqrt(uint256 a) internal pure returns (uint256) {\n unchecked {\n // Take care of easy edge cases when a == 0 or a == 1\n if (a <= 1) {\n return a;\n }\n\n // In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a\n // sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between\n // the current value as `ε_n = | x_n - sqrt(a) |`.\n //\n // For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root\n // of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is\n // bigger than any uint256.\n //\n // By noticing that\n // `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`\n // we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar\n // to the msb function.\n uint256 aa = a;\n uint256 xn = 1;\n\n if (aa >= (1 << 128)) {\n aa >>= 128;\n xn <<= 64;\n }\n if (aa >= (1 << 64)) {\n aa >>= 64;\n xn <<= 32;\n }\n if (aa >= (1 << 32)) {\n aa >>= 32;\n xn <<= 16;\n }\n if (aa >= (1 << 16)) {\n aa >>= 16;\n xn <<= 8;\n }\n if (aa >= (1 << 8)) {\n aa >>= 8;\n xn <<= 4;\n }\n if (aa >= (1 << 4)) {\n aa >>= 4;\n xn <<= 2;\n }\n if (aa >= (1 << 2)) {\n xn <<= 1;\n }\n\n // We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).\n //\n // We can refine our estimation by noticing that the middle of that interval minimizes the error.\n // If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).\n // This is going to be our x_0 (and ε_0)\n xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)\n\n // From here, Newton's method give us:\n // x_{n+1} = (x_n + a / x_n) / 2\n //\n // One should note that:\n // x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a\n // = ((x_n² + a) / (2 * x_n))² - a\n // = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a\n // = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)\n // = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)\n // = (x_n² - a)² / (2 * x_n)²\n // = ((x_n² - a) / (2 * x_n))²\n // ≥ 0\n // Which proves that for all n ≥ 1, sqrt(a) ≤ x_n\n //\n // This gives us the proof of quadratic convergence of the sequence:\n // ε_{n+1} = | x_{n+1} - sqrt(a) |\n // = | (x_n + a / x_n) / 2 - sqrt(a) |\n // = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |\n // = | (x_n - sqrt(a))² / (2 * x_n) |\n // = | ε_n² / (2 * x_n) |\n // = ε_n² / | (2 * x_n) |\n //\n // For the first iteration, we have a special case where x_0 is known:\n // ε_1 = ε_0² / | (2 * x_0) |\n // ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))\n // ≤ 2**(2*e-4) / (3 * 2**(e-1))\n // ≤ 2**(e-3) / 3\n // ≤ 2**(e-3-log2(3))\n // ≤ 2**(e-4.5)\n //\n // For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:\n // ε_{n+1} = ε_n² / | (2 * x_n) |\n // ≤ (2**(e-k))² / (2 * 2**(e-1))\n // ≤ 2**(2*e-2*k) / 2**e\n // ≤ 2**(e-2*k)\n xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above\n xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5\n xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9\n xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18\n xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36\n xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72\n\n // Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision\n // ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either\n // sqrt(a) or sqrt(a) + 1.\n return xn - SafeCast.toUint(xn > a / xn);\n }\n }\n\n /**\n * @dev Calculates sqrt(a), following the selected rounding direction.\n */\n function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = sqrt(a);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);\n }\n }\n\n /**\n * @dev Return the log in base 2 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n */\n function log2(uint256 x) internal pure returns (uint256 r) {\n // If value has upper 128 bits set, log2 result is at least 128\n r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;\n // If upper 64 bits of 128-bit half set, add 64 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;\n // If upper 32 bits of 64-bit half set, add 32 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;\n // If upper 16 bits of 32-bit half set, add 16 to result\n r |= SafeCast.toUint((x >> r) > 0xffff) << 4;\n // If upper 8 bits of 16-bit half set, add 8 to result\n r |= SafeCast.toUint((x >> r) > 0xff) << 3;\n // If upper 4 bits of 8-bit half set, add 4 to result\n r |= SafeCast.toUint((x >> r) > 0xf) << 2;\n\n // Shifts value right by the current result and use it as an index into this lookup table:\n //\n // | x (4 bits) | index | table[index] = MSB position |\n // |------------|---------|-----------------------------|\n // | 0000 | 0 | table[0] = 0 |\n // | 0001 | 1 | table[1] = 0 |\n // | 0010 | 2 | table[2] = 1 |\n // | 0011 | 3 | table[3] = 1 |\n // | 0100 | 4 | table[4] = 2 |\n // | 0101 | 5 | table[5] = 2 |\n // | 0110 | 6 | table[6] = 2 |\n // | 0111 | 7 | table[7] = 2 |\n // | 1000 | 8 | table[8] = 3 |\n // | 1001 | 9 | table[9] = 3 |\n // | 1010 | 10 | table[10] = 3 |\n // | 1011 | 11 | table[11] = 3 |\n // | 1100 | 12 | table[12] = 3 |\n // | 1101 | 13 | table[13] = 3 |\n // | 1110 | 14 | table[14] = 3 |\n // | 1111 | 15 | table[15] = 3 |\n //\n // The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.\n assembly (\"memory-safe\") {\n r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))\n }\n }\n\n /**\n * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log2(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);\n }\n }\n\n /**\n * @dev Return the log in base 10 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n */\n function log10(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >= 10 ** 64) {\n value /= 10 ** 64;\n result += 64;\n }\n if (value >= 10 ** 32) {\n value /= 10 ** 32;\n result += 32;\n }\n if (value >= 10 ** 16) {\n value /= 10 ** 16;\n result += 16;\n }\n if (value >= 10 ** 8) {\n value /= 10 ** 8;\n result += 8;\n }\n if (value >= 10 ** 4) {\n value /= 10 ** 4;\n result += 4;\n }\n if (value >= 10 ** 2) {\n value /= 10 ** 2;\n result += 2;\n }\n if (value >= 10 ** 1) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log10(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);\n }\n }\n\n /**\n * @dev Return the log in base 256 of a positive value rounded towards zero.\n * Returns 0 if given 0.\n *\n * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n */\n function log256(uint256 x) internal pure returns (uint256 r) {\n // If value has upper 128 bits set, log2 result is at least 128\n r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;\n // If upper 64 bits of 128-bit half set, add 64 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;\n // If upper 32 bits of 64-bit half set, add 32 to result\n r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;\n // If upper 16 bits of 32-bit half set, add 16 to result\n r |= SafeCast.toUint((x >> r) > 0xffff) << 4;\n // Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8\n return (r >> 3) | SafeCast.toUint((x >> r) > 0xff);\n }\n\n /**\n * @dev Return the log in base 256, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log256(value);\n return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);\n }\n }\n\n /**\n * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.\n */\n function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {\n return uint8(rounding) % 2 == 1;\n }\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/math/SafeCast.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)\n// This file was procedurally generated from scripts/generate/templates/SafeCast.js.\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n */\nlibrary SafeCast {\n /**\n * @dev Value doesn't fit in an uint of `bits` size.\n */\n error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);\n\n /**\n * @dev An int value doesn't fit in an uint of `bits` size.\n */\n error SafeCastOverflowedIntToUint(int256 value);\n\n /**\n * @dev Value doesn't fit in an int of `bits` size.\n */\n error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);\n\n /**\n * @dev An uint value doesn't fit in an int of `bits` size.\n */\n error SafeCastOverflowedUintToInt(uint256 value);\n\n /**\n * @dev Returns the downcasted uint248 from uint256, reverting on\n * overflow (when the input is greater than largest uint248).\n *\n * Counterpart to Solidity's `uint248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n */\n function toUint248(uint256 value) internal pure returns (uint248) {\n if (value > type(uint248).max) {\n revert SafeCastOverflowedUintDowncast(248, value);\n }\n return uint248(value);\n }\n\n /**\n * @dev Returns the downcasted uint240 from uint256, reverting on\n * overflow (when the input is greater than largest uint240).\n *\n * Counterpart to Solidity's `uint240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n */\n function toUint240(uint256 value) internal pure returns (uint240) {\n if (value > type(uint240).max) {\n revert SafeCastOverflowedUintDowncast(240, value);\n }\n return uint240(value);\n }\n\n /**\n * @dev Returns the downcasted uint232 from uint256, reverting on\n * overflow (when the input is greater than largest uint232).\n *\n * Counterpart to Solidity's `uint232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n */\n function toUint232(uint256 value) internal pure returns (uint232) {\n if (value > type(uint232).max) {\n revert SafeCastOverflowedUintDowncast(232, value);\n }\n return uint232(value);\n }\n\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n if (value > type(uint224).max) {\n revert SafeCastOverflowedUintDowncast(224, value);\n }\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint216 from uint256, reverting on\n * overflow (when the input is greater than largest uint216).\n *\n * Counterpart to Solidity's `uint216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n */\n function toUint216(uint256 value) internal pure returns (uint216) {\n if (value > type(uint216).max) {\n revert SafeCastOverflowedUintDowncast(216, value);\n }\n return uint216(value);\n }\n\n /**\n * @dev Returns the downcasted uint208 from uint256, reverting on\n * overflow (when the input is greater than largest uint208).\n *\n * Counterpart to Solidity's `uint208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n */\n function toUint208(uint256 value) internal pure returns (uint208) {\n if (value > type(uint208).max) {\n revert SafeCastOverflowedUintDowncast(208, value);\n }\n return uint208(value);\n }\n\n /**\n * @dev Returns the downcasted uint200 from uint256, reverting on\n * overflow (when the input is greater than largest uint200).\n *\n * Counterpart to Solidity's `uint200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n */\n function toUint200(uint256 value) internal pure returns (uint200) {\n if (value > type(uint200).max) {\n revert SafeCastOverflowedUintDowncast(200, value);\n }\n return uint200(value);\n }\n\n /**\n * @dev Returns the downcasted uint192 from uint256, reverting on\n * overflow (when the input is greater than largest uint192).\n *\n * Counterpart to Solidity's `uint192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n */\n function toUint192(uint256 value) internal pure returns (uint192) {\n if (value > type(uint192).max) {\n revert SafeCastOverflowedUintDowncast(192, value);\n }\n return uint192(value);\n }\n\n /**\n * @dev Returns the downcasted uint184 from uint256, reverting on\n * overflow (when the input is greater than largest uint184).\n *\n * Counterpart to Solidity's `uint184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n */\n function toUint184(uint256 value) internal pure returns (uint184) {\n if (value > type(uint184).max) {\n revert SafeCastOverflowedUintDowncast(184, value);\n }\n return uint184(value);\n }\n\n /**\n * @dev Returns the downcasted uint176 from uint256, reverting on\n * overflow (when the input is greater than largest uint176).\n *\n * Counterpart to Solidity's `uint176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n */\n function toUint176(uint256 value) internal pure returns (uint176) {\n if (value > type(uint176).max) {\n revert SafeCastOverflowedUintDowncast(176, value);\n }\n return uint176(value);\n }\n\n /**\n * @dev Returns the downcasted uint168 from uint256, reverting on\n * overflow (when the input is greater than largest uint168).\n *\n * Counterpart to Solidity's `uint168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n */\n function toUint168(uint256 value) internal pure returns (uint168) {\n if (value > type(uint168).max) {\n revert SafeCastOverflowedUintDowncast(168, value);\n }\n return uint168(value);\n }\n\n /**\n * @dev Returns the downcasted uint160 from uint256, reverting on\n * overflow (when the input is greater than largest uint160).\n *\n * Counterpart to Solidity's `uint160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n */\n function toUint160(uint256 value) internal pure returns (uint160) {\n if (value > type(uint160).max) {\n revert SafeCastOverflowedUintDowncast(160, value);\n }\n return uint160(value);\n }\n\n /**\n * @dev Returns the downcasted uint152 from uint256, reverting on\n * overflow (when the input is greater than largest uint152).\n *\n * Counterpart to Solidity's `uint152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n */\n function toUint152(uint256 value) internal pure returns (uint152) {\n if (value > type(uint152).max) {\n revert SafeCastOverflowedUintDowncast(152, value);\n }\n return uint152(value);\n }\n\n /**\n * @dev Returns the downcasted uint144 from uint256, reverting on\n * overflow (when the input is greater than largest uint144).\n *\n * Counterpart to Solidity's `uint144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n */\n function toUint144(uint256 value) internal pure returns (uint144) {\n if (value > type(uint144).max) {\n revert SafeCastOverflowedUintDowncast(144, value);\n }\n return uint144(value);\n }\n\n /**\n * @dev Returns the downcasted uint136 from uint256, reverting on\n * overflow (when the input is greater than largest uint136).\n *\n * Counterpart to Solidity's `uint136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n */\n function toUint136(uint256 value) internal pure returns (uint136) {\n if (value > type(uint136).max) {\n revert SafeCastOverflowedUintDowncast(136, value);\n }\n return uint136(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n if (value > type(uint128).max) {\n revert SafeCastOverflowedUintDowncast(128, value);\n }\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint120 from uint256, reverting on\n * overflow (when the input is greater than largest uint120).\n *\n * Counterpart to Solidity's `uint120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n */\n function toUint120(uint256 value) internal pure returns (uint120) {\n if (value > type(uint120).max) {\n revert SafeCastOverflowedUintDowncast(120, value);\n }\n return uint120(value);\n }\n\n /**\n * @dev Returns the downcasted uint112 from uint256, reverting on\n * overflow (when the input is greater than largest uint112).\n *\n * Counterpart to Solidity's `uint112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n */\n function toUint112(uint256 value) internal pure returns (uint112) {\n if (value > type(uint112).max) {\n revert SafeCastOverflowedUintDowncast(112, value);\n }\n return uint112(value);\n }\n\n /**\n * @dev Returns the downcasted uint104 from uint256, reverting on\n * overflow (when the input is greater than largest uint104).\n *\n * Counterpart to Solidity's `uint104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n */\n function toUint104(uint256 value) internal pure returns (uint104) {\n if (value > type(uint104).max) {\n revert SafeCastOverflowedUintDowncast(104, value);\n }\n return uint104(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n if (value > type(uint96).max) {\n revert SafeCastOverflowedUintDowncast(96, value);\n }\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint88 from uint256, reverting on\n * overflow (when the input is greater than largest uint88).\n *\n * Counterpart to Solidity's `uint88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n */\n function toUint88(uint256 value) internal pure returns (uint88) {\n if (value > type(uint88).max) {\n revert SafeCastOverflowedUintDowncast(88, value);\n }\n return uint88(value);\n }\n\n /**\n * @dev Returns the downcasted uint80 from uint256, reverting on\n * overflow (when the input is greater than largest uint80).\n *\n * Counterpart to Solidity's `uint80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n */\n function toUint80(uint256 value) internal pure returns (uint80) {\n if (value > type(uint80).max) {\n revert SafeCastOverflowedUintDowncast(80, value);\n }\n return uint80(value);\n }\n\n /**\n * @dev Returns the downcasted uint72 from uint256, reverting on\n * overflow (when the input is greater than largest uint72).\n *\n * Counterpart to Solidity's `uint72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n */\n function toUint72(uint256 value) internal pure returns (uint72) {\n if (value > type(uint72).max) {\n revert SafeCastOverflowedUintDowncast(72, value);\n }\n return uint72(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n if (value > type(uint64).max) {\n revert SafeCastOverflowedUintDowncast(64, value);\n }\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint56 from uint256, reverting on\n * overflow (when the input is greater than largest uint56).\n *\n * Counterpart to Solidity's `uint56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n */\n function toUint56(uint256 value) internal pure returns (uint56) {\n if (value > type(uint56).max) {\n revert SafeCastOverflowedUintDowncast(56, value);\n }\n return uint56(value);\n }\n\n /**\n * @dev Returns the downcasted uint48 from uint256, reverting on\n * overflow (when the input is greater than largest uint48).\n *\n * Counterpart to Solidity's `uint48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n */\n function toUint48(uint256 value) internal pure returns (uint48) {\n if (value > type(uint48).max) {\n revert SafeCastOverflowedUintDowncast(48, value);\n }\n return uint48(value);\n }\n\n /**\n * @dev Returns the downcasted uint40 from uint256, reverting on\n * overflow (when the input is greater than largest uint40).\n *\n * Counterpart to Solidity's `uint40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n */\n function toUint40(uint256 value) internal pure returns (uint40) {\n if (value > type(uint40).max) {\n revert SafeCastOverflowedUintDowncast(40, value);\n }\n return uint40(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n if (value > type(uint32).max) {\n revert SafeCastOverflowedUintDowncast(32, value);\n }\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint24 from uint256, reverting on\n * overflow (when the input is greater than largest uint24).\n *\n * Counterpart to Solidity's `uint24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n */\n function toUint24(uint256 value) internal pure returns (uint24) {\n if (value > type(uint24).max) {\n revert SafeCastOverflowedUintDowncast(24, value);\n }\n return uint24(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n if (value > type(uint16).max) {\n revert SafeCastOverflowedUintDowncast(16, value);\n }\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n if (value > type(uint8).max) {\n revert SafeCastOverflowedUintDowncast(8, value);\n }\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n if (value < 0) {\n revert SafeCastOverflowedIntToUint(value);\n }\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int248 from int256, reverting on\n * overflow (when the input is less than smallest int248 or\n * greater than largest int248).\n *\n * Counterpart to Solidity's `int248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n */\n function toInt248(int256 value) internal pure returns (int248 downcasted) {\n downcasted = int248(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(248, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int240 from int256, reverting on\n * overflow (when the input is less than smallest int240 or\n * greater than largest int240).\n *\n * Counterpart to Solidity's `int240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n */\n function toInt240(int256 value) internal pure returns (int240 downcasted) {\n downcasted = int240(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(240, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int232 from int256, reverting on\n * overflow (when the input is less than smallest int232 or\n * greater than largest int232).\n *\n * Counterpart to Solidity's `int232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n */\n function toInt232(int256 value) internal pure returns (int232 downcasted) {\n downcasted = int232(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(232, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int224 from int256, reverting on\n * overflow (when the input is less than smallest int224 or\n * greater than largest int224).\n *\n * Counterpart to Solidity's `int224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n */\n function toInt224(int256 value) internal pure returns (int224 downcasted) {\n downcasted = int224(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(224, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int216 from int256, reverting on\n * overflow (when the input is less than smallest int216 or\n * greater than largest int216).\n *\n * Counterpart to Solidity's `int216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n */\n function toInt216(int256 value) internal pure returns (int216 downcasted) {\n downcasted = int216(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(216, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int208 from int256, reverting on\n * overflow (when the input is less than smallest int208 or\n * greater than largest int208).\n *\n * Counterpart to Solidity's `int208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n */\n function toInt208(int256 value) internal pure returns (int208 downcasted) {\n downcasted = int208(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(208, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int200 from int256, reverting on\n * overflow (when the input is less than smallest int200 or\n * greater than largest int200).\n *\n * Counterpart to Solidity's `int200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n */\n function toInt200(int256 value) internal pure returns (int200 downcasted) {\n downcasted = int200(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(200, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int192 from int256, reverting on\n * overflow (when the input is less than smallest int192 or\n * greater than largest int192).\n *\n * Counterpart to Solidity's `int192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n */\n function toInt192(int256 value) internal pure returns (int192 downcasted) {\n downcasted = int192(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(192, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int184 from int256, reverting on\n * overflow (when the input is less than smallest int184 or\n * greater than largest int184).\n *\n * Counterpart to Solidity's `int184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n */\n function toInt184(int256 value) internal pure returns (int184 downcasted) {\n downcasted = int184(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(184, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int176 from int256, reverting on\n * overflow (when the input is less than smallest int176 or\n * greater than largest int176).\n *\n * Counterpart to Solidity's `int176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n */\n function toInt176(int256 value) internal pure returns (int176 downcasted) {\n downcasted = int176(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(176, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int168 from int256, reverting on\n * overflow (when the input is less than smallest int168 or\n * greater than largest int168).\n *\n * Counterpart to Solidity's `int168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n */\n function toInt168(int256 value) internal pure returns (int168 downcasted) {\n downcasted = int168(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(168, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int160 from int256, reverting on\n * overflow (when the input is less than smallest int160 or\n * greater than largest int160).\n *\n * Counterpart to Solidity's `int160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n */\n function toInt160(int256 value) internal pure returns (int160 downcasted) {\n downcasted = int160(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(160, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int152 from int256, reverting on\n * overflow (when the input is less than smallest int152 or\n * greater than largest int152).\n *\n * Counterpart to Solidity's `int152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n */\n function toInt152(int256 value) internal pure returns (int152 downcasted) {\n downcasted = int152(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(152, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int144 from int256, reverting on\n * overflow (when the input is less than smallest int144 or\n * greater than largest int144).\n *\n * Counterpart to Solidity's `int144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n */\n function toInt144(int256 value) internal pure returns (int144 downcasted) {\n downcasted = int144(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(144, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int136 from int256, reverting on\n * overflow (when the input is less than smallest int136 or\n * greater than largest int136).\n *\n * Counterpart to Solidity's `int136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n */\n function toInt136(int256 value) internal pure returns (int136 downcasted) {\n downcasted = int136(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(136, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n */\n function toInt128(int256 value) internal pure returns (int128 downcasted) {\n downcasted = int128(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(128, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int120 from int256, reverting on\n * overflow (when the input is less than smallest int120 or\n * greater than largest int120).\n *\n * Counterpart to Solidity's `int120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n */\n function toInt120(int256 value) internal pure returns (int120 downcasted) {\n downcasted = int120(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(120, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int112 from int256, reverting on\n * overflow (when the input is less than smallest int112 or\n * greater than largest int112).\n *\n * Counterpart to Solidity's `int112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n */\n function toInt112(int256 value) internal pure returns (int112 downcasted) {\n downcasted = int112(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(112, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int104 from int256, reverting on\n * overflow (when the input is less than smallest int104 or\n * greater than largest int104).\n *\n * Counterpart to Solidity's `int104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n */\n function toInt104(int256 value) internal pure returns (int104 downcasted) {\n downcasted = int104(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(104, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int96 from int256, reverting on\n * overflow (when the input is less than smallest int96 or\n * greater than largest int96).\n *\n * Counterpart to Solidity's `int96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n */\n function toInt96(int256 value) internal pure returns (int96 downcasted) {\n downcasted = int96(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(96, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int88 from int256, reverting on\n * overflow (when the input is less than smallest int88 or\n * greater than largest int88).\n *\n * Counterpart to Solidity's `int88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n */\n function toInt88(int256 value) internal pure returns (int88 downcasted) {\n downcasted = int88(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(88, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int80 from int256, reverting on\n * overflow (when the input is less than smallest int80 or\n * greater than largest int80).\n *\n * Counterpart to Solidity's `int80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n */\n function toInt80(int256 value) internal pure returns (int80 downcasted) {\n downcasted = int80(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(80, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int72 from int256, reverting on\n * overflow (when the input is less than smallest int72 or\n * greater than largest int72).\n *\n * Counterpart to Solidity's `int72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n */\n function toInt72(int256 value) internal pure returns (int72 downcasted) {\n downcasted = int72(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(72, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n */\n function toInt64(int256 value) internal pure returns (int64 downcasted) {\n downcasted = int64(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(64, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int56 from int256, reverting on\n * overflow (when the input is less than smallest int56 or\n * greater than largest int56).\n *\n * Counterpart to Solidity's `int56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n */\n function toInt56(int256 value) internal pure returns (int56 downcasted) {\n downcasted = int56(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(56, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int48 from int256, reverting on\n * overflow (when the input is less than smallest int48 or\n * greater than largest int48).\n *\n * Counterpart to Solidity's `int48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n */\n function toInt48(int256 value) internal pure returns (int48 downcasted) {\n downcasted = int48(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(48, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int40 from int256, reverting on\n * overflow (when the input is less than smallest int40 or\n * greater than largest int40).\n *\n * Counterpart to Solidity's `int40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n */\n function toInt40(int256 value) internal pure returns (int40 downcasted) {\n downcasted = int40(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(40, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n */\n function toInt32(int256 value) internal pure returns (int32 downcasted) {\n downcasted = int32(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(32, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int24 from int256, reverting on\n * overflow (when the input is less than smallest int24 or\n * greater than largest int24).\n *\n * Counterpart to Solidity's `int24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n */\n function toInt24(int256 value) internal pure returns (int24 downcasted) {\n downcasted = int24(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(24, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n */\n function toInt16(int256 value) internal pure returns (int16 downcasted) {\n downcasted = int16(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(16, value);\n }\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n */\n function toInt8(int256 value) internal pure returns (int8 downcasted) {\n downcasted = int8(value);\n if (downcasted != value) {\n revert SafeCastOverflowedIntDowncast(8, value);\n }\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n if (value > uint256(type(int256).max)) {\n revert SafeCastOverflowedUintToInt(value);\n }\n return int256(value);\n }\n\n /**\n * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.\n */\n function toUint(bool b) internal pure returns (uint256 u) {\n assembly (\"memory-safe\") {\n u := iszero(iszero(b))\n }\n }\n}" + }, + "lib/rooster/openzeppelin-custom/contracts/utils/Panic.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Helper library for emitting standardized panic codes.\n *\n * ```solidity\n * contract Example {\n * using Panic for uint256;\n *\n * // Use any of the declared internal constants\n * function foo() { Panic.GENERIC.panic(); }\n *\n * // Alternatively\n * function foo() { Panic.panic(Panic.GENERIC); }\n * }\n * ```\n *\n * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].\n *\n * _Available since v5.1._\n */\n// slither-disable-next-line unused-state\nlibrary Panic {\n /// @dev generic / unspecified error\n uint256 internal constant GENERIC = 0x00;\n /// @dev used by the assert() builtin\n uint256 internal constant ASSERT = 0x01;\n /// @dev arithmetic underflow or overflow\n uint256 internal constant UNDER_OVERFLOW = 0x11;\n /// @dev division or modulo by zero\n uint256 internal constant DIVISION_BY_ZERO = 0x12;\n /// @dev enum conversion error\n uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;\n /// @dev invalid encoding in storage\n uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;\n /// @dev empty array pop\n uint256 internal constant EMPTY_ARRAY_POP = 0x31;\n /// @dev array out of bounds access\n uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;\n /// @dev resource error (too large allocation or too large array)\n uint256 internal constant RESOURCE_ERROR = 0x41;\n /// @dev calling invalid internal function\n uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;\n\n /// @dev Reverts with a panic code. Recommended to use with\n /// the internal constants with predefined codes.\n function panic(uint256 code) internal pure {\n assembly (\"memory-safe\") {\n mstore(0x00, 0x4e487b71)\n mstore(0x20, code)\n revert(0x1c, 0x24)\n }\n }\n}" + }, + "lib/rooster/v2-common/libraries/Constants.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\n// factory contraints on pools\nuint8 constant MAX_PROTOCOL_FEE_RATIO_D3 = 0.25e3; // 25%\nuint256 constant MAX_PROTOCOL_LENDING_FEE_RATE_D18 = 0.02e18; // 2%\nuint64 constant MAX_POOL_FEE_D18 = 0.9e18; // 90%\nuint64 constant MIN_LOOKBACK = 1 seconds;\n\n// pool constraints\nuint8 constant NUMBER_OF_KINDS = 4;\nint32 constant NUMBER_OF_KINDS_32 = int32(int8(NUMBER_OF_KINDS));\nuint256 constant MAX_TICK = 322_378; // max price 1e14 in D18 scale\nint32 constant MAX_TICK_32 = int32(int256(MAX_TICK));\nint32 constant MIN_TICK_32 = int32(-int256(MAX_TICK));\nuint256 constant MAX_BINS_TO_MERGE = 3;\nuint128 constant MINIMUM_LIQUIDITY = 1e8;\n\n// accessor named constants\nuint8 constant ALL_KINDS_MASK = 0xF; // 0b1111\nuint8 constant PERMISSIONED_LIQUIDITY_MASK = 0x10; // 0b010000\nuint8 constant PERMISSIONED_SWAP_MASK = 0x20; // 0b100000\nuint8 constant OPTIONS_MASK = ALL_KINDS_MASK | PERMISSIONED_LIQUIDITY_MASK | PERMISSIONED_SWAP_MASK; // 0b111111\n\n// named values\naddress constant MERGED_LP_BALANCE_ADDRESS = address(0);\nuint256 constant MERGED_LP_BALANCE_SUBACCOUNT = 0;\nuint128 constant ONE = 1e18;\nuint128 constant ONE_SQUARED = 1e36;\nint256 constant INT256_ONE = 1e18;\nuint256 constant ONE_D8 = 1e8;\nuint256 constant ONE_D3 = 1e3;\nint40 constant INT_ONE_D8 = 1e8;\nint40 constant HALF_TICK_D8 = 0.5e8;\nuint8 constant DEFAULT_DECIMALS = 18;\nuint256 constant DEFAULT_SCALE = 1;\nbytes constant EMPTY_PRICE_BREAKS = hex\"010000000000000000000000\";" + }, + "lib/rooster/v2-common/libraries/Math.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport {Math as OzMath} from \"../../openzeppelin-custom/contracts/utils/math/Math.sol\";\n\nimport {ONE, DEFAULT_SCALE, DEFAULT_DECIMALS, INT_ONE_D8, ONE_SQUARED} from \"./Constants.sol\";\n\n/**\n * @notice Math functions.\n */\nlibrary Math {\n /**\n * @notice Returns the lesser of two values.\n * @param x First uint256 value.\n * @param y Second uint256 value.\n */\n function min(uint256 x, uint256 y) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), lt(y, x)))\n }\n }\n\n /**\n * @notice Returns the lesser of two uint128 values.\n * @param x First uint128 value.\n * @param y Second uint128 value.\n */\n function min128(uint128 x, uint128 y) internal pure returns (uint128 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), lt(y, x)))\n }\n }\n\n /**\n * @notice Returns the lesser of two int256 values.\n * @param x First int256 value.\n * @param y Second int256 value.\n */\n function min(int256 x, int256 y) internal pure returns (int256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), slt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two uint256 values.\n * @param x First uint256 value.\n * @param y Second uint256 value.\n */\n function max(uint256 x, uint256 y) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), gt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two int256 values.\n * @param x First int256 value.\n * @param y Second int256 value.\n */\n function max(int256 x, int256 y) internal pure returns (int256 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), sgt(y, x)))\n }\n }\n\n /**\n * @notice Returns the greater of two uint128 values.\n * @param x First uint128 value.\n * @param y Second uint128 value.\n */\n function max128(uint128 x, uint128 y) internal pure returns (uint128 z) {\n assembly (\"memory-safe\") {\n z := xor(x, mul(xor(x, y), gt(y, x)))\n }\n }\n\n /**\n * @notice Thresholds a value to be within the specified bounds.\n * @param value The value to bound.\n * @param lowerLimit The minimum allowable value.\n * @param upperLimit The maximum allowable value.\n */\n function boundValue(\n uint256 value,\n uint256 lowerLimit,\n uint256 upperLimit\n ) internal pure returns (uint256 outputValue) {\n outputValue = min(max(value, lowerLimit), upperLimit);\n }\n\n /**\n * @notice Returns the difference between two uint128 values or zero if the result would be negative.\n * @param x The minuend.\n * @param y The subtrahend.\n */\n function clip128(uint128 x, uint128 y) internal pure returns (uint128) {\n unchecked {\n return x < y ? 0 : x - y;\n }\n }\n\n /**\n * @notice Returns the difference between two uint256 values or zero if the result would be negative.\n * @param x The minuend.\n * @param y The subtrahend.\n */\n function clip(uint256 x, uint256 y) internal pure returns (uint256) {\n unchecked {\n return x < y ? 0 : x - y;\n }\n }\n\n /**\n * @notice Divides one uint256 by another, rounding down to the nearest\n * integer.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divFloor(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivFloor(x, ONE, y);\n }\n\n /**\n * @notice Divides one uint256 by another, rounding up to the nearest integer.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divCeil(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivCeil(x, ONE, y);\n }\n\n /**\n * @notice Multiplies two uint256 values and then divides by ONE, rounding down.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulFloor(uint256 x, uint256 y) internal pure returns (uint256) {\n return OzMath.mulDiv(x, y, ONE);\n }\n\n /**\n * @notice Multiplies two uint256 values and then divides by ONE, rounding up.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulCeil(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivCeil(x, y, ONE);\n }\n\n /**\n * @notice Calculates the multiplicative inverse of a uint256, rounding down.\n * @param x The value to invert.\n */\n function invFloor(uint256 x) internal pure returns (uint256) {\n unchecked {\n return ONE_SQUARED / x;\n }\n }\n\n /**\n * @notice Calculates the multiplicative inverse of a uint256, rounding up.\n * @param denominator The value to invert.\n */\n function invCeil(uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // divide z - 1 by the denominator and add 1.\n z := add(div(sub(ONE_SQUARED, 1), denominator), 1)\n }\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding down.\n * @param x The multiplicand.\n * @param y The multiplier.\n * @param k The divisor.\n */\n function mulDivFloor(uint256 x, uint256 y, uint256 k) internal pure returns (uint256 result) {\n result = OzMath.mulDiv(x, y, max(1, k));\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding up if there's a remainder.\n * @param x The multiplicand.\n * @param y The multiplier.\n * @param k The divisor.\n */\n function mulDivCeil(uint256 x, uint256 y, uint256 k) internal pure returns (uint256 result) {\n result = mulDivFloor(x, y, k);\n if (mulmod(x, y, max(1, k)) != 0) result = result + 1;\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding\n * down. Will revert if `x * y` is larger than `type(uint256).max`.\n * @param x The first operand for multiplication.\n * @param y The second operand for multiplication.\n * @param denominator The divisor after multiplication.\n */\n function mulDivDown(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // Store x * y in z for now.\n z := mul(x, y)\n if iszero(denominator) {\n denominator := 1\n }\n\n if iszero(or(iszero(x), eq(div(z, x), y))) {\n revert(0, 0)\n }\n\n // Divide z by the denominator.\n z := div(z, denominator)\n }\n }\n\n /**\n * @notice Multiplies two uint256 values and divides by a third, rounding\n * up. Will revert if `x * y` is larger than `type(uint256).max`.\n * @param x The first operand for multiplication.\n * @param y The second operand for multiplication.\n * @param denominator The divisor after multiplication.\n */\n function mulDivUp(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {\n assembly (\"memory-safe\") {\n // Store x * y in z for now.\n z := mul(x, y)\n if iszero(denominator) {\n denominator := 1\n }\n\n if iszero(or(iszero(x), eq(div(z, x), y))) {\n revert(0, 0)\n }\n\n // First, divide z - 1 by the denominator and add 1.\n // We allow z - 1 to underflow if z is 0, because we multiply the\n // end result by 0 if z is zero, ensuring we return 0 if z is zero.\n z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))\n }\n }\n\n /**\n * @notice Multiplies a uint256 by another and divides by a constant,\n * rounding down. Will revert if `x * y` is larger than\n * `type(uint256).max`.\n * @param x The multiplicand.\n * @param y The multiplier.\n */\n function mulDown(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivDown(x, y, ONE);\n }\n\n /**\n * @notice Divides a uint256 by another, rounding down the result. Will\n * revert if `x * 1e18` is larger than `type(uint256).max`.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divDown(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivDown(x, ONE, y);\n }\n\n /**\n * @notice Divides a uint256 by another, rounding up the result. Will\n * revert if `x * 1e18` is larger than `type(uint256).max`.\n * @param x The dividend.\n * @param y The divisor.\n */\n function divUp(uint256 x, uint256 y) internal pure returns (uint256) {\n return mulDivUp(x, ONE, y);\n }\n\n /**\n * @notice Scales a number based on a difference in decimals from a default.\n * @param decimals The new decimal precision.\n */\n function scale(uint8 decimals) internal pure returns (uint256) {\n unchecked {\n if (decimals == DEFAULT_DECIMALS) {\n return DEFAULT_SCALE;\n } else {\n return 10 ** (DEFAULT_DECIMALS - decimals);\n }\n }\n }\n\n /**\n * @notice Adjusts a scaled amount to the token decimal scale.\n * @param amount The scaled amount.\n * @param scaleFactor The scaling factor to adjust by.\n * @param ceil Whether to round up (true) or down (false).\n */\n function ammScaleToTokenScale(uint256 amount, uint256 scaleFactor, bool ceil) internal pure returns (uint256 z) {\n unchecked {\n if (scaleFactor == DEFAULT_SCALE || amount == 0) {\n return amount;\n } else {\n if (!ceil) return amount / scaleFactor;\n assembly (\"memory-safe\") {\n z := add(div(sub(amount, 1), scaleFactor), 1)\n }\n }\n }\n }\n\n /**\n * @notice Adjusts a token amount to the D18 AMM scale.\n * @param amount The amount in token scale.\n * @param scaleFactor The scale factor for adjustment.\n */\n function tokenScaleToAmmScale(uint256 amount, uint256 scaleFactor) internal pure returns (uint256) {\n if (scaleFactor == DEFAULT_SCALE) {\n return amount;\n } else {\n return amount * scaleFactor;\n }\n }\n\n /**\n * @notice Returns the absolute value of a signed 32-bit integer.\n * @param x The integer to take the absolute value of.\n */\n function abs32(int32 x) internal pure returns (uint32) {\n unchecked {\n return uint32(x < 0 ? -x : x);\n }\n }\n\n /**\n * @notice Returns the absolute value of a signed 256-bit integer.\n * @param x The integer to take the absolute value of.\n */\n function abs(int256 x) internal pure returns (uint256) {\n unchecked {\n return uint256(x < 0 ? -x : x);\n }\n }\n\n /**\n * @notice Calculates the integer square root of a uint256 rounded down.\n * @param x The number to take the square root of.\n */\n function sqrt(uint256 x) internal pure returns (uint256 z) {\n // from https://github.com/transmissions11/solmate/blob/e8f96f25d48fe702117ce76c79228ca4f20206cb/src/utils/FixedPointMathLib.sol\n assembly (\"memory-safe\") {\n let y := x\n z := 181\n\n if iszero(lt(y, 0x10000000000000000000000000000000000)) {\n y := shr(128, y)\n z := shl(64, z)\n }\n if iszero(lt(y, 0x1000000000000000000)) {\n y := shr(64, y)\n z := shl(32, z)\n }\n if iszero(lt(y, 0x10000000000)) {\n y := shr(32, y)\n z := shl(16, z)\n }\n if iszero(lt(y, 0x1000000)) {\n y := shr(16, y)\n z := shl(8, z)\n }\n\n z := shr(18, mul(z, add(y, 65536)))\n\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n z := shr(1, add(z, div(x, z)))\n\n z := sub(z, lt(div(x, z), z))\n }\n }\n\n /**\n * @notice Computes the floor of a D8-scaled number as an int32, ignoring\n * potential overflow in the cast.\n * @param val The D8-scaled number.\n */\n function floorD8Unchecked(int256 val) internal pure returns (int32) {\n int32 val32;\n bool check;\n unchecked {\n val32 = int32(val / INT_ONE_D8);\n check = (val < 0 && val % INT_ONE_D8 != 0);\n }\n return check ? val32 - 1 : val32;\n }\n}" + }, + "lib/rooster/v2-common/libraries/TickMath.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n// As the copyright holder of this work, Ubiquity Labs retains\n// the right to distribute, use, and modify this code under any license of\n// their choosing, in addition to the terms of the GPL-v2 or later.\npragma solidity ^0.8.25;\n\nimport {Math as OzMath} from \"../../openzeppelin-custom/contracts/utils/math/Math.sol\";\nimport {Math} from \"./Math.sol\";\nimport {MAX_TICK, ONE} from \"./Constants.sol\";\n\n/**\n * @notice Math functions related to tick operations.\n */\n// slither-disable-start divide-before-multiply\nlibrary TickMath {\n using Math for uint256;\n\n error TickMaxExceeded(int256 tick);\n\n /**\n * @notice Compute the lower and upper sqrtPrice of a tick.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n */\n function tickSqrtPrices(\n uint256 tickSpacing,\n int32 _tick\n ) internal pure returns (uint256 sqrtLowerPrice, uint256 sqrtUpperPrice) {\n unchecked {\n sqrtLowerPrice = tickSqrtPrice(tickSpacing, _tick);\n sqrtUpperPrice = tickSqrtPrice(tickSpacing, _tick + 1);\n }\n }\n\n /**\n * @notice Compute the base tick value from the pool tick and the\n * tickSpacing. Revert if base tick is beyond the max tick boundary.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n */\n function subTickIndex(uint256 tickSpacing, int32 _tick) internal pure returns (uint32 subTick) {\n subTick = Math.abs32(_tick);\n subTick *= uint32(tickSpacing);\n if (subTick > MAX_TICK) {\n revert TickMaxExceeded(_tick);\n }\n }\n\n /**\n * @notice Calculate the square root price for a given tick and tick spacing.\n * @param tickSpacing The tick spacing used for calculations.\n * @param _tick The input tick value.\n * @return _result The square root price.\n */\n function tickSqrtPrice(uint256 tickSpacing, int32 _tick) internal pure returns (uint256 _result) {\n unchecked {\n uint256 tick = subTickIndex(tickSpacing, _tick);\n\n uint256 ratio = tick & 0x1 != 0 ? 0xfffcb933bd6fad9d3af5f0b9f25db4d6 : 0x100000000000000000000000000000000;\n if (tick & 0x2 != 0) ratio = (ratio * 0xfff97272373d41fd789c8cb37ffcaa1c) >> 128;\n if (tick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656ac9229c67059486f389) >> 128;\n if (tick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e81259b3cddc7a064941) >> 128;\n if (tick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f67b19e8887e0bd251eb7) >> 128;\n if (tick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98cd2e57b660be99eb2c4a) >> 128;\n if (tick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c9838804e327cb417cafcb) >> 128;\n if (tick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99d51e2cc356c2f617dbe0) >> 128;\n if (tick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900aecf64236ab31f1f9dcb5) >> 128;\n if (tick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac4d9194200696907cf2e37) >> 128;\n if (tick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b88206f8abe8a3b44dd9be) >> 128;\n if (tick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c578ef4f1d17b2b235d480) >> 128;\n if (tick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd254ee83bdd3f248e7e785e) >> 128;\n if (tick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d8f7dd10e744d913d033333) >> 128;\n if (tick & 0x4000 != 0) ratio = (ratio * 0x70d869a156ddd32a39e257bc3f50aa9b) >> 128;\n if (tick & 0x8000 != 0) ratio = (ratio * 0x31be135f97da6e09a19dc367e3b6da40) >> 128;\n if (tick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7e5a9780b0cc4e25d61a56) >> 128;\n if (tick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedbcb3a6ccb7ce618d14225) >> 128;\n if (tick & 0x40000 != 0) ratio = (ratio * 0x2216e584f630389b2052b8db590e) >> 128;\n if (_tick > 0) ratio = type(uint256).max / ratio;\n _result = (ratio * ONE) >> 128;\n }\n }\n\n /**\n * @notice Calculate liquidity of a tick.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n */\n function getTickL(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n ) internal pure returns (uint256 liquidity) {\n // known:\n // - sqrt price values are different\n // - reserveA and reserveB fit in 128 bit\n // - sqrt price is in (1e-7, 1e7)\n // - D18 max for uint256 is 1.15e59\n // - D18 min is 1e-18\n\n unchecked {\n // diff is in (5e-12, 4e6); max tick spacing is 10_000\n uint256 diff = sqrtUpperTickPrice - sqrtLowerTickPrice;\n\n // Need to maximize precision by shifting small values A and B up so\n // that they use more of the available bit range. Two constraints to\n // consider: we need A * B * diff / sqrtPrice to be bigger than 1e-18\n // when the bump is not in play. This constrains the threshold for\n // bumping to be at least 77 bit; ie, either a or b needs 2^77 which\n // means that term A * B * diff / sqrtPrice > 1e-18.\n //\n // At the other end, the second constraint is that b^2 needs to fit in\n // a 256-bit number, so, post bump, the max reserve value needs to be\n // less than 6e22. With a 78-bit threshold and a 57-bit bump, we have A\n // and B are in (1.4e-1, 4.4e22 (2^(78+57))) with bump, and one of A or\n // B is at least 2^78 without the bump, but the other reserve value may\n // be as small as 1 wei.\n uint256 precisionBump = 0;\n if ((reserveA >> 78) == 0 && (reserveB >> 78) == 0) {\n precisionBump = 57;\n reserveA <<= precisionBump;\n reserveB <<= precisionBump;\n }\n\n if (reserveB == 0) return Math.divDown(reserveA, diff) >> precisionBump;\n if (reserveA == 0)\n return Math.mulDivDown(reserveB.mulDown(sqrtLowerTickPrice), sqrtUpperTickPrice, diff) >> precisionBump;\n\n // b is in (7.2e-9 (2^57 / 1e7 / 2), 2.8e29 (2^(78+57) * 1e7 / 2)) with bump\n // b is in a subset of the same range without bump\n uint256 b = (reserveA.divDown(sqrtUpperTickPrice) + reserveB.mulDown(sqrtLowerTickPrice)) >> 1;\n\n // b^2 is in (5.1e-17, 4.8e58); and will not overflow on either end;\n // A*B is in (3e-13 (2^78 / 1e18 * 1e-18), 1.9e45) without bump and is in a subset range with bump\n // A*B*diff/sqrtUpper is in (1.5e-17 (3e-13 * 5e-12 * 1e7), 7.6e58);\n\n // Since b^2 is at the upper edge of the precision range, we are not\n // able to multiply the argument of the sqrt by 1e18, instead, we move\n // this factor outside of the sqrt. The resulting loss of precision\n // means that this liquidity value is a lower bound on the tick\n // liquidity\n return\n OzMath.mulDiv(\n b +\n Math.sqrt(\n (OzMath.mulDiv(b, b, ONE) +\n OzMath.mulDiv(reserveB.mulFloor(reserveA), diff, sqrtUpperTickPrice))\n ) *\n 1e9,\n sqrtUpperTickPrice,\n diff\n ) >> precisionBump;\n }\n }\n\n /**\n * @notice Calculate square root price of a tick. Returns left edge of the\n * tick if the tick has no reserves.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n * @return sqrtPrice The calculated square root price.\n */\n function getSqrtPrice(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice,\n uint256 liquidity\n ) internal pure returns (uint256 sqrtPrice) {\n unchecked {\n if (reserveA == 0) {\n return sqrtLowerTickPrice;\n }\n if (reserveB == 0) {\n return sqrtUpperTickPrice;\n }\n sqrtPrice = Math.sqrt(\n ONE *\n (reserveA + liquidity.mulDown(sqrtLowerTickPrice)).divDown(\n reserveB + liquidity.divDown(sqrtUpperTickPrice)\n )\n );\n sqrtPrice = Math.boundValue(sqrtPrice, sqrtLowerTickPrice, sqrtUpperTickPrice);\n }\n }\n\n /**\n * @notice Calculate square root price of a tick. Returns left edge of the\n * tick if the tick has no reserves.\n * @param reserveA Tick reserve of token A.\n * @param reserveB Tick reserve of token B.\n * @param sqrtLowerTickPrice The square root price of the lower tick edge.\n * @param sqrtUpperTickPrice The square root price of the upper tick edge.\n * @return sqrtPrice The calculated square root price.\n * @return liquidity The calculated liquidity.\n */\n function getTickSqrtPriceAndL(\n uint256 reserveA,\n uint256 reserveB,\n uint256 sqrtLowerTickPrice,\n uint256 sqrtUpperTickPrice\n ) internal pure returns (uint256 sqrtPrice, uint256 liquidity) {\n liquidity = getTickL(reserveA, reserveB, sqrtLowerTickPrice, sqrtUpperTickPrice);\n sqrtPrice = getSqrtPrice(reserveA, reserveB, sqrtLowerTickPrice, sqrtUpperTickPrice, liquidity);\n }\n}\n// slither-disable-end divide-before-multiply" + }, + "solidity-bytes-utils/contracts/BytesLib.sol": { + "content": "// SPDX-License-Identifier: Unlicense\n/*\n * @title Solidity Bytes Arrays Utils\n * @author Gonçalo Sá \n *\n * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.\n * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.\n */\npragma solidity >=0.8.0 <0.9.0;\n\n\nlibrary BytesLib {\n function concat(\n bytes memory _preBytes,\n bytes memory _postBytes\n )\n internal\n pure\n returns (bytes memory)\n {\n bytes memory tempBytes;\n\n assembly {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // Store the length of the first bytes array at the beginning of\n // the memory for tempBytes.\n let length := mload(_preBytes)\n mstore(tempBytes, length)\n\n // Maintain a memory counter for the current write location in the\n // temp bytes array by adding the 32 bytes for the array length to\n // the starting location.\n let mc := add(tempBytes, 0x20)\n // Stop copying when the memory counter reaches the length of the\n // first bytes array.\n let end := add(mc, length)\n\n for {\n // Initialize a copy counter to the start of the _preBytes data,\n // 32 bytes into its memory.\n let cc := add(_preBytes, 0x20)\n } lt(mc, end) {\n // Increase both counters by 32 bytes each iteration.\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // Write the _preBytes data into the tempBytes memory 32 bytes\n // at a time.\n mstore(mc, mload(cc))\n }\n\n // Add the length of _postBytes to the current length of tempBytes\n // and store it as the new length in the first 32 bytes of the\n // tempBytes memory.\n length := mload(_postBytes)\n mstore(tempBytes, add(length, mload(tempBytes)))\n\n // Move the memory counter back from a multiple of 0x20 to the\n // actual end of the _preBytes data.\n mc := end\n // Stop copying when the memory counter reaches the new combined\n // length of the arrays.\n end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n // Update the free-memory pointer by padding our last write location\n // to 32 bytes: add 31 bytes to the end of tempBytes to move to the\n // next 32 byte block, then round down to the nearest multiple of\n // 32. If the sum of the length of the two arrays is zero then add\n // one before rounding down to leave a blank 32 bytes (the length block with 0).\n mstore(0x40, and(\n add(add(end, iszero(add(length, mload(_preBytes)))), 31),\n not(31) // Round down to the nearest 32 bytes.\n ))\n }\n\n return tempBytes;\n }\n\n function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {\n assembly {\n // Read the first 32 bytes of _preBytes storage, which is the length\n // of the array. (We don't need to use the offset into the slot\n // because arrays use the entire slot.)\n let fslot := sload(_preBytes.slot)\n // Arrays of 31 bytes or less have an even value in their slot,\n // while longer arrays have an odd value. The actual length is\n // the slot divided by two for odd values, and the lowest order\n // byte divided by two for even values.\n // If the slot is even, bitwise and the slot with 255 and divide by\n // two to get the length. If the slot is odd, bitwise and the slot\n // with -1 and divide by two.\n let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)\n let mlength := mload(_postBytes)\n let newlength := add(slength, mlength)\n // slength can contain both the length and contents of the array\n // if length < 32 bytes so let's prepare for that\n // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage\n switch add(lt(slength, 32), lt(newlength, 32))\n case 2 {\n // Since the new array still fits in the slot, we just need to\n // update the contents of the slot.\n // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length\n sstore(\n _preBytes.slot,\n // all the modifications to the slot are inside this\n // next block\n add(\n // we can just add to the slot contents because the\n // bytes we want to change are the LSBs\n fslot,\n add(\n mul(\n div(\n // load the bytes from memory\n mload(add(_postBytes, 0x20)),\n // zero all bytes to the right\n exp(0x100, sub(32, mlength))\n ),\n // and now shift left the number of bytes to\n // leave space for the length in the slot\n exp(0x100, sub(32, newlength))\n ),\n // increase length by the double of the memory\n // bytes length\n mul(mlength, 2)\n )\n )\n )\n }\n case 1 {\n // The stored value fits in the slot, but the combined value\n // will exceed it.\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n let sc := add(keccak256(0x0, 0x20), div(slength, 32))\n\n // save new length\n sstore(_preBytes.slot, add(mul(newlength, 2), 1))\n\n // The contents of the _postBytes array start 32 bytes into\n // the structure. Our first read should obtain the `submod`\n // bytes that can fit into the unused space in the last word\n // of the stored array. To get this, we read 32 bytes starting\n // from `submod`, so the data we read overlaps with the array\n // contents by `submod` bytes. Masking the lowest-order\n // `submod` bytes allows us to add that value directly to the\n // stored value.\n\n let submod := sub(32, slength)\n let mc := add(_postBytes, submod)\n let end := add(_postBytes, mlength)\n let mask := sub(exp(0x100, submod), 1)\n\n sstore(\n sc,\n add(\n and(\n fslot,\n 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00\n ),\n and(mload(mc), mask)\n )\n )\n\n for {\n mc := add(mc, 0x20)\n sc := add(sc, 1)\n } lt(mc, end) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n sstore(sc, mload(mc))\n }\n\n mask := exp(0x100, sub(mc, end))\n\n sstore(sc, mul(div(mload(mc), mask), mask))\n }\n default {\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n // Start copying to the last used word of the stored array.\n let sc := add(keccak256(0x0, 0x20), div(slength, 32))\n\n // save new length\n sstore(_preBytes.slot, add(mul(newlength, 2), 1))\n\n // Copy over the first `submod` bytes of the new data as in\n // case 1 above.\n let slengthmod := mod(slength, 32)\n let mlengthmod := mod(mlength, 32)\n let submod := sub(32, slengthmod)\n let mc := add(_postBytes, submod)\n let end := add(_postBytes, mlength)\n let mask := sub(exp(0x100, submod), 1)\n\n sstore(sc, add(sload(sc), and(mload(mc), mask)))\n\n for {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } lt(mc, end) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n sstore(sc, mload(mc))\n }\n\n mask := exp(0x100, sub(mc, end))\n\n sstore(sc, mul(div(mload(mc), mask), mask))\n }\n }\n }\n\n function slice(\n bytes memory _bytes,\n uint256 _start,\n uint256 _length\n )\n internal\n pure\n returns (bytes memory)\n {\n // We're using the unchecked block below because otherwise execution ends \n // with the native overflow error code.\n unchecked {\n require(_length + 31 >= _length, \"slice_overflow\");\n }\n require(_bytes.length >= _start + _length, \"slice_outOfBounds\");\n\n bytes memory tempBytes;\n\n assembly {\n switch iszero(_length)\n case 0 {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // The first word of the slice result is potentially a partial\n // word read from the original array. To read it, we calculate\n // the length of that partial word and start copying that many\n // bytes into the array. The first word we copy will start with\n // data we don't care about, but the last `lengthmod` bytes will\n // land at the beginning of the contents of the new array. When\n // we're done copying, we overwrite the full first word with\n // the actual length of the slice.\n let lengthmod := and(_length, 31)\n\n // The multiplication in the next line is necessary\n // because when slicing multiples of 32 bytes (lengthmod == 0)\n // the following copy loop was copying the origin's length\n // and then ending prematurely not copying everything it should.\n let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))\n let end := add(mc, _length)\n\n for {\n // The multiplication in the next line has the same exact purpose\n // as the one above.\n let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n mstore(tempBytes, _length)\n\n //update free-memory pointer\n //allocating the array padded to 32 bytes like the compiler does now\n mstore(0x40, and(add(mc, 31), not(31)))\n }\n //if we want a zero-length slice let's just return a zero-length array\n default {\n tempBytes := mload(0x40)\n //zero out the 32 bytes slice we are about to return\n //we need to do it because Solidity does not garbage collect\n mstore(tempBytes, 0)\n\n mstore(0x40, add(tempBytes, 0x20))\n }\n }\n\n return tempBytes;\n }\n\n function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {\n require(_bytes.length >= _start + 20, \"toAddress_outOfBounds\");\n address tempAddress;\n\n assembly {\n tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)\n }\n\n return tempAddress;\n }\n\n function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {\n require(_bytes.length >= _start + 1 , \"toUint8_outOfBounds\");\n uint8 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x1), _start))\n }\n\n return tempUint;\n }\n\n function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {\n require(_bytes.length >= _start + 2, \"toUint16_outOfBounds\");\n uint16 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x2), _start))\n }\n\n return tempUint;\n }\n\n function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {\n require(_bytes.length >= _start + 4, \"toUint32_outOfBounds\");\n uint32 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x4), _start))\n }\n\n return tempUint;\n }\n\n function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {\n require(_bytes.length >= _start + 8, \"toUint64_outOfBounds\");\n uint64 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x8), _start))\n }\n\n return tempUint;\n }\n\n function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {\n require(_bytes.length >= _start + 12, \"toUint96_outOfBounds\");\n uint96 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0xc), _start))\n }\n\n return tempUint;\n }\n\n function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {\n require(_bytes.length >= _start + 16, \"toUint128_outOfBounds\");\n uint128 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x10), _start))\n }\n\n return tempUint;\n }\n\n function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {\n require(_bytes.length >= _start + 32, \"toUint256_outOfBounds\");\n uint256 tempUint;\n\n assembly {\n tempUint := mload(add(add(_bytes, 0x20), _start))\n }\n\n return tempUint;\n }\n\n function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {\n require(_bytes.length >= _start + 32, \"toBytes32_outOfBounds\");\n bytes32 tempBytes32;\n\n assembly {\n tempBytes32 := mload(add(add(_bytes, 0x20), _start))\n }\n\n return tempBytes32;\n }\n\n function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {\n bool success = true;\n\n assembly {\n let length := mload(_preBytes)\n\n // if lengths don't match the arrays are not equal\n switch eq(length, mload(_postBytes))\n case 1 {\n // cb is a circuit breaker in the for loop since there's\n // no said feature for inline assembly loops\n // cb = 1 - don't breaker\n // cb = 0 - break\n let cb := 1\n\n let mc := add(_preBytes, 0x20)\n let end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n // the next line is the loop condition:\n // while(uint256(mc < end) + cb == 2)\n } eq(add(lt(mc, end), cb), 2) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // if any of these checks fails then arrays are not equal\n if iszero(eq(mload(mc), mload(cc))) {\n // unsuccess:\n success := 0\n cb := 0\n }\n }\n }\n default {\n // unsuccess:\n success := 0\n }\n }\n\n return success;\n }\n\n function equalStorage(\n bytes storage _preBytes,\n bytes memory _postBytes\n )\n internal\n view\n returns (bool)\n {\n bool success = true;\n\n assembly {\n // we know _preBytes_offset is 0\n let fslot := sload(_preBytes.slot)\n // Decode the length of the stored array like in concatStorage().\n let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)\n let mlength := mload(_postBytes)\n\n // if lengths don't match the arrays are not equal\n switch eq(slength, mlength)\n case 1 {\n // slength can contain both the length and contents of the array\n // if length < 32 bytes so let's prepare for that\n // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage\n if iszero(iszero(slength)) {\n switch lt(slength, 32)\n case 1 {\n // blank the last byte which is the length\n fslot := mul(div(fslot, 0x100), 0x100)\n\n if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {\n // unsuccess:\n success := 0\n }\n }\n default {\n // cb is a circuit breaker in the for loop since there's\n // no said feature for inline assembly loops\n // cb = 1 - don't breaker\n // cb = 0 - break\n let cb := 1\n\n // get the keccak hash to get the contents of the array\n mstore(0x0, _preBytes.slot)\n let sc := keccak256(0x0, 0x20)\n\n let mc := add(_postBytes, 0x20)\n let end := add(mc, mlength)\n\n // the next line is the loop condition:\n // while(uint256(mc < end) + cb == 2)\n for {} eq(add(lt(mc, end), cb), 2) {\n sc := add(sc, 1)\n mc := add(mc, 0x20)\n } {\n if iszero(eq(sload(sc), mload(mc))) {\n // unsuccess:\n success := 0\n cb := 0\n }\n }\n }\n }\n }\n default {\n // unsuccess:\n success := 0\n }\n }\n\n return success;\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/contracts/docs/NativeStakingSSVStrategyHierarchy.svg b/contracts/docs/NativeStakingSSVStrategyHierarchy.svg index 68f386df5c..c270349d0f 100644 --- a/contracts/docs/NativeStakingSSVStrategyHierarchy.svg +++ b/contracts/docs/NativeStakingSSVStrategyHierarchy.svg @@ -16,119 +16,119 @@ Governable ../contracts/governance/Governable.sol - + -286 +299 FeeAccumulator ../contracts/strategies/NativeStaking/FeeAccumulator.sol - + -288 +301 NativeStakingSSVStrategy ../contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol - + -288->286 +301->299 - + -289 +302 <<Abstract>> ValidatorAccountant ../contracts/strategies/NativeStaking/ValidatorAccountant.sol - + -288->289 +301->302 - + -217 +226 <<Abstract>> InitializableAbstractStrategy ../contracts/utils/InitializableAbstractStrategy.sol - + -288->217 +301->226 - + -291 +304 <<Abstract>> ValidatorRegistrator ../contracts/strategies/NativeStaking/ValidatorRegistrator.sol - + -289->291 +302->304 - + -291->20 +304->20 - + -348 +361 <<Abstract>> Pausable ../node_modules/@openzeppelin/contracts/security/Pausable.sol - + -291->348 +304->361 - + -216 +225 <<Abstract>> Initializable ../contracts/utils/Initializable.sol - + -217->20 +226->20 - + -217->216 +226->225 - + -353 +366 <<Abstract>> Context ../node_modules/@openzeppelin/contracts/utils/Context.sol - + -348->353 +361->366 diff --git a/contracts/docs/NativeStakingSSVStrategySquashed.svg b/contracts/docs/NativeStakingSSVStrategySquashed.svg index c4ba4dcb15..35836684bf 100644 --- a/contracts/docs/NativeStakingSSVStrategySquashed.svg +++ b/contracts/docs/NativeStakingSSVStrategySquashed.svg @@ -93,8 +93,8 @@    resetStakeETHTally() <<onlyStakingMonitor>> <<ValidatorRegistrator>>    stakeEth(validators: ValidatorStakeData[]) <<onlyRegistrator, whenNotPaused, nonReentrant>> <<ValidatorRegistrator>>    registerSsvValidators(publicKeys: bytes[], operatorIds: uint64[], sharesData: bytes[], ssvAmount: uint256, cluster: Cluster) <<onlyRegistrator, whenNotPaused>> <<ValidatorRegistrator>> -    exitSsvValidator(publicKey: bytes, operatorIds: uint64[]) <<onlyRegistrator, whenNotPaused>> <<ValidatorRegistrator>> -    removeSsvValidator(publicKey: bytes, operatorIds: uint64[], cluster: Cluster) <<onlyRegistrator, whenNotPaused>> <<ValidatorRegistrator>> +    exitSsvValidators(publicKeys: bytes[], operatorIds: uint64[]) <<onlyRegistrator, whenNotPaused, nonReentrant>> <<ValidatorRegistrator>> +    removeSsvValidators(publicKeys: bytes[], operatorIds: uint64[], cluster: Cluster) <<onlyRegistrator, whenNotPaused, nonReentrant>> <<ValidatorRegistrator>>    depositSSV(operatorIds: uint64[], ssvAmount: uint256, cluster: Cluster) <<onlyStrategist>> <<ValidatorRegistrator>>    setFuseInterval(_fuseIntervalStart: uint256, _fuseIntervalEnd: uint256) <<onlyGovernor>> <<ValidatorAccountant>>    doAccounting(): (accountingValid: bool) <<onlyRegistrator, whenNotPaused, nonReentrant>> <<ValidatorAccountant>> diff --git a/contracts/docs/plantuml/depositTimeline.png b/contracts/docs/plantuml/depositTimeline.png new file mode 100644 index 0000000000..f10dd859dd Binary files /dev/null and b/contracts/docs/plantuml/depositTimeline.png differ diff --git a/contracts/docs/plantuml/depositTimeline.puml b/contracts/docs/plantuml/depositTimeline.puml new file mode 100644 index 0000000000..7d519768d0 --- /dev/null +++ b/contracts/docs/plantuml/depositTimeline.puml @@ -0,0 +1,63 @@ +@startuml + +Title Deposit to a Validator Timeline + +scale 5 as 50 pixels + +robust "Strategy contract" as strat +' note top of strat : stakeETH +robust "Deposit contract" as con +robust "Validator deposit on Beacon Chain" as beacon +@10 <-> @20 : pending deposit +robust "Slot of first pending deposit" as queue +robust "Deposit block" as mb +robust "Earliest proof" as fpd +@10 <-> @25 : min gap for proof +robust "Later proof" as fpd2 +@15 <-> @30 : min gap for proof +robust "Later proof" as fpd3 +@25 <-> @40 : min gap for proof + + +@0 +strat is UNKNOWN +con is UNKNOWN +beacon is UNKNOWN +queue is "Before Deposit < 10" +mb is "Verify Fail" +fpd is "Verify Fail" +fpd2 is "Verify Fail" +fpd3 is "Verify Fail" + +@10 +strat is PENDING +con is DEPOSITED +beacon is PENDING +mb is "Verify Success" + +@15 +queue is "Same as Deposit = 10" + +@20 +beacon is PROCESSED + +@25 +queue is "After Deposit > 10" +' @46 +fpd is "Verify Success" + +@30 +strat is VERIFIED +fpd2 is "Verify Success" + +@40 +fpd3 is "Verify Success" + + +' queue@15 -> strat@50 : Mapped block +' queue@45 -> strat@50 : Slot of First Pending Deposit + +' highlight 10 to 20 : mapped\nblocks +highlight 15 to 25 : deposits from\nsame block\nprocessed + +@enduml diff --git a/contracts/docs/plantuml/oethContracts.png b/contracts/docs/plantuml/oethContracts.png index 1434af75ba..b32015ffdd 100644 Binary files a/contracts/docs/plantuml/oethContracts.png and b/contracts/docs/plantuml/oethContracts.png differ diff --git a/contracts/docs/plantuml/oethContracts.puml b/contracts/docs/plantuml/oethContracts.puml index af4b2d66f7..81d71aa162 100644 --- a/contracts/docs/plantuml/oethContracts.puml +++ b/contracts/docs/plantuml/oethContracts.puml @@ -6,12 +6,12 @@ !$changedColor = Orange !$thirdPartyColor = WhiteSmoke -' legend -' blue - Origin -' ' green - new -' ' orange - changed -' white - 3rd Party -' end legend +legend +blue - Origin +green - New +' orange - Changed +white - 3rd Party +end legend title "Origin Ether Contract Dependencies" @@ -19,9 +19,6 @@ object "OETHZapper" as zap <> #$originColor { assets: ETH } -' object "ARM Router" as router <><> #$newColor { -' } - object "OETH ARM" as arm <><> #$originColor { assets: WETH, OETH } @@ -31,8 +28,8 @@ object "OETHFixedRateDripper" as drip <><> #$originColor { asset: WETH } -object "OETHVaultValueChecker" as checker <> #$originColor { -} +' object "OETHVaultValueChecker" as checker <> #$originColor { +' } object "WOETH" as woeth <><> #$originColor { asset: OETH @@ -67,14 +64,14 @@ object "CurveAMOStrategy" as amoStrat <><> #$originColor { Rewards: CRV } -object "NativeStakingStrategy" as nativeStrat <><> #$originColor { - assets: WETH, ETH - Rewards: ETH, SSV -} +' object "NativeStakingStrategy" as nativeStrat <><> #$originColor { +' assets: WETH, ETH +' Rewards: ETH, SSV +' } -object "FeeAccumulator" as feeAcc <><> #$originColor { - assets: ETH -} +' object "FeeAccumulator" as feeAcc <><> #$originColor { +' assets: ETH +' } object "NativeStakingStrategy2" as nativeStrat2 <><> #$originColor { assets: WETH, ETH @@ -94,6 +91,30 @@ object "FeeAccumulator3" as feeAcc3 <><> #$originColor { assets: ETH } +object "CompoundingStakingSSVStrategy" as compStrat <><> #$originColor { + assets: WETH, ETH + Rewards: SSV +} +object "CompoundingStakingStrategyView" as compView <> #$originColor { + +} +object "BeaconProofs" as proofs <> #$originColor { + +} + +' object "DepositContract" as deposit <> #$thirdPartyColor { +' asset : ETH +' } + +' object "WithdrawalRequest" as withdraw <> #$thirdPartyColor { + +' } + +' object "BeaconRoots" as roots <> #$thirdPartyColor { + +' } + + ' ' Oracle ' object "OETHOracleRouter" as oracle <> #$originColor { ' pairs: @@ -128,19 +149,10 @@ object "FeeAccumulator3" as feeAcc3 <><> #$originColor { ' ' } ' ' SSV -' object "SSV Network" as ssvNet <> #$thirdPartyColor { -' assets: ETH, SSV -' } - -' ' SSV -' object "SSV Network" as ssvNet <> #$thirdPartyColor { +' object "SSV Network" as ssvNet <><> #$thirdPartyColor { ' assets: ETH, SSV ' } -' object "Deposit" as bDep <> #$thirdPartyColor { -' assets: ETH -' } - ' ' Assets ' object "WETH9" as weth <> { @@ -163,7 +175,6 @@ zap ..> oeth zap ..> oethv ' zap .....> weth -' router ..> arm arm ..> oethv ' drip .....> weth @@ -193,24 +204,31 @@ harv <..> amoStrat oethv <...> amoStrat oeth <... amoStrat -harv <..> nativeStrat -oethv <...> nativeStrat -nativeStrat <..> feeAcc +' harv <..> nativeStrat +' oethv <...> nativeStrat +' nativeStrat <..> feeAcc ' nativeStrat ..> ssvNet -' nativeStrat ..> bDep +' nativeStrat ..> deposit harv <..> nativeStrat2 oethv <...> nativeStrat2 nativeStrat2 <..> feeAcc2 ' nativeStrat2 ...> ssvNet -' nativeStrat2 ...> bDep - +' nativeStrat2 ...> deposit harv <..> nativeStrat3 oethv <...> nativeStrat3 nativeStrat3 <..> feeAcc3 ' nativeStrat2 ...> ssvNet -' nativeStrat2 ...> bDep +' nativeStrat2 ...> deposit + +oethv <...> compStrat +compStrat ..> proofs +compStrat <.. compView +' compStrat ..> ssvNet +' compStrat ...> deposit +' compStrat ...> withdraw +' compStrat ...> roots ' cvxStrat ...> crvPool ' cvxStrat ....> cvxPool diff --git a/contracts/docs/plantuml/oethProcesses-admin.png b/contracts/docs/plantuml/oethProcesses-admin.png index 3945573220..09f722e5ac 100644 Binary files a/contracts/docs/plantuml/oethProcesses-admin.png and b/contracts/docs/plantuml/oethProcesses-admin.png differ diff --git a/contracts/docs/plantuml/oethProcesses-deposit-existing.png b/contracts/docs/plantuml/oethProcesses-deposit-existing.png new file mode 100644 index 0000000000..03cabfd407 Binary files /dev/null and b/contracts/docs/plantuml/oethProcesses-deposit-existing.png differ diff --git a/contracts/docs/plantuml/oethProcesses-deposit-new.png b/contracts/docs/plantuml/oethProcesses-deposit-new.png new file mode 100644 index 0000000000..e4346831dc Binary files /dev/null and b/contracts/docs/plantuml/oethProcesses-deposit-new.png differ diff --git a/contracts/docs/plantuml/oethProcesses-register.png b/contracts/docs/plantuml/oethProcesses-register.png index 03ac1f8f61..110c544ba1 100644 Binary files a/contracts/docs/plantuml/oethProcesses-register.png and b/contracts/docs/plantuml/oethProcesses-register.png differ diff --git a/contracts/docs/plantuml/oethProcesses-rewards.png b/contracts/docs/plantuml/oethProcesses-rewards.png index 09279d0b1a..33eb6de13f 100644 Binary files a/contracts/docs/plantuml/oethProcesses-rewards.png and b/contracts/docs/plantuml/oethProcesses-rewards.png differ diff --git a/contracts/docs/plantuml/oethProcesses-verify-balances.png b/contracts/docs/plantuml/oethProcesses-verify-balances.png new file mode 100644 index 0000000000..5ba1884d31 Binary files /dev/null and b/contracts/docs/plantuml/oethProcesses-verify-balances.png differ diff --git a/contracts/docs/plantuml/oethProcesses-verify-deposit.png b/contracts/docs/plantuml/oethProcesses-verify-deposit.png new file mode 100644 index 0000000000..6e8adf6a91 Binary files /dev/null and b/contracts/docs/plantuml/oethProcesses-verify-deposit.png differ diff --git a/contracts/docs/plantuml/oethProcesses-verify-validator.png b/contracts/docs/plantuml/oethProcesses-verify-validator.png new file mode 100644 index 0000000000..ee37573927 Binary files /dev/null and b/contracts/docs/plantuml/oethProcesses-verify-validator.png differ diff --git a/contracts/docs/plantuml/oethProcesses-withdraw.png b/contracts/docs/plantuml/oethProcesses-withdraw.png index 8e946d90f2..3e73cbbe18 100644 Binary files a/contracts/docs/plantuml/oethProcesses-withdraw.png and b/contracts/docs/plantuml/oethProcesses-withdraw.png differ diff --git a/contracts/docs/plantuml/oethProcesses.png b/contracts/docs/plantuml/oethProcesses.png index 6459787503..fd7311fa94 100644 Binary files a/contracts/docs/plantuml/oethProcesses.png and b/contracts/docs/plantuml/oethProcesses.png differ diff --git a/contracts/docs/plantuml/oethProcesses.puml b/contracts/docs/plantuml/oethProcesses.puml index 823e622ea9..d132c08799 100644 --- a/contracts/docs/plantuml/oethProcesses.puml +++ b/contracts/docs/plantuml/oethProcesses.puml @@ -8,92 +8,41 @@ title "Origin ETH processes" actor "Anyone" as sender actor "Block\nBuilder\n(MEV)" as mev actor "Registrator\n(Relayer)" as reg <> -actor "Admin\n(5/8 Safe)" as admin <> actor "Strategist\n(2/8 Safe)" as strategist <> actor "Governor\n(Timelock)" as gov <> actor "Treasury" as treasury <> participant "API" as api <> -actor "Operators" as ssvOp <> box "Execution Chain" -participant "Harvester" as harv <> -participant "Dripper" as drip <> participant "Vault" as vault <> -participant "Native\nStaking SSV\nStrategy" as nativeStrat <> -participant "Fee\nAccumulator" as feeAccum <> +participant "Compounding\nStaking\nStrategy" as compStrat <> participant "SSV Network" as ssvNet <> participant "Token" as ssv <> participant "WETH" as weth <> -participant "Deposit" as dep <> +participant "BeaconProofs" as proofs <> +participant "Beacon\nDeposit\nContract" as dep <> +participant "Beacon\nConsolidation\nRequests" as consol <> +participant "Beacon\nWithdrawal\nRequests" as withdraw <> +participant "Beacon\nBlock\nRoots" as roots <> end box box "Beacon chain" participant "Validator" as val <> end box -group Governor initializes the Native Staking Strategy - -gov -> nativeStrat : initialize() -activate nativeStrat -nativeStrat -> ssv : approve(\nSSV Network,\namount) -activate ssv -note right : Native Staking Strategy approves\nSSV Network to spend\nSSV tokens -return -nativeStrat -> ssvNet : setFeeRecipientAddress(\nFeeAccumulator) -activate ssvNet -note right : set FeeAccumulator\nto receive MEV rewards -return -return - -gov -> ssv : transfer(\nfrom\nto\namount) -activate ssv -note right : transfer SSV tokens\nfrom Governor\nto Native Staking Strategy -return - -end group - -group Governor setup of the Native Staking Strategy - -gov -> nativeStrat : setHarvesterAddress(\nharvester) -activate nativeStrat -return - -gov -> nativeStrat : setFuseInterval(\nstart, end) -activate nativeStrat -return - -gov -> nativeStrat : setRegistrator(\nregistrator) -activate nativeStrat -return - -gov -> nativeStrat : setStakingMonitor(\ngovernor) -activate nativeStrat -return - -gov -> nativeStrat : setStakeETHThreshold(\nmax) -activate nativeStrat -return - -end group - -group Staking monitor resets staked ETH - -admin -> nativeStrat : resetStakeETHTally() -activate nativeStrat -note right: resets the ETH that\ncan be staked going forward -return - -end group - group Registrator creates a new SSV validator -reg -> api: POST\neth/staking/ssv/request/create\nuuid,\nvalidatorsCount,\ntype,\nwithdrawalAddress,\nfeeRecipientAddress,\nssvOwnerAddress,\noperationPeriodInDays +reg -> api: POST\neth/staking/ssv/request/create\nuuid,\nvalidatorsCount,\ntype,\nwithdrawalAddress,\nfeeRecipientAddress,\nssvOwnerAddress,\noperationPeriodInDays,\namountPerValidator,\nwithdrawalCredentialsType activate api note right -withdrawalAddress is Native Staking Strategy -feeRecipientAddress is FeeAccumulator contract -ssvOwnerAddress is Native Staking Strategy -type is without-encrypt-key +withdrawalAddress is Staking Strategy +feeRecipientAddress is Staking Strategy +ssvOwnerAddress is Staking Strategy +type is with-encrypt-key +ecdhPublicKey public key for encrypting the validator keys +operationPeriodInDays is 0 if not the first validator in the cluster +amountPerValidator 32 ETH in Gwei to limit front-run deposits +withdrawalCredentialsType 0x02 for compounding validators end note api -> api: private key note right : generate a validator private key @@ -106,8 +55,8 @@ activate api return status,\npubkey\nvalidatorRegistration,\nshareData note right : validatorRegistration contains the operatorIds and cluster details -reg -> nativeStrat : registerSsvValidators(\npublicKeys[],\noperatorIds,\nsharesData[],\namount,\ncluster) -activate nativeStrat +reg -> compStrat : registerSsvValidator(\npublicKey,\noperatorIds,\nsharesData,\nssvAmount,\ncluster) +activate compStrat note right cluster data: The number of validators in the cluster @@ -116,270 +65,481 @@ The last index calculated for the cluster Flag indicating whether the cluster is active The SSV balance of the cluster end note -nativeStrat -> ssvNet : bulkRegisterValidator(\npublicKeys[],\noperatorIds,\nsharesData[],\namount,\ncluster) +compStrat -> ssvNet : registerValidator(\npublicKey,\noperatorIds,\nsharesData,\nssvAmount,\ncluster) activate ssvNet ssvNet -> ssv : transferFrom(\nfrom\nto\namount) activate ssv -note right: transfer SSV tokens\nfrom NodeDelegator\nto SSV Network +note right: for the first validator\ntransfer SSV tokens\nfrom NodeDelegator\nto SSV Network return return return end group -... 60 minutes ... +... 20 minutes ... -group Registrator stakes to a new SSV validator +group Deposit to a new SSV validator reg -> api: GET\neth/staking/ssv/request/deposit-data/uuid activate api return status,\ndepositData -note right : depositData contains the signature and depositDataRoot +note right : depositData contains the signature and deposit root -reg -> nativeStrat : stakeEth([\npubkey,\nsignature,\ndepositDataRoot]) -activate nativeStrat -nativeStrat -> nativeStrat -note right : validate staked ETH under the threshold -nativeStrat -> weth : withdraw(32 ETH * number of validators) +reg -> compStrat : stakeEth(\npubkey,\nsignature,\ndeposit root,\namountGwei) +activate compStrat +compStrat -> weth : withdraw(amountGwei * 1 Gwei) activate weth note right : WETH burned for ETH return ETH -loop for each validator - -nativeStrat -> dep : stake(\npubkey,\nwithdrawal_credentials,\nsignature,\ndepositDataRoot) +compStrat -> dep : deposit(\npubkey,\nwithdrawal_credentials,\nsignature,\ndeposit root) activate dep note left -32 ETH from Native Staking Strategy is sent to Beacon Deposit. -Withdrawal credential is the Native Staking Strategy +32 ETH from Staking Strategy is sent to Beacon Deposit. +Withdrawal credential is the Staking Strategy. end note return -end + +note over compStrat +Calculate deposit slot from block timestamp. +Store deposit pubKeyHash, amountGwei, slot and depositRoot. +end note + return -note over val : Pending Deposit +... Beacon chain slot created for the block ... -... 1024 execution blocks (~4 hours) ... -... 32 consensus epochs (~3.5 hours) ... +note over val : Pending deposit added\nBeaconBlock.state.PendingDeposits[index] -dep -> val : 32 ETH +... 13 minutes to many days depending on the deposit queue ... -note over val : Pending Activation +note over val +Validator registered in +BeaconBlock.state.validators[validatorIndex] +Validator balance created in +BeaconBlock.state.balances[balanceIndex] +end note ... four validators are activated each epoch from the Validator Queue (1-10 days) ... -note over val : Active +note over val +Validator pending activation +Will activate when another 31 ETH is deposited +end note end group +group Deposit more to existing validator -group Registrator deposits more SSV to SSV cluster +reg -> api: POST\neth/staking/direct/increment-request/create\nuuid\npubkeys,amountPerValidator,withdrawalAddress +activate api +note right: amountPerValidator is in Gwei +return -strategist -> ssv : transfer(\nto\namount) -activate ssv -note right : transfer SSV tokens\nfrom Treasury\nto Native Staking Strategy -return +reg -> api: GET\neth/staking/eth/staking/increment-request/status/uuid +activate api +return status,\ndepositData +note right : depositData contains the signature and deposit root -group SSV ClusterScanner -reg -> ssvNet : getPastEvents(filter) -activate ssvNet -note right : get all events where the ownerAddress\nis the Native Staking Strategy -return events +reg -> compStrat : stakeEth(\npubkey,\nsignature,\ndeposit root,\namountGwei) +activate compStrat +compStrat -> weth : withdraw(\namountGwei * 1 Gwei) +activate weth +note right : WETH burned for ETH +return ETH -reg -> reg : getCluster(events):\n cluster -note right -cluster data: - validatorCount - networkFeeIndex - index - active - balance +compStrat -> dep : deposit(\npubkey,\nwithdrawal_credentials,\nsignature,\ndeposit root) +activate dep +note left +ETH from Staking Strategy is sent to Beacon Deposit. +Withdrawal credential is the Staking Strategy. end note -end group +return -strategist -> nativeStrat : depositSSV(\noperatorIds,\namount,\ncluster) -activate nativeStrat -nativeStrat -> ssvNet : deposit(\nclusterOwner,\noperatorIds,\namount,\ncluster) -activate ssvNet -note right -clusterOwner is Native Staking Strategy -operatorIds are the SSV Operators -amount of SSV tokens +note over compStrat +Calculate deposit slot from block timestamp. +Store deposit pubKeyHash, amountGwei, slot and depositRoot. end note -ssvNet -> ssv : transferFrom(\nfrom\nto\namount) -activate ssv -note right: transfer SSV tokens\nfrom Native Staking Strategy\nto SSV Network -return return -return -end group -group Consensus Rewards +... Beacon chain slot created for the block ... -note over val -attesting to blocks -participating in sync committees -end note -val -> val : ETH +note over val : Pending deposit added\nBeaconBlock.state.PendingDeposits[index] -... swept every 8-10 days ... +... 13 minutes to many days depending on the deposit queue ... -note over val : partial withdraw of excess ETH\nfrom validator to the Native Staking Strategy -val -> nativeStrat : ETH +note over val: Validator balance updated\nBeaconBlock.state.balances[balanceIndex] -note over nativeStrat : Native Staking Strategy's\nWETH balance does not change +... Start of the epoch ... + +note over val: Validator activated if >= 32 ETH end group -group Registrator full withdraw from validator +group Verify validator -reg -> nativeStrat : exitSsvValidator(\npublicKey\noperatorIds) -activate nativeStrat -nativeStrat -> ssvNet : exitValidator(\npublicKey\noperatorIds) -activate ssvNet -return -return +sender -> val : getBlockV2(slot) +note left : Any slot after the\nvalidator was processed +activate val +return beacon block -ssvOp -> ssvOp : sign(\npk,\nexit message) -note right : voluntary exit message signed by the validator private key -ssvOp -> val : signed voluntary exit message +sender -> val : getStateV2(slot) +note left : Any slot after the\nvalidator was processed activate val -return +return beacon state -... wait until validator has exited.\nmin four epochs (~25 min), currently 1.5 hours but can take a number of days depending on the number of validators in the exit queue ... +sender -> sender : generateValidatorPubKeyProof(\nvalidator index,\nbeacon block,\nbeacon state) -reg -> nativeStrat : removeSsvValidator(\npublicKey,\noperatorIds,\ncluster) -activate nativeStrat -nativeStrat -> ssvNet : removeValidator(\npublicKey\noperatorIds,\ncluster) -activate ssvNet -note right : stop paying SSV to Operators\n and reduce required SSV collateral -return -return +sender -> compStrat : verifyValidator(\nnextBlockTimestamp,\nvalidatorIndex,\npubKeyHash,\nvalidatorPubKeyProof) +activate compStrat -... wait for the validator to be swept on the Beacon chain\ncurrent time is every 8.5 days ... +note over compStrat : check the validator is STAKED -val -> nativeStrat : ETH -note left : transfer staked ETH and rewards\nfrom Beacon Deposit\nto Native Staking Strategy +compStrat -> roots : get(nextBlockTimestamp) +note left +Timestamp of any block after the block we want to verify. +This is because the timestamp will get the parent block root. +end note +activate roots +return parent block root + +compStrat -> proofs : verifyValidatorPubkey(\n beacon block root,\n pubKeyHash,\n validatorPubKeyProof,\n validatorIndex,\n withdrawalAddress,\n validatorType) +activate proofs +note over proofs +Index BeaconBlock.state.validators[validatorIndex].pubkey +Verify merkle proof of pubKeyHash, strategy address +and validatorType against the beacon block root. +end note +return -note over nativeStrat : Native Staking Strategy's\nWETH balance does not change +note over compStrat +Add the validator to the active validators. +end note end group -group Execution Rewards +group Verify validator deposit -mev -> feeAccum : ETH -note right : MEV rewards +sender -> val : getBlockV2(deposit processed slot) +note left : Any slot after the deposit was\nprocessed on the beacon chain +activate val +return deposit beacon block -sender -> nativeStrat : ETH -note right : tx priority fees +sender -> val : getStateV2(deposit processed slot) +note left : Any slot after the deposit was\nprocessed on the beacon chain +activate val +return deposit beacon state -note over nativeStrat : Native Staking Strategy's WETH balance\ndoes not change from either +sender -> val : getBlockV2(first pending validator created slot) +note left : Any slot after the validator of\nthe first pending deposit was created +activate val +return validator beacon block -end group +sender -> val : getStateV2(first pending validator created slot) +note left :Any slot after the validator of\nthe first pending deposit was created +activate val +return validator beacon state -group Registrator does accounting of consensus rewards and validator withdrawals +sender -> sender : generateFirstPendingDepositProof(\n deposit beacon block,\n deposit beacon state) +sender -> sender : generateVerifyValidatorWithdrawableProof(\n validator beacon block,\n validator beacon state) +sender -> sender : generateVerifyValidatorWithdrawableProof(\n deposit beacon block,\n deposit beacon state) -reg -> nativeStrat : doAccounting() -activate nativeStrat +sender -> compStrat : verifyDeposit(\n depositID,\n depositProcessedSlot,\n firstDepositValidatorCreatedSlot,\n firstPendingDeposit,\n strategyValidatorData) +activate compStrat +note right +FirstPendingDepositProofData: + slot + validatorIndex + pubKeyHash + pendingDepositPubKeyProof + withdrawableEpochProof + validatorPubKeyProof + +StrategyValidatorProofData: + withdrawableEpoch + withdrawableEpochProof +end note -note over nativeStrat -ETH received since last accounting = current ETH balance - previous consensus rewards -validator withdrawals = ETH received / 32 ETH +note over compStrat +Calculate next block timestamp from the depositProcessedSlot. end note -nativeStrat -> weth : deposit(\nwithdrawn ETH) -activate weth -note left : convert ETH from full withdrawals to WETH +compStrat -> roots : get(nextBlockTimestamp) +note left +Timestamp of block after the depositProcessedSlot. +This is because the timestamp will get the parent block root. +end note +activate roots +return beacon block root of the depositProcessedSlot + +compStrat -> proofs : verifyFirstPendingDeposit(\n beacon block root,\n firstPendingDepositSlot,\n pubKeyHash,\n firstPendingDepositSlotProof) +activate proofs +alt if empty pending deposit proof + note over proofs + verify the first pending deposit is empty + BeaconBlock.state.PendingDeposits[0] + end note +else if first pending deposit proof + note over proofs + Verify the pubKeyHash of the first pending deposit + BeaconBlock.state.PendingDeposits[0].pubkey + Verify the slot of the first pending deposit to + the third witness of the pubKeyProof + end note +end return -nativeStrat -> weth : transfer(\nvault,\nwithdrawn ETH) -activate weth -note left : transfer withdrawn WETH\nfrom Native Staking Strategy\nto OETH Vault -return +alt if pending deposits + + note over compStrat + Calculate next block timestamp from the firstDepositValidatorCreatedSlot. + end note + compStrat -> roots : get(nextBlockTimestamp) + note left + Timestamp of block after the firstDepositValidatorCreatedSlot. + This is because the timestamp will get the parent block root. + end note + activate roots + return beacon block root of the firstDepositValidatorCreatedSlot + + compStrat -> proofs : verifyValidatorWithdrawable(\n beacon block root,\n firstPendingDeposit.validatorIndex,\n firstPendingDeposit.pubKeyHash,\n farFuture,\n firstPendingDeposit.withdrawableEpochProof,\n firstPendingDeposit.validatorPubKeyProof) + activate proofs + + note over proofs + Verify the validator's withdrawable epoch to the beacon block root. + BeaconBlock.state.validators[validatorIndex].withdrawableEpoch + Verify the validator's public key to the third witness of the withdrawableEpochProof + end note + return +end -note over nativeStrat -Add remaining ETH to consensus rewards. -ETH from consensus rewards stays in the Native Staking Strategy. + +compStrat -> proofs : verifyValidatorWithdrawable(\n beacon block root,\n strategyValidator.validatorIndex,\n strategyValidator.withdrawableEpoch,\n strategyValidator.withdrawableEpochProof) +activate proofs + +note over proofs +Verify the validator's withdrawable epoch to the beacon block root. + BeaconBlock.state.validators[validatorIndex].withdrawableEpoch end note +return -return accounting valid flag +alt if strategy validator is exiting + note over compStrat + Store the withdrawable epoch on the deposit + Store the validator as EXITING + end note +else if strategy validator not exiting + note over compStrat + Check stored deposit slot is before the depositProcessedSlot. + Remove deposit from storage. + end note +end + +return end group -group Harvester collects ETH rewards +group Update balances + +sender -> compStrat : snapBalances() +activate compStrat +compStrat -> roots : get(block timestamp) +activate roots +return parent block root +note over compStrat +Store ETH balance against the parent block root. +Store block timestamp of snap. +end note +return + +sender -> val : getBlockV2(slot) +note left : slot before\nsnapBalances +activate val +return beacon block -sender -> harv : harvestAndTransfer(\nstrategy) -activate harv -harv -> nativeStrat : collectRewardTokens() -activate nativeStrat +sender -> val : getStateV2(slot) +note left : slot before\nsnapBalances +activate val +return beacon state + +sender -> sender : generateFirstPendingDepositProof(\n beacon block,\n beacon state) +note right : proof against beacon block root +sender -> sender : generateValidatorBalanceContainerProof(\n beacon block,\n beacon state) +note right : proof against beacon block root +loop active validator indexes +sender -> sender : generateValidatorBalanceProof(\n validator index,\n beacon block,\n beacon state) +note right: proof against root of\nbeaconBlock.state.balances +end -nativeStrat -> feeAccum : collect() -activate feeAccum -feeAccum -> nativeStrat : ETH -note right : send all execution rewards in the FeeAccumulator\nto the Native Staking Stragegy -return execution rewards +sender -> compStrat : verifyBalances(\n blockRoot,\n firstPendingDepositSlot,\n firstPendingDepositSlotProof,\n balancesContainerRoot,\n validatorContainerProof,\n validatorBalanceLeaves[],\n validatorBalanceProofs[]) +activate compStrat + +alt if any deposits + compStrat -> proofs : verifyFirstPendingDepositSlot(\n beacon block root,\n firstPendingDepositSlot,\n firstPendingDepositSlotProof) + activate proofs + note over proofs + Encode slot to little endian padded to 32 bytes. + Index BeaconBlock.state.PendingDeposits[0].slot + Verify merkle proof of slot against the beacon block root. + end note + return + + loop strategy deposits + note over compStrat + Check the first pending deposit slot is before the stored deposit slot. + Add deposit amount to total deposits. + end note + end +end -note over nativeStrat : total rewards = execution rewards + consensus rewards +alt if active validators + compStrat -> proofs : verifyBalancesContainer(\n beacon block root,\n balancesContainerRoot,\n balancesContainerProof) + activate proofs + note over proofs + Index BeaconBlock.state.balances + Verify merkle proof of balance container root against the beacon block root. + end note + return + + loop active validator indexes + + compStrat -> proofs : verifyValidatorBalance(\n beacon block root,\n balancesContainerRoot,\n balanceProof,\n validatorIndex) + activate proofs + note over proofs + Index is validatorIndex / 4 as there are four balances per leaf. + Verify validator balance against the balances container root. + Decode the validator balance from the balance leaf. + end note + return balance + + alt if balance is zero + note over compStrat + Remove from active validators. + end note + end + + note over compStrat + Add balance to total + end note + end +end -note over nativeStrat : reset consensus rewards to zero +note over compStrat + store strategy balance = + ETH balance from snap + + WETH balance + + total deposits + + total validator balances +end note -nativeStrat -> weth : deposit(\ntotal rewards) -activate weth -note left : convert ETH rewards to WETH return -nativeStrat -> weth : transfer(\nHarvester,\ntotal ETH rewards) -activate weth -note left : transfer rewards as WETH\nfrom Native Staking Strategy\nto Harvester -return +end group -return +group Registrator deposits more SSV to SSV cluster -harv -> weth : transfer(\nDripper,\nETH rewards) -activate weth -note left : transfer WETH rewards\nfrom Harvester\nto Dripper -return +group SSV ClusterScanner +strategist -> ssvNet : getPastEvents(filter) +activate ssvNet +note right : get all events where the ownerAddress\nis the Staking Strategy +return events -return +strategist -> strategist : getCluster(events):\n cluster +note right +cluster data: + validatorCount + networkFeeIndex + index + active + balance +end note +end group +strategist -> ssvNet : deposit(\nclusterOwner,\noperatorIds,\namount,\ncluster) +activate ssvNet +note right +clusterOwner is Staking Strategy +operatorIds are the SSV Operators +amount of SSV tokens +end note +ssvNet -> ssv : transferFrom(\nfrom\nto\namount) +activate ssv +note right: transfer SSV tokens\nfrom Staking Strategy\nto SSV Network +return +return end group -group Collect and rebase +group Consensus Rewards -sender -> drip : collectAndRebase() -activate drip +note over val +attesting to blocks +participating in sync committees +end note +val -> val : ETH -drip -> weth : transfer(\nvault,\nstreamed ETH) -note left : Stream ETH from last collect to now -activate weth -note over drip : Recalculate 7 day drip rate\nbased on WETH balance -return +... swept every 8-10 days ... -drip -> vault : rebase() -activate vault -return +note over val : partial withdraw of excess ETH\nfrom validator to the Staking Strategy +val -> compStrat : ETH -return +note over compStrat : Staking Strategy's\nWETH balance does not change end group -group Strategist pauses Native Staking Strategy +group Registrator full or partial withdraw from validator -strategist -> nativeStrat : pause() -activate nativeStrat +reg -> compStrat : validatorWithdrawal(\npublicKey\namount) +activate compStrat +note right: zero amount means full withdraw +compStrat -> withdraw : publicKey, amount +activate withdraw +return return -end group +... wait until withdrawal request have been processed.\nmin four epochs (~25 min), currently many days but can take a number of days depending on the number of validators in the exit queue ... + +val -> compStrat : ETH +note left : transfer withdrawn ETH\nfrom validator\nto Staking Strategy -group Strategist unpauses Native Staking Strategy +strategist -> vault : withdrawFromStrategy(\nstrategy,\nasset,\namount) +activate vault +vault -> compStrat : withdraw(\nasset,\namount) +activate compStrat +compStrat -> weth : deposit(amount) +activate weth +note left : convert all ETH to WETH +return +compStrat -> weth : transfer(\nvault,\namount) +activate weth +note left : transfer WETH\nfrom Staking Strategy\nto OETH Vault +return +return +return + + +reg -> compStrat : snapBalance() +activate compStrat +return -strategist -> nativeStrat : manuallyFixAccounting(0, 0, 0) -activate nativeStrat -note right : params _validatorsDelta, _consensusRewardsDelta\nand _ethToVaultAmount all set to zero -nativeStrat -> nativeStrat -note right : unpause +reg -> compStrat : verifyBalances(\nblockRoot,\nfirstPendingDepositSlot,\nfirstPendingDepositSlotProof,\nbalancesContainerRoot,\nvalidatorContainerProof,\nvalidatorBalanceLeaves[],\validatorBalanceProofs[]) +activate compStrat +note over compStrat +if validator balance is zero, +remove validator from active validators +and store as exited +end note return +reg -> compStrat : removeSsvValidator(\npublicKey,\noperatorIds,\ncluster) +activate compStrat +compStrat -> ssvNet : removeValidator(\npublicKey\noperatorIds,\ncluster) +activate ssvNet +note right : stop paying SSV to Operators\n and reduce required SSV collateral +return +return + +... wait for the validator to be swept on the Beacon chain\ncurrent time is every 9.5 days ... + end group @enduml \ No newline at end of file diff --git a/contracts/docs/plantuml/stakingDepositStates.png b/contracts/docs/plantuml/stakingDepositStates.png new file mode 100644 index 0000000000..d9d9406cdf Binary files /dev/null and b/contracts/docs/plantuml/stakingDepositStates.png differ diff --git a/contracts/docs/plantuml/stakingDepositStates.puml b/contracts/docs/plantuml/stakingDepositStates.puml new file mode 100644 index 0000000000..a801b684e2 --- /dev/null +++ b/contracts/docs/plantuml/stakingDepositStates.puml @@ -0,0 +1,10 @@ +@startuml + +title Compounding Staking Strategy Deposit States + +[*] --> PENDING : stakeEth + +PENDING --> VERIFIED : verifyDeposit +PENDING --> VERIFIED : verifyBalances + +@enduml \ No newline at end of file diff --git a/contracts/docs/plantuml/stakingStates.png b/contracts/docs/plantuml/stakingStates.png new file mode 100644 index 0000000000..9d9bda7df5 Binary files /dev/null and b/contracts/docs/plantuml/stakingStates.png differ diff --git a/contracts/docs/plantuml/stakingStates.puml b/contracts/docs/plantuml/stakingStates.puml new file mode 100644 index 0000000000..29af6dfbca --- /dev/null +++ b/contracts/docs/plantuml/stakingStates.puml @@ -0,0 +1,133 @@ +@startuml + +title Staking Strategy Validator States + +state "Registered SSV Validator" as RegisteredValidator +RegisteredValidator : validator REGISTERED +RegisteredValidator : call SSVNetwork.registerValidator + +state "Removed Invalid\nSSV Validator" as InvalidSSVValidator +InvalidSSVValidator : validator REMOVED +InvalidSSVValidator : call SSVNetwork.removeValidator + +state "Initial Deposit To Validator" as InitialDeposit +InitialDeposit : validator STAKED +InitialDeposit : validator index 0 +InitialDeposit : deposit PENDING +InitialDeposit : depositList added +InitialDeposit : firstDeposit true +InitialDeposit : 1 WETH convert to ETH +InitialDeposit : depositedWethAccountedFor decreased by deposit amount +InitialDeposit : lastVerifiedEthBalance increased by deposit amount +InitialDeposit : snappedBalance timestamp reset +InitialDeposit : call deposit contract with 1 ETH value + +state "Front-run Initial Deposit" as FrontRunDeposit +FrontRunDeposit : validator INVALID +FrontRunDeposit : deposit VERIFIED +FrontRunDeposit : depositList removed +FrontRunDeposit : lastVerifiedEthBalance reduced by deposit amount + +state "Removed Front-run Validator" as RemovedFrontRunValidator +RemovedFrontRunValidator : validator REMOVED +RemovedFrontRunValidator : call SSVNetwork.removeValidator + +state "Verified Validator" as VerifiedValidator +VerifiedValidator : validator VERIFIED +VerifiedValidator : validator index +VerifiedValidator : verifiedValidators added +VerifiedValidator : firstDeposit false + +state "Inactive Validator" as VerifiedDepositInactive +VerifiedDepositInactive : deposit VERIFIED +VerifiedDepositInactive : depositList removed + +state "Active Validator" as VerifiedDepositActive +VerifiedDepositActive : deposit ACTIVE +VerifiedDepositActive : depositList removed + +state "Verified Deposit to\nExiting Validator" as DepositToExitingValidator +DepositToExitingValidator : deposit VERIFIED +DepositToExitingValidator : depositList removed + +state "Deposit to Top-Up\nInactive Validator" as TopUpDepositInactive +TopUpDepositInactive : deposit PENDING +TopUpDepositInactive : depositList added +TopUpDepositInactive : deposit amount of WETH convert to ETH +TopUpDepositInactive : depositedWethAccountedFor decreased by deposit amount +TopUpDepositInactive : lastVerifiedEthBalance increased by deposit amount +TopUpDepositInactive : snappedBalance timestamp reset +TopUpDepositInactive : call deposit contract with deposit amount of ETH value + +state "Deposit to Top-Up\nActive Validator" as TopUpDepositActive +TopUpDepositActive : deposit PENDING +TopUpDepositActive : depositList added +TopUpDepositActive : deposit amount of WETH convert to ETH +TopUpDepositActive : depositedWethAccountedFor decreased by deposit amount +TopUpDepositActive : lastVerifiedEthBalance increased by deposit amount +TopUpDepositActive : snappedBalance timestamp reset +TopUpDepositActive : call deposit contract with deposit amount of ETH value + +state "Forced Exit with Deposit" as ForcedExitWithDeposit +state "Forced Exit no Deposit" as ForcedExitNoDeposit + +state "Exited Validator" as ExitedValidator +ExitedValidator : validator EXITED +ExitedValidator : verifiedValidators removed +ExitedValidator : lastVerifiedEthBalance deposits + validators + ETH balance +ExitedValidator : snappedBalance timestamp reset + +state "Partial Withdrawal\nfrom Validator" as PartialWithdrawal +PartialWithdrawal : request partial withdrawal + +state "Exiting Validator" as ExitingValidator +ExitingValidator : validator EXITING +ExitingValidator : request full exit + +state "Removed SSV Validator" as RemovedExitedValidator +RemovedExitedValidator : validator REMOVED +RemovedExitedValidator : call SSVNetwork.removeValidator + +' state "Snapped Balances" as SnappedBalances +' SnappedBalances : snappedBalance\n blockRoot\n timestamp\n ETH Balance + +' state "Verified Balances" as VerifiedBalances +' VerifiedBalances : snappedBalance timestamp reset +' VerifiedBalances : lastVerifiedEthBalance deposits + validators + ETH balance + +[*] --> RegisteredValidator : registerSsvValidator + +RegisteredValidator --> InitialDeposit : stakeETH(1 ETH) +RegisteredValidator --> InvalidSSVValidator : removeSsvValidator + +InitialDeposit --> VerifiedValidator : verifyValidator +InitialDeposit --> FrontRunDeposit : verifyValidator +FrontRunDeposit --> RemovedFrontRunValidator : removeSsvValidator +VerifiedValidator ---> VerifiedDepositInactive : verifyDeposit +VerifiedValidator --> ForcedExitWithDeposit + +TopUpDepositInactive <- VerifiedDepositInactive : stakeETH +TopUpDepositInactive -> VerifiedDepositInactive : verifyDeposit +VerifiedDepositInactive ---> ForcedExitNoDeposit +ForcedExitNoDeposit ---> ExitedValidator : verifyBalances +TopUpDepositInactive ---> ForcedExitWithDeposit +TopUpDepositActive --> ForcedExitWithDeposit +ForcedExitWithDeposit --> DepositToExitingValidator : verifyDeposit +VerifiedDepositInactive --> VerifiedDepositActive : verifyBalances + +TopUpDepositActive <- VerifiedDepositActive : stakeETH +TopUpDepositActive -> VerifiedDepositActive : verifyDeposit +VerifiedDepositActive --> ForcedExitNoDeposit + +DepositToExitingValidator --> ExitedValidator : verifyBalances + +VerifiedDepositActive -> PartialWithdrawal : validatorWithdrawal\n(amount > 0) +VerifiedDepositActive <- PartialWithdrawal +VerifiedDepositActive ---> ExitingValidator : validatorWithdrawal\n(amount = 0) +ExitingValidator --> ExitedValidator : verifyBalances +ExitedValidator --> RemovedExitedValidator : removeSsvValidator + +' SnappedBalances --> VerifiedBalances : verifyBalances +' VerifiedBalances --> SnappedBalances : snapBalances + +@enduml \ No newline at end of file diff --git a/contracts/docs/plantuml/stakingValidatorStates.png b/contracts/docs/plantuml/stakingValidatorStates.png new file mode 100644 index 0000000000..82d732fe10 Binary files /dev/null and b/contracts/docs/plantuml/stakingValidatorStates.png differ diff --git a/contracts/docs/plantuml/stakingValidatorStates.puml b/contracts/docs/plantuml/stakingValidatorStates.puml new file mode 100644 index 0000000000..e34c472f41 --- /dev/null +++ b/contracts/docs/plantuml/stakingValidatorStates.puml @@ -0,0 +1,23 @@ +@startuml + +title Compounding Validator States + +[*] --> REGISTERED : registerSsvValidator + +REGISTERED --> STAKED : stakeETH + +STAKED --> VERIFIED : verifyValidator +STAKED --> INVALID : verifyValidator + +VERIFIED --> ACTIVE : verifyBalances + +ACTIVE --> EXITING : validatorWithdrawal +ACTIVE ---> EXITED : verifyBalances +VERIFIED --> EXITED : verifyBalances +EXITING --> EXITED : verifyBalances + +REGISTERED --> REMOVED : removeSsvValidator +INVALID --> REMOVED : removeSsvValidator +EXITED --> REMOVED : removeSsvValidator + +@enduml \ No newline at end of file diff --git a/contracts/docs/plantuml/stakingValueTransitions.png b/contracts/docs/plantuml/stakingValueTransitions.png new file mode 100644 index 0000000000..e7c8cb3532 Binary files /dev/null and b/contracts/docs/plantuml/stakingValueTransitions.png differ diff --git a/contracts/docs/plantuml/stakingValueTransitions.puml b/contracts/docs/plantuml/stakingValueTransitions.puml new file mode 100644 index 0000000000..a271470b3a --- /dev/null +++ b/contracts/docs/plantuml/stakingValueTransitions.puml @@ -0,0 +1,39 @@ +@startuml + +title Native Staking Value Transitions + +package "Execution Layer" { + object "Vault" as vault + vault : WETH + object "Compounding\nStaking\nContract" as csc + csc : WETH, ETH + object "Beacon\nDeposit\nContract" as bdc + bdc : ETH + object "MEV" as mev + mev : ETH + object "Malicious\nActor" as malActor + malActor : ETH +} + +package "Consensus Layer" { + object "Validator" as val + val : ETH + object "Consensus" as cr + cr : ETH +} + +vault --> csc : deposit\ndepositAll +csc --> vault : withdraw\nwithdrawAll +csc <-- mev : rewards +csc <-- malActor : donate + +malActor --> bdc : deposit + +csc --> bdc : deposit + +bdc --> val : create\ntop-up +val --> csc : partial withdraw (on-chain)\nfull withdrawal (on-chain)\nexit (off-chain) + +val <- cr : rewards + +@enduml \ No newline at end of file diff --git a/contracts/package.json b/contracts/package.json index 250aa55322..bd6d1a3d81 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -127,11 +127,17 @@ }, "dependencies": { "@chainlink/contracts-ccip": "^1.2.1", + "@chainsafe/bls": "^8.2.0", "@layerzerolabs/devtools": "^0.4.10", "@layerzerolabs/lz-evm-messagelib-v2": "^3.0.103", "@layerzerolabs/lz-evm-protocol-v2": "^3.0.87", "@layerzerolabs/lz-v2-utilities": "^3.0.88", "@layerzerolabs/oapp-evm": "^0.3.2", + "@lodestar/api": "^1.31.0", + "@lodestar/config": "^1.31.0", + "@lodestar/params": "^1.33.0", + "@lodestar/state-transition": "^1.33.0", + "@lodestar/types": "^1.31.0", "ganache": "^7.9.2" }, "resolutions": { diff --git a/contracts/scripts/defender-actions/registerValidators.js b/contracts/scripts/defender-actions/registerValidators.js index a76f4389ee..0065230ce7 100644 --- a/contracts/scripts/defender-actions/registerValidators.js +++ b/contracts/scripts/defender-actions/registerValidators.js @@ -27,7 +27,7 @@ const handler = async (event) => { const signer = new DefenderRelaySigner(event, provider, { speed: "fastest" }); const network = await provider.getNetwork(); - const networkName = network.chainId === 1 ? "mainnet" : "holesky"; + const networkName = network.chainId === 1 ? "mainnet" : "hoodi"; log(`Network: ${networkName} with chain id (${network.chainId})`); const nativeStakingProxyAddress = diff --git a/contracts/storageLayout/mainnet/MockBeaconRoots.json b/contracts/storageLayout/mainnet/MockBeaconRoots.json new file mode 100644 index 0000000000..0e518b3612 --- /dev/null +++ b/contracts/storageLayout/mainnet/MockBeaconRoots.json @@ -0,0 +1,27 @@ +{ + "solcVersion": "0.8.28", + "storage": [ + { + "label": "_beaconRoots", + "offset": 0, + "slot": "0", + "type": "t_mapping(t_uint256,t_bytes32)", + "contract": "MockBeaconRoots", + "src": "contracts/mocks/MockBeaconRoots.sol:8" + } + ], + "types": { + "t_bytes32": { + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_bytes32)": { + "label": "mapping(uint256 => bytes32)", + "numberOfBytes": "32" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + } + } +} \ No newline at end of file diff --git a/contracts/tasks/beacon.js b/contracts/tasks/beacon.js new file mode 100644 index 0000000000..72550f7edd --- /dev/null +++ b/contracts/tasks/beacon.js @@ -0,0 +1,797 @@ +const ethers = require("ethers"); +const { + defaultAbiCoder, + formatUnits, + solidityPack, + parseUnits, + arrayify, +} = require("ethers/lib/utils"); + +const addresses = require("../utils/addresses"); +const { + getBeaconBlock, + getValidator: getValidatorBeacon, + getSlot, +} = require("../utils/beacon"); +const { bytes32 } = require("../utils/regex"); +const { resolveContract } = require("../utils/resolvers"); +const { + generateValidatorPubKeyProof, + generateFirstPendingDepositSlotProof, + generateValidatorWithdrawableEpochProof, + generateBalancesContainerProof, + generateBalanceProof, + generatePendingDepositsContainerProof, + generatePendingDepositProof, +} = require("../utils/proofs"); +const { toHex } = require("../utils/units"); +const { logTxDetails } = require("../utils/txLogger"); +const { getNetworkName } = require("../utils/hardhat-helpers"); +const { ZERO_BYTES32 } = require("../utils/constants"); + +const log = require("../utils/logger")("task:beacon"); + +/// Returns an ethers provider connected to the Ethereum mainnet or Hoodi. +/// @param {Provider} [provider] - Optional ethers provider connected to local fork or live chain. Uses Hardhat provider if not supplied. +async function getLiveProvider(provider) { + const networkName = await getNetworkName(provider); + if (networkName == "hoodi") { + return new ethers.providers.JsonRpcProvider(process.env.HOODI_PROVIDER_URL); + } + // Get provider to Ethereum mainnet and not a local fork + return new ethers.providers.JsonRpcProvider(process.env.PROVIDER_URL); +} + +async function requestValidatorWithdraw({ pubkey, amount, signer }) { + const amountGwei = parseUnits(amount.toString(), 9); + + const data = solidityPack(["bytes", "uint64"], [pubkey, amountGwei]); + log(`Encoded partial withdrawal data: ${data}`); + + const tx = await signer.sendTransaction({ + to: addresses.mainnet.beaconChainWithdrawRequest, + data, + value: 1, // 1 wei for the fee + }); + + await logTxDetails(tx, "requestWithdraw"); +} + +async function verifyValidator({ slot, index, dryrun, cred, signer }) { + // Get provider to mainnet or testnet and not a local fork + const provider = await getLiveProvider(signer.provider); + + const { blockView, blockTree, stateView } = await getBeaconBlock(slot); + + const strategy = await resolveContract( + "CompoundingStakingSSVStrategyProxy", + "CompoundingStakingSSVStrategy" + ); + + if (cred) { + log(`Overriding withdrawal credentials to ${cred}`); + + // Update the validator's withdrawalCredentials in stateView + const validator = stateView.validators.get(index); + if ( + !validator || + toHex(validator.node.root) == + "0x0000000000000000000000000000000000000000000000000000000000000000" + ) { + throw new Error(`Validator at index ${index} not found for slot ${slot}`); + } + + log( + `Original withdrawal credentials: ${toHex( + validator.withdrawalCredentials + )}` + ); + + // Override the address in the withdrawal credentials + validator.withdrawalCredentials = arrayify(cred); + stateView.validators.set(index, validator); // Update validator in state + + // Update blockTree with new stateRoot + const stateRootGindex = blockView.type.getPathInfo(["stateRoot"]).gindex; + blockTree.setNode(stateRootGindex, stateView.node); + } else { + cred = "0x020000000000000000000000" + strategy.address.slice(2); + } + + const nextBlock = blockView.body.executionPayload.blockNumber + 1; + const { timestamp: nextBlockTimestamp } = await provider.getBlock(nextBlock); + log( + `Next execution layer block ${nextBlock} has timestamp ${nextBlockTimestamp}` + ); + + const { + proof, + leaf: pubKeyHash, + root: beaconBlockRoot, + pubKey, + } = await generateValidatorPubKeyProof({ + validatorIndex: index, + blockView, + blockTree, + stateView, + }); + + // Check the validator is in STAKED state + const stateEnum = (await strategy.validator(pubKeyHash)).state; + log(`Validator with pub key hash ${pubKeyHash} has state: ${stateEnum}`); + if (stateEnum !== 2) + // STAKED + throw Error( + `Validator ${index} with pub key hash ${pubKeyHash} is not STAKED. Status: ${stateEnum}` + ); + + if (dryrun) { + console.log(`beaconBlockRoot : ${beaconBlockRoot}`); + console.log(`nextBlockTimestamp : ${nextBlockTimestamp}`); + console.log(`validator index : ${index}`); + console.log(`pubKeyHash : ${pubKeyHash}`); + console.log(`withdrawal credentials: ${cred}`); + console.log(`Validator status : ${stateEnum}`); + console.log(`proof:\n${proof}`); + return; + } + + log( + `About verify validator ${index} with pub key ${pubKey}, pub key hash ${pubKeyHash}, withdrawal credential ${cred} at slot ${blockView.slot} to beacon chain root ${beaconBlockRoot}` + ); + const tx = await strategy + .connect(signer) + .verifyValidator(nextBlockTimestamp, index, pubKeyHash, cred, proof); + await logTxDetails(tx, "verifyValidator"); +} + +async function verifyDeposit({ + slot, + root: depositRoot, + dryrun, + test, + index: strategyValidatorIndex, + signer, +}) { + const strategy = await resolveContract( + "CompoundingStakingSSVStrategyProxy", + "CompoundingStakingSSVStrategy" + ); + + let strategyDepositSlot = 0; + if (!test) { + const depositData = await strategy.deposits(depositRoot); + log( + `Found deposit for ${formatUnits( + depositData.amountGwei, + 9 + )} ETH, from slot ${depositData.slot} with public key hash ${ + depositData.pubKeyHash + } and deposit index ${depositData.depositIndex}` + ); + const strategyValidator = await strategy.validator(depositData.pubKeyHash); + if (strategyValidator.state !== 3) + throw Error( + `Validator with pub key hash ${depositData.pubKeyHash} is not VERIFIED. Status: ${strategyValidator.state}` + ); + + const { slot, amountGwei, pubKeyHash, status } = await strategy.deposits( + depositRoot + ); + strategyDepositSlot = slot; + if (strategyDepositSlot == 0) { + throw Error(`Failed to find deposit with root ${depositRoot}`); + } + log( + `Verifying deposit of ${formatUnits( + amountGwei, + 9 + )} ETH at slot ${strategyDepositSlot} with public key hash ${pubKeyHash}` + ); + if (status !== 1) { + throw Error( + `Deposit with root ${depositRoot} is not Pending. Status: ${status}` + ); + } + + strategyValidatorIndex = strategyValidator.index; + } + + if (!slot) { + const latestSlot = await getSlot(); + slot = latestSlot - 33; + log( + `Latest slot is ${latestSlot}, using slot ${slot} for verifying the deposit` + ); + } + + // Uses the latest slot if the slot is undefined + const depositProcessedBeaconData = await getBeaconBlock(slot); + const depositProcessedSlot = depositProcessedBeaconData.blockView.slot; + + // if generating unit testing data + if (test) { + // change the slot of the first pending deposit to be 2 years in the future + // to ensure the unit test deposit has been processed + const firstPendingDeposit = + depositProcessedBeaconData.stateView.pendingDeposits.get(0); + log(`Original first pending deposit slot: ${firstPendingDeposit.slot}`); + + // There are 2,628,000 12 second slots per year + firstPendingDeposit.slot = depositProcessedSlot + 2 * 2628000; + log(`Testing first pending deposit slot: ${firstPendingDeposit.slot}`); + depositProcessedBeaconData.stateView.pendingDeposits.set( + 0, + firstPendingDeposit + ); + + const stateRootGIndex = + depositProcessedBeaconData.blockView.type.getPropertyGindex("stateRoot"); + // Patching the tree by attaching the state in the `stateRoot` field of the block. + depositProcessedBeaconData.blockTree.setNode( + stateRootGIndex, + depositProcessedBeaconData.stateView.node + ); + } + + // Generate a proof of the first pending deposit + const { + proof: pendingDepositSlotProof, + slot: firstPendingDepositSlot, + pubkeyHash: firstPendingDepositPubKeyHash, + validatorIndex: firstPendingDepositValidatorIndex, + root: processedBeaconBlockRoot, + isEmpty, + } = await generateFirstPendingDepositSlotProof({ + ...depositProcessedBeaconData, + test, + }); + + if (!isEmpty && firstPendingDepositSlot == 0 && !test) { + throw Error( + `Can not verify when the first pending deposits has a zero slot. This is from a validator consolidating to a compounding validator.\nExecute again when the first pending deposit slot is not zero.` + ); + } + if (!isEmpty && strategyDepositSlot > firstPendingDepositSlot) { + throw Error( + `Deposit at slot ${strategyDepositSlot} has not been processed at slot ${depositProcessedSlot}. Next deposit in the queue is from slot ${firstPendingDepositSlot}.` + ); + } + + // Generate a proof of the withdrawable epoch for the strategy's validator to deposit is going to + const { + proof: strategyValidatorWithdrawableEpochProof, + withdrawableEpoch: strategyValidatorWithdrawableEpoch, + } = await generateValidatorWithdrawableEpochProof({ + ...depositProcessedBeaconData, + validatorIndex: strategyValidatorIndex, + includePubKeyProof: false, + }); + + const firstPendingDeposit = { + slot: firstPendingDepositSlot, + validatorIndex: firstPendingDepositValidatorIndex, + proof: pendingDepositSlotProof, + }; + const strategyValidator = { + withdrawableEpoch: strategyValidatorWithdrawableEpoch.toString(), + withdrawableEpochProof: strategyValidatorWithdrawableEpochProof, + }; + + if (dryrun) { + console.log( + `deposit slot : ${strategyDepositSlot}` + ); + console.log(`deposit root : ${depositRoot}`); + console.log( + `beacon block root : ${processedBeaconBlockRoot}` + ); + console.log( + `deposit processed slot : ${depositProcessedSlot}` + ); + console.log( + `first pending deposit pubkey. : ${firstPendingDepositPubKeyHash}` + ); + console.log( + `first pending deposit index : ${firstPendingDepositValidatorIndex}` + ); + console.log( + `first pending deposit slot : ${firstPendingDepositSlot}` + ); + console.log( + `first pending deposit slot proof : ${pendingDepositSlotProof}` + ); + console.log( + `Strategy validator index. : ${strategyValidatorIndex}` + ); + console.log( + `Strategy validator withdrawable epoch. : ${strategyValidatorWithdrawableEpoch}` + ); + console.log( + `Strategy validator withdrawable proof : ${strategyValidatorWithdrawableEpochProof}` + ); + return; + } + + if (test) { + console.log( + JSON.stringify( + { + firstPendingDeposit, + strategyValidator, + processedBeaconBlockRoot, + }, + null, + 2 + ) + ); + return; + } + + log( + `About to verify deposit from slot ${strategyDepositSlot} with processing slot ${depositProcessedSlot}, deposit root ${depositRoot}, slot of first pending deposit ${firstPendingDepositSlot} to beacon block root ${processedBeaconBlockRoot}` + ); + const tx = await strategy + .connect(signer) + .verifyDeposit( + depositRoot, + depositProcessedSlot, + firstPendingDeposit, + strategyValidator + ); + await logTxDetails(tx, "verifyDeposit"); +} + +async function verifyBalances({ + indexes, + deposits, + dryrun, + test, + signer, + slot, +}) { + const strategy = test + ? undefined + : await resolveContract( + "CompoundingStakingSSVStrategyProxy", + "CompoundingStakingSSVStrategy" + ); + const strategyView = test + ? undefined + : await resolveContract("CompoundingStakingStrategyView"); + + if (!slot) { + if (!test) { + const { blockRoot } = await strategy.snappedBalance(); + slot = blockRoot; + log(`Using slot with block root ${slot} for verifying balances`); + } else { + slot = "head"; + } + } + + // Uses the beacon chain data for the beacon block root + const { blockView, blockTree, stateView } = await getBeaconBlock(slot); + const verificationSlot = blockView.slot; + + const { + leaf: pendingDepositContainerRoot, + proof: pendingDepositContainerProof, + } = await generatePendingDepositsContainerProof({ + blockView, + blockTree, + stateView, + }); + + let pendingDepositIndexes = []; + let pendingDepositRoots = []; + let pendingDepositProofs = []; + if (test) { + const depositIndexes = (deposits || "") + .split(",") + .map((index) => Number(index)); + for (const depositIndex of depositIndexes) { + pendingDepositIndexes.push(depositIndex); + const { proof, leaf } = await generatePendingDepositProof({ + blockView, + blockTree, + stateView, + depositIndex, + }); + pendingDepositRoots.push(leaf); + pendingDepositProofs.push(proof); + } + } else { + const pendingDeposits = await strategyView.getPendingDeposits(); + // For each of the strategy's pending deposits + for (const deposit of pendingDeposits) { + // Find the strategy's deposit in the beacon chain's pending deposits + let pendingDepositIndex = -1; + for (let i = 0; i < stateView.pendingDeposits.length; i++) { + const pd = stateView.pendingDeposits.get(i); + if (toHex(pd.hashTreeRoot()) === deposit.pendingDepositRoot) { + log( + `Found pending deposit with root ${deposit.pendingDepositRoot} at index ${i}` + ); + pendingDepositIndex = i; + pendingDepositIndexes.push(pendingDepositIndex); + pendingDepositRoots.push(deposit.pendingDepositRoot); + break; + } + } + if (pendingDepositIndex === -1) { + throw Error( + `Could not find pending deposit with root hash ${deposit.pendingDepositRoot}` + ); + } + const { proof } = await generatePendingDepositProof({ + blockView, + blockTree, + stateView, + depositIndex: pendingDepositIndex, + }); + pendingDepositProofs.push(proof); + } + } + + const verifiedValidators = indexes + ? indexes.split(",").map((index) => ({ + index, + })) + : await strategyView.getVerifiedValidators(); + + let balancesContainerRoot = ZERO_BYTES32; + let balancesContainerProof = "0x"; + let blockRoot = ZERO_BYTES32; + if (verifiedValidators.length > 0) { + const balancesContainerProofData = await generateBalancesContainerProof({ + blockView, + blockTree, + stateView, + }); + balancesContainerRoot = balancesContainerProofData.leaf; + balancesContainerProof = balancesContainerProofData.proof; + blockRoot = balancesContainerProofData.root; + } + + const validatorBalanceLeaves = []; + const validatorBalanceProofs = []; + const validatorBalances = []; + for (const validator of verifiedValidators) { + const { proof, leaf, balance } = await generateBalanceProof({ + validatorIndex: validator.index, + blockView, + blockTree, + stateView, + }); + validatorBalanceLeaves.push(leaf); + validatorBalanceProofs.push(proof); + validatorBalances.push(balance); + + log( + `Validator ${validator.index} has balance: ${formatUnits(balance, 9)} ETH` + ); + } + const validatorBalancesFormatted = validatorBalances.map((bal) => + formatUnits(bal, 9) + ); + + if (dryrun) { + console.log(`snapped slot : ${verificationSlot}`); + console.log(`snap balances block root : ${blockRoot}`); + console.log(`\nbalancesContainerRoot : ${balancesContainerRoot}`); + console.log(`\nbalancesContainerProof:\n${balancesContainerProof}`); + console.log( + `\nvalidatorBalanceLeaves:\n[${validatorBalanceLeaves + .map((leaf) => `"${leaf}"`) + .join(",\n")}]` + ); + console.log( + `\nvalidatorBalanceProofs:\n[${validatorBalanceProofs + .map((proof) => `"${proof}"`) + .join(",\n")}]` + ); + console.log( + `validatorBalances: [${validatorBalancesFormatted.join(", ")}]` + ); + console.log( + `\npendingDepositsContainerRoot : ${pendingDepositContainerRoot}` + ); + console.log( + `\npendingDepositsContainerProof:\n${pendingDepositContainerProof}` + ); + console.log( + `\npendingDepositIndexes:\n[${pendingDepositIndexes + .map((index) => `"${index}"`) + .join(",")}]` + ); + console.log( + `\npendingDepositProofs:\n[${pendingDepositProofs + .map((proof) => `"${proof}"`) + .join(",\n")}]` + ); + return; + } + + const balanceProofs = { + balancesContainerRoot, + balancesContainerProof, + validatorBalanceLeaves, + validatorBalanceProofs, + }; + const pendingDepositProofsData = { + pendingDepositContainerRoot, + pendingDepositContainerProof, + pendingDepositIndexes, + pendingDepositRoots, + pendingDepositProofs, + }; + + if (test) { + console.log( + JSON.stringify( + { + blockRoot, + pendingDepositProofsData, + balanceProofs, + validatorBalances: validatorBalancesFormatted, + }, + null, + 2 + ) + ); + return; + } + + log( + `About to verify ${verifiedValidators.length} validator balances for slot ${verificationSlot} to beacon block root ${blockRoot}` + ); + log(balanceProofs); + log(pendingDepositProofsData); + const tx = await strategy + .connect(signer) + .verifyBalances(balanceProofs, pendingDepositProofsData); + await logTxDetails(tx, "verifyBalances"); +} + +async function beaconRoot({ block, live, signer }) { + // Either use live chain or local fork to get the block timestamp + const provider = live + ? await getLiveProvider(signer.provider) + : signer.provider; + + // Get timestamp of the block + const fetchedBlock = await provider.getBlock(block); + if (fetchedBlock == null) throw Error(`Block ${block} not found`); + + const { timestamp } = fetchedBlock; + log(`Block ${block} has timestamp ${timestamp}`); + + const data = defaultAbiCoder.encode(["uint256"], [timestamp]); + log(`Encoded timestamp data: ${data}`); + + // The Beacon Roots contract is the same on mainnet and Hoodi + const beaconRootsAddress = addresses.mainnet.beaconRoots; + const root = await provider.call( + { + to: beaconRootsAddress, + data, + }, + block // blockTag + ); + + if (!root.match(bytes32)) { + throw Error( + `Could not find parent beacon block root for block ${block} with timestamp ${timestamp} in ${beaconRootsAddress}.` + ); + } + + console.log(`Block ${block} has parent beacon block root ${root}`); + + return { root, timestamp }; +} + +async function getValidator({ slot, index, pubkey }) { + if (!index && !pubkey) { + throw new Error("Either `index` or `pubkey` parameter is required"); + } + + if (pubkey) { + const apiValidator = await getValidatorBeacon(pubkey); + index = apiValidator.validatorindex; + } + + // Uses the latest slot if the slot is undefined + const { blockView, stateView } = await getBeaconBlock(slot); + + const validator = stateView.validators.get(index); + if ( + !validator || + toHex(validator.node.root) == + "0x0000000000000000000000000000000000000000000000000000000000000000" + ) { + throw new Error(`Validator at index ${index} not found for slot ${slot}`); + } + + const balance = stateView.balances.get(index); + + console.log( + `Validator at index ${index} for slot ${stateView.slot}, epoch ${ + BigInt(stateView.slot) / 32n + }:` + ); + console.log(`Public Key : ${toHex(validator.pubkey)}`); + console.log( + `Withdrawal Credentials : ${toHex(validator.withdrawalCredentials)}` + ); + console.log(`Actual Balance : ${formatUnits(balance, 9)} ETH`); + console.log( + `Effective Balance : ${formatUnits( + validator.effectiveBalance, + 9 + )} ETH` + ); + console.log(`Slashed : ${validator.slashed}`); + console.log(`Activation Epoch : ${validator.activationEpoch}`); + console.log(`Exit Epoch : ${validator.exitEpoch}`); + console.log(`Withdrawable Epoch : ${validator.withdrawableEpoch}`); + console.log( + `Activation Eligibility Epoch: ${validator.activationEligibilityEpoch}` + ); + + console.log(`\n${stateView.pendingDeposits.length} pending deposits:`); + let depositsFound = 0; + let totalDeposits = 0; + for (let i = 0; i < stateView.pendingDeposits.length; i++) { + const deposit = stateView.pendingDeposits.get(i); + if (Buffer.from(deposit.pubkey).equals(validator.pubkey)) { + console.log( + ` pending deposit for ${formatUnits(deposit.amount, 9)}, slot ${ + deposit.slot + }, withdrawal credential ${toHex( + deposit.withdrawalCredentials + )} at position ${i}` + ); + // console.log(`signature ${toHex(deposit.signature)}`); + depositsFound++; + totalDeposits += deposit.amount; + } + } + console.log( + `${depositsFound} pending deposits worth ${formatUnits( + totalDeposits, + 9 + )} for validator in ${stateView.pendingDeposits.length} pending deposits` + ); + + console.log( + `\n${stateView.pendingPartialWithdrawals.length} pending partial withdrawals:` + ); + let partialWithdrawalsFound = 0; + let totalPartialWithdrawals = 0n; + for (let i = 0; i < stateView.pendingPartialWithdrawals.length; i++) { + const withdrawal = stateView.pendingPartialWithdrawals.get(i); + log( + `Pending partial withdrawal for validator ${ + withdrawal.validatorIndex + }, withdrawable epoch ${ + withdrawal.withdrawableEpoch + } and amount ${formatUnits(withdrawal.amount, 9)}` + ); + if (withdrawal.validatorIndex == index) { + console.log( + ` pending partial withdrawal at position ${i} with withdrawable epoch ${ + withdrawal.withdrawableEpoch + } for ${formatUnits(withdrawal.amount, 9)} ETH` + ); + partialWithdrawalsFound++; + totalPartialWithdrawals = totalPartialWithdrawals + withdrawal.amount; + } + } + console.log( + `${partialWithdrawalsFound} pending partial withdrawals worth ${formatUnits( + totalPartialWithdrawals, + 9 + )} ETH for validator in ${ + stateView.pendingPartialWithdrawals.length + } pending withdrawals` + ); + + console.log( + `\n${blockView.body.executionPayload.withdrawals.length} execution payload withdrawals:` + ); + let withdrawals = 0; + for (let i = 0; i < blockView.body.executionPayload.withdrawals.length; i++) { + const withdrawal = blockView.body.executionPayload.withdrawals.get(i); + log( + `Withdrawal ${withdrawal.index} for validator ${ + withdrawal.validatorIndex + }, amount ${formatUnits(withdrawal.amount, 9)}, address ${toHex( + withdrawal.address + )}` + ); + if (withdrawal.validatorIndex == index) { + console.log(`Found withdrawal at position ${i}`); + console.log(`amount : ${formatUnits(withdrawal.amount, 9)} ETH`); + console.log(`address: ${toHex(withdrawal.address)}`); + withdrawals++; + } + } + console.log( + `${withdrawals} withdrawals for validator in ${blockView.body.executionPayload.withdrawals.length} withdrawals` + ); + + console.log( + `\n${blockView.body.executionRequests.withdrawals.length} execution withdrawal requests:` + ); + let withdrawalRequests = 0; + for ( + let i = 0; + i < blockView.body.executionRequests.withdrawals.length; + i++ + ) { + const withdrawalRequest = + blockView.body.executionRequests.withdrawals.get(i); + log( + `Withdrawal request for validator ${toHex( + withdrawalRequest.validatorPubkey + )}, amount ${formatUnits( + withdrawalRequest.amount, + 9 + )} and source address ${toHex(withdrawalRequest.sourceAddress)}` + ); + if ( + Buffer.from(withdrawalRequest.validatorPubkey).equals(validator.pubkey) + ) { + console.log( + `Found withdrawal request at position ${i} on the execution layer` + ); + console.log(`amount : ${formatUnits(withdrawalRequest.amount, 9)} ETH`); + console.log(`address: ${toHex(withdrawalRequest.sourceAddress)}`); + withdrawalRequests++; + } + } + console.log( + `${withdrawalRequests} withdrawal requests on the execution layer found for validator in ${blockView.body.executionRequests.withdrawals.length} requests` + ); + + console.log( + `\n${blockView.body.voluntaryExits.length} execution voluntary exits:` + ); + let validatorExits = 0; + for (let i = 0; i < blockView.body.voluntaryExits.length; i++) { + const exit = blockView.body.voluntaryExits.get(i); + log( + `Voluntary exit for validator ${exit.message.validatorIndex}, epoch ${exit.message.epoch}` + ); + if (exit.message.validatorIndex == index) { + console.log(`Found voluntary exit at position ${i}`); + console.log(`epoch: ${exit.message.epoch}`); + validatorExits++; + } + } + console.log( + `${validatorExits} voluntary exits found for validator in ${blockView.body.voluntaryExits.length} exits` + ); + + console.log( + `\nNext withdrawable validator is ${stateView.nextWithdrawalValidatorIndex} with withdrawal index ${stateView.nextWithdrawalIndex}` + ); + const currentEpoch = Math.floor(blockView.slot / 32); + const earliestExitEpochDiff = stateView.earliestExitEpoch - currentEpoch; + const daysToExit = Number( + (earliestExitEpochDiff * 12 * 32) / (24 * 60 * 60) // 12 seconds per slot and 32 slots in an epoch, 24 hours in a day + ).toFixed(2); + console.log( + `Earliest exit epoch is ${stateView.earliestExitEpoch} which is ${earliestExitEpochDiff} epochs (${daysToExit} days) away from the current epoch ${currentEpoch}` + ); +} + +module.exports = { + requestValidatorWithdraw, + beaconRoot, + getValidator, + verifyValidator, + verifyDeposit, + verifyBalances, +}; diff --git a/contracts/tasks/beaconTesting.js b/contracts/tasks/beaconTesting.js new file mode 100644 index 0000000000..a7b3f7dccc --- /dev/null +++ b/contracts/tasks/beaconTesting.js @@ -0,0 +1,121 @@ +const { solidityPack, parseUnits } = require("ethers/lib/utils"); + +const { beaconRoot } = require("./beacon"); +const addresses = require("../utils/addresses"); +const { replaceContractAt } = require("../utils/hardhat"); +const { logTxDetails } = require("../utils/txLogger"); + +const log = require("../utils/logger")("task:beacon:test:utils"); + +const calcWithdrawalCredential = (type, owner) => { + const withdrawalCredential = solidityPack( + ["bytes1", "bytes11", "address"], + [type, "0x0000000000000000000000", owner] + ); + log(`Withdrawal Credentials: ${withdrawalCredential}`); + + return withdrawalCredential; +}; + +const calcDepositRoot = async (owner, type, pubkey, sig, amount) => { + // Dynamically import the Lodestar as its an ESM module + const { ssz } = await import("@lodestar/types"); + const { fromHex } = await import("@lodestar/utils"); + + const validTypes = ["0x00", "0x01", "0x02"]; + if (!validTypes.includes(type)) { + throw new Error(`Invalid type ${type}. Must be one of: 0x00, 0x01, 0x02`); + } + + const withdrawalCredential = solidityPack( + ["bytes1", "bytes11", "address"], + [type, "0x0000000000000000000000", owner] + ); + log(`Withdrawal Credentials: ${withdrawalCredential}`); + + // amount in Gwei + const amountGwei = parseUnits(amount.toString(), 9); + + // Define the DepositData object + const depositData = { + pubkey: fromHex(pubkey), // 48-byte public key + withdrawalCredentials: fromHex(withdrawalCredential), // 32-byte withdrawal credentials + amount: amountGwei.toString(), + signature: fromHex(sig), // 96-byte signature + }; + + // Compute the SSZ hash tree root + const depositDataRoot = ssz.electra.DepositData.hashTreeRoot(depositData); + + // Return as a hex string with 0x prefix + const depositDataRootHex = + "0x" + Buffer.from(depositDataRoot).toString("hex"); + + log(`Deposit Root Data: ${depositDataRootHex}`); + + return depositDataRootHex; +}; + +async function depositValidator({ pubkey, cred, sig, root, amount, signer }) { + const depositContract = await hre.ethers.getContractAt( + "IDepositContract", + addresses.mainnet.beaconChainDepositContract, + signer + ); + + const tx = await depositContract.deposit(pubkey, cred, sig, root, { + value: ethers.utils.parseEther(amount.toString()), + }); + await logTxDetails(tx, "deposit to validator"); +} + +async function copyBeaconRoot({ block, signer }) { + // Get the parent beacon block root from the mainnet BeaconRoots contract + const { root: parentBlockRoot, timestamp } = await beaconRoot({ + block, + live: true, + signer, + }); + + if (parentBlockRoot === "0x") { + throw new Error( + `No parent beacon block root found for block ${block} on live chain in the BeaconRoots contract ${addresses.mainnet.beaconRoots}` + ); + } + + // Now set on the mock contract on the local test network + const localBeaconRoots = await hre.ethers.getContractAt( + "MockBeaconRoots", + addresses.mainnet.beaconRoots + ); + log( + `About to set parent beacon block root ${parentBlockRoot} for timestamp ${timestamp} on local BeaconRoots contract at ${localBeaconRoots.address}` + ); + await localBeaconRoots["setBeaconRoot(uint256,bytes32)"]( + timestamp, + parentBlockRoot + ); + + return parentBlockRoot; +} + +async function mockBeaconRoot() { + if (hre.network.name == "mainnet") { + throw new Error( + "This task can only be run against a hardhat or a local forked network" + ); + } + + const factory = await hre.ethers.getContractFactory("MockBeaconRoots"); + const mockBeaconRoots = await factory.deploy(); + + await replaceContractAt(addresses.mainnet.beaconRoots, mockBeaconRoots); +} + +module.exports = { + calcDepositRoot, + calcWithdrawalCredential, + depositValidator, + copyBeaconRoot, + mockBeaconRoot, +}; diff --git a/contracts/tasks/beaconchain.js b/contracts/tasks/beaconchain.js deleted file mode 100644 index 3802f000b2..0000000000 --- a/contracts/tasks/beaconchain.js +++ /dev/null @@ -1,63 +0,0 @@ -const fetch = require("node-fetch"); -const API_URL = "https://beaconcha.in/api/v1/"; -const log = require("../utils/logger")("task:p2p"); - -const beaconchainRequest = async (endpoint) => { - const apikey = process.env.BEACONCHAIN_API_KEY; - const url = `${API_URL}${endpoint}`; - if (!apikey) { - throw new Error( - "Set BEACONCHAIN_API_KEY in order to be able to query the API" - ); - } - - const headers = { - Accept: "application/json", - "Content-Type": "application/json", - apikey, - }; - - log(`About to call Beaconcha.in API: ${url} `); - - const rawResponse = await fetch(url, { - method: "GET", - headers, - }); - - const response = await rawResponse.json(); - if (response.status != "OK") { - log(`Call to Beaconcha.in API failed: ${url}`); - log(`response: `, response); - throw new Error( - `Call to Beaconcha.in API failed. Error: ${JSON.stringify( - response.status - )}` - ); - } else { - log(`GET request to Beaconcha.in API succeeded. Response: `, response); - } - - return response.data; -}; - -const getValidator = async (pubkey) => { - return await beaconchainRequest(`validator/${pubkey}`); -}; - -const getValidators = async (pubkeys, beaconChainApiKey) => { - const encodedPubkeys = encodeURIComponent(pubkeys); - return await beaconchainRequest( - `validator/${encodedPubkeys}`, - beaconChainApiKey - ); -}; - -const getEpoch = async (epochId = "latest") => { - return await beaconchainRequest(`epoch/${epochId}`); -}; - -module.exports = { - getValidator, - getValidators, - getEpoch, -}; diff --git a/contracts/tasks/defender.js b/contracts/tasks/defender.js index e339759856..333555f76e 100644 --- a/contracts/tasks/defender.js +++ b/contracts/tasks/defender.js @@ -1,3 +1,4 @@ +require("dotenv").config(); const { AutotaskClient } = require("@openzeppelin/defender-autotask-client"); const log = require("../utils/logger")("task:defender"); @@ -20,7 +21,7 @@ const setActionVars = async ({ id, name }) => { ...envVars, DEBUG: "origin*", }); - console.log("updated Defender Action variables to:", variables); + console.log("Updated Defender Actions environment variables to:", variables); }; module.exports = { diff --git a/contracts/tasks/ssv.js b/contracts/tasks/ssv.js index 71429c978a..6282844ae6 100644 --- a/contracts/tasks/ssv.js +++ b/contracts/tasks/ssv.js @@ -1,21 +1,20 @@ -const { parseUnits, formatUnits, solidityPack } = require("ethers/lib/utils"); +const { parseUnits, formatUnits, hexlify } = require("ethers/lib/utils"); const addresses = require("../utils/addresses"); const { resolveContract } = require("../utils/resolvers"); const { getSigner } = require("../utils/signers"); -const { getClusterInfo } = require("../utils/ssv"); +const { getClusterInfo, sortOperatorIds } = require("../utils/ssv"); const { getNetworkName } = require("../utils/hardhat-helpers"); const { logTxDetails } = require("../utils/txLogger"); const { resolveNativeStakingStrategyProxy } = require("./validator"); -const { checkPubkeyFormat } = require("./taskUtils"); const log = require("../utils/logger")("task:ssv"); -async function removeValidator({ index, pubkey, operatorids }) { +async function removeValidators({ index, pubkeys, operatorids }) { const signer = await getSigner(); log(`Splitting operator IDs ${operatorids}`); - const operatorIds = operatorids.split(",").map((id) => parseInt(id)); + const operatorIds = await sortOperatorIds(operatorids); const strategy = await resolveNativeStakingStrategyProxy(index); @@ -29,15 +28,18 @@ async function removeValidator({ index, pubkey, operatorids }) { ownerAddress: strategy.address, }); - log(`About to remove validator`); - pubkey = checkPubkeyFormat(pubkey); + log(`Splitting public keys ${pubkeys}`); + const pubKeys = pubkeys.split(",").map((pubkey) => hexlify(pubkey)); + + log(`About to remove validators: ${pubKeys}`); const tx = await strategy .connect(signer) - .removeSsvValidator(pubkey, operatorIds, cluster); - await logTxDetails(tx, "removeSsvValidator"); + .removeSsvValidators(pubKeys, operatorIds, cluster); + await logTxDetails(tx, "removeSsvValidators"); } const printClusterInfo = async (options) => { + options.operatorids = await sortOperatorIds(options.operatorids); const cluster = await getClusterInfo(options); console.log(`block ${cluster.block}`); console.log(`Cluster: ${JSON.stringify(cluster.cluster, null, " ")}`); @@ -46,7 +48,7 @@ const printClusterInfo = async (options) => { const depositSSV = async ({ amount, index, operatorids }) => { const amountBN = parseUnits(amount.toString(), 18); log(`Splitting operator IDs ${operatorids}`); - const operatorIds = operatorids.split(",").map((id) => parseInt(id)); + const operatorIds = await sortOperatorIds(operatorids); const signer = await getSigner(); @@ -82,7 +84,7 @@ const depositSSV = async ({ amount, index, operatorids }) => { const withdrawSSV = async ({ amount, index, operatorids }) => { const amountBN = parseUnits(amount.toString(), 18); log(`Splitting operator IDs ${operatorids}`); - const operatorIds = operatorids.split(",").map((id) => parseInt(id)); + const operatorIds = await sortOperatorIds(operatorids); const signer = await getSigner(); @@ -115,48 +117,9 @@ const withdrawSSV = async ({ amount, index, operatorids }) => { await logTxDetails(tx, "withdrawSSV"); }; -const calcDepositRoot = async ({ index, pubkey, sig }, hre) => { - if (hre.network.name !== "hardhat") { - throw new Error("This task can only be run in hardhat network"); - } - - const factory = await ethers.getContractFactory("DepositContractUtils"); - const depositContractUtils = await factory.deploy(); - - const proxyNumber = - index === undefined || index === 1 ? "" : index.toString(); - const strategyAddress = - addresses.mainnet[`NativeStakingSSVStrategy${proxyNumber}Proxy`]; - log( - `Resolved Native Staking Strategy with index ${index} to address to ${strategyAddress}` - ); - - const withdrawalCredentials = solidityPack( - ["bytes1", "bytes11", "address"], - [ - "0x01", - "0x0000000000000000000000", - addresses.mainnet[`NativeStakingSSVStrategy${proxyNumber}Proxy`], - ] - ); - log(`Withdrawal Credentials: ${withdrawalCredentials}`); - - log( - `About to calculate deposit data root for pubkey ${pubkey} and sig ${sig}` - ); - const depositDataRoot = await depositContractUtils.calculateDepositDataRoot( - pubkey, - withdrawalCredentials, - sig - ); - - console.log(`Deposit Root Data: ${depositDataRoot}`); -}; - module.exports = { printClusterInfo, depositSSV, withdrawSSV, - calcDepositRoot, - removeValidator, + removeValidators, }; diff --git a/contracts/tasks/taskUtils.js b/contracts/tasks/taskUtils.js deleted file mode 100644 index 5dfa5e519c..0000000000 --- a/contracts/tasks/taskUtils.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * A separate file for small utils other files can use. The purpose of this being separate is also to not mess up the - * dependency graph too much, since there is a source file limit of 5MB on Defender actions - */ -const checkPubkeyFormat = (pubkey) => { - if (!pubkey.startsWith("0x")) { - pubkey = `0x${pubkey}`; - } - return pubkey; -}; - -module.exports = { - checkPubkeyFormat, -}; diff --git a/contracts/tasks/tasks.js b/contracts/tasks/tasks.js index 46b659ef38..1ccec5c3ea 100644 --- a/contracts/tasks/tasks.js +++ b/contracts/tasks/tasks.js @@ -66,11 +66,10 @@ const { curvePoolTask, } = require("./curve"); const { - calcDepositRoot, depositSSV, withdrawSSV, printClusterInfo, - removeValidator, + removeValidators, } = require("./ssv"); const { amoStrategyTask, @@ -92,7 +91,7 @@ const { } = require("./strategy"); const { validatorOperationsConfig, - exitValidator, + exitValidators, doAccounting, manuallyFixAccounting, resetStakeETHTally, @@ -103,7 +102,16 @@ const { resolveNativeStakingStrategyProxy, snapValidators, } = require("./validator"); - +const { + snapStakingStrategy, + snapBalances, + registerValidatorCreateRequest, + registerValidator, + stakeValidator, + withdrawValidator, + removeValidator, + setRegistrator, +} = require("./validatorCompound"); const { tenderlySync, tenderlyUpload } = require("./tenderly"); const { setDefaultValidator, snapSonicStaking } = require("../utils/sonic"); const { @@ -113,9 +121,21 @@ const { const { registerValidators, stakeValidators } = require("../utils/validator"); const { harvestAndSwap } = require("./harvest"); const { deployForceEtherSender, forceSend } = require("./simulation"); -const { sleep } = require("../utils/time"); - const { lzBridgeToken, lzSetConfig } = require("./layerzero"); +const { + requestValidatorWithdraw, + beaconRoot, + getValidator, + verifyValidator, + verifyDeposit, + verifyBalances, +} = require("./beacon"); +const { + calcDepositRoot, + depositValidator, + mockBeaconRoot, + copyBeaconRoot, +} = require("./beaconTesting"); const log = require("../utils/logger")("tasks"); @@ -1100,36 +1120,25 @@ task("withdrawSSV").setAction(async (_, __, runSuper) => { }); /** - * The native staking proxy needs to be deployed via the defender relayer because the SSV network + * The compounding staking proxy needs to be deployed via the defender relayer because the SSV network * grants the SSV rewards to the deployer of the contract. And we want the Defender Relayer to be * the recipient */ subtask( - "deployNativeStakingProxy", - "Deploy the native staking proxy via the Defender Relayer" -) - .addOptionalParam( - "index", - "The number of the Native Staking Contract deployed.", - undefined, - types.int - ) - .setAction(async ({ index }) => { - const signer = await getSigner(); - - if (!index) { - throw new Error("Index is required and must be a positive integer"); - } + "deployStakingProxy", + "Deploy the compounding staking proxy via the Defender Relayer" +).setAction(async () => { + const signer = await getSigner(); - log(`Deploy NativeStakingSSVStrategy${index}Proxy`); - const nativeStakingProxyFactory = await ethers.getContractFactory( - `NativeStakingSSVStrategy${index}Proxy` - ); - const contract = await nativeStakingProxyFactory.connect(signer).deploy(); - await contract.deployed(); - log(`Address of deployed contract is: ${contract.address}`); - }); -task("deployNativeStakingProxy").setAction(async (_, __, runSuper) => { + log(`Deploy CompoundingStakingSSVStrategyProxy`); + const stakingProxyFactory = await ethers.getContractFactory( + `CompoundingStakingSSVStrategyProxy` + ); + const contract = await stakingProxyFactory.connect(signer).deploy(); + await contract.deployed(); + log(`Address of deployed staking contract is: ${contract.address}`); +}); +task("deployStakingProxy").setAction(async (_, __, runSuper) => { return runSuper(); }); @@ -1204,37 +1213,10 @@ task("stakeValidators").setAction(async (_, __, runSuper) => { return runSuper(); }); -subtask("exitValidator", "Starts the exit process from a validator") - .addParam( - "pubkey", - "Public key of the validator to exit", - undefined, - types.string - ) - .addParam( - "operatorids", - "Comma separated operator ids. E.g. 342,343,344,345", - undefined, - types.string - ) - .addOptionalParam( - "index", - "The number of the Native Staking Contract deployed.", - undefined, - types.int - ) - .setAction(async (taskArgs) => { - const signer = await getSigner(); - await exitValidator({ ...taskArgs, signer }); - }); -task("exitValidator").setAction(async (_, __, runSuper) => { - return runSuper(); -}); - -subtask("exitValidators", "Starts the exit process from a list of validators") +subtask("exitValidators", "Starts the exit process from validators") .addParam( "pubkeys", - "Comma separated list of validator public keys", + "Comma separated validator public keys", undefined, types.string ) @@ -1250,33 +1232,17 @@ subtask("exitValidators", "Starts the exit process from a list of validators") undefined, types.int ) - .addOptionalParam( - "sleep", - "Seconds between each tx so the SSV API can be updated before getting the cluster data.", - 30, - types.int - ) .setAction(async (taskArgs) => { const signer = await getSigner(); - - // Split the comma separated list of public keys - const pubKeys = taskArgs.pubkeys.split(","); - // For each public key - for (const pubkey of pubKeys) { - log(`About to exit validator with pubkey: ${pubkey}`); - await exitValidator({ ...taskArgs, pubkey, signer }); - - // wait for the SSV API can be updated - await sleep(taskArgs.sleep * 1000); - } + await exitValidators({ ...taskArgs, signer }); }); task("exitValidators").setAction(async (_, __, runSuper) => { return runSuper(); }); subtask( - "removeValidator", - "Removes a validator from the SSV cluster after it has exited the beacon chain" + "removeValidators", + "Removes validators from the SSV cluster after they have exited the beacon chain" ) .addOptionalParam( "index", @@ -1284,33 +1250,9 @@ subtask( undefined, types.int ) - .addParam( - "pubkey", - "Public key of the validator to exit", - undefined, - types.string - ) - .addParam( - "operatorids", - "Comma separated operator ids. E.g. 342,343,344,345", - undefined, - types.string - ) - .setAction(async (taskArgs) => { - const signer = await getSigner(); - await removeValidator({ ...taskArgs, signer }); - }); -task("removeValidator").setAction(async (_, __, runSuper) => { - return runSuper(); -}); - -subtask( - "removeValidators", - "Removes validators from the SSV cluster after they have exited the beacon chain" -) .addParam( "pubkeys", - "Comma separated list of validator public keys", + "Comma separated validator public keys", undefined, types.string ) @@ -1320,31 +1262,9 @@ subtask( undefined, types.string ) - .addOptionalParam( - "index", - "The number of the Native Staking Contract deployed.", - undefined, - types.int - ) - .addOptionalParam( - "sleep", - "Seconds between each tx so the SSV API can be updated before getting the cluster data.", - 30, - types.int - ) .setAction(async (taskArgs) => { const signer = await getSigner(); - - // Split the comma separated list of public keys - const pubKeys = taskArgs.pubkeys.split(","); - // For each public key - for (const pubkey of pubKeys) { - log(`About to remove validator with pubkey: ${pubkey}`); - await removeValidator({ ...taskArgs, pubkey, signer }); - - // wait for the SSV API can be updated - await sleep(taskArgs.sleep * 1000); - } + await removeValidators({ ...taskArgs, signer }); }); task("removeValidators").setAction(async (_, __, runSuper) => { return runSuper(); @@ -1603,13 +1523,35 @@ subtask( undefined, types.string ) + .addParam( + "owner", + "The withdrawal address of the validator in hex format", + undefined, + types.string + ) .addOptionalParam( "index", "The number of the Native Staking Contract deployed.", undefined, types.int ) - .setAction(calcDepositRoot); + .addOptionalParam( + "type", + "Validator type. 0x00, 0x01 or 0x02", + "0x02", + types.string + ) + .addOptionalParam("amount", "The deposit amount.", 32, types.float) + .setAction(async (taskArgs) => { + const root = await calcDepositRoot( + taskArgs.owner, + taskArgs.type, + taskArgs.pubkey, + taskArgs.sig, + taskArgs.amount + ); + console.log(`Deposit data root: ${root}`); + }); task("depositRoot").setAction(async (_, __, runSuper) => { return runSuper(); }); @@ -1848,6 +1790,429 @@ task("lzSetConfig") await lzSetConfig(taskArgs, hre); }); +// Beacon Chain Operations +subtask("depositValidator", "Deposits ETH to a validator on the Beacon chain") + .addParam("pubkey", "Validator public key in hex format with a 0x prefix") + .addParam("sig", "Validator signature in hex format with a 0x prefix") + .addParam( + "cred", + "Validator withdrawal credentials in hex format with a 0x prefix" + ) + .addParam( + "root", + "Beacon chain deposit data root in hex format with a 0x prefix" + ) + .addOptionalParam("amount", "Amount to deposit", 32, types.float) + .setAction(async (taskArgs) => { + const signer = await getSigner(); + await depositValidator({ ...taskArgs, signer }); + }); +task("depositValidator").setAction(async (_, __, runSuper) => { + return runSuper(); +}); + +subtask( + "setRegistrator", + "Set the registrator of the compounding staking strategy" +) + .addParam("account", "Address of the registrator", undefined, types.string) + .setAction(setRegistrator); +task("setRegistrator").setAction(async (_, __, runSuper) => { + return runSuper(); +}); + +subtask( + "beaconRoot", + "Gets the parent beacon block root for an execution layer block from the BeaconRoot contract" +) + .addParam( + "block", + "Execution layer block number to get the parent beacon block root for", + undefined, + types.int + ) + .addOptionalParam( + "live", + "Use live chain (mainnet or testnet) to get block timestamp. Not the local fork", + true, + types.boolean + ) + .setAction(async (taskArgs) => { + const signer = await getSigner(); + await beaconRoot({ ...taskArgs, signer }); + }); +task("beaconRoot").setAction(async (_, __, runSuper) => { + return runSuper(); +}); + +subtask( + "copyBeaconRoot", + "Copies a parent beacon block root from mainnet to a local BeaconRoot contract (EIP-4788)" +) + .addParam( + "block", + "Execution layer block number to set the parent root for", + undefined, + types.int + ) + .setAction(async (taskArgs) => { + const signer = await getSigner(); + await copyBeaconRoot({ ...taskArgs, signer }); + }); +task("copyBeaconRoot").setAction(async (_, __, runSuper) => { + return runSuper(); +}); + +subtask( + "mockBeaconRoot", + "Replaces the BeaconRoot contract (EIP-4788) with a mocked one for testing purposes" +).setAction(mockBeaconRoot); +task("mockBeaconRoot").setAction(async (_, __, runSuper) => { + return runSuper(); +}); + +subtask("getValidator", "Gets the details of a validator") + .addOptionalParam( + "index", + "Index of the validator on the Beacon chain", + undefined, + types.int + ) + .addOptionalParam( + "pubkey", + "Validator public key in hex format with a 0x prefix" + ) + .addOptionalParam( + "slot", + "Beacon chain slot. Default head", + undefined, + types.int + ) + .setAction(getValidator); +task("getValidator").setAction(async (_, __, runSuper) => { + return runSuper(); +}); + +subtask("verifyValidator", "Verify a validator on the Beacon chain") + .addParam( + "index", + "Index of the validator on the Beacon chain", + undefined, + types.int + ) + .addOptionalParam( + "slot", + "Any slot after the validator was registered on the Beacon chain. Default latest", + undefined, + types.int + ) + .addOptionalParam( + "dryrun", + "Do not call verifyBalances on the strategy contract. Just log the params including the proofs", + false, + types.boolean + ) + .addOptionalParam( + "cred", + "Override the withdrawal credential. Used when generating proofs for unit tests or the deposit was front-run.", + undefined, + types.string + ) + .setAction(async (taskArgs) => { + const signer = await getSigner(); + await verifyValidator({ ...taskArgs, signer }); + }); +task("verifyValidator").setAction(async (_, __, runSuper) => { + return runSuper(); +}); + +subtask("verifyDeposit", "Verify a deposit on the Beacon chain") + .addParam( + "root", + "The pending deposit root emitted in the `ETHStaked` event from the `stakeETH` function", + undefined, + types.string + ) + .addOptionalParam( + "slot", + "The slot on or after the deposit was process on the beacon chain. Default deposit processed slot", + undefined, + types.int + ) + .addOptionalParam( + "dryrun", + "Do not call verifyDeposit on the strategy contract. Just log the params including the proofs", + false, + types.boolean + ) + .addOptionalParam( + "test", + "Used for generating unit test data.", + false, + types.boolean + ) + .addOptionalParam( + "index", + "Override the validator with the index of a test validator. Used when generating proofs for unit tests.", + undefined, + types.int + ) + .setAction(async (taskArgs) => { + const signer = await getSigner(); + await verifyDeposit({ ...taskArgs, signer }); + }); +task("verifyDeposit").setAction(async (_, __, runSuper) => { + return runSuper(); +}); + +subtask("verifyBalances", "Verify validator balances on the Beacon chain") + .addOptionalParam( + "slot", + "The slot snapBalances was executed. Default: last balances snapshot", + undefined, + types.int + ) + .addOptionalParam( + "indexes", + "Comma separated list of validator indexes. Default: strategy's active validators", + undefined, + types.string + ) + .addOptionalParam( + "deposits", + "Comma separated list of indexes to beacon chain pending deposits used for generating unit test data", + undefined, + types.string + ) + .addOptionalParam( + "dryrun", + "Do not call verifyBalances on the strategy contract. Just log the params including the proofs", + false, + types.boolean + ) + .addOptionalParam( + "test", + "Used for generating unit test data.", + false, + types.boolean + ) + .setAction(async (taskArgs) => { + const signer = await getSigner(); + await verifyBalances({ ...taskArgs, signer }); + }); +task("verifyBalances").setAction(async (_, __, runSuper) => { + return runSuper(); +}); + +subtask( + "requestNewValidator", + "Calls P2P's Create SSV Request to prepare a new SSV compounding (0x02) validator" +) + .addOptionalParam( + "days", + "SSV Cluster operational time in days", + 1, + types.int + ) + .setAction(async (taskArgs) => { + await registerValidatorCreateRequest(taskArgs); + console.log("Once the validator is created run: registerValidatorUuid"); + }); +task("requestNewValidator").setAction(async (_, __, runSuper) => { + return runSuper(); +}); + +subtask( + "registerValidator", + "Registers a new compounding validator in a SSV cluster" +) + .addParam( + "operatorids", + "Comma separated operator ids. E.g. 342,343,344,345", + undefined, + types.string + ) + .addOptionalParam( + "uuid", + "The uuid that has been used to create the request using requestNewValidator", + undefined, + types.string + ) + .addOptionalParam( + "pubkey", + "If no uuid, the validator's public key in hex format with a 0x prefix", + undefined, + types.string + ) + .addOptionalParam( + "shares", + "If no uuid, SSV shares data", + undefined, + types.string + ) + .addOptionalParam( + "ssv", + "Amount of SSV to deposit to the cluster.", + 0, + types.int + ) + .setAction(async (taskArgs) => { + await registerValidator(taskArgs); + }); +task("registerValidator").setAction(async (_, __, runSuper) => { + return runSuper(); +}); + +subtask( + "withdrawValidator", + "Requests a partial or full withdrawal from a compounding validator" +) + .addParam( + "pubkey", + "The validator's public key in hex format with a 0x prefix", + undefined, + types.string + ) + .addOptionalParam( + "amount", + "Amount of ETH to withdraw from the validator. A zero amount is a full exit.", + 0, + types.float + ) + .addOptionalParam( + "direct", + "Withdraw via the staking contract or directly to the request withdrawal contract", + false, + types.boolean + ) + .setAction(async (taskArgs) => { + const signer = await getSigner(); + if (taskArgs.direct) { + await requestValidatorWithdraw({ ...taskArgs, signer }); + } else { + await withdrawValidator({ ...taskArgs, signer }); + } + }); +task("withdrawValidator").setAction(async (_, __, runSuper) => { + return runSuper(); +}); + +subtask( + "removeValidator", + "Removes a registered or exited compounding validator from the SSV cluster" +) + .addParam( + "pubkey", + "The validator's public key in hex format with a 0x prefix", + undefined, + types.string + ) + .addParam( + "operatorids", + "Comma separated operator ids. E.g. 342,343,344,345", + undefined, + types.string + ) + .setAction(async (taskArgs) => { + const signer = await getSigner(); + await removeValidator({ ...taskArgs, signer }); + }); +task("removeValidator").setAction(async (_, __, runSuper) => { + return runSuper(); +}); + +subtask( + "stakeValidatorUuid", + "Converts WETH to ETH and deposits to a validator from the Compounding Staking Strategy" +) + .addParam( + "uuid", + "The P2P uuid used to create the Create SSV validators request", + undefined, + types.string + ) + .addOptionalParam( + "dryrun", + "Do not call stakeEth on the strategy contract. Just log the params and verify the deposit signature", + false, + types.boolean + ) + .setAction(stakeValidator); +task("stakeValidatorUuid").setAction(async (_, __, runSuper) => { + return runSuper(); +}); + +subtask( + "stakeValidator", + "Converts WETH to ETH and deposits to a validator from the Compounding Staking Strategy" +) + .addParam( + "pubkey", + "The validator's public key in hex format with a 0x prefix", + undefined, + types.string + ) + .addOptionalParam( + "sig", + "The validator's deposit signature in hex format with a 0x prefix", + undefined, + types.string + ) + .addParam( + "amount", + "Amount of ETH to deposit to the validator.", + undefined, + types.float + ) + .addOptionalParam( + "withdrawalCredentials", + "Withdrawal credentials of the validator", + undefined, + types.string + ) + .addOptionalParam( + "depositMessageRoot", + "Deposit message root provided by p2p", + undefined, + types.string + ) + .addOptionalParam( + "forkVersion", + "Fork version of the beacon chain. Required for validating the BLS signature", + "10000910", + types.string + ) + .addOptionalParam( + "dryrun", + "Do not call stakeEth on the strategy contract. Just log the params and verify the deposit signature", + false, + types.boolean + ) + .setAction(stakeValidator); +task("stakeValidator").setAction(async (_, __, runSuper) => { + return runSuper(); +}); + +subtask( + "snapBalances", + "Takes a snapshot of the staking strategy's balance" +).setAction(snapBalances); +task("snapBalances").setAction(async (_, __, runSuper) => { + return runSuper(); +}); + +subtask("snapStakingStrat", "Dumps the staking strategy's data") + .addOptionalParam( + "block", + "Block number. (default: latest)", + undefined, + types.int + ) + .setAction(snapStakingStrategy); +task("snapStakingStrat").setAction(async (_, __, runSuper) => { + return runSuper(); +}); + subtask( "tenderlySync", "Fetches all contracts from deployment descriptors and uploads them to Tenderly if they are not there yet." diff --git a/contracts/tasks/validator.js b/contracts/tasks/validator.js index 131c2b28b6..e2e4912304 100644 --- a/contracts/tasks/validator.js +++ b/contracts/tasks/validator.js @@ -1,11 +1,10 @@ -const { formatUnits, parseEther } = require("ethers").utils; +const { formatUnits, hexlify, parseEther } = require("ethers").utils; const { KeyValueStoreClient, } = require("@openzeppelin/defender-kvstore-client"); const { getBlock } = require("./block"); -const { checkPubkeyFormat } = require("./taskUtils"); -const { getValidator, getValidators, getEpoch } = require("./beaconchain"); +const { getValidator, getValidators, getEpoch } = require("../utils/beacon"); const addresses = require("../utils/addresses"); const { resolveContract } = require("../utils/resolvers"); const { logTxDetails } = require("../utils/txLogger"); @@ -48,7 +47,7 @@ const validatorOperationsConfig = async (taskArgs) => { "P2P API key environment variable is not set. P2P_MAINNET_API_KEY or P2P_HOLESKY_API_KEY" ); } - const p2p_base_url = isMainnet ? "api.p2p.org" : "api-test-holesky.p2p.org"; + const p2p_base_url = isMainnet ? "api.p2p.org" : "api-test.p2p.org"; const awsS3AccessKeyId = process.env.AWS_ACCESS_S3_KEY_ID; const awsS3SexcretAccessKeyId = process.env.AWS_SECRET_S3_ACCESS_KEY; @@ -93,17 +92,23 @@ const validatorOperationsConfig = async (taskArgs) => { // @dev check validator is eligible for exit - // has been active for at least 256 epochs -async function verifyMinActivationTime({ pubkey }) { +async function verifyMinActivationTimes({ pubkeys }) { const latestEpoch = await getEpoch("latest"); - const validator = await getValidator(pubkey); - const epochDiff = latestEpoch.epoch - validator.activationepoch; + log(`Splitting public keys ${pubkeys}`); + const pubKeys = pubkeys.split(",").map((id) => hexlify(id)); - if (epochDiff < 256) { - throw new Error( - `Can not exit validator. Validator needs to be ` + - `active for 256 epoch. Current one active for ${epochDiff}` - ); + for (const pubkey of pubKeys) { + const validator = await getValidator(pubkey); + + const epochDiff = latestEpoch.epoch - validator.activationepoch; + + if (epochDiff < 256) { + throw new Error( + `Can not exit validator. Validator needs to be ` + + `active for 256 epoch. ${pubkey} has only been active for ${epochDiff}` + ); + } } } @@ -128,7 +133,7 @@ async function snapValidators({ pubkeys }) { const validators = await getValidators(pubkeys); console.log(`Validators details`); - console.log(`pubkey, balance, status, withdrawalcredentials`); + console.log(`pubkey, balance, status, withdrawal credentials`); for (const validator of validators) { console.log( `${validator.pubkey}, ${formatUnits(validator.balance, 9)}, ${ @@ -138,21 +143,21 @@ async function snapValidators({ pubkeys }) { } } -async function exitValidator({ index, pubkey, operatorids, signer }) { - await verifyMinActivationTime({ pubkey }); +async function exitValidators({ index, pubkeys, operatorids, signer }) { + await verifyMinActivationTimes({ pubkeys }); log(`Splitting operator IDs ${operatorids}`); const operatorIds = operatorids.split(",").map((id) => parseInt(id)); const strategy = await resolveNativeStakingStrategyProxy(index); - log(`About to exit validator`); - pubkey = checkPubkeyFormat(pubkey); + const pubKeys = pubkeys.split(",").map((pubkey) => hexlify(pubkey)); + log(`About to exit validators: ${pubKeys}`); const tx = await strategy .connect(signer) - .exitSsvValidator(pubkey, operatorIds); - await logTxDetails(tx, "exitSsvValidator"); + .exitSsvValidators(pubKeys, operatorIds); + await logTxDetails(tx, "exitSsvValidators"); } async function doAccounting({ signer, nativeStakingStrategy }) { @@ -352,7 +357,7 @@ const resolveFeeAccumulatorProxy = async (index) => { module.exports = { validatorOperationsConfig, - exitValidator, + exitValidators, doAccounting, resetStakeETHTally, setStakeETHThreshold, diff --git a/contracts/tasks/validatorCompound.js b/contracts/tasks/validatorCompound.js new file mode 100644 index 0000000000..4644541151 --- /dev/null +++ b/contracts/tasks/validatorCompound.js @@ -0,0 +1,470 @@ +const addresses = require("../utils/addresses"); +const { formatUnits, parseUnits } = require("ethers/lib/utils"); +const { BigNumber } = require("ethers"); + +const { getBlock } = require("../tasks/block"); +const { + calcDepositRoot, + calcWithdrawalCredential, +} = require("./beaconTesting"); +const { + calcSlot, + getValidatorBalance, + getBeaconBlock, +} = require("../utils/beacon"); +const { getNetworkName } = require("../utils/hardhat-helpers"); +const { getSigner } = require("../utils/signers"); +const { verifyDepositSignatureAndMessageRoot } = require("../utils/beacon"); +const { resolveContract } = require("../utils/resolvers"); +const { getClusterInfo, splitOperatorIds } = require("../utils/ssv"); +const { logTxDetails } = require("../utils/txLogger"); +const { + createValidatorRequest, + getValidatorRequestStatus, + getValidatorRequestDepositData, +} = require("../utils/p2pValidatorCompound"); +const { toHex } = require("../utils/units"); + +const log = require("../utils/logger")("task:validator:compounding"); + +async function snapBalances() { + const signer = await getSigner(); + + // TODO check the slot of the first pending deposit is not zero + + const strategy = await resolveContract( + "CompoundingStakingSSVStrategyProxy", + "CompoundingStakingSSVStrategy" + ); + + log(`About to snap balances for strategy ${strategy.address}`); + const tx = await strategy.connect(signer).snapBalances(); + await logTxDetails(tx, "snapBalances"); + + const receipt = await tx.wait(); + const event = receipt.events.find( + (event) => event.event === "BalancesSnapped" + ); + if (!event) { + throw new Error("BalancesSnapped event not found in transaction receipt"); + } + console.log( + `Balances snapped successfully. Beacon block root ${ + event.args.blockRoot + }, block ${receipt.blockNumber}, ETH balance ${formatUnits( + event.args.ethBalance + )}` + ); +} + +async function registerValidatorCreateRequest({ days }) { + await createValidatorRequest({ + validatorSpawnOperationalPeriodInDays: days, + }); +} + +/** + * If the UUID is passed to this function then pubkey, shares, operatorIds are + * ignored and fetched from the P2P + */ +async function registerValidator({ pubkey, shares, operatorids, ssv, uuid }) { + const signer = await getSigner(); + + if (uuid) { + const { + pubkey: _pubkey, + shares: _shares, + operatorids: _operatorids, + } = await getValidatorRequestStatus({ uuid }); + pubkey = _pubkey; + shares = _shares; + // unsorted string of operators + operatorids = _operatorids; + } + + log(`Splitting operator IDs ${operatorids}`); + const operatorIds = splitOperatorIds(operatorids); + + const ssvAmount = parseUnits(ssv.toString(), 18); + + const strategy = await resolveContract( + "CompoundingStakingSSVStrategyProxy", + "CompoundingStakingSSVStrategy" + ); + + // Cluster details + const { chainId } = await ethers.provider.getNetwork(); + const { cluster } = await getClusterInfo({ + chainId, + operatorids, + ownerAddress: strategy.address, + }); + + log(`About to register compounding validator with pubkey ${pubkey}`); + const tx = await strategy + .connect(signer) + .registerSsvValidator(pubkey, operatorIds, shares, ssvAmount, cluster); + await logTxDetails(tx, "registerValidator"); +} + +/** + * If the UUID is passed to this function then pubkey, sig, amount are + * ignored and fetched from the P2P + */ +async function stakeValidator({ + dryrun, + pubkey, + sig, + amount, + withdrawalCredentials, + depositMessageRoot, + forkVersion, + uuid, +}) { + const signer = await getSigner(); + + if (uuid) { + const { + pubkey: _pubkey, + sig: _sig, + amount: _amount, + depositMessageRoot: _depositMessageRoot, + withdrawalCredentials: _withdrawalCredentials, + forkVersion: _forkVersion, + } = await getValidatorRequestDepositData({ uuid }); + pubkey = _pubkey; + sig = _sig; + amount = _amount; + withdrawalCredentials = _withdrawalCredentials; + depositMessageRoot = _depositMessageRoot; + forkVersion = _forkVersion; + } + + const strategy = await resolveContract( + "CompoundingStakingSSVStrategyProxy", + "CompoundingStakingSSVStrategy" + ); + + if (!withdrawalCredentials) { + withdrawalCredentials = calcWithdrawalCredential("0x02", strategy.address); + } + + if (amount == 1) { + if (!sig) { + throw new Error( + "The signature is required for the first deposit of 1 ETH" + ); + } + await verifyDepositSignatureAndMessageRoot({ + pubkey, + withdrawalCredentials, + amount, + signature: sig, + depositMessageRoot, + forkVersion, + }); + } else { + // The signatures doesn't mater after the first deposit + sig = + "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"; + } + + const depositDataRoot = await calcDepositRoot( + strategy.address, + "0x02", + pubkey, + sig, + amount + ); + + const amountGwei = parseUnits(amount.toString(), 9); + + if (dryrun) { + console.log(`About to stake ${amount} ETH to validator with`); + console.log(` pubkey : ${pubkey}`); + console.log(` signature : ${sig}`); + console.log(` depositDataRoot: ${depositDataRoot}`); + return; + } + + log( + `About to stake ${amount} ETH to validator with pubkey ${pubkey}, deposit root ${depositDataRoot} and signature ${sig}` + ); + const tx = await strategy + .connect(signer) + .stakeEth({ pubkey, signature: sig, depositDataRoot }, amountGwei); + const receipt = await logTxDetails(tx, "stakeETH"); + + const event = receipt.events.find((event) => event.event === "ETHStaked"); + if (!event) { + throw new Error("ETHStaked event not found in transaction receipt"); + } + console.log(`Deposit ID: ${event.args.depositID}`); +} + +async function withdrawValidator({ pubkey, amount, signer }) { + const strategy = await resolveContract( + "CompoundingStakingSSVStrategyProxy", + "CompoundingStakingSSVStrategy" + ); + + /// Get the validator's balance + const balance = await getValidatorBalance(pubkey); + + const isFullExit = amount === undefined || amount === 0; + const amountGwei = isFullExit ? 0 : parseUnits(amount.toString(), 9); + if (isFullExit) { + log( + `About to fully exit validator with balance ${formatUnits( + balance, + 9 + )} ETH and pubkey ${pubkey}` + ); + } else { + log( + `About to partially withdraw ${formatUnits( + amountGwei, + 9 + )} ETH from validator with balance ${formatUnits( + balance, + 9 + )} ETH and pubkey ${pubkey}` + ); + } + // Send 1 wei of value to cover the request withdrawal fee + const tx = await strategy + .connect(signer) + .validatorWithdrawal(pubkey, amountGwei, { value: 1 }); + await logTxDetails(tx, "validatorWithdrawal"); +} + +async function snapStakingStrategy({ block }) { + let blockTag = await getBlock(block); + // Don't use the latest block as the slot probably won't be available yet + if (!block) blockTag -= 1; + + const { timestamp } = await ethers.provider.getBlock(blockTag); + const networkName = await getNetworkName(); + const slot = calcSlot(timestamp, networkName); + log(`Snapping block ${blockTag} at slot ${slot}`); + + const { stateView } = await getBeaconBlock(slot); + + const wethAddress = addresses[networkName].WETH; + const weth = await ethers.getContractAt("IERC20", wethAddress); + const ssvAddress = addresses[networkName].SSV; + const ssv = await ethers.getContractAt("IERC20", ssvAddress); + + const strategy = await resolveContract( + "CompoundingStakingSSVStrategyProxy", + "CompoundingStakingSSVStrategy" + ); + const strategyView = await resolveContract("CompoundingStakingStrategyView"); + + // Pending deposits + const totalDeposits = await logDeposits(strategyView, blockTag); + + if (stateView.pendingDeposits.length === 0) { + console.log("No pending beacon chain deposits"); + } else { + const firstBeaconDeposit = stateView.pendingDeposits.get(0); + console.log( + `${ + stateView.pendingDeposits.length + } beacon chain deposits. The first has slot ${ + firstBeaconDeposit.slot + } and public key ${toHex(firstBeaconDeposit.pubkey)}` + ); + } + + // Verified validators + const verifiedValidators = await strategyView.getVerifiedValidators({ + blockTag, + }); + console.log(`\n${verifiedValidators.length || "No"} verified validators:`); + if (verifiedValidators.length > 0) { + console.log( + ` amount index status public key hash Withdrawable Exit epoch` + ); + } + let totalValidators = BigNumber.from(0); + for (const validator of verifiedValidators) { + const balance = stateView.balances.get(validator.index); + const validatorData = await strategy.validator(validator.pubKeyHash, { + blockTag, + }); + const beaconValidator = stateView.validators.get(validator.index); + console.log( + ` ${formatUnits(balance, 9).padEnd(12)} ETH ${ + validator.index + } ${validatorStatus(validatorData.state).padEnd(8)} ${ + validator.pubKeyHash + } ${beaconValidator.withdrawableEpoch || "\t\t"} ${ + beaconValidator.exitEpoch || "" + }` + ); + totalValidators = totalValidators.add(balance); + } + console.log( + `${ + stateView.pendingPartialWithdrawals.length || "No" + } pending beacon chain withdrawals` + ); + + const stratWethBalance = await weth.balanceOf(strategy.address, { blockTag }); + const stratEthBalance = await ethers.provider.getBalance( + strategy.address, + blockTag + ); + const stratSsvBalance = await ssv.balanceOf(strategy.address, { blockTag }); + const stratBalance = await strategy.checkBalance(wethAddress, { + blockTag, + }); + const totalAssets = parseUnits(totalDeposits.toString(), 9) + .add(parseUnits(totalValidators.toString(), 9)) + .add(stratWethBalance) + .add(stratEthBalance); + const assetDiff = totalAssets.sub(stratBalance); + const snappedBalance = await strategy.snappedBalance({ + blockTag, + }); + const snappedSlot = + snappedBalance.timestamp == 0 + ? 0n + : calcSlot(snappedBalance.timestamp, networkName); + const lastVerifiedEthBalance = await strategy.lastVerifiedEthBalance({ + blockTag, + }); + const depositedWethAccountedFor = await strategy.depositedWethAccountedFor({ + blockTag, + }); + + console.log(`\nBalances at block ${blockTag}, slot ${slot}:`); + console.log(`Deposits : ${formatUnits(totalDeposits, 9)}`); + console.log(`Validator balances : ${formatUnits(totalValidators, 9)}`); + console.log(`WETH in strategy : ${formatUnits(stratWethBalance, 18)}`); + console.log(`ETH in strategy : ${formatUnits(stratEthBalance, 18)}`); + console.log(`Total assets : ${formatUnits(totalAssets, 18)}`); + console.log( + `Strategy balance : ${formatUnits(stratBalance, 18)} diff ${formatUnits( + assetDiff, + 18 + )}` + ); + console.log( + `Last verified ETH : ${formatUnits(lastVerifiedEthBalance, 18)}` + ); + console.log( + `Last snapped ETH : ${formatUnits(snappedBalance.ethBalance, 18)}` + ); + console.log(`Last snapped root : ${snappedBalance.blockRoot}`); + console.log( + `Last snap timestamp: ${snappedBalance.timestamp} ${new Date( + snappedBalance.timestamp * 1000 + ).toISOString()} ` + ); + console.log( + `Last snap slot : ${snappedSlot} (${slot - snappedSlot} slots ago)` + ); + console.log(`SSV balance : ${formatUnits(stratSsvBalance, 18)}`); + console.log( + `WETH Deposits : ${formatUnits(depositedWethAccountedFor, 18)}` + ); +} + +async function logDeposits(strategyView, blockTag = "latest") { + const deposits = await strategyView.getPendingDeposits({ blockTag }); + let totalDeposits = BigNumber.from(0); + console.log(`\n${deposits.length || "No"} pending strategy deposits:`); + if (deposits.length > 0) { + console.log( + ` Pending deposit root amount slot public key hash` + ); + } + for (const deposit of deposits) { + console.log( + ` ${deposit.pendingDepositRoot.toString().padEnd(3)} ${formatUnits( + deposit.amountGwei, + 9 + ).padEnd(5)} ETH ${deposit.slot} ${deposit.pubKeyHash}` + ); + totalDeposits = totalDeposits.add(deposit.amountGwei); + } + + return totalDeposits; +} + +function validatorStatus(status) { + if (status === 0) { + return "NON_REGISTERED"; + } else if (status === 1) { + return "REGISTERED"; + } else if (status === 2) { + return "STAKED"; + } else if (status === 3) { + return "VERIFIED"; + } else if (status === 4) { + return "ACTIVE"; + } else if (status === 5) { + return "EXITING"; + } else if (status === 6) { + return "EXITED"; + } else if (status === 7) { + return "REMOVED"; + } else if (status === 8) { + return "INVALID"; + } else { + return "UNKNOWN"; + } +} + +async function setRegistrator({ account }) { + const signer = await getSigner(); + + const strategy = await resolveContract( + "CompoundingStakingSSVStrategyProxy", + "CompoundingStakingSSVStrategy" + ); + + const tx = await strategy.connect(signer).setRegistrator(account); + await logTxDetails(tx, "setRegistrator"); +} + +async function removeValidator({ pubkey, operatorids }) { + const signer = await getSigner(); + + log(`Splitting operator IDs ${operatorids}`); + const operatorIds = splitOperatorIds(operatorids); + + const strategy = await resolveContract( + "CompoundingStakingSSVStrategyProxy", + "CompoundingStakingSSVStrategy" + ); + + // Cluster details + const { chainId } = await ethers.provider.getNetwork(); + const { cluster } = await getClusterInfo({ + chainId, + operatorids, + ownerAddress: strategy.address, + }); + + log(`About to remove compounding validator with pubkey ${pubkey}`); + const tx = await strategy + .connect(signer) + .removeSsvValidator(pubkey, operatorIds, cluster); + await logTxDetails(tx, "removeSsvValidator"); +} + +module.exports = { + snapBalances, + registerValidatorCreateRequest, + registerValidator, + stakeValidator, + snapStakingStrategy, + logDeposits, + setRegistrator, + validatorStatus, + withdrawValidator, + removeValidator, +}; diff --git a/contracts/test/_fixture.js b/contracts/test/_fixture.js index 8e61777177..7106b7d5eb 100644 --- a/contracts/test/_fixture.js +++ b/contracts/test/_fixture.js @@ -15,6 +15,7 @@ const { fundAccounts, fundAccountsForOETHUnitTests, } = require("../utils/funding"); +const { deployWithConfirmation } = require("../utils/deploy"); const { replaceContractAt } = require("../utils/hardhat"); const { @@ -24,6 +25,7 @@ const { oethUnits, ousdUnits, units, + isTest, isFork, isHolesky, isHoleskyFork, @@ -1031,13 +1033,14 @@ const defaultFixture = deployments.createFixture(async () => { .mint(usds.address, usdsUnits("100"), 0); // Fund WETH contract - await hardhatSetBalance(user.address, "500"); - await weth.connect(user).deposit({ value: oethUnits("100") }); + await hardhatSetBalance(user.address, "50000"); + await weth.connect(user).deposit({ value: oethUnits("10000") }); await weth .connect(user) .approve(vaultAndTokenConracts.oethVault.address, oethUnits("100")); } } + return { ...vaultAndTokenConracts, // Accounts @@ -1975,6 +1978,112 @@ async function nativeStakingSSVStrategyFixture() { return fixture; } +/** + * CompoundingStakingSSVStrategy fixture + */ +async function compoundingStakingSSVStrategyFixture() { + const fixture = await beaconChainFixture(); + await hotDeployOption(fixture, "compoundingStakingSSVStrategyFixture", { + isOethFixture: true, + }); + + let compoundingStakingStrategyProxy; + if (isTest && !isFork) { + // For unit tests, the proxy is pinned to a fixed address + compoundingStakingStrategyProxy = await ethers.getContractAt( + "CompoundingStakingSSVStrategyProxy", + addresses.mainnet.CompoundingStakingStrategyProxy + ); + } else { + compoundingStakingStrategyProxy = await ethers.getContract( + "CompoundingStakingSSVStrategyProxy" + ); + } + + const compoundingStakingSSVStrategy = await ethers.getContractAt( + "CompoundingStakingSSVStrategy", + compoundingStakingStrategyProxy.address + ); + fixture.compoundingStakingSSVStrategy = compoundingStakingSSVStrategy; + + fixture.compoundingStakingStrategyView = await ethers.getContract( + "CompoundingStakingStrategyView" + ); + + if (isFork) { + /* + const { compoundingStakingSSVStrategy, ssv } = fixture; + + // The Defender Relayer + fixture.validatorRegistrator = await impersonateAndFund( + addresses.mainnet.validatorRegistrator + ); + + // Fund some SSV to the compounding staking strategy + const ssvWhale = await impersonateAndFund( + "0xf977814e90da44bfa03b6295a0616a897441acec" // Binance 8 + ); + await ssv + .connect(ssvWhale) + .transfer(compoundingStakingSSVStrategy.address, oethUnits("100")); + + fixture.ssvNetwork = await ethers.getContractAt( + "ISSVNetwork", + addresses.mainnet.SSVNetwork + ); + */ + } else { + fixture.ssvNetwork = await ethers.getContract("MockSSVNetwork"); + const { governorAddr } = await getNamedAccounts(); + const { oethVault, weth } = fixture; + const sGovernor = await ethers.provider.getSigner(governorAddr); + + // Approve Strategy + await oethVault + .connect(sGovernor) + .approveStrategy(compoundingStakingSSVStrategy.address); + + // Set as default + await oethVault + .connect(sGovernor) + .setAssetDefaultStrategy( + weth.address, + compoundingStakingSSVStrategy.address + ); + + await compoundingStakingSSVStrategy + .connect(sGovernor) + .setRegistrator(governorAddr); + + await compoundingStakingSSVStrategy + .connect(sGovernor) + .setHarvesterAddress(fixture.oethHarvester.address); + + fixture.validatorRegistrator = sGovernor; + } + + return fixture; +} + +async function compoundingStakingSSVStrategyMerkleProofsMockedFixture() { + const fixture = await compoundingStakingSSVStrategyFixture(); + + const beaconProofsAddress = + await fixture.compoundingStakingSSVStrategy.BEACON_PROOFS(); + + const mockBeaconProof = await ethers.getContract("MockBeaconProofs"); + + // replace beacon proofs library with the mocked one + await replaceContractAt(beaconProofsAddress, mockBeaconProof); + + fixture.mockBeaconProof = await ethers.getContractAt( + "MockBeaconProofs", + beaconProofsAddress + ); + + return fixture; +} + /** * Generalized strategy fixture that works only in forked environment * @@ -2558,6 +2667,145 @@ async function woethCcipZapperFixture() { return fixture; } +async function beaconChainFixture() { + const fixture = await defaultFixture(); + + fixture.beaconRoots = await ethers.getContractAt( + "MockBeaconRoots", + addresses.mainnet.beaconRoots + ); + + const { deploy } = deployments; + const { governorAddr } = await getNamedAccounts(); + + const { beaconConsolidationReplaced, beaconWithdrawalReplaced } = + await enableExecutionLayerGeneralPurposeRequests(); + + await deploy("MockBeaconConsolidation", { + from: governorAddr, + }); + + await deploy("MockPartialWithdrawal", { + from: governorAddr, + }); + + fixture.beaconConsolidationReplaced = beaconConsolidationReplaced; + fixture.beaconWithdrawalReplaced = beaconWithdrawalReplaced; + + fixture.beaconConsolidation = await resolveContract( + "MockBeaconConsolidation" + ); + fixture.partialWithdrawal = await resolveContract("MockPartialWithdrawal"); + + // fund the beacon communication contracts so they can pay the fee + await hardhatSetBalance(fixture.beaconConsolidation.address, "100"); + await hardhatSetBalance(fixture.partialWithdrawal.address, "100"); + + if (isFork) { + fixture.beaconProofs = await resolveContract("BeaconProofs"); + } else { + fixture.beaconProofs = await resolveContract("EnhancedBeaconProofs"); + } + + return fixture; +} + +/** + * Harhdat doesn't have a support for execution layer general purpose requests to the + * consensus layer. E.g. consolidation request and (partial) withdrawal request. + */ +async function enableExecutionLayerGeneralPurposeRequests() { + const executionLayerConsolidation = await deployWithConfirmation( + "ExecutionLayerConsolidation" + ); + const executionLayerWithdrawal = await deployWithConfirmation( + "ExecutionLayerWithdrawal" + ); + + await replaceContractAt( + addresses.mainnet.toConsensus.consolidation, + executionLayerConsolidation + ); + + await replaceContractAt( + addresses.mainnet.toConsensus.withdrawals, + executionLayerWithdrawal + ); + + const withdrawalAbi = `[ + { + "inputs": [], + "name": "lastAmount", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastPublicKey", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + } + ]`; + + const consolidationAbi = `[ + { + "inputs": [], + "name": "lastSource", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastTarget", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + } + ]`; + + const beaconConsolidationReplaced = await ethers.getContractAt( + JSON.parse(consolidationAbi), + addresses.mainnet.toConsensus.consolidation + ); + + const beaconWithdrawalReplaced = await ethers.getContractAt( + JSON.parse(withdrawalAbi), + addresses.mainnet.toConsensus.withdrawals + ); + + return { + beaconConsolidationReplaced, + beaconWithdrawalReplaced, + }; +} + /** * A fixture is a setup function that is run only the first time it's invoked. On subsequent invocations, * Hardhat will reset the state of the network to what it was at the point after the fixture was initially executed. @@ -2635,6 +2883,8 @@ module.exports = { rebornFixture, balancerREthFixture, nativeStakingSSVStrategyFixture, + compoundingStakingSSVStrategyFixture, + compoundingStakingSSVStrategyMerkleProofsMockedFixture, oethMorphoAaveFixture, oeth1InchSwapperFixture, oethCollateralSwapFixture, @@ -2645,5 +2895,6 @@ module.exports = { nodeRevert, woethCcipZapperFixture, bridgeHelperModuleFixture, + beaconChainFixture, claimRewardsModuleFixture, }; diff --git a/contracts/test/beacon/beaconConsolidation.mainnet.fork-test.js b/contracts/test/beacon/beaconConsolidation.mainnet.fork-test.js new file mode 100644 index 0000000000..fe218604fb --- /dev/null +++ b/contracts/test/beacon/beaconConsolidation.mainnet.fork-test.js @@ -0,0 +1,35 @@ +const { expect } = require("chai"); +const { createFixtureLoader, beaconChainFixture } = require("../_fixture"); + +const loadFixture = createFixtureLoader(beaconChainFixture); + +describe("ForkTest: Beacon Consolidation", function () { + this.timeout(0); + + let fixture; + beforeEach(async () => { + fixture = await loadFixture(); + }); + + it("Should get consolidation fee", async () => { + const { beaconConsolidation } = fixture; + + const fee = await beaconConsolidation.fee(); + expect(fee).to.be.gt(0); + expect(fee).to.be.lt(10); + }); + + it("Should request consolidation of validators", async () => { + const { beaconConsolidation, beaconConsolidationReplaced } = fixture; + + // These are two sweeping validators + const source = + "0xa31b5e5d655a06d849a36e5b03f1b9e647f911f38857c2a263973fba90f61b528173fb7a7cddd63dbe7e6604e7d61c87"; + const target = + "0xa258246e1217568a751670447879b7af5d6df585c59a15ebf0380f276069eadb11f30dea77cfb7357447dc24517be560"; + await beaconConsolidation.request(source, target); + + expect(await beaconConsolidationReplaced.lastSource()).to.equal(source); + expect(await beaconConsolidationReplaced.lastTarget()).to.equal(target); + }); +}); diff --git a/contracts/test/beacon/beaconProofs.js b/contracts/test/beacon/beaconProofs.js new file mode 100644 index 0000000000..c2b4c1b3cd --- /dev/null +++ b/contracts/test/beacon/beaconProofs.js @@ -0,0 +1,896 @@ +const { expect } = require("chai"); +const { hexZeroPad } = require("ethers").utils; + +const { beaconChainFixture } = require("../_fixture"); +const { ZERO_BYTES32, MAX_UINT64 } = require("../../utils/constants"); +const { hashPubKey } = require("../../utils/beacon"); +const { BigNumber } = require("ethers"); + +describe("Beacon chain proofs", async () => { + let fixture; + beforeEach(async () => { + fixture = await beaconChainFixture(); + }); + describe("Should calculate generalized index", () => { + it("from height and index", async () => { + const { beaconProofs } = fixture; + expect(await beaconProofs.concatGenIndices(1, 0, 0)).eq(1); + expect(await beaconProofs.concatGenIndices(1, 1, 0)).eq(2); + expect(await beaconProofs.concatGenIndices(1, 1, 1)).eq(3); + expect(await beaconProofs.concatGenIndices(1, 2, 0)).eq(4); + expect(await beaconProofs.concatGenIndices(1, 2, 3)).eq(7); + expect(await beaconProofs.concatGenIndices(1, 3, 0)).eq(8); + expect(await beaconProofs.concatGenIndices(1, 3, 1)).eq(9); + expect(await beaconProofs.concatGenIndices(1, 3, 2)).eq(10); + expect(await beaconProofs.concatGenIndices(1, 3, 6)).eq(14); + expect(await beaconProofs.concatGenIndices(1, 3, 7)).eq(15); + expect(await beaconProofs.concatGenIndices(1, 6, 12)).eq(76); + }); + it("for BeaconBlock.slot", async () => { + const { beaconProofs } = fixture; + + expect(await beaconProofs.concatGenIndices(1, 3, 0)).eq(8); + }); + it("for BeaconBlock.parentRoot", async () => { + const { beaconProofs } = fixture; + + expect(await beaconProofs.concatGenIndices(1, 3, 2)).eq(10); + }); + it("for BeaconBlock.body", async () => { + const { beaconProofs } = fixture; + + expect(await beaconProofs.concatGenIndices(1, 3, 4)).eq(12); + }); + it("for BeaconBlock.BeaconBlockBody.randaoReveal", async () => { + const { beaconProofs } = fixture; + + const beaconBlockBodyGenIndex = await beaconProofs.concatGenIndices( + 1, + 3, + 4 + ); + expect( + await beaconProofs.concatGenIndices(beaconBlockBodyGenIndex, 4, 0) + ).eq(192); + }); + it("for BeaconBlock.BeaconState.balances", async () => { + const { beaconProofs } = fixture; + + const beaconStateGenIndex = await beaconProofs.concatGenIndices(1, 3, 3); + expect( + await beaconProofs.concatGenIndices(beaconStateGenIndex, 6, 12) + ).eq(716); + }); + it("for BeaconBlock.body.executionPayload.blockNumber", async () => { + const { beaconProofs } = fixture; + + const beaconBlockBodyGenIndex = await beaconProofs.concatGenIndices( + 1, + 3, + 4 + ); + const executionPayloadGenIndex = await beaconProofs.concatGenIndices( + beaconBlockBodyGenIndex, + 4, + 9 + ); + expect( + await beaconProofs.concatGenIndices(executionPayloadGenIndex, 5, 6) + ).eq(6438); + }); + }); + describe("Should merkleize", () => { + it("pending deposit", async () => { + const { beaconProofs } = fixture; + + const publicKey = + "0xa18bd0e852ab796e8020fb277090aa474fe39a2fce99004dd247324fdbf57584da5ef6a32d1121210b9e7c2b95ecf667"; + const pubKeyHash = hashPubKey(publicKey); + const withdrawalCredentials = + "0x0100000000000000000000006f37216b54ea3fe4590ab3579fab8fd7f6dcf13f"; + const amountGwei = 32000000000; + const signature = + "0x97089277b0819bc5ecab141a2f65274994b4e7940de2e0278eb3714b4e9e85ae5814faa760e53c29b8c15bbb9b30e0c00e07f2b6d16fd1f1174a8c90d172b081d5d5b2b30b94f435045d209598232db27a31a76e652f95ddbfa453c409890668"; + const slot = 12235962; + const expectedRoot = + "0xc27ca5bb5e66430b4eccd9aa5a90bc1783fa8aa2279461eff32751572a98d819"; + const root = await beaconProofs.merkleizePendingDeposit( + pubKeyHash, + withdrawalCredentials, + amountGwei, + signature, + slot + ); + + expect(root).to.eq(expectedRoot); + }); + + it("BLS signature", async () => { + const { beaconProofs } = fixture; + + const signature = + "0xab2de5db0c4e6d61b29a48e4269251bff4565063126fcd5f77a113df22c684db709ba7c95c1eab08620090dac7267f5a07ce7e6a873ce6ec4c609c50419923b7cffdf9384d4157f19deb56f64e9072b464aa4ec0466918ca93ab4e581fab8187"; + const expectedRoot = + "0x5b449fedb4e3fc86a00c8b9c6de4a537c73e342bb1a83c1141d954e7912de501"; + const root = await beaconProofs.merkleizeSignature(signature); + + expect(root).to.eq(expectedRoot); + }); + }); + describe("Balances container to beacon block root proof", () => { + const beaconRoot = + "0x5afbdb19dd02b8d6bf10ee1722753b4a687326f1e7c3a4515ec47be3599b0474"; + const balancesContainerLeaf = + "0xa4181bd72c96848c06c64a28ce7c21563b6063f289ec27d2b5f05aae4dfdb57d"; + const proof = + "0x4938a396a5a5651cdeab2dbc058f866ebcda5fd4fc85a152f22dba474c009791732bb29b9703de0515129d79481b879a3dd9123eeffe7bf8afd4aaff84378560ab5cfe225d99d908dd717ced212090862faf3d42ef6d49b90e5a3d53a13a187ba1ba6d4a2373a34ace4c3bdff56faaf6dc7e93b538bab62355581ae2b679cf30b9db93bd03ab076a7c7dce90b2fcd3162c71977e7e58a31e1ca4a0dded313be333f54b1fbc27a269a843a4d3838e0013984cc884b7a88e4d7f528a1c9a76c98c41dd7ebb8c56a217d6881589c4e09ce0055bea097be50e2dcaa07757da3df8bb1561936559cd736ba1d1802b048e118c414a17c48ff04189f0b8df768d599c9171c990856b4ce5cd0c635561d221a760c5be68a43c7a26b82c92800a16e05ddc"; + + it("Should verify", async () => { + const { beaconProofs } = fixture; + + await beaconProofs.verifyBalancesContainer( + beaconRoot, + balancesContainerLeaf, + proof + ); + }); + it("Fail to verify with zero beacon block root", async () => { + const { beaconProofs } = fixture; + + const beaconRoot = ZERO_BYTES32; + + const tx = beaconProofs.verifyBalancesContainer( + beaconRoot, + balancesContainerLeaf, + proof + ); + await expect(tx).to.be.revertedWith("Invalid block root"); + }); + it("Fail to verify with invalid beacon block root", async () => { + const { beaconProofs } = fixture; + + // The last byte is changed to aa + const beaconRoot = + "0x5afbdb19dd02b8d6bf10ee1722753b4a687326f1e7c3a4515ec47be3599b04aa"; + + const tx = beaconProofs.verifyBalancesContainer( + beaconRoot, + balancesContainerLeaf, + proof + ); + await expect(tx).to.be.revertedWith("Invalid balance container proof"); + }); + it("Fail to verify with zero padded proof", async () => { + const { beaconProofs } = fixture; + + const proof = hexZeroPad("0x", 288); + + const tx = beaconProofs.verifyBalancesContainer( + beaconRoot, + balancesContainerLeaf, + proof + ); + await expect(tx).to.be.revertedWith("Invalid balance container proof"); + }); + it("Fail to verify with invalid proof", async () => { + const { beaconProofs } = fixture; + + // Changed the first byte to aa + const proof = + "0xaa38a396a5a5651cdeab2dbc058f866ebcda5fd4fc85a152f22dba474c009791732bb29b9703de0515129d79481b879a3dd9123eeffe7bf8afd4aaff84378560ab5cfe225d99d908dd717ced212090862faf3d42ef6d49b90e5a3d53a13a187ba1ba6d4a2373a34ace4c3bdff56faaf6dc7e93b538bab62355581ae2b679cf30b9db93bd03ab076a7c7dce90b2fcd3162c71977e7e58a31e1ca4a0dded313be333f54b1fbc27a269a843a4d3838e0013984cc884b7a88e4d7f528a1c9a76c98c41dd7ebb8c56a217d6881589c4e09ce0055bea097be50e2dcaa07757da3df8bb1561936559cd736ba1d1802b048e118c414a17c48ff04189f0b8df768d599c9171c990856b4ce5cd0c635561d221a760c5be68a43c7a26b82c92800a16e05ddc"; + + const tx = beaconProofs.verifyBalancesContainer( + beaconRoot, + balancesContainerLeaf, + proof + ); + await expect(tx).to.be.revertedWith("Invalid balance container proof"); + }); + it("Fail to verify with invalid beacon container root", async () => { + const { beaconProofs } = fixture; + + // Changed the first bytes to aa + const balancesContainerLeaf = + "0xaa181bd72c96848c06c64a28ce7c21563b6063f289ec27d2b5f05aae4dfdb57d"; + + const tx = beaconProofs.verifyBalancesContainer( + beaconRoot, + balancesContainerLeaf, + proof + ); + await expect(tx).to.be.revertedWith("Invalid balance container proof"); + }); + }); + describe("Validator balance to balances container proof", () => { + const balancesContainerRoot = + "0xdbdf8b18bb50a2ac84864bf12779da475aca1e2b98854b2a1b02506396250eff"; + const validatorIndex = 1770193; + const balanceLeaf = + "0x0000000000000000f5b87473070000000000000000000000dd06757307000000"; + const proof = + "0xe882747307000000b3ae74730700000022fe7473070000000000000000000000a0e70a60b292ba301171cec7c6fc2cbab7f3bf8f0dffe2cae5eaef133d3882f8868061031e7c19c701169dba0ff8f8a16652b144b1601012aef58da66aede34f31f54712eae171f1d7115be3e615f0987382f783a846ae438a894398c615efd1cc013d1c8bf5292755492ec4672e7c4a46e680a863f85d6de9ea59412f19c5350e7ee4a0132b26adb417f5d668ee4ec6311fa6552a3f2de928a9df72946fddf09dd9e3994b879b7de86f47c5f5178e3371493dee98b9c5830a9887fda366dea4c7ead58860119d8156dd5a3eb5251eb92dfe2d9c16d3c13cea6910f66af979f2333359c758ba132cb875a4ab011c5e2997f9a9d5d055babf64822b1aca2e13f1bf325f10a9ee255ff13647c4a6237ed79411f5f3fe148c0d7dafbcaae467da03027c76f6fadae20f8aa378b9e297c02d5bf0f557287613cfa049e22ec8c8980ec970e9372314604a5d1d561c967f76f072942c82c126a872ee804848a850ecd282ea91cfb0f1785b6d9cfdf6cddd0694466023e232ee9c8d9fdf264f5ef130fba42247e45be5fed39f7738913901f0659a4b28f5f8161bf82af82b6a094cfc89fa921156f8d23ae5ff5a27b3a362bab64ba53cd6ba38757f94bf3b93542f2852662b715121ee217b0832c95ee45923102b3d39f76bf11dadd2474de2bcd6611c1973c323c3046a54b239454b99a78a8af5e881d601e29e39d98ed70b36fa061cd2fc5311499e6939e2ba3737e47d319dfbcf370deee1c9cac017b945f88eb211f956a065b2ce0f0597ad85e4779af2b74ec8b1d0099b2f71356abaec97644f25f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a7476b51d0000000000000000000000000000000000000000000000000000000000"; + + it("Should verify with balance", async () => { + const { beaconProofs } = fixture; + + const balance = await beaconProofs.verifyValidatorBalance( + balancesContainerRoot, + balanceLeaf, + proof, + validatorIndex + ); + expect(balance).to.eq("32001800437"); + }); + it("Fail to verify with incorrect balance", async () => { + const { beaconProofs } = fixture; + + const balanceLeaf = + "0x0000000000000000f5b87473070000000000000000000000dd06757306000000"; + + const tx = beaconProofs.verifyValidatorBalance( + balancesContainerRoot, + balanceLeaf, + proof, + validatorIndex + ); + await expect(tx).to.be.revertedWith("Invalid balance proof"); + }); + it("Fail to verify with zero container root", async () => { + const { beaconProofs } = fixture; + + const balancesContainerRoot = ZERO_BYTES32; + + const tx = beaconProofs.verifyValidatorBalance( + balancesContainerRoot, + balanceLeaf, + proof, + validatorIndex + ); + await expect(tx).to.be.revertedWith("Invalid container root"); + }); + it("Fail to verify with incorrect container root", async () => { + const { beaconProofs } = fixture; + + const balancesContainerRoot = + "0xdbdf8b18bb50a2ac84864bf12779da475aca1e2b98854b2a1b02506396250eaa"; + + const tx = beaconProofs.verifyValidatorBalance( + balancesContainerRoot, + balanceLeaf, + proof, + validatorIndex + ); + await expect(tx).to.be.revertedWith("Invalid balance proof"); + }); + it("Fail to verify with zero padded proof", async () => { + const { beaconProofs } = fixture; + + const proof = hexZeroPad("0x", 1248); + + const tx = beaconProofs.verifyValidatorBalance( + balancesContainerRoot, + balanceLeaf, + proof, + validatorIndex + ); + await expect(tx).to.be.revertedWith("Invalid balance proof"); + }); + it("Fail to verify with no balance", async () => { + const { beaconProofs } = fixture; + + const balancesContainerRoot = + "0x4830cdf0422b784d8efeffce36b3e58c08f71404edd1115edf4bd4cf4e80354a"; + const validatorIndex = 1770193; + const balanceLeaf = + "0x00000000000000000000000000000000000000000000000025d28c7307000000"; + const proof = + "0x538b8c730700000076af8c730700000061a68c730700000000000000000000009eaac9c5af52f4c6e96fec410ff8b1ff1f8ccf045487fa72c11486955329504111216e61bd2dfab786cfed2a29205b60b968f1f35d562a625e7b25295c5f8ce91627ea1634fe3ccbdb4f7f5392da1af86cf1c9bb6edb52553239023f13d21f5fd1f872cd106cfae02d4f78cae00600778b2e583e7472cc6c3383386631110ac723664a2530d1555f2d799180dc50f5fbfaa53da3e0c40fa2c9115bcd7ff46b89b22824597545d61a2ae64a66daca00c92bd4b3fc3897be1a5f72b0a7041da0b11b39856c9afcaa6eb24247450cf24bcae344b9b298e9daceb740e8997a4e0ec7ccdcba46d9a8d6d6c139e4540d1c10610c96d0b4d817d12e2bc3e99de9aedf9422156e0c6f5978db9e96d8e13fe7a8652f78b33af44d368b092252fd39145c80713d22e5cbb3e6a94e24ddcf79fe867a315a25ea8ee1915ccd6cf982eed922b50b6e700f0efd267a905143deed182ab9f93846dff91d57a510a1ece20e238a6a59c1dc05d70e5aea1483faee9e7c0dcd2fba028c0e162c0107749c339fafd89f9010b30693b00cadf785839dcd8ea23c4a87a2452c3aad0d656f5c69565cc34a4c322b39a028f12887c348d26bea3f2054aa4a4c8a442a4a190c4b615f6cb0f61ee54fb062540a21583bc4c4bd0f5806db6fe0a99211b4ba94487318985281815ab0c9d9216a00fb65a8e25be3a4bb2a486deec0d281a45da03edfcc77f067ce63fc9a41892d10cb75f59b373f4658811eb668d93f256ddd8ccf0930136358ecc0313a61cee570ddbb7cc127f60091fd8b0cbc5ae95a9025cb0c78e9cf566d27f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7301e0000000000000000000000000000000000000000000000000000000000"; + + const balance = await beaconProofs.verifyValidatorBalance( + balancesContainerRoot, + balanceLeaf, + proof, + validatorIndex + ); + expect(balance).to.eq("0"); + }); + }); + describe("Validator public key to beacon block root proof", () => { + // From Hoodi + const beaconRoot = + "0x3f28c90c42bb5e0aa37b8ca105a1f109799e61b2bb682cae3fe8b500e0af687d"; + const validatorIndex = 1217565; + const publicKeyLeaf = + "0x867868ec64c9354c80e29feeb6e36ed6b1073bd5fea3353543f7426689af8fd2"; + const proof = + "0x020000000000000000000000ee45c342e7a183b1c2dee96c7278ab3bece36dec083d993e840e913d2cb98799d46c5631598dfbb681c1af772a0d5fb62a301f972c84ba62dc4e7011c24fb0878e3ef2245a9e2cf2cacbbaf2978a4efa4703728352499952dbcb7ec23c556afa297d7919adfac3e727b832082e95e1fee1d19e44f6f40a072763e2bae1d4c47043cd5f6baa3c7a87c5c3a0eb0a1393ed62d56ec15703728aef5ef6f1d1c944c0a70d1880cccb4ed3fa2c91a042b0be49d2614753e49bb4f1c98b96345f10bbf63ed989b1d9032f97727fee2340bed0f2776cd50a2cf2960f326cc55975be6d14ccd881987a6706c859801c11fc5ccb290cbc2303b705cc4e0702b73370ae7430fbf48d6c30b7b679004a73977d60b3f5035c884423a60780c5b128455a1e502031975ed0c11c460c4a8c1b16ec949e85f583569887eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e14ff7d34eb2887de014b316054b5bbb11918f94a8c6d0b638ab632d23b93b82736cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220570206ba0152ca41d56df2e853d8e059e1f62bb761d8c61908c8caa177558d42df6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784e0a8a1eee5571df2775586c7d3aeab67b387c8e58bed8938dbbe24a359da568b8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb48c72d2f86f620ad65b11564386290f64dddfed28cc93d977df34274aedf0c1d95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fb50330597511de5c536b657b86c73b5ae8d2960b8d67183364cac4a6691e4fd08a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206f4a941200000000000000000000000000000000000000000000000000000000007c6700000000000000000000000000000000000000000000000000000000000058375e031023c26ac0d597879d5eaf849bf16cbd33e9f77436012a6c703d267444c704597f59a5d014a1b06763966b222e9708bbd6e7ce9170f2f0fe5be3607126461b582193e72bb51f4c5919b453493c92679631e25fbdd825ce3a54544fefbe4f4685b557dfe179ee57a6b181f5efe1ae63dac8d006659eba585a602ddc27c0f7ddb37463b17a3dda355d81814e91fccbce4215506825f5407ff21debc1e633b7319c8246e7273b7a58dabc54d4c55d870fedfdb6fbc3fa7cd0b9f1e5b644bbe1f93d524b188f0e156703bd3cd944223e121a877166791ada0a8efe05fac76b5e4f786cbbd669b7ea409307e1322456d8036a99627e23ec0e8fd30568172f"; + const withdrawalCredentials = + "0x020000000000000000000000eE45C342E7A183B1C2DEE96c7278aB3beCe36DEc"; + + it("Should verify a 0x02 validator", async () => { + const { beaconProofs } = fixture; + + await beaconProofs.verifyValidator( + beaconRoot, + publicKeyLeaf, + proof, + validatorIndex, + withdrawalCredentials + ); + }); + it("Should verify a 0x01 validator", async () => { + const { beaconProofs } = fixture; + + // From Hoodi validator 1222119 + const beaconRoot = + "0xafdaf9d9572ee13f9a0d0cf4a41e3e6012dfd65642b1a2adab70a3503304bb51"; + const validatorIndex = 1222119; + const publicKeyLeaf = + "0x9763eab2448c08aaca5f3309aac635809691c3c51e41bd6afdf5c1a2b960a282"; + const proof = + "0x010000000000000000000000d7466c5fd68774d70a5f1590f7b51f879e192d2019327cb9763c96e00332bde93bdbb1032c4b796dda73e515c8c5f7ede9a419be3249810276d8d8740fc8cc9187edd31ddeee92650ae636e7b87b36ed015e4498f1558b2ad3a8b43719b54ea1c16a6fb6455031dd3d169615b9098c7d51eca13d8d0e092750a9efd9cbab5e6b7f36d0fb1ea7624a1e13d5afcbf078734b94aacbc50c4a69d59c0d1b803b986e4fc5f7fe88ad30ab0054842a18e82ce91dd0c10a30cf6f7d10dac06ca475da79ac3dd034d74f55413112262bca97fc091abfb549a1fd9bc4b48745f98956231d71308eb89820d7b4136d2ce3e92dcbb2fbd453eab3df69ea687467eca1972c07a4314c13acf1529e9aca0273aa0f72ed4afd7a3b7b0abaf8bfbebbfec6996a92dce8ec5f0139780a995d7edbd97480e84736b1a843b0421133a61ce5f137f8fc2aa2882b4177c72d044ffd580e17b5774752149754f025bf8c4f60adf28efe8f33e8c2da1266922461c02db9b018a8f1c450c99854a7f7ee6154f5e7d46777334414d6d051a6682f2dcf5f13b42732340aeec38b99496cb9db22328cc11624f671eda45ee940cd2e0933131a63d73e14d87ccf3c496a2b0447926938f277865a197d26d90f39d78ddc929c37c940dbe7fa5f75feb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fffe7e8de8c00f93a9e2716d864b95761eb28b9fde8d0d358647ea6e34a3b3ebdb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784898a54911e092b561e5bb20162d9f9b783010e062d45af64756a83c8c5d361fd8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92bebf52959c496012ab1260bb313a49f054aa8a68f4a671c681964fc2458c3cbef5c95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17f40193653818402a3b9e2cc714f85b60e4ae6eb3b52c506536e4011141b5b81f58a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fc9a81200000000000000000000000000000000000000000000000000000000007c67000000000000000000000000000000000000000000000000000000000000cc31bb7605a892a8e3a0774f3c3298f2f51e0a177d7cbab3cb05bb6a25b2753f493c36e4659b50251ebb083bcbefb1bacc1f81630d0e55049fb9e86600b79b39043b26951ef74da4c0cb5f1e1c4e5a2e429813edf762e51e7e4b4498c9404ecb5ff4bc3271625d63c5c56a9229856ce68d247a5ad43b8e18118a4e24121544687b0f5420f3432931918f1a395f499d3239333c301bb7d925c2b1041395e55e71a9f43bca28a11b8927de32393be7dfbf3255fe25576febfb181fb698f9ecf5ea6a7a12d251976a24713d32e6d23c486a56a4a55891b2121969107b21373f2dce75f0f9b7c815827cd54d4d53550351da9adb7dddba7687c49a6cfb11ef60efa6"; + const withdrawalCredentials = + "0x010000000000000000000000d7466c5fd68774d70a5f1590f7b51f879e192d20"; + + await beaconProofs.verifyValidator( + beaconRoot, + publicKeyLeaf, + proof, + validatorIndex, + withdrawalCredentials + ); + }); + it("Fail to verify with zero beacon block root", async () => { + const { beaconProofs } = fixture; + + const beaconRoot = ZERO_BYTES32; + + const tx = beaconProofs.verifyValidator( + beaconRoot, + publicKeyLeaf, + proof, + validatorIndex, + withdrawalCredentials + ); + await expect(tx).to.be.revertedWith("Invalid block root"); + }); + it("Fail to verify with invalid beacon block root", async () => { + const { beaconProofs } = fixture; + + // The last byte changes to aa + const beaconRoot = + "0xd33574842aabc553574750a093a4f5be40c79306de9915744f0fd297a3570eaa"; + + const tx = beaconProofs.verifyValidator( + beaconRoot, + publicKeyLeaf, + proof, + validatorIndex, + withdrawalCredentials + ); + await expect(tx).to.be.revertedWith("Invalid validator proof"); + }); + it("Fail to verify with zero padded proof", async () => { + const { beaconProofs } = fixture; + + // The first 32 bytes is the withdrawal credential + const proof = + // The withdrawal credential + "0x020000000000000000000000eE45C342E7A183B1C2DEE96c7278aB3beCe36DEc" + + // pad the rest with zeros + hexZeroPad("0x", 1696 - 32).slice(2); + + const tx = beaconProofs.verifyValidator( + beaconRoot, + publicKeyLeaf, + proof, + validatorIndex, + withdrawalCredentials + ); + await expect(tx).to.be.revertedWith("Invalid validator proof"); + }); + it("Fail to verify with invalid withdrawal address", async () => { + const { beaconProofs } = fixture; + + // The last byte changes to aa + const withdrawalCredentials = + "0x020000000000000000000000eE45C342E7A183B1C2DEE96c7278aB3beCe36Daa"; + + const tx = beaconProofs.verifyValidator( + beaconRoot, + publicKeyLeaf, + proof, + validatorIndex, + withdrawalCredentials + ); + await expect(tx).to.be.revertedWith("Invalid withdrawal cred"); + }); + it("Fail to verify when the validator type does not match", async () => { + const { beaconProofs } = fixture; + + // The first 32 bytes is the withdrawal credential + // The first byte is the validator type + const proof = + "0x010000000000000000000000ee45c342e7a183b1c2dee96c7278ab3bece36dec083d993e840e913d2cb98799d46c5631598dfbb681c1af772a0d5fb62a301f972c84ba62dc4e7011c24fb0878e3ef2245a9e2cf2cacbbaf2978a4efa4703728352499952dbcb7ec23c556afa297d7919adfac3e727b832082e95e1fee1d19e44f6f40a072763e2bae1d4c47043cd5f6baa3c7a87c5c3a0eb0a1393ed62d56ec15703728aef5ef6f1d1c944c0a70d1880cccb4ed3fa2c91a042b0be49d2614753e49bb4f1c98b96345f10bbf63ed989b1d9032f97727fee2340bed0f2776cd50a2cf2960f326cc55975be6d14ccd881987a6706c859801c11fc5ccb290cbc2303b705cc4e0702b73370ae7430fbf48d6c30b7b679004a73977d60b3f5035c884423a60780c5b128455a1e502031975ed0c11c460c4a8c1b16ec949e85f583569887eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e14ff7d34eb2887de014b316054b5bbb11918f94a8c6d0b638ab632d23b93b82736cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220570206ba0152ca41d56df2e853d8e059e1f62bb761d8c61908c8caa177558d42df6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784e0a8a1eee5571df2775586c7d3aeab67b387c8e58bed8938dbbe24a359da568b8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb48c72d2f86f620ad65b11564386290f64dddfed28cc93d977df34274aedf0c1d95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fb50330597511de5c536b657b86c73b5ae8d2960b8d67183364cac4a6691e4fd08a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206f4a941200000000000000000000000000000000000000000000000000000000007c6700000000000000000000000000000000000000000000000000000000000058375e031023c26ac0d597879d5eaf849bf16cbd33e9f77436012a6c703d267444c704597f59a5d014a1b06763966b222e9708bbd6e7ce9170f2f0fe5be3607126461b582193e72bb51f4c5919b453493c92679631e25fbdd825ce3a54544fefbe4f4685b557dfe179ee57a6b181f5efe1ae63dac8d006659eba585a602ddc27c0f7ddb37463b17a3dda355d81814e91fccbce4215506825f5407ff21debc1e633b7319c8246e7273b7a58dabc54d4c55d870fedfdb6fbc3fa7cd0b9f1e5b644bbe1f93d524b188f0e156703bd3cd944223e121a877166791ada0a8efe05fac76b5e4f786cbbd669b7ea409307e1322456d8036a99627e23ec0e8fd30568172f"; + + const tx = beaconProofs.verifyValidator( + beaconRoot, + publicKeyLeaf, + proof, + validatorIndex, + withdrawalCredentials + ); + await expect(tx).to.be.revertedWith("Invalid withdrawal cred"); + }); + const testPrefixes = [ + "0x021000000000000000000000", + "0x020100000000000000000000", + "0x020000000001000000000000", + "0x020000000000000000000010", + "0x020000000000000000000001", + ]; + for (const prefix of testPrefixes) { + it(`Fail to verify with withdrawal credential prefix ${prefix}`, async () => { + const { beaconProofs } = fixture; + + // The first 32 bytes is the withdrawal credential + const invalidProof = prefix + proof.slice(26); + const tx = beaconProofs.verifyValidator( + beaconRoot, + publicKeyLeaf, + invalidProof, + validatorIndex, + withdrawalCredentials + ); + await expect(tx).to.be.revertedWith("Invalid withdrawal cred"); + }); + } + }); + describe("Validator withdrawable epoch to beacon block root proof", () => { + describe("when validator is not exiting", () => { + // From Ethereum slot 11788492 + const beaconRoot = + "0xaa2b00b0ff536f96bacdb0f9d65055fb7b392d0d199a78719b5ef59225c7d7c6"; + const validatorIndex = 1930711; + const withdrawableEpochProof = + "0xffffffffffffffff000000000000000000000000000000000000000000000000448348fb3de1f630de0088ae5f4f7cc637944b9926153ed5e9cd877400aa2b114c5a65aee4cc28fcf63783832f80c412e509375be3ba9ec684bf9a6521f1524764024e0c64b2b3400504452ad10993b85a56c9447b0ae493470d44982a7da9353d3ff165c7c848dc4886b59f724cf42046cb9aae7bb4b58770fca3dd309e266c9684fa8eb4cbb72745a94a31923adb83bb9c9130c60043109a3e1b55e30f644c1490a7f0e048ff84548d7c7b785df5b891cebd18388cc5b21dcea995406a24c6d468d09e7792768eacfdf116ac5fd2d72be9d8303f360c30c120919f160b039b8f834a9a54e91c5da223eff8dcab81ee2c477e6cd84b089fbd7fe117faeeb0402b1b8084eec7221f94e537ac8b13bdcb87b52fad38f68d35f6824a155faeb92f49f9a4f93b759f9291605eba7af6d3d04587c99048f52c2144cc195e47597e383d4f8408914e612579596f3001ed8ebc7ccf5559dcfbb0268faac82d8d6401f20b43882f90e6e661d695db8317927af748f6674f34eef20496b597622be20a8e967635f6b34b6b9742bbacff77945ec52ea7b6169428f412d400ed6bb772267b6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa7596422019958f9a01eea80eb3c7af7bdb3e39c3fd97adb79027cc502d6c37dcf7da4549106d1b68ae3c5841c8ffe7cd30a77dec511669b38672e1478a3c1a158d20ba0552fda64b407397d1dfc5ada264d276349e900a8f5efd0ecf0356c6247cfe0922d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb94e9c3619c5d0176371eca00aed8f3bf4b91b9fc083c3c23ea3752295b5db5358d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603aba6cb95a7a65e7c61ca91957ace0a04fdc832ece50802c801f9b86a16cefd6c02c103b3c5a08e39d67b65a11f525842835f27347e678060db94b47ebe4bd2b01039daf4b3328d22c2e0645d19af3fe53f0a6dc3b9c929858ff0bcda67fc410dc18a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fdd761d0000000000000000000000000000000000000000000000000000000000c6341f00000000000000000000000000000000000000000000000000000000004dbb0c24e67a45529b8c7691c5ddcfeb32f3065bf806d9fffd151874e3d93089345bff33be172965d48003555d3de200bf32a79f892b7c47b3169949a4829a841b89b71ff7ab35e4dffc58187339aa1cf8acac069061533cd5d7ded71f54a1dd86cca3a3e76509c80dda9845e63b93be7993d1ea76ab8ee56af58cdabeae946152a8f7fe45f58fd177ac5eb68f74c4641a38a65b5cec9cda9257b7aacf9b80d25d281c85a81e960ec64b2b8f41bc341430fc5b3e0f9875da38a8094d2caa308dbf3e9a950bac1c2c3948d6109a4a99ee10c83d6aeae3c84239be363c9dd78c83851e04afb73b0aa7e834efc40c6e496ebabc958977ed72a273e76e106a68f735"; + const withdrawableEpoch = MAX_UINT64; + it("Should verify", async () => { + const { beaconProofs } = fixture; + + await beaconProofs.verifyValidatorWithdrawable( + beaconRoot, + validatorIndex, + withdrawableEpoch, + withdrawableEpochProof + ); + }); + it("Fail to verify with zero beacon block root", async () => { + const { beaconProofs } = fixture; + + const beaconRoot = ZERO_BYTES32; + + let tx = beaconProofs.verifyValidatorWithdrawable( + beaconRoot, + validatorIndex, + withdrawableEpoch, + withdrawableEpochProof + ); + await expect(tx).to.be.revertedWith("Invalid block root"); + }); + it("Fail to verify with invalid block root", async () => { + const { beaconProofs } = fixture; + + // First bytes changes to 00 + const beaconRoot = + "0x002b00b0ff536f96bacdb0f9d65055fb7b392d0d199a78719b5ef59225c7d7c6"; + + let tx = beaconProofs.verifyValidatorWithdrawable( + beaconRoot, + validatorIndex, + withdrawableEpoch, + withdrawableEpochProof + ); + await expect(tx).to.be.revertedWith("Invalid withdrawable proof"); + }); + it("Fail to verify with invalid validator index", async () => { + const { beaconProofs } = fixture; + + const invalidValidatorIndex = validatorIndex + 1; + + let tx = beaconProofs.verifyValidatorWithdrawable( + beaconRoot, + invalidValidatorIndex, + withdrawableEpoch, + withdrawableEpochProof + ); + await expect(tx).to.be.revertedWith("Invalid withdrawable proof"); + }); + it("Fail to verify with invalid withdrawable epoch", async () => { + const { beaconProofs } = fixture; + + const withdrawableEpoch = 0; + + let tx = beaconProofs.verifyValidatorWithdrawable( + beaconRoot, + validatorIndex, + withdrawableEpoch, + withdrawableEpochProof + ); + await expect(tx).to.be.revertedWith("Invalid withdrawable proof"); + }); + it("Fail to verify with zero padded withdrawable epoch proof", async () => { + const { beaconProofs } = fixture; + + const withdrawableEpochProof = hexZeroPad("0x", 1696); + + let tx = beaconProofs.verifyValidatorWithdrawable( + beaconRoot, + validatorIndex, + withdrawableEpoch, + withdrawableEpochProof + ); + await expect(tx).to.be.revertedWith("Invalid withdrawable proof"); + }); + }); + describe("when validator is exiting", () => { + // From Hoodi slot 1062956 + const beaconRoot = + "0xe67d0075baad5f0cfb93f8997101c73d045d2e258df0e20acd88a595e185c0bc"; + const validatorIndex = 1187281; + const withdrawableEpochProof = + "0xd274000000000000000000000000000000000000000000000000000000000000bcf5e1a2a4374aa96ef475a13410a1c39f9295ab2a42f5cd11afa5a40e35fa037f8bcd9996e785306d0a6a8cdbe092738c68f753b4fc412eac99de8b490c2d214c5e91487ad09c68b3ae7530bfc7b66db25f97bf5629aba7296268d5713359244b93d604e56318360c94e82a2d9e75ac719afb14cc31ad2dccea695cccd9dc147dd9b1f904d8f1740ee2b8fb72a1a16bcf296d8e7671e8ea0e6e4d16c62b79eb5f565f229f79c29a48a498df066104a12ab9135b3ed93038be19d276f6e6006d362f5f8b4b8565b347a08e9033d45eff0ee5798752694a3a2c84cb49df253bee5875fe2899f1263f4878fe6e195e4e572f7a6f8f29e08a1a38bc3a46bad28ebaf02782a4eee9fe6b708d01e4ad1c0a0a3ca5a9f36154140f515aede8ad967e45029fb8110aa3a7474c31895b274a1b829b1af9ee86fb089bda2589924b66e828ed2ada927524c831b65e592ab4d3ba2f4cb0f0cf03c12064be16e8bc6b7daa8a574ce484c0fa6165947e36f8019c81831c3de54e3937705f664ea85d66706576b4f46b9c0d75e37a0ca43f026a6e953c3fd56a099436a7ebdf1b1ca563875527bc32420f5cae1e64ec44f63e84cee47c9254447472483a58fc72542b9f43be2786bc9df664de2511916272fb577c28bc77c3a2ce2b433ef6047a01bd62583711a50330fc0a8524826117f2e9a24e2a20281205736a094be45b1534dc73de458f668eed8a60bdb485e0d45a37e2d4461939de5e74a2a96699a6159c432a4dbb79d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92bebb9537ad12c4d2d8a2700650f81ea6a53a84a027363fc6bebce014f8f16da706495eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fc1e28efda3442aff519e3ddf848524f4702e736abe56801ac815ac3b2d0116e68a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fc96f1200000000000000000000000000000000000000000000000000000000007c6700000000000000000000000000000000000000000000000000000000000059b78aabc3077279caa08a4d1ad4c95eb6b8b2cbb7d778d240ae29ea9889ca3eb50e24f3fb24980544b1b30b03f1d78b30f43873d5cc72d27efcc1ba2a290e31861a5917b8703ffefe09a9f77c1d658dbd26403528479cee506813e56d6cb0c0c885bf02745bd7eda7e3b5cfb5edf4fdf606df6c59402050ff3aead560d732097aafa84e1a3de050902f575c12c2db446526ffe92da401ff9012a307bdd1d649b6468ba2f1178ddd2aaa02c5b32c1723b426fc24cb9f8421363ca7fbdb02e5e33005877d3386aba9b3c3f90fc31d6fb0e30d958f3997b31a4c80865bce70eca497efaf27b7a534d1a2e820520ec9aa52a8e87c24031f3bd637fab00553653ae3"; + const withdrawableEpoch = 30162; + + it("Should verify", async () => { + const { beaconProofs } = fixture; + + await beaconProofs.verifyValidatorWithdrawable( + beaconRoot, + validatorIndex, + withdrawableEpoch, + withdrawableEpochProof + ); + }); + it("Fail to verify with invalid withdrawable epoch", async () => { + const { beaconProofs } = fixture; + + const invalidWithdrawableEpoch = withdrawableEpoch + 1; + + let tx = beaconProofs.verifyValidatorWithdrawable( + beaconRoot, + validatorIndex, + invalidWithdrawableEpoch, + withdrawableEpochProof + ); + await expect(tx).to.be.revertedWith("Invalid withdrawable proof"); + }); + }); + }); + describe("Pending deposit container to beacon block root proof", () => { + const beaconRoot = + "0x43012e41c2314bd2ad884d1e8b647f26fa49079615fd8a288aab302a4125eaa3"; + const pendingDepositContainerLeaf = + "0x29f5ff53b711973c7502fc210c7c820f80b3ece5ac4d5afe7b0281aa940d42b3"; + const proof = + "0x3143c5375c2ff81ac5898cd4951ca273234352195550f0640aba71184b55fdb995e66eb7fedfd7ade2566fc840c18692c4ecc458d52736b6ceed120ebb27d2111ae5deb8a5bf73487bce2069c7586f14404ed4a25c4ed660cb2acfc06731557ec78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c00404d4f2ddbe53d5deb282d04fcc31925fa6761e574b77555f004651b5429a217c7bbd7e3314490a2629fa266fb5504a943731d1ea5d582286ff7659fc68bf64565ffb1f673c0f1fcbd5dd78852ec34fa6e2b9975c4f8676ebbf0ddb0b7a117b800828e1f257df17b9d39f79762fd92197ac859c582e8a3326f5962fed90975"; + + it("Should verify", async () => { + const { beaconProofs } = fixture; + + await beaconProofs.verifyPendingDepositsContainer( + beaconRoot, + pendingDepositContainerLeaf, + proof + ); + }); + it("Fail to verify with zero beacon block root", async () => { + const { beaconProofs } = fixture; + + const beaconRoot = ZERO_BYTES32; + + const tx = beaconProofs.verifyPendingDepositsContainer( + beaconRoot, + pendingDepositContainerLeaf, + proof + ); + await expect(tx).to.be.revertedWith("Invalid block root"); + }); + it("Fail to verify with invalid beacon block root", async () => { + const { beaconProofs } = fixture; + + // The last byte is changed to bb + const beaconRoot = + "0x43012e41c2314bd2ad884d1e8b647f26fa49079615fd8a288aab302a4125eabb"; + + const tx = beaconProofs.verifyPendingDepositsContainer( + beaconRoot, + pendingDepositContainerLeaf, + proof + ); + await expect(tx).to.be.revertedWith("Invalid deposit container proof"); + }); + it("Fail to verify with zero padded proof", async () => { + const { beaconProofs } = fixture; + + const proof = hexZeroPad("0x", 288); + + const tx = beaconProofs.verifyPendingDepositsContainer( + beaconRoot, + pendingDepositContainerLeaf, + proof + ); + await expect(tx).to.be.revertedWith("Invalid deposit container proof"); + }); + it("Fail to verify with invalid proof", async () => { + const { beaconProofs } = fixture; + + // Changed the first byte to aa + const proof = + "0x3143c5375c2ff81ac5898cd4951ca273234352195550f0640aba71184b55fdb995e66eb7fedfd7ade2566fc840c18692c4ecc458d52736b6ceed120ebb27d2111ae5deb8a5bf73487bce2069c7586f14404ed4a25c4ed660cb2acfc06731557ec78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c00404d4f2ddbe53d5deb282d04fcc31925fa6761e574b77555f004651b5429a217c7bbd7e3314490a2629fa266fb5504a943731d1ea5d582286ff7659fc68bf64565ffb1f673c0f1fcbd5dd78852ec34fa6e2b9975c4f8676ebbf0ddb0b7a117b800828e1f257df17b9d39f79762fd92197ac859c582e8a3326f5962fed909aa"; + + const tx = beaconProofs.verifyPendingDepositsContainer( + beaconRoot, + pendingDepositContainerLeaf, + proof + ); + await expect(tx).to.be.revertedWith("Invalid deposit container proof"); + }); + it("Fail to verify with invalid pending deposit container root", async () => { + const { beaconProofs } = fixture; + + // Changed the first bytes to aa + const invalidPendingDepositContainerLeaf = + "0x29f5ff53b711973c7502fc210c7c820f80b3ece5ac4d5afe7b0281aa940d42aa"; + + const tx = beaconProofs.verifyPendingDepositsContainer( + beaconRoot, + invalidPendingDepositContainerLeaf, + proof + ); + await expect(tx).to.be.revertedWith("Invalid deposit container proof"); + }); + }); + describe("Pending deposit in pending deposit container proof", () => { + const pendingDepositsContainerRoot = + "0x29f5ff53b711973c7502fc210c7c820f80b3ece5ac4d5afe7b0281aa940d42b3"; + const depositIndex = 2; + const pendingDepositRoot = + "0x4dfe73a7c86302a851cd480b6b7172d32b24b2f7009bae5596137957257b1c8b"; + const proof = + "0x6fe027418339b44e486f12ca438ce8de6e2f418bf1aaec5971a171fe346c973e0d9ecc366daa3b2a6eee463c6d8094704724c700ced65cbedb77b1fb9a792ea47804dedf885666be91d10c060f63ab1b9f91e8960d3a04225ed738a471cfb9d4a50a6da2319d690eb00f79e2c9a186fbea24cce1ac4928efc63b91b510596f4f5cea5a2e0fdfcb562fd698797a41dc61bf4fab2966c6b2c733c3f8322bd74348c98697ea70c28039f19102d11523fb4de439dcfb87e708e4032e313ffc8cc5697e87f523cac962071ec232257ac26569cae4072a16802b766e4ef56113ca08c86bc8fa34142fa26b430c68f0c3a04566af0816f3283040f029cd1cadae6f86d15229599ade1dbdfccff506fb7afc2e4c85948eed0aa7301a82a83fdf29391213101fe7baa4130773e7d9ac4c33a31aa729a5840be3e06492f934b636027191faa0bd1d43e76578d1d9e4e6ebf5d1603809545f172d5ea7428255001a71ecc170602dbb58d5b6771620a08370061216184a8d5ea3f9562249ead84ade7c6ceccd7cfb59e768724e262d00f8e5ff198172e452908b24f4febaa648edce62942a1532db53b558d2014fe69b564cc0387f844254225c8d0224c5c7e99a1d7722406fb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467650a2a000000000000000000000000000000000000000000000000000000000000"; + + it("Should verify", async () => { + const { beaconProofs } = fixture; + + await beaconProofs.verifyPendingDeposit( + pendingDepositsContainerRoot, + pendingDepositRoot, + proof, + depositIndex + ); + }); + it("Fail to verify with incorrect pending deposit root", async () => { + const { beaconProofs } = fixture; + + // Changed the last byte to aa + const invalidPendingDepositRoot = + "0x4dfe73a7c86302a851cd480b6b7172d32b24b2f7009bae5596137957257b1caa"; + + const tx = beaconProofs.verifyPendingDeposit( + pendingDepositsContainerRoot, + invalidPendingDepositRoot, + proof, + depositIndex + ); + await expect(tx).to.be.revertedWith("Invalid deposit proof"); + }); + it("Fail to verify with zero container root", async () => { + const { beaconProofs } = fixture; + + const invalidPendingDepositsContainerRoot = ZERO_BYTES32; + + const tx = beaconProofs.verifyPendingDeposit( + invalidPendingDepositsContainerRoot, + pendingDepositRoot, + proof, + depositIndex + ); + await expect(tx).to.be.revertedWith("Invalid root"); + }); + it("Fail to verify with incorrect container root", async () => { + const { beaconProofs } = fixture; + + // Changed the last byte to aa + const invalidPendingDepositsContainerRoot = + "0x29f5ff53b711973c7502fc210c7c820f80b3ece5ac4d5afe7b0281aa940d42aa"; + + const tx = beaconProofs.verifyPendingDeposit( + invalidPendingDepositsContainerRoot, + pendingDepositRoot, + proof, + depositIndex + ); + await expect(tx).to.be.revertedWith("Invalid deposit proof"); + }); + it("Fail to verify with zero padded proof", async () => { + const { beaconProofs } = fixture; + + const proof = hexZeroPad("0x", 1248); + + const tx = beaconProofs.verifyPendingDeposit( + pendingDepositsContainerRoot, + pendingDepositRoot, + proof, + depositIndex + ); + await expect(tx).to.be.revertedWith("Invalid deposit proof"); + }); + it("Fail to verify with invalid deposit index", async () => { + const { beaconProofs } = fixture; + + const invalidDepositIndex = depositIndex + 1; + + const tx = beaconProofs.verifyPendingDeposit( + pendingDepositsContainerRoot, + pendingDepositRoot, + proof, + invalidDepositIndex + ); + await expect(tx).to.be.revertedWith("Invalid deposit proof"); + }); + it("Fail to verify a pending deposit index that is too big", async () => { + const { beaconProofs } = fixture; + + const pendingDepositIndexTooBig = BigNumber.from(2).pow(27); + + const tx = beaconProofs.verifyPendingDeposit( + pendingDepositsContainerRoot, + pendingDepositRoot, + proof, + pendingDepositIndexTooBig + ); + await expect(tx).to.be.revertedWith("Invalid deposit index"); + }); + }); + describe("First pending deposit to beacon block root proof", () => { + describe("for verifyDeposit which only checks the deposit slot", () => { + describe("with pending deposit", () => { + // Example is from Ethereum slot 11787450 + const beaconRoot = + "0xa5415fb0e0983887b72f43ce3d8efd1963790e1405f56f423e6bdda923ca3923"; + // The slot of the first pending deposit + const slot = 17043450; + // The proof of the first pending deposit's public key + const proof = + "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4bdc4e0f397dfe60f10c7d1c9a198e071f5b035eabb9e8830627ffbee3feed7fcb3b7d47a8dc0679cef80387a199d3dfd80f88bd28a6f7e5faf86727d0c0eaa60f5965e4b5ed84eca8c4631abcbe2e7dcdb1c113eddab16db4ffd6e681a5c04862f84aab13a65766f9bfd3986f314c2bb295a0d7e5164df25942f3eaea9b23610154d2a221d0042bbf0b0ac737652d7e3f24944f95ebaaf61e01772ff8fa8364805cae429d6cd53dfe6a6aaa3b7c3c20a50a5ac79c6f5f45d23d092007600daa0b335be9d7fb7fa0cb9bff2829c0b12024b800825642c34599fb7635b6f077019d2db831bcf9018a14220a0a2b14d3c698e8bf550d797387529035135fe598e530d991993693ea28319c312e9d14925e3fbdf545ea4de402dd04ba7f45ff936f37a0e1bb63436d0dfa84ac1c9056c23b8da7a04f2963fac189edbb9721ee1a38a1926abe4a8de6725131a92f2bed3681545a5145bf676e465fcfb4f80f5f3f1764ffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220b7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765a003000000000000000000000000000000000000000000000000000000000000caa54f8693175f58ca1a359dfa10592d68316fbdbd6bd9959c6cfae391b3a58021c31acb067ec9840693a9258cb460696dda1452cc8e096b3d2c830fea9242e616dc8ff3b420eecaa31fb1c77e66785d2d9513353df79fbec890dd2f20c0038ac78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c9d23bdb299c03957c0f9cbe012fdd9dec47cc34aa7a1d889fd31f29c08ab5835bf78426cad74f39a544240e1660589b2d313e8844d2732f9bd01147a9d80fea6ea96575b44df85fcfe4245a27e904ee82ccfd0ba6a23fb8f15b364a8f65d073f56c6c9b43ceec1e1bd8b4fe32e1b90969bed029efbaca61efd0e0788bd8fab48"; + + it("Should verify", async () => { + const { beaconProofs } = fixture; + + const isEmpty = await beaconProofs.verifyFirstPendingDeposit( + beaconRoot, + slot, + proof + ); + expect(isEmpty).to.be.false; + }); + it("Fail to verify with zero beacon block root", async () => { + const { beaconProofs } = fixture; + + const beaconRoot = ZERO_BYTES32; + + const tx = beaconProofs.verifyFirstPendingDeposit( + beaconRoot, + slot, + proof + ); + await expect(tx).to.be.revertedWith("Invalid block root"); + }); + it("Fail to verify with invalid beacon block root", async () => { + const { beaconProofs } = fixture; + + // Last byte changed to aa + const beaconRoot = + "0xc1825a2e9c8f353cbedee68dc636854c60f08962b6b246507183f9520dcc04aa"; + + const tx = beaconProofs.verifyFirstPendingDeposit( + beaconRoot, + slot, + proof + ); + await expect(tx).to.be.revertedWith("Invalid deposit slot proof"); + }); + it("Fail to verify with zero padded proof", async () => { + const { beaconProofs } = fixture; + + const proof = hexZeroPad("0x", 1280); + + const tx = beaconProofs.verifyFirstPendingDeposit( + beaconRoot, + slot, + proof + ); + await expect(tx).to.be.revertedWith("Invalid deposit slot proof"); + }); + it("Fail to verify with incorrect slot", async () => { + const { beaconProofs } = fixture; + + const tx = beaconProofs.verifyFirstPendingDeposit( + beaconRoot, + slot + 1, + proof + ); + await expect(tx).to.be.revertedWith("Invalid deposit slot proof"); + }); + }); + describe("with no pending deposit", () => { + // From Hoodi slot 1015023 + const beaconRoot = + "0x936a7ac91224df0522e8fc70521b604b025d37504a432ca9ea842a018ba7546c"; + const slot = 0; + const proof = + "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4bdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30d88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa187eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1ffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220b7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467650000000000000000000000000000000000000000000000000000000000000000049c9edd0970b512318fe4a7d9ff12b2b1402164d872e40948fc7d9042ae6fa615433386cfe4fc95585fb6eeb51df3a6f619db3b3955884f7e5a2c4600ed2d47dae6d9c51743d5d9263bf2bd09c1db3bd529965d7ee7857643c919c6b696004ec78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123ceb818784738117ef339dce506dc4996cecd38ef7ed6021eb0b4382bf9c3e81b3cce9d380b4759b9c6277871c289b42feed13f46b29b78c3be52296492ef902aecd1fa730ef94dfb6efa48a62de660970894608c2e16cce90ef2b3880778f8e383e09791016e57e609c54db8d85e1e0607a528e23b6c34dc738f899f2c284d765"; + + it("Should verify with zero slot", async () => { + const { beaconProofs } = fixture; + + const isEmpty = await beaconProofs.verifyFirstPendingDeposit( + beaconRoot, + slot, + proof + ); + expect(isEmpty).to.be.true; + }); + it("Should verify with non-zero slot", async () => { + const { beaconProofs } = fixture; + + const slot = 12345678; // Arbitrary non-zero slot + const isEmpty = await beaconProofs.verifyFirstPendingDeposit( + beaconRoot, + slot, + proof + ); + expect(isEmpty).to.be.true; + }); + it("Fail to verify with zero beacon root", async () => { + const { beaconProofs } = fixture; + + const beaconRoot = ZERO_BYTES32; + + const tx = beaconProofs.verifyFirstPendingDeposit( + beaconRoot, + slot, + proof + ); + await expect(tx).to.be.revertedWith("Invalid block root"); + }); + it("Fail to verify with invalid beacon root", async () => { + const { beaconProofs } = fixture; + // First byte changed to aa + const beaconRoot = + "0xaa6a7ac91224df0522e8fc70521b604b025d37504a432ca9ea842a018ba7546c"; + + const tx = beaconProofs.verifyFirstPendingDeposit( + beaconRoot, + slot, + proof + ); + await expect(tx).to.be.revertedWith("Invalid empty deposits proof"); + }); + it("Fail to verify with zero padded proof", async () => { + const { beaconProofs } = fixture; + + const proof = hexZeroPad("0x", 1184); + + const tx = beaconProofs.verifyFirstPendingDeposit( + beaconRoot, + slot, + proof + ); + await expect(tx).to.be.revertedWith("Invalid empty deposits proof"); + }); + }); + }); + }); +}); diff --git a/contracts/test/beacon/beaconProofs.mainnet.fork-test.js b/contracts/test/beacon/beaconProofs.mainnet.fork-test.js new file mode 100644 index 0000000000..75bf8eeee9 --- /dev/null +++ b/contracts/test/beacon/beaconProofs.mainnet.fork-test.js @@ -0,0 +1,193 @@ +const { expect } = require("chai"); +const { before } = require("mocha"); + +const { createFixtureLoader, beaconChainFixture } = require("../_fixture"); +const { getBeaconBlock, getSlot, hashPubKey } = require("../../utils/beacon"); +const { + generateBalancesContainerProof, + generateBalanceProof, + generatePendingDepositsContainerProof, + generatePendingDepositProof, + generateValidatorPubKeyProof, + generateValidatorWithdrawableEpochProof, + generateFirstPendingDepositSlotProof, +} = require("../../utils/proofs"); +const { MAX_UINT64 } = require("../../utils/constants"); + +const log = require("../../utils/logger")("test:fork:beacon:oracle"); + +const loadFixture = createFixtureLoader(beaconChainFixture); + +describe("ForkTest: Beacon Proofs", function () { + this.timeout(0); + + let blockView, blockTree; + let stateView; + let pastSlot; + let beaconBlockRoot; + before(async () => { + const currentSlot = await getSlot(); + + // Needs to be old enough so its before the local fork + // But not too old that its before the beacon root oracle ring buffer + pastSlot = Math.floor((currentSlot - 1000) / 1000) * 1000; + + ({ blockView, blockTree, stateView } = await getBeaconBlock(pastSlot)); + + beaconBlockRoot = blockView.hashTreeRoot(); + }); + let fixture; + beforeEach(async () => { + fixture = await loadFixture(); + }); + + it("Should verify validator public key", async () => { + const { beaconProofs } = fixture; + + const validatorIndex = 1804300; + + const { proof, leaf, pubKey } = await generateValidatorPubKeyProof({ + blockView, + blockTree, + stateView, + validatorIndex, + }); + + const pubKeyHash = hashPubKey(pubKey); + expect(pubKeyHash).to.eq(leaf); + + const withdrawalCredential = + "0x020000000000000000000000f80432285c9d2055449330bbd7686a5ecf2a7247"; + + log(`About to verify validator public key`); + await beaconProofs.verifyValidator( + beaconBlockRoot, + pubKeyHash, + proof, + validatorIndex, + withdrawalCredential + ); + }); + + async function assertValidatorWithdrawableEpoch(validatorIndex) { + const { beaconProofs } = fixture; + + const { proof: withdrawableEpochProof, withdrawableEpoch } = + await generateValidatorWithdrawableEpochProof({ + blockView, + blockTree, + stateView, + validatorIndex, + }); + + log(`About to verify validator withdrawable epoch of ${withdrawableEpoch}`); + await beaconProofs.verifyValidatorWithdrawable( + beaconBlockRoot, + validatorIndex, + withdrawableEpoch, + withdrawableEpochProof + ); + + return withdrawableEpoch; + } + + it("Should verify validator withdrawable epoch that is not exiting", async () => { + const validatorIndex = 1804301; + + const withdrawableEpoch = await assertValidatorWithdrawableEpoch( + validatorIndex + ); + expect(withdrawableEpoch).to.equal(MAX_UINT64); + }); + it("Should verify validator withdrawable epoch that has exited", async () => { + const validatorIndex = 1804300; + + const withdrawableEpoch = await assertValidatorWithdrawableEpoch( + validatorIndex + ); + expect(withdrawableEpoch).to.equal(380333); + }); + + it("Should verify balances container", async () => { + const { beaconProofs } = fixture; + + const { proof, leaf } = await generateBalancesContainerProof({ + blockView, + blockTree, + stateView, + }); + + log(`About to verify balances container`); + await beaconProofs.verifyBalancesContainer(beaconBlockRoot, leaf, proof); + }); + + it("Should verify validator balance in balances container", async () => { + const { beaconProofs } = fixture; + + const validatorIndex = 1804300; + + const { proof, leaf, root } = await generateBalanceProof({ + blockView, + blockTree, + stateView, + validatorIndex, + }); + + log(`About to verify validator balance in balances container`); + await beaconProofs.verifyValidatorBalance( + root, + leaf, + proof, + validatorIndex + ); + }); + + it("Should verify pending deposits container", async () => { + const { beaconProofs } = fixture; + + const { proof, leaf } = await generatePendingDepositsContainerProof({ + blockView, + blockTree, + stateView, + }); + + log(`About to verify pending deposits container`); + await beaconProofs.verifyPendingDepositsContainer( + beaconBlockRoot, + leaf, + proof + ); + }); + + it("Should verify a pending deposit in pending deposits container", async () => { + const { beaconProofs } = fixture; + + // This will fail if there are not at least 3 deposits in the deposit queue + const depositIndex = 2; + + const { proof, leaf, root } = await generatePendingDepositProof({ + blockView, + blockTree, + stateView, + depositIndex, + }); + + log(`About to verify pending deposit in pending deposits container`); + await beaconProofs.verifyPendingDeposit(root, leaf, proof, depositIndex); + }); + + it("Should verify the slot of the first pending deposit in the beacon block", async () => { + const { beaconProofs } = fixture; + + const { proof, slot, root } = await generateFirstPendingDepositSlotProof({ + blockView, + blockTree, + stateView, + }); + + log( + `About to verify the slot of the first pending deposit in the beacon block` + ); + await beaconProofs.verifyFirstPendingDeposit(root, slot, proof); + }); +}); diff --git a/contracts/test/beacon/beaconRoots.mainnet.fork-test.js b/contracts/test/beacon/beaconRoots.mainnet.fork-test.js new file mode 100644 index 0000000000..bb22edfa7d --- /dev/null +++ b/contracts/test/beacon/beaconRoots.mainnet.fork-test.js @@ -0,0 +1,67 @@ +const { expect } = require("chai"); + +const { bytes32 } = require("../../utils/regex"); + +describe("ForkTest: Beacon Roots", function () { + this.timeout(0); + + let provider; + let beaconRoots; + beforeEach(async () => { + // Get provider to mainnet and not a local fork + provider = new ethers.providers.JsonRpcProvider(process.env.PROVIDER_URL); + beaconRoots = await ethers.getContract("MockBeaconRoots", provider); + }); + + it("Should get the latest beacon root", async () => { + const results = await beaconRoots.latestBlockRoot(); + + expect(results.parentRoot).to.match(bytes32); + }); + it("Should to get beacon root from the current block", async () => { + // Get the current block from the execution layer + const currentBlock = await provider.getBlock(); + + expect(await beaconRoots.parentBlockRoot(currentBlock.timestamp)).to.match( + bytes32 + ); + }); + it("Should to get beacon root from the previous block", async () => { + // Get the current block number from the execution layer + const currentBlockNumber = await beaconRoots.provider.getBlockNumber(); + + // Get the timestamp of an old block before the local fork + const previousBlock = await beaconRoots.provider.getBlock( + currentBlockNumber - 1 + ); + + expect(await beaconRoots.parentBlockRoot(previousBlock.timestamp)).to.match( + bytes32 + ); + }); + it("Should get beacon root from old block", async () => { + // Get the current block number from the execution layer + const currentBlockNumber = await beaconRoots.provider.getBlockNumber(); + + // Get the timestamp of an old block before the local fork + const olderBlock = await beaconRoots.provider.getBlock( + currentBlockNumber - 1000 + ); + const olderTimestamp = olderBlock.timestamp; + + const root = await beaconRoots.parentBlockRoot(olderTimestamp); + expect(root).to.match(bytes32); + }); + it("Fail to get beacon root from block older than the buffer", async () => { + // Get the current block number from the execution layer + const currentBlockNumber = await beaconRoots.provider.getBlockNumber(); + + // Get the timestamp of the block from 10,000 blocks ago + const previousBlock = await beaconRoots.provider.getBlock( + currentBlockNumber - 10000 + ); + const previousTimestamp = previousBlock.timestamp; + + await expect(beaconRoots.parentBlockRoot(previousTimestamp)).to.be.reverted; + }); +}); diff --git a/contracts/test/beacon/partialWithdrawal.mainnet.fork-test.js b/contracts/test/beacon/partialWithdrawal.mainnet.fork-test.js new file mode 100644 index 0000000000..f361596e91 --- /dev/null +++ b/contracts/test/beacon/partialWithdrawal.mainnet.fork-test.js @@ -0,0 +1,37 @@ +const { expect } = require("chai"); +const { createFixtureLoader, beaconChainFixture } = require("../_fixture"); +const { ousdUnits } = require("../helpers"); + +const loadFixture = createFixtureLoader(beaconChainFixture); + +describe("ForkTest: Partial Withdrawal", function () { + this.timeout(0); + + let fixture; + beforeEach(async () => { + fixture = await loadFixture(); + }); + + it("Should get consolidation fee", async () => { + const { partialWithdrawal } = fixture; + + const fee = await partialWithdrawal.fee(); + expect(fee).to.be.gt(0); + expect(fee).to.be.lt(10); + }); + + it("Should request a partial withdrawal", async () => { + const { partialWithdrawal, beaconWithdrawalReplaced } = fixture; + + const amount = ousdUnits("1"); + // These are two sweeping validators + const validatorPKey = + "0xa258246e1217568a751670447879b7af5d6df585c59a15ebf0380f276069eadb11f30dea77cfb7357447dc24517be560"; + await partialWithdrawal.request(validatorPKey, amount); + + expect(await beaconWithdrawalReplaced.lastPublicKey()).to.equal( + validatorPKey + ); + expect(await beaconWithdrawalReplaced.lastAmount()).to.equal(amount); + }); +}); diff --git a/contracts/test/behaviour/ssvStrategy.js b/contracts/test/behaviour/ssvStrategy.js index c0d6612157..09b3f6a1cd 100644 --- a/contracts/test/behaviour/ssvStrategy.js +++ b/contracts/test/behaviour/ssvStrategy.js @@ -41,6 +41,14 @@ const { setERC20TokenBalance } = require("../_fund"); depositDataRoot: "0x3f327f69bb527386ff4c2f820e6e375fcc632b1b7ee826bd53d4d2807cfd6769", }, + activeValidators: { + publicKeys: [ + "0x80555037820e8afe44a45ed6d43fd4184f14f2ebcac2beaad382ed7e3dac52f87241d5ed8683a8101d8f49b0dbb6bc0e", + "0xb9588334499ee49ac5a1472424e968fe589362b419d9ed09a172f635727466780e11553fbfef1166de3438e9495d7257", + "0xaff707ce005f75d9c8b6b20a170c314219f6596e2fa043c99de0a50565d79155c4ce1ecf98742cd3fdbb878c3762e78b", + ], + operatorIds: [752, 753, 754, 755], + }, })); */ @@ -154,7 +162,7 @@ const shouldBehaveLikeAnSsvStrategy = (context) => { }); }); - describe("Validator operations", function () { + describe("Validator register and stake operations", function () { const stakeAmount = oethUnits("32"); const depositToStrategy = async (amount) => { const { weth, domen, nativeStakingSSVStrategy, oethVault } = @@ -388,7 +396,7 @@ const shouldBehaveLikeAnSsvStrategy = (context) => { await registerAndStakeEth(); }); - it("Should exit and remove validator by validator registrator", async () => { + it("Should remove registered validator by validator registrator", async () => { const { ssv, nativeStakingSSVStrategy, @@ -433,32 +441,10 @@ const shouldBehaveLikeAnSsvStrategy = (context) => { ); const { cluster: newCluster } = ValidatorAddedEvent.args; - // Stake 32 ETH to the new validator - await nativeStakingSSVStrategy.connect(validatorRegistrator).stakeEth([ - { - pubkey: testValidator.publicKey, - signature: testValidator.signature, - depositDataRoot: testValidator.depositDataRoot, - }, - ]); - - // exit validator from SSV network - const exitTx = await nativeStakingSSVStrategy - .connect(validatorRegistrator) - .exitSsvValidator(testValidator.publicKey, testValidator.operatorIds); - - await expect(exitTx) - .to.emit(nativeStakingSSVStrategy, "SSVValidatorExitInitiated") - .withArgs( - keccak256(testValidator.publicKey), - testValidator.publicKey, - testValidator.operatorIds - ); - const removeTx = await nativeStakingSSVStrategy .connect(validatorRegistrator) - .removeSsvValidator( - testValidator.publicKey, + .removeSsvValidators( + [testValidator.publicKey], testValidator.operatorIds, newCluster ); @@ -471,66 +457,54 @@ const shouldBehaveLikeAnSsvStrategy = (context) => { testValidator.operatorIds ); }); + }); - it("Should remove registered validator by validator registrator", async () => { + describe("Validator exit and remove operations", function () { + it("Should exit and remove validator by validator registrator", async () => { const { - ssv, nativeStakingSSVStrategy, - ssvNetwork, validatorRegistrator, addresses, - testValidator, + activeValidators, } = await context(); - await depositToStrategy(oethUnits("32")); const { cluster } = await getClusterInfo({ ownerAddress: nativeStakingSSVStrategy.address, - operatorids: testValidator.operatorIds, + operatorids: activeValidators.operatorIds, chainId: hre.network.config.chainId, ssvNetwork: addresses.SSVNetwork, }); - await setERC20TokenBalance( - nativeStakingSSVStrategy.address, - ssv, - "1000", - hre - ); - - // Register a new validator with the SSV network - const ssvAmount = oethUnits("4"); - const regTx = await nativeStakingSSVStrategy + // exit validator from SSV network + const exitTx = await nativeStakingSSVStrategy .connect(validatorRegistrator) - .registerSsvValidators( - [testValidator.publicKey], - testValidator.operatorIds, - [testValidator.sharesData], - ssvAmount, - cluster + .exitSsvValidators( + activeValidators.publicKeys, + activeValidators.operatorIds + ); + + await expect(exitTx) + .to.emit(nativeStakingSSVStrategy, "SSVValidatorExitInitiated") + .withArgs( + keccak256(activeValidators.publicKeys[0]), + activeValidators.publicKeys[0], + activeValidators.operatorIds ); - const regReceipt = await regTx.wait(); - const ValidatorAddedRawEvent = regReceipt.events.find( - (e) => e.address.toLowerCase() == ssvNetwork.address.toLowerCase() - ); - const ValidatorAddedEvent = ssvNetwork.interface.parseLog( - ValidatorAddedRawEvent - ); - const { cluster: newCluster } = ValidatorAddedEvent.args; const removeTx = await nativeStakingSSVStrategy .connect(validatorRegistrator) - .removeSsvValidator( - testValidator.publicKey, - testValidator.operatorIds, - newCluster + .removeSsvValidators( + activeValidators.publicKeys, + activeValidators.operatorIds, + cluster ); await expect(removeTx) .to.emit(nativeStakingSSVStrategy, "SSVValidatorExitCompleted") .withArgs( - keccak256(testValidator.publicKey), - testValidator.publicKey, - testValidator.operatorIds + keccak256(activeValidators.publicKeys[0]), + activeValidators.publicKeys[0], + activeValidators.operatorIds ); }); }); @@ -670,11 +644,11 @@ const shouldBehaveLikeAnSsvStrategy = (context) => { josh, nativeStakingSSVStrategy, nativeStakingFeeAccumulator, + oethVault, weth, validatorRegistrator, - oethVault, } = await context(); - const oethVaultWethBefore = await weth.balanceOf(oethVault.address); + const vaultWethBefore = await weth.balanceOf(oethVault.address); const strategyBalanceBefore = await nativeStakingSSVStrategy.checkBalance( weth.address ); @@ -719,8 +693,8 @@ const shouldBehaveLikeAnSsvStrategy = (context) => { ).to.equal(strategyBalanceBefore, "checkBalance should not increase"); expect(await weth.balanceOf(oethVault.address)).to.equal( - oethVaultWethBefore.add(executionRewards).add(consensusRewards), - "OETH Vault WETH balance should increase" + vaultWethBefore.add(executionRewards).add(consensusRewards), + "Vault WETH balance should increase" ); }); }); diff --git a/contracts/test/strategies/compoundingSSVStaking-validatorsData.json b/contracts/test/strategies/compoundingSSVStaking-validatorsData.json new file mode 100644 index 0000000000..bdf0ef2a1a --- /dev/null +++ b/contracts/test/strategies/compoundingSSVStaking-validatorsData.json @@ -0,0 +1,1175 @@ +{ + "testValidators": [ + { + "publicKey": "0xb3aad1f5a7b6bfbcd81b75f8a60e6e54cc64fbf09cb05a46f92bab8c6c017106d643d1de70027b5d30fa943b9207c543", + "publicKeyHash": "0xb1d3e454498123d8069f9138420c9a422fab4bf97f9786849b0d7daddb359f47", + "index": 1930685, + "exited": false, + "operatorIds": [424, 425, 426, 427], + "sharesData": "0xa819d1092f8c335cf85d318e9b6a4d82934190294c4a687a92ddb6cb9cd8ce3eee64676b899854535e34af10bd3575291077bb8617ed84424f80608a76664771129aea2f7a7cfcd390a6db04e87dfaefb170f057a2404210b04d1e1c0e1e9b4f886cef456a3dad1da1ac67a2976c59fe8cdb8442de60d70164ca8816dc69037a76a0ba7c06db9e954c67eadab7a4b5bdade0f92bc67ce24ef3e2a81141282124b8f00687fbf322604687b6eab9f62bdd23886c0418eb76878ffd49c26101b931a9dc12231d601a80aec054c382977168109863c1dfb477de7f32836a1d3ef53f76fe8c7c4c5ca79d8bd0194c30807f35b62fa199237b1ec2ad9f73a26a8dd561a6c9fd88b90a64a6a6e9e7c2a0401def70dd3858300366cbe1bcaec5fa8c009e57fe9150a98733ecc843d2c92f839ab31f9b73ee489ea059aff2c1576a8ae81a4db45ef417b07d640dea3fd1f70c279433a78044664e96d36c1fb7851166e601c42af2e9d7a8b7adeffd62a6e7cea8fb8de1610991b63609f833d5c7e2272c7caf07cd49645bf0d059a1f8b7b749b51b044de99df6511d378af6a72503ddb141344bb608c56965060d7d5d6bc6acb642a8b629f7997a5ebc1e6173acc538299acbd500686a0898ba6e33474fcef7f563dec872a5147b6cf13a0e86b4f8e3232698f24f429e9dfd6541bdd8be4e73d216740481ea08a77619fbc6cfc22bda7c43283d8b1057cb1cd66024735e739b875e55d5fcb5dd988dbfe9b2b2196f93d586643ba5642e2d486acb8a841e3901c53676e59ed6562ac0ea23d2e0f395bfbc12f75500352252d20178428df1799cda8c58b423a6c301549cbf75bffe97d1dd8d4ca9ef217e9f16ec2d6bb7fa5d04dc729bfafb7c262e33aa2b13bd4ff52e1050b7c9fe4768c63a8d82a5cb6c959a8e5d9170e82afb4f47b6055f246c883716a97299ee76eacb11b0d1e4beeaf5efd3ecd15f6395b40e9e29b06c308e22d833460b363c8e8ec5497f53866b1655ecca4fe5c34860a2f7d88fee2c3f98685af8729829c971fc1a16d6affb816d559e2440999e8db741148fa33db51a218ec2abdd6bdf190c4b7721b7dd36c1a1788bfb3bc14aeb979ce0e059b46bda1d182180fe46d7c56de8956f6ce64b85b2cca6e31e8c8ea30c3090bbe7454b217c80979bcdb0c802b5a4a0795edd4bcb11fdb7114bc1e59653274689530fcd6f5e84a5e7ad23e1f26129e48bfe450566791126dba7a3da69ad5e6730f498c267e3ca89760a9b6a7cb8dca4c6980fd58433193f78df0562429fb4bbe4e1484adb443e5dd50f3f4a91af0d3d37b987c623945cc5c6fb2db010fee3992c9a16d026410af8d608969da3367628feb29106497c6ef529dc7e48de81e1036c2bf0068d33e7f69ab65c3c13930b3aba111495c80e906542f6047fb7dcc3e770a7b43d87f310700d87a15ff138965bfc78f9d16e875825535d3aca4328aa725939e4a4544cd1fe8e772258485c41b6444b620200b3b2c5172a9ea13b79747157f1417fb8cb5eaf457571913696c779c7300991eeb51b7d61e99735aedb6e7aa9c24ec90f669706bafc28faa585e71d76db262d425d7882c2d7a00013ec4274c01d71564fae5e00f01f8c122728315fef1b80c4e8c1180a82565e82576e1018da9aaae9b1d3879350fd46f7cf3d93366236ea253d9dc4395237c2a06b27fcd19896294a320049773c3d9ac2001f75d3d0c34879f6ec31f4b43bde164147311d020bf5458deab4e5c804f00878d5938e228ce76034c34fff012051cd5a31cf7979cf41e6bc0c53a23b3ee4e8f0a9c20741a6167d0b15d8fbbc78adaaa687bb9c916aee900ebbeb7d75af", + "signature": "0x8754ee1ac90130cfb828172ed9e81fbbdf86f35298f05b3c152db673f2a6a82873e7acbdb5e2d0d035e253c396266254160e627e945a228a4c034157c7a4418efd08173e780ecbc30cedaae05f8239a453df924d8e8e11460f5e10b1c2024012", + "validatorProof": { + "bytes": "0x020000000000000000000000840081c97256d553a8f234d469d797b9535a3b496aa845b5bc63b3e02ed893c12395270ab812d5037f07848422ca5bca0c32ad227acaed3b0df28c00dea5d73add177d61599e0557652662aed8cb0d3660c8c55834045e71eeea95af8e6387ce442bc71c97520f0c73817a12fc938c02c2df6606485fc17ba0cacb97bf60a24649fa25e8f9735f00ba290f5326abdf1ed4b666920e73bb1cd62c992f3351d1ba6df0a34804b7a4f9f771febdaaae11fc7554c516f7330a2b034453ead5abd7a67f15da2acabaa5bd3e8337dcc72c013a44ae25366e122a3d7fef271e36ebece5b34ec3864fed40915d99c8a5ea2b22b4cbaa542ede39011814f92de0b7d3d210ae7e6483b145ed980107c9052536017334ba05e74bb60c8235fcd3a0cc8387d7d50dabb6bc80d251c7cb9d925e1e5005680b13c6e901446672fca79272f255180a281292b0521f9e210512147f9981570e0eba2f5da45e079aabec89844282bd8ce2a207d86d23d3acf91931b4164c3c82a8434711d2aae9e3714fb7f3f85ec4e0b94eebacca7020f6ba9db789289830d37c95c1dc780b83824c33ed9a4602c78cb294cabe50d5288daf7f974b7890d41d0389ecc18a95ef4052e45ef3813d4cb4936346b941b57d0cfec6e6d06cab25db4e5b575332a4f8e8fe5856e09fb0307faff4c4c86cd65f1232633985eee2e835bb5543ffbc9fae96a31e59159c5d02abe69fb16abf79e038bf188feaa215596f530c8a5d78f425c93b7ae2618708a663aeea2072b39f6c82a9e94208934aac447722502091694a22167adf183c7b8989e408b1166b3aeb3e58209a5fd4b8b455008a31a137c8f30ccc0c841cab8e43a450dd228a222cffc8fd7860f457e635b33bb6039def4a443b712c255e1ff4c45acdbfd3f33467499d0ea483d3f8e8517fd3c287c463bdfa01639dd97c4958e5f01466b524822e508b655fdd114eae2c35ad358af0476ab820285053f2ac14c6bf54ca249212a0985c31965e8a844a36f3fdd1445e68ab6ebb2f6d4d1e7804c2c561f64279aa294e71a90ed4e7e1356fbbc14a378a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206f85b21e0000000000000000000000000000000000000000000000000000000000c6341f000000000000000000000000000000000000000000000000000000000064f362ebbebed35401a76e4b6cd3571cb7d4aa34570a5c80968abaca292be98cb989749950f5c0a6759bc187f9ebb6fd274779f4cec4ed289be45b343f142ca5e788566b55848bce1bfd65c53e0ab603d80938d82d26d1b6a8e1052808c1ba5b0ed49f8bb58095e82dd7fa2c5502846df7b21e0939589102f5c68b1ba47b64b651c850a7171c1c56c23f7a1d3bdb116b072af1eb4f4bac80e1b8b7f68fdeca20d7241487ac6f78de4129bad248c82e219d89f3a22db57f1a5f7863ac78f67ecf8f2c61d63f9dd4a198f0de5ce9e1a7e2e38b57be9a716bbbbb4b545286b0268993a054be6fab494c904fa579e6333e89892bd616f8eaae4d23b03b984e1a1a5a", + "root": "0xa2c62c06678c2e53eb6bc5877fc53635daec28d759bcc03bc8a18c2285acfaf3", + "nextBlockTimestamp": 1752571487 + }, + "depositProof": { + "firstPendingDeposit": { + "slot": 17043328, + "validatorIndex": 1930687, + "pubKeyHash": "0xa86e0aec77b07b762564bd628ac9b0aa21fe8a25d4965428b84c3820396005f7", + "proof": "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4bd12205d338846f6b6be4226aead7fa04251bd12628947088d686ad72daf04bb37e0df1f4c0175584f753e22eb83d4a9009031a6c8e743c9515a0569b412f1e3b36bf669495bc280a4bf849932bb7bc023d714cdb3b78f19fa499920de24aa7b1304823365142e7d76d2f097d1bb4c41b173e1e5758e2666010934d5f68aba35feb7d4827b326bdc4413622fefaeb6908241626f88607b839f24e537f8fabc487497cadfb6c208fb64705087c8a3dd45a55d60e3e2e46e251bae554e9c75e2bbcbcd6e0880a444ad1f67570bb0bbaf940b6ef77a67708a14a009f251dda3ff12e77863fba27153df2db9326541c0dce444ab7f5c9a305b68dc5ab41e45c4e420951276fb14e5e3290da526da62697b9a09b6dfc48b7dcb3f7831644647e8e96946ba06e5111bf9243b9a4bcd5e4789b590924cd28484f25fdbcd5ab3a7b19adfa6dcea0966d8d3bbd0049ea6bb030a6130edf9441d8d81faf630cffb0f45800f4ffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220b7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765ba030000000000000000000000000000000000000000000000000000000000002a3777ca27bf5deda8106113e75209be1bdedd27498b83d8c81519263d3793cb21c31acb067ec9840693a9258cb460696dda1452cc8e096b3d2c830fea9242e6bcb577149db45a2a99d67071bfe21f8e7818a1b0fcddac335fa54021a75e8291c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123cb3e2ed37890bdc3a37eb371695599011c1a09d1483d3fe441d48ab33ff8c2a3136bccd5c98577914d93792b53c9d21ca6f615b8bd861b030d20818629d618aea298381442f54c6d06eb6c3f8a78661d48b89371e4024cd1941e223c75220339dcfeade6900d9725020f799e080d5e699a6505347da30ad8bb886aa53421c0aa8" + }, + "strategyValidator": { + "withdrawableEpoch": "18446744073709551615", + "withdrawableEpochProof": "0xffffffffffffffff000000000000000000000000000000000000000000000000520cc47c38af7c1a550b6f04a1de72582ceebd40bb079dfb08d97d3849da57de6ceeb05053496f4ea2db1d8b1c7f728fe5f0b949a7d627147d875602e52563355d3e51da4c780721d702e99fdc0310a7105dcb8733de658f8e894121e8ad8fc5caa7101f69a6d67e9e6f6835672742806ec80edc3acd0bad4df383708d99028a826e9dc3eae08205e98952751f94deaf9ff0dc5426279a21075621c3b500441af412cad17add0dbf378779c6ebeb354c6fbade103de0d3dc78c65923d60d6ea5641dbdcf45e5b27a902a1fe772799139a41dc43866468eabd76212ef332e5b23de39011814f92de0b7d3d210ae7e6483b145ed980107c9052536017334ba05e7d88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa149f9a4f93b759f9291605eba7af6d3d04587c99048f52c2144cc195e47597e383d4f8408914e612579596f3001ed8ebc7ccf5559dcfbb0268faac82d8d6401f2506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1018bd928752744034b76a05994a727119c87fae3ce3caff2cb1d3807cad0d8636cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220147952562a3a98a4a265f70545414a64fe6c0433dda05f03a29b170df883e14906b3322fdc5e641cde884132e6aa7544b6e7f7121026c8fd08f59683da950b3452fda64b407397d1dfc5ada264d276349e900a8f5efd0ecf0356c6247cfe0922d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb9e9769503336649d802c2cd4ed107a54963410e80a054b71d11f2b1dc43f6b5a8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab1953534b5f81d86b07465709bd23266d6fe56930919265f22503033cd5d6bee8b6cd901f1ca51fbe14aab77a43d6f8e4b8de38a085698edb6278abb114ec802c60c669fe6d1e41934a4f44b428cd2a489b7391f83ec744d21c0e77e136580ac28a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fbf751d0000000000000000000000000000000000000000000000000000000000c6341f00000000000000000000000000000000000000000000000000000000003c6de3c326e5a145d657d71bb05b97e9a81efb5bf0434a7444a230082d3aaf67f60352772732f2417abef9873cb294d2ce0d19fb0d4412521f9967409b739b91137cbe798cc943cd04642baea77592a782fc0088367ef1ebd2651e6ccce7e2c09b767729a992858f3952e09bb71406bd9a2752b8370793a6f470237973577420d7c3a72be939235a385ff77cb5f12c8bf22be623202ffe3ad6e9e0ef805df32436bccd5c98577914d93792b53c9d21ca6f615b8bd861b030d20818629d618aea298381442f54c6d06eb6c3f8a78661d48b89371e4024cd1941e223c75220339dcfeade6900d9725020f799e080d5e699a6505347da30ad8bb886aa53421c0aa8" + }, + "processedBeaconBlockRoot": "0x0e4eea9507759af4798091585a4690976949ca9c705d9c3995b914fbc45c2d2b", + "validatorBeaconBlockRoot": "0xb74fbad7041eedf65a68598ac24e1b0775e4b15fe9f7d8d437deb40eeeb01392", + "depositAmount": 137, + "depositDataRoot": "0x9de115e290009d56d89d8d72d7ed1528aa2c2586609f879cbb4ba215f92f5d27", + "depositBlockNumber": 22561013, + "depositSlot": 11780527, + "depositProcessedSlot": 11787328, + "processedBlockNumber": 22567745, + "firstDepositValidatorCreatedSlot": 11787456, + "command": "npx hardhat verifyDeposit --slot 11787328 --index 1930685 --id 1 --test true --network mainnet" + }, + "comments": { + "register tx": "0x33258f692fe9f5cb697918d2acb0beb634142f08efd02014bf345d14eb01b0f9", + "depositx tx": "0x6bee31cbe395435fac4b9632ef718f6cf6f938a48d417d05bb96502fa588cf2d", + "Proof from beacon chain slot": 12145621, + "execution layer block": 22923668, + "comment": "So next block is 22923669 which is needed to set the parent beacon block root, use the following Hardhat task to generate the proof and root: npx hardhat verifyValidator --index 1930685 --slot 12145621 --network mainnet --dryrun true --withdrawal 0x840081c97256d553A8F234D469D797B9535a3B49" + } + }, + { + "publicKey": "0x8120d1eedaa5d2fa64db35aac17a9fa6a1109ecdd45ce6f652cdb9680c8c5cc489faa351565c5dcf59a8bdf9c94493c7", + "publicKeyHash": "0xff08527678e0cdeb9c67bd52cff5338d11b944d63a0f28037aae135da4d8f472", + "index": 1998611, + "exited": true, + "operatorIds": [1926, 1927, 1928, 1929], + "sharesData": "0xb931fab17500ef26774e34e92b3398ac3e2c188ff47f69a5b5458fa8bd9268a5ea15f835efd585dd9a4995d9b85b2d010fe70c59f75038b649e2c0e86a0c49af826fd43f98441e331d1b2d4b35b1ce07d99b85b5a8f2039dcda8810f1fef18b883e957ccb82235292da2117217dbf19ad56a8083d8ab850d014e4f7c51481015de064e59b1733572453662a00ef69e4b9024a4f4c8f3d5f0dacdd2877d6f7d4cb2523163f24401997fb484db6f1611c8fb813c63009aea92b1dd8c4f822a369bab2da23ef711e8959fe3a0d0c33e1bcb08860912c40b6e808f910094e57fc657533d35f172044e1d6ffd931988bf4e25b23e9ca90cb48e9e3cea7deca4eb52da1f944bfab07f9bf054cf5840e7955cf9e2290f2f3adf3d491c3ae764d92f5a20629e3cb40134ce98e03ce37da64a2bdb5a5197f4a1eb9b2a691eb36945c00af76c3a27fc637c4be14ea3c08234a28c9eae4014f291351d61542eb3f58592839e25a51b89a7a210265f13a091e7fe5bcdee6d760f7459038b53f78b0ff060c127855f819cd7d47836257ced4f58ff65b698f0b21f0f72dca961c8bc6563ac51731d45d3c296cddd2262864cc0ed3179a4841d3adfdb9fc46971947e79d22e370caf8804eafcd7187c208f99deddbc6cdd6217114efbadda87e3566005a057eeee3f03c124c808e002b8c640f83a8140febb4a35c9b23ba8cd7f553aed10067e26536b5fa1d50219f706f7cc13b29c356cf3f16b049ed9e0c426b811b4e4254c320dae4ed189c24628f393a65e8bccff0bf80e00d781c3b9deb86148dbcf6d1cfe6197c2ea779baabeec432a0201f7bedc2b4f8ddba26fff2fd087c06d21106eeead909d104840404ee16ead987b2def3da80337c9535636191971cd8af3d8ab151f98428e4dc07954c387081c1a7ce85eba134b92da1aa3395cab5b67dbe796ca809ddc7a564913a13f5b7340ba5db430555ae969ff7d7d70629fcb0ce457143cb4a1df4cba42472b6556c0a6c327734ce1006024e38d00d376173383ca92bcdcbb21bd9d40bae0f4bad05679db658c1290b46e4d2d1145b03717eddc1b4f728ec02999a6a19a7a29e0fa55eb69e8cda88e94aada7ad60e83b3cb137d9826e24b5ed204785ee082ed9fefdd4e39338493fdf99a77de7552c38c309ed2182ab74b3c89eab4397c9345129269c69bc8331b2456a9d1f572cf5be643dec74367423673ec10e82b2dc159c3f6c8b90ccd2765d6607c4e13a0b881355bfbecf24fe78065d9b469b1a5830d0b6be17e24279fd3147983cfeb5a96649adf174fbe1c9f4d5a8a5be93d1b65db22f5bb395cf855ccfadefbc4816ff98c7784c7936c8c67154259e0a3726c417199c2534e995e3cf8241c23273e05d38a5bd48054f73b01ddcbe03b6eb761e17cbf14f0422aaed6140fbff0804423e36c0cc05b3516778cc974208287af29873d12c3580959dd906f6c00f2dc26cbea179fecf8b3c1cddb8192d1c1767aee837dadc137e571b874ba73607c00d7db564dcf9eb4ebc6399cb52d24323aa9160eae065ce23224a0d8e6912afc53abd330a91c7c96e25cc3ccff814c0d49459363cf8e5621ee6388090effdce97a4aeca5dc468e6686cce0c425202b17e935628cccefc5d1503b730178f546ab6c8f0ab2ee7d398ee640cfec3f37629eadef4414627b065b802ae7cda778e92359c25002f5d5fa76d40f244073d8d73a6811ecc9d0cedfab9f41fdda618558cc8aff4f10a15dca2ce82a573eabccc762405bb7ab593911b26d42986cb269abf82b651749509980677b35423502549bfb4f09187e6bb1a7830304ea343ba4bf0768b6f1932966706c5b38cbabd4", + "signature": "0x81fee344968d68e6bb492f34cc3417e883d50ed7e69ddb35f037a17be0feb2c8c0ca595322654bfe616ae0e4b19dbc630e93e9c4840c1d891100bb21540e4c85d40933df15dce0521ac439ed64edc77e3e13ba516bbc7b2d5c478fa728a60c79", + "validatorProof": { + "bytes": "0x020000000000000000000000840081c97256d553a8f234d469d797b9535a3b491b1805668954466fb0147aeac9632ad54b018f53ef52517c2bad115ff7f7f54b11aa27d8825d812e016ae44483dcb2cd662cbd6b0c1a30b927f4c30621297271285faa7f3cc8fc020fcf920efc151101f86a5f576219b79fa974137bbdf54ee564e60ed6fcf0ec96e6bb4fe2b874b8800f2c04dc2d03714bde660e82068c666f293f51767b1e763f2b5d5844d1575e2a6b33adfe2a200aac8fd57ea2b19b1721fe0f0e29e4176c54fda924222034f8bd5b653e44a5ef08412871995c80dd58f0c84094fc565c82f324b63519347e0fd76fe247ea635a28e6867344de840963e1474d4b62ce2c5a84c2bd8a45bfeb4c905be250b3dcb9e623e6a09a6669416dae7858c75e9a1b5543670dc36a5f0f9ceaeee4ecdba9e0f7dea19440698c66b6aa40b233f228f6eb59379b3df1f1016581027b46883c8a02a76bd90e4c278b072f57ffff855e60ca76fbb0c9121d03cca892fa0e2b3f942c0ecc993e073ecb705150f334fa971a409ba43ce4e680282d7f78ab0232ac10fb723a579be2ec6a3b65396df321e2d78e736eaa9966e37ba6c9fdb035928c3dbf6905d7725d96c821eed7bb561a63beca354dd98d15cfea9bd26063a88c5bc7239c6d2e2ca005c40998df74ab529aad3821dea9319704e8ff149097c6ff492a83bb4cd5f159c131aa95b270d258f99109b582cca1dcafe591ce472275a6379645c32e912a5fb5b99c94036501abd271f2e3436341b9fcefcd5d84125e61b990dd523aad3e784e0a8f7d33db8a2f7ee98d5ab1c889450a5194b58bf7e1263e8176da26f579073261fd8c8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb26f652fef22e4abed11428d40423caa52dc662a0f0012124ece37ebf01565dd26067b9b22a54a790d27afedd28b2afb7668e8e46c0163a8027601ccc83289c17c35a19e30a9656a85279f59d3e5c9b733dca6c127aad576ad34e5c87fecb3ec46b0d8d2b3a1970d0155fc2dd4d7e6a7e0dfa109a83415ff1dd0e52589e756a5c8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206f0cba1e0000000000000000000000000000000000000000000000000000000000c6341f000000000000000000000000000000000000000000000000000000000053f00cd3490234bab5baecb1b962d03da77d0f91874755e75b07504b14d7143deeca5c043fe152e2192796a8059dfcdeddb508653788bacc1df29a8c91484a4f7a97d65637c2ba366637832d74398642c6572ba69f63a6f0e891d9aa00c8ce0e58157074fd83c33a3da42d8d8373537501f49cf5bf62b9ad8a486eb2ba47b78a96cf3b980d8565df5ef1239a5b828719f2138a83ad5fe3c15371b72fc2e22fb7c18bb03029db2e0bb972ee4cf79d773b700c47099f277dfebb900ff6af1f144bd2a1456d4f01bdaa2300479e1371a9456141eb70915eb6e478d11035a42c13f602632c350c7304ef064b30e989b663e673077f15b8c5429d914a77f4693d4fcf", + "root": "0xaa4b932f1534932c81f4e0cbee440ec7cf088193ea1fea72ea22c16bcc695cd6", + "nextBlockTimestamp": 1752663899 + }, + "depositProof": { + "firstPendingDeposit": { + "slot": 17340224, + "validatorIndex": 1998612, + "pubKeyHash": "0xb8af6977c45292efcd223118371af89f479beb269af1093cf9ca48aa7d29b879", + "proof": "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b8cda77ba98ed8e8405f204592624bb8fbe4a5eb10ed9069ca0bb3e3a70ee99004bd19b05605fe4644cb9016e4e76ebbbb10acca6dbee683cc57eddd9f929df40039b34ee6b05da953b3d707b2b98c4d0f118ef302f961aa7866494ca7987b035fa2f82507cd46bc39b9962f6a422d9a8d7f73d5bd8b1d6cd6c49e57e47bb6957d95d78537a78184f9193825649136e5123e3bb27c295a5115ce44da37b329d2db3db89abaa62b152f9e1fc67f45825fac730181ab15e8ff6b67f0ad55ad5922c6aa6bf5b1067c03afe637f0d093ecbac8d18fac016c66e706a4aee212c62603884ae986fca3e19d30d6d80d126f81f5afa3a84eaef944a2fc2263ae710b58223dcc61b63b8084d5946c8fbfa3406058a6795adb185c82aee1b2596e38c10fff2bce7ae64470585bbe78461630a6cdc55f55ef9c3a376704b4bf2dc1eae4a5b9b531c2468c38686b513980dd29ffbedd247d402702b22e5f81cad23d9ff11a4454494865d08d4afccf12020ad4307821aceb0dc54c9be73012014eb5228d73300f93b58e6e935014ccb7cbfcdc02f94729d32d21df3dad9743cb79bb9d7dcffded2a036b792a6962dd37ff060f2209444b5aa27d5d5912f786c7874da068f19b9df6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467659512000000000000000000000000000000000000000000000000000000000000d3f2a91c21effe4723024b09b7e5c95db8b3530687212ab63ba493ac18f299d3e944e10830aca3727c0bc2f6c00b5dfeea756927cbd5ba8f11700b322e84b636c6ae848f040580943801fd324784201c8f315a79454f905b0bf02e3d6d6e18bbc78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c55387a817f50dcc4e08924cc090c1c18103eae0959d3496a4c28c50b40b2943dd06a8bf3adab321be3a88d858f0a4cf51cd2c87411d5445e4647de9b444f45c9d465ea5982fe5697a906cfd174cf7bd34b1d35acb7aafc6f291e4ce1b96a1476f7b172c971205667d2d485b24885de27d97993e79ed4c7666653297d235b0513" + }, + "strategyValidator": { + "withdrawableEpoch": "18446744073709551615", + "withdrawableEpochProof": "0xffffffffffffffff000000000000000000000000000000000000000000000000520cc47c38af7c1a550b6f04a1de72582ceebd40bb079dfb08d97d3849da57de314a83935559c88db6061cc20c625555b241dc8b384a34713dd20ea69e1d8b5a0c42964270199f106c348d72b56815a4794c9a016bc17dd49e24f15e482c99f42338707e23bdd5e4cca7c9c6a7f1d467acc8f99a6934805b2e157ce2c04e08a9db56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c55f9bdc52f9460d36177ce9bb02e6f64de423a821f49fe053d69e04fe5457c979efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30d88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa187eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2cece6f22a7cff227066603ee08ceaf910b3ce82a58076944755b0bee36ba68bb4fcf7ef3846b857c4105df9b918d5bcdb2765f42fdfb602bf844d7c4044da17ba10608f38f8cb6e697fa28c3bcbd3a636a941561bdb912bf79438a4d1bcfc3c2f6f9717ccec81d352e5ac9fb7820081f934f972f606361d12e47e708aab340c5ecba91781d7a9a1477e622a52a6d970d071feac0b0740ed1404392aa9fe5e95456d5e184f0e63555a33e9a53a31b52c440c554ba0acd1bce1b5198a1e497d21fde7d01f112f47336622d34369f92ed5b1e10ed9a862f3ca9953125283f29f5b3bd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb86f3ed221fea9c64a8a7f680f130e09222ea6dd192cea5597acbd3787dda4c55b6f2f7292b05c1dae94381ca8144bbcb9d62e66a17d7c2da0908b8be7fe61479b6ad5eef1e8fce0593dcc16f0bc6a1cf7dcb200faff52afae0936e3882284ef88f2a9ad59109fe9348475a34b33d0e8bd6f76b7d0c614cb1d23272f018b3f8ac8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206f147f1e0000000000000000000000000000000000000000000000000000000000c6341f00000000000000000000000000000000000000000000000000000000004743f7acd8fc8aab10e752093436c7806fa6166d77574799fad9cd127bf895a132c93fceb75876bb2f79fb09ce21dc4142299fa677a614b9a0cdf3c1d751052e3a4ebc7d1b18dd6bcb481663edf7e07585e0c4854bc0d0e7b66f0f7049d335bc28ba1fd79c193fc57718db56affd5f3c1ad9e63fd76ee25bfc9a7cd94338bea7e2ad0122f8646e3ad4105a1d67c663fe55696d99961fbd691b03f2d970e75778d06a8bf3adab321be3a88d858f0a4cf51cd2c87411d5445e4647de9b444f45c9d465ea5982fe5697a906cfd174cf7bd34b1d35acb7aafc6f291e4ce1b96a1476f7b172c971205667d2d485b24885de27d97993e79ed4c7666653297d235b0513" + }, + "processedBeaconBlockRoot": "0x7b398482220bf7eab1c5589c5d82a0b35e2cb65e8a8a54e9a3563e0ef896df26", + "validatorBeaconBlockRoot": "0xafa89892c5aae3217d46c0a3b006df119776995aef35efdc9354c30d6620ac98", + "depositAmount": 64, + "depositDataRoot": "0x98944436d0bb89d370a4257db513572eb867d9e17bf2d318464044e9a1b04c07", + "depositBlockNumber": 22800000, + "depositSlot": 12021192, + "depositProcessedSlot": 12084224, + "processedBlockNumber": 22862630, + "firstDepositValidatorCreatedSlot": 12084256, + "command": "npx hardhat verifyDeposit --slot 12084224 --index 1998611 --id 1 --test true --network mainnet" + }, + "comments": { + "register tx": "0x8998d4f271f149cf48eabe8b8d0d6d8d0b914734758ac36f5845c042dab21eba", + "depositx tx": "0xbc44fa6de0a8e1e307aff7a4702681de4a82ec495db5eb2152a632538ac2bd86", + "Proof from beacon chain slot": 12153322, + "execution layer block": 22931335, + "comment": "So next block is 22931336 which is needed to set the parent beacon block root, use the following Hardhat task to generate the proof and root: npx hardhat verifyValidator --index 1998611 --slot 12153322 --network mainnet --dryrun true --withdrawal 0x840081c97256d553A8F234D469D797B9535a3B49" + } + }, + { + "publicKey": "0xa8362aa1dd61743b73bda741c9144636e0c2af9d9e63f1b0d182d5ebdbd58c3c988d25e53f6da4bdb102efec2ae7f8bc", + "publicKeyHash": "0xf752db0fbb501101e6eaa793b251f19fb00cb5265c2298cd88ece23fb815570e", + "index": 1927585, + "exited": false, + "operatorIds": [684, 685, 686, 687], + "sharesData": "0xb4975324cc4c5c6e79772013e87904cff08e6445310ad2a06d495ec7a8b5b80322710be8ec39619da164066ad04a1605131f2efe8c0508ea2c2d4c13aa4bbe6058a44dfd528d5a33482a7e8507486d6b91565450e8d9e695ee96a79dff065308af178eb109fc3ffaf56552b5a5f0be236b936e0a6d9896bc9b2d4040b0aae455eda40849ce803ac578b1df306ed88c0ca9b3422c268a8e9a7686338a0f8c8fc41159add5a02a419aaf281bf2f083492d8c826dfdaebc803e024619e90e4ed24a97940ff3ad744d52fd299a9aeb2f5fc14e33f0c55e133c85580e8b1e5c6b947390ceef4c3bfaf297c2d1acb28c6caa069578b43cdd722f4c48ba52a4b7247fbbf5303b3b9a730e00f908b2440dab229921e6c7f8bc657271f3009b6dc0af0da62abf71f21063b65a4707a3b610267ede2f4fabd70b8ad9b37c847ecfc6c901b2bf252216d94ad338afa2049c7781dc9a436d85d438815b446a7d8145e146d72d3db4ffbf8143217d9834e7c91031fe3266e62476676204442114b1574b14714c76466bf4bca746ff59ddf37cef15991173cc3ddafb94a101cc2f5cd7997ef272bc725d4fc4c4fda60e346a8c728e31bcfbf1f758ddf060d2a2c7a6bd13b73566101ffe0b855e5aea6477fcfbc7f258e824ac6fad557225475ca4f492619e9f0514726c8abab31502ffac4b31fae56cb9d6d0011a1e02121e10cf994eee9ecfc7efd6a75793de806a2f743a1fdef11c741c51171febfe6984374726743975ab5b1fe269baff7aa47c0d96f0cd324a979c3e4427f9806f6eb4340ba05813dcb1a4df7cc5c14f932cd9de2712b16b7ea9f5e0ec3999336463300ad83f42d942bdd439fac3dc0e9157752e656ef7e69412dc20be850426be09f8143d080170f4936b51b4460fb9096b810dbe9e683f75c6d2c707c80d8ab5bfc3801b716b43f5180eb6d876c332c8451cfbb6bb67b54750ddda1eeeb16dcb28884b9d8e0838c2245cc627d4f8758db0a92524cfac2b1dc30363afba5a87dcbe5942659e81615d139b67d22aabf28f29cb89e901220f88da55097a82d4d21ac3540ac65413e6127784784f41c6659ec1d9b77be093a7225b5bcb2a0a939d3eb797dbcb698751c99822ca87a08da5596f443a7e90036c3df0bdc6287c095f1e992b397b19f3d4db0b5befcc7ec912d544ff840c7e18ae0278869b9744ca958bd7baad3640f4ad1230e539dd8fa18474d7af254e26f623ff8b31c5cfbb899a0ff1455b0863de2c3b8055b22a7ab22ede907e260b53d4e8b163f73a302531d6d54e19433fbe6f37af28ac0ee57b2c52db7f8ea78d52295d9590d5ce698f4c5d128277988be1651682dca48ae68f456a702530c0c09881cbee5c302eb3067a8ea3c12359b8b527ce1195bbeb086a28451af3035d24af4bd1ce88cc91e0c3068cbf5f48029516dc37e54f0ea770093c27208c6b38076091708f00101410b81ab1398aa2c49a04ee3e102de1574cf47accf6565ac796699933a1f5b6448a8063dfa4d1d33ba395201492ac1e979054cd50c4005b5bca8744b8dafbac66f85266203c90f82896ecc883402d08d776f2d17a1cf0342e5023479423c0744565b5e2e901b9234e82601cf9a4ac11a1cdfd3ef43b578a2817622c8f3661dc0b5d3a486d66d9f5c7389d50e7e309a6a636d502c3de5d655b5facbf53bd00211c9feccc8e3b23ed117476e995d43fa30bad694794f4e4bc9f6290b43de4be19b93eca086b46089abeb3c69e69de1f2acee146274b4009348d9f4be26237d28f89ff40f985bcf81d53b2c78a74fee19ce3710d96b2e74ada3d61a4f4c1ea9586e94bb0454d0e5e5362dc6098be4cf3af", + "signature": "0xa26f9988d2df42692270c1cf4c218feabcdb30f83fd751e1d480e82d09871a208edb7b1b20b922d23f0c1ed0698d4bf502554cd2312eb3058328091e0e4ef61bba31f2b3d872aaae536278d05e9a466c10c5b4722819787b44e09e3bb5c593b6", + "validatorProof": { + "bytes": "0x020000000000000000000000840081c97256d553a8f234d469d797b9535a3b495e620cfe84be23c7a92a30b1f889d77cfd37a6b9dbeff1cfefbf3ed905910513e109c69f738cbbd417a83007beaeb0b06e1d034498d35d8355c81064668a171b2830c29e1cef90cc6200635db0208fc6da71a8d8e99196d13b688420d614f80a2481fc13d2e2894ae19a130f72af77babe125d211b6ad022a795dd1360b30f6e262c14fd2a32c3f0bb7b96b11f68f195af3e2a19a8948565a3d12cc40cf41916ede494e2c2248a67cc717902972cd1858ac76a68a491a8417e324cea855b3e0f6cdf8c0d3b2d0641dda555b19edf95c9f6f0c987cc88c9554e97e6a0bed9919b284d7b9359fe7df982ee321e66d473d21bfc524ed9b09a9dfbce28e87173d4bbbef8ffde9d072721496577810eb8c219b03db4b46471f0763d32a2ab768ad6938fec83626dbb7fa130b9a415b3a125500baf87ef782a67dfc9604ce38bc80ee9fb502b89ff6f2c87cf9e696a492348b2a50bbf5767e234f8672f5256ade0deff305df63fe1d11f283a0e965d95a8be4f40b5b1bead4d55dac6674bb3cbf7d1bc07bba569fa0b2f386f8efefcb24b7cfaac6be7e73eb7ae97fca38a1ebc1b9f86af24cfcac3734e7f24ff265b51285f4002d5cafd45429e59ea0375d6e288d722b8c59e62a63e0706247b3aae66b03625c25b8cc079a4e9209cb976283599b88f694f614db2843534cbeb885c71aca540188f9fdf4a8fb852337333ed4f832b8b81cf2cc5e4e17fbdbd19332d6e025dfefd4bec18be76845b67e83542af8fa4551fc2db4ca6a870d31c4a6af26e69579753f670b3ecb9fb78c3537ba48c60523fd7a19f866269d0641d303ae741648236ede160774f5d1d879a52dd2e7e0d592a953a5daae1495a523295b6516999a9744d417b605f5b74c2c3534a1d747ff031c0422fbd1bbd34fccbc7cbcabb95775d61db45174b4c1ee2a9c19e0f7d0926e1642b3868b3000319d803edeb4626b618f6989882bad8bc6405e5af997d5cc7769b91c41fbd9e64f638367db170875a47c7da074ba0f0ff728be98e03b25495e78a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206f8ebd1e0000000000000000000000000000000000000000000000000000000000c6341f000000000000000000000000000000000000000000000000000000000006504d46a40b6242ed1d848798d9e58f46f6776f60d2a063a739c4c2fee0b2de91e36e8f730bb8f3c6803ea57a3cbd5e552c5572b4d6a2987c32228cb0cd892937a4cab7f7ee9eaa0ebcf2dc5f918b31c210537fe46ebc8de6cb964e0ac8a372acc61155d04e0a3e39cc3e9ad1cdf9f67c738c07794028f51ea4666ef5b3a2ba4def00275e900ea09e68a54e0a0eaf82e94addd5cccad6bfac1f982d8373881882bbfff6b3763787a0866282ba7d5703a830bad02ca7e3935494264675edc66a2ee9f3c925bdce756c501f0b1930e0ee15061f84eb2709a406ea4841f254dcd0f5268557b9af625ea3b350a12c99e12f942d6b74507095f9299f6be5c962a8f0", + "root": "0x808958a48f7d1ad71aeab8c815f602795b0b1636dfee63cb0f0b5a564fb4a5cd", + "nextBlockTimestamp": 1752711143 + }, + "depositProof": { + "firstPendingDeposit": { + "slot": 17028896, + "validatorIndex": 1927593, + "pubKeyHash": "0x07fc66ef1a40715e388bc722aad2a2a1686f0e46a58b6295a812fd46f92e9a6f", + "proof": "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b592b08142f2483f93f561ce14688731f9c5f5d5dc138bac26bdf286072787cda302bcccfe40bc731772ac4d671166b3141b99c70be5cbad7357725790ab3fbc89c07b43649038b7d52d7a067f194d6e3d73ad71500cc507da82e72ee20bbb4123970830406a0ead0f515fb7704a027853779699ff430cc7785bbcd2a714ab42149de5c11c936748734d7dca8553005567b36900fbe8328bba6fb677d004c45ca92873e3250fff5aec2bda0849144b69dfa2a4bd633c8c08949efe663f91863599bdfc26b03bf295bc598b5ceb7489495ba0979213d782f5f9cde75afe6071677bc533bdf27459ed7690e8099a13e20d44f1dfd237a956194a2ea3db285ade84d91de77ea16c34e1abc9cee7a7cd0023db7b37e5f285b08a129acb63376929aeaaa4e0195b6b2821055c9667c4e081a2af36f6987873e054aaf864131c33278588668a608f8d4b8c7817be4c95a60b61696dfecac49a40875aaf82482ae4ca620c3e0b2b4acbea95f348d7232b314ea7cc4a1efd9f4fcb4b55ce31566c9525d682f29a8abfe74c86db1e1215f8e9db607c045d1e4221dc153ce554b9ddd92f2c4b7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765340c00000000000000000000000000000000000000000000000000000000000074684e7b24c524cd553278d669f72c6078c36f4574e1e370a66925adef83e89b4f643b91d705421b98027b93bea77793c45ae4a527028166787656e4d8178e9e430143e20ce270268eea4cbc8506b92e002b54018abed5d76d3c292a4c057a2cc78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c294908f45d6389392a77d942a0093d7742f8661588c27b929ee216505252e400a087ec3b5a397d8a76957135521728d32d959cdf0a639facc61541064c8316bf32781ed20b400f50ce47c91acdcc918ff92c0604a246e25ca232cd2e9eabd7aaf37b7817a027ee38c9a9db401cc958422e2925e115a9b171a923da99e6f7422e" + }, + "strategyValidator": { + "withdrawableEpoch": "18446744073709551615", + "withdrawableEpochProof": "0xffffffffffffffff000000000000000000000000000000000000000000000000520cc47c38af7c1a550b6f04a1de72582ceebd40bb079dfb08d97d3849da57defb24f9ebd2c270ea6b96c087b3b5be0acb2416186aab3da306481c721e13d01108ae2a10bf0c56033b0f4d3a918f91c0723c9f4ec32696397d0a78d8884332b2efe57852c2affe19abfd57e08482e6711951995fd54a2fbcba623e4ac9097d2b86a126acd2fba205ca03dab273e1cffa4bc6a523517eed384710bc0b46ebc7b8456ff6837ccc81be10947d58b0c99e3de2fa1dfa78c843e4abfd89d9b69131b7536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123cf97da4078fa536452484cdcb216bc6ec8edf2418774a3ae0d67c0bd7b791d4c4d88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa188eb3d736f12a8f56e5749d7cbddbf870447c56cc254b0fa142a588c0402ef25a964c62309b7aa414c1b0d3f62456ef17ef614ddaae71916c2d730a4baa78f11506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1ffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b4798c97b3236c39741b89cddd9497904f11e4644a622e8a4d762e9dee491f6bab7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5f3b140b5516e85e8e4085b4918887a46e3aa3224843f42f97ba73b65c268a381e034555ef342ce0e1f6193c211e6a3299d44f89a4d9f9a21e6c7de6ca8c548877d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb1367b2645f3ef560be4e5b7c3c5c2bc43b41c960955bdb8e5d1ec24ed360bfd38d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603abfd026ad911e0de0fdbc80330e18b536b664f9ef90b37a33a7bdcf0ef6c183cf5904d42351665ac653602142c3f824d95e19e017c80a4cb2384e85f998e4eea2ec347febc89bb22abea691114b1b5d01e7ba63c1b6fbbf40c44c5cbd95da5ed128a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fa9691d0000000000000000000000000000000000000000000000000000000000c6341f0000000000000000000000000000000000000000000000000000000000a41cd70d5a887d01be52ebff2286fe4dec70d6473847692f5bd9545101edfd589a5ab2eb0c7bfa5d0a11d246d56e1ea74a4786b03a00369f26a9dddecdc088603943ac0a2af475d163457594c60ac28784c938f9b52199f03fd0dddea4cdf204fc762ea488aee498a778152b5a8ebcb210f0edda3458d679cd923520beeb22513418daaeab4d3be43ca0bc46de23477e7eaf9b6c96e595ff0ae6256bf92ba3fea087ec3b5a397d8a76957135521728d32d959cdf0a639facc61541064c8316bf32781ed20b400f50ce47c91acdcc918ff92c0604a246e25ca232cd2e9eabd7aaf37b7817a027ee38c9a9db401cc958422e2925e115a9b171a923da99e6f7422e" + }, + "processedBeaconBlockRoot": "0xc57cc4036228845cd7523327554fe96373325d1c39db3ffa42ee85edcc5947a7", + "validatorBeaconBlockRoot": "0xb2d8101381aa2631a6a0a1db0b18887b510a861d5a45cb1dddf7a554d333cf70", + "depositAmount": 500, + "depositDataRoot": "0x36a4a9d68b0545fcd002334a473bc4229e59da5dfc66c72912d75030763a7b6f", + "depositBlockNumber": "22539323", + "depositSlot": "11758655", + "depositProcessedSlot": "11772896", + "processedBlockNumber": "22553449", + "firstDepositValidatorCreatedSlot": 11772928, + "command": "npx hardhat verifyDeposit --slot 11772896 --index 1927585 --id 1 --test true --network mainnet" + }, + "comments": { + "register tx": "0x94e113697fdae7e6be3dfaf128c8147a7a7896f46f57c42d825dfc5ad72a021f", + "depositx tx": "0x5823e074eddcdfd72b79a184cb7d6b21bd4d6c959468dd00a751756125b6477d", + "Proof from beacon chain slot": 12157238, + "execution layer block": 22935232, + "comment": "So next block is 22935233 which is needed to set the parent beacon block root, use the following Hardhat task to generate the proof and root: npx hardhat verifyValidator --index 1927585 --slot 12157238 --network mainnet --dryrun true --withdrawal 0x840081c97256d553A8F234D469D797B9535a3B49" + } + }, + { + "publicKey": "0x970e5c88a87ee3e7cdf753b3b861812dd056e75446b0e40dbacc8547a2566b8f53c2c71090cba83ff1315040b16827fe", + "publicKeyHash": "0x26f5bde43076d37579ae5d1f5082883420d4178074a0ae1b39c5f2f56efab3a3", + "index": 1897126, + "exited": true, + "operatorIds": [684, 685, 686, 687], + "sharesData": "0xb4975324cc4c5c6e79772013e87904cff08e6445310ad2a06d495ec7a8b5b80322710be8ec39619da164066ad04a1605131f2efe8c0508ea2c2d4c13aa4bbe6058a44dfd528d5a33482a7e8507486d6b91565450e8d9e695ee96a79dff065308af178eb109fc3ffaf56552b5a5f0be236b936e0a6d9896bc9b2d4040b0aae455eda40849ce803ac578b1df306ed88c0ca9b3422c268a8e9a7686338a0f8c8fc41159add5a02a419aaf281bf2f083492d8c826dfdaebc803e024619e90e4ed24a97940ff3ad744d52fd299a9aeb2f5fc14e33f0c55e133c85580e8b1e5c6b947390ceef4c3bfaf297c2d1acb28c6caa069578b43cdd722f4c48ba52a4b7247fbbf5303b3b9a730e00f908b2440dab229921e6c7f8bc657271f3009b6dc0af0da62abf71f21063b65a4707a3b610267ede2f4fabd70b8ad9b37c847ecfc6c901b2bf252216d94ad338afa2049c7781dc9a436d85d438815b446a7d8145e146d72d3db4ffbf8143217d9834e7c91031fe3266e62476676204442114b1574b14714c76466bf4bca746ff59ddf37cef15991173cc3ddafb94a101cc2f5cd7997ef272bc725d4fc4c4fda60e346a8c728e31bcfbf1f758ddf060d2a2c7a6bd13b73566101ffe0b855e5aea6477fcfbc7f258e824ac6fad557225475ca4f492619e9f0514726c8abab31502ffac4b31fae56cb9d6d0011a1e02121e10cf994eee9ecfc7efd6a75793de806a2f743a1fdef11c741c51171febfe6984374726743975ab5b1fe269baff7aa47c0d96f0cd324a979c3e4427f9806f6eb4340ba05813dcb1a4df7cc5c14f932cd9de2712b16b7ea9f5e0ec3999336463300ad83f42d942bdd439fac3dc0e9157752e656ef7e69412dc20be850426be09f8143d080170f4936b51b4460fb9096b810dbe9e683f75c6d2c707c80d8ab5bfc3801b716b43f5180eb6d876c332c8451cfbb6bb67b54750ddda1eeeb16dcb28884b9d8e0838c2245cc627d4f8758db0a92524cfac2b1dc30363afba5a87dcbe5942659e81615d139b67d22aabf28f29cb89e901220f88da55097a82d4d21ac3540ac65413e6127784784f41c6659ec1d9b77be093a7225b5bcb2a0a939d3eb797dbcb698751c99822ca87a08da5596f443a7e90036c3df0bdc6287c095f1e992b397b19f3d4db0b5befcc7ec912d544ff840c7e18ae0278869b9744ca958bd7baad3640f4ad1230e539dd8fa18474d7af254e26f623ff8b31c5cfbb899a0ff1455b0863de2c3b8055b22a7ab22ede907e260b53d4e8b163f73a302531d6d54e19433fbe6f37af28ac0ee57b2c52db7f8ea78d52295d9590d5ce698f4c5d128277988be1651682dca48ae68f456a702530c0c09881cbee5c302eb3067a8ea3c12359b8b527ce1195bbeb086a28451af3035d24af4bd1ce88cc91e0c3068cbf5f48029516dc37e54f0ea770093c27208c6b38076091708f00101410b81ab1398aa2c49a04ee3e102de1574cf47accf6565ac796699933a1f5b6448a8063dfa4d1d33ba395201492ac1e979054cd50c4005b5bca8744b8dafbac66f85266203c90f82896ecc883402d08d776f2d17a1cf0342e5023479423c0744565b5e2e901b9234e82601cf9a4ac11a1cdfd3ef43b578a2817622c8f3661dc0b5d3a486d66d9f5c7389d50e7e309a6a636d502c3de5d655b5facbf53bd00211c9feccc8e3b23ed117476e995d43fa30bad694794f4e4bc9f6290b43de4be19b93eca086b46089abeb3c69e69de1f2acee146274b4009348d9f4be26237d28f89ff40f985bcf81d53b2c78a74fee19ce3710d96b2e74ada3d61a4f4c1ea9586e94bb0454d0e5e5362dc6098be4cf3af", + "signature": "0x9D2C305E0D26E3FDECD7C224F907B211926E6D457173EA2AA66C36FCE9CB53ECBD5C86F2A33AAEBB6C991359E5CDAD73C3A6640A91EFF51EE1420F1936F57EC8A99E84EB01E9273868B9F5F8E71FBAFCF71794E053EFFFB12EFC60059800A9F6", + "validatorProof": { + "bytes": "0x020000000000000000000000840081c97256d553a8f234d469d797b9535a3b49f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b99b713642e2ee8e825fea982cdaab16ec93294083e4910fe9950ccd28f1e3035a1ccfa45efdb3f6bec7ed55a981f5d01db87351f9afec41d8a50380bc2bd67a3fbd3e8dbd983cd7fbd03bc54b3938403f6d7505dc3d259e6aa86fcf49ddf27c858b5cf6165cb97b9e3ca546da60f9d31915a8f6b6f7bed3982155d2a62867e617299d9da5daf75df6bf3082eb6e17965778ea55ef7a6838e5cfae95646d6fc9ca4985ed8c929e483cf1d61c88ef13f0061f63bfac39fd0c71cfc0022437b072c71b7a19a907cea1cdabb4a7dce017525b7e2a00bedff2f23d93aa25c9c8b4261b5b33d3e4f6f050c58241affdd5d78439ecd4ec01f46fd38e0be62da2ada91940df7b24bf95f71183980d2d89a1dde8b953ce5ee9bfa6ee50b8ad5bf865a85e2aaf34e7dbb79b27ed84cdc8be096f37eefa32090ab9241787779225fba5d76f9da7ae9627c3563d9b95fee67ab53d9aeca9f5319cb8c926a88715dadb9ae9f16a815c56655f2d9f99d738d56da22ddfa68950b27ee8be873718338bbca3bd8c7f0d12cbea1cb040ce0406511d817270b7aba0216661089c1a5aaca7a33b83a0e314721f87dc2b4c885971b285c5c6cf19fec5c7e66bd626c59d5683ac1bc6c7998534184d57236bbebe9a4d4304c14409b9e07f8dbb6b3fc0d5f273b3a3b95e1b0e70adbdd9459d65973d2128654bfb7cd83b542c2a640c1c9f869f6a902362edd80ce0126069bdcb00ffa77d5aa9891b62a10da3f6087e14c2ee5b86a8da587fda203ecd612b6bf9608835609882d9cd6fed6f67aadc2f49bf93049695bfc8e6375b66eda10af23cc8f0ab907b8d745a128dbcb92309463acd27064941cd7215e6dc933dd4409545cbb0aa6be737c72d5bc72cd185df267babec812e2eb543076b6e310326178d665aab463237600ce3fbce63ffd62f7ec03604b9938c0469122d972f9e06a7355e800e1c388d856f87e5d1d42e0519417f947c1fc9f7d06e38a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206f8dbe1e0000000000000000000000000000000000000000000000000000000000c6341f000000000000000000000000000000000000000000000000000000000077fc3aa4d23a816a7a78a3c5be68d69929e15967b8797df9a4581d983c87256538423867718f1ef1f9f1d9a0acd6f1c8a7e59ee891ab6c3f80a5455629835a2ac8218d5c77ed033e03697d44b0fd43821badc7de8d26e105e7035daf8b6488f8e6974315ed465a721ddae8054d52fd783ece4a2f355fbe5b5f6aaf2d2a9028e6eabab4bdce8e9d311f4968ed0411640fb79cd25aaa61c67e9b4092f867cd00f9b1103e525ec8dc44ba3f79ac24cf48127da695e86b82b090e85465f8091cd5a494da8fee38bd34cef2425ce91f71ff9744bbe93234cb65acc776e34973ea17765d93dd1d7088652ff8fdaf031380743881946d4f82cfd6a36114508d7d9ef933", + "root": "0xe96dedc675361e240d5101fa44c504d1b1dd209419c451b953aada3ceb16f1f2", + "nextBlockTimestamp": 1752723203, + "comment": "npx hardhat verifyValidator --index 1897126 --slot 12158264 --network mainnet --dryrun true --withdrawal 0x840081c97256d553A8F234D469D797B9535a3B49" + }, + "depositProof": { + "firstPendingDeposit": { + "slot": 17003136, + "validatorIndex": 1922497, + "pubKeyHash": "0xfcfdf0c3a5a8e0b4ce30d75d24828524f21c43673c1e30b1f53baec004a35a44", + "proof": "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b7362cf6d9539896cc1bec3c2a86ed0d84026bdc5c0c9a2980075c345d3bb97c114d1a534ace181b150fc76e32be0c6d6c486d95ff7a91333fdb6b1be22fb1b3cf8e1f88e396f9f2b1d4e5ee865bd2845a5ef585bed48deddd3ffd22a97e07406f458973d59cbac2341f3188481e962a42da765f218215ff886d50f9b3bf62838985a2e6612f3342b46e166d79f8ed34d7591a9ec24e048d4e62be7a4e2ded85687057d9984f568834dd54c687427c18593077a1de86d6d33bbd0172bff764882a383c64e0ceaa264a31bc8c25692c59b48d888e786a87ca0a32612e783671ac92ca9900b10225491aefd8092be9b872575019498ef16035c47e19cebccbe76189f94db7b20cb266bf43ae6796755f13eb06052285a5ed409a10af77b69fa1e221da636de540fe80556814764dd63e69136128afa5f7dcb38ecc13dce0ea9916d75689ce6153c3d71a249b19353b22b06ba7f2eeca4b76773ce0997bb9b9948af895f0dad21fd24c716434e24bb30e59352f88ae23e0dadba8ca758748605026a6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220b7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467659f070000000000000000000000000000000000000000000000000000000000000488461b8fc27a0848bb22e974e387d0b3b1a4899d23158e391b281d9e681411da3cae00cbbdfb8497c219ff3b85ed58e9a51b8983924a945b57fe46d454526c0eeec362ebee29c3f1e3fcedc0c7d7166d63e7845f881dfa1d1416348364ccf0c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c6642dc78c891aed56247b6facf5b70905dbf2af9c0c17cd591f850aca1caaf1e253dad03a218cea22d176a579050500d05372f6e786f1e3442eab5208e32b225c3d3b102004cfb46356dc905d7350acb06af0fc4c1e126a68aa06b3905c0ed895f395ceb848ddb5fc6f351d262285d9fa06e4d0d6cfbe158516574f62e3a7175" + }, + "strategyValidator": { + "withdrawableEpoch": "18446744073709551615", + "withdrawableEpochProof": "0xffffffffffffffff000000000000000000000000000000000000000000000000a9f34d15fb334e6dbb221ff58359c108a839b4558c9a03b26e5ed2c8cb78ee8f9fcb9950660543b043abdb86f8568c4169bc3b00e4c250f9f7af9d7fb04483aaa1ccfa45efdb3f6bec7ed55a981f5d01db87351f9afec41d8a50380bc2bd67a3fbd3e8dbd983cd7fbd03bc54b3938403f6d7505dc3d259e6aa86fcf49ddf27c858b5cf6165cb97b9e3ca546da60f9d31915a8f6b6f7bed3982155d2a62867e617299d9da5daf75df6bf3082eb6e17965778ea55ef7a6838e5cfae95646d6fc9ca4985ed8c929e483cf1d61c88ef13f0061f63bfac39fd0c71cfc0022437b072c71b7a19a907cea1cdabb4a7dce017525b7e2a00bedff2f23d93aa25c9c8b4261b5b33d3e4f6f050c58241affdd5d78439ecd4ec01f46fd38e0be62da2ada9194c2921118b682331eb12206c4269237a8f28c9b2370443da7f0ee7daf56733763aaf34e7dbb79b27ed84cdc8be096f37eefa32090ab9241787779225fba5d76f9ace5491b308848a89c48eb066f5e63b310aa0770b27a85a9de3cee1a02c16547c4c88ff5ba5f7d842515c598cff1f175de0372c164d0e7f7d6bf0e84bdf17139d87e2e7fe8cf396e0cfa1b59be4120e79c02115263035b28a7b1b64e00771bf7a67b26276c67f686c1fc5b0ccb8cea54f247e0fa00ace097aed8fe3063f932702dcabd29f98052ddad92f8059e8ae403c67c468e9a98ffeb6a4e478ccd8f6236717193488c2ef193371451577aef097c6bb59b4b6f78ed83f1e6513e50fb25edf5f8e025d3b617664459637d47e5c7c0e332ed3d06e05508c5b49f3253a23cbe4ccfe440808c06a524098fb67330a83b5e69ad4da7382556efc500fbfafe488a8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603abf1038da5e0d2585f27fa39b40e8aa84e4f3dd2ad37503cba1a53a63adf31718dae147326f7d6abb50aea295eedc6f0340e8a050f89ba63dd1cc3d507b8f59241e1c86f42556572c589849723a136b014c8effb95b49a17d37c054d61e5c6a18b8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fc1551d0000000000000000000000000000000000000000000000000000000000c6341f00000000000000000000000000000000000000000000000000000000005b20858a4ee50df104f9ab97a071c4391ec504f8b3c39e7bd574f4c463b243e6d04af7ea7a3cc78a6533ef23ad1fbcbe044b24816dd74a9ef852e6930774ea291004983f4b1c621f65ab4bf74dcbc0c7b053e955ea43bf34ab309b0a0e0b4bc3950292f38ed2e97ac7e49e660bcaad599eceb804cad8b15561eeb34257e006afb2fd46d8d5a3b21f4da401e9f89c7aa6aa81f6cd616a9c85f544137cbeebcfa4253dad03a218cea22d176a579050500d05372f6e786f1e3442eab5208e32b225c3d3b102004cfb46356dc905d7350acb06af0fc4c1e126a68aa06b3905c0ed895f395ceb848ddb5fc6f351d262285d9fa06e4d0d6cfbe158516574f62e3a7175" + }, + "processedBeaconBlockRoot": "0xc2aa8399f2f7f94c6ea529d84654128b1833d80bf467c89d3eefaeffd178ad66", + "validatorBeaconBlockRoot": "0x28bd1f0e5857cdcf2dad530fca00029b7682e7b8eb5e1ffd4a2784a3506aa569", + "depositAmount": 51.497526, + "depositDataRoot": "0x9824beaea8d9e59977662f6f018d1f48c81f194b0c0c361622f3aaa6bc7f6d3c", + "depositBlockNumber": 22886969, + "depositSlot": 12108720, + "depositProcessedSlot": 11747136, + "command": "npx hardhat verifyDeposit --slot 11747136 --index 1897126 --id 1 --test true --network mainnet" + }, + "comments": { + "deposit tx 1": "0xf766074547a732de8517f6a7d531db6183ced5b1d0893163fe50f0a6910066ae", + "deposit tx 2": "0xdc0a7185a57166308f2f135fe99c3c31e1fb67e070391d9b8fc4f992d001a479", + "640 was requested to be withdrawn in tx": "0xdc0a7185a57166308f2f135fe99c3c31e1fb67e070391d9b8fc4f992d001a479", + "Withdrawals": [ + { + "slot": 11862438, + "amount": 1589.373182 + }, + { + "slot": 11763009, + "amount": 640 + }, + { + "slot": 11723496, + "amount": 0.017398 + } + ] + } + }, + { + "publicKey": "0xa84bc6de4bf74b8eca89cf4b8dd5c4db1d997b161fc5da80b01a7450b74fcc3c85cfb32f3a24e52aad627d5375b04402", + "publicKeyHash": "0xb417470b54c57f70ff64339561a509b584cb030e3e6887a38931e27f06917d14", + "index": 2018225, + "exited": false, + "operatorIds": [1926, 1927, 1928, 1929], + "sharesData": "0xb103e235fff447f2df283086ec527816182e8bfaa449f307b6f4dc2459828317c1c0815a6e6fdeac7cc86459935aa4140c47e0a28be8789a6ddce53c35512d1bf5e557b10a58c67313f4dff4610414f808e0750152b3efbc5a72431f260d64f6b7c9b95630a259d169638afc3ed4a44c0038740feaf457fb2dcde0731857184c601f007f970682004cd6d2db48dd76f38ccf0e9dfb45c02ea70d196f3222726112a839c8e306d06d02ae516188670b5bb8d165bb0a222fafb5639d52e80acb1f81a0c1aa021ac1765c53dcaf2a03010cfb2918c2365d3c9b100eb3991d084c7107555739017526b0821783e646c8a728a98dbab57cd57ee21c9874f22b50d1d370497c5a41ecd05d7bf459917d7c5a6e1d236aa43fc55916662e4c87d648fafe2b372beeb1ebe2dff5a5955aaf49282d6e41a83398e9566b671aefa3edbb5503bdd22cb9c27fb87db94bf9044aa8ed02d221dcdb298757922e95efd4c892863707777c17a3b7876984034e409a991f17e10b93b8845c2dadd88c3820ebc1e298e4cac95a3fe0e11ad4384dfe646602a1476fb23431fa679b32aac3880da0c66fa8677ea51393dac3956dce091a2ac23febf511663b6044ff9e08173ae01607db4155848635b307093283fd740d9a419513be90bb4a609704bea92615da98076dd0d619f2321448b1621800a9f22ac0f1ae9e5c12a4354ae742274849ad6b3209334af340ddb3b38810c70eddb184479b6998d8cfa8339e3de838077abfed608317f8bd2115d156c7a8432aa87d0bca21be380f66ae00a064dea824ddf8b24c385be8f8d5ddceb38be38f550ec73a604a78ca9573a69abc3c5b36300ab2eb07820ef771699f7b0dab25a8052c345e85972a799b78dd75917406bf0ef9c1a6e6ed96bb8602c8db9da331e5ee5cbbf11437664f40541ee1323d1c4a3c6b86e58e5ae50985851964db0313056c4a3fc9daf422f1b506de45202d7b790cf34821e7b347c5ffacbe970c27f90cf2d95e51a22ddad711372b7381478d9ac232bc32709680cef75867752690e366c6aad04de8a23f646e46a3efe4c67a5fa445ce0b686b5a1adfcdaabf08823bf7dffeadc2c3c7be6ad8ff7bc7dc8caa172c10785424ed3d4e7bc4eb33658739be39bdd069113aabf9201008343f381e47f8e51d064799dfa58d72f282594fca93b666ea53decefc91ba059f27ed0a72c1d583cfcfdf17fccefcffd9e0f63422e9c6eba7a093ffb1db526558ae5a8e95eda740585acf94ad44415c300f8fe03580bc8c6c29848963af58ae1041987a57f4af1771b33863a34db145828c6f8eaeae4ae19ae92186677422eb61ea0c2da9c281f217d94c5a6af024ac95ef8eb6d170037ae6cfab3990d1293750c1a2e8b4dfe4152f0a13d1b32774cba67f225d95f4a0e5da5a5916a902e1ed68dd1b1c9344b1a1be8fd5020326f76592c9b0927a46d820c0eb10e3fda5e761d66096b0f2644339773610c421699e6173f2e8f9c00e15b3f2331ef7c98a6472cc21e506697c4f162be07fa699f3552e91b4533642f96b267a2741e0993cacec01fc35165999250e8d45e2ea2a37346d6c3c7f8d2b20d8fb0ddb74e51a0e01a660e6bc1b40c07f118a95cb1ca3d1581e5c138d8740e13e43ce1befdb9b02eb80ec5097f9e7f68f389610ef2e69759e7ad77adf74b1156f498b180ae2ddd54855717b73f220a51609df11a992f409a02dc6e70dac9e7bbe9c5979e0445626cc1ca80a0bdb6eca1c552e7e8ded0f8f54e02ac77ae8a4fe47387bf835e5dc39de337b41da08544295d318e412f6f44985aba5cabd80e349e53311e31360b278b600fdeea0d84ed10e1830bf2e38", + "signature": "0x9658f1b39befb39081e418b8d97c68e27e71a3a28925447c233edae5da56c52e0feed7c887717c32bcdcdba1124a0c671740dabb802c207ab36256a4438e0ba1fd23b505b490d235f84db8503b290c1bf32a4fc8ead1875e907706c2f259a10e", + "validatorProof": { + "bytes": "0x020000000000000000000000840081c97256d553a8f234d469d797b9535a3b4919327cb9763c96e00332bde93bdbb1032c4b796dda73e515c8c5f7ede9a419be2c84ba62dc4e7011c24fb0878e3ef2245a9e2cf2cacbbaf2978a4efa47037283f3aff16e71474ca97d093ed75697c0264a121432a577184349ee9cb8aed99dfc47b4e6bcc69e8841161e1573e2bfce3551ccab84f1409e0f4fa51f76e3a7e425db56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c713347a8293a65389b6f46fd5c95cb7ebcce51ceb379c7e98f044a683abe5ff749ca5c76a247f8c5e7b7fd9798631dc28473eaa8e3bdc303c910904e62acb22fd88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa145896cd7347c98f17ce04b94d4534012c0eb373f5a2dd48023169f4b999b1531995db08ccde5f2003933ed2e65da8bb58e3dafbf34b3c049b50e1f09b13ecc1ba7b2a584ae7093a77b0de259f45d72d66a8e3bc62fcbfb5f28f089a386b604f5ffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b636b0109273a09276a67ab791f9d97da76ae62ba81fa4ba3c7c3d9bd3ef0aedbb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e41e7db8c1b2a606b558ad3e40c91ba8dc27e5b7e9dd9d94df8eca0c7f8f8f6bd4018d6e5d00e80b73872cd2c9f22776eec10633cdcbc720ea3e127837352c59b8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb6c868e4ca80f3f2dd7d20275d516462af2e6e87a8d28255204ea87fd8e0d23da11178598dca714a1977a52b64b63ff5dcf0897bd022b90e9e4b69cc8d7f3a138c682f09f5feb8a0e1616930f84f585de70e1f26cb8c9507dba860f3f4e3634f34b83484079e2d6cd5b3c666eb55b6f766df5a3441ead5c55c1304fd35d70e3038a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fb3cb1e0000000000000000000000000000000000000000000000000000000000c6341f0000000000000000000000000000000000000000000000000000000000c68a978f512d2a971f7e0fb35837c1a2a5b09b9a56f928f876a68778b453c6065e46200be51e38de9e3b9c85b2bba18f32810f5013635a67af5d3aa3741a087f8b7fc3a69740368d553bb15aa9b83aecd612ecad143245e09726d3b0bf4deebaefa9a3414ab0b3bf0645191a81700ef5199a9c0f17594b6dac9405cb2864f79ec3530caffa8048d74791a6485e3fbbd3c98102501c98ad8f6b5dd0a7eae54bcd2e63368ffc1a60aa47703836a25db76af635ced65b1b02c35c2e25f7135b9aa60d4b3f83d496489dcf8ef01aaa5a0b93db4b94d81277e60663c930d40b1939f7f83240858b18b32d47a4718a7cecd28756f7829257a38dd75ad76a7224f47a4a", + "root": "0xc75c99e28e4c0216ee418068024acfb759640f5c1a67ed1d7e4d8252b6924550", + "nextBlockTimestamp": "1752888803" + }, + "depositProof": { + "firstPendingDeposit": { + "slot": 17428064, + "validatorIndex": 1938267, + "pubKeyHash": "0x3be0d1f59524e5d546eaa75bf7e1ee46479cac0803a2bd842929909c09471c7e", + "proof": "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b7f568ddc0685a39bec61266eea23d7c8c6419966abd1205af134fd5f5e682c65ea47cda1ccc3fb2a8087c182d515ed20a45cad3ec4b0370147fc9587a59112ebe24c735c5674aa9134c3e3b650a5ab1e287d38b9f661148bd348189bdf5df816f4b4161c928eecb299b8d2322e18260f146f26acc80ec4e3776a34719b0946613d3e6eb1b3883013442a1b46d53aee7cad744ef866060de696785ad5a5ccd92a8344c2a28f9d72cc739c5cfcbe339342fda250800c00e880283c9c12d210aff8f2722721f1557d1c1f3410ede3402ce21d4ad8aca7119ec76242c3e923f8e020909522a1298230bc45dd2654d786534758cc29404400674272907bd9c526c19008c41e6688c0629a9785373766386bb1acf495c9a985052b7cfcdd8230c9a26e1b00e38ef4a1cd128181497db4fd7bf8a1bc871b9f57d139e5b842142436c5c44e5467bbc7ef600ec48effc00a38619c6148e052677ae19c04d378b85b8b37c5c37f631dd268afba314579a9e04dd24d41d8fa343448d74e561c301c3826e98364de06568bb626791266aa4b52238b14f7c16626fb8c85d54fbbde0beae4af273000c43e0ec1a14fdc6ef9151f6b78fad886d2792bef07ba2ef8c0d8984cb7234b00ef4f2f09617d9ac4b2427cdf37c5ee1230923b66774276e33f4778ebe9fbb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a4676556300000000000000000000000000000000000000000000000000000000000008aba0199ca5db0d6bc6b0ba2cb3a2baa7eea145e72c21d49e7f2215780f9d415316fbdfeb1dca35988689bccd2e98fab375075446c3132a8ab17c9d5a7c72b6b04d2ebd6334d4927ea0cb664d5e29fc3d202a59f69d3fd95c75ed7d1f068f0e7c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c0a8c42dced85f8d6dad730f2cc40645b9fb9273a5edc142e0cc5763b1245597b2e63368ffc1a60aa47703836a25db76af635ced65b1b02c35c2e25f7135b9aa60d4b3f83d496489dcf8ef01aaa5a0b93db4b94d81277e60663c930d40b1939f7f83240858b18b32d47a4718a7cecd28756f7829257a38dd75ad76a7224f47a4a" + }, + "strategyValidator": { + "withdrawableEpoch": "18446744073709551615", + "withdrawableEpochProof": "0xffffffffffffffff000000000000000000000000000000000000000000000000520cc47c38af7c1a550b6f04a1de72582ceebd40bb079dfb08d97d3849da57de40da6900a1ca90874d68dbb841bb28c5756b35001263c148bd64ca036c2e26b1f3aff16e71474ca97d093ed75697c0264a121432a577184349ee9cb8aed99dfc47b4e6bcc69e8841161e1573e2bfce3551ccab84f1409e0f4fa51f76e3a7e425db56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c713347a8293a65389b6f46fd5c95cb7ebcce51ceb379c7e98f044a683abe5ff749ca5c76a247f8c5e7b7fd9798631dc28473eaa8e3bdc303c910904e62acb22fd88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa145896cd7347c98f17ce04b94d4534012c0eb373f5a2dd48023169f4b999b1531995db08ccde5f2003933ed2e65da8bb58e3dafbf34b3c049b50e1f09b13ecc1ba7b2a584ae7093a77b0de259f45d72d66a8e3bc62fcbfb5f28f089a386b604f5ffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b636b0109273a09276a67ab791f9d97da76ae62ba81fa4ba3c7c3d9bd3ef0aedbb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e41e7db8c1b2a606b558ad3e40c91ba8dc27e5b7e9dd9d94df8eca0c7f8f8f6bd4018d6e5d00e80b73872cd2c9f22776eec10633cdcbc720ea3e127837352c59b8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb6c868e4ca80f3f2dd7d20275d516462af2e6e87a8d28255204ea87fd8e0d23da11178598dca714a1977a52b64b63ff5dcf0897bd022b90e9e4b69cc8d7f3a138c682f09f5feb8a0e1616930f84f585de70e1f26cb8c9507dba860f3f4e3634f34b83484079e2d6cd5b3c666eb55b6f766df5a3441ead5c55c1304fd35d70e3038a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fb3cb1e0000000000000000000000000000000000000000000000000000000000c6341f0000000000000000000000000000000000000000000000000000000000c68a978f512d2a971f7e0fb35837c1a2a5b09b9a56f928f876a68778b453c6065e46200be51e38de9e3b9c85b2bba18f32810f5013635a67af5d3aa3741a087f8b7fc3a69740368d553bb15aa9b83aecd612ecad143245e09726d3b0bf4deebaefa9a3414ab0b3bf0645191a81700ef5199a9c0f17594b6dac9405cb2864f79e794c2f402b398e291fcd29d2e790fa37eaf48b0996c421aa03106205c69a46162e63368ffc1a60aa47703836a25db76af635ced65b1b02c35c2e25f7135b9aa60d4b3f83d496489dcf8ef01aaa5a0b93db4b94d81277e60663c930d40b1939f7f83240858b18b32d47a4718a7cecd28756f7829257a38dd75ad76a7224f47a4a" + }, + "processedBeaconBlockRoot": "0x369552691e85c62d035b5fbba17506af1a5322e641c034ae093ea00f0e5d1497", + "validatorBeaconBlockRoot": "0x369552691e85c62d035b5fbba17506af1a5322e641c034ae093ea00f0e5d1497", + "depositAmount": 32, + "depositDataRoot": "0x0cb582bdb9325162b127d59baaa99b6185d910ef84749f34d1f31b1d63377dda", + "depositBlockNumber": "22898324", + "depositSlot": "12120147", + "depositProcessedSlot": "12172064", + "processedBlockNumber": "22949998", + "command": "npx hardhat verifyDeposit --slot 12172064 --index 2018225 --id 1 --test true --network mainnet" + }, + "comments": { + "register tx": "0x196014cc5d151529c2c7b73f97d72cec0f7957b8d4b15e758ec6b64066ff87a0", + "deposit tx": "0x33ba03d25965847eb1a6f383b8c5a7cc23f100b06c6cc71f705a876f0a5a6732" + } + }, + { + "publicKey": "0xa879b062ecabc05142a92d0f6b3d80176bcf5840175128df71315699d9510faf25b21f6a9dfd7f09f42929a4f88cf3bc", + "publicKeyHash": "0x3e16d57ef34dbc39b976b7e05d299a24fd71719594407c78aef7c91358cd6b26", + "index": 2018224, + "exited": false, + "operatorIds": [1926, 1927, 1928, 1929], + "sharesData": "0xa11f81a854b0ba74c5ea280646108d2b4f197645159b14319f37bb2ef614d8492ed9a4d346a0352fd44e23d8ecf3dc150ac129a449ab2fef1f9700b441aa98202f0b56b804d68089e1264d2ec38a39b547ba6961870123a4046ec795e722c9c8ae08d5c9b696488ed82bf2e4daefff783adca4acfdad555e0c51761107af195ed48b83734a99299f7f72845b9319cacc8e1305768ef54cdf26a30f6fb1e550b8d4c66cc9b865193e055edd27aa5c30e42d886094877a1c29b70f7168976e1387a4018f6404a7ef44211b91bb2d8f802bd9f89e4d204f0148ba763a76b1527459ad90d0d0f4e0e7b0f1386dcaefd762f88323ab13046a5ce68a5d5667dc003c2dfd63c171a79e8e289737ef9a1cef19368e50a55d0a41b09c70ed479e059a05f31c93a5493a2a741c87b7795d5c79cf1c0cfa81fb5fbaf60b321995a730cff7a632fbd53cf5b0e684a37ead3ae2d28768fedfb58cff63f0e4747d0659babb24426ba515e2d1250ad611d80c8f54135ad690e5fc875efe1e69af8a39317b6a5a40c66025221a9ee9ffb252dc47090a44558bd2b4ac9a51ab5999de3f8ec869ee7a4bcfc20f6bf769b2e7fe590cd45a3107bf51c74093a4ab19c0d44f213a3e7780edc4099b385bbe3d8397145f656663f09616213911d5ba38d378966ced1bb45c5ab7428ada3f7cfbeee3949727aeb7a3c45ce56597465d117e82ad8e2acdb55014c8addfcffcc5a3eda60dba5c4220456713e5847e026c9428199bcfd6e80c0214170762729bdd2df2bdc770fae88822977c6bb4527052126bb8642910adaf2d85eb544bc26348cf28f4a59d269cb7e18ad7ebec01f1f8beb741c804f918057b347945b12e4106067ddc770611ec8503aef3b2878b5c056d40e3de1016fae6ccd83c0a17639057f20d5a444c987a1860daab60ee5dd255b34ab187e19dca5f896ce2f458f06b9f3597a4043ece05bdd4c596600d577ea99f4bd95fac69219e0ed97a1242ad84dbfe8dddb2a39abe6ac3ce22f8918a6dbbb14d4762a7be7c5e6c39127acb5fe44a2621f1c9544197f631a90b75f299b6c2e746ea1c63d064ced44cbdabff9ffa8a5179f5cdf13e7383d658ffe32e2c0255227ce5c4416659b4281ee5e0b835518ceda3af34cbc03e75eaffe8d025ce771dd0d8807003729fbc3c54247076d0172e605690a1e060a4124dda65a2ccadb79356b3e7bc4467e586bd995810c9132c7aed0e89b5a6ac260d0e38691fb1c97340e0ee4b7deced9a685fc882a734a08b66c8fff593759ce8061abde1503b31f4b4610ce63fdb46773a2e6f8e8bb0f1d4c0b4eebbcf182535326a61282cd0250e74af99337fd06bb343643252da1c34d50847d564be6816b1a1a3f97d0d3bfc30b44b240d6052e570ebe9cc8c746b4bd7cb2d26466780f051b9bec11b84ed7988a10a6569a25a30d8a117fe8019efd820187467d7681af1b74ba2dbad4ff92b02560ec2821da83684db314c0a039faa2d0c08f44341c5c39a5fa7daaeedba930455b247964606b9947888bd2f93abccbd7ece8319c06c6b6d7a47873affba4ffe653eb9a8c59c26a5e4da54cac426c6c050397082cfe33ad4d08d319dda6576e492aa93ea75cbff74a8f01434119da84a61828f60d0d864d20d66815c95672d8c237a7141eaee1e70bd479f1d7ddbd6ef18ebdd3abcb05aa8992e867c34ef1c218ed963e6e765384915bfbe0c184a6969e02ed60b459e40e2dd02e40ad50ceff307ffc895c3fdb398787589b0b3742aa52d46c9ccc9e9182e42096acb6b2fc38eaa3a971f691382640731a6c38b2d4fcc7be97d915fc16ed3ca1340c78124124291a2eaa5c65f850c41be", + "signature": "0xa4e11f068d630fd773da8372807a017a96f75c7bd91fd8f96005188c50d96ed5a456bdb0b0cf7a014a4856a452f1ba50023d0b41f4bace8cc562194fb972c36acfeaf09e29b1ed254f9bd66752e8a14948ae2a972c443021b72c2f9b7fedd95b", + "validatorProof": { + "bytes": "0x020000000000000000000000840081c97256d553a8f234d469d797b9535a3b491b1805668954466fb0147aeac9632ad54b018f53ef52517c2bad115ff7f7f54b2c84ba62dc4e7011c24fb0878e3ef2245a9e2cf2cacbbaf2978a4efa47037283604bbdaef75569129836373c0d26a3c6d8858c597173063260da57517367421547b4e6bcc69e8841161e1573e2bfce3551ccab84f1409e0f4fa51f76e3a7e425db56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c713347a8293a65389b6f46fd5c95cb7ebcce51ceb379c7e98f044a683abe5ff749ca5c76a247f8c5e7b7fd9798631dc28473eaa8e3bdc303c910904e62acb22fd88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa145896cd7347c98f17ce04b94d4534012c0eb373f5a2dd48023169f4b999b1531995db08ccde5f2003933ed2e65da8bb58e3dafbf34b3c049b50e1f09b13ecc1ba7b2a584ae7093a77b0de259f45d72d66a8e3bc62fcbfb5f28f089a386b604f5ffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b636b0109273a09276a67ab791f9d97da76ae62ba81fa4ba3c7c3d9bd3ef0aedbb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e41e7db8c1b2a606b558ad3e40c91ba8dc27e5b7e9dd9d94df8eca0c7f8f8f6bd4018d6e5d00e80b73872cd2c9f22776eec10633cdcbc720ea3e127837352c59b8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb6c868e4ca80f3f2dd7d20275d516462af2e6e87a8d28255204ea87fd8e0d23da11178598dca714a1977a52b64b63ff5dcf0897bd022b90e9e4b69cc8d7f3a138c682f09f5feb8a0e1616930f84f585de70e1f26cb8c9507dba860f3f4e3634f34b83484079e2d6cd5b3c666eb55b6f766df5a3441ead5c55c1304fd35d70e3038a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fb3cb1e0000000000000000000000000000000000000000000000000000000000c6341f0000000000000000000000000000000000000000000000000000000000c68a978f512d2a971f7e0fb35837c1a2a5b09b9a56f928f876a68778b453c6065e46200be51e38de9e3b9c85b2bba18f32810f5013635a67af5d3aa3741a087f8b7fc3a69740368d553bb15aa9b83aecd612ecad143245e09726d3b0bf4deebaefa9a3414ab0b3bf0645191a81700ef5199a9c0f17594b6dac9405cb2864f79ec3530caffa8048d74791a6485e3fbbd3c98102501c98ad8f6b5dd0a7eae54bcd2e63368ffc1a60aa47703836a25db76af635ced65b1b02c35c2e25f7135b9aa60d4b3f83d496489dcf8ef01aaa5a0b93db4b94d81277e60663c930d40b1939f7f83240858b18b32d47a4718a7cecd28756f7829257a38dd75ad76a7224f47a4a", + "root": "0x020c0506cbbef1e734685281f440d3dc8e0cc976d696e1f4073067e25f628961", + "nextBlockTimestamp": "1752888803" + }, + "depositProof": { + "firstPendingDeposit": { + "slot": 17428064, + "validatorIndex": 1938267, + "pubKeyHash": "0x3be0d1f59524e5d546eaa75bf7e1ee46479cac0803a2bd842929909c09471c7e", + "proof": "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b7f568ddc0685a39bec61266eea23d7c8c6419966abd1205af134fd5f5e682c65ea47cda1ccc3fb2a8087c182d515ed20a45cad3ec4b0370147fc9587a59112ebe24c735c5674aa9134c3e3b650a5ab1e287d38b9f661148bd348189bdf5df816f4b4161c928eecb299b8d2322e18260f146f26acc80ec4e3776a34719b0946613d3e6eb1b3883013442a1b46d53aee7cad744ef866060de696785ad5a5ccd92a8344c2a28f9d72cc739c5cfcbe339342fda250800c00e880283c9c12d210aff8f2722721f1557d1c1f3410ede3402ce21d4ad8aca7119ec76242c3e923f8e020909522a1298230bc45dd2654d786534758cc29404400674272907bd9c526c19008c41e6688c0629a9785373766386bb1acf495c9a985052b7cfcdd8230c9a26e1b00e38ef4a1cd128181497db4fd7bf8a1bc871b9f57d139e5b842142436c5c44e5467bbc7ef600ec48effc00a38619c6148e052677ae19c04d378b85b8b37c5c37f631dd268afba314579a9e04dd24d41d8fa343448d74e561c301c3826e98364de06568bb626791266aa4b52238b14f7c16626fb8c85d54fbbde0beae4af273000c43e0ec1a14fdc6ef9151f6b78fad886d2792bef07ba2ef8c0d8984cb7234b00ef4f2f09617d9ac4b2427cdf37c5ee1230923b66774276e33f4778ebe9fbb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a4676556300000000000000000000000000000000000000000000000000000000000008aba0199ca5db0d6bc6b0ba2cb3a2baa7eea145e72c21d49e7f2215780f9d415316fbdfeb1dca35988689bccd2e98fab375075446c3132a8ab17c9d5a7c72b6b04d2ebd6334d4927ea0cb664d5e29fc3d202a59f69d3fd95c75ed7d1f068f0e7c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c0a8c42dced85f8d6dad730f2cc40645b9fb9273a5edc142e0cc5763b1245597b2e63368ffc1a60aa47703836a25db76af635ced65b1b02c35c2e25f7135b9aa60d4b3f83d496489dcf8ef01aaa5a0b93db4b94d81277e60663c930d40b1939f7f83240858b18b32d47a4718a7cecd28756f7829257a38dd75ad76a7224f47a4a" + }, + "strategyValidator": { + "withdrawableEpoch": "18446744073709551615", + "withdrawableEpochProof": "0xffffffffffffffff000000000000000000000000000000000000000000000000520cc47c38af7c1a550b6f04a1de72582ceebd40bb079dfb08d97d3849da57def8a8dba704964fea5d42b6bd9e3b81505b79630d08454d21f30f106e4ccf014a604bbdaef75569129836373c0d26a3c6d8858c597173063260da57517367421547b4e6bcc69e8841161e1573e2bfce3551ccab84f1409e0f4fa51f76e3a7e425db56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c713347a8293a65389b6f46fd5c95cb7ebcce51ceb379c7e98f044a683abe5ff749ca5c76a247f8c5e7b7fd9798631dc28473eaa8e3bdc303c910904e62acb22fd88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa145896cd7347c98f17ce04b94d4534012c0eb373f5a2dd48023169f4b999b1531995db08ccde5f2003933ed2e65da8bb58e3dafbf34b3c049b50e1f09b13ecc1ba7b2a584ae7093a77b0de259f45d72d66a8e3bc62fcbfb5f28f089a386b604f5ffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b636b0109273a09276a67ab791f9d97da76ae62ba81fa4ba3c7c3d9bd3ef0aedbb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e41e7db8c1b2a606b558ad3e40c91ba8dc27e5b7e9dd9d94df8eca0c7f8f8f6bd4018d6e5d00e80b73872cd2c9f22776eec10633cdcbc720ea3e127837352c59b8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb6c868e4ca80f3f2dd7d20275d516462af2e6e87a8d28255204ea87fd8e0d23da11178598dca714a1977a52b64b63ff5dcf0897bd022b90e9e4b69cc8d7f3a138c682f09f5feb8a0e1616930f84f585de70e1f26cb8c9507dba860f3f4e3634f34b83484079e2d6cd5b3c666eb55b6f766df5a3441ead5c55c1304fd35d70e3038a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fb3cb1e0000000000000000000000000000000000000000000000000000000000c6341f0000000000000000000000000000000000000000000000000000000000c68a978f512d2a971f7e0fb35837c1a2a5b09b9a56f928f876a68778b453c6065e46200be51e38de9e3b9c85b2bba18f32810f5013635a67af5d3aa3741a087f8b7fc3a69740368d553bb15aa9b83aecd612ecad143245e09726d3b0bf4deebaefa9a3414ab0b3bf0645191a81700ef5199a9c0f17594b6dac9405cb2864f79e794c2f402b398e291fcd29d2e790fa37eaf48b0996c421aa03106205c69a46162e63368ffc1a60aa47703836a25db76af635ced65b1b02c35c2e25f7135b9aa60d4b3f83d496489dcf8ef01aaa5a0b93db4b94d81277e60663c930d40b1939f7f83240858b18b32d47a4718a7cecd28756f7829257a38dd75ad76a7224f47a4a" + }, + "processedBeaconBlockRoot": "0x369552691e85c62d035b5fbba17506af1a5322e641c034ae093ea00f0e5d1497", + "validatorBeaconBlockRoot": "0x369552691e85c62d035b5fbba17506af1a5322e641c034ae093ea00f0e5d1497", + "depositAmount": 64, + "depositDataRoot": "0xbdaccbbd358d87c2b8eefb6c35ffb0cbc115f5a3535ec71b7041b2ea9e4edbf3", + "depositBlockNumber": "22898324", + "depositSlot": "12120147", + "depositProcessedSlot": "12172064", + "processedBlockNumber": "22949998", + "command": "npx hardhat verifyDeposit --slot 12172064 --index 2018224 --id 1 --test true --network mainnet" + }, + "comments": { + "register tx": "0x196014cc5d151529c2c7b73f97d72cec0f7957b8d4b15e758ec6b64066ff87a0", + "deposit tx": "0x33ba03d25965847eb1a6f383b8c5a7cc23f100b06c6cc71f705a876f0a5a6732" + } + }, + { + "publicKey": "0x866e697fefc93b86c6d82366ebe5dca6b49fd17326f32c7455083b2782b74d2889a76d0cce2d0dbb5af9179af9ea9c39", + "publicKeyHash": "0xb8af6977c45292efcd223118371af89f479beb269af1093cf9ca48aa7d29b879", + "index": 1998612, + "exited": false, + "operatorIds": [1926, 1927, 1928, 1929], + "sharesData": "0x95be8f3e97880dd4b1506936c76df220d2cde4c5cf1c0a1d2f60dd4a5c18b334349bf12ba09cb8d177e444ba87ca805912c6a1297008f272f504906b41ddf86f44273c4bf5ba945402eb30bd75ad6d50495c36d10db2e3838a0672d4331dea1a994ae379db1af2573aac4a81d86a781dcc3ce15c8f2ea58cadba08d65305da5882257e2eee2544d84f03b22fc8ab81648a12e1fd9e8959e1a08ee73321189754670b1e8290126fbf261d2f1e7cc240ed4ab15816a45c89a8c91653a829290676b08d39a47510c240c7c64368808212c58e73756589f3e807a4d6111a2d22ace218cb25be10b3d772d5b17196fc02e16f958a493d87268c14c939da1412f935c868ef12fdd7fe407154894989b47c73339538548de6f67d5bb87ac05d7f4ffd314de452cc01a8aa07f98599fee06e276335b5c3e9b5c877ed5d58b432536aaa8817301bc045c7f0c6c6b8119a60265cebfbf4b61af8592a994214d3717ae7559e56eea0638960c912cdd13202481a893f67053cdd70a5b6d5d5e425fba13999f51dc0cdde0597cbfdebd1253d10cc725bfbab95e2a9a4b5ce1c9a040de35e1d035f826b49741d48d97a4433ec41879d2b14d518bd90820deffc600ce772df36455d7930cb0630a3c75f456a9754e841acab341623b90b084ed7689cbe8e4f958e3b400b9c0d3aeb33becf4a1bb7a3562e4300e42eb6f8c898c9248e9d185241a07e28665477187a75ab38ac48fcc865bfdfa25afdb8ab5079d7b0dc1fb9f7c82c1502620ae0d6920b713802c776d124a481fff4841f66fc0c6e3263ab75e2866c507ee018a0bd6836d2f389cb4367f16688f9d58423e3b14d60dbad2e31c13c70b48b8444959942c659e7a0cf9cfcd6e792ebcf8d66cd9b27a0b25bb0bffa1cf9c7858169d34b1cd2c3168ce20216aed7011bf81ef772ddfb83a70eeba448bfe395650c579cc63dc037a0c31c4bd2d0fa37844ce2b4a38e7582bbe6efe3d48c1778d6b6dc45fa94f4fd809a0d9c9bcffe159a0e2cb22fba886d2636829e16b204b46ca1f7c9e93e805b6b46b91dc32e21bfd26099f7871d7397899e9334b249277448d661de21af3ee604234da8579a7d0a35b5b0dfeb05dfbceffca8bb4639077e4006a5802a4e4047648098cfd45b8b3e02586b92e58a9405bb6d03873590505ef63b90455b22852705287a1c43578dcbea4bf6d72027d22dd395f961747975c8b637c68205f29114387666598d1f1c976ffaa3fe983c75671729de538b0942f057597ea98e1daf15a8bdabcffe97467fff959ce1c28f9f4e76b68e13fcbb4ebb4cfc082f3612e651475a414490c285a2b8c8f6fc215a4a0ccf68b2da0c38a8199f98126f7bb5b813ce565ce1016985888c3e8e8e8f8106459b5b2e5cee6e6ddca7ba2045967b048c8495479e08d60d320ea2a5b60235004c31ff5fe98c6a6f9d0890221a25402e0182db297457ec98289db1541a597b8f15d7036b0a7713008805fc509bea460a6f5295ff961f9ee38b6ca5ff9b8caef2ca11545a9287f09751ae75655e98e0118cac85122b14bc9021399d1d8ce326fc9b56ef2ce462d53df50ed660fd4ea21277dc98dd390d086e0199605d33291ca02bf7e499468e9601eb06ec348a915c680818beaf323f3d6d9960be7b1c4d2b306826ddefc8f8e3bf9a4931f2f97aa9690e08c21cff652e93d6648b562afbc87060f4f68c948b0db316eee932e75282680fe9e90527ec3832483f8147f7ee4809b63de332f40f3dee3b55287cf23e6e310a757478af712137c29e95f24e50fbc029a24025db1075753308073eb19135928f16cba00fcc5a36aad29e0a1eeab906024c8ccbfe4a761d", + "signature": "0xa766ef537eb99f9f5b9393acb2ad079b75e9d9db6430dfd682ac0b14865252d7d369b589a595e272ecea1d840878e8410dbfe8fe49f9d42e188fa066cee58bb2c9e41008b1a22edc01fa4f0a34512c6dd804769e9b0394f426491ece7c3ba3b0", + "validatorProof": { + "bytes": "0x020000000000000000000000840081c97256d553a8f234d469d797b9535a3b4919327cb9763c96e00332bde93bdbb1032c4b796dda73e515c8c5f7ede9a419be2c84ba62dc4e7011c24fb0878e3ef2245a9e2cf2cacbbaf2978a4efa4703728394ba008224247a366d5e275e1fa0b857f17d93a08ada63bf50c1333fb718802fb1f8b3c01fd0e1c02b6dcb40b88efc7a98a81737cd6190a86e6111377c44cda640c46dae7aa2bcd9e0025f440d22247990d4bccc9ff418e20bbec25ecd603c16a3f843040886ece65232af3c967d92047e6bffc087d0b6b6d8d85b33a3ecbfab45424ee0d560d6963aa0344fe55795806c475e9625f9a8060ef44075155d18d49efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30d88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa187eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c57ffff855e60ca76fbb0c9121d03cca892fa0e2b3f942c0ecc993e073ecb7051fcf7ef3846b857c4105df9b918d5bcdb2765f42fdfb602bf844d7c4044da17ba10608f38f8cb6e697fa28c3bcbd3a636a941561bdb912bf79438a4d1bcfc3c2f6f9717ccec81d352e5ac9fb7820081f934f972f606361d12e47e708aab340c5ecba91781d7a9a1477e622a52a6d970d071feac0b0740ed1404392aa9fe5e95456d5e184f0e63555a33e9a53a31b52c440c554ba0acd1bce1b5198a1e497d21fde7d01f112f47336622d34369f92ed5b1e10ed9a862f3ca9953125283f29f5b3bd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb86f3ed221fea9c64a8a7f680f130e09222ea6dd192cea5597acbd3787dda4c55b6f2f7292b05c1dae94381ca8144bbcb9d62e66a17d7c2da0908b8be7fe61479b6ad5eef1e8fce0593dcc16f0bc6a1cf7dcb200faff52afae0936e3882284ef88f2a9ad59109fe9348475a34b33d0e8bd6f76b7d0c614cb1d23272f018b3f8ac8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206f1c7f1e0000000000000000000000000000000000000000000000000000000000c6341f00000000000000000000000000000000000000000000000000000000009209427d30b39a38564a9850d807d9d4b3cd8d741f2b11d956b0be59a3ffe396fa6084de5617aed0b08c666660be00ab672fd146fcd37c24c539ef7cb5d4b84ce048b9d52a0c9e32d61a69949e93ee725811957aa6c6766fb25913da05af2ab29a9aac754fc773260d6cad4980237b5d94001d569b94d5e3ee216ffbccf8291b157aea43212e97f4790b97727dc79109f2ff1062bd9cc2b50b61f862689be6f7d489eaaad2cb96d5d9b274cb8d5316e8d921bd9f77cfa9b59b752863b80360a3cf6d4b471627d370b36870a58a1fac397f49aa7b37730b29bf409f301955f30ef26a6ee85e12105902366062d0a523a22e87f3aa312880148e5305b1f00ac117", + "root": "0xe5cbd1873dcad7626207556eb270fda1c0ff6ac3e9af122e385334e8455bbd4f", + "nextBlockTimestamp": "1751835107" + }, + "depositProof": { + "firstPendingDeposit": { + "slot": 17340256, + "validatorIndex": 1998620, + "pubKeyHash": "0x568e44ddbed4d03b67b14e9b7d88bf5a637784514e66e19d01b781d5e6751530", + "proof": "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b24de99dd3221925933b1f30f45bc29a4bffa14088a9e1340475642ae78a0447154287a1c2fea708484ebe7ddfb6adfa736544bd812751844cbc353cf85fa81c00a8980212f0b8554a03a2025fd3796301728c0f27dcee28fdb345308ea17ad4875902d4c854e6af0d7fed5bd3753a93e55f06b5ede4558ac8f96d27e782a935f4e3c5021933b6f56aa3f05fc841e056a4c57065193d66133dc52c434d01fb7215fefa073e9a65c58bae515ec5ec8b02d874c18ad6220d103bcae8ce5ce42ac27ebb022ad981d88130ed26ee2c014315407b97eabae5dbf38727817f1bdce48c7851d74ed17ce5cac3ff542939b2eea9bf0e445df363fe52dc78dd10d60a00bd0540b7024711b9611b0afc3b8ece87c75cc7f59b9cb9cd72252248cb820e95c1b84b7bfd75e09cc21af6139fb003b781639efe87b72e1902e45b9d4fac073bdae9b79e8097e3685f3f2a0d149969c6a8bf9e36c22c6d4cc5b1111080dd81736f61159b20e5787fb07871b3d1780a351a24a1fbb9122e8456b9f7828f60463517f8825b08828ee7645524fe1fc5ea71cbb698bffdf4d5faa6354018a6640f7c41ac9758cbaf7c63d01eb9216b0c6fe1e37e65d2d80d99ba09e5179db6c7d04a59fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467658d12000000000000000000000000000000000000000000000000000000000000d3f2a91c21effe4723024b09b7e5c95db8b3530687212ab63ba493ac18f299d3e944e10830aca3727c0bc2f6c00b5dfeea756927cbd5ba8f11700b322e84b636c6ae848f040580943801fd324784201c8f315a79454f905b0bf02e3d6d6e18bbc78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123ce8589c2980e9ad4e5453d8b04e3ca8614fa7ef115f5675be84fd6cb59300da9dd489eaaad2cb96d5d9b274cb8d5316e8d921bd9f77cfa9b59b752863b80360a3cf6d4b471627d370b36870a58a1fac397f49aa7b37730b29bf409f301955f30ef26a6ee85e12105902366062d0a523a22e87f3aa312880148e5305b1f00ac117" + }, + "strategyValidator": { + "withdrawableEpoch": "18446744073709551615", + "withdrawableEpochProof": "0xffffffffffffffff000000000000000000000000000000000000000000000000520cc47c38af7c1a550b6f04a1de72582ceebd40bb079dfb08d97d3849da57dec6577d74f6330f7eac68fd70a2ab73d0151027f0b7ef34a1d49baf1f09fa709594ba008224247a366d5e275e1fa0b857f17d93a08ada63bf50c1333fb718802fb1f8b3c01fd0e1c02b6dcb40b88efc7a98a81737cd6190a86e6111377c44cda640c46dae7aa2bcd9e0025f440d22247990d4bccc9ff418e20bbec25ecd603c16a3f843040886ece65232af3c967d92047e6bffc087d0b6b6d8d85b33a3ecbfab45424ee0d560d6963aa0344fe55795806c475e9625f9a8060ef44075155d18d49efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30d88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa187eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c57ffff855e60ca76fbb0c9121d03cca892fa0e2b3f942c0ecc993e073ecb7051fcf7ef3846b857c4105df9b918d5bcdb2765f42fdfb602bf844d7c4044da17ba10608f38f8cb6e697fa28c3bcbd3a636a941561bdb912bf79438a4d1bcfc3c2f6f9717ccec81d352e5ac9fb7820081f934f972f606361d12e47e708aab340c5ecba91781d7a9a1477e622a52a6d970d071feac0b0740ed1404392aa9fe5e95456d5e184f0e63555a33e9a53a31b52c440c554ba0acd1bce1b5198a1e497d21fde7d01f112f47336622d34369f92ed5b1e10ed9a862f3ca9953125283f29f5b3bd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb86f3ed221fea9c64a8a7f680f130e09222ea6dd192cea5597acbd3787dda4c55b6f2f7292b05c1dae94381ca8144bbcb9d62e66a17d7c2da0908b8be7fe61479b6ad5eef1e8fce0593dcc16f0bc6a1cf7dcb200faff52afae0936e3882284ef88f2a9ad59109fe9348475a34b33d0e8bd6f76b7d0c614cb1d23272f018b3f8ac8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206f1c7f1e0000000000000000000000000000000000000000000000000000000000c6341f00000000000000000000000000000000000000000000000000000000009209427d30b39a38564a9850d807d9d4b3cd8d741f2b11d956b0be59a3ffe396fa6084de5617aed0b08c666660be00ab672fd146fcd37c24c539ef7cb5d4b84ce048b9d52a0c9e32d61a69949e93ee725811957aa6c6766fb25913da05af2ab29a9aac754fc773260d6cad4980237b5d94001d569b94d5e3ee216ffbccf8291b96e0e5fe931f9b78a24cea68413dfb7355cc9b8f446a8b42ad7cb2147f78dc3dd489eaaad2cb96d5d9b274cb8d5316e8d921bd9f77cfa9b59b752863b80360a3cf6d4b471627d370b36870a58a1fac397f49aa7b37730b29bf409f301955f30ef26a6ee85e12105902366062d0a523a22e87f3aa312880148e5305b1f00ac117" + }, + "processedBeaconBlockRoot": "0x2848ec0bcec98dd33ec445700ee85e9428ce706d7d4e03d53488162c17d43982", + "validatorBeaconBlockRoot": "0x313ee5c256ce7c7ee80586eda200d3af9f63cb2c481679c52696c8a41fc5df54", + "depositAmount": 32, + "depositDataRoot": "0x8cda77ba98ed8e8405f204592624bb8fbe4a5eb10ed9069ca0bb3e3a70ee9900", + "depositBlockNumber": "22839151", + "depositSlot": "12060596", + "depositRoot": "0xb79c73d23892c0444087328e549f8767edc746ef3999c35d797aec34bb3144e3", + "depositProcessedSlot": "12084256", + "processedBlockNumber": "22862662", + "command": "npx hardhat verifyDeposit --slot 12084256 --index 1998612 --id 1 --test true --network mainnet" + }, + "comments": { + "register tx": "0x8998d4f271f149cf48eabe8b8d0d6d8d0b914734758ac36f5845c042dab21eba", + "deposit tx": "0xbc44fa6de0a8e1e307aff7a4702681de4a82ec495db5eb2152a632538ac2bd86" + } + }, + { + "publicKey": "0xae6ffbc5503689b775143e5791574b3e5812f3eaab905f82b7afb31317753ca42c7784331d5d1ace7ef740b2cbb4cfdf", + "publicKeyHash": "0x571e5505bff1e9cd1910f44612429263d77f37a2a90a5d0d6857e981fbae99b6", + "index": 1970362, + "exited": false, + "operatorIds": [66, 88, 111, 121], + "sharesData": "0x9796d493a3dbe3f4449b5da09113d406a1d70fcc84256e55c813c0a5f05382146e8d60951b2ef6db5e1cbf12cd91891f179367a41a02366c25441791b20960f76b38993e71f65c0211fe3139c03584f8486b5ae6b0f9fa993b30a9dc1334181a805f7b4bb6244da124c0f66d9419295962507d2d429ab6c041e7f169d9bbd7c82ca340dba15619423b84725d62a403b7b61ec3bed139e5edae0ac03d26c435faf973d6031290a57dd01555d5144909a10381c3bee82c71faacbb1654e45e41c7b5b4a5087a673f870eb6a7803b7e81c2f0e407875adc3ac42d659124b36650f2b4e8a2a0be32dac7b3161edb2a64a68d90ca15f3a8fcc67123ed25316033ea91962be88ba08c9f25ecdd21b91157613a233433033185517c5adc67c90146172ec3565944e695890b34f0700effb090c8a4c2f976c32ec121aae48fd2de6665b25a1084ef877fc7a9d89b4b2b768af3fd49aec5758e70f4a4484062ab517b5af167e2b99bb57b2bf62f61544078de194bec9ae1409aeb6d7e9655684a33437e8f3cf54d7462916b91f92712f831365ef3e3d260d79dc086273fdad905ba2eb1e5b36e1439330d74796f5aa40172a711850b0d85112fed0e050b0d4cd3bf43ee01b18a29fff7257f207f5ba3a840e29dfc963ec28dcf9afe52618cb21cdeec47d1be2d868577d69e1cc708b9166d15ef5a0576067fe95a1a186e68c1fbf2673a549746fee41f9a06f391081568738b829b65832cc1bc04c5032a0e403b68f5de783594e264ed9546ffe677619bd125a7cf1c141f5cadc6ef763380a4cdc2081a560488e51d7d004bd203475458c228ff98539b947337eaa36e9dd933854e516a6dfae97f929c36e9a163e4632d93f285f3b532dcf8d87b2b308986129409f5b5902fdd960d976ed5af5f271d4a07ee7a1419724b6105202cad078aaa8742cf607a099a41ba1554b3eac2f7ab2159fcdf6f2fd936482a7bb08c27c4a148b6076881500ad91ef0ea8119b02fa214d20fae5188809bc162ccee97d3a80286ce8145310ecc8bfbdf5028601e3dcc1787661dd16400ab4ee800ae18fafed28e29bd76e92c59344c52c5e8e762b17a3c1fe0e73a5b55f4597c2f8bb727e7a806f0c83a6b1c2049223dee863f02810912b47e185a967a858f78f2d9a1f9814c13e5927adc1e599fb748e85781e9a3c71647d4d7e4da91445714313acc3555580049ae4faa33ed39d707cf063c4d40782e41096a800ae902cd9b762e2db240b18587e1e90909b33f525e792d2808d817f3da90ca3717880eca57b75402bab0f10fdb010b999796954f2af73653755fc81104b5c0defbee8e820a8df5a3fe746695068347a1cebbf49bcbcc202b5f7282f6ea35d20f6c37ae4602876840ee2064681aff59bd0cf7a9e8194a00d8474a55680a17091a7e283fa9ba6653596453519573f353e5042837f324bca538b1db893cf946d27057b0f1fc9099cc07e9439e20fad67f1370903433fa28de9629ddfeee7dcede326b6f8d5206f4b683e6274fd754e695a08e4bdce1f27025701fc76e9fca3a294d05d9a20e988829b9ffdfa78bd632fef1b4c89eec9fc18c47472144b695f53f571094b892ad9f0423ad20687e1dca9a664dd689257cca73af3026f9ebc9f761fb860aec8542d624612d89244a98d11fb4fbee2b0f577dcfcf39427a59cd80585d83e1ddd3287539aa5eeac157ec55331551239a460da5cf0d4764d1215a19daa1f19eae258cfe2f836e6c1cc249a0b1d6e6d6d9709939b2572e04eacff797cf1b4d16123209d853c7586fc48ad097c16198a16b8142ca382bfa275335f10ed695cdccb4fe4e4d14cb2a086d8a001ef7b7", + "signature": "0x91d683fe66e8b9a73848306cf492dd7479d24efc2aaa882aebfc3bb18c7edd8aec5758c02d4a5fce79ec334bea445a011600ffc66edbbbf036f31af5adfbb07c881cb63143b25d96b34f5942c453f64dc2952b851527ef863cc8102e561c5842", + "validatorProof": { + "bytes": "0x020000000000000000000000840081c97256d553a8f234d469d797b9535a3b4919327cb9763c96e00332bde93bdbb1032c4b796dda73e515c8c5f7ede9a419be7bcd0b7284699f44eb97abb558ba60e959653f455703d6ba43989d70cb102dcd25d885c6a7734dfccacc9d8e20169e36215a7561601a7092b13d91b2aa870104e315cf582f422fa3463aa980a01e9be23a0cca83942e9c5b8f321d116791b8f29dd2c65ac80d11bb1375d95727e420ec37e441b7a1f0abbe432bdefa852a0cab5ccd76ea79f7853030292d9cc15aa69b8066d9ce70bfe74c58b276604b26c69d5e3cb456f448a0b9d696b81609dafdb794370aca32aaf649aa80328bcca586c8fce2c87eff882c8739c5e02185749590e706d8314c656f3024c3e4af3ced4cc274d9e18633f8bcf76f9524c4574475b596d2c0dd0997f3f1dd9f1955fd32f8baccbefa5ce7d660bc1172c260a497a3d1bc384187ec996f31f659a8a915776fcdb611eed1d5ac71085e411dffba66891c6fd112ff5ceeecd6fbae7c88618edda216387278799eba90a9f86e3b628f50739030b1a4cc233db0bfeb181c54666c4118501f9364b84413857f2718c8490ea33671b9c9b625926ab669cc551fd1ea606cec0e3ee86c6fd45e7fbd7a2bfe3753def9c148ab56d555d0bf44ea63c8ea306cca6572b6f8a03d8a8dab0fb1c11d0741a952217613290f680523e46409093f093a6b7610254da83942661b17bd73b8427e93ce6317d19e95675a348572aeb35cc21eeaaef8664e7702b83ea092772ced7b00c1b953ec1d97a1f7a323e9f563f7138ae2a6ffa12d7e7d8f452ca1dec5cb87dd1dc2bebfc3576397afaa63aea88fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb48135f37c0a5f02c0cd358938dc3c24eb9d0158283fc328ce0f5176c040bdb043d7f638fde7f712618272899e5f6b465b016f1031b4a5710c32df7b4b451cccce2f559cd1a5000ecab92f2f9407e3ac6cbae7166e187688f56ea62c72d59096c5ce5e37b4d4a8bf060fbb61e86e16ea54068bc9c4aabd4273d4d76bf1504d0d18a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fa8e01e0000000000000000000000000000000000000000000000000000000000c6341f00000000000000000000000000000000000000000000000000000000006d833de4787b4b14e2cf638da50c1707c9ff3cb0dc7a45a2660b4d3458c7efdfb6c721321dfb0e529649f74be64a31bbb7d70d337f37a56b020ecc32893d1dec899f8e0893bc267e3256c7542c6cdead589454432274d6bfdfa33e5a4372c0408332dd055e96fbddab4824219c99c6efd32f62a94680d7d7fd9a209136498a235d9eae31ca1126c31a097992d1aa46e4959dac388d050aa45b9ba26dd04c29d71e55542dbbada164392f6f61345af3b028848382633277ad75a17af787f75b843767942c6c4d511c58ce23665f710e5206efbcb15a207e9f7bc0bb86306f7409c256512691d51102f5d48c78b0bee220af931edde3dd49847fd7675ad6ab7fec", + "root": "0x67b5a0c6b331ae00e44423f47c04bac99efe6648b93cff9ea4ea883f12948ad3", + "nextBlockTimestamp": 1753248035 + }, + "depositProof": { + "firstPendingDeposit": { + "slot": 17222000, + "validatorIndex": 1970492, + "pubKeyHash": "0x3ef2f55a026cf6c5afd71adf80ce6022633fde55d1fcd35b1b75c0fcbb192d5b", + "proof": "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b17cbcc37fad7e20f606f84f1610ce6558e38d455a8bf29ae6dfa5d3ebd176f5f82365cfe1a3e965f0a337ad95a56032c95b18963cd2bfc00d8c55b0b208f5b3eea8d49a6da1c9e1aca4f52713fa32b5f370579bcbc41a247aed882d0eae11ec41ebb02304647c0a12023580086795b1665770cbd6321e23246f6a9e2ca546badddcf9297c2302c6e5f1c5f708fc46433c31c7d77be582abed8e216be3690848bbff190913d38dfc25f83ec222d0965173d15e600bcf1ba8a364fd48332b81a6239361343788dd289deb7d88cdb8aee18a348c4a1cc097d9a84789b67bf923f6317ad6125a416946450764256c6e74fe91ce4fea4d7cde3e52c67b7b511e67824c2f2e98babb238d1b9b4e92374fbb8012c7b6d9d27ac86647707450b1af0a1e069731906b6ac2b7dc23b8906034fba9a19759312c08d82267b8106670dd5782623d44856c84a7dc7f84d7ce7f0346c49894c499291006761544122938b9aae253f30edffad80f7ada3e57974839a5f02dd1ac684ac0312877cf7c8da6804519c35147af572eefb6e876dbb0ebe24edbb869975c8d844618916983abedb92ef31c460fccb2b7cc02c75326c9e0dd9511ec5f8ff5862fd30590e7447e9b3831756c8de5c21399cdfb7af93a4b303e6a04f452b5d1ba40ce4d51efcf1f202c996ecb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467659931000000000000000000000000000000000000000000000000000000000000e368e7f85090466bfb97e90d690f9ad2dfa4bc5aec10085664467904bdcddb47a83fb68fedfbee08feaba25e6acf88d2717c21bddd2d23655eb1253e590adbc7be86c1d0bc26a3903e43070d53163919606e77c8a21eb79a798ea8a662928ef5c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123cf4401491f74ad92ff4e137c8b6b054b1fc4a71df804d8f2d5da5ceadb85c993738a64e3b8324f5d592e47c2d87961e7327716077fd5ff46efa8ac12c507dbb31465b804a2fa645116753287715e09033b43d63cea9fe04711a2466b421db0f19626b3f877d44248f5fc8384a59ff5a5e1487be077db492d2f0d1b55a6e45e1e9" + }, + "strategyValidator": { + "withdrawableEpoch": "18446744073709551615", + "withdrawableEpochProof": "0xffffffffffffffff000000000000000000000000000000000000000000000000629170c1f319a3a787075a5a44957b58fb0b99af3890fb0c21b2558b9fcd86427263d6c41ccb09beb36b7999186e87a3fdba2b976e8c4b0c37ecf4f05761771704b93382cac55b43ffd257b591a5415488ec289e9b2217861e640aa72839aadde315cf582f422fa3463aa980a01e9be23a0cca83942e9c5b8f321d116791b8f2166541c00257c393987a7345ec127bcb42eb7ab54fd1feb92e08762f33e62f4b5ccd76ea79f7853030292d9cc15aa69b8066d9ce70bfe74c58b276604b26c69d5e3cb456f448a0b9d696b81609dafdb794370aca32aaf649aa80328bcca586c8fce2c87eff882c8739c5e02185749590e706d8314c656f3024c3e4af3ced4cc274d9e18633f8bcf76f9524c4574475b596d2c0dd0997f3f1dd9f1955fd32f8baccbefa5ce7d660bc1172c260a497a3d1bc384187ec996f31f659a8a915776fcdfc5e0801ead773994d793c5732c7719c53559318349d9d6e218e10824a39a02f506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1ffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa759642201c39969ef959f11500559ce7773508561bf587826ca41e0244d91060dcd12973df6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92bebe4f8d34de800fdfde06a61b46c5a19ffb29c07850e8ed009bb8294c23701848e8a0aa86b62313bb13b657f47cce5f80cf47c17d768e754530c614b33647f68680a01c2b7ac4ba595a43521f97ca795d74300d304e7d225d0ca3b58064d1f0496c51797f80350b8b70bae2c1eaa02fa57283cd3b6c599afbaa91d4d237c356d188a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206f3c111e0000000000000000000000000000000000000000000000000000000000c6341f00000000000000000000000000000000000000000000000000000000007a8b2c55963766eed08369deb4450a622df0ff5586231a14c42cfe9d383cf05d62e61d1e474094946ae9e3291bd40855057cf25f08b6929a697dc17633eae3e417feb16986d9f595ba4739f46b9fef6499d02e396c9d41d9c870ca3bc08e9bdb9ef6c912f79ea5557c3c60d8eae10ab55471ddbb6fceca8fcd80eb7c4275f9208dc706cf4436f3e0343ff7e1463e04f597a87f7cc7db39140a0543bf6f4bd03b38a64e3b8324f5d592e47c2d87961e7327716077fd5ff46efa8ac12c507dbb31465b804a2fa645116753287715e09033b43d63cea9fe04711a2466b421db0f19626b3f877d44248f5fc8384a59ff5a5e1487be077db492d2f0d1b55a6e45e1e9" + }, + "processedBeaconBlockRoot": "0x81fdfe15c4b8aa60db878bc367c5a8a4e9e3f10d81a5db5092516ef747079f91", + "validatorBeaconBlockRoot": "0xadea9b809cf072d6aadf915c7ff14ca87604c52eb71c3107cadb48c1ebb85929", + "depositAmount": 32, + "depositDataRoot": "0x9c5c66351c164af50518df36feb8d2bdb5ce23790de71b03852c682d32a42056", + "depositBlockNumber": 22696000, + "depositSlot": 11916504, + "depositRoot": "0x079e6ec12cfc226e02d578e604ba0c7791e381f9299728189f9a7863c8f85b8e", + "depositProcessedSlot": 11966000, + "processedBlockNumber": 22745150, + "command": "npx hardhat verifyDeposit --slot 11966000 --index 1970362 --id 1 --test true --network mainnet" + }, + "comments": { + "register tx": "0x4b29a7a2f442b44d8f6d87626743fea9a292608e9f5348e0cc9ab3fcce6c328d", + "deposit tx": "0xa93890a08cd7c80224a467f2d7e6422ca466fcd26b069e0e8f4db70e433f1388", + "comment": "npx hardhat verifyValidator --index 1970362 --slot 12202000 --index 1970362 --network mainnet --test true --withdrawal 0x840081c97256d553A8F234D469D797B9535a3B49" + } + }, + { + "publicKey": "0x8151f954de22a227cb06e52b8eb166b85f7bae128b7fe6a011f83c8868d1590035a5e015b7369d01a54ab55ca9f64a09", + "publicKeyHash": "0xcba03c5675adb8c8c81c406ababd5b8d92e8a1c3a8b17584da19a593f010777e", + "index": 1922881, + "exited": false, + "operatorIds": [1032, 1033, 1034, 1035], + "sharesData": "0xb5813de09b45e832a1cc1685d8edbb9882c64f1be08f0149497c8ce51568464d82ae9c27676d0ef55f4c2a093f419d1e05e2506e5a111f482a19c929f96bdde514c7b7cfb44e8f439af32341f9bab1df65f76330fc7133825120612acae8811481054768ef819d3558e57d2f7d33ea029d2f8156a18f8d3d3e17379bec65da8befd83e2f08af50a35888ee733c24567e84e0e03c7c06ebb1a70ac926cb40bc1d4c0995d0b4114fd64b156a56fbb289b2935f227f0491dfb7390e5e091faa134090ad77c0aa2c720ce1020500b428f9b93aff8352b68f637b015629b7539fa1d4f480c448ca5966b380ab7027478d47b7a05f56798a7c9a07c5e9b86ac6210812048459fc2d223b70e53f6507e9a8b3c814df088c1d718c7acd4592627ee038ce34b3d042c4a7498ad8799c39f317de37c97d117732c63915f06a655df35b307247a6b78618328e0b885051e9c34637fabc3f3da461dbe310e5ba3b82009c1f6b396cc15f8ddec0f4aec1f863380eeea04fee4baafdb76a0e005c86ce6fbd732df742923690dfca3fe14667dfdd108223d337369bed5f3accf1cb9cfdc119ffb53bb5c3f0512f2d015d5b5dd065d17c5a63e7fa1166f49736ad604835a2b4e61be5bcd075e12ea5b43c8fb9da2d62b49ae925a1b5be4aee0d42efcc763c0ddfb026bc694ef0b73e518cb989eba3b102dcc10a5cfd3540a7efd922245f5e13a8304ac1ee554f490a00ff88b4fc5b0934c0ceffdd2e7aabf1b0c129b414c75b98668ada8358483f925c995fd022721746d16cf19d8a8da70593784987c870064c95888a6e4d8e6e8d1b67c52b1ac8b529231f849e51fd5560cc68f9cd84ec7f5c2b3771b5f81e8613a33dc1bf9afd134db0e99f89ebdcf2672ef98c59d79688427e01e222c0d71101d2f482b09deadc76bf9d71dbe0c64cb598786599267cd563c735283113506a1b2defdb616691590d9f5084f263c8c17451a350b91151d7d5d41e0ef830260b1754ede2f3c8eed48961c08afac2df1340003bf8fd901112e1b1f4e74935d8e2a3aa6846b5237ebe29018bced8ce3fef98b6acbb3a885191810f453f2997da86ac3b049c8d81684a723dfae9b60d606697c1b1f51cf6c18755cb4c02736447b7d59738ca776a36485eb7ac786560e6b623b2f2edefaeec59ea0fb4591d9214b3748ca5da7dfe67a0cbc1d4d3afa24741edcdbca26cbc71878be77278b6c808c19aa6c130a138bee6816f1e4db6a96df7bb24bfabc193c563e15f8f8898c6ceed4bf911d4d2961d225c3689e9d584d067659f21b133e925d6365606589370404275deb1c89fec4a0038843ad6fd398b0f023d9b88f997f06193da1dcf4d35c4b308ef8fa4c7752612c2afc4727acaaa253c01a6db74bc9f3bb1da46d6bead5defd482c197800801781d5d863f35ca0c01fa17ab731edfef6ac6b10611f86fa37e14c039c1e622344229d08ee95520e7563dc3820c83902ac5b8593ee982ef6b07e517d523b0a825679b1ef54629b3b13f369bf987b4c6ede7a6ccfd9d06a08ccc2b63f361350dd25b9579ffe56e3fbeb43727dc3f9e5288ad3ccc39e677d5a69cd937c1064988565e46c883ebcd81c1f64b66c03f0f3c9f390dc9b857b0b151a2e6bfa8ce189cc0ec6ef77c23a0ed6e495c99009d6c8aeeee17176bde5e42cfd00c995482cff10c279ee13480f0e8d8be847971a4addf546fc980741de4e422a9b957ecbebc84966b86ba95d4ac18603ea00f850feb2c6ead70af3647704a48ceaa3b5f9b86ddbb7d0e35d5e3f9c364ae23c6573517e199bcb9792068b5efb8064801507fc7f44524cfb9e9da68b73e7f88d8814ca5ff6bf30b37", + "signature": "0xabd81e276e6da099c3f2d9a531d356e11bdad98011ba377cc2163b2a638063bb4db28e7d3203a19d9022849fd898340f0788b9029d46f98e931db13b61afb00b9207ae7a7a2fb68bb03a2b703c638a488dd29ba32e55e6294d2a46b7e154a708", + "validatorProof": { + "bytes": "0x020000000000000000000000840081c97256d553a8f234d469d797b9535a3b49dd6a3e70baa48884b3a3f1eab1fe9673da1f7bcf82c262c7afeee7f1b40de69e5f9a0dd2b4f4344ab7d9fcf74df49d03e872af506bd23ced195f63b04734061002581ccc552516fef940a4ca68f0face8ffd1629b47f00613a5c21e8501b63f3830192003d60b01fea05d6db5b313458486b957ff1a92d07869af075b2b0ce86a11e5eebef6a59acdeffd2e80f01f7c36895c197345f43fbc9510f454b4510f0b8d8bc8cd3008e8afa8eb0a5042e6d38fab89f41e9e0a8889b3c191d12b84cdbcad6c0a6da6bc193f2ad8416df3525a8066949fe6876b0b2fc2f70354bdaff891130eda3523b0f9fc04c84432b1344dc2d9916f2e0c9af6ddad5d35920564c90194c33608fcbac82511c96b8115286234ae0ca3f660d5fe0096b042d928c006c19f3dd5d0c0cd761953836831014f94a170f5dd192dfe66b76bbffeee84ccf2455df520d7d598fdfb8bdce55b8df4b8c38a05b9f1dc7b5e0f2dcf613a848ad39bee4ca64f6c875935d9e43ef32604bf5955b84974d917ae32e7761483f6a5744e6a6bf432a5b23c90f535ff7bab5abdcd959d09368df1803320b6e268af8263cb445e1c6d80531f1c9adfdb81d05fae2298a9270baaa49a57c01def74a621e1eb6d834559825d78b834f75cb9adbeee309995d7c23e2a4856ca905756cc67d6e30637b643f4a03c8f5289e7163e81881ef3b2cfb540f7058d237576d52d4521c7871ca8eb3b3bb0c83dd15c2f91d4b9388f1fb9450545c0173b6eaa3f188f03e291d342ad0457de9916ef31cc38815375e6aa1c2b4940cd733873f2533a744ba859478df1c06b148271e8d12ef6a460c9ca26a636ba9b442cd141678890012106c44593d627de4af4487a52d4f449a373b225d7bd98193ee82ef6510cde5872d3d7f638fde7f712618272899e5f6b465b016f1031b4a5710c32df7b4b451cccce2f559cd1a5000ecab92f2f9407e3ac6cbae7166e187688f56ea62c72d59096c5ce5e37b4d4a8bf060fbb61e86e16ea54068bc9c4aabd4273d4d76bf1504d0d18a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fa8e01e0000000000000000000000000000000000000000000000000000000000c6341f00000000000000000000000000000000000000000000000000000000006d833de4787b4b14e2cf638da50c1707c9ff3cb0dc7a45a2660b4d3458c7efdfb6c721321dfb0e529649f74be64a31bbb7d70d337f37a56b020ecc32893d1dec899f8e0893bc267e3256c7542c6cdead589454432274d6bfdfa33e5a4372c0408332dd055e96fbddab4824219c99c6efd32f62a94680d7d7fd9a209136498a235d9eae31ca1126c31a097992d1aa46e4959dac388d050aa45b9ba26dd04c29d71e55542dbbada164392f6f61345af3b028848382633277ad75a17af787f75b843767942c6c4d511c58ce23665f710e5206efbcb15a207e9f7bc0bb86306f7409c256512691d51102f5d48c78b0bee220af931edde3dd49847fd7675ad6ab7fec", + "root": "0xff202dd46e6f86ee7756c183fdc0b34b4175671b3126799d0e4c4665d0908c2b", + "nextBlockTimestamp": 1753248035 + }, + "depositProof": { + "firstPendingDeposit": { + "slot": 17007000, + "validatorIndex": 1923098, + "pubKeyHash": "0xb9b8ce4ee440555a9971b37ec3504acacfb527575c559b2fb44956d2642b8dfc", + "proof": "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4be67a29c6a6a6f2d8d5370f2efaa6c7c509e8609f677939dbdf2ae3a623b70ebf16d7e1e6bbbba71684e04a51b9fe43a7685ab2a13a4d77f83818ddec26f46caee1ec526374a5b8afcce3beac6542d8bef0c93c7ca55b8c064b6dbacaca8be41cae8c4729b450f03f494460c1e4ea7da891f89d34ad31c9fb901e82d6af3fb180f605bcde9222845d0802fc367e0cda6d68926a7dd1d6dad19a968344a0bb05953a29ea29aab4b2983b41e2b5290ef897781f59898e48cc3cea8985ce39b82770d553a6787bc8aa20bf00e07258ee849c41fb6d642a6a5d82aa66bbc8055718cf0f6eb93fa22210a1e10a02571a4d10357f8ddafd452e684b1b884a1f08102bce8a3d61fab8a0bd72bb91decb9c96e0c36336a2f874e08b20d729cf599c984c575992c8fa21ee7996706ba6701d5f5dbd40d5a90e5cd5460b7a7a5861d75e7845d6a4c1b6ecc9d01768117d9de80593c670c342067c14a4b9d6128d761250bf76a284fcd9dcb636ad36648b12e9daaf9cc204f708cf19a4c9c26c1a53db5acbe884f4aa252a9a53c2cf8fa61d9413630dbb0338859de3e9bb8c114c70a66ce85ab7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765870800000000000000000000000000000000000000000000000000000000000040614e7af5417cd5b66c986b727d9c298bf1be608b058cc0f1ad761032db597f857589992b3ff898a63f77da162db0b6b41b542178642f7f3d953270477ef54b446240a0d633d2bf637517bba78d5431403aec57cf8abf4e8034701ebd96b76ac78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c6ffc3e34a5b0daf6e6b480670cfc024e15ad5a0fcfc5fbd93f3b7463348a390090cf89d70c614fd5a8deceff4b505b9b29bbb0b5935e96dcc602975d10ba17ac89feb19e44b1cbcec2dd3ebdebfa85f7bd6d573353a3171512f02d0a7aa19890c8ef9b095e983ed49ef225df3f4e211a650af432c9469f99792ec9f77cd7ab4d" + }, + "strategyValidator": { + "withdrawableEpoch": "18446744073709551615", + "withdrawableEpochProof": "0xffffffffffffffff000000000000000000000000000000000000000000000000b4b98406b552756583d35dc2ea9232ea125a12f76d4667bf5f2481974211e06aeb8944702d5448511c63a3b85e4ba75a3c75f12bcbda3ffb902d2bd0d1e4422302581ccc552516fef940a4ca68f0face8ffd1629b47f00613a5c21e8501b63f312155df2bd1610a5a053c33291461eae8ac683e5d9be12ad2deb144e7a0af908a11e5eebef6a59acdeffd2e80f01f7c36895c197345f43fbc9510f454b4510f0b8d8bc8cd3008e8afa8eb0a5042e6d38fab89f41e9e0a8889b3c191d12b84cdbcad6c0a6da6bc193f2ad8416df3525a8066949fe6876b0b2fc2f70354bdaff891130eda3523b0f9fc04c84432b1344dc2d9916f2e0c9af6ddad5d35920564c90dacc2ac0e8d5edb3cf6b1bcf955e10e68bd8fa9a1416015a734f2c9216ce3f7e232937fae58e5667ecc9a67b3b9087d220d92cd9f226a3bcfe61d0c376c5bcba01b9d063f6f9ca056e6d8cd04ff80bdc89c35d57165ea5ee016014ecf676c42eec71b92d93ce7e8e173caf9be5949642b64158c16532769496baa7ea1aade693ed7139ada0fb6614dca9cfd04d2fd023bab3c954e439a6ea8efe6ae7b41491459388a688047183bdbfb90f3b27cd00eb1dc86c19764f60a162cea29f8a9835900ecd6bdc34bb74d017f0eca286295731146c54e3f47eb4b6705abb90415be69ddf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eefdfbb80fbcee9881dc6bd8113cc2e5911d189de59a0fa0b2334ef9c48aab7b7d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb702a260ca4e86cde88bbd4e89a0434b1248f950f5285d9651497ab636a76487b8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab70c79bbb870e32d19d92a0b434f49c3635f0730a7fdf9fffc83715ef876ab74bdc48787efbb522105b00637ca18a2f575606003da37363dc724aa427c05f59dd52364a3ca50044766e6fd8dc88c992e38b2ee02f69dcaca013c4562a194c472d8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206f1a581d0000000000000000000000000000000000000000000000000000000000c6341f000000000000000000000000000000000000000000000000000000000012680a9549cb16f47b30cbe8cdaa7fb89ce055ca94a2eecabe7df0dad227370c5c7eb7c2e92ebf55383257188d487855e49b1abc16a02cff67be58a33f3ccbf3105216e9b481b873d1a263870df9f2952b334aa1d9f30b2170b92bf212a86393f19d742b493b1f45dde0813f3116ef999b60661c3e6b3a14a7a8130bb9d55589f55953e9d3da733ed84253366f6a2f1de8ec2b8df4fee97a961320d7614ec75c90cf89d70c614fd5a8deceff4b505b9b29bbb0b5935e96dcc602975d10ba17ac89feb19e44b1cbcec2dd3ebdebfa85f7bd6d573353a3171512f02d0a7aa19890c8ef9b095e983ed49ef225df3f4e211a650af432c9469f99792ec9f77cd7ab4d" + }, + "processedBeaconBlockRoot": "0x1f3dc896f18ab2f4099a1a582645b784e567a71a503798f6cfe4348180b890be", + "validatorBeaconBlockRoot": "0x66d9038a2eb131bb6e8ff7c43a35bdfc3ed90d2a3ba9beaf0bbef39db5532c94", + "depositAmount": 1360, + "depositDataRoot": "0x4e0414016c2d547190d41bfc9e9b80cc5f7b214936b61d3a8d4922406073f9ad", + "depositBlockNumber": 22524536, + "depositSlot": 11743750, + "depositRoot": "0xda937b3db89bc41751648b7d75d2260609503a2240849aaee45aeeca02a7d3c9", + "depositProcessedSlot": 11751000, + "processedBlockNumber": 22531723, + "command": "npx hardhat verifyDeposit --slot 11751000 --index 1922881 --id 1 --test true --network mainnet" + }, + "comments": { + "register tx": "0x1be26d6e8af2486aa0e26f1230540ce3ac2cd34af2726035c57a0f02c08b6779", + "deposit tx": "0x1be26d6e8af2486aa0e26f1230540ce3ac2cd34af2726035c57a0f02c08b6779", + "Proof from beacon chain slot": 12202000, + "comment": "npx hardhat verifyValidator --index 1922881 --slot 12202000 --network mainnet --test true --withdrawal 0x840081c97256d553A8F234D469D797B9535a3B49" + } + }, + { + "publicKey": "0x99765f2450c4a0716c43a34563f0d64ff39aa525422943e12110ee465ebd03a1f3547b3a78ebead223469cfd3d1489ac", + "publicKeyHash": "0xa949125c896d12c0f6e79e156ee188e095405a875b924bf88c8a42f8d4aea425", + "index": 1922882, + "exited": false, + "operatorIds": [1032, 1033, 1034, 1035], + "sharesData": "0xacfca77f34b2b71b2381c6ed8f946abcb57456233156a7c2ba4cc7378854029a8c27610ca94580e9e31078770c520c1111447a24751873a04326dd3ca94a6c5aaca1ba85e6a051606a3f232c07fe18fd4562abf02b96c45077ce677eaca50ec2ad242b6a5b08cea2dfb07a253a46b4679ba16b9d9cbc639d6d08a21908f6f12ece06bb8870b2ccd6f1829d917f83b03782700c4292829c26fb9ac64ecca27428a80a5d9ca617ba147d5d0aa6bc8887628d99edf8bef5c20f0fa55b17e4a23d8ab387a1d1a62a22b8c0e0b9d32288ff5b707ce3e5d5894f16e5424717d7ad4eab1384c2cbbaadcd66ecb448c49ba9c8eb837c0fee2a58820c439cfb78ad908bacf428536b2ed3bb2599418689832fe51c6bceb4d8cabf566f3ca9503a7fac5936985b39e8a143b670279215cdc734b8ef34100ee11c45832cd1835c11c1308093179896f46b05acd14d1867612e2c9506468065b1fe2903223f893aedbd21dcf07e9fd8820b726f95ef988a12b9310dd22a44781c657e0fec7e04e237fb71f28b9d858eba8c4c48c703f67dd3dfee91e5fe020a03250ed8d29c4ecbeda255cdb22dabce17cfce984bafff9ddcc564a4bdac7cd22777d694aa4fbf3aa69a05811c77bc9ff58c308a0a037c7ec6820e56d1777d214646206815484c03da31b718b218d2f18d5efb3cffff79f0f9c5751901a40cb4b3f546e97f0b0aef7fc829e3e0ad3ecd1b8432c9f54508dbb5b3731346872349cc6feeb9fb52b4c68c13e11569b933dceaeaaef0b10e455878a5371e9012a87cde5fc3ace7a4b3f4dc5f94548a4309d75f0d03b8fd7684d5072ebf566e9273bcd533d927b13aa9f2bb841603372494aeb2dae7ca5131175f7c9150cd46e07415db29930f9f8cebd4224175d06f5eda70b9ddc0e840b5e350506eaa65fb35e0cd5e1b8b1b677c9f2737417575cc434f19b0ab4fa8e988516539af6af361c1960a73655dca5cbd2e5c948d69418b53ca9339fd169e2ae1b0f1c521e96496081e1dfb7cf1418e688535ea7f4d5829dcb2ac1cb6a59ca26ac59ed50544ee8a005c78a6dd10229b4005137cbb73d3cd7fe463babfcd277c00a0e67f2250901eda9a6ebe8869ccf1fe9b63f2f24594072ac8b227672c08a9f6eee20b918fbc97248ca785ef635d8cecae5af9e363c4ee9760a63cc6cbb5977aea5871e5fcd03f561b5ede38eefc59fe1749fdf03a0507f9813e13cb0aef68cc992aca4660b06fc937f24b89e4a0c20f53038b0e92c361c26ad7b56f33fab9f80afe23b0cc043a6d4dd4c85d1169ad6b67ed0e18bdee5b9a31a1c48522676465e603898c97030933b32110385fbb5038aa8dc15af422245fe7095ea1a65ba35b0befc10fd91700bb0b2ecf7a8b3295fdc37df8942a12326d6e3c6273340b42a130e7f418ae3cf227494de9cce6aa22af7fa20b4d22833efd40ac6443988f6298f938bd0f31320e6b8c8f562a183fa56fafd9fa038a22804abce0caf755e5f7f0d6773220f7894a5c7e35bcbcde6cd8e9bf2caa616000c25ce14dea2beb45bbbf3aa17af886e26ea54b01b26544029538e70e3909f06ce37e12b325fe38b3a9136d87fbe7fe39455df7fa36e6d431ca0ea6a2d89a1951f262d6a598cdabd60a0a127d84f896624beb953f851cf191adce80a2281d9d4740ab7471483c275f95dfcf3af4c465bf69c8dff00edc7b37cc8c86c532542cc7454f5149be724445714a8228046c46dc9712aa778717a516e4c595422ff25475c132db05c73bda063217c7a202092609186a436a5634062151e58f14a7d71741340c5ec203db873ca5a1dbf63b23b457b2ab10397052b890cca6cdd73c702679ad", + "signature": "0xaab8902380875ea5e54e1d8668d83741f67cf8655709d81744632904fae7ff616d6b36e62cdc8e2bf3080cc491749e191616241e2c86acd477440373706c8e29d66fe0c5bece03b73b3b0c6d1498fcd3257e75651e295bbcec9177cb6282df2b", + "validatorProof": { + "bytes": "0x020000000000000000000000840081c97256d553a8f234d469d797b9535a3b49dd6a3e70baa48884b3a3f1eab1fe9673da1f7bcf82c262c7afeee7f1b40de69e1612043fd2f04c4fb8947bc583c2ecd38dcfb14b28e80b6ce71f9a68eeebbe6d97f4ec2db6ec10032f599cf0917a0020364d26af391a529cb15c9dc11b89bfb60626e2b98cc938c9e8007d50481d8869e6acb5875c17c9e02c9b8f1b8929ff85a11e5eebef6a59acdeffd2e80f01f7c36895c197345f43fbc9510f454b4510f0b8d8bc8cd3008e8afa8eb0a5042e6d38fab89f41e9e0a8889b3c191d12b84cdbcad6c0a6da6bc193f2ad8416df3525a8066949fe6876b0b2fc2f70354bdaff891130eda3523b0f9fc04c84432b1344dc2d9916f2e0c9af6ddad5d35920564c90194c33608fcbac82511c96b8115286234ae0ca3f660d5fe0096b042d928c006c19f3dd5d0c0cd761953836831014f94a170f5dd192dfe66b76bbffeee84ccf2455df520d7d598fdfb8bdce55b8df4b8c38a05b9f1dc7b5e0f2dcf613a848ad39bee4ca64f6c875935d9e43ef32604bf5955b84974d917ae32e7761483f6a5744e6a6bf432a5b23c90f535ff7bab5abdcd959d09368df1803320b6e268af8263cb445e1c6d80531f1c9adfdb81d05fae2298a9270baaa49a57c01def74a621e1eb6d834559825d78b834f75cb9adbeee309995d7c23e2a4856ca905756cc67d6e30637b643f4a03c8f5289e7163e81881ef3b2cfb540f7058d237576d52d4521c7871ca8eb3b3bb0c83dd15c2f91d4b9388f1fb9450545c0173b6eaa3f188f03e291d342ad0457de9916ef31cc38815375e6aa1c2b4940cd733873f2533a744ba859478df1c06b148271e8d12ef6a460c9ca26a636ba9b442cd141678890012106c44593d627de4af4487a52d4f449a373b225d7bd98193ee82ef6510cde5872d3d7f638fde7f712618272899e5f6b465b016f1031b4a5710c32df7b4b451cccce2f559cd1a5000ecab92f2f9407e3ac6cbae7166e187688f56ea62c72d59096c5ce5e37b4d4a8bf060fbb61e86e16ea54068bc9c4aabd4273d4d76bf1504d0d18a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fa8e01e0000000000000000000000000000000000000000000000000000000000c6341f00000000000000000000000000000000000000000000000000000000006d833de4787b4b14e2cf638da50c1707c9ff3cb0dc7a45a2660b4d3458c7efdfb6c721321dfb0e529649f74be64a31bbb7d70d337f37a56b020ecc32893d1dec899f8e0893bc267e3256c7542c6cdead589454432274d6bfdfa33e5a4372c0408332dd055e96fbddab4824219c99c6efd32f62a94680d7d7fd9a209136498a235d9eae31ca1126c31a097992d1aa46e4959dac388d050aa45b9ba26dd04c29d71e55542dbbada164392f6f61345af3b028848382633277ad75a17af787f75b843767942c6c4d511c58ce23665f710e5206efbcb15a207e9f7bc0bb86306f7409c256512691d51102f5d48c78b0bee220af931edde3dd49847fd7675ad6ab7fec", + "root": "0x8337e657e14d740dad20ce0cff07a24ff78b41df62b0e1a8dd7c283c1b92720a", + "nextBlockTimestamp": 1753248035 + }, + "depositProof": { + "firstPendingDeposit": { + "slot": 17006900, + "validatorIndex": 1923050, + "pubKeyHash": "0xdaf8dcb1ddae4d7b644b0694d9c6dd9644158aeaa34d404d000cd02f5052f307", + "proof": "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b580d20b130d8ece69f2ab4454a36b3268b42eafbcfde5bc0d923de761a985061ca4f750a03cd75ec396da052099f249e2226e00615ab3c2c6cd02c7507b69995fabcc9fde8777d5b7b86de069029453eeb2612633bee1bf6d69bacda61dad63a0814a6012f14ab7d430982b7ed99db1fe36c257358655abade232cc2dd5349cfeed619a2acf283e848d18275ca276d1e37c6ff47a378ebd0de2bf29ba0a266e1f64ec83c2b15de37f39d855b6884874e32c70a5b6835cf96af53c5e8a358db281e4de7d2afb0234cf10852dad8c2579714d6daf8043350e624da7b985e7088316a89497de18ab663e44a820d287a97f001dc002d3fb45c88ee0713b842716496cf14ecb6a1f74c19b19e910b6b7eab878437da4c1c003cc83255e92951dae796f5e5247794813e0770e4185774dfb5e7bc738cb6added4c77acc8f40e30d9b81d65ee9025fad98213a073aa21924ece429b4589fdc6c76bb9ec853dc112964c835197d3c20ee0607963a48583935baf1ed7f32e6a98a3badc46b118a6ab9b96b5bcd2ca71e4e09be3cfce10127cd92fcbdf2bccf564c4155de8d91093c574303b7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765900800000000000000000000000000000000000000000000000000000000000040614e7af5417cd5b66c986b727d9c298bf1be608b058cc0f1ad761032db597f90a3f143784a01fcfe4d39412d3cd4634920844300b9408e030a71f7bd98a08dfc228d8212edc2c32c54f7bd58bdabb2cca51fbd131e7597aec292b1455dc9bfc78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123cd49e3d6a116a2a659b94a2e3464fd7922adcf67c2ef1e4889cc7b6799e6bfdaa3cde88df75066e614e2bd09504a5dcf7e1b4f7a8c9390d67db087d6da52163644c8a6d1333c9bea9cebc4cb96264526b6fe52b9176ff2636df9d79951432b3545af2e2e304daad997773c9866778fab2381d49e0d16332e9b687b3edd747f7b4" + }, + "strategyValidator": { + "withdrawableEpoch": "18446744073709551615", + "withdrawableEpochProof": "0xffffffffffffffff000000000000000000000000000000000000000000000000646157aed01fca72974bfad02a979289ee362c5feefeac27f7bc3e0b53dca3aa52d950a689dd072790563bbf0eff3dd289fcbdc78b7bf521bb9d86fdabf530e997f4ec2db6ec10032f599cf0917a0020364d26af391a529cb15c9dc11b89bfb6b7b39636a01668d437b0c1e1578e74cced2cd12e0ced3c941350df70bc795a9aa11e5eebef6a59acdeffd2e80f01f7c36895c197345f43fbc9510f454b4510f0b8d8bc8cd3008e8afa8eb0a5042e6d38fab89f41e9e0a8889b3c191d12b84cdbcad6c0a6da6bc193f2ad8416df3525a8066949fe6876b0b2fc2f70354bdaff891130eda3523b0f9fc04c84432b1344dc2d9916f2e0c9af6ddad5d35920564c90dacc2ac0e8d5edb3cf6b1bcf955e10e68bd8fa9a1416015a734f2c9216ce3f7e24a3c6afd65c4435e4a8df9ef581484e71014eea432f43aedaab9e4945ba8e8401b9d063f6f9ca056e6d8cd04ff80bdc89c35d57165ea5ee016014ecf676c42eec71b92d93ce7e8e173caf9be5949642b64158c16532769496baa7ea1aade693ed7139ada0fb6614dca9cfd04d2fd023bab3c954e439a6ea8efe6ae7b41491456cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa759642200ecd6bdc34bb74d017f0eca286295731146c54e3f47eb4b6705abb90415be69ddf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eefdfbb80fbcee9881dc6bd8113cc2e5911d189de59a0fa0b2334ef9c48aab7b7d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb702a260ca4e86cde88bbd4e89a0434b1248f950f5285d9651497ab636a76487b8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab70c79bbb870e32d19d92a0b434f49c3635f0730a7fdf9fffc83715ef876ab74bdc48787efbb522105b00637ca18a2f575606003da37363dc724aa427c05f59ddbd8a86e0a0c6240a62a0cc9a6d2f058f0e5a7cc085ccd9f31545297d543d34c98a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fea571d0000000000000000000000000000000000000000000000000000000000c6341f0000000000000000000000000000000000000000000000000000000000e4e2ff52941fbd034cbf92d2b24a9b8a9382515d2852e7bda059c4ddfe283d383333aea96247ac548b30366dd2ffcfc0b32d78c043cb560b5a52713e3365dbebd66f1d3fa6db1a6e70638554a097f4ef78e6b5e46492e2c7908c5426cf5b5216e4eabb9921867e474f683bba984c033983abcb3cdbe65a2bd99780f9c7eb28372b748a82f33935d05eb8ff9aecf0d85861d59c0c619ef7173d8d6aa9b8dd8c3a3cde88df75066e614e2bd09504a5dcf7e1b4f7a8c9390d67db087d6da52163644c8a6d1333c9bea9cebc4cb96264526b6fe52b9176ff2636df9d79951432b3545af2e2e304daad997773c9866778fab2381d49e0d16332e9b687b3edd747f7b4" + }, + "processedBeaconBlockRoot": "0xd260e9155a832a1b04ad3ca09b4c4d6cb7affa669788086e6d29ff667541c43b", + "validatorBeaconBlockRoot": "0x4fda06f9de466abe013bcf2db950d1b3cf22e9140e3fc15c9e59307ac0d163a9", + "depositAmount": 1360, + "depositDataRoot": "0xeb58e0411e8345c3831c8806bb64af5805698bb01496d50a279d749ccb8809f0", + "depositBlockNumber": 22524536, + "depositSlot": 11743750, + "depositProcessedSlot": 11750900, + "processedBlockNumber": 22531624, + "command": "npx hardhat verifyDeposit --slot 11750900 --index 1922882 --id 1 --test true --network mainnet" + }, + "comments": { + "register tx": "0x1be26d6e8af2486aa0e26f1230540ce3ac2cd34af2726035c57a0f02c08b6779", + "deposit tx": "0x1be26d6e8af2486aa0e26f1230540ce3ac2cd34af2726035c57a0f02c08b6779", + "comment": "npx hardhat verifyValidator --index 1922882 --slot 12202000 --network mainnet --dryrun true --withdrawal 0x840081c97256d553A8F234D469D797B9535a3B49" + } + }, + { + "publicKey": "0x804d5c119f76bdc263b9794062433607712792f8c34b331746a17b4433ad492a54f81f10a07bb5fcf45ce88c35c695e8", + "publicKeyHash": "0x07a13c1aa6859e5e43f379f2f5dbf21feaba56cc9b07eccc9c62d099eb360c73", + "index": 2009356, + "exited": true, + "operatorIds": [1926, 1927, 1928, 1929], + "sharesData": "0x933f29050bf106aab9da31fa889a8544eac9a963ef7a71a22020b4413d4f533d9adba9a18e5ca12d1e88cd1bfc08c9931594ecd835e7b90e58f71d2f963f21fb2d45ee3253c2eb79e4ea4e825341db8d829a4a1e5bc7394197a9024aba54ebdab88acd364e84bd6f6d3a73815d9675ba37a042597b7fd3bda92ff9f298274a6fcbeac7bb9c0fad38761a75ca7c200a03b75dd0a1c145824f2836a5e1aeaebb630188749f4433a4ed3c93c262bcb0fd62396534eb54d1a690de2173d931c758f2b04efc5183f06758fac66e2706f9582fa5367b89ee90b4fefb12a59bc6cf5d6e94e9c5357126e885bf0d99b6c3533de58f809bcb2c5331e16403dfdb9802735fa3cd96d27ce93170184eefa2d1dce9b61848db399f47a2b2a1298812aa65f0d2320d2053fd90e970bd3d7dc1b673ba2aeb95ab6db63df8e196a68e9eb33c3a8bd88e77a863ffe8ff8d11a22b7daef4765dd5098bf1fc956fab4b884d66340d1767bd1d440f59b918dfa16a9034d3e4502e8adb1ee5a063ef1595e0a1d44df6c63912df72740bc887cb638c0415d2cecd4ea48fd63444c13239e6078f0ed570101a2a96ad97844cee56bf8dc203894f7fbecab3cdfd9bf38c3899ef84ebcfb343a0ea6a5960fca60800f60e909c651702bd2cafacf4655ea36ff4031aae25b9dca115aba027f9a65b8206b51a157653eb5c7bb632bbe00d21561c2cc20061bd406591ca648daf4578cd0ceb73c2a2bfce0f4dc8a38132e1c1b73fb9abc8f4efc826c2142d8275cb73debd25296af1bfc07d4a7457045f2415ff50ba8873f1b1100c9b6c82615a5dae2ebf51cef0fca8a6da2a8470bd03ff9073f304711036a54bce45d3a55a083e572305b15de2f117e14bd85927ca1dec7c0cfa889cec78ee3f932ff535eca8e39eb5f117917bdce220e253476f3b3b8118653765f5804c4969be66e9236a9c27a69481991aaccfa7c41d54978a4085cb864e3ffe7424418a0b76204ef4dd032e944d43f7557ec151f7a6eafa4fe632f05b670750c3b58e5a3a83647bd85fcf1c526e6a86bbb84be2acd908d08c93be283007c4ad712ca03953294445f8b07c636cf0fab75eed76d711ee481cbfa939477514769bb4aabd2737d237e017494dcce9fdcb18d5a24e1c01e049c55c46cdf2fdcef21d7b285f25b27d6d3569b0d6cee8d9127690db8aea3104ed32570372117c9f7926460bf917d700f894ac7b98ee09b4df2c8450345c75498341b3f9c732c33b83d205a80cb95f0ada1e6f5cb18a5c1410086b600840872d2ad9cf8319077be380fb3351156e7b0a9156f3328103c28e985ec533091f56ad9d9e16ffc81b7924bdb22a562d0648399a02ec2c4d83643564fe80b75325bb3834f7bfbc3fc67fef03e14b1f92f8be8efc9027efd936377f1ff03aa0559b403375cdd071d23054d3fa020eb8c2141d861456760b93cb57bc3731b1caa08748bee280a9eb7dae98e71d8d6583821271439ee69cc32d00ad131f7e35606db60b6b434e91d15e5651af28a2a58c26b6e4b5ceceae811481479c4e211e42e0b423f7188b9dbe6d4495a644fac0d3f101726835195f6108720520ebf5f579e0873d42e10b0efdc7435d4a12ff63745e143bcbd4db7024fe762c2593ad9a370709ab7f2ced1501951f49071ce008127571fad140ae1af67402d47a019132f2586b1cfc39e083ef091a8907cb30872a45b2a6a4ae61378d2e635f7076f99e00a6f929d73fd58919f6b9e5998df6d68e5f7b25845d979599f519cd1c35e0e5731ceb9f2b28175f8bb635449221f124c720e1db2e637f4fd99e93d9e46c8e4a3b256fb3529f06c4101bcdd2407e37978e7caea9", + "signature": "0xa922c09f964296fd30c9d7d5585536ae5d5903b92e106742acbf8f0211c209b61c78ef36d0d50604c754e58b023d693818c5dbee624918c7c1dfc4065b5c05de9d664ea2d205ab6843d81fe37060f6265e0127e4d513b5d04ccd24302d031124", + "validatorProof": { + "bytes": "0x020000000000000000000000840081c97256d553a8f234d469d797b9535a3b4919327cb9763c96e00332bde93bdbb1032c4b796dda73e515c8c5f7ede9a419be8906a19d279415f312a7832b3be3ce1adfc764ef6fdeb447c4506dd60582b10d24d81ccafb215a342a5b6641dbaa4d7eed133444a10c5f3a5f6870c7ee2983b40e953a318136f0018fadd776073029168c230adc79fc01a26fb14c2988829fd5053d1cb483ab3f2dfbdcaf072feab5f042c1c6559a074f1c56f3f30d81831fb082aeb9d255f9dd92d2dbf48e5fac6f8ebc9da5f530ae680b1dcdb93a44af4aeecaf377f9cce7a6ac1811ca8e98e040d2d9d30cf7ddb49fdcfbf441cce6f8c6c61bc46634c51228800a6a6f2364473158dae3fceb5ceaff45e2e9817b330dbc9def601f2323cb4ac912deec4a8656a4e0d846fc8f5d18a7944e0c3e1470c5c95529a9bc537c7ac5c18015e6d2dc1559ad2462f2c0ca22b15bc2754f6839c114014a7eb1414986a6c3c9a165f97ba0cfe7f1fb1aed75458410261bac167a5157ed4c184f5eeb792fdaa112ddf29db4b8fc42580698babe53b891734feb7a0ad43517b779b61ced3e3ef9aa596b3d8af7e784d5596612b1ed8f28ebb552a1e8064c07cd3faa083fa72917ec2996f29286e0271e10bb7578bf6eb5cf401328439d8ce6d8ab34cdc9aa46e1b15c11d7c953d70093fa0d0ea423eb992f6fe33067c2fa44e63a8d2177cffdb23a6e0d66cbbc1606709591a0305d7e0997c281c844102f807a05e8265d7fb12b34ccf5cf0e988e6ef1ad6a39098fc5bf04099e8dd51c66396f2e5ec8e570ca58a3ba4f78aa8c9b22614b299571cb32fb49b9b5506cca408fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb48135f37c0a5f02c0cd358938dc3c24eb9d0158283fc328ce0f5176c040bdb043d7f638fde7f712618272899e5f6b465b016f1031b4a5710c32df7b4b451cccce2f559cd1a5000ecab92f2f9407e3ac6cbae7166e187688f56ea62c72d59096c5ce5e37b4d4a8bf060fbb61e86e16ea54068bc9c4aabd4273d4d76bf1504d0d18a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fa8e01e0000000000000000000000000000000000000000000000000000000000c6341f00000000000000000000000000000000000000000000000000000000006d833de4787b4b14e2cf638da50c1707c9ff3cb0dc7a45a2660b4d3458c7efdfb6c721321dfb0e529649f74be64a31bbb7d70d337f37a56b020ecc32893d1dec899f8e0893bc267e3256c7542c6cdead589454432274d6bfdfa33e5a4372c0408332dd055e96fbddab4824219c99c6efd32f62a94680d7d7fd9a209136498a235d9eae31ca1126c31a097992d1aa46e4959dac388d050aa45b9ba26dd04c29d71e55542dbbada164392f6f61345af3b028848382633277ad75a17af787f75b843767942c6c4d511c58ce23665f710e5206efbcb15a207e9f7bc0bb86306f7409c256512691d51102f5d48c78b0bee220af931edde3dd49847fd7675ad6ab7fec", + "root": "0xa9c9076088f25265776a2839c0e3d798d8f41d0605483573ddebfa037631f8fb", + "nextBlockTimestamp": 1753248035 + }, + "depositProof": { + "firstPendingDeposit": { + "slot": 17392000, + "validatorIndex": 2009398, + "pubKeyHash": "0xfd6d87df18664fc8502ad7113ce4d1d5651c80d1c6f5b96b2026bf39edc33c72", + "proof": "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b537e6d2c655de7f6dd65ba894efb46bdfdea66c7945fca8b82d43cf520472ba8e3b3ed1c078c99f277f0e0fb0c8f7b6675489a3529c2727515680c4d067d4e66dce8a6512dcf90ebdf46c3748ab7e1cd56a9256a92665942388529f5e55714a45a00f7d7d75cd2cacc059cf2e33189abea0b3a166f276f5fc2a1882210b8d2f4399af547b650a698a1dd6fbfe96098cec475763d4df822997b9df4988567622c30e696ed85169c765c8516a578bef1a6a76355fe1b69261bc23d6b9317c532465c32fc66e87fb6137199e6e8bd114d25b2bdd558ae49ef674736596cc9fb50abd23a0bb6cd6037708ab3cb3062ff6f564d0045c07f096c44a895f8c150f0b279577bba7352123f185d904caf99158b5242a838597af2277b5cda7b8158335ee53b8ad3b0eb69fd376f0b8730beb6f3fc3df7d299fe07331389d2d2fae5522290a1fc6d44044640271760a6233e12f035ac2849bb2b48a6d3cae812fce0f001e41225ea60acc3c831473a54b2081c870e1541d2a11f09ef1b6e18b6bcfd1e2d9618d8b76403bbbb09b98df590afa3604cb482f48be821f6ef40c8c9467780171d0c3670df163a0a51c51e3f15847f81d4e4e1a352253675809990849fc88c68c43e812a1831cff36467ea631ade34737f335bdaae3d357fa931c4e16cee8fb7fcb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765922d000000000000000000000000000000000000000000000000000000000000614afa81cc0da581c523acbdcb80d56feb3d3067ed08ecdb00c7d299a01652af41d664a28b19e07ab920b5cd69356433ccf59050275da48f49a4e8e8e0e38dad1779916788e4f8513520905e252f91c15398e36eef7c7f11f6c1217d115293aec78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123cd80c7b10cd59242208ec5fa794e82e1985482efece7b3e17425b6d245b3e4ab7dcaed0462ac2080f6975dc488695a93b291acd0d8867729d7fb8d1f9edd2294b25ecf14032953e717695d1e931a86d8df417f5bccb0f7bb9b7cbaeabf1313b1859b6a9617f943009a3467e442c8ca19155484272965ea63142c34a69b94ef653" + }, + "strategyValidator": { + "withdrawableEpoch": "18446744073709551615", + "withdrawableEpochProof": "0xffffffffffffffff000000000000000000000000000000000000000000000000e40d9d1e498d91029fcbfcc9379dd56b5e4df3505578942b70d168c603e0d3aefe49924e38f7713831b2f3e635dabd75bf125f47ac687ff54196f38378a2f4ba24d81ccafb215a342a5b6641dbaa4d7eed133444a10c5f3a5f6870c7ee2983b40e953a318136f0018fadd776073029168c230adc79fc01a26fb14c2988829fd5053d1cb483ab3f2dfbdcaf072feab5f042c1c6559a074f1c56f3f30d81831fb082aeb9d255f9dd92d2dbf48e5fac6f8ebc9da5f530ae680b1dcdb93a44af4aee105dd53fcfe4fe06c9f2ee5f095b62e859e9fd31fa0fd27a49678faec46c18cfcff940aa66a25b5ef2151856017069ad72fba934adefb140605c223ae00e9ee7d88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa187eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c64e9f49f1152e6e888b2126dc3a7fac0f56ac89a86c528ad7be952fa6e635456506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1ffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b98f6925971e272591ecd848c19521e8389dfa28501612834fa4687dcfb59d471b7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fbc757184526a56573d966b1627b2401a14c47c73c38c424621b71b0c75613923b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da729378447af995c7083d85602bc47421c531c4ad86d74366e45e48a1a9929b682dccb568fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beba43b9107f1e05442bd1e6138211e808848736ee9b6c376564a6bd8aef5c38408273cb207ef2e6213cb4654d093d70a1fa82021ccdbef57d77243db82f427508f9addb7ed4a1733af7799c4622471264832127e60ec23d94fb4539d6bde37121fbd8aea08aa723ba9167256dd8f7f6ffeeac6ffa767d319ed6229425c929e60288a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206f36a91e0000000000000000000000000000000000000000000000000000000000c6341f0000000000000000000000000000000000000000000000000000000000def4eb4b71a506338308c2e75905df7922ed530bec72280078745d821e4175eda0d2292a658765820e0cbffac3871b34e5c94b6eb84268f2caf6c0e4563b2cd6dbe54c5601d180b06d08438c7695a5fe92310da2f33a7ba8bbd3d864578ccea5064ff03af3839290128837cac6211e54ffd1c6461674e2a42c137ea2fafb6b9166a2ff8b6ee53db26b2e76066975fc51e9acb783477f727bd7578827289446c3dcaed0462ac2080f6975dc488695a93b291acd0d8867729d7fb8d1f9edd2294b25ecf14032953e717695d1e931a86d8df417f5bccb0f7bb9b7cbaeabf1313b1859b6a9617f943009a3467e442c8ca19155484272965ea63142c34a69b94ef653" + }, + "processedBeaconBlockRoot": "0x2a63633749ff66c52fafff39201fa23924d2117cdfcdeeebdd3e949d72cbf78c", + "validatorBeaconBlockRoot": "0x6a868487a3813c98a06236183fca43ca95d6462afafe7c561565d508536be38d", + "depositAmount": 32, + "depositDataRoot": "0x38c2d4cb12e44908cd0b4abe03a64a50555cc3ef0b1f2a9a03d784a625791bbd", + "depositBlockNumber": 22876482, + "depositSlot": 12098163, + "depositProcessedSlot": 12136000, + "processedBlockNumber": 22914093, + "command": "npx hardhat verifyDeposit --slot 12136000 --index 2009356 --id 1 --test true --network mainnet" + }, + "comments": { + "register tx": "0x0c787be8606fedd512f7ce2a8e12f8957f681aef3b397bac1f2145b63f070be7", + "deposit tx": "0xe0da9236c1f121d3a927c2d744b006d642b9a4a47c4af5176f2063e9493df386", + "comment": "npx hardhat verifyValidator --index 2009356 --slot 12202000 --network mainnet --dryrun true --withdrawal 0x840081c97256d553A8F234D469D797B9535a3B49" + } + }, + { + "publicKey": "0x984ab222044148ebeeba72acc93d2db8969eff8e340d1ece57a3d9eeacb59cc68e4cd6cf0628f51dbe912877b3263d13", + "publicKeyHash": "0x7d16294519e5e8a1abb7b3e7194cf4ce74eef2702635cb0deb6d5287f9e0bd94", + "index": 1998140, + "exited": true, + "operatorIds": [1926, 1927, 1928, 1929], + "sharesData": "0xb9ade8d5269a26eefe6a019214d90c5024d340ac85fe8c9885e6b4c1e8bf2c5aff3aa6ec69fed40cc311e6cdc20603f212393dca2a1e138da533bce3f18cbc34308c8423944401d7e808ff28e3f9cfffeab0c3bb4c776ca054ec3ab371faea5d8d04ecf9f84bf29e99bb93736125e6ce5dc549ebce2c98906846502db8a65f846e4783e95885398bf499288e4953943f9781bece6974e482298c98a98807b64a4a44dac488fa98b2f7f2aef13d3510e4f97795882327c8a8a55b0b1fb0c0a1a1a0cd36b8ca8fef418aa72afd328623ed34f8f7edda0877a85063571350be3ea47882bf1c8c55098f9fa87acf51e74d88aad88103c42789ae75fdeff6b0e6e8d52a18feede87f5dec22f3d1c832141ee4110d0a589a411ba524aed206ac644101acc2135b19745888d93c5b8d57fc98e8c7952a041ccadc22dbfe9a2f45886ab79ca3d291ae37dfafd24bb729afd23ae1fa1f0b442b0d6d9cdfc92d2491149d0672adf08189b01a73ec6eba718f6dc39c3813647157ee260e81cde106111be0fc142ddcda06150ad4fe14aa0e0631d508257f985edb69589caa2705b1fc04c6653539693f84aa4644482430c46ea927460a57b4439d0d712105c27dd9de3e31f14191ef7fff7715c9542163534ecc07f1c8c8aa25efe63ddb580d0406b28ddb61d179e021684aacbbc67117c62d9536177579c5dd0be432f4f4172bb79aea3752255ec0ac30db58c3d9e3c7839d0154116a285940a2e6f688c4c071acbdd3822e467b5860ac76527cd8b87b023f963fb51f5793bc01ecf1ee55d60b626ddeae483dfe13f3529171e6b2294c0ddc9e7dbc3ffff09c189ea107d95bea5366b52c83fecaf46254d14d103a807cbb3377ba5e7bd5620408762558e34a18d69ae26293bf94851d287d36c74ab25febe66e8caef4887a1897c25978bc36ef07d421602367dbaf35f6c0d4ebd8f423636567904af87c287371cd6bd76ca10b982998caf31ff6d87278b666cfa001a25b9d4db5094609cac35a1e7deb4e8e711a43fabf97e83fe301f03b10e51185e22af6e437f1e00f32d148a46c35f6df07c7ffaf5cecef46a313494eb0e9601323066942dac19aad4f283eb1c6ac63afc120aa86e77e13bc532297f71b9cfcaf672012c51d6ca30d7c1de6af74c4cd4afa0160f96b3f3335fc18ab02e30bbb8b4ec6ff09c9e2f51af665dffa0fca46fcc60ccec661a10033f1760cce58c7c66cd838f9a39f986e62427eab8e2e15183d35160ee86d2a7a0a8bf2b021bdcfb4637d8135e9afdc20b9679ed78ac7c081ce4b45dccb1ade96fe0ff64f101bd7ff9ea6ac61e0046bc0760d33c19c37daa69ce3024cceb21268413988cfc20c9e5e515d792989f4cfb9707a39e17380f2bf48ba211bba21a18ea6ff3829fb488dd7ce5ab5e21f251e75fadfb6b95a428f689e6bc5e10a441c4a4d2460cc6f549be26456832813ec2b19d55a1e064cdb32a1e8d614911d2bd449ed21faffe49576eb9bf8e1082291cb696c8df180f19a00d1b0e35a919323840f56e684b9ef698c7c30653c18fd104c7ac00b2594a4b1e70898a5f9cb9a8f6b76fe26d24e7d7c9dafa5465216ef48507070ac86341ee8525b2ad224057adc1a1427150ce9ba02cbf75d106c089abf6de4c15b33e4dfdab9f364f1cd449e0783d02f7d3c8f90c4837770688d44cbd3c2ec6ff60a29b59118357ef535c3ac90dd5f415961a6e0adcd45b64452c6ff4dd3421942914d770b84451306b1ef0bfec271d84f9d273226249781916da276de926ef74befe3f3b6416ebc67356c5c96ee87d6dbf9564e7a1e21197190d8f4815eeaeff663eecfb7ea74d71dc1ec4dda7c", + "signature": "0xb21194278baa87dc3fc6ba78032cd805f4a410297812fd285b92bbce77e04f9a7858ab3f8dab11ba40e5bfcacb41f6a00fbd48ed191297e25e6e7ee28b72427db521dbcf82423ae351b3212c1bc812362802c52843fc781be7365831b512cc5b", + "validatorProof": { + "bytes": "0x020000000000000000000000840081c97256d553a8f234d469d797b9535a3b49f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4be497ec865c946695a0071f4ef056580e081c3bfd26d0639a46bb43e6f46d9a2236e587d30ce9d4b06b0562e3fcd75be0fd6d381d462cd62785ae1bece806af4ca38b5cf5a29dc662eb89fc190b030df89d6eb1b09213a9654009be4b89387b98d370bee20c352957231c4523c98da87c3fe72edbca0983684c9e2cde71e2f0125e9b0423a6d616d6d9e17f236fbacf04237d43f0dadf3a24c9abcd2f5fcf5c2698477de17a2d1903c4128b67fa8524d211b3d90cec8597a3db3a8193630ceabe225c3416f83857b41331af7f193efc05d89490fa589ca8949fad4d2121ab5f5c5f187fe9da83f571f62f58c8ea6e4f30e7697ed0e4bda6b97ace8e046af58cf4b88a53864f79ea7bffeab7f223662fee286a83d4e8681aca4ee57774bf9084ccb1ce5d0848b53a824b772753655cdd1abd40ca48273dc2e7b2676cc762ff3941004fcf71e981a6440aab9ce203868ea5151293aadef58aab63a87ed5b208a860f625807aca6ff1b017fa9c797856150a79c61119ab5f3617bd9cf90d850d4c2c17453901f5db9550fd5ba899337018fb2a3b2905eeacdcd4fa331ec18e77891247807a3e720a0dbafafc9482010c1471ef6701c4734987824eb507022b4ca8403c53330809e1ea3ea7a35d8138ae7bb9566c9e8639657f6a4f1aefdc0e3220968d13fc177ebca3a6521024a5ffe656b79194932d7975fa20ce7e2f358e939c92f7138ae2a6ffa12d7e7d8f452ca1dec5cb87dd1dc2bebfc3576397afaa63aea88fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb48135f37c0a5f02c0cd358938dc3c24eb9d0158283fc328ce0f5176c040bdb043d7f638fde7f712618272899e5f6b465b016f1031b4a5710c32df7b4b451cccce2f559cd1a5000ecab92f2f9407e3ac6cbae7166e187688f56ea62c72d59096c5ce5e37b4d4a8bf060fbb61e86e16ea54068bc9c4aabd4273d4d76bf1504d0d18a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fa8e01e0000000000000000000000000000000000000000000000000000000000c6341f00000000000000000000000000000000000000000000000000000000006d833de4787b4b14e2cf638da50c1707c9ff3cb0dc7a45a2660b4d3458c7efdfb6c721321dfb0e529649f74be64a31bbb7d70d337f37a56b020ecc32893d1dec899f8e0893bc267e3256c7542c6cdead589454432274d6bfdfa33e5a4372c0408332dd055e96fbddab4824219c99c6efd32f62a94680d7d7fd9a209136498a235d9eae31ca1126c31a097992d1aa46e4959dac388d050aa45b9ba26dd04c29d71e55542dbbada164392f6f61345af3b028848382633277ad75a17af787f75b843767942c6c4d511c58ce23665f710e5206efbcb15a207e9f7bc0bb86306f7409c256512691d51102f5d48c78b0bee220af931edde3dd49847fd7675ad6ab7fec", + "root": "0xcc6f4d8d8fa4327eff0e09ac287a9a598b95606e3eab0d7f7bbcbcc41190212f", + "nextBlockTimestamp": 1753248035 + }, + "depositProof": { + "firstPendingDeposit": { + "slot": 17338650, + "validatorIndex": 1998214, + "pubKeyHash": "0xe9d8ba17b2260095af564386a8a4e7b2fb93bf3030fa772cf3b2b01a2a8b89f9", + "proof": "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4bdaf14f77715d6e56614d499ce1b7eb17e6aeea04190664c10c11aa75f056168a48f4a19b5efc7de13aa949524e947d9d2a248a7a18118f7e1853da71988e648edcc26a21fd60f5517afb545e318fe42c675fb86be11e6dc5897583230cdeac2aa613942899786e98db65cec7fe974792e23083e8c0733dbe8cffa69a2a39bab469fb9af611e2fe8580bafc980ca334843c8f379ee17b3e18b5eaafcb66c06d7c8fd1df3396a7dfd4ca44aead80f2a8e439a2d48d8fd3873f4a8f49d8f0940ede4232965a91af9b6183f4bbd77d47b3bb9142d46c9a053a3382ec030a558de382a84b6a97857fec32e33e3a339bd8ce6dac661c67fb157c3c1fd4aa4a99fa03198d5117a46ec13c33febae1ce820f866ae01d95383cc0239e415e59d41f1a8e5ffe4792bc731fed1f56fef81c6e8285a1ba56bc143c8c6f4bfd994f23205fcac6b2df1e969af913713109fc3ed4acc98053f3e2397e79b31eb6bdb6346538956b0f1174ac3b3dc476bd54e392d81d111b094b70d25de288ca79c2260510c8cd0d5908762a893880cb54086969f448b7aeb6c3e2e7261a69c7cde59f2ba5c1adca2879407e15bf6d154519790eb49405d290e79a0af4585f927a5429a92215d5a4df6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467650214000000000000000000000000000000000000000000000000000000000000c18656485e58bbc42839ddada9c2bb7257fdc18c9789cd1c81b43c0473bfc987d47201b6700bd4a1b3fbcd811ddd386c723de6acfae337638db016a813ba6f0ccc1dffc3e6cf20869ce3ad12350dc8401574f7f88e097bfe12e5176e0a17e968c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c10fbdc0246b198e44c9729724854cda3dd408c942d7731f505f23ae5604ac2c3ffca1a00e3fa290c5dd5890c152b686b9873a75a30664159c8238285bbf510c4cc1222dd5144ea086480e11aea4b3b835189c72aebba8430f7918134760ba54d9713b6564eaa3c6da5c87ab8c5f242e4e2f410b8a9275c8f2af51b3f6e509dcf" + }, + "strategyValidator": { + "withdrawableEpoch": "18446744073709551615", + "withdrawableEpochProof": "0xffffffffffffffff0000000000000000000000000000000000000000000000008e753060f2f8b81ae780ace0c3be1f9ad57e4e1207f8afe1ff783acaa7b5501148a4309aadb60f539a736396ea6864d0cec795bc08c787fd95777ae6107d2d5736e587d30ce9d4b06b0562e3fcd75be0fd6d381d462cd62785ae1bece806af4ca38b5cf5a29dc662eb89fc190b030df89d6eb1b09213a9654009be4b89387b98d370bee20c352957231c4523c98da87c3fe72edbca0983684c9e2cde71e2f0125e9b0423a6d616d6d9e17f236fbacf04237d43f0dadf3a24c9abcd2f5fcf5c2698477de17a2d1903c4128b67fa8524d211b3d90cec8597a3db3a8193630ceabe225c3416f83857b41331af7f193efc05d89490fa589ca8949fad4d2121ab5f5cb5e611021ed3459eb0bf33944f52470b0dfcb58d9cb3df5369c1225ec67a6c3728279afde2a6b724f042cdbd31be3334bc0015f1f44e48e9a3834459f6bd71770b4ea78779a31f67acd4cbd1f856459103e6c644d1d93c3fed9d96127248339d506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e110608f38f8cb6e697fa28c3bcbd3a636a941561bdb912bf79438a4d1bcfc3c2f6f9717ccec81d352e5ac9fb7820081f934f972f606361d12e47e708aab340c5e809edf7ccc9be4f744514838ea53a177c0e117d8e6e65659295ac89539bf17196d5e184f0e63555a33e9a53a31b52c440c554ba0acd1bce1b5198a1e497d21fd7857d8c1e9fd1d4b17d5c7311a8916c631bf9476e3f006fef4e6d141057e6169d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb831d6daff4f55eae42bfd2d72a13594e3bd04e5bf1332c82df1211130742169e2e06eccfe4c25137ed15fc3312c2212d0425fbb84ab666ef065574389716be0b4fb89a69aab951efb83a6be699ef882d81ef5c5a0d7fcc82a8bab6fb2caff6c698c2712b8c430700d5e145ceb6f66454710c8ea4e99ecbb993a6ac98ec325f678a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206f867d1e0000000000000000000000000000000000000000000000000000000000c6341f00000000000000000000000000000000000000000000000000000000005b8d274a2a37b859c947d679fe38e459afdfecd81439aa5c5f13e442a5e897b0fe033d95d27906f3b4e7438688f6e30ed49c25543e65ed8a13952acd1c75b95f67d81896b5c95d6207d37fc6143100d50e2b616a9b1bed1c2df664b7d848c74fb0c29e6a4eb797ff96ac3300f08433042d563f543200aac8ed39c2ad17d09ba1343c81f5529b4c7e6f57ad4c7ad76e3461a2317cc4784065f79358f71e9c8547ffca1a00e3fa290c5dd5890c152b686b9873a75a30664159c8238285bbf510c4cc1222dd5144ea086480e11aea4b3b835189c72aebba8430f7918134760ba54d9713b6564eaa3c6da5c87ab8c5f242e4e2f410b8a9275c8f2af51b3f6e509dcf" + }, + "processedBeaconBlockRoot": "0x397f3445614df2225fb1bd7fb45ed68a4e6abd50210e4ffd8d0ed3a8447d74a6", + "validatorBeaconBlockRoot": "0xbd5e4d024576a32e2c35d263937f105a117cc0a54241c90442206df390cef216", + "depositAmount": 32, + "depositDataRoot": "0x5716cdb914e4a550245b6f517479431d4b9d19eb88e4a498052287825db50729", + "depositBlockNumber": 22835210, + "depositSlot": 12056630, + "depositProcessedSlot": 12082650, + "processedBlockNumber": 22861065, + "command": "npx hardhat verifyDeposit --slot 12082650 --index 1998140 --id 1 --test true --network mainnet" + }, + "comments": { + "register tx": "0x0fa6e09b0dfef4aa098bf82e8f3b6c8d9546369d828343781b1b7d8c14b4c75c", + "deposit tx": "0xb95c9f19d50a1a5c541d4347ecebaa74e05d57a9c1f902f0ca0c69cce10699a2", + "Proof from beacon chain slot": 12202000, + "comment": "npx hardhat verifyValidator --index 1998140 --slot 12202000 --network mainnet --dryrun true --withdrawal 0x840081c97256d553A8F234D469D797B9535a3B49" + } + }, + { + "publicKey": "0x966c17b77d7d92f2d6f0700212aabebb5ea65eeb96505ceac01c969fecdfa4db41f3bb5556e6e9b806595658a901fe3c", + "publicKeyHash": "0xa7fbfc743f37bdedf24fc528daa5d5cfc19a87bd12b34bbe4c6ee87af6daa65c", + "index": 1906722, + "exited": true, + "operatorIds": [1032, 1033, 1034, 1035], + "sharesData": "0x80f90e8e7a801d2555eda35eb2235a013a2db6c68f76bd0adb26bfeccde0913a706e2406586ce5ab2236bad4f9cb434c072b0de4df29d36718e2e40e0159e5e828839ee8456bba704a9880410b1412a94b3790e765186fc0bc325059d13803ff830b525b672bb578c051e2f3c20191251793c7237a6dee101a8a83efd1cb874fdcb926cc32ea237a25c8e55dd24babd2a75e26cfb1d813af8c3503152a6b5457e1e2b1ad108325059e3ca0258325704735b4e1233ae9252afa0a01a156066eb9957265d2df26646c8657cfee8224d811d8372b31ccf7f1e1ce377a9a26d866b0429bb2e703fc6d12a6a168e02d2809cb846a2af8d774a22470469a388aeeb26fcaae99cb2822d8732595c57363b4c5eab7afd230bc535d1c8880c39da08761f7a0c50e27e3b474e63707cb41c86de3e723edd5d854a087d40cd7cb1387df63652ee319b832393a26327f963f317a3dc52c4a96c255ffd6388495496c46209dd2a7559a4d919cba2fdf6a82739423a6604a32a99ceb5289d86fd4ad343399ae778b634af44469bc6566c7ab94ebe55d8ec9980248ed15a583586c09eadc1e720df7bfe918727c4aa87b86ae9df718cc7cf07d692587d592017855ee4f2a9f472a81167692c4a02cb8dfc5ed027c6dfba3077e6072b3ec3449c649660b9193b2f9882b1427cbd2ea74fb7ee575f35c4bd6545bd8e6e36e4e9448635830a8600a3999b997609931a8212e40ef32b93502552d81c1b1031e84a092ea0396dc013ad94cabeabe6c28476d7f87a5228e6a25a3d3ee40682774e4942f30069341ef0563ca26fddb78f1c8e7dd7c1f86bb969e978b513fa579fc37c04d6caf30ba6db707d89701520aeaab206054de92f360493896136bd0cc6f0faea3820c16d31ed33853bfff266459e40a969acf1c00b457b1efe2857f04581f735b4fb0668367de76fe73713e2cfe1eb67e12c51d0fbe68ec173a46a10d4bbdecbbf4f5fa6f4fbdb5e9344a1d424032dd7ddb3db46cde6356de9ee3c32fce9256a89d3bcbe09de44a2ecd2641036867e3941e1899c67207fcc5b5f82fc5197475a7719ae668769946309b0a262d5ad81ac236536410145ae078ba80a35122a046550cb424b868301a885c1cb70906a4ab93961b28f0b90029e1207158b60915fa5a0c581253d6b6a1c678f0a3f31689d7bb37daefc78e1b8687ae750bdb77b7d9556e9cf9d40a9c0ab0c29b2aff3e7af885bc5079860b7549164ba5cef2d0f5a096ad857742b094033ed366c81f005a141b24097b0e73f02e466b3b778c61e3fa56c14e97d8e90bdd3d2dd57880aae88c73f72792e2f69148164e151785265a59aa869471159fa8511fa23c55bda78727e05dde0860e94028e4ae195f2d873e919805304e2de165dd7894be6de3687ff5424519596454a87f37f9cf2e6f85bfb2531303c2ea3006c4b5564ddcf38bf1a2e82e276ce546aa2ff7edcadd1724fb90c9685494a31c45642a7e6e66f41b8fd4998b3784b53332858ede2b66222b4f08226ffcd882f3ee2e9cbc797f1f3f4c8f0263715dc0797230fbf3938b2505525d99a9173f743b7429fd6934aa9290d1d3c210d5d4d614bb6f6093aab04852e32a4669b30d7e003d0cd99256723758708606864c68867deefa317ff2a7cb774993cf316d8e6bfaff86ffbb34de7b5cd966d48cd366d0b84ded8b01cbbc74e5826cbc49cb6a819fdb93e9a919e7bd26789a9ec8e5160b01a46a95dabae2ca9c74e0500645407c9bd164e41b3241880f8c93a6933284bc87638763dc589007c45c7d3d56e7b8f7d23154ce32b892871e6b9cdf7b5641625d2bdad6dd4d8a265b3619a5e55e318fb334c5", + "signature": "0x88289c0883287f7ee3b8394f1054ad0b8a08c2be2a90b37aed227e5921ca5f415ee2bfd510ccc897cae280bc438fe1911712f0cf7378ee45dd9b9576ee1218baa358c9d5d73dadc50509471c18a2192411e90eefc43bd24098a07c6e5305d953", + "validatorProof": { + "bytes": "0x020000000000000000000000840081c97256d553a8f234d469d797b9535a3b49f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b1d729b86e7b242fee69aaee239b09949e8078aa277aa5601291696e718f26c275c539fc215ddfb1aae8eb7bf4df0dc5c2e4b9a23c49a2107bbb9def298b9b32961842cc0f839acda451823f447ce9f4158afbe5dd0fde532816821b4dfd162e85ab8a9d341aee57da1c455a57ce11a7bc775237d6526be38f3e4a8327c9be583f40f3b115bf303e51ea29dcef8a70848a8cfbdc3ab3b51d654d9b7c4a1822f4f6dcc89c27a2126bba31f34b8a6232a8bdee644395cbd51359ad7427eadd82df8a7587443e47482dc63ab0424e3e76ba6e75a96770efb29408a37549bdae7f235de007409eb57c450e4a6d094851828248b643fea37c8c2c7ca2722cc9ab4b5db46ab7c4613ad4366f6c89a5edd17ed6e2f3166b02a5be973a9822f441d4ad98043c1dc1c5df770f5b317a3f46e3197a8b0bfaa807453cd35639eb93fc9102a9bf6f6e8941ba7ed76d08a6cf25db94a263fd1fd41e72766e4728ee41939efb03c912efb5c038ccd2cf8037a532fb6f067dc33d5c283c5f89c741b3d761408b891651016de9ac1cf5c8abfa145cc01c01b5440b4d5245bab5031834a2be57b0b565660eb3971ecf12bed5d4c10b3d200ee0b1e85228d62b37ed4edd4f76c2698f18c797738207ebb7487bc7c92f1e6efc29234317a07502d81652ddbe6361497d02030d3fa106e3a8dc286dcba72227bd702e461915cd928ca69432674300a23f7291d342ad0457de9916ef31cc38815375e6aa1c2b4940cd733873f2533a744ba859478df1c06b148271e8d12ef6a460c9ca26a636ba9b442cd141678890012106c44593d627de4af4487a52d4f449a373b225d7bd98193ee82ef6510cde5872d3d7f638fde7f712618272899e5f6b465b016f1031b4a5710c32df7b4b451cccce2f559cd1a5000ecab92f2f9407e3ac6cbae7166e187688f56ea62c72d59096c5ce5e37b4d4a8bf060fbb61e86e16ea54068bc9c4aabd4273d4d76bf1504d0d18a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fa8e01e0000000000000000000000000000000000000000000000000000000000c6341f00000000000000000000000000000000000000000000000000000000006d833de4787b4b14e2cf638da50c1707c9ff3cb0dc7a45a2660b4d3458c7efdfb6c721321dfb0e529649f74be64a31bbb7d70d337f37a56b020ecc32893d1dec899f8e0893bc267e3256c7542c6cdead589454432274d6bfdfa33e5a4372c0408332dd055e96fbddab4824219c99c6efd32f62a94680d7d7fd9a209136498a235d9eae31ca1126c31a097992d1aa46e4959dac388d050aa45b9ba26dd04c29d71e55542dbbada164392f6f61345af3b028848382633277ad75a17af787f75b843767942c6c4d511c58ce23665f710e5206efbcb15a207e9f7bc0bb86306f7409c256512691d51102f5d48c78b0bee220af931edde3dd49847fd7675ad6ab7fec", + "root": "0xaed959e696d01de93608297f0f7a3d258b2b62552212c8eff9ed0f3b4f9a0531", + "nextBlockTimestamp": 1753248035 + }, + "depositProof": { + "firstPendingDeposit": { + "slot": 16924800, + "validatorIndex": 1906797, + "pubKeyHash": "0xf7246ab20653bfa6dc5292550dbe0ef43a3e0bee8d6920f022826897cad54288", + "proof": "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b877c5aeed0f309abd25ce4f329feca2b8711a9eaf52a5b9d9c50a8462d7f25f9c9f4728d3368511757a44904d2c1b35f85f1663422f931783fc28fd36635dc3227e301db7f015b0427f95e0605de039fa25938a3e19a20f1d2e12dc86cfa09df95889470af37741ce9756b47dec70749c42de23555ed2d81ca0cb57e4a0795d2f2a32d3226b848acd4bdbee2187d1b502303b2f3d2021106ce5329c07737043c69e0f66f99838bb85cbef669ba7258b7171dddb950290c25889bed04e0c5178796badabb22bd9c35c08a872ca07109179c341503cb934e3395032e0a949dcca6a1730bf65cc7e49c94dfd17b36ad339eafbe51cadb2e5411f194d90cc1a31c72956d7040496ab1503b9ae0909ad9c08743158004403565c71aa27aa032ba25f38c537df689aa72069f66e97a590e0f7d0c5f6435cfa53fb9491bc5b6b4fc5f1e2ea5506e43a262100cf0f443c4a257904f2a3f02b194dca90ef2352fef899c1f47d0aa1f7d115441e3a19d9ca63acb91caba2f5db097f10e66db3ee0fecba908f302ed8ef9414c16e748f6b425943a060d9ad10d0b9f90cea3ca88a63e176191b7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765c60d0000000000000000000000000000000000000000000000000000000000006178d955edad5cb4f0be10cba9224cf4eb3efdd9d6b19c5a40da09736c1d3716c34f0e1377ce18acb991ca7a2183cca849bb241243c90b80029ed936b6334f65afeefa372bc3dfbe24b2d9d77f04570a8eea28a65fae82b97d5811b49377bf19c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c706716d9d67f6a5de2ff2e7568525e0515ac52b8deb506f3743cf547df7da964a15366cf6eec0764be472623ba0eeaa34aaa5dcb0915e2882dab0fa89e8d8779ec6a4b7ec032ef6955c3f45b7d64b4780d4544d05e278f306fdb190a2d4397b33b77797df14767afc89d4bee94a52560d9fda6ec858bab4313ad73826fd9ef2e" + }, + "strategyValidator": { + "withdrawableEpoch": "18446744073709551615", + "withdrawableEpochProof": "0xffffffffffffffff0000000000000000000000000000000000000000000000003fb9d9aa4fc195b3b1722a33847a4ffe822b311efb18d6ff4c281bedd579ed59f48a9645646c82681bf059179a498b1d73edfc96d7e58387e9baac854ad7d8b5f37050c0569bb240017b1869d3c3fb153581812eaba8b0fa10796be84d62e9e36b27312b6df058f54d35dc75ea14edbc7f32edc9c491525351fd01c70a81337b3bdf23f5bb4cbc8ace33e43512cf931d100a7342317782a234b253138f000f8894f823e6c6bc72cf3fab420bce80854332705c406dc0e9806cd60cb7cacf078af2123a465c1efbe576f7fcbd6b5b7ff6cfa185fa49ac6bb5b37faa855b2d48cb22d035c95214be6c8186f5f4b919f02de09b3014ebb809ef7aa0715b1eee9bad4cae125198d561f18795046746cb7e362983d5d709332724c7502b75e9a66b3e87eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1ffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92bac7e77bd751b08062c7cf9bc8659aec2e148dc543bb79c45f29b390ab6270c527cf2a91306d6d5825b67a2923c12c0b9f68eda94bfcaf5064f52f72fc1fddc4edf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bbbb28eb518a4043c1bfc00cb86ef8615bc0e3eb811fafd7a7d5ab0016aa42b62f8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603abf6dbddae9efb7940260d8d7a144839f5f39e57c16688d15fcf5f83a095335aa15523976d4fc5a0c6eca3816bc73703b996776610f090ca9eb7fd74afc3e334a76e4e01421ffebceaa99fb70b014c5d9819aa97ee77cbe529cdc1b68842f811cf8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206f6d181d0000000000000000000000000000000000000000000000000000000000c6341f00000000000000000000000000000000000000000000000000000000008d8280f48451d5e42a445ba8c792aefc0524768a3837bd831d568576129d64b69ebf39f21077ab6a8d79d50e526b4a22f867d14d1bfe6e32801c53b4f648e15e7500449403da37470848b35bd08234051ea5dde1e3b0be7709b9ffba554f837cf76377a896448fd154348a660109eb0bca366bb944fa0ace4214aaedd93b32ca9648a9df558998f6c3c9d3a4b6da9a5bcde22c09dc08401813b00fa59d6f3667a15366cf6eec0764be472623ba0eeaa34aaa5dcb0915e2882dab0fa89e8d8779ec6a4b7ec032ef6955c3f45b7d64b4780d4544d05e278f306fdb190a2d4397b33b77797df14767afc89d4bee94a52560d9fda6ec858bab4313ad73826fd9ef2e" + }, + "processedBeaconBlockRoot": "0xfeb035bfb9c63c7e363c9832e73a9556acc71cbfa78b8da211e29a35d7c0804d", + "validatorBeaconBlockRoot": "0x1a335f824976fbfd0649c6dc1fa275198bc9c147721becd36571fd13d44f662b", + "depositAmount": 32.1, + "depositDataRoot": "0x06fccb128a990c5ef7301796324d5da7e2c59187ad249e179908db49a5e69987", + "depositBlockNumber": 22439571, + "depositSlot": 11657711, + "depositProcessedSlot": 11668800, + "processedBlockNumber": 22450481, + "command": "npx hardhat verifyDeposit --slot 11668800 --index 1906722 --id 1 --test true --network mainnet" + }, + "comments": { + "register tx": "0x93dee85a1f92b3f70cfaf14d08fe1631de23e8a23b4c185eef1c22482e686b4b", + "deposit tx": "0x93dee85a1f92b3f70cfaf14d08fe1631de23e8a23b4c185eef1c22482e686b4b", + "comment": "npx hardhat verifyValidator --index 1906722 --slot 12202000 --network mainnet --dryrun true --withdrawal 0x840081c97256d553A8F234D469D797B9535a3B49" + } + }, + { + "publicKey": "0xb292695a554674af552f9aefbb7df4b2bdd57101e56c5f4da509b9254e47ca0a0f54a28a2e94cd35fb969b7f0b966a97", + "publicKeyHash": "0x0aaddc930af60d2cf6fc0aa8b87f3000c4f8fdc9fbc608b7933960dca3336145", + "index": 1906721, + "exited": true, + "operatorIds": [1032, 1033, 1034, 1035], + "sharesData": "0x87e887677a3079e15c14e3cceb0e7163d95bd5bcf2a30d00489ba223929c568d5889f95ecdbbc8adfd2e7fbd23c6face0b896e66899229f16a01fd8b8805e69f9809a26d297d1c75b3be0c83e363fc251a70f0e2d5b69d3ebf47ce7f712ec0a7b1d4313c2197664d91b82d021ea312da2c85905ebf77f56f0dbe5b429a139afb3bf432af4b0a3b94a6b10b8478ad4c52903301a06e1dbd40a463b808400b784ccdae01ab6f5ad790eb6a74f88dc4b83b40ba3925105e9cdae38b9839f6b537bd8adc8d512ec387b97bf6cc087dd06eeaa29976167fdcca2b25e5ee677fc571f31491a7755d0bf4274f5a83f6788b34a1a62a6c9cefe38a1d922dc3a7f018741933f4ce8a284a39ccd4946106f2a67d6199f68cb3a2ca23d998993762f6d61d276b4fedee46a43a0baf0033e440b46ffa889e7dbf9a1d8d1e1f950f3f49d60faa4d10ca8eac114fb0f89598750888103c7030e73f69cd282992c4bbfbabaa53bb56733e68fa651a7fc0dbf0aae31b87a05f00c224dc583d0b3770c33df02b2e4cf7bd9e5220d6530412c9f2fd3d3615fb8394dd0d09633ba542a335c9942896b6d9bb6edfc1547335207c89d9c59705fa5e20f49a345ea474e5de9ca920fb868ddfd890fa768225726608fd5df70922aa2c1cfe13ad6e2c1a6841802cc3a84aca83ca7c6b05b625b5c2efb540c98c80cb0b274aa71aae65855b0d7e36a2f15d5fed3abceae541dc7ed0a13bdf888932ae5f243794877ace996a1ca45e4af25a331c7573d8e4d57abf60e2459e17957969be124671a51a960c969134cdd76663241020d4e504bb17c02c5fb6c70e17ef82df0f3ac8d157f671bba8adf90946c75b1d5e8bb3159e3694bac200350bc5f0ef466a32ac904aeb82fca2bebb3c1872958392618f61215aed79ae8738d55553bef9c7397414d261a719c291cdd90c14eef257632575c5a8c4ac374d7073a687e40babee14617eabd0dc3965cc9ac07c1d928683fef61dd350763bea17e9d2172b9a5242cac28ca4a875279bdc87a1fcd1646d7a9b74b6992a8d359831eafa9ede26ac8d514e00cc5f26496304ab0da05453698b47ad37414f0e4b2d20e6db0239497f0dbbadcfdd32a222a50f45a521924712b93bae0ce388ef806e5eb060ef8090fb56db8dc58abf9f591c1226ca67985758009f8b2aab2117e96d5bbbad723218dc801b579e017b179ae3993945a39be71d9dc52564c6cec1319172529b9c852813d72db52358e5617542202dd01d86b89ed049350db07468e3f18507701031dca9caebed7a95acf4950f82d14b7c57733f8fa247c2bfb622e31d2bdb5f8e455b6c330c09f1cf15e1d19132ee2e21fb0b7770e13fa91803b5cf3eff39b3250c4db89ed77e8f703cfd7fa8b667c32cdb53587e893e43753dd02c3e084e40e7c38fa47b9b771cde6b974511ace51ff75ee02141e72cdd55fc75b7f474d20007b58148465524bef87327e803627445d94268a79ba50d803da27e466554050a0b787d755304ab153e12454b4566a658d417d7807dffe03b5bef953be1d22829dc9e6c4d847b43c631d623647a79e62f9a6e464c05579924708c91a849b17e5c39cca7f60ee7f8aa979e88d65c236c6912bf7ed8c1d79dcbf70368acc0b286bfe3cd7d84c3a5d18c8d7b90cbd02ed0629dd4ede154c61113585a2154adc5aecd75cc3d51acb788962284cf00f93fd4d3c2d982ca284640eabb0c593b41d0e118ada124ca6f84c854e461fca72cc4d742e45f55360c33e196610c94c6e276890cf159441b0e518f0122be15816876de1a19b3a29baf1ecaadfa0ab95960e592a107a5aa2c35fa3a8ae7bd0442b6f397ffc487", + "signature": "0x818d4c2482a3a8bea82d90ac28ec2500207da0d7a274dd7ad52ac458c173dbaf8bc37014e439b4c414dfffcfba5a178301080003c9021bc6c20cd5bf5788206c20bc211254fcca4f34c83bedfd0f5f1a66984480ca5edb2202c11131ceb3c5d9", + "validatorProof": { + "bytes": "0x020000000000000000000000840081c97256d553a8f234d469d797b9535a3b49f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4bb8a4dc8346bceab5a54170169a975454f3d7d47b3f0a855a46b9c1682f0e68c45e1a2482875fa0c1844e9eb19fafd10dd001f7c448e223ac2a174a070a4ae663cc2503e8b1dfda99d6a473b05b63e4ef77211090d7c7de000a3d60eb0512e6735ab8a9d341aee57da1c455a57ce11a7bc775237d6526be38f3e4a8327c9be583f40f3b115bf303e51ea29dcef8a70848a8cfbdc3ab3b51d654d9b7c4a1822f4f6dcc89c27a2126bba31f34b8a6232a8bdee644395cbd51359ad7427eadd82df8a7587443e47482dc63ab0424e3e76ba6e75a96770efb29408a37549bdae7f235de007409eb57c450e4a6d094851828248b643fea37c8c2c7ca2722cc9ab4b5db46ab7c4613ad4366f6c89a5edd17ed6e2f3166b02a5be973a9822f441d4ad98043c1dc1c5df770f5b317a3f46e3197a8b0bfaa807453cd35639eb93fc9102a9bf6f6e8941ba7ed76d08a6cf25db94a263fd1fd41e72766e4728ee41939efb03c912efb5c038ccd2cf8037a532fb6f067dc33d5c283c5f89c741b3d761408b891651016de9ac1cf5c8abfa145cc01c01b5440b4d5245bab5031834a2be57b0b565660eb3971ecf12bed5d4c10b3d200ee0b1e85228d62b37ed4edd4f76c2698f18c797738207ebb7487bc7c92f1e6efc29234317a07502d81652ddbe6361497d02030d3fa106e3a8dc286dcba72227bd702e461915cd928ca69432674300a23f7291d342ad0457de9916ef31cc38815375e6aa1c2b4940cd733873f2533a744ba859478df1c06b148271e8d12ef6a460c9ca26a636ba9b442cd141678890012106c44593d627de4af4487a52d4f449a373b225d7bd98193ee82ef6510cde5872d3d7f638fde7f712618272899e5f6b465b016f1031b4a5710c32df7b4b451cccce2f559cd1a5000ecab92f2f9407e3ac6cbae7166e187688f56ea62c72d59096c5ce5e37b4d4a8bf060fbb61e86e16ea54068bc9c4aabd4273d4d76bf1504d0d18a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fa8e01e0000000000000000000000000000000000000000000000000000000000c6341f00000000000000000000000000000000000000000000000000000000006d833de4787b4b14e2cf638da50c1707c9ff3cb0dc7a45a2660b4d3458c7efdfb6c721321dfb0e529649f74be64a31bbb7d70d337f37a56b020ecc32893d1dec899f8e0893bc267e3256c7542c6cdead589454432274d6bfdfa33e5a4372c0408332dd055e96fbddab4824219c99c6efd32f62a94680d7d7fd9a209136498a235d9eae31ca1126c31a097992d1aa46e4959dac388d050aa45b9ba26dd04c29d71e55542dbbada164392f6f61345af3b028848382633277ad75a17af787f75b843767942c6c4d511c58ce23665f710e5206efbcb15a207e9f7bc0bb86306f7409c256512691d51102f5d48c78b0bee220af931edde3dd49847fd7675ad6ab7fec", + "root": "0x44b8ab595c44413189146115257225a6cc5bfb19a914c339fd35499d6b902ce7", + "nextBlockTimestamp": 1753248035, + "comment": "npx hardhat verifyValidator --index 1906721 --slot 12202000 --network mainnet --dryrun true --withdrawal 0x840081c97256d553A8F234D469D797B9535a3B49" + }, + "depositProof": { + "firstPendingDeposit": { + "slot": 16924900, + "validatorIndex": 1906845, + "pubKeyHash": "0x9ae6d8a1f458afe0f77deacdfaf6932c09709a940d04813cfa39ea33a8556374", + "proof": "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4bcba75bd7f41b72687e11241700d08bfbdbc73cefa5d2b849cd6d7a424157f555d550eae3f15354c8fb6bc4b3a021cda4909e2c4b05dabba71c0053244ea36c664d773db5f03e73e8d5ca509ee6921b67170217049c0a4052fbc944096643eb30d903258d0b05215db08f98b03244dd5a82f1bfdd24577cfb4f4efb0daa7d35a39983cd417067582081b644b2f90dd3266b998836bd19fe8461ca7f3fcc73f7f65fc8c0ea725e2635155ae68fead1144f257635adf38190aa3427625b5b2e77ba1fd1633437bafa071ec16fac85a65cb888e74efd13205907a6a7f7e217c6cec9664f15e642dbaa870122ea3ffbdbc4cc9ecdb2af5058cd362fb0c739f32c56dc811cd7feef85e41574e6be36b22cd88964db1f30fec29f4a6e13add516637e41e9af3ee93d7f86be871dcae567e69760dd6b39852814d1976424c5cdb69b9234ddf516dd1bd8406b50c8d796227b3cf6426d06380d0d7190303728317d40c5c56a1cb1888e4dd4c6d8d6802d469d0e33e583e76fb7a1c4fc72de875fc7bb1f763c6de33f5ca17cd62eb826b37b808d2fde4769b7b7639c5f9c27ce06bc1ba4d6b7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765990d0000000000000000000000000000000000000000000000000000000000006178d955edad5cb4f0be10cba9224cf4eb3efdd9d6b19c5a40da09736c1d37166a6ca2c7a85cab7226b4b5a6ba7d9d576d0c8983fa9b7956629eb7e1e3ca3b31ae8b5f91756ac63b58ab6782882102547a0b102e90b71f23f1da24e2211877d8c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c942486e1133017e628c4e01b836f6b2d473f45fc469b0d9d79141c07cb972b65350f255aba6cdb190b9694ea32b2d655d5f78dda9e2988927cc6826dce0fcba9ccd01c943c59d4e73dfa00725fd66bb5ae1feddbffa073a7ffe4aa5bd7c54004e5768b49a6ec0260a9ac6186de037c55bb05cd16280ee8730b0c68455e01ac82" + }, + "strategyValidator": { + "withdrawableEpoch": "18446744073709551615", + "withdrawableEpochProof": "0xffffffffffffffff0000000000000000000000000000000000000000000000003fb9d9aa4fc195b3b1722a33847a4ffe822b311efb18d6ff4c281bedd579ed59e7ec4478e9f562a1f852400d5dc64f09741d6c928cd5754c49c4e79a34c2ce002011a97d5771d32130fdb0bb2fec85d29169400d00358af212c9f48ddd73d7deff8c1de38d239ca25d110c7e3d48a4c99ec449215080c7c4f305429424bf11b93bdf23f5bb4cbc8ace33e43512cf931d100a7342317782a234b253138f000f8894f823e6c6bc72cf3fab420bce80854332705c406dc0e9806cd60cb7cacf078af2123a465c1efbe576f7fcbd6b5b7ff6cfa185fa49ac6bb5b37faa855b2d48cb22d035c95214be6c8186f5f4b919f02de09b3014ebb809ef7aa0715b1eee9badfa30bf4ce647886b15a48e7586dd5e06194a70f569dd8a9d3a1331b9550b06f28007c37d3ec94635a2058ef11f13edff52235391d57e7151b4736616e97c65fe26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1ffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92bac7e77bd751b08062c7cf9bc8659aec2e148dc543bb79c45f29b390ab6270c527cf2a91306d6d5825b67a2923c12c0b9f68eda94bfcaf5064f52f72fc1fddc4edf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bbbb28eb518a4043c1bfc00cb86ef8615bc0e3eb811fafd7a7d5ab0016aa42b62f8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603abf6dbddae9efb7940260d8d7a144839f5f39e57c16688d15fcf5f83a095335aa18c127639c68a76b4d4420b0e2db4fe3d3f72f9fb0e931e87854b8a1c175613c8cc61c24d3c69156218018e0b34d0bced1f6da9ab4c195422fed4d43a63472d208a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206f9d181d0000000000000000000000000000000000000000000000000000000000c6341f0000000000000000000000000000000000000000000000000000000000e3de6aae0bcafebebfbdbbac44316bf0fd3ac8b7276560cd10cbd70f927707535da71b40d30aa55b136343aca8b2f28ef0699f175fc1bc22cd4759c64183d8e18dd7743c3220cf54362c12a1d2b8f603c975e6abefce74e13ccaa4dd8007419b74bab9d5701037316ec4cf5adf2c56eb0c0ec6f1cd057cef68faeca5fb78f0425834d0c1e8c740753dfff7603a24f58ce5e26afeb4887d4c972bee3113bd2ac6350f255aba6cdb190b9694ea32b2d655d5f78dda9e2988927cc6826dce0fcba9ccd01c943c59d4e73dfa00725fd66bb5ae1feddbffa073a7ffe4aa5bd7c54004e5768b49a6ec0260a9ac6186de037c55bb05cd16280ee8730b0c68455e01ac82" + }, + "processedBeaconBlockRoot": "0x5a82bbde03427595c9d5e9598debba900d236a447536bacef4283cf76db4f2c6", + "validatorBeaconBlockRoot": "0xe71ab207e140e46b5408cf119f3bc3a679b023dd2554f04443818b531164e4f8", + "depositAmount": 32, + "depositDataRoot": "0xa8784f9e44d7419c0395e6f17579426f68cb3e1eb49a2035fed2d5a287cb3d85", + "depositBlockNumber": 22439550, + "depositSlot": 11657690, + "depositProcessedSlot": 11668900, + "processedBlockNumber": 22450580, + "command": "npx hardhat verifyDeposit --slot 11668900 --index 1906721 --id 1 --test true --network mainnet" + }, + "comments": { + "register tx": "0x10994ef5cca6868ec412f543a6430f53eabd4eeea29bf2d9ac93c66c782d1bbc", + "deposit tx": "0x10994ef5cca6868ec412f543a6430f53eabd4eeea29bf2d9ac93c66c782d1bbc" + } + }, + { + "publicKey": "0xb5b45d5d47bf5b2a2312cb98b3bbf1f6b5e5cb32f46561f843c46709b52fa0a048acdf61b98796376cee3e4788fee9d2", + "publicKeyHash": "0x3da89526b84e39cbd6f5e8845a38cd43fcd6738e0ef1700777ca50c6067962f4", + "index": 1906639, + "exited": true, + "operatorIds": [1032, 1033, 1034, 1035], + "sharesData": "0xa72efad3365e880819b8085c0f1dba39d37f499f65d97a92ba440baa56d14a77583b55273f2540ab410a0b8d890754d116396c83252990514adacdf59eee54d39896baa635dc764b282707aa2364cd6658f31df0c312965f4ff697b25c1edea4a8f593dd926e54fd05c59d7ddaa60995ba5378f5be33576214eec1cb8f5b12a8560620b3d5499853e4deec6a63aadb98a5212f1a7b2e2c59795f7e151a592b135716873f3d691ca71601918dcf13acb3466eab5b3df9fdb83e69155e0ca9b69baf396fa941265005d2f7f1b7db1a57ea6c7add916bfb36a8bc3ad095f7507f1a46c092fb08534ece66105b0d5dcd620bb30063d55238a33fd779928afd0a71d3604848255683d34c501854aac8dfbb797bea77f8180c7cf92f6a1d554664cf59c4d2e0de41603d866f4476566d92aa289dde7f73b33e601f8a41231bb2915904f25c94a50b5f0bdff40e3f5d1329efbcdbabe47f7e8741113028a378cf7e95d6e57da66d80303eb3b5c1a71ad09c8d3a7466893e74864c5cfb2ad0e21edb7a6a9510a8000264883fa7fd741485af0b5615cb95c343ae38c0256b5900adfc046b05a5dddeedc44ff9a7425cc5b28f79d6936d098b9c6148f57853e516f9c521ca2652ffbe76cb472113284e934b2e4c214dc28a12966c050a3042890641f68540a7dd20ba86e5fd2fac58440b1659abe19282b800e8fa4ac25ef29d8355c91ec9c4419d36c512572d2f81bde626a91ad3013d6a532818fa6e0fc7f35ee4a65d1c31133fd1f977c625894aa1c58df2f9a35e112188e720175e8a3d6cd2753a828ee6026afec4a79326481a3c882a7c5eca5b6e3da2ca989996545a523554f4eb80b49fdf9f28fb2e885199a685f5ce1f7fc8b4703717fd129b3baf65e7911e483f3cd4e60726cfb8d965d36c88c8df0a14d3ec4ee6bd0569970d6eadd430655e8c3169a6b08db594d6dc270ca487acf72f5deac8cdd7e61d21c79059840fa5fb14ae52e1c6031feb837d8cb4b651f1ebf41457a8bdc001550548d5237fc60d6250e4dd83c39788c806836bd46ba7cc03b8a0d285961846cf921012846237071b36baa8230602c4c5f9e574044a356aded9dbe8ec9ec33ffa6af440416c375cc7f3a88344f0d22967f2741e473a30b50afb244e90510692d831da9fe65b16e61e26960329ac78283fcb77a8e9f07c71b66849bccfce0c5108f29156a463b8e1c09fe922a94ad541978018c211872b0aad86989b973a04a7247b1c824cde08cc30dfb883036603e9dbb8a67102bae7e0593c1d4862cfdcaa75e395424227e10e7d1d1330e0c9f3541320722f0a6f35a9c97e16a570a0389ba27008e54b6d7fc287e0bd567c0c9d0ff6daf2911c559f6ca8b10314d5dc345497829056b2d6554f34f17a6a8c8a51b8a1291ca4dbd8bb9288bb4d61cbdf96badac3abab27b1da73753dc991f75710508ca4ce376455f446ba1e0548892baff474ea70ef737c76f702f5252d22aa8552567e1b45a755af4054ae4ded6ed8bb4ee414d690f1fb08e707d96e6eb1a31f7920ba232fa9bebe2cadc1cb17b8eeb820f9cc0f5ef3ae1a5e3c85ced8ba652df3ecdd362033663a46b7205be80befde462da47d5336588c89b3278cfc3726a4186a374d5f59c9445c383f12577fb7d0d3d238a5a26a01e312ee2dbcd6907c27e1e5ced4bf4605aa229cc231a83626141255a462f2277a9e859d00e5b855481fcb1004ec41edd9f0603bf39d50786bb1c2feba5f89532cdf4244d01efb817226215f036f4d99ec7919035c3b33ec9acc6f5f7510746e9fac5e872ca65084bc6bff03c0d95d023b021fa1947f266716914ee685701850e7f7f8eb7a", + "signature": "0x82178036a1c187c5b0bdacb9ce476993afb36a39943d322ae7a6dd419eff53e5b56e2bbef9f1691bf9d9c150db1a255716cee163528ed2dce20da91c0f65f0de0f75f85a1afe915d341354b6e184602e444d4899396ac60799eb903b2972108e", + "validatorProof": { + "bytes": "0x020000000000000000000000840081c97256d553a8f234d469d797b9535a3b49f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b408d4e15b7952171c266c8860e59dbb4450a443accfb8a45f5fb95e7d0afcb81329b8aaadec417d399d0fafd75e922405aa6c63b3a536bd5243068ee40e5fe84f1ad9b3e7b722e75872e933eb319c9da95f374f52713e18a1f9a6bae9724e8eb396175139b2f44d189636401de619a5dc6d1ef99a20bbe434d5d30c83c8d501b57f73329b325279fb5285e9ea666aa7003895507bffca4bbc8bd86682216f217248ed116364ea6208360d9191694ab21caa2e9cd3f4c227daf1f432d0c4f88d4936d76818304e3ce8d5c5b07d0a8f631d66110d66704a3167a19997db19ce71fe77ede8efe725d7a0cf9fa0e7a50b388b010bc0ca3cd9e75c07ecb7919fa16043399a5a7ee47f4f93ea985e085af9c0bc1e2d6f73af0a0796833f8f2c4aeb86def9eafbc9d345953789c8bd95853e91ab1c3f9564392f291a8a36137a86897ed2305b3f1ba8c14acc497443c0b639d9973dc558172e9eb51c0711a061992d55cdaec41be353f784f349ca38278812b7468ed921e5ebbf6ca91f3e57551c4b34ef8b868d0fcfa0d4ae990ae7e562edf68d70e9819fa34369f26133f44632566c55660eb3971ecf12bed5d4c10b3d200ee0b1e85228d62b37ed4edd4f76c2698f18c797738207ebb7487bc7c92f1e6efc29234317a07502d81652ddbe6361497d02030d3fa106e3a8dc286dcba72227bd702e461915cd928ca69432674300a23f7291d342ad0457de9916ef31cc38815375e6aa1c2b4940cd733873f2533a744ba859478df1c06b148271e8d12ef6a460c9ca26a636ba9b442cd141678890012106c44593d627de4af4487a52d4f449a373b225d7bd98193ee82ef6510cde5872d3d7f638fde7f712618272899e5f6b465b016f1031b4a5710c32df7b4b451cccce2f559cd1a5000ecab92f2f9407e3ac6cbae7166e187688f56ea62c72d59096c5ce5e37b4d4a8bf060fbb61e86e16ea54068bc9c4aabd4273d4d76bf1504d0d18a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fa8e01e0000000000000000000000000000000000000000000000000000000000c6341f00000000000000000000000000000000000000000000000000000000006d833de4787b4b14e2cf638da50c1707c9ff3cb0dc7a45a2660b4d3458c7efdfb6c721321dfb0e529649f74be64a31bbb7d70d337f37a56b020ecc32893d1dec899f8e0893bc267e3256c7542c6cdead589454432274d6bfdfa33e5a4372c0408332dd055e96fbddab4824219c99c6efd32f62a94680d7d7fd9a209136498a235d9eae31ca1126c31a097992d1aa46e4959dac388d050aa45b9ba26dd04c29d71e55542dbbada164392f6f61345af3b028848382633277ad75a17af787f75b843767942c6c4d511c58ce23665f710e5206efbcb15a207e9f7bc0bb86306f7409c256512691d51102f5d48c78b0bee220af931edde3dd49847fd7675ad6ab7fec", + "root": "0x6a4cd7909348a1a76f82d37cfc9882fed3ef9c73ac67c078b1b18a9a7828ba8a", + "nextBlockTimestamp": 1753248035 + }, + "depositProof": { + "firstPendingDeposit": { + "slot": 16924550, + "validatorIndex": 1906694, + "pubKeyHash": "0x277fcc5c971b26c4f06f3a701c674b593fd3ab882e540707c39715c1ffd486e1", + + "proof": "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b00615f2ef034f963cd27ed519ddf2dfb24be5f0e091cacca78320ed7bb12d0e9657aa2b2c87101f3c54469893aba7929408cbcc3789a796d0ca2c0473eebdc951089cabe886777849ac43813737208ec02239add244dc7e9c269929ceb60760f05e16ee3cb977aef3ec74050c6fe9efed6e4991f1862cf489a263df07120e830b7ee930d16f8f6471ecaa2939dea0caa1e5623b8223daebd8032b66dfc36c887e455671554a9899ab5858dada274fbf63782dbbf7c33068b3d4c887aa517084e1a3d2917a01f06e614ddada404211e704cc9cf330eb841c9cbf6a3e387e74c03e821010f2e9640fb40ab0100061388c35180172c183cecabb286891d82dc7e2f742668aed9905c90c68f639f96814a954301744177beeed387f0daea44e6584b7291d258fe3a828a7dce7a9a30d3505e93ed28824a6297eb70ccff96d558480508bef04bb8eceeae574329c164ec5ad00807154a75b48c21ddcaf457e067f99c80ecb891b50d053e0ae92518a1cc09be7c399cc890e978c63304f393982561b88ef7c5c03a30095c56dfa49cb580fdc495304f5a580157249ca3d66a466b83afb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765260e0000000000000000000000000000000000000000000000000000000000006178d955edad5cb4f0be10cba9224cf4eb3efdd9d6b19c5a40da09736c1d37165dddb721501273701583241089e25d1018ad4aab82aef74be43ba3ad743a6d99b69ccfadd1fff72b178e1443d4470fda04ca7ee63034f880a189b488aea87285c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c6eb1cddb5540f20d211a96be15b6e54a630a8cab5f61546c4ce9fe5543ce9e262ab07800403cc81a1b05ecc073a844e24005534f74142e999544a8dd6022df5479acf036d7541dc1689f1d27e433a30d87436cf4100907eca73428e5ac81598fd53be51327c3d2756fd9b39a1534ac92a1fb4d099a0a8029647de94fa4be5dfd" + }, + "strategyValidator": { + "withdrawableEpoch": "18446744073709551615", + "withdrawableEpochProof": "0xffffffffffffffff0000000000000000000000000000000000000000000000008762d46a30f6c0d6e51964cb8c86232689f938b985aacd3b5064f30ec06d103a31e6e4948f367392a8246c68a9c2887e9673f629dec1d0c5c405dc0a5b836a90329b8aaadec417d399d0fafd75e922405aa6c63b3a536bd5243068ee40e5fe84f1ad9b3e7b722e75872e933eb319c9da95f374f52713e18a1f9a6bae9724e8eb396175139b2f44d189636401de619a5dc6d1ef99a20bbe434d5d30c83c8d501b3f815d79a8e916439039f5cfafe27908f2d73981063e9e746e1080f655febcb6f4841292efc947d4a8bbd0e63eb87efb933df1fa07d088f23d420f44fd9b842ade4dfb96c6982d483a3530ec5d0628b04e7ef293814fd07d004bf5248e49f0f6e77ede8efe725d7a0cf9fa0e7a50b388b010bc0ca3cd9e75c07ecb7919fa160434ef9d568826a2d37e48551f58303894897f43049df62d945c35548f51888483e935349045e244f60747684a9e8e79de36fb4de8f831fcccd7353a33aa306b006244ff09d60f89043fcdb18e2147e2c1fd97ec40506afdaf591aa8f0beb4ceb22a5af6e97ce92a0e5eb571f2d4f0520454da4496fd0cb7920144cf670ef759a5beb97b5e6aec7bfd983ba7f70f037c3d834ad3c1c5c20e725f36567d3146e5d77cf2a91306d6d5825b67a2923c12c0b9f68eda94bfcaf5064f52f72fc1fddc4edf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bbbb28eb518a4043c1bfc00cb86ef8615bc0e3eb811fafd7a7d5ab0016aa42b62f8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab25523c0e775620ca4dae873057971affd8226fe98597aeb50d694db8fd1e07c9ac50402bc11508c7b11515bd705e0d8dbc6da3407d8b5f3cb1de1470d83e5d0ead53db8065a6973adcf83da3018f448706710d2e518268150d740013faebd1d48a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206f06181d0000000000000000000000000000000000000000000000000000000000c6341f0000000000000000000000000000000000000000000000000000000000e2cc2fadf27ae0283d433de63eb61526d541980d3546f2c73e16acb84d7f4bb324d65324fa0201733280b3290f236b9b94d9a7a5181be499fdb7b0c1ede2cac14582431722749c99ca726ceb48e68769495f7ee01c96b181928b8052c3d35868984861ca8fa87cd73ff72e7676cb1ef09d3c970285df0e3316b74b314aa60b4891bd806b007d4dd831fcd8a2c8914e527fb3d18d784b5e4f1f607e32ffc1b1312ab07800403cc81a1b05ecc073a844e24005534f74142e999544a8dd6022df5479acf036d7541dc1689f1d27e433a30d87436cf4100907eca73428e5ac81598fd53be51327c3d2756fd9b39a1534ac92a1fb4d099a0a8029647de94fa4be5dfd" + }, + "processedBeaconBlockRoot": "0xb1517167a3cbac09029489dd54692484f1f3d34381b88741a1fba2d74e5fe916", + "validatorBeaconBlockRoot": "0xf1c037f1c8a2dd1b5e67f3c2e59f134211089e6b94caa9dd496ec29f36d45d8e", + "depositAmount": 33, + "depositDataRoot": "0x0853b4bb60ad246e654c113f41ac8f76e818149f32d07a92cb57a39c600f1cd5", + "depositBlockNumber": 22438980, + "depositSlot": 11657106, + "depositProcessedSlot": 11668550, + "processedBlockNumber": 22450237, + "command": "npx hardhat verifyDeposit --slot 11668550 --index 1906639 --id 1 --test true --network mainnet" + }, + "comments": { + "register tx": "0xa6411cebf89098fe837646b017b7161fdfa10d6381600ff64625152793436f16", + "deposit tx": "0xa6411cebf89098fe837646b017b7161fdfa10d6381600ff64625152793436f16", + "comment": "npx hardhat verifyValidator --index 1906639 --slot 12202000 --network mainnet --dryrun true --withdrawal 0x840081c97256d553A8F234D469D797B9535a3B49" + } + }, + { + "publicKey": "0x870fe7565e27700e5a50d7c781c8b8648e3c394769c06ad0094cbe72d55a4bc52b9dc7415510751bf9475f7b6f6affbb", + "publicKeyHash": "0xbee727b8bf1f88d6eb8ca9450619e5c8e6ce64895c17dc3b0222c5e3229c7268", + "index": 2015001, + "exited": false, + "operatorIds": [1939, 1940, 1941, 1942], + "sharesData": "0x83e69a9925fa0b2c0a54d85977ff34d225a5f9adc58f74a4c5e41681fe44969390ce49baba8a9841cf37a4896fb4052b0a599231d501a58802620f6fdd0efdb0eea660c8d3ff81abf7dbf718d602846c7bbed35b1b2e7488d88e59e746ccb721b5ea6d59627142e94f613594c79483a2b47ebe5e6265785727d79398178b1f327add17ac167079291cea62a1eaa534c68906932e355e567a24e712e2079fdfdb349c40e040b8d7f57f4e85098e516e93d247225d38d3c98b8931d97b866f7e4789bb20e8353cc0e26dd0ba7472b201f86ed1d809cfae2831b670aea3eb52e3b9eea7a773574e2aef101a31552e8d7d80a94043b831afb9506a3e7ecdf14e0bb11a43ed83241cbbaeb7b2e205850c787571e1be15e27279b259d49262201cf4ec054c6f102e91aef157906b948dcbf26960d36f8b43552624ea5a941b31266739b1da7053079a4bc02b86d6507a809d056439e68f452213a40c86e17df83267c24efa15e36696af3675a7d79f26aae86ad2944e2da7ed4e5a83a07904e696cefd060654e60d37d15aa4ae1c269c1c555e431d10b46971c17e043f556513bbc5ba150e50b3ab0c3ad5b16ef3db4d44153c25258660be9a72b28f0a4a16eb7981d37cd1061cc14d141fda2c0a90b88e30b19332207b4e4932f4f00ff38829ceb9883c155bf350360649d5e93844aeb44c7927185bc2f735ae396e6b97b8197656fa295a7f55c52ddb8a01942c8716ddabe306e567333fa5dfe5ef1095e1474d75d7b38070acefa961c8c32d37f33a7d7ed5ca24f80a27e50faebc8cc6814a9375c69e28c0797a70a67add64179a1bf90c53d57ca17994a08445a5718562948bf1418a4aef4284963b75acd62636128e3aa427e895b7a7d46800dc934dbca551f33a988404104f2d1446955f18472191bb8e0aeecf7e07164ebd41a852df5a8044ab0a123bbabb47c74db9978b2e68196fcac3e6be1f8bcf54792db9d56ba9456ed2bcd22ae6740dfbc9be70fdf2c249270000493f28f6a6b7f22f6f8b7e09b6807058da7d2a4ba4199daa2fb9332ce5f6d9c29353ac05fd3760abd376f4d1e4adeac22fcbb77cd13a16ec86297c736bccb52a2ee5844282a9d1670e63ae96f255169fafc7d24bced184326b3058ba25c9995833f6a17d7d9be4684034af91a4631703fda29289cc7427c4ab3c16bf44cfe53da62a8799d953eb75abad3e002e3e49dcf6e1415156b9a7ec9489d625dc6bb5aadddef4afeb9dbf4993a25c8d92c0212826815f4a7f9537af7d0188546369d80756eca9d3bfb689c1875133f68ab883103a6edaef472383c6324b4edb40454379b65f916944496e09ed1d77d593311303c9e9fe7b21887dc65f9ccbfc81ed1e4d2a0657fc96590b5dfbe142533319c108e8dcb0011eca0f75a4dfe275d02e5613d7a0fdad116989d2c5c19fa885023c2679ea29399b2f7819b37e42b4daf1a9d71dee04a0aa0e453ed6dfa3b732fcfc15e6a692e662686db9394d2eb922758ae52bec0acaea0e40cfbda7d530eb09e253fd328f42bcd464830122445d321ced67ada7d83971f83697024a003534ec9bd53af1d09aabf6947e113c9dd427a9d7b97e4b0f29ff1141dbee93b8208d098b043cbb1830a6613753a994ac8398466f5f98768855a4eea6712d5c04ccc90ba731ecaf4670387d962be5b6d1d2a1b733ed8fcdb58bb188b5fc34ece77080789b3ce911bc92a3cc99afe47d86984009057a067be94e8b38e6438e1fa26fbf5daf51148c81601aac39094cd12cfddeb26ba421c9a6150fcf6211738aa47753aaa2cbc1b3bab5e1c89b2c997b01fbdab6639a85b3443f4294a85704b9ed57960549", + "signature": "0x917cd78d9c1503bff0d367bbdd799224b378b5af74457e97c93c17d34651de3fd0ce511a4d27cc3d2da5e95146fc33d014b9b2385599bce78e5208f8ab2e474c4af5f37637361c24baceae80b04590746dc76e35ac33981700026523122b7f85", + "validatorProof": { + "bytes": "0x020000000000000000000000840081c97256d553a8f234d469d797b9535a3b4919327cb9763c96e00332bde93bdbb1032c4b796dda73e515c8c5f7ede9a419be9681c12ab572a2194b200e682f823424309dd3d5461f99172e09e2c6feaaad23fc2007f3f59fa578aa01318a628b868a77baa3b2b0587e554662599a090cc6d2520b81f7c60519bf8997b298a7e208264becddccdcb4069e765d4dc79e32449e4f18814c75c97760a36ee51aa8a9c3ba47f3c546dd95efb652459d5624aaa3b0a619cd0a73d691ee8c705ac9fdbdc9526ffe5ff195a31ecc7ce7c16f6c9bc149b000b089f827c5d40b40d00ad6f1a3c00f55ff8e5f85a1ad56fa866627abe5d4be820a5a40ce9782af7ada1d96bc66ca27c8b2df685a56bfb3f3718ba7a177400397ce5afea8fdb145e52d9e4485dcab61ac1491c9ce243bd111b21dbb80418de01d363bfffa16640b68885f3d77928d17941ccddd15462128a86a9c94de78c513ee3535a4262d1ea2ccf0d0ca9b013a386b6d7c57962f49cafeabeaea487293ef518ba28d88abaf01fa978488a97de5359669cb121ed43c1356b434a538190c251b8646e24530fc13244222c56e0735a017bff9e444ae694cd960d1426172b0ff9280840c39feaf86b8475d556d170c3df366d41f8bf7975a799afeb5b9c09532fd2d7a4ecbcd6b7542f0e8afcdd8d01bfe5ee455a46f9f28f81bad6464e2e344e63a8d2177cffdb23a6e0d66cbbc1606709591a0305d7e0997c281c844102f807a05e8265d7fb12b34ccf5cf0e988e6ef1ad6a39098fc5bf04099e8dd51c66396f2e5ec8e570ca58a3ba4f78aa8c9b22614b299571cb32fb49b9b5506cca408fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb48135f37c0a5f02c0cd358938dc3c24eb9d0158283fc328ce0f5176c040bdb043d7f638fde7f712618272899e5f6b465b016f1031b4a5710c32df7b4b451cccce2f559cd1a5000ecab92f2f9407e3ac6cbae7166e187688f56ea62c72d59096c5ce5e37b4d4a8bf060fbb61e86e16ea54068bc9c4aabd4273d4d76bf1504d0d18a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fa8e01e0000000000000000000000000000000000000000000000000000000000c6341f00000000000000000000000000000000000000000000000000000000006d833de4787b4b14e2cf638da50c1707c9ff3cb0dc7a45a2660b4d3458c7efdfb6c721321dfb0e529649f74be64a31bbb7d70d337f37a56b020ecc32893d1dec899f8e0893bc267e3256c7542c6cdead589454432274d6bfdfa33e5a4372c0408332dd055e96fbddab4824219c99c6efd32f62a94680d7d7fd9a209136498a235d9eae31ca1126c31a097992d1aa46e4959dac388d050aa45b9ba26dd04c29d71e55542dbbada164392f6f61345af3b028848382633277ad75a17af787f75b843767942c6c4d511c58ce23665f710e5206efbcb15a207e9f7bc0bb86306f7409c256512691d51102f5d48c78b0bee220af931edde3dd49847fd7675ad6ab7fec", + "root": "0xde3ec6728c0e06643a9b16b0f2cfe6693e2c797cc16a51bec2cf2507ee40ef7f", + "nextBlockTimestamp": 1753248035 + }, + "depositProof": { + "firstPendingDeposit": { + "slot": 17415000, + "validatorIndex": 2015045, + "pubKeyHash": "0xc18587318a7606b1283082ff936aff177844464092e26ff10766d76088d25a10", + "proof": "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b3a186d89b8c395633b7800feb5aecd2858cae515fc3a898a61adf34ffdfd4a1da504e55c2a2fb8ef73936ef10cb5180a2aa7efc6a9c7b52fd234e49f21bbb2030a84c52eaaa85a54869efabab370757f33f4d9439b8338253355ae5fae1960b613a6204bcbffa937b5d05fcf475b22896a7cc676e717525e1dc337e556c7e1fbbd4a81fbc72812cde79ccfeea3badc6414bc50f8a222489513428d8f18668e6786159fd54923dc4890003c9a2da23c785eb51535c0a111a930b4b79c6e58fa1a9e98713b2635f4b2fe3a0247306dc7ffb205528773a2b446fbc075a95106566a28f70b00a2127e6349c21029624293761690bb48c38ecfbd783d019fad0a58c7eaf588910a026bc18553ce932e6604a67919790a683741d39e20cbfaefacf11745125b4510bf271e5346204f13b85f552e1723b3e98b72e4bc2d76d0b80173bbf23cbc061dd6464d7a23570968252a4d0c7cac79a712bbd5d0f1a0e3ff85b3127ccdb4d6fe75eaf78fc68ee876e6d118cd111377437dd6d506d3df3ea7282abe69d448c714c6d1093739bef8ae2f1b0a2576c40034259bbc01fd0a794ce61edc24e750becb8303feac61f284c05a303b4c8abdeab5fa2a7b7a9e59ad4ebb106cec56657ee6b33265125dca9d239a04736dc797b243f75c2d755d0c3d0e824fbbb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a4676581310000000000000000000000000000000000000000000000000000000000005aa1f0b33ed076d597709633084d4fc33d7fb8672e98c2276aae9db16761e21ac44d460ed335a779aaaf9f99d76f35e7a508087302392bc77585c5cc4227bbd9a0d19488b7ac3bf82d3e94711f87b9c3192a2818ef306bc706e8ea9c3841151ac78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c56a1ce07f625c064b33f3f08edd81df1803f2c5e30da19678de488feab03aa1ea7d023b404913a4805c124b31cdd657e3f9959ac04a3f61a7e70f0be8520980f2e7e6bc524b8f24d211685cd8acaa09036b3d859e1d97832efea4474a28c27ba7165ca2def1fd650e067662dec5dc609fd79c078a84f43e0d3d4f2765a7f7166" + }, + "strategyValidator": { + "withdrawableEpoch": "18446744073709551615", + "withdrawableEpochProof": "0xffffffffffffffff0000000000000000000000000000000000000000000000008838e039d069e1c72127b7636b0d20fb31476e44fe1f4ab8a27265b7047fc877ddd9536391d6feec685ef45ab62ebc374aaa6fe529b545f365b134158784525afc2007f3f59fa578aa01318a628b868a77baa3b2b0587e554662599a090cc6d2520b81f7c60519bf8997b298a7e208264becddccdcb4069e765d4dc79e32449e4f18814c75c97760a36ee51aa8a9c3ba47f3c546dd95efb652459d5624aaa3b0a619cd0a73d691ee8c705ac9fdbdc9526ffe5ff195a31ecc7ce7c16f6c9bc149b000b089f827c5d40b40d00ad6f1a3c00f55ff8e5f85a1ad56fa866627abe5d484828a514f48cdfde1856a2afe877435f91916f474c6dc573c60d3ba673daa256779d0efc4633104e654859a052a88c0684da93ebb5b0301198e5c4b3e56ef7e87eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c211100812ec8b70ce49e936cc046ac37252775aade28a3201adce9ff3c740b8f287690e36d43d0eec99470400bfbc6384476632939ec76f9c122970d9588fab6595a1f591be002be8d07d50462210c9bfb8c91b3b03af3a39d60117e18a069b42edfcee5b940ebb3349a99816678be658f85a1318e4e330c4f3eaa0efc07dcb1a2d970c5fd25b715e7d796c54c740ef2c80024e02b717c2f66fd4cfda7440540a9795d23036c884adbcad7adf776dc482cad8e384d8b9b3bd1cc8273fe8ad416b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da72937846b6775fb61613c6e39377667bdab239a5cb904b282dde0bd5393042d0d6bdcd98fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92bebcb0e71adc33600b1de344f7fc9463b95786f1eaa757da3916e67c2ae071480033f8314e472b324e6851800efefe9dde385b39013a284c7b7c01add71ecec7a5145af84fa4a73b302c107244296cd4fd084530b6de31fce7b5d4dd986e39116776e5789eb5938fad6e1ebc495740a7226680f48fd23c219e5f947a79c585267d08a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206f45bf1e0000000000000000000000000000000000000000000000000000000000c6341f00000000000000000000000000000000000000000000000000000000006ae09ba460640c89a1fe16f42a518dc6b3caf9c8c9cc0891c5fede00985e4af0087ab8366105028648f1cd294ee1a7068bb1d7f385ae28f1fc21d942884aba47ac0bfbc636937f1daf098ecb4898db5ceb3e3214e70fdbe3a260bbd3a721bf62fc40bcd64d3ac8f3960f860e9cfdf8eb26bb813d817965b1bcccb126ba1a48eb449f8da5e46f5ce2f5eafb92645d9b22c4d75fe7e5d3d7bd87ebc236b31932eaa7d023b404913a4805c124b31cdd657e3f9959ac04a3f61a7e70f0be8520980f2e7e6bc524b8f24d211685cd8acaa09036b3d859e1d97832efea4474a28c27ba7165ca2def1fd650e067662dec5dc609fd79c078a84f43e0d3d4f2765a7f7166" + }, + "processedBeaconBlockRoot": "0x9d3655c01b0dc6cd698ca5fc5302f353453b3a292d88c37d463e9b4751419c10", + "validatorBeaconBlockRoot": "0x008f7f8ba4cb7c69580ad40d20b70aa4b590982cfa7b2d69b38a2f68f1f6fe1d", + "depositAmount": 32, + "depositDataRoot": "0x1d6204bea6cf0e609abfc17b24ee2fede9e6580924bb808e379dcf800bd8f3a4", + "depositBlockNumber": 22887000, + "depositSlot": 12108751, + "depositProcessedSlot": 12159000, + "processedBlockNumber": 22936987, + "command": "npx hardhat verifyDeposit --slot 12159000 --index 2015001 --id 1 --test true --network mainnet" + }, + "comments": { + "register tx": "0x77a3d3ee1898ae6310bce5719ee883fe68deeca232c3dcf6ca60a396c64b4c29", + "deposit tx": "0x4d2ec847b4e52285b10e3dcbd8f28b7bf37d84b71d359272249c38973ad8df1a", + "comment": "npx hardhat verifyValidator --index 2009356 --slot 12202000 --network mainnet --dryrun true --withdrawal 0x840081c97256d553A8F234D469D797B9535a3B49" + } + }, + { + "publicKey": "0x871a6a037beb89d2969e056e0bde4cf890035ace0bac0e78b7353b0b96fa3ced9c2c9d484ffc4a52f760fba360d24501", + "publicKeyHash": "0xf4118d9e7e758a051e114cc26a1530d08158562403517f4c82872bd6e21a8e27", + "index": 2014925, + "exited": false, + "operatorIds": [1909, 1910, 1911, 1912], + "sharesData": "0xafaa65b93f786f34697d8e429cfcd959bb2baeeb1093de9185a921e55386a6b1202aa221cf251d235cc4a9832472478c07bae8ad4c40c84cdf4d375c25758542e1b93259f52100f76583ac483465382826ce633f48a7bb78975f5012e092d659a7665c3a03d0299d4689d4e4144ffca96b73618885069d86502d525b8f4c6ef9430a2e3ff773695da47c3ce25ab77fd7a9fff78bebf823b1e361d9c32afce1e3586f2a4e858a6e1ef85d8be559b1f517ef54561e17542c09abca720b5ce998b0841e1bab3d97f681a33624e9c7a6e2909a5623499f383ccea35ce51cf32a4adc6a160f6777c1ef1914a264fb8fb3c192a96b840d3d9003eb2f34414a8a6f7fe3a3868debba01c02155828dfeb551be65cc0a98af964c1339d248d77773ecdb5e06443f1e207b2baf6f16cf31dff61dcf826a741edf0ee23ed95b3e82536dd0dacf4ce7d612079c9c544276c26fa789f25a8529ef2c4defaf2761a6df46a5b2233f47d14818cc73617760ce467d3f6fb1ea57ec9d2225fe4811a7a1c965f9cfb6391a5bb861ebd341fb3817d41529d30396f35034af3f08d4aa1ce7331cef3ae90fc6947176bbcad3b51613376d714870761267e0bab139869a5eaca5c7fa05938a091338c7f9e62869b21b5f672b4b4d8e1aa2e2fa499781082263ff5590ebaa8ed834715cbaddc712effffa6b0f02e43568cf9aff238dcd0b0b333b18a747ae369a45eb004730f21c8093e256c760c74178bd72d20ffddb090f2c1b018e8d094b286cbcc3bbc32d8394dbc00374ab8a496967bd96d71c50a15b68a03e56b5b41077ca88388556f0b96a44cb5e7c0a710402a384f4442b57b70596093dfb51732ba1b36dc41a37e2b776c6b32b8569d944a065760e2bd2e18319d30398ed043778cd3446f1ac10f7f680726622f828ec3a79d2c40e811db4a6f2f432aaf4f18893d26aaf8c9940039d38481e188211ff7b068f580d0dfb3f0e42e049835a1e1b56d39e7681f6fcb6a89d036905df82eff043a3414eb83bf2634702483c748b0c3f463b443f1c39b2654c1ea7471f8b8e23077673555ba021976310f9d7b0aceb08823e1c7983cc39415939eed8eeb02a371f5d0c099ac2f37f890fb2911ef1685ec96352311ddf6dcf63a55c9e9bd0858b594f2504e944fba634d3053834bd580eaabf3bdee8dc37b5ae13f354dc7b90d908968fd0abaabbafd7b14a1f021d5973622b98f3a93567272d8b166b97ed44387aecf8a2b01e027f338a20972a24ba11c52f1ee5d654d0269c681b6a3203f52d4039f29a47f883398ba41d983882ee18e1c96dcb59668741bb0e864a9967c69bedbd11f5fb4fb569042c81bf5e692b6d471a38cd45181c9a78d71c609b5322efbf28af69def921380ea50136c4f732114a50ee7ea8df2c46a65484b068e33257a4e721950ad135c17ee8e77ed22b67838d5edb7b2a953fbefb0da111616cf02ce8fc0bfb866943c32f99cc1d08376b17e88472a071f6b18fb780a3d4f74e4d3d685765aa58a3f21774ec22effe1c9ece3c80a5900ca69f4ff0b4011acafbcf48a43548ad257037932747ad1ff52d68194503581949dda9abf139f580a75c4e7d81caf9a61c98e3ec345a8ac279147a1f3eb3a256be82ee87dbe92009a4cc974a814d13daeb7234c2a8a4cde0b4eac3f2a302c5c3886cca330b90f97f44e0374c1f86bba322b5edc84b9b5e2c41a657f2188f033858c3471f89fe3b6de17e9ce112359dfbf60795b27261ad12b24c1085e71a178d69e6532744c1acbf4fbe11c0b682330b00a4e19a2bc0fe7b162966474c3d71393e5212732e7e62b26b2db93d2744595b6dd844769620c749407d47", + "signature": "0x98d6ef8b5f9d00f46e4783b02d6d8e2cada444d46ec12fbf7de3f42c687db0fcf06d38fc20829316fdc6f7f1623872740482c13db46c866308e66fe8fd82c791fa0681927f13aafebc126d95236f993123004547e23bf881d94872b532d05fe9", + "validatorProof": { + "bytes": "0x020000000000000000000000840081c97256d553a8f234d469d797b9535a3b4919327cb9763c96e00332bde93bdbb1032c4b796dda73e515c8c5f7ede9a419be3d611f0e4ee59649c6daa0c0c74fa35c336ab92b928bea1fae9f6a17996c113987cd11165a08b78d64c6052e3c8a4166bbdbba0c54a3e717083dda42e64150a25a441dc23f5970fc0628098bb12fdac7d83e4cb55d40959dd3ef94612f70bc72dcaa6cf026d47ff5503ff81cf229ede8482964c455afd037c4a4d895aa34a3e15b3d3af83fdcef2d2b9a3382b530c51c90797b76a8a1a68c76a651ccfbc216ae64b04490ab004a39ad0076630ea5ff612064f859d59407160428f193e05b43f29cdc1701dc6f692d9d257625ea6fa11e5c9103d0670212ff73e62e1308d2964618ab5764e148e1336f5aae53283cc5211778c77a0c85a6cd3b42df59635883ad3e89a8baaf4ccdbcbc65af53c95a40bf72957f30b2c7174dc06277b75827e31429d40bf3b279722ca18134155adf22592e16a793e8e4ca2fcd59707295c896f1ef518ba28d88abaf01fa978488a97de5359669cb121ed43c1356b434a538190c251b8646e24530fc13244222c56e0735a017bff9e444ae694cd960d1426172b0ff9280840c39feaf86b8475d556d170c3df366d41f8bf7975a799afeb5b9c09532fd2d7a4ecbcd6b7542f0e8afcdd8d01bfe5ee455a46f9f28f81bad6464e2e344e63a8d2177cffdb23a6e0d66cbbc1606709591a0305d7e0997c281c844102f807a05e8265d7fb12b34ccf5cf0e988e6ef1ad6a39098fc5bf04099e8dd51c66396f2e5ec8e570ca58a3ba4f78aa8c9b22614b299571cb32fb49b9b5506cca408fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb48135f37c0a5f02c0cd358938dc3c24eb9d0158283fc328ce0f5176c040bdb043d7f638fde7f712618272899e5f6b465b016f1031b4a5710c32df7b4b451cccce2f559cd1a5000ecab92f2f9407e3ac6cbae7166e187688f56ea62c72d59096c5ce5e37b4d4a8bf060fbb61e86e16ea54068bc9c4aabd4273d4d76bf1504d0d18a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fa8e01e0000000000000000000000000000000000000000000000000000000000c6341f00000000000000000000000000000000000000000000000000000000006d833de4787b4b14e2cf638da50c1707c9ff3cb0dc7a45a2660b4d3458c7efdfb6c721321dfb0e529649f74be64a31bbb7d70d337f37a56b020ecc32893d1dec899f8e0893bc267e3256c7542c6cdead589454432274d6bfdfa33e5a4372c0408332dd055e96fbddab4824219c99c6efd32f62a94680d7d7fd9a209136498a235d9eae31ca1126c31a097992d1aa46e4959dac388d050aa45b9ba26dd04c29d71e55542dbbada164392f6f61345af3b028848382633277ad75a17af787f75b843767942c6c4d511c58ce23665f710e5206efbcb15a207e9f7bc0bb86306f7409c256512691d51102f5d48c78b0bee220af931edde3dd49847fd7675ad6ab7fec", + "root": "0x4fecf05456b7dfec7ebff7fd7291196aeac3470563560f473654853fd8d6ba01", + "nextBlockTimestamp": 1753248035 + }, + "depositProof": { + "firstPendingDeposit": { + "slot": 17415812, + "validatorIndex": 2015253, + "pubKeyHash": "0x808f9b9e414414066ef6db5ff3e3d3fd308daca44d875cb07b86b4c414d36581", + "proof": "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b0adccb05ebb25e9c5ef5845d0889a4052f2a071414df968c42fa452de90a1cf0bd95df28a5eada619929d1e65416894cf8f460ae9ce198153e46c5955aeae93f661ec2f9b1d47239eaa30ad76bf83ae25cf8e731c9931242f09a29502e7d4fad075e3102b0cb409b96d4053c1856c906fc163d4e42f3f9dda08e8e385163e6a68a9368355c45c9cc88f7113b610e9d0ad1520849b32bc17879fbcd56fde8a3fdcd5e08b6140a66ed02193674bad56afa0cb128fe8eb2cd74be21049c9bab89ff1e869e3cd5d9688155314e3483dc675343cd81ab39df3d41babd9d6f0f8da4ab943147848fb190ae7886340ea707299dd2eb7cce908c6303a04ba21a0ae1cac50b88cc4b5f96ee3c620495e184ae1edda10209105fc3a4d190e3321d1d4188d21ce17438c03477166691a44d8c928b9c69dfb5ca51daae7d3a1f60d43e52cbd4d79c193e0a10426711f54bd54ff6ac03c5674e0820f2aab91047d42ceaa951c0f47b8c5fe4d4b9ba500637ad3345f1798fc4ab5db85eb62b1b83f23c42b74e2b9c178cf1211dd9c6450f87b933280b8ca6d4ec91e095a4afb5ae98f6901ee1a320ab13c01f16b978eb5bec8dd599d26767e6454f0279fa32ad7ca820c9a48cf8586d2688832dae80797ea5fd4033d873b07c2a470f9755c37eaefac989e673e2b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765e930000000000000000000000000000000000000000000000000000000000000d3c9ccccbfb76c9930c9ef8db23e1cd0bbb9748514f1ea273c44a21497f11291bb393c1c8312ee479949dacc7a537818de54af74377092e3e84df5d417d64e058e4b448d80dafd28c916e26702083962fbb4085ca018e3aa28870637d9eeffa2c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c51149065ad90281092206f35b08a1a5d63d5ffcb8f067840bed0977f5654e612ee37554649d4fcea08d38bfa378a0ec77bcbf45e59c88d903a062c62d8e85e9752b986287bf4d81cb55bb2877deae9679441216bb08f00962cb73f57e1e428bd1d7df85bf72b7c23a66c35232969f717aa081e9d56a4380dc0317f21ca42fb96" + }, + "strategyValidator": { + "withdrawableEpoch": "18446744073709551615", + "withdrawableEpochProof": "0xffffffffffffffff0000000000000000000000000000000000000000000000007f0157dbaed21e908a3256e9b89b195118105ef0443f67c117791384956ac9c39c99ac0c4ad681cb74639b75f2adb68dbf04c47a56ffc29058007053639f86f087cd11165a08b78d64c6052e3c8a4166bbdbba0c54a3e717083dda42e64150a25a441dc23f5970fc0628098bb12fdac7d83e4cb55d40959dd3ef94612f70bc72dcaa6cf026d47ff5503ff81cf229ede8482964c455afd037c4a4d895aa34a3e15b3d3af83fdcef2d2b9a3382b530c51c90797b76a8a1a68c76a651ccfbc216ae64b04490ab004a39ad0076630ea5ff612064f859d59407160428f193e05b43f29cdc1701dc6f692d9d257625ea6fa11e5c9103d0670212ff73e62e1308d2964605cf6b16ec8e18d4a997726b28dd4b0ba35eaf092bfa01335f50bcbfb2f217873e89a8baaf4ccdbcbc65af53c95a40bf72957f30b2c7174dc06277b75827e3142e76432612b56b695c89cd2eb49df353787280d4340749ec5d6d42fa4158c687287690e36d43d0eec99470400bfbc6384476632939ec76f9c122970d9588fab6595a1f591be002be8d07d50462210c9bfb8c91b3b03af3a39d60117e18a069b42edfcee5b940ebb3349a99816678be658f85a1318e4e330c4f3eaa0efc07dcb1a2d970c5fd25b715e7d796c54c740ef2c80024e02b717c2f66fd4cfda7440540a9795d23036c884adbcad7adf776dc482cad8e384d8b9b3bd1cc8273fe8ad416f7ed37b7721689eb25b2fe9af24a690212b5530a0a331cb5c67b1f4b96d8f8b4d18c12c3085de75bfbea61de72e218a6dd45ddc17016002b0ff281e0c12e084d8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb18c8dad01c2589b871e63706bdbeb93b32538d38b0d0860299cc0f30b50a4e899a339f8567d194e01342a6cc9e3cac1f71a22a45d2ac65c389f7162ffd8735fad5bfa4820abdace730611e0f473c86f0fcbc09eb9d117ad37013a313aaa340538ba9e0eb3293118cc020f29826cf5bff1bbc5e848816c857cf76b27d45956ef18a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206f15c01e0000000000000000000000000000000000000000000000000000000000c6341f0000000000000000000000000000000000000000000000000000000000c4d2b1c79eaefb379e9f3eb772a0e936edd69e74f09cfd0be16cf2c4015f11e06f026ffaa0b651c2ed60586492776ab38b0a314dbc64088d4b0d5e4a647f6fcc47021241fb04448b7163edacf1df1a0b834fffc04f263206cbb51a5e5acfd34a38963c75b358ae02f83f25d2ada0dfff96cb29bb7c8c2bb4f21a202436fb096ca553ec71ef4f4af5839967de126148604e9c45ab2d26bd4da8f4447b16c4b9fdee37554649d4fcea08d38bfa378a0ec77bcbf45e59c88d903a062c62d8e85e9752b986287bf4d81cb55bb2877deae9679441216bb08f00962cb73f57e1e428bd1d7df85bf72b7c23a66c35232969f717aa081e9d56a4380dc0317f21ca42fb96" + }, + "processedBeaconBlockRoot": "0x2abd8e1722638e4a5222a46f57d26f9fe28afaa5b2cd7fb686318d7482505635", + "validatorBeaconBlockRoot": "0x3d4e579819100bb6ed76366757cf3779ce3acaf96d03edf42dfe7a4c84915948", + "depositAmount": 32, + "depositDataRoot": "0xbe9da33f47c1b51c18e8b67586f42ac9a5c3aa92e0ed9ec109bffa5f27de3573", + "depositBlockNumber": 22887200, + "depositSlot": 12108953, + "depositProcessedSlot": 12159812, + "processedBlockNumber": 22937797, + "command": "npx hardhat verifyDeposit --slot 12159812 --index 2014925 --id 1 --test true --network mainnet" + }, + "comments": { + "register tx": "0x094704cb3ee43a69a0a598d61ddd47d502b49252348897edda14c64dafc0a3ab", + "deposit tx": "0xdcfcb446e984b2587c91a04e6272a61c6ad55f19a0cc068c3e9e71c51b527070", + "comment": "npx hardhat verifyValidator --index 2014925 --slot 12202000 --network mainnet --dryrun true --withdrawal 0x840081c97256d553A8F234D469D797B9535a3B49" + } + }, + { + "publicKey": "0x84fa12b65c2d9420561576c1443cee7fa837004135ac132eeb3a406035344faa803e45e23ac491464c850cb43028d106", + "publicKeyHash": "0xfe33090787ba4caa570c4329af7a29a479ef435693fd302a62768459d8a3c09a", + "index": 2014907, + "exited": false, + "operatorIds": [1909, 1910, 1911, 1912], + "sharesData": "0x80a81a1a65a1bb47f0aa79fc1349fedb3e7c022602d8137515659516e29cb8b989ce382863191ddf6f38ab996da43be404c27daac89d07ec873b6ae6a7e8512bed3dcd0cbea9607dd7a2e3d4321ef771f1f1b9f66b35c020d8397041b8ebe6a88bb13dfdabf9b6de94ee54260905bc6d2b9147a8802c08e2d6e9361bb589cff069ea39fa662f1a2aff5045e0f2516957b9fda18e5a8e4a6f8ab9ff36c8fb154d0aab0daa1303b055b650cd9667f8d7c12b8a6b441cd8a3b5c67c154b5e98e228886e82a2a258abbfb6c62ae80d4cb5281140adafdea8e2e5af51dd22065e43be6f87a0e68fc917e534932c92e7982bcfb0f3d8230b643d80c6f0ae07e702f3bf5cc03e09b5db95ae78ccf6ade1f9f5c41891f486339f793c4353211b87790f706adc4547481a31a726cc6d4081d52387d5c6c6e10aa1bd3f2341eadab7fc14343fcf5b33d5de33f242c1036349f22814face272599ff824c82e834386f5c193b1a84b4a84adb47a6132934421911c7415902a80c08ef3f6c0aba7c8531df780c1d538585dc336baaf8ebab3bc17ccadc0919231e325f289796b315c1919c995b5a320dcea272edfe69ffca322a8632ce2b5b4c2306bb3a482bc876629be3af48a64c4ae79091d13288faeab3089afd9c8d8cd8698db933d815801873f472934fae1bd7e24962be3c0a98f5414c331a1313a19f372395f5708a3e2b6aca99effd62b4d3872f4315055e4e28bbb1afb2531de6456b5fbd19f07814e5708e3823b05c645662122d4676c943ec1718ef1f9ca2b134f382fe82a57fd375616c2a0962dd8a6fe0e099cd6d386261978c6be90a84eee5225d81e86d3ea7fec8ed1cd307326db4df55f4d6797c059a7bd81e7aa8f7041f23474f801c418b829acc7de4f13fb9678dc0dbc351ce000f6ca4c2afb0205ed4aa38e7ceec8fb7f8b91b1797e755b0107d4d97071712a960515e075a76ed73f4de107e1b3650258168c907696943a2928044acbe4c964f0ecd05a058a3b41ef92e282f8bf3b4038c843d64931b175e340b4e2d1ce7cc25032a12877bb02886749a6953f43630e4abe60827322e41b312f0cda786af5afa7c4681f935ad131266d9edac6c3e30c223a02b17279935d779a6fe54102672227d6437b1ca2bae3346a0877a55f0925ee9430854156eabcebfd2783707eeccda169dbaaae45d8da862d93630d2a72dd620bf1bd2723e768ba65e336aca530d79a430b495bf211f0e7800fb7a49e065e7ebe5138acd99d57f59111f1f2d83676a30ec3d908f319b301b9d538b15b8ed934c32e3b44e837735b59a694e00482f36e1cf0c6f67bd253b3a67c0cf6c694510a9912b2397dad5c2a235d0328bc571932dac2323f9d6bc64cce2a41d066e4dcd1591640a4fe4bb2eb8c489e6d18bd02e9e43bdd7b458dcedd432ce8d06ede873bb8618a47f01bb3536a7eab491fd8e1f534eaec602110e21dd3c2a2005246f8ab8ecc302d9ea3bd22fb4ea527d8b2630ccc4ded2e28156ea654419c828c82abaffd09c00957904172ca895cded60521b0dd903bc78f0327e22fddb3dbde10c193534dcf7c7c14cb9ab0bc7a1dd1b353e4ae34d12f7270118f3654b15372e698d09621a6bc4a8dc247e4a29006a8a9d817f1d581f874babe8c54874425f3bf54f55473f44f489d89a8cc44eabcced65eba61c8f699082babbb112e0691e831e77da3e2ae562adf2f7a17667721f815eca10bc896815f7e8f59b4819944e8d54c8a5e3973ebdb07af79f09c99324e9ed7ddef8f297a2122cef819e863851656d54c4a6ab0b8742f55c9cb54cfcc5aa07f95837ea4324a067c09c4a3e4ff6a0a1044d511641a422", + "signature": "0xa6994b8b3a8975effca1e6fa4874f8a7ba165da955cd2d582f4606f0545e7baff5393b7a8f8c07680d6d986b102cec600bf703af5008aca3e18f1a74e50072529e4e2b03715ef672f0c6354279eeb438ef4280239a6487c5e397c742fe7e74cc", + "validatorProof": { + "bytes": "0x020000000000000000000000840081c97256d553a8f234d469d797b9535a3b4919327cb9763c96e00332bde93bdbb1032c4b796dda73e515c8c5f7ede9a419beab64dc98c2a231241960381f8dc711b1cf9cc4aebe0beb68c2f3d16fcf457fadf76a8062752828f4919769a536eb3544dedd0eda36fe6d95e5a6a84a544a28855c6115d3001d3dcbbf40f33fc9bb2b48462cfde09fffbee28b7470f5baf224c08a73b7eae185c63235814ce38b3ac0514a7edf75f09b5a39cb0e80bb69c3b36084546b2d7c0b85a445789698888a6a04b1390301610bc8fac5d89e57cc82882b3b897143a2ca38ac656351be92b70360fa15195369ac084fe42364be690f79aa916fe0ba5fea086b9cbddc9712b1eb6088876966280c4c514ff9914ca9eca95d2beffdb59299a590ff929463822bc9f847159144e0eaa9068b8f4e480c8643c53e89a8baaf4ccdbcbc65af53c95a40bf72957f30b2c7174dc06277b75827e31429d40bf3b279722ca18134155adf22592e16a793e8e4ca2fcd59707295c896f1ef518ba28d88abaf01fa978488a97de5359669cb121ed43c1356b434a538190c251b8646e24530fc13244222c56e0735a017bff9e444ae694cd960d1426172b0ff9280840c39feaf86b8475d556d170c3df366d41f8bf7975a799afeb5b9c09532fd2d7a4ecbcd6b7542f0e8afcdd8d01bfe5ee455a46f9f28f81bad6464e2e344e63a8d2177cffdb23a6e0d66cbbc1606709591a0305d7e0997c281c844102f807a05e8265d7fb12b34ccf5cf0e988e6ef1ad6a39098fc5bf04099e8dd51c66396f2e5ec8e570ca58a3ba4f78aa8c9b22614b299571cb32fb49b9b5506cca408fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb48135f37c0a5f02c0cd358938dc3c24eb9d0158283fc328ce0f5176c040bdb043d7f638fde7f712618272899e5f6b465b016f1031b4a5710c32df7b4b451cccce2f559cd1a5000ecab92f2f9407e3ac6cbae7166e187688f56ea62c72d59096c5ce5e37b4d4a8bf060fbb61e86e16ea54068bc9c4aabd4273d4d76bf1504d0d18a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fa8e01e0000000000000000000000000000000000000000000000000000000000c6341f00000000000000000000000000000000000000000000000000000000006d833de4787b4b14e2cf638da50c1707c9ff3cb0dc7a45a2660b4d3458c7efdfb6c721321dfb0e529649f74be64a31bbb7d70d337f37a56b020ecc32893d1dec899f8e0893bc267e3256c7542c6cdead589454432274d6bfdfa33e5a4372c0408332dd055e96fbddab4824219c99c6efd32f62a94680d7d7fd9a209136498a235d9eae31ca1126c31a097992d1aa46e4959dac388d050aa45b9ba26dd04c29d71e55542dbbada164392f6f61345af3b028848382633277ad75a17af787f75b843767942c6c4d511c58ce23665f710e5206efbcb15a207e9f7bc0bb86306f7409c256512691d51102f5d48c78b0bee220af931edde3dd49847fd7675ad6ab7fec", + "root": "0xa11075ed2553759aa1bd4a2749e8800cb20daba65eb2d7507dc4e707129a71b6", + "nextBlockTimestamp": 1753248035 + }, + "depositProof": { + "firstPendingDeposit": { + "slot": 17415809, + "validatorIndex": 2015253, + "pubKeyHash": "0x808f9b9e414414066ef6db5ff3e3d3fd308daca44d875cb07b86b4c414d36581", + "proof": "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b0adccb05ebb25e9c5ef5845d0889a4052f2a071414df968c42fa452de90a1cf0bd95df28a5eada619929d1e65416894cf8f460ae9ce198153e46c5955aeae93f661ec2f9b1d47239eaa30ad76bf83ae25cf8e731c9931242f09a29502e7d4fad075e3102b0cb409b96d4053c1856c906fc163d4e42f3f9dda08e8e385163e6a68a9368355c45c9cc88f7113b610e9d0ad1520849b32bc17879fbcd56fde8a3fdcd5e08b6140a66ed02193674bad56afa0cb128fe8eb2cd74be21049c9bab89ff1e869e3cd5d9688155314e3483dc675343cd81ab39df3d41babd9d6f0f8da4ab943147848fb190ae7886340ea707299dd2eb7cce908c6303a04ba21a0ae1cac50b88cc4b5f96ee3c620495e184ae1edda10209105fc3a4d190e3321d1d4188d21ce17438c03477166691a44d8c928b9c69dfb5ca51daae7d3a1f60d43e52cbd4d79c193e0a10426711f54bd54ff6ac03c5674e0820f2aab91047d42ceaa951c0f47b8c5fe4d4b9ba500637ad3345f1798fc4ab5db85eb62b1b83f23c42b74e2b9c178cf1211dd9c6450f87b933280b8ca6d4ec91e095a4afb5ae98f6901ee1a320ab13c01f16b978eb5bec8dd599d26767e6454f0279fa32ad7ca820c9a48cf8586d2688832dae80797ea5fd4033d873b07c2a470f9755c37eaefac989e673e2b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765e930000000000000000000000000000000000000000000000000000000000000d3c9ccccbfb76c9930c9ef8db23e1cd0bbb9748514f1ea273c44a21497f11291bb393c1c8312ee479949dacc7a537818de54af74377092e3e84df5d417d64e058e4b448d80dafd28c916e26702083962fbb4085ca018e3aa28870637d9eeffa2c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123cda37ce8071cf9f601659482403675576ee061c9d23709df321d4a4a912920a0586c6b7a1e75f0456180175d95b44e523d036daab910d56d6f0d003b59dfe25e7a99878c89dc02061770337c9005bb734e664d044bb284e913a6629f7bec779a514373b92618401b378e28eae9ca6839557b7e89d21d763707adb43a4021202e2" + }, + "strategyValidator": { + "withdrawableEpoch": "18446744073709551615", + "withdrawableEpochProof": "0xffffffffffffffff000000000000000000000000000000000000000000000000ea0291ee7977795bca0a98f1f343cec641747f5a8ebde52081c86a6b93d18f30374f5214f925acecb13bdecc271e7f833cb85b2d29e20e75420cc9fbabea5c52f76a8062752828f4919769a536eb3544dedd0eda36fe6d95e5a6a84a544a28855c6115d3001d3dcbbf40f33fc9bb2b48462cfde09fffbee28b7470f5baf224c08a73b7eae185c63235814ce38b3ac0514a7edf75f09b5a39cb0e80bb69c3b36084546b2d7c0b85a445789698888a6a04b1390301610bc8fac5d89e57cc82882b3b897143a2ca38ac656351be92b70360fa15195369ac084fe42364be690f79aa5f50ed0ed3e909bcd4400ec27dd38472c516c2ffbdbe5dc7909ae8c51097cfbf2beffdb59299a590ff929463822bc9f847159144e0eaa9068b8f4e480c8643c53e89a8baaf4ccdbcbc65af53c95a40bf72957f30b2c7174dc06277b75827e3142e76432612b56b695c89cd2eb49df353787280d4340749ec5d6d42fa4158c687287690e36d43d0eec99470400bfbc6384476632939ec76f9c122970d9588fab6595a1f591be002be8d07d50462210c9bfb8c91b3b03af3a39d60117e18a069b42edfcee5b940ebb3349a99816678be658f85a1318e4e330c4f3eaa0efc07dcb1a2d970c5fd25b715e7d796c54c740ef2c80024e02b717c2f66fd4cfda7440540a9795d23036c884adbcad7adf776dc482cad8e384d8b9b3bd1cc8273fe8ad416f7ed37b7721689eb25b2fe9af24a690212b5530a0a331cb5c67b1f4b96d8f8b4d18c12c3085de75bfbea61de72e218a6dd45ddc17016002b0ff281e0c12e084d8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb18c8dad01c2589b871e63706bdbeb93b32538d38b0d0860299cc0f30b50a4e899a339f8567d194e01342a6cc9e3cac1f71a22a45d2ac65c389f7162ffd8735fad5bfa4820abdace730611e0f473c86f0fcbc09eb9d117ad37013a313aaa340538ba9e0eb3293118cc020f29826cf5bff1bbc5e848816c857cf76b27d45956ef18a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206f15c01e0000000000000000000000000000000000000000000000000000000000c6341f0000000000000000000000000000000000000000000000000000000000d6c291de2e0d7d449a9616e18c586905a3b6cf157f6a0dcf2098d63e4e60ffa91ff039a5abf663581be792e87e09a2cade103c7005c7cc0b820a955cd83c1abd2943d7cf9cbc10091c98931969eb329fcf1cef7886a459eea4c2e77c6c66938bb79b8a25ca0309bc13aa0ac27028bbbd31922d403cce18349d637daf63c7481c7e0b0df0561379189d01099fa5c0bad2ad4e8218a9ff98f9d42312dde0928a3286c6b7a1e75f0456180175d95b44e523d036daab910d56d6f0d003b59dfe25e7a99878c89dc02061770337c9005bb734e664d044bb284e913a6629f7bec779a514373b92618401b378e28eae9ca6839557b7e89d21d763707adb43a4021202e2" + }, + "processedBeaconBlockRoot": "0x18d4c3a4c766f8f400189873ec9ffc8c7bf3a3c1c3d0bc853ee1eb6aaee11e27", + "validatorBeaconBlockRoot": "0xa87dc7c3c48e1eea514c3004df48658039f04553f390ffa8d229ecf325b1b598", + "depositAmount": 32, + "depositDataRoot": "0xcfad7a8826027ec8b6bdf53a0457be4f9bd6bf4594694c57f43c5b5af6d6d221", + "depositBlockNumber": 22886981, + "depositSlot": 12108732, + "depositProcessedSlot": 12159809, + "processedBlockNumber": 22937794, + "command": "npx hardhat verifyDeposit --slot 12159809 --index 2014907 --id 1 --test true --network mainnet" + }, + "comments": { + "register tx": "0x094704cb3ee43a69a0a598d61ddd47d502b49252348897edda14c64dafc0a3ab", + "deposit tx": "0xdcfcb446e984b2587c91a04e6272a61c6ad55f19a0cc068c3e9e71c51b527070", + "comment": "npx hardhat verifyValidator --index 2014907 --slot 12202000 --network mainnet --dryrun true --withdrawal 0x840081c97256d553A8F234D469D797B9535a3B49" + } + }, + { + "publicKey": "0x8eb8285968eda6d74b438be03a9f91960b6a315814199c9c6528ed30ff8d33a09a440afc503b98ecec0a7a4f1f470219", + "publicKeyHash": "0xf492e7b3e1b7b3d745bd5b543da7093eab0e7e68ae547dd7de2737e691efdf71", + "index": 2015000, + "exited": false, + "operatorIds": [1939, 1940, 1941, 1942], + "sharesData": "0xadbf47c6f6917e72b1878befc014fe029d8e1cddbf253fcf65cfc20010aed5ee51ed0fee7c3cf79b08bdf5427093533e083459bc36425f35af4c0452e988cb683f681ee658d96a43ac3d64c8a51ccc403665a64b7e2185630ef193aa72bac53287ff3bdf7fa8ceccff6d7d71f1da23ed46672e03aa9812036469cd14736ac22d2171ae3b53c5227517afc3b48b790af8aa3a9887169e271420d3211d5d9815fc04239efdfecae3cc6baf7f6fd7fe8a27567f1de44b203c0ec85b11f7110b119b8c95475fc353b03b6c88fdcbed0df74aec5c27b8e134f4801742330f6c57f09807102be0a4a36a5df204359c1f960029a9d7a057abcd8f3baa96e04b22643b7275bd8738fa8c9d1f8fc161a1bbe613303fd215a2769ee1568eccbea13df24b2b0dbf2b94ed386ea22d06fa1fa9821448382c66c5b1645bfc2c822c69fff83679127cae328cb3a71335bf7023506ab21a0245e50eb89fc03fb6594d5f883759599fdda1ee4d7bc44d9f9860624d792d63a12efd4393efd6a6b47de84eff9ffbe6160b03389ac0743b73b3278bdbe7cf301c86ec4d00578214d3630c70b61ed0b7dc678129a5d48698e713d73c85afb65e7b01365bfcab3a5b6a2a8f8de3131c6cd786f1ad47f7c24ce08f0faccb4fe9a6a19e655d3ab4959c9878b6ec63f0a1fee986e6bfe0ec48df251595f79d8a6613898edc1b94350d2de9e36c75959b9708859977ba5b12bc4006309ae8448372f3b1dad1ddc4200cc8bcaab34004cf7e03aab467c5e5cbe7cf0c60aa5ea617b01809b534493798cc83d01fbdcb677b697d37cbeb2eb8b1a78cf82484eb2693a7fa2490444bb96b5c649a0c94cbb09ea83002d6ac8fef88c40fb7b7f3be4b5b2a4f759f8b51620a0a03a440c0e6f5d51ecf2e4d3575f71cf3945fdd26d9f5c4182c40c1ff747260c548ae00a410d9b8b44d9d385861041dfc5d911f0966a605a34add9f445eab569dc0a1154093557173da4be2edce05a9b59e4a2da2538d005b9bff4eead3c6bfb9ec6bd627ea984d1f96f1e6eef3e1dbbfb44d4a352f80120b5e3791306515b3e47c7ce97c8193765f6e8ff1a0f71aa388b93b1b1b1e44f7a119281f1aaab762b15d7d832636d93421ae04567c80ce03d9194ca0b78ac5112767c2af3a2b1259624c9959a0fb372723b403e81d22e1c8af2b2c4bde806a31974e63522224964a524b4223931123f8e469d278f9e0114befac307cfc4a7463f251c4f92145b479a3423126c5747dd234102289eff94f5efe3fc836628b8fdba6929d11f786a24b9d1855d08d15ee9fae328f974765d63f80f48f41d68282bda4999c2b6fa1416892337f83ce98706ba948b29ed120f81cbc10fe4be130bb9765332c31faa5842ef31ff04acc331a6b8b303a527ad0db63462948143dd3274b7be1e463dd647fad7d34f5d34ecc5871ee64b2c1e4af68de689f51fbae5b9bca673dd37f61793d3f5c2f60509fece5b5e34d5bf48f6a65496679b74a8fd0c30fbc4d77379b8f02e97ba100f6da2a1e1e5c540081b72995a7823e91fcc97aa88552a95f00868d12d44c4594c499f5e470332dfcf6f5b789182ecd61f819f7b08c1b0182343cd3b3a836d3cbf688ceeb5a77ad61e1e79a264e4b8e7f5282101757ea3d4f295f055df953b5f54760a5a508d0575e25606096559d5d13632b291a65c20143b00b06bcbfc9575efcc8a6e1d066e205ffd8cec05c0bf8cedb85ca957e538485a63d51298397a24223787a37889c5f756145c8c16ab1b9c71293f468f24cd2ac23a3ba68689713352922d427393db46ac08fc3a11a85a8e787d16006a00bd2d6cde84322316d469b6b635592fc5342", + "signature": "0x8bc9a4ac00df4016c071f19bf4971c5edf3c677c9f5546fe29adcd6119e461cb0aac3d21e44300e815cac4a8b812a6fd025b9fee5351eaf21b42730fa22cb73f75c28d89cba20b7b4ebbf697cb1456a9e4e533bd97b3aca8d01bdaadc14eeb8b", + "validatorProof": { + "bytes": "0x020000000000000000000000840081c97256d553a8f234d469d797b9535a3b4919327cb9763c96e00332bde93bdbb1032c4b796dda73e515c8c5f7ede9a419be9681c12ab572a2194b200e682f823424309dd3d5461f99172e09e2c6feaaad23c00df9141c471f1bf116fd531cb85383965482adafb42b6cb22635f3ce9a4afd520b81f7c60519bf8997b298a7e208264becddccdcb4069e765d4dc79e32449e4f18814c75c97760a36ee51aa8a9c3ba47f3c546dd95efb652459d5624aaa3b0a619cd0a73d691ee8c705ac9fdbdc9526ffe5ff195a31ecc7ce7c16f6c9bc149b000b089f827c5d40b40d00ad6f1a3c00f55ff8e5f85a1ad56fa866627abe5d4be820a5a40ce9782af7ada1d96bc66ca27c8b2df685a56bfb3f3718ba7a177400397ce5afea8fdb145e52d9e4485dcab61ac1491c9ce243bd111b21dbb80418de01d363bfffa16640b68885f3d77928d17941ccddd15462128a86a9c94de78c513ee3535a4262d1ea2ccf0d0ca9b013a386b6d7c57962f49cafeabeaea487293ef518ba28d88abaf01fa978488a97de5359669cb121ed43c1356b434a538190c251b8646e24530fc13244222c56e0735a017bff9e444ae694cd960d1426172b0ff9280840c39feaf86b8475d556d170c3df366d41f8bf7975a799afeb5b9c09532fd2d7a4ecbcd6b7542f0e8afcdd8d01bfe5ee455a46f9f28f81bad6464e2e344e63a8d2177cffdb23a6e0d66cbbc1606709591a0305d7e0997c281c844102f807a05e8265d7fb12b34ccf5cf0e988e6ef1ad6a39098fc5bf04099e8dd51c66396f2e5ec8e570ca58a3ba4f78aa8c9b22614b299571cb32fb49b9b5506cca408fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb48135f37c0a5f02c0cd358938dc3c24eb9d0158283fc328ce0f5176c040bdb043d7f638fde7f712618272899e5f6b465b016f1031b4a5710c32df7b4b451cccce2f559cd1a5000ecab92f2f9407e3ac6cbae7166e187688f56ea62c72d59096c5ce5e37b4d4a8bf060fbb61e86e16ea54068bc9c4aabd4273d4d76bf1504d0d18a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fa8e01e0000000000000000000000000000000000000000000000000000000000c6341f00000000000000000000000000000000000000000000000000000000006d833de4787b4b14e2cf638da50c1707c9ff3cb0dc7a45a2660b4d3458c7efdfb6c721321dfb0e529649f74be64a31bbb7d70d337f37a56b020ecc32893d1dec899f8e0893bc267e3256c7542c6cdead589454432274d6bfdfa33e5a4372c0408332dd055e96fbddab4824219c99c6efd32f62a94680d7d7fd9a209136498a235d9eae31ca1126c31a097992d1aa46e4959dac388d050aa45b9ba26dd04c29d71e55542dbbada164392f6f61345af3b028848382633277ad75a17af787f75b843767942c6c4d511c58ce23665f710e5206efbcb15a207e9f7bc0bb86306f7409c256512691d51102f5d48c78b0bee220af931edde3dd49847fd7675ad6ab7fec", + "root": "0x15e3bc25b5efc523e9189afcf9908eac8a8a3e786169016e83e2977847afe900", + "nextBlockTimestamp": 1753248035 + }, + "depositProof": { + "firstPendingDeposit": { + "slot": 17415732, + "validatorIndex": 2015229, + "pubKeyHash": "0x97175f4a6663fedfd3c61d322ce6f207a8b1106a95abd0f5d7ec8d16a7814b91", + "proof": "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4bcfdadc154a68cc57770c893853c2b6b05e853d1138d3f45b2823119887f9f8cfc5cabbdf05736941766dc307d51554987f0e065d6c3d848c2893a7261d73b115e9ab7dc06310aeadbc24647de2f1aa977484865d1eaac6417c2a76bd687329acb21797eb515ad77f0a3b8db6fec8d9cfeb3e15f8146e5be5d1c2da21f90a2da3ff4c6b4c3c03f6d3e7e4a804fef625d137d306f95ff4932435ea4bf5918c2656d9e8fff84bdf7d0fdc78719c7b25bda3901dd36c5e35023c67df6f97d33efb3abce0036b1877d50eb9e075ff002b735455110864a90d1addb541298c446756b7b6542d383980599e441e309389ba11b464bc7a515def3bae0e4c5629885420b225c63f12d1f589c3e34a363ca81a08cef75e50d376a277f0ad7aaf95f6c3c5797f130225d97a7b3fde3bf1bd44a42f0c45d0aa99486168bf243931db5c307945ae7d32a726540984c9858b2692bde746ae45857a7ce6706d5419f9357caf7e0c44fc56790b403b988c854e236875787ed0f2e8c8aeedcec0b5e757f8b158f0da95ce886b08b285c401145284c6862c0879460793fd8496cc986d63530008cd6ee0a6283ed79818fee58d33861083e2db3815860177162fbd31f98ad482a715f02f0356c085382172b4b57ae7d14a71f099e220e723dbcb4f9d089c9248461c21b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765f130000000000000000000000000000000000000000000000000000000000000d3c9ccccbfb76c9930c9ef8db23e1cd0bbb9748514f1ea273c44a21497f11291bb393c1c8312ee479949dacc7a537818de54af74377092e3e84df5d417d64e058e4b448d80dafd28c916e26702083962fbb4085ca018e3aa28870637d9eeffa2c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c1818a21d9d23345c368a2d466fcabac5c1b1df0e1168037b685f6200ef0504db47d261f1d332ed8ddea1eb5d69020ba5c810cc429b004c70bf6e0411845a605d75c42271779a7317f42a89e73a7580e5850dba727c8e1f2932d4c95be6370b1be2033b6dac992a11145b407f19cf6327247f2ee9b59b3d60e35332306732e558" + }, + "strategyValidator": { + "withdrawableEpoch": "18446744073709551615", + "withdrawableEpochProof": "0xffffffffffffffff0000000000000000000000000000000000000000000000008838e039d069e1c72127b7636b0d20fb31476e44fe1f4ab8a27265b7047fc8779cfef3c5d9401027ce6e9f20a9ff23f7d36a8a141f4b506bf59dd1c9e5dd9910c00df9141c471f1bf116fd531cb85383965482adafb42b6cb22635f3ce9a4afd520b81f7c60519bf8997b298a7e208264becddccdcb4069e765d4dc79e32449e4f18814c75c97760a36ee51aa8a9c3ba47f3c546dd95efb652459d5624aaa3b0a619cd0a73d691ee8c705ac9fdbdc9526ffe5ff195a31ecc7ce7c16f6c9bc149b000b089f827c5d40b40d00ad6f1a3c00f55ff8e5f85a1ad56fa866627abe5d4be820a5a40ce9782af7ada1d96bc66ca27c8b2df685a56bfb3f3718ba7a177400397ce5afea8fdb145e52d9e4485dcab61ac1491c9ce243bd111b21dbb80418dcc093aa3b5f5038611c993aa1c001d2c311e1c34db5eb9ed4981b2413e5c403f211100812ec8b70ce49e936cc046ac37252775aade28a3201adce9ff3c740b8f287690e36d43d0eec99470400bfbc6384476632939ec76f9c122970d9588fab6595a1f591be002be8d07d50462210c9bfb8c91b3b03af3a39d60117e18a069b42edfcee5b940ebb3349a99816678be658f85a1318e4e330c4f3eaa0efc07dcb1a2d970c5fd25b715e7d796c54c740ef2c80024e02b717c2f66fd4cfda7440540a9795d23036c884adbcad7adf776dc482cad8e384d8b9b3bd1cc8273fe8ad416b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d18c12c3085de75bfbea61de72e218a6dd45ddc17016002b0ff281e0c12e084d8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92bebd03f77f52c670e31f977b6cd15d133b887b7e31829f06fad2fa7e7e14afad68a4f222ae948959388c447091c6a4b899545164594ea6e404f2ae6efc64cb2f1ed3dacee679929b128ba5c7daba8cf2797a042d378e8eff90e4cacd56c31c46c116fd302451902fe93af6c36d930d1454ada1c1d350d25300e0a73626dc95d37438a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206ffdbf1e0000000000000000000000000000000000000000000000000000000000c6341f0000000000000000000000000000000000000000000000000000000000467ec7b4fea4e48e4de728e1d03c873a74db3e72d1dbe30c146c4d8be76ea081877fb69a28c348b443ef3d510c2061243e5adb55fb60ff009b19dc2de4d49d3a396fe1b13d43c5721d86aa1082dc3dcaa6864815f648186203d4d5e8906c27c083128bbb3702ab100a9310e6a0b99768f609381f2eae9472523bfe7b95a58d48d5b9b0476864e509fdd295000df7fe604955548b87962dbe3b983e0d34b9b93f47d261f1d332ed8ddea1eb5d69020ba5c810cc429b004c70bf6e0411845a605d75c42271779a7317f42a89e73a7580e5850dba727c8e1f2932d4c95be6370b1be2033b6dac992a11145b407f19cf6327247f2ee9b59b3d60e35332306732e558" + }, + "processedBeaconBlockRoot": "0x37a15c05fc6b2162be094a3ffbf55511f1f0c315a7702684d3eb7f91bfa9fc74", + "validatorBeaconBlockRoot": "0xfb8b8a3e3019918bd0295ee438ad884f9f57a3ae21214e2eb6d57e686466529f", + "depositAmount": 32, + "depositDataRoot": "0x707f088d099ab3ad9352cccd44a0a5c22a641e9a79706e64e616c45fca986c06", + "depositBlockNumber": 22887020, + "depositSlot": 12108771, + "depositProcessedSlot": 12159732, + "processedBlockNumber": 22937717, + "command": "npx hardhat verifyDeposit --slot 12159732 --index 2015000 --id 1 --test true --network mainnet" + }, + "comments": { + "register tx": "0x77a3d3ee1898ae6310bce5719ee883fe68deeca232c3dcf6ca60a396c64b4c29", + "deposit tx": "0x4d2ec847b4e52285b10e3dcbd8f28b7bf37d84b71d359272249c38973ad8df1a", + "comment": "npx hardhat verifyValidator --index 2015000 --slot 12202000 --network mainnet --dryrun true --withdrawal 0x840081c97256d553A8F234D469D797B9535a3B49" + } + }, + { + "publicKey": "0xabe9ff4f2db1b1db0883cf9e60434ea8c7d228e729255f892fd1526f02cbb3fe204c26009ee4db0ddbc9b05eea00b6f5", + "publicKeyHash": "0xee17c110f9f1d99714260f55df6d4067600b0de2c9c305371c69e01c94ee28e3", + "index": 2014998, + "exited": false, + "operatorIds": [1939, 1940, 1941, 1942], + "sharesData": "0x8dd01a0c68ca87f7342e85ffee17b85c73c9be11914821138ff9568657954dff02f9923d9c9e635b66b594f35474a5cb112e2a605924a208a7376c0b990084a78a10fb101623d74a0adb626b03e5d876270c3e99b9dfab9b7946ea28480012418b5c50a1b969b814d6bf8ae42d1879bbd32249caeb8b20267b63c64fc02ba1b76e6493f97116593325cf91662edf42bf8f3384209b4c4e59cb82c2e0f063323e44f97ad6d94d6f4a47933b4f90fc6cc83c4746e36e02c4951a40863210424c69a6741790bad6942a850ad25c42429e34ff12023a348912c0ee4b8786613ab2224ad260a125315a57a167cf295c81154898a36325423b351a360efeda243c8a0d8dc0b951e6dc8f0b258bddd63df1fe1ddd70cbc71711441fcfa9963092ac739d40a4d72e8b569bb01d102130ae87ac02be8198e0f736af847ec93f654dbeaed66eebf7d0c4eb135759b5af811c2e0ea88f6d7899fcaab564f5a849ec058b435c83239b5aeaac6db1636820429a7163b09e35c893f841a70aae8ad260dba54c8f653d54c04e38d397fd3b18d2cf54678fe8515e8a570dec55eb422e4fe65fe94fc023d2b6a505d46ed538475a06d66d2b5206bc009adc9529ab24bf78bccf40287d4cde4cc28d08fc759aca3c80bb15e265cee1fb84d025e9fd73aa161987f31e7d1576f606d5cbda25a4210eb85fb4a4106c9dbb0c6c4eeaab9ef067bea27dd49b028b34d51a3febedae78ab400e964fb4702ae347f63bba5432a407844ab4ff6a5d4dcfa66ac8a065f1fa002ac07e4087d618a98ee5495cc79e79ace7d1adb223208cbad8d14a77173b39509ae1e2c52c2aa3bb834892d209a4ebe4113e0db3179fb3e8be18e850d949c6d8329e4d304fde3b54ea2f74cf5a8940b3e9e27eece6f52a49d3e00a3c8cfe3da7e399ff477bf47590513a3068b6da28ec2bf4fd20c1bbd78e19b239edf03c335a360344e22d560e43b9626af0ba9fa13b7eaec536cad36485c28b6635589cdb8146a6a4ba259180be16501081458bd7d93f11a902456f56ca7499a85d8fe5167a6dc97504014e94d48991822dc68f965226eef722eae1288cd8eb3f1d1475a3e80b3343827fdd055c39a645873fa24713b97422723c0522c5fde35cf0d10935f7ac9b485cff93f5fe0427aaee570ec055ead4c078892a37ea40c7bf8b8abf1dd9b135427e2ccea94e758ddbf8bb091b84f7328c5116ee4ff1adae95e049be7137bd4f0ea3de7dac3c4302fed46ff99d904bd41395aeed7e5ec63fd409d339d5f832364c523a836af106fc8eb6be608945bffb37f9c09b460b167710be4e17ef9dff02981a19036eca3b48367c501e282e086aa62df229b42c863f7875886a99e22a7f7f984431e644812d684d5264a13cb28e0bbdd068a52a1695f6055f2992039fe1a3b96cbae92586229d42779c2d941abe47c019f9030d31ce1e32f078e981e87e8f1d25ee3ea1355a5bed87fbc225aef4cf1ca28639d4b99f42419267c9397175daf5c658d26eb5b0f978a95f3e6976a33dd88e7cc6134c9edff27df7df10f81433f019356afb7624977bf12f23ae4b2e26dc349a88b2674b9e0b7a0f963d3974373969cb270a26b63a96b4e0cebd8e38d47eb66ea43f694d12362b860bb42b1f39381ab3c54806cf8a73e54ab0e033062e9a945669495554213a8dc24241082b50a4bb7696aba2fdfd2ce5efffb0952b6048b8a2e684d2bd768402d565a497c44f79ef25578b3fd17d0830897cf433bb90d6734d8014b281c848052f627dad22db65d3deac5f2d6c30fd0971bb01e8170c857592dc081f28f69a07b483c895ae721fd44250f8c2ae7659bbfb64ac2ab393de", + "signature": "0xa274770deb3997d748c30a5d2e83f2213099d52a63bdf6874f80da10578bb24904e4fb678b5e97eee5ff6abecdadb3c9006f306147121b02651f2c00056ecbc7c1d5aff16c9dd58a398ff38ecc46adc588b33d3dbd9774204e0b8b80698f9d91", + "validatorProof": { + "bytes": "0x020000000000000000000000840081c97256d553a8f234d469d797b9535a3b4919327cb9763c96e00332bde93bdbb1032c4b796dda73e515c8c5f7ede9a419be9681c12ab572a2194b200e682f823424309dd3d5461f99172e09e2c6feaaad234761a518eff950ff65fbd3493858642bd9da6ec6b619d93c1f033193902d85a6e54415160eb9103c28b010a7f89a47e5d29756b683cd1645ffe13cf146a78652a3eb3b2c8f6eab9c7a3cb583626552b1e2cd7ce75900cae4c848743792da158a933fa111c282b76756f16754e5460c6bf8c39292c3889979ded14b1b0653069fb000b089f827c5d40b40d00ad6f1a3c00f55ff8e5f85a1ad56fa866627abe5d4be820a5a40ce9782af7ada1d96bc66ca27c8b2df685a56bfb3f3718ba7a177400397ce5afea8fdb145e52d9e4485dcab61ac1491c9ce243bd111b21dbb80418de01d363bfffa16640b68885f3d77928d17941ccddd15462128a86a9c94de78c513ee3535a4262d1ea2ccf0d0ca9b013a386b6d7c57962f49cafeabeaea487293ef518ba28d88abaf01fa978488a97de5359669cb121ed43c1356b434a538190c251b8646e24530fc13244222c56e0735a017bff9e444ae694cd960d1426172b0ff9280840c39feaf86b8475d556d170c3df366d41f8bf7975a799afeb5b9c09532fd2d7a4ecbcd6b7542f0e8afcdd8d01bfe5ee455a46f9f28f81bad6464e2e344e63a8d2177cffdb23a6e0d66cbbc1606709591a0305d7e0997c281c844102f807a05e8265d7fb12b34ccf5cf0e988e6ef1ad6a39098fc5bf04099e8dd51c66396f2e5ec8e570ca58a3ba4f78aa8c9b22614b299571cb32fb49b9b5506cca408fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb48135f37c0a5f02c0cd358938dc3c24eb9d0158283fc328ce0f5176c040bdb043d7f638fde7f712618272899e5f6b465b016f1031b4a5710c32df7b4b451cccce2f559cd1a5000ecab92f2f9407e3ac6cbae7166e187688f56ea62c72d59096c5ce5e37b4d4a8bf060fbb61e86e16ea54068bc9c4aabd4273d4d76bf1504d0d18a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fa8e01e0000000000000000000000000000000000000000000000000000000000c6341f00000000000000000000000000000000000000000000000000000000006d833de4787b4b14e2cf638da50c1707c9ff3cb0dc7a45a2660b4d3458c7efdfb6c721321dfb0e529649f74be64a31bbb7d70d337f37a56b020ecc32893d1dec899f8e0893bc267e3256c7542c6cdead589454432274d6bfdfa33e5a4372c0408332dd055e96fbddab4824219c99c6efd32f62a94680d7d7fd9a209136498a235d9eae31ca1126c31a097992d1aa46e4959dac388d050aa45b9ba26dd04c29d71e55542dbbada164392f6f61345af3b028848382633277ad75a17af787f75b843767942c6c4d511c58ce23665f710e5206efbcb15a207e9f7bc0bb86306f7409c256512691d51102f5d48c78b0bee220af931edde3dd49847fd7675ad6ab7fec", + "root": "0x552b9d0d552d576b28de5b441fad79eacdb9e285e1f391f7a1e468157a97e6b7", + "nextBlockTimestamp": 1753248035 + }, + "depositProof": { + "firstPendingDeposit": { + "slot": 17415764, + "validatorIndex": 2015237, + "pubKeyHash": "0x145533dc11fa92fe93361136043213fb9eb64a6ec97899a6e36d26a08857a67f", + "proof": "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b31d6dfe84a51b901fed2a109297f2645f67e052499f8e46ab50be03cc63d101d23fbb857a5ab89e0b1a29da5b729e2cb2904bd335b65efc3bbbdb749a87918752efd32a291a5434943db6bc2c84e23cca3a08c622a60f4a4c37f0da640eba0b70bba2af74420cdf7cb8e85f101d9a877c6ede50ee0efa006f9d86404d14b1e0b5bb887859518eb8f8bfe67ba5f6561e223c8a3f0b71133dc53c90c4ee28a3618e79e2ebf5024aba44413f320bf3cd198f4c9648edb1630fe0ce59d8fe386e32f722823f41b25c8dd5a0ce83a63b5e3dcd026fa9e3bcc9cfa91597dbf62db72ab1408abaf2019450ddf2570d943df31960debdaa6fa9a687f08f27b4a385bc6b02fb8d31f74058a586949ee40d54dad30029c520b2142c2eb049429f56fb7b82c8571c3617dcb0567d0d55a831812cd2f8cb55d61d02b6cb4abbd21dd67cb9ba3d34924ffda933e8cb427b31cf8ee1b62f6697c4481dee0fddc24d75bb981f48daccc63ef327032f007fd4a4f1bbfb6c3d136ee1dd77590b1197f13bbb6ed3cdf62707ff649d378323174bc6949b33d44a20357491198f13b6d01926b945bf552b37dab1ea6d10a2f910b629267b96e7edb8c34b95c29f683c6290d931f3f2e659e7d7a5ea49d26c6e72ded0d308bb06e6f728cb5e2406fd2346570a3cb757afdb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765ef30000000000000000000000000000000000000000000000000000000000000d3c9ccccbfb76c9930c9ef8db23e1cd0bbb9748514f1ea273c44a21497f11291bb393c1c8312ee479949dacc7a537818de54af74377092e3e84df5d417d64e058e4b448d80dafd28c916e26702083962fbb4085ca018e3aa28870637d9eeffa2c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c651f14477f514c546929685fab5504b0cd8f0250fd54b81fe1d947dee74220882482ef588d5124a6334a72cc8d40399a1f37be6c51d7cefaf7bf6c684559fbd47aaf9148b16fbb07255c2bc06484468c1a257fc919e2daf2fd59ed4aedc37ed788ef37307477d6868127e1b2b9d71a7c1255981bda7c2d97abe280363839aec4" + }, + "strategyValidator": { + "withdrawableEpoch": "18446744073709551615", + "withdrawableEpochProof": "0xffffffffffffffff0000000000000000000000000000000000000000000000008838e039d069e1c72127b7636b0d20fb31476e44fe1f4ab8a27265b7047fc877236fde5a25b3b6d7db6b4233165543be6bb1c1009e3e6a73295e13f2361ca4af4761a518eff950ff65fbd3493858642bd9da6ec6b619d93c1f033193902d85a6e54415160eb9103c28b010a7f89a47e5d29756b683cd1645ffe13cf146a78652a3eb3b2c8f6eab9c7a3cb583626552b1e2cd7ce75900cae4c848743792da158a933fa111c282b76756f16754e5460c6bf8c39292c3889979ded14b1b0653069fb000b089f827c5d40b40d00ad6f1a3c00f55ff8e5f85a1ad56fa866627abe5d4be820a5a40ce9782af7ada1d96bc66ca27c8b2df685a56bfb3f3718ba7a177400397ce5afea8fdb145e52d9e4485dcab61ac1491c9ce243bd111b21dbb80418dd6c67677ce8bf6d0cdda31d1933b9767c6e0b2232e0422271baec7e704b0491f211100812ec8b70ce49e936cc046ac37252775aade28a3201adce9ff3c740b8f287690e36d43d0eec99470400bfbc6384476632939ec76f9c122970d9588fab6595a1f591be002be8d07d50462210c9bfb8c91b3b03af3a39d60117e18a069b42edfcee5b940ebb3349a99816678be658f85a1318e4e330c4f3eaa0efc07dcb1a2d970c5fd25b715e7d796c54c740ef2c80024e02b717c2f66fd4cfda7440540a9795d23036c884adbcad7adf776dc482cad8e384d8b9b3bd1cc8273fe8ad416000fe7453e638de5943befba1f8b2cc6b12bbcfd9c5dc943237198e7b4cde782d18c12c3085de75bfbea61de72e218a6dd45ddc17016002b0ff281e0c12e084d8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92bebd41662aeab9197b52b456a33dc9fdc061bbeb2035fec968b31fdc30fcae39864d3ab63cc10fea24f81c21dc1bc078b666eb8a7e809ffcd729138a576fe8f6638d5bfa4820abdace730611e0f473c86f0fcbc09eb9d117ad37013a313aaa3405398b6b70209e1f53ee627b3ee28403109111c428f43d764a29e10d8c8a1034fc38a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206f05c01e0000000000000000000000000000000000000000000000000000000000c6341f0000000000000000000000000000000000000000000000000000000000c56ac84724595083179768895a9a997dee3c0637b4aaf3583b28ce44b437ea776ea745b24b559bdc485a80ce746cd67f0d4b367951b8e8b8c450d4f42fdbee50532c172d7471e9244f225dad7850da89db7e7637f377f1cbed8504b9065e6296f4a75e87787a517e22e4bd8808b06ac74d20573fde5d05e65d5bd12fddc86238cc59c2a2005f50f8b581c59567947b7311cb18b7d07d29a0878d43a7f7ace9c32482ef588d5124a6334a72cc8d40399a1f37be6c51d7cefaf7bf6c684559fbd47aaf9148b16fbb07255c2bc06484468c1a257fc919e2daf2fd59ed4aedc37ed788ef37307477d6868127e1b2b9d71a7c1255981bda7c2d97abe280363839aec4" + }, + "processedBeaconBlockRoot": "0xc77a5065bbf8267573c297aab9973ba32d259a5ea26355bce23d92cb09f1798d", + "validatorBeaconBlockRoot": "0x87b60e082baecaf021db5123c92c52f9511f9a15c717124072b9fba6c1a126da", + "depositAmount": 32, + "depositDataRoot": "0x6840dead900f2fe11adab6f496fab7bcae5613abc4c0fe70172156051218e635", + "depositBlockNumber": 22886999, + "depositSlot": 12108750, + "depositProcessedSlot": 12159764, + "processedBlockNumber": 22937749, + "command": "npx hardhat verifyDeposit --slot 12159764 --index 2014998 --id 1 --test true --network mainnet" + }, + "comments": { + "register tx": "0x77a3d3ee1898ae6310bce5719ee883fe68deeca232c3dcf6ca60a396c64b4c29", + "deposit tx": "0x4d2ec847b4e52285b10e3dcbd8f28b7bf37d84b71d359272249c38973ad8df1a", + "comment": "npx hardhat verifyValidator --index 2014998 --slot 12202000 --network mainnet --dryrun true --withdrawal 0x840081c97256d553A8F234D469D797B9535a3B49" + } + }, + { + "publicKey": "0x897fc2e3fde878b64202fff94a9c5609460360a9da91ae22f0f99fadb4fae59c1daf7b2d8d075dfafe8acaa0f7f718ab", + "publicKeyHash": "0x9d4a119b1f3f59da745628c4ddcf2a081e40a2374cd6f2fe25ff68c418c1d474", + "index": 2014926, + "exited": false, + "operatorIds": [1909, 1910, 1911, 1912], + "sharesData": "0xb5c411799794dd8c6e26782bbd779f2340d4699e876361f9e8719eaddd95d8257c38d5bc70957dd2f98c78c0584a001610dd1570d65efb29ddb429cc912e204e191d8a0277263bce2519809e61a114dff266799bb846a66c10c1c821a79e939985e3bbebea8439eb075605418a1995994ede314403106a14dc6428f966ee1aa50a66a602f49327cefdf6a05e2aa15815b254b20185b2a9841439ac2138f2d57bca91636e26b7a7ba8311accca7cdb96035f9d8e9b46c59e847accafc61c3bafe91f22c77d77489d6faa74a72b6a9d51c699ea19a545fc1e1ceae667aa83a7a3b79b8e804449c5aaf9bf7e05984acc330899f83c041d6911605f75d55fbce4faa541d70b370388a311e985d9a4a57599a156867630a07d6b389afa22b2a34038310f18f86daad648a1fcc12185bc5d31a643b45874ba983f83c6ce3e59e5f31c12c2914dd0b9cee658782653370cb9e94a49f26dfc9c44f8c2b61d26d70018337344d8245bf028c7488a351a154625c3fb6d05d2702be593c546221051408ffc2d679522e0998ee4d2631e87f73788bb069fc503070f2a582c0af7d023eaa21ea0ca31c0bf0dca36f9b475a3e9eb0a11b3cbf6621d5b4f6ee88b80e6f4a297f76568b01875a56f9d30e0aadf2a04b76d7e8fd2f72f2cfea126a900d3a00d58ca3e730662acc7a1e141e476abf876807fa2352b76c72fc8fc0dd30d85b16d236819d10fbd8a7f3dac858277c905497d2e4e44072c5443d957c6901ffca2fc7cbfe51f4a65ffe761b4f448dc26a12490fdbe8a02fa86f3c7370f097ead1e03470e72a42afb98ba5d2894adeb32b5cf70b382362748ea993fbc1b92aec8b9d4af95a1a1dfc7bdad509c7c58357d9334c8951cfcdd3ee3ad59aafa3400113249c955081fdf76d7450dd77715efb05131b200c88fff6d53a1dc753f5fd2973fd958b1b4171c157bb100569e41e5c1c49d7bd2d9890bdf50f25c8a049421a96efa42a2833819ed39d15d0eec27f3912b8d99ef629351cdd5ae03cb508a7b0591b272d8ba0ea8880e497fabae21059a9291ec38d3cefe4d3819cadec67f3d9e482d4cf6b509b1fad5bda1b52a75a991c8340c1c794e5a6fb5a37c944e1ac4d6b54dcdc8d714bca06be0c8b6235a8e475d24360a1786a53da4dc475fe61f4ae8da4bb1ea77a3ee99cdac36e19bc07718b5cddf43104981d82d5cd984d32f94bbb6044835fc4c3d65c9deaa3f18c53a7237b03c9c7c203d6e9a94c112be48165e1e37454ec60c83afe316ea309428b21d81d3caa4da60b44481471d0eafb3dc67b537962a8e6ea9cf772e341135a820b2810762b79823f80770ce6ebeb51360e53a874adcf121f64b915476d0c65d44b1e4e9f9f40aaba343a869accef6054738aae8d37b083386a64be66f0f2bc151ed7d4c12e10d875fbd0c4d2afa136d643bd30a27cb3a1a5d399b0adbc8eff138c5308c02906a4471eb3e2c286e1d25fd487bede331b0caa80c754d112f15264ddbc3445dd04af6b5b636fc12835e1167c9fd1769bac1ca9775a6e3e80aca22362daba5cd0a94f7093716f6f7e757821e949e9b1f2c168c2adda0f4029b3d824f30183a0551d78f03d7075cc72d8768ba80c3bceb9068311413e5297e0ab71b8245caf5dca2027d42dff11f6fbdef30658d86e8677144d38b29681f0b8827a528e18a31206c6f2b55b02039b21270958343407e8c7a2cf3227bb4447c69b847de8f26718f6cd68f887c2dd3d07cd8b1bab31a8e16e249e106a54c6bed003d1df5f08006d02d53d8bcf1ba561fbe98c612f5f8f875e7ef9a77641520768fde9567d53a299043ab315698e0651e906f8fa7ac609484294", + "signature": "0xa7bdf900771bf5f90c73250a651d466cd3001575e7e46943fd3d4a40caa6f0d69b63f94eb5e66efaf7bc6b2d11eaa52d10c395d3d8e24ed5d1b13e0242974e0e4170552a0b21bd1675092014d43870ece61af23740231e27edb38a2730e5171d", + "validatorProof": { + "bytes": "0x020000000000000000000000840081c97256d553a8f234d469d797b9535a3b4919327cb9763c96e00332bde93bdbb1032c4b796dda73e515c8c5f7ede9a419be3d611f0e4ee59649c6daa0c0c74fa35c336ab92b928bea1fae9f6a17996c11396c9792a17e25e48fd96226161528abfc4fadc7a306589f1cf8349a595d996559c321e794ad7f74093b52484408253a17f247d410188774e2d67b9d5449665253dcaa6cf026d47ff5503ff81cf229ede8482964c455afd037c4a4d895aa34a3e15b3d3af83fdcef2d2b9a3382b530c51c90797b76a8a1a68c76a651ccfbc216ae64b04490ab004a39ad0076630ea5ff612064f859d59407160428f193e05b43f29cdc1701dc6f692d9d257625ea6fa11e5c9103d0670212ff73e62e1308d2964618ab5764e148e1336f5aae53283cc5211778c77a0c85a6cd3b42df59635883ad3e89a8baaf4ccdbcbc65af53c95a40bf72957f30b2c7174dc06277b75827e31429d40bf3b279722ca18134155adf22592e16a793e8e4ca2fcd59707295c896f1ef518ba28d88abaf01fa978488a97de5359669cb121ed43c1356b434a538190c251b8646e24530fc13244222c56e0735a017bff9e444ae694cd960d1426172b0ff9280840c39feaf86b8475d556d170c3df366d41f8bf7975a799afeb5b9c09532fd2d7a4ecbcd6b7542f0e8afcdd8d01bfe5ee455a46f9f28f81bad6464e2e344e63a8d2177cffdb23a6e0d66cbbc1606709591a0305d7e0997c281c844102f807a05e8265d7fb12b34ccf5cf0e988e6ef1ad6a39098fc5bf04099e8dd51c66396f2e5ec8e570ca58a3ba4f78aa8c9b22614b299571cb32fb49b9b5506cca408fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb48135f37c0a5f02c0cd358938dc3c24eb9d0158283fc328ce0f5176c040bdb043d7f638fde7f712618272899e5f6b465b016f1031b4a5710c32df7b4b451cccce2f559cd1a5000ecab92f2f9407e3ac6cbae7166e187688f56ea62c72d59096c5ce5e37b4d4a8bf060fbb61e86e16ea54068bc9c4aabd4273d4d76bf1504d0d18a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fa8e01e0000000000000000000000000000000000000000000000000000000000c6341f00000000000000000000000000000000000000000000000000000000006d833de4787b4b14e2cf638da50c1707c9ff3cb0dc7a45a2660b4d3458c7efdfb6c721321dfb0e529649f74be64a31bbb7d70d337f37a56b020ecc32893d1dec899f8e0893bc267e3256c7542c6cdead589454432274d6bfdfa33e5a4372c0408332dd055e96fbddab4824219c99c6efd32f62a94680d7d7fd9a209136498a235d9eae31ca1126c31a097992d1aa46e4959dac388d050aa45b9ba26dd04c29d71e55542dbbada164392f6f61345af3b028848382633277ad75a17af787f75b843767942c6c4d511c58ce23665f710e5206efbcb15a207e9f7bc0bb86306f7409c256512691d51102f5d48c78b0bee220af931edde3dd49847fd7675ad6ab7fec", + "root": "0x43d73043b4afa84151e094ca7f5c97c107579ed966c83049690c799a7d4c9c7f", + "nextBlockTimestamp": 1753248035 + }, + "depositProof": { + "firstPendingDeposit": { + "slot": 17415345, + "validatorIndex": 2015133, + "pubKeyHash": "0xda41321d8041b7e1140a49a6e0dbcfd5685d71f3198511e76b76cb05f8171cf7", + "proof": "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b0481ea50c191bc44fffb7e6125790ea0dd3d589dc2e2a56b78f9105b575dc15ebec6ac4ed210f1069813253fca4dc63eb11e722ecc2d3e66417375792bbd2ee30f513f1010a63ead5cdded3d31767e128b9f98578effce4e81a84af3b7f2f9d282002744cc9973b3d871a793f25fa4a9093491536ffafda48af54f61b2dea2f1fc8ed48b0b97aa38fcc656f953bc8b2ec80d8c27dc134ca21a92461c2b4ccf06603a54af5c85ce5a112850c4771f9742f3f6a5ea8770a1a66f3ad4039553986969c821c662471bd55227f13e1d445fb1e3328a390213b87c70775baeccbd760d1b93fc24a8f6bf98c4ed59dd63f09e83798568db9ba1e4f1b44e44abb8f575a363c2ca81d56e57b2eace4cda116f3eabba6c629b8405286f2114cb15a1255f6971026ee60c22adadfd93fc56589e91147fefff3388ee472b81c30033b6943d6fd4c1633d430715dc0438351f530d6b06ac80dc7e3f677ccf3275fa9698c7027e656328a5e996d743296eb57aaa92716a42de47d242145d3e5adec4e9c6089ae584b3a51d34beac5d0bf06c31a64786475625364bed443376b90e420d526a08e23cd736a14757c82449bab75d75159344ea997249124a6798ca0642a40ec69c0e1736f40e9a805cb053cbc844fd08aff10e1019c6baa20925ef32ac33757e3567b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765323100000000000000000000000000000000000000000000000000000000000057f7f71bf27535a8181bd2281d786f1c7fb83830b1883f0ba4399ad5ee973988bb393c1c8312ee479949dacc7a537818de54af74377092e3e84df5d417d64e058e4b448d80dafd28c916e26702083962fbb4085ca018e3aa28870637d9eeffa2c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c61a0dd9bdfb1bfda469e3cff1c90257ad15657e09195e13ef974dae72372ac3a7ba39b33ec2e153a7d5c7e53582d87f7b62f8f06c73b9beb00542f76720019e65cfc50186fd44bff45357c86db9bd861b28ecefcd8fdf69753d15889dee1fec4f67ada367fded170d9a1928a3bc7e96e2ec926c50f9e8893e3c3beae4b09593f" + }, + "strategyValidator": { + "withdrawableEpoch": "18446744073709551615", + "withdrawableEpochProof": "0xffffffffffffffff0000000000000000000000000000000000000000000000007f0157dbaed21e908a3256e9b89b195118105ef0443f67c117791384956ac9c331b872ad689a4db0192515441bcf1621eefc2715bf4bc54fc8ee1de186ac832b6c9792a17e25e48fd96226161528abfc4fadc7a306589f1cf8349a595d996559c321e794ad7f74093b52484408253a17f247d410188774e2d67b9d5449665253dcaa6cf026d47ff5503ff81cf229ede8482964c455afd037c4a4d895aa34a3e15b3d3af83fdcef2d2b9a3382b530c51c90797b76a8a1a68c76a651ccfbc216ae64b04490ab004a39ad0076630ea5ff612064f859d59407160428f193e05b43f29cdc1701dc6f692d9d257625ea6fa11e5c9103d0670212ff73e62e1308d2964605cf6b16ec8e18d4a997726b28dd4b0ba35eaf092bfa01335f50bcbfb2f217873e89a8baaf4ccdbcbc65af53c95a40bf72957f30b2c7174dc06277b75827e314b31e164f60defb33eecf14f758c865272198155a475c4aba64815d03dd450fdc287690e36d43d0eec99470400bfbc6384476632939ec76f9c122970d9588fab6595a1f591be002be8d07d50462210c9bfb8c91b3b03af3a39d60117e18a069b42edfcee5b940ebb3349a99816678be658f85a1318e4e330c4f3eaa0efc07dcb1a2d970c5fd25b715e7d796c54c740ef2c80024e02b717c2f66fd4cfda7440540a9795d23036c884adbcad7adf776dc482cad8e384d8b9b3bd1cc8273fe8ad416b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da72937846b6775fb61613c6e39377667bdab239a5cb904b282dde0bd5393042d0d6bdcd98fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb195210c7d3f5250c573489a3316dd40a28d1d970b116c0873d3733bca3638e129e7db551920684f1990ac7f0014e623c1e015ac4fc3b96dde5164139466042c64d31d1682750e395886e80cebbdc929e717a6a50cb622b5fa29a7be3acdff11173455730482da365af1d0166e30876a42b0ce3af93038fd87531e1af39c0b4078a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206f9dbf1e0000000000000000000000000000000000000000000000000000000000c6341f0000000000000000000000000000000000000000000000000000000000258aab04192b44d38fd52914a9f1c1ed89a7eee865b10e986301c0c3da1a302850a9994496ffd61025033dd2e25e2bf8cd215a3e04a61c07d1f5e9c1d29b95504e28731be293617de12d54634736dc6c994d0fd68be64b089619d7a417a63d84aec32eb084134d1cae4d3f4274fd02ffd92e9a4d81c79d3bd43bd14698c340f41158978f10f81a892b44fa3ec2f0b5fe744cc2993bc2f033a1ce54651e46f9d07ba39b33ec2e153a7d5c7e53582d87f7b62f8f06c73b9beb00542f76720019e65cfc50186fd44bff45357c86db9bd861b28ecefcd8fdf69753d15889dee1fec4f67ada367fded170d9a1928a3bc7e96e2ec926c50f9e8893e3c3beae4b09593f" + }, + "processedBeaconBlockRoot": "0x48c5e54cf2ea467266a6356294d69fa069ffd6e0a12c977b066765e96ed09c4a", + "validatorBeaconBlockRoot": "0x41016175a13753a623d39d630ba5d0288a38614859e4cf9c6d7d2c0b96c5a864", + "depositAmount": 32, + "depositDataRoot": "0xafba9f527091d375d0e99a2ad0b0d098bef6d32474475c11440edc695ca6be70", + "depositBlockNumber": 22887002, + "depositSlot": 12108753, + "depositProcessedSlot": 12159345, + "processedBlockNumber": 22937331, + "command": "npx hardhat verifyDeposit --slot 12159345 --index 2014926 --id 1 --test true --network mainnet" + }, + "comments": { + "register tx": "0x094704cb3ee43a69a0a598d61ddd47d502b49252348897edda14c64dafc0a3ab", + "deposit tx": "0xdcfcb446e984b2587c91a04e6272a61c6ad55f19a0cc068c3e9e71c51b527070", + "comment": "npx hardhat verifyValidator --index 2014926 --slot 12202000 --network mainnet --dryrun true --withdrawal 0x840081c97256d553A8F234D469D797B9535a3B49" + } + } + ], + "testBalancesProofs": [ + { + "description": "The third validator had more than 2048 ETH", + "slot": 11763000, + "validators": [1930685, 1998611, 1897126], + "blockRoot": "0xd117cae6578e917494ae2efe62360ceabd53be93a196b2594afc56933fdebdd2", + "pendingDepositProofsData": { + "pendingDepositContainerRoot": "0x632222232678cb52a1793811137cd2ffeb771574973bbb94eef2ee3a384b54aa", + "pendingDepositContainerProof": "0x0fa56b361e25032741bde59f276f9d74fe03f3c771c4cf604cad5f3d606338570c43867f1948870817a105b8f183521cbdf6e894bee22305f9c2d8feebbaacf652e05e9aff51b5f89dd1d1a6ae2910a03d3102ec405f3b88a67f6784353b3073c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123ca8482d0e63bcb5f1b330b993f4cf32a3e541927c2e223f551dc72759a56cbf3e9d7d0cb62dc144daf5ab52b89f03cc110726d2225b7e2f7523fd5ce395c06126d1ad393ffdfd614c962da4d1e6a403316c23003ce74cce1c562517e98dcdf0bd39224bdaf75fb3b2838691706bc4afc2cbc85b8fbaee995e7db5dfe08069fb7e", + "pendingDepositIndexes": [1, 2, 3, 4], + "pendingDepositRoots": [ + "0x75492f3a50c0c2127236a50c7d5fe785281f92d94b84ab2c66c99fb2e1fec76e", + "0x2b6e781859a45a847284b53745378724645f3c34283a7056c85113a7f2525fe7", + "0x670d82d91e1505cf4521df8063ea7f89caefb49492a9887c23c6a75a8011a68e", + "0xa9458b80e06b80616c45af3d2e019069dec14900669e1636088e6ed74beb271b" + ], + "pendingDepositProofs": [ + "0x5fd2c3f8035d34c1aabbf4e13cb49d5307729377eb0fd139cf240df7940216ee7d92c45cab42e1eff9b53480a404f556ad09cf607f5bcecb04950ed36f9b5da2f14e7299187c17bb5c19f5b9afa12d77edd510ad3576d6f861d213018e0000f8286e7d493a8fe1dfe824cae130fce4a73446d2f8d1dcf7730aae97a3c7571a38a37dc46dd7ed3725d450aa0f03b47e83bdb9a4c825decb9dc77d95d7b7503939a2519201c393a29baefef46394f8cf01a6adf7a41e46319dfb7a6629556d20e70eb24654e8b114cdffbd46de192091f3dd539d9afe26b4fb48de3b304715e20f5bad058019d1403ed4f4f3a616b7bd64049e87955c60b83b8caa2f1f9742e04c026893dd75e0b8cbd0f17049de6dd43437919a83967f7e0bd454e350b2f5b7ae5c901c9cab5f36a2ab69ed526d709a4ddc99d1b26f2224bdc0016d65020480124b16ce2a9cee27a48bd791c4e5e9bad467fea460e51fdbf6441b93b696d2e69169a8a502144b2b5b39df7470c8c839abafb3699b83b8d151cc29b29b7d6f21ecb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467655e0e000000000000000000000000000000000000000000000000000000000000", + "0x670d82d91e1505cf4521df8063ea7f89caefb49492a9887c23c6a75a8011a68e9a8bddf1de3742c85cc7ecfb93bb41f21772cb250774e151f022bdef672fd254f14e7299187c17bb5c19f5b9afa12d77edd510ad3576d6f861d213018e0000f8286e7d493a8fe1dfe824cae130fce4a73446d2f8d1dcf7730aae97a3c7571a38a37dc46dd7ed3725d450aa0f03b47e83bdb9a4c825decb9dc77d95d7b7503939a2519201c393a29baefef46394f8cf01a6adf7a41e46319dfb7a6629556d20e70eb24654e8b114cdffbd46de192091f3dd539d9afe26b4fb48de3b304715e20f5bad058019d1403ed4f4f3a616b7bd64049e87955c60b83b8caa2f1f9742e04c026893dd75e0b8cbd0f17049de6dd43437919a83967f7e0bd454e350b2f5b7ae5c901c9cab5f36a2ab69ed526d709a4ddc99d1b26f2224bdc0016d65020480124b16ce2a9cee27a48bd791c4e5e9bad467fea460e51fdbf6441b93b696d2e69169a8a502144b2b5b39df7470c8c839abafb3699b83b8d151cc29b29b7d6f21ecb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467655e0e000000000000000000000000000000000000000000000000000000000000", + "0x2b6e781859a45a847284b53745378724645f3c34283a7056c85113a7f2525fe79a8bddf1de3742c85cc7ecfb93bb41f21772cb250774e151f022bdef672fd254f14e7299187c17bb5c19f5b9afa12d77edd510ad3576d6f861d213018e0000f8286e7d493a8fe1dfe824cae130fce4a73446d2f8d1dcf7730aae97a3c7571a38a37dc46dd7ed3725d450aa0f03b47e83bdb9a4c825decb9dc77d95d7b7503939a2519201c393a29baefef46394f8cf01a6adf7a41e46319dfb7a6629556d20e70eb24654e8b114cdffbd46de192091f3dd539d9afe26b4fb48de3b304715e20f5bad058019d1403ed4f4f3a616b7bd64049e87955c60b83b8caa2f1f9742e04c026893dd75e0b8cbd0f17049de6dd43437919a83967f7e0bd454e350b2f5b7ae5c901c9cab5f36a2ab69ed526d709a4ddc99d1b26f2224bdc0016d65020480124b16ce2a9cee27a48bd791c4e5e9bad467fea460e51fdbf6441b93b696d2e69169a8a502144b2b5b39df7470c8c839abafb3699b83b8d151cc29b29b7d6f21ecb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467655e0e000000000000000000000000000000000000000000000000000000000000", + "0x782665a58cf78248727c06737a616aaf0b98c6bb7f2be8ce5b41fc977d54ba92accd1ea2b360020d8e2b1ab6d8906461d4f3de9289c53d8d80da8e4d8cad2a704c12c7b051788f6145fe6b16352acc41ace526974c0e07e2a5ac98fd085d21c2286e7d493a8fe1dfe824cae130fce4a73446d2f8d1dcf7730aae97a3c7571a38a37dc46dd7ed3725d450aa0f03b47e83bdb9a4c825decb9dc77d95d7b7503939a2519201c393a29baefef46394f8cf01a6adf7a41e46319dfb7a6629556d20e70eb24654e8b114cdffbd46de192091f3dd539d9afe26b4fb48de3b304715e20f5bad058019d1403ed4f4f3a616b7bd64049e87955c60b83b8caa2f1f9742e04c026893dd75e0b8cbd0f17049de6dd43437919a83967f7e0bd454e350b2f5b7ae5c901c9cab5f36a2ab69ed526d709a4ddc99d1b26f2224bdc0016d65020480124b16ce2a9cee27a48bd791c4e5e9bad467fea460e51fdbf6441b93b696d2e69169a8a502144b2b5b39df7470c8c839abafb3699b83b8d151cc29b29b7d6f21ecb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467655e0e000000000000000000000000000000000000000000000000000000000000" + ] + }, + "balanceProofs": { + "balancesContainerRoot": "0xc5ae13ec1cafd0d4528b36b2e95c3aa8d5b4d7d68e80063420a9dd4a205cc581", + "balancesContainerProof": "0xf14dcee88d318ba00125a38c13c9ddd67251ddd45fd59b2dbca86291a07c0ed7bbf58332f44162f1e2134582297a654bd55e1f408d776e2557fd647dcce6e58adbbfb2b4c7d7ce38a504530f3c57cb1d9519ca624520c0c816f67d0296188c984314ee15aaa639b892490dd4dd1c30d33f6620b408e0f1e2999e4b3d43a1a248971bc93f62e587e437b7170b80a317860c20e0dae4fd1d0c8180faa2011c2c49b565b1ca721f409771852c99fd99367f221d0df2ae1c7c538547de649c1982709d7d0cb62dc144daf5ab52b89f03cc110726d2225b7e2f7523fd5ce395c06126d1ad393ffdfd614c962da4d1e6a403316c23003ce74cce1c562517e98dcdf0bd39224bdaf75fb3b2838691706bc4afc2cbc85b8fbaee995e7db5dfe08069fb7e", + "validatorBalanceLeaves": [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000000000000b57735d2060200000000000000000000" + ], + "validatorBalanceProofs": [ + "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4bdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30d88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa187eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1c1a4b47064baa6932ae2e99a45171f9b2a45c24d957b5ccc23e32b3c44a55cf15a23a0cfbd10aa8822a321259fa16c90bdfa81baa66d02653fbb0f354b80a33aba90e638c0b3bd3e8c72533be69d08f22e7a4210102102985ea077aece2f72a9df6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e04236d93dcefc2a3174296e54c7b9b0d79e7b4ffcd9b7e94d213593a8a8d04bdd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bbbe46e5ecae4cae745951aac1829362e4befed02b5f2883fef635d29980f95ac3b1f466434bb4ed8b6dbbbd6b7c18f699f8cb4c3d629d864cc7c89c2248cfe2710e112d2aa67f468308069e186c06dad6d2ee4b5b76d2f14dcbc2eda49d2718c3f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a7473611d0000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4bdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30d88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa187eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1ffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220b7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784e4404bbe7e55fde78e9b2282b17deedec3615ee4516362f16437e220fa4a54c1be46e5ecae4cae745951aac1829362e4befed02b5f2883fef635d29980f95ac3b1f466434bb4ed8b6dbbbd6b7c18f699f8cb4c3d629d864cc7c89c2248cfe2710e112d2aa67f468308069e186c06dad6d2ee4b5b76d2f14dcbc2eda49d2718c3f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a7473611d0000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000fd8187f25c25387543c0508add503dcfdffd5a1e5e41544e6829f7f431adf888db56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71f5e2dd323f21d778eb3d1f530fe54439f0b3d9e0a17ff66f873567d293d853b82cd36f1e6026a624ccb0dd33032e7ce877d4b9c1e1bf14cf40e26601fdd1a11e7a498a44340747a9aef556d72e7da6f43e6d4a2ff8c9e96cc11fe9ced5ec230e1739c8ea98f1e36473af195b09f5d82298a78a3b2453b4e59acf059cf5380dcb92d583d38370664db18db4e913eac4a476398db856ed08bdc7cefa18579e324cd823498f8bd1cc922e43635b40b5e00a044e8f71a290e1f536f736c7e5bb2ee0003d0834b50449591196cfbbfdf10faac03adedf90756fccc0ba49fa773b0cb99e3d179297b894060fb0e7a6a3fdec31cffc03b0db8baffcfe904a4e21fd3f11dc29bd4fee06e332143579e4cfec2f374bf1514e36de1734d2b05d73aa3216346b8fe3c7b685bb87a4623c6d1513a7643a773ceb863d5b5ecbeb752adb1e068c0d4562c1e0c5c2a5bd5c3e85fc656874b20ef16b790a4f9d0950c8962ceeed19537866290e44f831f355e54763429955b62e68098af96f22ab94df40224d4185d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bbbe46e5ecae4cae745951aac1829362e4befed02b5f2883fef635d29980f95ac3b1f466434bb4ed8b6dbbbd6b7c18f699f8cb4c3d629d864cc7c89c2248cfe2710e112d2aa67f468308069e186c06dad6d2ee4b5b76d2f14dcbc2eda49d2718c3f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a7473611d0000000000000000000000000000000000000000000000000000000000" + ] + }, + "validatorBalances": ["0.0", "0.0", "2228.319778741"], + "command": "npx hardhat verifyBalances --indexes 1930685,1998611,1897126 --test true --slot 11763000 --deposits 1,2,3,4 --network mainnet" + }, + { + "description": "The third validator has done a partial withdrawal", + "slot": 11787326, + "validators": [1930685, 1998611, 1897126], + "blockRoot": "0x2632705c197d5448f6d386a40f145b6d9ee769755b6ecf712f79fc44944570f3", + "pendingDepositProofsData": { + "pendingDepositContainerRoot": "0x5731d01d25c791a1c283eebb8267063426839a2a62db27ba2877d27525d314c6", + "pendingDepositContainerProof": "0x2a3777ca27bf5deda8106113e75209be1bdedd27498b83d8c81519263d3793cb21c31acb067ec9840693a9258cb460696dda1452cc8e096b3d2c830fea9242e6f3513a61a01b7dddd72bb498711fa6bb524d82ec98bab47e63bf44006482507bc78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123ca6c5b795997b9cd992a9dab87dd0d7805ecbada7a8be0478f46f1096454588be6c86c5fb4261abc9b4d7e992cffc63bb34a4116fcecb7aaed26a0d0b9945fa94b0615e0bfdcfc886bf0155b1caef376a62b7c976b88414f3ad624054c9f892ff3596bd8236c5a1f31b92a52ce81f575507a28b34c40e1039dee1351747b038fa", + "pendingDepositIndexes": [1, 2, 3, 4], + "pendingDepositRoots": [ + "0xcdc2284512f62aa94961dbec84440bf7fc4580c699c6a7cc2beaa43b8b19660d", + "0xd3ceda68ba73f518a98a4c02ffef33f3ce43e5784b800afb80398d47990d3a88", + "0x9536bcf5c3219b9218c082a296f60a182ee1ebfcb739fc78bfecf73eb3437d73", + "0x30a788946d7da3d4d09b6e0c96a0d81ca6168443600f210829e8c36ca0c39214" + ], + "pendingDepositProofs": [ + "0x00b7eaa2aaedd3c4e894bb3b64f894abb736a2c691e91e800933acf91027f46e270c1c9e6ade4ba231b6d025c07ba39980bf2b27679122d14eb663f9ef3436f54e4f68acaa184332565e6743b37f1a969b19a5821528fecc9a1baca011b1981e5efb30323874109b9730b156d536f8ebdb59f0645dd3953ac5c937204f79156cd2735b9e437a9809ac224e1f7c3f6a3f9d8189ed243d242b26bb84996672093b17014c6312d1eb5f670adedc3c69eb0e0b94f977a416173eb3ea3c2a0b0d6e1b434448d0999c0f6b0d4c7f292a88120c54351748a057951be833f625b43664da8db91f42868797f3adc6af452383c7fca8f1fff57af1d862cdbfb52cbca4d9a142de1f2f17fe0923f28edb3adedf42c72aa220eac59ede312a9275eeabcf5a7e06eddfcd3592815bc85ff4c935b08fafaf127e85d40c0430bec6898ed239e97cffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220b7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765be03000000000000000000000000000000000000000000000000000000000000", + "0x9536bcf5c3219b9218c082a296f60a182ee1ebfcb739fc78bfecf73eb3437d7390c3736e0daa75f0207f4423b13e6c523b869e66db60c7b216ad49103ff35ecd4e4f68acaa184332565e6743b37f1a969b19a5821528fecc9a1baca011b1981e5efb30323874109b9730b156d536f8ebdb59f0645dd3953ac5c937204f79156cd2735b9e437a9809ac224e1f7c3f6a3f9d8189ed243d242b26bb84996672093b17014c6312d1eb5f670adedc3c69eb0e0b94f977a416173eb3ea3c2a0b0d6e1b434448d0999c0f6b0d4c7f292a88120c54351748a057951be833f625b43664da8db91f42868797f3adc6af452383c7fca8f1fff57af1d862cdbfb52cbca4d9a142de1f2f17fe0923f28edb3adedf42c72aa220eac59ede312a9275eeabcf5a7e06eddfcd3592815bc85ff4c935b08fafaf127e85d40c0430bec6898ed239e97cffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220b7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765be03000000000000000000000000000000000000000000000000000000000000", + "0xd3ceda68ba73f518a98a4c02ffef33f3ce43e5784b800afb80398d47990d3a8890c3736e0daa75f0207f4423b13e6c523b869e66db60c7b216ad49103ff35ecd4e4f68acaa184332565e6743b37f1a969b19a5821528fecc9a1baca011b1981e5efb30323874109b9730b156d536f8ebdb59f0645dd3953ac5c937204f79156cd2735b9e437a9809ac224e1f7c3f6a3f9d8189ed243d242b26bb84996672093b17014c6312d1eb5f670adedc3c69eb0e0b94f977a416173eb3ea3c2a0b0d6e1b434448d0999c0f6b0d4c7f292a88120c54351748a057951be833f625b43664da8db91f42868797f3adc6af452383c7fca8f1fff57af1d862cdbfb52cbca4d9a142de1f2f17fe0923f28edb3adedf42c72aa220eac59ede312a9275eeabcf5a7e06eddfcd3592815bc85ff4c935b08fafaf127e85d40c0430bec6898ed239e97cffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220b7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765be03000000000000000000000000000000000000000000000000000000000000", + "0x7e0df1f4c0175584f753e22eb83d4a9009031a6c8e743c9515a0569b412f1e3b36bf669495bc280a4bf849932bb7bc023d714cdb3b78f19fa499920de24aa7b1371c09b022ebc155232fafbf6cd488ed4d82476421d19c1f2b1fcdc72971fa5c5efb30323874109b9730b156d536f8ebdb59f0645dd3953ac5c937204f79156cd2735b9e437a9809ac224e1f7c3f6a3f9d8189ed243d242b26bb84996672093b17014c6312d1eb5f670adedc3c69eb0e0b94f977a416173eb3ea3c2a0b0d6e1b434448d0999c0f6b0d4c7f292a88120c54351748a057951be833f625b43664da8db91f42868797f3adc6af452383c7fca8f1fff57af1d862cdbfb52cbca4d9a142de1f2f17fe0923f28edb3adedf42c72aa220eac59ede312a9275eeabcf5a7e06eddfcd3592815bc85ff4c935b08fafaf127e85d40c0430bec6898ed239e97cffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220b7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765be03000000000000000000000000000000000000000000000000000000000000" + ] + }, + "balanceProofs": { + "balancesContainerRoot": "0xd23e0a1aba7d695800ec194fb3ef631efd007e3c800afae28e196ec7cd3a4f04", + "balancesContainerProof": "0x840c7b6b8e4d5c622a512742a78eaf87421a82469216b211157d9329bb3d26164a5389a221f4bddf742dd667cdb872ec0c8063bd171d35c530b255344e4a90ab50a6f8b54bf6ea8e89ca4cc2a0366e1f00ea86afa9fdb6a4cbe0f5d9b8d054e3973f6a817349f52706bcf5829558ee1a79ff0b888355ad0a6bd3b30affbdc60e9563af66db5fca48caf71a77e2ef381cd67ac0824b221d8d55cf9ff8fe0ad69e29d34bd172d8d333b7f839f5e8b5b73bb8bcf998cfad0ac8e95b073a18e4f1396c86c5fb4261abc9b4d7e992cffc63bb34a4116fcecb7aaed26a0d0b9945fa94b0615e0bfdcfc886bf0155b1caef376a62b7c976b88414f3ad624054c9f892ff3596bd8236c5a1f31b92a52ce81f575507a28b34c40e1039dee1351747b038fa", + "validatorBalanceLeaves": [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000000000000290ae6f2710100000000000000000000" + ], + "validatorBalanceProofs": [ + "0x00405973070000000040597307000000004059730700000000405973070000006b22e4c31c927e650119b5663965f7eb42e95192fe363caeb7531cbb0f94fcb0df5629268ede2323ab7cb7196a7146a72373961f5bb96be7d9c2e1af3fd0fabfd77ad2e238c5087ceec41f87465eb35684dfad8094c69ebe5f167017e930ba89536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c61c9a747d8e05e63cebfcba1f205fd80224613d1f2c60e7e1cfc0d8f6f3c4e13336d7f8815e9eeeb4f945973d091a9a0ec784123981cd6abade1fca86b330c4187eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2ca89857d266f5c5768ee796658b771c69cca41ecacd5563dc7b686897563c2e25506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e189decf2fc19d0188f06217e4c445fac4f964dab386b94cb935e0c8dc57e7a7b03b732350111182a3f68497e70f344684e1bff5bdf9edaf46fcc578ee576537757fa2c0a377185c1925cf22f403f3b68522683ebb3db0fcb7dc5813744032b0a2df6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e69bff4ae09b54cfa7992351b91770e9fb793154ccc9f2c937bc2052d6bdd089cd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb46ec3096a79839470ed5a4f469ca0450fa631b440b25dad09e48d834bbc86c3759a1010b5db53cc78037a6e7dc3a4e0e69079ace3c787a3d5fd6e5d1ef82836900e595b3dae1e07369bedee5707172cce49ee0dafa3fcd61de27267142c8452df893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74bc751d0000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4bdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30d88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa187eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1ffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220b7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da72937845de4fb73a003937f4486b0a3ecc3f6409cf94e83333e8d7dbf08b408a6eba74d46ec3096a79839470ed5a4f469ca0450fa631b440b25dad09e48d834bbc86c3759a1010b5db53cc78037a6e7dc3a4e0e69079ace3c787a3d5fd6e5d1ef82836900e595b3dae1e07369bedee5707172cce49ee0dafa3fcd61de27267142c8452df893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74bc751d0000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000fd8187f25c25387543c0508add503dcfdffd5a1e5e41544e6829f7f431adf888db56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71ae1d28fec0eea7e663d3b9a2aa32ada4cd1357cdbe1f472073ffcd00de8943e797bbf49c70da197ef5be99efc5493d2f8457d3bb9605072922e345d037155ba5231d4ae883a37f72a275fb7abb2e1b41582019d992cf48d82f6fc81cc47b034c82fc57fe62be70b7dab4c1d5c850554ea0d0fcea196fdbc7cb0892d41a28b9047f479ed29592eba8e58e127b33fe5b5c74f86cee60fc91917dadbfc933b65cea04e643fbec5501ef7bab115dd068f7f2c941f705fa63eb2d753e1638c5a278777aa744bad98a1bc1d32b2f7706b1165b5b7ccb73d14bdce00a43bf1395318028299dfa0cccded0c481b40ce3c013f4bf56c86b2e7863b852d9e416fbe5dd70b9a5e62e4beb222f67209414ed88c753409bf8b9e5cdfe87d22c930a2d229f81d06177723a2f35321d8fcabc594d5b1cfc210676e20d3dea5777e97948f90bdc047b0e1b3743d261394059d081fa913309aa8d05f19af471d1d9f2595689c89aea5f20f5ce9a9b555211f6c149aaabd7b1d37346f1e979ef57bd53f2ff63daade1d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb46ec3096a79839470ed5a4f469ca0450fa631b440b25dad09e48d834bbc86c3759a1010b5db53cc78037a6e7dc3a4e0e69079ace3c787a3d5fd6e5d1ef82836900e595b3dae1e07369bedee5707172cce49ee0dafa3fcd61de27267142c8452df893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74bc751d0000000000000000000000000000000000000000000000000000000000" + ] + }, + "validatorBalances": [0, 0, 1588.918094377], + "command": "npx hardhat verifyBalances --indexes 1930685,1998611,1897126 --test true --slot 11787326 --deposits 1,2,3,4 --network mainnet" + }, + { + "description": "Only the first validator had a balance", + "slot": 12021100, + "validators": [1930685, 1998611, 1897126], + "blockRoot": "0x3fd4bec58e89729b7d6a17f425671356c598c92c379ffa3905b696a545362076", + "pendingDepositProofsData": { + "pendingDepositContainerRoot": "0x370c709ddfefadf33d60b49bf3af257ae83cd0e340fdf27733e6013da005e79b", + "pendingDepositContainerProof": "0xec16345419affe053d428f068727e82424d9902132f770aa75bb627f057a943a802f75c85345cf42c8a97d15e09aa7f9c2b7472f1518d3226fddb115c10ce08d7136966afa445b1a6488473bc0889769eeaa7f062fc74f13f6f024583cecbe97c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c9f5bd03b1e74d8f2c1a476e4cf276edf0e6e3b8def4310d6299759a9d20b9c764ef154b7e63d373f7df682344a1f7b5de7bb0826b046cdd14166ebe9c163d25be391287a9e590fc697dc86bb293adc40a6c58621178b018aa82058c60b59fc05de0859955015d987a7b5175a46e687529a3a1abaea12f9f10dd4499c729e34b5", + "pendingDepositIndexes": [1, 2, 3, 4], + "pendingDepositRoots": [ + "0xb1afd01f7475b6dd0c31458405492d608c87a4e3d273c6ef2cd8465f65f632fa", + "0x3ee08f35ff4ac7f64c963d42c20a567cc04a77669a68acb109549deb2642d870", + "0x99419912561605f39cb6727fdc4c2087a498c2e78d1f69f60b55347f3c2c5d43", + "0x1a39679d4a112257c9540696e22a95dc6afa6e0a23d6bcd81b80a0be1fb141cf" + ], + "pendingDepositProofs": [ + "0x46bb4f7d846e63aa4930decbf64804d8e708625362f90729c48914d271a4b2152e30c498211354b124cd3729fbe372508291c15666cda546663fc106ae011d7ad5f4d20010774537d65c6795c0afe4cf893823116974959434fde772315ba39053799ca7dc7f7ca5c5df88b1033f958e59e7a59df7fa5386a547c815fa2fb58439867f18a3af4fbaceba9a80f4c5bc29a15524e11cf0eb5d2b3431033b1e5c6d404fba2c23e4a3b4b3fd3df83ec4b90e19ad6240db50ede8e9c84e7f0b364edbc0324e185391108ae97d99c9a580b40b1d55458450f2383ab35da47550be516a99af717ec6b4850482c424300040745ffe8824172d4d308460a3c5626eb032250c7b2b309889ced27a4b68e253e93fa982fe2d50a5b598cfd1924e97f3cb00b781cef39cade7ea2f4a308ddfd6a85dbcb59cc0e537a94bfa57ebbcdd98353f2813112ad4ca904bb9cdee1f0f424b7cead64fc5c9b6d71d09c4d86395ad98e3b1dbe7467bf5f4548efa262ca6d8057ed0e19134156a8d9f02efcae7f93b34c55cc22428e63b55a41f82fe2f3d0c5b532f0637f1e5897107a4306834d7bc9c63dc4ed50a7e4fddb8da32601b2dd9a1d3ed41285ba920e79fc63d9660475b9f9062b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467654e22000000000000000000000000000000000000000000000000000000000000", + "0x99419912561605f39cb6727fdc4c2087a498c2e78d1f69f60b55347f3c2c5d43cdd3e5fbef1f249da287a619b4b67f9cd14408a362e4c474ebccdf968c86791dd5f4d20010774537d65c6795c0afe4cf893823116974959434fde772315ba39053799ca7dc7f7ca5c5df88b1033f958e59e7a59df7fa5386a547c815fa2fb58439867f18a3af4fbaceba9a80f4c5bc29a15524e11cf0eb5d2b3431033b1e5c6d404fba2c23e4a3b4b3fd3df83ec4b90e19ad6240db50ede8e9c84e7f0b364edbc0324e185391108ae97d99c9a580b40b1d55458450f2383ab35da47550be516a99af717ec6b4850482c424300040745ffe8824172d4d308460a3c5626eb032250c7b2b309889ced27a4b68e253e93fa982fe2d50a5b598cfd1924e97f3cb00b781cef39cade7ea2f4a308ddfd6a85dbcb59cc0e537a94bfa57ebbcdd98353f2813112ad4ca904bb9cdee1f0f424b7cead64fc5c9b6d71d09c4d86395ad98e3b1dbe7467bf5f4548efa262ca6d8057ed0e19134156a8d9f02efcae7f93b34c55cc22428e63b55a41f82fe2f3d0c5b532f0637f1e5897107a4306834d7bc9c63dc4ed50a7e4fddb8da32601b2dd9a1d3ed41285ba920e79fc63d9660475b9f9062b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467654e22000000000000000000000000000000000000000000000000000000000000", + "0x3ee08f35ff4ac7f64c963d42c20a567cc04a77669a68acb109549deb2642d870cdd3e5fbef1f249da287a619b4b67f9cd14408a362e4c474ebccdf968c86791dd5f4d20010774537d65c6795c0afe4cf893823116974959434fde772315ba39053799ca7dc7f7ca5c5df88b1033f958e59e7a59df7fa5386a547c815fa2fb58439867f18a3af4fbaceba9a80f4c5bc29a15524e11cf0eb5d2b3431033b1e5c6d404fba2c23e4a3b4b3fd3df83ec4b90e19ad6240db50ede8e9c84e7f0b364edbc0324e185391108ae97d99c9a580b40b1d55458450f2383ab35da47550be516a99af717ec6b4850482c424300040745ffe8824172d4d308460a3c5626eb032250c7b2b309889ced27a4b68e253e93fa982fe2d50a5b598cfd1924e97f3cb00b781cef39cade7ea2f4a308ddfd6a85dbcb59cc0e537a94bfa57ebbcdd98353f2813112ad4ca904bb9cdee1f0f424b7cead64fc5c9b6d71d09c4d86395ad98e3b1dbe7467bf5f4548efa262ca6d8057ed0e19134156a8d9f02efcae7f93b34c55cc22428e63b55a41f82fe2f3d0c5b532f0637f1e5897107a4306834d7bc9c63dc4ed50a7e4fddb8da32601b2dd9a1d3ed41285ba920e79fc63d9660475b9f9062b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467654e22000000000000000000000000000000000000000000000000000000000000", + "0x78be86ef7d1ced540c1edc91ff13a4c3d2a431f0fb3cf74a4df0a13f0c2f520a6c7791a241c60eaf7a6fbd733e46497c2aa69456359031175a079647cceecaad00c8fccc039be8ced2baa9ab70ac88cff9cd5fbc4828453eac140d8dd1a9633a53799ca7dc7f7ca5c5df88b1033f958e59e7a59df7fa5386a547c815fa2fb58439867f18a3af4fbaceba9a80f4c5bc29a15524e11cf0eb5d2b3431033b1e5c6d404fba2c23e4a3b4b3fd3df83ec4b90e19ad6240db50ede8e9c84e7f0b364edbc0324e185391108ae97d99c9a580b40b1d55458450f2383ab35da47550be516a99af717ec6b4850482c424300040745ffe8824172d4d308460a3c5626eb032250c7b2b309889ced27a4b68e253e93fa982fe2d50a5b598cfd1924e97f3cb00b781cef39cade7ea2f4a308ddfd6a85dbcb59cc0e537a94bfa57ebbcdd98353f2813112ad4ca904bb9cdee1f0f424b7cead64fc5c9b6d71d09c4d86395ad98e3b1dbe7467bf5f4548efa262ca6d8057ed0e19134156a8d9f02efcae7f93b34c55cc22428e63b55a41f82fe2f3d0c5b532f0637f1e5897107a4306834d7bc9c63dc4ed50a7e4fddb8da32601b2dd9a1d3ed41285ba920e79fc63d9660475b9f9062b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467654e22000000000000000000000000000000000000000000000000000000000000" + ] + }, + "balanceProofs": { + "balancesContainerRoot": "0xcfe14ca1fc0d9fe2a2ae362d87b969cd5a341a939b676e167099eaf26f52322c", + "balancesContainerProof": "0x6f9d2bd9d73047fde9fda701a8c993821ef24422066e90e093d1f8d11edaa8a6ebadf3ae1e02ce94ef516ba1fcd21cdb892a2951b5285ffb176e1e0d4f1bd43dbc97b4c88df3f098ebefa510e9ec36556b3d08f9bcd2fd7ae7970b92bfa9c02952838195213463ef1a331f2f280ad6591e0d495d7b985473c18606ed18c1323b08012a47cb5ebb26fb42eccb7127c58ed0c7a2e400dc397e91aefbb3d7803989b1a96c2e4be9d40f1d066b08a3f893426bc2a4ae600ebbbdb9b86d96addd67274ef154b7e63d373f7df682344a1f7b5de7bb0826b046cdd14166ebe9c163d25be391287a9e590fc697dc86bb293adc40a6c58621178b018aa82058c60b59fc05de0859955015d987a7b5175a46e687529a3a1abaea12f9f10dd4499c729e34b5", + "validatorBalanceLeaves": [ + "0xa314c2730700000084d0d5f61f000000a50bc27307000000c2dfc17307000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "validatorBalanceProofs": [ + "0x790bc27307000000880bc27307000000378ec1730700000035f9c17307000000cd3766fbbfc7c53c9a34b68a5cdd0a63e232495bc7328f6c545306d1204c17526d990b7361f6f7aa725fe0a95ae17a8b500a0be6cb10f567f03e9def30f70e1941c02204adc0fbe57974040fb37d9f1d709f2be60923954c2c87ee62d3d707a33a9f992f59bf03251b9163a73e473c56abfaa6988fbf14db481306e331f6af71ae8e551ef2680b1ea4e8e9ac2029af55685c1b0c57f89c12a045d5173e79dbe8c949f45a7ad993a9b5b28d6fd9bcec81748ff9c13a5b62599b8b37ba1cce0f113d092c5a55edb9077191fc8437eda8555dfadba7a5fed45789ca8f9aa2fd93b0cb868637225374a9b6f79acbf8843db2f00b17d9e687a387b8663c0e293c4536762c1f2ad21cf3bb509abe0acaec2f88d6140088af323ee0ba2ce58daee3caface1bf04fc14976cdb8351aed15bb7c466086255b53cfe49dcd0838d3a103809b241b650136c319536c06d684a391ddecd4c80037509d1d6dda3f579d8dcbb5342998b38d75db432cadd65f7790fb3d88166e330aa79e3edeac2227f807a72c56e4dd4f3e01d3243bdbe65c8d1b925a775bcb046e4ef14353b018ccb69ac37862f4c80979ac5e93e6ac12de7d23fd16afd43c07d7e6978e9eb5ae94a4731c6325b8dc90c641c368c566300d17bda2aa2b79ccd9d3d4796b878ad08787841f07bb3582bcc9f568223ad87aa3eef87b8db21d52202dbdd188e3fc5a05848f6779e4dc7ed11a0c65341b9faa4de38595fec48a0d4940e147b46048512094d35e3d23050dd060bfeb87461ab550829ee044efeddff8360622ca0c2c8f51d594345801f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a7426451e0000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4bdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30d88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa187eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1ffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92be6364cb24300098162d7453ca909ee4f6bcc1e08c8ef4ff9191672582e00c725bec3e563c894f479acd0d5ad6891ad6efd16a550007ca6b80f672c0c12b778bfdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da729378400745c0464b8cf751eec571df5152e8304e326058dc713fa8df9077bce87f0513582bcc9f568223ad87aa3eef87b8db21d52202dbdd188e3fc5a05848f6779e4dc7ed11a0c65341b9faa4de38595fec48a0d4940e147b46048512094d35e3d23050dd060bfeb87461ab550829ee044efeddff8360622ca0c2c8f51d594345801f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a7426451e0000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4bdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d7154116558b11c7b0a4db53eda85ccf0cdbca0c5391a5eb24df2b39b7b9e398f6f7f05bdd21b9030d37c5910313cb29428abd94b7ad5da198b39f90d8ec365796450e7a4da373f23cd784f162c1298ffa3a76d495cb96d93e718bd699cc99436d3e3f5349a438b8ba0199e979d91f7c5749748414a26f0c68463cf6a143f33a14ab6b174f995e60463b3b2d174db53b54dc95c145ccc544a533f302b6ff2492a74fa25e9bc7e20b52f6a7933c6e2d506bddc12a945e65a673dce609c8f982c7d9156b52894dc0097a5a5a163a8aa6dd2e2dfb6d502c80b1b4a513cbe52b8b51dc21a37df7878b946ba812e58db2d40f7255ec6fba3e17032b979cca7643749cd6f457e49ec0767cf4fad351277cf21f9ffd5e35ddf05845432556783efe14427bdfe98c0a5b7e7563ee59ee56a935ade5628f0faf0e38394f14a20bc21936660c61ac2057028bb5711cb8e9fbb8ef3ace4a86f8b15e86b020edf878300ff85b5338bdb6f47a7e2c0756a6ab37547ca6ccd6e9b4adc4aee088a77f2939f6a373269b8dc90c641c368c566300d17bda2aa2b79ccd9d3d4796b878ad08787841f07bb3582bcc9f568223ad87aa3eef87b8db21d52202dbdd188e3fc5a05848f6779e4dc7ed11a0c65341b9faa4de38595fec48a0d4940e147b46048512094d35e3d23050dd060bfeb87461ab550829ee044efeddff8360622ca0c2c8f51d594345801f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a7426451e0000000000000000000000000000000000000000000000000000000000" + ] + }, + "validatorBalances": ["137.28519386", "0.0", "0.0"], + "command": "npx hardhat verifyBalances --indexes 1930685,1998611,1897126 --test true --slot 12021100 --deposits 1,2,3,4 --network mainnet" + }, + { + "description": "The first and second validators had a balance", + "slot": 12153439, + "validators": [1930685, 1998611, 1897126], + "blockRoot": "0x2fef147728f441c0c1b19f894bb5b5afc3e1a42baa233f29976e36a9e1a6310f", + "pendingDepositProofsData": { + "pendingDepositContainerRoot": "0x4fa6ede806ec8fcbbae148e86fa9a0670676c8b7f8d9e0b724d91b341f6f97e4", + "pendingDepositContainerProof": "0xe0ae560ba496f96c4f29624320a452effe87b7b46eaabd7f19cf887ba355ce51f22a181c2996c6ad78bf4bcdf936a947f4de1b70c0d997b6fcbcc078e5887996462ea678962d9b7a9dcfc56f12e6865de8259a528b66c3bf24d015759e363082c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123ca748247fcecc853c1ce736bcefb1c3799d0f9621bc3d502e7a55a03264954e36b6e6086e95d327b62029b658fcf3c96af5c36622d11e40adf022be14d25dd6f83ff15e1cb8655af231f6d002d65fa6fa38f036dc55bb1834e59fdc5b162f0f6475407d90ab7c49689a3eaac8766eae5bed84012767ff8d5011063445930fb9e4", + "pendingDepositIndexes": [1, 2, 3, 4], + "pendingDepositRoots": [ + "0x4d38915cfaf6e105cb34da847cc05f67506dcbaa0820293803140cb6045f717c", + "0xdcc44248e2b72204c83f8211446c51a9567fc0fbdb8ee0db650cf8321d554dd8", + "0x2236c6ba1e42c86210ab1b4d46011ffa848ad3c33fe26b2dfc187016e046827f", + "0xa142c8425505001d776173e7f9dd73ad1c20998a51dfd04171a9f93fefe94b6f" + ], + "pendingDepositProofs": [ + "0xafd6e86442a8620cf72daaa3b134af22a025f771ff3132c1657dd474d42a9e571a2685988b8e6c179758fbdabdf36b1590f43a71cd71ff881999f3bfed99759bf7f2f109bd05ca2a2c999c76cd94bd74af7b8b84a1666314824a98eea7bd5a467c3e0316e922b459a270f88b18f697738af4958e167898fc2692bfd4dc76af5b7422fb78672d5717b62302d2e7a21f01c28a8fca19178239af091dae1084d2c1e627b797943ff69074bcd843f7fb9bcb80b574bb88dbe252490892f3676fc84ed5a36f677d7ee2363f5917b8a3d19f9085f5692bacc79e16a7404e4b7b7833cd2d5387674d715ec76a4cdef6530d74c3d965779b2590f60e685926b6ab9967cf8133f037629676c4ce25ec479b8872a5a3a8be35b52a012491d7847e9bb1be376a52dc5fdabc74019db3e12094591c91ae706c780a0ef92a6b364ad088b0521d0a5af8693f7f118b92f662659e6ce55b059e1e4cd2c1bf5812fceafd246b1e0cf0a6e1462212e94186d3a900e16acf120a20b1f09119d3530588c1fc7a3347c7f0ace84ca484756768ada96aff92ac7bc3d9875631a95aef568df62f6f0718abfad680d1b7afa17d59ce9bc4654416116607cad554ceaaae39ae5172aef94edbb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467659a30000000000000000000000000000000000000000000000000000000000000", + "0x2236c6ba1e42c86210ab1b4d46011ffa848ad3c33fe26b2dfc187016e046827fcdf03ba53b0c34d1c9f9d5de7440b874ba24f68f30eb1657651d1e54c2c7702ef7f2f109bd05ca2a2c999c76cd94bd74af7b8b84a1666314824a98eea7bd5a467c3e0316e922b459a270f88b18f697738af4958e167898fc2692bfd4dc76af5b7422fb78672d5717b62302d2e7a21f01c28a8fca19178239af091dae1084d2c1e627b797943ff69074bcd843f7fb9bcb80b574bb88dbe252490892f3676fc84ed5a36f677d7ee2363f5917b8a3d19f9085f5692bacc79e16a7404e4b7b7833cd2d5387674d715ec76a4cdef6530d74c3d965779b2590f60e685926b6ab9967cf8133f037629676c4ce25ec479b8872a5a3a8be35b52a012491d7847e9bb1be376a52dc5fdabc74019db3e12094591c91ae706c780a0ef92a6b364ad088b0521d0a5af8693f7f118b92f662659e6ce55b059e1e4cd2c1bf5812fceafd246b1e0cf0a6e1462212e94186d3a900e16acf120a20b1f09119d3530588c1fc7a3347c7f0ace84ca484756768ada96aff92ac7bc3d9875631a95aef568df62f6f0718abfad680d1b7afa17d59ce9bc4654416116607cad554ceaaae39ae5172aef94edbb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467659a30000000000000000000000000000000000000000000000000000000000000", + "0xdcc44248e2b72204c83f8211446c51a9567fc0fbdb8ee0db650cf8321d554dd8cdf03ba53b0c34d1c9f9d5de7440b874ba24f68f30eb1657651d1e54c2c7702ef7f2f109bd05ca2a2c999c76cd94bd74af7b8b84a1666314824a98eea7bd5a467c3e0316e922b459a270f88b18f697738af4958e167898fc2692bfd4dc76af5b7422fb78672d5717b62302d2e7a21f01c28a8fca19178239af091dae1084d2c1e627b797943ff69074bcd843f7fb9bcb80b574bb88dbe252490892f3676fc84ed5a36f677d7ee2363f5917b8a3d19f9085f5692bacc79e16a7404e4b7b7833cd2d5387674d715ec76a4cdef6530d74c3d965779b2590f60e685926b6ab9967cf8133f037629676c4ce25ec479b8872a5a3a8be35b52a012491d7847e9bb1be376a52dc5fdabc74019db3e12094591c91ae706c780a0ef92a6b364ad088b0521d0a5af8693f7f118b92f662659e6ce55b059e1e4cd2c1bf5812fceafd246b1e0cf0a6e1462212e94186d3a900e16acf120a20b1f09119d3530588c1fc7a3347c7f0ace84ca484756768ada96aff92ac7bc3d9875631a95aef568df62f6f0718abfad680d1b7afa17d59ce9bc4654416116607cad554ceaaae39ae5172aef94edbb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467659a30000000000000000000000000000000000000000000000000000000000000", + "0x39d529a8b40a79bbc11b00c9c557a68f74586793b075113a20cc97386b39e37282909bbfa13d76ebadb1eae85c3c990e6cf32aca129960d5c36258ad463d027d21b885448be4885974f8f330fd2b3e1b0a30cdb0d78e5a8a453c7aa7b2e84bf97c3e0316e922b459a270f88b18f697738af4958e167898fc2692bfd4dc76af5b7422fb78672d5717b62302d2e7a21f01c28a8fca19178239af091dae1084d2c1e627b797943ff69074bcd843f7fb9bcb80b574bb88dbe252490892f3676fc84ed5a36f677d7ee2363f5917b8a3d19f9085f5692bacc79e16a7404e4b7b7833cd2d5387674d715ec76a4cdef6530d74c3d965779b2590f60e685926b6ab9967cf8133f037629676c4ce25ec479b8872a5a3a8be35b52a012491d7847e9bb1be376a52dc5fdabc74019db3e12094591c91ae706c780a0ef92a6b364ad088b0521d0a5af8693f7f118b92f662659e6ce55b059e1e4cd2c1bf5812fceafd246b1e0cf0a6e1462212e94186d3a900e16acf120a20b1f09119d3530588c1fc7a3347c7f0ace84ca484756768ada96aff92ac7bc3d9875631a95aef568df62f6f0718abfad680d1b7afa17d59ce9bc4654416116607cad554ceaaae39ae5172aef94edbb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467659a30000000000000000000000000000000000000000000000000000000000000" + ] + }, + "balanceProofs": { + "balancesContainerRoot": "0x799f1fb9856c719d0ead17cf9222934c494e73568ef79d34788bf6a2b70fddc6", + "balancesContainerProof": "0x0efd3bef10a1e88413d5d1be3394c79e4ac8be4abbc888d19655c0926043b84d35d362cfbbe18c2c9c6dc30eecdfcb811d85130c5629d9519066551f7b69289f7bb844c651f1a86b07377c7771c42a4efb2613f1b3cae35fe09e83d89afafbe5a7e87e9e0840c5cba3f5343626e032d8a590cc7b6e9d8b73b87a421075a4c73152d9fccd927485d01877c8d9c3f0d8e3d08ee68fc78859878ca97ab65ad94402af80ef7aa69f43d6902e649ee8492e392c84134f6909fb1b833a9f418087eb73b6e6086e95d327b62029b658fcf3c96af5c36622d11e40adf022be14d25dd6f83ff15e1cb8655af231f6d002d65fa6fa38f036dc55bb1834e59fdc5b162f0f6475407d90ab7c49689a3eaac8766eae5bed84012767ff8d5011063445930fb9e4", + "validatorBalanceLeaves": [ + "0x41f4b57307000000b88a2a0320000000c915b673070000009749b67307000000", + "0x7bcdc6750700000066dca37307000000faeb672708000000a62703e90e000000", + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "validatorBalanceProofs": [ + "0xd552b673070000006639b673070000005889b573070000003453b67307000000b3099a8a826cb1ba94ca652e0a1740a10bbc4fb0a7ded5da81299828a5752733f1912d7507e8b6cadf1dd3e7aafb9438ced1a35606ba923010bd89d8264a9a56264732b1cabbbeb5838e4e806eee6561fd45d2d654920d509b16fe1c6a45eb51a64dfcb871407cf2f43cbcef26ee644fbc0e9332ddc2f09616108b52093e29cdbabfa17f57f089dada86a0c787afb0eda6b6a9cf5f3a19d318623193a2e6eb5397b00d2287b4c872dfa16c742d7ad3a89ad0695db3871c54aaf0b89101a2b480bb48d7f1beb3c041909540f5e3cbcd899cb00fc87c89714100a74aba97732150f6a8418f331db0b8eb033d0d78c32219d01ee7464b76e59c3e32cdd92da0113a1f5ef6d4bda1af2fbde0cf92eaa0b16c4d91ea6539eafddd57880bb107cd548c59205390839355571ca9a68550ac946ebe9ee9c3d8b5b5360023ddc4a8631912a95b39ed85238f2815e8101811a957c408f2c2a8d8f37f78c6ab8f13d7e206271c616ca9ffb44ed10d70997e4c6ce8ccfd7c5b164431bf0272f97ca4f4713878c291b04518a16a02f230255e89a330a63a293c72ad930c5853b2ce7acba1c7193595e04cd630ce8e4b72e35ec89d6f8c1bef429ccd322590e6b07dcbe12be3325182e631d8259e30b75a96e15cb057b8ae5940e6db188fce3b88445d5256bd1e3d9f97c705470565a9c019c6860626e5d0b512aae876d9d7e68bae4df2967547cb1be68faeb645c9d0bc5c1963ed10aa6b36de7d7cad1103f6988b77451bddb33d2cc08806c92076bd539bc0e0af349c53306b2312cb052f9befd4e3bf6aabb5f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a7424ba1e0000000000000000000000000000000000000000000000000000000000", + "0x8b5f81740700000080eca3730700000087e2a37307000000f7dba373070000003c9b315b036f4b138cba7297c1e6e53c80583b71e141698542848658ef4a3e3838dd29d62bc62309409b788d55d101e520d41ea8e3f72386fd569b08eadb627ddd962e9e4c8c2600e06a63122153ae2db660530572b133594a9f214dcaa7ead83ec0450e7c693a9754cf852ab1cf691258245f919d7137cdc13094fbd53538aa2a2f75fae7038ab4b74ecb6e1eff9970b0c48f5caa1d93f215b33c6bddc150dfda81c88fe313f4cb9cf1e5441ebea9670a8613d629cc1c9967811e4c049ed937632d1cecfacda3fa3e1641150a4657f8544a9fdce6286d30b41f0ffa25206181806b5f9ba807a1b19f83100b65296326631108872b13a3f7c2689e3c1063726ae9b71564f851519f3f0cdc8d65c659a306f92f7e774de0a3f8c183a862410c443e701cdc49280b903ce10af5948b67e13970be1b7f8f4db9594345677e12e22f4d50a2e2df55ceb7481fc18b8432af32e170cd28703b632a3aed51e94767a75b96071faed1905908d2b9dd7f6ae359f7e2e93173845e5e7e3a5f1d82d9c4994e6066f1749f111aec44d60b50b1ca4cb6d45dbc580b4657c27a0376afeffcf10db58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784891cfbd6e3470b128536365717433a0baf2f5d38f664da6bb66cf93b625d8e123d9f97c705470565a9c019c6860626e5d0b512aae876d9d7e68bae4df2967547cb1be68faeb645c9d0bc5c1963ed10aa6b36de7d7cad1103f6988b77451bddb33d2cc08806c92076bd539bc0e0af349c53306b2312cb052f9befd4e3bf6aabb5f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a7424ba1e0000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4bdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71b36d1ac22a4fb1e5c6d2d670d2b0b0e29111a1a9a1cbdb1bbfac05e09a35f24cd3e5d619750845a647104476f92bc28f77e45ba616ef16dc537923522828bde66a92ed6476527d0a43f8fa96cc0ec7a3ca046cb5d3e2c2601d1d058ba4d0205a305ea26f4d67565efc36958b19a7252fe6debcccb91cc31ea446f4a3348fba7c9a3bfae90845a9bb26e1ac898fb328a55b23dd3e757b91f94358c3e31a8a9ccc5452116d6348b210a30f9f9fc4cae3148995869fda1db15154683968807d88a19c7b9d9e9db7d82ef53b451a46c2b298ca126f5039d5af58ad9e959b4710ba98b1719ba47d271458e21e509ed6dedbed1ab3acd3613408267d325eca6e9f3887500b602d69ef2311256163807f7e3be3d38f3b066eb0dc809fb5ad3bd786b5d4f3d297af66b23e7258915269e073eac5ea3cc7c8848a305096c8d1614ba78d128e15fb2d799fdfd7c3276424a23b5f250a68ee34730f4b6fff960ed9eab1cfb53c8044bfb21d8b97816a0559df8f4c32756241a9c212fefab25022599cf93e5b5182e631d8259e30b75a96e15cb057b8ae5940e6db188fce3b88445d5256bd1e3d9f97c705470565a9c019c6860626e5d0b512aae876d9d7e68bae4df2967547cb1be68faeb645c9d0bc5c1963ed10aa6b36de7d7cad1103f6988b77451bddb33d2cc08806c92076bd539bc0e0af349c53306b2312cb052f9befd4e3bf6aabb5f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a7424ba1e0000000000000000000000000000000000000000000000000000000000" + ] + }, + "validatorBalances": ["137.492073144", "64.03884023", "0.0"], + "command": "npx hardhat verifyBalances --indexes 1930685,1998611,1897126 --test true --slot 12153439 --deposits 1,2,3,4 --network mainnet" + }, + { + "description": "Both validators have earned from consensus rewards", + "slot": 12157779, + "blockRoot": "0xc89da37b667e62431891cdf55a4c6e9ba9ec1d8202f4f1836a701272e6a9f6c2", + "pendingDepositProofsData": { + "pendingDepositContainerRoot": "0xaca9cb5c0d6bf07b68f97cb316841f658882a4598f2eae0c4d47b42f8ca0baf4", + "pendingDepositContainerProof": "0x16cef8a9b89bb43b5083deaa0256d929441637e7f3d9665ab5baf241d3a9bf2ca905124b9d1608612ed56452ac79940b2c34ee1e42fd63ba2f0b684d6bcce2abc145378404877b3350e692372912be480b0d9674156f9d673ec8747247dfa629c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123cab377f5dfd70fe4f288db18831762fb896617f0dafae6a0c538b056091de510563514282d1ff5d555bef25f9e20829ee06f77ded97c4a813edd5a5e8e46d29c9b554c335b1c056a586bf62cb30549a2f79403604cd68a337c262c49593a3eca9ad461af32dd665e0682aa8b99ee20c4059d607e2abe7144525b026ccaccac334", + "pendingDepositIndexes": [1, 2, 3, 4], + "pendingDepositRoots": [ + "0x3814bdb0c9dbe7bf4e5e9a9d4bfbd6cec3fafe293ab383e97ba02b6d82756943", + "0x41273993790a01263ef36e163007e70cfad98aba471d57634f5cfd943ee8ecef", + "0x41b0a2f41b2956643ba2e3f31d3c39deb8302c3413a9ce7bba9d0525ffed8f83", + "0xb7e94cd0cd3d97e88411a76b73f0288e5b39dddeecfca65857f1a191b9d2a213" + ], + "pendingDepositProofs": [ + "0x00056c5831f1a482a93bf1dfa173cf5d25a68d1a0fa34427ac5d469cc4940d35d048cb439fd145acdd1daa1cb7e78914649a4215fe68cedb85344d6776af0de51f1d576d950a66f2f42856502e336733afda95e0870915c6c6720a88c338b1e4bf59da495c5c8546d9c2e9d55cb2df148436e80a90c3aa77a23e5e019ea1ff2ea448abb2e668b3ae34c48fce9fe5b443eac9a863cfb1500b5b7b9fb2e9fb7e474b2086557fa211bb1e14c3b1d95eecae980740a68af1e16515141ad0823afe434adf0303428b1d8ef5b9399289981d39c6db2e3b2482dbe5c18eaef38754d8d3c96e041ecbac3f314ca332ef1ed7e0a822de7d8d3166b68c6f95b3a5bc62ac8f58ac3aa4fd7e49d2d9d40b61377096cf0285992a311da90032024434810700dfb42da151845cf7906035deb3aac4f8e798d2c07f7c3a66684285790ec921fab00caeff6de16bffd580548d75df90b51749254de73f371ee5373092d0d46e5501d631e011495ca08a7bb9197e93590eaaf45a8251d1577d71807da7f237316e42131c6df72b043d54dcc0001a1bb3c6723c40897c1265b75391c805f80d225d05750a2b141b8659fde4e1ca4da5053eeb26083cb6632c96ebc165f0e84ce795cab58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467654c31000000000000000000000000000000000000000000000000000000000000", + "0x41b0a2f41b2956643ba2e3f31d3c39deb8302c3413a9ce7bba9d0525ffed8f83d418362b1b5feabb1901c87e2646b2916e330051dbce152ca6f86d0f1b0e782d1f1d576d950a66f2f42856502e336733afda95e0870915c6c6720a88c338b1e4bf59da495c5c8546d9c2e9d55cb2df148436e80a90c3aa77a23e5e019ea1ff2ea448abb2e668b3ae34c48fce9fe5b443eac9a863cfb1500b5b7b9fb2e9fb7e474b2086557fa211bb1e14c3b1d95eecae980740a68af1e16515141ad0823afe434adf0303428b1d8ef5b9399289981d39c6db2e3b2482dbe5c18eaef38754d8d3c96e041ecbac3f314ca332ef1ed7e0a822de7d8d3166b68c6f95b3a5bc62ac8f58ac3aa4fd7e49d2d9d40b61377096cf0285992a311da90032024434810700dfb42da151845cf7906035deb3aac4f8e798d2c07f7c3a66684285790ec921fab00caeff6de16bffd580548d75df90b51749254de73f371ee5373092d0d46e5501d631e011495ca08a7bb9197e93590eaaf45a8251d1577d71807da7f237316e42131c6df72b043d54dcc0001a1bb3c6723c40897c1265b75391c805f80d225d05750a2b141b8659fde4e1ca4da5053eeb26083cb6632c96ebc165f0e84ce795cab58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467654c31000000000000000000000000000000000000000000000000000000000000", + "0x41273993790a01263ef36e163007e70cfad98aba471d57634f5cfd943ee8ecefd418362b1b5feabb1901c87e2646b2916e330051dbce152ca6f86d0f1b0e782d1f1d576d950a66f2f42856502e336733afda95e0870915c6c6720a88c338b1e4bf59da495c5c8546d9c2e9d55cb2df148436e80a90c3aa77a23e5e019ea1ff2ea448abb2e668b3ae34c48fce9fe5b443eac9a863cfb1500b5b7b9fb2e9fb7e474b2086557fa211bb1e14c3b1d95eecae980740a68af1e16515141ad0823afe434adf0303428b1d8ef5b9399289981d39c6db2e3b2482dbe5c18eaef38754d8d3c96e041ecbac3f314ca332ef1ed7e0a822de7d8d3166b68c6f95b3a5bc62ac8f58ac3aa4fd7e49d2d9d40b61377096cf0285992a311da90032024434810700dfb42da151845cf7906035deb3aac4f8e798d2c07f7c3a66684285790ec921fab00caeff6de16bffd580548d75df90b51749254de73f371ee5373092d0d46e5501d631e011495ca08a7bb9197e93590eaaf45a8251d1577d71807da7f237316e42131c6df72b043d54dcc0001a1bb3c6723c40897c1265b75391c805f80d225d05750a2b141b8659fde4e1ca4da5053eeb26083cb6632c96ebc165f0e84ce795cab58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467654c31000000000000000000000000000000000000000000000000000000000000", + "0x40b9917cd6882d12da8a0bb29097dc01d8b4599283afd38c8161480915c85983a9985d86d9864d342176176f4c9d96a8cd66380aa7574328079b0b9fc72bc3abbfd16ceead04b80cb63e8ce9085f571185519e74461a4e5d45c0c493c9a3cfc4bf59da495c5c8546d9c2e9d55cb2df148436e80a90c3aa77a23e5e019ea1ff2ea448abb2e668b3ae34c48fce9fe5b443eac9a863cfb1500b5b7b9fb2e9fb7e474b2086557fa211bb1e14c3b1d95eecae980740a68af1e16515141ad0823afe434adf0303428b1d8ef5b9399289981d39c6db2e3b2482dbe5c18eaef38754d8d3c96e041ecbac3f314ca332ef1ed7e0a822de7d8d3166b68c6f95b3a5bc62ac8f58ac3aa4fd7e49d2d9d40b61377096cf0285992a311da90032024434810700dfb42da151845cf7906035deb3aac4f8e798d2c07f7c3a66684285790ec921fab00caeff6de16bffd580548d75df90b51749254de73f371ee5373092d0d46e5501d631e011495ca08a7bb9197e93590eaaf45a8251d1577d71807da7f237316e42131c6df72b043d54dcc0001a1bb3c6723c40897c1265b75391c805f80d225d05750a2b141b8659fde4e1ca4da5053eeb26083cb6632c96ebc165f0e84ce795cab58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467654c31000000000000000000000000000000000000000000000000000000000000" + ] + }, + "balanceProofs": { + "balancesContainerRoot": "0x916a1eb4e0c346e87025e6dac37ec3a4cf42a9151ecd7fc33beb3498177ad570", + "balancesContainerProof": "0xed1943a53890b9301dabfbcc836e0d6abac3ff1366892781490631f938d84984093609ae83e3d14053cef57658d603fb5cefc11b2a4da54cb74a41dac4d51368e9caa48d343334c81f24a1a2e095575fa7156cb722dea496e011bc8ac09ff37bb5053bef3dcae7e54906fe52aecb5e96858fe46f9cc17f0990810cf5acbab83535423aa755d710f11c8a98b437693c6372834a2f91098e3e36168e96f6b5331619310b146b011abaacbd0eb472fb58a7d829ce6f0fa3d073b4df6f60cdd3479763514282d1ff5d555bef25f9e20829ee06f77ded97c4a813edd5a5e8e46d29c9b554c335b1c056a586bf62cb30549a2f79403604cd68a337c262c49593a3eca9ad461af32dd665e0682aa8b99ee20c4059d607e2abe7144525b026ccaccac334", + "validatorBalanceLeaves": [ + "0x08c0c8730700000003297a032000000068d0c873070000005e15c97307000000", + "0x4299d975070000005a74b673070000003b717c27080000003c9c28e90e000000", + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "validatorBalanceProofs": [ + "0xc315c973070000002d05c973070000007d21c87307000000fb1ec973070000009c63543bc9a30dc5cd7d84a15f4436e52f64e6e13e7becf227ca2c4b647d6f18c17abe1a8eaf29f99c2fb4f71eb7b775b6677508088c6c6c0df3fcd3fb131fceb0aaaa7b94c2dea4451f3e9ff3e0e22e411e60de3be4e21f0dd680c8175b37aac51476cea0ce892f545aef746f671b0631e3d256c27b3e33d53008fd538da5233532c2d961b3546de59377a2790216e2006ec9efd9ce1c3abf78c1e4975cbaa220a513034a6bf36a591c583d1b21fb3b7dab107c13f7c04a0e096a4668c593069a596b6cece8ab6abca72a6f21ab60b1d4435b1554fd59638e22d6f9b09b6dfe31dee10ac6f79320b26feddab5b65ae2218657cb54a952411470fc6dd965ba8100aefe97d0473b44c5f52d21f747831f5836f09fdca33b3290983640f22c9d2a6fc574fad7e412bcdab5c6b9a7ac09132450c47a9c1726c37fe5e4d79bf2db220cabe39d6d1fd3ff23a6f9e2039f5334637b968c78f98f8a8815a6f266a1da859a9f4c85f627e05f969aa764307ac226f8b89d39456eff2fc28f440f99a4ad5d0243c7dd9605b3b6d33c9d9853faea32d1146ed2161d7a84e5db895ed02211233bffc520354d986aee7850a96464a023a0306e68f5f6c43c4d9273ed61a6e31f3b397d632647fc7ebd56d8ebad6f33fef1598bcc25862bf470042e14d788828314c5b57712546918630b31a683e69c370511e6810ae81f4de80b8926453ea500db852159999612ca027445128d2310aab7da82d9d095e3e3715a3da466c8d6a8ad4f19dde4ebb4c10fd009264376f04723c53777862e887e2069ef5142f1f5ecf893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a7417be1e0000000000000000000000000000000000000000000000000000000000", + "0x18f69374070000000674b673070000004eaeb67307000000d29eb67307000000d5b197d28f7edc0f9e79ff2db8e425f6551290d8689168acd853b41a5b9af96dcd0288729d92537a0c80f0e9de1687b14d9992637d58486993152f93e5fea1cb36d7908abf6cfd852cda172ec7a4dda3d9d5ba2e7087aedd055f2feab3d45bfce33396c4bd3402e2190f3e65cff6e20d0ddfc7f6c4a65c6c693f139029437a4a9be9b1b49f759daddbbc3a36b05db544daf9750674d3e25afd683f4c56bd4bba7b2deacc0eddb6f0f9c9dc16c6c1fcb9f58787563ff596e5924656f00641456c9cc77230f7f6b9a04bd8cf29245264ff0e482a0ab33b4eaabd348e787f6dc2258e84632ec5110bd27177397af4dacf25e8a7d09fcb23b397911f2acbf9d66ccd81da646c80b12846687ab8cb5ccdf85182214087fa4eeb59585d2b0357cf21669c1b49f2ce4552d1cf7d22b51eb4c8031dc0ed6f3f35b4825a14be94b2243c5c561e59b318c65127415c8e8ff0e91909e693b2747a5a35c585feba2df10f8fa4bfaabe4564d3c44f4d60cbdc32898004c551814b6c68d3ebad1297b25e9ff1057024f829cd2163f6fe484e7ec6b2b1655454541c826b8f38327678c24bd43e6cb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da72937845080e2a965d075349c56d6771b7041356541ec8a37b6b72040a2a212006e912114c5b57712546918630b31a683e69c370511e6810ae81f4de80b8926453ea500db852159999612ca027445128d2310aab7da82d9d095e3e3715a3da466c8d6a8ad4f19dde4ebb4c10fd009264376f04723c53777862e887e2069ef5142f1f5ecf893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a7417be1e0000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4bdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d719879ecb47c5adba6f21b3a2a06406509c5d8b9fbe1e6c28c76b609258888dc7ded3ca53de3733a93a4097ea4d3e2229682149fbd5883307470a6fb3261b271a4aa7b618db40b868cb343318d8e75c471fcfac3f9edb2816f5c4b391c747e62f49e60fc7a6d3a22a886d0e6d9efe690816f5a65b2dbbd1e13f7e1779b8ad6cba9fdf4d22072a7777ae65a4f8b02c6bc56af73ebc81f7f584d08087ac8b920ab0d01a3badf968131196a94f668225e80fe53344e9e13f4f72344794319a1e4e768976901b92c9899fb88013f0608f118ec4643d4844cb6fa45edd7bb26e5e3976ec9d2ddee39a6ed81ccf0e3cc522bad265e139982f7f3fa57a2aa69bdc11a2816ef30ae19ce77fb72c1f547b0902d7ce7d976d07e063077e376778f02e7a4e4ebacd66743e2f06c94017688fba3f58b6d966df6f0da08ea1f8d9352430f456bfd45fd8dfa9524a6725348a5e3af194881be82292b234592264cd36111d4e44b27ff1c935be528cdc3ca8ca0046adfe94c043d369d1a26216daaeacb115afaebe93b397d632647fc7ebd56d8ebad6f33fef1598bcc25862bf470042e14d788828314c5b57712546918630b31a683e69c370511e6810ae81f4de80b8926453ea500db852159999612ca027445128d2310aab7da82d9d095e3e3715a3da466c8d6a8ad4f19dde4ebb4c10fd009264376f04723c53777862e887e2069ef5142f1f5ecf893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a7417be1e0000000000000000000000000000000000000000000000000000000000" + ] + }, + "validatorBalances": ["137.497291011", "64.041294908", "0.0"], + "command": "npx hardhat verifyBalances --indexes 1930685,1998611,1897126 --test true --slot 12157779 --deposits 1,2,3,4 --network mainnet" + }, + { + "description": "All validators 21 balance at the same time", + "slot": 12204316, + "validators": [ + 1930685, 1998611, 1927585, 1897126, 2018225, 2018224, 1998612, 1970362, + 1922881, 1922882, 2009356, 1998140, 1906722, 1906721, 1906639, 2015001, + 2014925, 2014907, 2015000, 2014998, 2014926 + ], + "blockRoot": "0x575ac66446760948762471e96ceda18d8052c852fde10223fe46a23f0e164e7d", + "pendingDepositProofsData": { + "pendingDepositContainerRoot": "0x4df4201932071cfe3e79ac64f635bcb09f1f5d9a1c12a95719ff826d5d0d622d", + "pendingDepositContainerProof": "0xff8c3e34629e08c9ced4aca0410aa8ace8099369af2251fff4290943b8385762eaf9a9b5bab79b06bb6b9e189a8436ac09383a28cdda5496b99e6fd3ea9cb082b3b3e0ec77e323a9731a35969141325892a1970822e7655b6438c541e8736040c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c75d0f29eab0aae94a77926cbf54f47ce0adeda5605907f0f7792f98af9da030e395293c84bf0d62fb21768fbeff01380c97250c0005f7b7864b436ad0649351b7e4808fbdedae04695632e249eac5c7fbc0a2ad7f4681fb7eda3d2eb18a904a880f2ecba251286eb8f8d62e8341d5e8c882ff9fbec6115a4cc1925dbb7017779", + "pendingDepositIndexes": [1, 2, 3, 4, 5, 6, 7, 8], + "pendingDepositRoots": [ + "0xd45b358adc96512b37fcb7b2c8a6dd958ec6d439798e5bfd5658cc5a50029770", + "0xb210f05ce8f86f149ac99d169397bd86286f3c8be13d2139400439438d80badb", + "0x5be1c93f55dba77472413de11b299aab6342b38e6891deefbfba11b8b5de2ff1", + "0xa45685527e5333d6955766c67c863ef53b9574690d74923a28411c0b71d21b6a", + "0xc4dc5db13823e694070ca4c1f2cb911475413ee5417ef8ae7bc17feb1eb23800", + "0x905c0b11c5f4fa6c9f1ff3c9ce63957746a6b1db662445a2fa810367904ff1d7", + "0x8232485fd5b787b5130d508af9471e84d157ec19a1c41107fef1f69f4cabcaaa", + "0xa013f6168c94b2f80de9af3d20654d09edce25d73949eb5a640520448503be3a" + ], + "pendingDepositProofs": [ + "0x39740f90050d74af310b81f9a1b1b0b077dff97e3855ac89a7afd0777e61656f76d774bfa013a4a2d671238e71ae8416f73afcbee00f4c03ebafa27af5793af96cfe1bd6e11c97f11aa98698c21cd05184571b9bdb6d1ae2b75815f65a12c0e11b4a10e3f10dad3f1bd921a161872bd4db32433a53ab1399e70fb1fc01f9cc03e4b561c62f17b4bbf0426c45008b63f1faee817f40b572db56f784753ef0e79edd813a55cb645aa114b7a0d0ccab7d0b2696d140327835be59aa9e502f509e4b7834eefb5e6fa4129c562fd8d792533628aa924c200b26231dfb0db131547912529e6fca18a58c49390dfd05ecd973999329ee6be74860283f133619e77b151dcc4f62c1bc4601d424b26d3591ff89906a79b61be533215d34d45ee981f3d7d5bf9d5d8caad02bb5b6271129b857b6bb455f0edc337aff683009017070e42c7492d0a674f87334bac3450458502f3291b18f2baa27aa3f8f96952fe8ff3ee2e323f3b9c342f28b7ac2ea63bcb56c3339351cbacb6bf759821db10a3fcad957cdb711e83760c1642230ed1e901c9fef940f6c5eed164f3e44127fff1f046262774ffe12e3e066f00cf56b92b0643eb5c802acb05ca7f2cee370467f274ebc9607b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765d123000000000000000000000000000000000000000000000000000000000000", + "0x5be1c93f55dba77472413de11b299aab6342b38e6891deefbfba11b8b5de2ff15fc818ba7f4d9c967f8c670fae7744d657db5bd0ab961d3ddc94fdd2381da9166cfe1bd6e11c97f11aa98698c21cd05184571b9bdb6d1ae2b75815f65a12c0e11b4a10e3f10dad3f1bd921a161872bd4db32433a53ab1399e70fb1fc01f9cc03e4b561c62f17b4bbf0426c45008b63f1faee817f40b572db56f784753ef0e79edd813a55cb645aa114b7a0d0ccab7d0b2696d140327835be59aa9e502f509e4b7834eefb5e6fa4129c562fd8d792533628aa924c200b26231dfb0db131547912529e6fca18a58c49390dfd05ecd973999329ee6be74860283f133619e77b151dcc4f62c1bc4601d424b26d3591ff89906a79b61be533215d34d45ee981f3d7d5bf9d5d8caad02bb5b6271129b857b6bb455f0edc337aff683009017070e42c7492d0a674f87334bac3450458502f3291b18f2baa27aa3f8f96952fe8ff3ee2e323f3b9c342f28b7ac2ea63bcb56c3339351cbacb6bf759821db10a3fcad957cdb711e83760c1642230ed1e901c9fef940f6c5eed164f3e44127fff1f046262774ffe12e3e066f00cf56b92b0643eb5c802acb05ca7f2cee370467f274ebc9607b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765d123000000000000000000000000000000000000000000000000000000000000", + "0xb210f05ce8f86f149ac99d169397bd86286f3c8be13d2139400439438d80badb5fc818ba7f4d9c967f8c670fae7744d657db5bd0ab961d3ddc94fdd2381da9166cfe1bd6e11c97f11aa98698c21cd05184571b9bdb6d1ae2b75815f65a12c0e11b4a10e3f10dad3f1bd921a161872bd4db32433a53ab1399e70fb1fc01f9cc03e4b561c62f17b4bbf0426c45008b63f1faee817f40b572db56f784753ef0e79edd813a55cb645aa114b7a0d0ccab7d0b2696d140327835be59aa9e502f509e4b7834eefb5e6fa4129c562fd8d792533628aa924c200b26231dfb0db131547912529e6fca18a58c49390dfd05ecd973999329ee6be74860283f133619e77b151dcc4f62c1bc4601d424b26d3591ff89906a79b61be533215d34d45ee981f3d7d5bf9d5d8caad02bb5b6271129b857b6bb455f0edc337aff683009017070e42c7492d0a674f87334bac3450458502f3291b18f2baa27aa3f8f96952fe8ff3ee2e323f3b9c342f28b7ac2ea63bcb56c3339351cbacb6bf759821db10a3fcad957cdb711e83760c1642230ed1e901c9fef940f6c5eed164f3e44127fff1f046262774ffe12e3e066f00cf56b92b0643eb5c802acb05ca7f2cee370467f274ebc9607b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765d123000000000000000000000000000000000000000000000000000000000000", + "0xc4dc5db13823e694070ca4c1f2cb911475413ee5417ef8ae7bc17feb1eb23800aa58093fdf90fe0df87228a809f1c73260c15539254692c91351543ff196bbaab421e7b8541916c5fbf953890bf2a96e055ed8894ca5fb6f53cab0a5c995897d1b4a10e3f10dad3f1bd921a161872bd4db32433a53ab1399e70fb1fc01f9cc03e4b561c62f17b4bbf0426c45008b63f1faee817f40b572db56f784753ef0e79edd813a55cb645aa114b7a0d0ccab7d0b2696d140327835be59aa9e502f509e4b7834eefb5e6fa4129c562fd8d792533628aa924c200b26231dfb0db131547912529e6fca18a58c49390dfd05ecd973999329ee6be74860283f133619e77b151dcc4f62c1bc4601d424b26d3591ff89906a79b61be533215d34d45ee981f3d7d5bf9d5d8caad02bb5b6271129b857b6bb455f0edc337aff683009017070e42c7492d0a674f87334bac3450458502f3291b18f2baa27aa3f8f96952fe8ff3ee2e323f3b9c342f28b7ac2ea63bcb56c3339351cbacb6bf759821db10a3fcad957cdb711e83760c1642230ed1e901c9fef940f6c5eed164f3e44127fff1f046262774ffe12e3e066f00cf56b92b0643eb5c802acb05ca7f2cee370467f274ebc9607b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765d123000000000000000000000000000000000000000000000000000000000000", + "0xa45685527e5333d6955766c67c863ef53b9574690d74923a28411c0b71d21b6aaa58093fdf90fe0df87228a809f1c73260c15539254692c91351543ff196bbaab421e7b8541916c5fbf953890bf2a96e055ed8894ca5fb6f53cab0a5c995897d1b4a10e3f10dad3f1bd921a161872bd4db32433a53ab1399e70fb1fc01f9cc03e4b561c62f17b4bbf0426c45008b63f1faee817f40b572db56f784753ef0e79edd813a55cb645aa114b7a0d0ccab7d0b2696d140327835be59aa9e502f509e4b7834eefb5e6fa4129c562fd8d792533628aa924c200b26231dfb0db131547912529e6fca18a58c49390dfd05ecd973999329ee6be74860283f133619e77b151dcc4f62c1bc4601d424b26d3591ff89906a79b61be533215d34d45ee981f3d7d5bf9d5d8caad02bb5b6271129b857b6bb455f0edc337aff683009017070e42c7492d0a674f87334bac3450458502f3291b18f2baa27aa3f8f96952fe8ff3ee2e323f3b9c342f28b7ac2ea63bcb56c3339351cbacb6bf759821db10a3fcad957cdb711e83760c1642230ed1e901c9fef940f6c5eed164f3e44127fff1f046262774ffe12e3e066f00cf56b92b0643eb5c802acb05ca7f2cee370467f274ebc9607b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765d123000000000000000000000000000000000000000000000000000000000000", + "0x8232485fd5b787b5130d508af9471e84d157ec19a1c41107fef1f69f4cabcaaa6c4a718b2feee8124a8526874025b539eabd87ce86a9c3c49af2a922d1e9c59cb421e7b8541916c5fbf953890bf2a96e055ed8894ca5fb6f53cab0a5c995897d1b4a10e3f10dad3f1bd921a161872bd4db32433a53ab1399e70fb1fc01f9cc03e4b561c62f17b4bbf0426c45008b63f1faee817f40b572db56f784753ef0e79edd813a55cb645aa114b7a0d0ccab7d0b2696d140327835be59aa9e502f509e4b7834eefb5e6fa4129c562fd8d792533628aa924c200b26231dfb0db131547912529e6fca18a58c49390dfd05ecd973999329ee6be74860283f133619e77b151dcc4f62c1bc4601d424b26d3591ff89906a79b61be533215d34d45ee981f3d7d5bf9d5d8caad02bb5b6271129b857b6bb455f0edc337aff683009017070e42c7492d0a674f87334bac3450458502f3291b18f2baa27aa3f8f96952fe8ff3ee2e323f3b9c342f28b7ac2ea63bcb56c3339351cbacb6bf759821db10a3fcad957cdb711e83760c1642230ed1e901c9fef940f6c5eed164f3e44127fff1f046262774ffe12e3e066f00cf56b92b0643eb5c802acb05ca7f2cee370467f274ebc9607b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765d123000000000000000000000000000000000000000000000000000000000000", + "0x905c0b11c5f4fa6c9f1ff3c9ce63957746a6b1db662445a2fa810367904ff1d76c4a718b2feee8124a8526874025b539eabd87ce86a9c3c49af2a922d1e9c59cb421e7b8541916c5fbf953890bf2a96e055ed8894ca5fb6f53cab0a5c995897d1b4a10e3f10dad3f1bd921a161872bd4db32433a53ab1399e70fb1fc01f9cc03e4b561c62f17b4bbf0426c45008b63f1faee817f40b572db56f784753ef0e79edd813a55cb645aa114b7a0d0ccab7d0b2696d140327835be59aa9e502f509e4b7834eefb5e6fa4129c562fd8d792533628aa924c200b26231dfb0db131547912529e6fca18a58c49390dfd05ecd973999329ee6be74860283f133619e77b151dcc4f62c1bc4601d424b26d3591ff89906a79b61be533215d34d45ee981f3d7d5bf9d5d8caad02bb5b6271129b857b6bb455f0edc337aff683009017070e42c7492d0a674f87334bac3450458502f3291b18f2baa27aa3f8f96952fe8ff3ee2e323f3b9c342f28b7ac2ea63bcb56c3339351cbacb6bf759821db10a3fcad957cdb711e83760c1642230ed1e901c9fef940f6c5eed164f3e44127fff1f046262774ffe12e3e066f00cf56b92b0643eb5c802acb05ca7f2cee370467f274ebc9607b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765d123000000000000000000000000000000000000000000000000000000000000", + "0xda628184a180c868f928421b5025075b874ab451b9d5f0fe213dbd920d5e46343fa6e5f384c24828d6d8f37dc304f0d2d286318029ee5f2d277c0c02dc4c0ee26f46451367f24d1cbb252fdd9d6dfc17075d245beed8a83d30b9d8539b2412f0563ccf027fe02e4492d2b79153f9592533af9899506168873ccbcf383f52a594e4b561c62f17b4bbf0426c45008b63f1faee817f40b572db56f784753ef0e79edd813a55cb645aa114b7a0d0ccab7d0b2696d140327835be59aa9e502f509e4b7834eefb5e6fa4129c562fd8d792533628aa924c200b26231dfb0db131547912529e6fca18a58c49390dfd05ecd973999329ee6be74860283f133619e77b151dcc4f62c1bc4601d424b26d3591ff89906a79b61be533215d34d45ee981f3d7d5bf9d5d8caad02bb5b6271129b857b6bb455f0edc337aff683009017070e42c7492d0a674f87334bac3450458502f3291b18f2baa27aa3f8f96952fe8ff3ee2e323f3b9c342f28b7ac2ea63bcb56c3339351cbacb6bf759821db10a3fcad957cdb711e83760c1642230ed1e901c9fef940f6c5eed164f3e44127fff1f046262774ffe12e3e066f00cf56b92b0643eb5c802acb05ca7f2cee370467f274ebc9607b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765d123000000000000000000000000000000000000000000000000000000000000" + ] + }, + "balanceProofs": { + "balancesContainerRoot": "0x224844053aa78defbc76ac463d88eb07bb2cc7144b288ce2008118d7ecfce407", + "balancesContainerProof": "0x86dba98c306e74de4047d4ba77fda8ca2ff6d38a6fe291e1652f096a09c13cbd6491c1d8d2da505a815143a5b3c4b41f6f788401ec5a4eadef307a6a074bee0395607bf04ee535ceeb443c0691a60db9f6b53b476df65257887ed19575fb21e637f34d69b4a0ae6df8f10f85f03cf80dccbfdfd544e6ea3418f7e785ec2e1a1388fda5d0e45d839df23a28d7d5427c36806f53f8d6cc557b937ea6187ff0e94bb791e3d3e44abaf8644e74fff43d53822d9348163558475d85e482a79eb927ef395293c84bf0d62fb21768fbeff01380c97250c0005f7b7864b436ad0649351b7e4808fbdedae04695632e249eac5c7fbc0a2ad7f4681fb7eda3d2eb18a904a880f2ecba251286eb8f8d62e8341d5e8c882ff9fbec6115a4cc1925dbb7017779", + "validatorBalanceLeaves": [ + "0x9ac16b7307000000ca2ed30620000000a48e6b7307000000f1ca6b7307000000", + "0xf6a9597307000000f6a95973070000007f8c5628080000002053b6ea0e000000", + "0x2b586c730700000007c2820375000000d5696c7307000000c7696c7307000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x1110c3e70e000000f7e3e17307000000b3a2e273070000006167e27307000000", + "0x1110c3e70e000000f7e3e17307000000b3a2e273070000006167e27307000000", + "0x8b6f5a7507000000f6a9597307000000f6a9597307000000f6a9597307000000", + "0x1c32617307000000c43a617307000000863d297a070000000000000000000000", + "0x65d16d7307000000e400cb3a3e010000cbba22303e010000d0376d7307000000", + "0x65d16d7307000000e400cb3a3e010000cbba22303e010000d0376d7307000000", + "0xe37580730700000035b97974070000007c6c7b740700000074887a7407000000", + "0x0000000000000000c8cc597307000000c8cc597307000000c8cc597307000000", + "0x1c133f7f07000000000000000000000000000000000000000000000000000000", + "0x1c133f7f07000000000000000000000000000000000000000000000000000000", + "0x7870717307000000c2927173070000005ca57173070000000000000000000000", + "0x708e1a7407000000ef211b740700000012631a7407000000d74a1a7407000000", + "0xcc231c740700000054c71a7407000000f7f01b74070000002dce1b7407000000", + "0xf4761c740700000027771c74070000001e021c740700000029851c7407000000", + "0x708e1a7407000000ef211b740700000012631a7407000000d74a1a7407000000", + "0xfcdb1a74070000007d481b7407000000e9e01a740700000094881a7407000000", + "0xcc231c740700000054c71a7407000000f7f01b74070000002dce1b7407000000" + ], + "validatorBalanceProofs": [ + "0x71ca6b730700000051d36b7307000000019e6b7307000000a1c16b73070000008b9f5bc0dd3157ece67ea0795247a0a10607a6903f6dbe02e575bb17166de13a5d8d6c10035d983cc50162242e218029a4d346baff384191a9d457b4b7636a1123d3960732d5b9e9ad7a0232c785626cc170a38e30b8285eefaed65983087dfb33328e5cddad3e1c176ef191d1a7e37944dce36766e5149d9f9e7c7af18ff1c7beaf2daafd3ad62358bb3e6136c4903923783da5d10565a6daf2b4e1bd54263d4c21e8d02acbdd7d73a8f23f7cd5ee3ba82087694b9d7763c15727af5ac583176dc07d9393721f65d9f3bfc50b3e5ecb2ad219d3ba42c8a0cdf0786124ef19780569dc71a55d9df074abc311e1b29afa4a7cacda15ed4d189b7506fcda208dbb3afc18301c568db985132be50a90c2429d9c3b88452e9581def3f6e078b725dd63ca41efda505c63835481002f2fe7c0df736fd3002752d50d5034fddf6ae5bea29f1ccff7a0ee21bb4263e810006a8aa9e4bd8c58bc0ea5ad4b1f781baa9c849eeb3b835c7789ac4725a49772049b4d726634b63cdd39b318c3e6582f92456fd143532bf8fec2d12b83ef6a5f7db0d95ee9a14bf7c3eb207c80863a33ddf706a78d55897055b8f1b27de4683fd0c1c71e23700a7a89c8ec2f6fd17cb76d55358e6f20532c117fbf60154047ded45e406224c476a29c2d7b70ede3ad1a3e935c20198233b30a76b5a6f968af29372f2973264d8994f14665ec14ca4b8ae3d500611f7807a2bcdf9329435aaef734f304a3104fd09a16b895c7f9b981a12859c444dbb270e33150ee8decf22459e03ed9a10984408f5d397e9a6c05bcfb920cb6f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74d1e21e0000000000000000000000000000000000000000000000000000000000", + "0x8b6f5a7507000000f6a9597307000000f6a9597307000000f6a95973070000009c1e95cfcdc2c1e95ef2aa388de59a5cbf6390302a483f2328ed4183b635b2ca6ed650f19d2cc6f14059c203ce0e9b7ce7166154b055ac48a633ca17d8ad9e86f7bf3901fef701be8c1f5c3256c67342bb25e8da16937d743bfa535a33ce7a8922e7c1f2fe9c2629c00bcaea4f8cb320e934f3e6a260836dd926270ed6d711b659505de684ffba3dee2b0b956662434a9afad18edc29eb6605117df2399875eef8fd5950e6f436e3dcd6ef382b2e39d967eec02351a2d71c59d5bf9bd40f368251c743cfa505eeddffc015148c6bd11bf323ad1349c93f8966c4e0e482bce0ca86c75724a7a0d6c44bc6dc0570f8b98ca049042f2e99e9f2c3e025a3f8c50d007f1e2da96f76720bb62fd1607fa9b35ee7c1ab7d894466bf22221c139c5e14f1e88840e970486a623b71a26a30a46ae720bb8d17718366aa8e686cc314645e1e7236eddced80f23a9fb8b6826f06f08339bc739282a8fad7ea1b7a06f1360739f875cbd735037405ab38187a9f1341254ce07e1d1fab652ab569c5239ae331284ae5a3c3d3cfc144a2dce140b4c9921f090d0b4383da38309c3432d9232eb57ab58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784be5c7cb62d9debd42ab3413e622737740f1b7bb826976a911d229a9a058650de20198233b30a76b5a6f968af29372f2973264d8994f14665ec14ca4b8ae3d500611f7807a2bcdf9329435aaef734f304a3104fd09a16b895c7f9b981a12859c444dbb270e33150ee8decf22459e03ed9a10984408f5d397e9a6c05bcfb920cb6f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74d1e21e0000000000000000000000000000000000000000000000000000000000", + "0xe99c6c730700000079616c7307000000fa9c6c7307000000f5826c7307000000898a10fc2ab561a096eab063a206b201a02993154537626b40bc7dc39042df9f184715f4683c8750e902c4f67acd3f0b3fa5da79ccd5a18acf97e3ea49d58de777c1a15c904443014e99c6a3322d67f0273b7928919618e333d467cc5c657a65763b64965a78bc00b454f94de18b16c166bb1a57c2ffdf4d25be7030eef347c43eda67fbc5a210a6536cfced39b5c9b8bb50d4295baa17aed4ce77cbe365f9f78f0b20b24dae71640f00e76391f68fc91a88b963e82b3a0be21d1702566ff2ab01d3693afa706d6d5fbebbb84292ad9d96d00541276a48f7476446b516f273fb15165d608da387b7beec3557bb134616a4b7f51ceeea80508fa8f6867091e810c847b2fdbf45654b1690c60a3048f5776aa362946e48f675c7f2c54ff1f72c947b26fb5e12f4d7397130ed55c412653cbfeea967b260b9f401206ebebf160362a29f1ccff7a0ee21bb4263e810006a8aa9e4bd8c58bc0ea5ad4b1f781baa9c849eeb3b835c7789ac4725a49772049b4d726634b63cdd39b318c3e6582f92456fd143532bf8fec2d12b83ef6a5f7db0d95ee9a14bf7c3eb207c80863a33ddf706a78d55897055b8f1b27de4683fd0c1c71e23700a7a89c8ec2f6fd17cb76d55358e6f20532c117fbf60154047ded45e406224c476a29c2d7b70ede3ad1a3e935c20198233b30a76b5a6f968af29372f2973264d8994f14665ec14ca4b8ae3d500611f7807a2bcdf9329435aaef734f304a3104fd09a16b895c7f9b981a12859c444dbb270e33150ee8decf22459e03ed9a10984408f5d397e9a6c05bcfb920cb6f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74d1e21e0000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4bdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d710565e05fe8865b6f8c6d0b6f87716c312e2f904dfeff4af8470302cbfabbf36bf235dc2dc7bcdddffb30e26066cf9e039c0b30daf95d066f4bff1461257040bf1ac8e309d3c2e38a69778005676222999941faa79fc3cbd979307128fd830330472e9e89f4a8e8e6e963773ad686d42ecadc1edce99f6f10d5a89d0c08e2ae8ba34265cd3ca78dc917b04a5143b95fca1af07b0e9ac19f200445a59ba11889049a8036b381deff024d48f1177894a554cc49a65d2d7bbe5393b1dac63d491cbbbcdfc561b1e1ab05ac3574b5501fb34a8b2028db93224f259ffe593ae5b760da535f9ace8e6adf57125636ca9f695d1a11f5b534ba4240d6c24f6d780801429c229fbc6d2fb7544ffe090efb31dabadd9fc4b1afc8d23721d9ffd79014f51a1811bde4e66986e450a56c4df2690b10a3902daad11afea01c517a807748b4fbf93ab5274098ed1613ddae0782a3606c58750c22783d6025973448471c5b89a15c77e8232b3c8a962b80db035f04a92d836e443471a091e4da5c4d49c0d07c92c18e6f20532c117fbf60154047ded45e406224c476a29c2d7b70ede3ad1a3e935c20198233b30a76b5a6f968af29372f2973264d8994f14665ec14ca4b8ae3d500611f7807a2bcdf9329435aaef734f304a3104fd09a16b895c7f9b981a12859c444dbb270e33150ee8decf22459e03ed9a10984408f5d397e9a6c05bcfb920cb6f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74d1e21e0000000000000000000000000000000000000000000000000000000000", + "0x943fdf730700000034f3de73070000007ec4de73070000001a5fdf7307000000ac67cade9449bba5f4bb8fadcda6b206e4a13904a627b00431dcd733e1b7dc4a8144590d3ea9e72f3397082f62c5b49a706d88cd8f89fce354cfe92fa2bf8d6b778af6689441df6920b6e8905699d78170e39e123aef5499561caaeca22903faa1f0165d2317c78b2cce4a7dce1cb554cd329443afa1e7a369e0ad0b9331c1e537af776e7a8b2e5f81b4490dd2b746f0eaf4b072fa0c2f2b98f0e9648014af9355aee837e26f343382e25b12373909809c9f9f6b7b1f04d957773133c3f7a32be8f735666437ab805964d20dd977b2a892eed69f55a224a4936a741ba0448fb040460af64f038f9e109f90c8a43e049468bc9598ca7c9965145a579c67d4da77eff774db8c13b039e0930393ae26156510f6d035711adbec03a33e96cd1c766d6ce456c592136bb2202bb5f6eb68d90c1a7cf4ca948ec31eae4402bd5f35fdbca9ac6d4f13e80def3154a9299974c637247e61f1fdfa97a59eda5e085c05384f354e80171b22cf3ac88313b32b21e86effc1300f0610340839bda3ae3dfb9bc34d96ceac7a615d7cafe9531d710b26f3b8740563d684b989803b154cdd763254b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784be5c7cb62d9debd42ab3413e622737740f1b7bb826976a911d229a9a058650de20198233b30a76b5a6f968af29372f2973264d8994f14665ec14ca4b8ae3d500611f7807a2bcdf9329435aaef734f304a3104fd09a16b895c7f9b981a12859c444dbb270e33150ee8decf22459e03ed9a10984408f5d397e9a6c05bcfb920cb6f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74d1e21e0000000000000000000000000000000000000000000000000000000000", + "0x943fdf730700000034f3de73070000007ec4de73070000001a5fdf7307000000ac67cade9449bba5f4bb8fadcda6b206e4a13904a627b00431dcd733e1b7dc4a8144590d3ea9e72f3397082f62c5b49a706d88cd8f89fce354cfe92fa2bf8d6b778af6689441df6920b6e8905699d78170e39e123aef5499561caaeca22903faa1f0165d2317c78b2cce4a7dce1cb554cd329443afa1e7a369e0ad0b9331c1e537af776e7a8b2e5f81b4490dd2b746f0eaf4b072fa0c2f2b98f0e9648014af9355aee837e26f343382e25b12373909809c9f9f6b7b1f04d957773133c3f7a32be8f735666437ab805964d20dd977b2a892eed69f55a224a4936a741ba0448fb040460af64f038f9e109f90c8a43e049468bc9598ca7c9965145a579c67d4da77eff774db8c13b039e0930393ae26156510f6d035711adbec03a33e96cd1c766d6ce456c592136bb2202bb5f6eb68d90c1a7cf4ca948ec31eae4402bd5f35fdbca9ac6d4f13e80def3154a9299974c637247e61f1fdfa97a59eda5e085c05384f354e80171b22cf3ac88313b32b21e86effc1300f0610340839bda3ae3dfb9bc34d96ceac7a615d7cafe9531d710b26f3b8740563d684b989803b154cdd763254b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784be5c7cb62d9debd42ab3413e622737740f1b7bb826976a911d229a9a058650de20198233b30a76b5a6f968af29372f2973264d8994f14665ec14ca4b8ae3d500611f7807a2bcdf9329435aaef734f304a3104fd09a16b895c7f9b981a12859c444dbb270e33150ee8decf22459e03ed9a10984408f5d397e9a6c05bcfb920cb6f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74d1e21e0000000000000000000000000000000000000000000000000000000000", + "0xf6a9597307000000f6a95973070000007f8c5628080000002053b6ea0e0000009c1e95cfcdc2c1e95ef2aa388de59a5cbf6390302a483f2328ed4183b635b2ca6ed650f19d2cc6f14059c203ce0e9b7ce7166154b055ac48a633ca17d8ad9e86f7bf3901fef701be8c1f5c3256c67342bb25e8da16937d743bfa535a33ce7a8922e7c1f2fe9c2629c00bcaea4f8cb320e934f3e6a260836dd926270ed6d711b659505de684ffba3dee2b0b956662434a9afad18edc29eb6605117df2399875eef8fd5950e6f436e3dcd6ef382b2e39d967eec02351a2d71c59d5bf9bd40f368251c743cfa505eeddffc015148c6bd11bf323ad1349c93f8966c4e0e482bce0ca86c75724a7a0d6c44bc6dc0570f8b98ca049042f2e99e9f2c3e025a3f8c50d007f1e2da96f76720bb62fd1607fa9b35ee7c1ab7d894466bf22221c139c5e14f1e88840e970486a623b71a26a30a46ae720bb8d17718366aa8e686cc314645e1e7236eddced80f23a9fb8b6826f06f08339bc739282a8fad7ea1b7a06f1360739f875cbd735037405ab38187a9f1341254ce07e1d1fab652ab569c5239ae331284ae5a3c3d3cfc144a2dce140b4c9921f090d0b4383da38309c3432d9232eb57ab58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784be5c7cb62d9debd42ab3413e622737740f1b7bb826976a911d229a9a058650de20198233b30a76b5a6f968af29372f2973264d8994f14665ec14ca4b8ae3d500611f7807a2bcdf9329435aaef734f304a3104fd09a16b895c7f9b981a12859c444dbb270e33150ee8decf22459e03ed9a10984408f5d397e9a6c05bcfb920cb6f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74d1e21e0000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000000000001732617307000000c43a6173070000008343cb4079a092be998ce385dc721041e8f978ec91916cadfb83e63e1c929597193e1d6895e08ada8bd2be9f6eecef2a8b5f464c8ee7503569fee7c1660c324bc034cc80479e2cf76378a1b25a703a6cc842322ab25424ca78fbc70e4362f5a6c9c9a52714e812c3f453e034e94cd37350465000de96d692b7e00c60ad3b610ae4531fd9fbd11638d4ec994295093daa9b6503da7d66d544d0b5627ff4a7bd35a495873c542c2507617509cd62bce5e6bdf21b73226ca95f1233cad0353c216848d1d07f9e0e1f49093ae96101f7cb55b90cb86ea3340aff516ebcb3ea3360ed1c006c1478cf864b43ae986742c9ff4b66eb4f040dbf845a406e98813523ab7db234aa7196bd8ff15bd02fc845ac8691eeb26e8b456906486809ec7268e1ea6a21dd2c80f842a50829e3eb109dbdc2ce034d9fa7f3cbe6a699c10f2049738502c552521773c9d86f8a2b9399e9d3c5c0d26f6be05fca55938205c8bea84ed95011478ddd8d980bdf0b3f2438af57aa9612e88bfe9a64d8235c2352a52ac0df8c4ae5a3c3d3cfc144a2dce140b4c9921f090d0b4383da38309c3432d9232eb57ab58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784be5c7cb62d9debd42ab3413e622737740f1b7bb826976a911d229a9a058650de20198233b30a76b5a6f968af29372f2973264d8994f14665ec14ca4b8ae3d500611f7807a2bcdf9329435aaef734f304a3104fd09a16b895c7f9b981a12859c444dbb270e33150ee8decf22459e03ed9a10984408f5d397e9a6c05bcfb920cb6f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74d1e21e0000000000000000000000000000000000000000000000000000000000", + "0x7ada6d7307000000fc706d730700000002b56d73070000007e946d73070000006b6e79d15b64eb2f310de3338b5fac6a2fc4fb34913b1116631cc03da1a64287a9f669bc48454659aa6e9bdb22ca4b20c212cbd184cf5c82a0a574782232f82662200e563afc0c63a001b599279184bd07da1a3310dcdc1a0eb649ebac1c68f7f4557817b4fa350956dae5eda51bc561180f059fbf08c8b0f056b7c34a863b1b099c92efe9ed83fbaf6497c3b0946c9263bb56441127f1a161c7c866367809f785386ab135cf10910c3e29e89d0b2dfdf72687fa49b0026515b15caec3271be5bd02c22cf8ed248a66b7388c8ec9e23ae6bf71dec35e23c3aa03f95724f65d0062907f3a7ff35fc2b17b218a78bbfef0ca6a64b48521f40524895b1632447c64e9d1b24edd587a540e837461fb55824018daa7fa2e163827036bf4959ce41e3f043281ffb2f4f0f9c1cd7aeee48dce1edfd2ec6f6338b7f74cb66a00c7953c1afc28be85435b8c6e76f9ae4dce64c0820437209a5a250ff34148b84bcb194bf69eeb3b835c7789ac4725a49772049b4d726634b63cdd39b318c3e6582f92456fd143532bf8fec2d12b83ef6a5f7db0d95ee9a14bf7c3eb207c80863a33ddf706a78d55897055b8f1b27de4683fd0c1c71e23700a7a89c8ec2f6fd17cb76d55358e6f20532c117fbf60154047ded45e406224c476a29c2d7b70ede3ad1a3e935c20198233b30a76b5a6f968af29372f2973264d8994f14665ec14ca4b8ae3d500611f7807a2bcdf9329435aaef734f304a3104fd09a16b895c7f9b981a12859c444dbb270e33150ee8decf22459e03ed9a10984408f5d397e9a6c05bcfb920cb6f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74d1e21e0000000000000000000000000000000000000000000000000000000000", + "0x7ada6d7307000000fc706d730700000002b56d73070000007e946d73070000006b6e79d15b64eb2f310de3338b5fac6a2fc4fb34913b1116631cc03da1a64287a9f669bc48454659aa6e9bdb22ca4b20c212cbd184cf5c82a0a574782232f82662200e563afc0c63a001b599279184bd07da1a3310dcdc1a0eb649ebac1c68f7f4557817b4fa350956dae5eda51bc561180f059fbf08c8b0f056b7c34a863b1b099c92efe9ed83fbaf6497c3b0946c9263bb56441127f1a161c7c866367809f785386ab135cf10910c3e29e89d0b2dfdf72687fa49b0026515b15caec3271be5bd02c22cf8ed248a66b7388c8ec9e23ae6bf71dec35e23c3aa03f95724f65d0062907f3a7ff35fc2b17b218a78bbfef0ca6a64b48521f40524895b1632447c64e9d1b24edd587a540e837461fb55824018daa7fa2e163827036bf4959ce41e3f043281ffb2f4f0f9c1cd7aeee48dce1edfd2ec6f6338b7f74cb66a00c7953c1afc28be85435b8c6e76f9ae4dce64c0820437209a5a250ff34148b84bcb194bf69eeb3b835c7789ac4725a49772049b4d726634b63cdd39b318c3e6582f92456fd143532bf8fec2d12b83ef6a5f7db0d95ee9a14bf7c3eb207c80863a33ddf706a78d55897055b8f1b27de4683fd0c1c71e23700a7a89c8ec2f6fd17cb76d55358e6f20532c117fbf60154047ded45e406224c476a29c2d7b70ede3ad1a3e935c20198233b30a76b5a6f968af29372f2973264d8994f14665ec14ca4b8ae3d500611f7807a2bcdf9329435aaef734f304a3104fd09a16b895c7f9b981a12859c444dbb270e33150ee8decf22459e03ed9a10984408f5d397e9a6c05bcfb920cb6f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74d1e21e0000000000000000000000000000000000000000000000000000000000", + "0x71267a74070000008a9a7b7407000000c1c57b7407000000af2a7b74070000008c037d7aa696ae2eade6e4b07004ada31eb7acb2cba762609bfe5691b2ac33dd4329bd33222cf9e7ce649586c58fa5b5d94b4abbcf1528ea19e6464d0037f0661927229ac5d6d3a2e815d8c3fdd51c1222536e86d0f81bfa78f8f5659cafe554bad3c6aced598e020b6871446986c333f802563ac3ec1f1faaea1412018dedce4809d1fc06f5f2f775b89aa25baaf464c0b6b9f4799b46ff7111d447b5717a863f4872b2f9f1855e3fbaa9c8708e1b94a4c27a3d7ca10f637e7283caba8de9524877c563b4b5942c8a379eed5a0f2847f5c1a4eabd717320a07bd4998cd639602cfbbfaac62488c4dc2da886ee939ca3cdb11f52aa42ca31da27ea6c7019e740d49633d8626eee344bb3d71bac5555ee8c48d6a988209ebe4e312b74944f7b3f98d910fd70ad472ee127c69f62af03ce2085f11f542ca4fe2791b8517b8b3458934901ab09b7dac3afa2f2e8353f2021c369a54fcbf37d6ae0e837d61dcc14d727e83f115c7ccb39d8c65e461a1af6c87a88913219af9f6f2564bca32bfb68364d96ceac7a615d7cafe9531d710b26f3b8740563d684b989803b154cdd763254b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784be5c7cb62d9debd42ab3413e622737740f1b7bb826976a911d229a9a058650de20198233b30a76b5a6f968af29372f2973264d8994f14665ec14ca4b8ae3d500611f7807a2bcdf9329435aaef734f304a3104fd09a16b895c7f9b981a12859c444dbb270e33150ee8decf22459e03ed9a10984408f5d397e9a6c05bcfb920cb6f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74d1e21e0000000000000000000000000000000000000000000000000000000000", + "0xc8cc597307000000c8cc597307000000c8cc597307000000a9c3597307000000a571feafb73de4fe5b4c86bbc5e7279a0871e483bfc54464957752f6d4c6c0795850d4cc246d1878d5f59cc8230eff3fd17a730a9b6029c623c29d6245df80d38712ded49785b5217da360eaa0b5329487c52708ebc310cebee361f71186849e096231a77c5e829b95c1f8aa4ea02619cad38154c684087a6f9b6aac3be39a582b7c0ab87fa315a44f259d3f3dfb379b4e28f5de39a92c4b895e681af222d6da03be2b7604aec803e99385db1f655b42dac5151c95a66f250a1b77f7fa295d307244dd44e9e7a9bbe88b6962b97a2d71201379cda19e5a0e4540f37d18fd6a0e86c75724a7a0d6c44bc6dc0570f8b98ca049042f2e99e9f2c3e025a3f8c50d007f1e2da96f76720bb62fd1607fa9b35ee7c1ab7d894466bf22221c139c5e14f1e88840e970486a623b71a26a30a46ae720bb8d17718366aa8e686cc314645e1e7236eddced80f23a9fb8b6826f06f08339bc739282a8fad7ea1b7a06f1360739f875cbd735037405ab38187a9f1341254ce07e1d1fab652ab569c5239ae331284ae5a3c3d3cfc144a2dce140b4c9921f090d0b4383da38309c3432d9232eb57ab58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784be5c7cb62d9debd42ab3413e622737740f1b7bb826976a911d229a9a058650de20198233b30a76b5a6f968af29372f2973264d8994f14665ec14ca4b8ae3d500611f7807a2bcdf9329435aaef734f304a3104fd09a16b895c7f9b981a12859c444dbb270e33150ee8decf22459e03ed9a10984408f5d397e9a6c05bcfb920cb6f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74d1e21e0000000000000000000000000000000000000000000000000000000000", + "0x5bb671730700000037ad71730700000063fb71730700000084c7717307000000a9ea4163e915a06e171ed40654ce2268bd6310c70205f101e86fa3ac783f077ac71f22a62b110cde1e338f1a09c84e31edb61ca8b9c3d0b438d66200bca80efd0c4e0bb6126b6feae8603efedb01ac3dabd00e92650321aec30f2972ae4ac7f4d6cd1e7e86d8551505ddd1549593fadc7efdd6021a6f52100a07581dbef35e4adc653e06bcb20ed5eb8790bf0d4e5239be2ff14b183f47729c8fe4ae1a852125a71da2164fedf6e7076fc9784c8b2dd85102dc14b8dbc8f6b460282c776976536ce3acd665948fa7cf40848f062f4c2a6dc99487b6de5cb6ccddf3688ecdc932faec52ed70766f42ea027dbae43b81136748cbca5c92be2a166cae736e3de975fbc875c25b82753c694238c000f3fc755eed73a434becceddf539aebbc6528411437aa951c6e3226f213264ed531601550ac6351dc24202c7f2cb568c5ea1fce5b8161b91affef91eccc443dcd7853029392dd51beebfbca5a49f66d9a76313920d6c15a9367fdc8f1ac43d1d2d7e671b97f876b4bdbd7685cbb2d4d333a3d8ad143532bf8fec2d12b83ef6a5f7db0d95ee9a14bf7c3eb207c80863a33ddf706a78d55897055b8f1b27de4683fd0c1c71e23700a7a89c8ec2f6fd17cb76d55358e6f20532c117fbf60154047ded45e406224c476a29c2d7b70ede3ad1a3e935c20198233b30a76b5a6f968af29372f2973264d8994f14665ec14ca4b8ae3d500611f7807a2bcdf9329435aaef734f304a3104fd09a16b895c7f9b981a12859c444dbb270e33150ee8decf22459e03ed9a10984408f5d397e9a6c05bcfb920cb6f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74d1e21e0000000000000000000000000000000000000000000000000000000000", + "0x5bb671730700000037ad71730700000063fb71730700000084c7717307000000a9ea4163e915a06e171ed40654ce2268bd6310c70205f101e86fa3ac783f077ac71f22a62b110cde1e338f1a09c84e31edb61ca8b9c3d0b438d66200bca80efd0c4e0bb6126b6feae8603efedb01ac3dabd00e92650321aec30f2972ae4ac7f4d6cd1e7e86d8551505ddd1549593fadc7efdd6021a6f52100a07581dbef35e4adc653e06bcb20ed5eb8790bf0d4e5239be2ff14b183f47729c8fe4ae1a852125a71da2164fedf6e7076fc9784c8b2dd85102dc14b8dbc8f6b460282c776976536ce3acd665948fa7cf40848f062f4c2a6dc99487b6de5cb6ccddf3688ecdc932faec52ed70766f42ea027dbae43b81136748cbca5c92be2a166cae736e3de975fbc875c25b82753c694238c000f3fc755eed73a434becceddf539aebbc6528411437aa951c6e3226f213264ed531601550ac6351dc24202c7f2cb568c5ea1fce5b8161b91affef91eccc443dcd7853029392dd51beebfbca5a49f66d9a76313920d6c15a9367fdc8f1ac43d1d2d7e671b97f876b4bdbd7685cbb2d4d333a3d8ad143532bf8fec2d12b83ef6a5f7db0d95ee9a14bf7c3eb207c80863a33ddf706a78d55897055b8f1b27de4683fd0c1c71e23700a7a89c8ec2f6fd17cb76d55358e6f20532c117fbf60154047ded45e406224c476a29c2d7b70ede3ad1a3e935c20198233b30a76b5a6f968af29372f2973264d8994f14665ec14ca4b8ae3d500611f7807a2bcdf9329435aaef734f304a3104fd09a16b895c7f9b981a12859c444dbb270e33150ee8decf22459e03ed9a10984408f5d397e9a6c05bcfb920cb6f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74d1e21e0000000000000000000000000000000000000000000000000000000000", + "0x28fb7173070000004d71717307000000beea717307000000bda3717307000000b3d27fcd1a7d712205c82ba7003262f95751cb462d30944a28a13de71dea0fd91844498aa66fa7db39fa523363d72549df83ec868fd33f701d1ff294f283cb5924d0dc07d425bd3ee3d31f24d96930566a0f2a602f6b0ac1d26bf7637ec60f61a127138d9d6c16f9bd8c4bd98f92b2589df77f1a46a2096f6a9525d0cf2693898f195ba5a950cff1a2d6d854ce685378b0f23e7bc61a7ef03aa2d3788f6c5cafb85048b3c5e1499ad5959a18cbd668ad4c86f4714da19afe7508f923a233bd8d9c01b630b7a688d7a08bf88a2a6adc5039510120bc1e0c0835755bd17db366e1bf89edcee147cb79e8a89235c03ffe60644c800fb0e91557605d241c3dbfea0dd6b8c7f1c6360eb501dceeee00d23d8746897cf005377469da61ac2da9a330921437aa951c6e3226f213264ed531601550ac6351dc24202c7f2cb568c5ea1fce5b8161b91affef91eccc443dcd7853029392dd51beebfbca5a49f66d9a76313920d6c15a9367fdc8f1ac43d1d2d7e671b97f876b4bdbd7685cbb2d4d333a3d8ad143532bf8fec2d12b83ef6a5f7db0d95ee9a14bf7c3eb207c80863a33ddf706a78d55897055b8f1b27de4683fd0c1c71e23700a7a89c8ec2f6fd17cb76d55358e6f20532c117fbf60154047ded45e406224c476a29c2d7b70ede3ad1a3e935c20198233b30a76b5a6f968af29372f2973264d8994f14665ec14ca4b8ae3d500611f7807a2bcdf9329435aaef734f304a3104fd09a16b895c7f9b981a12859c444dbb270e33150ee8decf22459e03ed9a10984408f5d397e9a6c05bcfb920cb6f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74d1e21e0000000000000000000000000000000000000000000000000000000000", + "0x93171a74070000008d2f1a74070000008d731a7407000000685e1a7407000000f70f7d67370c628b077649abaa1c2fa962fe7d09cde829dabcb777f4f091f17570d1a7d4f2565e4987364dc0910cfab724756af66a4ca6329173cf2a4fece82799ebc2741c0b5698993034c5920bc1a3469e4e3090839502d2d70d66958e54bf232d6ecc573d8148ca128e3b21aae08d1fa6a9ae4c811a2326206537ae482972d352214b20d42f1823cfe08cbfdc82e8df84818eb67435960438983eff044877fb773260c4c58f6f38683fa58b552581bfc6468bfb0ccf97cd4e89425f46b03224ed257863379b6f8d5f8691c7b9cab7509ace6c36ea6b8f28da47a775a05d5ce66c3a8b2b205b511aa62e7cb5b1041b1dfa73445ad6bbf9bf89d645dcab7c390afb37b14f3184f6ffb7af0a1c7c6de49635a3dd94a080f94002a3a5cf700abf159c0ea00c873ac1b61269b17378453cc787e4bb96afe4455a81ed0de6ef830a934901ab09b7dac3afa2f2e8353f2021c369a54fcbf37d6ae0e837d61dcc14d727e83f115c7ccb39d8c65e461a1af6c87a88913219af9f6f2564bca32bfb68364d96ceac7a615d7cafe9531d710b26f3b8740563d684b989803b154cdd763254b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784be5c7cb62d9debd42ab3413e622737740f1b7bb826976a911d229a9a058650de20198233b30a76b5a6f968af29372f2973264d8994f14665ec14ca4b8ae3d500611f7807a2bcdf9329435aaef734f304a3104fd09a16b895c7f9b981a12859c444dbb270e33150ee8decf22459e03ed9a10984408f5d397e9a6c05bcfb920cb6f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74d1e21e0000000000000000000000000000000000000000000000000000000000", + "0x83821b740700000093e01a740700000076f2ec760700000045d81b74070000002d12bcdbfa594786b15235a84e441607d65a4a2ed1e8cc66d8b851d2b13ce53fe1aafe7824d7cbbb6b72b4b7df4ea1e0066ac0158195845ac1322aac20678061b11ce1142d52855b3e99a0aed9d8a0a1ea0e757eee234501c37a7cc62c1eacfe54c397be3138f97ec298800c5ce981a458dfd6699646f351c9c4a480babe7500d2a00e1e5aa08fe020cf80ac14df0ed7b8431ee6883bc9956c345691da1d05887929ead45c2335c3d9351004664ad7781dbd2bf30fd0da7a47ae1eba0471a29b24ed257863379b6f8d5f8691c7b9cab7509ace6c36ea6b8f28da47a775a05d5ce66c3a8b2b205b511aa62e7cb5b1041b1dfa73445ad6bbf9bf89d645dcab7c390afb37b14f3184f6ffb7af0a1c7c6de49635a3dd94a080f94002a3a5cf700abf159c0ea00c873ac1b61269b17378453cc787e4bb96afe4455a81ed0de6ef830a934901ab09b7dac3afa2f2e8353f2021c369a54fcbf37d6ae0e837d61dcc14d727e83f115c7ccb39d8c65e461a1af6c87a88913219af9f6f2564bca32bfb68364d96ceac7a615d7cafe9531d710b26f3b8740563d684b989803b154cdd763254b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784be5c7cb62d9debd42ab3413e622737740f1b7bb826976a911d229a9a058650de20198233b30a76b5a6f968af29372f2973264d8994f14665ec14ca4b8ae3d500611f7807a2bcdf9329435aaef734f304a3104fd09a16b895c7f9b981a12859c444dbb270e33150ee8decf22459e03ed9a10984408f5d397e9a6c05bcfb920cb6f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74d1e21e0000000000000000000000000000000000000000000000000000000000", + "0x9a3f1c74070000005c591b740700000026161c740700000096711b7407000000f0170a37267081cfd8c2865e705af644f3d6ee2333f2a53ac4f80d20f64fef3aa1f0f6a84a4f5e13f0b5720c2c5d81b8b962616f37d1e5dd263b3676d92eefca1e11092e26497f8889dc06686e05c0b5d28dc252925610d7ea01f61d7c6397f18b25bb74bcf055cae8ed4dbe0ee8df8213a297726f7db8a95a2a2948eb3e77b8d2a00e1e5aa08fe020cf80ac14df0ed7b8431ee6883bc9956c345691da1d05887929ead45c2335c3d9351004664ad7781dbd2bf30fd0da7a47ae1eba0471a29b24ed257863379b6f8d5f8691c7b9cab7509ace6c36ea6b8f28da47a775a05d5ce66c3a8b2b205b511aa62e7cb5b1041b1dfa73445ad6bbf9bf89d645dcab7c390afb37b14f3184f6ffb7af0a1c7c6de49635a3dd94a080f94002a3a5cf700abf159c0ea00c873ac1b61269b17378453cc787e4bb96afe4455a81ed0de6ef830a934901ab09b7dac3afa2f2e8353f2021c369a54fcbf37d6ae0e837d61dcc14d727e83f115c7ccb39d8c65e461a1af6c87a88913219af9f6f2564bca32bfb68364d96ceac7a615d7cafe9531d710b26f3b8740563d684b989803b154cdd763254b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784be5c7cb62d9debd42ab3413e622737740f1b7bb826976a911d229a9a058650de20198233b30a76b5a6f968af29372f2973264d8994f14665ec14ca4b8ae3d500611f7807a2bcdf9329435aaef734f304a3104fd09a16b895c7f9b981a12859c444dbb270e33150ee8decf22459e03ed9a10984408f5d397e9a6c05bcfb920cb6f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74d1e21e0000000000000000000000000000000000000000000000000000000000", + "0x93171a74070000008d2f1a74070000008d731a7407000000685e1a7407000000f70f7d67370c628b077649abaa1c2fa962fe7d09cde829dabcb777f4f091f17570d1a7d4f2565e4987364dc0910cfab724756af66a4ca6329173cf2a4fece82799ebc2741c0b5698993034c5920bc1a3469e4e3090839502d2d70d66958e54bf232d6ecc573d8148ca128e3b21aae08d1fa6a9ae4c811a2326206537ae482972d352214b20d42f1823cfe08cbfdc82e8df84818eb67435960438983eff044877fb773260c4c58f6f38683fa58b552581bfc6468bfb0ccf97cd4e89425f46b03224ed257863379b6f8d5f8691c7b9cab7509ace6c36ea6b8f28da47a775a05d5ce66c3a8b2b205b511aa62e7cb5b1041b1dfa73445ad6bbf9bf89d645dcab7c390afb37b14f3184f6ffb7af0a1c7c6de49635a3dd94a080f94002a3a5cf700abf159c0ea00c873ac1b61269b17378453cc787e4bb96afe4455a81ed0de6ef830a934901ab09b7dac3afa2f2e8353f2021c369a54fcbf37d6ae0e837d61dcc14d727e83f115c7ccb39d8c65e461a1af6c87a88913219af9f6f2564bca32bfb68364d96ceac7a615d7cafe9531d710b26f3b8740563d684b989803b154cdd763254b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784be5c7cb62d9debd42ab3413e622737740f1b7bb826976a911d229a9a058650de20198233b30a76b5a6f968af29372f2973264d8994f14665ec14ca4b8ae3d500611f7807a2bcdf9329435aaef734f304a3104fd09a16b895c7f9b981a12859c444dbb270e33150ee8decf22459e03ed9a10984408f5d397e9a6c05bcfb920cb6f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74d1e21e0000000000000000000000000000000000000000000000000000000000", + "0x09211b740700000077381b740700000079f31a7407000000e5081b740700000077e793ae8a26650cdf06aa4e5203bfde96f0cd2678a32f6394fbb099fc0bd6e070d1a7d4f2565e4987364dc0910cfab724756af66a4ca6329173cf2a4fece82799ebc2741c0b5698993034c5920bc1a3469e4e3090839502d2d70d66958e54bf232d6ecc573d8148ca128e3b21aae08d1fa6a9ae4c811a2326206537ae482972d352214b20d42f1823cfe08cbfdc82e8df84818eb67435960438983eff044877fb773260c4c58f6f38683fa58b552581bfc6468bfb0ccf97cd4e89425f46b03224ed257863379b6f8d5f8691c7b9cab7509ace6c36ea6b8f28da47a775a05d5ce66c3a8b2b205b511aa62e7cb5b1041b1dfa73445ad6bbf9bf89d645dcab7c390afb37b14f3184f6ffb7af0a1c7c6de49635a3dd94a080f94002a3a5cf700abf159c0ea00c873ac1b61269b17378453cc787e4bb96afe4455a81ed0de6ef830a934901ab09b7dac3afa2f2e8353f2021c369a54fcbf37d6ae0e837d61dcc14d727e83f115c7ccb39d8c65e461a1af6c87a88913219af9f6f2564bca32bfb68364d96ceac7a615d7cafe9531d710b26f3b8740563d684b989803b154cdd763254b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784be5c7cb62d9debd42ab3413e622737740f1b7bb826976a911d229a9a058650de20198233b30a76b5a6f968af29372f2973264d8994f14665ec14ca4b8ae3d500611f7807a2bcdf9329435aaef734f304a3104fd09a16b895c7f9b981a12859c444dbb270e33150ee8decf22459e03ed9a10984408f5d397e9a6c05bcfb920cb6f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74d1e21e0000000000000000000000000000000000000000000000000000000000", + "0x83821b740700000093e01a740700000076f2ec760700000045d81b74070000002d12bcdbfa594786b15235a84e441607d65a4a2ed1e8cc66d8b851d2b13ce53fe1aafe7824d7cbbb6b72b4b7df4ea1e0066ac0158195845ac1322aac20678061b11ce1142d52855b3e99a0aed9d8a0a1ea0e757eee234501c37a7cc62c1eacfe54c397be3138f97ec298800c5ce981a458dfd6699646f351c9c4a480babe7500d2a00e1e5aa08fe020cf80ac14df0ed7b8431ee6883bc9956c345691da1d05887929ead45c2335c3d9351004664ad7781dbd2bf30fd0da7a47ae1eba0471a29b24ed257863379b6f8d5f8691c7b9cab7509ace6c36ea6b8f28da47a775a05d5ce66c3a8b2b205b511aa62e7cb5b1041b1dfa73445ad6bbf9bf89d645dcab7c390afb37b14f3184f6ffb7af0a1c7c6de49635a3dd94a080f94002a3a5cf700abf159c0ea00c873ac1b61269b17378453cc787e4bb96afe4455a81ed0de6ef830a934901ab09b7dac3afa2f2e8353f2021c369a54fcbf37d6ae0e837d61dcc14d727e83f115c7ccb39d8c65e461a1af6c87a88913219af9f6f2564bca32bfb68364d96ceac7a615d7cafe9531d710b26f3b8740563d684b989803b154cdd763254b58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784be5c7cb62d9debd42ab3413e622737740f1b7bb826976a911d229a9a058650de20198233b30a76b5a6f968af29372f2973264d8994f14665ec14ca4b8ae3d500611f7807a2bcdf9329435aaef734f304a3104fd09a16b895c7f9b981a12859c444dbb270e33150ee8decf22459e03ed9a10984408f5d397e9a6c05bcfb920cb6f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74d1e21e0000000000000000000000000000000000000000000000000000000000" + ] + }, + "validatorBalances": [ + "137.553456842", + "64.06735952", + "502.570074631", + "0.0", + "32.008954871", + "64.017862673", + "32.033632139", + "32.11429415", + "1366.785982692", + "1366.607182539", + "32.002569699", + "0.0", + "0.0", + "0.0", + "0.0", + "32.012706287", + "32.012683092", + "32.012797225", + "32.012668528", + "32.012689641", + "32.012759287" + ], + "command": "npx hardhat verifyBalances --indexes 1930685,1998611,1927585,1897126,2018225,2018224,1998612,1970362,1922881,1922882,2009356,1998140,1906722,1906721,1906639,2015001,2014925,2014907,2015000,2014998,2014926 --test true --slot 12204316 --deposits 1,2,3,4,5,6,7,8 --network mainnet" + } + ] +} diff --git a/contracts/test/strategies/compoundingSSVStaking.js b/contracts/test/strategies/compoundingSSVStaking.js new file mode 100644 index 0000000000..0069fa48c6 --- /dev/null +++ b/contracts/test/strategies/compoundingSSVStaking.js @@ -0,0 +1,3322 @@ +const { expect } = require("chai"); +const { network } = require("hardhat"); +const { BigNumber, Wallet } = require("ethers"); +const { parseEther, parseUnits, keccak256, hexZeroPad, solidityPack } = + require("ethers").utils; +const { + getStorageAt, + setBalance, + setStorageAt, +} = require("@nomicfoundation/hardhat-network-helpers"); +const { isCI } = require("../helpers"); +const { shouldBehaveLikeGovernable } = require("../behaviour/governable"); +const { shouldBehaveLikeStrategy } = require("../behaviour/strategy"); +const { MAX_UINT256, ZERO_BYTES32 } = require("../../utils/constants"); +const { impersonateAndFund } = require("../../utils/signers"); +const { ethUnits, advanceTime } = require("../helpers"); +const { setERC20TokenBalance } = require("../_fund"); +const { zero } = require("../../utils/addresses"); +const { calcDepositRoot } = require("../../tasks/beaconTesting"); +const { + hashPubKey, + calcSlot, + calcEpoch, + calcBlockTimestamp, +} = require("../../utils/beacon"); +const { randomBytes } = require("crypto"); +const { + testValidators, + testBalancesProofs, +} = require("./compoundingSSVStaking-validatorsData.json"); +const log = require("../../utils/logger")( + "test:unit:strategy:compoundingSSVStaking" +); + +const { + createFixtureLoader, + compoundingStakingSSVStrategyFixture, + compoundingStakingSSVStrategyMerkleProofsMockedFixture, +} = require("./../_fixture"); + +const loadFixture = createFixtureLoader(compoundingStakingSSVStrategyFixture); +const loadFixtureMockedProofs = createFixtureLoader( + compoundingStakingSSVStrategyMerkleProofsMockedFixture +); + +const emptyCluster = [ + 0, // validatorCount + 0, // networkFeeIndex + 0, // index + true, // active + 0, // balance +]; +const emptyOneBalanceProofs = { + balancesContainerRoot: ZERO_BYTES32, + balancesContainerProof: "0x", + validatorBalanceLeaves: [ZERO_BYTES32], + validatorBalanceProofs: ["0x"], +}; +const emptyPendingDepositProofs = { + pendingDepositContainerRoot: ZERO_BYTES32, + pendingDepositContainerProof: "0x", + pendingDepositIndexes: [], + pendingDepositProofs: [], +}; +const emptyOnePendingDepositProofs = { + pendingDepositContainerRoot: ZERO_BYTES32, + pendingDepositContainerProof: "0x", + pendingDepositIndexes: [0], + pendingDepositProofs: ["0x"], +}; +const emptyTwoPendingDepositProofs = { + pendingDepositContainerRoot: ZERO_BYTES32, + pendingDepositContainerProof: "0x", + pendingDepositIndexes: [1, 2], + pendingDepositProofs: ["0x", "0x"], +}; +const emptyPendingDepositsProof = { + beaconRoot: + "0x936a7ac91224df0522e8fc70521b604b025d37504a432ca9ea842a018ba7546c", + proof: + "0x0000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4bdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30d88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa187eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1ffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220b7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467650000000000000000000000000000000000000000000000000000000000000000049c9edd0970b512318fe4a7d9ff12b2b1402164d872e40948fc7d9042ae6fa615433386cfe4fc95585fb6eeb51df3a6f619db3b3955884f7e5a2c4600ed2d47dae6d9c51743d5d9263bf2bd09c1db3bd529965d7ee7857643c919c6b696004ec78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123ceb818784738117ef339dce506dc4996cecd38ef7ed6021eb0b4382bf9c3e81b3cce9d380b4759b9c6277871c289b42feed13f46b29b78c3be52296492ef902aecd1fa730ef94dfb6efa48a62de660970894608c2e16cce90ef2b3880778f8e383e09791016e57e609c54db8d85e1e0607a528e23b6c34dc738f899f2c284d765", +}; + +const getWithdrawalCredentials = (type, address) => { + return type + "0000000000000000000000" + address.slice(2); +}; + +const ETHInGwei = BigNumber.from("1000000000"); // 1 ETH in Gwei +const GweiInWei = BigNumber.from("1000000000"); // 1 Gwei in Wei + +describe("Unit test: Compounding SSV Staking Strategy", function () { + this.timeout(0); + + // Retry up to 3 times on CI + this.retries(isCI ? 3 : 0); + let sGov; + let sVault; + let fixture; + + beforeEach(async () => { + fixture = await loadFixture(); + const { compoundingStakingSSVStrategy, josh, weth } = fixture; + sGov = await impersonateAndFund( + await compoundingStakingSSVStrategy.governor() + ); + sVault = await impersonateAndFund( + await compoundingStakingSSVStrategy.vaultAddress() + ); + await weth + .connect(josh) + .approve(compoundingStakingSSVStrategy.address, MAX_UINT256); + }); + + shouldBehaveLikeGovernable(() => ({ + ...fixture, + strategy: fixture.compoundingStakingSSVStrategy, + })); + + shouldBehaveLikeStrategy(() => ({ + ...fixture, + strategy: fixture.compoundingStakingSSVStrategy, + assets: [fixture.weth], + valueAssets: [], + harvester: fixture.oethHarvester, + vault: fixture.oethVault, + })); + + describe("Initial setup", () => { + it("Should anyone to send ETH", async () => { + const { compoundingStakingSSVStrategy, strategist } = fixture; + + const signer = compoundingStakingSSVStrategy.provider.getSigner( + strategist.address + ); + const tx = { + to: compoundingStakingSSVStrategy.address, + value: parseEther("2"), + }; + + await expect(signer.sendTransaction(tx)).to.not.be.reverted; + }); + + it("SSV network should have allowance to spend SSV tokens of the strategy", async () => { + const { compoundingStakingSSVStrategy, ssv } = fixture; + + const ssvNetworkAddress = + await compoundingStakingSSVStrategy.SSV_NETWORK(); + await expect( + await ssv.allowance( + compoundingStakingSSVStrategy.address, + ssvNetworkAddress + ) + ).to.equal(MAX_UINT256); + }); + }); + + describe("Configuring the strategy", () => { + it("Governor should be able to change the registrator address", async () => { + const { compoundingStakingSSVStrategy, strategist } = fixture; + + const tx = await compoundingStakingSSVStrategy + .connect(sGov) + .setRegistrator(strategist.address); + + await expect(tx) + .to.emit(compoundingStakingSSVStrategy, "RegistratorChanged") + .withArgs(strategist.address); + }); + + it("Non governor should not be able to change the registrator address", async () => { + const { compoundingStakingSSVStrategy, strategist } = fixture; + + await expect( + compoundingStakingSSVStrategy + .connect(strategist) + .setRegistrator(strategist.address) + ).to.be.revertedWith("Caller is not the Governor"); + }); + + it("Should support WETH as the only asset", async () => { + const { compoundingStakingSSVStrategy, weth } = fixture; + + const assets = await compoundingStakingSSVStrategy.supportsAsset( + weth.address + ); + expect(assets).to.equal(true); + }); + it("Should not collect rewards", async () => { + const { compoundingStakingSSVStrategy, governor } = fixture; + + await compoundingStakingSSVStrategy + .connect(governor) + .setHarvesterAddress(governor.address); + + const collectRewards = compoundingStakingSSVStrategy + .connect(governor) + .collectRewardTokens(); + await expect(collectRewards).to.revertedWith("Unsupported function"); + }); + it("Should not set platform token", async () => { + const { compoundingStakingSSVStrategy, governor, weth } = fixture; + + const tx = compoundingStakingSSVStrategy + .connect(governor) + .setPTokenAddress(weth.address, weth.address); + + await expect(tx).to.revertedWith("Unsupported function"); + }); + it("Should not remove platform token", async () => { + const { compoundingStakingSSVStrategy, governor } = fixture; + + const tx = compoundingStakingSSVStrategy + .connect(governor) + .removePToken(0); + + await expect(tx).to.revertedWith("Unsupported function"); + }); + it("Non governor should not be able to withdraw SSV", async () => { + const { compoundingStakingSSVStrategy, strategist, josh } = fixture; + + const signers = [strategist, josh]; + for (const signer of signers) { + await expect( + compoundingStakingSSVStrategy + .connect(signer) + .withdrawSSV([1, 2, 3, 4], parseUnits("1", 18), emptyCluster) + ).to.be.revertedWith("Caller is not the Governor"); + } + }); + it("Non governor should not be able to reset the first deposit flag", async () => { + const { compoundingStakingSSVStrategy, strategist, josh } = fixture; + + const signers = [strategist, josh]; + for (const signer of signers) { + await expect( + compoundingStakingSSVStrategy.connect(signer).resetFirstDeposit() + ).to.be.revertedWith("Caller is not the Governor"); + } + }); + it("Should revert reset of first deposit if there is no first deposit", async () => { + const { compoundingStakingSSVStrategy, governor } = fixture; + + await expect( + compoundingStakingSSVStrategy.connect(governor).resetFirstDeposit() + ).to.be.revertedWith("No first deposit"); + }); + it("Registrator or governor should be the only ones to pause the strategy", async () => { + const { + compoundingStakingSSVStrategy, + governor, + validatorRegistrator, + josh, + } = fixture; + + await compoundingStakingSSVStrategy.connect(governor).pause(); + await compoundingStakingSSVStrategy.connect(governor).unPause(); + await compoundingStakingSSVStrategy.connect(validatorRegistrator).pause(); + await compoundingStakingSSVStrategy.connect(governor).unPause(); + + await expect( + compoundingStakingSSVStrategy.connect(josh).pause() + ).to.be.revertedWith("Not Registrator or Governor"); + }); + }); + + const processValidator = async ( + testValidator, + state = "VERIFIED_DEPOSIT" + ) => { + const { beaconRoots, compoundingStakingSSVStrategy, validatorRegistrator } = + fixture; + + const depositAmount = 1; + + // Register a new validator with the SSV Network + const regTx = await compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .registerSsvValidator( + testValidator.publicKey, + testValidator.operatorIds, + testValidator.sharesData, + 0, // SSV amount + emptyCluster + ); + + if (state === "REGISTERED") return regTx; + + // Stake ETH to the new validator + + await depositToStrategy(depositAmount); + + const depositDataRoot = await calcDepositRoot( + compoundingStakingSSVStrategy.address, + "0x02", + testValidator.publicKey, + testValidator.signature, + depositAmount + ); + + const depositGwei = BigNumber.from(depositAmount).mul(ETHInGwei); // Convert ETH to Gwei + + const stakeTx = await compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .stakeEth( + { + pubkey: testValidator.publicKey, + signature: testValidator.signature, + depositDataRoot, + }, + depositGwei + ); + const { pendingDepositRoot, depositSlot } = await getLastDeposit( + compoundingStakingSSVStrategy + ); + + if (state === "STAKED") return stakeTx; + + // Verify the validator + + // Set BeaconRoot for timestamp + await beaconRoots["setBeaconRoot(uint256,bytes32)"]( + testValidator.validatorProof.nextBlockTimestamp, + testValidator.validatorProof.root + ); + + // Verify the validator + const verifiedValidatorTx = + await compoundingStakingSSVStrategy.verifyValidator( + testValidator.validatorProof.nextBlockTimestamp, + testValidator.index, + testValidator.publicKeyHash, + getWithdrawalCredentials("0x02", compoundingStakingSSVStrategy.address), + testValidator.validatorProof.bytes + ); + + if (state === "VERIFIED_VALIDATOR") return verifiedValidatorTx; + + // Set parent beacon root for the block after the verification slots + const depositProcessedSlot = depositSlot + 10000n; + + await beaconRoots["setBeaconRoot(uint256,bytes32)"]( + calcBlockTimestamp(depositProcessedSlot) + 12n, + testValidator.depositProof.processedBeaconBlockRoot + ); + + const verifiedDepositTx = await compoundingStakingSSVStrategy.verifyDeposit( + pendingDepositRoot, + depositProcessedSlot, + testValidator.depositProof.firstPendingDeposit, + testValidator.depositProof.strategyValidator + ); + + if (state === "VERIFIED_DEPOSIT") return verifiedDepositTx; + + throw Error(`Invalid state: ${state}`); + }; + + const topUpValidator = async ( + testValidator, + depositAmount, + state = "VERIFIED_DEPOSIT" + ) => { + const { compoundingStakingSSVStrategy, validatorRegistrator } = fixture; + + // Stake ETH to the new validator + + await depositToStrategy(depositAmount); + + const depositDataRoot = await calcDepositRoot( + compoundingStakingSSVStrategy.address, + "0x02", + testValidator.publicKey, + testValidator.signature, + depositAmount + ); + + const depositGwei = parseUnits(depositAmount.toString(), 9); + + const stakeTx = await compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .stakeEth( + { + pubkey: testValidator.publicKey, + signature: testValidator.signature, + depositDataRoot, + }, + depositGwei + ); + + if (state === "STAKED") return stakeTx; + + const verifiedDepositTx = await verifyDeposit(testValidator); + + if (state === "VERIFIED_DEPOSIT") return verifiedDepositTx; + + throw Error(`Invalid state: ${state}`); + }; + + const verifyDeposit = async (testValidator) => { + const { beaconRoots, compoundingStakingSSVStrategy } = fixture; + + const { pendingDepositRoot, depositSlot } = await getLastDeposit( + compoundingStakingSSVStrategy + ); + + // Set parent beacon root for the block after the verification slots + const depositProcessedSlot = depositSlot + 10000n; + // Put the slot the validator of the first pending deposit was created one epoch later + const firstDepositValidatorCreatedSlot = depositProcessedSlot + 32n; + + await beaconRoots["setBeaconRoot(uint256,bytes32)"]( + calcBlockTimestamp(depositProcessedSlot) + 12n, + testValidator.depositProof.processedBeaconBlockRoot + ); + await beaconRoots["setBeaconRoot(uint256,bytes32)"]( + calcBlockTimestamp(firstDepositValidatorCreatedSlot) + 12n, + testValidator.depositProof.validatorBeaconBlockRoot + ); + + const verifiedDepositTx = await compoundingStakingSSVStrategy.verifyDeposit( + pendingDepositRoot, + depositProcessedSlot, + testValidator.depositProof.firstPendingDeposit, + testValidator.depositProof.strategyValidator + ); + + return verifiedDepositTx; + }; + + // call right after depositing to the strategy + const getLastDeposit = async (compoundingStakingSSVStrategy) => { + const lastBlock = await ethers.provider.getBlock("latest"); + // roughly the deposit slot + const depositSlot = calcSlot(lastBlock.timestamp); + + const pendingDepositRoot = await compoundingStakingSSVStrategy.depositList( + (await compoundingStakingSSVStrategy.depositListLength()).sub(1) + ); + + return { + depositSlot, + pendingDepositRoot, + }; + }; + + const snapBalances = async (beaconBlockRoot) => { + const { compoundingStakingSSVStrategy, beaconRoots, validatorRegistrator } = + fixture; + + if (!beaconBlockRoot) { + beaconBlockRoot = "0x" + randomBytes(32).toString("hex"); + log(`Generated random beacon block root: ${beaconBlockRoot}`); + } + + // Disable auto-mining dynamically + await network.provider.send("evm_setAutomine", [false]); + + await beaconRoots["setBeaconRoot(bytes32)"](beaconBlockRoot); + + await compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .snapBalances(); + + // Mine both txs + await ethers.provider.send("evm_mine", []); + // Enable auto-mining + await network.provider.send("evm_setAutomine", [true]); + + const lastBlock = await ethers.provider.getBlock("latest"); + + return { beaconBlockRoot, timestamp: lastBlock.timestamp }; + }; + + const assertBalances = async ({ + wethAmount, + ethAmount, + balancesProof, + pendingDepositAmount, + activeValidators, + hackDeposits = true, + }) => { + const { + compoundingStakingSSVStrategy, + compoundingStakingStrategyView, + weth, + } = fixture; + + if (wethAmount > 0) { + // Set some WETH in the strategy + await setERC20TokenBalance( + compoundingStakingSSVStrategy.address, + weth, + parseEther(wethAmount.toString()) + ); + } + + if (ethAmount > 0) { + // Set some execution rewards + await setBalance( + compoundingStakingSSVStrategy.address, + parseEther(ethAmount.toString()) + ); + } + + await snapBalances(balancesProof.blockRoot); + + const filteredBalanceLeaves = + balancesProof.balanceProofs.validatorBalanceLeaves.filter((_, index) => + activeValidators.includes(index) + ); + const filteredBalanceProofs = + balancesProof.balanceProofs.validatorBalanceProofs.filter((_, index) => + activeValidators.includes(index) + ); + const filteredBalances = balancesProof.validatorBalances.filter( + (_, index) => activeValidators.includes(index) + ); + + const deposits = await compoundingStakingStrategyView.getPendingDeposits(); + + const pendingDepositIndexes = + balancesProof.pendingDepositProofsData.pendingDepositIndexes.slice( + 0, + deposits.length + ); + const pendingDepositProofs = + balancesProof.pendingDepositProofsData.pendingDepositProofs.slice( + 0, + deposits.length + ); + + if (hackDeposits) { + // hack the pendingDepositRoots in the strategy's depositList array + for (let i = 0; i < deposits.length; i++) { + await hackDepositList( + i, + balancesProof.pendingDepositProofsData.pendingDepositRoots[i], + deposits[i] + ); + } + } + + const balanceProofsData = { + ...balancesProof.balanceProofs, + validatorBalanceLeaves: filteredBalanceLeaves, + validatorBalanceProofs: filteredBalanceProofs, + }; + const pendingDepositProofsData = { + pendingDepositContainerRoot: + balancesProof.pendingDepositProofsData.pendingDepositContainerRoot, + pendingDepositContainerProof: + balancesProof.pendingDepositProofsData.pendingDepositContainerProof, + pendingDepositIndexes, + pendingDepositProofs, + }; + + // Verify balances with pending deposits and active validators + const tx = await compoundingStakingSSVStrategy.verifyBalances( + balanceProofsData, + pendingDepositProofsData + ); + + // Do not restore the pendingDepositRoots as they can be removed in verifyBalances + // for (let i = 0; i < deposits.length; i++) { + // await hackDepositList(i, deposits[i].pendingDepositRoot, deposits[i]); + // } + + const totalDepositsWei = parseEther(pendingDepositAmount.toString()); + const wethBalance = parseEther(wethAmount.toString()); + const totalValidatorBalance = filteredBalances + .map((balance) => parseEther(balance.toString())) + .reduce((sum, balance) => sum.add(balance), parseEther("0")); + const ethBalance = parseEther(ethAmount.toString()); + const totalBalance = totalDepositsWei + .add(wethBalance) + .add(totalValidatorBalance) + .add(ethBalance); + + await expect(tx) + .to.emit(compoundingStakingSSVStrategy, "BalancesVerified") + .withNamedArgs({ + totalDepositsWei, + totalValidatorBalance, + ethBalance, + }); + + const verifiedEthBalance = + await compoundingStakingSSVStrategy.lastVerifiedEthBalance(); + + expect(verifiedEthBalance).to.equal( + totalDepositsWei.add(totalValidatorBalance).add(ethBalance) + ); + + const stratBalance = await compoundingStakingSSVStrategy.checkBalance( + weth.address + ); + + return { + tx, + totalDepositsWei, + wethBalance, + totalValidatorBalance, + ethBalance, + totalBalance, + verifiedEthBalance, + stratBalance, + }; + }; + + const hackDepositList = async ( + depositIndex, + newPendingDepositRoot, + oldDeposit + ) => { + const { compoundingStakingSSVStrategy } = fixture; + + // Calculate the storage slot for the deposit in the depositList array + const depositListSlot = 53; + const hexStringOf32Bytes = hexZeroPad( + BigNumber.from(depositListSlot).toHexString(), + 32 + ); + const storageSlot = BigNumber.from(keccak256(hexStringOf32Bytes)) + .add(depositIndex) + .toHexString(); + + // Set the pending deposit root in the deposit list + await setStorageAt( + compoundingStakingSSVStrategy.address, + storageSlot, + newPendingDepositRoot + ); + + expect( + await compoundingStakingSSVStrategy.depositList(depositIndex) + ).to.equal(newPendingDepositRoot); + + // Slot 52 (base slot for mapping) + const baseMappingSlot = 52n; + + const oldMappingSlot = keccak256( + solidityPack( + ["bytes32", "uint256"], + [oldDeposit.pendingDepositRoot, baseMappingSlot] + ) + ); + const depositSlot0 = await getStorageAt( + compoundingStakingSSVStrategy.address, + oldMappingSlot + ); + const depositSlot1 = await getStorageAt( + compoundingStakingSSVStrategy.address, + BigNumber.from(oldMappingSlot).add(1).toHexString() + ); + log(`Old deposit data:`); + log(` Slot 0: ${depositSlot0}`); + log(` Slot 1: ${depositSlot1}`); + + // Compute deposits mapping slot: keccak256(key . baseSlot) + const newMappingSlot = keccak256( + solidityPack( + ["bytes32", "uint256"], + [newPendingDepositRoot, baseMappingSlot] + ) + ); + + // Set the deposit data + await setStorageAt( + compoundingStakingSSVStrategy.address, + newMappingSlot, + depositSlot0 + ); + await setStorageAt( + compoundingStakingSSVStrategy.address, + BigNumber.from(newMappingSlot).add(1).toHexString(), + depositSlot1 + ); + + const newDeposit = await compoundingStakingSSVStrategy.deposits( + newPendingDepositRoot + ); + expect(newDeposit.pubKeyHash).to.equal(oldDeposit.pubKeyHash); + expect(newDeposit.amountGwei).to.equal(oldDeposit.amountGwei); + expect(newDeposit.slot).to.equal(oldDeposit.slot); + }; + + // Deposits WETH into the staking strategy + const depositToStrategy = async (amount) => { + const { compoundingStakingSSVStrategy, weth, josh } = fixture; + + const amountWei = parseEther(amount.toString()); + await weth + .connect(josh) + .transfer(compoundingStakingSSVStrategy.address, amountWei); + await compoundingStakingSSVStrategy.connect(sVault).depositAll(); + }; + + describe("Register, stake, withdraw and remove validators", () => { + beforeEach(async () => { + const { weth, josh, ssv, compoundingStakingSSVStrategy } = fixture; + + await setERC20TokenBalance( + compoundingStakingSSVStrategy.address, + ssv, + "1000", + hre + ); + + // Fund the strategy with WETH + await weth + .connect(josh) + .transfer(compoundingStakingSSVStrategy.address, ethUnits("5000")); + }); + + const stakeValidators = async (testValidatorIndex, amount = 1) => { + const { compoundingStakingSSVStrategy, validatorRegistrator } = fixture; + + const amountGwei = BigNumber.from(amount.toString()).mul(ETHInGwei); + + // there is a limitation to this function as it will only check for + // a failure transaction with the last stake call + // for (const testValidator of testValidators.slice(0, validators)) { + const testValidator = testValidators[testValidatorIndex]; + expect( + ( + await compoundingStakingSSVStrategy.validator( + testValidator.publicKeyHash + ) + ).state + ).to.equal(0, "Validator state not 0 (NON_REGISTERED)"); + + const ssvAmount = ethUnits("2"); + // Register a new validator with the SSV Network + const regTx = await compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .registerSsvValidator( + testValidator.publicKey, + testValidator.operatorIds, + testValidator.sharesData, + ssvAmount, + emptyCluster + ); + + await expect(regTx) + .to.emit(compoundingStakingSSVStrategy, "SSVValidatorRegistered") + .withArgs(testValidator.publicKeyHash, testValidator.operatorIds); + + expect( + ( + await compoundingStakingSSVStrategy.validator( + testValidator.publicKeyHash + ) + ).state + ).to.equal(1, "Validator state not 1 (REGISTERED)"); + + // Stake ETH to the new validator + + const depositDataRoot = await calcDepositRoot( + compoundingStakingSSVStrategy.address, + "0x02", + testValidator.publicKey, + testValidator.signature, + amount + ); + + const stakeTx = compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .stakeEth( + { + pubkey: testValidator.publicKey, + signature: testValidator.signature, + depositDataRoot, + }, + amountGwei + ); + + await stakeTx; + + await expect(stakeTx) + .to.emit(compoundingStakingSSVStrategy, "ETHStaked") + .withNamedArgs({ + pubKeyHash: testValidator.publicKeyHash, + pubKey: testValidator.publicKey, + amountWei: amountGwei.mul(GweiInWei), // Convert Gwei to Wei + }); + + expect( + ( + await compoundingStakingSSVStrategy.validator( + testValidator.publicKeyHash + ) + ).state + ).to.equal(2, "Validator state not 2 (STAKED)"); + }; + + it("Should stake to a validator: 1 ETH", async () => { + await stakeValidators(0, 1); + }); + + it("Should stake 1 ETH then 2047 ETH to a validator", async () => { + const { + compoundingStakingSSVStrategy, + validatorRegistrator, + beaconRoots, + weth, + } = fixture; + + const testValidator = testValidators[0]; + + const stratBalanceBefore = + await compoundingStakingSSVStrategy.checkBalance(weth.address); + + // Register a new validator with the SSV Network + await compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .registerSsvValidator( + testValidator.publicKey, + testValidator.operatorIds, + testValidator.sharesData, + ethUnits("2"), + emptyCluster + ); + + const depositDataRoot = await calcDepositRoot( + compoundingStakingSSVStrategy.address, + "0x02", + testValidator.publicKey, + testValidator.signature, + 1 + ); + + // Stake 1 ETH to the new validator + let stakeTx = await compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .stakeEth( + { + pubkey: testValidator.publicKey, + signature: testValidator.signature, + depositDataRoot, + }, + ETHInGwei.mul(1) // 1 ETH + ); + const { pendingDepositRoot, depositSlot } = await getLastDeposit( + compoundingStakingSSVStrategy + ); + + // The hash of the public key should match the leaf in the proof + expect(hashPubKey(testValidator.publicKey)).to.equal( + testValidator.publicKeyHash + ); + + // Set BeaconRoot for timestamp + await beaconRoots["setBeaconRoot(uint256,bytes32)"]( + testValidator.validatorProof.nextBlockTimestamp, + testValidator.validatorProof.root + ); + + // Verify the validator + await compoundingStakingSSVStrategy.verifyValidator( + testValidator.validatorProof.nextBlockTimestamp, + testValidator.index, + testValidator.publicKeyHash, + getWithdrawalCredentials("0x02", compoundingStakingSSVStrategy.address), + testValidator.validatorProof.bytes + ); + + // Set parent beacon root for the block after the verification slots + const depositProcessedSlot = depositSlot + 10000n; + // Put the slot the validator of the first pending deposit was created one epoch later + const firstDepositValidatorCreatedSlot = depositProcessedSlot + 32n; + + await beaconRoots["setBeaconRoot(uint256,bytes32)"]( + calcBlockTimestamp(depositProcessedSlot) + 12n, + testValidator.depositProof.processedBeaconBlockRoot + ); + await beaconRoots["setBeaconRoot(uint256,bytes32)"]( + calcBlockTimestamp(firstDepositValidatorCreatedSlot) + 12n, + testValidator.depositProof.validatorBeaconBlockRoot + ); + + await compoundingStakingSSVStrategy.verifyDeposit( + pendingDepositRoot, + depositProcessedSlot, + testValidator.depositProof.firstPendingDeposit, + testValidator.depositProof.strategyValidator + ); + + // Stake 2047 ETH to the new validator + + const secondDepositAmount = 2047; + const depositDataRoot2 = await calcDepositRoot( + compoundingStakingSSVStrategy.address, + "0x02", + testValidator.publicKey, + testValidator.signature, + secondDepositAmount + ); + + stakeTx = await compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .stakeEth( + { + pubkey: testValidator.publicKey, + signature: testValidator.signature, + depositDataRoot: depositDataRoot2, + }, + BigNumber.from(secondDepositAmount.toString()).mul(GweiInWei) + ); + + const { pendingDepositRoot: pendingDepositRoot2 } = await getLastDeposit( + compoundingStakingSSVStrategy + ); + + await expect(stakeTx) + .to.emit(compoundingStakingSSVStrategy, "ETHStaked") + .withArgs( + testValidator.publicKeyHash, + pendingDepositRoot2, + testValidator.publicKey, + parseEther(secondDepositAmount.toString()) + ); + + // Cheating here by using the same proof as before + // it works as the deposit block is after the second deposit on the execution layer + await compoundingStakingSSVStrategy.verifyDeposit( + pendingDepositRoot2, + depositProcessedSlot, + testValidator.depositProof.firstPendingDeposit, + testValidator.depositProof.strategyValidator + ); + + expect( + await compoundingStakingSSVStrategy.checkBalance(weth.address) + ).to.equal(stratBalanceBefore); + }); + + it("Should revert when first stake amount is not exactly 1 ETH", async () => { + const { compoundingStakingSSVStrategy, validatorRegistrator } = fixture; + + const testValidator = testValidators[0]; + + // Register a new validator with the SSV Network + await compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .registerSsvValidator( + testValidator.publicKey, + testValidator.operatorIds, + testValidator.sharesData, + ethUnits("2"), + emptyCluster + ); + + // Try to stake 2 ETH to the new validator + const stakeTx = compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .stakeEth( + { + pubkey: testValidator.publicKey, + signature: testValidator.signature, + depositDataRoot: testValidator.depositProof.depositDataRoot, + }, + BigNumber.from("2").mul(GweiInWei) + ); + + await expect(stakeTx).to.be.revertedWith("Invalid first deposit amount"); + }); + + it("Should revert registerSsvValidator when contract paused", async () => { + const { compoundingStakingSSVStrategy, governor, validatorRegistrator } = + fixture; + const testValidator = testValidators[0]; + + await compoundingStakingSSVStrategy.connect(governor).pause(); + // Register a new validator with the SSV Network + const tx = compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .registerSsvValidator( + testValidator.publicKey, + testValidator.operatorIds, + testValidator.sharesData, + ethUnits("2"), + emptyCluster + ); + + await expect(tx).to.be.revertedWith("Pausable: paused"); + }); + + it("Should revert stakeEth when contract paused", async () => { + const { compoundingStakingSSVStrategy, governor, validatorRegistrator } = + fixture; + const testValidator = testValidators[0]; + + // Register a new validator with the SSV Network + await compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .registerSsvValidator( + testValidator.publicKey, + testValidator.operatorIds, + testValidator.sharesData, + ethUnits("2"), + emptyCluster + ); + + await compoundingStakingSSVStrategy.connect(governor).pause(); + + const tx = compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .stakeEth( + { + pubkey: testValidator.publicKey, + signature: testValidator.signature, + depositDataRoot: testValidator.depositProof.depositDataRoot, + }, + GweiInWei + ); + + await expect(tx).to.be.revertedWith("Pausable: paused"); + }); + + it("Should revert when registering a validator that is already registered", async () => { + const { compoundingStakingSSVStrategy, validatorRegistrator } = fixture; + + const testValidator = testValidators[0]; + + // Register a new validator with the SSV Network + await compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .registerSsvValidator( + testValidator.publicKey, + testValidator.operatorIds, + testValidator.sharesData, + ethUnits("2"), + emptyCluster + ); + + // Try to register the same validator again + await expect( + compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .registerSsvValidator( + testValidator.publicKey, + testValidator.operatorIds, + testValidator.sharesData, + ethUnits("2"), + emptyCluster + ) + ).to.be.revertedWith("Validator already registered"); + }); + + it("Should revert when staking because of insufficient ETH balance", async () => { + const { compoundingStakingSSVStrategy, validatorRegistrator, weth } = + fixture; + const testValidator = testValidators[0]; + let balance = await weth.balanceOf(compoundingStakingSSVStrategy.address); + balance = balance.div(GweiInWei); // Convert from Wei to Gwei + // Stake ETH to the unregistered validator + const tx = compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .stakeEth( + { + pubkey: testValidator.publicKey, + signature: testValidator.signature, + depositDataRoot: testValidator.depositProof.depositDataRoot, + }, + balance.add(1) // 1e9 Gwei = 1 ETH + ); + + await expect(tx).to.be.revertedWith("Insufficient WETH"); + }); + + it("Should revert when staking a validator that hasn't been registered", async () => { + const { compoundingStakingSSVStrategy, validatorRegistrator } = fixture; + + const testValidator = testValidators[0]; + + // Stake ETH to the unregistered validator + const tx = compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .stakeEth( + { + pubkey: testValidator.publicKey, + signature: testValidator.signature, + depositDataRoot: testValidator.depositProof.depositDataRoot, + }, + ETHInGwei // 1e9 Gwei = 1 ETH + ); + + await expect(tx).to.be.revertedWith("Not registered or verified"); + }); + + // Full validator exit + + it("Should exit a validator with no pending deposit", async () => { + const { validatorRegistrator, compoundingStakingSSVStrategy } = fixture; + + // Third validator is later withdrawn later + await processValidator(testValidators[3], "VERIFIED_DEPOSIT"); + await topUpValidator( + testValidators[3], + testValidators[3].depositProof.depositAmount - 1, + "VERIFIED_DEPOSIT" + ); + + const validatorBefore = await compoundingStakingSSVStrategy.validator( + testValidators[3].publicKeyHash + ); + expect(validatorBefore.state).to.equal(3); // VERIFIED + + // Validator has 1588.918094377 ETH + // assert balances so validator can be fully withdrawable + await assertBalances({ + pendingDepositAmount: 0, + wethAmount: 0, + ethAmount: 0, + balancesProof: testBalancesProofs[1], + activeValidators: [2], + }); + + const validatorAfter = await compoundingStakingSSVStrategy.validator( + testValidators[3].publicKeyHash + ); + expect(validatorAfter.state).to.equal(4); // ACTIVE + + const tx = await compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .validatorWithdrawal(testValidators[3].publicKey, 0, { + value: 1, + }); + + await expect(tx) + .to.emit(compoundingStakingSSVStrategy, "ValidatorWithdraw") + .withArgs(testValidators[3].publicKeyHash, 0); + + expect( + ( + await compoundingStakingSSVStrategy.validator( + testValidators[3].publicKeyHash + ) + ).state + ).to.equal(5); // EXITING + }); + + it("Should exit a validator that is already exiting", async () => { + const { validatorRegistrator, compoundingStakingSSVStrategy } = fixture; + + // Third validator is later withdrawn later + await processValidator(testValidators[3], "VERIFIED_DEPOSIT"); + await topUpValidator( + testValidators[3], + testValidators[3].depositProof.depositAmount - 1, + "VERIFIED_DEPOSIT" + ); + + const validatorBefore = await compoundingStakingSSVStrategy.validator( + testValidators[3].publicKeyHash + ); + expect(validatorBefore.state).to.equal(3); // VERIFIED + + // Validator has 1588.918094377 ETH + // assert balances so validator can be fully withdrawable + await assertBalances({ + pendingDepositAmount: 0, + wethAmount: 0, + ethAmount: 0, + balancesProof: testBalancesProofs[1], + activeValidators: [2], + }); + + const validatorAfter = await compoundingStakingSSVStrategy.validator( + testValidators[3].publicKeyHash + ); + expect(validatorAfter.state).to.equal(4); // ACTIVE + + // First exit + await compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .validatorWithdrawal(testValidators[3].publicKey, 0, { + value: 1, + }); + + expect( + ( + await compoundingStakingSSVStrategy.validator( + testValidators[3].publicKeyHash + ) + ).state + ).to.equal(5); // EXITING + + // Second exit + const tx = await compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .validatorWithdrawal(testValidators[3].publicKey, 0, { + value: 1, + }); + + await expect(tx) + .to.emit(compoundingStakingSSVStrategy, "ValidatorWithdraw") + .withArgs(testValidators[3].publicKeyHash, 0); + }); + + it("Should revert when validator's balance hasn't been confirmed to equal or surpass 32.25 ETH", async () => { + const { validatorRegistrator, compoundingStakingSSVStrategy } = fixture; + + // Third validator is later withdrawn later + await processValidator(testValidators[3], "VERIFIED_DEPOSIT"); + await topUpValidator(testValidators[3], 32, "VERIFIED_DEPOSIT"); + + // verifyBalances has not been called so the validator is still VERIFIED even though the + // validator has more then 32.25 ETH staked + + const tx = compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .validatorWithdrawal(testValidators[3].publicKey, 0, { + value: 1, + }); + + await expect(tx).to.be.revertedWith("Validator not active/exiting"); + }); + + it("Should revert partial withdrawal when validator's balance hasn't been confirmed to equal or surpass 32 ETH", async () => { + const { validatorRegistrator, compoundingStakingSSVStrategy } = fixture; + + // Third validator is later withdrawn later + await processValidator(testValidators[3], "VERIFIED_DEPOSIT"); + await topUpValidator( + testValidators[3], + testValidators[3].depositProof.depositAmount - 1, + "VERIFIED_DEPOSIT" + ); + + const tx = compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .validatorWithdrawal(testValidators[3].publicKey, 1, { + value: 1, + }); + + await expect(tx).to.be.revertedWith("Validator not active/exiting"); + }); + + it("Should revert when exiting a validator with a pending deposit", async () => { + const { validatorRegistrator, compoundingStakingSSVStrategy } = fixture; + + // Third validator is later withdrawn later + await processValidator(testValidators[3], "VERIFIED_DEPOSIT"); + // Stake but do not verify the deposit + await topUpValidator( + testValidators[3], + testValidators[3].depositProof.depositAmount - 1, + "VERIFIED_DEPOSIT" + ); + + // Validator has 1588.918094377 ETH + // assert balances so validator can be fully withdrawable + await assertBalances({ + pendingDepositAmount: 0, + wethAmount: 0, + ethAmount: 0, + balancesProof: testBalancesProofs[1], + activeValidators: [2], + }); + + await topUpValidator(testValidators[3], 1, "STAKED"); + + // Amount 0 is a full validator exit + const tx = compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .validatorWithdrawal(testValidators[3].publicKey, 0, { value: 1 }); + + await expect(tx).to.be.revertedWith("Pending deposit"); + }); + + it("Should revert when verifying deposit between snapBalances and verifyBalances", async () => { + const { beaconRoots, compoundingStakingSSVStrategy } = fixture; + const testValidator = testValidators[3]; + + // Third validator is later withdrawn later + await processValidator(testValidator, "VERIFIED_VALIDATOR"); + const { pendingDepositRoot, depositSlot } = await getLastDeposit( + compoundingStakingSSVStrategy + ); + + // Snap balances before the deposit is processed + await compoundingStakingSSVStrategy.snapBalances(); + + // Set parent beacon root for the block after the verification slots + const depositProcessedSlot = depositSlot + 10000n; + + await beaconRoots["setBeaconRoot(uint256,bytes32)"]( + calcBlockTimestamp(depositProcessedSlot) + 12n, + testValidator.depositProof.processedBeaconBlockRoot + ); + + const verifiedDepositTx = compoundingStakingSSVStrategy.verifyDeposit( + pendingDepositRoot, + depositProcessedSlot, + testValidator.depositProof.firstPendingDeposit, + testValidator.depositProof.strategyValidator + ); + + await expect(verifiedDepositTx).to.be.revertedWith( + "Deposit after balance snapshot" + ); + }); + + it("Should partial withdraw from a validator with a pending deposit", async () => { + const { validatorRegistrator, compoundingStakingSSVStrategy } = fixture; + + // Third validator is later withdrawn later + await processValidator(testValidators[3], "VERIFIED_DEPOSIT"); + // Stake but do not verify the deposit + await topUpValidator( + testValidators[3], + testValidators[3].depositProof.depositAmount - 1, + "VERIFIED_DEPOSIT" + ); + + // Validator has 1588.918094377 ETH + // assert balances so validator can be fully withdrawable + await assertBalances({ + pendingDepositAmount: 0, + wethAmount: 0, + ethAmount: 0, + balancesProof: testBalancesProofs[1], + activeValidators: [2], + }); + + await topUpValidator(testValidators[3], 1, "STAKED"); + + expect( + ( + await compoundingStakingSSVStrategy.validator( + testValidators[3].publicKeyHash + ) + ).state + ).to.equal(4); // ACTIVE + + const withdrawAmountGwei = ETHInGwei.mul(5); + + const tx = await compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .validatorWithdrawal(testValidators[3].publicKey, withdrawAmountGwei, { + value: 1, + }); + + await expect(tx) + .to.emit(compoundingStakingSSVStrategy, "ValidatorWithdraw") + .withArgs( + testValidators[3].publicKeyHash, + withdrawAmountGwei.mul(GweiInWei) + ); + + expect( + ( + await compoundingStakingSSVStrategy.validator( + testValidators[3].publicKeyHash + ) + ).state + ).to.equal(4); // ACTIVE + }); + + // Remove validator + it("Should remove a validator when validator is registered", async () => { + const { compoundingStakingSSVStrategy, validatorRegistrator } = fixture; + + const testValidator = testValidators[0]; + + // Register a new validator with the SSV Network + await compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .registerSsvValidator( + testValidator.publicKey, + testValidator.operatorIds, + testValidator.sharesData, + ethUnits("2"), + emptyCluster + ); + + expect( + ( + await compoundingStakingSSVStrategy.validator( + testValidator.publicKeyHash + ) + ).state + ).to.equal(1, "Validator state not 1 (REGISTERED)"); + + // Withdraw from the validator + const removeTx = compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .removeSsvValidator( + testValidator.publicKey, + testValidator.operatorIds, + emptyCluster + ); + + await expect(removeTx) + .to.emit(compoundingStakingSSVStrategy, "SSVValidatorRemoved") + .withArgs(testValidator.publicKeyHash, testValidator.operatorIds); + }); + + it("Should revert when removing a validator that is not registered", async () => { + const { compoundingStakingSSVStrategy, validatorRegistrator } = fixture; + + const testValidator = testValidators[0]; + expect( + ( + await compoundingStakingSSVStrategy.validator( + testValidator.publicKeyHash + ) + ).state + ).to.equal(0, "Validator state not 0 (NON_REGISTERED)"); + + // Try to remove a validator that is not registered + const removeTx = compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .removeSsvValidator( + testValidator.publicKey, + testValidator.operatorIds, + emptyCluster + ); + + await expect(removeTx).to.be.revertedWith("Validator not regd or exited"); + }); + + it("Should remove a validator when validator is exited", async () => { + const { + validatorRegistrator, + compoundingStakingSSVStrategy, + compoundingStakingStrategyView, + } = fixture; + + // Third validator is later withdrawn later + await processValidator(testValidators[3], "VERIFIED_DEPOSIT"); + await topUpValidator( + testValidators[3], + testValidators[3].depositProof.depositAmount - 1, + "VERIFIED_DEPOSIT" + ); + + // Validator has 1588.918094377 ETH + await assertBalances({ + pendingDepositAmount: 0, + wethAmount: 0, + ethAmount: 0, + balancesProof: testBalancesProofs[1], + activeValidators: [2], + }); + + await expect( + ( + await compoundingStakingStrategyView.getVerifiedValidators() + ).length + ).to.equal(1); + + // Verify the validator with a zero balance which marks the validator as exited + await assertBalances({ + pendingDepositAmount: 0, + wethAmount: 0, + ethAmount: 0, + balancesProof: testBalancesProofs[2], + activeValidators: [2], + }); + + await expect( + ( + await compoundingStakingStrategyView.getVerifiedValidators() + ).length + ).to.equal(0); + + const removeTx = await compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .removeSsvValidator( + testValidators[3].publicKey, + testValidators[3].operatorIds, + emptyCluster + ); + + await expect(removeTx) + .to.emit(compoundingStakingSSVStrategy, "SSVValidatorRemoved") + .withArgs( + testValidators[3].publicKeyHash, + testValidators[3].operatorIds + ); + }); + + it("Should not remove a validator if it still has a pending deposit", async () => { + const { compoundingStakingStrategyView } = fixture; + const epochTime = 12 * 32; + + // Third validator is later withdrawn later + await processValidator(testValidators[3], "VERIFIED_DEPOSIT"); + await topUpValidator( + testValidators[3], + testValidators[3].depositProof.depositAmount - 1, + "VERIFIED_DEPOSIT" + ); + + // Validator has 1588.918094377 ETH + await assertBalances({ + pendingDepositAmount: 0, + wethAmount: 0, + ethAmount: 0, + balancesProof: testBalancesProofs[1], + activeValidators: [2], + }); + + await expect( + ( + await compoundingStakingStrategyView.getVerifiedValidators() + ).length + ).to.equal(1); + + // need to advance to a new slot so there are no duplicate deposits + await advanceTime(epochTime * 4); + + await topUpValidator( + testValidators[3], + testValidators[3].depositProof.depositAmount - 1, + "STAKED" + ); + + // Verify the validator with a zero balance doesn't mark the validator as exited + // because it still has one pending deposit + await assertBalances({ + pendingDepositAmount: 50.497526, + wethAmount: 0, + ethAmount: 0, + balancesProof: testBalancesProofs[2], + activeValidators: [2], + }); + + await expect( + ( + await compoundingStakingStrategyView.getVerifiedValidators() + ).length + ).to.equal(1); + + // deposit to on beacon chain exited validator can still be verified + await verifyDeposit(testValidators[3]); + + // and another snap/verify balances will exit that validator + await assertBalances({ + pendingDepositAmount: 0, + wethAmount: 0, + ethAmount: 0, + balancesProof: testBalancesProofs[2], + activeValidators: [2], + }); + + // which means no more active validators + await expect( + ( + await compoundingStakingStrategyView.getVerifiedValidators() + ).length + ).to.equal(0); + }); + + it("Should revert when removing a validator that has been found", async () => { + await stakeValidators(0, 1); + + const testValidator = testValidators[0]; + + const { compoundingStakingSSVStrategy } = fixture; + + expect( + ( + await compoundingStakingSSVStrategy.validator( + testValidator.publicKeyHash + ) + ).state + ).to.equal(2, "Validator state not 2 (STAKED)"); + }); + }); + + describe("Verify deposits", () => { + const testValidator = testValidators[1]; + let pendingDepositRoot; + let depositSlot; + beforeEach(async () => { + const { compoundingStakingSSVStrategy } = fixture; + + // register, stake and verify validator + await processValidator(testValidator, "VERIFIED_VALIDATOR"); + const lastDeposit = await getLastDeposit(compoundingStakingSSVStrategy); + pendingDepositRoot = lastDeposit.pendingDepositRoot; + depositSlot = lastDeposit.depositSlot; + }); + it("Should revert first pending deposit slot is zero", async () => { + const { compoundingStakingSSVStrategy } = fixture; + + const tx = compoundingStakingSSVStrategy.verifyDeposit( + pendingDepositRoot, + depositSlot + 100n, + { ...testValidator.depositProof.firstPendingDeposit, slot: 0 }, + testValidator.depositProof.strategyValidator + ); + + await expect(tx).to.be.revertedWith("Zero 1st pending deposit slot"); + }); + it("Should revert when no deposit", async () => { + const { compoundingStakingSSVStrategy } = fixture; + + const invalidPendingDepositRoot = "0x" + randomBytes(32).toString("hex"); + + const tx = compoundingStakingSSVStrategy.verifyDeposit( + invalidPendingDepositRoot, + depositSlot + 100n, + testValidator.depositProof.firstPendingDeposit, + testValidator.depositProof.strategyValidator + ); + + await expect(tx).to.be.revertedWith("Deposit not pending"); + }); + it("Should revert when deposit verified again", async () => { + const { beaconRoots, compoundingStakingSSVStrategy } = fixture; + + const depositProcessedSlot = depositSlot + 100n; + await beaconRoots["setBeaconRoot(uint256,bytes32)"]( + calcBlockTimestamp(depositProcessedSlot) + 12n, + testValidator.depositProof.processedBeaconBlockRoot + ); + + await compoundingStakingSSVStrategy.verifyDeposit( + pendingDepositRoot, + depositProcessedSlot, + testValidator.depositProof.firstPendingDeposit, + testValidator.depositProof.strategyValidator + ); + + const tx = compoundingStakingSSVStrategy.verifyDeposit( + pendingDepositRoot, + depositProcessedSlot + 1n, + testValidator.depositProof.firstPendingDeposit, + testValidator.depositProof.strategyValidator + ); + + await expect(tx).to.be.revertedWith("Deposit not pending"); + }); + it("Should revert when processed slot is after snapped balances", async () => { + const { compoundingStakingSSVStrategy } = fixture; + + // Make sure we are at the next slot by moving time forward 12 seconds + await advanceTime(12); + await compoundingStakingSSVStrategy.snapBalances(); + + const currentBlock = await ethers.provider.getBlock("latest"); + const currentSlot = calcSlot(BigInt(currentBlock.timestamp)); + const depositProcessedSlot = currentSlot; + + const tx = compoundingStakingSSVStrategy.verifyDeposit( + pendingDepositRoot, + depositProcessedSlot, + testValidator.depositProof.firstPendingDeposit, + testValidator.depositProof.strategyValidator + ); + + await expect(tx).to.be.revertedWith("Deposit after balance snapshot"); + }); + it("Should verify deposit with no snapped balances", async () => { + const { beaconRoots, compoundingStakingSSVStrategy } = fixture; + + const depositProcessedSlot = depositSlot + 1n; + await beaconRoots["setBeaconRoot(uint256,bytes32)"]( + calcBlockTimestamp(depositProcessedSlot) + 12n, + testValidator.depositProof.processedBeaconBlockRoot + ); + + const tx = await compoundingStakingSSVStrategy.verifyDeposit( + pendingDepositRoot, + depositProcessedSlot, + testValidator.depositProof.firstPendingDeposit, + testValidator.depositProof.strategyValidator + ); + + await expect(tx) + .to.emit(compoundingStakingSSVStrategy, "DepositVerified") + .withArgs(pendingDepositRoot, parseEther("1")); + }); + it("Should verify deposit with processed slot 1 before the snapped balances slot", async () => { + const { beaconRoots, compoundingStakingSSVStrategy } = fixture; + + // Move two slots ahead so depositProcessedSlot is after the snap + await advanceTime(24); + + await compoundingStakingSSVStrategy.snapBalances(); + + const { timestamp: snappedTimestamp } = + await compoundingStakingSSVStrategy.snappedBalance(); + const depositProcessedSlot = calcSlot(BigInt(snappedTimestamp)) - 1n; + + await beaconRoots["setBeaconRoot(uint256,bytes32)"]( + calcBlockTimestamp(depositProcessedSlot) + 12n, + testValidator.depositProof.processedBeaconBlockRoot + ); + + const tx = await compoundingStakingSSVStrategy.verifyDeposit( + pendingDepositRoot, + depositProcessedSlot, + testValidator.depositProof.firstPendingDeposit, + testValidator.depositProof.strategyValidator + ); + + await expect(tx) + .to.emit(compoundingStakingSSVStrategy, "DepositVerified") + .withArgs(pendingDepositRoot, parseEther("1")); + }); + it("Should verify deposit with processed slot well before the snapped balances slot", async () => { + const { beaconRoots, compoundingStakingSSVStrategy } = fixture; + + // Move 10 slots ahead of the deposit slot + await advanceTime(120); + await compoundingStakingSSVStrategy.snapBalances(); + + const depositProcessedSlot = depositSlot + 1n; + + await beaconRoots["setBeaconRoot(uint256,bytes32)"]( + calcBlockTimestamp(depositProcessedSlot) + 12n, + testValidator.depositProof.processedBeaconBlockRoot + ); + + const tx = await compoundingStakingSSVStrategy.verifyDeposit( + pendingDepositRoot, + depositProcessedSlot, + testValidator.depositProof.firstPendingDeposit, + testValidator.depositProof.strategyValidator + ); + + await expect(tx) + .to.emit(compoundingStakingSSVStrategy, "DepositVerified") + .withArgs(pendingDepositRoot, parseEther("1")); + }); + }); + + describe("Deposit/Withdraw in the strategy", () => { + it("Should deposit ETH in the strategy", async () => { + const { compoundingStakingSSVStrategy, weth, josh } = fixture; + const balBefore = + await compoundingStakingSSVStrategy.depositedWethAccountedFor(); + const checkBalanceBefore = + await compoundingStakingSSVStrategy.checkBalance(weth.address); + + const depositAmount = parseEther("10"); + await weth + .connect(josh) + .transfer(compoundingStakingSSVStrategy.address, depositAmount); + const depositTx = compoundingStakingSSVStrategy + .connect(sVault) + .deposit(weth.address, depositAmount); + + await expect(depositTx) + .to.emit(compoundingStakingSSVStrategy, "Deposit") + .withArgs(weth.address, zero, depositAmount); + + expect( + await compoundingStakingSSVStrategy.depositedWethAccountedFor() + ).to.equal( + balBefore.add(depositAmount), + "Deposit amount not set properly" + ); + expect( + await compoundingStakingSSVStrategy.checkBalance(weth.address) + ).to.equal( + checkBalanceBefore.add(depositAmount), + "Check balance not updated properly" + ); + }); + + it("Should depositAll ETH in the strategy when depositedWethAccountedFor is zero", async () => { + const { compoundingStakingSSVStrategy, weth, josh } = fixture; + + const checkBalanceBefore = + await compoundingStakingSSVStrategy.checkBalance(weth.address); + const depositAmount = parseEther("10"); + await weth + .connect(josh) + .transfer(compoundingStakingSSVStrategy.address, depositAmount); + const balBefore = + await compoundingStakingSSVStrategy.depositedWethAccountedFor(); + + const depositTx = compoundingStakingSSVStrategy + .connect(sVault) + .depositAll(); + + await expect(depositTx) + .to.emit(compoundingStakingSSVStrategy, "Deposit") + .withArgs(weth.address, zero, depositAmount); + + expect( + await compoundingStakingSSVStrategy.depositedWethAccountedFor() + ).to.equal( + balBefore.add(depositAmount), + "Deposit amount not set properly" + ); + + expect( + await compoundingStakingSSVStrategy.checkBalance(weth.address) + ).to.equal( + checkBalanceBefore.add(depositAmount), + "Check balance not updated properly" + ); + }); + + it("Should depositAll ETH in the strategy when depositedWethAccountedFor is not zero", async () => { + const { compoundingStakingSSVStrategy, weth, josh } = fixture; + + let depositAmount = parseEther("10"); + + await weth + .connect(josh) + .transfer(compoundingStakingSSVStrategy.address, depositAmount); + await compoundingStakingSSVStrategy + .connect(sVault) + .deposit(weth.address, depositAmount); + + const balBefore = + await compoundingStakingSSVStrategy.depositedWethAccountedFor(); + + expect(balBefore).to.equal( + depositAmount, + "Deposit amount not set properly" + ); + + depositAmount = parseEther("20"); + + const checkBalanceBefore = + await compoundingStakingSSVStrategy.checkBalance(weth.address); + + // Josh deposits more ETH + await weth + .connect(josh) + .transfer(compoundingStakingSSVStrategy.address, depositAmount); + + const depositTx = compoundingStakingSSVStrategy + .connect(sVault) + .depositAll(); + + await expect(depositTx) + .to.emit(compoundingStakingSSVStrategy, "Deposit") + .withArgs(weth.address, zero, depositAmount); + + expect( + await compoundingStakingSSVStrategy.depositedWethAccountedFor() + ).to.equal( + balBefore.add(depositAmount), + "Deposit amount not set properly" + ); + + expect( + await compoundingStakingSSVStrategy.checkBalance(weth.address) + ).to.equal( + checkBalanceBefore.add(depositAmount), + "Check balance not updated properly" + ); + }); + + it("Should revert when depositing 0 ETH in the strategy", async () => { + const { compoundingStakingSSVStrategy, weth } = fixture; + + await expect( + compoundingStakingSSVStrategy.connect(sVault).deposit( + weth.address, + 0 // 0 ETH + ) + ).to.be.revertedWith("Must deposit something"); + }); + + it("Should withdraw ETH from the strategy, no ETH", async () => { + const { compoundingStakingSSVStrategy, weth, josh } = fixture; + + const depositAmount = parseEther("10"); + await weth + .connect(josh) + .transfer(compoundingStakingSSVStrategy.address, depositAmount); + await compoundingStakingSSVStrategy + .connect(sVault) + .deposit(weth.address, depositAmount); + + const checkBalanceBefore = + await compoundingStakingSSVStrategy.checkBalance(weth.address); + + const withdrawTx = compoundingStakingSSVStrategy + .connect(sVault) + .withdraw(josh.address, weth.address, depositAmount); + + await expect(withdrawTx) + .to.emit(compoundingStakingSSVStrategy, "Withdrawal") + .withArgs(weth.address, zero, depositAmount); + + expect( + await compoundingStakingSSVStrategy.depositedWethAccountedFor() + ).to.equal(0, "Withdraw amount not set properly"); + + expect( + await compoundingStakingSSVStrategy.checkBalance(weth.address) + ).to.equal( + checkBalanceBefore.sub(depositAmount), + "Check balance not updated properly" + ); + }); + + it("Should withdraw ETH from the strategy, withdraw some ETH", async () => { + const { compoundingStakingSSVStrategy, weth, josh } = fixture; + + const depositAmount = parseEther("10"); + await weth + .connect(josh) + .transfer(compoundingStakingSSVStrategy.address, depositAmount); + await compoundingStakingSSVStrategy + .connect(sVault) + .deposit(weth.address, depositAmount); + + // Donate raw ETH to the strategy + await setBalance(compoundingStakingSSVStrategy.address, parseEther("5")); + + const checkBalanceBefore = + await compoundingStakingSSVStrategy.checkBalance(weth.address); + + const withdrawTx = compoundingStakingSSVStrategy + .connect(sVault) + .withdraw( + josh.address, + weth.address, + depositAmount.add(parseEther("5")) + ); + + await expect(withdrawTx) + .to.emit(compoundingStakingSSVStrategy, "Withdrawal") + .withArgs(weth.address, zero, depositAmount.add(parseEther("5"))); + + expect( + await compoundingStakingSSVStrategy.depositedWethAccountedFor() + ).to.equal(0, "Withdraw amount not set properly"); + + expect( + await compoundingStakingSSVStrategy.checkBalance(weth.address) + ).to.equal( + checkBalanceBefore.sub(depositAmount), // The extra 5 ETH is raw ETH are not taken into account, this is expected behavior + "Check balance not updated properly" + ); + }); + + it("Should revert when withdrawing other than WETH", async () => { + const { compoundingStakingSSVStrategy, josh } = fixture; + + // Try to withdraw USDC instead of WETH + await expect( + compoundingStakingSSVStrategy + .connect(sVault) + .withdraw(josh.address, josh.address, parseEther("10")) + ).to.be.revertedWith("Unsupported asset"); + }); + + it("Should revert when withdrawing 0 ETH from the strategy", async () => { + const { compoundingStakingSSVStrategy, weth, josh } = fixture; + + await expect( + compoundingStakingSSVStrategy.connect(sVault).withdraw( + josh.address, + weth.address, // 0 ETH + 0 // 0 amount + ) + ).to.be.revertedWith("Must withdraw something"); + }); + + it("Should revert when withdrawing to the zero address", async () => { + const { compoundingStakingSSVStrategy, weth } = fixture; + + await expect( + compoundingStakingSSVStrategy.connect(sVault).withdraw( + zero, // zero address + weth.address, + parseEther("10") + ) + ).to.be.revertedWith("Must specify recipient"); + }); + + it("Should withdrawAll ETH from the strategy, no ETH", async () => { + const { compoundingStakingSSVStrategy, weth, josh } = fixture; + + const depositAmount = parseEther("10"); + await weth + .connect(josh) + .transfer(compoundingStakingSSVStrategy.address, depositAmount); + await compoundingStakingSSVStrategy + .connect(sVault) + .deposit(weth.address, depositAmount); + + const withdrawTx = compoundingStakingSSVStrategy + .connect(sVault) + .withdrawAll(); + + await expect(withdrawTx) + .to.emit(compoundingStakingSSVStrategy, "Withdrawal") + .withArgs(weth.address, zero, depositAmount); + + expect( + await compoundingStakingSSVStrategy.depositedWethAccountedFor() + ).to.equal(0, "Withdraw amount not set properly"); + + expect( + await compoundingStakingSSVStrategy.checkBalance(weth.address) + ).to.equal(0, "Check balance not updated properly"); + }); + + it("Should withdrawAll ETH from the strategy, withdraw some ETH", async () => { + const { compoundingStakingSSVStrategy, weth, josh } = fixture; + + const depositAmount = parseEther("10"); + await weth + .connect(josh) + .transfer(compoundingStakingSSVStrategy.address, depositAmount); + await compoundingStakingSSVStrategy + .connect(sVault) + .deposit(weth.address, depositAmount); + + // Donate raw ETH to the strategy + await setBalance(compoundingStakingSSVStrategy.address, parseEther("5")); + + const withdrawTx = compoundingStakingSSVStrategy + .connect(sVault) + .withdrawAll(); + + await expect(withdrawTx) + .to.emit(compoundingStakingSSVStrategy, "Withdrawal") + .withArgs(weth.address, zero, depositAmount.add(parseEther("5"))); + + expect( + await compoundingStakingSSVStrategy.depositedWethAccountedFor() + ).to.equal(0, "Withdraw amount not set properly"); + + expect( + await compoundingStakingSSVStrategy.checkBalance(weth.address) + ).to.equal(0, "Check balance not updated properly"); + }); + }); + + describe("Strategy balances", () => { + describe("When no execution rewards (ETH), no pending deposits and no active validators", () => { + const verifyBalancesNoDepositsOrValidators = async () => { + const { compoundingStakingSSVStrategy } = fixture; + + const tx = await compoundingStakingSSVStrategy.verifyBalances( + { + balancesContainerRoot: ZERO_BYTES32, + balancesContainerProof: "0x", + validatorBalanceLeaves: [], + validatorBalanceProofs: [], + }, + emptyPendingDepositProofs + ); + + return tx; + }; + it("Should verify balances with no WETH", async () => { + const { compoundingStakingSSVStrategy, weth } = fixture; + + const { timestamp } = await snapBalances(); + + const tx = await verifyBalancesNoDepositsOrValidators(); + + await expect(tx) + .to.emit(compoundingStakingSSVStrategy, "BalancesVerified") + .withArgs( + timestamp, + 0, // totalDepositsWei + 0, // totalValidatorBalance + 0 // ethBalance + ); + + expect( + await compoundingStakingSSVStrategy.lastVerifiedEthBalance(), + "Last verified ETH balance" + ).to.equal(0); + expect( + await compoundingStakingSSVStrategy.checkBalance(weth.address) + ).to.equal(0); + }); + it("Should verify balances with some WETH transferred before snap", async () => { + const { compoundingStakingSSVStrategy, josh, weth } = fixture; + + // Send some WETH to the strategy before the snap + const wethAmountAdded = parseEther("1.23"); + await weth + .connect(josh) + .transfer(compoundingStakingSSVStrategy.address, wethAmountAdded); + await compoundingStakingSSVStrategy.connect(sVault).depositAll(); + + const { timestamp } = await snapBalances(); + + const tx = await verifyBalancesNoDepositsOrValidators(); + + await expect(tx) + .to.emit(compoundingStakingSSVStrategy, "BalancesVerified") + .withArgs( + timestamp, + 0, // totalDepositsWei + 0, // totalValidatorBalance + 0 // ethBalance + ); + + expect( + await compoundingStakingSSVStrategy.lastVerifiedEthBalance(), + "Last verified ETH balance" + ).to.equal(0); + expect( + await compoundingStakingSSVStrategy.checkBalance(weth.address) + ).to.equal(wethAmountAdded); + }); + it("Should verify balances with some WETH transferred after snap", async () => { + const { compoundingStakingSSVStrategy, josh, weth } = fixture; + + const { timestamp } = await snapBalances(); + + // Send some WETH to the strategy after the snap + const wethAmountAdded = parseEther("5.67"); + await weth + .connect(josh) + .transfer(compoundingStakingSSVStrategy.address, wethAmountAdded); + + const tx = await verifyBalancesNoDepositsOrValidators(); + + await expect(tx) + .to.emit(compoundingStakingSSVStrategy, "BalancesVerified") + .withArgs( + timestamp, + 0, // totalDepositsWei + 0, // totalValidatorBalance + 0 // ethBalance + ); + + expect( + await compoundingStakingSSVStrategy.lastVerifiedEthBalance(), + "Last verified ETH balance" + ).to.equal(0); + expect( + await compoundingStakingSSVStrategy.checkBalance(weth.address) + ).to.equal(wethAmountAdded); + }); + it("Should verify balances with some WETH transferred before and after snap", async () => { + const { compoundingStakingSSVStrategy, josh, weth } = fixture; + + // Send some WETH to the strategy before the snap + const wethAmountBefore = parseEther("1.23"); + await weth + .connect(josh) + .transfer(compoundingStakingSSVStrategy.address, wethAmountBefore); + + const { timestamp } = await snapBalances(); + + // Send some WETH to the strategy after the snap + const wethAmountAdded = parseEther("5.67"); + await weth + .connect(josh) + .transfer(compoundingStakingSSVStrategy.address, wethAmountAdded); + + const tx = await verifyBalancesNoDepositsOrValidators(); + + await expect(tx) + .to.emit(compoundingStakingSSVStrategy, "BalancesVerified") + .withArgs( + timestamp, + 0, // totalDepositsWei + 0, // totalValidatorBalance + 0 // ethBalance + ); + + expect( + await compoundingStakingSSVStrategy.lastVerifiedEthBalance(), + "Last verified ETH balance" + ).to.equal(0); + expect( + await compoundingStakingSSVStrategy.checkBalance(weth.address) + ).to.equal(wethAmountBefore.add(wethAmountAdded)); + }); + it("Should verify balances with one registered validator", async () => { + await processValidator(testValidators[0], "REGISTERED"); + + const balancesAfter = await assertBalances({ + pendingDepositAmount: 0, + wethAmount: 10, + ethAmount: 0, + balancesProof: testBalancesProofs[2], + activeValidators: [], // no active validators + }); + + expect(balancesAfter.wethBalance).to.equal(parseEther("10")); + expect(balancesAfter.verifiedEthBalance).to.equal(0); + expect(balancesAfter.stratBalance).to.equal(parseEther("10")); + }); + it("Should verify balances with one staked validator", async () => { + await processValidator(testValidators[0], "STAKED"); + + const balancesAfter = await assertBalances({ + pendingDepositAmount: 1, + wethAmount: 0, + ethAmount: 0, + balancesProof: testBalancesProofs[2], + activeValidators: [], // no active validators + }); + + const depositAmountWei = parseEther("1"); + expect(balancesAfter.totalDepositsWei).to.equal(depositAmountWei); + expect(balancesAfter.verifiedEthBalance).to.equal(depositAmountWei); + expect(balancesAfter.stratBalance).to.equal(depositAmountWei); + }); + it("Should verify balances with one exited verified validator", async () => { + // Test validator has index 2018225 has a 32.008954871 balance + const testValidatorIndex = 4; + await processValidator( + testValidators[testValidatorIndex], + "VERIFIED_VALIDATOR" + ); + + await assertBalances({ + pendingDepositAmount: 1, + wethAmount: 0, + ethAmount: 0, + balancesProof: testBalancesProofs[5], + activeValidators: [testValidatorIndex], + }); + }); + it("Should not verify a validator with incorrect withdrawal credential validator type", async () => { + const originalValidatorProof = testValidators[0].validatorProof.bytes; + // replace the 0x02 validator type credentials to an invalid 0x01 one + const wrongValidatorTypeProof = + "0x01" + originalValidatorProof.substring(4); + testValidators[0].validatorProof.bytes = wrongValidatorTypeProof; + + await expect( + processValidator(testValidators[0], "VERIFIED_DEPOSIT") + ).to.be.revertedWith("Invalid withdrawal cred"); + + testValidators[0].validatorProof.bytes = originalValidatorProof; + }); + + it("Should not verify a validator with incorrect withdrawal zero padding", async () => { + const originalValidatorProof = testValidators[0].validatorProof.bytes; + // replace the 0x02 validator type credentials to an invalid 0x01 one + const wrongValidatorTypeProof = + "0x020001" + originalValidatorProof.substring(8); + testValidators[0].validatorProof.bytes = wrongValidatorTypeProof; + + await expect( + processValidator(testValidators[0], "VERIFIED_DEPOSIT") + ).to.be.revertedWith("Invalid withdrawal cred"); + + testValidators[0].validatorProof.bytes = originalValidatorProof; + }); + + it("Should verify balances with one verified deposit", async () => { + await processValidator(testValidators[0], "VERIFIED_DEPOSIT"); + + const balancesAfter = await assertBalances({ + pendingDepositAmount: 0, + wethAmount: 0, + ethAmount: 0, + balancesProof: testBalancesProofs[2], + activeValidators: [0], + }); + + const expectedValidatorBalance = parseEther( + testBalancesProofs[2].validatorBalances[0].toString() + ); + expect(balancesAfter.totalDepositsWei).to.equal(0); + expect(balancesAfter.totalValidatorBalance).to.equal( + expectedValidatorBalance + ); + expect(balancesAfter.verifiedEthBalance).to.equal( + expectedValidatorBalance + ); + expect(balancesAfter.stratBalance).to.equal(expectedValidatorBalance); + }); + }); + describe("When an active validator does a", () => { + let balancesBefore; + beforeEach(async () => { + // Third validator is later withdrawn later + await processValidator(testValidators[3], "VERIFIED_DEPOSIT"); + await topUpValidator( + testValidators[3], + testValidators[3].depositProof.depositAmount - 1, + "VERIFIED_DEPOSIT" + ); + }); + describe("partial withdrawal", () => { + beforeEach(async () => { + balancesBefore = await assertBalances({ + pendingDepositAmount: 0, + wethAmount: 0, + ethAmount: 0, + balancesProof: testBalancesProofs[0], + activeValidators: [2], + }); + }); + it("Should account for a pending partial withdrawal", async () => { + const { compoundingStakingSSVStrategy, validatorRegistrator } = + fixture; + + const withdrawalAmount = 640; + // fund 1 WEI for the withdrawal request + await setBalance(compoundingStakingSSVStrategy.address, "0x1"); + const tx = await compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .validatorWithdrawal( + testValidators[3].publicKey, + parseUnits(withdrawalAmount.toString(), 9) + ); + + await expect(tx) + .to.emit(compoundingStakingSSVStrategy, "ValidatorWithdraw") + .withArgs( + testValidators[3].publicKeyHash, + parseEther(withdrawalAmount.toString()) + ); + + const balancesAfter = await assertBalances({ + pendingDepositAmount: 0, + wethAmount: 0, + ethAmount: 0, + balancesProof: testBalancesProofs[0], + activeValidators: [2], + }); + expect(balancesAfter.stratBalance).to.equal( + balancesBefore.stratBalance + ); + }); + it("Should account for a processed partial withdrawal", async () => { + const { compoundingStakingSSVStrategy, validatorRegistrator } = + fixture; + + const withdrawalAmount = 640; + // fund 1 WEI for the withdrawal request + await setBalance(compoundingStakingSSVStrategy.address, "0x1"); + const tx = await compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .validatorWithdrawal( + testValidators[3].publicKey, + parseUnits(withdrawalAmount.toString(), 9) + ); + + await expect(tx) + .to.emit(compoundingStakingSSVStrategy, "ValidatorWithdraw") + .withArgs( + testValidators[3].publicKeyHash, + parseEther(withdrawalAmount.toString()) + ); + + const concensusRewards = + testBalancesProofs[0].validatorBalances[2] - + testBalancesProofs[1].validatorBalances[2] - + withdrawalAmount; + + const balancesAfter = await assertBalances({ + pendingDepositAmount: 0, + wethAmount: 0, + ethAmount: withdrawalAmount + concensusRewards, + balancesProof: testBalancesProofs[1], + activeValidators: [2], + }); + expect(balancesAfter.stratBalance).to.equal( + balancesBefore.stratBalance + ); + }); + }); + describe("full withdrawal", () => { + beforeEach(async () => { + balancesBefore = await assertBalances({ + pendingDepositAmount: 0, + wethAmount: 0, + ethAmount: 0, + balancesProof: testBalancesProofs[1], + activeValidators: [2], + }); + }); + it("Should account for full withdrawal", async () => { + const { + compoundingStakingSSVStrategy, + compoundingStakingStrategyView, + validatorRegistrator, + } = fixture; + + // Validator has 1588.918094377 ETH + const withdrawalAmount = testBalancesProofs[1].validatorBalances[2]; + + // Stake before balance are verified + const activeValidatorsBefore = + await compoundingStakingStrategyView.getVerifiedValidators(); + expect(activeValidatorsBefore.length).to.eq(1); + expect( + ( + await compoundingStakingSSVStrategy.validator( + testValidators[3].publicKeyHash + ) + ).state + ).to.equal(4); // ACTIVE + + // fund 1 WEI for the withdrawal request + await setBalance(compoundingStakingSSVStrategy.address, "0x1"); + const tx = await compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .validatorWithdrawal( + testValidators[3].publicKey, + parseUnits(withdrawalAmount.toString(), 9) + ); + + await expect(tx) + .to.emit(compoundingStakingSSVStrategy, "ValidatorWithdraw") + .withArgs( + testValidators[3].publicKeyHash, + parseEther(withdrawalAmount.toString()) + ); + + const balancesAfter = await assertBalances({ + pendingDepositAmount: 0, + wethAmount: 0, + ethAmount: withdrawalAmount, + balancesProof: testBalancesProofs[2], + activeValidators: [2], + }); + + // Check state after the balances are verified + expect(balancesAfter.stratBalance).to.equal( + balancesBefore.stratBalance + ); + const activeValidatorsAfter = + await compoundingStakingStrategyView.getVerifiedValidators(); + expect(activeValidatorsAfter.length).to.eq(0); + expect( + ( + await compoundingStakingSSVStrategy.validator( + testValidators[3].publicKeyHash + ) + ).state + ).to.equal(6); // EXITED + }); + }); + }); + describe("When WETH, ETH, no pending deposits and 2 active validators", () => { + let balancesBefore; + beforeEach(async () => { + // register, stake, verify validator and verify deposit + await processValidator(testValidators[0], "VERIFIED_DEPOSIT"); + await topUpValidator( + testValidators[0], + testValidators[0].depositProof.depositAmount - 1, + "VERIFIED_DEPOSIT" + ); + + await processValidator(testValidators[1], "VERIFIED_DEPOSIT"); + await topUpValidator( + testValidators[1], + testValidators[1].depositProof.depositAmount - 1, + "VERIFIED_DEPOSIT" + ); + + balancesBefore = await assertBalances({ + pendingDepositAmount: 0, + wethAmount: 10, + ethAmount: 0.987, + balancesProof: testBalancesProofs[3], + activeValidators: [0, 1], + }); + }); + it("consensus rewards are earned by the validators", async () => { + const balancesAfter = await assertBalances({ + pendingDepositAmount: 0, + wethAmount: 10, + ethAmount: 0.987, + balancesProof: testBalancesProofs[4], + activeValidators: [0, 1], + }); + + // Check the increase in consensus rewards + const consensusRewards = parseEther("0.007672545"); + expect(balancesAfter.totalValidatorBalance).to.equal( + balancesBefore.totalValidatorBalance.add(consensusRewards) + ); + expect(balancesAfter.totalBalance).to.equal( + balancesBefore.totalBalance.add(consensusRewards) + ); + }); + it("execution rewards are earned as ETH in the strategy", async () => { + const balancesAfter = await assertBalances({ + pendingDepositAmount: 0, + wethAmount: 10, + ethAmount: 1, + balancesProof: testBalancesProofs[3], + activeValidators: [0, 1], + }); + + // Check the increase in execution rewards + const executionRewards = parseEther("0.013"); + expect(balancesAfter.ethBalance).to.equal( + balancesBefore.ethBalance.add(executionRewards) + ); + expect(balancesAfter.totalBalance).to.equal( + balancesBefore.totalBalance.add(executionRewards) + ); + }); + describe("when balances have been snapped", () => { + let balancesProof; + beforeEach(async () => { + balancesProof = testBalancesProofs[3]; + await snapBalances(balancesProof.blockRoot); + }); + it("Fail to verify balances with not enough validator leaves", async () => { + const { compoundingStakingSSVStrategy } = fixture; + + // Verify balances with pending deposits and active validators + const tx = compoundingStakingSSVStrategy.verifyBalances( + { + ...balancesProof.balanceProofs, + // Only one when there is three active validators + validatorBalanceLeaves: [ + balancesProof.balanceProofs.validatorBalanceLeaves[0], + ], + validatorBalanceProofs: [ + balancesProof.balanceProofs.validatorBalanceProofs[0], + balancesProof.balanceProofs.validatorBalanceProofs[1], + ], + }, + emptyPendingDepositProofs + ); + + await expect(tx).to.be.revertedWith("Invalid balance leaves"); + }); + it("Fail to verify balances with too many validator leaves", async () => { + const { compoundingStakingSSVStrategy } = fixture; + + // Verify balances with pending deposits and active validators + const tx = compoundingStakingSSVStrategy.verifyBalances( + { + ...balancesProof.balanceProofs, + // Three when there is two active validators + validatorBalanceLeaves: + balancesProof.balanceProofs.validatorBalanceLeaves, + validatorBalanceProofs: [ + balancesProof.balanceProofs.validatorBalanceProofs[0], + balancesProof.balanceProofs.validatorBalanceProofs[1], + ], + }, + emptyPendingDepositProofs + ); + + await expect(tx).to.be.revertedWith("Invalid balance leaves"); + }); + it("Fail to verify balances with not enough validator proofs", async () => { + const { compoundingStakingSSVStrategy } = fixture; + + // Verify balances with pending deposits and active validators + const tx = compoundingStakingSSVStrategy.verifyBalances( + { + ...balancesProof.balanceProofs, + validatorBalanceLeaves: [ + balancesProof.balanceProofs.validatorBalanceLeaves[0], + balancesProof.balanceProofs.validatorBalanceLeaves[1], + ], + // Only one when there is two active validators + validatorBalanceProofs: [ + balancesProof.balanceProofs.validatorBalanceProofs[0], + ], + }, + emptyPendingDepositProofs + ); + + await expect(tx).to.be.revertedWith("Invalid balance proofs"); + }); + it("Fail to verify balances with too many proofs", async () => { + const { compoundingStakingSSVStrategy } = fixture; + + // Verify balances with pending deposits and active validators + const tx = compoundingStakingSSVStrategy.verifyBalances( + { + ...balancesProof.balanceProofs, + validatorBalanceLeaves: [ + balancesProof.balanceProofs.validatorBalanceLeaves[0], + balancesProof.balanceProofs.validatorBalanceLeaves[1], + ], + // Three when there is two active validators + validatorBalanceProofs: + balancesProof.balanceProofs.validatorBalanceProofs, + }, + emptyPendingDepositProofs + ); + + await expect(tx).to.be.revertedWith("Invalid balance proofs"); + }); + }); + }); + describe("With 21 active validators", () => { + const testValidatorCount = 21; + const testValidatorProofs = [...Array(testValidatorCount).keys()]; + beforeEach(async () => { + // register, stake, verify validator and verify deposit + for (let i = 0; i < testValidatorCount; i++) { + log( + `Processing testValidators[${i}] with index ${testValidators[i].index}` + ); + expect(hashPubKey(testValidators[i].publicKey)).to.equal( + testValidators[i].publicKeyHash, + `testValidators[${i}] public key hash mismatch with validator index ${testValidators[i].index}` + ); + await processValidator(testValidators[i], "VERIFIED_DEPOSIT"); + // Top up the validator to ensure it has enough balance + await topUpValidator( + testValidators[i], + testValidators[i].depositProof.depositAmount - 1, + "VERIFIED_DEPOSIT" + ); + } + }); + it("Should verify balances with some WETH, ETH and no deposits", async () => { + const { compoundingStakingStrategyView } = fixture; + + const activeValidators = + await compoundingStakingStrategyView.getVerifiedValidators(); + log( + `Active validators: ${activeValidators.map((v) => v.index).join(",")}` + ); + + await assertBalances({ + pendingDepositAmount: 0, + wethAmount: 123.456, + ethAmount: 0.345, + balancesProof: testBalancesProofs[5], + activeValidators: testValidatorProofs, + }); + }); + it("Should verify balances with one validator exited with two pending deposits", async () => { + // Add two deposits to the fourth validator (index 3) that has a zero balance + // These deposits should be deleted + await topUpValidator(testValidators[3], 1, "STAKED"); + await topUpValidator(testValidators[3], 2, "STAKED"); + + await assertBalances({ + pendingDepositAmount: 3, + wethAmount: 123.456, + ethAmount: 0.345, + balancesProof: testBalancesProofs[5], + activeValidators: testValidatorProofs, + }); + }); + it("Should verify balances with one validator exited with two pending deposits and three deposits to non-exiting validators", async () => { + // Add two deposits to the first validator (index 0) that has a balance + // These deposits should be kept + await topUpValidator(testValidators[0], 2, "STAKED"); + await topUpValidator(testValidators[0], 3, "STAKED"); + // Add another deposit to the second validator (index 1) that has a balance + await topUpValidator(testValidators[1], 4, "STAKED"); + + // Add two deposits to the fourth validator (index 3) that has a zero balance + await topUpValidator(testValidators[3], 5, "STAKED"); + await topUpValidator(testValidators[3], 6, "STAKED"); + + await assertBalances({ + pendingDepositAmount: 20, // 2 + 3 + 4 + 5 + 6 + wethAmount: 123.456, + ethAmount: 0.345, + balancesProof: testBalancesProofs[5], + activeValidators: testValidatorProofs, + }); + }); + }); + }); + + describe("Compounding SSV Staking Strategy Mocked proofs", function () { + beforeEach(async () => { + fixture = await loadFixtureMockedProofs(); + const { compoundingStakingSSVStrategy, josh, weth } = fixture; + sGov = await impersonateAndFund( + await compoundingStakingSSVStrategy.governor() + ); + sVault = await impersonateAndFund( + await compoundingStakingSSVStrategy.vaultAddress() + ); + await weth + .connect(josh) + .approve(compoundingStakingSSVStrategy.address, MAX_UINT256); + }); + + it("Should be allowed 2 deposits to an exiting validator ", async () => { + const { + compoundingStakingSSVStrategy, + compoundingStakingStrategyView, + mockBeaconProof, + } = fixture; + // when modifying a json object make a copy + const testValidator = JSON.parse(JSON.stringify(testValidators[3])); + + // Third validator is later withdrawn later + await processValidator(testValidator, "VERIFIED_DEPOSIT"); + + await topUpValidator( + testValidator, + testValidator.depositProof.depositAmount - 1, + "STAKED" + ); + const { depositSlot, pendingDepositRoot: pendingDepositRoot1 } = + await getLastDeposit(compoundingStakingSSVStrategy); + + await topUpValidator( + testValidator, + testValidator.depositProof.depositAmount - 2, + "STAKED" + ); + const { pendingDepositRoot: pendingDepositRoot2 } = await getLastDeposit( + compoundingStakingSSVStrategy + ); + + const lastBlock = await ethers.provider.getBlock("latest"); + const epochTime = 12 * 32; + + // 2 epochs from now + const nextNextEpoch = calcEpoch( + lastBlock.timestamp + epochTime * 2 + ).toString(); + + // simulate validator slashed from the beacon chain + testValidator.depositProof.strategyValidator.withdrawableEpoch = + nextNextEpoch; + + await compoundingStakingSSVStrategy.verifyDeposit( + pendingDepositRoot1, + depositSlot + 1000n, + testValidator.depositProof.firstPendingDeposit, + testValidator.depositProof.strategyValidator + ); + + await compoundingStakingSSVStrategy.verifyDeposit( + pendingDepositRoot2, + depositSlot + 1000n, + testValidator.depositProof.firstPendingDeposit, + testValidator.depositProof.strategyValidator + ); + + let depositData1 = await compoundingStakingSSVStrategy.deposits( + pendingDepositRoot1 + ); + let depositData2 = await compoundingStakingSSVStrategy.deposits( + pendingDepositRoot2 + ); + + await expect(depositData1.status).to.equal(2); // VERIFIED + await expect(depositData2.status).to.equal(2); // VERIFIED + + await advanceTime(epochTime * 4); + + // simulate validator has exited and been swept by the beacon chain sweeping process + await mockBeaconProof.setValidatorBalance(testValidator.index, 0); + + await assertBalances({ + pendingDepositAmount: 0, + wethAmount: 0, + ethAmount: 0, + balancesProof: { + balanceProofs: emptyOneBalanceProofs, + pendingDepositProofsData: emptyTwoPendingDepositProofs, + validatorBalances: [], + }, + activeValidators: [0], + hackDeposits: false, + }); + + // verify that the deposits have been removed as the validator has simulated + // to been fully exited + const deposits = + await compoundingStakingStrategyView.getPendingDeposits(); + expect(deposits.length).to.equal(0); + + depositData1 = await compoundingStakingSSVStrategy.deposits( + pendingDepositRoot1 + ); + depositData2 = await compoundingStakingSSVStrategy.deposits( + pendingDepositRoot2 + ); + + // Verify that the deposits have been marked as VERIFIED as they + // were removed + await expect(depositData1.status).to.equal(2); // VERIFIED + await expect(depositData2.status).to.equal(2); // VERIFIED + }); + + it("Should verify validator that has a front-run deposit", async () => { + const { compoundingStakingSSVStrategy, compoundingStakingStrategyView } = + fixture; + + // Third validator is later withdrawn later + const testValidator = testValidators[3]; + + await processValidator(testValidator, "STAKED"); + const { pendingDepositRoot } = await getLastDeposit( + compoundingStakingSSVStrategy + ); + + const lastVerifiedEthBalanceBefore = + await compoundingStakingSSVStrategy.lastVerifiedEthBalance(); + + // Verify the the invalid validator + const attackerAddress = Wallet.createRandom().address; + + const tx = await compoundingStakingSSVStrategy.verifyValidator( + testValidator.validatorProof.nextBlockTimestamp, + testValidator.index, + testValidator.publicKeyHash, + getWithdrawalCredentials("0x02", attackerAddress), + "0x" // empty proof as it is not verified in the mock + ); + + await expect(tx) + .to.emit(compoundingStakingSSVStrategy, "ValidatorInvalid") + .withArgs(testValidator.publicKeyHash); + + // Validator is invalid + const { state: validatorStateAfter } = + await compoundingStakingSSVStrategy.validator( + testValidator.publicKeyHash + ); + expect(validatorStateAfter).to.equal(8); // INVALID + + // There are no pending deposits + const pendingDeposits = + await compoundingStakingStrategyView.getPendingDeposits(); + expect(pendingDeposits).to.have.lengthOf(0); + + // The deposit status is VERIFIED + const depositData = await compoundingStakingSSVStrategy.deposits( + pendingDepositRoot + ); + expect(depositData.status).to.equal(2); // VERIFIED + + // The last verified ETH balance is reduced by the 1 ETH deposit + expect( + await compoundingStakingSSVStrategy.lastVerifiedEthBalance() + ).to.equal(lastVerifiedEthBalanceBefore.sub(parseEther("1"))); + + // The first deposit flag is still set + expect(await compoundingStakingSSVStrategy.firstDeposit()).to.equal(true); + }); + + it("Should verify validator with incorrect type", async () => { + const { compoundingStakingSSVStrategy } = fixture; + + // Third validator is later withdrawn later + const testValidator = testValidators[3]; + + await processValidator(testValidator, "STAKED"); + + const tx = await compoundingStakingSSVStrategy.verifyValidator( + testValidator.validatorProof.nextBlockTimestamp, + testValidator.index, + testValidator.publicKeyHash, + getWithdrawalCredentials("0x01", compoundingStakingSSVStrategy.address), + "0x" // empty proof as it is not verified in the mock + ); + + await expect(tx) + .to.emit(compoundingStakingSSVStrategy, "ValidatorInvalid") + .withArgs(testValidator.publicKeyHash); + + // Validator is invalid + const { state: validatorStateAfter } = + await compoundingStakingSSVStrategy.validator( + testValidator.publicKeyHash + ); + expect(validatorStateAfter).to.equal(8); // INVALID + }); + + it("Should verify validator with malformed credentials", async () => { + const { compoundingStakingSSVStrategy } = fixture; + + // Third validator is later withdrawn later + const testValidator = testValidators[3]; + + await processValidator(testValidator, "STAKED"); + + const malformedCredentials = + "0x020000000bafa00000000000" + + compoundingStakingSSVStrategy.address.slice(2); + + const tx = await compoundingStakingSSVStrategy.verifyValidator( + testValidator.validatorProof.nextBlockTimestamp, + testValidator.index, + testValidator.publicKeyHash, + malformedCredentials, + "0x" // empty proof as it is not verified in the mock + ); + + await expect(tx) + .to.emit(compoundingStakingSSVStrategy, "ValidatorInvalid") + .withArgs(testValidator.publicKeyHash); + + // Validator is invalid + const { state: validatorStateAfter } = + await compoundingStakingSSVStrategy.validator( + testValidator.publicKeyHash + ); + expect(validatorStateAfter).to.equal(8); // INVALID + }); + + it("Should fail to verify front-run deposit", async () => { + const { compoundingStakingSSVStrategy } = fixture; + + // Third validator is later withdrawn later + const testValidator = testValidators[3]; + + await processValidator(testValidator, "STAKED"); + const { pendingDepositRoot } = await getLastDeposit( + compoundingStakingSSVStrategy + ); + + expect(await compoundingStakingSSVStrategy.firstDeposit()).to.equal(true); + + await compoundingStakingSSVStrategy.verifyValidator( + testValidator.validatorProof.nextBlockTimestamp, + testValidator.index, + testValidator.publicKeyHash, + getWithdrawalCredentials("0x02", Wallet.createRandom().address), + "0x" // empty proof as it is not verified in the mock + ); + + const currentBlock = await ethers.provider.getBlock(); + const depositSlot = calcSlot(currentBlock.timestamp); + // Set parent beacon root for the block after the verification slots + const depositProcessedSlot = depositSlot + 100n; + + const tx = compoundingStakingSSVStrategy.verifyDeposit( + pendingDepositRoot, + depositProcessedSlot, + testValidator.depositProof.firstPendingDeposit, + testValidator.depositProof.strategyValidator + ); + await expect(tx).to.be.revertedWith("Deposit not pending"); + }); + + it("Governor should reset first deposit after front-run deposit", async () => { + const { compoundingStakingSSVStrategy, governor } = fixture; + + // Third validator is later withdrawn later + const testValidator = testValidators[3]; + + await processValidator(testValidator, "STAKED"); + + expect(await compoundingStakingSSVStrategy.firstDeposit()).to.equal(true); + + await compoundingStakingSSVStrategy.verifyValidator( + testValidator.validatorProof.nextBlockTimestamp, + testValidator.index, + testValidator.publicKeyHash, + getWithdrawalCredentials("0x02", Wallet.createRandom().address), + "0x" // empty proof as it is not verified in the mock + ); + + const tx = await compoundingStakingSSVStrategy + .connect(governor) + .resetFirstDeposit(); + + await expect(tx).to.emit( + compoundingStakingSSVStrategy, + "FirstDepositReset" + ); + + expect(await compoundingStakingSSVStrategy.firstDeposit()).to.equal( + false + ); + }); + + it("Should remove a validator from SSV cluster when validator is invalid", async () => { + const { compoundingStakingSSVStrategy, validatorRegistrator } = fixture; + + // Third validator is later withdrawn later + const testValidator = testValidators[3]; + + await processValidator(testValidator, "STAKED"); + + expect(await compoundingStakingSSVStrategy.firstDeposit()).to.equal(true); + + await compoundingStakingSSVStrategy.verifyValidator( + testValidator.validatorProof.nextBlockTimestamp, + testValidator.index, + testValidator.publicKeyHash, + getWithdrawalCredentials("0x02", Wallet.createRandom().address), + "0x" // empty proof as it is not verified in the mock + ); + + const tx = await compoundingStakingSSVStrategy + .connect(validatorRegistrator) + .removeSsvValidator( + testValidator.publicKey, + testValidator.operatorIds, + emptyCluster + ); + + await expect(tx) + .to.emit(compoundingStakingSSVStrategy, "SSVValidatorRemoved") + .withArgs(testValidator.publicKeyHash, testValidator.operatorIds); + }); + + it("Should fail to active a validator with a 32.25 ETH balance", async () => { + const { compoundingStakingSSVStrategy, mockBeaconProof } = fixture; + + // Third validator is later withdrawn later + const testValidator = testValidators[3]; + + await processValidator(testValidator, "VERIFIED_DEPOSIT"); + await topUpValidator(testValidator, 31, "VERIFIED_DEPOSIT"); + + const validatorBefore = await compoundingStakingSSVStrategy.validator( + testValidator.publicKeyHash + ); + expect(validatorBefore.state).to.equal(3); // VERIFIED + + await compoundingStakingSSVStrategy.snapBalances(); + + // Set validator balance to 32.25 Gwei + await mockBeaconProof.setValidatorBalance( + testValidator.index, + parseUnits("32.25", 9) + ); + + const tx = await compoundingStakingSSVStrategy.verifyBalances( + emptyOneBalanceProofs, + emptyPendingDepositProofs + ); + + await expect(tx) + .to.emit(compoundingStakingSSVStrategy, "BalancesVerified") + .withNamedArgs({ + totalDepositsWei: 0, + }); + + // The validator should still be VERIFIED, not ACTIVE + const validatorAfter = await compoundingStakingSSVStrategy.validator( + testValidator.publicKeyHash + ); + expect(validatorAfter.state).to.equal(3); // VERIFIED + }); + + it("Should active a validator with more than 32.25 ETH balance", async () => { + const { compoundingStakingSSVStrategy, mockBeaconProof } = fixture; + + // Third validator is later withdrawn later + const testValidator = testValidators[3]; + + await processValidator(testValidator, "VERIFIED_DEPOSIT"); + await topUpValidator(testValidator, 31, "VERIFIED_DEPOSIT"); + + const validatorBefore = await compoundingStakingSSVStrategy.validator( + testValidator.publicKeyHash + ); + expect(validatorBefore.state).to.equal(3); // VERIFIED + + await compoundingStakingSSVStrategy.snapBalances(); + + // Set validator balance to 32.26 Gwei + await mockBeaconProof.setValidatorBalance( + testValidator.index, + parseUnits("32.26", 9) + ); + + const tx = await compoundingStakingSSVStrategy.verifyBalances( + emptyOneBalanceProofs, + emptyPendingDepositProofs + ); + + await expect(tx) + .to.emit(compoundingStakingSSVStrategy, "BalancesVerified") + .withNamedArgs({ + totalDepositsWei: 0, + }); + + // The validator should still be ACTIVE + const validatorAfter = await compoundingStakingSSVStrategy.validator( + testValidator.publicKeyHash + ); + expect(validatorAfter.state).to.equal(4); // ACTIVE + }); + + describe("When a verified validator is exiting after being slashed And a new deposit is made to the validator", () => { + const depositAmount = 3; + // Validator is later withdrawn later + const testValidator = testValidators[11]; + let lastDeposit; + let withdrawableEpoch, withdrawableSlot, withdrawableTimestamp; + let strategyValidatorData; + beforeEach(async () => { + const { compoundingStakingSSVStrategy } = fixture; + + await processValidator(testValidator, "VERIFIED_DEPOSIT"); + await topUpValidator( + testValidator, + testValidator.depositProof.depositAmount - 1, + "VERIFIED_DEPOSIT" + ); + + await topUpValidator(testValidator, depositAmount, "STAKED"); + + lastDeposit = await getLastDeposit(compoundingStakingSSVStrategy); + + // Withdrawable epoch is 4 epochs from the current block + const currentBlock = await ethers.provider.getBlock(); + withdrawableEpoch = calcEpoch(currentBlock.timestamp) + 4n; + withdrawableSlot = withdrawableEpoch * 32n; + withdrawableTimestamp = Number(calcBlockTimestamp(withdrawableSlot)); + + strategyValidatorData = { + ...testValidator.depositProof.strategyValidator, + withdrawableEpoch, + }; + }); + it("Should fail verify deposit when first pending deposit slot before the withdrawable epoch", async () => { + const { compoundingStakingSSVStrategy } = fixture; + + const firstPendingDepositSlot = withdrawableSlot - 1n; + const depositProcessedSlot = withdrawableSlot; + const firstPendingDeposit = { + ...testValidator.depositProof.firstPendingDeposit, + slot: firstPendingDepositSlot, + }; + + const tx = compoundingStakingSSVStrategy.verifyDeposit( + lastDeposit.pendingDepositRoot, + depositProcessedSlot, + firstPendingDeposit, + strategyValidatorData + ); + + await expect(tx).to.be.revertedWith("Exit Deposit likely not proc."); + }); + it("Should verify deposit when the pending deposit queue is empty", async () => { + const { + compoundingStakingSSVStrategy, + compoundingStakingStrategyView, + } = fixture; + + const depositProcessedSlot = withdrawableSlot; + const firstPendingDeposit = { + ...emptyPendingDepositsProof, + slot: 1, + }; + + const tx = await compoundingStakingSSVStrategy.verifyDeposit( + lastDeposit.pendingDepositRoot, + depositProcessedSlot, + firstPendingDeposit, + strategyValidatorData + ); + + await expect(tx) + .to.emit(compoundingStakingSSVStrategy, "DepositVerified") + .withArgs( + lastDeposit.pendingDepositRoot, + parseEther(depositAmount.toString()) + ); + + // The deposit is verified + const depositAfter = await compoundingStakingSSVStrategy.deposits( + lastDeposit.pendingDepositRoot + ); + expect(depositAfter.status).to.equal(2); // VERIFIED + + // No pending deposits + expect(await compoundingStakingStrategyView.getPendingDeposits()).to.be + .empty; + }); + it("Should verify deposit when the first pending deposit slot equals the withdrawable epoch", async () => { + const { + compoundingStakingSSVStrategy, + compoundingStakingStrategyView, + } = fixture; + + const firstPendingDepositSlot = withdrawableSlot; + const depositProcessedSlot = withdrawableSlot; + const firstPendingDeposit = { + ...testValidator.depositProof.firstPendingDeposit, + slot: firstPendingDepositSlot, + }; + + const tx = await compoundingStakingSSVStrategy.verifyDeposit( + lastDeposit.pendingDepositRoot, + depositProcessedSlot, + firstPendingDeposit, + strategyValidatorData + ); + + await expect(tx) + .to.emit(compoundingStakingSSVStrategy, "DepositVerified") + .withArgs( + lastDeposit.pendingDepositRoot, + parseEther(depositAmount.toString()) + ); + + // The deposit is verified + const depositAfter = await compoundingStakingSSVStrategy.deposits( + lastDeposit.pendingDepositRoot + ); + expect(depositAfter.status).to.equal(2); // VERIFIED + + // No pending deposits + expect(await compoundingStakingStrategyView.getPendingDeposits()).to.be + .empty; + }); + it("Should verify deposit when the first pending deposit slot is after the withdrawable epoch", async () => { + const { + compoundingStakingSSVStrategy, + compoundingStakingStrategyView, + } = fixture; + + const firstPendingDepositSlot = withdrawableSlot + 1n; + const depositProcessedSlot = firstPendingDepositSlot + 5n; + const firstPendingDeposit = { + ...testValidator.depositProof.firstPendingDeposit, + slot: firstPendingDepositSlot, + }; + + const tx = await compoundingStakingSSVStrategy.verifyDeposit( + lastDeposit.pendingDepositRoot, + depositProcessedSlot, + firstPendingDeposit, + strategyValidatorData + ); + + await expect(tx) + .to.emit(compoundingStakingSSVStrategy, "DepositVerified") + .withArgs( + lastDeposit.pendingDepositRoot, + parseEther(depositAmount.toString()) + ); + + // The deposit is verified + const depositAfter = await compoundingStakingSSVStrategy.deposits( + lastDeposit.pendingDepositRoot + ); + expect(depositAfter.status).to.equal(2); // VERIFIED + + // No pending deposits + expect(await compoundingStakingStrategyView.getPendingDeposits()).to.be + .empty; + }); + + describe("When deposit has been verified to an exiting validator", () => { + beforeEach(async () => { + const { compoundingStakingSSVStrategy } = fixture; + + const firstPendingDepositSlot = withdrawableSlot + 1n; + const depositProcessedSlot = firstPendingDepositSlot + 5n; + + await compoundingStakingSSVStrategy.verifyDeposit( + lastDeposit.pendingDepositRoot, + depositProcessedSlot, + testValidator.depositProof.firstPendingDeposit, + strategyValidatorData + ); + }); + + it("Should verify balances", async () => { + const { compoundingStakingSSVStrategy, mockBeaconProof } = fixture; + + const { timestamp: currentTimestamp } = + await ethers.provider.getBlock(); + + // Advance the EVM time to after the withdrawable timestamp + await advanceTime(withdrawableTimestamp - currentTimestamp + 12); + + const { timestamp: advancedTimestamp } = + await ethers.provider.getBlock(); + + expect(advancedTimestamp).to.greaterThan(withdrawableTimestamp); + + await compoundingStakingSSVStrategy.snapBalances(); + + // Set the validator balance to zero + await mockBeaconProof.setValidatorBalance( + testValidator.index, + MAX_UINT256 + ); + + const tx = await compoundingStakingSSVStrategy.verifyBalances( + emptyOneBalanceProofs, + emptyOnePendingDepositProofs + ); + + await expect(tx) + .to.emit(compoundingStakingSSVStrategy, "BalancesVerified") + .withNamedArgs({ + totalDepositsWei: 0, + }); + }); + }); + }); + }); + + /* + it("Deposit alternate deposit_data_root ", async () => { + const { depositContractUtils } = fixture; + + const withdrawalCredentials = solidityPack( + ["bytes1", "bytes11", "address"], + [ + "0x01", + "0x0000000000000000000000", + // mainnet Native Staking Strategy proxy + "0x34edb2ee25751ee67f68a45813b22811687c0238", + ] + ); + expect(withdrawalCredentials).to.equal( + "0x01000000000000000000000034edb2ee25751ee67f68a45813b22811687c0238" + ); + + const expectedDepositDataRoot = + await depositContractUtils.calculateDepositDataRoot( + // Mainnet fork test public key + "0xaba6acd335d524a89fb89b9977584afdb23f34a6742547fa9ec1c656fbd2bfc0e7a234460328c2731828c9a43be06e25", + withdrawalCredentials, + // Mainnet fork test signature + "0x90157a1c1b26384f0b4d41bec867d1a000f75e7b634ac7c4c6d8dfc0b0eaeb73bcc99586333d42df98c6b0a8c5ef0d8d071c68991afcd8fbbaa8b423e3632ee4fe0782bc03178a30a8bc6261f64f84a6c833fb96a0f29de1c34ede42c4a859b0" + ); + + expect( + "0xf7d704e25a2b5bea06fafa2dfe5c6fa906816e5c1622400339b2088a11d5f446" + ).to.equal(expectedDepositDataRoot, "Incorrect deposit data root"); + }); + */ +}); diff --git a/contracts/test/strategies/nativeSsvStaking.mainnet.fork-test.js b/contracts/test/strategies/nativeSsvStaking.mainnet.fork-test.js index 07addc1e97..c0935adfdf 100644 --- a/contracts/test/strategies/nativeSsvStaking.mainnet.fork-test.js +++ b/contracts/test/strategies/nativeSsvStaking.mainnet.fork-test.js @@ -84,6 +84,14 @@ describe("ForkTest: Second Native SSV Staking Strategy", function () { depositDataRoot: "0x6f9cc503009ceb0960637bbf2482b19a62153144ab091f0b9f66d5800f02cc2c", }, + activeValidators: { + publicKeys: [ + "0x80555037820e8afe44a45ed6d43fd4184f14f2ebcac2beaad382ed7e3dac52f87241d5ed8683a8101d8f49b0dbb6bc0e", + "0xb9588334499ee49ac5a1472424e968fe589362b419d9ed09a172f635727466780e11553fbfef1166de3438e9495d7257", + "0xaff707ce005f75d9c8b6b20a170c314219f6596e2fa043c99de0a50565d79155c4ce1ecf98742cd3fdbb878c3762e78b", + ], + operatorIds: [752, 753, 754, 755], + }, }; }); }); @@ -125,6 +133,14 @@ describe("ForkTest: Third Native SSV Staking Strategy", function () { depositDataRoot: "0x3b8409ac7e028e595ac84735da6c19cdbc50931e5d6f910338614ea9660b5c86", }, + activeValidators: { + publicKeys: [ + "0x81ff6c3dab37f0dd49856687bad2ca5406229223e2ac7efcf409aff7b8b55e0f21563adf6a6c23bbe3a8afe8c599ad23", + "0x8b7bdc261a7cbf96cbdfedd0250cef5de212ec497b7100a9a95ca030791e2080bf639ed6e1c8ebed152b17d101fe20cf", + "0xb8ee2f3a4520d9f96988ea57f216b16dd87ecf6e49a8ceb8d0b21a82fb420928c9e3a4b06e73f3ab55a50d61acc39906", + ], + operatorIds: [338, 339, 340, 341], + }, }; }); }); diff --git a/contracts/utils/addresses.js b/contracts/utils/addresses.js index 8769419c3e..85e641b0fd 100644 --- a/contracts/utils/addresses.js +++ b/contracts/utils/addresses.js @@ -184,6 +184,9 @@ addresses.mainnet.CurveOETHGauge = "0xd03BE91b1932715709e18021734fcB91BB431715"; addresses.mainnet.CVXETHRewardsPool = "0x24b65DC1cf053A8D96872c323d29e86ec43eB33A"; +addresses.mainnet.CompoundingStakingStrategyProxy = + "0x840081c97256d553A8F234D469D797B9535a3B49"; + // Votemarket - StakeDAO addresses.mainnet.CampaignRemoteManager = "0x53aD4Cd1F1e52DD02aa9FC4A8250A1b74F351CA2"; @@ -302,8 +305,15 @@ addresses.mainnet.curve.OETH_WETH.gauge = // SSV network addresses.mainnet.SSV = "0x9D65fF81a3c488d585bBfb0Bfe3c7707c7917f54"; addresses.mainnet.SSVNetwork = "0xDD9BC35aE942eF0cFa76930954a156B3fF30a4E1"; + +// Beacon chain contracts addresses.mainnet.beaconChainDepositContract = "0x00000000219ab540356cBB839Cbe05303d7705Fa"; +addresses.mainnet.mockBeaconRoots = + "0xC033785181372379dB2BF9dD32178a7FDf495AcD"; +addresses.mainnet.beaconRoots = "0x000f3df6d732807ef1319fb7b8bb8522d0beac02"; +addresses.mainnet.beaconChainWithdrawRequest = + "0x00000961Ef480Eb55e80D19ad83579A64c007002"; // Native Staking Strategy addresses.mainnet.NativeStakingSSVStrategyProxy = @@ -349,6 +359,13 @@ addresses.mainnet.passthrough.uniswap.OETH_OGN = addresses.mainnet.passthrough.uniswap.OETH_WETH = "0x216dEBBF25e5e67e6f5B2AD59c856Fc364478A6A"; +// General purpose execution to consensus layer communication +addresses.mainnet.toConsensus = {}; +addresses.mainnet.toConsensus.consolidation = + "0x0000BBdDc7CE488642fb579F8B00f3a590007251"; +addresses.mainnet.toConsensus.withdrawals = + "0x00000961Ef480Eb55e80D19ad83579A64c007002"; + // Arbitrum One addresses.arbitrumOne = {}; addresses.arbitrumOne.WOETHProxy = "0xD8724322f44E5c58D7A815F542036fb17DbbF839"; @@ -637,4 +654,13 @@ addresses.plume.admin = "0x92A19381444A001d62cE67BaFF066fA1111d7202"; addresses.plume.BridgedWOETHOracleFeed = "0x4915600Ed7d85De62011433eEf0BD5399f677e9b"; +addresses.hoodi = {}; +addresses.hoodi.WETH = "0x2387fD72C1DA19f6486B843F5da562679FbB4057"; +addresses.hoodi.SSV = "0x9F5d4Ec84fC4785788aB44F9de973cF34F7A038e"; +addresses.hoodi.SSVNetwork = "0x58410Bef803ECd7E63B23664C586A6DB72DAf59c"; +addresses.hoodi.beaconChainDepositContract = + "0x00000000219ab540356cBB839Cbe05303d7705Fa"; +addresses.hoodi.defenderRelayer = "0x419B6BdAE482f41b8B194515749F3A2Da26d583b"; +addresses.hoodi.mockBeaconRoots = "0xdCfcAE4A084AA843eE446f400B23aA7B6340484b"; + module.exports = addresses; diff --git a/contracts/utils/beacon.js b/contracts/utils/beacon.js new file mode 100644 index 0000000000..43a36c84fd --- /dev/null +++ b/contracts/utils/beacon.js @@ -0,0 +1,423 @@ +const fs = require("fs"); +const fetch = require("node-fetch"); +const ethers = require("ethers"); +const { createHash } = require("crypto"); +const { parseUnits } = require("ethers/lib/utils"); + +const { + beaconChainGenesisTimeMainnet, + beaconChainGenesisTimeHoodi, +} = require("./constants"); +const { getNetworkName } = require("./hardhat-helpers"); + +const log = require("./logger")("utils:beacon"); + +/// They following use Lodestar API calls + +const getValidatorBalance = async (pubkey) => { + const client = await configClient(); + + log(`Fetching validator details for ${pubkey} from the beacon node`); + const validatorRes = await client.beacon.getStateValidator({ + stateId: "head", + validatorId: pubkey, + }); + if (!validatorRes.ok) { + console.error(validatorRes); + throw Error( + `Failed to get validator details for ${pubkey}. Status ${validatorRes.status} ${validatorRes.statusText}` + ); + } + + const values = validatorRes.value(); + log(`Got balance ${values.balance} for validator ${values.index}`); + return values.balance; +}; + +/** + * Get the slot for a given block identifier. + * @param {string} [blockId=head] - is "head", slot number or the beacon block root. + */ +const getSlot = async (blockId = "head") => { + const client = await configClient(); + + // Get the latest beacon block data using Lodestar + log(`Fetching block header for blockId ${blockId} from the beacon node`); + const blockHeaderRes = await client.beacon.getBlockHeader({ + blockId, + }); + if (!blockHeaderRes.ok) { + console.error(blockHeaderRes); + throw Error( + `Failed to get block header for blockId ${blockId}. Status ${blockHeaderRes.status} ${blockHeaderRes.statusText}` + ); + } + + const slot = blockHeaderRes.value().header.message.slot; + log(`Got slot ${slot} for block id ${blockId}`); + + return slot; +}; + +const getBeaconBlockRoot = async (blockId = "head") => { + const client = await configClient(); + + log( + `Fetching beacon block root for block id ${blockId} from the beacon node` + ); + const blockHeaderRes = await client.beacon.getBlockRoot({ + blockId, + }); + if (!blockHeaderRes.ok) { + console.error(blockHeaderRes); + throw Error( + `Failed to get beacon block root for block id ${blockId}. Status ${blockHeaderRes.status} ${blockHeaderRes.statusText}` + ); + } + + const root = blockHeaderRes.root; + log(`Got beacon block root ${root} for block id ${blockId}`); + + return root; +}; + +/** + * Gets the full beacon chain data for a given slot, root or "head". + * @param {string|number} [slot=head] - The slot to get the beacon block for. Can be "head", a slot number or a beacon block root. + */ +const getBeaconBlock = async (slot = "head") => { + const client = await configClient(); + + const { ssz } = await import("@lodestar/types"); + const BeaconBlock = ssz.electra.BeaconBlock; + const BeaconState = ssz.electra.BeaconState; + + // Get the beacon block for the slot from the beacon node. + log(`Fetching block for slot ${slot} from the beacon node`); + const blockRes = await client.beacon.getBlockV2({ blockId: slot }); + if (!blockRes.ok) { + console.error(blockRes); + throw new Error( + `Failed to get beacon block for id ${slot}. It could be because the slot was missed or the provider URL does not support beacon chain API. Error: ${blockRes.status} ${blockRes.statusText}` + ); + } + + const blockView = BeaconBlock.toView(blockRes.value().message); + + // Read the state from a local file or fetch it from the beacon node. + let stateSsz; + const stateFilename = `./cache/state_${blockView.slot}.ssz`; + if (fs.existsSync(stateFilename)) { + log(`Loading state from file ${stateFilename}`); + stateSsz = fs.readFileSync(stateFilename); + } else { + log(`Fetching state for slot ${blockView.slot} from the beacon node`); + const stateRes = await client.debug.getStateV2( + { stateId: blockView.slot }, + "ssz" + ); + if (!stateRes.ok) { + console.error(stateRes); + throw new Error( + `Failed to get state for slot ${blockView.slot}. Probably because it was missed. Error: ${stateRes.status} ${stateRes.statusText}` + ); + } + + log(`Writing state to file ${stateFilename}`); + fs.writeFileSync(stateFilename, stateRes.ssz()); + stateSsz = stateRes.ssz(); + } + + const stateView = BeaconState.deserializeToView(stateSsz); + + const blockTree = blockView.tree.clone(); + const stateRootGIndex = blockView.type.getPropertyGindex("stateRoot"); + // Patching the tree by attaching the state in the `stateRoot` field of the block. + blockTree.setNode(stateRootGIndex, stateView.node); + + return { blockTree, blockView, stateView }; +}; + +const concatProof = (proof) => { + const witnessLength = proof.witnesses.length; + const witnessBytes = new Uint8Array(witnessLength * 32); + for (let i = 0; i < witnessLength; i++) { + witnessBytes.set(proof.witnesses[i], i * 32); + } + return witnessBytes; +}; + +const hashPubKey = (pubKey) => { + // Ensure pubKey is a hex string or Buffer + const pubKeyBytes = ethers.utils.arrayify(pubKey); + + // Create 16 bytes of zeros + const zeroBytes = ethers.utils.hexZeroPad("0x0", 16); + + // Concatenate pubKey and zero bytes + const concatenated = ethers.utils.concat([pubKeyBytes, zeroBytes]); + + // Compute SHA256 hash + return ethers.utils.sha256(concatenated); +}; + +/** + * Gets a Lodestar API client. + * @returns {Promise} - The Lodestar API client. + */ +const configClient = async () => { + // Get the latest slot from the beacon chain API + // Dynamically import the Lodestar API client as its an ESM module + const { getClient } = await import("@lodestar/api"); + const { config } = await import("@lodestar/config/default"); + + const baseUrl = process.env.BEACON_PROVIDER_URL; + + const client = await getClient({ baseUrl, timeoutMs: 60000 }, { config }); + + return client; +}; + +/// The following connect directly to the BeaconChain API +/// They could be replaced with Lodestar API calls in the future. + +const getValidator = async (pubkey) => { + const networkName = await getNetworkName(); + // some other beacon providers don't support fetching of the validator by pubkey + const beaconProvider = `https://${ + networkName == "hoodi" ? "hoodi." : "" + }beaconcha.in/api/v1/`; + + return await beaconchainRequest(`validator/${pubkey}`, beaconProvider); +}; + +const getValidators = async (pubkeys, beaconChainApiKey) => { + const encodedPubkeys = encodeURIComponent(pubkeys); + return await beaconchainRequest( + `validator/${encodedPubkeys}`, + beaconChainApiKey + ); +}; + +const getEpoch = async (epochId = "latest") => { + return await beaconchainRequest(`epoch/${epochId}`); +}; + +const beaconchainRequest = async (endpoint, overrideProvider) => { + const networkName = await getNetworkName(); + + const API_URL = + overrideProvider || + process.env.BEACON_PROVIDER_URL || + `https://${networkName == "hoodi" ? "hoodi." : ""}beaconcha.in/api/v1/`; + + const apikey = process.env.BEACONCHAIN_API_KEY; + const url = `${API_URL}${endpoint}`; + if (!apikey) { + throw new Error( + "Set BEACONCHAIN_API_KEY in order to be able to query the API" + ); + } + + const headers = { + Accept: "application/json", + "Content-Type": "application/json", + apikey, + }; + + log(`About to call Beacon API: ${url} `); + + const rawResponse = await fetch(url, { + method: "GET", + headers, + }); + + const response = await rawResponse.json(); + if (response.status != "OK") { + log(`Call to Beacon API failed: ${url}`); + log(`response: `, response); + throw new Error( + `Call to Beacon API failed. Error: ${JSON.stringify(response.status)}` + ); + } else { + log(`GET request to Beacon API succeeded. Response: `, response); + } + + return response.data; +}; + +const serializeUint64 = async (value) => { + const { ssz } = await import("@lodestar/types"); + + // Need to convert to little-endian Uint8Array + const slotLittleEndian = ssz.Slot.serialize(Number(value)); + // Pad to 32 bytes + const leafBuf = Buffer.concat([ + slotLittleEndian, + Buffer.alloc(32 - slotLittleEndian.length), + ]); + return "0x" + Buffer.from(leafBuf).toString("hex"); +}; + +/** + * Calculates the Merkle root (as hex string) from a leaf and flat Merkle proof. + * + * @param {string} leafHex - 0x-prefixed 32-byte hex string + * @param {string} proofHex - 0x-prefixed hex string containing N × 32-byte proof (concatenated) + * @param {bigint} gIndex - Generalized index of the leaf in the Merkle tree + * @returns {string} - 0x-prefixed hex string of the calculated Merkle root + */ +const calcBeaconBlockRoot = (leafHex, proofHex, gIndex) => { + const valueBytes = Buffer.from(leafHex.slice(2), "hex"); + const proofBytes = Buffer.from(proofHex.slice(2), "hex"); + + if (proofBytes.length % 32 !== 0) { + throw new Error("proofHex must be a multiple of 32 bytes"); + } + + const proofCount = proofBytes.length / 32; + let value = valueBytes; + let index = gIndex; + + for (let i = 0; i < proofCount; i++) { + const sibling = proofBytes.slice(i * 32, (i + 1) * 32); + const hasher = createHash("sha256"); + + if (index % 2n === 0n) { + hasher.update(value); + hasher.update(sibling); + } else { + hasher.update(sibling); + hasher.update(value); + } + + value = hasher.digest(); + index >>= 1n; + + if (index === 0n) throw new Error("proof has extra item"); + } + + if (index !== 1n) throw new Error("proof is missing items"); + + const rootHex = "0x" + value.toString("hex"); + + log( + `Calculated beacon block root: ${rootHex} from leaf: ${leafHex} and gindex: ${gIndex}` + ); + + return rootHex; +}; + +const calcBlockTimestamp = (slot, networkName = "mainnet") => { + const genesisTime = + networkName == "hoodi" + ? beaconChainGenesisTimeHoodi + : beaconChainGenesisTimeMainnet; + return 12n * BigInt(slot) + BigInt(genesisTime); +}; + +const calcSlot = (blockTimestamp, networkName = "mainnet") => { + const genesisTime = + networkName == "hoodi" + ? beaconChainGenesisTimeHoodi + : beaconChainGenesisTimeMainnet; + return (BigInt(blockTimestamp) - BigInt(genesisTime)) / 12n; +}; + +const calcEpoch = (blockTimestamp, networkName = "mainnet") => { + const slotsPerEpoch = 32n; + return calcSlot(blockTimestamp, networkName) / slotsPerEpoch; +}; + +// verifies the deposit signature so we can confirm P2P has not generated a faulty one +// and a the deposit message root. The latter should also be verified by the Beacon chain +// deposit contract +const verifyDepositSignatureAndMessageRoot = async ({ + pubkey, // validator public key with or without 0x + withdrawalCredentials, // withdrawal credentials with or without 0x + amount, // amount in eth units + signature, // signature without 0x + depositMessageRoot, // p2p supplied deposit message root with or without 0x + forkVersion, // fork version +}) => { + // Can not import via require since these packages support only ESM mode + const bls = await import("@chainsafe/bls"); + const { ssz } = await import("@lodestar/types/phase0"); + const { computeDomain, computeSigningRoot } = await import( + "@lodestar/state-transition" + ); + const { DOMAIN_DEPOSIT } = await import("@lodestar/params"); + const { fromHex } = await import("@lodestar/utils"); + + log("Validating BLS deposit message signature"); + log(`pubkey: ${pubkey}`); + log(`withdrawalCredentials: ${withdrawalCredentials}`); + log(`amount: ${amount}`); + log(`signature: ${signature}`); + log(`depositMessageRoot: ${depositMessageRoot}`); + log(`forkVersion: ${forkVersion}`); + + const amountGwei = parseUnits(amount.toString(), 9); + depositMessageRoot = depositMessageRoot.startsWith("0x") + ? depositMessageRoot.substring(2) + : depositMessageRoot; + + // Prepare the DepositMessage + const depositMessage = { + pubkey: fromHex(pubkey), + withdrawalCredentials: fromHex(withdrawalCredentials), + amount: amountGwei.toString(), + }; + + const domain = computeDomain( + DOMAIN_DEPOSIT, + fromHex(forkVersion), + new Uint8Array(32) + ); + + // Compute signing root + const signingRoot = computeSigningRoot( + ssz.DepositMessage, + depositMessage, + domain + ); + + if ( + !bls.default.verify(depositMessage.pubkey, signingRoot, fromHex(signature)) + ) { + throw Error(`BLS signature is invalid`); + } + + log(`BLS signature valid`); + + // Compare computed deposit_message_root with provided one for sanity check + const computedMessageRoot = ssz.DepositMessage.hashTreeRoot(depositMessage); + const computedMessageRootString = + Buffer.from(computedMessageRoot).toString("hex"); + if (depositMessageRoot != computedMessageRootString) { + throw Error( + `Deposit message root miss-match. Computed value: ${computedMessageRootString} vs supplied value: ${depositMessageRoot}` + ); + } + log( + `Deposit message root matches the computed message root: ${depositMessageRoot}` + ); +}; + +module.exports = { + concatProof, + getBeaconBlock, + getSlot, + getBeaconBlockRoot, + calcBlockTimestamp, + calcSlot, + calcEpoch, + getValidator, + getValidators, + getValidatorBalance, + getEpoch, + hashPubKey, + serializeUint64, + calcBeaconBlockRoot, + verifyDepositSignatureAndMessageRoot, +}; diff --git a/contracts/utils/constants.js b/contracts/utils/constants.js index a8b9386190..4ef3d5e9e2 100644 --- a/contracts/utils/constants.js +++ b/contracts/utils/constants.js @@ -3,6 +3,8 @@ const ethers = require("ethers"); const MAX_UINT256 = ethers.BigNumber.from( "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" ); +const MAX_UINT64 = ethers.BigNumber.from("0xffffffffffffffff"); +const ZERO_BYTES32 = ethers.utils.hexZeroPad("0x", 32); const ONE = ethers.utils.parseEther("1"); @@ -32,11 +34,19 @@ const ccip_arbChainSelector = "4949039107694359620"; const p2pApiEncodedKey = "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFVEcydGNwUWxHaEpQdlF3K2prZ1NmU2N3RjBDTgpUNEUva1ZXaWFWeVdLWkRFRXgvOWVWenNIc2FRQU5tbEJNV1pMbHhXQVhRWno2Qy9YQWN0bU56Y1BRPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg=="; +const beaconChainGenesisTimeMainnet = 1606824023; // Tue Dec 01 2020 12:00:23 GMT+0000 +const beaconChainGenesisTimeHoodi = 1742213400; // Mon Mar 17 2025 12:10:00 GMT+0000 + +const gIndexFirstPendingDepositPubKey = 1584842932224n; +const gIndexFirstPendingDepositSlot = 1584842932228n; + module.exports = { threeCRVPid, metapoolLPCRVPid, oethPoolLpPID, MAX_UINT256, + MAX_UINT64, + ZERO_BYTES32, aura_stETH_WETH_PID, balancer_stETH_WETH_PID, aura_wstETH_sfrxETH_rETH_PID, @@ -46,6 +56,10 @@ module.exports = { ccip_arbChainSelector, p2pApiEncodedKey, ONE, + beaconChainGenesisTimeMainnet, + beaconChainGenesisTimeHoodi, + gIndexFirstPendingDepositPubKey, + gIndexFirstPendingDepositSlot, }; // These are all the metapool ids. For easier future reference diff --git a/contracts/utils/funding.js b/contracts/utils/funding.js index 72212a5baf..9080e5ff99 100644 --- a/contracts/utils/funding.js +++ b/contracts/utils/funding.js @@ -34,7 +34,7 @@ const fundAccountsForOETHUnitTests = async () => { for (const address of signerAddresses) { const signer = await ethers.provider.getSigner(address); - await weth.connect(signer).mint(oethUnits("1000")); + await weth.connect(signer).mint(oethUnits("10000")); } }; diff --git a/contracts/utils/p2pValidatorCompound.js b/contracts/utils/p2pValidatorCompound.js new file mode 100644 index 0000000000..a82661fd5b --- /dev/null +++ b/contracts/utils/p2pValidatorCompound.js @@ -0,0 +1,143 @@ +const fetch = require("node-fetch"); +const { v4: uuidv4 } = require("uuid"); +const { getNetworkName } = require("./hardhat-helpers"); +const log = require("./logger")("task:validator:compounding"); + +const P2P_URL_MAINNET = "https://api.p2p.org"; +const P2P_URL_TESTNET = "https://api-test.p2p.org"; +const INITIAL_DEPOSIT_SIZE = "32000000000"; // 32 ETH + +const _p2pRequest = async (uri, method, body) => { + const networkName = await getNetworkName(); + let baseUrl, api_key; + + if (networkName == "mainnet") { + baseUrl = P2P_URL_MAINNET; + api_key = process.env.P2P_MAINNET_API_KEY; + } else if (networkName == "hoodi") { + baseUrl = P2P_URL_TESTNET; + api_key = process.env.P2P_HOODI_API_KEY; + } else { + throw new Error(`Unsupported network: ${networkName}`); + } + + const url = `${baseUrl}${uri}`; + const headers = { + Accept: "application/json", + Authorization: `Bearer ${api_key}`, + }; + + if (method === "POST") { + headers["Content-Type"] = "application/json"; + } + + const bodyString = JSON.stringify(body); + log( + `About to call P2P API: ${method} ${url} `, + body != undefined ? ` and body: ${bodyString}` : "" + ); + + const rawResponse = await fetch(url, { + method, + headers, + body: bodyString, + }); + + const response = await rawResponse.json(); + if (response.error != null) { + log(`Call to P2P API failed: ${method} ${url}`); + throw new Error( + `Failed to call to P2P API. Error: ${JSON.stringify(response.error)}` + ); + } else { + log(`${method} request to P2P API succeeded:`); + } + + return response; +}; + +const _getStakingContract = async () => { + return await ethers.getContract("CompoundingStakingSSVStrategyProxy"); +}; + +const getValidatorRequestStatus = async ({ uuid }) => { + log(`Fetching the p2p status of SSV validator create request: ${uuid}`); + + const response = await _p2pRequest( + `/api/v1/eth/staking/ssv/request/status/${uuid}`, + "GET" + ); + + if (response.result.status == "processing") { + throw new Error("The create request is still processing"); + } else if (response.result.status == "validator-ready") { + log(`Response`, response); + throw new Error(`Validator has already been registered on SSV.`); + } else if (response.result.status != "ready") { + log(`Response`, response); + throw new Error(`Unexpected request status: ${response.result.status}`); + } + + const validatorShare = response.result.encryptedShares[0]; + const operators = response.result.clusters[0].operators; + return { + pubkey: validatorShare.publicKey, + shares: validatorShare.sharesData, + operatorids: operators.join(","), + }; +}; + +const getValidatorRequestDepositData = async ({ uuid }) => { + log(`Fetching the p2p status of SSV validator deposit data request: ${uuid}`); + + const response = await _p2pRequest( + `/api/v1/eth/staking/ssv/request/deposit-data/${uuid}`, + "GET" + ); + + if (response.result.status != "validator-ready") { + log(`Response`, response); + throw new Error(`Unexpected validator status: ${response.result.status}`); + } + + const depositData = response.result.depositData[0]; + log(`DepositData: `, depositData); + + return { + pubkey: depositData.pubkey, + sig: depositData.signature, + amount: depositData.amount / 1e9, + withdrawalCredentials: depositData.withdrawalCredentials, + depositMessageRoot: depositData.depositMessageRoot, + forkVersion: depositData.forkVersion, + }; +}; + +const createValidatorRequest = async ({ + validatorSpawnOperationalPeriodInDays, +}) => { + const uuid = uuidv4(); + log(`About to create a SSV validator request with uuid: ${uuid}`); + + const stakingContractAddress = (await _getStakingContract()).address; + await _p2pRequest("/api/v1/eth/staking/ssv/request/create", "POST", { + id: uuid, + validatorsCount: 1, + amountPerValidator: INITIAL_DEPOSIT_SIZE, + withdrawalCredentialsType: "0x02", + withdrawalAddress: stakingContractAddress, + feeRecipientAddress: stakingContractAddress, + ssvOwnerAddress: stakingContractAddress, + type: "without-encrypt-key", + operationPeriodInDays: validatorSpawnOperationalPeriodInDays, + //ecdhPublicKey: "", + }); + + console.log(`Validator request created uuid: ${uuid}`); +}; + +module.exports = { + createValidatorRequest, + getValidatorRequestStatus, + getValidatorRequestDepositData, +}; diff --git a/contracts/utils/proofs.js b/contracts/utils/proofs.js new file mode 100644 index 0000000000..ff3823af59 --- /dev/null +++ b/contracts/utils/proofs.js @@ -0,0 +1,462 @@ +const { toHex } = require("../utils/units"); +const { concatProof, getValidator } = require("../utils/beacon"); +const { formatUnits } = require("ethers/lib/utils"); +const { MAX_UINT64 } = require("./constants"); + +const log = require("../utils/logger")("task:proof"); + +// BeaconBlock.state.PendingDeposits[0].slot +async function generateFirstPendingDepositSlotProof({ + blockView, + blockTree, + stateView, + test, +}) { + // Have to dynamically import the Lodestar API client as its an ESM module + const { concatGindices, createProof, ProofType, toGindex } = await import( + "@chainsafe/persistent-merkle-tree" + ); + + log(`There are ${stateView.pendingDeposits.length} pending deposits`); + const generalizedIndex = + stateView.pendingDeposits.length > 0 + ? concatGindices([ + blockView.type.getPathInfo(["stateRoot"]).gindex, + stateView.type.getPathInfo(["pendingDeposits", 0]).gindex, + toGindex(3, 4n), // depth 3, index 4 for slot = 12 + ]) + : concatGindices([ + blockView.type.getPathInfo(["stateRoot"]).gindex, + stateView.type.getPathInfo(["pendingDeposits", 0]).gindex, + ]); + log( + `Generalized index for the slot of the first pending deposit or the root node of the first pending deposit in the beacon block: ${generalizedIndex}` + ); + let firstPendingDepositSlot = 1; + let firstPendingDepositPubKey = "0x"; + if (stateView.pendingDeposits.length == 0) { + log("No deposits in the deposit queue"); + } else { + const firstPendingDeposit = stateView.pendingDeposits.get(0); + firstPendingDepositSlot = firstPendingDeposit.slot; + firstPendingDepositPubKey = toHex(firstPendingDeposit.pubkey); + log( + `First pending deposit has slot ${ + firstPendingDeposit.slot + }, withdrawal credential ${toHex( + firstPendingDeposit.withdrawalCredentials + )} and public key ${firstPendingDepositPubKey}` + ); + } + + log( + `Generating proof for the the first pending deposit slot to beacon block root ${toHex( + blockTree.root + )}` + ); + const proofObj = createProof(blockTree.rootNode, { + type: ProofType.single, + gindex: generalizedIndex, + }); + log(`First pending deposit slot leaf: ${toHex(proofObj.leaf)}`); + const proofBytes = toHex(concatProof(proofObj)); + log( + `First pending deposit slot proof of depth ${proofObj.witnesses.length} in bytes:\n${proofBytes}` + ); + + if (test) { + // Generate the proof of the slot within the first pending deposit + const subTreeGeneralizedIndex = concatGindices([ + blockView.type.getPathInfo(["stateRoot"]).gindex, + stateView.type.getPathInfo(["pendingDeposits", 0]).gindex, + toGindex(1, 1n), // depth 1, index 1 for slot = 3 + ]); + // Generate the slot proof in the first pending deposit + const subTree = blockTree.getSubtree(subTreeGeneralizedIndex); + log(`Sub tree root: ${toHex(subTree.root)}`); + const subTreeProofObj = createProof(subTree.rootNode, { + type: ProofType.single, + // depth 2, index 0 for slot = 4 + gindex: toGindex(2, 0n), + }); + log( + `First pending deposit slot ${firstPendingDepositSlot} has leaf: ${toHex( + subTreeProofObj.leaf + )}` + ); + const subTreeProofBytes = toHex(concatProof(subTreeProofObj)); + log( + `First pending deposit slot proof of depth ${subTreeProofObj.witnesses.length} in bytes:\n${subTreeProofBytes}` + ); + } + + return { + proof: proofBytes, + generalizedIndex, + root: toHex(blockTree.root), + leaf: toHex(proofObj.leaf), + slot: firstPendingDepositSlot, + isEmpty: stateView.pendingDeposits.length === 0, + }; +} + +async function generateValidatorWithdrawableEpochProof({ + blockView, + blockTree, + stateView, + validatorIndex, +}) { + // Have to dynamically import the Lodestar API client as its an ESM module + const { concatGindices, createProof, ProofType, toGindex } = await import( + "@chainsafe/persistent-merkle-tree" + ); + + const validator = stateView.validators.get(validatorIndex); + if ( + !validator || + toHex(validator.node.root) == + "0x0000000000000000000000000000000000000000000000000000000000000000" + ) { + throw new Error( + `Validator with index ${validatorIndex} not found in the state at slot ${blockView.slot}.` + ); + } + const withdrawableEpoch = + validator.withdrawableEpoch == Infinity + ? MAX_UINT64 + : validator.withdrawableEpoch; + log( + `Validator ${validatorIndex} has withdrawable epoch ${withdrawableEpoch} and public key ${toHex( + validator.pubkey + )}` + ); + log(`${stateView.validators.length} validators at slot ${blockView.slot}.`); + + const generalizedIndexValidatorContainer = concatGindices([ + // 715n, + // (2n ^ 41n) + BigInt(validatorIndex), + blockView.type.getPathInfo(["stateRoot"]).gindex, + stateView.type.getPathInfo(["validators", validatorIndex]).gindex, + ]); + const generalizedIndexWithdrawableEpoch = concatGindices([ + generalizedIndexValidatorContainer, + toGindex(3, 7n), // depth 3, withdrawableEpoch index 7 = 2 ^ 3 + 7 = 15 + ]); + + log( + `Gen index for withdrawableEpoch of validator ${validatorIndex} in beacon block: ${generalizedIndexWithdrawableEpoch}` + ); + + log( + `Generating validator withdrawableEpoch proof to beacon block root ${toHex( + blockTree.root + )}` + ); + const proofObj = createProof(blockTree.rootNode, { + type: ProofType.single, + gindex: generalizedIndexWithdrawableEpoch, + }); + log(`Validator withdrawableEpoch leaf (hash): ${toHex(proofObj.leaf)}`); + const proofBytes = toHex(concatProof(proofObj)); + const depth = proofObj.witnesses.length; + log(`Withdrawable epoch proof of depth ${depth} in bytes:\n${proofBytes}`); + + return { + proof: proofBytes, + generalizedIndexWithdrawableEpoch, + root: toHex(blockTree.root), + leaf: toHex(proofObj.leaf), + withdrawableEpoch, + }; +} + +// BeaconBlock.state.validators[validatorIndex].pubkey +async function generateValidatorPubKeyProof({ + validatorIndex, + blockView, + blockTree, + stateView, +}) { + // Have to dynamically import the Lodestar API client as its an ESM module + const { concatGindices, createProof, ProofType, toGindex } = await import( + "@chainsafe/persistent-merkle-tree" + ); + + const validatorDetails = stateView.validators.get(validatorIndex); + if ( + !validatorDetails || + toHex(validatorDetails.node.root) == + "0x0000000000000000000000000000000000000000000000000000000000000000" + ) { + throw new Error( + `Validator with index ${validatorIndex} not found in the state at slot ${blockView.slot}.` + ); + } + log( + `Validator public key for validator ${validatorIndex}: ${toHex( + validatorDetails.pubkey + )}` + ); + + const generalizedIndex = concatGindices([ + blockView.type.getPathInfo(["stateRoot"]).gindex, + stateView.type.getPathInfo(["validators", validatorIndex]).gindex, + toGindex(3, 0n), // depth 3, index 0 for pubkey = 8 + ]); + log( + `gen index for pubkey of validator ${validatorIndex} in beacon block: ${generalizedIndex}` + ); + + log( + `Generating validator pubkey proof to beacon block root ${toHex( + blockTree.root + )}` + ); + const proofObj = createProof(blockTree.rootNode, { + type: ProofType.single, + gindex: generalizedIndex, + }); + log(`Validator public key leaf (hash): ${toHex(proofObj.leaf)}`); + const proofBytes = toHex(concatProof(proofObj)); + const depth = proofObj.witnesses.length; + log(`Public key proof of depth ${depth} in bytes:\n${proofBytes}`); + + return { + proof: proofBytes, + generalizedIndex, + root: toHex(blockTree.root), + leaf: toHex(proofObj.leaf), + pubKey: toHex(validatorDetails.pubkey), + }; +} + +// BeaconBlock.state.pendingDeposits +async function generatePendingDepositsContainerProof({ + blockView, + blockTree, + stateView, +}) { + // Have to dynamically import the Lodestar API client as its an ESM module + const { concatGindices, createProof, ProofType } = await import( + "@chainsafe/persistent-merkle-tree" + ); + + const generalizedIndex = concatGindices([ + blockView.type.getPathInfo(["stateRoot"]).gindex, + stateView.type.getPathInfo(["pendingDeposits"]).gindex, + ]); + log( + `gen index for pending deposits container in beacon block: ${generalizedIndex}` + ); + + log( + `Generating pending deposits container proof to beacon block root ${toHex( + blockTree.root + )}` + ); + const proofObj = createProof(blockTree.rootNode, { + type: ProofType.single, + gindex: generalizedIndex, + }); + log(`Pending deposits container leaf: ${toHex(proofObj.leaf)}`); + + const proofBytes = toHex(concatProof(proofObj)); + const depth = proofObj.witnesses.length; + log( + `Pending deposits container proof of depth ${depth} in bytes:\n${proofBytes}` + ); + + return { + proof: proofBytes, + generalizedIndex, + root: toHex(blockTree.root), + leaf: toHex(proofObj.leaf), + }; +} + +// BeaconBlock.state.pendingDeposits[depositIndex] +// Generates a proof in the Pending Deposits container rather than the whole beacon block +async function generatePendingDepositProof({ + blockView, + blockTree, + stateView, + depositIndex, +}) { + // Have to dynamically import the Lodestar API client as its an ESM module + const { concatGindices, createProof, ProofType, toGindex } = await import( + "@chainsafe/persistent-merkle-tree" + ); + + // Read the pending deposit from the state + const pendingDeposit = stateView.pendingDeposits.get(depositIndex); + log(`Pending deposit ${depositIndex}:`); + log(` pubkey : ${toHex(pendingDeposit.pubkey)}`); + log(` cred : ${toHex(pendingDeposit.withdrawalCredentials)}`); + log(` amount : ${formatUnits(pendingDeposit.amount, 9)}`); + log(` slot : ${pendingDeposit.slot}`); + + // BeaconBlock.state.pendingDeposits + const genIndexPendingDepositsContainer = concatGindices([ + blockView.type.getPathInfo(["stateRoot"]).gindex, + stateView.type.getPathInfo(["pendingDeposits"]).gindex, + ]); + log( + `gen index for pending deposits container in beacon block: ${genIndexPendingDepositsContainer}` + ); + const pendingDepositsTree = blockTree.getSubtree( + genIndexPendingDepositsContainer + ); + + // BeaconBlock.state.pendingDeposits[depositIndex] + const genIndexPendingDepositContainer = toGindex( + stateView.pendingDeposits.type.depth, + BigInt(depositIndex) + ); + log( + `index for pending deposit in pending deposits container: ${genIndexPendingDepositContainer}` + ); + + log( + `Generating pending deposit proof to pending deposits container root ${toHex( + pendingDepositsTree.root + )}` + ); + const proofObj = createProof(pendingDepositsTree.rootNode, { + type: ProofType.single, + gindex: genIndexPendingDepositContainer, + }); + log(`Pending deposit leaf: ${toHex(proofObj.leaf)}`); + + const proofBytes = toHex(concatProof(proofObj)); + const depth = proofObj.witnesses.length; + log( + `pending deposit ${depositIndex} proof of depth ${depth} in Pending Deposits container in bytes:\n${proofBytes}` + ); + + return { + proof: proofBytes, + generalizedIndex: genIndexPendingDepositContainer, + root: toHex(pendingDepositsTree.root), + leaf: toHex(proofObj.leaf), + depth, + pendingDeposit, + }; +} + +// BeaconBlock.state.balances +async function generateBalancesContainerProof({ + blockView, + blockTree, + stateView, +}) { + // Have to dynamically import the Lodestar API client as its an ESM module + const { concatGindices, createProof, ProofType } = await import( + "@chainsafe/persistent-merkle-tree" + ); + + const generalizedIndex = concatGindices([ + blockView.type.getPathInfo(["stateRoot"]).gindex, + stateView.type.getPathInfo(["balances"]).gindex, + ]); + log(`gen index for balances container in beacon block: ${generalizedIndex}`); + + log( + `Generating balances container proof to beacon block root ${toHex( + blockTree.root + )}` + ); + const proofObj = createProof(blockTree.rootNode, { + type: ProofType.single, + gindex: generalizedIndex, + }); + log(`Balances container leaf: ${toHex(proofObj.leaf)}`); + + const proofBytes = toHex(concatProof(proofObj)); + const depth = proofObj.witnesses.length; + log(`Balances container proof of depth ${depth} in bytes:\n${proofBytes}`); + + return { + proof: proofBytes, + generalizedIndex, + root: toHex(blockTree.root), + leaf: toHex(proofObj.leaf), + }; +} + +// BeaconBlock.state.balances[validatorIndex] +// Generates a proof in the Balances container rather than the whole beacon block +async function generateBalanceProof({ + blockView, + blockTree, + stateView, + validatorIndex, +}) { + // Have to dynamically import the Lodestar API client as its an ESM module + const { concatGindices, createProof, ProofType, toGindex } = await import( + "@chainsafe/persistent-merkle-tree" + ); + + // Read the validator's balance from the state + const validatorBalance = stateView.balances.get(validatorIndex); + log( + `Validator ${validatorIndex} balance: ${formatUnits(validatorBalance, 9)}` + ); + + // BeaconBlock.state.balances + const genIndexBalancesContainer = concatGindices([ + blockView.type.getPathInfo(["stateRoot"]).gindex, + stateView.type.getPathInfo(["balances"]).gindex, + ]); + log( + `gen index for balances container in beacon block: ${genIndexBalancesContainer}` + ); + const balancesTree = blockTree.getSubtree(genIndexBalancesContainer); + + // BeaconBlock.state.balances[validatorIndex] + // There are 4 balances per leaf, so we need to divide by 4 which is right shift by 2. + const balanceIndex = validatorIndex >> 2; + log(`Balance index in the balances container: ${balanceIndex}`); + const genIndexBalanceContainer = toGindex( + stateView.balances.type.depth, + BigInt(balanceIndex) + ); + log(`index for balance in balances container: ${genIndexBalanceContainer}`); + + log(`Balances sub tree root: ${toHex(balancesTree.root)}`); + + log( + `Generating balance in balances container proof to balances container root ${toHex( + balancesTree.root + )}` + ); + const proofObj = createProof(balancesTree.rootNode, { + type: ProofType.single, + gindex: genIndexBalanceContainer, + }); + log(`Balances container leaf: ${toHex(proofObj.leaf)}`); + + const proofBytes = toHex(concatProof(proofObj)); + const depth = proofObj.witnesses.length; + log( + `Validator ${validatorIndex} balance proof of depth ${depth} in Balances container in bytes:\n${proofBytes}` + ); + + return { + proof: proofBytes, + generalizedIndex: genIndexBalancesContainer, + root: toHex(balancesTree.root), + leaf: toHex(proofObj.leaf), + depth, + balance: validatorBalance, + }; +} + +module.exports = { + generateFirstPendingDepositSlotProof, + generateValidatorWithdrawableEpochProof, + generateValidatorPubKeyProof, + generatePendingDepositsContainerProof, + generatePendingDepositProof, + generateBalancesContainerProof, + generateBalanceProof, +}; diff --git a/contracts/utils/ssv.js b/contracts/utils/ssv.js index c77d9b4f88..9594038ea3 100644 --- a/contracts/utils/ssv.js +++ b/contracts/utils/ssv.js @@ -103,7 +103,7 @@ const getClusterInfo = async ({ ownerAddress, operatorids, chainId }) => { // HTTP encode the operator IDs // the .toString() will convert the array to a comma-separated string if not already a string const encodedOperatorIds = encodeURIComponent(operatorids.toString()); - const network = chainId === 1 ? "mainnet" : "holesky"; + const network = chainId === 1 ? "mainnet" : "hoodi"; const url = `${SSV_API_ENDPOINT}/${network}/clusters/owner/${ownerAddress}/operators/${encodedOperatorIds}`; log(`SSV url: ${url}`); @@ -176,9 +176,27 @@ const printClusterInfo = async (options) => { console.log("Next Nonce:", nextNonce); }; +/// @returns {string} Returns a string of sorted, comma-separated, operator IDs +const sortOperatorIds = (operatorIdsString) => { + const operatorIds = splitOperatorIds(operatorIdsString); + + return operatorIds.join(","); +}; + +/// @returns {number[]} Returns an array of sorted operator IDs +const splitOperatorIds = (operatorIdsString) => { + log(`Splitting and sorting operator IDs ${operatorIdsString}`); + const operatorIds = operatorIdsString.split(",").map((id) => parseInt(id)); + operatorIds.sort((a, b) => a - b); + + return operatorIds; +}; + module.exports = { printClusterInfo, getClusterInfo, getClusterNonce, + sortOperatorIds, + splitOperatorIds, splitValidatorKey, }; diff --git a/contracts/utils/units.js b/contracts/utils/units.js index 1a002a47f7..dab55a68a2 100644 --- a/contracts/utils/units.js +++ b/contracts/utils/units.js @@ -50,9 +50,14 @@ const convertToBigNumber = (amount, decimals = 18) => { : parseUnits(amountStr, decimals); }; +const toHex = (buff) => { + return "0x" + Buffer.from(buff).toString("hex"); +}; + module.exports = { decimalsFor, scaleAmount, units, convertToBigNumber, + toHex, }; diff --git a/contracts/yarn.lock b/contracts/yarn.lock index 27c5eee01a..d6a0553c70 100644 --- a/contracts/yarn.lock +++ b/contracts/yarn.lock @@ -825,6 +825,237 @@ "@openzeppelin/contracts-upgradeable-4.7.3" "npm:@openzeppelin/contracts-upgradeable@v4.7.3" "@openzeppelin/contracts-v0.7" "npm:@openzeppelin/contracts@v3.4.2" +"@chainsafe/as-sha256@1.2.0", "@chainsafe/as-sha256@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@chainsafe/as-sha256/-/as-sha256-1.2.0.tgz#5764ac9959e147fe0908dd0f66c0cce525a633b3" + integrity sha512-H2BNHQ5C3RS+H0ZvOdovK6GjFAyq5T6LClad8ivwj9Oaiy28uvdsGVS7gNJKuZmg0FGHAI+n7F0Qju6U0QkKDA== + +"@chainsafe/bls-hd-key@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@chainsafe/bls-hd-key/-/bls-hd-key-0.3.0.tgz#2de2649d5daa76f7460fc23caacf4ed3ebd2c02a" + integrity sha512-LsYYnfBEEmqGFPDm8hQN3Kc+v9wPFnhn+CToD403KEynUiUSHKLAf5B6UCY5eooShDOcaGCUgAUhIw1CmpEf3Q== + dependencies: + "@noble/hashes" "^1.0.0" + +"@chainsafe/bls-keygen@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@chainsafe/bls-keygen/-/bls-keygen-0.4.0.tgz#782c6555cfb05b9efa6f144d782799e8695ca1b0" + integrity sha512-wqtuj4G/sWpIugJW1mb/nSTwcTuZKqB3DS3ANUIOn7pva8EB6LfxgIL34o4qk3lti/8Mdxqtqc2n4xRszrNdzA== + dependencies: + "@chainsafe/bls-hd-key" "^0.3.0" + "@noble/hashes" "^1.0.0" + "@scure/bip39" "^1.0.0" + +"@chainsafe/bls@^8.2.0": + version "8.2.0" + resolved "https://registry.yarnpkg.com/@chainsafe/bls/-/bls-8.2.0.tgz#6f72c39e5f255435ba1a192b09eef89416bb9c82" + integrity sha512-onCMG4K5GRWTHPf5T9DrOLsd5VoiOoL0NEpRoAigXVPqr+Gi2GyOncbvc9Vf4RjhmZGgtIJKKB4WJQbRV9Pddw== + dependencies: + "@chainsafe/bls-keygen" "^0.4.0" + bls-eth-wasm "^1.1.1" + +"@chainsafe/blst-darwin-arm64@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@chainsafe/blst-darwin-arm64/-/blst-darwin-arm64-2.2.0.tgz#0ab9083805c308106c2f2107df1e6376d9190b1b" + integrity sha512-BOOy2KHbV028cioPWaAMqHdLRKd6/3XyEmUEcQC2E/SpyYLdNcaKiBUYIU4pT9CrWBbJJxX68UI+3vZVg0M8/w== + +"@chainsafe/blst-darwin-x64@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@chainsafe/blst-darwin-x64/-/blst-darwin-x64-2.2.0.tgz#231943a7736f3f89d35e03fec890b7809c98ff1a" + integrity sha512-jG64cwIdPT7u/haRrW26tWCpfMfHBQCfGY169mFQifCwO4VEwvaiVBPOh5olFis6LjpcmD+O0jpM8GqrnsmUHQ== + +"@chainsafe/blst-linux-arm64-gnu@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@chainsafe/blst-linux-arm64-gnu/-/blst-linux-arm64-gnu-2.2.0.tgz#721aeec63e8e02aba3358a0084c095403a5438fa" + integrity sha512-L8xV2uuLn8we76vdzfryS9ePdheuZrmY6yArGUFaF1Uzcwml6V1/VvyPl9/uooo/YfVRIrvF/D+lQfI2GFAnhw== + +"@chainsafe/blst-linux-arm64-musl@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@chainsafe/blst-linux-arm64-musl/-/blst-linux-arm64-musl-2.2.0.tgz#dbbabaab93156548c86e2b2b3a1d27160b715000" + integrity sha512-0Vn0luxLYVgC3lvWT1MapFHSAoz99PldqjhilXTGv0AcAk/X5LXPH2RC9Dp2KJGqthyUkpbk1j47jUBfBI+BIg== + +"@chainsafe/blst-linux-x64-gnu@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@chainsafe/blst-linux-x64-gnu/-/blst-linux-x64-gnu-2.2.0.tgz#9f8ab825621b75227c75bb75d369d3d42e91fa74" + integrity sha512-gEY/z2SDBA7kXtFEI9VNhWTJAIjx16jdeAyCaS2k4ACGurWZaWk+Ee4KniTsr4WieSqeuNTUr7Pdja0Sr4EKNQ== + +"@chainsafe/blst-linux-x64-musl@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@chainsafe/blst-linux-x64-musl/-/blst-linux-x64-musl-2.2.0.tgz#11e99ac12b0f83cad68da56f4e9cfc4aa403a2e6" + integrity sha512-58GKtiUmtVSuerRzPEcMNQZpICPboBKFnL7+1Wo+PSuajkvbae7tEFrFTtWeMoKIPgOEsPMnk96LF+0yNgavUg== + +"@chainsafe/blst-win32-x64-msvc@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@chainsafe/blst-win32-x64-msvc/-/blst-win32-x64-msvc-2.2.0.tgz#f32b164721ff5edc279f6d6cd0fffde0ad2fe16c" + integrity sha512-UFrZshl4dfX5Uh2zeKXAZtrkQ+otczHMON2tsrapQNICWmfHZrzE6pKuBL+9QeGAbgflwpbz7+D5nQRDpiuHxQ== + +"@chainsafe/blst@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@chainsafe/blst/-/blst-2.2.0.tgz#ced8b861b94934e3c1c53e173c3e1205d775d93b" + integrity sha512-VBaQoNE2a9d9+skAjQKv3Suk0yGKqp3mZM0YWYJNPj/Ae/f6lAyeVSgKqo2LrsNQBzD/LqrJLKUY8rJT3vDKLA== + optionalDependencies: + "@chainsafe/blst-darwin-arm64" "2.2.0" + "@chainsafe/blst-darwin-x64" "2.2.0" + "@chainsafe/blst-linux-arm64-gnu" "2.2.0" + "@chainsafe/blst-linux-arm64-musl" "2.2.0" + "@chainsafe/blst-linux-x64-gnu" "2.2.0" + "@chainsafe/blst-linux-x64-musl" "2.2.0" + "@chainsafe/blst-win32-x64-msvc" "2.2.0" + +"@chainsafe/hashtree-darwin-arm64@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@chainsafe/hashtree-darwin-arm64/-/hashtree-darwin-arm64-1.0.1.tgz#e2c60090c56a1c8dc8bdff329856184ad32e4cd5" + integrity sha512-+KmEgQMpO7FDL3klAcpXbQ4DPZvfCe0qSaBBrtT4vLF8V1JGm3sp+j7oibtxtOsLKz7nJMiK1pZExi7vjXu8og== + +"@chainsafe/hashtree-linux-arm64-gnu@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@chainsafe/hashtree-linux-arm64-gnu/-/hashtree-linux-arm64-gnu-1.0.1.tgz#49d2604a6c9106219448af3eaf76f4da6e44daca" + integrity sha512-p1hnhGq2aFY+Zhdn1Q6L/6yLYNKjqXfn/Pc8jiM0e3+Lf/hB+yCdqYVu1pto26BrZjugCFZfupHaL4DjUTDttw== + +"@chainsafe/hashtree-linux-x64-gnu@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@chainsafe/hashtree-linux-x64-gnu/-/hashtree-linux-x64-gnu-1.0.1.tgz#31c5a2bb196b78f04f2bf4bfb5c1bf1f3331f071" + integrity sha512-uCIGuUWuWV0LiB4KLMy6JFa7Jp6NmPl3hKF5BYWu8TzUBe7vSXMZfqTzGxXPggFYN2/0KymfRdG9iDCOJfGRqg== + +"@chainsafe/hashtree@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@chainsafe/hashtree/-/hashtree-1.0.1.tgz#587666a261e1da6a37904095ce875fddc53c7c89" + integrity sha512-bleu9FjqBeR/l6W1u2Lz+HsS0b0LLJX2eUt3hOPBN7VqOhidx8wzkVh2S7YurS+iTQtfdK4K5QU9tcTGNrGwDg== + optionalDependencies: + "@chainsafe/hashtree-darwin-arm64" "1.0.1" + "@chainsafe/hashtree-linux-arm64-gnu" "1.0.1" + "@chainsafe/hashtree-linux-x64-gnu" "1.0.1" + +"@chainsafe/persistent-merkle-tree@1.2.0", "@chainsafe/persistent-merkle-tree@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-1.2.0.tgz#a402debcae6b386c51564c8cbacc9b0d56f64486" + integrity sha512-Ng2eqd6OPvFPPuroQ659ZrFMHtc44LxUfK7K2WkoBhlQ3hrvIn3UTQNKc77xUCU40xjeBGSxAfz+MSV256i+/g== + dependencies: + "@chainsafe/as-sha256" "1.2.0" + "@chainsafe/hashtree" "1.0.1" + "@noble/hashes" "^1.3.0" + +"@chainsafe/persistent-ts@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@chainsafe/persistent-ts/-/persistent-ts-1.0.1.tgz#4ab51ce5e72bfd4ddb5bc976c118bb7cbc88cf9c" + integrity sha512-VkPaBD3GxS5vpLkZWfStDL39jG1IPLhy5c3d3FtgwyfGvEthnQJeyUMinGFRoAfAW0yVkf7ggrEzyvhXFLIU0A== + +"@chainsafe/pubkey-index-map-darwin-arm64@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@chainsafe/pubkey-index-map-darwin-arm64/-/pubkey-index-map-darwin-arm64-3.0.0.tgz#1cb3c19aad0462e9960c2f67f31acccf87bf3526" + integrity sha512-gM/wwA6bpfeZm80aSoH2c3ddbPeZho73TUUprUFlHQNLldGzZ293yFQgj2Ontp1fMf9AI0w+F0uEN2qC6v7g9A== + +"@chainsafe/pubkey-index-map-darwin-x64@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@chainsafe/pubkey-index-map-darwin-x64/-/pubkey-index-map-darwin-x64-3.0.0.tgz#675e6bdad856c61bc6cb4641d61ac764ed5697e3" + integrity sha512-zy4dH//kLXfQHHFVp1Pcz4HQgSh/uUKWt219hlrb5Fi+xVESVmTnLPjHmL1KtGs/R5W0XcuBrTuBHiq+zMU7lQ== + +"@chainsafe/pubkey-index-map-linux-arm64-gnu@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@chainsafe/pubkey-index-map-linux-arm64-gnu/-/pubkey-index-map-linux-arm64-gnu-3.0.0.tgz#96235d3c286c75d2ff4d74bf3e46c7f2874adb74" + integrity sha512-M/HODHZcKDuoRFlx+Ka4NgyFSQvOECyIg/2pCQcgfCozR7rRx7VuOJ0ZSgU+BEhuY+fpaRSTesOlDIIY0ornWQ== + +"@chainsafe/pubkey-index-map-linux-arm64-musl@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@chainsafe/pubkey-index-map-linux-arm64-musl/-/pubkey-index-map-linux-arm64-musl-3.0.0.tgz#9105e9ef771b80e79ff902869f48a8c886ad3246" + integrity sha512-Sdm15mFsKiz5ndFfhcTjEl4i8TK0+gM1SEmYzmGUgnflrcvPc+US3qsMKyCqorDkHqYAYFFutQXz9QxjOMkIwg== + +"@chainsafe/pubkey-index-map-linux-x64-gnu@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@chainsafe/pubkey-index-map-linux-x64-gnu/-/pubkey-index-map-linux-x64-gnu-3.0.0.tgz#493f3b0287944543620e372f57de4e74761506f6" + integrity sha512-+4h995h2xEBmYdr9Tw05uYvFmMSELyaJagJ2KsFohkRjqiUyB76l8zR8aUKx7lwLXXHxWjHMYyWKHF3tTKRFNQ== + +"@chainsafe/pubkey-index-map-linux-x64-musl@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@chainsafe/pubkey-index-map-linux-x64-musl/-/pubkey-index-map-linux-x64-musl-3.0.0.tgz#e9cd7e69124b0e73167ed0c8d6bb1c578b7bc676" + integrity sha512-qwFuygeutP4o9J/8ylD+ZrqkqeyytMl9E38ucIWpbiPYXDiJxpRN148v4Ah+NWwz6PqCROVyTxEyZklzy0AxUA== + +"@chainsafe/pubkey-index-map-win32-arm64-msvc@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@chainsafe/pubkey-index-map-win32-arm64-msvc/-/pubkey-index-map-win32-arm64-msvc-3.0.0.tgz#7b45ee24055faf8e6b01480386f952d0961c11ca" + integrity sha512-2t01rQAdiM5KPM7XLsZDtMhWAXz/YKY20H1N4jvaWURjaLm+S0j9X/Mt0xOjJ5SC++1g64LXjV+wnXO5j1S8Nw== + +"@chainsafe/pubkey-index-map-win32-x64-msvc@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@chainsafe/pubkey-index-map-win32-x64-msvc/-/pubkey-index-map-win32-x64-msvc-3.0.0.tgz#8a7d2821a43addf615b3485d6925e7558e42534b" + integrity sha512-QemArQbgsLJ4eaM9S0Kdr3ucJUpZXTDfh3wTNp9eh7G3o1Y9O2Hp7G7BlEkF0cWU9ZsWe9YSzmeX24Uo5DNo4g== + +"@chainsafe/pubkey-index-map@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@chainsafe/pubkey-index-map/-/pubkey-index-map-3.0.0.tgz#eca4a5c0f2c6721f9b57c5a0817474bce3e2a705" + integrity sha512-KvWmKOG8WiVePHQDGyz+xo+RpsrmPEeipF8boqmXNYCLOGyVpkEXQk0IDVpnxngZziY6d97F9sml2vliDoyYyQ== + optionalDependencies: + "@chainsafe/pubkey-index-map-darwin-arm64" "3.0.0" + "@chainsafe/pubkey-index-map-darwin-x64" "3.0.0" + "@chainsafe/pubkey-index-map-linux-arm64-gnu" "3.0.0" + "@chainsafe/pubkey-index-map-linux-arm64-musl" "3.0.0" + "@chainsafe/pubkey-index-map-linux-x64-gnu" "3.0.0" + "@chainsafe/pubkey-index-map-linux-x64-musl" "3.0.0" + "@chainsafe/pubkey-index-map-win32-arm64-msvc" "3.0.0" + "@chainsafe/pubkey-index-map-win32-x64-msvc" "3.0.0" + +"@chainsafe/ssz@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@chainsafe/ssz/-/ssz-1.2.1.tgz#a9a89c9706de33444c0ee64fbc461ae6001132af" + integrity sha512-rchrNF+tJ1yOMZS5CQK3bK5UZC1vQP8ANxW4McRedvBls5b9Mvn+LtbOE4wffrVMOxQzOXcBGZDoCPPJYX76Rg== + dependencies: + "@chainsafe/as-sha256" "1.2.0" + "@chainsafe/persistent-merkle-tree" "1.2.0" + +"@chainsafe/swap-or-not-shuffle-darwin-arm64@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@chainsafe/swap-or-not-shuffle-darwin-arm64/-/swap-or-not-shuffle-darwin-arm64-1.2.1.tgz#c32bc6283b7b2276a90e6eccc38d2606e05eba38" + integrity sha512-kTewLZe1KqMAJ1gHfagOxo0BI4kTcMOAkGJ7pRLFM5ZkL2P7sh3/4ixnjdbtMkO207vMZEZL3fxJSSs14kg9Kg== + +"@chainsafe/swap-or-not-shuffle-darwin-x64@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@chainsafe/swap-or-not-shuffle-darwin-x64/-/swap-or-not-shuffle-darwin-x64-1.2.1.tgz#2e2bde10df6fe9c6c18f9cff0ed3bb4102fd6afc" + integrity sha512-B/f/peQqOLW5Tqib5CqanAlQgoeib75FNszzHwRBwHdRY5ThOcBbARvOtef39zU5lYjj4j3iWobpD7G70kQyUw== + +"@chainsafe/swap-or-not-shuffle-linux-arm64-gnu@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@chainsafe/swap-or-not-shuffle-linux-arm64-gnu/-/swap-or-not-shuffle-linux-arm64-gnu-1.2.1.tgz#769f0ecd91a71996b845813672100880c00e493c" + integrity sha512-sDpuUuo3rStvHMQgLxH1UkkUbo8rcjDI70Fq3xzbNMhfjlrP0+sJistlFJtDpWmKsFk/sgje3PzVU9G3KFXzXA== + +"@chainsafe/swap-or-not-shuffle-linux-arm64-musl@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@chainsafe/swap-or-not-shuffle-linux-arm64-musl/-/swap-or-not-shuffle-linux-arm64-musl-1.2.1.tgz#7d09ee6eca411d5e897794073e26516f2894ebaf" + integrity sha512-FFmpDF2dRhhOCT3WBYOoQW9wqhJMfx7iULskBe5cjowVQ15U1TQJ6tC+hPpouUMt3/pKz9tIS9dNWTUuo7kpQg== + +"@chainsafe/swap-or-not-shuffle-linux-x64-gnu@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@chainsafe/swap-or-not-shuffle-linux-x64-gnu/-/swap-or-not-shuffle-linux-x64-gnu-1.2.1.tgz#8c54b7b8fcb9ed6c87f8ce9dd53900d2d4abc298" + integrity sha512-nGXEnFCqRmCS7ATV7QQ7b7aKHcyET9XEpJylq0sljnkuNRwoQXFUYPMYTn4TMI6g3vgZt/AVK9RdCxp68JKwGQ== + +"@chainsafe/swap-or-not-shuffle-linux-x64-musl@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@chainsafe/swap-or-not-shuffle-linux-x64-musl/-/swap-or-not-shuffle-linux-x64-musl-1.2.1.tgz#17d02514059388db9fb2a72edd6653648e1e4543" + integrity sha512-VMQxE/EZjco4damGrJkkCQ2Y2WYgJcTR/ardTnSctM1/xxwUD1wQr7YW2TQsECfK3UZcW2W6gG7iqWfJgMdGSw== + +"@chainsafe/swap-or-not-shuffle-win32-arm64-msvc@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@chainsafe/swap-or-not-shuffle-win32-arm64-msvc/-/swap-or-not-shuffle-win32-arm64-msvc-1.2.1.tgz#684d523e7dd1f4ebbf1bee191605259fae2717f8" + integrity sha512-qV+ps6KSoR8blc3gMse1BOBUGcCtptnMYoqTMwFCIx9LGaeSo3vKu5XBQDwHIfAW73bZ+PHb5YUqkRgtWBiEUw== + +"@chainsafe/swap-or-not-shuffle-win32-x64-msvc@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@chainsafe/swap-or-not-shuffle-win32-x64-msvc/-/swap-or-not-shuffle-win32-x64-msvc-1.2.1.tgz#508320f5adb3cf90b9ce0e45487c3be7cd2755dd" + integrity sha512-hsDN4O6PPpF4rEA1tMngMJAOrWTGAu8TAqdEwgdP+U25o8UVlz8W2N9697AMGRnnArN2BgYS0zCeUR9kuZ2XEQ== + +"@chainsafe/swap-or-not-shuffle@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@chainsafe/swap-or-not-shuffle/-/swap-or-not-shuffle-1.2.1.tgz#51021bdc77a2476daf56c66a7d8a645012b1f58c" + integrity sha512-H8YdEoXXv2Hw17gDWGOJEya4LHlBbpChJP3jDQRfIk9hhwr0c/zbBemBRmjADowZhArL+ymkO+j5hGaYySjdpw== + optionalDependencies: + "@chainsafe/swap-or-not-shuffle-darwin-arm64" "1.2.1" + "@chainsafe/swap-or-not-shuffle-darwin-x64" "1.2.1" + "@chainsafe/swap-or-not-shuffle-linux-arm64-gnu" "1.2.1" + "@chainsafe/swap-or-not-shuffle-linux-arm64-musl" "1.2.1" + "@chainsafe/swap-or-not-shuffle-linux-x64-gnu" "1.2.1" + "@chainsafe/swap-or-not-shuffle-linux-x64-musl" "1.2.1" + "@chainsafe/swap-or-not-shuffle-win32-arm64-msvc" "1.2.1" + "@chainsafe/swap-or-not-shuffle-win32-x64-msvc" "1.2.1" + "@colors/colors@1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" @@ -1441,6 +1672,11 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" +"@fastify/busboy@^2.0.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d" + integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== + "@ganache/ethereum-address@0.1.4": version "0.1.4" resolved "https://registry.yarnpkg.com/@ganache/ethereum-address/-/ethereum-address-0.1.4.tgz#0e6d66f4a24f64bf687cb3ff7358fb85b9d9005e" @@ -1584,6 +1820,108 @@ resolved "https://registry.yarnpkg.com/@layerzerolabs/oft-evm/-/oft-evm-3.1.4.tgz#de20e47cf69655405d7c8cc67ff6afee97cf4484" integrity sha512-jxuEXtzAv2x/ZErBtg6OI6rxq4KyMamPgy8+r3/AQ5uD4Ih2Sd51mBIebpzueJKqMk7MNdOauLfYRRK6tOFWXQ== +"@lodestar/api@^1.31.0": + version "1.31.0" + resolved "https://registry.yarnpkg.com/@lodestar/api/-/api-1.31.0.tgz#09fd624550fbf5586bf71e1d743cb1e6068dbdfa" + integrity sha512-fH8ivkE9G/Kx0/zudxNJM/uT35D8a9M2AZljJD7Mu6gE2p6JKS8dHLDDmZO5csPCwm+VMN35n9+gOComTNRp5Q== + dependencies: + "@chainsafe/persistent-merkle-tree" "^1.2.0" + "@chainsafe/ssz" "^1.2.1" + "@lodestar/config" "^1.31.0" + "@lodestar/params" "^1.31.0" + "@lodestar/types" "^1.31.0" + "@lodestar/utils" "^1.31.0" + eventsource "^2.0.2" + qs "^6.11.1" + +"@lodestar/config@^1.31.0": + version "1.31.0" + resolved "https://registry.yarnpkg.com/@lodestar/config/-/config-1.31.0.tgz#f5e415d642f887e45117be641bcdffdeb801e1c6" + integrity sha512-DaFKeqYBu8yLW8IRUkxHalJHWLLJqswxttRhALnEUmEgLDiU1erVxwhjIe2UzTWHVqB8KdKTZHTVWP0A+QJq+Q== + dependencies: + "@chainsafe/ssz" "^1.2.1" + "@lodestar/params" "^1.31.0" + "@lodestar/types" "^1.31.0" + "@lodestar/utils" "^1.31.0" + +"@lodestar/config@^1.33.0": + version "1.33.0" + resolved "https://registry.yarnpkg.com/@lodestar/config/-/config-1.33.0.tgz#5359ebf5f811466c6cd6d15778735e1d29c987fc" + integrity sha512-r3bcwisA0eTN5o1ttk/7NVoYU/ZvocDw7INu5wfYxwks5ruNVOjjIVm1YT79600cqTDlL8trew1D9lvIj10Vkg== + dependencies: + "@chainsafe/ssz" "^1.2.1" + "@lodestar/params" "^1.33.0" + "@lodestar/types" "^1.33.0" + "@lodestar/utils" "^1.33.0" + +"@lodestar/params@^1.31.0": + version "1.31.0" + resolved "https://registry.yarnpkg.com/@lodestar/params/-/params-1.31.0.tgz#526cb62ce23680387344b6f4e208d68c5c019251" + integrity sha512-9ZsAQSbHSqu0je5IKhumCi0LI0YOO7ekjuMuZFov2QgySb/ofDvnuqjD4UO6/8u1yN6TS87r6h3F9IBzbT316g== + +"@lodestar/params@^1.33.0": + version "1.33.0" + resolved "https://registry.yarnpkg.com/@lodestar/params/-/params-1.33.0.tgz#794f0ab0923aefc1b5bbd969e9b0f3e0c01bf005" + integrity sha512-j0fD7Zu9eZNPEd3zEUw5FdAD8zrq2hZ1Mq6LOl69865CwNCW3jRX2TUuLEZ/bw6tJf3fuLYeTOQbRtUQ7RLdvA== + +"@lodestar/state-transition@^1.33.0": + version "1.33.0" + resolved "https://registry.yarnpkg.com/@lodestar/state-transition/-/state-transition-1.33.0.tgz#2186cf3d81f4abe26cb99a391a31c9dc11f745d0" + integrity sha512-7186xS1UKDbP3pWVpBdqqnNIXQv5kEQVFaJRJG17oRvozStsp7nrBLBw7UNdnz6X8GEwjdhicO+O0I3N//DXgg== + dependencies: + "@chainsafe/as-sha256" "^1.2.0" + "@chainsafe/blst" "^2.2.0" + "@chainsafe/persistent-merkle-tree" "^1.2.0" + "@chainsafe/persistent-ts" "^1.0.0" + "@chainsafe/pubkey-index-map" "^3.0.0" + "@chainsafe/ssz" "^1.2.1" + "@chainsafe/swap-or-not-shuffle" "^1.2.1" + "@lodestar/config" "^1.33.0" + "@lodestar/params" "^1.33.0" + "@lodestar/types" "^1.33.0" + "@lodestar/utils" "^1.33.0" + bigint-buffer "^1.1.5" + +"@lodestar/types@^1.31.0": + version "1.31.0" + resolved "https://registry.yarnpkg.com/@lodestar/types/-/types-1.31.0.tgz#481b5661420aba9d3c357be1a9ed89e937189d08" + integrity sha512-rpsSRfiEXqRO6IjbnZqkYa0guBK7s/BkqJT3ClBWmcUg/VSGYCjT2I2dNhfGxPmddovP7xonYXpXK/x8xZGh1g== + dependencies: + "@chainsafe/ssz" "^1.2.1" + "@lodestar/params" "^1.31.0" + ethereum-cryptography "^2.0.0" + +"@lodestar/types@^1.33.0": + version "1.33.0" + resolved "https://registry.yarnpkg.com/@lodestar/types/-/types-1.33.0.tgz#d4ba7851e421f9a3f669520445ad9c21815f91eb" + integrity sha512-TnzxrDA83IlZX65K2SZWT6fE78W4gKB0yd/RFt/Gtd3oB4i8DpENrhSNyNVTnzIsfly9q1FebjPh3faKYWo6Xw== + dependencies: + "@chainsafe/ssz" "^1.2.1" + "@lodestar/params" "^1.33.0" + ethereum-cryptography "^2.0.0" + +"@lodestar/utils@^1.31.0": + version "1.31.0" + resolved "https://registry.yarnpkg.com/@lodestar/utils/-/utils-1.31.0.tgz#6201c1891cbe0e01fe2be5dc167bc2fa686e2e49" + integrity sha512-eLVXJkzfl3Zeb9GRSQdPpAjrBRZEt6kKNfQF7Da8Hj4uo5bc6NFm0tTrpRnBhuFQKVbYAZU/uPkbNSR12U8mpA== + dependencies: + "@chainsafe/as-sha256" "^1.2.0" + any-signal "^4.1.1" + bigint-buffer "^1.1.5" + case "^1.6.3" + js-yaml "^4.1.0" + +"@lodestar/utils@^1.33.0": + version "1.33.0" + resolved "https://registry.yarnpkg.com/@lodestar/utils/-/utils-1.33.0.tgz#d188830ff412ee7f48654fec446140e02f16c967" + integrity sha512-HdfK9gCoS3PDMwxyMbVLf6JJlyJiiJUZjQCWSRBlOa/2OPAMXKZUjWokLwgyx94EMEHtVF/SdpZTrhW9BM2RDA== + dependencies: + "@chainsafe/as-sha256" "^1.2.0" + any-signal "^4.1.1" + bigint-buffer "^1.1.5" + case "^1.6.3" + js-yaml "^4.1.0" + "@noble/ciphers@^1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-1.3.0.tgz#f64b8ff886c240e644e5573c097f86e5b43676dc" @@ -1610,20 +1948,13 @@ dependencies: "@noble/hashes" "1.4.0" -"@noble/curves@1.9.2": +"@noble/curves@1.9.2", "@noble/curves@^1.9.1", "@noble/curves@~1.9.0": version "1.9.2" resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.9.2.tgz#73388356ce733922396214a933ff7c95afcef911" integrity sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g== dependencies: "@noble/hashes" "1.8.0" -"@noble/curves@^1.9.1", "@noble/curves@~1.9.0": - version "1.9.6" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.9.6.tgz#b45ebedca85bb75782f6be7e7f120f0c423c99e0" - integrity sha512-GIKz/j99FRthB8icyJQA51E8Uk5hXmdyThjgQXRKiv9h0zeRlzSCLIzFw6K1LotZ3XuB7yzlf76qk7uBmTdFqA== - dependencies: - "@noble/hashes" "1.8.0" - "@noble/curves@~1.8.1": version "1.8.2" resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.8.2.tgz#8f24c037795e22b90ae29e222a856294c1d9ffc7" @@ -1656,7 +1987,7 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.7.2.tgz#d53c65a21658fb02f3303e7ee3ba89d6754c64b4" integrity sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ== -"@noble/hashes@1.8.0", "@noble/hashes@^1.8.0", "@noble/hashes@~1.8.0": +"@noble/hashes@1.8.0", "@noble/hashes@^1.0.0", "@noble/hashes@^1.3.0", "@noble/hashes@^1.8.0", "@noble/hashes@~1.8.0": version "1.8.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.8.0.tgz#cee43d801fcef9644b11b8194857695acd5f815a" integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== @@ -1722,7 +2053,7 @@ resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.11.3.tgz#fb208b94553c7eb22246d73a1ac4de5bfdb97d01" integrity sha512-snvEf+WB3OV0wj2A7kQ+ZQqBquMcrozSLXcdnMdEl7Tmn+KDCbmFKBt3Tk0X3qOU4RKQpLPnTxdM07TJNVtung== -"@nomicfoundation/edr@^0.11.3": +"@nomicfoundation/edr@^0.11.1": version "0.11.3" resolved "https://registry.yarnpkg.com/@nomicfoundation/edr/-/edr-0.11.3.tgz#e8b30b868788e45d7a2ee2359a021ef7dcb96952" integrity sha512-kqILRkAd455Sd6v8mfP3C1/0tCOynJWY+Ir+k/9Boocu2kObCrsFgG+ZWB7fSBVdd9cPVSNrnhWS+V+PEo637g== @@ -1743,9 +2074,9 @@ ethereumjs-util "^7.1.4" "@nomicfoundation/hardhat-verify@^2.0.14": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-verify/-/hardhat-verify-2.1.1.tgz#0af5fc4228df860062865fcafb4a01bc0b89f8a3" - integrity sha512-K1plXIS42xSHDJZRkrE2TZikqxp9T4y6jUMUNI/imLgN5uCcEQokmfU0DlyP9zzHncYK92HlT5IWP35UVCLrPw== + version "2.0.14" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-verify/-/hardhat-verify-2.0.14.tgz#ba80918fac840f1165825f2a422a694486f82f6f" + integrity sha512-z3iVF1WYZHzcdMMUuureFpSAfcnlfJbJx3faOnGrOYg6PRTki1Ut9JAuRccnFzMHf1AmTEoSUpWcyvBCoxL5Rg== dependencies: "@ethersproject/abi" "^5.1.2" "@ethersproject/address" "^5.0.2" @@ -2289,7 +2620,7 @@ "@noble/hashes" "~1.4.0" "@scure/base" "~1.1.6" -"@scure/bip39@1.6.0", "@scure/bip39@^1.6.0": +"@scure/bip39@1.6.0", "@scure/bip39@^1.0.0", "@scure/bip39@^1.6.0": version "1.6.0" resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.6.0.tgz#475970ace440d7be87a6086cbee77cb8f1a684f9" integrity sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A== @@ -2902,9 +3233,9 @@ integrity sha512-RV16k/qIxW/wWc+mLzV3ARyKUaMUTBy9tOLMzFhtNSKYeTAanQ3a5MudJKf/8arIFnA2L27SNjarQKmFg0w/jA== "@solidity-parser/parser@^0.20.1": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.20.2.tgz#e07053488ed60dae1b54f6fe37bb6d2c5fe146a7" - integrity sha512-rbu0bzwNvMcwAjH86hiEAcOeRI2EeK8zCkHDrFykh/Al8mvJeFmjy3UrE7GYQjNwOgbGUUtCn5/k8CB8zIu7QA== + version "0.20.1" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.20.1.tgz#88efee3e0946a4856ed10355017692db9c259ff4" + integrity sha512-58I2sRpzaQUN+jJmWbHfbWf9AKfzqCI8JAdFB0vbyY+u8tBRcuTt9LxzasvR0LGQpcRv97eyV7l61FQ3Ib7zVw== "@szmarczak/http-timer@^1.1.2": version "1.1.2" @@ -3438,6 +3769,11 @@ antlr4ts@^0.5.0-alpha.4: resolved "https://registry.yarnpkg.com/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz#71702865a87478ed0b40c0709f422cf14d51652a" integrity sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ== +any-signal@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/any-signal/-/any-signal-4.1.1.tgz#928416c355c66899e6b2a91cad4488f0324bae03" + integrity sha512-iADenERppdC+A2YKbOXXB2WUeABLaM6qnpZ70kZbPZ1cZMMJ7eF+3CaYm+/PhBizgkzlvssC7QuHS30oOiQYWA== + anymatch@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" @@ -3657,6 +3993,13 @@ bech32@1.1.4: resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== +bigint-buffer@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/bigint-buffer/-/bigint-buffer-1.1.5.tgz#d038f31c8e4534c1f8d0015209bf34b4fa6dd442" + integrity sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA== + dependencies: + bindings "^1.3.0" + bignumber.js@^9.0.0: version "9.1.1" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.1.tgz#c4df7dc496bd849d4c9464344c1aa74228b4dac6" @@ -3667,6 +4010,13 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== +bindings@^1.3.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + bip39@3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/bip39/-/bip39-3.0.4.tgz#5b11fed966840b5e1b8539f0f54ab6392969b2a0" @@ -3687,6 +4037,11 @@ bls-eth-wasm@^1.0.4: resolved "https://registry.yarnpkg.com/bls-eth-wasm/-/bls-eth-wasm-1.2.1.tgz#85f165c17d8f16000f46695b56f72bf6af386825" integrity sha512-hl4oBzZQmPGNb9Wt5GI+oEuHM6twGc5HzXCzNZMVLMMg+dltsOuvuioRyLolpDFbncC0BJbGPzP1ZTysUGkksw== +bls-eth-wasm@^1.1.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/bls-eth-wasm/-/bls-eth-wasm-1.4.0.tgz#7e68ac6cf897ec6227b15e3567d7c392b5f50f76" + integrity sha512-9TJR3r3CUJQR97PU6zokV2kVA80H8g4tkVBnaf9HNH3lFMBZUKZETNCwIuN+exFLujdbt1K188rH3mnq8UgmKw== + bls-signatures@^0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/bls-signatures/-/bls-signatures-0.2.5.tgz#7c285e3cb535f279d842e53d1f6e0c8ceda793d1" @@ -3959,13 +4314,6 @@ builtin-modules@^3.3.0: resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== -busboy@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" - integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== - dependencies: - streamsearch "^1.1.0" - bytes@3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" @@ -4007,6 +4355,14 @@ cacheable-request@^7.0.2: normalize-url "^6.0.1" responselike "^2.0.0" +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -4026,6 +4382,14 @@ call-bind@^1.0.7: get-intrinsic "^1.2.4" set-function-length "^1.2.1" +call-bound@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" + caller-callsite@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" @@ -4055,6 +4419,11 @@ camelcase@^6.0.0, camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== +case@^1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/case/-/case-1.6.3.tgz#0a4386e3e9825351ca2e6216c60467ff5f1ea1c9" + integrity sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ== + caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" @@ -4512,16 +4881,7 @@ cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.2: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -cross-spawn@^7.0.6: +cross-spawn@^7.0.2, cross-spawn@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== @@ -4782,6 +5142,15 @@ dotenv@^16.3.1: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + duplexer3@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.5.tgz#0b5e4d7bad5de8901ea4440624c8e1d20099217e" @@ -4916,11 +5285,23 @@ es-define-property@^1.0.0: dependencies: get-intrinsic "^1.2.4" +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + es-errors@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + es5-ext@^0.10.35, es5-ext@^0.10.50, es5-ext@^0.10.62, es5-ext@^0.10.64, es5-ext@~0.10.14: version "0.10.64" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.64.tgz#12e4ffb48f1ba2ea777f1fcdd1918ef73ea21714" @@ -5457,6 +5838,11 @@ events@^3.3.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== +eventsource@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-2.0.2.tgz#76dfcc02930fb2ff339520b6d290da573a9e8508" + integrity sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA== + evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" @@ -5619,6 +6005,11 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -5664,11 +6055,12 @@ flat-cache@^2.0.1: write "1.0.3" flat-cache@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" - integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + version "3.2.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== dependencies: - flatted "^3.1.0" + flatted "^3.2.9" + keyv "^4.5.3" rimraf "^3.0.2" flat@^5.0.2: @@ -5681,10 +6073,10 @@ flatted@^2.0.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== -flatted@^3.1.0: - version "3.2.7" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" - integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== +flatted@^3.2.9: + version "3.3.3" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" + integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== fmix@^0.1.0: version "0.1.0" @@ -5928,6 +6320,30 @@ get-intrinsic@^1.2.4: has-symbols "^1.0.3" hasown "^2.0.0" +get-intrinsic@^1.2.5, get-intrinsic@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + get-random-values@^1.2.0: version "1.2.2" resolved "https://registry.yarnpkg.com/get-random-values/-/get-random-values-1.2.2.tgz#f1d944d0025433d53a2bd9941b9e975d98a2f7ff" @@ -6110,6 +6526,11 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + got@12.1.0: version "12.1.0" resolved "https://registry.yarnpkg.com/got/-/got-12.1.0.tgz#099f3815305c682be4fd6b0ee0726d8e4c6b0af4" @@ -6274,15 +6695,17 @@ hardhat-tracer@^3.2.1: semver "^7.6.2" hardhat@^2.25.0: - version "2.26.2" - resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.26.2.tgz#e331ad9c64730d00e1c71217a0161f204796b02d" - integrity sha512-uIETdC1MAWSnu+48B+583r3b8JqHtkBGEiXoAPV5hcXYGkCW5Fnnpn7SIgPa8owhObm9oUqIqMXMJ6H92N7Pyg== + version "2.25.0" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.25.0.tgz#473bf07b62a0ea30cf003e4585f71a0ffc70c739" + integrity sha512-yBiA74Yj3VnTRj7lhnn8GalvBdvsMOqTKRrRATSy/2v0VIR2hR0Jcnmfn4aQBLtGAnr3Q2c8CxL0g3LYegUp+g== dependencies: "@ethereumjs/util" "^9.1.0" "@ethersproject/abi" "^5.1.2" - "@nomicfoundation/edr" "^0.11.3" + "@nomicfoundation/edr" "^0.11.1" "@nomicfoundation/solidity-analyzer" "^0.1.0" "@sentry/node" "^5.18.1" + "@types/bn.js" "^5.1.0" + "@types/lru-cache" "^5.1.0" adm-zip "^0.4.16" aggregate-error "^3.0.0" ansi-escapes "^4.3.0" @@ -6357,6 +6780,11 @@ has-symbols@^1.0.2, has-symbols@^1.0.3: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== +has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + has-tostringtag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" @@ -6403,7 +6831,7 @@ hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: inherits "^2.0.3" minimalistic-assert "^1.0.1" -hasown@^2.0.0: +hasown@^2.0.0, hasown@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== @@ -7036,7 +7464,7 @@ keyv@^3.0.0: dependencies: json-buffer "3.0.0" -keyv@^4.0.0: +keyv@^4.0.0, keyv@^4.5.3: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== @@ -7315,6 +7743,11 @@ match-all@^1.2.6: resolved "https://registry.yarnpkg.com/match-all/-/match-all-1.2.6.tgz#66d276ad6b49655551e63d3a6ee53e8be0566f8d" integrity sha512-0EESkXiTkWzrQQntBu2uzKvLu6vVkUGz40nGPbSZuegcfE5UuSzNjLaIu76zJWuaT/2I3Z/8M06OlUOZLGwLlQ== +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + mcl-wasm@^0.7.1: version "0.7.9" resolved "https://registry.yarnpkg.com/mcl-wasm/-/mcl-wasm-0.7.9.tgz#c1588ce90042a8700c3b60e40efb339fc07ab87f" @@ -7792,6 +8225,11 @@ object-assign@^4, object-assign@^4.1.0, object-assign@^4.1.1: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== +object-inspect@^1.13.3: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== + object-inspect@^1.9.0: version "1.12.3" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" @@ -7891,10 +8329,10 @@ os-tmpdir@~1.0.2: resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== -ox@0.8.6: - version "0.8.6" - resolved "https://registry.yarnpkg.com/ox/-/ox-0.8.6.tgz#7dd666216ee8cda2eb2e5fef3fe4cb20dec3dcad" - integrity sha512-eiKcgiVVEGDtEpEdFi1EGoVVI48j6icXHce9nFwCNM7CKG3uoCXKdr4TPhS00Iy1TR2aWSF1ltPD0x/YgqIL9w== +ox@0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/ox/-/ox-0.8.1.tgz#c1328e4c890583b9c19d338126aef4b796d53543" + integrity sha512-e+z5epnzV+Zuz91YYujecW8cF01mzmrUtWotJ0oEPym/G82uccs7q0WDHTYL3eiONbTUEvcZrptAKLgTBD3u2A== dependencies: "@adraffy/ens-normalize" "^1.11.0" "@noble/ciphers" "^1.3.0" @@ -8258,6 +8696,13 @@ qs@^6.11.0, qs@^6.9.4: dependencies: side-channel "^1.0.4" +qs@^6.11.1: + version "6.14.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.0.tgz#c63fa40680d2c5c941412a0e899c89af60c0a930" + integrity sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w== + dependencies: + side-channel "^1.1.0" + qs@~6.5.2: version "6.5.3" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" @@ -8806,6 +9251,35 @@ shelljs@^0.8.3: interpret "^1.0.0" rechoir "^0.6.2" +side-channel-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -8815,6 +9289,17 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" +side-channel@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" + signal-exit@^3.0.2: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -9110,11 +9595,6 @@ stream@^0.0.2: dependencies: emitter-component "^1.1.1" -streamsearch@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" - integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== - strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" @@ -9125,7 +9605,7 @@ string-format@^2.0.0: resolved "https://registry.yarnpkg.com/string-format/-/string-format-2.0.0.tgz#f2df2e7097440d3b65de31b6d40d54c96eaffb9b" integrity sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA== -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -9151,6 +9631,15 @@ string-width@^3.0.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -9174,7 +9663,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -9195,6 +9684,13 @@ strip-ansi@^5.1.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -9574,11 +10070,11 @@ underscore@^1.13.4: integrity sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A== undici@^5.14.0: - version "5.22.1" - resolved "https://registry.yarnpkg.com/undici/-/undici-5.22.1.tgz#877d512effef2ac8be65e695f3586922e1a57d7b" - integrity sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw== + version "5.29.0" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.29.0.tgz#419595449ae3f2cdcba3580a2e8903399bd1f5a3" + integrity sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg== dependencies: - busboy "^1.6.0" + "@fastify/busboy" "^2.0.0" unfetch@^4.2.0: version "4.2.0" @@ -9719,9 +10215,9 @@ verror@1.10.0: extsprintf "^1.2.0" viem@^2.27.0: - version "2.33.3" - resolved "https://registry.yarnpkg.com/viem/-/viem-2.33.3.tgz#b69d7ff9edf649d1b7d9218e0225bcadc83a8caa" - integrity sha512-aWDr6i6r3OfNCs0h9IieHFhn7xQJJ8YsuA49+9T5JRyGGAkWhLgcbLq2YMecgwM7HdUZpx1vPugZjsShqNi7Gw== + version "2.31.4" + resolved "https://registry.yarnpkg.com/viem/-/viem-2.31.4.tgz#a8cce5579cc495a2586a16dd4062deecc0f53f1d" + integrity sha512-0UZ/asvzl6p44CIBRDbwEcn3HXIQQurBZcMo5qmLhQ8s27Ockk+RYohgTLlpLvkYs8/t4UUEREAbHLuek1kXcw== dependencies: "@noble/curves" "1.9.2" "@noble/hashes" "1.8.0" @@ -9729,7 +10225,7 @@ viem@^2.27.0: "@scure/bip39" "1.6.0" abitype "1.0.8" isows "1.0.7" - ox "0.8.6" + ox "0.8.1" ws "8.18.2" web3-bzz@1.10.4: @@ -10288,7 +10784,16 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==